[
  {
    "path": ".gitattributes",
    "content": "*.ts linguist-language=Go\n*.js linguist-language=Go\n*.css linguist-language=Go\n*.scss linguist-language=Go\n*.html linguist-language=Go\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1_bug_report.yml",
    "content": "name: 'Bug Report'\ndescription: 'Report an Bug'\ntitle: '[Bug] '\nassignees: wanghe-fit2cloud\nbody:\n  - type: markdown\n    attributes:\n      value: \"## Contact Information\"\n  - type: input\n    validations:\n      required: false\n    attributes:\n      label: \"Contact Information\"\n      description: \"The ways to quickly contact you: WeChat group number and nickname, email, etc.\"\n  - type: markdown\n    attributes:\n      value: \"## Environment Information\"\n  - type: input\n    validations:\n      required: true\n    attributes:\n      label: \"1Panel Version\"\n      description: \"Log in to the 1Panel Web console and check the current version at the bottom right of the page.\"\n  - type: markdown\n    attributes:\n      value: \"## Detailed information\"\n  - type: textarea\n    attributes:\n      label: \"Problem Description\"\n      description: \"Briefly describe the issue you’ve encountered.\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"Steps to Reproduce\"\n      description: \"How can this issue be reproduced.\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"The expected correct result\"\n  - type: textarea\n    attributes:\n      label: \"Related log output\"\n      description: \"Please paste any relevant log output here. It will automatically be formatted as code, so no backticks are necessary.\"\n      render: shell\n  - type: textarea\n    attributes:\n      label: \"Additional Information\"\n      description: \"If you have any additional information to provide, you can include it here (screenshots, videos, etc., are welcome).\""
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2_feature_request.yml",
    "content": "name: 'Feature Request'\ndescription: 'Suggest an idea'\ntitle: '[Feature] '\nassignees: wanghe-fit2cloud\nbody:\n  - type: markdown\n    attributes:\n      value: \"## Environment Information\"\n  - type: input\n    validations:\n      required: true\n    attributes:\n      label: \"1Panel Version\"\n      description: \"Log in to the 1Panel Web console and check the current version at the bottom right of the page.\"\n  - type: markdown\n    attributes:\n      value: \"## Detailed information\"\n  - type: textarea\n    attributes:\n      label: \"Please describe your needs or suggestions for improvements\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: \"Please describe the solution you suggest\"\n  - type: textarea\n    attributes:\n      label: \"Additional Information\"\n      description: \"If you have any additional information to provide, you can include it here (screenshots, videos, etc., are welcome).\""
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Questions & Discussions\n    url: https://github.com/1Panel-dev/1Panel/discussions\n    about: Raise questions about the installation, deployment, use and other aspects of the project."
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "#### What this PR does / why we need it?\n\n#### Summary of your change\n\n#### Please indicate you've done the following:\n\n- [ ] Made sure tests are passing and test coverage is added if needed.\n- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).\n- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed."
  },
  {
    "path": ".github/workflows/add-labels-for-pr.yml",
    "content": "\nname: General PR Handling for 1Panel\non: pull_request\npermissions:\n  pull-requests: write\njobs:\n  generic_handler:\n    name: Add Labels to PR\n    if: github.repository == '1Panel-dev/1Panel'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions-ecosystem/action-add-labels@v1\n        with:\n          github_token: ${{ secrets.GITHUBTOKEN }}\n          labels: ${{ github.base_ref }}\n"
  },
  {
    "path": ".github/workflows/build-and-publish.yml",
    "content": "name: Build And Publish (OSS + R2)\n\non:\n  push:\n    tags:\n      - 'v*'\n\njobs:\n  create-release:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Code\n        uses: actions/checkout@v4\n\n      - name: Setup Node\n        uses: actions/setup-node@v4\n        with:\n          node-version: '22.19.0'\n\n      - name: Build Web\n        run: |\n          cd frontend && npm install && npm run build:pro\n        env:\n          NODE_OPTIONS: --max-old-space-size=8192\n\n      - name: Setup Go\n        uses: actions/setup-go@v5\n        with:\n          go-version: '1.24'\n          cache-dependency-path: |\n            core/go.sum\n            agent/go.sum\n\n      - name: Build Release\n        uses: goreleaser/goreleaser-action@v6\n        with:\n          distribution: goreleaser\n          version: '~> v2'\n          args: release --skip=publish --clean\n\n      - name: Upload Assets\n        uses: softprops/action-gh-release@v1\n        if: startsWith(github.ref, 'refs/tags/')\n        with:\n          draft: true\n          files: |\n            dist/*.tar.gz\n            dist/checksums.txt\n\n      - name: Setup OSSUTIL\n        uses: yizhoumo/setup-ossutil@v2\n        with:\n          endpoint: ${{ secrets.OSS_ENDPOINT }}\n          access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}\n          access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}\n          ossutil-version: '1.7.18'\n\n      - name: Upload Assets to OSS\n        run: |\n          ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/v2/stable/${{ github.ref_name }}/release/ --include \"*.tar.gz\" --include \"checksums.txt\" --only-current-dir --force\n\n      - name: Setup Rclone\n        uses: AnimMouse/setup-rclone@v1\n        with:\n          rclone_config: ${{ secrets.RCLONE_CONFIG }}\n\n      - name: Upload to Cloudflare R2\n        run: |\n          rclone copy dist/ cloudflare_r2:package/v2/stable/${{ github.ref_name }}/release/ --include \"*.tar.gz\" --include \"checksums.txt\" --progress\n"
  },
  {
    "path": ".github/workflows/issue-translator.yml",
    "content": "name: Issue Translator\non: \n  issue_comment: \n    types: [created]\n  issues: \n    types: [opened]\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: usthe/issues-translate-action@v2.7\n        with:\n          IS_MODIFY_TITLE: true\n          BOT_GITHUB_TOKEN: ${{ secrets.ISSUE_TRANSLATOR_TOKEN }} "
  },
  {
    "path": ".github/workflows/llm-code-review.yml",
    "content": "name: LLM Code Review\npermissions:\n  contents: read\n  pull-requests: write\non:\n  pull_request:\n    types: [opened, reopened, synchronize]\njobs:\n  llm-code-review:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: fit2cloud/LLM-CodeReview-Action@main\n        env:\n          GITHUB_TOKEN: ${{ secrets.FIT2CLOUDRD_LLM_CODE_REVIEW_TOKEN }}\n          OPENAI_API_KEY: ${{ secrets.ALIYUN_LLM_API_KEY }}\n          LANGUAGE: English\n          OPENAI_API_ENDPOINT: https://dashscope.aliyuncs.com/compatible-mode/v1\n          MODEL: qwen2.5-coder-3b-instruct\n          PROMPT: \"Please check the following code differences for any irregularities, potential issues, or optimization suggestions, and provide your answers in English.\"\n          top_p: 1\n          temperature: 1\n          # max_tokens: 10000\n          MAX_PATCH_LENGTH: 10000 \n          IGNORE_PATTERNS: \"/node_modules,*.md,/dist,/.github\"\n          FILE_PATTERNS: \"*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html\""
  },
  {
    "path": ".github/workflows/sonarcloud-scan.yml",
    "content": "name: SonarCloud Scan\non:\n  push:\n    branches:\n      - dev\n  pull_request:\n    types: [opened, synchronize, reopened]\njobs:\n  sonarcloud:\n    name: SonarCloud\n    if: github.repository == '1Panel-dev/1Panel'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n      - name: SonarCloud Scan\n        uses: SonarSource/sonarcloud-github-action@master\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUBTOKEN }}\n          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}"
  },
  {
    "path": ".github/workflows/sync2gitee.yml",
    "content": "name: Synchronize to Gitee\non: [push]\njobs:\n  repo-sync:\n    if: github.repository == '1Panel-dev/1Panel'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Mirror the Github organization repos to Gitee.\n        uses: Yikun/hub-mirror-action@master\n        with:\n          src: 'github/1Panel-dev'\n          dst: 'gitee/fit2cloud-feizhiyun'\n          dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}\n          dst_token:  ${{ secrets.GITEE_TOKEN }}\n          static_list: \"1Panel\"\n          force_update: true"
  },
  {
    "path": ".github/workflows/tyops-check.yml",
    "content": "name: Typos Check\non: pull_request\njobs:\n  run:\n    name: Spell Check with Typos\n    runs-on: ubuntu-latest\n    steps:\n    - name: Checkout Actions Repository\n      uses: actions/checkout@v2\n    - name: Check spelling\n      uses: crate-ci/typos@master"
  },
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\nbuild/1panel-agent\nbuild/1panel-core\n\n# Mac\n.DS_Store\n*/.DS_Store\n\n# VS Code\n.vscode\n*.project\n*.factorypath\n__debug*\n\n# IntelliJ IDEA\n.idea/*\n!.idea/icon.png\n*.iws\n*.iml\n*.ipr\n\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Dependency directories\n/pkg/\nbackend/__debug_bin\ncore/cmd/server/__debug_bin\ncore/cmd/server/web/assets\ncore/cmd/server/web/monacoeditorwork\ncore/cmd/server/web/index.html\nfrontend/auto-imports.d.ts\nfrontend/components.d.ts\nfrontend/src/xpack\nagent/xpack\nagent/router/entry_xpack.go\nagent/server/init_xpack.go\nagent/utils/xpack/xpack.go\nagent/utils/xpack/xpack_xpack.go\ncore/xpack\ncore/router/entry_xpack.go\ncore/server/init_xpack.go\ncore/utils/xpack/xpack.go\ncore/utils/xpack/xpack_xpack.go\ncore/xpack-ee\ncore/router/entry_xpackee.go\ncore/server/init_xpackee.go\ncore/utils/xpack/xpack_xpackee.go\n\n.history/\ndist/\n1pctl\n1panel.service\ninstall.sh\nquick_start.sh\ncmd/server/fileList.txt\n.fileList.txt\n1Panel.code-workspace\n\ncore/.golangci.yml\nagent/.golangci.yml\n\n.opencode\nopenspec\nCLAUDE.md\nAGENTS.md\nopencode.json\nsuperpowers\n.worktrees/\n"
  },
  {
    "path": ".goreleaser.yaml",
    "content": "# yaml-language-server: $schema=https://goreleaser.com/static/schema.json\n# vim: set ts=2 sw=2 tw=0 fo=jcroql\nversion: 2\n\nbefore:\n  hooks:\n    # - export NODE_OPTIONS=\"--max-old-space-size=8192\"\n    # - make build_web\n    - chmod +x ./ci/script.sh\n    - ./ci/script.sh\n    - sed -i 's@ORIGINAL_VERSION=.*@ORIGINAL_VERSION=v{{ .Version }}@g' 1pctl\n    \nbuilds:\n  - id: agent\n    dir: agent\n    main: cmd/server/main.go\n    binary: 1panel-agent\n    flags:\n      - -tags=xpack\n      - -trimpath\n    ldflags:\n      - -w -s\n    env:\n      - CGO_ENABLED=0\n    goos:\n      - linux\n    goarm:\n      - 7\n    goarch:\n      - amd64\n      - arm64\n      - arm\n      - ppc64le\n      - s390x\n      - riscv64\n\n  - id: core\n    dir: core\n    main: cmd/server/main.go\n    binary: 1panel-core\n    flags:\n      - -tags=xpack\n      - -trimpath\n    ldflags:\n      - -w -s\n    env:\n      - CGO_ENABLED=0\n    goos:\n      - linux\n    goarm:\n      - 7\n    goarch:\n      - amd64\n      - arm64\n      - arm\n      - ppc64le\n      - s390x\n      - riscv64\n\narchives:\n  - formats: [ 'tar.gz' ]\n    ids: [core, agent]\n    name_template: \"1panel-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{- if .Arm }}v{{ .Arm }}{{ end }}\"\n    wrap_in_directory: true\n    files:\n      - 1pctl\n      - install.sh\n      - 1panel-core.service\n      - 1panel-agent.service\n      - initscript/*\n      - lang/*\n      - GeoIP.mmdb\n\nchecksum:\n  name_template: 'checksums.txt'\n\nchangelog:\n  sort: asc\n  filters:\n    exclude:\n      - \"^docs:\"\n      - \"^test:\"\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nAs a contributor, you should agree that:\n\n- The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.\n- Your contributed code may be used for commercial purposes, including but not limited to its cloud business operations.\n\n## Create pull request\nPR are always welcome, even if they only contain small fixes like typos or a few lines of code. If there will be a significant effort, please document it as an issue and get a discussion going before starting to work on it.\n\nPlease submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.\n\nThis [development guideline](https://docs.1panel.pro/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.\n\nNote: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.\n\n## Report issues\nIt is a great way to contribute by reporting an issue. Well-written and complete bug reports are always welcome! Please open an issue and follow the template to fill in required information.\n\nBefore opening any issue, please look up the existing issues to avoid submitting a duplication.\nIf you find a match, you can \"subscribe\" to it to get notified on updates. If you have additional helpful information about the issue, please leave a comment.\n\nWhen reporting issues, always include:\n\n* Which version you are using.\n* Steps to reproduce the issue.\n* Snapshots or log files if needed\n\nBecause the issues are open to the public, when submitting files, be sure to remove any sensitive information, e.g. user name, password, IP address, and company name. You can\nreplace those parts with \"REDACTED\" or other strings like \"****\".\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "Makefile",
    "content": "GOCMD=go\nGOBUILD=$(GOCMD) build\nGOCLEAN=$(GOCMD) clean\nGOARCH=$(shell go env GOARCH)\nGOOS=$(shell go env GOOS )\n\nBASE_PATH := $(shell pwd)\nBUILD_PATH = $(BASE_PATH)/build\nWEB_PATH=$(BASE_PATH)/frontend\nASSERT_PATH= $(BASE_PATH)/core/cmd/server/web/assets\n\nCORE_PATH=$(BASE_PATH)/core\nCORE_MAIN=$(CORE_PATH)/cmd/server/main.go\nCORE_NAME=1panel-core\n\nAGENT_PATH=$(BASE_PATH)/agent\nAGENT_MAIN=$(AGENT_PATH)/cmd/server/main.go\nAGENT_NAME=1panel-agent\n\n\nclean_assets:\n\trm -rf $(ASSERT_PATH)\n\nupx_bin:\n\tupx $(BUILD_PATH)/$(CORE_NAME)\n\tupx $(BUILD_PATH)/$(AGENT_NAME)\n\nbuild_frontend:\n\tcd $(WEB_PATH) && npm install && npm run build:pro\n\nbuild_core_on_linux:\n\tcd $(CORE_PATH) \\\n\t&& CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)\n\nbuild_agent_on_linux:\n\tcd $(AGENT_PATH) \\\n    && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)\n\nbuild_core_on_darwin:\n\tcd $(CORE_PATH) \\\n\t&&  CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w'  -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)\n\nbuild_agent_on_darwin:\n\tcd $(AGENT_PATH) \\\n    &&  CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w'  -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)\n\nbuild_all: build_frontend build_core_on_linux build_agent_on_linux\n\nbuild_on_local: clean_assets build_frontend build_core_on_darwin build_agent_on_darwin\n"
  },
  {
    "path": "OWNERS",
    "content": "reviewers:\n- wanghe-fit2cloud\n- zhengkunwang223\n- ssongliu\n\napprovers:\n- wanghe-fit2cloud\n- zhengkunwang223\n- ssongliu\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<h3 align=\"center\">Take control of your VPS with 1Panel</h3>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 240px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a><br>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n## What is 1Panel? \n\n1Panel is a modern, open-source VPS control panel that streamlines administration through an intuitive, clean web interface. \n\n## Key Features  \n\n- **One-Click OpenClaw Deployment**: Seamlessly deploy OpenClaw agents, host local LLMs using Ollama, and monitor GPU utilization—all within a unified workspace.  \n- **One-Click Website Deployment**: Launch production-ready websites instantly with automatic domain binding, SSL certificate provisioning, and configuration—zero manual setup required.  \n- **Curated App Marketplace**: Discover, install, and update trusted open-source applications (Nextcloud, Bitwarden, Umami, and more) with a single click.  \n- **Centralized Server Management**: Monitor system resources and manage files, databases, containers, and services effortlessly through a unified, intuitive dashboard.  \n- **Enterprise-Grade Security**: Strengthen your defenses with container isolation, firewall controls, audit logging, and secure deployment practices—ensuring compliance and peace of mind.  \n- **Intelligent Backup & Restore**: Schedule automated backups or perform instant restores to local storage or major cloud platforms (AWS S3, Cloudflare R2, and more), with flexible retention and destination options.  \n\n## Quick Start\n\nExecute the script below and follow the prompts to install 1Panel:\n\n```bash\nbash -c \"$(curl -sSL https://resource.1panel.pro/v2/quick_start.sh)\"\n```\n\n## Screenshot\n\n![UI Display](https://resource.1panel.pro/img/overview_en_v2.png)\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Pro Edition\n\nCompared to the OSS Edition, 1Panel Pro Edition provides users with a wealth of enhanced features and technical support services. Enhanced features include WAF enhancement, website tamper protection, website monitoring, GPU monitoring, custom logo and theme color, etc. [Click to view the detailed introduction of the Pro Edition](https://1panel.pro/pricing).\n\n## Security Information\n\nIf you discover any security issues, please refer to [SECURITY.md](/SECURITY.md).\n\n## License\n\nLicensed under The GNU General Public License version 3 (GPLv3)  (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security policy\n\n## Supported versions\n\n| Version | Supported          |\n| ------- | ------------------ |\n| v1.x     | :white_check_mark: |\n\n## Reporting a vulnerability\n\nWe first appreciate and are very thankful that you've found a vulnerability issue in 1Panel! By disclosing such issue to 1Panel development team you are helping 1Panel to become a much more safer project than before! ;)\n\nTo protect the existing users of 1Panel, we kindly ask you to not disclose the vulnerability to anyone except the 1Panel development team before a fix has been rolled out. Send an email to `wanghe@fit2cloud.com` instead.\n"
  },
  {
    "path": "agent/app/api/v2/agents.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags AI\n// @Summary Create Agent\n// @Accept json\n// @Param request body dto.AgentCreateReq true \"request\"\n// @Success 200 {object} dto.AgentItem\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents [post]\nfunc (b *BaseApi) CreateAgent(c *gin.Context) {\n\tvar req dto.AgentCreateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := agentService.Create(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags AI\n// @Summary Page Agents\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/search [post]\nfunc (b *BaseApi) PageAgents(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, list, err := agentService.Page(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags AI\n// @Summary Delete Agent\n// @Accept json\n// @Param request body dto.AgentDeleteReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/delete [post]\nfunc (b *BaseApi) DeleteAgent(c *gin.Context) {\n\tvar req dto.AgentDeleteReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.Delete(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Reset Agent token\n// @Accept json\n// @Param request body dto.AgentTokenResetReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/token/reset [post]\nfunc (b *BaseApi) ResetAgentToken(c *gin.Context) {\n\tvar req dto.AgentTokenResetReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.ResetToken(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Update Agent model config\n// @Accept json\n// @Param request body dto.AgentModelConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/model/update [post]\nfunc (b *BaseApi) UpdateAgentModelConfig(c *gin.Context) {\n\tvar req dto.AgentModelConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateModelConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get Providers\n// @Success 200 {array} dto.ProviderInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/providers [get]\nfunc (b *BaseApi) GetAgentProviders(c *gin.Context) {\n\tlist, err := agentService.GetProviders()\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags AI\n// @Summary Create Agent account\n// @Accept json\n// @Param request body dto.AgentAccountCreateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts [post]\nfunc (b *BaseApi) CreateAgentAccount(c *gin.Context) {\n\tvar req dto.AgentAccountCreateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.CreateAccount(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Update Agent account\n// @Accept json\n// @Param request body dto.AgentAccountUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts/update [post]\nfunc (b *BaseApi) UpdateAgentAccount(c *gin.Context) {\n\tvar req dto.AgentAccountUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateAccount(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Page Agent accounts\n// @Accept json\n// @Param request body dto.AgentAccountSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts/search [post]\nfunc (b *BaseApi) PageAgentAccounts(c *gin.Context) {\n\tvar req dto.AgentAccountSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, list, err := agentService.PageAccounts(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags AI\n// @Summary List Agent account models\n// @Accept json\n// @Param request body dto.AgentAccountModelReq true \"request\"\n// @Success 200 {array} dto.AgentAccountModel\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts/models [post]\nfunc (b *BaseApi) GetAgentAccountModels(c *gin.Context) {\n\tvar req dto.AgentAccountModelReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tlist, err := agentService.GetAccountModels(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags AI\n// @Summary Create Agent account model\n// @Accept json\n// @Param request body dto.AgentAccountModelCreateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts/models/create [post]\nfunc (b *BaseApi) CreateAgentAccountModel(c *gin.Context) {\n\tvar req dto.AgentAccountModelCreateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.CreateAccountModel(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Update Agent account model\n// @Accept json\n// @Param request body dto.AgentAccountModelUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts/models/update [post]\nfunc (b *BaseApi) UpdateAgentAccountModel(c *gin.Context) {\n\tvar req dto.AgentAccountModelUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateAccountModel(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Delete Agent account model\n// @Accept json\n// @Param request body dto.AgentAccountModelDeleteReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts/models/delete [post]\nfunc (b *BaseApi) DeleteAgentAccountModel(c *gin.Context) {\n\tvar req dto.AgentAccountModelDeleteReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.DeleteAccountModel(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Verify Agent account\n// @Accept json\n// @Param request body dto.AgentAccountVerifyReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts/verify [post]\nfunc (b *BaseApi) VerifyAgentAccount(c *gin.Context) {\n\tvar req dto.AgentAccountVerifyReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.VerifyAccount(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Delete Agent account\n// @Accept json\n// @Param request body dto.AgentAccountDeleteReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/accounts/delete [post]\nfunc (b *BaseApi) DeleteAgentAccount(c *gin.Context) {\n\tvar req dto.AgentAccountDeleteReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.DeleteAccount(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get Agent Feishu channel config\n// @Accept json\n// @Param request body dto.AgentFeishuConfigReq true \"request\"\n// @Success 200 {object} dto.AgentFeishuConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/feishu/get [post]\nfunc (b *BaseApi) GetAgentFeishuConfig(c *gin.Context) {\n\tvar req dto.AgentFeishuConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.GetFeishuConfig(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Update Agent Feishu channel config\n// @Accept json\n// @Param request body dto.AgentFeishuConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/feishu/update [post]\nfunc (b *BaseApi) UpdateAgentFeishuConfig(c *gin.Context) {\n\tvar req dto.AgentFeishuConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateFeishuConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get Agent Telegram channel config\n// @Accept json\n// @Param request body dto.AgentTelegramConfigReq true \"request\"\n// @Success 200 {object} dto.AgentTelegramConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/telegram/get [post]\nfunc (b *BaseApi) GetAgentTelegramConfig(c *gin.Context) {\n\tvar req dto.AgentTelegramConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.GetTelegramConfig(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Update Agent Telegram channel config\n// @Accept json\n// @Param request body dto.AgentTelegramConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/telegram/update [post]\nfunc (b *BaseApi) UpdateAgentTelegramConfig(c *gin.Context) {\n\tvar req dto.AgentTelegramConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateTelegramConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get Agent Discord channel config\n// @Accept json\n// @Param request body dto.AgentDiscordConfigReq true \"request\"\n// @Success 200 {object} dto.AgentDiscordConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/discord/get [post]\nfunc (b *BaseApi) GetAgentDiscordConfig(c *gin.Context) {\n\tvar req dto.AgentDiscordConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.GetDiscordConfig(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Update Agent Discord channel config\n// @Accept json\n// @Param request body dto.AgentDiscordConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/discord/update [post]\nfunc (b *BaseApi) UpdateAgentDiscordConfig(c *gin.Context) {\n\tvar req dto.AgentDiscordConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateDiscordConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get Agent QQ Bot channel config\n// @Accept json\n// @Param request body dto.AgentWecomConfigReq true \"request\"\n// @Success 200 {object} dto.AgentWecomConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/wecom/get [post]\nfunc (b *BaseApi) GetAgentWecomConfig(c *gin.Context) {\n\tvar req dto.AgentWecomConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.GetWecomConfig(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Update Agent WeCom channel config\n// @Accept json\n// @Param request body dto.AgentWecomConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/wecom/update [post]\nfunc (b *BaseApi) UpdateAgentWecomConfig(c *gin.Context) {\n\tvar req dto.AgentWecomConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateWecomConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get Agent DingTalk channel config\n// @Accept json\n// @Param request body dto.AgentDingTalkConfigReq true \"request\"\n// @Success 200 {object} dto.AgentDingTalkConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/dingtalk/get [post]\nfunc (b *BaseApi) GetAgentDingTalkConfig(c *gin.Context) {\n\tvar req dto.AgentDingTalkConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.GetDingTalkConfig(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Update Agent DingTalk channel config\n// @Accept json\n// @Param request body dto.AgentDingTalkConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/dingtalk/update [post]\nfunc (b *BaseApi) UpdateAgentDingTalkConfig(c *gin.Context) {\n\tvar req dto.AgentDingTalkConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateDingTalkConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get Agent QQ Bot channel config\n// @Accept json\n// @Param request body dto.AgentQQBotConfigReq true \"request\"\n// @Success 200 {object} dto.AgentQQBotConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/qqbot/get [post]\nfunc (b *BaseApi) GetAgentQQBotConfig(c *gin.Context) {\n\tvar req dto.AgentQQBotConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.GetQQBotConfig(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Update Agent QQ Bot channel config\n// @Accept json\n// @Param request body dto.AgentQQBotConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/qqbot/update [post]\nfunc (b *BaseApi) UpdateAgentQQBotConfig(c *gin.Context) {\n\tvar req dto.AgentQQBotConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateQQBotConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Install Agent plugin\n// @Accept json\n// @Param request body dto.AgentPluginInstallReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/plugin/install [post]\nfunc (b *BaseApi) InstallAgentPlugin(c *gin.Context) {\n\tvar req dto.AgentPluginInstallReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.InstallPlugin(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Check Agent plugin installation status\n// @Accept json\n// @Param request body dto.AgentPluginCheckReq true \"request\"\n// @Success 200 {object} dto.AgentPluginStatus\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/plugin/check [post]\nfunc (b *BaseApi) CheckAgentPlugin(c *gin.Context) {\n\tvar req dto.AgentPluginCheckReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.CheckPlugin(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Get Agent Security config\n// @Accept json\n// @Param request body dto.AgentSecurityConfigReq true \"request\"\n// @Success 200 {object} dto.AgentSecurityConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/security/get [post]\nfunc (b *BaseApi) GetAgentSecurityConfig(c *gin.Context) {\n\tvar req dto.AgentSecurityConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.GetSecurityConfig(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Update Agent Security config\n// @Accept json\n// @Param request body dto.AgentSecurityConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/security/update [post]\nfunc (b *BaseApi) UpdateAgentSecurityConfig(c *gin.Context) {\n\tvar req dto.AgentSecurityConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateSecurityConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get Agent Other config\n// @Accept json\n// @Param request body dto.AgentOtherConfigReq true \"request\"\n// @Success 200 {object} dto.AgentOtherConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/other/get [post]\nfunc (b *BaseApi) GetAgentOtherConfig(c *gin.Context) {\n\tvar req dto.AgentOtherConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := agentService.GetOtherConfig(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags AI\n// @Summary Update Agent Other config\n// @Accept json\n// @Param request body dto.AgentOtherConfigUpdateReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/other/update [post]\nfunc (b *BaseApi) UpdateAgentOtherConfig(c *gin.Context) {\n\tvar req dto.AgentOtherConfigUpdateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.UpdateOtherConfig(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Approve Agent channel pairing code\n// @Accept json\n// @Param request body dto.AgentChannelPairingApproveReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/agents/channel/pairing/approve [post]\nfunc (b *BaseApi) ApproveAgentChannelPairing(c *gin.Context) {\n\tvar req dto.AgentChannelPairingApproveReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := agentService.ApproveChannelPairing(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/ai.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags AI\n// @Summary Create Ollama model\n// @Accept json\n// @Param request body dto.OllamaModelName true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/ollama/model [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"添加 Ollama 模型 [name]\",\"formatEN\":\"add Ollama model [name]\"}\nfunc (b *BaseApi) CreateOllamaModel(c *gin.Context) {\n\tvar req dto.OllamaModelName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := aiToolService.Create(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, nil)\n}\n\n// @Tags AI\n// @Summary Rereate Ollama model\n// @Accept json\n// @Param request body dto.OllamaModelName true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/ollama/model/recreate [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"添加 Ollama 模型重试 [name]\",\"formatEN\":\"re-add Ollama model [name]\"}\nfunc (b *BaseApi) RecreateOllamaModel(c *gin.Context) {\n\tvar req dto.OllamaModelName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := aiToolService.Recreate(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, nil)\n}\n\n// @Tags AI\n// @Summary Close Ollama model conn\n// @Accept json\n// @Param request body dto.OllamaModelName true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/ollama/close [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"关闭 Ollama 模型连接 [name]\",\"formatEN\":\"close conn for Ollama model [name]\"}\nfunc (b *BaseApi) CloseOllamaModel(c *gin.Context) {\n\tvar req dto.OllamaModelName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := aiToolService.Close(req.Name); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, nil)\n}\n\n// @Tags AI\n// @Summary Sync Ollama model list\n// @Success 200 {array} dto.OllamaModelDropList\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/ollama/model/sync [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"同步 Ollama 模型列表\",\"formatEN\":\"sync Ollama model list\"}\nfunc (b *BaseApi) SyncOllamaModel(c *gin.Context) {\n\tlist, err := aiToolService.Sync()\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags AI\n// @Summary Page Ollama models\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/ollama/model/search [post]\nfunc (b *BaseApi) SearchOllamaModel(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := aiToolService.Search(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags AI\n// @Summary Page Ollama models\n// @Accept json\n// @Param request body dto.OllamaModelName true \"request\"\n// @Success 200 {string} details\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/ollama/model/load [post]\nfunc (b *BaseApi) LoadOllamaModelDetail(c *gin.Context) {\n\tvar req dto.OllamaModelName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdetail, err := aiToolService.LoadDetail(req.Name)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, detail)\n}\n\n// @Tags AI\n// @Summary Delete Ollama model\n// @Accept json\n// @Param request body dto.ForceDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/ollama/model/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"ollama_models\",\"output_column\":\"name\",\"output_value\":\"names\"}],\"formatZH\":\"删除 Ollama 模型 [names]\",\"formatEN\":\"remove Ollama model [names]\"}\nfunc (b *BaseApi) DeleteOllamaModel(c *gin.Context) {\n\tvar req dto.ForceDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := aiToolService.Delete(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Load gpu / xpu info\n// @Accept json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/gpu/load [get]\nfunc (b *BaseApi) LoadGpuInfo(c *gin.Context) {\n\tok, client := gpu.New()\n\tif ok {\n\t\tinfo, err := client.LoadGpuInfo()\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\thelper.SuccessWithData(c, info)\n\t\treturn\n\t}\n\txpuOK, xpuClient := xpu.New()\n\tif xpuOK {\n\t\tinfo, err := xpuClient.LoadGpuInfo()\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\thelper.SuccessWithData(c, info)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, &common.GpuInfo{})\n}\n\n// @Tags AI\n// @Summary Bind domain\n// @Accept json\n// @Param request body dto.OllamaBindDomain true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/domain/bind [post]\nfunc (b *BaseApi) BindDomain(c *gin.Context) {\n\tvar req dto.OllamaBindDomain\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := aiToolService.BindDomain(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags AI\n// @Summary Get bind domain\n// @Accept json\n// @Param request body dto.OllamaBindDomainReq true \"request\"\n// @Success 200 {object} dto.OllamaBindDomainRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/domain/get [post]\nfunc (b *BaseApi) GetBindDomain(c *gin.Context) {\n\tvar req dto.OllamaBindDomainReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := aiToolService.GetBindDomain(req)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// Tags AI\n// Summary Update bind domain\n// Accept json\n// Param request body dto.OllamaBindDomain true \"request\"\n// Success 200\n// Security ApiKeyAuth\n// Security Timestamp\n// Router /ai/domain/update [post]\nfunc (b *BaseApi) UpdateBindDomain(c *gin.Context) {\n\tvar req dto.OllamaBindDomain\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := aiToolService.UpdateBindDomain(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/alert.go",
    "content": "package v2\n\nimport (\n\t\"errors\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (b *BaseApi) PageAlert(c *gin.Context) {\n\tvar req dto.AlertSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, alerts, err := alertService.PageAlert(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: alerts,\n\t})\n}\n\nfunc (b *BaseApi) GetAlerts(c *gin.Context) {\n\talerts, err := alertService.GetAlerts()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, alerts)\n}\n\nfunc (b *BaseApi) CreateAlert(c *gin.Context) {\n\tvar req dto.AlertCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := alertService.CreateAlert(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) DeleteAlert(c *gin.Context) {\n\tvar req dto.DeleteRequest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := alertService.DeleteAlert(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) UpdateAlert(c *gin.Context) {\n\tvar req dto.AlertUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := alertService.UpdateAlert(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) GetAlert(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, errors.New(\"no such id in request param\"))\n\t\treturn\n\t}\n\talert, err := alertService.GetAlert(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, alert)\n}\n\nfunc (b *BaseApi) UpdateAlertStatus(c *gin.Context) {\n\tvar req dto.AlertUpdateStatus\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := alertService.UpdateStatus(req.ID, req.Status); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) GetDisks(c *gin.Context) {\n\talerts, err := alertService.GetDisks()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, alerts)\n}\n\nfunc (b *BaseApi) PageAlertLogs(c *gin.Context) {\n\tvar req dto.AlertLogSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, alertLogs, err := alertService.PageAlertLogs(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: alertLogs,\n\t})\n}\n\nfunc (b *BaseApi) CleanAlertLogs(c *gin.Context) {\n\tif err := alertService.CleanAlertLogs(); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) GetClams(c *gin.Context) {\n\tclams, err := alertService.GetClams()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, clams)\n}\n\nfunc (b *BaseApi) GetCronJobs(c *gin.Context) {\n\tvar req dto.CronJobReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tcronJobs, err := alertService.GetCronJobs(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, cronJobs)\n}\n\nfunc (b *BaseApi) GetAlertConfig(c *gin.Context) {\n\tconfig, err := alertService.GetAlertConfig()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, config)\n}\n\nfunc (b *BaseApi) UpdateAlertConfig(c *gin.Context) {\n\tvar req dto.AlertConfigUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := alertService.UpdateAlertConfig(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) DeleteAlertConfig(c *gin.Context) {\n\tvar req dto.DeleteRequest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := alertService.DeleteAlertConfig(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) TestAlertConfig(c *gin.Context) {\n\tvar req dto.AlertConfigTest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tflag, err := alertService.TestAlertConfig(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, flag)\n}\n"
  },
  {
    "path": "agent/app/api/v2/app.go",
    "content": "package v2\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/appicon\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags App\n// @Summary List apps\n// @Accept json\n// @Param request body request.AppSearch true \"request\"\n// @Success 200 {object} response.AppRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/search [post]\nfunc (b *BaseApi) SearchApp(c *gin.Context) {\n\tvar req request.AppSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tlist, err := appService.PageApp(c, req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithDataGzipped(c, list)\n}\n\n// @Tags App\n// @Summary Sync remote app list\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/sync/remote [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"应用商店同步\",\"formatEN\":\"App store synchronization\"}\nfunc (b *BaseApi) SyncApp(c *gin.Context) {\n\tvar req dto.OperateWithTask\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := appService.GetAppUpdate()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tif !res.CanUpdate {\n\t\tif res.IsSyncing {\n\t\t\thelper.SuccessWithMsg(c, i18n.GetMsgByKey(\"AppStoreIsSyncing\"))\n\t\t} else {\n\t\t\thelper.SuccessWithMsg(c, i18n.GetMsgByKey(\"AppStoreIsUpToDate\"))\n\t\t}\n\t\treturn\n\t}\n\tif err = appService.SyncAppListFromRemote(req.TaskID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Sync local  app list\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/sync/local [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"应用商店同步\",\"formatEN\":\"App store synchronization\"}\nfunc (b *BaseApi) SyncLocalApp(c *gin.Context) {\n\tvar req dto.OperateWithTask\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tgo appService.SyncAppListFromLocal(req.TaskID)\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Search app by key\n// @Accept json\n// @Param key path string true \"app key\"\n// @Success 200 {object} response.AppDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/:key [get]\nfunc (b *BaseApi) GetApp(c *gin.Context) {\n\tappKey, err := helper.GetStrParamByKey(c, \"key\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tappDTO, err := appService.GetApp(c, appKey)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, appDTO)\n}\n\n// @Tags App\n// @Summary Search app detail by appid\n// @Accept json\n// @Param appId path integer true \"app id\"\n// @Param version path string true \"app 版本\"\n// @Param version path string true \"app 类型\"\n// @Success 200 {object} response.AppDetailDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/detail/:appId/:version/:type [get]\nfunc (b *BaseApi) GetAppDetail(c *gin.Context) {\n\tappID, err := helper.GetIntParamByKey(c, \"appId\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tversion := c.Param(\"version\")\n\tappType := c.Param(\"type\")\n\tappDetailDTO, err := appService.GetAppDetail(appID, version, appType)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, appDetailDTO)\n}\n\n// @Tags App\n// @Summary Get app detail by id\n// @Accept json\n// @Param appId path integer true \"id\"\n// @Success 200 {object} response.AppDetailDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/details/:id [get]\nfunc (b *BaseApi) GetAppDetailByID(c *gin.Context) {\n\tappDetailID, err := helper.GetIntParamByKey(c, \"id\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tappDetailDTO, err := appService.GetAppDetailByID(appDetailID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, appDetailDTO)\n}\n\n// @Tags App\n// @Summary Install app\n// @Accept json\n// @Param request body request.AppInstallCreate true \"request\"\n// @Success 200 {object} model.AppInstall\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/install [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"安装应用 [name]\",\"formatEN\":\"Install app [name]\"}\nfunc (b *BaseApi) InstallApp(c *gin.Context) {\n\tvar req request.AppInstallCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tinstall, err := appService.Install(req, true)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, install)\n}\n\nfunc (b *BaseApi) GetAppTags(c *gin.Context) {\n\ttags, err := appService.GetAppTags(c)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, tags)\n}\n\n// @Tags App\n// @Summary Get app list update\n// @Success 200 {object} response.AppUpdateRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/checkupdate [get]\nfunc (b *BaseApi) GetAppListUpdate(c *gin.Context) {\n\tres, err := appService.GetAppUpdate()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags App\n// @Summary Get app icon by app_id\n// @Accept json\n// @Param appId path integer true \"app id\"\n// @Success 200 {file} file \"app icon\"\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/icon/:key [get]\nfunc (b *BaseApi) GetAppIcon(c *gin.Context) {\n\tappKey, err := helper.GetStrParamByKey(c, \"key\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\ticonBytes, filename, etag, err := appService.GetAppIcon(appKey)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\tif len(iconBytes) == 0 {\n\t\tc.Status(http.StatusNoContent)\n\t\treturn\n\t}\n\n\tc.Header(\"Cache-Control\", \"public, max-age=2592000\")\n\n\tif etag != \"\" && filename != \"\" {\n\t\tc.Header(\"ETag\", etag)\n\t\tif c.GetHeader(\"If-None-Match\") == etag {\n\t\t\tc.Status(http.StatusNotModified)\n\t\t\treturn\n\t\t}\n\t}\n\n\tc.Data(http.StatusOK, appicon.ContentTypePNG, iconBytes)\n}\n\n// @Tags App\n// @Summary Search app detail by appkey and version\n// @Accept json\n// @Param appId path integer true \"app key\"\n// @Param version path string true \"app version\"\n// @Success 200 {object} response.AppDetailSimpleDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/detail/node/:appKey/:version [get]\nfunc (b *BaseApi) GetAppDetailForNode(c *gin.Context) {\n\tappKey := c.Param(\"appKey\")\n\tversion := c.Param(\"version\")\n\tappDetailDTO, err := appService.GetAppDetailByKey(appKey, version)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, appDetailDTO)\n}\n"
  },
  {
    "path": "agent/app/api/v2/app_ignore_upgrade.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags App\n// @Summary List Upgrade Ignored App\n// @Accept json\n// @Success 200 {array} model.AppIgnoreUpgrade\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/ignored/detail [get]\nfunc (b *BaseApi) ListAppIgnored(c *gin.Context) {\n\tres, err := appIgnoreUpgradeService.List()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags App\n// @Summary Ignore Upgrade App\n// @Accept json\n// @Param request body request.AppIgnoreUpgradeReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/ignore [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"忽略应用升级\",\"formatEN\":\"Ignore application upgrade\"}\nfunc (b *BaseApi) IgnoreAppUpgrade(c *gin.Context) {\n\tvar req request.AppIgnoreUpgradeReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := appIgnoreUpgradeService.CreateAppIgnore(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// 写一个去掉忽略的接口\n// @Tags App\n// @Summary Cancel Ignore Upgrade App\n// @Accept json\n// @Param request body request.ReqWithID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/ignored/cancel [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"取消忽略应用升级\",\"formatEN\":\"Cancel ignore application upgrade\"}\nfunc (b *BaseApi) CancelIgnoreAppUpgrade(c *gin.Context) {\n\tvar req request.ReqWithID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := appIgnoreUpgradeService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/app_install.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags App\n// @Summary Page app installed\n// @Accept json\n// @Param request body request.AppInstalledSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/search [post]\nfunc (b *BaseApi) SearchAppInstalled(c *gin.Context) {\n\tvar req request.AppInstalledSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif req.All {\n\t\tlist, err := appInstallService.SearchForWebsite(req)\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\thelper.SuccessWithData(c, dto.PageResult{\n\t\t\tItems: list,\n\t\t\tTotal: int64(len(list)),\n\t\t})\n\t} else {\n\t\ttotal, list, err := appInstallService.Page(req)\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\thelper.SuccessWithData(c, dto.PageResult{\n\t\t\tItems: list,\n\t\t\tTotal: total,\n\t\t})\n\t}\n}\n\n// @Tags App\n// @Summary List app installed\n// @Accept json\n// @Success 200 {array} dto.AppInstallInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/list [get]\nfunc (b *BaseApi) ListAppInstalled(c *gin.Context) {\n\tlist, err := appInstallService.GetInstallList()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags App\n// @Summary Check app installed\n// @Accept json\n// @Param request body request.AppInstalledInfo true \"request\"\n// @Success 200 {object} response.AppInstalledCheck\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/check [post]\nfunc (b *BaseApi) CheckAppInstalled(c *gin.Context) {\n\tvar req request.AppInstalledInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tcheckData, err := appInstallService.CheckExist(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, checkData)\n}\n\n// @Tags App\n// @Summary Search app port by key\n// @Accept json\n// @Param request body dto.OperationWithNameAndType true \"request\"\n// @Success 200 {integer} port\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/loadport [post]\nfunc (b *BaseApi) LoadPort(c *gin.Context) {\n\tvar req dto.OperationWithNameAndType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tport, err := appInstallService.LoadPort(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, port)\n}\n\n// @Tags App\n// @Summary Search app password by key\n// @Accept json\n// @Param request body dto.OperationWithNameAndType true \"request\"\n// @Success 200 {object} response.DatabaseConn\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/conninfo [post]\nfunc (b *BaseApi) LoadConnInfo(c *gin.Context) {\n\tvar req dto.OperationWithNameAndType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tconn, err := appInstallService.LoadConnInfo(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, conn)\n}\n\n// @Tags App\n// @Summary Check before delete\n// @Accept json\n// @Param appInstallId path integer true \"App install id\"\n// @Success 200 {array} dto.AppResource\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/delete/check/:appInstallId [get]\nfunc (b *BaseApi) DeleteCheck(c *gin.Context) {\n\tappInstallId, err := helper.GetIntParamByKey(c, \"appInstallId\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tcheckData, err := appInstallService.DeleteCheck(appInstallId)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, checkData)\n}\n\n// Sync app installed\n// @Tags App\n// @Summary Sync app installed\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/sync [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"同步已安装应用列表\",\"formatEN\":\"Sync the list of installed apps\"}\nfunc (b *BaseApi) SyncInstalled(c *gin.Context) {\n\tif err := appInstallService.SyncAll(false); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Operate installed app\n// @Accept json\n// @Param request body request.AppInstalledOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/op [post]\n// @x-panel-log {\"bodyKeys\":[\"installId\",\"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"installId\",\"isList\":false,\"db\":\"app_installs\",\"output_column\":\"app_id\",\"output_value\":\"appId\"},{\"input_column\":\"id\",\"input_value\":\"installId\",\"isList\":false,\"db\":\"app_installs\",\"output_column\":\"name\",\"output_value\":\"appName\"},{\"input_column\":\"id\",\"input_value\":\"appId\",\"isList\":false,\"db\":\"apps\",\"output_column\":\"key\",\"output_value\":\"appKey\"}],\"formatZH\":\"[operate] 应用 [appKey][appName]\",\"formatEN\":\"[operate] App [appKey][appName]\"}\nfunc (b *BaseApi) OperateInstalled(c *gin.Context) {\n\tvar req request.AppInstalledOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := appInstallService.Operate(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Search app service by key\n// @Accept json\n// @Param key path string true \"request\"\n// @Success 200 {array} response.AppService\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/services/:key [get]\nfunc (b *BaseApi) GetServices(c *gin.Context) {\n\tkey := c.Param(\"key\")\n\tservices, err := appInstallService.GetServices(key)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, services)\n}\n\n// @Tags App\n// @Summary Search app update version by install id\n// @Accept json\n// @Param appInstallId path integer true \"request\"\n// @Success 200 {array} dto.AppVersion\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/update/versions [post]\nfunc (b *BaseApi) GetUpdateVersions(c *gin.Context) {\n\tvar req request.AppUpdateVersion\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tversions, err := appInstallService.GetUpdateVersions(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, versions)\n}\n\n// @Tags App\n// @Summary Change app port\n// @Accept json\n// @Param request body request.PortUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/port/change [post]\n// @x-panel-log {\"bodyKeys\":[\"key\",\"name\",\"port\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"应用端口修改 [key]-[name] => [port]\",\"formatEN\":\"Application port update [key]-[name] => [port]\"}\nfunc (b *BaseApi) ChangeAppPort(c *gin.Context) {\n\tvar req request.PortUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := appInstallService.ChangeAppPort(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Search default config by key\n// @Accept json\n// @Param request body dto.OperationWithNameAndType true \"request\"\n// @Success 200 {string} content\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/conf [post]\nfunc (b *BaseApi) GetDefaultConfig(c *gin.Context) {\n\tvar req dto.OperationWithNameAndType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tcontent, err := appInstallService.GetDefaultConfigByKey(req.Type, req.Name)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, content)\n}\n\n// @Tags App\n// @Summary Search params by appInstallId\n// @Accept json\n// @Param appInstallId path string true \"request\"\n// @Success 200 {object} response.AppConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/params/:appInstallId [get]\nfunc (b *BaseApi) GetParams(c *gin.Context) {\n\tappInstallId, err := helper.GetIntParamByKey(c, \"appInstallId\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tcontent, err := appInstallService.GetParams(appInstallId)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, content)\n}\n\n// @Tags App\n// @Summary Change app params\n// @Accept json\n// @Param request body request.AppInstalledUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/params/update [post]\n// @x-panel-log {\"bodyKeys\":[\"installId\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"installId\",\"isList\":false,\"db\":\"app_installs\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"应用参数修改 [name]\",\"formatEN\":\"Application param update [name]\"}\nfunc (b *BaseApi) UpdateInstalled(c *gin.Context) {\n\tvar req request.AppInstalledUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := appInstallService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Update app config\n// @Accept json\n// @Param request body request.AppConfigUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/config/update [post]\n// @x-panel-log {\"bodyKeys\":[\"installID\",\"webUI\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"应用配置更新 [installID]\",\"formatEN\":\"Application config update [installID]\"}\nfunc (b *BaseApi) UpdateAppConfig(c *gin.Context) {\n\tvar req request.AppConfigUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := appInstallService.UpdateAppConfig(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Get app install info\n// @Accept json\n// @Param appInstallId path integer true \"App install id\"\n// @Success 200 {object} dto.AppInstallInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /apps/installed/info/:appInstallId [get]\nfunc (b *BaseApi) GetAppInstallInfo(c *gin.Context) {\n\tappInstallId, err := helper.GetIntParamByKey(c, \"appInstallId\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tinfo, err := appInstallService.GetAppInstallInfo(appInstallId)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, info)\n}\n\nfunc (b *BaseApi) UpdateAppInstallSort(c *gin.Context) {\n\tvar req request.AppInstallSort\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := appInstallService.UpdateSort(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/backup.go",
    "content": "package v2\n\nimport (\n\t\"fmt\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (b *BaseApi) CheckBackupUsed(c *gin.Context) {\n\tname, err := helper.GetStrParamByKey(c, \"name\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\n\tif err := backupService.CheckUsed(name, true); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Check backup account\n// @Accept json\n// @Param request body dto.BackupOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/check [post]\nfunc (b *BaseApi) CheckBackup(c *gin.Context) {\n\tvar req dto.BackupOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, backupService.CheckConn(req))\n}\n\n// @Tags Backup Account\n// @Summary Create backup account\n// @Accept json\n// @Param request body dto.BackupOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups [post]\n// @x-panel-log {\"bodyKeys\":[\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建备份账号 [type]\",\"formatEN\":\"create backup account [type]\"}\nfunc (b *BaseApi) CreateBackup(c *gin.Context) {\n\tvar req dto.BackupOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Refresh token\n// @Accept json\n// @Param request body dto.BackupOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/refresh/token [post]\nfunc (b *BaseApi) RefreshToken(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := backupService.RefreshToken(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary List buckets\n// @Accept json\n// @Param request body dto.ForBuckets true \"request\"\n// @Success 200 {array} object\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/buckets [post]\nfunc (b *BaseApi) ListBuckets(c *gin.Context) {\n\tvar req dto.ForBuckets\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tbuckets, err := backupService.GetBuckets(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, buckets)\n}\n\n// @Tags Backup Account\n// @Summary Delete backup account\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"backup_accounts\",\"output_column\":\"type\",\"output_value\":\"types\"}],\"formatZH\":\"删除备份账号 [types]\",\"formatEN\":\"delete backup account [types]\"}\nfunc (b *BaseApi) DeleteBackup(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupService.Delete(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Update backup account\n// @Accept json\n// @Param request body dto.BackupOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/update [post]\n// @x-panel-log {\"bodyKeys\":[\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新备份账号 [types]\",\"formatEN\":\"update backup account [types]\"}\nfunc (b *BaseApi) UpdateBackup(c *gin.Context) {\n\tvar req dto.BackupOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Upload file for recover\n// @Accept json\n// @Param request body dto.UploadForRecover true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/upload [post]\n// @x-panel-log {\"bodyKeys\":[\"filePath\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"上传备份文件 [filePath]\",\"formatEN\":\"upload backup file [filePath]\"}\nfunc (b *BaseApi) UploadForRecover(c *gin.Context) {\n\tvar req dto.UploadForRecover\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupService.UploadForRecover(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Load backup account options\n// @Accept json\n// @Success 200 {array} dto.BackupOption\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/options [get]\nfunc (b *BaseApi) LoadBackupOptions(c *gin.Context) {\n\tlist, err := backupService.LoadBackupOptions()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Backup Account\n// @Summary Search backup accounts with page\n// @Accept json\n// @Param request body dto.SearchPageWithType true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/search [post]\nfunc (b *BaseApi) SearchBackup(c *gin.Context) {\n\tvar req dto.SearchPageWithType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := backupService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Backup Account\n// @Summary get local backup dir\n// @Success 200 {string} dir\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/local [get]\nfunc (b *BaseApi) GetLocalDir(c *gin.Context) {\n\tdir, err := backupService.GetLocalDir()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dir)\n}\n\n// @Tags Backup Account\n// @Summary Load backup record size\n// @Accept json\n// @Param request body dto.SearchForSize true \"request\"\n// @Success 200 {array} dto.RecordFileSize\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/record/size [post]\nfunc (b *BaseApi) LoadBackupRecordSize(c *gin.Context) {\n\tvar req dto.SearchForSize\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tlist, err := backupRecordService.LoadRecordSize(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Backup Account\n// @Summary Page backup records\n// @Accept json\n// @Param request body dto.RecordSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/record/search [post]\nfunc (b *BaseApi) SearchBackupRecords(c *gin.Context) {\n\tvar req dto.RecordSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := backupRecordService.SearchRecordsWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Backup Account\n// @Summary Page backup records by cronjob\n// @Accept json\n// @Param request body dto.RecordSearchByCronjob true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/record/search/bycronjob [post]\nfunc (b *BaseApi) SearchBackupRecordsByCronjob(c *gin.Context) {\n\tvar req dto.RecordSearchByCronjob\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := backupRecordService.SearchRecordsByCronjobWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Backup Account\n// @Summary Download backup record\n// @Accept json\n// @Param request body dto.DownloadRecord true \"request\"\n// @Success 200 {string} filePath\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/record/download [post]\n// @x-panel-log {\"bodyKeys\":[\"source\",\"fileName\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"下载备份记录 [source][fileName]\",\"formatEN\":\"download backup records [source][fileName]\"}\nfunc (b *BaseApi) DownloadRecord(c *gin.Context) {\n\tvar req dto.DownloadRecord\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tfilePath, err := backupRecordService.DownloadRecord(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, filePath)\n}\n\n// @Tags Backup Account\n// @Summary Update backup record description\n// @Accept json\n// @Param request body dto.UpdateDescription true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/record/description/update [post]\nfunc (b *BaseApi) UpdateRecordDescription(c *gin.Context) {\n\tvar req dto.UpdateDescription\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupRecordService.UpdateDescription(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Delete backup record\n// @Accept json\n// @Param request body dto.BatchDeleteReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/record/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"backup_records\",\"output_column\":\"file_name\",\"output_value\":\"files\"}],\"formatZH\":\"删除备份记录 [files]\",\"formatEN\":\"delete backup records [files]\"}\nfunc (b *BaseApi) DeleteBackupRecord(c *gin.Context) {\n\tvar req dto.BatchDeleteReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupRecordService.BatchDeleteRecord(req.Ids); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary List files from backup accounts\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/search/files [post]\nfunc (b *BaseApi) LoadFilesFromBackup(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata := backupRecordService.ListFiles(req)\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Backup Account\n// @Summary Backup system data\n// @Accept json\n// @Param request body dto.CommonBackup true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/backup [post]\n// @x-panel-log {\"bodyKeys\":[\"type\",\"name\",\"detailName\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"备份 [type] 数据 [name][detailName]\",\"formatEN\":\"backup [type] data [name][detailName]\"}\nfunc (b *BaseApi) Backup(c *gin.Context) {\n\tvar req dto.CommonBackup\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tswitch req.Type {\n\tcase \"app\":\n\t\tif _, err := backupService.AppBackup(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"mysql\", \"mariadb\", constant.AppMysqlCluster:\n\t\tif err := backupService.MysqlBackup(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase constant.AppPostgresql, constant.AppPostgresqlCluster:\n\t\tif err := backupService.PostgresqlBackup(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"website\":\n\t\tif err := backupService.WebsiteBackup(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"redis\", constant.AppRedisCluster:\n\t\tif err := backupService.RedisBackup(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"container\":\n\t\tif err := backupService.ContainerBackup(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"compose\":\n\t\tif err := backupService.ComposeBackup(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Recover system data\n// @Accept json\n// @Param request body dto.CommonRecover true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/recover [post]\n// @x-panel-log {\"bodyKeys\":[\"type\",\"name\",\"detailName\",\"file\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"从 [file] 恢复 [type] 数据 [name][detailName]\",\"formatEN\":\"recover [type] data [name][detailName] from [file]\"}\nfunc (b *BaseApi) Recover(c *gin.Context) {\n\tvar req dto.CommonRecover\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdownloadPath, err := backupRecordService.DownloadRecord(dto.DownloadRecord{\n\t\tDownloadAccountID: req.DownloadAccountID,\n\t\tFileDir:           path.Dir(req.File),\n\t\tFileName:          path.Base(req.File),\n\t})\n\tif err != nil {\n\t\thelper.BadRequest(c, fmt.Errorf(\"download file failed, err: %v\", err))\n\t\treturn\n\t}\n\treq.File = downloadPath\n\tswitch req.Type {\n\tcase \"mysql\", \"mariadb\", constant.AppMysqlCluster:\n\t\tif err := backupService.MysqlRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase constant.AppPostgresql, constant.AppPostgresqlCluster:\n\t\tif err := backupService.PostgresqlRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"website\":\n\t\tif err := backupService.WebsiteRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"redis\", constant.AppRedisCluster:\n\t\tif err := backupService.RedisRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"app\":\n\t\tif err := backupService.AppRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"container\":\n\t\tif err := backupService.ContainerRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"compose\":\n\t\tif err := backupService.ComposeRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Recover system data by upload\n// @Accept json\n// @Param request body dto.CommonRecover true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /backups/recover/byupload [post]\n// @x-panel-log {\"bodyKeys\":[\"type\",\"name\",\"detailName\",\"file\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"从 [file] 恢复 [type] 数据 [name][detailName]\",\"formatEN\":\"recover [type] data [name][detailName] from [file]\"}\nfunc (b *BaseApi) RecoverByUpload(c *gin.Context) {\n\tvar req dto.CommonRecover\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tswitch req.Type {\n\tcase \"mysql\", \"mariadb\", constant.AppMysqlCluster:\n\t\tif err := backupService.MysqlRecoverByUpload(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase constant.AppPostgresql, constant.AppPostgresqlCluster:\n\t\tif err := backupService.PostgresqlRecoverByUpload(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"app\":\n\t\tif err := backupService.AppRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"website\":\n\t\tif err := backupService.WebsiteRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"container\":\n\t\tif err := backupService.ContainerRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\tcase \"compose\":\n\t\tif err := backupService.ComposeRecover(req); err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/clam.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Clam\n// @Summary Create clam\n// @Accept json\n// @Param request body dto.ClamCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建扫描规则 [name][path]\",\"formatEN\":\"create clam [name][path]\"}\nfunc (b *BaseApi) CreateClam(c *gin.Context) {\n\tvar req dto.ClamCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := clamService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Clam\n// @Summary Update clam\n// @Accept json\n// @Param request body dto.ClamUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改扫描规则 [name][path]\",\"formatEN\":\"update clam [name][path]\"}\nfunc (b *BaseApi) UpdateClam(c *gin.Context) {\n\tvar req dto.ClamUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := clamService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Clam\n// @Summary Update clam status\n// @Accept json\n// @Param request body dto.ClamUpdateStatus true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/status/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\",\"status\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"clams\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"修改扫描规则 [name] 状态为 [status]\",\"formatEN\":\"change the status of clam [name] to [status].\"}\nfunc (b *BaseApi) UpdateClamStatus(c *gin.Context) {\n\tvar req dto.ClamUpdateStatus\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := clamService.UpdateStatus(req.ID, req.Status); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Clam\n// @Summary Page clam\n// @Accept json\n// @Param request body dto.SearchClamWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/search [post]\nfunc (b *BaseApi) SearchClam(c *gin.Context) {\n\tvar req dto.SearchClamWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := clamService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Clam\n// @Summary Load clam base info\n// @Accept json\n// @Success 200 {object} dto.ClamBaseInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/base [post]\nfunc (b *BaseApi) LoadClamBaseInfo(c *gin.Context) {\n\tinfo, err := clamService.LoadBaseInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, info)\n}\n\n// @Tags Clam\n// @Summary Operate Clam\n// @Accept json\n// @Success 200\n// @Param request body dto.Operate true \"request\"\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operation\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operation] Clam\",\"formatEN\":\"[operation] FTP\"}\nfunc (b *BaseApi) OperateClam(c *gin.Context) {\n\tvar req dto.Operate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := clamService.Operate(req.Operation); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Clam\n// @Summary Clean clam record\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/record/clean [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":true,\"db\":\"clams\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"清空扫描报告 [name]\",\"formatEN\":\"clean clam record [name]\"}\nfunc (b *BaseApi) CleanClamRecord(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := clamService.CleanRecord(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Clam\n// @Summary Page clam record\n// @Accept json\n// @Param request body dto.ClamLogSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/record/search [post]\nfunc (b *BaseApi) SearchClamRecord(c *gin.Context) {\n\tvar req dto.ClamLogSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := clamService.SearchRecords(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Clam\n// @Summary Load clam file\n// @Accept json\n// @Param request body dto.ClamFileReq true \"request\"\n// @Success 200 {string} content\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/file/search [post]\nfunc (b *BaseApi) SearchClamFile(c *gin.Context) {\n\tvar req dto.ClamFileReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tcontent, err := clamService.LoadFile(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, content)\n}\n\n// @Tags Clam\n// @Summary Update clam file\n// @Accept json\n// @Param request body dto.UpdateByNameAndFile true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/file/update [post]\nfunc (b *BaseApi) UpdateFile(c *gin.Context) {\n\tvar req dto.UpdateByNameAndFile\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := clamService.UpdateFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Clam\n// @Summary Delete clam\n// @Accept json\n// @Param request body dto.ClamDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"clams\",\"output_column\":\"name\",\"output_value\":\"names\"}],\"formatZH\":\"删除扫描规则 [names]\",\"formatEN\":\"delete clam [names]\"}\nfunc (b *BaseApi) DeleteClam(c *gin.Context) {\n\tvar req dto.ClamDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := clamService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Clam\n// @Summary Handle clam scan\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clam/handle [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":true,\"db\":\"clams\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"执行病毒扫描 [name]\",\"formatEN\":\"handle clam scan [name]\"}\nfunc (b *BaseApi) HandleClamScan(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := clamService.HandleOnce(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/compose_template.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Container Compose-template\n// @Summary Create compose template\n// @Accept json\n// @Param request body dto.ComposeTemplateCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/template [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建 compose 模版 [name]\",\"formatEN\":\"create compose template [name]\"}\nfunc (b *BaseApi) CreateComposeTemplate(c *gin.Context) {\n\tvar req dto.ComposeTemplateCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := composeTemplateService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Compose-template\n// @Summary Bacth compose template\n// @Accept json\n// @Param request body dto.ComposeTemplateBatch true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/template/batch [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"批量导入编排模版\",\"formatEN\":\"batch import compose templates\"}\nfunc (b *BaseApi) BatchComposeTemplate(c *gin.Context) {\n\tvar req dto.ComposeTemplateBatch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := composeTemplateService.Batch(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Compose-template\n// @Summary Page compose templates\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Produce json\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/template/search [post]\nfunc (b *BaseApi) SearchComposeTemplate(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := composeTemplateService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Container Compose-template\n// @Summary List compose templates\n// @Produce json\n// @Success 200 {array} dto.ComposeTemplateInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/template [get]\nfunc (b *BaseApi) ListComposeTemplate(c *gin.Context) {\n\tlist, err := composeTemplateService.List()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Container Compose-template\n// @Summary Delete compose template\n// @Accept json\n// @Param request body dto.BatchDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/template/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"compose_templates\",\"output_column\":\"name\",\"output_value\":\"names\"}],\"formatZH\":\"删除 compose 模版 [names]\",\"formatEN\":\"delete compose template [names]\"}\nfunc (b *BaseApi) DeleteComposeTemplate(c *gin.Context) {\n\tvar req dto.BatchDeleteReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := composeTemplateService.Delete(req.Ids); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Compose-template\n// @Summary Update compose template\n// @Accept json\n// @Param request body dto.ComposeTemplateUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/template/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"compose_templates\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"更新 compose 模版 [name]\",\"formatEN\":\"update compose template information [name]\"}\nfunc (b *BaseApi) UpdateComposeTemplate(c *gin.Context) {\n\tvar req dto.ComposeTemplateUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tupMap := make(map[string]interface{})\n\tupMap[\"content\"] = req.Content\n\tupMap[\"description\"] = req.Description\n\tif err := composeTemplateService.Update(req.ID, upMap); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/container.go",
    "content": "package v2\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"strconv\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/pkg/errors\"\n)\n\n// @Tags Container\n// @Summary Page containers\n// @Accept json\n// @Param request body dto.PageContainer true \"request\"\n// @Produce json\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/search [post]\nfunc (b *BaseApi) SearchContainer(c *gin.Context) {\n\tvar req dto.PageContainer\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := containerService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Container\n// @Summary Load container users\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Produce json\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/users [post]\nfunc (b *BaseApi) LoadContainerUsers(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, containerService.LoadUsers(req))\n}\n\n// @Tags Container\n// @Summary List container files\n// @Accept json\n// @Param request body dto.ContainerFileReq true \"request\"\n// @Success 200 {array} dto.ContainerFileInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/files/search [post]\nfunc (b *BaseApi) ListContainerFiles(c *gin.Context) {\n\tvar req dto.ContainerFileReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfiles, err := containerService.ListContainerFiles(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, files)\n}\n\n// @Tags Container\n// @Summary Upload container file\n// @Accept multipart/form-data\n// @Param containerID formData string true \"containerID\"\n// @Param path formData string true \"path\"\n// @Param file formData file true \"file\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/files/upload [post]\n// @x-panel-log {\"bodyKeys\":[\"containerID\",\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"容器 [containerID] 上传文件到 [path]\",\"formatEN\":\"Upload file to [path] in container [containerID]\"}\nfunc (b *BaseApi) UploadContainerFile(c *gin.Context) {\n\tform, err := c.MultipartForm()\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tcontainerIDs := form.Value[\"containerID\"]\n\tpaths := form.Value[\"path\"]\n\tuploadFiles := form.File[\"file\"]\n\tif len(containerIDs) == 0 || len(paths) == 0 || len(uploadFiles) == 0 {\n\t\thelper.BadRequest(c, errors.New(\"invalid container file upload params\"))\n\t\treturn\n\t}\n\treq := dto.ContainerFileReq{\n\t\tContainerID: containerIDs[0],\n\t\tPath:        paths[0],\n\t}\n\tfor _, uploadFile := range uploadFiles {\n\t\tfile, err := uploadFile.Open()\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\terr = containerService.UploadContainerFile(req, path.Base(uploadFile.Filename), uploadFile.Size, file)\n\t\t_ = file.Close()\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Get container file content\n// @Accept json\n// @Param request body dto.ContainerFileReq true \"request\"\n// @Success 200 {object} dto.ContainerFileContent\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/files/content [post]\nfunc (b *BaseApi) GetContainerFileContent(c *gin.Context) {\n\tvar req dto.ContainerFileReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tcontent, err := containerService.GetContainerFileContent(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, content)\n}\n\n// @Tags Container\n// @Summary Get container file size\n// @Accept json\n// @Param request body dto.ContainerFileReq true \"request\"\n// @Success 200 {int} size\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/files/size [post]\nfunc (b *BaseApi) GetContainerFileSize(c *gin.Context) {\n\tvar req dto.ContainerFileReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tsize, err := containerService.GetContainerFileSize(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, size)\n}\n\n// @Tags Container\n// @Summary Delete container file\n// @Accept json\n// @Param request body dto.ContainerFileBatchDeleteReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/files/del [post]\n// @x-panel-log {\"bodyKeys\":[\"containerID\",\"paths\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"删除容器 [containerID] 文件 [paths]\",\"formatEN\":\"Delete files [paths] in container [containerID]\"}\nfunc (b *BaseApi) DeleteContainerFile(c *gin.Context) {\n\tvar req dto.ContainerFileBatchDeleteReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := containerService.DeleteContainerFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Download container file\n// @Accept json\n// @Param request body dto.ContainerFileReq true \"request\"\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/files/download [post]\n// @x-panel-log {\"bodyKeys\":[\"containerID\",\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"下载容器 [containerID] 文件 [path]\",\"formatEN\":\"Download file [path] from container [containerID]\"}\nfunc (b *BaseApi) DownloadContainerFile(c *gin.Context) {\n\tvar req dto.ContainerFileReq\n\tif err := c.ShouldBindJSON(&req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tif req.ContainerID == \"\" || req.Path == \"\" {\n\t\thelper.BadRequest(c, errors.New(\"invalid container file download params\"))\n\t\treturn\n\t}\n\treader, fileName, contentType, err := containerService.DownloadContainerFile(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tdefer reader.Close()\n\tc.Header(\"Content-Disposition\", \"attachment; filename*=utf-8''\"+url.PathEscape(fileName))\n\tc.DataFromReader(http.StatusOK, -1, contentType, reader, nil)\n}\n\n// @Tags Container\n// @Summary List containers\n// @Accept json\n// @Produce json\n// @Success 200 {array} dto.ContainerOptions\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/list [post]\nfunc (b *BaseApi) ListContainer(c *gin.Context) {\n\thelper.SuccessWithData(c, containerService.List())\n}\n\n// @Tags Container\n// @Summary List containers by image\n// @Accept json\n// @Produce json\n// @Success 200 {array} dto.ContainerOptions\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/list/byimage [post]\nfunc (b *BaseApi) ListContainerByImage(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, containerService.ListByImage(req.Name))\n}\n\n// @Tags Container\n// @Summary Load containers status\n// @Accept json\n// @Produce json\n// @Success 200 {object} dto.ContainerStatus\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/status [get]\nfunc (b *BaseApi) LoadContainerStatus(c *gin.Context) {\n\tdata, err := containerService.LoadStatus()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Container Compose\n// @Summary Page composes\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/compose/search [post]\nfunc (b *BaseApi) SearchCompose(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := containerService.PageCompose(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Container Compose\n// @Summary Test compose\n// @Accept json\n// @Param request body dto.ComposeCreate true \"request\"\n// @Success 200 {boolean} isOK\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/compose/test [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"检测 compose [name] 格式\",\"formatEN\":\"check compose [name]\"}\nfunc (b *BaseApi) TestCompose(c *gin.Context) {\n\tvar req dto.ComposeCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tisOK, err := containerService.TestCompose(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, isOK)\n}\n\n// @Tags Container Compose\n// @Summary Create compose\n// @Accept json\n// @Param request body dto.ComposeCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/compose [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建 compose [name]\",\"formatEN\":\"create compose [name]\"}\nfunc (b *BaseApi) CreateCompose(c *gin.Context) {\n\tvar req dto.ComposeCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.CreateCompose(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Compose\n// @Summary Operate compose\n// @Accept json\n// @Param request body dto.ComposeOperation true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/compose/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"operation\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"compose [operation] [name]\",\"formatEN\":\"compose [operation] [name]\"}\nfunc (b *BaseApi) OperatorCompose(c *gin.Context) {\n\tvar req dto.ComposeOperation\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ComposeOperation(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Update container\n// @Accept json\n// @Param request body dto.ContainerOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"image\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新容器 [name][image]\",\"formatEN\":\"update container [name][image]\"}\nfunc (b *BaseApi) ContainerUpdate(c *gin.Context) {\n\tvar req dto.ContainerOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ContainerUpdate(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Load container info\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Success 200 {object} dto.ContainerOperate\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/info [post]\nfunc (b *BaseApi) ContainerInfo(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := containerService.ContainerInfo(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Summary Load container limits\n// @Success 200 {object} dto.ResourceLimit\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/limit [get]\nfunc (b *BaseApi) LoadResourceLimit(c *gin.Context) {\n\tdata, err := containerService.LoadResourceLimit()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Summary Load container stats\n// @Success 200 {array} dto.ContainerListStats\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/list/stats [get]\nfunc (b *BaseApi) ContainerListStats(c *gin.Context) {\n\tdata, err := containerService.ContainerListStats()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Summary Load container stats size\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Success 200 {object} dto.ContainerItemStats\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/item/stats [post]\nfunc (b *BaseApi) ContainerItemStats(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := containerService.ContainerItemStats(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Container\n// @Summary Create container\n// @Accept json\n// @Param request body dto.ContainerOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"image\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建容器 [name][image]\",\"formatEN\":\"create container [name][image]\"}\nfunc (b *BaseApi) ContainerCreate(c *gin.Context) {\n\tvar req dto.ContainerOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ContainerCreate(req, true); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Upgrade container\n// @Accept json\n// @Param request body dto.ContainerUpgrade true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/upgrade [post]\n// @x-panel-log {\"bodyKeys\":[\"names\",\"image\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新容器镜像 [names][image]\",\"formatEN\":\"upgrade container image [names][image]\"}\nfunc (b *BaseApi) ContainerUpgrade(c *gin.Context) {\n\tvar req dto.ContainerUpgrade\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ContainerUpgrade(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Clean container\n// @Accept json\n// @Param request body dto.ContainerPrune true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/prune [post]\n// @x-panel-log {\"bodyKeys\":[\"pruneType\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"清理容器 [pruneType]\",\"formatEN\":\"clean container [pruneType]\"}\nfunc (b *BaseApi) ContainerPrune(c *gin.Context) {\n\tvar req dto.ContainerPrune\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.Prune(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Clean container log\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/clean/log [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"清理容器 [name] 日志\",\"formatEN\":\"clean container [name] logs\"}\nfunc (b *BaseApi) CleanContainerLog(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ContainerLogClean(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Clean compose log\n// @Accept json\n// @Param request body dto.ComposeLogClean true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/compose/clean/log [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"清理容器编排 [name] 日志\",\"formatEN\":\"clean compose [name] logs\"}\nfunc (b *BaseApi) CleanComposeLog(c *gin.Context) {\n\tvar req dto.ComposeLogClean\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ComposeLogClean(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Rename Container\n// @Accept json\n// @Param request body dto.ContainerRename true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/rename [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"newName\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"容器重命名 [name] => [newName]\",\"formatEN\":\"rename container [name] => [newName]\"}\nfunc (b *BaseApi) ContainerRename(c *gin.Context) {\n\tvar req dto.ContainerRename\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ContainerRename(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Commit Container\n// @Accept json\n// @Param request body dto.ContainerCommit true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/commit [post]\nfunc (b *BaseApi) ContainerCommit(c *gin.Context) {\n\tvar req dto.ContainerCommit\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ContainerCommit(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Operate Container\n// @Accept json\n// @Param request body dto.ContainerOperation true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"names\",\"operation\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"容器 [names] 执行 [operation]\",\"formatEN\":\"container [operation] [names]\"}\nfunc (b *BaseApi) ContainerOperation(c *gin.Context) {\n\tvar req dto.ContainerOperation\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ContainerOperation(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container\n// @Summary Container stats\n// @Param id path string true \"容器id\"\n// @Success 200 {object} dto.ContainerStats\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/stats/:id [get]\nfunc (b *BaseApi) ContainerStats(c *gin.Context) {\n\tcontainerID, ok := c.Params.Get(\"id\")\n\tif !ok {\n\t\thelper.BadRequest(c, errors.New(\"error container id in path\"))\n\t\treturn\n\t}\n\n\tresult, err := containerService.ContainerStats(containerID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, result)\n}\n\n// @Tags Container\n// @Summary Container inspect\n// @Accept json\n// @Param request body dto.InspectReq true \"request\"\n// @Success 200 {string} result\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/inspect [post]\nfunc (b *BaseApi) Inspect(c *gin.Context) {\n\tvar req dto.InspectReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tresult, err := containerService.Inspect(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, result)\n}\n\nfunc (b *BaseApi) DownloadContainerLogs(c *gin.Context) {\n\tvar req dto.ContainerLog\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := containerService.DownloadContainerLogs(req.ContainerType, req.Container, req.Since, strconv.Itoa(int(req.Tail)), req.Timestamp, c)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t}\n}\n\n// @Tags Container Network\n// @Summary Page networks\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Produce json\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/network/search [post]\nfunc (b *BaseApi) SearchNetwork(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := containerService.PageNetwork(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Container Network\n// @Summary List networks\n// @Accept json\n// @Produce json\n// @Success 200 {array} dto.Options\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/network [get]\nfunc (b *BaseApi) ListNetwork(c *gin.Context) {\n\tlist, err := containerService.ListNetwork()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Container Network\n// @Summary Delete network\n// @Accept json\n// @Param request body dto.BatchDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/network/del [post]\n// @x-panel-log {\"bodyKeys\":[\"names\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"删除容器网络 [names]\",\"formatEN\":\"delete container network [names]\"}\nfunc (b *BaseApi) DeleteNetwork(c *gin.Context) {\n\tvar req dto.BatchDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.DeleteNetwork(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Network\n// @Summary Create network\n// @Accept json\n// @Param request body dto.NetworkCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/network [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建容器网络 name\",\"formatEN\":\"create container network [name]\"}\nfunc (b *BaseApi) CreateNetwork(c *gin.Context) {\n\tvar req dto.NetworkCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.CreateNetwork(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Volume\n// @Summary Page volumes\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Produce json\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/volume/search [post]\nfunc (b *BaseApi) SearchVolume(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := containerService.PageVolume(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Container Volume\n// @Summary List volumes\n// @Accept json\n// @Produce json\n// @Success 200 {array} dto.Options\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/volume [get]\nfunc (b *BaseApi) ListVolume(c *gin.Context) {\n\tlist, err := containerService.ListVolume()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Container Volume\n// @Summary Delete volume\n// @Accept json\n// @Param request body dto.BatchDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/volume/del [post]\n// @x-panel-log {\"bodyKeys\":[\"names\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"删除容器存储卷 [names]\",\"formatEN\":\"delete container volume [names]\"}\nfunc (b *BaseApi) DeleteVolume(c *gin.Context) {\n\tvar req dto.BatchDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.DeleteVolume(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Volume\n// @Summary Create volume\n// @Accept json\n// @Param request body dto.VolumeCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/volume [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建容器存储卷 [name]\",\"formatEN\":\"create container volume [name]\"}\nfunc (b *BaseApi) CreateVolume(c *gin.Context) {\n\tvar req dto.VolumeCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.CreateVolume(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Compose\n// @Summary Update compose\n// @Accept json\n// @Param request body dto.ComposeUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/compose/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新 compose [name]\",\"formatEN\":\"update compose information [name]\"}\nfunc (b *BaseApi) ComposeUpdate(c *gin.Context) {\n\tvar req dto.ComposeUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := containerService.ComposeUpdate(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Compose\n// @Summary Load compose environment variables\n// @Accept json\n// @Param request body dto.FilePath true \"request\"\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/compose/env [post]\nfunc (b *BaseApi) LoadComposeEnv(c *gin.Context) {\n\tvar req dto.FilePath\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := containerService.LoadComposeEnv(req.Path)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Container\n// @Summary Container logs\n// @Param container query string false \"容器名称\"\n// @Param since query string false \"时间筛选\"\n// @Param follow query string false \"是否追踪\"\n// @Param tail query string false \"显示行号\"\n// @Param timestamp query string false \"是否显示时间\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/search/log [get]\nfunc (b *BaseApi) ContainerStreamLogs(c *gin.Context) {\n\tc.Header(\"Content-Type\", \"text/event-stream\")\n\tc.Header(\"Cache-Control\", \"no-cache\")\n\tc.Header(\"Connection\", \"keep-alive\")\n\tc.Header(\"Transfer-Encoding\", \"chunked\")\n\n\tsince := c.Query(\"since\")\n\tfollow := c.Query(\"follow\") == \"true\"\n\ttail := c.Query(\"tail\")\n\ttimestamp := c.Query(\"timestamp\") == \"true\"\n\n\tcontainer := c.Query(\"container\")\n\tcompose := c.Query(\"compose\")\n\tstreamLog := dto.StreamLog{\n\t\tCompose:   compose,\n\t\tContainer: container,\n\t\tSince:     since,\n\t\tFollow:    follow,\n\t\tTail:      tail,\n\t\tTimestamp: timestamp,\n\t\tType:      \"container\",\n\t}\n\tif compose != \"\" {\n\t\tstreamLog.Type = \"compose\"\n\t}\n\n\tcontainerService.StreamLogs(c, streamLog)\n}\n"
  },
  {
    "path": "agent/app/api/v2/cronjob.go",
    "content": "package v2\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Cronjob\n// @Summary Create cronjob\n// @Accept json\n// @Param request body dto.CronjobOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs [post]\n// @x-panel-log {\"bodyKeys\":[\"type\",\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建计划任务 [type][name]\",\"formatEN\":\"create cronjob [type][name]\"}\nfunc (b *BaseApi) CreateCronjob(c *gin.Context) {\n\tvar req dto.CronjobOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Cronjob\n// @Summary Load cronjob info\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/load/info [post]\nfunc (b *BaseApi) LoadCronjobInfo(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := cronjobService.LoadInfo(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Cronjob\n// @Summary Export cronjob list\n// @Accept json\n// @Param request body dto.OperateByIDs true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/export [post]\nfunc (b *BaseApi) ExportCronjob(c *gin.Context) {\n\tvar req dto.OperateByIDs\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tcontent, err := cronjobService.Export(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thttp.ServeContent(c.Writer, c.Request, \"\", time.Now(), strings.NewReader(content))\n}\n\n// @Tags Cronjob\n// @Summary Import cronjob list\n// @Accept json\n// @Param request body dto.CronjobImport true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/import [post]\nfunc (b *BaseApi) ImportCronjob(c *gin.Context) {\n\tvar req dto.CronjobImport\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.Import(req.Cronjobs); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Cronjob\n// @Summary Load script options\n// @Success 200 {array} dto.ScriptOptions\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/script/options [get]\nfunc (b *BaseApi) LoadScriptOptions(c *gin.Context) {\n\thelper.SuccessWithData(c, cronjobService.LoadScriptOptions())\n}\n\n// @Tags Cronjob\n// @Summary Load cronjob spec time\n// @Accept json\n// @Param request body dto.CronjobSpec true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/next [post]\nfunc (b *BaseApi) LoadNextHandle(c *gin.Context) {\n\tvar req dto.CronjobSpec\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tlist, err := cronjobService.LoadNextHandle(req.Spec)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Cronjob\n// @Summary Page cronjobs\n// @Accept json\n// @Param request body dto.PageCronjob true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/search [post]\nfunc (b *BaseApi) SearchCronjob(c *gin.Context) {\n\tvar req dto.PageCronjob\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := cronjobService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Cronjob\n// @Summary Page job records\n// @Accept json\n// @Param request body dto.SearchRecord true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/search/records [post]\nfunc (b *BaseApi) SearchJobRecords(c *gin.Context) {\n\tvar req dto.SearchRecord\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tloc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\treq.StartTime = req.StartTime.In(loc)\n\treq.EndTime = req.EndTime.In(loc)\n\n\ttotal, list, err := cronjobService.SearchRecords(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Cronjob\n// @Summary Load Cronjob record log\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200 {string} content\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/records/log [post]\nfunc (b *BaseApi) LoadRecordLog(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tcontent := cronjobService.LoadRecordLog(req)\n\thelper.SuccessWithData(c, content)\n}\n\n// @Tags Cronjob\n// @Summary Clean job records\n// @Accept json\n// @Param request body dto.CronjobClean true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/records/clean [post]\n// @x-panel-log {\"bodyKeys\":[\"cronjobID\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"cronjobID\",\"isList\":false,\"db\":\"cronjobs\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"清空计划任务记录 [name]\",\"formatEN\":\"clean cronjob [name] records\"}\nfunc (b *BaseApi) CleanRecord(c *gin.Context) {\n\tvar req dto.CronjobClean\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.CleanRecord(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Cronjob\n// @Summary Handle stop job\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/stop [post]\nfunc (b *BaseApi) StopCronJob(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.HandleStop(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Cronjob\n// @Summary Delete cronjob\n// @Accept json\n// @Param request body dto.CronjobBatchDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"cronjobs\",\"output_column\":\"name\",\"output_value\":\"names\"}],\"formatZH\":\"删除计划任务 [names]\",\"formatEN\":\"delete cronjob [names]\"}\nfunc (b *BaseApi) DeleteCronjob(c *gin.Context) {\n\tvar req dto.CronjobBatchDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Cronjob\n// @Summary Update cronjob\n// @Accept json\n// @Param request body dto.CronjobOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"cronjobs\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"更新计划任务 [name]\",\"formatEN\":\"update cronjob [name]\"}\nfunc (b *BaseApi) UpdateCronjob(c *gin.Context) {\n\tvar req dto.CronjobOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.Update(req.ID, req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Cronjob\n// @Summary Update cronjob group\n// @Accept json\n// @Param request body dto.ChangeGroup true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/group/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"cronjobs\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"更新计划任务分组 [name]\",\"formatEN\":\"update cronjob group [name]\"}\nfunc (b *BaseApi) UpdateCronjobGroup(c *gin.Context) {\n\tvar req dto.ChangeGroup\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.UpdateGroup(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Cronjob\n// @Summary Update cronjob status\n// @Accept json\n// @Param request body dto.CronjobUpdateStatus true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/status [post]\n// @x-panel-log {\"bodyKeys\":[\"id\",\"status\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"cronjobs\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"修改计划任务 [name] 状态为 [status]\",\"formatEN\":\"change the status of cronjob [name] to [status].\"}\nfunc (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {\n\tvar req dto.CronjobUpdateStatus\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.UpdateStatus(req.ID, req.Status); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Cronjob\n// @Summary Handle cronjob once\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /cronjobs/handle [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"cronjobs\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"手动执行计划任务 [name]\",\"formatEN\":\"manually execute the cronjob [name]\"}\nfunc (b *BaseApi) HandleOnce(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := cronjobService.HandleOnce(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/dashboard.go",
    "content": "package v2\n\nimport (\n\t\"errors\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Dashboard\n// @Summary Load os info\n// @Accept json\n// @Success 200 {object} dto.OsInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/base/os [get]\nfunc (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) {\n\tdata, err := dashboardService.LoadOsInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Dashboard\n// @Summary Load app launcher\n// @Accept json\n// @Success 200 {array} dto.AppLauncher\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/app/launcher [get]\nfunc (b *BaseApi) LoadAppLauncher(c *gin.Context) {\n\tdata, err := dashboardService.LoadAppLauncher(c)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithDataGzipped(c, data)\n}\n\n// @Tags Dashboard\n// @Summary Load app launcher options\n// @Accept json\n// @Param request body dto.SearchByFilter true \"request\"\n// @Success 200 {array} dto.LauncherOption\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/app/launcher/option [post]\nfunc (b *BaseApi) LoadAppLauncherOption(c *gin.Context) {\n\tvar req dto.SearchByFilter\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := dashboardService.ListLauncherOption(req.Filter)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Dashboard\n// @Summary Update app Launcher\n// @Accept json\n// @Param request body dto.SettingUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/app/launcher/show [post]\n// @x-panel-log {\"bodyKeys\":[\"key\", \"value\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"首页应用 [key] => 显示：[value]\",\"formatEN\":\"app launcher [key] => show: [value]\"}\nfunc (b *BaseApi) UpdateAppLauncher(c *gin.Context) {\n\tvar req dto.SettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := dashboardService.ChangeShow(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Dashboard\n// @Summary Load quick jump options\n// @Success 200 {array} dto.QuickJump\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/quick/option [get]\nfunc (b *BaseApi) LoadQuickOption(c *gin.Context) {\n\thelper.SuccessWithData(c, dashboardService.LoadQuickOptions())\n}\n\n// @Tags Dashboard\n// @Summary Update quick jump\n// @Accept json\n// @Param request body dto.ChangeQuicks true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/quick/change [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"切换快速跳转\",\"formatEN\":\"change quick jump\"}\nfunc (b *BaseApi) UpdateQuickJump(c *gin.Context) {\n\tvar req dto.ChangeQuicks\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := dashboardService.ChangeQuick(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Dashboard\n// @Summary Load dashboard base info\n// @Accept json\n// @Param ioOption path string true \"request\"\n// @Param netOption path string true \"request\"\n// @Success 200 {object} dto.DashboardBase\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/base/:ioOption/:netOption [get]\nfunc (b *BaseApi) LoadDashboardBaseInfo(c *gin.Context) {\n\tioOption, ok := c.Params.Get(\"ioOption\")\n\tif !ok {\n\t\thelper.BadRequest(c, errors.New(\"error ioOption in path\"))\n\t\treturn\n\t}\n\tnetOption, ok := c.Params.Get(\"netOption\")\n\tif !ok {\n\t\thelper.BadRequest(c, errors.New(\"error ioOption in path\"))\n\t\treturn\n\t}\n\tdata, err := dashboardService.LoadBaseInfo(ioOption, netOption)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Dashboard\n// @Summary Load dashboard current info for node\n// @Success 200 {object} dto.NodeCurrent\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/current/node [get]\nfunc (b *BaseApi) LoadCurrentInfoForNode(c *gin.Context) {\n\tdata := dashboardService.LoadCurrentInfoForNode()\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Dashboard\n// @Summary Load dashboard current info\n// @Accept json\n// @Param ioOption path string true \"request\"\n// @Param netOption path string true \"request\"\n// @Success 200 {object} dto.DashboardCurrent\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/current/:ioOption/:netOption [get]\nfunc (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) {\n\tioOption, ok := c.Params.Get(\"ioOption\")\n\tif !ok {\n\t\thelper.BadRequest(c, errors.New(\"error ioOption in path\"))\n\t\treturn\n\t}\n\tnetOption, ok := c.Params.Get(\"netOption\")\n\tif !ok {\n\t\thelper.BadRequest(c, errors.New(\"error netOption in path\"))\n\t\treturn\n\t}\n\n\tdata := dashboardService.LoadCurrentInfo(ioOption, netOption)\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Dashboard\n// @Summary Load top cpu processes\n// @Success 200 {array} dto.Process\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/current/top/cpu [get]\nfunc (b *BaseApi) LoadDashboardTopCPU(c *gin.Context) {\n\tdata := dashboardService.LoadTopCPU()\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Dashboard\n// @Summary Load top memory processes\n// @Success 200 {array} dto.Process\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/current/top/mem [get]\nfunc (b *BaseApi) LoadDashboardTopMem(c *gin.Context) {\n\tdata := dashboardService.LoadTopMem()\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Dashboard\n// @Summary System restart\n// @Accept json\n// @Param operation path string true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /dashboard/system/restart/:operation [post]\nfunc (b *BaseApi) SystemRestart(c *gin.Context) {\n\toperation, ok := c.Params.Get(\"operation\")\n\tif !ok {\n\t\thelper.BadRequest(c, errors.New(\"error operation in path\"))\n\t\treturn\n\t}\n\tif err := dashboardService.Restart(operation); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/database.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Database\n// @Summary Create database\n// @Accept json\n// @Param request body dto.DatabaseCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db [post]\n// @x-panel-log {\"bodyKeys\":[\"name\", \"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建远程数据库 [name][type]\",\"formatEN\":\"create database [name][type]\"}\nfunc (b *BaseApi) CreateDatabase(c *gin.Context) {\n\tvar req dto.DatabaseCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif req.SSL {\n\t\tkey, _ := base64.StdEncoding.DecodeString(req.ClientKey)\n\t\treq.ClientKey = string(key)\n\t\tcert, _ := base64.StdEncoding.DecodeString(req.ClientCert)\n\t\treq.ClientCert = string(cert)\n\t\tca, _ := base64.StdEncoding.DecodeString(req.RootCert)\n\t\treq.RootCert = string(ca)\n\t}\n\n\tif err := databaseService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database\n// @Summary Check database\n// @Accept json\n// @Param request body dto.DatabaseCreate true \"request\"\n// @Success 200 {boolean} isOk\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db/check [post]\n// @x-panel-log {\"bodyKeys\":[\"name\", \"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"检测远程数据库 [name][type] 连接性\",\"formatEN\":\"check if database [name][type] is connectable\"}\nfunc (b *BaseApi) CheckDatabase(c *gin.Context) {\n\tvar req dto.DatabaseCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif req.SSL {\n\t\tclientKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)\n\t\treq.ClientKey = string(clientKey)\n\t\tclientCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)\n\t\treq.ClientCert = string(clientCert)\n\t\trootCert, _ := base64.StdEncoding.DecodeString(req.RootCert)\n\t\treq.RootCert = string(rootCert)\n\t}\n\n\thelper.SuccessWithData(c, databaseService.CheckDatabase(req))\n}\n\n// @Tags Database\n// @Summary Page databases\n// @Accept json\n// @Param request body dto.DatabaseSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db/search [post]\nfunc (b *BaseApi) SearchDatabase(c *gin.Context) {\n\tvar req dto.DatabaseSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := databaseService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Database\n// @Summary List databases\n// @Success 200 {array} dto.DatabaseOption\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db/list/:type [get]\nfunc (b *BaseApi) ListDatabase(c *gin.Context) {\n\tdbType, err := helper.GetStrParamByKey(c, \"type\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tlist, err := databaseService.List(dbType)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Database\n// @Summary List databases\n// @Success 200 {array} dto.DatabaseItem\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db/item/:type [get]\nfunc (b *BaseApi) LoadDatabaseItems(c *gin.Context) {\n\tdbType, err := helper.GetStrParamByKey(c, \"type\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tlist, err := databaseService.LoadItems(dbType)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Database\n// @Summary Get databases\n// @Success 200 {object} dto.DatabaseInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db/:name [get]\nfunc (b *BaseApi) GetDatabase(c *gin.Context) {\n\tname, err := helper.GetStrParamByKey(c, \"name\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdata, err := databaseService.Get(name)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Database\n// @Summary Check before delete remote database\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db/del/check [post]\nfunc (b *BaseApi) DeleteCheckDatabase(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tapps, err := databaseService.DeleteCheck(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, apps)\n}\n\n// @Tags Database\n// @Summary Delete database\n// @Accept json\n// @Param request body dto.DatabaseDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"databases\",\"output_column\":\"name\",\"output_value\":\"names\"}],\"formatZH\":\"删除远程数据库 [names]\",\"formatEN\":\"delete database [names]\"}\nfunc (b *BaseApi) DeleteDatabase(c *gin.Context) {\n\tvar req dto.DatabaseDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := databaseService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database\n// @Summary Update database\n// @Accept json\n// @Param request body dto.DatabaseUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/db/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新远程数据库 [name]\",\"formatEN\":\"update database [name]\"}\nfunc (b *BaseApi) UpdateDatabase(c *gin.Context) {\n\tvar req dto.DatabaseUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif req.SSL {\n\t\tcKey, _ := base64.StdEncoding.DecodeString(req.ClientKey)\n\t\treq.ClientKey = string(cKey)\n\t\tcCert, _ := base64.StdEncoding.DecodeString(req.ClientCert)\n\t\treq.ClientCert = string(cCert)\n\t\tca, _ := base64.StdEncoding.DecodeString(req.RootCert)\n\t\treq.RootCert = string(ca)\n\t}\n\n\tif err := databaseService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/database_common.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Database Common\n// @Summary Load base info\n// @Accept json\n// @Param request body dto.OperationWithNameAndType true \"request\"\n// @Success 200 {object} dto.DBBaseInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/common/info [post]\nfunc (b *BaseApi) LoadDBBaseInfo(c *gin.Context) {\n\tvar req dto.OperationWithNameAndType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := dbCommonService.LoadBaseInfo(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Database Common\n// @Summary Load Database conf\n// @Accept json\n// @Param request body dto.OperationWithNameAndType true \"request\"\n// @Success 200 {string} content\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/common/load/file [post]\nfunc (b *BaseApi) LoadDBFile(c *gin.Context) {\n\tvar req dto.OperationWithNameAndType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tcontent, err := dbCommonService.LoadDatabaseFile(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, content)\n}\n\n// @Tags Database Common\n// @Summary Update conf by upload file\n// @Accept json\n// @Param request body dto.DBConfUpdateByFile true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/common/update/conf [post]\n// @x-panel-log {\"bodyKeys\":[\"type\",\"database\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新 [type] 数据库 [database] 配置信息\",\"formatEN\":\"update the [type] [database] database configuration information\"}\nfunc (b *BaseApi) UpdateDBConfByFile(c *gin.Context) {\n\tvar req dto.DBConfUpdateByFile\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := dbCommonService.UpdateConfByFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/database_mysql.go",
    "content": "package v2\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Database Mysql\n// @Summary Create mysql database\n// @Accept json\n// @Param request body dto.MysqlDBCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建 mysql 数据库 [name]\",\"formatEN\":\"create mysql database [name]\"}\nfunc (b *BaseApi) CreateMysql(c *gin.Context) {\n\tvar req dto.MysqlDBCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.Password) != 0 {\n\t\tpassword, err := base64.StdEncoding.DecodeString(req.Password)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Password = string(password)\n\t}\n\n\tif _, err := mysqlService.Create(context.Background(), req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database Mysql\n// @Summary Bind user of mysql database\n// @Accept json\n// @Param request body dto.BindUser true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/bind [post]\n// @x-panel-log {\"bodyKeys\":[\"database\", \"username\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"绑定 mysql 数据库名 [database] [username]\",\"formatEN\":\"bind mysql database [database] [username]\"}\nfunc (b *BaseApi) BindUser(c *gin.Context) {\n\tvar req dto.BindUser\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.Password) != 0 {\n\t\tpassword, err := base64.StdEncoding.DecodeString(req.Password)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Password = string(password)\n\t}\n\n\tif err := mysqlService.BindUser(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database Mysql\n// @Summary Update mysql database description\n// @Accept json\n// @Param request body dto.UpdateDescription true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/description/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\",\"description\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"database_mysqls\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"mysql 数据库 [name] 描述信息修改 [description]\",\"formatEN\":\"The description of the mysql database [name] is modified => [description]\"}\nfunc (b *BaseApi) UpdateMysqlDescription(c *gin.Context) {\n\tvar req dto.UpdateDescription\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := mysqlService.UpdateDescription(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database Mysql\n// @Summary Change mysql password\n// @Accept json\n// @Param request body dto.ChangeDBInfo true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/change/password [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"database_mysqls\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"更新数据库 [name] 密码\",\"formatEN\":\"Update database [name] password\"}\nfunc (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {\n\tvar req dto.ChangeDBInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.Value) != 0 {\n\t\tvalue, err := base64.StdEncoding.DecodeString(req.Value)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Value = string(value)\n\t}\n\n\tif err := mysqlService.ChangePassword(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database Mysql\n// @Summary Change mysql access\n// @Accept json\n// @Param request body dto.ChangeDBInfo true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/change/access [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"database_mysqls\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"更新数据库 [name] 访问权限\",\"formatEN\":\"Update database [name] access\"}\nfunc (b *BaseApi) ChangeMysqlAccess(c *gin.Context) {\n\tvar req dto.ChangeDBInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := mysqlService.ChangeAccess(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database Mysql\n// @Summary Update mysql variables\n// @Accept json\n// @Param request body dto.MysqlVariablesUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/variables/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"调整 mysql 数据库性能参数\",\"formatEN\":\"adjust mysql database performance parameters\"}\nfunc (b *BaseApi) UpdateMysqlVariables(c *gin.Context) {\n\tvar req dto.MysqlVariablesUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := mysqlService.UpdateVariables(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database Mysql\n// @Summary Page mysql databases\n// @Accept json\n// @Param request body dto.MysqlDBSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/search [post]\nfunc (b *BaseApi) SearchMysql(c *gin.Context) {\n\tvar req dto.MysqlDBSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := mysqlService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Database Mysql\n// @Summary List mysql database format collation options\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Success 200 {array} dto.MysqlFormatCollationOption\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/format/options [post]\nfunc (b *BaseApi) ListDBFormatCollationOptions(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, mysqlService.LoadFormatOption(req))\n}\n\n// @Tags Database Mysql\n// @Summary Load mysql database from remote\n// @Accept json\n// @Param request body dto.MysqlLoadDB true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/load [post]\nfunc (b *BaseApi) LoadDBFromRemote(c *gin.Context) {\n\tvar req dto.MysqlLoadDB\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := mysqlService.LoadFromRemote(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Database Mysql\n// @Summary Check before delete mysql database\n// @Accept json\n// @Param request body dto.MysqlDBDeleteCheck true \"request\"\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/del/check [post]\nfunc (b *BaseApi) DeleteCheckMysql(c *gin.Context) {\n\tvar req dto.MysqlDBDeleteCheck\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tapps, err := mysqlService.DeleteCheck(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, apps)\n}\n\n// @Tags Database Mysql\n// @Summary Delete mysql database\n// @Accept json\n// @Param request body dto.MysqlDBDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"database_mysqls\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"删除 mysql 数据库 [name]\",\"formatEN\":\"delete mysql database [name]\"}\nfunc (b *BaseApi) DeleteMysql(c *gin.Context) {\n\tvar req dto.MysqlDBDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttx, ctx := helper.GetTxAndContext()\n\tif err := mysqlService.Delete(ctx, req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\ttx.Rollback()\n\t\treturn\n\t}\n\ttx.Commit()\n\thelper.Success(c)\n}\n\n// @Tags Database Mysql\n// @Summary Load mysql remote access\n// @Accept json\n// @Param request body dto.OperationWithNameAndType true \"request\"\n// @Success 200 {boolean} isRemote\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/remote [post]\nfunc (b *BaseApi) LoadRemoteAccess(c *gin.Context) {\n\tvar req dto.OperationWithNameAndType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tisRemote, err := mysqlService.LoadRemoteAccess(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, isRemote)\n}\n\n// @Tags Database Mysql\n// @Summary Load mysql status info\n// @Accept json\n// @Param request body dto.OperationWithNameAndType true \"request\"\n// @Success 200 {object} dto.MysqlStatus\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/status [post]\nfunc (b *BaseApi) LoadStatus(c *gin.Context) {\n\tvar req dto.OperationWithNameAndType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := mysqlService.LoadStatus(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Database Mysql\n// @Summary Load mysql variables info\n// @Accept json\n// @Param request body dto.OperationWithNameAndType true \"request\"\n// @Success 200 {object} dto.MysqlVariables\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/variables [post]\nfunc (b *BaseApi) LoadVariables(c *gin.Context) {\n\tvar req dto.OperationWithNameAndType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := mysqlService.LoadVariables(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n"
  },
  {
    "path": "agent/app/api/v2/database_postgresql.go",
    "content": "package v2\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Database PostgreSQL\n// @Summary Create postgresql database\n// @Accept json\n// @Param request body dto.PostgresqlDBCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建 postgresql 数据库 [name]\",\"formatEN\":\"create postgresql database [name]\"}\nfunc (b *BaseApi) CreatePostgresql(c *gin.Context) {\n\tvar req dto.PostgresqlDBCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.Password) != 0 {\n\t\tpassword, err := base64.StdEncoding.DecodeString(req.Password)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Password = string(password)\n\t}\n\n\tif _, err := postgresqlService.Create(context.Background(), req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database PostgreSQL\n// @Summary Bind postgresql user\n// @Accept json\n// @Param request body dto.PostgresqlBindUser true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg/bind [post]\n// @x-panel-log {\"bodyKeys\":[\"name\", \"username\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"绑定 postgresql 数据库 [name] 用户 [username]\",\"formatEN\":\"bind postgresql database [name] user [username]\"}\nfunc (b *BaseApi) BindPostgresqlUser(c *gin.Context) {\n\tvar req dto.PostgresqlBindUser\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := postgresqlService.BindUser(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database PostgreSQL\n// @Summary Update postgresql database description\n// @Accept json\n// @Param request body dto.UpdateDescription true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg/description [post]\n// @x-panel-log {\"bodyKeys\":[\"id\",\"description\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"database_postgresqls\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"postgresql 数据库 [name] 描述信息修改 [description]\",\"formatEN\":\"The description of the postgresql database [name] is modified => [description]\"}\nfunc (b *BaseApi) UpdatePostgresqlDescription(c *gin.Context) {\n\tvar req dto.UpdateDescription\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := postgresqlService.UpdateDescription(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database PostgreSQL\n// @Summary Change postgresql privileges\n// @Accept json\n// @Param request body dto.ChangeDBInfo true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg/privileges [post]\n// @x-panel-log {\"bodyKeys\":[\"database\", \"username\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新数据库 [database] 用户 [username] 权限\",\"formatEN\":\"Update [user] privileges of database [database]\"}\nfunc (b *BaseApi) ChangePostgresqlPrivileges(c *gin.Context) {\n\tvar req dto.PostgresqlPrivileges\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := postgresqlService.ChangePrivileges(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database PostgreSQL\n// @Summary Change postgresql password\n// @Accept json\n// @Param request body dto.ChangeDBInfo true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg/password [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"database_postgresqls\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"更新数据库 [name] 密码\",\"formatEN\":\"Update database [name] password\"}\nfunc (b *BaseApi) ChangePostgresqlPassword(c *gin.Context) {\n\tvar req dto.ChangeDBInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.Value) != 0 {\n\t\tvalue, err := base64.StdEncoding.DecodeString(req.Value)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Value = string(value)\n\t}\n\n\tif err := postgresqlService.ChangePassword(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database PostgreSQL\n// @Summary Page postgresql databases\n// @Accept json\n// @Param request body dto.PostgresqlDBSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg/search [post]\nfunc (b *BaseApi) SearchPostgresql(c *gin.Context) {\n\tvar req dto.PostgresqlDBSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := postgresqlService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Database PostgreSQL\n// @Summary Load postgresql database from remote\n// @Accept json\n// @Param request body dto.PostgresqlLoadDB true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg/:database/load [post]\nfunc (b *BaseApi) LoadPostgresqlDBFromRemote(c *gin.Context) {\n\tdatabase, err := helper.GetStrParamByKey(c, \"database\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\n\tif err := postgresqlService.LoadFromRemote(database); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Database PostgreSQL\n// @Summary Check before delete postgresql database\n// @Accept json\n// @Param request body dto.PostgresqlDBDeleteCheck true \"request\"\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg/del/check [post]\nfunc (b *BaseApi) DeleteCheckPostgresql(c *gin.Context) {\n\tvar req dto.PostgresqlDBDeleteCheck\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tapps, err := postgresqlService.DeleteCheck(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, apps)\n}\n\n// @Tags Database PostgreSQL\n// @Summary Delete postgresql database\n// @Accept json\n// @Param request body dto.PostgresqlDBDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/pg/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"database_postgresqls\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"删除 postgresql 数据库 [name]\",\"formatEN\":\"delete postgresql database [name]\"}\nfunc (b *BaseApi) DeletePostgresql(c *gin.Context) {\n\tvar req dto.PostgresqlDBDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttx, ctx := helper.GetTxAndContext()\n\tif err := postgresqlService.Delete(ctx, req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\ttx.Rollback()\n\t\treturn\n\t}\n\ttx.Commit()\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/database_redis.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Database Redis\n// @Summary Load redis status info\n// @Accept json\n// @Param request body dto.LoadRedisStatus true \"request\"\n// @Success 200 {object} dto.RedisStatus\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/redis/status [post]\nfunc (b *BaseApi) LoadRedisStatus(c *gin.Context) {\n\tvar req dto.LoadRedisStatus\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := redisService.LoadStatus(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Database Redis\n// @Summary Load redis conf\n// @Accept json\n// @Param request body dto.LoadRedisStatus true \"request\"\n// @Success 200 {object} dto.RedisConf\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/redis/conf [post]\nfunc (b *BaseApi) LoadRedisConf(c *gin.Context) {\n\tvar req dto.LoadRedisStatus\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := redisService.LoadConf(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Database Redis\n// @Summary Load redis persistence conf\n// @Accept json\n// @Param request body dto.LoadRedisStatus true \"request\"\n// @Success 200 {object} dto.RedisPersistence\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/redis/persistence/conf [post]\nfunc (b *BaseApi) LoadPersistenceConf(c *gin.Context) {\n\tvar req dto.LoadRedisStatus\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := redisService.LoadPersistenceConf(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\nfunc (b *BaseApi) CheckHasCli(c *gin.Context) {\n\thelper.SuccessWithData(c, redisService.CheckHasCli())\n}\n\n// @Tags Database Redis\n// @Summary Install redis-cli\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/redis/install/cli [post]\nfunc (b *BaseApi) InstallCli(c *gin.Context) {\n\tif err := redisService.InstallCli(); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Database Redis\n// @Summary Update redis conf\n// @Accept json\n// @Param request body dto.RedisConfUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/redis/conf/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新 redis 数据库配置信息\",\"formatEN\":\"update the redis database configuration information\"}\nfunc (b *BaseApi) UpdateRedisConf(c *gin.Context) {\n\tvar req dto.RedisConfUpdate\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := redisService.UpdateConf(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database Redis\n// @Summary Change redis password\n// @Accept json\n// @Param request body dto.ChangeRedisPass true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/redis/password [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改 redis 数据库密码\",\"formatEN\":\"change the password of the redis database\"}\nfunc (b *BaseApi) ChangeRedisPassword(c *gin.Context) {\n\tvar req dto.ChangeRedisPass\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.Value) != 0 {\n\t\tvalue, err := base64.StdEncoding.DecodeString(req.Value)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Value = string(value)\n\t}\n\n\tif err := redisService.ChangePassword(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Database Redis\n// @Summary Update redis persistence conf\n// @Accept json\n// @Param request body dto.RedisConfPersistenceUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /databases/redis/persistence/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"redis 数据库持久化配置更新\",\"formatEN\":\"redis database persistence configuration update\"}\nfunc (b *BaseApi) UpdateRedisPersistenceConf(c *gin.Context) {\n\tvar req dto.RedisConfPersistenceUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := redisService.UpdatePersistenceConf(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/device.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Device\n// @Summary Load device base info\n// @Success 200 {object} dto.DeviceBaseInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/base [post]\nfunc (b *BaseApi) LoadDeviceBaseInfo(c *gin.Context) {\n\tdata, err := deviceService.LoadBaseInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Device\n// @Summary list time zone options\n// @Accept json\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/zone/options [get]\nfunc (b *BaseApi) LoadTimeOption(c *gin.Context) {\n\tlist, err := deviceService.LoadTimeZone()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Device\n// @Summary load conf\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/conf [post]\nfunc (b *BaseApi) LoadDeviceConf(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tlist, err := deviceService.LoadConf(req.Name)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Device\n// @Summary Update device conf by file\n// @Accept json\n// @Param request body dto.UpdateByNameAndFile true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/update/byconf [post]\nfunc (b *BaseApi) UpdateDeviceByFile(c *gin.Context) {\n\tvar req dto.UpdateByNameAndFile\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := deviceService.UpdateByConf(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Device\n// @Summary Load user list\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/users  [get]\nfunc (b *BaseApi) LoadUsers(c *gin.Context) {\n\tusers, err := deviceService.LoadUsers()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, users)\n}\n\n// @Tags Device\n// @Summary Update device\n// @Accept json\n// @Param request body dto.SettingUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/update/conf [post]\n// @x-panel-log {\"bodyKeys\":[\"key\",\"value\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改主机参数 [key] => [value]\",\"formatEN\":\"update device conf [key] => [value]\"}\nfunc (b *BaseApi) UpdateDeviceConf(c *gin.Context) {\n\tvar req dto.SettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := deviceService.Update(req.Key, req.Value); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Device\n// @Summary Update device hosts\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/update/host [post]\n// @x-panel-log {\"bodyKeys\":[\"key\",\"value\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改主机 Host [key] => [value]\",\"formatEN\":\"update device host [key] => [value]\"}\nfunc (b *BaseApi) UpdateDeviceHost(c *gin.Context) {\n\tvar req []dto.HostHelper\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := deviceService.UpdateHosts(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Device\n// @Summary Update device passwd\n// @Accept json\n// @Param request body dto.ChangePasswd true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/update/passwd [post]\nfunc (b *BaseApi) UpdateDevicePasswd(c *gin.Context) {\n\tvar req dto.ChangePasswd\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif len(req.Passwd) != 0 {\n\t\tpassword, err := base64.StdEncoding.DecodeString(req.Passwd)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Passwd = string(password)\n\t}\n\tif err := deviceService.UpdatePasswd(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Device\n// @Summary Update device swap\n// @Accept json\n// @Param request body dto.SwapHelper true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/update/swap [post]\n// @x-panel-log {\"bodyKeys\":[\"operate\",\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operate] 主机 swap [path]\",\"formatEN\":\"[operate] device swap [path]\"}\nfunc (b *BaseApi) UpdateDeviceSwap(c *gin.Context) {\n\tvar req dto.SwapHelper\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := deviceService.UpdateSwap(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Device\n// @Summary Check device DNS conf\n// @Accept json\n// @Param request body dto.SettingUpdate true \"request\"\n// @Success 200 {boolean} data\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/device/check/dns [post]\nfunc (b *BaseApi) CheckDNS(c *gin.Context) {\n\tvar req dto.SettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := deviceService.CheckDNS(req.Key, req.Value)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Device\n// @Summary Scan system\n// @Success 200 {object} dto.CleanData\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/scan [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"扫描系统垃圾文件\",\"formatEN\":\"scan System Junk Files\"}\nfunc (b *BaseApi) ScanSystem(c *gin.Context) {\n\thelper.SuccessWithData(c, deviceService.Scan())\n}\n\n// @Tags Device\n// @Summary Clean system\n// @Accept json\n// @Param request body []dto.Clean true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/clean [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"清理系统垃圾文件\",\"formatEN\":\"Clean system junk files\"}\nfunc (b *BaseApi) SystemClean(c *gin.Context) {\n\tvar req []dto.Clean\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdeviceService.Clean(req)\n\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/disk.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Disk Management\n// @Summary Get complete disk information\n// @Description Get information about all disks including partitioned and unpartitioned disks\n// @Produce json\n// @Success 200 {object} response.CompleteDiskInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/disks [get]\nfunc (b *BaseApi) GetCompleteDiskInfo(c *gin.Context) {\n\tdiskInfo, err := diskService.GetCompleteDiskInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, diskInfo)\n}\n\n// @Tags Disk Management\n// @Summary Partition disk\n// @Description Create partition and format disk with specified filesystem\n// @Accept json\n// @Param request body request.DiskPartitionRequest true \"partition request\"\n// @Success 200 {string} string \"Partition created successfully\"\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/disks/partition [post]\n// @x-panel-log {\"bodyKeys\":[\"device\", \"filesystem\", \"mountPoint\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"对磁盘 [device] 进行分区，文件系统 [filesystem]，挂载点 [mountPoint]\",\"formatEN\":\"Partition disk [device] with filesystem [filesystem], mount point [mountPoint]\"}\nfunc (b *BaseApi) PartitionDisk(c *gin.Context) {\n\tvar req request.DiskPartitionRequest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tresult, err := diskService.PartitionDisk(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, result)\n}\n\n// @Tags Disk Management\n// @Summary Mount disk\n// @Description Mount partition to specified mount point\n// @Accept json\n// @Param request body request.DiskMountRequest true \"mount request\"\n// @Success 200 {string} string \"Disk mounted successfully\"\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/disks/mount [post]\n// @x-panel-log {\"bodyKeys\":[\"device\", \"mountPoint\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"挂载磁盘 [device] 到 [mountPoint]\",\"formatEN\":\"Mount disk [device] to [mountPoint]\"}\nfunc (b *BaseApi) MountDisk(c *gin.Context) {\n\tvar req request.DiskMountRequest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\terr := diskService.MountDisk(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Disk Management\n// @Summary Unmount disk\n// @Description Unmount partition from mount point\n// @Accept json\n// @Param request body request.DiskUnmountRequest true \"unmount request\"\n// @Success 200 {string} string \"Disk unmounted successfully\"\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/disks/unmount [post]\n// @x-panel-log {\"bodyKeys\":[\"device\", \"mountPoint\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"卸载磁盘 [device] 从 [mountPoint]\",\"formatEN\":\"Unmount disk [device] from [mountPoint]\"}\nfunc (b *BaseApi) UnmountDisk(c *gin.Context) {\n\tvar req request.DiskUnmountRequest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\terr := diskService.UnmountDisk(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/docker.go",
    "content": "package v2\n\nimport (\n\t\"os\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Container Docker\n// @Summary Load docker status\n// @Produce json\n// @Success 200 {object} dto.DockerStatus\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/docker/status [get]\nfunc (b *BaseApi) LoadDockerStatus(c *gin.Context) {\n\tstatus := dockerService.LoadDockerStatus()\n\thelper.SuccessWithData(c, status)\n}\n\n// @Tags Container Docker\n// @Summary Load docker daemon.json\n// @Produce json\n// @Success 200 {string} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/daemonjson/file [get]\nfunc (b *BaseApi) LoadDaemonJsonFile(c *gin.Context) {\n\tif _, err := os.Stat(constant.DaemonJsonPath); err != nil {\n\t\thelper.SuccessWithData(c, \"daemon.json is not find in path\")\n\t\treturn\n\t}\n\tcontent, err := os.ReadFile(constant.DaemonJsonPath)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, string(content))\n}\n\n// @Tags Container Docker\n// @Summary Load docker daemon.json\n// @Produce json\n// @Success 200 {object} dto.DaemonJsonConf\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/daemonjson [get]\nfunc (b *BaseApi) LoadDaemonJson(c *gin.Context) {\n\tconf, err := dockerService.LoadDockerConf()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, conf)\n}\n\n// @Tags Container Docker\n// @Summary Update docker daemon.json\n// @Accept json\n// @Param request body dto.SettingUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/daemonjson/update [post]\n// @x-panel-log {\"bodyKeys\":[\"key\", \"value\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新配置 [key]\",\"formatEN\":\"Updated configuration [key]\"}\nfunc (b *BaseApi) UpdateDaemonJson(c *gin.Context) {\n\tvar req dto.SettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := dockerService.UpdateConf(req, true); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Docker\n// @Summary Update docker daemon.json log option\n// @Accept json\n// @Param request body dto.LogOption true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/logoption/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新日志配置\",\"formatEN\":\"Updated the log option\"}\nfunc (b *BaseApi) UpdateLogOption(c *gin.Context) {\n\tvar req dto.LogOption\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := dockerService.UpdateLogOption(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Docker\n// @Summary Update docker daemon.json ipv6 option\n// @Accept json\n// @Param request body dto.LogOption true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/ipv6option/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新 ipv6 配置\",\"formatEN\":\"Updated the ipv6 option\"}\nfunc (b *BaseApi) UpdateIpv6Option(c *gin.Context) {\n\tvar req dto.Ipv6Option\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := dockerService.UpdateIpv6Option(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Docker\n// @Summary Update docker daemon.json by upload file\n// @Accept json\n// @Param request body dto.DaemonJsonUpdateByFile true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/daemonjson/update/byfile [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新配置文件\",\"formatEN\":\"Updated configuration file\"}\nfunc (b *BaseApi) UpdateDaemonJsonByFile(c *gin.Context) {\n\tvar req dto.DaemonJsonUpdateByFile\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := dockerService.UpdateConfByFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Docker\n// @Summary Operate docker\n// @Accept json\n// @Param request body dto.DockerOperation true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/docker/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operation\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"docker 服务 [operation]\",\"formatEN\":\"[operation] docker service\"}\nfunc (b *BaseApi) OperateDocker(c *gin.Context) {\n\tvar req dto.DockerOperation\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := dockerService.OperateDocker(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/entry.go",
    "content": "package v2\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/service\"\n\ntype ApiGroup struct {\n\tBaseApi\n}\n\nvar ApiGroupApp = new(ApiGroup)\n\ntype BaseApi struct{}\n\nvar (\n\tdashboardService = service.NewIDashboardService()\n\n\tappService              = service.NewIAppService()\n\tappInstallService       = service.NewIAppInstalledService()\n\tappIgnoreUpgradeService = service.NewIAppIgnoreUpgradeService()\n\n\taiToolService      = service.NewIAIToolService()\n\tmcpServerService   = service.NewIMcpServerService()\n\ttensorrtLLMService = service.NewITensorRTLLMService()\n\tagentService       = service.NewIAgentService()\n\n\tcontainerService       = service.NewIContainerService()\n\tcomposeTemplateService = service.NewIComposeTemplateService()\n\timageRepoService       = service.NewIImageRepoService()\n\timageService           = service.NewIImageService()\n\tdockerService          = service.NewIDockerService()\n\n\tdbCommonService   = service.NewIDBCommonService()\n\tmysqlService      = service.NewIMysqlService()\n\tpostgresqlService = service.NewIPostgresqlService()\n\tdatabaseService   = service.NewIDatabaseService()\n\tredisService      = service.NewIRedisService()\n\n\tcronjobService = service.NewICronjobService()\n\n\tfileService     = service.NewIFileService()\n\tsshService      = service.NewISSHService()\n\tfirewallService = service.NewIFirewallService()\n\tiptablesService = service.NewIIptablesService()\n\tmonitorService  = service.NewIMonitorService()\n\tsystemService   = service.NewISystemService()\n\n\tdeviceService   = service.NewIDeviceService()\n\tfail2banService = service.NewIFail2BanService()\n\tftpService      = service.NewIFtpService()\n\tclamService     = service.NewIClamService()\n\n\tsettingService      = service.NewISettingService()\n\tbackupService       = service.NewIBackupService()\n\tbackupRecordService = service.NewIBackupRecordService()\n\n\twebsiteService            = service.NewIWebsiteService()\n\twebsiteDnsAccountService  = service.NewIWebsiteDnsAccountService()\n\twebsiteSSLService         = service.NewIWebsiteSSLService()\n\twebsiteAcmeAccountService = service.NewIWebsiteAcmeAccountService()\n\n\tnginxService = service.NewINginxService()\n\n\tlogService      = service.NewILogService()\n\tsnapshotService = service.NewISnapshotService()\n\n\truntimeService       = service.NewRuntimeService()\n\tprocessService       = service.NewIProcessService()\n\tphpExtensionsService = service.NewIPHPExtensionsService()\n\n\thostToolService = service.NewIHostToolService()\n\n\trecycleBinService = service.NewIRecycleBinService()\n\tfavoriteService   = service.NewIFavoriteService()\n\thostService       = service.NewIHostService()\n\n\twebsiteCAService = service.NewIWebsiteCAService()\n\ttaskService      = service.NewITaskService()\n\tgroupService     = service.NewIGroupService()\n\talertService     = service.NewIAlertService()\n\n\tdiskService = service.NewIDiskService()\n)\n"
  },
  {
    "path": "agent/app/api/v2/fail2ban.go",
    "content": "package v2\n\nimport (\n\t\"os\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Fail2ban\n// @Summary Load fail2ban base info\n// @Success 200 {object} dto.Fail2BanBaseInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/fail2ban/base [get]\nfunc (b *BaseApi) LoadFail2BanBaseInfo(c *gin.Context) {\n\tdata, err := fail2banService.LoadBaseInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Fail2ban\n// @Summary Page fail2ban ip list\n// @Accept json\n// @Param request body dto.Fail2BanSearch true \"request\"\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/fail2ban/search [post]\nfunc (b *BaseApi) SearchFail2Ban(c *gin.Context) {\n\tvar req dto.Fail2BanSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tlist, err := fail2banService.Search(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Fail2ban\n// @Summary Operate fail2ban\n// @Accept json\n// @Param request body dto.Operate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/fail2ban/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operation\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operation] Fail2ban\",\"formatEN\":\"[operation] Fail2ban\"}\nfunc (b *BaseApi) OperateFail2Ban(c *gin.Context) {\n\tvar req dto.Operate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := fail2banService.Operate(req.Operation); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Fail2ban\n// @Summary Operate sshd of fail2ban\n// @Accept json\n// @Param request body dto.Fail2BanSet true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/fail2ban/operate/sshd [post]\nfunc (b *BaseApi) OperateSSHD(c *gin.Context) {\n\tvar req dto.Fail2BanSet\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := fail2banService.OperateSSHD(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Fail2ban\n// @Summary Update fail2ban conf\n// @Accept json\n// @Param request body dto.Fail2BanUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/fail2ban/update [post]\n// @x-panel-log {\"bodyKeys\":[\"key\",\"value\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改 Fail2ban 配置 [key] => [value]\",\"formatEN\":\"update fail2ban conf [key] => [value]\"}\nfunc (b *BaseApi) UpdateFail2BanConf(c *gin.Context) {\n\tvar req dto.Fail2BanUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := fail2banService.UpdateConf(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Fail2ban\n// @Summary Load fail2ban conf\n// @Accept json\n// @Success 200 {string} file\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/fail2ban/load/conf [get]\nfunc (b *BaseApi) LoadFail2BanConf(c *gin.Context) {\n\tpath := \"/etc/fail2ban/jail.local\"\n\tfile, err := os.ReadFile(path)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, string(file))\n}\n\n// @Tags Fail2ban\n// @Summary Update fail2ban conf by file\n// @Accept json\n// @Param request body dto.UpdateByFile true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/fail2ban/update/byconf [post]\nfunc (b *BaseApi) UpdateFail2BanConfByFile(c *gin.Context) {\n\tvar req dto.UpdateByFile\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := fail2banService.UpdateConfByFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/favorite.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags File\n// @Summary List favorites\n// @Accept json\n// @Param request body dto.PageInfo true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/favorite/search [post]\nfunc (b *BaseApi) SearchFavorite(c *gin.Context) {\n\tvar req dto.PageInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, list, err := favoriteService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: list,\n\t})\n}\n\n// @Tags File\n// @Summary Create favorite\n// @Accept json\n// @Param request body request.FavoriteCreate true \"request\"\n// @Success 200 {object} model.Favorite\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/favorite [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"收藏文件/文件夹 [path]\",\"formatEN\":\"收藏文件/文件夹 [path]\"}\nfunc (b *BaseApi) CreateFavorite(c *gin.Context) {\n\tvar req request.FavoriteCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfavorite, err := favoriteService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, favorite)\n}\n\n// @Tags File\n// @Summary Delete favorite\n// @Accept json\n// @Param request body request.FavoriteDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/favorite/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"favorites\",\"output_column\":\"path\",\"output_value\":\"path\"}],\"formatZH\":\"删除收藏 [path]\",\"formatEN\":\"delete avorite [path]\"}\nfunc (b *BaseApi) DeleteFavorite(c *gin.Context) {\n\tvar req request.FavoriteDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := favoriteService.Delete(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/file.go",
    "content": "package v2\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\twebsocket2 \"github.com/1Panel-dev/1Panel/agent/utils/websocket\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/gorilla/websocket\"\n)\n\n// @Tags File\n// @Summary List files\n// @Accept json\n// @Param request body request.FileOption true \"request\"\n// @Success 200 {object} response.FileInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/search [post]\nfunc (b *BaseApi) ListFiles(c *gin.Context) {\n\tvar req request.FileOption\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfileList, err := fileService.GetFileList(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, fileList)\n}\n\n// @Tags File\n// @Summary Page file\n// @Accept json\n// @Param request body request.SearchUploadWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/upload/search [post]\nfunc (b *BaseApi) SearchUploadWithPage(c *gin.Context) {\n\tvar req request.SearchUploadWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, files, err := fileService.SearchUploadWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: files,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags File\n// @Summary Load files tree\n// @Accept json\n// @Param request body request.FileOption true \"request\"\n// @Success 200 {array} response.FileTree\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/tree [post]\nfunc (b *BaseApi) GetFileTree(c *gin.Context) {\n\tvar req request.FileOption\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttree, err := fileService.GetFileTree(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithDataGzipped(c, tree)\n}\n\n// @Tags File\n// @Summary Create file\n// @Accept json\n// @Param request body request.FileCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建文件/文件夹 [path]\",\"formatEN\":\"Create dir or file [path]\"}\nfunc (b *BaseApi) CreateFile(c *gin.Context) {\n\tvar req request.FileCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := fileService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Delete file\n// @Accept json\n// @Param request body request.FileDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/del [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"删除文件/文件夹 [path]\",\"formatEN\":\"Delete dir or file [path]\"}\nfunc (b *BaseApi) DeleteFile(c *gin.Context) {\n\tvar req request.FileDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := fileService.Delete(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Batch delete file\n// @Accept json\n// @Param request body request.FileBatchDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/batch/del [post]\n// @x-panel-log {\"bodyKeys\":[\"paths\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"批量删除文件/文件夹 [paths]\",\"formatEN\":\"Batch delete dir or file [paths]\"}\nfunc (b *BaseApi) BatchDeleteFile(c *gin.Context) {\n\tvar req request.FileBatchDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := fileService.BatchDelete(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Change file mode\n// @Accept json\n// @Param request body request.FileCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/mode [post]\n// @x-panel-log {\"bodyKeys\":[\"path\",\"mode\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改权限 [paths] => [mode]\",\"formatEN\":\"Change mode [paths] => [mode]\"}\nfunc (b *BaseApi) ChangeFileMode(c *gin.Context) {\n\tvar req request.FileCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := fileService.ChangeMode(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Change file owner\n// @Accept json\n// @Param request body request.FileRoleUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/owner [post]\n// @x-panel-log {\"bodyKeys\":[\"path\",\"user\",\"group\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改用户/组 [paths] => [user]/[group]\",\"formatEN\":\"Change owner [paths] => [user]/[group]\"}\nfunc (b *BaseApi) ChangeFileOwner(c *gin.Context) {\n\tvar req request.FileRoleUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := fileService.ChangeOwner(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Compress file\n// @Accept json\n// @Param request body request.FileCompress true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/compress [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"压缩文件 [name]\",\"formatEN\":\"Compress file [name]\"}\nfunc (b *BaseApi) CompressFile(c *gin.Context) {\n\tvar req request.FileCompress\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := fileService.Compress(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Decompress file\n// @Accept json\n// @Param request body request.FileDeCompress true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/decompress [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"解压 [path]\",\"formatEN\":\"Decompress file [path]\"}\nfunc (b *BaseApi) DeCompressFile(c *gin.Context) {\n\tvar req request.FileDeCompress\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := fileService.DeCompress(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Load file content\n// @Accept json\n// @Param request body request.FileContentReq true \"request\"\n// @Success 200 {object} response.FileInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/content [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"获取文件内容 [path]\",\"formatEN\":\"Load file content [path]\"}\nfunc (b *BaseApi) GetContent(c *gin.Context) {\n\tvar req request.FileContentReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tinfo, err := fileService.GetContent(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tif info.Size > 2*1024 && info.Size < 5*1024*1024 {\n\t\thelper.SuccessWithDataGzipped(c, info)\n\t} else {\n\t\thelper.SuccessWithData(c, info)\n\t}\n}\n\n// @Tags File\n// @Summary Preview file content\n// @Accept json\n// @Param request body request.FileContentReq true \"request\"\n// @Success 200 {object} response.FileInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/preview [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"预览文件内容 [path]\",\"formatEN\":\"Preview file content [path]\"}\nfunc (b *BaseApi) PreviewContent(c *gin.Context) {\n\tvar req request.FileContentReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tinfo, err := fileService.GetPreviewContent(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, info)\n}\n\n// @Tags File\n// @Summary Update file content\n// @Accept json\n// @Param request body request.FileEdit true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/save [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新文件内容 [path]\",\"formatEN\":\"Update file content [path]\"}\nfunc (b *BaseApi) SaveContent(c *gin.Context) {\n\tvar req request.FileEdit\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := fileService.SaveContent(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Upload file\n// @Param file formData file true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/upload [post]\n// @x-panel-log {\"bodyKeys\":[\"path\", \"file\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"上传文件 [path]/[file]\",\"formatEN\":\"Upload file [path]/[file]\"}\nfunc (b *BaseApi) UploadFiles(c *gin.Context) {\n\tform, err := c.MultipartForm()\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tuploadFiles := form.File[\"file\"]\n\tpaths := form.Value[\"path\"]\n\n\toverwrite := true\n\tif ow, ok := form.Value[\"overwrite\"]; ok {\n\t\tif len(ow) != 0 {\n\t\t\tparseBool, _ := strconv.ParseBool(ow[0])\n\t\t\toverwrite = parseBool\n\t\t}\n\t}\n\n\tif len(paths) == 0 || !strings.Contains(paths[0], \"/\") {\n\t\thelper.BadRequest(c, errors.New(\"error paths in request\"))\n\t\treturn\n\t}\n\tdir := path.Dir(paths[0])\n\n\t_, err = os.Stat(dir)\n\tif err != nil && os.IsNotExist(err) {\n\t\tmode, err := files.GetParentMode(dir)\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\tif err = os.MkdirAll(dir, mode); err != nil {\n\t\t\thelper.BadRequest(c, fmt.Errorf(\"mkdir %s failed, err: %v\", dir, err))\n\t\t\treturn\n\t\t}\n\t}\n\tinfo, err := os.Stat(dir)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tmode := info.Mode()\n\n\tfileOp := files.NewFileOp()\n\tstat, ok := info.Sys().(*syscall.Stat_t)\n\tuid, gid := -1, -1\n\tif ok {\n\t\tuid, gid = int(stat.Uid), int(stat.Gid)\n\t}\n\tsuccess := 0\n\tfailures := make(buserr.MultiErr)\n\tfor _, file := range uploadFiles {\n\t\tdstFilename := path.Join(paths[0], file.Filename)\n\t\tdstDir := path.Dir(dstFilename)\n\t\tif !fileOp.Stat(dstDir) {\n\t\t\tif err = fileOp.CreateDir(dstDir, mode); err != nil {\n\t\t\t\te := fmt.Errorf(\"create dir [%s] failed, err: %v\", path.Dir(dstFilename), err)\n\t\t\t\tfailures[file.Filename] = e\n\t\t\t\tglobal.LOG.Error(e)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_ = os.Chown(dstDir, uid, gid)\n\t\t}\n\t\ttmpFilename := dstFilename + \".tmp\"\n\t\tif err := c.SaveUploadedFile(file, tmpFilename); err != nil {\n\t\t\t_ = os.Remove(tmpFilename)\n\t\t\te := fmt.Errorf(\"upload [%s] file failed, err: %v\", file.Filename, err)\n\t\t\tfailures[file.Filename] = e\n\t\t\tglobal.LOG.Error(e)\n\t\t\tcontinue\n\t\t}\n\t\tdstInfo, statErr := os.Stat(dstFilename)\n\t\tif overwrite {\n\t\t\t_ = os.Remove(dstFilename)\n\t\t}\n\n\t\terr = os.Rename(tmpFilename, dstFilename)\n\t\tif err != nil {\n\t\t\t_ = os.Remove(tmpFilename)\n\t\t\te := fmt.Errorf(\"upload [%s] file failed, err: %v\", file.Filename, err)\n\t\t\tfailures[file.Filename] = e\n\t\t\tglobal.LOG.Error(e)\n\t\t\tcontinue\n\t\t}\n\t\tif statErr == nil {\n\t\t\t_ = os.Chmod(dstFilename, dstInfo.Mode())\n\t\t} else {\n\t\t\t_ = os.Chmod(dstFilename, mode)\n\t\t}\n\t\tif uid != -1 && gid != -1 {\n\t\t\t_ = os.Chown(dstFilename, uid, gid)\n\t\t}\n\t\tsuccess++\n\t}\n\tif success == 0 {\n\t\thelper.InternalServer(c, failures)\n\t} else {\n\t\thelper.SuccessWithMsg(c, fmt.Sprintf(\"%d files upload success\", success))\n\t}\n}\n\n// @Tags File\n// @Summary Check file exist\n// @Accept json\n// @Param request body request.FilePathCheck true \"request\"\n// @Success 200 {boolean} isOk\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/check [post]\nfunc (b *BaseApi) CheckFile(c *gin.Context) {\n\tvar req request.FilePathCheck\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\tif fileOp.Stat(req.Path) {\n\t\thelper.SuccessWithData(c, true)\n\t\treturn\n\t}\n\tif req.WithInit {\n\t\tif err := fileOp.CreateDir(req.Path, 0644); err != nil {\n\t\t\thelper.SuccessWithData(c, false)\n\t\t\treturn\n\t\t}\n\t\thelper.SuccessWithData(c, true)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, false)\n}\n\n// @Tags File\n// @Summary Batch check file exist\n// @Accept json\n// @Param request body request.FilePathsCheck true \"request\"\n// @Success 200 {array} response.ExistFileInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/batch/check [post]\nfunc (b *BaseApi) BatchCheckFiles(c *gin.Context) {\n\tvar req request.FilePathsCheck\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfileList := fileService.BatchCheckFiles(req)\n\thelper.SuccessWithData(c, fileList)\n}\n\n// @Tags File\n// @Summary Change file name\n// @Accept json\n// @Param request body request.FileRename true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/rename [post]\n// @x-panel-log {\"bodyKeys\":[\"oldName\",\"newName\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"重命名 [oldName] => [newName]\",\"formatEN\":\"Rename [oldName] => [newName]\"}\nfunc (b *BaseApi) ChangeFileName(c *gin.Context) {\n\tvar req request.FileRename\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := fileService.ChangeName(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Wget file\n// @Accept json\n// @Param request body request.FileWget true \"request\"\n// @Success 200 {object} response.FileWgetRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/wget [post]\n// @x-panel-log {\"bodyKeys\":[\"url\",\"path\",\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"下载 url => [path]/[name]\",\"formatEN\":\"Download url => [path]/[name]\"}\nfunc (b *BaseApi) WgetFile(c *gin.Context) {\n\tvar req request.FileWget\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tkey, err := fileService.Wget(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, response.FileWgetRes{\n\t\tKey: key,\n\t})\n}\n\n// @Tags File\n// @Summary Move file\n// @Accept json\n// @Param request body request.FileMove true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/move [post]\n// @x-panel-log {\"bodyKeys\":[\"oldPaths\",\"newPath\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"移动文件 [oldPaths] => [newPath]\",\"formatEN\":\"Move [oldPaths] => [newPath]\"}\nfunc (b *BaseApi) MoveFile(c *gin.Context) {\n\tvar req request.FileMove\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := fileService.MvFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Download file\n// @Accept json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/download [get]\nfunc (b *BaseApi) Download(c *gin.Context) {\n\tfilePath := c.Query(\"path\")\n\tfile, err := os.Open(filePath)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tdefer file.Close()\n\tinfo, err := file.Stat()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tc.Header(\"Content-Length\", strconv.FormatInt(info.Size(), 10))\n\tc.Header(\"Content-Disposition\", \"attachment; filename*=utf-8''\"+url.PathEscape(info.Name()))\n\thttp.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)\n}\n\n// @Tags File\n// @Summary Chunk Download file\n// @Accept json\n// @Param request body request.FileDownload true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/chunkdownload [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"下载文件 [name]\",\"formatEN\":\"Download file [name]\"}\nfunc (b *BaseApi) DownloadChunkFiles(c *gin.Context) {\n\tvar req request.FileChunkDownload\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(req.Path) {\n\t\thelper.ErrorWithDetail(c, http.StatusInternalServerError, \"ErrPathNotFound\", nil)\n\t\treturn\n\t}\n\tfilePath := req.Path\n\tfstFile, err := fileOp.OpenFile(filePath)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tinfo, err := fstFile.Stat()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tif info.IsDir() {\n\t\thelper.ErrorWithDetail(c, http.StatusInternalServerError, \"ErrFileDownloadDir\", err)\n\t\treturn\n\t}\n\n\tc.Writer.Header().Set(\"Content-Disposition\", fmt.Sprintf(\"attachment; filename=%s\", req.Name))\n\tc.Writer.Header().Set(\"Content-Type\", \"application/octet-stream\")\n\tc.Writer.Header().Set(\"Content-Length\", strconv.FormatInt(info.Size(), 10))\n\tc.Writer.Header().Set(\"Accept-Ranges\", \"bytes\")\n\n\tif c.Request.Header.Get(\"Range\") != \"\" {\n\t\trangeHeader := c.Request.Header.Get(\"Range\")\n\t\trangeArr := strings.Split(rangeHeader, \"=\")[1]\n\t\trangeParts := strings.Split(rangeArr, \"-\")\n\n\t\tstartPos, _ := strconv.ParseInt(rangeParts[0], 10, 64)\n\n\t\tvar endPos int64\n\t\tif rangeParts[1] == \"\" {\n\t\t\tendPos = info.Size() - 1\n\t\t} else {\n\t\t\tendPos, _ = strconv.ParseInt(rangeParts[1], 10, 64)\n\t\t}\n\n\t\tc.Writer.Header().Set(\"Content-Range\", fmt.Sprintf(\"bytes %d-%d/%d\", startPos, endPos, info.Size()))\n\t\tc.Writer.WriteHeader(http.StatusPartialContent)\n\n\t\tbuffer := make([]byte, 1024*1024)\n\t\tfile, err := os.Open(filePath)\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\tdefer file.Close()\n\n\t\t_, _ = file.Seek(startPos, 0)\n\t\treader := io.LimitReader(file, endPos-startPos+1)\n\t\t_, err = io.CopyBuffer(c.Writer, reader, buffer)\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tc.File(filePath)\n\t}\n}\n\n// @Tags File\n// @Summary Load file size\n// @Accept json\n// @Param request body request.DirSizeReq true \"request\"\n// @Success 200 {object} response.DirSizeRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/size [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"获取文件夹大小 [path]\",\"formatEN\":\"Load file size [path]\"}\nfunc (b *BaseApi) Size(c *gin.Context) {\n\tvar req request.DirSizeReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := fileService.DirSize(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags File\n// @Summary Multi file size\n// @Accept json\n// @Param request body request.DirSizeReq true \"request\"\n// @Success 200 {array} response.DepthDirSizeRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/depth/size [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"获取目录及其第一层子目录文件夹大小 [path]\",\"formatEN\":\"Multi file size [path]\"}\nfunc (b *BaseApi) DepthDirSize(c *gin.Context) {\n\tvar req request.DirSizeReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := fileService.DepthDirSize(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\nfunc mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int, overwrite bool) error {\n\tdefer func() {\n\t\t_ = os.RemoveAll(fileDir)\n\t}()\n\n\top := files.NewFileOp()\n\tdstDir = strings.TrimSpace(dstDir)\n\tmode, _ := files.GetParentMode(dstDir)\n\tif mode == 0 {\n\t\tmode = constant.DirPerm\n\t}\n\tif _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {\n\t\tif err = op.CreateDir(dstDir, mode); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tdstFileName := filepath.Join(dstDir, fileName)\n\tdstInfo, statErr := os.Stat(dstFileName)\n\tif statErr == nil {\n\t\tmode = dstInfo.Mode()\n\t} else {\n\t\tmode = 0644\n\t}\n\tif overwrite {\n\t\t_ = os.Remove(dstFileName)\n\t}\n\ttargetFile, err := os.OpenFile(dstFileName, os.O_RDWR|os.O_CREATE, mode)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer targetFile.Close()\n\tfor i := 0; i < chunkCount; i++ {\n\t\tchunkPath := filepath.Join(fileDir, fmt.Sprintf(\"%s.%d\", fileName, i))\n\t\tchunkData, err := os.ReadFile(chunkPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = targetFile.Write(chunkData)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = os.Remove(chunkPath)\n\t}\n\n\treturn nil\n}\n\n// @Tags File\n// @Summary ChunkUpload file\n// @Param file formData file true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/chunkupload [post]\nfunc (b *BaseApi) UploadChunkFiles(c *gin.Context) {\n\tvar err error\n\tfileForm, err := c.FormFile(\"chunk\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tuploadFile, err := fileForm.Open()\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdefer uploadFile.Close()\n\tchunkIndex, err := strconv.Atoi(c.PostForm(\"chunkIndex\"))\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tchunkCount, err := strconv.Atoi(c.PostForm(\"chunkCount\"))\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\ttmpDir := path.Join(global.Dir.TmpDir, \"upload\")\n\tif !fileOp.Stat(tmpDir) {\n\t\tif err := fileOp.CreateDir(tmpDir, constant.DirPerm); err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t}\n\tfilename := c.PostForm(\"filename\")\n\tfileDir := filepath.Join(tmpDir, filename)\n\tif chunkIndex == 0 {\n\t\tif fileOp.Stat(fileDir) {\n\t\t\t_ = fileOp.DeleteDir(fileDir)\n\t\t}\n\t\t_ = os.MkdirAll(fileDir, constant.DirPerm)\n\t}\n\tfilePath := filepath.Join(fileDir, filename)\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = os.Remove(fileDir)\n\t\t}\n\t}()\n\tvar (\n\t\temptyFile *os.File\n\t\tchunkData []byte\n\t)\n\n\temptyFile, err = os.Create(filePath)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdefer emptyFile.Close()\n\n\tchunkData, err = io.ReadAll(uploadFile)\n\tif err != nil {\n\t\thelper.InternalServer(c, buserr.WithMap(\"ErrFileUpload\", map[string]interface{}{\"name\": filename, \"detail\": err.Error()}, err))\n\t\treturn\n\t}\n\n\tchunkPath := filepath.Join(fileDir, fmt.Sprintf(\"%s.%d\", filename, chunkIndex))\n\terr = os.WriteFile(chunkPath, chunkData, constant.DirPerm)\n\tif err != nil {\n\t\thelper.InternalServer(c, buserr.WithMap(\"ErrFileUpload\", map[string]interface{}{\"name\": filename, \"detail\": err.Error()}, err))\n\t\treturn\n\t}\n\n\tif chunkIndex+1 == chunkCount {\n\t\toverwrite := true\n\t\tif ow := c.PostForm(\"overwrite\"); ow != \"\" {\n\t\t\toverwrite, _ = strconv.ParseBool(ow)\n\t\t}\n\t\terr = mergeChunks(filename, fileDir, c.PostForm(\"path\"), chunkCount, overwrite)\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, buserr.WithMap(\"ErrFileUpload\", map[string]interface{}{\"name\": filename, \"detail\": err.Error()}, err))\n\t\t\treturn\n\t\t}\n\t\thelper.SuccessWithData(c, true)\n\t} else {\n\t\treturn\n\t}\n}\n\nvar wsUpgrade = websocket.Upgrader{\n\tCheckOrigin: func(r *http.Request) bool {\n\t\treturn true\n\t},\n}\n\nfunc (b *BaseApi) WgetProcess(c *gin.Context) {\n\tws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\twsClient := websocket2.NewWsClient(\"fileClient\", ws)\n\tgo wsClient.Read()\n\tgo wsClient.Write()\n}\n\nfunc (b *BaseApi) ProcessKeys(c *gin.Context) {\n\tres := &response.FileProcessKeys{}\n\tkeys := global.CACHE.PrefixScanKey(\"file-wget-\")\n\tfor _, key := range keys {\n\t\tvalue := global.CACHE.Get(key)\n\t\tif value == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tvar process files.Process\n\t\tif err := json.Unmarshal([]byte(value), &process); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tres.Keys = append(res.Keys, key)\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags File\n// @Summary Read file by Line\n// @Param request body request.FileReadByLineReq true \"request\"\n// @Success 200 {object} response.FileLineContent\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/read [post]\nfunc (b *BaseApi) ReadFileByLine(c *gin.Context) {\n\tvar req request.FileReadByLineReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := fileService.ReadLogByLine(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tif res.TotalLines > 100 {\n\t\thelper.SuccessWithDataGzipped(c, res)\n\t} else {\n\t\thelper.SuccessWithData(c, res)\n\t}\n}\n\n// @Tags File\n// @Summary Batch change file mode and owner\n// @Accept json\n// @Param request body request.FileRoleReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/batch/role [post]\n// @x-panel-log {\"bodyKeys\":[\"paths\",\"mode\",\"user\",\"group\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"批量修改文件权限和用户/组 [paths] => [mode]/[user]/[group]\",\"formatEN\":\"Batch change file mode and owner [paths] => [mode]/[user]/[group]\"}\nfunc (b *BaseApi) BatchChangeModeAndOwner(c *gin.Context) {\n\tvar req request.FileRoleReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := fileService.BatchChangeModeAndOwner(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) GetPathByType(c *gin.Context) {\n\tpathType, ok := c.Params.Get(\"type\")\n\tif !ok {\n\t\thelper.BadRequest(c, errors.New(\"error pathType id in path\"))\n\t\treturn\n\t}\n\tresPath := fileService.GetPathByType(pathType)\n\thelper.SuccessWithData(c, resPath)\n}\n\n// @Tags File\n// @Summary system mount\n// @Accept json\n// @Success 200 {object} dto.DiskInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/mount [post]\nfunc (b *BaseApi) GetHostMount(c *gin.Context) {\n\tdisks := fileService.GetHostMount()\n\thelper.SuccessWithData(c, disks)\n}\n\n// @Tags File\n// @Summary system user and group\n// @Accept json\n// @Success 200 {object} response.UserGroupResponse\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/user/group [post]\nfunc (b *BaseApi) GetUsersAndGroups(c *gin.Context) {\n\tres, err := fileService.GetUsersAndGroups()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags File\n// @Summary Convert file\n// @Accept json\n// @Param request body request.FileConvert true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/convert [post]\nfunc (b *BaseApi) ConvertFile(c *gin.Context) {\n\tvar req request.FileConvertRequest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfileService.Convert(req)\n\thelper.SuccessWithData(c, nil)\n}\n\n// @Tags File\n// @Summary Convert file\n// @Accept json\n// @Param request body dto.PageInfo true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/convert/log [post]\nfunc (b *BaseApi) ConvertLog(c *gin.Context) {\n\tvar req dto.PageInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, logs, err := fileService.ConvertLog(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: logs,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags File\n// @Summary Batch get file remarks\n// @Accept json\n// @Param request body request.FileRemarkBatch true \"request\"\n// @Success 200 {object} response.FileRemarksRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/remarks [post]\nfunc (b *BaseApi) BatchGetFileRemarks(c *gin.Context) {\n\tvar req request.FileRemarkBatch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tremarks := fileService.BatchGetRemarks(req)\n\thelper.SuccessWithData(c, response.FileRemarksRes{Remarks: remarks})\n}\n\n// @Tags File\n// @Summary Set file remark\n// @Accept json\n// @Param request body request.FileRemarkUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/remark [post]\nfunc (b *BaseApi) SetFileRemark(c *gin.Context) {\n\tvar req request.FileRemarkUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := fileService.SetRemark(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/firewall.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Firewall\n// @Summary Load firewall base info\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Success 200 {object} dto.FirewallBaseInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/base [post]\nfunc (b *BaseApi) LoadFirewallBaseInfo(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := firewallService.LoadBaseInfo(req.Name)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Firewall\n// @Summary Page firewall rules\n// @Accept json\n// @Param request body dto.RuleSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/search [post]\nfunc (b *BaseApi) SearchFirewallRule(c *gin.Context) {\n\tvar req dto.RuleSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := firewallService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Firewall\n// @Summary Operate firewall\n// @Accept json\n// @Param request body dto.FirewallOperation true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operation\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operation] 防火墙\",\"formatEN\":\"[operation] firewall\"}\nfunc (b *BaseApi) OperateFirewall(c *gin.Context) {\n\tvar req dto.FirewallOperation\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := firewallService.OperateFirewall(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary Create group\n// @Accept json\n// @Param request body dto.PortRuleOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/port [post]\n// @x-panel-log {\"bodyKeys\":[\"port\",\"strategy\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"添加端口规则 [strategy] [port]\",\"formatEN\":\"create port rules [strategy][port]\"}\nfunc (b *BaseApi) OperatePortRule(c *gin.Context) {\n\tvar req dto.PortRuleOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := firewallService.OperatePortRule(req, true); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// OperateForwardRule\n// @Tags Firewall\n// @Summary Operate forward rule\n// @Accept json\n// @Param request body dto.ForwardRuleOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/forward [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新端口转发规则\",\"formatEN\":\"update port forward rules\"}\nfunc (b *BaseApi) OperateForwardRule(c *gin.Context) {\n\tvar req dto.ForwardRuleOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := firewallService.OperateForwardRule(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary Operate Ip rule\n// @Accept json\n// @Param request body dto.AddrRuleOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/ip [post]\n// @x-panel-log {\"bodyKeys\":[\"strategy\",\"address\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"添加 ip 规则 [strategy] [address]\",\"formatEN\":\"create address rules [strategy][address]\"}\nfunc (b *BaseApi) OperateIPRule(c *gin.Context) {\n\tvar req dto.AddrRuleOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := firewallService.OperateAddressRule(req, true); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary Batch operate rule\n// @Accept json\n// @Param request body dto.BatchRuleOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/batch [post]\nfunc (b *BaseApi) BatchOperateRule(c *gin.Context) {\n\tvar req dto.BatchRuleOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := firewallService.BatchOperateRule(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary Update rule description\n// @Accept json\n// @Param request body dto.UpdateFirewallDescription true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/update/description [post]\nfunc (b *BaseApi) UpdateFirewallDescription(c *gin.Context) {\n\tvar req dto.UpdateFirewallDescription\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := firewallService.UpdateDescription(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary Update port rule\n// @Accept json\n// @Param request body dto.PortRuleUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/update/port [post]\nfunc (b *BaseApi) UpdatePortRule(c *gin.Context) {\n\tvar req dto.PortRuleUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := firewallService.UpdatePortRule(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary Update Ip rule\n// @Accept json\n// @Param request body dto.AddrRuleUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/update/addr [post]\nfunc (b *BaseApi) UpdateAddrRule(c *gin.Context) {\n\tvar req dto.AddrRuleUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := firewallService.UpdateAddrRule(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary search iptables filter rules\n// @Accept json\n// @Param request body dto.SearchPageWithType true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/filter/search [post]\nfunc (b *BaseApi) SearchFilterRules(c *gin.Context) {\n\tvar req dto.SearchPageWithType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := iptablesService.Search(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Firewall\n// @Summary Operate iptables filter rule\n// @Accept json\n// @Param request body dto.IptablesRuleOp true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/filter/rule/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operation\",\"chain\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operation] filter规则到 [chain]\",\"formatEN\":\"[operation] filter rule to [chain]\"}\nfunc (b *BaseApi) OperateFilterRule(c *gin.Context) {\n\tvar req dto.IptablesRuleOp\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := iptablesService.OperateRule(req, true); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary Batch operate iptables filter rules\n// @Accept json\n// @Param request body dto.IptablesBatchOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/filter/rule/batch [post]\nfunc (b *BaseApi) BatchOperateFilterRule(c *gin.Context) {\n\tvar req dto.IptablesBatchOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := iptablesService.BatchOperate(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary Apply/Unload/Init iptables filter\n// @Accept json\n// @Param request body dto.IptablesOp true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/filter/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operate] iptables filter 防火墙\",\"formatEN\":\"[operate] iptables filter firewall\"}\nfunc (b *BaseApi) OperateFilterChain(c *gin.Context) {\n\tvar req dto.IptablesOp\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := iptablesService.Operate(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Firewall\n// @Summary load chain status with name\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/firewall/filter/chain/status [post]\nfunc (b *BaseApi) LoadChainStatus(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, iptablesService.LoadChainStatus(req))\n}\n"
  },
  {
    "path": "agent/app/api/v2/ftp.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags FTP\n// @Summary Load FTP base info\n// @Success 200 {object} dto.FtpBaseInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/ftp/base [get]\nfunc (b *BaseApi) LoadFtpBaseInfo(c *gin.Context) {\n\tdata, err := ftpService.LoadBaseInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags FTP\n// @Summary Load FTP operation log\n// @Accept json\n// @Param request body dto.FtpLogSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/ftp/log/search [post]\nfunc (b *BaseApi) LoadFtpLogInfo(c *gin.Context) {\n\tvar req dto.FtpLogSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := ftpService.LoadLog(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags FTP\n// @Summary Operate FTP\n// @Accept json\n// @Param request body dto.Operate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/ftp/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operation\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operation] FTP\",\"formatEN\":\"[operation] FTP\"}\nfunc (b *BaseApi) OperateFtp(c *gin.Context) {\n\tvar req dto.Operate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := ftpService.Operate(req.Operation); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags FTP\n// @Summary Page FTP user\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/ftp/search [post]\nfunc (b *BaseApi) SearchFtp(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := ftpService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags FTP\n// @Summary Create FTP user\n// @Accept json\n// @Param request body dto.FtpCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/ftp [post]\n// @x-panel-log {\"bodyKeys\":[\"user\", \"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建 FTP 账户 [user][path]\",\"formatEN\":\"create FTP [user][path]\"}\nfunc (b *BaseApi) CreateFtp(c *gin.Context) {\n\tvar req dto.FtpCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.Password) != 0 {\n\t\tpass, err := base64.StdEncoding.DecodeString(req.Password)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Password = string(pass)\n\t}\n\tif _, err := ftpService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags FTP\n// @Summary Delete FTP user\n// @Accept json\n// @Param request body dto.BatchDeleteReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/ftp/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"ftps\",\"output_column\":\"user\",\"output_value\":\"users\"}],\"formatZH\":\"删除 FTP 账户 [users]\",\"formatEN\":\"delete FTP users [users]\"}\nfunc (b *BaseApi) DeleteFtp(c *gin.Context) {\n\tvar req dto.BatchDeleteReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := ftpService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags FTP\n// @Summary Sync FTP user\n// @Accept json\n// @Param request body dto.BatchDeleteReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/ftp/sync [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"同步 FTP 账户\",\"formatEN\":\"sync FTP users\"}\nfunc (b *BaseApi) SyncFtp(c *gin.Context) {\n\tif err := ftpService.Sync(); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags FTP\n// @Summary Update FTP user\n// @Accept json\n// @Param request body dto.FtpUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /toolbox/ftp/update [post]\n// @x-panel-log {\"bodyKeys\":[\"user\", \"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改 FTP 账户 [user][path]\",\"formatEN\":\"update FTP [user][path]\"}\nfunc (b *BaseApi) UpdateFtp(c *gin.Context) {\n\tvar req dto.FtpUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.Password) != 0 {\n\t\tpass, err := base64.StdEncoding.DecodeString(req.Password)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Password = string(pass)\n\t}\n\tif err := ftpService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/group.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags System Group\n// @Summary Create group\n// @Accept json\n// @Param request body dto.GroupCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /groups [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建组 [name][type]\",\"formatEN\":\"create group [name][type]\"}\nfunc (b *BaseApi) CreateGroup(c *gin.Context) {\n\tvar req dto.GroupCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := groupService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Group\n// @Summary Delete group\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /groups/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"groups\",\"output_column\":\"name\",\"output_value\":\"name\"},{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"groups\",\"output_column\":\"type\",\"output_value\":\"type\"}],\"formatZH\":\"删除组 [type][name]\",\"formatEN\":\"delete group [type][name]\"}\nfunc (b *BaseApi) DeleteGroup(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := groupService.Delete(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Group\n// @Summary Update group\n// @Accept json\n// @Param request body dto.GroupUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /groups/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新组 [name][type]\",\"formatEN\":\"update group [name][type]\"}\nfunc (b *BaseApi) UpdateGroup(c *gin.Context) {\n\tvar req dto.GroupUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := groupService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Group\n// @Summary List groups\n// @Accept json\n// @Param request body dto.GroupSearch true \"request\"\n// @Success 200 {array} dto.OperateByType\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /groups/search [post]\nfunc (b *BaseApi) ListGroup(c *gin.Context) {\n\tvar req dto.OperateByType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tlist, err := groupService.List(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n"
  },
  {
    "path": "agent/app/api/v2/health.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (b *BaseApi) CheckHealth(c *gin.Context) {\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/helper/helper.go",
    "content": "package helper\n\nimport (\n\t\"compress/gzip\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/pkg/errors\"\n)\n\nfunc ErrorWithDetail(ctx *gin.Context, code int, msgKey string, err error) {\n\tres := dto.Response{\n\t\tCode:    code,\n\t\tMessage: \"\",\n\t}\n\tres.Message = i18n.GetMsgWithDetail(msgKey, err.Error())\n\tctx.JSON(http.StatusOK, res)\n\tctx.Abort()\n}\n\nfunc InternalServer(ctx *gin.Context, err error) {\n\tErrorWithDetail(ctx, http.StatusInternalServerError, \"ErrInternalServer\", err)\n}\n\nfunc BadRequest(ctx *gin.Context, err error) {\n\tErrorWithDetail(ctx, http.StatusBadRequest, \"ErrInvalidParams\", err)\n}\n\nfunc SuccessWithData(ctx *gin.Context, data interface{}) {\n\tif data == nil {\n\t\tdata = gin.H{}\n\t}\n\tres := dto.Response{\n\t\tCode: http.StatusOK,\n\t\tData: data,\n\t}\n\tctx.JSON(http.StatusOK, res)\n\tctx.Abort()\n}\n\nvar gzipWriterPool = sync.Pool{\n\tNew: func() interface{} {\n\t\treturn gzip.NewWriter(nil)\n\t},\n}\n\nfunc SuccessWithDataGzipped(ctx *gin.Context, data interface{}) {\n\tif !strings.Contains(ctx.GetHeader(\"Accept-Encoding\"), \"gzip\") {\n\t\tSuccessWithData(ctx, data)\n\t\treturn\n\t}\n\tif data == nil {\n\t\tdata = gin.H{}\n\t}\n\tres := dto.Response{\n\t\tCode: http.StatusOK,\n\t\tData: data,\n\t}\n\tjsonBytes, err := json.Marshal(res)\n\tif err != nil {\n\t\tErrorWithDetail(ctx, http.StatusInternalServerError, \"ErrInternalServer\", err)\n\t\treturn\n\t}\n\n\tctx.Header(\"Content-Encoding\", \"gzip\")\n\tctx.Header(\"Content-Type\", \"application/json; charset=utf-8\")\n\tctx.Status(http.StatusOK)\n\n\tgz := gzipWriterPool.Get().(*gzip.Writer)\n\tgz.Reset(ctx.Writer)\n\t_, _ = gz.Write(jsonBytes)\n\t_ = gz.Close()\n\tgzipWriterPool.Put(gz)\n\tctx.Abort()\n}\n\nfunc Success(ctx *gin.Context) {\n\tres := dto.Response{\n\t\tCode:    http.StatusOK,\n\t\tMessage: \"success\",\n\t}\n\tctx.JSON(http.StatusOK, res)\n\tctx.Abort()\n}\n\nfunc SuccessWithMsg(ctx *gin.Context, msg string) {\n\tres := dto.Response{\n\t\tCode:    http.StatusOK,\n\t\tMessage: msg,\n\t}\n\tctx.JSON(http.StatusOK, res)\n\tctx.Abort()\n}\n\nfunc GetParamID(c *gin.Context) (uint, error) {\n\tidParam, ok := c.Params.Get(\"id\")\n\tif !ok {\n\t\treturn 0, errors.New(\"error id in path\")\n\t}\n\tintNum, _ := strconv.Atoi(idParam)\n\treturn uint(intNum), nil\n}\n\nfunc GetIntParamByKey(c *gin.Context, key string) (uint, error) {\n\tidParam, ok := c.Params.Get(key)\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"error %s in path\", key)\n\t}\n\tintNum, _ := strconv.Atoi(idParam)\n\treturn uint(intNum), nil\n}\n\nfunc GetStrParamByKey(c *gin.Context, key string) (string, error) {\n\tidParam, ok := c.Params.Get(key)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"error %s in path\", key)\n\t}\n\treturn idParam, nil\n}\n\nfunc GetTxAndContext() (tx *gorm.DB, ctx context.Context) {\n\ttx = global.DB.Begin()\n\tctx = context.WithValue(context.Background(), constant.DB, tx)\n\treturn\n}\n\nfunc CheckBindAndValidate(req interface{}, c *gin.Context) error {\n\tif err := c.ShouldBindJSON(req); err != nil {\n\t\tErrorWithDetail(c, http.StatusBadRequest, \"ErrInvalidParams\", err)\n\t\treturn err\n\t}\n\tif err := global.VALID.Struct(req); err != nil {\n\t\tErrorWithDetail(c, http.StatusBadRequest, \"ErrInvalidParams\", err)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc CheckBind(req interface{}, c *gin.Context) error {\n\tif err := c.ShouldBindJSON(&req); err != nil {\n\t\tErrorWithDetail(c, http.StatusBadRequest, \"ErrInvalidParams\", err)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc GetParamInt32(paramName string, c *gin.Context) (int32, error) {\n\tidParam, ok := c.Params.Get(paramName)\n\tif !ok {\n\t\treturn 0, errors.New(\"error id in path\")\n\t}\n\tintNum, _ := strconv.Atoi(idParam)\n\treturn int32(intNum), nil\n}\n"
  },
  {
    "path": "agent/app/api/v2/host.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (b *BaseApi) CreateHost(c *gin.Context) {\n\tvar req dto.HostOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\thost, err := hostService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, host)\n}\n\nfunc (b *BaseApi) TestByInfo(c *gin.Context) {\n\tvar req dto.HostConnTest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, hostService.TestByInfo(req))\n}\n\nfunc (b *BaseApi) TestByID(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, hostService.TestLocalConn(req.ID))\n}\n\nfunc (b *BaseApi) HostTree(c *gin.Context) {\n\tvar req dto.SearchForTree\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := hostService.SearchForTree(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\nfunc (b *BaseApi) SearchHost(c *gin.Context) {\n\tvar req dto.SearchPageWithGroup\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := hostService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{Items: list, Total: total})\n}\n\nfunc (b *BaseApi) DeleteHost(c *gin.Context) {\n\tvar req dto.OperateByIDs\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := hostService.Delete(req.IDs); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) UpdateHost(c *gin.Context) {\n\tvar req dto.HostOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tvar err error\n\tif len(req.Password) != 0 && req.AuthMode == \"password\" {\n\t\treq.Password, err = hostService.EncryptHost(req.Password)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.PrivateKey = \"\"\n\t\treq.PassPhrase = \"\"\n\t}\n\tif len(req.PrivateKey) != 0 && req.AuthMode == \"key\" {\n\t\treq.PrivateKey, err = hostService.EncryptHost(req.PrivateKey)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\tif len(req.PassPhrase) != 0 {\n\t\t\treq.PassPhrase, err = encrypt.StringEncrypt(req.PassPhrase)\n\t\t\tif err != nil {\n\t\t\t\thelper.BadRequest(c, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\treq.Password = \"\"\n\t}\n\n\tupMap := map[string]interface{}{\n\t\t\"name\":              req.Name,\n\t\t\"group_id\":          req.GroupID,\n\t\t\"addr\":              req.Addr,\n\t\t\"port\":              req.Port,\n\t\t\"user\":              req.User,\n\t\t\"auth_mode\":         req.AuthMode,\n\t\t\"remember_password\": req.RememberPassword,\n\t\t\"description\":       req.Description,\n\t}\n\tif req.AuthMode == \"password\" {\n\t\tupMap[\"password\"] = req.Password\n\t\tupMap[\"private_key\"] = \"\"\n\t\tupMap[\"pass_phrase\"] = \"\"\n\t} else {\n\t\tupMap[\"password\"] = \"\"\n\t\tupMap[\"private_key\"] = req.PrivateKey\n\t\tupMap[\"pass_phrase\"] = req.PassPhrase\n\t}\n\thostItem, err := hostService.Update(req.ID, upMap)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, hostItem)\n}\n\nfunc (b *BaseApi) UpdateHostGroup(c *gin.Context) {\n\tvar req dto.ChangeGroup\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif _, err := hostService.Update(req.ID, map[string]interface{}{\"group_id\": req.GroupID}); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) GetHostByID(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tinfo, err := hostService.GetHostByID(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, info)\n}\n"
  },
  {
    "path": "agent/app/api/v2/host_tool.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Host tool\n// @Summary Get tool status\n// @Accept json\n// @Param request body request.HostToolReq true \"request\"\n// @Success 200 {object} response.HostToolRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/tool [post]\nfunc (b *BaseApi) GetToolStatus(c *gin.Context) {\n\tvar req request.HostToolReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tconfig, err := hostToolService.GetToolStatus(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, config)\n}\n\n// @Tags Host tool\n// @Summary Create Host tool Config\n// @Accept json\n// @Param request body request.HostToolCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/tool/init [post]\n// @x-panel-log {\"bodyKeys\":[\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建 [type] 配置\",\"formatEN\":\"create [type] config\"}\nfunc (b *BaseApi) InitToolConfig(c *gin.Context) {\n\tvar req request.HostToolCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := hostToolService.CreateToolConfig(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Host tool\n// @Summary Operate tool\n// @Accept json\n// @Param request body request.HostToolReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/tool/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operate\",\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operate] [type] \",\"formatEN\":\"[operate] [type]\"}\nfunc (b *BaseApi) OperateTool(c *gin.Context) {\n\tvar req request.HostToolReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := hostToolService.OperateTool(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Host tool\n// @Summary Get tool config\n// @Accept json\n// @Param request body request.HostToolConfig true \"request\"\n// @Success 200 {object} response.HostToolConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/tool/config [post]\n// @x-panel-log {\"bodyKeys\":[\"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operate] 主机工具配置文件 \",\"formatEN\":\"[operate] tool config\"}\nfunc (b *BaseApi) OperateToolConfig(c *gin.Context) {\n\tvar req request.HostToolConfig\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tconfig, err := hostToolService.OperateToolConfig(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, config)\n}\n\n// @Tags Host tool\n// @Summary Create Supervisor process\n// @Accept json\n// @Param request body request.SupervisorProcessConfig true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/tool/supervisor/process [post]\n// @x-panel-log {\"bodyKeys\":[\"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operate] 守护进程 \",\"formatEN\":\"[operate] process\"}\nfunc (b *BaseApi) OperateProcess(c *gin.Context) {\n\tvar req request.SupervisorProcessConfig\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\terr := hostToolService.OperateSupervisorProcess(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Host tool\n// @Summary Get Supervisor process config\n// @Accept json\n// @Success 200 {object} response.SupervisorProcessConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/tool/supervisor/process [get]\nfunc (b *BaseApi) GetProcess(c *gin.Context) {\n\tconfigs, err := hostToolService.GetSupervisorProcessConfig()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, configs)\n}\n\n// @Tags Host tool\n// @Summary Get Supervisor process config file\n// @Accept json\n// @Param request body request.SupervisorProcessFileReq true \"request\"\n// @Success 200 {string} content\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/tool/supervisor/process/file [post]\n// @x-panel-log {\"bodyKeys\":[\"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operate] Supervisor 进程文件 \",\"formatEN\":\"[operate] Supervisor Process Config file\"}\nfunc (b *BaseApi) GetProcessFile(c *gin.Context) {\n\tvar req request.SupervisorProcessFileReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tcontent, err := hostToolService.OperateSupervisorProcessFile(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, content)\n}\n"
  },
  {
    "path": "agent/app/api/v2/image.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Container Image\n// @Summary Page images\n// @Accept json\n// @Param request body dto.PageImage true \"request\"\n// @Produce json\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/search [post]\nfunc (b *BaseApi) SearchImage(c *gin.Context) {\n\tvar req dto.PageImage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := imageService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Container Image\n// @Summary List all images\n// @Produce json\n// @Success 200 {array} dto.ImageInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/all [get]\nfunc (b *BaseApi) ListAllImage(c *gin.Context) {\n\tlist, err := imageService.ListAll()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Container Image\n// @Summary load images options\n// @Produce json\n// @Success 200 {array} dto.Options\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image [get]\nfunc (b *BaseApi) ListImage(c *gin.Context) {\n\tlist, err := imageService.List()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Container Image\n// @Summary Build image\n// @Accept json\n// @Param request body dto.ImageBuild true \"request\"\n// @Success 200 {string} log\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/build [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"构建镜像 [name]\",\"formatEN\":\"build image [name]\"}\nfunc (b *BaseApi) ImageBuild(c *gin.Context) {\n\tvar req dto.ImageBuild\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageService.ImageBuild(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Image\n// @Summary Pull image\n// @Accept json\n// @Param request body dto.ImagePull true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/pull [post]\n// @x-panel-log {\"bodyKeys\":[\"repoID\",\"imageName\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"repoID\",\"isList\":false,\"db\":\"image_repos\",\"output_column\":\"name\",\"output_value\":\"reponame\"}],\"formatZH\":\"镜像拉取 [reponame][imageName]\",\"formatEN\":\"image pull [reponame][imageName]\"}\nfunc (b *BaseApi) ImagePull(c *gin.Context) {\n\tvar req dto.ImagePull\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageService.ImagePull(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Image\n// @Summary Push image\n// @Accept json\n// @Param request body dto.ImagePush true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/push [post]\n// @x-panel-log {\"bodyKeys\":[\"repoID\",\"tagName\",\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"repoID\",\"isList\":false,\"db\":\"image_repos\",\"output_column\":\"name\",\"output_value\":\"reponame\"}],\"formatZH\":\"[tagName] 推送到 [reponame][name]\",\"formatEN\":\"push [tagName] to [reponame][name]\"}\nfunc (b *BaseApi) ImagePush(c *gin.Context) {\n\tvar req dto.ImagePush\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageService.ImagePush(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Image\n// @Summary Delete image\n// @Accept json\n// @Param request body dto.BatchDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/remove [post]\n// @x-panel-log {\"bodyKeys\":[\"names\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"移除镜像 [names]\",\"formatEN\":\"remove image [names]\"}\nfunc (b *BaseApi) ImageRemove(c *gin.Context) {\n\tvar req dto.BatchDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageService.ImageRemove(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Image\n// @Summary Save image\n// @Accept json\n// @Param request body dto.ImageSave true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/save [post]\n// @x-panel-log {\"bodyKeys\":[\"tagName\",\"path\",\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"保留 [tagName] 为 [path]/[name]\",\"formatEN\":\"save [tagName] as [path]/[name]\"}\nfunc (b *BaseApi) ImageSave(c *gin.Context) {\n\tvar req dto.ImageSave\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageService.ImageSave(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Image\n// @Summary Tag image\n// @Accept json\n// @Param request body dto.ImageTag true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/tag [post]\n// @x-panel-log {\"bodyKeys\":[\"repoID\",\"targetName\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"repoID\",\"isList\":false,\"db\":\"image_repos\",\"output_column\":\"name\",\"output_value\":\"reponame\"}],\"formatZH\":\"tag 镜像 [reponame][targetName]\",\"formatEN\":\"tag image [reponame][targetName]\"}\nfunc (b *BaseApi) ImageTag(c *gin.Context) {\n\tvar req dto.ImageTag\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageService.ImageTag(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Container Image\n// @Summary Load image\n// @Accept json\n// @Param request body dto.ImageLoad true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/image/load [post]\n// @x-panel-log {\"bodyKeys\":[\"path\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"从 [path] 加载镜像\",\"formatEN\":\"load image from [path]\"}\nfunc (b *BaseApi) ImageLoad(c *gin.Context) {\n\tvar req dto.ImageLoad\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageService.ImageLoad(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/image_repo.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Container Image-repo\n// @Summary Page image repos\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Produce json\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/repo/search [post]\nfunc (b *BaseApi) SearchRepo(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := imageRepoService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Container Image-repo\n// @Summary List image repos\n// @Produce json\n// @Success 200 {array} dto.ImageRepoOption\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/repo [get]\nfunc (b *BaseApi) ListRepo(c *gin.Context) {\n\tlist, err := imageRepoService.List()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Container Image-repo\n// @Summary Load repo status\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Produce json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/repo/status [post]\nfunc (b *BaseApi) CheckRepoStatus(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageRepoService.Login(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Image-repo\n// @Summary Create image repo\n// @Accept json\n// @Param request body dto.ImageRepoDelete true \"request\"\n// @Produce json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/repo [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建镜像仓库 [name]\",\"formatEN\":\"create image repo [name]\"}\nfunc (b *BaseApi) CreateRepo(c *gin.Context) {\n\tvar req dto.ImageRepoCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageRepoService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Image-repo\n// @Summary Delete image repo\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Produce json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/repo/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"image_repos\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"删除镜像仓库 [name]\",\"formatEN\":\"delete image repo [name]\"}\nfunc (b *BaseApi) DeleteRepo(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageRepoService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Container Image-repo\n// @Summary Update image repo\n// @Accept json\n// @Param request body dto.ImageRepoUpdate true \"request\"\n// @Produce json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /containers/repo/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"image_repos\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"更新镜像仓库 [name]\",\"formatEN\":\"update image repo information [name]\"}\nfunc (b *BaseApi) UpdateRepo(c *gin.Context) {\n\tvar req dto.ImageRepoUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := imageRepoService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/logs.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Logs\n// @Summary Load system log files\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /logs/system/files [get]\nfunc (b *BaseApi) GetSystemFiles(c *gin.Context) {\n\tdata, err := logService.ListSystemLogFile()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, data)\n}\n"
  },
  {
    "path": "agent/app/api/v2/mcp_server.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags McpServer\n// @Summary List mcp servers\n// @Accept json\n// @Param request body request.McpServerSearch true \"request\"\n// @Success 200 {object} response.McpServersRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/mcp/search [post]\nfunc (b *BaseApi) PageMcpServers(c *gin.Context) {\n\tvar req request.McpServerSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tlist := mcpServerService.Page(req)\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags McpServer\n// @Summary Create mcp server\n// @Accept json\n// @Param request body request.McpServerCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/mcp/server [post]\nfunc (b *BaseApi) CreateMcpServer(c *gin.Context) {\n\tvar req request.McpServerCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := mcpServerService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags McpServer\n// @Summary Update mcp server\n// @Accept json\n// @Param request body request.McpServerUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/mcp/server/update [post]\nfunc (b *BaseApi) UpdateMcpServer(c *gin.Context) {\n\tvar req request.McpServerUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := mcpServerService.Update(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags McpServer\n// @Summary Delete mcp server\n// @Accept json\n// @Param request body request.McpServerDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/mcp/server/del [post]\nfunc (b *BaseApi) DeleteMcpServer(c *gin.Context) {\n\tvar req request.McpServerDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := mcpServerService.Delete(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags McpServer\n// @Summary Operate mcp server\n// @Accept json\n// @Param request body request.McpServerOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/mcp/server/op [post]\nfunc (b *BaseApi) OperateMcpServer(c *gin.Context) {\n\tvar req request.McpServerOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := mcpServerService.Operate(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags McpServer\n// @Summary Bind Domain for mcp server\n// @Accept json\n// @Param request body request.McpBindDomain true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/mcp/domain/bind [post]\nfunc (b *BaseApi) BindMcpDomain(c *gin.Context) {\n\tvar req request.McpBindDomain\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := mcpServerService.BindDomain(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags McpServer\n// @Summary Update bind Domain for mcp server\n// @Accept json\n// @Param request body request.McpBindDomainUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/mcp/domain/update [post]\nfunc (b *BaseApi) UpdateMcpBindDomain(c *gin.Context) {\n\tvar req request.McpBindDomainUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := mcpServerService.UpdateBindDomain(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags McpServer\n// @Summary Get bin Domain for mcp server\n// @Accept json\n// @Success 200 {object} response.McpBindDomainRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /ai/mcp/domain/get [get]\nfunc (b *BaseApi) GetMcpBindDomain(c *gin.Context) {\n\tres, err := mcpServerService.GetBindDomain()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n"
  },
  {
    "path": "agent/app/api/v2/monitor.go",
    "content": "package v2\n\nimport (\n\t\"sort\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/v4/disk\"\n\t\"github.com/shirou/gopsutil/v4/net\"\n)\n\n// @Tags Monitor\n// @Summary Load monitor data\n// @Param request body dto.MonitorSearch true \"request\"\n// @Success 200 {array} dto.MonitorData\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/monitor/search [post]\nfunc (b *BaseApi) LoadMonitor(c *gin.Context) {\n\tvar req dto.MonitorSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := monitorService.LoadMonitorData(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithDataGzipped(c, data)\n}\n\n// @Tags Monitor\n// @Summary Load monitor data\n// @Param request body dto.MonitorGPUSearch true \"request\"\n// @Success 200 {object} dto.MonitorGPUData\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/monitor/gpu/search [post]\nfunc (b *BaseApi) LoadGPUMonitor(c *gin.Context) {\n\tvar req dto.MonitorGPUSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := monitorService.LoadGPUMonitorData(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Monitor\n// @Summary Clean monitor data\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/monitor/clean [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"清空监控数据\",\"formatEN\":\"clean monitor datas\"}\nfunc (b *BaseApi) CleanMonitor(c *gin.Context) {\n\tif err := monitorService.CleanData(); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags Monitor\n// @Summary Load monitor setting\n// @Success 200 {object} dto.MonitorSetting\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/monitor/setting [get]\nfunc (b *BaseApi) LoadMonitorSetting(c *gin.Context) {\n\tsetting, err := monitorService.LoadSetting()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, setting)\n}\n\n// @Tags Monitor\n// @Summary Update monitor setting\n// @Param request body dto.MonitorSettingUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/monitor/setting/update [post]\n// @x-panel-log {\"bodyKeys\":[\"key\", \"value\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改默认监控网卡 [name]-[value]\",\"formatEN\":\"update default monitor [name]-[value]\"}\nfunc (b *BaseApi) UpdateMonitorSetting(c *gin.Context) {\n\tvar req dto.MonitorSettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := monitorService.UpdateSetting(req.Key, req.Value); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) GetNetworkOptions(c *gin.Context) {\n\tnetStat, _ := net.IOCounters(true)\n\tvar options []string\n\toptions = append(options, \"all\")\n\tfor _, net := range netStat {\n\t\toptions = append(options, net.Name)\n\t}\n\tsort.Strings(options)\n\thelper.SuccessWithData(c, options)\n}\n\nfunc (b *BaseApi) GetIOOptions(c *gin.Context) {\n\tdiskStat, _ := disk.IOCounters()\n\tvar options []string\n\toptions = append(options, \"all\")\n\tfor _, net := range diskStat {\n\t\toptions = append(options, net.Name)\n\t}\n\tsort.Strings(options)\n\thelper.SuccessWithData(c, options)\n}\n\nfunc (b *BaseApi) GetCPUOptions(c *gin.Context) {\n\thelper.SuccessWithData(c, monitorService.LoadGPUOptions())\n}\n"
  },
  {
    "path": "agent/app/api/v2/nginx.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags OpenResty\n// @Summary Load OpenResty conf\n// @Success 200 {object} response.NginxFile\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty [get]\nfunc (b *BaseApi) GetNginx(c *gin.Context) {\n\tfileInfo, err := nginxService.GetNginxConfig()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, fileInfo)\n}\n\n// @Tags OpenResty\n// @Summary Load partial OpenResty conf\n// @Accept json\n// @Param request body request.NginxScopeReq true \"request\"\n// @Success 200 {array} response.NginxParam\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/scope [post]\nfunc (b *BaseApi) GetNginxConfigByScope(c *gin.Context) {\n\tvar req request.NginxScopeReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tparams, err := nginxService.GetConfigByScope(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, params)\n}\n\n// @Tags OpenResty\n// @Summary Update OpenResty conf\n// @Accept json\n// @Param request body request.NginxConfigUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/update [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteId\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteId\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"更新 nginx 配置 [domain]\",\"formatEN\":\"Update nginx conf [domain]\"}\nfunc (b *BaseApi) UpdateNginxConfigByScope(c *gin.Context) {\n\tvar req request.NginxConfigUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := nginxService.UpdateConfigByScope(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags OpenResty\n// @Summary Load OpenResty status info\n// @Success 200 {object} response.NginxStatus\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/status [get]\nfunc (b *BaseApi) GetNginxStatus(c *gin.Context) {\n\tres, err := nginxService.GetStatus()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags OpenResty\n// @Summary Update OpenResty conf by upload file\n// @Accept json\n// @Param request body request.NginxConfigFileUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/file [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新 nginx 配置\",\"formatEN\":\"Update nginx conf\"}\nfunc (b *BaseApi) UpdateNginxFile(c *gin.Context) {\n\tvar req request.NginxConfigFileUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := nginxService.UpdateConfigFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags OpenResty\n// @Summary Build OpenResty\n// @Accept json\n// @Param request body request.NginxBuildReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/build [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"构建 OpenResty\",\"formatEN\":\"Build OpenResty\"}\nfunc (b *BaseApi) BuildNginx(c *gin.Context) {\n\tvar req request.NginxBuildReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := nginxService.Build(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags OpenResty\n// @Summary Update OpenResty module\n// @Accept json\n// @Param request body request.NginxModuleUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/modules/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新 OpenResty 模块\",\"formatEN\":\"Update OpenResty module\"}\nfunc (b *BaseApi) UpdateNginxModule(c *gin.Context) {\n\tvar req request.NginxModuleUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := nginxService.UpdateModule(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags OpenResty\n// @Summary Get OpenResty modules\n// @Success 200 {object} response.NginxBuildConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/modules [get]\nfunc (b *BaseApi) GetNginxModules(c *gin.Context) {\n\tmodules, err := nginxService.GetModules()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, modules)\n}\n\n// @Tags OpenResty\n// @Summary Operate default HTTPs\n// @Accept json\n// @Param request body request.NginxDefaultHTTPSUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/https [post]\nfunc (b *BaseApi) OperateDefaultHTTPs(c *gin.Context) {\n\tvar req request.NginxDefaultHTTPSUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := nginxService.OperateDefaultHTTPs(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags OpenResty\n// @Summary Get default HTTPs status\n// @Success 200 {object} response.NginxConfigRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /openresty/https [get]\nfunc (b *BaseApi) GetDefaultHTTPsStatus(c *gin.Context) {\n\tres, err := nginxService.GetDefaultHttpsStatus()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n"
  },
  {
    "path": "agent/app/api/v2/php_extensions.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags PHP Extensions\n// @Summary Page Extensions\n// @Accept json\n// @Param request body request.PHPExtensionsSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/extensions/search [post]\nfunc (b *BaseApi) PagePHPExtensions(c *gin.Context) {\n\tvar req request.PHPExtensionsSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif req.All {\n\t\tlist, err := phpExtensionsService.List()\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\thelper.SuccessWithData(c, dto.PageResult{\n\t\t\tTotal: int64(len(list)),\n\t\t\tItems: list,\n\t\t})\n\t} else {\n\t\ttotal, list, err := phpExtensionsService.Page(req)\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\thelper.SuccessWithData(c, dto.PageResult{\n\t\t\tTotal: total,\n\t\t\tItems: list,\n\t\t})\n\t}\n\n}\n\n// @Tags PHP Extensions\n// @Summary Create Extensions\n// @Accept json\n// @Param request body request.PHPExtensionsCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/extensions [post]\nfunc (b *BaseApi) CreatePHPExtensions(c *gin.Context) {\n\tvar req request.PHPExtensionsCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := phpExtensionsService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags PHP Extensions\n// @Summary Update Extensions\n// @Accept json\n// @Param request body request.PHPExtensionsUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/extensions/update [post]\nfunc (b *BaseApi) UpdatePHPExtensions(c *gin.Context) {\n\tvar req request.PHPExtensionsUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := phpExtensionsService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags PHP Extensions\n// @Summary Delete Extensions\n// @Accept json\n// @Param request body request.PHPExtensionsDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/extensions/del [post]\nfunc (b *BaseApi) DeletePHPExtensions(c *gin.Context) {\n\tvar req request.PHPExtensionsDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := phpExtensionsService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/process.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\twebsocket2 \"github.com/1Panel-dev/1Panel/agent/utils/websocket\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (b *BaseApi) ProcessWs(c *gin.Context) {\n\tws, err := wsUpgrade.Upgrade(c.Writer, c.Request, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\twsClient := websocket2.NewWsClient(\"processClient\", ws)\n\tgo wsClient.Read()\n\tgo wsClient.Write()\n}\n\n// @Tags Process\n// @Summary Stop Process\n// @Param request body request.ProcessReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /process/stop [post]\n// @x-panel-log {\"bodyKeys\":[\"PID\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"结束进程 [PID]\",\"formatEN\":\"结束进程 [PID]\"}\nfunc (b *BaseApi) StopProcess(c *gin.Context) {\n\tvar req request.ProcessReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := processService.StopProcess(req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Process\n// @Summary Get Process Info By PID\n// @Param pid path int true \"PID\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /process/{pid} [get]\nfunc (b *BaseApi) GetProcessInfoByPID(c *gin.Context) {\n\tpid, err := helper.GetParamInt32(\"pid\", c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdata, err := processService.GetProcessInfoByPID(pid)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Process\n// @Summary Get Listening Process\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /process/listening [post]\nfunc (b *BaseApi) GetListeningProcess(c *gin.Context) {\n\tprocs, err := processService.GetListeningProcess(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, procs)\n}\n"
  },
  {
    "path": "agent/app/api/v2/recycle_bin.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags File\n// @Summary List RecycleBin files\n// @Accept json\n// @Param request body dto.PageInfo true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/recycle/search [post]\nfunc (b *BaseApi) SearchRecycleBinFile(c *gin.Context) {\n\tvar req dto.PageInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, list, err := recycleBinService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags File\n// @Summary Reduce RecycleBin files\n// @Accept json\n// @Param request body request.RecycleBinReduce true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/recycle/reduce [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"还原回收站文件 [name]\",\"formatEN\":\"Reduce RecycleBin file [name]\"}\nfunc (b *BaseApi) ReduceRecycleBinFile(c *gin.Context) {\n\tvar req request.RecycleBinReduce\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := recycleBinService.Reduce(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Clear RecycleBin files\n// @Accept json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/recycle/clear [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"清空回收站\",\"formatEN\":\"清空回收站\"}\nfunc (b *BaseApi) ClearRecycleBinFile(c *gin.Context) {\n\tif err := recycleBinService.Clear(); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags File\n// @Summary Get RecycleBin status\n// @Accept json\n// @Success 200 {string} content\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /files/recycle/status [get]\nfunc (b *BaseApi) GetRecycleStatus(c *gin.Context) {\n\tsettingInfo, err := settingService.GetSettingInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, settingInfo.FileRecycleBin)\n}\n"
  },
  {
    "path": "agent/app/api/v2/runtime.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Runtime\n// @Summary List runtimes\n// @Accept json\n// @Param request body request.RuntimeSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/search [post]\nfunc (b *BaseApi) SearchRuntimes(c *gin.Context) {\n\tvar req request.RuntimeSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, items, err := runtimeService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: items,\n\t})\n}\n\n// @Tags Runtime\n// @Summary Create runtime\n// @Accept json\n// @Param request body request.RuntimeCreate true \"request\"\n// @Success 200 {object} model.Runtime\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建运行环境 [name]\",\"formatEN\":\"Create runtime [name]\"}\nfunc (b *BaseApi) CreateRuntime(c *gin.Context) {\n\tvar req request.RuntimeCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tssl, err := runtimeService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, ssl)\n}\n\n// @Tags Website\n// @Summary Delete runtime\n// @Accept json\n// @Param request body request.RuntimeDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"删除运行环境 [name]\",\"formatEN\":\"Delete runtime [name]\"}\nfunc (b *BaseApi) DeleteRuntime(c *gin.Context) {\n\tvar req request.RuntimeDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := runtimeService.Delete(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Delete runtime\n// @Accept json\n// @Success 200 {array} dto.AppResource\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/installed/delete/check/:id [get]\nfunc (b *BaseApi) DeleteRuntimeCheck(c *gin.Context) {\n\trunTimeId, err := helper.GetIntParamByKey(c, \"id\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tcheckData, err := runtimeService.DeleteCheck(runTimeId)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, checkData)\n}\n\n// @Tags Runtime\n// @Summary Update runtime\n// @Accept json\n// @Param request body request.RuntimeUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新运行环境 [name]\",\"formatEN\":\"Update runtime [name]\"}\nfunc (b *BaseApi) UpdateRuntime(c *gin.Context) {\n\tvar req request.RuntimeUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := runtimeService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Get runtime\n// @Accept json\n// @Param id path string true \"request\"\n// @Success 200 {object} response.RuntimeDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/:id [get]\nfunc (b *BaseApi) GetRuntime(c *gin.Context) {\n\tid, err := helper.GetIntParamByKey(c, \"id\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tres, err := runtimeService.Get(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Runtime\n// @Summary Get Node package scripts\n// @Accept json\n// @Param request body request.NodePackageReq true \"request\"\n// @Success 200 {array} response.PackageScripts\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/node/package [post]\nfunc (b *BaseApi) GetNodePackageRunScript(c *gin.Context) {\n\tvar req request.NodePackageReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := runtimeService.GetNodePackageRunScript(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Runtime\n// @Summary Operate runtime\n// @Accept json\n// @Param request body request.RuntimeOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"操作运行环境 [name]\",\"formatEN\":\"Operate runtime [name]\"}\nfunc (b *BaseApi) OperateRuntime(c *gin.Context) {\n\tvar req request.RuntimeOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := runtimeService.OperateRuntime(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Get Node modules\n// @Accept json\n// @Param request body request.NodeModuleReq true \"request\"\n// @Success 200 {array} response.NodeModule\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/node/modules [post]\nfunc (b *BaseApi) GetNodeModules(c *gin.Context) {\n\tvar req request.NodeModuleReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := runtimeService.GetNodeModules(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Runtime\n// @Summary Operate Node modules\n// @Accept json\n// @Param request body request.NodeModuleReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/node/modules/operate [post]\nfunc (b *BaseApi) OperateNodeModules(c *gin.Context) {\n\tvar req request.NodeModuleOperateReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := runtimeService.OperateNodeModules(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Sync runtime status\n// @Accept json\n// @Success 200\n// @Security ApiKeyAuth\n// @Router /runtimes/sync [post]\nfunc (b *BaseApi) SyncStatus(c *gin.Context) {\n\terr := runtimeService.SyncRuntimeStatus()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Get php runtime extension\n// @Accept json\n// @Param id path string true \"request\"\n// @Success 200 {object} response.PHPExtensionRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/:id/extensions [get]\nfunc (b *BaseApi) GetRuntimeExtension(c *gin.Context) {\n\tid, err := helper.GetIntParamByKey(c, \"id\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tres, err := runtimeService.GetPHPExtensions(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Runtime\n// @Summary Install php extension\n// @Accept json\n// @Param request body request.PHPExtensionInstallReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/extensions/install [post]\nfunc (b *BaseApi) InstallPHPExtension(c *gin.Context) {\n\tvar req request.PHPExtensionInstallReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := runtimeService.InstallPHPExtension(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary UnInstall php extension\n// @Accept json\n// @Param request body request.PHPExtensionInstallReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/extensions/uninstall [post]\nfunc (b *BaseApi) UnInstallPHPExtension(c *gin.Context) {\n\tvar req request.PHPExtensionInstallReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := runtimeService.UnInstallPHPExtension(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Load php runtime conf\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {object} response.PHPConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/config/:id [get]\nfunc (b *BaseApi) GetPHPConfig(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdata, err := runtimeService.GetPHPConfig(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Runtime\n// @Summary Update runtime php conf\n// @Accept json\n// @Param request body request.PHPConfigUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/config [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"[domain] PHP 配置修改\",\"formatEN\":\"[domain] PHP conf update\"}\nfunc (b *BaseApi) UpdatePHPConfig(c *gin.Context) {\n\tvar req request.PHPConfigUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := runtimeService.UpdatePHPConfig(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Update php conf file\n// @Accept json\n// @Param request body request.PHPFileUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/update [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteId\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteId\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"php 配置修改 [domain]\",\"formatEN\":\"Nginx conf update [domain]\"}\nfunc (b *BaseApi) UpdatePHPFile(c *gin.Context) {\n\tvar req request.PHPFileUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := runtimeService.UpdatePHPConfigFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Get php conf file\n// @Accept json\n// @Param request body request.PHPFileReq true \"request\"\n// @Success 200 {object} response.FileInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/file [post]\nfunc (b *BaseApi) GetPHPConfigFile(c *gin.Context) {\n\tvar req request.PHPFileReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := runtimeService.GetPHPConfigFile(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Runtime\n// @Summary Update fpm config\n// @Accept json\n// @Param request body request.FPMConfig true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/fpm/config [post]\nfunc (b *BaseApi) UpdateFPMConfig(c *gin.Context) {\n\tvar req request.FPMConfig\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := runtimeService.UpdateFPMConfig(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Get fpm config\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {object} request.FPMConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/fpm/config/:id [get]\nfunc (b *BaseApi) GetFPMConfig(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdata, err := runtimeService.GetFPMConfig(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Runtime\n// @Summary Get supervisor process\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {array} response.SupervisorProcessConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/supervisor/process/:id [get]\nfunc (b *BaseApi) GetSupervisorProcess(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdata, err := runtimeService.GetSupervisorProcess(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Runtime\n// @Summary Operate supervisor process\n// @Accept json\n// @Param request body request.PHPSupervisorProcessConfig true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/supervisor/process [post]\nfunc (b *BaseApi) OperateSupervisorProcess(c *gin.Context) {\n\tvar req request.PHPSupervisorProcessConfig\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := runtimeService.OperateSupervisorProcess(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Operate supervisor process file\n// @Accept json\n// @Param request body request.PHPSupervisorProcessFileReq true \"request\"\n// @Success 200 {string} content\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/supervisor/process/file [post]\nfunc (b *BaseApi) OperateSupervisorProcessFile(c *gin.Context) {\n\tvar req request.PHPSupervisorProcessFileReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := runtimeService.OperateSupervisorProcessFile(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Runtime\n// @Summary Update PHP container config\n// @Accept json\n// @Param request body request.PHPContainerConfig true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/container/update [post]\nfunc (b *BaseApi) UpdatePHPContainer(c *gin.Context) {\n\tvar req request.PHPContainerConfig\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := runtimeService.UpdatePHPContainer(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Get PHP container config\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {object} request.PHPContainerConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/container/:id [get]\nfunc (b *BaseApi) GetPHPContainerConfig(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdata, err := runtimeService.GetPHPContainerConfig(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Runtime\n// @Summary Update runtime remark\n// @Accept json\n// @Param request body request.RuntimeRemark true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/remark [post]\nfunc (b *BaseApi) UpdateRuntimeRemark(c *gin.Context) {\n\tvar req request.RuntimeRemark\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := runtimeService.UpdateRemark(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Runtime\n// @Summary Get PHP runtime status\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {object} map[string]interface{}\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /runtimes/php/fpm/status/:id [get]\nfunc (b *BaseApi) GetFPMStatus(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tdata, err := runtimeService.GetFPMStatus(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n"
  },
  {
    "path": "agent/app/api/v2/setting.go",
    "content": "package v2\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ssh\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/pkg/errors\"\n)\n\n// @Tags System Setting\n// @Summary Load system setting info\n// @Success 200 {object} dto.SettingInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/search [post]\nfunc (b *BaseApi) GetSettingInfo(c *gin.Context) {\n\tsetting, err := settingService.GetSettingInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, setting)\n}\n\nfunc (b *BaseApi) GetTerminalAISettingInfo(c *gin.Context) {\n\tsetting, err := settingService.GetTerminalAIInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, setting)\n}\n\n// @Tags System Setting\n// @Summary Load system available status\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/search/available [get]\nfunc (b *BaseApi) GetSystemAvailable(c *gin.Context) {\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update system setting\n// @Accept json\n// @Param request body dto.SettingUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/update [post]\n// @x-panel-log {\"bodyKeys\":[\"key\",\"value\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改系统配置 [key] => [value]\",\"formatEN\":\"update system setting [key] => [value]\"}\nfunc (b *BaseApi) UpdateSetting(c *gin.Context) {\n\tvar req dto.SettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.Update(req.Key, req.Value); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) UpdateTerminalAISetting(c *gin.Context) {\n\tvar req dto.TerminalAIInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.UpdateTerminalAI(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Load local backup dir\n// @Success 200 {string} path\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/basedir [get]\nfunc (b *BaseApi) LoadBaseDir(c *gin.Context) {\n\thelper.SuccessWithData(c, global.Dir.DataDir)\n}\n\n// @Tags System Setting\n// @Summary Load local conn\n// @Success 200 {object} dto.SSHConnData\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/ssh/conn [get]\nfunc (b *BaseApi) LoadLocalConn(c *gin.Context) {\n\thelper.SuccessWithData(c, settingService.GetLocalConn())\n}\n\nfunc (b *BaseApi) CheckLocalConn(c *gin.Context) {\n\tclient, err := loadLocalConn()\n\tif err == nil && client != nil {\n\t\tclient.Close()\n\t}\n\thelper.SuccessWithData(c, err == nil)\n}\n\n// @Tags System Setting\n// @Summary Update local is conn\n// @Accept json\n// @Param request body dto.SSHDefaultConn true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/ssh/conn/default [post]\n// @x-panel-log {\"bodyKeys\":[\"defaultConn\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"本地终端默认连接 [defaultConn]\",\"formatEN\":\"update system default conn [defaultConn]\"}\nfunc (b *BaseApi) SetDefaultIsConn(c *gin.Context) {\n\tvar req dto.SSHDefaultConn\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.SetDefaultIsConn(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Check local conn info\n// @Success 200 {boolean} isOk\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/ssh/check/info [post]\nfunc (b *BaseApi) CheckLocalConnByInfo(c *gin.Context) {\n\tvar req dto.SSHConnData\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, settingService.TestConnByInfo(req))\n}\n\n// @Tags System Setting\n// @Summary Save local conn info\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/ssh [post]\nfunc (b *BaseApi) SaveLocalConn(c *gin.Context) {\n\tvar req dto.SSHConnData\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.SaveConnInfo(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc loadLocalConn() (*ssh.SSHClient, error) {\n\tconnInfoInDB := settingService.GetSettingByKey(\"LocalSSHConn\")\n\tif len(connInfoInDB) == 0 {\n\t\treturn nil, errors.New(\"no such ssh conn info in db!\")\n\t}\n\tvar connInDB model.LocalConnInfo\n\tif err := json.Unmarshal([]byte(connInfoInDB), &connInDB); err != nil {\n\t\treturn nil, err\n\t}\n\tsshInfo := ssh.ConnInfo{\n\t\tAddr:       connInDB.Addr,\n\t\tPort:       int(connInDB.Port),\n\t\tUser:       connInDB.User,\n\t\tAuthMode:   connInDB.AuthMode,\n\t\tPassword:   connInDB.Password,\n\t\tPrivateKey: []byte(connInDB.PrivateKey),\n\t\tPassPhrase: []byte(connInDB.PassPhrase),\n\t}\n\treturn ssh.NewClient(sshInfo)\n}\n\n// @Tags System Setting\n// @Summary Load system setting by key\n// @Param key path string true \"key\"\n// @Success 200 {object} dto.SettingInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/get/{key} [get]\nfunc (b *BaseApi) GetSettingByKey(c *gin.Context) {\n\tkey := c.Param(\"key\")\n\tif len(key) == 0 {\n\t\thelper.BadRequest(c, errors.New(\"key is empty\"))\n\t\treturn\n\t}\n\tvalue := settingService.GetSettingByKey(key)\n\thelper.SuccessWithData(c, value)\n}\n\n// @Tags System Setting\n// @Summary Save common description\n// @Accept json\n// @Param request body dto.CommonDescription true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/description/save [post]\nfunc (b *BaseApi) SaveDescription(c *gin.Context) {\n\tvar req dto.CommonDescription\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.SaveDescription(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/snapshot.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags System Setting\n// @Summary Load system snapshot data\n// @Success 200 {object} dto.SnapshotData\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot/load [get]\nfunc (b *BaseApi) LoadSnapshotData(c *gin.Context) {\n\tdata, err := snapshotService.LoadSnapshotData()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags System Setting\n// @Summary Create system snapshot\n// @Accept json\n// @Param request body dto.SnapshotCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot [post]\n// @x-panel-log {\"bodyKeys\":[\"from\", \"description\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建系统快照 [description] 到 [from]\",\"formatEN\":\"Create system backup [description] to [from]\"}\nfunc (b *BaseApi) CreateSnapshot(c *gin.Context) {\n\tvar req dto.SnapshotCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := snapshotService.SnapshotCreate(nil, req, 0, 3); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Recreate system snapshot\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot/recreate [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"snapshots\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"重试创建快照 [name]\",\"formatEN\":\"recrete the snapshot [name]\"}\nfunc (b *BaseApi) RecreateSnapshot(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := snapshotService.SnapshotReCreate(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Import system snapshot\n// @Accept json\n// @Param request body dto.SnapshotImport true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot/import [post]\n// @x-panel-log {\"bodyKeys\":[\"from\", \"names\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"从 [from] 同步系统快照 [names]\",\"formatEN\":\"Sync system snapshots [names] from [from]\"}\nfunc (b *BaseApi) ImportSnapshot(c *gin.Context) {\n\tvar req dto.SnapshotImport\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := snapshotService.SnapshotImport(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update snapshot description\n// @Accept json\n// @Param request body dto.UpdateDescription true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot/description/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\",\"description\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"snapshots\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"快照 [name] 描述信息修改 [description]\",\"formatEN\":\"The description of the snapshot [name] is modified => [description]\"}\nfunc (b *BaseApi) UpdateSnapDescription(c *gin.Context) {\n\tvar req dto.UpdateDescription\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := snapshotService.UpdateDescription(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Page system snapshot\n// @Accept json\n// @Param request body dto.PageSnapshot true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot/search [post]\nfunc (b *BaseApi) SearchSnapshot(c *gin.Context) {\n\tvar req dto.PageSnapshot\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, accounts, err := snapshotService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: accounts,\n\t})\n}\n\n// @Tags System Setting\n// @Summary Recover system backup\n// @Accept json\n// @Param request body dto.SnapshotRecover true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot/recover [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"snapshots\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"从系统快照 [name] 恢复\",\"formatEN\":\"Recover from system backup [name]\"}\nfunc (b *BaseApi) RecoverSnapshot(c *gin.Context) {\n\tvar req dto.SnapshotRecover\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := snapshotService.SnapshotRecover(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Rollback system backup\n// @Accept json\n// @Param request body dto.SnapshotRecover true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot/rollback [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"snapshots\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"从系统快照 [name] 回滚\",\"formatEN\":\"Rollback from system backup [name]\"}\nfunc (b *BaseApi) RollbackSnapshot(c *gin.Context) {\n\tvar req dto.SnapshotRecover\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := snapshotService.SnapshotRollback(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Delete system backup\n// @Accept json\n// @Param request body dto.SnapshotBatchDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /settings/snapshot/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"snapshots\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"删除系统快照 [name]\",\"formatEN\":\"Delete system backup [name]\"}\nfunc (b *BaseApi) DeleteSnapshot(c *gin.Context) {\n\tvar req dto.SnapshotBatchDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := snapshotService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/ssh.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags SSH\n// @Summary Load host SSH setting info\n// @Success 200 {object} dto.SSHInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/search [post]\nfunc (b *BaseApi) GetSSHInfo(c *gin.Context) {\n\tinfo, err := sshService.GetSSHInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, info)\n}\n\n// @Tags SSH\n// @Summary Operate SSH\n// @Accept json\n// @Param request body dto.Operate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"operation\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"[operation] SSH \",\"formatEN\":\"[operation] SSH\"}\nfunc (b *BaseApi) OperateSSH(c *gin.Context) {\n\tvar req dto.Operate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := sshService.OperateSSH(req.Operation); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags SSH\n// @Summary Update host SSH setting\n// @Accept json\n// @Param request body dto.SSHUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/update [post]\n// @x-panel-log {\"bodyKeys\":[\"key\",\"newValue\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改 SSH 配置 [key] => [newValue]\",\"formatEN\":\"update SSH setting [key] => [newValue]\"}\nfunc (b *BaseApi) UpdateSSH(c *gin.Context) {\n\tvar req dto.SSHUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := sshService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags SSH\n// @Summary Generate host SSH secret\n// @Accept json\n// @Param request body dto.RootCertOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/cert [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"生成 SSH 密钥 \",\"formatEN\":\"generate SSH secret\"}\nfunc (b *BaseApi) CreateRootCert(c *gin.Context) {\n\tvar req dto.RootCertOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := loadCertAfterDecrypt(&req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t}\n\tif err := sshService.CreateRootCert(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags SSH\n// @Summary Update host SSH secret\n// @Accept json\n// @Param request body dto.RootCertOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/cert/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"生成 SSH 密钥 \",\"formatEN\":\"generate SSH secret\"}\nfunc (b *BaseApi) EditRootCert(c *gin.Context) {\n\tvar req dto.RootCertOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := loadCertAfterDecrypt(&req); err != nil {\n\t\thelper.BadRequest(c, err)\n\t}\n\tif err := sshService.EditRootCert(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags SSH\n// @Summary Sycn host SSH secret\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/cert/sync [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"同步 SSH 密钥 \",\"formatEN\":\"sync SSH secret\"}\nfunc (b *BaseApi) SyncRootCert(c *gin.Context) {\n\tif err := sshService.SyncRootCert(); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags SSH\n// @Summary Load host SSH secret\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/cert/search [post]\nfunc (b *BaseApi) SearchRootCert(c *gin.Context) {\n\tvar req dto.SearchWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, data, err := sshService.SearchRootCerts(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: data,\n\t})\n}\n\n// @Tags SSH\n// @Summary Delete host SSH secret\n// @Accept json\n// @Param request body dto.ForceDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/cert/delete [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"删除 SSH 密钥 \",\"formatEN\":\"delete SSH secret\"}\nfunc (b *BaseApi) DeleteRootCert(c *gin.Context) {\n\tvar req dto.ForceDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := sshService.DeleteRootCerts(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags SSH\n// @Summary Load host SSH logs\n// @Accept json\n// @Param request body dto.SearchSSHLog true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/log [post]\nfunc (b *BaseApi) LoadSSHLogs(c *gin.Context) {\n\tvar req dto.SearchSSHLog\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, data, err := sshService.LoadLog(c, req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: data,\n\t})\n}\n\n// @Tags SSH\n// @Summary Export host SSH logs\n// @Accept json\n// @Param request body dto.SearchSSHLog true \"request\"\n// @Success 200 {string} path\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/log/export [post]\nfunc (b *BaseApi) ExportSSHLogs(c *gin.Context) {\n\tvar req dto.SearchSSHLog\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttmpFile, err := sshService.ExportLog(c, req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, tmpFile)\n}\n\n// @Tags SSH\n// @Summary Load host SSH conf\n// @Accept json\n// @Param request body dto.OperationWithName true \"request\"\n// @Success 200 {string} conf\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/file [post]\nfunc (b *BaseApi) LoadSSHFile(c *gin.Context) {\n\tvar req dto.OperationWithName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tdata, err := sshService.LoadSSHFile(req.Name)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags SSH\n// @Summary Update host SSH setting by file\n// @Accept json\n// @Param request body dto.SSHConf true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/ssh/file/update [post]\n// @x-panel-log {\"bodyKeys\":[\"key\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改 SSH 配置文件 [key]\",\"formatEN\":\"update SSH conf [key]\"}\nfunc (b *BaseApi) UpdateSSHByFile(c *gin.Context) {\n\tvar req dto.SettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := sshService.UpdateByFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc loadCertAfterDecrypt(req *dto.RootCertOperate) error {\n\tif len(req.PassPhrase) != 0 {\n\t\tpassPhrase, err := base64.StdEncoding.DecodeString(req.PassPhrase)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treq.PassPhrase = string(passPhrase)\n\t}\n\tif len(req.PrivateKey) != 0 {\n\t\tprivateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treq.PrivateKey = string(privateKey)\n\t}\n\tif len(req.PublicKey) != 0 {\n\t\tpublicKey, err := base64.StdEncoding.DecodeString(req.PublicKey)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treq.PublicKey = string(publicKey)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/api/v2/system.go",
    "content": "package v2\n\nimport (\n\t\"errors\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Host\n// @Summary Check if a system component exists\n// @Accept json\n// @Param name path string true \"Component name to check (e.g., rsync, docker)\"\n// @Success 200 {object} response.ComponentInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /hosts/components/{name} [get]\nfunc (b *BaseApi) CheckComponentExistence(c *gin.Context) {\n\tname := c.Param(\"name\")\n\tif name == \"\" {\n\t\thelper.BadRequest(c, errors.New(\"empty component name\"))\n\t\treturn\n\t}\n\n\tinfo := systemService.IsComponentExist(name)\n\thelper.SuccessWithData(c, info)\n}\n"
  },
  {
    "path": "agent/app/api/v2/task.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags TaskLog\n// @Summary Page task logs\n// @Accept json\n// @Param request body dto.SearchTaskLogReq true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /logs/tasks/search [post]\nfunc (b *BaseApi) PageTasks(c *gin.Context) {\n\tvar req dto.SearchTaskLogReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, list, err := taskService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags TaskLog\n// @Summary Get the number of executing tasks\n// @Success 200 {integer} int64\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /logs/tasks/executing/count [get]\nfunc (b *BaseApi) CountExecutingTasks(c *gin.Context) {\n\tcount, err := taskService.CountExecutingTask()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, count)\n}\n"
  },
  {
    "path": "agent/app/api/v2/tensorrt_llm.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc (b *BaseApi) PageTensorRTLLMs(c *gin.Context) {\n\tvar req request.TensorRTLLMSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tlist := tensorrtLLMService.Page(req)\n\thelper.SuccessWithData(c, list)\n}\n\nfunc (b *BaseApi) CreateTensorRTLLM(c *gin.Context) {\n\tvar req request.TensorRTLLMCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := tensorrtLLMService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) UpdateTensorRTLLM(c *gin.Context) {\n\tvar req request.TensorRTLLMUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := tensorrtLLMService.Update(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) DeleteTensorRTLLM(c *gin.Context) {\n\tvar req request.TensorRTLLMDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := tensorrtLLMService.Delete(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) OperateTensorRTLLM(c *gin.Context) {\n\tvar req request.TensorRTLLMOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := tensorrtLLMService.Operate(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/terminal.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ssh\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/terminal\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/gorilla/websocket\"\n\t\"github.com/pkg/errors\"\n)\n\nfunc (b *BaseApi) WsSSH(c *gin.Context) {\n\twsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"gin context http handler failed, err: %v\", err)\n\t\treturn\n\t}\n\tdefer wsConn.Close()\n\n\tif global.CONF.Base.IsDemo {\n\t\tif wshandleError(wsConn, errors.New(\"   demo server, prohibit this operation!\")) {\n\t\t\treturn\n\t\t}\n\t}\n\n\tcols, err := strconv.Atoi(c.DefaultQuery(\"cols\", \"80\"))\n\tif wshandleError(wsConn, errors.WithMessage(err, \"invalid param cols in request\")) {\n\t\treturn\n\t}\n\trows, err := strconv.Atoi(c.DefaultQuery(\"rows\", \"40\"))\n\tif wshandleError(wsConn, errors.WithMessage(err, \"invalid param rows in request\")) {\n\t\treturn\n\t}\n\n\thostID, _ := strconv.Atoi(c.DefaultQuery(\"id\", \"0\"))\n\tvar client *ssh.SSHClient\n\tif hostID > 0 {\n\t\thost, err := service.GetHostInfo(uint(hostID))\n\t\tif wshandleError(wsConn, errors.WithMessage(err, \"load host info by id failed\")) {\n\t\t\treturn\n\t\t}\n\t\tconnInfo := ssh.ConnInfo{\n\t\t\tAddr:       host.Addr,\n\t\t\tPort:       int(host.Port),\n\t\t\tUser:       host.User,\n\t\t\tAuthMode:   host.AuthMode,\n\t\t\tPassword:   host.Password,\n\t\t\tPrivateKey: []byte(host.PrivateKey),\n\t\t}\n\t\tif len(host.PassPhrase) != 0 {\n\t\t\tconnInfo.PassPhrase = []byte(host.PassPhrase)\n\t\t}\n\t\tclient, err = ssh.NewClient(connInfo)\n\t\tif wshandleError(wsConn, errors.WithMessage(err, \"failed to set up the connection. Please check the host information\")) {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tclient, err = loadLocalConn()\n\t\tif wshandleError(wsConn, errors.WithMessage(err, \"failed to set up the connection. Please check the host information\")) {\n\t\t\treturn\n\t\t}\n\t}\n\tdefer client.Close()\n\tcommand := c.DefaultQuery(\"command\", \"\")\n\tsws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, command)\n\tif wshandleError(wsConn, err) {\n\t\treturn\n\t}\n\tdefer sws.Close()\n\n\tquitChan := make(chan bool, 3)\n\tsws.Start(quitChan)\n\tgo sws.Wait(quitChan)\n\n\t<-quitChan\n\n\tdt := time.Now().Add(time.Second)\n\t_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)\n}\n\nfunc (b *BaseApi) ContainerWsSSH(c *gin.Context) {\n\twsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"gin context http handler failed, err: %v\", err)\n\t\treturn\n\t}\n\tdefer wsConn.Close()\n\n\tif global.CONF.Base.IsDemo {\n\t\tif wshandleError(wsConn, errors.New(\"   demo server, prohibit this operation!\")) {\n\t\t\treturn\n\t\t}\n\t}\n\n\tcols, err := strconv.Atoi(c.DefaultQuery(\"cols\", \"80\"))\n\tif wshandleError(wsConn, errors.WithMessage(err, \"invalid param cols in request\")) {\n\t\treturn\n\t}\n\trows, err := strconv.Atoi(c.DefaultQuery(\"rows\", \"40\"))\n\tif wshandleError(wsConn, errors.WithMessage(err, \"invalid param rows in request\")) {\n\t\treturn\n\t}\n\tsource := c.Query(\"source\")\n\tvar initCmd []string\n\tswitch source {\n\tcase \"redis\", \"redis-cluster\":\n\t\tinitCmd, err = loadRedisInitCmd(c, source)\n\tcase \"ollama\":\n\t\tinitCmd, err = loadOllamaInitCmd(c)\n\tcase \"container\":\n\t\tinitCmd, err = loadContainerInitCmd(c)\n\tcase \"database\":\n\t\tinitCmd, err = loadDatabaseInitCmd(c)\n\tdefault:\n\t\tif wshandleError(wsConn, fmt.Errorf(\"not support such source %s\", source)) {\n\t\t\treturn\n\t\t}\n\t}\n\tif wshandleError(wsConn, err) {\n\t\treturn\n\t}\n\tslave, err := terminal.NewCommand(\"docker\", initCmd...)\n\tif wshandleError(wsConn, err) {\n\t\treturn\n\t}\n\tdefer slave.Close()\n\n\ttty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)\n\tif wshandleError(wsConn, err) {\n\t\treturn\n\t}\n\n\tquitChan := make(chan bool, 3)\n\ttty.Start(quitChan)\n\tgo slave.Wait(quitChan)\n\n\t<-quitChan\n\n\tglobal.LOG.Info(\"websocket finished\")\n\tdt := time.Now().Add(time.Second)\n\t_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)\n}\n\nfunc loadRedisInitCmd(c *gin.Context, redisType string) ([]string, error) {\n\tname := c.Query(\"name\")\n\tfrom := c.Query(\"from\")\n\tcommands := []string{\"exec\", \"-it\"}\n\tdatabase, err := databaseService.Get(name)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"no such database in db, err: %v\", err)\n\t}\n\tif from == \"local\" {\n\t\tredisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: redisType})\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"no such app in db, err: %v\", err)\n\t\t}\n\t\tname = redisInfo.ContainerName\n\t\tcommands = append(commands, []string{name, \"redis-cli\"}...)\n\t\tif len(database.Password) != 0 {\n\t\t\tcommands = append(commands, []string{\"-a\", database.Password, \"--no-auth-warning\"}...)\n\t\t}\n\t} else {\n\t\tname = \"1Panel-redis-cli-tools\"\n\t\tcommands = append(commands, []string{name, \"redis-cli\", \"-h\", database.Address, \"-p\", fmt.Sprintf(\"%v\", database.Port)}...)\n\t\tif len(database.Password) != 0 {\n\t\t\tcommands = append(commands, []string{\"-a\", database.Password, \"--no-auth-warning\"}...)\n\t\t}\n\t}\n\treturn commands, nil\n}\n\nfunc loadOllamaInitCmd(c *gin.Context) ([]string, error) {\n\tname := c.Query(\"name\")\n\tif cmd.CheckIllegal(name) {\n\t\treturn nil, fmt.Errorf(\"ollama model %s contains illegal characters\", name)\n\t}\n\tollamaInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: \"\", Type: \"ollama\"})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"no such app in db, err: %v\", err)\n\t}\n\tcontainerName := ollamaInfo.ContainerName\n\treturn []string{\"exec\", \"-it\", containerName, \"ollama\", \"run\", name}, nil\n}\n\nfunc loadContainerInitCmd(c *gin.Context) ([]string, error) {\n\tcontainerID := c.Query(\"containerid\")\n\tcommand := c.Query(\"command\")\n\tuser := c.Query(\"user\")\n\tif cmd.CheckIllegal(user, containerID, command) {\n\t\treturn nil, fmt.Errorf(\"the command contains illegal characters. command: %s, user: %s, containerID: %s\", command, user, containerID)\n\t}\n\tif len(command) == 0 || len(containerID) == 0 {\n\t\treturn nil, fmt.Errorf(\"error param of command: %s or containerID: %s\", command, containerID)\n\t}\n\tcommands := []string{\"exec\", \"-it\", containerID, command}\n\tif len(user) != 0 {\n\t\tcommands = []string{\"exec\", \"-it\", \"-u\", user, containerID, command}\n\t}\n\n\treturn commands, nil\n}\n\nfunc loadDatabaseInitCmd(c *gin.Context) ([]string, error) {\n\tdatabase := c.Query(\"database\")\n\tdatabaseType := c.Query(\"databaseType\")\n\tif len(database) == 0 || len(databaseType) == 0 {\n\t\treturn nil, fmt.Errorf(\"error param of database: %s or database type: %s\", database, databaseType)\n\t}\n\tdatabaseConn, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Type: databaseType, Name: database})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"no such database in db, err: %v\", err)\n\t}\n\tcommands := []string{\"exec\", \"-it\", databaseConn.ContainerName}\n\tswitch databaseType {\n\tcase \"mysql\", \"mysql-cluster\":\n\t\tcommands = append(commands, []string{\"mysql\", \"-uroot\", \"-p\" + databaseConn.Password}...)\n\tcase \"mariadb\":\n\t\tcommands = append(commands, []string{\"mariadb\", \"-uroot\", \"-p\" + databaseConn.Password}...)\n\tcase \"postgresql\", \"postgresql-cluster\":\n\t\tcommands = []string{\"exec\", \"-e\", fmt.Sprintf(\"PGPASSWORD=%s\", databaseConn.Password), \"-it\", databaseConn.ContainerName, \"psql\", \"-t\", \"-U\", databaseConn.Username}\n\t}\n\n\treturn commands, nil\n}\n\nfunc wshandleError(ws *websocket.Conn, err error) bool {\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"handler ws faled:, err: %v\", err)\n\t\tdt := time.Now().Add(time.Second)\n\t\tif ctlerr := ws.WriteControl(websocket.CloseMessage, []byte(err.Error()), dt); ctlerr != nil {\n\t\t\twsData, err := json.Marshal(terminal.WsMsg{\n\t\t\t\tType: terminal.WsMsgCmd,\n\t\t\t\tData: base64.StdEncoding.EncodeToString([]byte(err.Error())),\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\t_ = ws.WriteMessage(websocket.TextMessage, []byte(\"{\\\"type\\\":\\\"cmd\\\",\\\"data\\\":\\\"failed to encoding to json\\\"}\"))\n\t\t\t} else {\n\t\t\t\t_ = ws.WriteMessage(websocket.TextMessage, wsData)\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\nvar upGrader = websocket.Upgrader{\n\tReadBufferSize:  4096,\n\tWriteBufferSize: 16384,\n\tCheckOrigin: func(r *http.Request) bool {\n\t\treturn true\n\t},\n}\n"
  },
  {
    "path": "agent/app/api/v2/website.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Website\n// @Summary Page websites\n// @Accept json\n// @Param request body request.WebsiteSearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/search [post]\nfunc (b *BaseApi) PageWebsite(c *gin.Context) {\n\tvar req request.WebsiteSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, websites, err := websiteService.PageWebsite(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: websites,\n\t})\n}\n\n// @Tags Website\n// @Summary List websites\n// @Success 200 {array} response.WebsiteDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/list [get]\nfunc (b *BaseApi) GetWebsites(c *gin.Context) {\n\twebsites, err := websiteService.GetWebsites()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, websites)\n}\n\n// @Tags Website\n// @Summary List website names\n// @Success 200 {array} response.WebsiteOption\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/options [post]\nfunc (b *BaseApi) GetWebsiteOptions(c *gin.Context) {\n\tvar req request.WebsiteOptionReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\twebsites, err := websiteService.GetWebsiteOptions(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, websites)\n}\n\n// @Tags Website\n// @Summary Create website\n// @Accept json\n// @Param request body request.WebsiteCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites [post]\n// @x-panel-log {\"bodyKeys\":[\"alias\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建网站 [alias]\",\"formatEN\":\"Create website [alias]\"}\nfunc (b *BaseApi) CreateWebsite(c *gin.Context) {\n\tvar req request.WebsiteCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := websiteService.CreateWebsite(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Operate website\n// @Accept json\n// @Param request body request.WebsiteOp true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/operate [post]\n// @x-panel-log {\"bodyKeys\":[\"id\", \"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"[operate] 网站 [domain]\",\"formatEN\":\"[operate] website [domain]\"}\nfunc (b *BaseApi) OpWebsite(c *gin.Context) {\n\tvar req request.WebsiteOp\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := websiteService.OpWebsite(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Delete website\n// @Accept json\n// @Param request body request.WebsiteDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"删除网站 [domain]\",\"formatEN\":\"Delete website [domain]\"}\nfunc (b *BaseApi) DeleteWebsite(c *gin.Context) {\n\tvar req request.WebsiteDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := websiteService.DeleteWebsite(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update website\n// @Accept json\n// @Param request body request.WebsiteUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/update [post]\n// @x-panel-log {\"bodyKeys\":[\"primaryDomain\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新网站 [primaryDomain]\",\"formatEN\":\"Update website [primaryDomain]\"}\nfunc (b *BaseApi) UpdateWebsite(c *gin.Context) {\n\tvar req request.WebsiteUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateWebsite(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Search website by id\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {object} response.WebsiteDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/:id [get]\nfunc (b *BaseApi) GetWebsite(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\twebsite, err := websiteService.GetWebsite(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, website)\n}\n\n// @Tags Website Nginx\n// @Summary Search website nginx by id\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {object} response.FileInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/:id/config/:type [get]\nfunc (b *BaseApi) GetWebsiteNginx(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tconfigType := c.Param(\"type\")\n\n\tfileInfo, err := websiteService.GetWebsiteNginxConfig(id, configType)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, fileInfo)\n}\n\n// @Tags Website Nginx\n// @Summary Load nginx conf\n// @Accept json\n// @Param request body request.NginxScopeReq true \"request\"\n// @Success 200 {object} response.WebsiteNginxConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/config [post]\nfunc (b *BaseApi) GetNginxConfig(c *gin.Context) {\n\tvar req request.NginxScopeReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tconfig, err := websiteService.GetNginxConfigByScope(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, config)\n}\n\n// @Tags Website Nginx\n// @Summary Update nginx conf\n// @Accept json\n// @Param request body request.NginxConfigUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/config/update [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteId\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteId\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"nginx 配置修改 [domain]\",\"formatEN\":\"Nginx conf update [domain]\"}\nfunc (b *BaseApi) UpdateNginxConfig(c *gin.Context) {\n\tvar req request.NginxConfigUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateNginxConfigByScope(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website HTTPS\n// @Summary Load https conf\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {object} response.WebsiteHTTPS\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/:id/https [get]\nfunc (b *BaseApi) GetHTTPSConfig(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tres, err := websiteService.GetWebsiteHTTPS(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website HTTPS\n// @Summary Update https conf\n// @Accept json\n// @Param request body request.WebsiteHTTPSOp true \"request\"\n// @Success 200 {object} response.WebsiteHTTPS\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/:id/https [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteId\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteId\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"更新网站 [domain] https 配置\",\"formatEN\":\"Update website https [domain] conf\"}\nfunc (b *BaseApi) UpdateHTTPSConfig(c *gin.Context) {\n\tvar req request.WebsiteHTTPSOp\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttx, ctx := helper.GetTxAndContext()\n\tres, err := websiteService.OpWebsiteHTTPS(ctx, req)\n\tif err != nil {\n\t\ttx.Rollback()\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\ttx.Commit()\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Check before create website\n// @Accept json\n// @Param request body request.WebsiteInstallCheckReq true \"request\"\n// @Success 200 {array} response.WebsitePreInstallCheck\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/check [post]\nfunc (b *BaseApi) CreateWebsiteCheck(c *gin.Context) {\n\tvar req request.WebsiteInstallCheckReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdata, err := websiteService.PreInstallCheck(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Website Nginx\n// @Summary Update website nginx conf\n// @Accept json\n// @Param request body request.WebsiteNginxUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/nginx/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"[domain] Nginx 配置修改\",\"formatEN\":\"[domain] Nginx conf update\"}\nfunc (b *BaseApi) UpdateWebsiteNginxConfig(c *gin.Context) {\n\tvar req request.WebsiteNginxUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateNginxConfigFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Operate website log\n// @Accept json\n// @Param request body request.WebsiteLogReq true \"request\"\n// @Success 200 {object} response.WebsiteLog\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/log [post]\n// @x-panel-log {\"bodyKeys\":[\"id\", \"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"[domain][operate] 日志\",\"formatEN\":\"[domain][operate] logs\"}\nfunc (b *BaseApi) OpWebsiteLog(c *gin.Context) {\n\tvar req request.WebsiteLogReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteService.OpWebsiteLog(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Change default server\n// @Accept json\n// @Param request body request.WebsiteDefaultUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/default/server [post]\n// @x-panel-log {\"bodyKeys\":[\"id\", \"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"修改默认 server => [domain]\",\"formatEN\":\"Change default server => [domain]\"}\nfunc (b *BaseApi) ChangeDefaultServer(c *gin.Context) {\n\tvar req request.WebsiteDefaultUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.ChangeDefaultServer(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website PHP\n// @Summary Update php version\n// @Accept json\n// @Param request body request.WebsitePHPVersionReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/php/version [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteId\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteId\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"php 版本变更 [domain]\",\"formatEN\":\"php version update [domain]\"}\nfunc (b *BaseApi) ChangePHPVersion(c *gin.Context) {\n\tvar req request.WebsitePHPVersionReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.ChangePHPVersion(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get rewrite conf\n// @Accept json\n// @Param request body request.NginxRewriteReq true \"request\"\n// @Success 200 {object} response.NginxRewriteRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/rewrite [post]\nfunc (b *BaseApi) GetRewriteConfig(c *gin.Context) {\n\tvar req request.NginxRewriteReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteService.GetRewriteConfig(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Update rewrite conf\n// @Accept json\n// @Param request body request.NginxRewriteUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/rewrite/update [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteID\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteID\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"伪静态配置修改 [domain]\",\"formatEN\":\"Nginx conf rewrite update [domain]\"}\nfunc (b *BaseApi) UpdateRewriteConfig(c *gin.Context) {\n\tvar req request.NginxRewriteUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateRewriteConfig(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update Site Dir\n// @Accept json\n// @Param request body request.WebsiteUpdateDir true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/dir/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"更新网站 [domain] 目录\",\"formatEN\":\"Update  domain [domain] dir\"}\nfunc (b *BaseApi) UpdateSiteDir(c *gin.Context) {\n\tvar req request.WebsiteUpdateDir\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateSiteDir(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update Site Dir permission\n// @Accept json\n// @Param request body request.WebsiteUpdateDirPermission true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/dir/permission [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"更新网站 [domain] 目录权限\",\"formatEN\":\"Update  domain [domain] dir permission\"}\nfunc (b *BaseApi) UpdateSiteDirPermission(c *gin.Context) {\n\tvar req request.WebsiteUpdateDirPermission\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateSitePermission(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get proxy conf\n// @Accept json\n// @Param request body request.WebsiteProxyReq true \"request\"\n// @Success 200 {array} request.WebsiteProxyConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/proxies [post]\nfunc (b *BaseApi) GetProxyConfig(c *gin.Context) {\n\tvar req request.WebsiteProxyReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteService.GetProxies(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Update proxy conf\n// @Accept json\n// @Param request body request.WebsiteProxyConfig true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/proxies/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\",\"name\",\"operate\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"修改 [operate] 网站 [domain] 反向代理配置 [name] \",\"formatEN\":\"Update [operate] domain [domain] proxy config [name]\"}\nfunc (b *BaseApi) UpdateProxyConfig(c *gin.Context) {\n\tvar req request.WebsiteProxyConfig\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := websiteService.OperateProxy(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Delete proxy config\n// @Accept json\n// @Param request body request.WebsiteProxyDel true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/proxies/delete [post]\n// @x-panel-log {\"bodyKeys\":[\"id\",\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"删除网站 [domain] 反向代理配置 [name] \",\"formatEN\":\"Delete domain [domain] proxy config [name]\"}\nfunc (b *BaseApi) DeleteProxyConfig(c *gin.Context) {\n\tvar req request.WebsiteProxyDel\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := websiteService.DeleteProxy(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update proxy config status\n// @Accept json\n// @Param request body request.WebsiteProxyStatusUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/proxies/status [post]\n// @x-panel-log {\"bodyKeys\":[\"id\",\"name\",\"status\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"修改网站 [domain] 反向代理配置 [name] 状态 [status] \",\"formatEN\":\"Update domain [domain] proxy config [name] status [status]\"}\nfunc (b *BaseApi) UpdateProxyConfigStatus(c *gin.Context) {\n\tvar req request.WebsiteProxyStatusUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := websiteService.UpdateProxyStatus(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update proxy file\n// @Accept json\n// @Param request body request.NginxProxyUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/proxies/file [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteID\",\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteID\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"修改网站 [domain] 反向代理配置文件 [name] \",\"formatEN\":\"Update domain [domain] proxy config file [name]\"}\nfunc (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {\n\tvar req request.NginxProxyUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateProxyFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get AuthBasic conf\n// @Accept json\n// @Param request body request.NginxAuthReq true \"request\"\n// @Success 200 {object} response.NginxAuthRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/auths [post]\nfunc (b *BaseApi) GetAuthConfig(c *gin.Context) {\n\tvar req request.NginxAuthReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteService.GetAuthBasics(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Get AuthBasic conf\n// @Accept json\n// @Param request body request.NginxAuthUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/auths/update [post]\nfunc (b *BaseApi) UpdateAuthConfig(c *gin.Context) {\n\tvar req request.NginxAuthUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateAuthBasic(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get AuthBasic conf\n// @Accept json\n// @Param request body request.NginxAuthReq true \"request\"\n// @Success 200 {object} response.NginxPathAuthRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/auths/path [post]\nfunc (b *BaseApi) GetPathAuthConfig(c *gin.Context) {\n\tvar req request.NginxAuthReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteService.GetPathAuthBasics(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Get AuthBasic conf\n// @Accept json\n// @Param request body request.NginxPathAuthUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/auths/path/update [post]\nfunc (b *BaseApi) UpdatePathAuthConfig(c *gin.Context) {\n\tvar req request.NginxPathAuthUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdatePathAuthBasic(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get AntiLeech conf\n// @Accept json\n// @Param request body request.NginxCommonReq true \"request\"\n// @Success 200 {object} response.NginxAntiLeechRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/leech [post]\nfunc (b *BaseApi) GetAntiLeech(c *gin.Context) {\n\tvar req request.NginxCommonReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteService.GetAntiLeech(req.WebsiteID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Update AntiLeech\n// @Accept json\n// @Param request body request.NginxAntiLeechUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/leech/update [post]\nfunc (b *BaseApi) UpdateAntiLeech(c *gin.Context) {\n\tvar req request.NginxAntiLeechUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateAntiLeech(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update redirect conf\n// @Accept json\n// @Param request body request.NginxRedirectReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/redirect/update [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteID\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteID\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"修改网站 [domain] 重定向配置 \",\"formatEN\":\"Update domain [domain] redirect config\"}\nfunc (b *BaseApi) UpdateRedirectConfig(c *gin.Context) {\n\tvar req request.NginxRedirectReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := websiteService.OperateRedirect(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get redirect conf\n// @Accept json\n// @Param request body request.WebsiteProxyReq true \"request\"\n// @Success 200 {array} response.NginxRedirectConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/redirect [post]\nfunc (b *BaseApi) GetRedirectConfig(c *gin.Context) {\n\tvar req request.WebsiteRedirectReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteService.GetRedirect(req.WebsiteID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Update redirect file\n// @Accept json\n// @Param request body request.NginxRedirectUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/redirect/file [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteID\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteID\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"更新重定向文件 [domain]\",\"formatEN\":\"Nginx conf redirect file update [domain]\"}\nfunc (b *BaseApi) UpdateRedirectConfigFile(c *gin.Context) {\n\tvar req request.NginxRedirectUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateRedirectFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get website dir\n// @Accept json\n// @Param request body request.WebsiteCommonReq true \"request\"\n// @Success 200 {object} response.WebsiteDirConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/dir [post]\nfunc (b *BaseApi) GetDirConfig(c *gin.Context) {\n\tvar req request.WebsiteCommonReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteService.LoadWebsiteDirConfig(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Get default html\n// @Accept json\n// @Success 200 {object} response.WebsiteHtmlRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/default/html/:type [get]\nfunc (b *BaseApi) GetDefaultHtml(c *gin.Context) {\n\tresourceType, err := helper.GetStrParamByKey(c, \"type\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tfileInfo, err := websiteService.GetDefaultHtml(resourceType)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, fileInfo)\n}\n\n// @Tags Website\n// @Summary Update default html\n// @Accept json\n// @Param request body request.WebsiteHtmlUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/default/html/update [post]\n// @x-panel-log {\"bodyKeys\":[\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新默认 html\",\"formatEN\":\"Update default html\"}\nfunc (b *BaseApi) UpdateDefaultHtml(c *gin.Context) {\n\tvar req request.WebsiteHtmlUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateDefaultHtml(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get website upstreams\n// @Accept json\n// @Param request body request.WebsiteCommonReq true \"request\"\n// @Success 200 {array} dto.NginxUpstream\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/lbs [get]\nfunc (b *BaseApi) GetLoadBalances(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tres, err := websiteService.GetLoadBalances(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Create website upstream\n// @Accept json\n// @Param request body request.WebsiteLBCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/lbs/create [post]\nfunc (b *BaseApi) CreateLoadBalance(c *gin.Context) {\n\tvar req request.WebsiteLBCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.CreateLoadBalance(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Delete website upstream\n// @Accept json\n// @Param request body request.WebsiteLBDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/lbs/del [post]\nfunc (b *BaseApi) DeleteLoadBalance(c *gin.Context) {\n\tvar req request.WebsiteLBDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.DeleteLoadBalance(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update website upstream\n// @Accept json\n// @Param request body request.WebsiteLBUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/lbs/update [post]\nfunc (b *BaseApi) UpdateLoadBalance(c *gin.Context) {\n\tvar req request.WebsiteLBUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateLoadBalance(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update website upstream file\n// @Accept json\n// @Param request body request.WebsiteLBUpdateFile true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/lbs/file [post]\nfunc (b *BaseApi) UpdateLoadBalanceFile(c *gin.Context) {\n\tvar req request.WebsiteLBUpdateFile\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateLoadBalanceFile(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) ChangeWebsiteGroup(c *gin.Context) {\n\tvar req dto.UpdateGroup\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.ChangeGroup(req.Group, req.NewGroup); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary update website proxy cache config\n// @Accept json\n// @Param request body request.NginxProxyCacheUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/proxy/config [post]\nfunc (b *BaseApi) UpdateProxyCache(c *gin.Context) {\n\tvar req request.NginxProxyCacheUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateProxyCache(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Summary Get website proxy cache config\n// @Accept json\n// @Param id path int true \"id\"\n// @Success 200 {object} response.NginxProxyCache\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/proxy/config/{id} [get]\nfunc (b *BaseApi) GetProxyCache(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tres, err := websiteService.GetProxyCache(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Set Real IP\n// @Accept json\n// @Param request body request.WebsiteRealIP true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/realip/config [post]\n// @x-panel-log {\"bodyKeys\":[\"websiteID\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"websiteID\",\"isList\":false,\"db\":\"websites\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"修改 [domain] 网站真实IP配置 \",\"formatEN\":\"Modify the real IP configuration of [domain] website\"}\nfunc (b *BaseApi) SetRealIPConfig(c *gin.Context) {\n\tvar req request.WebsiteRealIP\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.SetRealIPConfig(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get Real IP Config\n// @Accept json\n// @Param id path int true \"id\"\n// @Success 200 {object} response.WebsiteRealIP\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/realip/config/{id} [get]\nfunc (b *BaseApi) GetRealIPConfig(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tres, err := websiteService.GetRealIPConfig(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Get website resource\n// @Accept json\n// @Param id path int true \"id\"\n// @Success 200 {object} response.Resource\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/resource/{id} [get]\nfunc (b *BaseApi) GetWebsiteResource(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tres, err := websiteService.GetWebsiteResource(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Get databases\n// @Accept json\n// @Success 200 {object} response.Database\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/databases [get]\nfunc (b *BaseApi) GetWebsiteDatabase(c *gin.Context) {\n\tres, err := websiteService.ListDatabases()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Change website database\n// @Accept json\n// @Param request body request.ChangeDatabase true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/databases [post]\nfunc (b *BaseApi) ChangeWebsiteDatabase(c *gin.Context) {\n\tvar req request.ChangeDatabase\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.ChangeDatabase(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Operate custom rewrite\n// @Accept json\n// @Param request body request.CustomRewriteOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/rewrite/custom [post]\nfunc (b *BaseApi) OperateCustomRewrite(c *gin.Context) {\n\tvar req request.CustomRewriteOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.OperateCustomRewrite(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary List custom rewrite\n// @Accept json\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/rewrite/custom [get]\nfunc (b *BaseApi) ListCustomRewrite(c *gin.Context) {\n\tres, err := websiteService.ListCustomRewrite()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Clear Website proxy cache\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/proxy/clear [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"清理 Openresty 代理缓存\",\"formatEN\":\"Clear nginx proxy cache\"}\nfunc (b *BaseApi) ClearProxyCache(c *gin.Context) {\n\tvar req request.NginxCommonReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.ClearProxyCache(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Operate Cross Site Access\n// @Accept json\n// @Param request body request.CrossSiteAccessOp true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/crosssite [post]\nfunc (b *BaseApi) OperateCrossSiteAccess(c *gin.Context) {\n\tvar req request.CrossSiteAccessOp\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.OperateCrossSiteAccess(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Exec Composer\n// @Accept json\n// @Param request body request.ExecComposerReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/exec/composer [post]\nfunc (b *BaseApi) ExecComposer(c *gin.Context) {\n\tvar req request.ExecComposerReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.ExecComposer(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Batch operate websites\n// @Accept json\n// @Param request body request.BatchWebsiteOp true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/batch/operate [post]\nfunc (b *BaseApi) BatchOpWebsites(c *gin.Context) {\n\tvar req request.BatchWebsiteOp\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.BatchOpWebsite(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Batch set website group\n// @Accept json\n// @Param request body request.BatchWebsiteGroup true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/batch/group [post]\nfunc (b *BaseApi) BatchSetWebsiteGroup(c *gin.Context) {\n\tvar req request.BatchWebsiteGroup\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.BatchSetGroup(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Get CORS Config\n// @Accept json\n// @Param id path int true \"id\"\n// @Success 200 {object} request.CorsConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/cors/{id} [get]\nfunc (b *BaseApi) GetCORSConfig(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tres, err := websiteService.GetCors(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website\n// @Summary Update CORS Config\n// @Accept json\n// @Param request body request.CorsConfigReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/cors/update [post]\nfunc (b *BaseApi) UpdateCORSConfig(c *gin.Context) {\n\tvar req request.CorsConfigReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateCors(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Update Stream Config\n// @Accept json\n// @Param request body request.StreamUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/stream/update [post]\nfunc (b *BaseApi) UpdateStreamConfig(c *gin.Context) {\n\tvar req request.StreamUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateStream(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website\n// @Summary Batch set HTTPS for websites\n// @Accept json\n// @Param request body request.BatchWebsiteHttps true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/batch/https [post]\nfunc (b *BaseApi) BatchSetHttps(c *gin.Context) {\n\tvar req request.BatchWebsiteHttps\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.BatchSetHttps(c, req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/website_acme_account.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Website Acme\n// @Summary Page website acme accounts\n// @Accept json\n// @Param request body dto.PageInfo true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/acme/search [post]\nfunc (b *BaseApi) PageWebsiteAcmeAccount(c *gin.Context) {\n\tvar req dto.PageInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, accounts, err := websiteAcmeAccountService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: accounts,\n\t})\n}\n\n// @Tags Website Acme\n// @Summary Create website acme account\n// @Accept json\n// @Param request body request.WebsiteAcmeAccountCreate true \"request\"\n// @Success 200 {object} response.WebsiteAcmeAccountDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/acme [post]\n// @x-panel-log {\"bodyKeys\":[\"email\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建网站 acme [email]\",\"formatEN\":\"Create website acme [email]\"}\nfunc (b *BaseApi) CreateWebsiteAcmeAccount(c *gin.Context) {\n\tvar req request.WebsiteAcmeAccountCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteAcmeAccountService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website Acme\n// @Summary Delete website acme account\n// @Accept json\n// @Param request body request.WebsiteResourceReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/acme/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_acme_accounts\",\"output_column\":\"email\",\"output_value\":\"email\"}],\"formatZH\":\"删除网站 acme [email]\",\"formatEN\":\"Delete website acme [email]\"}\nfunc (b *BaseApi) DeleteWebsiteAcmeAccount(c *gin.Context) {\n\tvar req request.WebsiteResourceReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteAcmeAccountService.Delete(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website Acme\n// @Summary Update website acme account\n// @Accept json\n// @Param request body request.WebsiteAcmeAccountUpdate true \"request\"\n// @Success 200 {object} response.WebsiteAcmeAccountDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/acme/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_acme_accounts\",\"output_column\":\"email\",\"output_value\":\"email\"}],\"formatZH\":\"更新 acme [email]\",\"formatEN\":\"Update acme [email]\"}\nfunc (b *BaseApi) UpdateWebsiteAcmeAccount(c *gin.Context) {\n\tvar req request.WebsiteAcmeAccountUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteAcmeAccountService.Update(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n"
  },
  {
    "path": "agent/app/api/v2/website_ca.go",
    "content": "package v2\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Website CA\n// @Summary Page website ca\n// @Accept json\n// @Param request body request.WebsiteCASearch true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ca/search [post]\nfunc (b *BaseApi) PageWebsiteCA(c *gin.Context) {\n\tvar req request.WebsiteCASearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, cas, err := websiteCAService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: cas,\n\t})\n}\n\n// @Tags Website CA\n// @Summary Create website ca\n// @Accept json\n// @Param request body request.WebsiteCACreate true \"request\"\n// @Success 200 {object} request.WebsiteCACreate\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ca [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建网站 ca [name]\",\"formatEN\":\"Create website ca [name]\"}\nfunc (b *BaseApi) CreateWebsiteCA(c *gin.Context) {\n\tvar req request.WebsiteCACreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteCAService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website CA\n// @Summary Get website ca\n// @Accept json\n// @Param id path int true \"id\"\n// @Success 200 {object} response.WebsiteCADTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ca/{id} [get]\nfunc (b *BaseApi) GetWebsiteCA(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\treturn\n\t}\n\tres, err := websiteCAService.GetCA(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website CA\n// @Summary Delete website ca\n// @Accept json\n// @Param request body request.WebsiteCommonReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ca/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_cas\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"删除网站 ca [name]\",\"formatEN\":\"Delete website ca [name]\"}\nfunc (b *BaseApi) DeleteWebsiteCA(c *gin.Context) {\n\tvar req request.WebsiteCommonReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteCAService.Delete(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website CA\n// @Summary Obtain SSL\n// @Accept json\n// @Param request body request.WebsiteCAObtain true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ca/obtain [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_cas\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"自签 SSL 证书 [name]\",\"formatEN\":\"Obtain SSL [name]\"}\nfunc (b *BaseApi) ObtainWebsiteCA(c *gin.Context) {\n\tvar req request.WebsiteCAObtain\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteCAService.ObtainSSL(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website CA\n// @Summary Obtain SSL\n// @Accept json\n// @Param request body request.WebsiteCAObtain true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ca/renew [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_cas\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"自签 SSL 证书 [name]\",\"formatEN\":\"Obtain SSL [name]\"}\nfunc (b *BaseApi) RenewWebsiteCA(c *gin.Context) {\n\tvar req request.WebsiteCARenew\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif _, err := websiteCAService.ObtainSSL(request.WebsiteCAObtain{\n\t\tSSLID: req.SSLID,\n\t\tRenew: true,\n\t\tUnit:  \"year\",\n\t\tTime:  1,\n\t}); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website CA\n// @Summary Download CA file\n// @Accept json\n// @Param request body request.WebsiteResourceReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router  /websites/ca/download [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_cas\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"下载 CA 证书文件 [name]\",\"formatEN\":\"download ca file [name]\"}\nfunc (b *BaseApi) DownloadCAFile(c *gin.Context) {\n\tvar req request.WebsiteResourceReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfile, err := websiteCAService.DownloadFile(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tdefer file.Close()\n\tinfo, err := file.Stat()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tc.Header(\"Content-Length\", strconv.FormatInt(info.Size(), 10))\n\tc.Header(\"Content-Disposition\", \"attachment; filename*=utf-8''\"+url.PathEscape(info.Name()))\n\thttp.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)\n}\n"
  },
  {
    "path": "agent/app/api/v2/website_dns_account.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Website DNS\n// @Summary Page website dns accounts\n// @Accept json\n// @Param request body dto.PageInfo true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/dns/search [post]\nfunc (b *BaseApi) PageWebsiteDnsAccount(c *gin.Context) {\n\tvar req dto.PageInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, accounts, err := websiteDnsAccountService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: accounts,\n\t})\n}\n\n// @Tags Website DNS\n// @Summary Create website dns account\n// @Accept json\n// @Param request body request.WebsiteDnsAccountCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/dns [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建网站 dns [name]\",\"formatEN\":\"Create website dns [name]\"}\nfunc (b *BaseApi) CreateWebsiteDnsAccount(c *gin.Context) {\n\tvar req request.WebsiteDnsAccountCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif _, err := websiteDnsAccountService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website DNS\n// @Summary Update website dns account\n// @Accept json\n// @Param request body request.WebsiteDnsAccountUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/dns/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新网站 dns [name]\",\"formatEN\":\"Update website dns [name]\"}\nfunc (b *BaseApi) UpdateWebsiteDnsAccount(c *gin.Context) {\n\tvar req request.WebsiteDnsAccountUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif _, err := websiteDnsAccountService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website DNS\n// @Summary Delete website dns account\n// @Accept json\n// @Param request body request.WebsiteResourceReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/dns/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_dns_accounts\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"删除网站 dns [name]\",\"formatEN\":\"Delete website dns [name]\"}\nfunc (b *BaseApi) DeleteWebsiteDnsAccount(c *gin.Context) {\n\tvar req request.WebsiteResourceReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteDnsAccountService.Delete(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/website_domain.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Website Domain\n// @Summary Delete website domain\n// @Accept json\n// @Param request body request.WebsiteDomainDelete true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/domains/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_domains\",\"output_column\":\"domain\",\"output_value\":\"domain\"}],\"formatZH\":\"删除域名 [domain]\",\"formatEN\":\"Delete domain [domain]\"}\nfunc (b *BaseApi) DeleteWebDomain(c *gin.Context) {\n\tvar req request.WebsiteDomainDelete\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.DeleteWebsiteDomain(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website Domain\n// @Summary Create website domain\n// @Accept json\n// @Param request body request.WebsiteDomainCreate true \"request\"\n// @Success 200 {object} model.WebsiteDomain\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/domains [post]\n// @x-panel-log {\"bodyKeys\":[\"domain\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建域名 [domain]\",\"formatEN\":\"Create domain [domain]\"}\nfunc (b *BaseApi) CreateWebDomain(c *gin.Context) {\n\tvar req request.WebsiteDomainCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tdomain, err := websiteService.CreateWebsiteDomain(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, domain)\n}\n\n// @Tags Website Domain\n// @Summary Search website domains by websiteId\n// @Accept json\n// @Param websiteId path integer true \"request\"\n// @Success 200 {array} model.WebsiteDomain\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/domains/:websiteId [get]\nfunc (b *BaseApi) GetWebDomains(c *gin.Context) {\n\twebsiteId, err := helper.GetIntParamByKey(c, \"websiteId\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tlist, err := websiteService.GetWebsiteDomain(websiteId)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Website Domain\n// @Summary Update website domain\n// @Accept json\n// @Param request body request.WebsiteDomainUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/domains/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_domains\",\"output_column\":\"domain\",\"output_value\":\"domain\"}],\"formatZH\":\"更新域名 [domain]\",\"formatEN\":\"Update domain [domain]\"}\nfunc (b *BaseApi) UpdateWebDomain(c *gin.Context) {\n\tvar req request.WebsiteDomainUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteService.UpdateWebsiteDomain(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/api/v2/website_ssl.go",
    "content": "package v2\n\nimport (\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Website SSL\n// @Summary Page website ssl\n// @Accept json\n// @Param request body request.WebsiteSSLSearch true \"request\"\n// @Success 200 {array} response.WebsiteSSLDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/search [post]\nfunc (b *BaseApi) PageWebsiteSSL(c *gin.Context) {\n\tvar req request.WebsiteSSLSearch\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\ttotal, accounts, err := websiteSSLService.Page(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithDataGzipped(c, dto.PageResult{\n\t\tTotal: total,\n\t\tItems: accounts,\n\t})\n}\n\n// @Tags Website SSL\n// @Summary List website ssl\n// @Accept json\n// @Param request body request.WebsiteSSLListReq true \"request\"\n// @Success 200 {array} response.WebsiteSSLDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/list [post]\nfunc (b *BaseApi) ListWebsiteSSL(c *gin.Context) {\n\tvar req request.WebsiteSSLListReq\n\tif err := helper.CheckBind(&req, c); err != nil {\n\t\treturn\n\t}\n\tlist, err := websiteSSLService.Search(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithDataGzipped(c, list)\n}\n\n// @Tags Website SSL\n// @Summary Create website ssl\n// @Accept json\n// @Param request body request.WebsiteSSLCreate true \"request\"\n// @Success 200 {object} request.WebsiteSSLCreate\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl [post]\n// @x-panel-log {\"bodyKeys\":[\"primaryDomain\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建网站 ssl [primaryDomain]\",\"formatEN\":\"Create website ssl [primaryDomain]\"}\nfunc (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {\n\tvar req request.WebsiteSSLCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteSSLService.Create(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website SSL\n// @Summary Apply  ssl\n// @Accept json\n// @Param request body request.WebsiteSSLApply true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/obtain [post]\n// @x-panel-log {\"bodyKeys\":[\"ID\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ID\",\"isList\":false,\"db\":\"website_ssls\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"申请证书  [domain]\",\"formatEN\":\"apply ssl [domain]\"}\nfunc (b *BaseApi) ApplyWebsiteSSL(c *gin.Context) {\n\tvar req request.WebsiteSSLApply\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteSSLService.ObtainSSL(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website SSL\n// @Summary Resolve website ssl\n// @Accept json\n// @Param request body request.WebsiteDNSReq true \"request\"\n// @Success 200 {array} response.WebsiteDNSRes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/resolve [post]\nfunc (b *BaseApi) GetDNSResolve(c *gin.Context) {\n\tvar req request.WebsiteDNSReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, err := websiteSSLService.GetDNSResolve(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Website SSL\n// @Summary Delete website ssl\n// @Accept json\n// @Param request body request.WebsiteBatchDelReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"website_ssls\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"删除 ssl [domain]\",\"formatEN\":\"Delete ssl [domain]\"}\nfunc (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {\n\tvar req request.WebsiteBatchDelReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteSSLService.Delete(req.IDs); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website SSL\n// @Summary Search website ssl by website id\n// @Accept json\n// @Param websiteId path integer true \"request\"\n// @Success 200 {object} response.WebsiteSSLDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/website/:websiteId [get]\nfunc (b *BaseApi) GetWebsiteSSLByWebsiteId(c *gin.Context) {\n\twebsiteId, err := helper.GetIntParamByKey(c, \"websiteId\")\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\twebsiteSSL, err := websiteSSLService.GetWebsiteSSL(websiteId)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, websiteSSL)\n}\n\n// @Tags Website SSL\n// @Summary Search website ssl by id\n// @Accept json\n// @Param id path integer true \"request\"\n// @Success 200 {object} response.WebsiteSSLDTO\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/:id [get]\nfunc (b *BaseApi) GetWebsiteSSLById(c *gin.Context) {\n\tid, err := helper.GetParamID(c)\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\twebsiteSSL, err := websiteSSLService.GetSSL(id)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, websiteSSL)\n}\n\n// @Tags Website SSL\n// @Summary Update ssl\n// @Accept json\n// @Param request body request.WebsiteSSLUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_ssls\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"更新证书设置 [domain]\",\"formatEN\":\"Update ssl config [domain]\"}\nfunc (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {\n\tvar req request.WebsiteSSLUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteSSLService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website SSL\n// @Summary Upload ssl\n// @Accept json\n// @Param request body request.WebsiteSSLUpload true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/upload [post]\n// @x-panel-log {\"bodyKeys\":[\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"上传 ssl [type]\",\"formatEN\":\"Upload ssl [type]\"}\nfunc (b *BaseApi) UploadWebsiteSSL(c *gin.Context) {\n\tvar req request.WebsiteSSLUpload\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteSSLService.Upload(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Website SSL\n// @Summary Upload SSL file\n// @Accept multipart/form-data\n// @Param type formData string true \"type\"\n// @Param description formData string false \"description\"\n// @Param sslID formData string false \"sslID\"\n// @Param privateKeyFile formData file true \"privateKeyFile\"\n// @Param certificateFile formData file true \"certificateFile\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /websites/ssl/upload/file [post]\n// @x-panel-log {\"bodyKeys\":[\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"上传 ssl 文件 [type]\",\"formatEN\":\"Upload ssl file [type]\"}\nfunc (b *BaseApi) UploadSSLFile(c *gin.Context) {\n\tvar req request.WebsiteSSLFileUpload\n\n\treq.Description = c.PostForm(\"description\")\n\tsslID := c.PostForm(\"sslID\")\n\tif sslID != \"\" {\n\t\treq.SSLID, _ = strconv.ParseUint(sslID, 10, 64)\n\t}\n\n\tprivateKeyFile, err := c.FormFile(\"privateKeyFile\")\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\tcertificateFile, err := c.FormFile(\"certificateFile\")\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\tprivateKeyContent, err := readUploadedFile(privateKeyFile)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\tcertificateContent, err := readUploadedFile(certificateFile)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\tuploadReq := request.WebsiteSSLUpload{\n\t\tType:        \"paste\",\n\t\tPrivateKey:  string(privateKeyContent),\n\t\tCertificate: string(certificateContent),\n\t\tDescription: req.Description,\n\t\tSSLID:       uint(req.SSLID),\n\t}\n\n\tif err := websiteSSLService.Upload(uploadReq); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\nfunc readUploadedFile(fileHeader *multipart.FileHeader) ([]byte, error) {\n\tfile, err := fileHeader.Open()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\n\treturn io.ReadAll(file)\n}\n\n// @Tags Website SSL\n// @Summary Download SSL  file\n// @Accept json\n// @Param request body request.WebsiteResourceReq true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router  /websites/ssl/download [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"website_ssls\",\"output_column\":\"primary_domain\",\"output_value\":\"domain\"}],\"formatZH\":\"下载证书文件 [domain]\",\"formatEN\":\"download ssl file [domain]\"}\nfunc (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {\n\tvar req request.WebsiteResourceReq\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tfile, err := websiteSSLService.DownloadFile(req.ID)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tdefer file.Close()\n\tinfo, err := file.Stat()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tc.Header(\"Content-Length\", strconv.FormatInt(info.Size(), 10))\n\tc.Header(\"Content-Disposition\", \"attachment; filename*=utf-8''\"+url.PathEscape(info.Name()))\n\thttp.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)\n}\n\nfunc (b *BaseApi) ImportMasterSSL(c *gin.Context) {\n\tvar req model.WebsiteSSL\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := websiteSSLService.ImportMasterSSL(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "agent/app/dto/agents.go",
    "content": "package dto\n\nimport \"time\"\n\ntype AgentCreateReq struct {\n\tName           string   `json:\"name\" validate:\"required\"`\n\tAppVersion     string   `json:\"appVersion\" validate:\"required\"`\n\tWebUIPort      int      `json:\"webUIPort\" validate:\"required\"`\n\tBridgePort     int      `json:\"bridgePort\"`\n\tAllowedOrigins []string `json:\"allowedOrigins\"`\n\tAgentType      string   `json:\"agentType\" validate:\"required,oneof=openclaw copaw\"`\n\tModel          string   `json:\"model\"`\n\tAccountID      uint     `json:\"accountId\"`\n\tToken          string   `json:\"token\"`\n\tTaskID         string   `json:\"taskID\"`\n\tAdvanced       bool     `json:\"advanced\"`\n\tContainerName  string   `json:\"containerName\"`\n\tAllowPort      bool     `json:\"allowPort\"`\n\tSpecifyIP      string   `json:\"specifyIP\"`\n\tRestartPolicy  string   `json:\"restartPolicy\"`\n\tCpuQuota       float64  `json:\"cpuQuota\"`\n\tMemoryLimit    float64  `json:\"memoryLimit\"`\n\tMemoryUnit     string   `json:\"memoryUnit\"`\n\tPullImage      bool     `json:\"pullImage\"`\n\tEditCompose    bool     `json:\"editCompose\"`\n\tDockerCompose  string   `json:\"dockerCompose\"`\n}\n\ntype AgentItem struct {\n\tID            uint      `json:\"id\"`\n\tName          string    `json:\"name\"`\n\tAgentType     string    `json:\"agentType\"`\n\tProvider      string    `json:\"provider\"`\n\tProviderName  string    `json:\"providerName\"`\n\tModel         string    `json:\"model\"`\n\tAPIType       string    `json:\"apiType\"`\n\tMaxTokens     int       `json:\"maxTokens\"`\n\tContextWindow int       `json:\"contextWindow\"`\n\tBaseURL       string    `json:\"baseUrl\"`\n\tAPIKey        string    `json:\"apiKey\"`\n\tToken         string    `json:\"token\"`\n\tStatus        string    `json:\"status\"`\n\tMessage       string    `json:\"message\"`\n\tAppInstallID  uint      `json:\"appInstallId\"`\n\tAccountID     uint      `json:\"accountId\"`\n\tAppVersion    string    `json:\"appVersion\"`\n\tContainer     string    `json:\"containerName\"`\n\tWebUIPort     int       `json:\"webUIPort\"`\n\tBridgePort    int       `json:\"bridgePort\"`\n\tPath          string    `json:\"path\"`\n\tConfigPath    string    `json:\"configPath\"`\n\tUpgradable    bool      `json:\"upgradable\"`\n\tCreatedAt     time.Time `json:\"createdAt\"`\n}\n\ntype AgentDeleteReq struct {\n\tID          uint   `json:\"id\" validate:\"required\"`\n\tTaskID      string `json:\"taskID\"`\n\tForceDelete bool   `json:\"forceDelete\"`\n}\n\ntype AgentTokenResetReq struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype AgentModelConfigUpdateReq struct {\n\tAgentID   uint   `json:\"agentId\" validate:\"required\"`\n\tAccountID uint   `json:\"accountId\" validate:\"required\"`\n\tModel     string `json:\"model\" validate:\"required\"`\n}\n\ntype AgentAccountModel struct {\n\tRecordID      uint     `json:\"recordId\"`\n\tID            string   `json:\"id\"`\n\tName          string   `json:\"name\"`\n\tContextWindow int      `json:\"contextWindow\"`\n\tMaxTokens     int      `json:\"maxTokens\"`\n\tReasoning     bool     `json:\"reasoning\"`\n\tInput         []string `json:\"input\"`\n}\n\ntype AgentAccountModelReq struct {\n\tAccountID uint `json:\"accountId\" validate:\"required\"`\n}\n\ntype AgentAccountModelCreateReq struct {\n\tAccountID uint              `json:\"accountId\" validate:\"required\"`\n\tModel     AgentAccountModel `json:\"model\" validate:\"required\"`\n}\n\ntype AgentAccountModelUpdateReq struct {\n\tAccountID uint              `json:\"accountId\" validate:\"required\"`\n\tModel     AgentAccountModel `json:\"model\" validate:\"required\"`\n}\n\ntype AgentAccountModelDeleteReq struct {\n\tAccountID uint `json:\"accountId\" validate:\"required\"`\n\tRecordID  uint `json:\"recordId\" validate:\"required\"`\n}\n\ntype AgentAccountCreateReq struct {\n\tProvider       string              `json:\"provider\" validate:\"required\"`\n\tName           string              `json:\"name\" validate:\"required\"`\n\tAPIKey         string              `json:\"apiKey\" validate:\"required\"`\n\tRememberAPIKey bool                `json:\"rememberApiKey\"`\n\tBaseURL        string              `json:\"baseURL\"`\n\tModels         []AgentAccountModel `json:\"models\"`\n\tAPIType        string              `json:\"apiType\"`\n\tRemark         string              `json:\"remark\"`\n}\n\ntype AgentAccountUpdateReq struct {\n\tID             uint   `json:\"id\" validate:\"required\"`\n\tName           string `json:\"name\" validate:\"required\"`\n\tAPIKey         string `json:\"apiKey\" validate:\"required\"`\n\tRememberAPIKey bool   `json:\"rememberApiKey\"`\n\tBaseURL        string `json:\"baseURL\"`\n\tAPIType        string `json:\"apiType\"`\n\tRemark         string `json:\"remark\"`\n\tSyncAgents     bool   `json:\"syncAgents\"`\n}\n\ntype AgentAccountVerifyReq struct {\n\tProvider string `json:\"provider\" validate:\"required\"`\n\tAPIKey   string `json:\"apiKey\" validate:\"required\"`\n\tBaseURL  string `json:\"baseURL\"`\n}\n\ntype AgentAccountDeleteReq struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype AgentAccountSearch struct {\n\tPageInfo\n\tProvider string `json:\"provider\"`\n\tName     string `json:\"name\"`\n}\n\ntype AgentAccountInfo struct {\n\tID             uint                `json:\"id\"`\n\tProvider       string              `json:\"provider\"`\n\tProviderName   string              `json:\"providerName\"`\n\tName           string              `json:\"name\"`\n\tAPIKey         string              `json:\"apiKey\"`\n\tRememberAPIKey bool                `json:\"rememberApiKey\"`\n\tBaseURL        string              `json:\"baseUrl\"`\n\tModels         []AgentAccountModel `json:\"models\"`\n\tAPIType        string              `json:\"apiType\"`\n\tVerified       bool                `json:\"verified\"`\n\tRemark         string              `json:\"remark\"`\n\tCreatedAt      time.Time           `json:\"createdAt\"`\n}\n\ntype ProviderModelInfo struct {\n\tID            string   `json:\"id\"`\n\tName          string   `json:\"name\"`\n\tContextWindow int      `json:\"contextWindow\"`\n\tMaxTokens     int      `json:\"maxTokens\"`\n\tReasoning     bool     `json:\"reasoning\"`\n\tInput         []string `json:\"input\"`\n}\n\ntype ProviderInfo struct {\n\tSort        uint                `json:\"-\"`\n\tProvider    string              `json:\"provider\"`\n\tDisplayName string              `json:\"displayName\"`\n\tBaseURL     string              `json:\"baseUrl\"`\n\tModels      []ProviderModelInfo `json:\"models\"`\n}\n\ntype AgentFeishuConfigReq struct {\n\tAgentID uint `json:\"agentId\" validate:\"required\"`\n}\n\ntype AgentFeishuConfigUpdateReq struct {\n\tAgentID   uint   `json:\"agentId\" validate:\"required\"`\n\tBotName   string `json:\"botName\" validate:\"required\"`\n\tAppID     string `json:\"appId\" validate:\"required\"`\n\tAppSecret string `json:\"appSecret\" validate:\"required\"`\n\tEnabled   bool   `json:\"enabled\"`\n\tDmPolicy  string `json:\"dmPolicy\" validate:\"required\"`\n}\n\ntype AgentFeishuPairingApproveReq struct {\n\tAgentID     uint   `json:\"agentId\" validate:\"required\"`\n\tPairingCode string `json:\"pairingCode\" validate:\"required\"`\n}\n\ntype AgentFeishuConfig struct {\n\tEnabled   bool   `json:\"enabled\"`\n\tDmPolicy  string `json:\"dmPolicy\"`\n\tBotName   string `json:\"botName\"`\n\tAppID     string `json:\"appId\"`\n\tAppSecret string `json:\"appSecret\"`\n}\n\ntype AgentTelegramConfigReq struct {\n\tAgentID uint `json:\"agentId\" validate:\"required\"`\n}\n\ntype AgentTelegramConfigUpdateReq struct {\n\tAgentID  uint   `json:\"agentId\" validate:\"required\"`\n\tEnabled  bool   `json:\"enabled\"`\n\tDmPolicy string `json:\"dmPolicy\" validate:\"required\"`\n\tBotToken string `json:\"botToken\" validate:\"required\"`\n\tProxy    string `json:\"proxy\"`\n}\n\ntype AgentTelegramConfig struct {\n\tEnabled  bool   `json:\"enabled\"`\n\tDmPolicy string `json:\"dmPolicy\"`\n\tBotToken string `json:\"botToken\"`\n\tProxy    string `json:\"proxy\"`\n}\n\ntype AgentChannelPairingApproveReq struct {\n\tAgentID     uint   `json:\"agentId\" validate:\"required\"`\n\tType        string `json:\"type\" validate:\"required,oneof=feishu telegram discord wecom dingtalk-connector\"`\n\tPairingCode string `json:\"pairingCode\" validate:\"required\"`\n}\n\ntype AgentWecomConfigReq struct {\n\tAgentID uint `json:\"agentId\" validate:\"required\"`\n}\n\ntype AgentWecomConfigUpdateReq struct {\n\tAgentID  uint   `json:\"agentId\" validate:\"required\"`\n\tEnabled  bool   `json:\"enabled\"`\n\tDmPolicy string `json:\"dmPolicy\" validate:\"required,oneof=pairing open\"`\n\tBotID    string `json:\"botId\" validate:\"required\"`\n\tSecret   string `json:\"secret\" validate:\"required\"`\n}\n\ntype AgentWecomConfig struct {\n\tEnabled   bool   `json:\"enabled\"`\n\tDmPolicy  string `json:\"dmPolicy\"`\n\tBotID     string `json:\"botId\"`\n\tSecret    string `json:\"secret\"`\n\tInstalled bool   `json:\"installed\"`\n}\n\ntype AgentDingTalkConfigReq struct {\n\tAgentID uint `json:\"agentId\" validate:\"required\"`\n}\n\ntype AgentDingTalkConfigUpdateReq struct {\n\tAgentID        uint     `json:\"agentId\" validate:\"required\"`\n\tEnabled        bool     `json:\"enabled\"`\n\tClientID       string   `json:\"clientId\" validate:\"required\"`\n\tClientSecret   string   `json:\"clientSecret\" validate:\"required\"`\n\tDmPolicy       string   `json:\"dmPolicy\" validate:\"required,oneof=pairing allowlist open disabled\"`\n\tAllowFrom      []string `json:\"allowFrom\"`\n\tGroupPolicy    string   `json:\"groupPolicy\" validate:\"required,oneof=open allowlist disabled\"`\n\tGroupAllowFrom []string `json:\"groupAllowFrom\"`\n}\n\ntype AgentDingTalkConfig struct {\n\tEnabled        bool     `json:\"enabled\"`\n\tClientID       string   `json:\"clientId\"`\n\tClientSecret   string   `json:\"clientSecret\"`\n\tDmPolicy       string   `json:\"dmPolicy\"`\n\tAllowFrom      []string `json:\"allowFrom\"`\n\tGroupPolicy    string   `json:\"groupPolicy\"`\n\tGroupAllowFrom []string `json:\"groupAllowFrom\"`\n\tInstalled      bool     `json:\"installed\"`\n}\n\ntype AgentQQBotConfigReq struct {\n\tAgentID uint `json:\"agentId\" validate:\"required\"`\n}\n\ntype AgentQQBotConfigUpdateReq struct {\n\tAgentID      uint   `json:\"agentId\" validate:\"required\"`\n\tEnabled      bool   `json:\"enabled\"`\n\tAppID        string `json:\"appId\" validate:\"required\"`\n\tClientSecret string `json:\"clientSecret\" validate:\"required\"`\n}\n\ntype AgentQQBotConfig struct {\n\tEnabled      bool   `json:\"enabled\"`\n\tAppID        string `json:\"appId\"`\n\tClientSecret string `json:\"clientSecret\"`\n\tInstalled    bool   `json:\"installed\"`\n}\n\ntype AgentPluginInstallReq struct {\n\tAgentID uint   `json:\"agentId\" validate:\"required\"`\n\tType    string `json:\"type\" validate:\"required,oneof=qqbot wecom dingtalk\"`\n\tTaskID  string `json:\"taskID\" validate:\"required\"`\n}\n\ntype AgentPluginCheckReq struct {\n\tAgentID uint   `json:\"agentId\" validate:\"required\"`\n\tType    string `json:\"type\" validate:\"required,oneof=qqbot wecom dingtalk\"`\n}\n\ntype AgentPluginStatus struct {\n\tInstalled bool `json:\"installed\"`\n}\n\ntype AgentDiscordConfigReq struct {\n\tAgentID uint `json:\"agentId\" validate:\"required\"`\n}\n\ntype AgentDiscordConfigUpdateReq struct {\n\tAgentID     uint   `json:\"agentId\" validate:\"required\"`\n\tEnabled     bool   `json:\"enabled\"`\n\tDmPolicy    string `json:\"dmPolicy\" validate:\"required\"`\n\tGroupPolicy string `json:\"groupPolicy\" validate:\"required,oneof=open allowlist disabled\"`\n\tToken       string `json:\"token\" validate:\"required\"`\n\tProxy       string `json:\"proxy\"`\n}\n\ntype AgentDiscordConfig struct {\n\tEnabled     bool   `json:\"enabled\"`\n\tDmPolicy    string `json:\"dmPolicy\"`\n\tGroupPolicy string `json:\"groupPolicy\"`\n\tToken       string `json:\"token\"`\n\tProxy       string `json:\"proxy\"`\n}\n\ntype AgentSecurityConfigReq struct {\n\tAgentID uint `json:\"agentId\" validate:\"required\"`\n}\n\ntype AgentSecurityConfigUpdateReq struct {\n\tAgentID        uint     `json:\"agentId\" validate:\"required\"`\n\tAllowedOrigins []string `json:\"allowedOrigins\"`\n}\n\ntype AgentSecurityConfig struct {\n\tAllowedOrigins []string `json:\"allowedOrigins\"`\n}\n\ntype AgentOtherConfigReq struct {\n\tAgentID uint `json:\"agentId\" validate:\"required\"`\n}\n\ntype AgentOtherConfigUpdateReq struct {\n\tAgentID        uint   `json:\"agentId\" validate:\"required\"`\n\tUserTimezone   string `json:\"userTimezone\" validate:\"required\"`\n\tBrowserEnabled bool   `json:\"browserEnabled\"`\n}\n\ntype AgentOtherConfig struct {\n\tUserTimezone   string `json:\"userTimezone\"`\n\tBrowserEnabled bool   `json:\"browserEnabled\"`\n}\n"
  },
  {
    "path": "agent/app/dto/ai.go",
    "content": "package dto\n\nimport \"time\"\n\ntype OllamaModelInfo struct {\n\tID           uint   `json:\"id\"`\n\tName         string `json:\"name\"`\n\tSize         string `json:\"size\"`\n\tFrom         string `json:\"from\"`\n\tLogFileExist bool   `json:\"logFileExist\"`\n\n\tStatus    string    `json:\"status\"`\n\tMessage   string    `json:\"message\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n}\n\ntype OllamaModelDropList struct {\n\tID   uint   `json:\"id\"`\n\tName string `json:\"name\"`\n}\n\ntype OllamaModelName struct {\n\tName   string `json:\"name\"`\n\tTaskID string `json:\"taskID\"`\n}\n\ntype OllamaBindDomain struct {\n\tDomain       string `json:\"domain\" validate:\"required\"`\n\tAppInstallID uint   `json:\"appInstallID\" validate:\"required\"`\n\tSSLID        uint   `json:\"sslID\"`\n\tWebsiteID    uint   `json:\"websiteID\"`\n\tIPList       string `json:\"ipList\"`\n}\n\ntype OllamaBindDomainReq struct {\n\tAppInstallID uint `json:\"appInstallID\" validate:\"required\"`\n}\n\ntype OllamaBindDomainRes struct {\n\tDomain        string   `json:\"domain\"`\n\tSSLID         uint     `json:\"sslID\"`\n\tAllowIPs      []string `json:\"allowIPs\"`\n\tWebsiteID     uint     `json:\"websiteID\"`\n\tConnUrl       string   `json:\"connUrl\"`\n\tAcmeAccountID uint     `json:\"acmeAccountID\"`\n}\n"
  },
  {
    "path": "agent/app/dto/alert.go",
    "content": "package dto\n\nimport (\n\t\"encoding/json\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"time\"\n)\n\ntype CreateOrUpdateAlert struct {\n\tAlertTitle  string `json:\"alertTitle\"`\n\tAlertType   string `json:\"alertType\"`\n\tAlertMethod string `json:\"alertMethod\"`\n\tAlertCount  uint   `json:\"alertCount\"`\n\tEntryID     uint   `json:\"entryID\"`\n}\n\ntype AlertBase struct {\n\tAlertType string `json:\"alertType\"`\n\tEntryID   uint   `json:\"entryID\"`\n}\n\ntype PushAlert struct {\n\tTaskName  string `json:\"taskName\"`\n\tAlertType string `json:\"alertType\"`\n\tEntryID   uint   `json:\"entryID\"`\n\tParam     string `json:\"param\"`\n}\n\ntype AlertSearch struct {\n\tPageInfo\n\tOrderBy string `json:\"orderBy\" validate:\"required,oneof=created_at\"`\n\tOrder   string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n\tType    string `json:\"type\"`\n\tStatus  string `json:\"status\"`\n\tMethod  string `json:\"method\"`\n}\n\ntype AlertDTO struct {\n\tID             uint      `json:\"id\"`\n\tType           string    `json:\"type\"`\n\tCycle          uint      `json:\"cycle\"`\n\tCount          uint      `json:\"count\"`\n\tMethod         string    `json:\"method\"`\n\tTitle          string    `json:\"title\"`\n\tProject        string    `json:\"project\"`\n\tStatus         string    `json:\"status\"`\n\tSendCount      uint      `json:\"sendCount\"`\n\tAdvancedParams string    `json:\"advancedParams\"`\n\tCreatedAt      time.Time `json:\"createdAt\"`\n\tUpdatedAt      time.Time `json:\"updatedAt\"`\n}\n\ntype AlertCreate struct {\n\tType           string `json:\"type\" validate:\"required\"`\n\tCycle          uint   `json:\"cycle\"`\n\tCount          uint   `json:\"count\"`\n\tMethod         string `json:\"method\" validate:\"required\"`\n\tTitle          string `json:\"title\"`\n\tProject        string `json:\"project\"`\n\tStatus         string `json:\"status\"`\n\tSendCount      uint   `json:\"sendCount\"`\n\tAdvancedParams string `json:\"advancedParams\"`\n}\n\ntype AlertUpdate struct {\n\tID             uint   `json:\"id\" validate:\"required\"`\n\tType           string `json:\"type\"`\n\tCycle          uint   `json:\"cycle\"`\n\tCount          uint   `json:\"count\"`\n\tMethod         string `json:\"method\"`\n\tTitle          string `json:\"title\"`\n\tProject        string `json:\"project\"`\n\tStatus         string `json:\"status\"`\n\tSendCount      uint   `json:\"sendCount\"`\n\tAdvancedParams string `json:\"advancedParams\"`\n}\n\ntype DeleteRequest struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype AlertUpdateStatus struct {\n\tID     uint   `json:\"id\" validate:\"required\"`\n\tStatus string `json:\"status\" validate:\"required\"`\n}\n\ntype DiskDTO struct {\n\tPath        string  `json:\"path\"`\n\tType        string  `json:\"type\"`\n\tDevice      string  `json:\"device\"`\n\tTotal       uint64  `json:\"total\"`\n\tFree        uint64  `json:\"free\"`\n\tUsed        uint64  `json:\"used\"`\n\tUsedPercent float64 `json:\"usedPercent\"`\n\n\tInodesTotal       uint64  `json:\"inodesTotal\"`\n\tInodesUsed        uint64  `json:\"inodesUsed\"`\n\tInodesFree        uint64  `json:\"inodesFree\"`\n\tInodesUsedPercent float64 `json:\"inodesUsedPercent\"`\n}\n\ntype AlertLogSearch struct {\n\tPageInfo\n\tCount  uint   `json:\"count\"`\n\tStatus string `json:\"status\"`\n}\n\ntype AlertLogDTO struct {\n\tID          uint        `json:\"id\"`\n\tType        string      `json:\"type\"`\n\tCount       uint        `json:\"count\"`\n\tAlertId     uint        `json:\"alertId\"`\n\tAlertDetail AlertDetail `json:\"alertDetail\"`\n\tAlertRule   AlertRule   `json:\"alertRule\"`\n\tStatus      string      `json:\"status\"`\n\tMethod      string      `json:\"method\"`\n\tMessage     string      `json:\"message\"`\n\tCreatedAt   time.Time   `json:\"createdAt\"`\n\tUpdatedAt   time.Time   `json:\"updatedAt\"`\n}\n\ntype AlertLogCreate struct {\n\tType        string `json:\"type\" validate:\"required\"`\n\tCount       uint   `json:\"count\" validate:\"required\"`\n\tAlertId     uint   `json:\"alertId\" validate:\"required\"`\n\tAlertDetail string `json:\"alertDetail\" validate:\"required\"`\n\tAlertRule   string `json:\"alertRule\" validate:\"required\"`\n\tStatus      string `json:\"status\" validate:\"required\"`\n\tMethod      string `json:\"method\" validate:\"required\"`\n\tMessage     string `json:\"message\"`\n\tRecordId    uint   `json:\"recordId\"`\n\tLicenseId   string `json:\"licenseId\" validate:\"required\"`\n}\n\ntype AlertLog struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype AlertDetail struct {\n\tLicenseId   string  `json:\"licenseId\"`\n\tType        string  `json:\"type\"`\n\tSubType     string  `json:\"subType\"`\n\tTitle       string  `json:\"title\"`\n\tMethod      string  `json:\"method\"`\n\tLicenseCode string  `json:\"licenseCode\"`\n\tDeviceId    string  `json:\"deviceId\"`\n\tProject     string  `json:\"project\"`\n\tParams      []Param `json:\"params\"`\n\tPhone       string  `json:\"phone\"`\n}\n\ntype AlertRule struct {\n\tID        uint      `json:\"id\"`\n\tType      string    `json:\"type\"`\n\tCycle     uint      `json:\"cycle\"`\n\tCount     uint      `json:\"count\"`\n\tMethod    string    `json:\"method\"`\n\tTitle     string    `json:\"title\"`\n\tProject   string    `json:\"project\"`\n\tStatus    string    `json:\"status\"`\n\tSendCount uint      `json:\"sendCount\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n}\n\ntype Param struct {\n\tIndex string `json:\"index\"`\n\tKey   string `json:\"key\"`\n\tValue string `json:\"value\"`\n}\n\ntype ClamDTO struct {\n\tID        uint      `json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n\tName      string    `json:\"name\"`\n\tStatus    string    `json:\"status\"`\n\tPath      string    `json:\"path\"`\n}\n\ntype CronJobDTO struct {\n\tID        uint      `json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n\tName      string    `json:\"name\"`\n\tStatus    string    `json:\"status\"`\n\tType      string    `json:\"type\"`\n}\n\ntype CronJobReq struct {\n\tName   string `json:\"name\"`\n\tStatus string `json:\"status\"`\n\tType   string `json:\"type\"`\n}\n\ntype UpgradeInfo struct {\n\tTestVersion   string `json:\"testVersion\"`\n\tNewVersion    string `json:\"newVersion\"`\n\tLatestVersion string `json:\"latestVersion\"`\n\tReleaseNote   string `json:\"releaseNote\"`\n}\n\ntype AlertDiskInfo struct {\n\tType   string\n\tMount  string\n\tDevice string\n}\n\ntype SyncResult struct {\n\tID          uint   `json:\"id\"`\n\tLicenseID   string `json:\"licenseId\"`\n\tSendStatus  string `json:\"sendStatus\"`\n\tCreateTime  string `json:\"createTime\"`\n\tUpdateTime  string `json:\"updateTime\"`\n\tRemarks     string `json:\"remarks\"`\n\tMsgCount    int    `json:\"msgCount\"`\n\tMsgCountMax int    `json:\"msgCountMax\"`\n}\n\ntype QueryRequest struct {\n\tQueryIds  []uint `json:\"queryIds\"`\n\tLicenseId string `json:\"licenseId\"`\n}\n\ntype AlertResponse struct {\n\tResult  bool            `json:\"result\"`\n\tData    json.RawMessage `json:\"data\"`\n\tMessage string          `json:\"message\"`\n}\n\ntype PushResult struct {\n\tRecordId  uint   `json:\"recordId\"`\n\tLicenseId string `json:\"licenseId\"`\n}\n\ntype UpdateOfflineAlertLog struct {\n\tID        uint   `json:\"id\"`\n\tStatus    string `json:\"status\"`\n\tMessage   string `json:\"message\"`\n\tRecordId  uint   `json:\"recordId\"`\n\tLicenseId string `json:\"licenseId\"`\n}\n\ntype SyncOfflineAlertLogDTO struct {\n\tQueryRequest string           `json:\"queryRequest\"`\n\tIds          []uint           `json:\"ids\"`\n\tAlertLogs    []model.AlertLog `json:\"alertLogs\"`\n\tLicenseId    string           `json:\"licenseId\"`\n}\n\ntype OfflineAlertResponse struct {\n\tID             uint             `json:\"id\"`\n\tType           string           `json:\"type\"`\n\tRemoteErr      error            `json:\"remoteErr\"`\n\tResponseStruct AlertResponse    `json:\"responseStruct\"`\n\tAlertLogs      []model.AlertLog `json:\"alertLogs\"`\n\tIds            []uint           `json:\"ids\"`\n\tLicenseId      string           `json:\"licenseId\"`\n}\n\ntype OfflineAlertLogDTO struct {\n\tID          uint      `json:\"id\"`\n\tType        string    `json:\"type\"`\n\tCount       uint      `json:\"count\"`\n\tAlertId     uint      `json:\"alertId\"`\n\tAlertDetail string    `json:\"alertDetail\"`\n\tAlertRule   string    `json:\"alertRule\"`\n\tStatus      string    `json:\"status\"`\n\tMethod      string    `json:\"method\"`\n\tMessage     string    `json:\"message\"`\n\tRecordId    uint      `json:\"recordId\"`\n\tLicenseId   string    `json:\"licenseId\"`\n\tCreatedAt   time.Time `json:\"createdAt\"`\n\tUpdatedAt   time.Time `json:\"updatedAt\"`\n}\n\ntype OfflineQueryRequest struct {\n\tIds       []uint `json:\"ids\"`\n\tQueryIds  []uint `json:\"queryIds\"`\n\tLicenseId string `json:\"licenseId\"`\n}\n\ntype AlertConfigUpdate struct {\n\tID     uint   `json:\"id\"`\n\tType   string `json:\"type\"`\n\tTitle  string `json:\"title\"`\n\tStatus string `json:\"status\"`\n\tConfig string `json:\"config\"`\n}\n\ntype AlertConfigTest struct {\n\tHost        string `json:\"host\"`\n\tPort        int    `json:\"port\"`\n\tSender      string `json:\"sender\"`\n\tUserName    string `json:\"userName\"`\n\tPassword    string `json:\"password\"`\n\tDisplayName string `json:\"displayName\"`\n\tEncryption  string `json:\"encryption\"` // \"ssl\" / \"tls\" / \"none\"\n\tRecipient   string `json:\"recipient\"`\n}\n\ntype AlertSendTimeRange struct {\n\tNoticeAlert struct {\n\t\tSendTimeRange string   `json:\"sendTimeRange\"`\n\t\tType          []string `json:\"type\"`\n\t} `json:\"noticeAlert\"`\n\tResourceAlert struct {\n\t\tSendTimeRange string   `json:\"sendTimeRange\"`\n\t\tType          []string `json:\"type\"`\n\t} `json:\"resourceAlert\"`\n}\n\ntype AlertCommonConfig struct {\n\tIsOffline          string             `json:\"isOffline\"`\n\tAlertSendTimeRange AlertSendTimeRange `json:\"alertSendTimeRange\"`\n}\n\ntype AlertSmsConfig struct {\n\tPhone         string `json:\"phone\"`\n\tAlertDailyNum string `json:\"alertDailyNum\"`\n}\n\ntype AlertEmailConfig struct {\n\tHost        string `json:\"host\"`\n\tPort        int    `json:\"port\"`\n\tSender      string `json:\"sender\"`\n\tUserName    string `json:\"userName\"`\n\tPassword    string `json:\"password\"`\n\tDisplayName string `json:\"displayName\"`\n\tEncryption  string `json:\"encryption\"` // \"ssl\" / \"tls\" / \"none\"\n\tRecipient   string `json:\"recipient\"`\n}\n\ntype AgentInfo struct {\n\tNodeName string `json:\"nodeName\"`\n\tNodeAddr string `json:\"nodeAddr\"`\n}\n\ntype AlertWebhookConfig struct {\n\tDisplayName string `json:\"displayName\"`\n\tUrl         string `json:\"url\"`\n}\n"
  },
  {
    "path": "agent/app/dto/app.go",
    "content": "package dto\n\nimport (\n\t\"context\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n)\n\ntype AppDatabase struct {\n\tServiceName  string `json:\"PANEL_DB_HOST\"`\n\tDbName       string `json:\"PANEL_DB_NAME\"`\n\tDbUser       string `json:\"PANEL_DB_USER\"`\n\tPassword     string `json:\"PANEL_DB_USER_PASSWORD\"`\n\tDatabaseName string `json:\"DATABASE_NAME\"`\n\tFormat       string `json:\"format\"`\n\tCollation    string `json:\"collation\"`\n}\n\ntype AuthParam struct {\n\tRootPassword string `json:\"PANEL_DB_ROOT_PASSWORD\"`\n\tRootUser     string `json:\"PANEL_DB_ROOT_USER\"`\n}\n\ntype RedisAuthParam struct {\n\tRootPassword string `json:\"PANEL_REDIS_ROOT_PASSWORD\"`\n}\n\ntype MinioAuthParam struct {\n\tRootPassword string `json:\"PANEL_MINIO_ROOT_PASSWORD\"`\n\tRootUser     string `json:\"PANEL_MINIO_ROOT_USER\"`\n}\n\ntype ContainerExec struct {\n\tContainerName string      `json:\"containerName\"`\n\tDbParam       AppDatabase `json:\"dbParam\"`\n\tAuth          AuthParam   `json:\"auth\"`\n}\n\ntype AppOssConfig struct {\n\tVersion string `json:\"version\"`\n\tPackage string `json:\"package\"`\n}\n\ntype AppVersion struct {\n\tVersion       string `json:\"version\"`\n\tDetailId      uint   `json:\"detailId\"`\n\tDockerCompose string `json:\"dockerCompose\"`\n}\n\ntype AppList struct {\n\tValid        bool     `json:\"valid\"`\n\tViolations   []string `json:\"violations\"`\n\tLastModified int      `json:\"lastModified\"`\n\n\tApps  []AppDefine     `json:\"apps\"`\n\tExtra ExtraProperties `json:\"additionalProperties\"`\n}\n\ntype AppDefine struct {\n\tIcon         string `json:\"icon\"`\n\tName         string `json:\"name\"`\n\tReadMe       string `json:\"readMe\"`\n\tLastModified int    `json:\"lastModified\"`\n\n\tAppProperty AppProperty        `json:\"additionalProperties\"`\n\tVersions    []AppConfigVersion `json:\"versions\"`\n}\n\ntype LocalAppAppDefine struct {\n\tAppProperty AppProperty `json:\"additionalProperties\" yaml:\"additionalProperties\"`\n}\n\ntype LocalAppParam struct {\n\tAppParams LocalAppInstallDefine `json:\"additionalProperties\" yaml:\"additionalProperties\"`\n}\n\ntype LocalAppInstallDefine struct {\n\tFormFields interface{} `json:\"formFields\" yaml:\"formFields\"`\n}\n\ntype ExtraProperties struct {\n\tTags    []Tag  `json:\"tags\"`\n\tVersion string `json:\"version\"`\n}\n\ntype AppProperty struct {\n\tName                string   `json:\"name\"`\n\tType                string   `json:\"type\"`\n\tTags                []string `json:\"tags\"`\n\tShortDescZh         string   `json:\"shortDescZh\" yaml:\"shortDescZh\"`\n\tShortDescEn         string   `json:\"shortDescEn\" yaml:\"shortDescEn\"`\n\tDescription         Locale   `json:\"description\"`\n\tKey                 string   `json:\"key\"`\n\tRequired            []string `json:\"Required\"`\n\tCrossVersionUpdate  bool     `json:\"crossVersionUpdate\" yaml:\"crossVersionUpdate\"`\n\tLimit               int      `json:\"limit\" yaml:\"limit\"`\n\tRecommend           int      `json:\"recommend\" yaml:\"recommend\"`\n\tWebsite             string   `json:\"website\"`\n\tGithub              string   `json:\"github\"`\n\tDocument            string   `json:\"document\"`\n\tArchitectures       []string `json:\"architectures\"`\n\tMemoryRequired      int      `json:\"memoryRequired\" yaml:\"memoryRequired\"`\n\tGpuSupport          bool     `json:\"gpuSupport\" yaml:\"gpuSupport\"`\n\tVersion             float64  `json:\"version\"`\n\tDeprecated          float64  `json:\"deprecated\"`\n\tBatchInstallSupport bool     `json:\"batchInstallSupport\"`\n}\n\ntype AppConfigVersion struct {\n\tName                string      `json:\"name\"`\n\tLastModified        int         `json:\"lastModified\"`\n\tDownloadUrl         string      `json:\"downloadUrl\"`\n\tDownloadCallBackUrl string      `json:\"downloadCallBackUrl\"`\n\tAppForm             interface{} `json:\"additionalProperties\"`\n}\n\ntype Tag struct {\n\tKey     string `json:\"key\"`\n\tName    string `json:\"name\"`\n\tSort    int    `json:\"sort\"`\n\tLocales Locale `json:\"locales\"`\n}\n\ntype Locale struct {\n\tEn     string `json:\"en\"`\n\tJa     string `json:\"ja\"`\n\tMs     string `json:\"ms\"`\n\tPtBr   string `json:\"pt-br\" yaml:\"pt-br\"`\n\tRu     string `json:\"ru\"`\n\tZhHant string `json:\"zh-hant\" yaml:\"zh-hant\"`\n\tZh     string `json:\"zh\"`\n\tKo     string `json:\"ko\"`\n\tTr     string `json:\"tr\"`\n\tEs     string `json:\"es-es\" yaml:\"es-es\"`\n}\n\ntype AppForm struct {\n\tFormFields     []AppFormFields `json:\"formFields\"`\n\tSupportVersion float64         `json:\"supportVersion\"`\n}\n\ntype AppFormFields struct {\n\tType        string         `json:\"type\"`\n\tLabelZh     string         `json:\"labelZh\"`\n\tLabelEn     string         `json:\"labelEn\"`\n\tLabel       Locale         `json:\"label\"`\n\tDescription Locale         `json:\"description\"`\n\tRequired    bool           `json:\"required\"`\n\tDefault     interface{}    `json:\"default\"`\n\tEnvKey      string         `json:\"envKey\"`\n\tDisabled    bool           `json:\"disabled\"`\n\tEdit        bool           `json:\"edit\"`\n\tRule        string         `json:\"rule\"`\n\tMultiple    bool           `json:\"multiple\"`\n\tChild       interface{}    `json:\"child\"`\n\tValues      []AppFormValue `json:\"values\"`\n}\n\ntype AppFormValue struct {\n\tLabel string `json:\"label\"`\n\tValue string `json:\"value\"`\n}\n\ntype AppResource struct {\n\tType string `json:\"type\"`\n\tName string `json:\"name\"`\n}\n\nvar AppToolMap = map[string]string{\n\t\"mysql\": \"phpmyadmin\",\n\t\"redis\": \"redis-commander\",\n}\n\ntype AppInstallInfo struct {\n\tID   uint   `json:\"id\"`\n\tKey  string `json:\"key\"`\n\tName string `json:\"name\"`\n}\n\ntype DelAppLink struct {\n\tCtx         context.Context\n\tTask        *task.Task\n\tInstall     *model.AppInstall\n\tForceDelete bool\n}\n\ntype PHPForm struct {\n\tAdditionalProperties struct {\n\t\tFormFields []interface{} `yaml:\"formFields\"`\n\t} `yaml:\"additionalProperties\"`\n}\n"
  },
  {
    "path": "agent/app/dto/backup.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n)\n\ntype BackupOperate struct {\n\tID         uint   `json:\"id\"`\n\tName       string `json:\"name\"`\n\tType       string `json:\"type\" validate:\"required\"`\n\tIsPublic   bool   `json:\"isPublic\"`\n\tBucket     string `json:\"bucket\"`\n\tAccessKey  string `json:\"accessKey\"`\n\tCredential string `json:\"credential\"`\n\tBackupPath string `json:\"backupPath\"`\n\tVars       string `json:\"vars\" validate:\"required\"`\n\n\tRememberAuth bool `json:\"rememberAuth\"`\n}\n\ntype BackupInfo struct {\n\tID         uint      `json:\"id\"`\n\tName       string    `json:\"name\"`\n\tType       string    `json:\"type\"`\n\tIsPublic   bool      `json:\"isPublic\"`\n\tBucket     string    `json:\"bucket\"`\n\tAccessKey  string    `json:\"accessKey\"`\n\tCredential string    `json:\"credential\"`\n\tBackupPath string    `json:\"backupPath\"`\n\tVars       string    `json:\"vars\"`\n\tCreatedAt  time.Time `json:\"createdAt\"`\n\n\tRememberAuth bool `json:\"rememberAuth\"`\n}\n\ntype BackupCheckRes struct {\n\tIsOk  bool   `json:\"isOk\"`\n\tMsg   string `json:\"msg\"`\n\tToken string `json:\"token\"`\n}\n\ntype ForBuckets struct {\n\tType       string `json:\"type\" validate:\"required\"`\n\tAccessKey  string `json:\"accessKey\"`\n\tCredential string `json:\"credential\" validate:\"required\"`\n\tVars       string `json:\"vars\" validate:\"required\"`\n}\n\ntype SyncFromMaster struct {\n\tName      string `json:\"name\" validate:\"required\"`\n\tOperation string `json:\"operation\" validate:\"required,oneof=create delete update\"`\n\tData      string `json:\"data\"`\n}\n\ntype BackupOption struct {\n\tID       uint   `json:\"id\"`\n\tName     string `json:\"name\"`\n\tType     string `json:\"type\"`\n\tIsPublic bool   `json:\"isPublic\"`\n}\n\ntype UploadForRecover struct {\n\tFilePath  string `json:\"filePath\"`\n\tTargetDir string `json:\"targetDir\"`\n}\n\ntype CommonBackup struct {\n\tType       string   `json:\"type\" validate:\"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster container compose\"`\n\tName       string   `json:\"name\"`\n\tDetailName string   `json:\"detailName\"`\n\tSecret     string   `json:\"secret\"`\n\tStopBefore bool     `json:\"stopBefore\"`\n\tTaskID     string   `json:\"taskID\"`\n\tFileName   string   `json:\"fileName\"`\n\tArgs       []string `json:\"args\"`\n\n\tDescription string `json:\"description\"`\n}\ntype CommonRecover struct {\n\tDownloadAccountID uint   `json:\"downloadAccountID\" validate:\"required\"`\n\tType              string `json:\"type\" validate:\"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster container compose\"`\n\tName              string `json:\"name\"`\n\tDetailName        string `json:\"detailName\"`\n\tFile              string `json:\"file\"`\n\tSecret            string `json:\"secret\"`\n\tTaskID            string `json:\"taskID\"`\n\tBackupRecordID    uint   `json:\"backupRecordID\"`\n\tTimeout           int    `json:\"timeout\"`\n}\n\ntype RecordSearch struct {\n\tPageInfo\n\tType       string `json:\"type\" validate:\"required\"`\n\tName       string `json:\"name\"`\n\tDetailName string `json:\"detailName\"`\n}\n\ntype RecordSearchByCronjob struct {\n\tPageInfo\n\tCronjobID uint `json:\"cronjobID\" validate:\"required\"`\n}\n\ntype BackupRecords struct {\n\tID                uint      `json:\"id\"`\n\tCreatedAt         time.Time `json:\"createdAt\"`\n\tAccountType       string    `json:\"accountType\"`\n\tAccountName       string    `json:\"accountName\"`\n\tDownloadAccountID uint      `json:\"downloadAccountID\"`\n\tFileDir           string    `json:\"fileDir\"`\n\tFileName          string    `json:\"fileName\"`\n\tTaskID            string    `json:\"taskID\"`\n\tStatus            string    `json:\"status\"`\n\tMessage           string    `json:\"message\"`\n\tDescription       string    `json:\"description\"`\n}\n\ntype DownloadRecord struct {\n\tDownloadAccountID uint   `json:\"downloadAccountID\" validate:\"required\"`\n\tFileDir           string `json:\"fileDir\" validate:\"required\"`\n\tFileName          string `json:\"fileName\" validate:\"required\"`\n}\n\ntype SearchForSize struct {\n\tPageInfo\n\tType       string `json:\"type\" validate:\"required\"`\n\tName       string `json:\"name\"`\n\tDetailName string `json:\"detailName\"`\n\tInfo       string `json:\"info\"`\n\tCronjobID  uint   `json:\"cronjobID\"`\n\tOrderBy    string `json:\"orderBy\"`\n\tOrder      string `json:\"order\"`\n}\ntype RecordFileSize struct {\n\tID   uint   `json:\"id\"`\n\tName string `json:\"name\"`\n\tSize int64  `json:\"size\"`\n}\n"
  },
  {
    "path": "agent/app/dto/clam.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n)\n\ntype SearchClamWithPage struct {\n\tPageInfo\n\tInfo    string `json:\"info\"`\n\tOrderBy string `json:\"orderBy\" validate:\"required,oneof=name status createdAt\"`\n\tOrder   string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n}\n\ntype ClamBaseInfo struct {\n\tVersion  string `json:\"version\"`\n\tIsActive bool   `json:\"isActive\"`\n\tIsExist  bool   `json:\"isExist\"`\n\n\tFreshVersion  string `json:\"freshVersion\"`\n\tFreshIsActive bool   `json:\"freshIsActive\"`\n\tFreshIsExist  bool   `json:\"freshIsExist\"`\n}\n\ntype ClamInfo struct {\n\tID        uint      `json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\n\tName             string `json:\"name\"`\n\tStatus           string `json:\"status\"`\n\tPath             string `json:\"path\"`\n\tInfectedStrategy string `json:\"infectedStrategy\"`\n\tInfectedDir      string `json:\"infectedDir\"`\n\tLastRecordStatus string `json:\"lastRecordStatus\"`\n\tLastRecordTime   string `json:\"lastRecordTime\"`\n\tSpec             string `json:\"spec\"`\n\tTimeout          uint   `json:\"timeout\"`\n\tDescription      string `json:\"description\"`\n\tAlertCount       uint   `json:\"alertCount\"`\n\tAlertMethod      string `json:\"alertMethod\"`\n}\n\ntype ClamLogSearch struct {\n\tPageInfo\n\n\tClamID    uint      `json:\"clamID\"`\n\tStatus    string    `json:\"status\"`\n\tStartTime time.Time `json:\"startTime\"`\n\tEndTime   time.Time `json:\"endTime\"`\n}\n\ntype ClamLogReq struct {\n\tTail       string `json:\"tail\"`\n\tClamName   string `json:\"clamName\"`\n\tRecordName string `json:\"recordName\"`\n}\n\ntype ClamFileReq struct {\n\tTail string `json:\"tail\"`\n\tName string `json:\"name\" validate:\"required\"`\n}\n\ntype ClamRecord struct {\n\tID            uint      `json:\"id\"`\n\tTaskID        string    `json:\"taskID\"`\n\tStartTime     time.Time `json:\"startTime\"`\n\tScanTime      string    `json:\"scanTime\"`\n\tInfectedFiles string    `json:\"infectedFiles\"`\n\tTotalError    string    `json:\"totalError\"`\n\n\tStatus  string `json:\"status\"`\n\tMessage string `json:\"message\"`\n}\n\ntype ClamCreate struct {\n\tName             string `json:\"name\"`\n\tStatus           string `json:\"status\"`\n\tPath             string `json:\"path\"`\n\tInfectedStrategy string `json:\"infectedStrategy\"`\n\tInfectedDir      string `json:\"infectedDir\"`\n\tSpec             string `json:\"spec\"`\n\tTimeout          uint   `json:\"timeout\"`\n\tDescription      string `json:\"description\"`\n\tAlertCount       uint   `json:\"alertCount\"`\n\tAlertTitle       string `json:\"alertTitle\"`\n\tAlertMethod      string `json:\"alertMethod\"`\n}\n\ntype ClamUpdate struct {\n\tID uint `json:\"id\"`\n\n\tName             string `json:\"name\"`\n\tPath             string `json:\"path\"`\n\tInfectedStrategy string `json:\"infectedStrategy\"`\n\tInfectedDir      string `json:\"infectedDir\"`\n\tSpec             string `json:\"spec\"`\n\tTimeout          uint   `json:\"timeout\"`\n\tDescription      string `json:\"description\"`\n\tAlertCount       uint   `json:\"alertCount\"`\n\tAlertTitle       string `json:\"alertTitle\"`\n\tAlertMethod      string `json:\"alertMethod\"`\n}\n\ntype ClamUpdateStatus struct {\n\tID     uint   `json:\"id\"`\n\tStatus string `json:\"status\"`\n}\n\ntype ClamDelete struct {\n\tRemoveInfected bool   `json:\"removeInfected\"`\n\tIds            []uint `json:\"ids\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/command.go",
    "content": "package dto\n\ntype SearchCommandWithPage struct {\n\tPageInfo\n\tOrderBy string `json:\"orderBy\" validate:\"required,oneof=name command createdAt\"`\n\tOrder   string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n\tGroupID uint   `json:\"groupID\"`\n\tInfo    string `json:\"info\"`\n\tName    string `json:\"name\"`\n}\n\ntype CommandOperate struct {\n\tID          uint   `json:\"id\"`\n\tGroupID     uint   `json:\"groupID\"`\n\tGroupBelong string `json:\"groupBelong\"`\n\tName        string `json:\"name\" validate:\"required\"`\n\tCommand     string `json:\"command\" validate:\"required\"`\n}\n\ntype CommandInfo struct {\n\tID          uint   `json:\"id\"`\n\tGroupID     uint   `json:\"groupID\"`\n\tName        string `json:\"name\"`\n\tCommand     string `json:\"command\"`\n\tGroupBelong string `json:\"groupBelong\"`\n}\n\ntype CommandTree struct {\n\tID       uint          `json:\"id\"`\n\tLabel    string        `json:\"label\"`\n\tChildren []CommandInfo `json:\"children\"`\n}\n\ntype RedisCommand struct {\n\tID      uint   `json:\"id\"`\n\tName    string `json:\"name\"`\n\tCommand string `json:\"command\"`\n}\n"
  },
  {
    "path": "agent/app/dto/common_req.go",
    "content": "package dto\n\ntype SearchWithPage struct {\n\tPageInfo\n\tInfo string `json:\"info\"`\n}\n\ntype SearchPageWithType struct {\n\tPageInfo\n\tInfo string `json:\"info\"`\n\tType string `json:\"type\"`\n}\n\ntype SearchPageWithGroup struct {\n\tPageInfo\n\tGroupID uint   `json:\"groupID\"`\n\tInfo    string `json:\"info\"`\n}\n\ntype PageInfo struct {\n\tPage     int `json:\"page\" validate:\"required,number\"`\n\tPageSize int `json:\"pageSize\" validate:\"required,number\"`\n}\n\ntype UpdateDescription struct {\n\tID          uint   `json:\"id\" validate:\"required\"`\n\tDescription string `json:\"description\" validate:\"max=256\"`\n}\n\ntype OperationWithName struct {\n\tName string `json:\"name\" validate:\"required\"`\n}\n\ntype OperateByID struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\ntype OperateByIDs struct {\n\tIDs []uint `json:\"ids\"`\n}\n\ntype Operate struct {\n\tOperation string `json:\"operation\" validate:\"required\"`\n}\n\ntype SearchByFilter struct {\n\tFilter string `json:\"filter\"`\n}\n\ntype BatchDeleteReq struct {\n\tIds []uint `json:\"ids\" validate:\"required\"`\n}\n\ntype FilePath struct {\n\tPath string `json:\"path\" validate:\"required\"`\n}\n\ntype DeleteByName struct {\n\tName string `json:\"name\" validate:\"required\"`\n}\n\ntype UpdateByFile struct {\n\tFile string `json:\"file\"`\n}\n\ntype UpdateByNameAndFile struct {\n\tName string `json:\"name\"`\n\tFile string `json:\"file\"`\n}\n\ntype OperationWithNameAndType struct {\n\tName string `json:\"name\"`\n\tType string `json:\"type\" validate:\"required\"`\n}\n\ntype UpdateGroup struct {\n\tGroup    uint `json:\"group\"`\n\tNewGroup uint `json:\"newGroup\"`\n}\n\ntype OperateWithTask struct {\n\tTaskID string `json:\"taskID\"`\n}\n\ntype ForceDelete struct {\n\tIDs         []uint `json:\"ids\"`\n\tForceDelete bool   `json:\"forceDelete\"`\n}\n\ntype ChangeGroup struct {\n\tID      uint `json:\"id\" validate:\"required\"`\n\tGroupID uint `json:\"groupID\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/common_res.go",
    "content": "package dto\n\ntype PageResult struct {\n\tTotal int64       `json:\"total\"`\n\tItems interface{} `json:\"items\"`\n}\n\ntype Response struct {\n\tCode    int         `json:\"code\"`\n\tMessage string      `json:\"message\"`\n\tData    interface{} `json:\"data\"`\n}\n\ntype Options struct {\n\tOption string `json:\"option\"`\n}\n"
  },
  {
    "path": "agent/app/dto/compose_template.go",
    "content": "package dto\n\nimport \"time\"\n\ntype ComposeTemplateCreate struct {\n\tName        string `json:\"name\" validate:\"required\"`\n\tDescription string `json:\"description\"`\n\tContent     string `json:\"content\"`\n}\n\ntype ComposeTemplateBatch struct {\n\tTemplates []ComposeTemplateCreate `json:\"templates\" validate:\"required\"`\n}\n\ntype ComposeTemplateUpdate struct {\n\tID          uint   `json:\"id\"`\n\tDescription string `json:\"description\"`\n\tContent     string `json:\"content\"`\n}\n\ntype ComposeTemplateInfo struct {\n\tID          uint      `json:\"id\"`\n\tCreatedAt   time.Time `json:\"createdAt\"`\n\tName        string    `json:\"name\"`\n\tDescription string    `json:\"description\"`\n\tContent     string    `json:\"content\"`\n}\n"
  },
  {
    "path": "agent/app/dto/container.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n)\n\ntype PageContainer struct {\n\tPageInfo\n\tName            string `json:\"name\"`\n\tState           string `json:\"state\" validate:\"required,oneof=all created running paused restarting removing exited dead\"`\n\tOrderBy         string `json:\"orderBy\" validate:\"required,oneof=name createdAt state\"`\n\tOrder           string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n\tFilters         string `json:\"filters\"`\n\tExcludeAppStore bool   `json:\"excludeAppStore\"`\n}\n\ntype InspectReq struct {\n\tID     string `json:\"id\" validate:\"required\"`\n\tType   string `json:\"type\" validate:\"required\"`\n\tDetail string `json:\"detail\"`\n}\n\ntype ContainerInfo struct {\n\tContainerID string `json:\"containerID\"`\n\tName        string `json:\"name\"`\n\tImageId     string `json:\"imageID\"`\n\tImageName   string `json:\"imageName\"`\n\tCreateTime  string `json:\"createTime\"`\n\tState       string `json:\"state\"`\n\tRunTime     string `json:\"runTime\"`\n\n\tNetwork []string `json:\"network\"`\n\tPorts   []string `json:\"ports\"`\n\n\tIsFromApp     bool `json:\"isFromApp\"`\n\tIsFromCompose bool `json:\"isFromCompose\"`\n\n\tAppName        string   `json:\"appName\"`\n\tAppInstallName string   `json:\"appInstallName\"`\n\tWebsites       []string `json:\"websites\"`\n\n\tIsPinned    bool   `json:\"isPinned\"`\n\tDescription string `json:\"description\"`\n}\n\ntype ContainerOptions struct {\n\tName  string `json:\"name\"`\n\tState string `json:\"state\"`\n}\n\ntype ContainerFileReq struct {\n\tContainerID string `json:\"containerID\" validate:\"required\"`\n\tPath        string `json:\"path\" validate:\"required\"`\n}\n\ntype ContainerFileBatchDeleteReq struct {\n\tContainerID string   `json:\"containerID\" validate:\"required\"`\n\tPaths       []string `json:\"paths\" validate:\"required,min=1,dive,required\"`\n}\n\ntype ContainerFileInfo struct {\n\tName    string `json:\"name\"`\n\tPath    string `json:\"path\"`\n\tIsDir   bool   `json:\"isDir\"`\n\tIsLink  bool   `json:\"isLink\"`\n\tLinkTo  string `json:\"linkTo\"`\n\tSize    int64  `json:\"size\"`\n\tMode    string `json:\"mode\"`\n\tModTime string `json:\"modTime\"`\n}\n\ntype ContainerFileContent struct {\n\tContent   string `json:\"content\"`\n\tSize      int64  `json:\"size\"`\n\tTruncated bool   `json:\"truncated\"`\n\tIsBinary  bool   `json:\"isBinary\"`\n}\n\ntype ContainerStatus struct {\n\tCreated    int `json:\"created\"`\n\tRunning    int `json:\"running\"`\n\tPaused     int `json:\"paused\"`\n\tRestarting int `json:\"restarting\"`\n\tRemoving   int `json:\"removing\"`\n\tExited     int `json:\"exited\"`\n\tDead       int `json:\"dead\"`\n\n\tContainerCount       int `json:\"containerCount\"`\n\tComposeCount         int `json:\"composeCount\"`\n\tComposeTemplateCount int `json:\"composeTemplateCount\"`\n\tImageCount           int `json:\"imageCount\"`\n\tNetworkCount         int `json:\"networkCount\"`\n\tVolumeCount          int `json:\"volumeCount\"`\n\tRepoCount            int `json:\"repoCount\"`\n}\ntype ResourceLimit struct {\n\tCPU    int    `json:\"cpu\"`\n\tMemory uint64 `json:\"memory\"`\n}\n\ntype ContainerOperate struct {\n\tTaskID    string `json:\"taskID\"`\n\tForcePull bool   `json:\"forcePull\"`\n\tName      string `json:\"name\" validate:\"required\"`\n\tImage     string `json:\"image\" validate:\"required\"`\n\n\tHostname   string             `json:\"hostname\"`\n\tDomainName string             `json:\"domainName\"`\n\tDNS        []string           `json:\"dns\"`\n\tNetworks   []ContainerNetwork `json:\"networks\"`\n\n\tPublishAllPorts bool           `json:\"publishAllPorts\"`\n\tExposedPorts    []PortHelper   `json:\"exposedPorts\"`\n\tTty             bool           `json:\"tty\"`\n\tOpenStdin       bool           `json:\"openStdin\"`\n\tWorkingDir      string         `json:\"workingDir\"`\n\tUser            string         `json:\"user\"`\n\tCmd             []string       `json:\"cmd\"`\n\tEntrypoint      []string       `json:\"entrypoint\"`\n\tCPUShares       int64          `json:\"cpuShares\"`\n\tNanoCPUs        float64        `json:\"nanoCPUs\"`\n\tMemory          float64        `json:\"memory\"`\n\tPrivileged      bool           `json:\"privileged\"`\n\tAutoRemove      bool           `json:\"autoRemove\"`\n\tVolumes         []VolumeHelper `json:\"volumes\"`\n\tExtraHosts      []ExtraHost    `json:\"extraHosts\"`\n\tLabels          []string       `json:\"labels\"`\n\tEnv             []string       `json:\"env\"`\n\tRestartPolicy   string         `json:\"restartPolicy\"`\n}\n\ntype ExtraHost struct {\n\tHostname string `json:\"hostname\"`\n\tIP       string `json:\"ip\"`\n}\ntype ContainerNetwork struct {\n\tNetwork string `json:\"network\"`\n\tIpv4    string `json:\"ipv4\"`\n\tIpv6    string `json:\"ipv6\"`\n\tMacAddr string `json:\"macAddr\"`\n}\n\ntype ContainerCreateByCommand struct {\n\tTaskID  string `json:\"taskID\"`\n\tCommand string `json:\"command\"`\n}\n\ntype ContainerUpgrade struct {\n\tTaskID    string   `json:\"taskID\"`\n\tNames     []string `json:\"names\" validate:\"required\"`\n\tImage     string   `json:\"image\" validate:\"required\"`\n\tForcePull bool     `json:\"forcePull\"`\n}\n\ntype ContainerItemStats struct {\n\tSizeRw     int64 `json:\"sizeRw\"`\n\tSizeRootFs int64 `json:\"sizeRootFs\"`\n\n\tContainerUsage        int64 `json:\"containerUsage\"`\n\tContainerReclaimable  int64 `json:\"containerReclaimable\"`\n\tImageUsage            int64 `json:\"imageUsage\"`\n\tImageReclaimable      int64 `json:\"imageReclaimable\"`\n\tVolumeUsage           int64 `json:\"volumeUsage\"`\n\tVolumeReclaimable     int64 `json:\"volumeReclaimable\"`\n\tBuildCacheUsage       int64 `json:\"buildCacheUsage\"`\n\tBuildCacheReclaimable int64 `json:\"buildCacheReclaimable\"`\n}\ntype ContainerListStats struct {\n\tContainerID string `json:\"containerID\"`\n\n\tCPUTotalUsage uint64  `json:\"cpuTotalUsage\"`\n\tSystemUsage   uint64  `json:\"systemUsage\"`\n\tCPUPercent    float64 `json:\"cpuPercent\"`\n\tPercpuUsage   int     `json:\"percpuUsage\"`\n\n\tMemoryCache   uint64  `json:\"memoryCache\"`\n\tMemoryUsage   uint64  `json:\"memoryUsage\"`\n\tMemoryLimit   uint64  `json:\"memoryLimit\"`\n\tMemoryPercent float64 `json:\"memoryPercent\"`\n}\n\ntype ContainerStats struct {\n\tCPUPercent float64 `json:\"cpuPercent\"`\n\tMemory     float64 `json:\"memory\"`\n\tCache      float64 `json:\"cache\"`\n\tIORead     float64 `json:\"ioRead\"`\n\tIOWrite    float64 `json:\"ioWrite\"`\n\tNetworkRX  float64 `json:\"networkRX\"`\n\tNetworkTX  float64 `json:\"networkTX\"`\n\n\tShotTime time.Time `json:\"shotTime\"`\n}\n\ntype VolumeHelper struct {\n\tType         string `json:\"type\"`\n\tSourceDir    string `json:\"sourceDir\"`\n\tContainerDir string `json:\"containerDir\"`\n\tMode         string `json:\"mode\"`\n\tShared       string `json:\"shared\"`\n}\ntype PortHelper struct {\n\tHostIP        string `json:\"hostIP\"`\n\tHostPort      string `json:\"hostPort\"`\n\tContainerPort string `json:\"containerPort\"`\n\tProtocol      string `json:\"protocol\"`\n}\n\ntype ContainerOperation struct {\n\tTaskID    string   `json:\"taskID\"`\n\tNames     []string `json:\"names\" validate:\"required\"`\n\tOperation string   `json:\"operation\" validate:\"required,oneof=up start stop restart kill pause unpause remove\"`\n}\n\ntype ContainerRename struct {\n\tName    string `json:\"name\" validate:\"required\"`\n\tNewName string `json:\"newName\" validate:\"required\"`\n}\n\ntype ContainerCommit struct {\n\tContainerId   string `json:\"containerID\" validate:\"required\"`\n\tContainerName string `json:\"containerName\"`\n\tNewImageName  string `json:\"newImageName\"`\n\tComment       string `json:\"comment\"`\n\tAuthor        string `json:\"author\"`\n\tPause         bool   `json:\"pause\"`\n\n\tTaskID string `json:\"taskID\"`\n}\n\ntype ContainerPrune struct {\n\tTaskID     string `json:\"taskID\"`\n\tPruneType  string `json:\"pruneType\" validate:\"required,oneof=container image volume network buildcache\"`\n\tWithTagAll bool   `json:\"withTagAll\"`\n}\n\ntype Network struct {\n\tID         string    `json:\"id\"`\n\tName       string    `json:\"name\"`\n\tLabels     []string  `json:\"labels\"`\n\tDriver     string    `json:\"driver\"`\n\tIPAMDriver string    `json:\"ipamDriver\"`\n\tSubnet     string    `json:\"subnet\"`\n\tGateway    string    `json:\"gateway\"`\n\tCreatedAt  time.Time `json:\"createdAt\"`\n\tAttachable bool      `json:\"attachable\"`\n}\ntype NetworkCreate struct {\n\tName       string          `json:\"name\" validate:\"required\"`\n\tDriver     string          `json:\"driver\" validate:\"required\"`\n\tOptions    []string        `json:\"options\"`\n\tIpv4       bool            `json:\"ipv4\"`\n\tSubnet     string          `json:\"subnet\"`\n\tGateway    string          `json:\"gateway\"`\n\tIPRange    string          `json:\"ipRange\"`\n\tAuxAddress []SettingUpdate `json:\"auxAddress\"`\n\n\tIpv6         bool            `json:\"ipv6\"`\n\tSubnetV6     string          `json:\"subnetV6\"`\n\tGatewayV6    string          `json:\"gatewayV6\"`\n\tIPRangeV6    string          `json:\"ipRangeV6\"`\n\tAuxAddressV6 []SettingUpdate `json:\"auxAddressV6\"`\n\tLabels       []string        `json:\"labels\"`\n}\n\ntype Volume struct {\n\tName       string         `json:\"name\"`\n\tLabels     []VolumeOption `json:\"labels\"`\n\tDriver     string         `json:\"driver\"`\n\tMountpoint string         `json:\"mountpoint\"`\n\tCreatedAt  time.Time      `json:\"createdAt\"`\n\tOptions    []VolumeOption `json:\"options\"`\n}\ntype VolumeCreate struct {\n\tName    string   `json:\"name\" validate:\"required\"`\n\tDriver  string   `json:\"driver\" validate:\"required\"`\n\tOptions []string `json:\"options\"`\n\tLabels  []string `json:\"labels\"`\n}\ntype VolumeOption struct {\n\tKey   string `json:\"key\"`\n\tValue string `json:\"value\"`\n}\ntype BatchDelete struct {\n\tTaskID string   `json:\"taskID\"`\n\tForce  bool     `json:\"force\"`\n\tNames  []string `json:\"names\" validate:\"required\"`\n}\n\ntype ComposeInfo struct {\n\tName           string             `json:\"name\"`\n\tCreatedAt      string             `json:\"createdAt\"`\n\tCreatedBy      string             `json:\"createdBy\"`\n\tContainerCount int                `json:\"containerCount\"`\n\tRunningCount   int                `json:\"runningCount\"`\n\tConfigFile     string             `json:\"configFile\"`\n\tWorkdir        string             `json:\"workdir\"`\n\tPath           string             `json:\"path\"`\n\tContainers     []ComposeContainer `json:\"containers\"`\n\tEnv            string             `json:\"env\"`\n}\ntype ComposeContainer struct {\n\tContainerID string   `json:\"containerID\"`\n\tName        string   `json:\"name\"`\n\tCreateTime  string   `json:\"createTime\"`\n\tState       string   `json:\"state\"`\n\tPorts       []string `json:\"ports\"`\n}\ntype ComposeCreate struct {\n\tTaskID    string `json:\"taskID\"`\n\tName      string `json:\"name\"`\n\tFrom      string `json:\"from\" validate:\"required,oneof=edit path template\"`\n\tFile      string `json:\"file\"`\n\tPath      string `json:\"path\"`\n\tTemplate  uint   `json:\"template\"`\n\tEnv       string `json:\"env\"`\n\tForcePull bool   `json:\"forcePull\"`\n}\ntype ComposeOperation struct {\n\tName      string `json:\"name\" validate:\"required\"`\n\tPath      string `json:\"path\"`\n\tOperation string `json:\"operation\" validate:\"required,oneof=up start restart stop down delete\"`\n\tWithFile  bool   `json:\"withFile\"`\n\tForce     bool   `json:\"force\"`\n}\ntype ComposeUpdate struct {\n\tTaskID     string `json:\"taskID\"`\n\tName       string `json:\"name\" validate:\"required\"`\n\tPath       string `json:\"path\" validate:\"required\"`\n\tDetailPath string `json:\"detailPath\"`\n\tContent    string `json:\"content\" validate:\"required\"`\n\tEnv        string `json:\"env\"`\n\tForcePull  bool   `json:\"forcePull\"`\n}\ntype ComposeLogClean struct {\n\tName       string `json:\"name\" validate:\"required\"`\n\tPath       string `json:\"path\" validate:\"required\"`\n\tDetailPath string `json:\"detailPath\"`\n}\n\ntype ContainerLog struct {\n\tContainer     string `json:\"container\" validate:\"required\"`\n\tSince         string `json:\"since\"`\n\tTail          uint   `json:\"tail\"`\n\tTimestamp     bool   `json:\"timestamp\"`\n\tContainerType string `json:\"containerType\"`\n}\n\ntype StreamLog struct {\n\tCompose   string\n\tContainer string\n\tSince     string\n\tFollow    bool\n\tTail      string\n\tTimestamp bool\n\tType      string\n}\n"
  },
  {
    "path": "agent/app/dto/cronjob.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n)\n\ntype PageCronjob struct {\n\tPageInfo\n\tInfo     string `json:\"info\"`\n\tGroupIDs []uint `json:\"groupIDs\"`\n\tOrderBy  string `json:\"orderBy\" validate:\"required,oneof=name status createdAt\"`\n\tOrder    string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n}\n\ntype CronjobSpec struct {\n\tSpec string `json:\"spec\" validate:\"required\"`\n}\n\ntype CronjobOperate struct {\n\tID         uint   `json:\"id\"`\n\tName       string `json:\"name\" validate:\"required\"`\n\tType       string `json:\"type\" validate:\"required\"`\n\tGroupID    uint   `json:\"groupID\"`\n\tSpecCustom bool   `json:\"specCustom\"`\n\tSpec       string `json:\"spec\" validate:\"required\"`\n\n\tExecutor      string `json:\"executor\"`\n\tScriptMode    string `json:\"scriptMode\"`\n\tScript        string `json:\"script\"`\n\tCommand       string `json:\"command\"`\n\tContainerName string `json:\"containerName\"`\n\tUser          string `json:\"user\"`\n\n\tScriptID       uint         `json:\"scriptID\"`\n\tAppID          string       `json:\"appID\"`\n\tWebsite        string       `json:\"website\"`\n\tExclusionRules string       `json:\"exclusionRules\"`\n\tDBType         string       `json:\"dbType\"`\n\tDBName         string       `json:\"dbName\"`\n\tURL            string       `json:\"url\"`\n\tIsDir          bool         `json:\"isDir\"`\n\tSourceDir      string       `json:\"sourceDir\"`\n\tSnapshotRule   SnapshotRule `json:\"snapshotRule\"`\n\n\tSourceAccountIDs  string `json:\"sourceAccountIDs\"`\n\tDownloadAccountID uint   `json:\"downloadAccountID\"`\n\tRetainCopies      int    `json:\"retainCopies\" validate:\"number,min=1\"`\n\tRetryTimes        int    `json:\"retryTimes\" validate:\"number,min=0\"`\n\tTimeout           uint   `json:\"timeout\" validate:\"number,min=1\"`\n\tIgnoreErr         bool   `json:\"ignoreErr\"`\n\tSecret            string `json:\"secret\"`\n\tArgs              string `json:\"args\"`\n\n\tAlertCount  uint   `json:\"alertCount\"`\n\tAlertTitle  string `json:\"alertTitle\"`\n\tAlertMethod string `json:\"alertMethod\"`\n\n\tCleanLogConfig\n}\n\ntype CleanLogConfig struct {\n\tScopes []string `json:\"scopes\"`\n}\n\ntype SnapshotRule struct {\n\tWithImage    bool   `json:\"withImage\"`\n\tIgnoreAppIDs []uint `json:\"ignoreAppIDs\"`\n}\n\ntype CronjobUpdateStatus struct {\n\tID     uint   `json:\"id\" validate:\"required\"`\n\tStatus string `json:\"status\" validate:\"required\"`\n}\n\ntype CronjobClean struct {\n\tIsDelete        bool `json:\"isDelete\"`\n\tCleanData       bool `json:\"cleanData\"`\n\tCronjobID       uint `json:\"cronjobID\" validate:\"required\"`\n\tCleanRemoteData bool `json:\"cleanRemoteData\"`\n}\n\ntype CronjobBatchDelete struct {\n\tCleanData       bool   `json:\"cleanData\"`\n\tCleanRemoteData bool   `json:\"cleanRemoteData\"`\n\tIDs             []uint `json:\"ids\" validate:\"required\"`\n}\n\ntype CronjobInfo struct {\n\tID         uint   `json:\"id\"`\n\tName       string `json:\"name\"`\n\tType       string `json:\"type\"`\n\tGroupID    uint   `json:\"groupID\"`\n\tSpecCustom bool   `json:\"specCustom\"`\n\tSpec       string `json:\"spec\"`\n\n\tExecutor      string `json:\"executor\"`\n\tScriptMode    string `json:\"scriptMode\"`\n\tScript        string `json:\"script\"`\n\tCommand       string `json:\"command\"`\n\tContainerName string `json:\"containerName\"`\n\tUser          string `json:\"user\"`\n\n\tScriptID       uint         `json:\"scriptID\"`\n\tAppID          string       `json:\"appID\"`\n\tWebsite        string       `json:\"website\"`\n\tExclusionRules string       `json:\"exclusionRules\"`\n\tDBType         string       `json:\"dbType\"`\n\tDBName         string       `json:\"dbName\"`\n\tURL            string       `json:\"url\"`\n\tIsDir          bool         `json:\"isDir\"`\n\tSourceDir      string       `json:\"sourceDir\"`\n\tRetainCopies   int          `json:\"retainCopies\"`\n\tRetryTimes     int          `json:\"retryTimes\"`\n\tTimeout        uint         `json:\"timeout\"`\n\tIgnoreErr      bool         `json:\"ignoreErr\"`\n\tSnapshotRule   SnapshotRule `json:\"snapshotRule\"`\n\n\tSourceAccounts    []string `json:\"sourceAccounts\"`\n\tDownloadAccount   string   `json:\"downloadAccount\"`\n\tSourceAccountIDs  string   `json:\"sourceAccountIDs\"`\n\tDownloadAccountID uint     `json:\"downloadAccountID\"`\n\n\tLastRecordStatus string `json:\"lastRecordStatus\"`\n\tLastRecordTime   string `json:\"lastRecordTime\"`\n\tStatus           string `json:\"status\"`\n\tSecret           string `json:\"secret\"`\n\tArgs             string `json:\"args\"`\n\n\tAlertCount uint `json:\"alertCount\"`\n}\n\ntype CronjobImport struct {\n\tCronjobs []CronjobTrans `json:\"cronjobs\"`\n}\ntype CronjobTrans struct {\n\tName       string `json:\"name\"`\n\tType       string `json:\"type\"`\n\tGroupID    uint   `json:\"groupID\"`\n\tSpecCustom bool   `json:\"specCustom\"`\n\tSpec       string `json:\"spec\"`\n\n\tExecutor      string `json:\"executor\"`\n\tScriptMode    string `json:\"scriptMode\"`\n\tScript        string `json:\"script\"`\n\tCommand       string `json:\"command\"`\n\tContainerName string `json:\"containerName\"`\n\tUser          string `json:\"user\"`\n\tURL           string `json:\"url\"`\n\n\tScriptName string        `json:\"scriptName\"`\n\tApps       []TransHelper `json:\"apps\"`\n\tWebsites   []string      `json:\"websites\"`\n\tDBType     string        `json:\"dbType\"`\n\tDBNames    []TransHelper `json:\"dbName\"`\n\n\tExclusionRules string `json:\"exclusionRules\"`\n\n\tIsDir     bool   `json:\"isDir\"`\n\tSourceDir string `json:\"sourceDir\"`\n\n\tRetainCopies uint64              `json:\"retainCopies\"`\n\tRetryTimes   uint                `json:\"retryTimes\"`\n\tTimeout      uint                `json:\"timeout\"`\n\tIgnoreErr    bool                `json:\"ignoreErr\"`\n\tSnapshotRule SnapshotTransHelper `json:\"snapshotRule\"`\n\tSecret       string              `json:\"secret\"`\n\tArgs         string              `json:\"args\"`\n\n\tSourceAccounts  []string `json:\"sourceAccounts\"`\n\tDownloadAccount string   `json:\"downloadAccount\"`\n\n\tAlertCount  uint   `json:\"alertCount\"`\n\tAlertTitle  string `json:\"alertTitle\"`\n\tAlertMethod string `json:\"alertMethod\"`\n}\ntype TransHelper struct {\n\tName       string `json:\"name\"`\n\tDetailName string `json:\"detailName\"`\n}\ntype SnapshotTransHelper struct {\n\tWithImage  bool          `json:\"withImage\"`\n\tIgnoreApps []TransHelper `json:\"ignoreApps\"`\n}\n\ntype ScriptOptions struct {\n\tID   uint   `json:\"id\"`\n\tName string `json:\"name\"`\n}\n\ntype SearchRecord struct {\n\tPageInfo\n\tCronjobID int       `json:\"cronjobID\"`\n\tStartTime time.Time `json:\"startTime\"`\n\tEndTime   time.Time `json:\"endTime\"`\n\tStatus    string    `json:\"status\"`\n}\n\ntype Record struct {\n\tID         uint   `json:\"id\"`\n\tTaskID     string `json:\"taskID\"`\n\tStartTime  string `json:\"startTime\"`\n\tRecords    string `json:\"records\"`\n\tStatus     string `json:\"status\"`\n\tMessage    string `json:\"message\"`\n\tTargetPath string `json:\"targetPath\"`\n\tInterval   int    `json:\"interval\"`\n\tFile       string `json:\"file\"`\n}\n"
  },
  {
    "path": "agent/app/dto/dashboard.go",
    "content": "package dto\n\nimport \"time\"\n\ntype DashboardBase struct {\n\tWebsiteNumber      int `json:\"websiteNumber\"`\n\tAgentNumber        int `json:\"agentNumber\"`\n\tDatabaseNumber     int `json:\"databaseNumber\"`\n\tCronjobNumber      int `json:\"cronjobNumber\"`\n\tAppInstalledNumber int `json:\"appInstalledNumber\"`\n\n\tHostname             string `json:\"hostname\"`\n\tOS                   string `json:\"os\"`\n\tPlatform             string `json:\"platform\"`\n\tPlatformFamily       string `json:\"platformFamily\"`\n\tPlatformVersion      string `json:\"platformVersion\"`\n\tPrettyDistro         string `json:\"prettyDistro\"`\n\tKernelArch           string `json:\"kernelArch\"`\n\tKernelVersion        string `json:\"kernelVersion\"`\n\tVirtualizationSystem string `json:\"virtualizationSystem\"`\n\tIpV4Addr             string `json:\"ipV4Addr\"`\n\tSystemProxy          string `json:\"systemProxy\"`\n\n\tCPUCores        int     `json:\"cpuCores\"`\n\tCPULogicalCores int     `json:\"cpuLogicalCores\"`\n\tCPUModelName    string  `json:\"cpuModelName\"`\n\tCPUMhz          float64 `json:\"cpuMhz\"`\n\n\tQuickJumps  []QuickJump      `json:\"quickJump\"`\n\tCurrentInfo DashboardCurrent `json:\"currentInfo\"`\n}\n\ntype ChangeQuicks struct {\n\tQuicks []QuickJump `json:\"quicks\"`\n}\n\ntype QuickJump struct {\n\tID        uint   `json:\"id\"`\n\tName      string `json:\"name\"`\n\tAlias     string `json:\"alias\"`\n\tTitle     string `json:\"title\"`\n\tDetail    string `json:\"detail\"`\n\tRecommend int    `json:\"recommend\"`\n\tIsShow    bool   `json:\"isShow\"`\n\tRouter    string `json:\"router\"`\n}\n\ntype OsInfo struct {\n\tOS             string `json:\"os\"`\n\tPlatform       string `json:\"platform\"`\n\tPlatformFamily string `json:\"platformFamily\"`\n\tKernelArch     string `json:\"kernelArch\"`\n\tKernelVersion  string `json:\"kernelVersion\"`\n\tPrettyDistro   string `json:\"prettyDistro\"`\n\n\tDiskSize int64 `json:\"diskSize\"`\n}\n\ntype NodeCurrent struct {\n\tLoad1            float64 `json:\"load1\"`\n\tLoad5            float64 `json:\"load5\"`\n\tLoad15           float64 `json:\"load15\"`\n\tLoadUsagePercent float64 `json:\"loadUsagePercent\"`\n\n\tCPUUsedPercent     float64   `json:\"cpuUsedPercent\"`\n\tCPUUsed            float64   `json:\"cpuUsed\"`\n\tCPUTotal           int       `json:\"cpuTotal\"`\n\tCPUDetailedPercent []float64 `json:\"cpuDetailedPercent\"`\n\n\tMemoryTotal       uint64  `json:\"memoryTotal\"`\n\tMemoryAvailable   uint64  `json:\"memoryAvailable\"`\n\tMemoryUsed        uint64  `json:\"memoryUsed\"`\n\tMemoryUsedPercent float64 `json:\"memoryUsedPercent\"`\n\n\tSwapMemoryTotal       uint64  `json:\"swapMemoryTotal\"`\n\tSwapMemoryAvailable   uint64  `json:\"swapMemoryAvailable\"`\n\tSwapMemoryUsed        uint64  `json:\"swapMemoryUsed\"`\n\tSwapMemoryUsedPercent float64 `json:\"swapMemoryUsedPercent\"`\n}\n\ntype DashboardCurrent struct {\n\tUptime          uint64 `json:\"uptime\"`\n\tTimeSinceUptime string `json:\"timeSinceUptime\"`\n\n\tProcs uint64 `json:\"procs\"`\n\n\tLoad1            float64 `json:\"load1\"`\n\tLoad5            float64 `json:\"load5\"`\n\tLoad15           float64 `json:\"load15\"`\n\tLoadUsagePercent float64 `json:\"loadUsagePercent\"`\n\n\tCPUPercent         []float64 `json:\"cpuPercent\"`\n\tCPUUsedPercent     float64   `json:\"cpuUsedPercent\"`\n\tCPUUsed            float64   `json:\"cpuUsed\"`\n\tCPUTotal           int       `json:\"cpuTotal\"`\n\tCPUDetailedPercent []float64 `json:\"cpuDetailedPercent\"`\n\n\tMemoryTotal       uint64  `json:\"memoryTotal\"`\n\tMemoryUsed        uint64  `json:\"memoryUsed\"`\n\tMemoryFree        uint64  `json:\"memoryFree\"`\n\tMemoryShard       uint64  `json:\"memoryShard\"`\n\tMemoryCache       uint64  `json:\"memoryCache\"`\n\tMemoryAvailable   uint64  `json:\"memoryAvailable\"`\n\tMemoryUsedPercent float64 `json:\"memoryUsedPercent\"`\n\n\tSwapMemoryTotal       uint64  `json:\"swapMemoryTotal\"`\n\tSwapMemoryAvailable   uint64  `json:\"swapMemoryAvailable\"`\n\tSwapMemoryUsed        uint64  `json:\"swapMemoryUsed\"`\n\tSwapMemoryUsedPercent float64 `json:\"swapMemoryUsedPercent\"`\n\n\tIOReadBytes  uint64 `json:\"ioReadBytes\"`\n\tIOWriteBytes uint64 `json:\"ioWriteBytes\"`\n\tIOCount      uint64 `json:\"ioCount\"`\n\tIOReadTime   uint64 `json:\"ioReadTime\"`\n\tIOWriteTime  uint64 `json:\"ioWriteTime\"`\n\n\tDiskData []DiskInfo `json:\"diskData\"`\n\n\tNetBytesSent uint64 `json:\"netBytesSent\"`\n\tNetBytesRecv uint64 `json:\"netBytesRecv\"`\n\n\tGPUData []GPUInfo `json:\"gpuData\"`\n\tXPUData []XPUInfo `json:\"xpuData\"`\n\n\tTopCPUItems []Process `json:\"topCPUItems\"`\n\tTopMemItems []Process `json:\"topMemItems\"`\n\n\tShotTime time.Time `json:\"shotTime\"`\n}\n\ntype AppLauncherSync struct {\n\tKeys []string `json:\"keys\"`\n}\n\ntype DiskInfo struct {\n\tPath        string  `json:\"path\"`\n\tType        string  `json:\"type\"`\n\tDevice      string  `json:\"device\"`\n\tTotal       uint64  `json:\"total\"`\n\tFree        uint64  `json:\"free\"`\n\tUsed        uint64  `json:\"used\"`\n\tUsedPercent float64 `json:\"usedPercent\"`\n\n\tInodesTotal       uint64  `json:\"inodesTotal\"`\n\tInodesUsed        uint64  `json:\"inodesUsed\"`\n\tInodesFree        uint64  `json:\"inodesFree\"`\n\tInodesUsedPercent float64 `json:\"inodesUsedPercent\"`\n}\n\ntype GPUInfo struct {\n\tIndex            uint   `json:\"index\"`\n\tProductName      string `json:\"productName\"`\n\tGPUUtil          string `json:\"gpuUtil\"`\n\tTemperature      string `json:\"temperature\"`\n\tPerformanceState string `json:\"performanceState\"`\n\tPowerUsage       string `json:\"powerUsage\"`\n\tPowerDraw        string `json:\"powerDraw\"`\n\tMaxPowerLimit    string `json:\"maxPowerLimit\"`\n\tMemoryUsage      string `json:\"memoryUsage\"`\n\tMemUsed          string `json:\"memUsed\"`\n\tMemTotal         string `json:\"memTotal\"`\n\tFanSpeed         string `json:\"fanSpeed\"`\n}\n\ntype AppLauncher struct {\n\tKey         string `json:\"key\"`\n\tType        string `json:\"type\"`\n\tName        string `json:\"name\"`\n\tIcon        string `json:\"icon\"`\n\tLimit       int    `json:\"limit\"`\n\tDescription string `json:\"description\"`\n\tRecommend   int    `json:\"recommend\"`\n\n\tIsInstall   bool            `json:\"isInstall\"`\n\tIsRecommend bool            `json:\"isRecommend\"`\n\tDetail      []InstallDetail `json:\"detail\"`\n}\n\ntype InstallDetail struct {\n\tInstallID uint   `json:\"installID\"`\n\tDetailID  uint   `json:\"detailID\"`\n\tName      string `json:\"name\"`\n\tVersion   string `json:\"version\"`\n\tPath      string `json:\"path\"`\n\tStatus    string `json:\"status\"`\n\tWebUI     string `json:\"webUI\"`\n\tHttpPort  int    `json:\"httpPort\"`\n\tHttpsPort int    `json:\"httpsPort\"`\n}\n\ntype LauncherOption struct {\n\tKey    string `json:\"key\"`\n\tIsShow bool   `json:\"isShow\"`\n}\n\ntype XPUInfo struct {\n\tDeviceID    int    `json:\"deviceID\"`\n\tDeviceName  string `json:\"deviceName\"`\n\tMemory      string `json:\"memory\"`\n\tTemperature string `json:\"temperature\"`\n\tMemoryUsed  string `json:\"memoryUsed\"`\n\tPower       string `json:\"power\"`\n\tMemoryUtil  string `json:\"memoryUtil\"`\n}\n"
  },
  {
    "path": "agent/app/dto/database.go",
    "content": "package dto\n\nimport \"time\"\n\n// common\ntype DBConfUpdateByFile struct {\n\tType     string `json:\"type\" validate:\"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster\"`\n\tDatabase string `json:\"database\" validate:\"required\"`\n\tFile     string `json:\"file\"`\n}\ntype ChangeDBInfo struct {\n\tID       uint   `json:\"id\"`\n\tFrom     string `json:\"from\" validate:\"required,oneof=local remote\"`\n\tType     string `json:\"type\" validate:\"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster\"`\n\tDatabase string `json:\"database\" validate:\"required\"`\n\tValue    string `json:\"value\" validate:\"required\"`\n}\n\ntype DBBaseInfo struct {\n\tName          string `json:\"name\"`\n\tContainerName string `json:\"containerName\"`\n\tPort          int64  `json:\"port\"`\n}\n\n// mysql\ntype MysqlDBSearch struct {\n\tPageInfo\n\tInfo     string `json:\"info\"`\n\tDatabase string `json:\"database\" validate:\"required\"`\n\tOrderBy  string `json:\"orderBy\" validate:\"required,oneof=name createdAt\"`\n\tOrder    string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n}\n\ntype MysqlDBInfo struct {\n\tID          uint      `json:\"id\"`\n\tCreatedAt   time.Time `json:\"createdAt\"`\n\tName        string    `json:\"name\"`\n\tFrom        string    `json:\"from\"`\n\tMysqlName   string    `json:\"mysqlName\"`\n\tFormat      string    `json:\"format\"`\n\tCollation   string    `json:\"collation\"`\n\tUsername    string    `json:\"username\"`\n\tPassword    string    `json:\"password\"`\n\tPermission  string    `json:\"permission\"`\n\tIsDelete    bool      `json:\"isDelete\"`\n\tDescription string    `json:\"description\"`\n}\n\ntype MysqlOption struct {\n\tID       uint   `json:\"id\"`\n\tFrom     string `json:\"from\"`\n\tType     string `json:\"type\"`\n\tDatabase string `json:\"database\"`\n\tName     string `json:\"name\"`\n}\n\ntype MysqlDBCreate struct {\n\tName        string `json:\"name\" validate:\"required\"`\n\tFrom        string `json:\"from\" validate:\"required,oneof=local remote\"`\n\tDatabase    string `json:\"database\" validate:\"required\"`\n\tFormat      string `json:\"format\" validate:\"required\"`\n\tCollation   string `json:\"collation\"`\n\tUsername    string `json:\"username\" validate:\"required\"`\n\tPassword    string `json:\"password\" validate:\"required\"`\n\tPermission  string `json:\"permission\" validate:\"required\"`\n\tDescription string `json:\"description\"`\n}\n\ntype MysqlFormatCollationOption struct {\n\tFormat     string   `json:\"format\"`\n\tCollations []string `json:\"collations\"`\n}\n\ntype BindUser struct {\n\tDatabase   string `json:\"database\" validate:\"required\"`\n\tDB         string `json:\"db\" validate:\"required\"`\n\tUsername   string `json:\"username\" validate:\"required\"`\n\tPassword   string `json:\"password\" validate:\"required\"`\n\tPermission string `json:\"permission\" validate:\"required\"`\n}\n\ntype MysqlLoadDB struct {\n\tFrom     string `json:\"from\" validate:\"required,oneof=local remote\"`\n\tType     string `json:\"type\" validate:\"required,oneof=mysql mariadb mysql-cluster\"`\n\tDatabase string `json:\"database\" validate:\"required\"`\n}\n\ntype MysqlDBDeleteCheck struct {\n\tID       uint   `json:\"id\" validate:\"required\"`\n\tType     string `json:\"type\" validate:\"required,oneof=mysql mariadb mysql-cluster\"`\n\tDatabase string `json:\"database\" validate:\"required\"`\n}\n\ntype MysqlDBDelete struct {\n\tID           uint   `json:\"id\" validate:\"required\"`\n\tType         string `json:\"type\" validate:\"required,oneof=mysql mariadb mysql-cluster\"`\n\tDatabase     string `json:\"database\" validate:\"required\"`\n\tForceDelete  bool   `json:\"forceDelete\"`\n\tDeleteBackup bool   `json:\"deleteBackup\"`\n}\n\ntype MysqlStatus struct {\n\tAbortedClients               string `json:\"Aborted_clients\"`\n\tAbortedConnects              string `json:\"Aborted_connects\"`\n\tBytesReceived                string `json:\"Bytes_received\"`\n\tBytesSent                    string `json:\"Bytes_sent\"`\n\tComCommit                    string `json:\"Com_commit\"`\n\tComRollback                  string `json:\"Com_rollback\"`\n\tConnections                  string `json:\"Connections\"`\n\tCreatedTmpDiskTables         string `json:\"Created_tmp_disk_tables\"`\n\tCreatedTmpTables             string `json:\"Created_tmp_tables\"`\n\tInnodbBufferPoolPagesDirty   string `json:\"Innodb_buffer_pool_pages_dirty\"`\n\tInnodbBufferPoolReadRequests string `json:\"Innodb_buffer_pool_read_requests\"`\n\tInnodbBufferPoolReads        string `json:\"Innodb_buffer_pool_reads\"`\n\tKeyReadRequests              string `json:\"Key_read_requests\"`\n\tKeyReads                     string `json:\"Key_reads\"`\n\tKeyWriteEequests             string `json:\"Key_write_requests\"`\n\tKeyWrites                    string `json:\"Key_writes\"`\n\tMaxUsedConnections           string `json:\"Max_used_connections\"`\n\tOpenTables                   string `json:\"Open_tables\"`\n\tOpenedFiles                  string `json:\"Opened_files\"`\n\tOpenedTables                 string `json:\"Opened_tables\"`\n\tQcacheHits                   string `json:\"Qcache_hits\"`\n\tQcacheInserts                string `json:\"Qcache_inserts\"`\n\tQuestions                    string `json:\"Questions\"`\n\tSelectFullJoin               string `json:\"Select_full_join\"`\n\tSelectRangeCheck             string `json:\"Select_range_check\"`\n\tSortMergePasses              string `json:\"Sort_merge_passes\"`\n\tTableLocksWaited             string `json:\"Table_locks_waited\"`\n\tThreadsCached                string `json:\"Threads_cached\"`\n\tThreadsConnected             string `json:\"Threads_connected\"`\n\tThreadsCreated               string `json:\"Threads_created\"`\n\tThreadsRunning               string `json:\"Threads_running\"`\n\tUptime                       string `json:\"Uptime\"`\n\tRun                          string `json:\"Run\"`\n\tFile                         string `json:\"File\"`\n\tPosition                     string `json:\"Position\"`\n}\n\ntype MysqlVariables struct {\n\tBinlogCacheSize      string `json:\"binlog_cache_size\"`\n\tInnodbBufferPoolSize string `json:\"innodb_buffer_pool_size\"`\n\tInnodbLogBufferSize  string `json:\"innodb_log_buffer_size\"`\n\tJoinBufferSize       string `json:\"join_buffer_size\"`\n\tKeyBufferSize        string `json:\"key_buffer_size\"`\n\tMaxConnections       string `json:\"max_connections\"`\n\tMaxHeapTableSize     string `json:\"max_heap_table_size\"`\n\tQueryCacheSize       string `json:\"query_cache_size\"`\n\tQueryCacheType       string `json:\"query_cache_type\"`\n\tReadBufferSize       string `json:\"read_buffer_size\"`\n\tReadRndBufferSize    string `json:\"read_rnd_buffer_size\"`\n\tSortBufferSize       string `json:\"sort_buffer_size\"`\n\tTableOpenCache       string `json:\"table_open_cache\"`\n\tThreadCacheSize      string `json:\"thread_cache_size\"`\n\tThreadStack          string `json:\"thread_stack\"`\n\tTmpTableSize         string `json:\"tmp_table_size\"`\n\n\tSlowQueryLog  string `json:\"slow_query_log\"`\n\tLongQueryTime string `json:\"long_query_time\"`\n}\n\ntype MysqlVariablesUpdate struct {\n\tType      string                       `json:\"type\" validate:\"required,oneof=mysql mariadb mysql-cluster\"`\n\tDatabase  string                       `json:\"database\" validate:\"required\"`\n\tVariables []MysqlVariablesUpdateHelper `json:\"variables\"`\n}\n\ntype MysqlVariablesUpdateHelper struct {\n\tParam string      `json:\"param\"`\n\tValue interface{} `json:\"value\"`\n}\n\n// redis\ntype ChangeRedisPass struct {\n\tDatabase string `json:\"database\" validate:\"required\"`\n\tValue    string `json:\"value\"`\n}\n\ntype RedisConfUpdate struct {\n\tDatabase   string `json:\"database\" validate:\"required\"`\n\tTimeout    string `json:\"timeout\"`\n\tMaxclients string `json:\"maxclients\"`\n\tMaxmemory  string `json:\"maxmemory\"`\n\tDBType     string `json:\"dbType\" validate:\"required,oneof=redis redis-cluster\"`\n}\ntype RedisConfPersistenceUpdate struct {\n\tDatabase    string `json:\"database\" validate:\"required\"`\n\tType        string `json:\"type\" validate:\"required,oneof=aof rbd\"`\n\tAppendonly  string `json:\"appendonly\"`\n\tAppendfsync string `json:\"appendfsync\"`\n\tSave        string `json:\"save\"`\n\tDBType      string `json:\"dbType\" validate:\"required,oneof=redis redis-cluster\"`\n}\n\ntype RedisConf struct {\n\tDatabase      string `json:\"database\" validate:\"required\"`\n\tName          string `json:\"name\"`\n\tPort          int64  `json:\"port\"`\n\tContainerName string `json:\"containerName\"`\n\tTimeout       string `json:\"timeout\"`\n\tMaxclients    string `json:\"maxclients\"`\n\tRequirepass   string `json:\"requirepass\"`\n\tMaxmemory     string `json:\"maxmemory\"`\n}\n\ntype RedisPersistence struct {\n\tDatabase    string `json:\"database\" validate:\"required\"`\n\tAppendonly  string `json:\"appendonly\"`\n\tAppendfsync string `json:\"appendfsync\"`\n\tSave        string `json:\"save\"`\n}\n\ntype RedisStatus struct {\n\tDatabase                 string `json:\"database\" validate:\"required\"`\n\tTcpPort                  string `json:\"tcp_port\"`\n\tUptimeInDays             string `json:\"uptime_in_days\"`\n\tConnectedClients         string `json:\"connected_clients\"`\n\tUsedMemory               string `json:\"used_memory\"`\n\tUsedMemoryRss            string `json:\"used_memory_rss\"`\n\tUsedMemoryPeak           string `json:\"used_memory_peak\"`\n\tMemFragmentationRatio    string `json:\"mem_fragmentation_ratio\"`\n\tTotalConnectionsReceived string `json:\"total_connections_received\"`\n\tTotalCommandsProcessed   string `json:\"total_commands_processed\"`\n\tInstantaneousOpsPerSec   string `json:\"instantaneous_ops_per_sec\"`\n\tKeyspaceHits             string `json:\"keyspace_hits\"`\n\tKeyspaceMisses           string `json:\"keyspace_misses\"`\n\tLatestForkUsec           string `json:\"latest_fork_usec\"`\n}\n\ntype DatabaseFileRecords struct {\n\tDatabase  string `json:\"database\" validate:\"required\"`\n\tFileName  string `json:\"fileName\"`\n\tFileDir   string `json:\"fileDir\"`\n\tCreatedAt string `json:\"createdAt\"`\n\tSize      int    `json:\"size\"`\n}\ntype RedisBackupRecover struct {\n\tDatabase string `json:\"database\" validate:\"required\"`\n\tFileName string `json:\"fileName\"`\n\tFileDir  string `json:\"fileDir\"`\n}\n\n// database\ntype DatabaseSearch struct {\n\tPageInfo\n\tInfo    string `json:\"info\"`\n\tType    string `json:\"type\"`\n\tOrderBy string `json:\"orderBy\" validate:\"required,oneof=name createdAt\"`\n\tOrder   string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n}\n\ntype DatabaseInfo struct {\n\tID        uint      `json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tName      string    `json:\"name\" validate:\"max=256\"`\n\tFrom      string    `json:\"from\"`\n\tType      string    `json:\"type\"`\n\tVersion   string    `json:\"version\"`\n\tAddress   string    `json:\"address\"`\n\tPort      uint      `json:\"port\"`\n\tInitialDB string    `json:\"initialDB\"`\n\tUsername  string    `json:\"username\"`\n\tPassword  string    `json:\"password\"`\n\n\tSSL        bool   `json:\"ssl\"`\n\tRootCert   string `json:\"rootCert\"`\n\tClientKey  string `json:\"clientKey\"`\n\tClientCert string `json:\"clientCert\"`\n\tSkipVerify bool   `json:\"skipVerify\"`\n\n\tTimeout     uint   `json:\"timeout\"`\n\tDescription string `json:\"description\"`\n}\n\ntype DatabaseOption struct {\n\tID       uint   `json:\"id\"`\n\tType     string `json:\"type\"`\n\tFrom     string `json:\"from\"`\n\tDatabase string `json:\"database\"`\n\tVersion  string `json:\"version\"`\n\tAddress  string `json:\"address\"`\n}\n\ntype DatabaseItem struct {\n\tID       uint   `json:\"id\"`\n\tFrom     string `json:\"from\"`\n\tDatabase string `json:\"database\"`\n\tName     string `json:\"name\"`\n}\n\ntype DatabaseCreate struct {\n\tName      string `json:\"name\" validate:\"required,max=256\"`\n\tType      string `json:\"type\" validate:\"required\"`\n\tFrom      string `json:\"from\" validate:\"required,oneof=local remote\"`\n\tVersion   string `json:\"version\" validate:\"required\"`\n\tAddress   string `json:\"address\"`\n\tPort      uint   `json:\"port\"`\n\tInitialDB string `json:\"initialDB\"`\n\tUsername  string `json:\"username\" validate:\"required\"`\n\tPassword  string `json:\"password\"`\n\n\tSSL        bool   `json:\"ssl\"`\n\tRootCert   string `json:\"rootCert\"`\n\tClientKey  string `json:\"clientKey\"`\n\tClientCert string `json:\"clientCert\"`\n\tSkipVerify bool   `json:\"skipVerify\"`\n\n\tTimeout     uint   `json:\"timeout\"`\n\tDescription string `json:\"description\"`\n}\n\ntype DatabaseUpdate struct {\n\tID        uint   `json:\"id\"`\n\tType      string `json:\"type\" validate:\"required\"`\n\tVersion   string `json:\"version\" validate:\"required\"`\n\tAddress   string `json:\"address\"`\n\tPort      uint   `json:\"port\"`\n\tInitialDB string `json:\"initialDB\"`\n\tUsername  string `json:\"username\" validate:\"required\"`\n\tPassword  string `json:\"password\"`\n\n\tSSL        bool   `json:\"ssl\"`\n\tRootCert   string `json:\"rootCert\"`\n\tClientKey  string `json:\"clientKey\"`\n\tClientCert string `json:\"clientCert\"`\n\tSkipVerify bool   `json:\"skipVerify\"`\n\n\tTimeout     uint   `json:\"timeout\"`\n\tDescription string `json:\"description\"`\n}\n\ntype DatabaseDelete struct {\n\tID           uint `json:\"id\" validate:\"required\"`\n\tForceDelete  bool `json:\"forceDelete\"`\n\tDeleteBackup bool `json:\"deleteBackup\"`\n}\n\ntype LoadRedisStatus struct {\n\tName string `json:\"name\" validate:\"required\"`\n\tType string `json:\"type\" validate:\"required\"`\n}\n\ntype DBResource struct {\n\tType string `json:\"type\"`\n\tName string `json:\"name\"`\n}\n"
  },
  {
    "path": "agent/app/dto/database_postgresql.go",
    "content": "package dto\n\nimport \"time\"\n\ntype PostgresqlDBSearch struct {\n\tPageInfo\n\tInfo     string `json:\"info\"`\n\tDatabase string `json:\"database\" validate:\"required\"`\n\tOrderBy  string `json:\"orderBy\" validate:\"required,oneof=name createdAt\"`\n\tOrder    string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n}\n\ntype PostgresqlDBInfo struct {\n\tID             uint      `json:\"id\"`\n\tCreatedAt      time.Time `json:\"createdAt\"`\n\tName           string    `json:\"name\"`\n\tFrom           string    `json:\"from\"`\n\tPostgresqlName string    `json:\"postgresqlName\"`\n\tFormat         string    `json:\"format\"`\n\tUsername       string    `json:\"username\"`\n\tPassword       string    `json:\"password\"`\n\tSuperUser      bool      `json:\"superUser\"`\n\tIsDelete       bool      `json:\"isDelete\"`\n\tDescription    string    `json:\"description\"`\n}\n\ntype PostgresqlOption struct {\n\tID       uint   `json:\"id\"`\n\tFrom     string `json:\"from\"`\n\tType     string `json:\"type\"`\n\tDatabase string `json:\"database\"`\n\tName     string `json:\"name\"`\n}\n\ntype PostgresqlDBCreate struct {\n\tName        string `json:\"name\" validate:\"required\"`\n\tFrom        string `json:\"from\" validate:\"required,oneof=local remote\"`\n\tDatabase    string `json:\"database\" validate:\"required\"`\n\tFormat      string `json:\"format\"`\n\tUsername    string `json:\"username\" validate:\"required\"`\n\tPassword    string `json:\"password\" validate:\"required\"`\n\tSuperUser   bool   `json:\"superUser\"`\n\tDescription string `json:\"description\"`\n}\n\ntype PostgresqlBindUser struct {\n\tName      string `json:\"name\" validate:\"required\"`\n\tDatabase  string `json:\"database\" validate:\"required\"`\n\tUsername  string `json:\"username\" validate:\"required\"`\n\tPassword  string `json:\"password\" validate:\"required\"`\n\tSuperUser bool   `json:\"superUser\"`\n}\n\ntype PostgresqlPrivileges struct {\n\tName      string `json:\"name\" validate:\"required\"`\n\tDatabase  string `json:\"database\" validate:\"required\"`\n\tUsername  string `json:\"username\" validate:\"required\"`\n\tSuperUser bool   `json:\"superUser\"`\n}\n\ntype PostgresqlLoadDB struct {\n\tFrom     string `json:\"from\" validate:\"required,oneof=local remote\"`\n\tType     string `json:\"type\" validate:\"required,oneof=postgresql postgresql-cluster\"`\n\tDatabase string `json:\"database\" validate:\"required\"`\n}\n\ntype PostgresqlDBDeleteCheck struct {\n\tID       uint   `json:\"id\" validate:\"required\"`\n\tType     string `json:\"type\" validate:\"required,oneof=postgresql postgresql-cluster\"`\n\tDatabase string `json:\"database\" validate:\"required\"`\n}\n\ntype PostgresqlDBDelete struct {\n\tID           uint   `json:\"id\" validate:\"required\"`\n\tType         string `json:\"type\" validate:\"required,oneof=postgresql postgresql-cluster\"`\n\tDatabase     string `json:\"database\" validate:\"required\"`\n\tForceDelete  bool   `json:\"forceDelete\"`\n\tDeleteBackup bool   `json:\"deleteBackup\"`\n}\n"
  },
  {
    "path": "agent/app/dto/device.go",
    "content": "package dto\n\ntype DeviceBaseInfo struct {\n\tDNS       []string     `json:\"dns\"`\n\tHosts     []HostHelper `json:\"hosts\"`\n\tHostname  string       `json:\"hostname\"`\n\tTimeZone  string       `json:\"timeZone\"`\n\tLocalTime string       `json:\"localTime\"`\n\tNtp       string       `json:\"ntp\"`\n\tUser      string       `json:\"user\"`\n\n\tSwapMemoryTotal     uint64 `json:\"swapMemoryTotal\"`\n\tSwapMemoryAvailable uint64 `json:\"swapMemoryAvailable\"`\n\tSwapMemoryUsed      uint64 `json:\"swapMemoryUsed\"`\n\tMaxSize             uint64 `json:\"maxSize\"`\n\n\tSwapDetails []SwapHelper `json:\"swapDetails\"`\n}\n\ntype HostHelper struct {\n\tIP   string `json:\"ip\"`\n\tHost string `json:\"host\"`\n}\n\ntype SwapHelper struct {\n\tPath string `json:\"path\" validate:\"required\"`\n\tSize uint64 `json:\"size\"`\n\tUsed string `json:\"used\"`\n\n\tIsNew  bool   `json:\"isNew\"`\n\tTaskID string `json:\"taskID\"`\n}\n\ntype TimeZoneOptions struct {\n\tFrom  string   `json:\"from\"`\n\tZones []string `json:\"zones\"`\n}\n\ntype ChangePasswd struct {\n\tUser   string `json:\"user\"`\n\tPasswd string `json:\"passwd\"`\n}\n"
  },
  {
    "path": "agent/app/dto/disk.go",
    "content": "package dto\n\ntype LsblkDevice struct {\n\tName       string        `json:\"name\"`\n\tSize       string        `json:\"size\"`\n\tType       string        `json:\"type\"`\n\tMountPoint *string       `json:\"mountpoint\"`\n\tFsType     *string       `json:\"fstype\"`\n\tModel      *string       `json:\"model\"`\n\tSerial     string        `json:\"serial\"`\n\tTran       string        `json:\"tran\"`\n\tRota       bool          `json:\"rota\"`\n\tChildren   []LsblkDevice `json:\"children,omitempty\"`\n}\n\ntype LsblkOutput struct {\n\tBlockDevices []LsblkDevice `json:\"blockdevices\"`\n}\n\ntype DiskFormatRequest struct {\n\tDevice      string `json:\"device\" `\n\tFilesystem  string `json:\"filesystem\" `\n\tLabel       string `json:\"label,omitempty\" `\n\tQuickFormat bool   `json:\"quickFormat,omitempty\"`\n}\n"
  },
  {
    "path": "agent/app/dto/docker.go",
    "content": "package dto\n\ntype DaemonJsonUpdateByFile struct {\n\tFile string `json:\"file\"`\n}\n\ntype DockerStatus struct {\n\tIsActive bool `json:\"isActive\"`\n\tIsExist  bool `json:\"isExist\"`\n}\n\ntype DaemonJsonConf struct {\n\tIsSwarm      bool     `json:\"isSwarm\"`\n\tVersion      string   `json:\"version\"`\n\tMirrors      []string `json:\"registryMirrors\"`\n\tRegistries   []string `json:\"insecureRegistries\"`\n\tLiveRestore  bool     `json:\"liveRestore\"`\n\tIPTables     bool     `json:\"iptables\"`\n\tCgroupDriver string   `json:\"cgroupDriver\"`\n\n\tIpv6         bool   `json:\"ipv6\"`\n\tFixedCidrV6  string `json:\"fixedCidrV6\"`\n\tIp6Tables    bool   `json:\"ip6Tables\"`\n\tExperimental bool   `json:\"experimental\"`\n\n\tLogMaxSize string `json:\"logMaxSize\"`\n\tLogMaxFile string `json:\"logMaxFile\"`\n}\n\ntype LogOption struct {\n\tLogMaxSize string `json:\"logMaxSize\"`\n\tLogMaxFile string `json:\"logMaxFile\"`\n}\n\ntype Ipv6Option struct {\n\tFixedCidrV6  string `json:\"fixedCidrV6\"`\n\tIp6Tables    bool   `json:\"ip6Tables\" validate:\"required\"`\n\tExperimental bool   `json:\"experimental\"`\n}\n\ntype DockerOperation struct {\n\tOperation string `json:\"operation\" validate:\"required,oneof=start restart stop\"`\n}\n"
  },
  {
    "path": "agent/app/dto/fail2ban.go",
    "content": "package dto\n\ntype Fail2BanBaseInfo struct {\n\tIsEnable bool   `json:\"isEnable\"`\n\tIsActive bool   `json:\"isActive\"`\n\tIsExist  bool   `json:\"isExist\"`\n\tVersion  string `json:\"version\"`\n\n\tPort      int    `json:\"port\"`\n\tMaxRetry  int    `json:\"maxRetry\"`\n\tBanTime   string `json:\"banTime\"`\n\tFindTime  string `json:\"findTime\"`\n\tBanAction string `json:\"banAction\"`\n\tLogPath   string `json:\"logPath\"`\n}\n\ntype Fail2BanSearch struct {\n\tStatus string `json:\"status\" validate:\"required,oneof=banned ignore\"`\n}\n\ntype Fail2BanUpdate struct {\n\tKey   string `json:\"key\" validate:\"required,oneof=port bantime findtime maxretry banaction logpath port\"`\n\tValue string `json:\"value\"`\n}\n\ntype Fail2BanSet struct {\n\tIPs     []string `json:\"ips\"`\n\tOperate string   `json:\"operate\"  validate:\"required,oneof=banned ignore\"`\n}\n"
  },
  {
    "path": "agent/app/dto/file.go",
    "content": "package dto\n\ntype LogFileRes struct {\n\tLines       []string `json:\"lines\"`\n\tIsEndOfFile bool     `json:\"isEndOfFile\"`\n\tTotalPages  int      `json:\"totalPages\"`\n\tTotalLines  int      `json:\"totalLines\"`\n}\n"
  },
  {
    "path": "agent/app/dto/firewall.go",
    "content": "package dto\n\ntype FirewallBaseInfo struct {\n\tName       string `json:\"name\"`\n\tIsExist    bool   `json:\"isExist\"`\n\tIsActive   bool   `json:\"isActive\"`\n\tIsInit     bool   `json:\"isInit\"`\n\tIsBind     bool   `json:\"isBind\"`\n\tVersion    string `json:\"version\"`\n\tPingStatus string `json:\"pingStatus\"`\n}\n\ntype RuleSearch struct {\n\tPageInfo\n\tInfo     string `json:\"info\"`\n\tStatus   string `json:\"status\"`\n\tStrategy string `json:\"strategy\"`\n\tType     string `json:\"type\" validate:\"required\"`\n}\n\ntype FirewallOperation struct {\n\tOperation         string `json:\"operation\" validate:\"required,oneof=start stop restart disableBanPing enableBanPing\"`\n\tWithDockerRestart bool   `json:\"withDockerRestart\"`\n}\n\ntype PortRuleOperate struct {\n\tID        uint   `json:\"id\"`\n\tOperation string `json:\"operation\" validate:\"required,oneof=add remove\"`\n\tChain     string `json:\"chain\"`\n\tAddress   string `json:\"address\"`\n\tPort      string `json:\"port\" validate:\"required\"`\n\tProtocol  string `json:\"protocol\" validate:\"required,oneof=tcp udp tcp/udp\"`\n\tStrategy  string `json:\"strategy\" validate:\"required,oneof=accept drop\"`\n\n\tDescription string `json:\"description\"`\n}\n\ntype ForwardRuleOperate struct {\n\tForceDelete bool `json:\"forceDelete\"`\n\tRules       []struct {\n\t\tOperation  string `json:\"operation\" validate:\"required,oneof=add remove\"`\n\t\tNum        string `json:\"num\"`\n\t\tProtocol   string `json:\"protocol\" validate:\"required,oneof=tcp udp tcp/udp\"`\n\t\tInterface  string `json:\"interface\"`\n\t\tPort       string `json:\"port\" validate:\"required\"`\n\t\tTargetIP   string `json:\"targetIP\"`\n\t\tTargetPort string `json:\"targetPort\" validate:\"required\"`\n\t} `json:\"rules\"`\n}\n\ntype UpdateFirewallDescription struct {\n\tType     string `json:\"type\"`\n\tChain    string `json:\"chain\"`\n\tSrcIP    string `json:\"srcIP\"`\n\tDstIP    string `json:\"dstIP\"`\n\tSrcPort  string `json:\"srcPort\"`\n\tDstPort  string `json:\"dstPort\"`\n\tProtocol string `json:\"protocol\"`\n\tStrategy string `json:\"strategy\" validate:\"required,oneof=accept drop\"`\n\n\tDescription string `json:\"description\"`\n}\n\ntype AddrRuleOperate struct {\n\tID        uint   `json:\"id\"`\n\tOperation string `json:\"operation\" validate:\"required,oneof=add remove\"`\n\tAddress   string `json:\"address\"  validate:\"required\"`\n\tStrategy  string `json:\"strategy\" validate:\"required,oneof=accept drop\"`\n\n\tDescription string `json:\"description\"`\n}\n\ntype PortRuleUpdate struct {\n\tOldRule PortRuleOperate `json:\"oldRule\"`\n\tNewRule PortRuleOperate `json:\"newRule\"`\n}\n\ntype AddrRuleUpdate struct {\n\tOldRule AddrRuleOperate `json:\"oldRule\"`\n\tNewRule AddrRuleOperate `json:\"newRule\"`\n}\n\ntype BatchRuleOperate struct {\n\tType  string            `json:\"type\" validate:\"required\"`\n\tRules []PortRuleOperate `json:\"rules\"`\n}\n\ntype IptablesOp struct {\n\tName    string `json:\"name\" validate:\"required,oneof=1PANEL_INPUT 1PANEL_OUTPUT 1PANEL_BASIC\"`\n\tOperate string `json:\"operate\" validate:\"required,oneof=init-base init-forward init-advance bind-base unbind-base bind unbind\"`\n}\n\ntype IptablesRuleOp struct {\n\tOperation   string `json:\"operation\" validate:\"required,oneof=add remove\"`\n\tID          uint   `json:\"id\"`\n\tChain       string `json:\"chain\" validate:\"required,oneof=1PANEL_BASIC 1PANEL_BASIC_BEFORE 1PANEL_INPUT 1PANEL_OUTPUT\"`\n\tProtocol    string `json:\"protocol\"`\n\tSrcIP       string `json:\"srcIP\"`\n\tSrcPort     uint   `json:\"srcPort\"`\n\tDstIP       string `json:\"dstIP\"`\n\tDstPort     uint   `json:\"dstPort\"`\n\tStrategy    string `json:\"strategy\" validate:\"required,oneof=accept drop reject\"`\n\tDescription string `json:\"description\"`\n}\n\ntype IptablesBatchOperate struct {\n\tRules []IptablesRuleOp `json:\"rules\"`\n}\n\ntype IptablesChainStatus struct {\n\tIsBind          bool   `json:\"isBind\"`\n\tDefaultStrategy string `json:\"defaultStrategy\"`\n}\n"
  },
  {
    "path": "agent/app/dto/ftp.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n)\n\ntype FtpInfo struct {\n\tID        uint      `json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\n\tUser        string `json:\"user\"`\n\tPassword    string `json:\"password\"`\n\tPath        string `json:\"path\"`\n\tStatus      string `json:\"status\"`\n\tDescription string `json:\"description\"`\n}\n\ntype FtpBaseInfo struct {\n\tIsActive bool `json:\"isActive\"`\n\tIsExist  bool `json:\"isExist\"`\n}\n\ntype FtpLogSearch struct {\n\tPageInfo\n\tUser      string `json:\"user\"`\n\tOperation string `json:\"operation\"`\n}\n\ntype FtpCreate struct {\n\tUser        string `json:\"user\" validate:\"required\"`\n\tPassword    string `json:\"password\" validate:\"required\"`\n\tPath        string `json:\"path\" validate:\"required\"`\n\tDescription string `json:\"description\"`\n}\n\ntype FtpUpdate struct {\n\tID uint `json:\"id\"`\n\n\tPassword    string `json:\"password\" validate:\"required\"`\n\tPath        string `json:\"path\" validate:\"required\"`\n\tStatus      string `json:\"status\"`\n\tDescription string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/dto/group.go",
    "content": "package dto\n\ntype GroupCreate struct {\n\tID   uint   `json:\"id\"`\n\tName string `json:\"name\" validate:\"required\"`\n\tType string `json:\"type\" validate:\"required\"`\n}\n\ntype GroupSearch struct {\n\tType string `json:\"type\" validate:\"required\"`\n}\n\ntype GroupUpdate struct {\n\tID        uint   `json:\"id\"`\n\tName      string `json:\"name\"`\n\tType      string `json:\"type\" validate:\"required\"`\n\tIsDefault bool   `json:\"isDefault\"`\n}\n\ntype GroupInfo struct {\n\tID        uint   `json:\"id\"`\n\tName      string `json:\"name\"`\n\tType      string `json:\"type\"`\n\tIsDefault bool   `json:\"isDefault\"`\n}\n\ntype OperateByType struct {\n\tType string `json:\"type\"`\n}\n"
  },
  {
    "path": "agent/app/dto/host.go",
    "content": "package dto\n\nimport \"time\"\n\ntype HostOperate struct {\n\tID               uint   `json:\"id\"`\n\tGroupID          uint   `json:\"groupID\"`\n\tName             string `json:\"name\"`\n\tAddr             string `json:\"addr\" validate:\"required\"`\n\tPort             uint   `json:\"port\" validate:\"required,number,max=65535,min=1\"`\n\tUser             string `json:\"user\" validate:\"required\"`\n\tAuthMode         string `json:\"authMode\" validate:\"oneof=password key\"`\n\tPassword         string `json:\"password\"`\n\tPrivateKey       string `json:\"privateKey\"`\n\tPassPhrase       string `json:\"passPhrase\"`\n\tRememberPassword bool   `json:\"rememberPassword\"`\n\n\tDescription string `json:\"description\"`\n}\n\ntype HostConnTest struct {\n\tAddr       string `json:\"addr\" validate:\"required\"`\n\tPort       uint   `json:\"port\" validate:\"required,number,max=65535,min=1\"`\n\tUser       string `json:\"user\" validate:\"required\"`\n\tAuthMode   string `json:\"authMode\" validate:\"oneof=password key\"`\n\tPassword   string `json:\"password\"`\n\tPrivateKey string `json:\"privateKey\"`\n\tPassPhrase string `json:\"passPhrase\"`\n}\n\ntype SearchForTree struct {\n\tInfo string `json:\"info\"`\n}\n\ntype HostInfo struct {\n\tID               uint      `json:\"id\"`\n\tCreatedAt        time.Time `json:\"createdAt\"`\n\tGroupID          uint      `json:\"groupID\"`\n\tGroupBelong      string    `json:\"groupBelong\"`\n\tName             string    `json:\"name\"`\n\tAddr             string    `json:\"addr\"`\n\tPort             uint      `json:\"port\"`\n\tUser             string    `json:\"user\"`\n\tAuthMode         string    `json:\"authMode\"`\n\tPassword         string    `json:\"password\"`\n\tPrivateKey       string    `json:\"privateKey\"`\n\tPassPhrase       string    `json:\"passPhrase\"`\n\tRememberPassword bool      `json:\"rememberPassword\"`\n\n\tDescription string `json:\"description\"`\n}\n\ntype HostTree struct {\n\tID       uint        `json:\"id\"`\n\tLabel    string      `json:\"label\"`\n\tChildren []TreeChild `json:\"children\"`\n}\n\ntype TreeChild struct {\n\tID    uint   `json:\"id\"`\n\tLabel string `json:\"label\"`\n}\n"
  },
  {
    "path": "agent/app/dto/image.go",
    "content": "package dto\n\nimport \"time\"\n\ntype PageImage struct {\n\tPageInfo\n\tName    string `json:\"name\"`\n\tOrderBy string `json:\"orderBy\" validate:\"required,oneof=size tags createdAt isUsed\"`\n\tOrder   string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n}\n\ntype ImageInfo struct {\n\tID        string    `json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tIsUsed    bool      `json:\"isUsed\"`\n\tTags      []string  `json:\"tags\"`\n\tSize      int64     `json:\"size\"`\n\n\tIsPinned    bool   `json:\"isPinned\"`\n\tDescription string `json:\"description\"`\n}\n\ntype ImageLoad struct {\n\tTaskID string   `json:\"taskID\"`\n\tPaths  []string `json:\"paths\" validate:\"required,dive,required\"`\n}\n\ntype ImageBuild struct {\n\tTaskID     string   `json:\"taskID\"`\n\tFrom       string   `json:\"from\" validate:\"required\"`\n\tName       string   `json:\"name\" validate:\"required\"`\n\tDockerfile string   `json:\"dockerfile\" validate:\"required\"`\n\tTags       []string `json:\"tags\"`\n\tArgs       []string `json:\"args\"`\n}\n\ntype ImagePull struct {\n\tTaskID    string   `json:\"taskID\"`\n\tRepoID    uint     `json:\"repoID\"`\n\tImageName []string `json:\"imageName\" validate:\"required\"`\n}\n\ntype ImageTag struct {\n\tSourceID string   `json:\"sourceID\" validate:\"required\"`\n\tTags     []string `json:\"tags\" validate:\"required\"`\n}\n\ntype ImagePush struct {\n\tTaskID  string `json:\"taskID\"`\n\tRepoID  uint   `json:\"repoID\" validate:\"required\"`\n\tTagName string `json:\"tagName\" validate:\"required\"`\n\tName    string `json:\"name\" validate:\"required\"`\n}\n\ntype ImageSave struct {\n\tTaskID  string `json:\"taskID\"`\n\tTagName string `json:\"tagName\" validate:\"required\"`\n\tPath    string `json:\"path\" validate:\"required\"`\n\tName    string `json:\"name\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/image_repo.go",
    "content": "package dto\n\nimport \"time\"\n\ntype ImageRepoCreate struct {\n\tName        string `json:\"name\" validate:\"required\"`\n\tDownloadUrl string `json:\"downloadUrl\"`\n\tProtocol    string `json:\"protocol\"`\n\tUsername    string `json:\"username\"`\n\tPassword    string `json:\"password\"`\n\tAuth        bool   `json:\"auth\"`\n}\n\ntype ImageRepoUpdate struct {\n\tID          uint   `json:\"id\"`\n\tDownloadUrl string `json:\"downloadUrl\"`\n\tProtocol    string `json:\"protocol\"`\n\tUsername    string `json:\"username\"`\n\tPassword    string `json:\"password\"`\n\tAuth        bool   `json:\"auth\"`\n}\n\ntype ImageRepoInfo struct {\n\tID          uint      `json:\"id\"`\n\tCreatedAt   time.Time `json:\"createdAt\"`\n\tName        string    `json:\"name\"`\n\tDownloadUrl string    `json:\"downloadUrl\"`\n\tProtocol    string    `json:\"protocol\"`\n\tUsername    string    `json:\"username\"`\n\tAuth        bool      `json:\"auth\"`\n\n\tStatus  string `json:\"status\"`\n\tMessage string `json:\"message\"`\n}\n\ntype ImageRepoOption struct {\n\tID          uint   `json:\"id\"`\n\tName        string `json:\"name\"`\n\tDownloadUrl string `json:\"downloadUrl\"`\n}\n\ntype ImageRepoDelete struct {\n\tIds []uint `json:\"ids\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/logs.go",
    "content": "package dto\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype SearchTaskLogReq struct {\n\tStatus string `json:\"status\"`\n\tType   string `json:\"type\"`\n\tPageInfo\n}\n\ntype TaskDTO struct {\n\tmodel.Task\n}\n"
  },
  {
    "path": "agent/app/dto/monitor.go",
    "content": "package dto\n\nimport \"time\"\n\ntype MonitorSearch struct {\n\tParam     string    `json:\"param\" validate:\"required,oneof=all cpu memory load io network\"`\n\tIO        string    `json:\"io\"`\n\tNetwork   string    `json:\"network\"`\n\tStartTime time.Time `json:\"startTime\"`\n\tEndTime   time.Time `json:\"endTime\"`\n}\n\ntype MonitorData struct {\n\tParam string        `json:\"param\"`\n\tDate  []time.Time   `json:\"date\"`\n\tValue []interface{} `json:\"value\"`\n}\n\ntype Process struct {\n\tName    string  `json:\"name\"`\n\tPid     int32   `json:\"pid\"`\n\tPercent float64 `json:\"percent\"`\n\tMemory  uint64  `json:\"memory\"`\n\tCmd     string  `json:\"cmd\"`\n\tUser    string  `json:\"user\"`\n}\n\ntype MonitorSetting struct {\n\tMonitorStatus    string `json:\"monitorStatus\"`\n\tMonitorStoreDays string `json:\"monitorStoreDays\"`\n\tMonitorInterval  string `json:\"monitorInterval\"`\n\tDefaultNetwork   string `json:\"defaultNetwork\"`\n\tDefaultIO        string `json:\"defaultIO\"`\n}\n\ntype MonitorSettingUpdate struct {\n\tKey   string `json:\"key\" validate:\"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork DefaultIO\"`\n\tValue string `json:\"value\"`\n}\n\ntype MonitorGPUOptions struct {\n\tGPUType   string         `json:\"gpuType\"`\n\tChartHide []GPUChartHide `json:\"chartHide\"`\n\tOptions   []string       `json:\"options\"`\n}\ntype GPUChartHide struct {\n\tProductName string `json:\"productName\"`\n\tProcess     bool   `json:\"process\"`\n\tGPU         bool   `json:\"gpu\"`\n\tMemory      bool   `json:\"memory\"`\n\tPower       bool   `json:\"power\"`\n\tTemperature bool   `json:\"temperature\"`\n\tSpeed       bool   `json:\"speed\"`\n}\ntype MonitorGPUSearch struct {\n\tProductName string    `json:\"productName\"`\n\tStartTime   time.Time `json:\"startTime\"`\n\tEndTime     time.Time `json:\"endTime\"`\n}\ntype MonitorGPUData struct {\n\tDate             []time.Time `json:\"date\"`\n\tGPUValue         []float64   `json:\"gpuValue\"`\n\tTemperatureValue []float64   `json:\"temperatureValue\"`\n\tPowerTotal       []float64   `json:\"powerTotal\"`\n\tPowerUsed        []float64   `json:\"powerUsed\"`\n\tPowerPercent     []float64   `json:\"powerPercent\"`\n\tMemoryTotal      []float64   `json:\"memoryTotal\"`\n\tMemoryUsed       []float64   `json:\"memoryUsed\"`\n\tMemoryPercent    []float64   `json:\"memoryPercent\"`\n\tSpeedValue       []int       `json:\"speedValue\"`\n\n\tProcessCount []int          `json:\"processCount\"`\n\tGPUProcesses [][]GPUProcess `json:\"gpuProcesses\"`\n}\n\ntype GPUProcess struct {\n\tPid         string `json:\"pid\"`\n\tType        string `json:\"type\"`\n\tProcessName string `json:\"processName\"`\n\tUsedMemory  string `json:\"usedMemory\"`\n}\n"
  },
  {
    "path": "agent/app/dto/nginx.go",
    "content": "package dto\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n)\n\ntype NginxFull struct {\n\tInstall    model.AppInstall\n\tWebsite    model.Website\n\tConfigDir  string\n\tConfigFile string\n\tSiteDir    string\n\tDir        string\n\tRootConfig NginxConfig\n\tSiteConfig NginxConfig\n}\n\ntype NginxConfig struct {\n\tFilePath   string\n\tConfig     *components.Config\n\tOldContent string\n}\n\ntype NginxParam struct {\n\tUpdateScope string\n\tName        string\n\tParams      []string\n}\n\ntype NginxAuth struct {\n\tUsername string `json:\"username\"`\n\tRemark   string `json:\"remark\"`\n}\n\ntype NginxPathAuth struct {\n\tUsername string `json:\"username\"`\n\tRemark   string `json:\"remark\"`\n\tPath     string `json:\"path\"`\n\tName     string `json:\"name\"`\n}\n\ntype NginxKey string\n\nconst (\n\tIndex      NginxKey = \"index\"\n\tLimitConn  NginxKey = \"limit-conn\"\n\tSSL        NginxKey = \"ssl\"\n\tCACHE      NginxKey = \"cache\"\n\tHttpPer    NginxKey = \"http-per\"\n\tProxyCache NginxKey = \"proxy-cache\"\n)\n\nvar ScopeKeyMap = map[NginxKey][]string{\n\tIndex:     {\"index\"},\n\tLimitConn: {\"limit_conn\", \"limit_rate\", \"limit_conn_zone\"},\n\tSSL:       {\"ssl_certificate\", \"ssl_certificate_key\"},\n\tHttpPer:   {\"server_names_hash_bucket_size\", \"client_header_buffer_size\", \"client_max_body_size\", \"keepalive_timeout\", \"gzip\", \"gzip_min_length\", \"gzip_comp_level\"},\n}\n\nvar StaticFileKeyMap = map[NginxKey]struct {\n}{\n\tSSL:        {},\n\tCACHE:      {},\n\tProxyCache: {},\n}\n\ntype NginxUpstream struct {\n\tName      string                `json:\"name\"`\n\tAlgorithm string                `json:\"algorithm\"`\n\tServers   []NginxUpstreamServer `json:\"servers\"`\n\tContent   string                `json:\"content\"`\n}\n\ntype NginxUpstreamServer struct {\n\tServer          string `json:\"server\"`\n\tWeight          int    `json:\"weight\"`\n\tFailTimeout     int    `json:\"failTimeout\"`\n\tFailTimeoutUnit string `json:\"failTimeoutUnit\"`\n\tMaxFails        int    `json:\"maxFails\"`\n\tMaxConns        int    `json:\"maxConns\"`\n\tFlag            string `json:\"flag\"`\n}\n\nvar LBAlgorithms = map[string]struct{}{\"ip_hash\": {}, \"least_conn\": {}}\n\nvar RealIPKeys = map[string]struct{}{\"X-Forwarded-For\": {}, \"X-Real-IP\": {}, \"CF-Connecting-IP\": {}}\n\ntype NginxModule struct {\n\tName     string   `json:\"name\"`\n\tScript   string   `json:\"script\"`\n\tPackages []string `json:\"packages\"`\n\tParams   string   `json:\"params\"`\n\tEnable   bool     `json:\"enable\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/app.go",
    "content": "package request\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n)\n\ntype AppSearch struct {\n\tdto.PageInfo\n\tName            string   `json:\"name\"`\n\tTags            []string `json:\"tags\"`\n\tType            string   `json:\"type\"`\n\tRecommend       bool     `json:\"recommend\"`\n\tResource        string   `json:\"resource\"`\n\tShowCurrentArch bool     `json:\"showCurrentArch\"`\n}\n\ntype AppInstallCreate struct {\n\tAppDetailId uint                   `json:\"appDetailId\" validate:\"required\"`\n\tParams      map[string]interface{} `json:\"params\"`\n\tName        string                 `json:\"name\" validate:\"required\"`\n\tServices    map[string]string      `json:\"services\"`\n\tTaskID      string                 `json:\"taskID\"`\n\n\tAppContainerConfig\n\tNodePushConfig\n}\n\ntype NodePushConfig struct {\n\tNodes    []string `json:\"nodes\"`\n\tPushNode bool     `json:\"pushNode\"`\n\tAppKey   string   `json:\"appKey\"`\n\tVersion  string   `json:\"version\"`\n}\n\ntype AppContainerConfig struct {\n\tAdvanced      bool    `json:\"advanced\"`\n\tCpuQuota      float64 `json:\"cpuQuota\"`\n\tMemoryLimit   float64 `json:\"memoryLimit\"`\n\tMemoryUnit    string  `json:\"memoryUnit\"`\n\tContainerName string  `json:\"containerName\"`\n\tAllowPort     bool    `json:\"allowPort\"`\n\tEditCompose   bool    `json:\"editCompose\"`\n\tDockerCompose string  `json:\"dockerCompose\"`\n\tHostMode      bool    `json:\"hostMode\"`\n\tPullImage     bool    `json:\"pullImage\"`\n\tGpuConfig     bool    `json:\"gpuConfig\"`\n\tWebUI         string  `json:\"webUI\"`\n\tType          string  `json:\"type\"`\n\tSpecifyIP     string  `json:\"specifyIP\"`\n\tRestartPolicy string  `json:\"restartPolicy\" validate:\"omitempty,oneof=always unless-stopped no on-failure\"`\n}\n\ntype AppInstalledSearch struct {\n\tdto.PageInfo\n\tType        string   `json:\"type\"`\n\tName        string   `json:\"name\"`\n\tTags        []string `json:\"tags\"`\n\tUpdate      bool     `json:\"update\"`\n\tUnused      bool     `json:\"unused\"`\n\tAll         bool     `json:\"all\"`\n\tSync        bool     `json:\"sync\"`\n\tCheckUpdate bool     `json:\"checkUpdate\"`\n}\n\ntype AppInstalledInfo struct {\n\tKey  string `json:\"key\" validate:\"required\"`\n\tName string `json:\"name\"`\n}\n\ntype AppBackupSearch struct {\n\tdto.PageInfo\n\tAppInstallID uint `json:\"appInstallID\"`\n}\n\ntype AppBackupDelete struct {\n\tIds []uint `json:\"ids\"`\n}\n\ntype AppInstalledOperate struct {\n\tInstallId     uint                `json:\"installId\" validate:\"required\"`\n\tBackupId      uint                `json:\"backupId\"`\n\tDetailId      uint                `json:\"detailId\"`\n\tOperate       constant.AppOperate `json:\"operate\" validate:\"required\"`\n\tForceDelete   bool                `json:\"forceDelete\"`\n\tDeleteBackup  bool                `json:\"deleteBackup\"`\n\tDeleteDB      bool                `json:\"deleteDB\"`\n\tBackup        bool                `json:\"backup\"`\n\tPullImage     bool                `json:\"pullImage\"`\n\tDockerCompose string              `json:\"dockerCompose\"`\n\tTaskID        string              `json:\"taskID\"`\n\tDeleteImage   bool                `json:\"deleteImage\"`\n\tFavorite      bool                `json:\"favorite\"`\n}\n\ntype AppInstallUpgrade struct {\n\tInstallID     uint   `json:\"installId\"`\n\tDetailID      uint   `json:\"detailId\"`\n\tBackup        bool   `json:\"backup\"`\n\tPullImage     bool   `json:\"pullImage\"`\n\tDockerCompose string `json:\"dockerCompose\"`\n\tTaskID        string `json:\"taskID\"`\n}\n\ntype AppInstallDelete struct {\n\tInstall      model.AppInstall\n\tDeleteBackup bool   `json:\"deleteBackup\"`\n\tForceDelete  bool   `json:\"forceDelete\"`\n\tDeleteDB     bool   `json:\"deleteDB\"`\n\tDeleteImage  bool   `json:\"deleteImage\"`\n\tTaskID       string `json:\"taskID\"`\n}\n\ntype AppInstalledUpdate struct {\n\tInstallId uint                   `json:\"installId\" validate:\"required\"`\n\tParams    map[string]interface{} `json:\"params\" validate:\"required\"`\n\tAppContainerConfig\n}\n\ntype AppConfigUpdate struct {\n\tInstallID uint   `json:\"installID\" validate:\"required\"`\n\tWebUI     string `json:\"webUI\"`\n}\n\ntype AppInstalledIgnoreUpgrade struct {\n\tDetailID uint   `json:\"detailID\"  validate:\"required\"`\n\tOperate  string `json:\"operate\"   validate:\"required,oneof=cancel ignore\"`\n}\n\ntype PortUpdate struct {\n\tKey  string `json:\"key\"`\n\tName string `json:\"name\"`\n\tPort int64  `json:\"port\"`\n}\n\ntype AppUpdateVersion struct {\n\tAppInstallID  uint   `json:\"appInstallID\" validate:\"required\"`\n\tUpdateVersion string `json:\"updateVersion\"`\n}\n\ntype AppInstallSortItem struct {\n\tInstallID uint `json:\"installID\"`\n\tSortOrder int  `json:\"sortOrder\"`\n}\n\ntype AppInstallSort struct {\n\tItems []AppInstallSortItem `json:\"items\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/app_ignore_upgrade.go",
    "content": "package request\n\ntype AppIgnoreUpgradeReq struct {\n\tAppID       uint   `json:\"appID\" validate:\"required\"`\n\tAppDetailID uint   `json:\"appDetailID\"`\n\tScope       string `json:\"scope\" validate:\"required,oneof=all version\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/common.go",
    "content": "package request\n\ntype ReqWithID struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/disk.go",
    "content": "package request\n\ntype DiskPartitionRequest struct {\n\tDevice     string `json:\"device\" validate:\"required\"`\n\tFilesystem string `json:\"filesystem\" validate:\"required,oneof=ext4 xfs\"`\n\tLabel      string `json:\"label\"`\n\tAutoMount  bool   `json:\"autoMount\"`\n\tMountPoint string `json:\"mountPoint\" validate:\"required\"`\n}\n\ntype DiskMountRequest struct {\n\tDevice     string `json:\"device\" validate:\"required\"`\n\tMountPoint string `json:\"mountPoint\" validate:\"required\"`\n\tFilesystem string `json:\"filesystem\" validate:\"required,oneof=ext4 xfs\"`\n\tAutoMount  bool   `json:\"autoMount\"`\n\tNoFail     bool   `json:\"noFail\"`\n}\n\ntype DiskUnmountRequest struct {\n\tMountPoint string `json:\"mountPoint\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/favorite.go",
    "content": "package request\n\ntype FavoriteCreate struct {\n\tPath string `json:\"path\" validate:\"required\"`\n}\n\ntype FavoriteDelete struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/file.go",
    "content": "package request\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\ntype FileOption struct {\n\tfiles.FileOption\n}\n\ntype FileContentReq struct {\n\tPath     string `json:\"path\" validate:\"required\"`\n\tIsDetail bool   `json:\"isDetail\"`\n}\ntype SearchUploadWithPage struct {\n\tdto.PageInfo\n\tPath string `json:\"path\" validate:\"required\"`\n}\n\ntype FileCreate struct {\n\tPath      string `json:\"path\" validate:\"required\"`\n\tContent   string `json:\"content\"`\n\tIsDir     bool   `json:\"isDir\"`\n\tMode      int64  `json:\"mode\"`\n\tIsLink    bool   `json:\"isLink\"`\n\tIsSymlink bool   `json:\"isSymlink\"`\n\tLinkPath  string `json:\"linkPath\"`\n\tSub       bool   `json:\"sub\"`\n}\n\ntype FileRoleReq struct {\n\tPaths []string `json:\"paths\" validate:\"required\"`\n\tMode  int64    `json:\"mode\" validate:\"required\"`\n\tUser  string   `json:\"user\" validate:\"required\"`\n\tGroup string   `json:\"group\" validate:\"required\"`\n\tSub   bool     `json:\"sub\"`\n}\n\ntype FileDelete struct {\n\tPath        string `json:\"path\" validate:\"required\"`\n\tIsDir       bool   `json:\"isDir\"`\n\tForceDelete bool   `json:\"forceDelete\"`\n}\n\ntype FileBatchDelete struct {\n\tPaths []string `json:\"paths\" validate:\"required\"`\n\tIsDir bool     `json:\"isDir\"`\n}\n\ntype FileCompress struct {\n\tFiles   []string `json:\"files\" validate:\"required\"`\n\tDst     string   `json:\"dst\" validate:\"required\"`\n\tType    string   `json:\"type\" validate:\"required\"`\n\tName    string   `json:\"name\" validate:\"required\"`\n\tReplace bool     `json:\"replace\"`\n\tSecret  string   `json:\"secret\"`\n}\n\ntype FileDeCompress struct {\n\tDst    string `json:\"dst\"  validate:\"required\"`\n\tType   string `json:\"type\"  validate:\"required\"`\n\tPath   string `json:\"path\" validate:\"required\"`\n\tSecret string `json:\"secret\"`\n}\n\ntype FileEdit struct {\n\tPath    string `json:\"path\"  validate:\"required\"`\n\tContent string `json:\"content\"`\n}\n\ntype FileRename struct {\n\tOldName string `json:\"oldName\" validate:\"required\"`\n\tNewName string `json:\"newName\" validate:\"required\"`\n}\n\ntype FilePathCheck struct {\n\tPath     string `json:\"path\" validate:\"required\"`\n\tWithInit bool   `json:\"withInit\"`\n}\n\ntype FilePathsCheck struct {\n\tPaths []string `json:\"paths\" validate:\"required\"`\n}\n\ntype FileWget struct {\n\tUrl               string `json:\"url\" validate:\"required\"`\n\tPath              string `json:\"path\" validate:\"required\"`\n\tName              string `json:\"name\" validate:\"required\"`\n\tIgnoreCertificate bool   `json:\"ignoreCertificate\"`\n}\n\ntype FileMove struct {\n\tType       string   `json:\"type\" validate:\"required\"`\n\tOldPaths   []string `json:\"oldPaths\" validate:\"required\"`\n\tNewPath    string   `json:\"newPath\" validate:\"required\"`\n\tName       string   `json:\"name\"`\n\tCover      bool     `json:\"cover\"`\n\tCoverPaths []string `json:\"coverPaths\"`\n}\n\ntype FileDownload struct {\n\tPaths    []string `json:\"paths\" validate:\"required\"`\n\tType     string   `json:\"type\" validate:\"required\"`\n\tName     string   `json:\"name\" validate:\"required\"`\n\tCompress bool     `json:\"compress\"`\n}\n\ntype FileChunkDownload struct {\n\tPath string `json:\"path\" validate:\"required\"`\n\tName string `json:\"name\" validate:\"required\"`\n}\n\ntype DirSizeReq struct {\n\tPath string `json:\"path\" validate:\"required\"`\n}\n\ntype FileProcessReq struct {\n\tKey string `json:\"key\"`\n}\n\ntype FileRoleUpdate struct {\n\tPath  string `json:\"path\" validate:\"required\"`\n\tUser  string `json:\"user\" validate:\"required\"`\n\tGroup string `json:\"group\" validate:\"required\"`\n\tSub   bool   `json:\"sub\"`\n}\n\ntype FileReadByLineReq struct {\n\tPage     int    `json:\"page\" validate:\"required\"`\n\tPageSize int    `json:\"pageSize\" validate:\"required\"`\n\tType     string `json:\"type\" validate:\"required\"`\n\tID       uint   `json:\"ID\"`\n\tName     string `json:\"name\"`\n\tLatest   bool   `json:\"latest\"`\n\tTaskReq\n}\n\ntype TaskReq struct {\n\tTaskID      string `json:\"taskID\"`\n\tTaskType    string `json:\"taskType\"`\n\tTaskOperate string `json:\"taskOperate\"`\n\tResourceID  uint   `json:\"resourceID\"`\n}\n\ntype FileExistReq struct {\n\tName string `json:\"name\" validate:\"required\"`\n\tDir  string `json:\"dir\" validate:\"required\"`\n}\n\ntype FileConvert struct {\n\tPath         string `json:\"path\" validate:\"required\"`\n\tType         string `json:\"type\" validate:\"required\"`\n\tInputFile    string `json:\"inputFile\" validate:\"required\"`\n\tExtension    string `json:\"extension\" validate:\"required\"`\n\tOutputFormat string `json:\"outputFormat\" validate:\"required\"`\n\tStatus       string `json:\"status\"`\n}\n\ntype FileConvertRequest struct {\n\tFiles        []FileConvert `json:\"files\" validate:\"required\"`\n\tOutputPath   string        `json:\"outputPath\" validate:\"required\"`\n\tDeleteSource bool          `json:\"deleteSource\"`\n\tTaskID       string        `json:\"taskID\"`\n}\n\ntype FileRemarkBatch struct {\n\tPaths []string `json:\"paths\" validate:\"required\"`\n}\n\ntype FileRemarkUpdate struct {\n\tPath   string `json:\"path\" validate:\"required\"`\n\tRemark string `json:\"remark\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/host_tool.go",
    "content": "package request\n\ntype HostToolReq struct {\n\tType    string `json:\"type\" validate:\"required,oneof=supervisord\"`\n\tOperate string `json:\"operate\" validate:\"oneof=status restart start stop\"`\n}\n\ntype HostToolCreate struct {\n\tType string `json:\"type\" validate:\"required\"`\n\tSupervisorConfig\n}\n\ntype SupervisorConfig struct {\n\tConfigPath  string `json:\"configPath\"`\n\tServiceName string `json:\"serviceName\"`\n}\n\ntype HostToolLogReq struct {\n\tType string `json:\"type\" validate:\"required,oneof=supervisord\"`\n}\n\ntype HostToolConfig struct {\n\tType    string `json:\"type\" validate:\"required,oneof=supervisord\"`\n\tOperate string `json:\"operate\" validate:\"oneof=get set\"`\n\tContent string `json:\"content\"`\n}\n\ntype SupervisorProcessConfig struct {\n\tName        string `json:\"name\"`\n\tOperate     string `json:\"operate\"`\n\tCommand     string `json:\"command\"`\n\tUser        string `json:\"user\"`\n\tDir         string `json:\"dir\"`\n\tNumprocs    string `json:\"numprocs\"`\n\tAutoRestart string `json:\"autoRestart\"`\n\tAutoStart   string `json:\"autoStart\"`\n\tEnvironment string `json:\"environment\"`\n}\n\ntype SupervisorProcessFileReq struct {\n\tName    string `json:\"name\" validate:\"required\"`\n\tOperate string `json:\"operate\" validate:\"required,oneof=get clear update\" `\n\tContent string `json:\"content\"`\n\tFile    string `json:\"file\" validate:\"required,oneof=out.log err.log config\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/mcp_server.go",
    "content": "package request\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\ntype McpServerSearch struct {\n\tdto.PageInfo\n\tName string `json:\"name\"`\n\tSync bool   `json:\"sync\"`\n}\n\ntype McpServerCreate struct {\n\tName               string        `json:\"name\" validate:\"required\"`\n\tCommand            string        `json:\"command\" validate:\"required\"`\n\tEnvironments       []Environment `json:\"environments\"`\n\tVolumes            []Volume      `json:\"volumes\"`\n\tPort               int           `json:\"port\" validate:\"required\"`\n\tContainerName      string        `json:\"containerName\"`\n\tBaseURL            string        `json:\"baseUrl\"`\n\tSsePath            string        `json:\"ssePath\"`\n\tHostIP             string        `json:\"hostIP\"`\n\tStreamableHttpPath string        `json:\"streamableHttpPath\"`\n\tOutputTransport    string        `json:\"outputTransport\" validate:\"required\"`\n\tType               string        `json:\"type\" validate:\"required\"`\n}\n\ntype McpServerUpdate struct {\n\tID uint `json:\"id\" validate:\"required\"`\n\tMcpServerCreate\n}\n\ntype McpServerDelete struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype McpServerOperate struct {\n\tID      uint   `json:\"id\" validate:\"required\"`\n\tOperate string `json:\"operate\" validate:\"required\"`\n}\n\ntype McpBindDomain struct {\n\tDomain string `json:\"domain\" validate:\"required\"`\n\tSSLID  uint   `json:\"sslID\"`\n\tIPList string `json:\"ipList\"`\n}\n\ntype McpBindDomainUpdate struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tSSLID     uint   `json:\"sslID\"`\n\tIPList    string `json:\"ipList\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/nginx.go",
    "content": "package request\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\ntype NginxConfigFileUpdate struct {\n\tContent string `json:\"content\" validate:\"required\"`\n\tBackup  bool   `json:\"backup\"`\n}\n\ntype NginxScopeReq struct {\n\tScope     dto.NginxKey `json:\"scope\" validate:\"required\"`\n\tWebsiteID uint         `json:\"websiteId\"`\n}\n\ntype NginxConfigUpdate struct {\n\tScope     dto.NginxKey `json:\"scope\"`\n\tOperate   string       `json:\"operate\" validate:\"required,oneof=add update delete\"`\n\tWebsiteID uint         `json:\"websiteId\"`\n\tParams    interface{}  `json:\"params\"`\n}\n\ntype NginxRewriteReq struct {\n\tWebsiteID uint   `json:\"websiteId\" validate:\"required\"`\n\tName      string `json:\"name\" validate:\"required\"`\n}\n\ntype CustomRewriteOperate struct {\n\tOperate string `json:\"operate\" validate:\"required,oneof=create delete\"`\n\tContent string `json:\"content\"`\n\tName    string `json:\"name\"`\n}\n\ntype NginxRewriteUpdate struct {\n\tWebsiteID uint   `json:\"websiteId\" validate:\"required\"`\n\tName      string `json:\"name\" validate:\"required\"`\n\tContent   string `json:\"content\"`\n}\n\ntype NginxProxyUpdate struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tContent   string `json:\"content\" validate:\"required\"`\n\tName      string `json:\"name\" validate:\"required\"`\n}\n\ntype NginxProxyCacheUpdate struct {\n\tWebsiteID       uint   `json:\"websiteID\" validate:\"required\"`\n\tOpen            bool   `json:\"open\"`\n\tCacheLimit      int    `json:\"cacheLimit\" validate:\"required\"`\n\tCacheLimitUnit  string `json:\"cacheLimitUnit\" validate:\"required\"`\n\tShareCache      int    `json:\"shareCache\" validate:\"required\"`\n\tShareCacheUnit  string `json:\"shareCacheUnit\" validate:\"required\"`\n\tCacheExpire     int    `json:\"cacheExpire\" validate:\"required\"`\n\tCacheExpireUnit string `json:\"cacheExpireUnit\" validate:\"required\"`\n}\n\ntype NginxAuthUpdate struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tOperate   string `json:\"operate\" validate:\"required\"`\n\tUsername  string `json:\"username\"`\n\tPassword  string `json:\"password\"`\n\tRemark    string `json:\"remark\"`\n}\n\ntype NginxPathAuthUpdate struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tOperate   string `json:\"operate\" validate:\"required\"`\n\tName      string `json:\"name\"`\n\tUsername  string `json:\"username\"`\n\tPassword  string `json:\"password\"`\n\tPath      string `json:\"path\"`\n\tRemark    string `json:\"remark\"`\n}\n\ntype NginxAuthReq struct {\n\tWebsiteID uint `json:\"websiteID\" validate:\"required\"`\n}\n\ntype NginxCommonReq struct {\n\tWebsiteID uint `json:\"websiteID\" validate:\"required\"`\n}\n\ntype NginxAntiLeechUpdate struct {\n\tWebsiteID   uint     `json:\"websiteID\" validate:\"required\"`\n\tExtends     string   `json:\"extends\"`\n\tReturn      string   `json:\"return\"`\n\tEnable      bool     `json:\"enable\" `\n\tServerNames []string `json:\"serverNames\"`\n\tCache       bool     `json:\"cache\"`\n\tCacheTime   int      `json:\"cacheTime\"`\n\tCacheUint   string   `json:\"cacheUint\"`\n\tNoneRef     bool     `json:\"noneRef\"`\n\tLogEnable   bool     `json:\"logEnable\"`\n\tBlocked     bool     `json:\"blocked\"`\n}\n\ntype NginxRedirectReq struct {\n\tName         string   `json:\"name\" validate:\"required\"`\n\tWebsiteID    uint     `json:\"websiteID\" validate:\"required\"`\n\tDomains      []string `json:\"domains\"`\n\tKeepPath     bool     `json:\"keepPath\"`\n\tEnable       bool     `json:\"enable\"`\n\tType         string   `json:\"type\" validate:\"required\"`\n\tRedirect     string   `json:\"redirect\" validate:\"required\"`\n\tPath         string   `json:\"path\"`\n\tTarget       string   `json:\"target\" validate:\"required\"`\n\tOperate      string   `json:\"operate\" validate:\"required\"`\n\tRedirectRoot bool     `json:\"redirectRoot\"`\n}\n\ntype NginxRedirectUpdate struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tContent   string `json:\"content\" validate:\"required\"`\n\tName      string `json:\"name\" validate:\"required\"`\n}\n\ntype NginxBuildReq struct {\n\tTaskID string `json:\"taskID\" validate:\"required\"`\n\tMirror string `json:\"mirror\"  validate:\"required\"`\n}\n\ntype NginxModuleUpdate struct {\n\tOperate  string `json:\"operate\" validate:\"required,oneof=create delete update\"`\n\tName     string `json:\"name\" validate:\"required\"`\n\tScript   string `json:\"script\"`\n\tPackages string `json:\"packages\"`\n\tEnable   bool   `json:\"enable\"`\n\tParams   string `json:\"params\"`\n}\n\ntype NginxOperateReq struct {\n\tOperate string `json:\"operate\" validate:\"required,oneof=enable disable\"`\n}\n\ntype NginxDefaultHTTPSUpdate struct {\n\tOperate            string `json:\"operate\" validate:\"required,oneof=enable disable\"`\n\tSSLRejectHandshake bool   `json:\"sslRejectHandshake\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/php_extensions.go",
    "content": "package request\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\ntype PHPExtensionsSearch struct {\n\tdto.PageInfo\n\tAll bool `json:\"all\"`\n}\n\ntype PHPExtensionsCreate struct {\n\tName       string `json:\"name\" validate:\"required\"`\n\tExtensions string `json:\"extensions\" validate:\"required\"`\n}\n\ntype PHPExtensionsUpdate struct {\n\tID         uint   `json:\"id\" validate:\"required\"`\n\tExtensions string `json:\"extensions\" validate:\"required\"`\n}\n\ntype PHPExtensionsDelete struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/process.go",
    "content": "package request\n\ntype ProcessReq struct {\n\tPID int32 `json:\"PID\"  validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/recycle_bin.go",
    "content": "package request\n\ntype RecycleBinCreate struct {\n\tSourcePath string `json:\"sourcePath\" validate:\"required\"`\n}\n\ntype RecycleBinReduce struct {\n\tFrom  string `json:\"from\" validate:\"required\"`\n\tRName string `json:\"rName\" validate:\"required\"`\n\tName  string `json:\"name\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/runtime.go",
    "content": "package request\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n)\n\ntype RuntimeSearch struct {\n\tdto.PageInfo\n\tType   string `json:\"type\"`\n\tName   string `json:\"name\"`\n\tStatus string `json:\"status\"`\n}\n\ntype RuntimeCreate struct {\n\tAppDetailID uint   `json:\"appDetailId\"`\n\tName        string `json:\"name\"`\n\tResource    string `json:\"resource\"`\n\tImage       string `json:\"image\"`\n\tType        string `json:\"type\"`\n\tVersion     string `json:\"version\"`\n\tSource      string `json:\"source\"`\n\tCodeDir     string `json:\"codeDir\"`\n\tRemark      string `json:\"remark\"`\n\n\tParams map[string]interface{} `json:\"params\"`\n\tNodeConfig\n}\n\ntype NodeConfig struct {\n\tInstall      bool          `json:\"install\"`\n\tClean        bool          `json:\"clean\"`\n\tExposedPorts []ExposedPort `json:\"exposedPorts\"`\n\tEnvironments []Environment `json:\"environments\"`\n\tVolumes      []Volume      `json:\"volumes\"`\n\tExtraHosts   []ExtraHost   `json:\"extraHosts\"`\n}\n\ntype Environment struct {\n\tKey   string `json:\"key\"`\n\tValue string `json:\"value\"`\n}\ntype Volume struct {\n\tSource string `json:\"source\"`\n\tTarget string `json:\"target\"`\n}\n\ntype ExposedPort struct {\n\tHostPort      int    `json:\"hostPort\"`\n\tContainerPort int    `json:\"containerPort\"`\n\tHostIP        string `json:\"hostIP\"`\n}\n\ntype ExtraHost struct {\n\tHostname string `json:\"hostname\"`\n\tIP       string `json:\"ip\"`\n}\n\ntype RuntimeDelete struct {\n\tID          uint `json:\"id\"`\n\tForceDelete bool `json:\"forceDelete\"`\n}\n\ntype RuntimeUpdate struct {\n\tName    string `json:\"name\"`\n\tID      uint   `json:\"id\"`\n\tImage   string `json:\"image\"`\n\tVersion string `json:\"version\"`\n\tRebuild bool   `json:\"rebuild\"`\n\tSource  string `json:\"source\"`\n\tCodeDir string `json:\"codeDir\"`\n\tRemark  string `json:\"remark\"`\n\n\tParams map[string]interface{} `json:\"params\"`\n\tNodeConfig\n}\n\ntype NodePackageReq struct {\n\tCodeDir string `json:\"codeDir\"`\n}\n\ntype RuntimeOperate struct {\n\tOperate string `json:\"operate\"`\n\tID      uint   `json:\"ID\"`\n}\n\ntype NodeModuleOperateReq struct {\n\tOperate    string `json:\"operate\" validate:\"oneof=install uninstall update\"`\n\tID         uint   `json:\"ID\" validate:\"required\"`\n\tModule     string `json:\"module\"`\n\tPkgManager string `json:\"pkgManager\" validate:\"oneof=npm yarn\"`\n}\n\ntype NodeModuleReq struct {\n\tID uint `json:\"ID\" validate:\"required\"`\n}\n\ntype PHPExtensionInstallReq struct {\n\tID     uint   `json:\"ID\" validate:\"required\"`\n\tName   string `json:\"name\" validate:\"required\"`\n\tTaskID string `json:\"taskID\"`\n}\n\ntype PHPConfigUpdate struct {\n\tID               uint              `json:\"id\" validate:\"required\"`\n\tParams           map[string]string `json:\"params\"`\n\tScope            string            `json:\"scope\" validate:\"required\"`\n\tDisableFunctions []string          `json:\"disableFunctions\"`\n\tUploadMaxSize    string            `json:\"uploadMaxSize\"`\n\tMaxExecutionTime string            `json:\"maxExecutionTime\"`\n}\n\ntype PHPFileUpdate struct {\n\tID      uint   `json:\"id\" validate:\"required\"`\n\tType    string `json:\"type\" validate:\"required\"`\n\tContent string `json:\"content\" validate:\"required\"`\n}\n\ntype PHPFileReq struct {\n\tID   uint   `json:\"id\" validate:\"required\"`\n\tType string `json:\"type\" validate:\"required\"`\n}\n\ntype FPMConfig struct {\n\tID     uint                   `json:\"id\" validate:\"required\"`\n\tParams map[string]interface{} `json:\"params\" validate:\"required\"`\n}\n\ntype PHPSupervisorProcessConfig struct {\n\tID uint `json:\"id\" validate:\"required\"`\n\tSupervisorProcessConfig\n}\n\ntype PHPSupervisorProcessFileReq struct {\n\tID uint `json:\"id\" validate:\"required\"`\n\tSupervisorProcessFileReq\n}\n\ntype PHPContainerConfig struct {\n\tID            uint          `json:\"id\" validate:\"required\"`\n\tContainerName string        `json:\"containerName\"`\n\tExposedPorts  []ExposedPort `json:\"exposedPorts\"`\n\tEnvironments  []Environment `json:\"environments\"`\n\tVolumes       []Volume      `json:\"volumes\"`\n\tExtraHosts    []ExtraHost   `json:\"extraHosts\"`\n}\n\ntype RuntimeRemark struct {\n\tID     uint   `json:\"id\" validate:\"required\"`\n\tRemark string `json:\"remark\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/task.go",
    "content": "package request\n"
  },
  {
    "path": "agent/app/dto/request/tensorrt_llm.go",
    "content": "package request\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\ntype TensorRTLLMSearch struct {\n\tdto.PageInfo\n\tName string `json:\"name\"`\n}\n\ntype TensorRTLLMCreate struct {\n\tName          string `json:\"name\" validate:\"required\"`\n\tContainerName string `json:\"containerName\"  validate:\"required\"`\n\tVersion       string `json:\"version\"  validate:\"required\"`\n\tModelDir      string `json:\"modelDir\" validate:\"required\"`\n\tImage         string `json:\"image\"  validate:\"required\"`\n\tCommand       string `json:\"command\" validate:\"required\"`\n\tModelType     string `json:\"modelType\"`\n\tModelSpeedup  bool   `json:\"modelSpeedup\"`\n\tDockerConfig\n}\n\ntype TensorRTLLMUpdate struct {\n\tID uint `json:\"id\" validate:\"required\"`\n\tTensorRTLLMCreate\n}\n\ntype TensorRTLLMDelete struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype TensorRTLLMOperate struct {\n\tID      uint   `json:\"id\" validate:\"required\"`\n\tOperate string `json:\"operate\" validate:\"required\"`\n}\n\ntype DockerConfig struct {\n\tExposedPorts []ExposedPort `json:\"exposedPorts\"`\n\tEnvironments []Environment `json:\"environments\"`\n\tVolumes      []Volume      `json:\"volumes\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/website.go",
    "content": "package request\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n)\n\ntype WebsiteSearch struct {\n\tdto.PageInfo\n\tName           string `json:\"name\"`\n\tOrderBy        string `json:\"orderBy\" validate:\"required,oneof=primary_domain type status createdAt expire_date created_at favorite\"`\n\tOrder          string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n\tWebsiteGroupID uint   `json:\"websiteGroupId\"`\n\tType           string `json:\"type\"`\n}\n\ntype WebsiteCreate struct {\n\tType           string `json:\"type\" validate:\"required\"`\n\tAlias          string `json:\"alias\" validate:\"required\"`\n\tRemark         string `json:\"remark\"`\n\tProxy          string `json:\"proxy\"`\n\tWebsiteGroupID uint   `json:\"webSiteGroupID\" validate:\"required\"`\n\tIPV6           bool   `json:\"IPV6\"`\n\n\tDomains []WebsiteDomain `json:\"domains\"`\n\n\tAppType      string        `json:\"appType\" validate:\"oneof=new installed\"`\n\tAppInstall   NewAppInstall `json:\"appInstall\"`\n\tAppID        uint          `json:\"appID\"`\n\tAppInstallID uint          `json:\"appInstallID\"`\n\n\tRuntimeID       uint   `json:\"runtimeID\"`\n\tTaskID          string `json:\"taskID\"`\n\tParentWebsiteID uint   `json:\"parentWebsiteID\"`\n\n\tSiteDir string `json:\"siteDir\"`\n\n\tRuntimeConfig\n\tFtpConfig\n\tDataBaseConfig\n\tSSLConfig\n\tStreamConfig\n}\n\ntype StreamConfig struct {\n\tStreamPorts string `json:\"streamPorts\"`\n\tName        string `json:\"name\"`\n\tAlgorithm   string `json:\"algorithm\"`\n\tUDP         bool   `json:\"udp\"`\n\n\tServers []dto.NginxUpstreamServer `json:\"servers\"`\n}\n\ntype StreamUpdate struct {\n\tWebsiteID uint `json:\"websiteID\" validate:\"required\"`\n\n\tStreamConfig\n}\n\ntype WebsiteOptionReq struct {\n\tTypes []string `json:\"types\"`\n}\n\ntype RuntimeConfig struct {\n\tProxyType string `json:\"proxyType\"`\n\tPort      int    `json:\"port\"`\n}\n\ntype FtpConfig struct {\n\tFtpUser     string `json:\"ftpUser\"`\n\tFtpPassword string `json:\"ftpPassword\"`\n}\n\ntype DataBaseConfig struct {\n\tCreateDb   bool   `json:\"createDb\"`\n\tDbName     string `json:\"dbName\"`\n\tDbUser     string `json:\"dbUser\"`\n\tDbPassword string `json:\"dbPassword\"`\n\tDbHost     string `json:\"dbHost\"`\n\tDBFormat   string `json:\"dbFormat\"`\n}\n\ntype SSLConfig struct {\n\tEnableSSL    bool `json:\"enableSSL\"`\n\tWebsiteSSLID uint `json:\"websiteSSLID\"`\n}\n\ntype NewAppInstall struct {\n\tName        string                 `json:\"name\"`\n\tAppDetailId uint                   `json:\"appDetailID\"`\n\tParams      map[string]interface{} `json:\"params\"`\n\n\tAppContainerConfig\n}\n\ntype WebsiteInstallCheckReq struct {\n\tInstallIds []uint `json:\"InstallIds\"`\n}\n\ntype WebsiteUpdate struct {\n\tID             uint   `json:\"id\" validate:\"required\"`\n\tPrimaryDomain  string `json:\"primaryDomain\" validate:\"required\"`\n\tRemark         string `json:\"remark\"`\n\tWebsiteGroupID uint   `json:\"webSiteGroupID\"`\n\tExpireDate     string `json:\"expireDate\"`\n\tIPV6           bool   `json:\"IPV6\"`\n\tFavorite       bool   `json:\"favorite\"`\n}\n\ntype WebsiteDelete struct {\n\tID           uint `json:\"id\" validate:\"required\"`\n\tDeleteApp    bool `json:\"deleteApp\"`\n\tDeleteBackup bool `json:\"deleteBackup\"`\n\tForceDelete  bool `json:\"forceDelete\"`\n\tDeleteDB     bool `json:\"deleteDB\"`\n}\n\ntype WebsiteOp struct {\n\tID      uint   `json:\"id\" validate:\"required\"`\n\tOperate string `json:\"operate\"`\n}\n\ntype BatchWebsiteOp struct {\n\tIDs     []uint `json:\"ids\" validate:\"required\"`\n\tOperate string `json:\"operate\" validate:\"required\"`\n\tTaskID  string `json:\"taskID\" validate:\"required\"`\n}\n\ntype BatchWebsiteGroup struct {\n\tIDs     []uint `json:\"ids\" validate:\"required\"`\n\tGroupID uint   `json:\"groupID\" validate:\"required\"`\n}\n\ntype BatchWebsiteHttps struct {\n\tIDs                   []uint   `json:\"ids\" validate:\"required\"`\n\tTaskID                string   `json:\"taskID\" validate:\"required\"`\n\tWebsiteSSLID          uint     `json:\"websiteSSLId\"`\n\tType                  string   `json:\"type\"  validate:\"oneof=existed auto manual\"`\n\tPrivateKey            string   `json:\"privateKey\"`\n\tCertificate           string   `json:\"certificate\"`\n\tPrivateKeyPath        string   `json:\"privateKeyPath\"`\n\tCertificatePath       string   `json:\"certificatePath\"`\n\tImportType            string   `json:\"importType\"`\n\tHttpConfig            string   `json:\"httpConfig\"  validate:\"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS\"`\n\tSSLProtocol           []string `json:\"SSLProtocol\"`\n\tAlgorithm             string   `json:\"algorithm\"`\n\tHsts                  bool     `json:\"hsts\"`\n\tHstsIncludeSubDomains bool     `json:\"hstsIncludeSubDomains\"`\n\tHttpsPorts            []int    `json:\"httpsPorts\"`\n\tHttp3                 bool     `json:\"http3\"`\n}\n\ntype WebsiteRedirectUpdate struct {\n\tWebsiteID uint   `json:\"websiteId\" validate:\"required\"`\n\tKey       string `json:\"key\" validate:\"required\"`\n\tEnable    bool   `json:\"enable\"`\n}\n\ntype WebsiteRecover struct {\n\tWebsiteName string `json:\"websiteName\" validate:\"required\"`\n\tType        string `json:\"type\" validate:\"required\"`\n\tBackupName  string `json:\"backupName\" validate:\"required\"`\n}\n\ntype WebsiteRecoverByFile struct {\n\tWebsiteName string `json:\"websiteName\" validate:\"required\"`\n\tType        string `json:\"type\" validate:\"required\"`\n\tFileDir     string `json:\"fileDir\" validate:\"required\"`\n\tFileName    string `json:\"fileName\" validate:\"required\"`\n}\n\ntype WebsiteGroupCreate struct {\n\tName string `json:\"name\" validate:\"required\"`\n}\n\ntype WebsiteGroupUpdate struct {\n\tID      uint   `json:\"id\" validate:\"required\"`\n\tName    string `json:\"name\"`\n\tDefault bool   `json:\"default\"`\n}\n\ntype WebsiteDomainCreate struct {\n\tWebsiteID uint            `json:\"websiteID\" validate:\"required\"`\n\tDomains   []WebsiteDomain `json:\"domains\" validate:\"required\"`\n}\n\ntype WebsiteDomainUpdate struct {\n\tID  uint `json:\"id\" validate:\"required\"`\n\tSSL bool `json:\"ssl\"`\n}\n\ntype WebsiteDomain struct {\n\tDomain string `json:\"domain\" validate:\"required\"`\n\tPort   int    `json:\"port\"`\n\tSSL    bool   `json:\"ssl\"`\n}\n\ntype WebsiteDomainDelete struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype WebsiteHTTPSOp struct {\n\tWebsiteID             uint     `json:\"websiteId\" validate:\"required\"`\n\tEnable                bool     `json:\"enable\"`\n\tWebsiteSSLID          uint     `json:\"websiteSSLId\"`\n\tType                  string   `json:\"type\"  validate:\"oneof=existed auto manual\"`\n\tPrivateKey            string   `json:\"privateKey\"`\n\tCertificate           string   `json:\"certificate\"`\n\tPrivateKeyPath        string   `json:\"privateKeyPath\"`\n\tCertificatePath       string   `json:\"certificatePath\"`\n\tImportType            string   `json:\"importType\"`\n\tHttpConfig            string   `json:\"httpConfig\"  validate:\"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS\"`\n\tSSLProtocol           []string `json:\"SSLProtocol\"`\n\tAlgorithm             string   `json:\"algorithm\"`\n\tHsts                  bool     `json:\"hsts\"`\n\tHstsIncludeSubDomains bool     `json:\"hstsIncludeSubDomains\"`\n\tHttpsPorts            []int    `json:\"httpsPorts\"`\n\tHttp3                 bool     `json:\"http3\"`\n}\n\ntype WebsiteNginxUpdate struct {\n\tID      uint   `json:\"id\" validate:\"required\"`\n\tContent string `json:\"content\" validate:\"required\"`\n}\n\ntype WebsiteLogReq struct {\n\tID       uint   `json:\"id\" validate:\"required\"`\n\tOperate  string `json:\"operate\" validate:\"required\"`\n\tLogType  string `json:\"logType\" validate:\"required\"`\n\tPage     int    `json:\"page\"`\n\tPageSize int    `json:\"pageSize\"`\n}\n\ntype WebsiteDefaultUpdate struct {\n\tID uint `json:\"id\"`\n}\n\ntype WebsitePHPVersionReq struct {\n\tWebsiteID uint `json:\"websiteID\" validate:\"required\"`\n\tRuntimeID uint `json:\"runtimeID\"`\n}\n\ntype WebsiteUpdateDir struct {\n\tID      uint   `json:\"id\" validate:\"required\"`\n\tSiteDir string `json:\"siteDir\" validate:\"required\"`\n}\n\ntype WebsiteUpdateDirPermission struct {\n\tID    uint   `json:\"id\" validate:\"required\"`\n\tUser  string `json:\"user\" validate:\"required\"`\n\tGroup string `json:\"group\" validate:\"required\"`\n}\n\ntype WebsiteProxyConfig struct {\n\tID              uint              `json:\"id\" validate:\"required\"`\n\tOperate         string            `json:\"operate\" validate:\"required\"`\n\tEnable          bool              `json:\"enable\" `\n\tCache           bool              `json:\"cache\" `\n\tCacheTime       int               `json:\"cacheTime\"`\n\tCacheUnit       string            `json:\"cacheUnit\"`\n\tServerCacheTime int               `json:\"serverCacheTime\"`\n\tServerCacheUnit string            `json:\"serverCacheUnit\"`\n\tName            string            `json:\"name\" validate:\"required\"`\n\tModifier        string            `json:\"modifier\"`\n\tMatch           string            `json:\"match\" validate:\"required\"`\n\tProxyPass       string            `json:\"proxyPass\" validate:\"required\"`\n\tProxyHost       string            `json:\"proxyHost\" validate:\"required\"`\n\tContent         string            `json:\"content\"`\n\tFilePath        string            `json:\"filePath\"`\n\tReplaces        map[string]string `json:\"replaces\"`\n\tSNI             bool              `json:\"sni\"`\n\tProxySSLName    string            `json:\"proxySSLName\"`\n\tCorsConfig\n}\n\ntype WebsiteProxyDelete struct {\n\tID   uint   `json:\"id\" validate:\"required\"`\n\tName string `json:\"name\" validate:\"required\"`\n}\n\ntype WebsiteProxyStatusUpdate struct {\n\tID     uint   `json:\"id\" validate:\"required\"`\n\tName   string `json:\"name\" validate:\"required\"`\n\tStatus string `json:\"status\" validate:\"required\"`\n}\n\ntype CorsConfig struct {\n\tCors             bool   `json:\"cors\"`\n\tAllowOrigins     string `json:\"allowOrigins\"`\n\tAllowMethods     string `json:\"allowMethods\"`\n\tAllowHeaders     string `json:\"allowHeaders\"`\n\tAllowCredentials bool   `json:\"allowCredentials\"`\n\tPreflight        bool   `json:\"preflight\"`\n}\n\ntype CorsConfigReq struct {\n\tWebsiteID uint `json:\"websiteID\" validate:\"required\"`\n\tCorsConfig\n}\n\ntype WebsiteProxyReq struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype WebsiteRedirectReq struct {\n\tWebsiteID uint `json:\"websiteId\" validate:\"required\"`\n}\n\ntype WebsiteCommonReq struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype WafWebsite struct {\n\tKey     string   `json:\"key\"`\n\tDomains []string `json:\"domains\"`\n\tHost    []string `json:\"host\"`\n}\n\ntype WebsiteHtmlReq struct {\n\tType string `json:\"type\" validate:\"required\"`\n}\n\ntype WebsiteHtmlUpdate struct {\n\tType    string `json:\"type\" validate:\"required\"`\n\tContent string `json:\"content\" validate:\"required\"`\n\tSync    bool   `json:\"sync\"`\n}\n\ntype WebsiteLBCreate struct {\n\tWebsiteID uint                      `json:\"websiteID\" validate:\"required\"`\n\tName      string                    `json:\"name\" validate:\"required\"`\n\tAlgorithm string                    `json:\"algorithm\"`\n\tServers   []dto.NginxUpstreamServer `json:\"servers\"`\n}\n\ntype WebsiteLBUpdate struct {\n\tWebsiteID uint                      `json:\"websiteID\" validate:\"required\"`\n\tName      string                    `json:\"name\" validate:\"required\"`\n\tAlgorithm string                    `json:\"algorithm\"`\n\tServers   []dto.NginxUpstreamServer `json:\"servers\"`\n}\n\ntype WebsiteLBDelete struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tName      string `json:\"name\" validate:\"required\"`\n}\n\ntype WebsiteLBUpdateFile struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tName      string `json:\"name\" validate:\"required\"`\n\tContent   string `json:\"content\" validate:\"required\"`\n}\n\ntype WebsiteRealIP struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tOpen      bool   `json:\"open\"`\n\tIPFrom    string `json:\"ipFrom\"`\n\tIPHeader  string `json:\"ipHeader\"`\n\tIPOther   string `json:\"ipOther\"`\n}\n\ntype ChangeDatabase struct {\n\tWebsiteID    uint   `json:\"websiteID\" validate:\"required\"`\n\tDatabaseID   uint   `json:\"databaseID\" `\n\tDatabaseType string `json:\"databaseType\" `\n}\n\ntype WebsiteProxyDel struct {\n\tID   uint   `json:\"id\" validate:\"required\"`\n\tName string `json:\"name\" validate:\"required\"`\n}\n\ntype CrossSiteAccessOp struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tOperation string `json:\"operation\" validate:\"required,oneof=Enable Disable\"`\n}\n\ntype ExecComposerReq struct {\n\tCommand    string `json:\"command\" validate:\"required\"`\n\tExtCommand string `json:\"extCommand\"`\n\tMirror     string `json:\"mirror\" validate:\"required\"`\n\tDir        string `json:\"dir\" validate:\"required\"`\n\tUser       string `json:\"user\" validate:\"required\"`\n\tWebsiteID  uint   `json:\"websiteID\" validate:\"required\"`\n\tTaskID     string `json:\"taskID\" validate:\"required\"`\n}\n"
  },
  {
    "path": "agent/app/dto/request/website_ssl.go",
    "content": "package request\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\ntype WebsiteSSLSearch struct {\n\tdto.PageInfo\n\tAcmeAccountID string `json:\"acmeAccountID\"`\n\tDomain        string `json:\"domain\"`\n\tOrderBy       string `json:\"orderBy\" validate:\"omitempty,oneof=created_at expire_date\"`\n\tOrder         string `json:\"order\" validate:\"omitempty,oneof=null ascending descending\"`\n}\n\ntype WebsiteSSLListReq struct {\n\tAcmeAccountID string `json:\"acmeAccountID\"`\n}\n\ntype WebsiteSSLCreate struct {\n\tPrimaryDomain string `json:\"primaryDomain\" validate:\"required\"`\n\tOtherDomains  string `json:\"otherDomains\"`\n\tProvider      string `json:\"provider\" validate:\"required\"`\n\tAcmeAccountID uint   `json:\"acmeAccountId\" validate:\"required\"`\n\tDnsAccountID  uint   `json:\"dnsAccountId\"`\n\tAutoRenew     bool   `json:\"autoRenew\"`\n\tKeyType       string `json:\"keyType\"`\n\tApply         bool   `json:\"apply\"`\n\tPushDir       bool   `json:\"pushDir\"`\n\tDir           string `json:\"dir\"`\n\tID            uint   `json:\"id\"`\n\tDescription   string `json:\"description\"`\n\tDisableCNAME  bool   `json:\"disableCNAME\"`\n\tSkipDNS       bool   `json:\"skipDNS\"`\n\tNameserver1   string `json:\"nameserver1\"`\n\tNameserver2   string `json:\"nameserver2\"`\n\tExecShell     bool   `json:\"execShell\"`\n\tShell         string `json:\"shell\"`\n\tPushNode      bool   `json:\"pushNode\"`\n\tNodes         string `json:\"nodes\"`\n\tIsIp          bool   `json:\"isIp\"`\n}\n\ntype WebsiteDNSReq struct {\n\tAcmeAccountID uint `json:\"acmeAccountId\"  validate:\"required\"`\n\tWebsiteSSLID  uint `json:\"websiteSSLId\"  validate:\"required\"`\n}\n\ntype WebsiteSSLRenew struct {\n\tSSLID uint `json:\"SSLId\" validate:\"required\"`\n}\n\ntype WebsiteSSLApply struct {\n\tID           uint     `json:\"ID\" validate:\"required\"`\n\tSkipDNSCheck bool     `json:\"skipDNSCheck\"`\n\tNameservers  []string `json:\"nameservers\"`\n\tDisableLog   bool     `json:\"disableLog\"`\n}\n\ntype WebsiteSSLObtain struct {\n\tID         uint `json:\"ID\" validate:\"required\"`\n\tTXTRecords map[string]string\n}\n\ntype WebsiteAcmeAccountCreate struct {\n\tEmail      string `json:\"email\" validate:\"required\"`\n\tType       string `json:\"type\" validate:\"required,oneof=letsencrypt zerossl buypass google custom\"`\n\tKeyType    string `json:\"keyType\" validate:\"required,oneof=P256 P384 2048 3072 4096 8192\"`\n\tEabKid     string `json:\"eabKid\"`\n\tEabHmacKey string `json:\"eabHmacKey\"`\n\tUseProxy   bool   `json:\"useProxy\"`\n\tCaDirURL   string `json:\"caDirURL\"`\n\tUseEAB     bool   `json:\"useEAB\"`\n}\n\ntype WebsiteAcmeAccountUpdate struct {\n\tID       uint `json:\"id\" validate:\"required\"`\n\tUseProxy bool `json:\"useProxy\"`\n}\n\ntype WebsiteDnsAccountCreate struct {\n\tName          string            `json:\"name\" validate:\"required\"`\n\tType          string            `json:\"type\" validate:\"required\"`\n\tAuthorization map[string]string `json:\"authorization\" validate:\"required\"`\n}\n\ntype WebsiteDnsAccountUpdate struct {\n\tID            uint              `json:\"id\" validate:\"required\"`\n\tName          string            `json:\"name\" validate:\"required\"`\n\tType          string            `json:\"type\" validate:\"required\"`\n\tAuthorization map[string]string `json:\"authorization\" validate:\"required\"`\n}\n\ntype WebsiteResourceReq struct {\n\tID uint `json:\"id\" validate:\"required\"`\n}\n\ntype WebsiteBatchDelReq struct {\n\tIDs []uint `json:\"ids\" validate:\"required\"`\n}\n\ntype WebsiteSSLUpdate struct {\n\tID            uint   `json:\"id\" validate:\"required\"`\n\tAutoRenew     bool   `json:\"autoRenew\"`\n\tDescription   string `json:\"description\"`\n\tPrimaryDomain string `json:\"primaryDomain\" validate:\"required\"`\n\tOtherDomains  string `json:\"otherDomains\"`\n\tProvider      string `json:\"provider\" validate:\"required\"`\n\tAcmeAccountID uint   `json:\"acmeAccountId\"`\n\tDnsAccountID  uint   `json:\"dnsAccountId\"`\n\tKeyType       string `json:\"keyType\"`\n\tApply         bool   `json:\"apply\"`\n\tPushDir       bool   `json:\"pushDir\"`\n\tDir           string `json:\"dir\"`\n\tDisableCNAME  bool   `json:\"disableCNAME\"`\n\tSkipDNS       bool   `json:\"skipDNS\"`\n\tNameserver1   string `json:\"nameserver1\"`\n\tNameserver2   string `json:\"nameserver2\"`\n\tExecShell     bool   `json:\"execShell\"`\n\tShell         string `json:\"shell\"`\n\tPushNode      bool   `json:\"pushNode\"`\n\tNodes         string `json:\"nodes\"`\n}\n\ntype WebsiteSSLUpload struct {\n\tPrivateKey      string `json:\"privateKey\"`\n\tCertificate     string `json:\"certificate\"`\n\tPrivateKeyPath  string `json:\"privateKeyPath\"`\n\tCertificatePath string `json:\"certificatePath\"`\n\tType            string `json:\"type\" validate:\"required,oneof=paste local\"`\n\tSSLID           uint   `json:\"sslID\"`\n\tDescription     string `json:\"description\"`\n}\n\ntype WebsiteCASearch struct {\n\tdto.PageInfo\n}\n\ntype WebsiteCACreate struct {\n\tCommonName       string `json:\"commonName\" validate:\"required\"`\n\tCountry          string `json:\"country\" validate:\"required\"`\n\tOrganization     string `json:\"organization\" validate:\"required\"`\n\tOrganizationUint string `json:\"organizationUint\"`\n\tName             string `json:\"name\" validate:\"required\"`\n\tKeyType          string `json:\"keyType\" validate:\"required,oneof=P256 P384 2048 3072 4096 8192\"`\n\tProvince         string `json:\"province\" `\n\tCity             string `json:\"city\"`\n}\n\ntype WebsiteCAObtain struct {\n\tID          uint   `json:\"id\" validate:\"required\"`\n\tDomains     string `json:\"domains\" validate:\"required\"`\n\tKeyType     string `json:\"keyType\" validate:\"required,oneof=P256 P384 2048 3072 4096 8192\"`\n\tTime        int    `json:\"time\" validate:\"required\"`\n\tUnit        string `json:\"unit\" validate:\"required\"`\n\tPushDir     bool   `json:\"pushDir\"`\n\tDir         string `json:\"dir\"`\n\tAutoRenew   bool   `json:\"autoRenew\"`\n\tRenew       bool   `json:\"renew\"`\n\tSSLID       uint   `json:\"sslID\"`\n\tDescription string `json:\"description\"`\n\tExecShell   bool   `json:\"execShell\"`\n\tShell       string `json:\"shell\"`\n}\n\ntype WebsiteCARenew struct {\n\tSSLID uint `json:\"SSLID\" validate:\"required\"`\n}\n\ntype WebsiteSSLFileUpload struct {\n\tType        string `json:\"type\"`\n\tDescription string `json:\"description\"`\n\tSSLID       uint64 `json:\"sslID\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/app.go",
    "content": "package response\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype AppRes struct {\n\tItems []*AppItem `json:\"items\"`\n\tTotal int64      `json:\"total\"`\n}\n\ntype AppUpdateRes struct {\n\tCanUpdate            bool         `json:\"canUpdate\"`\n\tIsSyncing            bool         `json:\"isSyncing\"`\n\tAppStoreLastModified int          `json:\"appStoreLastModified\"`\n\tAppList              *dto.AppList `json:\"appList\"`\n}\n\ntype AppDTO struct {\n\tmodel.App\n\tInstalled bool     `json:\"installed\"`\n\tVersions  []string `json:\"versions\"`\n\tTags      []TagDTO `json:\"tags\"`\n}\n\ntype AppItem struct {\n\tName                string   `json:\"name\"`\n\tKey                 string   `json:\"key\"`\n\tID                  uint     `json:\"id\"`\n\tDescription         string   `json:\"description\"`\n\tStatus              string   `json:\"status\"`\n\tInstalled           bool     `json:\"installed\"`\n\tLimit               int      `json:\"limit\"`\n\tTags                []string `json:\"tags\"`\n\tGpuSupport          bool     `json:\"gpuSupport\"`\n\tRecommend           int      `json:\"recommend\"`\n\tType                string   `json:\"type\"`\n\tBatchInstallSupport bool     `json:\"batchInstallSupport\"`\n}\n\ntype TagDTO struct {\n\tID   uint   `json:\"id\"`\n\tKey  string `json:\"key\"`\n\tName string `json:\"name\"`\n}\n\ntype AppInstalledCheck struct {\n\tIsExist       bool      `json:\"isExist\"`\n\tName          string    `json:\"name\"`\n\tApp           string    `json:\"app\"`\n\tVersion       string    `json:\"version\"`\n\tStatus        string    `json:\"status\"`\n\tCreatedAt     time.Time `json:\"createdAt\"`\n\tLastBackupAt  string    `json:\"lastBackupAt\"`\n\tAppInstallID  uint      `json:\"appInstallId\"`\n\tContainerName string    `json:\"containerName\"`\n\tInstallPath   string    `json:\"installPath\"`\n\tHttpPort      int       `json:\"httpPort\"`\n\tHttpsPort     int       `json:\"httpsPort\"`\n\tWebsiteDir    string    `json:\"websiteDir\"`\n}\n\ntype AppDetailDTO struct {\n\tmodel.AppDetail\n\tEnable         bool        `json:\"enable\"`\n\tParams         interface{} `json:\"params\"`\n\tImage          string      `json:\"image\"`\n\tHostMode       bool        `json:\"hostMode\"`\n\tArchitectures  string      `json:\"architectures\"`\n\tMemoryRequired int         `json:\"memoryRequired\"`\n\tGpuSupport     bool        `json:\"gpuSupport\"`\n}\n\ntype AppDetailSimpleDTO struct {\n\tID uint `json:\"id\"`\n}\n\ntype IgnoredApp struct {\n\tIcon     string `json:\"icon\"`\n\tName     string `json:\"name\"`\n\tVersion  string `json:\"version\"`\n\tDetailID uint   `json:\"detailID\"`\n}\n\ntype AppInstalledDTO struct {\n\tmodel.AppInstall\n\tTotal     int    `json:\"total\"`\n\tReady     int    `json:\"ready\"`\n\tAppName   string `json:\"appName\"`\n\tIcon      string `json:\"icon\"`\n\tCanUpdate bool   `json:\"canUpdate\"`\n\tPath      string `json:\"path\"`\n}\n\ntype AppDetail struct {\n\tWebsite  string `json:\"website\"`\n\tDocument string `json:\"document\"`\n\tGithub   string `json:\"github\"`\n}\n\ntype AppInstallDTO struct {\n\tID            uint      `json:\"id\"`\n\tName          string    `json:\"name\"`\n\tAppID         uint      `json:\"appID\"`\n\tAppDetailID   uint      `json:\"appDetailID\"`\n\tVersion       string    `json:\"version\"`\n\tStatus        string    `json:\"status\"`\n\tMessage       string    `json:\"message\"`\n\tHttpPort      int       `json:\"httpPort\"`\n\tHttpsPort     int       `json:\"httpsPort\"`\n\tPath          string    `json:\"path\"`\n\tCanUpdate     bool      `json:\"canUpdate\"`\n\tIcon          string    `json:\"icon\"`\n\tAppName       string    `json:\"appName\"`\n\tReady         int       `json:\"ready\"`\n\tTotal         int       `json:\"total\"`\n\tAppKey        string    `json:\"appKey\"`\n\tAppType       string    `json:\"appType\"`\n\tAppStatus     string    `json:\"appStatus\"`\n\tDockerCompose string    `json:\"dockerCompose\"`\n\tWebUI         string    `json:\"webUI\"`\n\tCreatedAt     time.Time `json:\"createdAt\"`\n\tFavorite      bool      `json:\"favorite\"`\n\tSortOrder     int       `json:\"sortOrder\"`\n\tApp           AppDetail `json:\"app\"`\n\tContainer     string    `json:\"container\"`\n\tIsEdit        bool      `json:\"isEdit\"`\n\tLinkDB        bool      `json:\"linkDB\"`\n\tServiceName   string    `json:\"serviceName\"`\n}\n\ntype AppInstallInfo struct {\n\tID          uint   `json:\"id\"`\n\tName        string `json:\"name\"`\n\tVersion     string `json:\"version\"`\n\tStatus      string `json:\"status\"`\n\tMessage     string `json:\"message\"`\n\tHttpPort    int    `json:\"HttpPort\"`\n\tContainer   string `json:\"container\"`\n\tComposePath string `json:\"composePath\"`\n\tAppKey      string `json:\"appKey\"`\n\tAppPorts    []int  `json:\"appPorts\"`\n\n\tEnv map[string]interface{} `json:\"env\"`\n}\n\ntype DatabaseConn struct {\n\tStatus        string `json:\"status\"`\n\tUsername      string `json:\"username\"`\n\tPassword      string `json:\"password\"`\n\tContainerName string `json:\"containerName\"`\n\tServiceName   string `json:\"serviceName\"`\n\tPort          int64  `json:\"port\"`\n}\n\ntype AppService struct {\n\tLabel  string      `json:\"label\"`\n\tValue  string      `json:\"value\"`\n\tConfig interface{} `json:\"config\"`\n\tFrom   string      `json:\"from\"`\n\tStatus string      `json:\"status\"`\n}\n\ntype AppParam struct {\n\tValue     interface{} `json:\"value\"`\n\tEdit      bool        `json:\"edit\"`\n\tKey       string      `json:\"key\"`\n\tRule      string      `json:\"rule\"`\n\tLabelZh   string      `json:\"labelZh\"`\n\tLabelEn   string      `json:\"labelEn\"`\n\tType      string      `json:\"type\"`\n\tValues    interface{} `json:\"values\"`\n\tShowValue string      `json:\"showValue\"`\n\tRequired  bool        `json:\"required\"`\n\tMultiple  bool        `json:\"multiple\"`\n\tLabel     dto.Locale  `json:\"label\"`\n}\n\ntype AppConfig struct {\n\tParams     []AppParam `json:\"params\"`\n\tRawCompose string     `json:\"rawCompose\"`\n\trequest.AppContainerConfig\n}\n"
  },
  {
    "path": "agent/app/dto/response/app_ignore_upgrade.go",
    "content": "package response\n\ntype AppIgnoreUpgradeDTO struct {\n\tID          uint   `json:\"ID\"`\n\tAppID       uint   `json:\"appID\"`\n\tAppDetailID uint   `json:\"appDetailID\"`\n\tScope       string `json:\"scope\"`\n\tVersion     string `json:\"version\"`\n\tName        string `json:\"name\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/disk.go",
    "content": "package response\n\ntype DiskInfo struct {\n\tDiskBasicInfo\n\tPartitions []DiskBasicInfo `json:\"partitions\"`\n}\n\ntype DiskBasicInfo struct {\n\tDevice      string `json:\"device\"`\n\tSize        string `json:\"size\"`\n\tModel       string `json:\"model\"`\n\tDiskType    string `json:\"diskType\"`\n\tIsRemovable bool   `json:\"isRemovable\"`\n\tIsSystem    bool   `json:\"isSystem\"`\n\tFilesystem  string `json:\"filesystem\"`\n\tUsed        string `json:\"used\"`\n\tAvail       string `json:\"avail\"`\n\tUsePercent  int    `json:\"usePercent\"`\n\tMountPoint  string `json:\"mountPoint\"`\n\tIsMounted   bool   `json:\"isMounted\"`\n\tSerial      string `json:\"serial\"`\n}\n\ntype CompleteDiskInfo struct {\n\tDisks              []DiskInfo      `json:\"disks\"`\n\tUnpartitionedDisks []DiskBasicInfo `json:\"unpartitionedDisks\"`\n\tSystemDisks        []DiskInfo      `json:\"systemDisks\"`\n\tTotalDisks         int             `json:\"totalDisks\"`\n\tTotalCapacity      int64           `json:\"totalCapacity\"`\n}\n\ntype MountInfo struct {\n\tDevice     string `json:\"device\"`\n\tMountPoint string `json:\"mountPoint\"`\n\tFilesystem string `json:\"filesystem\"`\n\tOptions    string `json:\"options\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/favorite.go",
    "content": "package response\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/model\"\n\ntype FavoriteDTO struct {\n\tmodel.Favorite\n}\n"
  },
  {
    "path": "agent/app/dto/response/file.go",
    "content": "package response\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\ntype FileInfo struct {\n\tfiles.FileInfo\n}\n\ntype UploadInfo struct {\n\tName      string `json:\"name\"`\n\tSize      int    `json:\"size\"`\n\tCreatedAt string `json:\"createdAt\"`\n}\n\ntype FileTree struct {\n\tID        string     `json:\"id\"`\n\tName      string     `json:\"name\"`\n\tPath      string     `json:\"path\"`\n\tIsDir     bool       `json:\"isDir\"`\n\tExtension string     `json:\"extension\"`\n\tChildren  []FileTree `json:\"children\"`\n}\n\ntype DirSizeRes struct {\n\tSize int64 `json:\"size\" validate:\"required\"`\n}\n\ntype FileProcessKeys struct {\n\tKeys []string `json:\"keys\"`\n}\n\ntype FileWgetRes struct {\n\tKey string `json:\"key\"`\n}\n\ntype FileLineContent struct {\n\tEnd        bool     `json:\"end\"`\n\tPath       string   `json:\"path\"`\n\tTotal      int      `json:\"total\"`\n\tTaskStatus string   `json:\"taskStatus\"`\n\tLines      []string `json:\"lines\"`\n\tScope      string   `json:\"scope\"`\n\tTotalLines int      `json:\"totalLines\"`\n}\n\ntype FileExist struct {\n\tExist bool `json:\"exist\"`\n}\n\ntype ExistFileInfo struct {\n\tName    string    `json:\"name\"`\n\tPath    string    `json:\"path\"`\n\tSize    int64     `json:\"size\"`\n\tModTime time.Time `json:\"modTime\"`\n\tIsDir   bool      `json:\"isDir\"`\n}\n\ntype UserInfo struct {\n\tUsername string `json:\"username\"`\n\tGroup    string `json:\"group\"`\n}\n\ntype UserGroupResponse struct {\n\tUsers  []UserInfo `json:\"users\"`\n\tGroups []string   `json:\"groups\"`\n}\n\ntype DepthDirSizeRes struct {\n\tPath string `json:\"path\"`\n\tSize int64  `json:\"size\"`\n}\n\ntype FileConvertLog struct {\n\tDate    string `json:\"date\"`\n\tType    string `json:\"type\"`\n\tLog     string `json:\"log\"`\n\tStatus  string `json:\"status\"`\n\tMessage string `json:\"message\"`\n}\n\ntype FileRemarksRes struct {\n\tRemarks map[string]string `json:\"remarks\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/host_tool.go",
    "content": "package response\n\ntype HostToolRes struct {\n\tType   string      `json:\"type\"`\n\tConfig interface{} `json:\"config\"`\n}\n\ntype Supervisor struct {\n\tConfigPath  string `json:\"configPath\"`\n\tIncludeDir  string `json:\"includeDir\"`\n\tLogPath     string `json:\"logPath\"`\n\tIsExist     bool   `json:\"isExist\"`\n\tInit        bool   `json:\"init\"`\n\tMsg         string `json:\"msg\"`\n\tVersion     string `json:\"version\"`\n\tStatus      string `json:\"status\"`\n\tCtlExist    bool   `json:\"ctlExist\"`\n\tServiceName string `json:\"serviceName\"`\n}\n\ntype HostToolConfig struct {\n\tContent string `json:\"content\"`\n}\n\ntype SupervisorProcessConfig struct {\n\tName        string          `json:\"name\"`\n\tCommand     string          `json:\"command\"`\n\tUser        string          `json:\"user\"`\n\tDir         string          `json:\"dir\"`\n\tNumprocs    string          `json:\"numprocs\"`\n\tMsg         string          `json:\"msg\"`\n\tStatus      []ProcessStatus `json:\"status\"`\n\tAutoRestart string          `json:\"autoRestart\"`\n\tAutoStart   string          `json:\"autoStart\"`\n\tEnvironment string          `json:\"environment\"`\n}\n\ntype ProcessStatus struct {\n\tName   string `json:\"name\"`\n\tStatus string `json:\"status\"`\n\tPID    string `json:\"PID\"`\n\tUptime string `json:\"uptime\"`\n\tMsg    string `json:\"msg\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/mcp_server.go",
    "content": "package response\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype McpServersRes struct {\n\tItems []McpServerDTO `json:\"items\"`\n\tTotal int64          `json:\"total\"`\n}\n\ntype McpServerDTO struct {\n\tmodel.McpServer\n\tEnvironments []request.Environment `json:\"environments\"`\n\tVolumes      []request.Volume      `json:\"volumes\"`\n}\n\ntype McpBindDomainRes struct {\n\tDomain        string   `json:\"domain\"`\n\tSSLID         uint     `json:\"sslID\"`\n\tAcmeAccountID uint     `json:\"acmeAccountID\"`\n\tAllowIPs      []string `json:\"allowIPs\"`\n\tWebsiteID     uint     `json:\"websiteID\"`\n\tConnUrl       string   `json:\"connUrl\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/nginx.go",
    "content": "package response\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\ntype NginxStatus struct {\n\tActive   int `json:\"active\"`\n\tAccepts  int `json:\"accepts\"`\n\tHandled  int `json:\"handled\"`\n\tRequests int `json:\"requests\"`\n\tReading  int `json:\"reading\"`\n\tWriting  int `json:\"writing\"`\n\tWaiting  int `json:\"waiting\"`\n}\n\ntype NginxParam struct {\n\tName   string   `json:\"name\"`\n\tParams []string `json:\"params\"`\n}\n\ntype NginxAuthRes struct {\n\tEnable bool            `json:\"enable\"`\n\tItems  []dto.NginxAuth `json:\"items\"`\n}\n\ntype NginxPathAuthRes struct {\n\tdto.NginxPathAuth\n}\n\ntype NginxAntiLeechRes struct {\n\tEnable      bool     `json:\"enable\"`\n\tExtends     string   `json:\"extends\"`\n\tReturn      string   `json:\"return\"`\n\tServerNames []string `json:\"serverNames\"`\n\tCache       bool     `json:\"cache\"`\n\tCacheTime   int      `json:\"cacheTime\"`\n\tCacheUint   string   `json:\"cacheUint\"`\n\tNoneRef     bool     `json:\"noneRef\"`\n\tLogEnable   bool     `json:\"logEnable\"`\n\tBlocked     bool     `json:\"blocked\"`\n}\n\ntype NginxRedirectConfig struct {\n\tWebsiteID    uint     `json:\"websiteID\"`\n\tName         string   `json:\"name\"`\n\tDomains      []string `json:\"domains\"`\n\tKeepPath     bool     `json:\"keepPath\"`\n\tEnable       bool     `json:\"enable\"`\n\tType         string   `json:\"type\"`\n\tRedirect     string   `json:\"redirect\"`\n\tPath         string   `json:\"path\"`\n\tTarget       string   `json:\"target\"`\n\tFilePath     string   `json:\"filePath\"`\n\tContent      string   `json:\"content\"`\n\tRedirectRoot bool     `json:\"redirectRoot\"`\n}\n\ntype NginxFile struct {\n\tContent string `json:\"content\"`\n}\n\ntype NginxProxyCache struct {\n\tOpen            bool    `json:\"open\"`\n\tCacheLimit      float64 `json:\"cacheLimit\"`\n\tCacheLimitUnit  string  `json:\"cacheLimitUnit\" `\n\tShareCache      int     `json:\"shareCache\" `\n\tShareCacheUnit  string  `json:\"shareCacheUnit\" `\n\tCacheExpire     int     `json:\"cacheExpire\" `\n\tCacheExpireUnit string  `json:\"cacheExpireUnit\" `\n}\n\ntype NginxModule struct {\n\tName     string `json:\"name\"`\n\tScript   string `json:\"script\"`\n\tPackages string `json:\"packages\"`\n\tParams   string `json:\"params\"`\n\tEnable   bool   `json:\"enable\"`\n}\n\ntype NginxBuildConfig struct {\n\tMirror  string        `json:\"mirror\"`\n\tModules []NginxModule `json:\"modules\"`\n}\n\ntype NginxConfigRes struct {\n\tHttps              bool `json:\"https\"`\n\tSSLRejectHandshake bool `json:\"sslRejectHandshake\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/php_extensions.go",
    "content": "package response\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/model\"\n\ntype PHPExtensionsDTO struct {\n\tmodel.PHPExtensions\n}\n"
  },
  {
    "path": "agent/app/dto/response/recycle_bin.go",
    "content": "package response\n\nimport \"time\"\n\ntype RecycleBinDTO struct {\n\tName       string    `json:\"name\"`\n\tSize       int       `json:\"size\"`\n\tType       string    `json:\"type\"`\n\tDeleteTime time.Time `json:\"deleteTime\"`\n\tRName      string    `json:\"rName\"`\n\tSourcePath string    `json:\"sourcePath\"`\n\tIsDir      bool      `json:\"isDir\"`\n\tFrom       string    `json:\"from\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/runtime.go",
    "content": "package response\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype RuntimeDTO struct {\n\tID              uint                   `json:\"id\"`\n\tName            string                 `json:\"name\"`\n\tResource        string                 `json:\"resource\"`\n\tAppDetailID     uint                   `json:\"appDetailID\"`\n\tAppID           uint                   `json:\"appID\"`\n\tSource          string                 `json:\"source\"`\n\tStatus          string                 `json:\"status\"`\n\tType            string                 `json:\"type\"`\n\tImage           string                 `json:\"image\"`\n\tParams          map[string]interface{} `json:\"params\"`\n\tMessage         string                 `json:\"message\"`\n\tVersion         string                 `json:\"version\"`\n\tCreatedAt       time.Time              `json:\"createdAt\"`\n\tCodeDir         string                 `json:\"codeDir\"`\n\tAppParams       []AppParam             `json:\"appParams\"`\n\tPort            string                 `json:\"port\"`\n\tPath            string                 `json:\"path\"`\n\tExposedPorts    []request.ExposedPort  `json:\"exposedPorts\"`\n\tEnvironments    []request.Environment  `json:\"environments\"`\n\tVolumes         []request.Volume       `json:\"volumes\"`\n\tExtraHosts      []request.ExtraHost    `json:\"extraHosts\"`\n\tContainerStatus string                 `json:\"containerStatus\"`\n\tContainer       string                 `json:\"container\"`\n\tRemark          string                 `json:\"remark\"`\n}\n\ntype PackageScripts struct {\n\tName   string `json:\"name\"`\n\tScript string `json:\"script\"`\n}\n\nfunc NewRuntimeDTO(runtime model.Runtime) RuntimeDTO {\n\treturn RuntimeDTO{\n\t\tID:          runtime.ID,\n\t\tName:        runtime.Name,\n\t\tResource:    runtime.Resource,\n\t\tAppDetailID: runtime.AppDetailID,\n\t\tStatus:      runtime.Status,\n\t\tType:        runtime.Type,\n\t\tImage:       runtime.Image,\n\t\tMessage:     runtime.Message,\n\t\tCreatedAt:   runtime.CreatedAt,\n\t\tCodeDir:     runtime.CodeDir,\n\t\tVersion:     runtime.Version,\n\t\tPort:        runtime.Port,\n\t\tPath:        runtime.GetPath(),\n\t\tContainer:   runtime.ContainerName,\n\t\tRemark:      runtime.Remark,\n\t}\n}\n\ntype NodeModule struct {\n\tName        string `json:\"name\"`\n\tVersion     string `json:\"version\"`\n\tLicense     string `json:\"license\"`\n\tDescription string `json:\"description\"`\n}\n\ntype SupportExtension struct {\n\tName        string   `json:\"name\"`\n\tDescription string   `json:\"description\"`\n\tInstalled   bool     `json:\"installed\"`\n\tCheck       string   `json:\"check\"`\n\tVersions    []string `json:\"versions\"`\n\tFile        string   `json:\"file\"`\n}\n\ntype PHPExtensionRes struct {\n\tExtensions        []string           `json:\"extensions\"`\n\tSupportExtensions []SupportExtension `json:\"supportExtensions\"`\n}\n\ntype FpmStatusItem struct {\n\tKey   string      `json:\"key\"`\n\tValue interface{} `json:\"value\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/system.go",
    "content": "package response\n\ntype ComponentInfo struct {\n\tExists  bool   `json:\"exists\"`\n\tVersion string `json:\"version\"`\n\tPath    string `json:\"path\"`\n\tError   string `json:\"error\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/task.go",
    "content": "package response\n"
  },
  {
    "path": "agent/app/dto/response/tensorrt_llm.go",
    "content": "package response\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype TensorRTLLMsRes struct {\n\tItems []TensorRTLLMDTO `json:\"items\"`\n\tTotal int64            `json:\"total\"`\n}\n\ntype TensorRTLLMDTO struct {\n\tmodel.TensorRTLLM\n\tVersion  string `json:\"version\"`\n\tModel    string `json:\"model\"`\n\tDir      string `json:\"dir\"`\n\tModelDir string `json:\"modelDir\"`\n\tImage    string `json:\"image\"`\n\tCommand  string `json:\"command\"`\n\n\tExposedPorts []request.ExposedPort `json:\"exposedPorts\"`\n\tEnvironments []request.Environment `json:\"environments\"`\n\tVolumes      []request.Volume      `json:\"volumes\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/website.go",
    "content": "package response\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype WebsiteDTO struct {\n\tmodel.Website\n\tErrorLogPath  string `json:\"errorLogPath\"`\n\tAccessLogPath string `json:\"accessLogPath\"`\n\tSitePath      string `json:\"sitePath\"`\n\tAppName       string `json:\"appName\"`\n\tRuntimeName   string `json:\"runtimeName\"`\n\tRuntimeType   string `json:\"runtimeType\"`\n\tSiteDir       string `json:\"siteDir\"`\n\tOpenBaseDir   bool   `json:\"openBaseDir\"`\n\tAlgorithm     string `json:\"algorithm\"`\n\tUDP           bool   `json:\"udp\"`\n\n\tServers []dto.NginxUpstreamServer `json:\"servers\"`\n}\n\ntype WebsiteRes struct {\n\tID            uint      `json:\"id\"`\n\tCreatedAt     time.Time `json:\"createdAt\"`\n\tProtocol      string    `json:\"protocol\"`\n\tPrimaryDomain string    `json:\"primaryDomain\"`\n\tType          string    `json:\"type\"`\n\tAlias         string    `json:\"alias\"`\n\tRemark        string    `json:\"remark\"`\n\tStatus        string    `json:\"status\"`\n\tExpireDate    time.Time `json:\"expireDate\"`\n\tSitePath      string    `json:\"sitePath\"`\n\tAppName       string    `json:\"appName\"`\n\tRuntimeName   string    `json:\"runtimeName\"`\n\tSSLExpireDate time.Time `json:\"sslExpireDate\"`\n\tSSLStatus     string    `json:\"sslStatus\"`\n\tAppInstallID  uint      `json:\"appInstallId\"`\n\tChildSites    []string  `json:\"childSites\"`\n\tParentSite    string    `json:\"parentSite\"`\n\tRuntimeType   string    `json:\"runtimeType\"`\n\tFavorite      bool      `json:\"favorite\"`\n\tIPV6          bool      `json:\"IPV6\"`\n}\n\ntype WebsiteOption struct {\n\tID            uint   `json:\"id\"`\n\tPrimaryDomain string `json:\"primaryDomain\"`\n\tAlias         string `json:\"alias\"`\n}\n\ntype WebsitePreInstallCheck struct {\n\tName    string `json:\"name\"`\n\tStatus  string `json:\"status\"`\n\tVersion string `json:\"version\"`\n\tAppName string `json:\"appName\"`\n}\n\ntype WebsiteNginxConfig struct {\n\tEnable bool         `json:\"enable\"`\n\tParams []NginxParam `json:\"params\"`\n}\n\ntype WebsiteHTTPS struct {\n\tEnable                bool             `json:\"enable\"`\n\tHttpConfig            string           `json:\"httpConfig\"`\n\tSSL                   model.WebsiteSSL `json:\"SSL\"`\n\tSSLProtocol           []string         `json:\"SSLProtocol\"`\n\tAlgorithm             string           `json:\"algorithm\"`\n\tHsts                  bool             `json:\"hsts\"`\n\tHstsIncludeSubDomains bool             `json:\"hstsIncludeSubDomains\"`\n\tHttpsPorts            []int            `json:\"httpsPorts\"`\n\tHttpsPort             string           `json:\"httpsPort\"`\n\tHttp3                 bool             `json:\"http3\"`\n}\n\ntype WebsiteLog struct {\n\tEnable  bool   `json:\"enable\"`\n\tContent string `json:\"content\"`\n\tEnd     bool   `json:\"end\"`\n\tPath    string `json:\"path\"`\n}\n\ntype PHPConfig struct {\n\tParams           map[string]string `json:\"params\"`\n\tDisableFunctions []string          `json:\"disableFunctions\"`\n\tUploadMaxSize    string            `json:\"uploadMaxSize\"`\n\tMaxExecutionTime string            `json:\"maxExecutionTime\"`\n}\n\ntype NginxRewriteRes struct {\n\tContent string `json:\"content\"`\n}\n\ntype WebsiteDirConfig struct {\n\tDirs      []string `json:\"dirs\"`\n\tUser      string   `json:\"user\"`\n\tUserGroup string   `json:\"userGroup\"`\n\tMsg       string   `json:\"msg\"`\n}\n\ntype WebsiteHtmlRes struct {\n\tContent string `json:\"content\"`\n}\n\ntype WebsiteRealIP struct {\n\tWebsiteID uint   `json:\"websiteID\" validate:\"required\"`\n\tOpen      bool   `json:\"open\"`\n\tIPFrom    string `json:\"ipFrom\"`\n\tIPHeader  string `json:\"ipHeader\"`\n\tIPOther   string `json:\"ipOther\"`\n}\n\ntype Resource struct {\n\tName       string      `json:\"name\"`\n\tType       string      `json:\"type\"`\n\tResourceID uint        `json:\"resourceID\"`\n\tDetail     interface{} `json:\"detail\"`\n}\n\ntype Database struct {\n\tName         string `json:\"name\"`\n\tDatabaseName string `json:\"databaseName\"`\n\tType         string `json:\"type\"`\n\tFrom         string `json:\"from\"`\n\tID           uint   `json:\"id\"`\n}\n"
  },
  {
    "path": "agent/app/dto/response/website_ssl.go",
    "content": "package response\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/model\"\n\ntype WebsiteSSLDTO struct {\n\tmodel.WebsiteSSL\n\tLogPath string `json:\"logPath\"`\n}\n\ntype WebsiteDNSRes struct {\n\tKey    string `json:\"resolve\"`\n\tValue  string `json:\"value\"`\n\tDomain string `json:\"domain\"`\n\tErr    string `json:\"err\"`\n}\n\ntype WebsiteAcmeAccountDTO struct {\n\tmodel.WebsiteAcmeAccount\n}\n\ntype WebsiteDnsAccountDTO struct {\n\tmodel.WebsiteDnsAccount\n\tAuthorization map[string]string `json:\"authorization\"`\n}\n\ntype WebsiteCADTO struct {\n\tmodel.WebsiteCA\n\tCommonName       string `json:\"commonName\" `\n\tCountry          string `json:\"country\"`\n\tOrganization     string `json:\"organization\"`\n\tOrganizationUint string `json:\"organizationUint\"`\n\tProvince         string `json:\"province\" `\n\tCity             string `json:\"city\"`\n}\n"
  },
  {
    "path": "agent/app/dto/setting.go",
    "content": "package dto\n\ntype SettingInfo struct {\n\tDockerSockPath string `json:\"dockerSockPath\"`\n\tSystemVersion  string `json:\"systemVersion\"`\n\tSystemIP       string `json:\"systemIP\"`\n\n\tLocalTime string `json:\"localTime\"`\n\tTimeZone  string `json:\"timeZone\"`\n\tNtpSite   string `json:\"ntpSite\"`\n\n\tDefaultNetwork string `json:\"defaultNetwork\"`\n\tDefaultIO      string `json:\"defaultIO\"`\n\tLastCleanTime  string `json:\"lastCleanTime\"`\n\tLastCleanSize  string `json:\"lastCleanSize\"`\n\tLastCleanData  string `json:\"lastCleanData\"`\n\n\tMonitorStatus    string `json:\"monitorStatus\"`\n\tMonitorInterval  string `json:\"monitorInterval\"`\n\tMonitorStoreDays string `json:\"monitorStoreDays\"`\n\n\tAppStoreVersion      string `json:\"appStoreVersion\"`\n\tAppStoreLastModified string `json:\"appStoreLastModified\"`\n\tAppStoreSyncStatus   string `json:\"appStoreSyncStatus\"`\n\n\tFileRecycleBin string `json:\"fileRecycleBin\"`\n}\n\ntype SettingUpdate struct {\n\tKey   string `json:\"key\" validate:\"required\"`\n\tValue string `json:\"value\"`\n}\n\ntype SyncTime struct {\n\tNtpSite string `json:\"ntpSite\" validate:\"required\"`\n}\n\ntype CleanData struct {\n\tSystemClean    []CleanTree `json:\"systemClean\"`\n\tBackupClean    []CleanTree `json:\"backupClean\"`\n\tUploadClean    []CleanTree `json:\"uploadClean\"`\n\tDownloadClean  []CleanTree `json:\"downloadClean\"`\n\tSystemLogClean []CleanTree `json:\"systemLogClean\"`\n\tContainerClean []CleanTree `json:\"containerClean\"`\n}\n\ntype CleanTree struct {\n\tID       string      `json:\"id\"`\n\tLabel    string      `json:\"label\"`\n\tChildren []CleanTree `json:\"children\"`\n\n\tType string `json:\"type\"`\n\tName string `json:\"name\"`\n\n\tSize        uint64 `json:\"size\"`\n\tIsCheck     bool   `json:\"isCheck\"`\n\tIsRecommend bool   `json:\"isRecommend\"`\n}\n\ntype Clean struct {\n\tTreeType string `json:\"treeType\"`\n\tName     string `json:\"name\"`\n\tSize     uint64 `json:\"size\"`\n}\n\ntype SSHDefaultConn struct {\n\tWithReset   bool   `json:\"withReset\"`\n\tDefaultConn string `json:\"defaultConn\"`\n}\ntype SSHConnData struct {\n\tAddr       string `json:\"addr\" validate:\"required\"`\n\tPort       uint   `json:\"port\" validate:\"required,number,max=65535,min=1\"`\n\tUser       string `json:\"user\" validate:\"required\"`\n\tAuthMode   string `json:\"authMode\" validate:\"oneof=password key\"`\n\tPassword   string `json:\"password\"`\n\tPrivateKey string `json:\"privateKey\"`\n\tPassPhrase string `json:\"passPhrase\"`\n\n\tLocalSSHConnShow string `json:\"localSSHConnShow\"`\n}\n\ntype SystemProxy struct {\n\tType     string `json:\"type\"`\n\tURL      string `json:\"url\"`\n\tPort     string `json:\"port\"`\n\tUser     string `json:\"user\"`\n\tPassword string `json:\"password\"`\n}\n\ntype TerminalAIInfo struct {\n\tAIStatus       string `json:\"aiStatus\"`\n\tAIAccountID    string `json:\"aiAccountId\"`\n\tAIPrefix       string `json:\"aiPrefix\"`\n\tAIRiskCommands string `json:\"aiRiskCommands\"`\n}\n\ntype CommonDescription struct {\n\tID          string `json:\"id\" validate:\"required\"`\n\tType        string `json:\"type\" validate:\"required\"`\n\tDetailType  string `json:\"detailType\"`\n\tIsPinned    bool   `json:\"isPinned\"`\n\tDescription string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/dto/snapshot.go",
    "content": "package dto\n\nimport \"time\"\n\ntype PageSnapshot struct {\n\tPageInfo\n\tInfo    string `json:\"info\"`\n\tOrderBy string `json:\"orderBy\" validate:\"required,oneof=name createdAt\"`\n\tOrder   string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n}\n\ntype SnapshotCreate struct {\n\tID                uint   `json:\"id\"`\n\tName              string `json:\"name\"`\n\tTaskID            string `json:\"taskID\"`\n\tSourceAccountIDs  string `json:\"sourceAccountIDs\" validate:\"required\"`\n\tDownloadAccountID uint   `json:\"downloadAccountID\" validate:\"required\"`\n\tDescription       string `json:\"description\" validate:\"max=256\"`\n\tSecret            string `json:\"secret\"`\n\tInterruptStep     string `json:\"interruptStep\"`\n\tTimeout           uint   `json:\"timeout\"`\n\n\tAppData    []DataTree `json:\"appData\"`\n\tBackupData []DataTree `json:\"backupData\"`\n\tPanelData  []DataTree `json:\"panelData\"`\n\n\tWithDockerConf   bool `json:\"withDockerConf\"`\n\tWithMonitorData  bool `json:\"withMonitorData\"`\n\tWithLoginLog     bool `json:\"withLoginLog\"`\n\tWithOperationLog bool `json:\"withOperationLog\"`\n\tWithSystemLog    bool `json:\"withSystemLog\"`\n\tWithTaskLog      bool `json:\"withTaskLog\"`\n\n\tIgnoreFiles []string `json:\"ignoreFiles\"`\n}\n\ntype SnapshotData struct {\n\tAppData    []DataTree `json:\"appData\"`\n\tBackupData []DataTree `json:\"backupData\"`\n\tPanelData  []DataTree `json:\"panelData\"`\n\n\tWithDockerConf   bool     `json:\"withDockerConf\"`\n\tWithMonitorData  bool     `json:\"withMonitorData\"`\n\tWithLoginLog     bool     `json:\"withLoginLog\"`\n\tWithOperationLog bool     `json:\"withOperationLog\"`\n\tWithSystemLog    bool     `json:\"withSystemLog\"`\n\tWithTaskLog      bool     `json:\"withTaskLog\"`\n\tIgnoreFiles      []string `json:\"ignoreFiles\"`\n}\ntype DataTree struct {\n\tID        string `json:\"id\"`\n\tLabel     string `json:\"label\"`\n\tKey       string `json:\"key\"`\n\tName      string `json:\"name\"`\n\tIsLocal   bool   `json:\"isLocal\"`\n\tSize      uint64 `json:\"size\"`\n\tIsCheck   bool   `json:\"isCheck\"`\n\tIsDisable bool   `json:\"isDisable\"`\n\n\tPath string `json:\"path\"`\n\n\tRelationItemID string     `json:\"relationItemID\"`\n\tChildren       []DataTree `json:\"children\"`\n}\ntype SnapshotRecover struct {\n\tIsNew      bool   `json:\"isNew\"`\n\tReDownload bool   `json:\"reDownload\"`\n\tID         uint   `json:\"id\" validate:\"required\"`\n\tTaskID     string `json:\"taskID\"`\n\tSecret     string `json:\"secret\"`\n}\ntype SnapshotBatchDelete struct {\n\tDeleteWithFile bool   `json:\"deleteWithFile\"`\n\tIds            []uint `json:\"ids\" validate:\"required\"`\n}\n\ntype SnapshotImport struct {\n\tBackupAccountID uint     `json:\"backupAccountID\"`\n\tNames           []string `json:\"names\"`\n\tDescription     string   `json:\"description\" validate:\"max=256\"`\n}\n\ntype SnapshotInfo struct {\n\tID              uint      `json:\"id\"`\n\tName            string    `json:\"name\"`\n\tDescription     string    `json:\"description\" validate:\"max=256\"`\n\tSourceAccounts  []string  `json:\"sourceAccounts\"`\n\tDownloadAccount string    `json:\"downloadAccount\"`\n\tStatus          string    `json:\"status\"`\n\tMessage         string    `json:\"message\"`\n\tCreatedAt       time.Time `json:\"createdAt\"`\n\tVersion         string    `json:\"version\"`\n\tSize            int64     `json:\"size\"`\n\n\tTaskID         string `json:\"taskID\"`\n\tTaskRecoverID  string `json:\"taskRecoverID\"`\n\tTaskRollbackID string `json:\"taskRollbackID\"`\n\n\tInterruptStep    string `json:\"interruptStep\"`\n\tRecoverStatus    string `json:\"recoverStatus\"`\n\tRecoverMessage   string `json:\"recoverMessage\"`\n\tLastRecoveredAt  string `json:\"lastRecoveredAt\"`\n\tRollbackStatus   string `json:\"rollbackStatus\"`\n\tRollbackMessage  string `json:\"rollbackMessage\"`\n\tLastRollbackedAt string `json:\"lastRollbackedAt\"`\n}\n"
  },
  {
    "path": "agent/app/dto/ssh.go",
    "content": "package dto\n\nimport \"time\"\n\ntype SSHUpdate struct {\n\tKey      string `json:\"key\" validate:\"required\"`\n\tOldValue string `json:\"oldValue\"`\n\tNewValue string `json:\"newValue\"`\n}\n\ntype SSHInfo struct {\n\tAutoStart              bool   `json:\"autoStart\"`\n\tIsExist                bool   `json:\"isExist\"`\n\tIsActive               bool   `json:\"isActive\"`\n\tMessage                string `json:\"message\"`\n\tPort                   string `json:\"port\"`\n\tListenAddress          string `json:\"listenAddress\"`\n\tPasswordAuthentication string `json:\"passwordAuthentication\"`\n\tPubkeyAuthentication   string `json:\"pubkeyAuthentication\"`\n\tPermitRootLogin        string `json:\"permitRootLogin\"`\n\tUseDNS                 string `json:\"useDNS\"`\n\tCurrentUser            string `json:\"currentUser\"`\n}\n\ntype RootCertOperate struct {\n\tID             uint   `json:\"id\"`\n\tName           string `json:\"name\"`\n\tMode           string `json:\"mode\"`\n\tEncryptionMode string `json:\"encryptionMode\" validate:\"required,oneof=rsa ed25519 ecdsa dsa\"`\n\tPassPhrase     string `json:\"passPhrase\"`\n\tPublicKey      string `json:\"publicKey\"`\n\tPrivateKey     string `json:\"privateKey\"`\n\tDescription    string `json:\"description\"`\n}\ntype RootCert struct {\n\tID             uint      `json:\"id\"`\n\tCreatedAt      time.Time `json:\"createdAt\"`\n\tName           string    `json:\"name\"`\n\tEncryptionMode string    `json:\"encryptionMode\"`\n\tPassPhrase     string    `json:\"passPhrase\"`\n\tPublicKey      string    `json:\"publicKey\"`\n\tPrivateKey     string    `json:\"privateKey\"`\n\tDescription    string    `json:\"description\"`\n}\n\ntype GenerateLoad struct {\n\tEncryptionMode string `json:\"encryptionMode\" validate:\"required,oneof=rsa ed25519 ecdsa dsa\"`\n}\n\ntype SSHConf struct {\n\tFile string `json:\"file\"`\n}\ntype SearchSSHLog struct {\n\tPageInfo\n\tInfo   string `json:\"info\"`\n\tStatus string `json:\"Status\" validate:\"required,oneof=Success Failed All\"`\n}\n\ntype SSHHistory struct {\n\tDate     time.Time `json:\"date\"`\n\tDateStr  string    `json:\"dateStr\"`\n\tArea     string    `json:\"area\"`\n\tUser     string    `json:\"user\"`\n\tAuthMode string    `json:\"authMode\"`\n\tAddress  string    `json:\"address\"`\n\tPort     string    `json:\"port\"`\n\tStatus   string    `json:\"status\"`\n\tMessage  string    `json:\"message\"`\n}\n"
  },
  {
    "path": "agent/app/model/agent.go",
    "content": "package model\n\ntype Agent struct {\n\tBaseModel\n\tName          string `json:\"name\" gorm:\"not null;unique\"`\n\tAgentType     string `json:\"agentType\" gorm:\"default:openclaw\"`\n\tProvider      string `json:\"provider\"`\n\tModel         string `json:\"model\"`\n\tAPIType       string `json:\"apiType\"`\n\tMaxTokens     int    `json:\"maxTokens\"`\n\tContextWindow int    `json:\"contextWindow\"`\n\tBaseURL       string `json:\"baseUrl\"`\n\tAPIKey        string `json:\"apiKey\"`\n\tToken         string `json:\"token\"`\n\tStatus        string `json:\"status\"`\n\tMessage       string `json:\"message\"`\n\tAppInstallID  uint   `json:\"appInstallId\"`\n\tAccountID     uint   `json:\"accountId\"`\n\tConfigPath    string `json:\"configPath\"`\n}\n"
  },
  {
    "path": "agent/app/model/agent_account.go",
    "content": "package model\n\ntype AgentAccount struct {\n\tBaseModel\n\tProvider       string `json:\"provider\"`\n\tName           string `json:\"name\"`\n\tAPIKey         string `json:\"apiKey\"`\n\tBaseURL        string `json:\"baseUrl\"`\n\tAPIType        string `json:\"apiType\"`\n\tRememberAPIKey bool   `json:\"rememberApiKey\"`\n\tVerified       bool   `json:\"verified\"`\n\tRemark         string `json:\"remark\"`\n}\n\nfunc (AgentAccount) TableName() string {\n\treturn \"agent_provider_accounts\"\n}\n"
  },
  {
    "path": "agent/app/model/agent_account_model.go",
    "content": "package model\n\ntype AgentAccountModel struct {\n\tBaseModel\n\tAccountID     uint   `json:\"accountId\" gorm:\"index\"`\n\tModel         string `json:\"model\" gorm:\"index\"`\n\tName          string `json:\"name\"`\n\tContextWindow int    `json:\"contextWindow\"`\n\tMaxTokens     int    `json:\"maxTokens\"`\n\tReasoning     bool   `json:\"reasoning\"`\n\tInput         string `json:\"input\" gorm:\"type:text\"`\n\tSortOrder     int    `json:\"sortOrder\" gorm:\"index\"`\n}\n\nfunc (AgentAccountModel) TableName() string {\n\treturn \"agent_account_models\"\n}\n"
  },
  {
    "path": "agent/app/model/ai.go",
    "content": "package model\n\ntype OllamaModel struct {\n\tBaseModel\n\n\tName    string `json:\"name\"`\n\tSize    string `json:\"size\"`\n\tFrom    string `json:\"from\"`\n\tStatus  string `json:\"status\"`\n\tMessage string `json:\"message\"`\n}\n"
  },
  {
    "path": "agent/app/model/alert.go",
    "content": "package model\n\ntype Alert struct {\n\tBaseModel\n\n\tTitle          string `gorm:\"type:varchar(256);not null\" json:\"title\"`\n\tType           string `gorm:\"type:varchar(64);not null\" json:\"type\"`\n\tCycle          uint   `gorm:\"type:integer;not null\" json:\"cycle\"`\n\tCount          uint   `gorm:\"type:integer;not null\" json:\"count\"`\n\tProject        string `gorm:\"type:varchar(64)\" json:\"project\"`\n\tStatus         string `gorm:\"type:varchar(64);not null\" json:\"status\"`\n\tMethod         string `gorm:\"type:varchar(64);not null\" json:\"method\"`\n\tSendCount      uint   `gorm:\"type:integer\" json:\"sendCount\"`\n\tAdvancedParams string `gorm:\"type:longText\" json:\"advancedParams\"`\n}\n\ntype AlertTask struct {\n\tBaseModel\n\tType      string `gorm:\"type:varchar(64);not null\" json:\"type\"`\n\tQuota     string `gorm:\"type:varchar(64)\" json:\"quota\"`\n\tQuotaType string `gorm:\"type:varchar(64)\" json:\"quotaType\"`\n\tMethod    string `gorm:\"type:varchar(64);not null;default:'sms'\" json:\"method\"`\n}\n\ntype AlertLog struct {\n\tBaseModel\n\n\tType        string `gorm:\"type:varchar(64);not null\" json:\"type\"`\n\tStatus      string `gorm:\"type:varchar(64);not null\" json:\"status\"`\n\tAlertId     uint   `gorm:\"type:integer;not null\" json:\"alertId\"`\n\tAlertDetail string `gorm:\"type:varchar(256);not null\" json:\"alertDetail\"`\n\tAlertRule   string `gorm:\"type:varchar(256);not null\" json:\"alertRule\"`\n\tCount       uint   `gorm:\"type:integer;not null\" json:\"count\"`\n\tMessage     string `gorm:\"type:varchar(256);\" json:\"message\"`\n\tRecordId    uint   `gorm:\"type:integer;\" json:\"recordId\"`\n\tLicenseId   string `gorm:\"type:varchar(256);not null;\" json:\"licenseId\" `\n\tMethod      string `gorm:\"type:varchar(64);not null;default:'sms'\" json:\"method\"`\n}\n\ntype AlertConfig struct {\n\tBaseModel\n\tType   string `gorm:\"type:varchar(64);not null\" json:\"type\"`\n\tTitle  string `gorm:\"type:varchar(64);not null\" json:\"title\"`\n\tStatus string `gorm:\"type:varchar(64);not null\" json:\"status\"`\n\tConfig string `gorm:\"type:varchar(256);not null\" json:\"config\"`\n}\n\ntype LoginLog struct {\n\tBaseModel\n\tIP      string `json:\"ip\"`\n\tAddress string `json:\"address\"`\n\tAgent   string `json:\"agent\"`\n\tStatus  string `json:\"status\"`\n\tMessage string `json:\"message\"`\n}\n"
  },
  {
    "path": "agent/app/model/app.go",
    "content": "package model\n\nimport (\n\t\"encoding/json\"\n\t\"github.com/gin-gonic/gin\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype App struct {\n\tBaseModel\n\tName                 string  `json:\"name\" gorm:\"not null\"`\n\tKey                  string  `json:\"key\" gorm:\"not null;\"`\n\tShortDescZh          string  `json:\"shortDescZh\" yaml:\"shortDescZh\"`\n\tShortDescEn          string  `json:\"shortDescEn\" yaml:\"shortDescEn\"`\n\tDescription          string  `json:\"description\"`\n\tIcon                 string  `json:\"icon\"`\n\tType                 string  `json:\"type\" gorm:\"not null\"`\n\tStatus               string  `json:\"status\" gorm:\"not null\"`\n\tRequired             string  `json:\"required\"`\n\tCrossVersionUpdate   bool    `json:\"crossVersionUpdate\" yaml:\"crossVersionUpdate\"`\n\tLimit                int     `json:\"limit\" gorm:\"not null\"`\n\tWebsite              string  `json:\"website\" gorm:\"not null\"`\n\tGithub               string  `json:\"github\" gorm:\"not null\"`\n\tDocument             string  `json:\"document\" gorm:\"not null\"`\n\tRecommend            int     `json:\"recommend\" gorm:\"not null\"`\n\tResource             string  `json:\"resource\" gorm:\"not null;default:remote\"`\n\tReadMe               string  `json:\"readMe\"`\n\tLastModified         int     `json:\"lastModified\"`\n\tArchitectures        string  `json:\"architectures\"`\n\tMemoryRequired       int     `json:\"memoryRequired\"`\n\tGpuSupport           bool    `json:\"gpuSupport\"`\n\tRequiredPanelVersion float64 `json:\"requiredPanelVersion\"`\n\tBatchInstallSupport  bool    `json:\"batchInstallSupport\" yaml:\"batchInstallSupport\"`\n\n\tDetails []AppDetail `json:\"-\" gorm:\"-:migration\"`\n\tTagsKey []string    `json:\"tags\" yaml:\"tags\" gorm:\"-\"`\n\tAppTags []AppTag    `json:\"-\" gorm:\"-:migration\"`\n}\n\nfunc (i *App) IsLocalApp() bool {\n\treturn i.Resource == constant.ResourceLocal\n}\nfunc (i *App) IsCustomApp() bool {\n\treturn i.Resource == constant.AppResourceCustom\n}\n\nfunc (i *App) GetAppResourcePath() string {\n\tif i.IsLocalApp() {\n\t\treturn filepath.Join(global.Dir.LocalAppResourceDir, strings.TrimPrefix(i.Key, \"local\"))\n\t}\n\tif i.IsCustomApp() {\n\t\treturn filepath.Join(global.Dir.CustomAppResourceDir, i.Key)\n\t}\n\treturn filepath.Join(global.Dir.RemoteAppResourceDir, i.Key)\n}\n\nfunc getLang(c *gin.Context) string {\n\tlang := c.GetHeader(\"Accept-Language\")\n\tif lang == \"\" {\n\t\tlang = \"en\"\n\t}\n\treturn lang\n}\n\nfunc (i *App) GetDescription(ctx *gin.Context) string {\n\tvar translations = make(map[string]string)\n\t_ = json.Unmarshal([]byte(i.Description), &translations)\n\tlang := strings.ToLower(getLang(ctx))\n\tif desc, ok := translations[lang]; ok && desc != \"\" {\n\t\treturn desc\n\t}\n\tif lang == \"zh\" && i.ShortDescZh != \"\" {\n\t\treturn i.ShortDescZh\n\t}\n\tif desc, ok := translations[\"en\"]; ok && desc != \"\" {\n\t\treturn desc\n\t}\n\treturn i.ShortDescEn\n}\n"
  },
  {
    "path": "agent/app/model/app_detail.go",
    "content": "package model\n\ntype AppDetail struct {\n\tBaseModel\n\tAppId               uint   `json:\"appId\" gorm:\"not null\"`\n\tVersion             string `json:\"version\" gorm:\"not null\"`\n\tParams              string `json:\"-\"`\n\tDockerCompose       string `json:\"dockerCompose\"`\n\tStatus              string `json:\"status\" gorm:\"not null\"`\n\tLastVersion         string `json:\"lastVersion\"`\n\tLastModified        int    `json:\"lastModified\"`\n\tDownloadUrl         string `json:\"downloadUrl\"`\n\tDownloadCallBackUrl string `json:\"downloadCallBackUrl\"`\n\tUpdate              bool   `json:\"update\"`\n}\n"
  },
  {
    "path": "agent/app/model/app_ignore_upgrade.go",
    "content": "package model\n\ntype AppIgnoreUpgrade struct {\n\tBaseModel\n\tAppID       uint   `json:\"appID\"`\n\tAppDetailID uint   `json:\"appDetailID\"`\n\tScope       string `json:\"scope\"`\n}\n"
  },
  {
    "path": "agent/app/model/app_install.go",
    "content": "package model\n\nimport (\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype AppInstall struct {\n\tBaseModel\n\tName          string `json:\"name\" gorm:\"not null;UNIQUE\"`\n\tAppId         uint   `json:\"appId\" gorm:\"not null\"`\n\tAppDetailId   uint   `json:\"appDetailId\" gorm:\"not null\"`\n\tVersion       string `json:\"version\" gorm:\"not null\"`\n\tParam         string `json:\"param\"`\n\tEnv           string `json:\"env\"`\n\tDockerCompose string `json:\"dockerCompose\" `\n\tStatus        string `json:\"status\" gorm:\"not null\"`\n\tDescription   string `json:\"description\"`\n\tMessage       string `json:\"message\"`\n\tContainerName string `json:\"containerName\" gorm:\"not null\"`\n\tServiceName   string `json:\"serviceName\" gorm:\"not null\"`\n\tHttpPort      int    `json:\"httpPort\"`\n\tHttpsPort     int    `json:\"httpsPort\"`\n\tWebUI         string `json:\"webUI\"`\n\tFavorite      bool   `json:\"favorite\"`\n\tSortOrder     int    `json:\"sortOrder\" gorm:\"default:0\"`\n\n\tApp App `json:\"app\" gorm:\"-:migration\"`\n}\n\nfunc (i *AppInstall) GetPath() string {\n\treturn path.Join(i.GetAppPath(), i.Name)\n}\n\nfunc (i *AppInstall) GetComposePath() string {\n\treturn path.Join(i.GetAppPath(), i.Name, \"docker-compose.yml\")\n}\n\nfunc (i *AppInstall) GetEnvPath() string {\n\treturn path.Join(i.GetAppPath(), i.Name, \".env\")\n}\n\nfunc (i *AppInstall) GetAppPath() string {\n\tif i.App.Resource == constant.AppResourceLocal {\n\t\treturn path.Join(global.Dir.LocalAppInstallDir, strings.TrimPrefix(i.App.Key, constant.AppResourceLocal))\n\t} else {\n\t\treturn path.Join(global.Dir.AppInstallDir, i.App.Key)\n\t}\n}\n"
  },
  {
    "path": "agent/app/model/app_install_resource.go",
    "content": "package model\n\ntype AppInstallResource struct {\n\tBaseModel\n\tAppInstallId uint   `json:\"appInstallId\" gorm:\"not null;\"`\n\tLinkId       uint   `json:\"linkId\"  gorm:\"not null;\"`\n\tResourceId   uint   `json:\"resourceId\"`\n\tKey          string `json:\"key\" gorm:\"not null\"`\n\tFrom         string `json:\"from\" gorm:\"not null;default:local\"`\n}\n"
  },
  {
    "path": "agent/app/model/app_launcher.go",
    "content": "package model\n\ntype AppLauncher struct {\n\tBaseModel\n\tKey string `json:\"key\"`\n}\n\ntype QuickJump struct {\n\tBaseModel\n\tName      string `json:\"name\"`\n\tAlias     string `json:\"alias\"`\n\tTitle     string `json:\"title\"`\n\tDetail    string `json:\"detail\"`\n\tRecommend int    `json:\"recommend\"`\n\tIsShow    bool   `json:\"isShow\"`\n\tRouter    string `json:\"router\"`\n}\n"
  },
  {
    "path": "agent/app/model/app_tag.go",
    "content": "package model\n\ntype AppTag struct {\n\tBaseModel\n\tAppId uint `json:\"appId\" gorm:\"not null\"`\n\tTagId uint `json:\"tagId\" gorm:\"not null\"`\n}\n"
  },
  {
    "path": "agent/app/model/backup.go",
    "content": "package model\n\ntype BackupAccount struct {\n\tBaseModel\n\tName       string `gorm:\"not null;default:''\" json:\"name\"`\n\tType       string `gorm:\"not null;default:''\" json:\"type\"`\n\tIsPublic   bool   `json:\"isPublic\"`\n\tBucket     string `json:\"bucket\"`\n\tAccessKey  string `json:\"accessKey\"`\n\tCredential string `json:\"credential\"`\n\tBackupPath string `json:\"backupPath\"`\n\tVars       string `json:\"vars\"`\n\n\tRememberAuth bool `json:\"rememberAuth\"`\n}\n\ntype BackupRecord struct {\n\tBaseModel\n\tFrom              string `json:\"from\"`\n\tCronjobID         uint   `json:\"cronjobID\"`\n\tSourceAccountIDs  string `json:\"sourceAccountIDs\"`\n\tDownloadAccountID uint   `json:\"downloadAccountID\"`\n\n\tType       string `gorm:\"not null;default:''\" json:\"type\"`\n\tName       string `gorm:\"not null;default:''\" json:\"name\"`\n\tDetailName string `json:\"detailName\"`\n\tFileDir    string `json:\"fileDir\"`\n\tFileName   string `json:\"fileName\"`\n\n\tTaskID      string `json:\"taskID\"`\n\tStatus      string `json:\"status\"`\n\tMessage     string `json:\"message\"`\n\tDescription string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/model/base.go",
    "content": "package model\n\nimport \"time\"\n\ntype BaseModel struct {\n\tID        uint      `gorm:\"primarykey;AUTO_INCREMENT\" json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n}\n"
  },
  {
    "path": "agent/app/model/clam.go",
    "content": "package model\n\nimport \"time\"\n\ntype Clam struct {\n\tBaseModel\n\n\tName             string `gorm:\"not null\" json:\"name\"`\n\tPath             string `gorm:\"not null\" json:\"path\"`\n\tInfectedStrategy string `json:\"infectedStrategy\"`\n\tInfectedDir      string `json:\"infectedDir\"`\n\tSpec             string `json:\"spec\"`\n\tRetryTimes       uint   `json:\"retryTimes\"`\n\tTimeout          uint   `json:\"timeout\"`\n\tEntryID          int    `json:\"entryID\"`\n\tDescription      string `json:\"description\"`\n\n\tStatus      string `json:\"status\"`\n\tIsExecuting bool   `json:\"isExecuting\"`\n}\n\ntype ClamRecord struct {\n\tBaseModel\n\n\tClamID        uint      `json:\"clamID\"`\n\tTaskID        string    `json:\"taskID\"`\n\tStartTime     time.Time `json:\"startTime\"`\n\tScanTime      string    `json:\"scanTime\"`\n\tInfectedFiles string    `json:\"infectedFiles\"`\n\tTotalError    string    `json:\"totalError\"`\n\tStatus        string    `json:\"status\"`\n\tMessage       string    `json:\"message\"`\n}\n"
  },
  {
    "path": "agent/app/model/compose_template.go",
    "content": "package model\n\ntype ComposeTemplate struct {\n\tBaseModel\n\n\tName        string `gorm:\"not null;unique\" json:\"name\"`\n\tDescription string `json:\"description\"`\n\tContent     string `json:\"content\"`\n}\n\ntype Compose struct {\n\tBaseModel\n\n\tName string `json:\"name\"`\n\tPath string `json:\"path\"`\n}\n"
  },
  {
    "path": "agent/app/model/cronjob.go",
    "content": "package model\n\nimport (\n\t\"time\"\n)\n\ntype Cronjob struct {\n\tBaseModel\n\n\tName       string `gorm:\"not null\" json:\"name\"`\n\tType       string `gorm:\"not null\" json:\"type\"`\n\tGroupID    uint   `json:\"groupID\"`\n\tSpecCustom bool   `json:\"specCustom\"`\n\tSpec       string `gorm:\"not null\" json:\"spec\"`\n\n\tExecutor      string `json:\"executor\"`\n\tCommand       string `json:\"command\"`\n\tContainerName string `json:\"containerName\"`\n\tScriptMode    string `json:\"scriptMode\"`\n\tScript        string `json:\"script\"`\n\tUser          string `json:\"user\"`\n\n\tScriptID       uint   `json:\"scriptID\"`\n\tWebsite        string `json:\"website\"`\n\tAppID          string `json:\"appID\"`\n\tDBType         string `json:\"dbType\"`\n\tDBName         string `json:\"dbName\"`\n\tURL            string `json:\"url\"`\n\tIsDir          bool   `json:\"isDir\"`\n\tSourceDir      string `json:\"sourceDir\"`\n\tSnapshotRule   string `json:\"snapshotRule\"`\n\tExclusionRules string `json:\"exclusionRules\"`\n\n\tSourceAccountIDs  string `json:\"sourceAccountIDs\"`\n\tDownloadAccountID uint   `json:\"downloadAccountID\"`\n\tRetryTimes        uint   `json:\"retryTimes\"`\n\tTimeout           uint   `json:\"timeout\"`\n\tIgnoreErr         bool   `json:\"ignoreErr\"`\n\tRetainCopies      uint64 `json:\"retainCopies\"`\n\tArgs              string `json:\"args\"`\n\n\tIsExecuting bool         `json:\"isExecuting\"`\n\tStatus      string       `json:\"status\"`\n\tEntryIDs    string       `json:\"entryIDs\"`\n\tRecords     []JobRecords `json:\"records\"`\n\tSecret      string       `json:\"secret\"`\n\n\tConfig string `json:\"config\"`\n}\n\ntype JobRecords struct {\n\tBaseModel\n\n\tCronjobID uint      `json:\"cronjobID\"`\n\tTaskID    string    `json:\"taskID\"`\n\tStartTime time.Time `json:\"startTime\"`\n\tInterval  float64   `json:\"interval\"`\n\tRecords   string    `json:\"records\"`\n\tFromLocal bool      `json:\"source\"`\n\tFile      string    `json:\"file\"`\n\tStatus    string    `json:\"status\"`\n\tMessage   string    `json:\"message\"`\n}\n\ntype ScriptLibrary struct {\n\tBaseModel\n\tName   string `json:\"name\" gorm:\"not null;\"`\n\tScript string `json:\"script\" gorm:\"not null;\"`\n}\n"
  },
  {
    "path": "agent/app/model/database.go",
    "content": "package model\n\ntype Database struct {\n\tBaseModel\n\tAppInstallID uint   `json:\"appInstallID\"`\n\tName         string `json:\"name\" gorm:\"not null;unique\"`\n\tType         string `json:\"type\" gorm:\"not null\"`\n\tVersion      string `json:\"version\" gorm:\"not null\"`\n\tFrom         string `json:\"from\" gorm:\"not null\"`\n\tAddress      string `json:\"address\" gorm:\"not null\"`\n\tPort         uint   `json:\"port\" gorm:\"not null\"`\n\tInitialDB    string `json:\"initialDB\"`\n\tUsername     string `json:\"username\"`\n\tPassword     string `json:\"password\"`\n\n\tSSL        bool   `json:\"ssl\"`\n\tRootCert   string `json:\"rootCert\"`\n\tClientKey  string `json:\"clientKey\"`\n\tClientCert string `json:\"clientCert\"`\n\tSkipVerify bool   `json:\"skipVerify\"`\n\n\tTimeout     uint   `json:\"timeout\"`\n\tDescription string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/model/database_mysql.go",
    "content": "package model\n\ntype DatabaseMysql struct {\n\tBaseModel\n\tName        string `json:\"name\" gorm:\"not null\"`\n\tFrom        string `json:\"from\" gorm:\"not null;default:local\"`\n\tMysqlName   string `json:\"mysqlName\" gorm:\"not null\"`\n\tFormat      string `json:\"format\" gorm:\"not null\"`\n\tCollation   string `json:\"collation\"`\n\tUsername    string `json:\"username\" gorm:\"not null\"`\n\tPassword    string `json:\"password\" gorm:\"not null\"`\n\tPermission  string `json:\"permission\" gorm:\"not null\"`\n\tIsDelete    bool   `json:\"isDelete\"`\n\tDescription string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/model/database_postgresql.go",
    "content": "package model\n\ntype DatabasePostgresql struct {\n\tBaseModel\n\tName           string `json:\"name\" gorm:\"not null\"`\n\tFrom           string `json:\"from\" gorm:\"not null;default:local\"`\n\tPostgresqlName string `json:\"postgresqlName\" gorm:\"not null\"`\n\tFormat         string `json:\"format\" gorm:\"not null\"`\n\tUsername       string `json:\"username\" gorm:\"not null\"`\n\tPassword       string `json:\"password\" gorm:\"not null\"`\n\tSuperUser      bool   `json:\"superUser\"`\n\tIsDelete       bool   `json:\"isDelete\"`\n\tDescription    string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/model/favorite.go",
    "content": "package model\n\ntype Favorite struct {\n\tBaseModel\n\tName  string `gorm:\"not null;\" json:\"name\" `\n\tPath  string `gorm:\"not null;unique\" json:\"path\"`\n\tType  string `json:\"type\"`\n\tIsDir bool   `json:\"isDir\"`\n\tIsTxt bool   `json:\"isTxt\"`\n}\n"
  },
  {
    "path": "agent/app/model/firewall.go",
    "content": "package model\n\ntype Firewall struct {\n\tBaseModel\n\n\tType    string `json:\"type\"`\n\tPort    string `json:\"port\"`    // Deprecated\n\tAddress string `json:\"address\"` // Deprecated\n\n\tChain       string `json:\"chain\"`\n\tProtocol    string `json:\"protocol\"`\n\tSrcIP       string `json:\"srcIP\"`\n\tSrcPort     string `json:\"srcPort\"`\n\tDstIP       string `json:\"dstIP\"`\n\tDstPort     string `json:\"dstPort\"`\n\tStrategy    string `gorm:\"not null\" json:\"strategy\"`\n\tDescription string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/model/ftp.go",
    "content": "package model\n\ntype Ftp struct {\n\tBaseModel\n\n\tUser        string `gorm:\"not null\" json:\"user\"`\n\tPassword    string `gorm:\"not null\" json:\"password\"`\n\tStatus      string `gorm:\"not null\" json:\"status\"`\n\tPath        string `gorm:\"not null\" json:\"path\"`\n\tDescription string `gorm:\"not null\" json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/model/group.go",
    "content": "package model\n\ntype Group struct {\n\tBaseModel\n\tIsDefault bool   `json:\"isDefault\"`\n\tName      string `json:\"name\"`\n\tType      string `json:\"type\"`\n}\n"
  },
  {
    "path": "agent/app/model/host.go",
    "content": "package model\n\ntype Host struct {\n\tBaseModel\n\n\tGroupID          uint   `gorm:\"not null\" json:\"group_id\"`\n\tName             string `gorm:\"not null\" json:\"name\"`\n\tAddr             string `gorm:\"not null\" json:\"addr\"`\n\tPort             int    `gorm:\"not null\" json:\"port\"`\n\tUser             string `gorm:\"not null\" json:\"user\"`\n\tAuthMode         string `gorm:\"not null\" json:\"authMode\"`\n\tPassword         string `json:\"password\"`\n\tPrivateKey       string `json:\"privateKey\"`\n\tPassPhrase       string `json:\"passPhrase\"`\n\tRememberPassword bool   `json:\"rememberPassword\"`\n\n\tDescription string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/model/image_repo.go",
    "content": "package model\n\ntype ImageRepo struct {\n\tBaseModel\n\n\tName        string `gorm:\"not null\" json:\"name\"`\n\tDownloadUrl string `json:\"downloadUrl\"`\n\tProtocol    string `json:\"protocol\"`\n\tUsername    string `json:\"username\"`\n\tPassword    string `json:\"password\"`\n\tAuth        bool   `json:\"auth\"`\n\n\tStatus  string `json:\"status\"`\n\tMessage string `json:\"message\"`\n}\n"
  },
  {
    "path": "agent/app/model/mcp_server.go",
    "content": "package model\n\ntype McpServer struct {\n\tBaseModel\n\tName               string `json:\"name\"`\n\tDockerCompose      string `json:\"dockerCompose\"`\n\tCommand            string `json:\"command\"`\n\tContainerName      string `json:\"containerName\"`\n\tMessage            string `json:\"message\"`\n\tPort               int    `json:\"port\"`\n\tStatus             string `json:\"status\"`\n\tEnv                string `json:\"env\"`\n\tBaseURL            string `json:\"baseUrl\"`\n\tSsePath            string `json:\"ssePath\"`\n\tWebsiteID          int    `json:\"websiteID\"`\n\tDir                string `json:\"dir\"`\n\tHostIP             string `json:\"hostIP\"`\n\tStreamableHttpPath string `json:\"streamableHttpPath\"`\n\tOutputTransport    string `json:\"outputTransport\"`\n\tType               string `json:\"type\"`\n}\n"
  },
  {
    "path": "agent/app/model/monitor.go",
    "content": "package model\n\ntype MonitorBase struct {\n\tBaseModel\n\tCpu         float64     `json:\"cpu\"`\n\tTopCPU      string      `json:\"topCPU\"`\n\tTopCPUItems interface{} `gorm:\"-\" json:\"topCPUItems\"`\n\n\tMemory      float64     `json:\"memory\"`\n\tTopMem      string      `json:\"topMem\"`\n\tTopMemItems interface{} `gorm:\"-\" json:\"topMemItems\"`\n\n\tLoadUsage float64 `json:\"loadUsage\"`\n\tCpuLoad1  float64 `json:\"cpuLoad1\"`\n\tCpuLoad5  float64 `json:\"cpuLoad5\"`\n\tCpuLoad15 float64 `json:\"cpuLoad15\"`\n}\n\ntype MonitorIO struct {\n\tBaseModel\n\tName  string `json:\"name\"`\n\tRead  uint64 `json:\"read\"`\n\tWrite uint64 `json:\"write\"`\n\tCount uint64 `json:\"count\"`\n\tTime  uint64 `json:\"time\"`\n}\n\ntype MonitorNetwork struct {\n\tBaseModel\n\tName string  `json:\"name\"`\n\tUp   float64 `json:\"up\"`\n\tDown float64 `json:\"down\"`\n}\n\ntype MonitorGPU struct {\n\tBaseModel\n\tProductName   string  `json:\"productName\"`\n\tGPUUtil       float64 `json:\"gpuUtil\"`\n\tTemperature   float64 `json:\"temperature\"`\n\tPowerDraw     float64 `json:\"powerDraw\"`\n\tMaxPowerLimit float64 `json:\"maxPowerLimit\"`\n\tMemUsed       float64 `json:\"memUsed\"`\n\tMemTotal      float64 `json:\"memTotal\"`\n\tFanSpeed      int     `json:\"fanSpeed\"`\n\tProcesses     string  `json:\"processes\"`\n}\n"
  },
  {
    "path": "agent/app/model/php_extensions.go",
    "content": "package model\n\ntype PHPExtensions struct {\n\tBaseModel\n\tName       string `json:\"name\" gorm:\"not null\"`\n\tExtensions string `json:\"extensions\" gorm:\"not null\"`\n}\n"
  },
  {
    "path": "agent/app/model/runtime.go",
    "content": "package model\n\nimport (\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype Runtime struct {\n\tBaseModel\n\tName          string `gorm:\"not null\" json:\"name\"`\n\tAppDetailID   uint   `json:\"appDetailID\"`\n\tImage         string `json:\"image\"`\n\tWorkDir       string `json:\"workDir\"`\n\tDockerCompose string `json:\"dockerCompose\"`\n\tEnv           string `json:\"env\"`\n\tParams        string `json:\"params\"`\n\tVersion       string `gorm:\"not null\" json:\"version\"`\n\tType          string `gorm:\"not null\" json:\"type\"`\n\tStatus        string `gorm:\"not null\" json:\"status\"`\n\tResource      string `gorm:\"not null\" json:\"resource\"`\n\tPort          string `json:\"port\"`\n\tMessage       string `json:\"message\"`\n\tCodeDir       string `json:\"codeDir\"`\n\tContainerName string `json:\"containerName\"`\n\tRemark        string `json:\"remark\"`\n}\n\nfunc (r *Runtime) GetComposePath() string {\n\treturn path.Join(r.GetPath(), \"docker-compose.yml\")\n}\n\nfunc (r *Runtime) GetEnvPath() string {\n\treturn path.Join(r.GetPath(), \".env\")\n}\n\nfunc (r *Runtime) GetPath() string {\n\treturn path.Join(global.Dir.RuntimeDir, r.Type, r.Name)\n}\n\nfunc (r *Runtime) GetFPMPath() string {\n\treturn path.Join(global.Dir.RuntimeDir, r.Type, r.Name, \"conf\", \"php-fpm.conf\")\n}\n\nfunc (r *Runtime) GetPHPPath() string {\n\treturn path.Join(global.Dir.RuntimeDir, r.Type, r.Name, \"conf\", \"php.ini\")\n}\n\nfunc (r *Runtime) GetLogPath() string {\n\treturn path.Join(r.GetPath(), \"build.log\")\n}\n\nfunc (r *Runtime) GetSlowLogPath() string {\n\treturn path.Join(r.GetPath(), \"log\", \"fpm.slow.log\")\n}\n"
  },
  {
    "path": "agent/app/model/setting.go",
    "content": "package model\n\ntype Setting struct {\n\tBaseModel\n\tKey   string `json:\"key\" gorm:\"not null;\"`\n\tValue string `json:\"value\"`\n\tAbout string `json:\"about\"`\n}\n\ntype CommonDescription struct {\n\tID          string `json:\"id\"`\n\tType        string `json:\"type\"`\n\tDetailType  string `json:\"detailType\"`\n\tIsPinned    bool   `json:\"isPinned\"`\n\tDescription string `json:\"description\"`\n}\n\ntype NodeInfo struct {\n\tScope     string `json:\"scope\"`\n\tBaseDir   string `json:\"baseDir\"`\n\tNodePort  uint   `json:\"nodePort\"`\n\tVersion   string `json:\"version\"`\n\tServerCrt string `json:\"serverCrt\"`\n\tServerKey string `json:\"serverKey\"`\n}\n\ntype LocalConnInfo struct {\n\tAddr       string `json:\"addr\"`\n\tPort       uint   `json:\"port\"`\n\tUser       string `json:\"user\"`\n\tAuthMode   string `json:\"authMode\"`\n\tPassword   string `json:\"password\"`\n\tPrivateKey string `json:\"privateKey\"`\n\tPassPhrase string `json:\"passPhrase\"`\n}\n"
  },
  {
    "path": "agent/app/model/snapshot.go",
    "content": "package model\n\ntype Snapshot struct {\n\tBaseModel\n\tName              string `json:\"name\" gorm:\"not null;unique\"`\n\tSecret            string `json:\"secret\"`\n\tDescription       string `json:\"description\"`\n\tSourceAccountIDs  string `json:\"sourceAccountIDs\"`\n\tDownloadAccountID uint   `json:\"downloadAccountID\"`\n\tStatus            string `json:\"status\"`\n\tMessage           string `json:\"message\"`\n\tVersion           string `json:\"version\"`\n\tTimeout           uint   `json:\"timeout\"`\n\n\tTaskID         string `json:\"taskID\"`\n\tTaskRecoverID  string `json:\"taskRecoverID\"`\n\tTaskRollbackID string `json:\"taskRollbackID\"`\n\n\tAppData    string `json:\"appData\"`\n\tPanelData  string `json:\"panelData\"`\n\tBackupData string `json:\"backupData\"`\n\n\tWithDockerConf   bool   `json:\"withDockerConf\"`\n\tWithMonitorData  bool   `json:\"withMonitorData\"`\n\tWithLoginLog     bool   `json:\"withLoginLog\"`\n\tWithOperationLog bool   `json:\"withOperationLog\"`\n\tWithSystemLog    bool   `json:\"withSystemLog\"`\n\tWithTaskLog      bool   `json:\"withTaskLog\"`\n\tIgnoreFiles      string `json:\"ignoreFiles\"`\n\n\tInterruptStep   string `json:\"interruptStep\"`\n\tRecoverStatus   string `json:\"recoverStatus\"`\n\tRecoverMessage  string `json:\"recoverMessage\"`\n\tRollbackStatus  string `json:\"rollbackStatus\"`\n\tRollbackMessage string `json:\"rollbackMessage\"`\n}\n"
  },
  {
    "path": "agent/app/model/ssh.go",
    "content": "package model\n\ntype RootCert struct {\n\tBaseModel\n\tName           string `json:\"name\" gorm:\"not null;\"`\n\tEncryptionMode string `json:\"encryptionMode\"`\n\tPassPhrase     string `json:\"passPhrase\"`\n\tPublicKeyPath  string `json:\"publicKeyPath\"`\n\tPrivateKeyPath string `json:\"privateKeyPath\"`\n\tDescription    string `json:\"description\"`\n}\n"
  },
  {
    "path": "agent/app/model/tag.go",
    "content": "package model\n\ntype Tag struct {\n\tBaseModel\n\tKey          string `json:\"key\" gorm:\"not null\"`\n\tName         string `json:\"name\" gorm:\"not null\"`\n\tSort         int    `json:\"sort\" gorm:\"not null;default:1\"`\n\tTranslations string `json:\"translations\"`\n}\n"
  },
  {
    "path": "agent/app/model/task.go",
    "content": "package model\n\nimport \"time\"\n\ntype Task struct {\n\tID             string    `gorm:\"primarykey;\" json:\"id\"`\n\tName           string    `json:\"name\"`\n\tType           string    `json:\"type\"`\n\tOperate        string    `json:\"operate\"`\n\tLogFile        string    `json:\"logFile\"`\n\tStatus         string    `json:\"status\"`\n\tErrorMsg       string    `json:\"errorMsg\"`\n\tOperationLogID uint      `json:\"operationLogID\"`\n\tResourceID     uint      `json:\"resourceID\"`\n\tCurrentStep    string    `json:\"currentStep\"`\n\tEndAt          time.Time `json:\"endAt\"`\n\tCreatedAt      time.Time `json:\"createdAt\"`\n}\n"
  },
  {
    "path": "agent/app/model/tensorrt_llm.go",
    "content": "package model\n\ntype TensorRTLLM struct {\n\tBaseModel\n\tName          string `json:\"name\"`\n\tDockerCompose string `json:\"dockerCompose\"`\n\tContainerName string `json:\"containerName\"`\n\tMessage       string `json:\"message\"`\n\tStatus        string `json:\"status\"`\n\tEnv           string `json:\"env\"`\n\tTaskID        string `json:\"taskID\"`\n\tModelType     string `json:\"modelType\"`\n\tModelSpeedup  bool   `json:\"modelSpeedup\"`\n}\n"
  },
  {
    "path": "agent/app/model/website.go",
    "content": "package model\n\nimport \"time\"\n\ntype Website struct {\n\tBaseModel\n\tProtocol      string    `gorm:\"not null\" json:\"protocol\"`\n\tPrimaryDomain string    `gorm:\"not null\" json:\"primaryDomain\"`\n\tType          string    `gorm:\"not null\" json:\"type\"`\n\tAlias         string    `gorm:\"not null\" json:\"alias\"`\n\tRemark        string    `json:\"remark\"`\n\tStatus        string    `gorm:\"not null\" json:\"status\"`\n\tHttpConfig    string    `gorm:\"not null\" json:\"httpConfig\"`\n\tExpireDate    time.Time `json:\"expireDate\"`\n\n\tProxy         string `json:\"proxy\"`\n\tProxyType     string `json:\"proxyType\"`\n\tSiteDir       string `json:\"siteDir\"`\n\tErrorLog      bool   `json:\"errorLog\"`\n\tAccessLog     bool   `json:\"accessLog\"`\n\tDefaultServer bool   `json:\"defaultServer\"`\n\tIPV6          bool   `json:\"IPV6\"`\n\tRewrite       string `json:\"rewrite\"`\n\n\tWebsiteGroupID  uint `json:\"webSiteGroupId\"`\n\tWebsiteSSLID    uint `json:\"webSiteSSLId\"`\n\tRuntimeID       uint `json:\"runtimeID\"`\n\tAppInstallID    uint `json:\"appInstallId\"`\n\tFtpID           uint `json:\"ftpId\"`\n\tParentWebsiteID uint `json:\"parentWebsiteID\"`\n\n\tUser  string `json:\"user\"`\n\tGroup string `json:\"group\"`\n\n\tDbType string `json:\"dbType\"`\n\tDbID   uint   `json:\"dbID\"`\n\n\tFavorite bool `json:\"favorite\"`\n\n\tStreamPorts string `json:\"streamPorts\"`\n\n\tDomains    []WebsiteDomain `json:\"domains\" gorm:\"-:migration\"`\n\tWebsiteSSL WebsiteSSL      `json:\"webSiteSSL\" gorm:\"-:migration\"`\n}\n\nfunc (w Website) TableName() string {\n\treturn \"websites\"\n}\n"
  },
  {
    "path": "agent/app/model/website_acme_account.go",
    "content": "package model\n\ntype WebsiteAcmeAccount struct {\n\tBaseModel\n\tEmail      string `gorm:\"not null\" json:\"email\"`\n\tURL        string `gorm:\"not null\" json:\"url\"`\n\tPrivateKey string `gorm:\"not null\" json:\"-\"`\n\tType       string `gorm:\"not null;default:letsencrypt\" json:\"type\"`\n\tEabKid     string `json:\"eabKid\"`\n\tEabHmacKey string `json:\"eabHmacKey\"`\n\tKeyType    string `gorm:\"not null;default:2048\" json:\"keyType\"`\n\tUseProxy   bool   `gorm:\"default:false\" json:\"useProxy\"`\n\tCaDirURL   string `json:\"caDirURL\"`\n\tUseEAB     bool   `json:\"useEAB\"`\n}\n\nfunc (w WebsiteAcmeAccount) TableName() string {\n\treturn \"website_acme_accounts\"\n}\n"
  },
  {
    "path": "agent/app/model/website_ca.go",
    "content": "package model\n\ntype WebsiteCA struct {\n\tBaseModel\n\tCSR        string `gorm:\"not null;\" json:\"csr\"`\n\tName       string `gorm:\"not null;\" json:\"name\"`\n\tPrivateKey string `gorm:\"not null\" json:\"privateKey\"`\n\tKeyType    string `gorm:\"not null;default:2048\" json:\"keyType\"`\n}\n"
  },
  {
    "path": "agent/app/model/website_dns_account.go",
    "content": "package model\n\ntype WebsiteDnsAccount struct {\n\tBaseModel\n\tName          string `gorm:\"not null\" json:\"name\"`\n\tType          string `gorm:\"not null\" json:\"type\"`\n\tAuthorization string `gorm:\"not null\" json:\"-\"`\n}\n\nfunc (w WebsiteDnsAccount) TableName() string {\n\treturn \"website_dns_accounts\"\n}\n"
  },
  {
    "path": "agent/app/model/website_domain.go",
    "content": "package model\n\ntype WebsiteDomain struct {\n\tBaseModel\n\tWebsiteID uint   `gorm:\"column:website_id;not null;\" json:\"websiteId\"`\n\tDomain    string `gorm:\"not null\" json:\"domain\"`\n\tSSL       bool   `json:\"ssl\"`\n\tPort      int    `json:\"port\"`\n}\n\nfunc (w WebsiteDomain) TableName() string {\n\treturn \"website_domains\"\n}\n"
  },
  {
    "path": "agent/app/model/website_ssl.go",
    "content": "package model\n\nimport (\n\t\"fmt\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype WebsiteSSL struct {\n\tBaseModel\n\tPrimaryDomain  string    `json:\"primaryDomain\"`\n\tPrivateKey     string    `json:\"privateKey\"`\n\tPem            string    `json:\"pem\"`\n\tDomains        string    `json:\"domains\"`\n\tCertURL        string    `json:\"certURL\"`\n\tType           string    `json:\"type\"`\n\tProvider       string    `json:\"provider\"`\n\tOrganization   string    `json:\"organization\"`\n\tDnsAccountID   uint      `json:\"dnsAccountId\"`\n\tAcmeAccountID  uint      `gorm:\"column:acme_account_id\" json:\"acmeAccountId\" `\n\tCaID           uint      `json:\"caId\"`\n\tAutoRenew      bool      `json:\"autoRenew\"`\n\tExpireDate     time.Time `json:\"expireDate\"`\n\tStartDate      time.Time `json:\"startDate\"`\n\tStatus         string    `json:\"status\"`\n\tMessage        string    `json:\"message\"`\n\tKeyType        string    `json:\"keyType\"`\n\tPushDir        bool      `json:\"pushDir\"`\n\tDir            string    `json:\"dir\"`\n\tDescription    string    `json:\"description\"`\n\tSkipDNS        bool      `json:\"skipDNS\"`\n\tNameserver1    string    `json:\"nameserver1\"`\n\tNameserver2    string    `json:\"nameserver2\"`\n\tDisableCNAME   bool      `json:\"disableCNAME\"`\n\tExecShell      bool      `json:\"execShell\"`\n\tShell          string    `json:\"shell\"`\n\tMasterSSLID    uint      `json:\"masterSslId\"`\n\tNodes          string    `json:\"nodes\"`\n\tPushNode       bool      `json:\"pushNode\"`\n\tPrivateKeyPath string    `json:\"privateKeyPath\"`\n\tCertPath       string    `json:\"certPath\"`\n\tIsIp           bool      `json:\"isIP\"`\n\n\tAcmeAccount WebsiteAcmeAccount `json:\"acmeAccount\" gorm:\"-:migration\"`\n\tDnsAccount  WebsiteDnsAccount  `json:\"dnsAccount\" gorm:\"-:migration\"`\n\tWebsites    []Website          `json:\"websites\" gorm:\"-:migration\"`\n}\n\nfunc (w WebsiteSSL) TableName() string {\n\treturn \"website_ssls\"\n}\n\nfunc (w WebsiteSSL) GetLogPath() string {\n\treturn path.Join(global.Dir.SSLLogDir, fmt.Sprintf(\"%s-ssl-%d.log\", w.PrimaryDomain, w.ID))\n}\n"
  },
  {
    "path": "agent/app/provider/catalog.go",
    "content": "package provider\n\nimport (\n\t\"strings\"\n)\n\ntype Model struct {\n\tID            string\n\tName          string\n\tContextWindow int\n\tMaxTokens     int\n\tReasoning     bool\n\tInput         []string\n}\n\ntype Meta struct {\n\tKey            string\n\tDisplayName    string\n\tSort           uint\n\tDefaultBaseURL string\n\tEnvKey         string\n\tModels         []Model\n}\n\nvar catalog = map[string]Meta{\n\t\"custom\": {\n\t\tKey:            \"custom\",\n\t\tDisplayName:    \"Custom\",\n\t\tSort:           10,\n\t\tDefaultBaseURL: \"\",\n\t\tEnvKey:         \"CUSTOM_API_KEY\",\n\t\tModels:         []Model{},\n\t},\n\t\"ollama\": {\n\t\tKey:         \"ollama\",\n\t\tDisplayName: \"Ollama\",\n\t\tSort:        15,\n\t},\n\t\"vllm\": {\n\t\tKey:            \"vllm\",\n\t\tDisplayName:    \"vLLM\",\n\t\tSort:           20,\n\t\tDefaultBaseURL: \"\",\n\t\tEnvKey:         \"VLLM_API_KEY\",\n\t\tModels:         []Model{},\n\t},\n\t\"deepseek\": {\n\t\tKey:            \"deepseek\",\n\t\tDisplayName:    \"DeepSeek\",\n\t\tSort:           25,\n\t\tDefaultBaseURL: \"https://api.deepseek.com/v1\",\n\t\tEnvKey:         \"DEEPSEEK_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"deepseek/deepseek-chat\", Name: \"DeepSeek Chat\"},\n\t\t\t{ID: \"deepseek/deepseek-reasoner\", Name: \"DeepSeek Reasoner\"},\n\t\t\t{ID: \"deepseek/deepseek-r1:1.5b\", Name: \"DeepSeek R1 1.5B\"},\n\t\t},\n\t},\n\t\"bailian-coding-plan\": {\n\t\tKey:            \"bailian-coding-plan\",\n\t\tDisplayName:    \"阿里云百炼 Coding Plan\",\n\t\tSort:           30,\n\t\tDefaultBaseURL: \"https://coding.dashscope.aliyuncs.com/v1\",\n\t\tEnvKey:         \"QWEN_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"bailian-coding-plan/qwen3.5-plus\", Name: \"Qwen3.5-Plus\"},\n\t\t\t{ID: \"bailian-coding-plan/qwen3-max\", Name: \"Qwen3-Max\"},\n\t\t\t{ID: \"bailian-coding-plan/qwen3-coder-next\", Name: \"Qwen3-Coder-Next\"},\n\t\t\t{ID: \"bailian-coding-plan/qwen3-coder-plus\", Name: \"Qwen3-Coder-Plus\"},\n\t\t\t{ID: \"bailian-coding-plan/minimax-m2.5\", Name: \"MiniMax M2.5\"},\n\t\t\t{ID: \"bailian-coding-plan/glm-5\", Name: \"GLM-5\"},\n\t\t\t{ID: \"bailian-coding-plan/kimi-k2.5\", Name: \"Kimi-k2.5\"},\n\t\t\t{ID: \"bailian-coding-plan/glm-4.7\", Name: \"GLM-4.7\"},\n\t\t},\n\t},\n\t\"ark-coding-plan\": {\n\t\tKey:            \"ark-coding-plan\",\n\t\tDisplayName:    \"方舟 Coding Plan\",\n\t\tSort:           35,\n\t\tDefaultBaseURL: \"https://ark.cn-beijing.volces.com/api/coding/v3\",\n\t\tEnvKey:         \"ARK_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"ark-coding-plan/doubao-seed-2.0-code\", Name: \"Doubao-Seed-2.0-Code\"},\n\t\t\t{ID: \"ark-coding-plan/doubao-seed-code\", Name: \"Doubao-Seed-Code\"},\n\t\t\t{ID: \"ark-coding-plan/kimi-k2.5\", Name: \"Kimi-K2.5\"},\n\t\t\t{ID: \"ark-coding-plan/glm-4.7\", Name: \"GLM-4.7\"},\n\t\t\t{ID: \"ark-coding-plan/deepseek-v3.2\", Name: \"DeepSeek-V3.2\"},\n\t\t\t{ID: \"ark-coding-plan/kimi-k2-thinking\", Name: \"Kimi-K2-thinking\"},\n\t\t},\n\t},\n\t\"zai\": {\n\t\tKey:            \"zai\",\n\t\tDisplayName:    \"Z.ai\",\n\t\tSort:           40,\n\t\tDefaultBaseURL: \"https://open.bigmodel.cn/api/paas/v4\",\n\t\tEnvKey:         \"ZAI_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"zai/glm-5\", Name: \"GLM-5\"},\n\t\t\t{ID: \"zai/glm-4.7\", Name: \"GLM-4.7\"},\n\t\t\t{ID: \"zai/glm-4.7-flash\", Name: \"GLM-4.7-Flash\"},\n\t\t\t{ID: \"zai/glm-4.7-flashx\", Name: \"GLM-4.7-FlashX\"},\n\t\t},\n\t},\n\t\"minimax\": {\n\t\tKey:            \"minimax\",\n\t\tDisplayName:    \"MiniMax (CN)\",\n\t\tSort:           45,\n\t\tDefaultBaseURL: \"https://api.minimaxi.com/anthropic\",\n\t\tEnvKey:         \"MINIMAX_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"minimax/MiniMax-M2.5\", Name: \"MiniMax M2.5\"},\n\t\t\t{ID: \"minimax/MiniMax-M2.5-highspeed\", Name: \"MiniMax M2.5 highspeed\"},\n\t\t},\n\t},\n\t\"kimi\": {\n\t\tKey:            \"kimi\",\n\t\tDisplayName:    \"Kimi (CN)\",\n\t\tSort:           50,\n\t\tDefaultBaseURL: \"https://api.moonshot.cn/v1\",\n\t\tEnvKey:         \"KIMI_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"kimi/kimi-k2.5\", Name: \"Kimi K2.5\"},\n\t\t\t{ID: \"kimi/kimi-k2-0905-preview\", Name: \"Kimi K2 0905 Preview\"},\n\t\t\t{ID: \"kimi/kimi-k2-thinking\", Name: \"Kimi K2 Thinking\"},\n\t\t},\n\t},\n\t\"kimi-coding\": {\n\t\tKey:            \"kimi-coding\",\n\t\tDisplayName:    \"Kimi Coding\",\n\t\tSort:           51,\n\t\tDefaultBaseURL: \"https://api.kimi.com/coding/\",\n\t\tEnvKey:         \"KIMI_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"kimi-coding/k2p5\", Name: \"Kimi K2.5\"},\n\t\t},\n\t},\n\t\"openai\": {\n\t\tKey:            \"openai\",\n\t\tDisplayName:    \"OpenAI\",\n\t\tSort:           55,\n\t\tDefaultBaseURL: \"https://api.openai.com/v1\",\n\t\tEnvKey:         \"OPENAI_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"openai/codex-mini-latest\", Name: \"Codex Mini\"},\n\t\t\t{ID: \"openai/gpt-4.1\", Name: \"GPT-4.1\"},\n\t\t\t{ID: \"openai/gpt-4o\", Name: \"GPT-4o\"},\n\t\t\t{ID: \"openai/gpt-4o-mini\", Name: \"GPT-4o Mini\"},\n\t\t\t{ID: \"openai/gpt-5\", Name: \"GPT-5\"},\n\t\t\t{ID: \"openai/gpt-5-mini\", Name: \"GPT-5 Mini\"},\n\t\t},\n\t},\n\t\"openrouter\": {\n\t\tKey:            \"openrouter\",\n\t\tDisplayName:    \"OpenRouter\",\n\t\tSort:           56,\n\t\tDefaultBaseURL: \"https://openrouter.ai/api/v1\",\n\t\tEnvKey:         \"OPENROUTER_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"openrouter/free\", Name: \"openrouter/free\"},\n\t\t\t{ID: \"openrouter/auto\", Name: \"openrouter/auto\"},\n\t\t},\n\t},\n\t\"anthropic\": {\n\t\tKey:            \"anthropic\",\n\t\tDisplayName:    \"Anthropic\",\n\t\tSort:           60,\n\t\tDefaultBaseURL: \"https://api.anthropic.com\",\n\t\tEnvKey:         \"ANTHROPIC_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"anthropic/claude-3-haiku-20240307\", Name: \"Claude 3 Haiku\"},\n\t\t\t{ID: \"anthropic/claude-3-5-haiku-latest\", Name: \"Claude 3.5 Haiku\"},\n\t\t\t{ID: \"anthropic/claude-3-5-sonnet-20241022\", Name: \"Claude 3.5 Sonnet\"},\n\t\t\t{ID: \"anthropic/claude-3-7-sonnet-20250219\", Name: \"Claude 3.7 Sonnet\"},\n\t\t\t{ID: \"anthropic/claude-opus-4-1\", Name: \"Claude Opus 4.1\"},\n\t\t},\n\t},\n\t\"gemini\": {\n\t\tKey:            \"gemini\",\n\t\tDisplayName:    \"Gemini\",\n\t\tSort:           65,\n\t\tDefaultBaseURL: \"https://generativelanguage.googleapis.com\",\n\t\tEnvKey:         \"GEMINI_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"google/gemini-3-flash-preview\", Name: \"Gemini 3 Flash Preview\"},\n\t\t\t{ID: \"google/gemini-flash-latest\", Name: \"Gemini Flash Latest\"},\n\t\t\t{ID: \"google/gemini-3-pro-preview\", Name: \"Gemini 3 Pro Preview\"},\n\t\t},\n\t},\n\t\"moonshot\": {\n\t\tKey:            \"moonshot\",\n\t\tDisplayName:    \"Moonshot (Global)\",\n\t\tSort:           70,\n\t\tDefaultBaseURL: \"https://api.moonshot.ai/v1\",\n\t\tEnvKey:         \"MOONSHOT_API_KEY\",\n\t\tModels: []Model{\n\t\t\t{ID: \"moonshot/kimi-k2.5\", Name: \"Kimi K2.5\"},\n\t\t\t{ID: \"moonshot/kimi-k2-0905-preview\", Name: \"Kimi K2 0905 Preview\"},\n\t\t\t{ID: \"moonshot/kimi-k2-thinking\", Name: \"Kimi K2 Thinking\"},\n\t\t},\n\t},\n}\n\nfunc Get(key string) (Meta, bool) {\n\tmeta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]\n\tif !ok {\n\t\treturn Meta{}, false\n\t}\n\treturn cloneMeta(meta), true\n}\n\nfunc All() map[string]Meta {\n\tresult := make(map[string]Meta, len(catalog))\n\tfor key, meta := range catalog {\n\t\tresult[key] = cloneMeta(meta)\n\t}\n\treturn result\n}\n\nfunc DefaultBaseURL(key string) (string, bool) {\n\tmeta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]\n\tif !ok || strings.TrimSpace(meta.DefaultBaseURL) == \"\" {\n\t\treturn \"\", false\n\t}\n\treturn meta.DefaultBaseURL, true\n}\n\nfunc EnvKey(key string) string {\n\tmeta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]\n\tif !ok {\n\t\treturn \"\"\n\t}\n\treturn meta.EnvKey\n}\n\nfunc DisplayName(key string) string {\n\tmeta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]\n\tif !ok {\n\t\treturn key\n\t}\n\tif strings.TrimSpace(meta.DisplayName) == \"\" {\n\t\treturn key\n\t}\n\treturn meta.DisplayName\n}\n\nfunc cloneMeta(meta Meta) Meta {\n\tclone := meta\n\tif len(meta.Models) > 0 {\n\t\tclone.Models = make([]Model, len(meta.Models))\n\t\tfor i, item := range meta.Models {\n\t\t\tclone.Models[i] = normalizeModel(meta.Key, item)\n\t\t}\n\t}\n\treturn clone\n}\n\nfunc normalizeModel(provider string, model Model) Model {\n\tclone := model\n\tclone.ID = strings.TrimSpace(clone.ID)\n\tclone.Name = strings.TrimSpace(clone.Name)\n\tif clone.Name == \"\" {\n\t\tclone.Name = clone.ID\n\t}\n\tif clone.MaxTokens <= 0 || clone.ContextWindow <= 0 {\n\t\tresolvedMaxTokens, resolvedContextWindow := catalogRuntimeDefaults(strings.ToLower(strings.TrimSpace(provider)))\n\t\tif clone.MaxTokens <= 0 {\n\t\t\tclone.MaxTokens = resolvedMaxTokens\n\t\t}\n\t\tif clone.ContextWindow <= 0 {\n\t\t\tclone.ContextWindow = resolvedContextWindow\n\t\t}\n\t}\n\tif len(clone.Input) == 0 {\n\t\tclone.Input = defaultModelInputs(provider)\n\t}\n\tif !clone.Reasoning {\n\t\tclone.Reasoning = isReasoningModel(clone.ID)\n\t}\n\treturn clone\n}\n\nfunc defaultModelInputs(provider string) []string {\n\tswitch strings.ToLower(strings.TrimSpace(provider)) {\n\tcase \"kimi-coding\":\n\t\treturn []string{\"text\", \"image\"}\n\tdefault:\n\t\treturn []string{\"text\"}\n\t}\n}\n\nfunc catalogRuntimeDefaults(provider string) (int, int) {\n\tswitch provider {\n\tcase \"deepseek\":\n\t\treturn 8192, 128000\n\tcase \"zai\":\n\t\treturn 131072, 204800\n\tcase \"openrouter\":\n\t\treturn 8192, 128000\n\tcase \"minimax\", \"kimi-coding\":\n\t\treturn 8192, 200000\n\tcase \"custom\", \"vllm\":\n\t\treturn 8192, 128000\n\tdefault:\n\t\treturn 8192, 256000\n\t}\n}\n"
  },
  {
    "path": "agent/app/provider/openclaw.go",
    "content": "package provider\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype OpenClawPatch struct {\n\tPrimaryModel string\n\tModels       map[string]interface{}\n}\n\nfunc BuildOpenClawPatch(provider, modelName, apiType string, maxTokens, contextWindow int, baseURL, apiKey string) (*OpenClawPatch, error) {\n\tprovider = strings.ToLower(strings.TrimSpace(provider))\n\tmodelName = strings.TrimSpace(modelName)\n\tif modelName == \"\" {\n\t\treturn nil, fmt.Errorf(\"model is required\")\n\t}\n\tmodelID := modelName\n\tif parts := strings.SplitN(modelName, \"/\", 2); len(parts) == 2 {\n\t\tmodelID = parts[1]\n\t}\n\n\tswitch provider {\n\tcase \"deepseek\":\n\t\treturn buildDeepseekPatch(modelName, baseURL, apiKey), nil\n\tcase \"gemini\":\n\t\treturn buildGenericPatch(provider, modelName, modelID, apiType, maxTokens, contextWindow, baseURL, apiKey), nil\n\tcase \"moonshot\", \"kimi\":\n\t\treturn buildMoonshotPatch(provider, modelName, modelID, baseURL, apiKey), nil\n\tcase \"bailian-coding-plan\":\n\t\treturn buildBailianPatch(modelID, maxTokens, contextWindow, baseURL, apiKey), nil\n\tcase \"ark-coding-plan\":\n\t\treturn buildArkPatch(modelID, maxTokens, contextWindow, baseURL, apiKey), nil\n\tcase \"minimax\":\n\t\treturn buildMiniMaxPatch(modelID, baseURL, apiKey), nil\n\tcase \"custom\", \"vllm\":\n\t\treturn buildCustomPatch(provider, modelName, apiType, maxTokens, contextWindow, baseURL, apiKey), nil\n\tcase \"ollama\":\n\t\treturn buildOllamaPatch(modelName, modelID, apiType, baseURL), nil\n\tcase \"kimi-coding\":\n\t\treturn buildKimiCodingPatch(modelName, modelID, baseURL, apiKey), nil\n\tcase \"zai\":\n\t\treturn buildZaiPatch(modelID, maxTokens, contextWindow, baseURL, apiKey), nil\n\tdefault:\n\t\treturn buildGenericPatch(provider, modelName, modelID, apiType, maxTokens, contextWindow, baseURL, apiKey), nil\n\t}\n}\n\nfunc buildDeepseekPatch(modelName, baseURL, apiKey string) *OpenClawPatch {\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: modelName,\n\t\tModels: providerModels(\"deepseek\", strings.TrimSpace(apiKey), firstNonEmpty(strings.TrimSpace(baseURL), \"https://api.deepseek.com/v1\"), \"openai-completions\", map[string]interface{}{\n\t\t\t\"id\":            \"deepseek-chat\",\n\t\t\t\"name\":          \"DeepSeek Chat\",\n\t\t\t\"reasoning\":     false,\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": 128000,\n\t\t\t\"maxTokens\":     8192,\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildMoonshotPatch(provider, modelName, modelID, baseURL, apiKey string) *OpenClawPatch {\n\tconfigProvider := provider\n\tprimaryModel := modelName\n\tif provider == \"kimi\" {\n\t\tconfigProvider = \"moonshot\"\n\t\tprimaryModel = \"moonshot/\" + modelID\n\t}\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: primaryModel,\n\t\tModels: providerModels(configProvider, strings.TrimSpace(apiKey), withCatalogDefault(provider, baseURL), \"openai-completions\", map[string]interface{}{\n\t\t\t\"id\":            modelID,\n\t\t\t\"name\":          modelID,\n\t\t\t\"reasoning\":     strings.Contains(strings.ToLower(modelID), \"thinking\"),\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": 256000,\n\t\t\t\"maxTokens\":     8192,\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildBailianPatch(modelID string, maxTokens, contextWindow int, baseURL, apiKey string) *OpenClawPatch {\n\tnormalizedID := normalizeBailianCodingPlanModelID(modelID)\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: \"bailian-coding-plan/\" + bailianPrimaryModelID(normalizedID),\n\t\tModels: providerModels(\"bailian-coding-plan\", strings.TrimSpace(apiKey), withCatalogDefault(\"bailian-coding-plan\", baseURL), \"openai-completions\", map[string]interface{}{\n\t\t\t\"id\":            normalizedID,\n\t\t\t\"name\":          normalizedID,\n\t\t\t\"reasoning\":     isReasoningModel(normalizedID),\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": fallbackInt(contextWindow, 256000),\n\t\t\t\"maxTokens\":     fallbackInt(maxTokens, 8192),\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildArkPatch(modelID string, maxTokens, contextWindow int, baseURL, apiKey string) *OpenClawPatch {\n\tnormalizedID := normalizeArkCodingPlanModelID(modelID)\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: \"ark-coding-plan/\" + normalizedID,\n\t\tModels: providerModels(\"ark-coding-plan\", strings.TrimSpace(apiKey), withCatalogDefault(\"ark-coding-plan\", baseURL), \"openai-completions\", map[string]interface{}{\n\t\t\t\"id\":            normalizedID,\n\t\t\t\"name\":          normalizedID,\n\t\t\t\"reasoning\":     isReasoningModel(normalizedID),\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": fallbackInt(contextWindow, 256000),\n\t\t\t\"maxTokens\":     fallbackInt(maxTokens, 8192),\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildMiniMaxPatch(modelID, baseURL, apiKey string) *OpenClawPatch {\n\tnormalizedID := normalizeMiniMaxModelID(modelID)\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: \"minimax-portal/\" + normalizedID,\n\t\tModels: providerModels(\"minimax-portal\", strings.TrimSpace(apiKey), firstNonEmpty(strings.TrimSpace(baseURL), \"https://api.minimaxi.com/anthropic\"), \"anthropic-messages\", map[string]interface{}{\n\t\t\t\"id\":            normalizedID,\n\t\t\t\"name\":          strings.ReplaceAll(normalizedID, \"-\", \" \"),\n\t\t\t\"reasoning\":     false,\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": 200000,\n\t\t\t\"maxTokens\":     8192,\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildCustomPatch(provider, modelName, apiType string, maxTokens, contextWindow int, baseURL, apiKey string) *OpenClawPatch {\n\tcustomModelID := normalizeCustomModel(modelName)\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: provider + \"/\" + customModelID,\n\t\tModels: providerModels(provider, strings.TrimSpace(apiKey), strings.TrimSpace(baseURL), apiType, map[string]interface{}{\n\t\t\t\"id\":            customModelID,\n\t\t\t\"name\":          customModelID,\n\t\t\t\"reasoning\":     isReasoningModel(customModelID),\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": fallbackInt(contextWindow, 128000),\n\t\t\t\"maxTokens\":     fallbackInt(maxTokens, 8192),\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildOllamaPatch(modelName, modelID, apiType, baseURL string) *OpenClawPatch {\n\tapi := normalizeAPIType(apiType)\n\tif api != \"openai-completions\" && api != \"openai-responses\" {\n\t\tapi = \"openai-responses\"\n\t}\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: modelName,\n\t\tModels: providerModels(\"ollama\", \"ollama\", strings.TrimSpace(baseURL), api, map[string]interface{}{\n\t\t\t\"id\":            modelID,\n\t\t\t\"name\":          modelID,\n\t\t\t\"reasoning\":     api != \"openai-completions\",\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": 160000,\n\t\t\t\"maxTokens\":     8192,\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildKimiCodingPatch(modelName, modelID, baseURL, apiKey string) *OpenClawPatch {\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: modelName,\n\t\tModels: providerModels(\"kimi-coding\", strings.TrimSpace(apiKey), withCatalogDefault(\"kimi-coding\", baseURL), \"anthropic-messages\", map[string]interface{}{\n\t\t\t\"id\":            modelID,\n\t\t\t\"name\":          \"Kimi for Coding\",\n\t\t\t\"reasoning\":     true,\n\t\t\t\"input\":         []string{\"text\", \"image\"},\n\t\t\t\"contextWindow\": 262144,\n\t\t\t\"maxTokens\":     32768,\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildZaiPatch(modelID string, maxTokens, contextWindow int, baseURL, apiKey string) *OpenClawPatch {\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: \"zai/\" + modelID,\n\t\tModels: providerModels(\"zai\", strings.TrimSpace(apiKey), withCatalogDefault(\"zai\", baseURL), \"openai-completions\", map[string]interface{}{\n\t\t\t\"id\":            modelID,\n\t\t\t\"name\":          zaiModelDisplayName(modelID),\n\t\t\t\"reasoning\":     modelID == \"glm-5\",\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": fallbackInt(contextWindow, 204800),\n\t\t\t\"maxTokens\":     fallbackInt(maxTokens, 131072),\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc buildGenericPatch(provider, modelName, modelID, apiType string, maxTokens, contextWindow int, baseURL, apiKey string) *OpenClawPatch {\n\tproviderName := provider\n\tprimaryModel := modelName\n\tif provider == \"gemini\" {\n\t\tproviderName = \"google\"\n\t\tprimaryModel = \"google/\" + modelID\n\t}\n\treturn &OpenClawPatch{\n\t\tPrimaryModel: primaryModel,\n\t\tModels: providerModels(providerName, strings.TrimSpace(apiKey), withCatalogDefault(provider, baseURL), normalizeAPIType(apiType), map[string]interface{}{\n\t\t\t\"id\":            modelID,\n\t\t\t\"name\":          modelID,\n\t\t\t\"reasoning\":     isReasoningModel(modelID),\n\t\t\t\"input\":         []string{\"text\"},\n\t\t\t\"contextWindow\": fallbackInt(contextWindow, 256000),\n\t\t\t\"maxTokens\":     fallbackInt(maxTokens, 8192),\n\t\t\t\"cost\":          map[string]interface{}{},\n\t\t}),\n\t}\n}\n\nfunc providerModels(provider, apiKey, baseURL, api string, model map[string]interface{}) map[string]interface{} {\n\treturn map[string]interface{}{\n\t\t\"mode\": \"merge\",\n\t\t\"providers\": map[string]interface{}{\n\t\t\tprovider: map[string]interface{}{\n\t\t\t\t\"apiKey\":  apiKey,\n\t\t\t\t\"baseUrl\": baseURL,\n\t\t\t\t\"api\":     api,\n\t\t\t\t\"models\":  []map[string]interface{}{model},\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc withCatalogDefault(provider, baseURL string) string {\n\tif strings.TrimSpace(baseURL) != \"\" {\n\t\treturn strings.TrimSpace(baseURL)\n\t}\n\tif defaultURL, ok := DefaultBaseURL(provider); ok {\n\t\treturn defaultURL\n\t}\n\treturn \"\"\n}\n\nfunc firstNonEmpty(values ...string) string {\n\tfor _, value := range values {\n\t\tif strings.TrimSpace(value) != \"\" {\n\t\t\treturn strings.TrimSpace(value)\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc fallbackInt(value, fallback int) int {\n\tif value > 0 {\n\t\treturn value\n\t}\n\treturn fallback\n}\n\nfunc normalizeAPIType(apiType string) string {\n\ttrim := strings.ToLower(strings.TrimSpace(apiType))\n\tif trim == \"\" {\n\t\treturn \"openai-completions\"\n\t}\n\treturn trim\n}\n\nfunc normalizeCustomModel(modelName string) string {\n\ttrim := strings.TrimSpace(modelName)\n\ttrim = strings.TrimLeft(trim, \"/\")\n\tif parts := strings.SplitN(trim, \"/\", 2); len(parts) == 2 && strings.EqualFold(parts[0], \"custom\") {\n\t\treturn strings.TrimLeft(strings.TrimSpace(parts[1]), \"/\")\n\t}\n\treturn trim\n}\n\nfunc normalizeBailianCodingPlanModelID(modelID string) string {\n\ttrim := strings.TrimSpace(modelID)\n\tswitch strings.ToLower(trim) {\n\tcase \"minimax-m2.5\", \"minimax m2.5\", \"minimax/minimax-m2.5\", \"minimax/minimax m2.5\":\n\t\treturn \"MiniMax/MiniMax-M2.5\"\n\tdefault:\n\t\treturn trim\n\t}\n}\n\nfunc normalizeArkCodingPlanModelID(modelID string) string {\n\treturn strings.ToLower(strings.TrimSpace(modelID))\n}\n\nfunc normalizeMiniMaxModelID(modelID string) string {\n\tswitch strings.ToLower(strings.TrimSpace(modelID)) {\n\tcase \"minimax-m2.1\", \"minimax m2.1\", \"minimax-m2.1-preview\", \"minimax-m2.1-latest\":\n\t\treturn \"MiniMax-M2.1\"\n\tcase \"minimax-m2.1-lightning\", \"minimax m2.1 lightning\":\n\t\treturn \"MiniMax-M2.1-lightning\"\n\tdefault:\n\t\treturn modelID\n\t}\n}\n\nfunc zaiModelDisplayName(modelID string) string {\n\tswitch strings.ToLower(strings.TrimSpace(modelID)) {\n\tcase \"glm-5\":\n\t\treturn \"GLM-5\"\n\tcase \"glm-4.7\":\n\t\treturn \"GLM-4.7\"\n\tcase \"glm-4.7-flash\":\n\t\treturn \"GLM-4.7-Flash\"\n\tcase \"glm-4.7-flashx\":\n\t\treturn \"GLM-4.7-FlashX\"\n\tdefault:\n\t\treturn strings.TrimSpace(modelID)\n\t}\n}\n\nfunc bailianPrimaryModelID(modelID string) string {\n\ttrim := strings.TrimSpace(modelID)\n\tif trim == \"\" {\n\t\treturn \"\"\n\t}\n\tparts := strings.Split(trim, \"/\")\n\tfor i := len(parts) - 1; i >= 0; i-- {\n\t\tpart := strings.TrimSpace(parts[i])\n\t\tif part != \"\" {\n\t\t\treturn part\n\t\t}\n\t}\n\treturn trim\n}\n\nfunc isReasoningModel(modelID string) bool {\n\ttrim := strings.ToLower(strings.TrimSpace(modelID))\n\treturn strings.Contains(trim, \"reason\") || strings.Contains(trim, \"thinking\")\n}\n"
  },
  {
    "path": "agent/app/provider/verify.go",
    "content": "package provider\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n)\n\ntype VerifyRequest struct {\n\tMethod  string\n\tURL     string\n\tHeaders map[string]string\n\tBody    []byte\n}\n\nconst (\n\tdefaultVerifyTimeout = 30 * time.Second\n)\n\nfunc SkipVerification(key string) bool {\n\tswitch strings.ToLower(strings.TrimSpace(key)) {\n\tcase \"custom\", \"vllm\", \"ollama\", \"kimi-coding\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc VerifyAccount(provider, baseURL, apiKey string) error {\n\treq := BuildVerifyRequest(provider, baseURL, apiKey)\n\tvar body *bytes.Buffer\n\tif len(req.Body) > 0 {\n\t\tbody = bytes.NewBuffer(req.Body)\n\t} else {\n\t\tbody = bytes.NewBuffer(nil)\n\t}\n\thttpReq, err := http.NewRequest(req.Method, req.URL, body)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor key, value := range req.Headers {\n\t\thttpReq.Header.Set(key, value)\n\t}\n\tresp, err := (&http.Client{Timeout: verifyTimeout()}).Do(httpReq)\n\tif err != nil {\n\t\treturn buserr.WithErr(\"ErrAgentAccountUnavailable\", err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode >= 400 {\n\t\treturn buserr.WithErr(\"ErrAgentAccountUnavailable\", fmt.Errorf(\"verify failed: %s\", resp.Status))\n\t}\n\treturn nil\n}\n\nfunc verifyTimeout() time.Duration {\n\treturn defaultVerifyTimeout\n}\n\nfunc BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {\n\tprovider = strings.ToLower(strings.TrimSpace(provider))\n\tbase := strings.TrimRight(strings.TrimSpace(baseURL), \"/\")\n\theaders := map[string]string{}\n\trequest := VerifyRequest{Method: http.MethodGet, Headers: headers}\n\n\tswitch provider {\n\tcase \"anthropic\", \"kimi-coding\":\n\t\theaders[\"x-api-key\"] = apiKey\n\t\theaders[\"anthropic-version\"] = \"2023-06-01\"\n\t\tif strings.Contains(base, \"/v1\") {\n\t\t\trequest.URL = base + \"/models\"\n\t\t} else {\n\t\t\trequest.URL = base + \"/v1/models\"\n\t\t}\n\tcase \"gemini\":\n\t\trequest.Method = http.MethodPost\n\t\tif strings.Contains(base, \"/v1beta\") {\n\t\t\trequest.URL = base + \"/models/gemini-3-flash-preview:generateContent\"\n\t\t} else {\n\t\t\trequest.URL = base + \"/v1beta/models/gemini-3-flash-preview:generateContent\"\n\t\t}\n\t\theaders[\"x-goog-api-key\"] = apiKey\n\t\theaders[\"Content-Type\"] = \"application/json\"\n\t\trequest.Body = mustJSON(map[string]interface{}{\n\t\t\t\"contents\": []map[string]interface{}{{\n\t\t\t\t\"parts\": []map[string]string{{\n\t\t\t\t\t\"text\": \"Explain how AI works in a few words\",\n\t\t\t\t}},\n\t\t\t}},\n\t\t})\n\tcase \"zai\":\n\t\theaders[\"Authorization\"] = fmt.Sprintf(\"Bearer %s\", apiKey)\n\t\trequest.URL = base + \"/models\"\n\tcase \"bailian-coding-plan\":\n\t\trequest.Method = http.MethodPost\n\t\tif !strings.Contains(base, \"/v1\") {\n\t\t\tbase = base + \"/v1\"\n\t\t}\n\t\trequest.URL = base + \"/chat/completions\"\n\t\theaders[\"Authorization\"] = fmt.Sprintf(\"Bearer %s\", apiKey)\n\t\theaders[\"Content-Type\"] = \"application/json\"\n\t\trequest.Body = mustJSON(map[string]interface{}{\n\t\t\t\"model\":      \"qwen3.5-plus\",\n\t\t\t\"messages\":   []map[string]string{{\"role\": \"user\", \"content\": \"test\"}},\n\t\t\t\"max_tokens\": 1,\n\t\t})\n\tcase \"ark-coding-plan\":\n\t\trequest.Method = http.MethodPost\n\t\tif !strings.Contains(base, \"/api/coding/v3\") {\n\t\t\tbase = \"https://ark.cn-beijing.volces.com/api/coding/v3\"\n\t\t}\n\t\trequest.URL = base + \"/chat/completions\"\n\t\theaders[\"Authorization\"] = fmt.Sprintf(\"Bearer %s\", apiKey)\n\t\theaders[\"Content-Type\"] = \"application/json\"\n\t\trequest.Body = mustJSON(map[string]interface{}{\n\t\t\t\"model\":      \"doubao-seed-2.0-code\",\n\t\t\t\"messages\":   []map[string]string{{\"role\": \"user\", \"content\": \"test\"}},\n\t\t\t\"max_tokens\": 1,\n\t\t})\n\tcase \"minimax\":\n\t\trequest.Method = http.MethodPost\n\t\tif !strings.Contains(base, \"/v1\") {\n\t\t\tbase = base + \"/v1\"\n\t\t}\n\t\trequest.URL = base + \"/chat/completions\"\n\t\theaders[\"Authorization\"] = fmt.Sprintf(\"Bearer %s\", apiKey)\n\t\theaders[\"Content-Type\"] = \"application/json\"\n\t\trequest.Body = mustJSON(map[string]interface{}{\n\t\t\t\"model\":      \"MiniMax-M2.1\",\n\t\t\t\"messages\":   []map[string]string{{\"role\": \"user\", \"content\": \"test\"}},\n\t\t\t\"max_tokens\": 1,\n\t\t})\n\tdefault:\n\t\theaders[\"Authorization\"] = fmt.Sprintf(\"Bearer %s\", apiKey)\n\t\tif strings.Contains(base, \"/v1\") {\n\t\t\trequest.URL = base + \"/models\"\n\t\t} else {\n\t\t\trequest.URL = base + \"/v1/models\"\n\t\t}\n\t}\n\treturn request\n}\n\nfunc mustJSON(value interface{}) []byte {\n\tpayload, err := json.Marshal(value)\n\tif err != nil {\n\t\treturn []byte(\"{}\")\n\t}\n\treturn payload\n}\n"
  },
  {
    "path": "agent/app/repo/agent.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype AgentRepo struct{}\n\ntype IAgentRepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.Agent, error)\n\tGetFirst(opts ...DBOption) (*model.Agent, error)\n\tCreate(agent *model.Agent) error\n\tSave(agent *model.Agent) error\n\tDeleteByID(id uint) error\n\tDeleteByAppInstallID(appInstallID uint) error\n\tDeleteByAppInstallIDWithCtx(ctx context.Context, appInstallID uint) error\n\tList(opts ...DBOption) ([]model.Agent, error)\n}\n\nfunc NewIAgentRepo() IAgentRepo {\n\treturn &AgentRepo{}\n}\n\nfunc (a AgentRepo) Page(page, size int, opts ...DBOption) (int64, []model.Agent, error) {\n\tvar agents []model.Agent\n\tdb := getDb(opts...).Model(&model.Agent{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&agents).Error\n\treturn count, agents, err\n}\n\nfunc (a AgentRepo) GetFirst(opts ...DBOption) (*model.Agent, error) {\n\tvar agent model.Agent\n\tif err := getDb(opts...).First(&agent).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &agent, nil\n}\n\nfunc (a AgentRepo) Create(agent *model.Agent) error {\n\treturn getDb().Create(agent).Error\n}\n\nfunc (a AgentRepo) Save(agent *model.Agent) error {\n\treturn getDb().Save(agent).Error\n}\n\nfunc (a AgentRepo) DeleteByID(id uint) error {\n\treturn getDb().Delete(&model.Agent{}, id).Error\n}\n\nfunc (a AgentRepo) DeleteByAppInstallID(appInstallID uint) error {\n\tif appInstallID == 0 {\n\t\treturn nil\n\t}\n\treturn getDb().Where(\"app_install_id = ?\", appInstallID).Delete(&model.Agent{}).Error\n}\n\nfunc (a AgentRepo) DeleteByAppInstallIDWithCtx(ctx context.Context, appInstallID uint) error {\n\tif appInstallID == 0 {\n\t\treturn nil\n\t}\n\treturn getTx(ctx).Where(\"app_install_id = ?\", appInstallID).Delete(&model.Agent{}).Error\n}\n\nfunc (a AgentRepo) List(opts ...DBOption) ([]model.Agent, error) {\n\tvar agents []model.Agent\n\tif err := getDb(opts...).Find(&agents).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn agents, nil\n}\n"
  },
  {
    "path": "agent/app/repo/agent_account.go",
    "content": "package repo\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/model\"\n\ntype AgentAccountRepo struct{}\n\ntype IAgentAccountRepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.AgentAccount, error)\n\tGetFirst(opts ...DBOption) (*model.AgentAccount, error)\n\tCreate(account *model.AgentAccount) error\n\tSave(account *model.AgentAccount) error\n\tDeleteByID(id uint) error\n\tList(opts ...DBOption) ([]model.AgentAccount, error)\n}\n\nfunc NewIAgentAccountRepo() IAgentAccountRepo {\n\treturn &AgentAccountRepo{}\n}\n\nfunc (a AgentAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.AgentAccount, error) {\n\tvar accounts []model.AgentAccount\n\tdb := getDb(opts...).Model(&model.AgentAccount{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&accounts).Error\n\treturn count, accounts, err\n}\n\nfunc (a AgentAccountRepo) GetFirst(opts ...DBOption) (*model.AgentAccount, error) {\n\tvar account model.AgentAccount\n\tif err := getDb(opts...).First(&account).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &account, nil\n}\n\nfunc (a AgentAccountRepo) Create(account *model.AgentAccount) error {\n\treturn getDb().Create(account).Error\n}\n\nfunc (a AgentAccountRepo) Save(account *model.AgentAccount) error {\n\treturn getDb().Save(account).Error\n}\n\nfunc (a AgentAccountRepo) DeleteByID(id uint) error {\n\treturn getDb().Delete(&model.AgentAccount{}, id).Error\n}\n\nfunc (a AgentAccountRepo) List(opts ...DBOption) ([]model.AgentAccount, error) {\n\tvar accounts []model.AgentAccount\n\tif err := getDb(opts...).Find(&accounts).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn accounts, nil\n}\n"
  },
  {
    "path": "agent/app/repo/agent_account_model.go",
    "content": "package repo\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/model\"\n\ntype AgentAccountModelRepo struct{}\n\ntype IAgentAccountModelRepo interface {\n\tList(opts ...DBOption) ([]model.AgentAccountModel, error)\n\tGetFirst(opts ...DBOption) (*model.AgentAccountModel, error)\n\tCreate(item *model.AgentAccountModel) error\n\tSave(item *model.AgentAccountModel) error\n\tDeleteByID(id uint) error\n\tDelete(opts ...DBOption) error\n}\n\nfunc NewIAgentAccountModelRepo() IAgentAccountModelRepo {\n\treturn &AgentAccountModelRepo{}\n}\n\nfunc (a AgentAccountModelRepo) List(opts ...DBOption) ([]model.AgentAccountModel, error) {\n\tvar list []model.AgentAccountModel\n\tif err := getDb(opts...).Find(&list).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn list, nil\n}\n\nfunc (a AgentAccountModelRepo) GetFirst(opts ...DBOption) (*model.AgentAccountModel, error) {\n\tvar item model.AgentAccountModel\n\tif err := getDb(opts...).First(&item).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &item, nil\n}\n\nfunc (a AgentAccountModelRepo) Create(item *model.AgentAccountModel) error {\n\treturn getDb().Create(item).Error\n}\n\nfunc (a AgentAccountModelRepo) Save(item *model.AgentAccountModel) error {\n\treturn getDb().Save(item).Error\n}\n\nfunc (a AgentAccountModelRepo) DeleteByID(id uint) error {\n\treturn getDb().Delete(&model.AgentAccountModel{}, id).Error\n}\n\nfunc (a AgentAccountModelRepo) Delete(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.AgentAccountModel{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/ai.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype AiRepo struct{}\n\ntype IAiRepo interface {\n\tGet(opts ...DBOption) (model.OllamaModel, error)\n\tList(opts ...DBOption) ([]model.OllamaModel, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.OllamaModel, error)\n\tCreate(cronjob *model.OllamaModel) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(opts ...DBOption) error\n}\n\nfunc NewIAiRepo() IAiRepo {\n\treturn &AiRepo{}\n}\n\nfunc (u *AiRepo) Get(opts ...DBOption) (model.OllamaModel, error) {\n\tvar item model.OllamaModel\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&item).Error\n\treturn item, err\n}\n\nfunc (u *AiRepo) List(opts ...DBOption) ([]model.OllamaModel, error) {\n\tvar list []model.OllamaModel\n\tdb := global.DB.Model(&model.OllamaModel{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&list).Error\n\treturn list, err\n}\n\nfunc (u *AiRepo) Page(page, size int, opts ...DBOption) (int64, []model.OllamaModel, error) {\n\tvar list []model.OllamaModel\n\tdb := global.DB.Model(&model.OllamaModel{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&list).Error\n\treturn count, list, err\n}\n\nfunc (u *AiRepo) Create(item *model.OllamaModel) error {\n\treturn global.DB.Create(item).Error\n}\n\nfunc (u *AiRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.OllamaModel{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *AiRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.OllamaModel{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/alert.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"google.golang.org/genproto/googleapis/type/date\"\n\t\"gorm.io/gorm\"\n\t\"time\"\n)\n\ntype AlertRepo struct{}\n\ntype IAlertRepo interface {\n\tWithByType(alertType string) DBOption\n\tWithByStatusIn(status []string) DBOption\n\tWithByProject(project string) DBOption\n\tWithByCount(count uint) DBOption\n\tWithByAlertId(alertId uint) DBOption\n\tWithByCreateAt(date *date.Date) DBOption\n\tWithByLicenseId(licenseId string) DBOption\n\tWithByRecordId(recordId uint) DBOption\n\tWithByMethod(method string) DBOption\n\n\tCreate(alert *model.Alert) error\n\tGet(opts ...DBOption) (model.Alert, error)\n\tPage(page, size int, opts ...DBOption) (int64, []model.Alert, error)\n\tList(opts ...DBOption) ([]model.Alert, error)\n\tDelete(opts ...DBOption) error\n\tSave(alert *model.Alert) error\n\tUpdate(maps map[string]interface{}, opts ...DBOption) error\n\n\tGetLog(opts ...DBOption) (model.AlertLog, error)\n\tCreateLog(alertLog *model.AlertLog) error\n\tPageLog(limit, offset int, opts ...DBOption) (int64, []model.AlertLog, error)\n\tListLog(opts ...DBOption) ([]model.AlertLog, error)\n\tUpdateLog(id uint, maps map[string]interface{}) error\n\tBatchUpdateLogBy(maps map[string]interface{}, opts ...DBOption) error\n\tDeleteLog(opts ...DBOption) error\n\tCleanAlertLogs() error\n\n\tCreateAlertTask(alertTaskBase *model.AlertTask) error\n\tDeleteAlertTask(opts ...DBOption) error\n\tGetAlertTask(opts ...DBOption) (model.AlertTask, error)\n\tLoadTaskCount(alertType string, project string, method string) (uint, uint, error)\n\tGetTaskLog(alertType string, alertId uint) (time.Time, error)\n\tGetLicensePushCount(method string) (uint, error)\n\n\tGetConfig(opts ...DBOption) (model.AlertConfig, error)\n\tAlertConfigList(opts ...DBOption) ([]model.AlertConfig, error)\n\tUpdateAlertConfig(maps map[string]interface{}, opts ...DBOption) error\n\tCreateAlertConfig(config *model.AlertConfig) error\n\tDeleteAlertConfig(opts ...DBOption) error\n\n\tSyncAll(data []model.AlertConfig) error\n}\n\nfunc NewIAlertRepo() IAlertRepo {\n\treturn &AlertRepo{}\n}\n\nfunc (a *AlertRepo) WithByType(alertType string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"`type` = ?\", alertType)\n\t}\n}\n\nfunc (a *AlertRepo) WithByStatusIn(status []string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"status in (?)\", status)\n\t}\n}\n\nfunc (a *AlertRepo) WithByCount(count uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"count = ?\", count)\n\t}\n}\n\nfunc (a *AlertRepo) WithByProject(project string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"project = ?\", project)\n\t}\n}\n\nfunc (a *AlertRepo) WithByAlertId(alertId uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"alert_id = ?\", alertId)\n\t}\n}\n\nfunc (a *AlertRepo) WithByLicenseId(licenseId string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"license_id = ?\", licenseId)\n\t}\n}\n\nfunc (a *AlertRepo) WithByRecordId(recordId uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"record_id = ?\", recordId)\n\t}\n}\n\nfunc (a *AlertRepo) WithByMethod(method string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"method = ?\", method)\n\t}\n}\n\nfunc (a *AlertRepo) WithByCreateAt(createAt *date.Date) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"DATE(created_at) = DATE(?)\", createAt)\n\t}\n}\n\nfunc (a *AlertRepo) Create(alert *model.Alert) error {\n\treturn global.AlertDB.Model(&model.Alert{}).Create(alert).Error\n}\n\nfunc (a *AlertRepo) Save(alert *model.Alert) error {\n\treturn global.AlertDB.Save(alert).Error\n}\n\nfunc (a *AlertRepo) Get(opts ...DBOption) (model.Alert, error) {\n\tvar alert model.Alert\n\tdb, _ := getAlertDB(opts...)\n\terr := db.First(&alert).Error\n\treturn alert, err\n}\n\nfunc (a *AlertRepo) Page(page, size int, opts ...DBOption) (int64, []model.Alert, error) {\n\tvar alerts []model.Alert\n\talertDb, _ := getAlertDB(opts...)\n\tdb := alertDb.Model(&model.Alert{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&alerts).Error\n\treturn count, alerts, err\n}\n\nfunc (a *AlertRepo) List(opts ...DBOption) ([]model.Alert, error) {\n\tvar alert []model.Alert\n\tdb, _ := getAlertDB(opts...)\n\terr := db.Find(&alert).Error\n\treturn alert, err\n}\n\nfunc (a *AlertRepo) Update(maps map[string]interface{}, opts ...DBOption) error {\n\tdb, _ := getAlertDB(opts...)\n\treturn db.Model(&model.Alert{}).Updates(maps).Error\n}\n\nfunc (a *AlertRepo) Delete(opts ...DBOption) error {\n\tdb, _ := getAlertDB(opts...)\n\treturn db.Delete(&model.Alert{}).Error\n}\n\nfunc (a *AlertRepo) GetLog(opts ...DBOption) (model.AlertLog, error) {\n\tvar alertLog model.AlertLog\n\tdb, _ := getAlertDB(opts...)\n\terr := db.First(&alertLog).Error\n\treturn alertLog, err\n}\n\nfunc (a *AlertRepo) CreateLog(log *model.AlertLog) error {\n\treturn global.AlertDB.Model(&model.AlertLog{}).Create(&log).Error\n}\n\nfunc (a *AlertRepo) UpdateLog(id uint, maps map[string]interface{}) error {\n\treturn global.AlertDB.Model(&model.AlertLog{}).Where(\"id = ?\", id).Updates(maps).Error\n}\n\nfunc (a *AlertRepo) BatchUpdateLogBy(maps map[string]interface{}, opts ...DBOption) error {\n\tdb, _ := getAlertDB(opts...)\n\tif len(opts) == 0 {\n\t\tdb = db.Where(\"1=1\")\n\t}\n\treturn db.Model(&model.AlertLog{}).Updates(&maps).Error\n}\n\nfunc (a *AlertRepo) PageLog(page, size int, opts ...DBOption) (int64, []model.AlertLog, error) {\n\tvar alerts []model.AlertLog\n\tdb := global.AlertDB.Model(&model.AlertLog{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Order(\"created_at desc\").Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&alerts).Error\n\treturn count, alerts, err\n}\n\nfunc (a *AlertRepo) ListLog(opts ...DBOption) ([]model.AlertLog, error) {\n\tvar alertLog []model.AlertLog\n\tdb, _ := getAlertDB(opts...)\n\terr := db.Find(&alertLog).Error\n\treturn alertLog, err\n}\n\nfunc (a *AlertRepo) DeleteLog(opts ...DBOption) error {\n\tdb, _ := getAlertDB(opts...)\n\treturn db.Delete(&model.AlertLog{}).Error\n}\n\nfunc (a *AlertRepo) CleanAlertLogs() error {\n\treturn global.AlertDB.Where(\"1 = 1\").Delete(&model.AlertLog{}).Error\n}\n\nfunc (a *AlertRepo) CreateAlertTask(alertTaskBase *model.AlertTask) error {\n\treturn global.AlertDB.Model(&model.AlertTask{}).Create(&alertTaskBase).Error\n}\n\nfunc (a *AlertRepo) DeleteAlertTask(opts ...DBOption) error {\n\tdb, _ := getAlertDB(opts...)\n\treturn db.Delete(&model.AlertTask{}).Error\n}\n\nfunc (a *AlertRepo) GetAlertTask(opts ...DBOption) (model.AlertTask, error) {\n\tvar data model.AlertTask\n\tdb, _ := getAlertDB(opts...)\n\terr := db.First(&data).Error\n\treturn data, err\n}\n\nfunc (a *AlertRepo) LoadTaskCount(alertType string, project string, method string) (uint, uint, error) {\n\tvar (\n\t\ttodayCount int64\n\t\ttotalCount int64\n\t)\n\t_ = global.AlertDB.Model(&model.AlertTask{}).Where(\"type = ? AND quota_type = ? AND method = ?\", alertType, project, method).Count(&totalCount).Error\n\n\tnow := time.Now()\n\ttodayMidnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())\n\ttomorrowMidnight := todayMidnight.Add(24 * time.Hour)\n\terr := global.AlertDB.Model(&model.AlertTask{}).Where(\"type =  ? AND quota_type = ?  AND method = ? AND created_at > ? AND created_at < ?\", alertType, project, method, todayMidnight, tomorrowMidnight).Count(&todayCount).Error\n\treturn uint(todayCount), uint(totalCount), err\n}\n\nfunc (a *AlertRepo) GetTaskLog(alertType string, alertId uint) (time.Time, error) {\n\tvar newDate time.Time\n\tstatus := []string{constant.AlertSuccess, constant.AlertPushSuccess, constant.AlertSyncError, constant.AlertPushing}\n\tnow := time.Now()\n\ttodayMidnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())\n\ttomorrowMidnight := todayMidnight.Add(24 * time.Hour)\n\terr := global.AlertDB.Model(&model.AlertLog{}).\n\t\tWhere(\"type = ? AND alert_id = ? AND status in ? AND created_at > ? AND created_at < ?\", alertType, alertId, status, todayMidnight, tomorrowMidnight).\n\t\tOrder(\"created_at DESC\").\n\t\tLimit(1).\n\t\tPluck(\"created_at\", &newDate).Error\n\tif err != nil {\n\t\treturn time.Time{}, err\n\t}\n\n\tif newDate.IsZero() {\n\t\treturn time.Time{}, nil\n\t}\n\n\treturn newDate, nil\n}\n\nfunc getAlertDB(opts ...DBOption) (*gorm.DB, error) {\n\tvar db *gorm.DB\n\tdb = global.AlertDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db, nil\n}\n\nfunc (a *AlertRepo) GetLicensePushCount(method string) (uint, error) {\n\tvar (\n\t\ttodayCount int64\n\t)\n\tnow := time.Now()\n\ttodayMidnight := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())\n\ttomorrowMidnight := todayMidnight.Add(24 * time.Hour)\n\terr := global.AlertDB.Model(&model.AlertTask{}).Where(\"created_at > ? AND created_at < ? AND method = ?\", todayMidnight, tomorrowMidnight, method).Count(&todayCount).Error\n\treturn uint(todayCount), err\n}\n\nfunc (a *AlertRepo) AlertConfigList(opts ...DBOption) ([]model.AlertConfig, error) {\n\tvar config []model.AlertConfig\n\tdb, _ := getAlertDB(opts...)\n\terr := db.Find(&config).Error\n\treturn config, err\n}\n\nfunc (a *AlertRepo) UpdateAlertConfig(maps map[string]interface{}, opts ...DBOption) error {\n\tdb, _ := getAlertDB(opts...)\n\treturn db.Model(&model.AlertConfig{}).Updates(maps).Error\n}\n\nfunc (a *AlertRepo) CreateAlertConfig(config *model.AlertConfig) error {\n\treturn global.AlertDB.Model(&model.AlertConfig{}).Create(config).Error\n}\n\nfunc (a *AlertRepo) DeleteAlertConfig(opts ...DBOption) error {\n\tdb, _ := getAlertDB(opts...)\n\treturn db.Delete(&model.AlertConfig{}).Error\n}\n\nfunc (a *AlertRepo) GetConfig(opts ...DBOption) (model.AlertConfig, error) {\n\tvar alertConfig model.AlertConfig\n\tdb, _ := getAlertDB(opts...)\n\terr := db.First(&alertConfig).Error\n\treturn alertConfig, err\n}\n\nfunc (a *AlertRepo) SyncAll(data []model.AlertConfig) error {\n\ttx := global.AlertDB.Begin()\n\tvar oldConfigs []model.AlertConfig\n\t_ = tx.Find(&oldConfigs).Error\n\toldConfigMap := make(map[string]uint)\n\tfor _, item := range oldConfigs {\n\t\toldConfigMap[item.Type] = item.ID\n\t}\n\tfor _, item := range data {\n\t\tif val, ok := oldConfigMap[item.Type]; ok {\n\t\t\titem.ID = val\n\t\t\tdelete(oldConfigMap, item.Type)\n\t\t} else {\n\t\t\titem.ID = 0\n\t\t}\n\t\tif err := tx.Model(model.AlertConfig{}).Where(\"id = ?\", item.ID).Save(&item).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, val := range oldConfigMap {\n\t\tif err := tx.Where(\"id = ?\", val).Delete(&model.AlertConfig{}).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\ttx.Commit()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/repo/app.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\ntype AppRepo struct {\n}\n\ntype IAppRepo interface {\n\tWithKey(key string) DBOption\n\tWithType(typeStr string) DBOption\n\tOrderByRecommend() DBOption\n\tGetRecommend() DBOption\n\tWithResource(resource string) DBOption\n\tWithNotLocal() DBOption\n\tWithByLikeName(name string) DBOption\n\tWithKeyIn(keys []string) DBOption\n\tWithArch(arch string) DBOption\n\tWithPanelVersion(panelVersion string) DBOption\n\n\tPage(page, size int, opts ...DBOption) (int64, []model.App, error)\n\tGetFirst(opts ...DBOption) (model.App, error)\n\tGetBy(opts ...DBOption) ([]model.App, error)\n\tBatchCreate(ctx context.Context, apps []model.App) error\n\tCreate(ctx context.Context, app *model.App) error\n\tSave(ctx context.Context, app *model.App) error\n\tBatchDelete(ctx context.Context, apps []model.App) error\n\tDeleteByIDs(ctx context.Context, ids []uint) error\n\tDeleteBy(opts ...DBOption) error\n\n\tGetTopRecommend() ([]string, error)\n}\n\nfunc NewIAppRepo() IAppRepo {\n\treturn &AppRepo{}\n}\n\nfunc (a AppRepo) WithByLikeName(name string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(name) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"name like ? or  description like ? or short_desc_zh like ? or short_desc_en like ?\", \"%\"+name+\"%\", \"%\"+name+\"%\", \"%\"+name+\"%\", \"%\"+name+\"%\")\n\t}\n}\n\nfunc (a AppRepo) WithKey(key string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"key = ?\", key)\n\t}\n}\n\nfunc (a AppRepo) WithKeyIn(keys []string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"`key` in (?)\", keys)\n\t}\n}\n\nfunc (a AppRepo) WithType(typeStr string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"type = ?\", typeStr)\n\t}\n}\n\nfunc (a AppRepo) OrderByRecommend() DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Order(\"recommend asc\")\n\t}\n}\n\nfunc (a AppRepo) GetRecommend() DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"recommend < 9999\")\n\t}\n}\n\nfunc (a AppRepo) WithResource(resource string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"resource = ?\", resource)\n\t}\n}\n\nfunc (a AppRepo) WithNotLocal() DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"resource != 'local'\")\n\t}\n}\n\nfunc (a AppRepo) WithArch(arch string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"architectures like ?\", fmt.Sprintf(\"%%%s%%\", arch))\n\t}\n}\n\nfunc (a AppRepo) WithPanelVersion(panelVersion string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"required_panel_version >= ?\", panelVersion)\n\t}\n}\n\nfunc (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, error) {\n\tvar apps []model.App\n\tdb := getDb(opts...).Model(&model.App{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Preload(\"AppTags\").Find(&apps).Error\n\treturn count, apps, err\n}\n\nfunc (a AppRepo) GetFirst(opts ...DBOption) (model.App, error) {\n\tvar app model.App\n\tdb := getDb(opts...).Model(&model.App{})\n\tif err := db.Preload(\"AppTags\").First(&app).Error; err != nil {\n\t\treturn app, err\n\t}\n\treturn app, nil\n}\n\nfunc (a AppRepo) GetBy(opts ...DBOption) ([]model.App, error) {\n\tvar apps []model.App\n\tdb := getDb(opts...).Model(&model.App{})\n\tif err := db.Preload(\"Details\").Preload(\"AppTags\").Find(&apps).Error; err != nil {\n\t\treturn apps, err\n\t}\n\treturn apps, nil\n}\n\nfunc (a AppRepo) GetTopRecommend() ([]string, error) {\n\tvar (\n\t\tapps  []model.App\n\t\tnames []string\n\t)\n\tdb := getDb().Model(&model.App{})\n\tif err := db.Order(\"recommend asc\").Limit(6).Find(&apps).Error; err != nil {\n\t\treturn names, err\n\t}\n\tfor _, item := range apps {\n\t\tnames = append(names, item.Key)\n\t}\n\treturn names, nil\n}\n\nfunc (a AppRepo) BatchCreate(ctx context.Context, apps []model.App) error {\n\treturn getTx(ctx).Omit(clause.Associations).Create(&apps).Error\n}\n\nfunc (a AppRepo) Create(ctx context.Context, app *model.App) error {\n\treturn getTx(ctx).Omit(clause.Associations).Create(app).Error\n}\n\nfunc (a AppRepo) Save(ctx context.Context, app *model.App) error {\n\treturn getTx(ctx).Omit(clause.Associations).Save(app).Error\n}\n\nfunc (a AppRepo) BatchDelete(ctx context.Context, apps []model.App) error {\n\treturn getTx(ctx).Omit(clause.Associations).Delete(&apps).Error\n}\n\nfunc (a AppRepo) DeleteByIDs(ctx context.Context, ids []uint) error {\n\treturn getTx(ctx).Where(\"id in (?)\", ids).Delete(&model.App{}).Error\n}\n\nfunc (a AppRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb().Delete(&model.App{}, opts).Error\n}\n"
  },
  {
    "path": "agent/app/repo/app_detail.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\ntype AppDetailRepo struct {\n}\n\ntype IAppDetailRepo interface {\n\tWithVersion(version string) DBOption\n\tWithAppId(id uint) DBOption\n\tWithIgnored() DBOption\n\tGetFirst(opts ...DBOption) (model.AppDetail, error)\n\tUpdate(ctx context.Context, detail model.AppDetail) error\n\tBatchCreate(ctx context.Context, details []model.AppDetail) error\n\tDeleteByAppIds(ctx context.Context, appIds []uint) error\n\tDeleteByIDs(ctx context.Context, appIds []uint) error\n\tGetBy(opts ...DBOption) ([]model.AppDetail, error)\n\tBatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error\n\tBatchDelete(ctx context.Context, appDetails []model.AppDetail) error\n}\n\nfunc NewIAppDetailRepo() IAppDetailRepo {\n\treturn &AppDetailRepo{}\n}\n\nfunc (a AppDetailRepo) WithVersion(version string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"version = ?\", version)\n\t}\n}\n\nfunc (a AppDetailRepo) WithAppId(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_id = ?\", id)\n\t}\n}\n\nfunc (a AppDetailRepo) WithIgnored() DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"ignore_upgrade = 1\")\n\t}\n}\n\nfunc (a AppDetailRepo) GetFirst(opts ...DBOption) (model.AppDetail, error) {\n\tvar detail model.AppDetail\n\terr := getDb(opts...).Model(&model.AppDetail{}).Find(&detail).Error\n\treturn detail, err\n}\n\nfunc (a AppDetailRepo) Update(ctx context.Context, detail model.AppDetail) error {\n\treturn getTx(ctx).Save(&detail).Error\n}\n\nfunc (a AppDetailRepo) BatchCreate(ctx context.Context, details []model.AppDetail) error {\n\treturn getTx(ctx).Model(&model.AppDetail{}).Create(&details).Error\n}\n\nfunc (a AppDetailRepo) DeleteByAppIds(ctx context.Context, appIds []uint) error {\n\treturn getTx(ctx).Where(\"app_id in (?)\", appIds).Delete(&model.AppDetail{}).Error\n}\n\nfunc (a AppDetailRepo) DeleteByIDs(ctx context.Context, appIds []uint) error {\n\treturn getTx(ctx).Where(\"id in (?)\", appIds).Delete(&model.AppDetail{}).Error\n}\n\nfunc (a AppDetailRepo) GetBy(opts ...DBOption) ([]model.AppDetail, error) {\n\tvar details []model.AppDetail\n\terr := getDb(opts...).Find(&details).Error\n\treturn details, err\n}\n\nfunc (a AppDetailRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error {\n\tdb := getDb(opts...).Model(&model.AppDetail{})\n\tif len(opts) == 0 {\n\t\tdb = db.Where(\"1=1\")\n\t}\n\treturn db.Updates(&maps).Error\n}\n\nfunc (a AppDetailRepo) BatchDelete(ctx context.Context, appDetails []model.AppDetail) error {\n\treturn getTx(ctx).Omit(clause.Associations).Delete(&appDetails).Error\n}\n"
  },
  {
    "path": "agent/app/repo/app_ignore_upgrade.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype AppIgnoreUpgradeRepo struct {\n}\n\ntype IAppIgnoreUpgradeRepo interface {\n\tWithScope(scope string) DBOption\n\tWithAppID(appID uint) DBOption\n\tList(opts ...DBOption) ([]model.AppIgnoreUpgrade, error)\n\tCreate(appIgnoreUpgrade *model.AppIgnoreUpgrade) error\n\tDelete(opts ...DBOption) error\n}\n\nfunc NewIAppIgnoreUpgradeRepo() IAppIgnoreUpgradeRepo {\n\treturn &AppIgnoreUpgradeRepo{}\n}\n\nfunc (a AppIgnoreUpgradeRepo) WithScope(scope string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"scope = ?\", scope)\n\t}\n}\n\nfunc (a AppIgnoreUpgradeRepo) WithAppID(appID uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_id = ?\", appID)\n\t}\n}\n\nfunc (a AppIgnoreUpgradeRepo) List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error) {\n\tvar appIgnoreUpgradeList []model.AppIgnoreUpgrade\n\terr := getDb(opts...).Find(&appIgnoreUpgradeList).Error\n\treturn appIgnoreUpgradeList, err\n}\n\nfunc (a AppIgnoreUpgradeRepo) Create(appIgnoreUpgrade *model.AppIgnoreUpgrade) error {\n\treturn global.DB.Create(appIgnoreUpgrade).Error\n}\n\nfunc (a AppIgnoreUpgradeRepo) Delete(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.AppIgnoreUpgrade{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/app_install.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\n\t\"gorm.io/gorm/clause\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype AppInstallRepo struct{}\n\ntype IAppInstallRepo interface {\n\tWithDetailIdsIn(detailIds []uint) DBOption\n\tWithDetailIdNotIn(detailIds []uint) DBOption\n\tWithAppId(appId uint) DBOption\n\tWithAppIdsIn(appIds []uint) DBOption\n\tWithAppIdsNotIn(appIds []uint) DBOption\n\tWithStatus(status string) DBOption\n\tWithServiceName(serviceName string) DBOption\n\tWithContainerName(containerName string) DBOption\n\tWithPort(port int) DBOption\n\tWithIdNotInWebsite() DBOption\n\tWithIDNotIs(id uint) DBOption\n\tListBy(ctx context.Context, opts ...DBOption) ([]model.AppInstall, error)\n\tGetFirst(opts ...DBOption) (model.AppInstall, error)\n\tCreate(ctx context.Context, install *model.AppInstall) error\n\tSave(ctx context.Context, install *model.AppInstall) error\n\tDeleteBy(opts ...DBOption) error\n\tDelete(ctx context.Context, install model.AppInstall) error\n\tPage(page, size int, opts ...DBOption) (int64, []model.AppInstall, error)\n\tBatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error\n\tLoadBaseInfo(key string, name string) (*RootInfo, error)\n\tLoadInstallAppByKeyAndName(key string, name string) (*model.AppInstall, error)\n\tGetFirstByCtx(ctx context.Context, opts ...DBOption) (model.AppInstall, error)\n}\n\nfunc NewIAppInstallRepo() IAppInstallRepo {\n\treturn &AppInstallRepo{}\n}\n\nfunc (a *AppInstallRepo) WithDetailIdsIn(detailIds []uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_detail_id in (?)\", detailIds)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithDetailIdNotIn(detailIds []uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_detail_id not in (?)\", detailIds)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithAppId(appId uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_id = ?\", appId)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithIDNotIs(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id != ?\", id)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithAppIdsIn(appIds []uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_id in (?)\", appIds)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithAppIdsNotIn(appIds []uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_id not in (?)\", appIds)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithStatus(status string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"status = ?\", status)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithServiceName(serviceName string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"service_name = ?\", serviceName)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithContainerName(containerName string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"container_name = ?\", containerName)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithPort(port int) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"https_port = ? or  http_port = ?\", port, port)\n\t}\n}\n\nfunc (a *AppInstallRepo) WithIdNotInWebsite() DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"id not in (select app_install_id from websites)\")\n\t}\n}\n\nfunc (a *AppInstallRepo) ListBy(ctx context.Context, opts ...DBOption) ([]model.AppInstall, error) {\n\tvar install []model.AppInstall\n\tdb := getTx(ctx, opts...).Model(&model.AppInstall{})\n\terr := db.Preload(\"App\").Find(&install).Error\n\treturn install, err\n}\n\nfunc (a *AppInstallRepo) GetFirst(opts ...DBOption) (model.AppInstall, error) {\n\tvar install model.AppInstall\n\tdb := getDb(opts...).Model(&model.AppInstall{})\n\terr := db.Preload(\"App\").First(&install).Error\n\treturn install, err\n}\n\nfunc (a *AppInstallRepo) GetFirstByCtx(ctx context.Context, opts ...DBOption) (model.AppInstall, error) {\n\tvar install model.AppInstall\n\tdb := getTx(ctx, opts...).Model(&model.AppInstall{})\n\terr := db.Preload(\"App\").First(&install).Error\n\treturn install, err\n}\n\nfunc (a *AppInstallRepo) Create(ctx context.Context, install *model.AppInstall) error {\n\tdb := getTx(ctx).Model(&model.AppInstall{})\n\treturn db.Omit(clause.Associations).Create(&install).Error\n}\n\nfunc (a *AppInstallRepo) Save(ctx context.Context, install *model.AppInstall) error {\n\treturn getTx(ctx).Omit(\"App\").Save(&install).Error\n}\n\nfunc (a *AppInstallRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.AppInstall{}).Error\n}\n\nfunc (a *AppInstallRepo) Delete(ctx context.Context, install model.AppInstall) error {\n\tdb := getTx(ctx).Model(&model.AppInstall{})\n\treturn db.Delete(&install).Error\n}\n\nfunc (a *AppInstallRepo) Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error) {\n\tvar apps []model.AppInstall\n\tdb := getDb(opts...).Model(&model.AppInstall{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Preload(\"App\").Find(&apps).Error\n\treturn count, apps, err\n}\n\nfunc (a *AppInstallRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error {\n\tdb := getDb(opts...).Model(&model.AppInstall{})\n\tif len(opts) == 0 {\n\t\tdb = db.Where(\"1=1\")\n\t}\n\treturn db.Updates(&maps).Error\n}\n\ntype RootInfo struct {\n\tID            uint   `json:\"id\"`\n\tName          string `json:\"name\"`\n\tStatus        string `json:\"status\"`\n\tPort          int64  `json:\"port\"`\n\tHttpsPort     int64  `json:\"httpsPort\"`\n\tUserName      string `json:\"userName\"`\n\tPassword      string `json:\"password\"`\n\tUserPassword  string `json:\"userPassword\"`\n\tContainerName string `json:\"containerName\"`\n\tServiceName   string `json:\"serviceName\"`\n\tParam         string `json:\"param\"`\n\tEnv           string `json:\"env\"`\n\tKey           string `json:\"key\"`\n\tVersion       string `json:\"version\"`\n\tAppPath       string `json:\"app_path\"`\n}\n\nfunc (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error) {\n\tvar (\n\t\tapp        model.App\n\t\tappInstall model.AppInstall\n\t\tinfo       RootInfo\n\t)\n\tif err := global.DB.Where(\"key = ?\", key).First(&app).Error; err != nil {\n\t\treturn nil, err\n\t}\n\tif len(name) == 0 {\n\t\tif err := global.DB.Where(\"app_id = ?\", app.ID).First(&appInstall).Error; err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tif err := global.DB.Where(\"app_id = ? AND name = ?\", app.ID, name).First(&appInstall).Error; err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tenvMap := make(map[string]interface{})\n\tif err := json.Unmarshal([]byte(appInstall.Env), &envMap); err != nil {\n\t\treturn nil, err\n\t}\n\tswitch app.Key {\n\tcase \"mysql\", \"mariadb\", constant.AppMysqlCluster:\n\t\tpassword, ok := envMap[\"PANEL_DB_ROOT_PASSWORD\"].(string)\n\t\tif ok {\n\t\t\tinfo.Password = password\n\t\t}\n\tcase \"redis\", constant.AppRedisCluster:\n\t\tpassword, ok := envMap[\"PANEL_REDIS_ROOT_PASSWORD\"].(string)\n\t\tif ok {\n\t\t\tinfo.Password = password\n\t\t}\n\tcase \"mongodb\", constant.AppPostgresql, constant.AppPostgresqlCluster:\n\t\tuser, ok := envMap[\"PANEL_DB_ROOT_USER\"].(string)\n\t\tif ok {\n\t\t\tinfo.UserName = user\n\t\t}\n\t\tpassword, ok := envMap[\"PANEL_DB_ROOT_PASSWORD\"].(string)\n\t\tif ok {\n\t\t\tinfo.Password = password\n\t\t}\n\t}\n\n\tuserPassword, ok := envMap[\"PANEL_DB_USER_PASSWORD\"].(string)\n\tif ok {\n\t\tinfo.UserPassword = userPassword\n\t}\n\tinfo.Port = int64(appInstall.HttpPort)\n\tinfo.HttpsPort = int64(appInstall.HttpsPort)\n\tinfo.ID = appInstall.ID\n\tinfo.ServiceName = appInstall.ServiceName\n\tinfo.ContainerName = appInstall.ContainerName\n\tinfo.Name = appInstall.Name\n\tinfo.Env = appInstall.Env\n\tinfo.Param = appInstall.Param\n\tinfo.Version = appInstall.Version\n\tinfo.Key = app.Key\n\tappInstall.App = app\n\tinfo.AppPath = appInstall.GetAppPath()\n\tinfo.Status = appInstall.Status\n\treturn &info, nil\n}\n\nfunc (a *AppInstallRepo) LoadInstallAppByKeyAndName(key string, name string) (*model.AppInstall, error) {\n\tvar (\n\t\tapp        model.App\n\t\tappInstall model.AppInstall\n\t)\n\tif err := global.DB.Where(\"key = ?\", key).First(&app).Error; err != nil {\n\t\treturn nil, err\n\t}\n\tif len(name) == 0 {\n\t\tif err := global.DB.Where(\"app_id = ?\", app.ID).First(&appInstall).Error; err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tif err := global.DB.Where(\"app_id = ? AND name = ?\", app.ID, name).First(&appInstall).Error; err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &appInstall, nil\n}\n"
  },
  {
    "path": "agent/app/repo/app_install_resource.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype AppInstallResourceRpo struct {\n}\n\ntype IAppInstallResourceRpo interface {\n\tWithAppInstallId(appInstallId uint) DBOption\n\tWithLinkId(linkId uint) DBOption\n\tWithResourceId(resourceId uint) DBOption\n\tGetBy(opts ...DBOption) ([]model.AppInstallResource, error)\n\tGetFirst(opts ...DBOption) (model.AppInstallResource, error)\n\tCreate(ctx context.Context, resource *model.AppInstallResource) error\n\tDeleteBy(ctx context.Context, opts ...DBOption) error\n\tBatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error\n}\n\nfunc NewIAppInstallResourceRpo() IAppInstallResourceRpo {\n\treturn &AppInstallResourceRpo{}\n}\n\nfunc (a AppInstallResourceRpo) WithAppInstallId(appInstallId uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"app_install_id = ?\", appInstallId)\n\t}\n}\n\nfunc (a AppInstallResourceRpo) WithLinkId(linkId uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"link_id = ?\", linkId)\n\t}\n}\n\nfunc (a AppInstallResourceRpo) WithResourceId(resourceId uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"resource_id = ?\", resourceId)\n\t}\n}\n\nfunc (a AppInstallResourceRpo) GetBy(opts ...DBOption) ([]model.AppInstallResource, error) {\n\tdb := global.DB.Model(&model.AppInstallResource{})\n\tvar resources []model.AppInstallResource\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&resources).Error\n\treturn resources, err\n}\n\nfunc (a AppInstallResourceRpo) GetFirst(opts ...DBOption) (model.AppInstallResource, error) {\n\tdb := global.DB.Model(&model.AppInstallResource{})\n\tvar resources model.AppInstallResource\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&resources).Error\n\treturn resources, err\n}\n\nfunc (a AppInstallResourceRpo) Create(ctx context.Context, resource *model.AppInstallResource) error {\n\tdb := getTx(ctx).Model(&model.AppInstallResource{})\n\treturn db.Create(&resource).Error\n}\n\nfunc (a AppInstallResourceRpo) DeleteBy(ctx context.Context, opts ...DBOption) error {\n\treturn getTx(ctx, opts...).Delete(&model.AppInstallResource{}).Error\n}\n\nfunc (a AppInstallResourceRpo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error {\n\tdb := getDb(opts...).Model(&model.AppInstallResource{})\n\tif len(opts) == 0 {\n\t\tdb = db.Where(\"1=1\")\n\t}\n\treturn db.Updates(&maps).Error\n}\n"
  },
  {
    "path": "agent/app/repo/app_launcher.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype LauncherRepo struct{}\n\ntype ILauncherRepo interface {\n\tGet(opts ...DBOption) (model.AppLauncher, error)\n\tListName(opts ...DBOption) ([]string, error)\n\tCreate(launcher *model.AppLauncher) error\n\tSave(launcher *model.AppLauncher) error\n\tDelete(opts ...DBOption) error\n\n\tGetQuickJump(opts ...DBOption) (model.QuickJump, error)\n\tListQuickJump(withAll bool) []model.QuickJump\n\tUpdateQuicks(quicks []model.QuickJump) error\n}\n\nfunc NewILauncherRepo() ILauncherRepo {\n\treturn &LauncherRepo{}\n}\n\nfunc (u *LauncherRepo) Get(opts ...DBOption) (model.AppLauncher, error) {\n\tvar launcher model.AppLauncher\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&launcher).Error\n\treturn launcher, err\n}\nfunc (u *LauncherRepo) ListName(opts ...DBOption) ([]string, error) {\n\tvar ops []model.AppLauncher\n\tdb := global.DB.Model(&model.AppLauncher{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\t_ = db.Find(&ops).Error\n\tvar names []string\n\tfor i := 0; i < len(ops); i++ {\n\t\tnames = append(names, ops[i].Key)\n\t}\n\treturn names, nil\n}\n\nfunc (u *LauncherRepo) Create(launcher *model.AppLauncher) error {\n\treturn global.DB.Create(launcher).Error\n}\n\nfunc (u *LauncherRepo) Save(launcher *model.AppLauncher) error {\n\treturn global.DB.Save(launcher).Error\n}\n\nfunc (u *LauncherRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.AppLauncher{}).Error\n}\n\nfunc (u *LauncherRepo) GetQuickJump(opts ...DBOption) (model.QuickJump, error) {\n\tvar launcher model.QuickJump\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&launcher).Error\n\treturn launcher, err\n}\nfunc (u *LauncherRepo) ListQuickJump(withAll bool) []model.QuickJump {\n\tvar quicks []model.QuickJump\n\tif withAll {\n\t\t_ = global.DB.Find(&quicks).Error\n\t} else {\n\t\t_ = global.DB.Where(\"is_show = ?\", true).Find(&quicks).Error\n\t}\n\tif !withAll && len(quicks) == 0 {\n\t\treturn []model.QuickJump{\n\t\t\t{Name: \"Agent\", Title: \"aiTools.agents.agents\", Recommend: 1, IsShow: true, Router: \"/ai/agents/agent\"},\n\t\t\t{Name: \"Website\", Title: \"menu.website\", Recommend: 10, IsShow: true, Router: \"/websites\"},\n\t\t\t{Name: \"Database\", Title: \"menu.database\", Recommend: 30, IsShow: true, Router: \"/databases\"},\n\t\t\t{Name: \"Cronjob\", Title: \"menu.cronjob\", Recommend: 50, IsShow: false, Router: \"/cronjobs\"},\n\t\t\t{Name: \"AppInstalled\", Title: \"home.appInstalled\", Recommend: 70, IsShow: true, Router: \"/apps/installed\"},\n\t\t}\n\t}\n\n\treturn quicks\n}\nfunc (u *LauncherRepo) UpdateQuicks(quicks []model.QuickJump) error {\n\ttx := global.DB.Begin()\n\tfor _, item := range quicks {\n\t\tif err := tx.Model(&model.QuickJump{}).Where(\"id = ?\", item.ID).Updates(map[string]interface{}{\n\t\t\t\"is_show\": item.IsShow,\n\t\t\t\"detail\":  item.Detail,\n\t\t\t\"alias\":   item.Alias,\n\t\t}).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\ttx.Commit()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/repo/app_tag.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"gorm.io/gorm\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype AppTagRepo struct {\n}\n\ntype IAppTagRepo interface {\n\tBatchCreate(ctx context.Context, tags []*model.AppTag) error\n\tDeleteByAppIds(ctx context.Context, appIds []uint) error\n\tDeleteAll(ctx context.Context) error\n\tGetByAppId(appId uint) ([]model.AppTag, error)\n\tGetByTagIds(tagIds []uint) ([]model.AppTag, error)\n\tDeleteBy(ctx context.Context, opts ...DBOption) error\n\tGetFirst(ctx context.Context, opts ...DBOption) (*model.AppTag, error)\n\n\tWithByTagID(tagID uint) DBOption\n\tWithByAppID(appId uint) DBOption\n}\n\nfunc NewIAppTagRepo() IAppTagRepo {\n\treturn &AppTagRepo{}\n}\n\nfunc (a AppTagRepo) WithByTagID(tagID uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"tag_id = ?\", tagID)\n\t}\n}\n\nfunc (a AppTagRepo) WithByAppID(appId uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_id = ?\", appId)\n\t}\n}\n\nfunc (a AppTagRepo) BatchCreate(ctx context.Context, tags []*model.AppTag) error {\n\treturn getTx(ctx).Create(&tags).Error\n}\n\nfunc (a AppTagRepo) DeleteByAppIds(ctx context.Context, appIds []uint) error {\n\treturn getTx(ctx).Where(\"app_id in (?)\", appIds).Delete(&model.AppTag{}).Error\n}\n\nfunc (a AppTagRepo) DeleteAll(ctx context.Context) error {\n\treturn getTx(ctx).Where(\"1 = 1\").Delete(&model.AppTag{}).Error\n}\n\nfunc (a AppTagRepo) GetByAppId(appId uint) ([]model.AppTag, error) {\n\tvar appTags []model.AppTag\n\tif err := getDb().Where(\"app_id = ?\", appId).Find(&appTags).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn appTags, nil\n}\n\nfunc (a AppTagRepo) GetByTagIds(tagIds []uint) ([]model.AppTag, error) {\n\tvar appTags []model.AppTag\n\tif err := getDb().Where(\"tag_id in (?)\", tagIds).Find(&appTags).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn appTags, nil\n}\n\nfunc (a AppTagRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {\n\treturn getTx(ctx, opts...).Delete(&model.AppTag{}).Error\n}\n\nfunc (a AppTagRepo) GetFirst(ctx context.Context, opts ...DBOption) (*model.AppTag, error) {\n\tvar appTag model.AppTag\n\tif err := getTx(ctx, opts...).First(&appTag).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &appTag, nil\n\n}\n"
  },
  {
    "path": "agent/app/repo/backup.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype BackupRepo struct{}\n\ntype IBackupRepo interface {\n\tGet(opts ...DBOption) (model.BackupAccount, error)\n\tList(opts ...DBOption) ([]model.BackupAccount, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.BackupAccount, error)\n\tCreate(backup *model.BackupAccount) error\n\tSave(backup *model.BackupAccount) error\n\tDelete(opts ...DBOption) error\n\tWithByPublic(isPublic bool) DBOption\n\n\tListRecord(opts ...DBOption) ([]model.BackupRecord, error)\n\tGetRecord(opts ...DBOption) (*model.BackupRecord, error)\n\tPageRecord(page, size int, opts ...DBOption) (int64, []model.BackupRecord, error)\n\tCreateRecord(record *model.BackupRecord) error\n\tDeleteRecord(ctx context.Context, opts ...DBOption) error\n\tUpdateRecord(record *model.BackupRecord) error\n\tUpdateRecordByMap(id uint, upMap map[string]interface{}) error\n\tWithByDetailName(detailName string) DBOption\n\tWithByFileName(fileName string) DBOption\n\tWithByCronID(cronjobID uint) DBOption\n\tWithFileNameStartWith(filePrefix string) DBOption\n\n\tSyncAll(data []model.BackupAccount) error\n}\n\nfunc NewIBackupRepo() IBackupRepo {\n\treturn &BackupRepo{}\n}\n\nfunc (u *BackupRepo) WithByPublic(isPublic bool) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"is_public = ?\", isPublic)\n\t}\n}\n\nfunc (u *BackupRepo) Get(opts ...DBOption) (model.BackupAccount, error) {\n\tvar backup model.BackupAccount\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&backup).Error\n\treturn backup, err\n}\n\nfunc (u *BackupRepo) Page(page, size int, opts ...DBOption) (int64, []model.BackupAccount, error) {\n\tvar ops []model.BackupAccount\n\tdb := global.DB.Model(&model.BackupAccount{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error\n\treturn count, ops, err\n}\n\nfunc (u *BackupRepo) List(opts ...DBOption) ([]model.BackupAccount, error) {\n\tvar ops []model.BackupAccount\n\tdb := global.DB.Model(&model.BackupAccount{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&ops).Error\n\treturn ops, err\n}\n\nfunc (u *BackupRepo) Create(backup *model.BackupAccount) error {\n\treturn global.DB.Create(backup).Error\n}\n\nfunc (u *BackupRepo) Save(backup *model.BackupAccount) error {\n\treturn global.DB.Save(backup).Error\n}\n\nfunc (u *BackupRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.BackupAccount{}).Error\n}\n\nfunc (u *BackupRepo) ListRecord(opts ...DBOption) ([]model.BackupRecord, error) {\n\tvar users []model.BackupRecord\n\tdb := global.DB.Model(&model.BackupRecord{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&users).Error\n\treturn users, err\n}\n\nfunc (u *BackupRepo) PageRecord(page, size int, opts ...DBOption) (int64, []model.BackupRecord, error) {\n\tvar users []model.BackupRecord\n\tdb := global.DB.Model(&model.BackupRecord{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error\n\treturn count, users, err\n}\n\nfunc (u *BackupRepo) WithByFileName(fileName string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(fileName) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"file_name = ?\", fileName)\n\t}\n}\n\nfunc (u *BackupRepo) WithByDetailName(detailName string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(detailName) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"detail_name = ?\", detailName)\n\t}\n}\n\nfunc (u *BackupRepo) WithFileNameStartWith(filePrefix string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"file_name LIKE ?\", filePrefix+\"%\")\n\t}\n}\n\nfunc (u *BackupRepo) CreateRecord(record *model.BackupRecord) error {\n\treturn global.DB.Create(record).Error\n}\n\nfunc (u *BackupRepo) UpdateRecord(record *model.BackupRecord) error {\n\treturn global.DB.Save(record).Error\n}\n\nfunc (u *BackupRepo) UpdateRecordByMap(id uint, upMap map[string]interface{}) error {\n\treturn global.DB.Model(&model.BackupRecord{}).Where(\"id = ?\", id).Updates(upMap).Error\n}\n\nfunc (u *BackupRepo) DeleteRecord(ctx context.Context, opts ...DBOption) error {\n\treturn getTx(ctx, opts...).Delete(&model.BackupRecord{}).Error\n}\n\nfunc (u *BackupRepo) WithByCronID(cronjobID uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"cronjob_id = ?\", cronjobID)\n\t}\n}\n\nfunc (u *BackupRepo) GetRecord(opts ...DBOption) (*model.BackupRecord, error) {\n\trecord := &model.BackupRecord{}\n\tdb := global.DB.Model(&model.BackupRecord{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(record).Error\n\treturn record, err\n}\n\nfunc (u *BackupRepo) SyncAll(data []model.BackupAccount) error {\n\ttx := global.DB.Begin()\n\tvar oldAccounts []model.BackupAccount\n\t_ = tx.Where(\"is_public = ?\", 1).Find(&oldAccounts).Error\n\toldAccountMap := make(map[string]uint)\n\tfor _, item := range oldAccounts {\n\t\toldAccountMap[item.Name] = item.ID\n\t}\n\tfor _, item := range data {\n\t\tif val, ok := oldAccountMap[item.Name]; ok {\n\t\t\titem.ID = val\n\t\t\tdelete(oldAccountMap, item.Name)\n\t\t} else {\n\t\t\titem.ID = 0\n\t\t}\n\t\tif err := tx.Model(model.BackupAccount{}).Where(\"id = ?\", item.ID).Save(&item).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, val := range oldAccountMap {\n\t\tif err := tx.Where(\"id = ?\", val).Delete(&model.BackupAccount{}).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\ttx.Commit()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/repo/clam.go",
    "content": "package repo\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\ntype ClamRepo struct{}\n\ntype IClamRepo interface {\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.Clam, error)\n\tCreate(clam *model.Clam) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(opts ...DBOption) error\n\tGet(opts ...DBOption) (model.Clam, error)\n\tList(opts ...DBOption) ([]model.Clam, error)\n\n\tWithByClamID(id uint) DBOption\n\tListRecord(opts ...DBOption) ([]model.ClamRecord, error)\n\tRecordFirst(id uint) (model.ClamRecord, error)\n\tDeleteRecord(opts ...DBOption) error\n\tStartRecords(clamID uint) model.ClamRecord\n\tEndRecords(record model.ClamRecord, status, message string)\n\tPageRecords(page, size int, opts ...DBOption) (int64, []model.ClamRecord, error)\n}\n\nfunc NewIClamRepo() IClamRepo {\n\treturn &ClamRepo{}\n}\n\nfunc (u *ClamRepo) Get(opts ...DBOption) (model.Clam, error) {\n\tvar clam model.Clam\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&clam).Error\n\treturn clam, err\n}\n\nfunc (u *ClamRepo) List(opts ...DBOption) ([]model.Clam, error) {\n\tvar clam []model.Clam\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&clam).Error\n\treturn clam, err\n}\n\nfunc (u *ClamRepo) Page(page, size int, opts ...DBOption) (int64, []model.Clam, error) {\n\tvar users []model.Clam\n\tdb := global.DB.Model(&model.Clam{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error\n\treturn count, users, err\n}\n\nfunc (u *ClamRepo) Create(clam *model.Clam) error {\n\treturn global.DB.Create(clam).Error\n}\n\nfunc (u *ClamRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Clam{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *ClamRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Clam{}).Error\n}\n\nfunc (c *ClamRepo) WithByClamID(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"clam_id = ?\", id)\n\t}\n}\n\nfunc (u *ClamRepo) ListRecord(opts ...DBOption) ([]model.ClamRecord, error) {\n\tvar record []model.ClamRecord\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&record).Error\n\treturn record, err\n}\n\nfunc (u *ClamRepo) RecordFirst(id uint) (model.ClamRecord, error) {\n\tvar record model.ClamRecord\n\terr := global.DB.Where(\"clam_id = ?\", id).Order(\"created_at desc\").First(&record).Error\n\treturn record, err\n}\n\nfunc (u *ClamRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.ClamRecord, error) {\n\tvar records []model.ClamRecord\n\tdb := global.DB.Model(&model.ClamRecord{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Order(\"created_at desc\").Limit(size).Offset(size * (page - 1)).Find(&records).Error\n\treturn count, records, err\n}\nfunc (u *ClamRepo) StartRecords(clamID uint) model.ClamRecord {\n\tvar record model.ClamRecord\n\trecord.StartTime = time.Now()\n\trecord.ClamID = clamID\n\trecord.TaskID = uuid.New().String()\n\trecord.Status = constant.StatusWaiting\n\tif err := global.DB.Create(&record).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"create record status failed, err: %v\", err)\n\t}\n\t_ = u.Update(clamID, map[string]interface{}{\"is_executing\": true})\n\treturn record\n}\nfunc (u *ClamRepo) EndRecords(record model.ClamRecord, status, message string) {\n\tupMap := make(map[string]interface{})\n\tupMap[\"status\"] = status\n\tupMap[\"message\"] = message\n\tupMap[\"task_id\"] = record.TaskID\n\tupMap[\"scan_time\"] = record.ScanTime\n\tupMap[\"infected_files\"] = record.InfectedFiles\n\tupMap[\"total_error\"] = record.TotalError\n\tif err := global.DB.Model(&model.ClamRecord{}).Where(\"id = ?\", record.ID).Updates(upMap).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"update record status failed, err: %v\", err)\n\t}\n\t_ = u.Update(record.ClamID, map[string]interface{}{\"is_executing\": false})\n}\nfunc (u *ClamRepo) DeleteRecord(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.ClamRecord{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/common.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"gorm.io/gorm\"\n)\n\ntype DBOption func(*gorm.DB) *gorm.DB\n\nfunc WithByID(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id = ?\", id)\n\t}\n}\n\nfunc WithByGroupID(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"group_id = ?\", id)\n\t}\n}\n\nfunc WithByNOTID(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id != ?\", id)\n\t}\n}\n\nfunc WithByIDs(ids []uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id in (?)\", ids)\n\t}\n}\n\nfunc WithByIDNotIn(ids []uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id not in (?)\", ids)\n\t}\n}\n\nfunc WithByName(name string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"name = ?\", name)\n\t}\n}\n\nfunc WithByAddr(addr string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"addr = ?\", addr)\n\t}\n}\n\nfunc WithByKey(key string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"key = ?\", key)\n\t}\n}\n\nfunc WithByLowerName(name string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"LOWER(name) = LOWER(?)\", name)\n\t}\n}\n\nfunc WithByLikeName(name string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(name) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"name like ?\", \"%\"+name+\"%\")\n\t}\n}\n\nfunc WithByDetailName(detailName string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(detailName) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"detail_name = ?\", detailName)\n\t}\n}\n\nfunc WithByProvider(provider string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(provider) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"provider = ?\", provider)\n\t}\n}\n\nfunc WithByModel(model string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(model) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"model = ?\", model)\n\t}\n}\n\nfunc WithByAccountID(accountID uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif accountID == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"account_id = ?\", accountID)\n\t}\n}\n\nfunc WithByType(tp string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"`type` = ?\", tp)\n\t}\n}\nfunc WithByDetailType(tp string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"`detail_type` = ?\", tp)\n\t}\n}\n\nfunc WithTypes(types []string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"`type` in (?)\", types)\n\t}\n}\n\nfunc WithByStatus(status string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(status) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"status = ?\", status)\n\t}\n}\nfunc WithByFrom(from string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"`from` = ?\", from)\n\t}\n}\n\nfunc WithByDate(startTime, endTime time.Time) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"start_time > ? AND start_time < ?\", startTime, endTime)\n\t}\n}\n\nfunc WithByGroups(groupIDs []uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(groupIDs) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"group_id in (?)\", groupIDs)\n\t}\n}\n\nfunc WithByCreatedAt(startTime, endTime time.Time) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"created_at > ? AND created_at < ?\", startTime, endTime)\n\t}\n}\n\nfunc WithOrderDesc(orderBy string) DBOption {\n\treturn WithOrderRuleBy(orderBy, constant.Desc)\n}\n\nfunc WithOrderAsc(orderBy string) DBOption {\n\treturn WithOrderRuleBy(orderBy, constant.Asc)\n}\n\nfunc WithOrderRuleBy(orderBy, order string) DBOption {\n\tif orderBy == \"createdAt\" {\n\t\torderBy = \"created_at\"\n\t}\n\tif !re.GetRegex(re.OrderByValidationPattern).MatchString(orderBy) {\n\t\torderBy = \"created_at\"\n\t}\n\tswitch order {\n\tcase constant.OrderDesc:\n\t\torder = \"desc\"\n\tcase constant.OrderAsc:\n\t\torder = \"asc\"\n\tcase constant.Desc:\n\t\torder = \"desc\"\n\tcase constant.Asc:\n\t\torder = \"asc\"\n\tdefault:\n\t\torderBy = \"created_at\"\n\t\torder = \"desc\"\n\t}\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Order(fmt.Sprintf(\"%s %s\", orderBy, order))\n\t}\n}\n\nfunc getTx(ctx context.Context, opts ...DBOption) *gorm.DB {\n\ttx, ok := ctx.Value(constant.DB).(*gorm.DB)\n\tif ok {\n\t\tfor _, opt := range opts {\n\t\t\ttx = opt(tx)\n\t\t}\n\t\treturn tx\n\t}\n\treturn getDb(opts...)\n}\n\nfunc getDb(opts ...DBOption) *gorm.DB {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db\n}\n"
  },
  {
    "path": "agent/app/repo/compose_template.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype ComposeTemplateRepo struct{}\n\ntype IComposeTemplateRepo interface {\n\tGet(opts ...DBOption) (model.ComposeTemplate, error)\n\tList(opts ...DBOption) ([]model.ComposeTemplate, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.ComposeTemplate, error)\n\tCreate(compose *model.ComposeTemplate) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(opts ...DBOption) error\n\n\tGetRecord(opts ...DBOption) (model.Compose, error)\n\tCreateRecord(compose *model.Compose) error\n\tDeleteRecord(opts ...DBOption) error\n\tListRecord() ([]model.Compose, error)\n\tUpdateRecord(name string, vars map[string]interface{}) error\n}\n\nfunc NewIComposeTemplateRepo() IComposeTemplateRepo {\n\treturn &ComposeTemplateRepo{}\n}\n\nfunc (u *ComposeTemplateRepo) Get(opts ...DBOption) (model.ComposeTemplate, error) {\n\tvar compose model.ComposeTemplate\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&compose).Error\n\treturn compose, err\n}\n\nfunc (u *ComposeTemplateRepo) Page(page, size int, opts ...DBOption) (int64, []model.ComposeTemplate, error) {\n\tvar users []model.ComposeTemplate\n\tdb := global.DB.Model(&model.ComposeTemplate{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error\n\treturn count, users, err\n}\n\nfunc (u *ComposeTemplateRepo) List(opts ...DBOption) ([]model.ComposeTemplate, error) {\n\tvar composes []model.ComposeTemplate\n\tdb := global.DB.Model(&model.ComposeTemplate{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&composes).Error\n\treturn composes, err\n}\n\nfunc (u *ComposeTemplateRepo) Create(compose *model.ComposeTemplate) error {\n\treturn global.DB.Create(compose).Error\n}\n\nfunc (u *ComposeTemplateRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.ComposeTemplate{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *ComposeTemplateRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.ComposeTemplate{}).Error\n}\n\nfunc (u *ComposeTemplateRepo) GetRecord(opts ...DBOption) (model.Compose, error) {\n\tvar compose model.Compose\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&compose).Error\n\treturn compose, err\n}\n\nfunc (u *ComposeTemplateRepo) ListRecord() ([]model.Compose, error) {\n\tvar composes []model.Compose\n\tif err := global.DB.Find(&composes).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn composes, nil\n}\n\nfunc (u *ComposeTemplateRepo) CreateRecord(compose *model.Compose) error {\n\treturn global.DB.Create(compose).Error\n}\n\nfunc (u *ComposeTemplateRepo) DeleteRecord(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Compose{}).Error\n}\nfunc (u *ComposeTemplateRepo) UpdateRecord(name string, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Compose{}).Where(\"name = ?\", name).Updates(vars).Error\n}\n"
  },
  {
    "path": "agent/app/repo/cronjob.go",
    "content": "package repo\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/google/uuid\"\n\t\"gorm.io/gorm\"\n)\n\ntype CronjobRepo struct{}\n\ntype ICronjobRepo interface {\n\tGet(opts ...DBOption) (model.Cronjob, error)\n\tGetRecord(opts ...DBOption) (model.JobRecords, error)\n\tRecordFirst(id uint) (model.JobRecords, error)\n\tListRecord(opts ...DBOption) ([]model.JobRecords, error)\n\tList(opts ...DBOption) ([]model.Cronjob, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)\n\tCreate(cronjob *model.Cronjob) error\n\tWithByJobID(id int) DBOption\n\tWithByDbName(name string) DBOption\n\tWithByDownloadAccountID(id uint) DBOption\n\tWithByRecordDropID(id int) DBOption\n\tWithByRecordFile(file string) DBOption\n\tSave(id uint, cronjob model.Cronjob) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(opts ...DBOption) error\n\tDeleteRecord(opts ...DBOption) error\n\tStartRecords(cronjobID uint) model.JobRecords\n\tUpdateRecords(id uint, vars map[string]interface{}) error\n\tEndRecords(record model.JobRecords, status, message, records string)\n\tAddFailedRecord(cronjobID uint, message string)\n\tPageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error)\n}\n\nfunc NewICronjobRepo() ICronjobRepo {\n\treturn &CronjobRepo{}\n}\n\nfunc (u *CronjobRepo) Get(opts ...DBOption) (model.Cronjob, error) {\n\tvar cronjob model.Cronjob\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&cronjob).Error\n\treturn cronjob, err\n}\n\nfunc (u *CronjobRepo) GetRecord(opts ...DBOption) (model.JobRecords, error) {\n\tvar record model.JobRecords\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&record).Error\n\treturn record, err\n}\n\nfunc (u *CronjobRepo) List(opts ...DBOption) ([]model.Cronjob, error) {\n\tvar cronjobs []model.Cronjob\n\tdb := global.DB.Model(&model.Cronjob{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&cronjobs).Error\n\treturn cronjobs, err\n}\n\nfunc (u *CronjobRepo) ListRecord(opts ...DBOption) ([]model.JobRecords, error) {\n\tvar cronjobs []model.JobRecords\n\tdb := global.DB.Model(&model.JobRecords{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&cronjobs).Error\n\treturn cronjobs, err\n}\n\nfunc (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cronjob, error) {\n\tvar cronjobs []model.Cronjob\n\tdb := global.DB.Model(&model.Cronjob{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&cronjobs).Error\n\treturn count, cronjobs, err\n}\n\nfunc (u *CronjobRepo) RecordFirst(id uint) (model.JobRecords, error) {\n\tvar record model.JobRecords\n\terr := global.DB.Where(\"cronjob_id = ?\", id).Order(\"created_at desc\").First(&record).Error\n\treturn record, err\n}\n\nfunc (u *CronjobRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error) {\n\tvar cronjobs []model.JobRecords\n\tdb := global.DB.Model(&model.JobRecords{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Order(\"created_at desc\").Limit(size).Offset(size * (page - 1)).Find(&cronjobs).Error\n\treturn count, cronjobs, err\n}\n\nfunc (u *CronjobRepo) Create(cronjob *model.Cronjob) error {\n\treturn global.DB.Create(cronjob).Error\n}\n\nfunc (c *CronjobRepo) WithByJobID(id int) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"cronjob_id = ?\", id)\n\t}\n}\n\nfunc (c *CronjobRepo) WithByDbName(name string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"db_name = ?\", name)\n\t}\n}\n\nfunc (c *CronjobRepo) WithByDownloadAccountID(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"download_account_id = ?\", id)\n\t}\n}\n\nfunc (c *CronjobRepo) WithByRecordFile(file string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"records = ?\", file)\n\t}\n}\n\nfunc (c *CronjobRepo) WithByRecordDropID(id int) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id < ?\", id)\n\t}\n}\n\nfunc (u *CronjobRepo) StartRecords(cronjobID uint) model.JobRecords {\n\tvar record model.JobRecords\n\trecord.StartTime = time.Now()\n\trecord.CronjobID = cronjobID\n\trecord.TaskID = uuid.New().String()\n\trecord.Status = constant.StatusWaiting\n\tif err := global.DB.Create(&record).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"create record status failed, err: %v\", err)\n\t}\n\t_ = u.Update(cronjobID, map[string]interface{}{\"is_executing\": true})\n\treturn record\n}\nfunc (u *CronjobRepo) EndRecords(record model.JobRecords, status, message, records string) {\n\terrMap := make(map[string]interface{})\n\terrMap[\"records\"] = records\n\terrMap[\"status\"] = status\n\terrMap[\"file\"] = record.File\n\terrMap[\"message\"] = message\n\terrMap[\"task_id\"] = record.TaskID\n\terrMap[\"interval\"] = time.Since(record.StartTime).Milliseconds()\n\tif err := global.DB.Model(&model.JobRecords{}).Where(\"id = ?\", record.ID).Updates(errMap).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"update record status failed, err: %v\", err)\n\t}\n\t_ = u.Update(record.CronjobID, map[string]interface{}{\"is_executing\": false})\n}\nfunc (u *CronjobRepo) AddFailedRecord(cronjobID uint, message string) {\n\tvar record model.JobRecords\n\trecord.StartTime = time.Now()\n\trecord.CronjobID = cronjobID\n\trecord.Status = constant.StatusUnexecuted\n\trecord.Message = message\n\tif err := global.DB.Create(&record).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"create record status failed, err: %v\", err)\n\t}\n}\n\nfunc (u *CronjobRepo) Save(id uint, cronjob model.Cronjob) error {\n\treturn global.DB.Model(&model.Cronjob{}).Where(\"id = ?\", id).Save(&cronjob).Error\n}\nfunc (u *CronjobRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Cronjob{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *CronjobRepo) UpdateRecords(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.JobRecords{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *CronjobRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Cronjob{}).Error\n}\nfunc (u *CronjobRepo) DeleteRecord(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.JobRecords{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/database.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"gorm.io/gorm\"\n)\n\ntype DatabaseRepo struct{}\n\ntype IDatabaseRepo interface {\n\tGetList(opts ...DBOption) ([]model.Database, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.Database, error)\n\tCreate(ctx context.Context, database *model.Database) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(ctx context.Context, opts ...DBOption) error\n\tGet(opts ...DBOption) (model.Database, error)\n\tWithoutByFrom(from string) DBOption\n\tWithAppInstallID(appInstallID uint) DBOption\n\tWithTypeList(dbType string) DBOption\n}\n\nfunc NewIDatabaseRepo() IDatabaseRepo {\n\treturn &DatabaseRepo{}\n}\n\nfunc (d *DatabaseRepo) Get(opts ...DBOption) (model.Database, error) {\n\tvar database model.Database\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tif err := db.First(&database).Error; err != nil {\n\t\treturn database, err\n\t}\n\tpass, err := encrypt.StringDecrypt(database.Password)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"decrypt database %s password failed, err: %v\", database.Name, err)\n\t}\n\tdatabase.Password = pass\n\treturn database, nil\n}\n\nfunc (d *DatabaseRepo) Page(page, size int, opts ...DBOption) (int64, []model.Database, error) {\n\tvar databases []model.Database\n\tdb := global.DB.Model(&model.Database{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\tif err := db.Limit(size).Offset(size * (page - 1)).Find(&databases).Error; err != nil {\n\t\treturn count, databases, err\n\t}\n\tfor i := 0; i < len(databases); i++ {\n\t\tpass, err := encrypt.StringDecrypt(databases[i].Password)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"decrypt database db %s password failed, err: %v\", databases[i].Name, err)\n\t\t}\n\t\tdatabases[i].Password = pass\n\t}\n\treturn count, databases, nil\n}\n\nfunc (d *DatabaseRepo) GetList(opts ...DBOption) ([]model.Database, error) {\n\tvar databases []model.Database\n\tdb := global.DB.Model(&model.Database{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tif err := db.Find(&databases).Error; err != nil {\n\t\treturn databases, err\n\t}\n\tfor i := 0; i < len(databases); i++ {\n\t\tpass, err := encrypt.StringDecrypt(databases[i].Password)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"decrypt database db %s password failed, err: %v\", databases[i].Name, err)\n\t\t}\n\t\tdatabases[i].Password = pass\n\t}\n\treturn databases, nil\n}\n\nfunc (d *DatabaseRepo) WithoutByFrom(from string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"`from` != ?\", from)\n\t}\n}\n\nfunc (d *DatabaseRepo) WithTypeList(dbType string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif !strings.Contains(dbType, \",\") {\n\t\t\treturn g.Where(\"`type` = ?\", dbType)\n\t\t}\n\t\ttypes := strings.Split(dbType, \",\")\n\t\tvar (\n\t\t\trules  []string\n\t\t\tvalues []interface{}\n\t\t)\n\t\tfor _, ty := range types {\n\t\t\tif len(ty) != 0 {\n\t\t\t\trules = append(rules, \"`type` = ?\")\n\t\t\t\tvalues = append(values, ty)\n\t\t\t}\n\t\t}\n\t\treturn g.Where(strings.Join(rules, \" OR \"), values...)\n\t}\n}\n\nfunc (d *DatabaseRepo) WithAppInstallID(appInstallID uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_install_id = ?\", appInstallID)\n\t}\n}\n\nfunc (d *DatabaseRepo) Create(ctx context.Context, database *model.Database) error {\n\tpass, err := encrypt.StringEncrypt(database.Password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decrypt database db %s password failed, err: %v\", database.Name, err)\n\t}\n\tdatabase.Password = pass\n\treturn getTx(ctx).Create(database).Error\n}\n\nfunc (d *DatabaseRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Database{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (d *DatabaseRepo) Delete(ctx context.Context, opts ...DBOption) error {\n\treturn getTx(ctx, opts...).Delete(&model.Database{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/database_mysql.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"gorm.io/gorm\"\n)\n\ntype MysqlRepo struct{}\n\ntype IMysqlRepo interface {\n\tGet(opts ...DBOption) (model.DatabaseMysql, error)\n\tWithByMysqlName(mysqlName string) DBOption\n\tList(opts ...DBOption) ([]model.DatabaseMysql, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.DatabaseMysql, error)\n\tCreate(ctx context.Context, mysql *model.DatabaseMysql) error\n\tDelete(ctx context.Context, opts ...DBOption) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDeleteLocal(ctx context.Context) error\n}\n\nfunc NewIMysqlRepo() IMysqlRepo {\n\treturn &MysqlRepo{}\n}\n\nfunc (u *MysqlRepo) Get(opts ...DBOption) (model.DatabaseMysql, error) {\n\tvar mysql model.DatabaseMysql\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tif err := db.First(&mysql).Error; err != nil {\n\t\treturn mysql, err\n\t}\n\n\tpass, err := encrypt.StringDecrypt(mysql.Password)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"decrypt database db %s password failed, err: %v\", mysql.Name, err)\n\t}\n\tmysql.Password = pass\n\treturn mysql, err\n}\n\nfunc (u *MysqlRepo) List(opts ...DBOption) ([]model.DatabaseMysql, error) {\n\tvar mysqls []model.DatabaseMysql\n\tdb := global.DB.Model(&model.DatabaseMysql{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tif err := db.Find(&mysqls).Error; err != nil {\n\t\treturn mysqls, err\n\t}\n\tfor i := 0; i < len(mysqls); i++ {\n\t\tpass, err := encrypt.StringDecrypt(mysqls[i].Password)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"decrypt database db %s password failed, err: %v\", mysqls[i].Name, err)\n\t\t}\n\t\tmysqls[i].Password = pass\n\t}\n\treturn mysqls, nil\n}\n\nfunc (u *MysqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabaseMysql, error) {\n\tvar mysqls []model.DatabaseMysql\n\tdb := global.DB.Model(&model.DatabaseMysql{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\tif err := db.Limit(size).Offset(size * (page - 1)).Find(&mysqls).Error; err != nil {\n\t\treturn count, mysqls, err\n\t}\n\tfor i := 0; i < len(mysqls); i++ {\n\t\tpass, err := encrypt.StringDecrypt(mysqls[i].Password)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"decrypt database db %s password failed, err: %v\", mysqls[i].Name, err)\n\t\t}\n\t\tmysqls[i].Password = pass\n\t}\n\treturn count, mysqls, nil\n}\n\nfunc (u *MysqlRepo) Create(ctx context.Context, mysql *model.DatabaseMysql) error {\n\tpass, err := encrypt.StringEncrypt(mysql.Password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decrypt database db %s password failed, err: %v\", mysql.Name, err)\n\t}\n\tmysql.Password = pass\n\treturn getTx(ctx).Create(mysql).Error\n}\n\nfunc (u *MysqlRepo) Delete(ctx context.Context, opts ...DBOption) error {\n\treturn getTx(ctx, opts...).Delete(&model.DatabaseMysql{}).Error\n}\n\nfunc (u *MysqlRepo) DeleteLocal(ctx context.Context) error {\n\treturn getTx(ctx).Where(\"`from` = ?\", \"local\").Delete(&model.DatabaseMysql{}).Error\n}\n\nfunc (u *MysqlRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.DatabaseMysql{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *MysqlRepo) WithByMysqlName(mysqlName string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"mysql_name = ?\", mysqlName)\n\t}\n}\n"
  },
  {
    "path": "agent/app/repo/database_postgresql.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"gorm.io/gorm\"\n)\n\ntype PostgresqlRepo struct{}\n\ntype IPostgresqlRepo interface {\n\tGet(opts ...DBOption) (model.DatabasePostgresql, error)\n\tWithByPostgresqlName(postgresqlName string) DBOption\n\tList(opts ...DBOption) ([]model.DatabasePostgresql, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.DatabasePostgresql, error)\n\tCreate(ctx context.Context, postgresql *model.DatabasePostgresql) error\n\tDelete(ctx context.Context, opts ...DBOption) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDeleteLocal(ctx context.Context) error\n}\n\nfunc NewIPostgresqlRepo() IPostgresqlRepo {\n\treturn &PostgresqlRepo{}\n}\n\nfunc (u *PostgresqlRepo) Get(opts ...DBOption) (model.DatabasePostgresql, error) {\n\tvar postgresql model.DatabasePostgresql\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tif err := db.First(&postgresql).Error; err != nil {\n\t\treturn postgresql, err\n\t}\n\n\tpass, err := encrypt.StringDecrypt(postgresql.Password)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"decrypt database db %s password failed, err: %v\", postgresql.Name, err)\n\t}\n\tpostgresql.Password = pass\n\treturn postgresql, err\n}\n\nfunc (u *PostgresqlRepo) List(opts ...DBOption) ([]model.DatabasePostgresql, error) {\n\tvar postgresqls []model.DatabasePostgresql\n\tdb := global.DB.Model(&model.DatabasePostgresql{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tif err := db.Find(&postgresqls).Error; err != nil {\n\t\treturn postgresqls, err\n\t}\n\tfor i := 0; i < len(postgresqls); i++ {\n\t\tpass, err := encrypt.StringDecrypt(postgresqls[i].Password)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"decrypt database db %s password failed, err: %v\", postgresqls[i].Name, err)\n\t\t}\n\t\tpostgresqls[i].Password = pass\n\t}\n\treturn postgresqls, nil\n}\n\nfunc (u *PostgresqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabasePostgresql, error) {\n\tvar postgresqls []model.DatabasePostgresql\n\tdb := global.DB.Model(&model.DatabasePostgresql{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\tif err := db.Limit(size).Offset(size * (page - 1)).Find(&postgresqls).Error; err != nil {\n\t\treturn count, postgresqls, err\n\t}\n\tfor i := 0; i < len(postgresqls); i++ {\n\t\tpass, err := encrypt.StringDecrypt(postgresqls[i].Password)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"decrypt database db %s password failed, err: %v\", postgresqls[i].Name, err)\n\t\t}\n\t\tpostgresqls[i].Password = pass\n\t}\n\treturn count, postgresqls, nil\n}\n\nfunc (u *PostgresqlRepo) Create(ctx context.Context, postgresql *model.DatabasePostgresql) error {\n\tpass, err := encrypt.StringEncrypt(postgresql.Password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decrypt database db %s password failed, err: %v\", postgresql.Name, err)\n\t}\n\tpostgresql.Password = pass\n\treturn getTx(ctx).Create(postgresql).Error\n}\n\nfunc (u *PostgresqlRepo) Delete(ctx context.Context, opts ...DBOption) error {\n\treturn getTx(ctx, opts...).Delete(&model.DatabasePostgresql{}).Error\n}\n\nfunc (u *PostgresqlRepo) DeleteLocal(ctx context.Context) error {\n\treturn getTx(ctx).Where(\"`from` = ?\", \"local\").Delete(&model.DatabasePostgresql{}).Error\n}\n\nfunc (u *PostgresqlRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.DatabasePostgresql{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *PostgresqlRepo) WithByPostgresqlName(postgresqlName string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"postgresql_name = ?\", postgresqlName)\n\t}\n}\n"
  },
  {
    "path": "agent/app/repo/favorite.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype FavoriteRepo struct{}\n\ntype IFavoriteRepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.Favorite, error)\n\tCreate(group *model.Favorite) error\n\tDelete(opts ...DBOption) error\n\tGetFirst(opts ...DBOption) (model.Favorite, error)\n\tAll() ([]model.Favorite, error)\n\tWithByPath(path string) DBOption\n}\n\nfunc NewIFavoriteRepo() IFavoriteRepo {\n\treturn &FavoriteRepo{}\n}\n\nfunc (f *FavoriteRepo) WithByPath(path string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"path = ?\", path)\n\t}\n}\n\nfunc (f *FavoriteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Favorite, error) {\n\tvar (\n\t\tfavorites []model.Favorite\n\t\tcount     int64\n\t)\n\tcount = int64(0)\n\tdb := getDb(opts...).Model(&model.Favorite{})\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&favorites).Error\n\treturn count, favorites, err\n}\n\nfunc (f *FavoriteRepo) Create(favorite *model.Favorite) error {\n\treturn global.DB.Create(favorite).Error\n}\n\nfunc (f *FavoriteRepo) GetFirst(opts ...DBOption) (model.Favorite, error) {\n\tvar favorite model.Favorite\n\tdb := getDb(opts...).Model(&model.Favorite{})\n\tif err := db.First(&favorite).Error; err != nil {\n\t\treturn favorite, err\n\t}\n\treturn favorite, nil\n}\n\nfunc (f *FavoriteRepo) Delete(opts ...DBOption) error {\n\tdb := getDb(opts...).Model(&model.Favorite{})\n\treturn db.Delete(&model.Favorite{}).Error\n}\n\nfunc (f *FavoriteRepo) All() ([]model.Favorite, error) {\n\tvar favorites []model.Favorite\n\tif err := getDb().Find(&favorites).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn favorites, nil\n}\n"
  },
  {
    "path": "agent/app/repo/ftp.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype FtpRepo struct{}\n\ntype IFtpRepo interface {\n\tGet(opts ...DBOption) (model.Ftp, error)\n\tGetList(opts ...DBOption) ([]model.Ftp, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.Ftp, error)\n\tCreate(ftp *model.Ftp) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(opts ...DBOption) error\n\tWithLikeUser(user string) DBOption\n\tWithByUser(user string) DBOption\n}\n\nfunc NewIFtpRepo() IFtpRepo {\n\treturn &FtpRepo{}\n}\n\nfunc (u *FtpRepo) Get(opts ...DBOption) (model.Ftp, error) {\n\tvar ftp model.Ftp\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&ftp).Error\n\treturn ftp, err\n}\n\nfunc (h *FtpRepo) WithByUser(user string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"user = ?\", user)\n\t}\n}\n\nfunc (h *FtpRepo) WithLikeUser(user string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(user) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"user like ?\", \"%\"+user+\"%\")\n\t}\n}\n\nfunc (u *FtpRepo) GetList(opts ...DBOption) ([]model.Ftp, error) {\n\tvar ftps []model.Ftp\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&ftps).Error\n\treturn ftps, err\n}\n\nfunc (h *FtpRepo) Page(page, size int, opts ...DBOption) (int64, []model.Ftp, error) {\n\tvar users []model.Ftp\n\tdb := global.DB.Model(&model.Ftp{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error\n\treturn count, users, err\n}\n\nfunc (h *FtpRepo) Create(ftp *model.Ftp) error {\n\treturn global.DB.Create(ftp).Error\n}\n\nfunc (h *FtpRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Ftp{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (h *FtpRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Ftp{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/group.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype GroupRepo struct{}\n\ntype IGroupRepo interface {\n\tGet(opts ...DBOption) (model.Group, error)\n\tGetList(opts ...DBOption) ([]model.Group, error)\n\tCreate(group *model.Group) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(opts ...DBOption) error\n\tCancelDefault(groupType string) error\n\tWithByDefault(isDefault bool) DBOption\n\tWithByWebsiteDefault() DBOption\n}\n\nfunc NewIGroupRepo() IGroupRepo {\n\treturn &GroupRepo{}\n}\n\nfunc (g *GroupRepo) WithByDefault(isDefault bool) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"is_default = ?\", isDefault)\n\t}\n}\n\nfunc (g *GroupRepo) Get(opts ...DBOption) (model.Group, error) {\n\tvar group model.Group\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&group).Error\n\treturn group, err\n}\n\nfunc (g *GroupRepo) GetList(opts ...DBOption) ([]model.Group, error) {\n\tvar groups []model.Group\n\tdb := global.DB.Model(&model.Group{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&groups).Error\n\treturn groups, err\n}\n\nfunc (g *GroupRepo) Create(group *model.Group) error {\n\treturn global.DB.Create(group).Error\n}\n\nfunc (g *GroupRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Group{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (g *GroupRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Group{}).Error\n}\n\nfunc (g *GroupRepo) WithByWebsiteDefault() DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"is_default = ? AND type = ?\", 1, \"website\")\n\t}\n}\n\nfunc (g *GroupRepo) CancelDefault(groupType string) error {\n\treturn global.DB.Model(&model.Group{}).\n\t\tWhere(\"is_default = ? AND type = ?\", 1, groupType).\n\t\tUpdates(map[string]interface{}{\"is_default\": 0}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/host.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"gorm.io/gorm\"\n)\n\ntype HostRepo struct{}\n\ntype IHostRepo interface {\n\tGet(opts ...DBOption) (model.Host, error)\n\tGetList(opts ...DBOption) ([]model.Host, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.Host, error)\n\tCreate(host *model.Host) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tUpdateGroup(group, newGroup uint) error\n\tDelete(opts ...DBOption) error\n\n\tWithByInfo(info string) DBOption\n\tWithByPort(port uint) DBOption\n\tWithByUser(user string) DBOption\n\n\tGetFirewallRecord(opts ...DBOption) (model.Firewall, error)\n\tListFirewallRecord(opts ...DBOption) ([]model.Firewall, error)\n\tSaveFirewallRecord(firewall *model.Firewall) error\n\tDeleteFirewallRecordByID(id uint) error\n\n\tSyncCert(data []model.RootCert) error\n\tGetCert(opts ...DBOption) (model.RootCert, error)\n\tPageCert(limit, offset int, opts ...DBOption) (int64, []model.RootCert, error)\n\tListCert(opts ...DBOption) ([]model.RootCert, error)\n\tSaveCert(cert *model.RootCert) error\n\tUpdateCert(id uint, vars map[string]interface{}) error\n\tDeleteCert(opts ...DBOption) error\n\n\tWithByChain(chain string) DBOption\n}\n\nfunc NewIHostRepo() IHostRepo {\n\treturn &HostRepo{}\n}\n\nfunc (h *HostRepo) Get(opts ...DBOption) (model.Host, error) {\n\tvar host model.Host\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&host).Error\n\treturn host, err\n}\n\nfunc (h *HostRepo) GetList(opts ...DBOption) ([]model.Host, error) {\n\tvar hosts []model.Host\n\tdb := global.DB.Model(&model.Host{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&hosts).Error\n\treturn hosts, err\n}\n\nfunc (h *HostRepo) Page(page, size int, opts ...DBOption) (int64, []model.Host, error) {\n\tvar hosts []model.Host\n\tdb := global.DB.Model(&model.Host{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&hosts).Error\n\treturn count, hosts, err\n}\n\nfunc (h *HostRepo) WithByInfo(info string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(info) == 0 {\n\t\t\treturn g\n\t\t}\n\t\tinfoStr := \"%\" + info + \"%\"\n\t\treturn g.Where(\"name LIKE ? OR addr LIKE ?\", infoStr, infoStr)\n\t}\n}\n\nfunc (h *HostRepo) WithByPort(port uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"port = ?\", port)\n\t}\n}\n\nfunc (h *HostRepo) WithByUser(user string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"user = ?\", user)\n\t}\n}\n\nfunc (h *HostRepo) Create(host *model.Host) error {\n\treturn global.DB.Create(host).Error\n}\n\nfunc (h *HostRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Host{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (h *HostRepo) UpdateGroup(group, newGroup uint) error {\n\treturn global.DB.Model(&model.Host{}).Where(\"group_id = ?\", group).Updates(map[string]interface{}{\"group_id\": newGroup}).Error\n}\n\nfunc (h *HostRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Host{}).Error\n}\n\nfunc (h *HostRepo) GetFirewallRecord(opts ...DBOption) (model.Firewall, error) {\n\tvar firewall model.Firewall\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&firewall).Error\n\treturn firewall, err\n}\n\nfunc (h *HostRepo) ListFirewallRecord(opts ...DBOption) ([]model.Firewall, error) {\n\tvar firewalls []model.Firewall\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tif err := global.DB.Find(&firewalls).Error; err != nil {\n\t\treturn firewalls, nil\n\t}\n\treturn firewalls, nil\n}\n\nfunc (h *HostRepo) SaveFirewallRecord(firewall *model.Firewall) error {\n\tif firewall.ID != 0 {\n\t\treturn global.DB.Save(firewall).Error\n\t}\n\tvar data model.Firewall\n\tswitch firewall.Type {\n\tcase \"port\":\n\t\t_ = global.DB.Where(\"type = ? AND dst_port = ? AND protocol = ? AND src_ip = ? AND strategy = ?\", \"port\",\n\t\t\tfirewall.DstPort,\n\t\t\tfirewall.Protocol,\n\t\t\tfirewall.SrcIP,\n\t\t\tfirewall.Strategy,\n\t\t).First(&data).Error\n\tcase \"ip\":\n\t\t_ = global.DB.Where(\"type = ? AND src_ip = ? AND strategy = ?\", \"address\", firewall.SrcIP, firewall.Strategy).First(&data)\n\tdefault:\n\t\t_ = global.DB.Where(\"type = ? AND chain = ? AND src_port = ? AND dst_port = ? AND protocol = ? AND src_ip = ? AND dst_ip = ? AND strategy = ?\",\n\t\t\tfirewall.Type,\n\t\t\tfirewall.Chain,\n\t\t\tfirewall.SrcPort,\n\t\t\tfirewall.DstPort,\n\t\t\tfirewall.Protocol,\n\t\t\tfirewall.SrcIP,\n\t\t\tfirewall.DstIP,\n\t\t\tfirewall.Strategy,\n\t\t).First(&data).Error\n\t}\n\treturn global.DB.Save(firewall).Error\n}\n\nfunc (h *HostRepo) DeleteFirewallRecordByID(id uint) error {\n\treturn global.DB.Where(\"id = ?\", id).Delete(&model.Firewall{}).Error\n}\n\nfunc (u *HostRepo) GetCert(opts ...DBOption) (model.RootCert, error) {\n\tvar cert model.RootCert\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&cert).Error\n\treturn cert, err\n}\n\nfunc (u *HostRepo) PageCert(page, size int, opts ...DBOption) (int64, []model.RootCert, error) {\n\tvar ops []model.RootCert\n\tdb := global.DB.Model(&model.RootCert{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error\n\treturn count, ops, err\n}\n\nfunc (u *HostRepo) ListCert(opts ...DBOption) ([]model.RootCert, error) {\n\tvar ops []model.RootCert\n\tdb := global.DB.Model(&model.RootCert{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Find(&ops).Error\n\treturn ops, err\n}\n\nfunc (u *HostRepo) SaveCert(cert *model.RootCert) error {\n\treturn global.DB.Save(cert).Error\n}\n\nfunc (u *HostRepo) UpdateCert(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.RootCert{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *HostRepo) DeleteCert(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.RootCert{}).Error\n}\n\nfunc (u *HostRepo) SyncCert(data []model.RootCert) error {\n\ttx := global.DB.Begin()\n\tvar oldCerts []model.RootCert\n\t_ = tx.Where(\"1 = ?\", 1).Find(&oldCerts).Error\n\toldCertsMap := make(map[string]uint)\n\tfor _, item := range oldCerts {\n\t\toldCertsMap[item.Name] = item.ID\n\t}\n\tfor _, item := range data {\n\t\tif _, ok := oldCertsMap[item.Name]; ok {\n\t\t\tdelete(oldCertsMap, item.Name)\n\t\t\tcontinue\n\t\t}\n\t\titem.PassPhrase, _ = encrypt.StringEncrypt(\"<UN-SET>\")\n\t\tif err := tx.Model(model.RootCert{}).Create(&item).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, val := range oldCertsMap {\n\t\tif err := tx.Where(\"id = ?\", val).Delete(&model.RootCert{}).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\ttx.Commit()\n\treturn nil\n}\n\nfunc (u *HostRepo) WithByChain(chain string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"chain = ?\", chain)\n\t}\n}\n"
  },
  {
    "path": "agent/app/repo/image_repo.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype ImageRepoRepo struct{}\n\ntype IImageRepoRepo interface {\n\tGet(opts ...DBOption) (model.ImageRepo, error)\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.ImageRepo, error)\n\tList(opts ...DBOption) ([]model.ImageRepo, error)\n\tCreate(imageRepo *model.ImageRepo) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(opts ...DBOption) error\n}\n\nfunc NewIImageRepoRepo() IImageRepoRepo {\n\treturn &ImageRepoRepo{}\n}\n\nfunc (u *ImageRepoRepo) Get(opts ...DBOption) (model.ImageRepo, error) {\n\tvar imageRepo model.ImageRepo\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&imageRepo).Error\n\treturn imageRepo, err\n}\n\nfunc (u *ImageRepoRepo) Page(page, size int, opts ...DBOption) (int64, []model.ImageRepo, error) {\n\tvar ops []model.ImageRepo\n\tdb := global.DB.Model(&model.ImageRepo{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error\n\treturn count, ops, err\n}\n\nfunc (u *ImageRepoRepo) List(opts ...DBOption) ([]model.ImageRepo, error) {\n\tvar ops []model.ImageRepo\n\tdb := global.DB.Model(&model.ImageRepo{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Find(&ops).Error\n\treturn ops, err\n}\n\nfunc (u *ImageRepoRepo) Create(imageRepo *model.ImageRepo) error {\n\treturn global.DB.Create(imageRepo).Error\n}\n\nfunc (u *ImageRepoRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.ImageRepo{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *ImageRepoRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.ImageRepo{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/mcp_server.go",
    "content": "package repo\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/model\"\n\ntype McpServerRepo struct {\n}\n\ntype IMcpServerRepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.McpServer, error)\n\tGetFirst(opts ...DBOption) (*model.McpServer, error)\n\tCreate(mcpServer *model.McpServer) error\n\tSave(mcpServer *model.McpServer) error\n\tDeleteBy(opts ...DBOption) error\n\tList(opts ...DBOption) ([]model.McpServer, error)\n}\n\nfunc NewIMcpServerRepo() IMcpServerRepo {\n\treturn &McpServerRepo{}\n}\n\nfunc (m McpServerRepo) Page(page, size int, opts ...DBOption) (int64, []model.McpServer, error) {\n\tvar servers []model.McpServer\n\tdb := getDb(opts...).Model(&model.McpServer{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&servers).Error\n\treturn count, servers, err\n}\n\nfunc (m McpServerRepo) GetFirst(opts ...DBOption) (*model.McpServer, error) {\n\tvar mcpServer model.McpServer\n\tif err := getDb(opts...).First(&mcpServer).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &mcpServer, nil\n}\n\nfunc (m McpServerRepo) List(opts ...DBOption) ([]model.McpServer, error) {\n\tvar mcpServers []model.McpServer\n\tif err := getDb(opts...).Find(&mcpServers).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn mcpServers, nil\n}\n\nfunc (m McpServerRepo) Create(mcpServer *model.McpServer) error {\n\treturn getDb().Create(mcpServer).Error\n}\n\nfunc (m McpServerRepo) Save(mcpServer *model.McpServer) error {\n\treturn getDb().Save(mcpServer).Error\n}\n\nfunc (m McpServerRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.McpServer{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/monitor.go",
    "content": "package repo\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype MonitorRepo struct{}\n\ntype IMonitorRepo interface {\n\tGetBase(opts ...DBOption) ([]model.MonitorBase, error)\n\tGetGPU(opts ...DBOption) ([]model.MonitorGPU, error)\n\tGetIO(opts ...DBOption) ([]model.MonitorIO, error)\n\tGetNetwork(opts ...DBOption) ([]model.MonitorNetwork, error)\n\n\tCreateMonitorBase(model model.MonitorBase) error\n\tBatchCreateMonitorGPU(list []model.MonitorGPU) error\n\tBatchCreateMonitorIO(ioList []model.MonitorIO) error\n\tBatchCreateMonitorNet(ioList []model.MonitorNetwork) error\n\tDelMonitorBase(timeForDelete time.Time) error\n\tDelMonitorGPU(timeForDelete time.Time) error\n\tDelMonitorIO(timeForDelete time.Time) error\n\tDelMonitorNet(timeForDelete time.Time) error\n\n\tWithByProductName(name string) DBOption\n}\n\nfunc NewIMonitorRepo() IMonitorRepo {\n\treturn &MonitorRepo{}\n}\n\nfunc (u *MonitorRepo) GetBase(opts ...DBOption) ([]model.MonitorBase, error) {\n\tvar data []model.MonitorBase\n\tdb := global.MonitorDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&data).Error\n\treturn data, err\n}\nfunc (u *MonitorRepo) GetIO(opts ...DBOption) ([]model.MonitorIO, error) {\n\tvar data []model.MonitorIO\n\tdb := global.MonitorDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&data).Error\n\treturn data, err\n}\nfunc (u *MonitorRepo) GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, error) {\n\tvar data []model.MonitorNetwork\n\tdb := global.MonitorDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&data).Error\n\treturn data, err\n}\nfunc (u *MonitorRepo) GetGPU(opts ...DBOption) ([]model.MonitorGPU, error) {\n\tvar data []model.MonitorGPU\n\tdb := global.GPUMonitorDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&data).Error\n\treturn data, err\n}\n\nfunc (u *MonitorRepo) CreateMonitorBase(model model.MonitorBase) error {\n\treturn global.MonitorDB.Create(&model).Error\n}\nfunc (s *MonitorRepo) BatchCreateMonitorGPU(list []model.MonitorGPU) error {\n\treturn global.GPUMonitorDB.CreateInBatches(&list, len(list)).Error\n}\nfunc (u *MonitorRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {\n\treturn global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error\n}\nfunc (u *MonitorRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {\n\treturn global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error\n}\nfunc (u *MonitorRepo) DelMonitorBase(timeForDelete time.Time) error {\n\treturn global.MonitorDB.Where(\"created_at < ?\", timeForDelete).Delete(&model.MonitorBase{}).Error\n}\nfunc (u *MonitorRepo) DelMonitorIO(timeForDelete time.Time) error {\n\treturn global.MonitorDB.Where(\"created_at < ?\", timeForDelete).Delete(&model.MonitorIO{}).Error\n}\nfunc (u *MonitorRepo) DelMonitorNet(timeForDelete time.Time) error {\n\treturn global.MonitorDB.Where(\"created_at < ?\", timeForDelete).Delete(&model.MonitorNetwork{}).Error\n}\nfunc (s *MonitorRepo) DelMonitorGPU(timeForDelete time.Time) error {\n\treturn global.GPUMonitorDB.Where(\"created_at < ?\", timeForDelete).Delete(&model.MonitorGPU{}).Error\n}\n\nfunc (s *MonitorRepo) WithByProductName(name string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"product_name = ?\", name)\n\t}\n}\n"
  },
  {
    "path": "agent/app/repo/php_extensions.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype PHPExtensionsRepo struct {\n}\n\ntype IPHPExtensionsRepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error)\n\tSave(extension *model.PHPExtensions) error\n\tCreate(extension *model.PHPExtensions) error\n\tGetFirst(opts ...DBOption) (model.PHPExtensions, error)\n\tDeleteBy(opts ...DBOption) error\n\tList() ([]model.PHPExtensions, error)\n}\n\nfunc NewIPHPExtensionsRepo() IPHPExtensionsRepo {\n\treturn &PHPExtensionsRepo{}\n}\n\nfunc (p *PHPExtensionsRepo) Page(page, size int, opts ...DBOption) (int64, []model.PHPExtensions, error) {\n\tvar (\n\t\tphpExtensions []model.PHPExtensions\n\t)\n\tdb := getDb(opts...).Model(&model.PHPExtensions{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&phpExtensions).Error\n\treturn count, phpExtensions, err\n}\n\nfunc (p *PHPExtensionsRepo) List() ([]model.PHPExtensions, error) {\n\tvar (\n\t\tphpExtensions []model.PHPExtensions\n\t)\n\terr := getDb().Model(&model.PHPExtensions{}).Find(&phpExtensions).Error\n\treturn phpExtensions, err\n}\n\nfunc (p *PHPExtensionsRepo) Save(extension *model.PHPExtensions) error {\n\treturn getDb().Save(&extension).Error\n}\n\nfunc (p *PHPExtensionsRepo) Create(extension *model.PHPExtensions) error {\n\treturn getDb().Create(&extension).Error\n}\n\nfunc (p *PHPExtensionsRepo) GetFirst(opts ...DBOption) (model.PHPExtensions, error) {\n\tvar extension model.PHPExtensions\n\tdb := getDb(opts...).Model(&model.PHPExtensions{})\n\terr := db.First(&extension).Error\n\treturn extension, err\n}\n\nfunc (p *PHPExtensionsRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.PHPExtensions{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/runtime.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype RuntimeRepo struct {\n}\n\ntype IRuntimeRepo interface {\n\tWithImage(image string) DBOption\n\tWithNotId(id uint) DBOption\n\tWithStatus(status string) DBOption\n\tWithDetailId(id uint) DBOption\n\tWithDetailIdsIn(ids []uint) DBOption\n\tWithPort(port int) DBOption\n\tWithNormalStatus(status string) DBOption\n\tPage(page, size int, opts ...DBOption) (int64, []model.Runtime, error)\n\tCreate(ctx context.Context, runtime *model.Runtime) error\n\tSave(runtime *model.Runtime) error\n\tDeleteBy(opts ...DBOption) error\n\tGetFirst(ctx context.Context, opts ...DBOption) (*model.Runtime, error)\n\tList(opts ...DBOption) ([]model.Runtime, error)\n}\n\nfunc NewIRunTimeRepo() IRuntimeRepo {\n\treturn &RuntimeRepo{}\n}\n\nfunc (r *RuntimeRepo) WithStatus(status string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"status = ?\", status)\n\t}\n}\n\nfunc (r *RuntimeRepo) WithNormalStatus(status string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"status = ? or status = 'Normal'\", status)\n\t}\n}\n\nfunc (r *RuntimeRepo) WithImage(image string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"image = ?\", image)\n\t}\n}\n\nfunc (r *RuntimeRepo) WithDetailId(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_detail_id = ?\", id)\n\t}\n}\n\nfunc (r *RuntimeRepo) WithDetailIdsIn(ids []uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"app_detail_id in(?) \", ids)\n\t}\n}\n\nfunc (r *RuntimeRepo) WithNotId(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id != ?\", id)\n\t}\n}\n\nfunc (r *RuntimeRepo) WithPort(port int) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tportStr := fmt.Sprintf(\"%d\", port)\n\t\treturn g.Debug().Where(\n\t\t\t\"port = ? OR port LIKE ? OR port LIKE ? OR port LIKE ?\",\n\t\t\tportStr,\n\t\t\tportStr+\",%\",\n\t\t\t\"%,\"+portStr,\n\t\t\t\"%,\"+portStr+\",%\",\n\t\t)\n\t}\n}\n\nfunc (r *RuntimeRepo) Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error) {\n\tvar runtimes []model.Runtime\n\tdb := getDb(opts...).Model(&model.Runtime{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&runtimes).Error\n\treturn count, runtimes, err\n}\n\nfunc (r *RuntimeRepo) List(opts ...DBOption) ([]model.Runtime, error) {\n\tvar runtimes []model.Runtime\n\tdb := global.DB.Model(&model.Runtime{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&runtimes).Error\n\treturn runtimes, err\n}\n\nfunc (r *RuntimeRepo) Create(ctx context.Context, runtime *model.Runtime) error {\n\tdb := getTx(ctx).Model(&model.Runtime{})\n\treturn db.Create(&runtime).Error\n}\n\nfunc (r *RuntimeRepo) Save(runtime *model.Runtime) error {\n\treturn getDb().Save(&runtime).Error\n}\n\nfunc (r *RuntimeRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.Runtime{}).Error\n}\n\nfunc (r *RuntimeRepo) GetFirst(ctx context.Context, opts ...DBOption) (*model.Runtime, error) {\n\tvar runtime model.Runtime\n\tif err := getTx(ctx, opts...).First(&runtime).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &runtime, nil\n}\n"
  },
  {
    "path": "agent/app/repo/script.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype ScriptRepo struct{}\n\ntype IScriptRepo interface {\n\tGet(opts ...DBOption) (model.ScriptLibrary, error)\n\tList(opts ...DBOption) ([]model.ScriptLibrary, error)\n\n\tSyncAll(data []model.ScriptLibrary) error\n}\n\nfunc NewIScriptRepo() IScriptRepo {\n\treturn &ScriptRepo{}\n}\n\nfunc (u *ScriptRepo) Get(opts ...DBOption) (model.ScriptLibrary, error) {\n\tvar script model.ScriptLibrary\n\tdb := global.DB\n\tif global.IsMaster {\n\t\tdb = global.CoreDB\n\t}\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&script).Error\n\treturn script, err\n}\n\nfunc (u *ScriptRepo) List(opts ...DBOption) ([]model.ScriptLibrary, error) {\n\tvar ops []model.ScriptLibrary\n\tif global.IsMaster {\n\t\tdb := global.CoreDB.Model(&model.ScriptLibrary{})\n\t\tfor _, opt := range opts {\n\t\t\tdb = opt(db)\n\t\t}\n\t\terr := db.Where(\"is_interactive = ?\", false).Find(&ops).Error\n\t\treturn ops, err\n\t}\n\tdb := global.DB.Model(&model.ScriptLibrary{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&ops).Error\n\treturn ops, err\n}\n\nfunc (u *ScriptRepo) SyncAll(data []model.ScriptLibrary) error {\n\ttx := global.DB.Begin()\n\tvar oldScripts []model.ScriptLibrary\n\t_ = tx.Where(\"1 = 1\").Find(&oldScripts).Error\n\toldScriptMap := make(map[string]uint)\n\tfor _, item := range oldScripts {\n\t\toldScriptMap[item.Name] = item.ID\n\t}\n\tfor _, item := range data {\n\t\tif val, ok := oldScriptMap[item.Name]; ok {\n\t\t\titem.ID = val\n\t\t\tdelete(oldScriptMap, item.Name)\n\t\t} else {\n\t\t\titem.ID = 0\n\t\t}\n\t\tif err := tx.Model(model.ScriptLibrary{}).Where(\"id = ?\", item.ID).Save(&item).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, val := range oldScriptMap {\n\t\tif err := tx.Where(\"id = ?\", val).Delete(&model.ScriptLibrary{}).Error; err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn err\n\t\t}\n\t}\n\ttx.Commit()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/repo/setting.go",
    "content": "package repo\n\nimport (\n\t\"errors\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype SettingRepo struct{}\n\ntype ISettingRepo interface {\n\tGetList(opts ...DBOption) ([]model.Setting, error)\n\tGet(opts ...DBOption) (model.Setting, error)\n\tGetValueByKey(key string) (string, error)\n\tCreate(key, value string) error\n\tUpdate(key, value string) error\n\tWithByKey(key string) DBOption\n\n\tUpdateOrCreate(key, value string) error\n\n\tGetDescription(opts ...DBOption) (model.CommonDescription, error)\n\tGetDescriptionList(opts ...DBOption) ([]model.CommonDescription, error)\n\tCreateDescription(data *model.CommonDescription) error\n\tUpdateDescription(id string, val map[string]interface{}) error\n\tDelDescription(id string) error\n\tWithByDescriptionID(id string) DBOption\n}\n\nfunc NewISettingRepo() ISettingRepo {\n\treturn &SettingRepo{}\n}\n\nfunc (s *SettingRepo) GetList(opts ...DBOption) ([]model.Setting, error) {\n\tvar settings []model.Setting\n\tdb := global.DB.Model(&model.Setting{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&settings).Error\n\treturn settings, err\n}\n\nfunc (s *SettingRepo) Create(key, value string) error {\n\tsetting := &model.Setting{\n\t\tKey:   key,\n\t\tValue: value,\n\t}\n\treturn global.DB.Create(setting).Error\n}\n\nfunc (s *SettingRepo) Get(opts ...DBOption) (model.Setting, error) {\n\tvar settings model.Setting\n\tdb := global.DB.Model(&model.Setting{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&settings).Error\n\treturn settings, err\n}\n\nfunc (s *SettingRepo) GetValueByKey(key string) (string, error) {\n\tvar setting model.Setting\n\tif err := global.DB.Model(&model.Setting{}).Where(\"key = ?\", key).First(&setting).Error; err != nil {\n\t\treturn \"\", err\n\t}\n\treturn setting.Value, nil\n}\n\nfunc (s *SettingRepo) WithByKey(key string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"key = ?\", key)\n\t}\n}\n\nfunc (s *SettingRepo) Update(key, value string) error {\n\treturn global.DB.Model(&model.Setting{}).Where(\"key = ?\", key).Updates(map[string]interface{}{\"value\": value}).Error\n}\n\nfunc (s *SettingRepo) UpdateOrCreate(key, value string) error {\n\tvar setting model.Setting\n\tresult := global.DB.Where(\"key = ?\", key).First(&setting)\n\tif result.Error != nil {\n\t\tif errors.Is(result.Error, gorm.ErrRecordNotFound) {\n\t\t\treturn global.DB.Create(&model.Setting{Key: key, Value: value}).Error\n\t\t}\n\t\treturn result.Error\n\t}\n\treturn global.DB.Model(&setting).UpdateColumn(\"value\", value).Error\n}\n\nfunc (s *SettingRepo) GetDescriptionList(opts ...DBOption) ([]model.CommonDescription, error) {\n\tvar lists []model.CommonDescription\n\tdb := global.DB.Model(&model.CommonDescription{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&lists).Error\n\treturn lists, err\n}\nfunc (s *SettingRepo) GetDescription(opts ...DBOption) (model.CommonDescription, error) {\n\tvar data model.CommonDescription\n\tdb := global.DB.Model(&model.CommonDescription{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&data).Error\n\treturn data, err\n}\nfunc (s *SettingRepo) CreateDescription(data *model.CommonDescription) error {\n\treturn global.DB.Create(data).Error\n}\nfunc (s *SettingRepo) UpdateDescription(id string, val map[string]interface{}) error {\n\treturn global.DB.Model(&model.CommonDescription{}).Where(\"id = ?\", id).Updates(val).Error\n}\nfunc (s *SettingRepo) DelDescription(id string) error {\n\treturn global.DB.Where(\"id = ?\", id).Delete(&model.CommonDescription{}).Error\n}\nfunc (s *SettingRepo) WithByDescriptionID(id string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id = ?\", id)\n\t}\n}\n"
  },
  {
    "path": "agent/app/repo/snapshot.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype ISnapshotRepo interface {\n\tGet(opts ...DBOption) (model.Snapshot, error)\n\tGetList(opts ...DBOption) ([]model.Snapshot, error)\n\tCreate(snap *model.Snapshot) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tPage(limit, offset int, opts ...DBOption) (int64, []model.Snapshot, error)\n\tDelete(opts ...DBOption) error\n}\n\nfunc NewISnapshotRepo() ISnapshotRepo {\n\treturn &SnapshotRepo{}\n}\n\ntype SnapshotRepo struct{}\n\nfunc (u *SnapshotRepo) Get(opts ...DBOption) (model.Snapshot, error) {\n\tvar Snapshot model.Snapshot\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&Snapshot).Error\n\treturn Snapshot, err\n}\n\nfunc (u *SnapshotRepo) GetList(opts ...DBOption) ([]model.Snapshot, error) {\n\tvar snaps []model.Snapshot\n\tdb := global.DB.Model(&model.Snapshot{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&snaps).Error\n\treturn snaps, err\n}\n\nfunc (u *SnapshotRepo) Page(page, size int, opts ...DBOption) (int64, []model.Snapshot, error) {\n\tvar users []model.Snapshot\n\tdb := global.DB.Model(&model.Snapshot{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error\n\treturn count, users, err\n}\n\nfunc (u *SnapshotRepo) Create(Snapshot *model.Snapshot) error {\n\treturn global.DB.Create(Snapshot).Error\n}\n\nfunc (u *SnapshotRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Snapshot{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *SnapshotRepo) Delete(opts ...DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Snapshot{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/tag.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype TagRepo struct {\n}\n\ntype ITagRepo interface {\n\tBatchCreate(ctx context.Context, tags []*model.Tag) error\n\tDeleteAll(ctx context.Context) error\n\tAll() ([]model.Tag, error)\n\tGetByIds(ids []uint) ([]model.Tag, error)\n\tGetByKeys(keys []string) ([]model.Tag, error)\n\tGetByAppId(appId uint) ([]model.Tag, error)\n\tDeleteByID(ctx context.Context, id uint) error\n\tCreate(ctx context.Context, tag *model.Tag) error\n\tSave(ctx context.Context, tag *model.Tag) error\n\tGetByKey(key string) (*model.Tag, error)\n}\n\nfunc NewITagRepo() ITagRepo {\n\treturn &TagRepo{}\n}\n\nfunc (t TagRepo) BatchCreate(ctx context.Context, tags []*model.Tag) error {\n\treturn getTx(ctx).Create(&tags).Error\n}\n\nfunc (t TagRepo) DeleteAll(ctx context.Context) error {\n\treturn getTx(ctx).Where(\"1 = 1 \").Delete(&model.Tag{}).Error\n}\n\nfunc (t TagRepo) All() ([]model.Tag, error) {\n\tvar tags []model.Tag\n\tif err := getDb().Where(\"1 = 1 \").Order(\"sort asc\").Find(&tags).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn tags, nil\n}\n\nfunc (t TagRepo) GetByKey(key string) (*model.Tag, error) {\n\tvar tag model.Tag\n\tif err := getDb().Where(\"key = ?\", key).First(&tag).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &tag, nil\n}\n\nfunc (t TagRepo) GetByIds(ids []uint) ([]model.Tag, error) {\n\tvar tags []model.Tag\n\tif err := getDb().Where(\"id in (?)\", ids).Find(&tags).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn tags, nil\n}\n\nfunc (t TagRepo) GetByKeys(keys []string) ([]model.Tag, error) {\n\tvar tags []model.Tag\n\tif err := getDb().Where(\"key in (?)\", keys).Find(&tags).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn tags, nil\n}\n\nfunc (t TagRepo) GetByAppId(appId uint) ([]model.Tag, error) {\n\tvar tags []model.Tag\n\tif err := getDb().Where(\"id in (select tag_id from app_tags where app_id = ?)\", appId).Find(&tags).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn tags, nil\n}\n\nfunc (t TagRepo) DeleteByID(ctx context.Context, id uint) error {\n\treturn getTx(ctx).Where(\"id = ?\", id).Delete(&model.Tag{}).Error\n}\n\nfunc (t TagRepo) Create(ctx context.Context, tag *model.Tag) error {\n\treturn getTx(ctx).Create(tag).Error\n}\n\nfunc (t TagRepo) Save(ctx context.Context, tag *model.Tag) error {\n\treturn getTx(ctx).Save(tag).Error\n}\n"
  },
  {
    "path": "agent/app/repo/task.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"gorm.io/gorm\"\n)\n\ntype TaskRepo struct {\n}\n\ntype ITaskRepo interface {\n\tSave(ctx context.Context, task *model.Task) error\n\tGetFirst(opts ...DBOption) (model.Task, error)\n\tPage(page, size int, opts ...DBOption) (int64, []model.Task, error)\n\tUpdate(ctx context.Context, task *model.Task) error\n\tUpdateRunningTaskToFailed() error\n\tCountExecutingTask() (int64, error)\n\tDelete(opts ...DBOption) error\n\tDeleteAll() error\n\n\tWithByID(id string) DBOption\n\tWithByIDNotIn(ids []string) DBOption\n\tWithResourceID(id uint) DBOption\n\tWithOperate(taskOperate string) DBOption\n\tWithByStatus(status string) DBOption\n}\n\nfunc NewITaskRepo() ITaskRepo {\n\treturn &TaskRepo{}\n}\n\nfunc getTaskDb(opts ...DBOption) *gorm.DB {\n\tdb := global.TaskDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db\n}\n\nfunc getTaskTx(ctx context.Context, opts ...DBOption) *gorm.DB {\n\ttx, ok := ctx.Value(constant.DB).(*gorm.DB)\n\tif ok {\n\t\tfor _, opt := range opts {\n\t\t\ttx = opt(tx)\n\t\t}\n\t\treturn tx\n\t}\n\treturn getTaskDb(opts...)\n}\n\nfunc (t TaskRepo) WithByID(id string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id = ?\", id)\n\t}\n}\n\nfunc (t TaskRepo) WithByIDNotIn(ids []string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id not in (?)\", ids)\n\t}\n}\n\nfunc (t TaskRepo) WithOperate(taskOperate string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"operate = ?\", taskOperate)\n\t}\n}\n\nfunc (t TaskRepo) WithResourceID(id uint) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"resource_id = ?\", id)\n\t}\n}\n\nfunc (t TaskRepo) WithByStatus(status string) DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"status = ?\", status)\n\t}\n}\n\nfunc (t TaskRepo) Save(ctx context.Context, task *model.Task) error {\n\treturn getTaskTx(ctx).Save(&task).Error\n}\n\nfunc (t TaskRepo) GetFirst(opts ...DBOption) (model.Task, error) {\n\tvar task model.Task\n\tdb := getTaskDb(opts...).Model(&model.Task{})\n\tif err := db.First(&task).Error; err != nil {\n\t\treturn task, err\n\t}\n\treturn task, nil\n}\n\nfunc (t TaskRepo) Page(page, size int, opts ...DBOption) (int64, []model.Task, error) {\n\tvar tasks []model.Task\n\tdb := getTaskDb(opts...).Model(&model.Task{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&tasks).Error\n\treturn count, tasks, err\n}\n\nfunc (t TaskRepo) Update(ctx context.Context, task *model.Task) error {\n\treturn getTaskTx(ctx).Save(&task).Error\n}\n\nfunc (t TaskRepo) UpdateRunningTaskToFailed() error {\n\treturn getTaskDb(t.WithByStatus(constant.StatusExecuting)).Model(&model.Task{}).Updates(map[string]interface{}{\"status\": constant.StatusFailed, \"error_msg\": \"1Panel restart causes failure\"}).Error\n}\n\nfunc (t TaskRepo) CountExecutingTask() (int64, error) {\n\tvar count int64\n\terr := getTaskDb(t.WithByStatus(constant.StatusExecuting)).Model(&model.Task{}).Count(&count).Error\n\treturn count, err\n}\n\nfunc (t TaskRepo) Delete(opts ...DBOption) error {\n\tdb := global.TaskDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Task{}).Error\n}\n\nfunc (t TaskRepo) DeleteAll() error {\n\treturn global.TaskDB.Where(\"1 = 1\").Delete(&model.Task{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/tensorrt_llm.go",
    "content": "package repo\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/model\"\n\ntype TensorRTLLMRepo struct {\n}\n\ntype ITensorRTLLMRepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.TensorRTLLM, error)\n\tGetFirst(opts ...DBOption) (*model.TensorRTLLM, error)\n\tCreate(tensorrtLLM *model.TensorRTLLM) error\n\tSave(tensorrtLLM *model.TensorRTLLM) error\n\tDeleteBy(opts ...DBOption) error\n\tList(opts ...DBOption) ([]model.TensorRTLLM, error)\n}\n\nfunc NewITensorRTLLMRepo() ITensorRTLLMRepo {\n\treturn &TensorRTLLMRepo{}\n}\n\nfunc (t TensorRTLLMRepo) Page(page, size int, opts ...DBOption) (int64, []model.TensorRTLLM, error) {\n\tvar servers []model.TensorRTLLM\n\tdb := getDb(opts...).Model(&model.TensorRTLLM{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&servers).Error\n\treturn count, servers, err\n}\n\nfunc (t TensorRTLLMRepo) GetFirst(opts ...DBOption) (*model.TensorRTLLM, error) {\n\tvar tensorrtLLM model.TensorRTLLM\n\tif err := getDb(opts...).First(&tensorrtLLM).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &tensorrtLLM, nil\n}\n\nfunc (t TensorRTLLMRepo) List(opts ...DBOption) ([]model.TensorRTLLM, error) {\n\tvar tensorrtLLMs []model.TensorRTLLM\n\tif err := getDb(opts...).Find(&tensorrtLLMs).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn tensorrtLLMs, nil\n}\n\nfunc (t TensorRTLLMRepo) Create(tensorrtLLM *model.TensorRTLLM) error {\n\treturn getDb().Create(tensorrtLLM).Error\n}\n\nfunc (t TensorRTLLMRepo) Save(tensorrtLLM *model.TensorRTLLM) error {\n\treturn getDb().Save(tensorrtLLM).Error\n}\n\nfunc (t TensorRTLLMRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.TensorRTLLM{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/website.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\ntype IWebsiteRepo interface {\n\tWithAppInstallId(appInstallId uint) DBOption\n\tWithDomain(domain string) DBOption\n\tWithAlias(alias string) DBOption\n\tWithWebsiteSSLID(sslId uint) DBOption\n\tWithGroupID(groupId uint) DBOption\n\tWithDefaultServer() DBOption\n\tWithDomainLike(domain string) DBOption\n\tWithRuntimeID(runtimeID uint) DBOption\n\tWithParentID(websiteID uint) DBOption\n\tWithType(websiteType string) DBOption\n\tWithDBType(dbType string) DBOption\n\tWithDBID(dbID uint) DBOption\n\n\tPage(page, size int, opts ...DBOption) (int64, []model.Website, error)\n\tList(opts ...DBOption) ([]model.Website, error)\n\tGetFirst(opts ...DBOption) (model.Website, error)\n\tGetBy(opts ...DBOption) ([]model.Website, error)\n\tSave(ctx context.Context, app *model.Website) error\n\tSaveWithoutCtx(app *model.Website) error\n\tDeleteBy(ctx context.Context, opts ...DBOption) error\n\tCreate(ctx context.Context, app *model.Website) error\n\tDeleteAll(ctx context.Context) error\n\n\tUpdateGroup(group, newGroup uint) error\n}\n\nfunc NewIWebsiteRepo() IWebsiteRepo {\n\treturn &WebsiteRepo{}\n}\n\ntype WebsiteRepo struct {\n}\n\nfunc (w *WebsiteRepo) WithAppInstallId(appInstallID uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"app_install_id = ?\", appInstallID)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithRuntimeID(runtimeID uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"runtime_id = ?\", runtimeID)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithDomain(domain string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"primary_domain = ?\", domain)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithDomainLike(domain string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"primary_domain like ?\", \"%\"+domain+\"%\")\n\t}\n}\n\nfunc (w *WebsiteRepo) WithAlias(alias string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"alias = ?\", alias)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithWebsiteSSLID(sslId uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"website_ssl_id = ?\", sslId)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithParentID(websiteID uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"parent_website_id = ?\", websiteID)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithGroupID(groupId uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"website_group_id = ?\", groupId)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithDefaultServer() DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"default_server = 1\")\n\t}\n}\n\nfunc (w *WebsiteRepo) WithType(websiteType string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"type = ?\", websiteType)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithDBType(dbType string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"db_type = ?\", dbType)\n\t}\n}\n\nfunc (w *WebsiteRepo) WithDBID(dbID uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"db_id = ?\", dbID)\n\t}\n}\n\nfunc (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Website, error) {\n\tvar websites []model.Website\n\tdb := getDb(opts...).Model(&model.Website{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Preload(\"WebsiteSSL\").Find(&websites).Error\n\treturn count, websites, err\n}\n\nfunc (w *WebsiteRepo) List(opts ...DBOption) ([]model.Website, error) {\n\tvar websites []model.Website\n\terr := getDb(opts...).Model(&model.Website{}).Preload(\"Domains\").Preload(\"WebsiteSSL\").Find(&websites).Error\n\treturn websites, err\n}\n\nfunc (w *WebsiteRepo) GetFirst(opts ...DBOption) (model.Website, error) {\n\tvar website model.Website\n\tdb := getDb(opts...).Model(&model.Website{})\n\tif err := db.Preload(\"Domains\").First(&website).Error; err != nil {\n\t\treturn website, err\n\t}\n\treturn website, nil\n}\n\nfunc (w *WebsiteRepo) GetBy(opts ...DBOption) ([]model.Website, error) {\n\tvar websites []model.Website\n\tdb := getDb(opts...).Model(&model.Website{})\n\tif err := db.Find(&websites).Error; err != nil {\n\t\treturn websites, err\n\t}\n\treturn websites, nil\n}\n\nfunc (w *WebsiteRepo) Create(ctx context.Context, app *model.Website) error {\n\treturn getTx(ctx).Omit(clause.Associations).Create(app).Error\n}\n\nfunc (w *WebsiteRepo) Save(ctx context.Context, app *model.Website) error {\n\treturn getTx(ctx).Omit(clause.Associations).Save(app).Error\n}\n\nfunc (w *WebsiteRepo) SaveWithoutCtx(website *model.Website) error {\n\treturn global.DB.Save(website).Error\n}\n\nfunc (w *WebsiteRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {\n\treturn getTx(ctx, opts...).Delete(&model.Website{}).Error\n}\n\nfunc (w *WebsiteRepo) DeleteAll(ctx context.Context) error {\n\treturn getTx(ctx).Where(\"1 = 1 \").Delete(&model.Website{}).Error\n}\n\nfunc (w *WebsiteRepo) UpdateGroup(group, newGroup uint) error {\n\treturn global.DB.Model(&model.Website{}).Where(\"website_group_id = ?\", group).Updates(map[string]interface{}{\"website_group_id\": newGroup}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/website_acme_account.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"gorm.io/gorm\"\n)\n\ntype IAcmeAccountRepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.WebsiteAcmeAccount, error)\n\tGetFirst(opts ...DBOption) (*model.WebsiteAcmeAccount, error)\n\tCreate(account model.WebsiteAcmeAccount) error\n\tSave(account model.WebsiteAcmeAccount) error\n\tDeleteBy(opts ...DBOption) error\n\tWithEmail(email string) DBOption\n\tWithType(acType string) DBOption\n}\n\nfunc NewIAcmeAccountRepo() IAcmeAccountRepo {\n\treturn &WebsiteAcmeAccountRepo{}\n}\n\ntype WebsiteAcmeAccountRepo struct {\n}\n\nfunc (w *WebsiteAcmeAccountRepo) WithEmail(email string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"email = ?\", email)\n\t}\n}\nfunc (w *WebsiteAcmeAccountRepo) WithType(acType string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"type = ?\", acType)\n\t}\n}\n\nfunc (w *WebsiteAcmeAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteAcmeAccount, error) {\n\tvar accounts []model.WebsiteAcmeAccount\n\tdb := getDb(opts...).Model(&model.WebsiteAcmeAccount{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&accounts).Error\n\treturn count, accounts, err\n}\n\nfunc (w *WebsiteAcmeAccountRepo) GetFirst(opts ...DBOption) (*model.WebsiteAcmeAccount, error) {\n\tvar account model.WebsiteAcmeAccount\n\tdb := getDb(opts...).Model(&model.WebsiteAcmeAccount{})\n\tif err := db.First(&account).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &account, nil\n}\n\nfunc (w *WebsiteAcmeAccountRepo) Create(account model.WebsiteAcmeAccount) error {\n\treturn getDb().Create(&account).Error\n}\n\nfunc (w *WebsiteAcmeAccountRepo) Save(account model.WebsiteAcmeAccount) error {\n\treturn getDb().Save(&account).Error\n}\n\nfunc (w *WebsiteAcmeAccountRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.WebsiteAcmeAccount{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/website_ca.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype WebsiteCARepo struct {\n}\n\nfunc NewIWebsiteCARepo() IWebsiteCARepo {\n\treturn &WebsiteCARepo{}\n}\n\ntype IWebsiteCARepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.WebsiteCA, error)\n\tGetFirst(opts ...DBOption) (model.WebsiteCA, error)\n\tList(opts ...DBOption) ([]model.WebsiteCA, error)\n\tCreate(ctx context.Context, ca *model.WebsiteCA) error\n\tDeleteBy(opts ...DBOption) error\n}\n\nfunc (w WebsiteCARepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteCA, error) {\n\tvar caList []model.WebsiteCA\n\tdb := getDb(opts...).Model(&model.WebsiteCA{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&caList).Error\n\treturn count, caList, err\n}\n\nfunc (w WebsiteCARepo) GetFirst(opts ...DBOption) (model.WebsiteCA, error) {\n\tvar ca model.WebsiteCA\n\tdb := getDb(opts...).Model(&model.WebsiteCA{})\n\tif err := db.First(&ca).Error; err != nil {\n\t\treturn ca, err\n\t}\n\treturn ca, nil\n}\n\nfunc (w WebsiteCARepo) List(opts ...DBOption) ([]model.WebsiteCA, error) {\n\tvar caList []model.WebsiteCA\n\tdb := getDb(opts...).Model(&model.WebsiteCA{})\n\terr := db.Find(&caList).Error\n\treturn caList, err\n}\n\nfunc (w WebsiteCARepo) Create(ctx context.Context, ca *model.WebsiteCA) error {\n\treturn getTx(ctx).Create(ca).Error\n}\n\nfunc (w WebsiteCARepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.WebsiteCA{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/website_dns_account.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n)\n\ntype WebsiteDnsAccountRepo struct {\n}\n\ntype IWebsiteDnsAccountRepo interface {\n\tPage(page, size int, opts ...DBOption) (int64, []model.WebsiteDnsAccount, error)\n\tGetFirst(opts ...DBOption) (*model.WebsiteDnsAccount, error)\n\tList(opts ...DBOption) ([]model.WebsiteDnsAccount, error)\n\tCreate(account model.WebsiteDnsAccount) error\n\tSave(account model.WebsiteDnsAccount) error\n\tDeleteBy(opts ...DBOption) error\n}\n\nfunc NewIWebsiteDnsAccountRepo() IWebsiteDnsAccountRepo {\n\treturn &WebsiteDnsAccountRepo{}\n}\n\nfunc (w WebsiteDnsAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDnsAccount, error) {\n\tvar accounts []model.WebsiteDnsAccount\n\tdb := getDb(opts...).Model(&model.WebsiteDnsAccount{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&accounts).Error\n\treturn count, accounts, err\n}\n\nfunc (w WebsiteDnsAccountRepo) GetFirst(opts ...DBOption) (*model.WebsiteDnsAccount, error) {\n\tvar account model.WebsiteDnsAccount\n\tdb := getDb(opts...).Model(&model.WebsiteDnsAccount{})\n\tif err := db.First(&account).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn &account, nil\n}\n\nfunc (w WebsiteDnsAccountRepo) List(opts ...DBOption) ([]model.WebsiteDnsAccount, error) {\n\tvar accounts []model.WebsiteDnsAccount\n\tdb := getDb(opts...).Model(&model.WebsiteDnsAccount{})\n\tif err := db.Find(&accounts).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn accounts, nil\n}\n\nfunc (w WebsiteDnsAccountRepo) Create(account model.WebsiteDnsAccount) error {\n\treturn getDb().Create(&account).Error\n}\n\nfunc (w WebsiteDnsAccountRepo) Save(account model.WebsiteDnsAccount) error {\n\treturn getDb().Save(&account).Error\n}\n\nfunc (w WebsiteDnsAccountRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.WebsiteDnsAccount{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/website_domain.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\ntype WebsiteDomainRepo struct {\n}\n\ntype IWebsiteDomainRepo interface {\n\tWithWebsiteId(websiteId uint) DBOption\n\tWithPort(port int) DBOption\n\tWithDomain(domain string) DBOption\n\tWithDomainLike(domain string) DBOption\n\tPage(page, size int, opts ...DBOption) (int64, []model.WebsiteDomain, error)\n\tGetFirst(opts ...DBOption) (model.WebsiteDomain, error)\n\tGetBy(opts ...DBOption) ([]model.WebsiteDomain, error)\n\tBatchCreate(ctx context.Context, domains []model.WebsiteDomain) error\n\tCreate(ctx context.Context, app *model.WebsiteDomain) error\n\tSave(ctx context.Context, app *model.WebsiteDomain) error\n\tDeleteBy(ctx context.Context, opts ...DBOption) error\n\tDeleteAll(ctx context.Context) error\n}\n\nfunc NewIWebsiteDomainRepo() IWebsiteDomainRepo {\n\treturn &WebsiteDomainRepo{}\n}\n\nfunc (w WebsiteDomainRepo) WithWebsiteId(websiteId uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"website_id = ?\", websiteId)\n\t}\n}\n\nfunc (w WebsiteDomainRepo) WithPort(port int) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"port = ?\", port)\n\t}\n}\nfunc (w WebsiteDomainRepo) WithDomain(domain string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"domain = ?\", domain)\n\t}\n}\nfunc (w WebsiteDomainRepo) WithDomainLike(domain string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"domain like ?\", \"%\"+domain+\"%\")\n\t}\n}\nfunc (w WebsiteDomainRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDomain, error) {\n\tvar domains []model.WebsiteDomain\n\tdb := getDb(opts...).Model(&model.WebsiteDomain{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&domains).Error\n\treturn count, domains, err\n}\n\nfunc (w WebsiteDomainRepo) GetFirst(opts ...DBOption) (model.WebsiteDomain, error) {\n\tvar domain model.WebsiteDomain\n\tdb := getDb(opts...).Model(&model.WebsiteDomain{})\n\tif err := db.First(&domain).Error; err != nil {\n\t\treturn domain, err\n\t}\n\treturn domain, nil\n}\n\nfunc (w WebsiteDomainRepo) GetBy(opts ...DBOption) ([]model.WebsiteDomain, error) {\n\tvar domains []model.WebsiteDomain\n\tdb := getDb(opts...).Model(&model.WebsiteDomain{})\n\tif err := db.Find(&domains).Error; err != nil {\n\t\treturn domains, err\n\t}\n\treturn domains, nil\n}\n\nfunc (w WebsiteDomainRepo) BatchCreate(ctx context.Context, domains []model.WebsiteDomain) error {\n\treturn getTx(ctx).Model(&model.WebsiteDomain{}).Create(&domains).Error\n}\n\nfunc (w WebsiteDomainRepo) Create(ctx context.Context, app *model.WebsiteDomain) error {\n\treturn getTx(ctx).Omit(clause.Associations).Create(app).Error\n}\n\nfunc (w WebsiteDomainRepo) Save(ctx context.Context, app *model.WebsiteDomain) error {\n\treturn getTx(ctx).Omit(clause.Associations).Save(app).Error\n}\n\nfunc (w WebsiteDomainRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {\n\treturn getTx(ctx, opts...).Delete(&model.WebsiteDomain{}).Error\n}\n\nfunc (w WebsiteDomainRepo) DeleteAll(ctx context.Context) error {\n\treturn getTx(ctx).Where(\"1 = 1 \").Delete(&model.WebsiteDomain{}).Error\n}\n"
  },
  {
    "path": "agent/app/repo/website_ssl.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"gorm.io/gorm\"\n)\n\nfunc NewISSLRepo() ISSLRepo {\n\treturn &WebsiteSSLRepo{}\n}\n\ntype ISSLRepo interface {\n\tWithByAlias(alias string) DBOption\n\tWithByAcmeAccountId(acmeAccountId uint) DBOption\n\tWithByDnsAccountId(dnsAccountId uint) DBOption\n\tWithByCAID(caID uint) DBOption\n\tWithByDomain(domain string) DBOption\n\tWithByMasterSSLID(sslID uint) DBOption\n\tPage(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)\n\tGetFirst(opts ...DBOption) (*model.WebsiteSSL, error)\n\tList(opts ...DBOption) ([]model.WebsiteSSL, error)\n\tCreate(ctx context.Context, ssl *model.WebsiteSSL) error\n\tSave(ssl *model.WebsiteSSL) error\n\tDeleteBy(opts ...DBOption) error\n\tSaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error\n}\n\ntype WebsiteSSLRepo struct {\n}\n\nfunc (w WebsiteSSLRepo) WithByAlias(alias string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"alias = ?\", alias)\n\t}\n}\n\nfunc (w WebsiteSSLRepo) WithByAcmeAccountId(acmeAccountId uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"acme_account_id = ?\", acmeAccountId)\n\t}\n}\n\nfunc (w WebsiteSSLRepo) WithByDnsAccountId(dnsAccountId uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"dns_account_id = ?\", dnsAccountId)\n\t}\n}\n\nfunc (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"ca_id = ?\", caID)\n\t}\n}\n\nfunc (w WebsiteSSLRepo) WithByDomain(domain string) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"primary_domain Like ? or domains Like ?\", \"%\"+domain+\"%\", \"%\"+domain+\"%\")\n\t}\n}\n\nfunc (w WebsiteSSLRepo) WithByMasterSSLID(sslID uint) DBOption {\n\treturn func(db *gorm.DB) *gorm.DB {\n\t\treturn db.Where(\"master_ssl_id = ?\", sslID)\n\t}\n}\n\nfunc (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {\n\tvar sslList []model.WebsiteSSL\n\tdb := getDb(opts...).Model(&model.WebsiteSSL{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Preload(\"AcmeAccount\").Preload(\"DnsAccount\").Preload(\"Websites\").Find(&sslList).Error\n\treturn count, sslList, err\n}\n\nfunc (w WebsiteSSLRepo) GetFirst(opts ...DBOption) (*model.WebsiteSSL, error) {\n\tvar website *model.WebsiteSSL\n\tdb := getDb(opts...).Model(&model.WebsiteSSL{})\n\tif err := db.Preload(\"AcmeAccount\").Preload(\"DnsAccount\").First(&website).Error; err != nil {\n\t\treturn website, err\n\t}\n\treturn website, nil\n}\n\nfunc (w WebsiteSSLRepo) List(opts ...DBOption) ([]model.WebsiteSSL, error) {\n\tvar websites []model.WebsiteSSL\n\tdb := getDb(opts...).Model(&model.WebsiteSSL{})\n\tif err := db.Preload(\"AcmeAccount\").Preload(\"DnsAccount\").Find(&websites).Error; err != nil {\n\t\treturn websites, err\n\t}\n\treturn websites, nil\n}\n\nfunc (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error {\n\treturn getTx(ctx).Create(ssl).Error\n}\n\nfunc (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {\n\treturn getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{\n\t\tID: ssl.ID,\n\t}}).Save(&ssl).Error\n}\n\nfunc (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error {\n\treturn getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{\n\t\tID: ssl.ID,\n\t}}).Updates(params).Error\n}\n\nfunc (w WebsiteSSLRepo) DeleteBy(opts ...DBOption) error {\n\treturn getDb(opts...).Delete(&model.WebsiteSSL{}).Error\n}\n"
  },
  {
    "path": "agent/app/service/agents.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"gorm.io/gorm\"\n)\n\ntype IAgentService interface {\n\tCreate(req dto.AgentCreateReq) (*dto.AgentItem, error)\n\tPage(req dto.SearchWithPage) (int64, []dto.AgentItem, error)\n\tDelete(req dto.AgentDeleteReq) error\n\tResetToken(req dto.AgentTokenResetReq) error\n\tUpdateModelConfig(req dto.AgentModelConfigUpdateReq) error\n\tGetProviders() ([]dto.ProviderInfo, error)\n\tCreateAccount(req dto.AgentAccountCreateReq) error\n\tUpdateAccount(req dto.AgentAccountUpdateReq) error\n\tSyncAgentsByAccount(account *model.AgentAccount) error\n\tPageAccounts(req dto.AgentAccountSearch) (int64, []dto.AgentAccountInfo, error)\n\tGetAccountModels(req dto.AgentAccountModelReq) ([]dto.AgentAccountModel, error)\n\tCreateAccountModel(req dto.AgentAccountModelCreateReq) error\n\tUpdateAccountModel(req dto.AgentAccountModelUpdateReq) error\n\tDeleteAccountModel(req dto.AgentAccountModelDeleteReq) error\n\tVerifyAccount(req dto.AgentAccountVerifyReq) error\n\tDeleteAccount(req dto.AgentAccountDeleteReq) error\n\tGetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentFeishuConfig, error)\n\tUpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) error\n\tGetTelegramConfig(req dto.AgentTelegramConfigReq) (*dto.AgentTelegramConfig, error)\n\tUpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq) error\n\tGetDiscordConfig(req dto.AgentDiscordConfigReq) (*dto.AgentDiscordConfig, error)\n\tUpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error\n\tGetWecomConfig(req dto.AgentWecomConfigReq) (*dto.AgentWecomConfig, error)\n\tUpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error\n\tGetDingTalkConfig(req dto.AgentDingTalkConfigReq) (*dto.AgentDingTalkConfig, error)\n\tUpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error\n\tGetQQBotConfig(req dto.AgentQQBotConfigReq) (*dto.AgentQQBotConfig, error)\n\tUpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error\n\tInstallPlugin(req dto.AgentPluginInstallReq) error\n\tCheckPlugin(req dto.AgentPluginCheckReq) (*dto.AgentPluginStatus, error)\n\tGetSecurityConfig(req dto.AgentSecurityConfigReq) (*dto.AgentSecurityConfig, error)\n\tUpdateSecurityConfig(req dto.AgentSecurityConfigUpdateReq) error\n\tGetOtherConfig(req dto.AgentOtherConfigReq) (*dto.AgentOtherConfig, error)\n\tUpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error\n\tApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error\n}\n\nconst (\n\tdefaultBrowserExecutablePath  = \"/home/node/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome\"\n\tdefaultBrowserProfile         = \"openclaw\"\n\tdefaultUserTimezone           = \"Asia/Shanghai\"\n\tdefaultToolsProfile           = \"full\"\n\tdefaultToolsSessionVisibility = \"all\"\n\tmaxCommunityAIAgents          = int64(5)\n\topenclawPluginBaseDir         = \"/home/node/.openclaw/extensions\"\n\topenclawGatewayPort           = 18789\n\topenclawAllowedOriginHost     = \"127.0.0.1\"\n\topenclawHTTPSVersion          = \"2026.3.13\"\n\topenclawTrustedProxyLoopback  = \"127.0.0.1/32\"\n)\n\nfunc (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {\n\tagentType := req.AgentType\n\tif err := checkPortExist(req.WebUIPort); err != nil {\n\t\treturn nil, err\n\t}\n\tif exist, _ := agentRepo.GetFirst(repo.WithByLowerName(req.Name)); exist != nil && exist.ID > 0 {\n\t\treturn nil, buserr.New(\"ErrNameIsExist\")\n\t}\n\tif installs, _ := appInstallRepo.ListBy(context.Background(), repo.WithByLowerName(req.Name)); len(installs) > 0 {\n\t\treturn nil, buserr.New(\"ErrNameIsExist\")\n\t}\n\tif !xpack.IsXpack() {\n\t\tcount, _, err := agentRepo.Page(1, 1)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif count >= maxCommunityAIAgents {\n\t\t\treturn nil, buserr.WithMap(\"ErrAgentLimitReached\", map[string]interface{}{\"max\": maxCommunityAIAgents}, nil)\n\t\t}\n\t}\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(agentType))\n\tif err != nil || app.ID == 0 {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tdetail, err := appDetailRepo.GetFirst(appDetailRepo.WithAppId(app.ID), appDetailRepo.WithVersion(req.AppVersion))\n\tif err != nil || detail.ID == 0 {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\n\tprovider := \"\"\n\tbaseURL := \"\"\n\tapiType := \"\"\n\tmaxTokens := 0\n\tcontextWindow := 0\n\tapiKey := \"\"\n\truntimeModel := \"\"\n\taccountID := uint(0)\n\ttoken := \"\"\n\tconfigPath := \"\"\n\tstoredModel := \"\"\n\tvar allowedOrigins []string\n\tvar account *model.AgentAccount\n\tvar installHooks *appInstallHooks\n\n\tif agentType == constant.AppOpenclaw {\n\t\tvar err error\n\t\tallowedOrigins, err = normalizeAllowedOrigins(req.AllowedOrigins)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(allowedOrigins) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"allowed origins is required\")\n\t\t}\n\t\tif req.AccountID == 0 {\n\t\t\treturn nil, buserr.New(\"ErrAgentAccountRequired\")\n\t\t}\n\t\taccount, err = agentAccountRepo.GetFirst(repo.WithByID(req.AccountID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !account.Verified {\n\t\t\treturn nil, buserr.New(\"ErrAgentAccountNotVerified\")\n\t\t}\n\t\tprovider = account.Provider\n\t\tbaseURL = strings.TrimSpace(account.BaseURL)\n\t\tresolvedRuntime, err := resolveOpenclawAccountModelRuntimeByID(account, req.Model)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tstoredModel = resolvedRuntime.StoredModel\n\t\tapiType = resolvedRuntime.APIType\n\t\tmaxTokens = resolvedRuntime.MaxTokens\n\t\tcontextWindow = resolvedRuntime.ContextWindow\n\t\truntimeModel = resolvedRuntime.PrimaryModel\n\t\tapiKey = account.APIKey\n\t\taccountID = account.ID\n\t\ttoken = strings.TrimSpace(req.Token)\n\t\tif token == \"\" {\n\t\t\ttoken = generateToken()\n\t\t}\n\t\tinstallHooks = &appInstallHooks{\n\t\t\tAfterCopyData: func(appInstall *model.AppInstall) error {\n\t\t\t\treturn prepareOpenclawInstallFiles(appInstall, account, storedModel, token, allowedOrigins)\n\t\t\t},\n\t\t}\n\t}\n\n\tparams := map[string]interface{}{\n\t\tconstant.CPUS:        \"0\",\n\t\tconstant.MemoryLimit: \"0\",\n\t\tconstant.HostIP:      \"\",\n\t}\n\tif agentType == constant.AppOpenclaw {\n\t\tparams[\"PANEL_APP_PORT_HTTPS\"] = req.WebUIPort\n\t\tif allowedOrigin := firstAllowedOrigin(allowedOrigins); allowedOrigin != \"\" {\n\t\t\tparams[\"ALLOWED_ORIGIN\"] = allowedOrigin\n\t\t}\n\t\tparams[\"PROVIDER\"] = provider\n\t\tparams[\"MODEL\"] = runtimeModel\n\t\tparams[\"API_TYPE\"] = apiType\n\t\tparams[\"MAX_TOKENS\"] = maxTokens\n\t\tparams[\"CONTEXT_WINDOW\"] = contextWindow\n\t\tparams[\"BASE_URL\"] = baseURL\n\t\tparams[\"API_KEY\"] = apiKey\n\t\tparams[\"OPENCLAW_GATEWAY_TOKEN\"] = token\n\t} else {\n\t\tparams[\"PANEL_APP_PORT_HTTP\"] = req.WebUIPort\n\t}\n\n\tif req.EditCompose && strings.TrimSpace(req.DockerCompose) == \"\" {\n\t\treturn nil, buserr.New(\"ErrAgentComposeRequired\")\n\t}\n\tinstallReq := request.AppInstallCreate{\n\t\tAppDetailId: detail.ID,\n\t\tName:        req.Name,\n\t\tParams:      params,\n\t\tTaskID:      req.TaskID,\n\t\tAppContainerConfig: request.AppContainerConfig{\n\t\t\tAdvanced:      req.Advanced,\n\t\t\tContainerName: req.ContainerName,\n\t\t\tAllowPort:     req.AllowPort,\n\t\t\tSpecifyIP:     req.SpecifyIP,\n\t\t\tRestartPolicy: req.RestartPolicy,\n\t\t\tCpuQuota:      req.CpuQuota,\n\t\t\tMemoryLimit:   req.MemoryLimit,\n\t\t\tMemoryUnit:    req.MemoryUnit,\n\t\t\tPullImage:     req.PullImage,\n\t\t\tEditCompose:   req.EditCompose,\n\t\t\tDockerCompose: req.DockerCompose,\n\t\t},\n\t}\n\tappInstall, err := AppService{}.installWithHooks(installReq, false, installHooks)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif agentType == constant.AppOpenclaw {\n\t\tconfigPath = path.Join(appInstall.GetPath(), \"data\", \"conf\", \"openclaw.json\")\n\t}\n\tagent := &model.Agent{\n\t\tName:          req.Name,\n\t\tAgentType:     agentType,\n\t\tProvider:      provider,\n\t\tModel:         storedModel,\n\t\tAPIType:       apiType,\n\t\tMaxTokens:     maxTokens,\n\t\tContextWindow: contextWindow,\n\t\tBaseURL:       baseURL,\n\t\tAPIKey:        apiKey,\n\t\tToken:         token,\n\t\tStatus:        appInstall.Status,\n\t\tMessage:       appInstall.Message,\n\t\tAppInstallID:  appInstall.ID,\n\t\tAccountID:     accountID,\n\t\tConfigPath:    configPath,\n\t}\n\tif err := agentRepo.Create(agent); err != nil {\n\t\treturn nil, err\n\t}\n\n\titem := buildAgentItem(agent, appInstall, nil)\n\treturn &item, nil\n}\n\nfunc (a AgentService) Page(req dto.SearchWithPage) (int64, []dto.AgentItem, error) {\n\tvar opts []repo.DBOption\n\tif strings.TrimSpace(req.Info) != \"\" {\n\t\topts = append(opts, repo.WithByLikeName(req.Info))\n\t}\n\tcount, list, err := agentRepo.Page(req.Page, req.PageSize, opts...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\titems := make([]dto.AgentItem, 0, len(list))\n\tfor _, item := range list {\n\t\tappInstall, _ := appInstallRepo.GetFirst(repo.WithByID(item.AppInstallID))\n\t\tenvMap := readInstallEnv(appInstall.Env)\n\t\tagentItem := buildAgentItem(&item, &appInstall, envMap)\n\t\tagentItem.Upgradable = checkAgentUpgradable(appInstall)\n\t\titems = append(items, agentItem)\n\t}\n\treturn count, items, nil\n}\n\nfunc (a AgentService) Delete(req dto.AgentDeleteReq) error {\n\tagent, err := agentRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif agent.AppInstallID == 0 {\n\t\treturn agentRepo.DeleteByID(agent.ID)\n\t}\n\toperate := request.AppInstalledOperate{\n\t\tInstallId:   agent.AppInstallID,\n\t\tOperate:     constant.Delete,\n\t\tTaskID:      req.TaskID,\n\t\tForceDelete: req.ForceDelete,\n\t}\n\tif err := NewIAppInstalledService().Operate(operate); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (a AgentService) ResetToken(req dto.AgentTokenResetReq) error {\n\tagent, err := agentRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif agent.AgentType == constant.AppCopaw {\n\t\treturn fmt.Errorf(\"copaw does not support token\")\n\t}\n\tconf, err := readOpenclawConfig(agent.ConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewToken := generateToken()\n\tif newToken == \"\" {\n\t\treturn fmt.Errorf(\"generate token failed\")\n\t}\n\tgatewayMap := ensureChildMap(conf, \"gateway\")\n\tauthMap := ensureChildMap(gatewayMap, \"auth\")\n\tif _, ok := authMap[\"mode\"]; !ok {\n\t\tauthMap[\"mode\"] = \"token\"\n\t}\n\tauthMap[\"token\"] = newToken\n\tif err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {\n\t\treturn err\n\t}\n\tagent.Token = newToken\n\treturn agentRepo.Save(agent)\n}\n\nfunc (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error {\n\tagent, err := agentRepo.GetFirst(repo.WithByID(req.AgentID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif agent.AgentType == constant.AppCopaw {\n\t\treturn fmt.Errorf(\"copaw does not support model config\")\n\t}\n\taccount, err := agentAccountRepo.GetFirst(repo.WithByID(req.AccountID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tresolvedRuntime, err := resolveOpenclawAccountModelRuntimeByID(account, req.Model)\n\tif err != nil {\n\t\treturn err\n\t}\n\tmodelName := resolvedRuntime.StoredModel\n\tapiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow\n\tconfDir := path.Dir(agent.ConfigPath)\n\tif err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil); err != nil {\n\t\treturn err\n\t}\n\tagent.Provider = account.Provider\n\tagent.Model = modelName\n\tagent.APIType = apiType\n\tagent.MaxTokens = maxTokens\n\tagent.ContextWindow = contextWindow\n\tagent.BaseURL = account.BaseURL\n\tagent.APIKey = account.APIKey\n\tagent.AccountID = account.ID\n\treturn agentRepo.Save(agent)\n}\n\nfunc (a AgentService) GetProviders() ([]dto.ProviderInfo, error) {\n\tdefinitions := providerDefinitions()\n\tproviders := make([]dto.ProviderInfo, 0, len(definitions))\n\tfor key, def := range definitions {\n\t\tproviders = append(providers, dto.ProviderInfo{\n\t\t\tSort:        def.Sort,\n\t\t\tProvider:    key,\n\t\t\tDisplayName: def.DisplayName,\n\t\t\tBaseURL:     def.BaseURL,\n\t\t\tModels:      def.Models,\n\t\t})\n\t}\n\tsort.Slice(providers, func(i, j int) bool {\n\t\treturn providers[i].Sort < providers[j].Sort\n\t})\n\treturn providers, nil\n}\n\nfunc (a AgentService) CreateAccount(req dto.AgentAccountCreateReq) error {\n\tprovider := req.Provider\n\tif exist, _ := agentAccountRepo.GetFirst(repo.WithByProvider(provider), repo.WithByName(req.Name)); exist != nil && exist.ID > 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tresolvedInput, err := resolveAgentAccountInput(provider, req.APIKey, req.BaseURL, req.APIType, \"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\taccount := &model.AgentAccount{\n\t\tProvider:       resolvedInput.Provider,\n\t\tName:           req.Name,\n\t\tAPIKey:         resolvedInput.APIKey,\n\t\tRememberAPIKey: req.RememberAPIKey,\n\t\tBaseURL:        resolvedInput.BaseURL,\n\t\tAPIType:        resolvedInput.APIType,\n\t\tVerified:       true,\n\t\tRemark:         req.Remark,\n\t}\n\tinitialModels, err := buildInitialAgentAccountModels(account, req.Models)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := global.DB.Transaction(func(tx *gorm.DB) error {\n\t\tif err := tx.Create(account).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(initialModels) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\treturn replacePersistedAgentAccountModelsWithTx(tx, account.ID, initialModels)\n\t}); err != nil {\n\t\treturn err\n\t}\n\tasyncReportAIProviderInstall(provider)\n\treturn nil\n}\n\nfunc (a AgentService) UpdateAccount(req dto.AgentAccountUpdateReq) error {\n\taccount, err := agentAccountRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tprovider := account.Provider\n\tresolvedInput, err := resolveAgentAccountInput(provider, req.APIKey, req.BaseURL, req.APIType, account.APIType)\n\tif err != nil {\n\t\treturn err\n\t}\n\taccount.Name = req.Name\n\taccount.APIKey = resolvedInput.APIKey\n\taccount.RememberAPIKey = req.RememberAPIKey\n\taccount.BaseURL = resolvedInput.BaseURL\n\taccount.APIType = resolvedInput.APIType\n\taccount.Remark = req.Remark\n\taccount.Verified = true\n\n\tif err := global.DB.Save(account).Error; err != nil {\n\t\treturn err\n\t}\n\tif req.SyncAgents {\n\t\tif err := a.syncAgentsByAccount(account); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (a AgentService) PageAccounts(req dto.AgentAccountSearch) (int64, []dto.AgentAccountInfo, error) {\n\tvar opts []repo.DBOption\n\tif strings.TrimSpace(req.Provider) != \"\" {\n\t\topts = append(opts, repo.WithByProvider(req.Provider))\n\t}\n\tif strings.TrimSpace(req.Name) != \"\" {\n\t\topts = append(opts, repo.WithByLikeName(req.Name))\n\t}\n\tcount, list, err := agentAccountRepo.Page(req.Page, req.PageSize, opts...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\titems := make([]dto.AgentAccountInfo, 0, len(list))\n\tfor _, item := range list {\n\t\tapiKey := \"\"\n\t\tif item.RememberAPIKey {\n\t\t\tapiKey = item.APIKey\n\t\t}\n\t\titems = append(items, dto.AgentAccountInfo{\n\t\t\tID:             item.ID,\n\t\t\tProvider:       item.Provider,\n\t\t\tProviderName:   providerDisplayName(item.Provider),\n\t\t\tName:           item.Name,\n\t\t\tAPIKey:         apiKey,\n\t\t\tRememberAPIKey: item.RememberAPIKey,\n\t\t\tBaseURL:        item.BaseURL,\n\t\t\tModels:         nil,\n\t\t\tAPIType:        item.APIType,\n\t\t\tVerified:       item.Verified,\n\t\t\tRemark:         item.Remark,\n\t\t\tCreatedAt:      item.CreatedAt,\n\t\t})\n\t}\n\tfor i := range items {\n\t\tmodels, err := loadAgentAccountModels(&list[i])\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\titems[i].Models = models\n\t}\n\treturn count, items, nil\n}\n\nfunc (a AgentService) GetAccountModels(req dto.AgentAccountModelReq) ([]dto.AgentAccountModel, error) {\n\taccount, err := agentAccountRepo.GetFirst(repo.WithByID(req.AccountID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn loadAgentAccountModels(account)\n}\n\nfunc (a AgentService) CreateAccountModel(req dto.AgentAccountModelCreateReq) error {\n\taccount, err := agentAccountRepo.GetFirst(repo.WithByID(req.AccountID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tmodels, err := loadAgentAccountModels(account)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnormalized, err := normalizeAgentAccountModel(account, req.Model)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, ok := findAgentAccountModelForProvider(account.Provider, models, normalized.ID); ok {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tinputPayload, err := json.Marshal(sanitizeAgentAccountModelInputs(normalized.Input))\n\tif err != nil {\n\t\treturn err\n\t}\n\tsortOrder := len(models) + 1\n\trecord := &model.AgentAccountModel{\n\t\tAccountID:     account.ID,\n\t\tModel:         normalized.ID,\n\t\tName:          normalized.Name,\n\t\tContextWindow: normalized.ContextWindow,\n\t\tMaxTokens:     normalized.MaxTokens,\n\t\tReasoning:     normalized.Reasoning,\n\t\tInput:         string(inputPayload),\n\t\tSortOrder:     sortOrder,\n\t}\n\tif err := agentAccountModelRepo.Create(record); err != nil {\n\t\treturn err\n\t}\n\treturn a.syncAgentsByAccount(account)\n}\n\nfunc (a AgentService) UpdateAccountModel(req dto.AgentAccountModelUpdateReq) error {\n\taccount, err := agentAccountRepo.GetFirst(repo.WithByID(req.AccountID))\n\tif err != nil {\n\t\treturn err\n\t}\n\trecord, err := agentAccountModelRepo.GetFirst(repo.WithByID(req.Model.RecordID), repo.WithByAccountID(req.AccountID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tmodels, err := loadAgentAccountModels(account)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnormalized, err := normalizeAgentAccountModel(account, req.Model)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, item := range models {\n\t\tif item.RecordID == req.Model.RecordID {\n\t\t\tcontinue\n\t\t}\n\t\tif sameProviderModelID(account.Provider, item.ID, normalized.ID) {\n\t\t\treturn buserr.New(\"ErrRecordExist\")\n\t\t}\n\t}\n\tnextModels := make([]dto.AgentAccountModel, 0, len(models))\n\tfor _, item := range models {\n\t\tif item.RecordID == req.Model.RecordID {\n\t\t\tnextModels = append(nextModels, normalized)\n\t\t\tcontinue\n\t\t}\n\t\tnextModels = append(nextModels, item)\n\t}\n\tif err := ensureAccountModelsNotBound(account, nextModels); err != nil {\n\t\treturn err\n\t}\n\tinputPayload, err := json.Marshal(sanitizeAgentAccountModelInputs(normalized.Input))\n\tif err != nil {\n\t\treturn err\n\t}\n\trecord.Model = normalized.ID\n\trecord.Name = normalized.Name\n\trecord.ContextWindow = normalized.ContextWindow\n\trecord.MaxTokens = normalized.MaxTokens\n\trecord.Reasoning = normalized.Reasoning\n\trecord.Input = string(inputPayload)\n\tif err := agentAccountModelRepo.Save(record); err != nil {\n\t\treturn err\n\t}\n\treturn a.syncAgentsByAccount(account)\n}\n\nfunc (a AgentService) DeleteAccountModel(req dto.AgentAccountModelDeleteReq) error {\n\taccount, err := agentAccountRepo.GetFirst(repo.WithByID(req.AccountID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err := agentAccountModelRepo.GetFirst(repo.WithByID(req.RecordID), repo.WithByAccountID(req.AccountID)); err != nil {\n\t\treturn err\n\t}\n\tmodels, err := loadAgentAccountModels(account)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnextModels := make([]dto.AgentAccountModel, 0, len(models))\n\tfor _, item := range models {\n\t\tif item.RecordID == req.RecordID {\n\t\t\tcontinue\n\t\t}\n\t\tnextModels = append(nextModels, item)\n\t}\n\tif err := ensureAccountModelsNotBound(account, nextModels); err != nil {\n\t\treturn err\n\t}\n\tif err := agentAccountModelRepo.DeleteByID(req.RecordID); err != nil {\n\t\treturn err\n\t}\n\tif err := compactPersistedAgentAccountModelSortOrder(req.AccountID); err != nil {\n\t\treturn err\n\t}\n\treturn a.syncAgentsByAccount(account)\n}\n\nfunc (a AgentService) SyncAgentsByAccount(account *model.AgentAccount) error {\n\tif account == nil || account.ID == 0 {\n\t\treturn nil\n\t}\n\treturn a.syncAgentsByAccount(account)\n}\n\nfunc (a AgentService) VerifyAccount(req dto.AgentAccountVerifyReq) error {\n\tresolvedVerification, err := resolveAgentAccountVerification(req.Provider, req.APIKey, req.BaseURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn verifyResolvedAgentAccount(resolvedVerification)\n}\n\nfunc (a AgentService) DeleteAccount(req dto.AgentAccountDeleteReq) error {\n\tif exists, _ := agentRepo.GetFirst(repo.WithByAccountID(req.ID)); exists != nil && exists.ID > 0 {\n\t\treturn buserr.New(\"ErrAgentAccountBound\")\n\t}\n\tif err := agentAccountModelRepo.Delete(repo.WithByAccountID(req.ID)); err != nil {\n\t\treturn err\n\t}\n\treturn agentAccountRepo.DeleteByID(req.ID)\n}\n\nfunc (a AgentService) GetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentFeishuConfig, error) {\n\t_, _, conf, err := a.loadAgentConfig(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := extractFeishuConfig(conf)\n\treturn &result, nil\n}\n\nfunc (a AgentService) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) error {\n\treturn a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {\n\t\tsetFeishuConfig(conf, dto.AgentFeishuConfig{\n\t\t\tEnabled:   req.Enabled,\n\t\t\tDmPolicy:  req.DmPolicy,\n\t\t\tBotName:   req.BotName,\n\t\t\tAppID:     req.AppID,\n\t\t\tAppSecret: req.AppSecret,\n\t\t})\n\t\tsetFeishuPluginEnabled(conf, req.Enabled)\n\t\treturn nil\n\t})\n}\n\nfunc (a AgentService) GetTelegramConfig(req dto.AgentTelegramConfigReq) (*dto.AgentTelegramConfig, error) {\n\t_, _, conf, err := a.loadAgentConfig(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := extractTelegramConfig(conf)\n\treturn &result, nil\n}\n\nfunc (a AgentService) UpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq) error {\n\treturn a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {\n\t\tsetTelegramConfig(conf, dto.AgentTelegramConfig{\n\t\t\tEnabled:  req.Enabled,\n\t\t\tDmPolicy: req.DmPolicy,\n\t\t\tBotToken: req.BotToken,\n\t\t\tProxy:    req.Proxy,\n\t\t})\n\t\treturn nil\n\t})\n}\n\nfunc (a AgentService) GetDiscordConfig(req dto.AgentDiscordConfigReq) (*dto.AgentDiscordConfig, error) {\n\t_, _, conf, err := a.loadAgentConfig(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := extractDiscordConfig(conf)\n\treturn &result, nil\n}\n\nfunc (a AgentService) UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error {\n\treturn a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {\n\t\tsetDiscordConfig(conf, dto.AgentDiscordConfig{\n\t\t\tEnabled:     req.Enabled,\n\t\t\tDmPolicy:    req.DmPolicy,\n\t\t\tGroupPolicy: req.GroupPolicy,\n\t\t\tToken:       req.Token,\n\t\t\tProxy:       req.Proxy,\n\t\t})\n\t\treturn nil\n\t})\n}\n\nfunc (a AgentService) GetQQBotConfig(req dto.AgentQQBotConfigReq) (*dto.AgentQQBotConfig, error) {\n\t_, install, conf, err := a.loadAgentConfig(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := extractQQBotConfig(conf)\n\tinstalled, _ := checkPluginInstalled(install.ContainerName, \"qqbot\")\n\tresult.Installed = installed\n\treturn &result, nil\n}\n\nfunc (a AgentService) UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error {\n\treturn a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {\n\t\tsetQQBotConfig(conf, dto.AgentQQBotConfig{\n\t\t\tEnabled:      req.Enabled,\n\t\t\tAppID:        req.AppID,\n\t\t\tClientSecret: req.ClientSecret,\n\t\t})\n\t\treturn nil\n\t})\n}\n\nfunc (a AgentService) GetWecomConfig(req dto.AgentWecomConfigReq) (*dto.AgentWecomConfig, error) {\n\t_, install, conf, err := a.loadAgentConfig(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := extractWecomConfig(conf)\n\tinstalled, _ := checkPluginInstalled(install.ContainerName, \"wecom\")\n\tresult.Installed = installed\n\treturn &result, nil\n}\n\nfunc (a AgentService) UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error {\n\treturn a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {\n\t\tsetWecomConfig(conf, dto.AgentWecomConfig{\n\t\t\tEnabled:  req.Enabled,\n\t\t\tDmPolicy: req.DmPolicy,\n\t\t\tBotID:    req.BotID,\n\t\t\tSecret:   req.Secret,\n\t\t})\n\t\treturn nil\n\t})\n}\n\nfunc (a AgentService) GetDingTalkConfig(req dto.AgentDingTalkConfigReq) (*dto.AgentDingTalkConfig, error) {\n\t_, install, conf, err := a.loadAgentConfig(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := extractDingTalkConfig(conf)\n\tinstalled, _ := checkPluginInstalled(install.ContainerName, \"dingtalk\")\n\tresult.Installed = installed\n\treturn &result, nil\n}\n\nfunc (a AgentService) UpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error {\n\treturn a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {\n\t\tsetDingTalkConfig(conf, dto.AgentDingTalkConfig{\n\t\t\tEnabled:        req.Enabled,\n\t\t\tClientID:       req.ClientID,\n\t\t\tClientSecret:   req.ClientSecret,\n\t\t\tDmPolicy:       req.DmPolicy,\n\t\t\tAllowFrom:      req.AllowFrom,\n\t\t\tGroupPolicy:    req.GroupPolicy,\n\t\t\tGroupAllowFrom: req.GroupAllowFrom,\n\t\t})\n\t\treturn nil\n\t})\n}\n\nfunc (a AgentService) InstallPlugin(req dto.AgentPluginInstallReq) error {\n\t_, install, err := a.loadAgentAndInstall(req.AgentID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tspec, _, err := resolvePluginMeta(req.Type)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstallTask, err := task.NewTaskWithOps(req.Type, task.TaskInstall, task.TaskScopeAI, req.TaskID, req.AgentID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstallTask.AddSubTask(\"Install OpenClaw plugin\", func(t *task.Task) error {\n\t\tmgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))\n\t\treturn mgr.RunBashCf(\"docker exec %s openclaw plugins install %s\", install.ContainerName, spec)\n\t}, nil)\n\tgo func() {\n\t\tif err := installTask.Execute(); err != nil {\n\t\t\tglobal.LOG.Errorf(\"install openclaw plugin failed: %v\", err)\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc (a AgentService) CheckPlugin(req dto.AgentPluginCheckReq) (*dto.AgentPluginStatus, error) {\n\t_, install, err := a.loadAgentAndInstall(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tinstalled, err := checkPluginInstalled(install.ContainerName, req.Type)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &dto.AgentPluginStatus{Installed: installed}, nil\n}\n\nfunc (a AgentService) GetSecurityConfig(req dto.AgentSecurityConfigReq) (*dto.AgentSecurityConfig, error) {\n\tagent, _, err := a.loadAgentAndInstall(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif agent.AgentType == constant.AppCopaw {\n\t\treturn nil, fmt.Errorf(\"copaw does not support security config\")\n\t}\n\tconf, err := readOpenclawConfig(agent.ConfigPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := extractSecurityConfig(conf)\n\treturn &result, nil\n}\n\nfunc (a AgentService) UpdateSecurityConfig(req dto.AgentSecurityConfigUpdateReq) error {\n\tagent, install, err := a.loadAgentAndInstall(req.AgentID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif agent.AgentType == constant.AppCopaw {\n\t\treturn fmt.Errorf(\"copaw does not support security config\")\n\t}\n\tallowedOrigins, err := normalizeAllowedOrigins(req.AllowedOrigins)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(allowedOrigins) == 0 {\n\t\treturn fmt.Errorf(\"allowed origins is required\")\n\t}\n\tconf, err := readOpenclawConfig(agent.ConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsetSecurityConfig(conf, dto.AgentSecurityConfig{AllowedOrigins: allowedOrigins})\n\tif err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {\n\t\treturn err\n\t}\n\tif err := syncOpenclawAllowedOriginEnv(install, allowedOrigins); err != nil {\n\t\treturn err\n\t}\n\treturn appInstallRepo.Save(context.Background(), install)\n}\n\nfunc (a AgentService) GetOtherConfig(req dto.AgentOtherConfigReq) (*dto.AgentOtherConfig, error) {\n\tagent, _, err := a.loadAgentAndInstall(req.AgentID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconf, err := readOpenclawConfig(agent.ConfigPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := extractOtherConfig(conf)\n\treturn &result, nil\n}\n\nfunc (a AgentService) UpdateOtherConfig(req dto.AgentOtherConfigUpdateReq) error {\n\tagent, _, err := a.loadAgentAndInstall(req.AgentID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tconf, err := readOpenclawConfig(agent.ConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsetOtherConfig(conf, dto.AgentOtherConfig{\n\t\tUserTimezone:   strings.TrimSpace(req.UserTimezone),\n\t\tBrowserEnabled: req.BrowserEnabled,\n\t})\n\tif err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error {\n\t_, install, err := a.loadAgentAndInstall(req.AgentID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tchannelType := req.Type\n\tif err := cmd.RunDefaultBashCf(\n\t\t\"docker exec %s openclaw pairing approve %s %q\",\n\t\tinstall.ContainerName,\n\t\tchannelType,\n\t\tstrings.TrimSpace(req.PairingCode),\n\t); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (a AgentService) loadAgentAndInstall(agentID uint) (*model.Agent, *model.AppInstall, error) {\n\tagent, err := agentRepo.GetFirst(repo.WithByID(agentID))\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif agent.AppInstallID == 0 {\n\t\treturn nil, nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tinstall, err := appInstallRepo.GetFirst(repo.WithByID(agent.AppInstallID))\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn agent, &install, nil\n}\n\nfunc (a AgentService) loadAgentConfig(agentID uint) (*model.Agent, *model.AppInstall, map[string]interface{}, error) {\n\tagent, install, err := a.loadAgentAndInstall(agentID)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tconf, err := readOpenclawConfig(agent.ConfigPath)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\treturn agent, install, conf, nil\n}\n\nfunc (a AgentService) mutateAgentConfig(agentID uint, fn func(agent *model.Agent, install *model.AppInstall, conf map[string]interface{}) error) error {\n\tagent, install, conf, err := a.loadAgentConfig(agentID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := fn(agent, install, conf); err != nil {\n\t\treturn err\n\t}\n\treturn writeOpenclawConfigRaw(agent.ConfigPath, conf)\n}\n\nfunc (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {\n\tagents, err := agentRepo.List(repo.WithByAccountID(account.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\taccountModels, err := loadAgentAccountModels(account)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(accountModels) == 0 {\n\t\treturn nil\n\t}\n\tbaseURL := resolveAccountBaseURL(account)\n\tfor _, agent := range agents {\n\t\tconfDir := path.Dir(agent.ConfigPath)\n\t\tmodelName := strings.TrimSpace(agent.Model)\n\t\tvar selectedAccountModel dto.AgentAccountModel\n\t\tif modelName != \"\" {\n\t\t\tselectedAccountModel, err = requireAgentAccountModelForProvider(account.Provider, accountModels, modelName)\n\t\t\tif err != nil {\n\t\t\t\treturn buserr.WithName(\"ErrAgentModelInUse\", agent.Name)\n\t\t\t}\n\t\t} else {\n\t\t\tselectedAccountModel = accountModels[0]\n\t\t}\n\t\tresolvedRuntime, err := buildOpenclawAccountModelRuntime(account, selectedAccountModel)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tmodelName = resolvedRuntime.StoredModel\n\t\tapiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow\n\t\tif err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tagent.BaseURL = baseURL\n\t\tagent.APIKey = account.APIKey\n\t\tagent.Provider = account.Provider\n\t\tagent.Model = modelName\n\t\tagent.APIType = apiType\n\t\tagent.MaxTokens = maxTokens\n\t\tagent.ContextWindow = contextWindow\n\t\t_ = agentRepo.Save(&agent)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/agents_utils.go",
    "content": "package service\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\tprovidercatalog \"github.com/1Panel-dev/1Panel/agent/app/provider\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\topenclawutil \"github.com/1Panel-dev/1Panel/agent/utils/openclaw\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/req_helper\"\n\t\"gorm.io/gorm\"\n)\n\ntype AgentService struct{}\n\nfunc NewIAgentService() IAgentService {\n\treturn &AgentService{}\n}\n\ntype resolvedAgentAccountVerification struct {\n\tProvider string\n\tAPIKey   string\n\tBaseURL  string\n}\n\ntype resolvedAgentAccountInput struct {\n\tProvider string\n\tAPIKey   string\n\tBaseURL  string\n\tAPIType  string\n}\n\nfunc resolveAgentAccountVerification(provider, apiKey, baseURL string) (resolvedAgentAccountVerification, error) {\n\tresolvedAPIKey := strings.TrimSpace(apiKey)\n\tif resolvedAPIKey == \"\" {\n\t\treturn resolvedAgentAccountVerification{}, buserr.New(\"ErrAgentApiKeyRequired\")\n\t}\n\tresolvedBaseURL := strings.TrimSpace(baseURL)\n\tif fixedURL, ok := fixedProviderBaseURL(provider); ok {\n\t\tresolvedBaseURL = fixedURL\n\t}\n\tif (provider == \"custom\" || provider == \"vllm\") && resolvedBaseURL == \"\" {\n\t\treturn resolvedAgentAccountVerification{}, buserr.New(\"ErrAgentBaseURLRequired\")\n\t}\n\tif provider != \"custom\" && provider != \"vllm\" && resolvedBaseURL == \"\" {\n\t\tif defaultURL, ok := providerDefaultBaseURL(provider); ok {\n\t\t\tresolvedBaseURL = defaultURL\n\t\t}\n\t}\n\tif provider == \"ollama\" && resolvedBaseURL == \"\" {\n\t\treturn resolvedAgentAccountVerification{}, buserr.New(\"ErrAgentBaseURLRequired\")\n\t}\n\treturn resolvedAgentAccountVerification{\n\t\tProvider: provider,\n\t\tAPIKey:   resolvedAPIKey,\n\t\tBaseURL:  resolvedBaseURL,\n\t}, nil\n}\n\nfunc verifyResolvedAgentAccount(input resolvedAgentAccountVerification) error {\n\tif providercatalog.SkipVerification(input.Provider) {\n\t\treturn nil\n\t}\n\treturn providercatalog.VerifyAccount(input.Provider, input.BaseURL, input.APIKey)\n}\n\nfunc resolveAgentAccountAPIType(provider, apiType, fallbackAPIType string) (string, error) {\n\tresolvedAPIType := normalizeAPIType(apiType)\n\tif provider == \"custom\" || provider == \"vllm\" {\n\t\tif !isSupportedAPIType(resolvedAPIType) {\n\t\t\treturn \"\", fmt.Errorf(\"apiType is invalid\")\n\t\t}\n\t\treturn resolvedAPIType, nil\n\t}\n\tif provider == \"ollama\" {\n\t\tif apiType == \"\" && fallbackAPIType != \"\" {\n\t\t\tresolvedAPIType = normalizeAPIType(fallbackAPIType)\n\t\t\tif !isSupportedOllamaAPIType(resolvedAPIType) {\n\t\t\t\tresolvedAPIType = \"openai-responses\"\n\t\t\t}\n\t\t\treturn resolvedAPIType, nil\n\t\t}\n\t\tif !isSupportedOllamaAPIType(resolvedAPIType) {\n\t\t\treturn \"\", fmt.Errorf(\"apiType is invalid\")\n\t\t}\n\t\treturn resolvedAPIType, nil\n\t}\n\tif fallbackAPIType != \"\" {\n\t\treturn normalizeAPIType(fallbackAPIType), nil\n\t}\n\treturn resolvedAPIType, nil\n}\n\nfunc resolveAgentAccountInput(provider, apiKey, baseURL, apiType, fallbackAPIType string) (resolvedAgentAccountInput, error) {\n\tresolvedVerification, err := resolveAgentAccountVerification(provider, apiKey, baseURL)\n\tif err != nil {\n\t\treturn resolvedAgentAccountInput{}, err\n\t}\n\tif err := verifyResolvedAgentAccount(resolvedVerification); err != nil {\n\t\treturn resolvedAgentAccountInput{}, err\n\t}\n\tresolvedAPIType, err := resolveAgentAccountAPIType(provider, apiType, fallbackAPIType)\n\tif err != nil {\n\t\treturn resolvedAgentAccountInput{}, err\n\t}\n\treturn resolvedAgentAccountInput{\n\t\tProvider: provider,\n\t\tAPIKey:   resolvedVerification.APIKey,\n\t\tBaseURL:  resolvedVerification.BaseURL,\n\t\tAPIType:  resolvedAPIType,\n\t}, nil\n}\n\nfunc readOpenclawConfig(configPath string) (map[string]interface{}, error) {\n\tif strings.TrimSpace(configPath) == \"\" {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tfileOp := files.NewFileOp()\n\tcontent, err := fileOp.GetContent(configPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconf := map[string]interface{}{}\n\tif err := json.Unmarshal(content, &conf); err != nil {\n\t\treturn nil, err\n\t}\n\treturn conf, nil\n}\n\nfunc writeOpenclawConfigRaw(configPath string, conf map[string]interface{}) error {\n\tensureGatewaySecurityDefaults(conf)\n\tensureOpenclawUpdateDefaults(conf)\n\tpayload, err := json.MarshalIndent(conf, \"\", \"  \")\n\tif err != nil {\n\t\treturn err\n\t}\n\tfileOp := files.NewFileOp()\n\treturn fileOp.SaveFile(configPath, string(payload), 0600)\n}\n\nfunc normalizeAllowedOrigins(origins []string) ([]string, error) {\n\tif len(origins) == 0 {\n\t\treturn nil, nil\n\t}\n\tresult := make([]string, 0, len(origins))\n\tseen := make(map[string]struct{}, len(origins))\n\tfor _, origin := range origins {\n\t\torigin = strings.TrimSpace(origin)\n\t\tif origin == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tnormalized, err := normalizeAllowedOrigin(origin)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif _, ok := seen[normalized]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[normalized] = struct{}{}\n\t\tresult = append(result, normalized)\n\t}\n\treturn result, nil\n}\n\nfunc normalizeAllowedOrigin(origin string) (string, error) {\n\tparsed, err := url.Parse(strings.TrimSpace(origin))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid allowed origin: %s\", origin)\n\t}\n\tif parsed.Scheme != \"http\" && parsed.Scheme != \"https\" {\n\t\treturn \"\", fmt.Errorf(\"invalid allowed origin: %s\", origin)\n\t}\n\tif parsed.User != nil || parsed.Host == \"\" || parsed.Hostname() == \"\" {\n\t\treturn \"\", fmt.Errorf(\"invalid allowed origin: %s\", origin)\n\t}\n\tif parsed.RawQuery != \"\" || parsed.Fragment != \"\" {\n\t\treturn \"\", fmt.Errorf(\"invalid allowed origin: %s\", origin)\n\t}\n\tif pathValue := strings.TrimSpace(parsed.EscapedPath()); pathValue != \"\" && pathValue != \"/\" {\n\t\treturn \"\", fmt.Errorf(\"invalid allowed origin: %s\", origin)\n\t}\n\thost := parsed.Hostname()\n\tif strings.Contains(host, \":\") {\n\t\thost = \"[\" + host + \"]\"\n\t}\n\tnormalized := parsed.Scheme + \"://\" + host\n\tif parsed.Port() != \"\" {\n\t\tnormalized += \":\" + parsed.Port()\n\t}\n\treturn normalized, nil\n}\n\nfunc extractSecurityConfig(conf map[string]interface{}) dto.AgentSecurityConfig {\n\tresult := dto.AgentSecurityConfig{AllowedOrigins: []string{}}\n\tgateway, ok := conf[\"gateway\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tcontrolUi, ok := gateway[\"controlUi\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tswitch values := controlUi[\"allowedOrigins\"].(type) {\n\tcase []interface{}:\n\t\tfor _, value := range values {\n\t\t\tif text, ok := value.(string); ok && strings.TrimSpace(text) != \"\" {\n\t\t\t\tresult.AllowedOrigins = append(result.AllowedOrigins, strings.TrimSpace(text))\n\t\t\t}\n\t\t}\n\tcase []string:\n\t\tfor _, value := range values {\n\t\t\tif strings.TrimSpace(value) != \"\" {\n\t\t\t\tresult.AllowedOrigins = append(result.AllowedOrigins, strings.TrimSpace(value))\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n\nfunc setSecurityConfig(conf map[string]interface{}, config dto.AgentSecurityConfig) {\n\tensureGatewaySecurityDefaults(conf)\n\tgateway := ensureChildMap(conf, \"gateway\")\n\tcontrolUi := ensureChildMap(gateway, \"controlUi\")\n\tallowedOrigins := append([]string(nil), config.AllowedOrigins...)\n\tif len(allowedOrigins) > 0 {\n\t\tcontrolUi[\"allowedOrigins\"] = allowedOrigins\n\t} else {\n\t\tdelete(controlUi, \"allowedOrigins\")\n\t}\n}\n\nfunc ensureGatewaySecurityDefaults(conf map[string]interface{}) {\n\tgateway := ensureChildMap(conf, \"gateway\")\n\tcontrolUi := ensureChildMap(gateway, \"controlUi\")\n\tif _, ok := controlUi[\"dangerouslyDisableDeviceAuth\"]; !ok {\n\t\tcontrolUi[\"dangerouslyDisableDeviceAuth\"] = true\n\t}\n\tdelete(controlUi, \"dangerouslyAllowHostHeaderOriginFallback\")\n\tsetTrustedProxies(gateway)\n}\n\nfunc ensureOpenclawUpdateDefaults(conf map[string]interface{}) {\n\tupdate := ensureChildMap(conf, \"update\")\n\tif _, ok := update[\"checkOnStart\"]; !ok {\n\t\tupdate[\"checkOnStart\"] = false\n\t}\n}\n\nfunc setTrustedProxies(gateway map[string]interface{}) {\n\tproxies := make([]string, 0, 4)\n\tseen := map[string]struct{}{}\n\tswitch values := gateway[\"trustedProxies\"].(type) {\n\tcase []interface{}:\n\t\tfor _, value := range values {\n\t\t\ttext := strings.TrimSpace(fmt.Sprintf(\"%v\", value))\n\t\t\tif text == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, ok := seen[text]; ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tseen[text] = struct{}{}\n\t\t\tproxies = append(proxies, text)\n\t\t}\n\tcase []string:\n\t\tfor _, value := range values {\n\t\t\ttext := strings.TrimSpace(value)\n\t\t\tif text == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, ok := seen[text]; ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tseen[text] = struct{}{}\n\t\t\tproxies = append(proxies, text)\n\t\t}\n\t}\n\tif _, ok := seen[openclawTrustedProxyLoopback]; !ok {\n\t\tproxies = append(proxies, openclawTrustedProxyLoopback)\n\t}\n\tgateway[\"trustedProxies\"] = proxies\n}\n\nfunc extractFeishuConfig(conf map[string]interface{}) dto.AgentFeishuConfig {\n\tresult := dto.AgentFeishuConfig{Enabled: true, DmPolicy: \"pairing\"}\n\tchannels, ok := conf[\"channels\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tfeishu, ok := channels[\"feishu\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tif enabled, ok := feishu[\"enabled\"].(bool); ok {\n\t\tresult.Enabled = enabled\n\t}\n\tif dmPolicy, ok := feishu[\"dmPolicy\"].(string); ok && strings.TrimSpace(dmPolicy) != \"\" {\n\t\tresult.DmPolicy = dmPolicy\n\t}\n\taccounts, ok := feishu[\"accounts\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tmain, ok := accounts[\"main\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tif appID, ok := main[\"appId\"].(string); ok {\n\t\tresult.AppID = appID\n\t}\n\tif appSecret, ok := main[\"appSecret\"].(string); ok {\n\t\tresult.AppSecret = appSecret\n\t}\n\tif botName, ok := main[\"botName\"].(string); ok {\n\t\tresult.BotName = botName\n\t}\n\treturn result\n}\n\nfunc setFeishuConfig(conf map[string]interface{}, config dto.AgentFeishuConfig) {\n\tchannels := ensureChildMap(conf, \"channels\")\n\tfeishu := ensureChildMap(channels, \"feishu\")\n\tfeishu[\"enabled\"] = config.Enabled\n\tfeishu[\"dmPolicy\"] = config.DmPolicy\n\n\taccounts := ensureChildMap(feishu, \"accounts\")\n\tmain := ensureChildMap(accounts, \"main\")\n\tmain[\"appId\"] = config.AppID\n\tmain[\"appSecret\"] = config.AppSecret\n\tmain[\"botName\"] = config.BotName\n\n\tif strings.EqualFold(config.DmPolicy, \"open\") {\n\t\tfeishu[\"allowFrom\"] = []string{\"*\"}\n\t}\n}\n\nfunc setFeishuPluginEnabled(conf map[string]interface{}, enabled bool) {\n\tplugins := ensureChildMap(conf, \"plugins\")\n\tentries := ensureChildMap(plugins, \"entries\")\n\tfeishu := ensureChildMap(entries, \"feishu\")\n\tfeishu[\"enabled\"] = enabled\n}\n\nfunc extractTelegramConfig(conf map[string]interface{}) dto.AgentTelegramConfig {\n\tresult := dto.AgentTelegramConfig{Enabled: true, DmPolicy: \"pairing\"}\n\tchannels, ok := conf[\"channels\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\ttelegram, ok := channels[\"telegram\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tif enabled, ok := telegram[\"enabled\"].(bool); ok {\n\t\tresult.Enabled = enabled\n\t}\n\tif dmPolicy, ok := telegram[\"dmPolicy\"].(string); ok && strings.TrimSpace(dmPolicy) != \"\" {\n\t\tresult.DmPolicy = dmPolicy\n\t}\n\tif botToken, ok := telegram[\"botToken\"].(string); ok {\n\t\tresult.BotToken = botToken\n\t}\n\tif proxy, ok := telegram[\"proxy\"].(string); ok {\n\t\tresult.Proxy = proxy\n\t}\n\treturn result\n}\n\nfunc setTelegramConfig(conf map[string]interface{}, config dto.AgentTelegramConfig) {\n\tchannels := ensureChildMap(conf, \"channels\")\n\ttelegram := map[string]interface{}{\n\t\t\"enabled\":  config.Enabled,\n\t\t\"dmPolicy\": config.DmPolicy,\n\t\t\"botToken\": config.BotToken,\n\t}\n\tif strings.EqualFold(config.DmPolicy, \"open\") {\n\t\ttelegram[\"allowFrom\"] = []string{\"*\"}\n\t}\n\tif strings.TrimSpace(config.Proxy) != \"\" {\n\t\ttelegram[\"proxy\"] = strings.TrimSpace(config.Proxy)\n\t}\n\tchannels[\"telegram\"] = telegram\n}\n\nfunc extractDiscordConfig(conf map[string]interface{}) dto.AgentDiscordConfig {\n\tresult := dto.AgentDiscordConfig{Enabled: true, DmPolicy: \"pairing\", GroupPolicy: \"open\"}\n\tchannels, ok := conf[\"channels\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tdiscord, ok := channels[\"discord\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tif enabled, ok := discord[\"enabled\"].(bool); ok {\n\t\tresult.Enabled = enabled\n\t}\n\tif token, ok := discord[\"token\"].(string); ok {\n\t\tresult.Token = token\n\t}\n\tif groupPolicy, ok := discord[\"groupPolicy\"].(string); ok && strings.TrimSpace(groupPolicy) != \"\" {\n\t\tresult.GroupPolicy = groupPolicy\n\t}\n\tif proxy, ok := discord[\"proxy\"].(string); ok {\n\t\tresult.Proxy = proxy\n\t}\n\tif policy, ok := discord[\"dmPolicy\"].(string); ok && strings.TrimSpace(policy) != \"\" {\n\t\tresult.DmPolicy = policy\n\t\treturn result\n\t}\n\t// backward compatibility: old nested style\n\tdm, ok := discord[\"dm\"].(map[string]interface{})\n\tif ok {\n\t\tif policy, ok := dm[\"policy\"].(string); ok && strings.TrimSpace(policy) != \"\" {\n\t\t\tresult.DmPolicy = policy\n\t\t}\n\t}\n\treturn result\n}\n\nfunc setDiscordConfig(conf map[string]interface{}, config dto.AgentDiscordConfig) {\n\tchannels := ensureChildMap(conf, \"channels\")\n\tdiscord := ensureChildMap(channels, \"discord\")\n\tdiscord[\"enabled\"] = config.Enabled\n\tdiscord[\"token\"] = config.Token\n\tdiscord[\"dmPolicy\"] = config.DmPolicy\n\tdiscord[\"groupPolicy\"] = config.GroupPolicy\n\tif strings.EqualFold(config.DmPolicy, \"open\") {\n\t\tdiscord[\"allowFrom\"] = []string{\"*\"}\n\t} else {\n\t\tdelete(discord, \"allowFrom\")\n\t}\n\tif strings.TrimSpace(config.Proxy) != \"\" {\n\t\tdiscord[\"proxy\"] = strings.TrimSpace(config.Proxy)\n\t} else {\n\t\tdelete(discord, \"proxy\")\n\t}\n\tdelete(discord, \"dm\")\n}\n\nfunc extractBrowserConfig(conf map[string]interface{}) browserConfig {\n\tresult := browserConfig{\n\t\tEnabled:        true,\n\t\tExecutablePath: defaultBrowserExecutablePath,\n\t\tHeadless:       true,\n\t\tNoSandbox:      true,\n\t\tDefaultProfile: defaultBrowserProfile,\n\t}\n\tbrowser, ok := conf[\"browser\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tif enabled, ok := browser[\"enabled\"].(bool); ok {\n\t\tresult.Enabled = enabled\n\t}\n\tif executablePath, ok := browser[\"executablePath\"].(string); ok && strings.TrimSpace(executablePath) != \"\" {\n\t\tresult.ExecutablePath = executablePath\n\t}\n\tif headless, ok := browser[\"headless\"].(bool); ok {\n\t\tresult.Headless = headless\n\t}\n\tif noSandbox, ok := browser[\"noSandbox\"].(bool); ok {\n\t\tresult.NoSandbox = noSandbox\n\t}\n\tif defaultProfile, ok := browser[\"defaultProfile\"].(string); ok && strings.TrimSpace(defaultProfile) != \"\" {\n\t\tresult.DefaultProfile = defaultProfile\n\t}\n\treturn result\n}\n\nfunc setBrowserConfig(conf map[string]interface{}, config browserConfig) {\n\tbrowser := ensureChildMap(conf, \"browser\")\n\tbrowser[\"enabled\"] = config.Enabled\n\tbrowser[\"executablePath\"] = defaultBrowserExecutablePath\n\tbrowser[\"headless\"] = config.Headless\n\tbrowser[\"noSandbox\"] = config.NoSandbox\n\tif strings.TrimSpace(config.DefaultProfile) == \"\" {\n\t\tbrowser[\"defaultProfile\"] = defaultBrowserProfile\n\t} else {\n\t\tbrowser[\"defaultProfile\"] = strings.TrimSpace(config.DefaultProfile)\n\t}\n}\n\nfunc extractQQBotConfig(conf map[string]interface{}) dto.AgentQQBotConfig {\n\tresult := dto.AgentQQBotConfig{Enabled: true}\n\tchannels, ok := conf[\"channels\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tqqbot, ok := channels[\"qqbot\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tif enabled, ok := qqbot[\"enabled\"].(bool); ok {\n\t\tresult.Enabled = enabled\n\t}\n\tif appID, ok := qqbot[\"appId\"].(string); ok {\n\t\tresult.AppID = appID\n\t}\n\tif clientSecret, ok := qqbot[\"clientSecret\"].(string); ok {\n\t\tresult.ClientSecret = clientSecret\n\t}\n\treturn result\n}\n\nfunc extractWecomConfig(conf map[string]interface{}) dto.AgentWecomConfig {\n\tresult := dto.AgentWecomConfig{Enabled: true, DmPolicy: \"pairing\"}\n\tchannels, ok := conf[\"channels\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\twecom, ok := channels[\"wecom\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tif enabled, ok := wecom[\"enabled\"].(bool); ok {\n\t\tresult.Enabled = enabled\n\t}\n\tif dmPolicy, ok := wecom[\"dmPolicy\"].(string); ok && strings.TrimSpace(dmPolicy) != \"\" {\n\t\tresult.DmPolicy = strings.TrimSpace(dmPolicy)\n\t}\n\tif botID, ok := wecom[\"botId\"].(string); ok {\n\t\tresult.BotID = botID\n\t}\n\tif secret, ok := wecom[\"secret\"].(string); ok {\n\t\tresult.Secret = secret\n\t}\n\treturn result\n}\n\nfunc extractDingTalkConfig(conf map[string]interface{}) dto.AgentDingTalkConfig {\n\tresult := dto.AgentDingTalkConfig{\n\t\tEnabled:        true,\n\t\tDmPolicy:       \"pairing\",\n\t\tGroupPolicy:    \"disabled\",\n\t\tAllowFrom:      []string{},\n\t\tGroupAllowFrom: []string{},\n\t}\n\tchannels, ok := conf[\"channels\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tdingtalk, ok := channels[\"dingtalk-connector\"].(map[string]interface{})\n\tif !ok {\n\t\treturn result\n\t}\n\tif enabled, ok := dingtalk[\"enabled\"].(bool); ok {\n\t\tresult.Enabled = enabled\n\t}\n\tif clientID, ok := dingtalk[\"clientId\"].(string); ok {\n\t\tresult.ClientID = clientID\n\t}\n\tif clientSecret, ok := dingtalk[\"clientSecret\"].(string); ok {\n\t\tresult.ClientSecret = clientSecret\n\t}\n\tif dmPolicy, ok := dingtalk[\"dmPolicy\"].(string); ok && strings.TrimSpace(dmPolicy) != \"\" {\n\t\tresult.DmPolicy = dmPolicy\n\t}\n\tif groupPolicy, ok := dingtalk[\"groupPolicy\"].(string); ok && strings.TrimSpace(groupPolicy) != \"\" {\n\t\tresult.GroupPolicy = groupPolicy\n\t}\n\tresult.AllowFrom = extractStringList(dingtalk[\"allowFrom\"])\n\tresult.GroupAllowFrom = extractStringList(dingtalk[\"groupAllowFrom\"])\n\treturn result\n}\n\nfunc setWecomConfig(conf map[string]interface{}, config dto.AgentWecomConfig) {\n\tchannels := ensureChildMap(conf, \"channels\")\n\twecom := ensureChildMap(channels, \"wecom\")\n\twecom[\"enabled\"] = config.Enabled\n\twecom[\"botId\"] = strings.TrimSpace(config.BotID)\n\twecom[\"secret\"] = strings.TrimSpace(config.Secret)\n\twecom[\"dmPolicy\"] = strings.TrimSpace(config.DmPolicy)\n\tif strings.EqualFold(config.DmPolicy, \"open\") {\n\t\twecom[\"allowFrom\"] = []string{\"*\"}\n\t} else {\n\t\twecom[\"allowFrom\"] = []string{}\n\t}\n\n\tplugins := ensureChildMap(conf, \"plugins\")\n\tentries := ensureChildMap(plugins, \"entries\")\n\twecomEntry := ensureChildMap(entries, \"wecom-openclaw-plugin\")\n\twecomEntry[\"enabled\"] = config.Enabled\n}\n\nfunc setDingTalkConfig(conf map[string]interface{}, config dto.AgentDingTalkConfig) {\n\tchannels := ensureChildMap(conf, \"channels\")\n\tdingtalk := ensureChildMap(channels, \"dingtalk-connector\")\n\tdingtalk[\"enabled\"] = config.Enabled\n\tdingtalk[\"clientId\"] = strings.TrimSpace(config.ClientID)\n\tdingtalk[\"clientSecret\"] = strings.TrimSpace(config.ClientSecret)\n\tdingtalk[\"dmPolicy\"] = config.DmPolicy\n\tdingtalk[\"groupPolicy\"] = config.GroupPolicy\n\tdingtalk[\"gatewayToken\"] = extractGatewayToken(conf)\n\tswitch config.DmPolicy {\n\tcase \"open\":\n\t\tdingtalk[\"allowFrom\"] = []string{\"*\"}\n\tcase \"allowlist\":\n\t\tdingtalk[\"allowFrom\"] = append([]string(nil), config.AllowFrom...)\n\tdefault:\n\t\tdelete(dingtalk, \"allowFrom\")\n\t}\n\tswitch config.GroupPolicy {\n\tcase \"open\":\n\t\tdingtalk[\"groupAllowFrom\"] = []string{\"*\"}\n\tcase \"allowlist\":\n\t\tdingtalk[\"groupAllowFrom\"] = append([]string(nil), config.GroupAllowFrom...)\n\tdefault:\n\t\tdelete(dingtalk, \"groupAllowFrom\")\n\t}\n\n\tplugins := ensureChildMap(conf, \"plugins\")\n\tentries := ensureChildMap(plugins, \"entries\")\n\tdingtalkEntry := ensureChildMap(entries, \"dingtalk-connector\")\n\tdingtalkEntry[\"enabled\"] = config.Enabled\n\n\tgateway := ensureChildMap(conf, \"gateway\")\n\thttpMap := ensureChildMap(gateway, \"http\")\n\tendpoints := ensureChildMap(httpMap, \"endpoints\")\n\tchatCompletions := ensureChildMap(endpoints, \"chatCompletions\")\n\tchatCompletions[\"enabled\"] = true\n}\n\nfunc setQQBotConfig(conf map[string]interface{}, config dto.AgentQQBotConfig) {\n\tchannels := ensureChildMap(conf, \"channels\")\n\tqqbot := ensureChildMap(channels, \"qqbot\")\n\tqqbot[\"enabled\"] = config.Enabled\n\tqqbot[\"allowFrom\"] = []string{\"*\"}\n\tqqbot[\"appId\"] = strings.TrimSpace(config.AppID)\n\tqqbot[\"clientSecret\"] = strings.TrimSpace(config.ClientSecret)\n\n\tplugins := ensureChildMap(conf, \"plugins\")\n\tentries := ensureChildMap(plugins, \"entries\")\n\tqqbotEntry := ensureChildMap(entries, \"qqbot\")\n\tqqbotEntry[\"enabled\"] = config.Enabled\n}\n\nfunc resolvePluginMeta(pluginType string) (string, string, error) {\n\tswitch pluginType {\n\tcase \"qqbot\":\n\t\treturn \"@sliverp/qqbot@latest\", \"qqbot\", nil\n\tcase \"wecom\":\n\t\treturn \"@wecom/wecom-openclaw-plugin\", \"wecom-openclaw-plugin\", nil\n\tcase \"dingtalk\":\n\t\treturn \"@dingtalk-real-ai/dingtalk-connector\", \"dingtalk-connector\", nil\n\tdefault:\n\t\treturn \"\", \"\", fmt.Errorf(\"unsupported plugin type\")\n\t}\n}\n\nfunc checkPluginInstalled(containerName, pluginType string) (bool, error) {\n\t_, pluginDir, err := resolvePluginMeta(pluginType)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif strings.TrimSpace(containerName) == \"\" {\n\t\treturn false, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tpluginPath := path.Join(openclawPluginBaseDir, pluginDir)\n\tmgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))\n\tif err := mgr.RunBashCf(\"docker exec %s test -d %s\", containerName, pluginPath); err != nil {\n\t\treturn false, nil\n\t}\n\treturn true, nil\n}\n\nfunc extractOtherConfig(conf map[string]interface{}) dto.AgentOtherConfig {\n\tresult := dto.AgentOtherConfig{UserTimezone: resolveServerTimezone(), BrowserEnabled: true}\n\tagents, ok := conf[\"agents\"].(map[string]interface{})\n\tif !ok {\n\t\tbrowser := extractBrowserConfig(conf)\n\t\tresult.BrowserEnabled = browser.Enabled\n\t\treturn result\n\t}\n\tdefaults, ok := agents[\"defaults\"].(map[string]interface{})\n\tif !ok {\n\t\tbrowser := extractBrowserConfig(conf)\n\t\tresult.BrowserEnabled = browser.Enabled\n\t\treturn result\n\t}\n\tif timezone, ok := defaults[\"userTimezone\"].(string); ok && strings.TrimSpace(timezone) != \"\" {\n\t\tresult.UserTimezone = strings.TrimSpace(timezone)\n\t}\n\tbrowser := extractBrowserConfig(conf)\n\tresult.BrowserEnabled = browser.Enabled\n\treturn result\n}\n\nfunc setOtherConfig(conf map[string]interface{}, config dto.AgentOtherConfig) {\n\tagents := ensureChildMap(conf, \"agents\")\n\tdefaults := ensureChildMap(agents, \"defaults\")\n\ttimezone := strings.TrimSpace(config.UserTimezone)\n\tif timezone == \"\" {\n\t\ttimezone = resolveServerTimezone()\n\t}\n\tdefaults[\"userTimezone\"] = timezone\n\tsetBrowserConfig(conf, browserConfig{\n\t\tEnabled:        config.BrowserEnabled,\n\t\tExecutablePath: defaultBrowserExecutablePath,\n\t\tHeadless:       true,\n\t\tNoSandbox:      true,\n\t\tDefaultProfile: defaultBrowserProfile,\n\t})\n}\n\nfunc buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map[string]interface{}) dto.AgentItem {\n\tagentType := normalizeAgentType(agent.AgentType)\n\tif appInstall != nil && appInstall.ID > 0 && appInstall.App.Key == constant.AppCopaw {\n\t\tagentType = constant.AppCopaw\n\t}\n\titem := dto.AgentItem{\n\t\tID:            agent.ID,\n\t\tName:          agent.Name,\n\t\tAgentType:     agentType,\n\t\tProvider:      agent.Provider,\n\t\tProviderName:  providerDisplayName(agent.Provider),\n\t\tModel:         agent.Model,\n\t\tAPIType:       agent.APIType,\n\t\tMaxTokens:     agent.MaxTokens,\n\t\tContextWindow: agent.ContextWindow,\n\t\tBaseURL:       agent.BaseURL,\n\t\tAPIKey:        maskKey(agent.APIKey),\n\t\tToken:         agent.Token,\n\t\tStatus:        agent.Status,\n\t\tMessage:       agent.Message,\n\t\tAppInstallID:  agent.AppInstallID,\n\t\tAccountID:     agent.AccountID,\n\t\tConfigPath:    agent.ConfigPath,\n\t\tCreatedAt:     agent.CreatedAt,\n\t}\n\tif appInstall != nil && appInstall.ID > 0 {\n\t\titem.Container = appInstall.ContainerName\n\t\titem.AppVersion = appInstall.Version\n\t\tif agentType == constant.AppOpenclaw {\n\t\t\tif isOpenclawHTTPSVersion(appInstall.Version) {\n\t\t\t\titem.WebUIPort = appInstall.HttpsPort\n\t\t\t} else {\n\t\t\t\titem.WebUIPort = appInstall.HttpPort\n\t\t\t}\n\t\t} else {\n\t\t\titem.WebUIPort = appInstall.HttpPort\n\t\t}\n\t\titem.Path = appInstall.GetPath()\n\t\titem.Status = appInstall.Status\n\t\titem.Message = appInstall.Message\n\t\tif envMap != nil {\n\t\t\tif bridge, ok := envMap[\"PANEL_APP_PORT_BRIDGE\"]; ok {\n\t\t\t\titem.BridgePort = toInt(bridge)\n\t\t\t}\n\t\t}\n\t}\n\treturn item\n}\n\nfunc isOpenclawHTTPSVersion(version string) bool {\n\ttarget := strings.TrimSpace(strings.ToLower(version))\n\tif target == \"\" || target == \"latest\" {\n\t\treturn true\n\t}\n\tif !strings.ContainsAny(target, \"0123456789\") {\n\t\treturn true\n\t}\n\treturn common.CompareAppVersion(target, openclawHTTPSVersion)\n}\n\nfunc shouldMigrateOpenclawHTTPSUpgrade(install *model.AppInstall, fromVersion, toVersion string) bool {\n\tif install == nil || install.App.Key != constant.AppOpenclaw {\n\t\treturn false\n\t}\n\treturn !isOpenclawHTTPSVersion(fromVersion) && isOpenclawHTTPSVersion(toVersion)\n}\n\nfunc migrateOpenclawHTTPSUpgrade(install *model.AppInstall, fromVersion, toVersion string) error {\n\tsystemIP, _ := settingRepo.GetValueByKey(\"SystemIP\")\n\treturn migrateOpenclawHTTPSUpgradeWithSystemIP(install, fromVersion, toVersion, systemIP)\n}\n\nfunc migrateOpenclawHTTPSUpgradeWithSystemIP(install *model.AppInstall, fromVersion, toVersion, systemIP string) error {\n\tif !shouldMigrateOpenclawHTTPSUpgrade(install, fromVersion, toVersion) {\n\t\treturn nil\n\t}\n\tmigrateOpenclawInstallPorts(install)\n\tif err := openclawutil.WriteCatchAllCaddyfile(install.GetPath()); err != nil {\n\t\treturn err\n\t}\n\tconfigPath := path.Join(install.GetPath(), \"data\", \"conf\", \"openclaw.json\")\n\tvar allowedOrigins []string\n\tif conf, err := readOpenclawConfig(configPath); err == nil {\n\t\tallowedOrigins = extractSecurityConfig(conf).AllowedOrigins\n\t}\n\toriginHost := strings.TrimSpace(systemIP)\n\tif originHost == \"\" {\n\t\toriginHost = openclawAllowedOriginHost\n\t}\n\tif install.HttpsPort > 0 {\n\t\tallowedOrigin, err := buildOpenclawAllowedOrigin(originHost, install.HttpsPort)\n\t\tif err == nil {\n\t\t\tconf, err := readOpenclawConfig(configPath)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tallowedOrigins = []string{allowedOrigin}\n\t\t\tsetSecurityConfig(conf, dto.AgentSecurityConfig{AllowedOrigins: allowedOrigins})\n\t\t\tif err := writeOpenclawConfigRaw(configPath, conf); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn migrateOpenclawInstallEnv(install, allowedOrigins)\n}\n\nfunc migrateOpenclawInstallPorts(install *model.AppInstall) {\n\tif install == nil {\n\t\treturn\n\t}\n\tif install.HttpsPort == 0 && install.HttpPort > 0 {\n\t\tinstall.HttpsPort = install.HttpPort\n\t}\n\tif install.HttpPort > 0 {\n\t\tinstall.HttpPort = 0\n\t}\n}\n\nfunc migrateOpenclawInstallEnv(install *model.AppInstall, allowedOrigins []string) error {\n\tif install == nil {\n\t\treturn nil\n\t}\n\tenvMap := make(map[string]interface{})\n\tif strings.TrimSpace(install.Env) != \"\" {\n\t\tif err := json.Unmarshal([]byte(install.Env), &envMap); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif install.HttpsPort > 0 {\n\t\tenvMap[\"PANEL_APP_PORT_HTTPS\"] = install.HttpsPort\n\t}\n\tif allowedOrigin := firstAllowedOrigin(allowedOrigins); allowedOrigin != \"\" {\n\t\tenvMap[\"ALLOWED_ORIGIN\"] = allowedOrigin\n\t}\n\tdelete(envMap, \"PANEL_APP_PORT_HTTP\")\n\tpayload, err := json.Marshal(envMap)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstall.Env = string(payload)\n\treturn nil\n}\n\nfunc syncOpenclawAllowedOriginEnv(install *model.AppInstall, allowedOrigins []string) error {\n\tif install == nil {\n\t\treturn nil\n\t}\n\tenvMap := make(map[string]interface{})\n\tif strings.TrimSpace(install.Env) != \"\" {\n\t\tif err := json.Unmarshal([]byte(install.Env), &envMap); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif allowedOrigin := firstAllowedOrigin(allowedOrigins); allowedOrigin != \"\" {\n\t\tenvMap[\"ALLOWED_ORIGIN\"] = allowedOrigin\n\t} else {\n\t\tdelete(envMap, \"ALLOWED_ORIGIN\")\n\t}\n\tpayload, err := json.Marshal(envMap)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstall.Env = string(payload)\n\treturn nil\n}\n\nfunc firstAllowedOrigin(allowedOrigins []string) string {\n\tfor _, origin := range allowedOrigins {\n\t\ttrimmed := strings.TrimSpace(origin)\n\t\tif trimmed != \"\" {\n\t\t\treturn trimmed\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc buildOpenclawAllowedOrigin(host string, port int) (string, error) {\n\thost = strings.TrimSpace(host)\n\tif host == \"\" || port <= 0 {\n\t\treturn \"\", fmt.Errorf(\"invalid openclaw allowed origin\")\n\t}\n\tif strings.Contains(host, \":\") && !strings.HasPrefix(host, \"[\") && strings.Count(host, \":\") > 1 {\n\t\thost = \"[\" + host + \"]\"\n\t}\n\treturn normalizeAllowedOrigin(fmt.Sprintf(\"https://%s:%d\", host, port))\n}\n\nfunc checkAgentUpgradable(install model.AppInstall) bool {\n\tif install.ID == 0 || install.Version == \"\" || install.Version == \"latest\" {\n\t\treturn false\n\t}\n\tif install.App.ID == 0 {\n\t\treturn false\n\t}\n\tdetails, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(install.App.ID))\n\tif err != nil || len(details) == 0 {\n\t\treturn false\n\t}\n\tversions := make([]string, 0, len(details))\n\tfor _, item := range details {\n\t\tignores, _ := appIgnoreUpgradeRepo.List(runtimeRepo.WithDetailId(item.ID), appIgnoreUpgradeRepo.WithScope(\"version\"))\n\t\tif len(ignores) > 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif common.IsCrossVersion(install.Version, item.Version) && !install.App.CrossVersionUpdate {\n\t\t\tcontinue\n\t\t}\n\t\tversions = append(versions, item.Version)\n\t}\n\tif len(versions) == 0 {\n\t\treturn false\n\t}\n\tversions = common.GetSortedVersions(versions)\n\tlastVersion := versions[0]\n\tif common.IsCrossVersion(install.Version, lastVersion) {\n\t\treturn install.App.CrossVersionUpdate\n\t}\n\treturn common.CompareVersion(lastVersion, install.Version)\n}\n\ntype openclawConfig struct {\n\tGateway gatewayConfig `json:\"gateway\"`\n\tAgents  agentsConfig  `json:\"agents\"`\n\tBrowser browserConfig `json:\"browser\"`\n\tTools   toolsConfig   `json:\"tools\"`\n\tUpdate  updateConfig  `json:\"update\"`\n\tModels  *modelsConfig `json:\"models,omitempty\"`\n}\n\ntype toolsConfig struct {\n\tProfile  string             `json:\"profile,omitempty\"`\n\tSessions toolSessionsConfig `json:\"sessions,omitempty\"`\n}\n\ntype toolSessionsConfig struct {\n\tVisibility string `json:\"visibility,omitempty\"`\n}\n\ntype updateConfig struct {\n\tCheckOnStart bool `json:\"checkOnStart\"`\n}\n\ntype gatewayConfig struct {\n\tMode           string           `json:\"mode\"`\n\tBind           string           `json:\"bind\"`\n\tPort           int              `json:\"port\"`\n\tAuth           gatewayAuth      `json:\"auth\"`\n\tControlUi      gatewayControlUi `json:\"controlUi\"`\n\tTrustedProxies []string         `json:\"trustedProxies,omitempty\"`\n}\n\ntype gatewayControlUi struct {\n\tDangerouslyDisableDeviceAuth bool     `json:\"dangerouslyDisableDeviceAuth\"`\n\tAllowedOrigins               []string `json:\"allowedOrigins,omitempty\"`\n}\n\ntype gatewayAuth struct {\n\tMode  string `json:\"mode\"`\n\tToken string `json:\"token\"`\n}\n\ntype agentsConfig struct {\n\tDefaults agentDefaults `json:\"defaults\"`\n}\n\ntype agentDefaults struct {\n\tUserTimezone string                            `json:\"userTimezone,omitempty\"`\n\tModel        modelRef                          `json:\"model\"`\n\tModels       map[string]map[string]interface{} `json:\"models,omitempty\"`\n}\n\ntype modelRef struct {\n\tPrimary string `json:\"primary\"`\n}\n\ntype modelsConfig struct {\n\tMode      string                   `json:\"mode,omitempty\"`\n\tProviders map[string]modelProvider `json:\"providers,omitempty\"`\n}\n\ntype modelProvider struct {\n\tApiKey  string       `json:\"apiKey,omitempty\"`\n\tBaseUrl string       `json:\"baseUrl,omitempty\"`\n\tApi     string       `json:\"api,omitempty\"`\n\tModels  []modelEntry `json:\"models,omitempty\"`\n}\n\ntype modelEntry struct {\n\tID            string    `json:\"id\"`\n\tName          string    `json:\"name\"`\n\tReasoning     bool      `json:\"reasoning\"`\n\tInput         []string  `json:\"input\"`\n\tContextWindow int       `json:\"contextWindow\"`\n\tMaxTokens     int       `json:\"maxTokens\"`\n\tCost          modelCost `json:\"cost\"`\n}\n\ntype modelCost struct {\n\tInput      float64 `json:\"input\"`\n\tOutput     float64 `json:\"output\"`\n\tCacheRead  float64 `json:\"cacheRead\"`\n\tCacheWrite float64 `json:\"cacheWrite\"`\n}\n\ntype browserConfig struct {\n\tEnabled        bool   `json:\"enabled\"`\n\tExecutablePath string `json:\"executablePath\"`\n\tHeadless       bool   `json:\"headless\"`\n\tNoSandbox      bool   `json:\"noSandbox\"`\n\tDefaultProfile string `json:\"defaultProfile\"`\n}\n\nfunc writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName, token string, allowedOrigins []string) error {\n\tif strings.TrimSpace(confDir) == \"\" {\n\t\treturn fmt.Errorf(\"config dir is required\")\n\t}\n\tif account == nil {\n\t\treturn fmt.Errorf(\"account is required\")\n\t}\n\tif strings.TrimSpace(modelName) == \"\" {\n\t\treturn fmt.Errorf(\"model is required\")\n\t}\n\tif strings.TrimSpace(token) == \"\" {\n\t\treturn fmt.Errorf(\"gateway token is required\")\n\t}\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(confDir) {\n\t\tif err := fileOp.CreateDir(confDir, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tprimaryModel, defaultsModels, models, err := buildOpenclawModelsFromAccount(account, modelName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcfg := openclawConfig{\n\t\tGateway: gatewayConfig{\n\t\t\tMode: \"local\",\n\t\t\tBind: \"loopback\",\n\t\t\tPort: openclawGatewayPort,\n\t\t\tAuth: gatewayAuth{\n\t\t\t\tMode:  \"token\",\n\t\t\t\tToken: token,\n\t\t\t},\n\t\t\tControlUi: gatewayControlUi{\n\t\t\t\tDangerouslyDisableDeviceAuth: true,\n\t\t\t\tAllowedOrigins:               append([]string(nil), allowedOrigins...),\n\t\t\t},\n\t\t\tTrustedProxies: []string{openclawTrustedProxyLoopback},\n\t\t},\n\t\tAgents: agentsConfig{\n\t\t\tDefaults: agentDefaults{\n\t\t\t\tUserTimezone: resolveServerTimezone(),\n\t\t\t\tModel:        modelRef{Primary: primaryModel},\n\t\t\t\tModels:       defaultsModels,\n\t\t\t},\n\t\t},\n\t\tBrowser: browserConfig{\n\t\t\tEnabled:        true,\n\t\t\tExecutablePath: defaultBrowserExecutablePath,\n\t\t\tHeadless:       true,\n\t\t\tNoSandbox:      true,\n\t\t\tDefaultProfile: defaultBrowserProfile,\n\t\t},\n\t\tTools: toolsConfig{\n\t\t\tProfile: defaultToolsProfile,\n\t\t\tSessions: toolSessionsConfig{\n\t\t\t\tVisibility: defaultToolsSessionVisibility,\n\t\t\t},\n\t\t},\n\t\tUpdate: updateConfig{\n\t\t\tCheckOnStart: false,\n\t\t},\n\t\tModels: models,\n\t}\n\n\tconfigPath := path.Join(confDir, \"openclaw.json\")\n\tconf := map[string]interface{}{}\n\tif fileOp.Stat(configPath) {\n\t\texisting, err := readOpenclawConfig(configPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tconf = existing\n\t}\n\tif len(conf) == 0 {\n\t\tinitial, err := structToMap(cfg)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tconf = initial\n\t} else {\n\t\tif cfg.Models != nil {\n\t\t\tmodelsMap, err := structToMap(cfg.Models)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tconf[\"models\"] = modelsMap\n\t\t}\n\t\tif _, ok := conf[\"browser\"]; !ok {\n\t\t\tbrowserMap, err := structToMap(cfg.Browser)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tconf[\"browser\"] = browserMap\n\t\t}\n\t\ttoolsMap := ensureChildMap(conf, \"tools\")\n\t\tif profile, ok := toolsMap[\"profile\"]; !ok || strings.TrimSpace(fmt.Sprintf(\"%v\", profile)) == \"\" {\n\t\t\ttoolsMap[\"profile\"] = defaultToolsProfile\n\t\t}\n\t\tsessionsMap := ensureChildMap(toolsMap, \"sessions\")\n\t\tif visibility, ok := sessionsMap[\"visibility\"]; !ok || strings.TrimSpace(fmt.Sprintf(\"%v\", visibility)) == \"\" {\n\t\t\tsessionsMap[\"visibility\"] = defaultToolsSessionVisibility\n\t\t}\n\t\tagentsMap := ensureChildMap(conf, \"agents\")\n\t\tdefaultsMap := ensureChildMap(agentsMap, \"defaults\")\n\t\tif tz, ok := defaultsMap[\"userTimezone\"]; !ok || strings.TrimSpace(fmt.Sprintf(\"%v\", tz)) == \"\" {\n\t\t\tdefaultsMap[\"userTimezone\"] = resolveServerTimezone()\n\t\t}\n\t\tmodelMap := ensureChildMap(defaultsMap, \"model\")\n\t\tmodelMap[\"primary\"] = cfg.Agents.Defaults.Model.Primary\n\t\tif cfg.Agents.Defaults.Models != nil {\n\t\t\tdefaultsMap[\"models\"] = cfg.Agents.Defaults.Models\n\t\t}\n\n\t\tensureGatewaySecurityDefaults(conf)\n\t\tgatewayMap := ensureChildMap(conf, \"gateway\")\n\t\tif _, ok := gatewayMap[\"mode\"]; !ok {\n\t\t\tgatewayMap[\"mode\"] = \"local\"\n\t\t}\n\t\tif _, ok := gatewayMap[\"bind\"]; !ok {\n\t\t\tgatewayMap[\"bind\"] = \"loopback\"\n\t\t}\n\t\tif _, ok := gatewayMap[\"port\"]; !ok {\n\t\t\tgatewayMap[\"port\"] = openclawGatewayPort\n\t\t}\n\t\tauthMap := ensureChildMap(gatewayMap, \"auth\")\n\t\tif _, ok := authMap[\"mode\"]; !ok {\n\t\t\tauthMap[\"mode\"] = \"token\"\n\t\t}\n\t\tauthMap[\"token\"] = token\n\t}\n\tif allowedOrigins != nil {\n\t\tsetSecurityConfig(conf, dto.AgentSecurityConfig{AllowedOrigins: allowedOrigins})\n\t}\n\tif err := writeOpenclawConfigRaw(configPath, conf); err != nil {\n\t\treturn err\n\t}\n\tenvPath := path.Join(confDir, \".env\")\n\tlines := []string{fmt.Sprintf(\"OPENCLAW_GATEWAY_TOKEN=%s\", token)}\n\tif envKey := providerEnvKey(account.Provider); envKey != \"\" && strings.TrimSpace(account.APIKey) != \"\" {\n\t\tlines = append(lines, fmt.Sprintf(\"%s=%s\", envKey, account.APIKey))\n\t}\n\tcontent := strings.Join(lines, \"\\n\") + \"\\n\"\n\treturn fileOp.SaveFile(envPath, content, 0600)\n}\n\nfunc prepareOpenclawInstallFiles(appInstall *model.AppInstall, account *model.AgentAccount, modelName, token string, allowedOrigins []string) error {\n\tif appInstall == nil {\n\t\treturn fmt.Errorf(\"app install is required\")\n\t}\n\tconfDir := path.Join(appInstall.GetPath(), \"data\", \"conf\")\n\tif err := writeOpenclawConfig(confDir, account, modelName, token, allowedOrigins); err != nil {\n\t\treturn err\n\t}\n\tdataDir := path.Join(appInstall.GetPath(), \"data\")\n\treturn files.NewFileOp().ChownR(dataDir, \"1000\", \"1000\", true)\n}\n\nfunc buildOpenclawModelsFromAccount(account *model.AgentAccount, selectedModel string) (string, map[string]map[string]interface{}, *modelsConfig, error) {\n\taccountModels, err := loadAgentAccountModels(account)\n\tif err != nil {\n\t\treturn \"\", nil, nil, err\n\t}\n\tif len(accountModels) == 0 {\n\t\treturn \"\", nil, nil, fmt.Errorf(\"model is required\")\n\t}\n\tselectedModel = strings.TrimSpace(selectedModel)\n\tif selectedModel == \"\" {\n\t\tselectedModel = strings.TrimSpace(accountModels[0].ID)\n\t}\n\tif selectedModel == \"\" {\n\t\treturn \"\", nil, nil, fmt.Errorf(\"model is required\")\n\t}\n\tselectedAccountModel, err := requireAgentAccountModelForProvider(account.Provider, accountModels, selectedModel)\n\tif err != nil {\n\t\treturn \"\", nil, nil, err\n\t}\n\tselectedModel = selectedAccountModel.ID\n\n\tproviderKey := \"\"\n\tproviderCfg := modelProvider{}\n\tentries := make([]modelEntry, 0, len(accountModels))\n\tprimaryModel := \"\"\n\tdefaultsModels := make(map[string]map[string]interface{}, len(accountModels))\n\tfor _, item := range accountModels {\n\t\tresolvedPrimary, entry, key, baseCfg, err := buildOpenclawCatalogModel(account, item)\n\t\tif err != nil {\n\t\t\treturn \"\", nil, nil, err\n\t\t}\n\t\tif providerKey == \"\" {\n\t\t\tproviderKey = key\n\t\t\tproviderCfg.ApiKey = baseCfg.ApiKey\n\t\t\tproviderCfg.BaseUrl = baseCfg.BaseUrl\n\t\t\tproviderCfg.Api = baseCfg.Api\n\t\t}\n\t\tentries = append(entries, entry)\n\t\tdefaultsModels[resolvedPrimary] = map[string]interface{}{}\n\t\tif sameProviderModelID(account.Provider, item.ID, selectedModel) {\n\t\t\tprimaryModel = resolvedPrimary\n\t\t}\n\t}\n\tif primaryModel == \"\" {\n\t\treturn \"\", nil, nil, buserr.New(\"ErrAgentModelNotInAccount\")\n\t}\n\tproviderCfg.Models = entries\n\treturn primaryModel, defaultsModels, &modelsConfig{\n\t\tMode: \"merge\",\n\t\tProviders: map[string]modelProvider{\n\t\t\tproviderKey: providerCfg,\n\t\t},\n\t}, nil\n}\n\nfunc buildOpenclawCatalogModel(account *model.AgentAccount, model dto.AgentAccountModel) (string, modelEntry, string, modelProvider, error) {\n\tprimaryModel, inferredEntry, providerKey, providerCfg, err := inferOpenclawCatalogModel(account, model.ID, model.MaxTokens, model.ContextWindow)\n\tif err != nil {\n\t\treturn \"\", modelEntry{}, \"\", modelProvider{}, err\n\t}\n\tif strings.TrimSpace(model.Name) != \"\" {\n\t\tinferredEntry.Name = strings.TrimSpace(model.Name)\n\t}\n\tif len(model.Input) > 0 {\n\t\tinferredEntry.Input = sanitizeAgentAccountModelInputs(model.Input)\n\t}\n\tinferredEntry.Reasoning = model.Reasoning\n\tif model.ContextWindow > 0 {\n\t\tinferredEntry.ContextWindow = model.ContextWindow\n\t}\n\tif model.MaxTokens > 0 {\n\t\tinferredEntry.MaxTokens = model.MaxTokens\n\t}\n\treturn primaryModel, inferredEntry, providerKey, providerCfg, nil\n}\n\ntype openclawAccountModelRuntime struct {\n\tStoredModel   string\n\tPrimaryModel  string\n\tAPIType       string\n\tMaxTokens     int\n\tContextWindow int\n}\n\nfunc buildOpenclawAccountModelRuntime(account *model.AgentAccount, model dto.AgentAccountModel) (openclawAccountModelRuntime, error) {\n\tapiType, maxTokens, contextWindow := resolveRuntimeParams(\n\t\taccount.Provider,\n\t\taccount.APIType,\n\t\tmodel.MaxTokens,\n\t\tmodel.ContextWindow,\n\t)\n\tprimaryModel, _, _, _, err := buildOpenclawCatalogModel(account, model)\n\tif err != nil {\n\t\treturn openclawAccountModelRuntime{}, err\n\t}\n\treturn openclawAccountModelRuntime{\n\t\tStoredModel:   model.ID,\n\t\tPrimaryModel:  primaryModel,\n\t\tAPIType:       apiType,\n\t\tMaxTokens:     maxTokens,\n\t\tContextWindow: contextWindow,\n\t}, nil\n}\n\nfunc resolveOpenclawAccountModelRuntimeByID(account *model.AgentAccount, modelID string) (openclawAccountModelRuntime, error) {\n\taccountModels, err := loadAgentAccountModels(account)\n\tif err != nil {\n\t\treturn openclawAccountModelRuntime{}, err\n\t}\n\tselectedAccountModel, err := requireAgentAccountModelForProvider(account.Provider, accountModels, modelID)\n\tif err != nil {\n\t\treturn openclawAccountModelRuntime{}, err\n\t}\n\treturn buildOpenclawAccountModelRuntime(account, selectedAccountModel)\n}\n\nfunc inferOpenclawCatalogModel(account *model.AgentAccount, modelID string, maxTokens, contextWindow int) (string, modelEntry, string, modelProvider, error) {\n\tbaseURL := resolveAccountBaseURL(account)\n\tresolvedAPIType, resolvedMaxTokens, resolvedContextWindow := resolveRuntimeParams(account.Provider, account.APIType, maxTokens, contextWindow)\n\tpatch, err := providercatalog.BuildOpenClawPatch(account.Provider, modelID, resolvedAPIType, resolvedMaxTokens, resolvedContextWindow, baseURL, account.APIKey)\n\tif err != nil {\n\t\treturn \"\", modelEntry{}, \"\", modelProvider{}, err\n\t}\n\tif patch.Models == nil {\n\t\treturn \"\", modelEntry{}, \"\", modelProvider{}, fmt.Errorf(\"models patch is required\")\n\t}\n\tmodelsCfg, err := mapToModelsConfig(patch.Models)\n\tif err != nil {\n\t\treturn \"\", modelEntry{}, \"\", modelProvider{}, err\n\t}\n\tfor key, providerCfg := range modelsCfg.Providers {\n\t\tif len(providerCfg.Models) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\treturn patch.PrimaryModel, providerCfg.Models[0], key, modelProvider{\n\t\t\tApiKey:  providerCfg.ApiKey,\n\t\t\tBaseUrl: providerCfg.BaseUrl,\n\t\t\tApi:     providerCfg.Api,\n\t\t}, nil\n\t}\n\treturn \"\", modelEntry{}, \"\", modelProvider{}, fmt.Errorf(\"models patch is invalid\")\n}\n\nfunc resolveAccountBaseURL(account *model.AgentAccount) string {\n\tbaseURL := strings.TrimSpace(account.BaseURL)\n\tif baseURL == \"\" {\n\t\tif defaultURL, ok := providerDefaultBaseURL(account.Provider); ok {\n\t\t\tbaseURL = defaultURL\n\t\t}\n\t}\n\treturn baseURL\n}\n\nfunc buildInitialAgentAccountModels(account *model.AgentAccount, requested []dto.AgentAccountModel) ([]dto.AgentAccountModel, error) {\n\tif account == nil {\n\t\treturn nil, fmt.Errorf(\"account is required\")\n\t}\n\tif requiresInitialAgentAccountModels(account.Provider) && len(requested) > 1 {\n\t\treturn nil, buserr.New(\"ErrAgentAccountSingleInitialModel\")\n\t}\n\tif len(requested) > 0 {\n\t\tmodels, _, err := normalizeAgentAccountModels(account, requested, \"\", true)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn models, nil\n\t}\n\tmeta, ok := providercatalog.Get(account.Provider)\n\tif !ok || len(meta.Models) == 0 {\n\t\tif requiresInitialAgentAccountModels(account.Provider) {\n\t\t\treturn nil, buserr.New(\"ErrAgentAccountModelsRequired\")\n\t\t}\n\t\treturn nil, nil\n\t}\n\trequested = make([]dto.AgentAccountModel, 0, len(meta.Models))\n\tfor _, item := range meta.Models {\n\t\trequested = append(requested, dto.AgentAccountModel{\n\t\t\tID:            item.ID,\n\t\t\tName:          item.Name,\n\t\t\tContextWindow: item.ContextWindow,\n\t\t\tMaxTokens:     item.MaxTokens,\n\t\t\tReasoning:     item.Reasoning,\n\t\t\tInput:         append([]string(nil), item.Input...),\n\t\t})\n\t}\n\tmodels, _, err := normalizeAgentAccountModels(account, requested, \"\", true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn models, nil\n}\n\nfunc compactPersistedAgentAccountModelSortOrder(accountID uint) error {\n\trows, err := agentAccountModelRepo.List(repo.WithByAccountID(accountID), repo.WithOrderAsc(\"sort_order\"), repo.WithOrderAsc(\"id\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor index := range rows {\n\t\torder := index + 1\n\t\tif rows[index].SortOrder == order {\n\t\t\tcontinue\n\t\t}\n\t\trows[index].SortOrder = order\n\t\tif err := agentAccountModelRepo.Save(&rows[index]); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc loadAgentAccountModels(account *model.AgentAccount) ([]dto.AgentAccountModel, error) {\n\tif account == nil {\n\t\treturn nil, fmt.Errorf(\"account is required\")\n\t}\n\treturn listPersistedAgentAccountModels(account.ID)\n}\n\nfunc MergeCatalogAgentAccountModelsForMigration(account *model.AgentAccount, existing []dto.AgentAccountModel) ([]dto.AgentAccountModel, error) {\n\tif account == nil {\n\t\treturn nil, fmt.Errorf(\"account is required\")\n\t}\n\tmeta, ok := providercatalog.Get(account.Provider)\n\tif !ok || len(meta.Models) == 0 {\n\t\treturn append([]dto.AgentAccountModel(nil), existing...), nil\n\t}\n\trequested := append([]dto.AgentAccountModel(nil), existing...)\n\tseen := make(map[string]struct{}, len(existing))\n\tfor _, item := range existing {\n\t\tif strings.TrimSpace(item.ID) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tseen[strings.TrimSpace(item.ID)] = struct{}{}\n\t}\n\tfor _, item := range meta.Models {\n\t\tif _, ok := seen[strings.TrimSpace(item.ID)]; ok {\n\t\t\tcontinue\n\t\t}\n\t\trequested = append(requested, dto.AgentAccountModel{\n\t\t\tID:            item.ID,\n\t\t\tName:          item.Name,\n\t\t\tContextWindow: item.ContextWindow,\n\t\t\tMaxTokens:     item.MaxTokens,\n\t\t\tReasoning:     item.Reasoning,\n\t\t\tInput:         append([]string(nil), item.Input...),\n\t\t})\n\t}\n\tif len(requested) == len(existing) {\n\t\treturn append([]dto.AgentAccountModel(nil), existing...), nil\n\t}\n\tnormalized, _, err := normalizeAgentAccountModels(account, requested, \"\", true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn normalized, nil\n}\n\nfunc listPersistedAgentAccountModels(accountID uint) ([]dto.AgentAccountModel, error) {\n\tif accountID == 0 {\n\t\treturn nil, nil\n\t}\n\trows, err := agentAccountModelRepo.List(repo.WithByAccountID(accountID), repo.WithOrderAsc(\"sort_order\"), repo.WithOrderAsc(\"id\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := make([]dto.AgentAccountModel, 0, len(rows))\n\tfor _, row := range rows {\n\t\tinputs := []string{}\n\t\tif strings.TrimSpace(row.Input) != \"\" {\n\t\t\t_ = json.Unmarshal([]byte(row.Input), &inputs)\n\t\t}\n\t\tresult = append(result, dto.AgentAccountModel{\n\t\t\tRecordID:      row.ID,\n\t\t\tID:            strings.TrimSpace(row.Model),\n\t\t\tName:          strings.TrimSpace(row.Name),\n\t\t\tContextWindow: row.ContextWindow,\n\t\t\tMaxTokens:     row.MaxTokens,\n\t\t\tReasoning:     row.Reasoning,\n\t\t\tInput:         sanitizeAgentAccountModelInputs(inputs),\n\t\t})\n\t}\n\treturn result, nil\n}\n\nfunc replacePersistedAgentAccountModelsWithTx(tx *gorm.DB, accountID uint, models []dto.AgentAccountModel) error {\n\tif err := tx.Where(\"account_id = ?\", accountID).Delete(&model.AgentAccountModel{}).Error; err != nil {\n\t\treturn err\n\t}\n\tfor index, item := range models {\n\t\tinputPayload, err := json.Marshal(sanitizeAgentAccountModelInputs(item.Input))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trecord := &model.AgentAccountModel{\n\t\t\tAccountID:     accountID,\n\t\t\tModel:         strings.TrimSpace(item.ID),\n\t\t\tName:          strings.TrimSpace(item.Name),\n\t\t\tContextWindow: item.ContextWindow,\n\t\t\tMaxTokens:     item.MaxTokens,\n\t\t\tReasoning:     item.Reasoning,\n\t\t\tInput:         string(inputPayload),\n\t\t\tSortOrder:     index + 1,\n\t\t}\n\t\tif err := tx.Create(record).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc replacePersistedAgentAccountModels(accountID uint, models []dto.AgentAccountModel) error {\n\treturn global.DB.Transaction(func(tx *gorm.DB) error {\n\t\treturn replacePersistedAgentAccountModelsWithTx(tx, accountID, models)\n\t})\n}\n\nfunc normalizeAgentAccountModels(account *model.AgentAccount, models []dto.AgentAccountModel, defaultModel string, allowFallbackDefault bool) ([]dto.AgentAccountModel, string, error) {\n\trequested := append([]dto.AgentAccountModel(nil), models...)\n\tif len(requested) == 0 {\n\t\tif strings.TrimSpace(defaultModel) != \"\" {\n\t\t\trequested = []dto.AgentAccountModel{{ID: defaultModel}}\n\t\t} else {\n\t\t\trequested = buildLegacyAgentAccountModels(account)\n\t\t}\n\t}\n\tnormalized := make([]dto.AgentAccountModel, 0, len(requested))\n\tseen := make(map[string]struct{}, len(requested))\n\tfor _, item := range requested {\n\t\tnormalizedItem, err := normalizeAgentAccountModel(account, item)\n\t\tif err != nil {\n\t\t\treturn nil, \"\", err\n\t\t}\n\t\tif strings.TrimSpace(normalizedItem.ID) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[normalizedItem.ID]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[normalizedItem.ID] = struct{}{}\n\t\tnormalized = append(normalized, normalizedItem)\n\t}\n\tif len(normalized) == 0 {\n\t\treturn nil, \"\", fmt.Errorf(\"model is required\")\n\t}\n\n\tresolvedDefault := strings.TrimSpace(defaultModel)\n\tif resolvedDefault != \"\" {\n\t\tdefaultItem, err := normalizeAgentAccountModel(account, dto.AgentAccountModel{ID: resolvedDefault})\n\t\tif err == nil {\n\t\t\tresolvedDefault = defaultItem.ID\n\t\t}\n\t}\n\tif resolvedDefault == \"\" && allowFallbackDefault {\n\t\tresolvedDefault = normalized[0].ID\n\t}\n\tif _, ok := findAgentAccountModelForProvider(account.Provider, normalized, resolvedDefault); !ok {\n\t\tif allowFallbackDefault {\n\t\t\tresolvedDefault = normalized[0].ID\n\t\t} else {\n\t\t\treturn nil, \"\", buserr.New(\"ErrAgentModelNotInAccount\")\n\t\t}\n\t}\n\treturn normalized, resolvedDefault, nil\n}\n\nfunc normalizeAgentAccountModel(account *model.AgentAccount, model dto.AgentAccountModel) (dto.AgentAccountModel, error) {\n\tmodelID := strings.TrimSpace(model.ID)\n\tif modelID == \"\" {\n\t\treturn dto.AgentAccountModel{}, fmt.Errorf(\"model is required\")\n\t}\n\tprimaryModel, inferredEntry, _, _, err := inferOpenclawCatalogModel(account, modelID, model.MaxTokens, model.ContextWindow)\n\tif err != nil {\n\t\treturn dto.AgentAccountModel{}, err\n\t}\n\tname := strings.TrimSpace(model.Name)\n\tif name == \"\" {\n\t\tname = strings.TrimSpace(inferredEntry.Name)\n\t}\n\treasoning := model.Reasoning\n\tif !model.Reasoning && model.Name == \"\" && model.MaxTokens == 0 && model.ContextWindow == 0 && len(model.Input) == 0 {\n\t\treasoning = inferredEntry.Reasoning\n\t}\n\tinputs := sanitizeAgentAccountModelInputs(model.Input)\n\tif len(inputs) == 0 {\n\t\tinputs = sanitizeAgentAccountModelInputs(inferredEntry.Input)\n\t}\n\tcontextWindow := model.ContextWindow\n\tif contextWindow <= 0 {\n\t\tcontextWindow = inferredEntry.ContextWindow\n\t}\n\tmaxTokens := model.MaxTokens\n\tif maxTokens <= 0 {\n\t\tmaxTokens = inferredEntry.MaxTokens\n\t}\n\treturn dto.AgentAccountModel{\n\t\tID:            normalizeAgentAccountModelID(account.Provider, primaryModel, modelID),\n\t\tName:          name,\n\t\tContextWindow: contextWindow,\n\t\tMaxTokens:     maxTokens,\n\t\tReasoning:     reasoning,\n\t\tInput:         inputs,\n\t}, nil\n}\n\nfunc normalizeAgentAccountModelID(provider, primaryModel, requestedID string) string {\n\tswitch provider {\n\tcase \"custom\", \"vllm\":\n\t\ttarget := requestedID\n\t\tif strings.TrimSpace(target) == \"\" {\n\t\t\ttarget = primaryModel\n\t\t}\n\t\treturn normalizeCustomModel(target)\n\tcase \"ollama\":\n\t\ttarget := strings.TrimSpace(primaryModel)\n\t\tif strings.HasPrefix(target, \"ollama/\") {\n\t\t\treturn target\n\t\t}\n\t\ttarget = strings.TrimSpace(requestedID)\n\t\tif strings.HasPrefix(target, \"ollama/\") {\n\t\t\treturn target\n\t\t}\n\t\ttarget = strings.TrimLeft(strings.TrimSpace(target), \"/\")\n\t\tif target == \"\" {\n\t\t\ttarget = strings.TrimLeft(strings.TrimSpace(primaryModel), \"/\")\n\t\t}\n\t\tif target == \"\" {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn \"ollama/\" + target\n\tdefault:\n\t\ttarget := strings.TrimSpace(requestedID)\n\t\tif target == \"\" {\n\t\t\ttarget = strings.TrimSpace(primaryModel)\n\t\t}\n\t\tif target == \"\" {\n\t\t\treturn \"\"\n\t\t}\n\t\tprefix := poolModelPrefix(provider)\n\t\tif strings.Contains(target, \"/\") {\n\t\t\tparts := strings.SplitN(target, \"/\", 2)\n\t\t\ttargetPrefix := parts[0]\n\t\t\ttargetModel := strings.TrimSpace(parts[1])\n\t\t\tif targetModel == \"\" {\n\t\t\t\treturn strings.TrimSpace(target)\n\t\t\t}\n\t\t\tfor _, item := range supportedProviderModelPrefixes(provider) {\n\t\t\t\tif item == targetPrefix {\n\t\t\t\t\tif prefix != \"\" {\n\t\t\t\t\t\treturn prefix + \"/\" + targetModel\n\t\t\t\t\t}\n\t\t\t\t\treturn strings.TrimSpace(target)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn strings.TrimSpace(target)\n\t\t}\n\t\ttarget = strings.TrimLeft(strings.TrimSpace(target), \"/\")\n\t\tif prefix == \"\" {\n\t\t\treturn target\n\t\t}\n\t\treturn prefix + \"/\" + target\n\t}\n}\n\nfunc buildLegacyAgentAccountModels(account *model.AgentAccount) []dto.AgentAccountModel {\n\tmodelIDs := make([]string, 0, 4)\n\tseen := make(map[string]struct{}, 4)\n\tappendModel := func(value string) {\n\t\ttarget := strings.TrimSpace(value)\n\t\tif target == \"\" {\n\t\t\treturn\n\t\t}\n\t\tif _, ok := seen[target]; ok {\n\t\t\treturn\n\t\t}\n\t\tseen[target] = struct{}{}\n\t\tmodelIDs = append(modelIDs, target)\n\t}\n\tif account.ID > 0 {\n\t\tif agents, err := agentRepo.List(repo.WithByAccountID(account.ID)); err == nil {\n\t\t\tfor _, agent := range agents {\n\t\t\t\tappendModel(agent.Model)\n\t\t\t}\n\t\t}\n\t}\n\tif definitions, ok := providerDefinitions()[account.Provider]; ok && len(definitions.Models) > 0 {\n\t\tfor _, item := range definitions.Models {\n\t\t\tappendModel(item.ID)\n\t\t}\n\t}\n\tmodels := make([]dto.AgentAccountModel, 0, len(modelIDs))\n\tfor _, modelID := range modelIDs {\n\t\tmodels = append(models, dto.AgentAccountModel{ID: modelID})\n\t}\n\treturn models\n}\n\nfunc sanitizeAgentAccountModelInputs(values []string) []string {\n\tresult := make([]string, 0, len(values))\n\tseen := make(map[string]struct{}, len(values))\n\tfor _, value := range values {\n\t\tnormalized := value\n\t\tif normalized != \"text\" && normalized != \"image\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[normalized]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[normalized] = struct{}{}\n\t\tresult = append(result, normalized)\n\t}\n\tif len(result) == 0 {\n\t\treturn []string{\"text\"}\n\t}\n\treturn result\n}\n\nfunc requiresInitialAgentAccountModels(provider string) bool {\n\tswitch provider {\n\tcase \"custom\", \"vllm\", \"ollama\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc normalizeComparableProviderModelID(provider, modelID string) string {\n\ttarget := strings.TrimSpace(modelID)\n\tif target == \"\" {\n\t\treturn \"\"\n\t}\n\tif !strings.Contains(target, \"/\") {\n\t\treturn target\n\t}\n\tparts := strings.SplitN(target, \"/\", 2)\n\tprefix := parts[0]\n\tmodel := strings.TrimSpace(parts[1])\n\tif model == \"\" {\n\t\treturn target\n\t}\n\tfor _, item := range supportedProviderModelPrefixes(provider) {\n\t\tif item == prefix {\n\t\t\treturn model\n\t\t}\n\t}\n\treturn target\n}\n\nfunc sameProviderModelID(provider, left, right string) bool {\n\tleftTrimmed := strings.TrimSpace(left)\n\trightTrimmed := strings.TrimSpace(right)\n\tif leftTrimmed == rightTrimmed {\n\t\treturn true\n\t}\n\tleftComparable := normalizeComparableProviderModelID(provider, leftTrimmed)\n\trightComparable := normalizeComparableProviderModelID(provider, rightTrimmed)\n\treturn leftComparable != \"\" && leftComparable == rightComparable\n}\n\nfunc findAgentAccountModelForProvider(provider string, models []dto.AgentAccountModel, modelID string) (dto.AgentAccountModel, bool) {\n\tfor _, item := range models {\n\t\tif sameProviderModelID(provider, item.ID, modelID) {\n\t\t\treturn item, true\n\t\t}\n\t}\n\treturn dto.AgentAccountModel{}, false\n}\n\nfunc requireAgentAccountModelForProvider(provider string, models []dto.AgentAccountModel, modelID string) (dto.AgentAccountModel, error) {\n\tselectedAccountModel, ok := findAgentAccountModelForProvider(provider, models, modelID)\n\tif !ok {\n\t\treturn dto.AgentAccountModel{}, buserr.New(\"ErrAgentModelNotInAccount\")\n\t}\n\treturn selectedAccountModel, nil\n}\n\nfunc ensureAccountModelsNotBound(account *model.AgentAccount, models []dto.AgentAccountModel) error {\n\tif account == nil || account.ID == 0 {\n\t\treturn nil\n\t}\n\tagents, err := agentRepo.List(repo.WithByAccountID(account.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, agent := range agents {\n\t\tif strings.TrimSpace(agent.Model) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := findAgentAccountModelForProvider(account.Provider, models, agent.Model); !ok {\n\t\t\treturn buserr.WithName(\"ErrAgentModelInUse\", agent.Name)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc resolveServerTimezone() string {\n\ttimezone := strings.TrimSpace(common.LoadTimeZoneByCmd())\n\tif timezone == \"\" {\n\t\treturn defaultUserTimezone\n\t}\n\tif _, err := time.LoadLocation(timezone); err != nil {\n\t\treturn defaultUserTimezone\n\t}\n\treturn timezone\n}\n\nfunc extractStringList(value interface{}) []string {\n\tswitch values := value.(type) {\n\tcase []interface{}:\n\t\tresult := make([]string, 0, len(values))\n\t\tfor _, value := range values {\n\t\t\ttext := strings.TrimSpace(fmt.Sprintf(\"%v\", value))\n\t\t\tif text == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tresult = append(result, text)\n\t\t}\n\t\treturn result\n\tcase []string:\n\t\tresult := make([]string, 0, len(values))\n\t\tfor _, value := range values {\n\t\t\ttext := strings.TrimSpace(value)\n\t\t\tif text == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tresult = append(result, text)\n\t\t}\n\t\treturn result\n\tdefault:\n\t\treturn []string{}\n\t}\n}\n\nfunc extractGatewayToken(conf map[string]interface{}) string {\n\tgateway, ok := conf[\"gateway\"].(map[string]interface{})\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tauth, ok := gateway[\"auth\"].(map[string]interface{})\n\tif !ok {\n\t\treturn \"\"\n\t}\n\ttoken, _ := auth[\"token\"].(string)\n\treturn token\n}\n\nfunc ensureChildMap(parent map[string]interface{}, key string) map[string]interface{} {\n\tif child, ok := parent[key].(map[string]interface{}); ok {\n\t\treturn child\n\t}\n\tchild := map[string]interface{}{}\n\tparent[key] = child\n\treturn child\n}\n\nfunc structToMap(value interface{}) (map[string]interface{}, error) {\n\tpayload, err := json.Marshal(value)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := map[string]interface{}{}\n\tif err := json.Unmarshal(payload, &result); err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\nfunc mapToModelsConfig(value map[string]interface{}) (*modelsConfig, error) {\n\tpayload, err := json.Marshal(value)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresult := &modelsConfig{}\n\tif err := json.Unmarshal(payload, result); err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\nfunc providerEnvKey(provider string) string {\n\treturn providercatalog.EnvKey(provider)\n}\n\ntype providerDefinition struct {\n\tSort        uint\n\tDisplayName string\n\tBaseURL     string\n\tModels      []dto.ProviderModelInfo\n}\n\nfunc providerDefinitions() map[string]providerDefinition {\n\tdefinitions := map[string]providerDefinition{}\n\tfor key, meta := range providercatalog.All() {\n\t\tmodels := make([]dto.ProviderModelInfo, 0, len(meta.Models))\n\t\tfor _, m := range meta.Models {\n\t\t\tmodels = append(models, dto.ProviderModelInfo{\n\t\t\t\tID:            m.ID,\n\t\t\t\tName:          m.Name,\n\t\t\t\tContextWindow: m.ContextWindow,\n\t\t\t\tMaxTokens:     m.MaxTokens,\n\t\t\t\tReasoning:     m.Reasoning,\n\t\t\t\tInput:         append([]string(nil), m.Input...),\n\t\t\t})\n\t\t}\n\t\tdefinitions[key] = providerDefinition{\n\t\t\tSort:        meta.Sort,\n\t\t\tDisplayName: meta.DisplayName,\n\t\t\tBaseURL:     meta.DefaultBaseURL,\n\t\t\tModels:      models,\n\t\t}\n\t}\n\treturn definitions\n}\n\nfunc providerDefaultBaseURL(provider string) (string, bool) {\n\treturn providercatalog.DefaultBaseURL(provider)\n}\n\nfunc fixedProviderBaseURL(provider string) (string, bool) {\n\tswitch provider {\n\tcase \"bailian-coding-plan\":\n\t\treturn providerDefaultBaseURL(provider)\n\tcase \"ark-coding-plan\":\n\t\treturn providerDefaultBaseURL(provider)\n\tdefault:\n\t\treturn \"\", false\n\t}\n}\n\nfunc providerDisplayName(provider string) string {\n\treturn providercatalog.DisplayName(provider)\n}\n\nfunc readInstallEnv(envStr string) map[string]interface{} {\n\tif strings.TrimSpace(envStr) == \"\" {\n\t\treturn nil\n\t}\n\tdata := map[string]interface{}{}\n\tif err := json.Unmarshal([]byte(envStr), &data); err != nil {\n\t\treturn nil\n\t}\n\treturn data\n}\n\nfunc maskKey(value string) string {\n\ttrim := strings.TrimSpace(value)\n\tif len(trim) <= 6 {\n\t\treturn trim\n\t}\n\treturn fmt.Sprintf(\"%s****%s\", trim[:3], trim[len(trim)-3:])\n}\n\nfunc toInt(value interface{}) int {\n\tswitch v := value.(type) {\n\tcase int:\n\t\treturn v\n\tcase int64:\n\t\treturn int(v)\n\tcase float64:\n\t\treturn int(v)\n\tcase string:\n\t\tif v == \"\" {\n\t\t\treturn 0\n\t\t}\n\t\tparsed, _ := strconv.Atoi(v)\n\t\treturn parsed\n\tdefault:\n\t\treturn 0\n\t}\n}\n\nfunc normalizeCustomModel(modelName string) string {\n\ttrim := strings.TrimSpace(modelName)\n\ttrim = strings.TrimLeft(trim, \"/\")\n\tif parts := strings.SplitN(trim, \"/\", 2); len(parts) == 2 {\n\t\tif strings.EqualFold(parts[0], \"custom\") {\n\t\t\treturn strings.TrimLeft(strings.TrimSpace(parts[1]), \"/\")\n\t\t}\n\t}\n\treturn trim\n}\n\nfunc normalizeAgentType(agentType string) string {\n\ttrim := agentType\n\tif trim == \"\" {\n\t\treturn constant.AppOpenclaw\n\t}\n\treturn trim\n}\n\nfunc modelMatchesProvider(provider, modelName string) bool {\n\ttarget := strings.TrimSpace(modelName)\n\tfor _, prefix := range supportedProviderModelPrefixes(provider) {\n\t\tif prefix != \"\" && strings.HasPrefix(target, prefix+\"/\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc runtimeProviderModelPrefix(provider string) string {\n\tswitch provider {\n\tcase \"gemini\":\n\t\treturn \"google\"\n\tcase \"minimax\":\n\t\treturn \"minimax-portal\"\n\tcase \"kimi\":\n\t\treturn \"moonshot\"\n\tdefault:\n\t\treturn provider\n\t}\n}\n\nfunc poolModelPrefix(provider string) string {\n\ttarget := provider\n\tif definitions, ok := providerDefinitions()[target]; ok && len(definitions.Models) > 0 {\n\t\tparts := strings.SplitN(strings.TrimSpace(definitions.Models[0].ID), \"/\", 2)\n\t\tif len(parts) == 2 && strings.TrimSpace(parts[0]) != \"\" {\n\t\t\treturn parts[0]\n\t\t}\n\t}\n\treturn target\n}\n\nfunc supportedProviderModelPrefixes(provider string) []string {\n\tvalues := []string{poolModelPrefix(provider), runtimeProviderModelPrefix(provider)}\n\tresult := make([]string, 0, len(values))\n\tseen := make(map[string]struct{}, len(values))\n\tfor _, value := range values {\n\t\ttarget := value\n\t\tif target == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[target]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[target] = struct{}{}\n\t\tresult = append(result, target)\n\t}\n\treturn result\n}\n\nfunc normalizeAPIType(apiType string) string {\n\ttrim := apiType\n\tif trim == \"\" {\n\t\treturn \"openai-completions\"\n\t}\n\treturn trim\n}\n\nfunc isSupportedAPIType(apiType string) bool {\n\tswitch normalizeAPIType(apiType) {\n\tcase \"openai-completions\", \"openai-responses\", \"anthropic-messages\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc isSupportedOllamaAPIType(apiType string) bool {\n\tswitch normalizeAPIType(apiType) {\n\tcase \"openai-completions\", \"openai-responses\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc resolveRuntimeParams(provider, apiType string, maxTokens, contextWindow int) (string, int, int) {\n\tresolvedAPI := normalizeAPIType(apiType)\n\tif provider == \"ollama\" && !isSupportedOllamaAPIType(resolvedAPI) {\n\t\tresolvedAPI = \"openai-responses\"\n\t}\n\tresolvedMaxTokens := maxTokens\n\tresolvedContextWindow := contextWindow\n\tif resolvedMaxTokens <= 0 {\n\t\tswitch provider {\n\t\tcase \"deepseek\":\n\t\t\tresolvedMaxTokens = 8192\n\t\tcase \"zai\":\n\t\t\tresolvedMaxTokens = 131072\n\t\tcase \"openrouter\":\n\t\t\tresolvedMaxTokens = 8192\n\t\tcase \"minimax\", \"kimi-coding\", \"custom\":\n\t\t\tresolvedMaxTokens = 8192\n\t\tdefault:\n\t\t\tresolvedMaxTokens = 8192\n\t\t}\n\t}\n\tif resolvedContextWindow <= 0 {\n\t\tswitch provider {\n\t\tcase \"deepseek\":\n\t\t\tresolvedContextWindow = 128000\n\t\tcase \"zai\":\n\t\t\tresolvedContextWindow = 204800\n\t\tcase \"openrouter\":\n\t\t\tresolvedContextWindow = 128000\n\t\tcase \"minimax\", \"kimi-coding\":\n\t\t\tresolvedContextWindow = 200000\n\t\tcase \"custom\", \"vllm\":\n\t\t\tresolvedContextWindow = 128000\n\t\tdefault:\n\t\t\tresolvedContextWindow = 256000\n\t\t}\n\t}\n\treturn resolvedAPI, resolvedMaxTokens, resolvedContextWindow\n}\n\nfunc generateToken() string {\n\tbytes := make([]byte, 24)\n\tif _, err := rand.Read(bytes); err != nil {\n\t\treturn \"\"\n\t}\n\treturn hex.EncodeToString(bytes)\n}\n\nfunc asyncReportAIProviderInstall(provider string) {\n\tif global.CONF.Base.Mode != \"stable\" {\n\t\treturn\n\t}\n\tprovider = provider\n\tif provider == \"\" {\n\t\treturn\n\t}\n\tgo func(provider string) {\n\t\tquery := url.Values{}\n\t\tquery.Set(\"product\", \"ai-provider\")\n\t\tquery.Set(\"type\", \"install\")\n\t\tquery.Set(\"version\", provider)\n\t\treqURL := \"https://community.fit2cloud.com/installation-analytics?\" + query.Encode()\n\t\t_, _, _ = req_helper.HandleRequest(reqURL, http.MethodGet, constant.TimeOut5s)\n\t}(provider)\n}\n"
  },
  {
    "path": "agent/app/service/ai.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype AIToolService struct{}\n\ntype IAIToolService interface {\n\tSearch(search dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error)\n\tCreate(req dto.OllamaModelName) error\n\tClose(name string) error\n\tRecreate(req dto.OllamaModelName) error\n\tDelete(req dto.ForceDelete) error\n\tSync() ([]dto.OllamaModelDropList, error)\n\tLoadDetail(name string) (string, error)\n\tBindDomain(req dto.OllamaBindDomain) error\n\tGetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error)\n\tUpdateBindDomain(req dto.OllamaBindDomain) error\n}\n\nfunc NewIAIToolService() IAIToolService {\n\treturn &AIToolService{}\n}\n\nfunc (u *AIToolService) Search(req dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error) {\n\tvar options []repo.DBOption\n\tif len(req.Info) != 0 {\n\t\toptions = append(options, repo.WithByLikeName(req.Info))\n\t}\n\ttotal, list, err := aiRepo.Page(req.Page, req.PageSize, options...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar dtoLists []dto.OllamaModelInfo\n\tfor _, itemModel := range list {\n\t\tvar item dto.OllamaModelInfo\n\t\tif err := copier.Copy(&item, &itemModel); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\ttaskModel, _ := taskRepo.GetFirst(taskRepo.WithResourceID(item.ID), repo.WithByType(task.TaskScopeAI))\n\t\tif len(taskModel.ID) != 0 {\n\t\t\titem.LogFileExist = true\n\t\t}\n\t\tdtoLists = append(dtoLists, item)\n\t}\n\treturn total, dtoLists, err\n}\n\nfunc (u *AIToolService) LoadDetail(name string) (string, error) {\n\tif cmd.CheckIllegal(name) {\n\t\treturn \"\", buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcontainerName, err := LoadContainerName()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"docker exec %s ollama show %s\", containerName, name)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn stdout, err\n}\n\nfunc (u *AIToolService) Create(req dto.OllamaModelName) error {\n\tif cmd.CheckIllegal(req.Name) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tmodelInfo, _ := aiRepo.Get(repo.WithByName(req.Name))\n\tif modelInfo.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tcontainerName, err := LoadContainerName()\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo := model.OllamaModel{\n\t\tName:   req.Name,\n\t\tFrom:   \"local\",\n\t\tStatus: constant.StatusWaiting,\n\t}\n\tif err := aiRepo.Create(&info); err != nil {\n\t\treturn err\n\t}\n\ttaskItem, err := task.NewTaskWithOps(fmt.Sprintf(\"ollama-model-%s\", req.Name), task.TaskPull, task.TaskScopeAI, req.TaskID, info.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for exec shell failed, err: %v\", err)\n\t\treturn err\n\t}\n\tgo func() {\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"OllamaModelPull\", req.Name), func(t *task.Task) error {\n\t\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithTimeout(time.Hour))\n\t\t\treturn cmdMgr.Run(\"docker\", \"exec\", containerName, \"ollama\", \"pull\", info.Name)\n\t\t}, nil)\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"OllamaModelSize\", req.Name), func(t *task.Task) error {\n\t\t\titemSize, err := loadModelSize(info.Name, containerName)\n\t\t\tif len(itemSize) != 0 {\n\t\t\t\t_ = aiRepo.Update(info.ID, map[string]interface{}{\"status\": constant.StatusSuccess, \"size\": itemSize})\n\t\t\t} else {\n\t\t\t\t_ = aiRepo.Update(info.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\t\tif err := taskItem.Execute(); err != nil {\n\t\t\t_ = aiRepo.Update(info.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc (u *AIToolService) Close(name string) error {\n\tif cmd.CheckIllegal(name) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcontainerName, err := LoadContainerName()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := cmd.RunDefaultBashCf(\"docker exec %s ollama stop %s\", containerName, name); err != nil {\n\t\treturn fmt.Errorf(\"handle ollama stop %s failed, %v\", name, err)\n\t}\n\treturn nil\n}\n\nfunc (u *AIToolService) Recreate(req dto.OllamaModelName) error {\n\tif cmd.CheckIllegal(req.Name) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tmodelInfo, _ := aiRepo.Get(repo.WithByName(req.Name))\n\tif modelInfo.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tcontainerName, err := LoadContainerName()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := aiRepo.Update(modelInfo.ID, map[string]interface{}{\"status\": constant.StatusWaiting, \"from\": \"local\"}); err != nil {\n\t\treturn err\n\t}\n\ttaskItem, err := task.NewTaskWithOps(fmt.Sprintf(\"ollama-model-%s\", req.Name), task.TaskPull, task.TaskScopeAI, req.TaskID, modelInfo.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for exec shell failed, err: %v\", err)\n\t\treturn err\n\t}\n\tgo func() {\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"OllamaModelPull\", req.Name), func(t *task.Task) error {\n\t\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithTimeout(time.Hour))\n\t\t\treturn cmdMgr.Run(\"docker\", \"exec\", containerName, \"ollama\", \"pull\", req.Name)\n\t\t}, nil)\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"OllamaModelSize\", req.Name), func(t *task.Task) error {\n\t\t\titemSize, err := loadModelSize(modelInfo.Name, containerName)\n\t\t\tif len(itemSize) != 0 {\n\t\t\t\t_ = aiRepo.Update(modelInfo.ID, map[string]interface{}{\"status\": constant.StatusSuccess, \"size\": itemSize})\n\t\t\t} else {\n\t\t\t\t_ = aiRepo.Update(modelInfo.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\t\tif err := taskItem.Execute(); err != nil {\n\t\t\t_ = aiRepo.Update(modelInfo.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc (u *AIToolService) Delete(req dto.ForceDelete) error {\n\tollamaList, _ := aiRepo.List(repo.WithByIDs(req.IDs))\n\tif len(ollamaList) == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tcontainerName, err := LoadContainerName()\n\tif err != nil && !req.ForceDelete {\n\t\treturn err\n\t}\n\tfor _, item := range ollamaList {\n\t\tif item.Status != constant.StatusDeleted {\n\t\t\tif err := cmd.RunDefaultBashCf(\"docker exec %s ollama rm %s\", containerName, item.Name); err != nil && !req.ForceDelete {\n\t\t\t\treturn fmt.Errorf(\"handle ollama rm %s failed, %v\", item.Name, err)\n\t\t\t}\n\t\t}\n\t\t_ = aiRepo.Delete(repo.WithByID(item.ID))\n\t\tlogItem := path.Join(global.Dir.DataDir, \"log\", \"AITools\", item.Name)\n\t\t_ = os.Remove(logItem)\n\t}\n\treturn nil\n}\n\nfunc (u *AIToolService) Sync() ([]dto.OllamaModelDropList, error) {\n\tcontainerName, err := LoadContainerName()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"docker exec %s ollama list\", containerName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar list []model.OllamaModel\n\tlines := strings.Split(stdout, \"\\n\")\n\tfor _, line := range lines {\n\t\tparts := strings.Fields(line)\n\t\tif len(parts) < 5 {\n\t\t\tcontinue\n\t\t}\n\t\tif parts[0] == \"NAME\" {\n\t\t\tcontinue\n\t\t}\n\t\tlist = append(list, model.OllamaModel{Name: parts[0], Size: parts[2] + \" \" + parts[3]})\n\t}\n\tlistInDB, _ := aiRepo.List()\n\tvar dropList []dto.OllamaModelDropList\n\tfor _, itemModel := range listInDB {\n\t\tisExit := false\n\t\tfor i := 0; i < len(list); i++ {\n\t\t\tif list[i].Name == itemModel.Name {\n\t\t\t\t_ = aiRepo.Update(itemModel.ID, map[string]interface{}{\"status\": constant.StatusSuccess, \"message\": \"\", \"size\": list[i].Size})\n\t\t\t\tlist = append(list[:i], list[(i+1):]...)\n\t\t\t\tisExit = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !isExit && itemModel.Status != constant.StatusWaiting {\n\t\t\t_ = aiRepo.Update(itemModel.ID, map[string]interface{}{\"status\": constant.StatusDeleted, \"message\": \"not exist\", \"size\": \"\"})\n\t\t\tdropList = append(dropList, dto.OllamaModelDropList{ID: itemModel.ID, Name: itemModel.Name})\n\t\t\tcontinue\n\t\t}\n\t}\n\tfor _, item := range list {\n\t\titem.Status = constant.StatusSuccess\n\t\titem.From = \"remote\"\n\t\t_ = aiRepo.Create(&item)\n\t}\n\n\treturn dropList, nil\n}\n\nfunc (u *AIToolService) BindDomain(req dto.OllamaBindDomain) error {\n\tnginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)\n\tif nginxInstall.ID == 0 {\n\t\treturn buserr.New(\"ErrOpenrestyInstall\")\n\t}\n\tvar (\n\t\tipList []string\n\t\terr    error\n\t)\n\tif len(req.IPList) > 0 {\n\t\tipList, err = common.HandleIPList(req.IPList)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tcreateWebsiteReq := request.WebsiteCreate{\n\t\tDomains:      []request.WebsiteDomain{{Domain: req.Domain, Port: 80}},\n\t\tAlias:        strings.ToLower(req.Domain),\n\t\tType:         constant.Deployment,\n\t\tAppType:      constant.InstalledApp,\n\t\tAppInstallID: req.AppInstallID,\n\t}\n\tif req.SSLID > 0 {\n\t\tcreateWebsiteReq.WebsiteSSLID = req.SSLID\n\t\tcreateWebsiteReq.EnableSSL = true\n\t}\n\tres, _ := NewIGroupService().GetDefault()\n\tcreateWebsiteReq.WebsiteGroupID = res.ID\n\twebsiteService := NewIWebsiteService()\n\tif err = websiteService.CreateWebsite(createWebsiteReq); err != nil {\n\t\treturn err\n\t}\n\twebsite, err := websiteRepo.GetFirst(websiteRepo.WithAlias(strings.ToLower(req.Domain)))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(ipList) > 0 {\n\t\tif err = ConfigAllowIPs(ipList, website); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err = ConfigAIProxy(website); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *AIToolService) GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error) {\n\tinstall, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres := &dto.OllamaBindDomainRes{}\n\twebsite, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(install.ID))\n\tif website.ID == 0 {\n\t\treturn res, nil\n\t}\n\tres.WebsiteID = website.ID\n\tres.Domain = website.PrimaryDomain\n\tif website.WebsiteSSLID > 0 {\n\t\tres.SSLID = website.WebsiteSSLID\n\t\tssl, _ := websiteSSLRepo.GetFirst(repo.WithByID(website.WebsiteSSLID))\n\t\tres.AcmeAccountID = ssl.AcmeAccountID\n\t}\n\tres.ConnUrl = fmt.Sprintf(\"%s://%s\", strings.ToLower(website.Protocol), website.PrimaryDomain)\n\tres.AllowIPs = GetAllowIps(website)\n\treturn res, nil\n}\n\nfunc (u *AIToolService) UpdateBindDomain(req dto.OllamaBindDomain) error {\n\tnginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)\n\tif nginxInstall.ID == 0 {\n\t\treturn buserr.New(\"ErrOpenrestyInstall\")\n\t}\n\tvar (\n\t\tipList []string\n\t\terr    error\n\t)\n\tif len(req.IPList) > 0 {\n\t\tipList, err = common.HandleIPList(req.IPList)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\twebsiteService := NewIWebsiteService()\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = ConfigAllowIPs(ipList, website); err != nil {\n\t\treturn err\n\t}\n\tif req.SSLID > 0 {\n\t\tsslReq := request.WebsiteHTTPSOp{\n\t\t\tWebsiteID:    website.ID,\n\t\t\tEnable:       true,\n\t\t\tType:         constant.SSLExisted,\n\t\t\tWebsiteSSLID: req.SSLID,\n\t\t\tHttpConfig:   constant.HTTPToHTTPS,\n\t\t}\n\t\tif _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\tif website.WebsiteSSLID > 0 && req.SSLID == 0 {\n\t\tsslReq := request.WebsiteHTTPSOp{\n\t\t\tWebsiteID: website.ID,\n\t\t\tEnable:    false,\n\t\t}\n\t\tif _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc LoadContainerName() (string, error) {\n\tollamaBaseInfo, err := appInstallRepo.LoadBaseInfo(\"ollama\", \"\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"ollama service is not found, err: %v\", err)\n\t}\n\tif ollamaBaseInfo.Status != constant.StatusRunning {\n\t\treturn \"\", fmt.Errorf(\"container %s of ollama is not running, please check and retry!\", ollamaBaseInfo.ContainerName)\n\t}\n\treturn ollamaBaseInfo.ContainerName, nil\n}\n\nfunc loadModelSize(name string, containerName string) (string, error) {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"docker exec %s ollama list | grep %s\", containerName, name)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tlines := strings.Split(stdout, \"\\n\")\n\tfor _, line := range lines {\n\t\tparts := strings.Fields(line)\n\t\tif len(parts) < 5 {\n\t\t\tcontinue\n\t\t}\n\t\treturn parts[2] + \" \" + parts[3], nil\n\t}\n\treturn \"\", fmt.Errorf(\"no such model %s in ollama list, std: %s\", name, stdout)\n}\n"
  },
  {
    "path": "agent/app/service/alert.go",
    "content": "package service\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/copier\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/email\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"github.com/shirou/gopsutil/v4/disk\"\n\t\"mime\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype AlertService struct{}\n\ntype IAlertService interface {\n\tPageAlert(req dto.AlertSearch) (int64, []dto.AlertDTO, error)\n\tGetAlerts() ([]dto.AlertDTO, error)\n\tCreateAlert(create dto.AlertCreate) error\n\tUpdateAlert(req dto.AlertUpdate) error\n\tDeleteAlert(id uint) error\n\tGetAlert(id uint) (dto.AlertDTO, error)\n\tUpdateStatus(id uint, status string) error\n\tExternalUpdateAlert(req dto.AlertCreate) error\n\n\tGetDisks() ([]dto.DiskDTO, error)\n\tPageAlertLogs(req dto.AlertLogSearch) (int64, []dto.AlertLogDTO, error)\n\tCleanAlertLogs() error\n\tGetClams() ([]dto.ClamDTO, error)\n\tGetCronJobs(req dto.CronJobReq) ([]dto.CronJobDTO, error)\n\n\tGetAlertConfig() ([]model.AlertConfig, error)\n\tUpdateAlertConfig(req dto.AlertConfigUpdate) error\n\tDeleteAlertConfig(id uint) error\n\tTestAlertConfig(req dto.AlertConfigTest) (bool, error)\n}\n\nfunc NewIAlertService() IAlertService {\n\treturn &AlertService{}\n}\n\nfunc (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO, error) {\n\tvar (\n\t\topts   []repo.DBOption\n\t\tresult []dto.AlertDTO\n\t)\n\tif search.Status != \"\" {\n\t\topts = append(opts, repo.WithByStatus(search.Status))\n\t}\n\tif search.Type != \"\" {\n\t\topts = append(opts, alertRepo.WithByType(search.Type))\n\t}\n\topts = append(opts, repo.WithOrderDesc(\"created_at\"))\n\n\ttotal, alerts, err := alertRepo.Page(search.Page, search.PageSize, opts...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tfor _, item := range alerts {\n\n\t\tresult = append(result, dto.AlertDTO{\n\t\t\tID:             item.ID,\n\t\t\tType:           item.Type,\n\t\t\tCycle:          item.Cycle,\n\t\t\tCount:          item.Count,\n\t\t\tMethod:         item.Method,\n\t\t\tTitle:          item.Title,\n\t\t\tProject:        item.Project,\n\t\t\tStatus:         item.Status,\n\t\t\tSendCount:      item.SendCount,\n\t\t\tAdvancedParams: item.AdvancedParams,\n\t\t\tCreatedAt:      item.CreatedAt,\n\t\t\tUpdatedAt:      item.UpdatedAt,\n\t\t})\n\t}\n\n\treturn total, result, err\n}\n\nfunc (a AlertService) GetAlerts() ([]dto.AlertDTO, error) {\n\tvar (\n\t\topts   []repo.DBOption\n\t\tresult []dto.AlertDTO\n\t)\n\topts = append(opts, repo.WithByStatus(constant.AlertEnable))\n\talerts, err := alertRepo.List(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, item := range alerts {\n\n\t\tresult = append(result, dto.AlertDTO{\n\t\t\tID:             item.ID,\n\t\t\tType:           item.Type,\n\t\t\tCycle:          item.Cycle,\n\t\t\tCount:          item.Count,\n\t\t\tMethod:         item.Method,\n\t\t\tTitle:          item.Title,\n\t\t\tProject:        item.Project,\n\t\t\tStatus:         item.Status,\n\t\t\tSendCount:      item.SendCount,\n\t\t\tAdvancedParams: item.AdvancedParams,\n\t\t\tCreatedAt:      item.CreatedAt,\n\t\t\tUpdatedAt:      item.UpdatedAt,\n\t\t})\n\t}\n\n\treturn result, err\n}\n\nfunc (a AlertService) CreateAlert(create dto.AlertCreate) error {\n\tvar alertID uint\n\tvar alertInfo model.Alert\n\tif create.Project != \"\" {\n\t\talertInfo, _ := alertRepo.Get(alertRepo.WithByType(create.Type), alertRepo.WithByProject(create.Project))\n\t\talertID = alertInfo.ID\n\t} else {\n\t\talertInfo, _ := alertRepo.Get(alertRepo.WithByType(create.Type))\n\t\talertID = alertInfo.ID\n\t}\n\n\tif alertID != 0 {\n\t\tvar upAlert dto.AlertUpdate\n\t\tif err := copier.Copy(&upAlert, &create); err != nil {\n\t\t\treturn buserr.WithErr(\"ErrStructTransform\", err)\n\t\t}\n\t\tupAlert.ID = alertID\n\t\terr := a.UpdateAlert(upAlert)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\talertInfo.Status = constant.AlertEnable\n\t\tif err := copier.Copy(&alertInfo, &create); err != nil {\n\t\t\treturn buserr.WithErr(\"ErrStructTransform\", err)\n\t\t}\n\n\t\tif err := alertRepo.Create(&alertInfo); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tNewIAlertTaskHelper().InitTask(alertInfo.Type)\n\t}\n\n\treturn nil\n}\n\nfunc (a AlertService) UpdateAlert(req dto.AlertUpdate) error {\n\n\tupMap := make(map[string]interface{})\n\tupMap[\"id\"] = req.ID\n\tupMap[\"type\"] = req.Type\n\tupMap[\"cycle\"] = req.Cycle\n\tupMap[\"count\"] = req.Count\n\tupMap[\"method\"] = req.Method\n\tupMap[\"title\"] = req.Title\n\tupMap[\"project\"] = req.Project\n\tupMap[\"status\"] = req.Status\n\tupMap[\"send_count\"] = req.SendCount\n\tupMap[\"advanced_params\"] = req.AdvancedParams\n\n\tif err := alertRepo.Update(upMap, repo.WithByID(req.ID)); err != nil {\n\t\treturn err\n\t}\n\tNewIAlertTaskHelper().InitTask(req.Type)\n\treturn nil\n}\n\nfunc (a AlertService) DeleteAlert(id uint) error {\n\talertInfo, _ := alertRepo.Get(repo.WithByID(id))\n\tif alertInfo.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\terr := alertRepo.Delete(repo.WithByID(id))\n\tif err != nil {\n\t\treturn err\n\t}\n\talerts, err := a.GetAlerts()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(alerts) > 0 {\n\t\tNewIAlertTaskHelper().InitTask(alertInfo.Type)\n\t} else {\n\t\tNewIAlertTaskHelper().StopTask()\n\t}\n\treturn nil\n}\n\nfunc (a AlertService) GetAlert(id uint) (dto.AlertDTO, error) {\n\tvar res dto.AlertDTO\n\talertInfo, err := alertRepo.Get(repo.WithByID(id))\n\tif err != nil {\n\t\treturn res, err\n\t}\n\t_ = copier.Copy(&res, &alertInfo)\n\treturn res, nil\n}\n\nfunc (a AlertService) UpdateStatus(id uint, status string) error {\n\talertInfo, _ := alertRepo.Get(repo.WithByID(id))\n\tif alertInfo.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\terr := alertRepo.Update(map[string]interface{}{\"status\": status}, repo.WithByID(alertInfo.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\talerts, err := a.GetAlerts()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(alerts) > 0 {\n\t\tNewIAlertTaskHelper().InitTask(alertInfo.Type)\n\t} else {\n\t\tNewIAlertTaskHelper().StopTask()\n\t}\n\treturn nil\n}\n\nfunc (a AlertService) GetDisks() ([]dto.DiskDTO, error) {\n\tvar disks []dto.DiskDTO\n\texcludes := map[string]struct{}{\n\t\t\"/mnt/cdrom\": {}, \"/boot\": {}, \"/boot/efi\": {}, \"/dev\": {}, \"/dev/shm\": {},\n\t\t\"/run/lock\": {}, \"/run\": {}, \"/run/shm\": {}, \"/run/user\": {},\n\t}\n\tstdout, err := executeDiskCommand()\n\tif err != nil {\n\t\treturn disks, nil\n\t}\n\n\tlines := strings.Split(stdout, \"\\n\")\n\tvar mounts []dto.AlertDiskInfo\n\n\tfor _, line := range lines {\n\t\tfields := strings.Fields(line)\n\t\tif len(fields) < 7 {\n\t\t\tcontinue\n\t\t}\n\t\tmountPoint := strings.Join(fields[6:], \" \")\n\t\tif shouldExclude(fields, mountPoint, excludes) {\n\t\t\tcontinue\n\t\t}\n\t\tmounts = append(mounts, dto.AlertDiskInfo{Type: fields[1], Device: fields[0], Mount: mountPoint})\n\n\t}\n\n\tvar (\n\t\twg sync.WaitGroup\n\t\tmu sync.Mutex\n\t)\n\twg.Add(len(mounts))\n\tfor i := 0; i < len(mounts); i++ {\n\t\tgo func(timeoutCh <-chan time.Time, mount dto.AlertDiskInfo) {\n\t\t\tdefer wg.Done()\n\n\t\t\tvar itemData dto.DiskDTO\n\t\t\titemData.Path = mount.Mount\n\t\t\titemData.Type = mount.Type\n\t\t\titemData.Device = mount.Device\n\t\t\tselect {\n\t\t\tcase <-timeoutCh:\n\t\t\t\tmu.Lock()\n\t\t\t\tdisks = append(disks, itemData)\n\t\t\t\tmu.Unlock()\n\t\t\t\tglobal.LOG.Errorf(\"load disk info from %s failed, err: timeout\", mount.Mount)\n\t\t\tdefault:\n\t\t\t\tstate, err := disk.Usage(mount.Mount)\n\t\t\t\tif err != nil {\n\t\t\t\t\tmu.Lock()\n\t\t\t\t\tdisks = append(disks, itemData)\n\t\t\t\t\tmu.Unlock()\n\t\t\t\t\tglobal.LOG.Errorf(\"load disk info from %s failed, err: %v\", mount.Mount, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\titemData.Total = state.Total\n\t\t\t\titemData.Free = state.Free\n\t\t\t\titemData.Used = state.Used\n\t\t\t\titemData.UsedPercent = state.UsedPercent\n\t\t\t\titemData.InodesTotal = state.InodesTotal\n\t\t\t\titemData.InodesUsed = state.InodesUsed\n\t\t\t\titemData.InodesFree = state.InodesFree\n\t\t\t\titemData.InodesUsedPercent = state.InodesUsedPercent\n\t\t\t\tmu.Lock()\n\t\t\t\tdisks = append(disks, itemData)\n\t\t\t\tmu.Unlock()\n\t\t\t}\n\t\t}(time.After(5*time.Second), mounts[i])\n\t}\n\twg.Wait()\n\n\tsort.Slice(disks, func(i, j int) bool {\n\t\treturn disks[i].Path < disks[j].Path\n\t})\n\treturn disks, nil\n}\n\nfunc executeDiskCommand() (string, error) {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))\n\tstdout, err := cmdMgr.RunWithStdoutBashC(\"df -hT -P | grep '/' | grep -v tmpfs | grep -v 'snap/core' | grep -v udev\")\n\tif err != nil {\n\t\tcmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))\n\t\tstdout, err = cmdMgr2.RunWithStdoutBashC(\"df -lhT -P | grep '/' | grep -v tmpfs | grep -v 'snap/core' | grep -v udev\")\n\t}\n\treturn stdout, err\n}\n\nfunc shouldExclude(fields []string, mountPoint string, excludes map[string]struct{}) bool {\n\tif strings.HasPrefix(mountPoint, \"/snap\") || len(strings.Split(mountPoint, \"/\")) > 10 {\n\t\treturn true\n\t}\n\tif strings.TrimSpace(fields[1]) == \"tmpfs\" {\n\t\treturn true\n\t}\n\tif strings.Contains(fields[2], \"K\") {\n\t\treturn true\n\t}\n\tif strings.Contains(mountPoint, \"docker\") {\n\t\treturn true\n\t}\n\t_, excluded := excludes[mountPoint]\n\treturn excluded\n}\n\nfunc (a AlertService) PageAlertLogs(search dto.AlertLogSearch) (int64, []dto.AlertLogDTO, error) {\n\tvar (\n\t\topts   []repo.DBOption\n\t\tresult []dto.AlertLogDTO\n\t)\n\tif search.Status != \"\" {\n\t\topts = append(opts, repo.WithByStatus(search.Status))\n\t}\n\tif search.Count != 0 {\n\t\topts = append(opts, alertRepo.WithByCount(search.Count))\n\t}\n\topts = append(opts, repo.WithOrderDesc(\"created_at\"))\n\n\ttotal, alerts, err := alertRepo.PageLog(search.Page, search.PageSize, opts...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tfor _, item := range alerts {\n\t\talertLogDTO, err := a.parseAlertLog(item)\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tresult = append(result, alertLogDTO)\n\t}\n\n\treturn total, result, err\n}\n\nfunc (a AlertService) parseAlertLog(item model.AlertLog) (dto.AlertLogDTO, error) {\n\tvar alertDetail dto.AlertDetail\n\tvar alertRule dto.AlertRule\n\n\tif err := unmarshalAlertInfo(item.AlertDetail, &alertDetail); err != nil {\n\t\treturn dto.AlertLogDTO{}, err\n\t}\n\tif err := unmarshalAlertInfo(item.AlertRule, &alertRule); err != nil {\n\t\treturn dto.AlertLogDTO{}, err\n\t}\n\treturn dto.AlertLogDTO{\n\t\tID:          item.ID,\n\t\tCount:       item.Count,\n\t\tType:        item.Type,\n\t\tStatus:      item.Status,\n\t\tMethod:      item.Method,\n\t\tMessage:     item.Message,\n\t\tAlertId:     item.AlertId,\n\t\tAlertDetail: alertDetail,\n\t\tAlertRule:   alertRule,\n\t\tCreatedAt:   item.CreatedAt,\n\t\tUpdatedAt:   item.UpdatedAt,\n\t}, nil\n}\n\nfunc unmarshalAlertInfo(data string, v interface{}) error {\n\tif err := json.Unmarshal([]byte(data), v); err != nil {\n\t\treturn fmt.Errorf(\"unmarshal alert info vars failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (a AlertService) CleanAlertLogs() error {\n\treturn alertRepo.CleanAlertLogs()\n}\n\nfunc (a AlertService) GetClams() ([]dto.ClamDTO, error) {\n\tvar clams []dto.ClamDTO\n\tclamList, err := clamRepo.List()\n\n\tfor _, clam := range clamList {\n\t\tvar clamDTO dto.ClamDTO\n\t\tclamDTO.ID = clam.ID\n\t\tclamDTO.Name = clam.Name\n\t\tclamDTO.Path = clam.Path\n\t\tclamDTO.Status = clam.Status\n\t\tclamDTO.UpdatedAt = clam.UpdatedAt\n\t\tclamDTO.CreatedAt = clam.CreatedAt\n\t\tclams = append(clams, clamDTO)\n\t}\n\treturn clams, err\n}\n\nfunc (a AlertService) GetCronJobs(req dto.CronJobReq) ([]dto.CronJobDTO, error) {\n\tvar cronJobs []dto.CronJobDTO\n\tvar (\n\t\topts []repo.DBOption\n\t)\n\tif req.Status != \"\" {\n\t\topts = append(opts, repo.WithByStatus(req.Status))\n\t}\n\tif req.Type != \"\" {\n\t\topts = append(opts, repo.WithByType(req.Type))\n\t}\n\tcronjobList, err := cronjobRepo.List(opts...)\n\n\tfor _, cronJob := range cronjobList {\n\t\tvar cronJobDTO dto.CronJobDTO\n\t\tcronJobDTO.ID = cronJob.ID\n\t\tcronJobDTO.Name = cronJob.Name\n\t\tcronJobDTO.Status = cronJob.Status\n\t\tcronJobDTO.Type = cronJob.Type\n\t\tcronJobDTO.UpdatedAt = cronJob.UpdatedAt\n\t\tcronJobDTO.CreatedAt = cronJob.CreatedAt\n\t\tcronJobs = append(cronJobs, cronJobDTO)\n\t}\n\treturn cronJobs, err\n}\n\nfunc (a AlertService) GetAlertConfig() ([]model.AlertConfig, error) {\n\tvar (\n\t\topts    []repo.DBOption\n\t\tconfigs []model.AlertConfig\n\t)\n\topts = append(opts, repo.WithByStatus(constant.AlertEnable))\n\tconfigs, err := alertRepo.AlertConfigList(opts...)\n\treturn configs, err\n}\n\nfunc (a AlertService) UpdateAlertConfig(req dto.AlertConfigUpdate) error {\n\tif req.ID != 0 {\n\t\tupMap := make(map[string]interface{})\n\t\tupMap[\"id\"] = req.ID\n\t\tupMap[\"type\"] = req.Type\n\t\tupMap[\"title\"] = req.Title\n\t\tupMap[\"status\"] = req.Status\n\t\tupMap[\"config\"] = req.Config\n\t\tif err := alertRepo.UpdateAlertConfig(upMap, repo.WithByID(req.ID)); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tvar alertConfig model.AlertConfig\n\t\tif err := copier.Copy(&alertConfig, &req); err != nil {\n\t\t\treturn buserr.WithErr(\"ErrStructTransform\", err)\n\t\t}\n\t\tif err := alertRepo.CreateAlertConfig(&alertConfig); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (a AlertService) DeleteAlertConfig(id uint) error {\n\treturn alertRepo.DeleteAlertConfig(repo.WithByID(id))\n}\n\nfunc (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {\n\tusername := req.UserName\n\tif username == \"\" {\n\t\tusername = req.Sender\n\t}\n\tencodedDisplayName := mime.BEncoding.Encode(\"UTF-8\", req.DisplayName)\n\tcfg := email.SMTPConfig{\n\t\tHost:       req.Host,\n\t\tPort:       req.Port,\n\t\tSender:     req.Sender,\n\t\tUsername:   username,\n\t\tPassword:   req.Password,\n\t\tFrom:       fmt.Sprintf(`\"%s\" <%s>`, encodedDisplayName, req.Sender),\n\t\tEncryption: req.Encryption,\n\t\tRecipient:  req.Recipient,\n\t}\n\n\tmsg := email.EmailMessage{\n\t\tSubject: i18n.GetMsgByKey(\"TestAlertTitle\"),\n\t\tBody:    i18n.GetMsgByKey(\"TestAlert\"),\n\t\tIsHTML:  false,\n\t}\n\ttransport := xpack.LoadRequestTransport()\n\tif err := email.SendMail(cfg, msg, transport); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate) error {\n\tupMap := make(map[string]interface{})\n\tvar newStatus string\n\tif updateAlert.SendCount == 0 {\n\t\tnewStatus = constant.AlertDisable\n\t} else {\n\t\tnewStatus = constant.AlertEnable\n\t\tupMap[\"send_count\"] = updateAlert.SendCount\n\t\tif updateAlert.Method != \"\" {\n\t\t\tupMap[\"method\"] = updateAlert.Method\n\t\t}\n\t}\n\tupMap[\"status\"] = newStatus\n\n\talertInfo, _ := alertRepo.Get(\n\t\talertRepo.WithByType(updateAlert.Type),\n\t\talertRepo.WithByProject(updateAlert.Project),\n\t)\n\n\tif alertInfo.ID > 0 {\n\t\tshouldUpdate := false\n\n\t\tif alertInfo.Status != newStatus {\n\t\t\tshouldUpdate = true\n\t\t}\n\t\tif val, ok := upMap[\"send_count\"]; ok && val != alertInfo.SendCount {\n\t\t\tshouldUpdate = true\n\t\t}\n\t\tif val, ok := upMap[\"method\"]; ok && val != \"\" && val != alertInfo.Method {\n\t\t\tshouldUpdate = true\n\t\t}\n\n\t\tif shouldUpdate {\n\t\t\tif err := alertRepo.Update(\n\t\t\t\tupMap,\n\t\t\t\talertRepo.WithByProject(updateAlert.Project),\n\t\t\t\talertRepo.WithByType(updateAlert.Type),\n\t\t\t); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif updateAlert.Method != \"\" && updateAlert.Title != \"\" {\n\t\t\tupdateAlert.Status = newStatus\n\t\t\tif err := a.CreateAlert(updateAlert); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/alert_helper.go",
    "content": "package service\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\talertUtil \"github.com/1Panel-dev/1Panel/agent/utils/alert\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/psutil\"\n\tversionUtil \"github.com/1Panel-dev/1Panel/agent/utils/version\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"github.com/shirou/gopsutil/v4/cpu\"\n\t\"github.com/shirou/gopsutil/v4/disk\"\n\t\"github.com/shirou/gopsutil/v4/load\"\n\t\"github.com/shirou/gopsutil/v4/mem\"\n\t\"github.com/shirou/gopsutil/v4/net\"\n)\n\nconst (\n\tResourceAlertInterval = 30\n\tCheckIntervalSec      = 3\n\tLoadCheckIntervalMin  = 5\n)\n\ntype AlertTaskHelper struct {\n\tDiskIO chan []disk.IOCountersStat\n\tNetIO  chan []net.IOCountersStat\n}\n\ntype IAlertTaskHelper interface {\n\tStopTask()\n\tStartTask()\n\tResetTask()\n\tInitTask(alertType string)\n}\n\nvar cpuLoad1, cpuLoad5, cpuLoad15 []float64\nvar memoryLoad1, memoryLoad5, memoryLoad15 []float64\n\nvar baseTypes = map[string]bool{\"ssl\": true, \"siteEndTime\": true, \"panelPwdEndTime\": true, \"panelUpdate\": true}\nvar resourceTypes = map[string]bool{\"cpu\": true, \"memory\": true, \"disk\": true, \"load\": true, \"panelLogin\": true, \"sshLogin\": true, \"nodeException\": true, \"licenseException\": true}\n\nfunc NewIAlertTaskHelper() IAlertTaskHelper {\n\treturn &AlertTaskHelper{\n\t\tDiskIO: make(chan []disk.IOCountersStat, 1),\n\t\tNetIO:  make(chan []net.IOCountersStat, 1),\n\t}\n}\nfunc (m *AlertTaskHelper) StartTask() {\n\tbaseAlert, resourceAlert := m.getClassifiedAlerts()\n\tif len(baseAlert) == 0 && len(resourceAlert) == 0 {\n\t\treturn\n\t}\n\thandleBaseAlerts(baseAlert)\n\thandleResourceAlerts(resourceAlert)\n}\n\nfunc (m *AlertTaskHelper) StopTask() {\n\tstopBaseJob()\n\tstopResourceJob()\n}\n\nfunc (m *AlertTaskHelper) ResetTask() {\n\tm.StopTask()\n\tm.StartTask()\n}\n\nfunc (m *AlertTaskHelper) InitTask(alertType string) {\n\tresetAlertState(alertType)\n\tif baseTypes[alertType] {\n\t\tstopBaseJob()\n\t} else if resourceTypes[alertType] {\n\t\tstopResourceJob()\n\t}\n\tm.StartTask()\n}\n\nfunc resetAlertState(alertType string) {\n\tswitch alertType {\n\tcase \"cpu\":\n\t\tcpuLoad1 = []float64{}\n\t\tcpuLoad5 = []float64{}\n\t\tcpuLoad15 = []float64{}\n\tcase \"memory\":\n\t\tmemoryLoad1 = []float64{}\n\t\tmemoryLoad5 = []float64{}\n\t\tmemoryLoad15 = []float64{}\n\t}\n}\n\nfunc (m *AlertTaskHelper) getClassifiedAlerts() (baseAlerts, resourceAlerts []dto.AlertDTO) {\n\talertList, _ := NewIAlertService().GetAlerts()\n\tfor _, alert := range alertList {\n\t\tif baseTypes[alert.Type] {\n\t\t\tbaseAlerts = append(baseAlerts, alert)\n\t\t} else if resourceTypes[alert.Type] {\n\t\t\tresourceAlerts = append(resourceAlerts, alert)\n\t\t}\n\t}\n\treturn\n}\n\nfunc handleBaseAlerts(baseAlerts []dto.AlertDTO) {\n\tif len(baseAlerts) == 0 {\n\t\tstopBaseJob()\n\t\treturn\n\t}\n\tif global.AlertBaseJobID == 0 {\n\t\tbaseTask(baseAlerts)\n\t\tjobID, err := global.Cron.AddFunc(\"*/30 * * * *\", func() {\n\t\t\tbaseTask(baseAlerts)\n\t\t})\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"alert base job start failed: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tglobal.AlertBaseJobID = jobID\n\t\tglobal.LOG.Info(\"start alert base job\")\n\t}\n}\n\nfunc handleResourceAlerts(resourceAlerts []dto.AlertDTO) {\n\tif len(resourceAlerts) == 0 {\n\t\tstopResourceJob()\n\t\treturn\n\t}\n\tif global.AlertResourceJobID == 0 {\n\t\tjobID, err := global.Cron.AddFunc(\"*/1 * * * *\", func() {\n\t\t\tresourceTask(resourceAlerts)\n\t\t})\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"alert resource job start failed: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tglobal.AlertResourceJobID = jobID\n\t\tglobal.LOG.Info(\"start alert resource job\")\n\t}\n}\n\nfunc stopBaseJob() {\n\tif global.AlertBaseJobID != 0 {\n\t\tglobal.Cron.Remove(global.AlertBaseJobID)\n\t\tglobal.AlertBaseJobID = 0\n\t\tglobal.LOG.Info(\"stop alert base job\")\n\t}\n}\n\nfunc stopResourceJob() {\n\tif global.AlertResourceJobID != 0 {\n\t\tglobal.Cron.Remove(global.AlertResourceJobID)\n\t\tglobal.AlertResourceJobID = 0\n\t\tglobal.LOG.Info(\"stop alert resource job\")\n\t}\n}\n\nfunc baseTask(baseAlert []dto.AlertDTO) {\n\tfor _, alert := range baseAlert {\n\t\tif !alertUtil.CheckSendTimeRange(alert.Type) {\n\t\t\tcontinue\n\t\t}\n\t\tswitch alert.Type {\n\t\tcase \"ssl\":\n\t\t\tloadSSLInfo(alert)\n\t\tcase \"siteEndTime\":\n\t\t\tloadWebsiteInfo(alert)\n\t\tcase \"panelPwdEndTime\":\n\t\t\tif global.IsMaster {\n\t\t\t\tloadPanelPwd(alert)\n\t\t\t}\n\t\tcase \"panelUpdate\":\n\t\t\tif global.IsMaster {\n\t\t\t\tloadPanelUpdate(alert)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc resourceTask(resourceAlert []dto.AlertDTO) {\n\tminute := time.Now().Minute()\n\tfor _, alert := range resourceAlert {\n\t\tif !alertUtil.CheckSendTimeRange(alert.Type) {\n\t\t\tcontinue\n\t\t}\n\t\texecute := minute%LoadCheckIntervalMin == 0\n\t\tswitch alert.Type {\n\t\tcase \"cpu\":\n\t\t\tloadCPUUsage(alert)\n\t\tcase \"memory\":\n\t\t\tloadMemUsage(alert)\n\t\tcase \"load\":\n\t\t\tloadLoadInfo(alert)\n\t\tcase \"disk\":\n\t\t\tloadDiskUsage(alert)\n\t\tcase \"panelLogin\":\n\t\t\tloadPanelLogin(alert)\n\t\tcase \"sshLogin\":\n\t\t\tloadSSHLogin(alert)\n\t\tcase \"nodeException\":\n\t\t\tif execute && global.IsMaster {\n\t\t\t\tloadNodeException(alert)\n\t\t\t}\n\t\tcase \"licenseException\":\n\t\t\tif execute && global.IsMaster {\n\t\t\t\tloadLicenseException(alert)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc loadSSLInfo(alert dto.AlertDTO) {\n\topts := getRepoOptionsByProject(alert.Project)\n\tsslList, _ := repo.NewISSLRepo().List(opts...)\n\tif len(sslList) == 0 {\n\t\treturn\n\t}\n\tdaysDiffMap, projectMap := calculateSSLExpiryDays(sslList, alert.Cycle)\n\tprojectJSON := serializeAndSortProjects(projectMap)\n\tif projectJSON == \"\" || len(daysDiffMap) == 0 {\n\t\treturn\n\t}\n\tsender := NewAlertSender(alert, projectJSON)\n\tminDays := math.MaxInt\n\tmaxDays := 0\n\tallDomains := make([]string, 0)\n\tfor daysDiff, domains := range daysDiffMap {\n\t\tallDomains = append(allDomains, domains...)\n\t\tif daysDiff < minDays {\n\t\t\tminDays = daysDiff\n\t\t}\n\t\tif daysDiff > maxDays {\n\t\t\tmaxDays = daysDiff\n\t\t}\n\t}\n\tif len(allDomains) == 0 {\n\t\treturn\n\t}\n\tvar daysStr string\n\tif len(allDomains) == 1 {\n\t\tdaysStr = strconv.Itoa(minDays)\n\t} else {\n\t\tdaysStr = strconv.Itoa(minDays) + \"-\" + strconv.Itoa(maxDays)\n\t}\n\tdomainStr := strings.Join(allDomains, \",\")\n\tparams := createAlertBaseParams(strconv.Itoa(len(daysDiffMap)), daysStr)\n\tsender.Send(domainStr, params)\n}\n\nfunc loadWebsiteInfo(alert dto.AlertDTO) {\n\topts := getRepoOptionsByProject(alert.Project)\n\twebsiteList, _ := websiteRepo.List(opts...)\n\tif len(websiteList) == 0 {\n\t\treturn\n\t}\n\n\tdaysDiffMap, projectMap := calculateWebsiteExpiryDays(websiteList, alert.Cycle)\n\tprojectJSON := serializeAndSortProjects(projectMap)\n\tif projectJSON == \"\" || len(daysDiffMap) == 0 {\n\t\treturn\n\t}\n\tsender := NewAlertSender(alert, projectJSON)\n\tminDays := math.MaxInt\n\tmaxDays := 0\n\tallDomains := make([]string, 0)\n\tfor daysDiff, domains := range daysDiffMap {\n\t\tallDomains = append(allDomains, domains...)\n\t\tif daysDiff < minDays {\n\t\t\tminDays = daysDiff\n\t\t}\n\t\tif daysDiff > maxDays {\n\t\t\tmaxDays = daysDiff\n\t\t}\n\t}\n\tif len(allDomains) == 0 {\n\t\treturn\n\t}\n\tvar daysStr string\n\tif len(allDomains) == 1 {\n\t\tdaysStr = strconv.Itoa(minDays)\n\t} else {\n\t\tdaysStr = strconv.Itoa(minDays) + \"-\" + strconv.Itoa(maxDays)\n\t}\n\tdomainStr := strings.Join(allDomains, \",\")\n\tparams := createAlertBaseParams(strconv.Itoa(len(daysDiffMap)), daysStr)\n\tsender.Send(domainStr, params)\n}\n\nfunc loadPanelPwd(alert dto.AlertDTO) {\n\t// only master alert\n\texpDays, err := getSettingValue(\"ExpirationDays\")\n\tif err != nil || expDays == \"0\" {\n\t\tglobal.LOG.Info(\"panel password expiration setting not enabled, skip\")\n\t\treturn\n\t}\n\n\texpTimeStr, err := getSettingValue(\"ExpirationTime\")\n\tif err != nil {\n\t\treturn\n\t}\n\texpTime, _ := time.Parse(constant.DateTimeLayout, expTimeStr)\n\tdaysDiff := calculateDaysDifference(expTime)\n\tif daysDiff >= 0 && int(alert.Cycle) >= daysDiff {\n\t\tparams := createAlertPwdParams(strconv.Itoa(daysDiff))\n\t\tsender := NewAlertSender(alert, expTimeStr)\n\t\tsender.Send(strconv.Itoa(daysDiff), params)\n\t}\n}\n\nfunc loadPanelUpdate(alert dto.AlertDTO) {\n\t// only master alert\n\tinfo, err := versionUtil.GetUpgradeVersionInfo()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting version info: %s\", err)\n\t\treturn\n\t}\n\n\tversion := getValidVersion(info)\n\tif version == \"\" {\n\t\treturn\n\t}\n\n\tsender := NewAlertSender(alert, version)\n\tsender.Send(version, []dto.Param{})\n}\n\n// 获取 CPU 使用率数据并发送到通道\nfunc loadCPUUsage(alert dto.AlertDTO) {\n\tpercent, err := cpu.Percent(time.Duration(CheckIntervalSec)*time.Second, false)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting cpu usage, err: %v\", err)\n\t\treturn\n\t}\n\n\tif len(percent) > 0 {\n\t\tvar usageLoad *[]float64\n\t\tvar threshold int\n\n\t\tswitch alert.Cycle {\n\t\tcase 1:\n\t\t\tusageLoad = &cpuLoad1\n\t\t\tthreshold = 1\n\t\tcase 5:\n\t\t\tusageLoad = &cpuLoad5\n\t\t\tthreshold = 5\n\t\tcase 15:\n\t\t\tusageLoad = &cpuLoad15\n\t\t\tthreshold = 15\n\t\t}\n\t\tshouldSendResourceAlert(alert, percent[0], usageLoad, threshold)\n\t}\n}\n\n// 获取内存使用情况数据并发送到通道\nfunc loadMemUsage(alert dto.AlertDTO) {\n\tmemStat, err := mem.VirtualMemory()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting memory usage, err: %v\", err)\n\t\treturn\n\t}\n\n\tpercent := memStat.UsedPercent\n\tvar memoryLoad *[]float64\n\tvar threshold int\n\n\tswitch alert.Cycle {\n\tcase 1:\n\t\tmemoryLoad = &memoryLoad1\n\t\tthreshold = 1\n\tcase 5:\n\t\tmemoryLoad = &memoryLoad5\n\t\tthreshold = 5\n\tcase 15:\n\t\tmemoryLoad = &memoryLoad15\n\t\tthreshold = 15\n\t}\n\tshouldSendResourceAlert(alert, percent, memoryLoad, threshold)\n}\n\n// 获取系统负载数据并发送到通道\nfunc loadLoadInfo(alert dto.AlertDTO) {\n\tavgStat, err := load.Avg()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting load usage, err: %v\", err)\n\t\treturn\n\t}\n\tvar loadValue float64\n\tCPUTotal, _ := psutil.CPUInfo.GetLogicalCores(false)\n\tswitch alert.Cycle {\n\tcase 1:\n\t\tloadValue = avgStat.Load1 / (float64(CPUTotal*2) * 0.75) * 100\n\tcase 5:\n\t\tloadValue = avgStat.Load5 / (float64(CPUTotal*2) * 0.75) * 100\n\tcase 15:\n\t\tloadValue = avgStat.Load15 / (float64(CPUTotal*2) * 0.75) * 100\n\tdefault:\n\t\treturn\n\t}\n\tif loadValue < float64(alert.Count) {\n\t\treturn\n\t}\n\tnewDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"task log record not found, err: %v\", err)\n\t}\n\tif isAlertDue(newDate) {\n\t\tsendResourceAlert(alert, loadValue)\n\t}\n}\n\nfunc loadDiskUsage(alert dto.AlertDTO) {\n\tnewDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"record not found, err: %v\", err)\n\t\treturn\n\t}\n\tif isAlertDue(newDate) {\n\t\tif strings.Contains(alert.Project, \"all\") {\n\t\t\t_ = processAllDisks(alert)\n\t\t} else {\n\t\t\t_ = processSingleDisk(alert)\n\t\t}\n\t}\n}\n\nfunc loadPanelLogin(alert dto.AlertDTO) {\n\tcount, isAlert, err := alertUtil.CountRecentFailedLoginLogs(alert.Cycle, alert.Count)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Failed to count recent failed login logs: %v\", err)\n\t}\n\tif isAlert {\n\t\tparams := []dto.Param{\n\t\t\t{\n\t\t\t\tIndex: \"1\",\n\t\t\t\tKey:   \"cycle\",\n\t\t\t\tValue: \"\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tIndex: \"2\",\n\t\t\t\tKey:   \"project\",\n\t\t\t\tValue: \"\",\n\t\t\t},\n\t\t}\n\t\tsendAlerts(alert, \"panelLogin\", strconv.Itoa(count), \"panelLogin\", params)\n\t}\n\n\twhitelist := strings.Split(strings.TrimSpace(alert.AdvancedParams), \"\\n\")\n\trecords, err := alertUtil.FindRecentSuccessLoginsNotInWhitelist(30, whitelist)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Failed to check recent failed ip login logs: %v\", err)\n\t}\n\tif len(records) > 0 {\n\t\tquota := strings.Join(func() []string {\n\t\t\tvar ips []string\n\t\t\tfor _, r := range records {\n\t\t\t\tips = append(ips, r.IP)\n\t\t\t}\n\t\t\treturn ips\n\t\t}(), \"\\n\")\n\t\tparams := []dto.Param{\n\t\t\t{\n\t\t\t\tIndex: \"1\",\n\t\t\t\tKey:   \"cycle\",\n\t\t\t\tValue: \"\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tIndex: \"2\",\n\t\t\t\tKey:   \"project\",\n\t\t\t\tValue: \" IP \",\n\t\t\t},\n\t\t}\n\t\tsendAlerts(alert, \"panelIpLogin\", quota, \"panelIpLogin\", params)\n\t}\n}\n\nfunc loadSSHLogin(alert dto.AlertDTO) {\n\tcount, isAlert, err := alertUtil.CountRecentFailedSSHLog(alert.Cycle, alert.Count)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Failed to count recent failed ssh login logs: %v\", err)\n\t}\n\tif isAlert {\n\t\tparams := []dto.Param{\n\t\t\t{\n\t\t\t\tIndex: \"1\",\n\t\t\t\tKey:   \"cycle\",\n\t\t\t\tValue: \" SSH \",\n\t\t\t},\n\t\t\t{\n\t\t\t\tIndex: \"2\",\n\t\t\t\tKey:   \"project\",\n\t\t\t\tValue: \"\",\n\t\t\t},\n\t\t}\n\t\tsendAlerts(alert, \"sshLogin\", strconv.Itoa(count), \"sshLogin\", params)\n\t}\n\twhitelist := strings.Split(strings.TrimSpace(alert.AdvancedParams), \"\\n\")\n\trecords, err := alertUtil.FindRecentSuccessLoginNotInWhitelist(30, whitelist)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Failed to check recent failed ip ssh login logs: %v\", err)\n\t}\n\tif len(records) > 0 {\n\t\tquota := strings.Join(records, \"\\n\")\n\t\tparams := []dto.Param{\n\t\t\t{\n\t\t\t\tIndex: \"1\",\n\t\t\t\tKey:   \"cycle\",\n\t\t\t\tValue: \" SSH \",\n\t\t\t},\n\t\t\t{\n\t\t\t\tIndex: \"2\",\n\t\t\t\tKey:   \"project\",\n\t\t\t\tValue: \" IP \",\n\t\t\t},\n\t\t}\n\t\tsendAlerts(alert, \"sshIpLogin\", quota, \"sshIpLogin\", params)\n\t}\n}\n\nfunc loadNodeException(alert dto.AlertDTO) {\n\t// only master alert\n\tfailCount, err := xpack.GetNodeErrorAlert()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting node, err: %s\", err)\n\t\treturn\n\t}\n\tif failCount > 0 {\n\t\tquotaType := \"node-error\"\n\t\tparams := []dto.Param{\n\t\t\t{\n\t\t\t\tIndex: \"1\",\n\t\t\t\tKey:   \"cycle\",\n\t\t\t\tValue: strconv.Itoa(int(failCount)),\n\t\t\t},\n\t\t}\n\t\tnewDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"record not found, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tif isAlertDue(newDate) {\n\t\t\tsender := NewAlertSender(alert, quotaType)\n\t\t\tsender.ResourceSend(strconv.Itoa(int(failCount)), params)\n\t\t}\n\t}\n\n}\n\nfunc loadLicenseException(alert dto.AlertDTO) {\n\t// only master alert\n\tfailCount, err := xpack.GetLicenseErrorAlert()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting license, err: %s\", err)\n\t\treturn\n\t}\n\tif failCount > 0 {\n\t\tquotaType := \"license-error\"\n\t\tparams := []dto.Param{\n\t\t\t{\n\t\t\t\tIndex: \"1\",\n\t\t\t\tKey:   \"cycle\",\n\t\t\t\tValue: strconv.Itoa(int(failCount)),\n\t\t\t},\n\t\t}\n\t\tnewDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"record not found, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tif isAlertDue(newDate) {\n\t\t\tsender := NewAlertSender(alert, quotaType)\n\t\t\tsender.ResourceSend(strconv.Itoa(int(failCount)), params)\n\t\t}\n\t}\n}\n\nfunc sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params []dto.Param) {\n\tmethods := strings.Split(alert.Method, \",\")\n\tnewDate, err := alertRepo.GetTaskLog(alertType, alert.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"task log record not found, err: %v\", err)\n\t}\n\tif newDate.IsZero() || calculateMinutesDifference(newDate) > ResourceAlertInterval {\n\t\tfor _, m := range methods {\n\t\t\tm = strings.TrimSpace(m)\n\t\t\tswitch m {\n\t\t\tcase constant.SMS:\n\t\t\t\tif !alertUtil.CheckSMSSendLimit(constant.SMS) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttodayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, constant.SMS)\n\t\t\t\tif !isValid {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcreate := dto.AlertLogCreate{\n\t\t\t\t\tType:    alertType,\n\t\t\t\t\tAlertId: alert.ID,\n\t\t\t\t\tCount:   todayCount + 1,\n\t\t\t\t}\n\t\t\t\talertErr := xpack.CreateSMSAlertLog(alertType, alert, create, quotaType, params, constant.SMS)\n\t\t\t\tif alertErr != nil {\n\t\t\t\t\tglobal.LOG.Infof(\"%s alert sms push faild, err: %v\", alertType, alertErr.Error())\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\talertUtil.CreateNewAlertTask(quota, alertType, quotaType, constant.SMS)\n\t\t\tcase constant.Email:\n\t\t\t\ttodayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, constant.Email)\n\t\t\t\tif !isValid {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcreate := dto.AlertLogCreate{\n\t\t\t\t\tType:    alertType,\n\t\t\t\t\tAlertId: alert.ID,\n\t\t\t\t\tCount:   todayCount + 1,\n\t\t\t\t}\n\t\t\t\talertInfo := alert\n\t\t\t\talertInfo.Type = alertType\n\t\t\t\tcreate.AlertRule = alertUtil.ProcessAlertRule(alert)\n\t\t\t\tcreate.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, constant.Email)\n\t\t\t\ttransport := xpack.LoadRequestTransport()\n\t\t\t\tagentInfo, _ := xpack.GetAgentInfo()\n\t\t\t\talertErr := alertUtil.CreateEmailAlertLog(create, alertInfo, params, transport, agentInfo)\n\t\t\t\tif alertErr != nil {\n\t\t\t\t\tglobal.LOG.Infof(\"%s alert email push faild, err: %v\", alertType, alertErr.Error())\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\talertUtil.CreateNewAlertTask(quota, alertType, quotaType, constant.Email)\n\t\t\tcase constant.WeCom, constant.DingTalk, constant.FeiShu:\n\t\t\t\ttodayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, m)\n\t\t\t\tif !isValid {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tvar create = dto.AlertLogCreate{\n\t\t\t\t\tType:    alertUtil.GetCronJobType(alert.Type),\n\t\t\t\t\tAlertId: alert.ID,\n\t\t\t\t\tCount:   todayCount + 1,\n\t\t\t\t}\n\t\t\t\ttransport := xpack.LoadRequestTransport()\n\t\t\t\tagentInfo, _ := xpack.GetAgentInfo()\n\t\t\t\terr := xpack.CreateWebhookAlertLog(alertType, alert, create, quotaType, params, m, transport, agentInfo)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Infof(\"%s alert webhook %s push faild, err: %v\", alertType, m, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\talertUtil.CreateNewAlertTask(quota, alertType, quotaType, m)\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ------------------------------\nfunc getRepoOptionsByProject(project string) []repo.DBOption {\n\tvar opts []repo.DBOption\n\tif project != \"all\" {\n\t\titemID, _ := strconv.Atoi(project)\n\t\topts = append(opts, repo.WithByID(uint(itemID)))\n\t}\n\treturn opts\n}\n\nfunc serializeAndSortProjects(projectMap map[uint][]time.Time) string {\n\tif len(projectMap) == 0 {\n\t\treturn \"\"\n\t}\n\tkeys := make([]int, 0, len(projectMap))\n\tfor k := range projectMap {\n\t\tkeys = append(keys, int(k))\n\t}\n\tsort.Ints(keys)\n\tprojectJSON, err := json.Marshal(projectMap)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Failed to serialize projectMap: %v\", err)\n\t\treturn \"\"\n\t}\n\n\treturn string(projectJSON)\n}\n\nfunc calculateSSLExpiryDays(sslList []model.WebsiteSSL, cycle uint) (map[int][]string, map[uint][]time.Time) {\n\tcurrentDate := time.Now()\n\tdaysDiffMap := make(map[int][]string)\n\tprojectMap := make(map[uint][]time.Time)\n\n\tfor _, ssl := range sslList {\n\t\tdaysDiff := int(math.Ceil(\n\t\t\tssl.ExpireDate.Sub(currentDate).Hours() / 24,\n\t\t))\n\t\tif daysDiff > 0 && int(cycle) >= daysDiff {\n\t\t\tdaysDiffMap[daysDiff] = append(daysDiffMap[daysDiff], ssl.PrimaryDomain)\n\t\t\tprojectMap[ssl.ID] = append(projectMap[ssl.ID], ssl.ExpireDate)\n\t\t}\n\t}\n\treturn daysDiffMap, projectMap\n}\n\nfunc calculateWebsiteExpiryDays(websites []model.Website, cycle uint) (map[int][]string, map[uint][]time.Time) {\n\tcurrentDate := time.Now()\n\tdaysDiffMap := make(map[int][]string)\n\tprojectMap := make(map[uint][]time.Time)\n\n\tfor _, website := range websites {\n\t\tdaysDiff := int(math.Ceil(\n\t\t\twebsite.ExpireDate.Sub(currentDate).Hours() / 24,\n\t\t))\n\t\tif daysDiff > 0 && int(cycle) >= daysDiff {\n\t\t\tdaysDiffMap[daysDiff] = append(daysDiffMap[daysDiff], website.PrimaryDomain)\n\t\t\tprojectMap[website.ID] = append(projectMap[website.ID], website.ExpireDate)\n\t\t}\n\t}\n\treturn daysDiffMap, projectMap\n}\n\nfunc getSettingValue(key string) (string, error) {\n\tvar setting model.Setting\n\tif err := global.CoreDB.Model(&model.Setting{}).Where(\"key = ?\", key).First(&setting).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"load %s from db setting failed: %v\", key, err)\n\t\treturn \"\", err\n\t}\n\treturn setting.Value, nil\n}\n\nfunc getValidVersion(info *dto.UpgradeInfo) string {\n\tif info.NewVersion != \"\" {\n\t\treturn info.NewVersion\n\t} else if info.TestVersion != \"\" {\n\t\treturn info.TestVersion\n\t} else if info.LatestVersion != \"\" {\n\t\treturn info.LatestVersion\n\t}\n\treturn \"\"\n}\n\nfunc shouldSendResourceAlert(alert dto.AlertDTO, currentUsage float64, usageLoad *[]float64, threshold int) {\n\tnewDate, err := alertRepo.GetTaskLog(alert.Type, alert.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"record not found, err: %v\", err)\n\t}\n\tif isAlertDue(newDate) {\n\t\t*usageLoad = append(*usageLoad, currentUsage)\n\t\tif len(*usageLoad) > threshold {\n\t\t\t*usageLoad = (*usageLoad)[1:]\n\t\t}\n\t\tif len(*usageLoad) == threshold {\n\t\t\tavgUsage := average(*usageLoad)\n\t\t\tif avgUsage >= float64(alert.Count) {\n\t\t\t\tsendResourceAlert(alert, avgUsage)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc isAlertDue(lastAlertTime time.Time) bool {\n\tif lastAlertTime.IsZero() {\n\t\treturn true\n\t}\n\treturn calculateMinutesDifference(lastAlertTime) > ResourceAlertInterval\n}\n\nfunc sendResourceAlert(alert dto.AlertDTO, value float64) {\n\tvalueStr := common.FormatPercent(value)\n\tmodule := getModuleName(alert.Type)\n\tparams := createAlertAvgParams(strconv.Itoa(int(alert.Cycle)), module, valueStr)\n\tsender := NewAlertSender(alert, strconv.Itoa(int(alert.Cycle)))\n\tsender.ResourceSend(valueStr, params)\n}\n\nfunc getModuleName(alertType string) string {\n\tvar module string\n\tswitch alertType {\n\tcase \"cpu\":\n\t\tmodule = \" CPU \"\n\tcase \"memory\":\n\t\tmodule = \"内存\"\n\tcase \"load\":\n\t\tmodule = \"负载\"\n\tdefault:\n\t}\n\treturn module\n}\n\nfunc canSendAlertToday(alertType, quotaType string, sendCount uint, method string) (uint, bool) {\n\ttodayCount, _, err := alertRepo.LoadTaskCount(alertType, quotaType, method)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting task info, err: %v\", err)\n\t\treturn todayCount, false\n\t}\n\tif todayCount >= sendCount {\n\t\treturn todayCount, false\n\t}\n\n\treturn todayCount, true\n}\n\nfunc average(arr []float64) float64 {\n\ttotal := 0.0\n\tfor _, v := range arr {\n\t\ttotal += v\n\t}\n\treturn total / float64(len(arr))\n}\n\nfunc createAlertBaseParams(project, cycle string) []dto.Param {\n\treturn []dto.Param{\n\t\t{\n\t\t\tIndex: \"1\",\n\t\t\tKey:   \"project\",\n\t\t\tValue: project,\n\t\t},\n\t\t{\n\t\t\tIndex: \"2\",\n\t\t\tKey:   \"cycle\",\n\t\t\tValue: cycle,\n\t\t},\n\t}\n}\n\nfunc createAlertPwdParams(cycle string) []dto.Param {\n\treturn []dto.Param{\n\t\t{\n\t\t\tIndex: \"1\",\n\t\t\tKey:   \"cycle\",\n\t\t\tValue: cycle,\n\t\t},\n\t}\n}\n\nfunc createAlertAvgParams(cycle, module, count string) []dto.Param {\n\treturn []dto.Param{\n\t\t{\n\t\t\tIndex: \"1\",\n\t\t\tKey:   \"cycle\",\n\t\t\tValue: cycle,\n\t\t},\n\t\t{\n\t\t\tIndex: \"2\",\n\t\t\tKey:   \"module\",\n\t\t\tValue: module,\n\t\t},\n\t\t{\n\t\t\tIndex: \"3\",\n\t\t\tKey:   \"count\",\n\t\t\tValue: count,\n\t\t},\n\t}\n}\n\nfunc createAlertDiskParams(project, count string) []dto.Param {\n\treturn []dto.Param{\n\t\t{\n\t\t\tIndex: \"1\",\n\t\t\tKey:   \"project\",\n\t\t\tValue: project,\n\t\t},\n\t\t{\n\t\t\tIndex: \"2\",\n\t\t\tKey:   \"count\",\n\t\t\tValue: count,\n\t\t},\n\t}\n}\n\nfunc processAllDisks(alert dto.AlertDTO) error {\n\tdiskList, err := NewIAlertService().GetDisks()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting disk list, err: %v\", err)\n\t\treturn err\n\t}\n\tvar errMsgs []string\n\tfor _, item := range diskList {\n\t\terr := checkAndCreateDiskAlert(alert, item.Path)\n\t\tif err != nil {\n\t\t\terrMsg := fmt.Sprintf(\"disk path %s process failed: %v\", item.Path, err)\n\t\t\terrMsgs = append(errMsgs, errMsg)\n\t\t\tglobal.LOG.Errorf(\"%s\", errMsg)\n\t\t\tcontinue\n\t\t}\n\t}\n\tif len(errMsgs) > 0 {\n\t\treturn fmt.Errorf(\"batch process disks failed, error count: %d, details: %s\", len(errMsgs), strings.Join(errMsgs, \"; \"))\n\t}\n\treturn nil\n}\n\nfunc processSingleDisk(alert dto.AlertDTO) error {\n\terr := checkAndCreateDiskAlert(alert, alert.Project)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"%s\", err.Error())\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc checkAndCreateDiskAlert(alert dto.AlertDTO, path string) error {\n\tusageStat, err := psutil.DISK.GetUsage(path, false)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting disk usage for %s, err: %v\", path, err)\n\t\treturn err\n\t}\n\n\tusedTotal, usedStr := calculateUsedTotal(alert.Cycle, usageStat)\n\tcommonTotal := float64(alert.Count)\n\tif alert.Cycle == 1 {\n\t\tcommonTotal *= 1024 * 1024 * 1024\n\t}\n\tif usedTotal < commonTotal {\n\t\treturn nil\n\t}\n\tparams := createAlertDiskParams(path, usedStr)\n\tsender := NewAlertSender(alert, alert.Project)\n\tsender.ResourceSend(path, params)\n\treturn nil\n}\n\nfunc calculateUsedTotal(cycle uint, usageStat *disk.UsageStat) (float64, string) {\n\tif cycle == 1 {\n\t\treturn float64(usageStat.Used), common.FormatBytes(usageStat.Used)\n\t}\n\treturn usageStat.UsedPercent, common.FormatPercent(usageStat.UsedPercent)\n}\n\nfunc calculateDaysDifference(expirationTime time.Time) int {\n\tcurrentDate := time.Now()\n\tformattedTime := currentDate.Format(constant.DateTimeLayout)\n\tparsedTime, _ := time.Parse(constant.DateTimeLayout, formattedTime)\n\ttimeGap := expirationTime.Sub(parsedTime).Milliseconds()\n\tif timeGap < 0 {\n\t\treturn -1\n\t}\n\tdaysDifference := int(math.Floor(float64(timeGap) / (3600 * 1000 * 24)))\n\treturn daysDifference\n}\n\nfunc calculateMinutesDifference(newDate time.Time) int {\n\tnow := time.Now()\n\tif newDate.After(now) {\n\t\treturn -1\n\t}\n\tminutesDifference := int(now.Sub(newDate).Minutes())\n\treturn minutesDifference\n}\n"
  },
  {
    "path": "agent/app/service/alert_sender.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\talertUtil \"github.com/1Panel-dev/1Panel/agent/utils/alert\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"strings\"\n)\n\ntype AlertSender struct {\n\talert     dto.AlertDTO\n\tquotaType string\n}\n\nfunc NewAlertSender(alert dto.AlertDTO, quotaType string) *AlertSender {\n\treturn &AlertSender{\n\t\talert:     alert,\n\t\tquotaType: quotaType,\n\t}\n}\n\nfunc (s *AlertSender) Send(quota string, params []dto.Param) {\n\tmethods := strings.Split(s.alert.Method, \",\")\n\tfor _, method := range methods {\n\t\tmethod = strings.TrimSpace(method)\n\t\tswitch method {\n\t\tcase constant.SMS:\n\t\t\ts.sendSMS(quota, params)\n\t\tcase constant.Email:\n\t\t\ts.sendEmail(quota, params)\n\t\tcase constant.WeCom, constant.DingTalk, constant.FeiShu:\n\t\t\ts.sendWebhook(quota, params, method)\n\t\t}\n\t}\n}\n\nfunc (s *AlertSender) ResourceSend(quota string, params []dto.Param) {\n\tmethods := strings.Split(s.alert.Method, \",\")\n\tfor _, method := range methods {\n\t\tmethod = strings.TrimSpace(method)\n\t\tswitch method {\n\t\tcase constant.SMS:\n\t\t\ts.sendResourceSMS(quota, params)\n\t\tcase constant.Email:\n\t\t\ts.sendResourceEmail(quota, params)\n\t\tcase constant.WeCom, constant.DingTalk, constant.FeiShu:\n\t\t\ts.sendResourceWebhook(quota, params, method)\n\t\t}\n\t}\n}\n\nfunc (s *AlertSender) sendSMS(quota string, params []dto.Param) {\n\tif !alertUtil.CheckSMSSendLimit(constant.SMS) {\n\t\treturn\n\t}\n\n\ttotalCount, isValid := s.canSendAlert(constant.SMS)\n\tif !isValid {\n\t\treturn\n\t}\n\n\tcreate := dto.AlertLogCreate{\n\t\tStatus:  constant.AlertSuccess,\n\t\tCount:   totalCount + 1,\n\t\tAlertId: s.alert.ID,\n\t\tType:    s.alert.Type,\n\t}\n\n\terr := xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"%s alert sms push failed: %v\", s.alert.Type, err)\n\t\treturn\n\t}\n\talertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.SMS)\n}\n\nfunc (s *AlertSender) sendEmail(quota string, params []dto.Param) {\n\ttotalCount, isValid := s.canSendAlert(constant.Email)\n\tif !isValid {\n\t\treturn\n\t}\n\n\tcreate := dto.AlertLogCreate{\n\t\tStatus:      constant.AlertSuccess,\n\t\tCount:       totalCount + 1,\n\t\tAlertId:     s.alert.ID,\n\t\tType:        s.alert.Type,\n\t\tAlertRule:   alertUtil.ProcessAlertRule(s.alert),\n\t\tAlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),\n\t}\n\n\ttransport := xpack.LoadRequestTransport()\n\tagentInfo, _ := xpack.GetAgentInfo()\n\terr := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"%s alert email push failed: %v\", s.alert.Type, err)\n\t\treturn\n\t}\n\talertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)\n}\n\nfunc (s *AlertSender) sendWebhook(quota string, params []dto.Param, method string) {\n\ttotalCount, isValid := s.canSendAlert(method)\n\tif !isValid {\n\t\treturn\n\t}\n\n\tcreate := dto.AlertLogCreate{\n\t\tStatus:  constant.AlertSuccess,\n\t\tCount:   totalCount + 1,\n\t\tAlertId: s.alert.ID,\n\t\tType:    s.alert.Type,\n\t}\n\ttransport := xpack.LoadRequestTransport()\n\tagentInfo, _ := xpack.GetAgentInfo()\n\terr := xpack.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, method, transport, agentInfo)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"%s alert %s webhook push failed: %v\", s.alert.Type, method, err)\n\t\treturn\n\t}\n\talertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, method)\n}\n\nfunc (s *AlertSender) sendResourceSMS(quota string, params []dto.Param) {\n\tif !alertUtil.CheckSMSSendLimit(constant.SMS) {\n\t\treturn\n\t}\n\n\ttodayCount, isValid := s.canResourceSendAlert(constant.SMS)\n\tif !isValid {\n\t\treturn\n\t}\n\n\tcreate := dto.AlertLogCreate{\n\t\tStatus:  constant.AlertSuccess,\n\t\tCount:   todayCount + 1,\n\t\tAlertId: s.alert.ID,\n\t\tType:    s.alert.Type,\n\t}\n\n\tif err := xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS); err != nil {\n\t\tglobal.LOG.Errorf(\"failed to send SMS alert: %v\", err)\n\t\treturn\n\t}\n\talertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.SMS)\n}\n\nfunc (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {\n\ttodayCount, isValid := s.canResourceSendAlert(constant.Email)\n\tif !isValid {\n\t\treturn\n\t}\n\n\tcreate := dto.AlertLogCreate{\n\t\tStatus:      constant.AlertSuccess,\n\t\tCount:       todayCount + 1,\n\t\tAlertId:     s.alert.ID,\n\t\tType:        s.alert.Type,\n\t\tAlertRule:   alertUtil.ProcessAlertRule(s.alert),\n\t\tAlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),\n\t}\n\n\ttransport := xpack.LoadRequestTransport()\n\tagentInfo, _ := xpack.GetAgentInfo()\n\tif err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport, agentInfo); err != nil {\n\t\tglobal.LOG.Errorf(\"failed to send Email alert: %v\", err)\n\t\treturn\n\t}\n\talertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)\n}\n\nfunc (s *AlertSender) sendResourceWebhook(quota string, params []dto.Param, method string) {\n\ttodayCount, isValid := s.canResourceSendAlert(method)\n\tif !isValid {\n\t\treturn\n\t}\n\n\tcreate := dto.AlertLogCreate{\n\t\tStatus:  constant.AlertSuccess,\n\t\tCount:   todayCount + 1,\n\t\tAlertId: s.alert.ID,\n\t\tType:    s.alert.Type,\n\t}\n\ttransport := xpack.LoadRequestTransport()\n\tagentInfo, _ := xpack.GetAgentInfo()\n\tif err := xpack.CreateWebhookAlertLog(s.alert.Type, s.alert, create, quota, params, method, transport, agentInfo); err != nil {\n\t\tglobal.LOG.Errorf(\"%s alert %s webhook push failed: %v\", s.alert.Type, method, err)\n\t\treturn\n\t}\n\talertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, method)\n}\n\nfunc (s *AlertSender) canSendAlert(method string) (uint, bool) {\n\ttodayCount, totalCount, err := alertRepo.LoadTaskCount(s.alert.Type, s.quotaType, method)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting task count: %v\", err)\n\t\treturn totalCount, false\n\t}\n\n\tif todayCount >= 1 || s.alert.SendCount <= totalCount {\n\t\treturn totalCount, false\n\t}\n\treturn totalCount, true\n}\n\nfunc (s *AlertSender) canResourceSendAlert(method string) (uint, bool) {\n\ttodayCount, _, err := alertRepo.LoadTaskCount(s.alert.Type, s.quotaType, method)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting task count: %v\", err)\n\t\treturn todayCount, false\n\t}\n\tif s.alert.SendCount <= todayCount {\n\t\treturn todayCount, false\n\t}\n\treturn todayCount, true\n}\n"
  },
  {
    "path": "agent/app/service/app.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/gin-gonic/gin\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/appicon\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/req_helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nvar (\n\tappStoreSyncMu  sync.Mutex\n\tappStoreSyncing bool\n)\n\ntype AppService struct {\n}\n\ntype IAppService interface {\n\tPageApp(ctx *gin.Context, req request.AppSearch) (*response.AppRes, error)\n\tGetAppTags(ctx *gin.Context) ([]response.TagDTO, error)\n\tGetApp(ctx *gin.Context, key string) (*response.AppDTO, error)\n\tGetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)\n\tInstall(req request.AppInstallCreate, executeScript bool) (*model.AppInstall, error)\n\tSyncAppListFromRemote(taskID string) error\n\tGetAppUpdate() (*response.AppUpdateRes, error)\n\tGetAppDetailByID(id uint) (*response.AppDetailDTO, error)\n\tSyncAppListFromLocal(taskID string)\n\tGetAppIcon(key string) ([]byte, string, string, error)\n\tGetAppDetailByKey(appKey, version string) (response.AppDetailSimpleDTO, error)\n}\n\ntype appInstallHooks struct {\n\tAfterCopyData func(appInstall *model.AppInstall) error\n}\n\nfunc NewIAppService() IAppService {\n\treturn &AppService{}\n}\n\nfunc (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response.AppRes, error) {\n\tvar opts []repo.DBOption\n\topts = append(opts, appRepo.OrderByRecommend())\n\tif req.Name != \"\" {\n\t\topts = append(opts, appRepo.WithByLikeName(strings.TrimSpace(req.Name)))\n\t}\n\tif req.Type != \"\" {\n\t\topts = append(opts, appRepo.WithType(req.Type))\n\t}\n\tif req.Recommend {\n\t\topts = append(opts, appRepo.GetRecommend())\n\t}\n\tif req.Resource != \"\" && req.Resource != \"all\" {\n\t\topts = append(opts, appRepo.WithResource(req.Resource))\n\t}\n\n\tif req.ShowCurrentArch {\n\t\tinfo, err := NewIDashboardService().LoadOsInfo()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tkernelArch := info.KernelArch\n\t\tif kernelArch == \"aarch64\" {\n\t\t\tkernelArch = \"arm64\"\n\t\t}\n\t\topts = append(opts, appRepo.WithArch(kernelArch))\n\t}\n\tif len(req.Tags) != 0 {\n\t\ttags, err := tagRepo.GetByKeys(req.Tags)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar tagIds []uint\n\t\tfor _, t := range tags {\n\t\t\ttagIds = append(tagIds, t.ID)\n\t\t}\n\t\tappTags, err := appTagRepo.GetByTagIds(tagIds)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar appIds []uint\n\t\tfor _, t := range appTags {\n\t\t\tappIds = append(appIds, t.AppId)\n\t\t}\n\t\topts = append(opts, repo.WithByIDs(appIds))\n\t}\n\tres := &response.AppRes{}\n\n\ttotal, apps, err := appRepo.Page(req.Page, req.PageSize, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tappDTOs := make([]*response.AppItem, 0)\n\tinfo := &dto.SettingInfo{}\n\tif req.Type == \"php\" {\n\t\tinfo, _ = NewISettingService().GetSettingInfo()\n\t}\n\tlang := strings.ToLower(common.GetLang(ctx))\n\tfor _, ap := range apps {\n\t\tif req.Type == \"php\" {\n\t\t\tif !global.CONF.Base.IsOffLine && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf(\"%f\", ap.RequiredPanelVersion))) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tappDTO := &response.AppItem{\n\t\t\tID:                  ap.ID,\n\t\t\tName:                ap.Name,\n\t\t\tKey:                 ap.Key,\n\t\t\tLimit:               ap.Limit,\n\t\t\tGpuSupport:          ap.GpuSupport,\n\t\t\tRecommend:           ap.Recommend,\n\t\t\tDescription:         ap.GetDescription(ctx),\n\t\t\tType:                ap.Type,\n\t\t\tBatchInstallSupport: ap.BatchInstallSupport,\n\t\t}\n\t\tappDTOs = append(appDTOs, appDTO)\n\t\ttags, err := getAppTags(ap.ID, lang)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, tag := range tags {\n\t\t\tappDTO.Tags = append(appDTO.Tags, tag.Name)\n\t\t}\n\t\tif ap.Type == constant.RuntimePHP || ap.Type == constant.RuntimeGo || ap.Type == constant.RuntimeNode || ap.Type == constant.RuntimePython || ap.Type == constant.RuntimeJava || ap.Type == constant.RuntimeDotNet {\n\t\t\tdetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(ap.ID))\n\t\t\tvar ids []uint\n\t\t\tif len(details) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, d := range details {\n\t\t\t\tids = append(ids, d.ID)\n\t\t\t}\n\t\t\truntimes, _ := runtimeRepo.List(runtimeRepo.WithDetailIdsIn(ids))\n\t\t\tappDTO.Installed = len(runtimes) > 0\n\t\t} else {\n\t\t\tinstalls, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(ap.ID))\n\t\t\tappDTO.Installed = len(installs) > 0\n\t\t}\n\t}\n\tres.Items = appDTOs\n\tres.Total = total\n\n\treturn res, nil\n}\n\nfunc (a AppService) GetAppTags(ctx *gin.Context) ([]response.TagDTO, error) {\n\ttags, err := tagRepo.All()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []response.TagDTO\n\tlang := strings.ToLower(common.GetLang(ctx))\n\tfor _, tag := range tags {\n\t\ttagDTO := response.TagDTO{\n\t\t\tID:  tag.ID,\n\t\t\tKey: tag.Key,\n\t\t}\n\t\tvar translations = make(map[string]string)\n\t\t_ = json.Unmarshal([]byte(tag.Translations), &translations)\n\t\tif name, ok := translations[lang]; ok {\n\t\t\ttagDTO.Name = name\n\t\t}\n\t\tres = append(res, tagDTO)\n\t}\n\treturn res, nil\n}\n\nfunc (a AppService) GetApp(ctx *gin.Context, key string) (*response.AppDTO, error) {\n\tvar appDTO response.AppDTO\n\tif key == \"postgres\" {\n\t\tkey = \"postgresql\"\n\t}\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(key))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tappDTO.App = app\n\tappDTO.App.Description = app.GetDescription(ctx)\n\tdetails, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tappDTO.Versions = getAppVersions(key, details)\n\ttags, err := getAppTags(app.ID, strings.ToLower(common.GetLang(ctx)))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tappDTO.Tags = tags\n\treturn &appDTO, nil\n}\n\nfunc (a AppService) GetAppDetailByKey(appKey, version string) (response.AppDetailSimpleDTO, error) {\n\tvar appDetailDTO response.AppDetailSimpleDTO\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(appKey))\n\tif err != nil {\n\t\treturn appDetailDTO, err\n\t}\n\tappDetail, err := appDetailRepo.GetFirst(appDetailRepo.WithAppId(app.ID), appDetailRepo.WithVersion(version))\n\tif err != nil {\n\t\treturn appDetailDTO, err\n\t}\n\tappDetailDTO.ID = appDetail.ID\n\treturn appDetailDTO, nil\n}\n\nfunc (a AppService) GetAppDetail(appID uint, version, appType string) (response.AppDetailDTO, error) {\n\tvar (\n\t\tappDetailDTO response.AppDetailDTO\n\t\topts         []repo.DBOption\n\t)\n\topts = append(opts, appDetailRepo.WithAppId(appID), appDetailRepo.WithVersion(version))\n\tdetail, err := appDetailRepo.GetFirst(opts...)\n\tif err != nil {\n\t\treturn appDetailDTO, err\n\t}\n\tappDetailDTO.AppDetail = detail\n\tappDetailDTO.Enable = true\n\n\tif appType == \"runtime\" {\n\t\tapp, err := appRepo.GetFirst(repo.WithByID(appID))\n\t\tif err != nil {\n\t\t\treturn appDetailDTO, err\n\t\t}\n\t\tfileOp := files.NewFileOp()\n\n\t\tversionPath := filepath.Join(app.GetAppResourcePath(), detail.Version)\n\t\tif !fileOp.Stat(versionPath) || detail.Update {\n\t\t\tif err = downloadApp(app, detail, nil, nil); err != nil && !fileOp.Stat(versionPath) {\n\t\t\t\treturn appDetailDTO, err\n\t\t\t}\n\t\t}\n\t\tswitch app.Type {\n\t\tcase constant.RuntimePHP:\n\t\t\tparamsPath := filepath.Join(versionPath, \"data.yml\")\n\t\t\tif !fileOp.Stat(paramsPath) {\n\t\t\t\treturn appDetailDTO, buserr.WithDetail(\"ErrFileNotExist\", paramsPath, nil)\n\t\t\t}\n\t\t\tparam, err := fileOp.GetContent(paramsPath)\n\t\t\tif err != nil {\n\t\t\t\treturn appDetailDTO, err\n\t\t\t}\n\t\t\tparamMap := make(map[string]interface{})\n\t\t\tif err = yaml.Unmarshal(param, &paramMap); err != nil {\n\t\t\t\treturn appDetailDTO, err\n\t\t\t}\n\t\t\tappDetailDTO.Params = paramMap[\"additionalProperties\"]\n\t\t\tcomposePath := filepath.Join(versionPath, \"docker-compose.yml\")\n\t\t\tif !fileOp.Stat(composePath) {\n\t\t\t\treturn appDetailDTO, buserr.WithDetail(\"ErrFileNotExist\", composePath, nil)\n\t\t\t}\n\t\t\tcompose, err := fileOp.GetContent(composePath)\n\t\t\tif err != nil {\n\t\t\t\treturn appDetailDTO, err\n\t\t\t}\n\t\t\tcomposeMap := make(map[string]interface{})\n\t\t\tif err := yaml.Unmarshal(compose, &composeMap); err != nil {\n\t\t\t\treturn appDetailDTO, err\n\t\t\t}\n\t\t\tif service, ok := composeMap[\"services\"]; ok {\n\t\t\t\tservicesMap := service.(map[string]interface{})\n\t\t\t\tfor k := range servicesMap {\n\t\t\t\t\tappDetailDTO.Image = k\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tparamMap := make(map[string]interface{})\n\t\tif err := json.Unmarshal([]byte(detail.Params), &paramMap); err != nil {\n\t\t\treturn appDetailDTO, err\n\t\t}\n\t\tappDetailDTO.Params = paramMap\n\t}\n\n\tif appDetailDTO.DockerCompose == \"\" {\n\t\tfilename := filepath.Base(appDetailDTO.DownloadUrl)\n\t\tdockerComposeUrl := fmt.Sprintf(\"%s%s\", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), \"docker-compose.yml\")\n\t\tstatusCode, composeRes, err := req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)\n\t\tif err != nil {\n\t\t\treturn appDetailDTO, buserr.WithDetail(\"ErrGetCompose\", err.Error(), err)\n\t\t}\n\t\tif statusCode > 200 {\n\t\t\treturn appDetailDTO, buserr.WithDetail(\"ErrGetCompose\", string(composeRes), err)\n\t\t}\n\t\tdetail.DockerCompose = string(composeRes)\n\t\t_ = appDetailRepo.Update(context.Background(), detail)\n\t\tappDetailDTO.DockerCompose = string(composeRes)\n\t}\n\n\tappDetailDTO.HostMode = isHostModel(appDetailDTO.DockerCompose)\n\n\tapp, err := appRepo.GetFirst(repo.WithByID(detail.AppId))\n\tif err != nil {\n\t\treturn appDetailDTO, err\n\t}\n\tif err := checkLimit(app); err != nil {\n\t\tappDetailDTO.Enable = false\n\t}\n\tappDetailDTO.Architectures = app.Architectures\n\tappDetailDTO.MemoryRequired = app.MemoryRequired\n\tappDetailDTO.GpuSupport = app.GpuSupport\n\treturn appDetailDTO, nil\n}\nfunc (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {\n\tres := &response.AppDetailDTO{}\n\tappDetail, err := appDetailRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres.AppDetail = appDetail\n\tparamMap := make(map[string]interface{})\n\tif err := json.Unmarshal([]byte(appDetail.Params), &paramMap); err != nil {\n\t\treturn nil, err\n\t}\n\tres.Params = paramMap\n\tres.HostMode = isHostModel(appDetail.DockerCompose)\n\treturn res, nil\n}\n\nfunc (a AppService) Install(req request.AppInstallCreate, executeScript bool) (appInstall *model.AppInstall, err error) {\n\treturn a.installWithHooks(req, executeScript, nil)\n}\n\nfunc (a AppService) installWithHooks(req request.AppInstallCreate, executeScript bool, hooks *appInstallHooks) (appInstall *model.AppInstall, err error) {\n\tif err = docker.CreateDefaultDockerNetwork(); err != nil {\n\t\terr = buserr.WithDetail(\"Err1PanelNetworkFailed\", err.Error(), nil)\n\t\treturn\n\t}\n\tif list, _ := appInstallRepo.ListBy(context.Background(), repo.WithByLowerName(req.Name)); len(list) > 0 {\n\t\terr = buserr.New(\"ErrAppNameExist\")\n\t\treturn\n\t}\n\tvar (\n\t\thttpPort  int\n\t\thttpsPort int\n\t\tappDetail model.AppDetail\n\t\tapp       model.App\n\t)\n\tappDetail, err = appDetailRepo.GetFirst(repo.WithByID(req.AppDetailId))\n\tif err != nil {\n\t\treturn\n\t}\n\tapp, err = appRepo.GetFirst(repo.WithByID(appDetail.AppId))\n\tif err != nil {\n\t\treturn\n\t}\n\tif DatabaseKeys[app.Key] > 0 {\n\t\tif existDatabases, _ := databaseRepo.GetList(repo.WithByName(req.Name)); len(existDatabases) > 0 {\n\t\t\terr = buserr.New(\"ErrRemoteExist\")\n\t\t\treturn\n\t\t}\n\t}\n\tif hostName, ok := req.Params[\"PANEL_DB_HOST\"]; ok {\n\t\tdatabase, _ := databaseRepo.Get(repo.WithByName(hostName.(string)))\n\t\tif database.AppInstallID > 0 {\n\t\t\tdatabaseInstall, _ := appInstallRepo.GetFirst(repo.WithByID(database.AppInstallID))\n\t\t\tif databaseInstall.Status != constant.StatusRunning {\n\t\t\t\treturn nil, buserr.WithName(\"ErrAppIsDown\", databaseInstall.Name)\n\t\t\t}\n\t\t}\n\t}\n\tfor key := range req.Params {\n\t\tif !strings.Contains(key, \"PANEL_APP_PORT\") {\n\t\t\tcontinue\n\t\t}\n\t\tvar port int\n\t\tport, err = checkPort(key, req.Params)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif key == \"PANEL_APP_PORT_HTTP\" {\n\t\t\thttpPort = port\n\t\t}\n\t\tif key == \"PANEL_APP_PORT_HTTPS\" {\n\t\t\thttpsPort = port\n\t\t}\n\t}\n\n\tif err = checkRequiredAndLimit(app); err != nil {\n\t\treturn\n\t}\n\n\tappInstall = &model.AppInstall{\n\t\tName:        req.Name,\n\t\tAppId:       appDetail.AppId,\n\t\tAppDetailId: appDetail.ID,\n\t\tVersion:     appDetail.Version,\n\t\tStatus:      constant.StatusInstalling,\n\t\tHttpPort:    httpPort,\n\t\tHttpsPort:   httpsPort,\n\t\tApp:         app,\n\t}\n\tcomposeMap := make(map[string]interface{})\n\tvar composeRes []byte\n\tif req.EditCompose {\n\t\tif err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif appDetail.DockerCompose == \"\" {\n\t\t\tdockerComposeUrl := fmt.Sprintf(\"%s/%s/1panel/%s/%s/docker-compose.yml\", global.AppRepoURL(), global.CONF.Base.Mode, app.Key, appDetail.Version)\n\t\t\t_, composeRes, err = req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tappDetail.DockerCompose = string(composeRes)\n\t\t\t_ = appDetailRepo.Update(context.Background(), appDetail)\n\t\t}\n\t\tif err = yaml.Unmarshal([]byte(appDetail.DockerCompose), &composeMap); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tvalue, ok := composeMap[\"services\"]\n\tif !ok || value == nil {\n\t\terr = buserr.New(\"ErrFileParse\")\n\t\treturn\n\t}\n\tservicesMap := value.(map[string]interface{})\n\tcontainerName := constant.ContainerPrefix + app.Key + \"-\" + common.RandStr(4)\n\tif req.Advanced && req.ContainerName != \"\" {\n\t\tcontainerName = req.ContainerName\n\t\tappInstalls, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithContainerName(containerName))\n\t\tif len(appInstalls) > 0 {\n\t\t\terr = buserr.New(\"ErrContainerName\")\n\t\t\treturn\n\t\t}\n\t\tcontainerExist := false\n\t\tcontainerExist, err = checkContainerNameIsExist(req.ContainerName, appInstall.GetPath())\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif containerExist {\n\t\t\terr = buserr.New(\"ErrContainerName\")\n\t\t\treturn\n\t\t}\n\t}\n\treq.Params[constant.ContainerName] = containerName\n\tappInstall.ContainerName = containerName\n\n\tindex := 0\n\tserviceName := \"\"\n\tfor k := range servicesMap {\n\t\tserviceName = k\n\t\tif index > 0 {\n\t\t\tcontinue\n\t\t}\n\t\tindex++\n\t}\n\tnewServiceName := strings.ToLower(appInstall.Name)\n\tif app.Limit == 0 && newServiceName != serviceName && len(servicesMap) == 1 {\n\t\tservicesMap[newServiceName] = servicesMap[serviceName]\n\t\tdelete(servicesMap, serviceName)\n\t\tserviceName = newServiceName\n\t}\n\tappInstall.ServiceName = serviceName\n\n\tif err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil {\n\t\treturn\n\t}\n\tvar (\n\t\tcomposeByte []byte\n\t\tparamByte   []byte\n\t)\n\n\tcomposeByte, err = yaml.Marshal(composeMap)\n\tif err != nil {\n\t\treturn\n\t}\n\tappInstall.DockerCompose = string(composeByte)\n\n\tif hostName, ok := req.Params[\"PANEL_DB_HOST\"]; ok {\n\t\tdatabase, _ := databaseRepo.Get(repo.WithByName(hostName.(string)))\n\t\tif !reflect.DeepEqual(database, model.Database{}) {\n\t\t\treq.Params[\"PANEL_DB_HOST\"] = database.Address\n\t\t\treq.Params[\"PANEL_DB_PORT\"] = database.Port\n\t\t\treq.Params[\"PANEL_DB_HOST_NAME\"] = hostName\n\t\t\treq.Params[\"DATABASE_NAME\"] = database.Name\n\t\t}\n\t}\n\tif app.Key == \"openresty\" {\n\t\treq.Params[\"CONTAINER_PACKAGE_URL\"] = \"http://archive.ubuntu.com/ubuntu/\"\n\t\treq.Params[\"RESTY_ADD_PACKAGE_BUILDDEPS\"] = \"\"\n\t\treq.Params[\"RESTY_CONFIG_OPTIONS_MORE\"] = \"\"\n\t}\n\tif app.Key == \"openresty\" && (app.Resource == \"remote\" || app.Resource == \"custom\") && common.CompareVersion(appDetail.Version, \"1.27\") {\n\t\tif dir, ok := req.Params[\"WEBSITE_DIR\"]; ok {\n\t\t\tsiteDir := dir.(string)\n\t\t\tif siteDir == \"\" || !strings.HasPrefix(siteDir, \"/\") {\n\t\t\t\tsiteDir = path.Join(global.Dir.DataDir, dir.(string))\n\t\t\t}\n\t\t\treq.Params[\"WEBSITE_DIR\"] = siteDir\n\t\t}\n\t}\n\tparamByte, err = json.Marshal(req.Params)\n\tif err != nil {\n\t\treturn\n\t}\n\tappInstall.Env = string(paramByte)\n\n\tvar maxSort int\n\tglobal.DB.Model(&model.AppInstall{}).Where(\"favorite = ?\", false).Select(\"COALESCE(MAX(sort_order),0)\").Scan(&maxSort)\n\tappInstall.SortOrder = maxSort + 1\n\n\tif err = appInstallRepo.Create(context.Background(), appInstall); err != nil {\n\t\treturn\n\t}\n\n\tinstallTask, err := task.NewTaskWithOps(appInstall.Name, task.TaskInstall, task.TaskScopeApp, req.TaskID, appInstall.ID)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif err = createLink(context.Background(), installTask, app, appInstall, req.Params); err != nil {\n\t\treturn\n\t}\n\n\tinstallApp := func(t *task.Task) error {\n\t\tif err = copyData(t, app, appDetail, appInstall, req); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif hooks != nil && hooks.AfterCopyData != nil {\n\t\t\tif err = hooks.AfterCopyData(appInstall); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif executeScript {\n\t\t\tif err = runScript(t, appInstall, \"init\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif app.Key == \"openresty\" {\n\t\t\tif err = handleSiteDir(app, appDetail, req, t); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err = handleOpenrestyFile(appInstall); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err = upApp(t, appInstall, req.PullImage); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tupdateToolApp(appInstall)\n\t\treturn nil\n\t}\n\n\thandleAppStatus := func(t *task.Task) {\n\t\tappInstall.Status = constant.StatusUpErr\n\t\tappInstall.Message = installTask.Task.ErrorMsg\n\t\t_ = appInstallRepo.Save(context.Background(), appInstall)\n\t}\n\n\tinstallTask.AddSubTask(task.GetTaskName(appInstall.Name, task.TaskInstall, task.TaskScopeApp), installApp, handleAppStatus)\n\n\tgo func() {\n\t\tif taskErr := installTask.Execute(); taskErr != nil {\n\t\t\tappInstall.Status = constant.StatusInstallErr\n\t\t\tappInstall.Message = taskErr.Error()\n\t\t\tif strings.Contains(taskErr.Error(), \"Timeout\") && strings.Contains(taskErr.Error(), \"Pulling\") {\n\t\t\t\tappInstall.Message = buserr.New(\"PullImageTimeout\").Error() + appInstall.Message\n\t\t\t}\n\t\t\t_ = appInstallRepo.Save(context.Background(), appInstall)\n\t\t}\n\t}()\n\n\treturn\n}\n\nfunc (a AppService) SyncAppListFromLocal(TaskID string) {\n\tvar (\n\t\terr        error\n\t\tdirEntries []os.DirEntry\n\t\tlocalApps  []model.App\n\t)\n\n\tsyncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey(\"LocalApp\"), task.TaskSync, task.TaskScopeAppStore, TaskID, 0)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Create sync task failed %v\", err)\n\t\treturn\n\t}\n\n\tsyncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey(\"LocalApp\"), task.TaskSync, task.TaskScopeAppStore), func(t *task.Task) (err error) {\n\t\tfileOp := files.NewFileOp()\n\t\tlocalAppDir := global.Dir.LocalAppResourceDir\n\t\tif !fileOp.Stat(localAppDir) {\n\t\t\treturn nil\n\t\t}\n\t\tdirEntries, err = os.ReadDir(localAppDir)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tfor _, dirEntry := range dirEntries {\n\t\t\tif dirEntry.IsDir() {\n\t\t\t\tappDir := filepath.Join(localAppDir, dirEntry.Name())\n\t\t\t\tappDirEntries, err := os.ReadDir(appDir)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Log(i18n.GetWithNameAndErr(\"ErrAppDirNull\", dirEntry.Name(), err))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tapp, err := handleLocalApp(appDir)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Log(i18n.GetWithNameAndErr(\"LocalAppErr\", dirEntry.Name(), err))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tvar appDetails []model.AppDetail\n\t\t\t\tfor _, appDirEntry := range appDirEntries {\n\t\t\t\t\tif appDirEntry.IsDir() {\n\t\t\t\t\t\tappDetail := model.AppDetail{\n\t\t\t\t\t\t\tVersion: appDirEntry.Name(),\n\t\t\t\t\t\t\tStatus:  constant.AppNormal,\n\t\t\t\t\t\t}\n\t\t\t\t\t\tversionDir := filepath.Join(appDir, appDirEntry.Name())\n\t\t\t\t\t\tif err = handleLocalAppDetail(versionDir, &appDetail); err != nil {\n\t\t\t\t\t\t\tt.Log(i18n.GetMsgWithMap(\"LocalAppVersionErr\", map[string]interface{}{\"name\": app.Name, \"version\": appDetail.Version, \"err\": err.Error()}))\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tappDetails = append(appDetails, appDetail)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif len(appDetails) > 0 {\n\t\t\t\t\tapp.Details = appDetails\n\t\t\t\t\tlocalApps = append(localApps, *app)\n\t\t\t\t} else {\n\t\t\t\t\tt.Log(i18n.GetWithName(\"LocalAppVersionNull\", app.Name))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvar (\n\t\t\tnewApps    []model.App\n\t\t\tdeleteApps []model.App\n\t\t\tupdateApps []model.App\n\t\t\toldAppIds  []uint\n\n\t\t\tdeleteAppIds     []uint\n\t\t\tdeleteAppDetails []model.AppDetail\n\t\t\tnewAppDetails    []model.AppDetail\n\t\t\tupdateDetails    []model.AppDetail\n\n\t\t\tappTags []*model.AppTag\n\t\t)\n\n\t\toldApps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceLocal))\n\t\tapps := make(map[string]model.App, len(oldApps))\n\t\tfor _, old := range oldApps {\n\t\t\told.Status = constant.AppTakeDown\n\t\t\tapps[old.Key] = old\n\t\t}\n\t\tfor _, app := range localApps {\n\t\t\tif oldApp, ok := apps[app.Key]; ok {\n\t\t\t\tapp.ID = oldApp.ID\n\t\t\t\tappDetails := make(map[string]model.AppDetail, len(oldApp.Details))\n\t\t\t\tfor _, old := range oldApp.Details {\n\t\t\t\t\told.Status = constant.AppTakeDown\n\t\t\t\t\tappDetails[old.Version] = old\n\t\t\t\t}\n\t\t\t\tfor i, newDetail := range app.Details {\n\t\t\t\t\tversion := newDetail.Version\n\t\t\t\t\tnewDetail.Status = constant.AppNormal\n\t\t\t\t\tnewDetail.AppId = app.ID\n\t\t\t\t\toldDetail, exist := appDetails[version]\n\t\t\t\t\tif exist {\n\t\t\t\t\t\tnewDetail.ID = oldDetail.ID\n\t\t\t\t\t\tdelete(appDetails, version)\n\t\t\t\t\t}\n\t\t\t\t\tapp.Details[i] = newDetail\n\t\t\t\t}\n\t\t\t\tfor _, v := range appDetails {\n\t\t\t\t\tapp.Details = append(app.Details, v)\n\t\t\t\t}\n\t\t\t}\n\t\t\tapp.TagsKey = append(app.TagsKey, constant.AppResourceLocal)\n\t\t\tapps[app.Key] = app\n\t\t}\n\n\t\tfor _, app := range apps {\n\t\t\tif app.ID == 0 {\n\t\t\t\tnewApps = append(newApps, app)\n\t\t\t} else {\n\t\t\t\toldAppIds = append(oldAppIds, app.ID)\n\t\t\t\tif app.Status == constant.AppTakeDown {\n\t\t\t\t\tinstalls, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(app.ID))\n\t\t\t\t\tif len(installs) > 0 {\n\t\t\t\t\t\tupdateApps = append(updateApps, app)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tdeleteAppIds = append(deleteAppIds, app.ID)\n\t\t\t\t\tdeleteApps = append(deleteApps, app)\n\t\t\t\t\tdeleteAppDetails = append(deleteAppDetails, app.Details...)\n\t\t\t\t} else {\n\t\t\t\t\tupdateApps = append(updateApps, app)\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\ttags, _ := tagRepo.All()\n\t\ttagMap := make(map[string]uint, len(tags))\n\t\tfor _, tag := range tags {\n\t\t\ttagMap[tag.Key] = tag.ID\n\t\t}\n\n\t\ttx, ctx := getTxAndContext()\n\t\tdefer tx.Rollback()\n\t\tif len(newApps) > 0 {\n\t\t\tif err = appRepo.BatchCreate(ctx, newApps); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tfor _, update := range updateApps {\n\t\t\tif err = appRepo.Save(ctx, &update); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif len(deleteApps) > 0 {\n\t\t\tif err = appRepo.BatchDelete(ctx, deleteApps); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err = appDetailRepo.DeleteByAppIds(ctx, deleteAppIds); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {\n\t\t\treturn\n\t\t}\n\t\tfor _, newApp := range newApps {\n\t\t\tif newApp.ID > 0 {\n\t\t\t\tfor _, detail := range newApp.Details {\n\t\t\t\t\tdetail.AppId = newApp.ID\n\t\t\t\t\tnewAppDetails = append(newAppDetails, detail)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor _, update := range updateApps {\n\t\t\tfor _, detail := range update.Details {\n\t\t\t\tif detail.ID == 0 {\n\t\t\t\t\tdetail.AppId = update.ID\n\t\t\t\t\tnewAppDetails = append(newAppDetails, detail)\n\t\t\t\t} else {\n\t\t\t\t\tif detail.Status == constant.AppNormal {\n\t\t\t\t\t\tupdateDetails = append(updateDetails, detail)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdeleteAppDetails = append(deleteAppDetails, detail)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tallApps := append(newApps, updateApps...)\n\t\tfor _, app := range allApps {\n\t\t\tfor _, t := range app.TagsKey {\n\t\t\t\ttagId, ok := tagMap[t]\n\t\t\t\tif ok {\n\t\t\t\t\tappTags = append(appTags, &model.AppTag{\n\t\t\t\t\t\tAppId: app.ID,\n\t\t\t\t\t\tTagId: tagId,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif len(newAppDetails) > 0 {\n\t\t\tif err = appDetailRepo.BatchCreate(ctx, newAppDetails); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tfor _, updateAppDetail := range updateDetails {\n\t\t\tif err = appDetailRepo.Update(ctx, updateAppDetail); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif len(deleteAppDetails) > 0 {\n\t\t\tif err = appDetailRepo.BatchDelete(ctx, deleteAppDetails); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif len(oldAppIds) > 0 {\n\t\t\tif err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif len(appTags) > 0 {\n\t\t\tif err = appTagRepo.BatchCreate(ctx, appTags); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\ttx.Commit()\n\t\tglobal.LOG.Infof(\"Synchronization of local applications completed\")\n\t\treturn nil\n\t}, nil)\n\tgo func() {\n\t\t_ = syncTask.Execute()\n\t}()\n}\n\nfunc (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {\n\tres := &response.AppUpdateRes{\n\t\tCanUpdate: false,\n\t}\n\tmysql, _ := appRepo.GetFirst(appRepo.WithKey(\"mysql\"))\n\tif !mysql.BatchInstallSupport {\n\t\tres.CanUpdate = true\n\t\treturn res, nil\n\t}\n\n\tversionUrl := fmt.Sprintf(\"%s/%s/1panel.json.version.txt\", global.AppRepoURL(), global.CONF.Base.Mode)\n\t_, versionRes, err := req_helper.HandleRequest(versionUrl, http.MethodGet, constant.TimeOut20s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlastModifiedStr := string(versionRes)\n\tlastModified, err := strconv.Atoi(lastModifiedStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsetting, err := NewISettingService().GetSettingInfo()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif setting.AppStoreSyncStatus == constant.StatusSyncing {\n\t\tres.IsSyncing = true\n\t\treturn res, nil\n\t}\n\n\tappStoreLastModified, _ := strconv.Atoi(setting.AppStoreLastModified)\n\tres.AppStoreLastModified = appStoreLastModified\n\tif setting.AppStoreLastModified == \"\" || lastModified != appStoreLastModified {\n\t\tres.CanUpdate = true\n\t\treturn res, err\n\t}\n\tapps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote))\n\tfor _, app := range apps {\n\t\tif app.Icon == \"\" {\n\t\t\tres.CanUpdate = true\n\t\t\treturn res, err\n\t\t}\n\t\tif appicon.IsIconFile(app.Icon) {\n\t\t\tfileName, _ := appicon.ParseIconField(app.Icon)\n\t\t\tif fileName == \"\" || !appicon.IconFileExists(fileName) {\n\t\t\t\tres.CanUpdate = true\n\t\t\t\treturn res, err\n\t\t\t}\n\t\t}\n\t}\n\n\tlist, err := getAppList()\n\tif err != nil {\n\t\treturn res, err\n\t}\n\tif list.Extra.Version != \"\" && setting.SystemVersion != list.Extra.Version && !common.CompareVersion(setting.SystemVersion, list.Extra.Version) {\n\t\tglobal.LOG.Errorf(\"The current version %s is too low to synchronize with the App Store. The minimum required version is %s\", setting.SystemVersion, list.Extra.Version)\n\t\treturn nil, buserr.New(\"ErrVersionTooLow\")\n\t}\n\tres.AppList = list\n\treturn res, nil\n}\n\nfunc getAppFromRepo(downloadPath string) error {\n\tdownloadUrl := downloadPath\n\tglobal.LOG.Infof(\"[AppStore] download file from %s\", downloadUrl)\n\tfileOp := files.NewFileOp()\n\tpackagePath := filepath.Join(global.Dir.ResourceDir, filepath.Base(downloadUrl))\n\tif err := files.DownloadFileWithProxy(downloadUrl, packagePath); err != nil {\n\t\treturn err\n\t}\n\n\tif err := fileOp.Decompress(packagePath, global.Dir.ResourceDir, files.SdkZip, \"\"); err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t_ = fileOp.DeleteFile(packagePath)\n\t}()\n\treturn nil\n}\n\nfunc getAppList() (*dto.AppList, error) {\n\tlist := &dto.AppList{}\n\tif err := getAppFromRepo(fmt.Sprintf(\"%s/%s/1panel.json.zip\", global.AppRepoURL(), global.CONF.Base.Mode)); err != nil {\n\t\treturn nil, err\n\t}\n\tlistFile := filepath.Join(global.Dir.ResourceDir, \"1panel.json\")\n\tfile, err := os.Open(listFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\tif err = json.NewDecoder(file).Decode(list); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn list, nil\n}\n\nvar InitTypes = map[string]struct{}{\n\t\"runtime\": {},\n\t\"php\":     {},\n\t\"node\":    {},\n}\n\nfunc deleteCustomApp() {\n\tinstalls, err := appInstallRepo.ListBy(context.Background())\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"[AppStore] deleteCustomApp: failed to list installs, skipping: %v\", err)\n\t\treturn\n\t}\n\tvar appIDS []uint\n\tfor _, install := range installs {\n\t\tappIDS = append(appIDS, install.AppId)\n\t}\n\tvar ops []repo.DBOption\n\tif len(appIDS) > 0 {\n\t\tops = append(ops, repo.WithByIDNotIn(appIDS))\n\t}\n\tapps, err := appRepo.GetBy(ops...)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"[AppStore] deleteCustomApp: failed to get apps, skipping: %v\", err)\n\t\treturn\n\t}\n\tvar deleteIDS []uint\n\tfor _, app := range apps {\n\t\tif app.Resource == constant.AppResourceCustom {\n\t\t\tdeleteIDS = append(deleteIDS, app.ID)\n\t\t}\n\t}\n\tif len(deleteIDS) == 0 {\n\t\treturn\n\t}\n\tif err = appRepo.DeleteByIDs(context.Background(), deleteIDS); err != nil {\n\t\tglobal.LOG.Errorf(\"[AppStore] deleteCustomApp: failed to delete apps: %v\", err)\n\t}\n\tif err = appDetailRepo.DeleteByAppIds(context.Background(), deleteIDS); err != nil {\n\t\tglobal.LOG.Errorf(\"[AppStore] deleteCustomApp: failed to delete app details: %v\", err)\n\t}\n}\n\nfunc (a AppService) SyncAppListFromRemote(taskID string) (err error) {\n\tif xpack.IsUseCustomApp() {\n\t\treturn nil\n\t}\n\n\tappStoreSyncMu.Lock()\n\tglobal.LOG.Info(\"[AppStore] sync app from remote task create start\")\n\tif appStoreSyncing {\n\t\tappStoreSyncMu.Unlock()\n\t\tglobal.LOG.Info(\"[AppStore] sync already in progress, skipping\")\n\t\treturn nil\n\t}\n\tappStoreSyncing = true\n\tappStoreSyncMu.Unlock()\n\n\tsyncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey(\"App\"), task.TaskSync, task.TaskScopeAppStore, taskID, 0)\n\tif err != nil {\n\t\tappStoreSyncMu.Lock()\n\t\tappStoreSyncing = false\n\t\tappStoreSyncMu.Unlock()\n\t\treturn err\n\t}\n\n\tvar sharedCtx *appSyncContext\n\n\tsyncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey(\"App\"), task.TaskSync, task.TaskScopeAppStore), a.createSyncAppStoreTask(&sharedCtx), nil)\n\tsyncTask.AddSubTask(i18n.GetMsgByKey(\"SyncAppDetail\"), a.createSyncAppStoreMetaTask(&sharedCtx), nil)\n\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tglobal.LOG.Errorf(\"[AppStore] sync goroutine recovered from panic: %v\", r)\n\t\t\t\tif updateErr := NewISettingService().Update(\"AppStoreSyncStatus\", constant.StatusError); updateErr != nil {\n\t\t\t\t\tglobal.LOG.Warnf(\"[AppStore] failed to update sync status after panic: %v\", updateErr)\n\t\t\t\t}\n\t\t\t}\n\t\t\tappStoreSyncMu.Lock()\n\t\t\tappStoreSyncing = false\n\t\t\tappStoreSyncMu.Unlock()\n\t\t}()\n\t\tif err := syncTask.Execute(); err != nil {\n\t\t\tif updateErr := NewISettingService().Update(\"AppStoreLastModified\", \"0\"); updateErr != nil {\n\t\t\t\tglobal.LOG.Warnf(\"[AppStore] failed to reset last modified: %v\", updateErr)\n\t\t\t}\n\t\t\tif updateErr := NewISettingService().Update(\"AppStoreSyncStatus\", constant.StatusError); updateErr != nil {\n\t\t\t\tglobal.LOG.Warnf(\"[AppStore] failed to update sync status to error: %v\", updateErr)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}()\n\n\tglobal.LOG.Info(\"[AppStore] sync app from remote task create ok\")\n\treturn nil\n}\n\nfunc (a AppService) GetAppIcon(key string) ([]byte, string, string, error) {\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(key))\n\tif err != nil {\n\t\treturn nil, \"\", \"\", err\n\t}\n\n\tif appicon.IsIconFile(app.Icon) {\n\t\tfileName, etag := appicon.ParseIconField(app.Icon)\n\t\ticonBytes, err := appicon.ReadIconFile(fileName)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Warnf(\"[AppIcon] read icon file failed key=%s, file=%s, err=%v\", key, fileName, err)\n\t\t\treturn nil, \"\", \"\", nil\n\t\t}\n\t\treturn iconBytes, fileName, etag, nil\n\t}\n\n\ticonBytes, err := base64.StdEncoding.DecodeString(app.Icon)\n\tif err != nil {\n\t\tglobal.LOG.Warnf(\"[AppIcon] decode base64 icon failed key=%s, err=%v\", key, err)\n\t\treturn nil, \"\", \"\", nil\n\t}\n\treturn iconBytes, \"\", \"\", nil\n}\n"
  },
  {
    "path": "agent/app/service/app_ingore_upgrade.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/pkg/errors\"\n\t\"gorm.io/gorm\"\n)\n\ntype AppIgnoreUpgradeService struct {\n}\n\ntype IAppIgnoreUpgradeService interface {\n\tList() ([]response.AppIgnoreUpgradeDTO, error)\n\tCreateAppIgnore(req request.AppIgnoreUpgradeReq) error\n\tDelete(req request.ReqWithID) error\n}\n\nfunc NewIAppIgnoreUpgradeService() IAppIgnoreUpgradeService {\n\treturn AppIgnoreUpgradeService{}\n}\n\nfunc (a AppIgnoreUpgradeService) List() ([]response.AppIgnoreUpgradeDTO, error) {\n\tvar res []response.AppIgnoreUpgradeDTO\n\tignores, err := appIgnoreUpgradeRepo.List()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, ignore := range ignores {\n\t\tdto := response.AppIgnoreUpgradeDTO{\n\t\t\tID:          ignore.ID,\n\t\t\tAppID:       ignore.AppID,\n\t\t\tAppDetailID: ignore.AppDetailID,\n\t\t\tScope:       ignore.Scope,\n\t\t}\n\t\tapp, err := appRepo.GetFirst(repo.WithByID(ignore.AppID))\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t_ = appIgnoreUpgradeRepo.Delete(repo.WithByID(ignore.ID))\n\t\t\tcontinue\n\t\t}\n\t\tdto.Name = app.Name\n\t\tif ignore.Scope == \"version\" {\n\t\t\tappDetail, err := appDetailRepo.GetFirst(repo.WithByID(ignore.AppDetailID))\n\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\t_ = appIgnoreUpgradeRepo.Delete(repo.WithByID(ignore.ID))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdto.Version = appDetail.Version\n\t\t}\n\t\tres = append(res, dto)\n\t}\n\treturn res, nil\n}\n\nfunc (a AppIgnoreUpgradeService) CreateAppIgnore(req request.AppIgnoreUpgradeReq) error {\n\tappIgnoreUpgrade := model.AppIgnoreUpgrade{\n\t\tAppID: req.AppID,\n\t\tScope: req.Scope,\n\t}\n\tif req.Scope == \"version\" {\n\t\tappIgnoreUpgrade.AppDetailID = req.AppDetailID\n\t}\n\tif req.Scope == \"all\" {\n\t\t_ = appIgnoreUpgradeRepo.Delete(appInstallRepo.WithAppId(req.AppID))\n\t}\n\tif err := appIgnoreUpgradeRepo.Create(&appIgnoreUpgrade); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (a AppIgnoreUpgradeService) Delete(req request.ReqWithID) error {\n\treturn appIgnoreUpgradeRepo.Delete(repo.WithByID(req.ID))\n}\n"
  },
  {
    "path": "agent/app/service/app_install.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/env\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/req_helper\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/joho/godotenv\"\n\t\"github.com/pkg/errors\"\n\t\"gopkg.in/yaml.v3\"\n)\n\ntype AppInstallService struct {\n}\n\ntype IAppInstallService interface {\n\tPage(req request.AppInstalledSearch) (int64, []response.AppInstallDTO, error)\n\tCheckExist(req request.AppInstalledInfo) (*response.AppInstalledCheck, error)\n\tLoadPort(req dto.OperationWithNameAndType) (int64, error)\n\tLoadConnInfo(req dto.OperationWithNameAndType) (response.DatabaseConn, error)\n\tSearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstallDTO, error)\n\tOperate(req request.AppInstalledOperate) error\n\tUpdate(req request.AppInstalledUpdate) error\n\tSyncAll(systemInit bool) error\n\tGetServices(key string) ([]response.AppService, error)\n\tGetUpdateVersions(req request.AppUpdateVersion) ([]dto.AppVersion, error)\n\tGetParams(id uint) (*response.AppConfig, error)\n\tChangeAppPort(req request.PortUpdate) error\n\tGetDefaultConfigByKey(key, name string) (string, error)\n\tDeleteCheck(installId uint) ([]dto.AppResource, error)\n\n\tUpdateAppConfig(req request.AppConfigUpdate) error\n\tGetInstallList() ([]dto.AppInstallInfo, error)\n\tGetAppInstallInfo(appInstallID uint) (*response.AppInstallInfo, error)\n\tUpdateSort(req request.AppInstallSort) error\n}\n\nfunc NewIAppInstalledService() IAppInstallService {\n\treturn &AppInstallService{}\n}\n\nfunc (a *AppInstallService) GetInstallList() ([]dto.AppInstallInfo, error) {\n\tvar datas []dto.AppInstallInfo\n\tappInstalls, err := appInstallRepo.ListBy(context.Background())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, install := range appInstalls {\n\t\tdatas = append(datas, dto.AppInstallInfo{ID: install.ID, Key: install.App.Key, Name: install.Name})\n\t}\n\treturn datas, nil\n}\n\nfunc (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstallDTO, error) {\n\tvar (\n\t\topts     []repo.DBOption\n\t\ttotal    int64\n\t\tinstalls []model.AppInstall\n\t\terr      error\n\t)\n\topts = append(opts, repo.WithOrderRuleBy(\"favorite\", \"descending\"))\n\topts = append(opts, repo.WithOrderRuleBy(\"sort_order\", \"ascending\"))\n\n\tif req.Name != \"\" {\n\t\topts = append(opts, repo.WithByLikeName(req.Name))\n\t}\n\n\tif len(req.Tags) != 0 {\n\t\ttags, err := tagRepo.GetByKeys(req.Tags)\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tvar tagIds []uint\n\t\tfor _, t := range tags {\n\t\t\ttagIds = append(tagIds, t.ID)\n\t\t}\n\t\tappTags, err := appTagRepo.GetByTagIds(tagIds)\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tvar appIds []uint\n\t\tfor _, t := range appTags {\n\t\t\tappIds = append(appIds, t.AppId)\n\t\t}\n\n\t\topts = append(opts, appInstallRepo.WithAppIdsIn(appIds))\n\t}\n\n\tif req.Update {\n\t\tinstalls, err = appInstallRepo.ListBy(context.Background(), opts...)\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t} else {\n\t\ttotal, installs, err = appInstallRepo.Page(req.Page, req.PageSize, opts...)\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t}\n\n\tinstallDTOs, _ := handleInstalled(installs, req.Update, req.Sync, req.CheckUpdate)\n\tif req.Update {\n\t\ttotal = int64(len(installDTOs))\n\t}\n\treturn total, installDTOs, nil\n}\n\nfunc (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.AppInstalledCheck, error) {\n\tres := &response.AppInstalledCheck{\n\t\tIsExist: false,\n\t}\n\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(req.Key))\n\tif err != nil {\n\t\treturn res, nil\n\t}\n\tres.App = app.Name\n\n\tvar appInstall model.AppInstall\n\tif len(req.Name) == 0 {\n\t\tappInstall, _ = appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID))\n\t} else {\n\t\tappInstall, _ = appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID), repo.WithByName(req.Name))\n\t}\n\n\tif reflect.DeepEqual(appInstall, model.AppInstall{}) {\n\t\treturn res, nil\n\t}\n\tif err = syncAppInstallStatus(&appInstall, false); err != nil {\n\t\treturn nil, err\n\t}\n\n\tres.ContainerName = appInstall.ContainerName\n\tres.Name = appInstall.Name\n\tres.Version = appInstall.Version\n\tres.CreatedAt = appInstall.CreatedAt\n\tres.Status = appInstall.Status\n\tres.AppInstallID = appInstall.ID\n\tres.IsExist = true\n\tres.InstallPath = path.Join(global.Dir.AppInstallDir, appInstall.App.Key, appInstall.Name)\n\tres.HttpPort = appInstall.HttpPort\n\tres.HttpsPort = appInstall.HttpsPort\n\n\tif appInstall.App.Key == \"openresty\" {\n\t\twebsiteDir, _ := settingRepo.GetValueByKey(\"WEBSITE_DIR\")\n\t\tres.WebsiteDir = websiteDir\n\t}\n\n\treturn res, nil\n}\n\nfunc (a *AppInstallService) LoadPort(req dto.OperationWithNameAndType) (int64, error) {\n\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn int64(0), nil\n\t}\n\treturn app.Port, nil\n}\n\nfunc (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (response.DatabaseConn, error) {\n\tvar data response.DatabaseConn\n\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn data, nil\n\t}\n\tdata.Status = app.Status\n\tdata.Username = app.UserName\n\tdata.Password = app.Password\n\tdata.ServiceName = app.ServiceName\n\tdata.Port = app.Port\n\tdata.ContainerName = app.ContainerName\n\treturn data, nil\n}\n\nfunc (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstallDTO, error) {\n\tvar (\n\t\tinstalls []model.AppInstall\n\t\terr      error\n\t\topts     []repo.DBOption\n\t)\n\tif req.Type != \"\" {\n\t\tif req.Type == \"proxy\" {\n\t\t\tphpApps, _ := appRepo.GetBy(appRepo.WithType(\"php\"))\n\t\t\tvar ids []uint\n\t\t\tfor _, app := range phpApps {\n\t\t\t\tids = append(ids, app.ID)\n\t\t\t}\n\t\t\truntimeApps, _ := appRepo.GetBy(appRepo.WithType(\"runtime\"))\n\t\t\tfor _, app := range runtimeApps {\n\t\t\t\tids = append(ids, app.ID)\n\t\t\t}\n\t\t\topts = append(opts, appInstallRepo.WithAppIdsNotIn(ids))\n\t\t} else {\n\t\t\tapps, err := appRepo.GetBy(appRepo.WithType(req.Type))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tvar ids []uint\n\t\t\tfor _, app := range apps {\n\t\t\t\tids = append(ids, app.ID)\n\t\t\t}\n\t\t\tif req.Unused {\n\t\t\t\topts = append(opts, appInstallRepo.WithIdNotInWebsite())\n\t\t\t}\n\t\t\topts = append(opts, appInstallRepo.WithAppIdsIn(ids))\n\t\t}\n\t\tinstalls, err = appInstallRepo.ListBy(context.Background(), opts...)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tinstalls, err = appInstallRepo.ListBy(context.Background())\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn handleInstalled(installs, false, true, false)\n}\n\nfunc (a *AppInstallService) Operate(req request.AppInstalledOperate) error {\n\tinstall, err := appInstallRepo.GetFirstByCtx(context.Background(), repo.WithByID(req.InstallId))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !req.ForceDelete && !files.NewFileOp().Stat(install.GetPath()) {\n\t\treturn buserr.New(\"ErrInstallDirNotFound\")\n\t}\n\tdockerComposePath := install.GetComposePath()\n\tswitch req.Operate {\n\tcase constant.Rebuild:\n\t\treturn rebuildApp(install)\n\tcase constant.Start:\n\t\tout, err := compose.Up(dockerComposePath)\n\t\tif err != nil {\n\t\t\treturn handleErr(install, err, out)\n\t\t}\n\t\treturn syncAppInstallStatus(&install, false)\n\tcase constant.Stop:\n\t\tout, err := compose.Stop(dockerComposePath)\n\t\tif err != nil {\n\t\t\treturn handleErr(install, err, out)\n\t\t}\n\t\treturn syncAppInstallStatus(&install, false)\n\tcase constant.Restart:\n\t\tout, err := compose.Restart(dockerComposePath)\n\t\tif err != nil {\n\t\t\treturn handleErr(install, err, out)\n\t\t}\n\t\treturn syncAppInstallStatus(&install, false)\n\tcase constant.Delete:\n\t\tdeleteReq := request.AppInstallDelete{\n\t\t\tInstall:      install,\n\t\t\tDeleteBackup: req.DeleteBackup,\n\t\t\tForceDelete:  req.ForceDelete,\n\t\t\tDeleteDB:     req.DeleteDB,\n\t\t\tDeleteImage:  req.DeleteImage,\n\t\t\tTaskID:       req.TaskID,\n\t\t}\n\t\tif err = deleteAppInstall(deleteReq); err != nil && !req.ForceDelete {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\tcase constant.Sync:\n\t\treturn syncAppInstallStatus(&install, true)\n\tcase constant.Upgrade:\n\t\tupgradeReq := request.AppInstallUpgrade{\n\t\t\tInstallID:     install.ID,\n\t\t\tDetailID:      req.DetailId,\n\t\t\tBackup:        req.Backup,\n\t\t\tPullImage:     req.PullImage,\n\t\t\tDockerCompose: req.DockerCompose,\n\t\t\tTaskID:        req.TaskID,\n\t\t}\n\t\treturn upgradeInstall(upgradeReq)\n\tcase constant.Reload:\n\t\treturn opNginx(install.ContainerName, constant.NginxReload)\n\tcase constant.Favorite:\n\t\tinstall.Favorite = req.Favorite\n\t\tvar maxSort int\n\t\tglobal.DB.Model(&model.AppInstall{}).Where(\"favorite = ?\", req.Favorite).Select(\"COALESCE(MAX(sort_order),0)\").Scan(&maxSort)\n\t\tinstall.SortOrder = maxSort + 1\n\t\treturn appInstallRepo.Save(context.Background(), &install)\n\tdefault:\n\t\treturn errors.New(\"operate not support\")\n\t}\n}\n\nfunc (a *AppInstallService) UpdateAppConfig(req request.AppConfigUpdate) error {\n\tinstalled, err := appInstallRepo.GetFirst(repo.WithByID(req.InstallID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstalled.WebUI = \"\"\n\tif req.WebUI != \"\" {\n\t\tinstalled.WebUI = req.WebUI\n\t}\n\treturn appInstallRepo.Save(context.Background(), &installed)\n}\n\nfunc (a *AppInstallService) UpdateSort(req request.AppInstallSort) error {\n\tfor _, item := range req.Items {\n\t\tif err := appInstallRepo.BatchUpdateBy(map[string]interface{}{\"sort_order\": item.SortOrder}, repo.WithByID(item.InstallID)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (a *AppInstallService) Update(req request.AppInstalledUpdate) error {\n\tinstalled, err := appInstallRepo.GetFirst(repo.WithByID(req.InstallId))\n\tif err != nil {\n\t\treturn err\n\t}\n\toldInstalled := installed\n\tport, ok := req.Params[\"PANEL_APP_PORT_HTTP\"]\n\tif ok {\n\t\tportN := int(math.Ceil(port.(float64)))\n\t\tif portN != installed.HttpPort {\n\t\t\thttpPort, err := checkPort(\"PANEL_APP_PORT_HTTP\", req.Params)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tinstalled.HttpPort = httpPort\n\t\t}\n\t}\n\tports, ok := req.Params[\"PANEL_APP_PORT_HTTPS\"]\n\tif ok {\n\t\tportN := int(math.Ceil(ports.(float64)))\n\t\tif portN != installed.HttpsPort {\n\t\t\thttpsPort, err := checkPort(\"PANEL_APP_PORT_HTTPS\", req.Params)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tinstalled.HttpsPort = httpsPort\n\t\t}\n\t}\n\n\tbackupDockerCompose := installed.DockerCompose\n\tif req.Advanced {\n\t\tcomposeMap := make(map[string]interface{})\n\t\tif req.EditCompose {\n\t\t\tif err = yaml.Unmarshal([]byte(req.DockerCompose), &composeMap); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif err = yaml.Unmarshal([]byte(installed.DockerCompose), &composeMap); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err = addDockerComposeCommonParam(composeMap, installed.ServiceName, req.AppContainerConfig, req.Params); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcomposeByte, err := yaml.Marshal(composeMap)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinstalled.DockerCompose = string(composeByte)\n\t\tif req.ContainerName == \"\" {\n\t\t\treq.Params[constant.ContainerName] = installed.ContainerName\n\t\t} else {\n\t\t\treq.Params[constant.ContainerName] = req.ContainerName\n\t\t\tif installed.ContainerName != req.ContainerName {\n\t\t\t\texist, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(req.ContainerName), appInstallRepo.WithIDNotIs(installed.ID))\n\t\t\t\tif exist.ID > 0 {\n\t\t\t\t\treturn buserr.New(\"ErrContainerName\")\n\t\t\t\t}\n\t\t\t\tcontainerExist, err := checkContainerNameIsExist(req.ContainerName, installed.GetPath())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif containerExist {\n\t\t\t\t\treturn buserr.New(\"ErrContainerName\")\n\t\t\t\t}\n\t\t\t\tinstalled.ContainerName = req.ContainerName\n\t\t\t}\n\t\t}\n\t}\n\n\tenvPath := path.Join(installed.GetPath(), \".env\")\n\toldEnvMaps, err := godotenv.Read(envPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackupEnvMaps := oldEnvMaps\n\thandleMap(req.Params, oldEnvMaps)\n\tparamByte, err := json.Marshal(oldEnvMaps)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstalled.Env = string(paramByte)\n\tif err := env.Write(oldEnvMaps, envPath); err != nil {\n\t\treturn err\n\t}\n\tfileOp := files.NewFileOp()\n\t_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(installed.DockerCompose), constant.DirPerm)\n\tif err := rebuildApp(installed); err != nil {\n\t\t_ = env.Write(backupEnvMaps, envPath)\n\t\t_ = fileOp.WriteFile(installed.GetComposePath(), strings.NewReader(backupDockerCompose), constant.DirPerm)\n\t\treturn err\n\t}\n\tinstalled.Status = constant.StatusRunning\n\t_ = appInstallRepo.Save(context.Background(), &installed)\n\n\tproxyChanged := hasAppInstallProxyPassChanged(&oldInstalled, &installed)\n\tcurrentProxy, currentProxyErr := getAppInstallProxyPass(&installed)\n\twebsite, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID))\n\tif proxyChanged && website.ID != 0 && currentProxyErr == nil {\n\t\twebsite.Proxy = currentProxy\n\t\tif err := websiteRepo.SaveWithoutCtx(&website); err != nil {\n\t\t\tglobal.LOG.Error(buserr.WithErr(\"ErrUpdateBuWebsite\", err).Error())\n\t\t}\n\t}\n\tif proxyChanged && website.ID != 0 && website.Status == constant.StatusRunning && currentProxyErr == nil {\n\t\tgo func(website model.Website, proxy string) {\n\t\t\tnginxInstall, err := getNginxFull(&website)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Error(buserr.WithErr(\"ErrUpdateBuWebsite\", err).Error())\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconfig := nginxInstall.SiteConfig.Config\n\t\t\tservers := config.FindServers()\n\t\t\tif len(servers) == 0 {\n\t\t\t\tglobal.LOG.Error(buserr.WithErr(\"ErrUpdateBuWebsite\", errors.New(\"nginx config is not valid\")).Error())\n\t\t\t\treturn\n\t\t\t}\n\t\t\tserver := servers[0]\n\t\t\tserver.UpdateRootProxy([]string{proxy})\n\n\t\t\tif err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\t\t\tglobal.LOG.Error(buserr.WithErr(\"ErrUpdateBuWebsite\", err).Error())\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName); err != nil {\n\t\t\t\tglobal.LOG.Error(buserr.WithErr(\"ErrUpdateBuWebsite\", err).Error())\n\t\t\t\treturn\n\t\t\t}\n\t\t}(website, currentProxy)\n\t}\n\tif proxyChanged && website.ID != 0 && currentProxyErr != nil {\n\t\tglobal.LOG.Error(buserr.WithErr(\"ErrUpdateBuWebsite\", currentProxyErr).Error())\n\t}\n\treturn nil\n}\n\nfunc (a *AppInstallService) SyncAll(systemInit bool) error {\n\tallList, err := appInstallRepo.ListBy(context.Background())\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, i := range allList {\n\t\tif i.Status == constant.StatusInstalling || i.Status == constant.StatusUpgrading || i.Status == constant.StatusRebuilding || i.Status == constant.StatusUninstalling {\n\t\t\tif systemInit {\n\t\t\t\ti.Status = constant.StatusError\n\t\t\t\ti.Message = \"1Panel restart causes the task to terminate\"\n\t\t\t\t_ = appInstallRepo.Save(context.Background(), &i)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif i.Status == constant.StatusWaitingRestart {\n\t\t\tdockerComposePath := i.GetComposePath()\n\t\t\t_, _ = compose.Up(dockerComposePath)\n\t\t\ti.Status = constant.StatusRunning\n\t\t\t_ = appInstallRepo.Save(context.Background(), &i)\n\t\t\tcontinue\n\t\t}\n\t\tif !systemInit {\n\t\t\tif err = syncAppInstallStatus(&i, false); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"sync install app[%s] error,mgs: %s\", i.Name, err.Error())\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {\n\tvar res []response.AppService\n\tif DatabaseKeys[key] > 0 {\n\t\ttypes := []string{key}\n\t\tif key == constant.AppPostgres {\n\t\t\tkey = constant.AppPostgresql\n\t\t}\n\t\tswitch key {\n\t\tcase constant.AppMysql:\n\t\t\ttypes = []string{constant.AppMysql, constant.AppMysqlCluster}\n\t\tcase constant.AppPostgresql:\n\t\t\ttypes = []string{constant.AppPostgresql, constant.AppPostgresqlCluster}\n\t\tcase constant.AppRedis:\n\t\t\ttypes = []string{constant.AppRedis, constant.AppRedisCluster}\n\t\t}\n\n\t\tdbs, _ := databaseRepo.GetList(repo.WithTypes(types))\n\t\tif len(dbs) == 0 {\n\t\t\treturn res, nil\n\t\t}\n\t\tfor _, db := range dbs {\n\t\t\tservice := response.AppService{\n\t\t\t\tLabel: db.Name,\n\t\t\t\tValue: db.Name,\n\t\t\t}\n\t\t\tif db.AppInstallID > 0 {\n\t\t\t\tinstall, err := appInstallRepo.GetFirst(repo.WithByID(db.AppInstallID))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tparamMap := make(map[string]string)\n\t\t\t\tif install.Param != \"\" {\n\t\t\t\t\t_ = json.Unmarshal([]byte(install.Param), &paramMap)\n\t\t\t\t}\n\t\t\t\tservice.Config = paramMap\n\t\t\t\tservice.From = constant.AppResourceLocal\n\t\t\t\tservice.Status = install.Status\n\t\t\t} else {\n\t\t\t\tservice.From = constant.AppResourceRemote\n\t\t\t\tservice.Status = constant.StatusRunning\n\t\t\t\tservice.Config = map[string]string{\"PANEL_DB_ROOT_PASSWORD\": db.Password, \"PANEL_DB_ROOT_USER\": db.Username}\n\t\t\t}\n\t\t\tres = append(res, service)\n\t\t}\n\t} else {\n\t\tapp, err := appRepo.GetFirst(appRepo.WithKey(key))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tinstalls, err := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(app.ID), appInstallRepo.WithStatus(constant.StatusRunning))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, install := range installs {\n\t\t\tparamMap := make(map[string]string)\n\t\t\tif install.Param != \"\" {\n\t\t\t\t_ = json.Unmarshal([]byte(install.Param), &paramMap)\n\t\t\t}\n\t\t\tres = append(res, response.AppService{\n\t\t\t\tLabel:  install.Name,\n\t\t\t\tValue:  install.ServiceName,\n\t\t\t\tConfig: paramMap,\n\t\t\t\tStatus: install.Status,\n\t\t\t})\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (a *AppInstallService) GetUpdateVersions(req request.AppUpdateVersion) ([]dto.AppVersion, error) {\n\tinstall, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))\n\tvar versions []dto.AppVersion\n\tif err != nil {\n\t\treturn versions, err\n\t}\n\tapp, err := appRepo.GetFirst(repo.WithByID(install.AppId))\n\tif err != nil {\n\t\treturn versions, err\n\t}\n\tdetails, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))\n\tif err != nil {\n\t\treturn versions, err\n\t}\n\tfor _, detail := range details {\n\t\tignores, _ := appIgnoreUpgradeRepo.List(runtimeRepo.WithDetailId(detail.ID), appIgnoreUpgradeRepo.WithScope(\"version\"))\n\t\tif len(ignores) > 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif common.IsCrossVersion(install.Version, detail.Version) && !app.CrossVersionUpdate {\n\t\t\tcontinue\n\t\t}\n\t\tif common.CompareVersion(detail.Version, install.Version) {\n\t\t\tvar newCompose string\n\t\t\tif req.UpdateVersion != \"\" && req.UpdateVersion == detail.Version && detail.DockerCompose == \"\" && !app.IsLocalApp() {\n\t\t\t\tfilename := filepath.Base(detail.DownloadUrl)\n\t\t\t\tdockerComposeUrl := fmt.Sprintf(\"%s%s\", strings.TrimSuffix(detail.DownloadUrl, filename), \"docker-compose.yml\")\n\t\t\t\tstatusCode, composeRes, err := req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn versions, err\n\t\t\t\t}\n\t\t\t\tif statusCode > 200 {\n\t\t\t\t\treturn versions, err\n\t\t\t\t}\n\t\t\t\tdetail.DockerCompose = string(composeRes)\n\t\t\t\t_ = appDetailRepo.Update(context.Background(), detail)\n\t\t\t}\n\t\t\tnewCompose, err = getUpgradeCompose(install, detail)\n\t\t\tif err != nil {\n\t\t\t\treturn versions, err\n\t\t\t}\n\t\t\tif app.Key == constant.AppMysql {\n\t\t\t\tmajorVersion := getMajorVersion(install.Version)\n\t\t\t\tif !strings.HasPrefix(detail.Version, majorVersion) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tversions = append(versions, dto.AppVersion{\n\t\t\t\tVersion:       detail.Version,\n\t\t\t\tDetailId:      detail.ID,\n\t\t\t\tDockerCompose: newCompose,\n\t\t\t})\n\t\t}\n\t}\n\tsort.Slice(versions, func(i, j int) bool {\n\t\treturn common.CompareVersion(versions[i].Version, versions[j].Version)\n\t})\n\treturn versions, nil\n}\n\nfunc (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {\n\tif common.ScanPort(int(req.Port)) {\n\t\treturn buserr.WithDetail(\"ErrPortInUsed\", req.Port, nil)\n\t}\n\n\tappInstall, err := appInstallRepo.LoadBaseInfo(req.Key, req.Name)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tif err := updateInstallInfoInDB(req.Key, req.Name, \"port\", strconv.FormatInt(req.Port, 10)); err != nil {\n\t\treturn nil\n\t}\n\n\tappRess, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(appInstall.ID))\n\tfor _, appRes := range appRess {\n\t\tappInstall, err := appInstallRepo.GetFirst(repo.WithByID(appRes.AppInstallId))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif _, err := compose.Restart(fmt.Sprintf(\"%s/%s/%s/docker-compose.yml\", global.Dir.AppInstallDir, appInstall.App.Key, appInstall.Name)); err != nil {\n\t\t\tglobal.LOG.Errorf(\"docker-compose restart %s[%s] failed, err: %v\", appInstall.App.Key, appInstall.Name, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (a *AppInstallService) DeleteCheck(installID uint) ([]dto.AppResource, error) {\n\tvar res []dto.AppResource\n\tappInstall, err := appInstallRepo.GetFirst(repo.WithByID(installID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\twebsites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(appInstall.ID))\n\tfor _, website := range websites {\n\t\tres = append(res, dto.AppResource{\n\t\t\tType: \"website\",\n\t\t\tName: website.PrimaryDomain,\n\t\t})\n\t}\n\tresources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(appInstall.ID), repo.WithByFrom(constant.AppResourceLocal))\n\tfor _, resource := range resources {\n\t\tlinkInstall, _ := appInstallRepo.GetFirst(repo.WithByID(resource.AppInstallId))\n\t\tif linkInstall.ID > 0 {\n\t\t\tres = append(res, dto.AppResource{\n\t\t\t\tType: \"app\",\n\t\t\t\tName: linkInstall.Name,\n\t\t\t})\n\t\t} else {\n\t\t\t_ = appInstallResourceRepo.DeleteBy(context.Background(), appInstallResourceRepo.WithAppInstallId(resource.AppInstallId))\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (a *AppInstallService) GetDefaultConfigByKey(key, name string) (string, error) {\n\tbaseInfo, err := appInstallRepo.LoadBaseInfo(key, name)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfileOp := files.NewFileOp()\n\tfilePath := path.Join(global.Dir.AppResourceDir, \"remote\", baseInfo.Key, baseInfo.Version, \"conf\")\n\tif !fileOp.Stat(filePath) {\n\t\tfilePath = path.Join(global.Dir.AppResourceDir, baseInfo.Key, \"versions\", baseInfo.Version, \"conf\")\n\t}\n\tif !fileOp.Stat(filePath) {\n\t\treturn \"\", buserr.New(\"ErrPathNotFound\")\n\t}\n\n\tif key == constant.AppMysql || key == constant.AppMariaDB || key == constant.AppMysqlCluster {\n\t\tfilePath = path.Join(filePath, \"my.cnf\")\n\t}\n\tif key == constant.AppRedis || key == constant.AppRedisCluster {\n\t\tfilePath = path.Join(filePath, \"redis.conf\")\n\t}\n\tif key == constant.AppOpenresty {\n\t\tfilePath = path.Join(filePath, \"nginx.conf\")\n\t}\n\tcontentByte, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(contentByte), nil\n}\n\nfunc (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {\n\tvar (\n\t\tparams  []response.AppParam\n\t\tappForm dto.AppForm\n\t\tenvs    = make(map[string]interface{})\n\t\tres     response.AppConfig\n\t)\n\tinstall, err := appInstallRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdetail, err := appDetailRepo.GetFirst(repo.WithByID(install.AppDetailId))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err = json.Unmarshal([]byte(detail.Params), &appForm); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = json.Unmarshal([]byte(install.Env), &envs); err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, form := range appForm.FormFields {\n\t\tif v, ok := envs[form.EnvKey]; ok {\n\t\t\tappParam := response.AppParam{\n\t\t\t\tEdit:     false,\n\t\t\t\tKey:      form.EnvKey,\n\t\t\t\tRule:     form.Rule,\n\t\t\t\tType:     form.Type,\n\t\t\t\tMultiple: form.Multiple,\n\t\t\t\tRequired: form.Required,\n\t\t\t}\n\t\t\tif form.Edit {\n\t\t\t\tappParam.Edit = true\n\t\t\t}\n\t\t\tappParam.LabelZh = form.LabelZh\n\t\t\tappParam.LabelEn = form.LabelEn\n\t\t\tappParam.Label = form.Label\n\t\t\tappParam.Value = v\n\t\t\tif form.Type == \"service\" {\n\t\t\t\tappInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string)))\n\t\t\t\tappParam.ShowValue = appInstall.Name\n\t\t\t} else if form.Type == \"select\" {\n\t\t\t\tif form.Multiple {\n\t\t\t\t\tif v == \"\" {\n\t\t\t\t\t\tappParam.Value = []string{}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif str, ok := v.(string); ok {\n\t\t\t\t\t\t\tappParam.Value = strings.Split(str, \",\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfor _, fv := range form.Values {\n\t\t\t\t\t\tif fv.Value == v {\n\t\t\t\t\t\t\tappParam.ShowValue = fv.Label\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tappParam.Values = form.Values\n\t\t\t} else if form.Type == \"apps\" {\n\t\t\t\tif m, ok := form.Child.(map[string]interface{}); ok {\n\t\t\t\t\tresult := make(map[string]string)\n\t\t\t\t\tfor key, value := range m {\n\t\t\t\t\t\tif strVal, ok := value.(string); ok {\n\t\t\t\t\t\t\tresult[key] = strVal\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif envKey, ok := result[\"envKey\"]; ok {\n\t\t\t\t\t\tserviceName := envs[envKey]\n\t\t\t\t\t\tif serviceName != nil {\n\t\t\t\t\t\t\tappInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(serviceName.(string)))\n\t\t\t\t\t\t\tappParam.ShowValue = appInstall.Name\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tparams = append(params, appParam)\n\t\t} else {\n\t\t\tparams = append(params, response.AppParam{\n\t\t\t\tEdit:     form.Edit,\n\t\t\t\tKey:      form.EnvKey,\n\t\t\t\tRule:     form.Rule,\n\t\t\t\tType:     form.Type,\n\t\t\t\tLabelZh:  form.LabelZh,\n\t\t\t\tLabelEn:  form.LabelEn,\n\t\t\t\tValue:    form.Default,\n\t\t\t\tValues:   form.Values,\n\t\t\t\tMultiple: form.Multiple,\n\t\t\t\tRequired: form.Required,\n\t\t\t\tLabel:    form.Label,\n\t\t\t})\n\t\t}\n\t}\n\n\tconfig := getAppCommonConfig(envs)\n\tconfig.DockerCompose = install.DockerCompose\n\tres.Params = params\n\tif config.ContainerName == \"\" {\n\t\tconfig.ContainerName = install.ContainerName\n\t}\n\tres.AppContainerConfig = config\n\tres.HostMode = isHostModel(install.DockerCompose)\n\tres.RestartPolicy = getRestartPolicy(install.DockerCompose)\n\tres.WebUI = install.WebUI\n\tres.Type = install.App.Type\n\tif rawCompose, err := getUpgradeCompose(install, detail); err == nil {\n\t\tres.RawCompose = rawCompose\n\t}\n\treturn &res, nil\n}\n\nfunc syncAppInstallStatus(appInstall *model.AppInstall, force bool) error {\n\tif appInstall.Status == constant.StatusInstalling || appInstall.Status == constant.StatusRebuilding || appInstall.Status == constant.StatusUpgrading || appInstall.Status == constant.StatusUninstalling {\n\t\treturn nil\n\t}\n\tcli, err := docker.NewClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\n\tvar (\n\t\tcontainers     []container.Summary\n\t\tcontainersMap  map[string]container.Summary\n\t\tcontainerNames = strings.Split(appInstall.ContainerName, \",\")\n\t)\n\tcontainers, err = cli.ListContainersByName(containerNames)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontainersMap = make(map[string]container.Summary)\n\tfor _, con := range containers {\n\t\tcontainersMap[con.Names[0]] = con\n\t}\n\tsynAppInstall(containersMap, appInstall, force)\n\treturn nil\n}\n\nfunc updateInstallInfoInDB(appKey, appName, param string, value interface{}) error {\n\tif param != \"password\" && param != \"port\" && param != \"user-password\" {\n\t\treturn nil\n\t}\n\tappInstall, err := appInstallRepo.LoadBaseInfo(appKey, appName)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tenvPath := fmt.Sprintf(\"%s/%s/.env\", appInstall.AppPath, appInstall.Name)\n\tlineBytes, err := os.ReadFile(envPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tenvKey := \"\"\n\tswitch param {\n\tcase \"password\":\n\t\tif appKey == \"mysql\" || appKey == \"mariadb\" || appKey == \"postgresql\" {\n\t\t\tenvKey = \"PANEL_DB_ROOT_PASSWORD=\"\n\t\t} else {\n\t\t\tenvKey = \"PANEL_REDIS_ROOT_PASSWORD=\"\n\t\t}\n\tcase \"port\":\n\t\tenvKey = \"PANEL_APP_PORT_HTTP=\"\n\tdefault:\n\t\tenvKey = \"PANEL_DB_USER_PASSWORD=\"\n\t}\n\tfiles := strings.Split(string(lineBytes), \"\\n\")\n\tvar newFiles []string\n\tfor _, line := range files {\n\t\tif strings.HasPrefix(line, envKey) {\n\t\t\tnewFiles = append(newFiles, fmt.Sprintf(\"%s%v\", envKey, value))\n\t\t} else {\n\t\t\tnewFiles = append(newFiles, line)\n\t\t}\n\t}\n\tfile, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\t_, err = file.WriteString(strings.Join(newFiles, \"\\n\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\toldVal, newVal := \"\", \"\"\n\tif param == \"password\" {\n\t\toldVal = fmt.Sprintf(\"\\\"PANEL_DB_ROOT_PASSWORD\\\":\\\"%v\\\"\", appInstall.Password)\n\t\tnewVal = fmt.Sprintf(\"\\\"PANEL_DB_ROOT_PASSWORD\\\":\\\"%v\\\"\", value)\n\t\tif appKey == \"redis\" {\n\t\t\toldVal = fmt.Sprintf(\"\\\"PANEL_REDIS_ROOT_PASSWORD\\\":\\\"%v\\\"\", appInstall.Password)\n\t\t\tnewVal = fmt.Sprintf(\"\\\"PANEL_REDIS_ROOT_PASSWORD\\\":\\\"%v\\\"\", value)\n\t\t}\n\t\t_ = appInstallRepo.BatchUpdateBy(map[string]interface{}{\n\t\t\t\"param\": strings.ReplaceAll(appInstall.Param, oldVal, newVal),\n\t\t\t\"env\":   strings.ReplaceAll(appInstall.Env, oldVal, newVal),\n\t\t}, repo.WithByID(appInstall.ID))\n\t\tif appKey == \"mysql\" || appKey == \"postgresql\" {\n\t\t\treturn nil\n\t\t}\n\t}\n\tif param == \"user-password\" {\n\t\toldVal = fmt.Sprintf(\"\\\"PANEL_DB_USER_PASSWORD\\\":\\\"%v\\\"\", appInstall.UserPassword)\n\t\tnewVal = fmt.Sprintf(\"\\\"PANEL_DB_USER_PASSWORD\\\":\\\"%v\\\"\", value)\n\t\t_ = appInstallRepo.BatchUpdateBy(map[string]interface{}{\n\t\t\t\"param\": strings.ReplaceAll(appInstall.Param, oldVal, newVal),\n\t\t\t\"env\":   strings.ReplaceAll(appInstall.Env, oldVal, newVal),\n\t\t}, repo.WithByID(appInstall.ID))\n\t}\n\tif param == \"port\" {\n\t\toldVal = fmt.Sprintf(\"\\\"PANEL_APP_PORT_HTTP\\\":%v\", appInstall.Port)\n\t\tnewVal = fmt.Sprintf(\"\\\"PANEL_APP_PORT_HTTP\\\":%v\", value)\n\t\t_ = appInstallRepo.BatchUpdateBy(map[string]interface{}{\n\t\t\t\"param\":     strings.ReplaceAll(appInstall.Param, oldVal, newVal),\n\t\t\t\"env\":       strings.ReplaceAll(appInstall.Env, oldVal, newVal),\n\t\t\t\"http_port\": value,\n\t\t}, repo.WithByID(appInstall.ID))\n\t}\n\n\tComposeFile := fmt.Sprintf(\"%s/%s/%s/docker-compose.yml\", global.Dir.AppInstallDir, appKey, appInstall.Name)\n\tstdout, err := compose.Down(ComposeFile)\n\tif err != nil {\n\t\treturn errors.New(stdout)\n\t}\n\tstdout, err = compose.Up(ComposeFile)\n\tif err != nil {\n\t\treturn errors.New(stdout)\n\t}\n\treturn nil\n}\n\nfunc (a *AppInstallService) GetAppInstallInfo(installID uint) (*response.AppInstallInfo, error) {\n\tappInstall, _ := appInstallRepo.GetFirst(repo.WithByID(installID))\n\tif appInstall.ID == 0 {\n\t\treturn &response.AppInstallInfo{\n\t\t\tStatus: constant.StatusDeleted,\n\t\t}, nil\n\t}\n\t_ = syncAppInstallStatus(&appInstall, false)\n\tappInstall, _ = appInstallRepo.GetFirst(repo.WithByID(installID))\n\tvar envMap map[string]interface{}\n\terr := json.Unmarshal([]byte(appInstall.Env), &envMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres := &response.AppInstallInfo{\n\t\tID:          appInstall.ID,\n\t\tName:        appInstall.Name,\n\t\tVersion:     appInstall.Version,\n\t\tContainer:   appInstall.ContainerName,\n\t\tHttpPort:    appInstall.HttpPort,\n\t\tStatus:      appInstall.Status,\n\t\tMessage:     appInstall.Message,\n\t\tAppKey:      appInstall.App.Key,\n\t\tEnv:         envMap,\n\t\tComposePath: appInstall.GetComposePath(),\n\t}\n\tfor k, v := range envMap {\n\t\tif strings.Contains(strings.ToUpper(k), \"PANEL_APP_PORT\") {\n\t\t\tvar port int\n\t\t\tswitch val := v.(type) {\n\t\t\tcase int:\n\t\t\t\tport = val\n\t\t\tcase string:\n\t\t\t\tport, err = strconv.Atoi(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tres.AppPorts = append(res.AppPorts, port)\n\t\t}\n\t}\n\treturn res, nil\n}\n"
  },
  {
    "path": "agent/app/service/app_sync_task.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/appicon\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/req_helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n)\n\ntype appSyncContext struct {\n\ttask           *task.Task\n\thttpClient     http.Client\n\tbaseRemoteUrl  string\n\tsystemVersion  string\n\tappsMap        map[string]model.App\n\tsettingService ISettingService\n\tlist           *dto.AppList\n\toldAppIds      []uint\n\tappTags        []*model.AppTag\n\tskipMetaSync   bool\n\tpendingIcons   map[string]string\n}\n\nfunc (a AppService) createSyncAppStoreTask(sharedCtx **appSyncContext) func(t *task.Task) error {\n\treturn func(t *task.Task) (err error) {\n\t\tt.LogStart(i18n.GetMsgByKey(\"AppStore\") + \" \" + i18n.GetMsgByKey(\"TaskSync\"))\n\n\t\tupdateRes, err := a.GetAppUpdate()\n\t\tif err != nil {\n\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"CheckAppStoreUpdate\"), err)\n\t\t\treturn err\n\t\t}\n\t\tif !updateRes.CanUpdate {\n\t\t\tif updateRes.IsSyncing {\n\t\t\t\tt.Log(i18n.GetMsgByKey(\"AppStoreIsSyncing\"))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tglobal.LOG.Infof(\"[AppStore] Appstore is up to date\")\n\t\t\tt.Log(i18n.GetMsgByKey(\"AppStoreIsUpToDate\"))\n\t\t\t*sharedCtx = &appSyncContext{skipMetaSync: true}\n\t\t\tt.LogSuccess(i18n.GetMsgByKey(\"AppStore\") + \" \" + i18n.GetMsgByKey(\"TaskSync\"))\n\t\t\treturn nil\n\t\t}\n\n\t\tlist := &dto.AppList{}\n\t\tif updateRes.AppList == nil {\n\t\t\tlist, err = getAppList()\n\t\t\tif err != nil {\n\t\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"DownloadAppList\"), err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tlist = updateRes.AppList\n\t\t}\n\n\t\tsettingService := NewISettingService()\n\t\tif err := settingService.Update(\"AppStoreSyncStatus\", constant.StatusSyncing); err != nil {\n\t\t\tglobal.LOG.Warnf(\"[AppStore] failed to update sync status to syncing: %v\", err)\n\t\t}\n\n\t\tsetting, err := settingService.GetSettingInfo()\n\t\tif err != nil {\n\t\t\tt.LogFailedWithErr(\"GetSettingInfo\", err)\n\t\t\treturn err\n\t\t}\n\n\t\tctx := &appSyncContext{\n\t\t\ttask:           t,\n\t\t\thttpClient:     http.Client{Timeout: time.Duration(constant.TimeOut20s) * time.Second, Transport: xpack.LoadRequestTransport()},\n\t\t\tbaseRemoteUrl:  fmt.Sprintf(\"%s/%s/1panel\", global.AppRepoURL(), global.CONF.Base.Mode),\n\t\t\tsystemVersion:  setting.SystemVersion,\n\t\t\tsettingService: settingService,\n\t\t\tlist:           list,\n\t\t\tappTags:        make([]*model.AppTag, 0),\n\t\t\tpendingIcons:   make(map[string]string),\n\t\t}\n\n\t\tif err = SyncTags(list.Extra); err != nil {\n\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"SyncTags\"), err)\n\t\t\treturn err\n\t\t}\n\t\tdeleteCustomApp()\n\n\t\toldApps, err := appRepo.GetBy(appRepo.WithNotLocal())\n\t\tif err != nil {\n\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"LoadLocalApps\"), err)\n\t\t\treturn err\n\t\t}\n\t\tctx.oldAppIds = make([]uint, 0, len(oldApps))\n\t\tfor _, old := range oldApps {\n\t\t\tctx.oldAppIds = append(ctx.oldAppIds, old.ID)\n\t\t}\n\n\t\tctx.appsMap, ctx.pendingIcons = getApps(oldApps, list.Apps, setting.SystemVersion, t)\n\n\t\tvar addCount, updateCount, deleteCount int\n\t\tif err = ctx.classifyAndPersistAppsWithStats(&addCount, &updateCount, &deleteCount); err != nil {\n\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"PersistApps\"), err)\n\t\t\treturn err\n\t\t}\n\n\t\tif err := settingService.Update(\"AppStoreSyncStatus\", constant.StatusSyncSuccess); err != nil {\n\t\t\tglobal.LOG.Warnf(\"[AppStore] failed to update sync status to success: %v\", err)\n\t\t}\n\t\tif err := settingService.Update(\"AppStoreLastModified\", strconv.Itoa(list.LastModified)); err != nil {\n\t\t\tglobal.LOG.Warnf(\"[AppStore] failed to update last modified: %v\", err)\n\t\t}\n\t\tglobal.LOG.Infof(\"[AppStore] Appstore sync completed\")\n\n\t\t*sharedCtx = ctx\n\t\tt.LogSuccess(i18n.GetMsgByKey(\"AppStore\") + \" \" + i18n.GetMsgByKey(\"TaskSync\"))\n\t\treturn nil\n\t}\n}\n\ntype appWorkItem struct {\n\tappDef  dto.AppDefine\n\tapp     model.App\n\ticonUrl string\n\thadIcon bool\n}\n\ntype appWorkResult struct {\n\tappKey     string\n\tapp        model.App\n\ticonStatus int\n\thadIcon    bool\n\thttpFailed bool\n}\n\nfunc (c *appSyncContext) processOneApp(item appWorkItem) appWorkResult {\n\tapp := item.app\n\tl := item.appDef\n\tresult := appWorkResult{\n\t\tappKey:  l.AppProperty.Key,\n\t\thadIcon: item.hadIcon,\n\t}\n\n\tif item.hadIcon {\n\t\tstatus, iconField := c.downloadAppIcon(item.iconUrl, l.AppProperty.Key, app.Icon)\n\t\tresult.iconStatus = status\n\t\tswitch status {\n\t\tcase http.StatusOK:\n\t\t\tapp.Icon = iconField\n\t\tcase http.StatusNotModified:\n\t\tdefault:\n\t\t\tresult.httpFailed = true\n\t\t}\n\t}\n\n\tapp.TagsKey = l.AppProperty.Tags\n\tif l.AppProperty.Recommend > 0 {\n\t\tapp.Recommend = l.AppProperty.Recommend\n\t} else {\n\t\tapp.Recommend = 9999\n\t}\n\tapp.ReadMe = l.ReadMe\n\tapp.LastModified = l.LastModified\n\n\tversions := l.Versions\n\tdetailsMap := getAppDetails(app.Details, versions)\n\tfor _, v := range versions {\n\t\tversion := v.Name\n\t\tdetail := detailsMap[version]\n\t\tversionUrl := fmt.Sprintf(\"%s/%s/%s\", c.baseRemoteUrl, app.Key, version)\n\n\t\tparamByte, _ := json.Marshal(v.AppForm)\n\t\tvar appForm dto.AppForm\n\t\t_ = json.Unmarshal(paramByte, &appForm)\n\n\t\tif appForm.SupportVersion > 0 && common.CompareVersion(strconv.FormatFloat(appForm.SupportVersion, 'f', -1, 64), c.systemVersion) {\n\t\t\tdelete(detailsMap, version)\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, ok := InitTypes[app.Type]; ok {\n\t\t\tdockerComposeUrl := fmt.Sprintf(\"%s/%s\", versionUrl, \"docker-compose.yml\")\n\t\t\t_, composeRes, err := req_helper.HandleRequestWithClient(&c.httpClient, dockerComposeUrl, http.MethodGet, constant.TimeOut20s)\n\t\t\tif err == nil {\n\t\t\t\tdetail.DockerCompose = string(composeRes)\n\t\t\t} else {\n\t\t\t\tresult.httpFailed = true\n\t\t\t}\n\t\t} else {\n\t\t\tdetail.DockerCompose = \"\"\n\t\t}\n\n\t\tdetail.Params = string(paramByte)\n\t\tdetail.DownloadUrl = fmt.Sprintf(\"%s/%s\", versionUrl, app.Key+\"-\"+version+\".tar.gz\")\n\t\tdetail.DownloadCallBackUrl = v.DownloadCallBackUrl\n\t\tdetail.Update = true\n\t\tdetail.LastModified = v.LastModified\n\t\tdetailsMap[version] = detail\n\t}\n\n\tnewDetails := make([]model.AppDetail, 0, len(detailsMap))\n\tfor _, detail := range detailsMap {\n\t\tnewDetails = append(newDetails, detail)\n\t}\n\tapp.Details = newDetails\n\n\tresult.app = app\n\treturn result\n}\n\nfunc (c *appSyncContext) syncAppIconsAndDetails() error {\n\ttotal := len(c.list.Apps)\n\tglobal.LOG.Infof(\"[AppStore] sync app detail start, total apps: %d\", total)\n\n\tworkItems := make([]appWorkItem, 0, total)\n\tfor _, l := range c.list.Apps {\n\t\tapp, ok := c.appsMap[l.AppProperty.Key]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\ticonUrl, hasPending := c.pendingIcons[l.AppProperty.Key]\n\t\tworkItems = append(workItems, appWorkItem{\n\t\t\tappDef:  l,\n\t\t\tapp:     app,\n\t\t\ticonUrl: iconUrl,\n\t\t\thadIcon: hasPending,\n\t\t})\n\t}\n\n\ttotalWork := len(workItems)\n\tif totalWork == 0 {\n\t\treturn nil\n\t}\n\n\tconst maxWorkers = 4\n\tvar (\n\t\tfailFlag atomic.Bool\n\t\tworkCh   = make(chan int, maxWorkers)\n\t\tresultCh = make(chan appWorkResult, maxWorkers)\n\t\twg       sync.WaitGroup\n\t)\n\n\twg.Add(maxWorkers)\n\tfor range maxWorkers {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tfor idx := range workCh {\n\t\t\t\tresult := c.processOneApp(workItems[idx])\n\t\t\t\tresultCh <- result\n\t\t\t\tif result.httpFailed {\n\t\t\t\t\tfailFlag.Store(true)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n\tgo func() { wg.Wait(); close(resultCh) }()\n\n\tvar fed atomic.Int32\n\tgo func() {\n\t\tfor i := range workItems {\n\t\t\tif failFlag.Load() {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tworkCh <- i\n\t\t\tfed.Store(int32(i + 1))\n\t\t}\n\t\tclose(workCh)\n\t}()\n\n\tvar (\n\t\tcompleted    int\n\t\ticon200Count int\n\t\ticon304Count int\n\t\ticonFailCount int\n\t)\n\tmilestones := [4]int{totalWork / 4, totalWork / 2, totalWork * 3 / 4, totalWork}\n\tnextMS := 0\n\tcheckProgress := func() {\n\t\tif nextMS < len(milestones) && completed >= milestones[nextMS] {\n\t\t\tc.task.LogWithProgress(i18n.GetMsgByKey(\"SyncAppDetail\"), completed, totalWork)\n\t\t\tnextMS++\n\t\t}\n\t}\n\n\tapplyResult := func(result appWorkResult) {\n\t\tc.appsMap[result.appKey] = result.app\n\t\tif result.hadIcon {\n\t\t\tswitch result.iconStatus {\n\t\t\tcase http.StatusOK:\n\t\t\t\ticon200Count++\n\t\t\tcase http.StatusNotModified:\n\t\t\t\ticon304Count++\n\t\t\tdefault:\n\t\t\t\ticonFailCount++\n\t\t\t}\n\t\t}\n\t\tcompleted++\n\t\tcheckProgress()\n\t}\n\n\tfor result := range resultCh {\n\t\tapplyResult(result)\n\t}\n\n\tfedCount := int(fed.Load())\n\tif failFlag.Load() && fedCount < len(workItems) {\n\t\tglobal.LOG.Warnf(\"[AppStore] HTTP failure detected, falling back to serial for remaining %d apps\", len(workItems)-fedCount)\n\t\tfor i := fedCount; i < len(workItems); i++ {\n\t\t\tresult := c.processOneApp(workItems[i])\n\t\t\tapplyResult(result)\n\t\t}\n\t}\n\n\tglobal.LOG.Infof(\"[AppStore] icon download completed - total: %d, success(200): %d, cached(304): %d, failed: %d\",\n\t\ttotal, icon200Count, icon304Count, iconFailCount)\n\n\treturn nil\n}\n\nfunc (c *appSyncContext) downloadAppIcon(iconUrl, appKey, oldIcon string) (status int, iconField string) {\n\ticonFileName, existingEtag := appicon.ParseIconField(oldIcon)\n\n\treqHeaders := make(map[string]string)\n\tif existingEtag != \"\" && iconFileName != \"\" && appicon.IconFileExists(iconFileName) {\n\t\treqHeaders[\"If-None-Match\"] = existingEtag\n\t}\n\n\tresp, err := req_helper.HandleRequestWithHeaders(&c.httpClient, iconUrl, http.MethodGet, constant.TimeOut20s, reqHeaders)\n\tif err != nil {\n\t\tglobal.LOG.Warnf(\"[AppStore] request icon failed url=%s, err=%v\", iconUrl, err)\n\t\treturn 0, \"\"\n\t}\n\n\tif resp.StatusCode == http.StatusNotModified {\n\t\treturn http.StatusNotModified, \"\"\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tglobal.LOG.Warnf(\"[AppStore] download icon failed url=%s, status=%d\", iconUrl, resp.StatusCode)\n\t\treturn 0, \"\"\n\t}\n\n\tif len(resp.Body) == 0 {\n\t\tglobal.LOG.Warnf(\"[AppStore] download icon empty body url=%s\", iconUrl)\n\t\treturn 0, \"\"\n\t}\n\n\tif resp.Body[0] == '<' {\n\t\tglobal.LOG.Warnf(\"[AppStore] download icon got HTML response url=%s\", iconUrl)\n\t\treturn 0, \"\"\n\t}\n\n\tcontentType := resp.Header.Get(\"Content-Type\")\n\tct := strings.TrimSpace(strings.Split(contentType, \";\")[0])\n\tif strings.ToLower(ct) != \"image/png\" {\n\t\tglobal.LOG.Warnf(\"[AppStore] unexpected icon content-type: %s, expected image/png, url=%s\", ct, iconUrl)\n\t}\n\n\tfileName, err := appicon.WriteIconFile(appKey, resp.Body)\n\tif err != nil {\n\t\tglobal.LOG.Warnf(\"[AppStore] write icon file failed appKey=%s, err=%v\", appKey, err)\n\t\treturn 0, \"\"\n\t}\n\n\tnewEtag := resp.Header.Get(\"ETag\")\n\ticonField = appicon.BuildIconField(fileName, newEtag)\n\n\treturn http.StatusOK, iconField\n}\n\nfunc (a AppService) createSyncAppStoreMetaTask(sharedCtx **appSyncContext) func(t *task.Task) error {\n\treturn func(t *task.Task) (err error) {\n\t\tt.LogStart(i18n.GetMsgByKey(\"SyncAppDetail\"))\n\t\tctx := *sharedCtx\n\t\tif ctx == nil {\n\t\t\tglobal.LOG.Warnf(\"[AppStore] meta sync skipped: shared context is nil\")\n\t\t\tt.Log(i18n.GetMsgByKey(\"SyncAppDetail\") + \" skipped: shared context is nil\")\n\t\t\treturn nil\n\t\t}\n\n\t\tif ctx.skipMetaSync {\n\t\t\tglobal.LOG.Infof(\"[AppStore] meta sync skipped: no update needed\")\n\t\t\tt.Log(i18n.GetMsgByKey(\"SyncAppDetail\") + \" skipped: no update needed\")\n\t\t\treturn nil\n\t\t}\n\n\t\tif ctx.list == nil || ctx.appsMap == nil {\n\t\t\tglobal.LOG.Errorf(\"[AppStore] meta sync failed: shared context data not initialized\")\n\t\t\terr := fmt.Errorf(\"shared context data not initialized\")\n\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"SyncAppDetail\"), err)\n\t\t\treturn err\n\t\t}\n\n\t\tt.Logf(\"%s: %d apps\", i18n.GetMsgByKey(\"SyncAppDetail\"), len(ctx.list.Apps))\n\n\t\tctx.task = t\n\t\tctx.appTags = make([]*model.AppTag, 0)\n\n\t\tif err = ctx.syncAppIconsAndDetails(); err != nil {\n\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"SyncAppDetail\"), err)\n\t\t\treturn err\n\t\t}\n\n\t\tif err = ctx.classifyAndPersistApps(); err != nil {\n\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"PersistAppDetails\"), err)\n\t\t\treturn err\n\t\t}\n\n\t\tglobal.LOG.Infof(\"[AppStore] Appstore meta sync completed\")\n\t\treturn nil\n\t}\n}\n\nfunc (c *appSyncContext) classifyAndPersistApps() (err error) {\n\tvar addCount, updateCount, deleteCount int\n\treturn c.classifyAndPersistAppsWithStats(&addCount, &updateCount, &deleteCount)\n}\n\nfunc (c *appSyncContext) classifyAndPersistAppsWithStats(addCount, updateCount, deleteCount *int) (err error) {\n\ttags, _ := tagRepo.All()\n\tvar (\n\t\taddAppArray    []model.App\n\t\tupdateAppArray []model.App\n\t\tdeleteAppArray []model.App\n\t\tdeleteIds      []uint\n\t\ttagMap         = make(map[string]uint, len(tags))\n\t)\n\n\tfor _, v := range c.appsMap {\n\t\tif v.ID == 0 {\n\t\t\taddAppArray = append(addAppArray, v)\n\t\t} else {\n\t\t\tif v.Status == constant.AppTakeDown {\n\t\t\t\tinstalls, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(v.ID))\n\t\t\t\tif len(installs) > 0 {\n\t\t\t\t\tupdateAppArray = append(updateAppArray, v)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdeleteAppArray = append(deleteAppArray, v)\n\t\t\t\tdeleteIds = append(deleteIds, v.ID)\n\t\t\t} else {\n\t\t\t\tupdateAppArray = append(updateAppArray, v)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(addAppArray) > 0 {\n\t\taddKeys := make([]string, 0, len(addAppArray))\n\t\tfor _, app := range addAppArray {\n\t\t\taddKeys = append(addKeys, app.Key)\n\t\t}\n\t\texistingApps, _ := appRepo.GetBy(appRepo.WithKeyIn(addKeys))\n\t\tif len(existingApps) > 0 {\n\t\t\texistingMap := make(map[string]model.App, len(existingApps))\n\t\t\tfor _, e := range existingApps {\n\t\t\t\texistingMap[e.Key] = e\n\t\t\t}\n\t\t\tfilteredAdd := make([]model.App, 0, len(addAppArray))\n\t\t\tfor _, app := range addAppArray {\n\t\t\t\tif existing, ok := existingMap[app.Key]; ok {\n\t\t\t\t\tapp.ID = existing.ID\n\t\t\t\t\tif len(app.Details) == 0 {\n\t\t\t\t\t\tapp.Details = existing.Details\n\t\t\t\t\t}\n\t\t\t\t\tupdateAppArray = append(updateAppArray, app)\n\t\t\t\t} else {\n\t\t\t\t\tfilteredAdd = append(filteredAdd, app)\n\t\t\t\t}\n\t\t\t}\n\t\t\taddAppArray = filteredAdd\n\t\t}\n\t}\n\n\t*addCount = len(addAppArray)\n\t*updateCount = len(updateAppArray)\n\t*deleteCount = len(deleteAppArray)\n\n\ttx, ctx := getTxAndContext()\n\tdefer func() {\n\t\tif err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn\n\t\t}\n\t}()\n\n\tif len(addAppArray) > 0 {\n\t\tif err = appRepo.BatchCreate(ctx, addAppArray); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif len(deleteAppArray) > 0 {\n\t\tif err = appRepo.BatchDelete(ctx, deleteAppArray); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif err = appDetailRepo.DeleteByAppIds(ctx, deleteIds); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tfor _, tag := range tags {\n\t\ttagMap[tag.Key] = tag.ID\n\t}\n\n\tif len(updateAppArray) > 0 {\n\t\tfor _, update := range updateAppArray {\n\t\t\tif err = appRepo.Save(ctx, &update); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tapps := append(addAppArray, updateAppArray...)\n\n\tvar (\n\t\taddDetails    []model.AppDetail\n\t\tupdateDetails []model.AppDetail\n\t\tdeleteDetails []model.AppDetail\n\t)\n\n\ttotalDetails := 0\n\tfor _, app := range apps {\n\t\tfor _, tag := range app.TagsKey {\n\t\t\ttagId, ok := tagMap[tag]\n\t\t\tif ok {\n\t\t\t\texist, _ := appTagRepo.GetFirst(ctx, appTagRepo.WithByTagID(tagId), appTagRepo.WithByAppID(app.ID))\n\t\t\t\tif exist == nil {\n\t\t\t\t\tc.appTags = append(c.appTags, &model.AppTag{\n\t\t\t\t\t\tAppId: app.ID,\n\t\t\t\t\t\tTagId: tagId,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor _, d := range app.Details {\n\t\t\ttotalDetails++\n\t\t\td.AppId = app.ID\n\t\t\tif d.ID == 0 {\n\t\t\t\taddDetails = append(addDetails, d)\n\t\t\t} else {\n\t\t\t\tif d.Status == constant.AppTakeDown {\n\t\t\t\t\truntime, _ := runtimeRepo.GetFirst(ctx, runtimeRepo.WithDetailId(d.ID))\n\t\t\t\t\tif runtime != nil {\n\t\t\t\t\t\tupdateDetails = append(updateDetails, d)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tinstalls, _ := appInstallRepo.ListBy(ctx, appInstallRepo.WithDetailIdsIn([]uint{d.ID}))\n\t\t\t\t\tif len(installs) > 0 {\n\t\t\t\t\t\tupdateDetails = append(updateDetails, d)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tdeleteDetails = append(deleteDetails, d)\n\t\t\t\t} else {\n\t\t\t\t\tupdateDetails = append(updateDetails, d)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(addDetails) > 0 {\n\t\tif err = appDetailRepo.BatchCreate(ctx, addDetails); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif len(deleteDetails) > 0 {\n\t\tif err = appDetailRepo.BatchDelete(ctx, deleteDetails); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif len(updateDetails) > 0 {\n\t\tfor _, u := range updateDetails {\n\t\t\tif err = appDetailRepo.Update(ctx, u); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(c.oldAppIds) > 0 {\n\t\tif err = appTagRepo.DeleteByAppIds(ctx, deleteIds); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif len(c.appTags) > 0 {\n\t\tif err = appTagRepo.BatchCreate(ctx, c.appTags); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err = tx.Commit().Error; err != nil {\n\t\treturn\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/app_utils.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"maps\"\n\t\"math\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/env\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/req_helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"github.com/compose-spec/compose-go/v2/types\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/subosito/gotenv\"\n\t\"gopkg.in/yaml.v3\"\n)\n\ntype DatabaseOp string\n\nvar (\n\tAdd    DatabaseOp = \"add\"\n\tDelete DatabaseOp = \"delete\"\n)\n\nfunc checkPort(key string, params map[string]interface{}) (int, error) {\n\tport, ok := params[key]\n\tif ok {\n\t\tportN := 0\n\t\tvar err error\n\t\tswitch p := port.(type) {\n\t\tcase string:\n\t\t\tportN, err = strconv.Atoi(p)\n\t\t\tif err != nil {\n\t\t\t\treturn portN, nil\n\t\t\t}\n\t\tcase float64:\n\t\t\tportN = int(math.Ceil(p))\n\t\tcase int:\n\t\t\tportN = p\n\t\t}\n\n\t\toldInstalled, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithPort(portN))\n\t\tif len(oldInstalled) > 0 {\n\t\t\tvar apps []string\n\t\t\tfor _, install := range oldInstalled {\n\t\t\t\tapps = append(apps, install.App.Name)\n\t\t\t}\n\t\t\treturn portN, buserr.WithMap(\"ErrPortInOtherApp\", map[string]interface{}{\"port\": portN, \"apps\": apps}, nil)\n\t\t}\n\t\tif common.ScanPort(portN) {\n\t\t\treturn portN, buserr.WithDetail(\"ErrPortInUsed\", portN, nil)\n\t\t} else {\n\t\t\treturn portN, nil\n\t\t}\n\t}\n\treturn 0, nil\n}\n\nfunc checkPortExist(port int) error {\n\terrMap := make(map[string]interface{})\n\terrMap[\"port\"] = port\n\tappInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))\n\tif appInstall.ID > 0 {\n\t\terrMap[\"type\"] = i18n.GetMsgByKey(\"TYPE_APP\")\n\t\terrMap[\"name\"] = appInstall.Name\n\t\treturn buserr.WithMap(\"ErrPortExist\", errMap, nil)\n\t}\n\truntime, _ := runtimeRepo.GetFirst(context.Background(), runtimeRepo.WithPort(port))\n\tif runtime != nil {\n\t\terrMap[\"type\"] = i18n.GetMsgByKey(\"TYPE_RUNTIME\")\n\t\terrMap[\"name\"] = runtime.Name\n\t\treturn buserr.WithMap(\"ErrPortExist\", errMap, nil)\n\t}\n\tdomain, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithPort(port))\n\tif domain.ID > 0 {\n\t\terrMap[\"type\"] = i18n.GetMsgByKey(\"TYPE_DOMAIN\")\n\t\terrMap[\"name\"] = domain.Domain\n\t\treturn buserr.WithMap(\"ErrPortExist\", errMap, nil)\n\t}\n\tif common.ScanPort(port) {\n\t\treturn buserr.WithDetail(\"ErrPortInUsed\", port, nil)\n\t}\n\treturn nil\n}\n\nvar DatabaseKeys = map[string]uint{\n\tconstant.AppMysql:             3306,\n\tconstant.AppMariaDB:           3306,\n\tconstant.AppPostgresql:        5432,\n\tconstant.AppPostgres:          5432,\n\tconstant.AppMongodb:           27017,\n\tconstant.AppRedis:             6379,\n\tconstant.AppMemcached:         11211,\n\tconstant.AppMysqlCluster:      3306,\n\tconstant.AppPostgresqlCluster: 5432,\n\tconstant.AppRedisCluster:      6379,\n}\n\nvar ToolKeys = map[string]uint{\n\t\"minio\": 9001,\n}\n\nfunc CreateDB(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {\n\tdatabase := &model.Database{\n\t\tAppInstallID: appInstall.ID,\n\t\tName:         appInstall.Name,\n\t\tType:         app.Key,\n\t\tVersion:      appInstall.Version,\n\t\tFrom:         \"local\",\n\t\tAddress:      appInstall.ServiceName,\n\t\tPort:         DatabaseKeys[app.Key],\n\t}\n\tdetail, err := appDetailRepo.GetFirst(repo.WithByID(appInstall.AppDetailId))\n\tif err != nil {\n\t\treturn err\n\t}\n\tformFields := &dto.AppForm{}\n\tif err := json.Unmarshal([]byte(detail.Params), formFields); err != nil {\n\t\treturn err\n\t}\n\tfor _, form := range formFields.FormFields {\n\t\tif form.EnvKey == \"PANEL_APP_PORT_HTTP\" {\n\t\t\tportFloat, ok := form.Default.(float64)\n\t\t\tif ok {\n\t\t\t\tdatabase.Port = uint(int(portFloat))\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\tswitch app.Key {\n\tcase constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb, constant.AppMysqlCluster, constant.AppPostgresqlCluster:\n\t\tif password, ok := params[\"PANEL_DB_ROOT_PASSWORD\"]; ok {\n\t\t\tif password != \"\" {\n\t\t\t\tdatabase.Password = password.(string)\n\t\t\t\tif app.Key == \"mysql\" || app.Key == \"mariadb\" || app.Key == constant.AppMysqlCluster {\n\t\t\t\t\tdatabase.Username = \"root\"\n\t\t\t\t}\n\t\t\t\tif rootUser, ok := params[\"PANEL_DB_ROOT_USER\"]; ok {\n\t\t\t\t\tdatabase.Username = rootUser.(string)\n\t\t\t\t}\n\t\t\t\tauthParam := dto.AuthParam{\n\t\t\t\t\tRootPassword: password.(string),\n\t\t\t\t\tRootUser:     database.Username,\n\t\t\t\t}\n\t\t\t\tauthByte, err := json.Marshal(authParam)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tappInstall.Param = string(authByte)\n\n\t\t\t}\n\t\t}\n\tcase constant.AppRedis, constant.AppRedisCluster:\n\t\tif password, ok := params[\"PANEL_REDIS_ROOT_PASSWORD\"]; ok {\n\t\t\tauthParam := dto.RedisAuthParam{\n\t\t\t\tRootPassword: \"\",\n\t\t\t}\n\t\t\tif password != \"\" {\n\t\t\t\tauthParam.RootPassword = password.(string)\n\t\t\t\tdatabase.Password = password.(string)\n\t\t\t}\n\t\t\tauthByte, err := json.Marshal(authParam)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tappInstall.Param = string(authByte)\n\t\t}\n\t}\n\treturn databaseRepo.Create(ctx, database)\n}\n\nfunc createLink(ctx context.Context, installTask *task.Task, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {\n\tdeleteAppLink := func(t *task.Task) {\n\t\tdel := dto.DelAppLink{\n\t\t\tCtx:         ctx,\n\t\t\tInstall:     appInstall,\n\t\t\tForceDelete: true,\n\t\t}\n\t\t_ = deleteLink(del)\n\t}\n\tif DatabaseKeys[app.Key] > 0 {\n\t\thandleDataBaseApp := func(task *task.Task) error {\n\t\t\treturn CreateDB(ctx, app, appInstall, params)\n\t\t}\n\t\tinstallTask.AddSubTask(i18n.GetMsgByKey(\"HandleDatabaseApp\"), handleDataBaseApp, deleteAppLink)\n\t}\n\tif ToolKeys[app.Key] > 0 {\n\t\tif app.Key == \"minio\" {\n\t\t\tauthParam := dto.MinioAuthParam{}\n\t\t\tif password, ok := params[\"PANEL_MINIO_ROOT_PASSWORD\"]; ok {\n\t\t\t\tauthParam.RootPassword = password.(string)\n\t\t\t}\n\t\t\tif rootUser, ok := params[\"PANEL_MINIO_ROOT_USER\"]; ok {\n\t\t\t\tauthParam.RootUser = rootUser.(string)\n\t\t\t}\n\t\t\tauthByte, err := json.Marshal(authParam)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tappInstall.Param = string(authByte)\n\t\t}\n\t}\n\n\tvar dbConfig dto.AppDatabase\n\tif app.Type == \"website\" || app.Type == \"tool\" {\n\t\tparamByte, err := json.Marshal(params)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = json.Unmarshal(paramByte, &dbConfig); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif !reflect.DeepEqual(dbConfig, dto.AppDatabase{}) && dbConfig.ServiceName != \"\" {\n\t\tcreateAppDataBase := func(rootTask *task.Task) error {\n\t\t\thostName := params[\"PANEL_DB_HOST_NAME\"]\n\t\t\tif hostName == nil || hostName.(string) == \"\" {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tdatabase, _ := databaseRepo.Get(repo.WithByName(hostName.(string)))\n\t\t\tif database.ID == 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tvar resourceId uint\n\t\t\tif dbConfig.DbName != \"\" && dbConfig.DbUser != \"\" && dbConfig.Password != \"\" {\n\t\t\t\tswitch database.Type {\n\t\t\t\tcase constant.AppPostgresql, constant.AppPostgres, constant.AppPostgresqlCluster:\n\t\t\t\t\toldPostgresqlDb, _ := postgresqlRepo.Get(repo.WithByName(dbConfig.DbName), repo.WithByFrom(constant.ResourceLocal))\n\t\t\t\t\tif oldPostgresqlDb.ID == 0 {\n\t\t\t\t\t\toldPostgresqlDb, _ = postgresqlRepo.Get(repo.WithByName(dbConfig.DbName), postgresqlRepo.WithByPostgresqlName(dbConfig.DatabaseName), repo.WithByFrom(constant.AppResourceRemote))\n\t\t\t\t\t}\n\t\t\t\t\tresourceId = oldPostgresqlDb.ID\n\t\t\t\t\tif oldPostgresqlDb.ID > 0 {\n\t\t\t\t\t\tif oldPostgresqlDb.Username != dbConfig.DbUser || oldPostgresqlDb.Password != dbConfig.Password {\n\t\t\t\t\t\t\treturn buserr.New(\"ErrDbUserNotValid\")\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar createPostgresql dto.PostgresqlDBCreate\n\t\t\t\t\t\tcreatePostgresql.Name = dbConfig.DbName\n\t\t\t\t\t\tcreatePostgresql.Username = dbConfig.DbUser\n\t\t\t\t\t\tcreatePostgresql.Database = database.Name\n\t\t\t\t\t\tcreatePostgresql.Format = \"UTF8\"\n\t\t\t\t\t\tcreatePostgresql.Password = dbConfig.Password\n\t\t\t\t\t\tcreatePostgresql.From = database.From\n\t\t\t\t\t\tcreatePostgresql.SuperUser = true\n\t\t\t\t\t\tpgdb, err := NewIPostgresqlService().Create(ctx, createPostgresql)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresourceId = pgdb.ID\n\t\t\t\t\t}\n\t\t\t\tcase constant.AppMysql, constant.AppMariaDB, constant.AppMysqlCluster:\n\t\t\t\t\toldMysqlDb, _ := mysqlRepo.Get(repo.WithByName(dbConfig.DbName), repo.WithByFrom(constant.ResourceLocal))\n\t\t\t\t\tif oldMysqlDb.ID == 0 {\n\t\t\t\t\t\toldMysqlDb, _ = mysqlRepo.Get(repo.WithByName(dbConfig.DbName), mysqlRepo.WithByMysqlName(dbConfig.DatabaseName), repo.WithByFrom(constant.AppResourceRemote))\n\t\t\t\t\t}\n\t\t\t\t\tresourceId = oldMysqlDb.ID\n\t\t\t\t\tif oldMysqlDb.ID > 0 {\n\t\t\t\t\t\tif oldMysqlDb.Username != dbConfig.DbUser || oldMysqlDb.Password != dbConfig.Password {\n\t\t\t\t\t\t\treturn buserr.New(\"ErrDbUserNotValid\")\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar createMysql dto.MysqlDBCreate\n\t\t\t\t\t\tcreateMysql.Name = dbConfig.DbName\n\t\t\t\t\t\tcreateMysql.Username = dbConfig.DbUser\n\t\t\t\t\t\tcreateMysql.Database = database.Name\n\t\t\t\t\t\tcreateMysql.Format = dbConfig.Format\n\t\t\t\t\t\tcreateMysql.Permission = \"%\"\n\t\t\t\t\t\tcreateMysql.Password = dbConfig.Password\n\t\t\t\t\t\tcreateMysql.From = database.From\n\t\t\t\t\t\tcreateMysql.Collation = dbConfig.Collation\n\t\t\t\t\t\tmysqldb, err := NewIMysqlService().Create(ctx, createMysql)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresourceId = mysqldb.ID\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar installResource model.AppInstallResource\n\t\t\tinstallResource.ResourceId = resourceId\n\t\t\tinstallResource.AppInstallId = appInstall.ID\n\t\t\tif database.AppInstallID > 0 {\n\t\t\t\tinstallResource.LinkId = database.AppInstallID\n\t\t\t} else {\n\t\t\t\tinstallResource.LinkId = database.ID\n\t\t\t}\n\t\t\tinstallResource.Key = database.Type\n\t\t\tinstallResource.From = database.From\n\t\t\treturn appInstallResourceRepo.Create(ctx, &installResource)\n\t\t}\n\t\tinstallTask.AddSubTask(task.GetTaskName(dbConfig.DbName, task.TaskCreate, task.TaskScopeDatabase), createAppDataBase, deleteAppLink)\n\t}\n\treturn nil\n}\n\nfunc deleteAppInstall(deleteReq request.AppInstallDelete) error {\n\tinstall := deleteReq.Install\n\top := files.NewFileOp()\n\tappDir := install.GetPath()\n\n\tuninstallTask, err := task.NewTaskWithOps(install.Name, task.TaskUninstall, task.TaskScopeApp, deleteReq.TaskID, install.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tuninstall := func(t *task.Task) error {\n\t\tinstall.Status = constant.StatusUninstalling\n\t\t_ = appInstallRepo.Save(context.Background(), &install)\n\t\tdir, _ := os.Stat(appDir)\n\t\tif dir != nil {\n\t\t\tlogStr := i18n.GetMsgByKey(\"Stop\") + i18n.GetMsgByKey(\"App\")\n\t\t\tt.Log(logStr)\n\n\t\t\tout, err := compose.Down(install.GetComposePath())\n\t\t\tif err != nil && !deleteReq.ForceDelete {\n\t\t\t\treturn handleErr(install, err, out)\n\t\t\t}\n\t\t\tt.LogSuccess(logStr)\n\t\t\tif err = runScript(t, &install, \"uninstall\"); err != nil {\n\t\t\t\t_, _ = compose.Up(install.GetComposePath())\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif deleteReq.DeleteImage {\n\t\t\t\tdelImageStr := i18n.GetMsgByKey(\"TaskDelete\") + i18n.GetMsgByKey(\"Image\")\n\t\t\t\tcontent, err := op.GetContent(install.GetEnvPath())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\timages, err := docker.GetImagesFromDockerCompose(content, []byte(install.DockerCompose))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tclient, err := docker.NewClient()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdefer client.Close()\n\t\t\t\tfor _, image := range images {\n\t\t\t\t\timageID, err := client.GetImageIDByName(image)\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\timgStr := delImageStr + image\n\t\t\t\t\t\tt.Log(imgStr)\n\n\t\t\t\t\t\tif err = client.DeleteImage(imageID); err != nil {\n\t\t\t\t\t\t\tt.LogFailedWithErr(imgStr, err)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tt.LogSuccess(delImageStr + image)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttx, ctx := helper.GetTxAndContext()\n\t\tdefer tx.Rollback()\n\t\tif err = appInstallRepo.Delete(ctx, install); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tappKey := install.App.Key\n\t\tif appKey == constant.AppOpenclaw || appKey == constant.AppCopaw {\n\t\t\t_ = agentRepo.DeleteByAppInstallIDWithCtx(ctx, install.ID)\n\t\t}\n\n\t\tresources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))\n\t\tif len(resources) > 0 {\n\t\t\tif deleteReq.DeleteDB {\n\t\t\t\tdel := dto.DelAppLink{\n\t\t\t\t\tCtx:         ctx,\n\t\t\t\t\tInstall:     &install,\n\t\t\t\t\tForceDelete: deleteReq.ForceDelete,\n\t\t\t\t\tTask:        uninstallTask,\n\t\t\t\t}\n\t\t\t\tt.LogWithOps(task.TaskDelete, i18n.GetMsgByKey(\"Database\"))\n\t\t\t\tif err = deleteLink(del); err != nil {\n\t\t\t\t\tt.LogFailedWithOps(task.TaskDelete, i18n.GetMsgByKey(\"Database\"), err)\n\t\t\t\t\tif !deleteReq.ForceDelete {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tt.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey(\"Database\"))\n\t\t\t} else {\n\t\t\t\t_ = appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))\n\t\t\t}\n\t\t}\n\n\t\tif DatabaseKeys[install.App.Key] > 0 {\n\t\t\t_ = databaseRepo.Delete(ctx, databaseRepo.WithAppInstallID(install.ID))\n\t\t}\n\n\t\tswitch install.App.Key {\n\t\tcase constant.AppMysql, constant.AppMariaDB, constant.AppMysqlCluster:\n\t\t\t_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))\n\t\tcase constant.AppPostgresql, constant.AppPostgresqlCluster:\n\t\t\t_ = postgresqlRepo.Delete(ctx, postgresqlRepo.WithByPostgresqlName(install.Name))\n\t\t}\n\n\t\t_ = backupRepo.DeleteRecord(ctx, repo.WithByType(\"app\"), repo.WithByName(install.App.Key), repo.WithByDetailName(install.Name))\n\t\tuploadDir := path.Join(global.Dir.BaseDir, fmt.Sprintf(\"1panel/uploads/app/%s/%s\", install.App.Key, install.Name))\n\t\tif _, err := os.Stat(uploadDir); err == nil {\n\t\t\t_ = os.RemoveAll(uploadDir)\n\t\t}\n\t\tif deleteReq.DeleteBackup {\n\t\t\tbackupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf(\"app/%s/%s\", install.App.Key, install.Name))\n\t\t\tif _, err = os.Stat(backupDir); err == nil {\n\t\t\t\tt.LogWithOps(task.TaskDelete, i18n.GetMsgByKey(\"TaskBackup\"))\n\t\t\t\t_ = os.RemoveAll(backupDir)\n\t\t\t\tt.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey(\"TaskBackup\"))\n\t\t\t}\n\t\t}\n\t\t_ = op.DeleteDir(appDir)\n\t\tparentDir := filepath.Dir(appDir)\n\t\tentries, err := os.ReadDir(parentDir)\n\t\tif err == nil && len(entries) == 0 {\n\t\t\t_ = op.DeleteDir(parentDir)\n\t\t}\n\t\ttx.Commit()\n\n\t\texistApps, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(install.AppId))\n\t\tif len(existApps) == 0 {\n\t\t\t_ = appIgnoreUpgradeRepo.Delete(appIgnoreUpgradeRepo.WithAppID(install.AppId))\n\t\t}\n\t\treturn nil\n\t}\n\tuninstallTask.AddSubTask(task.GetTaskName(install.Name, task.TaskUninstall, task.TaskScopeApp), uninstall, nil)\n\tgo func() {\n\t\tif err := uninstallTask.Execute(); err != nil && !deleteReq.ForceDelete {\n\t\t\tinstall.Status = constant.StatusError\n\t\t\t_ = appInstallRepo.Save(context.Background(), &install)\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc deleteLink(del dto.DelAppLink) error {\n\tinstall := del.Install\n\tresources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))\n\tif len(resources) == 0 {\n\t\treturn nil\n\t}\n\tfor _, re := range resources {\n\t\tswitch re.Key {\n\t\tcase constant.AppMysql, constant.AppMariaDB:\n\t\t\tmysqlService := NewIMysqlService()\n\t\t\tdatabase, _ := mysqlRepo.Get(repo.WithByID(re.ResourceId))\n\t\t\tif reflect.DeepEqual(database, model.DatabaseMysql{}) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := mysqlService.Delete(del.Ctx, dto.MysqlDBDelete{\n\t\t\t\tID:           database.ID,\n\t\t\t\tForceDelete:  del.ForceDelete,\n\t\t\t\tDeleteBackup: true,\n\t\t\t\tType:         re.Key,\n\t\t\t\tDatabase:     database.MysqlName,\n\t\t\t}); err != nil && !del.ForceDelete {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase constant.AppPostgresql:\n\t\t\tpgsqlService := NewIPostgresqlService()\n\t\t\tdatabase, _ := postgresqlRepo.Get(repo.WithByID(re.ResourceId))\n\t\t\tif reflect.DeepEqual(database, model.DatabasePostgresql{}) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := pgsqlService.Delete(del.Ctx, dto.PostgresqlDBDelete{\n\t\t\t\tID:           database.ID,\n\t\t\t\tForceDelete:  del.ForceDelete,\n\t\t\t\tDeleteBackup: true,\n\t\t\t\tType:         re.Key,\n\t\t\t\tDatabase:     database.PostgresqlName,\n\t\t\t}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn appInstallResourceRepo.DeleteBy(del.Ctx, appInstallResourceRepo.WithAppInstallId(install.ID))\n}\n\nfunc handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map[string]interface{}, error) {\n\tcomposeMap := make(map[string]interface{})\n\tif err := yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); err != nil {\n\t\treturn nil, err\n\t}\n\tvalue, ok := composeMap[\"services\"]\n\tif !ok || value == nil {\n\t\treturn nil, buserr.New(\"ErrFileParse\")\n\t}\n\tservicesMap := value.(map[string]interface{})\n\tif len(servicesMap) == 1 {\n\t\tindex := 0\n\t\toldServiceName := \"\"\n\t\tfor k := range servicesMap {\n\t\t\toldServiceName = k\n\t\t\tindex++\n\t\t\tif index > 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tservicesMap[install.ServiceName] = servicesMap[oldServiceName]\n\t\tif install.ServiceName != oldServiceName {\n\t\t\tdelete(servicesMap, oldServiceName)\n\t\t}\n\t}\n\tserviceValue := servicesMap[install.ServiceName].(map[string]interface{})\n\n\toldComposeMap := make(map[string]interface{})\n\tif err := yaml.Unmarshal([]byte(install.DockerCompose), &oldComposeMap); err != nil {\n\t\treturn nil, err\n\t}\n\toldValue, ok := oldComposeMap[\"services\"]\n\tif !ok || oldValue == nil {\n\t\treturn nil, buserr.New(\"ErrFileParse\")\n\t}\n\toldValueMap, ok := oldValue.(map[string]interface{})\n\tif !ok {\n\t\treturn nil, buserr.New(\"ErrFileParse\")\n\t}\n\toldServiceValueInterface, ok := oldValueMap[install.ServiceName]\n\tif !ok || oldServiceValueInterface == nil {\n\t\treturn nil, buserr.New(\"ErrFileParse\")\n\t}\n\toldServiceValue, ok := oldServiceValueInterface.(map[string]interface{})\n\tif !ok {\n\t\treturn nil, buserr.New(\"ErrFileParse\")\n\t}\n\tif oldServiceValue[\"deploy\"] != nil {\n\t\tserviceValue[\"deploy\"] = oldServiceValue[\"deploy\"]\n\t}\n\tif oldServiceValue[\"restart\"] != nil {\n\t\tserviceValue[\"restart\"] = oldServiceValue[\"restart\"]\n\t}\n\tservicesMap[install.ServiceName] = serviceValue\n\tcomposeMap[\"services\"] = servicesMap\n\treturn composeMap, nil\n}\n\nfunc getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {\n\tif detail.DockerCompose == \"\" {\n\t\treturn \"\", nil\n\t}\n\tcomposeMap, err := handleUpgradeCompose(install, detail)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tenvs := make(map[string]interface{})\n\tif err := json.Unmarshal([]byte(install.Env), &envs); err != nil {\n\t\treturn \"\", err\n\t}\n\tconfig := getAppCommonConfig(envs)\n\tif config.ContainerName == \"\" {\n\t\tconfig.ContainerName = install.ContainerName\n\t\tenvs[constant.ContainerName] = install.ContainerName\n\t}\n\tconfig.Advanced = true\n\tif err := addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); err != nil {\n\t\treturn \"\", err\n\t}\n\tparamByte, err := json.Marshal(envs)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tinstall.Env = string(paramByte)\n\tcomposeByte, err := yaml.Marshal(composeMap)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(composeByte), nil\n}\n\nfunc buildNginx(parentTask *task.Task) error {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfileOp := files.NewFileOp()\n\tbuildPath := path.Join(nginxInstall.GetPath(), \"build\")\n\tif !fileOp.Stat(buildPath) {\n\t\treturn buserr.New(\"ErrBuildDirNotFound\")\n\t}\n\tmoduleConfigPath := path.Join(buildPath, \"module.json\")\n\tmoduleContent, err := fileOp.GetContent(moduleConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar (\n\t\tmodules         []dto.NginxModule\n\t\taddModuleParams []string\n\t\taddPackages     []string\n\t)\n\tif len(moduleContent) > 0 {\n\t\t_ = json.Unmarshal(moduleContent, &modules)\n\t\tbashFile, err := os.OpenFile(path.Join(buildPath, \"tmp\", \"pre.sh\"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.DirPerm)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer bashFile.Close()\n\t\tbashFileWriter := bufio.NewWriter(bashFile)\n\t\tfor _, module := range modules {\n\t\t\tif !module.Enable {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_, err = bashFileWriter.WriteString(module.Script + \"\\n\")\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\taddModuleParams = append(addModuleParams, module.Params)\n\t\t\taddPackages = append(addPackages, module.Packages...)\n\t\t}\n\t\terr = bashFileWriter.Flush()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tenvs, err := gotenv.Read(nginxInstall.GetEnvPath())\n\tif err != nil {\n\t\treturn err\n\t}\n\tenvs[\"RESTY_CONFIG_OPTIONS_MORE\"] = \"\"\n\tenvs[\"RESTY_ADD_PACKAGE_BUILDDEPS\"] = \"\"\n\tif len(addModuleParams) > 0 {\n\t\tenvs[\"RESTY_CONFIG_OPTIONS_MORE\"] = strings.Join(addModuleParams, \" \")\n\t}\n\tif len(addPackages) > 0 {\n\t\tenvs[\"RESTY_ADD_PACKAGE_BUILDDEPS\"] = strings.Join(addPackages, \" \")\n\t}\n\t_ = gotenv.Write(envs, nginxInstall.GetEnvPath())\n\tif len(addModuleParams) == 0 && len(addPackages) == 0 {\n\t\treturn nil\n\t}\n\tlogStr := fmt.Sprintf(\"%s %s\", i18n.GetMsgByKey(\"TaskBuild\"), i18n.GetMsgByKey(\"Image\"))\n\tparentTask.LogStart(logStr)\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTask(*parentTask), cmd.WithTimeout(60*time.Minute))\n\tif err = cmdMgr.RunBashCf(\"docker compose -f %s build\", nginxInstall.GetComposePath()); err != nil {\n\t\treturn err\n\t}\n\tparentTask.LogSuccess(logStr)\n\treturn nil\n}\n\nfunc upgradeInstall(req request.AppInstallUpgrade) error {\n\tinstall, err := appInstallRepo.GetFirst(repo.WithByID(req.InstallID))\n\tif err != nil {\n\t\treturn err\n\t}\n\toldVersion := install.Version\n\tdetail, err := appDetailRepo.GetFirst(repo.WithByID(req.DetailID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif install.Version == detail.Version {\n\t\treturn errors.New(\"two version is same\")\n\t}\n\n\tupgradeTask, err := task.NewTaskWithOps(install.Name, task.TaskUpgrade, task.TaskScopeApp, req.TaskID, install.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstall.Status = constant.StatusUpgrading\n\n\tvar (\n\t\tupErr      error\n\t\tbackupFile string\n\t)\n\tbackUpApp := func(t *task.Task) error {\n\t\tbackupService := NewIBackupService()\n\t\tbackupRecordService := NewIBackupRecordService()\n\t\tfileName := fmt.Sprintf(\"upgrade_backup_%s_%s.tar.gz\", install.Name, time.Now().Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))\n\t\tbackupRecord, err := backupService.AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name, FileName: fileName})\n\t\tif err == nil {\n\t\t\tbackups, _ := backupRecordService.ListAppRecords(install.App.Key, install.Name, \"upgrade_backup\")\n\t\t\tif len(backups) > 3 {\n\t\t\t\tbackupsToDelete := backups[:len(backups)-3]\n\t\t\t\tvar deleteIDs []uint\n\t\t\t\tfor _, backup := range backupsToDelete {\n\t\t\t\t\tdeleteIDs = append(deleteIDs, backup.ID)\n\t\t\t\t}\n\t\t\t\t_ = backupRecordService.BatchDeleteRecord(deleteIDs)\n\t\t\t}\n\t\t\tbackupFile = path.Join(global.Dir.LocalBackupDir, backupRecord.FileDir, backupRecord.FileName)\n\t\t} else {\n\t\t\treturn buserr.WithNameAndErr(\"ErrAppBackup\", install.Name, err)\n\t\t}\n\t\treturn nil\n\t}\n\tif req.Backup {\n\t\tupgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), backUpApp, nil)\n\t}\n\n\tupgradeApp := func(t *task.Task) error {\n\t\tfileOp := files.NewFileOp()\n\t\tdetailDir := path.Join(global.Dir.ResourceDir, \"apps\", install.App.Resource, install.App.Key, detail.Version)\n\t\tif install.App.Resource == constant.AppResourceRemote {\n\t\t\tif err = downloadApp(install.App, detail, &install, t.Logger); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif detail.DockerCompose == \"\" {\n\t\t\t\tcomposeDetail, err := fileOp.GetContent(path.Join(detailDir, \"docker-compose.yml\"))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdetail.DockerCompose = string(composeDetail)\n\t\t\t\t_ = appDetailRepo.Update(context.Background(), detail)\n\t\t\t}\n\t\t\tgo func() {\n\t\t\t\tRequestDownloadCallBack(detail.DownloadCallBackUrl)\n\t\t\t}()\n\t\t}\n\t\tif install.App.Resource == constant.AppResourceLocal {\n\t\t\tdetailDir = path.Join(global.Dir.ResourceDir, \"apps\", \"local\", strings.TrimPrefix(install.App.Key, \"local\"), detail.Version)\n\t\t}\n\n\t\tcontent, err := fileOp.GetContent(install.GetEnvPath())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdockerCLi, _ := docker.NewClient()\n\t\tif req.PullImage {\n\t\t\tcomposeContent := []byte(detail.DockerCompose)\n\t\t\tif req.DockerCompose != \"\" {\n\t\t\t\tcomposeContent = []byte(req.DockerCompose)\n\t\t\t}\n\t\t\timages, err := docker.GetImagesFromDockerCompose(content, composeContent)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, image := range images {\n\t\t\t\tt.Log(i18n.GetWithName(\"PullImageStart\", image))\n\t\t\t\tif err = dockerCLi.PullImageWithProcess(t, image); err != nil {\n\t\t\t\t\terr = buserr.WithNameAndErr(\"ErrDockerPullImage\", \"\", err)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tt.LogSuccess(i18n.GetMsgByKey(\"PullImage\"))\n\t\t\t}\n\t\t}\n\n\t\tcommand := exec.Command(\"/bin/bash\", \"-c\", fmt.Sprintf(\"cp -rn %s/* %s || true\", detailDir, install.GetPath()))\n\t\t_, _ = command.CombinedOutput()\n\t\tif install.App.Key == constant.AppOpenresty {\n\t\t\tinstallBuildDir := path.Join(install.GetPath(), \"build\")\n\t\t\tdetailBuildDir := path.Join(detailDir, \"build\")\n\t\t\tif !fileOp.Stat(installBuildDir) {\n\t\t\t\tif err := fileOp.CreateDir(installBuildDir, constant.DirPerm); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err := fileOp.DeleteDir(path.Join(installBuildDir, \"tmp\")); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := fileOp.CopyDir(path.Join(detailBuildDir, \"tmp\"), installBuildDir); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := fileOp.CopyFile(path.Join(detailBuildDir, \"Dockerfile\"), installBuildDir); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := fileOp.CopyFile(path.Join(detailBuildDir, \"nginx.conf\"), installBuildDir); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := fileOp.CopyFile(path.Join(detailBuildDir, \"nginx.vh.default.conf\"), installBuildDir); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tsourceScripts := path.Join(detailDir, \"scripts\")\n\t\tif fileOp.Stat(sourceScripts) {\n\t\t\tdstScripts := path.Join(install.GetPath(), \"scripts\")\n\t\t\t_ = fileOp.DeleteDir(dstScripts)\n\t\t\t_ = fileOp.CreateDir(dstScripts, constant.DirPerm)\n\t\t\tscriptCmd := exec.Command(\"cp\", \"-rf\", sourceScripts+\"/.\", dstScripts+\"/\")\n\t\t\t_, _ = scriptCmd.CombinedOutput()\n\t\t}\n\n\t\tvar newCompose string\n\t\tif err = migrateOpenclawHTTPSUpgrade(&install, oldVersion, detail.Version); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif req.DockerCompose == \"\" {\n\t\t\tnewCompose, err = getUpgradeCompose(install, detail)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tnewCompose = req.DockerCompose\n\t\t}\n\n\t\tinstall.DockerCompose = newCompose\n\t\tinstall.Version = detail.Version\n\t\tinstall.AppDetailId = req.DetailID\n\n\t\tif out, err := compose.Down(install.GetComposePath()); err != nil {\n\t\t\tif out != \"\" {\n\t\t\t\tupErr = errors.New(out)\n\t\t\t\treturn upErr\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tenvs := make(map[string]interface{})\n\t\tif err = json.Unmarshal([]byte(install.Env), &envs); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tenvParams := make(map[string]string, len(envs))\n\t\tif install.App.Key == constant.AppOpenresty {\n\t\t\tpackageUrl, _ := env.GetEnvValueByKey(install.GetEnvPath(), \"CONTAINER_PACKAGE_URL\")\n\t\t\taddPackage, _ := env.GetEnvValueByKey(install.GetEnvPath(), \"RESTY_ADD_PACKAGE_BUILDDEPS\")\n\t\t\toptions, _ := env.GetEnvValueByKey(install.GetEnvPath(), \"RESTY_CONFIG_OPTIONS_MORE\")\n\t\t\tenvParams[\"CONTAINER_PACKAGE_URL\"] = packageUrl\n\t\t\tenvParams[\"RESTY_ADD_PACKAGE_BUILDDEPS\"] = addPackage\n\t\t\tenvParams[\"RESTY_CONFIG_OPTIONS_MORE\"] = options\n\t\t}\n\t\thandleMap(envs, envParams)\n\t\tif err = env.Write(envParams, install.GetEnvPath()); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err = runScript(t, &install, \"upgrade\"); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), constant.FilePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif install.App.Key == constant.AppOpenresty {\n\t\t\tif err = buildNginx(t); err != nil {\n\t\t\t\tt.Log(err.Error())\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tlogStr := fmt.Sprintf(\"%s %s\", i18n.GetMsgByKey(\"Run\"), i18n.GetMsgByKey(\"App\"))\n\t\tt.Log(logStr)\n\t\tif out, err := compose.Up(install.GetComposePath()); err != nil {\n\t\t\tif out != \"\" {\n\t\t\t\treturn errors.New(out)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tt.LogSuccess(logStr)\n\t\tinstall.Status = constant.StatusRunning\n\t\treturn appInstallRepo.Save(context.Background(), &install)\n\t}\n\n\trollBackApp := func(t *task.Task) {\n\t\tif req.Backup {\n\t\t\tt.Log(i18n.GetWithName(\"AppRecover\", install.Name))\n\t\t\tif err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: \"app\", DownloadAccountID: 1, File: backupFile}); err != nil {\n\t\t\t\tt.LogFailedWithErr(i18n.GetWithName(\"AppRecover\", install.Name), err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.LogSuccess(i18n.GetWithName(\"AppRecover\", install.Name))\n\t\t\treturn\n\t\t}\n\t}\n\n\tupgradeTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskScopeApp, task.TaskUpgrade), upgradeApp, rollBackApp, 0, 1*time.Hour)\n\n\tgo func() {\n\t\terr = upgradeTask.Execute()\n\t\tif err != nil {\n\t\t\texistInstall, _ := appInstallRepo.GetFirst(repo.WithByID(req.InstallID))\n\t\t\tif existInstall.ID > 0 && existInstall.Status != constant.StatusRunning {\n\t\t\t\texistInstall.Status = constant.StatusUpgradeErr\n\t\t\t\texistInstall.Message = err.Error()\n\t\t\t\t_ = appInstallRepo.Save(context.Background(), &existInstall)\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn appInstallRepo.Save(context.Background(), &install)\n}\n\nfunc skipCheckStatus(service types.ServiceConfig) bool {\n\tfor key := range service.Labels {\n\t\tif key == \"skipStatusCheck\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc getContainerNames(install model.AppInstall) ([]string, error) {\n\tenvStr, err := coverEnvJsonToStr(install.Env)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tproject, err := docker.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcontainerMap := make(map[string]struct{})\n\tfor _, service := range project.AllServices() {\n\t\tif service.ContainerName == \"${CONTAINER_NAME}\" || service.ContainerName == \"\" || skipCheckStatus(service) {\n\t\t\tcontinue\n\t\t}\n\t\tcontainerMap[service.ContainerName] = struct{}{}\n\t}\n\tvar containerNames []string\n\tfor k := range containerMap {\n\t\tcontainerNames = append(containerNames, k)\n\t}\n\tif len(containerNames) == 0 {\n\t\tcontainerNames = append(containerNames, install.ContainerName)\n\t}\n\treturn containerNames, nil\n}\n\nfunc coverEnvJsonToStr(envJson string) (string, error) {\n\tenvMap := make(map[string]interface{})\n\t_ = json.Unmarshal([]byte(envJson), &envMap)\n\tnewEnvMap := make(map[string]string, len(envMap))\n\thandleMap(envMap, newEnvMap)\n\tenvStr, err := gotenv.Marshal(newEnvMap)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn envStr, nil\n}\n\nfunc checkLimit(app model.App) error {\n\tif app.Limit > 0 {\n\t\tinstalls, err := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(app.ID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(installs) >= app.Limit {\n\t\t\treturn buserr.New(\"ErrAppLimit\")\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc checkRequiredAndLimit(app model.App) error {\n\tif err := checkLimit(app); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc handleMap(params map[string]interface{}, envParams map[string]string) {\n\tfor k, v := range params {\n\t\tswitch t := v.(type) {\n\t\tcase string:\n\t\t\tenvParams[k] = t\n\t\tcase float64:\n\t\t\tenvParams[k] = strconv.FormatFloat(t, 'f', -1, 32)\n\t\tcase uint:\n\t\t\tenvParams[k] = strconv.Itoa(int(t))\n\t\tcase int:\n\t\t\tenvParams[k] = strconv.Itoa(t)\n\t\tcase []interface{}:\n\t\t\tstrArray := make([]string, len(t))\n\t\t\tfor i := range t {\n\t\t\t\tstrArray[i] = strings.ToLower(fmt.Sprintf(\"%v\", t[i]))\n\t\t\t}\n\t\t\tenvParams[k] = strings.Join(strArray, \",\")\n\t\tcase map[string]interface{}:\n\t\t\thandleMap(t, envParams)\n\t\t}\n\t}\n}\n\nfunc downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, logger *logrus.Logger) (err error) {\n\tif app.IsLocalApp() || app.IsCustomApp() {\n\t\treturn nil\n\t}\n\tappResourceDir := path.Join(global.Dir.AppResourceDir, app.Resource)\n\tappDownloadDir := app.GetAppResourcePath()\n\tappVersionDir := path.Join(appDownloadDir, appDetail.Version)\n\tfileOp := files.NewFileOp()\n\tif !appDetail.Update && fileOp.Stat(appVersionDir) {\n\t\treturn\n\t}\n\tif !fileOp.Stat(appDownloadDir) {\n\t\t_ = fileOp.CreateDir(appDownloadDir, constant.DirPerm)\n\t}\n\tif !fileOp.Stat(appVersionDir) {\n\t\t_ = fileOp.CreateDir(appVersionDir, constant.DirPerm)\n\t}\n\tif logger == nil {\n\t\tglobal.LOG.Infof(\"download app [%s] from %s\", app.Name, appDetail.DownloadUrl)\n\t} else {\n\t\tlogger.Printf(\"download app [%s] from %s\", app.Name, appDetail.DownloadUrl)\n\t}\n\n\tfilePath := path.Join(appVersionDir, app.Key+\"-\"+appDetail.Version+\".tar.gz\")\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tif appInstall != nil {\n\t\t\t\tappInstall.Status = constant.StatusDownloadErr\n\t\t\t\tappInstall.Message = err.Error()\n\t\t\t}\n\t\t}\n\t}()\n\n\tif err = files.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil {\n\t\tif logger == nil {\n\t\t\tglobal.LOG.Errorf(\"download app [%s] error %v\", app.Name, err)\n\t\t} else {\n\t\t\tlogger.Printf(\"download app [%s] error %v\", app.Name, err)\n\t\t}\n\t\treturn\n\t}\n\tif err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz, \"\"); err != nil {\n\t\tif logger == nil {\n\t\t\tglobal.LOG.Errorf(\"decompress app[%s] error %v\", app.Name, err)\n\t\t} else {\n\t\t\tlogger.Printf(\"decompress app[%s] error %v\", app.Name, err)\n\t\t}\n\t\treturn\n\t}\n\t_ = fileOp.DeleteFile(filePath)\n\tappDetail.Update = false\n\t_ = appDetailRepo.Update(context.Background(), appDetail)\n\treturn\n}\n\nfunc copyData(task *task.Task, app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, req request.AppInstallCreate) (err error) {\n\tfileOp := files.NewFileOp()\n\tappResourceDir := path.Join(global.Dir.AppResourceDir, app.Resource)\n\n\tif app.Resource == constant.AppResourceRemote {\n\t\terr = downloadApp(app, appDetail, appInstall, task.Logger)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tgo func() {\n\t\t\tRequestDownloadCallBack(appDetail.DownloadCallBackUrl)\n\t\t}()\n\t}\n\tappKey := app.Key\n\tinstallAppDir := path.Join(global.Dir.AppInstallDir, app.Key)\n\tif app.Resource == constant.AppResourceLocal {\n\t\tappResourceDir = global.Dir.LocalAppResourceDir\n\t\tappKey = strings.TrimPrefix(app.Key, \"local\")\n\t\tinstallAppDir = path.Join(global.Dir.LocalAppInstallDir, appKey)\n\t}\n\tif app.Resource == constant.AppResourceCustom {\n\t\tappResourceDir = path.Join(global.Dir.AppResourceDir, \"custom\")\n\t}\n\tresourceDir := path.Join(appResourceDir, appKey, appDetail.Version)\n\n\tif !fileOp.Stat(installAppDir) {\n\t\tif err = fileOp.CreateDir(installAppDir, constant.DirPerm); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tappDir := path.Join(installAppDir, req.Name)\n\tif fileOp.Stat(appDir) {\n\t\tif err = fileOp.DeleteDir(appDir); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif err = fileOp.Copy(resourceDir, installAppDir); err != nil {\n\t\treturn\n\t}\n\tversionDir := path.Join(installAppDir, appDetail.Version)\n\tif err = fileOp.Rename(versionDir, appDir); err != nil {\n\t\treturn\n\t}\n\tenvPath := path.Join(appDir, \".env\")\n\tenvParams := make(map[string]string)\n\tif fileOp.Stat(envPath) {\n\t\tenvs, _ := gotenv.Read(envPath)\n\t\tif envParams = maps.Clone(envs); envParams == nil {\n\t\t\tenvParams = make(map[string]string)\n\t\t}\n\t}\n\thandleMap(req.Params, envParams)\n\tif err = env.Write(envParams, envPath); err != nil {\n\t\treturn\n\t}\n\tif err := fileOp.WriteFile(appInstall.GetComposePath(), strings.NewReader(appInstall.DockerCompose), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\treturn\n}\n\nfunc runScript(task *task.Task, appInstall *model.AppInstall, operate string) error {\n\tworkDir := appInstall.GetPath()\n\tscriptPath := \"\"\n\tswitch operate {\n\tcase \"init\":\n\t\tscriptPath = path.Join(workDir, \"scripts\", \"init.sh\")\n\tcase \"upgrade\":\n\t\tscriptPath = path.Join(workDir, \"scripts\", \"upgrade.sh\")\n\tcase \"uninstall\":\n\t\tscriptPath = path.Join(workDir, \"scripts\", \"uninstall.sh\")\n\t}\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(scriptPath) {\n\t\treturn nil\n\t}\n\t_ = fileOp.ChmodR(scriptPath, constant.DirPerm, false)\n\tlogStr := i18n.GetWithName(\"ExecShell\", operate)\n\ttask.LogStart(logStr)\n\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir))\n\tif err := cmdMgr.RunBashC(scriptPath); err != nil {\n\t\ttask.LogFailedWithErr(logStr, err)\n\t\treturn err\n\t}\n\ttask.LogSuccess(logStr)\n\treturn nil\n}\n\nfunc checkContainerNameIsExist(containerName, appDir string) (bool, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer client.Close()\n\tvar options container.ListOptions\n\tlist, err := client.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tfor _, container := range list {\n\t\tif containerName == container.Names[0][1:] {\n\t\t\tif workDir, ok := container.Labels[composeWorkdirLabel]; ok {\n\t\t\t\tif workDir != appDir {\n\t\t\t\t\treturn true, nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn true, nil\n\t\t\t}\n\t\t}\n\n\t}\n\treturn false, nil\n}\n\nfunc upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error {\n\tupProject := func(appInstall *model.AppInstall) (err error) {\n\t\tvar (\n\t\t\tout    string\n\t\t\terrMsg string\n\t\t)\n\t\tif pullImages && appInstall.App.Type != \"php\" {\n\t\t\tenvByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\timages, err := docker.GetImagesFromDockerCompose(envByte, []byte(appInstall.DockerCompose))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\timagePrefix := xpack.GetImagePrefix()\n\t\t\tdockerCLi, err := docker.NewClient()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, image := range images {\n\t\t\t\tif imagePrefix != \"\" {\n\t\t\t\t\tlastSlashIndex := strings.LastIndex(image, \"/\")\n\t\t\t\t\tif lastSlashIndex != -1 {\n\t\t\t\t\t\timage = image[lastSlashIndex+1:]\n\t\t\t\t\t}\n\t\t\t\t\timage = imagePrefix + \"/\" + image\n\t\t\t\t}\n\n\t\t\t\ttask.Log(i18n.GetWithName(\"PullImageStart\", image))\n\t\t\t\tif err = dockerCLi.PullImageWithProcess(task, image); err != nil {\n\t\t\t\t\terrOur := err.Error()\n\t\t\t\t\tif errOur != \"\" {\n\t\t\t\t\t\tif strings.Contains(errOur, \"no such host\") {\n\t\t\t\t\t\t\terrMsg = i18n.GetMsgByKey(\"ErrNoSuchHost\") + \":\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif strings.Contains(errOur, \"Error response from daemon\") {\n\t\t\t\t\t\t\terrMsg = i18n.GetMsgByKey(\"PullImageTimeout\") + \":\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tappInstall.Message = errMsg + errOur\n\t\t\t\t\tinstallErr := errors.New(appInstall.Message)\n\t\t\t\t\ttask.LogFailedWithErr(i18n.GetMsgByKey(\"PullImage\"), installErr)\n\t\t\t\t\tif exist, _ := dockerCLi.ImageExists(image); !exist {\n\t\t\t\t\t\treturn installErr\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttask.Log(i18n.GetMsgByKey(\"UseExistImage\"))\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttask.Log(i18n.GetMsgByKey(\"PullImageSuccess\"))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlogStr := fmt.Sprintf(\"%s %s\", i18n.GetMsgByKey(\"Run\"), i18n.GetMsgByKey(\"App\"))\n\t\ttask.Log(logStr)\n\t\tout, err = compose.Up(appInstall.GetComposePath())\n\t\tif err != nil {\n\t\t\tif out != \"\" {\n\t\t\t\tappInstall.Message = errMsg + out\n\t\t\t\terr = errors.New(out)\n\t\t\t}\n\t\t\ttask.LogFailedWithErr(logStr, err)\n\t\t\treturn err\n\t\t}\n\t\ttask.LogSuccess(logStr)\n\t\treturn\n\t}\n\texist, _ := appInstallRepo.GetFirst(repo.WithByID(appInstall.ID))\n\tif exist.ID > 0 {\n\t\tcontainerNames, err := getContainerNames(*appInstall)\n\t\tif err == nil {\n\t\t\tif len(containerNames) > 0 {\n\t\t\t\tappInstall.ContainerName = strings.Join(containerNames, \",\")\n\t\t\t}\n\t\t\t_ = appInstallRepo.Save(context.Background(), appInstall)\n\t\t}\n\t}\n\tif err := upProject(appInstall); err != nil {\n\t\tif appInstall.Message == \"\" {\n\t\t\tappInstall.Message = err.Error()\n\t\t}\n\t\tappInstall.Status = constant.StatusUpErr\n\t\t_ = appInstallRepo.Save(context.Background(), appInstall)\n\t\treturn err\n\t} else {\n\t\tappInstall.Status = constant.StatusRunning\n\t\t_ = appInstallRepo.Save(context.Background(), appInstall)\n\t\treturn nil\n\t}\n}\n\nfunc rebuildApp(appInstall model.AppInstall) error {\n\tappInstall.Status = constant.StatusRebuilding\n\t_ = appInstallRepo.Save(context.Background(), &appInstall)\n\tgo func() {\n\t\tdockerComposePath := appInstall.GetComposePath()\n\t\tout, err := compose.Down(dockerComposePath)\n\t\tif err != nil {\n\t\t\t_ = handleErr(appInstall, err, out)\n\t\t\treturn\n\t\t}\n\t\tout, err = compose.Up(appInstall.GetComposePath())\n\t\tif err != nil {\n\t\t\t_ = handleErr(appInstall, err, out)\n\t\t\treturn\n\t\t}\n\t\tcontainerNames, err := getContainerNames(appInstall)\n\t\tif err != nil {\n\t\t\t_ = handleErr(appInstall, err, out)\n\t\t\treturn\n\t\t}\n\t\tappInstall.ContainerName = strings.Join(containerNames, \",\")\n\n\t\tappInstall.Status = constant.StatusRunning\n\t\t_ = appInstallRepo.Save(context.Background(), &appInstall)\n\t}()\n\treturn nil\n}\n\nfunc getAppDetails(details []model.AppDetail, versions []dto.AppConfigVersion) map[string]model.AppDetail {\n\tappDetails := make(map[string]model.AppDetail, len(details))\n\tfor _, old := range details {\n\t\told.Status = constant.AppTakeDown\n\t\tappDetails[old.Version] = old\n\t}\n\tfor _, v := range versions {\n\t\tversion := v.Name\n\t\tdetail, ok := appDetails[version]\n\t\tif ok {\n\t\t\tdetail.Status = constant.AppNormal\n\t\t\tappDetails[version] = detail\n\t\t} else {\n\t\t\tappDetails[version] = model.AppDetail{\n\t\t\t\tVersion: version,\n\t\t\t\tStatus:  constant.AppNormal,\n\t\t\t}\n\t\t}\n\t}\n\treturn appDetails\n}\n\nfunc getApps(oldApps []model.App, items []dto.AppDefine, systemVersion string, task *task.Task) (map[string]model.App, map[string]string) {\n\tapps := make(map[string]model.App, len(oldApps))\n\tpendingIcons := make(map[string]string, len(items))\n\tfor _, old := range oldApps {\n\t\told.Status = constant.AppTakeDown\n\t\tapps[old.Key] = old\n\t}\n\tfor _, item := range items {\n\t\tconfig := item.AppProperty\n\t\tif config.Version > 0 && common.CompareVersion(strconv.FormatFloat(config.Version, 'f', -1, 64), systemVersion) {\n\t\t\ttask.Log(i18n.GetWithName(\"AppVersionNotMatch\", item.Name))\n\t\t\tcontinue\n\t\t}\n\t\tif config.Deprecated > 0 && common.CompareAppVersion(systemVersion, strconv.FormatFloat(config.Deprecated, 'f', -1, 64)) {\n\t\t\tcontinue\n\t\t}\n\n\t\tkey := config.Key\n\t\tapp, ok := apps[key]\n\t\tif !ok {\n\t\t\tapp = model.App{}\n\t\t}\n\t\tapp.RequiredPanelVersion = config.Version\n\t\tapp.Resource = constant.AppResourceRemote\n\t\tapp.Name = item.Name\n\t\tapp.Limit = config.Limit\n\t\tapp.Key = key\n\t\tapp.ShortDescZh = config.ShortDescZh\n\t\tapp.ShortDescEn = config.ShortDescEn\n\t\tdescription, _ := json.Marshal(config.Description)\n\t\tapp.Description = string(description)\n\t\tapp.Website = config.Website\n\t\tapp.Document = config.Document\n\t\tapp.Github = config.Github\n\t\tapp.Type = config.Type\n\t\tapp.CrossVersionUpdate = config.CrossVersionUpdate\n\t\tapp.Status = constant.AppNormal\n\t\tapp.LastModified = item.LastModified\n\t\tapp.ReadMe = item.ReadMe\n\t\tapp.MemoryRequired = config.MemoryRequired\n\t\tapp.Architectures = strings.Join(config.Architectures, \",\")\n\t\tapp.GpuSupport = config.GpuSupport\n\t\tapp.BatchInstallSupport = config.BatchInstallSupport\n\t\tif item.Icon != \"\" {\n\t\t\tpendingIcons[key] = item.Icon\n\t\t}\n\t\tapps[key] = app\n\t}\n\treturn apps, pendingIcons\n}\n\nfunc handleLocalAppDetail(versionDir string, appDetail *model.AppDetail) error {\n\tfileOp := files.NewFileOp()\n\tdockerComposePath := path.Join(versionDir, \"docker-compose.yml\")\n\tif !fileOp.Stat(dockerComposePath) {\n\t\treturn buserr.WithName(\"ErrFileNotFound\", \"docker-compose.yml\")\n\t}\n\tdockerComposeByte, _ := fileOp.GetContent(dockerComposePath)\n\tif dockerComposeByte == nil {\n\t\treturn buserr.WithName(\"ErrFileParseApp\", \"docker-compose.yml\")\n\t}\n\tappDetail.DockerCompose = string(dockerComposeByte)\n\tparamPath := path.Join(versionDir, \"data.yml\")\n\tif !fileOp.Stat(paramPath) {\n\t\treturn buserr.WithName(\"ErrFileNotFound\", \"data.yml\")\n\t}\n\tparamByte, _ := fileOp.GetContent(paramPath)\n\tif paramByte == nil {\n\t\treturn buserr.WithName(\"ErrFileNotFound\", \"data.yml\")\n\t}\n\tappParamConfig := dto.LocalAppParam{}\n\tif err := yaml.Unmarshal(paramByte, &appParamConfig); err != nil {\n\t\treturn buserr.WithMap(\"ErrFileParseApp\", map[string]interface{}{\"name\": \"data.yml\", \"err\": err.Error()}, err)\n\t}\n\tdataJson, err := json.Marshal(appParamConfig.AppParams)\n\tif err != nil {\n\t\treturn buserr.WithMap(\"ErrFileParseApp\", map[string]interface{}{\"name\": \"data.yml\", \"err\": err.Error()}, err)\n\t}\n\tvar appParam dto.AppForm\n\tif err = json.Unmarshal(dataJson, &appParam); err != nil {\n\t\treturn buserr.WithMap(\"ErrFileParseApp\", map[string]interface{}{\"name\": \"data.yml\", \"err\": err.Error()}, err)\n\t}\n\tfor _, formField := range appParam.FormFields {\n\t\tif strings.Contains(formField.EnvKey, \" \") {\n\t\t\treturn buserr.WithName(\"ErrAppParamKey\", formField.EnvKey)\n\t\t}\n\t}\n\n\tvar dataMap map[string]interface{}\n\terr = yaml.Unmarshal(paramByte, &dataMap)\n\tif err != nil {\n\t\treturn buserr.WithMap(\"ErrFileParseApp\", map[string]interface{}{\"name\": \"data.yml\", \"err\": err.Error()}, err)\n\t}\n\n\tadditionalProperties, _ := dataMap[\"additionalProperties\"].(map[string]interface{})\n\tformFieldsInterface, ok := additionalProperties[\"formFields\"]\n\tif ok {\n\t\tformFields, ok := formFieldsInterface.([]interface{})\n\t\tif !ok {\n\t\t\treturn buserr.WithName(\"ErrAppParamKey\", \"formFields\")\n\t\t}\n\t\tfor _, item := range formFields {\n\t\t\tfield := item.(map[string]interface{})\n\t\t\tfor key, value := range field {\n\t\t\t\tif value == nil {\n\t\t\t\t\treturn buserr.WithName(\"ErrAppParamKey\", key)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tappDetail.Params = string(dataJson)\n\treturn nil\n}\n\nfunc handleLocalApp(appDir string) (app *model.App, err error) {\n\tfileOp := files.NewFileOp()\n\tconfigYamlPath := path.Join(appDir, \"data.yml\")\n\tif !fileOp.Stat(configYamlPath) {\n\t\terr = buserr.WithName(\"ErrFileNotFound\", \"data.yml\")\n\t\treturn\n\t}\n\ticonPath := path.Join(appDir, \"logo.png\")\n\tif !fileOp.Stat(iconPath) {\n\t\terr = buserr.WithName(\"ErrFileNotFound\", \"logo.png\")\n\t\treturn\n\t}\n\tconfigYamlByte, err := fileOp.GetContent(configYamlPath)\n\tif err != nil {\n\t\terr = buserr.WithMap(\"ErrFileParseApp\", map[string]interface{}{\"name\": \"data.yml\", \"err\": err.Error()}, err)\n\t\treturn\n\t}\n\tlocalAppDefine := dto.LocalAppAppDefine{}\n\tif err = yaml.Unmarshal(configYamlByte, &localAppDefine); err != nil {\n\t\terr = buserr.WithMap(\"ErrFileParseApp\", map[string]interface{}{\"name\": \"data.yml\", \"err\": err.Error()}, err)\n\t\treturn\n\t}\n\tappDefine := localAppDefine.AppProperty\n\tapp = &model.App{}\n\tapp.Name = appDefine.Name\n\tapp.TagsKey = append(appDefine.Tags, \"Local\")\n\tapp.Type = appDefine.Type\n\tapp.CrossVersionUpdate = appDefine.CrossVersionUpdate\n\tapp.Limit = appDefine.Limit\n\tapp.Recommend = appDefine.Recommend\n\tapp.Website = appDefine.Website\n\tapp.Github = appDefine.Github\n\tapp.Document = appDefine.Document\n\tif appDefine.ShortDescZh != \"\" {\n\t\tappDefine.Description.Zh = appDefine.ShortDescZh\n\t}\n\tif appDefine.ShortDescEn != \"\" {\n\t\tappDefine.Description.En = appDefine.ShortDescEn\n\t}\n\tdesc, _ := json.Marshal(appDefine.Description)\n\tapp.Description = string(desc)\n\tapp.Key = \"local\" + appDefine.Key\n\tapp.Architectures = strings.Join(appDefine.Architectures, \",\")\n\tapp.GpuSupport = appDefine.GpuSupport\n\tapp.MemoryRequired = appDefine.MemoryRequired\n\n\tapp.Resource = constant.AppResourceLocal\n\tapp.Status = constant.AppNormal\n\tapp.Recommend = 9999\n\treadMeByte, err := fileOp.GetContent(path.Join(appDir, \"README.md\"))\n\tif err == nil {\n\t\tapp.ReadMe = string(readMeByte)\n\t}\n\ticonByte, _ := fileOp.GetContent(iconPath)\n\tif iconByte != nil {\n\t\ticonStr := base64.StdEncoding.EncodeToString(iconByte)\n\t\tapp.Icon = iconStr\n\t}\n\treturn\n}\n\nfunc handleErr(install model.AppInstall, err error, out string) error {\n\treErr := err\n\tinstall.Message = err.Error()\n\tif out != \"\" {\n\t\tinstall.Message = out\n\t\treErr = errors.New(out)\n\t}\n\tinstall.Status = constant.StatusUpErr\n\t_ = appInstallRepo.Save(context.Background(), &install)\n\treturn reErr\n}\n\nfunc doNotNeedSync(installed model.AppInstall) bool {\n\treturn installed.Status == constant.StatusInstalling || installed.Status == constant.StatusRebuilding || installed.Status == constant.StatusUpgrading ||\n\t\tinstalled.Status == constant.StatusSyncing || installed.Status == constant.StatusUninstalling || installed.Status == constant.StatusInstallErr\n}\n\nfunc synAppInstall(containers map[string]container.Summary, appInstall *model.AppInstall, force bool) {\n\toldStatus := appInstall.Status\n\tcontainerNames := strings.Split(appInstall.ContainerName, \",\")\n\tif len(containers) == 0 {\n\t\tif appInstall.Status == constant.StatusUpErr && !force {\n\t\t\treturn\n\t\t}\n\t\tappInstall.Status = constant.StatusError\n\t\tappInstall.Message = buserr.WithName(\"ErrContainerNotFound\", strings.Join(containerNames, \",\")).Error()\n\t\t_ = appInstallRepo.Save(context.Background(), appInstall)\n\t\treturn\n\t}\n\tnotFoundNames := make([]string, 0)\n\texitNames := make([]string, 0)\n\texitedCount := 0\n\tpausedCount := 0\n\trunningCount := 0\n\trestartingCount := 0\n\ttotal := len(containerNames)\n\tfor _, name := range containerNames {\n\t\tif con, ok := containers[\"/\"+name]; ok {\n\t\t\tswitch con.State {\n\t\t\tcase \"exited\":\n\t\t\t\texitedCount++\n\t\t\t\texitNames = append(exitNames, name)\n\t\t\tcase \"running\":\n\t\t\t\trunningCount++\n\t\t\tcase \"restarting\":\n\t\t\t\trestartingCount++\n\t\t\tcase \"paused\":\n\t\t\t\tpausedCount++\n\t\t\t}\n\t\t} else {\n\t\t\tnotFoundNames = append(notFoundNames, name)\n\t\t}\n\t}\n\tswitch {\n\tcase exitedCount == total:\n\t\tappInstall.Status = constant.StatusStopped\n\tcase runningCount == total:\n\t\tappInstall.Status = constant.StatusRunning\n\t\tif oldStatus == constant.StatusRunning {\n\t\t\treturn\n\t\t}\n\tcase restartingCount == total:\n\t\tappInstall.Status = constant.StatusRestarting\n\tcase pausedCount == total:\n\t\tappInstall.Status = constant.StatusPaused\n\tcase len(notFoundNames) == total:\n\t\tif appInstall.Status == constant.StatusUpErr && !force {\n\t\t\treturn\n\t\t}\n\t\tappInstall.Status = constant.StatusError\n\t\tappInstall.Message = buserr.WithName(\"ErrContainerNotFound\", strings.Join(notFoundNames, \",\")).Error()\n\tdefault:\n\t\tvar msg string\n\t\tif exitedCount > 0 {\n\t\t\tmsg = buserr.WithName(\"ErrContainerMsg\", strings.Join(exitNames, \",\")).Error()\n\t\t}\n\t\tif len(notFoundNames) > 0 {\n\t\t\tmsg += buserr.WithName(\"ErrContainerNotFound\", strings.Join(notFoundNames, \",\")).Error()\n\t\t}\n\t\tif msg == \"\" {\n\t\t\tmsg = buserr.New(\"ErrAppWarn\").Error()\n\t\t}\n\t\tappInstall.Message = msg\n\t\tappInstall.Status = constant.StatusUnHealthy\n\t}\n\t_ = appInstallRepo.Save(context.Background(), appInstall)\n}\n\nfunc handleInstalled(appInstallList []model.AppInstall, updated, sync, checkUpdate bool) ([]response.AppInstallDTO, error) {\n\tvar (\n\t\tres           []response.AppInstallDTO\n\t\tcontainersMap map[string]container.Summary\n\t)\n\tif sync {\n\t\tcli, err := docker.NewClient()\n\t\tif err == nil {\n\t\t\tdefer cli.Close()\n\t\t\tcontainers, err := cli.ListAllContainers()\n\t\t\tif err == nil {\n\t\t\t\tcontainersMap = make(map[string]container.Summary, len(containers))\n\t\t\t\tfor _, contain := range containers {\n\t\t\t\t\tcontainersMap[contain.Names[0]] = contain\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, installed := range appInstallList {\n\t\tif updated && ignoreUpdate(installed) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif sync && !doNotNeedSync(installed) {\n\t\t\tsynAppInstall(containersMap, &installed, false)\n\t\t}\n\n\t\tinstallDTO := response.AppInstallDTO{\n\t\t\tID:          installed.ID,\n\t\t\tName:        installed.Name,\n\t\t\tAppID:       installed.AppId,\n\t\t\tAppDetailID: installed.AppDetailId,\n\t\t\tVersion:     installed.Version,\n\t\t\tStatus:      installed.Status,\n\t\t\tMessage:     installed.Message,\n\t\t\tHttpPort:    installed.HttpPort,\n\t\t\tHttpsPort:   installed.HttpsPort,\n\t\t\tAppName:     installed.App.Name,\n\t\t\tAppKey:      installed.App.Key,\n\t\t\tAppType:     installed.App.Type,\n\t\t\tPath:        installed.GetPath(),\n\t\t\tCreatedAt:   installed.CreatedAt,\n\t\t\tWebUI:       installed.WebUI,\n\t\t\tApp: response.AppDetail{\n\t\t\t\tGithub:   installed.App.Github,\n\t\t\t\tWebsite:  installed.App.Website,\n\t\t\t\tDocument: installed.App.Document,\n\t\t\t},\n\t\t\tFavorite:    installed.Favorite,\n\t\t\tSortOrder:   installed.SortOrder,\n\t\t\tContainer:   installed.ContainerName,\n\t\t\tServiceName: strings.ToLower(installed.ServiceName),\n\t\t}\n\n\t\tif !updated && !checkUpdate {\n\t\t\tinstallDTO.LinkDB = hasLinkDB(installed.ID)\n\t\t\tres = append(res, installDTO)\n\t\t\tcontinue\n\t\t}\n\n\t\tif installed.Version == \"latest\" {\n\t\t\tif checkUpdate {\n\t\t\t\tinstallDTO.CanUpdate = false\n\t\t\t\tinstallDTO.LinkDB = hasLinkDB(installed.ID)\n\t\t\t\tres = append(res, installDTO)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tinstallDTO.DockerCompose = installed.DockerCompose\n\t\tinstallDTO.IsEdit = isEditCompose(installed)\n\n\t\tdetails, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar versions []string\n\t\tfor _, appDetail := range details {\n\t\t\tignores, _ := appIgnoreUpgradeRepo.List(runtimeRepo.WithDetailId(appDetail.ID), appIgnoreUpgradeRepo.WithScope(\"version\"))\n\t\t\tif len(ignores) > 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif common.IsCrossVersion(installed.Version, appDetail.Version) && !installed.App.CrossVersionUpdate {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tversions = append(versions, appDetail.Version)\n\t\t}\n\n\t\tif len(versions) == 0 {\n\t\t\tif checkUpdate {\n\t\t\t\tinstallDTO.CanUpdate = false\n\t\t\t\tinstallDTO.LinkDB = hasLinkDB(installed.ID)\n\t\t\t\tres = append(res, installDTO)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tversions = common.GetSortedVersions(versions)\n\t\tlastVersion := versions[0]\n\n\t\tif installed.App.Key == constant.AppMysql || installed.App.Key == constant.AppMysqlCluster {\n\t\t\tfor _, version := range versions {\n\t\t\t\tmajorVersion := getMajorVersion(installed.Version)\n\t\t\t\tif !strings.HasPrefix(version, majorVersion) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tlastVersion = version\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif common.IsCrossVersion(installed.Version, lastVersion) {\n\t\t\tinstallDTO.CanUpdate = installed.App.CrossVersionUpdate\n\t\t} else {\n\t\t\tinstallDTO.CanUpdate = common.CompareVersion(lastVersion, installed.Version)\n\t\t}\n\n\t\tif updated {\n\t\t\tif installDTO.CanUpdate {\n\t\t\t\tres = append(res, installDTO)\n\t\t\t}\n\t\t} else if checkUpdate {\n\t\t\tinstallDTO.LinkDB = hasLinkDB(installed.ID)\n\t\t\tres = append(res, installDTO)\n\t\t}\n\t}\n\n\treturn res, nil\n}\n\nfunc getAppInstallByKey(key string) (model.AppInstall, error) {\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(key))\n\tif err != nil {\n\t\treturn model.AppInstall{}, err\n\t}\n\tappInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID))\n\tif err != nil {\n\t\treturn model.AppInstall{}, err\n\t}\n\treturn appInstall, nil\n}\n\nfunc getAppInstallPort(key string) (httpPort, httpsPort int, err error) {\n\tinstall, err := getAppInstallByKey(key)\n\tif err != nil {\n\t\treturn\n\t}\n\thttpPort = install.HttpPort\n\thttpsPort = install.HttpsPort\n\treturn\n}\n\nfunc updateToolApp(installed *model.AppInstall) {\n\ttooKey, ok := dto.AppToolMap[installed.App.Key]\n\tif !ok {\n\t\treturn\n\t}\n\ttoolInstall, _ := getAppInstallByKey(tooKey)\n\tif reflect.DeepEqual(toolInstall, model.AppInstall{}) {\n\t\treturn\n\t}\n\tparamMap := make(map[string]string)\n\t_ = json.Unmarshal([]byte(installed.Param), &paramMap)\n\tenvMap := make(map[string]interface{})\n\t_ = json.Unmarshal([]byte(toolInstall.Env), &envMap)\n\tif password, ok := paramMap[\"PANEL_DB_ROOT_PASSWORD\"]; ok {\n\t\tenvMap[\"PANEL_DB_ROOT_PASSWORD\"] = password\n\t}\n\tif _, ok := envMap[\"PANEL_REDIS_HOST\"]; ok {\n\t\tenvMap[\"PANEL_REDIS_HOST\"] = installed.ServiceName\n\t}\n\tif _, ok := envMap[\"PANEL_DB_HOST\"]; ok {\n\t\tenvMap[\"PANEL_DB_HOST\"] = installed.ServiceName\n\t}\n\n\tenvPath := path.Join(toolInstall.GetPath(), \".env\")\n\tcontentByte, err := json.Marshal(envMap)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"update tool app [%s] error : %s\", toolInstall.Name, err.Error())\n\t\treturn\n\t}\n\tenvFileMap := make(map[string]string)\n\thandleMap(envMap, envFileMap)\n\tif err = env.Write(envFileMap, envPath); err != nil {\n\t\tglobal.LOG.Errorf(\"update tool app [%s] error : %s\", toolInstall.Name, err.Error())\n\t\treturn\n\t}\n\ttoolInstall.Env = string(contentByte)\n\tif err := appInstallRepo.Save(context.Background(), &toolInstall); err != nil {\n\t\tglobal.LOG.Errorf(\"update tool app [%s] error : %s\", toolInstall.Name, err.Error())\n\t\treturn\n\t}\n\tif out, err := compose.Down(toolInstall.GetComposePath()); err != nil {\n\t\tglobal.LOG.Errorf(\"update tool app [%s] error : %s\", toolInstall.Name, out)\n\t\treturn\n\t}\n\tif out, err := compose.Up(toolInstall.GetComposePath()); err != nil {\n\t\tglobal.LOG.Errorf(\"update tool app [%s] error : %s\", toolInstall.Name, out)\n\t\treturn\n\t}\n}\n\nfunc addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName string, req request.AppContainerConfig, params map[string]interface{}) error {\n\tservices, serviceValid := composeMap[\"services\"].(map[string]interface{})\n\tif !serviceValid {\n\t\treturn buserr.New(\"ErrFileParse\")\n\t}\n\timagePreFix := xpack.GetImagePrefix()\n\tif imagePreFix != \"\" {\n\t\tfor _, service := range services {\n\t\t\tserviceValue := service.(map[string]interface{})\n\t\t\tif image, ok := serviceValue[\"image\"]; ok {\n\t\t\t\timageStr := image.(string)\n\t\t\t\tlastSlashIndex := strings.LastIndex(imageStr, \"/\")\n\t\t\t\tif lastSlashIndex != -1 {\n\t\t\t\t\timageStr = imageStr[lastSlashIndex+1:]\n\t\t\t\t}\n\t\t\t\timageStr = imagePreFix + \"/\" + imageStr\n\t\t\t\tserviceValue[\"image\"] = imageStr\n\t\t\t}\n\t\t}\n\t}\n\n\tservice, serviceExist := services[serviceName]\n\tif !serviceExist {\n\t\treturn buserr.New(\"ErrFileParse\")\n\t}\n\tserviceValue := service.(map[string]interface{})\n\n\tdeploy := map[string]interface{}{}\n\tif de, ok := serviceValue[\"deploy\"]; ok {\n\t\tdeploy = de.(map[string]interface{})\n\t}\n\tresource := map[string]interface{}{}\n\tif res, ok := deploy[\"resources\"]; ok {\n\t\tresource = res.(map[string]interface{})\n\t}\n\tresource[\"limits\"] = map[string]interface{}{\n\t\t\"cpus\":   \"${CPUS}\",\n\t\t\"memory\": \"${MEMORY_LIMIT}\",\n\t}\n\tif req.GpuConfig {\n\t\tresource[\"reservations\"] = map[string]interface{}{\n\t\t\t\"devices\": []map[string]interface{}{\n\t\t\t\t{\n\t\t\t\t\t\"driver\":       \"nvidia\",\n\t\t\t\t\t\"count\":        \"all\",\n\t\t\t\t\t\"capabilities\": []string{\"gpu\"},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\tdeploy[\"resources\"] = resource\n\tserviceValue[\"deploy\"] = deploy\n\n\tif req.RestartPolicy != \"\" {\n\t\tif req.RestartPolicy == \"on-failure\" {\n\t\t\tserviceValue[\"restart\"] = \"on-failure:5\"\n\t\t} else {\n\t\t\tserviceValue[\"restart\"] = req.RestartPolicy\n\t\t}\n\t}\n\n\tports, ok := serviceValue[\"ports\"].([]interface{})\n\tif ok {\n\t\tfor i, port := range ports {\n\t\t\tportStr, portOK := port.(string)\n\t\t\tif !portOK {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tportArray := strings.Split(portStr, \":\")\n\t\t\tif len(portArray) == 2 {\n\t\t\t\tportArray = append([]string{\"${HOST_IP}\"}, portArray...)\n\t\t\t}\n\t\t\tports[i] = strings.Join(portArray, \":\")\n\t\t}\n\t\tserviceValue[\"ports\"] = ports\n\t}\n\n\tparams[constant.CPUS] = \"0\"\n\tparams[constant.MemoryLimit] = \"0\"\n\tif req.Advanced {\n\t\tif req.CpuQuota > 0 {\n\t\t\tparams[constant.CPUS] = req.CpuQuota\n\t\t}\n\t\tif req.MemoryLimit > 0 {\n\t\t\tparams[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit\n\t\t}\n\t}\n\t_, portExist := serviceValue[\"ports\"].([]interface{})\n\tif portExist {\n\t\tallowHost := \"127.0.0.1\"\n\t\tif req.Advanced && req.AllowPort {\n\t\t\tallowHost = req.SpecifyIP\n\t\t}\n\t\tparams[constant.HostIP] = allowHost\n\t}\n\tservices[serviceName] = serviceValue\n\treturn nil\n}\n\nfunc getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig {\n\tconfig := request.AppContainerConfig{}\n\n\tif hostIp, ok := envs[constant.HostIP]; ok {\n\t\tconfig.AllowPort = hostIp.(string) != \"127.0.0.1\"\n\t\tconfig.SpecifyIP = hostIp.(string)\n\t} else {\n\t\tconfig.AllowPort = true\n\t}\n\tif cpuCore, ok := envs[constant.CPUS]; ok {\n\t\tnumStr, ok := cpuCore.(string)\n\t\tif ok {\n\t\t\tnum, err := strconv.ParseFloat(numStr, 64)\n\t\t\tif err == nil {\n\t\t\t\tconfig.CpuQuota = num\n\t\t\t}\n\t\t} else {\n\t\t\tnum64, flOk := cpuCore.(float64)\n\t\t\tif flOk {\n\t\t\t\tconfig.CpuQuota = num64\n\t\t\t}\n\t\t}\n\t} else {\n\t\tconfig.CpuQuota = 0\n\t}\n\tif memLimit, ok := envs[constant.MemoryLimit]; ok {\n\t\tmatches := re.GetRegex(re.NumberAlphaPattern).FindStringSubmatch(memLimit.(string))\n\t\tif len(matches) == 3 {\n\t\t\tnum, err := strconv.ParseFloat(matches[1], 64)\n\t\t\tif err == nil {\n\t\t\t\tunit := matches[2]\n\t\t\t\tconfig.MemoryLimit = num\n\t\t\t\tconfig.MemoryUnit = unit\n\t\t\t}\n\t\t}\n\t} else {\n\t\tconfig.MemoryLimit = 0\n\t\tconfig.MemoryUnit = \"M\"\n\t}\n\n\tif containerName, ok := envs[constant.ContainerName]; ok {\n\t\tconfig.ContainerName = containerName.(string)\n\t}\n\n\treturn config\n}\n\nfunc isHostModel(dockerCompose string) bool {\n\tcomposeMap := make(map[string]interface{})\n\t_ = yaml.Unmarshal([]byte(dockerCompose), &composeMap)\n\tservices, serviceValid := composeMap[\"services\"].(map[string]interface{})\n\tif !serviceValid {\n\t\treturn false\n\t}\n\tfor _, service := range services {\n\t\tserviceValue := service.(map[string]interface{})\n\t\tif value, ok := serviceValue[\"network_mode\"]; ok && value == \"host\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc getRestartPolicy(yml string) string {\n\tvar project docker.ComposeProject\n\tif err := yaml.Unmarshal([]byte(yml), &project); err != nil {\n\t\treturn \"\"\n\t}\n\tfor _, service := range project.Services {\n\t\treturn service.Restart\n\t}\n\treturn \"\"\n}\n\nfunc getMajorVersion(version string) string {\n\tparts := strings.Split(version, \".\")\n\tif len(parts) >= 2 {\n\t\treturn parts[0] + \".\" + parts[1]\n\t}\n\treturn version\n}\n\nfunc ignoreUpdate(installed model.AppInstall) bool {\n\tif installed.App.Type == \"php\" || installed.Status == constant.StatusInstalling {\n\t\treturn true\n\t}\n\tignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope(\"all\"))\n\tif len(ignores) > 0 {\n\t\treturn true\n\t}\n\tif installed.App.Key == constant.AppMysql {\n\t\tmajorVersion := getMajorVersion(installed.Version)\n\t\tappDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID))\n\t\tfor _, appDetail := range appDetails {\n\t\t\tif strings.HasPrefix(appDetail.Version, majorVersion) && common.CompareVersion(appDetail.Version, installed.Version) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tif installed.App.Key == \"sqlbot\" {\n\t\tif common.CompareVersion(\"1.1.0\", installed.Version) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc RequestDownloadCallBack(downloadCallBackUrl string) {\n\tif downloadCallBackUrl == \"\" {\n\t\treturn\n\t}\n\t_, _, _ = req_helper.HandleRequest(downloadCallBackUrl, http.MethodGet, constant.TimeOut5s)\n}\n\nfunc getAppTags(appID uint, lang string) ([]response.TagDTO, error) {\n\tappTags, err := appTagRepo.GetByAppId(appID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar tagIds []uint\n\tfor _, at := range appTags {\n\t\ttagIds = append(tagIds, at.TagId)\n\t}\n\ttags, err := tagRepo.GetByIds(tagIds)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []response.TagDTO\n\tfor _, t := range tags {\n\t\tvar translations = make(map[string]string)\n\t\t_ = json.Unmarshal([]byte(t.Translations), &translations)\n\t\tif name, ok := translations[lang]; ok {\n\t\t\ttagDTO := response.TagDTO{\n\t\t\t\tID:   t.ID,\n\t\t\t\tKey:  t.Key,\n\t\t\t\tName: name,\n\t\t\t}\n\t\t\tres = append(res, tagDTO)\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc handleSiteDir(app model.App, appDetail model.AppDetail, req request.AppInstallCreate, t *task.Task) error {\n\tif app.Key == \"openresty\" && (app.Resource == \"remote\" || app.Resource == \"custom\") && common.CompareVersion(appDetail.Version, \"1.27\") {\n\t\tif dir, ok := req.Params[\"WEBSITE_DIR\"]; ok {\n\t\t\tsiteDir := dir.(string)\n\t\t\tif siteDir == \"\" || !strings.HasPrefix(siteDir, \"/\") {\n\t\t\t\tsiteDir = path.Join(global.Dir.DataDir, dir.(string))\n\t\t\t}\n\t\t\treq.Params[\"WEBSITE_DIR\"] = siteDir\n\t\t\toldWebStePath, _ := settingRepo.GetValueByKey(\"WEBSITE_DIR\")\n\t\t\tfileOp := files.NewFileOp()\n\t\t\tmovePath := func(src, dst string) error {\n\t\t\t\treturn cmd.NewCommandMgr().Run(\"mv\", src, dst)\n\t\t\t}\n\t\t\tif oldWebStePath != \"\" && oldWebStePath != siteDir && fileOp.Stat(oldWebStePath) {\n\t\t\t\tt.Log(i18n.GetWithName(\"MoveSiteDir\", siteDir))\n\t\t\t\tif fileOp.Stat(siteDir) {\n\t\t\t\t\tif fileOp.Stat(path.Join(siteDir, \"conf.d\")) {\n\t\t\t\t\t\t_ = movePath(path.Join(siteDir, \"conf.d\"), path.Join(siteDir, \"conf.d.bak\"))\n\t\t\t\t\t}\n\t\t\t\t\tif fileOp.Stat(path.Join(siteDir, \"sites\")) {\n\t\t\t\t\t\t_ = movePath(path.Join(siteDir, \"sites\"), path.Join(siteDir, \"sites.bak\"))\n\t\t\t\t\t}\n\t\t\t\t\tif err := movePath(path.Join(oldWebStePath, \"sites\"), path.Join(siteDir, \"sites\")); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tif err := movePath(path.Join(oldWebStePath, \"conf.d\"), path.Join(siteDir, \"conf.d\")); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\terr := movePath(oldWebStePath, siteDir)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tt.Log(i18n.GetMsgByKey(\"MoveSiteDirSuccess\"))\n\t\t\t}\n\t\t\tif !fileOp.Stat(siteDir) {\n\t\t\t\t_ = fileOp.CreateDir(siteDir, constant.DirPerm)\n\t\t\t\t_ = fileOp.CreateDir(path.Join(siteDir, \"conf.d\"), constant.DirPerm)\n\t\t\t}\n\t\t\terr := settingRepo.UpdateOrCreate(\"WEBSITE_DIR\", siteDir)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tgo RestartPHPRuntime()\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc handleOpenrestyFile(appInstall *model.AppInstall) error {\n\twebsites, _ := websiteRepo.List()\n\thasDefaultWebsite := false\n\tfor _, website := range websites {\n\t\tif website.DefaultServer {\n\t\t\thasDefaultWebsite = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif err := handleSSLConfig(appInstall, hasDefaultWebsite, true); err != nil {\n\t\treturn err\n\t}\n\tif len(websites) == 0 {\n\t\treturn nil\n\t}\n\tif hasDefaultWebsite {\n\t\tif err := handleDefaultServer(appInstall); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn createAllWebsitesWAFConfig(websites)\n}\n\nfunc handleDefaultServer(appInstall *model.AppInstall) error {\n\tinstallDir := appInstall.GetPath()\n\tdefaultConfigPath := path.Join(installDir, \"conf\", \"default\", \"00.default.conf\")\n\tfileOp := files.NewFileOp()\n\tcontent, err := fileOp.GetContent(defaultConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewContent := strings.ReplaceAll(string(content), \"default_server\", \"\")\n\tif err := fileOp.WriteFile(defaultConfigPath, strings.NewReader(newContent), constant.FilePerm); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool, sslRejectHandshake bool) error {\n\tsslDir := path.Join(appInstall.GetPath(), \"conf\", \"ssl\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(sslDir) {\n\t\treturn errors.New(\"ssl dir not found\")\n\t}\n\thasDefaultSSL := fileOp.Stat(path.Join(sslDir, \"fullchain.pem\")) && fileOp.Stat(path.Join(sslDir, \"privkey.pem\")) && fileOp.Stat(path.Join(sslDir, \"root_ssl.conf\"))\n\tif !hasDefaultSSL {\n\t\tca, _ := websiteCARepo.GetFirst(repo.WithByName(\"1Panel\"))\n\t\tif ca.ID == 0 {\n\t\t\tglobal.LOG.Errorf(\"create openresty default ssl failed ca not found\")\n\t\t\treturn nil\n\t\t}\n\t\tcaService := NewIWebsiteCAService()\n\t\tcaRequest := request.WebsiteCAObtain{\n\t\t\tID:      ca.ID,\n\t\t\tDomains: \"localhost\",\n\t\t\tKeyType: \"4096\",\n\t\t\tTime:    99,\n\t\t\tUnit:    \"year\",\n\t\t\tDir:     sslDir,\n\t\t\tPushDir: true,\n\t\t}\n\t\twebsiteSSL, err := caService.ObtainSSL(caRequest)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() {\n\t\t\t_ = NewIWebsiteSSLService().Delete([]uint{websiteSSL.ID})\n\t\t}()\n\t}\n\tdefaultConfigPath := path.Join(appInstall.GetPath(), \"conf\", \"default\", \"00.default.conf\")\n\tcontent, err := os.ReadFile(defaultConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefaultConfig, err := parser.NewStringParser(string(content)).Parse()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefaultConfig.FilePath = defaultConfigPath\n\tdefaultServer := defaultConfig.FindServers()[0]\n\tupdateDefaultServer(defaultServer, appInstall.HttpPort, appInstall.HttpsPort, !hasDefaultWebsite, true)\n\tdefaultServer.UpdateDirective(\"include\", []string{\"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf\"})\n\tdefaultServer.UpdateDirective(\"http2\", []string{\"on\"})\n\tif sslRejectHandshake {\n\t\tdefaultServer.UpdateDirective(\"ssl_reject_handshake\", []string{\"on\"})\n\t} else {\n\t\tdefaultServer.RemoveDirective(\"ssl_reject_handshake\", []string{})\n\t}\n\tif err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc SyncTags(remoteProperties dto.ExtraProperties) (err error) {\n\ttx, ctx := getTxAndContext()\n\tdefer func() {\n\t\tif err != nil {\n\t\t\ttx.Rollback()\n\t\t}\n\t}()\n\tlocalTags, err := tagRepo.All()\n\tif err != nil {\n\t\treturn err\n\t}\n\tlocalTagsMap := make(map[string]*model.Tag)\n\tfor i := range localTags {\n\t\tlocalTagsMap[localTags[i].Key] = &localTags[i]\n\t}\n\tremoteTagsMap := make(map[string]*dto.Tag)\n\tfor i := range remoteProperties.Tags {\n\t\tremoteTagsMap[remoteProperties.Tags[i].Key] = &remoteProperties.Tags[i]\n\t}\n\n\tfor key, localTag := range localTagsMap {\n\t\tif _, exists := remoteTagsMap[key]; !exists {\n\t\t\tif err = tagRepo.DeleteByID(ctx, localTag.ID); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, remoteTag := range remoteProperties.Tags {\n\t\ttranslations, _ := json.Marshal(remoteTag.Locales)\n\n\t\tif existTag, exists := localTagsMap[remoteTag.Key]; exists {\n\t\t\tif needsUpdate(existTag, remoteTag, string(translations)) {\n\t\t\t\texistTag.Name = remoteTag.Name\n\t\t\t\texistTag.Sort = remoteTag.Sort\n\t\t\t\texistTag.Translations = string(translations)\n\n\t\t\t\tif err = tagRepo.Save(ctx, existTag); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tnewTag := &model.Tag{\n\t\t\t\tKey:          remoteTag.Key,\n\t\t\t\tName:         remoteTag.Name,\n\t\t\t\tSort:         remoteTag.Sort,\n\t\t\t\tTranslations: string(translations),\n\t\t\t}\n\t\t\tif err = tagRepo.Create(ctx, newTag); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tif err = tx.Commit().Error; err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc needsUpdate(localTag *model.Tag, remoteTag dto.Tag, translations string) bool {\n\treturn localTag.Name != remoteTag.Name ||\n\t\tlocalTag.Sort != remoteTag.Sort ||\n\t\tlocalTag.Translations != translations\n}\n\nfunc hasLinkDB(installID uint) bool {\n\tresources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(installID))\n\thasDB := false\n\tif len(resources) > 0 {\n\t\tfor _, resource := range resources {\n\t\t\tif resource.Key == constant.AppPostgres || resource.Key == constant.AppMysql ||\n\t\t\t\tresource.Key == constant.AppMariaDB || resource.Key == constant.AppMysqlCluster ||\n\t\t\t\tresource.Key == constant.AppPostgresql || resource.Key == constant.AppPostgresqlCluster {\n\t\t\t\thasDB = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn hasDB\n}\n\nfunc isEditCompose(installed model.AppInstall) bool {\n\tdetail, _ := appDetailRepo.GetFirst(repo.WithByID(installed.AppDetailId))\n\trawCompose, err := getUpgradeCompose(installed, detail)\n\tif rawCompose == \"\" || err != nil {\n\t\treturn false\n\t}\n\tif rawCompose != installed.DockerCompose {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc getAppVersions(key string, details []model.AppDetail) []string {\n\tvar (\n\t\tversionsRaw []string\n\t\tversions    []string\n\t)\n\thasLatest := false\n\tlatestVersion := \"\"\n\tfor _, detail := range details {\n\t\tif key != \"mssql\" && strings.Contains(detail.Version, \"latest\") {\n\t\t\thasLatest = true\n\t\t\tlatestVersion = detail.Version\n\t\t\tcontinue\n\t\t}\n\t\tif key == \"openresty\" && !common.CompareAppVersion(detail.Version, \"1.27\") {\n\t\t\tcontinue\n\t\t}\n\t\tversionsRaw = append(versionsRaw, detail.Version)\n\t}\n\tversions = common.GetSortedVersions(versionsRaw)\n\tif hasLatest {\n\t\tversions = append([]string{latestVersion}, versions...)\n\t}\n\treturn versions\n}\n"
  },
  {
    "path": "agent/app/service/backup.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage/client\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype BackupService struct{}\n\ntype IBackupService interface {\n\tCheckUsed(name string, isPublic bool) error\n\n\tLoadBackupOptions() ([]dto.BackupOption, error)\n\tSearchWithPage(search dto.SearchPageWithType) (int64, interface{}, error)\n\tCreate(backupDto dto.BackupOperate) error\n\tCheckConn(req dto.BackupOperate) dto.BackupCheckRes\n\tGetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)\n\tUpdate(req dto.BackupOperate) error\n\tDelete(id uint) error\n\tRefreshToken(req dto.OperateByID) error\n\tGetLocalDir() (string, error)\n\tUploadForRecover(req dto.UploadForRecover) error\n\n\tMysqlBackup(db dto.CommonBackup) error\n\tPostgresqlBackup(db dto.CommonBackup) error\n\tMysqlRecover(db dto.CommonRecover) error\n\tPostgresqlRecover(db dto.CommonRecover) error\n\tMysqlRecoverByUpload(req dto.CommonRecover) error\n\tPostgresqlRecoverByUpload(req dto.CommonRecover) error\n\n\tRedisBackup(db dto.CommonBackup) error\n\tRedisRecover(db dto.CommonRecover) error\n\n\tWebsiteBackup(db dto.CommonBackup) error\n\tWebsiteRecover(req dto.CommonRecover) error\n\n\tAppBackup(db dto.CommonBackup) (*model.BackupRecord, error)\n\tAppRecover(req dto.CommonRecover) error\n\n\tContainerBackup(req dto.CommonBackup) error\n\tContainerRecover(req dto.CommonRecover) error\n\tComposeBackup(req dto.CommonBackup) error\n\tComposeRecover(req dto.CommonRecover) error\n}\n\nfunc NewIBackupService() IBackupService {\n\treturn &BackupService{}\n}\n\nfunc (u *BackupService) GetLocalDir() (string, error) {\n\taccount, err := backupRepo.Get(repo.WithByType(constant.Local))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn account.BackupPath, nil\n}\n\nfunc (u *BackupService) SearchWithPage(req dto.SearchPageWithType) (int64, interface{}, error) {\n\toptions := []repo.DBOption{repo.WithOrderDesc(\"created_at\")}\n\tif len(req.Type) != 0 {\n\t\toptions = append(options, repo.WithByType(req.Type))\n\t}\n\tif len(req.Info) != 0 {\n\t\toptions = append(options, repo.WithByType(req.Info))\n\t}\n\tcount, accounts, err := backupRepo.Page(req.Page, req.PageSize, options...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar data []dto.BackupInfo\n\tfor _, account := range accounts {\n\t\tvar item dto.BackupInfo\n\t\tif err := copier.Copy(&item, &account); err != nil {\n\t\t\tglobal.LOG.Errorf(\"copy backup account to dto backup info failed, err: %v\", err)\n\t\t}\n\t\tif item.Type != constant.Sftp && item.Type != constant.Local {\n\t\t\titem.BackupPath = path.Join(\"/\", strings.TrimPrefix(item.BackupPath, \"/\"))\n\t\t}\n\t\tif !item.RememberAuth {\n\t\t\titem.AccessKey = \"\"\n\t\t\titem.Credential = \"\"\n\t\t\tif account.Type == constant.Sftp {\n\t\t\t\tvarMap := make(map[string]interface{})\n\t\t\t\tif err := json.Unmarshal([]byte(item.Vars), &varMap); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdelete(varMap, \"passPhrase\")\n\t\t\t\titemVars, _ := json.Marshal(varMap)\n\t\t\t\titem.Vars = string(itemVars)\n\t\t\t}\n\t\t} else {\n\t\t\titem.AccessKey, _ = encrypt.StringDecryptWithBase64(item.AccessKey)\n\t\t\titem.Credential, _ = encrypt.StringDecryptWithBase64(item.Credential)\n\t\t}\n\n\t\tif account.Type == constant.OneDrive || account.Type == constant.ALIYUN || account.Type == constant.GoogleDrive {\n\t\t\tvarMap := make(map[string]interface{})\n\t\t\tif err := json.Unmarshal([]byte(item.Vars), &varMap); err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdelete(varMap, \"refresh_token\")\n\t\t\tdelete(varMap, \"drive_id\")\n\t\t\titemVars, _ := json.Marshal(varMap)\n\t\t\titem.Vars = string(itemVars)\n\t\t}\n\t\tdata = append(data, item)\n\t}\n\treturn count, data, nil\n}\n\nfunc (u *BackupService) CheckConn(req dto.BackupOperate) dto.BackupCheckRes {\n\tvar res dto.BackupCheckRes\n\tvar backup model.BackupAccount\n\tif err := copier.Copy(&backup, &req); err != nil {\n\t\tres.Msg = i18n.GetMsgWithDetail(\"ErrStructTransform\", err.Error())\n\t\treturn res\n\t}\n\titemAccessKey, err := base64.StdEncoding.DecodeString(backup.AccessKey)\n\tif err != nil {\n\t\tres.Msg = err.Error()\n\t\treturn res\n\t}\n\tbackup.AccessKey = string(itemAccessKey)\n\titemCredential, err := base64.StdEncoding.DecodeString(backup.Credential)\n\tif err != nil {\n\t\tres.Msg = err.Error()\n\t\treturn res\n\t}\n\tbackup.Credential = string(itemCredential)\n\n\tif req.Type == constant.OneDrive || req.Type == constant.GoogleDrive {\n\t\trefreshToken, err := loadRefreshTokenByCode(&backup)\n\t\tif err != nil {\n\t\t\tres.Msg = err.Error()\n\t\t\treturn res\n\t\t}\n\t\tres.Token = base64.StdEncoding.EncodeToString([]byte(refreshToken))\n\t}\n\tisOk, err := u.checkBackupConn(&backup)\n\tif err != nil {\n\t\tres.Msg = err.Error()\n\t\treturn res\n\t}\n\tres.IsOk = isOk\n\treturn res\n}\n\nfunc (u *BackupService) Create(req dto.BackupOperate) error {\n\tif req.Type == constant.Local {\n\t\treturn buserr.New(\"ErrBackupLocalCreate\")\n\t}\n\tif req.Type != constant.Sftp {\n\t\treq.BackupPath = strings.TrimPrefix(req.BackupPath, \"/\")\n\t}\n\tbackup, _ := backupRepo.Get(repo.WithByName(req.Name))\n\tif backup.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tif err := copier.Copy(&backup, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\titemAccessKey, err := base64.StdEncoding.DecodeString(backup.AccessKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackup.AccessKey = string(itemAccessKey)\n\titemCredential, err := base64.StdEncoding.DecodeString(backup.Credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackup.Credential = string(itemCredential)\n\tbackup.AccessKey, err = encrypt.StringEncrypt(backup.AccessKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackup.Credential, err = encrypt.StringEncrypt(backup.Credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := backupRepo.Create(&backup); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) GetBuckets(req dto.ForBuckets) ([]interface{}, error) {\n\titemAccessKey, err := base64.StdEncoding.DecodeString(req.AccessKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.AccessKey = string(itemAccessKey)\n\titemCredential, err := base64.StdEncoding.DecodeString(req.Credential)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Credential = string(itemCredential)\n\n\tvarMap := make(map[string]interface{})\n\tif err := json.Unmarshal([]byte(req.Vars), &varMap); err != nil {\n\t\treturn nil, err\n\t}\n\tswitch req.Type {\n\tcase constant.Sftp, constant.WebDAV:\n\t\tvarMap[\"username\"] = req.AccessKey\n\t\tvarMap[\"password\"] = req.Credential\n\tcase constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:\n\t\tvarMap[\"accessKey\"] = req.AccessKey\n\t\tvarMap[\"secretKey\"] = req.Credential\n\t}\n\tclient, err := cloud_storage.NewCloudStorageClient(req.Type, varMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn client.ListBuckets()\n}\n\nfunc (u *BackupService) Delete(id uint) error {\n\tbackup, _ := backupRepo.Get(repo.WithByID(id))\n\tif backup.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif backup.Type == constant.Local {\n\t\treturn buserr.New(\"ErrBackupLocalDelete\")\n\t}\n\tif err := u.CheckUsed(backup.Name, false); err != nil {\n\t\treturn err\n\t}\n\treturn backupRepo.Delete(repo.WithByID(id))\n}\n\nfunc (u *BackupService) Update(req dto.BackupOperate) error {\n\tbackup, _ := backupRepo.Get(repo.WithByID(req.ID))\n\tif backup.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif req.Type != constant.Sftp && req.Type != constant.Local && req.BackupPath != \"/\" {\n\t\treq.BackupPath = strings.TrimPrefix(req.BackupPath, \"/\")\n\t}\n\tvar newBackup model.BackupAccount\n\tif err := copier.Copy(&newBackup, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\titemAccessKey, err := base64.StdEncoding.DecodeString(newBackup.AccessKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewBackup.AccessKey = string(itemAccessKey)\n\titemCredential, err := base64.StdEncoding.DecodeString(newBackup.Credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewBackup.Credential = string(itemCredential)\n\tif backup.Type == constant.Local {\n\t\tif err := changeLocalBackup(backup.BackupPath, newBackup.BackupPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tglobal.Dir.LocalBackupDir = newBackup.BackupPath\n\t}\n\n\tif backup.Type != constant.Local {\n\t\tnewBackup.AccessKey, err = encrypt.StringEncrypt(newBackup.AccessKey)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tnewBackup.Credential, err = encrypt.StringEncrypt(newBackup.Credential)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tnewBackup.ID = backup.ID\n\tnewBackup.CreatedAt = backup.CreatedAt\n\tnewBackup.UpdatedAt = backup.UpdatedAt\n\tif err := backupRepo.Save(&newBackup); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) RefreshToken(req dto.OperateByID) error {\n\tbackup, _ := backupRepo.Get(repo.WithByID(req.ID))\n\tif backup.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tvarMap := make(map[string]interface{})\n\tif err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {\n\t\treturn fmt.Errorf(\"failed to refresh %s - %s token, please retry, err: %v\", backup.Type, backup.Name, err)\n\t}\n\tvar (\n\t\trefreshToken string\n\t\terr          error\n\t)\n\tswitch backup.Type {\n\tcase constant.OneDrive:\n\t\trefreshToken, err = client.RefreshToken(\"refresh_token\", \"refreshToken\", varMap)\n\tcase constant.ALIYUN:\n\t\trefreshToken, err = client.RefreshALIToken(varMap)\n\t}\n\tif err != nil {\n\t\tvarMap[\"refresh_status\"] = constant.StatusFailed\n\t\tvarMap[\"refresh_msg\"] = err.Error()\n\t\treturn fmt.Errorf(\"failed to refresh %s-%s token, please retry, err: %v\", backup.Type, backup.Name, err)\n\t}\n\tvarMap[\"refresh_status\"] = constant.StatusSuccess\n\tvarMap[\"refresh_time\"] = time.Now().Format(constant.DateTimeLayout)\n\tvarMap[\"refresh_token\"] = refreshToken\n\n\tvarsItem, _ := json.Marshal(varMap)\n\tbackup.Vars = string(varsItem)\n\treturn backupRepo.Save(&backup)\n}\n\nfunc (u *BackupService) UploadForRecover(req dto.UploadForRecover) error {\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(req.TargetDir) {\n\t\tif err := fileOp.CreateDir(req.TargetDir, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn fileOp.Copy(req.FilePath, req.TargetDir)\n}\n\nfunc (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, error) {\n\tclient, err := newClient(backup, false)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tfileItem := path.Join(global.Dir.BaseDir, \"1panel/tmp/test/1panel\")\n\tif _, err := os.Stat(path.Dir(fileItem)); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(path.Dir(fileItem), os.ModePerm); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tfile, err := os.OpenFile(fileItem, os.O_WRONLY|os.O_CREATE, constant.FilePerm)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(\"1Panel 备份账号测试文件。\\n\")\n\t_, _ = write.WriteString(\"1Panel 備份賬號測試文件。\\n\")\n\t_, _ = write.WriteString(\"1Panel Backs up account test files.\\n\")\n\t_, _ = write.WriteString(\"1Panelアカウントのテストファイルをバックアップします。\\n\")\n\twrite.Flush()\n\n\ttargetPath := path.Join(backup.BackupPath, \"test/1panel\")\n\tif backup.Type != constant.Sftp && backup.Type != constant.Local && targetPath != \"/\" {\n\t\ttargetPath = strings.TrimPrefix(targetPath, \"/\")\n\t}\n\n\tif _, err := client.Upload(fileItem, targetPath); err != nil {\n\t\treturn false, err\n\t}\n\t_, _ = client.Delete(path.Join(backup.BackupPath, \"test/1panel\"))\n\treturn true, nil\n}\n\nfunc (u *BackupService) LoadBackupOptions() ([]dto.BackupOption, error) {\n\taccounts, err := backupRepo.List(repo.WithOrderDesc(\"created_at\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar data []dto.BackupOption\n\tfor _, account := range accounts {\n\t\tvar item dto.BackupOption\n\t\tif err := copier.Copy(&item, &account); err != nil {\n\t\t\tglobal.LOG.Errorf(\"copy backup account to dto backup info failed, err: %v\", err)\n\t\t}\n\t\tdata = append(data, item)\n\t}\n\treturn data, nil\n}\n\nfunc (u *BackupService) CheckUsed(name string, isPublic bool) error {\n\taccount, _ := backupRepo.Get(repo.WithByName(name), backupRepo.WithByPublic(isPublic))\n\tif account.ID == 0 {\n\t\treturn nil\n\t}\n\tcronjobs, _ := cronjobRepo.List()\n\tfor _, job := range cronjobs {\n\t\tif job.DownloadAccountID == account.ID {\n\t\t\treturn buserr.New(\"ErrBackupInUsed\")\n\t\t}\n\t\tids := strings.Split(job.SourceAccountIDs, \",\")\n\t\tfor _, idItem := range ids {\n\t\t\tif idItem == fmt.Sprintf(\"%v\", account.ID) {\n\t\t\t\treturn buserr.New(\"ErrBackupInUsed\")\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc NewBackupClientWithID(id uint) (*model.BackupAccount, cloud_storage.CloudStorageClient, error) {\n\taccount, _ := backupRepo.Get(repo.WithByID(id))\n\tbackClient, err := newClient(&account, true)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn &account, backClient, nil\n}\n\ntype backupClientHelper struct {\n\tid          uint\n\taccountType string\n\tname        string\n\tbackupPath  string\n\tclient      cloud_storage.CloudStorageClient\n\n\tisOk      bool\n\thasBackup bool\n\tmessage   string\n}\n\nfunc NewBackupClientMap(ids []string) map[string]backupClientHelper {\n\tvar accounts []model.BackupAccount\n\tvar idItems []uint\n\tfor i := 0; i < len(ids); i++ {\n\t\titem, _ := strconv.Atoi(ids[i])\n\t\tidItems = append(idItems, uint(item))\n\t}\n\taccounts, _ = backupRepo.List(repo.WithByIDs(idItems))\n\tclientMap := make(map[string]backupClientHelper)\n\tfor _, item := range accounts {\n\t\tbackClient, err := newClient(&item, true)\n\t\titemHelper := backupClientHelper{\n\t\t\tclient:      backClient,\n\t\t\tname:        item.Name,\n\t\t\tbackupPath:  item.BackupPath,\n\t\t\taccountType: item.Type,\n\t\t\tid:          item.ID,\n\t\t\tisOk:        err == nil,\n\t\t}\n\t\tif err != nil {\n\t\t\titemHelper.message = err.Error()\n\t\t}\n\t\tclientMap[fmt.Sprintf(\"%v\", item.ID)] = itemHelper\n\t}\n\treturn clientMap\n}\n\nfunc uploadWithMap(taskItem task.Task, accountMap map[string]backupClientHelper, src, dst, accountIDs string, downloadAccountID, retry uint) error {\n\taccounts := strings.Split(accountIDs, \",\")\n\tfor _, account := range accounts {\n\t\tif len(account) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\titemBackup, ok := accountMap[account]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tif itemBackup.hasBackup {\n\t\t\tcontinue\n\t\t}\n\t\tif !itemBackup.isOk {\n\t\t\ttaskItem.LogFailed(i18n.GetMsgWithDetail(\"LoadBackupFailed\", itemBackup.message))\n\t\t\tcontinue\n\t\t}\n\t\tname := itemBackup.name\n\t\tif itemBackup.name == \"localhost\" {\n\t\t\tname = i18n.GetMsgByKey(\"Localhost\")\n\t\t}\n\t\ttaskItem.LogStart(i18n.GetMsgWithMap(\"UploadFile\", map[string]interface{}{\n\t\t\t\"file\":   path.Join(itemBackup.backupPath, dst),\n\t\t\t\"backup\": name,\n\t\t}))\n\t\tfor i := 0; i < int(retry)+1; i++ {\n\t\t\t_, err := itemBackup.client.Upload(src, path.Join(itemBackup.backupPath, dst))\n\t\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"Upload\"), err)\n\t\t\tif err != nil {\n\t\t\t\tif account == fmt.Sprintf(\"%d\", downloadAccountID) {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\titemBackup.hasBackup = true\n\t\taccountMap[account] = itemBackup\n\t}\n\tos.RemoveAll(src)\n\treturn nil\n}\n\nfunc newClient(account *model.BackupAccount, isEncrypt bool) (cloud_storage.CloudStorageClient, error) {\n\tvarMap := make(map[string]interface{})\n\tif len(account.Vars) != 0 {\n\t\tif err := json.Unmarshal([]byte(account.Vars), &varMap); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tvarMap[\"bucket\"] = account.Bucket\n\tvarMap[\"backupPath\"] = account.BackupPath\n\tif isEncrypt {\n\t\taccount.AccessKey, _ = encrypt.StringDecrypt(account.AccessKey)\n\t\taccount.Credential, _ = encrypt.StringDecrypt(account.Credential)\n\t}\n\tswitch account.Type {\n\tcase constant.Sftp, constant.WebDAV:\n\t\tvarMap[\"username\"] = account.AccessKey\n\t\tvarMap[\"password\"] = account.Credential\n\tcase constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:\n\t\tvarMap[\"accessKey\"] = account.AccessKey\n\t\tvarMap[\"secretKey\"] = account.Credential\n\tcase constant.UPYUN:\n\t\tvarMap[\"operator\"] = account.AccessKey\n\t\tvarMap[\"password\"] = account.Credential\n\t}\n\n\tclient, err := cloud_storage.NewCloudStorageClient(account.Type, varMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn client, nil\n}\n\nfunc loadRefreshTokenByCode(backup *model.BackupAccount) (string, error) {\n\tvarMap := make(map[string]interface{})\n\tif err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {\n\t\treturn \"\", fmt.Errorf(\"unmarshal backup vars failed, err: %v\", err)\n\t}\n\tif token, ok := varMap[\"refresh_token\"]; ok && len(token.(string)) != 0 {\n\t\treturn \"\", nil\n\t}\n\trefreshToken := \"\"\n\tvar err error\n\tswitch backup.Type {\n\tcase constant.GoogleDrive:\n\t\trefreshToken, err = client.RefreshGoogleToken(\"authorization_code\", \"refreshToken\", varMap)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\tcase constant.OneDrive:\n\t\trefreshToken, err = client.RefreshToken(\"authorization_code\", \"refreshToken\", varMap)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tif backup.Type != constant.ALIYUN {\n\t\tvarMap[\"refresh_token\"] = refreshToken\n\t}\n\titemVars, _ := json.Marshal(varMap)\n\tbackup.Vars = string(itemVars)\n\treturn refreshToken, nil\n}\n\nfunc loadBackupNamesByID(accountIDs string, downloadID uint) ([]string, string, error) {\n\taccountIDList := strings.Split(accountIDs, \",\")\n\tvar ids []uint\n\tfor _, item := range accountIDList {\n\t\tif len(item) != 0 {\n\t\t\titemID, _ := strconv.Atoi(item)\n\t\t\tids = append(ids, uint(itemID))\n\t\t}\n\t}\n\tlist, err := backupRepo.List(repo.WithByIDs(ids))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tvar accounts []string\n\tvar downloadAccount string\n\tfor _, item := range list {\n\t\taccounts = append(accounts, item.Name)\n\t\tif item.ID == downloadID {\n\t\t\tdownloadAccount = item.Name\n\t\t}\n\t}\n\treturn accounts, downloadAccount, nil\n}\n\nfunc changeLocalBackup(oldPath, newPath string) error {\n\tfileOp := files.NewFileOp()\n\tif fileOp.Stat(path.Join(oldPath, \"app\")) {\n\t\tif err := fileOp.CopyDir(path.Join(oldPath, \"app\"), newPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif fileOp.Stat(path.Join(oldPath, \"database\")) {\n\t\tif err := fileOp.CopyDir(path.Join(oldPath, \"database\"), newPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif fileOp.Stat(path.Join(oldPath, \"directory\")) {\n\t\tif err := fileOp.CopyDir(path.Join(oldPath, \"directory\"), newPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif fileOp.Stat(path.Join(oldPath, \"system_snapshot\")) {\n\t\tif err := fileOp.CopyDir(path.Join(oldPath, \"system_snapshot\"), newPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif fileOp.Stat(path.Join(oldPath, \"website\")) {\n\t\tif err := fileOp.CopyDir(path.Join(oldPath, \"website\"), newPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif fileOp.Stat(path.Join(oldPath, \"log\")) {\n\t\tif err := fileOp.CopyDir(path.Join(oldPath, \"log\"), newPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif fileOp.Stat(path.Join(oldPath, \"master\")) {\n\t\tif err := fileOp.CopyDir(path.Join(oldPath, \"master\"), newPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t_ = fileOp.RmRf(path.Join(oldPath, \"app\"))\n\t_ = fileOp.RmRf(path.Join(oldPath, \"database\"))\n\t_ = fileOp.RmRf(path.Join(oldPath, \"directory\"))\n\t_ = fileOp.RmRf(path.Join(oldPath, \"system_snapshot\"))\n\t_ = fileOp.RmRf(path.Join(oldPath, \"website\"))\n\t_ = fileOp.RmRf(path.Join(oldPath, \"log\"))\n\t_ = fileOp.RmRf(path.Join(oldPath, \"master\"))\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/backup_app.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/subosito/gotenv\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/pkg/errors\"\n)\n\nfunc (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, error) {\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(req.Name))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tinstall, err := appInstallRepo.GetFirst(repo.WithByName(req.DetailName), appInstallRepo.WithAppId(app.ID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttimeNow := time.Now().Format(constant.DateTimeSlimLayout)\n\titemDir := fmt.Sprintf(\"app/%s/%s\", req.Name, req.DetailName)\n\tbackupDir := path.Join(global.Dir.LocalBackupDir, itemDir)\n\n\tfileName := req.FileName\n\tif req.FileName == \"\" {\n\t\tfileName = fmt.Sprintf(\"%s_%s.tar.gz\", req.DetailName, timeNow+common.RandStrAndNum(5))\n\t}\n\n\trecord := &model.BackupRecord{\n\t\tType:              \"app\",\n\t\tName:              req.Name,\n\t\tDetailName:        req.DetailName,\n\t\tSourceAccountIDs:  \"1\",\n\t\tDownloadAccountID: 1,\n\t\tFileDir:           itemDir,\n\t\tFileName:          fileName,\n\t\tTaskID:            req.TaskID,\n\t\tStatus:            constant.StatusWaiting,\n\t\tDescription:       req.Description,\n\t}\n\tif err := backupRepo.CreateRecord(record); err != nil {\n\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\treturn nil, err\n\t}\n\n\tif err = handleAppBackup(&install, nil, record.ID, backupDir, fileName, \"\", req.Secret, req.TaskID); err != nil {\n\t\tglobal.LOG.Errorf(\"backup app %s failed, err: %v\", req.DetailName, err)\n\t\treturn nil, err\n\t}\n\n\treturn record, nil\n}\n\nfunc (u *BackupService) AppRecover(req dto.CommonRecover) error {\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(req.Name))\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstall, err := appInstallRepo.GetFirst(repo.WithByName(req.DetailName), appInstallRepo.WithAppId(app.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(req.File) {\n\t\treturn buserr.WithName(\"ErrFileNotFound\", req.File)\n\t}\n\tif _, err := compose.Down(install.GetComposePath()); err != nil {\n\t\treturn err\n\t}\n\tif err := handleAppRecover(&install, nil, req.File, false, req.Secret, req.TaskID); err != nil {\n\t\tglobal.LOG.Errorf(\"recover app %s failed, err: %v\", req.DetailName, err)\n\t}\n\treturn nil\n}\n\nfunc backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name string, databaseID uint) error {\n\tswitch resourceKey {\n\tcase constant.AppMysql, constant.AppMariaDB:\n\t\tdb, err := mysqlRepo.Get(repo.WithByID(databaseID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))\n\t\tdatabaseHelper := DatabaseHelper{Database: db.MysqlName, DBType: resourceKey, Name: db.Name}\n\t\tif err := handleMysqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf(\"%s.sql.gz\", name), \"\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))\n\tcase constant.AppPostgresql:\n\t\tdb, err := postgresqlRepo.Get(repo.WithByID(databaseID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))\n\t\tdatabaseHelper := DatabaseHelper{Database: db.PostgresqlName, DBType: resourceKey, Name: db.Name}\n\t\tif err := handlePostgresqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf(\"%s.sql.gz\", name), \"\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))\n\t}\n\treturn nil\n}\n\nfunc handleAppBackup(install *model.AppInstall, parentTask *task.Task, recordID uint, backupDir, fileName, excludes, secret, taskID string) error {\n\tvar (\n\t\terr        error\n\t\tbackupTask *task.Task\n\t)\n\tbackupTask = parentTask\n\tif parentTask == nil {\n\t\tbackupTask, err = task.NewTaskWithOps(install.Name, task.TaskBackup, task.TaskScopeBackup, taskID, install.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\titemHandler := func() error { return doAppBackup(install, backupTask, backupDir, fileName, excludes, secret) }\n\tif parentTask != nil {\n\t\treturn itemHandler()\n\t}\n\n\tbackupTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)\n\tgo func() {\n\t\tif err := backupTask.Execute(); err != nil {\n\t\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\treturn\n\t\t}\n\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusSuccess})\n\t}()\n\n\treturn nil\n}\n\nfunc handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {\n\tvar (\n\t\terr          error\n\t\trecoverTask  *task.Task\n\t\tisOk         = false\n\t\trollbackFile string\n\t)\n\trecoverTask = parentTask\n\tif parentTask == nil {\n\t\trecoverTask, err = task.NewTaskWithOps(install.Name, task.TaskRecover, task.TaskScopeBackup, taskID, install.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\trecoverApp := func(t *task.Task) error {\n\t\tfileOp := files.NewFileOp()\n\t\tif !isRollback {\n\t\t\trollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf(\"app/%s_%s.tar.gz\", install.Name, time.Now().Format(constant.DateTimeSlimLayout)))\n\t\t\tif err := handleAppBackup(install, recoverTask, 0, path.Dir(rollbackFile), path.Base(rollbackFile), \"\", \"\", taskID); err != nil {\n\t\t\t\tt.Log(fmt.Sprintf(\"backup app %s for rollback before recover failed, err: %v\", install.Name, err))\n\t\t\t}\n\t\t}\n\n\t\tif err := fileOp.TarGzExtractPro(recoverFile, path.Dir(recoverFile), secret); err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttmpPath := strings.ReplaceAll(recoverFile, \".tar.gz\", \"\")\n\t\tif err := fileOp.TarGzExtractPro(tmpPath+\"/app.tar.gz\", tmpPath, \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() {\n\t\t\t_, _ = compose.Up(install.GetComposePath())\n\t\t\t_ = os.RemoveAll(strings.ReplaceAll(recoverFile, \".tar.gz\", \"\"))\n\t\t}()\n\n\t\tif !fileOp.Stat(tmpPath+\"/app.json\") || !fileOp.Stat(tmpPath+\"/app.tar.gz\") {\n\t\t\treturn errors.New(i18n.GetMsgByKey(\"AppBackupFileIncomplete\"))\n\t\t}\n\t\tvar backupInstall model.AppInstall\n\t\tappJson, err := os.ReadFile(tmpPath + \"/app.json\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := json.Unmarshal(appJson, &backupInstall); err != nil {\n\t\t\treturn fmt.Errorf(\"unmarshal app.json failed, err: %v\", err)\n\t\t}\n\t\tif backupInstall.App.Key != install.App.Key || backupInstall.Name != install.Name {\n\t\t\treturn errors.New(i18n.GetMsgByKey(\"AppAttributesNotMatch\"))\n\t\t}\n\t\tbackupEnvMap, err := getEnvMapByPath(path.Join(tmpPath, install.Name, \".env\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinstalledEnvMap, err := getEnvMapByPath(install.GetEnvPath())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar mergedEnvContent string\n\t\tresources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))\n\t\tfor _, resource := range resources {\n\t\t\tvar database model.Database\n\t\t\tswitch resource.From {\n\t\t\tcase constant.AppResourceRemote:\n\t\t\t\tdatabase, err = databaseRepo.Get(repo.WithByID(resource.LinkId))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\tcase constant.AppResourceLocal:\n\t\t\t\tresourceApp, err := appInstallRepo.GetFirst(repo.WithByID(resource.LinkId))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdatabase, err = databaseRepo.Get(databaseRepo.WithAppInstallID(resourceApp.ID), repo.WithByType(resource.Key), repo.WithByFrom(constant.AppResourceLocal), repo.WithByName(resourceApp.Name))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch database.Type {\n\t\t\tcase constant.AppPostgresql:\n\t\t\t\tdb, err := postgresqlRepo.Get(repo.WithByID(resource.ResourceId))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tnewDB, err := reCreatePostgresqlDB(db.ID, database, backupEnvMap)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tbackupInstall.Env, mergedEnvContent, err = buildRecoverEnv(backupInstall.Env, backupEnvMap, installedEnvMap)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{\"resource_id\": newDB.ID}, repo.WithByID(resource.ID))\n\t\t\t\ttaskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)\n\t\t\t\tt.LogStart(taskName)\n\t\t\t\tif err := handlePostgresqlRecover(dto.CommonRecover{\n\t\t\t\t\tName:       newDB.PostgresqlName,\n\t\t\t\t\tDetailName: newDB.Name,\n\t\t\t\t\tFile:       fmt.Sprintf(\"%s/%s.sql.gz\", tmpPath, install.Name),\n\t\t\t\t}, parentTask, true); err != nil {\n\t\t\t\t\tt.LogFailedWithErr(taskName, err)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tt.LogSuccess(taskName)\n\t\t\tcase constant.AppMysql, constant.AppMariaDB:\n\t\t\t\tdb, err := mysqlRepo.Get(repo.WithByID(resource.ResourceId))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tnewDB, err := reCreateDB(db.ID, database, backupEnvMap)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tbackupInstall.Env, mergedEnvContent, err = buildRecoverEnv(backupInstall.Env, backupEnvMap, installedEnvMap)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{\"resource_id\": newDB.ID}, repo.WithByID(resource.ID))\n\t\t\t\ttaskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)\n\t\t\t\tt.LogStart(taskName)\n\t\t\t\tif err := handleMysqlRecover(dto.CommonRecover{\n\t\t\t\t\tName:       newDB.MysqlName,\n\t\t\t\t\tDetailName: newDB.Name,\n\t\t\t\t\tFile:       fmt.Sprintf(\"%s/%s.sql.gz\", tmpPath, install.Name),\n\t\t\t\t}, parentTask, true); err != nil {\n\t\t\t\t\tt.LogFailedWithErr(taskName, err)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tt.LogSuccess(taskName)\n\t\t\t}\n\t\t}\n\n\t\tappDir := install.GetPath()\n\t\tbackPath := fmt.Sprintf(\"%s_bak\", appDir)\n\t\t_ = fileOp.Rename(appDir, backPath)\n\t\t_ = fileOp.CreateDir(appDir, constant.DirPerm)\n\n\t\tdeCompressName := i18n.GetWithName(\"DeCompressFile\", \"app.tar.gz\")\n\t\tt.LogStart(deCompressName)\n\t\tif err := fileOp.TarGzExtractPro(tmpPath+\"/app.tar.gz\", install.GetAppPath(), \"\"); err != nil {\n\t\t\tt.LogFailedWithErr(deCompressName, err)\n\t\t\t_ = fileOp.DeleteDir(appDir)\n\t\t\t_ = fileOp.Rename(backPath, appDir)\n\t\t\treturn err\n\t\t}\n\t\tt.LogSuccess(deCompressName)\n\t\t_ = fileOp.DeleteDir(backPath)\n\n\t\tif len(mergedEnvContent) != 0 {\n\t\t\tenvPath := fmt.Sprintf(\"%s/%s/.env\", install.GetAppPath(), install.Name)\n\t\t\tfile, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer file.Close()\n\t\t\t_, _ = file.WriteString(mergedEnvContent)\n\t\t}\n\n\t\tbackupInstall.ID = install.ID\n\t\tbackupInstall.Status = constant.StatusRunning\n\t\tbackupInstall.AppId = install.AppId\n\t\tbackupInstall.AppDetailId = install.AppDetailId\n\t\tbackupInstall.App.ID = install.AppId\n\t\tif err := appInstallRepo.Save(context.Background(), &backupInstall); err != nil {\n\t\t\tglobal.LOG.Errorf(\"save db app install failed, err: %v\", err)\n\t\t\treturn err\n\t\t}\n\t\tisOk = true\n\n\t\treturn nil\n\t}\n\n\tif parentTask != nil {\n\t\treturn recoverApp(parentTask)\n\t}\n\n\trollBackApp := func(t *task.Task) {\n\t\tif isRollback {\n\t\t\treturn\n\t\t}\n\t\tif !isOk {\n\t\t\tt.Log(i18n.GetMsgByKey(\"RecoverFailedStartRollBack\"))\n\t\t\tif err := handleAppRecover(install, t, rollbackFile, true, \"\", \"\"); err != nil {\n\t\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"Rollback\"), err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.LogSuccess(i18n.GetMsgByKey(\"Rollback\"))\n\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t} else {\n\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t}\n\t}\n\trecoverTask.AddSubTask(task.GetTaskName(install.Name, task.TaskRecover, task.TaskScopeBackup), recoverApp, rollBackApp)\n\tgo func() {\n\t\t_ = recoverTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc doAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fileName, excludes, secret string) error {\n\tfileOp := files.NewFileOp()\n\ttmpDir := fmt.Sprintf(\"%s/%s\", backupDir, strings.ReplaceAll(fileName, \".tar.gz\", \"\"))\n\tif !fileOp.Stat(tmpDir) {\n\t\tif err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", backupDir, err)\n\t\t}\n\t}\n\tdefer func() {\n\t\t_ = os.RemoveAll(tmpDir)\n\t}()\n\n\tremarkInfo, _ := json.Marshal(install)\n\tremarkInfoPath := fmt.Sprintf(\"%s/app.json\", tmpDir)\n\tif err := fileOp.SaveFile(remarkInfoPath, string(remarkInfo), fs.ModePerm); err != nil {\n\t\treturn err\n\t}\n\n\tappPath := install.GetPath()\n\tparentTask.LogStart(i18n.GetMsgByKey(\"TaskBackup\"))\n\tif err := fileOp.TarGzCompressPro(true, appPath, path.Join(tmpDir, \"app.tar.gz\"), \"\", excludes); err != nil {\n\t\treturn err\n\t}\n\n\tresources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))\n\tfor _, resource := range resources {\n\t\tif err := backupDatabaseWithTask(parentTask, resource.Key, tmpDir, install.Name, resource.ResourceId); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tparentTask.LogStart(i18n.GetMsgByKey(\"CompressDir\"))\n\tif err := fileOp.TarGzCompressPro(true, tmpDir, path.Join(backupDir, fileName), secret, \"\"); err != nil {\n\t\treturn err\n\t}\n\tparentTask.Log(i18n.GetWithName(\"CompressFileSuccess\", fileName))\n\treturn nil\n}\n\nfunc reCreateDB(dbID uint, database model.Database, envMap map[string]interface{}) (*model.DatabaseMysql, error) {\n\tmysqlService := NewIMysqlService()\n\tctx := context.Background()\n\t_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, Database: database.Name, Type: database.Type, DeleteBackup: false, ForceDelete: true})\n\n\tdbInfo := getDBCreateInfoFromEnv(envMap, \"utf8mb4\")\n\tcreateDB, err := mysqlService.Create(context.Background(), dto.MysqlDBCreate{\n\t\tName:       dbInfo.Name,\n\t\tFrom:       database.From,\n\t\tDatabase:   database.Name,\n\t\tFormat:     dbInfo.Format,\n\t\tUsername:   dbInfo.User,\n\t\tPassword:   dbInfo.Password,\n\t\tPermission: \"%\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tupdateCronjobsDBRef(dbID, createDB.ID)\n\treturn createDB, nil\n}\n\nfunc reCreatePostgresqlDB(dbID uint, database model.Database, envMap map[string]interface{}) (*model.DatabasePostgresql, error) {\n\tpostgresqlService := NewIPostgresqlService()\n\t_ = postgresqlService.Delete(context.Background(), dto.PostgresqlDBDelete{\n\t\tID:           dbID,\n\t\tType:         database.Type,\n\t\tDatabase:     database.Name,\n\t\tDeleteBackup: false,\n\t\tForceDelete:  true,\n\t})\n\n\tdbInfo := getDBCreateInfoFromEnv(envMap, \"UTF8\")\n\tcreateDB, err := postgresqlService.Create(context.Background(), dto.PostgresqlDBCreate{\n\t\tName:      dbInfo.Name,\n\t\tFrom:      database.From,\n\t\tDatabase:  database.Name,\n\t\tFormat:    dbInfo.Format,\n\t\tUsername:  dbInfo.User,\n\t\tPassword:  dbInfo.Password,\n\t\tSuperUser: true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tupdateCronjobsDBRef(dbID, createDB.ID)\n\treturn createDB, nil\n}\n\ntype dbRecreateInfo struct {\n\tName     string\n\tUser     string\n\tPassword string\n\tFormat   string\n}\n\nfunc getDBCreateInfoFromEnv(envMap map[string]interface{}, defaultFormat string) dbRecreateInfo {\n\tname, _ := envMap[\"PANEL_DB_NAME\"].(string)\n\tuser, _ := envMap[\"PANEL_DB_USER\"].(string)\n\tpassword, _ := envMap[\"PANEL_DB_USER_PASSWORD\"].(string)\n\tformat, _ := envMap[\"format\"].(string)\n\tif len(format) == 0 {\n\t\tformat = defaultFormat\n\t}\n\treturn dbRecreateInfo{\n\t\tName:     name,\n\t\tUser:     user,\n\t\tPassword: password,\n\t\tFormat:   format,\n\t}\n}\n\nfunc updateCronjobsDBRef(oldDBID, newDBID uint) {\n\tcronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDbName(fmt.Sprintf(\"%v\", oldDBID)))\n\tfor _, job := range cronjobs {\n\t\t_ = cronjobRepo.Update(job.ID, map[string]interface{}{\"db_name\": fmt.Sprintf(\"%v\", newDBID)})\n\t}\n}\n\nfunc buildRecoverEnv(appEnv string, backupEnvMap, installedEnvMap map[string]interface{}) (string, string, error) {\n\tcurrentHostVal, hasCurrentHost := installedEnvMap[\"PANEL_DB_HOST\"]\n\tif hasCurrentHost && fmt.Sprintf(\"%v\", currentHostVal) != \"\" {\n\t\tbackupHost := fmt.Sprintf(\"\\\"PANEL_DB_HOST\\\":\\\"%v\\\"\", backupEnvMap[\"PANEL_DB_HOST\"])\n\t\tcurrentHost := fmt.Sprintf(\"\\\"PANEL_DB_HOST\\\":\\\"%v\\\"\", currentHostVal)\n\t\tappEnv = strings.ReplaceAll(appEnv, backupHost, currentHost)\n\t\tif _, ok := backupEnvMap[\"CASDOOR_DATASOURCE_NAME\"]; ok {\n\t\t\tbackupEnvMap[\"CASDOOR_DATASOURCE_NAME\"] = strings.ReplaceAll(\n\t\t\t\tfmt.Sprintf(\"%v\", backupEnvMap[\"CASDOOR_DATASOURCE_NAME\"]),\n\t\t\t\tfmt.Sprintf(\"%v\", backupEnvMap[\"PANEL_DB_HOST\"]),\n\t\t\t\tfmt.Sprintf(\"%v\", currentHostVal),\n\t\t\t)\n\t\t}\n\t\tbackupEnvMap[\"PANEL_DB_HOST\"] = currentHostVal\n\t}\n\tnewEnvMap := make(map[string]string, len(backupEnvMap))\n\thandleMap(backupEnvMap, newEnvMap)\n\tmergedEnvContent, err := gotenv.Marshal(newEnvMap)\n\tif err != nil {\n\t\treturn appEnv, \"\", err\n\t}\n\treturn appEnv, mergedEnvContent, nil\n}\n\nfunc getEnvMapByPath(envPath string) (map[string]interface{}, error) {\n\tenvMap := make(map[string]interface{})\n\tenvFile, err := os.ReadFile(envPath)\n\tif err != nil {\n\t\treturn envMap, err\n\t}\n\tlines := strings.Split(string(envFile), \"\\n\")\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" || strings.HasPrefix(line, \"#\") {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.SplitN(line, \"=\", 2)\n\t\tif len(parts) == 2 {\n\t\t\tvalue := strings.TrimSpace(parts[1])\n\t\t\tif len(value) >= 2 {\n\t\t\t\tif (value[0] == '\"' && value[len(value)-1] == '\"') || (value[0] == '\\'' && value[len(value)-1] == '\\'') {\n\t\t\t\t\tvalue = value[1 : len(value)-1]\n\t\t\t\t}\n\t\t\t}\n\t\t\tenvMap[parts[0]] = value\n\t\t}\n\t}\n\treturn envMap, nil\n}\n"
  },
  {
    "path": "agent/app/service/backup_compose.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\tdockerUtils \"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/api/types/filters\"\n\t\"github.com/docker/docker/client\"\n)\n\ntype composeBackupFile struct {\n\tOriginalPath string `json:\"originalPath\"`\n\tFileName     string `json:\"fileName\"`\n\tRelativePath string `json:\"relativePath,omitempty\"`\n\tBackupPath   string `json:\"backupPath\"`\n}\n\ntype composeBackupMeta struct {\n\tComposeName string              `json:\"composeName\"`\n\tComposePath string              `json:\"composePath\"`\n\tCreatedAt   string              `json:\"createdAt\"`\n\tFiles       []composeBackupFile `json:\"files\"`\n\tContainers  []string            `json:\"containers\"`\n}\n\ntype composeBackupContext struct {\n\treq          dto.CommonBackup\n\tcomposeName  string\n\tcomposePath  string\n\tcomposeFiles []string\n\tcomposeDir   string\n\tfileOp       files.FileOp\n\tdockerClient *client.Client\n\tstopped      bool\n\tbackupDir    string\n\tfileName     string\n\tfilePath     string\n\ttmpDir       string\n\tmeta         composeBackupMeta\n}\n\ntype composeRecoverContext struct {\n\treq         dto.CommonRecover\n\tfileOp      files.FileOp\n\ttmpDir      string\n\tmeta        composeBackupMeta\n\tcomposeName string\n\ttargetDir   string\n\tcomposePath string\n\tenqueued    bool\n}\n\nfunc (u *BackupService) ComposeBackup(req dto.CommonBackup) error {\n\ttimeNow := time.Now().Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)\n\tfileName := req.FileName\n\tif fileName == \"\" {\n\t\tfileName = fmt.Sprintf(\"%s_%s.tar.gz\", req.Name, timeNow)\n\t}\n\tif !strings.HasSuffix(fileName, \".tar.gz\") {\n\t\tfileName += \".tar.gz\"\n\t}\n\titemDir := fmt.Sprintf(\"compose/%s\", req.Name)\n\tbackupDir := path.Join(global.Dir.LocalBackupDir, itemDir)\n\trecord := &model.BackupRecord{\n\t\tType:              req.Type,\n\t\tName:              req.Name,\n\t\tSourceAccountIDs:  \"1\",\n\t\tDownloadAccountID: 1,\n\t\tFileDir:           itemDir,\n\t\tFileName:          fileName,\n\t\tTaskID:            req.TaskID,\n\t\tStatus:            constant.StatusWaiting,\n\t\tDescription:       req.Description,\n\t}\n\tif err := backupRepo.CreateRecord(record); err != nil {\n\t\tglobal.LOG.Errorf(\"save compose backup record failed, err: %v\", err)\n\t\treturn err\n\t}\n\tif err := handleComposeBackup(req, nil, record.ID, backupDir, fileName); err != nil {\n\t\tbackupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) ComposeRecover(req dto.CommonRecover) error {\n\treturn handleComposeRecover(req, nil)\n}\n\nfunc handleComposeBackup(req dto.CommonBackup, parentTask *task.Task, recordID uint, backupDir, fileName string) error {\n\tcomposeCtx, err := newComposeBackupContext(req, backupDir, fileName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontainerNames, err := loadComposeContainerNames(composeCtx)\n\tif err != nil {\n\t\tcomposeCtx.close()\n\t\treturn err\n\t}\n\n\tbackupTask := parentTask\n\tif backupTask == nil {\n\t\tbackupTask, err = task.NewTaskWithOps(composeCtx.composeName, task.TaskBackup, task.TaskScopeBackup, req.TaskID, 1)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif req.StopBefore {\n\t\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeBackupStop\"), func(t *task.Task) error {\n\t\t\treturn stepStopComposeForBackup(composeCtx)\n\t\t}, func(t *task.Task) {\n\t\t\t_ = stepStartComposeAfterBackup(composeCtx)\n\t\t}, 3, time.Hour)\n\t}\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeBackupPrepare\"), func(t *task.Task) error { return stepPrepareComposeBackup(composeCtx) }, nil, 3, time.Hour)\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeBackupFiles\"), func(t *task.Task) error { return stepBackupComposeFiles(composeCtx) }, nil, 3, time.Hour)\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeBackupContainers\"), func(t *task.Task) error { return nil }, nil, 3, time.Hour)\n\tfor _, containerName := range containerNames {\n\t\tbackupFileName := fmt.Sprintf(\"%s.tar.gz\", sanitizeComposeFileName(containerName))\n\t\tbackupFile := path.Join(composeCtx.tmpDir, \"containers\", backupFileName)\n\t\tif err := handleContainerBackup(containerName, backupTask, 0, path.Dir(backupFile), path.Base(backupFile), \"\", \"\", false); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcomposeCtx.meta.Containers = append(composeCtx.meta.Containers, path.Join(\"containers\", backupFileName))\n\t}\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeBackupMeta\"), func(t *task.Task) error { return stepWriteComposeBackupMeta(composeCtx) }, nil, 3, time.Hour)\n\tbackupTask.AddSubTaskWithOps(task.GetTaskName(composeCtx.composeName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error {\n\t\treturn stepPackComposeBackup(composeCtx)\n\t}, nil, 3, time.Hour)\n\tif req.StopBefore {\n\t\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeBackupStart\"), func(t *task.Task) error {\n\t\t\treturn stepStartComposeAfterBackup(composeCtx)\n\t\t}, nil, 3, time.Hour)\n\t}\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeBackupCleanup\"), func(t *task.Task) error {\n\t\tcomposeCtx.close()\n\t\treturn nil\n\t}, nil, 0, time.Hour)\n\tif parentTask != nil {\n\t\treturn nil\n\t}\n\tgo func() {\n\t\tdefer composeCtx.close()\n\t\tif err := backupTask.Execute(); err != nil {\n\t\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\treturn\n\t\t}\n\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusSuccess})\n\t}()\n\treturn nil\n}\n\nfunc loadComposeContainerNames(composeCtx *composeBackupContext) ([]string, error) {\n\toptions := container.ListOptions{All: true}\n\toptions.Filters = filters.NewArgs(filters.Arg(\"label\", composeProjectLabel+\"=\"+composeCtx.composeName))\n\tcontainers, err := composeCtx.dockerClient.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnames := make([]string, 0, len(containers))\n\tfor _, item := range containers {\n\t\tif len(item.Names) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tnames = append(names, strings.TrimPrefix(item.Names[0], \"/\"))\n\t}\n\tsort.Strings(names)\n\treturn names, nil\n}\n\nfunc handleComposeRecover(req dto.CommonRecover, parentTask *task.Task) error {\n\tvar recoverCtx *composeRecoverContext\n\n\trecoverTask := parentTask\n\tvar err error\n\tif recoverTask == nil {\n\t\tif isImportRecover(req) {\n\t\t\ttaskName := i18n.GetMsgByKey(\"TaskImport\") + i18n.GetMsgByKey(\"Compose\")\n\t\t\trecoverTask, err = task.NewTask(taskName, task.TaskImport, task.TaskScopeBackup, req.TaskID, 1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\ttaskName := req.Name\n\t\t\tif taskName == \"\" {\n\t\t\t\ttaskName = \"compose\"\n\t\t\t}\n\t\t\trecoverTask, err = task.NewTaskWithOps(taskName, task.TaskRecover, task.TaskScopeBackup, req.TaskID, 1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\ttimeout := loadRecoverTimeout(req.Timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeRecoverPrepare\"), func(t *task.Task) error {\n\t\tctx, err := newComposeRecoverContext(req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trecoverCtx = ctx\n\t\tif err := stepPrepareComposeRecover(recoverCtx); err != nil {\n\t\t\trecoverCtx.close()\n\t\t\trecoverCtx = nil\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}, func(t *task.Task) {\n\t\tif recoverCtx != nil {\n\t\t\trecoverCtx.close()\n\t\t\trecoverCtx = nil\n\t\t}\n\t}, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeRecoverExtract\"), func(t *task.Task) error { return stepExtractComposeRecover(recoverCtx) }, nil, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeRecoverMeta\"), func(t *task.Task) error {\n\t\tif err := stepLoadComposeRecoverMeta(recoverCtx); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tt.Log(i18n.GetMsgWithMap(\"ComposeRecoverMetaLogName\", map[string]interface{}{\n\t\t\t\"name\": recoverCtx.composeName,\n\t\t}))\n\t\tt.Log(i18n.GetMsgWithMap(\"ComposeRecoverMetaLogPath\", map[string]interface{}{\n\t\t\t\"backupPath\": recoverCtx.meta.ComposePath,\n\t\t\t\"targetDir\":  recoverCtx.targetDir,\n\t\t}))\n\t\tt.Log(i18n.GetMsgWithMap(\"ComposeRecoverMetaLogCount\", map[string]interface{}{\n\t\t\t\"files\":      len(recoverCtx.meta.Files),\n\t\t\t\"containers\": len(recoverCtx.meta.Containers),\n\t\t}))\n\t\treturn nil\n\t}, nil, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeRecoverFiles\"), func(t *task.Task) error { return stepRestoreComposeFiles(recoverCtx) }, nil, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeRecoverContainers\"), func(t *task.Task) error {\n\t\tif recoverCtx.enqueued {\n\t\t\treturn nil\n\t\t}\n\t\tcontainerItems := make([]string, 0, len(recoverCtx.meta.Containers))\n\t\tfor _, item := range recoverCtx.meta.Containers {\n\t\t\tbackupItem := item\n\t\t\tfilePath, err := safeJoinWithinBase(recoverCtx.tmpDir, backupItem)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"invalid container backup path %q, err: %v\", backupItem, err)\n\t\t\t}\n\t\t\tif !recoverCtx.fileOp.Stat(filePath) {\n\t\t\t\treturn fmt.Errorf(\"container backup file not found: %s\", backupItem)\n\t\t\t}\n\t\t\tcontainerItems = append(containerItems, backupItem)\n\t\t}\n\t\tfor _, backupItem := range containerItems {\n\t\t\tfilePath, err := safeJoinWithinBase(recoverCtx.tmpDir, backupItem)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"invalid container backup path %q, err: %v\", backupItem, err)\n\t\t\t}\n\t\t\tcontainerLabel := strings.TrimSuffix(path.Base(backupItem), \".tar.gz\")\n\t\t\tcontainerReq := recoverCtx.req\n\t\t\tcontainerReq.Type = \"container\"\n\t\t\tcontainerReq.Name = containerLabel\n\t\t\tcontainerReq.DetailName = \"\"\n\t\t\tcontainerReq.File = filePath\n\t\t\tcontainerReq.Secret = \"\"\n\t\t\tif err := handleContainerRecover(containerReq, recoverTask); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeRecoverRecord\"), func(t *task.Task) error {\n\t\t\treturn stepSaveComposeRecord(recoverCtx)\n\t\t}, nil, 3, timeout)\n\t\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ComposeRecoverCleanup\"), func(t *task.Task) error {\n\t\t\tif recoverCtx != nil {\n\t\t\t\trecoverCtx.close()\n\t\t\t\trecoverCtx = nil\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil, 0, timeout)\n\t\trecoverCtx.enqueued = true\n\t\treturn nil\n\t}, nil, 3, timeout)\n\tif parentTask != nil {\n\t\treturn nil\n\t}\n\tgo func() {\n\t\t_ = recoverTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc newComposeBackupContext(req dto.CommonBackup, backupDir, fileName string) (*composeBackupContext, error) {\n\tif req.Name == \"\" {\n\t\treturn nil, fmt.Errorf(\"compose name is required\")\n\t}\n\tdockerClient, err := dockerUtils.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcomposePath, composeFiles, err := loadComposePathAndFiles(req.Name, dockerClient)\n\tif err != nil {\n\t\t_ = dockerClient.Close()\n\t\treturn nil, err\n\t}\n\tfilePath := path.Join(backupDir, fileName)\n\ttmpDir := path.Join(path.Dir(filePath), strings.TrimSuffix(path.Base(filePath), \".tar.gz\"))\n\tctx := &composeBackupContext{\n\t\treq:          req,\n\t\tcomposeName:  req.Name,\n\t\tcomposePath:  composePath,\n\t\tcomposeFiles: composeFiles,\n\t\tcomposeDir:   path.Dir(composeFiles[0]),\n\t\tfileOp:       files.NewFileOp(),\n\t\tdockerClient: dockerClient,\n\t\tbackupDir:    backupDir,\n\t\tfileName:     fileName,\n\t\tfilePath:     filePath,\n\t\ttmpDir:       tmpDir,\n\t\tmeta: composeBackupMeta{\n\t\t\tComposeName: req.Name,\n\t\t\tComposePath: composePath,\n\t\t\tCreatedAt:   time.Now().Format(constant.DateTimeLayout),\n\t\t\tFiles:       make([]composeBackupFile, 0),\n\t\t\tContainers:  make([]string, 0),\n\t\t},\n\t}\n\treturn ctx, nil\n}\n\nfunc loadComposePathAndFiles(composeName string, dockerClient *client.Client) (string, []string, error) {\n\tcomposeRecord, _ := composeRepo.GetRecord(repo.WithByName(composeName))\n\tif composeRecord.ID == 0 {\n\t\tcomposeRecord, _ = composeRepo.GetRecord(repo.WithByName(strings.ToLower(composeName)))\n\t}\n\tcomposePath := composeRecord.Path\n\tif composePath == \"\" {\n\t\toptions := container.ListOptions{All: true}\n\t\toptions.Filters = filters.NewArgs(filters.Arg(\"label\", composeProjectLabel))\n\t\tlist, err := dockerClient.ContainerList(context.Background(), options)\n\t\tif err != nil {\n\t\t\treturn \"\", nil, err\n\t\t}\n\t\tif len(list) == 0 {\n\t\t\treturn \"\", nil, fmt.Errorf(\"compose %s not found\", composeName)\n\t\t}\n\t\tvar targetContainer *container.Summary\n\t\tfor i := range list {\n\t\t\tif strings.EqualFold(list[i].Labels[composeProjectLabel], composeName) {\n\t\t\t\ttargetContainer = &list[i]\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif targetContainer == nil {\n\t\t\treturn \"\", nil, fmt.Errorf(\"compose %s not found\", composeName)\n\t\t}\n\t\tconfig := targetContainer.Labels[composeConfigLabel]\n\t\tworkdir := targetContainer.Labels[composeWorkdirLabel]\n\t\tif len(config) != 0 && len(workdir) != 0 && strings.Contains(config, workdir) {\n\t\t\tcomposePath = config\n\t\t} else {\n\t\t\tcomposePath = workdir\n\t\t}\n\t}\n\tcomposeFiles := normalizeComposeFiles(composePath)\n\tif len(composeFiles) == 0 {\n\t\treturn \"\", nil, fmt.Errorf(\"compose file not found for %s\", composeName)\n\t}\n\treturn composePath, composeFiles, nil\n}\n\nfunc normalizeComposeFiles(composePath string) []string {\n\titems := strings.Split(composePath, \",\")\n\tresult := make([]string, 0)\n\tseen := make(map[string]struct{})\n\tfor _, item := range items {\n\t\titem = strings.TrimSpace(item)\n\t\tif item == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tstat, err := os.Stat(item)\n\t\tif err == nil && stat.IsDir() {\n\t\t\titem = path.Join(item, \"docker-compose.yml\")\n\t\t}\n\t\tif _, err := os.Stat(item); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[item]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[item] = struct{}{}\n\t\tresult = append(result, item)\n\t}\n\tsort.Strings(result)\n\treturn result\n}\n\nfunc (c *composeBackupContext) close() {\n\tif c.dockerClient != nil {\n\t\t_ = c.dockerClient.Close()\n\t\tc.dockerClient = nil\n\t}\n\tif c.tmpDir != \"\" {\n\t\t_ = os.RemoveAll(c.tmpDir)\n\t\tc.tmpDir = \"\"\n\t}\n}\n\nfunc stepPrepareComposeBackup(composeCtx *composeBackupContext) error {\n\tif err := os.MkdirAll(composeCtx.backupDir, os.ModePerm); err != nil {\n\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", composeCtx.backupDir, err)\n\t}\n\t_ = os.RemoveAll(composeCtx.tmpDir)\n\tif err := os.MkdirAll(path.Join(composeCtx.tmpDir, \"compose_files\"), os.ModePerm); err != nil {\n\t\treturn err\n\t}\n\tif err := os.MkdirAll(path.Join(composeCtx.tmpDir, \"containers\"), os.ModePerm); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc stepStopComposeForBackup(composeCtx *composeBackupContext) error {\n\tif composeCtx.stopped {\n\t\treturn nil\n\t}\n\toptions := container.ListOptions{All: false}\n\toptions.Filters = filters.NewArgs(filters.Arg(\"label\", composeProjectLabel+\"=\"+composeCtx.composeName))\n\trunningList, err := composeCtx.dockerClient.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(runningList) == 0 {\n\t\treturn nil\n\t}\n\tif stdout, err := compose.Operate(composeCtx.composePath, \"stop\"); err != nil {\n\t\treturn fmt.Errorf(\"docker-compose stop failed, std: %s, err: %v\", stdout, err)\n\t}\n\tcomposeCtx.stopped = true\n\treturn nil\n}\n\nfunc stepStartComposeAfterBackup(composeCtx *composeBackupContext) error {\n\tif !composeCtx.stopped {\n\t\treturn nil\n\t}\n\tif stdout, err := compose.Up(composeCtx.composePath); err != nil {\n\t\treturn fmt.Errorf(\"docker-compose up failed, std: %s, err: %v\", stdout, err)\n\t}\n\tcomposeCtx.stopped = false\n\treturn nil\n}\n\nfunc stepBackupComposeFiles(composeCtx *composeBackupContext) error {\n\tfor i, filePath := range composeCtx.composeFiles {\n\t\tbackupName := fmt.Sprintf(\"%02d_%s\", i, path.Base(filePath))\n\t\tbackupPath := path.Join(composeCtx.tmpDir, \"compose_files\", backupName)\n\t\tcontent, err := os.ReadFile(filePath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := composeCtx.fileOp.SaveFile(backupPath, string(content), fs.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\trelativePath := path.Base(filePath)\n\t\tif composeCtx.composeDir != \"\" {\n\t\t\trel, relErr := filepath.Rel(composeCtx.composeDir, filePath)\n\t\t\tif relErr == nil {\n\t\t\t\trel = filepath.ToSlash(rel)\n\t\t\t\tif rel != \"\" && rel != \".\" && !strings.HasPrefix(rel, \"../\") {\n\t\t\t\t\trelativePath = rel\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcomposeCtx.meta.Files = append(composeCtx.meta.Files, composeBackupFile{\n\t\t\tOriginalPath: filePath,\n\t\t\tFileName:     path.Base(filePath),\n\t\t\tRelativePath: relativePath,\n\t\t\tBackupPath:   path.Join(\"compose_files\", backupName),\n\t\t})\n\t}\n\tif len(composeCtx.composeFiles) != 0 {\n\t\tenvPath := path.Join(path.Dir(composeCtx.composeFiles[0]), \".env\")\n\t\tif composeCtx.fileOp.Stat(envPath) {\n\t\t\tenvContent, err := os.ReadFile(envPath)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := composeCtx.fileOp.SaveFile(path.Join(composeCtx.tmpDir, \"compose_files\", \".env\"), string(envContent), fs.ModePerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc stepWriteComposeBackupMeta(composeCtx *composeBackupContext) error {\n\tmetaBytes, err := json.MarshalIndent(composeCtx.meta, \"\", \"  \")\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn composeCtx.fileOp.SaveFile(path.Join(composeCtx.tmpDir, \"compose_meta.json\"), string(metaBytes), fs.ModePerm)\n}\n\nfunc stepPackComposeBackup(composeCtx *composeBackupContext) error {\n\treturn composeCtx.fileOp.TarGzCompressPro(true, composeCtx.tmpDir, composeCtx.filePath, composeCtx.req.Secret, \"\")\n}\n\nfunc newComposeRecoverContext(req dto.CommonRecover) (*composeRecoverContext, error) {\n\ttmpDir := path.Join(path.Dir(req.File), strings.TrimSuffix(path.Base(req.File), \".tar.gz\"))\n\tctx := &composeRecoverContext{\n\t\treq:    req,\n\t\tfileOp: files.NewFileOp(),\n\t\ttmpDir: tmpDir,\n\t\tmeta: composeBackupMeta{\n\t\t\tFiles:      make([]composeBackupFile, 0),\n\t\t\tContainers: make([]string, 0),\n\t\t},\n\t}\n\treturn ctx, nil\n}\n\nfunc (c *composeRecoverContext) close() {\n\tif c.tmpDir != \"\" {\n\t\t_ = os.RemoveAll(c.tmpDir)\n\t}\n}\n\nfunc stepPrepareComposeRecover(recoverCtx *composeRecoverContext) error {\n\tif !recoverCtx.fileOp.Stat(recoverCtx.req.File) {\n\t\treturn buserr.WithName(\"ErrFileNotFound\", recoverCtx.req.File)\n\t}\n\t_ = os.RemoveAll(recoverCtx.tmpDir)\n\treturn nil\n}\n\nfunc stepExtractComposeRecover(recoverCtx *composeRecoverContext) error {\n\treturn recoverCtx.fileOp.TarGzExtractPro(recoverCtx.req.File, path.Dir(recoverCtx.req.File), recoverCtx.req.Secret)\n}\n\nfunc stepLoadComposeRecoverMeta(recoverCtx *composeRecoverContext) error {\n\tmetaPath := path.Join(recoverCtx.tmpDir, \"compose_meta.json\")\n\tif !recoverCtx.fileOp.Stat(metaPath) {\n\t\treturn fmt.Errorf(\"compose_meta.json not found in backup file\")\n\t}\n\tmetaBytes, err := os.ReadFile(metaPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := json.Unmarshal(metaBytes, &recoverCtx.meta); err != nil {\n\t\treturn fmt.Errorf(\"unmarshal compose_meta.json failed, err: %v\", err)\n\t}\n\trecoverCtx.composeName = strings.TrimSpace(recoverCtx.req.Name)\n\tif recoverCtx.composeName == \"\" {\n\t\trecoverCtx.composeName = strings.TrimSpace(recoverCtx.meta.ComposeName)\n\t}\n\tif recoverCtx.composeName == \"\" {\n\t\treturn fmt.Errorf(\"compose name not found in recover request or backup file\")\n\t}\n\trecoverCtx.targetDir = resolveComposeRecoverTargetDir(recoverCtx.meta, recoverCtx.composeName)\n\treturn nil\n}\n\nfunc resolveComposeRecoverTargetDir(meta composeBackupMeta, composeName string) string {\n\tcomposePath := strings.TrimSpace(meta.ComposePath)\n\tif composePath != \"\" {\n\t\titems := strings.Split(composePath, \",\")\n\t\tfor _, item := range items {\n\t\t\tp := strings.TrimSpace(item)\n\t\t\tif p == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\text := strings.ToLower(path.Ext(p))\n\t\t\tif ext == \".yml\" || ext == \".yaml\" {\n\t\t\t\treturn path.Dir(p)\n\t\t\t}\n\t\t\treturn p\n\t\t}\n\t}\n\treturn path.Join(global.Dir.DataDir, \"docker/compose\", composeName)\n}\n\nfunc safeJoinWithinBase(baseDir, name string) (string, error) {\n\tbase := filepath.Clean(baseDir)\n\tcandidate := strings.TrimSpace(name)\n\tcandidate = strings.ReplaceAll(candidate, \"\\\\\", \"/\")\n\tcandidate = filepath.Clean(filepath.FromSlash(candidate))\n\tif candidate == \"\" || candidate == \".\" {\n\t\treturn \"\", fmt.Errorf(\"invalid path: empty\")\n\t}\n\tif filepath.IsAbs(candidate) {\n\t\treturn \"\", fmt.Errorf(\"invalid path %q: absolute path is not allowed\", name)\n\t}\n\tif candidate == \"..\" || strings.HasPrefix(candidate, \"..\"+string(filepath.Separator)) {\n\t\treturn \"\", fmt.Errorf(\"invalid path %q: path escapes base directory\", name)\n\t}\n\tresolved := filepath.Clean(filepath.Join(base, candidate))\n\trel, err := filepath.Rel(base, resolved)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"resolve path %q failed, err: %v\", name, err)\n\t}\n\tif rel == \"..\" || strings.HasPrefix(rel, \"..\"+string(filepath.Separator)) {\n\t\treturn \"\", fmt.Errorf(\"invalid path %q: path escapes base directory\", name)\n\t}\n\treturn resolved, nil\n}\n\nfunc stepRestoreComposeFiles(recoverCtx *composeRecoverContext) error {\n\tif recoverCtx.targetDir != \"\" {\n\t\t_ = os.RemoveAll(recoverCtx.targetDir)\n\t}\n\tif err := os.MkdirAll(recoverCtx.targetDir, os.ModePerm); err != nil {\n\t\treturn err\n\t}\n\trestored := make([]string, 0, len(recoverCtx.meta.Files))\n\tfor _, item := range recoverCtx.meta.Files {\n\t\tbackupPath, err := safeJoinWithinBase(recoverCtx.tmpDir, item.BackupPath)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid compose backup path %q, err: %v\", item.BackupPath, err)\n\t\t}\n\t\tif !recoverCtx.fileOp.Stat(backupPath) {\n\t\t\tcontinue\n\t\t}\n\t\ttargetName := item.FileName\n\t\tif item.RelativePath != \"\" {\n\t\t\ttargetName = item.RelativePath\n\t\t}\n\t\tif targetName == \"\" {\n\t\t\ttargetName = path.Base(item.OriginalPath)\n\t\t}\n\t\tif targetName == \"\" {\n\t\t\ttargetName = \"docker-compose.yml\"\n\t\t}\n\t\ttargetPath, err := safeJoinWithinBase(recoverCtx.targetDir, targetName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid compose target path %q, err: %v\", targetName, err)\n\t\t}\n\t\tif err := os.MkdirAll(path.Dir(targetPath), os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcontent, err := os.ReadFile(backupPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := recoverCtx.fileOp.SaveFile(targetPath, string(content), fs.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\trestored = append(restored, targetPath)\n\t}\n\tenvPath := path.Join(recoverCtx.tmpDir, \"compose_files\", \".env\")\n\tif recoverCtx.fileOp.Stat(envPath) {\n\t\tenvContent, err := os.ReadFile(envPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := recoverCtx.fileOp.SaveFile(path.Join(recoverCtx.targetDir, \".env\"), string(envContent), fs.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif len(restored) == 0 {\n\t\tdefaultPath := path.Join(recoverCtx.targetDir, \"docker-compose.yml\")\n\t\tif !recoverCtx.fileOp.Stat(defaultPath) {\n\t\t\treturn fmt.Errorf(\"compose file not found in backup data\")\n\t\t}\n\t\trestored = append(restored, defaultPath)\n\t}\n\tsort.Strings(restored)\n\trecoverCtx.composePath = strings.Join(restored, \",\")\n\treturn nil\n}\n\nfunc stepSaveComposeRecord(recoverCtx *composeRecoverContext) error {\n\tif recoverCtx.composePath == \"\" {\n\t\trecoverCtx.composePath = path.Join(recoverCtx.targetDir, \"docker-compose.yml\")\n\t}\n\trecordName := strings.ToLower(recoverCtx.composeName)\n\trecord, _ := composeRepo.GetRecord(repo.WithByName(recordName))\n\tif record.ID == 0 {\n\t\treturn composeRepo.CreateRecord(&model.Compose{Name: recordName, Path: recoverCtx.composePath})\n\t}\n\treturn composeRepo.UpdateRecord(recordName, map[string]interface{}{\"path\": recoverCtx.composePath})\n}\n\nfunc sanitizeComposeFileName(in string) string {\n\tname := strings.TrimSpace(in)\n\tname = strings.ReplaceAll(name, \"/\", \"_\")\n\tname = strings.ReplaceAll(name, \":\", \"_\")\n\tif name == \"\" {\n\t\treturn \"container\"\n\t}\n\treturn name\n}\n"
  },
  {
    "path": "agent/app/service/backup_container.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\tdockerUtils \"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/api/types/mount\"\n\t\"github.com/docker/docker/api/types/network\"\n\t\"github.com/docker/docker/api/types/volume\"\n\t\"github.com/docker/docker/client\"\n)\n\ntype containerBackupMeta struct {\n\tContainerName string                 `json:\"containerName\"`\n\tContainerID   string                 `json:\"containerID\"`\n\tCreatedAt     string                 `json:\"createdAt\"`\n\tImage         string                 `json:\"image\"`\n\tHostConfig    *container.HostConfig  `json:\"hostConfig,omitempty\"`\n\tConfig        *container.Config      `json:\"config,omitempty\"`\n\tMounts        []containerMountBackup `json:\"mounts\"`\n}\n\ntype containerMountBackup struct {\n\tType        string `json:\"type\"`\n\tName        string `json:\"name,omitempty\"`\n\tSource      string `json:\"source,omitempty\"`\n\tDestination string `json:\"destination\"`\n\tMode        string `json:\"mode,omitempty\"`\n\tRW          bool   `json:\"rw\"`\n\tPropagation string `json:\"propagation,omitempty\"`\n\tBackupPath  string `json:\"backupPath,omitempty\"`\n\tStatus      string `json:\"status\"`\n\tMessage     string `json:\"message,omitempty\"`\n}\n\ntype containerBackupContext struct {\n\tcontainerName string\n\tbackupDir     string\n\tfileName      string\n\tsecret        string\n\tfilePath      string\n\ttmpDir        string\n\tmountRoot     string\n\twasRunning    bool\n\tstopped       bool\n\tfileOp        files.FileOp\n\tinspectInfo   container.InspectResponse\n\tmeta          containerBackupMeta\n}\n\ntype containerRecoverContext struct {\n\treq                dto.CommonRecover\n\ttargetName         string\n\tfileOp             files.FileOp\n\tclient             *client.Client\n\ttmpDir             string\n\tmeta               containerBackupMeta\n\tinspectInfo        container.InspectResponse\n\tshouldStart        bool\n\tcreatedContainerID string\n}\n\nfunc (u *BackupService) ContainerBackup(req dto.CommonBackup) error {\n\ttimeNow := time.Now().Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)\n\tfileName := req.FileName\n\tif fileName == \"\" {\n\t\tfileName = fmt.Sprintf(\"%s_%s.tar.gz\", req.Name, timeNow)\n\t}\n\tif !strings.HasSuffix(fileName, \".tar.gz\") {\n\t\tfileName += \".tar.gz\"\n\t}\n\titemDir := fmt.Sprintf(\"container/%s\", req.Name)\n\tbackupDir := path.Join(global.Dir.LocalBackupDir, itemDir)\n\trecord := &model.BackupRecord{\n\t\tType:              req.Type,\n\t\tName:              req.Name,\n\t\tSourceAccountIDs:  \"1\",\n\t\tDownloadAccountID: 1,\n\t\tFileDir:           itemDir,\n\t\tFileName:          fileName,\n\t\tTaskID:            req.TaskID,\n\t\tStatus:            constant.StatusWaiting,\n\t\tDescription:       req.Description,\n\t}\n\tif err := backupRepo.CreateRecord(record); err != nil {\n\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\treturn err\n\t}\n\tif err := handleContainerBackup(req.Name, nil, record.ID, backupDir, fileName, req.TaskID, req.Secret, req.StopBefore); err != nil {\n\t\tbackupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) ContainerRecover(req dto.CommonRecover) error {\n\treturn handleContainerRecover(req, nil)\n}\n\nfunc handleContainerBackup(containerName string, parentTask *task.Task, recordID uint, backupDir, fileName, taskID, secret string, stopBefore bool) error {\n\tvar (\n\t\terr        error\n\t\tbackupTask *task.Task\n\t)\n\tbackupCtx, err := newContainerBackupContext(containerName, backupDir, fileName, secret)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackupTask = parentTask\n\tif backupTask == nil {\n\t\tbackupTask, err = task.NewTaskWithOps(containerName, task.TaskBackup, task.TaskScopeBackup, taskID, 1)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif stopBefore {\n\t\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerBackupStop\"), func(t *task.Task) error {\n\t\t\treturn stepStopContainerForBackup(backupCtx)\n\t\t}, func(t *task.Task) {\n\t\t\t_ = stepStartContainerAfterBackup(backupCtx)\n\t\t}, 3, time.Hour)\n\t}\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerBackupPrepare\"), func(t *task.Task) error {\n\t\tt.Logf(\"------------------ %s ------------------\", containerName)\n\t\treturn stepPrepareContainerBackup(backupCtx)\n\t}, nil, 3, time.Hour)\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerBackupInspect\"), func(t *task.Task) error { return stepBackupContainerInspect(backupCtx) }, nil, 3, time.Hour)\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerBackupMounts\"), func(t *task.Task) error { return stepBackupContainerMounts(backupCtx) }, nil, 3, time.Hour)\n\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerBackupMeta\"), func(t *task.Task) error { return stepWriteContainerMeta(backupCtx) }, nil, 3, time.Hour)\n\tbackupTask.AddSubTaskWithOps(task.GetTaskName(containerName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return stepPackContainerBackup(backupCtx) }, nil, 3, time.Hour)\n\tif stopBefore {\n\t\tbackupTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerBackupStart\"), func(t *task.Task) error {\n\t\t\treturn stepStartContainerAfterBackup(backupCtx)\n\t\t}, nil, 3, time.Hour)\n\t}\n\tif parentTask != nil {\n\t\treturn nil\n\t}\n\tgo func() {\n\t\tdefer backupCtx.close()\n\t\tif err := backupTask.Execute(); err != nil {\n\t\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\treturn\n\t\t}\n\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusSuccess})\n\t}()\n\treturn nil\n}\n\nfunc handleContainerRecover(req dto.CommonRecover, parentTask *task.Task) error {\n\tvar (\n\t\terr         error\n\t\trecoverTask *task.Task\n\t\trecoverCtx  *containerRecoverContext\n\t)\n\trecoverTask = parentTask\n\tif recoverTask == nil {\n\t\tif isImportRecover(req) {\n\t\t\ttaskName := i18n.GetMsgByKey(\"TaskImport\") + i18n.GetMsgByKey(\"Container\")\n\t\t\trecoverTask, err = task.NewTask(taskName, task.TaskImport, task.TaskScopeBackup, req.TaskID, 1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\trecoverTask, err = task.NewTaskWithOps(\"container\", task.TaskRecover, task.TaskScopeBackup, req.TaskID, 1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\ttimeout := loadRecoverTimeout(req.Timeout)\n\tlogName := strings.TrimSpace(req.Name)\n\tif logName == \"\" && req.File != \"\" {\n\t\tlogName = strings.TrimSuffix(path.Base(req.File), \".tar.gz\")\n\t}\n\tif logName == \"\" {\n\t\tlogName = \"container\"\n\t}\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerRecoverPrepare\"), func(t *task.Task) error {\n\t\tctx, err := newContainerRecoverContext(req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trecoverCtx = ctx\n\t\tt.Logf(\"------------------ %s ------------------\", logName)\n\t\tif err := stepPrepareContainerRecover(recoverCtx); err != nil {\n\t\t\trecoverCtx.close()\n\t\t\trecoverCtx = nil\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}, func(t *task.Task) {\n\t\tif recoverCtx != nil {\n\t\t\trecoverCtx.close()\n\t\t\trecoverCtx = nil\n\t\t}\n\t}, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerRecoverExtract\"), func(t *task.Task) error { return stepExtractContainerRecover(recoverCtx) }, nil, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerRecoverParse\"), func(t *task.Task) error { return stepLoadContainerRecoverData(recoverCtx) }, nil, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerRecoverCreate\"), func(t *task.Task) error { return stepRecreateContainer(recoverCtx, t) }, nil, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerRecoverMounts\"), func(t *task.Task) error { return stepRestoreContainerMounts(recoverCtx) }, nil, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerRecoverStart\"), func(t *task.Task) error { return stepStartRecoveredContainer(recoverCtx) }, nil, 3, timeout)\n\trecoverTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"ContainerRecoverCleanup\"), func(t *task.Task) error {\n\t\tif recoverCtx != nil {\n\t\t\trecoverCtx.close()\n\t\t\trecoverCtx = nil\n\t\t}\n\t\treturn nil\n\t}, nil, 0, timeout)\n\tif parentTask != nil {\n\t\treturn nil\n\t}\n\tgo func() {\n\t\t_ = recoverTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc loadRecoverTimeout(timeout int) time.Duration {\n\tswitch timeout {\n\tcase -1:\n\t\treturn 0\n\tcase 0:\n\t\treturn 3 * time.Hour\n\tdefault:\n\t\treturn time.Duration(timeout) * time.Second\n\t}\n}\n\nfunc isImportRecover(req dto.CommonRecover) bool {\n\treturn req.BackupRecordID == 0\n}\n\nfunc newContainerBackupContext(containerName, backupDir, fileName, secret string) (*containerBackupContext, error) {\n\tdockerClient, err := dockerUtils.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() {\n\t\t_ = dockerClient.Close()\n\t}()\n\tinspectInfo, err := dockerClient.ContainerInspect(context.Background(), containerName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfilePath := path.Join(backupDir, fileName)\n\ttmpDir := path.Join(path.Dir(filePath), strings.TrimSuffix(path.Base(filePath), \".tar.gz\"))\n\tbackupCtx := &containerBackupContext{\n\t\tcontainerName: containerName,\n\t\tbackupDir:     backupDir,\n\t\tfileName:      fileName,\n\t\tsecret:        secret,\n\t\tfilePath:      filePath,\n\t\ttmpDir:        tmpDir,\n\t\tmountRoot:     path.Join(tmpDir, \"mounts\"),\n\t\twasRunning:    inspectInfo.State != nil && inspectInfo.State.Running,\n\t\tfileOp:        files.NewFileOp(),\n\t\tinspectInfo:   inspectInfo,\n\t\tmeta: containerBackupMeta{\n\t\t\tContainerName: containerName,\n\t\t\tContainerID:   inspectInfo.ID,\n\t\t\tCreatedAt:     time.Now().Format(constant.DateTimeLayout),\n\t\t\tImage:         inspectInfo.Config.Image,\n\t\t\tHostConfig:    inspectInfo.HostConfig,\n\t\t\tConfig:        inspectInfo.Config,\n\t\t\tMounts:        make([]containerMountBackup, 0),\n\t\t},\n\t}\n\treturn backupCtx, nil\n}\n\nfunc newContainerRecoverContext(req dto.CommonRecover) (*containerRecoverContext, error) {\n\tdockerClient, err := dockerUtils.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttmpDir := path.Join(path.Dir(req.File), strings.TrimSuffix(path.Base(req.File), \".tar.gz\"))\n\tctx := &containerRecoverContext{\n\t\treq:        req,\n\t\ttargetName: req.Name,\n\t\tfileOp:     files.NewFileOp(),\n\t\tclient:     dockerClient,\n\t\ttmpDir:     tmpDir,\n\t\tmeta: containerBackupMeta{\n\t\t\tMounts: make([]containerMountBackup, 0),\n\t\t},\n\t}\n\treturn ctx, nil\n}\n\nfunc (c *containerBackupContext) close() {\n\tif c.tmpDir != \"\" {\n\t\t_ = os.RemoveAll(c.tmpDir)\n\t}\n}\n\nfunc (c *containerRecoverContext) close() {\n\tif c.client != nil {\n\t\t_ = c.client.Close()\n\t}\n\tif c.tmpDir != \"\" {\n\t\t_ = os.RemoveAll(c.tmpDir)\n\t}\n}\n\nfunc stepPrepareContainerBackup(backupCtx *containerBackupContext) error {\n\tif err := os.MkdirAll(backupCtx.backupDir, os.ModePerm); err != nil {\n\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", backupCtx.backupDir, err)\n\t}\n\t_ = os.RemoveAll(backupCtx.tmpDir)\n\tif err := os.MkdirAll(backupCtx.mountRoot, os.ModePerm); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc stepStopContainerForBackup(backupCtx *containerBackupContext) error {\n\tif !backupCtx.wasRunning || backupCtx.stopped {\n\t\treturn nil\n\t}\n\tdockerClient, err := dockerUtils.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t_ = dockerClient.Close()\n\t}()\n\tif err := dockerClient.ContainerStop(context.Background(), backupCtx.inspectInfo.ID, container.StopOptions{}); err != nil {\n\t\treturn err\n\t}\n\tbackupCtx.stopped = true\n\treturn nil\n}\n\nfunc stepStartContainerAfterBackup(backupCtx *containerBackupContext) error {\n\tif !backupCtx.stopped {\n\t\treturn nil\n\t}\n\tdockerClient, err := dockerUtils.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t_ = dockerClient.Close()\n\t}()\n\tif err := dockerClient.ContainerStart(context.Background(), backupCtx.inspectInfo.ID, container.StartOptions{}); err != nil {\n\t\treturn err\n\t}\n\tbackupCtx.stopped = false\n\treturn nil\n}\n\nfunc stepBackupContainerInspect(backupCtx *containerBackupContext) error {\n\tinspectBytes, err := json.MarshalIndent(backupCtx.inspectInfo, \"\", \"  \")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := backupCtx.fileOp.SaveFile(path.Join(backupCtx.tmpDir, \"inspect.json\"), string(inspectBytes), fs.ModePerm); err != nil {\n\t\treturn err\n\t}\n\tif backupCtx.inspectInfo.NetworkSettings != nil {\n\t\tnetworkBytes, err := json.MarshalIndent(backupCtx.inspectInfo.NetworkSettings, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := backupCtx.fileOp.SaveFile(path.Join(backupCtx.tmpDir, \"network.json\"), string(networkBytes), fs.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc stepBackupContainerMounts(backupCtx *containerBackupContext) error {\n\tvar (\n\t\tdockerClient *client.Client\n\t\tclientErr    error\n\t)\n\tensureClient := func() (*client.Client, error) {\n\t\tif dockerClient != nil || clientErr != nil {\n\t\t\treturn dockerClient, clientErr\n\t\t}\n\t\tdockerClient, clientErr = dockerUtils.NewDockerClient()\n\t\treturn dockerClient, clientErr\n\t}\n\tdefer func() {\n\t\tif dockerClient != nil {\n\t\t\t_ = dockerClient.Close()\n\t\t}\n\t}()\n\n\tfor i, item := range backupCtx.inspectInfo.Mounts {\n\t\tmountMeta := containerMountBackup{\n\t\t\tType:        string(item.Type),\n\t\t\tName:        item.Name,\n\t\t\tSource:      item.Source,\n\t\t\tDestination: item.Destination,\n\t\t\tMode:        item.Mode,\n\t\t\tRW:          item.RW,\n\t\t\tPropagation: string(item.Propagation),\n\t\t\tStatus:      \"skipped\",\n\t\t}\n\n\t\tmountDirName := fmt.Sprintf(\"%02d_%s\", i, sanitizeContainerMountName(item.Destination))\n\t\tmountDir := path.Join(backupCtx.mountRoot, mountDirName)\n\t\tmountMeta.BackupPath = path.Join(\"mounts\", mountDirName, \"data\")\n\n\t\tswitch item.Type {\n\t\tcase mount.TypeBind, mount.TypeVolume:\n\t\t\tif item.Source == \"\" {\n\t\t\t\tmountMeta.Message = \"empty source\"\n\t\t\t\tbackupCtx.meta.Mounts = append(backupCtx.meta.Mounts, mountMeta)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsourceInfo, statErr := os.Stat(item.Source)\n\t\t\tif statErr != nil {\n\t\t\t\tmountMeta.Message = statErr.Error()\n\t\t\t\tbackupCtx.meta.Mounts = append(backupCtx.meta.Mounts, mountMeta)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdataDir := path.Join(mountDir, \"data\")\n\t\t\tif err := os.MkdirAll(dataDir, os.ModePerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif sourceInfo.IsDir() {\n\t\t\t\tif err := backupCtx.fileOp.CopyDirWithNewName(item.Source, dataDir, \".\"); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := backupCtx.fileOp.CopyFile(item.Source, dataDir); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif item.Type == mount.TypeVolume && item.Name != \"\" {\n\t\t\t\tcli, err := ensureClient()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tvolumeInfo, volumeErr := cli.VolumeInspect(context.Background(), item.Name)\n\t\t\t\tif volumeErr == nil {\n\t\t\t\t\tvolumeBytes, volumeMarshalErr := json.MarshalIndent(volumeInfo, \"\", \"  \")\n\t\t\t\t\tif volumeMarshalErr == nil {\n\t\t\t\t\t\t_ = backupCtx.fileOp.SaveFile(path.Join(mountDir, \"volume.json\"), string(volumeBytes), fs.ModePerm)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tmountMeta.Status = \"backed_up\"\n\t\tdefault:\n\t\t\tmountMeta.Message = \"mount type not supported for data backup\"\n\t\t}\n\t\tbackupCtx.meta.Mounts = append(backupCtx.meta.Mounts, mountMeta)\n\t}\n\treturn nil\n}\n\nfunc stepWriteContainerMeta(backupCtx *containerBackupContext) error {\n\tmetaBytes, err := json.MarshalIndent(backupCtx.meta, \"\", \"  \")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := backupCtx.fileOp.SaveFile(path.Join(backupCtx.tmpDir, \"meta.json\"), string(metaBytes), fs.ModePerm); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc stepPackContainerBackup(backupCtx *containerBackupContext) error {\n\tif err := backupCtx.fileOp.TarGzCompressPro(true, backupCtx.tmpDir, backupCtx.filePath, backupCtx.secret, \"\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc stepPrepareContainerRecover(recoverCtx *containerRecoverContext) error {\n\tif !recoverCtx.fileOp.Stat(recoverCtx.req.File) {\n\t\treturn buserr.WithName(\"ErrFileNotFound\", recoverCtx.req.File)\n\t}\n\t_ = os.RemoveAll(recoverCtx.tmpDir)\n\treturn nil\n}\n\nfunc stepExtractContainerRecover(recoverCtx *containerRecoverContext) error {\n\treturn recoverCtx.fileOp.TarGzExtractPro(recoverCtx.req.File, path.Dir(recoverCtx.req.File), recoverCtx.req.Secret)\n}\n\nfunc stepLoadContainerRecoverData(recoverCtx *containerRecoverContext) error {\n\tif err := loadContainerRecoverMeta(recoverCtx); err != nil {\n\t\treturn err\n\t}\n\tif err := loadContainerRecoverInspect(recoverCtx); err != nil {\n\t\treturn err\n\t}\n\tif recoverCtx.targetName == \"\" {\n\t\trecoverCtx.targetName = strings.TrimPrefix(recoverCtx.inspectInfo.Name, \"/\")\n\t}\n\tif recoverCtx.targetName == \"\" {\n\t\trecoverCtx.targetName = recoverCtx.meta.ContainerName\n\t}\n\tif recoverCtx.targetName == \"\" {\n\t\treturn fmt.Errorf(\"container name not found in recover request or backup file\")\n\t}\n\tif recoverCtx.inspectInfo.Config == nil {\n\t\trecoverCtx.inspectInfo.Config = recoverCtx.meta.Config\n\t}\n\tif recoverCtx.inspectInfo.HostConfig == nil {\n\t\trecoverCtx.inspectInfo.HostConfig = recoverCtx.meta.HostConfig\n\t}\n\tif recoverCtx.inspectInfo.Config == nil {\n\t\treturn fmt.Errorf(\"container config not found in backup file\")\n\t}\n\tif recoverCtx.inspectInfo.HostConfig == nil {\n\t\trecoverCtx.inspectInfo.HostConfig = &container.HostConfig{}\n\t}\n\trecoverCtx.shouldStart = recoverCtx.inspectInfo.State != nil && recoverCtx.inspectInfo.State.Running\n\treturn nil\n}\n\nfunc loadContainerRecoverMeta(recoverCtx *containerRecoverContext) error {\n\tmetaPath := path.Join(recoverCtx.tmpDir, \"meta.json\")\n\tif !recoverCtx.fileOp.Stat(metaPath) {\n\t\treturn nil\n\t}\n\tmetaBytes, err := os.ReadFile(metaPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := json.Unmarshal(metaBytes, &recoverCtx.meta); err != nil {\n\t\treturn fmt.Errorf(\"unmarshal meta.json failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc loadContainerRecoverInspect(recoverCtx *containerRecoverContext) error {\n\tinspectPath := path.Join(recoverCtx.tmpDir, \"inspect.json\")\n\tif !recoverCtx.fileOp.Stat(inspectPath) {\n\t\treturn fmt.Errorf(\"inspect.json not found in backup file\")\n\t}\n\tinspectBytes, err := os.ReadFile(inspectPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := json.Unmarshal(inspectBytes, &recoverCtx.inspectInfo); err != nil {\n\t\treturn fmt.Errorf(\"unmarshal inspect.json failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc stepRecreateContainer(recoverCtx *containerRecoverContext, taskItem *task.Task) error {\n\tctx := context.Background()\n\tif err := ensureContainerRecoverNetworks(recoverCtx); err != nil {\n\t\treturn err\n\t}\n\tif err := ensureContainerRecoverVolumes(recoverCtx); err != nil {\n\t\treturn err\n\t}\n\n\tconfig := cloneContainerConfig(recoverCtx.inspectInfo.Config)\n\thostConfig := cloneContainerHostConfig(recoverCtx.inspectInfo.HostConfig)\n\tif config.Image == \"\" {\n\t\tconfig.Image = recoverCtx.meta.Image\n\t}\n\tif config.Image == \"\" {\n\t\treturn fmt.Errorf(\"container image not found in backup file\")\n\t}\n\tif !checkImageExist(recoverCtx.client, config.Image) {\n\t\tif err := pullImages(taskItem, recoverCtx.client, config.Image); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif _, err := recoverCtx.client.ContainerInspect(ctx, recoverCtx.targetName); err == nil {\n\t\tif err := recoverCtx.client.ContainerRemove(ctx, recoverCtx.targetName, container.RemoveOptions{Force: true, RemoveVolumes: false}); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if !client.IsErrNotFound(err) {\n\t\treturn err\n\t}\n\n\tnetworkConfig, extraNetworks := buildContainerRecoverNetworkConfig(recoverCtx.inspectInfo.NetworkSettings, hostConfig)\n\tremoveBridgeDriverIPAM(recoverCtx.client, networkConfig, extraNetworks)\n\tcreateRes, err := recoverCtx.client.ContainerCreate(ctx, config, hostConfig, networkConfig, nil, recoverCtx.targetName)\n\tif err != nil {\n\t\treturn err\n\t}\n\trecoverCtx.createdContainerID = createRes.ID\n\n\textraNames := make([]string, 0, len(extraNetworks))\n\tfor name := range extraNetworks {\n\t\textraNames = append(extraNames, name)\n\t}\n\tsort.Strings(extraNames)\n\tfor _, item := range extraNames {\n\t\tif err := recoverCtx.client.NetworkConnect(ctx, item, recoverCtx.createdContainerID, extraNetworks[item]); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc removeBridgeDriverIPAM(cli *client.Client, primary *network.NetworkingConfig, extras map[string]*network.EndpointSettings) {\n\tif primary != nil {\n\t\tremoveBridgeDriverIPAMFromEndpoints(cli, primary.EndpointsConfig)\n\t}\n\tremoveBridgeDriverIPAMFromEndpoints(cli, extras)\n}\n\nfunc removeBridgeDriverIPAMFromEndpoints(cli *client.Client, endpoints map[string]*network.EndpointSettings) {\n\tfor netName, endpoint := range endpoints {\n\t\tif endpoint == nil || endpoint.IPAMConfig == nil {\n\t\t\tcontinue\n\t\t}\n\t\tinfo, err := cli.NetworkInspect(context.Background(), netName, network.InspectOptions{})\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif info.Driver == \"bridge\" {\n\t\t\tendpoint.IPAMConfig = nil\n\t\t}\n\t}\n}\n\nfunc ensureContainerRecoverNetworks(recoverCtx *containerRecoverContext) error {\n\tif recoverCtx.inspectInfo.NetworkSettings == nil {\n\t\treturn nil\n\t}\n\tfor netName := range recoverCtx.inspectInfo.NetworkSettings.Networks {\n\t\tif netName == \"\" || netName == \"bridge\" || netName == \"host\" || netName == \"none\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, err := recoverCtx.client.NetworkInspect(context.Background(), netName, network.InspectOptions{}); err != nil {\n\t\t\tif !client.IsErrNotFound(err) {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err := recoverCtx.client.NetworkCreate(context.Background(), netName, network.CreateOptions{Driver: \"bridge\"}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc ensureContainerRecoverVolumes(recoverCtx *containerRecoverContext) error {\n\tfor _, item := range recoverCtx.meta.Mounts {\n\t\tif item.Type != string(mount.TypeVolume) || item.Name == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, err := recoverCtx.client.VolumeInspect(context.Background(), item.Name); err == nil {\n\t\t\tcontinue\n\t\t} else if !client.IsErrNotFound(err) {\n\t\t\treturn err\n\t\t}\n\t\tcreateOptions := volume.CreateOptions{Name: item.Name}\n\t\tif item.BackupPath != \"\" {\n\t\t\tvolumeMetaPath := path.Join(recoverCtx.tmpDir, path.Dir(item.BackupPath), \"volume.json\")\n\t\t\tif recoverCtx.fileOp.Stat(volumeMetaPath) {\n\t\t\t\tvolumeBytes, readErr := os.ReadFile(volumeMetaPath)\n\t\t\t\tif readErr != nil {\n\t\t\t\t\treturn readErr\n\t\t\t\t}\n\t\t\t\tvar volumeInfo volume.Volume\n\t\t\t\tif unmarshalErr := json.Unmarshal(volumeBytes, &volumeInfo); unmarshalErr != nil {\n\t\t\t\t\treturn unmarshalErr\n\t\t\t\t}\n\t\t\t\tif volumeInfo.Driver != \"\" {\n\t\t\t\t\tcreateOptions.Driver = volumeInfo.Driver\n\t\t\t\t}\n\t\t\t\tif len(volumeInfo.Options) != 0 {\n\t\t\t\t\tcreateOptions.DriverOpts = volumeInfo.Options\n\t\t\t\t}\n\t\t\t\tif len(volumeInfo.Labels) != 0 {\n\t\t\t\t\tcreateOptions.Labels = volumeInfo.Labels\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif _, err := recoverCtx.client.VolumeCreate(context.Background(), createOptions); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc buildContainerRecoverNetworkConfig(networkSettings *container.NetworkSettings, hostConfig *container.HostConfig) (*network.NetworkingConfig, map[string]*network.EndpointSettings) {\n\textraNetworks := make(map[string]*network.EndpointSettings)\n\tif hostConfig != nil {\n\t\tnetworkMode := string(hostConfig.NetworkMode)\n\t\tif networkMode == \"host\" || networkMode == \"none\" {\n\t\t\treturn nil, extraNetworks\n\t\t}\n\t}\n\tif networkSettings == nil || len(networkSettings.Networks) == 0 {\n\t\treturn nil, extraNetworks\n\t}\n\n\tprimaryName := \"\"\n\tif hostConfig != nil {\n\t\tnetworkMode := string(hostConfig.NetworkMode)\n\t\tif networkMode != \"\" && networkMode != \"default\" && networkMode != \"bridge\" {\n\t\t\tif _, ok := networkSettings.Networks[networkMode]; ok {\n\t\t\t\tprimaryName = networkMode\n\t\t\t}\n\t\t}\n\t}\n\tif primaryName == \"\" {\n\t\tif _, ok := networkSettings.Networks[\"bridge\"]; ok {\n\t\t\tprimaryName = \"bridge\"\n\t\t} else {\n\t\t\tnames := make([]string, 0, len(networkSettings.Networks))\n\t\t\tfor name := range networkSettings.Networks {\n\t\t\t\tnames = append(names, name)\n\t\t\t}\n\t\t\tsort.Strings(names)\n\t\t\tif len(names) > 0 {\n\t\t\t\tprimaryName = names[0]\n\t\t\t}\n\t\t}\n\t}\n\n\tconfig := &network.NetworkingConfig{EndpointsConfig: make(map[string]*network.EndpointSettings)}\n\tfor name, endpoint := range networkSettings.Networks {\n\t\tif name == \"host\" || name == \"none\" {\n\t\t\tcontinue\n\t\t}\n\t\tendpointSetting := &network.EndpointSettings{Aliases: append([]string(nil), endpoint.Aliases...), MacAddress: endpoint.MacAddress}\n\t\tif endpoint.IPAMConfig != nil {\n\t\t\tendpointSetting.IPAMConfig = &network.EndpointIPAMConfig{\n\t\t\t\tIPv4Address: endpoint.IPAMConfig.IPv4Address,\n\t\t\t\tIPv6Address: endpoint.IPAMConfig.IPv6Address,\n\t\t\t}\n\t\t} else if endpoint.IPAddress != \"\" || endpoint.GlobalIPv6Address != \"\" {\n\t\t\tendpointSetting.IPAMConfig = &network.EndpointIPAMConfig{\n\t\t\t\tIPv4Address: endpoint.IPAddress,\n\t\t\t\tIPv6Address: endpoint.GlobalIPv6Address,\n\t\t\t}\n\t\t}\n\t\tif name == primaryName {\n\t\t\tconfig.EndpointsConfig[name] = endpointSetting\n\t\t} else {\n\t\t\textraNetworks[name] = endpointSetting\n\t\t}\n\t}\n\tif len(config.EndpointsConfig) == 0 {\n\t\treturn nil, extraNetworks\n\t}\n\treturn config, extraNetworks\n}\n\nfunc cloneContainerConfig(config *container.Config) *container.Config {\n\tif config == nil {\n\t\treturn &container.Config{}\n\t}\n\titem := *config\n\tif len(config.Env) != 0 {\n\t\titem.Env = append([]string(nil), config.Env...)\n\t}\n\tif len(config.Cmd) != 0 {\n\t\titem.Cmd = append([]string(nil), config.Cmd...)\n\t}\n\tif len(config.Entrypoint) != 0 {\n\t\titem.Entrypoint = append([]string(nil), config.Entrypoint...)\n\t}\n\tif len(config.Labels) != 0 {\n\t\tlabels := make(map[string]string, len(config.Labels))\n\t\tfor key, val := range config.Labels {\n\t\t\tlabels[key] = val\n\t\t}\n\t\titem.Labels = labels\n\t}\n\tif len(config.Volumes) != 0 {\n\t\tvolumes := make(map[string]struct{}, len(config.Volumes))\n\t\tfor key, val := range config.Volumes {\n\t\t\tvolumes[key] = val\n\t\t}\n\t\titem.Volumes = volumes\n\t}\n\treturn &item\n}\n\nfunc cloneContainerHostConfig(hostConfig *container.HostConfig) *container.HostConfig {\n\tif hostConfig == nil {\n\t\treturn &container.HostConfig{}\n\t}\n\titem := *hostConfig\n\tif len(hostConfig.Binds) != 0 {\n\t\titem.Binds = append([]string(nil), hostConfig.Binds...)\n\t}\n\tif len(hostConfig.DNS) != 0 {\n\t\titem.DNS = append([]string(nil), hostConfig.DNS...)\n\t}\n\tif len(hostConfig.ExtraHosts) != 0 {\n\t\titem.ExtraHosts = append([]string(nil), hostConfig.ExtraHosts...)\n\t}\n\tif len(hostConfig.Mounts) != 0 {\n\t\titem.Mounts = append([]mount.Mount(nil), hostConfig.Mounts...)\n\t}\n\treturn &item\n}\n\nfunc stepRestoreContainerMounts(recoverCtx *containerRecoverContext) error {\n\tcurrentContainer := recoverCtx.createdContainerID\n\tif currentContainer == \"\" {\n\t\tcurrentContainer = recoverCtx.targetName\n\t}\n\tcurrentInspect, err := recoverCtx.client.ContainerInspect(context.Background(), currentContainer)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcurrentMounts := make(map[string]container.MountPoint, len(currentInspect.Mounts))\n\tfor _, item := range currentInspect.Mounts {\n\t\tcurrentMounts[item.Destination] = item\n\t}\n\n\tfor _, item := range recoverCtx.meta.Mounts {\n\t\tif item.Status != \"backed_up\" || item.BackupPath == \"\" || !item.RW {\n\t\t\tcontinue\n\t\t}\n\t\tbackupPath := path.Join(recoverCtx.tmpDir, item.BackupPath)\n\t\tif !recoverCtx.fileOp.Stat(backupPath) {\n\t\t\tcontinue\n\t\t}\n\t\tsourcePath := item.Source\n\t\tif currentMount, ok := currentMounts[item.Destination]; ok {\n\t\t\tif currentMount.Source != \"\" {\n\t\t\t\tsourcePath = currentMount.Source\n\t\t\t}\n\t\t\tif item.Type == string(mount.TypeVolume) && item.Name == \"\" {\n\t\t\t\titem.Name = currentMount.Name\n\t\t\t}\n\t\t}\n\t\tif sourcePath == \"\" && item.Type == string(mount.TypeVolume) && item.Name != \"\" {\n\t\t\tvolumeInfo, volumeErr := recoverCtx.client.VolumeInspect(context.Background(), item.Name)\n\t\t\tif volumeErr != nil {\n\t\t\t\treturn volumeErr\n\t\t\t}\n\t\t\tsourcePath = volumeInfo.Mountpoint\n\t\t}\n\t\tif sourcePath == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif err := restoreContainerMountData(recoverCtx.fileOp, backupPath, sourcePath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc restoreContainerMountData(fileOp files.FileOp, backupPath, sourcePath string) error {\n\tif sourcePath == \"/\" {\n\t\treturn fmt.Errorf(\"invalid mount source path /\")\n\t}\n\tentries, err := os.ReadDir(backupPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(entries) == 1 && !entries[0].IsDir() && entries[0].Name() == path.Base(sourcePath) {\n\t\tif err := os.MkdirAll(path.Dir(sourcePath), os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = os.RemoveAll(sourcePath)\n\t\tif err := fileOp.CopyFile(path.Join(backupPath, entries[0].Name()), path.Dir(sourcePath)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\n\t_ = os.RemoveAll(sourcePath)\n\tif err := os.MkdirAll(sourcePath, os.ModePerm); err != nil {\n\t\treturn err\n\t}\n\tif err := fileOp.CopyDirWithNewName(backupPath, sourcePath, \".\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc stepStartRecoveredContainer(recoverCtx *containerRecoverContext) error {\n\tif !recoverCtx.shouldStart {\n\t\treturn nil\n\t}\n\tcontainerID := recoverCtx.createdContainerID\n\tif containerID == \"\" {\n\t\tcontainerID = recoverCtx.targetName\n\t}\n\treturn recoverCtx.client.ContainerStart(context.Background(), containerID, container.StartOptions{})\n}\n\nfunc sanitizeContainerMountName(in string) string {\n\tname := strings.TrimSpace(in)\n\tname = strings.Trim(name, \"/\")\n\tname = strings.ReplaceAll(name, \"/\", \"_\")\n\tname = strings.ReplaceAll(name, \":\", \"_\")\n\tif name == \"\" {\n\t\treturn \"root\"\n\t}\n\treturn name\n}\n"
  },
  {
    "path": "agent/app/service/backup_mysql.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/mysql/client\"\n)\n\nfunc (u *BackupService) MysqlBackup(req dto.CommonBackup) error {\n\ttimeNow := time.Now().Format(constant.DateTimeSlimLayout)\n\titemDir := fmt.Sprintf(\"database/%s/%s/%s\", req.Type, req.Name, req.DetailName)\n\ttargetDir := path.Join(global.Dir.LocalBackupDir, itemDir)\n\tfileName := fmt.Sprintf(\"%s_%s.sql.gz\", req.DetailName, timeNow+common.RandStrAndNum(5))\n\n\trecord := &model.BackupRecord{\n\t\tType:              req.Type,\n\t\tName:              req.Name,\n\t\tDetailName:        req.DetailName,\n\t\tSourceAccountIDs:  \"1\",\n\t\tDownloadAccountID: 1,\n\t\tFileDir:           itemDir,\n\t\tFileName:          fileName,\n\t\tTaskID:            req.TaskID,\n\t\tStatus:            constant.StatusWaiting,\n\t\tDescription:       req.Description,\n\t}\n\tif err := backupRepo.CreateRecord(record); err != nil {\n\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\treturn err\n\t}\n\n\tdatabaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName, Args: req.Args}\n\tif err := handleMysqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID, req.Secret); err != nil {\n\t\tbackupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) MysqlRecover(req dto.CommonRecover) error {\n\treturn handleMysqlRecover(req, nil, false)\n}\n\nfunc (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {\n\trecoverFile, err := loadSqlFile(req.File)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.File = recoverFile\n\n\tif err := handleMysqlRecover(req, nil, false); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID, secret string) error {\n\tvar (\n\t\terr        error\n\t\tbackupTask *task.Task\n\t)\n\tbackupTask = parentTask\n\tdbInfo, err := mysqlRepo.Get(repo.WithByName(db.Name), mysqlRepo.WithByMysqlName(db.Database))\n\tif err != nil {\n\t\treturn err\n\t}\n\titemName := fmt.Sprintf(\"%s[%s] - %s\", db.Database, db.DBType, db.Name)\n\tif parentTask == nil {\n\t\tbackupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeBackup, taskID, dbInfo.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\titemHandler := func() error {\n\t\tif len(db.Args) != 0 {\n\t\t\tbackupTask.Logf(\"%s: %v\", i18n.GetMsgByKey(\"Arg\"), db.Args)\n\t\t}\n\t\treturn doMysqlBackup(db, targetDir, fileName, secret)\n\t}\n\tif parentTask != nil {\n\t\treturn itemHandler()\n\t}\n\tbackupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 0, 3*time.Hour)\n\tgo func() {\n\t\tif err := backupTask.Execute(); err != nil {\n\t\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\treturn\n\t\t}\n\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusSuccess})\n\t}()\n\treturn nil\n}\n\nfunc handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool) error {\n\tvar (\n\t\terr      error\n\t\titemTask *task.Task\n\t)\n\titemTask = parentTask\n\tdbInfo, err := mysqlRepo.Get(repo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name))\n\tif err != nil {\n\t\treturn err\n\t}\n\titemName := fmt.Sprintf(\"%s[%s] - %s\", req.Name, req.Type, req.DetailName)\n\tif parentTask == nil {\n\t\titemTask, err = task.NewTaskWithOps(itemName, task.TaskRecover, task.TaskScopeBackup, req.TaskID, dbInfo.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\trecoverDatabase := func(t *task.Task) error {\n\t\tisOk := false\n\t\tfileOp := files.NewFileOp()\n\t\tif !fileOp.Stat(req.File) {\n\t\t\treturn buserr.WithName(\"ErrFileNotFound\", req.File)\n\t\t}\n\t\tdbInfo, err := mysqlRepo.Get(repo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcli, version, err := LoadMysqlClientByFrom(req.Name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif !isRollback {\n\t\t\trollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf(\"database/%s/%s_%s.sql.gz\", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))\n\t\t\tif err := cli.Backup(client.BackupInfo{\n\t\t\t\tName:      req.DetailName,\n\t\t\t\tType:      req.Type,\n\t\t\t\tVersion:   version,\n\t\t\t\tFormat:    dbInfo.Format,\n\t\t\t\tTargetDir: path.Dir(rollbackFile),\n\t\t\t\tFileName:  path.Base(rollbackFile),\n\t\t\t}); err != nil {\n\t\t\t\treturn fmt.Errorf(\"backup mysql db %s for rollback before recover failed, err: %v\", req.DetailName, err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tif !isOk {\n\t\t\t\t\tglobal.LOG.Info(\"recover failed, start to rollback now\")\n\t\t\t\t\tif err := cli.Recover(client.RecoverInfo{\n\t\t\t\t\t\tName:       req.DetailName,\n\t\t\t\t\t\tType:       req.Type,\n\t\t\t\t\t\tVersion:    version,\n\t\t\t\t\t\tFormat:     dbInfo.Format,\n\t\t\t\t\t\tSourceFile: rollbackFile,\n\t\t\t\t\t}); err != nil {\n\t\t\t\t\t\tglobal.LOG.Errorf(\"rollback mysql db %s from %s failed, err: %v\", req.DetailName, rollbackFile, err)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tglobal.LOG.Infof(\"rollback mysql db %s from %s successful\", req.DetailName, rollbackFile)\n\t\t\t\t\t}\n\t\t\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t\t\t} else {\n\t\t\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\tif len(req.Secret) != 0 {\n\t\t\terr = files.OpensslDecrypt(req.File, req.Secret)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treq.File = path.Join(path.Dir(req.File), \"tmp_\"+path.Base(req.File))\n\t\t\tdefer os.Remove(req.File)\n\t\t\tt.LogWithStatus(i18n.GetMsgByKey(\"Decrypt\"), err)\n\t\t}\n\t\tif err := cli.Recover(client.RecoverInfo{\n\t\t\tName:       req.DetailName,\n\t\t\tType:       req.Type,\n\t\t\tVersion:    version,\n\t\t\tFormat:     dbInfo.Format,\n\t\t\tSourceFile: req.File,\n\t\t}); err != nil {\n\t\t\tglobal.LOG.Errorf(\"recover mysql db %s from %s failed, err: %v\", req.DetailName, req.File, err)\n\t\t\treturn err\n\t\t}\n\t\tisOk = true\n\t\treturn nil\n\t}\n\tif parentTask != nil {\n\t\treturn recoverDatabase(parentTask)\n\t}\n\n\tvar timeout time.Duration\n\tswitch req.Timeout {\n\tcase -1:\n\t\ttimeout = 0\n\tcase 0:\n\t\ttimeout = 3 * time.Hour\n\tdefault:\n\t\ttimeout = time.Duration(req.Timeout) * time.Second\n\t}\n\titemTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"TaskRecover\"), recoverDatabase, nil, 0, timeout)\n\tgo func() {\n\t\t_ = itemTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc doMysqlBackup(db DatabaseHelper, targetDir, fileName, secret string) error {\n\tdbInfo, err := mysqlRepo.Get(repo.WithByName(db.Name), mysqlRepo.WithByMysqlName(db.Database))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcli, version, err := LoadMysqlClientByFrom(db.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackupInfo := client.BackupInfo{\n\t\tName:      db.Name,\n\t\tType:      db.DBType,\n\t\tVersion:   version,\n\t\tFormat:    dbInfo.Format,\n\t\tTargetDir: targetDir,\n\t\tFileName:  fileName,\n\t\tArgs:      db.Args,\n\t}\n\tif err := cli.Backup(backupInfo); err != nil {\n\t\treturn err\n\t}\n\tif len(secret) != 0 {\n\t\treturn files.OpensslEncrypt(path.Join(targetDir, fileName), secret)\n\t}\n\treturn nil\n}\n\nfunc loadSqlFile(file string) (string, error) {\n\tif !strings.HasSuffix(file, \".tar.gz\") && !strings.HasSuffix(file, \".zip\") {\n\t\treturn file, nil\n\t}\n\tfileName := path.Base(file)\n\tfileDir := path.Dir(file)\n\tfileNameItem := time.Now().Format(constant.DateTimeSlimLayout)\n\tdstDir := fmt.Sprintf(\"%s/%s\", fileDir, fileNameItem)\n\t_ = os.Mkdir(dstDir, constant.DirPerm)\n\tif strings.HasSuffix(fileName, \".tar.gz\") {\n\t\tfileOp := files.NewFileOp()\n\t\tif err := fileOp.TarGzExtractPro(file, dstDir, \"\"); err != nil {\n\t\t\t_ = os.RemoveAll(dstDir)\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tif strings.HasSuffix(fileName, \".zip\") {\n\t\tarchiver, err := files.NewShellArchiver(files.Zip)\n\t\tif err != nil {\n\t\t\t_ = os.RemoveAll(dstDir)\n\t\t\treturn \"\", err\n\t\t}\n\t\tif err := archiver.Extract(file, dstDir, \"\"); err != nil {\n\t\t\t_ = os.RemoveAll(dstDir)\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tglobal.LOG.Infof(\"decompress file %s successful, now start to check test.sql is exist\", file)\n\tvar sqlFiles []string\n\thasTestSql := false\n\t_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tif !info.IsDir() && strings.HasSuffix(info.Name(), \".sql\") {\n\t\t\tsqlFiles = append(sqlFiles, path)\n\t\t\tif info.Name() == \"test.sql\" {\n\t\t\t\thasTestSql = true\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tif len(sqlFiles) == 1 {\n\t\treturn sqlFiles[0], nil\n\t}\n\tif !hasTestSql {\n\t\t_ = os.RemoveAll(dstDir)\n\t\treturn \"\", fmt.Errorf(\"no such file named test.sql in %s\", fileName)\n\t}\n\treturn \"\", nil\n}\n"
  },
  {
    "path": "agent/app/service/backup_postgresql.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client\"\n)\n\nfunc (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {\n\ttimeNow := time.Now().Format(constant.DateTimeSlimLayout)\n\titemDir := fmt.Sprintf(\"database/%s/%s/%s\", req.Type, req.Name, req.DetailName)\n\ttargetDir := path.Join(global.Dir.LocalBackupDir, itemDir)\n\tfileName := fmt.Sprintf(\"%s_%s.sql.gz\", req.DetailName, timeNow+common.RandStrAndNum(5))\n\n\trecord := &model.BackupRecord{\n\t\tType:              req.Type,\n\t\tName:              req.Name,\n\t\tDetailName:        req.DetailName,\n\t\tSourceAccountIDs:  \"1\",\n\t\tDownloadAccountID: 1,\n\t\tFileDir:           itemDir,\n\t\tFileName:          fileName,\n\t\tTaskID:            req.TaskID,\n\t\tStatus:            constant.StatusWaiting,\n\t\tDescription:       req.Description,\n\t}\n\tif err := backupRepo.CreateRecord(record); err != nil {\n\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t}\n\n\tdatabaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}\n\tif err := handlePostgresqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID, req.Secret); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\nfunc (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {\n\tif err := handlePostgresqlRecover(req, nil, false); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {\n\trecoverFile, err := loadSqlFile(req.File)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.File = recoverFile\n\tif err := handlePostgresqlRecover(req, nil, false); err != nil {\n\t\treturn err\n\t}\n\tglobal.LOG.Info(\"recover from uploads successful!\")\n\treturn nil\n}\n\nfunc handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID, secret string) error {\n\tvar (\n\t\terr        error\n\t\tbackupTask *task.Task\n\t)\n\tbackupTask = parentTask\n\titemName := fmt.Sprintf(\"%s - %s\", db.Database, db.Name)\n\tif parentTask == nil {\n\t\tbackupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeBackup, taskID, db.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\titemHandler := func() error { return doPostgresqlBackup(db, targetDir, fileName, secret, backupTask) }\n\tif parentTask != nil {\n\t\treturn itemHandler()\n\t}\n\tbackupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 0, 3*time.Hour)\n\tgo func() {\n\t\tif err := backupTask.Execute(); err != nil {\n\t\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\treturn\n\t\t}\n\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusSuccess})\n\t}()\n\treturn nil\n}\n\nfunc handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool) error {\n\tvar (\n\t\terr      error\n\t\titemTask *task.Task\n\t)\n\tdbInfo, err := postgresqlRepo.Get(repo.WithByName(req.DetailName), postgresqlRepo.WithByPostgresqlName(req.Name))\n\tif err != nil {\n\t\treturn err\n\t}\n\titemTask = parentTask\n\tif parentTask == nil {\n\t\titemTask, err = task.NewTaskWithOps(req.Name, task.TaskRecover, task.TaskScopeBackup, req.TaskID, dbInfo.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\trecoverDatabase := func(t *task.Task) error {\n\t\tisOk := false\n\t\tfileOp := files.NewFileOp()\n\t\tif !fileOp.Stat(req.File) {\n\t\t\treturn buserr.WithName(\"ErrFileNotFound\", req.File)\n\t\t}\n\n\t\tcli, err := LoadPostgresqlClientByFrom(req.Name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer cli.Close()\n\n\t\tif !isRollback {\n\t\t\trollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf(\"database/%s/%s_%s.sql.gz\", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))\n\t\t\tif err := cli.Backup(client.BackupInfo{\n\t\t\t\tDatabase:  req.Name,\n\t\t\t\tName:      req.DetailName,\n\t\t\t\tTargetDir: path.Dir(rollbackFile),\n\t\t\t\tFileName:  path.Base(rollbackFile),\n\n\t\t\t\tTask:    t,\n\t\t\t\tTimeout: 300,\n\t\t\t}); err != nil {\n\t\t\t\treturn fmt.Errorf(\"backup postgresql db %s for rollback before recover failed, err: %v\", req.DetailName, err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tif !isOk {\n\t\t\t\t\tglobal.LOG.Info(\"recover failed, start to rollback now\")\n\t\t\t\t\tif err := cli.Recover(client.RecoverInfo{\n\t\t\t\t\t\tDatabase:   req.Name,\n\t\t\t\t\t\tName:       req.DetailName,\n\t\t\t\t\t\tSourceFile: rollbackFile,\n\n\t\t\t\t\t\tTask:    t,\n\t\t\t\t\t\tTimeout: 300,\n\t\t\t\t\t}); err != nil {\n\t\t\t\t\t\tglobal.LOG.Errorf(\"rollback postgresql db %s from %s failed, err: %v\", req.DetailName, rollbackFile, err)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tglobal.LOG.Infof(\"rollback postgresql db %s from %s successful\", req.DetailName, rollbackFile)\n\t\t\t\t\t}\n\t\t\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t\t\t} else {\n\t\t\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\tif len(req.Secret) != 0 {\n\t\t\terr = files.OpensslDecrypt(req.File, req.Secret)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treq.File = path.Join(path.Dir(req.File), \"tmp_\"+path.Base(req.File))\n\t\t\tdefer os.Remove(req.File)\n\t\t\tt.LogWithStatus(i18n.GetMsgByKey(\"Decrypt\"), err)\n\t\t}\n\t\tif err := cli.Recover(client.RecoverInfo{\n\t\t\tDatabase:   req.Name,\n\t\t\tName:       req.DetailName,\n\t\t\tSourceFile: req.File,\n\t\t\tUsername:   dbInfo.Username,\n\t\t\tTask:       t,\n\t\t\tTimeout:    300,\n\t\t}); err != nil {\n\t\t\tglobal.LOG.Errorf(\"recover postgresql db %s from %s failed, err: %v\", req.DetailName, req.File, err)\n\t\t\treturn err\n\t\t}\n\t\tisOk = true\n\t\treturn nil\n\t}\n\tif parentTask != nil {\n\t\treturn recoverDatabase(parentTask)\n\t}\n\n\tvar timeout time.Duration\n\tswitch req.Timeout {\n\tcase -1:\n\t\ttimeout = 0\n\tcase 0:\n\t\ttimeout = 3 * time.Hour\n\tdefault:\n\t\ttimeout = time.Duration(req.Timeout) * time.Second\n\t}\n\titemTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"TaskRecover\"), recoverDatabase, nil, 0, timeout)\n\tgo func() {\n\t\t_ = itemTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc doPostgresqlBackup(db DatabaseHelper, targetDir, fileName, secret string, task *task.Task) error {\n\tcli, err := LoadPostgresqlClientByFrom(db.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tbackupInfo := client.BackupInfo{\n\t\tDatabase:  db.Database,\n\t\tName:      db.Name,\n\t\tTargetDir: targetDir,\n\t\tFileName:  fileName,\n\n\t\tTask:    task,\n\t\tTimeout: 300,\n\t}\n\tif err := cli.Backup(backupInfo); err != nil {\n\t\treturn err\n\t}\n\tif len(secret) != 0 {\n\t\treturn files.OpensslEncrypt(path.Join(targetDir, fileName), secret)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/backup_record.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype BackupRecordService struct{}\n\ntype IBackupRecordService interface {\n\tSearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)\n\tSearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error)\n\tDownloadRecord(info dto.DownloadRecord) (string, error)\n\tDeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error\n\tBatchDeleteRecord(ids []uint) error\n\tListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error)\n\n\tListFiles(req dto.OperateByID) []string\n\tLoadRecordSize(req dto.SearchForSize) ([]dto.RecordFileSize, error)\n\tUpdateDescription(req dto.UpdateDescription) error\n}\n\nfunc NewIBackupRecordService() IBackupRecordService {\n\treturn &BackupRecordService{}\n}\n\nfunc (u *BackupRecordService) SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error) {\n\ttotal, records, err := backupRepo.PageRecord(\n\t\tsearch.Page, search.PageSize,\n\t\trepo.WithOrderDesc(\"created_at\"),\n\t\trepo.WithByName(search.Name),\n\t\trepo.WithByType(search.Type),\n\t\trepo.WithByDetailName(search.DetailName),\n\t)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\taccounts, _ := backupRepo.List()\n\tvar data []dto.BackupRecords\n\tfor _, record := range records {\n\t\tvar item dto.BackupRecords\n\t\tif err := copier.Copy(&item, &record); err != nil {\n\t\t\tglobal.LOG.Errorf(\"copy backup account to dto backup info failed, err: %v\", err)\n\t\t}\n\t\tfor _, account := range accounts {\n\t\t\tif account.ID == record.DownloadAccountID {\n\t\t\t\titem.DownloadAccountID = account.ID\n\t\t\t\titem.AccountName = account.Name\n\t\t\t\titem.AccountType = account.Type\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tdata = append(data, item)\n\t}\n\treturn total, data, err\n}\n\nfunc (u *BackupRecordService) SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error) {\n\ttotal, records, err := backupRepo.PageRecord(\n\t\tsearch.Page, search.PageSize,\n\t\trepo.WithOrderDesc(\"created_at\"),\n\t\tbackupRepo.WithByCronID(search.CronjobID),\n\t)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\taccounts, _ := backupRepo.List()\n\tvar data []dto.BackupRecords\n\tfor _, record := range records {\n\t\tvar item dto.BackupRecords\n\t\tif err := copier.Copy(&item, &record); err != nil {\n\t\t\tglobal.LOG.Errorf(\"copy backup account to dto backup info failed, err: %v\", err)\n\t\t}\n\t\tfor _, account := range accounts {\n\t\t\tif account.ID == record.DownloadAccountID {\n\t\t\t\titem.DownloadAccountID = account.ID\n\t\t\t\titem.AccountName = account.Name\n\t\t\t\titem.AccountType = account.Type\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tdata = append(data, item)\n\t}\n\treturn total, data, err\n}\n\nfunc (u *BackupRecordService) DownloadRecord(info dto.DownloadRecord) (string, error) {\n\taccount, client, err := NewBackupClientWithID(info.DownloadAccountID)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"new cloud storage client failed, err: %v\", err)\n\t}\n\tif account.Type == \"LOCAL\" {\n\t\treturn path.Join(global.Dir.LocalBackupDir, info.FileDir, info.FileName), nil\n\t}\n\ttargetPath := fmt.Sprintf(\"%s/download/%s/%s\", global.Dir.DataDir, info.FileDir, info.FileName)\n\tif _, err := os.Stat(path.Dir(targetPath)); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(path.Dir(targetPath), os.ModePerm); err != nil {\n\t\t\tglobal.LOG.Errorf(\"mkdir %s failed, err: %v\", path.Dir(targetPath), err)\n\t\t}\n\t}\n\tsrcPath := fmt.Sprintf(\"%s/%s\", info.FileDir, info.FileName)\n\tif len(account.BackupPath) != 0 {\n\t\tsrcPath = path.Join(account.BackupPath, srcPath)\n\t}\n\tif exist, _ := client.Exist(srcPath); exist {\n\t\tisOK, err := client.Download(srcPath, targetPath)\n\t\tif !isOK || err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"cloud storage download failed, err: %v\", err)\n\t\t}\n\t}\n\treturn targetPath, nil\n}\n\nfunc (u *BackupRecordService) DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error {\n\tif !withDeleteFile {\n\t\treturn backupRepo.DeleteRecord(context.Background(), repo.WithByType(backupType), repo.WithByName(name), repo.WithByDetailName(detailName))\n\t}\n\n\trecords, err := backupRepo.ListRecord(repo.WithByType(backupType), repo.WithByName(name), repo.WithByDetailName(detailName))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, record := range records {\n\t\tbackup, client, err := NewBackupClientWithID(record.DownloadAccountID)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"new client for backup account failed, err: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif _, err = client.Delete(path.Join(backup.BackupPath, record.FileDir, record.FileName)); err != nil {\n\t\t\tglobal.LOG.Errorf(\"remove file %s failed, err: %v\", path.Join(record.FileDir, record.FileName), err)\n\t\t}\n\t\t_ = backupRepo.DeleteRecord(context.Background(), repo.WithByID(record.ID))\n\t}\n\treturn nil\n}\n\nfunc (u *BackupRecordService) BatchDeleteRecord(ids []uint) error {\n\trecords, err := backupRepo.ListRecord(repo.WithByIDs(ids))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, record := range records {\n\t\tbackup, client, err := NewBackupClientWithID(record.DownloadAccountID)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"new client for backup account failed, err: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif _, err = client.Delete(path.Join(backup.BackupPath, record.FileDir, record.FileName)); err != nil {\n\t\t\tglobal.LOG.Errorf(\"remove file %s failed, err: %v\", path.Join(record.FileDir, record.FileName), err)\n\t\t}\n\t}\n\treturn backupRepo.DeleteRecord(context.Background(), repo.WithByIDs(ids))\n}\n\nfunc (u *BackupRecordService) ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error) {\n\trecords, err := backupRepo.ListRecord(\n\t\trepo.WithOrderAsc(\"created_at\"),\n\t\trepo.WithByName(name),\n\t\trepo.WithByType(\"app\"),\n\t\tbackupRepo.WithFileNameStartWith(fileName),\n\t\tbackupRepo.WithByDetailName(detailName),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn records, err\n}\n\nfunc (u *BackupRecordService) ListFiles(req dto.OperateByID) []string {\n\tbackupItem, client, err := NewBackupClientWithID(req.ID)\n\tif err != nil {\n\t\treturn []string{}\n\t}\n\tprefix := \"system_snapshot\"\n\tif len(backupItem.BackupPath) != 0 {\n\t\tprefix = path.Join(backupItem.BackupPath, prefix)\n\t}\n\tfiles, err := client.ListObjects(prefix)\n\tif err != nil {\n\t\tglobal.LOG.Debugf(\"load files failed, err: %v\", err)\n\t\treturn []string{}\n\t}\n\tvar datas []string\n\tfor _, file := range files {\n\t\tfileName := path.Base(file)\n\t\tif len(file) != 0 && checkSnapshotIsOk(fileName) {\n\t\t\tdatas = append(datas, path.Base(file))\n\t\t}\n\t}\n\treturn datas\n}\n\ntype backupSizeHelper struct {\n\tID         uint   `json:\"id\"`\n\tDownloadID uint   `json:\"downloadID\"`\n\tFilePath   string `json:\"filePath\"`\n\tSize       uint   `json:\"size\"`\n}\n\nfunc (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.RecordFileSize, error) {\n\tvar list []backupSizeHelper\n\tswitch req.Type {\n\tcase \"snapshot\":\n\t\t_, records, err := snapshotRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, item := range records {\n\t\t\tlist = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: fmt.Sprintf(\"system_snapshot/%s.tar.gz\", item.Name)})\n\t\t}\n\tcase \"cronjob\":\n\t\t_, records, err := backupRepo.PageRecord(req.Page, req.PageSize, repo.WithOrderDesc(\"created_at\"), backupRepo.WithByCronID(req.CronjobID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, item := range records {\n\t\t\tlist = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: path.Join(item.FileDir, item.FileName)})\n\t\t}\n\tdefault:\n\t\t_, records, err := backupRepo.PageRecord(\n\t\t\treq.Page, req.PageSize,\n\t\t\trepo.WithOrderDesc(\"created_at\"),\n\t\t\trepo.WithByName(req.Name),\n\t\t\trepo.WithByType(req.Type),\n\t\t\trepo.WithByDetailName(req.DetailName),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, item := range records {\n\t\t\tif item.Status == constant.StatusWaiting {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlist = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: path.Join(item.FileDir, item.FileName)})\n\t\t}\n\t}\n\trecordMap := make(map[uint]struct{})\n\tvar recordIds []string\n\tfor _, record := range list {\n\t\tif _, ok := recordMap[record.DownloadID]; !ok {\n\t\t\trecordMap[record.DownloadID] = struct{}{}\n\t\t\trecordIds = append(recordIds, fmt.Sprintf(\"%v\", record.DownloadID))\n\t\t}\n\t}\n\n\tclientMap := NewBackupClientMap(recordIds)\n\tvar datas []dto.RecordFileSize\n\tvar wg sync.WaitGroup\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\tfor i := 0; i < len(list); i++ {\n\t\tdatas = append(datas, dto.RecordFileSize{ID: list[i].ID})\n\t\tif val, ok := clientMap[fmt.Sprintf(\"%v\", list[i].DownloadID)]; ok {\n\t\t\tif !val.isOk {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\twg.Add(1)\n\t\t\tgo func(index int) {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tdone := make(chan struct{}, 1)\n\t\t\t\tgo func() {\n\t\t\t\t\tdatas[index].Size, _ = val.client.Size(path.Join(val.backupPath, list[i].FilePath))\n\t\t\t\t\tdefer close(done)\n\t\t\t\t}()\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn\n\t\t\t\tcase <-done:\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}(i)\n\t\t}\n\t}\n\twg.Wait()\n\treturn datas, nil\n}\n\nfunc (u *BackupRecordService) UpdateDescription(req dto.UpdateDescription) error {\n\treturn backupRepo.UpdateRecordByMap(req.ID, map[string]interface{}{\"description\": req.Description})\n}\n"
  },
  {
    "path": "agent/app/service/backup_redis.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\nfunc (u *BackupService) RedisBackup(req dto.CommonBackup) error {\n\tredisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tappendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, \"appendonly\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tglobal.LOG.Infof(\"appendonly in redis conf is %s\", appendonly)\n\n\ttimeNow := time.Now().Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)\n\tfileName := fmt.Sprintf(\"%s.rdb\", timeNow)\n\tif appendonly == \"yes\" {\n\t\tif strings.HasPrefix(redisInfo.Version, \"6.\") {\n\t\t\tfileName = fmt.Sprintf(\"%s.aof\", timeNow)\n\t\t} else {\n\t\t\tfileName = fmt.Sprintf(\"%s.tar.gz\", timeNow)\n\t\t}\n\t}\n\titemDir := fmt.Sprintf(\"database/redis/%s\", redisInfo.Name)\n\tbackupDir := path.Join(global.Dir.LocalBackupDir, itemDir)\n\trecord := &model.BackupRecord{\n\t\tType:              req.Type,\n\t\tName:              req.Name,\n\t\tSourceAccountIDs:  \"1\",\n\t\tDownloadAccountID: 1,\n\t\tFileDir:           itemDir,\n\t\tFileName:          fileName,\n\t\tTaskID:            req.TaskID,\n\t\tStatus:            constant.StatusWaiting,\n\t\tDescription:       req.Description,\n\t}\n\tif err := backupRepo.CreateRecord(record); err != nil {\n\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t}\n\n\tif err := handleRedisBackup(redisInfo, nil, record.ID, backupDir, fileName, req.Secret, req.TaskID); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) RedisRecover(req dto.CommonRecover) error {\n\tredisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tglobal.LOG.Infof(\"recover redis from backup file %s\", req.File)\n\tif err := handleRedisRecover(redisInfo, nil, req.File, false, req.Secret, req.TaskID); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, recordID uint, backupDir, fileName, secret, taskID string) error {\n\tvar (\n\t\terr      error\n\t\titemTask *task.Task\n\t)\n\titemTask = parentTask\n\tif parentTask == nil {\n\t\titemTask, err = task.NewTaskWithOps(\"Redis\", task.TaskBackup, task.TaskScopeBackup, taskID, redisInfo.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tbackupDatabase := func(t *task.Task) error {\n\t\tfileOp := files.NewFileOp()\n\t\tif !fileOp.Stat(backupDir) {\n\t\t\tif err := os.MkdirAll(backupDir, os.ModePerm); err != nil {\n\t\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", backupDir, err)\n\t\t\t}\n\t\t}\n\n\t\tif err := cmd.RunDefaultBashCf(\"docker exec %s redis-cli -a %s --no-auth-warning save\", redisInfo.ContainerName, redisInfo.Password); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif strings.HasSuffix(fileName, \".tar.gz\") {\n\t\t\tredisDataDir := fmt.Sprintf(\"%s/%s/%s/data/appendonlydir\", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name)\n\t\t\tif err := fileOp.TarGzCompressPro(true, redisDataDir, path.Join(backupDir, fileName), secret, \"\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tif strings.HasSuffix(fileName, \".aof\") {\n\t\t\tif err := cmd.RunDefaultBashCf(\"docker cp %s:/data/appendonly.aof %s/%s\", redisInfo.ContainerName, backupDir, fileName); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\tif err := cmd.RunDefaultBashCf(\"docker cp %s:/data/dump.rdb %s/%s\", redisInfo.ContainerName, backupDir, fileName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\titemTask.AddSubTask(i18n.GetMsgByKey(\"TaskBackup\"), backupDatabase, nil)\n\tif parentTask != nil {\n\t\treturn backupDatabase(parentTask)\n\t}\n\n\titemTask.AddSubTaskWithOps(i18n.GetMsgByKey(\"TaskBackup\"), backupDatabase, nil, 3, time.Hour)\n\tgo func() {\n\t\tif err := itemTask.Execute(); err != nil {\n\t\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\treturn\n\t\t}\n\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusSuccess})\n\t}()\n\treturn itemTask.Execute()\n}\n\nfunc handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {\n\tvar (\n\t\terr      error\n\t\titemTask *task.Task\n\t)\n\titemTask = parentTask\n\tif parentTask == nil {\n\t\titemTask, err = task.NewTaskWithOps(\"Redis\", task.TaskRecover, task.TaskScopeBackup, taskID, redisInfo.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\trecoverDatabase := func(t *task.Task) error {\n\t\tfileOp := files.NewFileOp()\n\t\tif !fileOp.Stat(recoverFile) {\n\t\t\treturn buserr.WithName(\"ErrFileNotFound\", recoverFile)\n\t\t}\n\n\t\tappendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, \"appendonly\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif appendonly == \"yes\" {\n\t\t\tif strings.HasPrefix(redisInfo.Version, \"6.\") && !strings.HasSuffix(recoverFile, \".aof\") {\n\t\t\t\treturn buserr.New(\"ErrTypeOfRedis\")\n\t\t\t}\n\t\t\tif strings.HasPrefix(redisInfo.Version, \"7.\") && !strings.HasSuffix(recoverFile, \".tar.gz\") {\n\t\t\t\treturn buserr.New(\"ErrTypeOfRedis\")\n\t\t\t}\n\t\t} else {\n\t\t\tif !strings.HasSuffix(recoverFile, \".rdb\") {\n\t\t\t\treturn buserr.New(\"ErrTypeOfRedis\")\n\t\t\t}\n\t\t}\n\n\t\tglobal.LOG.Infof(\"appendonly in redis conf is %s\", appendonly)\n\t\tisOk := false\n\t\tif !isRollback {\n\t\t\tsuffix := \"rdb\"\n\t\t\tif appendonly == \"yes\" {\n\t\t\t\tif strings.HasPrefix(redisInfo.Version, \"6.\") {\n\t\t\t\t\tsuffix = \"aof\"\n\t\t\t\t} else {\n\t\t\t\t\tsuffix = \"tar.gz\"\n\t\t\t\t}\n\t\t\t}\n\t\t\trollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf(\"database/redis/%s_%s.%s\", redisInfo.Name, time.Now().Format(constant.DateTimeSlimLayout), suffix))\n\t\t\tif err := handleRedisBackup(redisInfo, nil, 0, path.Dir(rollbackFile), path.Base(rollbackFile), secret, \"\"); err != nil {\n\t\t\t\treturn fmt.Errorf(\"backup database %s for rollback before recover failed, err: %v\", redisInfo.Name, err)\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tif !isOk {\n\t\t\t\t\tglobal.LOG.Info(\"recover failed, start to rollback now\")\n\t\t\t\t\tif err := handleRedisRecover(redisInfo, itemTask, rollbackFile, true, secret, \"\"); err != nil {\n\t\t\t\t\t\tglobal.LOG.Errorf(\"rollback redis from %s failed, err: %v\", rollbackFile, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tglobal.LOG.Infof(\"rollback redis from %s successful\", rollbackFile)\n\t\t\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t\t\t} else {\n\t\t\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\tcomposeDir := fmt.Sprintf(\"%s/%s/%s\", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name)\n\t\tif _, err := compose.Down(composeDir + \"/docker-compose.yml\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif appendonly == \"yes\" && strings.HasPrefix(redisInfo.Version, \"7.\") {\n\t\t\tredisDataDir := fmt.Sprintf(\"%s/%s/%s/data\", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name)\n\t\t\tif err := fileOp.TarGzExtractPro(recoverFile, redisDataDir, secret); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\titemName := \"dump.rdb\"\n\t\t\tif appendonly == \"yes\" && strings.HasPrefix(redisInfo.Version, \"6.\") {\n\t\t\t\titemName = \"appendonly.aof\"\n\t\t\t}\n\t\t\tinput, err := os.ReadFile(recoverFile)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err = os.WriteFile(composeDir+\"/data/\"+itemName, input, 0640); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif _, err := compose.Up(composeDir + \"/docker-compose.yml\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tisOk = true\n\t\treturn nil\n\t}\n\titemTask.AddSubTask(i18n.GetMsgByKey(\"TaskRecover\"), recoverDatabase, nil)\n\tif parentTask != nil {\n\t\treturn recoverDatabase(parentTask)\n\t}\n\n\tgo func() {\n\t\t_ = itemTask.Execute()\n\t}()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/backup_runtime.go",
    "content": "package service\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\nfunc handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string, excludes string, secret string) error {\n\tfileOp := files.NewFileOp()\n\ttmpDir := fmt.Sprintf(\"%s/%s\", backupDir, strings.ReplaceAll(fileName, \".tar.gz\", \"\"))\n\tif !fileOp.Stat(tmpDir) {\n\t\tif err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", backupDir, err)\n\t\t}\n\t}\n\tdefer func() {\n\t\t_ = os.RemoveAll(tmpDir)\n\t}()\n\n\tremarkInfo, _ := json.Marshal(runtime)\n\tremarkInfoPath := fmt.Sprintf(\"%s/runtime.json\", tmpDir)\n\tif err := fileOp.SaveFile(remarkInfoPath, string(remarkInfo), fs.ModePerm); err != nil {\n\t\treturn err\n\t}\n\n\tappPath := runtime.GetPath()\n\tif err := fileOp.TarGzCompressPro(true, appPath, path.Join(tmpDir, \"runtime.tar.gz\"), secret, excludes); err != nil {\n\t\treturn err\n\t}\n\tif err := fileOp.TarGzCompressPro(true, tmpDir, path.Join(backupDir, fileName), secret, \"\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback bool, secret string) error {\n\tisOk := false\n\tif !isRollback {\n\t\trollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf(\"runtime/%s_%s.tar.gz\", runtime.Name, time.Now().Format(constant.DateTimeSlimLayout)))\n\t\tif err := handleRuntimeBackup(runtime, path.Dir(rollbackFile), path.Base(rollbackFile), \"\", secret); err != nil {\n\t\t\treturn fmt.Errorf(\"backup runtime %s for rollback before recover failed, err: %v\", runtime.Name, err)\n\t\t}\n\t\tdefer func() {\n\t\t\tif !isOk {\n\t\t\t\tglobal.LOG.Info(\"recover failed, start to rollback now\")\n\t\t\t\tif err := handleRuntimeRecover(runtime, rollbackFile, true, \"\"); err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"rollback runtime %s from %s failed, err: %v\", runtime.Name, rollbackFile, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tglobal.LOG.Infof(\"rollback runtime %s from %s successful\", runtime.Name, rollbackFile)\n\t\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t\t} else {\n\t\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t\t}\n\t\t}()\n\t}\n\tfileOp := files.NewFileOp()\n\tif err := fileOp.TarGzExtractPro(recoverFile, path.Dir(recoverFile), secret); err != nil {\n\t\treturn err\n\t}\n\ttmpPath := strings.ReplaceAll(recoverFile, \".tar.gz\", \"\")\n\tdefer func() {\n\t\tgo startRuntime(runtime)\n\t\t_ = os.RemoveAll(strings.ReplaceAll(recoverFile, \".tar.gz\", \"\"))\n\t}()\n\n\tif !fileOp.Stat(tmpPath+\"/runtime.json\") || !fileOp.Stat(tmpPath+\"/runtime.tar.gz\") {\n\t\treturn errors.New(\"the wrong recovery package does not have runtime.json or runtime.tar.gz files\")\n\t}\n\tvar oldRuntime model.Runtime\n\truntimeJson, err := os.ReadFile(tmpPath + \"/runtime.json\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := json.Unmarshal(runtimeJson, &oldRuntime); err != nil {\n\t\treturn fmt.Errorf(\"unmarshal runtime.json failed, err: %v\", err)\n\t}\n\tif oldRuntime.Type != runtime.Type || oldRuntime.Name != runtime.Name {\n\t\treturn errors.New(\"the current backup file does not match the application\")\n\t}\n\n\tnewEnvFile, err := coverEnvJsonToStr(runtime.Env)\n\tif err != nil {\n\t\treturn err\n\t}\n\truntimeDir := runtime.GetPath()\n\tbackPath := fmt.Sprintf(\"%s_bak\", runtimeDir)\n\t_ = fileOp.Rename(runtimeDir, backPath)\n\t_ = fileOp.CreateDir(runtimeDir, constant.DirPerm)\n\n\tif err := fileOp.TarGzExtractPro(tmpPath+\"/runtime.tar.gz\", fmt.Sprintf(\"%s/%s\", global.Dir.RuntimeDir, runtime.Type), secret); err != nil {\n\t\tglobal.LOG.Errorf(\"handle recover from runtime.tar.gz failed, err: %v\", err)\n\t\t_ = fileOp.DeleteDir(runtimeDir)\n\t\t_ = fileOp.Rename(backPath, runtimeDir)\n\t\treturn err\n\t}\n\t_ = fileOp.DeleteDir(backPath)\n\n\tif len(newEnvFile) != 0 {\n\t\tenvPath := fmt.Sprintf(\"%s/%s/%s/.env\", global.Dir.RuntimeDir, runtime.Type, runtime.Name)\n\t\tfile, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer file.Close()\n\t\t_, _ = file.WriteString(newEnvFile)\n\t}\n\n\toldRuntime.ID = runtime.ID\n\toldRuntime.Status = constant.StatusStarting\n\tif err := runtimeRepo.Save(&oldRuntime); err != nil {\n\t\tglobal.LOG.Errorf(\"save db app install failed, err: %v\", err)\n\t\treturn err\n\t}\n\tisOk = true\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/backup_website.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\nfunc (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {\n\twebsite, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttimeNow := time.Now().Format(constant.DateTimeSlimLayout)\n\titemDir := fmt.Sprintf(\"website/%s\", website.Alias)\n\tbackupDir := path.Join(global.Dir.LocalBackupDir, itemDir)\n\tfileName := fmt.Sprintf(\"%s_%s.tar.gz\", website.Alias, timeNow+common.RandStrAndNum(5))\n\n\trecord := &model.BackupRecord{\n\t\tType:              \"website\",\n\t\tName:              website.Alias,\n\t\tDetailName:        website.Alias,\n\t\tSourceAccountIDs:  \"1\",\n\t\tDownloadAccountID: 1,\n\t\tFileDir:           itemDir,\n\t\tFileName:          fileName,\n\t\tTaskID:            req.TaskID,\n\t\tStatus:            constant.StatusWaiting,\n\t\tDescription:       req.Description,\n\t}\n\tif err = backupRepo.CreateRecord(record); err != nil {\n\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\treturn err\n\t}\n\tif err = handleWebsiteBackup(&website, nil, record.ID, backupDir, fileName, \"\", req.Secret, req.TaskID); err != nil {\n\t\tglobal.LOG.Errorf(\"backup website %s failed, err: %v\", website.Alias, err)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {\n\twebsite, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := handleWebsiteRecover(&website, nil, req.File, false, req.Secret, req.TaskID); err != nil {\n\t\tglobal.LOG.Errorf(\"recover website %s failed, err: %v\", website.Alias, err)\n\t}\n\treturn nil\n}\n\nfunc handleWebsiteRecover(website *model.Website, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {\n\tvar (\n\t\terr          error\n\t\trecoverTask  *task.Task\n\t\tisOk         = false\n\t\trollbackFile string\n\t)\n\trecoverTask = parentTask\n\tif parentTask == nil {\n\t\trecoverTask, err = task.NewTaskWithOps(website.PrimaryDomain, task.TaskRecover, task.TaskScopeBackup, taskID, website.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\trecoverWebsite := func(t *task.Task) error {\n\t\tif !isRollback {\n\t\t\trollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf(\"website/%s_%s.tar.gz\", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))\n\t\t\tif err := handleWebsiteBackup(website, recoverTask, 0, path.Dir(rollbackFile), path.Base(rollbackFile), \"\", \"\", taskID); err != nil {\n\t\t\t\treturn fmt.Errorf(\"backup website %s for rollback before recover failed, err: %v\", website.Alias, err)\n\t\t\t}\n\t\t}\n\n\t\tfileOp := files.NewFileOp()\n\t\ttmpPath := strings.ReplaceAll(recoverFile, \".tar.gz\", \"\")\n\t\tt.Log(i18n.GetWithName(\"DeCompressFile\", recoverFile))\n\t\tif err = fileOp.TarGzExtractPro(recoverFile, path.Dir(recoverFile), secret); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() {\n\t\t\t_ = os.RemoveAll(tmpPath)\n\t\t}()\n\n\t\tvar oldWebsite model.Website\n\t\twebsiteJson, err := os.ReadFile(tmpPath + \"/website.json\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = json.Unmarshal(websiteJson, &oldWebsite); err != nil {\n\t\t\treturn fmt.Errorf(\"unmarshal app.json failed, err: %v\", err)\n\t\t}\n\n\t\tif err = checkValidOfWebsite(&oldWebsite, website); err != nil {\n\t\t\tt.Log(i18n.GetWithName(\"ErrCheckValid\", err.Error()))\n\t\t\treturn err\n\t\t}\n\n\t\ttemPathWithName := tmpPath + \"/\" + website.Alias\n\t\tif !fileOp.Stat(tmpPath+\"/website.json\") || !fileOp.Stat(temPathWithName+\".conf\") || !fileOp.Stat(temPathWithName+\".web.tar.gz\") {\n\t\t\treturn buserr.WithDetail(\"ErrBackupExist\", \".conf or .web.tar.gz\", nil)\n\t\t}\n\t\tif website.Type == constant.Deployment {\n\t\t\tif !fileOp.Stat(temPathWithName + \".app.tar.gz\") {\n\t\t\t\treturn buserr.WithDetail(\"ErrBackupExist\", \".app.tar.gz\", nil)\n\t\t\t}\n\t\t}\n\n\t\tnginxInfo, err := appInstallRepo.LoadBaseInfo(constant.AppOpenresty, \"\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = fileOp.CopyFile(fmt.Sprintf(\"%s/%s.conf\", tmpPath, website.Alias), GetConfDir(*website)); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tswitch website.Type {\n\t\tcase constant.Deployment:\n\t\t\tapp, err := appInstallRepo.GetFirst(repo.WithByID(website.AppInstallID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ttaskName := task.GetTaskName(app.Name, task.TaskRecover, task.TaskScopeApp)\n\t\t\tt.LogStart(taskName)\n\t\t\tif err := handleAppRecover(&app, recoverTask, fmt.Sprintf(\"%s/%s.app.tar.gz\", tmpPath, website.Alias), true, \"\", \"\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tt.LogSuccess(taskName)\n\t\t\tif _, err = compose.DownAndUp(fmt.Sprintf(\"%s/%s/%s/docker-compose.yml\", global.Dir.AppInstallDir, app.App.Key, app.Name)); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase constant.Runtime:\n\t\t\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ttaskName := task.GetTaskName(runtime.Name, task.TaskRecover, task.TaskScopeRuntime)\n\t\t\tt.LogStart(taskName)\n\t\t\tif err := handleRuntimeRecover(runtime, fmt.Sprintf(\"%s/%s.runtime.tar.gz\", tmpPath, website.Alias), true, \"\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tt.LogSuccess(taskName)\n\t\t\tif oldWebsite.DbID > 0 {\n\t\t\t\tif err := recoverWebsiteDatabase(t, oldWebsite.DbID, oldWebsite.DbType, tmpPath, website.Alias); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\tcase constant.Static:\n\t\t\tif oldWebsite.DbID > 0 {\n\t\t\t\tif err := recoverWebsiteDatabase(t, oldWebsite.DbID, oldWebsite.DbType, tmpPath, website.Alias); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttaskName := i18n.GetMsgByKey(\"TaskRecover\") + i18n.GetMsgByKey(\"websiteDir\")\n\t\tt.Log(taskName)\n\t\tif err = fileOp.TarGzExtractPro(fmt.Sprintf(\"%s/%s.web.tar.gz\", tmpPath, website.Alias), GetOpenrestyDir(SitesRootDir), \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := cmd.RunDefaultBashCf(\"docker exec -i %s nginx -s reload\", nginxInfo.ContainerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\toldWebsite.ID = website.ID\n\t\tif err := websiteRepo.SaveWithoutCtx(&oldWebsite); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tisOk = true\n\t\treturn nil\n\t}\n\n\tif parentTask != nil {\n\t\treturn recoverWebsite(parentTask)\n\t}\n\n\trollBackWebsite := func(t *task.Task) {\n\t\tif isRollback {\n\t\t\treturn\n\t\t}\n\t\tif !isOk {\n\t\t\tt.Log(i18n.GetMsgByKey(\"RecoverFailedStartRollBack\"))\n\t\t\tif err := handleWebsiteRecover(website, t, rollbackFile, true, \"\", \"\"); err != nil {\n\t\t\t\tt.LogFailedWithErr(i18n.GetMsgByKey(\"Rollback\"), err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tt.LogSuccess(i18n.GetMsgByKey(\"Rollback\"))\n\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t} else {\n\t\t\t_ = os.RemoveAll(rollbackFile)\n\t\t}\n\t}\n\trecoverTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskRecover, task.TaskScopeBackup), recoverWebsite, rollBackWebsite)\n\tgo func() {\n\t\t_ = recoverTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc handleWebsiteBackup(website *model.Website, parentTask *task.Task, recordID uint, backupDir, fileName, excludes, secret, taskID string) error {\n\tvar (\n\t\terr        error\n\t\tbackupTask *task.Task\n\t)\n\tbackupTask = parentTask\n\tif parentTask == nil {\n\t\tbackupTask, err = task.NewTaskWithOps(website.Alias, task.TaskBackup, task.TaskScopeBackup, taskID, website.ID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\titemHandler := func() error { return doWebsiteBackup(website, backupTask, backupDir, fileName, excludes, secret) }\n\tif parentTask != nil {\n\t\treturn itemHandler()\n\t}\n\tbackupTask.AddSubTaskWithOps(task.GetTaskName(website.Alias, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)\n\tgo func() {\n\t\tif err := backupTask.Execute(); err != nil {\n\t\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\treturn\n\t\t}\n\t\tbackupRepo.UpdateRecordByMap(recordID, map[string]interface{}{\"status\": constant.StatusSuccess})\n\t}()\n\treturn nil\n}\n\nfunc doWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, fileName, excludes, secret string) error {\n\tfileOp := files.NewFileOp()\n\ttmpDir := fmt.Sprintf(\"%s/%s\", backupDir, strings.ReplaceAll(fileName, \".tar.gz\", \"\"))\n\tif !fileOp.Stat(tmpDir) {\n\t\tif err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", backupDir, err)\n\t\t}\n\t}\n\tdefer func() {\n\t\t_ = os.RemoveAll(tmpDir)\n\t}()\n\n\tremarkInfo, _ := json.Marshal(website)\n\tif err := fileOp.SaveFile(tmpDir+\"/website.json\", string(remarkInfo), fs.ModePerm); err != nil {\n\t\treturn err\n\t}\n\tnginxConfFile := GetWebsiteConfigPath(*website)\n\tif err := fileOp.CopyFile(nginxConfFile, tmpDir); err != nil {\n\t\treturn err\n\t}\n\tparentTask.Log(i18n.GetMsgByKey(\"BackupNginxConfig\"))\n\n\tswitch website.Type {\n\tcase constant.Deployment:\n\t\tapp, err := appInstallRepo.GetFirst(repo.WithByID(website.AppInstallID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparentTask.LogStart(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))\n\t\tif err = handleAppBackup(&app, parentTask, 0, tmpDir, fmt.Sprintf(\"%s.app.tar.gz\", website.Alias), excludes, \"\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparentTask.LogSuccess(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))\n\tcase constant.Runtime:\n\t\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparentTask.LogStart(task.GetTaskName(runtime.Name, task.TaskBackup, task.TaskScopeRuntime))\n\t\tif err = handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf(\"%s.runtime.tar.gz\", website.Alias), excludes, \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparentTask.LogSuccess(task.GetTaskName(runtime.Name, task.TaskBackup, task.TaskScopeRuntime))\n\t\tif website.DbID > 0 {\n\t\t\tif err = backupDatabaseWithTask(parentTask, website.DbType, tmpDir, website.Alias, website.DbID); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tcase constant.Static:\n\t\tif website.DbID > 0 {\n\t\t\tif err := backupDatabaseWithTask(parentTask, website.DbType, tmpDir, website.Alias, website.DbID); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\twebsiteDir := GetSitePath(*website, SiteDir)\n\tparentTask.LogStart(i18n.GetMsgByKey(\"CompressDir\"))\n\tif err := fileOp.TarGzCompressPro(true, websiteDir, path.Join(tmpDir, fmt.Sprintf(\"%s.web.tar.gz\", website.Alias)), \"\", excludes); err != nil {\n\t\treturn err\n\t}\n\tif err := fileOp.TarGzCompressPro(true, tmpDir, path.Join(backupDir, fileName), secret, \"\"); err != nil {\n\t\treturn err\n\t}\n\tparentTask.Log(i18n.GetWithName(\"CompressFileSuccess\", fileName))\n\treturn nil\n}\n\nfunc checkValidOfWebsite(oldWebsite, website *model.Website) error {\n\tif oldWebsite.Alias != website.Alias || oldWebsite.Type != website.Type {\n\t\treturn buserr.WithDetail(\"ErrBackupMatch\", fmt.Sprintf(\"oldName: %s, oldType: %v\", oldWebsite.Alias, oldWebsite.Type), nil)\n\t}\n\tif oldWebsite.AppInstallID != 0 {\n\t\t_, err := appInstallRepo.GetFirst(repo.WithByID(oldWebsite.AppInstallID))\n\t\tif err != nil {\n\t\t\treturn buserr.WithDetail(\"ErrBackupMatch\", \"app\", nil)\n\t\t}\n\t}\n\tif oldWebsite.RuntimeID != 0 {\n\t\tif _, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(oldWebsite.RuntimeID)); err != nil {\n\t\t\treturn buserr.WithDetail(\"ErrBackupMatch\", \"runtime\", nil)\n\t\t}\n\t}\n\tif oldWebsite.WebsiteSSLID != 0 {\n\t\tif _, err := websiteSSLRepo.GetFirst(repo.WithByID(oldWebsite.WebsiteSSLID)); err != nil {\n\t\t\treturn buserr.WithDetail(\"ErrBackupMatch\", \"ssl\", nil)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc recoverWebsiteDatabase(t *task.Task, dbID uint, dbType, tmpPath, websiteKey string) error {\n\tswitch dbType {\n\tcase constant.AppPostgresql:\n\t\tdb, err := postgresqlRepo.Get(repo.WithByID(dbID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttaskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)\n\t\tt.LogStart(taskName)\n\t\tif err := handlePostgresqlRecover(dto.CommonRecover{\n\t\t\tName:       db.PostgresqlName,\n\t\t\tDetailName: db.Name,\n\t\t\tFile:       fmt.Sprintf(\"%s/%s.sql.gz\", tmpPath, websiteKey),\n\t\t}, t, true); err != nil {\n\t\t\tt.LogFailedWithErr(taskName, err)\n\t\t\treturn err\n\t\t}\n\t\tt.LogSuccess(taskName)\n\tcase constant.AppMysql, constant.AppMariaDB:\n\t\tdb, err := mysqlRepo.Get(repo.WithByID(dbID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttaskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)\n\t\tt.LogStart(taskName)\n\t\tif err := handleMysqlRecover(dto.CommonRecover{\n\t\t\tName:       db.MysqlName,\n\t\t\tDetailName: db.Name,\n\t\t\tFile:       fmt.Sprintf(\"%s/%s.sql.gz\", tmpPath, websiteKey),\n\t\t}, t, true); err != nil {\n\t\t\tt.LogFailedWithErr(taskName, err)\n\t\t\treturn err\n\t\t}\n\t\tt.LogSuccess(taskName)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/clam.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/alert_push\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/clam\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"github.com/jinzhu/copier\"\n\t\"github.com/robfig/cron/v3\"\n)\n\ntype ClamService struct {\n\tserviceName      string\n\tfreshClamService string\n}\n\ntype IClamService interface {\n\tLoadBaseInfo() (dto.ClamBaseInfo, error)\n\tOperate(operate string) error\n\tSearchWithPage(search dto.SearchClamWithPage) (int64, interface{}, error)\n\tCreate(req dto.ClamCreate) error\n\tUpdate(req dto.ClamUpdate) error\n\tUpdateStatus(id uint, status string) error\n\tDelete(req dto.ClamDelete) error\n\tHandleOnce(id uint) error\n\n\tLoadFile(req dto.ClamFileReq) (string, error)\n\tUpdateFile(req dto.UpdateByNameAndFile) error\n\n\tSearchRecords(req dto.ClamLogSearch) (int64, interface{}, error)\n\tCleanRecord(id uint) error\n}\n\nfunc NewIClamService() IClamService {\n\treturn &ClamService{}\n}\n\nfunc (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {\n\tvar baseInfo dto.ClamBaseInfo\n\tbaseInfo.Version = \"-\"\n\tbaseInfo.FreshVersion = \"-\"\n\n\tclamSvc, err := controller.LoadServiceName(\"clam\")\n\tif err != nil {\n\t\tbaseInfo.IsExist = false\n\t\treturn baseInfo, nil\n\t}\n\tc.serviceName = clamSvc\n\texist, _ := controller.CheckExist(clamSvc)\n\tif exist {\n\t\tbaseInfo.IsExist = true\n\t\tbaseInfo.IsActive, _ = controller.CheckActive(clamSvc)\n\t}\n\n\tfreshSvc, err := controller.LoadServiceName(\"freshclam\")\n\tif err != nil {\n\t\tbaseInfo.FreshIsExist = false\n\t\treturn baseInfo, nil\n\t}\n\tc.freshClamService = freshSvc\n\tfreshExist, _ := controller.CheckExist(freshSvc)\n\tif freshExist {\n\t\tbaseInfo.FreshIsExist = true\n\t\tbaseInfo.FreshIsActive, _ = controller.CheckActive(freshSvc)\n\t}\n\n\tif !cmd.Which(\"clamdscan\") {\n\t\tbaseInfo.IsActive = false\n\t}\n\n\tif baseInfo.IsActive {\n\t\tversion, err := cmd.RunDefaultWithStdoutBashC(\"clamdscan --version\")\n\t\tif err == nil {\n\t\t\tif strings.Contains(version, \"/\") {\n\t\t\t\tbaseInfo.Version = strings.TrimPrefix(strings.Split(version, \"/\")[0], \"ClamAV \")\n\t\t\t} else {\n\t\t\t\tbaseInfo.Version = strings.TrimPrefix(version, \"ClamAV \")\n\t\t\t}\n\t\t}\n\t} else {\n\t\t_ = clam.CheckWithStopAll(false, clamRepo)\n\t}\n\tif baseInfo.FreshIsActive {\n\t\tversion, err := cmd.RunDefaultWithStdoutBashC(\"freshclam --version\")\n\t\tif err == nil {\n\t\t\tif strings.Contains(version, \"/\") {\n\t\t\t\tbaseInfo.FreshVersion = strings.TrimPrefix(strings.Split(version, \"/\")[0], \"ClamAV \")\n\t\t\t} else {\n\t\t\t\tbaseInfo.FreshVersion = strings.TrimPrefix(version, \"ClamAV \")\n\t\t\t}\n\t\t}\n\t}\n\treturn baseInfo, nil\n}\n\nfunc (c *ClamService) Operate(operate string) error {\n\tswitch operate {\n\tcase \"start\", \"restart\", \"stop\":\n\t\tif err := controller.Handle(operate, c.serviceName); err != nil {\n\t\t\treturn fmt.Errorf(\"%s the %s failed, err: %s\", operate, c.serviceName, err)\n\t\t}\n\t\treturn nil\n\tcase \"fresh-start\", \"fresh-restart\", \"fresh-stop\":\n\t\tif err := controller.Handle(strings.TrimPrefix(operate, \"fresh-\"), c.freshClamService); err != nil {\n\t\t\treturn fmt.Errorf(\"%s the %s failed, err: %s\", operate, c.serviceName, err)\n\t\t}\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"not support such operation: %v\", operate)\n\t}\n}\n\nfunc (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interface{}, error) {\n\ttotal, clams, err := clamRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order))\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar datas []dto.ClamInfo\n\tfor _, clam := range clams {\n\t\tvar item dto.ClamInfo\n\t\tif err := copier.Copy(&item, &clam); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdatas = append(datas, item)\n\t}\n\tfor i := 0; i < len(datas); i++ {\n\t\trecord, _ := clamRepo.RecordFirst(datas[i].ID)\n\t\tif record.ID != 0 {\n\t\t\tdatas[i].LastRecordStatus = record.Status\n\t\t\tdatas[i].LastRecordTime = record.StartTime.Format(constant.DateTimeLayout)\n\t\t} else {\n\t\t\tdatas[i].LastRecordTime = \"-\"\n\t\t}\n\t\talertBase := dto.AlertBase{\n\t\t\tAlertType: \"clams\",\n\t\t\tEntryID:   datas[i].ID,\n\t\t}\n\t\talertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))\n\t\tdatas[i].AlertMethod = alertInfo.Method\n\t\tif alertInfo.SendCount != 0 {\n\t\t\tdatas[i].AlertCount = alertInfo.SendCount\n\t\t} else {\n\t\t\tdatas[i].AlertCount = 0\n\t\t}\n\t}\n\treturn total, datas, err\n}\n\nfunc (c *ClamService) Create(req dto.ClamCreate) error {\n\tclam, _ := clamRepo.Get(repo.WithByName(req.Name))\n\tif clam.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tif cmd.CheckIllegal(req.Path) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif err := copier.Copy(&clam, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif clam.InfectedStrategy == \"none\" || clam.InfectedStrategy == \"remove\" {\n\t\tclam.InfectedDir = \"\"\n\t}\n\tif len(req.Spec) != 0 {\n\t\tentryID, err := xpack.StartClam(&clam, false)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tclam.EntryID = entryID\n\t\tclam.Status = constant.StatusEnable\n\t}\n\tif err := clamRepo.Create(&clam); err != nil {\n\t\treturn err\n\t}\n\tif req.AlertCount != 0 && req.AlertTitle != \"\" && req.AlertMethod != \"\" {\n\t\tcreateAlert := dto.AlertCreate{\n\t\t\tTitle:     req.AlertTitle,\n\t\t\tSendCount: req.AlertCount,\n\t\t\tMethod:    req.AlertMethod,\n\t\t\tType:      \"clams\",\n\t\t\tProject:   strconv.Itoa(int(clam.ID)),\n\t\t\tStatus:    constant.AlertEnable,\n\t\t}\n\t\terr := NewIAlertService().CreateAlert(createAlert)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *ClamService) Update(req dto.ClamUpdate) error {\n\tif cmd.CheckIllegal(req.Path) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tclam, _ := clamRepo.Get(repo.WithByName(req.Name))\n\tif clam.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif req.InfectedStrategy == \"none\" || req.InfectedStrategy == \"remove\" {\n\t\treq.InfectedDir = \"\"\n\t}\n\tvar clamItem model.Clam\n\tif err := copier.Copy(&clamItem, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tclamItem.EntryID = clam.EntryID\n\tupMap := map[string]interface{}{}\n\tif len(clam.Spec) != 0 && clam.EntryID != 0 {\n\t\tglobal.Cron.Remove(cron.EntryID(clamItem.EntryID))\n\t\tupMap[\"entry_id\"] = 0\n\t}\n\tif len(req.Spec) == 0 {\n\t\tupMap[\"status\"] = \"\"\n\t\tupMap[\"entry_id\"] = 0\n\t}\n\tif len(req.Spec) != 0 && clam.Status != constant.StatusDisable {\n\t\tnewEntryID, err := xpack.StartClam(&clamItem, true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tupMap[\"entry_id\"] = newEntryID\n\t}\n\tif len(clam.Spec) == 0 && len(req.Spec) != 0 {\n\t\tupMap[\"status\"] = constant.StatusEnable\n\t}\n\n\tupMap[\"name\"] = req.Name\n\tupMap[\"path\"] = req.Path\n\tupMap[\"infected_dir\"] = req.InfectedDir\n\tupMap[\"infected_strategy\"] = req.InfectedStrategy\n\tupMap[\"spec\"] = req.Spec\n\tupMap[\"timeout\"] = req.Timeout\n\tupMap[\"description\"] = req.Description\n\tif err := clamRepo.Update(req.ID, upMap); err != nil {\n\t\treturn err\n\t}\n\tupdateAlert := dto.AlertCreate{\n\t\tTitle:     req.AlertTitle,\n\t\tSendCount: req.AlertCount,\n\t\tMethod:    req.AlertMethod,\n\t\tType:      \"clams\",\n\t\tProject:   strconv.Itoa(int(clam.ID)),\n\t}\n\terr := NewIAlertService().ExternalUpdateAlert(updateAlert)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *ClamService) UpdateStatus(id uint, status string) error {\n\tclam, _ := clamRepo.Get(repo.WithByID(id))\n\tif clam.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tvar (\n\t\tentryID int\n\t\terr     error\n\t)\n\tif status == constant.StatusEnable {\n\t\tentryID, err = xpack.StartClam(&clam, true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tglobal.Cron.Remove(cron.EntryID(clam.EntryID))\n\t\tglobal.LOG.Infof(\"stop cronjob entryID: %v\", clam.EntryID)\n\t}\n\n\treturn clamRepo.Update(clam.ID, map[string]interface{}{\"status\": status, \"entry_id\": entryID})\n}\n\nfunc (c *ClamService) Delete(req dto.ClamDelete) error {\n\tfor _, id := range req.Ids {\n\t\tclam, _ := clamRepo.Get(repo.WithByID(id))\n\t\tif clam.ID == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif len(clam.Spec) != 0 {\n\t\t\tglobal.Cron.Remove(cron.EntryID(clam.EntryID))\n\t\t}\n\t\t_ = c.CleanRecord(clam.ID)\n\t\tif req.RemoveInfected {\n\t\t\t_ = os.RemoveAll(path.Join(clam.InfectedDir, \"1panel-infected\", clam.Name))\n\t\t}\n\t\tif err := clamRepo.Delete(repo.WithByID(id)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr := alertRepo.Delete(alertRepo.WithByProject(strconv.Itoa(int(clam.ID))), alertRepo.WithByType(\"clams\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *ClamService) HandleOnce(id uint) error {\n\tif active := clam.CheckWithStopAll(true, clamRepo); !active {\n\t\treturn buserr.New(\"ErrClamdscanNotFound\")\n\t}\n\tclamItem, _ := clamRepo.Get(repo.WithByID(id))\n\tif clamItem.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\trecord := clamRepo.StartRecords(clamItem.ID)\n\ttaskItem, err := task.NewTaskWithOps(\"clam-\"+clamItem.Name, task.TaskScan, task.TaskScopeClam, record.TaskID, clamItem.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for exec shell failed, err: %v\", err)\n\t}\n\tclam.AddScanTask(taskItem, clamItem, record.StartTime.Format(constant.DateTimeSlimLayout))\n\tgo func() {\n\t\terr := taskItem.Execute()\n\t\ttaskRepo := repo.NewITaskRepo()\n\t\ttaskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))\n\t\tif len(taskItem.ID) == 0 {\n\t\t\trecord.TaskID = \"\"\n\t\t}\n\t\tif err != nil {\n\t\t\tclamRepo.EndRecords(record, constant.StatusFailed, err.Error())\n\t\t\treturn\n\t\t}\n\t\tclam.AnalysisFromLog(taskItem.LogFile, &record)\n\t\tclamRepo.EndRecords(record, constant.StatusDone, \"\")\n\t\thandleAlert(record.InfectedFiles, clamItem.Name, clamItem.ID)\n\t}()\n\treturn nil\n}\n\nfunc (c *ClamService) SearchRecords(req dto.ClamLogSearch) (int64, interface{}, error) {\n\tclam, _ := clamRepo.Get(repo.WithByID(req.ClamID))\n\tif clam.ID == 0 {\n\t\treturn 0, nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tloc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\treq.StartTime = req.StartTime.In(loc)\n\treq.EndTime = req.EndTime.In(loc)\n\n\ttotal, records, err := clamRepo.PageRecords(req.Page, req.PageSize, clamRepo.WithByClamID(req.ClamID), repo.WithByStatus(req.Status), repo.WithByCreatedAt(req.StartTime, req.EndTime))\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar datas []dto.ClamRecord\n\tfor _, record := range records {\n\t\tvar item dto.ClamRecord\n\t\tif err := copier.Copy(&item, &record); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdatas = append(datas, item)\n\t}\n\treturn int64(total), datas, nil\n}\n\nfunc (c *ClamService) CleanRecord(id uint) error {\n\trecord, _ := clamRepo.ListRecord()\n\tfor _, item := range record {\n\t\tif len(item.TaskID) != 0 {\n\t\t\tcontinue\n\t\t}\n\t\ttaskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(item.TaskID))\n\t\tif len(taskItem.LogFile) != 0 {\n\t\t\t_ = os.Remove(taskItem.LogFile)\n\t\t}\n\t}\n\treturn clamRepo.DeleteRecord(clamRepo.WithByClamID(id))\n}\n\nfunc (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {\n\tfilePath := \"\"\n\tswitch req.Name {\n\tcase \"clamd\":\n\t\tfilePath = c.loadConfigPath(\"clamd\")\n\tcase \"clamd-log\":\n\t\tfilePath = c.loadLogPath(\"clamd-log\")\n\tcase \"freshclam\":\n\t\tfilePath = c.loadConfigPath(\"freshclam\")\n\tcase \"freshclam-log\":\n\t\tfilePath = c.loadLogPath(\"freshclam-log\")\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"not support such type\")\n\t}\n\tif _, err := os.Stat(filePath); err != nil {\n\t\treturn \"\", buserr.New(\"ErrHttpReqNotFound\")\n\t}\n\tvar tail string\n\tif req.Tail != \"0\" {\n\t\ttail = req.Tail\n\t} else {\n\t\ttail = \"+1\"\n\t}\n\tcmd := exec.Command(\"tail\", \"-n\", tail, filePath)\n\tstdout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"tail -n %v failed, err: %v\", req.Tail, err)\n\t}\n\treturn string(stdout), nil\n}\n\nfunc (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {\n\tfilePath := \"\"\n\tswitch req.Name {\n\tcase \"clamd\":\n\t\tfilePath = c.loadConfigPath(\"clamd\")\n\tcase \"freshclam\":\n\t\tfilePath = c.loadConfigPath(\"freshclam\")\n\tdefault:\n\t\treturn fmt.Errorf(\"not support such type\")\n\t}\n\tfile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(req.File)\n\twrite.Flush()\n\n\t_ = controller.HandleRestart(c.serviceName)\n\treturn nil\n}\n\nfunc (c *ClamService) loadLogPath(name string) string {\n\tconfigKey := \"clamd\"\n\tsearchPrefix := \"LogFile \"\n\tif name != \"clamd-log\" {\n\t\tconfigKey = \"freshclam\"\n\t\tsearchPrefix = \"UpdateLogFile \"\n\t}\n\tconfPath := c.loadConfigPath(configKey)\n\tcontent, err := os.ReadFile(confPath)\n\tif err != nil {\n\t\tglobal.LOG.Debugf(\"read config of %s failed, err: %v\", configKey, err)\n\t\treturn \"\"\n\t}\n\tlines := strings.Split(string(content), \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.HasPrefix(line, searchPrefix) {\n\t\t\treturn strings.Trim(strings.ReplaceAll(line, searchPrefix, \"\"), \" \")\n\t\t}\n\t}\n\tif configKey == \"clamd\" {\n\t\tif _, err := os.Stat(\"/var/log/clamav/clamav.log\"); err == nil {\n\t\t\treturn \"/var/log/clamav/clamav.log\"\n\t\t}\n\t\tif _, err := os.Stat(\"/var/log/clamd.scan\"); err == nil {\n\t\t\treturn \"/var/log/clamd.scan\"\n\t\t}\n\t}\n\tif configKey == \"freshclam\" {\n\t\tif _, err := os.Stat(\"/var/log/clamav/freshclam.log\"); err == nil {\n\t\t\treturn \"/var/log/clamav/freshclam.log\"\n\t\t}\n\t\tif _, err := os.Stat(\"/var/log/freshclam.log\"); err == nil {\n\t\t\treturn \"/var/log/freshclam.log\"\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc (c *ClamService) loadConfigPath(confType string) string {\n\tswitch confType {\n\tcase \"clamd\":\n\t\tif _, err := os.Stat(\"/etc/clamav/clamd.conf\"); err == nil {\n\t\t\treturn \"/etc/clamav/clamd.conf\"\n\t\t}\n\t\treturn \"/etc/clamd.d/scan.conf\"\n\tcase \"freshclam\":\n\t\tif _, err := os.Stat(\"/etc/clamav/freshclam.conf\"); err == nil {\n\t\t\treturn \"/etc/clamav/freshclam.conf\"\n\t\t}\n\t\treturn \"/etc/freshclam.conf\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc handleAlert(infectedFiles, clamName string, clamId uint) {\n\titemInfected, _ := strconv.Atoi(strings.TrimSpace(infectedFiles))\n\tif itemInfected < 0 {\n\t\treturn\n\t}\n\tpushAlert := dto.PushAlert{\n\t\tTaskName:  clamName,\n\t\tAlertType: \"clams\",\n\t\tEntryID:   clamId,\n\t\tParam:     strconv.Itoa(itemInfected),\n\t}\n\t_ = alert_push.PushAlert(pushAlert)\n}\n"
  },
  {
    "path": "agent/app/service/compose_template.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype ComposeTemplateService struct{}\n\ntype IComposeTemplateService interface {\n\tList() ([]dto.ComposeTemplateInfo, error)\n\tSearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)\n\tCreate(req dto.ComposeTemplateCreate) error\n\tUpdate(id uint, upMap map[string]interface{}) error\n\tBatch(req dto.ComposeTemplateBatch) error\n\tDelete(ids []uint) error\n}\n\nfunc NewIComposeTemplateService() IComposeTemplateService {\n\treturn &ComposeTemplateService{}\n}\n\nfunc (u *ComposeTemplateService) List() ([]dto.ComposeTemplateInfo, error) {\n\tcomposes, err := composeRepo.List()\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tvar dtoLists []dto.ComposeTemplateInfo\n\tfor _, compose := range composes {\n\t\tvar item dto.ComposeTemplateInfo\n\t\tif err := copier.Copy(&item, &compose); err != nil {\n\t\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdtoLists = append(dtoLists, item)\n\t}\n\treturn dtoLists, err\n}\n\nfunc (u *ComposeTemplateService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {\n\ttotal, composes, err := composeRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info))\n\tvar dtoComposeTemplates []dto.ComposeTemplateInfo\n\tfor _, compose := range composes {\n\t\tvar item dto.ComposeTemplateInfo\n\t\tif err := copier.Copy(&item, &compose); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdtoComposeTemplates = append(dtoComposeTemplates, item)\n\t}\n\treturn total, dtoComposeTemplates, err\n}\n\nfunc (u *ComposeTemplateService) Create(composeDto dto.ComposeTemplateCreate) error {\n\tcompose, _ := composeRepo.Get(repo.WithByName(composeDto.Name))\n\tif compose.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tif err := copier.Copy(&compose, &composeDto); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif err := composeRepo.Create(&compose); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *ComposeTemplateService) Batch(req dto.ComposeTemplateBatch) error {\n\tfor _, item := range req.Templates {\n\t\ttemplate, _ := composeRepo.Get(repo.WithByName(item.Name))\n\t\tif template.ID == 0 {\n\t\t\tif err := composeRepo.Create(&model.ComposeTemplate{Name: item.Name, Content: item.Content, Description: item.Description}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif err := composeRepo.Update(template.ID, map[string]interface{}{\"content\": item.Content, \"description\": item.Description}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *ComposeTemplateService) Delete(ids []uint) error {\n\tif len(ids) == 1 {\n\t\tcompose, _ := composeRepo.Get(repo.WithByID(ids[0]))\n\t\tif compose.ID == 0 {\n\t\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t\t}\n\t\treturn composeRepo.Delete(repo.WithByID(ids[0]))\n\t}\n\treturn composeRepo.Delete(repo.WithByIDs(ids))\n}\n\nfunc (u *ComposeTemplateService) Update(id uint, upMap map[string]interface{}) error {\n\treturn composeRepo.Update(id, upMap)\n}\n"
  },
  {
    "path": "agent/app/service/container.go",
    "content": "package service\n\nimport (\n\t\"archive/tar\"\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker/docker/api/types/build\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/api/types/filters\"\n\t\"github.com/docker/docker/api/types/image\"\n\t\"github.com/docker/docker/api/types/mount\"\n\t\"github.com/docker/docker/api/types/network\"\n\t\"github.com/docker/docker/api/types/registry\"\n\t\"github.com/docker/docker/api/types/volume\"\n\t\"github.com/docker/docker/client\"\n\t\"github.com/docker/docker/pkg/stdcopy\"\n\t\"github.com/docker/go-connections/nat\"\n\t\"github.com/gin-gonic/gin\"\n\tv1 \"github.com/opencontainers/image-spec/specs-go/v1\"\n\t\"github.com/shirou/gopsutil/v4/cpu\"\n\t\"github.com/shirou/gopsutil/v4/mem\"\n)\n\ntype ContainerService struct{}\n\ntype IContainerService interface {\n\tPage(req dto.PageContainer) (int64, interface{}, error)\n\tList() []dto.ContainerOptions\n\tListByImage(imageName string) []dto.ContainerOptions\n\tLoadStatus() (dto.ContainerStatus, error)\n\tPageNetwork(req dto.SearchWithPage) (int64, interface{}, error)\n\tListNetwork() ([]dto.Options, error)\n\tPageVolume(req dto.SearchWithPage) (int64, interface{}, error)\n\tListVolume() ([]dto.Options, error)\n\n\tPageCompose(req dto.SearchWithPage) (int64, interface{}, error)\n\tLoadComposeEnv(name string) (string, error)\n\tCreateCompose(req dto.ComposeCreate) error\n\tComposeOperation(req dto.ComposeOperation) error\n\tTestCompose(req dto.ComposeCreate) (bool, error)\n\tComposeUpdate(req dto.ComposeUpdate) error\n\tComposeLogClean(req dto.ComposeLogClean) error\n\n\tContainerCreate(req dto.ContainerOperate, inThread bool) error\n\tContainerUpdate(req dto.ContainerOperate) error\n\tContainerUpgrade(req dto.ContainerUpgrade) error\n\tContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)\n\tContainerListStats() ([]dto.ContainerListStats, error)\n\tContainerItemStats(req dto.OperationWithName) (dto.ContainerItemStats, error)\n\tLoadResourceLimit() (*dto.ResourceLimit, error)\n\tContainerRename(req dto.ContainerRename) error\n\tContainerCommit(req dto.ContainerCommit) error\n\tContainerLogClean(req dto.OperationWithName) error\n\tContainerOperation(req dto.ContainerOperation) error\n\tDownloadContainerLogs(containerType, container, since, tail string, timestamp bool, c *gin.Context) error\n\tContainerStats(id string) (*dto.ContainerStats, error)\n\n\tInspect(req dto.InspectReq) (string, error)\n\tDeleteNetwork(req dto.BatchDelete) error\n\tCreateNetwork(req dto.NetworkCreate) error\n\tDeleteVolume(req dto.BatchDelete) error\n\tCreateVolume(req dto.VolumeCreate) error\n\tPrune(req dto.ContainerPrune) error\n\n\tLoadUsers(req dto.OperationWithName) []string\n\tListContainerFiles(req dto.ContainerFileReq) ([]dto.ContainerFileInfo, error)\n\tUploadContainerFile(req dto.ContainerFileReq, fileName string, fileSize int64, file io.Reader) error\n\tGetContainerFileContent(req dto.ContainerFileReq) (*dto.ContainerFileContent, error)\n\tGetContainerFileSize(req dto.ContainerFileReq) (int64, error)\n\tDeleteContainerFile(req dto.ContainerFileBatchDeleteReq) error\n\tDownloadContainerFile(req dto.ContainerFileReq) (io.ReadCloser, string, string, error)\n\n\tStreamLogs(ctx *gin.Context, params dto.StreamLog)\n}\n\nfunc NewIContainerService() IContainerService {\n\treturn &ContainerService{}\n}\n\nfunc (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer func() { _ = client.Close() }()\n\toptions := container.ListOptions{All: true}\n\tif len(req.Filters) != 0 {\n\t\toptions.Filters = filters.NewArgs()\n\t\toptions.Filters.Add(\"label\", req.Filters)\n\t}\n\tcontainers, err := client.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\trecords := searchWithFilter(req, containers)\n\n\tvar backData []dto.ContainerInfo\n\ttotal, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\tbackData = make([]dto.ContainerInfo, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\tbackData = records[start:end]\n\t}\n\n\tfor i := 0; i < len(backData); i++ {\n\t\tinstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(backData[i].Name))\n\t\tif install.ID > 0 {\n\t\t\tbackData[i].AppInstallName = install.Name\n\t\t\tbackData[i].AppName = install.App.Name\n\t\t\twebsites, _ := websiteRepo.GetBy(websiteRepo.WithAppInstallId(install.ID))\n\t\t\tfor _, website := range websites {\n\t\t\t\tbackData[i].Websites = append(backData[i].Websites, website.PrimaryDomain)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn int64(total), backData, nil\n}\n\nfunc (u *ContainerService) List() []dto.ContainerOptions {\n\tvar options []dto.ContainerOptions\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load docker client for contianer list failed, err: %v\", err)\n\t\treturn nil\n\t}\n\tdefer client.Close()\n\tcontainers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load container list failed, err: %v\", err)\n\t\treturn nil\n\t}\n\tfor _, container := range containers {\n\t\tfor _, name := range container.Names {\n\t\t\tif len(name) != 0 {\n\t\t\t\toptions = append(options, dto.ContainerOptions{Name: strings.TrimPrefix(name, \"/\"), State: container.State})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn options\n}\n\nfunc (u *ContainerService) ListByImage(imageName string) []dto.ContainerOptions {\n\tvar options []dto.ContainerOptions\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load docker client for contianer list failed, err: %v\", err)\n\t\treturn nil\n\t}\n\tdefer client.Close()\n\tcontainers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load container list failed, err: %v\", err)\n\t\treturn nil\n\t}\n\tfor _, container := range containers {\n\t\tif container.Image != imageName {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, name := range container.Names {\n\t\t\tif len(name) != 0 {\n\t\t\t\toptions = append(options, dto.ContainerOptions{Name: strings.TrimPrefix(name, \"/\"), State: container.State})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn options\n}\n\nfunc (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {\n\tvar data dto.ContainerStatus\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tdefer client.Close()\n\tc := context.Background()\n\n\timages, _ := client.ImageList(c, image.ListOptions{All: true})\n\tdata.ImageCount = len(images)\n\trepo, _ := imageRepoRepo.List()\n\tdata.RepoCount = len(repo)\n\ttemplates, _ := composeRepo.List()\n\tdata.ComposeTemplateCount = len(templates)\n\tnetworks, _ := client.NetworkList(c, network.ListOptions{})\n\tdata.NetworkCount = len(networks)\n\tvolumes, _ := client.VolumeList(c, volume.ListOptions{})\n\tdata.VolumeCount = len(volumes.Volumes)\n\tdata.ComposeCount = loadComposeCount(client)\n\tcontainers, _ := client.ContainerList(c, container.ListOptions{All: true})\n\tdata.ContainerCount = len(containers)\n\tfor _, item := range containers {\n\t\tswitch item.State {\n\t\tcase \"created\":\n\t\t\tdata.Created++\n\t\tcase \"running\":\n\t\t\tdata.Running++\n\t\tcase \"paused\":\n\t\t\tdata.Paused++\n\t\tcase \"restarting\":\n\t\t\tdata.Restarting++\n\t\tcase \"dead\":\n\t\t\tdata.Dead++\n\t\tcase \"exited\":\n\t\t\tdata.Exited++\n\t\tcase \"removing\":\n\t\t\tdata.Removing++\n\t\t}\n\t}\n\treturn data, nil\n}\nfunc (u *ContainerService) ContainerItemStats(req dto.OperationWithName) (dto.ContainerItemStats, error) {\n\tvar data dto.ContainerItemStats\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tif req.Name != \"system\" {\n\t\tdefer client.Close()\n\t\tcontainerInfo, _, err := client.ContainerInspectWithRaw(context.Background(), req.Name, true)\n\t\tif err != nil {\n\t\t\treturn data, err\n\t\t}\n\t\tdata.SizeRw = *containerInfo.SizeRw\n\t\tdata.SizeRootFs = *containerInfo.SizeRootFs\n\t\treturn data, nil\n\t}\n\n\tusage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tfor _, item := range usage.Images {\n\t\tdata.ImageUsage += item.Size\n\t\tif item.Containers < 1 {\n\t\t\tdata.ImageReclaimable += item.Size\n\t\t}\n\t}\n\tfor _, item := range usage.Containers {\n\t\tdata.ContainerUsage += item.SizeRw\n\t\tif item.State != \"running\" {\n\t\t\tdata.ContainerReclaimable += item.SizeRw\n\t\t}\n\t}\n\tfor _, item := range usage.Volumes {\n\t\tdata.VolumeUsage += item.UsageData.Size\n\t\tif item.UsageData.RefCount == 0 {\n\t\t\tdata.VolumeReclaimable += item.UsageData.Size\n\t\t}\n\t}\n\tfor _, item := range usage.BuildCache {\n\t\tdata.BuildCacheUsage += item.Size\n\t}\n\treturn data, nil\n}\nfunc (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\tlist, err := client.ContainerList(context.Background(), container.ListOptions{All: true})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []dto.ContainerListStats\n\tvar wg sync.WaitGroup\n\twg.Add(len(list))\n\tfor i := 0; i < len(list); i++ {\n\t\tgo func(item container.Summary) {\n\t\t\tdatas = append(datas, loadCpuAndMem(client, item.ID))\n\t\t\twg.Done()\n\t\t}(list[i])\n\t}\n\twg.Wait()\n\treturn datas, nil\n}\n\nfunc (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer client.Close()\n\tvar inspectInfo interface{}\n\tswitch req.Type {\n\tcase \"container\":\n\t\tinspectInfo, err = client.ContainerInspect(context.Background(), req.ID)\n\tcase \"compose\":\n\t\tfilePath := \"\"\n\t\tcli, err := docker.NewDockerClient()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tdefer cli.Close()\n\t\toptions := container.ListOptions{All: true}\n\t\toptions.Filters = filters.NewArgs()\n\t\toptions.Filters.Add(\"label\", fmt.Sprintf(\"%s=%s\", composeProjectLabel, req.ID))\n\t\tcontainers, err := cli.ContainerList(context.Background(), options)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tfor _, container := range containers {\n\t\t\tconfig := container.Labels[composeConfigLabel]\n\t\t\tif len(req.Detail) != 0 && strings.Contains(config, req.Detail) {\n\t\t\t\tconfig = req.Detail\n\t\t\t}\n\t\t\tworkdir := container.Labels[composeWorkdirLabel]\n\t\t\tif len(config) != 0 && len(workdir) != 0 && strings.Contains(config, workdir) {\n\t\t\t\tfilePath = config\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\tfilePath = workdir\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif len(containers) == 0 {\n\t\t\tcomposeItem, _ := composeRepo.GetRecord(repo.WithByName(req.ID))\n\t\t\tfilePath = composeItem.Path\n\t\t}\n\t\tif _, err := os.Stat(filePath); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tcontent, err := os.ReadFile(filePath)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn string(content), nil\n\tcase \"image\":\n\t\tinspectInfo, _, err = client.ImageInspectWithRaw(context.Background(), req.ID)\n\tcase \"network\":\n\t\tinspectInfo, err = client.NetworkInspect(context.TODO(), req.ID, network.InspectOptions{})\n\tcase \"volume\":\n\t\tinspectInfo, err = client.VolumeInspect(context.TODO(), req.ID)\n\t}\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tbytes, err := json.Marshal(inspectInfo)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(bytes), nil\n}\n\nfunc (u *ContainerService) Prune(req dto.ContainerPrune) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tname := \"\"\n\tswitch req.PruneType {\n\tcase \"container\":\n\t\tname = \"Container\"\n\tcase \"image\":\n\t\tname = \"Image\"\n\tcase \"volume\":\n\t\tname = \"Volume\"\n\tcase \"buildcache\":\n\t\tname = \"BuildCache\"\n\tcase \"network\":\n\t\tname = \"Network\"\n\t}\n\ttaskItem, err := task.NewTaskWithOps(i18n.GetMsgByKey(name), task.TaskClean, task.TaskScopeContainer, req.TaskID, 1)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create container failed, err: %v\", err)\n\t\treturn err\n\t}\n\n\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"TaskClean\"), func(t *task.Task) error {\n\t\tpruneFilters := filters.NewArgs()\n\t\tif req.WithTagAll {\n\t\t\tpruneFilters.Add(\"dangling\", \"false\")\n\t\t\tif req.PruneType != \"image\" {\n\t\t\t\tpruneFilters.Add(\"until\", \"24h\")\n\t\t\t}\n\t\t}\n\t\ttaskItem.Log(i18n.GetMsgByKey(\"PruneStart\"))\n\t\tSpaceReclaimed := 0\n\t\tswitch req.PruneType {\n\t\tcase \"container\":\n\t\t\trep, err := client.ContainersPrune(context.Background(), pruneFilters)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tSpaceReclaimed = int(rep.SpaceReclaimed)\n\t\tcase \"image\":\n\t\t\trep, err := client.ImagesPrune(context.Background(), pruneFilters)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tSpaceReclaimed = int(rep.SpaceReclaimed)\n\t\tcase \"network\":\n\t\t\t_, err := client.NetworksPrune(context.Background(), pruneFilters)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase \"volume\":\n\t\t\tversions, err := client.ServerVersion(context.Background())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif common.ComparePanelVersion(versions.APIVersion, \"1.42\") {\n\t\t\t\tpruneFilters.Add(\"all\", \"true\")\n\t\t\t}\n\t\t\trep, err := client.VolumesPrune(context.Background(), pruneFilters)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tSpaceReclaimed = int(rep.SpaceReclaimed)\n\t\tcase \"buildcache\":\n\t\t\topts := build.CachePruneOptions{}\n\t\t\topts.All = true\n\t\t\trep, err := client.BuildCachePrune(context.Background(), opts)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tSpaceReclaimed = int(rep.SpaceReclaimed)\n\t\t}\n\t\ttaskItem.Log(i18n.GetMsgWithMap(\"PruneHelper\", map[string]interface{}{\"name\": i18n.GetMsgByKey(name), \"size\": common.LoadSizeUnit2F(float64(SpaceReclaimed))}))\n\t\treturn nil\n\t}, nil)\n\tgo func() {\n\t\t_ = taskItem.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {\n\tcpuCounts, err := cpu.Counts(true)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load cpu limit failed, err: %v\", err)\n\t}\n\tmemoryInfo, err := mem.VirtualMemory()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load memory limit failed, err: %v\", err)\n\t}\n\n\tdata := dto.ResourceLimit{\n\t\tCPU:    cpuCounts,\n\t\tMemory: memoryInfo.Total,\n\t}\n\treturn &data, nil\n}\n\nfunc (u *ContainerService) ContainerCreate(req dto.ContainerOperate, inThread bool) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tctx := context.Background()\n\tnewContainer, _ := client.ContainerInspect(ctx, req.Name)\n\tif newContainer.ContainerJSONBase != nil {\n\t\treturn buserr.New(\"ErrContainerName\")\n\t}\n\n\ttaskItem, err := task.NewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeContainer, req.TaskID, 1)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create container failed, err: %v\", err)\n\t\treturn err\n\t}\n\n\ttaskItem.AddSubTask(i18n.GetWithName(\"ContainerImagePull\", req.Image), func(t *task.Task) error {\n\t\tif !checkImageExist(client, req.Image) || req.ForcePull {\n\t\t\tif err := pullImages(taskItem, client, req.Image); err != nil {\n\t\t\t\tif !req.ForcePull {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}, nil)\n\n\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"ContainerImageCheck\"), func(t *task.Task) error {\n\t\timageInfo, _, err := client.ImageInspectWithRaw(ctx, req.Image)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(req.Entrypoint) == 0 {\n\t\t\treq.Entrypoint = imageInfo.Config.Entrypoint\n\t\t}\n\t\tif len(req.Cmd) == 0 {\n\t\t\treq.Cmd = imageInfo.Config.Cmd\n\t\t}\n\t\treturn nil\n\t}, nil)\n\n\ttaskItem.AddSubTask(i18n.GetWithName(\"ContainerCreate\", req.Name), func(t *task.Task) error {\n\t\tconfig, hostConf, networkConf, err := loadConfigInfo(true, req, nil)\n\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"ContainerLoadInfo\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcon, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)\n\t\tif err != nil {\n\t\t\ttaskItem.Log(i18n.GetMsgByKey(\"ContainerCreateFailed\"))\n\t\t\t_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})\n\t\t\treturn err\n\t\t}\n\t\terr = client.ContainerStart(ctx, con.ID, container.StartOptions{})\n\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"ContainerStartCheck\"), err)\n\t\tif err != nil {\n\t\t\ttaskItem.Log(i18n.GetMsgByKey(\"ContainerCreateFailed\"))\n\t\t\t_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})\n\t\t\treturn fmt.Errorf(\"create successful but start failed, err: %v\", err)\n\t\t}\n\t\treturn nil\n\t}, nil)\n\n\tif inThread {\n\t\tgo func() {\n\t\t\tif err := taskItem.Execute(); err != nil {\n\t\t\t\tglobal.LOG.Error(err.Error())\n\t\t\t}\n\t\t}()\n\t\treturn nil\n\t}\n\treturn taskItem.Execute()\n}\n\nfunc (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\tctx := context.Background()\n\toldContainer, err := client.ContainerInspect(ctx, req.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar data dto.ContainerOperate\n\tdata.Name = strings.ReplaceAll(oldContainer.Name, \"/\", \"\")\n\tdata.Image = oldContainer.Config.Image\n\tif oldContainer.NetworkSettings != nil {\n\t\tfor net, val := range oldContainer.NetworkSettings.Networks {\n\t\t\tnetItem := dto.ContainerNetwork{\n\t\t\t\tNetwork: net,\n\t\t\t\tMacAddr: val.MacAddress,\n\t\t\t}\n\t\t\tif val.IPAMConfig != nil {\n\t\t\t\tif netItem.Network != \"bridge\" {\n\t\t\t\t\tnetItem.Ipv4 = val.IPAMConfig.IPv4Address\n\t\t\t\t\tnetItem.Ipv6 = val.IPAMConfig.IPv6Address\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif netItem.Network != \"bridge\" {\n\t\t\t\t\tnetItem.Ipv4 = val.IPAddress\n\t\t\t\t}\n\t\t\t}\n\t\t\tdata.Networks = append(data.Networks, netItem)\n\t\t}\n\t}\n\n\texposePorts, _ := loadPortByInspect(oldContainer.ID, client)\n\tdata.ExposedPorts = loadContainerPortForInfo(exposePorts)\n\tdata.Hostname = oldContainer.Config.Hostname\n\tdata.DNS = oldContainer.HostConfig.DNS\n\tfor _, item := range oldContainer.HostConfig.ExtraHosts {\n\t\tparts := strings.SplitN(item, \":\", 2)\n\t\tif len(parts) != 2 || len(parts[0]) == 0 || len(parts[1]) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tdata.ExtraHosts = append(data.ExtraHosts, dto.ExtraHost{\n\t\t\tHostname: parts[0],\n\t\t\tIP:       parts[1],\n\t\t})\n\t}\n\tdata.DomainName = oldContainer.Config.Domainname\n\n\tdata.Cmd = oldContainer.Config.Cmd\n\tdata.WorkingDir = oldContainer.Config.WorkingDir\n\tdata.User = oldContainer.Config.User\n\tdata.OpenStdin = oldContainer.Config.OpenStdin\n\tdata.Tty = oldContainer.Config.Tty\n\tdata.Entrypoint = oldContainer.Config.Entrypoint\n\tdata.Env = oldContainer.Config.Env\n\tdata.CPUShares = oldContainer.HostConfig.CPUShares\n\tfor key, val := range oldContainer.Config.Labels {\n\t\tdata.Labels = append(data.Labels, fmt.Sprintf(\"%s=%s\", key, val))\n\t}\n\tdata.AutoRemove = oldContainer.HostConfig.AutoRemove\n\tdata.Privileged = oldContainer.HostConfig.Privileged\n\tdata.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts\n\tdata.RestartPolicy = string(oldContainer.HostConfig.RestartPolicy.Name)\n\tif oldContainer.HostConfig.NanoCPUs != 0 {\n\t\tdata.NanoCPUs = float64(oldContainer.HostConfig.NanoCPUs) / 1000000000\n\t}\n\tif oldContainer.HostConfig.Memory != 0 {\n\t\tdata.Memory = float64(oldContainer.HostConfig.Memory) / 1024 / 1024\n\t}\n\tdata.Volumes = loadVolumeBinds(oldContainer.Mounts)\n\n\treturn &data, nil\n}\n\nfunc (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tctx := context.Background()\n\toldContainer, err := client.ContainerInspect(ctx, req.Name)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttaskItem, err := task.NewTaskWithOps(req.Name, task.TaskUpdate, task.TaskScopeContainer, req.TaskID, 1)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create container failed, err: %v\", err)\n\t\treturn err\n\t}\n\tgo func() {\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"ContainerImagePull\", req.Image), func(t *task.Task) error {\n\t\t\tif !checkImageExist(client, req.Image) || req.ForcePull {\n\t\t\t\tif err := pullImages(taskItem, client, req.Image); err != nil {\n\t\t\t\t\tif !req.ForcePull {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\treturn fmt.Errorf(\"pull image %s failed, err: %v\", req.Image, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"ContainerCreate\", req.Name), func(t *task.Task) error {\n\t\t\terr := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true})\n\t\t\ttaskItem.LogWithStatus(i18n.GetWithName(\"ContainerRemoveOld\", req.Name), err)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tconfig, hostConf, networkConf, err := loadConfigInfo(false, req, &oldContainer)\n\t\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"ContainerLoadInfo\"), err)\n\t\t\tif err != nil {\n\t\t\t\ttaskItem.Log(i18n.GetMsgByKey(\"ContainerRecreate\"))\n\t\t\t\treCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tcon, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)\n\t\t\tif err != nil {\n\t\t\t\ttaskItem.Log(i18n.GetMsgByKey(\"ContainerRecreate\"))\n\t\t\t\treCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)\n\t\t\t\treturn fmt.Errorf(\"update container failed, err: %v\", err)\n\t\t\t}\n\t\t\terr = client.ContainerStart(ctx, con.ID, container.StartOptions{})\n\t\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"ContainerStartCheck\"), err)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"update successful but start failed, err: %v\", err)\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\n\t\tif err := taskItem.Execute(); err != nil {\n\t\t\tglobal.LOG.Error(err.Error())\n\t\t}\n\t}()\n\n\treturn nil\n}\n\nfunc (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tctx := context.Background()\n\ttaskItem, err := task.NewTaskWithOps(req.Image, task.TaskUpgrade, task.TaskScopeImage, req.TaskID, 1)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create container failed, err: %v\", err)\n\t\treturn err\n\t}\n\tgo func() {\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"ContainerImagePull\", req.Image), func(t *task.Task) error {\n\t\t\ttaskItem.LogStart(i18n.GetWithName(\"ContainerImagePull\", req.Image))\n\t\t\tif !checkImageExist(client, req.Image) || req.ForcePull {\n\t\t\t\tif err := pullImages(taskItem, client, req.Image); err != nil {\n\t\t\t\t\tif !req.ForcePull {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\treturn fmt.Errorf(\"pull image %s failed, err: %v\", req.Image, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\t\tfor _, item := range req.Names {\n\t\t\tvar oldContainer container.InspectResponse\n\t\t\ttaskItem.AddSubTask(i18n.GetWithName(\"ContainerLoadInfo\", item), func(t *task.Task) error {\n\t\t\t\ttaskItem.Logf(\"----------------- %s -----------------\", item)\n\t\t\t\toldContainer, err = client.ContainerInspect(ctx, item)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}, nil)\n\n\t\t\ttaskItem.AddSubTask(i18n.GetWithName(\"ContainerCreate\", item), func(t *task.Task) error {\n\t\t\t\tconfig := oldContainer.Config\n\t\t\t\tconfig.Image = req.Image\n\t\t\t\thostConf := oldContainer.HostConfig\n\t\t\t\tvar networkConf network.NetworkingConfig\n\t\t\t\tif oldContainer.NetworkSettings != nil {\n\t\t\t\t\tfor networkKey := range oldContainer.NetworkSettings.Networks {\n\t\t\t\t\t\tnetworkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\terr := client.ContainerRemove(ctx, item, container.RemoveOptions{Force: true})\n\t\t\t\ttaskItem.LogWithStatus(i18n.GetWithName(\"ContainerRemoveOld\", item), err)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tcon, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, item)\n\t\t\t\tif err != nil {\n\t\t\t\t\ttaskItem.Log(i18n.GetMsgByKey(\"ContainerRecreate\"))\n\t\t\t\t\treCreateAfterUpdate(item, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)\n\t\t\t\t\treturn fmt.Errorf(\"upgrade container failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\terr = client.ContainerStart(ctx, con.ID, container.StartOptions{})\n\t\t\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"ContainerStartCheck\"), err)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"upgrade successful but start failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}, nil)\n\n\t\t}\n\t\tif err := taskItem.Execute(); err != nil {\n\t\t\tglobal.LOG.Error(err.Error())\n\t\t}\n\t}()\n\n\treturn nil\n}\n\nfunc (u *ContainerService) ContainerRename(req dto.ContainerRename) error {\n\tctx := context.Background()\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\n\tnewContainer, _ := client.ContainerInspect(ctx, req.NewName)\n\tif newContainer.ContainerJSONBase != nil {\n\t\treturn buserr.New(\"ErrContainerName\")\n\t}\n\treturn client.ContainerRename(ctx, req.Name, req.NewName)\n}\n\nfunc (u *ContainerService) ContainerCommit(req dto.ContainerCommit) error {\n\tctx := context.Background()\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\toptions := container.CommitOptions{\n\t\tReference: req.NewImageName,\n\t\tComment:   req.Comment,\n\t\tAuthor:    req.Author,\n\t\tChanges:   nil,\n\t\tPause:     req.Pause,\n\t\tConfig:    nil,\n\t}\n\n\ttaskItem, err := task.NewTaskWithOps(req.NewImageName, task.TaskCommit, task.TaskScopeContainer, req.TaskID, 1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for container commit failed, err: %v\", err)\n\t}\n\n\tgo func() {\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"TaskCommit\", req.NewImageName), func(t *task.Task) error {\n\t\t\tres, err := client.ContainerCommit(ctx, req.ContainerId, options)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to commit container, err: %v\", err)\n\t\t\t}\n\t\t\ttaskItem.Log(res.ID)\n\t\t\treturn nil\n\t\t}, nil)\n\t\t_ = taskItem.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error {\n\tvar err error\n\tctx := context.Background()\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\ttaskItem, err := task.NewTaskWithOps(strings.Join(req.Names, \" \"), req.Operation, task.TaskScopeContainer, req.TaskID, 1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for container commit failed, err: %v\", err)\n\t}\n\n\tfor _, item := range req.Names {\n\t\ttaskItem.AddSubTask(item, func(t *task.Task) error {\n\t\t\tswitch req.Operation {\n\t\t\tcase constant.ContainerOpStart:\n\t\t\t\terr = client.ContainerStart(ctx, item, container.StartOptions{})\n\t\t\tcase constant.ContainerOpStop:\n\t\t\t\terr = client.ContainerStop(ctx, item, container.StopOptions{})\n\t\t\tcase constant.ContainerOpRestart:\n\t\t\t\terr = client.ContainerRestart(ctx, item, container.StopOptions{})\n\t\t\tcase constant.ContainerOpKill:\n\t\t\t\terr = client.ContainerKill(ctx, item, \"SIGKILL\")\n\t\t\tcase constant.ContainerOpPause:\n\t\t\t\terr = client.ContainerPause(ctx, item)\n\t\t\tcase constant.ContainerOpUnpause:\n\t\t\t\terr = client.ContainerUnpause(ctx, item)\n\t\t\tcase constant.ContainerOpRemove:\n\t\t\t\terr = client.ContainerRemove(ctx, item, container.RemoveOptions{RemoveVolumes: true, Force: true})\n\t\t\t}\n\t\t\treturn err\n\t\t}, nil)\n\t}\n\n\tgo func() {\n\t\t_ = taskItem.Execute()\n\t}()\n\treturn err\n}\n\nfunc (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tctx := context.Background()\n\tcontainerItem, err := client.ContainerInspect(ctx, req.Name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil {\n\t\treturn err\n\t}\n\tfile, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, constant.FilePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\tif err = file.Truncate(0); err != nil {\n\t\treturn err\n\t}\n\t_, _ = file.Seek(0, 0)\n\n\tfiles, _ := filepath.Glob(fmt.Sprintf(\"%s.*\", containerItem.LogPath))\n\tfor _, file := range files {\n\t\t_ = os.Remove(file)\n\t}\n\n\tif err := client.ContainerStart(ctx, containerItem.ID, container.StartOptions{}); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *ContainerService) StreamLogs(ctx *gin.Context, params dto.StreamLog) {\n\tmessageChan := make(chan string, 1024)\n\terrorChan := make(chan error, 1)\n\tdoneChan := make(chan struct{})\n\n\tgo func() {\n\t\t<-ctx.Request.Context().Done()\n\t\tclose(doneChan)\n\t}()\n\tgo collectLogs(doneChan, params, messageChan, errorChan)\n\tctx.Stream(func(w io.Writer) bool {\n\t\tselect {\n\t\tcase msg, ok := <-messageChan:\n\t\t\tif !ok {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t_, err := fmt.Fprintf(w, \"data: %s\\n\\n\", msg)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\tcase err := <-errorChan:\n\t\t\tif err != nil {\n\t\t\t\t_, _ = fmt.Fprintf(w, \"event: error\\ndata: %v\\n\\n\", err.Error())\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t})\n}\n\nfunc collectLogs(done <-chan struct{}, params dto.StreamLog, messageChan chan<- string, errorChan chan<- error) {\n\tdefer close(messageChan)\n\tdefer close(errorChan)\n\tvar cmdArgs []string\n\tcmdArgs = append(cmdArgs, \"logs\")\n\tif params.Follow {\n\t\tcmdArgs = append(cmdArgs, \"-f\")\n\t}\n\tif params.Timestamp {\n\t\tcmdArgs = append(cmdArgs, \"-t\")\n\t}\n\tif params.Tail != \"0\" {\n\t\tcmdArgs = append(cmdArgs, \"--tail\", params.Tail)\n\t}\n\tif params.Since != \"all\" {\n\t\tcmdArgs = append(cmdArgs, \"--since\", params.Since)\n\t}\n\tif params.Container != \"\" {\n\t\tcmdArgs = append(cmdArgs, params.Container)\n\t}\n\n\tvar dockerCmd *exec.Cmd\n\tif params.Type == \"compose\" {\n\t\tdockerComposeCmd := common.GetDockerComposeCommand()\n\t\tvar yamlFiles []string\n\t\tfor _, item := range strings.Split(params.Compose, \",\") {\n\t\t\tif len(item) != 0 {\n\t\t\t\tyamlFiles = append(yamlFiles, \"-f\", item)\n\t\t\t}\n\t\t}\n\t\tif dockerComposeCmd == \"docker-compose\" {\n\t\t\tnewCmdArgs := append(yamlFiles, cmdArgs...)\n\t\t\tdockerCmd = exec.Command(dockerComposeCmd, newCmdArgs...)\n\t\t} else {\n\t\t\tnewCmdArgs := append(append([]string{\"compose\"}, yamlFiles...), cmdArgs...)\n\t\t\tdockerCmd = exec.Command(\"docker\", newCmdArgs...)\n\t\t}\n\t} else {\n\t\tdockerCmd = exec.Command(\"docker\", cmdArgs...)\n\t}\n\n\tdockerCmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}\n\n\tstdout, err := dockerCmd.StdoutPipe()\n\tif err != nil {\n\t\terrorChan <- fmt.Errorf(\"failed to get stdout pipe: %v\", err)\n\t\treturn\n\t}\n\n\tdockerCmd.Stderr = dockerCmd.Stdout\n\n\tif err = dockerCmd.Start(); err != nil {\n\t\terrorChan <- fmt.Errorf(\"failed to start docker logs command: %v\", err)\n\t\treturn\n\t}\n\n\tdefer func() {\n\t\tif dockerCmd.Process != nil {\n\t\t\tif pgid, err := syscall.Getpgid(dockerCmd.Process.Pid); err == nil {\n\t\t\t\t_ = syscall.Kill(-pgid, syscall.SIGKILL)\n\t\t\t}\n\t\t\t_ = dockerCmd.Process.Kill()\n\t\t\t_ = dockerCmd.Wait()\n\t\t}\n\t}()\n\n\treader := bufio.NewReader(stdout)\n\n\tprocessKilled := false\n\tgo func() {\n\t\t<-done\n\t\tif !processKilled && dockerCmd.Process != nil {\n\t\t\tprocessKilled = true\n\t\t\tif pgid, err := syscall.Getpgid(dockerCmd.Process.Pid); err == nil {\n\t\t\t\t_ = syscall.Kill(-pgid, syscall.SIGKILL)\n\t\t\t}\n\t\t\t_ = dockerCmd.Process.Kill()\n\t\t}\n\t}()\n\n\tfor {\n\t\tline, err := reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tif len(line) > 0 {\n\t\t\t\t\tline = strings.TrimSuffix(line, \"\\n\")\n\t\t\t\t\tselect {\n\t\t\t\t\tcase messageChan <- line:\n\t\t\t\t\tcase <-done:\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\terrorChan <- fmt.Errorf(\"reader error: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tline = strings.TrimSuffix(line, \"\\n\")\n\t\tselect {\n\t\tcase messageChan <- line:\n\t\tcase <-done:\n\t\t\treturn\n\t\t}\n\t}\n\t_ = dockerCmd.Wait()\n}\n\nfunc (u *ContainerService) DownloadContainerLogs(containerType, container, since, tail string, timestamp bool, c *gin.Context) error {\n\tif cmd.CheckIllegal(container, since, tail) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcommandArg := []string{\"logs\", container}\n\tdockerCommand := global.CONF.DockerConfig.Command\n\tif containerType == \"compose\" {\n\t\tvar yamlFiles []string\n\t\tfor _, item := range strings.Split(container, \",\") {\n\t\t\tif len(item) != 0 {\n\t\t\t\tyamlFiles = append(yamlFiles, \"-f\", item)\n\t\t\t}\n\t\t}\n\t\tif dockerCommand == \"docker-compose\" {\n\t\t\tcommandArg = append(yamlFiles, \"logs\")\n\t\t} else {\n\t\t\tcommandArg = append(append([]string{\"compose\"}, yamlFiles...), \"logs\")\n\t\t}\n\t}\n\n\tif tail != \"0\" {\n\t\tcommandArg = append(commandArg, \"--tail\")\n\t\tcommandArg = append(commandArg, tail)\n\t}\n\tif since != \"all\" {\n\t\tcommandArg = append(commandArg, \"--since\")\n\t\tcommandArg = append(commandArg, since)\n\t}\n\tif timestamp {\n\t\tcommandArg = append(commandArg, \"-t\")\n\t}\n\tvar dockerCmd *exec.Cmd\n\tif containerType == \"compose\" && dockerCommand == \"docker-compose\" {\n\t\tdockerCmd = exec.Command(\"docker-compose\", commandArg...)\n\t} else {\n\t\tdockerCmd = exec.Command(\"docker\", commandArg...)\n\t}\n\tstdout, err := dockerCmd.StdoutPipe()\n\tif err != nil {\n\t\t_ = dockerCmd.Process.Signal(syscall.SIGTERM)\n\t\treturn err\n\t}\n\tdockerCmd.Stderr = dockerCmd.Stdout\n\tif err := dockerCmd.Start(); err != nil {\n\t\t_ = dockerCmd.Process.Signal(syscall.SIGTERM)\n\t\treturn err\n\t}\n\n\ttempFile, err := os.CreateTemp(\"\", \"cmd_output_*.txt\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer tempFile.Close()\n\tdefer func() {\n\t\tif err := os.Remove(tempFile.Name()); err != nil {\n\t\t\tglobal.LOG.Errorf(\"os.Remove() failed: %v\", err)\n\t\t}\n\t}()\n\terrCh := make(chan error)\n\tgo func() {\n\t\tscanner := bufio.NewScanner(stdout)\n\t\tvar ansiRegex = re.GetRegex(re.AnsiEscapePattern)\n\t\tfor scanner.Scan() {\n\t\t\tline := scanner.Text()\n\t\t\tcleanLine := ansiRegex.ReplaceAllString(line, \"\")\n\t\t\tif _, err := tempFile.WriteString(cleanLine + \"\\n\"); err != nil {\n\t\t\t\terrCh <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif err := scanner.Err(); err != nil {\n\t\t\terrCh <- err\n\t\t\treturn\n\t\t}\n\t\terrCh <- nil\n\t}()\n\tselect {\n\tcase err := <-errCh:\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"Error: %v\", err)\n\t\t}\n\tcase <-time.After(3 * time.Second):\n\t\tglobal.LOG.Errorf(\"Timeout reached\")\n\t}\n\tinfo, _ := tempFile.Stat()\n\n\tc.Header(\"Content-Length\", strconv.FormatInt(info.Size(), 10))\n\tc.Header(\"Content-Disposition\", \"attachment; filename*=utf-8''\"+url.PathEscape(info.Name()))\n\thttp.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), tempFile)\n\treturn nil\n}\n\nfunc (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\tres, err := client.ContainerStats(context.TODO(), id, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer res.Body.Close()\n\n\tbody, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar stats *container.StatsResponse\n\tif err := json.Unmarshal(body, &stats); err != nil {\n\t\treturn nil, err\n\t}\n\tvar data dto.ContainerStats\n\tdata.CPUPercent = calculateCPUPercentUnix(stats)\n\tdata.IORead, data.IOWrite = calculateBlockIO(stats.BlkioStats)\n\tdata.Memory = float64(stats.MemoryStats.Usage) / 1024 / 1024\n\tif cache, ok := stats.MemoryStats.Stats[\"cache\"]; ok {\n\t\tdata.Cache = float64(cache) / 1024 / 1024\n\t}\n\tdata.NetworkRX, data.NetworkTX = calculateNetwork(stats.Networks)\n\tdata.ShotTime = stats.Read\n\treturn &data, nil\n}\n\nfunc (u *ContainerService) LoadUsers(req dto.OperationWithName) []string {\n\tvar users []string\n\tstd, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout(\"docker\", \"exec\", req.Name, \"cat\", \"/etc/passwd\")\n\tif err != nil {\n\t\treturn users\n\t}\n\tlines := strings.Split(string(std), \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.Contains(line, \":\") {\n\t\t\tusers = append(users, strings.Split(line, \":\")[0])\n\t\t}\n\t}\n\treturn users\n}\n\nfunc (u *ContainerService) ListContainerFiles(req dto.ContainerFileReq) ([]dto.ContainerFileInfo, error) {\n\tif len(req.Path) == 0 {\n\t\treq.Path = \"/\"\n\t}\n\tcli, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cli.Close()\n\n\tctx := context.Background()\n\tstat, err := cli.ContainerStatPath(ctx, req.ContainerID, req.Path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tisDir := stat.Mode.IsDir()\n\tisLink := stat.Mode&os.ModeSymlink != 0\n\tif isLink && !isDir {\n\t\tlinkDir, linkErr := isContainerDir(cli, req.ContainerID, req.Path)\n\t\tif linkErr == nil {\n\t\t\tisDir = linkDir\n\t\t}\n\t}\n\tif !isDir {\n\t\treturn []dto.ContainerFileInfo{toContainerFileInfo(req.Path, stat, isDir)}, nil\n\t}\n\n\toutput, err := runContainerCommand(cli, req.ContainerID, []string{\"ls\", \"-1A\", \"--\", req.Path})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlines := strings.Split(strings.TrimSpace(output), \"\\n\")\n\tfiles := make([]dto.ContainerFileInfo, 0, len(lines))\n\tfor _, line := range lines {\n\t\tname := strings.TrimSpace(line)\n\t\tif len(name) == 0 || name == \".\" || name == \"..\" {\n\t\t\tcontinue\n\t\t}\n\t\tchildPath := req.Path\n\t\tif childPath == \"/\" {\n\t\t\tchildPath = \"/\" + name\n\t\t} else {\n\t\t\tchildPath = strings.TrimSuffix(childPath, \"/\") + \"/\" + name\n\t\t}\n\t\tchildStat, statErr := cli.ContainerStatPath(ctx, req.ContainerID, childPath)\n\t\tif statErr != nil {\n\t\t\tcontinue\n\t\t}\n\t\tchildIsDir := childStat.Mode.IsDir()\n\t\tif childStat.Mode&os.ModeSymlink != 0 && !childIsDir {\n\t\t\tlinkDir, linkErr := isContainerDir(cli, req.ContainerID, childPath)\n\t\t\tif linkErr == nil {\n\t\t\t\tchildIsDir = linkDir\n\t\t\t}\n\t\t}\n\t\tfiles = append(files, toContainerFileInfo(childPath, childStat, childIsDir))\n\t}\n\tsort.Slice(files, func(i, j int) bool {\n\t\tif files[i].IsDir != files[j].IsDir {\n\t\t\treturn files[i].IsDir\n\t\t}\n\t\treturn strings.ToLower(files[i].Name) < strings.ToLower(files[j].Name)\n\t})\n\treturn files, nil\n}\n\nfunc (u *ContainerService) DeleteContainerFile(req dto.ContainerFileBatchDeleteReq) error {\n\tfor _, item := range req.Paths {\n\t\tif strings.TrimSpace(item) == \"/\" {\n\t\t\treturn buserr.New(\"ErrPathNotDelete\")\n\t\t}\n\t}\n\tcli, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\n\tcommand := []string{\"rm\", \"-rf\", \"--\"}\n\tcommand = append(command, req.Paths...)\n\t_, err = runContainerCommand(cli, req.ContainerID, command)\n\treturn err\n}\n\nfunc (u *ContainerService) UploadContainerFile(req dto.ContainerFileReq, fileName string, fileSize int64, file io.Reader) error {\n\tif len(req.Path) == 0 {\n\t\treq.Path = \"/\"\n\t}\n\tsafeName := path.Base(fileName)\n\tif safeName == \".\" || safeName == \"/\" || len(safeName) == 0 {\n\t\treturn buserr.New(\"ErrInvalidChar\")\n\t}\n\n\tcli, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\n\tctx := context.Background()\n\tstat, err := cli.ContainerStatPath(ctx, req.ContainerID, req.Path)\n\tif err != nil {\n\t\tif _, mkErr := runContainerCommand(cli, req.ContainerID, []string{\"mkdir\", \"-p\", \"--\", req.Path}); mkErr != nil {\n\t\t\treturn mkErr\n\t\t}\n\t\tstat, err = cli.ContainerStatPath(ctx, req.ContainerID, req.Path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif !stat.Mode.IsDir() {\n\t\treturn fmt.Errorf(\"path %s is not directory\", req.Path)\n\t}\n\n\tpipeReader, pipeWriter := io.Pipe()\n\twriteErr := make(chan error, 1)\n\tgo func() {\n\t\ttw := tar.NewWriter(pipeWriter)\n\t\theader := &tar.Header{\n\t\t\tName:    safeName,\n\t\t\tMode:    0644,\n\t\t\tSize:    fileSize,\n\t\t\tModTime: time.Now(),\n\t\t}\n\t\tif err := tw.WriteHeader(header); err != nil {\n\t\t\t_ = tw.Close()\n\t\t\t_ = pipeWriter.CloseWithError(err)\n\t\t\twriteErr <- err\n\t\t\treturn\n\t\t}\n\t\tif _, err := io.Copy(tw, file); err != nil {\n\t\t\t_ = tw.Close()\n\t\t\t_ = pipeWriter.CloseWithError(err)\n\t\t\twriteErr <- err\n\t\t\treturn\n\t\t}\n\t\tif err := tw.Close(); err != nil {\n\t\t\t_ = pipeWriter.CloseWithError(err)\n\t\t\twriteErr <- err\n\t\t\treturn\n\t\t}\n\t\t_ = pipeWriter.Close()\n\t\twriteErr <- nil\n\t}()\n\n\terr = cli.CopyToContainer(ctx, req.ContainerID, req.Path, pipeReader, container.CopyToContainerOptions{\n\t\tCopyUIDGID: true,\n\t})\n\tif err != nil {\n\t\t_ = pipeReader.CloseWithError(err)\n\t\t_ = pipeWriter.CloseWithError(err)\n\t\t<-writeErr\n\t\treturn err\n\t}\n\tif err := <-writeErr; err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *ContainerService) GetContainerFileContent(req dto.ContainerFileReq) (*dto.ContainerFileContent, error) {\n\tif len(req.Path) == 0 {\n\t\treturn nil, buserr.New(\"ErrInvalidChar\")\n\t}\n\tcli, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cli.Close()\n\n\tstat, err := cli.ContainerStatPath(context.Background(), req.ContainerID, req.Path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif stat.Mode.IsDir() {\n\t\treturn nil, fmt.Errorf(\"path %s is directory\", req.Path)\n\t}\n\n\tcontent := &dto.ContainerFileContent{Size: stat.Size}\n\theadBytes, err := runContainerCommandRaw(cli, req.ContainerID, []string{\"head\", \"-c\", \"4096\", \"--\", req.Path})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif bytes.IndexByte(headBytes, 0) >= 0 {\n\t\tcontent.IsBinary = true\n\t\treturn content, nil\n\t}\n\n\tconst inlinePreviewMax = 512 * 1024\n\tif stat.Size <= inlinePreviewMax {\n\t\traw, err := runContainerCommandRaw(cli, req.ContainerID, []string{\"cat\", \"--\", req.Path})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcontent.Content = string(raw)\n\t\treturn content, nil\n\t}\n\n\traw, err := runContainerCommandRaw(cli, req.ContainerID, []string{\"tail\", \"-n\", \"300\", \"--\", req.Path})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcontent.Content = string(raw)\n\tcontent.Truncated = true\n\treturn content, nil\n}\n\nfunc (u *ContainerService) GetContainerFileSize(req dto.ContainerFileReq) (int64, error) {\n\tif len(req.Path) == 0 {\n\t\treturn 0, buserr.New(\"ErrInvalidChar\")\n\t}\n\tcli, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer cli.Close()\n\n\tstat, err := cli.ContainerStatPath(context.Background(), req.ContainerID, req.Path)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif !stat.Mode.IsDir() {\n\t\treturn stat.Size, nil\n\t}\n\toutput, err := runContainerCommand(cli, req.ContainerID, []string{\"du\", \"-sb\", \"--\", req.Path})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tparts := strings.Fields(output)\n\tif len(parts) == 0 {\n\t\treturn 0, fmt.Errorf(\"invalid du output\")\n\t}\n\tsize, err := strconv.ParseInt(parts[0], 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn size, nil\n}\n\nfunc (u *ContainerService) DownloadContainerFile(req dto.ContainerFileReq) (io.ReadCloser, string, string, error) {\n\tif len(req.Path) == 0 {\n\t\treq.Path = \"/\"\n\t}\n\tcli, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, \"\", \"\", err\n\t}\n\n\tctx := context.Background()\n\tstat, err := cli.ContainerStatPath(ctx, req.ContainerID, req.Path)\n\tif err != nil {\n\t\t_ = cli.Close()\n\t\treturn nil, \"\", \"\", err\n\t}\n\n\tfileName := stat.Name\n\tif len(fileName) == 0 {\n\t\tfileName = \"container-file\"\n\t}\n\tif stat.Mode.IsDir() {\n\t\tif _, err := runContainerCommand(cli, req.ContainerID, []string{\"sh\", \"-c\", \"command -v tar >/dev/null 2>&1\"}); err != nil {\n\t\t\t_ = cli.Close()\n\t\t\treturn nil, \"\", \"\", fmt.Errorf(\"tar command not found in container\")\n\t\t}\n\n\t\ttargetPath := path.Clean(req.Path)\n\t\tparentPath := path.Dir(targetPath)\n\t\ttargetName := path.Base(targetPath)\n\t\tif parentPath == \".\" || parentPath == \"\" {\n\t\t\tparentPath = \"/\"\n\t\t}\n\t\ttarStream, err := runContainerCommandStream(cli, req.ContainerID, []string{\n\t\t\t\"tar\", \"-czf\", \"-\", \"-C\", parentPath, \"--\", targetName,\n\t\t})\n\t\tif err != nil {\n\t\t\t_ = cli.Close()\n\t\t\treturn nil, \"\", \"\", err\n\t\t}\n\t\tif !strings.HasSuffix(fileName, \".tar.gz\") {\n\t\t\tfileName += \".tar.gz\"\n\t\t}\n\t\treturn &closeHookReader{\n\t\t\tReadCloser: tarStream,\n\t\t\tonClose:    cli.Close,\n\t\t}, fileName, \"application/gzip\", nil\n\t}\n\n\tfileStream, err := runContainerCommandStream(cli, req.ContainerID, []string{\"cat\", \"--\", req.Path})\n\tif err != nil {\n\t\t_ = cli.Close()\n\t\treturn nil, \"\", \"\", err\n\t}\n\treturn &closeHookReader{\n\t\tReadCloser: fileStream,\n\t\tonClose:    cli.Close,\n\t}, fileName, \"application/octet-stream\", nil\n}\n\nfunc runContainerCommand(cli *client.Client, containerID string, command []string) (string, error) {\n\traw, err := runContainerCommandRaw(cli, containerID, command)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn strings.TrimSpace(string(raw)), nil\n}\n\ntype closeHookReader struct {\n\tio.ReadCloser\n\tonClose func() error\n}\n\nfunc (r *closeHookReader) Close() error {\n\tvar closeErr error\n\tif r.ReadCloser != nil {\n\t\tcloseErr = r.ReadCloser.Close()\n\t}\n\tif r.onClose != nil {\n\t\tif err := r.onClose(); err != nil && closeErr == nil {\n\t\t\tcloseErr = err\n\t\t}\n\t}\n\treturn closeErr\n}\n\nfunc runContainerCommandRaw(cli *client.Client, containerID string, command []string) ([]byte, error) {\n\tctx := context.Background()\n\tresp, err := cli.ContainerExecCreate(ctx, containerID, container.ExecOptions{\n\t\tCmd:          command,\n\t\tAttachStdout: true,\n\t\tAttachStderr: true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\thijack, err := cli.ContainerExecAttach(ctx, resp.ID, container.ExecAttachOptions{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer hijack.Close()\n\n\traw, err := io.ReadAll(hijack.Reader)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar stdout bytes.Buffer\n\tvar stderr bytes.Buffer\n\tif _, err := stdcopy.StdCopy(&stdout, &stderr, bytes.NewReader(raw)); err != nil {\n\t\treturn nil, err\n\t}\n\toutput := strings.TrimSpace(stdout.String())\n\terrorOutput := strings.TrimSpace(stderr.String())\n\tinfo, err := cli.ContainerExecInspect(ctx, resp.ID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif info.ExitCode != 0 {\n\t\tif len(errorOutput) != 0 {\n\t\t\treturn nil, fmt.Errorf(\"%s\", errorOutput)\n\t\t}\n\t\tif len(output) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"container command failed with exit code %d\", info.ExitCode)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"%s\", output)\n\t}\n\treturn stdout.Bytes(), nil\n}\n\nfunc runContainerCommandStream(cli *client.Client, containerID string, command []string) (io.ReadCloser, error) {\n\tctx := context.Background()\n\tresp, err := cli.ContainerExecCreate(ctx, containerID, container.ExecOptions{\n\t\tCmd:          command,\n\t\tAttachStdout: true,\n\t\tAttachStderr: true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\thijack, err := cli.ContainerExecAttach(ctx, resp.ID, container.ExecAttachOptions{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpipeReader, pipeWriter := io.Pipe()\n\tgo func() {\n\t\tdefer hijack.Close()\n\t\tvar stderr bytes.Buffer\n\t\t_, copyErr := stdcopy.StdCopy(pipeWriter, &stderr, hijack.Reader)\n\t\tif copyErr != nil {\n\t\t\t_ = pipeWriter.CloseWithError(copyErr)\n\t\t\treturn\n\t\t}\n\t\tinfo, inspectErr := cli.ContainerExecInspect(ctx, resp.ID)\n\t\tif inspectErr != nil {\n\t\t\t_ = pipeWriter.CloseWithError(inspectErr)\n\t\t\treturn\n\t\t}\n\t\tif info.ExitCode != 0 {\n\t\t\tmsg := strings.TrimSpace(stderr.String())\n\t\t\tif len(msg) == 0 {\n\t\t\t\tmsg = fmt.Sprintf(\"container command failed with exit code %d\", info.ExitCode)\n\t\t\t}\n\t\t\t_ = pipeWriter.CloseWithError(fmt.Errorf(\"%s\", msg))\n\t\t\treturn\n\t\t}\n\t\t_ = pipeWriter.Close()\n\t}()\n\treturn pipeReader, nil\n}\n\nfunc toContainerFileInfo(filePath string, stat container.PathStat, isDir bool) dto.ContainerFileInfo {\n\tname := stat.Name\n\tif len(name) == 0 {\n\t\titems := strings.Split(strings.TrimSuffix(filePath, \"/\"), \"/\")\n\t\tname = items[len(items)-1]\n\t}\n\tisLink := stat.Mode&os.ModeSymlink != 0\n\treturn dto.ContainerFileInfo{\n\t\tName:    name,\n\t\tPath:    filePath,\n\t\tIsDir:   isDir,\n\t\tIsLink:  isLink,\n\t\tLinkTo:  stat.LinkTarget,\n\t\tSize:    stat.Size,\n\t\tMode:    stat.Mode.String(),\n\t\tModTime: stat.Mtime.Format(constant.DateTimeLayout),\n\t}\n}\n\nfunc isContainerDir(cli *client.Client, containerID, targetPath string) (bool, error) {\n\t_, err := runContainerCommand(cli, containerID, []string{\n\t\t\"sh\", \"-c\", \"[ -d \\\"$1\\\" ]\", \"sh\", targetPath,\n\t})\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc stringsToMap(list []string) map[string]string {\n\tvar labelMap = make(map[string]string)\n\tfor _, label := range list {\n\t\tif strings.Contains(label, \"=\") {\n\t\t\tsps := strings.SplitN(label, \"=\", 2)\n\t\t\tlabelMap[sps[0]] = sps[1]\n\t\t}\n\t}\n\treturn labelMap\n}\nfunc stringsToMap2(list []string) map[string]*string {\n\tvar labelMap = make(map[string]*string)\n\tfor _, label := range list {\n\t\tif strings.Contains(label, \"=\") {\n\t\t\tsps := strings.SplitN(label, \"=\", 2)\n\t\t\tlabelMap[sps[0]] = &sps[1]\n\t\t}\n\t}\n\treturn labelMap\n}\n\nfunc calculateCPUPercentUnix(stats *container.StatsResponse) float64 {\n\tcpuPercent := 0.0\n\tcpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(stats.PreCPUStats.CPUUsage.TotalUsage)\n\tsystemDelta := float64(stats.CPUStats.SystemUsage) - float64(stats.PreCPUStats.SystemUsage)\n\n\tif systemDelta > 0.0 && cpuDelta > 0.0 {\n\t\tcpuPercent = (cpuDelta / systemDelta) * 100.0\n\t\tif len(stats.CPUStats.CPUUsage.PercpuUsage) != 0 {\n\t\t\tcpuPercent = cpuPercent * float64(len(stats.CPUStats.CPUUsage.PercpuUsage))\n\t\t}\n\t}\n\treturn cpuPercent\n}\nfunc calculateMemPercentUnix(memStats container.MemoryStats) float64 {\n\tmemPercent := 0.0\n\tmemUsage := calculateMemUsageUnixNoCache(memStats)\n\tmemLimit := float64(memStats.Limit)\n\tif memUsage > 0.0 && memLimit > 0.0 {\n\t\tmemPercent = (float64(memUsage) / float64(memLimit)) * 100.0\n\t}\n\treturn memPercent\n}\nfunc calculateMemUsageUnixNoCache(mem container.MemoryStats) uint64 {\n\tif v, isCgroup1 := mem.Stats[\"total_inactive_file\"]; isCgroup1 && v < mem.Usage {\n\t\treturn mem.Usage - v\n\t}\n\tif v := mem.Stats[\"inactive_file\"]; v < mem.Usage {\n\t\treturn mem.Usage - v\n\t}\n\treturn mem.Usage\n}\nfunc calculateBlockIO(blkio container.BlkioStats) (blkRead float64, blkWrite float64) {\n\tfor _, bioEntry := range blkio.IoServiceBytesRecursive {\n\t\tswitch strings.ToLower(bioEntry.Op) {\n\t\tcase \"read\":\n\t\t\tblkRead = (blkRead + float64(bioEntry.Value)) / 1024 / 1024\n\t\tcase \"write\":\n\t\t\tblkWrite = (blkWrite + float64(bioEntry.Value)) / 1024 / 1024\n\t\t}\n\t}\n\treturn\n}\nfunc calculateNetwork(network map[string]container.NetworkStats) (float64, float64) {\n\tvar rx, tx float64\n\n\tfor _, v := range network {\n\t\trx += float64(v.RxBytes) / 1024\n\t\ttx += float64(v.TxBytes) / 1024\n\t}\n\treturn rx, tx\n}\n\nfunc checkImageExist(client *client.Client, imageItem string) bool {\n\tif client == nil {\n\t\tvar err error\n\t\tclient, err = docker.NewDockerClient()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\timages, err := client.ImageList(context.Background(), image.ListOptions{})\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tfor _, img := range images {\n\t\tfor _, tag := range img.RepoTags {\n\t\t\tif tag == imageItem || tag == imageItem+\":latest\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc checkImageLike(client *client.Client, imageName string) bool {\n\tif client == nil {\n\t\tvar err error\n\t\tclient, err = docker.NewDockerClient()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\timages, err := client.ImageList(context.Background(), image.ListOptions{})\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tfor _, img := range images {\n\t\tfor _, tag := range img.RepoTags {\n\t\t\tparts := strings.Split(tag, \"/\")\n\t\t\timageNameWithTag := parts[len(parts)-1]\n\n\t\t\tif imageNameWithTag == imageName {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc pullImages(task *task.Task, client *client.Client, imageName string) error {\n\tdockerCli := docker.NewClientWithExist(client)\n\toptions := image.PullOptions{}\n\trepos, _ := imageRepoRepo.List()\n\tif len(repos) != 0 {\n\t\tfor _, repo := range repos {\n\t\t\tif strings.HasPrefix(imageName, repo.DownloadUrl) && repo.Auth {\n\t\t\t\tauthConfig := registry.AuthConfig{\n\t\t\t\t\tUsername: repo.Username,\n\t\t\t\t\tPassword: repo.Password,\n\t\t\t\t}\n\t\t\t\tencodedJSON, err := json.Marshal(authConfig)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tauthStr := base64.URLEncoding.EncodeToString(encodedJSON)\n\t\t\t\toptions.RegistryAuth = authStr\n\t\t\t}\n\t\t}\n\t} else {\n\t\thasAuth, authStr := loadAuthInfo(imageName)\n\t\tif hasAuth {\n\t\t\toptions.RegistryAuth = authStr\n\t\t}\n\t}\n\treturn dockerCli.PullImageWithProcessAndOptions(task, imageName, options)\n}\n\nfunc loadCpuAndMem(client *client.Client, containerItem string) dto.ContainerListStats {\n\tdata := dto.ContainerListStats{\n\t\tContainerID: containerItem,\n\t}\n\tres, err := client.ContainerStats(context.Background(), containerItem, false)\n\tif err != nil {\n\t\treturn data\n\t}\n\n\tdefer res.Body.Close()\n\tbody, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn data\n\t}\n\tvar stats *container.StatsResponse\n\tif err := json.Unmarshal(body, &stats); err != nil {\n\t\treturn data\n\t}\n\n\tdata.CPUTotalUsage = stats.CPUStats.CPUUsage.TotalUsage - stats.PreCPUStats.CPUUsage.TotalUsage\n\tdata.SystemUsage = stats.CPUStats.SystemUsage - stats.PreCPUStats.SystemUsage\n\tdata.CPUPercent = calculateCPUPercentUnix(stats)\n\tdata.PercpuUsage = len(stats.CPUStats.CPUUsage.PercpuUsage)\n\n\tdata.MemoryCache = stats.MemoryStats.Stats[\"cache\"]\n\tdata.MemoryUsage = calculateMemUsageUnixNoCache(stats.MemoryStats)\n\tdata.MemoryLimit = stats.MemoryStats.Limit\n\n\tdata.MemoryPercent = calculateMemPercentUnix(stats.MemoryStats)\n\treturn data\n}\n\nfunc checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {\n\tportMap := make(nat.PortMap)\n\tif len(ports) == 0 {\n\t\treturn portMap, nil\n\t}\n\tfor _, port := range ports {\n\t\tif strings.Contains(port.ContainerPort, \"-\") {\n\t\t\tif !strings.Contains(port.HostPort, \"-\") {\n\t\t\t\treturn portMap, buserr.New(\"ErrPortRules\")\n\t\t\t}\n\t\t\thostStart, _ := strconv.Atoi(strings.Split(port.HostPort, \"-\")[0])\n\t\t\thostEnd, _ := strconv.Atoi(strings.Split(port.HostPort, \"-\")[1])\n\t\t\tcontainerStart, _ := strconv.Atoi(strings.Split(port.ContainerPort, \"-\")[0])\n\t\t\tcontainerEnd, _ := strconv.Atoi(strings.Split(port.ContainerPort, \"-\")[1])\n\t\t\tif (hostEnd-hostStart) <= 0 || (containerEnd-containerStart) <= 0 {\n\t\t\t\treturn portMap, buserr.New(\"ErrPortRules\")\n\t\t\t}\n\t\t\tif (containerEnd - containerStart) != (hostEnd - hostStart) {\n\t\t\t\treturn portMap, buserr.New(\"ErrPortRules\")\n\t\t\t}\n\t\t\tfor i := 0; i <= hostEnd-hostStart; i++ {\n\t\t\t\tbindItem := nat.PortBinding{HostPort: strconv.Itoa(hostStart + i), HostIP: port.HostIP}\n\t\t\t\tportMap[nat.Port(fmt.Sprintf(\"%d/%s\", containerStart+i, port.Protocol))] = []nat.PortBinding{bindItem}\n\t\t\t}\n\t\t\tfor i := hostStart; i <= hostEnd; i++ {\n\t\t\t\tif common.ScanPortWithIP(port.HostIP, i) {\n\t\t\t\t\treturn portMap, buserr.WithDetail(\"ErrPortInUsed\", i, nil)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tportItem := 0\n\t\t\tif strings.Contains(port.HostPort, \"-\") {\n\t\t\t\tportItem, _ = strconv.Atoi(strings.Split(port.HostPort, \"-\")[0])\n\t\t\t} else {\n\t\t\t\tportItem, _ = strconv.Atoi(port.HostPort)\n\t\t\t}\n\t\t\tif common.ScanPortWithIP(port.HostIP, portItem) {\n\t\t\t\treturn portMap, buserr.WithDetail(\"ErrPortInUsed\", portItem, nil)\n\t\t\t}\n\t\t\tbindItem := nat.PortBinding{HostPort: strconv.Itoa(portItem), HostIP: port.HostIP}\n\t\t\tportMap[nat.Port(fmt.Sprintf(\"%s/%s\", port.ContainerPort, port.Protocol))] = []nat.PortBinding{bindItem}\n\t\t}\n\t}\n\treturn portMap, nil\n}\n\nfunc loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *container.InspectResponse) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {\n\tvar config container.Config\n\tvar hostConf container.HostConfig\n\tif !isCreate {\n\t\tconfig = *oldContainer.Config\n\t\thostConf = *oldContainer.HostConfig\n\t}\n\tvar networkConf network.NetworkingConfig\n\n\tportMap, err := checkPortStats(req.ExposedPorts)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\texposed := make(nat.PortSet)\n\tfor port := range portMap {\n\t\texposed[port] = struct{}{}\n\t}\n\tconfig.Image = req.Image\n\tconfig.Cmd = req.Cmd\n\tconfig.Entrypoint = req.Entrypoint\n\tconfig.Env = req.Env\n\tconfig.Labels = stringsToMap(req.Labels)\n\tconfig.ExposedPorts = exposed\n\tconfig.OpenStdin = req.OpenStdin\n\tconfig.Tty = req.Tty\n\tconfig.Hostname = req.Hostname\n\tconfig.Domainname = req.DomainName\n\tconfig.User = req.User\n\tconfig.WorkingDir = req.WorkingDir\n\n\tif len(req.Networks) != 0 {\n\t\tnetworkConf.EndpointsConfig = make(map[string]*network.EndpointSettings)\n\t\tfor _, item := range req.Networks {\n\t\t\tswitch item.Network {\n\t\t\tcase \"host\", \"none\", \"bridge\":\n\t\t\t\thostConf.NetworkMode = container.NetworkMode(item.Network)\n\t\t\t}\n\t\t\tif item.Ipv4 != \"\" || item.Ipv6 != \"\" {\n\t\t\t\tnetworkConf.EndpointsConfig[item.Network] = &network.EndpointSettings{\n\t\t\t\t\tIPAMConfig: &network.EndpointIPAMConfig{\n\t\t\t\t\t\tIPv4Address: item.Ipv4,\n\t\t\t\t\t\tIPv6Address: item.Ipv6,\n\t\t\t\t\t}, MacAddress: item.MacAddr}\n\t\t\t} else {\n\t\t\t\tnetworkConf.EndpointsConfig[item.Network] = &network.EndpointSettings{}\n\t\t\t}\n\t\t}\n\t} else {\n\t\treturn nil, nil, nil, fmt.Errorf(\"please set up the network\")\n\t}\n\n\thostConf.Privileged = req.Privileged\n\thostConf.AutoRemove = req.AutoRemove\n\thostConf.CPUShares = req.CPUShares\n\thostConf.PublishAllPorts = req.PublishAllPorts\n\thostConf.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(req.RestartPolicy)}\n\tif req.RestartPolicy == \"on-failure\" {\n\t\thostConf.RestartPolicy.MaximumRetryCount = 5\n\t}\n\thostConf.NanoCPUs = int64(req.NanoCPUs * 1000000000)\n\thostConf.Memory = int64(req.Memory * 1024 * 1024)\n\thostConf.MemorySwap = 0\n\thostConf.PortBindings = portMap\n\thostConf.Binds = []string{}\n\thostConf.Mounts = []mount.Mount{}\n\thostConf.DNS = req.DNS\n\thostConf.ExtraHosts = []string{}\n\tfor _, item := range req.ExtraHosts {\n\t\tif len(item.Hostname) == 0 || len(item.IP) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\thostConf.ExtraHosts = append(hostConf.ExtraHosts, fmt.Sprintf(\"%s:%s\", item.Hostname, item.IP))\n\t}\n\tconfig.Volumes = make(map[string]struct{})\n\tfor _, volume := range req.Volumes {\n\t\titem := mount.Mount{\n\t\t\tType:     mount.Type(volume.Type),\n\t\t\tSource:   volume.SourceDir,\n\t\t\tTarget:   volume.ContainerDir,\n\t\t\tReadOnly: volume.Mode == \"ro\",\n\t\t}\n\t\tif volume.Type == \"bind\" {\n\t\t\titem.BindOptions = &mount.BindOptions{\n\t\t\t\tPropagation: mount.Propagation(volume.Shared),\n\t\t\t}\n\t\t}\n\t\thostConf.Mounts = append(hostConf.Mounts, item)\n\t\tconfig.Volumes[volume.ContainerDir] = struct{}{}\n\t}\n\treturn &config, &hostConf, &networkConf, nil\n}\n\nfunc reCreateAfterUpdate(name string, client *client.Client, config *container.Config, hostConf *container.HostConfig, networkConf *container.NetworkSettings) {\n\tctx := context.Background()\n\n\tvar oldNetworkConf network.NetworkingConfig\n\tif networkConf != nil {\n\t\tfor networkKey := range networkConf.Networks {\n\t\t\toldNetworkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}\n\t\t\tbreak\n\t\t}\n\t}\n\n\toldContainer, err := client.ContainerCreate(ctx, config, hostConf, &oldNetworkConf, &v1.Platform{}, name)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"recreate after container update failed, err: %v\", err)\n\t\treturn\n\t}\n\tif err := client.ContainerStart(ctx, oldContainer.ID, container.StartOptions{}); err != nil {\n\t\tglobal.LOG.Errorf(\"restart after container update failed, err: %v\", err)\n\t}\n\tglobal.LOG.Info(\"recreate after container update successful\")\n}\n\nfunc loadVolumeBinds(binds []container.MountPoint) []dto.VolumeHelper {\n\tvar datas []dto.VolumeHelper\n\tfor _, bind := range binds {\n\t\tvar volumeItem dto.VolumeHelper\n\t\tvolumeItem.Type = string(bind.Type)\n\t\tif bind.Type == \"volume\" {\n\t\t\tvolumeItem.SourceDir = bind.Name\n\t\t} else {\n\t\t\tvolumeItem.SourceDir = bind.Source\n\t\t}\n\t\tvolumeItem.ContainerDir = bind.Destination\n\t\tvolumeItem.Mode = \"ro\"\n\t\tif bind.RW {\n\t\t\tvolumeItem.Mode = \"rw\"\n\t\t}\n\t\tvolumeItem.Shared = string(bind.Propagation)\n\t\tdatas = append(datas, volumeItem)\n\t}\n\treturn datas\n}\n\nfunc loadPortByInspect(id string, client *client.Client) ([]container.Port, error) {\n\tcontainerItem, err := client.ContainerInspect(context.Background(), id)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar itemPorts []container.Port\n\tfor key, val := range containerItem.ContainerJSONBase.HostConfig.PortBindings {\n\t\tif !strings.Contains(string(key), \"/\") {\n\t\t\tcontinue\n\t\t}\n\t\titem := strings.Split(string(key), \"/\")\n\t\titemPort, _ := strconv.ParseUint(item[0], 10, 16)\n\n\t\tfor _, itemVal := range val {\n\t\t\tpublicPort, _ := strconv.ParseUint(itemVal.HostPort, 10, 16)\n\t\t\titemPorts = append(itemPorts, container.Port{PrivatePort: uint16(itemPort), Type: item[1], PublicPort: uint16(publicPort), IP: itemVal.HostIP})\n\t\t}\n\t}\n\treturn itemPorts, nil\n}\nfunc transPortToStr(ports []container.Port) []string {\n\tvar (\n\t\tipv4Ports []container.Port\n\t\tipv6Ports []container.Port\n\t)\n\tfor _, port := range ports {\n\t\tif strings.Contains(port.IP, \":\") {\n\t\t\tipv6Ports = append(ipv6Ports, port)\n\t\t} else {\n\t\t\tipv4Ports = append(ipv4Ports, port)\n\t\t}\n\t}\n\tlist1 := simplifyPort(ipv4Ports)\n\tlist2 := simplifyPort(ipv6Ports)\n\treturn append(list1, list2...)\n}\nfunc simplifyPort(ports []container.Port) []string {\n\tvar datas []string\n\tif len(ports) == 0 {\n\t\treturn datas\n\t}\n\tif len(ports) == 1 {\n\t\tip := \"\"\n\t\tif len(ports[0].IP) != 0 {\n\t\t\tip = ports[0].IP + \":\"\n\t\t}\n\t\titemPortStr := fmt.Sprintf(\"%s%v/%s\", ip, ports[0].PrivatePort, ports[0].Type)\n\t\tif ports[0].PublicPort != 0 {\n\t\t\titemPortStr = fmt.Sprintf(\"%s%v->%v/%s\", ip, ports[0].PublicPort, ports[0].PrivatePort, ports[0].Type)\n\t\t}\n\t\tdatas = append(datas, itemPortStr)\n\t\treturn datas\n\t}\n\n\tsort.Slice(ports, func(i, j int) bool {\n\t\treturn ports[i].PrivatePort < ports[j].PrivatePort\n\t})\n\tstart := ports[0]\n\n\tfor i := 1; i < len(ports); i++ {\n\t\tif ports[i].PrivatePort != ports[i-1].PrivatePort+1 || ports[i].IP != ports[i-1].IP || ports[i].PublicPort != ports[i-1].PublicPort+1 || ports[i].Type != ports[i-1].Type {\n\t\t\tif ports[i-1].PrivatePort == start.PrivatePort {\n\t\t\t\titemPortStr := fmt.Sprintf(\"%s:%v/%s\", start.IP, start.PrivatePort, start.Type)\n\t\t\t\tif start.PublicPort != 0 {\n\t\t\t\t\titemPortStr = fmt.Sprintf(\"%s:%v->%v/%s\", start.IP, start.PublicPort, start.PrivatePort, start.Type)\n\t\t\t\t}\n\t\t\t\tif len(start.IP) == 0 {\n\t\t\t\t\titemPortStr = strings.TrimPrefix(itemPortStr, \":\")\n\t\t\t\t}\n\t\t\t\tdatas = append(datas, itemPortStr)\n\t\t\t} else {\n\t\t\t\titemPortStr := fmt.Sprintf(\"%s:%v-%v/%s\", start.IP, start.PrivatePort, ports[i-1].PrivatePort, start.Type)\n\t\t\t\tif start.PublicPort != 0 {\n\t\t\t\t\titemPortStr = fmt.Sprintf(\"%s:%v-%v->%v-%v/%s\", start.IP, start.PublicPort, ports[i-1].PublicPort, start.PrivatePort, ports[i-1].PrivatePort, start.Type)\n\t\t\t\t}\n\t\t\t\tif len(start.IP) == 0 {\n\t\t\t\t\titemPortStr = strings.TrimPrefix(itemPortStr, \":\")\n\t\t\t\t}\n\t\t\t\tdatas = append(datas, itemPortStr)\n\t\t\t}\n\t\t\tstart = ports[i]\n\t\t}\n\t\tif i == len(ports)-1 {\n\t\t\tif ports[i].PrivatePort == start.PrivatePort {\n\t\t\t\titemPortStr := fmt.Sprintf(\"%s:%v/%s\", start.IP, start.PrivatePort, start.Type)\n\t\t\t\tif start.PublicPort != 0 {\n\t\t\t\t\titemPortStr = fmt.Sprintf(\"%s:%v->%v/%s\", start.IP, start.PublicPort, start.PrivatePort, start.Type)\n\t\t\t\t}\n\t\t\t\tif len(start.IP) == 0 {\n\t\t\t\t\titemPortStr = strings.TrimPrefix(itemPortStr, \":\")\n\t\t\t\t}\n\t\t\t\tdatas = append(datas, itemPortStr)\n\t\t\t} else {\n\t\t\t\titemPortStr := fmt.Sprintf(\"%s:%v-%v/%s\", start.IP, start.PrivatePort, ports[i].PrivatePort, start.Type)\n\t\t\t\tif start.PublicPort != 0 {\n\t\t\t\t\titemPortStr = fmt.Sprintf(\"%s:%v-%v->%v-%v/%s\", start.IP, start.PublicPort, ports[i].PublicPort, start.PrivatePort, ports[i].PrivatePort, start.Type)\n\t\t\t\t}\n\t\t\t\tif len(start.IP) == 0 {\n\t\t\t\t\titemPortStr = strings.TrimPrefix(itemPortStr, \":\")\n\t\t\t\t}\n\t\t\t\tdatas = append(datas, itemPortStr)\n\t\t\t}\n\t\t}\n\t}\n\treturn datas\n}\n\nfunc loadComposeCount(client *client.Client) int {\n\toptions := container.ListOptions{All: true}\n\toptions.Filters = filters.NewArgs()\n\toptions.Filters.Add(\"label\", composeProjectLabel)\n\tlist, err := client.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn 0\n\t}\n\tcomposeCreatedByLocal, _ := composeRepo.ListRecord()\n\tcomposeMap := make(map[string]struct{})\n\tfor _, container := range list {\n\t\tif name, ok := container.Labels[composeProjectLabel]; ok {\n\t\t\tif _, has := composeMap[name]; !has {\n\t\t\t\tcomposeMap[name] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\tfor _, compose := range composeCreatedByLocal {\n\t\tif _, has := composeMap[compose.Name]; !has {\n\t\t\tcomposeMap[compose.Name] = struct{}{}\n\t\t}\n\t}\n\n\treturn len(composeMap)\n}\nfunc loadContainerPortForInfo(itemPorts []container.Port) []dto.PortHelper {\n\tvar exposedPorts []dto.PortHelper\n\tsamePortMap := make(map[string]dto.PortHelper)\n\tports := transPortToStr(itemPorts)\n\tfor _, item := range ports {\n\t\titemStr := strings.Split(item, \"->\")\n\t\tif len(itemStr) < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tvar itemPort dto.PortHelper\n\t\tlastIndex := strings.LastIndex(itemStr[0], \":\")\n\t\tif lastIndex == -1 {\n\t\t\titemPort.HostPort = itemStr[0]\n\t\t} else {\n\t\t\titemPort.HostIP = itemStr[0][0:lastIndex]\n\t\t\titemPort.HostPort = itemStr[0][lastIndex+1:]\n\t\t}\n\t\titemContainer := strings.Split(itemStr[1], \"/\")\n\t\tif len(itemContainer) != 2 {\n\t\t\tcontinue\n\t\t}\n\t\titemPort.ContainerPort = itemContainer[0]\n\t\titemPort.Protocol = itemContainer[1]\n\t\tkeyItem := fmt.Sprintf(\"%s->%s/%s\", itemPort.HostPort, itemPort.ContainerPort, itemPort.Protocol)\n\t\tif val, ok := samePortMap[keyItem]; ok {\n\t\t\tval.HostIP = \"\"\n\t\t\tsamePortMap[keyItem] = val\n\t\t} else {\n\t\t\tsamePortMap[keyItem] = itemPort\n\t\t}\n\t}\n\tfor _, val := range samePortMap {\n\t\texposedPorts = append(exposedPorts, val)\n\t}\n\treturn exposedPorts\n}\n\nfunc searchWithFilter(req dto.PageContainer, containers []container.Summary) []dto.ContainerInfo {\n\tvar (\n\t\trecords []dto.ContainerInfo\n\t\tlist    []container.Summary\n\t)\n\n\tif req.ExcludeAppStore {\n\t\tfor _, item := range containers {\n\t\t\tif created, ok := item.Labels[composeCreatedBy]; ok && created == \"Apps\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlist = append(list, item)\n\t\t}\n\t} else {\n\t\tlist = containers\n\t}\n\n\tif len(req.Name) != 0 {\n\t\tlength, count := len(list), 0\n\t\tfor count < length {\n\t\t\tif !strings.Contains(list[count].Names[0][1:], req.Name) && !strings.Contains(list[count].Image, req.Name) {\n\t\t\t\tlist = append(list[:count], list[(count+1):]...)\n\t\t\t\tlength--\n\t\t\t} else {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\tif req.State != \"all\" {\n\t\tlength, count := len(list), 0\n\t\tfor count < length {\n\t\t\tif list[count].State != req.State {\n\t\t\t\tlist = append(list[:count], list[(count+1):]...)\n\t\t\t\tlength--\n\t\t\t} else {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\tswitch req.OrderBy {\n\tcase \"name\":\n\t\tsort.Slice(list, func(i, j int) bool {\n\t\t\tif req.Order == constant.OrderAsc {\n\t\t\t\treturn list[i].Names[0][1:] < list[j].Names[0][1:]\n\t\t\t}\n\t\t\treturn list[i].Names[0][1:] > list[j].Names[0][1:]\n\t\t})\n\tdefault:\n\t\tsort.Slice(list, func(i, j int) bool {\n\t\t\tif req.Order == constant.OrderAsc {\n\t\t\t\treturn list[i].Created < list[j].Created\n\t\t\t}\n\t\t\treturn list[i].Created > list[j].Created\n\t\t})\n\t}\n\tfor _, item := range list {\n\t\tIsFromCompose := false\n\t\tif _, ok := item.Labels[composeProjectLabel]; ok {\n\t\t\tIsFromCompose = true\n\t\t}\n\t\tIsFromApp := false\n\t\tif created, ok := item.Labels[composeCreatedBy]; ok && created == \"Apps\" {\n\t\t\tIsFromApp = true\n\t\t}\n\t\texposePorts := transPortToStr(item.Ports)\n\t\tinfo := dto.ContainerInfo{\n\t\t\tContainerID:   item.ID,\n\t\t\tCreateTime:    time.Unix(item.Created, 0).Format(constant.DateTimeLayout),\n\t\t\tName:          item.Names[0][1:],\n\t\t\tPorts:         exposePorts,\n\t\t\tImageId:       strings.Split(item.ImageID, \":\")[1],\n\t\t\tImageName:     item.Image,\n\t\t\tState:         item.State,\n\t\t\tRunTime:       item.Status,\n\t\t\tIsFromApp:     IsFromApp,\n\t\t\tIsFromCompose: IsFromCompose,\n\t\t}\n\t\tif item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {\n\t\t\tnetworks := make([]string, 0, len(item.NetworkSettings.Networks))\n\t\t\tfor key := range item.NetworkSettings.Networks {\n\t\t\t\tnetworks = append(networks, item.NetworkSettings.Networks[key].IPAddress)\n\t\t\t}\n\t\t\tsort.Strings(networks)\n\t\t\tinfo.Network = networks\n\t\t}\n\t\trecords = append(records, info)\n\t}\n\tdescriptions, _ := settingRepo.GetDescriptionList(repo.WithByType(\"container\"))\n\tfor i := 0; i < len(records); i++ {\n\t\tfor _, desc := range descriptions {\n\t\t\tif desc.ID == records[i].ContainerID {\n\t\t\t\trecords[i].Description = desc.Description\n\t\t\t\trecords[i].IsPinned = desc.IsPinned\n\t\t\t}\n\t\t}\n\t}\n\tsort.Slice(records, func(i, j int) bool {\n\t\treturn records[i].IsPinned && !records[j].IsPinned\n\t})\n\treturn records\n}\n"
  },
  {
    "path": "agent/app/service/container_compose.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/api/types/filters\"\n)\n\nconst composeProjectLabel = \"com.docker.compose.project\"\nconst composeConfigLabel = \"com.docker.compose.project.config_files\"\nconst composeWorkdirLabel = \"com.docker.compose.project.working_dir\"\nconst composeCreatedBy = \"createdBy\"\n\nfunc (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface{}, error) {\n\tvar (\n\t\trecords   []dto.ComposeInfo\n\t\tBackDatas []dto.ComposeInfo\n\t)\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer client.Close()\n\n\toptions := container.ListOptions{All: true}\n\toptions.Filters = filters.NewArgs()\n\toptions.Filters.Add(\"label\", composeProjectLabel)\n\n\tlist, err := client.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tcomposeCreatedByLocal, _ := composeRepo.ListRecord()\n\tcomposeLocalMap := make(map[string]dto.ComposeInfo)\n\tfor _, localItem := range composeCreatedByLocal {\n\t\tcomposeItemLocal := dto.ComposeInfo{\n\t\t\tContainerCount: 0,\n\t\t\tCreatedAt:      localItem.CreatedAt.Format(constant.DateTimeLayout),\n\t\t\tConfigFile:     localItem.Path,\n\t\t\tWorkdir:        strings.TrimSuffix(localItem.Path, \"/docker-compose.yml\"),\n\t\t}\n\t\tcomposeItemLocal.CreatedBy = \"1Panel\"\n\t\tcomposeItemLocal.Path = localItem.Path\n\t\tcomposeLocalMap[localItem.Name] = composeItemLocal\n\t}\n\n\tcomposeMap := make(map[string]dto.ComposeInfo)\n\tfor _, container := range list {\n\t\tif name, ok := container.Labels[composeProjectLabel]; ok {\n\t\t\tcontainerItem := dto.ComposeContainer{\n\t\t\t\tContainerID: container.ID,\n\t\t\t\tName:        container.Names[0][1:],\n\t\t\t\tState:       container.State,\n\t\t\t\tCreateTime:  time.Unix(container.Created, 0).Format(constant.DateTimeLayout),\n\t\t\t\tPorts:       transPortToStr(container.Ports),\n\t\t\t}\n\t\t\tif compose, has := composeMap[name]; has {\n\t\t\t\tcompose.ContainerCount++\n\t\t\t\tif strings.ToLower(containerItem.State) == \"running\" {\n\t\t\t\t\tcompose.RunningCount++\n\t\t\t\t}\n\t\t\t\tcompose.Containers = append(compose.Containers, containerItem)\n\t\t\t\tcomposeMap[name] = compose\n\t\t\t} else {\n\t\t\t\tconfig := container.Labels[composeConfigLabel]\n\t\t\t\tworkdir := container.Labels[composeWorkdirLabel]\n\t\t\t\tcomposeItem := dto.ComposeInfo{\n\t\t\t\t\tContainerCount: 1,\n\t\t\t\t\tCreatedAt:      time.Unix(container.Created, 0).Format(constant.DateTimeLayout),\n\t\t\t\t\tConfigFile:     config,\n\t\t\t\t\tWorkdir:        workdir,\n\t\t\t\t\tContainers:     []dto.ComposeContainer{containerItem},\n\t\t\t\t}\n\t\t\t\tif strings.ToLower(containerItem.State) == \"running\" {\n\t\t\t\t\tcomposeItem.RunningCount = 1\n\t\t\t\t}\n\t\t\t\tcreatedBy, ok := container.Labels[composeCreatedBy]\n\t\t\t\tif ok {\n\t\t\t\t\tcomposeItem.CreatedBy = createdBy\n\t\t\t\t}\n\t\t\t\tif len(config) != 0 && len(workdir) != 0 && strings.Contains(config, workdir) {\n\t\t\t\t\tcomposeItem.Path = config\n\t\t\t\t} else {\n\t\t\t\t\tcomposeItem.Path = workdir\n\t\t\t\t}\n\t\t\t\tfor i := 0; i < len(composeCreatedByLocal); i++ {\n\t\t\t\t\tif composeCreatedByLocal[i].Name == name {\n\t\t\t\t\t\tcomposeItem.CreatedBy = \"1Panel\"\n\t\t\t\t\t\tcomposeCreatedByLocal = append(composeCreatedByLocal[:i], composeCreatedByLocal[i+1:]...)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcomposeMap[name] = composeItem\n\t\t\t}\n\t\t}\n\t}\n\n\tmergedMap := make(map[string]dto.ComposeInfo)\n\tfor key, localItem := range composeLocalMap {\n\t\tmergedMap[key] = localItem\n\t}\n\tfor key, item := range composeMap {\n\t\tif existingItem, exists := mergedMap[key]; exists {\n\t\t\tif item.ContainerCount > 0 {\n\t\t\t\tif existingItem.ContainerCount <= 0 {\n\t\t\t\t\tmergedMap[key] = item\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tmergedMap[key] = item\n\t\t}\n\t}\n\n\tfor key, value := range mergedMap {\n\t\tvalue.Name = key\n\t\trecords = append(records, value)\n\t}\n\tif len(req.Info) != 0 {\n\t\tlength, count := len(records), 0\n\t\tfor count < length {\n\t\t\tif !strings.Contains(records[count].Name, req.Info) {\n\t\t\t\trecords = append(records[:count], records[(count+1):]...)\n\t\t\t\tlength--\n\t\t\t} else {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\tsort.Slice(records, func(i, j int) bool {\n\t\treturn records[i].CreatedAt > records[j].CreatedAt\n\t})\n\ttotal, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\tBackDatas = make([]dto.ComposeInfo, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\tBackDatas = records[start:end]\n\t}\n\tlistItem := loadEnv(BackDatas)\n\treturn int64(total), listItem, nil\n}\n\nfunc (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {\n\tif cmd.CheckIllegal(req.Path) {\n\t\treturn false, buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcomposeItem, _ := composeRepo.GetRecord(repo.WithByName(req.Name))\n\tif composeItem.ID != 0 {\n\t\treturn false, buserr.New(\"ErrRecordExist\")\n\t}\n\tif err := u.loadPath(&req); err != nil {\n\t\treturn false, err\n\t}\n\tif err := newComposeEnv(req.Path, req.Env); err != nil {\n\t\treturn false, err\n\t}\n\tcmd := getComposeCmd(req.Path, \"config\")\n\tstdout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"docker-compose config failed, std: %s, err: %v\", string(stdout), err)\n\t}\n\treturn true, nil\n}\n\nfunc (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {\n\tif cmd.CheckIllegal(req.Name, req.Path) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif err := u.loadPath(&req); err != nil {\n\t\treturn err\n\t}\n\tif req.From == \"path\" {\n\t\treq.Name = path.Base(path.Dir(req.Path))\n\t}\n\ttaskItem, err := task.NewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeCompose, req.TaskID, 1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for image build failed, err: %v\", err)\n\t}\n\tif err := newComposeEnv(req.Path, req.Env); err != nil {\n\t\treturn err\n\t}\n\tgo func() {\n\t\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"ComposeCreate\"), func(t *task.Task) error {\n\t\t\terr := compose.UpWithTask(req.Path, t, req.ForcePull)\n\t\t\tt.LogWithStatus(i18n.GetMsgByKey(\"ComposeCreate\"), err)\n\t\t\tif err != nil {\n\t\t\t\t_, _ = compose.Down(req.Path)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_ = composeRepo.CreateRecord(&model.Compose{Name: strings.ToLower(req.Name), Path: req.Path})\n\t\t\treturn nil\n\t\t}, nil)\n\t\t_ = taskItem.Execute()\n\t}()\n\n\treturn nil\n}\n\nfunc (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {\n\tif len(req.Path) == 0 && req.Operation == \"delete\" {\n\t\t_ = composeRepo.DeleteRecord(repo.WithByName(req.Name))\n\t\treturn nil\n\t}\n\tif cmd.CheckIllegal(req.Path, req.Operation) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif req.Operation == \"delete\" {\n\t\tif err := removeContainerForCompose(req.Name, req.Path); err != nil && !req.Force {\n\t\t\treturn err\n\t\t}\n\t\tif req.WithFile {\n\t\t\tfor _, item := range strings.Split(req.Path, \",\") {\n\t\t\t\tif len(item) != 0 {\n\t\t\t\t\t_ = os.RemoveAll(path.Dir(item))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_ = composeRepo.DeleteRecord(repo.WithByName(req.Name))\n\t\treturn nil\n\t}\n\tif req.Operation == \"up\" {\n\t\tif stdout, err := compose.Up(req.Path); err != nil {\n\t\t\treturn fmt.Errorf(\"docker-compose up failed, std: %s, err: %v\", stdout, err)\n\t\t}\n\t} else {\n\t\tif stdout, err := compose.Operate(req.Path, req.Operation); err != nil {\n\t\t\treturn fmt.Errorf(\"docker-compose %s failed, std: %s, err: %v\", req.Operation, stdout, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {\n\tif cmd.CheckIllegal(req.Name, req.Path) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\ttaskItem, err := task.NewTaskWithOps(req.Name, task.TaskUpdate, task.TaskScopeCompose, req.TaskID, 1)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for update compose failed, err: %v\", err)\n\t\treturn err\n\t}\n\tgo func() {\n\t\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"TaskUpdate\"), func(t *task.Task) error {\n\t\t\toldFile, err := os.ReadFile(req.DetailPath)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"load file with path %s failed, %v\", req.DetailPath, err)\n\t\t\t}\n\t\t\tfile, err := os.OpenFile(req.DetailPath, os.O_WRONLY|os.O_TRUNC, 0640)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer file.Close()\n\t\t\twrite := bufio.NewWriter(file)\n\t\t\t_, _ = write.WriteString(req.Content)\n\t\t\twrite.Flush()\n\n\t\t\tglobal.LOG.Infof(\"docker-compose.yml %s has been replaced, now start to docker-compose restart\", req.DetailPath)\n\t\t\tif err := newComposeEnv(req.DetailPath, req.Env); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif err := compose.UpWithTask(req.Path, t, req.ForcePull); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"update failed when handle compose up, err: %s, now try to recreate the old compose file\", err)\n\t\t\t\tif err := recreateCompose(string(oldFile), req.Path); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"update failed and recreate old compose file also failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"update failed when handle compose up, err: %s\", err)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}, nil)\n\t\t_ = taskItem.Execute()\n\t}()\n\n\treturn nil\n}\n\nfunc (u *ContainerService) ComposeLogClean(req dto.ComposeLogClean) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\n\toptions := container.ListOptions{All: true}\n\toptions.Filters = filters.NewArgs()\n\toptions.Filters.Add(\"label\", composeProjectLabel)\n\n\tlist, err := client.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn err\n\t}\n\tctx := context.Background()\n\tfor _, item := range list {\n\t\tif name, ok := item.Labels[composeProjectLabel]; ok {\n\t\t\tif name != req.Name {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcontainerItem, err := client.ContainerInspect(ctx, item.ID)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfile, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, constant.FilePerm)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer file.Close()\n\t\t\tif err = file.Truncate(0); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_, _ = file.Seek(0, 0)\n\n\t\t\tfiles, _ := filepath.Glob(fmt.Sprintf(\"%s.*\", containerItem.LogPath))\n\t\t\tfor _, file := range files {\n\t\t\t\t_ = os.Remove(file)\n\t\t\t}\n\t\t}\n\t}\n\treturn u.ComposeOperation(dto.ComposeOperation{\n\t\tName:      req.Name,\n\t\tPath:      req.Path,\n\t\tOperation: \"restart\",\n\t})\n}\n\nfunc (u *ContainerService) LoadComposeEnv(name string) (string, error) {\n\tenvFilePath := path.Join(path.Dir(name), \".env\")\n\tfile, err := os.ReadFile(envFilePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(file), nil\n}\n\nfunc (u *ContainerService) loadPath(req *dto.ComposeCreate) error {\n\tif req.From == \"template\" || req.From == \"edit\" {\n\t\tdir := fmt.Sprintf(\"%s/docker/compose/%s\", global.Dir.DataDir, req.Name)\n\t\tif _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {\n\t\t\tif err = os.MkdirAll(dir, os.ModePerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tpath := fmt.Sprintf(\"%s/docker-compose.yml\", dir)\n\t\tfile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer file.Close()\n\t\twrite := bufio.NewWriter(file)\n\t\t_, _ = write.WriteString(string(req.File))\n\t\twrite.Flush()\n\t\treq.Path = path\n\t}\n\treturn nil\n}\n\nfunc removeContainerForCompose(composeName, composePath string) error {\n\tif stdout, err := compose.Operate(composePath, \"down\"); err != nil {\n\t\treturn errors.New(stdout)\n\t}\n\tvar options container.ListOptions\n\toptions.All = true\n\toptions.Filters = filters.NewArgs()\n\toptions.Filters.Add(\"label\", \"com.docker.compose.project=\"+composeName)\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tctx := context.Background()\n\tcontainers, err := client.ContainerList(ctx, options)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, c := range containers {\n\t\t_ = client.ContainerRemove(ctx, c.ID, container.RemoveOptions{RemoveVolumes: true, Force: true})\n\t}\n\treturn nil\n}\n\nfunc recreateCompose(content, path string) error {\n\tfile, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(content)\n\twrite.Flush()\n\n\tif stdout, err := compose.Up(path); err != nil {\n\t\treturn errors.New(string(stdout))\n\t}\n\treturn nil\n}\n\nfunc loadEnv(list []dto.ComposeInfo) []dto.ComposeInfo {\n\tfor i := 0; i < len(list); i++ {\n\t\ttmpPath := list[i].Path\n\t\tif strings.Contains(list[i].Path, \",\") {\n\t\t\ttmpPath = strings.Split(list[i].Path, \",\")[0]\n\t\t}\n\t\tenvFilePath := path.Join(path.Dir(tmpPath), \".env\")\n\t\tfile, err := os.ReadFile(envFilePath)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tlist[i].Env = string(file)\n\t}\n\treturn list\n}\n\nfunc newComposeEnv(pathItem string, env string) error {\n\tenvFilePath := path.Join(path.Dir(pathItem), \".env\")\n\tfile, err := os.OpenFile(envFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"failed to create env file: %v\", err)\n\t\treturn err\n\t}\n\tdefer file.Close()\n\tif _, err := file.WriteString(env); err != nil {\n\t\tglobal.LOG.Errorf(\"failed to write env to file: %v\", err)\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/container_network.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/docker/docker/api/types/network\"\n)\n\nfunc (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface{}, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer client.Close()\n\tlist, err := client.NetworkList(context.TODO(), network.ListOptions{})\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tif len(req.Info) != 0 {\n\t\tlength, count := len(list), 0\n\t\tfor count < length {\n\t\t\tif !strings.Contains(list[count].Name, req.Info) {\n\t\t\t\tlist = append(list[:count], list[(count+1):]...)\n\t\t\t\tlength--\n\t\t\t} else {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\tvar (\n\t\tdata    []dto.Network\n\t\trecords []network.Inspect\n\t)\n\tsort.Slice(list, func(i, j int) bool {\n\t\treturn list[i].Created.Before(list[j].Created)\n\t})\n\ttotal, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\trecords = make([]network.Inspect, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\trecords = list[start:end]\n\t}\n\n\tfor _, item := range records {\n\t\ttag := make([]string, 0)\n\t\tfor key, val := range item.Labels {\n\t\t\ttag = append(tag, fmt.Sprintf(\"%s=%s\", key, val))\n\t\t}\n\t\tvar ipam network.IPAMConfig\n\t\tif len(item.IPAM.Config) > 0 {\n\t\t\tipam = item.IPAM.Config[0]\n\t\t}\n\t\tdata = append(data, dto.Network{\n\t\t\tID:         item.ID,\n\t\t\tCreatedAt:  item.Created,\n\t\t\tName:       item.Name,\n\t\t\tDriver:     item.Driver,\n\t\t\tIPAMDriver: item.IPAM.Driver,\n\t\t\tSubnet:     ipam.Subnet,\n\t\t\tGateway:    ipam.Gateway,\n\t\t\tAttachable: item.Attachable,\n\t\t\tLabels:     tag,\n\t\t})\n\t}\n\n\treturn int64(total), data, nil\n}\n\nfunc (u *ContainerService) ListNetwork() ([]dto.Options, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\tlist, err := client.NetworkList(context.TODO(), network.ListOptions{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []dto.Options\n\tfor _, item := range list {\n\t\tdatas = append(datas, dto.Options{Option: item.Name})\n\t}\n\tsort.Slice(datas, func(i, j int) bool {\n\t\treturn datas[i].Option < datas[j].Option\n\t})\n\treturn datas, nil\n}\n\nfunc (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tfor _, id := range req.Names {\n\t\tif err := client.NetworkRemove(context.TODO(), id); err != nil {\n\t\t\tif strings.Contains(err.Error(), \"has active endpoints\") {\n\t\t\t\treturn buserr.WithDetail(\"ErrInUsed\", id, nil)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\nfunc (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tvar ipams []network.IPAMConfig\n\n\tif req.Ipv4 {\n\t\tvar itemIpam network.IPAMConfig\n\t\tif len(req.AuxAddress) != 0 {\n\t\t\titemIpam.AuxAddress = make(map[string]string)\n\t\t}\n\t\tif len(req.Subnet) != 0 {\n\t\t\titemIpam.Subnet = req.Subnet\n\t\t}\n\t\tif len(req.Gateway) != 0 {\n\t\t\titemIpam.Gateway = req.Gateway\n\t\t}\n\t\tif len(req.IPRange) != 0 {\n\t\t\titemIpam.IPRange = req.IPRange\n\t\t}\n\t\tfor _, addr := range req.AuxAddress {\n\t\t\titemIpam.AuxAddress[addr.Key] = addr.Value\n\t\t}\n\t\tipams = append(ipams, itemIpam)\n\t}\n\tif req.Ipv6 {\n\t\tvar itemIpam network.IPAMConfig\n\t\tif len(req.AuxAddress) != 0 {\n\t\t\titemIpam.AuxAddress = make(map[string]string)\n\t\t}\n\t\tif len(req.SubnetV6) != 0 {\n\t\t\titemIpam.Subnet = req.SubnetV6\n\t\t}\n\t\tif len(req.GatewayV6) != 0 {\n\t\t\titemIpam.Gateway = req.GatewayV6\n\t\t}\n\t\tif len(req.IPRangeV6) != 0 {\n\t\t\titemIpam.IPRange = req.IPRangeV6\n\t\t}\n\t\tfor _, addr := range req.AuxAddressV6 {\n\t\t\titemIpam.AuxAddress[addr.Key] = addr.Value\n\t\t}\n\t\tipams = append(ipams, itemIpam)\n\t}\n\n\toptions := network.CreateOptions{\n\t\tEnableIPv6: &req.Ipv6,\n\t\tDriver:     req.Driver,\n\t\tOptions:    stringsToMap(req.Options),\n\t\tLabels:     stringsToMap(req.Labels),\n\t}\n\tif len(ipams) != 0 {\n\t\toptions.IPAM = &network.IPAM{Config: ipams}\n\t}\n\tif _, err := client.NetworkCreate(context.TODO(), req.Name, options); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/container_volume.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/docker/docker/api/types/filters\"\n\t\"github.com/docker/docker/api/types/volume\"\n)\n\nfunc (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{}, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer client.Close()\n\tlist, err := client.VolumeList(context.TODO(), volume.ListOptions{})\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tif len(req.Info) != 0 {\n\t\tlength, count := len(list.Volumes), 0\n\t\tfor count < length {\n\t\t\tif !strings.Contains(list.Volumes[count].Name, req.Info) {\n\t\t\t\tlist.Volumes = append(list.Volumes[:count], list.Volumes[(count+1):]...)\n\t\t\t\tlength--\n\t\t\t} else {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\tvar (\n\t\tdata    []dto.Volume\n\t\trecords []*volume.Volume\n\t)\n\tsort.Slice(list.Volumes, func(i, j int) bool {\n\t\treturn list.Volumes[i].CreatedAt > list.Volumes[j].CreatedAt\n\t})\n\ttotal, start, end := len(list.Volumes), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\trecords = make([]*volume.Volume, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\trecords = list.Volumes[start:end]\n\t}\n\n\tnyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\tfor _, item := range records {\n\t\tvar volume dto.Volume\n\t\tvolume.Driver = item.Driver\n\t\tvolume.Mountpoint = item.Mountpoint\n\t\tvolume.Name = item.Name\n\t\tfor key, val := range item.Labels {\n\t\t\tvolume.Labels = append(volume.Labels, dto.VolumeOption{Key: key, Value: val})\n\t\t}\n\t\tfor key, val := range item.Options {\n\t\t\tvolume.Options = append(volume.Options, dto.VolumeOption{Key: key, Value: val})\n\t\t}\n\t\tsort.Slice(volume.Options, func(i, j int) bool {\n\t\t\treturn volume.Options[i].Key < volume.Options[j].Key\n\t\t})\n\t\tif strings.Contains(item.CreatedAt, \"Z\") {\n\t\t\tvolume.CreatedAt, _ = time.ParseInLocation(\"2006-01-02T15:04:05Z\", item.CreatedAt, nyc)\n\t\t} else if strings.Contains(item.CreatedAt, \"+\") {\n\t\t\tvolume.CreatedAt, _ = time.ParseInLocation(\"2006-01-02T15:04:05+08:00\", item.CreatedAt, nyc)\n\t\t} else {\n\t\t\tvolume.CreatedAt, _ = time.ParseInLocation(\"2006-01-02T15:04:05\", item.CreatedAt, nyc)\n\t\t}\n\t\tdata = append(data, volume)\n\t}\n\n\treturn int64(total), data, nil\n}\nfunc (u *ContainerService) ListVolume() ([]dto.Options, error) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\tlist, err := client.VolumeList(context.TODO(), volume.ListOptions{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []dto.Options\n\tfor _, item := range list.Volumes {\n\t\tdatas = append(datas, dto.Options{\n\t\t\tOption: item.Name,\n\t\t})\n\t}\n\tsort.Slice(datas, func(i, j int) bool {\n\t\treturn datas[i].Option < datas[j].Option\n\t})\n\treturn datas, nil\n}\nfunc (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tfor _, id := range req.Names {\n\t\tif err := client.VolumeRemove(context.TODO(), id, true); err != nil {\n\t\t\tif strings.Contains(err.Error(), \"volume is in use\") {\n\t\t\t\treturn buserr.WithDetail(\"ErrInUsed\", id, nil)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\nfunc (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\targ := filters.NewArgs()\n\targ.Add(\"name\", req.Name)\n\tvos, _ := client.VolumeList(context.TODO(), volume.ListOptions{Filters: arg})\n\tif len(vos.Volumes) != 0 {\n\t\tfor _, v := range vos.Volumes {\n\t\t\tif v.Name == req.Name {\n\t\t\t\treturn buserr.New(\"ErrRecordExist\")\n\t\t\t}\n\t\t}\n\t}\n\toptions := volume.CreateOptions{\n\t\tName:       req.Name,\n\t\tDriver:     req.Driver,\n\t\tDriverOpts: stringsToMap(req.Options),\n\t\tLabels:     stringsToMap(req.Labels),\n\t}\n\tif _, err := client.VolumeCreate(context.TODO(), options); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/cronjob.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/jinzhu/copier\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/robfig/cron/v3\"\n)\n\ntype CronjobService struct{}\n\ntype ICronjobService interface {\n\tSearchWithPage(search dto.PageCronjob) (int64, interface{}, error)\n\tSearchRecords(search dto.SearchRecord) (int64, interface{}, error)\n\tCreate(cronjobDto dto.CronjobOperate) error\n\tLoadNextHandle(spec string) ([]string, error)\n\tHandleOnce(id uint) error\n\tUpdate(id uint, req dto.CronjobOperate) error\n\tUpdateStatus(id uint, status string) error\n\tUpdateGroup(req dto.ChangeGroup) error\n\tDelete(req dto.CronjobBatchDelete) error\n\tStartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)\n\tCleanRecord(req dto.CronjobClean) error\n\tHandleStop(id uint) error\n\n\tExport(req dto.OperateByIDs) (string, error)\n\tImport(req []dto.CronjobTrans) error\n\tLoadScriptOptions() []dto.ScriptOptions\n\n\tLoadInfo(req dto.OperateByID) (*dto.CronjobOperate, error)\n\tLoadRecordLog(req dto.OperateByID) string\n}\n\nfunc NewICronjobService() ICronjobService {\n\treturn &CronjobService{}\n}\n\nfunc (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interface{}, error) {\n\ttotal, cronjobs, err := cronjobRepo.Page(search.Page,\n\t\tsearch.PageSize,\n\t\trepo.WithByGroups(search.GroupIDs),\n\t\trepo.WithByLikeName(search.Info),\n\t\trepo.WithOrderRuleBy(search.OrderBy, search.Order))\n\tvar dtoCronjobs []dto.CronjobInfo\n\tfor _, cronjob := range cronjobs {\n\t\tvar item dto.CronjobInfo\n\t\tif err := copier.Copy(&item, &cronjob); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\trecord, _ := cronjobRepo.RecordFirst(cronjob.ID)\n\t\tif record.ID != 0 {\n\t\t\titem.LastRecordStatus = record.Status\n\t\t\titem.LastRecordTime = record.StartTime.Format(constant.DateTimeLayout)\n\t\t} else {\n\t\t\titem.LastRecordTime = \"-\"\n\t\t}\n\t\titem.SourceAccounts, item.DownloadAccount, _ = loadBackupNamesByID(cronjob.SourceAccountIDs, cronjob.DownloadAccountID)\n\t\talertBase := dto.AlertBase{\n\t\t\tAlertType: cronjob.Type,\n\t\t\tEntryID:   cronjob.ID,\n\t\t}\n\t\talertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))\n\t\tif alertInfo.SendCount != 0 {\n\t\t\titem.AlertCount = alertInfo.SendCount\n\t\t} else {\n\t\t\titem.AlertCount = 0\n\t\t}\n\t\tif cronjob.Type == \"snapshot\" && len(cronjob.SnapshotRule) != 0 {\n\t\t\t_ = json.Unmarshal([]byte(cronjob.SnapshotRule), &item.SnapshotRule)\n\t\t}\n\t\tdtoCronjobs = append(dtoCronjobs, item)\n\t}\n\treturn total, dtoCronjobs, err\n}\n\nfunc (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, error) {\n\tcronjob, err := cronjobRepo.Get(repo.WithByID(req.ID))\n\tvar item dto.CronjobOperate\n\tif err := copier.Copy(&item, &cronjob); err != nil {\n\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\talertBase := dto.AlertBase{\n\t\tAlertType: cronjob.Type,\n\t\tEntryID:   cronjob.ID,\n\t}\n\talertInfo, _ := alertRepo.Get(alertRepo.WithByType(alertBase.AlertType), alertRepo.WithByProject(strconv.Itoa(int(alertBase.EntryID))), repo.WithByStatus(constant.AlertEnable))\n\titem.AlertMethod = alertInfo.Method\n\tif alertInfo.SendCount != 0 {\n\t\titem.AlertCount = alertInfo.SendCount\n\t} else {\n\t\titem.AlertCount = 0\n\t}\n\tif cronjob.Type == \"snapshot\" && len(cronjob.SnapshotRule) != 0 {\n\t\t_ = json.Unmarshal([]byte(cronjob.SnapshotRule), &item.SnapshotRule)\n\t}\n\tif cronjob.Type == \"cleanLog\" {\n\t\t_ = json.Unmarshal([]byte(cronjob.Config), &item.CleanLogConfig)\n\t}\n\treturn &item, err\n}\n\nfunc (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {\n\tcronjobs, err := cronjobRepo.List(repo.WithByIDs(req.IDs))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar data []dto.CronjobTrans\n\tfor _, cronjob := range cronjobs {\n\t\titem := dto.CronjobTrans{\n\t\t\tName:           cronjob.Name,\n\t\t\tType:           cronjob.Type,\n\t\t\tGroupID:        cronjob.GroupID,\n\t\t\tSpecCustom:     cronjob.SpecCustom,\n\t\t\tSpec:           cronjob.Spec,\n\t\t\tExecutor:       cronjob.Executor,\n\t\t\tScriptMode:     cronjob.ScriptMode,\n\t\t\tScript:         cronjob.Script,\n\t\t\tCommand:        cronjob.Command,\n\t\t\tContainerName:  cronjob.ContainerName,\n\t\t\tUser:           cronjob.User,\n\t\t\tURL:            cronjob.URL,\n\t\t\tDBType:         cronjob.DBType,\n\t\t\tExclusionRules: cronjob.ExclusionRules,\n\t\t\tIsDir:          cronjob.IsDir,\n\t\t\tSourceDir:      cronjob.SourceDir,\n\t\t\tRetainCopies:   cronjob.RetainCopies,\n\t\t\tRetryTimes:     cronjob.RetryTimes,\n\t\t\tTimeout:        cronjob.Timeout,\n\t\t\tIgnoreErr:      cronjob.IgnoreErr,\n\t\t\tSecret:         cronjob.Secret,\n\t\t\tArgs:           cronjob.Args,\n\t\t}\n\t\tswitch cronjob.Type {\n\t\tcase \"app\":\n\t\t\tif cronjob.AppID == \"all\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tapps := loadAppsForJob(cronjob)\n\t\t\tfor _, app := range apps {\n\t\t\t\titem.Apps = append(item.Apps, dto.TransHelper{Name: app.App.Key, DetailName: app.Name})\n\t\t\t}\n\t\tcase \"website\", \"cutWebsiteLog\":\n\t\t\tif cronjob.Website == \"all\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\twebsites := loadWebsForJob(cronjob)\n\t\t\tfor _, website := range websites {\n\t\t\t\titem.Websites = append(item.Websites, website.Alias)\n\t\t\t}\n\t\tcase \"database\":\n\t\t\tif cronjob.DBName == \"all\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdatabases := loadDbsForJob(cronjob)\n\t\t\tfor _, db := range databases {\n\t\t\t\titem.DBNames = append(item.DBNames, dto.TransHelper{Name: db.Database, DetailName: db.Name})\n\t\t\t}\n\t\tcase \"shell\":\n\t\t\tif cronjob.ScriptMode == \"library\" {\n\t\t\t\tscript, err := scriptRepo.Get(repo.WithByID(cronjob.ScriptID))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn \"\", err\n\t\t\t\t}\n\t\t\t\titem.ScriptName = script.Name\n\t\t\t}\n\t\tcase \"snapshot\":\n\t\t\tif len(cronjob.SnapshotRule) == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvar snapRule dto.SnapshotRule\n\t\t\tif err := json.Unmarshal([]byte(cronjob.SnapshotRule), &snapRule); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\titem.SnapshotRule.WithImage = snapRule.WithImage\n\t\t\tif len(snapRule.IgnoreAppIDs) != 0 {\n\t\t\t\tignoreApps, _ := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(snapRule.IgnoreAppIDs))\n\t\t\t\tfor _, app := range ignoreApps {\n\t\t\t\t\titem.SnapshotRule.IgnoreApps = append(item.SnapshotRule.IgnoreApps, dto.TransHelper{Name: app.App.Key, DetailName: app.Name})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\titem.SourceAccounts, item.DownloadAccount, _ = loadBackupNamesByID(cronjob.SourceAccountIDs, cronjob.DownloadAccountID)\n\t\talertInfo, _ := alertRepo.Get(alertRepo.WithByType(cronjob.Type), alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))), repo.WithByStatus(constant.AlertEnable))\n\t\tif alertInfo.SendCount != 0 {\n\t\t\titem.AlertCount = alertInfo.SendCount\n\t\t\titem.AlertTitle = alertInfo.Title\n\t\t\titem.AlertMethod = alertInfo.Method\n\t\t} else {\n\t\t\titem.AlertCount = 0\n\t\t}\n\t\tdata = append(data, item)\n\t}\n\titemJson, err := json.Marshal(&data)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(itemJson), nil\n}\n\nfunc (u *CronjobService) Import(req []dto.CronjobTrans) error {\n\tfor _, item := range req {\n\t\tcronjobItem, _ := cronjobRepo.Get(repo.WithByName(item.Name))\n\t\tif cronjobItem.ID != 0 {\n\t\t\tcontinue\n\t\t}\n\t\tcronjob := model.Cronjob{\n\t\t\tName:           item.Name,\n\t\t\tType:           item.Type,\n\t\t\tGroupID:        item.GroupID,\n\t\t\tSpecCustom:     item.SpecCustom,\n\t\t\tSpec:           item.Spec,\n\t\t\tExecutor:       item.Executor,\n\t\t\tScriptMode:     item.ScriptMode,\n\t\t\tCommand:        item.Command,\n\t\t\tContainerName:  item.ContainerName,\n\t\t\tUser:           item.User,\n\t\t\tURL:            item.URL,\n\t\t\tDBType:         item.DBType,\n\t\t\tExclusionRules: item.ExclusionRules,\n\t\t\tIsDir:          item.IsDir,\n\t\t\tRetainCopies:   item.RetainCopies,\n\t\t\tRetryTimes:     item.RetryTimes,\n\t\t\tTimeout:        item.Timeout,\n\t\t\tIgnoreErr:      item.IgnoreErr,\n\t\t\tSecret:         item.Secret,\n\t\t\tArgs:           item.Args,\n\t\t}\n\t\thasNotFound := false\n\t\tswitch item.Type {\n\t\tcase \"app\":\n\t\t\tif len(item.Apps) == 0 {\n\t\t\t\tcronjob.AppID = \"all\"\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvar appIDs []string\n\t\t\tfor _, app := range item.Apps {\n\t\t\t\tappItem, err := appInstallRepo.LoadInstallAppByKeyAndName(app.Name, app.DetailName)\n\t\t\t\tif err != nil {\n\t\t\t\t\thasNotFound = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tappIDs = append(appIDs, fmt.Sprintf(\"%v\", appItem.ID))\n\t\t\t}\n\t\t\tcronjob.AppID = strings.Join(appIDs, \",\")\n\t\tcase \"website\", \"cutWebsiteLog\":\n\t\t\tif len(item.Websites) == 0 {\n\t\t\t\tcronjob.Website = \"all\"\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvar webIDs []string\n\t\t\tfor _, web := range item.Websites {\n\t\t\t\twebItem, err := websiteRepo.GetFirst(websiteRepo.WithAlias(web))\n\t\t\t\tif err != nil {\n\t\t\t\t\thasNotFound = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\twebIDs = append(webIDs, fmt.Sprintf(\"%v\", webItem.ID))\n\t\t\t}\n\t\t\tcronjob.Website = strings.Join(webIDs, \",\")\n\t\tcase \"database\":\n\t\t\tif len(item.DBNames) == 0 {\n\t\t\t\tcronjob.DBName = \"all\"\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvar dbIDs []string\n\t\t\tif strings.Contains(cronjob.DBType, \"postgresql\") {\n\t\t\t\tfor _, db := range item.DBNames {\n\t\t\t\t\tdbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(db.Name), repo.WithByName(db.DetailName))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\thasNotFound = true\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tdbIDs = append(dbIDs, fmt.Sprintf(\"%v\", dbItem.ID))\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor _, db := range item.DBNames {\n\t\t\t\t\tdbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(db.Name), repo.WithByName(db.DetailName))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\thasNotFound = true\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tdbIDs = append(dbIDs, fmt.Sprintf(\"%v\", dbItem.ID))\n\t\t\t\t}\n\t\t\t}\n\t\t\tcronjob.DBName = strings.Join(dbIDs, \",\")\n\t\tcase \"shell\":\n\t\t\tif len(item.ContainerName) != 0 {\n\t\t\t\tcronjob.Script = item.Script\n\t\t\t\tclient, err := docker.NewDockerClient()\n\t\t\t\tif err != nil {\n\t\t\t\t\thasNotFound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tdefer client.Close()\n\t\t\t\tif _, err := client.ContainerStats(context.Background(), item.ContainerName, false); err != nil {\n\t\t\t\t\thasNotFound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tswitch item.ScriptMode {\n\t\t\tcase \"library\":\n\t\t\t\tlibrary, _ := scriptRepo.Get(repo.WithByName(item.ScriptName))\n\t\t\t\tif library.ID == 0 {\n\t\t\t\t\thasNotFound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcronjob.ScriptID = library.ID\n\t\t\tcase \"select\":\n\t\t\t\tif _, err := os.Stat(item.Script); err != nil {\n\t\t\t\t\thasNotFound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcronjob.Script = item.Script\n\t\t\tcase \"input\":\n\t\t\t\tcronjob.Script = item.Script\n\t\t\t}\n\t\tcase \"directory\":\n\t\t\tif item.IsDir {\n\t\t\t\tif _, err := os.Stat(item.SourceDir); err != nil {\n\t\t\t\t\thasNotFound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcronjob.SourceDir = item.SourceDir\n\t\t\t} else {\n\t\t\t\tfileList := strings.Split(item.SourceDir, \",\")\n\t\t\t\tvar newFiles []string\n\t\t\t\tfor _, item := range fileList {\n\t\t\t\t\tif len(item) == 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif _, err := os.Stat(item); err != nil {\n\t\t\t\t\t\thasNotFound = true\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tnewFiles = append(newFiles, item)\n\t\t\t\t}\n\t\t\t\tcronjob.SourceDir = strings.Join(newFiles, \",\")\n\t\t\t}\n\t\tcase \"snapshot\":\n\t\t\tif len(item.SnapshotRule.IgnoreApps) == 0 && !item.SnapshotRule.WithImage {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvar itemRules dto.SnapshotRule\n\t\t\titemRules.WithImage = item.SnapshotRule.WithImage\n\t\t\tfor _, app := range item.SnapshotRule.IgnoreApps {\n\t\t\t\tappItem, err := appInstallRepo.LoadInstallAppByKeyAndName(app.Name, app.DetailName)\n\t\t\t\tif err != nil {\n\t\t\t\t\thasNotFound = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\titemRules.IgnoreAppIDs = append(itemRules.IgnoreAppIDs, appItem.ID)\n\t\t\t}\n\t\t\titemRulesStr, _ := json.Marshal(itemRules)\n\t\t\tcronjob.SnapshotRule = string(itemRulesStr)\n\t\t}\n\t\tvar acIDs []string\n\t\tfor _, ac := range item.SourceAccounts {\n\t\t\tbackup, err := backupRepo.Get(repo.WithByName(ac))\n\t\t\tif err != nil {\n\t\t\t\thasNotFound = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif ac == item.DownloadAccount {\n\t\t\t\tcronjob.DownloadAccountID = backup.ID\n\t\t\t}\n\t\t\tacIDs = append(acIDs, fmt.Sprintf(\"%v\", backup.ID))\n\t\t}\n\t\tcronjob.SourceAccountIDs = strings.Join(acIDs, \",\")\n\t\tif hasNotFound {\n\t\t\tcronjob.Status = constant.StatusPending\n\t\t} else {\n\t\t\tcronjob.Status = constant.StatusDisable\n\t\t}\n\t\t_ = cronjobRepo.Create(&cronjob)\n\t\tif item.AlertCount != 0 && item.AlertTitle != \"\" && item.AlertMethod != \"\" {\n\t\t\tcreateAlert := dto.AlertCreate{\n\t\t\t\tTitle:     item.AlertTitle,\n\t\t\t\tSendCount: item.AlertCount,\n\t\t\t\tMethod:    item.AlertMethod,\n\t\t\t\tType:      cronjob.Type,\n\t\t\t\tProject:   strconv.Itoa(int(cronjob.ID)),\n\t\t\t\tStatus:    constant.AlertEnable,\n\t\t\t}\n\t\t\t_ = NewIAlertService().CreateAlert(createAlert)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) LoadScriptOptions() []dto.ScriptOptions {\n\tscripts, err := scriptRepo.List()\n\tif err != nil {\n\t\treturn nil\n\t}\n\tlang, _ := settingRepo.GetValueByKey(\"Language\")\n\tif len(lang) == 0 {\n\t\tlang = \"en\"\n\t}\n\tvar options []dto.ScriptOptions\n\tfor _, script := range scripts {\n\t\tvar item dto.ScriptOptions\n\t\titem.ID = script.ID\n\t\tvar translations = make(map[string]string)\n\t\t_ = json.Unmarshal([]byte(script.Name), &translations)\n\t\tif name, ok := translations[lang]; ok {\n\t\t\titem.Name = strings.ReplaceAll(name, \" \", \"_\")\n\t\t} else {\n\t\t\titem.Name = strings.ReplaceAll(script.Name, \" \", \"_\")\n\t\t}\n\t\toptions = append(options, item)\n\t}\n\treturn options\n}\n\nfunc (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interface{}, error) {\n\ttotal, records, err := cronjobRepo.PageRecords(\n\t\tsearch.Page,\n\t\tsearch.PageSize,\n\t\trepo.WithByStatus(search.Status),\n\t\tcronjobRepo.WithByJobID(search.CronjobID),\n\t\trepo.WithByDate(search.StartTime, search.EndTime))\n\tvar dtoCronjobs []dto.Record\n\tfor _, record := range records {\n\t\tvar item dto.Record\n\t\tif err := copier.Copy(&item, &record); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\titem.StartTime = record.StartTime.Format(constant.DateTimeLayout)\n\t\tdtoCronjobs = append(dtoCronjobs, item)\n\t}\n\treturn total, dtoCronjobs, err\n}\n\nfunc (u *CronjobService) LoadNextHandle(specStr string) ([]string, error) {\n\tspec := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)\n\tnow := time.Now()\n\tvar next [5]string\n\tif strings.HasPrefix(specStr, \"@every \") {\n\t\tduration := time.Minute\n\t\tif strings.HasSuffix(specStr, \"s\") {\n\t\t\tduration = time.Second\n\t\t}\n\t\tinterval := strings.ReplaceAll(specStr, \"@every \", \"\")\n\t\tinterval = strings.ReplaceAll(interval, \"s\", \"\")\n\t\tinterval = strings.ReplaceAll(interval, \"m\", \"\")\n\t\tdurationItem, err := strconv.Atoi(interval)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor i := 0; i < 5; i++ {\n\t\t\tnextTime := now.Add(time.Duration(durationItem) * duration)\n\t\t\tnext[i] = nextTime.Format(constant.DateTimeLayout)\n\t\t\tnow = nextTime\n\t\t}\n\t\treturn next[:], nil\n\t}\n\tsched, err := spec.Parse(specStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor i := 0; i < 5; i++ {\n\t\tnextTime := sched.Next(now)\n\t\tnext[i] = nextTime.Format(constant.DateTimeLayout)\n\t\tnow = nextTime\n\t}\n\treturn next[:], nil\n}\n\nfunc (u *CronjobService) LoadRecordLog(req dto.OperateByID) string {\n\trecord, err := cronjobRepo.GetRecord(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tif _, err := os.Stat(record.Records); err != nil {\n\t\treturn \"\"\n\t}\n\tcontent, err := os.ReadFile(record.Records)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn string(content)\n}\n\nfunc (u *CronjobService) CleanRecord(req dto.CronjobClean) error {\n\tcronjob, err := cronjobRepo.Get(repo.WithByID(req.CronjobID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif req.CleanData {\n\t\tif hasBackup(cronjob.Type) {\n\t\t\taccountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, \",\"))\n\t\t\tif !req.CleanRemoteData {\n\t\t\t\tfor key, val := range accountMap {\n\t\t\t\t\tif val.accountType != constant.Local {\n\t\t\t\t\t\tdelete(accountMap, key)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcronjob.RetainCopies = 0\n\t\t\tif len(accountMap) != 0 {\n\t\t\t\tu.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})\n\t\t\t}\n\t\t}\n\t}\n\tif req.IsDelete {\n\t\trecords, _ := backupRepo.ListRecord(backupRepo.WithByCronID(cronjob.ID))\n\t\tfor _, records := range records {\n\t\t\trecords.CronjobID = 0\n\t\t\t_ = backupRepo.UpdateRecord(&records)\n\t\t}\n\t}\n\tdelRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(req.CronjobID)))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, del := range delRecords {\n\t\t_ = os.RemoveAll(del.Records)\n\t}\n\tif err := cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(req.CronjobID))); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) HandleOnce(id uint) error {\n\tcronjob, _ := cronjobRepo.Get(repo.WithByID(id))\n\tif cronjob.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif cronjob.IsExecuting {\n\t\treturn buserr.New(\"InExecuting\")\n\t}\n\tu.HandleJob(&cronjob)\n\treturn nil\n}\n\nfunc (u *CronjobService) Create(req dto.CronjobOperate) error {\n\tcronjob, _ := cronjobRepo.Get(repo.WithByName(req.Name))\n\tif cronjob.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tcronjob.Secret = req.Secret\n\tif err := copier.Copy(&cronjob, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif cronjob.Type == \"snapshot\" {\n\t\trule, err := json.Marshal(req.SnapshotRule)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcronjob.SnapshotRule = string(rule)\n\t}\n\tif cronjob.Type == \"cutWebsiteLog\" {\n\t\tbackupAccount, err := backupRepo.Get(repo.WithByType(constant.Local))\n\t\tif backupAccount.ID == 0 {\n\t\t\treturn fmt.Errorf(\"load local backup dir failed, err: %v\", err)\n\t\t}\n\t\tcronjob.DownloadAccountID, cronjob.SourceAccountIDs = backupAccount.ID, fmt.Sprintf(\"%v\", backupAccount.ID)\n\t}\n\tcronjob.Status = constant.StatusEnable\n\n\tglobal.LOG.Infof(\"create cronjob %s successful, spec: %s\", cronjob.Name, cronjob.Spec)\n\tspec := cronjob.Spec\n\tentryIDs, err := u.StartJob(&cronjob, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcronjob.Spec = spec\n\tcronjob.EntryIDs = entryIDs\n\tif req.Type == \"cleanLog\" {\n\t\tconfig, _ := json.Marshal(req.CleanLogConfig)\n\t\tcronjob.Config = string(config)\n\t}\n\tif err := cronjobRepo.Create(&cronjob); err != nil {\n\t\treturn err\n\t}\n\tif req.AlertCount != 0 && req.AlertTitle != \"\" && req.AlertMethod != \"\" {\n\t\tcreateAlert := dto.AlertCreate{\n\t\t\tTitle:     req.AlertTitle,\n\t\t\tSendCount: req.AlertCount,\n\t\t\tMethod:    req.AlertMethod,\n\t\t\tType:      cronjob.Type,\n\t\t\tProject:   strconv.Itoa(int(cronjob.ID)),\n\t\t\tStatus:    constant.AlertEnable,\n\t\t}\n\t\terr := NewIAlertService().CreateAlert(createAlert)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error) {\n\tif len(cronjob.EntryIDs) != 0 && isUpdate {\n\t\tids := strings.Split(cronjob.EntryIDs, \",\")\n\t\tfor _, id := range ids {\n\t\t\tidItem, _ := strconv.Atoi(id)\n\t\t\tglobal.Cron.Remove(cron.EntryID(idItem))\n\t\t}\n\t}\n\tspecs := strings.Split(cronjob.Spec, \"&&\")\n\tvar ids []string\n\tfor _, spec := range specs {\n\t\tcronjob.Spec = spec\n\t\tentryID, err := u.AddCronJob(cronjob)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tids = append(ids, fmt.Sprintf(\"%v\", entryID))\n\t}\n\treturn strings.Join(ids, \",\"), nil\n}\n\nfunc (u *CronjobService) HandleStop(id uint) error {\n\trecord, _ := cronjobRepo.GetRecord(repo.WithByID(id))\n\tif record.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif len(record.TaskID) == 0 {\n\t\treturn nil\n\t}\n\tif cancel, ok := global.TaskCtxMap[record.TaskID]; ok {\n\t\tcancel()\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {\n\tfor _, id := range req.IDs {\n\t\tcronjob, _ := cronjobRepo.Get(repo.WithByID(id))\n\t\tif cronjob.ID == 0 {\n\t\t\treturn errors.New(\"find cronjob in db failed\")\n\t\t}\n\t\t_ = os.RemoveAll(path.Join(global.Dir.DataDir, \"task/shell\", cronjob.Name))\n\t\tids := strings.Split(cronjob.EntryIDs, \",\")\n\t\tfor _, id := range ids {\n\t\t\tidItem, _ := strconv.Atoi(id)\n\t\t\tglobal.Cron.Remove(cron.EntryID(idItem))\n\t\t}\n\t\tglobal.LOG.Infof(\"stop cronjob entryID: %s\", cronjob.EntryIDs)\n\t\tif err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData, CleanRemoteData: req.CleanRemoteData, IsDelete: true}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := cronjobRepo.Delete(repo.WithByID(id)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr := alertRepo.Delete(alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))), alertRepo.WithByType(cronjob.Type))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {\n\tvar cronjob model.Cronjob\n\tif err := copier.Copy(&cronjob, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif req.Type == \"snapshot\" {\n\t\titemRule, err := json.Marshal(req.SnapshotRule)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcronjob.SnapshotRule = string(itemRule)\n\t}\n\tcronModel, err := cronjobRepo.Get(repo.WithByID(id))\n\tif err != nil {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tupMap := make(map[string]interface{})\n\tcronjob.EntryIDs = cronModel.EntryIDs\n\tcronjob.Type = cronModel.Type\n\tspec := cronjob.Spec\n\tif cronModel.Status == constant.StatusEnable {\n\t\tnewEntryIDs, err := u.StartJob(&cronjob, true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tupMap[\"entry_ids\"] = newEntryIDs\n\t} else {\n\t\tids := strings.Split(cronjob.EntryIDs, \",\")\n\t\tfor _, id := range ids {\n\t\t\tidItem, _ := strconv.Atoi(id)\n\t\t\tglobal.Cron.Remove(cron.EntryID(idItem))\n\t\t}\n\t}\n\n\tif cronModel.Status == constant.StatusPending {\n\t\tupMap[\"status\"] = constant.StatusEnable\n\t}\n\tupMap[\"name\"] = req.Name\n\tupMap[\"group_id\"] = req.GroupID\n\tupMap[\"spec_custom\"] = req.SpecCustom\n\tupMap[\"spec\"] = spec\n\tupMap[\"script\"] = req.Script\n\tupMap[\"script_mode\"] = req.ScriptMode\n\tupMap[\"command\"] = req.Command\n\tupMap[\"container_name\"] = req.ContainerName\n\tupMap[\"executor\"] = req.Executor\n\tupMap[\"user\"] = req.User\n\n\tupMap[\"script_id\"] = req.ScriptID\n\tupMap[\"app_id\"] = req.AppID\n\tupMap[\"website\"] = req.Website\n\tupMap[\"exclusion_rules\"] = req.ExclusionRules\n\tupMap[\"db_type\"] = req.DBType\n\tupMap[\"db_name\"] = req.DBName\n\tupMap[\"url\"] = req.URL\n\tupMap[\"is_dir\"] = req.IsDir\n\tupMap[\"source_dir\"] = req.SourceDir\n\tupMap[\"snapshot_rule\"] = cronjob.SnapshotRule\n\n\tupMap[\"source_account_ids\"] = req.SourceAccountIDs\n\tupMap[\"download_account_id\"] = req.DownloadAccountID\n\tupMap[\"retain_copies\"] = req.RetainCopies\n\tupMap[\"retry_times\"] = req.RetryTimes\n\tupMap[\"timeout\"] = req.Timeout\n\tupMap[\"ignore_err\"] = req.IgnoreErr\n\tupMap[\"secret\"] = req.Secret\n\tupMap[\"args\"] = req.Args\n\terr = cronjobRepo.Update(id, upMap)\n\tif err != nil {\n\t\treturn err\n\t}\n\tupdateAlert := dto.AlertCreate{\n\t\tTitle:     req.AlertTitle,\n\t\tSendCount: req.AlertCount,\n\t\tMethod:    req.AlertMethod,\n\t\tType:      cronjob.Type,\n\t\tProject:   strconv.Itoa(int(cronModel.ID)),\n\t}\n\terr = NewIAlertService().ExternalUpdateAlert(updateAlert)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) UpdateStatus(id uint, status string) error {\n\tcronjob, _ := cronjobRepo.Get(repo.WithByID(id))\n\tif cronjob.ID == 0 {\n\t\treturn errors.WithMessage(buserr.New(\"ErrRecordNotFound\"), \"record not found\")\n\t}\n\tvar (\n\t\tentryIDs string\n\t\terr      error\n\t)\n\n\tif status == constant.StatusEnable {\n\t\tentryIDs, err = u.StartJob(&cronjob, true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tids := strings.Split(cronjob.EntryIDs, \",\")\n\t\tfor _, id := range ids {\n\t\t\tidItem, _ := strconv.Atoi(id)\n\t\t\tglobal.Cron.Remove(cron.EntryID(idItem))\n\t\t}\n\t\tglobal.LOG.Infof(\"stop cronjob entryID: %s\", cronjob.EntryIDs)\n\t}\n\treturn cronjobRepo.Update(cronjob.ID, map[string]interface{}{\"status\": status, \"entry_ids\": entryIDs})\n}\n\nfunc (u *CronjobService) UpdateGroup(req dto.ChangeGroup) error {\n\tcronjob, _ := cronjobRepo.Get(repo.WithByID(req.ID))\n\tif cronjob.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\treturn cronjobRepo.Update(cronjob.ID, map[string]interface{}{\"group_id\": req.GroupID})\n}\n\nfunc (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {\n\taddFunc := func() {\n\t\tu.HandleJob(cronjob)\n\t}\n\tglobal.LOG.Infof(\"add %s job %s successful\", cronjob.Type, cronjob.Name)\n\tentryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tglobal.LOG.Infof(\"start cronjob entryID: %d\", entryID)\n\treturn int(entryID), nil\n}\n"
  },
  {
    "path": "agent/app/service/cronjob_backup.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\nfunc (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {\n\tapps := loadAppsForJob(cronjob)\n\tif len(apps) == 0 {\n\t\taddSkipTask(\"App\", taskItem)\n\t\treturn nil\n\t}\n\taccountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, \",\"))\n\tif !accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].isOk {\n\t\treturn buserr.New(i18n.GetMsgWithDetail(\"LoadBackupFailed\", accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].message))\n\t}\n\tfor _, app := range apps {\n\t\tretry := 0\n\t\ttaskItem.AddSubTaskWithOps(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {\n\t\t\tvar record model.BackupRecord\n\t\t\trecord.Status = constant.StatusSuccess\n\t\t\trecord.From = \"cronjob\"\n\t\t\trecord.Type = \"app\"\n\t\t\trecord.CronjobID = cronjob.ID\n\t\t\trecord.Name = app.App.Key\n\t\t\trecord.DetailName = app.Name\n\t\t\trecord.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs\n\t\t\tbackupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf(\"tmp/app/%s/%s\", app.App.Key, app.Name))\n\t\t\trecord.FileName = simplifiedFileName(fmt.Sprintf(\"app_%s_%s.tar.gz\", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))\n\t\t\tif err := doAppBackup(&app, task, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {\n\t\t\t\tif retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {\n\t\t\t\t\tretry++\n\t\t\t\t\treturn err\n\t\t\t\t} else {\n\t\t\t\t\ttask.Log(i18n.GetMsgWithDetail(\"IgnoreBackupErr\", err.Error()))\n\t\t\t\t\tcleanAccountMap(accountMap)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsrc := path.Join(backupDir, record.FileName)\n\t\t\tdst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+\"/tmp/\")\n\t\t\tif err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {\n\t\t\t\tif retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {\n\t\t\t\t\tretry++\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\ttask.Log(i18n.GetMsgWithDetail(\"IgnoreUploadErr\", err.Error()))\n\t\t\t\tcleanAccountMap(accountMap)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\trecord.FileDir = path.Dir(dst)\n\t\t\tif err := backupRepo.CreateRecord(&record); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tu.removeExpiredBackup(cronjob, accountMap, record)\n\t\t\tcleanAccountMap(accountMap)\n\t\t\treturn nil\n\t\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {\n\twebs := loadWebsForJob(cronjob)\n\tif len(webs) == 0 {\n\t\taddSkipTask(\"Website\", taskItem)\n\t\treturn nil\n\t}\n\taccountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, \",\"))\n\tif !accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].isOk {\n\t\treturn buserr.New(i18n.GetMsgWithDetail(\"LoadBackupFailed\", accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].message))\n\t}\n\tfor _, web := range webs {\n\t\tretry := 0\n\t\ttaskItem.AddSubTaskWithOps(task.GetTaskName(web.Alias, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {\n\t\t\tvar record model.BackupRecord\n\t\t\trecord.Status = constant.StatusSuccess\n\t\t\trecord.From = \"cronjob\"\n\t\t\trecord.Type = \"website\"\n\t\t\trecord.CronjobID = cronjob.ID\n\t\t\trecord.Name = web.Alias\n\t\t\trecord.DetailName = web.Alias\n\t\t\trecord.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs\n\t\t\tbackupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf(\"tmp/website/%s\", web.Alias))\n\t\t\trecord.FileName = simplifiedFileName(fmt.Sprintf(\"website_%s_%s.tar.gz\", web.Alias, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))\n\n\t\t\tif err := doWebsiteBackup(&web, taskItem, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {\n\t\t\t\tif retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {\n\t\t\t\t\tretry++\n\t\t\t\t\treturn err\n\t\t\t\t} else {\n\t\t\t\t\ttask.Log(i18n.GetMsgWithDetail(\"IgnoreBackupErr\", err.Error()))\n\t\t\t\t\tcleanAccountMap(accountMap)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsrc := path.Join(backupDir, record.FileName)\n\t\t\tdst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+\"/tmp/\")\n\t\t\tif err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {\n\t\t\t\tif retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {\n\t\t\t\t\tretry++\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\ttask.Log(i18n.GetMsgWithDetail(\"IgnoreUploadErr\", err.Error()))\n\t\t\t\tcleanAccountMap(accountMap)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\trecord.FileDir = path.Dir(dst)\n\t\t\tif err := backupRepo.CreateRecord(&record); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tu.removeExpiredBackup(cronjob, accountMap, record)\n\t\t\tcleanAccountMap(accountMap)\n\t\t\treturn nil\n\t\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {\n\tdbs := loadDbsForJob(cronjob)\n\tif len(dbs) == 0 {\n\t\taddSkipTask(\"Database\", taskItem)\n\t\treturn nil\n\t}\n\taccountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, \",\"))\n\tif !accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].isOk {\n\t\treturn buserr.New(i18n.GetMsgWithDetail(\"LoadBackupFailed\", accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].message))\n\t}\n\tfor _, dbInfo := range dbs {\n\t\tretry := 0\n\t\titemName := fmt.Sprintf(\"%s[%s] - %s\", dbInfo.Database, dbInfo.DBType, dbInfo.Name)\n\t\ttaskItem.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {\n\t\t\tvar record model.BackupRecord\n\t\t\trecord.Status = constant.StatusSuccess\n\t\t\trecord.From = \"cronjob\"\n\t\t\trecord.Type = dbInfo.DBType\n\t\t\trecord.CronjobID = cronjob.ID\n\t\t\trecord.Name = dbInfo.Database\n\t\t\trecord.DetailName = dbInfo.Name\n\t\t\trecord.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs\n\n\t\t\tbackupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf(\"tmp/database/%s/%s/%s\", dbInfo.DBType, record.Name, dbInfo.Name))\n\t\t\trecord.FileName = simplifiedFileName(fmt.Sprintf(\"db_%s_%s.sql.gz\", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))\n\t\t\tif cronjob.DBType == \"mysql\" || cronjob.DBType == \"mariadb\" || cronjob.DBType == \"mysql-cluster\" {\n\t\t\t\tif err := doMysqlBackup(dbInfo, backupDir, record.FileName, cronjob.Secret); err != nil {\n\t\t\t\t\tif retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {\n\t\t\t\t\t\tretry++\n\t\t\t\t\t\treturn err\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttask.Log(i18n.GetMsgWithDetail(\"IgnoreBackupErr\", err.Error()))\n\t\t\t\t\t\tcleanAccountMap(accountMap)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := doPostgresqlBackup(dbInfo, backupDir, record.FileName, cronjob.Secret, taskItem); err != nil {\n\t\t\t\t\tif retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {\n\t\t\t\t\t\tretry++\n\t\t\t\t\t\treturn err\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttask.Log(i18n.GetMsgWithDetail(\"IgnoreBackupErr\", err.Error()))\n\t\t\t\t\t\tcleanAccountMap(accountMap)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsrc := path.Join(backupDir, record.FileName)\n\t\t\tdst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+\"/tmp/\")\n\t\t\tif err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {\n\t\t\t\tif retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {\n\t\t\t\t\tretry++\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\ttask.Log(i18n.GetMsgWithDetail(\"IgnoreUploadErr\", err.Error()))\n\t\t\t\tcleanAccountMap(accountMap)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\trecord.FileDir = path.Dir(dst)\n\t\t\tif err := backupRepo.CreateRecord(&record); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tu.removeExpiredBackup(cronjob, accountMap, record)\n\t\t\tcleanAccountMap(accountMap)\n\t\t\treturn nil\n\t\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {\n\taccountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, \",\"))\n\tif !accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].isOk {\n\t\treturn buserr.New(i18n.GetMsgWithDetail(\"LoadBackupFailed\", accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].message))\n\t}\n\ttaskItem.AddSubTaskWithOps(task.GetTaskName(cronjob.SourceDir, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {\n\t\tfileName := fmt.Sprintf(\"%s.tar.gz\", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(2))\n\t\tif cronjob.IsDir || len(strings.Split(cronjob.SourceDir, \",\")) == 1 {\n\t\t\tfileName = loadFileName(cronjob.SourceDir)\n\t\t}\n\t\tfileName = simplifiedFileName(fileName)\n\t\tbackupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf(\"tmp/%s/%s\", cronjob.Type, cronjob.Name))\n\n\t\tfileOp := files.NewFileOp()\n\t\tif cronjob.IsDir {\n\t\t\ttaskItem.Logf(\"Dir: %s, Excludes: %s\", cronjob.SourceDir, cronjob.ExclusionRules)\n\t\t\tif err := fileOp.TarGzCompressPro(true, cronjob.SourceDir, path.Join(backupDir, fileName), cronjob.Secret, cronjob.ExclusionRules); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\ttaskItem.Logf(\"Files: %s\", cronjob.SourceDir)\n\t\t\tfileLists := strings.Split(cronjob.SourceDir, \",\")\n\t\t\tif err := fileOp.TarGzFilesWithCompressPro(fileLists, path.Join(backupDir, fileName), cronjob.Secret); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tvar record model.BackupRecord\n\t\trecord.Status = constant.StatusSuccess\n\t\trecord.From = \"cronjob\"\n\t\trecord.Type = \"directory\"\n\t\trecord.CronjobID = cronjob.ID\n\t\trecord.Name = cronjob.Name\n\t\trecord.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs\n\n\t\tsrc := path.Join(backupDir, fileName)\n\t\tdst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+\"/tmp/\")\n\t\tif err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {\n\t\t\treturn err\n\t\t}\n\t\trecord.FileDir = path.Dir(dst)\n\t\trecord.FileName = fileName\n\t\tif err := backupRepo.CreateRecord(&record); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tu.removeExpiredBackup(cronjob, accountMap, record)\n\t\treturn nil\n\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n\treturn nil\n}\n\nfunc (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {\n\taccountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, \",\"))\n\tif !accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].isOk {\n\t\treturn buserr.New(i18n.GetMsgWithDetail(\"LoadBackupFailed\", accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].message))\n\t}\n\ttaskItem.AddSubTaskWithOps(task.GetTaskName(i18n.GetMsgByKey(\"SystemLog\"), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {\n\t\tnameItem := startTime.Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)\n\t\tfileName := fmt.Sprintf(\"system_log_%s.tar.gz\", nameItem)\n\t\tbackupDir := path.Join(global.Dir.LocalBackupDir, \"tmp/log\", nameItem)\n\t\tif err := handleBackupLogs(taskItem, backupDir, fileName, cronjob.Secret); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar record model.BackupRecord\n\t\trecord.Status = constant.StatusSuccess\n\t\trecord.From = \"cronjob\"\n\t\trecord.Type = \"log\"\n\t\trecord.CronjobID = cronjob.ID\n\t\trecord.Name = cronjob.Name\n\t\trecord.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs\n\n\t\tsrc := path.Join(path.Dir(backupDir), fileName)\n\t\tdst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+\"/tmp/\")\n\t\tif err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {\n\t\t\treturn err\n\t\t}\n\t\trecord.FileDir = path.Dir(dst)\n\t\trecord.FileName = fileName\n\t\tif err := backupRepo.CreateRecord(&record); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tu.removeExpiredBackup(cronjob, accountMap, record)\n\t\treturn nil\n\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n\treturn nil\n}\n\nfunc (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.JobRecords, taskItem *task.Task) error {\n\taccountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, \",\"))\n\tif !accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].isOk {\n\t\treturn buserr.New(i18n.GetMsgWithDetail(\"LoadBackupFailed\", accountMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].message))\n\t}\n\tvar record model.BackupRecord\n\trecord.Status = constant.StatusSuccess\n\trecord.From = \"cronjob\"\n\trecord.Type = \"snapshot\"\n\trecord.CronjobID = cronjob.ID\n\trecord.Name = cronjob.Name\n\trecord.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs\n\trecord.FileDir = \"system_snapshot\"\n\n\tversionItem, _ := settingRepo.Get(settingRepo.WithByKey(\"SystemVersion\"))\n\tscope := \"core\"\n\tif !global.IsMaster {\n\t\tscope = \"agent\"\n\t}\n\n\titemData, err := loadSnapWithRule(cronjob)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq := dto.SnapshotCreate{\n\t\tName:    fmt.Sprintf(\"snapshot-1panel-%s-%s-linux-%s-%s\", scope, versionItem.Value, loadOs(), jobRecord.StartTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)),\n\t\tSecret:  cronjob.Secret,\n\t\tTaskID:  jobRecord.TaskID,\n\t\tTimeout: cronjob.Timeout,\n\n\t\tSourceAccountIDs:  record.SourceAccountIDs,\n\t\tDownloadAccountID: cronjob.DownloadAccountID,\n\t\tAppData:           itemData.AppData,\n\t\tPanelData:         itemData.PanelData,\n\t\tBackupData:        itemData.BackupData,\n\t\tWithDockerConf:    true,\n\t\tWithMonitorData:   true,\n\t\tWithLoginLog:      true,\n\t\tWithOperationLog:  true,\n\t\tWithSystemLog:     true,\n\t\tWithTaskLog:       true,\n\t\tIgnoreFiles:       strings.Split(cronjob.ExclusionRules, \",\"),\n\t}\n\n\tif err := NewISnapshotService().SnapshotCreate(taskItem, req, jobRecord.ID, cronjob.RetryTimes); err != nil {\n\t\treturn err\n\t}\n\trecord.FileName = req.Name + \".tar.gz\"\n\n\tif err := backupRepo.CreateRecord(&record); err != nil {\n\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\treturn err\n\t}\n\tu.removeExpiredBackup(cronjob, accountMap, record)\n\treturn nil\n}\n\nfunc loadAppsForJob(cronjob model.Cronjob) []model.AppInstall {\n\tvar apps []model.AppInstall\n\tif cronjob.AppID == \"all\" {\n\t\tapps, _ = appInstallRepo.ListBy(context.Background())\n\t} else {\n\t\tappIds := strings.Split(cronjob.AppID, \",\")\n\t\tvar idItems []uint\n\t\tfor i := 0; i < len(appIds); i++ {\n\t\t\titemID, _ := strconv.Atoi(appIds[i])\n\t\t\tidItems = append(idItems, uint(itemID))\n\t\t}\n\t\tappItems, _ := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(idItems))\n\t\tapps = appItems\n\t}\n\treturn apps\n}\n\ntype DatabaseHelper struct {\n\tID       uint\n\tDBType   string\n\tDatabase string\n\tName     string\n\tArgs     []string\n}\n\nfunc addSkipTask(source string, taskItem *task.Task) {\n\ttaskItem.AddSubTask(task.GetTaskName(i18n.GetMsgByKey(source), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {\n\t\ttaskItem.Log(i18n.GetMsgByKey(\"NoSuchResource\"))\n\t\treturn nil\n\t}, nil)\n}\n\nfunc loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {\n\tvar dbs []DatabaseHelper\n\tif cronjob.DBName == \"all\" {\n\t\tif cronjob.DBType == \"mysql\" || cronjob.DBType == \"mariadb\" || cronjob.DBType == \"mysql-cluster\" {\n\t\t\tdatabaseService := NewIDatabaseService()\n\t\t\tmysqlItems, _ := databaseService.LoadItems(cronjob.DBType)\n\t\t\tfor _, mysql := range mysqlItems {\n\t\t\t\tdbs = append(dbs, DatabaseHelper{\n\t\t\t\t\tID:       mysql.ID,\n\t\t\t\t\tDBType:   cronjob.DBType,\n\t\t\t\t\tDatabase: mysql.Database,\n\t\t\t\t\tName:     mysql.Name,\n\t\t\t\t\tArgs:     strings.Split(cronjob.Args, \",\"),\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\tpgItems, _ := postgresqlRepo.List()\n\t\t\tfor _, pg := range pgItems {\n\t\t\t\tdbs = append(dbs, DatabaseHelper{\n\t\t\t\t\tID:       pg.ID,\n\t\t\t\t\tDBType:   cronjob.DBType,\n\t\t\t\t\tDatabase: pg.PostgresqlName,\n\t\t\t\t\tName:     pg.Name,\n\t\t\t\t\tArgs:     strings.Split(cronjob.Args, \",\"),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\treturn dbs\n\t}\n\tdbNames := strings.Split(cronjob.DBName, \",\")\n\tfor _, name := range dbNames {\n\t\titemID, _ := strconv.Atoi(name)\n\t\tif cronjob.DBType == \"mysql\" || cronjob.DBType == \"mariadb\" || cronjob.DBType == \"mysql-cluster\" {\n\t\t\tmysqlItem, _ := mysqlRepo.Get(repo.WithByID(uint(itemID)))\n\t\t\tdbs = append(dbs, DatabaseHelper{\n\t\t\t\tID:       mysqlItem.ID,\n\t\t\t\tDBType:   cronjob.DBType,\n\t\t\t\tDatabase: mysqlItem.MysqlName,\n\t\t\t\tName:     mysqlItem.Name,\n\t\t\t\tArgs:     strings.Split(cronjob.Args, \",\"),\n\t\t\t})\n\t\t} else {\n\t\t\tpgItem, _ := postgresqlRepo.Get(repo.WithByID(uint(itemID)))\n\t\t\tdbs = append(dbs, DatabaseHelper{\n\t\t\t\tID:       pgItem.ID,\n\t\t\t\tDBType:   cronjob.DBType,\n\t\t\t\tDatabase: pgItem.PostgresqlName,\n\t\t\t\tName:     pgItem.Name,\n\t\t\t\tArgs:     strings.Split(cronjob.Args, \",\"),\n\t\t\t})\n\t\t}\n\t}\n\treturn dbs\n}\n\nfunc loadWebsForJob(cronjob model.Cronjob) []model.Website {\n\tvar list []model.Website\n\tif cronjob.Website == \"all\" {\n\t\tlist, _ = websiteRepo.List()\n\t\treturn list\n\t}\n\twebsites := strings.Split(cronjob.Website, \",\")\n\tvar idItems []uint\n\tfor i := 0; i < len(websites); i++ {\n\t\titemID, _ := strconv.Atoi(websites[i])\n\t\tidItems = append(idItems, uint(itemID))\n\t}\n\tlist, _ = websiteRepo.GetBy(repo.WithByIDs(idItems))\n\treturn list\n}\n\nfunc handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret string) error {\n\tfileOp := files.NewFileOp()\n\twebsites, err := websiteRepo.List()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(websites) != 0 {\n\t\twebItem := GetOpenrestyDir(SitesRootDir)\n\t\tfor _, website := range websites {\n\t\t\ttaskItem.Logf(\"%s Website logs %s...\", i18n.GetMsgByKey(\"TaskBackup\"), website.Alias)\n\t\t\tdirItem := path.Join(targetDir, \"website\", website.Alias)\n\t\t\tif _, err := os.Stat(dirItem); err != nil && os.IsNotExist(err) {\n\t\t\t\tif err = os.MkdirAll(dirItem, os.ModePerm); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\titemDir := path.Join(webItem, website.Alias, \"log\")\n\t\t\tlogFiles, _ := os.ReadDir(itemDir)\n\t\t\tif len(logFiles) != 0 {\n\t\t\t\tfor i := 0; i < len(logFiles); i++ {\n\t\t\t\t\tif !logFiles[i].IsDir() {\n\t\t\t\t\t\t_ = fileOp.CopyFile(path.Join(itemDir, logFiles[i].Name()), dirItem)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\titemDir2 := path.Join(global.Dir.LocalBackupDir, \"tmp/log/website\", website.Alias)\n\t\t\tlogFiles2, _ := os.ReadDir(itemDir2)\n\t\t\tif len(logFiles2) != 0 {\n\t\t\t\tfor i := 0; i < len(logFiles2); i++ {\n\t\t\t\t\tif !logFiles2[i].IsDir() {\n\t\t\t\t\t\t_ = fileOp.CopyFile(path.Join(itemDir2, logFiles2[i].Name()), dirItem)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\ttaskItem.Logf(\"%s Website logs...\", i18n.GetMsgByKey(\"TaskBackup\"))\n\n\tsystemDir := path.Join(targetDir, \"system\")\n\tif _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(systemDir, os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tsystemLogFiles, _ := os.ReadDir(global.Dir.LogDir)\n\tif len(systemLogFiles) != 0 {\n\t\tfor i := 0; i < len(systemLogFiles); i++ {\n\t\t\tif !systemLogFiles[i].IsDir() {\n\t\t\t\t_ = fileOp.CopyFile(path.Join(global.Dir.LogDir, systemLogFiles[i].Name()), systemDir)\n\t\t\t}\n\t\t}\n\t}\n\ttaskItem.Logf(\"%s System logs...\", i18n.GetMsgByKey(\"TaskBackup\"))\n\n\tloginLogFiles, _ := os.ReadDir(\"/var/log\")\n\tloginDir := path.Join(targetDir, \"login\")\n\tif _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(loginDir, os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif len(loginLogFiles) != 0 {\n\t\tfor i := 0; i < len(loginLogFiles); i++ {\n\t\t\tif !loginLogFiles[i].IsDir() && (strings.HasPrefix(loginLogFiles[i].Name(), \"secure\") || strings.HasPrefix(loginLogFiles[i].Name(), \"auth.log\")) {\n\t\t\t\t_ = fileOp.CopyFile(path.Join(\"/var/log\", loginLogFiles[i].Name()), loginDir)\n\t\t\t}\n\t\t}\n\t}\n\ttaskItem.Logf(\"%s SSH logs...\", i18n.GetMsgByKey(\"TaskBackup\"))\n\n\tif err := fileOp.TarGzCompressPro(true, targetDir, path.Join(path.Dir(targetDir), fileName), secret, \"\"); err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t_ = os.RemoveAll(targetDir)\n\t}()\n\treturn nil\n}\n\nfunc loadSnapWithRule(cronjob model.Cronjob) (dto.SnapshotData, error) {\n\titemData, err := NewISnapshotService().LoadSnapshotData()\n\tif err != nil {\n\t\treturn itemData, err\n\t}\n\n\tif len(cronjob.SnapshotRule) == 0 {\n\t\treturn itemData, nil\n\t}\n\tvar snapRule dto.SnapshotRule\n\tif err := json.Unmarshal([]byte(cronjob.SnapshotRule), &snapRule); err != nil {\n\t\treturn itemData, err\n\t}\n\tif len(snapRule.IgnoreAppIDs) == 0 && !snapRule.WithImage {\n\t\treturn itemData, nil\n\t}\n\n\tvar ignoreApps []model.AppInstall\n\tif len(snapRule.IgnoreAppIDs) != 0 {\n\t\tignoreApps, _ = appInstallRepo.ListBy(context.Background(), repo.WithByIDs(snapRule.IgnoreAppIDs))\n\t}\n\tif len(ignoreApps) == 0 && !snapRule.WithImage {\n\t\treturn itemData, nil\n\t}\n\tfor i := 0; i < len(itemData.AppData); i++ {\n\t\tisIgnore := false\n\t\tfor _, ignore := range ignoreApps {\n\t\t\tif ignore.App.Key == itemData.AppData[i].Key && ignore.Name == itemData.AppData[i].Name {\n\t\t\t\tisIgnore = true\n\t\t\t\titemData.AppData[i].IsCheck = false\n\t\t\t\tfor j := 0; j < len(itemData.AppData[i].Children); j++ {\n\t\t\t\t\tif itemData.AppData[i].Children[j].Label == \"appData\" {\n\t\t\t\t\t\titemData.AppData[i].Children[j].IsCheck = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif snapRule.WithImage && !isIgnore {\n\t\t\tfor j := 0; j < len(itemData.AppData[i].Children); j++ {\n\t\t\t\tif itemData.AppData[i].Children[j].Label == \"appImage\" {\n\t\t\t\t\titemData.AppData[i].Children[j].IsCheck = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn itemData, nil\n}\n\nfunc loadFileName(src string) string {\n\tdirs := strings.Split(filepath.ToSlash(src), \"/\")\n\tvar keyPart string\n\tif len(dirs) >= 3 {\n\t\tkeyPart = filepath.Join(dirs[len(dirs)-3], dirs[len(dirs)-2], dirs[len(dirs)-1])\n\t}\n\tcleanName := strings.ReplaceAll(keyPart, string(filepath.Separator), \"_\")\n\ttimestamp := time.Now().Format(constant.DateTimeSlimLayout)\n\treturn fmt.Sprintf(\"%s_%s_%s.tar.gz\", cleanName, timestamp, common.RandStrAndNum(2))\n}\n\nfunc simplifiedFileName(name string) string {\n\tname = strings.ReplaceAll(name, \"/\", \"_\")\n\tname = strings.ReplaceAll(name, \":\", \"_\")\n\tname = strings.ReplaceAll(name, \"*\", \"_\")\n\tname = strings.ReplaceAll(name, \"?\", \"_\")\n\tname = strings.ReplaceAll(name, \"\\\"\", \"_\")\n\tname = strings.ReplaceAll(name, \"<\", \"_\")\n\tname = strings.ReplaceAll(name, \">\", \"_\")\n\tname = strings.ReplaceAll(name, \"|\", \"_\")\n\treturn name\n}\n\nfunc cleanAccountMap(accountMap map[string]backupClientHelper) {\n\tfor key, val := range accountMap {\n\t\tval.hasBackup = false\n\t\taccountMap[key] = val\n\t}\n}\n"
  },
  {
    "path": "agent/app/service/cronjob_helper.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\tpathUtils \"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/alert_push\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ntp\"\n)\n\nfunc (u *CronjobService) HandleJob(cronjob *model.Cronjob) {\n\tcronjobItem, _ := cronjobRepo.Get(repo.WithByID(cronjob.ID))\n\tif cronjobItem.IsExecuting {\n\t\tcronjobRepo.AddFailedRecord(cronjob.ID, i18n.GetMsgByKey(\"InExecuting\"))\n\t\treturn\n\t}\n\trecord := cronjobRepo.StartRecords(cronjob.ID)\n\ttaskItem, err := task.NewTaskWithOps(fmt.Sprintf(\"cronjob-%s\", cronjob.Name), task.TaskHandle, task.TaskScopeCronjob, record.TaskID, cronjob.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for exec shell failed, err: %v\", err)\n\t\treturn\n\t}\n\tif cronjob.Type == \"snapshot\" {\n\t\tgo func() {\n\t\t\t_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{\"records\": record.Records})\n\t\t\tif err := taskRepo.Save(context.Background(), taskItem.Task); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"save task for snapshot cronjob failed, err: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err = u.handleSnapshot(*cronjob, record, taskItem); err != nil {\n\t\t\t\tif len(taskItem.Task.CurrentStep) == 0 {\n\t\t\t\t\ttaskItem.Log(err.Error())\n\t\t\t\t\ttaskItem.Task.Status = constant.StatusFailed\n\t\t\t\t\ttaskItem.Task.ErrorMsg = err.Error()\n\t\t\t\t\ttaskItem.Task.EndAt = time.Now()\n\t\t\t\t\t_ = taskRepo.Save(context.Background(), taskItem.Task)\n\t\t\t\t}\n\t\t\t\tcronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)\n\t\t\t\thandleCronJobAlert(cronjob)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcronjobRepo.EndRecords(record, constant.StatusSuccess, \"\", record.Records)\n\t\t}()\n\t\treturn\n\t}\n\tif err = u.loadTask(cronjob, &record, taskItem); err != nil {\n\t\tglobal.LOG.Debugf(\"prepare to handle cron job [%s] %s failed, err: %v\", cronjob.Type, cronjob.Name, err)\n\t\titem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))\n\t\tif len(item.ID) == 0 {\n\t\t\trecord.TaskID = \"\"\n\t\t}\n\t\tcronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)\n\t\thandleCronJobAlert(cronjob)\n\t\treturn\n\t}\n\tgo func() {\n\t\tif err := taskItem.Execute(); err != nil {\n\t\t\ttaskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))\n\t\t\tif len(taskItem.ID) == 0 {\n\t\t\t\trecord.TaskID = \"\"\n\t\t\t}\n\t\t\tcronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)\n\t\t\thandleCronJobAlert(cronjob)\n\t\t} else {\n\t\t\tcronjobRepo.EndRecords(record, constant.StatusSuccess, \"\", record.Records)\n\t\t}\n\t}()\n}\n\nfunc (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecords, taskItem *task.Task) error {\n\tvar err error\n\tswitch cronjob.Type {\n\tcase \"shell\":\n\t\tif cronjob.ScriptMode == \"library\" {\n\t\t\tscriptItem, _ := scriptRepo.Get(repo.WithByID(cronjob.ScriptID))\n\t\t\tif scriptItem.ID == 0 {\n\t\t\t\treturn fmt.Errorf(\"load script from db failed, err: %v\", err)\n\t\t\t}\n\t\t\tcronjob.Script = scriptItem.Script\n\t\t\tcronjob.ScriptMode = \"input\"\n\t\t}\n\t\tif len(cronjob.Script) == 0 {\n\t\t\treturn fmt.Errorf(\"the script content is empty and is skipped\")\n\t\t}\n\t\tu.handleShell(*cronjob, taskItem)\n\t\tu.removeExpiredLog(*cronjob)\n\tcase \"curl\":\n\t\tif len(cronjob.URL) == 0 {\n\t\t\treturn fmt.Errorf(\"the url is empty and is skipped\")\n\t\t}\n\t\tu.handleCurl(*cronjob, taskItem)\n\t\tu.removeExpiredLog(*cronjob)\n\tcase \"ntp\":\n\t\tu.handleNtpSync(*cronjob, taskItem)\n\t\tu.removeExpiredLog(*cronjob)\n\tcase \"cutWebsiteLog\":\n\t\terr = u.handleCutWebsiteLog(cronjob, record.StartTime, taskItem)\n\tcase \"clean\":\n\t\tu.handleSystemClean(*cronjob, taskItem)\n\t\tu.removeExpiredLog(*cronjob)\n\tcase \"website\":\n\t\terr = u.handleWebsite(*cronjob, record.StartTime, taskItem)\n\tcase \"app\":\n\t\terr = u.handleApp(*cronjob, record.StartTime, taskItem)\n\tcase \"database\":\n\t\terr = u.handleDatabase(*cronjob, record.StartTime, taskItem)\n\tcase \"directory\":\n\t\tif len(cronjob.SourceDir) == 0 {\n\t\t\treturn fmt.Errorf(\"the source dir is empty and is skipped\")\n\t\t}\n\t\terr = u.handleDirectory(*cronjob, record.StartTime, taskItem)\n\tcase \"log\":\n\t\terr = u.handleSystemLog(*cronjob, record.StartTime, taskItem)\n\tcase \"syncIpGroup\":\n\t\tu.handleSyncIpGroup(*cronjob, taskItem)\n\tcase \"cleanLog\":\n\t\tu.handleCleanLog(*cronjob, taskItem)\n\t}\n\treturn err\n}\n\nfunc (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task) {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithContext(taskItem.TaskCtx))\n\n\ttaskItem.AddSubTaskWithOps(i18n.GetWithName(\"HandleShell\", cronjob.Name), func(t *task.Task) error {\n\t\tif len(cronjob.ContainerName) != 0 {\n\t\t\tscriptItem := cronjob.Script\n\t\t\tif cronjob.ScriptMode == \"select\" {\n\t\t\t\tscriptItem = pathUtils.Join(\"/tmp\", pathUtils.Base(cronjob.Script))\n\t\t\t\tif err := cmdMgr.Run(\"docker\", \"cp\", cronjob.Script, cronjob.ContainerName+\":\"+scriptItem); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tcommand := \"sh\"\n\t\t\tif len(cronjob.Command) != 0 {\n\t\t\t\tcommand = cronjob.Command\n\t\t\t}\n\t\t\tif len(cronjob.User) != 0 {\n\t\t\t\treturn cmdMgr.Run(\"docker\", \"exec\", \"-u\", cronjob.User, cronjob.ContainerName, command, \"-c\", scriptItem)\n\t\t\t}\n\t\t\treturn cmdMgr.Run(\"docker\", \"exec\", cronjob.ContainerName, command, \"-c\", scriptItem)\n\t\t}\n\t\tif len(cronjob.Executor) == 0 {\n\t\t\tcronjob.Executor = \"bash\"\n\t\t}\n\t\tif cronjob.ScriptMode == \"input\" {\n\t\t\tsuffix := \".sh\"\n\t\t\tif strings.HasPrefix(cronjob.Executor, \"python\") {\n\t\t\t\tsuffix = \".py\"\n\t\t\t}\n\t\t\tfileItem := pathUtils.Join(global.Dir.DataDir, \"task\", \"shell\", cronjob.Name, cronjob.Name+suffix)\n\t\t\t_ = os.MkdirAll(pathUtils.Dir(fileItem), os.ModePerm)\n\t\t\tshellFile, err := os.OpenFile(fileItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.DirPerm)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer shellFile.Close()\n\t\t\tif _, err := shellFile.WriteString(cronjob.Script); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif len(cronjob.User) == 0 {\n\t\t\t\treturn cmdMgr.Run(cronjob.Executor, fileItem)\n\t\t\t}\n\t\t\treturn cmdMgr.Run(\"sudo\", \"-u\", cronjob.User, cronjob.Executor, fileItem)\n\t\t}\n\t\tif len(cronjob.User) == 0 {\n\t\t\treturn cmdMgr.Run(cronjob.Executor, cronjob.Script)\n\t\t}\n\t\tif err := cmdMgr.Run(\"sudo\", \"-u\", cronjob.User, cronjob.Executor, cronjob.Script); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n}\n\nfunc (u *CronjobService) handleCurl(cronjob model.Cronjob, taskItem *task.Task) {\n\turls := strings.Split(cronjob.URL, \",\")\n\tfor _, url := range urls {\n\t\tif len(strings.TrimSpace(url)) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\ttaskItem.AddSubTaskWithOps(i18n.GetWithName(\"HandleCurl\", url), func(t *task.Task) error {\n\t\t\ttaskItem.LogStart(i18n.GetWithName(\"HandleCurl\", url))\n\t\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))\n\t\t\treturn cmdMgr.Run(\"curl\", url)\n\t\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n\t}\n}\n\nfunc (u *CronjobService) handleNtpSync(cronjob model.Cronjob, taskItem *task.Task) {\n\ttaskItem.AddSubTaskWithOps(i18n.GetMsgByKey(\"HandleNtpSync\"), func(t *task.Task) error {\n\t\tntpServer, err := settingRepo.Get(settingRepo.WithByKey(\"NtpSite\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttaskItem.Logf(\"ntp server: %s\", ntpServer.Value)\n\t\tntime, err := ntp.GetRemoteTime(ntpServer.Value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := ntp.UpdateSystemTime(ntime.Format(constant.DateTimeLayout)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n}\n\nfunc (u *CronjobService) handleCleanLog(cronjob model.Cronjob, taskItem *task.Task) {\n\ttaskItem.AddSubTaskWithOps(i18n.GetWithName(\"CleanLog\", cronjob.Name), func(t *task.Task) error {\n\t\tconfig := GetCleanLogConfig(cronjob)\n\t\tfor _, scope := range config.Scopes {\n\t\t\tswitch scope {\n\t\t\tcase \"website\":\n\t\t\t\twebsites, _ := websiteRepo.List()\n\t\t\t\tfor _, website := range websites {\n\t\t\t\t\tcurStr := i18n.GetWithName(\"CleanLogByName\", website.PrimaryDomain)\n\t\t\t\t\tt.LogStart(curStr)\n\t\t\t\t\taccessLogPath := GetSitePath(website, SiteAccessLog)\n\t\t\t\t\tif err := os.Truncate(accessLogPath, 0); err != nil {\n\t\t\t\t\t\tt.LogFailedWithErr(curStr, err)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\terrLogPath := GetSitePath(website, SiteErrorLog)\n\t\t\t\t\tif err := os.Truncate(errLogPath, 0); err != nil {\n\t\t\t\t\t\tt.LogFailedWithErr(curStr, err)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tt.LogSuccess(curStr)\n\t\t\t\t}\n\t\t\t\tappInstall, _ := getAppInstallByKey(constant.AppOpenresty)\n\t\t\t\tif appInstall.ID > 0 {\n\t\t\t\t\tcurStr := i18n.GetWithName(\"CleanLogByName\", \"OpenResty\")\n\t\t\t\t\tt.LogStart(curStr)\n\t\t\t\t\taccessLogPath := pathUtils.Join(appInstall.GetPath(), \"log\", \"access.log\")\n\t\t\t\t\tif err := os.Truncate(accessLogPath, 0); err != nil {\n\t\t\t\t\t\tt.LogFailedWithErr(curStr, err)\n\t\t\t\t\t}\n\t\t\t\t\terrLogPath := pathUtils.Join(appInstall.GetPath(), \"log\", \"error.log\")\n\t\t\t\t\tif err := os.Truncate(errLogPath, 0); err != nil {\n\t\t\t\t\t\tt.LogFailedWithErr(curStr, err)\n\t\t\t\t\t}\n\t\t\t\t\tt.LogSuccess(curStr)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n}\n\nfunc (u *CronjobService) handleSyncIpGroup(cronjob model.Cronjob, taskItem *task.Task) {\n\ttaskItem.AddSubTaskWithOps(i18n.GetWithName(\"SyncIpGroup\", cronjob.Name), func(t *task.Task) error {\n\t\tappInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tipGroupDir := pathUtils.Join(appInstall.GetPath(), \"1pwaf\", \"data\", \"rules\", \"ip_group\")\n\t\turlDir := pathUtils.Join(ipGroupDir, \"ip_group_url\")\n\t\t_, err = os.Stat(urlDir)\n\t\tif err != nil && os.IsNotExist(err) {\n\t\t\treturn buserr.New(\"WafIpGroupNotFound\")\n\t\t}\n\n\t\turlsFiles, err := os.ReadDir(urlDir)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, file := range urlsFiles {\n\t\t\tif file.IsDir() || !strings.HasSuffix(file.Name(), \"_url\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\turlFilePath := filepath.Join(urlDir, file.Name())\n\n\t\t\turlContent, err := os.ReadFile(urlFilePath)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tremoteURL := strings.TrimSpace(string(urlContent))\n\t\t\tif remoteURL == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tresp, err := http.Get(remoteURL)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tipGroupFile := strings.TrimSuffix(file.Name(), \"_url\")\n\t\t\tipGroupPath := filepath.Join(ipGroupDir, ipGroupFile)\n\n\t\t\tif resp.StatusCode != http.StatusOK {\n\t\t\t\tresp.Body.Close()\n\t\t\t\ttaskItem.Logf(\"get remote ip group %s failed %s status code %d\", ipGroupFile, remoteURL, resp.StatusCode)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\toutFile, err := os.OpenFile(ipGroupPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)\n\t\t\tif err != nil {\n\t\t\t\tresp.Body.Close()\n\t\t\t\ttaskItem.Logf(\"sync %s failed %v\", ipGroupFile, err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\twriter := bufio.NewWriter(outFile)\n\t\t\t_, err = io.Copy(writer, resp.Body)\n\t\t\tif err != nil {\n\t\t\t\toutFile.Close()\n\t\t\t\tresp.Body.Close()\n\t\t\t\ttaskItem.Logf(\"sync %s failed , write file failed %v\", ipGroupFile, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\twriter.Flush()\n\t\t\toutFile.Close()\n\t\t\tresp.Body.Close()\n\t\t\ttaskItem.LogSuccess(i18n.Get(\"TaskSync\") + \" \" + ipGroupFile)\n\t\t}\n\t\tif err := opNginx(appInstall.ContainerName, constant.NginxReload); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n}\n\nfunc (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time, taskItem *task.Task) error {\n\tclientMap := NewBackupClientMap([]string{fmt.Sprintf(\"%v\", cronjob.DownloadAccountID)})\n\tif !clientMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].isOk {\n\t\treturn buserr.New(i18n.GetMsgWithDetail(\"LoadBackupFailed\", clientMap[fmt.Sprintf(\"%d\", cronjob.DownloadAccountID)].message))\n\t}\n\ttaskItem.AddSubTaskWithOps(i18n.GetWithName(\"CutWebsiteLog\", cronjob.Name), func(t *task.Task) error {\n\t\twebsites := loadWebsForJob(*cronjob)\n\t\tfileOp := files.NewFileOp()\n\t\tbaseDir := GetOpenrestyDir(SitesRootDir)\n\t\tfor _, website := range websites {\n\t\t\ttaskItem.Log(website.Alias)\n\t\t\tvar record model.BackupRecord\n\t\t\trecord.Status = constant.StatusSuccess\n\t\t\trecord.From = \"cronjob\"\n\t\t\trecord.Type = \"cut-website-log\"\n\t\t\trecord.CronjobID = cronjob.ID\n\t\t\trecord.Name = website.Alias\n\t\t\trecord.DetailName = website.Alias\n\t\t\trecord.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs\n\t\t\tbackupDir := pathUtils.Join(global.Dir.LocalBackupDir, \"log\", \"website\", website.Alias)\n\t\t\tif !fileOp.Stat(backupDir) {\n\t\t\t\t_ = os.MkdirAll(backupDir, constant.DirPerm)\n\t\t\t}\n\t\t\trecord.FileDir = strings.TrimPrefix(backupDir, global.Dir.LocalBackupDir+\"/\")\n\t\t\trecord.FileName = fmt.Sprintf(\"%s_log_%s.gz\", website.PrimaryDomain, startTime.Format(constant.DateTimeSlimLayout))\n\t\t\tif err := backupRepo.CreateRecord(&record); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"save backup record failed, err: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\twebsiteLogDir := pathUtils.Join(baseDir, website.Alias, \"log\")\n\t\t\tsrcAccessLogPath := pathUtils.Join(websiteLogDir, \"access.log\")\n\t\t\tsrcErrorLogPath := pathUtils.Join(websiteLogDir, \"error.log\")\n\n\t\t\tdstFilePath := pathUtils.Join(backupDir, record.FileName)\n\t\t\tif err := backupLogFile(dstFilePath, websiteLogDir, fileOp); err != nil {\n\t\t\t\ttaskItem.LogFailedWithErr(\"CutWebsiteLog\", err)\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\t_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(\"\"), constant.DirPerm)\n\t\t\t\t_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(\"\"), constant.DirPerm)\n\t\t\t}\n\t\t\ttaskItem.Log(i18n.GetMsgWithMap(\"CutWebsiteLogSuccess\", map[string]interface{}{\"name\": website.PrimaryDomain, \"path\": dstFilePath}))\n\t\t\tu.removeExpiredBackup(*cronjob, clientMap, record)\n\t\t}\n\t\treturn nil\n\t}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n\treturn nil\n}\n\nfunc backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error {\n\tcmdMgr := cmd.NewCommandMgr()\n\tif err := cmdMgr.RunBashCf(\"tar -czf %s -C %s %s\", dstFilePath, websiteLogDir, strings.Join([]string{\"access.log\", \"error.log\"}, \" \")); err != nil {\n\t\tdstDir := pathUtils.Dir(dstFilePath)\n\t\tif err = fileOp.Copy(pathUtils.Join(websiteLogDir, \"access.log\"), dstDir); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = fileOp.Copy(pathUtils.Join(websiteLogDir, \"error.log\"), dstDir); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = cmdMgr.RunBashCf(\"tar -czf %s -C %s %s\", dstFilePath, dstDir, strings.Join([]string{\"access.log\", \"error.log\"}, \" \")); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = fileOp.DeleteFile(pathUtils.Join(dstDir, \"access.log\"))\n\t\t_ = fileOp.DeleteFile(pathUtils.Join(dstDir, \"error.log\"))\n\t\treturn nil\n\t}\n\treturn nil\n}\n\nfunc (u *CronjobService) handleSystemClean(cronjob model.Cronjob, taskItem *task.Task) {\n\tcleanTask := doSystemClean(taskItem)\n\ttaskItem.AddSubTaskWithOps(i18n.GetMsgByKey(\"HandleSystemClean\"), cleanTask, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)\n}\n\nfunc (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]backupClientHelper, record model.BackupRecord) {\n\tvar opts []repo.DBOption\n\topts = append(opts, repo.WithByFrom(\"cronjob\"))\n\topts = append(opts, backupRepo.WithByCronID(cronjob.ID))\n\topts = append(opts, repo.WithOrderDesc(\"created_at\"))\n\tif record.ID != 0 {\n\t\topts = append(opts, repo.WithByType(record.Type))\n\t\topts = append(opts, repo.WithByName(record.Name))\n\t\topts = append(opts, repo.WithByDetailName(record.DetailName))\n\t}\n\trecords, _ := backupRepo.ListRecord(opts...)\n\tif len(records) <= int(cronjob.RetainCopies) {\n\t\treturn\n\t}\n\tfor i := int(cronjob.RetainCopies); i < len(records); i++ {\n\t\taccounts := strings.Split(cronjob.SourceAccountIDs, \",\")\n\t\tif cronjob.Type == \"snapshot\" {\n\t\t\tfor _, account := range accounts {\n\t\t\t\tif len(account) != 0 {\n\t\t\t\t\tif _, ok := accountMap[account]; !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif !accountMap[account].isOk {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\t_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, \"system_snapshot\", records[i].FileName))\n\t\t\t\t}\n\t\t\t}\n\t\t\t_ = snapshotRepo.Delete(repo.WithByName(strings.TrimSuffix(records[i].FileName, \".tar.gz\")))\n\t\t} else {\n\t\t\tfor _, account := range accounts {\n\t\t\t\tif len(account) != 0 {\n\t\t\t\t\tif _, ok := accountMap[account]; !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif !accountMap[account].isOk {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\t_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_ = backupRepo.DeleteRecord(context.Background(), repo.WithByID(records[i].ID))\n\t}\n}\n\nfunc (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {\n\trecords, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), repo.WithOrderDesc(\"created_at\"))\n\tif len(records) <= int(cronjob.RetainCopies) {\n\t\treturn\n\t}\n\tfor i := int(cronjob.RetainCopies); i < len(records); i++ {\n\t\tif len(records[i].File) != 0 {\n\t\t\tfiles := strings.Split(records[i].File, \",\")\n\t\t\tfor _, file := range files {\n\t\t\t\t_ = os.Remove(file)\n\t\t\t}\n\t\t}\n\t\t_ = cronjobRepo.DeleteRecord(repo.WithByID(records[i].ID))\n\t\t_ = taskRepo.Delete(taskRepo.WithByID(records[i].TaskID))\n\t\t_ = os.Remove(pathUtils.Join(global.CONF.Base.InstallDir, \"1panel/log/task/Cronjob\", records[i].TaskID+\".log\"))\n\t}\n}\n\nfunc hasBackup(cronjobType string) bool {\n\treturn cronjobType == \"app\" || cronjobType == \"database\" || cronjobType == \"website\" || cronjobType == \"directory\" || cronjobType == \"snapshot\" || cronjobType == \"log\" || cronjobType == \"cutWebsiteLog\"\n}\n\nfunc handleCronJobAlert(cronjob *model.Cronjob) {\n\tpushAlert := dto.PushAlert{\n\t\tTaskName:  cronjob.Name,\n\t\tAlertType: cronjob.Type,\n\t\tEntryID:   cronjob.ID,\n\t\tParam:     cronjob.Type,\n\t}\n\t_ = alert_push.PushAlert(pushAlert)\n}\n\nfunc GetCleanLogConfig(cronJob model.Cronjob) dto.CleanLogConfig {\n\tconfig := &dto.CleanLogConfig{}\n\t_ = json.Unmarshal([]byte(cronJob.Config), config)\n\treturn *config\n}\n"
  },
  {
    "path": "agent/app/service/dashboard.go",
    "content": "package service\n\nimport (\n\t\"cmp\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\tnetwork \"net\"\n\t\"os\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/copier\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/psutil\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/shirou/gopsutil/v4/disk\"\n\t\"github.com/shirou/gopsutil/v4/load\"\n\t\"github.com/shirou/gopsutil/v4/mem\"\n\t\"github.com/shirou/gopsutil/v4/net\"\n)\n\ntype DashboardService struct{}\n\ntype IDashboardService interface {\n\tLoadOsInfo() (*dto.OsInfo, error)\n\tLoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)\n\tLoadCurrentInfoForNode() *dto.NodeCurrent\n\tLoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent\n\tLoadTopCPU() []dto.Process\n\tLoadTopMem() []dto.Process\n\n\tLoadQuickOptions() []dto.QuickJump\n\tChangeQuick(req dto.ChangeQuicks) error\n\n\tLoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error)\n\tChangeShow(req dto.SettingUpdate) error\n\tListLauncherOption(filter string) ([]dto.LauncherOption, error)\n\tRestart(operation string) error\n}\n\nfunc NewIDashboardService() IDashboardService {\n\treturn &DashboardService{}\n}\n\nfunc (u *DashboardService) Restart(operation string) error {\n\tswitch operation {\n\tcase \"system\":\n\t\t{\n\t\t\tgo func() {\n\t\t\t\tif err := cmd.RunDefaultBashCf(\"%s reboot\", cmd.SudoHandleCmd()); err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"handle reboot failed, %v\", err)\n\t\t\t\t}\n\t\t\t}()\n\t\t\treturn nil\n\t\t}\n\tcase \"1panel-agent\":\n\t\tcontroller.RestartPanel(false, true, false)\n\t\treturn nil\n\tcase \"1panel\":\n\t\tcontroller.RestartPanel(true, true, false)\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"handle restart operation %s failed, err: nonsupport such operation\", operation)\n\t}\n}\n\nfunc (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {\n\tvar baseInfo dto.OsInfo\n\thostInfo, err := psutil.HOST.GetHostInfo(false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbaseInfo.OS = hostInfo.OS\n\tbaseInfo.Platform = hostInfo.Platform\n\tbaseInfo.PlatformFamily = hostInfo.PlatformFamily\n\tbaseInfo.KernelArch = hostInfo.KernelArch\n\tbaseInfo.KernelVersion = hostInfo.KernelVersion\n\tbaseInfo.PrettyDistro = psutil.HOST.GetDistro()\n\n\tdiskInfo, err := psutil.DISK.GetUsage(global.Dir.BaseDir, false)\n\tif err == nil {\n\t\tbaseInfo.DiskSize = int64(diskInfo.Free)\n\t}\n\n\tif baseInfo.KernelArch == \"armv7l\" {\n\t\tbaseInfo.KernelArch = \"armv7\"\n\t}\n\tif baseInfo.KernelArch == \"x86_64\" {\n\t\tbaseInfo.KernelArch = \"amd64\"\n\t}\n\treturn &baseInfo, nil\n}\n\nfunc (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {\n\tvar currentInfo dto.NodeCurrent\n\n\tcurrentInfo.CPUTotal, _ = psutil.CPUInfo.GetLogicalCores(false)\n\n\tcpuUsedPercent, perCore, cpuDetailedPercent := psutil.CPU.GetCPUUsage()\n\tif len(perCore) == 0 {\n\t\tcurrentInfo.CPUTotal = psutil.CPU.NumCPU()\n\t} else {\n\t\tcurrentInfo.CPUTotal = len(perCore)\n\t}\n\tcurrentInfo.CPUUsedPercent = cpuUsedPercent\n\tcurrentInfo.CPUUsed = cpuUsedPercent * 0.01 * float64(currentInfo.CPUTotal)\n\tcurrentInfo.CPUDetailedPercent = cpuDetailedPercent\n\n\tloadInfo, _ := load.Avg()\n\tcurrentInfo.Load1 = loadInfo.Load1\n\tcurrentInfo.Load5 = loadInfo.Load5\n\tcurrentInfo.Load15 = loadInfo.Load15\n\tcurrentInfo.LoadUsagePercent = loadInfo.Load1 / (float64(currentInfo.CPUTotal*2) * 0.75) * 100\n\n\tmemoryInfo, _ := mem.VirtualMemory()\n\tcurrentInfo.MemoryTotal = memoryInfo.Total\n\tcurrentInfo.MemoryAvailable = memoryInfo.Available\n\tcurrentInfo.MemoryUsed = memoryInfo.Used\n\tcurrentInfo.MemoryUsedPercent = memoryInfo.UsedPercent\n\n\tswapInfo, _ := mem.SwapMemory()\n\tcurrentInfo.SwapMemoryTotal = swapInfo.Total\n\tcurrentInfo.SwapMemoryAvailable = swapInfo.Free\n\tcurrentInfo.SwapMemoryUsed = swapInfo.Used\n\tcurrentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent\n\n\treturn &currentInfo\n}\n\nfunc (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error) {\n\tvar baseInfo dto.DashboardBase\n\thostInfo, err := psutil.HOST.GetHostInfo(true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tss, _ := json.Marshal(hostInfo)\n\tbaseInfo = dto.DashboardBase{\n\t\tHostname:             hostInfo.Hostname,\n\t\tOS:                   hostInfo.OS,\n\t\tPlatform:             hostInfo.Platform,\n\t\tPlatformFamily:       hostInfo.PlatformFamily,\n\t\tPlatformVersion:      hostInfo.PlatformVersion,\n\t\tPrettyDistro:         psutil.HOST.GetDistro(),\n\t\tKernelArch:           hostInfo.KernelArch,\n\t\tKernelVersion:        hostInfo.KernelVersion,\n\t\tVirtualizationSystem: string(ss),\n\t\tIpV4Addr:             loadOutboundIP(),\n\t\tSystemProxy:          \"noProxy\",\n\t}\n\n\tif proxy := cmp.Or(os.Getenv(\"http_proxy\"), os.Getenv(\"HTTP_PROXY\")); proxy != \"\" {\n\t\tbaseInfo.SystemProxy = proxy\n\t}\n\n\tloadQuickJump(&baseInfo)\n\n\tcpuInfo, err := psutil.CPUInfo.GetCPUInfo(false)\n\tif err == nil && len(cpuInfo) > 0 {\n\t\tbaseInfo.CPUModelName = cpuInfo[0].ModelName\n\t}\n\n\tbaseInfo.CPUCores, _ = psutil.CPUInfo.GetPhysicalCores(false)\n\tbaseInfo.CPULogicalCores, _ = psutil.CPUInfo.GetLogicalCores(false)\n\tbaseInfo.CPUMhz = cpuInfo[0].Mhz\n\n\tbaseInfo.CurrentInfo = *u.LoadCurrentInfo(ioOption, netOption)\n\treturn &baseInfo, nil\n}\n\nfunc (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent {\n\tvar currentInfo dto.DashboardCurrent\n\thostInfo, _ := psutil.HOST.GetHostInfo(false)\n\tcurrentInfo.Uptime = hostInfo.Uptime\n\tcurrentInfo.TimeSinceUptime = time.Unix(int64(hostInfo.BootTime), 0).Format(constant.DateTimeLayout)\n\tcurrentInfo.Procs = hostInfo.Procs\n\tcurrentInfo.CPUTotal, _ = psutil.CPUInfo.GetLogicalCores(false)\n\n\tcpuUsedPercent, perCore, cpuDetailedPercent := psutil.CPU.GetCPUUsage()\n\tif len(perCore) == 0 {\n\t\tcurrentInfo.CPUTotal = psutil.CPU.NumCPU()\n\t} else {\n\t\tcurrentInfo.CPUTotal = len(perCore)\n\t}\n\tcurrentInfo.CPUPercent = perCore\n\tcurrentInfo.CPUUsedPercent = cpuUsedPercent\n\tcurrentInfo.CPUUsed = cpuUsedPercent * 0.01 * float64(currentInfo.CPUTotal)\n\tcurrentInfo.CPUDetailedPercent = cpuDetailedPercent\n\n\tloadInfo, _ := load.Avg()\n\tcurrentInfo.Load1 = loadInfo.Load1\n\tcurrentInfo.Load5 = loadInfo.Load5\n\tcurrentInfo.Load15 = loadInfo.Load15\n\tcurrentInfo.LoadUsagePercent = loadInfo.Load1 / (float64(currentInfo.CPUTotal*2) * 0.75) * 100\n\n\tmemoryInfo, _ := mem.VirtualMemory()\n\tcurrentInfo.MemoryTotal = memoryInfo.Total\n\tcurrentInfo.MemoryUsed = memoryInfo.Used\n\tcurrentInfo.MemoryFree = memoryInfo.Free\n\tcurrentInfo.MemoryCache = memoryInfo.Cached + memoryInfo.Buffers\n\tcurrentInfo.MemoryShard = memoryInfo.Shared\n\tcurrentInfo.MemoryAvailable = memoryInfo.Available\n\tcurrentInfo.MemoryUsedPercent = memoryInfo.UsedPercent\n\n\tswapInfo, _ := mem.SwapMemory()\n\tcurrentInfo.SwapMemoryTotal = swapInfo.Total\n\tcurrentInfo.SwapMemoryAvailable = swapInfo.Free\n\tcurrentInfo.SwapMemoryUsed = swapInfo.Used\n\tcurrentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent\n\n\tcurrentInfo.DiskData = loadDiskInfo()\n\tcurrentInfo.GPUData = loadGPUInfo()\n\tcurrentInfo.XPUData = loadXpuInfo()\n\n\tif ioOption == \"all\" {\n\t\tdiskInfo, _ := disk.IOCounters()\n\t\tfor _, state := range diskInfo {\n\t\t\tcurrentInfo.IOReadBytes += state.ReadBytes\n\t\t\tcurrentInfo.IOWriteBytes += state.WriteBytes\n\t\t\tcurrentInfo.IOCount += (state.ReadCount + state.WriteCount)\n\t\t\tcurrentInfo.IOReadTime += state.ReadTime\n\t\t\tcurrentInfo.IOWriteTime += state.WriteTime\n\t\t}\n\t} else {\n\t\tdiskInfo, _ := disk.IOCounters(ioOption)\n\t\tfor _, state := range diskInfo {\n\t\t\tcurrentInfo.IOReadBytes += state.ReadBytes\n\t\t\tcurrentInfo.IOWriteBytes += state.WriteBytes\n\t\t\tcurrentInfo.IOCount += (state.ReadCount + state.WriteCount)\n\t\t\tcurrentInfo.IOReadTime += state.ReadTime\n\t\t\tcurrentInfo.IOWriteTime += state.WriteTime\n\t\t}\n\t}\n\n\tif netOption == \"all\" {\n\t\tnetInfo, _ := net.IOCounters(false)\n\t\tif len(netInfo) != 0 {\n\t\t\tcurrentInfo.NetBytesSent = netInfo[0].BytesSent\n\t\t\tcurrentInfo.NetBytesRecv = netInfo[0].BytesRecv\n\t\t}\n\t} else {\n\t\tnetInfo, _ := net.IOCounters(true)\n\t\tfor _, state := range netInfo {\n\t\t\tif state.Name == netOption {\n\t\t\t\tcurrentInfo.NetBytesSent = state.BytesSent\n\t\t\t\tcurrentInfo.NetBytesRecv = state.BytesRecv\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tcurrentInfo.ShotTime = time.Now()\n\treturn &currentInfo\n}\n\nfunc (u *DashboardService) LoadTopCPU() []dto.Process {\n\treturn loadTopCPU()\n}\n\nfunc (u *DashboardService) LoadTopMem() []dto.Process {\n\treturn loadTopMem()\n}\n\nfunc (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error) {\n\tvar data []dto.AppLauncher\n\tappInstalls, err := appInstallRepo.ListBy(context.Background())\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tapps, err := appRepo.GetBy()\n\tif err != nil {\n\t\treturn data, err\n\t}\n\n\tshowList, _ := launcherRepo.ListName()\n\tdefaultList, err := appRepo.GetTopRecommend()\n\tif err != nil {\n\t\treturn data, nil\n\t}\n\tallList := common.RemoveRepeatStr(append(defaultList, showList...))\n\tfor _, showItem := range allList {\n\t\tvar itemData dto.AppLauncher\n\t\tfor _, app := range apps {\n\t\t\tif showItem == app.Key {\n\t\t\t\titemData.Key = app.Key\n\t\t\t\titemData.Type = app.Type\n\t\t\t\titemData.Name = app.Name\n\t\t\t\titemData.Icon = app.Icon\n\t\t\t\titemData.Limit = app.Limit\n\t\t\t\titemData.Recommend = app.Recommend\n\t\t\t\titemData.Description = app.GetDescription(ctx)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif len(itemData.Icon) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, install := range appInstalls {\n\t\t\tif install.App.Key == showItem {\n\t\t\t\titemData.IsInstall = true\n\t\t\t\titemData.Detail = append(itemData.Detail, dto.InstallDetail{\n\t\t\t\t\tInstallID: install.ID,\n\t\t\t\t\tDetailID:  install.AppDetailId,\n\t\t\t\t\tName:      install.Name,\n\t\t\t\t\tVersion:   install.Version,\n\t\t\t\t\tStatus:    install.Status,\n\t\t\t\t\tPath:      install.GetPath(),\n\t\t\t\t\tWebUI:     install.WebUI,\n\t\t\t\t\tHttpPort:  install.HttpPort,\n\t\t\t\t\tHttpsPort: install.HttpsPort,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif (ArryContains(showList, showItem) && len(itemData.Detail) != 0) ||\n\t\t\t(ArryContains(defaultList, showItem) && len(itemData.Detail) == 0) {\n\t\t\tdata = append(data, itemData)\n\t\t}\n\t}\n\n\tsort.Slice(data, func(i, j int) bool {\n\t\tif data[i].IsInstall != data[j].IsInstall {\n\t\t\treturn data[i].IsInstall\n\t\t}\n\t\treturn data[i].Recommend < data[j].Recommend\n\t})\n\treturn data, nil\n}\n\nfunc (u *DashboardService) ChangeShow(req dto.SettingUpdate) error {\n\tlauncher, _ := launcherRepo.Get(repo.WithByKey(req.Key))\n\tif req.Value == constant.StatusEnable && launcher.ID == 0 {\n\t\tif err := launcherRepo.Create(&model.AppLauncher{Key: req.Key}); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif req.Value == constant.StatusDisable && launcher.ID != 0 {\n\t\tif err := launcherRepo.Delete(repo.WithByKey(req.Key)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *DashboardService) LoadQuickOptions() []dto.QuickJump {\n\tquicks := launcherRepo.ListQuickJump(true)\n\tvar list []dto.QuickJump\n\tfor _, quick := range quicks {\n\t\tvar item dto.QuickJump\n\t\t_ = copier.Copy(&item, &quick)\n\t\tlist = append(list, item)\n\t}\n\tsort.Slice(list, func(i, j int) bool {\n\t\treturn list[i].Recommend < list[j].Recommend\n\t})\n\treturn list\n}\nfunc (u *DashboardService) ChangeQuick(req dto.ChangeQuicks) error {\n\tshowCount := 0\n\tvar quicks []model.QuickJump\n\tfor _, item := range req.Quicks {\n\t\tvar quick model.QuickJump\n\t\tif item.IsShow {\n\t\t\tshowCount++\n\t\t}\n\t\tif err := copier.Copy(&quick, &item); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tquicks = append(quicks, quick)\n\t}\n\tif showCount == 0 {\n\t\treturn buserr.New(\"ErrMinQuickJump\")\n\t}\n\tif showCount > 4 {\n\t\treturn buserr.New(\"ErrMaxQuickJump\")\n\t}\n\n\treturn launcherRepo.UpdateQuicks(quicks)\n}\n\nfunc (u *DashboardService) ListLauncherOption(filter string) ([]dto.LauncherOption, error) {\n\tshowList, _ := launcherRepo.ListName()\n\tvar data []dto.LauncherOption\n\toptionMap := make(map[string]bool)\n\tappInstalls, err := appInstallRepo.ListBy(context.Background())\n\tif err != nil {\n\t\treturn data, err\n\t}\n\n\tfor _, install := range appInstalls {\n\t\tisShow := false\n\t\tfor _, item := range showList {\n\t\t\tif install.App.Key == item {\n\t\t\t\tisShow = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\toptionMap[install.App.Key] = isShow\n\t}\n\tfor key, val := range optionMap {\n\t\tif len(filter) != 0 && !strings.Contains(key, filter) {\n\t\t\tcontinue\n\t\t}\n\t\tdata = append(data, dto.LauncherOption{Key: key, IsShow: val})\n\t}\n\tsort.Slice(data, func(i, j int) bool {\n\t\treturn data[i].Key < data[j].Key\n\t})\n\treturn data, nil\n}\n\ntype diskInfo struct {\n\tType   string\n\tMount  string\n\tDevice string\n}\n\nfunc loadDiskInfo() []dto.DiskInfo {\n\tvar datas []dto.DiskInfo\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))\n\tformat := `awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {printf \"%s\\t%s\\t%s\\t%s\\t%s\\t%s\\t%s\\n\", $1, $2, $3, $4, $5, $6, $7}'`\n\tstdout, err := cmdMgr.RunWithStdout(\"bash\", \"-c\", `timeout 2 df -hT -P | `+format)\n\tif err != nil {\n\t\tcmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))\n\t\tstdout, err = cmdMgr2.RunWithStdout(\"bash\", \"-c\", `timeout 1 df -lhT -P | `+format)\n\t\tif err != nil {\n\t\t\treturn datas\n\t\t}\n\t}\n\tlines := strings.Split(stdout, \"\\n\")\n\n\tvar mounts []diskInfo\n\tvar excludes = []string{\"/mnt/cdrom\", \"/boot\", \"/boot/efi\", \"/dev\", \"/dev/shm\", \"/run/lock\", \"/run\", \"/run/shm\", \"/run/user\"}\n\tfor _, line := range lines {\n\t\tfields := strings.Split(line, \"\\t\")\n\t\tif len(fields) < 7 {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(fields[6], \"/snap\") || len(strings.Split(fields[6], \"/\")) > 10 {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.TrimSpace(fields[1]) == \"tmpfs\" || strings.TrimSpace(fields[1]) == \"overlay\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(fields[2], \"K\") {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(fields[6], \"docker\") || strings.Contains(fields[6], \"podman\") || strings.Contains(fields[6], \"containerd\") || strings.HasPrefix(fields[6], \"/var/lib/containers\") {\n\t\t\tcontinue\n\t\t}\n\t\tisExclude := false\n\t\tfor _, exclude := range excludes {\n\t\t\tif exclude == fields[6] {\n\t\t\t\tisExclude = true\n\t\t\t}\n\t\t}\n\t\tif isExclude {\n\t\t\tcontinue\n\t\t}\n\t\tmounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: strings.Join(fields[6:], \" \")})\n\t}\n\n\tvar (\n\t\twg sync.WaitGroup\n\t\tmu sync.Mutex\n\t)\n\twg.Add(len(mounts))\n\tfor i := 0; i < len(mounts); i++ {\n\t\tgo func(mount diskInfo) {\n\t\t\tdefer wg.Done()\n\n\t\t\tvar itemData dto.DiskInfo\n\t\t\titemData.Path = mount.Mount\n\t\t\titemData.Type = mount.Type\n\t\t\titemData.Device = mount.Device\n\n\t\t\ttype diskResult struct {\n\t\t\t\tstate *disk.UsageStat\n\t\t\t\terr   error\n\t\t\t}\n\t\t\tresultCh := make(chan diskResult, 1)\n\n\t\t\tgo func() {\n\t\t\t\tstate, err := psutil.DISK.GetUsage(mount.Mount, false)\n\t\t\t\tresultCh <- diskResult{state: state, err: err}\n\t\t\t}()\n\n\t\t\tselect {\n\t\t\tcase <-time.After(5 * time.Second):\n\t\t\t\tmu.Lock()\n\t\t\t\tdatas = append(datas, itemData)\n\t\t\t\tmu.Unlock()\n\t\t\t\tglobal.LOG.Errorf(\"load disk info from %s failed, err: timeout\", mount.Mount)\n\t\t\tcase result := <-resultCh:\n\t\t\t\tif result.err != nil {\n\t\t\t\t\tmu.Lock()\n\t\t\t\t\tdatas = append(datas, itemData)\n\t\t\t\t\tmu.Unlock()\n\t\t\t\t\tglobal.LOG.Errorf(\"load disk info from %s failed, err: %v\", mount.Mount, result.err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\titemData.Total = result.state.Total\n\t\t\t\titemData.Free = result.state.Free\n\t\t\t\titemData.Used = result.state.Used\n\t\t\t\titemData.UsedPercent = result.state.UsedPercent\n\t\t\t\titemData.InodesTotal = result.state.InodesTotal\n\t\t\t\titemData.InodesUsed = result.state.InodesUsed\n\t\t\t\titemData.InodesFree = result.state.InodesFree\n\t\t\t\titemData.InodesUsedPercent = result.state.InodesUsedPercent\n\t\t\t\tmu.Lock()\n\t\t\t\tdatas = append(datas, itemData)\n\t\t\t\tmu.Unlock()\n\t\t\t}\n\t\t}(mounts[i])\n\t}\n\twg.Wait()\n\n\tsort.Slice(datas, func(i, j int) bool {\n\t\treturn datas[i].Path < datas[j].Path\n\t})\n\treturn datas\n}\n\nfunc loadGPUInfo() []dto.GPUInfo {\n\tok, client := gpu.New()\n\tvar list []interface{}\n\tif ok {\n\t\tinfo, err := client.LoadGpuInfo()\n\t\tif err != nil || len(info.GPUs) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tfor _, item := range info.GPUs {\n\t\t\tlist = append(list, item)\n\t\t}\n\t}\n\tif len(list) == 0 {\n\t\treturn nil\n\t}\n\tvar data []dto.GPUInfo\n\tfor _, gpu := range list {\n\t\tvar dataItem dto.GPUInfo\n\t\tif err := copier.Copy(&dataItem, &gpu); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tdataItem.PowerUsage = dataItem.PowerDraw + \" / \" + dataItem.MaxPowerLimit\n\t\tdataItem.MemoryUsage = dataItem.MemUsed + \" / \" + dataItem.MemTotal\n\t\tdata = append(data, dataItem)\n\t}\n\treturn data\n}\n\ntype AppLauncher struct {\n\tKey string `json:\"key\"`\n}\n\nfunc ArryContains(arr []string, element string) bool {\n\tfor _, v := range arr {\n\t\tif v == element {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc loadXpuInfo() []dto.XPUInfo {\n\tvar list []interface{}\n\tok, xpuClient := xpu.New()\n\tif ok {\n\t\txpus, err := xpuClient.LoadDashData()\n\t\tif err != nil || len(xpus) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tfor _, item := range xpus {\n\t\t\tlist = append(list, item)\n\t\t}\n\t}\n\tif len(list) == 0 {\n\t\treturn nil\n\t}\n\tvar data []dto.XPUInfo\n\tfor _, gpu := range list {\n\t\tvar dataItem dto.XPUInfo\n\t\tif err := copier.Copy(&dataItem, &gpu); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tdata = append(data, dataItem)\n\t}\n\treturn data\n}\n\nfunc loadOutboundIP() string {\n\tconn, err := network.Dial(\"udp\", \"8.8.8.8:80\")\n\n\tif err != nil {\n\t\treturn \"IPNotFound\"\n\t}\n\tdefer conn.Close()\n\n\tlocalAddr := conn.LocalAddr().(*network.UDPAddr)\n\treturn localAddr.IP.String()\n}\n\nfunc loadQuickJump(base *dto.DashboardBase) {\n\twebsite, _ := websiteRepo.GetBy()\n\tbase.WebsiteNumber = len(website)\n\n\tagents, _ := agentRepo.List()\n\tbase.AgentNumber = len(agents)\n\n\tpostgresqlDbs, _ := postgresqlRepo.List()\n\tmysqlDbs, _ := mysqlRepo.List()\n\tbase.DatabaseNumber = len(mysqlDbs) + len(postgresqlDbs)\n\n\tcronjobs, _ := cronjobRepo.List()\n\tbase.CronjobNumber = len(cronjobs)\n\n\tappInstall, _ := appInstallRepo.ListBy(context.Background())\n\tbase.AppInstalledNumber = len(appInstall)\n\n\tquicks := launcherRepo.ListQuickJump(false)\n\tfor i := 0; i < len(quicks); i++ {\n\t\tswitch quicks[i].Name {\n\t\tcase \"Agent\":\n\t\t\tquicks[i].Detail = fmt.Sprintf(\"%d\", base.AgentNumber)\n\t\tcase \"Website\":\n\t\t\tquicks[i].Detail = fmt.Sprintf(\"%d\", base.WebsiteNumber)\n\t\tcase \"Database\":\n\t\t\tquicks[i].Detail = fmt.Sprintf(\"%d\", base.DatabaseNumber)\n\t\tcase \"Cronjob\":\n\t\t\tquicks[i].Detail = fmt.Sprintf(\"%d\", base.CronjobNumber)\n\t\tcase \"AppInstalled\":\n\t\t\tquicks[i].Detail = fmt.Sprintf(\"%d\", base.AppInstalledNumber)\n\t\t}\n\t\tvar item dto.QuickJump\n\t\t_ = copier.Copy(&item, quicks[i])\n\t\tbase.QuickJumps = append(base.QuickJumps, item)\n\t}\n\tsort.Slice(base.QuickJumps, func(i, j int) bool {\n\t\treturn base.QuickJumps[i].Recommend < base.QuickJumps[j].Recommend\n\t})\n}\n"
  },
  {
    "path": "agent/app/service/database.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/mysql\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/mysql/client\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/postgresql\"\n\tpgclient \"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client\"\n\tredisclient \"github.com/1Panel-dev/1Panel/agent/utils/redis\"\n\t\"github.com/jinzhu/copier\"\n\t\"github.com/pkg/errors\"\n)\n\ntype DatabaseService struct{}\n\ntype IDatabaseService interface {\n\tGet(name string) (dto.DatabaseInfo, error)\n\tSearchWithPage(search dto.DatabaseSearch) (int64, interface{}, error)\n\tCheckDatabase(req dto.DatabaseCreate) bool\n\tCreate(req dto.DatabaseCreate) error\n\tUpdate(req dto.DatabaseUpdate) error\n\tDeleteCheck(id uint) ([]string, error)\n\tDelete(req dto.DatabaseDelete) error\n\tList(dbType string) ([]dto.DatabaseOption, error)\n\tLoadItems(dbType string) ([]dto.DatabaseItem, error)\n}\n\nfunc NewIDatabaseService() IDatabaseService {\n\treturn &DatabaseService{}\n}\n\nfunc (u *DatabaseService) SearchWithPage(search dto.DatabaseSearch) (int64, interface{}, error) {\n\ttotal, dbs, err := databaseRepo.Page(search.Page, search.PageSize,\n\t\tdatabaseRepo.WithTypeList(search.Type),\n\t\trepo.WithByLikeName(search.Info),\n\t\trepo.WithOrderRuleBy(search.OrderBy, search.Order),\n\t\tdatabaseRepo.WithoutByFrom(\"local\"),\n\t)\n\tvar datas []dto.DatabaseInfo\n\tfor _, db := range dbs {\n\t\tvar item dto.DatabaseInfo\n\t\tif err := copier.Copy(&item, &db); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdatas = append(datas, item)\n\t}\n\treturn total, datas, err\n}\n\nfunc (u *DatabaseService) Get(name string) (dto.DatabaseInfo, error) {\n\tvar data dto.DatabaseInfo\n\tremote, err := databaseRepo.Get(repo.WithByName(name))\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tif err := copier.Copy(&data, &remote); err != nil {\n\t\treturn data, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\treturn data, nil\n}\n\nfunc (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {\n\tdbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []dto.DatabaseOption\n\tfor _, db := range dbs {\n\t\tvar item dto.DatabaseOption\n\t\tif err := copier.Copy(&item, &db); err != nil {\n\t\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\titem.Database = db.Name\n\t\tdatas = append(datas, item)\n\t}\n\treturn datas, err\n}\n\nfunc (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {\n\tdbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))\n\tvar datas []dto.DatabaseItem\n\tfor _, db := range dbs {\n\t\tif dbType == constant.AppPostgresql || dbType == constant.AppPostgresqlCluster {\n\t\t\titems, _ := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(db.Name))\n\t\t\tfor _, item := range items {\n\t\t\t\tvar dItem dto.DatabaseItem\n\t\t\t\tif err := copier.Copy(&dItem, &item); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdItem.Database = db.Name\n\t\t\t\tdatas = append(datas, dItem)\n\t\t\t}\n\t\t} else {\n\t\t\titems, _ := mysqlRepo.List(mysqlRepo.WithByMysqlName(db.Name))\n\t\t\tfor _, item := range items {\n\t\t\t\tvar dItem dto.DatabaseItem\n\t\t\t\tif err := copier.Copy(&dItem, &item); err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tdItem.Database = db.Name\n\t\t\t\tdatas = append(datas, dItem)\n\t\t\t}\n\t\t}\n\t}\n\treturn datas, err\n}\n\nfunc (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {\n\tif req.Timeout == 0 {\n\t\treq.Timeout = 30\n\t}\n\tvar err error\n\tswitch req.Type {\n\tcase constant.AppPostgresql:\n\t\t_, err = postgresql.NewPostgresqlClient(pgclient.DBInfo{\n\t\t\tFrom:      \"remote\",\n\t\t\tAddress:   req.Address,\n\t\t\tPort:      req.Port,\n\t\t\tInitialDB: req.InitialDB,\n\t\t\tUsername:  req.Username,\n\t\t\tPassword:  req.Password,\n\t\t\tTimeout:   req.Timeout,\n\t\t})\n\tcase constant.AppRedis:\n\t\t_, err = redisclient.NewRedisClient(redisclient.DBInfo{\n\t\t\tAddress:  req.Address,\n\t\t\tPort:     req.Port,\n\t\t\tPassword: req.Password,\n\t\t\tTimeout:  req.Timeout,\n\t\t})\n\tcase \"mysql\", \"mariadb\":\n\t\t_, err = mysql.NewMysqlClient(client.DBInfo{\n\t\t\tFrom:     \"remote\",\n\t\t\tAddress:  req.Address,\n\t\t\tPort:     req.Port,\n\t\t\tUsername: req.Username,\n\t\t\tPassword: req.Password,\n\n\t\t\tSSL:        req.SSL,\n\t\t\tRootCert:   req.RootCert,\n\t\t\tClientKey:  req.ClientKey,\n\t\t\tClientCert: req.ClientCert,\n\t\t\tSkipVerify: req.SkipVerify,\n\t\t\tTimeout:    req.Timeout,\n\t\t})\n\t}\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"check database connection failed, err: %v\", err)\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (u *DatabaseService) Create(req dto.DatabaseCreate) error {\n\tif req.Timeout == 0 {\n\t\treq.Timeout = 30\n\t}\n\tdb, _ := databaseRepo.Get(repo.WithByName(req.Name))\n\tif db.ID != 0 {\n\t\tif db.From == \"local\" {\n\t\t\treturn buserr.New(\"ErrLocalExist\")\n\t\t}\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tswitch req.Type {\n\tcase constant.AppPostgresql:\n\t\tif _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{\n\t\t\tFrom:      \"remote\",\n\t\t\tAddress:   req.Address,\n\t\t\tPort:      req.Port,\n\t\t\tInitialDB: req.InitialDB,\n\t\t\tUsername:  req.Username,\n\t\t\tPassword:  req.Password,\n\t\t\tTimeout:   req.Timeout,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase constant.AppRedis:\n\t\tif _, err := redisclient.NewRedisClient(redisclient.DBInfo{\n\t\t\tAddress:  req.Address,\n\t\t\tPort:     req.Port,\n\t\t\tPassword: req.Password,\n\t\t\tTimeout:  req.Timeout,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"mysql\", \"mariadb\":\n\t\tif _, err := mysql.NewMysqlClient(client.DBInfo{\n\t\t\tFrom:     \"remote\",\n\t\t\tAddress:  req.Address,\n\t\t\tPort:     req.Port,\n\t\t\tUsername: req.Username,\n\t\t\tPassword: req.Password,\n\n\t\t\tSSL:        req.SSL,\n\t\t\tRootCert:   req.RootCert,\n\t\t\tClientKey:  req.ClientKey,\n\t\t\tClientCert: req.ClientCert,\n\t\t\tSkipVerify: req.SkipVerify,\n\t\t\tTimeout:    req.Timeout,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn errors.New(\"database type not supported\")\n\t}\n\n\tif err := copier.Copy(&db, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif err := databaseRepo.Create(context.Background(), &db); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *DatabaseService) DeleteCheck(id uint) ([]string, error) {\n\tvar appInUsed []string\n\tapps, _ := appInstallResourceRepo.GetBy(repo.WithByFrom(\"remote\"), appInstallResourceRepo.WithLinkId(id))\n\tfor _, app := range apps {\n\t\tappInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))\n\t\tif appInstall.ID != 0 {\n\t\t\tappInUsed = append(appInUsed, appInstall.Name)\n\t\t}\n\t}\n\n\treturn appInUsed, nil\n}\n\nfunc (u *DatabaseService) Delete(req dto.DatabaseDelete) error {\n\tdb, _ := databaseRepo.Get(repo.WithByID(req.ID))\n\tif db.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\n\tif req.DeleteBackup {\n\t\tuploadDir := path.Join(global.Dir.DataDir, fmt.Sprintf(\"uploads/database/%s/%s\", db.Type, db.Name))\n\t\tif _, err := os.Stat(uploadDir); err == nil {\n\t\t\t_ = os.RemoveAll(uploadDir)\n\t\t}\n\t\tbackupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf(\"database/%s/%s\", db.Type, db.Name))\n\t\tif _, err := os.Stat(backupDir); err == nil {\n\t\t\t_ = os.RemoveAll(backupDir)\n\t\t}\n\t\t_ = backupRepo.DeleteRecord(context.Background(), repo.WithByType(db.Type), repo.WithByName(db.Name))\n\t\tglobal.LOG.Infof(\"delete database %s-%s backups successful\", db.Type, db.Name)\n\t}\n\n\tif err := databaseRepo.Delete(context.Background(), repo.WithByID(req.ID)); err != nil && !req.ForceDelete {\n\t\treturn err\n\t}\n\tif db.From != \"local\" {\n\t\tif db.Type == \"mysql\" || db.Type == \"mariadb\" {\n\t\t\tif err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil && !req.ForceDelete {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif err := postgresqlRepo.Delete(context.Background(), postgresqlRepo.WithByPostgresqlName(db.Name)); err != nil && !req.ForceDelete {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *DatabaseService) Update(req dto.DatabaseUpdate) error {\n\tswitch req.Type {\n\tcase constant.AppPostgresql:\n\t\tif _, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{\n\t\t\tFrom:      \"remote\",\n\t\t\tAddress:   req.Address,\n\t\t\tPort:      req.Port,\n\t\t\tInitialDB: req.InitialDB,\n\t\t\tUsername:  req.Username,\n\t\t\tPassword:  req.Password,\n\t\t\tTimeout:   req.Timeout,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase constant.AppRedis:\n\t\tif _, err := redisclient.NewRedisClient(redisclient.DBInfo{\n\t\t\tAddress:  req.Address,\n\t\t\tPort:     req.Port,\n\t\t\tPassword: req.Password,\n\t\t\tTimeout:  req.Timeout,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"mysql\", \"mariadb\":\n\t\tif _, err := mysql.NewMysqlClient(client.DBInfo{\n\t\t\tFrom:     \"remote\",\n\t\t\tAddress:  req.Address,\n\t\t\tPort:     req.Port,\n\t\t\tUsername: req.Username,\n\t\t\tPassword: req.Password,\n\n\t\t\tSSL:        req.SSL,\n\t\t\tRootCert:   req.RootCert,\n\t\t\tClientKey:  req.ClientKey,\n\t\t\tClientCert: req.ClientCert,\n\t\t\tSkipVerify: req.SkipVerify,\n\t\t\tTimeout:    req.Timeout,\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn errors.New(\"database type not supported\")\n\t}\n\n\tpass, err := encrypt.StringEncrypt(req.Password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decrypt database password failed, err: %v\", err)\n\t}\n\n\tupMap := make(map[string]interface{})\n\tupMap[\"type\"] = req.Type\n\tupMap[\"version\"] = req.Version\n\tupMap[\"address\"] = req.Address\n\tupMap[\"port\"] = req.Port\n\tupMap[\"username\"] = req.Username\n\tupMap[\"password\"] = pass\n\tupMap[\"initial_db\"] = req.InitialDB\n\tupMap[\"timeout\"] = req.Timeout\n\tupMap[\"description\"] = req.Description\n\tupMap[\"ssl\"] = req.SSL\n\tupMap[\"client_key\"] = req.ClientKey\n\tupMap[\"client_cert\"] = req.ClientCert\n\tupMap[\"root_cert\"] = req.RootCert\n\tupMap[\"skip_verify\"] = req.SkipVerify\n\treturn databaseRepo.Update(req.ID, upMap)\n}\n"
  },
  {
    "path": "agent/app/service/database_common.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n)\n\ntype DBCommonService struct{}\n\ntype IDBCommonService interface {\n\tLoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)\n\tLoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)\n\n\tUpdateConfByFile(req dto.DBConfUpdateByFile) error\n}\n\nfunc NewIDBCommonService() IDBCommonService {\n\treturn &DBCommonService{}\n}\n\nfunc (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {\n\tvar data dto.DBBaseInfo\n\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdata.ContainerName = app.ContainerName\n\tdata.Name = app.Name\n\tdata.Port = app.Port\n\n\treturn &data, nil\n}\n\nfunc (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {\n\tfilePath := \"\"\n\tsafeName := filepath.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn \"\", buserr.New(\"ErrInvalidParams\")\n\t}\n\tswitch req.Type {\n\tcase \"mysql-cluster-conf\":\n\t\tfilePath = path.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/mysql-cluster/%s/conf/my.cnf\", req.Name))\n\tcase \"postgresql-cluster-conf\":\n\t\tfilePath = path.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/postgresql-cluster/%s/data/postgresql.conf\", req.Name))\n\tcase \"mysql-conf\":\n\t\tfilePath = path.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/mysql/%s/conf/my.cnf\", req.Name))\n\tcase \"mariadb-conf\":\n\t\tfilePath = path.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/mariadb/%s/conf/my.cnf\", req.Name))\n\tcase \"postgresql-conf\":\n\t\tdatabase, err := databaseRepo.Get(repo.WithByName(req.Name))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tfilePath = path.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/postgresql/%s/data/postgresql.conf\", req.Name))\n\t\tif strings.HasPrefix(database.Version, \"18.\") {\n\t\t\tfilePath = path.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/postgresql/%s/data/18/docker/postgresql.conf\", req.Name))\n\t\t}\n\tcase \"redis-conf\":\n\t\tfilePath = path.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/redis/%s/conf/redis.conf\", req.Name))\n\tcase \"redis-cluster-conf\":\n\t\tfilePath = path.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/redis-cluster/%s/conf/redis.conf\", req.Name))\n\t}\n\tif _, err := os.Stat(filePath); err != nil {\n\t\treturn \"\", buserr.New(\"ErrHttpReqNotFound\")\n\t}\n\tcontent, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(content), nil\n}\n\nfunc (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {\n\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpath := \"\"\n\tswitch req.Type {\n\tcase constant.AppMariaDB, constant.AppMysql, constant.AppMysqlCluster:\n\t\tpath = fmt.Sprintf(\"%s/%s/%s/conf/my.cnf\", global.Dir.AppInstallDir, req.Type, app.Name)\n\tcase constant.AppPostgresqlCluster:\n\t\tpath = fmt.Sprintf(\"%s/%s/%s/data/postgresql.conf\", global.Dir.AppInstallDir, req.Type, app.Name)\n\tcase constant.AppPostgresql:\n\t\tpath = fmt.Sprintf(\"%s/%s/%s/data/postgresql.conf\", global.Dir.AppInstallDir, req.Type, app.Name)\n\t\tif strings.HasPrefix(app.Version, \"18.\") {\n\t\t\tpath = fmt.Sprintf(\"%s/%s/%s/data/18/docker/postgresql.conf\", global.Dir.AppInstallDir, req.Type, app.Name)\n\t\t}\n\tcase constant.AppRedis, constant.AppRedisCluster:\n\t\tpath = fmt.Sprintf(\"%s/%s/%s/conf/redis.conf\", global.Dir.AppInstallDir, req.Type, app.Name)\n\t}\n\tfile, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(req.File)\n\twrite.Flush()\n\tif _, err := compose.Restart(fmt.Sprintf(\"%s/%s/%s/docker-compose.yml\", global.Dir.AppInstallDir, req.Type, app.Name)); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/database_mysql.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/mysql\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/mysql/client\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/jinzhu/copier\"\n\t\"github.com/pkg/errors\"\n)\n\ntype MysqlService struct{}\n\ntype IMysqlService interface {\n\tSearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)\n\tListDBOption() ([]dto.MysqlOption, error)\n\tCreate(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)\n\tBindUser(req dto.BindUser) error\n\tLoadFromRemote(req dto.MysqlLoadDB) error\n\tChangeAccess(info dto.ChangeDBInfo) error\n\tChangePassword(info dto.ChangeDBInfo) error\n\tUpdateVariables(req dto.MysqlVariablesUpdate) error\n\tUpdateDescription(req dto.UpdateDescription) error\n\tDeleteCheck(req dto.MysqlDBDeleteCheck) ([]dto.DBResource, error)\n\tDelete(ctx context.Context, req dto.MysqlDBDelete) error\n\n\tLoadFormatOption(req dto.OperationWithName) []dto.MysqlFormatCollationOption\n\tLoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)\n\tLoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error)\n\tLoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error)\n}\n\nfunc NewIMysqlService() IMysqlService {\n\treturn &MysqlService{}\n}\n\nfunc (u *MysqlService) SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error) {\n\ttotal, mysqls, err := mysqlRepo.Page(search.Page, search.PageSize,\n\t\tmysqlRepo.WithByMysqlName(search.Database),\n\t\trepo.WithByLikeName(search.Info),\n\t\trepo.WithOrderRuleBy(search.OrderBy, search.Order),\n\t)\n\tvar dtoMysqls []dto.MysqlDBInfo\n\tfor _, mysql := range mysqls {\n\t\tvar item dto.MysqlDBInfo\n\t\tif err := copier.Copy(&item, &mysql); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdtoMysqls = append(dtoMysqls, item)\n\t}\n\treturn total, dtoMysqls, err\n}\n\nfunc (u *MysqlService) ListDBOption() ([]dto.MysqlOption, error) {\n\tmysqls, err := mysqlRepo.List()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdatabases, err := databaseRepo.GetList(databaseRepo.WithTypeList(\"mysql,mariadb\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar dbs []dto.MysqlOption\n\tfor _, mysql := range mysqls {\n\t\tvar item dto.MysqlOption\n\t\tif err := copier.Copy(&item, &mysql); err != nil {\n\t\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\titem.Database = mysql.MysqlName\n\t\tfor _, database := range databases {\n\t\t\tif database.Name == item.Database {\n\t\t\t\titem.Type = database.Type\n\t\t\t}\n\t\t}\n\t\tdbs = append(dbs, item)\n\t}\n\treturn dbs, err\n}\n\nfunc (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error) {\n\tif cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format, req.Collation, req.Permission) {\n\t\treturn nil, buserr.New(\"ErrCmdIllegal\")\n\t}\n\n\tmysql, _ := mysqlRepo.Get(repo.WithByName(req.Name), mysqlRepo.WithByMysqlName(req.Database), repo.WithByFrom(req.From))\n\tif mysql.ID != 0 {\n\t\treturn nil, buserr.New(\"ErrRecordExist\")\n\t}\n\n\tvar createItem model.DatabaseMysql\n\tif err := copier.Copy(&createItem, &req); err != nil {\n\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\n\tif req.From == \"local\" && req.Username == \"root\" {\n\t\treturn nil, errors.New(\"cannot set root as user name\")\n\t}\n\n\tcli, version, err := LoadMysqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcreateItem.MysqlName = req.Database\n\tdefer cli.Close()\n\tif err := cli.Create(client.CreateInfo{\n\t\tName:       req.Name,\n\t\tFormat:     req.Format,\n\t\tCollation:  req.Collation,\n\t\tUsername:   req.Username,\n\t\tPassword:   req.Password,\n\t\tPermission: req.Permission,\n\t\tVersion:    version,\n\t\tTimeout:    300,\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\n\tglobal.LOG.Infof(\"create database %s successful!\", req.Name)\n\tif err := mysqlRepo.Create(ctx, &createItem); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &createItem, nil\n}\n\nfunc (u *MysqlService) BindUser(req dto.BindUser) error {\n\tif cmd.CheckIllegal(req.Username, req.Password, req.Permission) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\n\tdbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(req.Database), repo.WithByName(req.DB))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcli, version, err := LoadMysqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\n\tif err := cli.CreateUser(client.CreateInfo{\n\t\tName:       dbItem.Name,\n\t\tFormat:     dbItem.Format,\n\t\tUsername:   req.Username,\n\t\tPassword:   req.Password,\n\t\tPermission: req.Permission,\n\t\tVersion:    version,\n\t\tTimeout:    300,\n\t}, false); err != nil {\n\t\treturn err\n\t}\n\tpass, err := encrypt.StringEncrypt(req.Password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decrypt database db password failed, err: %v\", err)\n\t}\n\tif err := mysqlRepo.Update(dbItem.ID, map[string]interface{}{\n\t\t\"username\":   req.Username,\n\t\t\"password\":   pass,\n\t\t\"permission\": req.Permission,\n\t}); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {\n\tclient, version, err := LoadMysqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdatabases, err := mysqlRepo.List(mysqlRepo.WithByMysqlName(req.Database))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdatas, err := client.SyncDB(version)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdeleteList := databases\n\tfor _, data := range datas {\n\t\thasOld := false\n\t\tfor i := 0; i < len(databases); i++ {\n\t\t\tif strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].MysqlName, data.MysqlName) {\n\t\t\t\thasOld = true\n\t\t\t\tif databases[i].IsDelete {\n\t\t\t\t\t_ = mysqlRepo.Update(databases[i].ID, map[string]interface{}{\"is_delete\": false})\n\t\t\t\t}\n\t\t\t\tdeleteList = append(deleteList[:i], deleteList[i+1:]...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !hasOld {\n\t\t\tvar createItem model.DatabaseMysql\n\t\t\tif err := copier.Copy(&createItem, &data); err != nil {\n\t\t\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t\t}\n\t\t\tif err := mysqlRepo.Create(context.Background(), &createItem); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tfor _, delItem := range deleteList {\n\t\t_ = mysqlRepo.Update(delItem.ID, map[string]interface{}{\"is_delete\": true})\n\t}\n\treturn nil\n}\n\nfunc (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error {\n\treturn mysqlRepo.Update(req.ID, map[string]interface{}{\"description\": req.Description})\n}\n\nfunc (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]dto.DBResource, error) {\n\tvar res []dto.DBResource\n\tdb, err := mysqlRepo.Get(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn res, err\n\t}\n\n\twebsite, _ := websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppMysql), websiteRepo.WithDBID(req.ID))\n\tif website.ID != 0 {\n\t\tres = append(res, dto.DBResource{\n\t\t\tType: constant.TypeWebsite,\n\t\t\tName: website.PrimaryDomain,\n\t\t})\n\t}\n\twebsite, _ = websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppMysqlCluster), websiteRepo.WithDBID(req.ID))\n\tif website.ID != 0 {\n\t\tres = append(res, dto.DBResource{\n\t\t\tType: constant.TypeWebsite,\n\t\t\tName: website.PrimaryDomain,\n\t\t})\n\t}\n\n\tif db.From == \"local\" {\n\t\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)\n\t\tif err != nil {\n\t\t\treturn res, err\n\t\t}\n\t\tapps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))\n\t\tfor _, app := range apps {\n\t\t\tappInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))\n\t\t\tif appInstall.ID != 0 {\n\t\t\t\tres = append(res, dto.DBResource{\n\t\t\t\t\tType: constant.TypeApp,\n\t\t\t\t\tName: appInstall.Name,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t} else {\n\t\tapps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID), appRepo.WithKey(req.Type))\n\t\tfor _, app := range apps {\n\t\t\tappInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))\n\t\t\tif appInstall.ID != 0 {\n\t\t\t\tres = append(res, dto.DBResource{\n\t\t\t\t\tType: constant.TypeApp,\n\t\t\t\t\tName: appInstall.Name,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn res, nil\n}\n\nfunc (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error {\n\tdb, err := mysqlRepo.Get(repo.WithByID(req.ID))\n\tif err != nil && !req.ForceDelete {\n\t\treturn err\n\t}\n\tcli, version, err := LoadMysqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tif err := cli.Delete(client.DeleteInfo{\n\t\tName:       db.Name,\n\t\tVersion:    version,\n\t\tUsername:   db.Username,\n\t\tPermission: db.Permission,\n\t\tTimeout:    300,\n\t}); err != nil && !req.ForceDelete {\n\t\treturn err\n\t}\n\n\tif req.DeleteBackup {\n\t\tuploadDir := filepath.Join(global.Dir.DataDir, fmt.Sprintf(\"uploads/database/%s/%s/%s\", req.Type, req.Database, db.Name))\n\t\tif _, err := os.Stat(uploadDir); err == nil {\n\t\t\t_ = os.RemoveAll(uploadDir)\n\t\t}\n\t\tbackupDir := filepath.Join(global.Dir.LocalBackupDir, fmt.Sprintf(\"database/%s/%s/%s\", req.Type, db.MysqlName, db.Name))\n\t\tif _, err := os.Stat(backupDir); err == nil {\n\t\t\t_ = os.RemoveAll(backupDir)\n\t\t}\n\t\t_ = backupRepo.DeleteRecord(ctx, repo.WithByType(req.Type), repo.WithByName(req.Database), repo.WithByDetailName(db.Name))\n\t\tglobal.LOG.Infof(\"delete database %s-%s backups successful\", req.Database, db.Name)\n\t}\n\n\t_ = mysqlRepo.Delete(ctx, repo.WithByID(db.ID))\n\treturn nil\n}\n\nfunc (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error {\n\tif cmd.CheckIllegal(req.Value) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcli, version, err := LoadMysqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tvar (\n\t\tmysqlData    model.DatabaseMysql\n\t\tpasswordInfo client.PasswordChangeInfo\n\t)\n\tpasswordInfo.Password = req.Value\n\tpasswordInfo.Timeout = 300\n\tpasswordInfo.Version = version\n\n\tif req.ID != 0 {\n\t\tmysqlData, err = mysqlRepo.Get(repo.WithByID(req.ID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpasswordInfo.Name = mysqlData.Name\n\t\tpasswordInfo.Username = mysqlData.Username\n\t\tpasswordInfo.Permission = mysqlData.Permission\n\t} else {\n\t\tpasswordInfo.Username = \"root\"\n\t}\n\tif err := cli.ChangePassword(passwordInfo); err != nil {\n\t\treturn err\n\t}\n\n\tif req.ID != 0 {\n\t\tvar appRess []model.AppInstallResource\n\t\tif req.From == \"local\" {\n\t\t\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tappRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(mysqlData.ID))\n\t\t} else {\n\t\t\tappRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(mysqlData.ID))\n\t\t}\n\t\tfor _, appRes := range appRess {\n\t\t\tappInstall, err := appInstallRepo.GetFirst(repo.WithByID(appRes.AppInstallId))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tappModel, err := appRepo.GetFirst(repo.WithByID(appInstall.AppId))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tglobal.LOG.Infof(\"start to update mysql password used by app %s-%s\", appModel.Key, appInstall.Name)\n\t\t\tif err := updateInstallInfoInDB(appModel.Key, appInstall.Name, \"user-password\", req.Value); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tglobal.LOG.Info(\"execute password change sql successful\")\n\t\tpass, err := encrypt.StringEncrypt(req.Value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"decrypt database db password failed, err: %v\", err)\n\t\t}\n\t\t_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{\"password\": pass})\n\t\treturn nil\n\t}\n\n\tif err := updateInstallInfoInDB(req.Type, req.Database, \"password\", req.Value); err != nil {\n\t\treturn err\n\t}\n\tif req.From == \"local\" {\n\t\tremote, err := databaseRepo.Get(repo.WithByName(req.Database))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpass, err := encrypt.StringEncrypt(req.Value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"decrypt database password failed, err: %v\", err)\n\t\t}\n\t\t_ = databaseRepo.Update(remote.ID, map[string]interface{}{\"password\": pass})\n\t}\n\treturn nil\n}\n\nfunc (u *MysqlService) ChangeAccess(req dto.ChangeDBInfo) error {\n\tif cmd.CheckIllegal(req.Value) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcli, version, err := LoadMysqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tvar (\n\t\tmysqlData  model.DatabaseMysql\n\t\taccessInfo client.AccessChangeInfo\n\t)\n\taccessInfo.Permission = req.Value\n\taccessInfo.Timeout = 300\n\taccessInfo.Version = version\n\n\tif req.ID != 0 {\n\t\tmysqlData, err = mysqlRepo.Get(repo.WithByID(req.ID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\taccessInfo.Name = mysqlData.Name\n\t\taccessInfo.Username = mysqlData.Username\n\t\taccessInfo.Password = mysqlData.Password\n\t\taccessInfo.OldPermission = mysqlData.Permission\n\t} else {\n\t\taccessInfo.Username = \"root\"\n\t}\n\tif err := cli.ChangeAccess(accessInfo); err != nil {\n\t\treturn err\n\t}\n\n\tif mysqlData.ID != 0 {\n\t\t_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{\"permission\": req.Value})\n\t}\n\n\treturn nil\n}\n\nfunc (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {\n\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar files []string\n\n\tpath := fmt.Sprintf(\"%s/%s/%s/conf/my.cnf\", global.Dir.AppInstallDir, req.Type, app.Name)\n\tlineBytes, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfiles = strings.Split(string(lineBytes), \"\\n\")\n\n\tgroup := \"[mysqld]\"\n\tfor _, info := range req.Variables {\n\t\tif info.Param == \"slow_query_log\" && info.Value == \"ON\" {\n\t\t\tlogFilePath := filepath.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/%s/%s/data/1Panel-slow.log\", app.Key, app.Name))\n\t\t\tif req.Type == \"mariadb\" {\n\t\t\t\tlogFilePath = filepath.Join(global.Dir.DataDir, fmt.Sprintf(\"apps/%s/%s/db/data/1Panel-slow.log\", app.Key, app.Name))\n\t\t\t}\n\t\t\tfile, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer file.Close()\n\t\t}\n\t\tif !strings.HasPrefix(app.Version, \"5.7\") && !strings.HasPrefix(app.Version, \"5.6\") {\n\t\t\tif info.Param == \"query_cache_size\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif _, ok := info.Value.(float64); ok {\n\t\t\tfiles = updateMyCnf(files, group, info.Param, common.LoadSizeUnit(info.Value.(float64)))\n\t\t} else {\n\t\t\tfiles = updateMyCnf(files, group, info.Param, info.Value)\n\t\t}\n\t}\n\tfile, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\t_, err = file.WriteString(strings.Join(files, \"\\n\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif _, err := compose.Restart(fmt.Sprintf(\"%s/%s/%s/docker-compose.yml\", global.Dir.AppInstallDir, req.Type, app.Name)); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (u *MysqlService) LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error) {\n\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\thosts, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, \"select host from mysql.user where user='root';\")\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tfor _, host := range hosts {\n\t\tif host == \"%\" {\n\t\t\treturn true, nil\n\t\t}\n\t}\n\n\treturn false, nil\n}\n\nfunc (u *MysqlService) LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error) {\n\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvariableMap, err := executeSqlForMaps(app.ContainerName, app.Key, app.Password, \"show global variables;\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar info dto.MysqlVariables\n\tarr, err := json.Marshal(variableMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_ = json.Unmarshal(arr, &info)\n\treturn &info, nil\n}\n\nfunc (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error) {\n\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstatusMap, err := executeSqlForMaps(app.ContainerName, app.Key, app.Password, \"show global status;\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar info dto.MysqlStatus\n\tarr, err := json.Marshal(statusMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_ = json.Unmarshal(arr, &info)\n\n\tif value, ok := statusMap[\"Run\"]; ok {\n\t\tuptime, _ := strconv.Atoi(value)\n\t\tinfo.Run = time.Unix(time.Now().Unix()-int64(uptime), 0).Format(constant.DateTimeLayout)\n\t} else {\n\t\tif value, ok := statusMap[\"Uptime\"]; ok {\n\t\t\tuptime, _ := strconv.Atoi(value)\n\t\t\tinfo.Run = time.Unix(time.Now().Unix()-int64(uptime), 0).Format(constant.DateTimeLayout)\n\t\t}\n\t}\n\n\tinfo.File = \"OFF\"\n\tinfo.Position = \"OFF\"\n\tmasterStatus := \"show master status;\"\n\tif common.CompareAppVersion(app.Version, \"8.4.0\") && (req.Type == constant.AppMysql || req.Type == constant.AppMysqlCluster) {\n\t\tmasterStatus = \"show binary log status;\"\n\t}\n\trows, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, masterStatus)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(rows) > 2 {\n\t\titemValue := strings.Split(rows[1], \"\\t\")\n\t\tif len(itemValue) > 2 {\n\t\t\tinfo.File = itemValue[0]\n\t\t\tinfo.Position = itemValue[1]\n\t\t}\n\t}\n\n\treturn &info, nil\n}\n\nfunc (u *MysqlService) LoadFormatOption(req dto.OperationWithName) []dto.MysqlFormatCollationOption {\n\tdefaultList := []dto.MysqlFormatCollationOption{{Format: \"utf8mb4\"}, {Format: \"utf8mb3\"}, {Format: \"gbk\"}, {Format: \"big5\"}}\n\tclient, _, err := LoadMysqlClientByFrom(req.Name)\n\tif err != nil {\n\t\treturn defaultList\n\t}\n\toptions, err := client.LoadFormatCollation(3)\n\tif err != nil {\n\t\treturn defaultList\n\t}\n\treturn options\n}\n\nfunc executeSqlForMaps(containerName, dbType, password, command string) (map[string]string, error) {\n\tif dbType == \"mysql-cluster\" {\n\t\tdbType = \"mysql\"\n\t}\n\tcmd := exec.Command(\"docker\", \"exec\", containerName, dbType, \"-uroot\", \"-p\"+password, \"-e\", command)\n\tstdout, err := cmd.CombinedOutput()\n\tstdStr := strings.ReplaceAll(string(stdout), \"mysql: [Warning] Using a password on the command line interface can be insecure.\\n\", \"\")\n\tif err != nil || strings.HasPrefix(stdStr, \"ERROR \") {\n\t\treturn nil, errors.New(stdStr)\n\t}\n\n\trows := strings.Split(stdStr, \"\\n\")\n\trowMap := make(map[string]string)\n\tfor _, v := range rows {\n\t\titemRow := strings.Split(v, \"\\t\")\n\t\tif len(itemRow) == 2 {\n\t\t\trowMap[itemRow[0]] = itemRow[1]\n\t\t}\n\t}\n\treturn rowMap, nil\n}\n\nfunc executeSqlForRows(containerName, dbType, password, command string) ([]string, error) {\n\tif dbType == \"mysql-cluster\" {\n\t\tdbType = \"mysql\"\n\t}\n\tcmd := exec.Command(\"docker\", \"exec\", containerName, dbType, \"-uroot\", \"-p\"+password, \"-e\", command)\n\tstdout, err := cmd.CombinedOutput()\n\tstdStr := strings.ReplaceAll(string(stdout), \"mysql: [Warning] Using a password on the command line interface can be insecure.\\n\", \"\")\n\tif err != nil || strings.HasPrefix(stdStr, \"ERROR \") {\n\t\treturn nil, errors.New(stdStr)\n\t}\n\treturn strings.Split(stdStr, \"\\n\"), nil\n}\n\nfunc updateMyCnf(oldFiles []string, group string, param string, value interface{}) []string {\n\tisOn := false\n\thasGroup := false\n\thasKey := false\n\tregItem := re.GetRegex(re.MysqlGroupPattern)\n\tvar newFiles []string\n\ti := 0\n\tfor _, line := range oldFiles {\n\t\ti++\n\t\tif strings.HasPrefix(line, group) {\n\t\t\tisOn = true\n\t\t\thasGroup = true\n\t\t\tnewFiles = append(newFiles, line)\n\t\t\tcontinue\n\t\t}\n\t\tif !isOn {\n\t\t\tnewFiles = append(newFiles, line)\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(line, param+\"=\") || strings.HasPrefix(line, \"# \"+param+\"=\") {\n\t\t\tnewFiles = append(newFiles, fmt.Sprintf(\"%s=%v\", param, value))\n\t\t\thasKey = true\n\t\t\tcontinue\n\t\t}\n\t\tif regItem.Match([]byte(line)) || i == len(oldFiles) {\n\t\t\tisOn = false\n\t\t\tif !hasKey {\n\t\t\t\tnewFiles = append(newFiles, fmt.Sprintf(\"%s=%v\", param, value))\n\t\t\t}\n\t\t\tnewFiles = append(newFiles, line)\n\t\t\tcontinue\n\t\t}\n\t\tnewFiles = append(newFiles, line)\n\t}\n\tif !hasGroup {\n\t\tnewFiles = append(newFiles, group+\"\\n\")\n\t\tnewFiles = append(newFiles, fmt.Sprintf(\"%s=%v\\n\", param, value))\n\t}\n\treturn newFiles\n}\n\nfunc LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {\n\tvar (\n\t\tdbInfo  client.DBInfo\n\t\tversion string\n\t\terr     error\n\t)\n\n\tdbInfo.Timeout = 300\n\tdatabaseItem, err := databaseRepo.Get(repo.WithByName(database))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tdbInfo.Type = databaseItem.Type\n\tdbInfo.From = databaseItem.From\n\tdbInfo.Database = database\n\tif dbInfo.From != \"local\" {\n\t\tdbInfo.Address = databaseItem.Address\n\t\tdbInfo.Port = databaseItem.Port\n\t\tdbInfo.Username = databaseItem.Username\n\t\tdbInfo.Password = databaseItem.Password\n\t\tdbInfo.SSL = databaseItem.SSL\n\t\tdbInfo.ClientKey = databaseItem.ClientKey\n\t\tdbInfo.ClientCert = databaseItem.ClientCert\n\t\tdbInfo.RootCert = databaseItem.RootCert\n\t\tdbInfo.SkipVerify = databaseItem.SkipVerify\n\t\tversion = databaseItem.Version\n\n\t} else {\n\t\tapp, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)\n\t\tif err != nil {\n\t\t\treturn nil, \"\", err\n\t\t}\n\t\tdbInfo.Address = app.ContainerName\n\t\tdbInfo.Username = \"root\"\n\t\tdbInfo.Password = app.Password\n\t\tversion = app.Version\n\t}\n\n\tcli, err := mysql.NewMysqlClient(dbInfo)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\treturn cli, version, nil\n}\n"
  },
  {
    "path": "agent/app/service/database_postgresql.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/postgresql\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client\"\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype PostgresqlService struct{}\n\ntype IPostgresqlService interface {\n\tSearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error)\n\tListDBOption() ([]dto.PostgresqlOption, error)\n\tBindUser(req dto.PostgresqlBindUser) error\n\tCreate(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error)\n\tLoadFromRemote(database string) error\n\tChangePrivileges(req dto.PostgresqlPrivileges) error\n\tChangePassword(info dto.ChangeDBInfo) error\n\tUpdateDescription(req dto.UpdateDescription) error\n\tDeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]dto.DBResource, error)\n\tDelete(ctx context.Context, req dto.PostgresqlDBDelete) error\n}\n\nfunc NewIPostgresqlService() IPostgresqlService {\n\treturn &PostgresqlService{}\n}\n\nfunc (u *PostgresqlService) SearchWithPage(search dto.PostgresqlDBSearch) (int64, interface{}, error) {\n\ttotal, postgresqls, err := postgresqlRepo.Page(search.Page, search.PageSize,\n\t\tpostgresqlRepo.WithByPostgresqlName(search.Database),\n\t\trepo.WithByLikeName(search.Info),\n\t\trepo.WithOrderRuleBy(search.OrderBy, search.Order),\n\t)\n\tvar dtoPostgresqls []dto.PostgresqlDBInfo\n\tfor _, pg := range postgresqls {\n\t\tvar item dto.PostgresqlDBInfo\n\t\tif err := copier.Copy(&item, &pg); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdtoPostgresqls = append(dtoPostgresqls, item)\n\t}\n\treturn total, dtoPostgresqls, err\n}\n\nfunc (u *PostgresqlService) ListDBOption() ([]dto.PostgresqlOption, error) {\n\tpostgresqls, err := postgresqlRepo.List()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdatabases, err := databaseRepo.GetList(databaseRepo.WithTypeList(\"postgresql,mariadb\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar dbs []dto.PostgresqlOption\n\tfor _, pg := range postgresqls {\n\t\tvar item dto.PostgresqlOption\n\t\tif err := copier.Copy(&item, &pg); err != nil {\n\t\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\titem.Database = pg.PostgresqlName\n\t\tfor _, database := range databases {\n\t\t\tif database.Name == item.Database {\n\t\t\t\titem.Type = database.Type\n\t\t\t}\n\t\t}\n\t\tdbs = append(dbs, item)\n\t}\n\treturn dbs, err\n}\n\nfunc (u *PostgresqlService) BindUser(req dto.PostgresqlBindUser) error {\n\tif cmd.CheckIllegal(req.Name, req.Username, req.Password) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tdbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), repo.WithByName(req.Name))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpgClient, err := LoadPostgresqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := pgClient.CreateUser(client.CreateInfo{\n\t\tName:      req.Name,\n\t\tUsername:  req.Username,\n\t\tPassword:  req.Password,\n\t\tSuperUser: req.SuperUser,\n\t\tTimeout:   300,\n\t}, false); err != nil {\n\t\treturn err\n\t}\n\tpass, err := encrypt.StringEncrypt(req.Password)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decrypt database db password failed, err: %v\", err)\n\t}\n\tif err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{\n\t\t\"username\":   req.Username,\n\t\t\"password\":   pass,\n\t\t\"super_user\": req.SuperUser,\n\t}); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *PostgresqlService) Create(ctx context.Context, req dto.PostgresqlDBCreate) (*model.DatabasePostgresql, error) {\n\tif cmd.CheckIllegal(req.Name, req.Username, req.Password, req.Format) {\n\t\treturn nil, buserr.New(\"ErrCmdIllegal\")\n\t}\n\n\tpgsql, _ := postgresqlRepo.Get(repo.WithByName(req.Name), postgresqlRepo.WithByPostgresqlName(req.Database), repo.WithByFrom(req.From))\n\tif pgsql.ID != 0 {\n\t\treturn nil, buserr.New(\"ErrRecordExist\")\n\t}\n\n\tvar createItem model.DatabasePostgresql\n\tif err := copier.Copy(&createItem, &req); err != nil {\n\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\n\tcli, err := LoadPostgresqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cli.Close()\n\n\tcreateItem.PostgresqlName = req.Database\n\tdefer cli.Close()\n\tif err := cli.Create(client.CreateInfo{\n\t\tName:      req.Name,\n\t\tUsername:  req.Username,\n\t\tPassword:  req.Password,\n\t\tSuperUser: req.SuperUser,\n\t\tTimeout:   300,\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\n\tglobal.LOG.Infof(\"create database %s successful!\", req.Name)\n\tif err := postgresqlRepo.Create(ctx, &createItem); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &createItem, nil\n}\n\nfunc LoadPostgresqlClientByFrom(database string) (postgresql.PostgresqlClient, error) {\n\tvar (\n\t\tdbInfo client.DBInfo\n\t\terr    error\n\t)\n\n\tdbInfo.Timeout = 300\n\tdatabaseItem, err := databaseRepo.Get(repo.WithByName(database))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdbInfo.From = databaseItem.From\n\tdbInfo.Database = database\n\tif dbInfo.From != \"local\" {\n\t\tdbInfo.Address = databaseItem.Address\n\t\tdbInfo.Port = databaseItem.Port\n\t\tdbInfo.Username = databaseItem.Username\n\t\tdbInfo.Password = databaseItem.Password\n\t\tdbInfo.InitialDB = databaseItem.InitialDB\n\t} else {\n\t\tapp, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdbInfo.From = \"local\"\n\t\tdbInfo.Address = app.ContainerName\n\t\tdbInfo.Username = app.UserName\n\t\tdbInfo.Password = app.Password\n\t\tdbInfo.Port = uint(app.Port)\n\t}\n\n\tcli, err := postgresql.NewPostgresqlClient(dbInfo)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cli, nil\n}\n\nfunc (u *PostgresqlService) LoadFromRemote(database string) error {\n\tclient, err := LoadPostgresqlClientByFrom(database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\n\tdatabases, err := postgresqlRepo.List(postgresqlRepo.WithByPostgresqlName(database))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdatas, err := client.SyncDB()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdeleteList := databases\n\tfor _, data := range datas {\n\t\thasOld := false\n\t\tfor i := 0; i < len(databases); i++ {\n\t\t\tif strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].PostgresqlName, data.PostgresqlName) {\n\t\t\t\thasOld = true\n\t\t\t\tif databases[i].IsDelete {\n\t\t\t\t\t_ = postgresqlRepo.Update(databases[i].ID, map[string]interface{}{\"is_delete\": false})\n\t\t\t\t}\n\t\t\t\tdeleteList = append(deleteList[:i], deleteList[i+1:]...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !hasOld {\n\t\t\tvar createItem model.DatabasePostgresql\n\t\t\tif err := copier.Copy(&createItem, &data); err != nil {\n\t\t\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t\t}\n\t\t\tif err := postgresqlRepo.Create(context.Background(), &createItem); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tfor _, delItem := range deleteList {\n\t\t_ = postgresqlRepo.Update(delItem.ID, map[string]interface{}{\"is_delete\": true})\n\t}\n\treturn nil\n}\n\nfunc (u *PostgresqlService) UpdateDescription(req dto.UpdateDescription) error {\n\treturn postgresqlRepo.Update(req.ID, map[string]interface{}{\"description\": req.Description})\n}\n\nfunc (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]dto.DBResource, error) {\n\tvar res []dto.DBResource\n\tdb, err := postgresqlRepo.Get(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn res, err\n\t}\n\n\twebsite, _ := websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppPostgresql), websiteRepo.WithDBID(req.ID))\n\tif website.ID != 0 {\n\t\tres = append(res, dto.DBResource{\n\t\t\tType: constant.TypeWebsite,\n\t\t\tName: website.PrimaryDomain,\n\t\t})\n\t}\n\twebsite, _ = websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppPostgresqlCluster), websiteRepo.WithDBID(req.ID))\n\tif website.ID != 0 {\n\t\tres = append(res, dto.DBResource{\n\t\t\tType: constant.TypeWebsite,\n\t\t\tName: website.PrimaryDomain,\n\t\t})\n\t}\n\n\tif db.From == \"local\" {\n\t\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)\n\t\tif err != nil {\n\t\t\treturn res, err\n\t\t}\n\t\tapps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))\n\t\tfor _, app := range apps {\n\t\t\tappInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))\n\t\t\tif appInstall.ID != 0 {\n\t\t\t\tres = append(res, dto.DBResource{\n\t\t\t\t\tType: constant.TypeApp,\n\t\t\t\t\tName: appInstall.Name,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t} else {\n\t\tapps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID), appRepo.WithKey(req.Type))\n\t\tfor _, app := range apps {\n\t\t\tappInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))\n\t\t\tif appInstall.ID != 0 {\n\t\t\t\tres = append(res, dto.DBResource{\n\t\t\t\t\tType: constant.TypeApp,\n\t\t\t\t\tName: appInstall.Name,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn res, nil\n}\n\nfunc (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDelete) error {\n\tdb, err := postgresqlRepo.Get(repo.WithByID(req.ID))\n\tif err != nil && !req.ForceDelete {\n\t\treturn err\n\t}\n\tcli, err := LoadPostgresqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tif err := cli.Delete(client.DeleteInfo{\n\t\tName:        db.Name,\n\t\tUsername:    db.Username,\n\t\tForceDelete: req.ForceDelete,\n\t\tTimeout:     300,\n\t}); err != nil && !req.ForceDelete {\n\t\tif strings.HasPrefix(err.Error(), \"drop user\") {\n\t\t\t_ = postgresqlRepo.Update(db.ID, map[string]interface{}{\"is_delete\": true})\n\t\t}\n\t\treturn err\n\t}\n\n\tif req.DeleteBackup {\n\t\tuploadDir := path.Join(global.Dir.DataDir, fmt.Sprintf(\"uploads/database/%s/%s/%s\", req.Type, req.Database, db.Name))\n\t\tif _, err := os.Stat(uploadDir); err == nil {\n\t\t\t_ = os.RemoveAll(uploadDir)\n\t\t}\n\t\tbackupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf(\"database/%s/%s/%s\", req.Type, db.PostgresqlName, db.Name))\n\t\tif _, err := os.Stat(backupDir); err == nil {\n\t\t\t_ = os.RemoveAll(backupDir)\n\t\t}\n\t\t_ = backupRepo.DeleteRecord(ctx, repo.WithByType(req.Type), repo.WithByName(req.Database), repo.WithByDetailName(db.Name))\n\t\tglobal.LOG.Infof(\"delete database %s-%s backups successful\", req.Database, db.Name)\n\t}\n\n\t_ = postgresqlRepo.Delete(ctx, repo.WithByID(db.ID))\n\treturn nil\n}\n\nfunc (u *PostgresqlService) ChangePrivileges(req dto.PostgresqlPrivileges) error {\n\tif cmd.CheckIllegal(req.Database, req.Username) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tdbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(req.Database), repo.WithByName(req.Name))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcli, err := LoadPostgresqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\n\tif err := cli.ChangePrivileges(client.Privileges{Username: req.Username, SuperUser: req.SuperUser, Timeout: 300}); err != nil {\n\t\treturn err\n\t}\n\tif err := postgresqlRepo.Update(dbItem.ID, map[string]interface{}{\n\t\t\"super_user\": req.SuperUser,\n\t}); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error {\n\tif cmd.CheckIllegal(req.Value) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcli, err := LoadPostgresqlClientByFrom(req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tvar (\n\t\tpostgresqlData model.DatabasePostgresql\n\t\tpasswordInfo   client.PasswordChangeInfo\n\t)\n\tpasswordInfo.Password = req.Value\n\tpasswordInfo.Timeout = 300\n\n\tif req.ID != 0 {\n\t\tpostgresqlData, err = postgresqlRepo.Get(repo.WithByID(req.ID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpasswordInfo.Username = postgresqlData.Username\n\t} else {\n\t\tdbItem, err := databaseRepo.Get(repo.WithByType(req.Type), repo.WithByFrom(req.From))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpasswordInfo.Username = dbItem.Username\n\t}\n\tif err := cli.ChangePassword(passwordInfo); err != nil {\n\t\treturn err\n\t}\n\n\tif req.ID != 0 {\n\t\tvar appRess []model.AppInstallResource\n\t\tif req.From == \"local\" {\n\t\t\tapp, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tappRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(postgresqlData.ID))\n\t\t} else {\n\t\t\tappRess, _ = appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(postgresqlData.ID))\n\t\t}\n\t\tfor _, appRes := range appRess {\n\t\t\tappInstall, err := appInstallRepo.GetFirst(repo.WithByID(appRes.AppInstallId))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tappModel, err := appRepo.GetFirst(repo.WithByID(appInstall.AppId))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tglobal.LOG.Infof(\"start to update postgresql password used by app %s-%s\", appModel.Key, appInstall.Name)\n\t\t\tif err := updateInstallInfoInDB(appModel.Key, appInstall.Name, \"user-password\", req.Value); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tglobal.LOG.Info(\"execute password change sql successful\")\n\t\tpass, err := encrypt.StringEncrypt(req.Value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"decrypt database db password failed, err: %v\", err)\n\t\t}\n\t\t_ = postgresqlRepo.Update(postgresqlData.ID, map[string]interface{}{\"password\": pass})\n\t\treturn nil\n\t}\n\n\tif err := updateInstallInfoInDB(req.Type, req.Database, \"password\", req.Value); err != nil {\n\t\treturn err\n\t}\n\tif req.From == \"local\" {\n\t\tremote, err := databaseRepo.Get(repo.WithByName(req.Database))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpass, err := encrypt.StringEncrypt(req.Value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"decrypt database password failed, err: %v\", err)\n\t\t}\n\t\t_ = databaseRepo.Update(remote.ID, map[string]interface{}{\"password\": pass})\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/database_redis.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/docker/docker/api/types/container\"\n\t_ \"github.com/go-sql-driver/mysql\"\n)\n\ntype RedisService struct{}\n\ntype IRedisService interface {\n\tUpdateConf(req dto.RedisConfUpdate) error\n\tUpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error\n\tChangePassword(info dto.ChangeRedisPass) error\n\n\tLoadStatus(req dto.LoadRedisStatus) (*dto.RedisStatus, error)\n\tLoadConf(req dto.LoadRedisStatus) (*dto.RedisConf, error)\n\tLoadPersistenceConf(req dto.LoadRedisStatus) (*dto.RedisPersistence, error)\n\n\tCheckHasCli() bool\n\tInstallCli() error\n}\n\nfunc NewIRedisService() IRedisService {\n\treturn &RedisService{}\n}\n\nfunc (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {\n\tredisInfo, err := appInstallRepo.LoadBaseInfo(req.DBType, req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar confs []redisConfig\n\tconfs = append(confs, redisConfig{key: \"timeout\", value: req.Timeout})\n\tconfs = append(confs, redisConfig{key: \"maxclients\", value: req.Maxclients})\n\tconfs = append(confs, redisConfig{key: \"maxmemory\", value: req.Maxmemory})\n\tif err := confSet(redisInfo.Name, req.DBType, \"\", confs); err != nil {\n\t\treturn err\n\t}\n\tif _, err := compose.Restart(fmt.Sprintf(\"%s/%s/%s/docker-compose.yml\", global.Dir.AppInstallDir, req.DBType, redisInfo.Name)); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (u *RedisService) CheckHasCli() bool {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer client.Close()\n\tcontainerLists, err := client.ContainerList(context.Background(), container.ListOptions{})\n\tif err != nil {\n\t\treturn false\n\t}\n\tfor _, item := range containerLists {\n\t\tif strings.ReplaceAll(item.Names[0], \"/\", \"\") == \"1Panel-redis-cli-tools\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (u *RedisService) InstallCli() error {\n\titem := dto.ContainerOperate{\n\t\tName:     \"1Panel-redis-cli-tools\",\n\t\tImage:    \"redis:7.4.4\",\n\t\tNetworks: []dto.ContainerNetwork{{Network: \"1panel-network\"}},\n\t}\n\treturn NewIContainerService().ContainerCreate(item, false)\n}\n\nfunc (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {\n\tif err := updateInstallInfoInDB(\"redis\", req.Database, \"password\", req.Value); err != nil {\n\t\treturn err\n\t}\n\tremote, err := databaseRepo.Get(repo.WithByName(req.Database))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif remote.From == \"local\" {\n\t\tpass, err := encrypt.StringEncrypt(req.Value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"decrypt database password failed, err: %v\", err)\n\t\t}\n\t\t_ = databaseRepo.Update(remote.ID, map[string]interface{}{\"password\": pass})\n\t}\n\n\treturn nil\n}\n\nfunc (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error {\n\tredisInfo, err := appInstallRepo.LoadBaseInfo(req.DBType, req.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar confs []redisConfig\n\tif req.Type == \"rbd\" {\n\t\tconfs = append(confs, redisConfig{key: \"save\", value: req.Save})\n\t} else {\n\t\tconfs = append(confs, redisConfig{key: \"appendonly\", value: req.Appendonly})\n\t\tconfs = append(confs, redisConfig{key: \"appendfsync\", value: req.Appendfsync})\n\t}\n\tif err := confSet(redisInfo.Name, req.DBType, req.Type, confs); err != nil {\n\t\treturn err\n\t}\n\tif _, err := compose.Restart(fmt.Sprintf(\"%s/%s/%s/docker-compose.yml\", global.Dir.AppInstallDir, req.DBType, redisInfo.Name)); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (u *RedisService) LoadStatus(req dto.LoadRedisStatus) (*dto.RedisStatus, error) {\n\tredisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcommands := append(redisExec(redisInfo.ContainerName, redisInfo.Password), \"info\")\n\tcmd := exec.Command(\"docker\", commands...)\n\tstdout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn nil, errors.New(string(stdout))\n\t}\n\trows := strings.Split(string(stdout), \"\\r\\n\")\n\trowMap := make(map[string]string)\n\tfor _, v := range rows {\n\t\titemRow := strings.Split(v, \":\")\n\t\tif len(itemRow) == 2 {\n\t\t\trowMap[itemRow[0]] = itemRow[1]\n\t\t}\n\t}\n\tvar info dto.RedisStatus\n\tarr, err := json.Marshal(rowMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_ = json.Unmarshal(arr, &info)\n\treturn &info, nil\n}\n\nfunc (u *RedisService) LoadConf(req dto.LoadRedisStatus) (*dto.RedisConf, error) {\n\tredisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar item dto.RedisConf\n\titem.ContainerName = redisInfo.ContainerName\n\titem.Name = redisInfo.Name\n\titem.Port = redisInfo.Port\n\titem.Requirepass = redisInfo.Password\n\titem.Timeout, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, \"timeout\")\n\titem.Maxclients, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, \"maxclients\")\n\titem.Maxmemory, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, \"maxmemory\")\n\treturn &item, nil\n}\n\nfunc (u *RedisService) LoadPersistenceConf(req dto.LoadRedisStatus) (*dto.RedisPersistence, error) {\n\tredisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar item dto.RedisPersistence\n\tif item.Appendonly, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, \"appendonly\"); err != nil {\n\t\treturn nil, err\n\t}\n\tif item.Appendfsync, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, \"appendfsync\"); err != nil {\n\t\treturn nil, err\n\t}\n\tif item.Save, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, \"save\"); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &item, nil\n}\n\nfunc configGetStr(containerName, password, param string) (string, error) {\n\tcommands := append(redisExec(containerName, password), []string{\"config\", \"get\", param}...)\n\tcmd := exec.Command(\"docker\", commands...)\n\tstdout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn \"\", errors.New(string(stdout))\n\t}\n\trows := strings.Split(string(stdout), \"\\r\\n\")\n\tfor _, v := range rows {\n\t\titemRow := strings.Split(v, \"\\n\")\n\t\tif len(itemRow) == 3 {\n\t\t\treturn itemRow[1], nil\n\t\t}\n\t}\n\treturn \"\", nil\n}\n\ntype redisConfig struct {\n\tkey   string\n\tvalue string\n}\n\nfunc confSet(redisName string, redisType string, updateType string, changeConf []redisConfig) error {\n\tpath := fmt.Sprintf(\"%s/%s/%s/conf/redis.conf\", global.Dir.AppInstallDir, redisType, redisName)\n\tlineBytes, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfiles := strings.Split(string(lineBytes), \"\\n\")\n\n\tstartIndex, endIndex, emptyLine := 0, 0, 0\n\tvar newFiles []string\n\tfor i := 0; i < len(files); i++ {\n\t\tif files[i] == \"# Redis configuration rewrite by 1Panel\" {\n\t\t\tstartIndex = i\n\t\t\tnewFiles = append(newFiles, files[i])\n\t\t\tcontinue\n\t\t}\n\t\tif files[i] == \"# End Redis configuration rewrite by 1Panel\" {\n\t\t\tendIndex = i\n\t\t\tbreak\n\t\t}\n\t\tif startIndex == 0 && strings.HasPrefix(files[i], \"save \") {\n\t\t\tnewFiles = append(newFiles, \"#   \"+files[i])\n\t\t\tcontinue\n\t\t}\n\t\tif startIndex != 0 && endIndex == 0 && (len(files[i]) == 0 || (updateType == \"rbd\" && strings.HasPrefix(files[i], \"save \"))) {\n\t\t\temptyLine++\n\t\t\tcontinue\n\t\t}\n\t\tnewFiles = append(newFiles, files[i])\n\t}\n\tif startIndex == 0 {\n\t\tnewFiles = append(newFiles, \"# Redis configuration rewrite by 1Panel\")\n\t\tstartIndex = len(newFiles) - 1\n\t}\n\tendIndex = endIndex - emptyLine\n\tfor _, item := range changeConf {\n\t\tif item.key == \"save\" {\n\t\t\tsaveVal := strings.Split(item.value, \",\")\n\t\t\tfor i := 0; i < len(saveVal); i++ {\n\t\t\t\tnewFiles = append(newFiles, \"save \"+saveVal[i])\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tisExist := false\n\t\tfor i := startIndex; i < endIndex; i++ {\n\t\t\tif strings.HasPrefix(newFiles[i], item.key) || strings.HasPrefix(newFiles[i], \"# \"+item.key) {\n\t\t\t\tif item.value == \"0\" || len(item.value) == 0 {\n\t\t\t\t\tnewFiles[i] = fmt.Sprintf(\"# %s %s\", item.key, item.value)\n\t\t\t\t} else {\n\t\t\t\t\tnewFiles[i] = fmt.Sprintf(\"%s %s\", item.key, item.value)\n\t\t\t\t}\n\t\t\t\tisExist = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !isExist {\n\t\t\tif item.value == \"0\" || len(item.value) == 0 {\n\t\t\t\tnewFiles = append(newFiles, fmt.Sprintf(\"# %s %s\", item.key, item.value))\n\t\t\t} else {\n\t\t\t\tnewFiles = append(newFiles, fmt.Sprintf(\"%s %s\", item.key, item.value))\n\t\t\t}\n\t\t}\n\t}\n\tnewFiles = append(newFiles, \"# End Redis configuration rewrite by 1Panel\")\n\n\tfile, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\t_, err = file.WriteString(strings.Join(newFiles, \"\\n\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc redisExec(containerName, password string) []string {\n\tcmds := []string{\"exec\", containerName, \"redis-cli\", \"-a\", password, \"--no-auth-warning\"}\n\tif len(password) == 0 {\n\t\tcmds = []string{\"exec\", containerName, \"redis-cli\"}\n\t}\n\treturn cmds\n}\n"
  },
  {
    "path": "agent/app/service/device.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ntp\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/psutil\"\n\t\"github.com/shirou/gopsutil/v4/mem\"\n)\n\nconst defaultDNSPath = \"/etc/resolv.conf\"\nconst defaultHostPath = \"/etc/hosts\"\nconst defaultFstab = \"/etc/fstab\"\n\ntype DeviceService struct{}\n\ntype IDeviceService interface {\n\tLoadBaseInfo() (dto.DeviceBaseInfo, error)\n\tUpdate(key, value string) error\n\tUpdateHosts(req []dto.HostHelper) error\n\tUpdatePasswd(req dto.ChangePasswd) error\n\tUpdateSwap(req dto.SwapHelper) error\n\tUpdateByConf(req dto.UpdateByNameAndFile) error\n\tLoadTimeZone() ([]string, error)\n\tCheckDNS(key, value string) (bool, error)\n\tLoadConf(name string) (string, error)\n\tLoadUsers() ([]string, error)\n\n\tScan() dto.CleanData\n\tClean(req []dto.Clean)\n}\n\nfunc NewIDeviceService() IDeviceService {\n\treturn &DeviceService{}\n}\n\nfunc (u *DeviceService) LoadBaseInfo() (dto.DeviceBaseInfo, error) {\n\tvar baseInfo dto.DeviceBaseInfo\n\tbaseInfo.LocalTime = time.Now().Format(\"2006-01-02 15:04:05 MST -0700\")\n\tbaseInfo.TimeZone = common.LoadTimeZoneByCmd()\n\tbaseInfo.DNS = loadDNS()\n\tbaseInfo.Hosts = loadHosts()\n\tbaseInfo.Hostname = loadHostname()\n\tbaseInfo.User = loadUser()\n\tntp, err := settingRepo.Get(settingRepo.WithByKey(\"NtpSite\"))\n\tif err == nil {\n\t\tbaseInfo.Ntp = ntp.Value\n\t}\n\n\tswapInfo, err := mem.SwapMemory()\n\tif err != nil {\n\t\treturn baseInfo, err\n\t}\n\tbaseInfo.SwapMemoryTotal = swapInfo.Total\n\tbaseInfo.SwapMemoryAvailable = swapInfo.Free\n\tbaseInfo.SwapMemoryUsed = swapInfo.Used\n\tif baseInfo.SwapMemoryTotal != 0 {\n\t\tbaseInfo.SwapDetails = loadSwap()\n\t}\n\tdisks := loadDiskInfo()\n\tfor _, item := range disks {\n\t\tbaseInfo.MaxSize += item.Free\n\t}\n\n\treturn baseInfo, nil\n}\n\nfunc (u *DeviceService) LoadTimeZone() ([]string, error) {\n\tstd, err := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Minute)).RunWithStdoutBashC(\"timedatectl list-timezones\")\n\tif err != nil {\n\t\treturn []string{}, err\n\t}\n\treturn strings.Split(std, \"\\n\"), nil\n}\n\nfunc (u *DeviceService) CheckDNS(key, value string) (bool, error) {\n\tcontent, err := os.ReadFile(defaultDNSPath)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer func() { _ = u.UpdateByConf(dto.UpdateByNameAndFile{Name: \"DNS\", File: string(content)}) }()\n\tif key == \"form\" {\n\t\tif err := u.Update(\"DNS\", value); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t} else {\n\t\tif err := u.UpdateByConf(dto.UpdateByNameAndFile{Name: \"DNS\", File: value}); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tconn, err := net.DialTimeout(\"ip4:icmp\", \"www.baidu.com\", time.Second*2)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer func() { _ = conn.Close() }()\n\n\treturn true, nil\n}\n\nfunc (u *DeviceService) Update(key, value string) error {\n\tswitch key {\n\tcase \"TimeZone\":\n\t\tif cmd.CheckIllegal(value) {\n\t\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t\t}\n\t\tif err := ntp.UpdateSystemTimeZone(value); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo controller.RestartPanel(global.IsMaster, true, false)\n\tcase \"DNS\":\n\t\tif err := updateDNS(strings.Split(value, \",\")); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"Hostname\":\n\t\tif cmd.CheckIllegal(value) {\n\t\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t\t}\n\t\tif err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run(\"hostnamectl\", \"set-hostname\", value); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, _ = psutil.HOST.GetHostInfo(true)\n\tcase \"Ntp\", \"LocalTime\":\n\t\tif cmd.CheckIllegal(value) {\n\t\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t\t}\n\t\tntpValue := value\n\t\tif key == \"LocalTime\" {\n\t\t\tntpItem, err := settingRepo.Get(settingRepo.WithByKey(\"NtpSite\"))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tntpValue = ntpItem.Value\n\t\t} else {\n\t\t\tif err := settingRepo.Update(\"NtpSite\", ntpValue); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tntime, err := ntp.GetRemoteTime(ntpValue)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tts := ntime.Format(constant.DateTimeLayout)\n\t\tif err := ntp.UpdateSystemTime(ts); err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"not support such key %s\", key)\n\t}\n\treturn nil\n}\n\nfunc (u *DeviceService) LoadUsers() ([]string, error) {\n\tfile, err := os.ReadFile(\"/etc/passwd\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar users []string\n\tlines := strings.Split(string(file), \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.Contains(line, \":\") {\n\t\t\tusers = append(users, strings.Split(line, \":\")[0])\n\t\t}\n\t}\n\treturn users, nil\n}\n\nfunc (u *DeviceService) UpdateHosts(req []dto.HostHelper) error {\n\tconf, err := os.ReadFile(defaultHostPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read namesever conf of %s failed, err: %v\", defaultHostPath, err)\n\t}\n\tlines := strings.Split(string(conf), \"\\n\")\n\tnewFile := \"\"\n\tfor _, line := range lines {\n\t\tif len(line) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Fields(line)\n\t\tif len(parts) < 2 {\n\t\t\tnewFile += line + \"\\n\"\n\t\t\tcontinue\n\t\t}\n\t\tfor index, item := range req {\n\t\t\tif item.IP == parts[0] && item.Host == strings.Join(parts[1:], \" \") {\n\t\t\t\tnewFile += line + \"\\n\"\n\t\t\t\treq = append(req[:index], req[index+1:]...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tfor _, item := range req {\n\t\tnewFile += fmt.Sprintf(\"%s   %s \\n\", item.IP, item.Host)\n\t}\n\tfile, err := os.OpenFile(defaultHostPath, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(newFile)\n\twrite.Flush()\n\treturn nil\n}\n\nfunc (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {\n\tif cmd.CheckIllegal(req.User, req.Passwd) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif err := cmd.RunDefaultBashCf(\"%s echo '%s:%s' | %s chpasswd\", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd()); err != nil {\n\t\tif strings.Contains(err.Error(), \"does not exist\") {\n\t\t\treturn buserr.New(\"ErrNotExistUser\")\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {\n\ttaskItem, err := task.NewTaskWithOps(req.Path, task.TaskSwapSet, task.TaskScopeSystem, req.TaskID, 1)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create container failed, err: %v\", err)\n\t\treturn err\n\t}\n\n\ttaskItem.AddSubTask(i18n.GetMsgWithMap(\"SetSwap\", map[string]interface{}{\"path\": req.Path, \"size\": common.LoadSizeUnit2F(float64(req.Size * 1024))}), func(t *task.Task) error {\n\t\tif cmd.CheckIllegal(req.Path) {\n\t\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t\t}\n\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))\n\t\tif !req.IsNew {\n\t\t\tif err := cmdMgr.Run(\"swapoff\", req.Path); err != nil {\n\t\t\t\treturn fmt.Errorf(\"handle swapoff %s failed, %v\", req.Path, err)\n\t\t\t}\n\t\t}\n\t\tif req.Size == 0 {\n\t\t\tif req.Path == path.Join(global.Dir.BaseDir, \".1panel_swap\") {\n\t\t\t\t_ = os.Remove(path.Join(global.Dir.BaseDir, \".1panel_swap\"))\n\t\t\t}\n\t\t\treturn operateSwapWithFile(true, req)\n\t\t}\n\t\ttaskItem.LogStart(i18n.GetMsgByKey(\"CreateSwap\"))\n\t\tif err := cmdMgr.Run(\"dd\", \"if=/dev/zero\", fmt.Sprintf(\"of=%s\", req.Path), \"bs=1024\", fmt.Sprintf(\"count=%d\", req.Size)); err != nil {\n\t\t\treturn fmt.Errorf(\"handle dd %s failed, %v\", req.Path, err)\n\t\t}\n\n\t\ttaskItem.Log(\"chmod 0600 \" + req.Path)\n\t\tif err := cmdMgr.Run(\"chmod\", \"0600\", req.Path); err != nil {\n\t\t\treturn fmt.Errorf(\"handle chmod 0600 %s failed, %v\", req.Path, err)\n\t\t}\n\t\ttaskItem.LogStart(i18n.GetMsgByKey(\"FormatSwap\"))\n\t\tif err := cmdMgr.Run(\"mkswap\", \"-f\", req.Path); err != nil {\n\t\t\treturn fmt.Errorf(\"handle mkswap -f %s failed, %v\", req.Path, err)\n\t\t}\n\n\t\ttaskItem.LogStart(i18n.GetMsgByKey(\"EnableSwap\"))\n\t\tif err := cmdMgr.Run(\"swapon\", req.Path); err != nil {\n\t\t\t_ = cmdMgr.Run(\"swapoff\", req.Path)\n\t\t\treturn fmt.Errorf(\"handle swapoff %s failed, %v\", req.Path, err)\n\t\t}\n\t\treturn operateSwapWithFile(false, req)\n\t}, nil)\n\tgo func() {\n\t\t_ = taskItem.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (u *DeviceService) LoadConf(name string) (string, error) {\n\tpathItem := \"\"\n\tswitch name {\n\tcase \"DNS\":\n\t\tpathItem = defaultDNSPath\n\tcase \"Hosts\":\n\t\tpathItem = defaultHostPath\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"not support such name %s\", name)\n\t}\n\tif _, err := os.Stat(pathItem); err != nil {\n\t\treturn \"\", err\n\t}\n\tcontent, err := os.ReadFile(pathItem)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(content), nil\n}\n\nfunc (u *DeviceService) UpdateByConf(req dto.UpdateByNameAndFile) error {\n\tif req.Name != \"DNS\" && req.Name != \"Hosts\" {\n\t\treturn fmt.Errorf(\"not support such name %s\", req.Name)\n\t}\n\tpath := defaultDNSPath\n\tif req.Name == \"Hosts\" {\n\t\tpath = defaultHostPath\n\t}\n\tfile, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(req.File)\n\twrite.Flush()\n\treturn nil\n}\n\nfunc loadDNS() []string {\n\tvar list []string\n\tdnsConf, err := os.ReadFile(defaultDNSPath)\n\tif err == nil {\n\t\tlines := strings.Split(string(dnsConf), \"\\n\")\n\t\tfor _, line := range lines {\n\t\t\tif strings.HasPrefix(line, \"nameserver \") {\n\t\t\t\tlist = append(list, strings.TrimPrefix(line, \"nameserver \"))\n\t\t\t}\n\t\t}\n\t}\n\treturn list\n}\n\nfunc updateDNS(list []string) error {\n\tconf, err := os.ReadFile(defaultDNSPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read nameserver conf of %s failed, err: %v\", defaultDNSPath, err)\n\t}\n\tlines := strings.Split(string(conf), \"\\n\")\n\tnewFile := \"\"\n\tfor _, line := range lines {\n\t\tif len(line) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Fields(line)\n\t\tif len(parts) < 2 || parts[0] != \"nameserver\" {\n\t\t\tnewFile += line + \"\\n\"\n\t\t\tcontinue\n\t\t}\n\t\titemNs := strings.Join(parts[1:], \" \")\n\t\tfor index, item := range list {\n\t\t\tif item == itemNs {\n\t\t\t\tnewFile += line + \"\\n\"\n\t\t\t\tlist = append(list[:index], list[index+1:]...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tfor _, item := range list {\n\t\tif len(item) != 0 {\n\t\t\tnewFile += fmt.Sprintf(\"nameserver %s \\n\", item)\n\t\t}\n\t}\n\tfile, err := os.OpenFile(defaultDNSPath, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(newFile)\n\twrite.Flush()\n\treturn nil\n}\n\nfunc loadHosts() []dto.HostHelper {\n\tvar list []dto.HostHelper\n\thostConf, err := os.ReadFile(defaultHostPath)\n\tif err == nil {\n\t\tlines := strings.Split(string(hostConf), \"\\n\")\n\t\tfor _, line := range lines {\n\t\t\tparts := strings.Fields(line)\n\t\t\tif len(parts) < 2 || strings.HasPrefix(strings.TrimPrefix(line, \" \"), \"#\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlist = append(list, dto.HostHelper{IP: parts[0], Host: strings.Join(parts[1:], \" \")})\n\t\t}\n\t}\n\treturn list\n}\n\nfunc loadHostname() string {\n\tstd, err := cmd.RunDefaultWithStdoutBashC(\"hostname\")\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strings.ReplaceAll(std, \"\\n\", \"\")\n}\n\nfunc loadUser() string {\n\tstd, err := cmd.RunDefaultWithStdoutBashC(\"whoami\")\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strings.ReplaceAll(std, \"\\n\", \"\")\n}\n\nfunc loadSwap() []dto.SwapHelper {\n\tvar data []dto.SwapHelper\n\tstd, err := cmd.RunDefaultWithStdoutBashCf(\"%s swapon --summary\", cmd.SudoHandleCmd())\n\tif err != nil {\n\t\treturn data\n\t}\n\tlines := strings.Split(std, \"\\n\")\n\tfor index, line := range lines {\n\t\tif index == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Fields(line)\n\t\tif len(parts) < 5 {\n\t\t\tcontinue\n\t\t}\n\t\tsizeItem, _ := strconv.Atoi(parts[2])\n\t\tdata = append(data, dto.SwapHelper{Path: parts[0], Size: uint64(sizeItem), Used: parts[3]})\n\t}\n\treturn data\n}\n\nfunc operateSwapWithFile(delete bool, req dto.SwapHelper) error {\n\tconf, err := os.ReadFile(defaultFstab)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read file %s failed, err: %v\", defaultFstab, err)\n\t}\n\tlines := strings.Split(string(conf), \"\\n\")\n\tnewFile := \"\"\n\tfor _, line := range lines {\n\t\tif len(line) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Fields(line)\n\t\tif len(parts) == 6 && parts[0] == req.Path {\n\t\t\tcontinue\n\t\t}\n\t\tnewFile += line + \"\\n\"\n\t}\n\tif !delete {\n\t\tnewFile += fmt.Sprintf(\"%s    swap    swap    defaults    0    0\\n\", req.Path)\n\t}\n\tfile, err := os.OpenFile(defaultFstab, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(newFile)\n\twrite.Flush()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/device_clean.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\tfileUtils \"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker/docker/api/types/build\"\n\t\"github.com/docker/docker/api/types/filters\"\n\t\"github.com/google/uuid\"\n)\n\nconst (\n\trollbackPath = \"1panel/tmp\"\n\tupgradePath  = \"1panel/tmp/upgrade\"\n\tuploadPath   = \"1panel/uploads\"\n\tdownloadPath = \"1panel/download\"\n)\n\nfunc (u *DeviceService) Scan() dto.CleanData {\n\tvar (\n\t\tSystemClean dto.CleanData\n\t\ttreeData    []dto.CleanTree\n\t)\n\tfileOp := fileUtils.NewFileOp()\n\n\toriginalPath := path.Join(global.Dir.BaseDir, \"1panel_original\")\n\toriginalSize, _ := fileOp.GetDirSize(originalPath)\n\ttreeData = append(treeData, dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"1panel_original\",\n\t\tSize:        uint64(originalSize),\n\t\tIsCheck:     originalSize > 0,\n\t\tIsRecommend: true,\n\t\tType:        \"1panel_original\",\n\t\tChildren:    loadTreeWithDir(true, \"1panel_original\", originalPath, fileOp),\n\t})\n\ttreeData = append(treeData, loadUpgradeTree(fileOp))\n\ttreeData = append(treeData, loadAgentPackage(fileOp))\n\n\tSystemClean.BackupClean = loadBackupTree(fileOp)\n\n\trollBackTree := loadRollBackTree(fileOp)\n\trollbackSize := uint64(0)\n\tfor _, rollback := range rollBackTree {\n\t\trollbackSize += rollback.Size\n\t}\n\ttreeData = append(treeData, dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"rollback\",\n\t\tSize:        rollbackSize,\n\t\tIsCheck:     rollbackSize > 0,\n\t\tIsRecommend: true,\n\t\tType:        \"rollback\",\n\t\tChildren:    rollBackTree,\n\t})\n\tSystemClean.SystemClean = treeData\n\n\tuploadTreeData := loadUploadTree(fileOp)\n\tSystemClean.UploadClean = append(SystemClean.UploadClean, uploadTreeData...)\n\n\tdownloadTreeData := loadDownloadTree(fileOp)\n\tSystemClean.DownloadClean = append(SystemClean.DownloadClean, downloadTreeData...)\n\n\tlogTree := loadLogTree(fileOp)\n\tSystemClean.SystemLogClean = append(SystemClean.SystemLogClean, logTree...)\n\n\tcontainerTree := loadContainerTree()\n\tSystemClean.ContainerClean = append(SystemClean.ContainerClean, containerTree...)\n\n\treturn SystemClean\n}\n\nfunc (u *DeviceService) Clean(req []dto.Clean) {\n\tsize := uint64(0)\n\tfor _, item := range req {\n\t\tsize += item.Size\n\t\tswitch item.TreeType {\n\t\tcase \"1panel_original\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, \"1panel_original\", item.Name))\n\n\t\tcase \"upgrade\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, upgradePath, item.Name))\n\n\t\tcase \"agent\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, \"1panel/agent/package\", item.Name))\n\n\t\tcase \"tmp_backup\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.LocalBackupDir, \"tmp\"))\n\t\tcase \"unknown_backup\":\n\t\t\tif strings.HasPrefix(item.Name, path.Join(global.Dir.LocalBackupDir, \"log/website\")) {\n\t\t\t\tdropFileOrDir(item.Name)\n\t\t\t} else {\n\t\t\t\tdropFile(item.Name)\n\t\t\t}\n\n\t\tcase \"rollback\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, \"app\"))\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, \"database\"))\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, \"website\"))\n\t\tcase \"rollback_app\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, \"app\", item.Name))\n\t\tcase \"rollback_database\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, \"database\", item.Name))\n\t\tcase \"rollback_website\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, \"website\", item.Name))\n\n\t\tcase \"upload\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, uploadPath, item.Name))\n\t\tcase \"upload_app\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, uploadPath, \"app\", item.Name))\n\t\tcase \"upload_database\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, uploadPath, \"database\", item.Name))\n\t\tcase \"upload_website\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, uploadPath, \"website\", item.Name))\n\t\tcase \"download\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, downloadPath, item.Name))\n\t\tcase \"download_app\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, downloadPath, \"app\", item.Name))\n\t\tcase \"download_database\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, downloadPath, \"database\", item.Name))\n\t\tcase \"download_website\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.BaseDir, downloadPath, \"website\", item.Name))\n\n\t\tcase \"system_log\":\n\t\t\tif len(item.Name) == 0 {\n\t\t\t\tfiles, _ := os.ReadDir(global.Dir.LogDir)\n\t\t\t\tif len(files) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, file := range files {\n\t\t\t\t\tif file.Name() == \"1Panel-Core.log\" || file.Name() == \"1Panel.log\" || file.IsDir() {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tdropFileOrDir(path.Join(global.Dir.LogDir, file.Name()))\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdropFileOrDir(path.Join(global.Dir.LogDir, item.Name))\n\t\t\t}\n\t\tcase \"task_log\":\n\t\t\tif len(item.Name) == 0 {\n\t\t\t\tfiles, _ := os.ReadDir(global.Dir.TaskDir)\n\t\t\t\tif len(files) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, file := range files {\n\t\t\t\t\tif file.Name() == \"ssl\" || !file.IsDir() {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tdropTaskLog(path.Join(global.Dir.TaskDir, file.Name()))\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdropTaskLog(path.Join(global.Dir.TaskDir, item.Name))\n\t\t\t}\n\t\tcase \"website_log\":\n\t\t\tdropWebsiteLog(item.Name)\n\t\tcase \"script\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.TmpDir, \"script\", item.Name))\n\t\tcase \"images\":\n\t\t\t_, _ = dropImages()\n\t\tcase \"containers\":\n\t\t\t_, _ = dropContainers()\n\t\tcase \"volumes\":\n\t\t\t_, _ = dropVolumes()\n\t\tcase \"build_cache\":\n\t\t\t_, _ = dropBuildCache()\n\t\tcase \"app_tmp_download_version\":\n\t\t\tdropFileOrDir(path.Join(global.Dir.RemoteAppResourceDir, item.Name))\n\t\t}\n\t}\n\n\t_ = cleanEmptyDirs(global.Dir.LocalBackupDir)\n\t_ = settingRepo.Update(\"LastCleanTime\", time.Now().Format(constant.DateTimeLayout))\n\t_ = settingRepo.Update(\"LastCleanSize\", fmt.Sprintf(\"%v\", size))\n\t_ = settingRepo.Update(\"LastCleanData\", fmt.Sprintf(\"%v\", len(req)))\n}\n\nfunc doSystemClean(taskItem *task.Task) func(t *task.Task) error {\n\treturn func(t *task.Task) error {\n\t\tsize := int64(0)\n\t\tfileCount := 0\n\t\tdropWithTask(path.Join(global.Dir.BaseDir, \"1panel_original\"), taskItem, &size, &fileCount)\n\n\t\tupgradePath := path.Join(global.Dir.BaseDir, upgradePath)\n\t\tupgradeFiles, _ := os.ReadDir(upgradePath)\n\t\tif len(upgradeFiles) != 0 {\n\t\t\tsort.Slice(upgradeFiles, func(i, j int) bool {\n\t\t\t\treturn upgradeFiles[i].Name() > upgradeFiles[j].Name()\n\t\t\t})\n\t\t\tfor i := 0; i < len(upgradeFiles); i++ {\n\t\t\t\tif i != 0 {\n\t\t\t\t\tdropWithTask(path.Join(upgradePath, upgradeFiles[i].Name()), taskItem, &size, &fileCount)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdropWithTask(path.Join(global.Dir.LocalBackupDir, \"tmp/system\"), taskItem, &size, &fileCount)\n\n\t\tdropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, \"app\"), taskItem, &size, &fileCount)\n\t\tdropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, \"website\"), taskItem, &size, &fileCount)\n\t\tdropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, \"database\"), taskItem, &size, &fileCount)\n\n\t\tupgrades := path.Join(global.Dir.BaseDir, upgradePath)\n\t\toldUpgradeFiles, _ := os.ReadDir(upgrades)\n\t\tif len(oldUpgradeFiles) != 0 {\n\t\t\tfor i := 0; i < len(oldUpgradeFiles); i++ {\n\t\t\t\tdropWithTask(path.Join(upgrades, oldUpgradeFiles[i].Name()), taskItem, &size, &fileCount)\n\t\t\t}\n\t\t}\n\n\t\tdropWithExclude(path.Join(global.Dir.BaseDir, uploadPath), []string{\"theme\"}, taskItem, &size, &fileCount)\n\t\tdropWithTask(path.Join(global.Dir.BaseDir, downloadPath), taskItem, &size, &fileCount)\n\n\t\tlogFiles, _ := os.ReadDir(global.Dir.LogDir)\n\t\tif len(logFiles) != 0 {\n\t\t\tfor i := 0; i < len(logFiles); i++ {\n\t\t\t\tif logFiles[i].IsDir() {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif logFiles[i].Name() != \"1Panel.log\" && logFiles[i].Name() != \"1Panel-Core.log\" {\n\t\t\t\t\tdropWithTask(path.Join(global.Dir.LogDir, logFiles[i].Name()), taskItem, &size, &fileCount)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcount1, size1 := dropVolumes()\n\t\tsize += int64(size1)\n\t\tfileCount += count1\n\t\tcount2, size2 := dropBuildCache()\n\t\tsize += int64(size2)\n\t\tfileCount += count2\n\n\t\ttimeNow := time.Now().Format(constant.DateTimeLayout)\n\t\tif fileCount != 0 {\n\t\t\ttaskItem.Log(i18n.GetMsgWithMap(\"FileDropSum\", map[string]interface{}{\"size\": common.LoadSizeUnit2F(float64(size)), \"count\": fileCount}))\n\t\t}\n\n\t\t_ = settingRepo.Update(\"LastCleanTime\", timeNow)\n\t\t_ = settingRepo.Update(\"LastCleanSize\", fmt.Sprintf(\"%v\", size))\n\t\t_ = settingRepo.Update(\"LastCleanData\", fmt.Sprintf(\"%v\", fileCount))\n\n\t\treturn nil\n\t}\n}\n\nfunc loadUpgradeTree(fileOp fileUtils.FileOp) dto.CleanTree {\n\tupgradePath := path.Join(global.Dir.BaseDir, upgradePath)\n\tupgradeSize, _ := fileOp.GetDirSize(upgradePath)\n\tupgradeTree := dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"upgrade\",\n\t\tSize:        uint64(upgradeSize),\n\t\tIsCheck:     false,\n\t\tIsRecommend: true,\n\t\tType:        \"upgrade\",\n\t\tChildren:    loadTreeWithDir(true, \"upgrade\", upgradePath, fileOp),\n\t}\n\tif len(upgradeTree.Children) != 0 {\n\t\tsort.Slice(upgradeTree.Children, func(i, j int) bool {\n\t\t\treturn common.CompareVersion(upgradeTree.Children[i].Label, upgradeTree.Children[j].Label)\n\t\t})\n\t\tif global.IsMaster {\n\t\t\tvar copiesSetting model.Setting\n\t\t\t_ = global.CoreDB.Where(\"key = ?\", \"UpgradeBackupCopies\").First(&copiesSetting).Error\n\t\t\tcopies, _ := strconv.Atoi(copiesSetting.Value)\n\t\t\tif copies == 0 || copies > len(upgradeTree.Children) {\n\t\t\t\tcopies = len(upgradeTree.Children)\n\t\t\t}\n\t\t\tfor i := 0; i < copies; i++ {\n\t\t\t\tupgradeTree.Children[i].IsCheck = false\n\t\t\t\tupgradeTree.Children[i].IsRecommend = false\n\t\t\t}\n\t\t} else {\n\t\t\tupgradeTree.Children[0].IsCheck = false\n\t\t\tupgradeTree.Children[0].IsRecommend = false\n\t\t}\n\t}\n\treturn upgradeTree\n}\n\nfunc loadAgentPackage(fileOp fileUtils.FileOp) dto.CleanTree {\n\tpathItem := path.Join(global.Dir.BaseDir, \"1panel/agent/package\")\n\titemTree := dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"agent_packages\",\n\t\tIsCheck:     false,\n\t\tIsRecommend: true,\n\t\tType:        \"agent\",\n\t}\n\tfiles, _ := os.ReadDir(pathItem)\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\titemSize, _ := fileOp.GetDirSize(path.Join(pathItem, file.Name()))\n\t\t\titemTree.Size += uint64(itemSize)\n\t\t\titemTree.Children = append(itemTree.Children, dto.CleanTree{\n\t\t\t\tID:          uuid.NewString(),\n\t\t\t\tLabel:       file.Name(),\n\t\t\t\tName:        file.Name(),\n\t\t\t\tSize:        uint64(itemSize),\n\t\t\t\tIsCheck:     true,\n\t\t\t\tIsRecommend: true,\n\t\t\t\tType:        \"agent\",\n\t\t\t})\n\t\t} else {\n\t\t\titemSize, _ := file.Info()\n\t\t\titemName := file.Name()\n\t\t\tisCurrentVersion := strings.HasPrefix(itemName, fmt.Sprintf(\"1panel-agent_%s_\", global.CONF.Base.Version))\n\t\t\tif isCurrentVersion {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\titemTree.Size += uint64(itemSize.Size())\n\t\t\titemTree.Children = append(itemTree.Children, dto.CleanTree{\n\t\t\t\tID:          uuid.NewString(),\n\t\t\t\tLabel:       itemName,\n\t\t\t\tName:        itemName,\n\t\t\t\tSize:        uint64(itemSize.Size()),\n\t\t\t\tIsCheck:     !isCurrentVersion,\n\t\t\t\tIsRecommend: true,\n\t\t\t\tType:        \"agent\",\n\t\t\t})\n\t\t}\n\t}\n\tif itemTree.Size == 0 {\n\t\titemTree.IsCheck = false\n\t}\n\treturn itemTree\n}\n\nfunc loadBackupTree(fileOp fileUtils.FileOp) []dto.CleanTree {\n\tvar treeData []dto.CleanTree\n\n\ttmpSize, _ := fileOp.GetDirSize(path.Join(global.Dir.LocalBackupDir, \"tmp\"))\n\ttreeData = append(treeData, dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"tmp_backup\",\n\t\tSize:        uint64(tmpSize),\n\t\tIsCheck:     tmpSize != 0,\n\t\tIsRecommend: true,\n\t\tType:        \"tmp_backup\",\n\t})\n\tbackupRecords, _ := backupRepo.ListRecord()\n\tvar recordMap = make(map[string][]string)\n\tfor _, record := range backupRecords {\n\t\tif val, ok := recordMap[record.FileDir]; ok {\n\t\t\tval = append(val, record.FileName)\n\t\t\trecordMap[record.FileDir] = val\n\t\t} else {\n\t\t\trecordMap[record.FileDir] = []string{record.FileName}\n\t\t}\n\t}\n\n\ttreeData = append(treeData, loadUnknownApps(fileOp, recordMap))\n\ttreeData = append(treeData, loadUnknownDbs(fileOp, recordMap))\n\ttreeData = append(treeData, loadUnknownWebsites(fileOp, recordMap))\n\ttreeData = append(treeData, loadUnknownSnapshot(fileOp))\n\ttreeData = append(treeData, loadUnknownWebsiteLog(fileOp))\n\treturn treeData\n}\n\nfunc loadUnknownApps(fileOp fileUtils.FileOp, recordMap map[string][]string) dto.CleanTree {\n\tapps, _ := appInstallRepo.ListBy(context.Background())\n\tvar excludePaths []string\n\tfor _, app := range apps {\n\t\titemName := fmt.Sprintf(\"app/%s/%s\", app.App.Key, app.Name)\n\t\tif val, ok := recordMap[itemName]; ok {\n\t\t\tfor _, item := range val {\n\t\t\t\texcludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, itemName, item))\n\t\t\t}\n\t\t}\n\t}\n\tbackupPath := path.Join(global.Dir.LocalBackupDir, \"app\")\n\ttreeData := dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"unknown_app\",\n\t\tIsCheck:     false,\n\t\tIsRecommend: false,\n\t\tName:        backupPath,\n\t\tType:        \"unknown_backup\",\n\t}\n\t_ = loadFileOrDirWithExclude(fileOp, 0, backupPath, &treeData, excludePaths)\n\treturn treeData\n}\nfunc loadUnknownDbs(fileOp fileUtils.FileOp, recordMap map[string][]string) dto.CleanTree {\n\tdbs, _ := databaseRepo.GetList()\n\tvar excludePaths []string\n\tdbMap := make(map[string]struct{})\n\tfor _, db := range dbs {\n\t\tdbMap[fmt.Sprintf(\"database/%s/%s\", db.Type, db.Name)] = struct{}{}\n\t}\n\tfor key, val := range recordMap {\n\t\titemName := path.Dir(key)\n\t\tif _, ok := dbMap[itemName]; ok {\n\t\t\tfor _, item := range val {\n\t\t\t\texcludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, key, item))\n\t\t\t}\n\t\t}\n\t}\n\tbackupPath := path.Join(global.Dir.LocalBackupDir, \"database\")\n\ttreeData := dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"unknown_database\",\n\t\tName:        backupPath,\n\t\tIsCheck:     false,\n\t\tIsRecommend: false,\n\t\tType:        \"unknown_backup\",\n\t}\n\t_ = loadFileOrDirWithExclude(fileOp, 0, backupPath, &treeData, excludePaths)\n\treturn treeData\n}\nfunc loadUnknownWebsites(fileOp fileUtils.FileOp, recordMap map[string][]string) dto.CleanTree {\n\twebsites, _ := websiteRepo.List()\n\tvar excludePaths []string\n\tfor _, website := range websites {\n\t\titemName := fmt.Sprintf(\"website/%s\", website.Alias)\n\t\tif val, ok := recordMap[itemName]; ok {\n\t\t\tfor _, item := range val {\n\t\t\t\texcludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, itemName, item))\n\t\t\t}\n\t\t}\n\t}\n\tbackupPath := path.Join(global.Dir.LocalBackupDir, \"website\")\n\ttreeData := dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"unknown_website\",\n\t\tName:        backupPath,\n\t\tIsCheck:     false,\n\t\tIsRecommend: false,\n\t\tType:        \"unknown_backup\",\n\t}\n\t_ = loadFileOrDirWithExclude(fileOp, 0, backupPath, &treeData, excludePaths)\n\treturn treeData\n}\nfunc loadUnknownSnapshot(fileOp fileUtils.FileOp) dto.CleanTree {\n\tsnaps, _ := snapshotRepo.GetList()\n\tvar excludePaths []string\n\tfor _, item := range snaps {\n\t\texcludePaths = append(excludePaths, path.Join(global.Dir.LocalBackupDir, \"system_snapshot\", item.Name+\".tar.gz\"))\n\t}\n\tbackupPath := path.Join(global.Dir.LocalBackupDir, \"system_snapshot\")\n\ttreeData := dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"unknown_snapshot\",\n\t\tName:        backupPath,\n\t\tIsCheck:     false,\n\t\tIsRecommend: false,\n\t\tType:        \"unknown_backup\",\n\t}\n\tentries, _ := os.ReadDir(backupPath)\n\tfor _, entry := range entries {\n\t\tchildPath := filepath.Join(backupPath, entry.Name())\n\t\tif isExactPathMatch(childPath, excludePaths) {\n\t\t\tcontinue\n\t\t}\n\t\tchildNode := dto.CleanTree{\n\t\t\tID:          uuid.NewString(),\n\t\t\tLabel:       entry.Name(),\n\t\t\tIsCheck:     false,\n\t\t\tIsRecommend: false,\n\t\t\tName:        childPath,\n\t\t\tType:        \"unknown_backup\",\n\t\t}\n\t\tif entry.IsDir() {\n\t\t\titemSize, _ := fileOp.GetDirSize(childPath)\n\t\t\tchildNode.Size = uint64(itemSize)\n\t\t\tchildNode.IsCheck = true\n\t\t\tchildNode.IsRecommend = true\n\t\t\ttreeData.Size += childNode.Size\n\t\t} else {\n\t\t\tinfo, _ := entry.Info()\n\t\t\tchildNode.Size = uint64(info.Size())\n\t\t\ttreeData.Size += childNode.Size\n\t\t}\n\n\t\ttreeData.Children = append(treeData.Children, childNode)\n\t}\n\treturn treeData\n}\n\nfunc loadUnknownWebsiteLog(fileOp fileUtils.FileOp) dto.CleanTree {\n\ttreeData := dto.CleanTree{\n\t\tID:          uuid.NewString(),\n\t\tLabel:       \"unknown_website_log\",\n\t\tIsCheck:     false,\n\t\tIsRecommend: true,\n\t\tType:        \"unknown_backup\",\n\t}\n\tdir := path.Join(global.Dir.LocalBackupDir, \"log/website\")\n\twebsites, _ := websiteRepo.List()\n\twebsiteMap := make(map[string]struct{})\n\tfor _, website := range websites {\n\t\twebsiteMap[website.Alias] = struct{}{}\n\t}\n\n\tentries, _ := os.ReadDir(dir)\n\tfor _, entry := range entries {\n\t\tif !entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tdirName := entry.Name()\n\t\tif _, ok := websiteMap[dirName]; !ok {\n\t\t\tdirPath := path.Join(dir, dirName)\n\t\t\titemSize, _ := fileOp.GetDirSize(dirPath)\n\t\t\tchildData := dto.CleanTree{\n\t\t\t\tID:          uuid.NewString(),\n\t\t\t\tLabel:       dirName,\n\t\t\t\tIsCheck:     true,\n\t\t\t\tIsRecommend: true,\n\t\t\t\tName:        dirPath,\n\t\t\t\tType:        \"unknown_backup\",\n\t\t\t\tSize:        uint64(itemSize),\n\t\t\t}\n\t\t\ttreeData.Size += uint64(itemSize)\n\t\t\ttreeData.Children = append(treeData.Children, childData)\n\t\t}\n\t}\n\tif treeData.Size > 0 {\n\t\ttreeData.IsCheck = true\n\t}\n\treturn treeData\n}\n\nfunc loadFileOrDirWithExclude(fileOp fileUtils.FileOp, index uint, dir string, rootTree *dto.CleanTree, excludes []string) error {\n\tindex++\n\tentries, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, entry := range entries {\n\t\tchildPath := filepath.Join(dir, entry.Name())\n\t\tif isExactPathMatch(childPath, excludes) {\n\t\t\tcontinue\n\t\t}\n\t\tchildNode := dto.CleanTree{\n\t\t\tID:          uuid.NewString(),\n\t\t\tLabel:       entry.Name(),\n\t\t\tIsCheck:     false,\n\t\t\tIsRecommend: false,\n\t\t\tName:        childPath,\n\t\t\tType:        \"unknown_backup\",\n\t\t}\n\t\tif entry.IsDir() {\n\t\t\tif index < 4 {\n\t\t\t\tif err = loadFileOrDirWithExclude(fileOp, index, childPath, &childNode, excludes); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tchildNode.Size = 0\n\t\t\t\tfor _, child := range childNode.Children {\n\t\t\t\t\tchildNode.Size += child.Size\n\t\t\t\t}\n\t\t\t\trootTree.Size += childNode.Size\n\t\t\t} else {\n\t\t\t\titemSize, _ := fileOp.GetDirSize(childPath)\n\t\t\t\tchildNode.Size = uint64(itemSize)\n\t\t\t\trootTree.Size += childNode.Size\n\t\t\t}\n\t\t} else {\n\t\t\tinfo, _ := entry.Info()\n\t\t\tchildNode.Size = uint64(info.Size())\n\t\t\trootTree.Size += childNode.Size\n\t\t}\n\n\t\trootTree.Children = append(rootTree.Children, childNode)\n\t}\n\treturn nil\n}\n\nfunc isExactPathMatch(path string, excludePaths []string) bool {\n\tcleanPath := filepath.Clean(path)\n\n\tfor _, excludePath := range excludePaths {\n\t\tcleanExclude := filepath.Clean(excludePath)\n\t\tif cleanPath == cleanExclude {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc loadRollBackTree(fileOp fileUtils.FileOp) []dto.CleanTree {\n\tvar treeData []dto.CleanTree\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, \"app\"), \"rollback_app\", fileOp)\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, \"website\"), \"rollback_website\", fileOp)\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, \"database\"), \"rollback_database\", fileOp)\n\n\treturn treeData\n}\n\nfunc loadUploadTree(fileOp fileUtils.FileOp) []dto.CleanTree {\n\tvar treeData []dto.CleanTree\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, \"app\"), \"upload_app\", fileOp)\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, \"website\"), \"upload_website\", fileOp)\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, \"database\"), \"upload_database\", fileOp)\n\n\tpath5 := path.Join(global.Dir.BaseDir, uploadPath)\n\tuploadTreeData := loadTreeWithAllFile(true, path5, \"upload\", path5, fileOp)\n\ttreeData = append(treeData, uploadTreeData...)\n\n\treturn treeData\n}\n\nfunc loadDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {\n\tvar treeData []dto.CleanTree\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, \"app\"), \"download_app\", fileOp)\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, \"website\"), \"download_website\", fileOp)\n\ttreeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, \"database\"), \"download_database\", fileOp)\n\n\tpath5 := path.Join(global.Dir.BaseDir, downloadPath)\n\tuploadTreeData := loadTreeWithAllFile(true, path5, \"download\", path5, fileOp)\n\ttreeData = append(treeData, uploadTreeData...)\n\n\tappTmpDownloadTree := loadAppTmpDownloadTree(fileOp)\n\tif len(appTmpDownloadTree) > 0 {\n\t\tparentTree := dto.CleanTree{\n\t\t\tID:          uuid.NewString(),\n\t\t\tLabel:       \"app_tmp_download\",\n\t\t\tIsCheck:     true,\n\t\t\tIsRecommend: true,\n\t\t\tType:        \"app_tmp_download\",\n\t\t\tName:        \"apps\",\n\t\t}\n\t\tfor _, child := range appTmpDownloadTree {\n\t\t\tparentTree.Size += child.Size\n\t\t}\n\t\tparentTree.Children = appTmpDownloadTree\n\t\ttreeData = append(treeData, parentTree)\n\t}\n\treturn treeData\n}\n\nfunc loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {\n\tvar treeData []dto.CleanTree\n\tpath1 := path.Join(global.Dir.LogDir)\n\tlist1 := loadTreeWithAllFile(true, path1, \"system_log\", path1, fileOp)\n\tsize := uint64(0)\n\tfor _, file := range list1 {\n\t\tsize += file.Size\n\t}\n\ttreeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: \"system_log\", Size: size, Children: list1, Type: \"system_log\", IsRecommend: true})\n\n\tpath2 := path.Join(global.Dir.TaskDir)\n\tlist2 := loadTreeWithDir(false, \"task_log\", path2, fileOp)\n\tsize2, _ := fileOp.GetDirSize(path2)\n\ttreeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: \"task_log\", Size: uint64(size2), Children: list2, Type: \"task_log\"})\n\n\twebsiteLogList := loadWebsiteLogTree(fileOp)\n\tlogTotalSize := uint64(0)\n\tfor _, websiteLog := range websiteLogList {\n\t\tlogTotalSize += websiteLog.Size\n\t}\n\ttreeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: \"website_log\", Size: logTotalSize, Children: websiteLogList, Type: \"website_log\", IsRecommend: false})\n\n\treturn treeData\n}\n\nfunc loadWebsiteLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {\n\twebsites, _ := websiteRepo.List()\n\tif len(websites) == 0 {\n\t\treturn nil\n\t}\n\tvar res []dto.CleanTree\n\tfor _, website := range websites {\n\t\tsize3, _ := fileOp.GetDirSize(path.Join(GetSiteDir(website.Alias), \"log\"))\n\t\tres = append(res, dto.CleanTree{\n\t\t\tID:    uuid.NewString(),\n\t\t\tLabel: website.PrimaryDomain,\n\t\t\tSize:  uint64(size3),\n\t\t\tType:  \"website_log\",\n\t\t\tName:  website.Alias,\n\t\t})\n\t}\n\treturn res\n}\n\nfunc loadAppTmpDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {\n\tappDirs, err := os.ReadDir(global.Dir.RemoteAppResourceDir)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tvar res []dto.CleanTree\n\tfor _, appDir := range appDirs {\n\t\tif !appDir.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tappKey := appDir.Name()\n\t\tapp, _ := appRepo.GetFirst(appRepo.WithKey(appKey))\n\t\tif app.ID == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tappPath := filepath.Join(global.Dir.RemoteAppResourceDir, appKey)\n\t\tversionDirs, err := os.ReadDir(appPath)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tappDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))\n\t\texistingVersions := make(map[string]bool)\n\t\tfor _, appDetail := range appDetails {\n\t\t\texistingVersions[appDetail.Version] = true\n\t\t}\n\t\tvar missingVersions []string\n\t\tfor _, versionDir := range versionDirs {\n\t\t\tif !versionDir.IsDir() {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tversion := versionDir.Name()\n\t\t\tif !existingVersions[version] {\n\t\t\t\tmissingVersions = append(missingVersions, version)\n\t\t\t}\n\t\t}\n\t\tif len(missingVersions) > 0 {\n\t\t\tvar appTree dto.CleanTree\n\t\t\tappTree.ID = uuid.NewString()\n\t\t\tappTree.Label = app.Name\n\t\t\tappTree.Type = \"app_tmp_download\"\n\t\t\tappTree.Name = appKey\n\t\t\tappTree.IsRecommend = true\n\t\t\tappTree.IsCheck = true\n\t\t\tfor _, version := range missingVersions {\n\t\t\t\tversionPath := filepath.Join(appPath, version)\n\t\t\t\tsize, _ := fileOp.GetDirSize(versionPath)\n\t\t\t\tappTree.Size += uint64(size)\n\t\t\t\tappTree.Children = append(appTree.Children, dto.CleanTree{\n\t\t\t\t\tID:          uuid.NewString(),\n\t\t\t\t\tLabel:       version,\n\t\t\t\t\tSize:        uint64(size),\n\t\t\t\t\tIsCheck:     true,\n\t\t\t\t\tIsRecommend: true,\n\t\t\t\t\tType:        \"app_tmp_download_version\",\n\t\t\t\t\tName:        path.Join(appKey, version),\n\t\t\t\t})\n\t\t\t}\n\t\t\tres = append(res, appTree)\n\t\t}\n\t}\n\treturn res\n}\n\nfunc loadContainerTree() []dto.CleanTree {\n\tvar treeData []dto.CleanTree\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn treeData\n\t}\n\tdefer client.Close()\n\tdiskUsage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})\n\tif err != nil {\n\t\treturn treeData\n\t}\n\timageSize := uint64(0)\n\tfor _, file := range diskUsage.Images {\n\t\tif file.Containers == 0 {\n\t\t\timageSize += uint64(file.Size)\n\t\t}\n\t}\n\ttreeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: \"container_images\", Size: imageSize, Children: nil, Type: \"images\", IsRecommend: true})\n\n\tcontainerSize := uint64(0)\n\tfor _, file := range diskUsage.Containers {\n\t\tif file.State != \"running\" {\n\t\t\tcontainerSize += uint64(file.SizeRw)\n\t\t}\n\t}\n\ttreeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: \"container_containers\", Size: containerSize, Children: nil, Type: \"containers\", IsRecommend: true})\n\n\tvolumeSize := uint64(0)\n\tfor _, file := range diskUsage.Volumes {\n\t\tif file.UsageData.RefCount <= 0 {\n\t\t\tvolumeSize += uint64(file.UsageData.Size)\n\t\t}\n\t}\n\ttreeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: \"container_volumes\", Size: volumeSize, IsCheck: volumeSize > 0, Children: nil, Type: \"volumes\", IsRecommend: true})\n\n\tvar buildCacheTotalSize int64\n\tfor _, cache := range diskUsage.BuildCache {\n\t\tif cache.Type == \"source.local\" {\n\t\t\tbuildCacheTotalSize += cache.Size\n\t\t}\n\t}\n\ttreeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: \"build_cache\", Size: uint64(buildCacheTotalSize), IsCheck: buildCacheTotalSize > 0, Type: \"build_cache\", IsRecommend: true})\n\treturn treeData\n}\n\nfunc loadTreeWithCheck(treeData []dto.CleanTree, pathItem, treeType string, fileOp fileUtils.FileOp) []dto.CleanTree {\n\tsize, _ := fileOp.GetDirSize(pathItem)\n\tif size == 0 {\n\t\treturn treeData\n\t}\n\tlist := loadTreeWithAllFile(true, pathItem, treeType, pathItem, fileOp)\n\ttreeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: treeType, Size: uint64(size), IsCheck: size > 0, Children: list, Type: treeType, IsRecommend: true})\n\treturn treeData\n}\n\nfunc loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.FileOp) []dto.CleanTree {\n\tvar lists []dto.CleanTree\n\tfiles, err := os.ReadDir(pathItem)\n\tif err != nil {\n\t\treturn lists\n\t}\n\tfor _, file := range files {\n\t\tif file.Name() == \"ssl\" {\n\t\t\tcontinue\n\t\t}\n\t\tif file.IsDir() {\n\t\t\tsize, err := fileOp.GetDirSize(path.Join(pathItem, file.Name()))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\titem := dto.CleanTree{\n\t\t\t\tID:          uuid.NewString(),\n\t\t\t\tLabel:       file.Name(),\n\t\t\t\tType:        treeType,\n\t\t\t\tSize:        uint64(size),\n\t\t\t\tName:        strings.TrimPrefix(file.Name(), \"/\"),\n\t\t\t\tIsCheck:     isCheck,\n\t\t\t\tIsRecommend: isCheck,\n\t\t\t}\n\t\t\tlists = append(lists, item)\n\t\t}\n\t}\n\treturn lists\n}\n\nfunc loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string, fileOp fileUtils.FileOp) []dto.CleanTree {\n\tvar lists []dto.CleanTree\n\n\tfiles, err := os.ReadDir(pathItem)\n\tif err != nil {\n\t\treturn lists\n\t}\n\tfor _, file := range files {\n\t\tif treeType == \"upload\" && (file.Name() == \"theme\" && file.IsDir()) {\n\t\t\tcontinue\n\t\t}\n\t\tif treeType == \"system_log\" && (file.Name() == \"1Panel-Core.log\" || file.Name() == \"1Panel.log\" || file.IsDir()) {\n\t\t\tcontinue\n\t\t}\n\t\tif (treeType == \"upload\" || treeType == \"download\") && file.IsDir() && (file.Name() == \"app\" || file.Name() == \"database\" || file.Name() == \"website\" || file.Name() == \"directory\") {\n\t\t\tcontinue\n\t\t}\n\t\tsize := uint64(0)\n\t\tname := strings.TrimPrefix(path.Join(pathItem, file.Name()), originalPath+\"/\")\n\t\tif file.IsDir() {\n\t\t\tsizeItem, err := fileOp.GetDirSize(path.Join(pathItem, file.Name()))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsize = uint64(sizeItem)\n\t\t} else {\n\t\t\tfileInfo, err := file.Info()\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsize = uint64(fileInfo.Size())\n\t\t}\n\t\titem := dto.CleanTree{\n\t\t\tID:          uuid.NewString(),\n\t\t\tLabel:       file.Name(),\n\t\t\tType:        treeType,\n\t\t\tSize:        size,\n\t\t\tName:        name,\n\t\t\tIsCheck:     isCheck,\n\t\t\tIsRecommend: isCheck,\n\t\t}\n\t\tif file.IsDir() {\n\t\t\titem.Children = loadTreeWithAllFile(isCheck, originalPath, treeType, path.Join(pathItem, file.Name()), fileOp)\n\t\t}\n\t\tlists = append(lists, item)\n\t}\n\treturn lists\n}\n\nfunc dropFileOrDir(itemPath string) {\n\tif err := os.RemoveAll(itemPath); err != nil {\n\t\tglobal.LOG.Errorf(\"drop file %s failed, err %v\", itemPath, err)\n\t}\n}\nfunc dropFile(itemPath string) {\n\tinfo, err := os.Stat(itemPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tif info.IsDir() {\n\t\treturn\n\t}\n\tif err := os.Remove(itemPath); err != nil {\n\t\tglobal.LOG.Errorf(\"drop file %s failed, err %v\", itemPath, err)\n\t}\n}\n\nfunc dropBuildCache() (int, int) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"do not get docker client\")\n\t\treturn 0, 0\n\t}\n\tdefer client.Close()\n\topts := build.CachePruneOptions{}\n\topts.All = true\n\tres, err := client.BuildCachePrune(context.Background(), opts)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"drop build cache failed, err %v\", err)\n\t\treturn 0, 0\n\t}\n\treturn len(res.CachesDeleted), int(res.SpaceReclaimed)\n}\n\nfunc dropImages() (int, int) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"do not get docker client\")\n\t\treturn 0, 0\n\t}\n\tdefer client.Close()\n\tpruneFilters := filters.NewArgs()\n\tpruneFilters.Add(\"dangling\", \"false\")\n\tres, err := client.ImagesPrune(context.Background(), pruneFilters)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"drop images failed, err %v\", err)\n\t\treturn 0, 0\n\t}\n\treturn len(res.ImagesDeleted), int(res.SpaceReclaimed)\n}\n\nfunc dropContainers() (int, int) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"do not get docker client\")\n\t\treturn 0, 0\n\t}\n\tdefer client.Close()\n\tpruneFilters := filters.NewArgs()\n\tres, err := client.ContainersPrune(context.Background(), pruneFilters)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"drop containers failed, err %v\", err)\n\t\treturn 0, 0\n\t}\n\treturn len(res.ContainersDeleted), int(res.SpaceReclaimed)\n}\n\nfunc dropVolumes() (int, int) {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"do not get docker client\")\n\t\treturn 0, 0\n\t}\n\tdefer client.Close()\n\tpruneFilters := filters.NewArgs()\n\tversions, err := client.ServerVersion(context.Background())\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"do not get docker api versions\")\n\t\treturn 0, 0\n\t}\n\tif common.ComparePanelVersion(versions.APIVersion, \"1.42\") {\n\t\tpruneFilters.Add(\"all\", \"true\")\n\t}\n\tres, err := client.VolumesPrune(context.Background(), pruneFilters)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"drop volumes failed, err %v\", err)\n\t\treturn 0, 0\n\t}\n\treturn len(res.VolumesDeleted), int(res.SpaceReclaimed)\n}\n\nfunc dropWebsiteLog(alias string) {\n\taccessLogPath := path.Join(GetSiteDir(alias), \"log\", \"access.log\")\n\terrorLogPath := path.Join(GetSiteDir(alias), \"log\", \"error.log\")\n\tif err := os.Truncate(accessLogPath, 0); err != nil {\n\t\tglobal.LOG.Errorf(\"truncate access log %s failed, err %v\", accessLogPath, err)\n\t}\n\n\tif err := os.Truncate(errorLogPath, 0); err != nil {\n\t\tglobal.LOG.Errorf(\"truncate error log %s failed, err %v\", errorLogPath, err)\n\t}\n}\n\nfunc dropTaskLog(logDir string) {\n\tfiles, err := os.ReadDir(logDir)\n\tif err != nil {\n\t\treturn\n\t}\n\ttaskType := path.Base(logDir)\n\tvar usedTasks []string\n\tswitch taskType {\n\tcase \"Cronjob\":\n\t\t_ = global.DB.Model(&model.JobRecords{}).Where(\"task_id != ?\", \"\").Select(\"task_id\").Find(&usedTasks).Error\n\tcase \"Snapshot\":\n\t\tvar (\n\t\t\tsnapIDs     []string\n\t\t\trecoverIDs  []string\n\t\t\trollbackIDs []string\n\t\t)\n\t\t_ = global.DB.Model(&model.Snapshot{}).Where(\"task_id != ?\", \"\").Select(\"task_id\").Find(&snapIDs).Error\n\t\t_ = global.DB.Model(&model.Snapshot{}).Where(\"task_recover_id != \", \"\").Select(\"task_id\").Find(&recoverIDs).Error\n\t\t_ = global.DB.Model(&model.Snapshot{}).Where(\"task_rollback_id != ?\", \"\").Select(\"task_id\").Find(&rollbackIDs).Error\n\t\tusedTasks = append(usedTasks, snapIDs...)\n\t\tusedTasks = append(usedTasks, recoverIDs...)\n\t\tusedTasks = append(usedTasks, rollbackIDs...)\n\tcase \"Backup\":\n\t\t_ = global.DB.Model(&model.BackupRecord{}).Where(\"task_id != ?\", \"\").Select(\"task_id\").Find(&usedTasks).Error\n\tcase \"Clam\":\n\t\t_ = global.DB.Model(&model.ClamRecord{}).Where(\"task_id != ?\", \"\").Select(\"task_id\").Find(&usedTasks).Error\n\tcase \"Tamper\":\n\t\txpackDB, err := common.LoadDBConnByPathWithErr(path.Join(global.CONF.Base.InstallDir, \"1panel/db/xpack.db\"), \"xpack.db\")\n\t\tif err == nil {\n\t\t\t_ = xpackDB.Table(\"tampers\").Where(\"task_id != ?\", \"\").Select(\"task_id\").Find(&usedTasks).Error\n\t\t}\n\t\tdefer common.CloseDB(xpackDB)\n\tcase \"System\":\n\t\txpackDB, err := common.LoadDBConnByPathWithErr(path.Join(global.CONF.Base.InstallDir, \"1panel/db/xpack.db\"), \"xpack.db\")\n\t\tif err == nil {\n\t\t\t_ = xpackDB.Model(\"nodes\").Where(\"task_id != ?\", \"\").Select(\"task_id\").Find(&usedTasks).Error\n\t\t}\n\t\tdefer common.CloseDB(xpackDB)\n\tdefault:\n\t\tdropFileOrDir(logDir)\n\t\t_ = taskRepo.Delete(repo.WithByType(taskType))\n\t\treturn\n\t}\n\tusedMap := make(map[string]struct{})\n\tfor _, item := range usedTasks {\n\t\tif _, ok := usedMap[item]; !ok {\n\t\t\tusedMap[item] = struct{}{}\n\t\t}\n\t}\n\tfor _, item := range files {\n\t\tif _, ok := usedMap[strings.TrimSuffix(item.Name(), \".log\")]; ok {\n\t\t\tcontinue\n\t\t}\n\t\t_ = os.Remove(logDir + \"/\" + item.Name())\n\t}\n\t_ = taskRepo.Delete(repo.WithByType(taskType), taskRepo.WithByIDNotIn(usedTasks))\n}\n\nfunc dropWithExclude(pathToDelete string, excludeSubDirs []string, taskItem *task.Task, size *int64, count *int) {\n\tentries, err := os.ReadDir(pathToDelete)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tfor _, entry := range entries {\n\t\tname := entry.Name()\n\t\tfullPath := filepath.Join(pathToDelete, name)\n\t\texcluded := false\n\t\tfor _, ex := range excludeSubDirs {\n\t\t\tif name == ex {\n\t\t\t\texcluded = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif excluded {\n\t\t\tcontinue\n\t\t}\n\t\tdropWithTask(fullPath, taskItem, size, count)\n\t}\n}\n\nfunc dropWithTask(itemPath string, taskItem *task.Task, size *int64, count *int) {\n\titemSize := int64(0)\n\titemCount := 0\n\tscanFile(itemPath, &itemSize, &itemCount)\n\t*size += itemSize\n\t*count += itemCount\n\tif err := os.RemoveAll(itemPath); err != nil {\n\t\ttaskItem.Log(i18n.GetWithNameAndErr(\"FileDropFailed\", itemPath, err))\n\t\treturn\n\t}\n\tif itemCount != 0 {\n\t\ttaskItem.Log(i18n.GetMsgWithMap(\"FileDropSuccess\", map[string]interface{}{\"name\": itemPath, \"count\": itemCount, \"size\": common.LoadSizeUnit2F(float64(itemSize))}))\n\t}\n}\n\nfunc scanFile(pathItem string, size *int64, count *int) {\n\tfiles, _ := os.ReadDir(pathItem)\n\tfor _, f := range files {\n\t\tif f.IsDir() {\n\t\t\tscanFile(path.Join(pathItem, f.Name()), size, count)\n\t\t} else {\n\t\t\tfileInfo, err := f.Info()\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t*count++\n\t\t\t*size += fileInfo.Size()\n\t\t}\n\t}\n}\n\nfunc cleanEmptyDirs(root string) error {\n\tdirsToCheck := make([]string, 0)\n\terr := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tif d.IsDir() {\n\t\t\tdirsToCheck = append(dirsToCheck, path)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor i := len(dirsToCheck) - 1; i >= 0; i-- {\n\t\tdir := dirsToCheck[i]\n\t\tif dir == root {\n\t\t\tcontinue\n\t\t}\n\n\t\tentries, err := os.ReadDir(dir)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(entries) == 0 {\n\t\t\t_ = os.Remove(dir)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/disk.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n)\n\ntype DiskService struct{}\n\ntype IDiskService interface {\n\tGetCompleteDiskInfo() (*response.CompleteDiskInfo, error)\n\tPartitionDisk(req request.DiskPartitionRequest) (string, error)\n\tMountDisk(req request.DiskMountRequest) error\n\tUnmountDisk(req request.DiskUnmountRequest) error\n}\n\nfunc NewIDiskService() IDiskService {\n\treturn &DiskService{}\n}\n\nfunc (s *DiskService) GetCompleteDiskInfo() (*response.CompleteDiskInfo, error) {\n\tvar diskInfos []response.DiskBasicInfo\n\toutput, err := cmd.RunDefaultWithStdoutBashC(\"lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA\")\n\tif err == nil {\n\t\tdiskInfos, err = parseLsblkJsonOutput(output)\n\t\tif err == nil {\n\t\t\tresult := organizeDiskInfo(diskInfos)\n\t\t\treturn &result, nil\n\t\t}\n\t}\n\toutput, err = cmd.RunDefaultWithStdoutBashC(\"lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to run lsblk command: %v\", err)\n\t}\n\tdiskInfos, err = parseLsblkOutput(output)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse lsblk output: %v\", err)\n\t}\n\tresult := organizeDiskInfo(diskInfos)\n\treturn &result, nil\n}\n\nfunc (s *DiskService) PartitionDisk(req request.DiskPartitionRequest) (string, error) {\n\tif cmd.CheckIllegal(req.Device, req.Filesystem, req.MountPoint, req.Label) {\n\t\treturn \"\", buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif !strings.HasPrefix(\"/dev\", req.Device) {\n\t\treq.Device = \"/dev/\" + req.Device\n\t}\n\tif !deviceExists(req.Device) {\n\t\treturn \"\", buserr.WithName(\"DeviceNotFound\", req.Device)\n\t}\n\n\tif isDeviceMounted(req.Device) {\n\t\treturn \"\", buserr.WithName(\"DeviceIsMounted\", req.Device)\n\t}\n\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Second))\n\tif err := cmdMgr.Run(\"partprobe\", req.Device); err != nil {\n\t\treturn \"\", buserr.WithErr(\"PartitionDiskErr\", err)\n\t}\n\n\tif err := cmdMgr.Run(\"parted\", \"-s\", req.Device, \"mklabel\", \"gpt\"); err != nil {\n\t\treturn \"\", buserr.WithErr(\"PartitionDiskErr\", err)\n\t}\n\n\tif err := cmdMgr.Run(\"parted\", \"-s\", req.Device, \"mkpart\", \"primary\", \"1MiB\", \"100%\"); err != nil {\n\t\treturn \"\", buserr.WithErr(\"PartitionDiskErr\", err)\n\t}\n\n\tif err := cmdMgr.Run(\"partprobe\", req.Device); err != nil {\n\t\treturn \"\", buserr.WithErr(\"PartitionDiskErr\", err)\n\t}\n\tpartition := req.Device + \"1\"\n\n\tformatReq := dto.DiskFormatRequest{\n\t\tDevice:     partition,\n\t\tFilesystem: req.Filesystem,\n\t\tLabel:      req.Label,\n\t}\n\tif err := formatDisk(formatReq); err != nil {\n\t\treturn \"\", buserr.WithErr(\"FormatDiskErr\", err)\n\t}\n\n\tif req.MountPoint != \"\" {\n\t\tmountReq := request.DiskMountRequest{\n\t\t\tDevice:     partition,\n\t\t\tMountPoint: req.MountPoint,\n\t\t\tFilesystem: req.Filesystem,\n\t\t\tAutoMount:  req.AutoMount,\n\t\t}\n\t\tif err := s.MountDisk(mountReq); err != nil {\n\t\t\treturn \"\", buserr.WithErr(\"MountDiskErr\", err)\n\t\t}\n\t}\n\n\treturn partition, nil\n}\n\nfunc (s *DiskService) MountDisk(req request.DiskMountRequest) error {\n\tif cmd.CheckIllegal(req.Device, req.MountPoint, req.Filesystem) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif !deviceExists(req.Device) {\n\t\treturn buserr.WithName(\"DeviceNotFound\", req.Device)\n\t}\n\n\tif err := os.MkdirAll(req.MountPoint, 0755); err != nil {\n\t\treturn err\n\t}\n\n\tfileSystem, err := getFilesystemType(req.Device)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif fileSystem == \"\" {\n\t\tformatReq := dto.DiskFormatRequest{\n\t\t\tDevice:     req.Device,\n\t\t\tFilesystem: req.Filesystem,\n\t\t}\n\t\tif err := formatDisk(formatReq); err != nil {\n\t\t\treturn buserr.WithErr(\"FormatDiskErr\", err)\n\t\t}\n\t}\n\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Minute))\n\tif err := cmdMgr.Run(\"mount\", \"-t\", req.Filesystem, req.Device, req.MountPoint); err != nil {\n\t\treturn buserr.WithErr(\"MountDiskErr\", err)\n\t}\n\tif req.AutoMount {\n\t\toptions := \"\"\n\t\tif req.NoFail {\n\t\t\toptions = \"defaults,nofail\"\n\t\t}\n\t\tif err := addToFstabWithOptions(req.Device, req.MountPoint, req.Filesystem, options); err != nil {\n\t\t\treturn buserr.WithErr(\"MountDiskErr\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *DiskService) UnmountDisk(req request.DiskUnmountRequest) error {\n\tif cmd.CheckIllegal(req.MountPoint) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif !isPointMounted(req.MountPoint) {\n\t\treturn buserr.New(\"MountDiskErr\")\n\t}\n\tif err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run(\"umount\", \"-f\", req.MountPoint); err != nil {\n\t\treturn buserr.WithErr(\"MountDiskErr\", err)\n\t}\n\tif err := removeFromFstab(req.MountPoint); err != nil {\n\t\tglobal.LOG.Errorf(\"remove %s mountPoint err: %v\", req.MountPoint, err)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/disk_utils.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype LsblkRaw struct {\n\tBlockdevices []LsblkDevice `json:\"blockdevices\"`\n}\n\ntype LsblkDevice struct {\n\tName       string        `json:\"name\"`\n\tSize       string        `json:\"size\"`\n\tType       string        `json:\"type\"`\n\tMountpoint string        `json:\"mountpoint\"`\n\tFstype     string        `json:\"fstype\"`\n\tModel      string        `json:\"model\"`\n\tSerial     string        `json:\"serial\"`\n\tTran       string        `json:\"tran\"`\n\tRota       bool          `json:\"rota\"`\n\tChildren   []LsblkDevice `json:\"children\"`\n}\n\nfunc parseDevice(dev LsblkDevice) []response.DiskBasicInfo {\n\tvar list []response.DiskBasicInfo\n\n\tif strings.HasPrefix(dev.Name, \"loop\") || strings.HasPrefix(dev.Name, \"dm-\") || dev.Type == \"rom\" {\n\t\treturn list\n\t}\n\n\tif dev.Type == \"lvm\" {\n\t\treturn list\n\t}\n\n\tdiskType := \"Unknown\"\n\tif dev.Type == \"disk\" || dev.Type == \"part\" {\n\t\tif dev.Rota {\n\t\t\tdiskType = \"HDD\"\n\t\t} else {\n\t\t\tdiskType = \"SSD\"\n\t\t}\n\t}\n\n\tmountPoint := dev.Mountpoint\n\tfilesystem := dev.Fstype\n\tsize := dev.Size\n\n\tvar used, avail, totalSize string\n\tvar usePercent int\n\tisMounted := mountPoint != \"\"\n\tisSystem := false\n\n\tif dev.Fstype == \"LVM2_member\" && len(dev.Children) > 0 {\n\t\tfor _, child := range dev.Children {\n\t\t\tif child.Type == \"lvm\" && child.Mountpoint != \"\" {\n\t\t\t\tdevicePath := \"/dev/mapper/\" + child.Name\n\t\t\t\ttotalSize, used, avail, usePercent, _ := getDiskUsageInfo(devicePath)\n\n\t\t\t\tchildInfo := response.DiskBasicInfo{\n\t\t\t\t\tDevice:      dev.Name,\n\t\t\t\t\tSize:        totalSize,\n\t\t\t\t\tModel:       dev.Model,\n\t\t\t\t\tDiskType:    diskType,\n\t\t\t\t\tFilesystem:  child.Fstype,\n\t\t\t\t\tMountPoint:  child.Mountpoint,\n\t\t\t\t\tIsMounted:   true,\n\t\t\t\t\tUsePercent:  usePercent,\n\t\t\t\t\tUsed:        used,\n\t\t\t\t\tAvail:       avail,\n\t\t\t\t\tSerial:      dev.Serial,\n\t\t\t\t\tIsRemovable: dev.Tran == \"usb\",\n\t\t\t\t\tIsSystem:    isSystemDisk(child.Mountpoint),\n\t\t\t\t}\n\t\t\t\tlist = append(list, childInfo)\n\t\t\t}\n\t\t}\n\t\treturn list\n\t} else if isMounted {\n\t\tisSystem = isSystemDisk(mountPoint)\n\t\tdevicePath := \"/dev/\" + dev.Name\n\t\ttotalSize, used, avail, usePercent, _ = getDiskUsageInfo(devicePath)\n\t\tif totalSize != \"\" {\n\t\t\tsize = totalSize\n\t\t}\n\t}\n\n\tinfo := response.DiskBasicInfo{\n\t\tDevice:      dev.Name,\n\t\tSize:        size,\n\t\tModel:       dev.Model,\n\t\tDiskType:    diskType,\n\t\tFilesystem:  filesystem,\n\t\tMountPoint:  mountPoint,\n\t\tIsMounted:   isMounted,\n\t\tUsePercent:  usePercent,\n\t\tUsed:        used,\n\t\tAvail:       avail,\n\t\tSerial:      dev.Serial,\n\t\tIsRemovable: dev.Tran == \"usb\",\n\t\tIsSystem:    isSystem,\n\t}\n\n\tlist = append(list, info)\n\n\tfor _, child := range dev.Children {\n\t\tchildList := parseDevice(child)\n\t\tlist = append(list, childList...)\n\t}\n\n\treturn list\n}\n\nfunc parseLsblkJsonOutput(output string) ([]response.DiskBasicInfo, error) {\n\traw := &LsblkRaw{}\n\tif err := json.Unmarshal([]byte(output), raw); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse lsblk json output: %v\", err)\n\t}\n\tvar disks []response.DiskBasicInfo\n\n\tfor _, dev := range raw.Blockdevices {\n\t\tif strings.HasPrefix(dev.Name, \"loop\") ||\n\t\t\tstrings.HasPrefix(dev.Name, \"dm-\") {\n\t\t\tcontinue\n\t\t}\n\t\tdevList := parseDevice(dev)\n\t\tdisks = append(disks, devList...)\n\t}\n\treturn disks, nil\n}\n\nfunc organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskInfo {\n\tvar result response.CompleteDiskInfo\n\tdiskMap := make(map[string]*response.DiskInfo)\n\tpartitions := make(map[string][]response.DiskBasicInfo)\n\n\tfor _, info := range diskInfos {\n\t\tisPartition := isPartitionDevice(info.Device)\n\n\t\tif isPartition {\n\t\t\tparentDevice := getParentDevice(info.Device)\n\t\t\tpartitions[parentDevice] = append(partitions[parentDevice], info)\n\t\t} else {\n\t\t\tdisk := &response.DiskInfo{\n\t\t\t\tDiskBasicInfo: info,\n\t\t\t\tPartitions:    []response.DiskBasicInfo{},\n\t\t\t}\n\t\t\tdiskMap[info.Device] = disk\n\t\t}\n\t}\n\n\tfor parentDevice, partList := range partitions {\n\t\tif disk, exists := diskMap[parentDevice]; exists {\n\t\t\tfor index, part := range partList {\n\t\t\t\tpart.Device = fmt.Sprintf(\"/dev/%s\", part.Device)\n\t\t\t\tif part.IsSystem {\n\t\t\t\t\tdisk.IsSystem = true\n\t\t\t\t}\n\t\t\t\tpartList[index] = part\n\t\t\t}\n\t\t\tdisk.Partitions = partList\n\t\t}\n\t}\n\n\tvar totalCapacity int64\n\tfor _, disk := range diskMap {\n\t\tcapacity := parseSizeToBytes(disk.Size)\n\t\ttotalCapacity += capacity\n\n\t\tif disk.IsSystem {\n\t\t\tresult.SystemDisks = append(result.SystemDisks, *disk)\n\t\t} else if len(disk.Partitions) == 0 {\n\t\t\tresult.UnpartitionedDisks = append(result.UnpartitionedDisks, disk.DiskBasicInfo)\n\t\t} else {\n\t\t\tresult.Disks = append(result.Disks, *disk)\n\t\t}\n\t}\n\n\tresult.TotalDisks = len(diskMap)\n\tresult.TotalCapacity = totalCapacity\n\n\treturn result\n}\n\nfunc parseLsblkOutput(output string) ([]response.DiskBasicInfo, error) {\n\tlines := strings.Split(strings.TrimSpace(output), \"\\n\")\n\tif len(lines) == 0 {\n\t\treturn nil, fmt.Errorf(\"invalid lsblk output\")\n\t}\n\n\tvar diskInfos []response.DiskBasicInfo\n\tlvmMap := make(map[string]response.DiskBasicInfo)\n\tvar pendingDevices []map[string]string\n\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tfields := parseKeyValuePairs(line)\n\n\t\tname, ok := fields[\"NAME\"]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.HasPrefix(name, \"loop\") ||\n\t\t\tstrings.HasPrefix(name, \"dm-\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tdiskType := fields[\"TYPE\"]\n\t\tmountPoint := fields[\"MOUNTPOINT\"]\n\t\tfsType := fields[\"FSTYPE\"]\n\t\tsize := fields[\"SIZE\"]\n\n\t\tif diskType == \"lvm\" {\n\t\t\ttotal, used, avail, usePercent, _ := getDiskUsageInfo(\"/dev/mapper/\" + name)\n\t\t\tif total != \"\" && fsType != \"\" {\n\t\t\t\tsize = total\n\t\t\t}\n\n\t\t\tlvmInfo := response.DiskBasicInfo{\n\t\t\t\tDevice:      name,\n\t\t\t\tSize:        size,\n\t\t\t\tModel:       fields[\"MODEL\"],\n\t\t\t\tDiskType:    \"LVM\",\n\t\t\t\tIsRemovable: false,\n\t\t\t\tIsSystem:    isSystemDisk(mountPoint),\n\t\t\t\tFilesystem:  fsType,\n\t\t\t\tUsed:        used,\n\t\t\t\tAvail:       avail,\n\t\t\t\tUsePercent:  usePercent,\n\t\t\t\tMountPoint:  mountPoint,\n\t\t\t\tIsMounted:   mountPoint != \"\" && mountPoint != \"-\",\n\t\t\t\tSerial:      fields[\"SERIAL\"],\n\t\t\t}\n\t\t\tlvmMap[name] = lvmInfo\n\t\t} else if diskType == \"disk\" || diskType == \"part\" {\n\t\t\tpendingDevices = append(pendingDevices, fields)\n\t\t}\n\t}\n\n\tfor _, fields := range pendingDevices {\n\t\tname := fields[\"NAME\"]\n\t\tsize := fields[\"SIZE\"]\n\t\tmountPoint := fields[\"MOUNTPOINT\"]\n\t\tfsType := fields[\"FSTYPE\"]\n\t\tmodel := fields[\"MODEL\"]\n\t\tserial := fields[\"SERIAL\"]\n\t\ttran := fields[\"TRAN\"]\n\t\trota := fields[\"ROTA\"]\n\n\t\tvar (\n\t\t\tused, avail, totalSize string\n\t\t\tusePercent             int\n\t\t)\n\t\tif mountPoint != \"\" {\n\t\t\ttotalSize, used, avail, usePercent, _ = getDiskUsageInfo(\"/dev/\" + name)\n\t\t\tif totalSize != \"\" {\n\t\t\t\tsize = totalSize\n\t\t\t}\n\t\t}\n\n\t\tactualMountPoint := mountPoint\n\t\tactualFsType := fsType\n\t\tactualUsed := used\n\t\tactualAvail := avail\n\t\tactualUsePercent := usePercent\n\t\tisMounted := mountPoint != \"\" && mountPoint != \"-\"\n\t\tisSystemPartition := isSystemDisk(mountPoint)\n\n\t\tif fsType == \"LVM2_member\" {\n\t\t\tfor _, lvmInfo := range lvmMap {\n\t\t\t\tif lvmInfo.IsMounted {\n\t\t\t\t\tlvmDiskInfo := response.DiskBasicInfo{\n\t\t\t\t\t\tDevice:      name,\n\t\t\t\t\t\tSize:        lvmInfo.Size,\n\t\t\t\t\t\tModel:       model,\n\t\t\t\t\t\tDiskType:    \"LVM\",\n\t\t\t\t\t\tIsRemovable: tran == \"usb\",\n\t\t\t\t\t\tIsSystem:    lvmInfo.IsSystem,\n\t\t\t\t\t\tFilesystem:  lvmInfo.Filesystem,\n\t\t\t\t\t\tUsed:        lvmInfo.Used,\n\t\t\t\t\t\tAvail:       lvmInfo.Avail,\n\t\t\t\t\t\tUsePercent:  lvmInfo.UsePercent,\n\t\t\t\t\t\tMountPoint:  lvmInfo.MountPoint,\n\t\t\t\t\t\tIsMounted:   true,\n\t\t\t\t\t\tSerial:      serial,\n\t\t\t\t\t}\n\t\t\t\t\tdiskInfos = append(diskInfos, lvmDiskInfo)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tinfo := response.DiskBasicInfo{\n\t\t\tDevice:      name,\n\t\t\tSize:        size,\n\t\t\tModel:       model,\n\t\t\tDiskType:    getDiskType(rota),\n\t\t\tIsRemovable: tran == \"usb\",\n\t\t\tIsSystem:    isSystemPartition,\n\t\t\tFilesystem:  actualFsType,\n\t\t\tUsed:        actualUsed,\n\t\t\tAvail:       actualAvail,\n\t\t\tUsePercent:  actualUsePercent,\n\t\t\tMountPoint:  actualMountPoint,\n\t\t\tIsMounted:   isMounted,\n\t\t\tSerial:      serial,\n\t\t}\n\n\t\tdiskInfos = append(diskInfos, info)\n\t}\n\n\treturn diskInfos, nil\n}\n\nfunc getDiskType(rota string) string {\n\tif rota == \"0\" {\n\t\treturn \"SSD\"\n\t} else if rota == \"1\" {\n\t\treturn \"HDD\"\n\t}\n\treturn \"Unknown\"\n}\n\nfunc parseKeyValuePairs(line string) map[string]string {\n\tfields := make(map[string]string)\n\n\tmatches := re.GetRegex(re.DiskKeyValuePattern).FindAllStringSubmatch(line, -1)\n\tfor _, m := range matches {\n\t\tkey := m[1]\n\t\traw := m[2]\n\n\t\tval := raw\n\t\tif len(val) >= 2 && val[0] == '\"' && val[len(val)-1] == '\"' {\n\t\t\tif unq, err := strconv.Unquote(val); err == nil {\n\t\t\t\tval = unq\n\t\t\t} else {\n\t\t\t\tval = val[1 : len(val)-1]\n\t\t\t}\n\t\t}\n\t\tfields[key] = val\n\t}\n\treturn fields\n}\n\nfunc isPartitionDevice(device string) bool {\n\tif strings.Contains(device, \"nvme\") {\n\t\treturn strings.Contains(device, \"p\") &&\n\t\t\tstrings.ContainsAny(device[strings.LastIndex(device, \"p\")+1:], \"0123456789\")\n\t} else if strings.HasPrefix(device, \"sd\") || strings.HasPrefix(device, \"hd\") {\n\t\treturn len(device) > 3 &&\n\t\t\tstrings.ContainsAny(device[len(device)-1:], \"0123456789\")\n\t} else if strings.HasPrefix(device, \"vd\") {\n\t\treturn len(device) > 3 &&\n\t\t\tstrings.ContainsAny(device[len(device)-1:], \"0123456789\")\n\t}\n\n\treturn false\n}\n\nfunc getParentDevice(device string) string {\n\tif strings.Contains(device, \"nvme\") {\n\t\tif idx := strings.LastIndex(device, \"p\"); idx != -1 {\n\t\t\treturn device[:idx]\n\t\t}\n\t} else {\n\t\treturn strings.TrimRight(device, \"0123456789\")\n\t}\n\n\treturn device\n}\n\nfunc getDiskUsageInfo(device string) (size, used, avail string, usePercent int, err error) {\n\toutput, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf(\"df -h %s | tail -1\", device))\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", 0, nil\n\t}\n\n\tfields := strings.Fields(output)\n\tif len(fields) >= 5 {\n\t\tsize = fields[1]\n\t\tused = fields[2]\n\t\tavail = fields[3]\n\t\tusePercentStr := strings.TrimSuffix(fields[4], \"%\")\n\t\tusePercent, _ = strconv.Atoi(usePercentStr)\n\t}\n\n\treturn size, used, avail, usePercent, nil\n}\n\nfunc formatDisk(req dto.DiskFormatRequest) error {\n\tvar mkfsCmd *exec.Cmd\n\n\tswitch req.Filesystem {\n\tcase \"ext4\":\n\t\tmkfsCmd = exec.Command(\"mkfs.ext4\", \"-F\", req.Device)\n\tcase \"xfs\":\n\t\tif !cmd.Which(\"mkfs.xfs\") {\n\t\t\treturn buserr.New(\"XfsNotFound\")\n\t\t}\n\t\tmkfsCmd = exec.Command(\"mkfs.xfs\", \"-f\", req.Device)\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupport type: %s\", req.Filesystem)\n\t}\n\tif err := mkfsCmd.Run(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc deviceExists(device string) bool {\n\t_, err := os.Stat(device)\n\treturn err == nil\n}\n\nfunc isDeviceMounted(device string) bool {\n\tfile, err := os.Open(\"/proc/mounts\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer file.Close()\n\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tfields := strings.Fields(scanner.Text())\n\t\tif len(fields) >= 2 && fields[0] == device {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc isPointMounted(mountPoint string) bool {\n\tfile, err := os.Open(\"/proc/mounts\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer file.Close()\n\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tfields := strings.Fields(scanner.Text())\n\t\tif len(fields) >= 2 && fields[1] == mountPoint {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc addToFstabWithOptions(device, mountPoint, filesystem, options string) error {\n\tuuid, err := getDeviceUUID(device)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get UUID for device %s: %v\", device, err)\n\t}\n\n\tif filesystem == \"\" {\n\t\tfsType, err := getFilesystemType(device)\n\t\tif err != nil {\n\t\t\tfilesystem = \"auto\"\n\t\t} else {\n\t\t\tfilesystem = fsType\n\t\t}\n\t}\n\n\tif options == \"\" {\n\t\toptions = \"defaults\"\n\t}\n\n\tentry := fmt.Sprintf(\"UUID=%s %s %s %s 0 2\\n\", uuid, mountPoint, filesystem, options)\n\n\tfile, err := os.OpenFile(\"/etc/fstab\", os.O_APPEND|os.O_WRONLY, 0644)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\t_, err = file.WriteString(entry)\n\treturn err\n}\n\nfunc removeFromFstab(mountPoint string) error {\n\tfile, err := os.Open(\"/etc/fstab\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\tvar lines []string\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tfields := strings.Fields(line)\n\t\tif len(fields) >= 2 && fields[1] == mountPoint {\n\t\t\tcontinue\n\t\t}\n\t\tlines = append(lines, line)\n\t}\n\n\treturn os.WriteFile(\"/etc/fstab\", []byte(strings.Join(lines, \"\\n\")+\"\\n\"), 0644)\n}\n\nfunc getFilesystemType(device string) (string, error) {\n\toutput, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf(\"blkid -o value -s TYPE %s\", device))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn strings.TrimSpace(output), nil\n}\n\nfunc isSystemDisk(mountPoint string) bool {\n\tsystemMountPoints := []string{\n\t\t\"/\",\n\t\t\"/boot\",\n\t\t\"/boot/efi\",\n\t\t\"/usr\",\n\t\t\"/var\",\n\t\t\"/etc\",\n\t}\n\n\tfor _, sysMount := range systemMountPoints {\n\t\tif mountPoint == sysMount {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc parseSizeToBytes(sizeStr string) int64 {\n\tif sizeStr == \"\" || sizeStr == \"-\" {\n\t\treturn 0\n\t}\n\n\tsizeStr = strings.TrimSpace(sizeStr)\n\tif len(sizeStr) < 2 {\n\t\treturn 0\n\t}\n\n\tunit := strings.ToUpper(sizeStr[len(sizeStr)-1:])\n\tvalueStr := sizeStr[:len(sizeStr)-1]\n\n\tvalue, err := strconv.ParseFloat(valueStr, 64)\n\tif err != nil {\n\t\treturn 0\n\t}\n\n\tswitch unit {\n\tcase \"K\":\n\t\treturn int64(value * 1024)\n\tcase \"M\":\n\t\treturn int64(value * 1024 * 1024)\n\tcase \"G\":\n\t\treturn int64(value * 1024 * 1024 * 1024)\n\tcase \"T\":\n\t\treturn int64(value * 1024 * 1024 * 1024 * 1024)\n\tdefault:\n\t\tval, _ := strconv.ParseInt(sizeStr, 10, 64)\n\t\treturn val\n\t}\n}\n\nfunc getDeviceUUID(device string) (string, error) {\n\toutput, err := cmd.RunDefaultWithStdoutBashC(fmt.Sprintf(\"blkid -s UUID -o value %s\", device))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tuuid := strings.TrimSpace(output)\n\tif uuid == \"\" {\n\t\treturn \"\", fmt.Errorf(\"no UUID found for device %s\", device)\n\t}\n\treturn uuid, nil\n}\n"
  },
  {
    "path": "agent/app/service/docker.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n)\n\ntype DockerService struct{}\n\ntype IDockerService interface {\n\tUpdateConf(req dto.SettingUpdate, withRestart bool) error\n\tUpdateLogOption(req dto.LogOption) error\n\tUpdateIpv6Option(req dto.Ipv6Option) error\n\tUpdateConfByFile(info dto.DaemonJsonUpdateByFile) error\n\tLoadDockerStatus() *dto.DockerStatus\n\tLoadDockerConf() (*dto.DaemonJsonConf, error)\n\tOperateDocker(req dto.DockerOperation) error\n}\n\nfunc NewIDockerService() IDockerService {\n\treturn &DockerService{}\n}\n\ntype daemonJsonItem struct {\n\tStatus       string    `json:\"status\"`\n\tMirrors      []string  `json:\"registry-mirrors\"`\n\tRegistries   []string  `json:\"insecure-registries\"`\n\tLiveRestore  bool      `json:\"live-restore\"`\n\tIpv6         bool      `json:\"ipv6\"`\n\tFixedCidrV6  string    `json:\"fixed-cidr-v6\"`\n\tIp6Tables    bool      `json:\"ip6tables\"`\n\tExperimental bool      `json:\"experimental\"`\n\tIPTables     bool      `json:\"iptables\"`\n\tExecOpts     []string  `json:\"exec-opts\"`\n\tLogOption    logOption `json:\"log-opts\"`\n}\ntype logOption struct {\n\tLogMaxSize string `json:\"max-size\"`\n\tLogMaxFile string `json:\"max-file\"`\n}\n\nfunc (u *DockerService) LoadDockerStatus() *dto.DockerStatus {\n\tctx := context.Background()\n\tvar data dto.DockerStatus\n\tif !cmd.Which(\"docker\") {\n\t\tdata.IsExist = false\n\t\treturn &data\n\t}\n\tdata.IsExist = true\n\tdata.IsActive = true\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load docker client failed, err: %v\", err)\n\t\tdata.IsActive = false\n\t\treturn &data\n\t}\n\tdefer client.Close()\n\tif _, err := client.Ping(ctx); err != nil {\n\t\tglobal.LOG.Errorf(\"ping docker client failed, err: %v\", err)\n\t\tdata.IsActive = false\n\t}\n\n\treturn &data\n}\n\nfunc (u *DockerService) LoadDockerConf() (*dto.DaemonJsonConf, error) {\n\tctx := context.Background()\n\tvar data dto.DaemonJsonConf\n\tdata.IPTables = true\n\tdata.Version = \"-\"\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn &data, err\n\t}\n\titemVersion, err := client.ServerVersion(ctx)\n\tif err == nil {\n\t\tdata.Version = itemVersion.Version\n\t}\n\tdata.IsSwarm = false\n\tstdout2, _ := cmd.RunDefaultWithStdoutBashC(\"docker info  | grep Swarm\")\n\tif string(stdout2) == \" Swarm: active\\n\" {\n\t\tdata.IsSwarm = true\n\t}\n\tif _, err := os.Stat(constant.DaemonJsonPath); err != nil {\n\t\treturn &data, nil\n\t}\n\tfile, err := os.ReadFile(constant.DaemonJsonPath)\n\tif err != nil {\n\t\treturn &data, nil\n\t}\n\tvar conf daemonJsonItem\n\tdaemonMap := make(map[string]interface{})\n\tif err := json.Unmarshal(file, &daemonMap); err != nil {\n\t\treturn &data, nil\n\t}\n\tarr, err := json.Marshal(daemonMap)\n\tif err != nil {\n\t\treturn &data, err\n\t}\n\tif err := json.Unmarshal(arr, &conf); err != nil {\n\t\treturn &data, err\n\t}\n\tif _, ok := daemonMap[\"iptables\"]; !ok {\n\t\tconf.IPTables = true\n\t}\n\tdata.CgroupDriver = \"cgroupfs\"\n\tfor _, opt := range conf.ExecOpts {\n\t\tif strings.HasPrefix(opt, \"native.cgroupdriver=\") {\n\t\t\tdata.CgroupDriver = strings.ReplaceAll(opt, \"native.cgroupdriver=\", \"\")\n\t\t\tbreak\n\t\t}\n\t}\n\tdata.Ipv6 = conf.Ipv6\n\tdata.FixedCidrV6 = conf.FixedCidrV6\n\tdata.Ip6Tables = conf.Ip6Tables\n\tdata.Experimental = conf.Experimental\n\tdata.LogMaxSize = conf.LogOption.LogMaxSize\n\tdata.LogMaxFile = conf.LogOption.LogMaxFile\n\tdata.Mirrors = conf.Mirrors\n\tdata.Registries = conf.Registries\n\tdata.IPTables = conf.IPTables\n\tdata.LiveRestore = conf.LiveRestore\n\treturn &data, nil\n}\n\nfunc (u *DockerService) UpdateConf(req dto.SettingUpdate, withRestart bool) error {\n\terr := createIfNotExistDaemonJsonFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfile, err := os.ReadFile(constant.DaemonJsonPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdaemonMap := make(map[string]interface{})\n\t_ = json.Unmarshal(file, &daemonMap)\n\tswitch req.Key {\n\tcase \"Registries\":\n\t\treq.Value = strings.TrimSuffix(req.Value, \",\")\n\t\tif len(req.Value) == 0 {\n\t\t\tdelete(daemonMap, \"insecure-registries\")\n\t\t} else {\n\t\t\tdaemonMap[\"insecure-registries\"] = strings.Split(req.Value, \",\")\n\t\t}\n\tcase \"Mirrors\":\n\t\treq.Value = strings.TrimSuffix(req.Value, \",\")\n\t\tif len(req.Value) == 0 {\n\t\t\tdelete(daemonMap, \"registry-mirrors\")\n\t\t} else {\n\t\t\tdaemonMap[\"registry-mirrors\"] = strings.Split(req.Value, \",\")\n\t\t}\n\tcase \"Ipv6\":\n\t\tif req.Value == \"disable\" {\n\t\t\tdelete(daemonMap, \"ipv6\")\n\t\t\tdelete(daemonMap, \"fixed-cidr-v6\")\n\t\t\tdelete(daemonMap, \"ip6tables\")\n\t\t\tdelete(daemonMap, \"experimental\")\n\t\t}\n\tcase \"LogOption\":\n\t\tif req.Value == \"disable\" {\n\t\t\tdelete(daemonMap, \"log-opts\")\n\t\t}\n\tcase \"LiveRestore\":\n\t\tif req.Value == \"disable\" {\n\t\t\tdelete(daemonMap, \"live-restore\")\n\t\t} else {\n\t\t\tdaemonMap[\"live-restore\"] = true\n\t\t}\n\tcase \"IPtables\":\n\t\tif req.Value == \"enable\" {\n\t\t\tdelete(daemonMap, \"iptables\")\n\t\t} else {\n\t\t\tdaemonMap[\"iptables\"] = false\n\t\t}\n\tcase \"Driver\":\n\t\tif opts, ok := daemonMap[\"exec-opts\"]; ok {\n\t\t\tif optsValue, isArray := opts.([]interface{}); isArray {\n\t\t\t\tfor i := 0; i < len(optsValue); i++ {\n\t\t\t\t\tif opt, isStr := optsValue[i].(string); isStr {\n\t\t\t\t\t\tif strings.HasPrefix(opt, \"native.cgroupdriver=\") {\n\t\t\t\t\t\t\toptsValue[i] = \"native.cgroupdriver=\" + req.Value\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif req.Value == \"systemd\" {\n\t\t\t\tdaemonMap[\"exec-opts\"] = []string{\"native.cgroupdriver=systemd\"}\n\t\t\t}\n\t\t}\n\tcase \"http-proxy\", \"https-proxy\":\n\t\tdelete(daemonMap, \"proxies\")\n\t\tif len(req.Value) > 0 {\n\t\t\tproxies := map[string]interface{}{\n\t\t\t\treq.Key: req.Value,\n\t\t\t}\n\t\t\tdaemonMap[\"proxies\"] = proxies\n\t\t}\n\tcase \"socks5-proxy\", \"close-proxy\":\n\t\tdelete(daemonMap, \"proxies\")\n\t\tif len(req.Value) > 0 {\n\t\t\tproxies := map[string]interface{}{\n\t\t\t\t\"http-proxy\":  req.Value,\n\t\t\t\t\"https-proxy\": req.Value,\n\t\t\t}\n\t\t\tdaemonMap[\"proxies\"] = proxies\n\t\t}\n\t}\n\tnewJson, err := json.MarshalIndent(daemonMap, \"\", \"\\t\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif string(newJson) == string(file) {\n\t\treturn nil\n\t}\n\tif err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {\n\t\treturn err\n\t}\n\tif err := validateDockerConfig(); err != nil {\n\t\treturn err\n\t}\n\n\tif withRestart {\n\t\tif err := controller.HandleRestart(\"docker\"); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to restart Docker: %v\", err)\n\t\t}\n\t}\n\treturn nil\n}\nfunc createIfNotExistDaemonJsonFile() error {\n\tif _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar daemonFile *os.File\n\t\tdaemonFile, err = os.Create(constant.DaemonJsonPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer daemonFile.Close()\n\t}\n\treturn nil\n}\n\nfunc (u *DockerService) UpdateLogOption(req dto.LogOption) error {\n\terr := createIfNotExistDaemonJsonFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfile, err := os.ReadFile(constant.DaemonJsonPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdaemonMap := make(map[string]interface{})\n\t_ = json.Unmarshal(file, &daemonMap)\n\n\tchangeLogOption(daemonMap, req.LogMaxFile, req.LogMaxSize)\n\tnewJson, err := json.MarshalIndent(daemonMap, \"\", \"\\t\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {\n\t\treturn err\n\t}\n\n\tif err := validateDockerConfig(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := controller.HandleRestart(\"docker\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to restart Docker: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {\n\terr := createIfNotExistDaemonJsonFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfile, err := os.ReadFile(constant.DaemonJsonPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdaemonMap := make(map[string]interface{})\n\t_ = json.Unmarshal(file, &daemonMap)\n\n\tdaemonMap[\"ipv6\"] = true\n\tdaemonMap[\"fixed-cidr-v6\"] = req.FixedCidrV6\n\tif req.Ip6Tables {\n\t\tdaemonMap[\"ip6tables\"] = req.Ip6Tables\n\t}\n\tif req.Experimental {\n\t\tdaemonMap[\"experimental\"] = req.Experimental\n\t}\n\tnewJson, err := json.MarshalIndent(daemonMap, \"\", \"\\t\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {\n\t\treturn err\n\t}\n\n\tif err := validateDockerConfig(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := controller.HandleRestart(\"docker\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to restart Docker: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {\n\terr := createIfNotExistDaemonJsonFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfile, err := os.OpenFile(constant.DaemonJsonPath, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(req.File)\n\twrite.Flush()\n\n\tif err := validateDockerConfig(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := controller.HandleRestart(\"docker\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to restart Docker: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *DockerService) OperateDocker(req dto.DockerOperation) error {\n\tservice := \"docker\"\n\tif req.Operation == \"stop\" {\n\t\tisSocketActive, _ := controller.CheckExist(\"docker.socket\")\n\t\tif isSocketActive {\n\t\t\tif err := controller.HandleStop(\"docker.socket\"); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"handle stop docker.socket failed, err: %v\", err)\n\t\t\t}\n\t\t}\n\t}\n\tif req.Operation == \"restart\" {\n\t\tif err := validateDockerConfig(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := controller.Handle(req.Operation, service); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc changeLogOption(daemonMap map[string]interface{}, logMaxFile, logMaxSize string) {\n\tif opts, ok := daemonMap[\"log-opts\"]; ok {\n\t\tif len(logMaxFile) != 0 || len(logMaxSize) != 0 {\n\t\t\tdaemonMap[\"log-driver\"] = \"json-file\"\n\t\t}\n\t\toptsMap, isMap := opts.(map[string]interface{})\n\t\tif isMap {\n\t\t\tif len(logMaxFile) != 0 {\n\t\t\t\toptsMap[\"max-file\"] = logMaxFile\n\t\t\t} else {\n\t\t\t\tdelete(optsMap, \"max-file\")\n\t\t\t}\n\t\t\tif len(logMaxSize) != 0 {\n\t\t\t\toptsMap[\"max-size\"] = logMaxSize\n\t\t\t} else {\n\t\t\t\tdelete(optsMap, \"max-size\")\n\t\t\t}\n\t\t\tif len(optsMap) == 0 {\n\t\t\t\tdelete(daemonMap, \"log-opts\")\n\t\t\t}\n\t\t} else {\n\t\t\toptsMap := make(map[string]interface{})\n\t\t\tif len(logMaxFile) != 0 {\n\t\t\t\toptsMap[\"max-file\"] = logMaxFile\n\t\t\t}\n\t\t\tif len(logMaxSize) != 0 {\n\t\t\t\toptsMap[\"max-size\"] = logMaxSize\n\t\t\t}\n\t\t\tif len(optsMap) != 0 {\n\t\t\t\tdaemonMap[\"log-opts\"] = optsMap\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif len(logMaxFile) != 0 || len(logMaxSize) != 0 {\n\t\t\tdaemonMap[\"log-driver\"] = \"json-file\"\n\t\t}\n\t\toptsMap := make(map[string]interface{})\n\t\tif len(logMaxFile) != 0 {\n\t\t\toptsMap[\"max-file\"] = logMaxFile\n\t\t}\n\t\tif len(logMaxSize) != 0 {\n\t\t\toptsMap[\"max-size\"] = logMaxSize\n\t\t}\n\t\tif len(optsMap) != 0 {\n\t\t\tdaemonMap[\"log-opts\"] = optsMap\n\t\t}\n\t}\n}\n\nfunc validateDockerConfig() error {\n\tif !cmd.Which(\"dockerd\") {\n\t\treturn nil\n\t}\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"dockerd --validate\")\n\tif strings.Contains(stdout, \"unknown flag: --validate\") {\n\t\treturn nil\n\t}\n\tif err != nil || (stdout != \"\" && strings.TrimSpace(stdout) != \"configuration OK\") {\n\t\treturn fmt.Errorf(\"Docker configuration validation failed, %v\", err)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/entry.go",
    "content": "package service\n\nimport \"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\nvar (\n\tappRepo                = repo.NewIAppRepo()\n\tappTagRepo             = repo.NewIAppTagRepo()\n\tappDetailRepo          = repo.NewIAppDetailRepo()\n\ttagRepo                = repo.NewITagRepo()\n\tappInstallRepo         = repo.NewIAppInstallRepo()\n\tlauncherRepo           = repo.NewILauncherRepo()\n\tappInstallResourceRepo = repo.NewIAppInstallResourceRpo()\n\tappIgnoreUpgradeRepo   = repo.NewIAppIgnoreUpgradeRepo()\n\n\taiRepo                = repo.NewIAiRepo()\n\tmcpServerRepo         = repo.NewIMcpServerRepo()\n\ttensorrtLLMRepo       = repo.NewITensorRTLLMRepo()\n\tagentRepo             = repo.NewIAgentRepo()\n\tagentAccountRepo      = repo.NewIAgentAccountRepo()\n\tagentAccountModelRepo = repo.NewIAgentAccountModelRepo()\n\n\tmysqlRepo      = repo.NewIMysqlRepo()\n\tpostgresqlRepo = repo.NewIPostgresqlRepo()\n\tdatabaseRepo   = repo.NewIDatabaseRepo()\n\n\timageRepoRepo = repo.NewIImageRepoRepo()\n\tcomposeRepo   = repo.NewIComposeTemplateRepo()\n\n\tscriptRepo  = repo.NewIScriptRepo()\n\tcronjobRepo = repo.NewICronjobRepo()\n\n\thostRepo    = repo.NewIHostRepo()\n\tftpRepo     = repo.NewIFtpRepo()\n\tclamRepo    = repo.NewIClamRepo()\n\tmonitorRepo = repo.NewIMonitorRepo()\n\n\tsettingRepo = repo.NewISettingRepo()\n\tbackupRepo  = repo.NewIBackupRepo()\n\n\twebsiteRepo       = repo.NewIWebsiteRepo()\n\twebsiteDomainRepo = repo.NewIWebsiteDomainRepo()\n\twebsiteDnsRepo    = repo.NewIWebsiteDnsAccountRepo()\n\twebsiteSSLRepo    = repo.NewISSLRepo()\n\twebsiteAcmeRepo   = repo.NewIAcmeAccountRepo()\n\twebsiteCARepo     = repo.NewIWebsiteCARepo()\n\n\tsnapshotRepo = repo.NewISnapshotRepo()\n\n\truntimeRepo       = repo.NewIRunTimeRepo()\n\tphpExtensionsRepo = repo.NewIPHPExtensionsRepo()\n\n\tfavoriteRepo = repo.NewIFavoriteRepo()\n\n\ttaskRepo = repo.NewITaskRepo()\n\n\tgroupRepo = repo.NewIGroupRepo()\n\talertRepo = repo.NewIAlertRepo()\n)\n"
  },
  {
    "path": "agent/app/service/fail2ban.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/toolbox\"\n)\n\nconst defaultFail2BanPath = \"/etc/fail2ban/jail.local\"\n\ntype Fail2BanService struct{}\n\ntype IFail2BanService interface {\n\tLoadBaseInfo() (dto.Fail2BanBaseInfo, error)\n\tSearch(search dto.Fail2BanSearch) ([]string, error)\n\tOperate(operation string) error\n\tOperateSSHD(req dto.Fail2BanSet) error\n\tUpdateConf(req dto.Fail2BanUpdate) error\n\tUpdateConfByFile(req dto.UpdateByFile) error\n}\n\nfunc NewIFail2BanService() IFail2BanService {\n\treturn &Fail2BanService{}\n}\n\nfunc (u *Fail2BanService) LoadBaseInfo() (dto.Fail2BanBaseInfo, error) {\n\tvar baseInfo dto.Fail2BanBaseInfo\n\tclient, err := toolbox.NewFail2Ban()\n\tif err != nil {\n\t\treturn baseInfo, err\n\t}\n\tbaseInfo.IsEnable, baseInfo.IsActive, baseInfo.IsExist = client.Status()\n\tif !baseInfo.IsActive {\n\t\tbaseInfo.Version = \"-\"\n\t} else {\n\t\tbaseInfo.Version = client.Version()\n\t}\n\tconf, err := os.ReadFile(defaultFail2BanPath)\n\tif err != nil {\n\t\tif baseInfo.IsActive {\n\t\t\treturn baseInfo, fmt.Errorf(\"read fail2ban conf of %s failed, err: %v\", defaultFail2BanPath, err)\n\t\t} else {\n\t\t\treturn baseInfo, nil\n\t\t}\n\t}\n\tlines := strings.Split(string(conf), \"\\n\")\n\n\tblock := \"\"\n\tfor _, line := range lines {\n\t\tif strings.HasPrefix(strings.ToLower(line), \"[default]\") {\n\t\t\tblock = \"default\"\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(line, \"[sshd]\") {\n\t\t\tblock = \"sshd\"\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(line, \"[\") {\n\t\t\tblock = \"\"\n\t\t\tcontinue\n\t\t}\n\t\tif block != \"default\" && block != \"sshd\" {\n\t\t\tcontinue\n\t\t}\n\t\tloadFailValue(line, &baseInfo)\n\t}\n\n\treturn baseInfo, nil\n}\n\nfunc (u *Fail2BanService) Search(req dto.Fail2BanSearch) ([]string, error) {\n\tvar list []string\n\tclient, err := toolbox.NewFail2Ban()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif req.Status == \"banned\" {\n\t\tlist, err = client.ListBanned()\n\n\t} else {\n\t\tlist, err = client.ListIgnore()\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn list, nil\n}\n\nfunc (u *Fail2BanService) Operate(operation string) error {\n\tclient, err := toolbox.NewFail2Ban()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn client.Operate(operation)\n}\n\nfunc (u *Fail2BanService) UpdateConf(req dto.Fail2BanUpdate) error {\n\tif req.Key == \"banaction\" {\n\t\tif req.Value == \"firewallcmd-ipset\" || req.Value == \"ufw\" {\n\t\t\titemName := \"ufw\"\n\t\t\tif req.Value == \"firewallcmd-ipset\" {\n\t\t\t\titemName = \"firewalld\"\n\t\t\t}\n\t\t\tclient, err := firewall.NewFirewallClient()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif client.Name() != itemName {\n\t\t\t\treturn buserr.WithName(\"ErrBanAction\", itemName)\n\t\t\t}\n\t\t\tisActive, _ := client.Status()\n\t\t\tif !isActive {\n\t\t\t\treturn buserr.WithName(\"ErrBanAction\", itemName)\n\t\t\t}\n\t\t}\n\t}\n\tif req.Key == \"logpath\" {\n\t\tif _, err := os.Stat(req.Value); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tconf, err := os.ReadFile(defaultFail2BanPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read fail2ban conf of %s failed, err: %v\", defaultFail2BanPath, err)\n\t}\n\tlines := strings.Split(string(conf), \"\\n\")\n\n\tisStart, isEnd, hasKey := false, false, false\n\tnewFile := \"\"\n\tfor index, line := range lines {\n\t\tif !isStart && strings.HasPrefix(line, \"[sshd]\") {\n\t\t\tisStart = true\n\t\t\tnewFile += fmt.Sprintf(\"%s\\n\", line)\n\t\t\tcontinue\n\t\t}\n\t\tif !isStart || isEnd {\n\t\t\tnewFile += fmt.Sprintf(\"%s\\n\", line)\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(line, req.Key) {\n\t\t\thasKey = true\n\t\t\tnewFile += fmt.Sprintf(\"%s = %s\\n\", req.Key, req.Value)\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(line, \"[\") || index == len(lines)-1 {\n\t\t\tisEnd = true\n\t\t\tif !hasKey {\n\t\t\t\tnewFile += fmt.Sprintf(\"%s = %s\\n\", req.Key, req.Value)\n\t\t\t}\n\t\t}\n\t\tnewFile += line\n\t\tif index != len(lines)-1 {\n\t\t\tnewFile += \"\\n\"\n\t\t}\n\t}\n\tfile, err := os.OpenFile(defaultFail2BanPath, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(newFile)\n\twrite.Flush()\n\n\tclient, err := toolbox.NewFail2Ban()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := client.Operate(\"restart\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *Fail2BanService) UpdateConfByFile(req dto.UpdateByFile) error {\n\tfile, err := os.OpenFile(defaultFail2BanPath, os.O_WRONLY|os.O_TRUNC, 0640)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(req.File)\n\twrite.Flush()\n\n\tclient, err := toolbox.NewFail2Ban()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := client.Operate(\"restart\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *Fail2BanService) OperateSSHD(req dto.Fail2BanSet) error {\n\tif req.Operate == \"ignore\" {\n\t\tif err := u.UpdateConf(dto.Fail2BanUpdate{Key: \"ignoreip\", Value: strings.Join(req.IPs, \",\")}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\tclient, err := toolbox.NewFail2Ban()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := client.ReBanIPs(req.IPs); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc loadFailValue(line string, baseInfo *dto.Fail2BanBaseInfo) {\n\tif strings.HasPrefix(line, \"port\") {\n\t\titemValue := strings.ReplaceAll(line, \"port\", \"\")\n\t\titemValue = strings.ReplaceAll(itemValue, \"=\", \"\")\n\t\tbaseInfo.Port, _ = strconv.Atoi(strings.TrimSpace(itemValue))\n\t}\n\tif strings.HasPrefix(line, \"maxretry\") {\n\t\titemValue := strings.ReplaceAll(line, \"maxretry\", \"\")\n\t\titemValue = strings.ReplaceAll(itemValue, \"=\", \"\")\n\t\tbaseInfo.MaxRetry, _ = strconv.Atoi(strings.TrimSpace(itemValue))\n\t}\n\tif strings.HasPrefix(line, \"findtime\") {\n\t\titemValue := strings.ReplaceAll(line, \"findtime\", \"\")\n\t\titemValue = strings.ReplaceAll(itemValue, \"=\", \"\")\n\t\tbaseInfo.FindTime = strings.TrimSpace(itemValue)\n\t}\n\tif strings.HasPrefix(line, \"bantime\") {\n\t\titemValue := strings.ReplaceAll(line, \"bantime\", \"\")\n\t\titemValue = strings.ReplaceAll(itemValue, \"=\", \"\")\n\t\tbaseInfo.BanTime = strings.TrimSpace(itemValue)\n\t}\n\tif strings.HasPrefix(line, \"banaction\") {\n\t\titemValue := strings.ReplaceAll(line, \"banaction\", \"\")\n\t\titemValue = strings.ReplaceAll(itemValue, \"=\", \"\")\n\t\tbaseInfo.BanAction = strings.TrimSpace(itemValue)\n\t}\n\tif strings.HasPrefix(line, \"logpath\") {\n\t\titemValue := strings.ReplaceAll(line, \"logpath\", \"\")\n\t\titemValue = strings.ReplaceAll(itemValue, \"=\", \"\")\n\t\tbaseInfo.LogPath = strings.TrimSpace(itemValue)\n\t}\n}\n"
  },
  {
    "path": "agent/app/service/favorite.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/spf13/afero\"\n)\n\ntype FavoriteService struct {\n}\n\ntype IFavoriteService interface {\n\tPage(req dto.PageInfo) (int64, []response.FavoriteDTO, error)\n\tCreate(req request.FavoriteCreate) (*model.Favorite, error)\n\tDelete(id uint) error\n}\n\nfunc NewIFavoriteService() IFavoriteService {\n\treturn &FavoriteService{}\n}\n\nfunc (f *FavoriteService) Page(req dto.PageInfo) (int64, []response.FavoriteDTO, error) {\n\ttotal, favorites, err := favoriteRepo.Page(req.Page, req.PageSize)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar dtoFavorites []response.FavoriteDTO\n\tfor _, favorite := range favorites {\n\t\tdtoFavorites = append(dtoFavorites, response.FavoriteDTO{\n\t\t\tFavorite: favorite,\n\t\t})\n\t}\n\treturn total, dtoFavorites, nil\n}\n\nfunc (f *FavoriteService) Create(req request.FavoriteCreate) (*model.Favorite, error) {\n\texist, _ := favoriteRepo.GetFirst(favoriteRepo.WithByPath(req.Path))\n\tif exist.ID > 0 {\n\t\treturn nil, buserr.New(\"ErrFavoriteExist\")\n\t}\n\top := files.NewFileOp()\n\tif !op.Stat(req.Path) {\n\t\treturn nil, buserr.New(\"ErrLinkPathNotFound\")\n\t}\n\topenFile, err := op.OpenFile(req.Path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfileInfo, err := openFile.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfavorite := &model.Favorite{\n\t\tName:  fileInfo.Name(),\n\t\tIsDir: fileInfo.IsDir(),\n\t\tPath:  req.Path,\n\t}\n\tif fileInfo.Size() <= 10*1024*1024 {\n\t\tafs := &afero.Afero{Fs: op.Fs}\n\t\tcByte, err := afs.ReadFile(req.Path)\n\t\tif err == nil {\n\t\t\tif len(cByte) > 0 && !files.DetectBinary(cByte) {\n\t\t\t\tfavorite.IsTxt = true\n\t\t\t}\n\t\t}\n\t}\n\tif err := favoriteRepo.Create(favorite); err != nil {\n\t\treturn nil, err\n\t}\n\treturn favorite, nil\n}\n\nfunc (f *FavoriteService) Delete(id uint) error {\n\tif err := favoriteRepo.Delete(repo.WithByID(id)); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/file.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/user\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/convert\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ini_conf\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/jinzhu/copier\"\n\t\"golang.org/x/text/encoding\"\n\t\"golang.org/x/text/encoding/simplifiedchinese\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"golang.org/x/net/html/charset\"\n\t\"golang.org/x/sys/unix\"\n\t\"golang.org/x/text/transform\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/pkg/errors\"\n)\n\ntype FileService struct {\n}\n\ntype IFileService interface {\n\tGetFileList(op request.FileOption) (response.FileInfo, error)\n\tSearchUploadWithPage(req request.SearchUploadWithPage) (int64, interface{}, error)\n\tGetFileTree(op request.FileOption) ([]response.FileTree, error)\n\tCreate(op request.FileCreate) error\n\tDelete(op request.FileDelete) error\n\tBatchDelete(op request.FileBatchDelete) error\n\tCompress(c request.FileCompress) error\n\tDeCompress(c request.FileDeCompress) error\n\tGetContent(op request.FileContentReq) (response.FileInfo, error)\n\tGetPreviewContent(op request.FileContentReq) (response.FileInfo, error)\n\tSaveContent(edit request.FileEdit) error\n\tFileDownload(d request.FileDownload) (string, error)\n\tDirSize(req request.DirSizeReq) (response.DirSizeRes, error)\n\tDepthDirSize(req request.DirSizeReq) ([]response.DepthDirSizeRes, error)\n\tChangeName(req request.FileRename) error\n\tWget(w request.FileWget) (string, error)\n\tMvFile(m request.FileMove) error\n\tChangeOwner(req request.FileRoleUpdate) error\n\tChangeMode(op request.FileCreate) error\n\tBatchChangeModeAndOwner(op request.FileRoleReq) error\n\tReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error)\n\n\tGetPathByType(pathType string) string\n\tBatchCheckFiles(req request.FilePathsCheck) []response.ExistFileInfo\n\tGetHostMount() []dto.DiskInfo\n\tGetUsersAndGroups() (*response.UserGroupResponse, error)\n\tConvert(req request.FileConvertRequest)\n\tConvertLog(req dto.PageInfo) (int64, []response.FileConvertLog, error)\n\tBatchGetRemarks(req request.FileRemarkBatch) map[string]string\n\tSetRemark(req request.FileRemarkUpdate) error\n}\n\nvar filteredPaths = []string{\n\t\"/.1panel_clash\",\n}\n\nconst (\n\tfileRemarkXattr         = \"user.1panel.remark\"\n\tfileRemarkEncodedMaxLen = 256\n)\n\nfunc NewIFileService() IFileService {\n\treturn &FileService{}\n}\n\nfunc (f *FileService) GetFileList(op request.FileOption) (response.FileInfo, error) {\n\tvar fileInfo response.FileInfo\n\tdata, err := os.Stat(op.Path)\n\tif err != nil && os.IsNotExist(err) {\n\t\treturn fileInfo, nil\n\t}\n\tif !data.IsDir() {\n\t\top.FileOption.Path = filepath.Dir(op.FileOption.Path)\n\t}\n\tinfo, err := files.NewFileInfo(op.FileOption)\n\tif err != nil {\n\t\treturn fileInfo, err\n\t}\n\tfileInfo.FileInfo = *info\n\treturn fileInfo, nil\n}\n\nfunc (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (int64, interface{}, error) {\n\tvar (\n\t\tfiles    []response.UploadInfo\n\t\tbackData []response.UploadInfo\n\t)\n\tfileList, err := os.ReadDir(req.Path)\n\tif err != nil {\n\t\treturn 0, files, nil\n\t}\n\tfor _, item := range fileList {\n\t\tif item.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tfileItem, err := item.Info()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tfiles = append(files, response.UploadInfo{\n\t\t\tCreatedAt: fileItem.ModTime().Format(constant.DateTimeLayout),\n\t\t\tSize:      int(fileItem.Size()),\n\t\t\tName:      item.Name(),\n\t\t})\n\t}\n\n\ttotal, start, end := len(files), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\tbackData = make([]response.UploadInfo, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\tbackData = files[start:end]\n\t}\n\treturn int64(total), backData, nil\n}\n\nfunc (f *FileService) GetFileTree(op request.FileOption) ([]response.FileTree, error) {\n\tvar treeArray []response.FileTree\n\tif _, err := os.Stat(op.Path); err != nil && os.IsNotExist(err) {\n\t\treturn treeArray, nil\n\t}\n\tinfo, err := files.NewFileInfo(op.FileOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnode := response.FileTree{\n\t\tID:        common.GetUuid(),\n\t\tName:      info.Name,\n\t\tPath:      info.Path,\n\t\tIsDir:     info.IsDir,\n\t\tExtension: info.Extension,\n\t}\n\terr = f.buildFileTree(&node, info.Items, op, 2)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn append(treeArray, node), nil\n}\n\nfunc shouldFilterPath(path string) bool {\n\tcleanedPath := filepath.Clean(path)\n\tfor _, filteredPath := range filteredPaths {\n\t\tcleanedFilteredPath := filepath.Clean(filteredPath)\n\t\tif cleanedFilteredPath == cleanedPath || strings.HasPrefix(cleanedPath, cleanedFilteredPath+\"/\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (f *FileService) buildFileTree(node *response.FileTree, items []*files.FileInfo, op request.FileOption, level int) error {\n\tfor _, v := range items {\n\t\tif shouldFilterPath(v.Path) {\n\t\t\tglobal.LOG.Infof(\"File Tree: Skipping %s due to filter\\n\", v.Path)\n\t\t\tcontinue\n\t\t}\n\t\tchildNode := response.FileTree{\n\t\t\tID:        common.GetUuid(),\n\t\t\tName:      v.Name,\n\t\t\tPath:      v.Path,\n\t\t\tIsDir:     v.IsDir,\n\t\t\tExtension: v.Extension,\n\t\t}\n\t\tif level > 1 && v.IsDir {\n\t\t\tif err := f.buildChildNode(&childNode, v, op, level); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tnode.Children = append(node.Children, childNode)\n\t}\n\treturn nil\n}\n\nfunc (f *FileService) buildChildNode(childNode *response.FileTree, fileInfo *files.FileInfo, op request.FileOption, level int) error {\n\top.Path = fileInfo.Path\n\tsubInfo, err := files.NewFileInfo(op.FileOption)\n\tif err != nil {\n\t\tif os.IsPermission(err) || errors.Is(err, unix.EACCES) {\n\t\t\tglobal.LOG.Infof(\"File Tree: Skipping %s due to permission denied\\n\", fileInfo.Path)\n\t\t\treturn nil\n\t\t}\n\t\tglobal.LOG.Errorf(\"File Tree: Skipping %s due to error: %s\\n\", fileInfo.Path, err.Error())\n\t\treturn nil\n\t}\n\n\treturn f.buildFileTree(childNode, subInfo.Items, op, level-1)\n}\n\nfunc (f *FileService) Create(op request.FileCreate) error {\n\tif files.IsInvalidChar(op.Path) {\n\t\treturn buserr.New(\"ErrInvalidChar\")\n\t}\n\tfo := files.NewFileOp()\n\tif fo.Stat(op.Path) {\n\t\treturn buserr.New(\"ErrFileIsExist\")\n\t}\n\tmode := op.Mode\n\tif mode == 0 {\n\t\tfileInfo, err := os.Stat(filepath.Dir(op.Path))\n\t\tif err == nil {\n\t\t\tmode = int64(fileInfo.Mode().Perm())\n\t\t} else {\n\t\t\tmode = constant.DirPerm\n\t\t}\n\t}\n\tif op.IsDir {\n\t\tif err := fo.CreateDirWithMode(op.Path, fs.FileMode(mode)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\thandleDefaultOwn(op.Path)\n\t\treturn nil\n\t}\n\tif op.IsLink {\n\t\tif !fo.Stat(op.LinkPath) {\n\t\t\treturn buserr.New(\"ErrLinkPathNotFound\")\n\t\t}\n\t\tif err := fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink); err != nil {\n\t\t\treturn err\n\t\t}\n\t\thandleDefaultOwn(op.Path)\n\t\treturn nil\n\t}\n\tif err := fo.CreateFileWithMode(op.Path, fs.FileMode(mode)); err != nil {\n\t\treturn err\n\t}\n\thandleDefaultOwn(op.Path)\n\treturn nil\n}\n\nfunc (f *FileService) Delete(op request.FileDelete) error {\n\tif op.IsDir {\n\t\texcludeDir := global.Dir.DataDir\n\t\tif path.Base(op.Path) == \".1panel_clash\" || op.Path == excludeDir {\n\t\t\treturn buserr.New(\"ErrPathNotDelete\")\n\t\t}\n\t}\n\tfo := files.NewFileOp()\n\trecycleBinStatus, _ := settingRepo.Get(settingRepo.WithByKey(\"FileRecycleBin\"))\n\tif recycleBinStatus.Value == \"Disable\" {\n\t\top.ForceDelete = true\n\t}\n\tif op.ForceDelete {\n\t\tif op.IsDir {\n\t\t\treturn fo.DeleteDir(op.Path)\n\t\t} else {\n\t\t\treturn fo.DeleteFile(op.Path)\n\t\t}\n\t}\n\tinfo, _ := fo.Fs.Stat(op.Path)\n\tif info == nil || files.IsSymlink(info.Mode()) {\n\t\treturn os.Remove(op.Path)\n\t}\n\n\tif err := NewIRecycleBinService().Create(request.RecycleBinCreate{SourcePath: op.Path}); err != nil {\n\t\treturn err\n\t}\n\treturn favoriteRepo.Delete(favoriteRepo.WithByPath(op.Path))\n}\n\nfunc (f *FileService) BatchDelete(op request.FileBatchDelete) error {\n\tfo := files.NewFileOp()\n\tif op.IsDir {\n\t\tfor _, file := range op.Paths {\n\t\t\tif err := fo.DeleteDir(file); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor _, file := range op.Paths {\n\t\t\tif err := fo.DeleteFile(file); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (f *FileService) ChangeMode(op request.FileCreate) error {\n\tfo := files.NewFileOp()\n\treturn fo.ChmodR(op.Path, op.Mode, op.Sub)\n}\n\nfunc (f *FileService) BatchChangeModeAndOwner(op request.FileRoleReq) error {\n\tfo := files.NewFileOp()\n\tfor _, p := range op.Paths {\n\t\tif !fo.Stat(p) {\n\t\t\treturn buserr.New(\"ErrPathNotFound\")\n\t\t}\n\t}\n\tif err := fo.ChownRPaths(op.Paths, op.User, op.Group, op.Sub); err != nil {\n\t\treturn err\n\t}\n\tif err := fo.ChmodRPaths(op.Paths, op.Mode, op.Sub); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f *FileService) ChangeOwner(req request.FileRoleUpdate) error {\n\tfo := files.NewFileOp()\n\treturn fo.ChownR(req.Path, req.User, req.Group, req.Sub)\n}\n\nfunc (f *FileService) Compress(c request.FileCompress) error {\n\tfo := files.NewFileOp()\n\tif !c.Replace && fo.Stat(filepath.Join(c.Dst, c.Name)) {\n\t\treturn buserr.New(\"ErrFileIsExist\")\n\t}\n\treturn fo.Compress(c.Files, c.Dst, c.Name, files.CompressType(c.Type), c.Secret)\n}\n\nfunc (f *FileService) DeCompress(c request.FileDeCompress) error {\n\tfo := files.NewFileOp()\n\tif c.Type == \"tar\" && len(c.Secret) != 0 {\n\t\tc.Type = \"tar.gz\"\n\t}\n\treturn fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type), c.Secret)\n}\n\nfunc (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo, error) {\n\tinfo, err := files.NewFileInfo(files.FileOption{\n\t\tPath:     op.Path,\n\t\tExpand:   true,\n\t\tIsDetail: op.IsDetail,\n\t})\n\tif err != nil {\n\t\treturn response.FileInfo{}, err\n\t}\n\n\tcontent := []byte(info.Content)\n\tif len(content) > 1024 {\n\t\tcontent = content[:1024]\n\t}\n\tif !utf8.Valid(content) {\n\t\t_, decodeName, _ := charset.DetermineEncoding(content, \"\")\n\t\tdecoder := files.GetDecoderByName(decodeName)\n\t\tif decoder != nil {\n\t\t\treader := strings.NewReader(info.Content)\n\t\t\tvar dec *encoding.Decoder\n\t\t\tif decodeName == \"windows-1252\" {\n\t\t\t\tdec = simplifiedchinese.GBK.NewDecoder()\n\t\t\t} else {\n\t\t\t\tdec = decoder.NewDecoder()\n\t\t\t}\n\t\t\tdecodedReader := transform.NewReader(reader, dec)\n\t\t\tcontents, err := io.ReadAll(decodedReader)\n\t\t\tif err != nil {\n\t\t\t\treturn response.FileInfo{}, err\n\t\t\t}\n\t\t\tinfo.Content = string(contents)\n\t\t}\n\t}\n\treturn response.FileInfo{FileInfo: *info}, nil\n}\n\nfunc (f *FileService) GetPreviewContent(op request.FileContentReq) (response.FileInfo, error) {\n\tinfo, err := files.NewFileInfo(files.FileOption{\n\t\tPath:     op.Path,\n\t\tExpand:   false,\n\t\tIsDetail: op.IsDetail,\n\t})\n\tif err != nil {\n\t\treturn response.FileInfo{}, err\n\t}\n\n\tif files.IsBlockDevice(info.FileMode) {\n\t\treturn response.FileInfo{FileInfo: *info}, nil\n\t}\n\n\tfile, err := os.Open(op.Path)\n\tif err != nil {\n\t\treturn response.FileInfo{}, err\n\t}\n\tdefer file.Close()\n\n\theadBuf := make([]byte, 1024)\n\tn, err := file.Read(headBuf)\n\tif err != nil && err != io.EOF {\n\t\treturn response.FileInfo{}, err\n\t}\n\theadBuf = headBuf[:n]\n\n\tif len(headBuf) > 0 && files.DetectBinary(headBuf) {\n\t\treturn response.FileInfo{FileInfo: *info}, nil\n\t}\n\n\tconst maxSize = 10 * 1024 * 1024\n\tif info.Size <= maxSize {\n\t\tif _, err := file.Seek(0, 0); err != nil {\n\t\t\treturn response.FileInfo{}, err\n\t\t}\n\t\tcontent, err := io.ReadAll(file)\n\t\tif err != nil {\n\t\t\treturn response.FileInfo{}, err\n\t\t}\n\t\tinfo.Content = string(content)\n\t} else {\n\t\tlines, err := files.TailFromEnd(op.Path, 300)\n\t\tif err != nil {\n\t\t\treturn response.FileInfo{}, err\n\t\t}\n\t\tinfo.Content = strings.Join(lines, \"\\n\")\n\t}\n\n\tcontent := []byte(info.Content)\n\tif len(content) > 1024 {\n\t\tcontent = content[:1024]\n\t}\n\tif !utf8.Valid(content) {\n\t\t_, decodeName, _ := charset.DetermineEncoding(content, \"\")\n\t\tdecoder := files.GetDecoderByName(decodeName)\n\t\tif decoder != nil {\n\t\t\treader := strings.NewReader(info.Content)\n\t\t\tvar dec *encoding.Decoder\n\t\t\tif decodeName == \"windows-1252\" {\n\t\t\t\tdec = simplifiedchinese.GBK.NewDecoder()\n\t\t\t} else {\n\t\t\t\tdec = decoder.NewDecoder()\n\t\t\t}\n\t\t\tdecodedReader := transform.NewReader(reader, dec)\n\t\t\tcontents, err := io.ReadAll(decodedReader)\n\t\t\tif err != nil {\n\t\t\t\treturn response.FileInfo{}, err\n\t\t\t}\n\t\t\tinfo.Content = string(contents)\n\t\t}\n\t}\n\n\treturn response.FileInfo{FileInfo: *info}, nil\n}\n\nfunc (f *FileService) SaveContent(edit request.FileEdit) error {\n\tinfo, err := files.NewFileInfo(files.FileOption{\n\t\tPath:   edit.Path,\n\t\tExpand: false,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfo := files.NewFileOp()\n\treturn fo.WriteFile(edit.Path, strings.NewReader(edit.Content), info.FileMode)\n}\n\nfunc (f *FileService) ChangeName(req request.FileRename) error {\n\tif files.IsInvalidChar(req.NewName) {\n\t\treturn buserr.New(\"ErrInvalidChar\")\n\t}\n\tfo := files.NewFileOp()\n\treturn fo.Rename(req.OldName, req.NewName)\n}\n\nfunc (f *FileService) Wget(w request.FileWget) (string, error) {\n\tfo := files.NewFileOp()\n\tkey := \"file-wget-\" + common.GetUuid()\n\treturn key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key, w.IgnoreCertificate)\n}\n\nfunc (f *FileService) MvFile(m request.FileMove) error {\n\tfo := files.NewFileOp()\n\tif !fo.Stat(m.NewPath) {\n\t\treturn buserr.New(\"ErrPathNotFound\")\n\t}\n\tfor _, oldPath := range m.OldPaths {\n\t\tif !fo.Stat(oldPath) {\n\t\t\treturn buserr.WithName(\"ErrFileNotFound\", oldPath)\n\t\t}\n\t\tif oldPath == m.NewPath || strings.Contains(m.NewPath, filepath.Clean(oldPath)+\"/\") {\n\t\t\treturn buserr.New(\"ErrMovePathFailed\")\n\t\t}\n\t}\n\tvar errs []error\n\tif m.Type == \"cut\" {\n\t\tif len(m.CoverPaths) > 0 {\n\t\t\tfor _, src := range m.CoverPaths {\n\t\t\t\tif err := fo.CopyAndReName(src, m.NewPath, \"\", true); err != nil {\n\t\t\t\t\terrs = append(errs, err)\n\t\t\t\t\tglobal.LOG.Errorf(\"cut copy file [%s] to [%s] failed, err: %s\", src, m.NewPath, err.Error())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn fo.Cut(m.OldPaths, m.NewPath, m.Name, m.Cover)\n\t}\n\tif m.Type == \"copy\" {\n\t\tfor _, src := range m.OldPaths {\n\t\t\tif err := fo.CopyAndReName(src, m.NewPath, m.Name, m.Cover); err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t\tglobal.LOG.Errorf(\"copy file [%s] to [%s] failed, err: %s\", src, m.NewPath, err.Error())\n\t\t\t}\n\t\t}\n\t\tif len(m.CoverPaths) > 0 {\n\t\t\tfor _, src := range m.CoverPaths {\n\t\t\t\tif err := fo.CopyAndReName(src, m.NewPath, \"\", true); err != nil {\n\t\t\t\t\terrs = append(errs, err)\n\t\t\t\t\tglobal.LOG.Errorf(\"copy file [%s] to [%s] failed, err: %s\", src, m.NewPath, err.Error())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvar errString string\n\tfor _, err := range errs {\n\t\terrString += err.Error() + \"\\n\"\n\t}\n\tif errString != \"\" {\n\t\treturn errors.New(errString)\n\t}\n\treturn nil\n}\n\nfunc (f *FileService) FileDownload(d request.FileDownload) (string, error) {\n\tfilePath := d.Paths[0]\n\tif d.Compress {\n\t\ttempPath := filepath.Join(os.TempDir(), fmt.Sprintf(\"%d\", time.Now().UnixNano()))\n\t\tif err := os.MkdirAll(tempPath, os.ModePerm); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tfo := files.NewFileOp()\n\t\tif err := fo.Compress(d.Paths, tempPath, d.Name, files.CompressType(d.Type), \"\"); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tfilePath = filepath.Join(tempPath, d.Name)\n\t}\n\treturn filePath, nil\n}\n\nfunc (f *FileService) DirSize(req request.DirSizeReq) (response.DirSizeRes, error) {\n\tvar (\n\t\tres response.DirSizeRes\n\t)\n\tif req.Path == \"/proc\" {\n\t\treturn res, nil\n\t}\n\tfo := files.NewFileOp()\n\tsize, err := fo.GetDirSize(req.Path)\n\tif err != nil {\n\t\treturn res, err\n\t}\n\tres.Size = size\n\treturn res, nil\n}\n\nfunc (f *FileService) DepthDirSize(req request.DirSizeReq) ([]response.DepthDirSizeRes, error) {\n\tvar (\n\t\tres []response.DepthDirSizeRes\n\t)\n\tif req.Path == \"/proc\" {\n\t\treturn res, nil\n\t}\n\tfo := files.NewFileOp()\n\tdirSizes, err := fo.GetDepthDirSize(req.Path)\n\t_ = copier.Copy(&res, &dirSizes)\n\tif err != nil {\n\t\treturn res, err\n\t}\n\treturn res, nil\n}\n\nfunc (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {\n\tlogFilePath := \"\"\n\ttaskStatus := \"\"\n\tif len(req.Name) != 0 {\n\t\tsafeName := path.Base(req.Name)\n\t\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\t\treturn nil, buserr.New(\"ErrInvalidParams\")\n\t\t}\n\t}\n\tswitch req.Type {\n\tcase constant.TypeWebsite:\n\t\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlogFilePath = GetSitePath(website, req.Name)\n\tcase constant.TypePhp:\n\t\tphp, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlogFilePath = php.GetLogPath()\n\tcase constant.TypeSSL:\n\t\tssl, err := websiteSSLRepo.GetFirst(repo.WithByID(req.ID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlogFilePath = ssl.GetLogPath()\n\tcase constant.TypeSystem:\n\t\tfileName := \"\"\n\t\tif len(req.Name) == 0 {\n\t\t\tfileName = \"1Panel.log\"\n\t\t} else {\n\t\t\tif strings.HasSuffix(req.Name, time.Now().Format(\"2006-01-02\")) {\n\t\t\t\tfileName = \"1Panel.log\"\n\t\t\t\tif strings.HasPrefix(req.Name, \"Core-\") {\n\t\t\t\t\tfileName = \"1Panel-Core.log\"\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfileName = \"1Panel-\" + req.Name + \".log\"\n\t\t\t}\n\t\t}\n\t\tlogFilePath = path.Join(global.Dir.DataDir, \"log\", fileName)\n\t\tif _, err := os.Stat(logFilePath); err != nil {\n\t\t\tfileGzPath := path.Join(global.Dir.DataDir, \"log\", fileName+\".gz\")\n\t\t\tif _, err := os.Stat(fileGzPath); err != nil {\n\t\t\t\treturn nil, buserr.New(\"ErrHttpReqNotFound\")\n\t\t\t}\n\t\t\tif err := handleGunzip(fileGzPath); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"handle ungzip file %s failed, err: %v\", fileGzPath, err)\n\t\t\t}\n\t\t}\n\tcase constant.TypeTask:\n\t\tvar opts []repo.DBOption\n\t\tif req.TaskID != \"\" {\n\t\t\topts = append(opts, taskRepo.WithByID(req.TaskID))\n\t\t} else {\n\t\t\topts = append(opts, repo.WithOrderRuleBy(\"created_at\", \"desc\"), repo.WithByType(req.TaskType), taskRepo.WithOperate(req.TaskOperate), taskRepo.WithResourceID(req.ResourceID))\n\t\t}\n\t\ttaskModel, err := taskRepo.GetFirst(opts...)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlogFilePath = taskModel.LogFile\n\t\ttaskStatus = taskModel.Status\n\tcase \"mysql-slow-logs\":\n\t\tlogFilePath = path.Join(global.Dir.DataDir, \"apps\", \"mysql\", req.Name, \"data\", \"1Panel-slow.log\")\n\tcase \"mariadb-slow-logs\":\n\t\tlogFilePath = path.Join(global.Dir.DataDir, \"apps\", \"mariadb\", req.Name, \"db\", \"data\", \"1Panel-slow.log\")\n\tcase \"php-fpm-slow-logs\":\n\t\tphp, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlogFilePath = php.GetSlowLogPath()\n\tcase constant.Supervisord:\n\t\tconfigPath := \"/etc/supervisord.conf\"\n\t\tpathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))\n\t\tif pathSet.ID != 0 || pathSet.Value != \"\" {\n\t\t\tconfigPath = pathSet.Value\n\t\t}\n\t\tlogFilePath, _ = ini_conf.GetIniValue(configPath, \"supervisord\", \"logfile\")\n\tcase constant.Supervisor:\n\t\tlogFilePath = path.Join(global.Dir.DataDir, \"tools\", \"supervisord\", \"log\", req.Name)\n\t}\n\n\tfile, err := os.Open(logFilePath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\tstat, err := file.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar (\n\t\tlines       []string\n\t\tisEndOfFile bool\n\t\tscope       string\n\t\tlogFileRes  *dto.LogFileRes\n\t)\n\tif stat.Size() > files.MaxReadFileSize {\n\t\tlines, _ = files.TailFromEnd(logFilePath, req.PageSize)\n\t\tisEndOfFile = true\n\t\tscope = \"tail\"\n\t} else {\n\t\tlogFileRes, err = files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tscope = \"page\"\n\t\tlines = logFileRes.Lines\n\t}\n\n\tres := &response.FileLineContent{\n\t\tEnd:        isEndOfFile,\n\t\tPath:       logFilePath,\n\t\tTaskStatus: taskStatus,\n\t\tLines:      lines,\n\t\tScope:      scope,\n\t}\n\tif logFileRes != nil {\n\t\tres.TotalLines = logFileRes.TotalLines\n\t\tres.Total = logFileRes.TotalPages\n\t\tres.End = logFileRes.IsEndOfFile\n\t}\n\treturn res, nil\n}\n\nfunc (f *FileService) GetPathByType(pathType string) string {\n\tif pathType == \"websiteDir\" {\n\t\tvalue, _ := settingRepo.GetValueByKey(\"WEBSITE_DIR\")\n\t\tif value == \"\" {\n\t\t\treturn path.Join(global.Dir.BaseDir, \"1panel\", \"www\")\n\t\t}\n\t\treturn value\n\t}\n\treturn \"\"\n}\n\nfunc (f *FileService) BatchCheckFiles(req request.FilePathsCheck) []response.ExistFileInfo {\n\tfileList := make([]response.ExistFileInfo, 0, len(req.Paths))\n\tfor _, filePath := range req.Paths {\n\t\tif info, err := os.Stat(filePath); err == nil {\n\t\t\tfileList = append(fileList, response.ExistFileInfo{\n\t\t\t\tSize:    info.Size(),\n\t\t\t\tName:    info.Name(),\n\t\t\t\tPath:    filePath,\n\t\t\t\tModTime: info.ModTime(),\n\t\t\t\tIsDir:   info.IsDir(),\n\t\t\t})\n\t\t}\n\t}\n\treturn fileList\n}\n\nfunc (f *FileService) GetHostMount() []dto.DiskInfo {\n\treturn loadDiskInfo()\n}\n\nfunc (f *FileService) GetUsersAndGroups() (*response.UserGroupResponse, error) {\n\tgroupMap, err := getValidGroups()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tusers, groupSet, err := getValidUsers(groupMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar groups []string\n\tfor group := range groupSet {\n\t\tgroups = append(groups, group)\n\t}\n\tsort.Strings(groups)\n\n\treturn &response.UserGroupResponse{\n\t\tUsers:  users,\n\t\tGroups: groups,\n\t}, nil\n}\n\nfunc (f *FileService) BatchGetRemarks(req request.FileRemarkBatch) map[string]string {\n\tremarks := make(map[string]string)\n\tfor _, filePath := range req.Paths {\n\t\tremark, err := getFileRemark(filePath)\n\t\tif err != nil {\n\t\t\tif isXattrNotSupported(err) {\n\t\t\t\treturn map[string]string{}\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif remark == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tremarks[filePath] = remark\n\t}\n\n\treturn remarks\n}\n\nfunc (f *FileService) SetRemark(req request.FileRemarkUpdate) error {\n\tif req.Remark == \"\" {\n\t\tif err := unix.Lremovexattr(req.Path, fileRemarkXattr); err != nil {\n\t\t\tif isXattrNotFound(err) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif isXattrNotSupported(err) {\n\t\t\t\treturn buserr.WithDetail(\"ErrInvalidParams\", \"xattr not supported\", err)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\n\tencoded := base64.StdEncoding.EncodeToString([]byte(req.Remark))\n\tif len(encoded) >= fileRemarkEncodedMaxLen {\n\t\treturn buserr.WithDetail(\"ErrInvalidParams\", \"remark length must be less than 256\", nil)\n\t}\n\tif err := unix.Lsetxattr(req.Path, fileRemarkXattr, []byte(encoded), 0); err != nil {\n\t\tif isXattrNotSupported(err) {\n\t\t\treturn buserr.WithDetail(\"ErrInvalidParams\", \"xattr not supported\", err)\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc getValidGroups() (map[string]bool, error) {\n\tgroupFile, err := os.Open(\"/etc/group\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open /etc/group: %w\", err)\n\t}\n\tdefer groupFile.Close()\n\n\tgroupMap := make(map[string]bool)\n\tscanner := bufio.NewScanner(groupFile)\n\tfor scanner.Scan() {\n\t\tparts := strings.Split(scanner.Text(), \":\")\n\t\tif len(parts) < 3 {\n\t\t\tcontinue\n\t\t}\n\t\tgroupName := parts[0]\n\t\tgid, _ := strconv.Atoi(parts[2])\n\t\tif groupName == \"root\" || gid >= 1000 {\n\t\t\tgroupMap[groupName] = true\n\t\t}\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to scan /etc/group: %w\", err)\n\t}\n\treturn groupMap, nil\n}\n\nfunc getFileRemark(filePath string) (string, error) {\n\tsize, err := unix.Lgetxattr(filePath, fileRemarkXattr, nil)\n\tif err != nil {\n\t\tif isXattrNotFound(err) {\n\t\t\treturn \"\", nil\n\t\t}\n\t\treturn \"\", err\n\t}\n\tif size == 0 {\n\t\treturn \"\", nil\n\t}\n\tbuf := make([]byte, size)\n\tn, err := unix.Lgetxattr(filePath, fileRemarkXattr, buf)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdecoded, err := base64.StdEncoding.DecodeString(string(buf[:n]))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(decoded), nil\n}\n\nfunc isXattrNotSupported(err error) bool {\n\treturn errors.Is(err, unix.ENOTSUP) || errors.Is(err, unix.EOPNOTSUPP)\n}\n\nfunc isXattrNotFound(err error) bool {\n\treturn errors.Is(err, unix.ENODATA)\n}\n\nfunc getValidUsers(validGroups map[string]bool) ([]response.UserInfo, map[string]struct{}, error) {\n\tpasswdFile, err := os.Open(\"/etc/passwd\")\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to open /etc/passwd: %w\", err)\n\t}\n\tdefer passwdFile.Close()\n\n\tvar users []response.UserInfo\n\tgroupSet := make(map[string]struct{})\n\tscanner := bufio.NewScanner(passwdFile)\n\tfor scanner.Scan() {\n\t\tparts := strings.Split(scanner.Text(), \":\")\n\t\tif len(parts) < 4 {\n\t\t\tcontinue\n\t\t}\n\n\t\tusername := parts[0]\n\t\tuid, _ := strconv.Atoi(parts[2])\n\t\tgid := parts[3]\n\n\t\tif username != \"root\" && uid < 1000 {\n\t\t\tcontinue\n\t\t}\n\n\t\tgroupName := gid\n\t\tif g, err := user.LookupGroupId(gid); err == nil {\n\t\t\tgroupName = g.Name\n\t\t}\n\n\t\tif !validGroups[groupName] {\n\t\t\tcontinue\n\t\t}\n\n\t\tusers = append(users, response.UserInfo{\n\t\t\tUsername: username,\n\t\t\tGroup:    groupName,\n\t\t})\n\t\tgroupSet[groupName] = struct{}{}\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to scan /etc/passwd: %w\", err)\n\t}\n\treturn users, groupSet, nil\n}\n\nfunc (f *FileService) Convert(req request.FileConvertRequest) {\n\tconvertTask, err := task.NewTaskWithOps(i18n.GetMsgByKey(\"FileConvert\"), task.TaskExec, task.TaskScopeFileConvert, req.TaskID, 1)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Create convert task failed %v\", err)\n\t\treturn\n\t}\n\tconvertTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey(\"FileConvert\"), task.TaskExec, task.TaskScopeFileConvert), func(t *task.Task) (err error) {\n\t\tfor _, file := range req.Files {\n\t\t\tinput := filepath.Join(file.Path, file.InputFile)\n\t\t\tnameOnly := file.InputFile[0 : len(file.InputFile)-len(file.Extension)]\n\t\t\toutput := filepath.Join(req.OutputPath, nameOnly+\".\"+file.OutputFormat)\n\t\t\tstatus, errMsg := convert.MediaFile(input, output, file.OutputFormat, req.DeleteSource)\n\t\t\tif status == \"FAILED\" {\n\t\t\t\tconvertTask.Log(fmt.Sprintf(\"%s -> %s [%s]: %s\\n\",\n\t\t\t\t\tinput, output, status, errMsg))\n\t\t\t} else {\n\t\t\t\tconvertTask.Log(fmt.Sprintf(\"%s -> %s [%s]: %s\\n\",\n\t\t\t\t\tinput, output, status, \"SUCCESS\"))\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}, nil)\n\tgo func() {\n\t\t_ = convertTask.Execute()\n\t}()\n}\n\nfunc (f *FileService) ConvertLog(req dto.PageInfo) (total int64, data []response.FileConvertLog, err error) {\n\tlogFilePath := filepath.Join(global.Dir.ConvertLogDir, \"convert.log\")\n\tfile, err := os.Open(logFilePath)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer file.Close()\n\n\tconst chunkSize = 64 * 1024\n\tstat, err := file.Stat()\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tfileSize := stat.Size()\n\n\tvar (\n\t\tbuf       []byte\n\t\tremainder []byte\n\t\toffset    = fileSize\n\t\tlines     []string\n\t)\n\n\tpageStart := int64((req.Page - 1) * req.PageSize)\n\tpageEnd := pageStart + int64(req.PageSize)\n\n\tfor offset > 0 {\n\t\treadSize := chunkSize\n\t\tif offset < int64(readSize) {\n\t\t\treadSize = int(offset)\n\t\t}\n\t\toffset -= int64(readSize)\n\n\t\ttmp := make([]byte, readSize)\n\t\tif _, err := file.ReadAt(tmp, offset); err != nil && err != io.EOF {\n\t\t\treturn 0, nil, err\n\t\t}\n\n\t\tbuf = append(tmp, remainder...)\n\t\tlinesSplit := strings.Split(string(buf), \"\\n\")\n\n\t\tif offset > 0 {\n\t\t\tremainder = []byte(linesSplit[0])\n\t\t\tlinesSplit = linesSplit[1:]\n\t\t}\n\n\t\tfor i := len(linesSplit) - 1; i >= 0; i-- {\n\t\t\tline := strings.TrimSpace(linesSplit[i])\n\t\t\tif line == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttotal++\n\t\t\tif total > pageStart && total <= pageEnd {\n\t\t\t\tlines = append(lines, line)\n\t\t\t}\n\t\t\tif total >= pageEnd {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif total >= pageEnd {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor _, line := range lines {\n\t\tvar entry response.FileConvertLog\n\t\tif err := json.Unmarshal([]byte(line), &entry); err == nil {\n\t\t\tdata = append(data, entry)\n\t\t}\n\t}\n\n\treturn total, data, nil\n}\n"
  },
  {
    "path": "agent/app/service/firewall.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall\"\n\tfireClient \"github.com/1Panel-dev/1Panel/agent/utils/firewall/client\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype FirewallService struct{}\n\ntype IFirewallService interface {\n\tLoadBaseInfo(tab string) (dto.FirewallBaseInfo, error)\n\tSearchWithPage(search dto.RuleSearch) (int64, interface{}, error)\n\tOperateFirewall(req dto.FirewallOperation) error\n\tOperatePortRule(req dto.PortRuleOperate, reload bool) error\n\tOperateForwardRule(req dto.ForwardRuleOperate) error\n\tOperateAddressRule(req dto.AddrRuleOperate, reload bool) error\n\tUpdatePortRule(req dto.PortRuleUpdate) error\n\tUpdateAddrRule(req dto.AddrRuleUpdate) error\n\tUpdateDescription(req dto.UpdateFirewallDescription) error\n\tBatchOperateRule(req dto.BatchRuleOperate) error\n}\n\nfunc NewIFirewallService() IFirewallService {\n\treturn &FirewallService{}\n}\n\nfunc (u *FirewallService) LoadBaseInfo(tab string) (dto.FirewallBaseInfo, error) {\n\tvar baseInfo dto.FirewallBaseInfo\n\tbaseInfo.Version = \"-\"\n\tbaseInfo.Name = \"-\"\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load firewall failed, err: %v\", err)\n\t\tbaseInfo.IsExist = false\n\t\treturn baseInfo, nil\n\t}\n\tbaseInfo.IsExist = true\n\tbaseInfo.Name = client.Name()\n\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tbaseInfo.PingStatus = firewall.LoadPingStatus()\n\t\tbaseInfo.Version, _ = client.Version()\n\t}()\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tbaseInfo.IsActive, _ = client.Status()\n\t\tbaseInfo.IsInit, baseInfo.IsBind = iptables.LoadInitStatus(baseInfo.Name, tab)\n\t}()\n\twg.Wait()\n\treturn baseInfo, nil\n}\n\nfunc (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}, error) {\n\tvar (\n\t\tdatas     []fireClient.FireInfo\n\t\tbackDatas []fireClient.FireInfo\n\t)\n\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tvar rules []fireClient.FireInfo\n\tswitch req.Type {\n\tcase \"port\":\n\t\trules, err = client.ListPort()\n\tcase \"forward\":\n\t\trules, err = client.ListForward()\n\tcase \"address\":\n\t\trules, err = client.ListAddress()\n\t}\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tif len(req.Info) != 0 {\n\t\tfor _, addr := range rules {\n\t\t\tif strings.Contains(addr.Address, req.Info) ||\n\t\t\t\tstrings.Contains(addr.Port, req.Info) ||\n\t\t\t\tstrings.Contains(addr.TargetPort, req.Info) ||\n\t\t\t\tstrings.Contains(addr.TargetIP, req.Info) {\n\t\t\t\tdatas = append(datas, addr)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tdatas = rules\n\t}\n\tif req.Type == \"port\" {\n\t\tapps := u.loadPortByApp()\n\t\tfor i := 0; i < len(datas); i++ {\n\t\t\tdatas[i].UsedStatus = checkPortUsed(datas[i].Port, datas[i].Protocol, apps)\n\t\t}\n\t}\n\n\tvar datasFilterStrategy []fireClient.FireInfo\n\tif len(req.Strategy) != 0 {\n\t\tfor _, data := range datas {\n\t\t\tif req.Strategy == data.Strategy {\n\t\t\t\tdatasFilterStrategy = append(datasFilterStrategy, data)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tdatasFilterStrategy = datas\n\t}\n\n\ttotal, start, end := len(datasFilterStrategy), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\tbackDatas = make([]fireClient.FireInfo, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\tbackDatas = datasFilterStrategy[start:end]\n\t}\n\n\tdatasFromDB, _ := hostRepo.ListFirewallRecord()\n\tfor i := 0; i < len(backDatas); i++ {\n\t\tfor _, des := range datasFromDB {\n\t\t\tif req.Type != des.Type {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif backDatas[i].Port == des.DstPort &&\n\t\t\t\treq.Type == \"port\" &&\n\t\t\t\tbackDatas[i].Protocol == des.Protocol &&\n\t\t\t\tbackDatas[i].Strategy == des.Strategy &&\n\t\t\t\tbackDatas[i].Address == des.SrcIP {\n\t\t\t\tbackDatas[i].ID = des.ID\n\t\t\t\tbackDatas[i].Description = des.Description\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif req.Type == \"address\" && backDatas[i].Strategy == des.Strategy && backDatas[i].Address == des.SrcIP {\n\t\t\t\tbackDatas[i].ID = des.ID\n\t\t\t\tbackDatas[i].Description = des.Description\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tgo u.cleanUnUsedData(client)\n\n\treturn int64(total), backDatas, nil\n}\n\nfunc (u *FirewallService) OperateFirewall(req dto.FirewallOperation) error {\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tneedRestartDocker := false\n\tswitch req.Operation {\n\tcase \"start\":\n\t\tif err := client.Start(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := u.addPortsBeforeStart(client); err != nil {\n\t\t\t_ = client.Stop()\n\t\t\treturn err\n\t\t}\n\t\tneedRestartDocker = true\n\tcase \"stop\":\n\t\tif err := client.Stop(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tneedRestartDocker = true\n\tcase \"restart\":\n\t\tif err := client.Restart(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tneedRestartDocker = true\n\tcase \"disableBanPing\":\n\t\tif err := firewall.UpdatePingStatus(\"0\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = settingRepo.Update(\"BanPing\", constant.StatusDisable)\n\t\treturn nil\n\tcase \"enableBanPing\":\n\t\tif err := firewall.UpdatePingStatus(\"1\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = settingRepo.Update(\"BanPing\", constant.StatusEnable)\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"not supported operation: %s\", req.Operation)\n\t}\n\tif needRestartDocker && req.WithDockerRestart {\n\t\tif err := controller.HandleRestart(\"docker\"); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to restart Docker: %v\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) error {\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(req.Chain) == 0 && client.Name() == \"iptables\" {\n\t\treq.Chain = iptables.Chain1PanelBasic\n\t}\n\tprotos := strings.Split(req.Protocol, \"/\")\n\titemAddress := strings.Split(strings.TrimSuffix(req.Address, \",\"), \",\")\n\n\tif client.Name() == \"ufw\" {\n\t\tif strings.Contains(req.Port, \",\") || strings.Contains(req.Port, \"-\") {\n\t\t\tfor _, proto := range protos {\n\t\t\t\tfor _, addr := range itemAddress {\n\t\t\t\t\tif len(addr) == 0 {\n\t\t\t\t\t\taddr = \"Anywhere\"\n\t\t\t\t\t}\n\t\t\t\t\treq.Address = addr\n\t\t\t\t\treq.Port = strings.ReplaceAll(req.Port, \"-\", \":\")\n\t\t\t\t\treq.Protocol = proto\n\t\t\t\t\tif err := u.operatePort(client, req); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\treq.Port = strings.ReplaceAll(req.Port, \":\", \"-\")\n\t\t\t\t\tif err := u.addPortRecord(req); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tfor _, addr := range itemAddress {\n\t\t\tif len(addr) == 0 {\n\t\t\t\taddr = \"Anywhere\"\n\t\t\t}\n\t\t\tif req.Protocol == \"tcp/udp\" {\n\t\t\t\treq.Protocol = \"\"\n\t\t\t}\n\t\t\treq.Address = addr\n\t\t\tif err := u.operatePort(client, req); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif len(req.Protocol) == 0 {\n\t\t\t\treq.Protocol = \"tcp/udp\"\n\t\t\t}\n\t\t\tif err := u.addPortRecord(req); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\titemPorts := req.Port\n\tfor _, proto := range protos {\n\t\tif strings.Contains(req.Port, \"-\") {\n\t\t\tfor _, addr := range itemAddress {\n\t\t\t\treq.Protocol = proto\n\t\t\t\treq.Address = addr\n\t\t\t\tif err := u.operatePort(client, req); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif err := u.addPortRecord(req); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tports := strings.Split(itemPorts, \",\")\n\t\t\tfor _, port := range ports {\n\t\t\t\tif len(port) == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, addr := range itemAddress {\n\t\t\t\t\treq.Address = addr\n\t\t\t\t\treq.Port = port\n\t\t\t\t\treq.Protocol = proto\n\t\t\t\t\tif err := u.operatePort(client, req); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tif err := u.addPortRecord(req); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif reload {\n\t\treturn client.Reload()\n\t}\n\treturn nil\n}\n\nfunc (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trules, _ := client.ListForward()\n\ti := 0\n\tfor _, rule := range rules {\n\t\tshouldKeep := true\n\t\tfor i := range req.Rules {\n\t\t\treqRule := &req.Rules[i]\n\t\t\tif reqRule.TargetIP == \"\" {\n\t\t\t\treqRule.TargetIP = \"127.0.0.1\"\n\t\t\t}\n\n\t\t\tif reqRule.Operation == \"remove\" {\n\t\t\t\tfor _, proto := range strings.Split(reqRule.Protocol, \"/\") {\n\t\t\t\t\tif reqRule.Port == rule.Port &&\n\t\t\t\t\t\treqRule.TargetPort == rule.TargetPort &&\n\t\t\t\t\t\treqRule.TargetIP == rule.TargetIP &&\n\t\t\t\t\t\tproto == rule.Protocol &&\n\t\t\t\t\t\treqRule.Interface == rule.Interface {\n\t\t\t\t\t\tshouldKeep = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif shouldKeep {\n\t\t\trules[i] = rule\n\t\t\ti++\n\t\t}\n\t}\n\trules = rules[:i]\n\n\tfor _, rule := range rules {\n\t\tfor _, reqRule := range req.Rules {\n\t\t\tif reqRule.Operation == \"remove\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor _, proto := range strings.Split(reqRule.Protocol, \"/\") {\n\t\t\t\tif reqRule.Port == rule.Port &&\n\t\t\t\t\treqRule.TargetPort == rule.TargetPort &&\n\t\t\t\t\treqRule.TargetIP == rule.TargetIP &&\n\t\t\t\t\tproto == rule.Protocol &&\n\t\t\t\t\treqRule.Interface == rule.Interface {\n\t\t\t\t\treturn buserr.New(\"ErrRecordExist\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tsort.SliceStable(req.Rules, func(i, j int) bool {\n\t\tif req.Rules[i].Operation == \"remove\" && req.Rules[j].Operation != \"remove\" {\n\t\t\treturn true\n\t\t}\n\t\tif req.Rules[i].Operation != \"remove\" && req.Rules[j].Operation == \"remove\" {\n\t\t\treturn false\n\t\t}\n\t\tn1, _ := strconv.Atoi(req.Rules[i].Num)\n\t\tn2, _ := strconv.Atoi(req.Rules[j].Num)\n\t\treturn n1 > n2\n\t})\n\n\tfor _, r := range req.Rules {\n\t\tfor _, p := range strings.Split(r.Protocol, \"/\") {\n\t\t\tif r.TargetIP == \"\" {\n\t\t\t\tr.TargetIP = \"127.0.0.1\"\n\t\t\t}\n\t\t\tif err = client.PortForward(fireClient.Forward{\n\t\t\t\tNum:        r.Num,\n\t\t\t\tProtocol:   p,\n\t\t\t\tPort:       r.Port,\n\t\t\t\tTargetIP:   r.TargetIP,\n\t\t\t\tTargetPort: r.TargetPort,\n\t\t\t\tInterface:  r.Interface,\n\t\t\t}, r.Operation); err != nil {\n\t\t\t\tif req.ForceDelete {\n\t\t\t\t\tglobal.LOG.Error(err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload bool) error {\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tchain := \"\"\n\tif client.Name() == \"iptables\" {\n\t\tchain = iptables.Chain1PanelBasic\n\t}\n\tvar fireInfo fireClient.FireInfo\n\tif err := copier.Copy(&fireInfo, &req); err != nil {\n\t\treturn err\n\t}\n\n\taddressList := strings.Split(req.Address, \",\")\n\tfor i := 0; i < len(addressList); i++ {\n\t\tif len(addressList[i]) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfireInfo.Address = addressList[i]\n\t\tif err := client.RichRules(fireInfo, req.Operation); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treq.Address = addressList[i]\n\t\tif err := u.addAddressRecord(chain, req); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif reload {\n\t\treturn client.Reload()\n\t}\n\treturn nil\n}\n\nfunc (u *FirewallService) UpdatePortRule(req dto.PortRuleUpdate) error {\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := u.OperatePortRule(req.OldRule, false); err != nil {\n\t\treturn err\n\t}\n\tif err := u.OperatePortRule(req.NewRule, false); err != nil {\n\t\treturn err\n\t}\n\treturn client.Reload()\n}\n\nfunc (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := u.OperateAddressRule(req.OldRule, false); err != nil {\n\t\treturn err\n\t}\n\tif err := u.OperateAddressRule(req.NewRule, false); err != nil {\n\t\treturn err\n\t}\n\treturn client.Reload()\n}\n\nfunc (u *FirewallService) UpdateDescription(req dto.UpdateFirewallDescription) error {\n\tfirewall := model.Firewall{\n\t\tType:        req.Type,\n\t\tChain:       req.Chain,\n\t\tSrcIP:       req.SrcIP,\n\t\tDstIP:       req.DstIP,\n\t\tSrcPort:     req.SrcPort,\n\t\tDstPort:     req.DstPort,\n\t\tProtocol:    req.Protocol,\n\t\tStrategy:    req.Strategy,\n\t\tDescription: req.Description,\n\t}\n\n\treturn hostRepo.SaveFirewallRecord(&firewall)\n}\n\nfunc (u *FirewallService) BatchOperateRule(req dto.BatchRuleOperate) error {\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif req.Type == \"port\" {\n\t\tfor _, rule := range req.Rules {\n\t\t\t_ = u.OperatePortRule(rule, false)\n\t\t}\n\t\treturn client.Reload()\n\t}\n\tfor _, rule := range req.Rules {\n\t\titemRule := dto.AddrRuleOperate{Operation: rule.Operation, Address: rule.Address, Strategy: rule.Strategy}\n\t\t_ = u.OperateAddressRule(itemRule, false)\n\t}\n\treturn client.Reload()\n}\n\nfunc OperateFirewallPort(oldPorts, newPorts []int) error {\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, port := range newPorts {\n\t\tif err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: \"tcp\", Strategy: \"accept\"}, \"add\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, port := range oldPorts {\n\t\tif err := client.Port(fireClient.FireInfo{Port: strconv.Itoa(port), Protocol: \"tcp\", Strategy: \"accept\"}, \"remove\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn client.Reload()\n}\n\nfunc (u *FirewallService) operatePort(client firewall.FirewallClient, req dto.PortRuleOperate) error {\n\tvar fireInfo fireClient.FireInfo\n\tif err := copier.Copy(&fireInfo, &req); err != nil {\n\t\treturn err\n\t}\n\n\tif client.Name() == \"ufw\" {\n\t\tif len(fireInfo.Address) != 0 && !strings.EqualFold(fireInfo.Address, \"Anywhere\") {\n\t\t\treturn client.RichRules(fireInfo, req.Operation)\n\t\t}\n\t\treturn client.Port(fireInfo, req.Operation)\n\t}\n\n\tif len(fireInfo.Address) != 0 || fireInfo.Strategy == \"drop\" {\n\t\treturn client.RichRules(fireInfo, req.Operation)\n\t}\n\treturn client.Port(fireInfo, req.Operation)\n}\n\ntype portOfApp struct {\n\tAppName   string\n\tHttpPort  string\n\tHttpsPort string\n}\n\nfunc (u *FirewallService) loadPortByApp() []portOfApp {\n\tvar datas []portOfApp\n\tapps, err := appInstallRepo.ListBy(context.Background())\n\tif err != nil {\n\t\treturn datas\n\t}\n\tfor i := 0; i < len(apps); i++ {\n\t\tdatas = append(datas, portOfApp{\n\t\t\tAppName:   apps[i].App.Key,\n\t\t\tHttpPort:  strconv.Itoa(apps[i].HttpPort),\n\t\t\tHttpsPort: strconv.Itoa(apps[i].HttpsPort),\n\t\t})\n\t}\n\tsystemPort, err := settingRepo.Get(settingRepo.WithByKey(\"ServerPort\"))\n\tif err != nil {\n\t\treturn datas\n\t}\n\tdatas = append(datas, portOfApp{AppName: \"1panel\", HttpPort: systemPort.Value})\n\n\treturn datas\n}\n\nfunc (u *FirewallService) cleanUnUsedData(client firewall.FirewallClient) {\n\tlist, _ := client.ListPort()\n\taddressList, _ := client.ListAddress()\n\tlist = append(list, addressList...)\n\tif len(list) == 0 {\n\t\treturn\n\t}\n\trecords, _ := hostRepo.ListFirewallRecord()\n\tif len(records) == 0 {\n\t\treturn\n\t}\n\tfor _, item := range list {\n\t\tfor i := 0; i < len(records); i++ {\n\t\t\tif records[i].DstPort == item.Port && records[i].Protocol == item.Protocol && records[i].Strategy == item.Strategy && records[i].SrcIP == item.Address {\n\t\t\t\trecords = append(records[:i], records[i+1:]...)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, record := range records {\n\t\t_ = hostRepo.DeleteFirewallRecordByID(record.ID)\n\t}\n}\n\nfunc (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) error {\n\tif !global.IsMaster {\n\t\tif err := client.Port(fireClient.FireInfo{Port: global.CONF.Base.Port, Protocol: \"tcp\", Strategy: \"accept\"}, \"add\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tvar portSetting model.Setting\n\t\t_ = global.CoreDB.Where(\"key = ?\", \"ServerPort\").First(&portSetting).Error\n\t\tif len(portSetting.Value) != 0 {\n\t\t\tif err := client.Port(fireClient.FireInfo{Port: portSetting.Value, Protocol: \"tcp\", Strategy: \"accept\"}, \"add\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tif err := client.Port(fireClient.FireInfo{Port: loadSSHPort(), Protocol: \"tcp\", Strategy: \"accept\"}, \"add\"); err != nil {\n\t\treturn err\n\t}\n\tif err := client.Port(fireClient.FireInfo{Port: \"80\", Protocol: \"tcp\", Strategy: \"accept\"}, \"add\"); err != nil {\n\t\treturn err\n\t}\n\tif err := client.Port(fireClient.FireInfo{Port: \"443\", Protocol: \"tcp\", Strategy: \"accept\"}, \"add\"); err != nil {\n\t\treturn err\n\t}\n\tif err := client.Port(fireClient.FireInfo{Port: \"443\", Protocol: \"udp\", Strategy: \"accept\"}, \"add\"); err != nil {\n\t\treturn err\n\t}\n\n\treturn client.Reload()\n}\n\nfunc (u *FirewallService) addPortRecord(req dto.PortRuleOperate) error {\n\tif req.Operation == \"remove\" {\n\t\tif req.ID != 0 {\n\t\t\treturn hostRepo.DeleteFirewallRecordByID(req.ID)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif len(req.Description) == 0 {\n\t\treturn nil\n\t}\n\tif err := hostRepo.SaveFirewallRecord(&model.Firewall{\n\t\tType:        \"port\",\n\t\tChain:       req.Chain,\n\t\tDstPort:     req.Port,\n\t\tProtocol:    req.Protocol,\n\t\tSrcIP:       req.Address,\n\t\tStrategy:    req.Strategy,\n\t\tDescription: req.Description,\n\t}); err != nil {\n\t\treturn fmt.Errorf(\"add record %s/%s failed (strategy: %s, address: %s), err: %v\", req.Port, req.Protocol, req.Strategy, req.Address, err)\n\t}\n\n\treturn nil\n}\n\nfunc (u *FirewallService) addAddressRecord(chain string, req dto.AddrRuleOperate) error {\n\tif req.Operation == \"remove\" {\n\t\tif req.ID != 0 {\n\t\t\treturn hostRepo.DeleteFirewallRecordByID(req.ID)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif err := hostRepo.SaveFirewallRecord(&model.Firewall{\n\t\tType:        \"address\",\n\t\tChain:       chain,\n\t\tSrcIP:       req.Address,\n\t\tStrategy:    req.Strategy,\n\t\tDescription: req.Description,\n\t}); err != nil {\n\t\treturn fmt.Errorf(\"add record failed (strategy: %s, address: %s), err: %v\", req.Strategy, req.Address, err)\n\t}\n\treturn nil\n}\n\nfunc checkPortUsed(ports, proto string, apps []portOfApp) string {\n\tvar portList []int\n\trangeSplit := \"\"\n\tif strings.Contains(ports, \"-\") {\n\t\trangeSplit = \"-\"\n\t}\n\tif strings.Contains(ports, \":\") {\n\t\trangeSplit = \":\"\n\t}\n\tif len(rangeSplit) != 0 {\n\t\tport1, err := strconv.Atoi(strings.Split(ports, rangeSplit)[0])\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\" convert string %s to int failed, err: %v\", strings.Split(ports, rangeSplit)[0], err)\n\t\t\treturn \"\"\n\t\t}\n\t\tport2, err := strconv.Atoi(strings.Split(ports, rangeSplit)[1])\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\" convert string %s to int failed, err: %v\", strings.Split(ports, rangeSplit)[1], err)\n\t\t\treturn \"\"\n\t\t}\n\t\tfor i := port1; i <= port2; i++ {\n\t\t\tportList = append(portList, i)\n\t\t}\n\t}\n\tif strings.Contains(ports, \",\") {\n\t\tportLists := strings.Split(ports, \",\")\n\t\tfor _, item := range portLists {\n\t\t\tportItem, _ := strconv.Atoi(item)\n\t\t\tportList = append(portList, portItem)\n\t\t}\n\t}\n\tif len(portList) != 0 {\n\t\tvar usedPorts []string\n\t\tfor _, port := range portList {\n\t\t\tportItem := fmt.Sprintf(\"%v\", port)\n\t\t\tisUsedByApp := false\n\t\t\tfor _, app := range apps {\n\t\t\t\tif app.HttpPort == portItem || app.HttpsPort == portItem {\n\t\t\t\t\tisUsedByApp = true\n\t\t\t\t\tusedPorts = append(usedPorts, fmt.Sprintf(\"%s (%s)\", portItem, app.AppName))\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !isUsedByApp && common.ScanPortWithProto(port, proto) {\n\t\t\t\tusedPorts = append(usedPorts, fmt.Sprintf(\"%v\", port))\n\t\t\t}\n\t\t}\n\t\treturn strings.Join(usedPorts, \",\")\n\t}\n\n\tfor _, app := range apps {\n\t\tif app.HttpPort == ports || app.HttpsPort == ports {\n\t\t\treturn app.AppName\n\t\t}\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "agent/app/service/ftp.go",
    "content": "package service\n\nimport (\n\t\"os\"\n\t\"sort\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/toolbox\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype FtpService struct{}\n\ntype IFtpService interface {\n\tLoadBaseInfo() (dto.FtpBaseInfo, error)\n\tSearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)\n\tOperate(operation string) error\n\tCreate(req dto.FtpCreate) (uint, error)\n\tDelete(req dto.BatchDeleteReq) error\n\tUpdate(req dto.FtpUpdate) error\n\tSync() error\n\tLoadLog(req dto.FtpLogSearch) (int64, interface{}, error)\n}\n\nfunc NewIFtpService() IFtpService {\n\treturn &FtpService{}\n}\n\nfunc (f *FtpService) LoadBaseInfo() (dto.FtpBaseInfo, error) {\n\tvar baseInfo dto.FtpBaseInfo\n\tclient, err := toolbox.NewFtpClient()\n\tif err != nil {\n\t\treturn baseInfo, err\n\t}\n\tbaseInfo.IsActive, baseInfo.IsExist = client.Status()\n\treturn baseInfo, nil\n}\n\nfunc (f *FtpService) LoadLog(req dto.FtpLogSearch) (int64, interface{}, error) {\n\tclient, err := toolbox.NewFtpClient()\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tlogItem, err := client.LoadLogs(req.User, req.Operation)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tsort.Slice(logItem, func(i, j int) bool {\n\t\treturn logItem[i].Time > logItem[j].Time\n\t})\n\tvar logs []toolbox.FtpLog\n\ttotal, start, end := len(logItem), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\tlogs = make([]toolbox.FtpLog, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\tlogs = logItem[start:end]\n\t}\n\treturn int64(total), logs, nil\n}\n\nfunc (u *FtpService) Operate(operation string) error {\n\tclient, err := toolbox.NewFtpClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn client.Operate(operation)\n}\n\nfunc (f *FtpService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {\n\ttotal, lists, err := ftpRepo.Page(req.Page, req.PageSize, ftpRepo.WithLikeUser(req.Info), repo.WithOrderDesc(\"created_at\"))\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar users []dto.FtpInfo\n\tfor _, user := range lists {\n\t\tvar item dto.FtpInfo\n\t\tif err := copier.Copy(&item, &user); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\titem.Password, _ = encrypt.StringDecrypt(item.Password)\n\t\tusers = append(users, item)\n\t}\n\treturn total, users, err\n}\n\nfunc (f *FtpService) Sync() error {\n\tclient, err := toolbox.NewFtpClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tlists, err := client.LoadList()\n\tif err != nil {\n\t\treturn nil\n\t}\n\tlistsInDB, err := ftpRepo.GetList()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcurrentData := make(map[string]model.Ftp)\n\tfor _, item := range listsInDB {\n\t\tcurrentData[item.User] = item\n\t}\n\tsameData := make(map[string]struct{})\n\tfor _, item := range lists {\n\t\tif itemInDB, ok := currentData[item.User]; ok {\n\t\t\tsameData[item.User] = struct{}{}\n\t\t\tif item.Path != itemInDB.Path || item.Status != itemInDB.Status {\n\t\t\t\tif err := ftpRepo.Update(itemInDB.ID, map[string]interface{}{\"path\": item.Path, \"status\": item.Status}); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif err := ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tfor _, item := range listsInDB {\n\t\tif _, ok := sameData[item.User]; !ok {\n\t\t\t_ = ftpRepo.Update(item.ID, map[string]interface{}{\"status\": constant.StatusDeleted})\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (f *FtpService) Create(req dto.FtpCreate) (uint, error) {\n\tif _, err := os.Stat(req.Path); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\tif err := os.MkdirAll(req.Path, os.ModePerm); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t} else {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\tpass, err := encrypt.StringEncrypt(req.Password)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tuserInDB, _ := ftpRepo.Get(ftpRepo.WithByUser(req.User))\n\tif userInDB.ID != 0 {\n\t\treturn 0, buserr.New(\"ErrRecordExist\")\n\t}\n\tclient, err := toolbox.NewFtpClient()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif err := client.UserAdd(req.User, req.Password, req.Path); err != nil {\n\t\treturn 0, err\n\t}\n\tvar ftp model.Ftp\n\tif err := copier.Copy(&ftp, &req); err != nil {\n\t\treturn 0, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tftp.Status = constant.StatusEnable\n\tftp.Password = pass\n\tif err := ftpRepo.Create(&ftp); err != nil {\n\t\treturn 0, err\n\t}\n\treturn ftp.ID, nil\n}\n\nfunc (f *FtpService) Delete(req dto.BatchDeleteReq) error {\n\tclient, err := toolbox.NewFtpClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, id := range req.Ids {\n\t\tftpItem, err := ftpRepo.Get(repo.WithByID(id))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = client.UserDel(ftpItem.User)\n\t\t_ = ftpRepo.Delete(repo.WithByID(id))\n\t}\n\treturn nil\n}\n\nfunc (f *FtpService) Update(req dto.FtpUpdate) error {\n\tif _, err := os.Stat(req.Path); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\tif err := os.MkdirAll(req.Path, os.ModePerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tpass, err := encrypt.StringEncrypt(req.Password)\n\tif err != nil {\n\t\treturn err\n\t}\n\tftpItem, _ := ftpRepo.Get(repo.WithByID(req.ID))\n\tif ftpItem.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tpassItem, err := encrypt.StringDecrypt(ftpItem.Password)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient, err := toolbox.NewFtpClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tneedReload := false\n\tupdates := make(map[string]interface{})\n\tif req.Password != passItem {\n\t\tif err := client.SetPasswd(ftpItem.User, req.Password); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tupdates[\"password\"] = pass\n\t\tneedReload = true\n\t}\n\tif req.Status != ftpItem.Status {\n\t\tif err := client.SetStatus(ftpItem.User, req.Status); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tupdates[\"status\"] = req.Status\n\t\tneedReload = true\n\t}\n\tif req.Path != ftpItem.Path {\n\t\tif err := client.SetPath(ftpItem.User, req.Path); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tupdates[\"path\"] = req.Path\n\t\tneedReload = true\n\t}\n\tif req.Description != ftpItem.Description {\n\t\tupdates[\"description\"] = req.Description\n\t}\n\tif needReload {\n\t\t_ = client.Reload()\n\t}\n\tif len(updates) != 0 {\n\t\treturn ftpRepo.Update(ftpItem.ID, updates)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/group.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype GroupService struct{}\n\ntype IGroupService interface {\n\tList(req dto.OperateByType) ([]dto.GroupInfo, error)\n\tCreate(req dto.GroupCreate) error\n\tUpdate(req dto.GroupUpdate) error\n\tDelete(id uint) error\n\tGetDefault() (model.Group, error)\n}\n\nfunc NewIGroupService() IGroupService {\n\treturn &GroupService{}\n}\n\nfunc (u *GroupService) List(req dto.OperateByType) ([]dto.GroupInfo, error) {\n\toptions := []repo.DBOption{\n\t\trepo.WithOrderDesc(\"is_default\"),\n\t\trepo.WithOrderDesc(\"created_at\"),\n\t}\n\tif len(req.Type) != 0 {\n\t\toptions = append(options, repo.WithByType(req.Type))\n\t}\n\tvar (\n\t\tgroups []model.Group\n\t\terr    error\n\t)\n\tgroups, err = groupRepo.GetList(options...)\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tvar dtoUsers []dto.GroupInfo\n\tfor _, group := range groups {\n\t\tvar item dto.GroupInfo\n\t\tif err := copier.Copy(&item, &group); err != nil {\n\t\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdtoUsers = append(dtoUsers, item)\n\t}\n\treturn dtoUsers, err\n}\n\nfunc (u *GroupService) Create(req dto.GroupCreate) error {\n\tgroup, _ := groupRepo.Get(repo.WithByName(req.Name), repo.WithByType(req.Type))\n\tif group.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tif err := copier.Copy(&group, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif err := groupRepo.Create(&group); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *GroupService) Delete(id uint) error {\n\tgroup, _ := groupRepo.Get(repo.WithByID(id))\n\tif group.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif group.IsDefault {\n\t\treturn buserr.New(\"ErrGroupIsDefault\")\n\t}\n\tif group.Type == \"website\" {\n\t\twebsites, _ := websiteRepo.List(websiteRepo.WithGroupID(group.ID))\n\t\tif len(websites) > 0 {\n\t\t\treturn buserr.New(\"ErrGroupIsInWebsiteUse\")\n\t\t}\n\t}\n\treturn groupRepo.Delete(repo.WithByID(id))\n}\n\nfunc (u *GroupService) Update(req dto.GroupUpdate) error {\n\tif req.IsDefault {\n\t\tif err := groupRepo.CancelDefault(req.Type); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tupMap := make(map[string]interface{})\n\tupMap[\"name\"] = req.Name\n\tupMap[\"is_default\"] = req.IsDefault\n\treturn groupRepo.Update(req.ID, upMap)\n}\n\nfunc (u *GroupService) GetDefault() (model.Group, error) {\n\treturn groupRepo.Get(groupRepo.WithByDefault(true))\n}\n"
  },
  {
    "path": "agent/app/service/helper.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\nfunc getTxAndContext() (tx *gorm.DB, ctx context.Context) {\n\ttx = global.DB.Begin()\n\tctx = context.WithValue(context.Background(), constant.DB, tx)\n\treturn\n}\n"
  },
  {
    "path": "agent/app/service/host.go",
    "content": "package service\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ssh\"\n\t\"github.com/jinzhu/copier\"\n\t\"github.com/pkg/errors\"\n)\n\ntype HostService struct{}\n\ntype IHostService interface {\n\tTestLocalConn(id uint) bool\n\tTestByInfo(req dto.HostConnTest) bool\n\tGetHostByID(id uint) (*dto.HostInfo, error)\n\tSearchForTree(search dto.SearchForTree) ([]dto.HostTree, error)\n\tSearchWithPage(search dto.SearchPageWithGroup) (int64, interface{}, error)\n\tCreate(req dto.HostOperate) (*dto.HostInfo, error)\n\tUpdate(id uint, upMap map[string]interface{}) (*dto.HostInfo, error)\n\tDelete(id []uint) error\n\n\tEncryptHost(itemVal string) (string, error)\n}\n\nfunc NewIHostService() IHostService {\n\treturn &HostService{}\n}\n\nfunc (u *HostService) TestByInfo(req dto.HostConnTest) bool {\n\tif req.AuthMode == \"password\" && len(req.Password) != 0 {\n\t\tpassword, err := base64.StdEncoding.DecodeString(req.Password)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treq.Password = string(password)\n\t}\n\tif req.AuthMode == \"key\" && len(req.PrivateKey) != 0 {\n\t\tprivateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treq.PrivateKey = string(privateKey)\n\t}\n\tif len(req.Password) == 0 && len(req.PrivateKey) == 0 {\n\t\thost, err := hostRepo.Get(repo.WithByAddr(req.Addr), hostRepo.WithByPort(req.Port))\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treq.Password = host.Password\n\t\treq.AuthMode = host.AuthMode\n\t\treq.PrivateKey = host.PrivateKey\n\t\treq.PassPhrase = host.PassPhrase\n\t}\n\n\tvar connInfo ssh.ConnInfo\n\t_ = copier.Copy(&connInfo, &req)\n\tconnInfo.PrivateKey = []byte(req.PrivateKey)\n\tif len(req.PassPhrase) != 0 {\n\t\tconnInfo.PassPhrase = []byte(req.PassPhrase)\n\t}\n\tclient, err := ssh.NewClient(connInfo)\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer client.Close()\n\treturn true\n}\n\nfunc (u *HostService) TestLocalConn(id uint) bool {\n\thost, err := hostRepo.Get(repo.WithByID(id))\n\tif err != nil {\n\t\treturn false\n\t}\n\tvar connInfo ssh.ConnInfo\n\tif err := copier.Copy(&connInfo, &host); err != nil {\n\t\treturn false\n\t}\n\tif len(host.Password) != 0 {\n\t\thost.Password, err = encrypt.StringDecrypt(host.Password)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tconnInfo.Password = host.Password\n\t}\n\tif len(host.PrivateKey) != 0 {\n\t\thost.PrivateKey, err = encrypt.StringDecrypt(host.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tconnInfo.PrivateKey = []byte(host.PrivateKey)\n\t}\n\tif len(host.PassPhrase) != 0 {\n\t\thost.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\tconnInfo.PassPhrase = []byte(host.PassPhrase)\n\t}\n\tclient, err := ssh.NewClient(connInfo)\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer client.Close()\n\treturn true\n}\n\nfunc (u *HostService) SearchWithPage(req dto.SearchPageWithGroup) (int64, interface{}, error) {\n\tvar options []repo.DBOption\n\tif len(req.Info) != 0 {\n\t\toptions = append(options, hostRepo.WithByInfo(req.Info))\n\t}\n\tif req.GroupID != 0 {\n\t\toptions = append(options, repo.WithByGroupID(req.GroupID))\n\t}\n\ttotal, hosts, err := hostRepo.Page(req.Page, req.PageSize, options...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar dtoHosts []dto.HostInfo\n\tfor _, host := range hosts {\n\t\tvar item dto.HostInfo\n\t\tif err := copier.Copy(&item, &host); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tgroup, _ := groupRepo.Get(repo.WithByID(host.GroupID))\n\t\titem.GroupBelong = group.Name\n\t\tif !item.RememberPassword {\n\t\t\titem.Password = \"\"\n\t\t\titem.PrivateKey = \"\"\n\t\t\titem.PassPhrase = \"\"\n\t\t} else {\n\t\t\tif len(host.Password) != 0 {\n\t\t\t\titem.Password, err = encrypt.StringDecrypt(host.Password)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn 0, nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(host.PrivateKey) != 0 {\n\t\t\t\titem.PrivateKey, err = encrypt.StringDecrypt(host.PrivateKey)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn 0, nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(host.PassPhrase) != 0 {\n\t\t\t\titem.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn 0, nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdtoHosts = append(dtoHosts, item)\n\t}\n\treturn total, dtoHosts, err\n}\n\nfunc (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, error) {\n\thosts, err := hostRepo.GetList(hostRepo.WithByInfo(search.Info))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgroups, err := groupRepo.GetList(repo.WithByType(\"host\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []dto.HostTree\n\tfor _, group := range groups {\n\t\tvar data dto.HostTree\n\t\tdata.ID = group.ID + 10000\n\t\tdata.Label = group.Name\n\t\tfor _, host := range hosts {\n\t\t\tlabel := fmt.Sprintf(\"%s@%s:%d\", host.User, host.Addr, host.Port)\n\t\t\tif len(host.Name) != 0 {\n\t\t\t\tlabel = fmt.Sprintf(\"%s - %s@%s:%d\", host.Name, host.User, host.Addr, host.Port)\n\t\t\t}\n\t\t\tif host.GroupID == group.ID {\n\t\t\t\tdata.Children = append(data.Children, dto.TreeChild{ID: host.ID, Label: label})\n\t\t\t}\n\t\t}\n\t\tif len(data.Children) != 0 {\n\t\t\tdatas = append(datas, data)\n\t\t}\n\t}\n\treturn datas, err\n}\n\nfunc (u *HostService) GetHostByID(id uint) (*dto.HostInfo, error) {\n\tvar item dto.HostInfo\n\thost, _ := hostRepo.Get(repo.WithByID(id))\n\tif host.ID == 0 {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif err := copier.Copy(&item, &host); err != nil {\n\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif !item.RememberPassword {\n\t\titem.Password = \"\"\n\t\titem.PrivateKey = \"\"\n\t\titem.PassPhrase = \"\"\n\t\treturn &item, nil\n\t}\n\tvar err error\n\tif len(host.Password) != 0 {\n\t\titem.Password, err = encrypt.StringDecrypt(host.Password)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif len(host.PrivateKey) != 0 {\n\t\titem.PrivateKey, err = encrypt.StringDecrypt(host.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif len(host.PassPhrase) != 0 {\n\t\titem.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &item, err\n}\n\nfunc (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {\n\thostItem, _ := hostRepo.Get(repo.WithByAddr(req.Addr), hostRepo.WithByUser(req.User), hostRepo.WithByPort(req.Port))\n\tif hostItem.ID != 0 {\n\t\treturn nil, buserr.New(\"ErrRecordExist\")\n\t}\n\n\tvar err error\n\tif len(req.Password) != 0 && req.AuthMode == \"password\" {\n\t\treq.Password, err = u.EncryptHost(req.Password)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treq.PrivateKey = \"\"\n\t\treq.PassPhrase = \"\"\n\t}\n\tif len(req.PrivateKey) != 0 && req.AuthMode == \"key\" {\n\t\treq.PrivateKey, err = u.EncryptHost(req.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(req.PassPhrase) != 0 {\n\t\t\treq.PassPhrase, err = encrypt.StringEncrypt(req.PassPhrase)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\treq.Password = \"\"\n\t}\n\tvar host model.Host\n\tif err := copier.Copy(&host, &req); err != nil {\n\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif req.GroupID == 0 {\n\t\tgroup, err := groupRepo.Get(repo.WithByType(\"host\"), groupRepo.WithByDefault(true))\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"get default group failed\")\n\t\t}\n\t\thost.GroupID = group.ID\n\t\treq.GroupID = group.ID\n\t}\n\n\tif err := hostRepo.Create(&host); err != nil {\n\t\treturn nil, err\n\t}\n\tvar hostinfo dto.HostInfo\n\tif err := copier.Copy(&hostinfo, &host); err != nil {\n\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\treturn &hostinfo, nil\n}\n\nfunc (u *HostService) Delete(ids []uint) error {\n\tfor _, id := range ids {\n\t\thost, _ := hostRepo.Get(repo.WithByID(id))\n\t\tif host.ID == 0 {\n\t\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t\t}\n\t\tif err := hostRepo.Delete(repo.WithByID(id)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *HostService) Update(id uint, upMap map[string]interface{}) (*dto.HostInfo, error) {\n\tif err := hostRepo.Update(id, upMap); err != nil {\n\t\treturn nil, err\n\t}\n\thostItem, _ := hostRepo.Get(repo.WithByID(id))\n\tvar hostinfo dto.HostInfo\n\tif err := copier.Copy(&hostinfo, &hostItem); err != nil {\n\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\treturn &hostinfo, nil\n}\n\nfunc (u *HostService) EncryptHost(itemVal string) (string, error) {\n\tprivateKey, err := base64.StdEncoding.DecodeString(itemVal)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tkeyItem, err := encrypt.StringEncrypt(string(privateKey))\n\treturn keyItem, err\n}\n\nfunc GetHostInfo(id uint) (*model.Host, error) {\n\thost, err := hostRepo.Get(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif len(host.Password) != 0 {\n\t\thost.Password, err = encrypt.StringDecrypt(host.Password)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif len(host.PrivateKey) != 0 {\n\t\thost.PrivateKey, err = encrypt.StringDecrypt(host.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif len(host.PassPhrase) != 0 {\n\t\thost.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &host, err\n}\n"
  },
  {
    "path": "agent/app/service/host_tool.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ini_conf\"\n\t\"github.com/pkg/errors\"\n\t\"gopkg.in/ini.v1\"\n)\n\ntype HostToolService struct{}\n\ntype IHostToolService interface {\n\tGetToolStatus(req request.HostToolReq) (*response.HostToolRes, error)\n\tCreateToolConfig(req request.HostToolCreate) error\n\tOperateTool(req request.HostToolReq) error\n\tOperateToolConfig(req request.HostToolConfig) (*response.HostToolConfig, error)\n\tOperateSupervisorProcess(req request.SupervisorProcessConfig) error\n\tGetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error)\n\tOperateSupervisorProcessFile(req request.SupervisorProcessFileReq) (string, error)\n}\n\nfunc NewIHostToolService() IHostToolService {\n\treturn &HostToolService{}\n}\n\nfunc (h *HostToolService) GetToolStatus(req request.HostToolReq) (*response.HostToolRes, error) {\n\tres := &response.HostToolRes{}\n\tres.Type = req.Type\n\tswitch req.Type {\n\tcase constant.Supervisord:\n\t\tsupervisorConfig := &response.Supervisor{}\n\t\tif !cmd.Which(constant.Supervisord) {\n\t\t\tsupervisorConfig.IsExist = false\n\t\t\tres.Config = supervisorConfig\n\t\t\treturn res, nil\n\t\t}\n\t\tsupervisorConfig.IsExist = true\n\t\tserviceExist, _ := controller.CheckExist(constant.Supervisord)\n\t\tif !serviceExist {\n\t\t\tserviceExist, _ = controller.CheckExist(constant.Supervisor)\n\t\t\tif !serviceExist {\n\t\t\t\tsupervisorConfig.IsExist = false\n\t\t\t\tres.Config = supervisorConfig\n\t\t\t\treturn res, nil\n\t\t\t} else {\n\t\t\t\tsupervisorConfig.ServiceName = constant.Supervisor\n\t\t\t}\n\t\t} else {\n\t\t\tsupervisorConfig.ServiceName = constant.Supervisord\n\t\t}\n\n\t\tserviceNameSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorServiceName))\n\t\tif serviceNameSet.ID != 0 || serviceNameSet.Value != \"\" {\n\t\t\tsupervisorConfig.ServiceName = serviceNameSet.Value\n\t\t}\n\n\t\tversionRes, _ := cmd.RunDefaultWithStdoutBashC(\"supervisord -v\")\n\t\tsupervisorConfig.Version = strings.TrimSuffix(versionRes, \"\\n\")\n\t\t_, ctlRrr := exec.LookPath(\"supervisorctl\")\n\t\tsupervisorConfig.CtlExist = ctlRrr == nil\n\n\t\tactive, _ := controller.CheckActive(supervisorConfig.ServiceName)\n\t\tif active {\n\t\t\tsupervisorConfig.Status = \"running\"\n\t\t} else {\n\t\t\tsupervisorConfig.Status = \"stopped\"\n\t\t}\n\n\t\tpathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))\n\t\tif pathSet.ID != 0 || pathSet.Value != \"\" {\n\t\t\tsupervisorConfig.ConfigPath = pathSet.Value\n\t\t\tres.Config = supervisorConfig\n\t\t\treturn res, nil\n\t\t} else {\n\t\t\tsupervisorConfig.Init = true\n\t\t}\n\n\t\tservicePath := \"/usr/lib/systemd/system/supervisor.service\"\n\t\tfileOp := files.NewFileOp()\n\t\tif !fileOp.Stat(servicePath) {\n\t\t\tservicePath = \"/usr/lib/systemd/system/supervisord.service\"\n\t\t}\n\t\tif fileOp.Stat(servicePath) {\n\t\t\tstartCmd, _ := ini_conf.GetIniValue(servicePath, \"Service\", \"ExecStart\")\n\t\t\tif startCmd != \"\" {\n\t\t\t\targs := strings.Fields(startCmd)\n\t\t\t\tcIndex := -1\n\t\t\t\tfor i, arg := range args {\n\t\t\t\t\tif arg == \"-c\" {\n\t\t\t\t\t\tcIndex = i\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif cIndex != -1 && cIndex+1 < len(args) {\n\t\t\t\t\tsupervisorConfig.ConfigPath = args[cIndex+1]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif supervisorConfig.ConfigPath == \"\" {\n\t\t\tconfigPath := \"/etc/supervisord.conf\"\n\t\t\tif !fileOp.Stat(configPath) {\n\t\t\t\tconfigPath = \"/etc/supervisor/supervisord.conf\"\n\t\t\t\tif fileOp.Stat(configPath) {\n\t\t\t\t\tsupervisorConfig.ConfigPath = configPath\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tres.Config = supervisorConfig\n\t}\n\treturn res, nil\n}\n\nfunc (h *HostToolService) CreateToolConfig(req request.HostToolCreate) error {\n\tswitch req.Type {\n\tcase constant.Supervisord:\n\t\tfileOp := files.NewFileOp()\n\t\tif !fileOp.Stat(req.ConfigPath) {\n\t\t\treturn buserr.New(\"ErrConfigNotFound\")\n\t\t}\n\t\tcfg, err := ini.Load(req.ConfigPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tservice, err := cfg.GetSection(\"include\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttargetKey, err := service.GetKey(\"files\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif targetKey != nil {\n\t\t\t_, err = service.NewKey(\";files\", targetKey.Value())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tsupervisorDir := path.Join(global.Dir.DataDir, \"tools\", \"supervisord\")\n\t\tincludeDir := path.Join(supervisorDir, \"supervisor.d\")\n\t\tif !fileOp.Stat(includeDir) {\n\t\t\tif err = fileOp.CreateDir(includeDir, constant.DirPerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tlogDir := path.Join(supervisorDir, \"log\")\n\t\tif !fileOp.Stat(logDir) {\n\t\t\tif err = fileOp.CreateDir(logDir, constant.DirPerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tincludePath := path.Join(includeDir, \"*.ini\")\n\t\ttargetKey.SetValue(includePath)\n\t\tif err = cfg.SaveTo(req.ConfigPath); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tserviceNameSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorServiceName))\n\t\tif serviceNameSet.ID != 0 {\n\t\t\tif err = settingRepo.Update(constant.SupervisorServiceName, req.ServiceName); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif err = settingRepo.Create(constant.SupervisorServiceName, req.ServiceName); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tconfigPathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))\n\t\tif configPathSet.ID != 0 {\n\t\t\tif err = settingRepo.Update(constant.SupervisorConfigPath, req.ConfigPath); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif err = settingRepo.Create(constant.SupervisorConfigPath, req.ConfigPath); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err = controller.HandleRestart(req.ServiceName); err != nil {\n\t\t\tglobal.LOG.Errorf(\"[init] restart %s failed err %s\", req.ServiceName, err.Error())\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (h *HostToolService) OperateTool(req request.HostToolReq) error {\n\tserviceName := req.Type\n\tif req.Type == constant.Supervisord {\n\t\tserviceNameSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorServiceName))\n\t\tif serviceNameSet.ID != 0 || serviceNameSet.Value != \"\" {\n\t\t\tserviceName = serviceNameSet.Value\n\t\t}\n\t}\n\treturn controller.Handle(req.Operate, serviceName)\n}\n\nfunc (h *HostToolService) OperateToolConfig(req request.HostToolConfig) (*response.HostToolConfig, error) {\n\tfileOp := files.NewFileOp()\n\tres := &response.HostToolConfig{}\n\tconfigPath := \"\"\n\tserviceName := \"supervisord\"\n\tswitch req.Type {\n\tcase constant.Supervisord:\n\t\tpathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))\n\t\tif pathSet.ID != 0 || pathSet.Value != \"\" {\n\t\t\tconfigPath = pathSet.Value\n\t\t}\n\t\tserviceNameSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorServiceName))\n\t\tif serviceNameSet.ID != 0 || serviceNameSet.Value != \"\" {\n\t\t\tserviceName = serviceNameSet.Value\n\t\t}\n\t}\n\tswitch req.Operate {\n\tcase \"get\":\n\t\tcontent, err := fileOp.GetContent(configPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres.Content = string(content)\n\tcase \"set\":\n\t\tfile, err := fileOp.OpenFile(configPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\toldContent, err := fileOp.GetContent(configPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfileInfo, err := file.Stat()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err = fileOp.WriteFile(configPath, strings.NewReader(req.Content), fileInfo.Mode()); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err = controller.HandleRestart(serviceName); err != nil {\n\t\t\t_ = fileOp.WriteFile(configPath, bytes.NewReader(oldContent), fileInfo.Mode())\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn res, nil\n}\n\nfunc (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcessConfig) error {\n\tvar (\n\t\tsupervisordDir = path.Join(global.Dir.DataDir, \"tools\", \"supervisord\")\n\t\tfileOp         = files.NewFileOp()\n\t)\n\tif req.Operate == \"update\" || req.Operate == \"create\" {\n\t\tif !fileOp.Stat(req.Dir) {\n\t\t\treturn buserr.New(\"ErrConfigDirNotFound\")\n\t\t}\n\t\t_, err := user.Lookup(req.User)\n\t\tif err != nil {\n\t\t\treturn buserr.WithMap(\"ErrUserFindErr\", map[string]interface{}{\"name\": req.User, \"err\": err.Error()}, err)\n\t\t}\n\t}\n\treturn handleProcess(supervisordDir, req, \"\")\n}\n\nfunc handleProcess(supervisordDir string, req request.SupervisorProcessConfig, containerName string) error {\n\tsafeName := path.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn buserr.New(\"ErrInvalidParams\")\n\t}\n\tvar (\n\t\tfileOp     = files.NewFileOp()\n\t\tlogDir     = path.Join(supervisordDir, \"log\")\n\t\tincludeDir = path.Join(supervisordDir, \"supervisor.d\")\n\t\toutLog     = path.Join(logDir, fmt.Sprintf(\"%s.out.log\", req.Name))\n\t\terrLog     = path.Join(logDir, fmt.Sprintf(\"%s.err.log\", req.Name))\n\t\tiniPath    = path.Join(includeDir, fmt.Sprintf(\"%s.ini\", req.Name))\n\t)\n\tif containerName != \"\" {\n\t\toutLog = path.Join(\"/var/log/supervisor\", fmt.Sprintf(\"%s.out.log\", req.Name))\n\t\terrLog = path.Join(\"/var/log/supervisor\", fmt.Sprintf(\"%s.err.log\", req.Name))\n\t}\n\tswitch req.Operate {\n\tcase \"create\":\n\t\tif fileOp.Stat(iniPath) {\n\t\t\treturn buserr.New(\"ErrConfigAlreadyExist\")\n\t\t}\n\t\tconfigFile := ini.Empty()\n\t\tsection, err := configFile.NewSection(fmt.Sprintf(\"program:%s\", req.Name))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, _ = section.NewKey(\"command\", strings.TrimSpace(req.Command))\n\t\t_, _ = section.NewKey(\"directory\", req.Dir)\n\t\t_, _ = section.NewKey(\"autorestart\", req.AutoRestart)\n\t\t_, _ = section.NewKey(\"autostart\", req.AutoStart)\n\t\t_, _ = section.NewKey(\"startsecs\", \"3\")\n\t\t_, _ = section.NewKey(\"stdout_logfile\", outLog)\n\t\t_, _ = section.NewKey(\"stderr_logfile\", errLog)\n\t\t_, _ = section.NewKey(\"stdout_logfile_maxbytes\", \"2MB\")\n\t\t_, _ = section.NewKey(\"stderr_logfile_maxbytes\", \"2MB\")\n\t\t_, _ = section.NewKey(\"user\", req.User)\n\t\t_, _ = section.NewKey(\"priority\", \"999\")\n\t\t_, _ = section.NewKey(\"numprocs\", req.Numprocs)\n\t\t_, _ = section.NewKey(\"process_name\", \"%(program_name)s_%(process_num)02d\")\n\t\tif req.Environment != \"\" {\n\t\t\t_, _ = section.NewKey(\"environment\", req.Environment)\n\t\t}\n\n\t\tif err = configFile.SaveTo(iniPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := operateSupervisorCtl(\"reread\", \"\", \"\", includeDir, containerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn operateSupervisorCtl(\"update\", \"\", \"\", includeDir, containerName)\n\tcase \"update\":\n\t\tconfigFile, err := ini.Load(iniPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsection, err := configFile.GetSection(fmt.Sprintf(\"program:%s\", req.Name))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcommandKey := section.Key(\"command\")\n\t\tcommandKey.SetValue(strings.TrimSpace(req.Command))\n\t\tdirectoryKey := section.Key(\"directory\")\n\t\tdirectoryKey.SetValue(req.Dir)\n\t\tuserKey := section.Key(\"user\")\n\t\tuserKey.SetValue(req.User)\n\t\tnumprocsKey := section.Key(\"numprocs\")\n\t\tnumprocsKey.SetValue(req.Numprocs)\n\t\tautoRestart := section.Key(\"autorestart\")\n\t\tautoRestart.SetValue(req.AutoRestart)\n\t\tautoStart := section.Key(\"autostart\")\n\t\tautoStart.SetValue(req.AutoStart)\n\t\tenvironment := section.Key(\"environment\")\n\t\tenvironment.SetValue(req.Environment)\n\n\t\tif err = configFile.SaveTo(iniPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := operateSupervisorCtl(\"reread\", \"\", \"\", includeDir, containerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn operateSupervisorCtl(\"update\", \"\", \"\", includeDir, containerName)\n\tcase \"restart\":\n\t\treturn operateSupervisorCtl(\"restart\", req.Name, \"\", includeDir, containerName)\n\tcase \"start\":\n\t\treturn operateSupervisorCtl(\"start\", req.Name, \"\", includeDir, containerName)\n\tcase \"stop\":\n\t\treturn operateSupervisorCtl(\"stop\", req.Name, \"\", includeDir, containerName)\n\tcase \"delete\":\n\t\t_ = operateSupervisorCtl(\"remove\", \"\", req.Name, includeDir, containerName)\n\t\t_ = fileOp.DeleteFile(iniPath)\n\t\t_ = fileOp.DeleteFile(outLog)\n\t\t_ = fileOp.DeleteFile(errLog)\n\t\tif err := operateSupervisorCtl(\"reread\", \"\", \"\", includeDir, containerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn operateSupervisorCtl(\"update\", \"\", \"\", includeDir, containerName)\n\t}\n\treturn nil\n}\n\nfunc handleProcessConfig(configDir, containerName string) ([]response.SupervisorProcessConfig, error) {\n\tvar (\n\t\tresult []response.SupervisorProcessConfig\n\t)\n\tentries, err := os.ReadDir(configDir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tfileName := entry.Name()\n\t\tf, err := ini.Load(path.Join(configDir, fileName))\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"get %s file err %s\", fileName, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasSuffix(fileName, \".ini\") {\n\t\t\tconfig := response.SupervisorProcessConfig{}\n\t\t\tname := strings.TrimSuffix(fileName, \".ini\")\n\t\t\tconfig.Name = name\n\t\t\tsection, err := f.GetSection(fmt.Sprintf(\"program:%s\", name))\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"get %s file section err %s\", fileName, err.Error())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif command, _ := section.GetKey(\"command\"); command != nil {\n\t\t\t\tconfig.Command = command.Value()\n\t\t\t}\n\t\t\tif directory, _ := section.GetKey(\"directory\"); directory != nil {\n\t\t\t\tconfig.Dir = directory.Value()\n\t\t\t}\n\t\t\tif user, _ := section.GetKey(\"user\"); user != nil {\n\t\t\t\tconfig.User = user.Value()\n\t\t\t}\n\t\t\tif numprocs, _ := section.GetKey(\"numprocs\"); numprocs != nil {\n\t\t\t\tconfig.Numprocs = numprocs.Value()\n\t\t\t}\n\t\t\tif autoRestart, _ := section.GetKey(\"autorestart\"); autoRestart != nil {\n\t\t\t\tconfig.AutoRestart = autoRestart.Value()\n\t\t\t}\n\t\t\tif autoStart, _ := section.GetKey(\"autostart\"); autoStart != nil {\n\t\t\t\tconfig.AutoStart = autoStart.Value()\n\t\t\t}\n\t\t\tif environment, _ := section.GetKey(\"environment\"); environment != nil {\n\t\t\t\tconfig.Environment = environment.Value()\n\t\t\t}\n\t\t\t_ = getProcessStatus(&config, containerName)\n\t\t\tresult = append(result, config)\n\t\t}\n\t}\n\treturn result, nil\n}\n\nfunc (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error) {\n\tconfigDir := path.Join(global.Dir.DataDir, \"tools\", \"supervisord\", \"supervisor.d\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(configDir) {\n\t\t_ = fileOp.CreateDir(configDir, constant.DirPerm)\n\t\treturn []response.SupervisorProcessConfig{}, nil\n\t}\n\treturn handleProcessConfig(configDir, \"\")\n}\n\nfunc (h *HostToolService) OperateSupervisorProcessFile(req request.SupervisorProcessFileReq) (string, error) {\n\tvar (\n\t\tincludeDir = path.Join(global.Dir.DataDir, \"tools\", \"supervisord\", \"supervisor.d\")\n\t)\n\treturn handleSupervisorFile(req, includeDir, \"\", \"\")\n}\n\nfunc handleSupervisorFile(req request.SupervisorProcessFileReq, includeDir, containerName, logFile string) (string, error) {\n\tvar (\n\t\tfileOp     = files.NewFileOp()\n\t\tgroup      = fmt.Sprintf(\"program:%s\", req.Name)\n\t\tconfigPath = path.Join(includeDir, fmt.Sprintf(\"%s.ini\", req.Name))\n\t\terr        error\n\t)\n\tswitch req.File {\n\tcase \"err.log\":\n\t\tif logFile == \"\" {\n\t\t\tlogFile, err = ini_conf.GetIniValue(configPath, group, \"stderr_logfile\")\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\t\tswitch req.Operate {\n\t\tcase \"get\":\n\t\t\tcontent, err := fileOp.GetContent(logFile)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn string(content), nil\n\t\tcase \"clear\":\n\t\t\tif err = fileOp.WriteFile(logFile, strings.NewReader(\"\"), constant.DirPerm); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\n\tcase \"out.log\":\n\t\tif logFile == \"\" {\n\t\t\tlogFile, err = ini_conf.GetIniValue(configPath, group, \"stdout_logfile\")\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\t\tswitch req.Operate {\n\t\tcase \"get\":\n\t\t\tcontent, err := fileOp.GetContent(logFile)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn string(content), nil\n\t\tcase \"clear\":\n\t\t\tif err = fileOp.WriteFile(logFile, strings.NewReader(\"\"), constant.DirPerm); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\n\tcase \"config\":\n\t\tswitch req.Operate {\n\t\tcase \"get\":\n\t\t\tcontent, err := fileOp.GetContent(configPath)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn string(content), nil\n\t\tcase \"update\":\n\t\t\tif req.Content == \"\" {\n\t\t\t\treturn \"\", buserr.New(\"ErrConfigIsNull\")\n\t\t\t}\n\t\t\tif err := fileOp.WriteFile(configPath, strings.NewReader(req.Content), constant.DirPerm); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"\", operateSupervisorCtl(\"update\", \"\", req.Name, includeDir, containerName)\n\t\t}\n\n\t}\n\treturn \"\", nil\n}\n\nfunc operateSupervisorCtl(operate, name, group, includeDir, containerName string) error {\n\tprocessNames := []string{operate}\n\tif name != \"\" {\n\t\tf, err := ini.Load(path.Join(includeDir, fmt.Sprintf(\"%s.ini\", name)))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsection, err := f.GetSection(fmt.Sprintf(\"program:%s\", name))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tnumprocsNum := \"\"\n\t\tif numprocs, _ := section.GetKey(\"numprocs\"); numprocs != nil {\n\t\t\tnumprocsNum = numprocs.Value()\n\t\t}\n\t\tif numprocsNum == \"\" {\n\t\t\treturn buserr.New(\"ErrConfigParse\")\n\t\t}\n\t\tprocessNames = append(processNames, getProcessName(name, numprocsNum)...)\n\t}\n\tif group != \"\" {\n\t\tprocessNames = append(processNames, group)\n\t}\n\n\tvar (\n\t\toutput string\n\t\terr    error\n\t)\n\tif containerName != \"\" {\n\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30 * time.Second))\n\t\toutput, err = cmdMgr.RunWithStdoutBashCf(\"docker exec  %s supervisorctl %s\", containerName, strings.Join(processNames, \" \"))\n\t} else {\n\t\tvar out []byte\n\t\tout, err = exec.Command(\"supervisorctl\", processNames...).Output()\n\t\toutput = string(out)\n\t}\n\tif err != nil && output != \"\" {\n\t\treturn errors.New(output)\n\t}\n\treturn err\n}\n\nfunc getProcessName(name, numprocs string) []string {\n\tvar (\n\t\tprocessNames []string\n\t)\n\tnum, err := strconv.Atoi(numprocs)\n\tif err != nil {\n\t\treturn processNames\n\t}\n\tif num == 1 {\n\t\tprocessNames = append(processNames, fmt.Sprintf(\"%s:%s_00\", name, name))\n\t} else {\n\t\tfor i := 0; i < num; i++ {\n\t\t\tprocessName := fmt.Sprintf(\"%s:%s_0%s\", name, name, strconv.Itoa(i))\n\t\t\tif i >= 10 {\n\t\t\t\tprocessName = fmt.Sprintf(\"%s:%s_%s\", name, name, strconv.Itoa(i))\n\t\t\t}\n\t\t\tprocessNames = append(processNames, processName)\n\t\t}\n\t}\n\treturn processNames\n}\n\nfunc getProcessStatus(config *response.SupervisorProcessConfig, containerName string) error {\n\tvar (\n\t\tprocessNames = []string{\"status\"}\n\t\toutput       string\n\t\terr          error\n\t)\n\tprocessNames = append(processNames, getProcessName(config.Name, config.Numprocs)...)\n\tif containerName != \"\" {\n\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(3 * time.Second))\n\t\toutput, err = cmdMgr.RunWithStdoutBashCf(\"docker exec %s supervisorctl %s\", containerName, strings.Join(processNames, \" \"))\n\t} else {\n\t\tvar out []byte\n\t\tout, err = exec.Command(\"supervisorctl\", processNames...).Output()\n\t\toutput = string(out)\n\t}\n\tif output == \"\" && err != nil {\n\t\treturn err\n\t}\n\tlines := strings.Split(output, \"\\n\")\n\tfor _, line := range lines {\n\t\tline = strings.TrimPrefix(line, \"stdout:\")\n\t\tfields := strings.Fields(line)\n\t\tif len(fields) >= 4 {\n\t\t\tstatus := response.ProcessStatus{\n\t\t\t\tName:   fields[0],\n\t\t\t\tStatus: fields[1],\n\t\t\t}\n\t\t\tif fields[1] == \"RUNNING\" {\n\t\t\t\tstatus.PID = strings.TrimSuffix(fields[3], \",\")\n\t\t\t\tstatus.Uptime = fields[5]\n\t\t\t} else {\n\t\t\t\tstatus.Msg = strings.Join(fields[2:], \" \")\n\t\t\t}\n\t\t\tconfig.Status = append(config.Status, status)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/image.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/docker/docker/api/types/build\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/api/types/image\"\n\t\"github.com/docker/docker/api/types/registry\"\n\t\"github.com/docker/docker/pkg/archive\"\n\t\"github.com/docker/docker/pkg/homedir\"\n)\n\ntype ImageService struct{}\n\ntype IImageService interface {\n\tPage(req dto.PageImage) (int64, interface{}, error)\n\tList() ([]dto.Options, error)\n\tListAll() ([]dto.ImageInfo, error)\n\tImageBuild(req dto.ImageBuild) error\n\tImagePull(req dto.ImagePull) error\n\tImageLoad(req dto.ImageLoad) error\n\tImageSave(req dto.ImageSave) error\n\tImagePush(req dto.ImagePush) error\n\tImageRemove(req dto.BatchDelete) error\n\tImageTag(req dto.ImageTag) error\n}\n\nfunc NewIImageService() IImageService {\n\treturn &ImageService{}\n}\nfunc (u *ImageService) Page(req dto.PageImage) (int64, interface{}, error) {\n\tvar (\n\t\tlist      []image.Summary\n\t\trecords   []dto.ImageInfo\n\t\tbackDatas []dto.ImageInfo\n\t)\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer client.Close()\n\tlist, err = client.ImageList(context.Background(), image.ListOptions{})\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tcontainers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})\n\tif len(req.Name) != 0 {\n\t\tlength, count := len(list), 0\n\t\tfor count < length {\n\t\t\thasTag := false\n\t\t\tfor _, tag := range list[count].RepoTags {\n\t\t\t\tif strings.Contains(tag, req.Name) {\n\t\t\t\t\thasTag = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !hasTag {\n\t\t\t\tlist = append(list[:count], list[(count+1):]...)\n\t\t\t\tlength--\n\t\t\t} else {\n\t\t\t\tcount++\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, image := range list {\n\t\trecords = append(records, dto.ImageInfo{\n\t\t\tID:        image.ID,\n\t\t\tTags:      image.RepoTags,\n\t\t\tIsUsed:    checkUsed(image.ID, containers),\n\t\t\tCreatedAt: time.Unix(image.Created, 0),\n\t\t\tSize:      image.Size,\n\t\t})\n\t}\n\tswitch req.OrderBy {\n\tcase \"size\":\n\t\tsort.Slice(records, func(i, j int) bool {\n\t\t\tif req.Order == constant.OrderAsc {\n\t\t\t\treturn records[i].Size < records[j].Size\n\t\t\t}\n\t\t\treturn records[i].Size > records[j].Size\n\t\t})\n\tcase \"isUsed\":\n\t\tsort.Slice(records, func(i, j int) bool {\n\t\t\tif req.Order == constant.OrderAsc {\n\t\t\t\treturn !records[i].IsUsed\n\t\t\t}\n\t\t\treturn records[i].IsUsed\n\t\t})\n\tcase \"tags\":\n\t\tsort.Slice(records, func(i, j int) bool {\n\t\t\tif len(records[i].Tags) == 0 || len(records[j].Tags) == 0 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif req.Order == constant.OrderAsc {\n\t\t\t\treturn records[i].Tags[0] < records[j].Tags[0]\n\t\t\t}\n\t\t\treturn records[i].Tags[0] > records[j].Tags[0]\n\t\t})\n\tdefault:\n\t\tsort.Slice(records, func(i, j int) bool {\n\t\t\tif req.Order == constant.OrderAsc {\n\t\t\t\treturn records[i].CreatedAt.Before(records[j].CreatedAt)\n\t\t\t}\n\t\t\treturn records[i].CreatedAt.After(records[j].CreatedAt)\n\t\t})\n\t}\n\n\timageDescriptions, _ := settingRepo.GetDescriptionList(repo.WithByType(\"image\"))\n\tfor i := 0; i < len(list); i++ {\n\t\tfor _, desc := range imageDescriptions {\n\t\t\tif \"sha256:\"+desc.ID == records[i].ID {\n\t\t\t\trecords[i].Description = desc.Description\n\t\t\t\trecords[i].IsPinned = desc.IsPinned\n\t\t\t}\n\t\t}\n\t}\n\tsort.Slice(records, func(i, j int) bool {\n\t\treturn records[i].IsPinned && !records[j].IsPinned\n\t})\n\ttotal, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\tbackDatas = make([]dto.ImageInfo, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\tbackDatas = records[start:end]\n\t}\n\n\treturn int64(total), backDatas, nil\n}\n\nfunc (u *ImageService) ListAll() ([]dto.ImageInfo, error) {\n\tvar records []dto.ImageInfo\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\tlist, err := client.ImageList(context.Background(), image.ListOptions{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcontainers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})\n\tfor _, image := range list {\n\t\trecords = append(records, dto.ImageInfo{\n\t\t\tID:        image.ID,\n\t\t\tTags:      image.RepoTags,\n\t\t\tIsUsed:    checkUsed(image.ID, containers),\n\t\t\tCreatedAt: time.Unix(image.Created, 0),\n\t\t\tSize:      image.Size,\n\t\t})\n\t}\n\treturn records, nil\n}\n\nfunc (u *ImageService) List() ([]dto.Options, error) {\n\tvar (\n\t\tlist      []image.Summary\n\t\tbackDatas []dto.Options\n\t)\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\tlist, err = client.ImageList(context.Background(), image.ListOptions{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, image := range list {\n\t\tfor _, tag := range image.RepoTags {\n\t\t\tbackDatas = append(backDatas, dto.Options{\n\t\t\t\tOption: tag,\n\t\t\t})\n\t\t}\n\t}\n\treturn backDatas, nil\n}\n\nfunc (u *ImageService) ImageBuild(req dto.ImageBuild) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\tfileName := \"Dockerfile\"\n\tif req.From == \"edit\" {\n\t\tdir := fmt.Sprintf(\"%s/docker/build/%s\", global.Dir.DataDir, strings.ReplaceAll(req.Name, \":\", \"_\"))\n\t\tif _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {\n\t\t\tif err = os.MkdirAll(dir, os.ModePerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tpathItem := fmt.Sprintf(\"%s/Dockerfile\", dir)\n\t\tfile, err := os.OpenFile(pathItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer file.Close()\n\t\twrite := bufio.NewWriter(file)\n\t\t_, _ = write.WriteString(string(req.Dockerfile))\n\t\twrite.Flush()\n\t\treq.Dockerfile = dir\n\t} else {\n\t\tfileName = path.Base(req.Dockerfile)\n\t\treq.Dockerfile = path.Dir(req.Dockerfile)\n\t}\n\ttar, err := archive.TarWithOptions(req.Dockerfile+\"/\", &archive.TarOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\topts := build.ImageBuildOptions{\n\t\tDockerfile: fileName,\n\t\tTags:       []string{req.Name},\n\t\tRemove:     true,\n\t\tLabels:     stringsToMap(req.Tags),\n\t\tBuildArgs:  stringsToMap2(req.Args),\n\t}\n\ttaskItem, err := task.NewTaskWithOps(req.Name, task.TaskBuild, task.TaskScopeImage, req.TaskID, 1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for image build failed, err: %v\", err)\n\t}\n\n\tgo func() {\n\t\tdefer tar.Close()\n\t\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"ImageBuild\"), func(t *task.Task) error {\n\t\t\tdockerCli := docker.NewClientWithExist(client)\n\t\t\tif err := dockerCli.BuildImageWithProcessAndOptions(taskItem, tar, opts); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\n\t\t_ = taskItem.Execute()\n\t}()\n\n\treturn nil\n}\n\nfunc (u *ImageService) ImagePull(req dto.ImagePull) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\ttaskItem, err := task.NewTaskWithOps(strings.Join(req.ImageName, \",\"), task.TaskPull, task.TaskScopeImage, req.TaskID, 1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for image pull failed, err: %v\", err)\n\t}\n\n\tfor _, item := range req.ImageName {\n\t\titemName := strings.ReplaceAll(path.Base(item), \":\", \"_\")\n\t\ttaskItem.AddSubTask(i18n.GetWithName(\"ImagePull\", itemName), func(t *task.Task) error {\n\t\t\ttaskItem.Logf(\"----------------- %s -----------------\", itemName)\n\t\t\toptions := image.PullOptions{}\n\t\t\timageName := item\n\t\t\tif req.RepoID == 0 {\n\t\t\t\thasAuth, authStr := loadAuthInfo(item)\n\t\t\t\tif hasAuth {\n\t\t\t\t\toptions.RegistryAuth = authStr\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trepo, err := imageRepoRepo.Get(repo.WithByID(req.RepoID))\n\t\t\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"ImageRepoAuthFromDB\"), err)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif repo.Auth {\n\t\t\t\t\tauthConfig := registry.AuthConfig{\n\t\t\t\t\t\tUsername: repo.Username,\n\t\t\t\t\t\tPassword: repo.Password,\n\t\t\t\t\t}\n\t\t\t\t\tencodedJSON, err := json.Marshal(authConfig)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tauthStr := base64.URLEncoding.EncodeToString(encodedJSON)\n\t\t\t\t\toptions.RegistryAuth = authStr\n\t\t\t\t}\n\t\t\t\timageName = repo.DownloadUrl + \"/\" + item\n\t\t\t}\n\t\t\tdockerCli := docker.NewClientWithExist(client)\n\t\t\terr = dockerCli.PullImageWithProcessAndOptions(taskItem, imageName, options)\n\t\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"TaskPull\"), err)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\t}\n\tgo func() {\n\t\t_ = taskItem.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (u *ImageService) ImageLoad(req dto.ImageLoad) error {\n\ttaskItem, err := task.NewTaskWithOps(strings.Join(req.Paths, \",\"), task.TaskImport, task.TaskScopeImage, req.TaskID, 1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for image import failed, err: %v\", err)\n\t}\n\n\tgo func() {\n\t\tclient, err := docker.NewDockerClient()\n\t\tif err != nil {\n\t\t\ttaskItem.Log(\"Failed to create Docker client: \" + err.Error())\n\t\t\treturn\n\t\t}\n\t\tdefer client.Close()\n\n\t\tfor _, itemPath := range req.Paths {\n\t\t\tcurrentPath := itemPath\n\t\t\titemName := path.Base(currentPath)\n\t\t\ttaskItem.AddSubTask(i18n.GetWithName(\"TaskImport\", itemName), func(t *task.Task) error {\n\t\t\t\ttaskItem.Logf(\"----------------- %s -----------------\", itemName)\n\t\t\t\tfile, err := os.Open(currentPath)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdefer file.Close()\n\t\t\t\tres, err := client.ImageLoad(context.TODO(), file)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdefer res.Body.Close()\n\t\t\t\tcontent, err := io.ReadAll(res.Body)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif strings.Contains(string(content), \"Error\") {\n\t\t\t\t\treturn errors.New(string(content))\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}, nil)\n\t\t}\n\t\t_ = taskItem.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (u *ImageService) ImageSave(req dto.ImageSave) error {\n\ttaskItem, err := task.NewTaskWithOps(req.Name, task.TaskExport, task.TaskScopeImage, req.TaskID, 1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for image export failed, err: %v\", err)\n\t}\n\ttaskItem.AddSubTask(i18n.GetWithName(\"TaskExport\", req.Name), func(t *task.Task) error {\n\t\ttaskItem.Log(req.TagName)\n\t\tclient, err := docker.NewDockerClient()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer client.Close()\n\n\t\tout, err := client.ImageSave(context.TODO(), []string{req.TagName})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer out.Close()\n\t\tfile, err := os.OpenFile(fmt.Sprintf(\"%s/%s.tar\", req.Path, req.Name), os.O_WRONLY|os.O_CREATE|os.O_EXCL, constant.FilePerm)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer file.Close()\n\t\tif _, err = io.Copy(file, out); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}, nil)\n\tgo func() {\n\t\t_ = taskItem.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (u *ImageService) ImageTag(req dto.ImageTag) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\n\timageItem, err := client.ImageInspect(context.Background(), req.SourceID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, tag := range req.Tags {\n\t\tisNew := true\n\t\tfor _, tagOld := range imageItem.RepoTags {\n\t\t\tif tag == tagOld {\n\t\t\t\tisNew = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif isNew {\n\t\t\tif err := client.ImageTag(context.TODO(), req.SourceID, tag); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tfor _, tagOld := range imageItem.RepoTags {\n\t\tisDel := true\n\t\tfor _, tag := range req.Tags {\n\t\t\tif tag == tagOld {\n\t\t\t\tisDel = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif isDel {\n\t\t\tif _, err := client.ImageRemove(context.TODO(), tagOld, image.RemoveOptions{}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *ImageService) ImagePush(req dto.ImagePush) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\n\ttaskItem, err := task.NewTaskWithOps(req.Name, task.TaskPush, task.TaskScopeImage, req.TaskID, 1)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new task for image push failed, err: %v\", err)\n\t}\n\n\tgo func() {\n\t\toptions := image.PushOptions{All: true}\n\t\tvar imageRepo model.ImageRepo\n\t\tnewName := \"\"\n\t\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"ImagePush\"), func(t *task.Task) error {\n\t\t\timageRepo, err = imageRepoRepo.Get(repo.WithByID(req.RepoID))\n\t\t\tnewName = fmt.Sprintf(\"%s/%s\", imageRepo.DownloadUrl, req.Name)\n\t\t\ttaskItem.LogWithStatus(i18n.GetMsgByKey(\"ImageRepoAuthFromDB\"), err)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\toptions = image.PushOptions{All: true}\n\t\t\tauthConfig := registry.AuthConfig{\n\t\t\t\tUsername: imageRepo.Username,\n\t\t\t\tPassword: imageRepo.Password,\n\t\t\t}\n\t\t\tencodedJSON, _ := json.Marshal(authConfig)\n\t\t\tauthStr := base64.URLEncoding.EncodeToString(encodedJSON)\n\t\t\toptions.RegistryAuth = authStr\n\t\t\treturn nil\n\t\t}, nil)\n\t\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"ImageRenameTag\"), func(t *task.Task) error {\n\t\t\ttaskItem.Log(i18n.GetWithName(\"ImageNewTag\", newName))\n\t\t\tif newName != req.TagName {\n\t\t\t\tif err := client.ImageTag(context.TODO(), req.TagName, newName); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\t\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"TaskPush\"), func(t *task.Task) error {\n\t\t\tdockerCli := docker.NewClientWithExist(client)\n\t\t\tif err := dockerCli.PushImageWithProcessAndOptions(taskItem, newName, options); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil)\n\t\t_ = taskItem.Execute()\n\t}()\n\n\treturn nil\n}\n\nfunc (u *ImageService) ImageRemove(req dto.BatchDelete) error {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\ttaskItem, err := task.NewTaskWithOps(task.TaskScopeImage, task.TaskDelete, task.TaskScopeContainer, req.TaskID, 1)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create container failed, err: %v\", err)\n\t\treturn err\n\t}\n\n\tfor _, id := range req.Names {\n\t\ttaskItem.AddSubTask(i18n.GetMsgByKey(\"TaskDelete\")+id, func(t *task.Task) error {\n\t\t\timageItem, err := client.ImageInspect(context.TODO(), id)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {\n\t\t\t\tif strings.Contains(err.Error(), \"image is being used\") || strings.Contains(err.Error(), \"is using\") {\n\t\t\t\t\tif strings.Contains(id, \"sha256:\") {\n\t\t\t\t\t\treturn buserr.New(\"ErrObjectInUsed\")\n\t\t\t\t\t}\n\t\t\t\t\treturn buserr.WithDetail(\"ErrInUsed\", id, nil)\n\t\t\t\t}\n\t\t\t\tif strings.Contains(err.Error(), \"image has dependent\") {\n\t\t\t\t\treturn buserr.New(\"ErrObjectBeDependent\")\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ttaskItem.Log(i18n.GetMsgWithMap(\"ImageRemoveHelper\", map[string]interface{}{\"name\": id, \"size\": common.LoadSizeUnit2F(float64(imageItem.Size))}))\n\t\t\treturn nil\n\t\t}, nil)\n\t}\n\tif len(req.TaskID) == 0 {\n\t\treturn taskItem.Execute()\n\t}\n\tgo func() {\n\t\t_ = taskItem.Execute()\n\t}()\n\treturn nil\n}\n\nfunc checkUsed(imageID string, containers []container.Summary) bool {\n\tfor _, container := range containers {\n\t\tif container.ImageID == imageID {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc loadAuthInfo(image string) (bool, string) {\n\tif !strings.Contains(image, \"/\") {\n\t\treturn false, \"\"\n\t}\n\thomeDir := homedir.Get()\n\tconfPath := path.Join(homeDir, \".docker/config.json\")\n\tconfigFileBytes, err := os.ReadFile(confPath)\n\tif err != nil {\n\t\treturn false, \"\"\n\t}\n\tvar config dockerConfig\n\tif err = json.Unmarshal(configFileBytes, &config); err != nil {\n\t\treturn false, \"\"\n\t}\n\tvar (\n\t\tuser   string\n\t\tpasswd string\n\t)\n\timagePrefix := strings.Split(image, \"/\")[0]\n\tif val, ok := config.Auths[imagePrefix]; ok {\n\t\titemByte, _ := base64.StdEncoding.DecodeString(val.Auth)\n\t\titemStr := string(itemByte)\n\t\tif strings.Contains(itemStr, \":\") {\n\t\t\tuser = strings.Split(itemStr, \":\")[0]\n\t\t\tpasswd = strings.Split(itemStr, \":\")[1]\n\t\t}\n\t}\n\tauthConfig := registry.AuthConfig{\n\t\tUsername: user,\n\t\tPassword: passwd,\n\t}\n\tencodedJSON, err := json.Marshal(authConfig)\n\tif err != nil {\n\t\treturn false, \"\"\n\t}\n\tauthStr := base64.URLEncoding.EncodeToString(encodedJSON)\n\treturn true, authStr\n}\n\ntype dockerConfig struct {\n\tAuths map[string]authConfig `json:\"auths\"`\n}\ntype authConfig struct {\n\tAuth string `json:\"auth\"`\n}\n"
  },
  {
    "path": "agent/app/service/image_repo.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/jinzhu/copier\"\n\t\"github.com/pkg/errors\"\n)\n\ntype ImageRepoService struct{}\n\ntype IImageRepoService interface {\n\tPage(search dto.SearchWithPage) (int64, interface{}, error)\n\tList() ([]dto.ImageRepoOption, error)\n\tLogin(req dto.OperateByID) error\n\tCreate(req dto.ImageRepoCreate) error\n\tUpdate(req dto.ImageRepoUpdate) error\n\tDelete(req dto.OperateByID) error\n}\n\nfunc NewIImageRepoService() IImageRepoService {\n\treturn &ImageRepoService{}\n}\n\nfunc (u *ImageRepoService) Page(req dto.SearchWithPage) (int64, interface{}, error) {\n\ttotal, ops, err := imageRepoRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderDesc(\"created_at\"))\n\tvar dtoOps []dto.ImageRepoInfo\n\tfor _, op := range ops {\n\t\tvar item dto.ImageRepoInfo\n\t\tif err := copier.Copy(&item, &op); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdtoOps = append(dtoOps, item)\n\t}\n\treturn total, dtoOps, err\n}\n\nfunc (u *ImageRepoService) Login(req dto.OperateByID) error {\n\trepo, err := imageRepoRepo.Get(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif repo.Auth {\n\t\tif err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil {\n\t\t\t_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error()})\n\t\t\treturn err\n\t\t}\n\t}\n\t_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{\"status\": constant.StatusSuccess})\n\treturn nil\n}\n\nfunc (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {\n\tops, err := imageRepoRepo.List(repo.WithOrderDesc(\"created_at\"))\n\tvar dtoOps []dto.ImageRepoOption\n\tfor _, op := range ops {\n\t\tif op.Status == constant.StatusSuccess {\n\t\t\tvar item dto.ImageRepoOption\n\t\t\tif err := copier.Copy(&item, &op); err != nil {\n\t\t\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t\t}\n\t\t\tdtoOps = append(dtoOps, item)\n\t\t}\n\t}\n\treturn dtoOps, err\n}\n\nfunc (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {\n\tif cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\timageRepo, _ := imageRepoRepo.Get(repo.WithByName(req.Name))\n\tif imageRepo.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\n\tif req.Protocol == \"http\" {\n\t\tif err := u.handleRegistries(req.DownloadUrl, \"\", \"create\"); err != nil {\n\t\t\treturn fmt.Errorf(\"create registry %s failed, err: %v\", req.DownloadUrl, err)\n\t\t}\n\t\tif err := stopBeforeUpdateRepo(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif req.Auth {\n\t\tif err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif err := copier.Copy(&imageRepo, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\n\timageRepo.Status = constant.StatusSuccess\n\treturn imageRepoRepo.Create(&imageRepo)\n}\n\nfunc (u *ImageRepoService) Delete(req dto.OperateByID) error {\n\tif req.ID == 1 {\n\t\treturn errors.New(\"The default value cannot be edit !\")\n\t}\n\titemRepo, _ := imageRepoRepo.Get(repo.WithByID(req.ID))\n\tif itemRepo.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif itemRepo.Auth {\n\t\t_, _ = cmd.NewCommandMgr().RunWithStdout(\"docker\", \"logout\", \"-i\", itemRepo.DownloadUrl)\n\t}\n\tif itemRepo.Protocol == \"https\" {\n\t\treturn imageRepoRepo.Delete(repo.WithByID(req.ID))\n\t}\n\tif err := u.handleRegistries(\"\", itemRepo.DownloadUrl, \"delete\"); err != nil {\n\t\treturn fmt.Errorf(\"delete registry %s failed, err: %v\", itemRepo.DownloadUrl, err)\n\t}\n\tif err := validateDockerConfig(); err != nil {\n\t\treturn err\n\t}\n\tif err := imageRepoRepo.Delete(repo.WithByID(req.ID)); err != nil {\n\t\treturn err\n\t}\n\tgo func() {\n\t\t_ = controller.HandleRestart(\"docker\")\n\t}()\n\treturn nil\n}\n\nfunc (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {\n\tif req.ID == 1 {\n\t\treturn errors.New(\"The default value cannot be deleted !\")\n\t}\n\tif cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\trepo, err := imageRepoRepo.Get(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tneedRestart := false\n\tif repo.Protocol == \"http\" && req.Protocol == \"https\" {\n\t\tif err := u.handleRegistries(\"\", repo.DownloadUrl, \"delete\"); err != nil {\n\t\t\treturn fmt.Errorf(\"delete registry %s failed, err: %v\", repo.DownloadUrl, err)\n\t\t}\n\t\tneedRestart = true\n\t}\n\tif repo.Protocol == \"http\" && req.Protocol == \"http\" {\n\t\tif err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, \"update\"); err != nil {\n\t\t\treturn fmt.Errorf(\"update registry %s => %s failed, err: %v\", repo.DownloadUrl, req.DownloadUrl, err)\n\t\t}\n\t\tneedRestart = repo.DownloadUrl == req.DownloadUrl\n\t}\n\tif repo.Protocol == \"https\" && req.Protocol == \"http\" {\n\t\tif req.DownloadUrl != repo.DownloadUrl {\n\t\t\tif err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, \"update\"); err != nil {\n\t\t\t\treturn fmt.Errorf(\"update registry %s => %s failed, err: %v\", repo.DownloadUrl, req.DownloadUrl, err)\n\t\t\t}\n\t\t\tneedRestart = true\n\t\t}\n\t}\n\tif needRestart {\n\t\tif err := stopBeforeUpdateRepo(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif repo.Auth {\n\t\t_, _ = cmd.NewCommandMgr().RunWithStdout(\"docker\", \"logout\", \"-i\", repo.DownloadUrl)\n\t}\n\tif req.Auth {\n\t\tif err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tupMap := make(map[string]interface{})\n\tupMap[\"download_url\"] = req.DownloadUrl\n\tupMap[\"protocol\"] = req.Protocol\n\tupMap[\"username\"] = req.Username\n\tupMap[\"password\"] = req.Password\n\tupMap[\"auth\"] = req.Auth\n\tupMap[\"status\"] = constant.StatusSuccess\n\tupMap[\"message\"] = \"\"\n\treturn imageRepoRepo.Update(req.ID, upMap)\n}\n\nfunc (u *ImageRepoService) CheckConn(host, user, password string) error {\n\tcmdMgr := cmd.NewCommandMgr()\n\tstdout, err := cmdMgr.RunWithStdout(\"docker\", \"login\", \"-u\", user, \"-p\", password, host)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"stdout: %s, stderr: %v\", stdout, err)\n\t}\n\tif strings.Contains(string(stdout), \"Login Succeeded\") {\n\t\treturn nil\n\t}\n\treturn errors.New(string(stdout))\n}\n\nfunc (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) error {\n\terr := createIfNotExistDaemonJsonFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdaemonMap := make(map[string]interface{})\n\tfile, err := os.ReadFile(constant.DaemonJsonPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(file) != 0 {\n\t\tif err := json.Unmarshal(file, &daemonMap); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tiRegistries := daemonMap[\"insecure-registries\"]\n\tregistries, _ := iRegistries.([]interface{})\n\tswitch handle {\n\tcase \"create\":\n\t\tregistries = common.RemoveRepeatElement(append(registries, newHost))\n\tcase \"update\":\n\t\tfor i, regi := range registries {\n\t\t\tif regi == delHost {\n\t\t\t\tregistries = append(registries[:i], registries[i+1:]...)\n\t\t\t}\n\t\t}\n\t\tregistries = common.RemoveRepeatElement(append(registries, newHost))\n\tcase \"delete\":\n\t\tfor i, regi := range registries {\n\t\t\tif regi == delHost {\n\t\t\t\tregistries = append(registries[:i], registries[i+1:]...)\n\t\t\t}\n\t\t}\n\t}\n\tif len(registries) == 0 {\n\t\tdelete(daemonMap, \"insecure-registries\")\n\t} else {\n\t\tdaemonMap[\"insecure-registries\"] = registries\n\t}\n\tnewJson, err := json.MarshalIndent(daemonMap, \"\", \"\\t\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc stopBeforeUpdateRepo() error {\n\tif err := validateDockerConfig(); err != nil {\n\t\treturn err\n\t}\n\tdockerService, _ := controller.LoadServiceName(\"docker\")\n\tif len(dockerService) == 0 {\n\t\treturn errors.New(\"docker service not found\")\n\t}\n\tclient, err := controller.New()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := client.Operate(\"restart\", dockerService); err != nil {\n\t\treturn fmt.Errorf(\"failed to restart Docker: %v\", err)\n\t}\n\tticker := time.NewTicker(3 * time.Second)\n\tdefer ticker.Stop()\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*30)\n\tif err := func() error {\n\t\tfor range ticker.C {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\tcancel()\n\t\t\t\treturn errors.New(\"the docker service cannot be restarted\")\n\t\t\tdefault:\n\t\t\t\tactive, _ := client.IsActive(dockerService)\n\t\t\t\tif active {\n\t\t\t\t\tglobal.LOG.Info(\"docker restart with new conf successful!\")\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/iptables.go",
    "content": "package service\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall/client\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables\"\n)\n\ntype IIptablesService interface {\n\tSearch(req dto.SearchPageWithType) (int64, interface{}, error)\n\tOperateRule(req dto.IptablesRuleOp, withSave bool) error\n\tBatchOperate(req dto.IptablesBatchOperate) error\n\tLoadChainStatus(req dto.OperationWithName) dto.IptablesChainStatus\n\n\tOperate(req dto.IptablesOp) error\n}\n\ntype IptablesService struct{}\n\nfunc NewIIptablesService() IIptablesService {\n\treturn &IptablesService{}\n}\n\nfunc (s *IptablesService) Search(req dto.SearchPageWithType) (int64, interface{}, error) {\n\trules, err := iptables.ReadFilterRulesByChain(req.Type)\n\tif err != nil {\n\t\treturn 0, nil, fmt.Errorf(\"failed to read iptables rules: %w\", err)\n\t}\n\tvar records []iptables.FilterRules\n\ttotal, start, end := len(rules), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\trecords = make([]iptables.FilterRules, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\trecords = rules[start:end]\n\t}\n\n\trulesInDB, _ := hostRepo.ListFirewallRecord(hostRepo.WithByChain(req.Type))\n\n\tfor i := 0; i < len(records); i++ {\n\t\tfor _, item := range rulesInDB {\n\t\t\tif records[i].Strategy == item.Strategy &&\n\t\t\t\trecords[i].DstIP == item.DstIP &&\n\t\t\t\tfmt.Sprintf(\"%v\", records[i].DstPort) == item.DstPort &&\n\t\t\t\trecords[i].Protocol == item.Protocol &&\n\t\t\t\trecords[i].SrcIP == item.SrcIP &&\n\t\t\t\tfmt.Sprintf(\"%v\", records[i].SrcPort) == item.SrcPort {\n\t\t\t\trecords[i].ID = item.ID\n\t\t\t\trecords[i].Description = item.Description\n\t\t\t}\n\t\t}\n\t}\n\treturn int64(total), records, nil\n}\n\nfunc (s *IptablesService) OperateRule(req dto.IptablesRuleOp, withSave bool) error {\n\taction := \"ACCEPT\"\n\tif req.Strategy == \"drop\" {\n\t\taction = \"DROP\"\n\t}\n\tpolicy := iptables.FilterRules{\n\t\tProtocol: req.Protocol,\n\t\tSrcIP:    req.SrcIP,\n\t\tDstIP:    req.DstIP,\n\t\tStrategy: action,\n\t}\n\tif req.SrcPort != 0 {\n\t\tpolicy.SrcPort = fmt.Sprintf(\"%v\", req.SrcPort)\n\t}\n\tif req.DstPort != 0 {\n\t\tpolicy.DstPort = fmt.Sprintf(\"%v\", req.DstPort)\n\t}\n\n\tname := iptables.InputFileName\n\tif req.Chain == iptables.Chain1PanelOutput {\n\t\tname = iptables.OutputFileName\n\t}\n\tswitch req.Operation {\n\tcase \"add\":\n\t\tif err := s.validateRuleInput(&req); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := iptables.AddFilterRule(req.Chain, policy); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to add iptables rule: %w\", err)\n\t\t}\n\n\t\tif len(req.Description) != 0 {\n\t\t\trule := &model.Firewall{\n\t\t\t\tChain:       req.Chain,\n\t\t\t\tProtocol:    req.Protocol,\n\t\t\t\tSrcIP:       req.SrcIP,\n\t\t\t\tSrcPort:     policy.SrcPort,\n\t\t\t\tDstIP:       req.DstIP,\n\t\t\t\tDstPort:     policy.DstPort,\n\t\t\t\tStrategy:    req.Strategy,\n\t\t\t\tDescription: req.Description,\n\t\t\t}\n\n\t\t\tif err := hostRepo.SaveFirewallRecord(rule); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to save rule to database: %w\", err)\n\t\t\t}\n\t\t}\n\tcase \"remove\":\n\t\tif err := iptables.DeleteFilterRule(req.Chain, policy); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to remove iptables rule: %w\", err)\n\t\t}\n\t\tif req.ID != 0 {\n\t\t\tif err := hostRepo.DeleteFirewallRecordByID(req.ID); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to delete rule from database: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tif !withSave {\n\t\treturn nil\n\t}\n\tif err := iptables.SaveRulesToFile(iptables.FilterTab, req.Chain, name); err != nil {\n\t\tglobal.LOG.Errorf(\"persistence for %s failed, err: %v\", iptables.Chain1PanelBasic, err)\n\t}\n\treturn nil\n}\n\nfunc (s *IptablesService) BatchOperate(req dto.IptablesBatchOperate) error {\n\tif len(req.Rules) == 0 {\n\t\treturn errors.New(\"no rules to operate\")\n\t}\n\tfor _, rule := range req.Rules {\n\t\tif err := s.OperateRule(rule, false); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tchain := iptables.Chain1PanelInput\n\tfileName := iptables.InputFileName\n\tif req.Rules[0].Chain == iptables.Chain1PanelOutput {\n\t\tchain = iptables.Chain1PanelOutput\n\t\tfileName = iptables.OutputFileName\n\t}\n\tif err := iptables.SaveRulesToFile(iptables.FilterTab, chain, fileName); err != nil {\n\t\tglobal.LOG.Errorf(\"persistence for %s failed, err: %v\", iptables.Chain1PanelBasic, err)\n\t}\n\treturn nil\n}\n\nfunc (s *IptablesService) Operate(req dto.IptablesOp) error {\n\ttargetChain := iptables.ChainInput\n\tif req.Name == iptables.Chain1PanelOutput {\n\t\ttargetChain = iptables.ChainOutput\n\t}\n\tswitch req.Operate {\n\tcase \"init-base\":\n\t\tif ok := cmd.Which(\"iptables\"); !ok {\n\t\t\treturn fmt.Errorf(\"failed to find iptables\")\n\t\t}\n\t\tif err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasicBefore); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasic); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasicAfter); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := initPreRules(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore, 1); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic, 2); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.BasicBeforeFileName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.BasicAfterFileName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = settingRepo.Update(\"IptablesStatus\", constant.StatusEnable)\n\t\treturn nil\n\tcase \"init-forward\":\n\t\tif err := client.EnableIptablesForward(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = settingRepo.Update(\"IptablesForwardStatus\", constant.StatusEnable)\n\t\treturn nil\n\tcase \"init-advance\":\n\t\tif err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelInput); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelOutput); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainOutput, iptables.Chain1PanelOutput, 1); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tnumber := loadBindNumber(iptables.Chain1PanelInput)\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelInput, number); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = settingRepo.Update(\"IptablesInputStatus\", constant.StatusEnable)\n\t\t_ = settingRepo.Update(\"IptablesOutputStatus\", constant.StatusEnable)\n\t\treturn nil\n\tcase \"bind-base\":\n\t\tif err := initPreRules(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore, 1); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic, 2); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = settingRepo.Update(\"IptablesStatus\", constant.StatusEnable)\n\t\treturn nil\n\tcase \"bind-base-without-init\":\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore, 1); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic, 2); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = settingRepo.Update(\"IptablesStatus\", constant.StatusEnable)\n\t\treturn nil\n\tcase \"unbind-base\":\n\t\tif err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = settingRepo.Update(\"IptablesStatus\", constant.StatusDisable)\n\t\treturn nil\n\tcase \"bind\":\n\t\tif err := iptables.BindChain(iptables.FilterTab, targetChain, req.Name, loadBindNumber(req.Name)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif req.Name == iptables.Chain1PanelInput {\n\t\t\t_ = settingRepo.Update(\"IptablesInputStatus\", constant.StatusEnable)\n\t\t}\n\t\tif req.Name == iptables.Chain1PanelOutput {\n\t\t\t_ = settingRepo.Update(\"IptablesOutputStatus\", constant.StatusEnable)\n\t\t}\n\t\treturn nil\n\tcase \"unbind\":\n\t\tif err := iptables.UnbindChain(iptables.FilterTab, targetChain, req.Name); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif req.Name == iptables.Chain1PanelInput {\n\t\t\t_ = settingRepo.Update(\"IptablesInputStatus\", constant.StatusDisable)\n\t\t}\n\t\tif req.Name == iptables.Chain1PanelOutput {\n\t\t\t_ = settingRepo.Update(\"IptablesOutputStatus\", constant.StatusDisable)\n\t\t}\n\t\treturn nil\n\t}\n\treturn nil\n}\n\nfunc (s *IptablesService) LoadChainStatus(req dto.OperationWithName) dto.IptablesChainStatus {\n\tvar data dto.IptablesChainStatus\n\tvar err error\n\tdata.DefaultStrategy, err = iptables.LoadDefaultStrategy(req.Name)\n\tif err != nil {\n\t\tglobal.LOG.Error(err)\n\t}\n\tswitch req.Name {\n\tcase iptables.Chain1PanelBasic:\n\t\tdata.IsBind, _ = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)\n\tcase iptables.Chain1PanelInput:\n\t\tdata.IsBind, _ = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)\n\tcase iptables.Chain1PanelOutput:\n\t\tdata.IsBind, _ = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainOutput, req.Name)\n\t}\n\treturn data\n}\n\nfunc (s *IptablesService) validateRuleInput(req *dto.IptablesRuleOp) error {\n\tif req.Protocol != \"\" {\n\t\tvalidProtocols := map[string]bool{\"tcp\": true, \"udp\": true, \"icmp\": true, \"all\": true}\n\t\tif !validProtocols[strings.ToLower(req.Protocol)] {\n\t\t\treturn fmt.Errorf(\"invalid protocol: %s, must be tcp, udp, icmp or all\", req.Protocol)\n\t\t}\n\t}\n\tif req.SrcIP != \"\" {\n\t\tif err := s.validateIPOrCIDR(req.SrcIP); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid source IP: %w\", err)\n\t\t}\n\t}\n\tif req.DstIP != \"\" {\n\t\tif err := s.validateIPOrCIDR(req.DstIP); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid destination IP: %w\", err)\n\t\t}\n\t}\n\tif req.SrcPort > 65535 {\n\t\treturn fmt.Errorf(\"invalid source port: %d, must be between 1 and 65535\", req.SrcPort)\n\t}\n\tif req.DstPort > 65535 {\n\t\treturn fmt.Errorf(\"invalid destination port: %d, must be between 1 and 65535\", req.DstPort)\n\t}\n\tif (req.SrcPort > 0 || req.DstPort > 0) && req.Protocol == \"\" {\n\t\treturn fmt.Errorf(\"port specification requires protocol (tcp/udp)\")\n\t}\n\n\treturn nil\n}\n\nfunc (s *IptablesService) validateIPOrCIDR(ipStr string) error {\n\tif strings.Contains(ipStr, \"/\") {\n\t\t_, _, err := net.ParseCIDR(ipStr)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid CIDR format: %w\", err)\n\t\t}\n\t\treturn nil\n\t}\n\tip := net.ParseIP(ipStr)\n\tif ip == nil {\n\t\treturn fmt.Errorf(\"invalid IP address format\")\n\t}\n\n\treturn nil\n}\n\nfunc loadBindNumber(chain string) int {\n\tif chain == iptables.Chain1PanelOutput {\n\t\treturn 1\n\t}\n\tnumber := 1\n\tif exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore); exist {\n\t\tnumber++\n\t}\n\tif exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasic); exist {\n\t\tnumber++\n\t}\n\treturn number\n}\n\nfunc initPreRules() error {\n\tif err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.IoRuleIn); err != nil {\n\t\treturn err\n\t}\n\tif err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.EstablishedRule); err != nil {\n\t\treturn err\n\t}\n\tpanelPort := LoadPanelPort()\n\tif len(panelPort) == 0 {\n\t\treturn errors.New(\"find 1panel service port failed\")\n\t}\n\tports := []string{\"80\", \"443\", panelPort, loadSSHPort()}\n\tfor _, item := range ports {\n\t\tif err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, fmt.Sprintf(\"-p tcp -m tcp --dport %v -j ACCEPT\", item)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, \"-p udp -m udp --dport 443 -j ACCEPT\"); err != nil {\n\t\treturn err\n\t}\n\tif err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllTcp); err != nil {\n\t\treturn err\n\t}\n\tif err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllUdp); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc LoadPanelPort() string {\n\tif !global.IsMaster {\n\t\treturn global.CONF.Base.Port\n\t} else {\n\t\tvar portSetting model.Setting\n\t\t_ = global.CoreDB.Where(\"key = ?\", \"ServerPort\").First(&portSetting).Error\n\t\tif len(portSetting.Value) != 0 {\n\t\t\treturn portSetting.Value\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "agent/app/service/logs.go",
    "content": "package service\n\nimport (\n\t\"os\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype LogService struct{}\n\ntype ILogService interface {\n\tListSystemLogFile() ([]string, error)\n}\n\nfunc NewILogService() ILogService {\n\treturn &LogService{}\n}\n\nfunc (u *LogService) ListSystemLogFile() ([]string, error) {\n\tvar listFile []string\n\tfiles, err := os.ReadDir(global.Dir.LogDir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlistMap := make(map[string]struct{})\n\tfor _, item := range files {\n\t\tif item.IsDir() || !strings.HasPrefix(item.Name(), \"1Panel\") {\n\t\t\tcontinue\n\t\t}\n\t\tif item.Name() == \"1Panel.log\" || item.Name() == \"1Panel-Core.log\" {\n\t\t\titemName := time.Now().Format(\"2006-01-02\")\n\t\t\tif _, ok := listMap[itemName]; ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlistMap[itemName] = struct{}{}\n\t\t\tlistFile = append(listFile, itemName)\n\t\t\tcontinue\n\t\t}\n\t\titemFileName := strings.TrimPrefix(item.Name(), \"1Panel-Core-\")\n\t\titemFileName = strings.TrimPrefix(itemFileName, \"1Panel-\")\n\t\titemFileName = strings.TrimSuffix(itemFileName, \".gz\")\n\t\titemFileName = strings.TrimSuffix(itemFileName, \".log\")\n\t\tif len(itemFileName) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := listMap[itemFileName]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tlistMap[itemFileName] = struct{}{}\n\t\tlistFile = append(listFile, itemFileName)\n\t}\n\tif len(listFile) < 2 {\n\t\treturn listFile, nil\n\t}\n\tsort.Slice(listFile, func(i, j int) bool {\n\t\treturn listFile[i] > listFile[j]\n\t})\n\n\treturn listFile, nil\n}\n"
  },
  {
    "path": "agent/app/service/mcp_server.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/ai\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n\t\"github.com/subosito/gotenv\"\n\t\"gopkg.in/yaml.v3\"\n)\n\ntype McpServerService struct{}\n\ntype IMcpServerService interface {\n\tPage(req request.McpServerSearch) response.McpServersRes\n\tCreate(create request.McpServerCreate) error\n\tUpdate(req request.McpServerUpdate) error\n\tDelete(id uint) error\n\tOperate(req request.McpServerOperate) error\n\tGetBindDomain() (response.McpBindDomainRes, error)\n\tBindDomain(req request.McpBindDomain) error\n\tUpdateBindDomain(req request.McpBindDomainUpdate) error\n}\n\nfunc NewIMcpServerService() IMcpServerService {\n\treturn &McpServerService{}\n}\n\nfunc (m McpServerService) Page(req request.McpServerSearch) response.McpServersRes {\n\tvar (\n\t\tres   response.McpServersRes\n\t\titems []response.McpServerDTO\n\t)\n\n\ttotal, data, _ := mcpServerRepo.Page(req.PageInfo.Page, req.PageInfo.PageSize)\n\tfor _, item := range data {\n\t\t_ = syncMcpServerContainerStatus(&item)\n\t\tserverDTO := response.McpServerDTO{\n\t\t\tMcpServer:    item,\n\t\t\tEnvironments: make([]request.Environment, 0),\n\t\t\tVolumes:      make([]request.Volume, 0),\n\t\t}\n\t\tproject, err := docker.GetComposeProject(item.Name, path.Join(global.Dir.McpDir, item.Name), []byte(item.DockerCompose), []byte(item.Env), true)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"get mcp compose project error: %s\", err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tfor _, service := range project.Services {\n\t\t\tif service.Environment != nil {\n\t\t\t\tfor key, value := range service.Environment {\n\t\t\t\t\tserverDTO.Environments = append(serverDTO.Environments, request.Environment{\n\t\t\t\t\t\tKey:   key,\n\t\t\t\t\t\tValue: *value,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tif service.Volumes != nil {\n\t\t\t\tfor _, volume := range service.Volumes {\n\t\t\t\t\tserverDTO.Volumes = append(serverDTO.Volumes, request.Volume{\n\t\t\t\t\t\tSource: volume.Source,\n\t\t\t\t\t\tTarget: volume.Target,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\titems = append(items, serverDTO)\n\t}\n\tres.Total = total\n\tres.Items = items\n\treturn res\n}\n\nfunc (m McpServerService) Update(req request.McpServerUpdate) error {\n\tgo pullImage(req.Type)\n\tmcpServer, err := mcpServerRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif mcpServer.Port != req.Port {\n\t\tif err := checkPortExist(req.Port); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif mcpServer.ContainerName != req.ContainerName {\n\t\tif err := checkContainerName(req.ContainerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treq.Command = strings.TrimSuffix(req.Command, \"\\n\")\n\tmcpServer.Name = req.Name\n\tmcpServer.ContainerName = req.ContainerName\n\tmcpServer.Port = req.Port\n\tmcpServer.Command = req.Command\n\tmcpServer.BaseURL = req.BaseURL\n\tmcpServer.HostIP = req.HostIP\n\tmcpServer.OutputTransport = req.OutputTransport\n\tmcpServer.Type = req.Type\n\tif req.OutputTransport == \"sse\" {\n\t\tmcpServer.SsePath = req.SsePath\n\t} else {\n\t\tmcpServer.StreamableHttpPath = req.StreamableHttpPath\n\t}\n\tif err := handleCreateParams(mcpServer, req.Environments, req.Volumes); err != nil {\n\t\treturn err\n\t}\n\tenv := handleEnv(mcpServer)\n\tmcpDir := path.Join(global.Dir.McpDir, mcpServer.Name)\n\tenvPath := path.Join(mcpDir, \".env\")\n\tif err := gotenv.Write(env, envPath); err != nil {\n\t\treturn err\n\t}\n\tdockerComposePath := path.Join(mcpDir, \"docker-compose.yml\")\n\tif err := files.NewFileOp().SaveFile(dockerComposePath, mcpServer.DockerCompose, 0644); err != nil {\n\t\treturn err\n\t}\n\tmcpServer.Status = constant.StatusStarting\n\tif err := mcpServerRepo.Save(mcpServer); err != nil {\n\t\treturn err\n\t}\n\tgo startMcp(mcpServer)\n\treturn nil\n}\n\nfunc (m McpServerService) Create(create request.McpServerCreate) error {\n\tgo pullImage(create.Type)\n\tservers, _ := mcpServerRepo.List()\n\tfor _, server := range servers {\n\t\tif server.Port == create.Port {\n\t\t\treturn buserr.New(\"ErrPortInUsed\")\n\t\t}\n\t\tif server.ContainerName == create.ContainerName {\n\t\t\treturn buserr.New(\"ErrContainerName\")\n\t\t}\n\t\tif server.Name == create.Name {\n\t\t\treturn buserr.New(\"ErrNameIsExist\")\n\t\t}\n\t\tif server.SsePath == create.SsePath {\n\t\t\treturn buserr.New(\"ErrSsePath\")\n\t\t}\n\t}\n\tcreate.Command = strings.TrimSuffix(create.Command, \"\\n\")\n\tif err := checkPortExist(create.Port); err != nil {\n\t\treturn err\n\t}\n\tif err := checkContainerName(create.ContainerName); err != nil {\n\t\treturn err\n\t}\n\tmcpDir := path.Join(global.Dir.McpDir, create.Name)\n\tmcpServer := &model.McpServer{\n\t\tName:            create.Name,\n\t\tContainerName:   create.ContainerName,\n\t\tPort:            create.Port,\n\t\tCommand:         create.Command,\n\t\tStatus:          constant.StatusStarting,\n\t\tBaseURL:         create.BaseURL,\n\t\tDir:             mcpDir,\n\t\tHostIP:          create.HostIP,\n\t\tOutputTransport: create.OutputTransport,\n\t\tType:            create.Type,\n\t}\n\tif create.OutputTransport == \"sse\" {\n\t\tmcpServer.SsePath = create.SsePath\n\t} else {\n\t\tmcpServer.StreamableHttpPath = create.StreamableHttpPath\n\t}\n\n\tif err := handleCreateParams(mcpServer, create.Environments, create.Volumes); err != nil {\n\t\treturn err\n\t}\n\n\tenv := handleEnv(mcpServer)\n\tfilesOP := files.NewFileOp()\n\tif !filesOP.Stat(mcpDir) {\n\t\t_ = filesOP.CreateDir(mcpDir, 0644)\n\t}\n\tenvPath := path.Join(mcpDir, \".env\")\n\tif err := gotenv.Write(env, envPath); err != nil {\n\t\treturn err\n\t}\n\tdockerComposePath := path.Join(mcpDir, \"docker-compose.yml\")\n\tif err := filesOP.SaveFile(dockerComposePath, mcpServer.DockerCompose, 0644); err != nil {\n\t\treturn err\n\t}\n\tif err := mcpServerRepo.Create(mcpServer); err != nil {\n\t\treturn err\n\t}\n\taddProxy(mcpServer)\n\tgo startMcp(mcpServer)\n\treturn nil\n}\n\nfunc (m McpServerService) Delete(id uint) error {\n\tmcpServer, err := mcpServerRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcomposePath := path.Join(global.Dir.McpDir, mcpServer.Name, \"docker-compose.yml\")\n\t_, _ = compose.Down(composePath)\n\t_ = files.NewFileOp().DeleteDir(path.Join(global.Dir.McpDir, mcpServer.Name))\n\n\twebsiteID := GetWebsiteID()\n\tif websiteID > 0 {\n\t\twebsiteService := NewIWebsiteService()\n\t\tdelProxyReq := request.WebsiteProxyDel{\n\t\t\tID:   websiteID,\n\t\t\tName: mcpServer.Name,\n\t\t}\n\t\t_ = websiteService.DeleteProxy(delProxyReq)\n\t}\n\treturn mcpServerRepo.DeleteBy(repo.WithByID(id))\n}\n\nfunc (m McpServerService) Operate(req request.McpServerOperate) error {\n\tmcpServer, err := mcpServerRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcomposePath := path.Join(mcpServer.Dir, \"docker-compose.yml\")\n\tvar out string\n\tswitch req.Operate {\n\tcase \"start\":\n\t\tout, err = compose.Up(composePath)\n\t\tmcpServer.Status = constant.StatusRunning\n\tcase \"stop\":\n\t\tout, err = compose.Down(composePath)\n\t\tmcpServer.Status = constant.StatusStopped\n\tcase \"restart\":\n\t\tout, err = compose.Restart(composePath)\n\t\tmcpServer.Status = constant.StatusRunning\n\t}\n\tif err != nil {\n\t\tmcpServer.Status = constant.StatusError\n\t\tmcpServer.Message = out\n\t}\n\treturn mcpServerRepo.Save(mcpServer)\n}\n\nfunc (m McpServerService) GetBindDomain() (response.McpBindDomainRes, error) {\n\tvar res response.McpBindDomainRes\n\twebsiteID := GetWebsiteID()\n\tif websiteID == 0 {\n\t\treturn res, nil\n\t}\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteID))\n\tif err != nil {\n\t\treturn res, nil\n\t}\n\tres.WebsiteID = website.ID\n\tres.Domain = website.PrimaryDomain\n\tif website.WebsiteSSLID > 0 {\n\t\tres.SSLID = website.WebsiteSSLID\n\t\tssl, _ := websiteSSLRepo.GetFirst(repo.WithByID(website.WebsiteSSLID))\n\t\tres.AcmeAccountID = ssl.AcmeAccountID\n\t}\n\tres.ConnUrl = fmt.Sprintf(\"%s://%s\", strings.ToLower(website.Protocol), website.PrimaryDomain)\n\tres.AllowIPs = GetAllowIps(website)\n\treturn res, nil\n\n}\n\nfunc (m McpServerService) BindDomain(req request.McpBindDomain) error {\n\tnginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)\n\tif nginxInstall.ID == 0 {\n\t\treturn buserr.New(\"ErrOpenrestyInstall\")\n\t}\n\tvar (\n\t\tipList []string\n\t\terr    error\n\t)\n\tif len(req.IPList) > 0 {\n\t\tipList, err = common.HandleIPList(req.IPList)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif req.SSLID > 0 {\n\t\tssl, err := websiteSSLRepo.GetFirst(repo.WithByID(req.SSLID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif ssl.Pem == \"\" {\n\t\t\treturn buserr.New(\"ErrSSL\")\n\t\t}\n\t}\n\tgroup, _ := groupRepo.Get(groupRepo.WithByWebsiteDefault())\n\n\tdomain, err := ParseDomain(req.Domain)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif domain.Port == 0 {\n\t\tdomain.Port = nginxInstall.HttpPort\n\t}\n\tcreateWebsiteReq := request.WebsiteCreate{\n\t\tDomains: []request.WebsiteDomain{\n\t\t\t{\n\t\t\t\tDomain: domain.Domain,\n\t\t\t\tPort:   domain.Port,\n\t\t\t},\n\t\t},\n\t\tAlias:          strings.ToLower(req.Domain),\n\t\tType:           constant.Static,\n\t\tWebsiteGroupID: group.ID,\n\t}\n\tif req.SSLID > 0 {\n\t\tcreateWebsiteReq.WebsiteSSLID = req.SSLID\n\t\tcreateWebsiteReq.EnableSSL = true\n\t}\n\twebsiteService := NewIWebsiteService()\n\tif err := websiteService.CreateWebsite(createWebsiteReq); err != nil {\n\t\treturn err\n\t}\n\twebsite, err := websiteRepo.GetFirst(websiteRepo.WithAlias(strings.ToLower(req.Domain)))\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = settingRepo.UpdateOrCreate(\"MCP_WEBSITE_ID\", fmt.Sprintf(\"%d\", website.ID))\n\tif len(ipList) > 0 {\n\t\tif err = ConfigAllowIPs(ipList, website); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err = addMCPProxy(website.ID); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (m McpServerService) UpdateBindDomain(req request.McpBindDomainUpdate) error {\n\tnginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)\n\tif nginxInstall.ID == 0 {\n\t\treturn buserr.New(\"ErrOpenrestyInstall\")\n\t}\n\tvar (\n\t\tipList []string\n\t\terr    error\n\t)\n\tif len(req.IPList) > 0 {\n\t\tipList, err = common.HandleIPList(req.IPList)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif req.SSLID > 0 {\n\t\tssl, err := websiteSSLRepo.GetFirst(repo.WithByID(req.SSLID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif ssl.Pem == \"\" {\n\t\t\treturn buserr.New(\"ErrSSL\")\n\t\t}\n\t}\n\twebsiteService := NewIWebsiteService()\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = ConfigAllowIPs(ipList, website); err != nil {\n\t\treturn err\n\t}\n\tif req.SSLID > 0 {\n\t\tsslReq := request.WebsiteHTTPSOp{\n\t\t\tWebsiteID:    website.ID,\n\t\t\tEnable:       true,\n\t\t\tType:         \"existed\",\n\t\t\tWebsiteSSLID: req.SSLID,\n\t\t\tHttpConfig:   \"HTTPSOnly\",\n\t\t}\n\t\tif _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif website.WebsiteSSLID > 0 && req.SSLID == 0 {\n\t\tsslReq := request.WebsiteHTTPSOp{\n\t\t\tWebsiteID: website.ID,\n\t\t\tEnable:    false,\n\t\t}\n\t\tif _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tgo updateMcpConfig(website.ID)\n\treturn nil\n}\n\nfunc updateMcpConfig(websiteID uint) {\n\tservers, _ := mcpServerRepo.List()\n\tif len(servers) == 0 {\n\t\treturn\n\t}\n\twebsite, _ := websiteRepo.GetFirst(repo.WithByID(websiteID))\n\twebsiteDomain := website.Domains[0]\n\tvar baseUrl string\n\tif website.Protocol == constant.ProtocolHTTP {\n\t\tbaseUrl = fmt.Sprintf(\"http://%s\", websiteDomain.Domain)\n\t} else {\n\t\tbaseUrl = fmt.Sprintf(\"https://%s\", websiteDomain.Domain)\n\t}\n\n\tgo func() {\n\t\tfor _, server := range servers {\n\t\t\tif server.BaseURL != baseUrl {\n\t\t\t\tserver.BaseURL = baseUrl\n\t\t\t\tserver.HostIP = \"127.0.0.1\"\n\t\t\t\t_ = updateMcpServer(&server)\n\t\t\t}\n\t\t}\n\t}()\n}\n\nfunc addProxy(server *model.McpServer) {\n\twebsiteID := GetWebsiteID()\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteID))\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"[mcp] add proxy failed, err: %v\", err)\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\tincludeDir := GetSitePath(website, SiteProxyDir)\n\tif !fileOp.Stat(includeDir) {\n\t\tif err = fileOp.CreateDir(includeDir, 0644); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tconfig, err := parser.NewStringParser(string(nginx_conf.SSE)).Parse()\n\tif err != nil {\n\t\treturn\n\t}\n\tincludePath := path.Join(includeDir, server.Name+\".conf\")\n\tconfig.FilePath = includePath\n\tdirectives := config.Directives\n\tlocation, ok := directives[0].(*components.Location)\n\tif !ok {\n\t\treturn\n\t}\n\tvar proxyPath string\n\tif server.OutputTransport == \"sse\" {\n\t\tproxyPath = server.SsePath\n\t} else {\n\t\tproxyPath = server.StreamableHttpPath\n\t}\n\tlocation.UpdateDirective(\"proxy_pass\", []string{fmt.Sprintf(\"http://127.0.0.1:%d%s\", server.Port, proxyPath)})\n\tlocation.ChangePath(\"^~\", proxyPath)\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\tglobal.LOG.Errorf(\"write config failed, err: %v\", buserr.WithErr(\"ErrUpdateBuWebsite\", err))\n\t\treturn\n\t}\n\tnginxInclude := fmt.Sprintf(\"/www/sites/%s/proxy/*.conf\", website.Alias)\n\tif err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: \"include\", Params: []string{nginxInclude}}}, &website); err != nil {\n\t\tglobal.LOG.Errorf(\"update nginx config failed, err: %v\", err)\n\t\treturn\n\t}\n}\n\nfunc addMCPProxy(websiteID uint) error {\n\tservers, _ := mcpServerRepo.List()\n\tif len(servers) == 0 {\n\t\treturn nil\n\t}\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfileOp := files.NewFileOp()\n\tincludeDir := GetSitePath(website, SiteProxyDir)\n\tif !fileOp.Stat(includeDir) {\n\t\tif err = fileOp.CreateDir(includeDir, 0644); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tconfig, err := parser.NewStringParser(string(nginx_conf.SSE)).Parse()\n\tif err != nil {\n\t\treturn err\n\t}\n\twebsiteDomain := website.Domains[0]\n\tvar baseUrl string\n\tif website.Protocol == constant.ProtocolHTTP {\n\t\tbaseUrl = fmt.Sprintf(\"http://%s\", websiteDomain.Domain)\n\t} else {\n\t\tbaseUrl = fmt.Sprintf(\"https://%s\", websiteDomain.Domain)\n\t}\n\tif websiteDomain.Port != 80 && websiteDomain.Port != 443 {\n\t\tbaseUrl = fmt.Sprintf(\"%s:%d\", baseUrl, websiteDomain.Port)\n\t}\n\tfor _, server := range servers {\n\t\tincludePath := path.Join(includeDir, server.Name+\".conf\")\n\t\tconfig.FilePath = includePath\n\t\tdirectives := config.Directives\n\t\tlocation, ok := directives[0].(*components.Location)\n\t\tif !ok {\n\t\t\terr = errors.New(\"error\")\n\t\t\treturn err\n\t\t}\n\t\tvar proxyPath string\n\t\tif server.OutputTransport == \"sse\" {\n\t\t\tproxyPath = server.SsePath\n\t\t} else {\n\t\t\tproxyPath = server.StreamableHttpPath\n\t\t}\n\t\tlocation.UpdateDirective(\"proxy_pass\", []string{fmt.Sprintf(\"http://127.0.0.1:%d%s\", server.Port, proxyPath)})\n\t\tlocation.ChangePath(\"^~\", proxyPath)\n\t\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\t\treturn buserr.WithErr(\"ErrUpdateBuWebsite\", err)\n\t\t}\n\t\tserver.BaseURL = baseUrl\n\t\tserver.HostIP = \"127.0.0.1\"\n\t\tgo updateMcpServer(&server)\n\t}\n\tnginxInclude := fmt.Sprintf(\"/www/sites/%s/proxy/*.conf\", website.Alias)\n\tif err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: \"include\", Params: []string{nginxInclude}}}, &website); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc updateMcpServer(mcpServer *model.McpServer) error {\n\tenv := handleEnv(mcpServer)\n\tif err := gotenv.Write(env, path.Join(mcpServer.Dir, \".env\")); err != nil {\n\t\treturn err\n\t}\n\t_ = mcpServerRepo.Save(mcpServer)\n\tcomposePath := path.Join(global.Dir.McpDir, mcpServer.Name, \"docker-compose.yml\")\n\t_, _ = compose.Down(composePath)\n\tif _, err := compose.Up(composePath); err != nil {\n\t\tmcpServer.Status = constant.StatusError\n\t\tmcpServer.Message = err.Error()\n\t}\n\treturn mcpServerRepo.Save(mcpServer)\n}\n\nfunc handleEnv(mcpServer *model.McpServer) gotenv.Env {\n\tenv := make(gotenv.Env)\n\tenv[\"CONTAINER_NAME\"] = mcpServer.ContainerName\n\tenv[\"COMMAND\"] = mcpServer.Command\n\tenv[\"PANEL_APP_PORT_HTTP\"] = strconv.Itoa(mcpServer.Port)\n\tenv[\"BASE_URL\"] = mcpServer.BaseURL\n\tenv[\"SSE_PATH\"] = mcpServer.SsePath\n\tenv[\"HOST_IP\"] = mcpServer.HostIP\n\tenv[\"STREAMABLE_HTTP_PATH\"] = mcpServer.StreamableHttpPath\n\tenv[\"OUTPUT_TRANSPORT\"] = mcpServer.OutputTransport\n\tenvStr, _ := gotenv.Marshal(env)\n\tmcpServer.Env = envStr\n\treturn env\n}\n\nfunc handleCreateParams(mcpServer *model.McpServer, environments []request.Environment, volumes []request.Volume) error {\n\tvar composeContent []byte\n\tif mcpServer.ID == 0 {\n\t\tcomposeContent = ai.DefaultMcpCompose\n\t} else {\n\t\tcomposeContent = []byte(mcpServer.DockerCompose)\n\t}\n\tcomposeMap := make(map[string]interface{})\n\tif err := yaml.Unmarshal(composeContent, &composeMap); err != nil {\n\t\treturn err\n\t}\n\tservices, serviceValid := composeMap[\"services\"].(map[string]interface{})\n\tif !serviceValid {\n\t\treturn buserr.New(\"ErrFileParse\")\n\t}\n\tserviceName := \"\"\n\tserviceValue := make(map[string]interface{})\n\n\tif mcpServer.ID > 0 {\n\t\tserviceName = mcpServer.Name\n\t\tserviceValue = services[serviceName].(map[string]interface{})\n\t} else {\n\t\tfor name, service := range services {\n\t\t\tserviceName = name\n\t\t\tserviceValue = service.(map[string]interface{})\n\t\t\tbreak\n\t\t}\n\t\tdelete(services, serviceName)\n\t}\n\tdelete(serviceValue, \"environment\")\n\tif len(environments) > 0 {\n\t\tenvMap := make(map[string]string)\n\t\tfor _, env := range environments {\n\t\t\tenvMap[env.Key] = env.Value\n\t\t}\n\t\tserviceValue[\"environment\"] = envMap\n\t}\n\tdelete(serviceValue, \"volumes\")\n\tif len(volumes) > 0 {\n\t\tvolumeList := make([]string, 0)\n\t\tfor _, volume := range volumes {\n\t\t\tvolumeList = append(volumeList, fmt.Sprintf(\"%s:%s\", volume.Source, volume.Target))\n\t\t}\n\t\tserviceValue[\"volumes\"] = volumeList\n\t}\n\tif mcpServer.Type == \"npx\" {\n\t\tserviceValue[\"image\"] = \"supercorp/supergateway:latest\"\n\t} else {\n\t\tserviceValue[\"image\"] = \"supercorp/supergateway:uvx\"\n\t}\n\n\tservices[mcpServer.Name] = serviceValue\n\tcomposeByte, err := yaml.Marshal(composeMap)\n\tif err != nil {\n\t\treturn err\n\t}\n\tmcpServer.DockerCompose = string(composeByte)\n\treturn nil\n}\n\nfunc startMcp(mcpServer *model.McpServer) {\n\tcomposePath := path.Join(global.Dir.McpDir, mcpServer.Name, \"docker-compose.yml\")\n\tif mcpServer.Status != constant.StatusNormal {\n\t\t_, _ = compose.Down(composePath)\n\t}\n\tif out, err := compose.Up(composePath); err != nil {\n\t\tmcpServer.Status = constant.StatusError\n\t\tmcpServer.Message = out\n\t} else {\n\t\tmcpServer.Status = constant.StatusRunning\n\t\tmcpServer.Message = \"\"\n\t}\n\t_ = syncMcpServerContainerStatus(mcpServer)\n}\n\nfunc syncMcpServerContainerStatus(mcpServer *model.McpServer) error {\n\tcontainerNames := []string{mcpServer.ContainerName}\n\tcli, err := docker.NewClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tcontainers, err := cli.ListContainersByName(containerNames)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(containers) == 0 {\n\t\tmcpServer.Status = constant.StatusStopped\n\t\treturn mcpServerRepo.Save(mcpServer)\n\t}\n\tcontainer := containers[0]\n\tswitch container.State {\n\tcase \"exited\":\n\t\tmcpServer.Status = constant.StatusError\n\tcase \"running\":\n\t\tmcpServer.Status = constant.StatusRunning\n\tcase \"paused\":\n\t\tmcpServer.Status = constant.StatusStopped\n\tcase \"restarting\":\n\t\tmcpServer.Status = constant.StatusRestarting\n\tdefault:\n\t\tif mcpServer.Status != constant.StatusStarting {\n\t\t\tmcpServer.Status = constant.StatusStopped\n\t\t}\n\t}\n\treturn mcpServerRepo.Save(mcpServer)\n}\n\nfunc GetWebsiteID() uint {\n\twebsiteID, _ := settingRepo.Get(settingRepo.WithByKey(\"MCP_WEBSITE_ID\"))\n\tif websiteID.Value == \"\" {\n\t\treturn 0\n\t}\n\twebsiteIDUint, _ := strconv.ParseUint(websiteID.Value, 10, 64)\n\treturn uint(websiteIDUint)\n}\n\nfunc pullImage(imageType string) {\n\tif global.CONF.Base.IsOffLine {\n\t\treturn\n\t}\n\tif imageType == \"npx\" {\n\t\tif err := docker.PullImage(\"supercorp/supergateway:latest\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"docker pull mcp image error: %s\", err.Error())\n\t\t}\n\t} else {\n\t\tif err := docker.PullImage(\"supercorp/supergateway:uvx\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"docker pull mcp image error: %s\", err.Error())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "agent/app/service/monitor.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/psutil\"\n\t\"github.com/robfig/cron/v3\"\n\t\"github.com/shirou/gopsutil/v4/cpu\"\n\t\"github.com/shirou/gopsutil/v4/disk\"\n\t\"github.com/shirou/gopsutil/v4/load\"\n\t\"github.com/shirou/gopsutil/v4/mem\"\n\t\"github.com/shirou/gopsutil/v4/net\"\n\t\"github.com/shirou/gopsutil/v4/process\"\n)\n\ntype MonitorService struct {\n\tDiskIO chan ([]disk.IOCountersStat)\n\tNetIO  chan ([]net.IOCountersStat)\n}\n\nvar monitorCancel context.CancelFunc\n\ntype IMonitorService interface {\n\tRun()\n\tLoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorData, error)\n\tLoadGPUOptions() dto.MonitorGPUOptions\n\tLoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error)\n\tLoadSetting() (*dto.MonitorSetting, error)\n\tUpdateSetting(key, value string) error\n\tCleanData() error\n\n\tsaveIODataToDB(ctx context.Context, interval float64)\n\tsaveNetDataToDB(ctx context.Context, interval float64)\n}\n\nfunc NewIMonitorService() IMonitorService {\n\treturn &MonitorService{\n\t\tDiskIO: make(chan []disk.IOCountersStat, 2),\n\t\tNetIO:  make(chan []net.IOCountersStat, 2),\n\t}\n}\n\nfunc (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorData, error) {\n\tloc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\treq.StartTime = req.StartTime.In(loc)\n\treq.EndTime = req.EndTime.In(loc)\n\n\tvar data []dto.MonitorData\n\tif req.Param == \"all\" || req.Param == \"cpu\" || req.Param == \"memory\" || req.Param == \"load\" {\n\t\tbases, err := monitorRepo.GetBase(repo.WithByCreatedAt(req.StartTime, req.EndTime))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar itemData dto.MonitorData\n\t\titemData.Param = \"base\"\n\t\tfor _, base := range bases {\n\t\t\titemData.Date = append(itemData.Date, base.CreatedAt)\n\t\t\tif req.Param == \"all\" || req.Param == \"cpu\" {\n\t\t\t\tvar processes []dto.Process\n\t\t\t\t_ = json.Unmarshal([]byte(base.TopCPU), &processes)\n\t\t\t\tbase.TopCPUItems = processes\n\t\t\t\tbase.TopCPU = \"\"\n\t\t\t}\n\t\t\tif req.Param == \"all\" || req.Param == \"mem\" {\n\t\t\t\tvar processes []dto.Process\n\t\t\t\t_ = json.Unmarshal([]byte(base.TopMem), &processes)\n\t\t\t\tbase.TopMemItems = processes\n\t\t\t\tbase.TopMem = \"\"\n\t\t\t}\n\t\t\titemData.Value = append(itemData.Value, base)\n\t\t}\n\t\tdata = append(data, itemData)\n\t}\n\tif req.Param == \"all\" || req.Param == \"io\" {\n\t\tbases, err := monitorRepo.GetIO(repo.WithByName(req.IO), repo.WithByCreatedAt(req.StartTime, req.EndTime))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar itemData dto.MonitorData\n\t\titemData.Param = \"io\"\n\t\tfor _, base := range bases {\n\t\t\titemData.Date = append(itemData.Date, base.CreatedAt)\n\t\t\titemData.Value = append(itemData.Value, base)\n\t\t}\n\t\tdata = append(data, itemData)\n\t}\n\tif req.Param == \"all\" || req.Param == \"network\" {\n\t\tbases, err := monitorRepo.GetNetwork(repo.WithByName(req.Network), repo.WithByCreatedAt(req.StartTime, req.EndTime))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar itemData dto.MonitorData\n\t\titemData.Param = \"network\"\n\t\tfor _, base := range bases {\n\t\t\titemData.Date = append(itemData.Date, base.CreatedAt)\n\t\t\titemData.Value = append(itemData.Value, base)\n\t\t}\n\t\tdata = append(data, itemData)\n\t}\n\treturn data, nil\n}\n\nfunc (m *MonitorService) LoadGPUOptions() dto.MonitorGPUOptions {\n\tvar data dto.MonitorGPUOptions\n\tgpuExist, gpuClient := gpu.New()\n\txpuExist, xpuClient := xpu.New()\n\tif !gpuExist && !xpuExist {\n\t\treturn data\n\t}\n\tif gpuExist {\n\t\tdata.GPUType = \"gpu\"\n\t\tgpuInfo, err := gpuClient.LoadGpuInfo()\n\t\tif err != nil || len(gpuInfo.GPUs) == 0 {\n\t\t\tglobal.LOG.Error(\"Load GPU info failed or no GPU found, err: \", err)\n\t\t\treturn data\n\t\t}\n\t\tsort.Slice(gpuInfo.GPUs, func(i, j int) bool {\n\t\t\treturn gpuInfo.GPUs[i].Index < gpuInfo.GPUs[j].Index\n\t\t})\n\t\tfor _, item := range gpuInfo.GPUs {\n\t\t\tvar chartHide dto.GPUChartHide\n\t\t\tchartHide.ProductName = fmt.Sprintf(\"%d - %s\", item.Index, item.ProductName)\n\t\t\tchartHide.GPU = item.GPUUtil == \"\" || item.GPUUtil == \"N/A\"\n\t\t\tif (item.MemTotal == \"\" || item.MemTotal == \"N/A\") && (item.MemUsed == \"\" || item.MemUsed == \"N/A\") {\n\t\t\t\tchartHide.Memory = true\n\t\t\t}\n\t\t\tif (item.MaxPowerLimit == \"\" || item.MaxPowerLimit == \"N/A\") && (item.PowerDraw == \"\" || item.PowerDraw == \"N/A\") {\n\t\t\t\tchartHide.Power = true\n\t\t\t}\n\t\t\tchartHide.Temperature = item.Temperature == \"\" || item.Temperature == \"N/A\"\n\t\t\tchartHide.Speed = item.FanSpeed == \"\" || item.FanSpeed == \"N/A\"\n\t\t\tdata.ChartHide = append(data.ChartHide, chartHide)\n\t\t\tdata.Options = append(data.Options, fmt.Sprintf(\"%d - %s\", item.Index, item.ProductName))\n\t\t}\n\t\treturn data\n\t} else {\n\t\tdata.GPUType = \"xpu\"\n\t\txpu, err := xpuClient.LoadGpuInfo()\n\t\tif err != nil || len(xpu.Xpu) == 0 {\n\t\t\tglobal.LOG.Error(\"Load XPU info failed or no XPU found, err: \", err)\n\t\t}\n\t\tsort.Slice(xpu.Xpu, func(i, j int) bool {\n\t\t\treturn xpu.Xpu[i].Basic.DeviceID < xpu.Xpu[j].Basic.DeviceID\n\t\t})\n\t\tfor _, item := range xpu.Xpu {\n\t\t\tvar chartHide dto.GPUChartHide\n\t\t\tchartHide.GPU = true\n\t\t\tchartHide.Speed = true\n\t\t\tchartHide.ProductName = fmt.Sprintf(\"%d - %s\", item.Basic.DeviceID, item.Basic.DeviceName)\n\t\t\tif (item.Stats.MemoryUsed == \"\" || item.Stats.MemoryUsed == \"N/A\") && (item.Basic.Memory == \"\" || item.Basic.FreeMemory == \"N/A\") {\n\t\t\t\tchartHide.Memory = true\n\t\t\t}\n\t\t\tif item.Stats.Power == \"\" || item.Stats.Power == \"N/A\" {\n\t\t\t\tchartHide.Power = true\n\t\t\t}\n\t\t\tchartHide.Temperature = item.Stats.Temperature == \"\" || item.Stats.Temperature == \"N/A\"\n\t\t\tdata.ChartHide = append(data.ChartHide, chartHide)\n\t\t\tdata.Options = append(data.Options, fmt.Sprintf(\"%d - %s\", item.Basic.DeviceID, item.Basic.DeviceName))\n\t\t}\n\t\treturn data\n\t}\n}\n\nfunc (m *MonitorService) LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error) {\n\tloc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\treq.StartTime = req.StartTime.In(loc)\n\treq.EndTime = req.EndTime.In(loc)\n\tvar data dto.MonitorGPUData\n\tgpuList, err := monitorRepo.GetGPU(repo.WithByCreatedAt(req.StartTime, req.EndTime), monitorRepo.WithByProductName(req.ProductName))\n\tif err != nil {\n\t\treturn data, err\n\t}\n\n\tfor _, gpu := range gpuList {\n\t\tdata.Date = append(data.Date, gpu.CreatedAt)\n\t\tdata.GPUValue = append(data.GPUValue, gpu.GPUUtil)\n\t\tdata.TemperatureValue = append(data.TemperatureValue, gpu.Temperature)\n\t\tdata.PowerUsed = append(data.PowerUsed, gpu.PowerDraw)\n\t\tdata.PowerTotal = append(data.PowerTotal, gpu.MaxPowerLimit)\n\t\tif gpu.MaxPowerLimit != 0 {\n\t\t\tdata.PowerPercent = append(data.PowerPercent, gpu.PowerDraw/gpu.MaxPowerLimit*100)\n\t\t} else {\n\t\t\tdata.PowerPercent = append(data.PowerPercent, float64(0))\n\t\t}\n\n\t\tdata.MemoryTotal = append(data.MemoryTotal, gpu.MemTotal)\n\t\tdata.MemoryUsed = append(data.MemoryUsed, gpu.MemUsed)\n\t\tif gpu.MemTotal != 0 {\n\t\t\tdata.MemoryPercent = append(data.MemoryPercent, gpu.MemUsed/gpu.MemTotal*100)\n\t\t} else {\n\t\t\tdata.MemoryPercent = append(data.MemoryPercent, float64(0))\n\t\t}\n\t\tvar process []dto.GPUProcess\n\t\tif err := json.Unmarshal([]byte(gpu.Processes), &process); err == nil {\n\t\t\tdata.ProcessCount = append(data.ProcessCount, len(process))\n\t\t\tdata.GPUProcesses = append(data.GPUProcesses, process)\n\t\t} else {\n\t\t\tdata.ProcessCount = append(data.ProcessCount, 0)\n\t\t\tdata.GPUProcesses = append(data.GPUProcesses, []dto.GPUProcess{})\n\t\t}\n\t\tdata.SpeedValue = append(data.SpeedValue, gpu.FanSpeed)\n\t}\n\treturn data, nil\n}\n\nfunc (m *MonitorService) LoadSetting() (*dto.MonitorSetting, error) {\n\tsetting, err := settingRepo.GetList()\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tsettingMap := make(map[string]string)\n\tfor _, set := range setting {\n\t\tsettingMap[set.Key] = set.Value\n\t}\n\tvar info dto.MonitorSetting\n\tarr, err := json.Marshal(settingMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := json.Unmarshal(arr, &info); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &info, err\n}\n\nfunc (m *MonitorService) UpdateSetting(key, value string) error {\n\tswitch key {\n\tcase \"MonitorStatus\":\n\t\tif value == constant.StatusEnable && global.MonitorCronID == 0 {\n\t\t\tinterval, err := settingRepo.Get(settingRepo.WithByKey(\"MonitorInterval\"))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := StartMonitor(false, interval.Value); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif value == constant.StatusDisable && global.MonitorCronID != 0 {\n\t\t\tmonitorCancel()\n\t\t\tglobal.Cron.Remove(cron.EntryID(global.MonitorCronID))\n\t\t\tglobal.MonitorCronID = 0\n\t\t}\n\tcase \"MonitorInterval\":\n\t\tstatus, err := settingRepo.Get(settingRepo.WithByKey(\"MonitorStatus\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif status.Value == constant.StatusEnable && global.MonitorCronID != 0 {\n\t\t\tif err := StartMonitor(true, value); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn settingRepo.Update(key, value)\n}\n\nfunc (m *MonitorService) CleanData() error {\n\tif err := global.MonitorDB.Exec(\"DELETE FROM monitor_bases\").Error; err != nil {\n\t\treturn err\n\t}\n\tif err := global.MonitorDB.Exec(\"DELETE FROM monitor_ios\").Error; err != nil {\n\t\treturn err\n\t}\n\tif err := global.MonitorDB.Exec(\"DELETE FROM monitor_networks\").Error; err != nil {\n\t\treturn err\n\t}\n\t_ = global.GPUMonitorDB.Exec(\"DELETE FROM monitor_gpus\").Error\n\treturn nil\n}\n\nfunc (m *MonitorService) Run() {\n\tsaveGPUDataToDB()\n\tsaveXPUDataToDB()\n\tvar itemModel model.MonitorBase\n\ttotalPercent, _ := cpu.Percent(3*time.Second, false)\n\tif len(totalPercent) == 1 {\n\t\titemModel.Cpu = totalPercent[0]\n\t}\n\ttopCPU := loadTopCPU()\n\tif len(topCPU) != 0 {\n\t\ttopItemCPU, err := json.Marshal(topCPU)\n\t\tif err == nil {\n\t\t\titemModel.TopCPU = string(topItemCPU)\n\t\t}\n\t}\n\tcpuCount, _ := psutil.CPUInfo.GetPhysicalCores(false)\n\tloadInfo, _ := load.Avg()\n\titemModel.CpuLoad1 = loadInfo.Load1\n\titemModel.CpuLoad5 = loadInfo.Load5\n\titemModel.CpuLoad15 = loadInfo.Load15\n\titemModel.LoadUsage = loadInfo.Load1 / (float64(cpuCount*2) * 0.75) * 100\n\n\tmemoryInfo, _ := mem.VirtualMemory()\n\titemModel.Memory = memoryInfo.UsedPercent\n\ttopMem := loadTopMem()\n\tif len(topMem) != 0 {\n\t\ttopMemItem, err := json.Marshal(topMem)\n\t\tif err == nil {\n\t\t\titemModel.TopMem = string(topMemItem)\n\t\t}\n\t}\n\n\tif err := monitorRepo.CreateMonitorBase(itemModel); err != nil {\n\t\tglobal.LOG.Errorf(\"Insert basic monitoring data failed, err: %v\", err)\n\t}\n\n\tm.loadDiskIO()\n\tm.loadNetIO()\n\n\tMonitorStoreDays, err := settingRepo.Get(settingRepo.WithByKey(\"MonitorStoreDays\"))\n\tif err != nil {\n\t\treturn\n\t}\n\tstoreDays, _ := strconv.Atoi(MonitorStoreDays.Value)\n\ttimeForDelete := time.Now().AddDate(0, 0, -storeDays)\n\t_ = monitorRepo.DelMonitorBase(timeForDelete)\n\t_ = monitorRepo.DelMonitorIO(timeForDelete)\n\t_ = monitorRepo.DelMonitorNet(timeForDelete)\n}\n\nfunc (m *MonitorService) loadDiskIO() {\n\tioStat, _ := disk.IOCounters()\n\tvar diskIOList []disk.IOCountersStat\n\tvar ioStatAll disk.IOCountersStat\n\tfor _, io := range ioStat {\n\t\tioStatAll.Name = \"all\"\n\t\tioStatAll.ReadBytes += io.ReadBytes\n\t\tioStatAll.WriteBytes += io.WriteBytes\n\t\tioStatAll.ReadTime += io.ReadTime\n\t\tioStatAll.WriteTime += io.WriteTime\n\t\tioStatAll.WriteCount += io.WriteCount\n\t\tioStatAll.ReadCount += io.ReadCount\n\t\tdiskIOList = append(diskIOList, io)\n\t}\n\tdiskIOList = append(diskIOList, ioStatAll)\n\tm.DiskIO <- diskIOList\n}\n\nfunc (m *MonitorService) loadNetIO() {\n\tnetStat, _ := net.IOCounters(true)\n\tnetStatAll, _ := net.IOCounters(false)\n\tvar netList []net.IOCountersStat\n\tnetList = append(netList, netStat...)\n\tnetList = append(netList, netStatAll...)\n\tm.NetIO <- netList\n}\n\nfunc (m *MonitorService) saveIODataToDB(ctx context.Context, interval float64) {\n\tdefer close(m.DiskIO)\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase ioStat := <-m.DiskIO:\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\t\t\tcase ioStat2 := <-m.DiskIO:\n\t\t\t\tvar ioList []model.MonitorIO\n\t\t\t\tfor _, io2 := range ioStat2 {\n\t\t\t\t\tfor _, io1 := range ioStat {\n\t\t\t\t\t\tif io2.Name == io1.Name {\n\t\t\t\t\t\t\tvar itemIO model.MonitorIO\n\t\t\t\t\t\t\titemIO.Name = io1.Name\n\t\t\t\t\t\t\tif io2.ReadBytes != 0 && io1.ReadBytes != 0 && io2.ReadBytes > io1.ReadBytes {\n\t\t\t\t\t\t\t\titemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / interval)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif io2.WriteBytes != 0 && io1.WriteBytes != 0 && io2.WriteBytes > io1.WriteBytes {\n\t\t\t\t\t\t\t\titemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / interval)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif io2.ReadCount != 0 && io1.ReadCount != 0 && io2.ReadCount > io1.ReadCount {\n\t\t\t\t\t\t\t\titemIO.Count = uint64(float64(io2.ReadCount-io1.ReadCount) / interval)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\twriteCount := uint64(0)\n\t\t\t\t\t\t\tif io2.WriteCount != 0 && io1.WriteCount != 0 && io2.WriteCount > io1.WriteCount {\n\t\t\t\t\t\t\t\twriteCount = uint64(float64(io2.WriteCount-io1.WriteCount) / interval)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif writeCount > itemIO.Count {\n\t\t\t\t\t\t\t\titemIO.Count = writeCount\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif io2.ReadTime != 0 && io1.ReadTime != 0 && io2.ReadTime > io1.ReadTime {\n\t\t\t\t\t\t\t\titemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / interval)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\twriteTime := uint64(0)\n\t\t\t\t\t\t\tif io2.WriteTime != 0 && io1.WriteTime != 0 && io2.WriteTime > io1.WriteTime {\n\t\t\t\t\t\t\t\twriteTime = uint64(float64(io2.WriteTime-io1.WriteTime) / interval)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif writeTime > itemIO.Time {\n\t\t\t\t\t\t\t\titemIO.Time = writeTime\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tioList = append(ioList, itemIO)\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_ = monitorRepo.BatchCreateMonitorIO(ioList)\n\t\t\t\tm.DiskIO <- ioStat2\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64) {\n\tdefer close(m.NetIO)\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\tcase netStat := <-m.NetIO:\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\t\t\tcase netStat2 := <-m.NetIO:\n\t\t\t\tvar netList []model.MonitorNetwork\n\t\t\t\tfor _, net2 := range netStat2 {\n\t\t\t\t\tfor _, net1 := range netStat {\n\t\t\t\t\t\tif net2.Name == net1.Name {\n\t\t\t\t\t\t\tvar itemNet model.MonitorNetwork\n\t\t\t\t\t\t\titemNet.Name = net1.Name\n\n\t\t\t\t\t\t\tif net2.BytesSent != 0 && net1.BytesSent != 0 && net2.BytesSent > net1.BytesSent {\n\t\t\t\t\t\t\t\titemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / interval\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif net2.BytesRecv != 0 && net1.BytesRecv != 0 && net2.BytesRecv > net1.BytesRecv {\n\t\t\t\t\t\t\t\titemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / interval\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnetList = append(netList, itemNet)\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t_ = monitorRepo.BatchCreateMonitorNet(netList)\n\t\t\t\tm.NetIO <- netStat2\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc loadTopCPU() []dto.Process {\n\tprocesses, err := process.Processes()\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\ttop5 := make([]dto.Process, 0, 5)\n\tfor _, p := range processes {\n\t\tpercent, err := p.CPUPercent()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tminIndex := 0\n\t\tif len(top5) >= 5 {\n\t\t\tminCPU := top5[0].Percent\n\t\t\tfor i := 1; i < len(top5); i++ {\n\t\t\t\tif top5[i].Percent < minCPU {\n\t\t\t\t\tminCPU = top5[i].Percent\n\t\t\t\t\tminIndex = i\n\t\t\t\t}\n\t\t\t}\n\t\t\tif percent < minCPU {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tname, err := p.Name()\n\t\tif err != nil {\n\t\t\tname = \"undefined\"\n\t\t}\n\t\tcmd, err := p.Cmdline()\n\t\tif err != nil {\n\t\t\tcmd = \"undefined\"\n\t\t}\n\t\tuser, err := p.Username()\n\t\tif err != nil {\n\t\t\tuser = \"undefined\"\n\t\t}\n\t\tif len(top5) == 5 {\n\t\t\ttop5[minIndex] = dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd}\n\t\t} else {\n\t\t\ttop5 = append(top5, dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd})\n\t\t}\n\t}\n\tsort.Slice(top5, func(i, j int) bool {\n\t\treturn top5[i].Percent > top5[j].Percent\n\t})\n\n\treturn top5\n}\n\nfunc loadTopMem() []dto.Process {\n\tprocesses, err := process.Processes()\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\ttop5 := make([]dto.Process, 0, 5)\n\tfor _, p := range processes {\n\t\tstat, err := p.MemoryInfo()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tmemItem := stat.RSS\n\t\tminIndex := 0\n\t\tif len(top5) >= 5 {\n\t\t\tmin := top5[0].Memory\n\t\t\tfor i := 1; i < len(top5); i++ {\n\t\t\t\tif top5[i].Memory < min {\n\t\t\t\t\tmin = top5[i].Memory\n\t\t\t\t\tminIndex = i\n\t\t\t\t}\n\t\t\t}\n\t\t\tif memItem < min {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tname, err := p.Name()\n\t\tif err != nil {\n\t\t\tname = \"undefined\"\n\t\t}\n\t\tcmd, err := p.Cmdline()\n\t\tif err != nil {\n\t\t\tcmd = \"undefined\"\n\t\t}\n\t\tuser, err := p.Username()\n\t\tif err != nil {\n\t\t\tuser = \"undefined\"\n\t\t}\n\t\tpercent, _ := p.MemoryPercent()\n\t\tif len(top5) == 5 {\n\t\t\ttop5[minIndex] = dto.Process{Percent: float64(percent), Pid: p.Pid, User: user, Name: name, Cmd: cmd, Memory: memItem}\n\t\t} else {\n\t\t\ttop5 = append(top5, dto.Process{Percent: float64(percent), Pid: p.Pid, User: user, Name: name, Cmd: cmd, Memory: memItem})\n\t\t}\n\t}\n\n\tsort.Slice(top5, func(i, j int) bool {\n\t\treturn top5[i].Memory > top5[j].Memory\n\t})\n\treturn top5\n}\n\nfunc StartMonitor(removeBefore bool, interval string) error {\n\tif removeBefore {\n\t\tmonitorCancel()\n\t\tglobal.Cron.Remove(cron.EntryID(global.MonitorCronID))\n\t}\n\tintervalItem, err := strconv.Atoi(interval)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tservice := NewIMonitorService()\n\tctx, cancel := context.WithCancel(context.Background())\n\tmonitorCancel = cancel\n\tnow := time.Now()\n\tnextMinute := now.Truncate(time.Minute).Add(time.Minute)\n\ttime.AfterFunc(time.Until(nextMinute), func() {\n\t\tmonitorID, err := global.Cron.AddJob(fmt.Sprintf(\"@every %ss\", interval), service)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tglobal.MonitorCronID = monitorID\n\t})\n\n\tservice.Run()\n\n\tgo service.saveIODataToDB(ctx, float64(intervalItem))\n\tgo service.saveNetDataToDB(ctx, float64(intervalItem))\n\n\treturn nil\n}\n\nfunc saveGPUDataToDB() {\n\texist, client := gpu.New()\n\tif !exist {\n\t\treturn\n\t}\n\tgpuInfo, err := client.LoadGpuInfo()\n\tif err != nil {\n\t\treturn\n\t}\n\tvar list []model.MonitorGPU\n\tfor _, gpuItem := range gpuInfo.GPUs {\n\t\titem := model.MonitorGPU{\n\t\t\tProductName:   fmt.Sprintf(\"%d - %s\", gpuItem.Index, gpuItem.ProductName),\n\t\t\tGPUUtil:       loadGPUInfoFloat(gpuItem.GPUUtil),\n\t\t\tTemperature:   loadGPUInfoFloat(gpuItem.Temperature),\n\t\t\tPowerDraw:     loadGPUInfoFloat(gpuItem.PowerDraw),\n\t\t\tMaxPowerLimit: loadGPUInfoFloat(gpuItem.MaxPowerLimit),\n\t\t\tMemUsed:       loadGPUInfoFloat(gpuItem.MemUsed),\n\t\t\tMemTotal:      loadGPUInfoFloat(gpuItem.MemTotal),\n\t\t\tFanSpeed:      loadGPUInfoInt(gpuItem.FanSpeed),\n\t\t}\n\t\tprocess, _ := json.Marshal(gpuItem.Processes)\n\t\tif len(process) != 0 {\n\t\t\titem.Processes = string(process)\n\t\t}\n\t\tlist = append(list, item)\n\t}\n\tif err := repo.NewIMonitorRepo().BatchCreateMonitorGPU(list); err != nil {\n\t\tglobal.LOG.Errorf(\"batch create gpu monitor data failed, err: %v\", err)\n\t\treturn\n\t}\n}\nfunc saveXPUDataToDB() {\n\texist, client := xpu.New()\n\tif !exist {\n\t\treturn\n\t}\n\txpuInfo, err := client.LoadGpuInfo()\n\tif err != nil {\n\t\treturn\n\t}\n\tvar list []model.MonitorGPU\n\tfor _, xpuItem := range xpuInfo.Xpu {\n\t\titem := model.MonitorGPU{\n\t\t\tProductName: fmt.Sprintf(\"%d - %s\", xpuItem.Basic.DeviceID, xpuItem.Basic.DeviceName),\n\t\t\tTemperature: loadGPUInfoFloat(xpuItem.Stats.Temperature),\n\t\t\tPowerDraw:   loadGPUInfoFloat(xpuItem.Stats.Power),\n\t\t\tMemUsed:     loadGPUInfoFloat(xpuItem.Stats.MemoryUsed),\n\t\t\tMemTotal:    loadGPUInfoFloat(xpuItem.Basic.Memory),\n\t\t}\n\t\tif len(xpuItem.Processes) != 0 {\n\t\t\tvar processItem []dto.GPUProcess\n\t\t\tfor _, ps := range xpuItem.Processes {\n\t\t\t\tprocessItem = append(processItem, dto.GPUProcess{\n\t\t\t\t\tPid:         fmt.Sprintf(\"%v\", ps.PID),\n\t\t\t\t\tType:        ps.SHR,\n\t\t\t\t\tProcessName: ps.Command,\n\t\t\t\t\tUsedMemory:  ps.Memory,\n\t\t\t\t})\n\t\t\t}\n\t\t\tprocess, _ := json.Marshal(processItem)\n\t\t\tif len(process) != 0 {\n\t\t\t\titem.Processes = string(process)\n\t\t\t}\n\t\t}\n\t\tlist = append(list, item)\n\t}\n\tif err := repo.NewIMonitorRepo().BatchCreateMonitorGPU(list); err != nil {\n\t\tglobal.LOG.Errorf(\"batch create gpu monitor data failed, err: %v\", err)\n\t\treturn\n\t}\n}\nfunc loadGPUInfoInt(val string) int {\n\tval = strings.TrimSuffix(val, \"%\")\n\tval = strings.TrimSpace(val)\n\tdata, _ := strconv.Atoi(val)\n\treturn data\n}\nfunc loadGPUInfoFloat(val string) float64 {\n\tval = strings.TrimSpace(val)\n\tsuffixes := []string{\"W\", \"MB\", \"MiB\", \"°C\", \"C\", \"%\"}\n\tfor _, suffix := range suffixes {\n\t\tval = strings.TrimSuffix(val, suffix)\n\t}\n\tval = strings.TrimSpace(val)\n\tdata, _ := strconv.ParseFloat(val, 64)\n\treturn data\n}\n"
  },
  {
    "path": "agent/app/service/nginx.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/subosito/gotenv\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\ntype NginxService struct {\n}\n\ntype INginxService interface {\n\tGetNginxConfig() (*response.NginxFile, error)\n\tGetConfigByScope(req request.NginxScopeReq) ([]response.NginxParam, error)\n\tUpdateConfigByScope(req request.NginxConfigUpdate) error\n\tGetStatus() (response.NginxStatus, error)\n\tUpdateConfigFile(req request.NginxConfigFileUpdate) error\n\n\tBuild(req request.NginxBuildReq) error\n\tGetModules() (*response.NginxBuildConfig, error)\n\tUpdateModule(req request.NginxModuleUpdate) error\n\n\tOperateDefaultHTTPs(req request.NginxDefaultHTTPSUpdate) error\n\tGetDefaultHttpsStatus() (*response.NginxConfigRes, error)\n}\n\nfunc NewINginxService() INginxService {\n\treturn &NginxService{}\n}\n\nfunc (n NginxService) GetNginxConfig() (*response.NginxFile, error) {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfigPath := path.Join(global.Dir.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, \"conf\", \"nginx.conf\")\n\tbyteContent, err := files.NewFileOp().GetContent(configPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &response.NginxFile{Content: string(byteContent)}, nil\n}\n\nfunc (n NginxService) GetConfigByScope(req request.NginxScopeReq) ([]response.NginxParam, error) {\n\tkeys, ok := dto.ScopeKeyMap[req.Scope]\n\tif !ok || len(keys) == 0 {\n\t\treturn nil, nil\n\t}\n\treturn getNginxParamsByKeys(constant.NginxScopeHttp, keys, nil)\n}\n\nfunc (n NginxService) UpdateConfigByScope(req request.NginxConfigUpdate) error {\n\tkeys, ok := dto.ScopeKeyMap[req.Scope]\n\tif !ok || len(keys) == 0 {\n\t\treturn nil\n\t}\n\treturn updateNginxConfig(constant.NginxScopeHttp, getNginxParams(req.Params, keys), nil)\n}\n\nfunc (n NginxService) GetStatus() (response.NginxStatus, error) {\n\thttpPort, _, err := getAppInstallPort(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn response.NginxStatus{}, err\n\t}\n\turl := \"http://127.0.0.1/nginx_status\"\n\tif httpPort != 80 {\n\t\turl = fmt.Sprintf(\"http://127.0.0.1:%v/nginx_status\", httpPort)\n\t}\n\tres, err := http.Get(url)\n\tif err != nil || res.StatusCode > 300 {\n\t\treturn response.NginxStatus{}, err\n\t}\n\tdefer res.Body.Close()\n\tcontent, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn response.NginxStatus{}, err\n\t}\n\tvar status response.NginxStatus\n\tresArray := strings.Split(string(content), \" \")\n\tactive, err := strconv.Atoi(resArray[2])\n\tif err == nil {\n\t\tstatus.Active = active\n\t}\n\taccepts, err := strconv.Atoi(resArray[7])\n\tif err == nil {\n\t\tstatus.Accepts = accepts\n\t}\n\thandled, err := strconv.Atoi(resArray[8])\n\tif err == nil {\n\t\tstatus.Handled = handled\n\t}\n\trequests, err := strconv.Atoi(resArray[9])\n\tif err == nil {\n\t\tstatus.Requests = requests\n\t}\n\treading, err := strconv.Atoi(resArray[11])\n\tif err == nil {\n\t\tstatus.Reading = reading\n\t}\n\twriting, err := strconv.Atoi(resArray[13])\n\tif err == nil {\n\t\tstatus.Writing = writing\n\t}\n\twaiting, err := strconv.Atoi(resArray[15])\n\tif err == nil {\n\t\tstatus.Waiting = waiting\n\t}\n\treturn status, nil\n}\n\nfunc (n NginxService) UpdateConfigFile(req request.NginxConfigFileUpdate) error {\n\tfileOp := files.NewFileOp()\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfilePath := path.Join(global.Dir.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, \"conf\", \"nginx.conf\")\n\tif req.Backup {\n\t\tbackupPath := path.Join(path.Dir(filePath), \"bak\")\n\t\tif !fileOp.Stat(backupPath) {\n\t\t\tif err := fileOp.CreateDir(backupPath, constant.DirPerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tnewFile := path.Join(backupPath, \"nginx.bak\"+\"-\"+time.Now().Format(\"2006-01-02-15-04-05\"))\n\t\tif err := fileOp.Copy(filePath, backupPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := fileOp.Rename(path.Join(backupPath, \"nginx.conf\"), newFile); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\toldContent, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = fileOp.WriteFile(filePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\tif status, err := checkContainerStatus(nginxInstall.ContainerName); err == nil && status != \"running\" {\n\t\tif out, err := compose.DownAndUp(nginxInstall.GetComposePath()); err != nil {\n\t\t\t_ = fileOp.SaveFile(filePath, string(oldContent), constant.DirPerm)\n\t\t\treturn fmt.Errorf(\"nginx restart failed: %v\", out)\n\t\t} else {\n\t\t\treturn nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName)\n\t\t}\n\t}\n\treturn nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName)\n}\n\nfunc (n NginxService) Build(req request.NginxBuildReq) error {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttaskName := task.GetTaskName(nginxInstall.Name, task.TaskBuild, task.TaskScopeApp)\n\tif err = task.CheckTaskIsExecuting(taskName); err != nil {\n\t\treturn err\n\t}\n\tfileOp := files.NewFileOp()\n\tbuildPath := path.Join(nginxInstall.GetPath(), \"build\")\n\tif !fileOp.Stat(buildPath) {\n\t\treturn buserr.New(\"ErrBuildDirNotFound\")\n\t}\n\tmoduleConfigPath := path.Join(buildPath, \"module.json\")\n\tmoduleContent, err := fileOp.GetContent(moduleConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar (\n\t\tmodules         []dto.NginxModule\n\t\taddModuleParams []string\n\t\taddPackages     []string\n\t)\n\tif len(moduleContent) > 0 {\n\t\t_ = json.Unmarshal(moduleContent, &modules)\n\t\tbashFile, err := os.OpenFile(path.Join(buildPath, \"tmp\", \"pre.sh\"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.DirPerm)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer bashFile.Close()\n\t\tbashFileWriter := bufio.NewWriter(bashFile)\n\t\tfor _, module := range modules {\n\t\t\tif !module.Enable {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_, err = bashFileWriter.WriteString(module.Script + \"\\n\")\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\taddModuleParams = append(addModuleParams, module.Params)\n\t\t\taddPackages = append(addPackages, module.Packages...)\n\t\t}\n\t\terr = bashFileWriter.Flush()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tenvs, err := gotenv.Read(nginxInstall.GetEnvPath())\n\tif err != nil {\n\t\treturn err\n\t}\n\tenvs[\"CONTAINER_PACKAGE_URL\"] = req.Mirror\n\tenvs[\"RESTY_CONFIG_OPTIONS_MORE\"] = \"\"\n\tenvs[\"RESTY_ADD_PACKAGE_BUILDDEPS\"] = \"\"\n\tif len(addModuleParams) > 0 {\n\t\tenvs[\"RESTY_CONFIG_OPTIONS_MORE\"] = strings.Join(addModuleParams, \" \")\n\t}\n\tif len(addPackages) > 0 {\n\t\tenvs[\"RESTY_ADD_PACKAGE_BUILDDEPS\"] = strings.Join(addPackages, \" \")\n\t}\n\t_ = gotenv.Write(envs, nginxInstall.GetEnvPath())\n\n\tbuildTask, err := task.NewTaskWithOps(nginxInstall.Name, task.TaskBuild, task.TaskScopeApp, req.TaskID, nginxInstall.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbuildTask.AddSubTaskWithOps(\"\", func(t *task.Task) error {\n\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTask(*buildTask), cmd.WithTimeout(120*time.Minute))\n\t\tif err = cmdMgr.RunBashCf(\"docker compose -f %s build\", nginxInstall.GetComposePath()); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = compose.DownAndUp(nginxInstall.GetComposePath())\n\t\treturn err\n\t}, nil, 0, 120*time.Minute)\n\n\tgo func() {\n\t\t_ = buildTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (n NginxService) GetModules() (*response.NginxBuildConfig, error) {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfileOp := files.NewFileOp()\n\tvar modules []dto.NginxModule\n\tmoduleConfigPath := path.Join(nginxInstall.GetPath(), \"build\", \"module.json\")\n\tif !fileOp.Stat(moduleConfigPath) {\n\t\treturn nil, nil\n\t}\n\tmoduleContent, err := fileOp.GetContent(moduleConfigPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(moduleContent) > 0 {\n\t\t_ = json.Unmarshal(moduleContent, &modules)\n\t}\n\tvar resList []response.NginxModule\n\tfor _, module := range modules {\n\t\tresList = append(resList, response.NginxModule{\n\t\t\tName:     module.Name,\n\t\t\tScript:   module.Script,\n\t\t\tPackages: strings.Join(module.Packages, \",\"),\n\t\t\tParams:   module.Params,\n\t\t\tEnable:   module.Enable,\n\t\t})\n\t}\n\tenvs, err := gotenv.Read(nginxInstall.GetEnvPath())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &response.NginxBuildConfig{\n\t\tMirror:  envs[\"CONTAINER_PACKAGE_URL\"],\n\t\tModules: resList,\n\t}, nil\n}\n\nfunc (n NginxService) UpdateModule(req request.NginxModuleUpdate) error {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfileOp := files.NewFileOp()\n\tvar (\n\t\tmodules []dto.NginxModule\n\t)\n\tmoduleConfigPath := path.Join(nginxInstall.GetPath(), \"build\", \"module.json\")\n\tif !fileOp.Stat(moduleConfigPath) {\n\t\t_ = fileOp.CreateFile(moduleConfigPath)\n\t}\n\tmoduleContent, err := fileOp.GetContent(moduleConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(moduleContent) > 0 {\n\t\t_ = json.Unmarshal(moduleContent, &modules)\n\t}\n\n\tswitch req.Operate {\n\tcase \"create\":\n\t\tfor _, module := range modules {\n\t\t\tif module.Name == req.Name {\n\t\t\t\treturn buserr.New(\"ErrNameIsExist\")\n\t\t\t}\n\t\t}\n\t\tmodules = append(modules, dto.NginxModule{\n\t\t\tName:     req.Name,\n\t\t\tScript:   req.Script,\n\t\t\tPackages: strings.Split(req.Packages, \",\"),\n\t\t\tParams:   req.Params,\n\t\t\tEnable:   true,\n\t\t})\n\tcase \"update\":\n\t\tfor i, module := range modules {\n\t\t\tif module.Name == req.Name {\n\t\t\t\tmodules[i].Script = req.Script\n\t\t\t\tmodules[i].Packages = strings.Split(req.Packages, \",\")\n\t\t\t\tmodules[i].Params = req.Params\n\t\t\t\tmodules[i].Enable = req.Enable\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\tcase \"delete\":\n\t\tfor i, module := range modules {\n\t\t\tif module.Name == req.Name {\n\t\t\t\tmodules = append(modules[:i], modules[i+1:]...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tmoduleByte, err := json.Marshal(modules)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn fileOp.SaveFileWithByte(moduleConfigPath, moduleByte, constant.DirPerm)\n}\n\nfunc (n NginxService) OperateDefaultHTTPs(req request.NginxDefaultHTTPSUpdate) error {\n\tappInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\twebsites, _ := websiteRepo.List()\n\thasDefaultWebsite := false\n\tfor _, website := range websites {\n\t\tif website.DefaultServer {\n\t\t\thasDefaultWebsite = true\n\t\t\tbreak\n\t\t}\n\t}\n\tdefaultConfigPath := path.Join(appInstall.GetPath(), \"conf\", \"default\", \"00.default.conf\")\n\tcontent, err := os.ReadFile(defaultConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tswitch req.Operate {\n\tcase \"enable\":\n\t\tif req.SSLRejectHandshake {\n\t\t\tdefaultWebsite, _ := websiteRepo.GetFirst(websiteRepo.WithDefaultServer())\n\t\t\tif defaultWebsite.ID > 0 {\n\t\t\t\treturn buserr.New(\"ErrDefaultWebsite\")\n\t\t\t}\n\t\t}\n\t\tif err := handleSSLConfig(&appInstall, hasDefaultWebsite, req.SSLRejectHandshake); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"disable\":\n\t\tdefaultConfig, err := parser.NewStringParser(string(content)).Parse()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefaultConfig.FilePath = defaultConfigPath\n\t\tdefaultServer := defaultConfig.FindServers()[0]\n\t\tdefaultServer.RemoveListen(fmt.Sprintf(\"%d\", appInstall.HttpsPort))\n\t\tdefaultServer.RemoveListen(fmt.Sprintf(\"[::]:%d\", appInstall.HttpsPort))\n\t\tdefaultServer.RemoveDirective(\"include\", []string{\"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf\"})\n\t\tdefaultServer.RemoveDirective(\"http2\", []string{\"on\"})\n\t\tdefaultServer.RemoveDirective(\"ssl_reject_handshake\", []string{\"on\"})\n\t\tif err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nginxCheckAndReload(string(content), defaultConfigPath, appInstall.ContainerName)\n}\n\nfunc (n NginxService) GetDefaultHttpsStatus() (*response.NginxConfigRes, error) {\n\tappInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefaultConfigPath := path.Join(appInstall.GetPath(), \"conf\", \"default\", \"00.default.conf\")\n\tcontent, err := os.ReadFile(defaultConfigPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefaultConfig, err := parser.NewStringParser(string(content)).Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefaultConfig.FilePath = defaultConfigPath\n\tdefaultServer := defaultConfig.FindServers()[0]\n\tres := &response.NginxConfigRes{}\n\tfor _, directive := range defaultServer.GetDirectives() {\n\t\tif directive.GetName() == \"include\" && directive.GetParameters()[0] == \"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf\" {\n\t\t\tres.Https = true\n\t\t}\n\t\tif directive.GetName() == \"ssl_reject_handshake\" && directive.GetParameters()[0] == \"on\" {\n\t\t\tres.Https = true\n\t\t\tres.SSLRejectHandshake = true\n\t\t}\n\t}\n\treturn res, nil\n}\n"
  },
  {
    "path": "agent/app/service/nginx_utils.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n)\n\nfunc getNginxFull(website *model.Website) (dto.NginxFull, error) {\n\tvar nginxFull dto.NginxFull\n\tnginxInstall, err := getAppInstallByKey(\"openresty\")\n\tif err != nil {\n\t\treturn nginxFull, err\n\t}\n\tnginxFull.Install = nginxInstall\n\tnginxFull.Dir = path.Join(global.Dir.AppInstallDir, constant.AppOpenresty, nginxInstall.Name)\n\tnginxFull.ConfigDir = path.Join(nginxFull.Dir, \"conf\")\n\tnginxFull.ConfigFile = \"nginx.conf\"\n\tnginxFull.SiteDir = path.Join(nginxFull.Dir, \"www\")\n\n\tvar nginxConfig dto.NginxConfig\n\tnginxConfig.FilePath = path.Join(nginxFull.Dir, \"conf\", \"nginx.conf\")\n\tcontent, err := os.ReadFile(path.Join(nginxFull.ConfigDir, nginxFull.ConfigFile))\n\tif err != nil {\n\t\treturn nginxFull, err\n\t}\n\tconfig, err := parser.NewStringParser(string(content)).Parse()\n\tif err != nil {\n\t\treturn dto.NginxFull{}, err\n\t}\n\tconfig.FilePath = nginxConfig.FilePath\n\tnginxConfig.OldContent = string(content)\n\tnginxConfig.Config = config\n\n\tnginxFull.RootConfig = nginxConfig\n\n\tif website != nil {\n\t\tnginxFull.Website = *website\n\t\tvar siteNginxConfig dto.NginxConfig\n\t\tsiteConfigPath := GetWebsiteConfigPath(*website)\n\t\tsiteNginxConfig.FilePath = siteConfigPath\n\t\tsiteNginxContent, err := os.ReadFile(siteConfigPath)\n\t\tif err != nil {\n\t\t\treturn nginxFull, err\n\t\t}\n\t\tsiteConfig, err := parser.NewStringParser(string(siteNginxContent)).Parse()\n\t\tif err != nil {\n\t\t\treturn dto.NginxFull{}, err\n\t\t}\n\t\tsiteConfig.FilePath = siteConfigPath\n\t\tsiteNginxConfig.Config = siteConfig\n\t\tsiteNginxConfig.OldContent = string(siteNginxContent)\n\t\tnginxFull.SiteConfig = siteNginxConfig\n\t}\n\n\treturn nginxFull, nil\n}\n\nfunc getNginxParamsByKeys(scope string, keys []string, website *model.Website) ([]response.NginxParam, error) {\n\tnginxFull, err := getNginxFull(website)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []response.NginxParam\n\tvar block components.IBlock\n\tif scope == constant.NginxScopeHttp {\n\t\tblock = nginxFull.RootConfig.Config.FindHttp()\n\t} else {\n\t\tblock = nginxFull.SiteConfig.Config.FindServers()[0]\n\t}\n\tfor _, key := range keys {\n\t\tdirs := block.FindDirectives(key)\n\t\tfor _, dir := range dirs {\n\t\t\tnginxParam := response.NginxParam{\n\t\t\t\tName:   dir.GetName(),\n\t\t\t\tParams: dir.GetParameters(),\n\t\t\t}\n\t\t\tres = append(res, nginxParam)\n\t\t}\n\t\tif len(dirs) == 0 {\n\t\t\tnginxParam := response.NginxParam{\n\t\t\t\tName:   key,\n\t\t\t\tParams: []string{},\n\t\t\t}\n\t\t\tres = append(res, nginxParam)\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc updateDefaultServerConfig(enable bool) error {\n\tnginxInstall, err := getAppInstallByKey(\"openresty\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefaultConfigPath := path.Join(nginxInstall.GetPath(), \"conf\", \"default\", \"00.default.conf\")\n\tcontent, err := os.ReadFile(defaultConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefaultConfig, err := parser.NewStringParser(string(content)).Parse()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefaultConfig.FilePath = defaultConfigPath\n\tdefaultServer := defaultConfig.FindServers()[0]\n\n\tincludeSSL := false\n\tfor _, dir := range defaultServer.GetDirectives() {\n\t\tif dir.GetName() == \"ssl_reject_handshake\" && dir.GetParameters()[0] == \"on\" {\n\t\t\tdefaultServer.RemoveDirective(\"ssl_reject_handshake\", []string{\"on\"})\n\t\t}\n\t\tif dir.GetName() == \"include\" && dir.GetParameters()[0] == \"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf\" {\n\t\t\tincludeSSL = true\n\t\t}\n\t}\n\tupdateDefaultServer(defaultServer, nginxInstall.HttpPort, nginxInstall.HttpsPort, enable, includeSSL)\n\n\tif err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(string(content), defaultConfigPath, nginxInstall.ContainerName)\n}\n\nfunc updateNginxConfig(scope string, params []dto.NginxParam, website *model.Website) error {\n\tnginxFull, err := getNginxFull(website)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar block components.IBlock\n\tvar config dto.NginxConfig\n\tif scope == constant.NginxScopeHttp {\n\t\tconfig = nginxFull.RootConfig\n\t\tblock = nginxFull.RootConfig.Config.FindHttp()\n\t} else if scope == constant.NginxScopeServer {\n\t\tconfig = nginxFull.SiteConfig\n\t\tblock = nginxFull.SiteConfig.Config.FindServers()[0]\n\t} else {\n\t\tconfig = nginxFull.SiteConfig\n\t\tblock = config.Config.Block\n\t}\n\n\tfor _, p := range params {\n\t\tif p.UpdateScope == constant.NginxScopeOut {\n\t\t\tconfig.Config.UpdateDirective(p.Name, p.Params)\n\t\t} else {\n\t\t\tblock.UpdateDirective(p.Name, p.Params)\n\t\t}\n\t}\n\tif err := nginx.WriteConfig(config.Config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(config.OldContent, config.FilePath, nginxFull.Install.ContainerName)\n}\n\nfunc deleteNginxConfig(scope string, params []dto.NginxParam, website *model.Website) error {\n\tnginxFull, err := getNginxFull(website)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar block components.IBlock\n\tvar config dto.NginxConfig\n\tif scope == constant.NginxScopeHttp {\n\t\tconfig = nginxFull.RootConfig\n\t\tblock = nginxFull.RootConfig.Config.FindHttp()\n\t} else if scope == constant.NginxScopeServer {\n\t\tconfig = nginxFull.SiteConfig\n\t\tblock = nginxFull.SiteConfig.Config.FindServers()[0]\n\t} else {\n\t\tconfig = nginxFull.SiteConfig\n\t\tblock = config.Config.Block\n\t}\n\n\tfor _, param := range params {\n\t\tblock.RemoveDirective(param.Name, param.Params)\n\t}\n\n\tif err := nginx.WriteConfig(config.Config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(config.OldContent, config.FilePath, nginxFull.Install.ContainerName)\n}\n\nfunc getNginxParamsFromStaticFile(scope dto.NginxKey, newParams []dto.NginxParam) []dto.NginxParam {\n\tvar (\n\t\tnewConfig = &components.Config{}\n\t\terr       error\n\t)\n\n\tupdateScope := \"in\"\n\tswitch scope {\n\tcase dto.SSL:\n\t\tnewConfig, err = parser.NewStringParser(string(nginx_conf.SSL)).Parse()\n\tcase dto.CACHE:\n\t\tnewConfig, err = parser.NewStringParser(string(nginx_conf.Cache)).Parse()\n\tcase dto.ProxyCache:\n\t\tnewConfig, err = parser.NewStringParser(string(nginx_conf.ProxyCache)).Parse()\n\t}\n\tif err != nil {\n\t\treturn nil\n\t}\n\tfor _, dir := range newConfig.GetDirectives() {\n\t\taddParam := dto.NginxParam{\n\t\t\tName:        dir.GetName(),\n\t\t\tParams:      dir.GetParameters(),\n\t\t\tUpdateScope: updateScope,\n\t\t}\n\t\tisExist := false\n\t\tfor _, newParam := range newParams {\n\t\t\tif newParam.Name == dir.GetName() {\n\t\t\t\tif components.IsRepeatKey(newParam.Name) {\n\t\t\t\t\tif len(newParam.Params) > 0 && newParam.Params[0] == dir.GetParameters()[0] {\n\t\t\t\t\t\tisExist = true\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tisExist = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !isExist {\n\t\t\tnewParams = append(newParams, addParam)\n\t\t}\n\t}\n\treturn newParams\n}\n\nfunc opNginx(containerName, operate string) error {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))\n\tcmdStr := fmt.Sprintf(\"docker exec -i %s nginx \", containerName)\n\tif operate == constant.NginxCheck {\n\t\tcmdStr = cmdStr + \"-t\"\n\t} else {\n\t\tcmdStr = cmdStr + \"-s reload\"\n\t}\n\tif err := cmdMgr.RunBashC(cmdStr); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc nginxCheckAndReload(oldContent string, filePath string, containerName string) error {\n\tif err := opNginx(containerName, constant.NginxCheck); err != nil {\n\t\t_ = files.NewFileOp().WriteFile(filePath, strings.NewReader(oldContent), constant.DirPerm)\n\t\treturn err\n\t}\n\tif err := opNginx(containerName, constant.NginxReload); err != nil {\n\t\t_ = files.NewFileOp().WriteFile(filePath, strings.NewReader(oldContent), constant.DirPerm)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc updateDefaultServer(server *components.Server, httpPort int, httpsPort int, defaultServer bool, ssl bool) {\n\tserver.UpdateListen(fmt.Sprintf(\"%d\", httpPort), defaultServer)\n\tserver.UpdateListen(fmt.Sprintf(\"[::]:%d\", httpPort), defaultServer)\n\tif ssl {\n\t\tserver.UpdateListen(fmt.Sprintf(\"%d\", httpsPort), defaultServer, \"ssl\")\n\t\tserver.UpdateListen(fmt.Sprintf(\"[::]:%d\", httpsPort), defaultServer, \"ssl\")\n\t\tserver.UpdateListen(fmt.Sprintf(\"%d\", httpsPort), defaultServer, \"quic\", \"reuseport\")\n\t\tserver.UpdateListen(fmt.Sprintf(\"[::]:%d\", httpsPort), defaultServer, \"quic\", \"reuseport\")\n\t}\n}\n"
  },
  {
    "path": "agent/app/service/php_extensions.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n)\n\ntype PHPExtensionsService struct {\n}\n\ntype IPHPExtensionsService interface {\n\tPage(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error)\n\tList() ([]response.PHPExtensionsDTO, error)\n\tCreate(req request.PHPExtensionsCreate) error\n\tUpdate(req request.PHPExtensionsUpdate) error\n\tDelete(req request.PHPExtensionsDelete) error\n}\n\nfunc NewIPHPExtensionsService() IPHPExtensionsService {\n\treturn &PHPExtensionsService{}\n}\n\nfunc (p PHPExtensionsService) Page(req request.PHPExtensionsSearch) (int64, []response.PHPExtensionsDTO, error) {\n\tvar (\n\t\ttotal      int64\n\t\textensions []model.PHPExtensions\n\t\terr        error\n\t\tresult     []response.PHPExtensionsDTO\n\t)\n\ttotal, extensions, err = phpExtensionsRepo.Page(req.Page, req.PageSize)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tfor _, extension := range extensions {\n\t\tresult = append(result, response.PHPExtensionsDTO{\n\t\t\tPHPExtensions: extension,\n\t\t})\n\t}\n\treturn total, result, nil\n}\n\nfunc (p PHPExtensionsService) List() ([]response.PHPExtensionsDTO, error) {\n\tvar (\n\t\textensions []model.PHPExtensions\n\t\terr        error\n\t\tresult     []response.PHPExtensionsDTO\n\t)\n\textensions, err = phpExtensionsRepo.List()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, extension := range extensions {\n\t\tresult = append(result, response.PHPExtensionsDTO{\n\t\t\tPHPExtensions: extension,\n\t\t})\n\t}\n\treturn result, nil\n}\n\nfunc (p PHPExtensionsService) Create(req request.PHPExtensionsCreate) error {\n\texist, _ := phpExtensionsRepo.GetFirst(repo.WithByName(req.Name))\n\tif exist.ID > 0 {\n\t\treturn buserr.New(\"ErrNameIsExist\")\n\t}\n\textension := model.PHPExtensions{\n\t\tName:       req.Name,\n\t\tExtensions: req.Extensions,\n\t}\n\treturn phpExtensionsRepo.Create(&extension)\n}\n\nfunc (p PHPExtensionsService) Update(req request.PHPExtensionsUpdate) error {\n\texist, err := phpExtensionsRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\texist.Extensions = req.Extensions\n\treturn phpExtensionsRepo.Save(&exist)\n}\n\nfunc (p PHPExtensionsService) Delete(req request.PHPExtensionsDelete) error {\n\treturn phpExtensionsRepo.DeleteBy(repo.WithByID(req.ID))\n}\n"
  },
  {
    "path": "agent/app/service/process.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/websocket\"\n\t\"github.com/shirou/gopsutil/v4/net\"\n\t\"github.com/shirou/gopsutil/v4/process\"\n)\n\ntype ProcessService struct{}\n\ntype IProcessService interface {\n\tStopProcess(req request.ProcessReq) error\n\tGetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error)\n\tGetListeningProcess(c context.Context) ([]ListeningProcess, error)\n}\n\nfunc NewIProcessService() IProcessService {\n\treturn &ProcessService{}\n}\n\nfunc (ps *ProcessService) StopProcess(req request.ProcessReq) error {\n\tproc, err := process.NewProcess(req.PID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := proc.Kill(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\ntype ListeningProcess struct {\n\tPID      int32\n\tPort     map[uint32]struct{}\n\tProtocol uint32\n\tName     string\n}\n\nfunc (ps *ProcessService) GetListeningProcess(c context.Context) ([]ListeningProcess, error) {\n\tconn, err := net.ConnectionsMaxWithContext(c, \"inet\", 32768)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tprocCache := make(map[int32]ListeningProcess, 64)\n\n\tfor _, conn := range conn {\n\t\tif conn.Pid == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif (conn.Status == \"LISTEN\" && conn.Type == syscall.SOCK_STREAM) || (conn.Type == syscall.SOCK_DGRAM && conn.Raddr.Port == 0) {\n\t\t\tif _, exists := procCache[conn.Pid]; !exists {\n\t\t\t\tproc, err := process.NewProcess(conn.Pid)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tprocData := ListeningProcess{\n\t\t\t\t\tPID: conn.Pid,\n\t\t\t\t}\n\t\t\t\tprocData.Name, _ = proc.Name()\n\t\t\t\tprocData.Port = make(map[uint32]struct{})\n\t\t\t\tprocData.Port[conn.Laddr.Port] = struct{}{}\n\t\t\t\tprocData.Protocol = conn.Type\n\t\t\t\tprocCache[conn.Pid] = procData\n\t\t\t} else {\n\t\t\t\tp := procCache[conn.Pid]\n\t\t\t\tp.Port[conn.Laddr.Port] = struct{}{}\n\t\t\t\tprocCache[conn.Pid] = p\n\t\t\t}\n\t\t}\n\t}\n\n\tprocs := make([]ListeningProcess, 0, len(procCache))\n\tfor _, proc := range procCache {\n\t\tprocs = append(procs, proc)\n\t}\n\n\treturn procs, nil\n}\n\nfunc (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error) {\n\tp, err := process.NewProcess(pid)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get process info by pid %v: %v\", pid, err)\n\t}\n\n\texists, err := p.IsRunning()\n\tif err != nil || !exists {\n\t\treturn nil, fmt.Errorf(\"process %v is not running\", pid)\n\t}\n\n\tdata := &websocket.PsProcessData{\n\t\tPID: pid,\n\t}\n\n\tif name, err := p.Name(); err == nil {\n\t\tdata.Name = name\n\t}\n\n\tif ppid, err := p.Ppid(); err == nil {\n\t\tdata.PPID = ppid\n\t}\n\n\tif username, err := p.Username(); err == nil {\n\t\tdata.Username = username\n\t}\n\n\tif status, err := p.Status(); err == nil {\n\t\tif len(status) > 0 {\n\t\t\tdata.Status = status[0]\n\t\t}\n\t}\n\n\tif createTime, err := p.CreateTime(); err == nil {\n\t\tdata.StartTime = time.Unix(createTime/1000, 0).Format(\"2006-01-02 15:04:05\")\n\t}\n\n\tif numThreads, err := p.NumThreads(); err == nil {\n\t\tdata.NumThreads = numThreads\n\t}\n\n\tif connections, err := p.Connections(); err == nil {\n\t\tdata.NumConnections = len(connections)\n\n\t\tvar connects []websocket.ProcessConnect\n\t\tfor _, conn := range connections {\n\t\t\tpc := websocket.ProcessConnect{\n\t\t\t\tStatus: conn.Status,\n\t\t\t\tLaddr:  conn.Laddr,\n\t\t\t\tRaddr:  conn.Raddr,\n\t\t\t\tPID:    pid,\n\t\t\t\tName:   data.Name,\n\t\t\t}\n\t\t\tconnects = append(connects, pc)\n\t\t}\n\t\tdata.Connects = connects\n\t}\n\n\tif cpuPercent, err := p.CPUPercent(); err == nil {\n\t\tdata.CpuValue = cpuPercent\n\t\tdata.CpuPercent = fmt.Sprintf(\"%.2f%%\", cpuPercent)\n\t}\n\n\tif ioCounters, err := p.IOCounters(); err == nil {\n\t\tdata.DiskRead = common.FormatBytes(ioCounters.ReadBytes)\n\t\tdata.DiskWrite = common.FormatBytes(ioCounters.WriteBytes)\n\t}\n\n\tif cmdline, err := p.Cmdline(); err == nil {\n\t\tdata.CmdLine = cmdline\n\t}\n\n\tif memDetail, err := getMemoryDetail(p.Pid); err == nil {\n\t\tdata.Rss = common.FormatBytes(memDetail.RSS)\n\t\tdata.VMS = common.FormatBytes(memDetail.VMS)\n\t\tdata.HWM = common.FormatBytes(memDetail.HWM)\n\t\tdata.Data = common.FormatBytes(memDetail.Data)\n\t\tdata.Stack = common.FormatBytes(memDetail.Stack)\n\t\tdata.Locked = common.FormatBytes(memDetail.Locked)\n\t\tdata.Swap = common.FormatBytes(memDetail.Swap)\n\t\tdata.Dirty = common.FormatBytes(memDetail.Dirty)\n\t\tdata.RssValue = memDetail.RSS\n\t\tdata.PSS = common.FormatBytes(memDetail.PSS)\n\t\tdata.USS = common.FormatBytes(memDetail.USS)\n\t\tdata.Shared = common.FormatBytes(memDetail.Shared)\n\t\tdata.Text = common.FormatBytes(memDetail.Text)\n\t}\n\n\tif envs, err := p.Environ(); err == nil {\n\t\tdata.Envs = envs\n\t}\n\n\tif openFiles, err := p.OpenFiles(); err == nil {\n\t\tdata.OpenFiles = openFiles\n\t}\n\n\treturn data, nil\n}\n\ntype MemoryDetail struct {\n\tRSS    uint64\n\tVMS    uint64\n\tHWM    uint64\n\tData   uint64\n\tStack  uint64\n\tLocked uint64\n\tSwap   uint64\n\n\tPSS    uint64\n\tUSS    uint64\n\tShared uint64\n\tText   uint64\n\tDirty  uint64\n}\n\nfunc getMemoryDetail(pid int32) (*MemoryDetail, error) {\n\tmem := &MemoryDetail{}\n\n\tif err := readStatus(pid, mem); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := readSmapsRollup(pid, mem); err != nil {\n\t\tif err := readSmaps(pid, mem); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn mem, nil\n}\n\nfunc readStatus(pid int32, mem *MemoryDetail) error {\n\tfilePath := fmt.Sprintf(\"/proc/%d/status\", pid)\n\tfile, err := os.Open(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\tscanner := bufio.NewScanner(file)\n\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tfields := strings.Fields(line)\n\t\tif len(fields) < 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tkey := strings.TrimSuffix(fields[0], \":\")\n\t\tvalue, _ := strconv.ParseUint(fields[1], 10, 64)\n\t\tvalue *= 1024\n\n\t\tswitch key {\n\t\tcase \"VmRSS\":\n\t\t\tmem.RSS = value\n\t\tcase \"VmSize\":\n\t\t\tmem.VMS = value\n\t\tcase \"VmData\":\n\t\t\tmem.Data = value\n\t\tcase \"VmSwap\":\n\t\t\tmem.Swap = value\n\t\tcase \"VmExe\":\n\t\t\tmem.Text = value\n\t\tcase \"RssShmem\":\n\t\t\tmem.Shared = value\n\t\tcase \"VmHWM\":\n\t\t\tmem.HWM = value\n\t\tcase \"VmStk\":\n\t\t\tmem.Stack = value\n\t\tcase \"VmLck\":\n\t\t\tmem.Locked = value\n\t\t}\n\t}\n\n\treturn scanner.Err()\n}\n\nfunc readSmapsRollup(pid int32, mem *MemoryDetail) error {\n\tfilePath := fmt.Sprintf(\"/proc/%d/smaps_rollup\", pid)\n\tfile, err := os.Open(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\tscanner := bufio.NewScanner(file)\n\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tfields := strings.Fields(line)\n\t\tif len(fields) < 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tkey := strings.TrimSuffix(fields[0], \":\")\n\t\tvalue, _ := strconv.ParseUint(fields[1], 10, 64)\n\t\tvalue *= 1024\n\n\t\tswitch key {\n\t\tcase \"Pss\":\n\t\t\tmem.PSS = value\n\t\tcase \"Private_Clean\", \"Private_Dirty\":\n\t\t\tmem.USS += value\n\t\tcase \"Shared_Clean\", \"Shared_Dirty\":\n\t\t\tif mem.Shared == 0 {\n\t\t\t\tmem.Shared = value\n\t\t\t}\n\t\t}\n\t}\n\n\treturn scanner.Err()\n}\n\nfunc readSmaps(pid int32, mem *MemoryDetail) error {\n\tfilePath := fmt.Sprintf(\"/proc/%d/smaps\", pid)\n\tfile, err := os.Open(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\tscanner := bufio.NewScanner(file)\n\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tfields := strings.Fields(line)\n\t\tif len(fields) < 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tkey := strings.TrimSuffix(fields[0], \":\")\n\t\tvalue, _ := strconv.ParseUint(fields[1], 10, 64)\n\t\tvalue *= 1024\n\n\t\tswitch key {\n\t\tcase \"Pss\":\n\t\t\tmem.PSS += value\n\t\tcase \"Private_Clean\", \"Private_Dirty\":\n\t\t\tmem.USS += value\n\t\tcase \"Shared_Clean\", \"Shared_Dirty\":\n\t\t\tif mem.Shared == 0 {\n\t\t\t\tmem.Shared += value\n\t\t\t}\n\t\t}\n\t}\n\n\treturn scanner.Err()\n}\n"
  },
  {
    "path": "agent/app/service/recycle_bin.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"github.com/shirou/gopsutil/v4/disk\"\n)\n\ntype RecycleBinService struct {\n}\n\ntype IRecycleBinService interface {\n\tPage(search dto.PageInfo) (int64, []response.RecycleBinDTO, error)\n\tCreate(create request.RecycleBinCreate) error\n\tReduce(reduce request.RecycleBinReduce) error\n\tClear() error\n}\n\nfunc NewIRecycleBinService() IRecycleBinService {\n\treturn &RecycleBinService{}\n}\n\nfunc (r RecycleBinService) Page(search dto.PageInfo) (int64, []response.RecycleBinDTO, error) {\n\tvar (\n\t\tresult []response.RecycleBinDTO\n\t)\n\tpartitions, err := disk.Partitions(false)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\top := files.NewFileOp()\n\tfor _, p := range partitions {\n\t\tdir := path.Join(p.Mountpoint, \".1panel_clash\")\n\t\tif !op.Stat(dir) {\n\t\t\tcontinue\n\t\t}\n\t\tclashFiles, err := os.ReadDir(dir)\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tfor _, file := range clashFiles {\n\t\t\tif strings.HasPrefix(file.Name(), \"_1p_\") {\n\t\t\t\trecycleDTO, err := getRecycleBinDTOFromName(file.Name())\n\t\t\t\trecycleDTO.IsDir = file.IsDir()\n\t\t\t\trecycleDTO.From = dir\n\t\t\t\tif err == nil {\n\t\t\t\t\tresult = append(result, *recycleDTO)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tstartIndex := (search.Page - 1) * search.PageSize\n\tendIndex := startIndex + search.PageSize\n\n\tif startIndex > len(result) {\n\t\treturn int64(len(result)), result, nil\n\t}\n\tif endIndex > len(result) {\n\t\tendIndex = len(result)\n\t}\n\treturn int64(len(result)), result[startIndex:endIndex], nil\n}\n\nfunc (r RecycleBinService) Create(create request.RecycleBinCreate) error {\n\tif files.IsProtected(create.SourcePath) {\n\t\treturn buserr.New(\"ErrPathNotDelete\")\n\t}\n\top := files.NewFileOp()\n\tif !op.Stat(create.SourcePath) {\n\t\treturn buserr.New(\"ErrLinkPathNotFound\")\n\t}\n\tclashDir, err := getClashDir(create.SourcePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpaths := strings.Split(create.SourcePath, \"/\")\n\trNamePre := strings.Join(paths, \"_1p_\")\n\tdeleteTime := time.Now()\n\topenFile, err := op.OpenFile(create.SourcePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfileInfo, err := openFile.Stat()\n\tif err != nil {\n\t\treturn err\n\t}\n\tsize := 0\n\tif fileInfo.IsDir() {\n\t\tsizeF, err := op.GetDirSize(create.SourcePath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsize = int(sizeF)\n\t} else {\n\t\tsize = int(fileInfo.Size())\n\t}\n\n\trName := fmt.Sprintf(\"_1p_%s%s_p_%d_%d\", \"file\", rNamePre, size, deleteTime.Unix())\n\treturn op.Mv(create.SourcePath, path.Join(clashDir, rName))\n}\n\nfunc (r RecycleBinService) Reduce(reduce request.RecycleBinReduce) error {\n\tfilePath := path.Join(reduce.From, reduce.RName)\n\top := files.NewFileOp()\n\tif !op.Stat(filePath) {\n\t\treturn buserr.New(\"ErrLinkPathNotFound\")\n\t}\n\trecycleBinDTO, err := getRecycleBinDTOFromName(reduce.RName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !op.Stat(path.Dir(recycleBinDTO.SourcePath)) {\n\t\treturn buserr.New(\"ErrSourcePathNotFound\")\n\t}\n\tif op.Stat(recycleBinDTO.SourcePath) {\n\t\tif err = op.RmRf(recycleBinDTO.SourcePath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn op.Mv(filePath, recycleBinDTO.SourcePath)\n}\n\nfunc (r RecycleBinService) Clear() error {\n\tpartitions, err := disk.Partitions(false)\n\tif err != nil {\n\t\treturn err\n\t}\n\top := files.NewFileOp()\n\tfor _, p := range partitions {\n\t\tdir := path.Join(p.Mountpoint, \".1panel_clash\")\n\t\tif !op.Stat(dir) {\n\t\t\tcontinue\n\t\t}\n\t\tnewDir := path.Join(p.Mountpoint, \"1panel_clash\")\n\t\tif err := op.Mv(dir, newDir); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func() {\n\t\t\t_ = op.DeleteDir(newDir)\n\t\t}()\n\t}\n\treturn nil\n}\n\nfunc getClashDir(realPath string) (string, error) {\n\tpartitions, err := disk.Partitions(false)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tfor _, p := range partitions {\n\t\tif p.Mountpoint == \"/\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(realPath, p.Mountpoint) {\n\t\t\tclashDir := path.Join(p.Mountpoint, \".1panel_clash\")\n\t\t\tif err = createClashDir(path.Join(p.Mountpoint, \".1panel_clash\")); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn clashDir, nil\n\t\t}\n\t}\n\treturn global.Dir.RecycleBinDir, createClashDir(global.Dir.RecycleBinDir)\n}\n\nfunc createClashDir(clashDir string) error {\n\top := files.NewFileOp()\n\tif !op.Stat(clashDir) {\n\t\tif err := op.CreateDir(clashDir, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc getRecycleBinDTOFromName(filename string) (*response.RecycleBinDTO, error) {\n\tmatches := re.GetRegex(re.RecycleBinFilePattern).FindStringSubmatch(filename)\n\tif len(matches) != 4 {\n\t\treturn nil, fmt.Errorf(\"invalid filename format\")\n\t}\n\tsourcePath := \"/\" + strings.ReplaceAll(matches[1], \"_1p_\", \"/\")\n\tsize, err := strconv.ParseInt(matches[2], 10, 64)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif size < math.MinInt || size > math.MaxInt {\n\t\treturn nil, fmt.Errorf(\"size out of int range\")\n\t}\n\n\tdeleteTime, err := strconv.ParseInt(matches[3], 10, 64)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &response.RecycleBinDTO{\n\t\tName:       path.Base(sourcePath),\n\t\tSize:       int(size),\n\t\tType:       \"file\",\n\t\tDeleteTime: time.Unix(deleteTime, 0),\n\t\tSourcePath: sourcePath,\n\t\tRName:      filename,\n\t}, nil\n}\n"
  },
  {
    "path": "agent/app/service/runtime.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"maps\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\tfcgiclient \"github.com/tomasen/fcgi_client\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"gopkg.in/ini.v1\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/env\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/subosito/gotenv\"\n)\n\ntype RuntimeService struct {\n}\n\ntype IRuntimeService interface {\n\tPage(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error)\n\tCreate(create request.RuntimeCreate) (*model.Runtime, error)\n\tDelete(delete request.RuntimeDelete) error\n\tUpdate(req request.RuntimeUpdate) error\n\tGet(id uint) (res *response.RuntimeDTO, err error)\n\tGetNodePackageRunScript(req request.NodePackageReq) ([]response.PackageScripts, error)\n\tOperateRuntime(req request.RuntimeOperate) error\n\tGetNodeModules(req request.NodeModuleReq) ([]response.NodeModule, error)\n\tOperateNodeModules(req request.NodeModuleOperateReq) error\n\tSyncForRestart() error\n\tSyncRuntimeStatus() error\n\tDeleteCheck(installID uint) ([]dto.AppResource, error)\n\tUpdateRemark(req request.RuntimeRemark) error\n\n\tGetPHPExtensions(runtimeID uint) (response.PHPExtensionRes, error)\n\tInstallPHPExtension(req request.PHPExtensionInstallReq) error\n\tUnInstallPHPExtension(req request.PHPExtensionInstallReq) error\n\n\tGetPHPConfig(id uint) (*response.PHPConfig, error)\n\tUpdatePHPConfig(req request.PHPConfigUpdate) (err error)\n\tUpdatePHPConfigFile(req request.PHPFileUpdate) error\n\tGetPHPConfigFile(req request.PHPFileReq) (*response.FileInfo, error)\n\tUpdateFPMConfig(req request.FPMConfig) error\n\tGetFPMConfig(id uint) (*request.FPMConfig, error)\n\n\tUpdatePHPContainer(req request.PHPContainerConfig) error\n\tGetPHPContainerConfig(id uint) (*request.PHPContainerConfig, error)\n\n\tGetSupervisorProcess(id uint) ([]response.SupervisorProcessConfig, error)\n\tOperateSupervisorProcess(req request.PHPSupervisorProcessConfig) error\n\tOperateSupervisorProcessFile(req request.PHPSupervisorProcessFileReq) (string, error)\n\n\tGetFPMStatus(runtimeID uint) ([]response.FpmStatusItem, error)\n}\n\nfunc NewRuntimeService() IRuntimeService {\n\treturn &RuntimeService{}\n}\n\nfunc (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, error) {\n\tvar (\n\t\topts []repo.DBOption\n\t)\n\tif create.Name != \"\" {\n\t\topts = append(opts, repo.WithByName(create.Name))\n\t}\n\tif create.Type != \"\" {\n\t\topts = append(opts, repo.WithByType(create.Type))\n\t}\n\texist, _ := runtimeRepo.GetFirst(context.Background(), opts...)\n\tif exist != nil {\n\t\treturn nil, buserr.New(\"ErrNameIsExist\")\n\t}\n\tfileOp := files.NewFileOp()\n\n\truntimeDir := path.Join(global.Dir.RuntimeDir, create.Type)\n\tif !fileOp.Stat(runtimeDir) {\n\t\tif err := fileOp.CreateDir(runtimeDir, constant.DirPerm); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tvar hostPorts []string\n\tswitch create.Type {\n\tcase constant.RuntimePHP:\n\t\tif create.Resource == constant.ResourceLocal {\n\t\t\truntime := &model.Runtime{\n\t\t\t\tName:     create.Name,\n\t\t\t\tResource: create.Resource,\n\t\t\t\tType:     create.Type,\n\t\t\t\tVersion:  create.Version,\n\t\t\t\tStatus:   constant.StatusNormal,\n\t\t\t\tRemark:   create.Remark,\n\t\t\t}\n\t\t\treturn nil, runtimeRepo.Create(context.Background(), runtime)\n\t\t}\n\t\texist, _ = runtimeRepo.GetFirst(context.Background(), runtimeRepo.WithImage(create.Image))\n\t\tif exist != nil {\n\t\t\treturn nil, buserr.New(\"ErrImageExist\")\n\t\t}\n\t\tfpmPort, ok := create.Params[\"PANEL_APP_PORT_HTTP\"]\n\t\tif !ok {\n\t\t\treturn nil, buserr.New(\"ErrPortNotFound\")\n\t\t}\n\t\thostPorts = append(hostPorts, fmt.Sprintf(\"%.0f\", fpmPort.(float64)))\n\t\tif err := checkPortExist(int(fpmPort.(float64))); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:\n\t\tif !fileOp.Stat(create.CodeDir) {\n\t\t\treturn nil, buserr.New(\"ErrPathNotFound\")\n\t\t}\n\t\tcreate.Install = true\n\t\tfor _, export := range create.ExposedPorts {\n\t\t\thostPorts = append(hostPorts, strconv.Itoa(export.HostPort))\n\t\t\tif err := checkPortExist(export.HostPort); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\tcontainerName, ok := create.Params[\"CONTAINER_NAME\"]\n\tif !ok {\n\t\treturn nil, buserr.New(\"ErrContainerNameIsNull\")\n\t}\n\tif err := checkContainerName(containerName.(string)); err != nil {\n\t\treturn nil, err\n\t}\n\n\tappDetail, err := appDetailRepo.GetFirst(repo.WithByID(create.AppDetailID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tapp, err := appRepo.GetFirst(repo.WithByID(appDetail.AppId))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tappVersionDir := filepath.Join(app.GetAppResourcePath(), appDetail.Version)\n\tif !fileOp.Stat(appVersionDir) {\n\t\tif err = downloadApp(app, appDetail, nil, nil); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\truntime := &model.Runtime{\n\t\tName:          create.Name,\n\t\tAppDetailID:   create.AppDetailID,\n\t\tType:          create.Type,\n\t\tImage:         create.Image,\n\t\tResource:      create.Resource,\n\t\tVersion:       create.Version,\n\t\tContainerName: containerName.(string),\n\t\tPort:          strings.Join(hostPorts, \",\"),\n\t\tRemark:        create.Remark,\n\t}\n\n\tswitch create.Type {\n\tcase constant.RuntimePHP:\n\t\tif err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:\n\t\tif err = handleRuntime(create, runtime, fileOp, appVersionDir); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif err := runtimeRepo.Create(context.Background(), runtime); err != nil {\n\t\treturn nil, err\n\t}\n\treturn runtime, nil\n}\n\nfunc (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.RuntimeDTO, error) {\n\tvar (\n\t\topts []repo.DBOption\n\t\tres  []response.RuntimeDTO\n\t)\n\tif req.Name != \"\" {\n\t\topts = append(opts, repo.WithByLikeName(req.Name))\n\t}\n\tif req.Status != \"\" {\n\t\tif req.Type == constant.TypePhp {\n\t\t\topts = append(opts, runtimeRepo.WithNormalStatus(req.Status))\n\t\t} else {\n\t\t\topts = append(opts, runtimeRepo.WithStatus(req.Status))\n\t\t}\n\t}\n\tif req.Type != \"\" {\n\t\topts = append(opts, repo.WithByType(req.Type))\n\t}\n\ttotal, runtimes, err := runtimeRepo.Page(req.Page, req.PageSize, opts...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tif len(runtimes) == 0 {\n\t\treturn 0, res, nil\n\t}\n\tif err = SyncRuntimesStatus(runtimes); err != nil {\n\t\treturn 0, nil, err\n\t}\n\tfor _, runtime := range runtimes {\n\t\tif runtime.Resource == constant.ResourceLocal {\n\t\t\truntime.Status = constant.StatusNormal\n\t\t}\n\t\truntimeDTO := response.NewRuntimeDTO(runtime)\n\t\truntimeDTO.Params = make(map[string]interface{})\n\t\tenvs, err := gotenv.Unmarshal(runtime.Env)\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tdetail, _ := appDetailRepo.GetFirst(repo.WithByID(runtime.AppDetailID))\n\t\tif detail.AppId == 0 {\n\t\t\tappID, appDetailID := handleRuntimeDetailID(runtime)\n\t\t\truntimeDTO.AppDetailID = appDetailID\n\t\t\truntimeDTO.AppID = appID\n\t\t} else {\n\t\t\truntimeDTO.AppID = detail.AppId\n\t\t}\n\t\tfor k, v := range envs {\n\t\t\truntimeDTO.Params[k] = v\n\t\t\tif strings.Contains(k, \"CONTAINER_PORT\") || strings.Contains(k, \"HOST_PORT\") {\n\t\t\t\tif strings.Contains(k, \"CONTAINER_PORT\") {\n\t\t\t\t\tmatches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k)\n\t\t\t\t\tif len(matches) < 2 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tcontainerPort, err := strconv.Atoi(v)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\thostPort, err := strconv.Atoi(envs[fmt.Sprintf(\"HOST_PORT_%s\", matches[1])])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\thostIP := envs[fmt.Sprintf(\"HOST_IP_%s\", matches[1])]\n\t\t\t\t\truntimeDTO.ExposedPorts = append(runtimeDTO.ExposedPorts, request.ExposedPort{\n\t\t\t\t\t\tContainerPort: containerPort,\n\t\t\t\t\t\tHostPort:      hostPort,\n\t\t\t\t\t\tHostIP:        hostIP,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tres = append(res, runtimeDTO)\n\t}\n\treturn total, res, nil\n}\n\nfunc (r *RuntimeService) DeleteCheck(runTimeId uint) ([]dto.AppResource, error) {\n\tvar res []dto.AppResource\n\twebsites, _ := websiteRepo.GetBy(websiteRepo.WithRuntimeID(runTimeId))\n\tfor _, website := range websites {\n\t\tres = append(res, dto.AppResource{\n\t\t\tType: \"website\",\n\t\t\tName: website.PrimaryDomain,\n\t\t})\n\t}\n\treturn res, nil\n}\n\nfunc (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(runtimeDelete.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\twebsite, _ := websiteRepo.GetFirst(websiteRepo.WithRuntimeID(runtimeDelete.ID))\n\tif website.ID > 0 {\n\t\treturn buserr.New(\"ErrDelWithWebsite\")\n\t}\n\tif runtime.Resource != constant.ResourceAppstore {\n\t\treturn runtimeRepo.DeleteBy(repo.WithByID(runtimeDelete.ID))\n\t}\n\tprojectDir := runtime.GetPath()\n\tif out, err := compose.Down(runtime.GetComposePath()); err != nil && !runtimeDelete.ForceDelete {\n\t\tif out != \"\" {\n\t\t\treturn errors.New(out)\n\t\t}\n\t\treturn err\n\t}\n\tif runtime.Type == constant.RuntimePHP {\n\t\tclient, err := docker.NewClient()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer client.Close()\n\t\timageID, err := client.GetImageIDByName(runtime.Image)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif imageID != \"\" {\n\t\t\tif err := client.DeleteImage(imageID); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"delete image id [%s] error %v\", imageID, err)\n\t\t\t}\n\t\t}\n\t}\n\tif err := files.NewFileOp().DeleteDir(projectDir); err != nil && !runtimeDelete.ForceDelete {\n\t\treturn err\n\t}\n\treturn runtimeRepo.DeleteBy(repo.WithByID(runtimeDelete.ID))\n}\n\nfunc (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tres := response.NewRuntimeDTO(*runtime)\n\tif runtime.Resource == constant.ResourceLocal {\n\t\treturn &res, nil\n\t}\n\tappDetail, err := appDetailRepo.GetFirst(repo.WithByID(runtime.AppDetailID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres.AppID = appDetail.AppId\n\tswitch runtime.Type {\n\tcase constant.RuntimePHP:\n\t\tvar (\n\t\t\tappForm   dto.AppForm\n\t\t\tappParams []response.AppParam\n\t\t)\n\t\tif err := json.Unmarshal([]byte(runtime.Params), &appForm); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tenvs, err := gotenv.Unmarshal(runtime.Env)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif v, ok := envs[\"CONTAINER_PACKAGE_URL\"]; ok {\n\t\t\tres.Source = v\n\t\t}\n\t\tres.Params = make(map[string]interface{})\n\t\tfor k, v := range envs {\n\t\t\tif k == \"PANEL_APP_PORT_HTTP\" {\n\t\t\t\tport, err := strconv.Atoi(v)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tres.Params[k] = port\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tres.Params[k] = v\n\t\t}\n\n\t\tfor _, form := range appForm.FormFields {\n\t\t\tif v, ok := envs[form.EnvKey]; ok {\n\t\t\t\tappParam := response.AppParam{\n\t\t\t\t\tEdit:     false,\n\t\t\t\t\tKey:      form.EnvKey,\n\t\t\t\t\tRule:     form.Rule,\n\t\t\t\t\tType:     form.Type,\n\t\t\t\t\tRequired: form.Required,\n\t\t\t\t}\n\t\t\t\tif form.Edit {\n\t\t\t\t\tappParam.Edit = true\n\t\t\t\t}\n\t\t\t\tappParam.LabelZh = form.LabelZh\n\t\t\t\tappParam.LabelEn = form.LabelEn\n\t\t\t\tappParam.Multiple = form.Multiple\n\t\t\t\tappParam.Value = v\n\t\t\t\tif form.Type == \"select\" {\n\t\t\t\t\tif form.Multiple {\n\t\t\t\t\t\tif v == \"\" {\n\t\t\t\t\t\t\tappParam.Value = []string{}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tappParam.Value = strings.Split(v, \",\")\n\t\t\t\t\t\t\tif strSlice, ok := appParam.Value.([]string); ok && len(strSlice) > 0 && strSlice[0] == \"\" {\n\t\t\t\t\t\t\t\tappParam.Value = strSlice[1:]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor _, fv := range form.Values {\n\t\t\t\t\t\t\tif fv.Value == v {\n\t\t\t\t\t\t\t\tappParam.ShowValue = fv.Label\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tappParam.Values = form.Values\n\t\t\t\t}\n\t\t\t\tappParams = append(appParams, appParam)\n\t\t\t}\n\t\t}\n\t\tres.AppParams = appParams\n\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:\n\t\tif err := handleRuntimeDTO(&res, *runtime); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn &res, nil\n}\n\nfunc (r *RuntimeService) Update(req request.RuntimeUpdate) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif runtime.Resource == constant.ResourceLocal {\n\t\truntime.Version = req.Version\n\t\treturn runtimeRepo.Save(runtime)\n\t}\n\toldImage := runtime.Image\n\toldEnv := runtime.Env\n\tvar hostPorts []string\n\tswitch runtime.Type {\n\tcase constant.RuntimePHP:\n\t\texist, _ := runtimeRepo.GetFirst(context.Background(), runtimeRepo.WithImage(req.Name), runtimeRepo.WithNotId(req.ID))\n\t\tif exist != nil {\n\t\t\treturn buserr.New(\"ErrImageExist\")\n\t\t}\n\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:\n\t\tfor _, export := range req.ExposedPorts {\n\t\t\thostPorts = append(hostPorts, strconv.Itoa(export.HostPort))\n\t\t\tif err = checkRuntimePortExist(export.HostPort, false, runtime.ID); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tappDetail, err := appDetailRepo.GetFirst(repo.WithByID(runtime.AppDetailID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tapp, err := appRepo.GetFirst(repo.WithByID(appDetail.AppId))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfileOp := files.NewFileOp()\n\t\tappVersionDir := path.Join(global.Dir.AppResourceDir, app.Resource, app.Key, appDetail.Version)\n\t\tif !fileOp.Stat(appVersionDir) || appDetail.Update {\n\t\t\tif err := downloadApp(app, appDetail, nil, nil); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_ = fileOp.Rename(path.Join(runtime.GetPath(), \"run.sh\"), path.Join(runtime.GetPath(), \"run.sh.bak\"))\n\t\t\t_ = fileOp.CopyFile(path.Join(appVersionDir, \"run.sh\"), runtime.GetPath())\n\t\t}\n\t}\n\n\tif containerName, ok := req.Params[\"CONTAINER_NAME\"]; ok && containerName != getRuntimeEnv(runtime.Env, \"CONTAINER_NAME\") {\n\t\tif err := checkContainerName(containerName.(string)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\truntime.ContainerName = containerName.(string)\n\t}\n\n\tprojectDir := path.Join(global.Dir.RuntimeDir, runtime.Type, runtime.Name)\n\tcreate := request.RuntimeCreate{\n\t\tImage:   req.Image,\n\t\tType:    runtime.Type,\n\t\tSource:  req.Source,\n\t\tParams:  req.Params,\n\t\tCodeDir: req.CodeDir,\n\t\tVersion: req.Version,\n\t\tRemark:  req.Remark,\n\t\tNodeConfig: request.NodeConfig{\n\t\t\tInstall:      true,\n\t\t\tExposedPorts: req.ExposedPorts,\n\t\t\tEnvironments: req.Environments,\n\t\t\tVolumes:      req.Volumes,\n\t\t\tExtraHosts:   req.ExtraHosts,\n\t\t},\n\t}\n\tcomposeContent, envContent, _, err := handleParams(create, projectDir)\n\tif err != nil {\n\t\treturn err\n\t}\n\truntime.Remark = req.Remark\n\truntime.Env = string(envContent)\n\truntime.DockerCompose = string(composeContent)\n\n\tswitch runtime.Type {\n\tcase constant.RuntimePHP:\n\t\truntime.Image = req.Image\n\t\truntime.Status = constant.StatusBuilding\n\t\t_ = runtimeRepo.Save(runtime)\n\t\tclient, err := docker.NewClient()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer client.Close()\n\t\timageID, err := client.GetImageIDByName(oldImage)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo buildRuntime(runtime, imageID, oldEnv, req.Rebuild)\n\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:\n\t\truntime.Version = req.Version\n\t\truntime.CodeDir = req.CodeDir\n\t\truntime.Port = strings.Join(hostPorts, \",\")\n\t\truntime.Status = constant.StatusReCreating\n\t\truntime.ContainerName = req.Params[\"CONTAINER_NAME\"].(string)\n\t\t_ = runtimeRepo.Save(runtime)\n\t\tgo reCreateRuntime(runtime)\n\t}\n\treturn nil\n}\n\nfunc (r *RuntimeService) GetNodePackageRunScript(req request.NodePackageReq) ([]response.PackageScripts, error) {\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(req.CodeDir) {\n\t\treturn nil, buserr.New(\"ErrPathNotFound\")\n\t}\n\tif !fileOp.Stat(path.Join(req.CodeDir, \"package.json\")) {\n\t\treturn nil, buserr.New(\"ErrPackageJsonNotFound\")\n\t}\n\tcontent, err := fileOp.GetContent(path.Join(req.CodeDir, \"package.json\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar packageMap map[string]interface{}\n\terr = json.Unmarshal(content, &packageMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tscripts, ok := packageMap[\"scripts\"]\n\tif !ok {\n\t\treturn nil, buserr.New(\"ErrScriptsNotFound\")\n\t}\n\tvar packageScripts []response.PackageScripts\n\tfor k, v := range scripts.(map[string]interface{}) {\n\t\tpackageScripts = append(packageScripts, response.PackageScripts{\n\t\t\tName:   k,\n\t\t\tScript: v.(string),\n\t\t})\n\t}\n\treturn packageScripts, nil\n}\n\nfunc (r *RuntimeService) OperateRuntime(req request.RuntimeOperate) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\truntime.Status = constant.StatusError\n\t\t\truntime.Message = err.Error()\n\t\t\t_ = runtimeRepo.Save(runtime)\n\t\t}\n\t}()\n\tswitch req.Operate {\n\tcase constant.RuntimeUp:\n\t\tif err = runComposeCmdWithLog(req.Operate, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = SyncRuntimeContainerStatus(runtime); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase constant.RuntimeDown:\n\t\tif err = runComposeCmdWithLog(req.Operate, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {\n\t\t\treturn err\n\t\t}\n\t\truntime.Status = constant.StatusStopped\n\tcase constant.RuntimeRestart:\n\t\tif err = restartRuntime(runtime); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = SyncRuntimeContainerStatus(runtime); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn runtimeRepo.Save(runtime)\n}\n\nfunc (r *RuntimeService) GetNodeModules(req request.NodeModuleReq) ([]response.NodeModule, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []response.NodeModule\n\tnodeModulesPath := path.Join(runtime.CodeDir, \"node_modules\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(nodeModulesPath) {\n\t\treturn res, nil\n\t}\n\tmoduleDirs, err := os.ReadDir(nodeModulesPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, moduleDir := range moduleDirs {\n\t\tpackagePath := path.Join(nodeModulesPath, moduleDir.Name(), \"package.json\")\n\t\tif !fileOp.Stat(packagePath) {\n\t\t\tcontinue\n\t\t}\n\t\tcontent, err := fileOp.GetContent(packagePath)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tmodule := response.NodeModule{}\n\t\tif err := json.Unmarshal(content, &module); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tres = append(res, module)\n\t}\n\treturn res, nil\n}\n\nfunc (r *RuntimeService) OperateNodeModules(req request.NodeModuleOperateReq) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcontainerName, err := env.GetEnvValueByKey(runtime.GetEnvPath(), \"CONTAINER_NAME\")\n\tif err != nil {\n\t\treturn err\n\t}\n\toperation := getOperation(req.Operate, req.PkgManager)\n\texecScript := fmt.Sprintf(\"%s %s %s\", req.PkgManager, operation, req.Module)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute)\n\tdefer cancel()\n\tinstallCmd := exec.CommandContext(ctx, \"docker\", \"exec\", \"-i\", containerName, \"bash\", \"-c\", execScript)\n\toutput, err := installCmd.CombinedOutput()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to execute command: %s, error: %w\", string(output), err)\n\t}\n\treturn nil\n}\n\nfunc (r *RuntimeService) SyncForRestart() error {\n\truntimes, err := runtimeRepo.List()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, runtime := range runtimes {\n\t\tif runtime.Status == constant.StatusBuilding || runtime.Status == constant.StatusReCreating || runtime.Status == constant.StatusStarting || runtime.Status == constant.StatusCreating {\n\t\t\truntime.Status = constant.SystemRestart\n\t\t\truntime.Message = \"System restart causing interrupt\"\n\t\t\t_ = runtimeRepo.Save(&runtime)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (r *RuntimeService) SyncRuntimeStatus() error {\n\truntimes, err := runtimeRepo.List()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, runtime := range runtimes {\n\t\tif runtime.Type == constant.RuntimeNode || runtime.Type == constant.RuntimeJava || runtime.Type == constant.RuntimeGo || runtime.Type == constant.RuntimePython || runtime.Type == constant.RuntimeDotNet {\n\t\t\t_ = SyncRuntimeContainerStatus(&runtime)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (r *RuntimeService) GetPHPExtensions(runtimeID uint) (response.PHPExtensionRes, error) {\n\tvar res response.PHPExtensionRes\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(runtimeID))\n\tif err != nil {\n\t\treturn res, err\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))\n\tout, err := cmdMgr.RunWithStdout(\"docker\", \"exec\", \"-i\", runtime.ContainerName, \"php\", \"-m\")\n\tif err != nil {\n\t\treturn res, err\n\t}\n\textensions := strings.Split(out, \"\\n\")\n\texitExtensions := make(map[string]string)\n\tfor _, ext := range extensions {\n\t\textStr := strings.TrimSpace(ext)\n\t\tif extStr != \"\" && extStr != \"[Zend Modules]\" && extStr != \"[PHP Modules]\" {\n\t\t\texitExtensions[strings.ToLower(extStr)] = extStr\n\t\t}\n\t}\n\tvar phpExtensions []response.SupportExtension\n\tif err = json.Unmarshal(nginx_conf.GetWebsiteFile(\"php_extensions.json\"), &phpExtensions); err != nil {\n\t\treturn res, err\n\t}\n\tfor _, ext := range phpExtensions {\n\t\tif _, ok := exitExtensions[strings.ToLower(ext.Check)]; ok {\n\t\t\text.Installed = true\n\t\t}\n\t\tres.SupportExtensions = append(res.SupportExtensions, ext)\n\t}\n\tfor _, name := range exitExtensions {\n\t\tres.Extensions = append(res.Extensions, name)\n\t}\n\tsort.Slice(res.Extensions, func(i, j int) bool {\n\t\treturn strings.ToLower(res.Extensions[i]) < strings.ToLower(res.Extensions[j])\n\t})\n\treturn res, nil\n}\n\nfunc (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif task.CheckResourceTaskIsExecuting(task.TaskInstall, task.TaskScopeRuntimeExtension, runtime.ID) {\n\t\treturn buserr.New(\"ErrInstallExtension\")\n\t}\n\tinstallTask, err := task.NewTaskWithOps(req.Name, task.TaskInstall, task.TaskScopeRuntimeExtension, req.TaskID, runtime.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinstallTask.AddSubTask(\"\", func(t *task.Task) error {\n\t\terr = cmd.NewCommandMgr(cmd.WithTask(*installTask), cmd.WithTimeout(20*time.Minute)).\n\t\t\tRun(\"docker\", \"exec\", \"-i\", runtime.ContainerName, \"install-ext\", req.Name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tclient, err := docker.NewClient()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer client.Close()\n\t\toldImageID, err := client.GetImageIDByName(runtime.Image)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = cmd.NewCommandMgr(cmd.WithTask(*installTask), cmd.WithTimeout(15*time.Minute)).\n\t\t\tRun(\"docker\", \"commit\", runtime.ContainerName, runtime.Image)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\thandlePHPDir(*runtime)\n\t\tif err = restartRuntime(runtime); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tnewImageID, err := client.GetImageIDByName(runtime.Image)\n\t\tif err == nil && newImageID != oldImageID {\n\t\t\tif err := client.DeleteImage(oldImageID); err != nil {\n\t\t\t\tt.Log(fmt.Sprintf(\"delete old image %s failed %v\", oldImageID, err))\n\t\t\t} else {\n\t\t\t\tt.Log(\"delete old image success\")\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}, nil)\n\tgo func() {\n\t\terr = installTask.Execute()\n\t\tif err == nil {\n\t\t\tenvs, err := gotenv.Unmarshal(runtime.Env)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"get runtime env error %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\textensions, ok := envs[\"PHP_EXTENSIONS\"]\n\t\t\texist := false\n\t\t\tvar extensionArray []string\n\t\t\tif ok {\n\t\t\t\textensions = strings.TrimPrefix(extensions, \",\")\n\t\t\t\textensionArray = strings.Split(extensions, \",\")\n\t\t\t\tfor _, ext := range extensionArray {\n\t\t\t\t\tif ext == req.Name {\n\t\t\t\t\t\texist = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !exist {\n\t\t\t\textensionArray = append(extensionArray, req.Name)\n\t\t\t\tenvs[\"PHP_EXTENSIONS\"] = strings.Join(extensionArray, \",\")\n\t\t\t\tif err = gotenv.Write(envs, runtime.GetEnvPath()); err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"write runtime env error %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tenvStr, err := gotenv.Marshal(envs)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"marshal runtime env error %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\truntime.Env = envStr\n\t\t\t\t_ = runtimeRepo.Save(runtime)\n\t\t\t}\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc (r *RuntimeService) UnInstallPHPExtension(req request.PHPExtensionInstallReq) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = unInstallPHPExtension(runtime, []string{req.Name}); err != nil {\n\t\treturn err\n\t}\n\tif err = restartRuntime(runtime); err != nil {\n\t\treturn err\n\t}\n\treturn runtimeRepo.Save(runtime)\n}\n\nfunc (r *RuntimeService) GetPHPConfig(id uint) (*response.PHPConfig, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tphpConfigPath := path.Join(runtime.GetPath(), \"conf\", \"php.ini\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(phpConfigPath) {\n\t\treturn nil, buserr.WithName(\"ErrFileNotFound\", \"php.ini\")\n\t}\n\tparams := make(map[string]string)\n\tconfigFile, err := fileOp.OpenFile(phpConfigPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer configFile.Close()\n\tscanner := bufio.NewScanner(configFile)\n\tfor scanner.Scan() {\n\t\tline := strings.TrimSpace(scanner.Text())\n\t\tif strings.HasPrefix(line, \";\") {\n\t\t\tcontinue\n\t\t}\n\t\tmatches := re.GetRegex(re.PhpAssignmentPattern).FindStringSubmatch(line)\n\t\tif len(matches) == 3 {\n\t\t\tparams[matches[1]] = matches[2]\n\t\t}\n\t}\n\tcfg, err := ini.Load(phpConfigPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tphpConfig, err := cfg.GetSection(\"PHP\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdisableFunctionStr := phpConfig.Key(\"disable_functions\").Value()\n\tres := &response.PHPConfig{Params: params}\n\tif disableFunctionStr != \"\" {\n\t\tdisableFunctions := strings.Split(disableFunctionStr, \",\")\n\t\tif len(disableFunctions) > 0 {\n\t\t\tres.DisableFunctions = disableFunctions\n\t\t}\n\t}\n\tuploadMaxSize := phpConfig.Key(\"upload_max_filesize\").Value()\n\tif uploadMaxSize != \"\" {\n\t\tres.UploadMaxSize = uploadMaxSize\n\t}\n\ttimeout := phpConfig.Key(\"max_execution_time\").Value()\n\tif timeout != \"\" {\n\t\tres.MaxExecutionTime = timeout\n\t}\n\treturn res, nil\n}\n\nfunc (r *RuntimeService) UpdatePHPConfig(req request.PHPConfigUpdate) (err error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tphpConfigPath := path.Join(runtime.GetPath(), \"conf\", \"php.ini\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(phpConfigPath) {\n\t\treturn buserr.WithName(\"ErrFileNotFound\", \"php.ini\")\n\t}\n\tconfigFile, err := fileOp.OpenFile(phpConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer configFile.Close()\n\n\tcontentBytes, err := fileOp.GetContent(phpConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcontent := string(contentBytes)\n\tlines := strings.Split(content, \"\\n\")\n\tfor i, line := range lines {\n\t\tif strings.HasPrefix(line, \";\") {\n\t\t\tcontinue\n\t\t}\n\t\tswitch req.Scope {\n\t\tcase \"params\":\n\t\t\tfor key, value := range req.Params {\n\t\t\t\tif phpConfigLineMatchesKey(line, key) {\n\t\t\t\t\tlines[i] = fmt.Sprintf(\"%s = %s\", key, value)\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"disable_functions\":\n\t\t\tif phpConfigLineMatchesKey(line, \"disable_functions\") {\n\t\t\t\tlines[i] = fmt.Sprintf(\"disable_functions = %s\", strings.Join(req.DisableFunctions, \",\"))\n\t\t\t}\n\t\tcase \"upload_max_filesize\":\n\t\t\tif phpConfigLineMatchesKey(line, \"post_max_size\") {\n\t\t\t\tlines[i] = fmt.Sprintf(\"post_max_size = %s\", req.UploadMaxSize)\n\t\t\t}\n\t\t\tif phpConfigLineMatchesKey(line, \"upload_max_filesize\") {\n\t\t\t\tlines[i] = fmt.Sprintf(\"upload_max_filesize = %s\", req.UploadMaxSize)\n\t\t\t}\n\t\tcase \"max_execution_time\":\n\t\t\tif phpConfigLineMatchesKey(line, \"max_execution_time\") {\n\t\t\t\tlines[i] = fmt.Sprintf(\"max_execution_time = %s\", req.MaxExecutionTime)\n\t\t\t}\n\t\t\tif phpConfigLineMatchesKey(line, \"max_input_time\") {\n\t\t\t\tlines[i] = fmt.Sprintf(\"max_input_time = %s\", req.MaxExecutionTime)\n\t\t\t}\n\t\t}\n\t}\n\tupdatedContent := strings.Join(lines, \"\\n\")\n\tif err := fileOp.WriteFile(phpConfigPath, strings.NewReader(updatedContent), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\n\terr = restartRuntime(runtime)\n\tif err != nil {\n\t\t_ = fileOp.WriteFile(phpConfigPath, strings.NewReader(string(contentBytes)), constant.DirPerm)\n\t\treturn err\n\t}\n\treturn\n}\n\nfunc (r *RuntimeService) GetPHPConfigFile(req request.PHPFileReq) (*response.FileInfo, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfigPath := \"\"\n\tswitch req.Type {\n\tcase constant.ConfigFPM:\n\t\tconfigPath = path.Join(runtime.GetPath(), \"conf\", \"php-fpm.conf\")\n\tcase constant.ConfigPHP:\n\t\tconfigPath = path.Join(runtime.GetPath(), \"conf\", \"php.ini\")\n\t}\n\tinfo, err := files.NewFileInfo(files.FileOption{\n\t\tPath:   configPath,\n\t\tExpand: true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &response.FileInfo{FileInfo: *info}, nil\n}\n\nfunc phpConfigLineMatchesKey(line, key string) bool {\n\ttrim := strings.TrimSpace(line)\n\n\tidx := strings.Index(trim, \"=\")\n\tif idx == -1 {\n\t\treturn false\n\t}\n\tcurrentKey := strings.TrimSpace(trim[:idx])\n\treturn currentKey == key\n}\n\nfunc (r *RuntimeService) UpdatePHPConfigFile(req request.PHPFileUpdate) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfigPath := \"\"\n\tif req.Type == constant.ConfigFPM {\n\t\tconfigPath = path.Join(runtime.GetPath(), \"conf\", \"php-fpm.conf\")\n\t} else {\n\t\tconfigPath = path.Join(runtime.GetPath(), \"conf\", \"php.ini\")\n\t}\n\tif err := files.NewFileOp().WriteFile(configPath, strings.NewReader(req.Content), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\tif _, err := compose.Restart(runtime.GetComposePath()); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (r *RuntimeService) UpdateFPMConfig(req request.FPMConfig) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcfg, err := ini.Load(runtime.GetFPMPath())\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, v := range req.Params {\n\t\tvar valueStr string\n\t\tswitch v := v.(type) {\n\t\tcase string:\n\t\t\tvalueStr = v\n\t\tcase int:\n\t\t\tvalueStr = fmt.Sprintf(\"%d\", v)\n\t\tcase float64:\n\t\t\tvalueStr = fmt.Sprintf(\"%.f\", v)\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\t\tcfg.Section(\"www\").Key(k).SetValue(valueStr)\n\t}\n\tif err := cfg.SaveTo(runtime.GetFPMPath()); err != nil {\n\t\treturn err\n\t}\n\tif _, err := compose.Restart(runtime.GetComposePath()); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nvar PmKeys = map[string]struct {\n}{\n\t\"pm\":                   {},\n\t\"pm.max_children\":      {},\n\t\"pm.start_servers\":     {},\n\t\"pm.min_spare_servers\": {},\n\t\"pm.max_spare_servers\": {},\n}\n\nfunc (r *RuntimeService) GetFPMConfig(id uint) (*request.FPMConfig, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(runtime.GetFPMPath()) {\n\t\treturn nil, buserr.WithName(\"ErrFileNotFound\", \"php-fpm.conf\")\n\t}\n\tparams := make(map[string]interface{})\n\tcfg, err := ini.Load(runtime.GetFPMPath())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, key := range cfg.Section(\"www\").Keys() {\n\t\tif _, ok := PmKeys[key.Name()]; ok {\n\t\t\tparams[key.Name()] = key.Value()\n\t\t}\n\t}\n\tres := &request.FPMConfig{Params: params}\n\treturn res, nil\n}\n\nfunc (r *RuntimeService) UpdatePHPContainer(req request.PHPContainerConfig) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar (\n\t\tcomposeContent []byte\n\t)\n\tfor _, export := range req.ExposedPorts {\n\t\tif strconv.Itoa(export.HostPort) == runtime.Port {\n\t\t\treturn buserr.WithName(\"ErrPHPRuntimePortFailed\", strconv.Itoa(export.HostPort))\n\t\t}\n\t\tif export.ContainerPort == 9000 {\n\t\t\treturn buserr.New(\"ErrPHPPortIsDefault\")\n\t\t}\n\t\tif err = checkRuntimePortExist(export.HostPort, false, runtime.ID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif req.ContainerName != \"\" && req.ContainerName != getRuntimeEnv(runtime.Env, \"CONTAINER_NAME\") {\n\t\tif err := checkContainerName(req.ContainerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\truntime.ContainerName = req.ContainerName\n\t}\n\tfileOp := files.NewFileOp()\n\tprojectDir := path.Join(global.Dir.RuntimeDir, runtime.Type, runtime.Name)\n\tcomposeContent, err = fileOp.GetContent(path.Join(projectDir, \"docker-compose.yml\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tenvPath := path.Join(projectDir, \".env\")\n\tif !fileOp.Stat(envPath) {\n\t\t_ = fileOp.CreateFile(envPath)\n\t}\n\tenvs, err := gotenv.Read(envPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k := range envs {\n\t\tif strings.HasPrefix(k, \"CONTAINER_PORT_\") || strings.HasPrefix(k, \"HOST_PORT_\") || strings.HasPrefix(k, \"HOST_IP_\") || strings.Contains(k, \"APP_PORT\") {\n\t\t\tdelete(envs, k)\n\t\t}\n\t}\n\tcreate := request.RuntimeCreate{\n\t\tImage:  runtime.Image,\n\t\tType:   runtime.Type,\n\t\tParams: make(map[string]interface{}),\n\t\tNodeConfig: request.NodeConfig{\n\t\t\tExposedPorts: req.ExposedPorts,\n\t\t\tEnvironments: req.Environments,\n\t\t\tVolumes:      req.Volumes,\n\t\t},\n\t}\n\tcomposeContent, err = handleCompose(envs, composeContent, create, projectDir)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewMap := make(map[string]string)\n\thandleMap(create.Params, newMap)\n\tmaps.Copy(envs, newMap)\n\tenvs[\"PANEL_APP_PORT_HTTP\"] = runtime.Port\n\tenvStr, err := gotenv.Marshal(envs)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = gotenv.Write(envs, envPath); err != nil {\n\t\treturn err\n\t}\n\tenvContent := []byte(envStr)\n\truntime.Env = string(envContent)\n\truntime.DockerCompose = string(composeContent)\n\truntime.Status = constant.StatusReCreating\n\t_ = runtimeRepo.Save(runtime)\n\tgo reCreateRuntime(runtime)\n\treturn nil\n}\n\nfunc (r *RuntimeService) GetPHPContainerConfig(id uint) (*request.PHPContainerConfig, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\truntimeDTO := response.NewRuntimeDTO(*runtime)\n\tif err := handleRuntimeDTO(&runtimeDTO, *runtime); err != nil {\n\t\treturn nil, err\n\t}\n\tres := &request.PHPContainerConfig{\n\t\tID:            runtime.ID,\n\t\tContainerName: runtime.ContainerName,\n\t\tExposedPorts:  runtimeDTO.ExposedPorts,\n\t\tEnvironments:  runtimeDTO.Environments,\n\t\tVolumes:       runtimeDTO.Volumes,\n\t\tExtraHosts:    runtimeDTO.ExtraHosts,\n\t}\n\treturn res, nil\n}\n\nfunc (r *RuntimeService) GetSupervisorProcess(id uint) ([]response.SupervisorProcessConfig, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfigDir := path.Join(global.Dir.RuntimeDir, \"php\", runtime.Name, \"supervisor\", \"supervisor.d\")\n\treturn handleProcessConfig(configDir, runtime.ContainerName)\n}\n\nfunc (r *RuntimeService) OperateSupervisorProcess(req request.PHPSupervisorProcessConfig) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfigDir := path.Join(global.Dir.RuntimeDir, \"php\", runtime.Name, \"supervisor\")\n\treturn handleProcess(configDir, req.SupervisorProcessConfig, runtime.ContainerName)\n}\n\nfunc (r *RuntimeService) OperateSupervisorProcessFile(req request.PHPSupervisorProcessFileReq) (string, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tsupervisorDir := path.Join(global.Dir.RuntimeDir, \"php\", runtime.Name, \"supervisor\")\n\tconfigDir := path.Join(supervisorDir, \"supervisor.d\")\n\tlogFile := path.Join(supervisorDir, \"log\", fmt.Sprintf(\"%s.out.log\", req.SupervisorProcessFileReq.Name))\n\treturn handleSupervisorFile(req.SupervisorProcessFileReq, configDir, runtime.ContainerName, logFile)\n}\n\nfunc (r *RuntimeService) UpdateRemark(req request.RuntimeRemark) error {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\truntime.Remark = req.Remark\n\treturn runtimeRepo.Save(runtime)\n}\n\nfunc (r *RuntimeService) GetFPMStatus(runtimeID uint) ([]response.FpmStatusItem, error) {\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(runtimeID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfcgiClient, err := fcgiclient.DialTimeout(\"tcp\", \"127.0.0.1:\"+runtime.Port, 10*time.Second)\n\tif err != nil {\n\t\treturn nil, errors.New(\"<UNK> FastCGI <UNK>: \" + err.Error())\n\t}\n\tdefer fcgiClient.Close()\n\n\treqEnv := map[string]string{\n\t\t\"REQUEST_METHOD\":    \"GET\",\n\t\t\"REQUEST_URI\":       \"/status\",\n\t\t\"SCRIPT_FILENAME\":   \"/status\",\n\t\t\"SCRIPT_NAME\":       \"/status\",\n\t\t\"QUERY_STRING\":      \"\",\n\t\t\"CONTENT_TYPE\":      \"\",\n\t\t\"CONTENT_LENGTH\":    \"0\",\n\t\t\"SERVER_SOFTWARE\":   \"go-fcgi-client\",\n\t\t\"SERVER_NAME\":       \"localhost\",\n\t\t\"SERVER_PORT\":       runtime.Port,\n\t\t\"REMOTE_ADDR\":       \"127.0.0.1\",\n\t\t\"GATEWAY_INTERFACE\": \"CGI/1.1\",\n\t}\n\n\tresp, err := fcgiClient.Get(reqEnv)\n\tif err != nil {\n\t\treturn nil, errors.New(\"<UNK> FastCGI <UNK>: \" + err.Error())\n\t}\n\tdefer resp.Body.Close()\n\n\tvar status []response.FpmStatusItem\n\tscanner := bufio.NewScanner(resp.Body)\n\n\tfor scanner.Scan() {\n\t\tline := strings.TrimSpace(scanner.Text())\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.SplitN(line, \":\", 2)\n\t\tif len(parts) != 2 {\n\t\t\tcontinue\n\t\t}\n\t\tkey := strings.TrimSpace(parts[0])\n\t\tvalue := strings.TrimSpace(parts[1])\n\t\tstatus = append(status, response.FpmStatusItem{\n\t\t\tKey:   key,\n\t\t\tValue: value,\n\t\t})\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, errors.New(fmt.Sprintf(\"<UNK> FastCGI <UNK>: %v\", err))\n\t}\n\treturn status, nil\n}\n"
  },
  {
    "path": "agent/app/service/runtime_utils.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\tcmd2 \"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/subosito/gotenv\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc handleRuntime(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {\n\truntimeDir := path.Join(global.Dir.RuntimeDir, create.Type)\n\tif err = fileOp.CopyDir(appVersionDir, runtimeDir); err != nil {\n\t\treturn\n\t}\n\tversionDir := path.Join(runtimeDir, filepath.Base(appVersionDir))\n\tprojectDir := path.Join(runtimeDir, create.Name)\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = fileOp.DeleteDir(projectDir)\n\t\t}\n\t}()\n\tif err = fileOp.Rename(versionDir, projectDir); err != nil {\n\t\treturn\n\t}\n\tcomposeContent, envContent, _, err := handleParams(create, projectDir)\n\tif err != nil {\n\t\treturn\n\t}\n\truntime.DockerCompose = string(composeContent)\n\truntime.Env = string(envContent)\n\truntime.Status = constant.StatusCreating\n\truntime.CodeDir = create.CodeDir\n\n\tnodeDetail, err := appDetailRepo.GetFirst(repo.WithByID(runtime.AppDetailID))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tgo func() {\n\t\tRequestDownloadCallBack(nodeDetail.DownloadCallBackUrl)\n\t}()\n\tgo startRuntime(runtime)\n\n\treturn\n}\n\nfunc handlePHP(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {\n\truntimeDir := path.Join(global.Dir.RuntimeDir, create.Type)\n\tif err = fileOp.CopyDirWithNewName(appVersionDir, runtimeDir, create.Name); err != nil {\n\t\treturn\n\t}\n\tprojectDir := path.Join(runtimeDir, create.Name)\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = fileOp.DeleteDir(projectDir)\n\t\t}\n\t}()\n\n\tversion, ok := create.Params[\"PHP_VERSION\"]\n\tif ok {\n\t\textensionsDir := path.Join(projectDir, \"extensions\", getExtensionDir(version.(string)))\n\t\t_ = fileOp.CreateDir(extensionsDir, 0755)\n\t}\n\n\tcomposeContent, envContent, forms, err := handleParams(create, projectDir)\n\tif err != nil {\n\t\treturn\n\t}\n\truntime.DockerCompose = string(composeContent)\n\truntime.Env = string(envContent)\n\truntime.Params = string(forms)\n\truntime.Status = constant.StatusBuilding\n\n\tgo func() {\n\t\tappDetail, err := appDetailRepo.GetFirst(repo.WithByID(runtime.AppDetailID))\n\t\tif err == nil {\n\t\t\tRequestDownloadCallBack(appDetail.DownloadCallBackUrl)\n\t\t}\n\t}()\n\n\tgo buildRuntime(runtime, \"\", \"\", false)\n\treturn\n}\n\nfunc startRuntime(runtime *model.Runtime) {\n\tif err := runComposeCmdWithLog(\"up\", runtime.GetComposePath(), runtime.GetLogPath()); err != nil {\n\t\truntime.Status = constant.StatusError\n\t\truntime.Message = err.Error()\n\t\t_ = runtimeRepo.Save(runtime)\n\t\treturn\n\t}\n\n\tif err := SyncRuntimeContainerStatus(runtime); err != nil {\n\t\truntime.Status = constant.StatusError\n\t\truntime.Message = err.Error()\n\t\t_ = runtimeRepo.Save(runtime)\n\t\treturn\n\t}\n}\n\nfunc reCreateRuntime(runtime *model.Runtime) {\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\truntime.Status = constant.StatusError\n\t\t\truntime.Message = err.Error()\n\t\t\t_ = runtimeRepo.Save(runtime)\n\t\t}\n\t}()\n\tif err = runComposeCmdWithLog(\"down\", runtime.GetComposePath(), runtime.GetLogPath()); err != nil {\n\t\treturn\n\t}\n\tif err = runComposeCmdWithLog(\"up\", runtime.GetComposePath(), runtime.GetLogPath()); err != nil {\n\t\treturn\n\t}\n\tif err := SyncRuntimeContainerStatus(runtime); err != nil {\n\t\treturn\n\t}\n}\n\nfunc getComposeCmd(composePath, operate string) *exec.Cmd {\n\tdockerCommand := global.CONF.DockerConfig.Command\n\tvar cmd *exec.Cmd\n\tif dockerCommand == \"docker-compose\" {\n\t\tif operate == \"up\" {\n\t\t\tcmd = exec.Command(\"docker-compose\", \"-f\", composePath, operate, \"-d\")\n\t\t} else {\n\t\t\tcmd = exec.Command(\"docker-compose\", \"-f\", composePath, operate)\n\t\t}\n\t} else {\n\t\tif operate == \"up\" {\n\t\t\tcmd = exec.Command(\"docker\", \"compose\", \"-f\", composePath, operate, \"-d\")\n\t\t} else {\n\t\t\tcmd = exec.Command(\"docker\", \"compose\", \"-f\", composePath, operate)\n\t\t}\n\t}\n\treturn cmd\n}\n\nfunc runComposeCmdWithLog(operate string, composePath string, logPath string) error {\n\tcmd := getComposeCmd(composePath, operate)\n\tlogFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, constant.FilePerm)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Failed to open log file: %v\", err)\n\t\treturn err\n\t}\n\tdefer logFile.Close()\n\tmultiWriterStdout := io.MultiWriter(os.Stdout, logFile)\n\tcmd.Stdout = multiWriterStdout\n\tvar stderrBuf bytes.Buffer\n\tmultiWriterStderr := io.MultiWriter(&stderrBuf, logFile, os.Stderr)\n\tcmd.Stderr = multiWriterStderr\n\n\terr = cmd.Run()\n\tif err != nil {\n\t\treturn errors.New(buserr.New(\"ErrRuntimeStart\").Error() + \":\" + stderrBuf.String())\n\t}\n\treturn nil\n}\n\nfunc SyncRuntimesStatus(runtimes []model.Runtime) error {\n\tcli, err := docker.NewClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tvar containerNames []string\n\truntimeContainer := make(map[string]int)\n\tfor index, runtime := range runtimes {\n\t\tcontainerNames = append(containerNames, runtime.ContainerName)\n\t\truntimeContainer[\"/\"+runtime.ContainerName] = index\n\t}\n\tcontainers, err := cli.ListContainersByName(containerNames)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, contain := range containers {\n\t\tif index, ok := runtimeContainer[contain.Names[0]]; ok {\n\t\t\tif runtimes[index].Status == constant.StatusBuilding || runtimes[index].Status == constant.StatusCreating {\n\t\t\t\tdelete(runtimeContainer, contain.Names[0])\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch contain.State {\n\t\t\tcase \"exited\":\n\t\t\t\truntimes[index].Status = constant.StatusError\n\t\t\tcase \"running\":\n\t\t\t\truntimes[index].Status = constant.StatusRunning\n\t\t\tcase \"paused\":\n\t\t\t\truntimes[index].Status = constant.StatusStopped\n\t\t\tcase \"restarting\":\n\t\t\t\truntimes[index].Status = constant.StatusRestarting\n\t\t\t}\n\t\t\t_ = runtimeRepo.Save(&runtimes[index])\n\t\t\tdelete(runtimeContainer, contain.Names[0])\n\t\t}\n\t}\n\tfor _, index := range runtimeContainer {\n\t\tif runtimes[index].Status != constant.StatusBuilding && runtimes[index].Status != constant.StatusCreating {\n\t\t\truntimes[index].Status = constant.StatusStopped\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc SyncRuntimeContainerStatus(runtime *model.Runtime) error {\n\tenv, err := gotenv.Unmarshal(runtime.Env)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar containerNames []string\n\tif containerName, ok := env[\"CONTAINER_NAME\"]; !ok {\n\t\treturn buserr.New(\"ErrContainerNameNotFound\")\n\t} else {\n\t\tcontainerNames = append(containerNames, containerName)\n\t}\n\tcli, err := docker.NewClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tcontainers, err := cli.ListContainersByName(containerNames)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(containers) == 0 {\n\t\treturn buserr.WithNameAndErr(\"ErrContainerNotFound\", containerNames[0], nil)\n\t}\n\tcontainer := containers[0]\n\n\tswitch container.State {\n\tcase \"exited\":\n\t\truntime.Status = constant.StatusError\n\tcase \"running\":\n\t\truntime.Status = constant.StatusRunning\n\tcase \"paused\":\n\t\truntime.Status = constant.StatusStopped\n\tdefault:\n\t\tif runtime.Status != constant.StatusBuilding {\n\t\t\truntime.Status = constant.StatusStopped\n\t\t}\n\t}\n\n\treturn runtimeRepo.Save(runtime)\n}\n\nfunc getRuntimeEnv(envStr, key string) string {\n\tenv, err := gotenv.Unmarshal(envStr)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tif v, ok := env[key]; ok {\n\t\treturn v\n\t}\n\treturn \"\"\n}\n\nfunc deleteImageByID(oldImageID, imageName string, client docker.Client) {\n\tnewImageID, err := client.GetImageIDByName(imageName)\n\tif err == nil && newImageID != oldImageID {\n\t\tglobal.LOG.Infof(\"delete imageID [%s] \", oldImageID)\n\t\tif err := client.DeleteImage(oldImageID); err != nil {\n\t\t\tglobal.LOG.Errorf(\"delete imageID [%s] error %v\", oldImageID, err)\n\t\t} else {\n\t\t\tglobal.LOG.Infof(\"delete old image success\")\n\t\t}\n\t}\n}\n\nfunc buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebuild bool) {\n\truntimePath := runtime.GetPath()\n\tcomposePath := runtime.GetComposePath()\n\tlogPath := path.Join(runtimePath, \"build.log\")\n\n\tlogFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"failed to open log file: %v\", err)\n\t\treturn\n\t}\n\tdefer func() {\n\t\t_ = logFile.Close()\n\t}()\n\n\tnewPHPVersion := getRuntimeEnv(runtime.Env, \"PHP_VERSION\")\n\toldPHPVersion := getRuntimeEnv(oldEnv, \"PHP_VERSION\")\n\tif newPHPVersion != oldPHPVersion {\n\t\t_ = os.Rename(path.Join(runtimePath, \"extensions\", getExtensionDir(oldPHPVersion)), path.Join(runtimePath, \"extensions\", getExtensionDir(newPHPVersion)))\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Hour)\n\tdefer cancel()\n\n\tdockerCommand := global.CONF.DockerConfig.Command\n\tvar cmd *exec.Cmd\n\tif dockerCommand == \"docker-compose\" {\n\t\tcmd = exec.CommandContext(ctx, \"docker-compose\", \"-f\", composePath, \"build\")\n\t} else {\n\t\tcmd = exec.CommandContext(ctx, \"docker\", \"compose\", \"-f\", composePath, \"build\")\n\t}\n\tcmd.Stdout = logFile\n\tvar stderrBuf bytes.Buffer\n\tmultiWriterStderr := io.MultiWriter(&stderrBuf, logFile)\n\tcmd.Stderr = multiWriterStderr\n\n\terr = cmd.Run()\n\tif err != nil {\n\t\truntime.Status = constant.StatusError\n\t\truntime.Message = buserr.New(\"ErrImageBuildErr\").Error() + \":\" + stderrBuf.String()\n\t\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\t\truntime.Message = buserr.New(\"ErrImageBuildErr\").Error() + \":\" + buserr.New(\"ErrCmdTimeout\").Error()\n\t\t} else {\n\t\t\truntime.Message = buserr.New(\"ErrImageBuildErr\").Error() + \":\" + stderrBuf.String()\n\t\t}\n\t\t_ = runtimeRepo.Save(runtime)\n\t\treturn\n\t}\n\tif err = runComposeCmdWithLog(constant.RuntimeDown, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {\n\t\treturn\n\t}\n\tclient, err := docker.NewClient()\n\tif err != nil {\n\t\t_, _ = logFile.WriteString(fmt.Sprintf(\"failed to connect to docker client: %v\", err))\n\t\treturn\n\t}\n\truntime.Message = \"\"\n\tif rebuild && runtime.ID > 0 {\n\t\textensionsStr := getRuntimeEnv(runtime.Env, \"PHP_EXTENSIONS\")\n\t\textensionsArray := strings.Split(extensionsStr, \",\")\n\t\toldExtensionStr := getRuntimeEnv(oldEnv, \"PHP_EXTENSIONS\")\n\t\toldExtensionArray := strings.Split(oldExtensionStr, \",\")\n\t\tvar delExtensions []string\n\t\tfor _, oldExt := range oldExtensionArray {\n\t\t\texist := false\n\t\t\tfor _, ext := range extensionsArray {\n\t\t\t\tif oldExt == ext {\n\t\t\t\t\texist = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !exist {\n\t\t\t\tdelExtensions = append(delExtensions, oldExt)\n\t\t\t}\n\t\t}\n\t\tif err = unInstallPHPExtension(runtime, delExtensions); err != nil {\n\t\t\t_, _ = logFile.WriteString(fmt.Sprintf(\"unInstallPHPExtension error %v\", err))\n\t\t}\n\t}\n\n\tdefer func() {\n\t\t_ = runtimeRepo.Save(runtime)\n\t}()\n\n\tif out, err := compose.Up(composePath); err != nil {\n\t\truntime.Status = constant.StatusStartErr\n\t\truntime.Message = out\n\t\treturn\n\t}\n\tdeleteImageID := \"\"\n\textensions := getRuntimeEnv(runtime.Env, \"PHP_EXTENSIONS\")\n\tif extensions != \"\" {\n\t\tdeleteImageID, _ = client.GetImageIDByName(runtime.Image)\n\t\tcmdMgr := cmd2.NewCommandMgr(cmd2.WithTimeout(60*time.Minute), cmd2.WithOutputFile(logPath))\n\t\tif err = cmdMgr.Run(\"docker\", \"exec\", \"-i\", runtime.ContainerName, \"install-ext\", extensions); err != nil {\n\t\t\truntime.Status = constant.StatusError\n\t\t\truntime.Message = buserr.New(\"ErrImageBuildErr\").Error() + \":\" + err.Error()\n\t\t\treturn\n\t\t}\n\t\tcommitMgr := cmd2.NewCommandMgr(cmd2.WithTimeout(10*time.Minute), cmd2.WithOutputFile(logPath))\n\t\terr = commitMgr.Run(\"docker\", \"commit\", runtime.ContainerName, runtime.Image)\n\t\tif err != nil {\n\t\t\truntime.Status = constant.StatusError\n\t\t\truntime.Message = buserr.New(\"ErrImageBuildErr\").Error() + \":\" + err.Error()\n\t\t\treturn\n\t\t}\n\t}\n\tif oldImageID != \"\" {\n\t\tdeleteImageByID(oldImageID, runtime.Image, client)\n\t}\n\tif deleteImageID != \"\" {\n\t\tdeleteImageByID(deleteImageID, runtime.Image, client)\n\t}\n\thandlePHPDir(*runtime)\n\tif out, err := compose.DownAndUp(composePath); err != nil {\n\t\truntime.Status = constant.StatusStartErr\n\t\truntime.Message = out\n\t\treturn\n\t}\n\truntime.Status = constant.StatusRunning\n\t_ = runtimeRepo.Save(runtime)\n}\n\nfunc handleParams(create request.RuntimeCreate, projectDir string) (composeContent []byte, envContent []byte, forms []byte, err error) {\n\tfileOp := files.NewFileOp()\n\tcomposeContent, err = fileOp.GetContent(path.Join(projectDir, \"docker-compose.yml\"))\n\tif err != nil {\n\t\treturn\n\t}\n\tenvPath := path.Join(projectDir, \".env\")\n\tif !fileOp.Stat(envPath) {\n\t\t_ = fileOp.CreateFile(envPath)\n\t}\n\tenv, err := gotenv.Read(envPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tfor k := range env {\n\t\tif strings.HasPrefix(k, \"CONTAINER_PORT_\") || strings.HasPrefix(k, \"HOST_PORT_\") || strings.HasPrefix(k, \"HOST_IP_\") || strings.Contains(k, \"APP_PORT\") {\n\t\t\tdelete(env, k)\n\t\t}\n\t}\n\tswitch create.Type {\n\tcase constant.RuntimePHP:\n\t\tcreate.Params[\"IMAGE_NAME\"] = create.Image\n\t\tvar fromYml []byte\n\t\tfromYml, err = fileOp.GetContent(path.Join(projectDir, \"data.yml\"))\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar data dto.PHPForm\n\t\terr = yaml.Unmarshal(fromYml, &data)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tformFields := data.AdditionalProperties.FormFields\n\t\tforms, err = json.MarshalIndent(map[string]interface{}{\n\t\t\t\"formFields\": formFields,\n\t\t}, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif extends, ok := create.Params[\"PHP_EXTENSIONS\"]; ok {\n\t\t\tif extendsArray, ok := extends.([]interface{}); ok {\n\t\t\t\tstrArray := make([]string, len(extendsArray))\n\t\t\t\tfor i, v := range extendsArray {\n\t\t\t\t\tstrArray[i] = strings.ToLower(fmt.Sprintf(\"%v\", v))\n\t\t\t\t}\n\t\t\t\tcreate.Params[\"PHP_EXTENSIONS\"] = strings.Join(strArray, \",\")\n\t\t\t}\n\t\t}\n\t\tcreate.Params[\"CONTAINER_PACKAGE_URL\"] = create.Source\n\t\tsiteDir, _ := settingRepo.Get(settingRepo.WithByKey(\"WEBSITE_DIR\"))\n\t\tif siteDir.Value == \"\" {\n\t\t\tsiteDir.Value = path.Join(global.Dir.BaseDir, \"1panel\", \"www\")\n\t\t}\n\t\tcreate.Params[\"PANEL_WEBSITE_DIR\"] = siteDir.Value\n\t\tcomposeContent, err = handleEnvironments(composeContent, create, projectDir)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase constant.RuntimeNode:\n\t\tcreate.Params[\"CODE_DIR\"] = create.CodeDir\n\t\tcreate.Params[\"NODE_VERSION\"] = create.Version\n\t\tif create.NodeConfig.Install {\n\t\t\tcreate.Params[\"RUN_INSTALL\"] = \"1\"\n\t\t} else {\n\t\t\tcreate.Params[\"RUN_INSTALL\"] = \"0\"\n\t\t}\n\t\tcreate.Params[\"CONTAINER_PACKAGE_URL\"] = create.Source\n\t\tcomposeContent, err = handleCompose(env, composeContent, create, projectDir)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase constant.RuntimeJava:\n\t\tcreate.Params[\"CODE_DIR\"] = create.CodeDir\n\t\tcreate.Params[\"JAVA_VERSION\"] = create.Version\n\t\tcomposeContent, err = handleCompose(env, composeContent, create, projectDir)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase constant.RuntimeGo:\n\t\tcreate.Params[\"CODE_DIR\"] = create.CodeDir\n\t\tcreate.Params[\"GO_VERSION\"] = create.Version\n\t\tcomposeContent, err = handleCompose(env, composeContent, create, projectDir)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase constant.RuntimePython:\n\t\tcreate.Params[\"CODE_DIR\"] = create.CodeDir\n\t\tcreate.Params[\"PYTHON_VERSION\"] = create.Version\n\t\tcomposeContent, err = handleCompose(env, composeContent, create, projectDir)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase constant.RuntimeDotNet:\n\t\tcreate.Params[\"CODE_DIR\"] = create.CodeDir\n\t\tcreate.Params[\"DOTNET_VERSION\"] = create.Version\n\t\tcomposeContent, err = handleCompose(env, composeContent, create, projectDir)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tnewMap := make(map[string]string)\n\thandleMap(create.Params, newMap)\n\tmaps.Copy(env, newMap)\n\n\tenvStr, err := gotenv.Marshal(env)\n\tif err != nil {\n\t\treturn\n\t}\n\tif err = gotenv.Write(env, envPath); err != nil {\n\t\treturn\n\t}\n\tenvContent = []byte(envStr)\n\treturn\n}\n\nfunc handleEnvironments(composeContent []byte, create request.RuntimeCreate, projectDir string) (composeByte []byte, err error) {\n\tcomposeMap := make(map[string]interface{})\n\tif err = yaml.Unmarshal(composeContent, &composeMap); err != nil {\n\t\treturn\n\t}\n\tservices, serviceValid := composeMap[\"services\"].(map[string]interface{})\n\tif !serviceValid {\n\t\terr = buserr.New(\"ErrFileParse\")\n\t\treturn\n\t}\n\tserviceName := \"\"\n\tserviceValue := make(map[string]interface{})\n\tfor name, service := range services {\n\t\tserviceName = name\n\t\tserviceValue = service.(map[string]interface{})\n\t\tvar environments []interface{}\n\t\tfor _, e := range create.Environments {\n\t\t\tenvironments = append(environments, fmt.Sprintf(\"%s=%s\", e.Key, e.Value))\n\t\t}\n\t\tdelete(serviceValue, \"environment\")\n\t\tif len(environments) > 0 {\n\t\t\tserviceValue[\"environment\"] = environments\n\t\t}\n\t\tbreak\n\t}\n\tservices[serviceName] = serviceValue\n\tcomposeMap[\"services\"] = services\n\tcomposeByte, err = yaml.Marshal(composeMap)\n\tif err != nil {\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\t_ = fileOp.SaveFile(path.Join(projectDir, \"docker-compose.yml\"), string(composeByte), constant.DirPerm)\n\treturn\n}\n\nfunc handleCompose(env gotenv.Env, composeContent []byte, create request.RuntimeCreate, projectDir string) (composeByte []byte, err error) {\n\texistMap := make(map[string]interface{})\n\tcomposeMap := make(map[string]interface{})\n\tif err = yaml.Unmarshal(composeContent, &composeMap); err != nil {\n\t\treturn\n\t}\n\tservices, serviceValid := composeMap[\"services\"].(map[string]interface{})\n\tif !serviceValid {\n\t\terr = buserr.New(\"ErrFileParse\")\n\t\treturn\n\t}\n\tserviceName := \"\"\n\tserviceValue := make(map[string]interface{})\n\tfor name, service := range services {\n\t\tserviceName = name\n\t\tserviceValue = service.(map[string]interface{})\n\t\tdelete(serviceValue, \"ports\")\n\t\tif len(create.ExposedPorts) > 0 {\n\t\t\tvar ports []interface{}\n\t\t\tfor i, port := range create.ExposedPorts {\n\t\t\t\tcontainerPortStr := fmt.Sprintf(\"CONTAINER_PORT_%d\", i)\n\t\t\t\thostPortStr := fmt.Sprintf(\"HOST_PORT_%d\", i)\n\t\t\t\texistMap[containerPortStr] = struct{}{}\n\t\t\t\texistMap[hostPortStr] = struct{}{}\n\t\t\t\thostIPStr := fmt.Sprintf(\"HOST_IP_%d\", i)\n\t\t\t\tports = append(ports, fmt.Sprintf(\"${%s}:${%s}:${%s}\", hostIPStr, hostPortStr, containerPortStr))\n\t\t\t\tcreate.Params[containerPortStr] = port.ContainerPort\n\t\t\t\tcreate.Params[hostPortStr] = port.HostPort\n\t\t\t\tcreate.Params[hostIPStr] = port.HostIP\n\t\t\t}\n\t\t\tif create.Type == constant.RuntimePHP {\n\t\t\t\tports = append(ports, \"127.0.0.1:${PANEL_APP_PORT_HTTP}:9000\")\n\t\t\t}\n\t\t\tserviceValue[\"ports\"] = ports\n\t\t} else {\n\t\t\tif create.Type == constant.RuntimePHP {\n\t\t\t\tserviceValue[\"ports\"] = []interface{}{\"127.0.0.1:${PANEL_APP_PORT_HTTP}:9000\"}\n\t\t\t}\n\t\t}\n\t\tvar environments []interface{}\n\t\tfor _, e := range create.Environments {\n\t\t\tenvironments = append(environments, fmt.Sprintf(\"%s=%s\", e.Key, e.Value))\n\t\t}\n\t\tdelete(serviceValue, \"environment\")\n\t\tif len(environments) > 0 {\n\t\t\tserviceValue[\"environment\"] = environments\n\t\t}\n\t\tvar volumes []interface{}\n\t\tdefaultVolumes := make(map[string]string)\n\t\tswitch create.Type {\n\t\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimePython, constant.RuntimeDotNet:\n\t\t\tdefaultVolumes = constant.RuntimeDefaultVolumes\n\t\tcase constant.RuntimeGo:\n\t\t\tdefaultVolumes = constant.GoDefaultVolumes\n\t\tcase constant.RuntimePHP:\n\t\t\tdefaultVolumes = constant.PHPDefaultVolumes\n\t\t}\n\t\tfor k, v := range defaultVolumes {\n\t\t\tvolumes = append(volumes, fmt.Sprintf(\"%s:%s\", k, v))\n\t\t}\n\t\tfor _, volume := range create.Volumes {\n\t\t\tvolumes = append(volumes, fmt.Sprintf(\"%s:%s\", volume.Source, volume.Target))\n\t\t}\n\n\t\tvar extraHosts []interface{}\n\t\tfor _, host := range create.ExtraHosts {\n\t\t\textraHosts = append(extraHosts, fmt.Sprintf(\"%s:%s\", host.Hostname, host.IP))\n\t\t}\n\t\tdelete(serviceValue, \"extraHosts\")\n\t\tif len(extraHosts) > 0 {\n\t\t\tserviceValue[\"extra_hosts\"] = extraHosts\n\t\t}\n\n\t\tserviceValue[\"volumes\"] = volumes\n\t\tbreak\n\t}\n\tfor k := range env {\n\t\tif strings.Contains(k, \"CONTAINER_PORT_\") || strings.Contains(k, \"HOST_PORT_\") {\n\t\t\tif _, ok := existMap[k]; !ok {\n\t\t\t\tdelete(env, k)\n\t\t\t}\n\t\t}\n\t}\n\n\tservices[serviceName] = serviceValue\n\tcomposeMap[\"services\"] = services\n\tcomposeByte, err = yaml.Marshal(composeMap)\n\tif err != nil {\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\t_ = fileOp.SaveFile(path.Join(projectDir, \"docker-compose.yml\"), string(composeByte), constant.DirPerm)\n\treturn\n}\n\nfunc checkContainerName(name string) error {\n\tdockerCli, err := docker.NewClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer dockerCli.Close()\n\tnames, err := dockerCli.ListContainersByName([]string{name})\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(names) > 0 {\n\t\treturn buserr.New(\"ErrContainerName\")\n\t}\n\treturn nil\n}\n\nfunc checkContainerStatus(name string) (string, error) {\n\tdockerCli, err := docker.NewClient()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer dockerCli.Close()\n\tnames, err := dockerCli.ListContainersByName([]string{name})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(names) > 0 {\n\t\treturn names[0].State, nil\n\t}\n\treturn \"\", nil\n}\n\nfunc delPHPExtensions(dir, phpExtensionDir, fileName, extName string) {\n\tfileOp := files.NewFileOp()\n\t_ = fileOp.DeleteFile(path.Join(phpExtensionDir, fileName))\n\t_ = fileOp.DeleteFile(path.Join(dir, \"conf\", \"conf.d\", \"docker-php-ext-\"+extName+\".ini\"))\n\t_ = removePHPIniExt(path.Join(dir, \"conf\", \"php.ini\"), fileName)\n}\n\nfunc unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error {\n\tdir := runtime.GetPath()\n\tvar phpExtensions []response.SupportExtension\n\tif err := json.Unmarshal(nginx_conf.GetWebsiteFile(\"php_extensions.json\"), &phpExtensions); err != nil {\n\t\treturn err\n\t}\n\tphpVersion := getRuntimeEnv(runtime.Env, \"PHP_VERSION\")\n\tphpExtensionDir := path.Join(dir, \"extensions\", getExtensionDir(phpVersion))\n\n\tdelMap := make(map[string]struct{})\n\tfor _, del := range delExtensions {\n\t\texist := false\n\t\tfor _, ext := range phpExtensions {\n\t\t\tif ext.Name == del {\n\t\t\t\texist = true\n\t\t\t\tdelMap[ext.Check] = struct{}{}\n\t\t\t\tdelPHPExtensions(dir, phpExtensionDir, ext.File, ext.Name)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !exist {\n\t\t\tdelMap[del] = struct{}{}\n\t\t\tdelPHPExtensions(dir, phpExtensionDir, del+\".so\", del)\n\t\t}\n\t}\n\textensions := getRuntimeEnv(runtime.Env, \"PHP_EXTENSIONS\")\n\tvar (\n\t\toldExts []string\n\t\tnewExts []string\n\t)\n\toldExts = strings.Split(extensions, \",\")\n\tfor _, ext := range oldExts {\n\t\tif _, ok := delMap[ext]; !ok {\n\t\t\tnewExts = append(newExts, ext)\n\t\t}\n\t}\n\tnewExtensions := strings.Join(newExts, \",\")\n\tenvs, err := gotenv.Unmarshal(runtime.Env)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenvs[\"PHP_EXTENSIONS\"] = newExtensions\n\tif err = gotenv.Write(envs, runtime.GetEnvPath()); err != nil {\n\t\treturn err\n\t}\n\tenvContent, err := gotenv.Marshal(envs)\n\tif err != nil {\n\t\treturn err\n\t}\n\truntime.Env = envContent\n\treturn nil\n}\n\nfunc removePHPIniExt(filePath, extensionName string) error {\n\tfile, err := os.Open(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\ttargetLine1 := `extension=\"` + extensionName + `\"`\n\ttargetLine2 := `zend_extension=\"` + extensionName + `\"`\n\n\ttempFile, err := os.CreateTemp(path.Dir(filePath), \"temp_*.txt\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer tempFile.Close()\n\n\tscanner := bufio.NewScanner(file)\n\twriter := bufio.NewWriter(tempFile)\n\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tif !strings.Contains(line, targetLine1) && !strings.Contains(line, targetLine2) {\n\t\t\t_, err := writer.WriteString(line + \"\\n\")\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\n\treturn os.Rename(tempFile.Name(), filePath)\n}\n\nfunc restartRuntime(runtime *model.Runtime) (err error) {\n\tif err = runComposeCmdWithLog(constant.RuntimeDown, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {\n\t\treturn\n\t}\n\tif err = runComposeCmdWithLog(constant.RuntimeUp, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getDockerComposeEnvironments(yml []byte) ([]request.Environment, error) {\n\tvar (\n\t\tcomposeProject docker.ComposeProject\n\t\terr            error\n\t)\n\terr = yaml.Unmarshal(yml, &composeProject)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []request.Environment\n\tfor _, service := range composeProject.Services {\n\t\tfor key, value := range service.Environment.Variables {\n\t\t\tres = append(res, request.Environment{\n\t\t\t\tKey:   key,\n\t\t\t\tValue: value,\n\t\t\t})\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc getDockerComposeVolumes(yml []byte) ([]request.Volume, error) {\n\tvar (\n\t\tcomposeProject docker.ComposeProject\n\t\terr            error\n\t)\n\terr = yaml.Unmarshal(yml, &composeProject)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []request.Volume\n\tfor _, service := range composeProject.Services {\n\t\tfor _, volume := range service.Volumes {\n\t\t\tenvArray := strings.Split(volume, \":\")\n\t\t\tsource := envArray[0]\n\t\t\ttarget := \"\"\n\t\t\tif len(envArray) > 1 {\n\t\t\t\ttarget = envArray[1]\n\t\t\t}\n\t\t\tres = append(res, request.Volume{\n\t\t\t\tSource: source,\n\t\t\t\tTarget: target,\n\t\t\t})\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc getDockerComposeExtraHosts(yml []byte) ([]request.ExtraHost, error) {\n\tvar (\n\t\tcomposeProject docker.ComposeProject\n\t\terr            error\n\t)\n\terr = yaml.Unmarshal(yml, &composeProject)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []request.ExtraHost\n\tfor _, service := range composeProject.Services {\n\t\tfor _, extraHosts := range service.ExtraHosts {\n\t\t\tenvArray := strings.Split(extraHosts, \":\")\n\t\t\tsource := envArray[0]\n\t\t\ttarget := \"\"\n\t\t\tif len(envArray) > 1 {\n\t\t\t\ttarget = envArray[1]\n\t\t\t}\n\t\t\tres = append(res, request.ExtraHost{\n\t\t\t\tHostname: source,\n\t\t\t\tIP:       target,\n\t\t\t})\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc checkRuntimePortExist(port int, scanPort bool, runtimeID uint) error {\n\terrMap := make(map[string]interface{})\n\terrMap[\"port\"] = port\n\tappInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))\n\tif appInstall.ID > 0 {\n\t\terrMap[\"type\"] = i18n.GetMsgByKey(\"TYPE_APP\")\n\t\terrMap[\"name\"] = appInstall.Name\n\t\treturn buserr.WithMap(\"ErrPortExist\", errMap, nil)\n\t}\n\topts := []repo.DBOption{runtimeRepo.WithPort(port)}\n\tif runtimeID > 0 {\n\t\topts = append(opts, repo.WithByNOTID(runtimeID))\n\t}\n\truntime, _ := runtimeRepo.GetFirst(context.Background(), opts...)\n\tif runtime != nil {\n\t\terrMap[\"type\"] = i18n.GetMsgByKey(\"TYPE_RUNTIME\")\n\t\terrMap[\"name\"] = runtime.Name\n\t\treturn buserr.WithMap(\"ErrPortExist\", errMap, nil)\n\t}\n\tdomain, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithPort(port))\n\tif domain.ID > 0 {\n\t\terrMap[\"type\"] = i18n.GetMsgByKey(\"TYPE_DOMAIN\")\n\t\terrMap[\"name\"] = domain.Domain\n\t\treturn buserr.WithMap(\"ErrPortExist\", errMap, nil)\n\t}\n\tif scanPort && common.ScanPort(port) {\n\t\treturn buserr.WithDetail(\"ErrPortInUsed\", port, nil)\n\t}\n\treturn nil\n}\n\nfunc getExtensionDir(version string) string {\n\tif strings.HasPrefix(version, \"8.4\") {\n\t\treturn \"no-debug-non-zts-20240924\"\n\t}\n\tif strings.HasPrefix(version, \"8.3\") {\n\t\treturn \"no-debug-non-zts-20230831\"\n\t}\n\tif strings.HasPrefix(version, \"8.2\") {\n\t\treturn \"no-debug-non-zts-20220829\"\n\t}\n\tif strings.HasPrefix(version, \"8.1\") {\n\t\treturn \"no-debug-non-zts-20210902\"\n\t}\n\tif strings.HasPrefix(version, \"8.0\") {\n\t\treturn \"no-debug-non-zts-20200930\"\n\t}\n\tif strings.HasPrefix(version, \"7.4\") {\n\t\treturn \"no-debug-non-zts-20190902\"\n\t}\n\tif strings.HasPrefix(version, \"7.3\") {\n\t\treturn \"no-debug-non-zts-20180731\"\n\t}\n\tif strings.HasPrefix(version, \"7.2\") {\n\t\treturn \"no-debug-non-zts-20170718\"\n\t}\n\tif strings.HasPrefix(version, \"7.1\") {\n\t\treturn \"no-debug-non-zts-20160303\"\n\t}\n\tif strings.HasPrefix(version, \"7.0\") {\n\t\treturn \"no-debug-non-zts-20151012\"\n\t}\n\tif strings.HasPrefix(version, \"5.6\") {\n\t\treturn \"no-debug-non-zts-20131226\"\n\t}\n\treturn \"\"\n}\n\nfunc RestartPHPRuntime() {\n\truntimes, err := runtimeRepo.List(repo.WithByType(constant.RuntimePHP))\n\tif err != nil {\n\t\treturn\n\t}\n\twebsiteDir, _ := settingRepo.GetValueByKey(\"WEBSITE_DIR\")\n\tfor _, runtime := range runtimes {\n\t\tenvs, err := gotenv.Unmarshal(runtime.Env)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Warningf(\"restart php runtime failed %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tenvs[\"PANEL_WEBSITE_DIR\"] = websiteDir\n\t\tif err = gotenv.Write(envs, runtime.GetEnvPath()); err != nil {\n\t\t\tglobal.LOG.Warningf(\"restart php runtime failed %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tgo func() {\n\t\t\t_ = restartRuntime(&runtime)\n\t\t}()\n\t}\n}\n\nfunc handleRuntimeDTO(res *response.RuntimeDTO, runtime model.Runtime) error {\n\tres.Params = make(map[string]interface{})\n\tenvs, err := gotenv.Unmarshal(runtime.Env)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor k, v := range envs {\n\t\tif strings.Contains(k, \"CONTAINER_PORT\") || strings.Contains(k, \"HOST_PORT\") {\n\t\t\tif strings.Contains(k, \"CONTAINER_PORT\") {\n\t\t\t\tmatches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k)\n\t\t\t\tif len(matches) < 2 {\n\t\t\t\t\treturn fmt.Errorf(\"invalid container port key: %s\", k)\n\t\t\t\t}\n\t\t\t\tcontainerPort, err := strconv.Atoi(v)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\thostPort, err := strconv.Atoi(envs[fmt.Sprintf(\"HOST_PORT_%s\", matches[1])])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\thostIP := envs[fmt.Sprintf(\"HOST_IP_%s\", matches[1])]\n\t\t\t\tres.ExposedPorts = append(res.ExposedPorts, request.ExposedPort{\n\t\t\t\t\tContainerPort: containerPort,\n\t\t\t\t\tHostPort:      hostPort,\n\t\t\t\t\tHostIP:        hostIP,\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\tres.Params[k] = v\n\t\t}\n\t}\n\tif v, ok := envs[\"CONTAINER_PACKAGE_URL\"]; ok {\n\t\tres.Source = v\n\t}\n\tcomposeByte, err := files.NewFileOp().GetContent(runtime.GetComposePath())\n\tif err != nil {\n\t\treturn err\n\t}\n\tres.Environments, err = getDockerComposeEnvironments(composeByte)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvolumes, err := getDockerComposeVolumes(composeByte)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tres.ExtraHosts, err = getDockerComposeExtraHosts(composeByte)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefaultVolumes := make(map[string]string)\n\tswitch runtime.Type {\n\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimePython, constant.RuntimeDotNet:\n\t\tdefaultVolumes = constant.RuntimeDefaultVolumes\n\tcase constant.RuntimeGo:\n\t\tdefaultVolumes = constant.GoDefaultVolumes\n\tcase constant.RuntimePHP:\n\t\tdefaultVolumes = constant.PHPDefaultVolumes\n\t}\n\tfor _, volume := range volumes {\n\t\texist := false\n\t\tfor key, value := range defaultVolumes {\n\t\t\tif key == volume.Source && value == volume.Target {\n\t\t\t\texist = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !exist {\n\t\t\tres.Volumes = append(res.Volumes, volume)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc handlePHPDir(runtime model.Runtime) {\n\tfileOp := files.NewFileOp()\n\tdirs := []string{\n\t\tpath.Join(runtime.GetPath(), \"conf\"),\n\t\tpath.Join(runtime.GetPath(), \"extensions\"),\n\t\tpath.Join(runtime.GetPath(), \"composer\"),\n\t\tpath.Join(runtime.GetPath(), \"log\"),\n\t}\n\tfor _, dir := range dirs {\n\t\t_ = fileOp.ChmodR(dir, 0755, true)\n\t\t_ = fileOp.ChownR(dir, strconv.Itoa(1000), strconv.Itoa(1000), true)\n\t}\n}\n\nfunc HandleOldPHPRuntime() {\n\truntimes, _ := runtimeRepo.List(repo.WithByType(constant.RuntimePHP))\n\tif len(runtimes) == 0 {\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\tfor _, runtime := range runtimes {\n\t\tcomposePtah := runtime.GetComposePath()\n\t\tcomposeBytes, _ := fileOp.GetContent(composePtah)\n\t\tcomposeContent := strings.ReplaceAll(string(composeBytes), \"./conf:/usr/local/etc/php\", \"./conf/php.ini:/usr/local/etc/php/php.ini\")\n\t\tcomposeContent = strings.ReplaceAll(composeContent, \"./conf/php-fpm.conf:/usr/local/etc/php-fpm.d/www.conf\", \"./conf/php-fpm.conf:/usr/local/etc/php-fpm.conf\")\n\t\tcomposeContent = strings.ReplaceAll(composeContent, \"./extensions:${EXTENSION_DIR}\", \"./extensions:/usr/local/lib/php/extensions\")\n\t\t_ = fileOp.WriteFile(composePtah, strings.NewReader(composeContent), constant.DirPerm)\n\t\t_ = fileOp.WriteFile(runtime.GetFPMPath(), bytes.NewReader(nginx_conf.GetWebsiteFile(\"php-fpm.conf\")), constant.DirPerm)\n\t\tsupervisorConfigPath := path.Join(runtime.GetPath(), \"supervisor\", \"supervisor.d\", \"php-fpm.ini\")\n\t\tsupervisorConfigBytes, _ := fileOp.GetContent(supervisorConfigPath)\n\t\tif !strings.Contains(string(supervisorConfigBytes), \"nodaemonize\") {\n\t\t\tnewConfigContent := strings.ReplaceAll(string(supervisorConfigBytes), \"command=php-fpm\", \"command=php-fpm --nodaemonize\")\n\t\t\t_ = fileOp.WriteFile(supervisorConfigPath, bytes.NewReader([]byte(newConfigContent)), constant.DirPerm)\n\t\t}\n\t\tgo func() {\n\t\t\t_ = restartRuntime(&runtime)\n\t\t}()\n\t}\n}\n\nfunc getOperation(operate, pkgManager string) string {\n\toperations := map[string][2]string{\n\t\tconstant.RuntimeInstall:   {\"install\", \"add\"},\n\t\tconstant.RuntimeUninstall: {\"uninstall\", \"remove\"},\n\t\tconstant.RuntimeUpdate:    {\"update\", \"upgrade\"},\n\t}\n\n\tops := operations[operate]\n\tif pkgManager == constant.RuntimeNpm {\n\t\treturn ops[0]\n\t}\n\treturn ops[1]\n}\n\nfunc handleRuntimeDetailID(runtime model.Runtime) (uint, uint) {\n\tapp, _ := appRepo.GetFirst(appRepo.WithKey(runtime.Type))\n\tif app.ID == 0 {\n\t\treturn 0, 0\n\t}\n\tappDetail, _ := appDetailRepo.GetFirst(appDetailRepo.WithVersion(runtime.Version), appDetailRepo.WithAppId(app.ID))\n\tif appDetail.ID == 0 {\n\t\treturn 0, 0\n\t}\n\truntime.AppDetailID = appDetail.ID\n\t_ = runtimeRepo.Save(&runtime)\n\treturn app.ID, appDetail.ID\n}\n"
  },
  {
    "path": "agent/app/service/setting.go",
    "content": "package service\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ssh\"\n\tterminalai \"github.com/1Panel-dev/1Panel/agent/utils/terminal/ai\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype SettingService struct{}\n\ntype ISettingService interface {\n\tGetSettingInfo() (*dto.SettingInfo, error)\n\tGetTerminalAIInfo() (*dto.TerminalAIInfo, error)\n\tUpdate(key, value string) error\n\tUpdateTerminalAI(req dto.TerminalAIInfo) error\n\n\tTestConnByInfo(req dto.SSHConnData) bool\n\tSaveConnInfo(req dto.SSHConnData) error\n\tSetDefaultIsConn(req dto.SSHDefaultConn) error\n\tGetSystemProxy() (*dto.SystemProxy, error)\n\tGetLocalConn() dto.SSHConnData\n\tGetSettingByKey(key string) string\n\n\tSaveDescription(req dto.CommonDescription) error\n}\n\nfunc NewISettingService() ISettingService {\n\treturn &SettingService{}\n}\n\nfunc (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {\n\tsetting, err := settingRepo.GetList()\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tsettingMap := make(map[string]string)\n\tfor _, set := range setting {\n\t\tsettingMap[set.Key] = set.Value\n\t}\n\tvar info dto.SettingInfo\n\tarr, err := json.Marshal(settingMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := json.Unmarshal(arr, &info); err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo.LocalTime = time.Now().Format(\"2006-01-02 15:04:05 MST -0700\")\n\treturn &info, err\n}\n\nfunc (u *SettingService) GetTerminalAIInfo() (*dto.TerminalAIInfo, error) {\n\tinfo := &dto.TerminalAIInfo{\n\t\tAIStatus:       constant.StatusDisable,\n\t\tAIAccountID:    \"\",\n\t\tAIPrefix:       \"#\",\n\t\tAIRiskCommands: \"[\\\"rm -rf\\\",\\\"mkfs\\\",\\\"dd if=\\\",\\\"curl | sh\\\",\\\"wget | sh\\\",\\\"chmod -R 777 /\\\",\\\"shutdown\\\",\\\"reboot\\\",\\\"poweroff\\\",\\\"init 0\\\",\\\":(){ :|:& };:\\\"]\",\n\t}\n\n\tif value, err := settingRepo.GetValueByKey(\"AIStatus\"); err == nil && value != \"\" {\n\t\tinfo.AIStatus = value\n\t}\n\tif value, err := settingRepo.GetValueByKey(\"AIAccountID\"); err == nil {\n\t\tinfo.AIAccountID = value\n\t}\n\tif value, err := settingRepo.GetValueByKey(\"AIPrefix\"); err == nil && value != \"\" {\n\t\tinfo.AIPrefix = value\n\t}\n\tif value, err := settingRepo.GetValueByKey(\"AIRiskCommands\"); err == nil && value != \"\" {\n\t\tinfo.AIRiskCommands = value\n\t}\n\n\treturn info, nil\n}\n\nfunc (u *SettingService) Update(key, value string) error {\n\treturn settingRepo.UpdateOrCreate(key, value)\n}\n\nfunc (u *SettingService) UpdateTerminalAI(req dto.TerminalAIInfo) error {\n\tif strings.EqualFold(strings.TrimSpace(req.AIStatus), constant.StatusEnable) {\n\t\taccountID, err := strconv.ParseUint(strings.TrimSpace(req.AIAccountID), 10, 64)\n\t\tif err != nil || accountID == 0 {\n\t\t\treturn buserr.New(\"ErrAgentAccountIDRequired\")\n\t\t}\n\t\tcurrentStatus, _ := settingRepo.GetValueByKey(\"AIStatus\")\n\t\tcurrentAccountID, _ := settingRepo.GetValueByKey(\"AIAccountID\")\n\t\tneedValidate := !strings.EqualFold(strings.TrimSpace(currentStatus), constant.StatusEnable) ||\n\t\t\tstrings.TrimSpace(currentAccountID) != strings.TrimSpace(req.AIAccountID)\n\t\tif needValidate {\n\t\t\tif err := terminalai.ValidateTerminalAccount(uint(accountID)); err != nil {\n\t\t\t\treturn buserr.WithErr(\"ErrAgentAccountUnavailable\", err)\n\t\t\t}\n\t\t}\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"AIStatus\", req.AIStatus); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"AIAccountID\", req.AIAccountID); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"AIPrefix\", req.AIPrefix); err != nil {\n\t\treturn err\n\t}\n\treturn settingRepo.UpdateOrCreate(\"AIRiskCommands\", req.AIRiskCommands)\n}\n\nfunc (u *SettingService) TestConnByInfo(req dto.SSHConnData) bool {\n\tif req.AuthMode == \"password\" && len(req.Password) != 0 {\n\t\tpassword, err := base64.StdEncoding.DecodeString(req.Password)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treq.Password = string(password)\n\t}\n\tif req.AuthMode == \"key\" && len(req.PrivateKey) != 0 {\n\t\tprivateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treq.PrivateKey = string(privateKey)\n\t}\n\n\tvar connInfo ssh.ConnInfo\n\t_ = copier.Copy(&connInfo, &req)\n\tconnInfo.PrivateKey = []byte(req.PrivateKey)\n\tif len(req.PassPhrase) != 0 {\n\t\tconnInfo.PassPhrase = []byte(req.PassPhrase)\n\t}\n\tclient, err := ssh.NewClient(connInfo)\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer client.Close()\n\treturn true\n}\n\nfunc (u *SettingService) SaveConnInfo(req dto.SSHConnData) error {\n\tif req.AuthMode == \"password\" && len(req.Password) != 0 {\n\t\tpassword, err := base64.StdEncoding.DecodeString(req.Password)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treq.Password = string(password)\n\t}\n\tif req.AuthMode == \"key\" && len(req.PrivateKey) != 0 {\n\t\tprivateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treq.PrivateKey = string(privateKey)\n\t}\n\n\tvar connInfo ssh.ConnInfo\n\t_ = copier.Copy(&connInfo, &req)\n\tconnInfo.PrivateKey = []byte(req.PrivateKey)\n\tif len(req.PassPhrase) != 0 {\n\t\tconnInfo.PassPhrase = []byte(req.PassPhrase)\n\t}\n\tclient, err := ssh.NewClient(connInfo)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer client.Close()\n\n\tvar connItem model.LocalConnInfo\n\t_ = copier.Copy(&connItem, &req)\n\tlocalConn, _ := json.Marshal(&connItem)\n\tconnAfterEncrypt, _ := encrypt.StringEncrypt(string(localConn))\n\t_ = settingRepo.Update(\"LocalSSHConn\", connAfterEncrypt)\n\treturn nil\n}\n\nfunc (u *SettingService) SetDefaultIsConn(req dto.SSHDefaultConn) error {\n\tif req.DefaultConn == constant.StatusDisable && req.WithReset {\n\t\tif err := settingRepo.Update(\"LocalSSHConn\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn settingRepo.Update(\"LocalSSHConnShow\", req.DefaultConn)\n}\n\nfunc (u *SettingService) GetSystemProxy() (*dto.SystemProxy, error) {\n\tsystemProxy := dto.SystemProxy{}\n\tsystemProxy.Type, _ = settingRepo.GetValueByKey(\"ProxyType\")\n\tsystemProxy.URL, _ = settingRepo.GetValueByKey(\"ProxyUrl\")\n\tsystemProxy.Port, _ = settingRepo.GetValueByKey(\"ProxyPort\")\n\tsystemProxy.User, _ = settingRepo.GetValueByKey(\"ProxyUser\")\n\tpasswd, _ := settingRepo.GetValueByKey(\"ProxyPasswd\")\n\tsystemProxy.Password, _ = encrypt.StringDecrypt(passwd)\n\treturn &systemProxy, nil\n}\n\nfunc (u *SettingService) GetLocalConn() dto.SSHConnData {\n\tvar data dto.SSHConnData\n\tdata.LocalSSHConnShow, _ = settingRepo.GetValueByKey(\"LocalSSHConnShow\")\n\tconnItem, _ := settingRepo.GetValueByKey(\"LocalSSHConn\")\n\tif len(connItem) == 0 {\n\t\treturn data\n\t}\n\tconnInfoInDB, _ := encrypt.StringDecrypt(connItem)\n\tdata.LocalSSHConnShow, _ = settingRepo.GetValueByKey(\"LocalSSHConnShow\")\n\tif err := json.Unmarshal([]byte(connInfoInDB), &data); err != nil {\n\t\treturn data\n\t}\n\tif len(data.Password) != 0 {\n\t\tdata.Password = base64.StdEncoding.EncodeToString([]byte(data.Password))\n\t}\n\tif len(data.PrivateKey) != 0 {\n\t\tdata.PrivateKey = base64.StdEncoding.EncodeToString([]byte(data.PrivateKey))\n\t}\n\tif len(data.PassPhrase) != 0 {\n\t\tdata.PassPhrase = base64.StdEncoding.EncodeToString([]byte(data.PassPhrase))\n\t}\n\treturn data\n}\n\nfunc (u *SettingService) GetSettingByKey(key string) string {\n\tswitch key {\n\tcase \"LocalSSHConn\":\n\t\tvalue, _ := settingRepo.GetValueByKey(key)\n\t\tif len(value) == 0 {\n\t\t\treturn \"\"\n\t\t}\n\t\titemStr, _ := encrypt.StringDecrypt(value)\n\t\treturn itemStr\n\tdefault:\n\t\tvalue, _ := settingRepo.GetValueByKey(key)\n\t\treturn value\n\t}\n}\n\nfunc (u *SettingService) SaveDescription(req dto.CommonDescription) error {\n\tif len(req.Description) == 0 && !req.IsPinned {\n\t\t_ = settingRepo.DelDescription(req.ID)\n\t\treturn nil\n\t}\n\tdata, _ := settingRepo.GetDescription(settingRepo.WithByDescriptionID(req.ID), repo.WithByType(req.Type), repo.WithByDetailType(req.DetailType))\n\tif data.ID == \"\" {\n\t\tif err := copier.Copy(&data, &req); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn settingRepo.CreateDescription(&data)\n\t}\n\tvalMap := make(map[string]interface{})\n\tvalMap[\"type\"] = req.Type\n\tvalMap[\"detail_type\"] = req.DetailType\n\tvalMap[\"is_pinned\"] = req.IsPinned\n\tvalMap[\"description\"] = req.Description\n\n\treturn settingRepo.UpdateDescription(data.ID, valMap)\n}\n"
  },
  {
    "path": "agent/app/service/snapshot.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\tfileUtils \"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/docker/docker/api/types/image\"\n\t\"github.com/google/uuid\"\n\t\"github.com/jinzhu/copier\"\n\t\"github.com/shirou/gopsutil/v4/host\"\n)\n\ntype SnapshotService struct {\n\tOriginalPath string\n}\n\ntype ISnapshotService interface {\n\tSearchWithPage(req dto.PageSnapshot) (int64, interface{}, error)\n\tLoadSnapshotData() (dto.SnapshotData, error)\n\tSnapshotCreate(parentTask *task.Task, req dto.SnapshotCreate, jobID, retry uint) error\n\tSnapshotReCreate(id uint) error\n\tSnapshotRecover(req dto.SnapshotRecover) error\n\tSnapshotRollback(req dto.SnapshotRecover) error\n\tSnapshotImport(req dto.SnapshotImport) error\n\tDelete(req dto.SnapshotBatchDelete) error\n\n\tUpdateDescription(req dto.UpdateDescription) error\n}\n\nfunc NewISnapshotService() ISnapshotService {\n\treturn &SnapshotService{}\n}\n\nfunc (u *SnapshotService) SearchWithPage(req dto.PageSnapshot) (int64, interface{}, error) {\n\ttotal, records, err := snapshotRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order))\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar datas []dto.SnapshotInfo\n\tfor i := 0; i < len(records); i++ {\n\t\tvar item dto.SnapshotInfo\n\t\tif err := copier.Copy(&item, &records[i]); err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\titem.SourceAccounts, item.DownloadAccount, _ = loadBackupNamesByID(records[i].SourceAccountIDs, records[i].DownloadAccountID)\n\t\tdatas = append(datas, item)\n\t}\n\treturn total, datas, err\n}\n\nfunc (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {\n\tif len(req.Names) == 0 {\n\t\treturn fmt.Errorf(\"incorrect snapshot request body: %v\", req.Names)\n\t}\n\n\tfor _, snapName := range req.Names {\n\t\tif !checkSnapshotIsOk(snapName) {\n\t\t\treturn fmt.Errorf(\"incorrect snapshot name format of %s\", snapName)\n\t\t}\n\t\tsnap, _ := snapshotRepo.Get(repo.WithByName(strings.ReplaceAll(snapName, \".tar.gz\", \"\")))\n\t\tif snap.ID != 0 {\n\t\t\treturn buserr.New(\"ErrRecordExist\")\n\t\t}\n\t}\n\tfor _, snap := range req.Names {\n\t\tshortName := strings.ReplaceAll(snap, \"snapshot-\", \"\")\n\t\tshortName = strings.ReplaceAll(shortName, \"1panel-\", \"\")\n\t\tshortName = strings.ReplaceAll(shortName, \"core-\", \"\")\n\t\tshortName = strings.ReplaceAll(shortName, \"agent-\", \"\")\n\t\tnameItems := strings.Split(shortName, \"-linux\")\n\t\tif strings.HasSuffix(snap, \".tar.gz\") {\n\t\t\tsnap = strings.ReplaceAll(snap, \".tar.gz\", \"\")\n\t\t}\n\t\titemSnap := model.Snapshot{\n\t\t\tName:              snap,\n\t\t\tSourceAccountIDs:  fmt.Sprintf(\"%v\", req.BackupAccountID),\n\t\t\tDownloadAccountID: req.BackupAccountID,\n\t\t\tVersion:           nameItems[0],\n\t\t\tDescription:       req.Description,\n\t\t\tStatus:            constant.StatusSuccess,\n\t\t}\n\t\tif err := snapshotRepo.Create(&itemSnap); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *SnapshotService) LoadSnapshotData() (dto.SnapshotData, error) {\n\tvar (\n\t\tdata dto.SnapshotData\n\t\terr  error\n\t)\n\tfileOp := fileUtils.NewFileOp()\n\tdata.AppData, err = loadApps(fileOp)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tdata.WithDockerConf = true\n\tdata.PanelData, err = loadPanelFile(fileOp)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\titemBackups, err := loadFile(global.Dir.LocalBackupDir, 8, fileOp)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\ti := 0\n\tfor _, item := range itemBackups {\n\t\tif item.Label != \"app\" && item.Label != \"system_snapshot\" && item.Label != \"tmp\" {\n\t\t\titemBackups[i] = item\n\t\t\ti++\n\t\t}\n\t}\n\tdata.BackupData = itemBackups[:i]\n\n\treturn data, nil\n}\n\nfunc (u *SnapshotService) UpdateDescription(req dto.UpdateDescription) error {\n\treturn snapshotRepo.Update(req.ID, map[string]interface{}{\"description\": req.Description})\n}\n\ntype SnapshotJson struct {\n\tBaseDir       string `json:\"baseDir\"`\n\tOperestyDir   string `json:\"operestyDir\"`\n\tBackupDataDir string `json:\"backupDataDir\"`\n\tSize          uint64 `json:\"size\"`\n}\n\nfunc (u *SnapshotService) Delete(req dto.SnapshotBatchDelete) error {\n\tsnaps, _ := snapshotRepo.GetList(repo.WithByIDs(req.Ids))\n\tfor _, snap := range snaps {\n\t\tif req.DeleteWithFile {\n\t\t\taccounts := NewBackupClientMap(strings.Split(snap.SourceAccountIDs, \",\"))\n\t\t\tfor _, item := range accounts {\n\t\t\t\tglobal.LOG.Debugf(\"remove snapshot file %s.tar.gz from %s\", snap.Name, item.name)\n\t\t\t\tif !item.isOk {\n\t\t\t\t\tglobal.LOG.Errorf(\"remove snapshot file %s.tar.gz from %s failed, err: %s\", snap.Name, item.name, item.message)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t_, _ = item.client.Delete(path.Join(item.backupPath, \"system_snapshot\", snap.Name+\".tar.gz\"))\n\t\t\t}\n\t\t\t_ = backupRepo.DeleteRecord(context.Background(), repo.WithByType(\"snapshot\"), backupRepo.WithByFileName(snap.Name+\".tar.gz\"))\n\t\t}\n\t\t_ = os.Remove(path.Join(global.Dir.LocalBackupDir, \"tmp/system\", snap.Name+\".tar.gz\"))\n\n\t\tif err := snapshotRepo.Delete(repo.WithByID(snap.ID)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc hasOs(name string) bool {\n\treturn strings.Contains(name, \"amd64\") ||\n\t\tstrings.Contains(name, \"arm64\") ||\n\t\tstrings.Contains(name, \"armv7\") ||\n\t\tstrings.Contains(name, \"ppc64le\") ||\n\t\tstrings.Contains(name, \"s390x\") ||\n\t\tstrings.Contains(name, \"riscv64\")\n}\n\nfunc loadOs() string {\n\thostInfo, _ := host.Info()\n\tswitch hostInfo.KernelArch {\n\tcase \"x86_64\":\n\t\treturn \"amd64\"\n\tcase \"armv7l\":\n\t\treturn \"armv7\"\n\tdefault:\n\t\treturn hostInfo.KernelArch\n\t}\n}\n\nfunc loadApps(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {\n\tvar data []dto.DataTree\n\tapps, err := appInstallRepo.ListBy(context.Background())\n\tif err != nil {\n\t\treturn data, err\n\t}\n\topenrestyID := 0\n\tfor _, app := range apps {\n\t\tif app.App.Key == constant.AppOpenresty {\n\t\t\topenrestyID = int(app.ID)\n\t\t}\n\t}\n\twebsites, err := websiteRepo.List()\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tappRelationMap := make(map[uint]uint)\n\tfor _, website := range websites {\n\t\tif website.Type == constant.Deployment && website.AppInstallID != 0 {\n\t\t\tappRelationMap[uint(openrestyID)] = website.AppInstallID\n\t\t}\n\t}\n\tappRelations, err := appInstallResourceRepo.GetBy()\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tfor _, relation := range appRelations {\n\t\tappRelationMap[uint(relation.AppInstallId)] = relation.LinkId\n\t}\n\tappMap := make(map[uint]string)\n\tfor _, app := range apps {\n\t\tappMap[app.ID] = fmt.Sprintf(\"%s-%s\", app.App.Key, app.Name)\n\t}\n\n\tappTreeMap := make(map[string]dto.DataTree)\n\tfor _, app := range apps {\n\t\titemApp := dto.DataTree{\n\t\t\tID:      uuid.NewString(),\n\t\t\tLabel:   fmt.Sprintf(\"%s - %s\", app.App.Name, app.Name),\n\t\t\tKey:     app.App.Key,\n\t\t\tName:    app.Name,\n\t\t\tIsLocal: app.App.Resource == \"local\",\n\t\t}\n\t\tappPath := path.Join(global.Dir.DataDir, \"apps\", app.App.Key, app.Name)\n\t\tif itemApp.IsLocal {\n\t\t\tappPath = path.Join(global.Dir.AppDir, \"local\", strings.TrimPrefix(app.App.Key, \"local\"), app.Name)\n\t\t}\n\t\titemAppData := dto.DataTree{ID: uuid.NewString(), Label: \"appData\", Key: app.App.Key, Name: app.Name, IsCheck: true, Path: appPath}\n\t\tif app.App.Key == constant.AppOpenresty && len(websites) != 0 {\n\t\t\titemAppData.IsDisable = true\n\t\t}\n\t\tif val, ok := appRelationMap[app.ID]; ok {\n\t\t\titemAppData.RelationItemID = appMap[val]\n\t\t}\n\t\tsizeItem, err := fileOp.GetDirSize(appPath)\n\t\tif err == nil {\n\t\t\titemAppData.Size = uint64(sizeItem)\n\t\t}\n\t\titemApp.Size += itemAppData.Size\n\t\tdata = append(data, itemApp)\n\t\tappTreeMap[fmt.Sprintf(\"%s-%s\", itemApp.Key, itemApp.Name)] = itemAppData\n\t}\n\n\tfor key, val := range appTreeMap {\n\t\tif valRelation, ok := appTreeMap[val.RelationItemID]; ok {\n\t\t\tvalRelation.IsDisable = true\n\t\t\tappTreeMap[val.RelationItemID] = valRelation\n\n\t\t\tval.RelationItemID = valRelation.ID\n\t\t\tappTreeMap[key] = val\n\t\t}\n\t}\n\tfor i := 0; i < len(data); i++ {\n\t\tif val, ok := appTreeMap[fmt.Sprintf(\"%s-%s\", data[i].Key, data[i].Name)]; ok {\n\t\t\tdata[i].Children = append(data[i].Children, val)\n\t\t}\n\t}\n\tdata = loadAppBackup(data, fileOp)\n\tdata = loadAppImage(data)\n\treturn data, nil\n}\nfunc loadAppBackup(list []dto.DataTree, fileOp fileUtils.FileOp) []dto.DataTree {\n\tfor i := 0; i < len(list); i++ {\n\t\tappBackupPath := path.Join(global.Dir.LocalBackupDir, \"app\", list[i].Key, list[i].Name)\n\t\titemAppBackupTree, err := loadFile(appBackupPath, 8, fileOp)\n\t\titemAppBackup := dto.DataTree{ID: uuid.NewString(), Label: \"appBackup\", IsCheck: true, Children: itemAppBackupTree, Path: appBackupPath}\n\t\tif err == nil {\n\t\t\tbackupSizeItem, err := fileOp.GetDirSize(appBackupPath)\n\t\t\tif err == nil {\n\t\t\t\titemAppBackup.Size = uint64(backupSizeItem)\n\t\t\t\tlist[i].Size += itemAppBackup.Size\n\t\t\t}\n\t\t\tlist[i].Children = append(list[i].Children, itemAppBackup)\n\t\t}\n\t}\n\treturn list\n}\nfunc loadAppImage(list []dto.DataTree) []dto.DataTree {\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new docker client failed, err: %v\", err)\n\t\treturn list\n\t}\n\tdefer client.Close()\n\timageList, err := client.ImageList(context.Background(), image.ListOptions{})\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load image list failed, err: %v\", err)\n\t\treturn list\n\t}\n\n\tfor i := 0; i < len(list); i++ {\n\t\titemAppImage := dto.DataTree{ID: uuid.NewString(), Label: \"appImage\"}\n\t\tappPath := path.Join(global.Dir.DataDir, \"apps\", list[i].Key, list[i].Name)\n\t\tif list[i].IsLocal {\n\t\t\tappPath = path.Join(global.Dir.AppDir, \"local\", strings.TrimPrefix(list[i].Key, \"local\"), list[i].Name)\n\t\t}\n\t\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"cat %s | grep image: \", path.Join(appPath, \"docker-compose.yml\"))\n\t\tif err != nil {\n\t\t\tlist[i].Children = append(list[i].Children, itemAppImage)\n\t\t\tcontinue\n\t\t}\n\t\titemAppImage.Name = strings.ReplaceAll(strings.ReplaceAll(strings.TrimSpace(stdout), \"\\n\", \"\"), \"image: \", \"\")\n\t\tfor _, imageItem := range imageList {\n\t\t\tfor _, tag := range imageItem.RepoTags {\n\t\t\t\tif tag == itemAppImage.Name {\n\t\t\t\t\titemAppImage.Size = uint64(imageItem.Size)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlist[i].Children = append(list[i].Children, itemAppImage)\n\t}\n\treturn list\n}\n\nfunc loadPanelFile(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {\n\tvar data []dto.DataTree\n\tsnapFiles, err := os.ReadDir(global.Dir.DataDir)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tfor _, fileItem := range snapFiles {\n\t\titemData := dto.DataTree{\n\t\t\tID:      uuid.NewString(),\n\t\t\tLabel:   fileItem.Name(),\n\t\t\tIsCheck: true,\n\t\t\tPath:    path.Join(global.Dir.DataDir, fileItem.Name()),\n\t\t}\n\t\tswitch itemData.Label {\n\t\tcase \"runtime\", \"docker\", \"task\", \"geo\", \"secret\", \"uploads\":\n\t\t\titemData.IsDisable = true\n\t\tcase \"clamav\", \"download\":\n\t\t\tpanelPath := path.Join(global.Dir.DataDir, itemData.Label)\n\t\t\titemData.Children, _ = loadFile(panelPath, 3, fileOp)\n\t\tcase \"agent\":\n\t\t\tpanelPath := path.Join(global.Dir.DataDir, itemData.Label)\n\t\t\titemData.Children, _ = loadFile(panelPath, 2, fileOp)\n\t\t\titemData.IsCheck = false\n\t\t\tfor i := 0; i < len(itemData.Children); i++ {\n\t\t\t\tif itemData.Children[i].Label != \"package\" {\n\t\t\t\t\titemData.Children[i].IsDisable = true\n\t\t\t\t} else {\n\t\t\t\t\titemData.Size = -itemData.Children[i].Size\n\t\t\t\t\titemData.Children[i].IsCheck = false\n\t\t\t\t\tfor j := 0; j < len(itemData.Children[i].Children); j++ {\n\t\t\t\t\t\titemData.Children[i].Children[j].IsCheck = false\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"apps\", \"backup\", \"log\", \"db\", \"tmp\":\n\t\t\tcontinue\n\t\t}\n\t\tif fileItem.IsDir() {\n\t\t\tsizeItem, err := fileOp.GetDirSize(path.Join(global.Dir.DataDir, itemData.Label))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\titemData.Size = uint64(int64(sizeItem) + int64(itemData.Size))\n\t\t} else {\n\t\t\tfileInfo, err := fileItem.Info()\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\titemData.Size = uint64(fileInfo.Size())\n\t\t}\n\t\tif itemData.IsCheck && itemData.Size == 0 {\n\t\t\titemData.IsCheck = false\n\t\t\titemData.IsDisable = true\n\t\t}\n\n\t\tdata = append(data, itemData)\n\t}\n\n\topenrestySite, _ := settingRepo.GetValueByKey(\"WEBSITE_DIR\")\n\tif len(openrestySite) != 0 && !strings.Contains(openrestySite, global.Dir.DataDir) {\n\t\tsizeItem, _ := fileOp.GetDirSize(openrestySite)\n\t\tdata = append(data, dto.DataTree{\n\t\t\tID:        uuid.NewString(),\n\t\t\tLabel:     \"www\",\n\t\t\tIsCheck:   true,\n\t\t\tIsDisable: true,\n\t\t\tPath:      openrestySite,\n\t\t\tSize:      uint64(sizeItem),\n\t\t})\n\t}\n\n\treturn data, nil\n}\n\nfunc loadFile(pathItem string, index int, fileOp fileUtils.FileOp) ([]dto.DataTree, error) {\n\tvar data []dto.DataTree\n\tsnapFiles, err := os.ReadDir(pathItem)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tfor _, fileItem := range snapFiles {\n\t\titemData := dto.DataTree{\n\t\t\tID:      uuid.NewString(),\n\t\t\tLabel:   fileItem.Name(),\n\t\t\tName:    fileItem.Name(),\n\t\t\tPath:    path.Join(pathItem, fileItem.Name()),\n\t\t\tIsCheck: true,\n\t\t}\n\t\tif fileItem.IsDir() {\n\t\t\tsizeItem, err := fileOp.GetDirSize(path.Join(pathItem, itemData.Label))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\titemData.Size = uint64(sizeItem)\n\t\t\tif index > 1 {\n\t\t\t\titemData.Children, _ = loadFile(path.Join(pathItem, itemData.Label), index-1, fileOp)\n\t\t\t}\n\t\t} else {\n\t\t\tfileInfo, err := fileItem.Info()\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\titemData.Size = uint64(fileInfo.Size())\n\t\t}\n\t\tdata = append(data, itemData)\n\t}\n\treturn data, nil\n}\n\nfunc checkSnapshotIsOk(name string) bool {\n\tnames := []string{\"1panel-core-v2.\", \"1panel-agent-v2.\", \"1panel-v2.\", \"snapshot-1panel-core-v2.\", \"snapshot-1panel-agent-v2.\"}\n\tfor _, item := range names {\n\t\tif strings.HasPrefix(name, item) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "agent/app/service/snapshot_create.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/docker/docker/api/types/image\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/copier\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"gorm.io/gorm\"\n)\n\nfunc (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.SnapshotCreate, jobID, retry uint) error {\n\tversionItem, _ := settingRepo.Get(settingRepo.WithByKey(\"SystemVersion\"))\n\n\tscope := \"core\"\n\tif !global.IsMaster {\n\t\tscope = \"agent\"\n\t}\n\tif jobID == 0 {\n\t\treq.Name = fmt.Sprintf(\"1panel-%s-%s-linux-%s-%s\", scope, versionItem.Value, loadOs(), time.Now().Format(constant.DateTimeSlimLayout))\n\t}\n\tappItem, _ := json.Marshal(req.AppData)\n\tpanelItem, _ := json.Marshal(req.PanelData)\n\tbackupItem, _ := json.Marshal(req.BackupData)\n\tsnap := model.Snapshot{\n\t\tName:              req.Name,\n\t\tTaskID:            req.TaskID,\n\t\tSecret:            req.Secret,\n\t\tDescription:       req.Description,\n\t\tSourceAccountIDs:  req.SourceAccountIDs,\n\t\tDownloadAccountID: req.DownloadAccountID,\n\n\t\tAppData:          string(appItem),\n\t\tPanelData:        string(panelItem),\n\t\tBackupData:       string(backupItem),\n\t\tWithDockerConf:   req.WithDockerConf,\n\t\tWithMonitorData:  req.WithMonitorData,\n\t\tWithLoginLog:     req.WithLoginLog,\n\t\tWithOperationLog: req.WithOperationLog,\n\t\tWithTaskLog:      req.WithTaskLog,\n\t\tWithSystemLog:    req.WithSystemLog,\n\t\tIgnoreFiles:      strings.Join(req.IgnoreFiles, \",\"),\n\n\t\tVersion: versionItem.Value,\n\t\tStatus:  constant.StatusWaiting,\n\t\tTimeout: req.Timeout,\n\t}\n\tif err := snapshotRepo.Create(&snap); err != nil {\n\t\tglobal.LOG.Errorf(\"create snapshot record to db failed, err: %v\", err)\n\t\treturn err\n\t}\n\n\treq.ID = snap.ID\n\tvar err error\n\ttaskItem := parentTask\n\tif parentTask == nil {\n\t\ttaskItem, err = task.NewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeSnapshot, req.TaskID, req.ID)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"new task for create snapshot failed, err: %v\", err)\n\t\t\treturn err\n\t\t}\n\t}\n\tif jobID == 0 {\n\t\tgo func() {\n\t\t\t_ = handleSnapshot(req, taskItem, jobID, 3, snap.Timeout)\n\t\t}()\n\t\treturn nil\n\t}\n\n\treturn handleSnapshot(req, taskItem, jobID, retry, snap.Timeout)\n}\n\nfunc (u *SnapshotService) SnapshotReCreate(id uint) error {\n\tsnap, err := snapshotRepo.Get(repo.WithByID(id))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar req dto.SnapshotCreate\n\t_ = copier.Copy(&req, snap)\n\tif err := json.Unmarshal([]byte(snap.PanelData), &req.PanelData); err != nil {\n\t\treturn err\n\t}\n\tif err := json.Unmarshal([]byte(snap.AppData), &req.AppData); err != nil {\n\t\treturn err\n\t}\n\tif err := json.Unmarshal([]byte(snap.BackupData), &req.BackupData); err != nil {\n\t\treturn err\n\t}\n\treq.TaskID = snap.TaskID\n\ttaskItem, err := task.ReNewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeSnapshot, req.TaskID, req.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create snapshot failed, err: %v\", err)\n\t\treturn err\n\t}\n\t_ = snapshotRepo.Update(req.ID, map[string]interface{}{\"status\": constant.StatusWaiting, \"message\": \"\"})\n\tgo func() {\n\t\t_ = handleSnapshot(req, taskItem, 0, 3, req.Timeout)\n\t}()\n\n\treturn nil\n}\n\nfunc handleSnapshot(req dto.SnapshotCreate, taskItem *task.Task, jobID, retry, timeout uint) error {\n\trootDir := path.Join(global.Dir.LocalBackupDir, \"tmp/system\", req.Name)\n\topenrestyDir, _ := settingRepo.GetValueByKey(\"WEBSITE_DIR\")\n\titemHelper := snapHelper{SnapID: req.ID, Task: *taskItem, FileOp: files.NewFileOp(), Ctx: context.Background(), OpenrestyDir: openrestyDir}\n\tbaseDir := path.Join(rootDir, \"base\")\n\t_ = os.MkdirAll(baseDir, os.ModePerm)\n\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\"SnapDBInfo\",\n\t\tfunc(t *task.Task) error {\n\t\t\tif err := loadDbConn(&itemHelper, rootDir, req); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_ = itemHelper.snapAgentDB.Where(\"id = ?\", req.ID).Delete(&model.Snapshot{}).Error\n\t\t\tif jobID != 0 {\n\t\t\t\t_ = itemHelper.snapAgentDB.Where(\"id = ?\", jobID).Delete(&model.JobRecords{}).Error\n\t\t\t}\n\t\t\treturn nil\n\t\t}, nil, int(retry), time.Duration(timeout)*time.Second,\n\t)\n\n\tif len(req.InterruptStep) == 0 || req.InterruptStep == \"SnapBaseInfo\" {\n\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\"SnapBaseInfo\",\n\t\t\tfunc(t *task.Task) error { return snapBaseData(itemHelper, baseDir, req.WithDockerConf) },\n\t\t\tnil, int(retry), time.Duration(timeout)*time.Second,\n\t\t)\n\t\treq.InterruptStep = \"\"\n\t}\n\tif len(req.InterruptStep) == 0 || req.InterruptStep == \"SnapInstallApp\" {\n\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\"SnapInstallApp\",\n\t\t\tfunc(t *task.Task) error { return snapAppImage(itemHelper, req, rootDir) },\n\t\t\tnil, int(retry), time.Duration(timeout)*time.Second,\n\t\t)\n\t\treq.InterruptStep = \"\"\n\t}\n\tif len(req.InterruptStep) == 0 || req.InterruptStep == \"SnapLocalBackup\" {\n\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\"SnapLocalBackup\",\n\t\t\tfunc(t *task.Task) error { return snapBackupData(itemHelper, req, rootDir) },\n\t\t\tnil, int(retry), time.Duration(timeout)*time.Second,\n\t\t)\n\t\treq.InterruptStep = \"\"\n\t}\n\tif len(req.InterruptStep) == 0 || req.InterruptStep == \"SnapPanelData\" {\n\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\"SnapPanelData\",\n\t\t\tfunc(t *task.Task) error { return snapPanelData(itemHelper, req, rootDir) },\n\t\t\tnil, int(retry), time.Duration(timeout)*time.Second,\n\t\t)\n\t\treq.InterruptStep = \"\"\n\t}\n\n\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\"SnapCloseDBConn\",\n\t\tfunc(t *task.Task) error {\n\t\t\ttaskItem.Log(\"---------------------- 6 / 8 ----------------------\")\n\t\t\tcommon.CloseDB(itemHelper.snapAgentDB)\n\t\t\tcommon.CloseDB(itemHelper.snapCoreDB)\n\t\t\treturn nil\n\t\t}, nil, int(retry), time.Duration(timeout)*time.Second,\n\t)\n\tif len(req.InterruptStep) == 0 || req.InterruptStep == \"SnapCompress\" {\n\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\"SnapCompress\",\n\t\t\tfunc(t *task.Task) error { return snapCompress(itemHelper, rootDir, req.Secret) },\n\t\t\tnil, int(retry), time.Duration(timeout)*time.Second,\n\t\t)\n\t\treq.InterruptStep = \"\"\n\t}\n\tif len(req.InterruptStep) == 0 || req.InterruptStep == \"SnapUpload\" {\n\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\"SnapUpload\",\n\t\t\tfunc(t *task.Task) error {\n\t\t\t\treturn snapUpload(itemHelper, req.SourceAccountIDs, req.DownloadAccountID, retry, fmt.Sprintf(\"%s.tar.gz\", rootDir))\n\t\t\t}, nil, int(retry), time.Duration(timeout)*time.Second,\n\t\t)\n\t\treq.InterruptStep = \"\"\n\t}\n\tif err := taskItem.Execute(); err != nil {\n\t\t_ = snapshotRepo.Update(req.ID, map[string]interface{}{\"status\": constant.StatusFailed, \"message\": err.Error(), \"interrupt_step\": taskItem.Task.CurrentStep})\n\t\treturn err\n\t}\n\t_ = snapshotRepo.Update(req.ID, map[string]interface{}{\"status\": constant.StatusSuccess, \"interrupt_step\": \"\"})\n\t_ = os.RemoveAll(rootDir)\n\treturn nil\n}\n\ntype snapHelper struct {\n\tSnapID      uint\n\tSnapName    string\n\tsnapAgentDB *gorm.DB\n\tsnapCoreDB  *gorm.DB\n\tCtx         context.Context\n\tFileOp      files.FileOp\n\tWg          *sync.WaitGroup\n\tTask        task.Task\n\n\tOpenrestyDir string\n}\n\nfunc loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) error {\n\tsnap.Task.Log(\"---------------------- 1 / 8 ----------------------\")\n\tsnap.Task.LogStart(i18n.GetMsgByKey(\"SnapDBInfo\"))\n\tpathDB := path.Join(global.Dir.DataDir, \"db\")\n\n\terr := snap.FileOp.CopyDir(pathDB, targetDir)\n\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", pathDB), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = os.Remove(path.Join(targetDir, \"db/session.db\"))\n\n\tagentDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, \"db/agent.db\"), \"agent.db\")\n\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapNewDB\", \"agent\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsnap.snapAgentDB = agentDb\n\t_ = snap.snapAgentDB.Model(&model.Setting{}).Where(\"key = ?\", \"SystemIP\").Updates(map[string]interface{}{\"value\": \"\"}).Error\n\tcoreDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, \"db/core.db\"), \"core.db\")\n\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapNewDB\", \"core\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsnap.snapCoreDB = coreDb\n\n\tif !req.WithMonitorData {\n\t\terr = os.Remove(path.Join(targetDir, \"db/monitor.db\"))\n\t\tsnap.Task.LogWithStatus(i18n.GetMsgByKey(\"SnapDeleteMonitor\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif !req.WithTaskLog {\n\t\terr = os.Remove(path.Join(targetDir, \"db/task.db\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\ttaskDB, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, \"db/task.db\"), \"task.db\")\n\t\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapNewDB\", \"task\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer common.CloseDB(taskDB)\n\t\t_ = taskDB.Where(\"id = ?\", req.TaskID).Delete(&model.Task{}).Error\n\t}\n\tif !req.WithOperationLog && global.IsMaster {\n\t\terr = snap.snapCoreDB.Exec(\"DELETE FROM operation_logs\").Error\n\t\tsnap.Task.LogWithStatus(i18n.GetMsgByKey(\"SnapDeleteOperationLog\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif !req.WithLoginLog && global.IsMaster {\n\t\terr = snap.snapCoreDB.Exec(\"DELETE FROM login_logs\").Error\n\t\tsnap.Task.LogWithStatus(i18n.GetMsgByKey(\"SnapDeleteLoginLog\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc snapBaseData(snap snapHelper, targetDir string, withDockerConf bool) error {\n\tsnap.Task.Log(\"---------------------- 2 / 8 ----------------------\")\n\tsnap.Task.LogStart(i18n.GetMsgByKey(\"SnapBaseInfo\"))\n\n\tif global.IsMaster {\n\t\terr := snap.FileOp.CopyFile(\"/usr/local/bin/1panel-core\", targetDir)\n\t\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1panel-core\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = snap.FileOp.CopyFile(\"/usr/local/bin/1pctl\", targetDir)\n\t\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1pctl\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\terr := snap.FileOp.CopyFile(\"/usr/local/bin/1panel-agent\", targetDir)\n\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1panel-agent\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif global.IsMaster {\n\t\tsvcCorePath, _ := controller.GetServicePath(\"1panel-core\")\n\t\terr = snap.FileOp.CopyFile(svcCorePath, targetDir)\n\t\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", svcCorePath), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tsvcAgentName, _ := controller.GetServicePath(\"1panel-agent\")\n\terr = snap.FileOp.CopyFile(svcAgentName, targetDir)\n\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", svcAgentName), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinitScriptPath := path.Join(global.Dir.ResourceDir, \"initscript\")\n\tif _, err := os.Stat(initScriptPath); err == nil {\n\t\terr = snap.FileOp.CopyDirWithNewName(initScriptPath, targetDir, \".\")\n\t\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", initScriptPath), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif withDockerConf {\n\t\tif snap.FileOp.Stat(constant.DaemonJsonPath) {\n\t\t\terr = snap.FileOp.CopyFile(constant.DaemonJsonPath, targetDir)\n\t\t\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", constant.DaemonJsonPath), err)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tremarkInfo, _ := json.MarshalIndent(SnapshotJson{\n\t\tBaseDir:       global.Dir.BaseDir,\n\t\tOperestyDir:   snap.OpenrestyDir,\n\t\tBackupDataDir: global.Dir.LocalBackupDir,\n\t}, \"\", \"\\t\")\n\terr = os.WriteFile(path.Join(targetDir, \"snapshot.json\"), remarkInfo, 0640)\n\tsnap.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", path.Join(targetDir, \"snapshot.json\")), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) error {\n\tsnap.Task.Log(\"---------------------- 3 / 8 ----------------------\")\n\tsnap.Task.LogStart(i18n.GetMsgByKey(\"SnapInstallApp\"))\n\n\tvar appInstalls []model.AppInstall\n\t_ = snap.snapAgentDB.Where(\"1 = 1\").Find(&appInstalls).Error\n\tfor _, item := range appInstalls {\n\t\tif err := snap.snapAgentDB.\n\t\t\tModel(&model.AppInstall{}).\n\t\t\tWhere(\"id = ?\", item.ID).\n\t\t\tUpdates(map[string]interface{}{\"status\": constant.StatusWaitingRestart}).\n\t\t\tError; err != nil {\n\t\t\tglobal.LOG.Errorf(\"update app %s status failed, err: %v\", item.Name, err)\n\t\t}\n\t}\n\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\tsnap.Task.Log(\"load docker client failed, skip save app images\")\n\t\treturn nil\n\t}\n\timages, err := client.ImageList(context.Background(), image.ListOptions{})\n\tif err != nil {\n\t\tsnap.Task.Log(\"list docker images failed, skip save app images\")\n\t\treturn nil\n\t}\n\tvar existImages []string\n\tfor _, img := range images {\n\t\texistImages = append(existImages, img.RepoTags...)\n\t}\n\tvar imageList []string\n\tfor _, app := range req.AppData {\n\t\tfor _, item := range app.Children {\n\t\t\tif item.Label == \"appImage\" && item.IsCheck {\n\t\t\t\tfor _, existImage := range existImages {\n\t\t\t\t\tif len(existImage) == 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif existImage == item.Name {\n\t\t\t\t\t\timageList = append(imageList, item.Name)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(imageList) != 0 {\n\t\tsnap.Task.Log(strings.Join(imageList, \" \"))\n\t\tsnap.Task.Logf(\"docker save %s | gzip -c > %s\", strings.Join(imageList, \" \"), path.Join(targetDir, \"images.tar.gz\"))\n\t\tif err := cmd.RunDefaultBashCf(\"docker save %s | gzip -c > %s\", strings.Join(imageList, \" \"), path.Join(targetDir, \"images.tar.gz\")); err != nil {\n\t\t\tsnap.Task.LogFailedWithErr(i18n.GetMsgByKey(\"SnapDockerSave\"), err)\n\t\t\treturn err\n\t\t}\n\t\tsnap.Task.LogSuccess(i18n.GetMsgByKey(\"SnapDockerSave\"))\n\t} else {\n\t\tsnap.Task.Log(i18n.GetMsgByKey(\"SnapInstallAppImageEmpty\"))\n\t}\n\treturn nil\n}\n\nfunc snapBackupData(snap snapHelper, req dto.SnapshotCreate, targetDir string) error {\n\tsnap.Task.Log(\"---------------------- 4 / 8 ----------------------\")\n\tsnap.Task.LogStart(i18n.GetMsgByKey(\"SnapLocalBackup\"))\n\n\texcludes := loadBackupExcludes(snap, req.BackupData)\n\texcludes = append(excludes, req.IgnoreFiles...)\n\texcludes = append(excludes, \"./system_snapshot\")\n\texcludes = append(excludes, \"./tmp\")\n\tfor _, item := range req.AppData {\n\t\tfor _, itemApp := range item.Children {\n\t\t\tif itemApp.Label == \"appBackup\" {\n\t\t\t\texcludes = append(excludes, loadAppBackupExcludes([]dto.DataTree{itemApp})...)\n\t\t\t}\n\t\t}\n\t}\n\terr := snap.FileOp.TarGzCompressPro(false, global.Dir.LocalBackupDir, path.Join(targetDir, \"1panel_backup.tar.gz\"), \"\", strings.Join(excludes, \",\"))\n\tsnap.Task.LogWithStatus(i18n.GetMsgByKey(\"SnapCompressBackup\"), err)\n\n\treturn err\n}\nfunc loadBackupExcludes(snap snapHelper, req []dto.DataTree) []string {\n\tvar excludes []string\n\tfor _, item := range req {\n\t\tif len(item.Children) == 0 {\n\t\t\tif item.IsCheck {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := snap.snapAgentDB.Where(\"file_dir = ? AND file_name = ?\", strings.TrimPrefix(path.Dir(item.Path), global.Dir.LocalBackupDir+\"/\"), path.Base(item.Path)).Delete(&model.BackupRecord{}).Error; err != nil {\n\t\t\t\tsnap.Task.LogWithStatus(\"delete backup file from database\", err)\n\t\t\t}\n\t\t\titemDir, _ := filepath.Rel(global.Dir.LocalBackupDir, item.Path)\n\t\t\texcludes = append(excludes, itemDir)\n\t\t} else {\n\t\t\texcludes = append(excludes, loadBackupExcludes(snap, item.Children)...)\n\t\t}\n\t}\n\treturn excludes\n}\nfunc loadAppBackupExcludes(req []dto.DataTree) []string {\n\tvar excludes []string\n\tfor _, item := range req {\n\t\tif len(item.Children) == 0 {\n\t\t\tif !item.IsCheck {\n\t\t\t\titemDir, _ := filepath.Rel(global.Dir.LocalBackupDir, item.Path)\n\t\t\t\texcludes = append(excludes, itemDir)\n\t\t\t}\n\t\t} else {\n\t\t\texcludes = append(excludes, loadAppBackupExcludes(item.Children)...)\n\t\t}\n\t}\n\treturn excludes\n}\n\nfunc snapPanelData(snap snapHelper, req dto.SnapshotCreate, targetDir string) error {\n\tsnap.Task.Log(\"---------------------- 5 / 8 ----------------------\")\n\tsnap.Task.LogStart(i18n.GetMsgByKey(\"SnapPanelData\"))\n\n\texcludes := loadPanelExcludes(req.PanelData)\n\tfor _, item := range req.AppData {\n\t\tfor _, itemApp := range item.Children {\n\t\t\tif itemApp.Label == \"appData\" {\n\t\t\t\texcludes = append(excludes, loadPanelExcludes([]dto.DataTree{itemApp})...)\n\t\t\t}\n\t\t}\n\t}\n\texcludes = append(excludes, \"./cache\")\n\texcludes = append(excludes, \"./db\")\n\texcludes = append(excludes, \"./tmp\")\n\tif !req.WithSystemLog {\n\t\texcludes = append(excludes, \"./log/1Panel*\")\n\t}\n\tif !req.WithTaskLog {\n\t\texcludes = append(excludes, \"./log/task\")\n\t}\n\n\trootDir := global.Dir.DataDir\n\tif strings.Contains(global.Dir.LocalBackupDir, rootDir) {\n\t\titemDir, _ := filepath.Rel(rootDir, global.Dir.LocalBackupDir)\n\t\texcludes = append(excludes, itemDir)\n\t}\n\tif len(snap.OpenrestyDir) != 0 && strings.Contains(snap.OpenrestyDir, rootDir) {\n\t\titemDir, _ := filepath.Rel(rootDir, snap.OpenrestyDir)\n\t\texcludes = append(excludes, itemDir)\n\t}\n\texcludes = append(excludes, req.IgnoreFiles...)\n\terr := snap.FileOp.TarGzCompressPro(false, rootDir, path.Join(targetDir, \"1panel_data.tar.gz\"), \"\", strings.Join(excludes, \",\"))\n\tsnap.Task.LogWithStatus(i18n.GetMsgByKey(\"SnapCompressPanel\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(snap.OpenrestyDir) != 0 {\n\t\terr := snap.FileOp.TarGzCompressPro(false, snap.OpenrestyDir, path.Join(targetDir, \"website.tar.gz\"), \"\", strings.Join(req.IgnoreFiles, \",\"))\n\t\tsnap.Task.LogWithStatus(i18n.GetMsgByKey(\"SnapWebsite\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn err\n}\nfunc loadPanelExcludes(req []dto.DataTree) []string {\n\tvar excludes []string\n\tfor _, item := range req {\n\t\tif len(item.Children) == 0 {\n\t\t\tif !item.IsCheck {\n\t\t\t\titemDir, _ := filepath.Rel(path.Join(global.Dir.BaseDir, \"1panel\"), item.Path)\n\t\t\t\texcludes = append(excludes, itemDir)\n\t\t\t}\n\t\t} else {\n\t\t\texcludes = append(excludes, loadPanelExcludes(item.Children)...)\n\t\t}\n\t}\n\treturn excludes\n}\n\nfunc snapCompress(snap snapHelper, rootDir string, secret string) error {\n\tsnap.Task.Log(\"---------------------- 7 / 8 ----------------------\")\n\tsnap.Task.LogStart(i18n.GetMsgByKey(\"SnapCompress\"))\n\n\ttmpDir := path.Join(global.Dir.LocalBackupDir, \"tmp/system\")\n\tfileName := fmt.Sprintf(\"%s.tar.gz\", path.Base(rootDir))\n\terr := snap.FileOp.TarGzCompressPro(true, rootDir, path.Join(tmpDir, fileName), secret, \"\")\n\tsnap.Task.LogWithStatus(i18n.GetMsgByKey(\"SnapCompressFile\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstat, err := os.Stat(path.Join(tmpDir, fileName))\n\tsnap.Task.LogWithStatus(i18n.GetMsgByKey(\"SnapCheckCompress\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsize := common.LoadSizeUnit2F(float64(stat.Size()))\n\tsnap.Task.Log(i18n.GetWithName(\"SnapCompressSize\", size))\n\t_ = os.RemoveAll(rootDir)\n\treturn nil\n}\n\nfunc snapUpload(snap snapHelper, accounts string, downloadID, retry uint, file string) error {\n\tsnap.Task.Log(\"---------------------- 8 / 8 ----------------------\")\n\tsnap.Task.LogStart(i18n.GetMsgByKey(\"SnapUpload\"))\n\n\tsrc := path.Join(global.Dir.LocalBackupDir, \"tmp/system\", path.Base(file))\n\tdst := path.Join(\"system_snapshot\", path.Base(file))\n\taccountMap := NewBackupClientMap(strings.Split(accounts, \",\"))\n\tif !accountMap[fmt.Sprintf(\"%d\", downloadID)].isOk {\n\t\treturn buserr.New(i18n.GetMsgWithDetail(\"LoadBackupFailed\", accountMap[fmt.Sprintf(\"%d\", downloadID)].message))\n\t}\n\treturn uploadWithMap(snap.Task, accountMap, src, dst, accounts, downloadID, retry)\n}\n"
  },
  {
    "path": "agent/app/service/snapshot_recover.go",
    "content": "package service\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/pkg/errors\"\n)\n\nvar (\n\tsvcBasePath, _     = controller.GetServicePath(\"\")\n\tsvcCoreName, _     = controller.LoadServiceName(\"1panel-core\")\n\tselCoreName, _     = controller.SelectInitScript(\"1panel-core\")\n\tscriptCoreName, _  = controller.GetScriptName(\"1panel-core\")\n\tsvcAgentName, _    = controller.LoadServiceName(\"1panel-agent\")\n\tselAgentName, _    = controller.SelectInitScript(\"1panel-agent\")\n\tscriptAgentName, _ = controller.GetScriptName(\"1panel-agent\")\n)\n\ntype snapRecoverHelper struct {\n\tFileOp files.FileOp\n\tTask   *task.Task\n}\n\nfunc (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {\n\tglobal.LOG.Info(\"start to recover panel by snapshot now\")\n\tsnap, err := snapshotRepo.Get(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif hasOs(snap.Name) && !strings.Contains(snap.Name, loadOs()) {\n\t\terrInfo := fmt.Sprintf(\"restoring snapshots(%s) between different server architectures(%s) is not supported\", snap.Name, loadOs())\n\t\t_ = snapshotRepo.Update(snap.ID, map[string]interface{}{\"recover_status\": constant.StatusFailed, \"recover_message\": errInfo})\n\t\treturn errors.New(errInfo)\n\t}\n\tif !strings.Contains(snap.Name, \"-v2.\") {\n\t\treturn errors.New(\"snapshots are currently not supported for recovery across major versions\")\n\t}\n\tif !strings.Contains(snap.Name, \"-core\") && !strings.Contains(snap.Name, \"-agent\") {\n\t\treturn errors.New(\"the name of the snapshot file does not conform to the format\")\n\t}\n\tif strings.Contains(snap.Name, \"-core\") && !global.IsMaster {\n\t\treturn errors.New(\"the snapshot of the master node cannot be restored on the agent nodes\")\n\t}\n\tif strings.Contains(snap.Name, \"-agent\") && global.IsMaster {\n\t\treturn errors.New(\"the snapshot of the agent node cannot be restored on the master node\")\n\t}\n\tif len(snap.RollbackStatus) != 0 && snap.RollbackStatus != constant.StatusSuccess {\n\t\treq.IsNew = true\n\t}\n\tif !req.IsNew && (snap.InterruptStep == \"RecoverDownload\" || snap.InterruptStep == \"RecoverDecompress\" || snap.InterruptStep == \"BackupBeforeRecover\") {\n\t\treq.IsNew = true\n\t}\n\t_ = snapshotRepo.Update(snap.ID, map[string]interface{}{\"recover_status\": constant.StatusWaiting})\n\t_ = settingRepo.Update(\"SystemStatus\", \"Recovering\")\n\n\tif len(snap.InterruptStep) == 0 {\n\t\treq.IsNew = true\n\t}\n\tif len(snap.TaskRecoverID) != 0 {\n\t\treq.TaskID = snap.TaskRecoverID\n\t} else {\n\t\t_ = snapshotRepo.Update(snap.ID, map[string]interface{}{\"task_recover_id\": req.TaskID})\n\t}\n\n\tvar taskItem *task.Task\n\tif req.IsNew {\n\t\ttaskItem, err = task.NewTaskWithOps(snap.Name, task.TaskRecover, task.TaskScopeSnapshot, req.TaskID, snap.ID)\n\t} else {\n\t\ttaskItem, err = task.ReNewTaskWithOps(snap.Name, task.TaskRecover, task.TaskScopeSnapshot, req.TaskID, snap.ID)\n\t}\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create snapshot failed, err: %v\", err)\n\t\treturn err\n\t}\n\trootDir := path.Join(global.Dir.LocalBackupDir, \"tmp/system\", snap.Name)\n\tif _, err := os.Stat(rootDir); err != nil && os.IsNotExist(err) {\n\t\t_ = os.MkdirAll(rootDir, os.ModePerm)\n\t}\n\titemHelper := snapRecoverHelper{Task: taskItem, FileOp: files.NewFileOp()}\n\n\tgo func() {\n\t\t_ = global.Cron.Stop()\n\t\tdefer func() {\n\t\t\tglobal.Cron.Start()\n\t\t}()\n\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverDownload\" || req.ReDownload {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverDownload\",\n\t\t\t\tfunc(t *task.Task) error { return handleDownloadSnapshot(&itemHelper, snap, rootDir) },\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverDecompress\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverDecompress\",\n\t\t\t\tfunc(t *task.Task) error {\n\t\t\t\t\titemHelper.Task.Log(\"---------------------- 2 / 11 ----------------------\")\n\t\t\t\t\titemHelper.Task.LogStart(i18n.GetWithName(\"RecoverDecompress\", snap.Name))\n\t\t\t\t\terr := itemHelper.FileOp.TarGzExtractPro(fmt.Sprintf(\"%s/%s.tar.gz\", rootDir, snap.Name), rootDir, req.Secret)\n\t\t\t\t\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"Decompress\"), err)\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\t\tif req.IsNew || snap.InterruptStep == \"BackupBeforeRecover\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"BackupBeforeRecover\",\n\t\t\t\tfunc(t *task.Task) error { return backupBeforeRecover(snap.Name, &itemHelper) },\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\n\t\tvar snapJson SnapshotJson\n\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\"ReadJson\",\n\t\t\tfunc(t *task.Task) error {\n\t\t\t\tsnapJson, err = readFromJson(path.Join(rootDir, snap.Name), &itemHelper)\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tnil, 0, 90*time.Minute,\n\t\t)\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverApp\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverApp\",\n\t\t\t\tfunc(t *task.Task) error { return recoverAppData(path.Join(rootDir, snap.Name), &itemHelper) },\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverBaseData\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverBaseData\",\n\t\t\t\tfunc(t *task.Task) error { return recoverBaseData(path.Join(rootDir, snap.Name, \"base\"), &itemHelper) },\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverBackups\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverBackups\",\n\t\t\t\tfunc(t *task.Task) error {\n\t\t\t\t\titemHelper.Task.Log(\"---------------------- 8 / 11 ----------------------\")\n\t\t\t\t\titemHelper.Task.LogStart(i18n.GetWithName(\"RecoverBackups\", snap.Name))\n\t\t\t\t\terr := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, \"/1panel_backup.tar.gz\"), snapJson.BackupDataDir, \"\")\n\t\t\t\t\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"Decompress\"), err)\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverWebsite\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverWebsite\",\n\t\t\t\tfunc(t *task.Task) error {\n\t\t\t\t\titemHelper.Task.Log(\"---------------------- 9 / 11 ----------------------\")\n\t\t\t\t\titemHelper.Task.LogStart(i18n.GetWithName(\"RecoverWebsite\", snap.Name))\n\t\t\t\t\twebFile := path.Join(rootDir, snap.Name, \"/website.tar.gz\")\n\t\t\t\t\t_ = itemHelper.FileOp.CreateDir(snapJson.OperestyDir, os.ModePerm)\n\t\t\t\t\tvar err error\n\t\t\t\t\tif itemHelper.FileOp.Stat(webFile) {\n\t\t\t\t\t\terr = itemHelper.FileOp.TarGzExtractPro(webFile, snapJson.OperestyDir, \"\")\n\t\t\t\t\t}\n\t\t\t\t\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"Decompress\"), err)\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverPanelData\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverPanelData\",\n\t\t\t\tfunc(t *task.Task) error {\n\t\t\t\t\titemHelper.Task.Log(\"---------------------- 10 / 11 ----------------------\")\n\t\t\t\t\titemHelper.Task.LogStart(i18n.GetWithName(\"RecoverPanelData\", snap.Name))\n\t\t\t\t\terr := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, \"/1panel_data.tar.gz\"), path.Join(snapJson.BaseDir, \"1panel\"), \"\")\n\t\t\t\t\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"Decompress\"), err)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\n\t\t\t\t\tif len(snapJson.OperestyDir) != 0 {\n\t\t\t\t\t\terr := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, \"/website.tar.gz\"), snapJson.OperestyDir, \"\")\n\t\t\t\t\t\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"RecoverWebsite\"), err)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverCompose\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverCompose\",\n\t\t\t\tfunc(t *task.Task) error {\n\t\t\t\t\treturn restartCompose(path.Join(snapJson.BaseDir, \"1panel/docker/compose\"), &itemHelper)\n\t\t\t\t},\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\t\tif req.IsNew || snap.InterruptStep == \"RecoverDBData\" {\n\t\t\ttaskItem.AddSubTaskWithAliasAndOps(\n\t\t\t\t\"RecoverDBData\",\n\t\t\t\tfunc(t *task.Task) error { return recoverDBData(path.Join(rootDir, snap.Name, \"db\"), &itemHelper) },\n\t\t\t\tnil, 0, 90*time.Minute,\n\t\t\t)\n\t\t\treq.IsNew = true\n\t\t}\n\n\t\tif err := taskItem.Execute(); err != nil {\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\t_ = snapshotRepo.Update(req.ID, map[string]interface{}{\"recover_status\": constant.StatusFailed, \"recover_message\": err.Error(), \"interrupt_step\": taskItem.Task.CurrentStep})\n\t\t\treturn\n\t\t}\n\t\t_ = os.RemoveAll(rootDir)\n\t\tcontroller.RestartPanel(true, true, true)\n\t}()\n\treturn nil\n}\n\nfunc handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot, targetDir string) error {\n\titemHelper.Task.Log(\"---------------------- 1 / 11 ----------------------\")\n\titemHelper.Task.LogStart(i18n.GetMsgByKey(\"RecoverDownload\"))\n\n\taccount, client, err := NewBackupClientWithID(snap.DownloadAccountID)\n\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"RecoverDownloadAccount\", fmt.Sprintf(\"%s - %s\", account.Type, account.Name)), err)\n\ttargetPath := \"\"\n\tif len(account.BackupPath) != 0 {\n\t\ttargetPath = path.Join(account.BackupPath, fmt.Sprintf(\"system_snapshot/%s.tar.gz\", snap.Name))\n\t} else {\n\t\ttargetPath = fmt.Sprintf(\"system_snapshot/%s.tar.gz\", snap.Name)\n\t}\n\tfilePath := fmt.Sprintf(\"%s/%s.tar.gz\", targetDir, snap.Name)\n\t_ = os.RemoveAll(filePath)\n\t_, err = client.Download(targetPath, filePath)\n\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"Download\"), err)\n\treturn err\n}\n\nfunc backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {\n\titemHelper.Task.Log(\"---------------------- 3 / 11 ----------------------\")\n\titemHelper.Task.LogStart(i18n.GetMsgByKey(\"BackupBeforeRecover\"))\n\n\trootDir := fmt.Sprintf(\"%s/1panel_original/original_%s\", global.Dir.BaseDir, name)\n\tbaseDir := path.Join(rootDir, \"base\")\n\tif _, err := os.Stat(baseDir); err != nil {\n\t\t_ = os.MkdirAll(baseDir, os.ModePerm)\n\t}\n\tinitScriptPath := path.Join(global.Dir.ResourceDir, \"initscript\")\n\tif _, err := os.Stat(initScriptPath); err == nil {\n\t\terr = itemHelper.FileOp.CopyDirWithNewName(initScriptPath, baseDir, \".\")\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", initScriptPath), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tdataDir := global.Dir.DataDir\n\terr := itemHelper.FileOp.CopyDirWithExclude(dataDir, rootDir, []string{\"cache\", \"tmp\", \"backup\"})\n\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", dataDir), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\topenrestyDir, _ := settingRepo.GetValueByKey(\"WEBSITE_DIR\")\n\tif len(openrestyDir) != 0 && !strings.Contains(openrestyDir, dataDir) {\n\t\terr := itemHelper.FileOp.CopyDirWithExclude(openrestyDir, rootDir, nil)\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", openrestyDir), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tbackupDir := global.Dir.LocalBackupDir\n\tif len(backupDir) != 0 && (backupDir == dataDir+\"/backup\" || !strings.HasPrefix(backupDir, dataDir)) {\n\t\terr = itemHelper.FileOp.CopyDirWithExclude(backupDir, rootDir, []string{\"system_snapshot\", \"tmp\"})\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", backupDir), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif global.IsMaster {\n\t\terr = itemHelper.FileOp.CopyFile(\"/usr/local/bin/1pctl\", baseDir)\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1pctl\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = itemHelper.FileOp.CopyFile(\"/usr/local/bin/1panel-core\", baseDir)\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1panel-core\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsvcCorePath, _ := controller.GetServicePath(\"1panel-core\")\n\t\terr = itemHelper.FileOp.CopyFile(svcCorePath, baseDir)\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", svcCorePath), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\terr = itemHelper.FileOp.CopyFile(\"/usr/local/bin/1panel-agent\", baseDir)\n\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1panel-agent\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsvcAgentPath, _ := controller.GetServicePath(\"1panel-agent\")\n\terr = itemHelper.FileOp.CopyFile(svcAgentPath, baseDir)\n\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", svcAgentPath), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif itemHelper.FileOp.Stat(constant.DaemonJsonPath) {\n\t\terr = itemHelper.FileOp.CopyFile(constant.DaemonJsonPath, baseDir)\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", constant.DaemonJsonPath), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson, error) {\n\titemHelper.Task.Log(\"---------------------- 4 / 11 ----------------------\")\n\titemHelper.Task.LogStart(i18n.GetMsgByKey(\"ReadJson\"))\n\n\tsnapJsonPath := path.Join(rootDir, \"base/snapshot.json\")\n\tvar snap SnapshotJson\n\t_, err := os.Stat(snapJsonPath)\n\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"ReadJsonPath\"), err)\n\tif err != nil {\n\t\treturn snap, err\n\t}\n\tfileByte, err := os.ReadFile(snapJsonPath)\n\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"ReadJsonContent\"), err)\n\tif err != nil {\n\t\treturn snap, err\n\t}\n\terr = json.Unmarshal(fileByte, &snap)\n\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"ReadJsonMarshal\"), err)\n\tif err != nil {\n\t\treturn snap, err\n\t}\n\treturn snap, nil\n}\n\nfunc recoverAppData(src string, itemHelper *snapRecoverHelper) error {\n\titemHelper.Task.Log(\"---------------------- 5 / 11 ----------------------\")\n\titemHelper.Task.LogStart(i18n.GetMsgByKey(\"RecoverApp\"))\n\n\tif _, err := os.Stat(path.Join(src, \"images.tar.gz\")); err != nil {\n\t\titemHelper.Task.Log(i18n.GetMsgByKey(\"RecoverAppEmpty\"))\n\t\treturn nil\n\t}\n\tif err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunBashCf(\"docker load < %s\", path.Join(src, \"images.tar.gz\")); err != nil {\n\t\titemHelper.Task.LogFailedWithErr(i18n.GetMsgByKey(\"RecoverAppImage\"), err)\n\t\treturn fmt.Errorf(\"docker load images failed, %v\", err)\n\t}\n\titemHelper.Task.LogSuccess(i18n.GetMsgByKey(\"RecoverAppImage\"))\n\treturn nil\n}\n\nfunc recoverBaseData(src string, itemHelper *snapRecoverHelper) error {\n\titemHelper.Task.Log(\"---------------------- 6 / 11 ----------------------\")\n\titemHelper.Task.LogStart(i18n.GetMsgByKey(\"SnapBaseInfo\"))\n\tif global.IsMaster {\n\t\terr := itemHelper.FileOp.CopyFile(path.Join(src, \"1pctl\"), \"/usr/local/bin\")\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1pctl\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = itemHelper.FileOp.CopyFile(path.Join(src, \"1panel-core\"), \"/usr/local/bin\")\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1panel-core\"), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsvcCoreName, _ := controller.LoadServiceName(\"1panel-core\")\n\t\terr = itemHelper.FileOp.CopyFile(path.Join(src, svcCoreName), svcBasePath)\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", path.Join(svcBasePath, svcCoreName)), err)\n\t\tif err != nil {\n\t\t\terr = itemHelper.FileOp.CopyFile(path.Join(src, selCoreName), path.Join(svcBasePath, scriptCoreName))\n\t\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", path.Join(svcBasePath, scriptCoreName)), err)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\terr := itemHelper.FileOp.CopyFile(path.Join(src, \"1panel-agent\"), \"/usr/local/bin\")\n\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1panel-agent\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsvcAgentName, _ := controller.LoadServiceName(\"1panel-agent\")\n\terr = itemHelper.FileOp.CopyFile(path.Join(src, svcAgentName), svcBasePath)\n\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", path.Join(svcBasePath, svcAgentName)), err)\n\tif err != nil {\n\t\terr = itemHelper.FileOp.CopyFile(path.Join(src, selAgentName), path.Join(svcBasePath, scriptAgentName))\n\t\titemHelper.Task.LogWithStatus(i18n.GetWithName(\"SnapCopy\", path.Join(svcBasePath, scriptAgentName)), err)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif !itemHelper.FileOp.Stat(path.Join(src, \"daemon.json\")) {\n\t\titemHelper.Task.Log(i18n.GetMsgByKey(\"RecoverDaemonJsonEmpty\"))\n\t\treturn nil\n\t} else {\n\t\terr = itemHelper.FileOp.CopyFile(path.Join(src, \"daemon.json\"), path.Dir(constant.DaemonJsonPath))\n\t\titemHelper.Task.Log(i18n.GetMsgByKey(\"RecoverDaemonJson\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"recover docker daemon.json failed, err: %v\", err)\n\t\t}\n\t}\n\n\tif err := controller.HandleRestart(\"docker\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to restart Docker: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc restartCompose(composePath string, itemHelper *snapRecoverHelper) error {\n\titemHelper.Task.Log(\"---------------------- 7 / 11 ----------------------\")\n\titemHelper.Task.LogStart(i18n.GetMsgByKey(\"RecoverCompose\"))\n\n\tcomposes, err := composeRepo.ListRecord()\n\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"RecoverComposeList\"), err)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, compose := range composes {\n\t\tpathItem := path.Join(composePath, compose.Name, \"docker-compose.yml\")\n\t\tif _, err := os.Stat(pathItem); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tupCmd := fmt.Sprintf(\"docker compose -f %s up -d\", pathItem)\n\t\tif err := cmd.RunDefaultBashC(upCmd); err != nil {\n\t\t\titemHelper.Task.LogFailedWithErr(i18n.GetMsgByKey(\"RecoverCompose\"), err)\n\t\t\tcontinue\n\t\t}\n\t\titemHelper.Task.LogSuccess(i18n.GetWithName(\"RecoverComposeItem\", pathItem))\n\t}\n\treturn nil\n}\n\nfunc recoverDBData(src string, itemHelper *snapRecoverHelper) error {\n\titemHelper.Task.Log(\"---------------------- 11 / 11 ----------------------\")\n\titemHelper.Task.LogStart(i18n.GetMsgByKey(\"RecoverDBData\"))\n\tdbDir := path.Join(global.Dir.DataDir, \"db\")\n\tif err := os.RemoveAll(dbDir); err != nil {\n\t\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"RecoverDBData\"), err)\n\t\treturn err\n\t}\n\n\terr := itemHelper.FileOp.CopyDirWithExclude(src, global.Dir.DataDir, nil)\n\tif err != nil {\n\t\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"RecoverDBData\"), err)\n\t\treturn err\n\t}\n\tif cleanErr := cleanOrphanSQLiteSidecars(dbDir); cleanErr != nil {\n\t\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"RecoverDBData\"), cleanErr)\n\t\treturn cleanErr\n\t}\n\n\titemHelper.Task.LogWithStatus(i18n.GetMsgByKey(\"RecoverDBData\"), nil)\n\treturn nil\n}\n\nfunc cleanOrphanSQLiteSidecars(dbDir string) error {\n\tentries, err := os.ReadDir(dbDir)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tname := entry.Name()\n\t\tvar mainDB string\n\t\tswitch {\n\t\tcase strings.HasSuffix(name, \".db-wal\"):\n\t\t\tmainDB = strings.TrimSuffix(name, \"-wal\")\n\t\tcase strings.HasSuffix(name, \".db-shm\"):\n\t\t\tmainDB = strings.TrimSuffix(name, \"-shm\")\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, statErr := os.Stat(path.Join(dbDir, mainDB)); statErr != nil {\n\t\t\tif !os.IsNotExist(statErr) {\n\t\t\t\treturn statErr\n\t\t\t}\n\t\t\tif removeErr := os.Remove(path.Join(dbDir, name)); removeErr != nil && !os.IsNotExist(removeErr) {\n\t\t\t\treturn removeErr\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/snapshot_rollback.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\nfunc (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {\n\tglobal.LOG.Info(\"start to rollback now\")\n\tsnap, err := snapshotRepo.Get(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(snap.TaskRollbackID) != 0 {\n\t\treq.TaskID = snap.TaskRollbackID\n\t} else {\n\t\t_ = snapshotRepo.Update(snap.ID, map[string]interface{}{\"task_rollback_id\": req.TaskID})\n\t}\n\ttaskItem, err := task.NewTaskWithOps(snap.Name, task.TaskRollback, task.TaskScopeSnapshot, req.TaskID, snap.ID)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"new task for create snapshot failed, err: %v\", err)\n\t\treturn err\n\t}\n\tgo func() {\n\t\trootDir := fmt.Sprintf(\"%s/1panel_original/original_%s\", global.Dir.BaseDir, snap.Name)\n\t\tbaseDir := path.Join(rootDir, \"base\")\n\n\t\tFileOp := files.NewFileOp()\n\t\ttaskItem.AddSubTask(\n\t\t\ti18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1pctl\"),\n\t\t\tfunc(t *task.Task) error {\n\t\t\t\treturn FileOp.CopyFile(path.Join(baseDir, \"1pctl\"), \"/usr/local/bin\")\n\t\t\t},\n\t\t\tnil,\n\t\t)\n\t\tif global.IsMaster {\n\t\t\ttaskItem.AddSubTask(\n\t\t\t\ti18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1panel-core\"),\n\t\t\t\tfunc(t *task.Task) error {\n\t\t\t\t\treturn FileOp.CopyFile(path.Join(baseDir, \"1panel\"), \"/usr/local/bin\")\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t)\n\t\t}\n\t\ttaskItem.AddSubTask(\n\t\t\ti18n.GetWithName(\"SnapCopy\", \"/usr/local/bin/1panel-agent\"),\n\t\t\tfunc(t *task.Task) error {\n\t\t\t\treturn FileOp.CopyFile(path.Join(baseDir, \"1panel-agent\"), \"/usr/local/bin\")\n\t\t\t},\n\t\t\tnil,\n\t\t)\n\t\tif global.IsMaster {\n\t\t\ttaskItem.AddSubTask(\n\t\t\t\ti18n.GetWithName(\"SnapCopy\", path.Join(svcBasePath, svcCoreName)),\n\t\t\t\tfunc(t *task.Task) error {\n\t\t\t\t\treturn FileOp.CopyFile(path.Join(baseDir, svcCoreName), svcBasePath)\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t)\n\t\t}\n\t\ttaskItem.AddSubTask(\n\t\t\ti18n.GetWithName(\"SnapCopy\", path.Join(svcBasePath, svcAgentName)),\n\t\t\tfunc(t *task.Task) error {\n\t\t\t\treturn FileOp.CopyFile(path.Join(baseDir, svcAgentName), svcBasePath)\n\t\t\t},\n\t\t\tnil,\n\t\t)\n\t\ttaskItem.AddSubTask(\n\t\t\ti18n.GetWithName(\"SnapCopy\", constant.DaemonJsonPath),\n\t\t\tfunc(t *task.Task) error {\n\t\t\t\tif FileOp.Stat(path.Join(baseDir, \"daemon.json\")) {\n\t\t\t\t\treturn FileOp.CopyFile(path.Join(baseDir, \"daemon.json\"), path.Dir(constant.DaemonJsonPath))\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tnil,\n\t\t)\n\t\ttaskItem.AddSubTask(\n\t\t\ti18n.GetWithName(\"SnapCopy\", global.Dir.LocalBackupDir),\n\t\t\tfunc(t *task.Task) error {\n\t\t\t\treturn FileOp.CopyDir(path.Join(rootDir, \"backup\"), strings.TrimSuffix(global.Dir.LocalBackupDir, \"/backup\"))\n\t\t\t},\n\t\t\tnil,\n\t\t)\n\t\topenrestyDir, _ := settingRepo.GetValueByKey(\"WEBSITE_DIR\")\n\t\tif len(openrestyDir) != 0 {\n\t\t\ttaskItem.AddSubTask(\n\t\t\t\ti18n.GetWithName(\"SnapCopy\", openrestyDir),\n\t\t\t\tfunc(t *task.Task) error {\n\t\t\t\t\treturn FileOp.CopyDir(path.Join(rootDir, \"www\"), strings.TrimSuffix(openrestyDir, \"/www\"))\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t)\n\t\t}\n\t\ttaskItem.AddSubTask(\n\t\t\ti18n.GetWithName(\"SnapCopy\", global.Dir.BaseDir),\n\t\t\tfunc(t *task.Task) error {\n\t\t\t\treturn FileOp.CopyDir(path.Join(rootDir, \"1panel\"), global.Dir.BaseDir)\n\t\t\t},\n\t\t\tnil,\n\t\t)\n\t\tif err := taskItem.Execute(); err != nil {\n\t\t\t_ = snapshotRepo.Update(req.ID, map[string]interface{}{\"rollback_status\": constant.StatusFailed, \"rollback_message\": err.Error()})\n\t\t\treturn\n\t\t}\n\t\t_ = snapshotRepo.Update(req.ID, map[string]interface{}{\n\t\t\t\"recover_status\":   \"\",\n\t\t\t\"recover_message\":  \"\",\n\t\t\t\"rollback_status\":  \"\",\n\t\t\t\"rollback_message\": \"\",\n\t\t\t\"interrupt_step\":   \"\",\n\t\t})\n\t\t_ = os.RemoveAll(rootDir)\n\t}()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/ssh.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"os/user\"\n\t\"path\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/copier\"\n\tcsvexport \"github.com/1Panel-dev/1Panel/agent/utils/csv_export\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/geo\"\n\t\"github.com/gin-gonic/gin\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/pkg/errors\"\n)\n\nconst sshPath = \"/etc/ssh/sshd_config\"\n\ntype SSHService struct{}\n\ntype ISSHService interface {\n\tGetSSHInfo() (*dto.SSHInfo, error)\n\tOperateSSH(operation string) error\n\tUpdate(req dto.SSHUpdate) error\n\tLoadSSHFile(name string) (string, error)\n\tUpdateByFile(req dto.SettingUpdate) error\n\n\tLoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []dto.SSHHistory, error)\n\tExportLog(ctx *gin.Context, req dto.SearchSSHLog) (string, error)\n\n\tSyncRootCert() error\n\tCreateRootCert(req dto.RootCertOperate) error\n\tEditRootCert(req dto.RootCertOperate) error\n\tSearchRootCerts(req dto.SearchWithPage) (int64, interface{}, error)\n\tDeleteRootCerts(req dto.ForceDelete) error\n}\n\nfunc NewISSHService() ISSHService {\n\treturn &SSHService{}\n}\n\nfunc (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {\n\tdata := dto.SSHInfo{\n\t\tAutoStart:              true,\n\t\tIsExist:                true,\n\t\tIsActive:               true,\n\t\tMessage:                \"\",\n\t\tPort:                   \"22\",\n\t\tListenAddress:          \"\",\n\t\tPasswordAuthentication: \"yes\",\n\t\tPubkeyAuthentication:   \"yes\",\n\t\tPermitRootLogin:        \"yes\",\n\t\tUseDNS:                 \"yes\",\n\t}\n\tserviceName, err := loadServiceName()\n\tif err != nil {\n\t\tdata.IsExist = false\n\t\tdata.Message = err.Error()\n\t} else {\n\t\tactive, err := controller.CheckActive(serviceName)\n\t\tdata.IsActive = active\n\t\tif !active && err != nil {\n\t\t\tdata.Message = err.Error()\n\t\t}\n\t}\n\n\tenable, err := controller.CheckEnable(serviceName)\n\tif err != nil {\n\t\tdata.AutoStart = false\n\t} else {\n\t\tdata.AutoStart = enable\n\t}\n\n\tsshConf, err := os.ReadFile(sshPath)\n\tif err != nil {\n\t\tdata.Message = err.Error()\n\t\tdata.IsActive = false\n\t}\n\tlines := strings.Split(string(sshConf), \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.HasPrefix(line, \"Port \") {\n\t\t\tdata.Port = strings.ReplaceAll(line, \"Port \", \"\")\n\t\t}\n\t\tif strings.HasPrefix(line, \"ListenAddress \") {\n\t\t\titemAddr := strings.ReplaceAll(line, \"ListenAddress \", \"\")\n\t\t\tif len(data.ListenAddress) != 0 {\n\t\t\t\tdata.ListenAddress += (\",\" + itemAddr)\n\t\t\t} else {\n\t\t\t\tdata.ListenAddress = itemAddr\n\t\t\t}\n\t\t}\n\t\tif strings.HasPrefix(line, \"PasswordAuthentication \") {\n\t\t\tdata.PasswordAuthentication = strings.ReplaceAll(line, \"PasswordAuthentication \", \"\")\n\t\t}\n\t\tif strings.HasPrefix(line, \"PubkeyAuthentication \") {\n\t\t\tdata.PubkeyAuthentication = strings.ReplaceAll(line, \"PubkeyAuthentication \", \"\")\n\t\t}\n\t\tif strings.HasPrefix(line, \"PermitRootLogin \") {\n\t\t\tdata.PermitRootLogin = strings.ReplaceAll(strings.ReplaceAll(line, \"PermitRootLogin \", \"\"), \"prohibit-password\", \"without-password\")\n\t\t}\n\t\tif strings.HasPrefix(line, \"UseDNS \") {\n\t\t\tdata.UseDNS = strings.ReplaceAll(line, \"UseDNS \", \"\")\n\t\t}\n\t}\n\n\tcurrentUser, err := user.Current()\n\tif err != nil || len(currentUser.Name) == 0 {\n\t\tdata.CurrentUser = \"root\"\n\t} else {\n\t\tdata.CurrentUser = currentUser.Name\n\t}\n\n\treturn &data, nil\n}\n\nfunc (u *SSHService) OperateSSH(operation string) error {\n\tserviceName, err := loadServiceName()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif operation == \"enable\" || operation == \"disable\" {\n\t\tserviceName += \".service\"\n\t}\n\tif operation == \"stop\" {\n\t\tisSocketActive, _ := controller.CheckActive(serviceName + \".socket\")\n\t\tif isSocketActive {\n\t\t\tif err := controller.HandleStop(serviceName + \".socket\"); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"handle stop %s.socket failed, err: %v\", serviceName, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := controller.Handle(operation, serviceName); err != nil {\n\t\treturn fmt.Errorf(\"%s %s failed, err: %v\", operation, serviceName, err)\n\t}\n\treturn nil\n}\n\nfunc (u *SSHService) Update(req dto.SSHUpdate) error {\n\tserviceName, err := loadServiceName()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsshConf, err := os.ReadFile(sshPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlines := strings.Split(string(sshConf), \"\\n\")\n\tnewFiles := updateSSHConf(lines, req.Key, req.NewValue)\n\tfile, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\tif _, err = file.WriteString(strings.Join(newFiles, \"\\n\")); err != nil {\n\t\treturn err\n\t}\n\tsudo := cmd.SudoHandleCmd()\n\tif req.Key == \"Port\" {\n\t\tstdout, _ := cmd.RunDefaultWithStdoutBashCf(\"%s getenforce\", sudo)\n\t\tif stdout == \"Enforcing\\n\" {\n\t\t\t_ = cmd.RunDefaultBashCf(\"%s semanage port -a -t ssh_port_t -p tcp %s\", sudo, req.NewValue)\n\t\t}\n\n\t\truleItem := dto.PortRuleUpdate{\n\t\t\tOldRule: dto.PortRuleOperate{\n\t\t\t\tOperation: \"remove\",\n\t\t\t\tPort:      req.OldValue,\n\t\t\t\tProtocol:  \"tcp\",\n\t\t\t\tStrategy:  \"accept\",\n\t\t\t},\n\t\t\tNewRule: dto.PortRuleOperate{\n\t\t\t\tOperation: \"add\",\n\t\t\t\tPort:      req.NewValue,\n\t\t\t\tProtocol:  \"tcp\",\n\t\t\t\tStrategy:  \"accept\",\n\t\t\t},\n\t\t}\n\t\tif err := NewIFirewallService().UpdatePortRule(ruleItem); err != nil {\n\t\t\tglobal.LOG.Errorf(\"reset firewall rules %s -> %s failed, err: %v\", req.OldValue, req.NewValue, err)\n\t\t}\n\t\tnewPort, _ := strconv.Atoi(req.NewValue)\n\t\tif err := updateLocalConn(uint(newPort)); err != nil {\n\t\t\tglobal.LOG.Errorf(\"update local conn for terminal failed, err: %v\", err)\n\t\t}\n\n\t\tif err := updateSSHSocketFile(req.NewValue); err != nil {\n\t\t\tglobal.LOG.Errorf(\"update port for ssh.socket failed, err: %v\", err)\n\t\t}\n\t}\n\n\t_ = controller.HandleRestart(serviceName)\n\treturn nil\n}\n\nfunc (u *SSHService) SyncRootCert() error {\n\tcurrentUser, err := user.Current()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"load current user failed, err: %v\", err)\n\t}\n\tsshDir := fmt.Sprintf(\"%s/.ssh\", currentUser.HomeDir)\n\n\tfileList, err := os.ReadDir(sshDir)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar rootCerts []model.RootCert\n\tfileMap := make(map[string]bool)\n\tfor _, item := range fileList {\n\t\tif !item.IsDir() {\n\t\t\tfileMap[item.Name()] = true\n\t\t}\n\t}\n\tfor item := range fileMap {\n\t\tif !strings.HasSuffix(item, \".pub\") {\n\t\t\tcontinue\n\t\t}\n\t\tif !fileMap[strings.TrimSuffix(item, \".pub\")] {\n\t\t\tcontinue\n\t\t}\n\t\tcert := model.RootCert{Name: strings.TrimSuffix(item, \".pub\"), PublicKeyPath: path.Join(sshDir, item), PrivateKeyPath: path.Join(sshDir, strings.TrimSuffix(item, \".pub\"))}\n\t\tpubItem, err := os.ReadFile(path.Join(sshDir, item))\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"read pubic key of %s for sync failed, err: %v\", item, err)\n\t\t\tcontinue\n\t\t}\n\t\tcert.EncryptionMode = loadEncryptioMode(string(pubItem))\n\t\trootCerts = append(rootCerts, cert)\n\t}\n\treturn hostRepo.SyncCert(rootCerts)\n}\n\nfunc (u *SSHService) CreateRootCert(req dto.RootCertOperate) error {\n\tif cmd.CheckIllegal(req.EncryptionMode, req.PassPhrase) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcertItem, _ := hostRepo.GetCert(repo.WithByName(req.Name))\n\tif certItem.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tcurrentUser, err := user.Current()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"load current user failed, err: %v\", err)\n\t}\n\tvar cert model.RootCert\n\tif err := copier.Copy(&cert, req); err != nil {\n\t\treturn err\n\t}\n\tprivatePath := fmt.Sprintf(\"%s/.ssh/%s\", currentUser.HomeDir, req.Name)\n\tpublicPath := fmt.Sprintf(\"%s/.ssh/%s.pub\", currentUser.HomeDir, req.Name)\n\tauthFilePath := currentUser.HomeDir + \"/.ssh/authorized_keys\"\n\n\tif info, err := os.Stat(authFilePath); err == nil && info.Size() > 0 {\n\t\tf, err := os.Open(authFilePath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() { _ = f.Close() }()\n\n\t\tif _, err := f.Seek(-1, 2); err == nil {\n\t\t\tbuf := make([]byte, 1)\n\t\t\tif _, err := f.Read(buf); err == nil && buf[0] != '\\n' {\n\t\t\t\tappendFile, err := os.OpenFile(authFilePath, os.O_APPEND|os.O_WRONLY, 0600)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif _, err := appendFile.Write([]byte(\"\\n\")); err != nil {\n\t\t\t\t\t_ = appendFile.Close()\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t_ = appendFile.Close()\n\t\t\t}\n\t\t}\n\t}\n\n\tif req.Mode == \"input\" || req.Mode == \"import\" {\n\t\tif err := os.WriteFile(privatePath, []byte(req.PrivateKey), constant.FilePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := os.WriteFile(publicPath, []byte(req.PublicKey), constant.FilePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\ttmpPrivatePath := privatePath + \".tmp\"\n\t\ttmpPublicPath := privatePath + \".tmp.pub\"\n\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Minute))\n\t\targs := []string{\n\t\t\t\"-t\", req.EncryptionMode,\n\t\t\t\"-f\", tmpPrivatePath,\n\t\t\t\"-N\", req.PassPhrase,\n\t\t\t\"-q\",\n\t\t}\n\n\t\tif err := cmdMgr.Run(\"ssh-keygen\", args...); err != nil {\n\t\t\t_ = os.Remove(tmpPrivatePath)\n\t\t\t_ = os.Remove(tmpPublicPath)\n\t\t\treturn fmt.Errorf(\"generate failed, %v\", err)\n\t\t}\n\t\tif err := os.Rename(tmpPrivatePath, privatePath); err != nil {\n\t\t\treturn fmt.Errorf(\"replace private key failed, %v\", err)\n\t\t}\n\t\tif err := os.Rename(tmpPublicPath, publicPath); err != nil {\n\t\t\treturn fmt.Errorf(\"replace public key failed, %v\", err)\n\t\t}\n\t}\n\n\tpublicKeyBytes, err := os.ReadFile(publicPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read public key failed, %v\", err)\n\t}\n\n\tcleanKey := strings.TrimRight(string(publicKeyBytes), \"\\n\") + \"\\n\"\n\tauthFile, err := os.OpenFile(authFilePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open authorized_keys failed, %v\", err)\n\t}\n\tdefer func() { _ = authFile.Close() }()\n\n\tif _, err := authFile.Write([]byte(cleanKey)); err != nil {\n\t\treturn fmt.Errorf(\"append authorized_keys failed, %v\", err)\n\t}\n\n\tcert.PrivateKeyPath = privatePath\n\tcert.PublicKeyPath = publicPath\n\tif len(cert.PassPhrase) != 0 {\n\t\tcert.PassPhrase, _ = encrypt.StringEncrypt(cert.PassPhrase)\n\t}\n\treturn hostRepo.SaveCert(&cert)\n}\n\nfunc (u *SSHService) EditRootCert(req dto.RootCertOperate) error {\n\tcurrentUser, err := user.Current()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"load current user failed, err: %v\", err)\n\t}\n\tcertItem, _ := hostRepo.GetCert(repo.WithByID(req.ID))\n\tif certItem.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\toldPublicItem, err := os.ReadFile(certItem.PublicKeyPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar cert model.RootCert\n\tif err := copier.Copy(&cert, req); err != nil {\n\t\treturn err\n\t}\n\tcert.PrivateKeyPath = fmt.Sprintf(\"%s/.ssh/%s\", currentUser.HomeDir, req.Name)\n\tcert.PublicKeyPath = fmt.Sprintf(\"%s/.ssh/%s.pub\", currentUser.HomeDir, req.Name)\n\tif err := os.WriteFile(cert.PrivateKeyPath, []byte(req.PrivateKey), constant.FilePerm); err != nil {\n\t\treturn err\n\t}\n\tif err := os.WriteFile(cert.PublicKeyPath, []byte(req.PublicKey), constant.FilePerm); err != nil {\n\t\treturn err\n\t}\n\n\tauthFilePath := currentUser.HomeDir + \"/.ssh/authorized_keys\"\n\tauthItem, err := os.ReadFile(authFilePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\toldPublic := strings.ReplaceAll(string(oldPublicItem), \"\\n\", \"\")\n\tnewPublic := strings.ReplaceAll(string(req.PublicKey), \"\\n\", \"\")\n\tlines := strings.Split(string(authItem), \"\\n\")\n\tvar newFiles []string\n\tfor i := 0; i < len(lines); i++ {\n\t\tif len(lines[i]) != 0 && lines[i] != oldPublic && lines[i] != newPublic {\n\t\t\tnewFiles = append(newFiles, lines[i])\n\t\t}\n\t}\n\tnewFiles = append(newFiles, newPublic)\n\tif err := os.WriteFile(authFilePath, []byte(strings.Join(newFiles, \"\\n\")), constant.FilePerm); err != nil {\n\t\treturn fmt.Errorf(\"refresh authorized_keys failed, err: %v\", err)\n\t}\n\tif len(cert.PassPhrase) != 0 {\n\t\tcert.PassPhrase, _ = encrypt.StringEncrypt(cert.PassPhrase)\n\t}\n\treturn hostRepo.SaveCert(&cert)\n}\n\nfunc (u *SSHService) SearchRootCerts(req dto.SearchWithPage) (int64, interface{}, error) {\n\ttotal, records, err := hostRepo.PageCert(req.Page, req.PageSize)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar datas []dto.RootCert\n\tfor i := 0; i < len(records); i++ {\n\t\tpublicItem, err := os.ReadFile(records[i].PublicKeyPath)\n\t\tvar publicBase64 string\n\t\tif err == nil && len(publicItem) != 0 {\n\t\t\tpublicBase64 = base64.StdEncoding.EncodeToString(publicItem)\n\t\t}\n\t\tprivateItem, _ := os.ReadFile(records[i].PrivateKeyPath)\n\t\tvar privateBase64 string\n\t\tif err == nil && len(publicItem) != 0 {\n\t\t\tprivateBase64 = base64.StdEncoding.EncodeToString(privateItem)\n\t\t}\n\t\tpassPhrase, _ := encrypt.StringDecryptWithBase64(records[i].PassPhrase)\n\t\tdatas = append(datas, dto.RootCert{\n\t\t\tID:             records[i].ID,\n\t\t\tCreatedAt:      records[i].CreatedAt,\n\t\t\tName:           records[i].Name,\n\t\t\tEncryptionMode: records[i].EncryptionMode,\n\t\t\tPassPhrase:     passPhrase,\n\t\t\tPublicKey:      publicBase64,\n\t\t\tPrivateKey:     privateBase64,\n\t\t\tDescription:    records[i].Description,\n\t\t})\n\t}\n\treturn total, datas, err\n}\n\nfunc (u *SSHService) DeleteRootCerts(req dto.ForceDelete) error {\n\tcurrentUser, err := user.Current()\n\tif err != nil && !req.ForceDelete {\n\t\treturn fmt.Errorf(\"load current user failed, err: %v\", err)\n\t}\n\tauthFilePath := currentUser.HomeDir + \"/.ssh/authorized_keys\"\n\tauthItem, err := os.ReadFile(authFilePath)\n\tif err != nil && !req.ForceDelete {\n\t\treturn err\n\t}\n\tfor _, id := range req.IDs {\n\t\tcert, _ := hostRepo.GetCert(repo.WithByID(id))\n\t\tif cert.ID == 0 {\n\t\t\tif !req.ForceDelete {\n\t\t\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tpublicItem, err := os.ReadFile(cert.PublicKeyPath)\n\t\tif err != nil && !req.ForceDelete {\n\t\t\treturn err\n\t\t}\n\t\tnewFile := bytes.ReplaceAll(authItem, publicItem, nil)\n\t\tif err := os.WriteFile(authFilePath, newFile, constant.FilePerm); err != nil && !req.ForceDelete {\n\t\t\treturn fmt.Errorf(\"refresh authorized_keys failed, err: %v\", err)\n\t\t}\n\t\t_ = os.Remove(cert.PublicKeyPath)\n\t\t_ = os.Remove(cert.PrivateKeyPath)\n\t\tif err := hostRepo.DeleteCert(repo.WithByID(id)); err != nil && !req.ForceDelete {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype sshFileItem struct {\n\tName string\n\tYear int\n}\n\nfunc (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []dto.SSHHistory, error) {\n\tvar fileList []sshFileItem\n\tvar data []dto.SSHHistory\n\tbaseDir := \"/var/log\"\n\tfileItems, err := os.ReadDir(baseDir)\n\tif err != nil {\n\t\treturn 0, data, err\n\t}\n\tfor _, item := range fileItems {\n\t\tif item.IsDir() || (!strings.HasPrefix(item.Name(), \"secure\") && !strings.HasPrefix(item.Name(), \"auth\")) {\n\t\t\tcontinue\n\t\t}\n\t\tinfo, _ := item.Info()\n\t\titemPath := path.Join(baseDir, info.Name())\n\t\tif !strings.HasSuffix(item.Name(), \".gz\") {\n\t\t\tfileList = append(fileList, sshFileItem{Name: itemPath, Year: info.ModTime().Year()})\n\t\t\tcontinue\n\t\t}\n\t\titemFileName := strings.TrimSuffix(itemPath, \".gz\")\n\t\tif _, err := os.Stat(itemFileName); err != nil && os.IsNotExist(err) {\n\t\t\tif err := handleGunzip(itemPath); err == nil {\n\t\t\t\tfileList = append(fileList, sshFileItem{Name: itemFileName, Year: info.ModTime().Year()})\n\t\t\t}\n\t\t}\n\t}\n\tfileList = sortFileList(fileList)\n\n\tcommand := \"\"\n\tif len(req.Info) != 0 {\n\t\tif cmd.CheckIllegal(req.Info) {\n\t\t\treturn 0, data, buserr.New(\"ErrCmdIllegal\")\n\t\t}\n\t\tcommand = fmt.Sprintf(\" | grep '%s'\", req.Info)\n\t}\n\n\tshowCountFrom := (req.Page - 1) * req.PageSize\n\tshowCountTo := req.Page * req.PageSize\n\tnyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\titemFailed, itemTotal := 0, 0\n\tfor _, file := range fileList {\n\t\tcommandItem := \"\"\n\t\tif strings.HasPrefix(path.Base(file.Name), \"secure\") {\n\t\t\tswitch req.Status {\n\t\t\tcase constant.StatusSuccess:\n\t\t\t\tcommandItem = fmt.Sprintf(\"cat %s | grep -a Accepted %s\", file.Name, command)\n\t\t\tcase constant.StatusFailed:\n\t\t\t\tcommandItem = fmt.Sprintf(\"cat %s | grep -a 'Failed password for' %s\", file.Name, command)\n\t\t\tdefault:\n\t\t\t\tcommandItem = fmt.Sprintf(\"cat %s | grep -aE '(Failed password for|Accepted)' %s\", file.Name, command)\n\t\t\t}\n\t\t}\n\t\tif strings.HasPrefix(path.Base(file.Name), \"auth.log\") {\n\t\t\tswitch req.Status {\n\t\t\tcase constant.StatusSuccess:\n\t\t\t\tcommandItem = fmt.Sprintf(\"cat %s | grep -a Accepted %s\", file.Name, command)\n\t\t\tcase constant.StatusFailed:\n\t\t\t\tcommandItem = fmt.Sprintf(\"cat %s | grep -aE 'Failed password for|Connection closed by authenticating user' %s\", file.Name, command)\n\t\t\tdefault:\n\t\t\t\tcommandItem = fmt.Sprintf(\"cat %s | grep -aE \\\"(Failed password for|Connection closed by authenticating user|Accepted)\\\" %s\", file.Name, command)\n\t\t\t}\n\t\t}\n\t\tdataItem, successCount, failedCount := loadSSHData(ctx, commandItem, showCountFrom, showCountTo, file.Year, nyc)\n\t\titemFailed += failedCount\n\t\titemTotal += successCount + failedCount\n\t\tshowCountFrom = showCountFrom - (successCount + failedCount)\n\t\tif showCountTo != -1 {\n\t\t\tshowCountTo = showCountTo - (successCount + failedCount)\n\t\t}\n\t\tdata = append(data, dataItem...)\n\t}\n\n\ttotal := itemTotal\n\tif req.Status == constant.StatusFailed {\n\t\ttotal = itemFailed\n\t}\n\tif req.Status == constant.StatusSuccess {\n\t\ttotal = itemTotal - itemFailed\n\t}\n\treturn int64(total), data, nil\n}\n\nfunc (u *SSHService) ExportLog(ctx *gin.Context, req dto.SearchSSHLog) (string, error) {\n\t_, logs, err := u.LoadLog(ctx, req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(logs) == 0 {\n\t\treturn \"\", buserr.New(\"ErrRecordNotFound\")\n\t}\n\ttmpFileName := path.Join(global.Dir.TmpDir, \"export/ssh-log\", fmt.Sprintf(\"1panel-ssh-log-%s.csv\", time.Now().Format(constant.DateTimeSlimLayout)))\n\tif _, err := os.Stat(path.Dir(tmpFileName)); err != nil {\n\t\t_ = os.MkdirAll(path.Dir(tmpFileName), constant.DirPerm)\n\t}\n\tif err := csvexport.ExportSSHLogs(tmpFileName, logs); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn tmpFileName, nil\n}\n\nfunc (u *SSHService) LoadSSHFile(name string) (string, error) {\n\tvar fileName string\n\tswitch name {\n\tcase \"authKeys\":\n\t\tcurrentUser, err := user.Current()\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"load current user failed, err: %v\", err)\n\t\t}\n\t\tfileName = currentUser.HomeDir + \"/.ssh/authorized_keys\"\n\tcase \"sshdConf\":\n\t\tfileName = \"/etc/ssh/sshd_config\"\n\tdefault:\n\t\treturn \"\", buserr.WithName(\"ErrNotSupportType\", name)\n\t}\n\tif _, err := os.Stat(fileName); err != nil {\n\t\treturn \"\", buserr.WithErr(\"ErrHttpReqNotFound\", err)\n\t}\n\tcontent, err := os.ReadFile(fileName)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(content), nil\n}\n\nfunc (u *SSHService) UpdateByFile(req dto.SettingUpdate) error {\n\tvar fileName string\n\tswitch req.Key {\n\tcase \"authKeys\":\n\t\tcurrentUser, err := user.Current()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"load current user failed, err: %v\", err)\n\t\t}\n\t\tfileName = currentUser.HomeDir + \"/.ssh/authorized_keys\"\n\tcase \"sshdConf\":\n\t\tfileName = \"/etc/ssh/sshd_config\"\n\tdefault:\n\t\treturn buserr.WithName(\"ErrNotSupportType\", req.Key)\n\t}\n\tfile, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\tif _, err = file.WriteString(req.Value); err != nil {\n\t\treturn err\n\t}\n\tif req.Key == \"authKeys\" {\n\t\treturn nil\n\t}\n\tserviceName, err := loadServiceName()\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = controller.HandleRestart(serviceName)\n\treturn nil\n}\n\nfunc sortFileList(fileNames []sshFileItem) []sshFileItem {\n\tif len(fileNames) < 2 {\n\t\treturn fileNames\n\t}\n\tif strings.HasPrefix(path.Base(fileNames[0].Name), \"secure\") {\n\t\tvar itemFile []sshFileItem\n\t\tsort.Slice(fileNames, func(i, j int) bool {\n\t\t\treturn fileNames[i].Name > fileNames[j].Name\n\t\t})\n\t\titemFile = append(itemFile, fileNames[len(fileNames)-1])\n\t\titemFile = append(itemFile, fileNames[:len(fileNames)-1]...)\n\t\treturn itemFile\n\t}\n\tsort.Slice(fileNames, func(i, j int) bool {\n\t\treturn fileNames[i].Name < fileNames[j].Name\n\t})\n\treturn fileNames\n}\n\nfunc updateSSHConf(oldFiles []string, param string, value string) []string {\n\tvar valueItems []string\n\tif param != \"ListenAddress\" {\n\t\tvalueItems = append(valueItems, value)\n\t} else {\n\t\tif value != \"\" {\n\t\t\tvalueItems = strings.Split(value, \",\")\n\t\t}\n\t}\n\tvar newFiles []string\n\tfor _, line := range oldFiles {\n\t\tlineItem := strings.TrimSpace(line)\n\t\tif (strings.HasPrefix(lineItem, param) || strings.HasPrefix(lineItem, fmt.Sprintf(\"#%s\", param))) && len(valueItems) != 0 {\n\t\t\tnewFiles = append(newFiles, fmt.Sprintf(\"%s %s\", param, valueItems[0]))\n\t\t\tvalueItems = valueItems[1:]\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(lineItem, param) && len(valueItems) == 0 {\n\t\t\tnewFiles = append(newFiles, fmt.Sprintf(\"#%s\", line))\n\t\t\tcontinue\n\t\t}\n\t\tnewFiles = append(newFiles, line)\n\t}\n\tif len(valueItems) != 0 {\n\t\tfor _, item := range valueItems {\n\t\t\tnewFiles = append(newFiles, fmt.Sprintf(\"%s %s\", param, item))\n\t\t}\n\t}\n\treturn newFiles\n}\n\nfunc loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, currentYear int, nyc *time.Location) ([]dto.SSHHistory, int, int) {\n\tvar (\n\t\tdatas        []dto.SSHHistory\n\t\tsuccessCount int\n\t\tfailedCount  int\n\t)\n\tgetLoc, err := geo.NewGeo()\n\tif err != nil {\n\t\treturn datas, 0, 0\n\t}\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(command)\n\tif err != nil {\n\t\treturn datas, 0, 0\n\t}\n\tlines := strings.Split(stdout, \"\\n\")\n\tfor i := len(lines) - 1; i >= 0; i-- {\n\t\tvar itemData dto.SSHHistory\n\t\tswitch {\n\t\tcase strings.Contains(lines[i], \"Failed password for\"):\n\t\t\titemData = loadFailedSecureDatas(lines[i])\n\t\t\tif checkIsStandard(itemData) {\n\t\t\t\tif successCount+failedCount >= showCountFrom && (showCountTo == -1 || successCount+failedCount < showCountTo) {\n\t\t\t\t\titemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))\n\t\t\t\t\titemData.Date = loadDate(currentYear, itemData.DateStr, nyc)\n\t\t\t\t\tdatas = append(datas, itemData)\n\t\t\t\t}\n\t\t\t\tfailedCount++\n\t\t\t}\n\t\tcase strings.Contains(lines[i], \"Connection closed by authenticating user\"):\n\t\t\titemData = loadFailedAuthDatas(lines[i])\n\t\t\tif checkIsStandard(itemData) {\n\t\t\t\tif successCount+failedCount >= showCountFrom && (showCountTo == -1 || successCount+failedCount < showCountTo) {\n\t\t\t\t\titemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))\n\t\t\t\t\titemData.Date = loadDate(currentYear, itemData.DateStr, nyc)\n\t\t\t\t\tdatas = append(datas, itemData)\n\t\t\t\t}\n\t\t\t\tfailedCount++\n\t\t\t}\n\t\tcase strings.Contains(lines[i], \"Accepted \"):\n\t\t\titemData = loadSuccessDatas(lines[i])\n\t\t\tif checkIsStandard(itemData) {\n\t\t\t\tif successCount+failedCount >= showCountFrom && (showCountTo == -1 || successCount+failedCount < showCountTo) {\n\t\t\t\t\titemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))\n\t\t\t\t\titemData.Date = loadDate(currentYear, itemData.DateStr, nyc)\n\t\t\t\t\tdatas = append(datas, itemData)\n\t\t\t\t}\n\t\t\t\tsuccessCount++\n\t\t\t}\n\t\t}\n\t}\n\treturn datas, successCount, failedCount\n}\n\nfunc loadSuccessDatas(line string) dto.SSHHistory {\n\tvar data dto.SSHHistory\n\tparts := strings.Fields(line)\n\tindex, dataStr := analyzeDateStr(parts)\n\tif dataStr == \"\" {\n\t\treturn data\n\t}\n\tdata.DateStr = dataStr\n\tdata.AuthMode = parts[4+index]\n\tdata.User = parts[6+index]\n\tdata.Address = parts[8+index]\n\tdata.Port = parts[10+index]\n\tdata.Status = constant.StatusSuccess\n\treturn data\n}\nfunc loadFailedAuthDatas(line string) dto.SSHHistory {\n\tvar data dto.SSHHistory\n\tparts := strings.Fields(line)\n\tindex, dataStr := analyzeDateStr(parts)\n\tif dataStr == \"\" {\n\t\treturn data\n\t}\n\tdata.DateStr = dataStr\n\tswitch index {\n\tcase 1:\n\t\tdata.User = parts[9]\n\tcase 2:\n\t\tdata.User = parts[10]\n\tdefault:\n\t\tdata.User = parts[7]\n\t}\n\tdata.AuthMode = parts[6+index]\n\tdata.Address = parts[9+index]\n\tdata.Port = parts[11+index]\n\tdata.Status = constant.StatusFailed\n\tif strings.Contains(line, \": \") {\n\t\tdata.Message = strings.Split(line, \": \")[1]\n\t}\n\treturn data\n}\nfunc loadFailedSecureDatas(line string) dto.SSHHistory {\n\tvar data dto.SSHHistory\n\tparts := strings.Fields(line)\n\tindex, dataStr := analyzeDateStr(parts)\n\tif dataStr == \"\" {\n\t\treturn data\n\t}\n\tdata.DateStr = dataStr\n\tif strings.Contains(line, \" invalid \") {\n\t\tdata.AuthMode = parts[4+index]\n\t\tindex += 2\n\t} else {\n\t\tdata.AuthMode = parts[4+index]\n\t}\n\tdata.User = parts[6+index]\n\tdata.Address = parts[8+index]\n\tdata.Port = parts[10+index]\n\tdata.Status = constant.StatusFailed\n\tif strings.Contains(line, \": \") {\n\t\tdata.Message = strings.Split(line, \": \")[1]\n\t}\n\treturn data\n}\n\nfunc checkIsStandard(item dto.SSHHistory) bool {\n\tif len(item.Address) == 0 || net.ParseIP(item.Address) == nil {\n\t\treturn false\n\t}\n\tportItem, _ := strconv.Atoi(item.Port)\n\treturn portItem > 0 && portItem < 65536\n}\n\nfunc handleGunzip(path string) error {\n\tif err := cmd.RunDefaultBashCf(\"gunzip %s\", path); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc loadServiceName() (string, error) {\n\tif exist, _ := controller.CheckExist(\"sshd\"); exist {\n\t\treturn \"sshd\", nil\n\t} else if exist, _ := controller.CheckExist(\"ssh\"); exist {\n\t\treturn \"ssh\", nil\n\t}\n\treturn \"\", errors.New(\"The ssh or sshd service is unavailable\")\n}\n\nfunc loadDate(currentYear int, DateStr string, nyc *time.Location) time.Time {\n\titemDate, err := time.ParseInLocation(\"2006 Jan 2 15:04:05\", fmt.Sprintf(\"%d %s\", currentYear, DateStr), nyc)\n\tif err != nil {\n\t\titemDate, _ = time.ParseInLocation(\"2006 Jan 2 15:04:05\", DateStr, nyc)\n\t}\n\treturn itemDate\n}\n\nfunc analyzeDateStr(parts []string) (int, string) {\n\tt, err := time.Parse(time.RFC3339Nano, parts[0])\n\tif err == nil {\n\t\tif len(parts) < 12 {\n\t\t\treturn 0, \"\"\n\t\t}\n\t\treturn 0, t.Format(\"2006 Jan 2 15:04:05\")\n\t}\n\tt, err = time.Parse(constant.DateTimeLayout, fmt.Sprintf(\"%s %s\", parts[0], parts[1]))\n\tif err == nil {\n\t\tif len(parts) < 14 {\n\t\t\treturn 0, \"\"\n\t\t}\n\t\treturn 1, t.Format(\"2006 Jan 2 15:04:05\")\n\t}\n\n\tif len(parts) < 14 {\n\t\treturn 0, \"\"\n\t}\n\treturn 2, fmt.Sprintf(\"%s %s %s\", parts[0], parts[1], parts[2])\n}\n\nfunc loadEncryptioMode(content string) string {\n\tif strings.HasPrefix(content, \"ssh-rsa\") {\n\t\treturn \"rsa\"\n\t}\n\tif strings.HasPrefix(content, \"ssh-ed25519\") {\n\t\treturn \"ed25519\"\n\t}\n\tif strings.HasPrefix(content, \"ssh-ecdsa\") {\n\t\treturn \"ecdsa\"\n\t}\n\tif strings.HasPrefix(content, \"ssh-dsa\") {\n\t\treturn \"dsa\"\n\t}\n\treturn \"\"\n}\n\nfunc updateLocalConn(newPort uint) error {\n\tconn, _ := settingRepo.GetValueByKey(\"LocalSSHConn\")\n\tif len(conn) == 0 {\n\t\treturn nil\n\t}\n\tconnItem, err := encrypt.StringDecrypt(conn)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar data dto.SSHConnData\n\tif err := json.Unmarshal([]byte(connItem), &data); err != nil {\n\t\treturn err\n\t}\n\tdata.Port = newPort\n\tconnNew, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\tconnNewItem, err := encrypt.StringEncrypt(string(connNew))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn settingRepo.Update(\"LocalSSHConn\", connNewItem)\n}\n\nfunc updateSSHSocketFile(newPort string) error {\n\tactive, _ := controller.CheckActive(\"ssh.socket\")\n\tif !active {\n\t\treturn nil\n\t}\n\tfilepath := \"/usr/lib/systemd/system/ssh.socket\"\n\tfile, err := os.ReadFile(filepath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlines := strings.Split(string(file), \"\\n\")\n\tfor i := 0; i < len(lines); i++ {\n\t\tif strings.HasPrefix(lines[i], \"ListenStream=\") {\n\t\t\tparts := strings.Split(lines[i], \":\")\n\t\t\tif len(parts) > 1 {\n\t\t\t\tlines[i] = strings.ReplaceAll(lines[i], parts[len(parts)-1], newPort)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tparts = strings.Split(lines[i], \"=\")\n\t\t\tif len(parts) > 1 {\n\t\t\t\tlines[i] = strings.ReplaceAll(lines[i], parts[len(parts)-1], newPort)\n\t\t\t}\n\t\t}\n\t}\n\tfileItem, err := os.OpenFile(filepath, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer fileItem.Close()\n\tif _, err = fileItem.WriteString(strings.Join(lines, \"\\n\")); err != nil {\n\t\treturn err\n\t}\n\t_ = controller.Reload()\n\t_ = controller.HandleRestart(\"ssh.socket\")\n\treturn nil\n}\n\nfunc loadSSHPort() string {\n\tport := \"22\"\n\tsshConf, err := os.ReadFile(sshPath)\n\tif err != nil {\n\t\treturn port\n\t}\n\tlines := strings.Split(string(sshConf), \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.HasPrefix(line, \"Port \") {\n\t\t\tportStr := strings.ReplaceAll(line, \"Port \", \"\")\n\t\t\tportItem, _ := strconv.Atoi(portStr)\n\t\t\tif portItem > 0 && portItem < 65535 {\n\t\t\t\treturn portStr\n\t\t\t}\n\t\t}\n\t}\n\treturn port\n}\n"
  },
  {
    "path": "agent/app/service/system.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"os/exec\"\n)\n\ntype SystemService struct{}\n\ntype ISystemService interface {\n\tIsComponentExist(name string) response.ComponentInfo\n}\n\nfunc NewISystemService() ISystemService {\n\treturn &SystemService{}\n}\n\nfunc (s *SystemService) IsComponentExist(name string) response.ComponentInfo {\n\tinfo := response.ComponentInfo{}\n\tpath, err := exec.LookPath(name)\n\tif err != nil {\n\t\tinfo.Exists = false\n\t\tinfo.Error = err.Error()\n\t\treturn info\n\t}\n\tinfo.Exists = true\n\tinfo.Path = path\n\treturn info\n}\n"
  },
  {
    "path": "agent/app/service/task.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n)\n\ntype TaskLogService struct{}\n\ntype ITaskLogService interface {\n\tPage(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error)\n\tSyncForRestart() error\n\tCountExecutingTask() (int64, error)\n}\n\nfunc NewITaskService() ITaskLogService {\n\treturn &TaskLogService{}\n}\n\nfunc (u *TaskLogService) Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error) {\n\topts := []repo.DBOption{\n\t\trepo.WithOrderDesc(\"created_at\"),\n\t}\n\tif req.Status != \"\" {\n\t\topts = append(opts, repo.WithByStatus(req.Status))\n\t}\n\tif req.Type != \"\" {\n\t\topts = append(opts, repo.WithByType(req.Type))\n\t}\n\n\ttotal, tasks, err := taskRepo.Page(\n\t\treq.Page,\n\t\treq.PageSize,\n\t\topts...,\n\t)\n\tvar items []dto.TaskDTO\n\tfor _, t := range tasks {\n\t\titem := dto.TaskDTO{\n\t\t\tTask: t,\n\t\t}\n\t\titems = append(items, item)\n\t}\n\treturn total, items, err\n}\n\nfunc (u *TaskLogService) SyncForRestart() error {\n\treturn taskRepo.UpdateRunningTaskToFailed()\n}\n\nfunc (u *TaskLogService) CountExecutingTask() (int64, error) {\n\treturn taskRepo.CountExecutingTask()\n}\n"
  },
  {
    "path": "agent/app/service/tensorrt_llm.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/subosito/gotenv\"\n\t\"gopkg.in/yaml.v3\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/ai\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/compose\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/env\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n)\n\ntype TensorRTLLMService struct{}\n\ntype ITensorRTLLMService interface {\n\tPage(req request.TensorRTLLMSearch) response.TensorRTLLMsRes\n\tCreate(create request.TensorRTLLMCreate) error\n\tUpdate(req request.TensorRTLLMUpdate) error\n\tDelete(id uint) error\n\tOperate(req request.TensorRTLLMOperate) error\n}\n\nfunc NewITensorRTLLMService() ITensorRTLLMService {\n\treturn &TensorRTLLMService{}\n}\n\nfunc (t TensorRTLLMService) Page(req request.TensorRTLLMSearch) response.TensorRTLLMsRes {\n\tvar (\n\t\tres   response.TensorRTLLMsRes\n\t\titems []response.TensorRTLLMDTO\n\t)\n\n\ttotal, data, _ := tensorrtLLMRepo.Page(req.PageInfo.Page, req.PageInfo.PageSize)\n\tfor _, item := range data {\n\t\t_ = syncTensorRTLLMContainerStatus(&item)\n\t\tserverDTO := response.TensorRTLLMDTO{\n\t\t\tTensorRTLLM: item,\n\t\t}\n\t\tenvs, _ := gotenv.Unmarshal(item.Env)\n\t\tserverDTO.Version = envs[\"VERSION\"]\n\t\tserverDTO.ModelDir = envs[\"MODEL_PATH\"]\n\t\tserverDTO.Dir = path.Join(global.Dir.TensorRTLLMDir, item.Name)\n\t\tserverDTO.Image = envs[\"IMAGE\"]\n\t\tserverDTO.Command = getCommand(item.Env)\n\n\t\tfor k, v := range envs {\n\t\t\tif strings.Contains(k, \"CONTAINER_PORT\") || strings.Contains(k, \"HOST_PORT\") {\n\t\t\t\tif strings.Contains(k, \"CONTAINER_PORT\") {\n\t\t\t\t\tmatches := re.GetRegex(re.TrailingDigitsPattern).FindStringSubmatch(k)\n\t\t\t\t\tif len(matches) < 2 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tcontainerPort, err := strconv.Atoi(v)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\thostPort, err := strconv.Atoi(envs[fmt.Sprintf(\"HOST_PORT_%s\", matches[1])])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\thostIP := envs[fmt.Sprintf(\"HOST_IP_%s\", matches[1])]\n\t\t\t\t\tif hostIP == \"\" {\n\t\t\t\t\t\thostIP = \"0.0.0.0\"\n\t\t\t\t\t}\n\t\t\t\t\tserverDTO.ExposedPorts = append(serverDTO.ExposedPorts, request.ExposedPort{\n\t\t\t\t\t\tContainerPort: containerPort,\n\t\t\t\t\t\tHostPort:      hostPort,\n\t\t\t\t\t\tHostIP:        hostIP,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcomposeByte, err := files.NewFileOp().GetContent(path.Join(global.Dir.TensorRTLLMDir, item.Name, \"docker-compose.yml\"))\n\t\tif err == nil {\n\t\t\tserverDTO.Environments, _ = getDockerComposeEnvironments(composeByte)\n\t\t}\n\t\tvolumes, err := getDockerComposeVolumes(composeByte)\n\t\tif err == nil {\n\t\t\tvar defaultVolumes = map[string]string{\n\t\t\t\t\"${MODEL_PATH}\": \"${MODEL_PATH}\",\n\t\t\t}\n\t\t\tfor _, volume := range volumes {\n\t\t\t\texist := false\n\t\t\t\tfor key, value := range defaultVolumes {\n\t\t\t\t\tif key == volume.Source && value == volume.Target {\n\t\t\t\t\t\texist = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !exist {\n\t\t\t\t\tserverDTO.Volumes = append(serverDTO.Volumes, volume)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\titems = append(items, serverDTO)\n\t}\n\tres.Total = total\n\tres.Items = items\n\treturn res\n}\n\nfunc handleLLMParams(llm *model.TensorRTLLM, create request.TensorRTLLMCreate) error {\n\tvar composeContent []byte\n\tif llm.ID == 0 {\n\t\tcomposeContent = ai.DefaultTensorrtLLMCompose\n\t} else {\n\t\tcomposeContent = []byte(llm.DockerCompose)\n\t}\n\tcomposeMap := make(map[string]interface{})\n\tif err := yaml.Unmarshal(composeContent, &composeMap); err != nil {\n\t\treturn err\n\t}\n\tservices, serviceValid := composeMap[\"services\"].(map[string]interface{})\n\tif !serviceValid {\n\t\treturn buserr.New(\"ErrFileParse\")\n\t}\n\tserviceName := \"\"\n\tserviceValue := make(map[string]interface{})\n\n\tif llm.ID > 0 {\n\t\tserviceName = llm.Name\n\t\tserviceValue = services[serviceName].(map[string]interface{})\n\t} else {\n\t\tfor name, service := range services {\n\t\t\tserviceName = name\n\t\t\tserviceValue = service.(map[string]interface{})\n\t\t\tbreak\n\t\t}\n\t\tdelete(services, serviceName)\n\t}\n\n\tdelete(serviceValue, \"ports\")\n\tif len(create.ExposedPorts) > 0 {\n\t\tvar ports []interface{}\n\t\tfor i := range create.ExposedPorts {\n\t\t\tcontainerPortStr := fmt.Sprintf(\"CONTAINER_PORT_%d\", i)\n\t\t\thostPortStr := fmt.Sprintf(\"HOST_PORT_%d\", i)\n\t\t\thostIPStr := fmt.Sprintf(\"HOST_IP_%d\", i)\n\t\t\tports = append(ports, fmt.Sprintf(\"${%s}:${%s}:${%s}\", hostIPStr, hostPortStr, containerPortStr))\n\t\t}\n\t\tserviceValue[\"ports\"] = ports\n\t}\n\n\tdelete(serviceValue, \"environment\")\n\tvar environments []interface{}\n\tenvironments = append(environments, fmt.Sprintf(\"MODEL_PATH=%s\", create.ModelDir))\n\tfor _, e := range create.Environments {\n\t\tenvironments = append(environments, fmt.Sprintf(\"%s=%s\", e.Key, e.Value))\n\t}\n\tserviceValue[\"environment\"] = environments\n\n\tvar volumes []interface{}\n\tvar defaultVolumes = map[string]string{\n\t\t\"${MODEL_PATH}\": \"${MODEL_PATH}\",\n\t}\n\tfor k, v := range defaultVolumes {\n\t\tvolumes = append(volumes, fmt.Sprintf(\"%s:%s\", k, v))\n\t}\n\tfor _, volume := range create.Volumes {\n\t\tvolumes = append(volumes, fmt.Sprintf(\"%s:%s\", volume.Source, volume.Target))\n\t}\n\tserviceValue[\"volumes\"] = volumes\n\n\tservices[llm.Name] = serviceValue\n\tcomposeByte, err := yaml.Marshal(composeMap)\n\tif err != nil {\n\t\treturn err\n\t}\n\tllm.DockerCompose = string(composeByte)\n\treturn nil\n}\n\nfunc handleLLMEnv(llm *model.TensorRTLLM, create request.TensorRTLLMCreate) gotenv.Env {\n\tenvMap := make(gotenv.Env)\n\tenvMap[\"CONTAINER_NAME\"] = create.ContainerName\n\tenvMap[\"MODEL_PATH\"] = create.ModelDir\n\tenvMap[\"VERSION\"] = create.Version\n\tenvMap[\"IMAGE\"] = create.Image\n\tenvMap[\"COMMAND\"] = create.Command\n\tfor i, port := range create.ExposedPorts {\n\t\tcontainerPortStr := fmt.Sprintf(\"CONTAINER_PORT_%d\", i)\n\t\thostPortStr := fmt.Sprintf(\"HOST_PORT_%d\", i)\n\t\thostIPStr := fmt.Sprintf(\"HOST_IP_%d\", i)\n\t\tenvMap[containerPortStr] = strconv.Itoa(port.ContainerPort)\n\t\tenvMap[hostPortStr] = strconv.Itoa(port.HostPort)\n\t\tenvMap[hostIPStr] = port.HostIP\n\t}\n\torders := []string{\"MODEL_PATH\", \"COMMAND\"}\n\tenvStr, _ := env.MarshalWithOrder(envMap, orders)\n\tllm.Env = envStr\n\treturn envMap\n}\n\nfunc (t TensorRTLLMService) Create(create request.TensorRTLLMCreate) error {\n\tservers, _ := tensorrtLLMRepo.List()\n\tfor _, server := range servers {\n\t\tif server.ContainerName == create.ContainerName {\n\t\t\treturn buserr.New(\"ErrContainerName\")\n\t\t}\n\t\tif server.Name == create.Name {\n\t\t\treturn buserr.New(\"ErrNameIsExist\")\n\t\t}\n\t}\n\tfor _, export := range create.ExposedPorts {\n\t\tif err := checkPortExist(export.HostPort); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := checkContainerName(create.ContainerName); err != nil {\n\t\treturn err\n\t}\n\n\ttensorrtLLMDir := path.Join(global.Dir.TensorRTLLMDir, create.Name)\n\tfilesOp := files.NewFileOp()\n\tif !filesOp.Stat(tensorrtLLMDir) {\n\t\t_ = filesOp.CreateDir(tensorrtLLMDir, 0644)\n\t}\n\tif create.ModelSpeedup {\n\t\tif err := handleModelArchive(create.ModelType, create.ModelDir); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\ttensorrtLLM := &model.TensorRTLLM{\n\t\tName:          create.Name,\n\t\tContainerName: create.ContainerName,\n\t\tStatus:        constant.StatusStarting,\n\t\tModelType:     create.ModelType,\n\t\tModelSpeedup:  create.ModelSpeedup,\n\t}\n\n\tif err := handleLLMParams(tensorrtLLM, create); err != nil {\n\t\treturn err\n\t}\n\tenvMap := handleLLMEnv(tensorrtLLM, create)\n\tllmDir := path.Join(global.Dir.TensorRTLLMDir, create.Name)\n\tenvPath := path.Join(llmDir, \".env\")\n\tif err := env.WriteWithOrder(envMap, envPath, []string{\"MODEL_PATH\", \"COMMAND\"}); err != nil {\n\t\treturn err\n\t}\n\tdockerComposePath := path.Join(llmDir, \"docker-compose.yml\")\n\tif err := filesOp.SaveFile(dockerComposePath, tensorrtLLM.DockerCompose, 0644); err != nil {\n\t\treturn err\n\t}\n\ttensorrtLLM.Status = constant.StatusStarting\n\n\tif err := tensorrtLLMRepo.Create(tensorrtLLM); err != nil {\n\t\treturn err\n\t}\n\tgo startTensorRTLLM(tensorrtLLM)\n\treturn nil\n}\n\nfunc (t TensorRTLLMService) Update(req request.TensorRTLLMUpdate) error {\n\ttensorrtLLM, err := tensorrtLLMRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif tensorrtLLM.ContainerName != req.ContainerName {\n\t\tif err := checkContainerName(req.ContainerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\ttensorrtLLM.ModelType = req.ModelType\n\ttensorrtLLM.ModelSpeedup = req.ModelSpeedup\n\ttensorrtLLM.ContainerName = req.ContainerName\n\n\tif err := handleLLMParams(tensorrtLLM, req.TensorRTLLMCreate); err != nil {\n\t\treturn err\n\t}\n\n\tenvMap := handleLLMEnv(tensorrtLLM, req.TensorRTLLMCreate)\n\tenvStr, _ := gotenv.Marshal(envMap)\n\ttensorrtLLM.Env = envStr\n\tllmDir := path.Join(global.Dir.TensorRTLLMDir, tensorrtLLM.Name)\n\tenvPath := path.Join(llmDir, \".env\")\n\tif err := env.WriteWithOrder(envMap, envPath, []string{\"MODEL_PATH\", \"COMMAND\"}); err != nil {\n\t\treturn err\n\t}\n\tdockerComposePath := path.Join(llmDir, \"docker-compose.yml\")\n\tif err := files.NewFileOp().SaveFile(dockerComposePath, tensorrtLLM.DockerCompose, 0644); err != nil {\n\t\treturn err\n\t}\n\ttensorrtLLM.Status = constant.StatusStarting\n\tif err := tensorrtLLMRepo.Save(tensorrtLLM); err != nil {\n\t\treturn err\n\t}\n\tgo startTensorRTLLM(tensorrtLLM)\n\treturn nil\n}\n\nfunc (t TensorRTLLMService) Delete(id uint) error {\n\ttensorrtLLM, err := tensorrtLLMRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcomposePath := path.Join(global.Dir.TensorRTLLMDir, tensorrtLLM.Name, \"docker-compose.yml\")\n\t_, _ = compose.Down(composePath)\n\t_ = files.NewFileOp().DeleteDir(path.Join(global.Dir.TensorRTLLMDir, tensorrtLLM.Name))\n\treturn tensorrtLLMRepo.DeleteBy(repo.WithByID(id))\n}\n\nfunc (t TensorRTLLMService) Operate(req request.TensorRTLLMOperate) error {\n\ttensorrtLLM, err := tensorrtLLMRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcomposePath := path.Join(global.Dir.TensorRTLLMDir, tensorrtLLM.Name, \"docker-compose.yml\")\n\tvar out string\n\tswitch req.Operate {\n\tcase \"start\":\n\t\tout, err = compose.Up(composePath)\n\t\ttensorrtLLM.Status = constant.StatusRunning\n\tcase \"stop\":\n\t\tout, err = compose.Down(composePath)\n\t\ttensorrtLLM.Status = constant.StatusStopped\n\tcase \"restart\":\n\t\tout, err = compose.Restart(composePath)\n\t\ttensorrtLLM.Status = constant.StatusRunning\n\t}\n\tif err != nil {\n\t\ttensorrtLLM.Status = constant.StatusError\n\t\ttensorrtLLM.Message = out\n\t}\n\treturn tensorrtLLMRepo.Save(tensorrtLLM)\n}\n\nfunc startTensorRTLLM(tensorrtLLM *model.TensorRTLLM) {\n\tcomposePath := path.Join(global.Dir.TensorRTLLMDir, tensorrtLLM.Name, \"docker-compose.yml\")\n\tif tensorrtLLM.Status != constant.StatusNormal {\n\t\t_, _ = compose.Down(composePath)\n\t}\n\tif out, err := compose.Up(composePath); err != nil {\n\t\ttensorrtLLM.Status = constant.StatusError\n\t\ttensorrtLLM.Message = out\n\t} else {\n\t\ttensorrtLLM.Status = constant.StatusRunning\n\t\ttensorrtLLM.Message = \"\"\n\t}\n\t_ = syncTensorRTLLMContainerStatus(tensorrtLLM)\n}\n\nfunc syncTensorRTLLMContainerStatus(tensorrtLLM *model.TensorRTLLM) error {\n\tcontainerNames := []string{tensorrtLLM.ContainerName}\n\tcli, err := docker.NewClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\tcontainers, err := cli.ListContainersByName(containerNames)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(containers) == 0 {\n\t\tif tensorrtLLM.Status == constant.StatusStarting {\n\t\t\treturn nil\n\t\t}\n\t\ttensorrtLLM.Status = constant.StatusStopped\n\t\treturn tensorrtLLMRepo.Save(tensorrtLLM)\n\t}\n\tcontainer := containers[0]\n\tswitch container.State {\n\tcase \"exited\":\n\t\ttensorrtLLM.Status = constant.StatusError\n\tcase \"running\":\n\t\ttensorrtLLM.Status = constant.StatusRunning\n\tcase \"paused\":\n\t\ttensorrtLLM.Status = constant.StatusStopped\n\tcase \"restarting\":\n\t\ttensorrtLLM.Status = constant.StatusRestarting\n\tdefault:\n\t\tif tensorrtLLM.Status != constant.StatusStarting {\n\t\t\ttensorrtLLM.Status = constant.StatusStopped\n\t\t}\n\t}\n\treturn tensorrtLLMRepo.Save(tensorrtLLM)\n}\n\nfunc findModelArchive(modelType string) (string, error) {\n\tconst baseDir = \"/home/models\"\n\tprefix := fmt.Sprintf(\"FusionXplay_%s_Accelerator\", modelType)\n\n\tentries, err := os.ReadDir(baseDir)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to read %s: %w\", baseDir, err)\n\t}\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tname := entry.Name()\n\t\tif strings.HasPrefix(name, prefix) && strings.HasSuffix(name, \".tar.gz\") {\n\t\t\treturn filepath.Join(baseDir, name), nil\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"no FusionXplay_%s_Accelerator*.tar.gz found in /home/models\", modelType)\n}\n\nfunc handleModelArchive(modelType string, modelDir string) error {\n\tfilePath, err := findModelArchive(modelType)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfileOp := files.NewFileOp()\n\tif err = fileOp.TarGzExtractPro(filePath, modelDir, \"\"); err != nil {\n\t\treturn err\n\t}\n\tif err = fileOp.ChmodR(path.Join(modelDir, \"fusionxpark_accelerator\"), 0755, false); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc getCommand(envStr string) string {\n\tlines := strings.Split(envStr, \"\\n\")\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif strings.HasPrefix(line, \"COMMAND=\") {\n\t\t\treturn strings.TrimPrefix(line, \"COMMAND=\")\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "agent/app/service/website.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"path\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/jinzhu/copier\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n)\n\ntype WebsiteService struct {\n}\n\ntype IWebsiteService interface {\n\tPageWebsite(req request.WebsiteSearch) (int64, []response.WebsiteRes, error)\n\tGetWebsites() ([]response.WebsiteDTO, error)\n\tCreateWebsite(create request.WebsiteCreate) error\n\tOpWebsite(req request.WebsiteOp) error\n\tGetWebsiteOptions(req request.WebsiteOptionReq) ([]response.WebsiteOption, error)\n\tUpdateWebsite(req request.WebsiteUpdate) error\n\tDeleteWebsite(req request.WebsiteDelete) error\n\tGetWebsite(id uint) (response.WebsiteDTO, error)\n\n\tChangePHPVersion(req request.WebsitePHPVersionReq) error\n\tOperateCrossSiteAccess(req request.CrossSiteAccessOp) error\n\tExecComposer(req request.ExecComposerReq) error\n\tChangeGroup(group, newGroup uint) error\n\tChangeDefaultServer(id uint) error\n\tPreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error)\n\tOpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error)\n\tUpdateStream(req request.StreamUpdate) error\n\n\tGetNginxConfigByScope(req request.NginxScopeReq) (*response.WebsiteNginxConfig, error)\n\tUpdateNginxConfigByScope(req request.NginxConfigUpdate) error\n\tGetWebsiteNginxConfig(websiteId uint, configType string) (*response.FileInfo, error)\n\tUpdateNginxConfigFile(req request.WebsiteNginxUpdate) error\n\n\tGetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS, error)\n\tOpWebsiteHTTPS(ctx context.Context, req request.WebsiteHTTPSOp) (*response.WebsiteHTTPS, error)\n\n\tLoadWebsiteDirConfig(req request.WebsiteCommonReq) (*response.WebsiteDirConfig, error)\n\tUpdateSiteDir(req request.WebsiteUpdateDir) error\n\tUpdateSitePermission(req request.WebsiteUpdateDirPermission) error\n\n\tUpdateCors(req request.CorsConfigReq) error\n\tGetCors(websiteID uint) (*request.CorsConfig, error)\n\n\tGetAntiLeech(id uint) (*response.NginxAntiLeechRes, error)\n\tUpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error)\n\n\tOperateRedirect(req request.NginxRedirectReq) (err error)\n\tGetRedirect(id uint) (res []response.NginxRedirectConfig, err error)\n\tUpdateRedirectFile(req request.NginxRedirectUpdate) (err error)\n\n\tUpdateDefaultHtml(req request.WebsiteHtmlUpdate) error\n\tGetDefaultHtml(resourceType string) (*response.WebsiteHtmlRes, error)\n\n\tSetRealIPConfig(req request.WebsiteRealIP) error\n\tGetRealIPConfig(websiteID uint) (*response.WebsiteRealIP, error)\n\n\tGetWebsiteResource(websiteID uint) ([]response.Resource, error)\n\tListDatabases() ([]response.Database, error)\n\tChangeDatabase(req request.ChangeDatabase) error\n\n\tGetLoadBalances(id uint) ([]dto.NginxUpstream, error)\n\tCreateLoadBalance(req request.WebsiteLBCreate) error\n\tDeleteLoadBalance(req request.WebsiteLBDelete) error\n\tUpdateLoadBalance(req request.WebsiteLBUpdate) error\n\tUpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error\n\n\tOperateProxy(req request.WebsiteProxyConfig) (err error)\n\tGetProxies(id uint) (res []request.WebsiteProxyConfig, err error)\n\tUpdateProxyFile(req request.NginxProxyUpdate) (err error)\n\tUpdateProxyCache(req request.NginxProxyCacheUpdate) (err error)\n\tGetProxyCache(id uint) (res response.NginxProxyCache, err error)\n\tClearProxyCache(req request.NginxCommonReq) error\n\tDeleteProxy(req request.WebsiteProxyDel) (err error)\n\tUpdateProxyStatus(req request.WebsiteProxyStatusUpdate) (err error)\n\n\tCreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error)\n\tGetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error)\n\tDeleteWebsiteDomain(domainId uint) error\n\tUpdateWebsiteDomain(req request.WebsiteDomainUpdate) error\n\n\tGetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error)\n\tUpdateRewriteConfig(req request.NginxRewriteUpdate) error\n\tOperateCustomRewrite(req request.CustomRewriteOperate) error\n\tListCustomRewrite() ([]string, error)\n\n\tGetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error)\n\tUpdateAuthBasic(req request.NginxAuthUpdate) (err error)\n\tGetPathAuthBasics(req request.NginxAuthReq) (res []response.NginxPathAuthRes, err error)\n\tUpdatePathAuthBasic(req request.NginxPathAuthUpdate) error\n\n\tBatchOpWebsite(req request.BatchWebsiteOp) error\n\tBatchSetGroup(req request.BatchWebsiteGroup) error\n\tBatchSetHttps(ctx context.Context, req request.BatchWebsiteHttps) error\n}\n\nfunc NewIWebsiteService() IWebsiteService {\n\treturn &WebsiteService{}\n}\n\nfunc (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []response.WebsiteRes, error) {\n\tvar (\n\t\twebsiteDTOs []response.WebsiteRes\n\t\topts        []repo.DBOption\n\t)\n\topts = append(opts, repo.WithOrderRuleBy(req.OrderBy, req.Order), repo.WithOrderRuleBy(\"updated_at\", \"descending\"))\n\tif req.Name != \"\" {\n\t\tdomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithDomainLike(req.Name))\n\t\tif len(domains) > 0 {\n\t\t\tvar websiteIds []uint\n\t\t\tfor _, domain := range domains {\n\t\t\t\twebsiteIds = append(websiteIds, domain.WebsiteID)\n\t\t\t}\n\t\t\topts = append(opts, repo.WithByIDs(websiteIds))\n\t\t} else {\n\t\t\topts = append(opts, websiteRepo.WithDomainLike(req.Name))\n\t\t}\n\t}\n\tif req.WebsiteGroupID != 0 {\n\t\topts = append(opts, websiteRepo.WithGroupID(req.WebsiteGroupID))\n\t}\n\tif req.Type != \"\" {\n\t\topts = append(opts, websiteRepo.WithType(req.Type))\n\t}\n\ttotal, websites, err := websiteRepo.Page(req.Page, req.PageSize, opts...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tfor _, web := range websites {\n\t\tvar (\n\t\t\tappName      string\n\t\t\truntimeName  string\n\t\t\truntimeType  string\n\t\t\tappInstallID uint\n\t\t)\n\t\tswitch web.Type {\n\t\tcase constant.Deployment:\n\t\t\tappInstall, err := appInstallRepo.GetFirst(repo.WithByID(web.AppInstallID))\n\t\t\tif err == nil {\n\t\t\t\tappName = appInstall.Name\n\t\t\t\tappInstallID = appInstall.ID\n\t\t\t}\n\t\tcase constant.Runtime:\n\t\t\truntime, _ := runtimeRepo.GetFirst(context.Background(), repo.WithByID(web.RuntimeID))\n\t\t\tif runtime != nil {\n\t\t\t\truntimeName = runtime.Name\n\t\t\t\truntimeType = runtime.Type\n\t\t\t}\n\t\t}\n\t\tsitePath := GetSitePath(web, SiteDir)\n\n\t\tsiteDTO := response.WebsiteRes{\n\t\t\tID:            web.ID,\n\t\t\tCreatedAt:     web.CreatedAt,\n\t\t\tProtocol:      web.Protocol,\n\t\t\tPrimaryDomain: web.PrimaryDomain,\n\t\t\tType:          web.Type,\n\t\t\tRemark:        web.Remark,\n\t\t\tStatus:        web.Status,\n\t\t\tAlias:         web.Alias,\n\t\t\tAppName:       appName,\n\t\t\tExpireDate:    web.ExpireDate,\n\t\t\tSSLExpireDate: web.WebsiteSSL.ExpireDate,\n\t\t\tSSLStatus:     checkSSLStatus(web.WebsiteSSL.ExpireDate),\n\t\t\tRuntimeName:   runtimeName,\n\t\t\tSitePath:      sitePath,\n\t\t\tAppInstallID:  appInstallID,\n\t\t\tRuntimeType:   runtimeType,\n\t\t\tFavorite:      web.Favorite,\n\t\t\tIPV6:          web.IPV6,\n\t\t}\n\n\t\tif siteDTO.Type == constant.Subsite {\n\t\t\tparentWeb, _ := websiteRepo.GetFirst(repo.WithByID(web.ParentWebsiteID))\n\t\t\tif parentWeb.ID != 0 {\n\t\t\t\tsiteDTO.ParentSite = parentWeb.PrimaryDomain\n\t\t\t}\n\t\t}\n\n\t\tsites, _ := websiteRepo.List(websiteRepo.WithParentID(web.ID))\n\t\tif len(sites) > 0 {\n\t\t\tfor _, site := range sites {\n\t\t\t\tsiteDTO.ChildSites = append(siteDTO.ChildSites, site.PrimaryDomain)\n\t\t\t}\n\t\t}\n\t\twebsiteDTOs = append(websiteDTOs, siteDTO)\n\t}\n\treturn total, websiteDTOs, nil\n}\n\nfunc (w WebsiteService) GetWebsites() ([]response.WebsiteDTO, error) {\n\tvar websiteDTOs []response.WebsiteDTO\n\twebsites, _ := websiteRepo.List(repo.WithOrderRuleBy(\"primary_domain\", \"ascending\"))\n\tfor _, web := range websites {\n\t\tres := response.WebsiteDTO{\n\t\t\tWebsite: web,\n\t\t}\n\t\twebsiteDTOs = append(websiteDTOs, res)\n\t}\n\treturn websiteDTOs, nil\n}\n\nfunc (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) {\n\talias := create.Alias\n\tif alias == \"default\" {\n\t\treturn buserr.New(\"ErrDefaultAlias\")\n\t}\n\tif common.ContainsChinese(alias) {\n\t\talias, err = common.PunycodeEncode(alias)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif exist, _ := websiteRepo.GetBy(websiteRepo.WithAlias(alias)); len(exist) > 0 {\n\t\treturn buserr.New(\"ErrAliasIsExist\")\n\t}\n\tif len(create.FtpPassword) != 0 {\n\t\tpass, err := base64.StdEncoding.DecodeString(create.FtpPassword)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcreate.FtpPassword = string(pass)\n\t}\n\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefaultHttpPort := nginxInstall.HttpPort\n\tdefaultDate, _ := time.Parse(constant.DateLayout, constant.WebsiteDefaultExpireDate)\n\n\twebsite := &model.Website{\n\t\tType:           create.Type,\n\t\tAlias:          alias,\n\t\tRemark:         create.Remark,\n\t\tStatus:         constant.WebRunning,\n\t\tExpireDate:     defaultDate,\n\t\tWebsiteGroupID: create.WebsiteGroupID,\n\t\tProxy:          create.Proxy,\n\t\tSiteDir:        \"/\",\n\t\tAccessLog:      true,\n\t\tErrorLog:       true,\n\t\tIPV6:           create.IPV6,\n\t}\n\n\tvar (\n\t\tdomains       []model.WebsiteDomain\n\t\tappInstall    *model.AppInstall\n\t\truntime       *model.Runtime\n\t\tprimaryDomain string\n\t)\n\tif website.Type == constant.Stream {\n\t\tif create.StreamConfig.StreamPorts == \"\" {\n\t\t\treturn buserr.New(\"ErrTypePortRange\")\n\t\t}\n\t\twebsite.PrimaryDomain = create.Name\n\t\twebsite.Protocol = constant.ProtocolStream\n\t\twebsite.StreamPorts = create.StreamConfig.StreamPorts\n\t\tports := strings.Split(create.StreamConfig.StreamPorts, \",\")\n\t\tfor _, port := range ports {\n\t\t\tportNum, _ := strconv.Atoi(port)\n\t\t\tif err = checkWebsitePort(nginxInstall.HttpsPort, portNum, website.Type); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t} else {\n\t\tdomains, _, _, err = getWebsiteDomains(create.Domains, defaultHttpPort, nginxInstall.HttpsPort, 0)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tprimaryDomain = domains[0].Domain\n\t\tif domains[0].Port != defaultHttpPort {\n\t\t\tprimaryDomain = fmt.Sprintf(\"%s:%v\", domains[0].Domain, domains[0].Port)\n\t\t}\n\t\twebsite.PrimaryDomain = primaryDomain\n\t\twebsite.Protocol = constant.ProtocolHTTP\n\t}\n\n\tcreateTask, err := task.NewTaskWithOps(website.PrimaryDomain, task.TaskCreate, task.TaskScopeWebsite, create.TaskID, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif create.CreateDb {\n\t\tcreateDataBase := func(t *task.Task) error {\n\t\t\tdatabase, _ := databaseRepo.Get(repo.WithByName(create.DbHost))\n\t\t\tif database.ID == 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tdbConfig := create.DataBaseConfig\n\t\t\tswitch database.Type {\n\t\t\tcase constant.AppPostgresql, constant.AppPostgres:\n\t\t\t\toldPostgresqlDb, _ := postgresqlRepo.Get(repo.WithByName(create.DbName), repo.WithByFrom(constant.ResourceLocal))\n\t\t\t\tif oldPostgresqlDb.ID > 0 {\n\t\t\t\t\treturn buserr.New(\"ErrDbUserNotValid\")\n\t\t\t\t}\n\t\t\t\tvar createPostgresql dto.PostgresqlDBCreate\n\t\t\t\tcreatePostgresql.Name = dbConfig.DbName\n\t\t\t\tcreatePostgresql.Username = dbConfig.DbUser\n\t\t\t\tcreatePostgresql.Database = database.Name\n\t\t\t\tcreatePostgresql.Format = dbConfig.DBFormat\n\t\t\t\tcreatePostgresql.Password = dbConfig.DbPassword\n\t\t\t\tcreatePostgresql.From = database.From\n\t\t\t\tcreatePostgresql.SuperUser = true\n\t\t\t\tpgDB, err := NewIPostgresqlService().Create(context.Background(), createPostgresql)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\twebsite.DbID = pgDB.ID\n\t\t\t\twebsite.DbType = database.Type\n\t\t\tcase constant.AppMysql, constant.AppMariaDB:\n\t\t\t\toldMysqlDb, _ := mysqlRepo.Get(repo.WithByName(dbConfig.DbName), repo.WithByFrom(constant.ResourceLocal))\n\t\t\t\tif oldMysqlDb.ID > 0 {\n\t\t\t\t\treturn buserr.New(\"ErrDbUserNotValid\")\n\t\t\t\t}\n\t\t\t\tvar createMysql dto.MysqlDBCreate\n\t\t\t\tcreateMysql.Name = dbConfig.DbName\n\t\t\t\tcreateMysql.Username = dbConfig.DbUser\n\t\t\t\tcreateMysql.Database = database.Name\n\t\t\t\tcreateMysql.Format = dbConfig.DBFormat\n\t\t\t\tcreateMysql.Permission = \"%\"\n\t\t\t\tcreateMysql.Password = dbConfig.DbPassword\n\t\t\t\tcreateMysql.From = database.From\n\t\t\t\tmysqlDB, err := NewIMysqlService().Create(context.Background(), createMysql)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\twebsite.DbID = mysqlDB.ID\n\t\t\t\twebsite.DbType = database.Type\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tcreateTask.AddSubTask(task.GetTaskName(create.DbName, task.TaskCreate, task.TaskScopeDatabase), createDataBase, nil)\n\t}\n\n\tvar proxy string\n\tswitch create.Type {\n\tcase constant.Deployment:\n\t\tif create.AppType == constant.NewApp {\n\t\t\tvar (\n\t\t\t\treq     request.AppInstallCreate\n\t\t\t\tinstall *model.AppInstall\n\t\t\t)\n\t\t\treq.Name = create.AppInstall.Name\n\t\t\treq.AppDetailId = create.AppInstall.AppDetailId\n\t\t\treq.Params = create.AppInstall.Params\n\t\t\treq.AppContainerConfig = create.AppInstall.AppContainerConfig\n\t\t\tinstall, err = NewIAppService().Install(req, true)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tappInstall = install\n\t\t\twebsite.AppInstallID = install.ID\n\t\t\twebsite.Proxy, err = getAppInstallProxyPass(appInstall)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tvar install model.AppInstall\n\t\t\tinstall, err = appInstallRepo.GetFirst(repo.WithByID(create.AppInstallID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tconfigApp := func(t *task.Task) error {\n\t\t\t\tappInstall = &install\n\t\t\t\twebsite.AppInstallID = appInstall.ID\n\t\t\t\twebsite.Proxy, err = getAppInstallProxyPass(appInstall)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tcreateTask.AddSubTask(i18n.GetMsgByKey(\"ConfigApp\"), configApp, nil)\n\t\t}\n\tcase constant.Runtime:\n\t\truntime, err = runtimeRepo.GetFirst(context.Background(), repo.WithByID(create.RuntimeID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twebsite.RuntimeID = runtime.ID\n\n\t\tswitch runtime.Type {\n\t\tcase constant.RuntimePHP:\n\t\t\tif runtime.Resource == constant.ResourceAppstore {\n\t\t\t\tif !checkImageLike(nil, runtime.Image) {\n\t\t\t\t\treturn buserr.WithName(\"ErrImageNotExist\", runtime.Name)\n\t\t\t\t}\n\t\t\t\twebsite.Proxy = fmt.Sprintf(\"127.0.0.1:%s\", runtime.Port)\n\t\t\t} else {\n\t\t\t\twebsite.ProxyType = create.ProxyType\n\t\t\t\tif website.ProxyType == constant.RuntimeProxyUnix {\n\t\t\t\t\tproxy = fmt.Sprintf(\"unix:%s\", path.Join(\"/www/sites\", website.Alias, \"php-pool\", \"php-fpm.sock\"))\n\t\t\t\t}\n\t\t\t\tif website.ProxyType == constant.RuntimeProxyTcp {\n\t\t\t\t\tproxy = fmt.Sprintf(\"127.0.0.1:%d\", create.Port)\n\t\t\t\t}\n\t\t\t\twebsite.Proxy = proxy\n\t\t\t}\n\t\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:\n\t\t\tproxyPort := runtime.Port\n\t\t\tif proxyPort == \"\" {\n\t\t\t\treturn buserr.New(\"ErrRuntimeNoPort\")\n\t\t\t}\n\t\t\tproxyPort = strconv.Itoa(create.Port)\n\t\t\twebsite.Proxy = fmt.Sprintf(\"127.0.0.1:%s\", proxyPort)\n\t\t}\n\tcase constant.Subsite:\n\t\tparentWebsite, err := websiteRepo.GetFirst(repo.WithByID(create.ParentWebsiteID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twebsite.ParentWebsiteID = parentWebsite.ID\n\t\twebsite.SiteDir = create.SiteDir\n\t}\n\n\tconfigNginx := func(t *task.Task) error {\n\t\tif err = configDefaultNginx(website, domains, appInstall, runtime, create.StreamConfig); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif website.Type != constant.Stream {\n\t\t\tif err = createWafConfig(website, domains); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif create.Type == constant.Runtime {\n\t\t\t\truntime, err = runtimeRepo.GetFirst(context.Background(), repo.WithByID(create.RuntimeID))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceAppstore {\n\t\t\t\t\tcreateOpenBasedirConfig(website)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttx, ctx := helper.GetTxAndContext()\n\t\tdefer tx.Rollback()\n\t\tif err = websiteRepo.Create(ctx, website); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tt.Task.ResourceID = website.ID\n\t\tif len(domains) > 0 {\n\t\t\tfor i := range domains {\n\t\t\t\tdomains[i].WebsiteID = website.ID\n\t\t\t}\n\t\t\tif err = websiteDomainRepo.BatchCreate(ctx, domains); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\ttx.Commit()\n\t\treturn nil\n\t}\n\n\tdeleteWebsite := func(t *task.Task) {\n\t\t_ = deleteWebsiteFolder(website)\n\t}\n\n\tcreateTask.AddSubTask(i18n.GetMsgByKey(\"ConfigOpenresty\"), configNginx, deleteWebsite)\n\n\tif create.EnableSSL {\n\t\tenableSSL := func(t *task.Task) error {\n\t\t\twebsiteModel, err := websiteSSLRepo.GetFirst(repo.WithByID(create.WebsiteSSLID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\twebsite.Protocol = constant.ProtocolHTTPS\n\t\t\twebsite.WebsiteSSLID = create.WebsiteSSLID\n\t\t\tappSSLReq := request.WebsiteHTTPSOp{\n\t\t\t\tWebsiteID:             website.ID,\n\t\t\t\tEnable:                true,\n\t\t\t\tWebsiteSSLID:          websiteModel.ID,\n\t\t\t\tType:                  \"existed\",\n\t\t\t\tHttpConfig:            \"HTTPToHTTPS\",\n\t\t\t\tSSLProtocol:           []string{\"TLSv1.3\", \"TLSv1.2\"},\n\t\t\t\tAlgorithm:             \"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED\",\n\t\t\t\tHsts:                  true,\n\t\t\t\tHstsIncludeSubDomains: true,\n\t\t\t}\n\t\t\tif err = applySSL(website, *websiteModel, appSSLReq); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err = websiteRepo.Save(context.Background(), website); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tcreateTask.AddSubTaskWithIgnoreErr(i18n.GetMsgByKey(\"EnableSSL\"), enableSSL)\n\t}\n\n\tif len(create.FtpUser) != 0 && len(create.FtpPassword) != 0 {\n\t\tcreateFtpUser := func(t *task.Task) error {\n\t\t\tindexDir := GetSitePath(*website, SiteIndexDir)\n\t\t\titemID, err := NewIFtpService().Create(dto.FtpCreate{User: create.FtpUser, Password: create.FtpPassword, Path: indexDir})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\twebsite.FtpID = itemID\n\t\t\treturn nil\n\t\t}\n\t\tcreateTask.AddSubTaskWithIgnoreErr(i18n.GetWithName(\"ConfigFTP\", create.FtpUser), createFtpUser)\n\t}\n\n\treturn createTask.Execute()\n}\n\nfunc (w WebsiteService) OpWebsite(req request.WebsiteOp) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := opWebsite(&website, req.Operate); err != nil {\n\t\treturn err\n\t}\n\treturn websiteRepo.Save(context.Background(), &website)\n}\n\nfunc (w WebsiteService) GetWebsiteOptions(req request.WebsiteOptionReq) ([]response.WebsiteOption, error) {\n\tvar options []repo.DBOption\n\tif len(req.Types) > 0 {\n\t\toptions = append(options, repo.WithTypes(req.Types))\n\t}\n\twebs, _ := websiteRepo.List(options...)\n\tvar datas []response.WebsiteOption\n\tfor _, web := range webs {\n\t\tvar item response.WebsiteOption\n\t\tif err := copier.Copy(&item, &web); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdatas = append(datas, item)\n\t}\n\treturn datas, nil\n}\n\nfunc (w WebsiteService) UpdateWebsite(req request.WebsiteUpdate) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif website.IPV6 != req.IPV6 {\n\t\tif err := changeIPV6(website, req.IPV6); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\twebsite.PrimaryDomain = req.PrimaryDomain\n\tif req.WebsiteGroupID != 0 {\n\t\twebsite.WebsiteGroupID = req.WebsiteGroupID\n\t}\n\twebsite.Remark = req.Remark\n\twebsite.IPV6 = req.IPV6\n\twebsite.Favorite = req.Favorite\n\n\tif req.ExpireDate != \"\" {\n\t\texpireDate, err := time.Parse(constant.DateLayout, req.ExpireDate)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twebsite.ExpireDate = expireDate\n\t}\n\n\treturn websiteRepo.Save(context.TODO(), &website)\n}\n\nfunc (w WebsiteService) GetWebsite(id uint) (response.WebsiteDTO, error) {\n\tvar res response.WebsiteDTO\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn res, err\n\t}\n\tres.Website = website\n\tres.ErrorLogPath = GetSitePath(website, SiteErrorLog)\n\tres.AccessLogPath = GetSitePath(website, SiteAccessLog)\n\tres.SitePath = GetSitePath(website, SiteDir)\n\tres.SiteDir = website.SiteDir\n\tfileOp := files.NewFileOp()\n\tswitch website.Type {\n\tcase constant.Runtime:\n\t\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\t\tif err != nil {\n\t\t\treturn res, err\n\t\t}\n\t\tres.RuntimeType = runtime.Type\n\t\tres.RuntimeName = runtime.Name\n\t\tif runtime.Type == constant.RuntimePHP {\n\t\t\tres.OpenBaseDir = fileOp.Stat(path.Join(GetSitePath(website, SiteIndexDir), \".user.ini\"))\n\t\t}\n\tcase constant.Stream:\n\t\tnginxParser, err := parser.NewParser(GetSitePath(website, StreamConf))\n\t\tif err != nil {\n\t\t\treturn res, err\n\t\t}\n\t\tconfig, err := nginxParser.Parse()\n\t\tif err != nil {\n\t\t\treturn res, err\n\t\t}\n\t\tlistens := config.FindDirectives(\"listen\")\n\t\tfor _, listen := range listens {\n\t\t\tparams := listen.GetParameters()\n\t\t\tif len(params) > 1 && params[1] == \"udp\" {\n\t\t\t\tres.UDP = true\n\t\t\t}\n\t\t}\n\t\tupstreams := config.FindUpstreams()\n\t\tfor _, up := range upstreams {\n\t\t\tdirectives := up.GetDirectives()\n\t\t\tfor _, d := range directives {\n\t\t\t\tdName := d.GetName()\n\t\t\t\tif _, ok := dto.LBAlgorithms[dName]; ok {\n\t\t\t\t\tres.Algorithm = dName\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tres.Servers = getNginxUpstreamServers(up.UpstreamServers)\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif website.Type != constant.Subsite {\n\t\tparentWebsites, _ := websiteRepo.List(websiteRepo.WithParentID(website.ID))\n\t\tif len(parentWebsites) > 0 {\n\t\t\tvar names []string\n\t\t\tfor _, site := range parentWebsites {\n\t\t\t\tnames = append(names, site.PrimaryDomain)\n\t\t\t}\n\t\t\treturn buserr.WithName(\"ErrParentWebsite\", strings.Join(names, \",\"))\n\t\t}\n\t}\n\n\tif website.Type == constant.Runtime && req.DeleteDB && website.DbID != 0 {\n\t\tswitch website.DbType {\n\t\tcase constant.AppMysql, constant.AppMariaDB:\n\t\t\tmysqlDB, _ := mysqlRepo.Get(repo.WithByID(website.DbID))\n\t\t\tif mysqlDB.ID > 0 {\n\t\t\t\tdeleteReq := dto.MysqlDBDelete{\n\t\t\t\t\tID:          mysqlDB.ID,\n\t\t\t\t\tDatabase:    mysqlDB.MysqlName,\n\t\t\t\t\tForceDelete: req.ForceDelete,\n\t\t\t\t}\n\t\t\t\tif err = NewIMysqlService().Delete(context.TODO(), deleteReq); err != nil && !req.ForceDelete {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\tcase constant.AppPostgresql, constant.AppPostgres:\n\t\t\tpgDB, _ := postgresqlRepo.Get(repo.WithByID(website.DbID))\n\t\t\tif pgDB.ID > 0 {\n\t\t\t\tdeleteReq := dto.PostgresqlDBDelete{\n\t\t\t\t\tID:          pgDB.ID,\n\t\t\t\t\tForceDelete: req.ForceDelete,\n\t\t\t\t\tDatabase:    pgDB.PostgresqlName,\n\t\t\t\t}\n\t\t\t\tif err = NewIPostgresqlService().Delete(context.TODO(), deleteReq); err != nil && !req.ForceDelete {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif err = delNginxConfig(website, req.ForceDelete); err != nil {\n\t\treturn err\n\t}\n\n\tif website.Type != constant.Stream {\n\t\tif err = delWafConfig(website, req.ForceDelete); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif checkIsLinkApp(website) && req.DeleteApp {\n\t\tappInstall, _ := appInstallRepo.GetFirst(repo.WithByID(website.AppInstallID))\n\t\tif appInstall.ID > 0 {\n\t\t\tdeleteReq := request.AppInstallDelete{\n\t\t\t\tInstall:      appInstall,\n\t\t\t\tForceDelete:  req.ForceDelete,\n\t\t\t\tDeleteBackup: true,\n\t\t\t\tDeleteDB:     true,\n\t\t\t}\n\t\t\tif err = deleteAppInstall(deleteReq); err != nil && !req.ForceDelete {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\ttx, ctx := helper.GetTxAndContext()\n\tdefer tx.Rollback()\n\n\tgo func() {\n\t\t_ = NewIBackupRecordService().DeleteRecordByName(\"website\", website.PrimaryDomain, website.Alias, req.DeleteBackup)\n\t}()\n\n\tif err := websiteRepo.DeleteBy(ctx, repo.WithByID(req.ID)); err != nil {\n\t\treturn err\n\t}\n\tif err := websiteDomainRepo.DeleteBy(ctx, websiteDomainRepo.WithWebsiteId(req.ID)); err != nil {\n\t\treturn err\n\t}\n\ttx.Commit()\n\n\tuploadDir := path.Join(global.Dir.DataDir, \"uploads/website\", website.Alias)\n\tif _, err := os.Stat(uploadDir); err == nil {\n\t\t_ = os.RemoveAll(uploadDir)\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteService) UpdateWebsiteDomain(req request.WebsiteDomainUpdate) error {\n\tdomain, err := websiteDomainRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdomain.SSL = req.SSL\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(domain.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tnginxConfig := nginxFull.SiteConfig\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\tserver.DeleteListen(strconv.Itoa(domain.Port))\n\tif website.IPV6 {\n\t\tserver.DeleteListen(\"[::]:\" + strconv.Itoa(domain.Port))\n\t}\n\thttp3 := isHttp3(server)\n\tsetListen(server, strconv.Itoa(domain.Port), website.IPV6, http3, website.DefaultServer, domain.SSL && website.Protocol == constant.ProtocolHTTPS)\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\tif err = nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName); err != nil {\n\t\treturn err\n\t}\n\treturn websiteDomainRepo.Save(context.TODO(), &domain)\n}\n\nfunc (w WebsiteService) GetNginxConfigByScope(req request.NginxScopeReq) (*response.WebsiteNginxConfig, error) {\n\tkeys, ok := dto.ScopeKeyMap[req.Scope]\n\tif !ok || len(keys) == 0 {\n\t\treturn nil, nil\n\t}\n\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar config response.WebsiteNginxConfig\n\tparams, err := getNginxParamsByKeys(constant.NginxScopeServer, keys, &website)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.Params = params\n\tconfig.Enable = len(params[0].Params) > 0\n\n\treturn &config, nil\n}\n\nfunc (w WebsiteService) UpdateNginxConfigByScope(req request.NginxConfigUpdate) error {\n\tkeys, ok := dto.ScopeKeyMap[req.Scope]\n\tif !ok || len(keys) == 0 {\n\t\treturn nil\n\t}\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif req.Operate == constant.ConfigDel {\n\t\tvar nginxParams []dto.NginxParam\n\t\tfor _, key := range keys {\n\t\t\tnginxParams = append(nginxParams, dto.NginxParam{\n\t\t\t\tName: key,\n\t\t\t})\n\t\t}\n\t\treturn deleteNginxConfig(constant.NginxScopeServer, nginxParams, &website)\n\t}\n\tparams := getNginxParams(req.Params, keys)\n\tif req.Operate == constant.ConfigNew {\n\t\tif _, ok := dto.StaticFileKeyMap[req.Scope]; ok {\n\t\t\tparams = getNginxParamsFromStaticFile(req.Scope, params)\n\t\t}\n\t}\n\treturn updateNginxConfig(constant.NginxScopeServer, params, &website)\n}\n\nfunc (w WebsiteService) GetWebsiteNginxConfig(websiteID uint, configType string) (*response.FileInfo, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfigPath := \"\"\n\tswitch configType {\n\tcase constant.AppOpenresty:\n\t\tconfigPath = GetWebsiteConfigPath(website)\n\t}\n\tinfo, err := files.NewFileInfo(files.FileOption{\n\t\tPath:   configPath,\n\t\tExpand: true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &response.FileInfo{FileInfo: *info}, nil\n}\n\nfunc (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteId))\n\tif err != nil {\n\t\treturn response.WebsiteHTTPS{}, err\n\t}\n\tvar (\n\t\tres        response.WebsiteHTTPS\n\t\thttpsPorts []string\n\t)\n\n\thttpsPortsMap := getHttpsPort(websiteId)\n\tfor port := range httpsPortsMap {\n\t\thttpsPorts = append(httpsPorts, strconv.Itoa(port))\n\t}\n\tres.HttpsPort = strings.Join(httpsPorts, \",\")\n\tif website.WebsiteSSLID == 0 {\n\t\tres.Enable = false\n\t\treturn res, nil\n\t}\n\twebsiteSSL, err := websiteSSLRepo.GetFirst(repo.WithByID(website.WebsiteSSLID))\n\tif err != nil {\n\t\treturn response.WebsiteHTTPS{}, err\n\t}\n\tres.SSL = *websiteSSL\n\tres.Enable = true\n\tif website.HttpConfig != \"\" {\n\t\tres.HttpConfig = website.HttpConfig\n\t} else {\n\t\tres.HttpConfig = constant.HTTPToHTTPS\n\t}\n\tparams, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{\"ssl_protocols\", \"ssl_ciphers\", \"add_header\", \"listen\"}, &website)\n\tif err != nil {\n\t\treturn res, err\n\t}\n\tfor _, p := range params {\n\t\tif p.Name == \"ssl_protocols\" {\n\t\t\tres.SSLProtocol = p.Params\n\t\t}\n\t\tif p.Name == \"ssl_ciphers\" {\n\t\t\tres.Algorithm = p.Params[0]\n\t\t}\n\t\tif p.Name == \"add_header\" && len(p.Params) > 0 {\n\t\t\tif p.Params[0] == \"Strict-Transport-Security\" {\n\t\t\t\tres.Hsts = true\n\t\t\t\tif len(p.Params) > 1 {\n\t\t\t\t\thstsValue := p.Params[1]\n\t\t\t\t\tif strings.Contains(hstsValue, \"includeSubDomains\") {\n\t\t\t\t\t\tres.HstsIncludeSubDomains = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif p.Params[0] == \"Alt-Svc\" {\n\t\t\t\tres.Http3 = true\n\t\t\t}\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteHTTPSOp) (*response.WebsiteHTTPS, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar (\n\t\tres        response.WebsiteHTTPS\n\t\twebsiteSSL model.WebsiteSSL\n\t)\n\tif err = ChangeHSTSConfig(req.Hsts, req.HstsIncludeSubDomains, req.Http3, website); err != nil {\n\t\treturn nil, err\n\t}\n\tres.Enable = req.Enable\n\tres.SSLProtocol = req.SSLProtocol\n\tres.Algorithm = req.Algorithm\n\tres.HstsIncludeSubDomains = req.HstsIncludeSubDomains\n\tif !req.Enable {\n\t\twebsite.Protocol = constant.ProtocolHTTP\n\t\twebsite.WebsiteSSLID = 0\n\n\t\twebsiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))\n\n\t\tports := make(map[int]struct{})\n\t\tfor _, domain := range websiteDomains {\n\t\t\tports[domain.Port] = struct{}{}\n\t\t}\n\t\tfor port := range ports {\n\t\t\tif err = removeSSLListen(website, []string{strconv.Itoa(port)}); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif _, ok := ports[nginxInstall.HttpsPort]; !ok {\n\t\t\thttpsPortStr := strconv.Itoa(nginxInstall.HttpsPort)\n\t\t\tif err = deleteListenAndServerName(website, []string{httpsPortStr, \"[::]:\" + httpsPortStr}, []string{}); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tnginxParams := getNginxParamsFromStaticFile(dto.SSL, nil)\n\t\tnginxParams = append(nginxParams,\n\t\t\tdto.NginxParam{\n\t\t\t\tName:   \"if\",\n\t\t\t\tParams: []string{\"($scheme\", \"=\", \"http)\"},\n\t\t\t},\n\t\t\tdto.NginxParam{\n\t\t\t\tName: \"ssl_certificate\",\n\t\t\t},\n\t\t\tdto.NginxParam{\n\t\t\t\tName: \"ssl_certificate_key\",\n\t\t\t},\n\t\t\tdto.NginxParam{\n\t\t\t\tName: \"ssl_protocols\",\n\t\t\t},\n\t\t\tdto.NginxParam{\n\t\t\t\tName: \"ssl_ciphers\",\n\t\t\t},\n\t\t\tdto.NginxParam{\n\t\t\t\tName: \"http2\",\n\t\t\t},\n\t\t\tdto.NginxParam{\n\t\t\t\tName:   \"add_header\",\n\t\t\t\tParams: []string{\"Strict-Transport-Security\"},\n\t\t\t},\n\t\t)\n\t\tif err = deleteNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err = websiteRepo.Save(ctx, &website); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn nil, nil\n\t}\n\n\tif req.Type == constant.SSLExisted {\n\t\twebsiteModel, err := websiteSSLRepo.GetFirst(repo.WithByID(req.WebsiteSSLID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif websiteModel.Pem == \"\" {\n\t\t\treturn nil, buserr.New(\"ErrSSLValid\")\n\t\t}\n\t\twebsite.WebsiteSSLID = websiteModel.ID\n\t\tres.SSL = *websiteModel\n\t\twebsiteSSL = *websiteModel\n\t}\n\tif req.Type == constant.SSLManual {\n\t\twebsiteSSL, err = getManualWebsiteSSL(req)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres.SSL = websiteSSL\n\t}\n\n\twebsite.Protocol = constant.ProtocolHTTPS\n\tif err = applySSL(&website, websiteSSL, req); err != nil {\n\t\treturn nil, err\n\t}\n\twebsite.HttpConfig = req.HttpConfig\n\n\tif websiteSSL.ID == 0 {\n\t\tif err = websiteSSLRepo.Create(ctx, &websiteSSL); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\twebsite.WebsiteSSLID = websiteSSL.ID\n\t}\n\tif err = websiteRepo.Save(ctx, &website); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &res, nil\n}\n\nfunc (w WebsiteService) PreInstallCheck(req request.WebsiteInstallCheckReq) ([]response.WebsitePreInstallCheck, error) {\n\tvar (\n\t\tres      []response.WebsitePreInstallCheck\n\t\tcheckIds []uint\n\t\tshowErr  = false\n\t)\n\n\tapp, err := appRepo.GetFirst(appRepo.WithKey(constant.AppOpenresty))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tappInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID))\n\tif reflect.DeepEqual(appInstall, model.AppInstall{}) {\n\t\tres = append(res, response.WebsitePreInstallCheck{\n\t\t\tName:    appInstall.Name,\n\t\t\tAppName: app.Name,\n\t\t\tStatus:  buserr.WithDetail(\"ErrNotInstall\", app.Name, nil).Error(),\n\t\t\tVersion: appInstall.Version,\n\t\t})\n\t\tshowErr = true\n\t} else {\n\t\tcheckIds = append(req.InstallIds, appInstall.ID)\n\t}\n\tif len(checkIds) > 0 {\n\t\tinstallList, _ := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(checkIds))\n\t\tfor _, install := range installList {\n\t\t\tif err = syncAppInstallStatus(&install, false); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tres = append(res, response.WebsitePreInstallCheck{\n\t\t\t\tName:    install.Name,\n\t\t\t\tStatus:  install.Status,\n\t\t\t\tVersion: install.Version,\n\t\t\t\tAppName: install.App.Name,\n\t\t\t})\n\t\t\tif install.Status != constant.StatusRunning {\n\t\t\t\tshowErr = true\n\t\t\t}\n\t\t}\n\t}\n\tif showErr {\n\t\treturn res, nil\n\t}\n\treturn nil, nil\n}\n\nfunc (w WebsiteService) UpdateNginxConfigFile(req request.WebsiteNginxUpdate) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfilePath := nginxFull.SiteConfig.FilePath\n\tif err = files.NewFileOp().WriteFile(filePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(nginxFull.SiteConfig.OldContent, filePath, nginxFull.Install.ContainerName)\n}\n\nfunc (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.WebsiteLog, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsitePath := GetSitePath(website, SiteDir)\n\tres := &response.WebsiteLog{\n\t\tContent: \"\",\n\t}\n\tswitch req.Operate {\n\tcase constant.GetLog:\n\t\tswitch req.LogType {\n\t\tcase constant.AccessLog:\n\t\t\tres.Enable = website.AccessLog\n\t\t\tif !website.AccessLog {\n\t\t\t\treturn res, nil\n\t\t\t}\n\t\tcase constant.ErrorLog:\n\t\t\tres.Enable = website.ErrorLog\n\t\t\tif !website.ErrorLog {\n\t\t\t\treturn res, nil\n\t\t\t}\n\t\t}\n\t\tfilePath := path.Join(sitePath, \"log\", req.LogType)\n\t\tlogFileRes, err := files.ReadFileByLine(filePath, req.Page, req.PageSize, false)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres.End = logFileRes.IsEndOfFile\n\t\tres.Path = filePath\n\t\tres.Content = strings.Join(logFileRes.Lines, \"\\n\")\n\t\treturn res, nil\n\tcase constant.DisableLog:\n\t\tparams := dto.NginxParam{}\n\t\tswitch req.LogType {\n\t\tcase constant.AccessLog:\n\t\t\tparams.Name = \"access_log\"\n\t\t\tparams.Params = []string{\"off\"}\n\t\t\twebsite.AccessLog = false\n\t\tcase constant.ErrorLog:\n\t\t\tparams.Name = \"error_log\"\n\t\t\tparams.Params = []string{\"/dev/null\", \"crit\"}\n\t\t\twebsite.ErrorLog = false\n\t\t}\n\t\tvar nginxParams []dto.NginxParam\n\t\tnginxParams = append(nginxParams, params)\n\n\t\tif err := updateNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := websiteRepo.Save(context.Background(), &website); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase constant.EnableLog:\n\t\tkey := \"access_log\"\n\t\tlogPath := path.Join(\"/www\", \"sites\", website.Alias, \"log\", req.LogType)\n\t\tparams := []string{logPath}\n\t\tswitch req.LogType {\n\t\tcase constant.AccessLog:\n\t\t\tif website.Type != constant.Stream {\n\t\t\t\tparams = append(params, \"main\")\n\t\t\t} else {\n\t\t\t\tparams = append(params, \"streamlog\")\n\t\t\t}\n\t\t\twebsite.AccessLog = true\n\t\tcase constant.ErrorLog:\n\t\t\tkey = \"error_log\"\n\t\t\twebsite.ErrorLog = true\n\t\t}\n\t\tif err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: key, Params: params}}, &website); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := websiteRepo.Save(context.Background(), &website); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase constant.DeleteLog:\n\t\tlogPath := path.Join(sitePath, \"log\", req.LogType)\n\t\tif err := files.NewFileOp().WriteFile(logPath, strings.NewReader(\"\"), constant.DirPerm); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (w WebsiteService) ChangeDefaultServer(id uint) error {\n\tdefaultWebsite, _ := websiteRepo.GetFirst(websiteRepo.WithDefaultServer())\n\tif defaultWebsite.ID > 0 {\n\t\tparams, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{\"listen\"}, &defaultWebsite)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar changeParams []dto.NginxParam\n\t\tfor _, param := range params {\n\t\t\tparamLen := len(param.Params)\n\t\t\tvar newParam []string\n\t\t\tif paramLen > 1 && param.Params[paramLen-1] == components.DefaultServer {\n\t\t\t\tnewParam = param.Params[:paramLen-1]\n\t\t\t}\n\t\t\tchangeParams = append(changeParams, dto.NginxParam{\n\t\t\t\tName:   param.Name,\n\t\t\t\tParams: newParam,\n\t\t\t})\n\t\t}\n\t\tif err := updateNginxConfig(constant.NginxScopeServer, changeParams, &defaultWebsite); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefaultWebsite.DefaultServer = false\n\t\tif err := websiteRepo.Save(context.Background(), &defaultWebsite); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := updateDefaultServerConfig(!(id > 0)); err != nil {\n\t\treturn err\n\t}\n\tif id == 0 {\n\t\treturn nil\n\t}\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn err\n\t}\n\tparams, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{\"listen\"}, &website)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar changeParams []dto.NginxParam\n\tfor _, param := range params {\n\t\tif hasHttp3(param.Params) || hasDefaultServer(param.Params) {\n\t\t\tcontinue\n\t\t}\n\t\tnewParam := append(param.Params, components.DefaultServer)\n\t\tchangeParams = append(changeParams, dto.NginxParam{\n\t\t\tName:   param.Name,\n\t\t\tParams: newParam,\n\t\t})\n\t}\n\tif err := updateNginxConfig(constant.NginxScopeServer, changeParams, &website); err != nil {\n\t\treturn err\n\t}\n\twebsite.DefaultServer = true\n\treturn websiteRepo.Save(context.Background(), &website)\n}\n\nfunc (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif website.Type == constant.Runtime {\n\t\toldRuntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif oldRuntime.Resource == constant.ResourceLocal {\n\t\t\treturn buserr.New(\"ErrPHPResource\")\n\t\t}\n\t\tclient, err := docker.NewDockerClient()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer client.Close()\n\t\tif !checkImageLike(client, oldRuntime.Image) {\n\t\t\treturn buserr.WithName(\"ErrImageNotExist\", oldRuntime.Name)\n\t\t}\n\t}\n\tconfigPath := GetSitePath(website, SiteConf)\n\tnginxContent, err := files.NewFileOp().GetContent(configPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfig, err := parser.NewStringParser(string(nginxContent)).Parse()\n\tif err != nil {\n\t\treturn err\n\t}\n\tservers := config.FindServers()\n\tif len(servers) == 0 {\n\t\treturn errors.New(\"nginx config is not valid\")\n\t}\n\tserver := servers[0]\n\tfileOp := files.NewFileOp()\n\tindexPHPPath := path.Join(GetSitePath(website, SiteIndexDir), \"index.php\")\n\tindexHtmlPath := path.Join(GetSitePath(website, SiteIndexDir), \"index.html\")\n\tif req.RuntimeID > 0 {\n\t\tserver.UpdateDirective(\"index\", []string{\"index.php index.html index.htm default.php default.htm default.html\"})\n\t\tserver.RemoveDirective(\"location\", []string{\"~\", \"[^/]\\\\.php(/|$)\"})\n\t\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.RuntimeID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif runtime.Resource == constant.ResourceLocal {\n\t\t\treturn buserr.New(\"ErrPHPResource\")\n\t\t}\n\t\twebsite.RuntimeID = req.RuntimeID\n\t\twebsite.AppInstallID = 0\n\t\tphpProxy := fmt.Sprintf(\"127.0.0.1:%s\", runtime.Port)\n\t\twebsite.Proxy = phpProxy\n\t\tserver.UpdatePHPProxy([]string{website.Proxy}, \"\")\n\t\twebsite.Type = constant.Runtime\n\t\tif !fileOp.Stat(indexPHPPath) {\n\t\t\t_ = fileOp.WriteFile(indexPHPPath, strings.NewReader(string(nginx_conf.IndexPHP)), constant.FilePerm)\n\t\t}\n\t} else {\n\t\tserver.UpdateDirective(\"index\", []string{\"index.html index.php index.htm default.php default.htm default.html\"})\n\t\twebsite.RuntimeID = 0\n\t\twebsite.Type = constant.Static\n\t\twebsite.Proxy = \"\"\n\t\tserver.RemoveDirective(\"location\", []string{\"~\", \"[^/]\\\\.php(/|$)\"})\n\t\tif !fileOp.Stat(indexHtmlPath) {\n\t\t\t_ = fileOp.WriteFile(indexHtmlPath, strings.NewReader(string(nginx_conf.Index)), constant.FilePerm)\n\t\t}\n\t}\n\n\tconfig.FilePath = configPath\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = nginxCheckAndReload(string(nginxContent), configPath, nginxInstall.ContainerName); err != nil {\n\t\treturn err\n\t}\n\treturn websiteRepo.Save(context.Background(), &website)\n}\n\nfunc (w WebsiteService) UpdateSiteDir(req request.WebsiteUpdateDir) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\trunDir := req.SiteDir\n\tsiteDir := path.Join(\"/www/sites\", website.Alias, \"index\")\n\tif req.SiteDir != \"/\" {\n\t\tsiteDir = fmt.Sprintf(\"%s%s\", siteDir, req.SiteDir)\n\t}\n\tif err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: \"root\", Params: []string{siteDir}}}, &website); err != nil {\n\t\treturn err\n\t}\n\twebsite.SiteDir = runDir\n\treturn websiteRepo.Save(context.Background(), &website)\n}\n\nfunc (w WebsiteService) UpdateSitePermission(req request.WebsiteUpdateDirPermission) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tabsoluteIndexPath := GetSitePath(website, SiteIndexDir)\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Second))\n\tif err := cmdMgr.Run(\"chown\", \"-R\", fmt.Sprintf(\"%s:%s\", req.User, req.Group), absoluteIndexPath); err != nil {\n\t\treturn err\n\t}\n\twebsite.User = req.User\n\twebsite.Group = req.Group\n\treturn websiteRepo.Save(context.Background(), &website)\n}\n\nfunc (w WebsiteService) UpdateCors(req request.CorsConfigReq) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tparams := []dto.NginxParam{\n\t\t{Name: \"add_header\", Params: []string{\"Access-Control-Allow-Origin\"}},\n\t\t{Name: \"add_header\", Params: []string{\"Access-Control-Allow-Methods\"}},\n\t\t{Name: \"add_header\", Params: []string{\"Access-Control-Allow-Headers\"}},\n\t\t{Name: \"add_header\", Params: []string{\"Access-Control-Allow-Credentials\"}},\n\t\t{Name: \"if\", Params: []string{\"(\", \"$request_method\", \"=\", \"'OPTIONS'\", \")\"}},\n\t}\n\tif err := deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil {\n\t\treturn err\n\t}\n\tif req.Cors {\n\t\treturn updateWebsiteConfig(website, func(server *components.Server) error {\n\t\t\tserver.UpdateDirective(\"add_header\", []string{\"Access-Control-Allow-Origin\", req.AllowOrigins, \"always\"})\n\t\t\tif req.AllowMethods != \"\" {\n\t\t\t\tserver.UpdateDirective(\"add_header\", []string{\"Access-Control-Allow-Methods\", req.AllowMethods, \"always\"})\n\t\t\t}\n\t\t\tif req.AllowHeaders != \"\" {\n\t\t\t\tserver.UpdateDirective(\"add_header\", []string{\"Access-Control-Allow-Headers\", req.AllowHeaders, \"always\"})\n\t\t\t}\n\t\t\tif req.AllowCredentials {\n\t\t\t\tserver.UpdateDirective(\"add_header\", []string{\"Access-Control-Allow-Credentials\", \"true\", \"always\"})\n\t\t\t}\n\t\t\tif req.Preflight {\n\t\t\t\tserver.AddCorsOption()\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteService) GetCors(websiteID uint) (*request.CorsConfig, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tserver, err := getServer(website)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif server == nil {\n\t\treturn nil, nil\n\t}\n\tcors := &request.CorsConfig{\n\t\tCors:             server.Cors,\n\t\tAllowOrigins:     server.AllowOrigins,\n\t\tAllowMethods:     server.AllowMethods,\n\t\tAllowHeaders:     server.AllowHeaders,\n\t\tAllowCredentials: server.AllowCredentials,\n\t\tPreflight:        server.Preflight,\n\t}\n\treturn cors, nil\n}\n\nfunc (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn\n\t}\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\tbackupContent, err := fileOp.GetContent(nginxFull.SiteConfig.Config.FilePath)\n\tif err != nil {\n\t\treturn\n\t}\n\tblock := nginxFull.SiteConfig.Config.FindServers()[0]\n\tlocations := block.FindDirectives(\"location\")\n\tfor _, location := range locations {\n\t\tloParams := location.GetParameters()\n\t\tif len(loParams) > 1 || loParams[0] == \"~\" {\n\t\t\textendStr := loParams[1]\n\t\t\tif strings.HasPrefix(extendStr, `.*\\.(`) && strings.HasSuffix(extendStr, `)$`) {\n\t\t\t\tblock.RemoveDirective(\"location\", loParams)\n\t\t\t}\n\t\t}\n\t}\n\tif req.Enable || req.Cache {\n\t\texts := strings.Split(req.Extends, \",\")\n\t\tnewDirective := components.Directive{\n\t\t\tName:       \"location\",\n\t\t\tParameters: []string{\"~\", fmt.Sprintf(`.*\\.(%s)$`, strings.Join(exts, \"|\"))},\n\t\t}\n\n\t\tnewBlock := &components.Block{}\n\t\tnewBlock.Directives = make([]components.IDirective, 0)\n\t\tif req.Cache {\n\t\t\tnewBlock.AppendDirectives(&components.Directive{\n\t\t\t\tName:       \"expires\",\n\t\t\t\tParameters: []string{strconv.Itoa(req.CacheTime) + req.CacheUint},\n\t\t\t})\n\t\t}\n\t\tif !req.LogEnable {\n\t\t\tnewBlock.AppendDirectives(&components.Directive{\n\t\t\t\tName:       \"access_log\",\n\t\t\t\tParameters: []string{\"off\"},\n\t\t\t})\n\t\t}\n\t\tnewBlock.AppendDirectives(&components.Directive{\n\t\t\tName:       \"log_not_found\",\n\t\t\tParameters: []string{\"off\"},\n\t\t})\n\t\tif req.Enable {\n\t\t\tvalidDir := &components.Directive{\n\t\t\t\tName:       \"valid_referers\",\n\t\t\t\tParameters: []string{},\n\t\t\t}\n\t\t\tif req.NoneRef {\n\t\t\t\tvalidDir.Parameters = append(validDir.Parameters, \"none\")\n\t\t\t}\n\t\t\tif req.Blocked {\n\t\t\t\tvalidDir.Parameters = append(validDir.Parameters, \"blocked\")\n\t\t\t}\n\t\t\tif len(req.ServerNames) > 0 {\n\t\t\t\tvalidDir.Parameters = append(validDir.Parameters, strings.Join(req.ServerNames, \" \"))\n\t\t\t}\n\t\t\tnewBlock.AppendDirectives(validDir)\n\n\t\t\tifDir := &components.Directive{\n\t\t\t\tName:       \"if\",\n\t\t\t\tParameters: []string{\"($invalid_referer)\"},\n\t\t\t}\n\t\t\tif !req.LogEnable {\n\t\t\t\tifDir.Block = &components.Block{\n\t\t\t\t\tDirectives: []components.IDirective{\n\t\t\t\t\t\t&components.Directive{\n\t\t\t\t\t\t\tName:       \"access_log\",\n\t\t\t\t\t\t\tParameters: []string{\"off\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t&components.Directive{\n\t\t\t\t\t\t\tName:       \"return\",\n\t\t\t\t\t\t\tParameters: []string{req.Return},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tifDir.Block = &components.Block{\n\t\t\t\t\tDirectives: []components.IDirective{\n\t\t\t\t\t\t&components.Directive{\n\t\t\t\t\t\t\tName:       \"return\",\n\t\t\t\t\t\t\tParameters: []string{req.Return},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\t\t\tnewBlock.AppendDirectives(ifDir)\n\t\t}\n\t\tif website.Type == constant.Deployment {\n\t\t\tproxyDirectives := getRootProxyDirectives(website.Proxy)\n\t\t\tif len(proxyDirectives) == 0 {\n\t\t\t\treturn errors.New(\"failed to build deployment proxy directives\")\n\t\t\t}\n\t\t\tnewBlock.AppendDirectives(proxyDirectives...)\n\t\t}\n\t\tnewDirective.Block = newBlock\n\t\tindex := -1\n\t\tfor i, directive := range block.Directives {\n\t\t\tif directive.GetName() == \"include\" {\n\t\t\t\tindex = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif index != -1 {\n\t\t\tblock.Directives = append(block.Directives[:index], append([]components.IDirective{&newDirective}, block.Directives[index:]...)...)\n\t\t} else {\n\t\t\tblock.Directives = append(block.Directives, &newDirective)\n\t\t}\n\t}\n\n\tif err = nginx.WriteConfig(nginxFull.SiteConfig.Config, nginx.IndentedStyle); err != nil {\n\t\treturn\n\t}\n\tif err = updateNginxConfig(constant.NginxScopeServer, nil, &website); err != nil {\n\t\t_ = fileOp.WriteFile(nginxFull.SiteConfig.Config.FilePath, bytes.NewReader(backupContent), constant.DirPerm)\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres := &response.NginxAntiLeechRes{\n\t\tLogEnable:   true,\n\t\tServerNames: []string{},\n\t}\n\tblock := nginxFull.SiteConfig.Config.FindServers()[0]\n\tlocations := block.FindDirectives(\"location\")\n\tfor _, location := range locations {\n\t\tloParams := location.GetParameters()\n\t\tif len(loParams) > 1 || loParams[0] == \"~\" {\n\t\t\textendStr := loParams[1]\n\t\t\tif strings.HasPrefix(extendStr, `.*\\.(`) && strings.HasSuffix(extendStr, `)$`) {\n\t\t\t\tstr1 := strings.TrimPrefix(extendStr, `.*\\.(`)\n\t\t\t\tstr2 := strings.TrimSuffix(str1, \")$\")\n\t\t\t\tres.Extends = strings.Join(strings.Split(str2, \"|\"), \",\")\n\t\t\t}\n\t\t}\n\t\tlDirectives := location.GetBlock().GetDirectives()\n\t\tfor _, lDir := range lDirectives {\n\t\t\tif lDir.GetName() == \"access_log\" {\n\t\t\t\tif strings.Join(lDir.GetParameters(), \"\") == \"off\" {\n\t\t\t\t\tres.LogEnable = false\n\t\t\t\t}\n\t\t\t}\n\t\t\tif lDir.GetName() == \"valid_referers\" {\n\t\t\t\tres.Enable = true\n\t\t\t\tparams := lDir.GetParameters()\n\t\t\t\tfor _, param := range params {\n\t\t\t\t\tif param == \"none\" {\n\t\t\t\t\t\tres.NoneRef = true\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif param == \"blocked\" {\n\t\t\t\t\t\tres.Blocked = true\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif param == \"server_names\" {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tres.ServerNames = append(res.ServerNames, param)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif lDir.GetName() == \"if\" && lDir.GetParameters()[0] == \"($invalid_referer)\" {\n\t\t\t\tdirectives := lDir.GetBlock().GetDirectives()\n\t\t\t\tfor _, dir := range directives {\n\t\t\t\t\tif dir.GetName() == \"return\" {\n\t\t\t\t\t\tres.Return = strings.Join(dir.GetParameters(), \" \")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif lDir.GetName() == \"expires\" {\n\t\t\t\tres.Cache = true\n\t\t\t\tmatches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(lDir.GetParameters()[0])\n\t\t\t\tif matches == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcacheTime, err := strconv.Atoi(matches[1])\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tunit := matches[2]\n\t\t\t\tres.CacheUint = unit\n\t\t\t\tres.CacheTime = cacheTime\n\t\t\t}\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error) {\n\tvar (\n\t\twebsite    model.Website\n\t\toldContent []byte\n\t)\n\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tincludeDir := GetSitePath(website, SiteRedirectDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(includeDir) {\n\t\t_ = fileOp.CreateDir(includeDir, constant.DirPerm)\n\t}\n\tfileName := fmt.Sprintf(\"%s.conf\", req.Name)\n\tincludePath := path.Join(includeDir, fileName)\n\tbackName := fmt.Sprintf(\"%s.bak\", req.Name)\n\tbackPath := path.Join(includeDir, backName)\n\n\tif req.Operate == \"create\" && (fileOp.Stat(includePath) || fileOp.Stat(backPath)) {\n\t\terr = buserr.New(\"ErrNameIsExist\")\n\t\treturn\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tswitch req.Operate {\n\t\t\tcase \"create\":\n\t\t\t\t_ = fileOp.DeleteFile(includePath)\n\t\t\tcase \"edit\":\n\t\t\t\t_ = fileOp.WriteFile(includePath, bytes.NewReader(oldContent), constant.DirPerm)\n\t\t\t}\n\t\t}\n\t}()\n\n\tvar (\n\t\tconfig *components.Config\n\t\toldPar *parser.Parser\n\t)\n\n\tswitch req.Operate {\n\tcase \"create\":\n\t\tconfig = &components.Config{}\n\tcase \"edit\":\n\t\toldPar, err = parser.NewParser(includePath)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tconfig, err = oldPar.Parse()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\toldContent, err = fileOp.GetContent(includePath)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase \"delete\":\n\t\t_ = fileOp.DeleteFile(includePath)\n\t\t_ = fileOp.DeleteFile(backPath)\n\t\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n\tcase \"disable\":\n\t\t_ = fileOp.Rename(includePath, backPath)\n\t\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n\tcase \"enable\":\n\t\t_ = fileOp.Rename(backPath, includePath)\n\t\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n\t}\n\n\ttarget := req.Target\n\tblock := &components.Block{}\n\n\tswitch req.Type {\n\tcase \"path\":\n\t\tif req.KeepPath {\n\t\t\ttarget = req.Target + \"$1\"\n\t\t} else {\n\t\t\ttarget = req.Target + \"?\"\n\t\t}\n\t\tredirectKey := \"permanent\"\n\t\tif req.Redirect == \"302\" {\n\t\t\tredirectKey = \"redirect\"\n\t\t}\n\t\tblock = &components.Block{\n\t\t\tDirectives: []components.IDirective{\n\t\t\t\t&components.Directive{\n\t\t\t\t\tName:       \"rewrite\",\n\t\t\t\t\tParameters: []string{fmt.Sprintf(\"^%s(.*)\", req.Path), target, redirectKey},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\tcase \"domain\":\n\t\tif req.KeepPath {\n\t\t\ttarget = req.Target + \"$request_uri\"\n\t\t}\n\t\treturnBlock := &components.Block{\n\t\t\tDirectives: []components.IDirective{\n\t\t\t\t&components.Directive{\n\t\t\t\t\tName:       \"return\",\n\t\t\t\t\tParameters: []string{req.Redirect, target},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tfor _, domain := range req.Domains {\n\t\t\tblock.Directives = append(block.Directives, &components.Directive{\n\t\t\t\tName:       \"if\",\n\t\t\t\tParameters: []string{\"($host\", \"~\", fmt.Sprintf(\"'^%s')\", domain)},\n\t\t\t\tBlock:      returnBlock,\n\t\t\t})\n\t\t}\n\tcase \"404\":\n\t\tif req.RedirectRoot {\n\t\t\ttarget = \"/\"\n\t\t}\n\t\tblock = &components.Block{\n\t\t\tDirectives: []components.IDirective{\n\t\t\t\t&components.Directive{\n\t\t\t\t\tName:       \"error_page\",\n\t\t\t\t\tParameters: []string{\"404\", \"=\", \"@notfound\"},\n\t\t\t\t},\n\t\t\t\t&components.Directive{\n\t\t\t\t\tName:       \"location\",\n\t\t\t\t\tParameters: []string{\"@notfound\"},\n\t\t\t\t\tBlock: &components.Block{\n\t\t\t\t\t\tDirectives: []components.IDirective{\n\t\t\t\t\t\t\t&components.Directive{\n\t\t\t\t\t\t\t\tName:       \"return\",\n\t\t\t\t\t\t\t\tParameters: []string{req.Redirect, target},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\tconfig.FilePath = includePath\n\tconfig.Block = block\n\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn buserr.WithErr(\"ErrUpdateBuWebsite\", err)\n\t}\n\n\tnginxInclude := fmt.Sprintf(\"/www/sites/%s/redirect/*.conf\", website.Alias)\n\tif err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: \"include\", Params: []string{nginxInclude}}}, &website); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (w WebsiteService) GetRedirect(id uint) (res []response.NginxRedirectConfig, err error) {\n\tvar (\n\t\twebsite model.Website\n\t)\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn\n\t}\n\tincludeDir := GetSitePath(website, SiteRedirectDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(includeDir) {\n\t\treturn\n\t}\n\tentries, err := os.ReadDir(includeDir)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(entries) == 0 {\n\t\treturn\n\t}\n\n\tvar (\n\t\tcontent []byte\n\t\tconfig  *components.Config\n\t)\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tfileName := entry.Name()\n\t\tif !strings.HasSuffix(fileName, \".conf\") && !strings.HasSuffix(fileName, \".bak\") {\n\t\t\tcontinue\n\t\t}\n\t\tredirectConfig := response.NginxRedirectConfig{\n\t\t\tWebsiteID: website.ID,\n\t\t}\n\t\tparts := strings.Split(fileName, \".\")\n\t\tredirectConfig.Name = parts[0]\n\t\tif parts[1] == \"conf\" {\n\t\t\tredirectConfig.Enable = true\n\t\t} else {\n\t\t\tredirectConfig.Enable = false\n\t\t}\n\t\tfilePath := path.Join(includeDir, fileName)\n\t\tredirectConfig.FilePath = filePath\n\t\tcontent, err = fileOp.GetContent(filePath)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tredirectConfig.Content = string(content)\n\t\tconfig, err = parser.NewStringParser(string(content)).Parse()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tdirs := config.GetDirectives()\n\t\tif len(dirs) > 0 {\n\t\t\tfirstName := dirs[0].GetName()\n\t\t\tswitch firstName {\n\t\t\tcase \"if\":\n\t\t\t\tfor _, ifDir := range dirs {\n\t\t\t\t\tparams := ifDir.GetParameters()\n\t\t\t\t\tif len(params) > 2 && params[0] == \"($host\" {\n\t\t\t\t\t\tdomain := strings.Trim(strings.Trim(params[2], \"'\"), \"^\")\n\t\t\t\t\t\tredirectConfig.Domains = append(redirectConfig.Domains, domain)\n\t\t\t\t\t\tif len(redirectConfig.Domains) > 1 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tredirectConfig.Type = \"domain\"\n\t\t\t\t\t}\n\t\t\t\t\tchildDirs := ifDir.GetBlock().GetDirectives()\n\t\t\t\t\tfor _, dir := range childDirs {\n\t\t\t\t\t\tif dir.GetName() == \"return\" {\n\t\t\t\t\t\t\tdirParams := dir.GetParameters()\n\t\t\t\t\t\t\tif len(dirParams) > 1 {\n\t\t\t\t\t\t\t\tredirectConfig.Redirect = dirParams[0]\n\t\t\t\t\t\t\t\tif strings.HasSuffix(dirParams[1], \"$request_uri\") {\n\t\t\t\t\t\t\t\t\tredirectConfig.KeepPath = true\n\t\t\t\t\t\t\t\t\tredirectConfig.Target = strings.TrimSuffix(dirParams[1], \"$request_uri\")\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tredirectConfig.KeepPath = false\n\t\t\t\t\t\t\t\t\tredirectConfig.Target = dirParams[1]\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase \"rewrite\":\n\t\t\t\tredirectConfig.Type = \"path\"\n\t\t\t\tfor _, pathDir := range dirs {\n\t\t\t\t\tif pathDir.GetName() == \"rewrite\" {\n\t\t\t\t\t\tparams := pathDir.GetParameters()\n\t\t\t\t\t\tif len(params) > 2 {\n\t\t\t\t\t\t\tredirectConfig.Path = strings.Trim(strings.Trim(params[0], \"^\"), \"(.*)\")\n\t\t\t\t\t\t\tif strings.HasSuffix(params[1], \"$1\") {\n\t\t\t\t\t\t\t\tredirectConfig.KeepPath = true\n\t\t\t\t\t\t\t\tredirectConfig.Target = strings.TrimSuffix(params[1], \"$1\")\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tredirectConfig.KeepPath = false\n\t\t\t\t\t\t\t\tredirectConfig.Target = strings.TrimSuffix(params[1], \"?\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif params[2] == \"permanent\" {\n\t\t\t\t\t\t\t\tredirectConfig.Redirect = \"301\"\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tredirectConfig.Redirect = \"302\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase \"error_page\":\n\t\t\t\tredirectConfig.Type = \"404\"\n\t\t\t\tfor _, errDir := range dirs {\n\t\t\t\t\tif errDir.GetName() == \"location\" {\n\t\t\t\t\t\tchildDirs := errDir.GetBlock().GetDirectives()\n\t\t\t\t\t\tfor _, dir := range childDirs {\n\t\t\t\t\t\t\tif dir.GetName() == \"return\" {\n\t\t\t\t\t\t\t\tdirParams := dir.GetParameters()\n\t\t\t\t\t\t\t\tif len(dirParams) > 1 {\n\t\t\t\t\t\t\t\t\tredirectConfig.Redirect = dirParams[0]\n\t\t\t\t\t\t\t\t\tif strings.HasSuffix(dirParams[1], \"$request_uri\") {\n\t\t\t\t\t\t\t\t\t\tredirectConfig.KeepPath = true\n\t\t\t\t\t\t\t\t\t\tredirectConfig.Target = strings.TrimSuffix(dirParams[1], \"$request_uri\")\n\t\t\t\t\t\t\t\t\t\tredirectConfig.RedirectRoot = false\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tredirectConfig.KeepPath = false\n\t\t\t\t\t\t\t\t\t\tredirectConfig.Target = dirParams[1]\n\t\t\t\t\t\t\t\t\t\tredirectConfig.RedirectRoot = redirectConfig.Target == \"/\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tres = append(res, redirectConfig)\n\t}\n\treturn\n}\n\nfunc (w WebsiteService) UpdateRedirectFile(req request.NginxRedirectUpdate) (err error) {\n\tvar (\n\t\twebsite           model.Website\n\t\toldRewriteContent []byte\n\t)\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tsafeName := path.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn buserr.New(\"ErrInvalidParams\")\n\t}\n\tabsolutePath := path.Join(GetSitePath(website, SiteRedirectDir), req.Name+\".conf\")\n\tfileOp := files.NewFileOp()\n\toldRewriteContent, err = fileOp.GetContent(absolutePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm)\n\t\t}\n\t}()\n\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n}\n\nfunc (w WebsiteService) LoadWebsiteDirConfig(req request.WebsiteCommonReq) (*response.WebsiteDirConfig, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres := &response.WebsiteDirConfig{}\n\tabsoluteIndexPath := GetSitePath(website, SiteIndexDir)\n\tvar appFs = afero.NewOsFs()\n\tinfo, err := appFs.Stat(absoluteIndexPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres.User = strconv.FormatUint(uint64(info.Sys().(*syscall.Stat_t).Uid), 10)\n\tres.UserGroup = strconv.FormatUint(uint64(info.Sys().(*syscall.Stat_t).Gid), 10)\n\n\tindexFiles, err := os.ReadDir(absoluteIndexPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres.Dirs = []string{\"/\"}\n\tcheckAndAppendDirs := func(relPath string, entries []os.DirEntry) {\n\t\tfor _, entry := range entries {\n\t\t\tif !entry.IsDir() || entry.Name() == \"node_modules\" || entry.Name() == \"vendor\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnextRelPath := path.Join(relPath, entry.Name())\n\t\t\tres.Dirs = append(res.Dirs, \"/\"+strings.TrimPrefix(nextRelPath, \"/\"))\n\t\t\tentryInfo, _ := entry.Info()\n\t\t\tif entryInfo != nil {\n\t\t\t\tif stat, ok := entryInfo.Sys().(*syscall.Stat_t); ok {\n\t\t\t\t\tif stat.Uid != 1000 || stat.Gid != 1000 {\n\t\t\t\t\t\tres.Msg = i18n.GetMsgByKey(\"ErrPathPermission\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tcheckAndAppendDirs(\"\", indexFiles)\n\tfor _, firstDir := range indexFiles {\n\t\tif !firstDir.IsDir() || firstDir.Name() == \"node_modules\" || firstDir.Name() == \"vendor\" {\n\t\t\tcontinue\n\t\t}\n\t\tsecondLevelPath := path.Join(absoluteIndexPath, firstDir.Name())\n\t\tsecondLevelDirs, _ := os.ReadDir(secondLevelPath)\n\t\tcheckAndAppendDirs(firstDir.Name(), secondLevelDirs)\n\n\t\tfor _, secondDir := range secondLevelDirs {\n\t\t\tif !secondDir.IsDir() || secondDir.Name() == \"node_modules\" || secondDir.Name() == \"vendor\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthirdLevelPath := path.Join(secondLevelPath, secondDir.Name())\n\t\t\tthirdLevelDirs, _ := os.ReadDir(thirdLevelPath)\n\t\t\tcheckAndAppendDirs(path.Join(firstDir.Name(), secondDir.Name()), thirdLevelDirs)\n\t\t}\n\t}\n\n\treturn res, nil\n}\n\nfunc (w WebsiteService) GetDefaultHtml(resourceType string) (*response.WebsiteHtmlRes, error) {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trootPath := path.Join(nginxInstall.GetPath(), \"root\")\n\tfileOp := files.NewFileOp()\n\tdefaultPath := path.Join(rootPath, \"default\")\n\tif !fileOp.Stat(defaultPath) {\n\t\t_ = fileOp.CreateDir(defaultPath, constant.DirPerm)\n\t}\n\n\tres := &response.WebsiteHtmlRes{}\n\n\tswitch resourceType {\n\tcase \"404\":\n\t\tresourcePath := path.Join(defaultPath, \"404.html\")\n\t\tif content, _ := getResourceContent(fileOp, resourcePath); content != \"\" {\n\t\t\tres.Content = content\n\t\t\treturn res, nil\n\t\t}\n\t\tres.Content = string(nginx_conf.NotFoundHTML)\n\t\treturn res, nil\n\tcase \"php\":\n\t\tresourcePath := path.Join(defaultPath, \"index.php\")\n\t\tif content, _ := getResourceContent(fileOp, resourcePath); content != \"\" {\n\t\t\tres.Content = content\n\t\t\treturn res, nil\n\t\t}\n\t\tres.Content = string(nginx_conf.IndexPHP)\n\t\treturn res, nil\n\tcase \"index\":\n\t\tresourcePath := path.Join(defaultPath, \"index.html\")\n\t\tif content, _ := getResourceContent(fileOp, resourcePath); content != \"\" {\n\t\t\tres.Content = content\n\t\t\treturn res, nil\n\t\t}\n\t\tres.Content = string(nginx_conf.Index)\n\t\treturn res, nil\n\tcase \"domain404\":\n\t\tresourcePath := path.Join(rootPath, \"404.html\")\n\t\tif content, _ := getResourceContent(fileOp, resourcePath); content != \"\" {\n\t\t\tres.Content = content\n\t\t\treturn res, nil\n\t\t}\n\t\tres.Content = string(nginx_conf.DomainNotFoundHTML)\n\t\treturn res, nil\n\tcase \"stop\":\n\t\tresourcePath := path.Join(rootPath, \"stop\", \"index.html\")\n\t\tif content, _ := getResourceContent(fileOp, resourcePath); content != \"\" {\n\t\t\tres.Content = content\n\t\t\treturn res, nil\n\t\t}\n\t\tres.Content = string(nginx_conf.StopHTML)\n\t\treturn res, nil\n\t}\n\treturn res, nil\n}\n\nfunc (w WebsiteService) UpdateDefaultHtml(req request.WebsiteHtmlUpdate) error {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\trootPath := path.Join(nginxInstall.GetPath(), \"root\")\n\tfileOp := files.NewFileOp()\n\tdefaultPath := path.Join(rootPath, \"default\")\n\tif !fileOp.Stat(defaultPath) {\n\t\t_ = fileOp.CreateDir(defaultPath, constant.DirPerm)\n\t}\n\tvar resourcePath string\n\tswitch req.Type {\n\tcase \"404\":\n\t\tresourcePath = path.Join(defaultPath, \"404.html\")\n\t\tif req.Sync {\n\t\t\twebsites, _ := websiteRepo.GetBy(repo.WithTypes([]string{constant.Static, constant.Runtime}))\n\t\t\tfor _, website := range websites {\n\t\t\t\tfilePath := path.Join(GetSitePath(website, SiteIndexDir), \"404.html\")\n\t\t\t\tif fileOp.Stat(filePath) {\n\t\t\t\t\t_ = fileOp.SaveFile(filePath, req.Content, constant.DirPerm)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tcase \"php\":\n\t\tresourcePath = path.Join(defaultPath, \"index.php\")\n\tcase \"index\":\n\t\tresourcePath = path.Join(defaultPath, \"index.html\")\n\tcase \"domain404\":\n\t\tresourcePath = path.Join(rootPath, \"404.html\")\n\tcase \"stop\":\n\t\tresourcePath = path.Join(rootPath, \"stop\", \"index.html\")\n\tdefault:\n\t\treturn nil\n\t}\n\treturn fileOp.SaveFile(resourcePath, req.Content, constant.DirPerm)\n}\n\nfunc (w WebsiteService) ChangeGroup(group, newGroup uint) error {\n\treturn websiteRepo.UpdateGroup(group, newGroup)\n}\n\nfunc (w WebsiteService) SetRealIPConfig(req request.WebsiteRealIP) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tparams := []dto.NginxParam{\n\t\t{Name: \"real_ip_recursive\", Params: []string{\"on\"}},\n\t\t{Name: \"set_real_ip_from\", Params: []string{}},\n\t\t{Name: \"real_ip_header\", Params: []string{}},\n\t}\n\tif req.Open {\n\t\tif err := deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparams = []dto.NginxParam{\n\t\t\t{Name: \"real_ip_recursive\", Params: []string{\"on\"}},\n\t\t}\n\t\tvar ips []string\n\t\tipArray := strings.Split(req.IPFrom, \"\\n\")\n\t\tfor _, ip := range ipArray {\n\t\t\tif ip == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif parsedIP := net.ParseIP(ip); parsedIP == nil {\n\t\t\t\tif _, _, err := net.ParseCIDR(ip); err != nil {\n\t\t\t\t\treturn buserr.New(\"ErrParseIP\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tips = append(ips, strings.TrimSpace(ip))\n\t\t}\n\t\tfor _, ip := range ips {\n\t\t\tparams = append(params, dto.NginxParam{Name: \"set_real_ip_from\", Params: []string{ip}})\n\t\t}\n\t\tif req.IPHeader == \"other\" {\n\t\t\tparams = append(params, dto.NginxParam{Name: \"real_ip_header\", Params: []string{req.IPOther}})\n\t\t} else {\n\t\t\tparams = append(params, dto.NginxParam{Name: \"real_ip_header\", Params: []string{req.IPHeader}})\n\t\t}\n\t\treturn updateNginxConfig(constant.NginxScopeServer, params, &website)\n\t}\n\treturn deleteNginxConfig(constant.NginxScopeServer, params, &website)\n}\n\nfunc (w WebsiteService) GetRealIPConfig(websiteID uint) (*response.WebsiteRealIP, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tparams, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{\"real_ip_recursive\"}, &website)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(params) == 0 || len(params[0].Params) == 0 {\n\t\treturn &response.WebsiteRealIP{Open: false}, nil\n\t}\n\tparams, err = getNginxParamsByKeys(constant.NginxScopeServer, []string{\"set_real_ip_from\", \"real_ip_header\"}, &website)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres := &response.WebsiteRealIP{\n\t\tOpen: true,\n\t}\n\tvar ips []string\n\tfor _, param := range params {\n\t\tif param.Name == \"set_real_ip_from\" {\n\t\t\tips = append(ips, param.Params...)\n\t\t}\n\t\tif param.Name == \"real_ip_header\" {\n\t\t\tif _, ok := dto.RealIPKeys[param.Params[0]]; ok {\n\t\t\t\tres.IPHeader = param.Params[0]\n\t\t\t} else {\n\t\t\t\tres.IPHeader = \"other\"\n\t\t\t\tres.IPOther = param.Params[0]\n\t\t\t}\n\t\t}\n\t}\n\tres.IPFrom = strings.Join(ips, \"\\n\")\n\treturn res, err\n}\n\nfunc (w WebsiteService) GetWebsiteResource(websiteID uint) ([]response.Resource, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar (\n\t\tres          []response.Resource\n\t\tdatabaseID   uint\n\t\tdatabaseType string\n\t)\n\tif website.Type == constant.Runtime {\n\t\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres = append(res, response.Resource{\n\t\t\tName:       runtime.Name,\n\t\t\tType:       \"runtime\",\n\t\t\tResourceID: runtime.ID,\n\t\t\tDetail:     runtime,\n\t\t})\n\t}\n\tif website.Type == constant.Deployment {\n\t\tinstall, err := appInstallRepo.GetFirst(repo.WithByID(website.AppInstallID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres = append(res, response.Resource{\n\t\t\tName:       install.Name,\n\t\t\tType:       \"app\",\n\t\t\tResourceID: install.ID,\n\t\t\tDetail:     install,\n\t\t})\n\t\tinstallResources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))\n\t\tfor _, resource := range installResources {\n\t\t\tif resource.Key == constant.AppMysql || resource.Key == constant.AppMariaDB || resource.Key == constant.AppPostgres || resource.Key == constant.AppPostgresql {\n\t\t\t\tdatabaseType = resource.Key\n\t\t\t\tdatabaseID = resource.ResourceId\n\t\t\t}\n\t\t}\n\t}\n\tif website.DbID > 0 {\n\t\tdatabaseType = website.DbType\n\t\tdatabaseID = website.DbID\n\t}\n\tif databaseID > 0 {\n\t\tswitch databaseType {\n\t\tcase constant.AppMysql, constant.AppMariaDB:\n\t\t\tdb, _ := mysqlRepo.Get(repo.WithByID(databaseID))\n\t\t\tif db.ID > 0 {\n\t\t\t\tres = append(res, response.Resource{\n\t\t\t\t\tName:       db.Name,\n\t\t\t\t\tType:       \"database\",\n\t\t\t\t\tResourceID: db.ID,\n\t\t\t\t\tDetail:     db,\n\t\t\t\t})\n\t\t\t}\n\t\tcase constant.AppPostgresql, constant.AppPostgres:\n\t\t\tdb, _ := postgresqlRepo.Get(repo.WithByID(databaseID))\n\t\t\tif db.ID > 0 {\n\t\t\t\tres = append(res, response.Resource{\n\t\t\t\t\tName:       db.Name,\n\t\t\t\t\tType:       \"database\",\n\t\t\t\t\tResourceID: db.ID,\n\t\t\t\t\tDetail:     db,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn res, nil\n}\n\nfunc (w WebsiteService) ListDatabases() ([]response.Database, error) {\n\tvar res []response.Database\n\tmysqlDBs, _ := mysqlRepo.List()\n\tfor _, db := range mysqlDBs {\n\t\tdatabase, _ := databaseRepo.Get(repo.WithByName(db.MysqlName))\n\t\tif database.ID > 0 {\n\t\t\tres = append(res, response.Database{\n\t\t\t\tID:           db.ID,\n\t\t\t\tName:         db.Name,\n\t\t\t\tType:         database.Type,\n\t\t\t\tFrom:         database.From,\n\t\t\t\tDatabaseName: database.Name,\n\t\t\t})\n\t\t}\n\t}\n\tpgSqls, _ := postgresqlRepo.List()\n\tfor _, db := range pgSqls {\n\t\tdatabase, _ := databaseRepo.Get(repo.WithByName(db.PostgresqlName))\n\t\tif database.ID > 0 {\n\t\t\tres = append(res, response.Database{\n\t\t\t\tID:           db.ID,\n\t\t\t\tName:         db.Name,\n\t\t\t\tType:         database.Type,\n\t\t\t\tFrom:         database.From,\n\t\t\t\tDatabaseName: database.Name,\n\t\t\t})\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc (w WebsiteService) ChangeDatabase(req request.ChangeDatabase) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif website.DbID == req.DatabaseID {\n\t\treturn nil\n\t}\n\twebsite.DbID = req.DatabaseID\n\twebsite.DbType = req.DatabaseType\n\treturn websiteRepo.Save(context.Background(), &website)\n}\n\nfunc (w WebsiteService) OperateCrossSiteAccess(req request.CrossSiteAccessOp) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif req.Operation == constant.StatusEnable {\n\t\tcreateOpenBasedirConfig(&website)\n\t}\n\tif req.Operation == constant.StatusDisable {\n\t\tfileOp := files.NewFileOp()\n\t\treturn fileOp.DeleteFile(path.Join(GetSitePath(website, SiteIndexDir), \".user.ini\"))\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteService) ExecComposer(req request.ExecComposerReq) error {\n\tif cmd.CheckIllegal(req.User, req.Mirror, req.Command, req.ExtCommand) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tsitePath := GetSitePath(website, SiteDir)\n\tif !strings.Contains(req.Dir, sitePath) {\n\t\treturn buserr.New(\"ErrWebsiteDir\")\n\t}\n\tif !files.NewFileOp().Stat(path.Join(req.Dir, \"composer.json\")) {\n\t\treturn buserr.New(\"ErrComposerFileNotFound\")\n\t}\n\tif task.CheckResourceTaskIsExecuting(task.TaskExec, req.Command, website.ID) {\n\t\treturn buserr.New(\"ErrInstallExtension\")\n\t}\n\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar command string\n\tif req.Command != \"custom\" {\n\t\tcommand = fmt.Sprintf(\"%s %s\", req.Command, req.ExtCommand)\n\t} else {\n\t\tcommand = req.ExtCommand\n\t}\n\tresourceName := fmt.Sprintf(\"composer %s\", command)\n\tcomposerTask, err := task.NewTaskWithOps(resourceName, task.TaskExec, req.Command, req.TaskID, website.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTask(*composerTask), cmd.WithTimeout(20*time.Minute))\n\tsiteDir, _ := settingRepo.Get(settingRepo.WithByKey(\"WEBSITE_DIR\"))\n\texecDir := strings.ReplaceAll(req.Dir, siteDir.Value, \"/www\")\n\tcomposerTask.AddSubTask(\"\", func(t *task.Task) error {\n\t\tif err := cmdMgr.Run(\"docker\", \"exec\",\n\t\t\t\"-u\", req.User,\n\t\t\truntime.ContainerName,\n\t\t\t\"composer\",\n\t\t\t\"config\", \"-g\",\n\t\t\t\"repo.packagist\",\n\t\t\t\"composer\",\n\t\t\treq.Mirror,\n\t\t); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := cmdMgr.Run(\"docker\", \"exec\",\n\t\t\t\"-u\", req.User,\n\t\t\truntime.ContainerName,\n\t\t\t\"composer\",\n\t\t\tcommand,\n\t\t\t\"--working-dir=\"+execDir,\n\t\t); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}, nil)\n\tgo func() {\n\t\t_ = composerTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (w WebsiteService) UpdateStream(req request.StreamUpdate) error {\n\tif req.StreamConfig.StreamPorts == \"\" {\n\t\treturn buserr.New(\"ErrTypePortRange\")\n\t}\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\twebsite.StreamPorts = req.StreamConfig.StreamPorts\n\tports := strings.Split(req.StreamConfig.StreamPorts, \",\")\n\tfor _, port := range ports {\n\t\tportNum, _ := strconv.Atoi(port)\n\t\tif err = checkWebsitePort(nginxFull.Install.HttpsPort, portNum, website.Type); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tconfig := nginxFull.SiteConfig.Config\n\tservers := config.FindServers()\n\tif len(servers) == 0 {\n\t\treturn errors.New(\"nginx config is not valid\")\n\t}\n\tserver := servers[0]\n\tserver.Listens = []*components.ServerListen{}\n\tvar params []string\n\tif req.UDP {\n\t\tparams = []string{\"udp\"}\n\t}\n\tfor _, port := range ports {\n\t\tserver.UpdateListen(port, false, params...)\n\t\tif website.IPV6 {\n\t\t\tserver.UpdateListen(\"[::]:\"+port, false, params...)\n\t\t}\n\t}\n\tupstream := components.Upstream{\n\t\tUpstreamName: website.Alias,\n\t}\n\tif req.Algorithm != \"default\" {\n\t\tupstream.UpdateDirective(req.Algorithm, []string{})\n\t}\n\tupstream.UpstreamServers = parseUpstreamServers(req.Servers)\n\tfor i, dir := range config.Block.Directives {\n\t\tif dir.GetName() == \"upstream\" && dir.GetParameters()[0] == website.Alias {\n\t\t\tconfig.Block.Directives[i] = &upstream\n\t\t}\n\t}\n\n\tif err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\tif err = nginxCheckAndReload(nginxFull.SiteConfig.OldContent, config.FilePath, nginxFull.Install.ContainerName); err != nil {\n\t\treturn err\n\t}\n\twebsite.StreamPorts = req.StreamConfig.StreamPorts\n\treturn websiteRepo.Save(context.Background(), &website)\n}\n"
  },
  {
    "path": "agent/app/service/website_acme_account.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ssl\"\n)\n\ntype WebsiteAcmeAccountService struct {\n}\n\ntype IWebsiteAcmeAccountService interface {\n\tPage(search dto.PageInfo) (int64, []response.WebsiteAcmeAccountDTO, error)\n\tCreate(create request.WebsiteAcmeAccountCreate) (*response.WebsiteAcmeAccountDTO, error)\n\tDelete(id uint) error\n\tUpdate(update request.WebsiteAcmeAccountUpdate) (*response.WebsiteAcmeAccountDTO, error)\n}\n\nfunc NewIWebsiteAcmeAccountService() IWebsiteAcmeAccountService {\n\treturn &WebsiteAcmeAccountService{}\n}\n\nfunc (w WebsiteAcmeAccountService) Page(search dto.PageInfo) (int64, []response.WebsiteAcmeAccountDTO, error) {\n\ttotal, accounts, err := websiteAcmeRepo.Page(search.Page, search.PageSize, repo.WithOrderDesc(\"created_at\"))\n\tvar accountDTOs []response.WebsiteAcmeAccountDTO\n\tfor _, account := range accounts {\n\t\taccountDTOs = append(accountDTOs, response.WebsiteAcmeAccountDTO{\n\t\t\tWebsiteAcmeAccount: account,\n\t\t})\n\t}\n\treturn total, accountDTOs, err\n}\n\nfunc (w WebsiteAcmeAccountService) Create(create request.WebsiteAcmeAccountCreate) (*response.WebsiteAcmeAccountDTO, error) {\n\texist, _ := websiteAcmeRepo.GetFirst(websiteAcmeRepo.WithEmail(create.Email), websiteAcmeRepo.WithType(create.Type))\n\tif exist != nil {\n\t\treturn nil, buserr.New(\"ErrEmailIsExist\")\n\t}\n\tacmeAccount := &model.WebsiteAcmeAccount{\n\t\tEmail:    create.Email,\n\t\tType:     create.Type,\n\t\tKeyType:  create.KeyType,\n\t\tUseProxy: create.UseProxy,\n\t\tUseEAB:   create.UseEAB,\n\t}\n\n\tif create.Type == \"google\" || create.Type == \"freessl\" || (create.Type == \"custom\" && create.UseEAB) {\n\t\tif create.EabKid == \"\" || create.EabHmacKey == \"\" {\n\t\t\treturn nil, buserr.New(\"ErrEabKidOrEabHmacKeyCannotBlank\")\n\t\t}\n\t\tacmeAccount.EabKid = create.EabKid\n\t\tacmeAccount.EabHmacKey = create.EabHmacKey\n\t}\n\tif create.Type == \"custom\" && create.CaDirURL != \"\" {\n\t\tacmeAccount.CaDirURL = create.CaDirURL\n\t}\n\n\tclient, err := ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tprivateKey, err := ssl.GetPrivateKey(client.User.GetPrivateKey(), ssl.KeyType(create.KeyType))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tacmeAccount.PrivateKey = string(privateKey)\n\tacmeAccount.URL = client.User.Registration.URI\n\n\tif err := websiteAcmeRepo.Create(*acmeAccount); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: *acmeAccount}, nil\n}\n\nfunc (w WebsiteAcmeAccountService) Delete(id uint) error {\n\tif ssls, _ := websiteSSLRepo.List(websiteSSLRepo.WithByAcmeAccountId(id)); len(ssls) > 0 {\n\t\treturn buserr.New(\"ErrAccountCannotDelete\")\n\t}\n\treturn websiteAcmeRepo.DeleteBy(repo.WithByID(id))\n}\n\nfunc (w WebsiteAcmeAccountService) Update(update request.WebsiteAcmeAccountUpdate) (*response.WebsiteAcmeAccountDTO, error) {\n\taccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(update.ID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\taccount.UseProxy = update.UseProxy\n\tif err := websiteAcmeRepo.Save(*account); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &response.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: *account}, nil\n}\n"
  },
  {
    "path": "agent/app/service/website_auth_basic.go",
    "content": "package service\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\nfunc (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) {\n\tvar (\n\t\twebsite     model.Website\n\t\tauthContent []byte\n\t\tnginxParams []response.NginxParam\n\t)\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn\n\t}\n\tabsoluteAuthPath := GetSitePath(website, SiteRootAuthBasicPath)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(absoluteAuthPath) {\n\t\treturn\n\t}\n\tnginxParams, err = getNginxParamsByKeys(constant.NginxScopeServer, []string{\"auth_basic\"}, &website)\n\tif err != nil {\n\t\treturn\n\t}\n\tres.Enable = len(nginxParams[0].Params) > 0\n\tauthContent, err = fileOp.GetContent(absoluteAuthPath)\n\tauthArray := strings.Split(string(authContent), \"\\n\")\n\tfor _, line := range authArray {\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tparams := strings.Split(line, \":\")\n\t\tauth := dto.NginxAuth{\n\t\t\tUsername: params[0],\n\t\t}\n\t\tif len(params) == 3 {\n\t\t\tauth.Remark = params[2]\n\t\t}\n\t\tres.Items = append(res.Items, auth)\n\t}\n\treturn\n}\n\nfunc (w WebsiteService) UpdateAuthBasic(req request.NginxAuthUpdate) (err error) {\n\tvar (\n\t\twebsite     model.Website\n\t\tparams      []dto.NginxParam\n\t\tauthContent []byte\n\t\tauthArray   []string\n\t)\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tauthPath := fmt.Sprintf(\"/www/sites/%s/auth_basic/auth.pass\", website.Alias)\n\tabsoluteAuthPath := GetSitePath(website, SiteRootAuthBasicPath)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(path.Dir(absoluteAuthPath)) {\n\t\t_ = fileOp.CreateDir(path.Dir(absoluteAuthPath), constant.DirPerm)\n\t}\n\tif !fileOp.Stat(absoluteAuthPath) {\n\t\t_ = fileOp.CreateFile(absoluteAuthPath)\n\t}\n\n\tparams = append(params, dto.NginxParam{Name: \"auth_basic\", Params: []string{`\"Authentication\"`}})\n\tparams = append(params, dto.NginxParam{Name: \"auth_basic_user_file\", Params: []string{authPath}})\n\tauthContent, err = fileOp.GetContent(absoluteAuthPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(authContent) > 0 {\n\t\tauthArray = strings.Split(string(authContent), \"\\n\")\n\t}\n\tswitch req.Operate {\n\tcase \"disable\":\n\t\treturn deleteNginxConfig(constant.NginxScopeServer, params, &website)\n\tcase \"enable\":\n\t\treturn updateNginxConfig(constant.NginxScopeServer, params, &website)\n\tcase \"create\":\n\t\tfor _, line := range authArray {\n\t\t\tauthParams := strings.Split(line, \":\")\n\t\t\tusername := authParams[0]\n\t\t\tif username == req.Username {\n\t\t\t\terr = buserr.New(\"ErrUsernameIsExist\")\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tvar passwdHash []byte\n\t\tpasswdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tline := fmt.Sprintf(\"%s:%s\\n\", req.Username, passwdHash)\n\t\tif req.Remark != \"\" {\n\t\t\tline = fmt.Sprintf(\"%s:%s:%s\\n\", req.Username, passwdHash, req.Remark)\n\t\t}\n\t\tauthArray = append(authArray, line)\n\tcase \"edit\":\n\t\tuserExist := false\n\t\tfor index, line := range authArray {\n\t\t\tauthParams := strings.Split(line, \":\")\n\t\t\tusername := authParams[0]\n\t\t\tif username == req.Username {\n\t\t\t\tuserExist = true\n\t\t\t\tvar passwdHash []byte\n\t\t\t\tpasswdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tuserPasswd := fmt.Sprintf(\"%s:%s\\n\", req.Username, passwdHash)\n\t\t\t\tif req.Remark != \"\" {\n\t\t\t\t\tuserPasswd = fmt.Sprintf(\"%s:%s:%s\\n\", req.Username, passwdHash, req.Remark)\n\t\t\t\t}\n\t\t\t\tauthArray[index] = userPasswd\n\t\t\t}\n\t\t}\n\t\tif !userExist {\n\t\t\terr = buserr.New(\"ErrUsernameIsNotExist\")\n\t\t\treturn\n\t\t}\n\tcase \"delete\":\n\t\tdeleteIndex := -1\n\t\tfor index, line := range authArray {\n\t\t\tauthParams := strings.Split(line, \":\")\n\t\t\tusername := authParams[0]\n\t\t\tif username == req.Username {\n\t\t\t\tdeleteIndex = index\n\t\t\t}\n\t\t}\n\t\tif deleteIndex < 0 {\n\t\t\treturn\n\t\t}\n\t\tauthArray = append(authArray[:deleteIndex], authArray[deleteIndex+1:]...)\n\t}\n\n\tvar passFile *os.File\n\tpassFile, err = os.Create(absoluteAuthPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer passFile.Close()\n\twriter := bufio.NewWriter(passFile)\n\tfor _, line := range authArray {\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\t_, err = writer.WriteString(line + \"\\n\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\terr = writer.Flush()\n\tif err != nil {\n\t\treturn\n\t}\n\tauthContent, err = fileOp.GetContent(absoluteAuthPath)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(authContent) == 0 {\n\t\tif err = deleteNginxConfig(constant.NginxScopeServer, params, &website); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (w WebsiteService) GetPathAuthBasics(req request.NginxAuthReq) (res []response.NginxPathAuthRes, err error) {\n\tvar (\n\t\twebsite     model.Website\n\t\tauthContent []byte\n\t)\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\tabsoluteAuthDir := GetSitePath(website, SitePathAuthBasicDir)\n\tpassDir := path.Join(absoluteAuthDir, \"pass\")\n\tif !fileOp.Stat(absoluteAuthDir) || !fileOp.Stat(passDir) {\n\t\treturn\n\t}\n\n\tentries, err := os.ReadDir(absoluteAuthDir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, entry := range entries {\n\t\tif !entry.IsDir() {\n\t\t\tname := strings.TrimSuffix(entry.Name(), \".conf\")\n\t\t\tpathAuth := dto.NginxPathAuth{\n\t\t\t\tName: name,\n\t\t\t}\n\t\t\tconfigPath := path.Join(absoluteAuthDir, entry.Name())\n\t\t\tcontent, err := fileOp.GetContent(configPath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tconfig, err := parser.NewStringParser(string(content)).Parse()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tdirectives := config.Directives\n\t\t\tlocation, _ := directives[0].(*components.Location)\n\t\t\tpathAuth.Path = strings.TrimPrefix(location.Match, \"^\")\n\t\t\tpassPath := path.Join(passDir, fmt.Sprintf(\"%s.pass\", name))\n\t\t\tauthContent, err = fileOp.GetContent(passPath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tauthArray := strings.Split(string(authContent), \"\\n\")\n\t\t\tfor _, line := range authArray {\n\t\t\t\tif line == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tparams := strings.Split(line, \":\")\n\t\t\t\tpathAuth.Username = params[0]\n\t\t\t\tif len(params) == 3 {\n\t\t\t\t\tpathAuth.Remark = params[2]\n\t\t\t\t}\n\t\t\t}\n\t\t\tres = append(res, response.NginxPathAuthRes{\n\t\t\t\tNginxPathAuth: pathAuth,\n\t\t\t})\n\t\t}\n\t}\n\treturn\n}\n\nfunc (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfileOp := files.NewFileOp()\n\tauthDir := GetSitePath(website, SitePathAuthBasicDir)\n\tif !fileOp.Stat(authDir) {\n\t\t_ = fileOp.CreateDir(authDir, constant.DirPerm)\n\t}\n\tpassDir := path.Join(authDir, \"pass\")\n\tif !fileOp.Stat(passDir) {\n\t\t_ = fileOp.CreateDir(passDir, constant.DirPerm)\n\t}\n\tsafeName := path.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn buserr.New(\"ErrInvalidParams\")\n\t}\n\tconfPath := path.Join(authDir, fmt.Sprintf(\"%s.conf\", req.Name))\n\tpassPath := path.Join(passDir, fmt.Sprintf(\"%s.pass\", req.Name))\n\tvar config *components.Config\n\tswitch req.Operate {\n\tcase \"delete\":\n\t\t_ = fileOp.DeleteFile(confPath)\n\t\t_ = fileOp.DeleteFile(passPath)\n\t\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n\tcase \"create\":\n\t\tconfig, err = parser.NewStringParser(string(nginx_conf.PathAuth)).Parse()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif fileOp.Stat(confPath) || fileOp.Stat(passPath) {\n\t\t\treturn buserr.New(\"ErrNameIsExist\")\n\t\t}\n\tcase \"edit\":\n\t\tpar, err := parser.NewParser(confPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tconfig, err = par.Parse()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tconfig.FilePath = confPath\n\tdirectives := config.Directives\n\tlocation, _ := directives[0].(*components.Location)\n\tlocation.UpdateDirective(\"auth_basic_user_file\", []string{fmt.Sprintf(\"/www/sites/%s/path_auth/pass/%s\", website.Alias, fmt.Sprintf(\"%s.pass\", req.Name))})\n\tlocation.ChangePath(\"~*\", fmt.Sprintf(\"^%s\", req.Path))\n\tvar passwdHash []byte\n\tpasswdHash, err = bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)\n\tif err != nil {\n\t\treturn err\n\t}\n\tline := fmt.Sprintf(\"%s:%s\\n\", req.Username, passwdHash)\n\tif req.Remark != \"\" {\n\t\tline = fmt.Sprintf(\"%s:%s:%s\\n\", req.Username, passwdHash, req.Remark)\n\t}\n\t_ = fileOp.SaveFile(passPath, line, constant.DirPerm)\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn buserr.WithErr(\"ErrUpdateBuWebsite\", err)\n\t}\n\tnginxInclude := fmt.Sprintf(\"/www/sites/%s/path_auth/*.conf\", website.Alias)\n\tif err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: \"include\", Params: []string{nginxInclude}}}, &website); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/website_ca.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/ecdsa\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"log\"\n\t\"math/big\"\n\t\"net\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ssl\"\n\t\"github.com/go-acme/lego/v4/certcrypto\"\n)\n\ntype WebsiteCAService struct {\n}\n\ntype IWebsiteCAService interface {\n\tPage(search request.WebsiteCASearch) (int64, []response.WebsiteCADTO, error)\n\tCreate(create request.WebsiteCACreate) (*request.WebsiteCACreate, error)\n\tGetCA(id uint) (*response.WebsiteCADTO, error)\n\tDelete(id uint) error\n\tObtainSSL(req request.WebsiteCAObtain) (*model.WebsiteSSL, error)\n\tDownloadFile(id uint) (*os.File, error)\n}\n\nfunc NewIWebsiteCAService() IWebsiteCAService {\n\treturn &WebsiteCAService{}\n}\n\nfunc (w WebsiteCAService) Page(search request.WebsiteCASearch) (int64, []response.WebsiteCADTO, error) {\n\ttotal, cas, err := websiteCARepo.Page(search.Page, search.PageSize, repo.WithOrderDesc(\"created_at\"))\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar caDTOs []response.WebsiteCADTO\n\tfor _, ca := range cas {\n\t\tcaDTOs = append(caDTOs, response.WebsiteCADTO{\n\t\t\tWebsiteCA: ca,\n\t\t})\n\t}\n\treturn total, caDTOs, err\n}\n\nfunc (w WebsiteCAService) Create(create request.WebsiteCACreate) (*request.WebsiteCACreate, error) {\n\tif exist, _ := websiteCARepo.GetFirst(repo.WithByName(create.Name)); exist.ID > 0 {\n\t\treturn nil, buserr.New(\"ErrNameIsExist\")\n\t}\n\n\tca := &model.WebsiteCA{\n\t\tName:    create.Name,\n\t\tKeyType: create.KeyType,\n\t}\n\n\tpkixName := pkix.Name{\n\t\tCommonName:         create.CommonName,\n\t\tCountry:            []string{create.Country},\n\t\tOrganization:       []string{create.Organization},\n\t\tOrganizationalUnit: []string{create.OrganizationUint},\n\t}\n\tif create.Province != \"\" {\n\t\tpkixName.Province = []string{create.Province}\n\t}\n\tif create.City != \"\" {\n\t\tpkixName.Locality = []string{create.City}\n\t}\n\n\trootCA := &x509.Certificate{\n\t\tSerialNumber:          big.NewInt(time.Now().Unix() + 1),\n\t\tSubject:               pkixName,\n\t\tNotBefore:             time.Now(),\n\t\tNotAfter:              time.Now().AddDate(10, 0, 0),\n\t\tBasicConstraintsValid: true,\n\t\tIsCA:                  true,\n\t\tMaxPathLen:            1,\n\t\tMaxPathLenZero:        false,\n\t\tKeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign,\n\t}\n\n\tinterPrivateKey, interPublicKey, privateBytes, err := createPrivateKey(create.KeyType)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tca.PrivateKey = string(privateBytes)\n\n\trootDer, err := x509.CreateCertificate(rand.Reader, rootCA, rootCA, interPublicKey, interPrivateKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trootCert, err := x509.ParseCertificate(rootDer)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcertBlock := &pem.Block{\n\t\tType:  \"CERTIFICATE\",\n\t\tBytes: rootCert.Raw,\n\t}\n\tpemData := pem.EncodeToMemory(certBlock)\n\tca.CSR = string(pemData)\n\n\tif err := websiteCARepo.Create(context.Background(), ca); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &create, nil\n}\n\nfunc (w WebsiteCAService) GetCA(id uint) (*response.WebsiteCADTO, error) {\n\tres := &response.WebsiteCADTO{}\n\tca, err := websiteCARepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres.WebsiteCA = ca\n\tcertBlock, _ := pem.Decode([]byte(ca.CSR))\n\tif certBlock == nil {\n\t\treturn nil, buserr.New(\"ErrSSLCertificateFormat\")\n\t}\n\tcert, err := x509.ParseCertificate(certBlock.Bytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres.CommonName = cert.Issuer.CommonName\n\tres.Organization = strings.Join(cert.Issuer.Organization, \",\")\n\tres.Country = strings.Join(cert.Issuer.Country, \",\")\n\tres.Province = strings.Join(cert.Issuer.Province, \",\")\n\tres.City = strings.Join(cert.Issuer.Locality, \",\")\n\tres.OrganizationUint = strings.Join(cert.Issuer.OrganizationalUnit, \",\")\n\n\treturn res, nil\n}\n\nfunc (w WebsiteCAService) Delete(id uint) error {\n\tssls, _ := websiteSSLRepo.List(websiteSSLRepo.WithByCAID(id))\n\tif len(ssls) > 0 {\n\t\treturn buserr.New(\"ErrDeleteCAWithSSL\")\n\t}\n\texist, err := websiteCARepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif exist.Name == \"1Panel\" {\n\t\treturn buserr.New(\"ErrDefaultCA\")\n\t}\n\treturn websiteCARepo.DeleteBy(repo.WithByID(id))\n}\n\nfunc (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.WebsiteSSL, error) {\n\tvar (\n\t\tdomains    []string\n\t\tips        []net.IP\n\t\twebsiteSSL = &model.WebsiteSSL{}\n\t\terr        error\n\t\tca         model.WebsiteCA\n\t)\n\tif req.Renew {\n\t\twebsiteSSL, err = websiteSSLRepo.GetFirst(repo.WithByID(req.SSLID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tca, err = websiteCARepo.GetFirst(repo.WithByID(websiteSSL.CaID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\texistDomains := []string{websiteSSL.PrimaryDomain}\n\t\tif websiteSSL.Domains != \"\" {\n\t\t\texistDomains = append(existDomains, strings.Split(websiteSSL.Domains, \",\")...)\n\t\t}\n\t\tfor _, domain := range existDomains {\n\t\t\tif ipAddress := net.ParseIP(domain); ipAddress == nil {\n\t\t\t\tdomains = append(domains, domain)\n\t\t\t} else {\n\t\t\t\tips = append(ips, ipAddress)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tca, err = websiteCARepo.GetFirst(repo.WithByID(req.ID))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\twebsiteSSL = &model.WebsiteSSL{\n\t\t\tProvider:    constant.SelfSigned,\n\t\t\tKeyType:     req.KeyType,\n\t\t\tPushDir:     req.PushDir,\n\t\t\tCaID:        ca.ID,\n\t\t\tAutoRenew:   req.AutoRenew,\n\t\t\tDescription: req.Description,\n\t\t\tExecShell:   req.ExecShell,\n\t\t}\n\t\tif req.ExecShell {\n\t\t\twebsiteSSL.Shell = req.Shell\n\t\t}\n\t\tif req.PushDir {\n\t\t\tif !files.NewFileOp().Stat(req.Dir) {\n\t\t\t\treturn nil, buserr.New(\"ErrLinkPathNotFound\")\n\t\t\t}\n\t\t\twebsiteSSL.Dir = req.Dir\n\t\t}\n\t\tif req.Domains != \"\" {\n\t\t\tdomainArray := strings.Split(req.Domains, \"\\n\")\n\t\t\tfor _, domain := range domainArray {\n\t\t\t\tif ipAddress := net.ParseIP(domain); ipAddress == nil {\n\t\t\t\t\tif domain != \"localhost\" && !common.IsValidDomain(domain) {\n\t\t\t\t\t\terr = buserr.WithName(\"ErrDomainFormat\", domain)\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\tdomains = append(domains, domain)\n\t\t\t\t} else {\n\t\t\t\t\tips = append(ips, ipAddress)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(domains) > 0 {\n\t\t\t\twebsiteSSL.PrimaryDomain = domains[0]\n\t\t\t\twebsiteSSL.Domains = strings.Join(domains[1:], \",\")\n\t\t\t}\n\t\t\tipStrings := make([]string, len(ips))\n\t\t\tfor i, ip := range ips {\n\t\t\t\tipStrings[i] = ip.String()\n\t\t\t}\n\t\t\tif websiteSSL.PrimaryDomain == \"\" && len(ips) > 0 {\n\t\t\t\twebsiteSSL.PrimaryDomain = ipStrings[0]\n\t\t\t\tipStrings = ipStrings[1:]\n\t\t\t}\n\t\t\tif len(ipStrings) > 0 {\n\t\t\t\tif websiteSSL.Domains != \"\" {\n\t\t\t\t\twebsiteSSL.Domains += \",\"\n\t\t\t\t}\n\t\t\t\twebsiteSSL.Domains += strings.Join(ipStrings, \",\")\n\t\t\t}\n\n\t\t}\n\t}\n\n\trootCertBlock, _ := pem.Decode([]byte(ca.CSR))\n\tif rootCertBlock == nil {\n\t\treturn nil, buserr.New(\"ErrSSLCertificateFormat\")\n\t}\n\trootCsr, err := x509.ParseCertificate(rootCertBlock.Bytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trootPrivateKeyBlock, _ := pem.Decode([]byte(ca.PrivateKey))\n\tif rootPrivateKeyBlock == nil {\n\t\treturn nil, buserr.New(\"ErrSSLCertificateFormat\")\n\t}\n\n\tvar rootPrivateKey any\n\tif ssl.KeyType(ca.KeyType) == certcrypto.EC256 || ssl.KeyType(ca.KeyType) == certcrypto.EC384 {\n\t\trootPrivateKey, err = x509.ParseECPrivateKey(rootPrivateKeyBlock.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\trootPrivateKey, err = x509.ParsePKCS1PrivateKey(rootPrivateKeyBlock.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tinterPrivateKey, interPublicKey, _, err := createPrivateKey(websiteSSL.KeyType)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnotAfter := time.Now()\n\tif req.Unit == \"year\" {\n\t\tnotAfter = notAfter.AddDate(req.Time, 0, 0)\n\t} else {\n\t\tnotAfter = notAfter.AddDate(0, 0, req.Time)\n\t}\n\tinterCsr := &x509.Certificate{\n\t\tSerialNumber:          big.NewInt(time.Now().Unix() + 2),\n\t\tSubject:               rootCsr.Subject,\n\t\tNotBefore:             time.Now(),\n\t\tNotAfter:              notAfter,\n\t\tBasicConstraintsValid: true,\n\t\tIsCA:                  true,\n\t\tMaxPathLen:            0,\n\t\tMaxPathLenZero:        true,\n\t\tKeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign,\n\t}\n\tinterDer, err := x509.CreateCertificate(rand.Reader, interCsr, rootCsr, interPublicKey, rootPrivateKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tinterCert, err := x509.ParseCertificate(interDer)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tinterCertBlock := &pem.Block{\n\t\tType:  \"CERTIFICATE\",\n\t\tBytes: interCert.Raw,\n\t}\n\t_, publicKey, privateKeyBytes, err := createPrivateKey(websiteSSL.KeyType)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcommonName := \"\"\n\tif len(domains) > 0 {\n\t\tcommonName = domains[0]\n\t}\n\tif len(ips) > 0 {\n\t\tcommonName = ips[0].String()\n\t}\n\tsubject := rootCsr.Subject\n\tsubject.CommonName = commonName\n\tcsr := &x509.Certificate{\n\t\tSerialNumber:          big.NewInt(time.Now().Unix() + 3),\n\t\tSubject:               subject,\n\t\tNotBefore:             time.Now(),\n\t\tNotAfter:              notAfter,\n\t\tBasicConstraintsValid: true,\n\t\tIsCA:                  false,\n\t\tKeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},\n\t\tDNSNames:              domains,\n\t\tIPAddresses:           ips,\n\t}\n\n\tder, err := x509.CreateCertificate(rand.Reader, csr, interCert, publicKey, interPrivateKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcert, err := x509.ParseCertificate(der)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcertBlock := &pem.Block{\n\t\tType:  \"CERTIFICATE\",\n\t\tBytes: cert.Raw,\n\t}\n\twebsiteSSL.Pem = string(pem.EncodeToMemory(certBlock)) + string(pem.EncodeToMemory(rootCertBlock)) + string(pem.EncodeToMemory(interCertBlock))\n\twebsiteSSL.PrivateKey = string(privateKeyBytes)\n\twebsiteSSL.ExpireDate = cert.NotAfter\n\twebsiteSSL.StartDate = cert.NotBefore\n\twebsiteSSL.Type = cert.Issuer.CommonName\n\twebsiteSSL.Organization = rootCsr.Subject.Organization[0]\n\twebsiteSSL.Status = constant.SSLReady\n\n\tif req.Renew {\n\t\tif err := websiteSSLRepo.Save(websiteSSL); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tif err := websiteSSLRepo.Create(context.Background(), websiteSSL); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tlogFile, _ := os.OpenFile(path.Join(global.Dir.SSLLogDir, fmt.Sprintf(\"%s-ssl-%d.log\", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tdefer logFile.Close()\n\tlogger := log.New(logFile, \"\", log.LstdFlags)\n\tlogger.Println(i18n.GetMsgWithMap(\"ApplySSLSuccess\", map[string]interface{}{\"domain\": strings.Join(domains, \",\")}))\n\tsaveCertificateFile(websiteSSL, logger)\n\tif websiteSSL.ExecShell {\n\t\tworkDir := global.Dir.DataDir\n\t\tif websiteSSL.PushDir {\n\t\t\tworkDir = websiteSSL.Dir\n\t\t}\n\t\tlogger.Println(i18n.GetMsgByKey(\"ExecShellStart\"))\n\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30*time.Minute), cmd.WithLogger(logger), cmd.WithWorkDir(workDir))\n\t\tif err = cmdMgr.RunBashC(websiteSSL.Shell); err != nil {\n\t\t\tlogger.Println(i18n.GetMsgWithMap(\"ErrExecShell\", map[string]interface{}{\"err\": err.Error()}))\n\t\t} else {\n\t\t\tlogger.Println(i18n.GetMsgByKey(\"ExecShellSuccess\"))\n\t\t}\n\t}\n\treloadSystemSSL(websiteSSL, logger)\n\treturn websiteSSL, nil\n}\n\nfunc createPrivateKey(keyType string) (privateKey any, publicKey any, privateKeyBytes []byte, err error) {\n\tprivateKey, err = certcrypto.GeneratePrivateKey(ssl.KeyType(keyType))\n\tif err != nil {\n\t\treturn\n\t}\n\tvar (\n\t\tcaPrivateKeyPEM = new(bytes.Buffer)\n\t)\n\tif ssl.KeyType(keyType) == certcrypto.EC256 || ssl.KeyType(keyType) == certcrypto.EC384 {\n\t\tpublicKey = &privateKey.(*ecdsa.PrivateKey).PublicKey\n\t\tpublicKey = publicKey.(*ecdsa.PublicKey)\n\t\tblock := &pem.Block{\n\t\t\tType: \"EC PRIVATE KEY\",\n\t\t}\n\t\tprivateBytes, sErr := x509.MarshalECPrivateKey(privateKey.(*ecdsa.PrivateKey))\n\t\tif sErr != nil {\n\t\t\terr = sErr\n\t\t\treturn\n\t\t}\n\t\tblock.Bytes = privateBytes\n\t\t_ = pem.Encode(caPrivateKeyPEM, block)\n\t} else {\n\t\tpublicKey = &privateKey.(*rsa.PrivateKey).PublicKey\n\t\t_ = pem.Encode(caPrivateKeyPEM, &pem.Block{\n\t\t\tType:  \"RSA PRIVATE KEY\",\n\t\t\tBytes: x509.MarshalPKCS1PrivateKey(privateKey.(*rsa.PrivateKey)),\n\t\t})\n\t}\n\tprivateKeyBytes = caPrivateKeyPEM.Bytes()\n\treturn\n}\n\nfunc (w WebsiteCAService) DownloadFile(id uint) (*os.File, error) {\n\tca, err := websiteCARepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfileOp := files.NewFileOp()\n\tdir := path.Join(global.Dir.DataDir, \"tmp/ssl\", ca.Name)\n\tif fileOp.Stat(dir) {\n\t\tif err = fileOp.DeleteDir(dir); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif err = fileOp.CreateDir(dir, constant.DirPerm); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = fileOp.WriteFile(path.Join(dir, \"ca.crt\"), strings.NewReader(ca.CSR), constant.DirPerm); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = fileOp.WriteFile(path.Join(dir, \"ca.key\"), strings.NewReader(ca.PrivateKey), constant.DirPerm); err != nil {\n\t\treturn nil, err\n\t}\n\tfileName := ca.Name + \".zip\"\n\tif err = fileOp.Compress([]string{path.Join(dir, \"ca.crt\"), path.Join(dir, \"ca.key\")}, dir, fileName, files.SdkZip, \"\"); err != nil {\n\t\treturn nil, err\n\t}\n\treturn os.Open(path.Join(dir, fileName))\n}\n"
  },
  {
    "path": "agent/app/service/website_dns_account.go",
    "content": "package service\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n)\n\ntype WebsiteDnsAccountService struct {\n}\n\ntype IWebsiteDnsAccountService interface {\n\tPage(search dto.PageInfo) (int64, []response.WebsiteDnsAccountDTO, error)\n\tCreate(create request.WebsiteDnsAccountCreate) (request.WebsiteDnsAccountCreate, error)\n\tUpdate(update request.WebsiteDnsAccountUpdate) (request.WebsiteDnsAccountUpdate, error)\n\tDelete(id uint) error\n}\n\nfunc NewIWebsiteDnsAccountService() IWebsiteDnsAccountService {\n\treturn &WebsiteDnsAccountService{}\n}\n\nfunc (w WebsiteDnsAccountService) Page(search dto.PageInfo) (int64, []response.WebsiteDnsAccountDTO, error) {\n\ttotal, accounts, err := websiteDnsRepo.Page(search.Page, search.PageSize, repo.WithOrderDesc(\"created_at\"))\n\tvar accountDTOs []response.WebsiteDnsAccountDTO\n\tfor _, account := range accounts {\n\t\tauth := make(map[string]string)\n\t\t_ = json.Unmarshal([]byte(account.Authorization), &auth)\n\t\taccountDTOs = append(accountDTOs, response.WebsiteDnsAccountDTO{\n\t\t\tWebsiteDnsAccount: account,\n\t\t\tAuthorization:     auth,\n\t\t})\n\t}\n\treturn total, accountDTOs, err\n}\n\nfunc (w WebsiteDnsAccountService) Create(create request.WebsiteDnsAccountCreate) (request.WebsiteDnsAccountCreate, error) {\n\texist, _ := websiteDnsRepo.GetFirst(repo.WithByName(create.Name))\n\tif exist != nil {\n\t\treturn request.WebsiteDnsAccountCreate{}, buserr.New(\"ErrNameIsExist\")\n\t}\n\n\tauthorization, err := json.Marshal(create.Authorization)\n\tif err != nil {\n\t\treturn request.WebsiteDnsAccountCreate{}, err\n\t}\n\n\tif err := websiteDnsRepo.Create(model.WebsiteDnsAccount{\n\t\tName:          create.Name,\n\t\tType:          create.Type,\n\t\tAuthorization: string(authorization),\n\t}); err != nil {\n\t\treturn request.WebsiteDnsAccountCreate{}, err\n\t}\n\n\treturn create, nil\n}\n\nfunc (w WebsiteDnsAccountService) Update(update request.WebsiteDnsAccountUpdate) (request.WebsiteDnsAccountUpdate, error) {\n\tauthorization, err := json.Marshal(update.Authorization)\n\tif err != nil {\n\t\treturn request.WebsiteDnsAccountUpdate{}, err\n\t}\n\texists, _ := websiteDnsRepo.List(repo.WithByName(update.Name))\n\tfor _, exist := range exists {\n\t\tif exist.ID != update.ID {\n\t\t\treturn request.WebsiteDnsAccountUpdate{}, buserr.New(\"ErrNameIsExist\")\n\t\t}\n\t}\n\tif err := websiteDnsRepo.Save(model.WebsiteDnsAccount{\n\t\tBaseModel: model.BaseModel{\n\t\t\tID: update.ID,\n\t\t},\n\t\tName:          update.Name,\n\t\tType:          update.Type,\n\t\tAuthorization: string(authorization),\n\t}); err != nil {\n\t\treturn request.WebsiteDnsAccountUpdate{}, err\n\t}\n\n\treturn update, nil\n}\n\nfunc (w WebsiteDnsAccountService) Delete(id uint) error {\n\tif ssls, _ := websiteSSLRepo.List(websiteSSLRepo.WithByDnsAccountId(id)); len(ssls) > 0 {\n\t\treturn buserr.New(\"ErrAccountCannotDelete\")\n\t}\n\treturn websiteDnsRepo.DeleteBy(repo.WithByID(id))\n}\n"
  },
  {
    "path": "agent/app/service/website_domain.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"path\"\n\t\"strconv\"\n)\n\nfunc (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error) {\n\tvar (\n\t\tdomainModels []model.WebsiteDomain\n\t\taddPorts     []int\n\t)\n\thttpPort, httpsPort, err := getAppInstallPort(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(create.WebsiteID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdomainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, httpsPort, create.WebsiteID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgo func() {\n\t\t_ = OperateFirewallPort(nil, addPorts)\n\t}()\n\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\twafDataPath := path.Join(nginxInstall.GetPath(), \"1pwaf\", \"data\")\n\tfileOp := files.NewFileOp()\n\tif fileOp.Stat(wafDataPath) {\n\t\twebsitesConfigPath := path.Join(wafDataPath, \"conf\", \"sites.json\")\n\t\tcontent, err := fileOp.GetContent(websitesConfigPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar websitesArray []request.WafWebsite\n\t\tif content != nil {\n\t\t\tif err := json.Unmarshal(content, &websitesArray); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tfor index, wafWebsite := range websitesArray {\n\t\t\tif wafWebsite.Key == website.Alias {\n\t\t\t\twafSite := request.WafWebsite{\n\t\t\t\t\tKey:     website.Alias,\n\t\t\t\t\tDomains: wafWebsite.Domains,\n\t\t\t\t\tHost:    wafWebsite.Host,\n\t\t\t\t}\n\t\t\t\tfor _, domain := range domainModels {\n\t\t\t\t\twafSite.Domains = append(wafSite.Domains, domain.Domain)\n\t\t\t\t\twafSite.Host = append(wafSite.Host, domain.Domain+\":\"+strconv.Itoa(domain.Port))\n\t\t\t\t}\n\t\t\t\tif len(wafSite.Host) == 0 {\n\t\t\t\t\twafSite.Host = []string{}\n\t\t\t\t}\n\t\t\t\twebsitesArray[index] = wafSite\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\twebsitesContent, err := json.Marshal(websitesArray)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif err = addListenAndServerName(website, domainModels); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn domainModels, websiteDomainRepo.BatchCreate(context.TODO(), domainModels)\n}\n\nfunc (w WebsiteService) GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error) {\n\treturn websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteId))\n}\n\nfunc (w WebsiteService) DeleteWebsiteDomain(domainId uint) error {\n\twebSiteDomain, err := websiteDomainRepo.GetFirst(repo.WithByID(domainId))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID)); len(websiteDomains) == 1 {\n\t\treturn fmt.Errorf(\"can not delete last domain\")\n\t}\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(webSiteDomain.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar ports []int\n\tif oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithPort(webSiteDomain.Port)); len(oldDomains) == 1 {\n\t\tports = append(ports, webSiteDomain.Port)\n\t}\n\n\tvar domains []string\n\tif oldDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(webSiteDomain.WebsiteID), websiteDomainRepo.WithDomain(webSiteDomain.Domain)); len(oldDomains) == 1 {\n\t\tdomains = append(domains, webSiteDomain.Domain)\n\t}\n\n\tif len(ports) > 0 || len(domains) > 0 {\n\t\tstringBinds := make([]string, len(ports))\n\t\tfor i := 0; i < len(ports); i++ {\n\t\t\tstringBinds[i] = strconv.Itoa(ports[i])\n\t\t}\n\t\tif err := deleteListenAndServerName(website, stringBinds, domains); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\twafDataPath := path.Join(nginxInstall.GetPath(), \"1pwaf\", \"data\")\n\tfileOp := files.NewFileOp()\n\tif fileOp.Stat(wafDataPath) {\n\t\twebsitesConfigPath := path.Join(wafDataPath, \"conf\", \"sites.json\")\n\t\tcontent, err := fileOp.GetContent(websitesConfigPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar websitesArray []request.WafWebsite\n\t\tvar newWebsitesArray []request.WafWebsite\n\t\tif content != nil {\n\t\t\tif err := json.Unmarshal(content, &websitesArray); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tfor _, wafWebsite := range websitesArray {\n\t\t\tif wafWebsite.Key == website.Alias {\n\t\t\t\twafSite := wafWebsite\n\t\t\t\toldDomains := wafSite.Domains\n\t\t\t\tvar newDomains []string\n\t\t\t\tfor _, domain := range oldDomains {\n\t\t\t\t\tif domain == webSiteDomain.Domain {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tnewDomains = append(newDomains, domain)\n\t\t\t\t}\n\t\t\t\twafSite.Domains = newDomains\n\t\t\t\toldHostArray := wafSite.Host\n\t\t\t\tvar newHostArray []string\n\t\t\t\tfor _, host := range oldHostArray {\n\t\t\t\t\tif host == webSiteDomain.Domain+\":\"+strconv.Itoa(webSiteDomain.Port) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tnewHostArray = append(newHostArray, host)\n\t\t\t\t}\n\t\t\t\twafSite.Host = newHostArray\n\t\t\t\tif len(wafSite.Host) == 0 {\n\t\t\t\t\twafSite.Host = []string{}\n\t\t\t\t}\n\t\t\t\tnewWebsitesArray = append(newWebsitesArray, wafSite)\n\t\t\t} else {\n\t\t\t\tnewWebsitesArray = append(newWebsitesArray, wafWebsite)\n\t\t\t}\n\t\t}\n\t\twebsitesContent, err := json.Marshal(newWebsitesArray)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn websiteDomainRepo.DeleteBy(context.TODO(), repo.WithByID(domainId))\n}\n"
  },
  {
    "path": "agent/app/service/website_lb.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n)\n\nfunc (w WebsiteService) GetLoadBalances(id uint) ([]dto.NginxUpstream, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tincludeDir := GetSitePath(website, SiteUpstreamDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(includeDir) {\n\t\treturn nil, nil\n\t}\n\tentries, err := os.ReadDir(includeDir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []dto.NginxUpstream\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tname := entry.Name()\n\t\tif !strings.HasSuffix(name, \".conf\") {\n\t\t\tcontinue\n\t\t}\n\t\tupstreamName := strings.TrimSuffix(name, \".conf\")\n\t\tupstream := dto.NginxUpstream{\n\t\t\tName: upstreamName,\n\t\t}\n\t\tupstreamPath := path.Join(includeDir, name)\n\t\tcontent, err := fileOp.GetContent(upstreamPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tupstream.Content = string(content)\n\t\tnginxParser, err := parser.NewParser(upstreamPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tconfig, err := nginxParser.Parse()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tupstreams := config.FindUpstreams()\n\t\tfor _, up := range upstreams {\n\t\t\tif up.UpstreamName == upstreamName {\n\t\t\t\tdirectives := up.GetDirectives()\n\t\t\t\tfor _, d := range directives {\n\t\t\t\t\tdName := d.GetName()\n\t\t\t\t\tif _, ok := dto.LBAlgorithms[dName]; ok {\n\t\t\t\t\t\tupstream.Algorithm = dName\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tupstream.Servers = getNginxUpstreamServers(up.UpstreamServers)\n\t\t\t}\n\t\t}\n\t\tres = append(res, upstream)\n\t}\n\treturn res, nil\n}\n\nfunc (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tincludeDir := GetSitePath(website, SiteUpstreamDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(includeDir) {\n\t\t_ = fileOp.CreateDir(includeDir, constant.DirPerm)\n\t}\n\tsafeName := path.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn buserr.New(\"ErrInvalidParams\")\n\t}\n\tfilePath := path.Join(includeDir, fmt.Sprintf(\"%s.conf\", req.Name))\n\tif fileOp.Stat(filePath) {\n\t\treturn buserr.New(\"ErrNameIsExist\")\n\t}\n\tconfig, err := parser.NewStringParser(string(nginx_conf.Upstream)).Parse()\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfig.Block = &components.Block{}\n\tconfig.FilePath = filePath\n\tupstream := components.Upstream{\n\t\tUpstreamName: req.Name,\n\t}\n\tif req.Algorithm != \"default\" {\n\t\tupstream.UpdateDirective(req.Algorithm, []string{})\n\t}\n\tupstream.UpstreamServers = parseUpstreamServers(req.Servers)\n\tconfig.Block.Directives = append(config.Block.Directives, &upstream)\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = fileOp.DeleteFile(filePath)\n\t\t}\n\t}()\n\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn buserr.WithErr(\"ErrUpdateBuWebsite\", err)\n\t}\n\tnginxInclude := fmt.Sprintf(\"/www/sites/%s/upstream/*.conf\", website.Alias)\n\tif err = updateNginxConfig(\"\", []dto.NginxParam{{Name: \"include\", Params: []string{nginxInclude}}}, &website); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tincludeDir := GetSitePath(website, SiteUpstreamDir)\n\tfileOp := files.NewFileOp()\n\tsafeName := path.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn buserr.New(\"ErrInvalidParams\")\n\t}\n\tfilePath := path.Join(includeDir, fmt.Sprintf(\"%s.conf\", req.Name))\n\tif !fileOp.Stat(filePath) {\n\t\treturn nil\n\t}\n\toldContent, err := fileOp.GetContent(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tparser, err := parser.NewParser(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfig, err := parser.Parse()\n\tif err != nil {\n\t\treturn err\n\t}\n\tupstreams := config.FindUpstreams()\n\tfor _, up := range upstreams {\n\t\tif up.UpstreamName == req.Name {\n\t\t\tdirectives := up.GetDirectives()\n\t\t\tfor _, d := range directives {\n\t\t\t\tdName := d.GetName()\n\t\t\t\tif _, ok := dto.LBAlgorithms[dName]; ok {\n\t\t\t\t\tup.RemoveDirective(dName, nil)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif req.Algorithm != \"default\" {\n\t\t\t\tup.UpdateDirective(req.Algorithm, []string{})\n\t\t\t}\n\t\t\tup.UpstreamServers = parseUpstreamServers(req.Servers)\n\t\t}\n\t}\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn buserr.WithErr(\"ErrUpdateBuWebsite\", err)\n\t}\n\treturn nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName)\n}\n\nfunc (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tproxies, _ := w.GetProxies(website.ID)\n\tif len(proxies) > 0 {\n\t\tfor _, proxy := range proxies {\n\t\t\tif strings.HasSuffix(proxy.ProxyPass, fmt.Sprintf(\"://%s\", req.Name)) {\n\t\t\t\treturn buserr.New(\"ErrProxyIsUsed\")\n\t\t\t}\n\t\t}\n\t}\n\n\tincludeDir := GetSitePath(website, SiteUpstreamDir)\n\tfileOp := files.NewFileOp()\n\tsafeName := path.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn buserr.New(\"ErrInvalidParams\")\n\t}\n\tfilePath := path.Join(includeDir, fmt.Sprintf(\"%s.conf\", req.Name))\n\tif !fileOp.Stat(filePath) {\n\t\treturn nil\n\t}\n\tif err = fileOp.DeleteFile(filePath); err != nil {\n\t\treturn err\n\t}\n\treturn opNginx(nginxInstall.ContainerName, constant.NginxReload)\n}\n\nfunc (w WebsiteService) UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tincludeDir := GetSitePath(website, SiteUpstreamDir)\n\tsafeName := path.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn buserr.New(\"ErrInvalidParams\")\n\t}\n\tfilePath := path.Join(includeDir, fmt.Sprintf(\"%s.conf\", req.Name))\n\tfileOp := files.NewFileOp()\n\toldContent, err := fileOp.GetContent(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = fileOp.WriteFile(filePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = fileOp.WriteFile(filePath, bytes.NewReader(oldContent), constant.DirPerm)\n\t\t}\n\t}()\n\treturn opNginx(nginxInstall.ContainerName, constant.NginxReload)\n}\n"
  },
  {
    "path": "agent/app/service/website_op.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"sort\"\n)\n\nfunc (w WebsiteService) BatchOpWebsite(req request.BatchWebsiteOp) error {\n\twebsites, _ := websiteRepo.List(repo.WithByIDs(req.IDs))\n\topTask, err := task.NewTaskWithOps(i18n.GetMsgByKey(\"Status\"), task.TaskBatch, task.TaskScopeWebsite, req.TaskID, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsort.SliceStable(websites, func(i, j int) bool {\n\t\tif websites[i].Type == constant.Subsite && websites[j].Type != constant.Subsite {\n\t\t\treturn true\n\t\t}\n\t\tif websites[i].Type != constant.Subsite && websites[j].Type == constant.Subsite {\n\t\t\treturn false\n\t\t}\n\t\treturn false\n\t})\n\topWebsiteTask := func(t *task.Task) error {\n\t\tfor _, web := range websites {\n\t\t\tmsg := fmt.Sprintf(\"%s %s\", i18n.GetMsgByKey(req.Operate), web.PrimaryDomain)\n\t\t\tswitch req.Operate {\n\t\t\tcase constant.StopWeb, constant.StartWeb:\n\t\t\t\tif err := opWebsite(&web, req.Operate); err != nil {\n\t\t\t\t\tt.LogFailedWithErr(msg, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t_ = websiteRepo.Save(context.Background(), &web)\n\t\t\tcase \"delete\":\n\t\t\t\tif err := w.DeleteWebsite(request.WebsiteDelete{\n\t\t\t\t\tID: web.ID,\n\t\t\t\t}); err != nil {\n\t\t\t\t\tt.LogFailedWithErr(msg, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tt.LogSuccess(msg)\n\t\t}\n\t\treturn nil\n\t}\n\topTask.AddSubTask(\"\", opWebsiteTask, nil)\n\n\tgo func() {\n\t\t_ = opTask.Execute()\n\t}()\n\treturn nil\n}\n\nfunc (w WebsiteService) BatchSetGroup(req request.BatchWebsiteGroup) error {\n\twebsites, _ := websiteRepo.List(repo.WithByIDs(req.IDs))\n\tfor _, web := range websites {\n\t\tweb.WebsiteGroupID = req.GroupID\n\t\tif err := websiteRepo.Save(context.Background(), &web); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteService) BatchSetHttps(ctx context.Context, req request.BatchWebsiteHttps) error {\n\twebsites, _ := websiteRepo.List(repo.WithByIDs(req.IDs))\n\topTask, err := task.NewTaskWithOps(i18n.GetMsgByKey(\"SSL\"), task.TaskBatch, task.TaskScopeWebsite, req.TaskID, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\twebsiteHttpsOp := request.WebsiteHTTPSOp{\n\t\tEnable:                true,\n\t\tWebsiteSSLID:          req.WebsiteSSLID,\n\t\tType:                  req.Type,\n\t\tPrivateKey:            req.PrivateKey,\n\t\tCertificate:           req.Certificate,\n\t\tPrivateKeyPath:        req.PrivateKeyPath,\n\t\tCertificatePath:       req.CertificatePath,\n\t\tImportType:            req.ImportType,\n\t\tHttpConfig:            req.HttpConfig,\n\t\tSSLProtocol:           req.SSLProtocol,\n\t\tAlgorithm:             req.Algorithm,\n\t\tHsts:                  req.Hsts,\n\t\tHstsIncludeSubDomains: req.HstsIncludeSubDomains,\n\t\tHttpsPorts:            req.HttpsPorts,\n\t\tHttp3:                 req.Http3,\n\t}\n\tif req.Type == constant.SSLManual {\n\t\twebsiteSSL, err := getManualWebsiteSSL(websiteHttpsOp)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = websiteSSLRepo.Create(ctx, &websiteSSL); err != nil {\n\t\t\treturn err\n\t\t}\n\t\twebsiteHttpsOp.Type = constant.SSLExisted\n\t\twebsiteHttpsOp.WebsiteSSLID = websiteSSL.ID\n\t}\n\topWebsiteTask := func(t *task.Task) error {\n\t\tfor _, web := range websites {\n\t\t\tif web.Type == constant.Stream {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\twebsiteHttpsOp.WebsiteID = web.ID\n\t\t\tmsg := fmt.Sprintf(\"%s [%s] %s\", i18n.GetMsgByKey(\"Set\"), web.PrimaryDomain, i18n.GetMsgByKey(\"SSL\"))\n\t\t\tif _, err := w.OpWebsiteHTTPS(ctx, websiteHttpsOp); err != nil {\n\t\t\t\tt.LogFailedWithErr(msg, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tt.LogSuccess(msg)\n\t\t}\n\t\treturn nil\n\t}\n\n\topTask.AddSubTask(\"\", opWebsiteTask, nil)\n\tgo func() {\n\t\t_ = opTask.Execute()\n\t}()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/website_proxy.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n)\n\nfunc (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error) {\n\tswitch req.Operate {\n\tcase \"delete\":\n\t\treturn w.DeleteProxy(request.WebsiteProxyDel{\n\t\t\tID:   req.ID,\n\t\t\tName: req.Name,\n\t\t})\n\tcase \"disable\":\n\t\tfallthrough\n\tcase \"enable\":\n\t\treturn w.UpdateProxyStatus(request.WebsiteProxyStatusUpdate{\n\t\t\tID:     req.ID,\n\t\t\tName:   req.Name,\n\t\t\tStatus: req.Operate,\n\t\t})\n\t}\n\tvar (\n\t\twebsite    model.Website\n\t\tpar        *parser.Parser\n\t\toldContent []byte\n\t)\n\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn\n\t}\n\tfileOp := files.NewFileOp()\n\tincludeDir := GetSitePath(website, SiteProxyDir)\n\tif !fileOp.Stat(includeDir) {\n\t\t_ = fileOp.CreateDir(includeDir, constant.DirPerm)\n\t}\n\tfileName := fmt.Sprintf(\"%s.conf\", req.Name)\n\tincludePath := path.Join(includeDir, fileName)\n\tbackName := fmt.Sprintf(\"%s.bak\", req.Name)\n\tbackPath := path.Join(includeDir, backName)\n\n\tif req.Operate == \"create\" && (fileOp.Stat(includePath) || fileOp.Stat(backPath)) {\n\t\terr = buserr.New(\"ErrNameIsExist\")\n\t\treturn\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tswitch req.Operate {\n\t\t\tcase \"create\":\n\t\t\t\t_ = fileOp.DeleteFile(includePath)\n\t\t\tcase \"edit\":\n\t\t\t\t_ = fileOp.WriteFile(includePath, bytes.NewReader(oldContent), constant.DirPerm)\n\t\t\t}\n\t\t}\n\t}()\n\n\tvar config *components.Config\n\n\tswitch req.Operate {\n\tcase \"create\":\n\t\tconfig, err = parser.NewStringParser(string(nginx_conf.GetWebsiteFile(\"proxy.conf\"))).Parse()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase \"edit\":\n\t\tpar, err = parser.NewParser(includePath)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tconfig, err = par.Parse()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\toldContent, err = fileOp.GetContent(includePath)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tdefault:\n\t\treturn errors.New(\"unknown operate\")\n\t}\n\n\tconfig.FilePath = includePath\n\tdirectives := config.Directives\n\n\tvar location *components.Location\n\tfor _, directive := range directives {\n\t\tif loc, ok := directive.(*components.Location); ok {\n\t\t\tlocation = loc\n\t\t\tbreak\n\t\t}\n\t}\n\tif location == nil {\n\t\terr = errors.New(\"invalid proxy config, no location found\")\n\t\treturn\n\t}\n\tapplyLocationProxyPass(location, req.ProxyPass, &req.SNI, req.ProxySSLName)\n\tlocation.UpdateDirective(\"proxy_set_header\", []string{\"Host\", req.ProxyHost})\n\tlocation.ChangePath(req.Modifier, req.Match)\n\t// Server Cache Settings\n\tif req.Cache {\n\t\tif err = openProxyCache(website); err != nil {\n\t\t\treturn\n\t\t}\n\t\tlocation.AddServerCache(fmt.Sprintf(\"proxy_cache_zone_of_%s\", website.Alias), req.ServerCacheTime, req.ServerCacheUnit)\n\t} else {\n\t\tlocation.RemoveServerCache(fmt.Sprintf(\"proxy_cache_zone_of_%s\", website.Alias))\n\t}\n\t// Browser Cache Settings\n\t// cacheTime > 0: enable expires;\n\t// cacheTime == 0: use upstream cache-control, remove self-set;\n\t// cacheTime < 0: force no-cache\n\tif req.CacheTime > 0 {\n\t\tlocation.AddBrowserCache(req.CacheTime, req.CacheUnit)\n\t} else if req.CacheTime < 0 {\n\t\tlocation.AddBroswerNoCache()\n\t} else {\n\t\tlocation.RemoveBrowserCache()\n\t}\n\t// Content Replace Settings\n\tif len(req.Replaces) > 0 {\n\t\tlocation.AddSubFilter(req.Replaces)\n\t} else {\n\t\tlocation.RemoveSubFilter()\n\t}\n\t// Explicit SNI configuration still takes precedence over the automatic HTTPS upstream default.\n\t// CORS Settings\n\tif req.Cors {\n\t\tlocation.UpdateDirective(\"add_header\", []string{\"Access-Control-Allow-Origin\", req.AllowOrigins, \"always\"})\n\t\tif req.AllowMethods != \"\" {\n\t\t\tlocation.UpdateDirective(\"add_header\", []string{\"Access-Control-Allow-Methods\", req.AllowMethods, \"always\"})\n\t\t} else {\n\t\t\tlocation.RemoveDirective(\"add_header\", []string{\"Access-Control-Allow-Methods\"})\n\t\t}\n\t\tif req.AllowHeaders != \"\" {\n\t\t\tlocation.UpdateDirective(\"add_header\", []string{\"Access-Control-Allow-Headers\", req.AllowHeaders, \"always\"})\n\t\t} else {\n\t\t\tlocation.RemoveDirective(\"add_header\", []string{\"Access-Control-Allow-Headers\"})\n\t\t}\n\t\tif req.AllowCredentials {\n\t\t\tlocation.UpdateDirective(\"add_header\", []string{\"Access-Control-Allow-Credentials\", \"true\", \"always\"})\n\t\t} else {\n\t\t\tlocation.RemoveDirective(\"add_header\", []string{\"Access-Control-Allow-Credentials\"})\n\t\t}\n\t\tif req.Preflight {\n\t\t\tlocation.AddCorsOption()\n\t\t} else {\n\t\t\tlocation.RemoveCorsOption()\n\t\t}\n\t} else {\n\t\tlocation.RemoveDirective(\"add_header\", []string{\"Access-Control-Allow-Origin\"})\n\t\tlocation.RemoveDirective(\"add_header\", []string{\"Access-Control-Allow-Methods\"})\n\t\tlocation.RemoveDirective(\"add_header\", []string{\"Access-Control-Allow-Headers\"})\n\t\tlocation.RemoveDirective(\"add_header\", []string{\"Access-Control-Allow-Credentials\"})\n\t\tlocation.RemoveDirectiveByFullParams(\"if\", []string{\"(\", \"$request_method\", \"=\", \"'OPTIONS'\", \")\"})\n\t}\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn buserr.WithErr(\"ErrUpdateBuWebsite\", err)\n\t}\n\tnginxInclude := fmt.Sprintf(\"/www/sites/%s/proxy/*.conf\", website.Alias)\n\treturn updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: \"include\", Params: []string{nginxInclude}}}, &website)\n}\n\nfunc (w WebsiteService) UpdateProxyCache(req request.NginxProxyCacheUpdate) (err error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn\n\t}\n\tcacheDir := GetSitePath(website, SiteCacheDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(cacheDir) {\n\t\t_ = fileOp.CreateDir(cacheDir, constant.DirPerm)\n\t}\n\tif req.Open {\n\t\tproxyCachePath := fmt.Sprintf(\"/www/sites/%s/cache levels=1:2 keys_zone=proxy_cache_zone_of_%s:%d%s max_size=%d%s inactive=%d%s\", website.Alias, website.Alias, req.ShareCache, req.ShareCacheUnit, req.CacheLimit, req.CacheLimitUnit, req.CacheExpire, req.CacheExpireUnit)\n\t\treturn updateNginxConfig(\"\", []dto.NginxParam{{Name: \"proxy_cache_path\", Params: []string{proxyCachePath}}}, &website)\n\t}\n\treturn deleteNginxConfig(\"\", []dto.NginxParam{{Name: \"proxy_cache_path\"}}, &website)\n}\n\nfunc (w WebsiteService) GetProxyCache(id uint) (res response.NginxProxyCache, err error) {\n\tvar (\n\t\twebsite model.Website\n\t)\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn\n\t}\n\n\tparser, err := parser.NewParser(GetSitePath(website, SiteConf))\n\tif err != nil {\n\t\treturn\n\t}\n\tconfig, err := parser.Parse()\n\tif err != nil {\n\t\treturn\n\t}\n\tvar params []string\n\tfor _, d := range config.GetDirectives() {\n\t\tif d.GetName() == \"proxy_cache_path\" {\n\t\t\tparams = d.GetParameters()\n\t\t}\n\t}\n\tif len(params) == 0 {\n\t\treturn\n\t}\n\tfor _, param := range params {\n\t\tif re.GetRegex(re.ProxyCacheZonePattern).MatchString(param) {\n\t\t\tmatches := re.GetRegex(re.ProxyCacheZonePattern).FindStringSubmatch(param)\n\t\t\tif len(matches) > 0 {\n\t\t\t\tres.ShareCache, _ = strconv.Atoi(matches[1])\n\t\t\t\tres.ShareCacheUnit = matches[2]\n\t\t\t}\n\t\t}\n\n\t\tif re.GetRegex(re.ProxyCacheMaxSizeValidationPattern).MatchString(param) {\n\t\t\tmatches := re.GetRegex(re.ProxyCacheMaxSizePattern).FindStringSubmatch(param)\n\t\t\tif len(matches) > 0 {\n\t\t\t\tres.CacheLimit, _ = strconv.ParseFloat(matches[1], 64)\n\t\t\t\tres.CacheLimitUnit = matches[2]\n\t\t\t}\n\t\t}\n\t\tif re.GetRegex(re.ProxyCacheInactivePattern).MatchString(param) {\n\t\t\tmatches := re.GetRegex(re.ProxyCacheInactivePattern).FindStringSubmatch(param)\n\t\t\tif len(matches) > 0 {\n\t\t\t\tres.CacheExpire, _ = strconv.Atoi(matches[1])\n\t\t\t\tres.CacheExpireUnit = matches[2]\n\t\t\t}\n\t\t}\n\t}\n\tres.Open = true\n\treturn\n}\n\nfunc (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, err error) {\n\tvar (\n\t\twebsite  model.Website\n\t\tfileList response.FileInfo\n\t)\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn\n\t}\n\tincludeDir := GetSitePath(website, SiteProxyDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(includeDir) {\n\t\treturn\n\t}\n\tfileList, err = NewIFileService().GetFileList(request.FileOption{FileOption: files.FileOption{Path: includeDir, Expand: true, Page: 1, PageSize: 100}})\n\tif len(fileList.Items) == 0 {\n\t\treturn\n\t}\n\tvar (\n\t\tcontent []byte\n\t\tconfig  *components.Config\n\t)\n\tfor _, configFile := range fileList.Items {\n\t\tproxyConfig := request.WebsiteProxyConfig{\n\t\t\tID: website.ID,\n\t\t}\n\t\tparts := strings.Split(configFile.Name, \".\")\n\t\tproxyConfig.Name = parts[0]\n\t\tif parts[1] == \"conf\" {\n\t\t\tproxyConfig.Enable = true\n\t\t} else {\n\t\t\tproxyConfig.Enable = false\n\t\t}\n\t\tproxyConfig.FilePath = configFile.Path\n\t\tcontent, err = fileOp.GetContent(configFile.Path)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tproxyConfig.Content = string(content)\n\t\tconfig, err = parser.NewStringParser(string(content)).Parse()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdirectives := config.GetDirectives()\n\n\t\tvar location *components.Location\n\t\tfor _, directive := range directives {\n\t\t\tif loc, ok := directive.(*components.Location); ok {\n\t\t\t\tlocation = loc\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif location == nil {\n\t\t\terr = errors.New(\"invalid proxy config, no location found\")\n\t\t\treturn\n\t\t}\n\t\tproxyConfig.ProxyPass = location.ProxyPass\n\t\tproxyConfig.Cache = location.Cache\n\t\tif location.CacheTime > 0 {\n\t\t\tproxyConfig.CacheTime = location.CacheTime\n\t\t\tproxyConfig.CacheUnit = location.CacheUint\n\t\t}\n\t\tif location.ServerCacheTime > 0 {\n\t\t\tproxyConfig.ServerCacheTime = location.ServerCacheTime\n\t\t\tproxyConfig.ServerCacheUnit = location.ServerCacheUint\n\t\t}\n\t\tproxyConfig.Match = location.Match\n\t\tproxyConfig.Modifier = location.Modifier\n\t\tproxyConfig.ProxyHost = location.Host\n\t\tproxyConfig.Replaces = location.Replaces\n\t\tfor _, directive := range location.Directives {\n\t\t\tif directive.GetName() == \"proxy_ssl_server_name\" {\n\t\t\t\tproxyConfig.SNI = directive.GetParameters()[0] == \"on\"\n\t\t\t}\n\t\t\tif directive.GetName() == \"proxy_ssl_name\" && len(directive.GetParameters()) > 0 {\n\t\t\t\tproxyConfig.ProxySSLName = directive.GetParameters()[0]\n\t\t\t}\n\t\t}\n\t\tproxyConfig.Cors = location.Cors\n\t\tproxyConfig.AllowCredentials = location.AllowCredentials\n\t\tproxyConfig.AllowHeaders = location.AllowHeaders\n\t\tproxyConfig.AllowOrigins = location.AllowOrigins\n\t\tproxyConfig.AllowMethods = location.AllowMethods\n\t\tproxyConfig.Preflight = location.Preflight\n\t\tres = append(res, proxyConfig)\n\t}\n\treturn\n}\n\nfunc (w WebsiteService) UpdateProxyFile(req request.NginxProxyUpdate) (err error) {\n\tvar (\n\t\twebsite           model.Website\n\t\toldRewriteContent []byte\n\t)\n\twebsite, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tabsolutePath := fmt.Sprintf(\"%s/%s.conf\", GetSitePath(website, SiteProxyDir), req.Name)\n\tfileOp := files.NewFileOp()\n\toldRewriteContent, err = fileOp.GetContent(absolutePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm)\n\t\t}\n\t}()\n\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n}\n\nfunc (w WebsiteService) ClearProxyCache(req request.NginxCommonReq) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tcacheDir := GetSitePath(website, SiteCacheDir)\n\tfileOp := files.NewFileOp()\n\tif fileOp.Stat(cacheDir) {\n\t\tif err = fileOp.CleanDir(cacheDir); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteService) DeleteProxy(req request.WebsiteProxyDel) (err error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn\n\t}\n\tincludeDir := GetSitePath(website, SiteProxyDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(includeDir) {\n\t\treturn\n\t}\n\tfileName := fmt.Sprintf(\"%s.conf\", req.Name)\n\tincludePath := path.Join(includeDir, fileName)\n\tbackName := fmt.Sprintf(\"%s.bak\", req.Name)\n\tbackPath := path.Join(includeDir, backName)\n\n\t_ = fileOp.DeleteFile(includePath)\n\t_ = fileOp.DeleteFile(backPath)\n\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n}\n\nfunc (w WebsiteService) UpdateProxyStatus(req request.WebsiteProxyStatusUpdate) (err error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.ID))\n\tif err != nil {\n\t\treturn\n\t}\n\tincludeDir := GetSitePath(website, SiteProxyDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(includeDir) {\n\t\treturn\n\t}\n\tfileName := fmt.Sprintf(\"%s.conf\", req.Name)\n\tincludePath := path.Join(includeDir, fileName)\n\tbackName := fmt.Sprintf(\"%s.bak\", req.Name)\n\tbackPath := path.Join(includeDir, backName)\n\n\tswitch req.Status {\n\tcase \"disable\":\n\t\t_ = fileOp.Rename(includePath, backPath)\n\t\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n\tcase \"enable\":\n\t\t_ = fileOp.Rename(backPath, includePath)\n\t\treturn updateNginxConfig(constant.NginxScopeServer, nil, &website)\n\t}\n\treturn errors.New(\"unknown status\")\n}\n"
  },
  {
    "path": "agent/app/service/website_rewrite.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\nfunc (w WebsiteService) UpdateRewriteConfig(req request.NginxRewriteUpdate) error {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tincludePath := fmt.Sprintf(\"/www/sites/%s/rewrite/%s.conf\", website.Alias, website.Alias)\n\tabsolutePath := GetSitePath(website, SiteReWritePath)\n\tfileOp := files.NewFileOp()\n\tvar oldRewriteContent []byte\n\tif !fileOp.Stat(path.Dir(absolutePath)) {\n\t\tif err := fileOp.CreateDir(path.Dir(absolutePath), constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif !fileOp.Stat(absolutePath) {\n\t\tif err := fileOp.CreateFile(absolutePath); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\toldRewriteContent, err = fileOp.GetContent(absolutePath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\n\tif err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: \"include\", Params: []string{includePath}}}, &website); err != nil {\n\t\t_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm)\n\t\treturn err\n\t}\n\twebsite.Rewrite = req.Name\n\treturn websiteRepo.Save(context.Background(), &website)\n}\n\nfunc (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error) {\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar contentByte []byte\n\tif req.Name == \"current\" {\n\t\trewriteConfPath := GetSitePath(website, SiteReWritePath)\n\t\tfileOp := files.NewFileOp()\n\t\tif fileOp.Stat(rewriteConfPath) {\n\t\t\tcontentByte, err = fileOp.GetContent(rewriteConfPath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t} else {\n\t\trewriteFile := fmt.Sprintf(\"rewrite/%s.conf\", strings.ToLower(req.Name))\n\t\tcontentByte, _ = nginx_conf.Rewrites.ReadFile(rewriteFile)\n\t\tif contentByte == nil {\n\t\t\tcustomRewriteDir := GetOpenrestyDir(DefaultRewriteDir)\n\t\t\tsafeName := path.Base(req.Name)\n\t\t\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\t\t\treturn nil, buserr.New(\"ErrInvalidParams\")\n\t\t\t}\n\t\t\tcustomRewriteFile := path.Join(customRewriteDir, fmt.Sprintf(\"%s.conf\", strings.ToLower(req.Name)))\n\t\t\tcontentByte, err = files.NewFileOp().GetContent(customRewriteFile)\n\t\t}\n\t}\n\treturn &response.NginxRewriteRes{\n\t\tContent: string(contentByte),\n\t}, err\n}\n\nfunc (w WebsiteService) OperateCustomRewrite(req request.CustomRewriteOperate) error {\n\trewriteDir := GetOpenrestyDir(DefaultRewriteDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(rewriteDir) {\n\t\tif err := fileOp.CreateDir(rewriteDir, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tsafeName := path.Base(req.Name)\n\tif safeName != req.Name || strings.Contains(safeName, \"..\") {\n\t\treturn buserr.New(\"ErrInvalidParams\")\n\t}\n\trewriteFile := path.Join(rewriteDir, fmt.Sprintf(\"%s.conf\", req.Name))\n\tswitch req.Operate {\n\tcase \"create\":\n\t\tif fileOp.Stat(rewriteFile) {\n\t\t\treturn buserr.New(\"ErrNameIsExist\")\n\t\t}\n\t\treturn fileOp.WriteFile(rewriteFile, strings.NewReader(req.Content), constant.DirPerm)\n\tcase \"delete\":\n\t\treturn fileOp.DeleteFile(rewriteFile)\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteService) ListCustomRewrite() ([]string, error) {\n\trewriteDir := GetOpenrestyDir(DefaultRewriteDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(rewriteDir) {\n\t\treturn nil, nil\n\t}\n\tentries, err := os.ReadDir(rewriteDir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []string\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tres = append(res, strings.TrimSuffix(entry.Name(), \".conf\"))\n\t}\n\treturn res, nil\n}\n"
  },
  {
    "path": "agent/app/service/website_ssl.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"github.com/go-acme/lego/v4/certificate\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/req_helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ssl\"\n\tlegoLogger \"github.com/go-acme/lego/v4/log\"\n\t\"github.com/jinzhu/gorm\"\n)\n\ntype WebsiteSSLService struct {\n}\n\ntype IWebsiteSSLService interface {\n\tPage(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error)\n\tGetSSL(id uint) (*response.WebsiteSSLDTO, error)\n\tSearch(req request.WebsiteSSLListReq) ([]response.WebsiteSSLDTO, error)\n\tCreate(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error)\n\tGetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error)\n\tGetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO, error)\n\tDelete(ids []uint) error\n\tUpdate(update request.WebsiteSSLUpdate) error\n\tUpload(req request.WebsiteSSLUpload) error\n\tObtainSSL(apply request.WebsiteSSLApply) error\n\tSyncForRestart() error\n\tDownloadFile(id uint) (*os.File, error)\n\tImportMasterSSL(create model.WebsiteSSL) error\n}\n\nfunc NewIWebsiteSSLService() IWebsiteSSLService {\n\treturn &WebsiteSSLService{}\n}\n\nfunc (w WebsiteSSLService) Page(search request.WebsiteSSLSearch) (int64, []response.WebsiteSSLDTO, error) {\n\tvar (\n\t\tresult []response.WebsiteSSLDTO\n\t\topts   []repo.DBOption\n\t)\n\tif search.OrderBy != \"\" && search.Order != \"null\" {\n\t\topts = append(opts, repo.WithOrderRuleBy(search.OrderBy, search.Order))\n\t} else {\n\t\topts = append(opts, repo.WithOrderDesc(\"created_at\"))\n\t}\n\tif search.Domain != \"\" {\n\t\topts = append(opts, websiteSSLRepo.WithByDomain(search.Domain))\n\t}\n\ttotal, sslList, err := websiteSSLRepo.Page(search.Page, search.PageSize, opts...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tfor _, model := range sslList {\n\t\tresult = append(result, response.WebsiteSSLDTO{\n\t\t\tWebsiteSSL: model,\n\t\t\tLogPath:    path.Join(global.Dir.SSLLogDir, fmt.Sprintf(\"%s-ssl-%d.log\", model.PrimaryDomain, model.ID)),\n\t\t})\n\t}\n\treturn total, result, err\n}\n\nfunc (w WebsiteSSLService) GetSSL(id uint) (*response.WebsiteSSLDTO, error) {\n\tvar res response.WebsiteSSLDTO\n\twebsiteSSL, err := websiteSSLRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tres.WebsiteSSL = *websiteSSL\n\treturn &res, nil\n}\n\nfunc (w WebsiteSSLService) Search(search request.WebsiteSSLListReq) ([]response.WebsiteSSLDTO, error) {\n\tvar (\n\t\topts   []repo.DBOption\n\t\tresult []response.WebsiteSSLDTO\n\t)\n\topts = append(opts, repo.WithOrderDesc(\"created_at\"))\n\tif search.AcmeAccountID != \"\" {\n\t\tacmeAccountID, err := strconv.ParseUint(search.AcmeAccountID, 10, 64)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\topts = append(opts, websiteSSLRepo.WithByAcmeAccountId(uint(acmeAccountID)))\n\t}\n\tsslList, err := websiteSSLRepo.List(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, sslModel := range sslList {\n\t\tresult = append(result, response.WebsiteSSLDTO{\n\t\t\tWebsiteSSL: sslModel,\n\t\t})\n\t}\n\treturn result, err\n}\n\nfunc (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.WebsiteSSLCreate, error) {\n\tif create.Nameserver1 != \"\" && !common.IsValidIP(create.Nameserver1) {\n\t\treturn create, buserr.New(\"ErrParseIP\")\n\t}\n\tif create.Nameserver2 != \"\" && !common.IsValidIP(create.Nameserver2) {\n\t\treturn create, buserr.New(\"ErrParseIP\")\n\t}\n\tvar res request.WebsiteSSLCreate\n\tacmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(create.AcmeAccountID))\n\tif err != nil {\n\t\treturn res, err\n\t}\n\twebsiteSSL := model.WebsiteSSL{\n\t\tStatus:        constant.SSLInit,\n\t\tProvider:      create.Provider,\n\t\tAcmeAccountID: acmeAccount.ID,\n\t\tPrimaryDomain: create.PrimaryDomain,\n\t\tExpireDate:    time.Now(),\n\t\tKeyType:       create.KeyType,\n\t\tPushDir:       create.PushDir,\n\t\tDescription:   create.Description,\n\t\tNameserver1:   create.Nameserver1,\n\t\tNameserver2:   create.Nameserver2,\n\t\tSkipDNS:       create.SkipDNS,\n\t\tDisableCNAME:  create.DisableCNAME,\n\t\tExecShell:     create.ExecShell,\n\t\tIsIp:          create.IsIp,\n\t}\n\tif create.ExecShell {\n\t\twebsiteSSL.Shell = create.Shell\n\t}\n\tif create.PushDir {\n\t\tfileOP := files.NewFileOp()\n\t\tif !fileOP.Stat(create.Dir) {\n\t\t\treturn res, buserr.New(\"ErrLinkPathNotFound\")\n\t\t}\n\t\twebsiteSSL.Dir = create.Dir\n\t}\n\tif create.PushNode && global.IsMaster && len(create.Nodes) > 0 {\n\t\twebsiteSSL.PushNode = true\n\t\twebsiteSSL.Nodes = create.Nodes\n\t}\n\n\tvar domains []string\n\tif create.OtherDomains != \"\" {\n\t\totherDomainArray := strings.Split(create.OtherDomains, \"\\n\")\n\t\tfor _, domain := range otherDomainArray {\n\t\t\tif !common.IsValidDomain(domain) {\n\t\t\t\terr = buserr.WithName(\"ErrDomainFormat\", domain)\n\t\t\t\treturn res, err\n\t\t\t}\n\t\t\tdomains = append(domains, domain)\n\t\t}\n\t}\n\tif create.Provider == constant.Http {\n\t\tif strings.Contains(create.PrimaryDomain, \"*\") {\n\t\t\treturn res, buserr.New(\"ErrWildcardDomain\")\n\t\t}\n\t\tfor _, domain := range domains {\n\t\t\tif strings.Contains(domain, \"*\") {\n\t\t\t\treturn res, buserr.New(\"ErrWildcardDomain\")\n\t\t\t}\n\t\t}\n\t}\n\twebsiteSSL.Domains = strings.Join(domains, \",\")\n\n\tif create.Provider == constant.DNSAccount || create.Provider == constant.Http {\n\t\twebsiteSSL.AutoRenew = create.AutoRenew\n\t}\n\tif create.Provider == constant.DNSAccount {\n\t\tdnsAccount, err := websiteDnsRepo.GetFirst(repo.WithByID(create.DnsAccountID))\n\t\tif err != nil {\n\t\t\treturn res, err\n\t\t}\n\t\twebsiteSSL.DnsAccountID = dnsAccount.ID\n\t}\n\n\tif err := websiteSSLRepo.Create(context.TODO(), &websiteSSL); err != nil {\n\t\treturn res, err\n\t}\n\tcreate.ID = websiteSSL.ID\n\tlogFile, _ := os.OpenFile(path.Join(global.Dir.SSLLogDir, fmt.Sprintf(\"%s-ssl-%d.log\", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\tlogFile.Close()\n\tgo func() {\n\t\tif create.Provider != constant.DnsManual {\n\t\t\tif err = w.ObtainSSL(request.WebsiteSSLApply{\n\t\t\t\tID: websiteSSL.ID,\n\t\t\t}); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"obtain ssl failed, err: %v\", err)\n\t\t\t}\n\t\t}\n\t}()\n\treturn create, nil\n}\n\nfunc printSSLLog(logger *log.Logger, msgKey string, params map[string]interface{}, disableLog bool) {\n\tif disableLog {\n\t\treturn\n\t}\n\tlogger.Println(i18n.GetMsgWithMap(msgKey, params))\n}\n\nfunc reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) {\n\tif !global.IsMaster {\n\t\treturn\n\t}\n\tsystemSSLEnable, sslID := GetSystemSSL()\n\tif systemSSLEnable && sslID == websiteSSL.ID {\n\t\tfileOp := files.NewFileOp()\n\t\tcertPath := path.Join(global.Dir.DataDir, \"secret/server.crt\")\n\t\tkeyPath := path.Join(global.Dir.DataDir, \"secret/server.key\")\n\t\tprintSSLLog(logger, \"StartUpdateSystemSSL\", nil, logger == nil)\n\t\tif err := fileOp.WriteFile(certPath, strings.NewReader(websiteSSL.Pem), 0600); err != nil {\n\t\t\tlogger.Printf(\"Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s\", websiteSSL.PrimaryDomain, err.Error())\n\t\t\treturn\n\t\t}\n\t\tif err := fileOp.WriteFile(keyPath, strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil {\n\t\t\tlogger.Printf(\"Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s\", websiteSSL.PrimaryDomain, err.Error())\n\t\t\treturn\n\t\t}\n\t\tif err := req_helper.PostLocalCore(\"/core/settings/ssl/reload\"); err != nil {\n\t\t\tlogger.Printf(\"Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s\", websiteSSL.PrimaryDomain, err.Error())\n\t\t\treturn\n\t\t}\n\t\tprintSSLLog(logger, \"UpdateSystemSSLSuccess\", nil, logger == nil)\n\t}\n}\n\nfunc (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {\n\tvar (\n\t\terr          error\n\t\twebsiteSSL   *model.WebsiteSSL\n\t\tacmeAccount  *model.WebsiteAcmeAccount\n\t\tdnsAccount   *model.WebsiteDnsAccount\n\t\tclient       *ssl.AcmeClient\n\t\tmanualClient *ssl.ManualClient\n\t\tresource     certificate.Resource\n\t)\n\n\twebsiteSSL, err = websiteSSLRepo.GetFirst(repo.WithByID(apply.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tacmeAccount, err = websiteAcmeRepo.GetFirst(repo.WithByID(websiteSSL.AcmeAccountID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdomains := []string{websiteSSL.PrimaryDomain}\n\tif websiteSSL.Domains != \"\" {\n\t\tdomains = append(domains, strings.Split(websiteSSL.Domains, \",\")...)\n\t}\n\tif websiteSSL.Provider != constant.DnsManual {\n\t\tclient, err = ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tswitch websiteSSL.Provider {\n\t\tcase constant.DNSAccount:\n\t\t\tdnsAccount, err = websiteDnsRepo.GetFirst(repo.WithByID(websiteSSL.DnsAccountID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, *websiteSSL); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase constant.Http:\n\t\t\tappInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\t\t\tif err != nil {\n\t\t\t\tif gorm.IsRecordNotFoundError(err) {\n\t\t\t\t\treturn buserr.New(\"ErrOpenrestyNotFound\")\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, domain := range domains {\n\t\t\t\tif strings.Contains(domain, \"*\") {\n\t\t\t\t\treturn buserr.New(\"ErrWildcardDomain\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err := client.UseHTTP(path.Join(appInstall.GetPath(), \"root\")); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\twebsiteSSL.Status = constant.SSLApply\n\terr = websiteSSLRepo.Save(websiteSSL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tgo func() {\n\t\tlogFile, _ := os.OpenFile(path.Join(global.Dir.SSLLogDir, fmt.Sprintf(\"%s-ssl-%d.log\", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)\n\t\tdefer logFile.Close()\n\t\tlogger := log.New(logFile, \"\", log.LstdFlags)\n\t\tlegoLogger.Logger = logger\n\t\tif !apply.DisableLog {\n\t\t\tstartMsg := i18n.GetMsgWithMap(\"ApplySSLStart\", map[string]interface{}{\"domain\": strings.Join(domains, \",\"), \"type\": i18n.GetMsgByKey(websiteSSL.Provider)})\n\t\t\tif websiteSSL.Provider == constant.DNSAccount {\n\t\t\t\tstartMsg = startMsg + i18n.GetMsgWithMap(\"DNSAccountName\", map[string]interface{}{\"name\": dnsAccount.Name, \"type\": dnsAccount.Type})\n\t\t\t}\n\t\t\tlogger.Println(startMsg)\n\t\t}\n\t\tif websiteSSL.Provider != constant.DnsManual {\n\t\t\tprivateKey, err := ssl.GetPrivateKeyByType(websiteSSL.KeyType, websiteSSL.PrivateKey)\n\t\t\tif err != nil {\n\t\t\t\thandleError(websiteSSL, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif websiteSSL.IsIp {\n\t\t\t\tresource, err = client.ObtainIPSSL(domains[0], privateKey)\n\t\t\t} else {\n\t\t\t\tresource, err = client.ObtainSSL(domains, privateKey)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\thandleError(websiteSSL, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\tmanualClient, err = ssl.NewCustomAcmeClient(acmeAccount, logger)\n\t\t\tif err != nil {\n\t\t\t\thandleError(websiteSSL, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresource, err = manualClient.RequestCertificate(context.Background(), websiteSSL)\n\t\t\tif err != nil {\n\t\t\t\thandleError(websiteSSL, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\twebsiteSSL.PrivateKey = string(resource.PrivateKey)\n\t\twebsiteSSL.Pem = string(resource.Certificate)\n\t\twebsiteSSL.CertURL = resource.CertURL\n\t\tcertBlock, _ := pem.Decode(resource.Certificate)\n\t\tcert, err := x509.ParseCertificate(certBlock.Bytes)\n\t\tif err != nil {\n\t\t\thandleError(websiteSSL, err)\n\t\t\treturn\n\t\t}\n\t\twebsiteSSL.ExpireDate = cert.NotAfter\n\t\twebsiteSSL.StartDate = cert.NotBefore\n\t\twebsiteSSL.Type = cert.Issuer.CommonName\n\t\tif len(cert.Issuer.Organization) > 0 {\n\t\t\twebsiteSSL.Organization = cert.Issuer.Organization[0]\n\t\t}\n\t\twebsiteSSL.Status = constant.SSLReady\n\t\tprintSSLLog(logger, \"ApplySSLSuccess\", map[string]interface{}{\"domain\": strings.Join(domains, \",\")}, apply.DisableLog)\n\t\tsaveCertificateFile(websiteSSL, logger)\n\n\t\tif websiteSSL.ExecShell {\n\t\t\tworkDir := global.Dir.DataDir\n\t\t\tif websiteSSL.PushDir {\n\t\t\t\tworkDir = websiteSSL.Dir\n\t\t\t}\n\t\t\tprintSSLLog(logger, \"ExecShellStart\", nil, apply.DisableLog)\n\t\t\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30*time.Minute), cmd.WithLogger(logger), cmd.WithWorkDir(workDir))\n\t\t\tif err = cmdMgr.RunBashC(websiteSSL.Shell); err != nil {\n\t\t\t\tprintSSLLog(logger, \"ErrExecShell\", map[string]interface{}{\"err\": err.Error()}, apply.DisableLog)\n\t\t\t} else {\n\t\t\t\tprintSSLLog(logger, \"ExecShellSuccess\", nil, apply.DisableLog)\n\t\t\t}\n\t\t}\n\n\t\terr = websiteSSLRepo.Save(websiteSSL)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\twebsites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))\n\t\tif len(websites) > 0 {\n\t\t\tfor _, website := range websites {\n\t\t\t\tprintSSLLog(logger, \"ApplyWebSiteSSLLog\", map[string]interface{}{\"name\": website.PrimaryDomain}, apply.DisableLog)\n\t\t\t\tif err := createPemFile(website, *websiteSSL); err != nil {\n\t\t\t\t\tprintSSLLog(logger, \"ErrUpdateWebsiteSSL\", map[string]interface{}{\"name\": website.PrimaryDomain, \"err\": err.Error()}, apply.DisableLog)\n\t\t\t\t}\n\t\t\t}\n\t\t\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {\n\t\t\t\tprintSSLLog(logger, \"ErrSSLApply\", nil, apply.DisableLog)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tprintSSLLog(logger, \"ApplyWebSiteSSLSuccess\", nil, apply.DisableLog)\n\t\t}\n\t\treloadSystemSSL(websiteSSL, logger)\n\t\tif websiteSSL.PushNode {\n\t\t\tprintSSLLog(logger, \"StartPushSSLToNode\", nil, apply.DisableLog)\n\t\t\tif err = xpack.PushSSLToNode(websiteSSL); err != nil {\n\t\t\t\tprintSSLLog(logger, \"PushSSLToNodeFailed\", map[string]interface{}{\"err\": err.Error()}, apply.DisableLog)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tprintSSLLog(logger, \"PushSSLToNodeSuccess\", nil, apply.DisableLog)\n\t\t}\n\t}()\n\n\treturn nil\n}\n\nfunc handleError(websiteSSL *model.WebsiteSSL, err error) {\n\tif websiteSSL.Status == constant.SSLInit || websiteSSL.Status == constant.SSLError {\n\t\twebsiteSSL.Status = constant.StatusError\n\t} else {\n\t\twebsiteSSL.Status = constant.SSLApplyError\n\t}\n\twebsiteSSL.Message = err.Error()\n\tlegoLogger.Logger.Println(i18n.GetErrMsg(\"ApplySSLFailed\", map[string]interface{}{\"domain\": websiteSSL.PrimaryDomain, \"detail\": err.Error()}))\n\t_ = websiteSSLRepo.Save(websiteSSL)\n}\n\nfunc (w WebsiteSSLService) GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error) {\n\tacmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(req.AcmeAccountID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclient, err := ssl.NewCustomAcmeClient(acmeAccount, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\twebsiteSSL, err := websiteSSLRepo.GetFirst(repo.WithByID(req.WebsiteSSLID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresolves, err := client.GetDNSResolve(context.TODO(), websiteSSL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []response.WebsiteDNSRes\n\tfor k, v := range resolves {\n\t\tres = append(res, response.WebsiteDNSRes{\n\t\t\tDomain: k,\n\t\t\tKey:    v.Key,\n\t\t\tValue:  v.Value,\n\t\t\tErr:    v.Err,\n\t\t})\n\t}\n\treturn res, nil\n}\n\nfunc (w WebsiteSSLService) GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO, error) {\n\tvar res response.WebsiteSSLDTO\n\twebsite, err := websiteRepo.GetFirst(repo.WithByID(websiteId))\n\tif err != nil {\n\t\treturn res, err\n\t}\n\twebsiteSSL, err := websiteSSLRepo.GetFirst(repo.WithByID(website.WebsiteSSLID))\n\tif err != nil {\n\t\treturn res, err\n\t}\n\tres.WebsiteSSL = *websiteSSL\n\treturn res, nil\n}\n\nfunc (w WebsiteSSLService) Delete(ids []uint) error {\n\tvar (\n\t\twebsiteSSLS []string\n\t\tapplySSLS   []string\n\t)\n\tfor _, id := range ids {\n\t\tif websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 {\n\t\t\toldSSL, _ := websiteSSLRepo.GetFirst(repo.WithByID(id))\n\t\t\tif oldSSL.ID > 0 {\n\t\t\t\twebsiteSSLS = append(websiteSSLS, oldSSL.PrimaryDomain)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tsslSetting, _ := settingRepo.Get(settingRepo.WithByKey(\"SSL\"))\n\t\tif sslSetting.Value == \"enable\" {\n\t\t\tsslID, _ := settingRepo.Get(settingRepo.WithByKey(\"SSLID\"))\n\t\t\tidValue, _ := strconv.Atoi(sslID.Value)\n\t\t\tif idValue > 0 && uint(idValue) == id {\n\t\t\t\treturn buserr.New(\"ErrDeleteWithPanelSSL\")\n\t\t\t}\n\t\t}\n\t\twebsiteSSL, err := websiteSSLRepo.GetFirst(repo.WithByID(id))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif websiteSSL.Status == constant.SSLApply {\n\t\t\tapplySSLS = append(applySSLS, websiteSSL.PrimaryDomain)\n\t\t\tcontinue\n\t\t}\n\t\tif websiteSSL.Provider != constant.Manual && websiteSSL.Provider != constant.SelfSigned {\n\t\t\tgo func() {\n\t\t\t\tacmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(websiteSSL.AcmeAccountID))\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"Failed to get acme account for SSL revoke, err: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tclient, err := ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"Failed to create ACME client for SSL revoke, err: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\terr = client.RevokeSSL([]byte(websiteSSL.Pem))\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"Failed to revoke SSL for domain %s, err: %v\", websiteSSL.PrimaryDomain, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\t_ = websiteSSLRepo.DeleteBy(repo.WithByID(id))\n\t}\n\tif len(websiteSSLS) > 0 {\n\t\treturn buserr.WithName(\"ErrSSLCannotDelete\", strings.Join(websiteSSLS, \",\"))\n\t}\n\tif len(applySSLS) > 0 {\n\t\treturn buserr.WithName(\"ErrApplySSLCanNotDelete\", strings.Join(applySSLS, \",\"))\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {\n\twebsiteSSL, err := websiteSSLRepo.GetFirst(repo.WithByID(update.ID))\n\tif err != nil {\n\t\treturn err\n\t}\n\tupdateParams := make(map[string]interface{})\n\tupdateParams[\"primary_domain\"] = update.PrimaryDomain\n\tupdateParams[\"description\"] = update.Description\n\tupdateParams[\"provider\"] = update.Provider\n\tupdateParams[\"push_dir\"] = update.PushDir\n\tupdateParams[\"disable_cname\"] = update.DisableCNAME\n\tupdateParams[\"skip_dns\"] = update.SkipDNS\n\tupdateParams[\"nameserver1\"] = update.Nameserver1\n\tupdateParams[\"nameserver2\"] = update.Nameserver2\n\tupdateParams[\"exec_shell\"] = update.ExecShell\n\tif update.ExecShell {\n\t\tupdateParams[\"shell\"] = update.Shell\n\t} else {\n\t\tupdateParams[\"shell\"] = \"\"\n\t}\n\tif update.PushNode {\n\t\tupdateParams[\"push_node\"] = true\n\t\tupdateParams[\"nodes\"] = update.Nodes\n\t} else {\n\t\tupdateParams[\"push_node\"] = false\n\t\tupdateParams[\"nodes\"] = \"\"\n\t}\n\n\tif websiteSSL.Provider != constant.SelfSigned && websiteSSL.Provider != constant.Manual {\n\t\tacmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(update.AcmeAccountID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tupdateParams[\"acme_account_id\"] = acmeAccount.ID\n\t}\n\n\tif update.PushDir {\n\t\tfileOP := files.NewFileOp()\n\t\tif !fileOP.Stat(update.Dir) {\n\t\t\t_ = fileOP.CreateDir(update.Dir, constant.DirPerm)\n\t\t}\n\t\tupdateParams[\"dir\"] = update.Dir\n\t}\n\tvar domains []string\n\tif update.OtherDomains != \"\" {\n\t\totherDomainArray := strings.Split(update.OtherDomains, \"\\n\")\n\t\tfor _, domain := range otherDomainArray {\n\t\t\tif websiteSSL.Provider != constant.SelfSigned && !common.IsValidDomain(domain) {\n\t\t\t\treturn buserr.WithName(\"ErrDomainFormat\", domain)\n\t\t\t}\n\t\t\tdomains = append(domains, domain)\n\t\t}\n\t}\n\tupdateParams[\"domains\"] = strings.Join(domains, \",\")\n\tif update.Provider == constant.DNSAccount || update.Provider == constant.Http || update.Provider == constant.SelfSigned {\n\t\tupdateParams[\"auto_renew\"] = update.AutoRenew\n\t} else {\n\t\tupdateParams[\"auto_renew\"] = false\n\t}\n\tif update.Provider == constant.DNSAccount {\n\t\tdnsAccount, err := websiteDnsRepo.GetFirst(repo.WithByID(update.DnsAccountID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tupdateParams[\"dns_account_id\"] = dnsAccount.ID\n\t} else {\n\t\tupdateParams[\"dns_account_id\"] = 0\n\t}\n\treturn websiteSSLRepo.SaveByMap(websiteSSL, updateParams)\n}\n\nfunc (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {\n\twebsiteSSL := &model.WebsiteSSL{\n\t\tProvider:    constant.Manual,\n\t\tDescription: req.Description,\n\t\tStatus:      constant.SSLReady,\n\t}\n\tvar err error\n\tif req.SSLID > 0 {\n\t\twebsiteSSL, err = websiteSSLRepo.GetFirst(repo.WithByID(req.SSLID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\twebsiteSSL.Description = req.Description\n\t}\n\tif req.Type == \"local\" {\n\t\tfileOp := files.NewFileOp()\n\t\tif !fileOp.Stat(req.PrivateKeyPath) {\n\t\t\treturn buserr.New(\"ErrSSLKeyNotFound\")\n\t\t}\n\t\tif !fileOp.Stat(req.CertificatePath) {\n\t\t\treturn buserr.New(\"ErrSSLCertificateNotFound\")\n\t\t}\n\t\tif content, err := fileOp.GetContent(req.PrivateKeyPath); err != nil {\n\t\t\treturn err\n\t\t} else {\n\t\t\twebsiteSSL.PrivateKey = string(content)\n\t\t}\n\t\tif content, err := fileOp.GetContent(req.CertificatePath); err != nil {\n\t\t\treturn err\n\t\t} else {\n\t\t\twebsiteSSL.Pem = string(content)\n\t\t}\n\t\twebsiteSSL.CertPath = req.CertificatePath\n\t\twebsiteSSL.PrivateKeyPath = req.PrivateKeyPath\n\t} else {\n\t\twebsiteSSL.PrivateKey = req.PrivateKey\n\t\twebsiteSSL.Pem = req.Certificate\n\t\twebsiteSSL.CertPath = \"\"\n\t\twebsiteSSL.PrivateKeyPath = \"\"\n\t}\n\n\tprivateKeyCertBlock, _ := pem.Decode([]byte(websiteSSL.PrivateKey))\n\tif privateKeyCertBlock == nil {\n\t\treturn buserr.New(\"ErrSSLKeyFormat\")\n\t}\n\n\tvar (\n\t\tcert    *x509.Certificate\n\t\tpemData = []byte(websiteSSL.Pem)\n\t)\n\tfor {\n\t\tcertBlock, reset := pem.Decode(pemData)\n\t\tif certBlock == nil {\n\t\t\tbreak\n\t\t}\n\t\tcert, err = x509.ParseCertificate(certBlock.Bytes)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(cert.DNSNames) > 0 || len(cert.IPAddresses) > 0 {\n\t\t\tbreak\n\t\t}\n\t\tpemData = reset\n\t}\n\tif pemData == nil || cert == nil {\n\t\treturn buserr.New(\"ErrSSLCertificateFormat\")\n\t}\n\n\twebsiteSSL.ExpireDate = cert.NotAfter\n\twebsiteSSL.StartDate = cert.NotBefore\n\twebsiteSSL.Type = cert.Issuer.CommonName\n\tif len(cert.Issuer.Organization) > 0 {\n\t\twebsiteSSL.Organization = cert.Issuer.Organization[0]\n\t} else {\n\t\twebsiteSSL.Organization = cert.Issuer.CommonName\n\t}\n\n\tvar domains []string\n\tif len(cert.DNSNames) > 0 {\n\t\twebsiteSSL.PrimaryDomain = cert.DNSNames[0]\n\t\tdomains = cert.DNSNames[1:]\n\t}\n\tif len(cert.IPAddresses) > 0 {\n\t\tif websiteSSL.PrimaryDomain == \"\" {\n\t\t\twebsiteSSL.PrimaryDomain = cert.IPAddresses[0].String()\n\t\t\tfor _, ip := range cert.IPAddresses[1:] {\n\t\t\t\tdomains = append(domains, ip.String())\n\t\t\t}\n\t\t} else {\n\t\t\tfor _, ip := range cert.IPAddresses {\n\t\t\t\tdomains = append(domains, ip.String())\n\t\t\t}\n\t\t}\n\t}\n\twebsiteSSL.Domains = strings.Join(domains, \",\")\n\n\tif websiteSSL.ID > 0 {\n\t\tif err := UpdateSSLConfig(*websiteSSL); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn websiteSSLRepo.Save(websiteSSL)\n\t}\n\treturn websiteSSLRepo.Create(context.Background(), websiteSSL)\n}\n\nfunc (w WebsiteSSLService) DownloadFile(id uint) (*os.File, error) {\n\twebsiteSSL, err := websiteSSLRepo.GetFirst(repo.WithByID(id))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfileOp := files.NewFileOp()\n\tdir := path.Join(global.Dir.DataDir, \"tmp/ssl\", websiteSSL.PrimaryDomain)\n\tif fileOp.Stat(dir) {\n\t\tif err = fileOp.DeleteDir(dir); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif err = fileOp.CreateDir(dir, constant.DirPerm); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = fileOp.WriteFile(path.Join(dir, \"fullchain.pem\"), strings.NewReader(websiteSSL.Pem), constant.DirPerm); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = fileOp.WriteFile(path.Join(dir, \"privkey.pem\"), strings.NewReader(websiteSSL.PrivateKey), constant.DirPerm); err != nil {\n\t\treturn nil, err\n\t}\n\tfileName := websiteSSL.PrimaryDomain + \".zip\"\n\tif err = fileOp.Compress([]string{path.Join(dir, \"fullchain.pem\"), path.Join(dir, \"privkey.pem\")}, dir, fileName, files.SdkZip, \"\"); err != nil {\n\t\treturn nil, err\n\t}\n\treturn os.Open(path.Join(dir, fileName))\n}\n\nfunc (w WebsiteSSLService) SyncForRestart() error {\n\tsslList, err := websiteSSLRepo.List()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, ssl := range sslList {\n\t\tif ssl.Status == constant.SSLApply {\n\t\t\tssl.Status = constant.SystemRestart\n\t\t\tssl.Message = \"System restart causing interrupt\"\n\t\t\t_ = websiteSSLRepo.Save(&ssl)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (w WebsiteSSLService) ImportMasterSSL(create model.WebsiteSSL) error {\n\twebsiteSSL, _ := websiteSSLRepo.GetFirst(websiteSSLRepo.WithByMasterSSLID(create.ID))\n\twebsiteSSL.Status = constant.SSLReady\n\twebsiteSSL.Provider = constant.FromMaster\n\twebsiteSSL.PrimaryDomain = create.PrimaryDomain\n\twebsiteSSL.StartDate = create.StartDate\n\twebsiteSSL.ExpireDate = create.ExpireDate\n\twebsiteSSL.KeyType = create.KeyType\n\twebsiteSSL.Description = create.Description\n\twebsiteSSL.PrivateKey = create.PrivateKey\n\twebsiteSSL.Pem = create.Pem\n\twebsiteSSL.Type = create.Type\n\twebsiteSSL.Organization = create.Organization\n\twebsiteSSL.MasterSSLID = create.ID\n\twebsiteSSL.Domains = create.Domains\n\tif err := websiteSSLRepo.Save(websiteSSL); err != nil {\n\t\treturn err\n\t}\n\twebsites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))\n\tif len(websites) == 0 {\n\t\treturn nil\n\t}\n\tfor _, website := range websites {\n\t\tif err := createPemFile(website, *websiteSSL); err != nil {\n\t\t\tcontinue\n\t\t}\n\t}\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err == nil {\n\t\tif err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/app/service/website_utils.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"crypto/x509\"\n\t\"encoding/json\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"github.com/pkg/errors\"\n\t\"gorm.io/gorm\"\n)\n\nfunc handleChineseDomain(domain string) (string, error) {\n\tif common.ContainsChinese(domain) {\n\t\treturn common.PunycodeEncode(domain)\n\t}\n\treturn domain, nil\n}\n\nfunc isHTTPSProxyPass(proxyPass string) bool {\n\treturn strings.HasPrefix(strings.ToLower(strings.TrimSpace(proxyPass)), \"https://\")\n}\n\nfunc normalizeProxyPass(proxyPass string) string {\n\tproxyPass = strings.TrimSpace(proxyPass)\n\tif proxyPass == \"\" {\n\t\treturn \"\"\n\t}\n\tif strings.Contains(proxyPass, \"://\") || strings.HasPrefix(proxyPass, \"unix:\") {\n\t\treturn proxyPass\n\t}\n\treturn fmt.Sprintf(\"http://%s\", proxyPass)\n}\n\nfunc getAppInstallProxyPass(appInstall *model.AppInstall) (string, error) {\n\tif appInstall == nil {\n\t\treturn \"\", errors.New(\"app install is nil\")\n\t}\n\tif appInstall.HttpPort > 0 {\n\t\treturn fmt.Sprintf(\"http://127.0.0.1:%d\", appInstall.HttpPort), nil\n\t}\n\tif appInstall.HttpsPort > 0 {\n\t\treturn fmt.Sprintf(\"https://127.0.0.1:%d\", appInstall.HttpsPort), nil\n\t}\n\treturn \"\", fmt.Errorf(\"app %s has no available http or https port\", appInstall.Name)\n}\n\nfunc getAppInstallProxyPassOrEmpty(appInstall *model.AppInstall) string {\n\tproxyPass, err := getAppInstallProxyPass(appInstall)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn proxyPass\n}\n\nfunc hasAppInstallProxyPassChanged(before *model.AppInstall, after *model.AppInstall) bool {\n\treturn getAppInstallProxyPassOrEmpty(before) != getAppInstallProxyPassOrEmpty(after)\n}\n\nfunc getRootProxyDirectives(proxyPass string) []components.IDirective {\n\tserver := &components.Server{}\n\tserver.UpdateRootProxy([]string{normalizeProxyPass(proxyPass)})\n\n\tlocations := server.FindDirectives(\"location\")\n\tif len(locations) == 0 || locations[0].GetBlock() == nil {\n\t\treturn nil\n\t}\n\n\treturn append([]components.IDirective(nil), locations[0].GetBlock().GetDirectives()...)\n}\n\nfunc applyLocationProxyPass(location *components.Location, proxyPass string, sni *bool, proxySSLName string) {\n\tlocation.UpdateDirective(\"proxy_pass\", []string{proxyPass})\n\n\tenableSNI := isHTTPSProxyPass(proxyPass)\n\tif sni != nil {\n\t\tenableSNI = enableSNI && *sni\n\t}\n\tif enableSNI {\n\t\tlocation.UpdateDirective(\"proxy_ssl_server_name\", []string{\"on\"})\n\t} else {\n\t\tlocation.UpdateDirective(\"proxy_ssl_server_name\", []string{\"off\"})\n\t}\n\n\tsslName := \"$proxy_host\"\n\tif proxySSLName != \"\" {\n\t\tsslName = proxySSLName\n\t}\n\tlocation.UpdateDirective(\"proxy_ssl_name\", []string{sslName})\n}\n\nfunc createIndexFile(website *model.Website, runtime *model.Runtime) error {\n\tvar (\n\t\tindexPath      string\n\t\tindexContent   string\n\t\twebsiteService = NewIWebsiteService()\n\t\tindexFolder    = GetSitePath(*website, SiteIndexDir)\n\t)\n\n\tswitch website.Type {\n\tcase constant.Static:\n\t\tindexPath = path.Join(indexFolder, \"index.html\")\n\t\tindexHtml, _ := websiteService.GetDefaultHtml(\"index\")\n\t\tindexContent = indexHtml.Content\n\tcase constant.Runtime:\n\t\tif runtime.Type == constant.RuntimePHP {\n\t\t\tindexPath = path.Join(indexFolder, \"index.php\")\n\t\t\tindexPhp, _ := websiteService.GetDefaultHtml(\"php\")\n\t\t\tindexContent = indexPhp.Content\n\t\t}\n\t}\n\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(indexFolder) {\n\t\tif err := fileOp.CreateDir(indexFolder, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif !fileOp.Stat(indexPath) {\n\t\tif err := fileOp.CreateFile(indexPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif website.Type == constant.Runtime && runtime.Resource == constant.ResourceAppstore {\n\t\tif err := chownRootDir(indexFolder); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := fileOp.WriteFile(indexPath, strings.NewReader(indexContent), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\n\thtml404, _ := websiteService.GetDefaultHtml(\"404\")\n\tpath404 := path.Join(indexFolder, \"404.html\")\n\tif err := fileOp.WriteFile(path404, strings.NewReader(html404.Content), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc createProxyFile(website *model.Website) error {\n\tproxyFolder := GetSitePath(*website, SiteProxyDir)\n\tfilePath := path.Join(proxyFolder, \"root.conf\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(proxyFolder) {\n\t\tif err := fileOp.CreateDir(proxyFolder, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif !fileOp.Stat(filePath) {\n\t\tif err := fileOp.CreateFile(filePath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tconfig, err := parser.NewStringParser(string(nginx_conf.Proxy)).Parse()\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfig.FilePath = filePath\n\tdirectives := config.Directives\n\tlocation, ok := directives[0].(*components.Location)\n\tif !ok {\n\t\treturn errors.New(\"error\")\n\t}\n\tlocation.ChangePath(\"^~\", \"/\")\n\tapplyLocationProxyPass(location, website.Proxy, nil, \"\")\n\tlocation.UpdateDirective(\"proxy_set_header\", []string{\"Host\", \"$host\"})\n\tif err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn buserr.WithErr(\"ErrUpdateBuWebsite\", err)\n\t}\n\treturn nil\n}\n\nfunc createWebsiteFolder(website *model.Website, runtime *model.Runtime) error {\n\tsiteFolder := GetSiteDir(website.Alias)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(siteFolder) {\n\t\tif err := fileOp.CreateDir(siteFolder, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := fileOp.CreateDir(path.Join(siteFolder, \"log\"), constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := fileOp.CreateFile(path.Join(siteFolder, \"log\", \"access.log\")); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := fileOp.CreateFile(path.Join(siteFolder, \"log\", \"error.log\")); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif website.Type != constant.Stream {\n\t\t\tif err := fileOp.CreateDir(path.Join(siteFolder, \"index\"), constant.DirPerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif err := fileOp.CreateDir(path.Join(siteFolder, \"ssl\"), constant.DirPerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif website.Type == constant.Runtime {\n\t\t\tif runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceLocal {\n\t\t\t\tphpPoolDir := path.Join(siteFolder, \"php-pool\")\n\t\t\t\tif err := fileOp.CreateDir(phpPoolDir, constant.DirPerm); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif err := fileOp.CreateFile(path.Join(phpPoolDir, \"php-fpm.sock\")); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif website.Type == constant.Static || (website.Type == constant.Runtime && runtime.Type == constant.RuntimePHP) {\n\t\t\tif err := createIndexFile(website, runtime); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif website.Type == constant.Proxy {\n\t\t\tif err := createProxyFile(website); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, appInstall *model.AppInstall, runtime *model.Runtime, streamConfig request.StreamConfig) error {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = createWebsiteFolder(website, runtime); err != nil {\n\t\treturn err\n\t}\n\tvar (\n\t\tconfigPath string\n\t\tconfig     *components.Config\n\t)\n\tif website.Type == constant.Stream {\n\t\tif streamConfig.StreamPorts == \"\" {\n\t\t\treturn buserr.New(\"ErrTypePortRange\")\n\t\t}\n\t\tnginxContent := nginx_conf.GetWebsiteFile(\"stream_default.conf\")\n\t\tconfig, err = parser.NewStringParser(string(nginxContent)).Parse()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tservers := config.FindServers()\n\t\tif len(servers) == 0 {\n\t\t\treturn errors.New(\"nginx config is not valid\")\n\t\t}\n\t\tserver := servers[0]\n\t\tports := strings.Split(streamConfig.StreamPorts, \",\")\n\t\tvar params []string\n\t\tif streamConfig.UDP {\n\t\t\tparams = []string{\"udp\"}\n\t\t}\n\t\tfor _, port := range ports {\n\t\t\tserver.UpdateListen(port, false, params...)\n\t\t\tif website.IPV6 {\n\t\t\t\tserver.UpdateListen(\"[::]:\"+port, false, params...)\n\t\t\t}\n\t\t}\n\t\tsiteFolder := path.Join(\"/www\", \"sites\", website.Alias)\n\t\tserver.UpdateDirective(\"access_log\", []string{path.Join(siteFolder, \"log\", \"access.log\"), \"streamlog\"})\n\t\tserver.UpdateDirective(\"error_log\", []string{path.Join(siteFolder, \"log\", \"error.log\")})\n\t\tserver.UpdateDirective(\"proxy_pass\", []string{website.Alias})\n\n\t\tupstream := components.Upstream{\n\t\t\tUpstreamName: website.Alias,\n\t\t}\n\t\tif streamConfig.Algorithm != \"default\" {\n\t\t\tupstream.UpdateDirective(streamConfig.Algorithm, []string{})\n\t\t}\n\t\tupstream.UpstreamServers = parseUpstreamServers(streamConfig.Servers)\n\t\tconfig.Block.Directives = append(config.Block.Directives, &upstream)\n\t\tconfigPath = GetSitePath(*website, StreamConf)\n\t} else {\n\t\tconfigPath = GetSitePath(*website, SiteConf)\n\t\tnginxContent := nginx_conf.GetWebsiteFile(\"website_default.conf\")\n\t\tconfig, err = parser.NewStringParser(string(nginxContent)).Parse()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tservers := config.FindServers()\n\t\tif len(servers) == 0 {\n\t\t\treturn errors.New(\"nginx config is not valid\")\n\t\t}\n\t\tserver := servers[0]\n\t\tserver.DeleteListen(\"80\")\n\t\tvar serverNames []string\n\t\tfor _, domain := range domains {\n\t\t\tserverNames = append(serverNames, domain.Domain)\n\t\t\tsetListen(server, strconv.Itoa(domain.Port), website.IPV6, false, website.DefaultServer, false)\n\t\t}\n\t\tserver.UpdateServerName(serverNames)\n\n\t\tsiteFolder := path.Join(\"/www\", \"sites\", website.Alias)\n\t\tserver.UpdateDirective(\"access_log\", []string{path.Join(siteFolder, \"log\", \"access.log\"), \"main\"})\n\t\tserver.UpdateDirective(\"error_log\", []string{path.Join(siteFolder, \"log\", \"error.log\")})\n\n\t\trootIndex := path.Join(\"/www/sites\", website.Alias, \"index\")\n\t\tswitch website.Type {\n\t\tcase constant.Deployment:\n\t\t\tproxy, err := getAppInstallProxyPass(appInstall)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tserver.UpdateRootProxy([]string{proxy})\n\t\tcase constant.Static:\n\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\tserver.UpdateDirective(\"error_page\", []string{\"404\", \"/404.html\"})\n\t\tcase constant.Proxy:\n\t\t\tnginxInclude := fmt.Sprintf(\"/www/sites/%s/proxy/*.conf\", website.Alias)\n\t\t\tserver.UpdateDirective(\"include\", []string{nginxInclude})\n\t\t\tserver.UpdateRoot(rootIndex)\n\t\tcase constant.Runtime:\n\t\t\tswitch runtime.Type {\n\t\t\tcase constant.RuntimePHP:\n\t\t\t\tserver.UpdateDirective(\"error_page\", []string{\"404\", \"/404.html\"})\n\t\t\t\tif runtime.Resource == constant.ResourceLocal {\n\t\t\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\t\t\tlocalPath := path.Join(rootIndex, \"index.php\")\n\t\t\t\t\tserver.UpdatePHPProxy([]string{website.Proxy}, localPath)\n\t\t\t\t} else {\n\t\t\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\t\t\tserver.UpdatePHPProxy([]string{website.Proxy}, \"\")\n\t\t\t\t}\n\t\t\tcase constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:\n\t\t\t\tserver.UpdateRootProxy([]string{normalizeProxyPass(website.Proxy)})\n\t\t\t}\n\t\tcase constant.Subsite:\n\t\t\tparentWebsite, err := websiteRepo.GetFirst(repo.WithByID(website.ParentWebsiteID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\twebsite.Proxy = parentWebsite.Proxy\n\t\t\trootIndex = path.Join(\"/www/sites\", parentWebsite.Alias, \"index\", website.SiteDir)\n\t\t\tserver.UpdateDirective(\"error_page\", []string{\"404\", \"/404.html\"})\n\t\t\tif parentWebsite.Type == constant.Runtime {\n\t\t\t\tparentRuntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(parentWebsite.RuntimeID))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\twebsite.RuntimeID = parentRuntime.ID\n\t\t\t\tif parentRuntime.Type == constant.RuntimePHP {\n\t\t\t\t\tif parentRuntime.Resource == constant.ResourceLocal {\n\t\t\t\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\t\t\t\tlocalPath := path.Join(rootIndex, \"index.php\")\n\t\t\t\t\t\tserver.UpdatePHPProxy([]string{website.Proxy}, localPath)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\t\t\t\tserver.UpdatePHPProxy([]string{website.Proxy}, \"\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif parentWebsite.Type == constant.Static {\n\t\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\t}\n\t\t}\n\t}\n\tconfig.FilePath = configPath\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\tif err = opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {\n\t\treturn err\n\t}\n\tif err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc moveDefaultWafConfig(websiteDir string, defaultConfigContent []byte, defaultRuleDir string, fileOp files.FileOp) error {\n\tif !fileOp.Stat(websiteDir) {\n\t\tif err := fileOp.CreateDir(websiteDir, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := fileOp.SaveFileWithByte(path.Join(websiteDir, \"config.json\"), defaultConfigContent, constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\twebsiteRuleDir := path.Join(websiteDir, \"rules\")\n\tif !fileOp.Stat(websiteRuleDir) {\n\t\tif err := fileOp.CreateDir(websiteRuleDir, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tdefaultRulesName := []string{\"acl\", \"args\", \"cookie\", \"defaultUaBlack\", \"defaultUrlBlack\", \"fileExt\", \"header\", \"methodWhite\", \"cdn\"}\n\tfor _, ruleName := range defaultRulesName {\n\t\tsrcPath := path.Join(defaultRuleDir, ruleName+\".json\")\n\t\tif fileOp.Stat(srcPath) {\n\t\t\t_ = fileOp.Copy(srcPath, websiteRuleDir)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc createAllWebsitesWAFConfig(websites []model.Website) error {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\twafDataPath := path.Join(nginxInstall.GetPath(), \"1pwaf\", \"data\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(wafDataPath) {\n\t\treturn nil\n\t}\n\twebsitesConfigPath := path.Join(wafDataPath, \"conf\", \"sites.json\")\n\tvar websitesArray []request.WafWebsite\n\tfor _, website := range websites {\n\t\twafWebsite := request.WafWebsite{\n\t\t\tKey:     website.Alias,\n\t\t\tDomains: make([]string, 0),\n\t\t\tHost:    make([]string, 0),\n\t\t}\n\t\twebsiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))\n\t\tfor _, domain := range websiteDomains {\n\t\t\twafWebsite.Domains = append(wafWebsite.Domains, domain.Domain)\n\t\t\twafWebsite.Host = append(wafWebsite.Host, domain.Domain+\":\"+strconv.Itoa(domain.Port))\n\t\t}\n\t\twebsitesArray = append(websitesArray, wafWebsite)\n\t}\n\twebsitesContent, err := json.Marshal(websitesArray)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\tvar (\n\t\tdefaultConfigPath = path.Join(wafDataPath, \"conf\", \"siteConfig.json\")\n\t\tdefaultRuleDir    = path.Join(wafDataPath, \"rules\")\n\t\tsitesDir          = path.Join(wafDataPath, \"sites\")\n\t)\n\tdefaultConfigContent, err := fileOp.GetContent(defaultConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, website := range websites {\n\t\twebsiteDir := path.Join(sitesDir, website.Alias)\n\t\tif err := moveDefaultWafConfig(websiteDir, defaultConfigContent, defaultRuleDir, fileOp); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc createOpenBasedirConfig(website *model.Website) {\n\tfileOp := files.NewFileOp()\n\tuserIniPath := path.Join(GetSitePath(*website, SiteIndexDir), \".user.ini\")\n\t_ = fileOp.CreateFile(userIniPath)\n\t_ = fileOp.SaveFile(userIniPath, fmt.Sprintf(\"open_basedir=/www/sites/%s/index:/tmp/\", website.Alias), 0644)\n}\n\nfunc createWafConfig(website *model.Website, domains []model.WebsiteDomain) error {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\twafDataPath := path.Join(nginxInstall.GetPath(), \"1pwaf\", \"data\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(wafDataPath) {\n\t\treturn nil\n\t}\n\twebsitesConfigPath := path.Join(wafDataPath, \"conf\", \"sites.json\")\n\tcontent, err := fileOp.GetContent(websitesConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar websitesArray []request.WafWebsite\n\tif len(content) != 0 {\n\t\tif err := json.Unmarshal(content, &websitesArray); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\twafWebsite := request.WafWebsite{\n\t\tKey:     website.Alias,\n\t\tDomains: make([]string, 0),\n\t\tHost:    make([]string, 0),\n\t}\n\n\tfor _, domain := range domains {\n\t\twafWebsite.Domains = append(wafWebsite.Domains, domain.Domain)\n\t\twafWebsite.Host = append(wafWebsite.Host, domain.Domain+\":\"+strconv.Itoa(domain.Port))\n\t}\n\twebsitesArray = append(websitesArray, wafWebsite)\n\twebsitesContent, err := json.Marshal(websitesArray)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\n\tvar (\n\t\tsitesDir          = path.Join(wafDataPath, \"sites\")\n\t\tdefaultConfigPath = path.Join(wafDataPath, \"conf\", \"siteConfig.json\")\n\t\tdefaultRuleDir    = path.Join(wafDataPath, \"rules\")\n\t\twebsiteDir        = path.Join(sitesDir, website.Alias)\n\t)\n\n\tdefaultConfigContent, err := fileOp.GetContent(defaultConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = fileOp.DeleteDir(websiteDir)\n\t\t}\n\t}()\n\n\tif err := moveDefaultWafConfig(websiteDir, defaultConfigContent, defaultRuleDir, fileOp); err != nil {\n\t\treturn err\n\t}\n\n\tif err = opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {\n\t\treturn err\n\t}\n\tif err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc delNginxConfig(website model.Website, force bool) error {\n\tfileOp := files.NewFileOp()\n\tvar (\n\t\tconfigPath string\n\t)\n\tif website.Type == constant.Stream {\n\t\tconfigPath = GetSitePath(website, StreamConf)\n\n\t} else {\n\t\tconfigPath = GetSitePath(website, SiteConf)\n\t}\n\tif fileOp.Stat(configPath) {\n\t\tif err := fileOp.DeleteFile(configPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tsitePath := GetSiteDir(website.Alias)\n\tif fileOp.Stat(sitePath) {\n\t\txpack.RemoveTamper(website.Alias)\n\t\t_ = fileOp.DeleteDir(sitePath)\n\t}\n\n\tnginxApp, err := appRepo.GetFirst(appRepo.WithKey(constant.AppOpenresty))\n\tif err != nil {\n\t\treturn err\n\t}\n\tnginxInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(nginxApp.ID))\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\tif err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {\n\t\tif force {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc delWafConfig(website model.Website, force bool) error {\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !common.CompareVersion(nginxInstall.Version, \"1.21.4.3-2-0\") {\n\t\treturn nil\n\t}\n\twafDataPath := path.Join(nginxInstall.GetPath(), \"1pwaf\", \"data\")\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(wafDataPath) {\n\t\treturn nil\n\t}\n\tmonitorDir := path.Join(wafDataPath, \"db\", \"sites\", website.Alias)\n\tif fileOp.Stat(monitorDir) {\n\t\t_ = fileOp.DeleteDir(monitorDir)\n\t}\n\twebsitesConfigPath := path.Join(wafDataPath, \"conf\", \"sites.json\")\n\tcontent, err := fileOp.GetContent(websitesConfigPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar websitesArray []request.WafWebsite\n\tvar newWebsiteArray []request.WafWebsite\n\tif len(content) > 0 {\n\t\tif err = json.Unmarshal(content, &websitesArray); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, wafWebsite := range websitesArray {\n\t\tif wafWebsite.Key != website.Alias {\n\t\t\tnewWebsiteArray = append(newWebsiteArray, wafWebsite)\n\t\t}\n\t}\n\twebsitesContent, err := json.Marshal(newWebsiteArray)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := fileOp.SaveFileWithByte(websitesConfigPath, websitesContent, constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\n\t_ = fileOp.DeleteDir(path.Join(wafDataPath, \"sites\", website.Alias))\n\n\tif err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {\n\t\tif force {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc isHttp3(server *components.Server) bool {\n\tfor _, listen := range server.Listens {\n\t\tfor _, param := range listen.Parameters {\n\t\t\tif param == \"quic\" {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc addListenAndServerName(website model.Website, domains []model.WebsiteDomain) error {\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tnginxConfig := nginxFull.SiteConfig\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\thttp3 := isHttp3(server)\n\n\tvar allDomains []string\n\texistDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))\n\tfor _, domain := range existDomains {\n\t\tallDomains = append(allDomains, domain.Domain)\n\t}\n\n\tfor _, domain := range domains {\n\t\tsetListen(server, strconv.Itoa(domain.Port), website.IPV6, http3, website.DefaultServer, website.Protocol == constant.ProtocolHTTPS && domain.SSL)\n\t\tallDomains = append(allDomains, domain.Domain)\n\t}\n\tserver.UpdateServerName(allDomains)\n\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName)\n}\n\nfunc deleteListenAndServerName(website model.Website, binds []string, domains []string) error {\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tnginxConfig := nginxFull.SiteConfig\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\tfor _, bind := range binds {\n\t\tserver.DeleteListen(bind)\n\t\tserver.DeleteListen(\"[::]:\" + bind)\n\t}\n\tfor _, domain := range domains {\n\t\tserver.DeleteServerName(domain)\n\t}\n\n\tif err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName)\n}\n\nfunc setListen(server *components.Server, port string, ipv6, http3, defaultServer, ssl bool) {\n\tvar params []string\n\tif ssl {\n\t\tparams = []string{\"ssl\"}\n\t}\n\tserver.UpdateListen(port, defaultServer, params...)\n\tif ssl && http3 {\n\t\tserver.UpdateListen(port, defaultServer, \"quic\")\n\t}\n\tif !ipv6 {\n\t\treturn\n\t}\n\tserver.UpdateListen(\"[::]:\"+port, defaultServer, params...)\n\tif ssl && http3 {\n\t\tserver.UpdateListen(\"[::]:\"+port, defaultServer, \"quic\")\n\t}\n}\n\nfunc removeSSLListen(website model.Website, binds []string) error {\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tnginxConfig := nginxFull.SiteConfig\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\thttp3 := isHttp3(server)\n\tfor _, bind := range binds {\n\t\tserver.DeleteListen(bind)\n\t\tif website.IPV6 {\n\t\t\tserver.DeleteListen(\"[::]:\" + bind)\n\t\t}\n\t\tsetListen(server, bind, website.IPV6, http3, website.DefaultServer, website.Protocol == constant.ProtocolHTTPS)\n\t}\n\tif err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName)\n}\n\nfunc createPemFile(website model.Website, websiteSSL model.WebsiteSSL) error {\n\tconfigDir := GetSitePath(website, SiteSSLDir)\n\tfileOp := files.NewFileOp()\n\n\tif !fileOp.Stat(configDir) {\n\t\tif err := fileOp.CreateDir(configDir, constant.DirPerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfullChainFile := path.Join(configDir, \"fullchain.pem\")\n\tprivatePemFile := path.Join(configDir, \"privkey.pem\")\n\n\tif !fileOp.Stat(fullChainFile) {\n\t\tif err := fileOp.CreateFile(fullChainFile); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif !fileOp.Stat(privatePemFile) {\n\t\tif err := fileOp.CreateFile(privatePemFile); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif err := fileOp.WriteFile(fullChainFile, strings.NewReader(websiteSSL.Pem), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\tif err := fileOp.WriteFile(privatePemFile, strings.NewReader(websiteSSL.PrivateKey), constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc getHttpsPort(websiteID uint) map[int]struct{} {\n\tdomains, err := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteID))\n\tif err != nil {\n\t\treturn nil\n\t}\n\thttpsPorts := make(map[int]struct{})\n\tnginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)\n\thasDefaultPort := false\n\tfor _, domain := range domains {\n\t\tif domain.Port == nginxInstall.HttpPort {\n\t\t\thasDefaultPort = true\n\t\t}\n\t\tif domain.SSL {\n\t\t\thttpsPorts[domain.Port] = struct{}{}\n\t\t}\n\t}\n\tif hasDefaultPort {\n\t\thttpsPorts[nginxInstall.HttpsPort] = struct{}{}\n\t}\n\tif len(httpsPorts) == 0 {\n\t\tfor _, domain := range domains {\n\t\t\tif !domain.SSL {\n\t\t\t\thttpsPorts[domain.Port] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\treturn httpsPorts\n}\n\nfunc applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.WebsiteHTTPSOp) error {\n\tnginxFull, err := getNginxFull(website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tdomains, err := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))\n\tif err != nil {\n\t\treturn nil\n\t}\n\thttpPorts := make(map[int]struct{})\n\thttpsPorts := make(map[int]struct{})\n\tsslPort := 0\n\n\thasDefaultPort := false\n\tfor _, domain := range domains {\n\t\tif domain.Port == nginxFull.Install.HttpPort {\n\t\t\thasDefaultPort = true\n\t\t}\n\t\tif domain.SSL {\n\t\t\thttpsPorts[domain.Port] = struct{}{}\n\t\t} else {\n\t\t\thttpPorts[domain.Port] = struct{}{}\n\t\t}\n\t}\n\tif hasDefaultPort {\n\t\thttpsPorts[nginxFull.Install.HttpsPort] = struct{}{}\n\t}\n\tif len(httpsPorts) == 0 {\n\t\tfor port := range httpPorts {\n\t\t\thttpsPorts[port] = struct{}{}\n\t\t}\n\t}\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\n\tdefaultHttpPort := strconv.Itoa(nginxFull.Install.HttpPort)\n\tdefaultHttpPortIPV6 := \"[::]:\" + defaultHttpPort\n\n\tfor port := range httpsPorts {\n\t\tsslPort = port\n\t\tportStr := strconv.Itoa(port)\n\t\tserver.RemoveListenByBind(portStr)\n\t\tserver.RemoveListenByBind(\"[::]:\" + portStr)\n\t\tsetListen(server, portStr, website.IPV6, req.Http3, website.DefaultServer, true)\n\t}\n\n\tserver.UpdateDirective(\"http2\", []string{\"on\"})\n\n\tswitch req.HttpConfig {\n\tcase constant.HTTPSOnly:\n\t\tserver.RemoveListenByBind(defaultHttpPort)\n\t\tserver.RemoveListenByBind(defaultHttpPortIPV6)\n\t\tserver.RemoveDirective(\"if\", []string{\"($scheme\"})\n\tcase constant.HTTPToHTTPS:\n\t\tif hasDefaultPort {\n\t\t\tserver.UpdateListen(defaultHttpPort, website.DefaultServer)\n\t\t\tif website.IPV6 {\n\t\t\t\tserver.UpdateListen(defaultHttpPortIPV6, website.DefaultServer)\n\t\t\t}\n\t\t}\n\t\tserver.AddHTTP2HTTPS(sslPort)\n\tcase constant.HTTPAlso:\n\t\tif hasDefaultPort {\n\t\t\tserver.UpdateListen(defaultHttpPort, website.DefaultServer)\n\t\t\tif website.IPV6 {\n\t\t\t\tserver.UpdateListen(defaultHttpPortIPV6, website.DefaultServer)\n\t\t\t}\n\t\t}\n\t\tserver.RemoveDirective(\"if\", []string{\"($scheme\"})\n\t}\n\n\tif !req.Hsts {\n\t\tserver.RemoveDirective(\"add_header\", []string{\"Strict-Transport-Security\", \"\\\"max-age=31536000\\\"\"})\n\t\tserver.RemoveDirective(\"add_header\", []string{\"Strict-Transport-Security\", \"\\\"max-age=31536000; includeSubDomains\\\"\"})\n\t}\n\tif !req.Http3 {\n\t\tfor port := range httpsPorts {\n\t\t\tserver.RemoveListen(strconv.Itoa(port), \"quic\")\n\t\t\tif website.IPV6 {\n\t\t\t\thttpsPortIPV6 := \"[::]:\" + strconv.Itoa(port)\n\t\t\t\tserver.RemoveListen(httpsPortIPV6, \"quic\")\n\t\t\t}\n\t\t}\n\t\tserver.RemoveDirective(\"add_header\", []string{\"Alt-Svc\"})\n\t}\n\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\tif err = createPemFile(*website, websiteSSL); err != nil {\n\t\treturn err\n\t}\n\tnginxParams := getNginxParamsFromStaticFile(dto.SSL, []dto.NginxParam{})\n\tfor i, param := range nginxParams {\n\t\tif param.Name == \"ssl_certificate\" {\n\t\t\tnginxParams[i].Params = []string{path.Join(\"/www\", \"sites\", website.Alias, \"ssl\", \"fullchain.pem\")}\n\t\t}\n\t\tif param.Name == \"ssl_certificate_key\" {\n\t\t\tnginxParams[i].Params = []string{path.Join(\"/www\", \"sites\", website.Alias, \"ssl\", \"privkey.pem\")}\n\t\t}\n\t\tif param.Name == \"ssl_protocols\" {\n\t\t\tnginxParams[i].Params = req.SSLProtocol\n\t\t\tif len(req.SSLProtocol) == 0 {\n\t\t\t\tnginxParams[i].Params = []string{\"TLSv1.3\", \"TLSv1.2\"}\n\t\t\t}\n\t\t}\n\t\tif param.Name == \"ssl_ciphers\" {\n\t\t\tnginxParams[i].Params = []string{req.Algorithm}\n\t\t\tif len(req.Algorithm) == 0 {\n\t\t\t\tnginxParams[i].Params = []string{\"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED\"}\n\t\t\t}\n\t\t}\n\t\tif param.Name == \"error_page\" {\n\t\t\tif len(param.Params) < 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcode := param.Params[0]\n\t\t\tif code == \"497\" {\n\t\t\t\tif sslPort != 443 && param.Params[1] == \"https://$host$request_uri\" {\n\t\t\t\t\tparam.Params[1] = fmt.Sprintf(\"https://$host:%d$request_uri\", sslPort)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif req.Hsts {\n\t\tvar hstsValue string\n\t\tif req.HstsIncludeSubDomains {\n\t\t\thstsValue = \"\\\"max-age=31536000; includeSubDomains\\\"\"\n\t\t} else {\n\t\t\thstsValue = \"\\\"max-age=31536000\\\"\"\n\t\t}\n\t\tnginxParams = append(nginxParams, dto.NginxParam{\n\t\t\tName:   \"add_header\",\n\t\t\tParams: []string{\"Strict-Transport-Security\", hstsValue},\n\t\t})\n\t}\n\tif req.Http3 {\n\t\tnginxParams = append(nginxParams, dto.NginxParam{\n\t\t\tName:   \"add_header\",\n\t\t\tParams: []string{\"Alt-Svc\", \"'h3=\\\":443\\\"; ma=2592000'\"},\n\t\t})\n\t}\n\n\tif err := updateNginxConfig(constant.NginxScopeServer, nginxParams, website); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc getParamArray(key string, param interface{}) []string {\n\tvar res []string\n\tswitch p := param.(type) {\n\tcase string:\n\t\tif key == \"index\" {\n\t\t\tres = strings.Split(p, \"\\n\")\n\t\t\treturn res\n\t\t}\n\n\t\tres = strings.Split(p, \" \")\n\t\treturn res\n\t}\n\treturn res\n}\n\nfunc handleParamMap(paramMap map[string]string, keys []string) []dto.NginxParam {\n\tvar nginxParams []dto.NginxParam\n\tfor k, v := range paramMap {\n\t\tfor _, name := range keys {\n\t\t\tif name == k {\n\t\t\t\tparam := dto.NginxParam{\n\t\t\t\t\tName:   k,\n\t\t\t\t\tParams: getParamArray(k, v),\n\t\t\t\t}\n\t\t\t\tnginxParams = append(nginxParams, param)\n\t\t\t}\n\t\t}\n\t}\n\treturn nginxParams\n}\n\nfunc getNginxParams(params interface{}, keys []string) []dto.NginxParam {\n\tvar nginxParams []dto.NginxParam\n\n\tswitch p := params.(type) {\n\tcase map[string]interface{}:\n\t\treturn handleParamMap(toMapStr(p), keys)\n\tcase []interface{}:\n\t\tfor _, mA := range p {\n\t\t\tif m, ok := mA.(map[string]interface{}); ok {\n\t\t\t\tnginxParams = append(nginxParams, handleParamMap(toMapStr(m), keys)...)\n\t\t\t}\n\t\t}\n\t}\n\treturn nginxParams\n}\n\nfunc toMapStr(m map[string]interface{}) map[string]string {\n\tret := make(map[string]string, len(m))\n\tfor k, v := range m {\n\t\tret[k] = fmt.Sprint(v)\n\t}\n\treturn ret\n}\n\nfunc deleteWebsiteFolder(website *model.Website) error {\n\tsiteFolder := GetSitePath(*website, SiteDir)\n\tfileOp := files.NewFileOp()\n\tif fileOp.Stat(siteFolder) {\n\t\t_ = fileOp.DeleteDir(siteFolder)\n\t}\n\tif website.Type == constant.Stream {\n\t\tsteamFilePath := GetSitePath(*website, StreamConf)\n\t\tif fileOp.Stat(steamFilePath) {\n\t\t\t_ = fileOp.DeleteFile(steamFilePath)\n\t\t}\n\t} else {\n\t\tnginxFilePath := GetSitePath(*website, SiteConf)\n\t\tif fileOp.Stat(nginxFilePath) {\n\t\t\t_ = fileOp.DeleteFile(nginxFilePath)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc opWebsite(website *model.Website, operate string) error {\n\tnginxInstall, err := getNginxFull(website)\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfig := nginxInstall.SiteConfig.Config\n\tservers := config.FindServers()\n\tif len(servers) == 0 {\n\t\treturn errors.New(\"nginx config is not valid\")\n\t}\n\tserver := servers[0]\n\tif operate == constant.StopWeb {\n\t\tproxyInclude := fmt.Sprintf(\"/www/sites/%s/proxy/*.conf\", website.Alias)\n\t\tserver.RemoveDirective(\"include\", []string{proxyInclude})\n\t\trewriteInclude := fmt.Sprintf(\"/www/sites/%s/rewrite/%s.conf\", website.Alias, website.Alias)\n\t\tserver.RemoveDirective(\"include\", []string{rewriteInclude})\n\t\tredirectInclude := fmt.Sprintf(\"/www/sites/%s/redirect/%s.conf\", website.Alias, website.Alias)\n\t\tserver.RemoveDirective(\"include\", []string{redirectInclude})\n\n\t\tswitch website.Type {\n\t\tcase constant.Deployment:\n\t\t\tserver.RemoveDirective(\"location\", []string{\"/\"})\n\t\tcase constant.Runtime:\n\t\t\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif runtime.Type == constant.RuntimePHP {\n\t\t\t\tserver.RemoveDirective(\"location\", []string{\"~\", \"[^/]\\\\.php(/|$)\"})\n\t\t\t} else {\n\t\t\t\tserver.RemoveDirective(\"location\", []string{\"/\"})\n\t\t\t}\n\t\t}\n\t\tserver.UpdateRoot(\"/usr/share/nginx/html/stop\")\n\t\twebsite.Status = constant.WebStopped\n\t}\n\tif operate == constant.StartWeb {\n\t\tabsoluteIncludeDir := GetSitePath(*website, SiteProxyDir)\n\t\tfileOp := files.NewFileOp()\n\t\tif fileOp.Stat(absoluteIncludeDir) && !files.IsEmptyDir(absoluteIncludeDir) {\n\t\t\tproxyInclude := fmt.Sprintf(\"/www/sites/%s/proxy/*.conf\", website.Alias)\n\t\t\tserver.UpdateDirective(\"include\", []string{proxyInclude})\n\t\t}\n\t\trewriteInclude := fmt.Sprintf(\"/www/sites/%s/rewrite/%s.conf\", website.Alias, website.Alias)\n\t\tabsoluteRewritePath := GetSitePath(*website, SiteReWritePath)\n\t\tif fileOp.Stat(absoluteRewritePath) {\n\t\t\tserver.UpdateDirective(\"include\", []string{rewriteInclude})\n\t\t}\n\t\tredirectInclude := fmt.Sprintf(\"/www/sites/%s/redirect/%s.conf\", website.Alias, website.Alias)\n\t\tabsoluteRedirectPath := GetSitePath(*website, SiteRedirectDir)\n\t\tif fileOp.Stat(absoluteRedirectPath) {\n\t\t\tserver.UpdateDirective(\"include\", []string{redirectInclude})\n\t\t}\n\t\trootIndex := path.Join(\"/www/sites\", website.Alias, \"index\")\n\t\tif website.SiteDir != \"/\" {\n\t\t\trootIndex = path.Join(rootIndex, website.SiteDir)\n\t\t}\n\t\tswitch website.Type {\n\t\tcase constant.Deployment:\n\t\t\tserver.RemoveDirective(\"root\", nil)\n\t\t\tappInstall, err := appInstallRepo.GetFirst(repo.WithByID(website.AppInstallID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tproxy, err := getAppInstallProxyPass(&appInstall)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tserver.UpdateRootProxy([]string{proxy})\n\t\tcase constant.Static:\n\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\tserver.UpdateRootLocation()\n\t\tcase constant.Proxy:\n\t\t\tserver.RemoveDirective(\"root\", nil)\n\t\tcase constant.Runtime:\n\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\tlocalPath := \"\"\n\t\t\truntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif runtime.Type == constant.RuntimePHP {\n\t\t\t\tif website.ProxyType == constant.RuntimeProxyUnix || website.ProxyType == constant.RuntimeProxyTcp {\n\t\t\t\t\tlocalPath = path.Join(rootIndex, \"index.php\")\n\t\t\t\t}\n\t\t\t\tserver.UpdatePHPProxy([]string{website.Proxy}, localPath)\n\t\t\t} else {\n\t\t\t\tproxy := normalizeProxyPass(website.Proxy)\n\t\t\t\tserver.UpdateRootProxy([]string{proxy})\n\t\t\t}\n\t\tcase constant.Subsite:\n\t\t\tparentWebsite, err := websiteRepo.GetFirst(repo.WithByID(website.ParentWebsiteID))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\twebsite.Proxy = parentWebsite.Proxy\n\t\t\trootIndex = path.Join(\"/www/sites\", parentWebsite.Alias, \"index\", website.SiteDir)\n\t\t\tif parentWebsite.Type == constant.Runtime {\n\t\t\t\tparentRuntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(parentWebsite.RuntimeID))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\twebsite.RuntimeID = parentRuntime.ID\n\t\t\t\tif parentRuntime.Type == constant.RuntimePHP {\n\t\t\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\t\t\tlocalPath := \"\"\n\t\t\t\t\tif parentRuntime.Resource == constant.ResourceLocal {\n\t\t\t\t\t\tlocalPath = path.Join(rootIndex, \"index.php\")\n\t\t\t\t\t}\n\t\t\t\t\tserver.UpdatePHPProxy([]string{website.Proxy}, localPath)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif parentWebsite.Type == constant.Static {\n\t\t\t\tserver.UpdateRoot(rootIndex)\n\t\t\t}\n\t\t}\n\t\twebsite.Status = constant.WebRunning\n\t\tnow := time.Now()\n\t\tif website.ExpireDate.Before(now) {\n\t\t\tdefaultDate, _ := time.Parse(constant.DateLayout, constant.WebsiteDefaultExpireDate)\n\t\t\twebsite.ExpireDate = defaultDate\n\t\t}\n\t}\n\n\tif err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(nginxInstall.SiteConfig.OldContent, config.FilePath, nginxInstall.Install.ContainerName)\n}\n\nfunc changeIPV6(website model.Website, enable bool) error {\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\tlistens := server.Listens\n\tif enable {\n\t\tfor _, listen := range listens {\n\t\t\tif strings.HasPrefix(listen.Bind, \"[::]:\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\texist := false\n\t\t\tipv6Bind := fmt.Sprintf(\"[::]:%s\", listen.Bind)\n\t\t\tfor _, li := range listens {\n\t\t\t\tif li.Bind == ipv6Bind {\n\t\t\t\t\texist = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !exist {\n\t\t\t\tserver.UpdateListen(ipv6Bind, false, listen.GetParameters()[1:]...)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor _, listen := range listens {\n\t\t\tif strings.HasPrefix(listen.Bind, \"[::]:\") {\n\t\t\t\tserver.RemoveListenByBind(listen.Bind)\n\t\t\t}\n\t\t}\n\t}\n\tif err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(nginxFull.SiteConfig.OldContent, config.FilePath, nginxFull.Install.ContainerName)\n}\n\nfunc checkIsLinkApp(website model.Website) bool {\n\tif website.Type == constant.Deployment {\n\t\treturn true\n\t}\n\tif website.Type == constant.Runtime {\n\t\truntime, _ := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))\n\t\treturn runtime.Resource == constant.ResourceAppstore\n\t}\n\treturn false\n}\n\nfunc chownRootDir(path string) error {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))\n\tif err := cmdMgr.RunBashCf(`chown -R 1000:1000 \"%s\"`, path); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, defaultHTTPsPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) {\n\tvar (\n\t\tports     = make(map[int]struct{})\n\t\texistPort = make(map[int]struct{})\n\t)\n\texistDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteID))\n\tfor _, domain := range existDomains {\n\t\texistPort[domain.Port] = struct{}{}\n\t}\n\tfor _, domain := range domains {\n\t\tif domain.Domain == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif !(common.IsValidNginxServerName(domain.Domain) || common.IsValidIP(domain.Domain)) {\n\t\t\terr = buserr.WithName(\"ErrDomainFormat\", domain.Domain)\n\t\t\treturn\n\t\t}\n\t\tvar domainModel model.WebsiteDomain\n\t\tdomainModel.Domain, err = handleChineseDomain(domain.Domain)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdomainModel.Domain = strings.ToLower(domainModel.Domain)\n\t\tdomainModel.Port = domain.Port\n\t\tif domain.Port == 0 {\n\t\t\tdomain.Port = defaultHTTPPort\n\t\t}\n\t\tdomainModel.SSL = domain.SSL\n\t\tdomainModel.WebsiteID = websiteID\n\t\tdomainModels = append(domainModels, domainModel)\n\t\tif _, ok := existPort[domainModel.Port]; !ok {\n\t\t\tports[domainModel.Port] = struct{}{}\n\t\t}\n\t\tif exist, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithDomain(domainModel.Domain), websiteDomainRepo.WithWebsiteId(websiteID)); exist.ID == 0 {\n\t\t\taddDomains = append(addDomains, domainModel.Domain)\n\t\t}\n\t}\n\tfor _, domain := range domainModels {\n\t\tif exist, _ := websiteDomainRepo.GetFirst(websiteDomainRepo.WithDomain(domain.Domain), websiteDomainRepo.WithPort(domain.Port)); exist.ID > 0 {\n\t\t\twebsite, _ := websiteRepo.GetFirst(repo.WithByID(exist.WebsiteID))\n\t\t\terr = buserr.WithName(\"ErrDomainIsUsed\", website.PrimaryDomain)\n\t\t\treturn\n\t\t}\n\t}\n\n\tfor port := range ports {\n\t\tif port == defaultHTTPPort || port == defaultHTTPsPort {\n\t\t\taddPorts = append(addPorts, port)\n\t\t\tcontinue\n\t\t}\n\t\tif err = checkWebsitePort(defaultHTTPsPort, port, \"\"); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteID), websiteDomainRepo.WithPort(port)); len(existPorts) == 0 {\n\t\t\taddPorts = append(addPorts, port)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc checkWebsitePort(defaultHTTPsPort, port int, websiteType string) error {\n\tif existPorts, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(port)); len(existPorts) > 0 {\n\t\treturn nil\n\t}\n\tif websiteType == constant.Stream {\n\t\twebsites, _ := websiteRepo.List(websiteRepo.WithType(constant.Stream))\n\t\tfor _, website := range websites {\n\t\t\tports := strings.Split(website.StreamPorts, \",\")\n\t\t\tfor _, p := range ports {\n\t\t\t\tpInt, _ := strconv.Atoi(p)\n\t\t\t\tif pInt == port {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\terrMap := make(map[string]interface{})\n\terrMap[\"port\"] = port\n\tappInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(port))\n\tif appInstall.ID > 0 {\n\t\terrMap[\"type\"] = i18n.GetMsgByKey(\"TYPE_APP\")\n\t\terrMap[\"name\"] = appInstall.Name\n\t\treturn buserr.WithMap(\"ErrPortExist\", errMap, nil)\n\t}\n\truntime, _ := runtimeRepo.GetFirst(context.Background(), runtimeRepo.WithPort(port))\n\tif runtime != nil {\n\t\terrMap[\"type\"] = i18n.GetMsgByKey(\"TYPE_RUNTIME\")\n\t\terrMap[\"name\"] = runtime.Name\n\t\treturn buserr.WithMap(\"ErrPortExist\", errMap, nil)\n\t}\n\tif port != defaultHTTPsPort && common.ScanPort(port) {\n\t\treturn buserr.WithDetail(\"ErrPortInUsed\", port, nil)\n\t}\n\treturn nil\n}\n\nfunc saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {\n\tif websiteSSL.PushDir {\n\t\tfileOp := files.NewFileOp()\n\t\tvar (\n\t\t\tpushErr error\n\t\t\tMsgMap  = map[string]interface{}{\"path\": websiteSSL.Dir, \"status\": i18n.GetMsgByKey(\"Success\")}\n\t\t)\n\t\tif pushErr = fileOp.SaveFile(path.Join(websiteSSL.Dir, \"privkey.pem\"), websiteSSL.PrivateKey, constant.FilePerm); pushErr != nil {\n\t\t\tMsgMap[\"status\"] = i18n.GetMsgByKey(\"Failed\")\n\t\t\tlogger.Println(i18n.GetMsgWithMap(\"PushDirLog\", MsgMap))\n\t\t\tlogger.Println(\"Push dir failed:\" + pushErr.Error())\n\t\t}\n\t\tif pushErr = fileOp.SaveFile(path.Join(websiteSSL.Dir, \"fullchain.pem\"), websiteSSL.Pem, constant.FilePerm); pushErr != nil {\n\t\t\tMsgMap[\"status\"] = i18n.GetMsgByKey(\"Failed\")\n\t\t\tlogger.Println(i18n.GetMsgWithMap(\"PushDirLog\", MsgMap))\n\t\t\tlogger.Println(\"Push dir failed:\" + pushErr.Error())\n\t\t}\n\t\tif pushErr == nil {\n\t\t\tlogger.Println(i18n.GetMsgWithMap(\"PushDirLog\", MsgMap))\n\t\t}\n\t}\n}\n\nfunc GetSystemSSL() (bool, uint) {\n\tvar sslSetting model.Setting\n\t_ = global.CoreDB.Model(&model.Setting{}).Where(\"key = ?\", \"SSL\").First(&sslSetting).Error\n\tif sslSetting.Value == \"Enable\" || sslSetting.Value == \"Mux\" {\n\t\tvar sslIDSetting model.Setting\n\t\t_ = global.CoreDB.Model(&model.Setting{}).Where(\"key = ?\", \"SSLID\").First(&sslIDSetting).Error\n\t\tidValue, _ := strconv.Atoi(sslIDSetting.Value)\n\t\tif idValue > 0 {\n\t\t\treturn true, uint(idValue)\n\t\t}\n\t}\n\treturn false, 0\n}\n\nfunc UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {\n\twebsites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))\n\tif len(websites) > 0 {\n\t\tfor _, website := range websites {\n\t\t\tif err := createPemFile(website, websiteSSL); err != nil {\n\t\t\t\treturn buserr.WithMap(\"ErrUpdateWebsiteSSL\", map[string]interface{}{\"name\": website.PrimaryDomain, \"err\": err.Error()}, err)\n\t\t\t}\n\t\t}\n\t\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {\n\t\t\treturn buserr.WithErr(\"ErrSSLApply\", err)\n\t\t}\n\t}\n\tif !global.IsMaster {\n\t\treturn nil\n\t}\n\tenable, sslID := GetSystemSSL()\n\tif enable && sslID == websiteSSL.ID {\n\t\tfileOp := files.NewFileOp()\n\t\tsecretDir := path.Join(global.Dir.DataDir, \"secret\")\n\t\tif err := fileOp.WriteFile(path.Join(secretDir, \"server.crt\"), strings.NewReader(websiteSSL.Pem), 0600); err != nil {\n\t\t\tglobal.LOG.Errorf(\"Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s\", websiteSSL.PrimaryDomain, err.Error())\n\t\t\treturn err\n\t\t}\n\t\tif err := fileOp.WriteFile(path.Join(secretDir, \"server.key\"), strings.NewReader(websiteSSL.PrivateKey), 0600); err != nil {\n\t\t\tglobal.LOG.Errorf(\"Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s\", websiteSSL.PrimaryDomain, err.Error())\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc ChangeHSTSConfig(enable bool, includeSubDomains bool, http3Enable bool, website model.Website) error {\n\tincludeDir := GetSitePath(website, SiteProxyDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(includeDir) {\n\t\treturn nil\n\t}\n\terr := filepath.Walk(includeDir, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !info.IsDir() {\n\t\t\tif filepath.Ext(path) == \".conf\" {\n\t\t\t\tpar, err := parser.NewParser(path)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tconfig, err := par.Parse()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tconfig.FilePath = path\n\t\t\t\tdirectives := config.Directives\n\t\t\t\tlocation, ok := directives[0].(*components.Location)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\t//前置移除HSTS配置\n\t\t\t\tlocation.RemoveDirective(\"add_header\", []string{\"Strict-Transport-Security\", \"\\\"max-age=31536000\\\"\"})\n\t\t\t\tlocation.RemoveDirective(\"add_header\", []string{\"Strict-Transport-Security\", \"\\\"max-age=31536000; includeSubDomains\\\"\"})\n\t\t\t\tif enable {\n\t\t\t\t\tvar hstsValue string\n\t\t\t\t\tif includeSubDomains {\n\t\t\t\t\t\thstsValue = \"\\\"max-age=31536000; includeSubDomains\\\"\"\n\t\t\t\t\t} else {\n\t\t\t\t\t\thstsValue = \"\\\"max-age=31536000\\\"\"\n\t\t\t\t\t}\n\t\t\t\t\tlocation.UpdateDirective(\"add_header\", []string{\"Strict-Transport-Security\", hstsValue})\n\t\t\t\t}\n\n\t\t\t\tif http3Enable {\n\t\t\t\t\tlocation.UpdateDirective(\"add_header\", []string{\"Alt-Svc\", \"'h3=\\\":443\\\"; ma=2592000'\"})\n\t\t\t\t} else {\n\t\t\t\t\tlocation.RemoveDirective(\"add_header\", []string{\"Alt-Svc\", \"'h3=\\\":443\\\"; ma=2592000'\"})\n\t\t\t\t}\n\t\t\t\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\t\t\t\treturn buserr.WithErr(\"ErrUpdateBuWebsite\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc checkSSLStatus(expireDate time.Time) string {\n\tnow := time.Now()\n\tdaysUntilExpiry := int(expireDate.Sub(now).Hours() / 24)\n\n\tif daysUntilExpiry < 0 {\n\t\treturn \"danger\"\n\t} else if daysUntilExpiry <= 10 {\n\t\treturn \"warning\"\n\t}\n\treturn \"success\"\n}\n\nfunc getResourceContent(fileOp files.FileOp, resourcePath string) (string, error) {\n\tif fileOp.Stat(resourcePath) {\n\t\tcontent, err := fileOp.GetContent(resourcePath)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn string(content), nil\n\t}\n\treturn \"\", nil\n}\n\nfunc GetWebSiteRootDir() string {\n\tsiteSetting, _ := settingRepo.Get(settingRepo.WithByKey(\"WEBSITE_DIR\"))\n\tdir := siteSetting.Value\n\tif dir == \"\" {\n\t\tdir = path.Join(global.Dir.DataDir, \"www\")\n\t}\n\treturn dir\n}\n\nfunc GetSiteDir(alias string) string {\n\treturn path.Join(GetWebSiteRootDir(), \"sites\", alias)\n}\n\nconst (\n\tSiteConf              = \"SiteConf\"\n\tSiteAccessLog         = \"access.log\"\n\tSiteErrorLog          = \"error.log\"\n\tWebsiteRootDir        = \"WebsiteRootDir\"\n\tSiteDir               = \"SiteDir\"\n\tSiteIndexDir          = \"SiteIndexDir\"\n\tSiteProxyDir          = \"SiteProxyDir\"\n\tSiteSSLDir            = \"SiteSSLDir\"\n\tSiteReWritePath       = \"SiteReWritePath\"\n\tSiteRedirectDir       = \"SiteRedirectDir\"\n\tSiteCacheDir          = \"SiteCacheDir\"\n\tSiteConfDir           = \"SiteConfDir\"\n\tSitesRootDir          = \"SitesRootDir\"\n\tDefaultDir            = \"DefaultDir\"\n\tDefaultRewriteDir     = \"DefaultRewriteDir\"\n\tSiteRootAuthBasicPath = \"SiteRootAuthBasicPath\"\n\tSitePathAuthBasicDir  = \"SitePathAuthBasicDir\"\n\tSiteUpstreamDir       = \"SiteUpstreamDir\"\n\tSiteCorsPath          = \"SiteCorsPath\"\n\tStreamDir             = \"StreamDir\"\n\tStreamConf            = \"StreamConf\"\n)\n\nfunc GetWebsiteConfigPath(website model.Website) string {\n\tif website.Type != constant.Stream {\n\t\treturn GetSitePath(website, SiteConf)\n\t}\n\treturn GetSitePath(website, StreamConf)\n}\n\nfunc GetSitePath(website model.Website, confType string) string {\n\tswitch confType {\n\tcase SiteConf:\n\t\treturn path.Join(GetWebSiteRootDir(), \"conf.d\", website.Alias+\".conf\")\n\tcase StreamConf:\n\t\treturn path.Join(GetWebSiteRootDir(), \"stream.d\", website.Alias+\".conf\")\n\tcase SiteAccessLog:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"log\", \"access.log\")\n\tcase SiteErrorLog:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"log\", \"error.log\")\n\tcase SiteDir:\n\t\treturn GetSiteDir(website.Alias)\n\tcase SiteIndexDir:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"index\")\n\tcase SiteCacheDir:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"cache\")\n\tcase SiteProxyDir:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"proxy\")\n\tcase SiteSSLDir:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"ssl\")\n\tcase SiteReWritePath:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"rewrite\", website.Alias+\".conf\")\n\tcase SiteRedirectDir:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"redirect\")\n\tcase SiteRootAuthBasicPath:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"auth_basic\", \"auth.pass\")\n\tcase SitePathAuthBasicDir:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"path_auth\")\n\tcase SiteUpstreamDir:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"upstream\")\n\tcase SiteCorsPath:\n\t\treturn path.Join(GetSiteDir(website.Alias), \"cors\", \"cors.conf\")\n\t}\n\treturn \"\"\n}\n\nfunc GetConfDir(website model.Website) string {\n\tif website.Type != constant.Stream {\n\t\treturn GetOpenrestyDir(SiteConf)\n\t}\n\treturn GetOpenrestyDir(StreamDir)\n}\n\nfunc GetOpenrestyDir(confType string) string {\n\tswitch confType {\n\tcase WebsiteRootDir:\n\t\treturn GetWebSiteRootDir()\n\tcase SiteConfDir:\n\t\treturn path.Join(GetWebSiteRootDir(), \"conf.d\")\n\tcase StreamDir:\n\t\treturn path.Join(GetWebSiteRootDir(), \"stream.d\")\n\tcase SitesRootDir:\n\t\treturn path.Join(GetWebSiteRootDir(), \"sites\")\n\tcase DefaultDir:\n\t\treturn path.Join(GetWebSiteRootDir(), \"default\")\n\tcase DefaultRewriteDir:\n\t\treturn path.Join(GetWebSiteRootDir(), \"default\", \"rewrite\")\n\t}\n\treturn \"\"\n}\n\nfunc openProxyCache(website model.Website) error {\n\tcacheDir := GetSitePath(website, SiteCacheDir)\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(cacheDir) {\n\t\t_ = fileOp.CreateDir(cacheDir, constant.DirPerm)\n\t}\n\tcontent, err := fileOp.GetContent(GetSitePath(website, SiteConf))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif strings.Contains(string(content), \"proxy_cache_path\") {\n\t\treturn nil\n\t}\n\tproxyCachePath := fmt.Sprintf(\"/www/sites/%s/cache levels=1:2 keys_zone=proxy_cache_zone_of_%s:5m max_size=1g inactive=24h\", website.Alias, website.Alias)\n\treturn updateNginxConfig(\"\", []dto.NginxParam{{Name: \"proxy_cache_path\", Params: []string{proxyCachePath}}}, &website)\n}\n\nfunc ConfigAllowIPs(ips []string, website model.Website) error {\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnginxConfig := nginxFull.SiteConfig\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\tserver.RemoveDirective(\"allow\", nil)\n\tserver.RemoveDirective(\"deny\", nil)\n\tif len(ips) > 0 {\n\t\tserver.UpdateAllowIPs(ips)\n\t}\n\tif err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(nginxConfig.OldContent, config.FilePath, nginxFull.Install.ContainerName)\n}\n\nfunc GetAllowIps(website model.Website) []string {\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\tdirs := server.GetDirectives()\n\tvar ips []string\n\tfor _, dir := range dirs {\n\t\tif dir.GetName() == \"allow\" {\n\t\t\tips = append(ips, dir.GetParameters()...)\n\t\t}\n\t}\n\treturn ips\n}\n\nfunc ConfigAIProxy(website model.Website) error {\n\tnginxFull, err := getNginxFull(&website)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tconfig := nginxFull.SiteConfig.Config\n\tserver := config.FindServers()[0]\n\tdirs := server.GetDirectives()\n\tfor _, dir := range dirs {\n\t\tif dir.GetName() == \"location\" && dir.GetParameters()[0] == \"/\" {\n\t\t\tserver.UpdateRootProxyForAi([]string{fmt.Sprintf(\"http://%s\", website.Proxy)})\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc handleDefaultOwn(dir string) {\n\tparentDir := path.Dir(dir)\n\tinfo, err := os.Stat(parentDir)\n\tif err != nil {\n\t\treturn\n\t}\n\tstat, ok := info.Sys().(*syscall.Stat_t)\n\tuid, gid := -1, -1\n\tif ok {\n\t\tuid, gid = int(stat.Uid), int(stat.Gid)\n\t}\n\t_ = os.Chown(dir, uid, gid)\n}\n\nfunc getSystemProxy(useProxy bool) *dto.SystemProxy {\n\tif !useProxy {\n\t\treturn nil\n\t}\n\tsettingService := NewISettingService()\n\tsystemProxy, _ := settingService.GetSystemProxy()\n\treturn systemProxy\n}\n\nfunc hasHttp3(params []string) bool {\n\tfor _, param := range params {\n\t\tif param == \"quic\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc hasDefaultServer(params []string) bool {\n\tfor _, param := range params {\n\t\tif param == \"default_server\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc ParseDomain(domain string) (*model.WebsiteDomain, error) {\n\thost, portStr, err := net.SplitHostPort(domain)\n\tif err != nil {\n\t\treturn &model.WebsiteDomain{\n\t\t\tDomain: domain,\n\t\t\tPort:   0,\n\t\t}, nil\n\t}\n\n\tport, err := strconv.Atoi(portStr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid port in domain %s: %v\", domain, err)\n\t}\n\treturn &model.WebsiteDomain{\n\t\tDomain: host,\n\t\tPort:   port,\n\t}, nil\n}\n\nfunc updateWebsiteConfig(website model.Website, updateFunc func(server *components.Server) error) error {\n\tconfigPath := GetSitePath(website, SiteConf)\n\tnginxContent, err := files.NewFileOp().GetContent(configPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tconfig, err := parser.NewStringParser(string(nginxContent)).Parse()\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfig.FilePath = configPath\n\tservers := config.FindServers()\n\tif len(servers) == 0 {\n\t\treturn errors.New(\"nginx config is not valid\")\n\t}\n\tserver := servers[0]\n\tif err := updateFunc(server); err != nil {\n\t\treturn err\n\t}\n\tif err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {\n\t\treturn err\n\t}\n\tnginxInstall, err := getAppInstallByKey(constant.AppOpenresty)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nginxCheckAndReload(string(nginxContent), configPath, nginxInstall.ContainerName)\n}\n\nfunc getServer(website model.Website) (*components.Server, error) {\n\tconfigPath := GetSitePath(website, SiteConf)\n\tnginxContent, err := files.NewFileOp().GetContent(configPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconfig, err := parser.NewStringParser(string(nginxContent)).Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tservers := config.FindServers()\n\tif len(servers) == 0 {\n\t\treturn nil, errors.New(\"nginx config is not valid\")\n\t}\n\tserver := servers[0]\n\treturn server, nil\n}\n\nfunc parseTimeString(input string) (int, string, error) {\n\tinput = strings.TrimSpace(input)\n\n\tmatches := re.GetRegex(re.DurationWithOptionalUnitPattern).FindStringSubmatch(input)\n\n\tif len(matches) < 2 {\n\t\treturn 0, \"\", fmt.Errorf(\"invalid time format: %s\", input)\n\t}\n\n\tvalue, err := strconv.Atoi(matches[1])\n\tif err != nil {\n\t\treturn 0, \"\", fmt.Errorf(\"invalid number: %s\", matches[1])\n\t}\n\n\tunit := matches[2]\n\tif unit == \"\" {\n\t\tunit = \"s\"\n\t}\n\treturn value, unit, nil\n}\n\nfunc parseUpstreamServers(reqServers []dto.NginxUpstreamServer) []*components.UpstreamServer {\n\tvar servers []*components.UpstreamServer\n\tfor _, server := range reqServers {\n\t\tupstreamServer := &components.UpstreamServer{\n\t\t\tAddress: server.Server,\n\t\t}\n\t\tparameters := make(map[string]string)\n\t\tif server.Weight > 0 {\n\t\t\tparameters[\"weight\"] = strconv.Itoa(server.Weight)\n\t\t}\n\t\tif server.MaxFails > 0 {\n\t\t\tparameters[\"max_fails\"] = strconv.Itoa(server.MaxFails)\n\t\t}\n\t\tif server.FailTimeout > 0 {\n\t\t\tparameters[\"fail_timeout\"] = fmt.Sprintf(\"%d%s\", server.FailTimeout, server.FailTimeoutUnit)\n\t\t}\n\t\tif server.MaxConns > 0 {\n\t\t\tparameters[\"max_conns\"] = strconv.Itoa(server.MaxConns)\n\t\t}\n\t\tif server.Flag != \"\" {\n\t\t\tupstreamServer.Flags = []string{server.Flag}\n\t\t}\n\t\tupstreamServer.Parameters = parameters\n\t\tservers = append(servers, upstreamServer)\n\t}\n\treturn servers\n}\n\nfunc getNginxUpstreamServers(upstreamServers []*components.UpstreamServer) []dto.NginxUpstreamServer {\n\tvar servers []dto.NginxUpstreamServer\n\tfor _, ups := range upstreamServers {\n\t\tserver := dto.NginxUpstreamServer{\n\t\t\tServer: ups.Address,\n\t\t}\n\t\tparameters := ups.Parameters\n\t\tif weight, ok := parameters[\"weight\"]; ok {\n\t\t\tnum, err := strconv.Atoi(weight)\n\t\t\tif err == nil {\n\t\t\t\tserver.Weight = num\n\t\t\t}\n\t\t}\n\t\tif maxFails, ok := parameters[\"max_fails\"]; ok {\n\t\t\tnum, err := strconv.Atoi(maxFails)\n\t\t\tif err == nil {\n\t\t\t\tserver.MaxFails = num\n\t\t\t}\n\t\t}\n\t\tif failTimeout, ok := parameters[\"fail_timeout\"]; ok {\n\t\t\tserver.FailTimeout, server.FailTimeoutUnit, _ = parseTimeString(failTimeout)\n\t\t}\n\t\tif maxConns, ok := parameters[\"max_conns\"]; ok {\n\t\t\tnum, err := strconv.Atoi(maxConns)\n\t\t\tif err == nil {\n\t\t\t\tserver.MaxConns = num\n\t\t\t}\n\t\t}\n\t\tfor _, flag := range ups.Flags {\n\t\t\tserver.Flag = flag\n\t\t}\n\t\tservers = append(servers, server)\n\t}\n\treturn servers\n}\n\nfunc getManualWebsiteSSL(req request.WebsiteHTTPSOp) (model.WebsiteSSL, error) {\n\tvar websiteSSL model.WebsiteSSL\n\tvar (\n\t\tcertificate string\n\t\tprivateKey  string\n\t)\n\tswitch req.ImportType {\n\tcase \"paste\":\n\t\tcertificate = req.Certificate\n\t\tprivateKey = req.PrivateKey\n\tcase \"local\":\n\t\tfileOp := files.NewFileOp()\n\t\tif !fileOp.Stat(req.PrivateKeyPath) {\n\t\t\treturn websiteSSL, buserr.New(\"ErrSSLKeyNotFound\")\n\t\t}\n\t\tif !fileOp.Stat(req.CertificatePath) {\n\t\t\treturn websiteSSL, buserr.New(\"ErrSSLCertificateNotFound\")\n\t\t}\n\t\tif content, err := fileOp.GetContent(req.PrivateKeyPath); err != nil {\n\t\t\treturn websiteSSL, err\n\t\t} else {\n\t\t\tprivateKey = string(content)\n\t\t}\n\t\tif content, err := fileOp.GetContent(req.CertificatePath); err != nil {\n\t\t\treturn websiteSSL, err\n\t\t} else {\n\t\t\tcertificate = string(content)\n\t\t}\n\t}\n\n\tprivateKeyCertBlock, _ := pem.Decode([]byte(privateKey))\n\tif privateKeyCertBlock == nil {\n\t\treturn websiteSSL, buserr.New(\"ErrSSLKeyFormat\")\n\t}\n\n\tcertBlock, _ := pem.Decode([]byte(certificate))\n\tif certBlock == nil {\n\t\treturn websiteSSL, buserr.New(\"ErrSSLCertificateFormat\")\n\t}\n\tcert, err := x509.ParseCertificate(certBlock.Bytes)\n\tif err != nil {\n\t\treturn websiteSSL, err\n\t}\n\twebsiteSSL.ExpireDate = cert.NotAfter\n\twebsiteSSL.StartDate = cert.NotBefore\n\twebsiteSSL.Type = cert.Issuer.CommonName\n\tif len(cert.Issuer.Organization) > 0 {\n\t\twebsiteSSL.Organization = cert.Issuer.Organization[0]\n\t} else {\n\t\twebsiteSSL.Organization = cert.Issuer.CommonName\n\t}\n\tif len(cert.DNSNames) > 0 {\n\t\twebsiteSSL.PrimaryDomain = cert.DNSNames[0]\n\t\twebsiteSSL.Domains = strings.Join(cert.DNSNames, \",\")\n\t}\n\twebsiteSSL.Provider = constant.Manual\n\twebsiteSSL.PrivateKey = privateKey\n\twebsiteSSL.Pem = certificate\n\twebsiteSSL.Status = constant.SSLReady\n\treturn websiteSSL, nil\n}\n"
  },
  {
    "path": "agent/app/task/task.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/sirupsen/logrus\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/google/uuid\"\n)\n\ntype ActionFunc func(*Task) error\ntype RollbackFunc func(*Task)\n\ntype Task struct {\n\tTaskCtx context.Context\n\n\tName      string\n\tTaskID    string\n\tLogger    *logrus.Logger\n\tSubTasks  []*SubTask\n\tRollbacks []RollbackFunc\n\tlogFile   *os.File\n\ttaskRepo  repo.ITaskRepo\n\tTask      *model.Task\n\tParentID  string\n}\n\ntype SubTask struct {\n\tRootTask  *Task\n\tName      string\n\tStepAlias string\n\tRetry     int\n\tTimeout   time.Duration\n\tAction    ActionFunc\n\tRollback  RollbackFunc\n\tError     error\n\tIgnoreErr bool\n}\n\nconst (\n\tTaskInstall   = \"TaskInstall\"\n\tTaskUninstall = \"TaskUninstall\"\n\tTaskCreate    = \"TaskCreate\"\n\tTaskDelete    = \"TaskDelete\"\n\tTaskUpgrade   = \"TaskUpgrade\"\n\tTaskUpdate    = \"TaskUpdate\"\n\tTaskRestart   = \"TaskRestart\"\n\tTaskBackup    = \"TaskBackup\"\n\tTaskRecover   = \"TaskRecover\"\n\tTaskRollback  = \"TaskRollback\"\n\tTaskSync      = \"TaskSync\"\n\tTaskBuild     = \"TaskBuild\"\n\tTaskPull      = \"TaskPull\"\n\tTaskImport    = \"TaskImport\"\n\tTaskExport    = \"TaskExport\"\n\tTaskCommit    = \"TaskCommit\"\n\tTaskPush      = \"TaskPush\"\n\tTaskClean     = \"TaskClean\"\n\tTaskHandle    = \"TaskHandle\"\n\tTaskScan      = \"TaskScan\"\n\tTaskExec      = \"TaskExec\"\n\tTaskBatch     = \"TaskBatch\"\n\tTaskProtect   = \"TaskProtect\"\n\tTaskConvert   = \"TaskConvert\"\n\tTaskSwapSet   = \"TaskSwapSet\"\n)\n\nconst (\n\tTaskScopeWebsite          = \"Website\"\n\tTaskScopeAI               = \"AI\"\n\tTaskScopeApp              = \"App\"\n\tTaskScopeRuntime          = \"Runtime\"\n\tTaskScopeDatabase         = \"Database\"\n\tTaskScopeCronjob          = \"Cronjob\"\n\tTaskScopeClam             = \"Clam\"\n\tTaskScopeSystem           = \"System\"\n\tTaskScopeAppStore         = \"AppStore\"\n\tTaskScopeSnapshot         = \"Snapshot\"\n\tTaskScopeContainer        = \"Container\"\n\tTaskScopeCompose          = \"Compose\"\n\tTaskScopeImage            = \"Image\"\n\tTaskScopeBackup           = \"Backup\"\n\tTaskScopeRuntimeExtension = \"RuntimeExtension\"\n\tTaskScopeCustomAppstore   = \"CustomAppstore\"\n\tTaskScopeTamper           = \"Tamper\"\n\tTaskScopeFileConvert      = \"Convert\"\n\tTaskScopeTask             = \"Task\"\n)\n\nfunc GetTaskName(resourceName, operate, scope string) string {\n\treturn fmt.Sprintf(\"%s%s [%s]\", i18n.GetMsgByKey(operate), i18n.GetMsgByKey(scope), resourceName)\n}\n\nfunc NewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) {\n\treturn NewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID)\n}\n\nfunc CheckTaskIsExecuting(name string) error {\n\ttaskRepo := repo.NewITaskRepo()\n\ttask, _ := taskRepo.GetFirst(taskRepo.WithByStatus(constant.StatusExecuting), repo.WithByName(name))\n\tif task.ID != \"\" {\n\t\treturn buserr.New(\"TaskIsExecuting\")\n\t}\n\treturn nil\n}\n\nfunc CheckResourceTaskIsExecuting(operate, scope string, resourceID uint) bool {\n\ttaskRepo := repo.NewITaskRepo()\n\ttask, _ := taskRepo.GetFirst(\n\t\ttaskRepo.WithByStatus(constant.StatusExecuting),\n\t\ttaskRepo.WithResourceID(resourceID),\n\t\ttaskRepo.WithOperate(operate),\n\t\trepo.WithByType(scope))\n\treturn task.ID != \"\"\n}\n\nfunc NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {\n\tif taskID == \"\" {\n\t\ttaskID = uuid.New().String()\n\t}\n\tlogDir := path.Join(global.Dir.TaskDir, taskScope)\n\tif _, err := os.Stat(logDir); os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(logDir, constant.DirPerm); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create log directory: %w\", err)\n\t\t}\n\t}\n\tlogPath := path.Join(global.Dir.TaskDir, taskScope, taskID+\".log\")\n\tlogger := logrus.New()\n\tlogger.SetFormatter(&SimpleFormatter{})\n\tlogFile, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, constant.FilePerm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open log file: %w\", err)\n\t}\n\tlogger.SetOutput(logFile)\n\ttaskModel := &model.Task{\n\t\tID:         taskID,\n\t\tName:       name,\n\t\tType:       taskScope,\n\t\tLogFile:    logPath,\n\t\tStatus:     constant.StatusExecuting,\n\t\tResourceID: resourceID,\n\t\tOperate:    operate,\n\t}\n\ttaskRepo := repo.NewITaskRepo()\n\tctx, cancel := context.WithCancel(context.Background())\n\tglobal.TaskCtxMap[taskID] = cancel\n\ttask := &Task{TaskCtx: ctx, Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: taskModel}\n\treturn task, nil\n}\n\nfunc ReNewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) {\n\treturn ReNewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID)\n}\nfunc ReNewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {\n\ttaskRepo := repo.NewITaskRepo()\n\ttaskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(taskID))\n\tif taskItem.ID == \"\" {\n\t\treturn NewTask(name, operate, taskScope, taskID, resourceID)\n\t}\n\tlogDir := path.Join(global.Dir.TaskDir, taskScope)\n\tif _, err := os.Stat(logDir); err != nil {\n\t\tif err = os.MkdirAll(logDir, constant.DirPerm); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create log directory: %w\", err)\n\t\t}\n\t}\n\n\tlogPath := path.Join(global.Dir.TaskDir, taskScope, taskID+\".log\")\n\tlogger := logrus.New()\n\tlogger.SetFormatter(&SimpleFormatter{})\n\tlogFile, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, constant.FilePerm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open log file: %w\", err)\n\t}\n\tlogger.SetOutput(logFile)\n\tlogger.Print(\"\\n --------------------------------------------------- \\n\")\n\ttaskItem.Status = constant.StatusExecuting\n\ttask := &Task{Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: &taskItem}\n\ttask.updateTask(&taskItem)\n\treturn task, nil\n}\n\nfunc (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {\n\tsubTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 30 * time.Minute, Action: action, Rollback: rollback}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (t *Task) AddSubTaskWithAlias(key string, action ActionFunc, rollback RollbackFunc) {\n\tsubTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), StepAlias: key, Retry: 0, Timeout: 30 * time.Minute, Action: action, Rollback: rollback}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {\n\tsubTask := &SubTask{RootTask: t, Name: name, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (t *Task) AddSubTaskWithAliasAndOps(key string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {\n\tsubTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), StepAlias: key, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (t *Task) AddSubTaskWithIgnoreErr(name string, action ActionFunc) {\n\tsubTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 30 * time.Minute, Action: action, Rollback: nil, IgnoreErr: true}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (s *SubTask) Execute() error {\n\tdefer delete(global.TaskCtxMap, s.RootTask.TaskID)\n\tsubTaskName := s.Name\n\tif s.Name == \"\" {\n\t\tsubTaskName = i18n.GetMsgByKey(\"SubTask\")\n\t}\n\tvar err error\n\tfor i := 0; i < s.Retry+1; i++ {\n\t\tif i > 0 {\n\t\t\ts.RootTask.Log(i18n.GetWithName(\"TaskRetry\", strconv.Itoa(i)))\n\t\t}\n\t\tctx, cancel := context.WithTimeout(context.Background(), s.Timeout)\n\t\tif s.Timeout == 0 {\n\t\t\tctx, cancel = context.WithCancel(context.Background())\n\t\t}\n\t\tdefer cancel()\n\n\t\tdone := make(chan error)\n\t\tgo func() {\n\t\t\tdone <- s.Action(s.RootTask)\n\t\t}()\n\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\ts.RootTask.Log(i18n.GetWithName(\"TaskTimeout\", subTaskName))\n\t\t\terr = errors.New(\"timeout!\")\n\t\tcase err = <-done:\n\t\t\tif err != nil {\n\t\t\t\ts.RootTask.Log(i18n.GetWithNameAndErr(\"SubTaskFailed\", subTaskName, err))\n\t\t\t\tif err.Error() == i18n.GetMsgByKey(\"ErrShutDown\") {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ts.RootTask.Log(i18n.GetWithName(\"SubTaskSuccess\", subTaskName))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tif i == s.Retry {\n\t\t\tif s.Rollback != nil {\n\t\t\t\ts.Rollback(s.RootTask)\n\t\t\t}\n\t\t}\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\treturn err\n}\n\nfunc (t *Task) updateTask(task *model.Task) {\n\t_ = t.taskRepo.Update(context.Background(), task)\n}\n\nfunc (t *Task) Execute() error {\n\tif err := t.taskRepo.Save(context.Background(), t.Task); err != nil {\n\t\treturn err\n\t}\n\tvar err error\n\tt.Log(i18n.GetWithName(\"TaskStart\", t.Name))\n\tfor i := 0; i < len(t.SubTasks); i++ {\n\t\tsubTask := t.SubTasks[i]\n\t\tt.Task.CurrentStep = subTask.StepAlias\n\t\tt.updateTask(t.Task)\n\t\tif err = subTask.Execute(); err == nil {\n\t\t\tif subTask.Rollback != nil {\n\t\t\t\tt.Rollbacks = append(t.Rollbacks, subTask.Rollback)\n\t\t\t}\n\t\t} else {\n\t\t\tif subTask.IgnoreErr {\n\t\t\t\terr = nil\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tt.Task.ErrorMsg = err.Error()\n\t\t\tt.Task.Status = constant.StatusFailed\n\t\t\tfor _, rollback := range t.Rollbacks {\n\t\t\t\trollback(t)\n\t\t\t}\n\t\t\tt.updateTask(t.Task)\n\t\t\tbreak\n\t\t}\n\t}\n\tif t.Task.Status == constant.StatusExecuting {\n\t\tt.Task.Status = constant.StatusSuccess\n\t}\n\tt.Log(\"[TASK-END]\")\n\tt.Task.EndAt = time.Now()\n\tt.updateTask(t.Task)\n\t_ = t.logFile.Close()\n\treturn err\n}\n\nfunc (t *Task) DeleteLogFile() {\n\t_ = os.Remove(t.Task.LogFile)\n}\n\nfunc (t *Task) LogWithStatus(msg string, err error) {\n\tif err != nil {\n\t\tt.Logger.Print(i18n.GetWithNameAndErr(\"FailedStatus\", msg, err))\n\t} else {\n\t\tt.Logger.Print(i18n.GetWithName(\"SuccessStatus\", msg))\n\t}\n}\n\nfunc (t *Task) Log(msg string) {\n\tt.Logger.Print(msg)\n}\n\nfunc (t *Task) Logf(format string, v ...any) {\n\tt.Logger.Printf(format, v...)\n}\n\nfunc (t *Task) LogFailed(msg string) {\n\tt.Logger.Println(msg + i18n.GetMsgByKey(\"Failed\"))\n}\n\nfunc (t *Task) LogFailedWithErr(msg string, err error) {\n\tt.Logger.Printf(\"%s %s : %s\", msg, i18n.GetMsgByKey(\"Failed\"), err.Error())\n}\n\nfunc (t *Task) LogSuccess(msg string) {\n\tt.Logger.Println(msg + \" \" + i18n.GetMsgByKey(\"Success\"))\n}\nfunc (t *Task) LogSuccessF(format string, v ...any) {\n\tt.Logger.Println(fmt.Sprintf(format, v...) + i18n.GetMsgByKey(\"Success\"))\n}\n\nfunc (t *Task) LogStart(msg string) {\n\tt.Logger.Printf(\"%s%s\", i18n.GetMsgByKey(\"Start\"), msg)\n}\n\nfunc (t *Task) LogWithOps(operate, msg string) {\n\tt.Logger.Printf(\"%s%s\", i18n.GetMsgByKey(operate), msg)\n}\n\nfunc (t *Task) LogSuccessWithOps(operate, msg string) {\n\tt.Logger.Printf(\"%s%s%s\", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey(\"Success\"))\n}\n\nfunc (t *Task) LogFailedWithOps(operate, msg string, err error) {\n\tt.Logger.Printf(\"%s%s : %s \", msg, i18n.GetMsgByKey(\"Failed\"), err.Error())\n}\n\nfunc (t *Task) LogWithProgress(msg string, current int, total int) {\n\tconst barWidth = 10\n\tfilled := int(float64(current) / float64(total) * 100 / 10)\n\tif filled > barWidth {\n\t\tfilled = barWidth\n\t}\n\tif filled < 0 {\n\t\tfilled = 0\n\t}\n\tbar := strings.Repeat(\"=\", filled) + strings.Repeat(\"-\", barWidth-filled)\n\tt.Logger.Printf(\"%s [%s] %.2f%%\", msg, bar, float64(current)/float64(total)*100)\n}\n\ntype SimpleFormatter struct{}\n\nfunc (f *SimpleFormatter) Format(entry *logrus.Entry) ([]byte, error) {\n\ttimestamp := entry.Time.Format(\"2006/01/02 15:04:05\")\n\tmessage := fmt.Sprintf(\"%s %s\\n\", timestamp, entry.Message)\n\treturn []byte(message), nil\n}\n"
  },
  {
    "path": "agent/buserr/errors.go",
    "content": "package buserr\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/pkg/errors\"\n)\n\ntype BusinessError struct {\n\tMsg    string\n\tDetail interface{}\n\tMap    map[string]interface{}\n\tErr    error\n}\n\nfunc (e BusinessError) Error() string {\n\tcontent := \"\"\n\tif e.Detail != nil {\n\t\tcontent = i18n.GetErrMsg(e.Msg, map[string]interface{}{\"detail\": e.Detail})\n\t} else if e.Map != nil {\n\t\tcontent = i18n.GetErrMsg(e.Msg, e.Map)\n\t} else {\n\t\tcontent = i18n.GetErrMsg(e.Msg, nil)\n\t}\n\tif content == \"\" {\n\t\tif e.Err != nil {\n\t\t\treturn e.Err.Error()\n\t\t}\n\t\treturn errors.New(e.Msg).Error()\n\t}\n\treturn content\n}\n\nfunc New(Key string) BusinessError {\n\treturn BusinessError{\n\t\tMsg:    Key,\n\t\tDetail: nil,\n\t\tErr:    nil,\n\t}\n}\n\nfunc WithDetail(Key string, detail interface{}, err error) BusinessError {\n\treturn BusinessError{\n\t\tMsg:    Key,\n\t\tDetail: detail,\n\t\tErr:    err,\n\t}\n}\n\nfunc WithErr(Key string, err error) BusinessError {\n\tparamMap := map[string]interface{}{}\n\tif err != nil {\n\t\tparamMap[\"err\"] = err\n\t}\n\treturn BusinessError{\n\t\tMsg: Key,\n\t\tMap: paramMap,\n\t\tErr: err,\n\t}\n}\n\nfunc WithMap(Key string, maps map[string]interface{}, err error) BusinessError {\n\treturn BusinessError{\n\t\tMsg: Key,\n\t\tMap: maps,\n\t\tErr: err,\n\t}\n}\n\nfunc WithNameAndErr(Key string, name string, err error) BusinessError {\n\tparamMap := map[string]interface{}{}\n\tif name != \"\" {\n\t\tparamMap[\"name\"] = name\n\t}\n\tif err != nil {\n\t\tparamMap[\"err\"] = err.Error()\n\t}\n\treturn BusinessError{\n\t\tMsg: Key,\n\t\tMap: paramMap,\n\t\tErr: err,\n\t}\n}\n\nfunc WithName(Key string, name string) BusinessError {\n\tparamMap := map[string]interface{}{}\n\tif name != \"\" {\n\t\tparamMap[\"name\"] = name\n\t}\n\treturn BusinessError{\n\t\tMsg: Key,\n\t\tMap: paramMap,\n\t}\n}\n"
  },
  {
    "path": "agent/buserr/multi_err.go",
    "content": "package buserr\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sort\"\n)\n\ntype MultiErr map[string]error\n\nfunc (e MultiErr) Error() string {\n\tvar keys []string\n\tfor key := range e {\n\t\tkeys = append(keys, key)\n\t}\n\tsort.Strings(keys)\n\n\tbuffer := bytes.NewBufferString(\"\")\n\tfor _, key := range keys {\n\t\tbuffer.WriteString(fmt.Sprintf(\"[%s] %s\\n\", key, e[key]))\n\t}\n\treturn buffer.String()\n}\n"
  },
  {
    "path": "agent/cmd/server/ai/ai.go",
    "content": "package ai\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed compose.yml\nvar DefaultMcpCompose []byte\n\n//go:embed llm-compose.yml\nvar DefaultTensorrtLLMCompose []byte\n"
  },
  {
    "path": "agent/cmd/server/ai/compose.yml",
    "content": "services:\n  mcp-server:\n    image: supercorp/supergateway:latest\n    container_name: ${CONTAINER_NAME}\n    restart: unless-stopped\n    ports:\n      - \"${HOST_IP}:${PANEL_APP_PORT_HTTP}:${PANEL_APP_PORT_HTTP}\"\n    command: [\n      \"--stdio\", \"${COMMAND}\",\n      \"--outputTransport\",\"${OUTPUT_TRANSPORT}\",\n      \"--port\", \"${PANEL_APP_PORT_HTTP}\",\n      \"--baseUrl\", \"${BASE_URL}\",\n      \"--ssePath\", \"${SSE_PATH}\",\n      \"--streamableHttpPath\", \"${STREAMABLE_HTTP_PATH}\",\n      \"--messagePath\", \"${SSE_PATH}/messages\"\n    ]\n    networks:\n      - 1panel-network\nnetworks:\n  1panel-network:\n    external: true"
  },
  {
    "path": "agent/cmd/server/ai/llm-compose.yml",
    "content": "services:\n  tensorrt-llm:\n    image: ${IMAGE}:${VERSION}\n    container_name: ${CONTAINER_NAME}\n    restart: always\n    runtime: nvidia\n    deploy:\n      resources:\n        reservations:\n          devices:\n            - driver: nvidia\n              count: all\n              capabilities: [gpu]\n    networks:\n      - 1panel-network\n    volumes:\n      - ${MODEL_PATH}:${MODEL_PATH}\n    ipc: host\n    ulimits:\n      memlock:\n        soft: -1\n        hard: -1\n      nofile:\n        soft: 65535\n        hard: 65535\n      stack: 67108864\n    command: ${COMMAND}\nnetworks:\n  1panel-network:\n    external: true"
  },
  {
    "path": "agent/cmd/server/cmd/root.go",
    "content": "package cmd\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/server\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar RootCmd = &cobra.Command{\n\tUse: \"1panel-agent\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\tserver.Start()\n\t\treturn nil\n\t},\n}\n"
  },
  {
    "path": "agent/cmd/server/conf/app.yaml",
    "content": "base:\n  install_dir: /opt\n  mode: dev\n  is_demo: false\n  is_offline: false\n\nlog:\n  level: debug\n  time_zone: Asia/Shanghai\n  log_name: 1Panel\n  log_suffix: .log\n  max_backup: 10\n"
  },
  {
    "path": "agent/cmd/server/conf/conf.go",
    "content": "package conf\n\nimport _ \"embed\"\n\n//go:embed app.yaml\nvar AppYaml []byte\n"
  },
  {
    "path": "agent/cmd/server/docs/swagger.go",
    "content": "package docs\n\nimport _ \"embed\"\n\n//go:embed x-log.json\nvar XLogJson []byte\n"
  },
  {
    "path": "agent/cmd/server/docs/x-log.json",
    "content": "{\n\t\"/ai/ollama/close\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"关闭 Ollama 模型连接 [name]\",\n\t\t\"formatEN\": \"close conn for Ollama model [name]\"\n\t},\n\t\"/ai/ollama/model\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 Ollama 模型 [name]\",\n\t\t\"formatEN\": \"add Ollama model [name]\"\n\t},\n\t\"/ai/ollama/model/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"ollama_models\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 Ollama 模型 [names]\",\n\t\t\"formatEN\": \"remove Ollama model [names]\"\n\t},\n\t\"/ai/ollama/model/recreate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 Ollama 模型重试 [name]\",\n\t\t\"formatEN\": \"re-add Ollama model [name]\"\n\t},\n\t\"/ai/ollama/model/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步 Ollama 模型列表\",\n\t\t\"formatEN\": \"sync Ollama model list\"\n\t},\n\t\"/apps/ignored/cancel\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"取消忽略应用升级\",\n\t\t\"formatEN\": \"Cancel ignore application upgrade\"\n\t},\n\t\"/apps/install\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"安装应用 [name]\",\n\t\t\"formatEN\": \"Install app [name]\"\n\t},\n\t\"/apps/installed/config/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"installID\",\n\t\t\t\"webUI\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"应用配置更新 [installID]\",\n\t\t\"formatEN\": \"Application config update [installID]\"\n\t},\n\t\"/apps/installed/ignore\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"忽略应用升级\",\n\t\t\"formatEN\": \"Ignore application upgrade\"\n\t},\n\t\"/apps/installed/op\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"installId\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\"output_column\": \"app_id\",\n\t\t\t\t\"output_value\": \"appId\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"appName\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"appId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"apps\",\n\t\t\t\t\"output_column\": \"key\",\n\t\t\t\t\"output_value\": \"appKey\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[operate] 应用 [appKey][appName]\",\n\t\t\"formatEN\": \"[operate] App [appKey][appName]\"\n\t},\n\t\"/apps/installed/params/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"installId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"应用参数修改 [name]\",\n\t\t\"formatEN\": \"Application param update [name]\"\n\t},\n\t\"/apps/installed/port/change\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"name\",\n\t\t\t\"port\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"应用端口修改 [key]-[name] =\\u003e [port]\",\n\t\t\"formatEN\": \"Application port update [key]-[name] =\\u003e [port]\"\n\t},\n\t\"/apps/installed/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步已安装应用列表\",\n\t\t\"formatEN\": \"Sync the list of installed apps\"\n\t},\n\t\"/apps/sync/local\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"应用商店同步\",\n\t\t\"formatEN\": \"App store synchronization\"\n\t},\n\t\"/apps/sync/remote\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"应用商店同步\",\n\t\t\"formatEN\": \"App store synchronization\"\n\t},\n\t\"/backups\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建备份账号 [type]\",\n\t\t\"formatEN\": \"create backup account [type]\"\n\t},\n\t\"/backups/backup\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"name\",\n\t\t\t\"detailName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"备份 [type] 数据 [name][detailName]\",\n\t\t\"formatEN\": \"backup [type] data [name][detailName]\"\n\t},\n\t\"/backups/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"backup_accounts\",\n\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\"output_value\": \"types\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除备份账号 [types]\",\n\t\t\"formatEN\": \"delete backup account [types]\"\n\t},\n\t\"/backups/record/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"backup_records\",\n\t\t\t\t\"output_column\": \"file_name\",\n\t\t\t\t\"output_value\": \"files\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除备份记录 [files]\",\n\t\t\"formatEN\": \"delete backup records [files]\"\n\t},\n\t\"/backups/record/download\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"source\",\n\t\t\t\"fileName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"下载备份记录 [source][fileName]\",\n\t\t\"formatEN\": \"download backup records [source][fileName]\"\n\t},\n\t\"/backups/recover\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"name\",\n\t\t\t\"detailName\",\n\t\t\t\"file\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"从 [file] 恢复 [type] 数据 [name][detailName]\",\n\t\t\"formatEN\": \"recover [type] data [name][detailName] from [file]\"\n\t},\n\t\"/backups/recover/byupload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"name\",\n\t\t\t\"detailName\",\n\t\t\t\"file\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"从 [file] 恢复 [type] 数据 [name][detailName]\",\n\t\t\"formatEN\": \"recover [type] data [name][detailName] from [file]\"\n\t},\n\t\"/backups/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新备份账号 [types]\",\n\t\t\"formatEN\": \"update backup account [types]\"\n\t},\n\t\"/backups/upload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"filePath\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"上传备份文件 [filePath]\",\n\t\t\"formatEN\": \"upload backup file [filePath]\"\n\t},\n\t\"/containers\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"image\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建容器 [name][image]\",\n\t\t\"formatEN\": \"create container [name][image]\"\n\t},\n\t\"/containers/clean/log\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理容器 [name] 日志\",\n\t\t\"formatEN\": \"clean container [name] logs\"\n\t},\n\t\"/containers/compose\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 compose [name]\",\n\t\t\"formatEN\": \"create compose [name]\"\n\t},\n\t\"/containers/compose/clean/log\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理容器编排 [name] 日志\",\n\t\t\"formatEN\": \"clean compose [name] logs\"\n\t},\n\t\"/containers/compose/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"compose [operation] [name]\",\n\t\t\"formatEN\": \"compose [operation] [name]\"\n\t},\n\t\"/containers/compose/test\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"检测 compose [name] 格式\",\n\t\t\"formatEN\": \"check compose [name]\"\n\t},\n\t\"/containers/compose/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 compose [name]\",\n\t\t\"formatEN\": \"update compose information [name]\"\n\t},\n\t\"/containers/daemonjson/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新配置 [key]\",\n\t\t\"formatEN\": \"Updated configuration [key]\"\n\t},\n\t\"/containers/daemonjson/update/byfile\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新配置文件\",\n\t\t\"formatEN\": \"Updated configuration file\"\n\t},\n\t\"/containers/docker/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"docker 服务 [operation]\",\n\t\t\"formatEN\": \"[operation] docker service\"\n\t},\n\t\"/containers/files/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"containerID\",\n\t\t\t\"paths\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除容器 [containerID] 文件 [paths]\",\n\t\t\"formatEN\": \"Delete files [paths] in container [containerID]\"\n\t},\n\t\"/containers/files/download\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"containerID\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"下载容器 [containerID] 文件 [path]\",\n\t\t\"formatEN\": \"Download file [path] from container [containerID]\"\n\t},\n\t\"/containers/files/upload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"containerID\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"容器 [containerID] 上传文件到 [path]\",\n\t\t\"formatEN\": \"Upload file to [path] in container [containerID]\"\n\t},\n\t\"/containers/image/build\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"构建镜像 [name]\",\n\t\t\"formatEN\": \"build image [name]\"\n\t},\n\t\"/containers/image/load\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"从 [path] 加载镜像\",\n\t\t\"formatEN\": \"load image from [path]\"\n\t},\n\t\"/containers/image/pull\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"repoID\",\n\t\t\t\"imageName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"镜像拉取 [reponame][imageName]\",\n\t\t\"formatEN\": \"image pull [reponame][imageName]\"\n\t},\n\t\"/containers/image/push\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"repoID\",\n\t\t\t\"tagName\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[tagName] 推送到 [reponame][name]\",\n\t\t\"formatEN\": \"push [tagName] to [reponame][name]\"\n\t},\n\t\"/containers/image/remove\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"移除镜像 [names]\",\n\t\t\"formatEN\": \"remove image [names]\"\n\t},\n\t\"/containers/image/save\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"tagName\",\n\t\t\t\"path\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"保留 [tagName] 为 [path]/[name]\",\n\t\t\"formatEN\": \"save [tagName] as [path]/[name]\"\n\t},\n\t\"/containers/image/tag\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"repoID\",\n\t\t\t\"targetName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"tag 镜像 [reponame][targetName]\",\n\t\t\"formatEN\": \"tag image [reponame][targetName]\"\n\t},\n\t\"/containers/ipv6option/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 ipv6 配置\",\n\t\t\"formatEN\": \"Updated the ipv6 option\"\n\t},\n\t\"/containers/logoption/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新日志配置\",\n\t\t\"formatEN\": \"Updated the log option\"\n\t},\n\t\"/containers/network\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建容器网络 name\",\n\t\t\"formatEN\": \"create container network [name]\"\n\t},\n\t\"/containers/network/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除容器网络 [names]\",\n\t\t\"formatEN\": \"delete container network [names]\"\n\t},\n\t\"/containers/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\",\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"容器 [names] 执行 [operation]\",\n\t\t\"formatEN\": \"container [operation] [names]\"\n\t},\n\t\"/containers/prune\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"pruneType\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理容器 [pruneType]\",\n\t\t\"formatEN\": \"clean container [pruneType]\"\n\t},\n\t\"/containers/rename\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"newName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"容器重命名 [name] =\\u003e [newName]\",\n\t\t\"formatEN\": \"rename container [name] =\\u003e [newName]\"\n\t},\n\t\"/containers/repo\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建镜像仓库 [name]\",\n\t\t\"formatEN\": \"create image repo [name]\"\n\t},\n\t\"/containers/repo/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除镜像仓库 [name]\",\n\t\t\"formatEN\": \"delete image repo [name]\"\n\t},\n\t\"/containers/repo/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新镜像仓库 [name]\",\n\t\t\"formatEN\": \"update image repo information [name]\"\n\t},\n\t\"/containers/template\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 compose 模版 [name]\",\n\t\t\"formatEN\": \"create compose template [name]\"\n\t},\n\t\"/containers/template/batch\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"批量导入编排模版\",\n\t\t\"formatEN\": \"batch import compose templates\"\n\t},\n\t\"/containers/template/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"compose_templates\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 compose 模版 [names]\",\n\t\t\"formatEN\": \"delete compose template [names]\"\n\t},\n\t\"/containers/template/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"compose_templates\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新 compose 模版 [name]\",\n\t\t\"formatEN\": \"update compose template information [name]\"\n\t},\n\t\"/containers/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"image\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新容器 [name][image]\",\n\t\t\"formatEN\": \"update container [name][image]\"\n\t},\n\t\"/containers/upgrade\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\",\n\t\t\t\"image\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新容器镜像 [names][image]\",\n\t\t\"formatEN\": \"upgrade container image [names][image]\"\n\t},\n\t\"/containers/volume\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建容器存储卷 [name]\",\n\t\t\"formatEN\": \"create container volume [name]\"\n\t},\n\t\"/containers/volume/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除容器存储卷 [names]\",\n\t\t\"formatEN\": \"delete container volume [names]\"\n\t},\n\t\"/core/backups\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建备份账号 [type]\",\n\t\t\"formatEN\": \"create backup account [type]\"\n\t},\n\t\"/core/backups/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除备份账号 [name]\",\n\t\t\"formatEN\": \"delete backup account [name]\"\n\t},\n\t\"/core/backups/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新备份账号 [types]\",\n\t\t\"formatEN\": \"update backup account [types]\"\n\t},\n\t\"/core/commands\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"command\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建快捷命令 [name][command]\",\n\t\t\"formatEN\": \"create quick command [name][command]\"\n\t},\n\t\"/core/commands/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"commands\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除快捷命令 [names]\",\n\t\t\"formatEN\": \"delete quick command [names]\"\n\t},\n\t\"/core/commands/export\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"导出快速命令\",\n\t\t\"formatEN\": \"export quick commands\"\n\t},\n\t\"/core/commands/import\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"导入快速命令\",\n\t\t\"formatEN\": \"import quick commands\"\n\t},\n\t\"/core/commands/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新快捷命令 [name]\",\n\t\t\"formatEN\": \"update quick command [name]\"\n\t},\n\t\"/core/groups\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建组 [name][type]\",\n\t\t\"formatEN\": \"create group [name][type]\"\n\t},\n\t\"/core/groups/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\"output_value\": \"type\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除组 [type][name]\",\n\t\t\"formatEN\": \"delete group [type][name]\"\n\t},\n\t\"/core/groups/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新组 [name][type]\",\n\t\t\"formatEN\": \"update group [name][type]\"\n\t},\n\t\"/core/hosts\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"addr\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建主机 [name][addr]\",\n\t\t\"formatEN\": \"create host [name][addr]\"\n\t},\n\t\"/core/hosts/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"hosts\",\n\t\t\t\t\"output_column\": \"addr\",\n\t\t\t\t\"output_value\": \"addrs\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除主机 [addrs]\",\n\t\t\"formatEN\": \"delete host [addrs]\"\n\t},\n\t\"/core/hosts/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"addr\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新主机信息 [name][addr]\",\n\t\t\"formatEN\": \"update host [name][addr]\"\n\t},\n\t\"/core/hosts/update/group\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"group\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"hosts\",\n\t\t\t\t\"output_column\": \"addr\",\n\t\t\t\t\"output_value\": \"addr\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"切换主机[addr]分组 =\\u003e [group]\",\n\t\t\"formatEN\": \"change host [addr] group =\\u003e [group]\"\n\t},\n\t\"/core/licenses/add\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加许可证\",\n\t\t\"formatEN\": \"import license\"\n\t},\n\t\"/core/licenses/bind\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"绑定许可证\",\n\t\t\"formatEN\": \"bind license\"\n\t},\n\t\"/core/licenses/bind/free\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改许可证免费节点绑定\",\n\t\t\"formatEN\": \"change bind of free node for license\"\n\t},\n\t\"/core/licenses/del\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除许可证\",\n\t\t\"formatEN\": \"delete license\"\n\t},\n\t\"/core/licenses/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步许可证信息\",\n\t\t\"formatEN\": \"sync license info\"\n\t},\n\t\"/core/licenses/unbind\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"解绑许可证\",\n\t\t\"formatEN\": \"unbind license\"\n\t},\n\t\"/core/licenses/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"替换许可证\",\n\t\t\"formatEN\": \"change license\"\n\t},\n\t\"/core/logs/clean\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"logType\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空 [logType] 日志信息\",\n\t\t\"formatEN\": \"Clean the [logType] log information\"\n\t},\n\t\"/core/script\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加脚本库脚本 [name]\",\n\t\t\"formatEN\": \"add script [name]\"\n\t},\n\t\"/core/script/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"script_librarys\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除脚本库脚本 [names]\",\n\t\t\"formatEN\": \"delete script [names]\"\n\t},\n\t\"/core/script/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步脚本库脚本\",\n\t\t\"formatEN\": \"sync scripts\"\n\t},\n\t\"/core/script/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新脚本库脚本 [name]\",\n\t\t\"formatEN\": \"update script [name]\"\n\t},\n\t\"/core/settings/api/config/generate/key\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"生成 API 接口密钥\",\n\t\t\"formatEN\": \"generate api key\"\n\t},\n\t\"/core/settings/api/config/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ipWhiteList\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 API 接口配置 =\\u003e IP 白名单: [ipWhiteList]\",\n\t\t\"formatEN\": \"update api config =\\u003e IP White List: [ipWhiteList]\"\n\t},\n\t\"/core/settings/bind/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ipv6\",\n\t\t\t\"bindAddress\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统监听信息 =\\u003e ipv6: [ipv6], 监听 IP: [bindAddress]\",\n\t\t\"formatEN\": \"update system bind info =\\u003e ipv6: [ipv6], 监听 IP: [bindAddress]\"\n\t},\n\t\"/core/settings/expired/handle\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"重置过期密码\",\n\t\t\"formatEN\": \"reset an expired Password\"\n\t},\n\t\"/core/settings/memo\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新仪表盘备忘录\",\n\t\t\"formatEN\": \"update dashboard memo\"\n\t},\n\t\"/core/settings/menu/default\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"初始化菜单\",\n\t\t\"formatEN\": \"Init menu.\"\n\t},\n\t\"/core/settings/menu/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"隐藏高级功能菜单\",\n\t\t\"formatEN\": \"Hide advanced feature menu.\"\n\t},\n\t\"/core/settings/mfa/bind\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"mfa 绑定\",\n\t\t\"formatEN\": \"bind mfa\"\n\t},\n\t\"/core/settings/password/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统密码\",\n\t\t\"formatEN\": \"update system password\"\n\t},\n\t\"/core/settings/port/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"serverPort\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统端口 =\\u003e [serverPort]\",\n\t\t\"formatEN\": \"update system port =\\u003e [serverPort]\"\n\t},\n\t\"/core/settings/proxy/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"proxyUrl\",\n\t\t\t\"proxyPort\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"服务器代理配置 [proxyPort]:[proxyPort]\",\n\t\t\"formatEN\": \"set proxy [proxyPort]:[proxyPort].\"\n\t},\n\t\"/core/settings/ssl/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ssl\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统 ssl =\\u003e [ssl]\",\n\t\t\"formatEN\": \"update system ssl =\\u003e [ssl]\"\n\t},\n\t\"/core/settings/terminal/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统终端配置\",\n\t\t\"formatEN\": \"update system terminal setting\"\n\t},\n\t\"/core/settings/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统配置 [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update system setting [key] =\\u003e [value]\"\n\t},\n\t\"/core/settings/upgrade\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"version\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新系统 =\\u003e [version]\",\n\t\t\"formatEN\": \"upgrade system =\\u003e [version]\"\n\t},\n\t\"/core/xpack/nodes\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加节点 [name]\",\n\t\t\"formatEN\": \"add node [name]\"\n\t},\n\t\"/core/xpack/nodes/backup\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"备份主节点数据\",\n\t\t\"formatEN\": \"backup master data\"\n\t},\n\t\"/core/xpack/nodes/backup/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改主节点备份设置\",\n\t\t\"formatEN\": \"update master backup setting\"\n\t},\n\t\"/core/xpack/nodes/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除节点 [names]\",\n\t\t\"formatEN\": \"delete node [names]\"\n\t},\n\t\"/core/xpack/nodes/fix\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修复节点 [name]\",\n\t\t\"formatEN\": \"fix node [name]\"\n\t},\n\t\"/core/xpack/nodes/rollback\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"nodeID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"nodeID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"回滚节点 [name]\",\n\t\t\"formatEN\": \"rollback node [name]\"\n\t},\n\t\"/core/xpack/nodes/simple\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加节点 [name]\",\n\t\t\"formatEN\": \"add node [name]\"\n\t},\n\t\"/core/xpack/nodes/simple/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"simple_nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除节点 [names]\",\n\t\t\"formatEN\": \"delete node [names]\"\n\t},\n\t\"/core/xpack/nodes/simple/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [name]\",\n\t\t\"formatEN\": \"update node [name]\"\n\t},\n\t\"/core/xpack/nodes/simple/update/base\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [name] 基础信息\",\n\t\t\"formatEN\": \"update base info for node [name]\"\n\t},\n\t\"/core/xpack/nodes/sync\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"同步节点数据 [name]\",\n\t\t\"formatEN\": \"sync node [name]\"\n\t},\n\t\"/core/xpack/nodes/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [name]\",\n\t\t\"formatEN\": \"update node [name]\"\n\t},\n\t\"/core/xpack/nodes/update/base\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [name] 基础信息\",\n\t\t\"formatEN\": \"update base info for node [name]\"\n\t},\n\t\"/core/xpack/nodes/upgrade\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"升级节点 [name]\",\n\t\t\"formatEN\": \"upgrade node [name]\"\n\t},\n\t\"/core/xpack/nodes/upgrade/log/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"nodeID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"nodeID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除节点更新日志 [name]\",\n\t\t\"formatEN\": \"delete node upgrade log [name]\"\n\t},\n\t\"/cronjobs\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建计划任务 [type][name]\",\n\t\t\"formatEN\": \"create cronjob [type][name]\"\n\t},\n\t\"/cronjobs/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除计划任务 [names]\",\n\t\t\"formatEN\": \"delete cronjob [names]\"\n\t},\n\t\"/cronjobs/group/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新计划任务分组 [name]\",\n\t\t\"formatEN\": \"update cronjob group [name]\"\n\t},\n\t\"/cronjobs/handle\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"手动执行计划任务 [name]\",\n\t\t\"formatEN\": \"manually execute the cronjob [name]\"\n\t},\n\t\"/cronjobs/records/clean\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"cronjobID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"cronjobID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"清空计划任务记录 [name]\",\n\t\t\"formatEN\": \"clean cronjob [name] records\"\n\t},\n\t\"/cronjobs/status\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"status\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改计划任务 [name] 状态为 [status]\",\n\t\t\"formatEN\": \"change the status of cronjob [name] to [status].\"\n\t},\n\t\"/cronjobs/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新计划任务 [name]\",\n\t\t\"formatEN\": \"update cronjob [name]\"\n\t},\n\t\"/dashboard/app/launcher/show\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"首页应用 [key] =\\u003e 显示：[value]\",\n\t\t\"formatEN\": \"app launcher [key] =\\u003e show: [value]\"\n\t},\n\t\"/dashboard/quick/change\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"切换快速跳转\",\n\t\t\"formatEN\": \"change quick jump\"\n\t},\n\t\"/databases\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 mysql 数据库 [name]\",\n\t\t\"formatEN\": \"create mysql database [name]\"\n\t},\n\t\"/databases/bind\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"database\",\n\t\t\t\"username\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"绑定 mysql 数据库名 [database] [username]\",\n\t\t\"formatEN\": \"bind mysql database [database] [username]\"\n\t},\n\t\"/databases/change/access\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新数据库 [name] 访问权限\",\n\t\t\"formatEN\": \"Update database [name] access\"\n\t},\n\t\"/databases/change/password\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新数据库 [name] 密码\",\n\t\t\"formatEN\": \"Update database [name] password\"\n\t},\n\t\"/databases/common/update/conf\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"database\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [type] 数据库 [database] 配置信息\",\n\t\t\"formatEN\": \"update the [type] [database] database configuration information\"\n\t},\n\t\"/databases/db\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建远程数据库 [name][type]\",\n\t\t\"formatEN\": \"create database [name][type]\"\n\t},\n\t\"/databases/db/check\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"检测远程数据库 [name][type] 连接性\",\n\t\t\"formatEN\": \"check if database [name][type] is connectable\"\n\t},\n\t\"/databases/db/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"databases\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除远程数据库 [names]\",\n\t\t\"formatEN\": \"delete database [names]\"\n\t},\n\t\"/databases/db/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新远程数据库 [name]\",\n\t\t\"formatEN\": \"update database [name]\"\n\t},\n\t\"/databases/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 mysql 数据库 [name]\",\n\t\t\"formatEN\": \"delete mysql database [name]\"\n\t},\n\t\"/databases/description/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"description\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"mysql 数据库 [name] 描述信息修改 [description]\",\n\t\t\"formatEN\": \"The description of the mysql database [name] is modified =\\u003e [description]\"\n\t},\n\t\"/databases/pg\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 postgresql 数据库 [name]\",\n\t\t\"formatEN\": \"create postgresql database [name]\"\n\t},\n\t\"/databases/pg/bind\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"username\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"绑定 postgresql 数据库 [name] 用户 [username]\",\n\t\t\"formatEN\": \"bind postgresql database [name] user [username]\"\n\t},\n\t\"/databases/pg/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 postgresql 数据库 [name]\",\n\t\t\"formatEN\": \"delete postgresql database [name]\"\n\t},\n\t\"/databases/pg/description\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"description\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"postgresql 数据库 [name] 描述信息修改 [description]\",\n\t\t\"formatEN\": \"The description of the postgresql database [name] is modified =\\u003e [description]\"\n\t},\n\t\"/databases/pg/password\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新数据库 [name] 密码\",\n\t\t\"formatEN\": \"Update database [name] password\"\n\t},\n\t\"/databases/pg/privileges\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"database\",\n\t\t\t\"username\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新数据库 [database] 用户 [username] 权限\",\n\t\t\"formatEN\": \"Update [user] privileges of database [database]\"\n\t},\n\t\"/databases/redis/conf/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 redis 数据库配置信息\",\n\t\t\"formatEN\": \"update the redis database configuration information\"\n\t},\n\t\"/databases/redis/password\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 redis 数据库密码\",\n\t\t\"formatEN\": \"change the password of the redis database\"\n\t},\n\t\"/databases/redis/persistence/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"redis 数据库持久化配置更新\",\n\t\t\"formatEN\": \"redis database persistence configuration update\"\n\t},\n\t\"/databases/variables/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"调整 mysql 数据库性能参数\",\n\t\t\"formatEN\": \"adjust mysql database performance parameters\"\n\t},\n\t\"/files\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建文件/文件夹 [path]\",\n\t\t\"formatEN\": \"Create dir or file [path]\"\n\t},\n\t\"/files/batch/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"paths\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"批量删除文件/文件夹 [paths]\",\n\t\t\"formatEN\": \"Batch delete dir or file [paths]\"\n\t},\n\t\"/files/batch/role\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"paths\",\n\t\t\t\"mode\",\n\t\t\t\"user\",\n\t\t\t\"group\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"批量修改文件权限和用户/组 [paths] =\\u003e [mode]/[user]/[group]\",\n\t\t\"formatEN\": \"Batch change file mode and owner [paths] =\\u003e [mode]/[user]/[group]\"\n\t},\n\t\"/files/chunkdownload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"下载文件 [name]\",\n\t\t\"formatEN\": \"Download file [name]\"\n\t},\n\t\"/files/compress\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"压缩文件 [name]\",\n\t\t\"formatEN\": \"Compress file [name]\"\n\t},\n\t\"/files/content\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"获取文件内容 [path]\",\n\t\t\"formatEN\": \"Load file content [path]\"\n\t},\n\t\"/files/decompress\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"解压 [path]\",\n\t\t\"formatEN\": \"Decompress file [path]\"\n\t},\n\t\"/files/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除文件/文件夹 [path]\",\n\t\t\"formatEN\": \"Delete dir or file [path]\"\n\t},\n\t\"/files/depth/size\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"获取目录及其第一层子目录文件夹大小 [path]\",\n\t\t\"formatEN\": \"Multi file size [path]\"\n\t},\n\t\"/files/favorite\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"收藏文件/文件夹 [path]\",\n\t\t\"formatEN\": \"收藏文件/文件夹 [path]\"\n\t},\n\t\"/files/favorite/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"favorites\",\n\t\t\t\t\"output_column\": \"path\",\n\t\t\t\t\"output_value\": \"path\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除收藏 [path]\",\n\t\t\"formatEN\": \"delete avorite [path]\"\n\t},\n\t\"/files/mode\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\",\n\t\t\t\"mode\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改权限 [paths] =\\u003e [mode]\",\n\t\t\"formatEN\": \"Change mode [paths] =\\u003e [mode]\"\n\t},\n\t\"/files/move\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"oldPaths\",\n\t\t\t\"newPath\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"移动文件 [oldPaths] =\\u003e [newPath]\",\n\t\t\"formatEN\": \"Move [oldPaths] =\\u003e [newPath]\"\n\t},\n\t\"/files/owner\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\",\n\t\t\t\"user\",\n\t\t\t\"group\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改用户/组 [paths] =\\u003e [user]/[group]\",\n\t\t\"formatEN\": \"Change owner [paths] =\\u003e [user]/[group]\"\n\t},\n\t\"/files/preview\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"预览文件内容 [path]\",\n\t\t\"formatEN\": \"Preview file content [path]\"\n\t},\n\t\"/files/recycle/clear\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空回收站\",\n\t\t\"formatEN\": \"清空回收站\"\n\t},\n\t\"/files/recycle/reduce\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"还原回收站文件 [name]\",\n\t\t\"formatEN\": \"Reduce RecycleBin file [name]\"\n\t},\n\t\"/files/rename\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"oldName\",\n\t\t\t\"newName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"重命名 [oldName] =\\u003e [newName]\",\n\t\t\"formatEN\": \"Rename [oldName] =\\u003e [newName]\"\n\t},\n\t\"/files/save\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新文件内容 [path]\",\n\t\t\"formatEN\": \"Update file content [path]\"\n\t},\n\t\"/files/size\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"获取文件夹大小 [path]\",\n\t\t\"formatEN\": \"Load file size [path]\"\n\t},\n\t\"/files/upload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\",\n\t\t\t\"file\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"上传文件 [path]/[file]\",\n\t\t\"formatEN\": \"Upload file [path]/[file]\"\n\t},\n\t\"/files/wget\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"url\",\n\t\t\t\"path\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"下载 url =\\u003e [path]/[name]\",\n\t\t\"formatEN\": \"Download url =\\u003e [path]/[name]\"\n\t},\n\t\"/groups\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建组 [name][type]\",\n\t\t\"formatEN\": \"create group [name][type]\"\n\t},\n\t\"/groups/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\"output_value\": \"type\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除组 [type][name]\",\n\t\t\"formatEN\": \"delete group [type][name]\"\n\t},\n\t\"/groups/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新组 [name][type]\",\n\t\t\"formatEN\": \"update group [name][type]\"\n\t},\n\t\"/hosts/disks/mount\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"device\",\n\t\t\t\"mountPoint\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"挂载磁盘 [device] 到 [mountPoint]\",\n\t\t\"formatEN\": \"Mount disk [device] to [mountPoint]\"\n\t},\n\t\"/hosts/disks/partition\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"device\",\n\t\t\t\"filesystem\",\n\t\t\t\"mountPoint\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"对磁盘 [device] 进行分区，文件系统 [filesystem]，挂载点 [mountPoint]\",\n\t\t\"formatEN\": \"Partition disk [device] with filesystem [filesystem], mount point [mountPoint]\"\n\t},\n\t\"/hosts/disks/unmount\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"device\",\n\t\t\t\"mountPoint\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"卸载磁盘 [device] 从 [mountPoint]\",\n\t\t\"formatEN\": \"Unmount disk [device] from [mountPoint]\"\n\t},\n\t\"/hosts/firewall/filter/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] iptables filter 防火墙\",\n\t\t\"formatEN\": \"[operate] iptables filter firewall\"\n\t},\n\t\"/hosts/firewall/filter/rule/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\",\n\t\t\t\"chain\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] filter规则到 [chain]\",\n\t\t\"formatEN\": \"[operation] filter rule to [chain]\"\n\t},\n\t\"/hosts/firewall/forward\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新端口转发规则\",\n\t\t\"formatEN\": \"update port forward rules\"\n\t},\n\t\"/hosts/firewall/ip\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"strategy\",\n\t\t\t\"address\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 ip 规则 [strategy] [address]\",\n\t\t\"formatEN\": \"create address rules [strategy][address]\"\n\t},\n\t\"/hosts/firewall/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] 防火墙\",\n\t\t\"formatEN\": \"[operation] firewall\"\n\t},\n\t\"/hosts/firewall/port\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"port\",\n\t\t\t\"strategy\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加端口规则 [strategy] [port]\",\n\t\t\"formatEN\": \"create port rules [strategy][port]\"\n\t},\n\t\"/hosts/monitor/clean\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空监控数据\",\n\t\t\"formatEN\": \"clean monitor datas\"\n\t},\n\t\"/hosts/monitor/setting/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改默认监控网卡 [name]-[value]\",\n\t\t\"formatEN\": \"update default monitor [name]-[value]\"\n\t},\n\t\"/hosts/ssh/cert\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"生成 SSH 密钥 \",\n\t\t\"formatEN\": \"generate SSH secret\"\n\t},\n\t\"/hosts/ssh/cert/delete\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除 SSH 密钥 \",\n\t\t\"formatEN\": \"delete SSH secret\"\n\t},\n\t\"/hosts/ssh/cert/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步 SSH 密钥 \",\n\t\t\"formatEN\": \"sync SSH secret\"\n\t},\n\t\"/hosts/ssh/cert/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"生成 SSH 密钥 \",\n\t\t\"formatEN\": \"generate SSH secret\"\n\t},\n\t\"/hosts/ssh/file/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 SSH 配置文件 [key]\",\n\t\t\"formatEN\": \"update SSH conf [key]\"\n\t},\n\t\"/hosts/ssh/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] SSH \",\n\t\t\"formatEN\": \"[operation] SSH\"\n\t},\n\t\"/hosts/ssh/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"newValue\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 SSH 配置 [key] =\\u003e [newValue]\",\n\t\t\"formatEN\": \"update SSH setting [key] =\\u003e [newValue]\"\n\t},\n\t\"/hosts/tool/config\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] 主机工具配置文件 \",\n\t\t\"formatEN\": \"[operate] tool config\"\n\t},\n\t\"/hosts/tool/init\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 [type] 配置\",\n\t\t\"formatEN\": \"create [type] config\"\n\t},\n\t\"/hosts/tool/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] [type] \",\n\t\t\"formatEN\": \"[operate] [type]\"\n\t},\n\t\"/hosts/tool/supervisor/process\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] 守护进程 \",\n\t\t\"formatEN\": \"[operate] process\"\n\t},\n\t\"/hosts/tool/supervisor/process/file\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] Supervisor 进程文件 \",\n\t\t\"formatEN\": \"[operate] Supervisor Process Config file\"\n\t},\n\t\"/openresty/build\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"构建 OpenResty\",\n\t\t\"formatEN\": \"Build OpenResty\"\n\t},\n\t\"/openresty/file\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 nginx 配置\",\n\t\t\"formatEN\": \"Update nginx conf\"\n\t},\n\t\"/openresty/modules/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 OpenResty 模块\",\n\t\t\"formatEN\": \"Update OpenResty module\"\n\t},\n\t\"/openresty/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新 nginx 配置 [domain]\",\n\t\t\"formatEN\": \"Update nginx conf [domain]\"\n\t},\n\t\"/process/stop\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"PID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"结束进程 [PID]\",\n\t\t\"formatEN\": \"结束进程 [PID]\"\n\t},\n\t\"/runtimes\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建运行环境 [name]\",\n\t\t\"formatEN\": \"Create runtime [name]\"\n\t},\n\t\"/runtimes/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除运行环境 [name]\",\n\t\t\"formatEN\": \"Delete runtime [name]\"\n\t},\n\t\"/runtimes/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"操作运行环境 [name]\",\n\t\t\"formatEN\": \"Operate runtime [name]\"\n\t},\n\t\"/runtimes/php/config\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[domain] PHP 配置修改\",\n\t\t\"formatEN\": \"[domain] PHP conf update\"\n\t},\n\t\"/runtimes/php/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"php 配置修改 [domain]\",\n\t\t\"formatEN\": \"Nginx conf update [domain]\"\n\t},\n\t\"/runtimes/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新运行环境 [name]\",\n\t\t\"formatEN\": \"Update runtime [name]\"\n\t},\n\t\"/settings/snapshot\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"from\",\n\t\t\t\"description\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建系统快照 [description] 到 [from]\",\n\t\t\"formatEN\": \"Create system backup [description] to [from]\"\n\t},\n\t\"/settings/snapshot/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除系统快照 [name]\",\n\t\t\"formatEN\": \"Delete system backup [name]\"\n\t},\n\t\"/settings/snapshot/description/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"description\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"快照 [name] 描述信息修改 [description]\",\n\t\t\"formatEN\": \"The description of the snapshot [name] is modified =\\u003e [description]\"\n\t},\n\t\"/settings/snapshot/import\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"from\",\n\t\t\t\"names\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"从 [from] 同步系统快照 [names]\",\n\t\t\"formatEN\": \"Sync system snapshots [names] from [from]\"\n\t},\n\t\"/settings/snapshot/recover\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"从系统快照 [name] 恢复\",\n\t\t\"formatEN\": \"Recover from system backup [name]\"\n\t},\n\t\"/settings/snapshot/recreate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"重试创建快照 [name]\",\n\t\t\"formatEN\": \"recrete the snapshot [name]\"\n\t},\n\t\"/settings/snapshot/rollback\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"从系统快照 [name] 回滚\",\n\t\t\"formatEN\": \"Rollback from system backup [name]\"\n\t},\n\t\"/settings/ssh/conn/default\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"defaultConn\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"本地终端默认连接 [defaultConn]\",\n\t\t\"formatEN\": \"update system default conn [defaultConn]\"\n\t},\n\t\"/settings/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统配置 [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update system setting [key] =\\u003e [value]\"\n\t},\n\t\"/toolbox/clam\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建扫描规则 [name][path]\",\n\t\t\"formatEN\": \"create clam [name][path]\"\n\t},\n\t\"/toolbox/clam/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除扫描规则 [names]\",\n\t\t\"formatEN\": \"delete clam [names]\"\n\t},\n\t\"/toolbox/clam/handle\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"执行病毒扫描 [name]\",\n\t\t\"formatEN\": \"handle clam scan [name]\"\n\t},\n\t\"/toolbox/clam/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] Clam\",\n\t\t\"formatEN\": \"[operation] FTP\"\n\t},\n\t\"/toolbox/clam/record/clean\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"清空扫描报告 [name]\",\n\t\t\"formatEN\": \"clean clam record [name]\"\n\t},\n\t\"/toolbox/clam/status/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"status\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改扫描规则 [name] 状态为 [status]\",\n\t\t\"formatEN\": \"change the status of clam [name] to [status].\"\n\t},\n\t\"/toolbox/clam/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改扫描规则 [name][path]\",\n\t\t\"formatEN\": \"update clam [name][path]\"\n\t},\n\t\"/toolbox/clean\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理系统垃圾文件\",\n\t\t\"formatEN\": \"Clean system junk files\"\n\t},\n\t\"/toolbox/device/update/conf\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改主机参数 [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update device conf [key] =\\u003e [value]\"\n\t},\n\t\"/toolbox/device/update/host\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改主机 Host [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update device host [key] =\\u003e [value]\"\n\t},\n\t\"/toolbox/device/update/swap\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] 主机 swap [path]\",\n\t\t\"formatEN\": \"[operate] device swap [path]\"\n\t},\n\t\"/toolbox/fail2ban/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] Fail2ban\",\n\t\t\"formatEN\": \"[operation] Fail2ban\"\n\t},\n\t\"/toolbox/fail2ban/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 Fail2ban 配置 [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update fail2ban conf [key] =\\u003e [value]\"\n\t},\n\t\"/toolbox/ftp\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"user\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 FTP 账户 [user][path]\",\n\t\t\"formatEN\": \"create FTP [user][path]\"\n\t},\n\t\"/toolbox/ftp/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"ftps\",\n\t\t\t\t\"output_column\": \"user\",\n\t\t\t\t\"output_value\": \"users\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 FTP 账户 [users]\",\n\t\t\"formatEN\": \"delete FTP users [users]\"\n\t},\n\t\"/toolbox/ftp/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] FTP\",\n\t\t\"formatEN\": \"[operation] FTP\"\n\t},\n\t\"/toolbox/ftp/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步 FTP 账户\",\n\t\t\"formatEN\": \"sync FTP users\"\n\t},\n\t\"/toolbox/ftp/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"user\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 FTP 账户 [user][path]\",\n\t\t\"formatEN\": \"update FTP [user][path]\"\n\t},\n\t\"/toolbox/scan\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"扫描系统垃圾文件\",\n\t\t\"formatEN\": \"scan System Junk Files\"\n\t},\n\t\"/websites\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"alias\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 [alias]\",\n\t\t\"formatEN\": \"Create website [alias]\"\n\t},\n\t\"/websites/:id/https\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新网站 [domain] https 配置\",\n\t\t\"formatEN\": \"Update website https [domain] conf\"\n\t},\n\t\"/websites/acme\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"email\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 acme [email]\",\n\t\t\"formatEN\": \"Create website acme [email]\"\n\t},\n\t\"/websites/acme/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_acme_accounts\",\n\t\t\t\t\"output_column\": \"email\",\n\t\t\t\t\"output_value\": \"email\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 acme [email]\",\n\t\t\"formatEN\": \"Delete website acme [email]\"\n\t},\n\t\"/websites/acme/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_acme_accounts\",\n\t\t\t\t\"output_column\": \"email\",\n\t\t\t\t\"output_value\": \"email\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新 acme [email]\",\n\t\t\"formatEN\": \"Update acme [email]\"\n\t},\n\t\"/websites/ca\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 ca [name]\",\n\t\t\"formatEN\": \"Create website ca [name]\"\n\t},\n\t\"/websites/ca/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 ca [name]\",\n\t\t\"formatEN\": \"Delete website ca [name]\"\n\t},\n\t\"/websites/ca/download\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"下载 CA 证书文件 [name]\",\n\t\t\"formatEN\": \"download ca file [name]\"\n\t},\n\t\"/websites/ca/obtain\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"自签 SSL 证书 [name]\",\n\t\t\"formatEN\": \"Obtain SSL [name]\"\n\t},\n\t\"/websites/ca/renew\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"自签 SSL 证书 [name]\",\n\t\t\"formatEN\": \"Obtain SSL [name]\"\n\t},\n\t\"/websites/config/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"nginx 配置修改 [domain]\",\n\t\t\"formatEN\": \"Nginx conf update [domain]\"\n\t},\n\t\"/websites/default/html/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新默认 html\",\n\t\t\"formatEN\": \"Update default html\"\n\t},\n\t\"/websites/default/server\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改默认 server =\\u003e [domain]\",\n\t\t\"formatEN\": \"Change default server =\\u003e [domain]\"\n\t},\n\t\"/websites/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 [domain]\",\n\t\t\"formatEN\": \"Delete website [domain]\"\n\t},\n\t\"/websites/dir/permission\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新网站 [domain] 目录权限\",\n\t\t\"formatEN\": \"Update  domain [domain] dir permission\"\n\t},\n\t\"/websites/dir/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新网站 [domain] 目录\",\n\t\t\"formatEN\": \"Update  domain [domain] dir\"\n\t},\n\t\"/websites/dns\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 dns [name]\",\n\t\t\"formatEN\": \"Create website dns [name]\"\n\t},\n\t\"/websites/dns/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_dns_accounts\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 dns [name]\",\n\t\t\"formatEN\": \"Delete website dns [name]\"\n\t},\n\t\"/websites/dns/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站 dns [name]\",\n\t\t\"formatEN\": \"Update website dns [name]\"\n\t},\n\t\"/websites/domains\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"domain\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建域名 [domain]\",\n\t\t\"formatEN\": \"Create domain [domain]\"\n\t},\n\t\"/websites/domains/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_domains\",\n\t\t\t\t\"output_column\": \"domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除域名 [domain]\",\n\t\t\"formatEN\": \"Delete domain [domain]\"\n\t},\n\t\"/websites/domains/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_domains\",\n\t\t\t\t\"output_column\": \"domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新域名 [domain]\",\n\t\t\"formatEN\": \"Update domain [domain]\"\n\t},\n\t\"/websites/log\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[domain][operate] 日志\",\n\t\t\"formatEN\": \"[domain][operate] logs\"\n\t},\n\t\"/websites/nginx/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[domain] Nginx 配置修改\",\n\t\t\"formatEN\": \"[domain] Nginx conf update\"\n\t},\n\t\"/websites/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[operate] 网站 [domain]\",\n\t\t\"formatEN\": \"[operate] website [domain]\"\n\t},\n\t\"/websites/php/version\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"php 版本变更 [domain]\",\n\t\t\"formatEN\": \"php version update [domain]\"\n\t},\n\t\"/websites/proxies/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 [domain] 反向代理配置 [name] \",\n\t\t\"formatEN\": \"Delete domain [domain] proxy config [name]\"\n\t},\n\t\"/websites/proxies/file\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改网站 [domain] 反向代理配置文件 [name] \",\n\t\t\"formatEN\": \"Update domain [domain] proxy config file [name]\"\n\t},\n\t\"/websites/proxies/status\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"name\",\n\t\t\t\"status\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改网站 [domain] 反向代理配置 [name] 状态 [status] \",\n\t\t\"formatEN\": \"Update domain [domain] proxy config [name] status [status]\"\n\t},\n\t\"/websites/proxies/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"name\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改 [operate] 网站 [domain] 反向代理配置 [name] \",\n\t\t\"formatEN\": \"Update [operate] domain [domain] proxy config [name]\"\n\t},\n\t\"/websites/proxy/clear\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理 Openresty 代理缓存\",\n\t\t\"formatEN\": \"Clear nginx proxy cache\"\n\t},\n\t\"/websites/realip/config\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改 [domain] 网站真实IP配置 \",\n\t\t\"formatEN\": \"Modify the real IP configuration of [domain] website\"\n\t},\n\t\"/websites/redirect/file\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新重定向文件 [domain]\",\n\t\t\"formatEN\": \"Nginx conf redirect file update [domain]\"\n\t},\n\t\"/websites/redirect/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改网站 [domain] 重定向配置 \",\n\t\t\"formatEN\": \"Update domain [domain] redirect config\"\n\t},\n\t\"/websites/rewrite/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"伪静态配置修改 [domain]\",\n\t\t\"formatEN\": \"Nginx conf rewrite update [domain]\"\n\t},\n\t\"/websites/ssl\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"primaryDomain\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 ssl [primaryDomain]\",\n\t\t\"formatEN\": \"Create website ssl [primaryDomain]\"\n\t},\n\t\"/websites/ssl/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 ssl [domain]\",\n\t\t\"formatEN\": \"Delete ssl [domain]\"\n\t},\n\t\"/websites/ssl/download\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"下载证书文件 [domain]\",\n\t\t\"formatEN\": \"download ssl file [domain]\"\n\t},\n\t\"/websites/ssl/obtain\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"申请证书  [domain]\",\n\t\t\"formatEN\": \"apply ssl [domain]\"\n\t},\n\t\"/websites/ssl/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新证书设置 [domain]\",\n\t\t\"formatEN\": \"Update ssl config [domain]\"\n\t},\n\t\"/websites/ssl/upload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"上传 ssl [type]\",\n\t\t\"formatEN\": \"Upload ssl [type]\"\n\t},\n\t\"/websites/ssl/upload/file\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"上传 ssl 文件 [type]\",\n\t\t\"formatEN\": \"Upload ssl file [type]\"\n\t},\n\t\"/websites/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"primaryDomain\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站 [primaryDomain]\",\n\t\t\"formatEN\": \"Update website [primaryDomain]\"\n\t},\n\t\"/xpack/monitor/config/global\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新全局设置\",\n\t\t\"formatEN\": \"update global config\"\n\t},\n\t\"/xpack/monitor/log/clear\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空日志\",\n\t\t\"formatEN\": \"clear log\"\n\t},\n\t\"/xpack/monitor/site/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站设置\",\n\t\t\"formatEN\": \"update website config\"\n\t},\n\t\"/xpack/tampers/template\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"content\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"保存防篡改模版 [name] - [content]\",\n\t\t\"formatEN\": \"save tamper template info  [name] - [content]\"\n\t},\n\t\"/xpack/tampers/template/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"tampers\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除防篡改模版 [name]\",\n\t\t\"formatEN\": \"delete tamper template [name]\"\n\t},\n\t\"/xpack/tampers/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"website\",\n\t\t\t\"status\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新防篡改信息 [website][path] =\\u003e [status]\",\n\t\t\"formatEN\": \"update tamper info [website][path] =\\u003e [status]\"\n\t},\n\t\"/xpack/waf/acl/create\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建自定义规则 [name]\",\n\t\t\"formatEN\": \"create acl [name]\"\n\t},\n\t\"/xpack/waf/acl/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除自定义规则 [name]\",\n\t\t\"formatEN\": \"delete acl [name]\"\n\t},\n\t\"/xpack/waf/acl/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新自定义规则 [name]\",\n\t\t\"formatEN\": \"update acl [name]\"\n\t},\n\t\"/xpack/waf/block/remove\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ip\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"解封 IP [ip]\",\n\t\t\"formatEN\": \"unblock ip [ip]\"\n\t},\n\t\"/xpack/waf/cdn/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 CDN 配置\",\n\t\t\"formatEN\": \"update CDN config\"\n\t},\n\t\"/xpack/waf/config/global/state\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\",\n\t\t\t\"state\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [state]全局配置[scope]\",\n\t\t\"formatEN\": \"update [state] global config [scope]\"\n\t},\n\t\"/xpack/waf/config/website/state\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\",\n\t\t\t\"state\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [state]网站配置[scope]\",\n\t\t\"formatEN\": \"update [state] website config [scope]\"\n\t},\n\t\"/xpack/waf/html/revert\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"回滚拦截页面 [name]\",\n\t\t\"formatEN\": \"revert html res [name]\"\n\t},\n\t\"/xpack/waf/html/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新拦截页面 [name]\",\n\t\t\"formatEN\": \"update html res [name]\"\n\t},\n\t\"/xpack/waf/ip/default\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ip\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 IP [ip] 到默认[type]组\",\n\t\t\"formatEN\": \"add ip [ip] to default[type]group\"\n\t},\n\t\"/xpack/waf/ip/group/create\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 IP 组 [name]\",\n\t\t\"formatEN\": \"create ip group [name]\"\n\t},\n\t\"/xpack/waf/ip/group/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除 IP 组 [name]\",\n\t\t\"formatEN\": \"delete ip group [name]\"\n\t},\n\t\"/xpack/waf/ip/group/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 IP 组 [name]\",\n\t\t\"formatEN\": \"update ip group [name]\"\n\t},\n\t\"/xpack/waf/location/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新[type]地址库信息\",\n\t\t\"formatEN\": \"update [type] location\"\n\t},\n\t\"/xpack/waf/log/clear\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空 WAF 日志\",\n\t\t\"formatEN\": \"clear waf log\"\n\t},\n\t\"/xpack/waf/rule/cc\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新[scope]频率访问限制\",\n\t\t\"formatEN\": \"update [scope] CC config\"\n\t},\n\t\"/xpack/waf/rule/common/create\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建规则 [scope]\",\n\t\t\"formatEN\": \"create rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/common/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除规则 [scope]\",\n\t\t\"formatEN\": \"delete rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/common/export\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"导出规则 [scope]\",\n\t\t\"formatEN\": \"export rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/common/import\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"导入规则 [scope]\",\n\t\t\"formatEN\": \"import rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/common/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新规则 [scope]\",\n\t\t\"formatEN\": \"update rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/geo\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新地区访问限制\",\n\t\t\"formatEN\": \"update geo restrict\"\n\t},\n\t\"/xpack/waf/rule/ip/create\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建[scope] IP 规则\",\n\t\t\"formatEN\": \"create [scope] IP rule\"\n\t},\n\t\"/xpack/waf/rule/ip/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除[scope] IP 规则\",\n\t\t\"formatEN\": \"delete [scope] IP rule\"\n\t},\n\t\"/xpack/waf/rule/ip/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新[scope] IP 规则\",\n\t\t\"formatEN\": \"update [scope] IP rule\"\n\t},\n\t\"/xpack/waf/rule/log\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新日志配置\",\n\t\t\"formatEN\": \"update waf log config\"\n\t},\n\t\"/xpack/waf/rule/urlcc/create\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 URL 频率访问限制规则\",\n\t\t\"formatEN\": \"create URL CC rule\"\n\t},\n\t\"/xpack/waf/rule/urlcc/delete\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除 URL 频率访问限制规则\",\n\t\t\"formatEN\": \"delete URL CC rule\"\n\t},\n\t\"/xpack/waf/rule/urlcc/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 URL 频率访问限制规则\",\n\t\t\"formatEN\": \"update URL CC rule\"\n\t},\n\t\"/xpack/waf/url/default\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"url\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 URL [url] 到默认[type]组\",\n\t\t\"formatEN\": \"add URL [url] to default[type]group\"\n\t},\n\t\"/xpack/website/rule/cc\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站频率访问限制\",\n\t\t\"formatEN\": \"update website CC config\"\n\t},\n\t\"/xpack/website/rule/geo\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站地区访问限制\",\n\t\t\"formatEN\": \"update geo restrict\"\n\t}\n}"
  },
  {
    "path": "agent/cmd/server/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/cmd\"\n\t\"os\"\n)\n\n// @title 1Panel\n// @version 2.0\n// @description Top-Rated Web-based Linux Server Management Tool\n// @termsOfService http://swagger.io/terms/\n// @license.name GPL-3.0\n// @license.url https://www.gnu.org/licenses/gpl-3.0.html\n// @BasePath /api/v2\n// @schemes http https\n\n// @securityDefinitions.apikey ApiKeyAuth\n// @description Custom Token Format, Format: md5('1panel' + API-Key + UnixTimestamp).\n// @description ```\n// @description eg:\n// @description curl -X GET \"http://{host}:{port}/api/v2/toolbox/device/base\" \\\n// @description -H \"1Panel-Token: <1panel_token>\" \\\n// @description -H \"1Panel-Timestamp: <current_unix_timestamp>\"\n// @description ```\n// @description - `1Panel-Token` is the key for the panel API Key.\n// @type apiKey\n// @in Header\n// @name 1Panel-Token\n// @securityDefinitions.apikey Timestamp\n// @type apiKey\n// @in header\n// @name 1Panel-Timestamp\n// @description - `1Panel-Timestamp` is the Unix timestamp of the current time in seconds.\n\nfunc main() {\n\tif err := cmd.RootCmd.Execute(); err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/404.html",
    "content": "<html>\n<head><title>404 Not Found</title></head>\n<body>\n<center><h1>404 Not Found</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "agent/cmd/server/nginx_conf/cache.conf",
    "content": "proxy_temp_path /www/common/proxy/proxy_temp_dir;\nproxy_cache_path /www/common/proxy/proxy_cache_dir levels=1:2 keys_zone=proxy_cache_panel:20m inactive=1d max_size=5g;\nclient_body_buffer_size 512k;\nproxy_connect_timeout 60;\nproxy_read_timeout 60;\nproxy_send_timeout 60;\nproxy_buffer_size 32k;\nproxy_buffers 4 64k;\nproxy_busy_buffers_size 128k;\nproxy_temp_file_write_size 128k;\nproxy_next_upstream error timeout invalid_header http_500 http_503 http_404;\nproxy_cache proxy_cache_panel;"
  },
  {
    "path": "agent/cmd/server/nginx_conf/domain404.html",
    "content": "<html>\n<head><title>404 Not Found</title></head>\n<body>\n<center><h1>404 Not Found</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "agent/cmd/server/nginx_conf/gzip.conf",
    "content": "gzip                on;\ngzip_comp_level     6;\ngzip_min_length     1k;\ngzip_types          text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype;"
  },
  {
    "path": "agent/cmd/server/nginx_conf/index.html",
    "content": "<!doctype html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"robots\" content=\"noindex,nofollow\" />\n    <title>恭喜，站点创建成功！</title>\n    <style>\n        .container {\n            width: 60%;\n            margin: 10% auto 0;\n            background-color: #f0f0f0;\n            padding: 2% 5%;\n            border-radius: 10px\n        }\n\n        ul {\n            padding-left: 20px;\n        }\n\n        ul li {\n            line-height: 2.3\n        }\n\n        a {\n            color: #20a53a\n        }\n    </style>\n</head>\n<body>\n<div class=\"container\">\n    <h1>恭喜, 站点创建成功！</h1>\n    <h3>这是默认index.html，本页面由系统自动生成</h3>\n</div>\n</body>\n</html>"
  },
  {
    "path": "agent/cmd/server/nginx_conf/index.php",
    "content": "<?php\nerror_reporting(E_ALL);\nini_set('display_errors', 1);\n\necho '<h1 style=\"text-align: center;\">欢迎使用 PHP！</h1>';\necho '<h2>版本信息</h2>';\n\necho '<ul>';\necho '<li>PHP版本：', PHP_VERSION, '</li>';\necho '</ul>';\n\necho '<h2>已安装扩展</h2>';\nprintExtensions();\n\n/**\n * 获取已安装扩展列表\n */\nfunction printExtensions()\n{\n    echo '<ol>';\n    foreach (get_loaded_extensions() as $i => $name) {\n        echo \"<li>\", $name, '=', phpversion($name), '</li>';\n    }\n    echo '</ol>';\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/nginx_conf.go",
    "content": "package nginx_conf\n\nimport (\n\t\"embed\"\n\t\"io\"\n)\n\n//go:embed ssl.conf\nvar SSL []byte\n\n//go:embed index.html\nvar Index []byte\n\n//go:embed index.php\nvar IndexPHP []byte\n\n//go:embed rewrite/*\nvar Rewrites embed.FS\n\n//go:embed cache.conf\nvar Cache []byte\n\n//go:embed proxy.conf\nvar Proxy []byte\n\n//go:embed proxy_cache.conf\nvar ProxyCache []byte\n\n//go:embed 404.html\nvar NotFoundHTML []byte\n\n//go:embed domain404.html\nvar DomainNotFoundHTML []byte\n\n//go:embed stop.html\nvar StopHTML []byte\n\n//go:embed path_auth.conf\nvar PathAuth []byte\n\n//go:embed upstream.conf\nvar Upstream []byte\n\n//go:embed sse.conf\nvar SSE []byte\n\n//go:embed *.json *.conf\nvar websitesFiles embed.FS\n\nfunc GetWebsiteFile(filename string) []byte {\n\tfile, err := websitesFiles.Open(filename)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tdefer file.Close()\n\tres, _ := io.ReadAll(file)\n\treturn res\n}\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/path_auth.conf",
    "content": "location ~* ^/test* {\n    auth_basic \"Authorization\";\n    auth_basic_user_file /www/site/pass/ceshi.pass ;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/php-fpm.conf",
    "content": "[global]\nerror_log = /var/log/php/fpm.error.log\nlog_level = notice\n[www]\nuser = www-data\ngroup = www-data\nlisten = 0.0.0.0:9000\npm = dynamic\npm.max_children = 10\npm.start_servers = 2\npm.min_spare_servers = 1\npm.max_spare_servers = 3\npm.status_path = /status\nslowlog = /var/log/php/fpm.slow.log\nrequest_slowlog_timeout = 3\ncatch_workers_output = yes"
  },
  {
    "path": "agent/cmd/server/nginx_conf/php_extensions.json",
    "content": "[\n  {\n    \"name\": \"amqp\",\n    \"check\": \"amqp\",\n    \"file\": \"amqp.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"apcu\",\n    \"check\": \"apcu\",\n    \"file\": \"apcu.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"bcmath\",\n    \"check\": \"bcmath\",\n    \"file\": \"bcmath.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"ionCube\",\n    \"check\": \"ionCube Loader\",\n    \"file\": \"ioncube_loader.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"81\", \"82\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"opcache\",\n    \"check\": \"Zend OPcache\",\n    \"file\": \"opcache.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"memcache\",\n    \"check\": \"memcache\",\n    \"file\": \"memcache.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"memcached\",\n    \"check\": \"memcached\",\n    \"file\": \"memcached.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"redis\",\n    \"check\": \"redis\",\n    \"file\": \"redis.so\",\n    \"versions\": [ \"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"mcrypt\",\n    \"check\": \"mcrypt\",\n    \"file\": \"mcrypt.so\",\n    \"versions\": [\"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n\n  {\n    \"name\": \"imagick\",\n    \"check\": \"imagick\",\n    \"file\": \"imagick.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\",\"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"xdebug\",\n    \"check\": \"xdebug\",\n    \"file\": \"xdebug.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"imap\",\n    \"check\": \"imap\",\n    \"file\": \"imap.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"exif\",\n    \"check\": \"exif\",\n    \"file\": \"exif.so\",\n    \"versions\": [ \"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"intl\",\n    \"check\": \"intl\",\n    \"file\": \"intl.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"xsl\",\n    \"check\": \"xsl\",\n    \"file\": \"xsl.so\",\n    \"versions\": [\"56\", \"70\", \"71\", \"72\", \"73\", \"74\", \"80\", \"81\", \"82\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"swoole\",\n    \"check\": \"swoole\",\n    \"file\": \"swoole.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"zstd\",\n    \"check\": \"zstd\",\n    \"file\": \"zstd.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"xlswriter\",\n    \"check\": \"xlswriter\",\n    \"file\": \"xlswriter.so\",\n    \"versions\": [\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"oci8\",\n    \"check\": \"oci8\",\n    \"file\": \"oci8.so\",\n    \"versions\": [\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"pdo_oci\",\n    \"check\": \"pdo_oci\",\n    \"file\": \"pdo_oci.so\",\n    \"versions\": [\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"pdo_sqlsrv\",\n    \"check\": \"pdo_sqlsrv\",\n    \"file\": \"pdo_sqlsrv.so\",\n    \"versions\": [\"70\", \"71\", \"72\",  \"73\", \"74\", \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"sqlsrv\",\n    \"check\": \"sqlsrv\",\n    \"file\": \"sqlsrv.so\",\n    \"versions\": [\"81\", \"82\", \"83\",\"84\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"yaf\",\n    \"check\": \"yaf\",\n    \"file\": \"yaf.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"mongodb\",\n    \"check\": \"mongodb\",\n    \"file\": \"mongodb.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"yac\",\n    \"check\": \"yac\",\n    \"file\": \"yac.so\",\n    \"versions\": [\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"pgsql\",\n    \"check\": \"pgsql\",\n    \"file\": \"pgsql.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"ssh2\",\n    \"check\": \"ssh2\",\n    \"file\": \"ssh2.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"grpc\",\n    \"check\": \"grpc\",\n    \"file\": \"grpc.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"xhprof\",\n    \"check\": \"xhprof\",\n    \"file\": \"xhprof.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"protobuf\",\n    \"check\": \"protobuf\",\n    \"file\": \"protobuf.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"pdo_pgsql\",\n    \"check\": \"pdo_pgsql\",\n    \"file\": \"pdo_pgsql.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"snmp\",\n    \"check\": \"snmp\",\n    \"file\": \"snmp.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"ldap\",\n    \"check\": \"ldap\",\n    \"file\": \"ldap.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"recode\",\n    \"check\": \"recode\",\n    \"file\": \"recode.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"enchant\",\n    \"check\": \"enchant\",\n    \"file\": \"enchant.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"pspell\",\n    \"check\": \"pspell\",\n    \"file\": \"pspell.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"bz2\",\n    \"check\": \"bz2\",\n    \"file\": \"bz2.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"sysvshm\",\n    \"check\": \"sysvshm\",\n    \"file\": \"sysvshm.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"calendar\",\n    \"check\": \"calendar\",\n    \"file\": \"calendar.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"gmp\",\n    \"check\": \"gmp\",\n    \"file\": \"gmp.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"wddx\",\n    \"check\": \"wddx\",\n    \"file\": \"wddx.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"sysvmsg\",\n    \"check\": \"sysvmsg\",\n    \"file\": \"sysvmsg.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"igbinary\",\n    \"check\": \"igbinary\",\n    \"file\": \"igbinary.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"zmq\",\n    \"check\": \"zmq\",\n    \"file\": \"zmq.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"smbclient\",\n    \"check\": \"smbclient\",\n    \"file\": \"smbclient.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"event\",\n    \"check\": \"event\",\n    \"file\": \"event.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"mailparse\",\n    \"check\": \"mailparse\",\n    \"file\": \"mailparse.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"yaml\",\n    \"check\": \"yaml\",\n    \"file\": \"yaml.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"sg16\",\n    \"check\": \"SourceGuardian\",\n    \"file\": \"sourceguardian.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"mysqli\",\n    \"check\": \"mysqli\",\n    \"file\": \"mysqli.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"pdo_mysql\",\n    \"check\": \"pdo_mysql\",\n    \"file\": \"pdo_mysql.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"zip\",\n    \"check\": \"zip\",\n    \"file\": \"zip.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"shmop\",\n    \"check\": \"shmop\",\n    \"file\": \"shmop.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"gd\",\n    \"check\": \"gd\",\n    \"file\": \"gd.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n\n  {\n    \"name\": \"pcntl\",\n    \"check\": \"pcntl\",\n    \"file\": \"pcntl.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"sodium\",\n    \"check\": \"sodium\",\n    \"file\": \"sodium.so\",\n    \"versions\": [\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"gettext\",\n    \"check\": \"gettext\",\n    \"file\": \"gettext.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"soap\",\n    \"check\": \"soap\",\n    \"file\": \"soap.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"sysvsem\",\n    \"check\": \"sysvsem\",\n    \"file\": \"sysvsem.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"sockets\",\n    \"check\": \"sockets\",\n    \"file\": \"sockets.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"xmlrpc\",\n    \"check\": \"xmlrpc\",\n    \"file\": \"xmlrpc.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  },\n  {\n    \"name\": \"lz4\",\n    \"check\": \"lz4\",\n    \"file\": \"lz4.so\",\n    \"versions\": [\"56\",\"70\", \"71\", \"72\",  \"73\", \"74\",  \"80\", \"81\", \"82\", \"83\",\"84\",\"85\"],\n    \"installed\": false\n  }\n]\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/proxy.conf",
    "content": "location ^~ /test {\n    proxy_pass http://1panel.cloud/;\n    proxy_set_header Host $host;\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header REMOTE-HOST $remote_addr;\n    proxy_set_header Upgrade $http_upgrade;\n    proxy_set_header Connection $http_connection;\n    proxy_set_header X-Forwarded-Proto $scheme;\n    proxy_set_header X-Forwarded-Port $server_port;\n    proxy_http_version 1.1;\n\n    add_header X-Cache $upstream_cache_status;\n    proxy_ssl_server_name off; \n    proxy_ssl_name $proxy_host;\n}\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/proxy_cache.conf",
    "content": "proxy_ignore_headers Set-Cookie Cache-Control expires;\nproxy_cache cache_one;\nproxy_cache_key $host$uri$is_args$args;\nproxy_cache_valid 200 304 301 302 10m;"
  },
  {
    "path": "agent/cmd/server/nginx_conf/proxy_no_cache.conf",
    "content": "set $static_fileg 0;\nif ( $uri ~* \"\\.(gif|png|jpg|css|js|woff|woff2)$\" )\n{\n    set $static_fileg 1;\n    expires 1m;\n}\nif ( $static_fileg = 0 )\n{\n    add_header Cache-Control no-cache;\n}\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/crmeb.conf",
    "content": "location / {\n   if (!-e $request_filename) {\n   \trewrite  ^(.*)$  /index.php?s=/$1  last;\n   \tbreak;\n   }\n}\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/dabr.conf",
    "content": "location / {\nif (!-e $request_filename) {\nrewrite ^/(.*)$ /index.php?q=$1 last;\n}\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/dbshop.conf",
    "content": "location /{\n    try_files $uri $uri/ /index.php$is_args$args;\n}\n\nlocation ~ \\.htaccess{\n    deny all;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/dedecms.conf",
    "content": "rewrite \"^/list-([0-9]+)\\.html$\" /plus/list.php?tid=$1 last;\nrewrite \"^/list-([0-9]+)-([0-9]+)-([0-9]+)\\.html$\" /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last;\nrewrite \"^/view-([0-9]+)-1\\.html$\" /plus/view.php?arcID=$1 last;\nrewrite \"^/view-([0-9]+)-([0-9]+)\\.html$\" /plus/view.php?aid=$1&pageno=$2 last;\nrewrite \"^/plus/list-([0-9]+)\\.html$\" /plus/list.php?tid=$1 last;\nrewrite \"^/plus/list-([0-9]+)-([0-9]+)-([0-9]+)\\.html$\" /plus/list.php?tid=$1&totalresult=$2&PageNo=$3 last;\nrewrite \"^/plus/view-([0-9]+)-1\\.html$\" /plus/view.php?arcID=$1 last;\nrewrite \"^/plus/view-([0-9]+)-([0-9]+)\\.html$\" /plus/view.php?aid=$1&pageno=$2 last;\nrewrite \"^/tags.html$\" /tags.php last;\nrewrite \"^/tag-([0-9]+)-([0-9]+)\\.html$\" /tags.php?/$1/$2/ last;\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/default.conf",
    "content": ""
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/discuz.conf",
    "content": "location / {\n            rewrite ^/archiver/((fid|tid)-[\\w\\-]+\\.html)$ /archiver/index.php?$1 last;\n            rewrite ^/forum-([0-9]+)-([0-9]+)\\.html$ /forumdisplay.php?fid=$1&page=$2 last;\n            rewrite ^/thread-([0-9]+)-([0-9]+)-([0-9]+)\\.html$ /viewthread.php?tid=$1&extra=page%3D$3&page=$2 last;\n            rewrite ^/space-(username|uid)-(.+)\\.html$ /space.php?$1=$2 last;\n            rewrite ^/tag-(.+)\\.html$ /tag.php?name=$1 last;\n        }"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/discuzx.conf",
    "content": "rewrite ^([^\\.]*)/topic-(.+)\\.html$ $1/portal.php?mod=topic&topic=$2 last;\nrewrite ^([^\\.]*)/article-([0-9]+)-([0-9]+)\\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last;\nrewrite ^([^\\.]*)/forum-(\\w+)-([0-9]+)\\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;\nrewrite ^([^\\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;\nrewrite ^([^\\.]*)/group-([0-9]+)-([0-9]+)\\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;\nrewrite ^([^\\.]*)/space-(username|uid)-(.+)\\.html$ $1/home.php?mod=space&$2=$3 last;\nrewrite ^([^\\.]*)/blog-([0-9]+)-([0-9]+)\\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last;\nrewrite ^([^\\.]*)/(fid|tid)-([0-9]+)\\.html$ $1/index.php?action=$2&value=$3 last;\nrewrite ^([^\\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\\-]+)\\.html$ $1/plugin.php?id=$2:$3 last;\nif (!-e $request_filename) {\n\treturn 404;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/discuzx2.conf",
    "content": "location /bbs/ {\n\trewrite ^([^\\.]*)/topic-(.+)\\.html$ $1/portal.php?mod=topic&topic=$2 last;\n\trewrite ^([^\\.]*)/article-([0-9]+)-([0-9]+)\\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last;\n\trewrite ^([^\\.]*)/forum-(\\w+)-([0-9]+)\\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;\n\trewrite ^([^\\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;\n\trewrite ^([^\\.]*)/group-([0-9]+)-([0-9]+)\\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;\n\trewrite ^([^\\.]*)/space-(username|uid)-(.+)\\.html$ $1/home.php?mod=space&$2=$3 last;\n\trewrite ^([^\\.]*)/blog-([0-9]+)-([0-9]+)\\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last;\n\trewrite ^([^\\.]*)/(fid|tid)-([0-9]+)\\.html$ $1/index.php?action=$2&value=$3 last;\n\trewrite ^([^\\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\\-]+)\\.html$ $1/plugin.php?id=$2:$3 last;\n\tif (!-e $request_filename) {\n\t\treturn 404;\n\t}\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/discuzx3.conf",
    "content": "location / {\n\trewrite ^([^\\.]*)/topic-(.+)\\.html$ $1/portal.php?mod=topic&topic=$2 last;\n\trewrite ^([^\\.]*)/article-([0-9]+)-([0-9]+)\\.html$ $1/portal.php?mod=view&aid=$2&page=$3 last;\n\trewrite ^([^\\.]*)/forum-(\\w+)-([0-9]+)\\.html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3 last;\n\trewrite ^([^\\.]*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;\n\trewrite ^([^\\.]*)/group-([0-9]+)-([0-9]+)\\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;\n\trewrite ^([^\\.]*)/space-(username|uid)-(.+)\\.html$ $1/home.php?mod=space&$2=$3 last;\n\trewrite ^([^\\.]*)/blog-([0-9]+)-([0-9]+)\\.html$ $1/home.php?mod=space&uid=$2&do=blog&id=$3 last;\n\trewrite ^([^\\.]*)/(fid|tid)-([0-9]+)\\.html$ $1/index.php?action=$2&value=$3 last;\n\trewrite ^([^\\.]*)/([a-z]+[a-z0-9_]*)-([a-z0-9_\\-]+)\\.html$ $1/plugin.php?id=$2:$3 last;\n\tif (!-e $request_filename) {\n\t\t\treturn 404;\n\t}\n}\n\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/drupal.conf",
    "content": "if (!-e $request_filename) {\n      rewrite ^/(.*)$ /index.php?q=$1 last;\n    }"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/ecshop.conf",
    "content": "if (!-e $request_filename)\n{\nrewrite \"^/index\\.html\" /index.php last;\nrewrite \"^/category$\" /index.php last;\nrewrite \"^/feed-c([0-9]+)\\.xml$\" /feed.php?cat=$1 last;\nrewrite \"^/feed-b([0-9]+)\\.xml$\" /feed.php?brand=$1 last;\nrewrite \"^/feed\\.xml$\" /feed.php last;\nrewrite \"^/category-([0-9]+)-b([0-9]+)-min([0-9]+)-max([0-9]+)-attr([^-]*)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\\.html$\" /category.php?id=$1&brand=$2&price_min=$3&price_max=$4&filter_attr=$5&page=$6&sort=$7&order=$8 last;\nrewrite \"^/category-([0-9]+)-b([0-9]+)-min([0-9]+)-max([0-9]+)-attr([^-]*)(.*)\\.html$\" /category.php?id=$1&brand=$2&price_min=$3&price_max=$4&filter_attr=$5 last;\nrewrite \"^/category-([0-9]+)-b([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\\.html$\" /category.php?id=$1&brand=$2&page=$3&sort=$4&order=$5 last;\nrewrite \"^/category-([0-9]+)-b([0-9]+)-([0-9]+)(.*)\\.html$\" /category.php?id=$1&brand=$2&page=$3 last;\nrewrite \"^/category-([0-9]+)-b([0-9]+)(.*)\\.html$\" /category.php?id=$1&brand=$2 last;\nrewrite \"^/category-([0-9]+)(.*)\\.html$\" /category.php?id=$1 last;\nrewrite \"^/goods-([0-9]+)(.*)\\.html\" /goods.php?id=$1 last;\nrewrite \"^/article_cat-([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\\.html$\" /article_cat.php?id=$1&page=$2&sort=$3&order=$4 last;\nrewrite \"^/article_cat-([0-9]+)-([0-9]+)(.*)\\.html$\" /article_cat.php?id=$1&page=$2 last;\nrewrite \"^/article_cat-([0-9]+)(.*)\\.html$\" /article_cat.php?id=$1 last;\nrewrite \"^/article-([0-9]+)(.*)\\.html$\" /article.php?id=$1 last;\nrewrite \"^/brand-([0-9]+)-c([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)\\.html\" /brand.php?id=$1&cat=$2&page=$3&sort=$4&order=$5 last;\nrewrite \"^/brand-([0-9]+)-c([0-9]+)-([0-9]+)(.*)\\.html\" /brand.php?id=$1&cat=$2&page=$3 last;\nrewrite \"^/brand-([0-9]+)-c([0-9]+)(.*)\\.html\" /brand.php?id=$1&cat=$2 last;\nrewrite \"^/brand-([0-9]+)(.*)\\.html\" /brand.php?id=$1 last;\nrewrite \"^/tag-(.*)\\.html\" /search.php?keywords=$1 last;\nrewrite \"^/snatch-([0-9]+)\\.html$\" /snatch.php?id=$1 last;\nrewrite \"^/group_buy-([0-9]+)\\.html$\" /group_buy.php?act=view&id=$1 last;\nrewrite \"^/auction-([0-9]+)\\.html$\" /auction.php?act=view&id=$1 last;\nrewrite \"^/exchange-id([0-9]+)(.*)\\.html$\" /exchange.php?id=$1&act=view last;\nrewrite \"^/exchange-([0-9]+)-min([0-9]+)-max([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\\.html$\" /exchange.php?cat_id=$1&integral_min=$2&integral_max=$3&page=$4&sort=$5&order=$6 last;\nrewrite ^/exchange-([0-9]+)-([0-9]+)-(.+)-([a-zA-Z]+)(.*)\\.html$\" /exchange.php?cat_id=$1&page=$2&sort=$3&order=$4 last;\nrewrite \"^/exchange-([0-9]+)-([0-9]+)(.*)\\.html$\" /exchange.php?cat_id=$1&page=$2 last;\nrewrite \"^/exchange-([0-9]+)(.*)\\.html$\" /exchange.php?cat_id=$1 last;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/edusoho.conf",
    "content": "location / {\n    index app.php;\n    try_files $uri @rewriteapp;\n}\n\nlocation @rewriteapp {\n    rewrite ^(.*)$ /app.php/$1 last;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/emlog.conf",
    "content": "location / {\nindex index.php index.html;\n\tif (!-e $request_filename)\n\t{\n\t\trewrite ^/(.*)$ /index.php last;\n\t}\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/empirecms.conf",
    "content": "rewrite ^([^\\.]*)/listinfo-(.+?)-(.+?)\\.html$ $1/e/action/ListInfo/index.php?classid=$2&page=$3 last;\nrewrite ^([^\\.]*)/showinfo-(.+?)-(.+?)-(.+?)\\.html$ $1/e/action/ShowInfo.php?classid=$2&id=$3&page=$4 last;\nrewrite ^([^\\.]*)/infotype-(.+?)-(.+?)\\.html$ $1/e/action/InfoType/index.php?ttid=$2&page=$3 last;\nrewrite ^([^\\.]*)/tags-(.+?)-(.+?)\\.html$ $1/e/tags/index.php?tagname=$2&page=$3 last;\nrewrite ^([^\\.]*)/comment-(.+?)-(.+?)-(.+?)-(.+?)-(.+?)-(.+?)\\.html$  $1/e/pl/index\\.php\\?doaction=$2&classid=$3&id=$4&page=$5&myorder=$6&tempid=$7 last;\nif (!-e $request_filename) {\n\treturn 404;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/laravel5.conf",
    "content": "location / {\n    try_files $uri $uri/ /index.php?$query_string;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/maccms.conf",
    "content": "if (!-e $request_filename) {\n        rewrite ^/index.php(.*)$ /index.php?s=$1 break;\n        # MacCMS要求强制修改后台文件名称 所以需要手动修改下方这条重写规则 将admin修改为你修改后的文件名即可\n        rewrite ^/admin.php(.*)$ /admin.php?s=$1 break;\n        rewrite ^/api.php(.*)$ /api.php?s=$1 break;\n        rewrite ^/(.*)$ /index.php?s=$1 break;\n        rewrite ^/vod-(.*)$ /index.php?m=vod-$1 break;\n        rewrite ^/art-(.*)$ /index.php?m=art-$1 break;\n        rewrite ^/gbook-(.*)$ /index.php?m=gbook-$1 break;\n        rewrite ^/label-(.*)$ /index.php?m=label-$1 break;\n        rewrite ^/map-(.*)$ /index.php?m=map-$1 break;\n    }\ntry_files $uri $uri/ /index.php?$query_string;"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/mvc.conf",
    "content": "location /{\n\tif (!-e $request_filename) {\n\t   rewrite  ^(.*)$  /index.php/$1  last;\n\t   break;\n\t}\n}\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/niushop.conf",
    "content": "location / {\n\tif (!-e $request_filename) {\n\t\trewrite  ^(.*)$  /index.php?s=$1  last;\n\t\tbreak;\n\t}\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/phpcms.conf",
    "content": "location / {\n\t###以下为PHPCMS 伪静态化rewrite法则\n\trewrite ^(.*)show-([0-9]+)-([0-9]+)\\.html$ $1/show.php?itemid=$2&page=$3;\n\trewrite ^(.*)list-([0-9]+)-([0-9]+)\\.html$ $1/list.php?catid=$2&page=$3;\n\trewrite ^(.*)show-([0-9]+)\\.html$ $1/show.php?specialid=$2;\n\t####以下为PHPWind 伪静态化rewrite法则\n\trewrite ^(.*)-htm-(.*)$ $1.php?$2 last;\n\trewrite ^(.*)/simple/([a-z0-9\\_]+\\.html)$ $1/simple/index.php?$2 last;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/phpwind.conf",
    "content": "location / {\n            rewrite ^(.*)-htm-(.*)$ $1.php?$2 last;\n            rewrite ^(.*)/simple/([a-z0-9\\_]+\\.html)$ $1/simple/index.php?$2 last;\n        }"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/sablog.conf",
    "content": "location / {\n\trewrite \"^/date/([0-9]{6})/?([0-9]+)?/?$\" /index.php?action=article&setdate=$1&page=$2 last;\n\trewrite ^/page/([0-9]+)?/?$ /index.php?action=article&page=$1 last;\n\trewrite ^/category/([0-9]+)/?([0-9]+)?/?$ /index.php?action=article&cid=$1&page=$2 last;\n\trewrite ^/category/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&curl=$1&page=$2 last;\n\trewrite ^/(archives|search|article|links)/?$ /index.php?action=$1 last;\n\trewrite ^/(comments|tagslist|trackbacks|article)/?([0-9]+)?/?$ /index.php?action=$1&page=$2 last;\n\trewrite ^/tag/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&item=$1&page=$2 last;\n\trewrite ^/archives/([0-9]+)/?([0-9]+)?/?$ /index.php?action=show&id=$1&page=$2 last;\n\trewrite ^/rss/([0-9]+)?/?$ /rss.php?cid=$1 last;\n\trewrite ^/rss/([^/]+)/?$ /rss.php?url=$1 last;\n\trewrite ^/uid/([0-9]+)/?([0-9]+)?/?$ /index.php?action=article&uid=$1&page=$2 last;\n\trewrite ^/user/([^/]+)/?([0-9]+)?/?$ /index.php?action=article&user=$1&page=$2 last;\n\trewrite sitemap.xml sitemap.php last;\n\trewrite ^(.*)/([0-9a-zA-Z\\-\\_]+)/?([0-9]+)?/?$ $1/index.php?action=show&alias=$2&page=$3 last;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/seacms.conf",
    "content": "location / {\n\trewrite ^/frim/index(.+?)\\.html$ /list/index.php?$1 last;\n\trewrite ^/movie/index(.+?)\\.html$ /detail/index.php?$1 last;\n\trewrite ^/play/([0-9]+)-([0-9]+)-([0-9]+)\\.html$ /video/index.php?$1-$2-$3 last;\n\trewrite ^/topic/index(.+?)\\.html$ /topic/index.php?$1 last;\n\trewrite ^/topiclist/index(.+?).html$ /topiclist/index.php?$1 last;\n\trewrite ^/index\\.html$ index.php permanent;\n\trewrite ^/news\\.html$ news/ permanent;\n\trewrite ^/part/index(.+?)\\.html$ /articlelist/index.php?$1 last;\n\trewrite ^/article/index(.+?)\\.html$ /article/index.php?$1 last;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/shopex.conf",
    "content": "location / {\nif (!-e $request_filename) {\nrewrite ^/(.+\\.(html|xml|json|htm|php|jsp|asp|shtml))$ /index.php?$1 last;\n}\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/shopwind.conf",
    "content": "location / {\n  #Redirect everything that isn't a real file to index.php\n  try_files $uri $uri/ /index.php$is_args$args;\n}\n#If you want a single domain name at the front and back ends\nlocation /admin {\n  try_files $uri $uri/ /admin/index.php$is_args$args;\n}\nlocation /mobile {\n  try_files $uri $uri/ /mobile/index.php$is_args$args;\n}\nlocation /api {\n  try_files $uri $uri/ /api/index.php$is_args$args;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/thinkphp.conf",
    "content": "location ~* (runtime|application)/ {\n\treturn 403;\n}\nlocation / {\n\tif (!-e $request_filename){\n\t\trewrite  ^(.*)$  /index.php?s=$1  last;   break;\n\t}\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/typecho.conf",
    "content": "    if (!-e $request_filename) {\n        rewrite ^(.*)$ /index.php$1 last;\n    }\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/typecho2.conf",
    "content": "location /typecho/ {\n    if (!-e $request_filename) {\n        rewrite ^(.*)$ /typecho/index.php$1 last;\n    }\n}\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/wordpress.conf",
    "content": "location /\n{\n\t try_files $uri $uri/ /index.php?$args;\n}\n\nrewrite /wp-admin$ $scheme://$host$uri/ permanent;"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/wp2.conf",
    "content": "rewrite ^.*/files/(.*)$ /wp-includes/ms-files.php?file=$1 last;\nif (!-e $request_filename){\n\trewrite ^.+?(/wp-.*) $1 last;\n\trewrite ^.+?(/.*\\.php)$ $1 last;\n\trewrite ^ /index.php last;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/yii2.conf",
    "content": "location / {\n        try_files  $uri $uri/ /index.php$is_args$args;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/rewrite/zblog.conf",
    "content": "if (-f $request_filename/index.html){\n\trewrite (.*) $1/index.html break;\n}\nif (-f $request_filename/index.php){\n\trewrite (.*) $1/index.php;\n}\nif (!-f $request_filename){\n\trewrite (.*) /index.php;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/root_ssl.conf",
    "content": "ssl_certificate    /usr/local/openresty/nginx/conf/ssl/fullchain.pem;\nssl_certificate_key    /usr/local/openresty/nginx/conf/ssl/privkey.pem;\nssl_protocols TLSv1.2 TLSv1.3;\nssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;\nssl_prefer_server_ciphers off;\nssl_session_cache shared:SSL:10m;\nssl_session_timeout 10m;\nadd_header Strict-Transport-Security \"max-age=31536000\";"
  },
  {
    "path": "agent/cmd/server/nginx_conf/sse.conf",
    "content": "location ^~ /github {\n    proxy_pass http://127.0.0.1:8001/github; \n    proxy_buffering off;\n    proxy_http_version 1.1;\n    proxy_set_header Connection '';\n    chunked_transfer_encoding off;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/ssl.conf",
    "content": "ssl_certificate    /www/sites/1panel.pro/ssl/fullchain.pem;\nssl_certificate_key    /www/sites/1panel.pro/ssl/privkey.pem;\nssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;\nssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED;\nssl_prefer_server_ciphers off;\nssl_session_cache shared:SSL:10m;\nssl_session_timeout 10m;\nerror_page 497  https://$host$request_uri;\nproxy_set_header X-Forwarded-Proto https;"
  },
  {
    "path": "agent/cmd/server/nginx_conf/stop.html",
    "content": "<!doctype html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <title>抱歉，站点已暂停</title>\n    <style>\n        html,body,div,h1,*{margin:0;padding:0;}\n        body{\n            background-color:#fefefe;\n            color:#333\n        }\n        .box{\n            width:580px;\n            margin:0 auto;\n        }\n        h1{\n            font-size:20px;\n            text-align:center;\n            background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAQAAABpN6lAAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAH+UlEQVR42uyde3AV1RnAfyFpTCBpFIVoaxUpSQSEAezwMDUJRK11DOTDAjJTtPRBBx1bK/VRO7T1VdFCRx6j6DgjltaamcIJWBpICRg0MFjGio6BRGKp4iOUMtBEkiBJ+ocxzQ17957de3bv3WY//mDYc75zzve73549z4+Ubga2DCIEEAIIAYQAQgAhgBBACCAEEAIIAYQAQgAhgBBACCAEEAIIAYQAQgADR9L8q0pyGUM+BRRwIVlkk00mp2ilhRaaaaCBRupVs78AUrzfF5ChlFBKKQVa2RuooYaX1fH/AwCSgfAdrnHxonWxneeoVO2BBSBj+BHzODeuQk5QwSpVHzgAMo6l3GSog+1iAw+ptwIDQPJZRjkpRgvtRnGfeifpAUgGP+NezvHkp+rgMR413ycYBCDX8hRf9bTHOsRt6q9JCUDSeJQlhh3f+mVYzv3qTJIBkIup4Crfxi6vcrP6IIkAyNVs5AJfh2/HmK1eSZK5gJRR7bP5cAHb5MakACC3sJGMBMxhMlGyIOEAZBHr/JxO9ZvGPS8/SGgfILPYQGpC57KdzFabEwRACtmeEOePlDZK1Z4EAJBR7GVoUqxoHGeKOuRzHyDpVCSJ+TCUFyXd707wN0wieeRKHvP1FZCZbCLZpEz92ScAkkMDuZqZD7CTowxnBpc7fK+3cJghTKBE00ebKVAn3X1NncrDmuYf4YfqL33Gi09zkZbeaR5kuero0cvjaaZraOXyAHf64AEygX1a3/5/UKzej9AcSS0Xx9Rrp1xti9BLZR3f1qjxDJPcrBs57QTXaJl/mvJI80G9yzy6Ymr+ONJ8UJ0s5G9avrzG86+AfJNCrYxPqDfPfqh281wMvT3qGQu9M+gNeYvkWq894OdaubpYFSVlZQzN31o/VvupMdg+twCkRPP3fyPacoV6iw9t3+KtUdNe0qq5WAq99ID7NfO9bpP2d5u0g6o1atpeoz7qBoCMRPcNO+YyrdllWl+5Xi7xygNu0c6Z6jJtkEu9iM86CzwBIE4KHm6TNsx2MOOuTLc/lCMPKGSkdpmTbTB+zUbvcsmJmjZVu/Z8meoFgHIHZY6WvKhmnG/bljIj9Zd7AaDEkV/dFeX5T2LoLRHL9sg0rnZQ+3TjAORcJjoCsEgstknkOubE0JvAEgu9DJ51tj4gXzTtAUUOR4yD+FP/10DG8gcNzV/Lt/rppVPBGEe1p1JkGoDj8RUXUSdzJeXzzk/ms0tr+ySNP8ojktkH28vMdFy7g/ZqTYdlk4tGfDYp3sG/GEYpIxzptVDFYQYziWmuNlwrlZhdEMnHnVzG91zpZTOXeCTfqAdIKqdIJ0jSwWDVZa4PuDRg5sM5XGqyExxO8GSYSQBZAQSQbRJAdgABZA10DzAKICOAADJNAmgLIIA2kwBaAwigdaADaAk9wCCAowEEcNQkgH9yOmDmd/CeQQCqk3cDBqBJdyqkuyDSGDAADtqrtx5wwOWCSKR8yiEOUE89H9HS8yeNLIYwhGxGkMco8hitP4iJKgdNA6iLqzndvMk2tlKnrPqSEz1/1/asPqQzjRmUMpkvuK7xVf2sektiORx3eZ6siSd5QX3sXFGGcSvf17xqFymdDFX/MQoAZB9XOm7IVlZTpeI6jy9F/NRmu8RaXlNTTL8CsNMhgD0sie8Ia88XaBe7ZDKro2+3WcgOJzXoOnalo2HobRSaML8HwmtM5Q4HUzJHpxi1T4lJk+b26H7meHHBTcayWasFjcpRv6F/TnA9v9TItYV56pOoRgxiFOP4Cl8il8FkkM6ntNPOMT7iQw7ytjoV1Q/elilU2e4ufya/cwZW3wNG0hTbW5mjOi21x3MD32Ayg231u2ikhmq2W4OQ86hlXIxP7gj1nicAQKpjHJLZw/TPT3j20cplIQsc7u59wibWU332gFZGsM92i71K3eCRB4CUsNMm+QTj+x+OlIncw02uBzSHWcva/ieAZS4VNjpfV3WeAQCps7kdeKda2a/TWkb8N7tOsorHI0+P2XhirSpxWoGz8d0jNvPvtX2aOESeYD8mLrblsJSmfvfDfuWifWY8wMYHHlf39ua5it9zmeGv4FYW/m9ALfWMtsjziipyXrDTEf7tWPby9J4NlbuoNW4+XM9+uab3X82WM4Db3RTsEIB6g6csE4oBJE2eYYVHNwmHUyWLACTFcvt7jbsgC25ujDRabpfOYgsvxLmvH1vuZgVLeeCs565vjJi7M9TN+1yC9/IBX7Z4OlO95K44F7N8tZnVVih9MR9L81e6Nd/ttbm7bU99+y2vc497Zbc3R/PYy3lJYX4ibo6Ceocy2pPA/DbK4jE/juvzqo75dCXY/E7mq93xFRFH/ABVyWIS+V+UdLNYxX2HNc4YInIrzyYohMIZvqvWx19M3EFUZCYVCThD0sY8958+owBAithou0hhXpIpigyoXUxgt4/m72aiKfMNhdRURyhmhU8d33KK1RFzBZqMJXYdT3ocS6yJxaZjiRkMqqqquYKHPTtM0cFDXGHafC/iCRawjFnG4wlWcp/y5JSCNxElx/MLZhuC0M0GHgxQRMleCGO5g5vJiauQk7wYyJiivRAyERYyw1VU2RrWsTHAUWX7YDif6ZQyQ/MiSyM17GCn+rc/g4oU/2YzciFjKOiNLJ1FNpm00UIrLXxMIw00UO/mNElAACSnhNHlQwAhgBBACCAEEAIIAYQAQgAhgBDAgJT/DgDyxCJjaj0UmAAAAABJRU5ErkJggg==) no-repeat top center;\n            padding-top:160px;\n            margin-top:30%;\n            font-weight:normal;\n        }\n\n    </style>\n</head>\n\n<body>\n<div class=\"box\">\n    <h1>抱歉！该站点已经被管理员停止运行，请联系管理员了解详情！</h1>\n</div>\n</body>\n</html>"
  },
  {
    "path": "agent/cmd/server/nginx_conf/stream_default.conf",
    "content": "server {\n    proxy_pass mysql_cluster;\n    access_log /www/sites/domain/log/access.log streamlog;\n    error_log /www/sites/domain/log/error.log;\n}\n"
  },
  {
    "path": "agent/cmd/server/nginx_conf/upstream.conf",
    "content": "upstream backend {\n    server backend1.example.com;\n}"
  },
  {
    "path": "agent/cmd/server/nginx_conf/website_default.conf",
    "content": "server {\n    listen 80;\n    server_name ko.wp-1.com;\n\n    index index.php index.html index.htm default.php default.htm default.html;\n\n    access_log /www/sites/domain/log/access.log main;\n    error_log /www/sites/domain/log/error.log;\n    \n    location ~ ^/(\\.user.ini|\\.htaccess|\\.git|\\.env|\\.svn|\\.project|LICENSE|README.md){\n        return 404;\n    }\n    location ^~ /.well-known/acme-challenge {\n        allow all;\n        root /usr/share/nginx/html;\n    }\n    if ( $uri ~ \"^/\\.well-known/.*\\.(php|jsp|py|js|css|lua|ts|go|zip|tar\\.gz|rar|7z|sql|bak)$\" ) {\n        return 403;\n    }\n\n}\n"
  },
  {
    "path": "agent/constant/alert.go",
    "content": "package constant\n\nconst (\n\tAlertEnable      = \"Enable\"\n\tAlertDisable     = \"Disable\"\n\tAlertSuccess     = \"Success\"\n\tAlertPushing     = \"Pushing\"\n\tAlertError       = \"Error\"\n\tAlertSyncError   = \"SyncError\"\n\tAlertPushError   = \"PushError\"\n\tAlertPushSuccess = \"PushSuccess\"\n)\n\nconst (\n\tCommonConfig = \"common\"\n\tSMSConfig    = \"sms\"\n\tEmailConfig  = \"email\"\n)\n\nconst (\n\tWeChat   = \"wechat\"\n\tSMS      = \"sms\"\n\tEmail    = \"mail\"\n\tWeCom    = \"weCom\"\n\tDingTalk = \"dingTalk\"\n\tFeiShu   = \"feiShu\"\n\tCustom   = \"custom\"\n)\n"
  },
  {
    "path": "agent/constant/app.go",
    "content": "package constant\n\nconst (\n\tContainerPrefix = \"1Panel-\"\n\n\tAppNormal   = \"Normal\"\n\tAppTakeDown = \"TakeDown\"\n\n\tAppOpenresty         = \"openresty\"\n\tAppOpenclaw          = \"openclaw\"\n\tAppCopaw             = \"copaw\"\n\tAppMysql             = \"mysql\"\n\tAppMariaDB           = \"mariadb\"\n\tAppPostgresql        = \"postgresql\"\n\tAppRedis             = \"redis\"\n\tAppPostgres          = \"postgres\"\n\tAppMongodb           = \"mongodb\"\n\tAppMemcached         = \"memcached\"\n\tAppMysqlCluster      = \"mysql-cluster\"\n\tAppPostgresqlCluster = \"postgresql-cluster\"\n\tAppRedisCluster      = \"redis-cluster\"\n\n\tAppResourceLocal  = \"local\"\n\tAppResourceRemote = \"remote\"\n\tAppResourceCustom = \"custom\"\n\n\tCPUS          = \"CPUS\"\n\tMemoryLimit   = \"MEMORY_LIMIT\"\n\tHostIP        = \"HOST_IP\"\n\tContainerName = \"CONTAINER_NAME\"\n)\n\ntype AppOperate string\n\nvar (\n\tStart    AppOperate = \"start\"\n\tStop     AppOperate = \"stop\"\n\tRestart  AppOperate = \"restart\"\n\tDelete   AppOperate = \"delete\"\n\tSync     AppOperate = \"sync\"\n\tBackup   AppOperate = \"backup\"\n\tUpdate   AppOperate = \"update\"\n\tRebuild  AppOperate = \"rebuild\"\n\tUpgrade  AppOperate = \"upgrade\"\n\tReload   AppOperate = \"reload\"\n\tFavorite AppOperate = \"favorite\"\n)\n"
  },
  {
    "path": "agent/constant/backup.go",
    "content": "package constant\n\nconst (\n\tValid        = \"VALID\"\n\tDisConnect   = \"DISCONNECT\"\n\tVerifyFailed = \"VERIFYFAILED\"\n\tS3           = \"S3\"\n\tOSS          = \"OSS\"\n\tSftp         = \"SFTP\"\n\tOneDrive     = \"OneDrive\"\n\tMinIo        = \"MINIO\"\n\tCos          = \"COS\"\n\tKodo         = \"KODO\"\n\tWebDAV       = \"WebDAV\"\n\tLocal        = \"LOCAL\"\n\tUPYUN        = \"UPYUN\"\n\tALIYUN       = \"ALIYUN\"\n\tGoogleDrive  = \"GoogleDrive\"\n)\n"
  },
  {
    "path": "agent/constant/common.go",
    "content": "package constant\n\ntype DBContext string\n\nconst (\n\tDB DBContext = \"db\"\n\n\tSystemRestart = \"systemRestart\"\n\n\tTypeWebsite       = \"website\"\n\tTypePhp           = \"php\"\n\tTypeSSL           = \"ssl\"\n\tTypeSystem        = \"system\"\n\tTypeTask          = \"task\"\n\tTypeApp           = \"app\"\n\tTypeImagePull     = \"image-pull\"\n\tTypeImagePush     = \"image-push\"\n\tTypeImageBuild    = \"image-build\"\n\tTypeComposeCreate = \"compose-create\"\n\n\tInterruptedMsg = \"the task was interrupted due to the restart of the 1panel service\"\n)\n\nconst (\n\tTimeOut5s  = 5\n\tTimeOut20s = 20\n\tTimeOut5m  = 300\n\n\tDateLayout               = \"2006-01-02\"          // or use time.DateOnly while go version >= 1.20\n\tDateTimeLayout           = \"2006-01-02 15:04:05\" // or use time.DateTime while go version >= 1.20\n\tDateTimeSlimLayout       = \"20060102150405\"\n\tWebsiteDefaultExpireDate = \"9999-12-31\"\n)\n\nconst (\n\tDirPerm  = 0755\n\tFilePerm = 0644\n)\n"
  },
  {
    "path": "agent/constant/container.go",
    "content": "package constant\n\nconst (\n\tContainerOpStart   = \"start\"\n\tContainerOpStop    = \"stop\"\n\tContainerOpRestart = \"restart\"\n\tContainerOpKill    = \"kill\"\n\tContainerOpPause   = \"pause\"\n\tContainerOpUnpause = \"unpause\"\n\tContainerOpRename  = \"rename\"\n\tContainerOpRemove  = \"remove\"\n\n\tComposeOpStop    = \"stop\"\n\tComposeOpRestart = \"restart\"\n\tComposeOpRemove  = \"remove\"\n)\n\nvar DaemonJsonPath = \"/etc/docker/daemon.json\"\n"
  },
  {
    "path": "agent/constant/host_tool.go",
    "content": "package constant\n\nconst (\n\tSupervisord           = \"supervisord\"\n\tSupervisor            = \"supervisor\"\n\tSupervisorConfigPath  = \"SupervisorConfigPath\"\n\tSupervisorServiceName = \"SupervisorServiceName\"\n)\n"
  },
  {
    "path": "agent/constant/nginx.go",
    "content": "package constant\n\nconst (\n\tNginxScopeServer = \"server\"\n\tNginxScopeHttp   = \"http\"\n\tNginxScopeOut    = \"out\"\n\n\tNginxReload  = \"reload\"\n\tNginxCheck   = \"check\"\n\tNginxRestart = \"restart\"\n\n\tConfigNew    = \"add\"\n\tConfigUpdate = \"update\"\n\tConfigDel    = \"delete\"\n)\n"
  },
  {
    "path": "agent/constant/runtime.go",
    "content": "package constant\n\nconst (\n\tResourceLocal    = \"local\"\n\tResourceAppstore = \"appstore\"\n\n\tRuntimePHP    = \"php\"\n\tRuntimeNode   = \"node\"\n\tRuntimeJava   = \"java\"\n\tRuntimeGo     = \"go\"\n\tRuntimePython = \"python\"\n\tRuntimeDotNet = \"dotnet\"\n\n\tRuntimeProxyUnix = \"unix\"\n\tRuntimeProxyTcp  = \"tcp\"\n\n\tRuntimeUp      = \"up\"\n\tRuntimeDown    = \"down\"\n\tRuntimeRestart = \"restart\"\n\n\tRuntimeInstall   = \"install\"\n\tRuntimeUninstall = \"uninstall\"\n\tRuntimeUpdate    = \"update\"\n\n\tRuntimeNpm  = \"npm\"\n\tRuntimeYarn = \"yarn\"\n)\n\nvar GoDefaultVolumes = map[string]string{\n\t\"${CODE_DIR}\": \"/app\",\n\t\"./run.sh\":    \"/run.sh\",\n\t\"./.env\":      \"/.env\",\n\t\"./mod\":       \"/go/pkg/mod\",\n}\n\nvar RuntimeDefaultVolumes = map[string]string{\n\t\"${CODE_DIR}\": \"/app\",\n\t\"./run.sh\":    \"/run.sh\",\n\t\"./.env\":      \"/.env\",\n}\n\nvar PHPDefaultVolumes = map[string]string{\n\t\"${PANEL_WEBSITE_DIR}\":                  \"/www/\",\n\t\"./conf/php.ini\":                        \"/usr/local/etc/php/php.ini\",\n\t\"./conf/conf.d\":                         \"/usr/local/etc/php/conf.d\",\n\t\"./conf/php-fpm.conf\":                   \"/usr/local/etc/php-fpm.conf\",\n\t\"./log\":                                 \"/var/log/php\",\n\t\"./extensions\":                          \"/usr/local/lib/php/extensions\",\n\t\"./supervisor/supervisord.conf\":         \"/etc/supervisord.conf\",\n\t\"./supervisor/supervisor.d/php-fpm.ini\": \"/etc/supervisor.d/php-fpm.ini\",\n\t\"./supervisor/supervisor.d\":             \"/etc/supervisor.d\",\n\t\"./supervisor/log\":                      \"/var/log/supervisor\",\n\t\"./composer\":                            \"/tmp/composer\",\n}\n"
  },
  {
    "path": "agent/constant/status.go",
    "content": "package constant\n\nconst (\n\tStatusRunning        = \"Running\"\n\tStatusCanceled       = \"Canceled\"\n\tStatusDone           = \"Done\"\n\tStatusWaiting        = \"Waiting\"\n\tStatusPending        = \"Pending\"\n\tStatusSuccess        = \"Success\"\n\tStatusFailed         = \"Failed\"\n\tStatusUploading      = \"Uploading\"\n\tStatusEnable         = \"Enable\"\n\tStatusDisable        = \"Disable\"\n\tStatusNone           = \"None\"\n\tStatusDeleted        = \"Deleted\"\n\tStatusExecuting      = \"Executing\"\n\tStatusBuilding       = \"Building\"\n\tStatusReCreating     = \"Recreating\"\n\tStatusStopped        = \"Stopped\"\n\tStatusCreating       = \"Creating\"\n\tStatusStartErr       = \"StartErr\"\n\tStatusNormal         = \"Normal\"\n\tStatusError          = \"Error\"\n\tStatusStarting       = \"Starting\"\n\tStatusRestarting     = \"ReStarting\"\n\tStatusUnHealthy      = \"UnHealthy\"\n\tStatusInstalling     = \"Installing\"\n\tStatusDownloadErr    = \"DownloadErr\"\n\tStatusUpgrading      = \"Upgrading\"\n\tStatusUpgradeErr     = \"UpgradeErr\"\n\tStatusRebuilding     = \"Rebuilding\"\n\tStatusSyncing        = \"Syncing\"\n\tStatusSyncSuccess    = \"SyncSuccess\"\n\tStatusPaused         = \"Paused\"\n\tStatusUpErr          = \"UpErr\"\n\tStatusInstallErr     = \"InstallErr\"\n\tStatusUninstalling   = \"Uninstalling\"\n\tStatusWaitingRestart = \"WaitingRestart\"\n\tStatusUnexecuted     = \"Unexecuted\"\n\n\tOrderDesc = \"descending\"\n\tOrderAsc  = \"ascending\"\n\tDesc      = \"desc\"\n\tAsc       = \"asc\"\n)\n"
  },
  {
    "path": "agent/constant/task.go",
    "content": "package constant\n\nconst (\n\tTaskInstall       = \"installApp\"\n\tTaskCreateWebsite = \"createWebsite\"\n)\n"
  },
  {
    "path": "agent/constant/website.go",
    "content": "package constant\n\nconst (\n\tWebRunning = \"Running\"\n\tWebStopped = \"Stopped\"\n\n\tProtocolHTTP   = \"HTTP\"\n\tProtocolHTTPS  = \"HTTPS\"\n\tProtocolStream = \"TCP/UDP\"\n\n\tNewApp       = \"new\"\n\tInstalledApp = \"installed\"\n\n\tDeployment = \"deployment\"\n\tStatic     = \"static\"\n\tProxy      = \"proxy\"\n\tRuntime    = \"runtime\"\n\tSubsite    = \"subsite\"\n\tStream     = \"stream\"\n\n\tSSLExisted = \"existed\"\n\tSSLAuto    = \"auto\"\n\tSSLManual  = \"manual\"\n\n\tDNSAccount = \"dnsAccount\"\n\tDnsManual  = \"dnsManual\"\n\tHttp       = \"http\"\n\tManual     = \"manual\"\n\tSelfSigned = \"selfSigned\"\n\tFromMaster = \"fromMaster\"\n\n\tStartWeb = \"start\"\n\tStopWeb  = \"stop\"\n\tSetHttps = \"setHttps\"\n\n\tHTTPSOnly   = \"HTTPSOnly\"\n\tHTTPAlso    = \"HTTPAlso\"\n\tHTTPToHTTPS = \"HTTPToHTTPS\"\n\n\tGetLog     = \"get\"\n\tDisableLog = \"disable\"\n\tEnableLog  = \"enable\"\n\tDeleteLog  = \"delete\"\n\n\tAccessLog = \"access.log\"\n\tErrorLog  = \"error.log\"\n\n\tConfigPHP = \"php\"\n\tConfigFPM = \"fpm\"\n\n\tSSLInit       = \"init\"\n\tSSLError      = \"error\"\n\tSSLReady      = \"ready\"\n\tSSLApply      = \"applying\"\n\tSSLApplyError = \"applyError\"\n)\n"
  },
  {
    "path": "agent/cron/cron.go",
    "content": "package cron\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/cron/job\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ntp\"\n\t\"github.com/robfig/cron/v3\"\n)\n\nfunc Run() {\n\tnyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\tglobal.Cron = cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))\n\n\tvar (\n\t\tinterval model.Setting\n\t\tstatus   model.Setting\n\t)\n\tgo syncBeforeStart()\n\tif err := global.DB.Where(\"key = ?\", \"MonitorStatus\").Find(&status).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"load monitor status from db failed, err: %v\", err)\n\t}\n\tif status.Value == \"Enable\" {\n\t\tif err := global.DB.Where(\"key = ?\", \"MonitorInterval\").Find(&interval).Error; err != nil {\n\t\t\tglobal.LOG.Errorf(\"load monitor interval from db failed, err: %v\", err)\n\t\t}\n\t\tif err := service.StartMonitor(false, interval.Value); err != nil {\n\t\t\tglobal.LOG.Errorf(\"can not add monitor corn job: %s\", err.Error())\n\t\t}\n\t}\n\n\tif _, err := global.Cron.AddJob(\"@daily\", job.NewWebsiteJob()); err != nil {\n\t\tglobal.LOG.Errorf(\"can not add  website corn job: %s\", err.Error())\n\t}\n\tif _, err := global.Cron.AddJob(\"@daily\", job.NewSSLJob()); err != nil {\n\t\tglobal.LOG.Errorf(\"can not add  ssl corn job: %s\", err.Error())\n\t}\n\tminuteRand, err := rand.Int(rand.Reader, big.NewInt(60))\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"generate random minute failed: %v\", err)\n\t\tminuteRand = big.NewInt(0)\n\t}\n\thourRand, err := rand.Int(rand.Reader, big.NewInt(3))\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"generate random hour failed: %v\", err)\n\t\thourRand = big.NewInt(0)\n\t}\n\tif _, err := global.Cron.AddJob(fmt.Sprintf(\"%v %v * * *\", minuteRand.Int64(), hourRand.Int64()), job.NewAppStoreJob()); err != nil {\n\t\tglobal.LOG.Errorf(\"can not add  appstore corn job: %s\", err.Error())\n\t}\n\tif _, err := global.Cron.AddJob(\"0 3 */31 * *\", job.NewBackupJob()); err != nil {\n\t\tglobal.LOG.Errorf(\"can not add  backup token refresh corn job: %s\", err.Error())\n\t}\n\n\tvar cronJobs []model.Cronjob\n\tif err := global.DB.Where(\"status = ?\", constant.StatusEnable).Find(&cronJobs).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"start my cronjob failed, err: %v\", err)\n\t}\n\tif err := global.DB.Model(&model.JobRecords{}).\n\t\tWhere(\"status = ?\", constant.StatusRunning).\n\t\tUpdates(map[string]interface{}{\n\t\t\t\"status\":  constant.StatusFailed,\n\t\t\t\"message\": \"Task Cancel\",\n\t\t\t\"records\": \"errHandle\",\n\t\t}).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"start my cronjob failed, err: %v\", err)\n\t}\n\tfor i := 0; i < len(cronJobs); i++ {\n\t\tentryIDs, err := service.NewICronjobService().StartJob(&cronJobs[i], false)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"start %s job %s failed, err: %v\", cronJobs[i].Type, cronJobs[i].Name, err)\n\t\t}\n\t\tif err := repo.NewICronjobRepo().Update(cronJobs[i].ID, map[string]interface{}{\"entry_ids\": entryIDs}); err != nil {\n\t\t\tglobal.LOG.Errorf(\"update cronjob %s %s failed, err: %v\", cronJobs[i].Type, cronJobs[i].Name, err)\n\t\t}\n\t}\n\tglobal.Cron.Start()\n}\n\nfunc syncBeforeStart() {\n\tvar ntpSite model.Setting\n\tif err := global.DB.Where(\"key = ?\", \"NtpSite\").Find(&ntpSite).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"load ntp serve from db failed, err: %v\", err)\n\t}\n\tif len(ntpSite.Value) == 0 {\n\t\tntpSite.Value = \"pool.ntp.org\"\n\t}\n\tntime, err := ntp.GetRemoteTime(ntpSite.Value)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load remote time with [%s] failed, err: %v\", ntpSite.Value, err)\n\t\treturn\n\t}\n\tts := ntime.Format(constant.DateTimeLayout)\n\tif err := ntp.UpdateSystemTime(ts); err != nil {\n\t\tglobal.LOG.Errorf(\"failed to synchronize system time with [%s], err: %v\", ntpSite.Value, err)\n\t}\n\tglobal.LOG.Debugf(\"synchronize system time with [%s] successful!\", ntpSite.Value)\n}\n"
  },
  {
    "path": "agent/cron/job/app.go",
    "content": "package job\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype app struct{}\n\nfunc NewAppStoreJob() *app {\n\treturn &app{}\n}\n\nfunc (a *app) Run() {\n\tglobal.LOG.Info(\"AppStore scheduled task in progress ...\")\n\tif err := service.NewIAppService().SyncAppListFromRemote(\"\"); err != nil {\n\t\tglobal.LOG.Errorf(\"AppStore sync failed %s\", err.Error())\n\t}\n\tglobal.LOG.Info(\"AppStore scheduled task has completed\")\n}\n"
  },
  {
    "path": "agent/cron/job/backup.go",
    "content": "package job\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage/client\"\n)\n\ntype backup struct{}\n\nfunc NewBackupJob() *backup {\n\treturn &backup{}\n}\n\nfunc (b *backup) Run() {\n\tvar backups []model.BackupAccount\n\t_ = global.DB.Where(\"`type` in (?) AND is_public = 0\", []string{constant.OneDrive, constant.ALIYUN}).Find(&backups)\n\tif len(backups) == 0 {\n\t\treturn\n\t}\n\tfor _, backupItem := range backups {\n\t\tif backupItem.ID == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tglobal.LOG.Infof(\"Start to refresh %s-%s access_token ...\", backupItem.Type, backupItem.Name)\n\t\tvarMap := make(map[string]interface{})\n\t\tif err := json.Unmarshal([]byte(backupItem.Vars), &varMap); err != nil {\n\t\t\tglobal.LOG.Errorf(\"failed to refresh %s - %s token, please retry, err: %v\", backupItem.Type, backupItem.Name, err)\n\t\t\tcontinue\n\t\t}\n\t\tvar (\n\t\t\trefreshToken string\n\t\t\terr          error\n\t\t)\n\t\tswitch backupItem.Type {\n\t\tcase constant.OneDrive:\n\t\t\trefreshToken, err = client.RefreshToken(\"refresh_token\", \"refreshToken\", varMap)\n\t\tcase constant.ALIYUN:\n\t\t\trefreshToken, err = client.RefreshALIToken(varMap)\n\t\t}\n\t\tif err != nil {\n\t\t\tvarMap[\"refresh_status\"] = constant.StatusFailed\n\t\t\tvarMap[\"refresh_msg\"] = err.Error()\n\t\t\tglobal.LOG.Errorf(\"failed to refresh OneDrive token, please retry, err: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tvarMap[\"refresh_status\"] = constant.StatusSuccess\n\t\tvarMap[\"refresh_time\"] = time.Now().Format(constant.DateTimeLayout)\n\t\tvarMap[\"refresh_token\"] = refreshToken\n\n\t\tvarsItem, _ := json.Marshal(varMap)\n\t\t_ = global.DB.Model(&model.BackupAccount{}).Where(\"id = ?\", backupItem.ID).Updates(map[string]interface{}{\"vars\": string(varsItem)}).Error\n\t\tglobal.LOG.Infof(\"Refresh %s-%s access_token successful!\", backupItem.Type, backupItem.Name)\n\t}\n}\n"
  },
  {
    "path": "agent/cron/job/ssl.go",
    "content": "package job\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\ntype ssl struct {\n}\n\nfunc NewSSLJob() *ssl {\n\treturn &ssl{}\n}\n\nfunc (ssl *ssl) Run() {\n\tsslRepo := repo.NewISSLRepo()\n\tsslService := service.NewIWebsiteSSLService()\n\tsslList, _ := sslRepo.List()\n\tnyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\tglobal.LOG.Info(\"The scheduled certificate update task is currently in progress ...\")\n\tnow := time.Now().Add(10 * time.Second)\n\tfor _, s := range sslList {\n\t\tif !s.AutoRenew || s.Provider == \"manual\" || s.Provider == \"dnsManual\" || s.Status == \"applying\" {\n\t\t\tcontinue\n\t\t}\n\t\texpireDate := s.ExpireDate.In(nyc)\n\t\tsub := expireDate.Sub(now)\n\t\tif s.IsIp && sub.Hours() < 72 || !s.IsIp && sub.Hours() < 720 {\n\t\t\tglobal.LOG.Infof(\"Update the SSL certificate for the [%s] domain\", s.PrimaryDomain)\n\t\t\tif s.Provider == constant.SelfSigned {\n\t\t\t\tcaService := service.NewIWebsiteCAService()\n\t\t\t\tif _, err := caService.ObtainSSL(request.WebsiteCAObtain{\n\t\t\t\t\tID:    s.CaID,\n\t\t\t\t\tSSLID: s.ID,\n\t\t\t\t\tRenew: true,\n\t\t\t\t\tUnit:  \"year\",\n\t\t\t\t\tTime:  10,\n\t\t\t\t}); err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"Failed to update the SSL certificate for the [%s] domain , err:%s\", s.PrimaryDomain, err.Error())\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := sslService.ObtainSSL(request.WebsiteSSLApply{\n\t\t\t\t\tID:         s.ID,\n\t\t\t\t\tDisableLog: true,\n\t\t\t\t}); err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"Failed to update the SSL certificate for the [%s] domain , err:%s\", s.PrimaryDomain, err.Error())\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tglobal.LOG.Infof(\"The SSL certificate for the [%s] domain has been successfully updated\", s.PrimaryDomain)\n\t\t}\n\t}\n\tglobal.LOG.Info(\"The scheduled certificate update task has completed\")\n}\n"
  },
  {
    "path": "agent/cron/job/website.go",
    "content": "package job\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\ntype website struct{}\n\nfunc NewWebsiteJob() *website {\n\treturn &website{}\n}\n\nfunc (w *website) Run() {\n\tnyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\twebsites, _ := repo.NewIWebsiteRepo().List()\n\tglobal.LOG.Info(\"Website scheduled task in progress ...\")\n\tnow := time.Now().Add(10 * time.Minute)\n\tif len(websites) > 0 {\n\t\tneverExpireDate, _ := time.Parse(constant.DateLayout, constant.WebsiteDefaultExpireDate)\n\t\tvar wg sync.WaitGroup\n\t\tfor _, site := range websites {\n\t\t\tif site.Status != constant.WebRunning || neverExpireDate.Equal(site.ExpireDate) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\texpireDate, err := time.ParseInLocation(constant.DateLayout, site.ExpireDate.Format(constant.DateLayout), nyc)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"time parse err %v\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif expireDate.Before(now) {\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func(ws model.Website) {\n\t\t\t\t\tstopWebsite(ws.ID, ws.PrimaryDomain, &wg)\n\t\t\t\t}(site)\n\t\t\t}\n\t\t}\n\t\twg.Wait()\n\t}\n\tglobal.LOG.Info(\"Website scheduled task has completed\")\n}\n\nfunc stopWebsite(websiteId uint, websiteName string, wg *sync.WaitGroup) {\n\twebsiteService := service.NewIWebsiteService()\n\treq := request.WebsiteOp{\n\t\tID:      websiteId,\n\t\tOperate: constant.StopWeb,\n\t}\n\tif err := websiteService.OpWebsite(req); err != nil {\n\t\tglobal.LOG.Errorf(\"Website [%s]  seop failed err %v\", websiteName, err)\n\t} else {\n\t\tglobal.LOG.Infof(\"Website [%s]  stopped successfully\", websiteName)\n\t}\n\twg.Done()\n}\n"
  },
  {
    "path": "agent/global/config.go",
    "content": "package global\n\ntype ServerConfig struct {\n\tBase         Base      `mapstructure:\"base\"`\n\tLog          LogConfig `mapstructure:\"log\"`\n\tDockerConfig DockerConfig\n}\n\ntype Base struct {\n\tPort       string `mapstructure:\"port\"`\n\tEdition    string `mapstructure:\"edition\"` // [ cn / intl]\n\tVersion    string `mapstructure:\"version\"`\n\tEncryptKey string `mapstructure:\"encrypt_key\"`\n\tMode       string `mapstructure:\"mode\"` // xpack [ Enable / Disable ]\n\tIsDemo     bool   `mapstructure:\"is_demo\"`\n\tInstallDir string `mapstructure:\"install_dir\"`\n\tIsOffLine  bool   `mapstructure:\"is_offline\"`\n}\n\ntype SystemDir struct {\n\tBaseDir        string\n\tDbDir          string\n\tLogDir         string\n\tTaskDir        string\n\tDataDir        string\n\tTmpDir         string\n\tLocalBackupDir string\n\n\tAppDir                string\n\tResourceDir           string\n\tIconCacheDir          string\n\tAppResourceDir        string\n\tAppInstallDir         string\n\tLocalAppResourceDir   string\n\tLocalAppInstallDir    string\n\tRemoteAppResourceDir  string\n\tCustomAppResourceDir  string\n\tOfflineAppResourceDir string\n\tRuntimeDir            string\n\tRecycleBinDir         string\n\tSSLLogDir             string\n\tMcpDir                string\n\tConvertLogDir         string\n\tTensorRTLLMDir        string\n\tFirewallDir           string\n}\n\ntype LogConfig struct {\n\tLevel     string `mapstructure:\"level\"`\n\tTimeZone  string `mapstructure:\"timeZone\"`\n\tLogName   string `mapstructure:\"log_name\"`\n\tLogSuffix string `mapstructure:\"log_suffix\"`\n\tMaxBackup int    `mapstructure:\"max_backup\"`\n}\n\ntype DockerConfig struct {\n\tCommand string\n}\n"
  },
  {
    "path": "agent/global/global.go",
    "content": "package global\n\nimport (\n\t\"context\"\n\n\tbadger_db \"github.com/1Panel-dev/1Panel/agent/init/cache/db\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/nicksnyder/go-i18n/v2/i18n\"\n\t\"github.com/robfig/cron/v3\"\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/spf13/viper\"\n\t\"gorm.io/gorm\"\n)\n\nvar (\n\tDB           *gorm.DB\n\tMonitorDB    *gorm.DB\n\tGPUMonitorDB *gorm.DB\n\tTaskDB       *gorm.DB\n\tCoreDB       *gorm.DB\n\tAlertDB      *gorm.DB\n\n\tLOG   *logrus.Logger\n\tCONF  ServerConfig\n\tVALID *validator.Validate\n\tCACHE *badger_db.Cache\n\tViper *viper.Viper\n\n\tDir SystemDir\n\n\tCron          *cron.Cron\n\tMonitorCronID cron.EntryID\n\n\tIsMaster bool\n\n\tI18n *i18n.Localizer\n\n\tAlertBaseJobID     cron.EntryID\n\tAlertResourceJobID cron.EntryID\n\n\tTaskCtxMap = make(map[string]context.CancelFunc)\n)\n\nfunc RepoURL() string {\n\tif CONF.Base.Edition != \"intl\" {\n\t\treturn \"https://resource.fit2cloud.com/1panel/package/v2\"\n\t}\n\treturn \"https://resource.1panel.pro/v2\"\n}\nfunc ResourceURL() string {\n\tif CONF.Base.Edition != \"intl\" {\n\t\treturn \"https://resource.fit2cloud.com/1panel/resource/v2\"\n\t}\n\treturn \"https://resource.1panel.pro/v2/resource\"\n}\nfunc AppRepoURL() string {\n\tif CONF.Base.Edition != \"intl\" {\n\t\treturn \"https://apps-assets.fit2cloud.com\"\n\t}\n\treturn \"https://apps.1panel.pro\"\n}\n"
  },
  {
    "path": "agent/go.mod",
    "content": "module github.com/1Panel-dev/1Panel/agent\n\ngo 1.25.7\n\nreplace github.com/moby/go-archive => github.com/moby/go-archive v0.1.0\n\nrequire (\n\tgithub.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible\n\tgithub.com/aws/aws-sdk-go v1.55.0\n\tgithub.com/compose-spec/compose-go/v2 v2.9.1\n\tgithub.com/creack/pty v1.1.24\n\tgithub.com/docker/cli v29.2.0+incompatible\n\tgithub.com/docker/compose/v2 v2.40.3\n\tgithub.com/docker/docker v28.5.2+incompatible\n\tgithub.com/docker/go-connections v0.6.0\n\tgithub.com/fsnotify/fsnotify v1.9.0\n\tgithub.com/gin-gonic/gin v1.10.0\n\tgithub.com/glebarez/sqlite v1.11.0\n\tgithub.com/go-acme/lego/v4 v4.30.1\n\tgithub.com/go-gormigrate/gormigrate/v2 v2.1.2\n\tgithub.com/go-playground/validator/v10 v10.26.0\n\tgithub.com/go-redis/redis v6.15.9+incompatible\n\tgithub.com/go-resty/resty/v2 v2.17.0\n\tgithub.com/go-sql-driver/mysql v1.9.3\n\tgithub.com/goh-chunlin/go-onedrive v1.1.1\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674\n\tgithub.com/jackc/pgx/v5 v5.7.5\n\tgithub.com/jinzhu/copier v0.4.0\n\tgithub.com/jinzhu/gorm v1.9.16\n\tgithub.com/joho/godotenv v1.5.1\n\tgithub.com/klauspost/compress v1.18.2\n\tgithub.com/mholt/archiver/v4 v4.0.0-alpha.8\n\tgithub.com/miekg/dns v1.1.69\n\tgithub.com/minio/minio-go/v7 v7.0.74\n\tgithub.com/nicksnyder/go-i18n/v2 v2.4.0\n\tgithub.com/opencontainers/image-spec v1.1.1\n\tgithub.com/oschwald/maxminddb-golang v1.13.1\n\tgithub.com/patrickmn/go-cache v2.1.0+incompatible\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/pkg/sftp v1.13.6\n\tgithub.com/qiniu/go-sdk/v7 v7.26.4\n\tgithub.com/robfig/cron/v3 v3.0.1\n\tgithub.com/shirou/gopsutil/v4 v4.25.11\n\tgithub.com/sirupsen/logrus v1.9.4\n\tgithub.com/spf13/afero v1.11.0\n\tgithub.com/spf13/cobra v1.10.2\n\tgithub.com/spf13/viper v1.19.0\n\tgithub.com/subosito/gotenv v1.6.0\n\tgithub.com/tencentyun/cos-go-sdk-v5 v0.7.54\n\tgithub.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19\n\tgithub.com/upyun/go-sdk v2.1.0+incompatible\n\tgolang.org/x/crypto v0.46.0\n\tgolang.org/x/net v0.48.0\n\tgolang.org/x/oauth2 v0.34.0\n\tgolang.org/x/sys v0.40.0\n\tgolang.org/x/text v0.32.0\n\tgoogle.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38\n\tgopkg.in/ini.v1 v1.67.0\n\tgopkg.in/yaml.v3 v3.0.1\n\tgorm.io/gorm v1.30.0\n)\n\nrequire (\n\tfilippo.io/edwards25519 v1.1.1 // indirect\n\tgithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect\n\tgithub.com/BurntSushi/toml v1.5.0 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect\n\tgithub.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect\n\tgithub.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 // indirect\n\tgithub.com/alibabacloud-go/debug v1.0.1 // indirect\n\tgithub.com/alibabacloud-go/tea v1.3.14 // indirect\n\tgithub.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect\n\tgithub.com/aliyun/credentials-go v1.4.7 // indirect\n\tgithub.com/andybalholm/brotli v1.0.4 // indirect\n\tgithub.com/aws/aws-sdk-go-v2 v1.41.0 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/config v1.32.5 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/credentials v1.19.5 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sso v1.30.7 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect\n\tgithub.com/aws/smithy-go v1.24.0 // indirect\n\tgithub.com/baidubce/bce-sdk-go v0.9.254 // indirect\n\tgithub.com/bodgit/plumbing v1.2.0 // indirect\n\tgithub.com/bodgit/sevenzip v1.3.0 // indirect\n\tgithub.com/bodgit/windows v1.0.0 // indirect\n\tgithub.com/bytedance/sonic v1.11.6 // indirect\n\tgithub.com/bytedance/sonic/loader v0.1.1 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.3.0 // indirect\n\tgithub.com/cenkalti/backoff/v5 v5.0.3 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/clbanning/mxj v1.8.4 // indirect\n\tgithub.com/clbanning/mxj/v2 v2.7.0 // indirect\n\tgithub.com/cloudwego/base64x v0.1.4 // indirect\n\tgithub.com/cloudwego/iasm v0.2.0 // indirect\n\tgithub.com/connesc/cipherio v0.2.1 // indirect\n\tgithub.com/containerd/console v1.0.5 // indirect\n\tgithub.com/containerd/containerd/api v1.10.0 // indirect\n\tgithub.com/containerd/containerd/v2 v2.2.1 // indirect\n\tgithub.com/containerd/continuity v0.4.5 // indirect\n\tgithub.com/containerd/errdefs v1.0.0 // indirect\n\tgithub.com/containerd/errdefs/pkg v0.3.0 // indirect\n\tgithub.com/containerd/log v0.1.0 // indirect\n\tgithub.com/containerd/platforms v1.0.0-rc.2 // indirect\n\tgithub.com/containerd/ttrpc v1.2.7 // indirect\n\tgithub.com/containerd/typeurl/v2 v2.2.3 // indirect\n\tgithub.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 // indirect\n\tgithub.com/distribution/reference v0.6.0 // indirect\n\tgithub.com/docker/buildx v0.31.1 // indirect\n\tgithub.com/docker/docker-credential-helpers v0.9.5 // indirect\n\tgithub.com/docker/go-units v0.5.0 // indirect\n\tgithub.com/dsnet/compress v0.0.1 // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/ebitengine/purego v0.9.1 // indirect\n\tgithub.com/fatih/color v1.18.0 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/fvbommel/sortorder v1.1.0 // indirect\n\tgithub.com/gabriel-vasile/mimetype v1.4.8 // indirect\n\tgithub.com/gammazero/toposort v0.1.1 // indirect\n\tgithub.com/gin-contrib/sse v0.1.0 // indirect\n\tgithub.com/glebarez/go-sqlite v1.22.0 // indirect\n\tgithub.com/go-acme/alidns-20150109/v4 v4.7.0 // indirect\n\tgithub.com/go-acme/esa-20240910/v2 v2.40.3 // indirect\n\tgithub.com/go-acme/tencentclouddnspod v1.1.25 // indirect\n\tgithub.com/go-ini/ini v1.67.0 // indirect\n\tgithub.com/go-jose/go-jose/v4 v4.1.3 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-ole/go-ole v1.2.6 // indirect\n\tgithub.com/go-playground/locales v0.14.1 // indirect\n\tgithub.com/go-playground/universal-translator v0.18.1 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // indirect\n\tgithub.com/goccy/go-json v0.10.5 // indirect\n\tgithub.com/goccy/go-yaml v1.9.8 // indirect\n\tgithub.com/gofrs/flock v0.13.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/golang/snappy v0.0.4 // indirect\n\tgithub.com/google/go-cmp v0.7.0 // indirect\n\tgithub.com/google/go-querystring v1.1.0 // indirect\n\tgithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect\n\tgithub.com/h2non/filetype v1.1.1 // indirect\n\tgithub.com/hashicorp/errwrap v1.1.0 // indirect\n\tgithub.com/hashicorp/go-multierror v1.1.1 // indirect\n\tgithub.com/hashicorp/go-version v1.8.0 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180 // indirect\n\tgithub.com/in-toto/in-toto-golang v0.9.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jackc/pgpassfile v1.0.0 // indirect\n\tgithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect\n\tgithub.com/jackc/puddle/v2 v2.2.2 // indirect\n\tgithub.com/jinzhu/inflection v1.0.0 // indirect\n\tgithub.com/jinzhu/now v1.1.5 // indirect\n\tgithub.com/jmespath/go-jmespath v0.4.0 // indirect\n\tgithub.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.8 // indirect\n\tgithub.com/klauspost/pgzip v1.2.5 // indirect\n\tgithub.com/kr/fs v0.1.0 // indirect\n\tgithub.com/leodido/go-urn v1.4.0 // indirect\n\tgithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect\n\tgithub.com/magiconair/properties v1.8.9 // indirect\n\tgithub.com/mattn/go-colorable v0.1.14 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.16 // indirect\n\tgithub.com/mattn/go-shellwords v1.0.12 // indirect\n\tgithub.com/minio/md5-simd v1.1.2 // indirect\n\tgithub.com/mitchellh/hashstructure/v2 v2.0.2 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgithub.com/moby/buildkit v0.27.0 // indirect\n\tgithub.com/moby/docker-image-spec v1.3.1 // indirect\n\tgithub.com/moby/go-archive v0.2.0 // indirect\n\tgithub.com/moby/locker v1.0.1 // indirect\n\tgithub.com/moby/moby/api v1.52.0 // indirect\n\tgithub.com/moby/moby/client v0.2.1 // indirect\n\tgithub.com/moby/patternmatcher v0.6.0 // indirect\n\tgithub.com/moby/sys/atomicwriter v0.1.0 // indirect\n\tgithub.com/moby/sys/sequential v0.6.0 // indirect\n\tgithub.com/moby/sys/signal v0.7.1 // indirect\n\tgithub.com/moby/sys/user v0.4.0 // indirect\n\tgithub.com/moby/sys/userns v0.1.0 // indirect\n\tgithub.com/moby/term v0.5.2 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect\n\tgithub.com/morikuni/aec v1.1.0 // indirect\n\tgithub.com/mozillazg/go-httpheader v0.2.1 // indirect\n\tgithub.com/namedotcom/go/v4 v4.0.2 // indirect\n\tgithub.com/ncruces/go-strftime v0.1.9 // indirect\n\tgithub.com/nrdcg/dnspod-go v0.4.0 // indirect\n\tgithub.com/nrdcg/freemyip v0.3.0 // indirect\n\tgithub.com/nrdcg/goacmedns v0.2.0 // indirect\n\tgithub.com/nrdcg/namesilo v0.5.0 // indirect\n\tgithub.com/nrdcg/porkbun v0.4.0 // indirect\n\tgithub.com/nwaples/rardecode/v2 v2.2.0 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/ovh/go-ovh v1.9.0 // indirect\n\tgithub.com/pelletier/go-toml v1.9.5 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.2.4 // indirect\n\tgithub.com/pierrec/lz4/v4 v4.1.15 // indirect\n\tgithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect\n\tgithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect\n\tgithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect\n\tgithub.com/rivo/uniseg v0.4.7 // indirect\n\tgithub.com/rs/xid v1.5.0 // indirect\n\tgithub.com/sagikazarmark/locafero v0.4.0 // indirect\n\tgithub.com/sagikazarmark/slog-shim v0.1.0 // indirect\n\tgithub.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect\n\tgithub.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect\n\tgithub.com/shibumi/go-pathspec v1.3.0 // indirect\n\tgithub.com/sigstore/sigstore v1.10.0 // indirect\n\tgithub.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 // indirect\n\tgithub.com/sourcegraph/conc v0.3.0 // indirect\n\tgithub.com/spf13/cast v1.7.0 // indirect\n\tgithub.com/spf13/pflag v1.0.10 // indirect\n\tgithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.12 // indirect\n\tgithub.com/therootcompany/xz v1.0.1 // indirect\n\tgithub.com/tjfoc/gmsm v1.4.1 // indirect\n\tgithub.com/tklauser/go-sysconf v0.3.16 // indirect\n\tgithub.com/tklauser/numcpus v0.11.0 // indirect\n\tgithub.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect\n\tgithub.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f // indirect\n\tgithub.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect\n\tgithub.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect\n\tgithub.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect\n\tgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirect\n\tgithub.com/ugorji/go/codec v1.2.12 // indirect\n\tgithub.com/ulikunitz/xz v0.5.15 // indirect\n\tgithub.com/volcengine/volc-sdk-golang v1.0.230 // indirect\n\tgithub.com/xhit/go-str2duration/v2 v2.1.0 // indirect\n\tgithub.com/yusufpapurcu/wmi v1.2.4 // indirect\n\tgo.mongodb.org/mongo-driver v1.17.6 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect\n\tgo.opentelemetry.io/otel v1.40.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.40.0 // indirect\n\tgo.opentelemetry.io/otel/sdk v1.40.0 // indirect\n\tgo.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.40.0 // indirect\n\tgo.opentelemetry.io/proto/otlp v1.7.1 // indirect\n\tgo.uber.org/multierr v1.11.0 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgo4.org v0.0.0-20200411211856-f5505b9728dd // indirect\n\tgolang.org/x/arch v0.8.0 // indirect\n\tgolang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect\n\tgolang.org/x/mod v0.31.0 // indirect\n\tgolang.org/x/sync v0.19.0 // indirect\n\tgolang.org/x/term v0.38.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/tools v0.39.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect\n\tgoogle.golang.org/grpc v1.77.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tmodernc.org/fileutil v1.3.3 // indirect\n\tmodernc.org/libc v1.66.1 // indirect\n\tmodernc.org/mathutil v1.7.1 // indirect\n\tmodernc.org/memory v1.11.0 // indirect\n\tmodernc.org/sqlite v1.38.0 // indirect\n)\n"
  },
  {
    "path": "agent/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\nfilippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=\nfilippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=\ngithub.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=\ngithub.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ=\ngithub.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=\ngithub.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=\ngithub.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=\ngithub.com/Shopify/sarama v1.30.1/go.mod h1:hGgx05L/DiW8XYBXeJdKIN6V2QUy2H6JqME5VT1NLRw=\ngithub.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=\ngithub.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0=\ngithub.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=\ngithub.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=\ngithub.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4=\ngithub.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=\ngithub.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=\ngithub.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=\ngithub.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=\ngithub.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=\ngithub.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=\ngithub.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=\ngithub.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=\ngithub.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=\ngithub.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=\ngithub.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=\ngithub.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=\ngithub.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 h1:Q00FU3H94Ts0ZIHDmY+fYGgB7dV9D/YX6FGsgorQPgw=\ngithub.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=\ngithub.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=\ngithub.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=\ngithub.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=\ngithub.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=\ngithub.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=\ngithub.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=\ngithub.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=\ngithub.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=\ngithub.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=\ngithub.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=\ngithub.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=\ngithub.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28=\ngithub.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw=\ngithub.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=\ngithub.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=\ngithub.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=\ngithub.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=\ngithub.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=\ngithub.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=\ngithub.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=\ngithub.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=\ngithub.com/alibabacloud-go/tea v1.3.14 h1:/Uzj5ZCFPpbPR+Bs7jfzsyXkYIVsi5TOIuQNOWwc/9c=\ngithub.com/alibabacloud-go/tea v1.3.14/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=\ngithub.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=\ngithub.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=\ngithub.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=\ngithub.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=\ngithub.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=\ngithub.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=\ngithub.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=\ngithub.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=\ngithub.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=\ngithub.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=\ngithub.com/aliyun/credentials-go v1.4.7 h1:T17dLqEtPUFvjDRRb5giVvLh6dFT8IcNFJJb7MeyCxw=\ngithub.com/aliyun/credentials-go v1.4.7/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=\ngithub.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=\ngithub.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=\ngithub.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30=\ngithub.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=\ngithub.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=\ngithub.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=\ngithub.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=\ngithub.com/aws/aws-sdk-go v1.55.0 h1:hVALKPjXz33kP1R9nTyJpUK7qF59dO2mleQxUW9mCVE=\ngithub.com/aws/aws-sdk-go v1.55.0/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=\ngithub.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=\ngithub.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4=\ngithub.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.5 h1:pz3duhAfUgnxbtVhIK39PGF/AHYyrzGEyRD9Og0QrE8=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.5/go.mod h1:xmDjzSUs/d0BB7ClzYPAZMmgQdrodNjPPhd6bGASwoE=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.5 h1:xMo63RlqP3ZZydpJDMBsH9uJ10hgHYfQFIk1cHDXrR4=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.5/go.mod h1:hhbH6oRcou+LpXfA/0vPElh/e0M3aFeOblE1sssAAEk=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=\ngithub.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM=\ngithub.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 h1:80pDB3Tpmb2RCSZORrK9/3iQxsd+w6vSzVqpT1FGiwE=\ngithub.com/aws/aws-sdk-go-v2/service/route53 v1.62.0/go.mod h1:6EZUGGNLPLh5Unt30uEoA+KQcByERfXIkax9qrc80nA=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.7 h1:eYnlt6QxnFINKzwxP5/Ucs1vkG7VT3Iezmvfgc2waUw=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.7/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk=\ngithub.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=\ngithub.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=\ngithub.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=\ngithub.com/baidubce/bce-sdk-go v0.9.254 h1:A7GtBOt7z2lnV7fqlZPZefhcBFg7z6iliUAhEOiIhoE=\ngithub.com/baidubce/bce-sdk-go v0.9.254/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=\ngithub.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8=\ngithub.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY=\ngithub.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM=\ngithub.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA=\ngithub.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=\ngithub.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=\ngithub.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=\ngithub.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=\ngithub.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=\ngithub.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=\ngithub.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=\ngithub.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\ngithub.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=\ngithub.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=\ngithub.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=\ngithub.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=\ngithub.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=\ngithub.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=\ngithub.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=\ngithub.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=\ngithub.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=\ngithub.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=\ngithub.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=\ngithub.com/compose-spec/compose-go/v2 v2.9.0 h1:UHSv/QHlo6QJtrT4igF1rdORgIUhDo1gWuyJUoiNNIM=\ngithub.com/compose-spec/compose-go/v2 v2.9.0/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=\ngithub.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs=\ngithub.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=\ngithub.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=\ngithub.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=\ngithub.com/containerd/cgroups/v3 v3.1.0 h1:azxYVj+91ZgSnIBp2eI3k9y2iYQSR/ZQIgh9vKO+HSY=\ngithub.com/containerd/cgroups/v3 v3.1.0/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=\ngithub.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4=\ngithub.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=\ngithub.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=\ngithub.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o=\ngithub.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM=\ngithub.com/containerd/containerd/v2 v2.2.0 h1:K7TqcXy+LnFmZaui2DgHsnp2gAHhVNWYaHlx7HXfys8=\ngithub.com/containerd/containerd/v2 v2.2.0/go.mod h1:YCMjKjA4ZA7egdHNi3/93bJR1+2oniYlnS+c0N62HdE=\ngithub.com/containerd/containerd/v2 v2.2.1 h1:TpyxcY4AL5A+07dxETevunVS5zxqzuq7ZqJXknM11yk=\ngithub.com/containerd/containerd/v2 v2.2.1/go.mod h1:NR70yW1iDxe84F2iFWbR9xfAN0N2F0NcjTi1OVth4nU=\ngithub.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=\ngithub.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=\ngithub.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=\ngithub.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=\ngithub.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=\ngithub.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=\ngithub.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=\ngithub.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=\ngithub.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=\ngithub.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=\ngithub.com/containerd/nydus-snapshotter v0.15.2 h1:qsHI4M+Wwrf6Jr4eBqhNx8qh+YU0dSiJ+WPmcLFWNcg=\ngithub.com/containerd/nydus-snapshotter v0.15.2/go.mod h1:FfwH2KBkNYoisK/e+KsmNr7xTU53DmnavQHMFOcXwfM=\ngithub.com/containerd/nydus-snapshotter v0.15.4 h1:l59kGRVMtwMLDLh322HsWhEsBCkRKMkGWYV5vBeLYCE=\ngithub.com/containerd/nydus-snapshotter v0.15.10 h1:hphjuKOqSHLGznNJiAvmsOWkdu4qFXjf4DzGrWSuIsM=\ngithub.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4=\ngithub.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=\ngithub.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=\ngithub.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=\ngithub.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4=\ngithub.com/containerd/stargz-snapshotter v0.17.0 h1:djNS4KU8ztFhLdEDZ1bsfzOiYuVHT6TgSU5qwRk+cNc=\ngithub.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=\ngithub.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=\ngithub.com/containerd/stargz-snapshotter/estargz v0.17.0 h1:+TyQIsR/zSFI1Rm31EQBwpAA1ovYgIKHy7kctL3sLcE=\ngithub.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=\ngithub.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=\ngithub.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=\ngithub.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=\ngithub.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=\ngithub.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 h1:OGNva6WhsKst5OZf7eZOklDztV3hwtTHovdrLHV+MsA=\ngithub.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=\ngithub.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=\ngithub.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=\ngithub.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/docker/buildx v0.29.1 h1:58hxM5Z4mnNje3G5NKfULT9xCr8ooM8XFtlfUK9bKaA=\ngithub.com/docker/buildx v0.29.1/go.mod h1:J4EFv6oxlPiV1MjO0VyJx2u5tLM7ImDEl9zyB8d4wPI=\ngithub.com/docker/buildx v0.30.1 h1:3vthfaTQOLt5QfN2nl7rKuPLUvx69nL5ZikFIXp//c8=\ngithub.com/docker/buildx v0.30.1/go.mod h1:8nwT0V6UNYNo9rXq6WO/BQd9KrJ0JYcY/QX6x0y1Oro=\ngithub.com/docker/buildx v0.31.1 h1:zbvbrb9nxBNVV8nnI33f2F+4aAZBA1gY+AmeBFflMqY=\ngithub.com/docker/buildx v0.31.1/go.mod h1:SD+jYLnt3S4SXqohVtV+8z+dihnOgwMJ8t+bLQvsaCk=\ngithub.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY=\ngithub.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM=\ngithub.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/compose/v2 v2.40.2 h1:h2bDBJkOuqmj93XvT2oI0ArPQonE0lGtWiILXdiXvbA=\ngithub.com/docker/compose/v2 v2.40.2/go.mod h1:CbSJpKGw20LInVsPjglZ8z7Squ3OBQOD7Ux5nkjGfIU=\ngithub.com/docker/compose/v2 v2.40.3 h1:XeYkQu1svDtyfZPv5nTwFryQ25ZJMkIlc4pz9HalMPI=\ngithub.com/docker/compose/v2 v2.40.3/go.mod h1:iNY1tvoHTyN3C3QHCuWAgj3OjR2T6mGkk/qxfbBF/4M=\ngithub.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=\ngithub.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=\ngithub.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=\ngithub.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=\ngithub.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=\ngithub.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=\ngithub.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=\ngithub.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=\ngithub.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=\ngithub.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=\ngithub.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=\ngithub.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=\ngithub.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=\ngithub.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=\ngithub.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=\ngithub.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=\ngithub.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=\ngithub.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=\ngithub.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=\ngithub.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=\ngithub.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=\ngithub.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=\ngithub.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=\ngithub.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=\ngithub.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=\ngithub.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=\ngithub.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=\ngithub.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=\ngithub.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=\ngithub.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=\ngithub.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=\ngithub.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=\ngithub.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=\ngithub.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=\ngithub.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=\ngithub.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg=\ngithub.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=\ngithub.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=\ngithub.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=\ngithub.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=\ngithub.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=\ngithub.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=\ngithub.com/go-acme/alidns-20150109/v4 v4.7.0 h1:PqJ/wR0JTpL4v0Owu1uM7bPQ1Yww0eQLAuuSdLjjQaQ=\ngithub.com/go-acme/alidns-20150109/v4 v4.7.0/go.mod h1:btQvB6xZoN6ykKB74cPhiR+uvhrEE2AFVXm6RDmCHm0=\ngithub.com/go-acme/esa-20240910/v2 v2.40.3 h1:xXOMRex148wKEHbv7Izn73/HdAxSmz5GOaF4HdnqN+M=\ngithub.com/go-acme/esa-20240910/v2 v2.40.3/go.mod h1:ZYdN9EN9ikn26SNapxCVjZ65pHT/1qm4fzuJ7QGVX6g=\ngithub.com/go-acme/lego/v4 v4.30.1 h1:tmb6U0lvy8Mc3lQbqKwTat7oAhE8FUYNJ3D0gSg6pJU=\ngithub.com/go-acme/lego/v4 v4.30.1/go.mod h1:V7m/Ip+EeFkjOe028+zeH+SwWtESxw1LHelwMIfAjm4=\ngithub.com/go-acme/tencentclouddnspod v1.1.25 h1:7H3ZKshkaHzCXfRpAHVB5nvxeDDl2XLeNZfrNHiZj/s=\ngithub.com/go-acme/tencentclouddnspod v1.1.25/go.mod h1:XXfzp0AYV7UAUsHKT6R0KAUJFhqAUXmWGF07Elpa5cE=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9zTgHSBoOOZ4CY=\ngithub.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo=\ngithub.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=\ngithub.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=\ngithub.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=\ngithub.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=\ngithub.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=\ngithub.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=\ngithub.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=\ngithub.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=\ngithub.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=\ngithub.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=\ngithub.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=\ngithub.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=\ngithub.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=\ngithub.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=\ngithub.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-resty/resty/v2 v2.17.0 h1:pW9DeXcaL4Rrym4EZ8v7L19zZiIlWPg5YXAcVmt+gN0=\ngithub.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=\ngithub.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=\ngithub.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=\ngithub.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=\ngithub.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=\ngithub.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=\ngithub.com/goccy/go-yaml v1.9.8 h1:5gMyLUeU1/6zl+WFfR1hN7D2kf+1/eRGa7DFtToiBvQ=\ngithub.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=\ngithub.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/goh-chunlin/go-onedrive v1.1.1 h1:HGtHk5iG0MZ92zYUtaY04czfZPBIJUr12UuFc+PW8m4=\ngithub.com/goh-chunlin/go-onedrive v1.1.1/go.mod h1:N8qIGHD7tryO734epiBKk5oXcpGwxKET/u3LuBHciTs=\ngithub.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=\ngithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=\ngithub.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=\ngithub.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=\ngithub.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=\ngithub.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=\ngithub.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=\ngithub.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=\ngithub.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=\ngithub.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=\ngithub.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=\ngithub.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=\ngithub.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=\ngithub.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=\ngithub.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4=\ngithub.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=\ngithub.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=\ngithub.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=\ngithub.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=\ngithub.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=\ngithub.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=\ngithub.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=\ngithub.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180 h1:uia+R3K1izQRGpxTV+bS4q3/ueMW7ProAMWqM6OlqOU=\ngithub.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=\ngithub.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=\ngithub.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=\ngithub.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=\ngithub.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=\ngithub.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=\ngithub.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=\ngithub.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=\ngithub.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=\ngithub.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=\ngithub.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=\ngithub.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=\ngithub.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=\ngithub.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=\ngithub.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=\ngithub.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=\ngithub.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=\ngithub.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=\ngithub.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=\ngithub.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=\ngithub.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=\ngithub.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=\ngithub.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=\ngithub.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=\ngithub.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=\ngithub.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=\ngithub.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU=\ngithub.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=\ngithub.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=\ngithub.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=\ngithub.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=\ngithub.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=\ngithub.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=\ngithub.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=\ngithub.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=\ngithub.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=\ngithub.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=\ngithub.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=\ngithub.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=\ngithub.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=\ngithub.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=\ngithub.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=\ngithub.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=\ngithub.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=\ngithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=\ngithub.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=\ngithub.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=\ngithub.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=\ngithub.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=\ngithub.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=\ngithub.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=\ngithub.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=\ngithub.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=\ngithub.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=\ngithub.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=\ngithub.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=\ngithub.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=\ngithub.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=\ngithub.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=\ngithub.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=\ngithub.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=\ngithub.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=\ngithub.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=\ngithub.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=\ngithub.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=\ngithub.com/minio/minio-go/v7 v7.0.74 h1:fTo/XlPBTSpo3BAMshlwKL5RspXRv9us5UeHEGYCFe0=\ngithub.com/minio/minio-go/v7 v7.0.74/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8=\ngithub.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=\ngithub.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/moby/buildkit v0.25.1 h1:j7IlVkeNbEo+ZLoxdudYCHpmTsbwKvhgc/6UJ/mY/o8=\ngithub.com/moby/buildkit v0.25.1/go.mod h1:phM8sdqnvgK2y1dPDnbwI6veUCXHOZ6KFSl6E164tkc=\ngithub.com/moby/buildkit v0.26.3 h1:D+ruZVAk/3ipRq5XRxBH9/DIFpRjSlTtMbghT5gQP9g=\ngithub.com/moby/buildkit v0.26.3/go.mod h1:4T4wJzQS4kYWIfFRjsbJry4QoxDBjK+UGOEOs1izL7w=\ngithub.com/moby/buildkit v0.27.0 h1:1gtNaMcVE0XXCZrybC32L79A7Ga1JeB7V3PfpCt1bDc=\ngithub.com/moby/buildkit v0.27.0/go.mod h1:4STUkNc5t1nf03HS+01UmI2X6FdfOI3XaKt9QNoTsms=\ngithub.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=\ngithub.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=\ngithub.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=\ngithub.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=\ngithub.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=\ngithub.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=\ngithub.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=\ngithub.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=\ngithub.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=\ngithub.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=\ngithub.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=\ngithub.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=\ngithub.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=\ngithub.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=\ngithub.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=\ngithub.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=\ngithub.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=\ngithub.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=\ngithub.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=\ngithub.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=\ngithub.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=\ngithub.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=\ngithub.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=\ngithub.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=\ngithub.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=\ngithub.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=\ngithub.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=\ngithub.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=\ngithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=\ngithub.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\ngithub.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=\ngithub.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=\ngithub.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=\ngithub.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/namedotcom/go/v4 v4.0.2 h1:4gNkPaPRG/2tqFNUUof7jAVsA6vDutFutEOd7ivnDwA=\ngithub.com/namedotcom/go/v4 v4.0.2/go.mod h1:J6sVueHMb0qbarPgdhrzEVhEaYp+R1SCaTGl2s6/J1Q=\ngithub.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q=\ngithub.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY=\ngithub.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g=\ngithub.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=\ngithub.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=\ngithub.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=\ngithub.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=\ngithub.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=\ngithub.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=\ngithub.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=\ngithub.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U=\ngithub.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=\ngithub.com/nrdcg/freemyip v0.3.0 h1:0D2rXgvLwe2RRaVIjyUcQ4S26+cIS2iFwnhzDsEuuwc=\ngithub.com/nrdcg/freemyip v0.3.0/go.mod h1:c1PscDvA0ukBF0dwelU/IwOakNKnVxetpAQ863RMJoM=\ngithub.com/nrdcg/goacmedns v0.2.0 h1:ADMbThobzEMnr6kg2ohs4KGa3LFqmgiBA22/6jUWJR0=\ngithub.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg=\ngithub.com/nrdcg/namesilo v0.5.0 h1:6QNxT/XxE+f5B+7QlfWorthNzOzcGlBLRQxqi6YeBrE=\ngithub.com/nrdcg/namesilo v0.5.0/go.mod h1:4UkwlwQfDt74kSGmhLaDylnBrD94IfflnpoEaj6T2qw=\ngithub.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=\ngithub.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=\ngithub.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=\ngithub.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.16.2 h1:HFB2fbVIlhIfCfOW81bZFbiC/RvnpXSdhbF2/DJr134=\ngithub.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=\ngithub.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=\ngithub.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=\ngithub.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=\ngithub.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=\ngithub.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=\ngithub.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=\ngithub.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=\ngithub.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=\ngithub.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=\ngithub.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=\ngithub.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=\ngithub.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=\ngithub.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=\ngithub.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE=\ngithub.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=\ngithub.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=\ngithub.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=\ngithub.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=\ngithub.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=\ngithub.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=\ngithub.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=\ngithub.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=\ngithub.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=\ngithub.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=\ngithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=\ngithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=\ngithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=\ngithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=\ngithub.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=\ngithub.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=\ngithub.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=\ngithub.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=\ngithub.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=\ngithub.com/qiniu/go-sdk/v7 v7.26.4 h1:D/k6cPbhjKvSx9hVBQSh97GuPu1ZtM3OLPCclSpusjo=\ngithub.com/qiniu/go-sdk/v7 v7.26.4/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=\ngithub.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=\ngithub.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=\ngithub.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=\ngithub.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=\ngithub.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=\ngithub.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=\ngithub.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=\ngithub.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=\ngithub.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=\ngithub.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=\ngithub.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=\ngithub.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=\ngithub.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LMdZGpHi4QYSH/9FZyNGqMYIA=\ngithub.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk=\ngithub.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g=\ngithub.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU=\ngithub.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=\ngithub.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=\ngithub.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=\ngithub.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=\ngithub.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sigstore/sigstore v1.10.0 h1:lQrmdzqlR8p9SCfWIpFoGUqdXEzJSZT2X+lTXOMPaQI=\ngithub.com/sigstore/sigstore v1.10.0/go.mod h1:Ygq+L/y9Bm3YnjpJTlQrOk/gXyrjkpn3/AEJpmk1n9Y=\ngithub.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 h1:94NLPmq4bxvdmslzcG670IOkrlS98CGpmob8cjpFHuI=\ngithub.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7/go.mod h1:4r/PNX0G7uzkLpc3PSdYs5E2k4bWEJNXTK6kwAyw9TM=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=\ngithub.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=\ngithub.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=\ngithub.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk=\ngithub.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=\ngithub.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=\ngithub.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=\ngithub.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=\ngithub.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=\ngithub.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=\ngithub.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=\ngithub.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=\ngithub.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=\ngithub.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=\ngithub.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=\ngithub.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.25/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.12 h1:/ABtv4x4FSGxGW0d6Sc88iQn6Up2LalWKwt/Tj7Dtz8=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.12/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=\ngithub.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=\ngithub.com/tencentyun/cos-go-sdk-v5 v0.7.54 h1:FRamEhNBbSeggyYfWfzFejTLftgbICocSYFk4PKTSV4=\ngithub.com/tencentyun/cos-go-sdk-v5 v0.7.54/go.mod h1:UN+VdbCl1hg+kKi5RXqZgaP+Boqfmk+D04GRc4XFk70=\ngithub.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=\ngithub.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=\ngithub.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=\ngithub.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=\ngithub.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=\ngithub.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=\ngithub.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=\ngithub.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=\ngithub.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=\ngithub.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=\ngithub.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=\ngithub.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19 h1:ZCmSnT6CLGhfoQ2lPEhL4nsJstKDCw1F1RfN8/smTCU=\ngithub.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19/go.mod h1:SXTY+QvI+KTTKXQdg0zZ7nx0u94QWh8ZAwBQYsW9cqk=\ngithub.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=\ngithub.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=\ngithub.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f h1:MoxeMfHAe5Qj/ySSBfL8A7l1V+hxuluj8owsIEEZipI=\ngithub.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=\ngithub.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f h1:Z4NEQ86qFl1mHuCu9gwcE+EYCwDKfXAYXZbdIXyxmEA=\ngithub.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=\ngithub.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE=\ngithub.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=\ngithub.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=\ngithub.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=\ngithub.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=\ngithub.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=\ngithub.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=\ngithub.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=\ngithub.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=\ngithub.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=\ngithub.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=\ngithub.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=\ngithub.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=\ngithub.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=\ngithub.com/upyun/go-sdk v2.1.0+incompatible h1:OdjXghQ/TVetWV16Pz3C1/SUpjhGBVPr+cLiqZLLyq0=\ngithub.com/upyun/go-sdk v2.1.0+incompatible/go.mod h1:eu3F5Uz4b9ZE5bE5QsCL6mgSNWRwfj0zpJ9J626HEqs=\ngithub.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=\ngithub.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=\ngithub.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=\ngithub.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=\ngithub.com/volcengine/volc-sdk-golang v1.0.230 h1:84/MOF0zUPtAHt3e1+MsFq5qrnQRC+e3XzTUwIOzZxw=\ngithub.com/volcengine/volc-sdk-golang v1.0.230/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=\ngithub.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=\ngithub.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=\ngithub.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=\ngithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=\ngithub.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngo.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=\ngo.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=\ngo.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=\ngo.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=\ngo.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=\ngo.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=\ngo.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 h1:2pn7OzMewmYRiNtv1doZnLo3gONcnMHlFnmOR8Vgt+8=\ngo.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0/go.mod h1:rjbQTDEPQymPE0YnRQp9/NuPwwtL0sesz/fnqRW/v84=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=\ngo.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=\ngo.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=\ngo.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=\ngo.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=\ngo.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=\ngo.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=\ngo.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=\ngo.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=\ngo.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=\ngo.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=\ngo.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=\ngo.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=\ngo.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=\ngo.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=\ngo.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=\ngo.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=\ngo.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngo4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=\ngo4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=\ngolang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=\ngolang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=\ngolang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=\ngolang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=\ngolang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=\ngolang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=\ngolang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=\ngolang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=\ngolang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=\ngolang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=\ngolang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=\ngolang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=\ngolang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=\ngolang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=\ngolang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=\ngolang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=\ngolang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=\ngolang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=\ngolang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=\ngolang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=\ngolang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=\ngolang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=\ngolang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=\ngolang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=\ngolang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=\ngolang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=\ngolang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=\ngolang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=\ngolang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=\ngolang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=\ngolang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=\ngolang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=\ngolang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=\ngolang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=\ngolang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=\ngolang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=\ngolang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=\ngolang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=\ngolang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=\ngolang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=\ngolang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=\ngolang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=\ngolang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=\ngolang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=\ngolang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=\ngolang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=\ngolang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=\ngonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=\ngonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=\ngoogle.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 h1:vk5TfqZHNn0obhPIYeS+cxIFKFQgser/M2jnI+9c6MM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101/go.mod h1:E17fc4PDhkr22dE3RgnH2hEubUaky6ZwW4VhANxyspg=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=\ngoogle.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=\ngoogle.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=\ngopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=\ngorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=\ngotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=\ngotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nmodernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=\nmodernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=\nmodernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=\nmodernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=\nmodernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=\nmodernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=\nmodernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=\nmodernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=\nmodernc.org/goabi0 v0.0.3 h1:y81b9r3asCh6Xtse6Nz85aYGB0cG3M3U6222yap1KWI=\nmodernc.org/goabi0 v0.0.3/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=\nmodernc.org/libc v1.66.1 h1:4uQsntXbVyAgrV+j6NhKvDiUypoJL48BWQx6sy9y8ok=\nmodernc.org/libc v1.66.1/go.mod h1:AiZxInURfEJx516LqEaFcrC+X38rt9G7+8ojIXQKHbo=\nmodernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=\nmodernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=\nmodernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=\nmodernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=\nmodernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=\nmodernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=\nmodernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=\nmodernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=\nmodernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=\nmodernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=\nmodernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=\nmodernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=\nmodernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=\nmodernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=\nnullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "agent/i18n/i18n.go",
    "content": "package i18n\n\nimport (\n\t\"embed\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/nicksnyder/go-i18n/v2/i18n\"\n\t\"golang.org/x/text/language\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nvar langFiles = map[string]string{\n\t\"zh\":      \"lang/zh.yaml\",\n\t\"en\":      \"lang/en.yaml\",\n\t\"zh-Hant\": \"lang/zh-Hant.yaml\",\n\t\"pt-BR\":   \"lang/pt-BR.yaml\",\n\t\"ja\":      \"lang/ja.yaml\",\n\t\"ru\":      \"lang/ru.yaml\",\n\t\"ms\":      \"lang/ms.yaml\",\n\t\"ko\":      \"lang/ko.yaml\",\n\t\"tr\":      \"lang/tr.yaml\",\n\t\"es-ES\":   \"lang/es-ES.yaml\",\n}\n\nfunc GetMsgWithMap(key string, maps map[string]interface{}) string {\n\tvar content string\n\tif maps == nil {\n\t\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID: key,\n\t\t})\n\t} else {\n\t\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID:    key,\n\t\t\tTemplateData: maps,\n\t\t})\n\t}\n\tcontent = strings.ReplaceAll(content, \": <no value>\", \"\")\n\tif content == \"\" {\n\t\treturn key\n\t} else {\n\t\treturn content\n\t}\n}\n\nfunc GetMsgWithDetail(key string, detail string) string {\n\tvar (\n\t\tcontent string\n\t\tdataMap = make(map[string]interface{})\n\t)\n\tdataMap[\"detail\"] = detail\n\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID:    key,\n\t\tTemplateData: dataMap,\n\t})\n\tif content != \"\" {\n\t\treturn content\n\t}\n\treturn key\n}\n\nfunc GetErrMsg(key string, maps map[string]interface{}) string {\n\tvar content string\n\tif maps == nil {\n\t\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID: key,\n\t\t})\n\t} else {\n\t\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID:    key,\n\t\t\tTemplateData: maps,\n\t\t})\n\t}\n\treturn content\n}\n\nfunc GetMsgByKey(key string) string {\n\tcontent, _ := global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID: key,\n\t})\n\treturn content\n}\n\nfunc Get(key string) string {\n\tcontent, _ := global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID: key,\n\t})\n\tif content != \"\" {\n\t\treturn content\n\t}\n\treturn key\n}\n\nfunc GetWithName(key string, name string) string {\n\tvar (\n\t\tdataMap = make(map[string]interface{})\n\t)\n\tdataMap[\"name\"] = name\n\tcontent, _ := global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID:    key,\n\t\tTemplateData: dataMap,\n\t})\n\treturn content\n}\n\nfunc GetWithNameAndErr(key string, name string, err error) string {\n\tvar (\n\t\tdataMap = make(map[string]interface{})\n\t)\n\tdataMap[\"name\"] = name\n\tdataMap[\"err\"] = err.Error()\n\tcontent, _ := global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID:    key,\n\t\tTemplateData: dataMap,\n\t})\n\treturn content\n}\n\n//go:embed lang/*\nvar fs embed.FS\nvar bundle *i18n.Bundle\n\nfunc UseI18n() gin.HandlerFunc {\n\treturn func(context *gin.Context) {\n\t\tlang := context.GetHeader(\"Accept-Language\")\n\t\tif lang == \"\" {\n\t\t\tlang = GetLanguageFromDB()\n\t\t}\n\t\tglobal.I18n = i18n.NewLocalizer(bundle, lang)\n\t}\n}\n\nfunc Init() {\n\tbundle = i18n.NewBundle(language.Chinese)\n\tbundle.RegisterUnmarshalFunc(\"yaml\", yaml.Unmarshal)\n\tisSuccess := true\n\tfor _, file := range langFiles {\n\t\tif _, err := bundle.LoadMessageFileFS(fs, file); err != nil {\n\t\t\tglobal.LOG.Errorf(\"[i18n] load language file %s failed: %v\\n\", file, err)\n\t\t\tisSuccess = false\n\t\t}\n\t}\n\n\tif !isSuccess {\n\t\tpanic(\"[i18n] failed to init language files, See log above for details\")\n\t}\n\tlang := GetLanguageFromDB()\n\tglobal.I18n = i18n.NewLocalizer(bundle, lang)\n}\n\nfunc GetLanguageFromDB() string {\n\tif global.DB == nil {\n\t\treturn \"en\"\n\t}\n\tlang, _ := repo.NewISettingRepo().GetValueByKey(\"Language\")\n\tif lang == \"\" {\n\t\treturn \"en\"\n\t}\n\treturn lang\n}\n"
  },
  {
    "path": "agent/i18n/lang/en.yaml",
    "content": "ErrInvalidParams: 'Bad request: {{ .detail }}'\nErrTokenParse: 'Token error: {{ .detail }}'\nErrInitialPassword: 'Original password is incorrect'\nErrInternalServer: 'Internal server error: {{ .detail }}'\nErrRecordExist: 'Record already exists'\nErrRecordNotFound: 'Record not found'\nErrStructTransform: 'Type conversion failed: {{ .err }}'\nErrNotLogin: 'Not logged in: {{ .detail }}'\nErrPasswordExpired: 'Password expired: {{ .detail }}'\nErrNotSupportType: 'Unsupported type: {{ .name }}'\nErrProxy: 'Request failed: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API access disabled: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Invalid API key: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP not in API allowlist: {{ .detail }}'\nErrApiConfigDisable: 'Endpoint not allowed via API: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Invalid API timestamp: {{ .detail }}'\nErrShutDown: 'Command stopped manually'\n\nErrMinQuickJump: 'Set at least one quick jump entry'\nErrMaxQuickJump: 'At most 4 quick jump entries'\n\n#common\nErrUsernameIsExist: 'Username already exists'\nErrNameIsExist: 'Name already exists'\nErrDemoEnvironment: 'Disabled in demo environment'\nErrCmdTimeout: 'Command timed out'\nErrCmdIllegal: 'Command has invalid characters'\nErrPortExist: '{{ .port }} port is already occupied by {{ .type }} [{{ .name }}]'\nTYPE_APP: 'Application'\nTYPE_RUNTIME: 'Runtime'\nTYPE_DOMAIN: 'Domain'\nErrTypePort: 'Invalid port format: {{ .name }}'\nErrTypePortRange: 'Port range must be 1-65535'\nSuccess: 'Success'\nFailed: 'Failed'\nSystemRestart: 'Task interrupted by system restart'\nErrGroupIsDefault: 'Default group, cannot be deleted'\nErrGroupIsInWebsiteUse: 'Group is in use by a website'\nDecrypt: \"Decrypt\"\n\n#agent\nErrAgentAccountBound: 'Account is bound to an agent and cannot be deleted'\nErrAgentAccountUnavailable: 'Account connection unavailable: {{ .err }}'\nErrAgentProviderNotSupported: 'Unsupported agent provider'\nErrAgentAccountRequired: 'Select an agent account first'\nErrAgentAccountNotVerified: 'Agent account not verified'\nErrAgentAccountModelsRequired: 'Please configure at least one model'\nErrAgentAccountSingleInitialModel: 'Only one initial model can be configured during account creation'\nErrAgentModelNotInAccount: 'Model is not configured in the selected account'\nErrAgentModelInUse: 'A bound agent is still using this model: {{ .name }}'\nErrAgentBaseURLRequired: 'Base URL is required'\nErrAgentApiKeyRequired: 'API key is required'\nErrAgentComposeRequired: 'Compose content is required'\nErrAgentIDRequired: 'Agent ID is required'\nErrAgentAccountIDRequired: 'Account ID is required'\nErrAgentLimitReached: 'Community Edition supports up to {{ .max }} AI agents. Upgrade to Professional Edition for unlimited agents'\n\n#backup\nLocalhost: 'Local'\nErrBackupInUsed: 'Backup account is in use by a scheduled task'\nErrBackupCheck: 'Backup account connectivity test failed: {{ .err }}'\nErrBackupLocalDelete: 'Local backup accounts cannot be deleted'\nErrBackupLocalCreate: 'Local backup accounts cannot be created'\n\n#app\nErrPortInUsed: '{{ .detail }} port is already occupied!'\nErrAppLimit: 'App install limit reached'\nErrNotInstall: 'Application not installed'\nErrPortInOtherApp: '{{ .port }} port is already occupied by application {{ .apps }}!'\nErrDbUserNotValid: 'Database username/password mismatch'\nErrUpdateBuWebsite: 'App updated, but website config update failed'\nErr1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}'\nErrFileParse: 'Failed to parse app docker-compose file'\nErrInstallDirNotFound: 'Install directory not found; use Force Uninstall'\nAppStoreIsUpToDate: 'The app store is already the latest version'\nLocalAppVersionNull: 'App {{ .name }} has no synced version'\nLocalAppVersionErr: 'Failed to sync {{ .name }} version {{ .version }}: {{ .err }}'\nErrFileNotFound: '{{ .name }} file does not exist'\nErrFileParseApp: 'Failed to parse {{ .name }}: {{ .err }}'\nErrAppDirNull: 'The version folder does not exist'\nLocalAppErr: 'Failed to sync app {{ .name }}: {{ .err }}'\nErrContainerName: 'Container name already exists'\nErrCreateHttpClient: 'Failed to create HTTP request: {{ .err }}'\nErrHttpReqTimeOut: 'HTTP request timed out: {{ .err }}'\nErrHttpReqFailed: 'HTTP request failed: {{ .err }}'\nErrNoSuchHost: 'Host resolve failed: {{ .err }}'\nErrHttpReqNotFound: 'Requested resource not found: {{ .err }}'\nErrContainerNotFound: '{{ .name }} container does not exist'\nErrContainerMsg: 'Container {{ .name }} is abnormal; check logs'\nErrAppBackup: 'App backup failed: {{ .name }} {{ .err }}'\nErrVersionTooLow: '1Panel version too low to update App Store'\nErrAppNameExist: 'The application name already exists'\nAppStoreIsSyncing: 'App Store is syncing; try again later'\nErrGetCompose: 'Failed to read docker-compose.yml: {{ .detail }}'\nErrAppWarn: 'App status abnormal; check logs'\nErrAppParamKey: 'Invalid app parameter: {{ .name }}'\nErrAppUpgrade: 'App upgrade failed: {{ .name }} {{ .err }}'\nAppRecover: 'Rollback application {{ .name }}'\nPullImageStart: 'Pull image [{{ .name }}]'\nPullImageSuccess: 'Image pulled'\nAppStoreSyncSuccess: 'App Store synced'\nSyncAppDetail: 'Sync app config'\nAppVersionNotMatch: 'Skip {{ .name }}: requires newer 1Panel'\nMoveSiteDir: 'Website dir changed; migrate OpenResty dir to {{ .name }}'\nMoveSiteDirSuccess: 'Website dir migrated'\nDeleteRuntimePHP: 'Delete PHP runtime'\nCustomAppStoreFileValid: 'App Store package must be .tar.gz'\nPullImageTimeout: 'Image pull timed out; try another mirror'\nErrAppIsDown: 'App {{ .name }} is abnormal'\nErrCustomApps: 'Installed apps detected; uninstall first'\nErrCustomRuntimes: 'Installed runtimes detected; delete first'\nErrAppVersionDeprecated: \"The {{ .name }} application is not compatible with the current 1Panel version, skipped\"\nErrDockerFailed: 'Docker is abnormal; check service status'\nErrDockerComposeCmdNotFound: 'Docker Compose not found on host'\nUseExistImage: 'Image exists; using existing image'\nErrDatabaseNotFound: 'Database not found: {{ .name }}'\n\n#ssh\nExportIP: \"Login IP\"\nExportArea: \"Location\"\nExportPort: \"Port\"\nExportAuthMode: \"Login Method\"\nExportUser: \"User\"\nExportStatus: \"Login Status\"\nExportDate: \"Time\"\n\n#file\nErrFileCanNotRead: 'File preview is not supported'\nErrFileToLarge: 'File is larger than 10 MB'\nErrPathNotFound: 'Directory does not exist'\nErrMovePathFailed: 'Target path cannot include source path'\nErrLinkPathNotFound: 'The target path does not exist!'\nErrFileIsExist: 'The file or folder already exists!'\nErrFileUpload: 'Upload failed: {{ .name }} {{ .detail }}'\nErrFileDownloadDir: 'Directory download is not supported'\nErrCmdNotFound: 'Command not found: {{ .name }}'\nErrSourcePathNotFound: 'Source directory does not exist'\nErrFavoriteExist: 'Path already in favorites'\nErrInvalidChar: 'Illegal characters are not allowed'\nErrPathNotDelete: 'The selected directory cannot be deleted'\nErrLogFileToLarge: 'Log file exceeds 500 MB'\nFileDropFailed: 'File cleanup failed: {{ .name }} {{ .err }}'\nFileDropSuccess: 'Cleaned {{ .name }}; files: {{ .count }}, freed: {{ .size }}'\nFileDropSum: 'Cleanup done; files: {{ .count }}, freed: {{ .size }}'\nErrBadDecrypt: 'Wrong archive password; extract failed'\n\n#website\nErrAliasIsExist: 'Alias already exists'\nErrBackupMatch: 'Backup data mismatch: {{ .detail }}'\nErrBackupExist: 'Backup source data missing: {{ .detail }}'\nErrPHPResource: 'Switching local runtime is not supported'\nErrPathPermission: 'Non-1000:1000 folder in index dir may cause Access denied'\nErrDomainIsUsed: 'The domain name is already used by the website [{{ .name }}]'\nErrDomainFormat: 'Invalid domain format: {{ .name }}'\nErrDefaultAlias: 'default is reserved; use another alias'\nErrParentWebsite: 'Delete subsite {{ .name }} first'\nErrBuildDirNotFound: 'The build directory does not exist'\nErrImageNotExist: 'Runtime image not found: {{ .name }}'\nErrProxyIsUsed: 'Load balancer is used by reverse proxy'\nErrSSLValid: 'Certificate file is invalid'\nErrWebsiteDir: 'Select a subdirectory under the website directory'\nErrComposerFileNotFound: \"composer.json file does not exist\"\nErrRuntimeNoPort: 'Runtime port is not set'\nStatus: 'Status'\nstart: 'Start'\nstop: 'Stop'\nrestart: 'Restart'\nkill: 'Force Stop'\npause: 'Pause'\nunpause: 'Resume'\nremove: 'Delete'\ndelete: 'Delete'\nErrDefaultWebsite: 'Default website already set; unset it first'\nSSL: 'Certificate'\nSet: 'Settings'\n\n#ssl\nErrSSLCannotDelete: 'Certificate {{ .name }} is in use by a website'\nErrAccountCannotDelete: 'Account is linked to a certificate and cannot be deleted'\nErrSSLApply: 'Certificate renewed; OpenResty reload failed'\nErrEmailIsExist: 'Email already exists'\nErrSSLKeyNotFound: 'The private key file does not exist'\nErrSSLCertificateNotFound: 'The certificate file does not exist'\nErrSSLKeyFormat: 'Private key file verification failed'\nErrSSLCertificateFormat: 'Invalid certificate format; use PEM'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid or EabHmacKey cannot be blank'\nErrOpenrestyNotFound: 'HTTP mode requires OpenResty'\nApplySSLStart: 'Starting certificate request. Domain: [{{ .domain }}], method: [{{ .type }}]'\ndnsAccount: 'DNS Auto'\ndnsManual: 'DNS Manual'\nhttp: 'HTTP'\nApplySSLFailed: 'Failed to request certificate for [{{ .domain }}]: {{ .detail }}'\nApplySSLSuccess: 'Certificate issued for [{{ .domain }}]'\nDNSAccountName: 'DNS account [{{ .name }}], provider [{{ .type }}]'\nPushDirLog: 'Certificate pushed to directory [{{ .path }}] {{ .status }}'\nErrDeleteCAWithSSL: 'CA has issued certificates; cannot delete'\nErrDeleteWithPanelSSL: 'Certificate is in use by Panel SSL settings'\nErrDefaultCA: 'The default authority cannot be deleted'\nApplyWebSiteSSLLog: 'Updating website certificate: {{ .name }}'\nErrUpdateWebsiteSSL: '{{ .name }} website certificate update failed: {{ .err }}'\nApplyWebSiteSSLSuccess: 'Website certificate updated'\nErrExecShell: 'Failed to execute script {{ .err }}'\nExecShellStart: 'Run script'\nExecShellSuccess: 'Script succeeded'\nStartUpdateSystemSSL: 'Update system certificate'\nUpdateSystemSSLSuccess: 'System certificate updated'\nErrWildcardDomain: 'Wildcard certificates are not supported in HTTP mode'\nErrApplySSLCanNotDelete: 'Certificate {{ .name }} is being issued and cannot be deleted yet'\nStartPushSSLToNode: 'Push certificate to node'\nPushSSLToNodeFailed: \"Failed to push certificate to node: {{ .err }}\"\nPushSSLToNodeSuccess: 'Certificate pushed to node'\n\n#mysql\nErrUserIsExist: 'User already exists'\nErrDatabaseIsExist: 'Database already exists'\nErrExecTimeOut: 'SQL execution timed out'\nErrRemoteExist: 'Remote database name already exists'\nErrLocalExist: 'Local database name already exists'\nRemoteBackup: 'Start local DB container with image {{ .name }} before backing up remote DB'\nRemoteRecover: 'Start local DB container with image {{ .name }} before restoring remote DB'\nArg: \"Argument\"\n\n#redis\nErrTypeOfRedis: 'Backup file type does not match current persistence mode'\n\n#container\nErrInUsed: \"{{ .detail }} is in use and cannot be deleted!\"\nErrObjectInUsed: \"This object is in use and cannot be deleted!\"\nErrObjectBeDependent: \"This image is referenced by other images and cannot be deleted!\"\nErrPortRules: 'Port count does not match'\nErrPgImagePull: 'Image pull timed out; configure acceleration or pull {{ .name }} manually'\nPruneHelper: \"Cleanup {{ .name }} released {{ .size }} disk space\"\nImageRemoveHelper: \"Removed image {{ .name }}, released {{ .size }} disk space\"\nBuildCache: 'Build Cache'\nVolume: 'Volume'\nNetwork: \"Network\"\nPruneStart: 'Cleaning in progress'\nContainerBackupPrepare: 'Prepare container backup workspace'\nContainerBackupStop: 'Stop container before backup'\nContainerBackupInspect: 'Backup container inspect and network'\nContainerBackupMounts: 'Backup container mounts'\nContainerBackupMeta: 'Write container backup metadata'\nContainerBackupStart: 'Start container after backup'\nContainerRecoverPrepare: 'Prepare container restore workspace'\nContainerRecoverExtract: 'Extract container backup archive'\nContainerRecoverParse: 'Load container backup metadata'\nContainerRecoverCreate: 'Recreate container'\nContainerRecoverMounts: 'Restore container mounts'\nContainerRecoverStart: 'Start restored container'\nContainerRecoverCleanup: 'Cleanup container restore workspace'\nComposeBackupPrepare: 'Prepare compose backup workspace'\nComposeBackupStop: 'Stop compose before backup'\nComposeBackupFiles: 'Backup compose files'\nComposeBackupContainers: 'Backup compose containers'\nComposeBackupMeta: 'Write compose backup metadata'\nComposeBackupStart: 'Start compose after backup'\nComposeBackupCleanup: 'Cleanup compose backup workspace'\nComposeRecoverPrepare: 'Prepare compose restore workspace'\nComposeRecoverExtract: 'Extract compose backup archive'\nComposeRecoverMeta: 'Load compose backup metadata'\nComposeRecoverMetaLogName: 'Compose restore meta: name={{ .name }}'\nComposeRecoverMetaLogPath: 'Compose restore meta: backupPath={{ .backupPath }} targetDir={{ .targetDir }}'\nComposeRecoverMetaLogCount: 'Compose restore meta: files={{ .files }} containers={{ .containers }}'\nComposeRecoverFiles: 'Restore compose files'\nComposeRecoverContainers: 'Restore compose containers'\nComposeRecoverRecord: 'Save compose record'\nComposeRecoverCleanup: 'Cleanup compose restore workspace'\n\n#runtime\nErrFileNotExist: 'Source file not found: {{ .detail }}'\nErrImageBuildErr: 'Image build failed'\nErrImageExist: \"Image already exists. Use a different image name.\"\nErrDelWithWebsite: 'Runtime is linked to a website and cannot be deleted'\nErrRuntimeStart: 'Startup failed'\nErrPackageJsonNotFound: 'package.json file does not exist'\nErrScriptsNotFound: 'No scripts section found in package.json'\nErrContainerNameNotFound: 'Container name not found; check .env'\nErrNodeModulesNotFound: 'node_modules not found; edit runtime or wait for startup'\nErrContainerNameIsNull: 'Container name is empty'\nErrPHPPortIsDefault: 'Port 9000 is reserved as default; use another port'\nErrPHPRuntimePortFailed: 'Port {{ .name }} is already used by this runtime'\n\n#tool\nErrConfigNotFound: 'Configuration file does not exist'\nErrConfigParse: 'Invalid configuration file format'\nErrConfigIsNull: 'Configuration file cannot be empty'\nErrConfigDirNotFound: 'Run directory not found'\nErrConfigAlreadyExist: 'Configuration file with the same name already exists'\nErrUserFindErr: 'Failed to find user {{ .name }}: {{ .err }}'\n\n#cronjob\nCutWebsiteLogSuccess: 'Website log rotated: {{ .name }} (backup: {{ .path }})'\nHandleShell: 'Run script {{ .name }}'\nHandleCurl: 'Request URL {{ .name }}'\nHandleNtpSync: 'Sync system time'\nHandleSystemClean: 'Clean system cache'\nSystemLog: 'System Log'\nCutWebsiteLog: 'Rotate Website Log'\nFileOrDir: 'Directory / File'\nUploadFile: 'Upload backup {{ .file }} to {{ .backup }}'\nUpload: \"Upload\"\nIgnoreBackupErr: 'Backup failed: {{ .detail }} (ignored)'\nIgnoreUploadErr: 'Upload failed: {{ .detail }} (ignored)'\nLoadBackupFailed: 'Failed to get backup account connection info: {{ .detail }}'\nInExecuting: 'Task is already running'\nNoSuchResource: 'No backup records found; skipped'\nCleanLog: 'Clean logs'\nCleanLogByName: 'Clean {{ .name }} logs'\nWafIpGroupNotFound: \"WAF IP group not found. Go to [Advanced Features -> WAF -> Black/White List -> IP Group] and add one using Remote Download.\"\n\n#toolbox\nErrNotExistUser: 'User does not exist'\nErrBanAction: 'Config failed: service {{ .name }} unavailable'\nErrClamdscanNotFound: 'clamdscan command not found'\nTaskSwapSet: 'Set swap'\nSetSwap: \"Set Swap {{ .path }} - {{ .size }}\"\nCreateSwap: 'Create swap file'\nFormatSwap: 'Format swap file'\nEnableSwap: 'Enable swap'\n\n#tamper\nCleanTamperSetting: 'Clean historical protection settings'\nSetTamperWithRules: 'Enable directory protection by rules'\nProtectDir: \"Protect directory {{ .name }}\"\nIgnoreFile: \"Protect file {{ .name }}\"\nTamperSetSuccessful: 'Protection set. Monitoring these directories:'\n\n#waf\nErrScope: 'This config cannot be changed'\nErrStateChange: 'Failed to change state'\nErrRuleExist: 'Rule already exists'\nErrRuleNotExist: 'Rule does not exist'\nErrParseIP: 'Invalid IP format'\nErrDefaultIP: 'default is reserved; use another name'\nErrGroupInUse: 'IP group is used by blacklist/whitelist rules'\nErrIPGroupAclUse: 'IP group is used by website {{ .name }} custom rules'\nErrGroupExist: 'IP group name already exists'\nErrIPRange: 'Invalid IP range'\nErrIPExist: 'IP already exists'\nurlDefense: 'URL rules'\nurlHelper: 'Blocked URL'\ndirFilter: 'Directory filter'\nxss: 'XSS'\nphpExec: 'PHP script execution'\noneWordTrojan: 'One Word Trojan'\nappFilter: 'App dangerous directory filtering'\nwebShell: 'WebShell'\nargs: 'Parameter rules'\nprotocolFilter: 'Protocol filtering'\njavaFilter: 'Java dangerous file filtering'\nscannerFilter: 'Scanner filter'\nescapeFilter: 'Escape filtering'\ncustomRule: 'Custom rules'\nhttpMethod: 'HTTP method filtering'\nfileExt: 'File upload restrictions'\ndefaultIpBlack: 'Malicious IP group'\ncookie: 'Cookie Rules'\nurlBlack: 'URL blacklist'\nuaBlack: 'User-Agent blacklist'\nattackCount: 'Attack frequency limit'\nfileExtCheck: 'File upload restrictions'\ngeoRestrict: 'Regional access restrictions'\nunknownWebsite: 'Unauthorized domain name access'\nnotFoundCount: '404 Rate Limit'\nheaderDefense: 'Header rules'\ndefaultUaBlack: 'User-Agent rules'\nmethodWhite: 'HTTP rules'\ncaptcha: 'Human verification'\nfiveSeconds: '5-second verification'\nvulnCheck: 'Supplementary rules'\nacl: 'Custom rules'\nsql: 'SQL injection'\ncc: 'Access frequency limit'\ndefaultUrlBlack: 'URL rules'\nsqlInject: 'SQL injection'\nErrDBNotExist: 'Database does not exist'\nallow: 'Allow'\ndeny: 'Deny'\nOpenrestyNotFound: 'OpenResty is not installed'\nremoteIpIsNull: \"IP list is empty\"\nOpenrestyVersionErr: 'OpenResty version too low; upgrade to 1.27.1.2-2-2-focal'\nErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'\n\n#task\nTaskStart: '{{ .name }} [START]'\nTaskEnd: '{{ .name }} [DONE]'\nTaskFailed: '{{ .name }} task failed'\nTaskTimeout: '{{ .name }} timed out'\nTaskSuccess: '{{ .name }} succeeded'\nTaskRetry: 'Retry #{{ .name }}'\nSubTaskSuccess: '{{ .name }} succeeded'\nSubTaskFailed: '{{ .name }} failed: {{ .err }}'\nTaskInstall: 'Install'\nTaskUninstall: 'Uninstall'\nTaskCreate: 'Create'\nTaskDelete: 'Delete'\nTaskUpgrade: 'Upgrade'\nTaskUpdate: 'Update'\nTaskRestart: 'Restart'\nTaskProtect: \"Protect\"\nTaskBackup: 'Backup'\nTaskRecover: 'Recover'\nTaskRollback: 'Rollback'\nTaskPull: 'Pull'\nTaskCommit: 'Commit'\nTaskBuild: 'Build'\nTaskPush: 'Push'\nTaskClean: \"Cleanup\"\nTaskHandle: 'Execute'\nTaskImport: \"Import\"\nTaskExport: \"Export\"\nWebsite: 'Website'\nApp: 'Application'\nRuntime: 'Runtime'\nDatabase: 'Database'\nConfigFTP: 'Create FTP user {{ .name }}'\nConfigOpenresty: 'Create OpenResty config file'\nInstallAppSuccess: 'App {{ .name }} installed'\nConfigRuntime: 'Configure runtime'\nConfigApp: 'Configure app'\nSuccessStatus: '{{ .name }} succeeded'\nFailedStatus: '{{ .name }} failed: {{ .err }}'\nHandleLink: 'Process app links'\nHandleDatabaseApp: 'Process app parameters'\nExecShell: 'Run script {{ .name }}'\nPullImage: 'Pull image'\nStart: 'Start'\nRun: 'Start'\nStop: 'Stop'\nImage: 'Image'\nCompose: 'Compose'\nContainer: 'Container'\nAppLink: 'Linked Application'\nEnableSSL: 'Enable HTTPS'\nAppStore: 'App Store'\nTaskSync: 'Sync'\nLocalApp: 'Local Application'\nSubTask: 'Subtask'\nRuntimeExtension: 'Runtime extension'\nTaskIsExecuting: 'Task is running'\nCustomAppstore: 'Custom application repository'\nTaskExec: 'Execute'\nTaskBatch: \"Batch Operation\"\nFileConvert: 'File Conversion'\n\n# task - clam\nClamscan: \"Scan {{ .name }}\"\nTaskScan: \"Scan\"\n\n# task - ai\nOllamaModelPull: 'Pull Ollama model {{ .name }}'\nOllamaModelSize: 'Get Ollama model size: {{ .name }}'\n\n# task-snapshot\nSnapshot: 'Snapshot'\nSnapDBInfo: 'Write 1Panel DB info'\nSnapCopy: 'Copy files/directories {{ .name }}'\nSnapNewDB: 'Init DB connection {{ .name }}'\nSnapDeleteOperationLog: 'Delete operation logs'\nSnapDeleteLoginLog: 'Delete access logs'\nSnapDeleteMonitor: 'Delete monitoring data'\nSnapRemoveSystemIP: 'Remove system IP'\nSnapBaseInfo: 'Write 1Panel base info'\nSnapInstallAppImageEmpty: 'No app images selected; skipped'\nSnapInstallApp: 'Backup installed apps'\nSnapDockerSave: 'Compress installed applications'\nSnapLocalBackup: 'Backup local backup directory'\nSnapCompressBackup: 'Compress local backup directory'\nSnapPanelData: 'Backup 1Panel data directory'\nSnapCompressPanel: 'Compress data directory'\nSnapWebsite: 'Backup website directory'\nSnapCloseDBConn: 'Close DB connection'\nSnapCompress: 'Create snapshot file'\nSnapCompressFile: 'Compress snapshot file'\nSnapCheckCompress: 'Check snapshot archive'\nSnapCompressSize: 'Snapshot size {{ .name }}'\nSnapUpload: 'Upload snapshot file'\nSnapUploadTo: 'Upload snapshot to {{ .name }}'\nSnapUploadRes: 'Upload snapshot to {{ .name }}'\n\nSnapshotRecover: 'Recover snapshot'\nRecoverDownload: 'Download snapshot file'\nDownload: 'Download'\nRecoverDownloadAccount: 'Get snapshot download account {{ .name }}'\nRecoverDecompress: 'Decompress snapshot archive'\nDecompress: 'Decompression'\nBackupBeforeRecover: 'Backup system data before recovery'\nReadJson: 'Read snapshot JSON file'\nReadJsonPath: 'Get snapshot JSON path'\nReadJsonContent: 'Read JSON content'\nReadJsonMarshal: 'Json escape processing'\nRecoverApp: 'Restore installed apps'\nRecoverWebsite: 'Restore website directory'\nRecoverAppImage: 'Restore snapshot image backup'\nRecoverCompose: 'Restore other compose projects'\nRecoverComposeList: 'Get all compose projects to restore'\nRecoverComposeItem: 'Recover compose {{ .name }}'\nRecoverAppEmpty: 'No app image backup found in snapshot'\nRecoverBaseData: 'Restore base data and files'\nRecoverDaemonJsonEmpty: 'daemon.json not found in snapshot or current host'\nRecoverDaemonJson: 'Restore daemon.json'\nRecoverDBData: 'Recover database data'\nRecoverBackups: 'Restore local backup directory'\nRecoverPanelData: 'Restore data directory'\n\n# task - container\nContainerImagePull: 'Pull container image {{ .name }}'\nContainerRemoveOld: 'Remove the original container {{ .name }}'\nContainerImageCheck: 'Check image pull result'\nContainerLoadInfo: 'Load container info'\nContainerRecreate: 'Container update failed; restoring original container'\nContainerCreate: 'Create a new container {{ .name }}'\nContainerCreateFailed: 'Container create failed; deleting failed container'\nContainerStartCheck: 'Check container started'\n\n# task - image\nImageBuild: 'Build image'\nImagePull: 'Pull image'\nImageRepoAuthFromDB: 'Load registry auth from DB'\nImagePush: 'Push image'\nImageRenameTag: 'Update image tag'\nImageNewTag: 'New image tag {{ .name }}'\nComposeCreate: 'Create compose project'\n\n# task - website\nBackupNginxConfig: 'Backup website OpenResty config'\nCompressFileSuccess: 'Directory compressed to {{ .name }}'\nCompressDir: 'Compress directory'\nDeCompressFile: 'Decompress file {{ .name }}'\nErrCheckValid: 'Backup file validation failed: {{ .name }}'\nRollback: 'Rollback'\nwebsiteDir: 'Website directory'\nRecoverFailedStartRollBack: 'Recovery failed; starting rollback'\nAppBackupFileIncomplete: 'Backup is incomplete: missing app.json or app.tar.gz'\nAppAttributesNotMatch: 'Application type or name does not match'\n\n#alert\nErrAlertSync: 'Failed to sync alert message'\n\n#task - runtime\nErrInstallExtension: 'Another extension install task is running'\n\n# alert mail template\nPanelAlertTitle: \"Panel Alert Notification\"\nTestAlertTitle: \"Test Email - Verify Email Connectivity\"\nTestAlert: \"This is a test email to verify that your email sending configuration is correct.\"\nLicenseExpirationAlert: \"Panel {{ .node }}{{ .ip }}: license expires in {{ .day }} days. Log in to view details.\"\nCronJobFailedAlert: \"Panel {{ .node }}{{ .ip }}: scheduled task {{ .name }} failed. Log in to view details.\"\nClamAlert: \"Panel {{ .node }}{{ .ip }}: virus scan found {{ .num }} infected files. Log in to view details.\"\nWebSiteAlert: \"Panel {{ .node }}{{ .ip }}: {{ .num }} websites expire in {{ .day }} days. Log in to view details.\"\nSSLAlert: \"Panel {{ .node }}{{ .ip }}: {{ .num }} SSL certificates expire in {{ .day }} days. Log in to view details.\"\nDiskUsedAlert: \"Panel {{ .node }}{{ .ip }}: disk {{ .name }} usage reached {{ .used }}. Log in to view details.\"\nResourceAlert: \"Panel {{ .node }}{{ .ip }}: average {{ .name }} usage in {{ .time }} minutes is {{ .used }}. Log in to view details.\"\nPanelVersionAlert: \"Panel {{ .node }}{{ .ip }}: a new panel version is available. Log in to view details.\"\nPanelPwdExpirationAlert: \"Panel {{ .node }}{{ .ip }}: panel password expires in {{ .day }} days. Log in to view details.\"\nCommonAlert: \"Panel {{ .node }}{{ .ip }}: {{ .msg }}. Log in to view details.\"\nNodeExceptionAlert: \"Panel {{ .node }}{{ .ip }}: {{ .num }} nodes are abnormal. Log in to view details.\"\nLicenseExceptionAlert: \"Panel {{ .node }}{{ .ip }}: {{ .num }} licenses are abnormal. Log in to view details.\"\nSSHAndPanelLoginAlert: \"Panel {{ .node }}{{ .ip }}: abnormal {{ .name }} login from {{ .loginIp }}. Log in to view details.\"\n\n#disk\nDeviceNotFound: \"Device {{ .name }} not found\"\nDeviceIsMounted: 'Device {{ .name }} is mounted; unmount first'\nPartitionDiskErr: 'Partition failed: {{ .err }}'\nFormatDiskErr: 'Format failed: {{ .err }}'\nMountDiskErr: 'Mount failed: {{ .err }}'\nXfsNotFound: 'xfs not found; install xfsprogs first'\n"
  },
  {
    "path": "agent/i18n/lang/es-ES.yaml",
    "content": "ErrInvalidParams: 'Solicitud inválida: {{ .detail }}'\nErrTokenParse: 'Token inválido: {{ .detail }}'\nErrInitialPassword: 'Contraseña original incorrecta'\nErrInternalServer: 'Error interno: {{ .detail }}'\nErrRecordExist: 'Registro ya existe'\nErrRecordNotFound: 'Registro no encontrado'\nErrStructTransform: 'Error de conversión: {{ .err }}'\nErrNotLogin: 'No has iniciado sesión: {{ .detail }}'\nErrPasswordExpired: 'Contraseña expirada: {{ .detail }}'\nErrNotSupportType: 'Tipo no soportado: {{ .name }}'\nErrProxy: 'Solicitud fallida: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API desactivada: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Clave API inválida: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP no permitida: {{ .detail }}'\nErrApiConfigDisable: 'API no permitida: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Timestamp inválido: {{ .detail }}'\nErrShutDown: 'Comando detenido manualmente'\nErrMinQuickJump: 'Agrega al menos una entrada rápida'\nErrMaxQuickJump: 'Máximo 4 entradas rápidas'\nErrUsernameIsExist: 'Usuario ya existe'\nErrNameIsExist: 'Nombre ya existe'\nErrDemoEnvironment: 'No disponible en demo'\nErrCmdTimeout: 'Comando agotó tiempo'\nErrCmdIllegal: 'Comando contiene caracteres inválidos'\nErrPortExist: 'El puerto {{ .port }} ya está ocupado por {{ .type }} [{{ .name }}]'\nTYPE_APP: 'Aplicación'\nTYPE_RUNTIME: 'Entorno de ejecución'\nTYPE_DOMAIN: 'Nombre de dominio'\nErrTypePort: 'El formato del puerto {{ .name }} es incorrecto'\nErrTypePortRange: 'El rango de puertos debe estar entre 1-65535'\nSuccess: 'Éxito'\nFailed: 'Error'\nSystemRestart: 'La tarea fue interrumpida debido a un reinicio del sistema'\nErrGroupIsDefault: 'Grupo predeterminado, no se puede eliminar'\nErrGroupIsInWebsiteUse: 'El grupo está siendo usado por otro sitio web y no se puede eliminar.'\nDecrypt: 'Descifrar'\nErrAgentAccountBound: 'Cuenta vinculada a agente'\nErrAgentAccountUnavailable: 'Conexión de cuenta no disponible: {{ .err }}'\nErrAgentProviderNotSupported: 'Proveedor de agente no soportado'\nErrAgentAccountRequired: 'Elige una cuenta de agente primero'\nErrAgentAccountNotVerified: 'Cuenta de agente no verificada'\nErrAgentAccountModelsRequired: 'Configure al menos un modelo'\nErrAgentAccountSingleInitialModel: 'Solo se puede configurar un modelo inicial al crear la cuenta'\nErrAgentModelNotInAccount: 'El modelo no está configurado en la cuenta seleccionada'\nErrAgentModelInUse: 'Un agente vinculado todavía usa este modelo: {{ .name }}'\nErrAgentBaseURLRequired: 'URL base requerida'\nErrAgentApiKeyRequired: 'Clave API requerida'\nErrAgentComposeRequired: 'Contenido compose requerido'\nErrAgentIDRequired: 'ID de agente requerido'\nErrAgentAccountIDRequired: 'ID de cuenta requerido'\nErrAgentLimitReached: 'La edición Community admite hasta {{ .max }} agentes de IA. Actualice a la edición Professional para eliminar el límite'\nLocalhost: 'Máquina local'\nErrBackupInUsed: 'Cuenta de respaldo en uso por tarea programada'\nErrBackupCheck: 'Conexión de respaldo falló: {{ .err }}'\nErrBackupLocalDelete: 'No se puede eliminar cuentas de respaldo locales'\nErrBackupLocalCreate: 'No se pueden crear cuentas de respaldo locales'\nErrPortInUsed: 'El puerto {{ .detail }} ya está ocupado'\nErrAppLimit: 'El número de aplicaciones instaladas ha superado el límite'\nErrNotInstall: 'Aplicación no instalada'\nErrPortInOtherApp: 'El puerto {{ .port }} ya está ocupado por la aplicación {{ .apps }}'\nErrDbUserNotValid: 'Base de datos existente, usuario y contraseña no coinciden'\nErrUpdateBuWebsite: 'La aplicación se actualizó correctamente, pero falló la modificación del archivo de configuración del sitio web. revise la configuración'\nErr1PanelNetworkFailed: 'Fallo la creación de la red por defecto del contenedor {{ .detail }}'\nErrFileParse: 'Fallo al analizar el archivo docker-compose de la aplicación'\nErrInstallDirNotFound: 'El directorio de instalación no existe. Si desea desinstalar, seleccione Desinstalación forzada'\nAppStoreIsUpToDate: 'La tienda de aplicaciones ya está en la última versión'\nLocalAppVersionNull: 'La aplicación {{ .name }} no está sincronizada a la versión No se puede agregar a la lista de aplicaciones'\nLocalAppVersionErr: 'La sincronización de la versión {{ .version }} de {{ .name }} falló {{ .err }}'\nErrFileNotFound: 'El archivo {{ .name }} no existe'\nErrFileParseApp: 'El archivo {{ .name }} falló al analizarse {{ .err }}'\nErrAppDirNull: 'La carpeta de la versión no existe'\nLocalAppErr: 'La sincronización de la aplicación {{ .name }} falló {{ .err }}'\nErrContainerName: 'El nombre del contenedor ya existe'\nErrCreateHttpClient: 'Fallo al crear la solicitud {{ .err }}'\nErrHttpReqTimeOut: 'La solicitud superó el tiempo de espera {{ .err }}'\nErrHttpReqFailed: 'La solicitud falló {{ .err }}'\nErrNoSuchHost: 'No se pudo encontrar el servidor solicitado {{ .err }}'\nErrHttpReqNotFound: 'No se pudo encontrar el recurso solicitado {{ .err }}'\nErrContainerNotFound: 'El contenedor {{ .name }} no existe'\nErrContainerMsg: 'El contenedor {{ .name }} presenta anomalías. Revise el log en la página de contenedores para más detalles'\nErrAppBackup: 'Falló el respaldo de la aplicación {{ .name }} {{ .err }}'\nErrVersionTooLow: 'La versión actual de 1Panel es demasiado baja para actualizar la tienda de aplicaciones. Actualice primero.'\nErrAppNameExist: 'El nombre de la aplicación ya existe'\nAppStoreIsSyncing: 'La tienda de aplicaciones está sincronizando, inténtelo más tarde'\nErrGetCompose: 'Fallo al obtener el archivo docker-compose.yml {{ .detail }}'\nErrAppWarn: 'Estado anómalo, revise el log'\nErrAppParamKey: 'El campo de parámetro {{ .name }} es anómalo'\nErrAppUpgrade: 'La actualización de la aplicación {{ .name }} falló {{ .err }}'\nAppRecover: 'Revertir la aplicación {{ .name }}'\nPullImageStart: 'Iniciar descarga de la imagen [{{ .name }}]'\nPullImageSuccess: 'Descarga de imagen exitosa'\nAppStoreSyncSuccess: 'Sincronización de la tienda de aplicaciones exitosa'\nSyncAppDetail: 'Sincronizar configuración de la aplicación'\nAppVersionNotMatch: 'La aplicación {{ .name }} requiere una versión superior de 1Panel, se omite la sincronización'\nMoveSiteDir: 'La actualización actual requiere migrar el directorio del sitio web OpenResty'\nMoveSiteDirSuccess: 'Migración del directorio del sitio exitosa'\nDeleteRuntimePHP: 'Eliminar entorno de ejecución PHP'\nCustomAppStoreFileValid: 'Los paquetes de la tienda de aplicaciones deben estar en formato.tar.gz'\nPullImageTimeout: 'Tiempo de espera al descargar la imagen, intente aumentar la aceleración o use otra'\nErrAppIsDown: 'El estado de la aplicación {{ .name }} es anómalo, revise'\nErrCustomApps: 'Hay una aplicación instalada, desinstálela primero'\nErrCustomRuntimes: 'Hay un entorno de ejecución instalado, elimínelo primero'\nErrAppVersionDeprecated: 'La aplicación {{ .name }} no es compatible con la versión actual de 1Panel, omitida'\nErrDockerFailed: 'El estado de Docker es anómalo, revise el servicio'\nErrDockerComposeCmdNotFound: 'El comando Docker Compose no existe instálelo primero en el host'\nUseExistImage: 'La imagen ya existe, usando imagen existente'\nErrDatabaseNotFound: 'La base de datos {{ .name }} no existe'\nExportIP: 'IP de inicio de sesión'\nExportArea: 'Ubicación'\nExportPort: 'Puerto'\nExportAuthMode: 'Método de inicio de sesión'\nExportUser: 'Usuario'\nExportStatus: 'Estado de inicio de sesión'\nExportDate: 'Fecha y hora'\nErrFileCanNotRead: 'Este archivo no soporta vista previa'\nErrFileToLarge: 'El archivo es mayor a 10M y no puede abrirse'\nErrPathNotFound: 'El directorio no existe'\nErrMovePathFailed: 'La ruta de destino no puede contener la ruta original'\nErrLinkPathNotFound: 'La ruta de destino no existe'\nErrFileIsExist: 'El archivo o carpeta ya existe'\nErrFileUpload: '{{ .name }} falló al subir archivo {{ .detail }}'\nErrFileDownloadDir: 'No se admite la descarga de carpetas'\nErrCmdNotFound: 'El comando {{ .name }} no existe, instálelo primero en el host'\nErrSourcePathNotFound: 'El directorio fuente no existe'\nErrFavoriteExist: 'Esta ruta ya ha sido marcada como favorita'\nErrInvalidChar: 'No se permiten caracteres ilegales'\nErrPathNotDelete: 'No se puede eliminar el directorio seleccionado'\nErrLogFileToLarge: 'Log file exceeds 500 MB'\nFileDropFailed: 'Error al limpiar el archivo {{ .name }}: {{ .err }}'\nFileDropSuccess: 'Archivo {{ .name }} limpiado correctamente, {{ .count }} archivos eliminados, {{ .size }} de espacio liberado'\nFileDropSum: 'Limpieza de archivos completada, total {{ .count }} archivos eliminados, {{ .size }} de espacio liberado'\nErrBadDecrypt: 'Error de contraseña de descifrado del paquete comprimido, descompresión fallida, compruebe e inténtelo de nuevo'\nErrAliasIsExist: 'El alias ya existe'\nErrBackupMatch: 'El archivo de respaldo no coincide con parte de los datos actuales del sitio web {{ .detail }}'\nErrBackupExist: 'Parte de los datos fuente del respaldo no existe {{ .detail }}'\nErrPHPResource: 'El entorno local no soporta cambio'\nErrPathPermission: 'Se detectó una carpeta con permisos distintos a 1000:1000 en el directorio index, lo cual puede causar errores de acceso denegado. Haga clic en Guardar arriba'\nErrDomainIsUsed: 'El dominio ya está en uso por el sitio web [{{ .name }}]'\nErrDomainFormat: 'El formato del dominio {{ .name }} es incorrecto'\nErrDefaultAlias: 'default es un código reservado, use otro'\nErrParentWebsite: 'Primero debe eliminar el sub-sitio {{ .name }}'\nErrBuildDirNotFound: 'El directorio de compilación no existe'\nErrImageNotExist: 'La imagen del entorno {{ .name }} no existe, edítela de nuevo'\nErrProxyIsUsed: 'El balanceo de carga ya está usado por un proxy reverso, no se puede eliminar'\nErrSSLValid: 'Archivo de certificado anómalo, revise el estado del certificado'\nErrWebsiteDir: 'seleccione un directorio dentro del directorio del sitio web'\nErrComposerFileNotFound: 'El archivo composer.json no existe'\nErrRuntimeNoPort: 'El entorno de ejecución no tiene configurado un puerto, edítelo primero'\nStatus: 'Estado'\nstart: 'Iniciar'\nstop: 'Detener'\nrestart: 'Reiniciar'\nkill: 'Detener Forzosamente'\npause: 'Pausar'\nunpause: 'Reanudar'\nremove: 'Eliminar'\ndelete: 'Eliminar'\nErrDefaultWebsite: 'El sitio web predeterminado ya está configurado, cancélelo antes de configurar'\nSSL: 'Certificado'\nSet: 'Configuración'\nErrSSLCannotDelete: 'El certificado {{ .name }} está siendo utilizado por un sitio web y no puede eliminarse'\nErrAccountCannotDelete: 'La cuenta está asociada a un certificado y no puede eliminarse'\nErrSSLApply: 'Renovación del certificado exitosa, recarga de openresty fallida, revise la configuración'\nErrEmailIsExist: 'El correo ya existe'\nErrSSLKeyNotFound: 'El archivo de clave privada no existe'\nErrSSLCertificateNotFound: 'El archivo de certificado no existe'\nErrSSLKeyFormat: 'Error en la verificación del archivo de clave privada'\nErrSSLCertificateFormat: 'Formato de certificado incorrecto, use formato pem'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid o EabHmacKey no pueden estar en blanco'\nErrOpenrestyNotFound: 'El modo Http requiere tener Openresty instalado primero'\nApplySSLStart: 'Iniciando solicitud de certificado, dominio [{{ .domain }}] método de solicitud [{{ .type }}]'\ndnsAccount: 'DNS Automático'\ndnsManual: 'DNS Manual'\nhttp: 'HTTP'\nApplySSLFailed: 'Solicitud de certificado para [{{ .domain }}] fallida, {{ .detail }}'\nApplySSLSuccess: 'Solicitud de certificado para [{{ .domain }}] exitosa'\nDNSAccountName: 'Cuenta DNS [{{ .name }}] proveedor [{{ .type }}]'\nPushDirLog: 'Certificado empujado al directorio [{{ .path }}] {{ .status }}'\nErrDeleteCAWithSSL: 'La organización actual tiene un certificado emitido y no puede eliminarse.'\nErrDeleteWithPanelSSL: 'La configuración SSL del panel utiliza este certificado y no puede eliminarse'\nErrDefaultCA: 'La autoridad por defecto no puede eliminarse'\nApplyWebSiteSSLLog: 'Iniciando renovación del certificado del sitio web {{ .name }}'\nErrUpdateWebsiteSSL: 'Actualización de certificado del sitio web {{ .name }} falló: {{ .err }}'\nApplyWebSiteSSLSuccess: 'Certificado del sitio web actualizado exitosamente'\nErrExecShell: 'Ejecución del script fallida {{ .err }}'\nExecShellStart: 'Iniciando ejecución de script'\nExecShellSuccess: 'Ejecución de script exitosa'\nStartUpdateSystemSSL: 'Iniciando actualización del certificado del sistema'\nUpdateSystemSSLSuccess: 'Certificado del sistema actualizado correctamente'\nErrWildcardDomain: 'No se puede solicitar certificado de dominio wildcard en modo HTTP'\nErrApplySSLCanNotDelete: 'El certificado {{ .name }} en proceso de solicitud no puede eliminarse, inténtelo más tarde'\nStartPushSSLToNode: 'Iniciando envío de certificado al nodo'\nPushSSLToNodeFailed: 'Error al enviar el certificado al nodo: {{ .err }}'\nPushSSLToNodeSuccess: 'Certificado enviado correctamente al nodo'\nErrUserIsExist: 'El usuario actual ya existe, intente con otro'\nErrDatabaseIsExist: 'La base de datos actual ya existe, intente con otro nombre'\nErrExecTimeOut: 'Tiempo de espera en la ejecución SQL, revise la base de datos'\nErrRemoteExist: 'La base de datos remota ya existe con ese nombre, modifíquelo e intente de nuevo'\nErrLocalExist: 'El nombre ya existe en la base de datos local, modifíquelo e intente de nuevo'\nRemoteBackup: 'Para hacer una copia de seguridad de la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere...'\nRemoteRecover: 'Para restaurar la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere...'\nArg: 'Argumento'\nErrTypeOfRedis: 'El tipo de archivo de recuperación no coincide con el método de persistencia actual, modifíquelo e intente'\nErrInUsed: '{{ .detail }} está en uso y no se puede eliminar'\nErrObjectInUsed: 'Este objeto está en uso y no se puede eliminar'\nErrObjectBeDependent: 'Esta imagen es dependida por otras imágenes y no se puede eliminar'\nErrPortRules: 'El número de puerto no coincide, ingrese de nuevo'\nErrPgImagePull: 'Tiempo de espera al descargar la imagen, configure la aceleración o descárguela manualmente ({{ .name }}) e intente de nuevo'\nPruneHelper: 'Esta limpieza {{ .name }} liberó espacio en disco {{ .size }}'\nImageRemoveHelper: 'Imagen {{ .name }} eliminada, liberando {{ .size }} de espacio en disco'\nBuildCache: 'Caché de compilación'\nVolume: 'Volumen de almacenamiento'\nNetwork: 'Red'\nPruneStart: 'Limpiando en progreso espere...'\nContainerBackupPrepare: 'Preparar espacio de respaldo del contenedor'\nContainerBackupStop: 'Detener contenedor antes del respaldo'\nContainerBackupInspect: 'Respaldar inspect y red del contenedor'\nContainerBackupMounts: 'Respaldar montajes del contenedor'\nContainerBackupMeta: 'Escribir metadatos del respaldo del contenedor'\nContainerBackupStart: 'Iniciar contenedor después del respaldo'\nContainerRecoverPrepare: 'Preparar espacio de restauración del contenedor'\nContainerRecoverExtract: 'Extraer archivo de respaldo del contenedor'\nContainerRecoverParse: 'Cargar metadatos del respaldo del contenedor'\nContainerRecoverCreate: 'Recrear contenedor'\nContainerRecoverMounts: 'Restaurar montajes del contenedor'\nContainerRecoverStart: 'Iniciar contenedor restaurado'\nContainerRecoverCleanup: 'Limpiar espacio de restauracion del contenedor'\nComposeBackupPrepare: 'Preparar espacio de respaldo del compose'\nComposeBackupStop: 'Detener compose antes del respaldo'\nComposeBackupFiles: 'Respaldar archivos de compose'\nComposeBackupContainers: 'Respaldar contenedores del compose'\nComposeBackupMeta: 'Escribir metadatos del respaldo de compose'\nComposeBackupStart: 'Iniciar compose después del respaldo'\nComposeBackupCleanup: 'Limpiar espacio de respaldo del compose'\nComposeRecoverPrepare: 'Preparar espacio de restauración del compose'\nComposeRecoverExtract: 'Extraer archivo de respaldo de compose'\nComposeRecoverMeta: 'Cargar metadatos del respaldo de compose'\nComposeRecoverMetaLogName: 'Metadatos de restauracion de compose: nombre={{ .name }}'\nComposeRecoverMetaLogPath: 'Metadatos de restauracion de compose: rutaRespaldo={{ .backupPath }} rutaDestino={{ .targetDir }}'\nComposeRecoverMetaLogCount: 'Metadatos de restauracion de compose: archivos={{ .files }} contenedores={{ .containers }}'\nComposeRecoverFiles: 'Restaurar archivos de compose'\nComposeRecoverContainers: 'Restaurar contenedores del compose'\nComposeRecoverRecord: 'Guardar registro de compose'\nComposeRecoverCleanup: 'Limpiar espacio de restauracion de compose'\nErrFileNotExist: 'El archivo {{ .detail }} no existe, verifique la integridad'\nErrImageBuildErr: 'Fallo al construir la imagen'\nErrImageExist: 'La imagen ya existe Modifique el nombre de la imagen.'\nErrDelWithWebsite: 'El entorno de ejecución ya está asociado a un sitio web y no puede eliminarse'\nErrRuntimeStart: 'Fallo al iniciar'\nErrPackageJsonNotFound: 'El archivo package.json no existe'\nErrScriptsNotFound: 'No se encontró la configuración de scripts en package.json'\nErrContainerNameNotFound: 'No se puede obtener el nombre del contenedor, revise el archivo.env'\nErrNodeModulesNotFound: 'La carpeta node_modules no existe, edite o espere a que inicie'\nErrContainerNameIsNull: 'El nombre del contenedor no existe'\nErrPHPPortIsDefault: 'El puerto 9000 es el predeterminado, modifique e intente de nuevo'\nErrPHPRuntimePortFailed: 'El puerto {{ .name }} ya está en uso por el entorno actual, modifique e intente'\nErrConfigNotFound: 'El archivo de configuración no existe'\nErrConfigParse: 'El formato del archivo de configuración es incorrecto'\nErrConfigIsNull: 'El archivo de configuración no puede estar vacío'\nErrConfigDirNotFound: 'El directorio de ejecución no existe'\nErrConfigAlreadyExist: 'Ya existe un archivo de configuración con ese nombre'\nErrUserFindErr: 'Búsqueda del usuario {{ .name }} fallida {{ .err }}'\nCutWebsiteLogSuccess: 'Log del sitio web {{ .name }} cortado con éxito, ruta de respaldo {{ .path }}'\nHandleShell: 'Ejecutar script {{ .name }}'\nHandleCurl: 'URL {{ .name }} にアクセス'\nHandleNtpSync: 'Sincronizar hora del sistema'\nHandleSystemClean: 'Limpiar caché del sistema'\nSystemLog: 'Log del sistema'\nCutWebsiteLog: 'Rotar log del sitio web'\nFileOrDir: 'Directorio / Archivo'\nUploadFile: 'Subiendo archivo de respaldo {{ .file }} a {{ .backup }}'\nUpload: 'Subir'\nIgnoreBackupErr: 'Fallo en el respaldo, error: {{ .detail }}, se ignora este error...'\nIgnoreUploadErr: 'Fallo en la subida, error: {{ .detail }}, se ignora este error...'\nLoadBackupFailed: 'Error al obtener la conexión de la cuenta de respaldo, error: {{ .detail }}'\nInExecuting: 'La tarea actual se está ejecutando no la repita'\nNoSuchResource: 'No se encontraron contenidos de respaldo en la base de datos, omitiendo...'\nCleanLog: 'Limpiar registro'\nCleanLogByName: 'Limpiar registro de {{ .name }}'\nWafIpGroupNotFound: 'Grupo de IP de WAF no encontrado., vaya a [Funciones Avanzadas - WAF - Lista Blanca/Negra - Grupo de IP] para agregar un grupo de IP mediante el método de descarga remota.'\nErrNotExistUser: 'El usuario actual no existe, modifíquelo e intente de nuevo'\nErrBanAction: 'Fallo al configurar. El servicio {{ .name }} no está disponible, revise e intente de nuevo'\nErrClamdscanNotFound: 'El comando clamdscan no fue detectado, siga la documentación para instalarlo'\nTaskSwapSet: 'Configurar Swap'\nSetSwap: 'Configurar Swap {{ .path }} - {{ .size }}'\nCreateSwap: 'Crear Archivo Swap'\nFormatSwap: 'Formatear Archivo Swap'\nEnableSwap: 'Habilitar Swap'\nCleanTamperSetting: 'Limpiar configuraciones de protección históricas'\nSetTamperWithRules: 'Iniciar protección de directorio según reglas'\nProtectDir: 'Proteger directorio {{ .name }}'\nIgnoreFile: 'Proteger archivo {{ .name }}'\nTamperSetSuccessful: 'Configuración de protección completada, comenzando monitoreo de los siguientes directorios:'\nErrScope: 'No se admite la modificación de esta configuración'\nErrStateChange: 'Fallo al cambiar de estado'\nErrRuleExist: 'La regla ya existe'\nErrRuleNotExist: 'La regla no existe'\nErrParseIP: 'Formato de IP incorrecto'\nErrDefaultIP: 'default es un nombre reservado, cámbielo'\nErrGroupInUse: 'El grupo IP está en uso por listas negras/blancas y no se puede eliminar'\nErrIPGroupAclUse: 'El grupo IP está en uso por reglas personalizadas del sitio web {{ .name }}, no puede eliminarse'\nErrGroupExist: 'El nombre del grupo IP ya existe'\nErrIPRange: 'Rango de IP incorrecto'\nErrIPExist: 'La IP ya existe'\nurlDefense: 'Reglas de URL'\nurlHelper: 'URL prohibida'\ndirFilter: 'Filtro de directorio'\nxss: 'XSS'\nphpExec: 'Ejecución de scripts PHP'\noneWordTrojan: 'Troyano de una sola palabra'\nappFilter: 'Filtrar directorios peligrosos'\nwebShell: 'Webshell'\nargs: 'Reglas de parámetros'\nprotocolFilter: 'Filtrado de protocolos'\njavaFilter: 'Filtrado de archivos Java peligrosos'\nscannerFilter: 'Filtrado de escáneres'\nescapeFilter: 'Filtro de escape'\ncustomRule: 'Regla personalizada'\nhttpMethod: 'Filtrado de métodos HTTP'\nfileExt: 'Restricciones de carga de archivos'\ndefaultIpBlack: 'Grupo de IPs maliciosas'\ncookie: 'Reglas de Cookie'\nurlBlack: 'Lista negra de URLs'\nuaBlack: 'Lista negra de User-Agent'\nattackCount: 'Límite de frecuencia de ataques'\nfileExtCheck: 'Restricción de carga de archivos'\ngeoRestrict: 'Restricciones de acceso regional'\nunknownWebsite: 'Acceso no autorizado al dominio'\nnotFoundCount: 'Límite de tasa 404'\nheaderDefense: 'Reglas de encabezado'\ndefaultUaBlack: 'Reglas de User-Agent'\nmethodWhite: 'Reglas HTTP'\ncaptcha: 'Verificación humano-máquina'\nfiveSeconds: 'Verificación de 5 segundos'\nvulnCheck: 'Reglas suplementarias'\nacl: 'Reglas personalizadas'\nsql: 'Inyección SQL'\ncc: 'Límite de frecuencia de acceso'\ndefaultUrlBlack: 'Reglas de URL'\nsqlInject: 'Inyección SQL'\nErrDBNotExist: 'La base de datos no existe'\nallow: 'permitir'\ndeny: 'denegar'\nOpenrestyNotFound: 'Openresty no está instalado'\nremoteIpIsNull: 'La lista de IP está vacía'\nOpenrestyVersionErr: 'La versión de Openresty es demasiado baja, actualice a 1.27.1.2-2-2-focal'\nErrFileTooLarge: 'El archivo supera 1 MB y no se puede cargar'\nTaskStart: 'La tarea {{ .name }} inicia [INICIO]'\nTaskEnd: 'La tarea {{ .name }} completada [COMPLETADA]'\nTaskFailed: 'La tarea {{ .name }} falló'\nTaskTimeout: 'La tarea {{ .name }} excedió el tiempo'\nTaskSuccess: 'La tarea {{ .name }} se realizó con éxito'\nTaskRetry: 'Iniciar el {{ .name }} reintento'\nSubTaskSuccess: '{{ .name }} exitoso'\nSubTaskFailed: '{{ .name }} falló: {{ .err }}'\nTaskInstall: 'Instalar'\nTaskUninstall: 'Desinstalar'\nTaskCreate: 'Crear'\nTaskDelete: 'Eliminar'\nTaskUpgrade: 'Actualizar'\nTaskUpdate: 'Actualizar'\nTaskRestart: 'Reiniciar'\nTaskProtect: 'Proteger'\nTaskBackup: 'Respaldar'\nTaskRecover: 'Recuperar'\nTaskRollback: 'Revertir'\nTaskPull: 'Descargar'\nTaskCommit: 'Commit'\nTaskBuild: 'Construir'\nTaskPush: 'Subir'\nTaskClean: 'Limpieza'\nTaskHandle: 'Ejecutar'\nTaskImport: 'Importar'\nTaskExport: 'Exportar'\nWebsite: 'Sitio web'\nApp: 'Aplicación'\nRuntime: 'Entorno de ejecución'\nDatabase: 'Base de datos'\nConfigFTP: 'Crear usuario FTP {{ .name }}'\nConfigOpenresty: 'Crear archivo de configuración Openresty'\nInstallAppSuccess: 'Aplicación {{ .name }} instalada correctamente'\nConfigRuntime: 'Configurar entorno de ejecución'\nConfigApp: 'Configurar aplicación'\nSuccessStatus: '{{ .name }} exitoso'\nFailedStatus: '{{ .name }} falló {{ .err }}'\nHandleLink: 'Gestionar asociación de aplicación'\nHandleDatabaseApp: 'Gestionar parámetros de aplicación'\nExecShell: 'Ejecutar script {{ .name }}'\nPullImage: 'Descargar imagen'\nStart: 'Iniciar'\nRun: 'Ejecutar'\nStop: 'Detener'\nImage: 'Imagen'\nCompose: 'Orquestación'\nContainer: 'Contenedor'\nAppLink: 'Aplicación enlazada'\nEnableSSL: 'Habilitar HTTPS'\nAppStore: 'Tienda de aplicaciones'\nTaskSync: 'Sincronizar'\nLocalApp: 'Aplicación local'\nSubTask: 'Subtarea'\nRuntimeExtension: 'Extensión del entorno de ejecución'\nTaskIsExecuting: 'Tarea en ejecución'\nCustomAppstore: 'Almacén de aplicaciones personalizado'\nTaskExec: 'Ejecutar'\nTaskBatch: 'Operación por Lotes'\nFileConvert: 'Conversión de Formato de Archivo'\nClamscan: 'Escanear {{ .name }}'\nTaskScan: 'Escaneo'\nOllamaModelPull: 'Descargar modelo Ollama {{ .name }}'\nOllamaModelSize: 'Obtener tamaño del modelo Ollama {{ .name }}'\nSnapshot: 'Instantánea'\nSnapDBInfo: 'Escribir información de base de datos de 1Panel'\nSnapCopy: 'Copiar archivos y directorios {{ .name }}'\nSnapNewDB: 'Inicializar conexión a base de datos {{ .name }}'\nSnapDeleteOperationLog: 'Eliminar log de operaciones'\nSnapDeleteLoginLog: 'Eliminar log de accesos'\nSnapDeleteMonitor: 'Eliminar datos de monitoreo'\nSnapRemoveSystemIP: 'Eliminar IP del sistema'\nSnapBaseInfo: 'Escribir información básica de 1Panel'\nSnapInstallAppImageEmpty: 'No se seleccionaron imágenes de aplicaciones, omitiendo...'\nSnapInstallApp: 'Respaldar aplicaciones instaladas de 1Panel'\nSnapDockerSave: 'Comprimir aplicaciones instaladas'\nSnapLocalBackup: 'Respaldar directorio local de 1Panel'\nSnapCompressBackup: 'Comprimir directorio local de respaldo'\nSnapPanelData: 'Respaldar directorio de datos de 1Panel'\nSnapCompressPanel: 'Directorio de datos comprimido'\nSnapWebsite: 'Respaldar directorio de sitios web de 1Panel'\nSnapCloseDBConn: 'Cerrar conexión de base de datos'\nSnapCompress: 'Crear archivos de instantánea'\nSnapCompressFile: 'Comprimir archivo de instantánea'\nSnapCheckCompress: 'Verificar archivo de compresión de instantánea'\nSnapCompressSize: 'Tamaño del archivo de instantánea {{ .name }}'\nSnapUpload: 'Subir archivo de instantánea'\nSnapUploadTo: 'Subir archivo de instantánea a {{ .name }}'\nSnapUploadRes: 'Subir archivo de instantánea a {{ .name }}'\nSnapshotRecover: 'Restaurar instantánea'\nRecoverDownload: 'Descargar archivo de instantánea'\nDownload: 'Descargar'\nRecoverDownloadAccount: 'Obtener cuenta de respaldo para descargar instantánea {{ .name }}'\nRecoverDecompress: 'Descomprimir archivos de instantánea'\nDecompress: 'Descompresión'\nBackupBeforeRecover: 'Respaldar datos del sistema antes de restaurar instantánea'\nReadJson: 'Leer archivo Json de la instantánea'\nReadJsonPath: 'Obtener ruta del archivo Json en la instantánea'\nReadJsonContent: 'Leer archivo Json'\nReadJsonMarshal: 'Procesar escape de Json'\nRecoverApp: 'Restaurar aplicaciones instaladas'\nRecoverWebsite: 'Recuperar directorio del sitio web'\nRecoverAppImage: 'Restaurar imagen de la instantánea'\nRecoverCompose: 'Restaurar otros contenidos de compose'\nRecoverComposeList: 'Obtener todos los compose a restaurar'\nRecoverComposeItem: 'Restaurar compose {{ .name }}'\nRecoverAppEmpty: 'No se encontraron imágenes de respaldo de aplicaciones en la instantánea'\nRecoverBaseData: 'Recuperar datos y archivos básicos'\nRecoverDaemonJsonEmpty: 'El archivo daemon.json no existe ni en la instantánea ni en el equipo actual'\nRecoverDaemonJson: 'Restaurar archivo daemon.json de configuración de contenedor'\nRecoverDBData: 'Restaurar datos de base de datos'\nRecoverBackups: 'Restaurar directorio local de respaldo'\nRecoverPanelData: 'Recuperar directorio de datos'\nContainerImagePull: 'Descargar imagen de contenedor {{ .name }}'\nContainerRemoveOld: 'Eliminar el contenedor original {{ .name }}'\nContainerImageCheck: 'Comprobar si la imagen se descargó correctamente'\nContainerLoadInfo: 'Obtener información básica del contenedor'\nContainerRecreate: 'Actualización del contenedor fallida, restaurando el contenedor original'\nContainerCreate: 'Crear nuevo contenedor {{ .name }}'\nContainerCreateFailed: 'Falló la creación del contenedor, eliminando el contenedor fallido'\nContainerStartCheck: 'Verificar si el contenedor ha iniciado'\nImageBuild: 'Construcción de imagen'\nImagePull: 'Descargar imagen'\nImageRepoAuthFromDB: 'Obtener información de autenticación del repositorio desde la base de datos'\nImagePush: 'Subir imagen'\nImageRenameTag: 'Modificar etiqueta de imagen'\nImageNewTag: 'Nueva etiqueta de imagen {{ .name }}'\nComposeCreate: 'Crear orquestación'\nBackupNginxConfig: 'Respaldar archivo de configuración OpenResty'\nCompressFileSuccess: 'Directorio comprimido con éxito, guardado en {{ .name }}'\nCompressDir: 'Directorio comprimido'\nDeCompressFile: 'Descomprimir archivo {{ .name }}'\nErrCheckValid: 'Verificación de archivo de respaldo fallida, {{ .name }}'\nRollback: 'Revertir'\nwebsiteDir: 'Directorio del sitio web'\nRecoverFailedStartRollBack: 'Restauración fallida, iniciando reversión'\nAppBackupFileIncomplete: 'El archivo de respaldo está incompleto, falta app.json o app.tar.gz'\nAppAttributesNotMatch: 'El tipo o nombre de la aplicación no coincide'\nErrAlertSync: 'Error en la sincronización de la alerta, revise e intente de nuevo'\nErrInstallExtension: 'Ya hay una tarea de instalación en curso, espere a que termine'\nPanelAlertTitle: 'Notificación de alerta del panel'\nTestAlertTitle: 'Correo de prueba - Verificar conectividad de correo'\nTestAlert: 'Este es un correo de prueba para verificar que la configuración de envío de correos es correcta.'\nLicenseExpirationAlert: 'Su Panel {{ .node }}{{ .ip }}, la licencia expirará en {{ .day }} días. Inicie sesión en el panel para ver los detalles.'\nCronJobFailedAlert: 'Su Panel {{ .node }}{{ .ip }}, la ejecución de la tarea programada {{ .name }} falló. Inicie sesión en el panel para ver los detalles.'\nClamAlert: 'Su Panel {{ .node }}{{ .ip }}, la tarea de escaneo antivirus detectó {{ .num }} archivos infectados. Inicie sesión en el panel para ver los detalles.'\nWebSiteAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} sitios web expirarán en {{ .day }} días. Inicie sesión en el panel para ver los detalles.'\nSSLAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} certificados SSL expirarán en {{ .day }} días. Inicie sesión en el panel para ver los detalles.'\nDiskUsedAlert: 'Su Panel {{ .node }}{{ .ip }}, el uso del disco {{ .name }} ha alcanzado {{ .used }}. Inicie sesión en el panel para ver los detalles.'\nResourceAlert: 'Su Panel {{ .node }}{{ .ip }}, la tasa de uso promedio de {{ .name }} en {{ .time }} minutos es {{ .used }}. Inicie sesión en el panel para ver los detalles.'\nPanelVersionAlert: 'Su Panel {{ .node }}{{ .ip }}, está disponible la última versión del panel para actualizar. Inicie sesión en el panel para ver los detalles.'\nPanelPwdExpirationAlert: 'Su Panel {{ .node }}{{ .ip }}, la contraseña del panel expirará en {{ .day }} días. Inicie sesión en el panel para ver los detalles.'\nCommonAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .msg }}. Inicie sesión en el panel para ver los detalles.'\nNodeExceptionAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} nodos son anómalos. Inicie sesión en el panel para ver los detalles.'\nLicenseExceptionAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} licencias son anómalas. Inicie sesión en el panel para ver los detalles.'\nSSHAndPanelLoginAlert: 'Su Panel {{ .node }}{{ .ip }}, el inicio de sesión {{ .name }} desde {{ .loginIp }} es anómalo. Inicie sesión en el panel para ver los detalles.'\nDeviceNotFound: 'Dispositivo {{ .name }} no encontrado'\nDeviceIsMounted: 'El dispositivo {{ .name }} está montado, desmóntelo primero'\nPartitionDiskErr: 'Error al particionar, {{ .err }}'\nFormatDiskErr: 'Error al formatear disco, {{ .err }}'\nMountDiskErr: 'Error al montar disco, {{ .err }}'\nXfsNotFound: 'No se detectó el sistema de archivos xfs instale xfsprogs primero'\n"
  },
  {
    "path": "agent/i18n/lang/ja.yaml",
    "content": "ErrInvalidParams: '不正なリクエスト: {{ .detail }}'\nErrTokenParse: 'トークンエラー: {{ .detail }}'\nErrInitialPassword: '元のパスワードが違います'\nErrInternalServer: '内部エラー: {{ .detail }}'\nErrRecordExist: 'レコードが既に存在します'\nErrRecordNotFound: 'レコードが見つかりません'\nErrStructTransform: '型変換エラー: {{ .err }}'\nErrNotLogin: 'ログインしていません: {{ .detail }}'\nErrPasswordExpired: 'パスワード期限切れ: {{ .detail }}'\nErrNotSupportType: '未対応のタイプ: {{ .name }}'\nErrProxy: 'リクエストに失敗しました: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API利用不可: {{ .detail }}'\nErrApiConfigKeyInvalid: 'APIキーが無効です: {{ .detail }}'\nErrApiConfigIPInvalid: 'IPが許可されていません: {{ .detail }}'\nErrApiConfigDisable: 'APIは無効です: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'タイムスタンプが無効です: {{ .detail }}'\nErrShutDown: 'コマンドは手動で停止されました'\nErrMinQuickJump: 'クイックアクセスを1件以上追加'\nErrMaxQuickJump: '最大4件のクイックアクセス'\nErrUsernameIsExist: 'ユーザー名はすでに使われています'\nErrNameIsExist: '名称はすでに使われています'\nErrDemoEnvironment: 'デモ環境では利用できません'\nErrCmdTimeout: 'コマンドがタイムアウトしました'\nErrCmdIllegal: 'コマンドに無効な文字が含まれています'\nErrPortExist: 'ポート {{ .port }} は {{ .type }} [{{ .name }}] が使用中です'\nTYPE_APP: 'アプリケーション'\nTYPE_RUNTIME: 'ランタイム環境'\nTYPE_DOMAIN: 'ドメイン名'\nErrTypePort: 'ポート {{ .name }} の形式が正しくありません'\nErrTypePortRange: 'ポート範囲は 1 ～ 65535 である必要があります'\nSuccess: '成功'\nFailed: '失敗'\nSystemRestart: 'システムの再起動によりタスクが中断されました'\nErrGroupIsDefault: 'デフォルト グループ、削除できません'\nErrGroupIsInWebsiteUse: 'グループは別の Web サイトで使用されているため、削除できません。'\nDecrypt: '復号化'\nErrAgentAccountBound: 'アカウントはエージェントに紐づいています'\nErrAgentAccountUnavailable: 'アカウント接続不可: {{ .err }}'\nErrAgentProviderNotSupported: 'エージェントプロバイダ非対応'\nErrAgentAccountRequired: 'まずエージェントアカウントを選択'\nErrAgentAccountNotVerified: 'アカウント未検証です'\nErrAgentAccountModelsRequired: '少なくとも 1 つのモデルを設定してください'\nErrAgentAccountSingleInitialModel: 'アカウント作成時に設定できる初期モデルは 1 つだけです'\nErrAgentModelNotInAccount: '選択したモデルはこのアカウントに設定されていません'\nErrAgentModelInUse: 'このモデルはまだエージェントで使用中です: {{ .name }}'\nErrAgentBaseURLRequired: 'ベースURLが必要です'\nErrAgentApiKeyRequired: 'APIキーが必要です'\nErrAgentComposeRequired: 'Composeが必要です'\nErrAgentIDRequired: 'エージェントIDが必要です'\nErrAgentAccountIDRequired: 'アカウントIDが必要です'\nErrAgentLimitReached: 'Community Edition では AI エージェントを最大 {{ .max }} 件まで作成できます。Professional Edition にアップグレードすると数の制限はありません'\nLocalhost: 'ローカルマシン'\nErrBackupInUsed: 'バックアップアカウントがスケジュールで使用中'\nErrBackupCheck: '接続テストに失敗しました: {{ .err }}'\nErrBackupLocalDelete: 'ローカルバックアップは削除できません'\nErrBackupLocalCreate: 'ローカルバックアップは作成できません'\nErrPortInUsed: '{{ .detail }} ポートはすでに使用されています'\nErrAppLimit: 'インストールされているアプリケーションの数が制限を超えました'\nErrNotInstall: 'アプリケーションがインストールされていません'\nErrPortInOtherApp: '{{ .port }} ポートは既にアプリケーション {{ .apps }} によって使用されています'\nErrDbUserNotValid: '既存のデータベース、ユーザー名、およびパスワードが一致しません'\nErrUpdateBuWebsite: 'アプリケーションは正常に更新されましたが、Web サイト構成ファイルの変更に失敗しました。設定を確認してください'\nErr1PanelNetworkFailed: 'デフォルトのコンテナ ネットワークの作成に失敗しました。 {{ .detail }}'\nErrFileParse: 'アプリケーションの docker-compose ファイルの解析に失敗しました'\nErrInstallDirNotFound: 'インストール ディレクトリが存在しません。アンインストールする必要がある場合は、強制アンインストールを選択してください。'\nAppStoreIsUpToDate: 'アプリストアはすでに最新バージョンです'\nLocalAppVersionNull: '{{ .name }} アプリケーションはバージョンに同期されていません。アプリリストに追加できません'\nLocalAppVersionErr: '{{ .name }} バージョン {{ .version }} の同期に失敗しました {{ .err }}'\nErrFileNotFound: '{{ .name }} ファイルが存在しません'\nErrFileParseApp: '{{ .name }} ファイルの解析に失敗しました {{ .err }}'\nErrAppDirNull: 'バージョン フォルダーが存在しません'\nLocalAppErr: 'アプリケーション {{ .name }} の同期に失敗しました {{ .err }}'\nErrContainerName: 'コンテナ名が既に存在します'\nErrCreateHttpClient: 'リクエスト {{ .err }} の作成に失敗しました'\nErrHttpReqTimeOut: 'リクエストがタイムアウトしました {{ .err }}'\nErrHttpReqFailed: 'リクエストが失敗しました {{ .err }}'\nErrNoSuchHost: '要求されたサーバー {{ .err }} が見つかりません'\nErrHttpReqNotFound: '要求されたリソース {{ .err }} が見つかりませんでした'\nErrContainerNotFound: '{{ .name }} コンテナが存在しません'\nErrContainerMsg: '{{ .name }} コンテナが異常です。詳細についてはコンテナページのログを確認してください。'\nErrAppBackup: '{{ .name }} アプリケーションのバックアップに失敗しました。エラー {{ .err }}'\nErrVersionTooLow: '現在の 1Panel のバージョンが低すぎるため、App Store を更新できません。操作する前にバージョンをアップグレードしてください。'\nErrAppNameExist: 'アプリケーション名がすでに存在します'\nAppStoreIsSyncing: 'App Store が同期中です。しばらくしてからもう一度お試しください'\nErrGetCompose: 'docker-compose.yml ファイルの取得に失敗しました {{ .detail }}'\nErrAppWarn: '異常な状態です。ログを確認してください'\nErrAppParamKey: 'パラメータ {{ .name }} フィールドが異常です'\nErrAppUpgrade: 'アプリケーション {{ .name }} のアップグレードに失敗しました {{ .err }}'\nAppRecover: 'アプリケーション {{ .name }} をロールバックします'\nPullImageStart: 'イメージ [{{ .name }}] のプルを開始します'\nPullImageSuccess: 'イメージのプルが成功しました'\nAppStoreSyncSuccess: 'App Store の同期が成功しました'\nSyncAppDetail: 'アプリケーション構成を同期する'\nAppVersionNotMatch: '{{ .name }} アプリケーションには、より高い 1Panel バージョンが必要なため、同期をスキップします'\nMoveSiteDir: 'ウェブサイトディレクトリの変更を検出しました。OpenResty ウェブサイトディレクトリを {{ .name }} に移行する必要があります'\nMoveSiteDirSuccess: 'Web サイト ディレクトリの移行に成功しました'\nDeleteRuntimePHP: 'PHP ランタイムを削除する'\nCustomAppStoreFileValid: 'App Store パッケージは.tar.gz 形式である必要があります'\nPullImageTimeout: 'プル イメージのタイムアウトです。イメージのアクセラレーションを増やすか、別のイメージのアクセラレーションに変更してください'\nErrAppIsDown: '{{ .name }} アプリケーションの状態が異常です。確認してください'\nErrCustomApps: 'インストールされているアプリケーションがあります。まずアンインストールしてください'\nErrCustomRuntimes: 'ランタイム環境がインストールされています。まずそれを削除してください'\nErrAppVersionDeprecated: '{{ .name }} アプリケーションは現在の 1Panel バージョンと互換性がありません、スキップしました'\nErrDockerFailed: 'Docker の状態が異常です。サービス状態を確認してください'\nErrDockerComposeCmdNotFound: 'Docker Compose コマンドは存在しません。ホストマシンにこのコマンドを先にインストールしてください'\nUseExistImage: 'イメージは既に存在します。既存のイメージを使用します'\nErrDatabaseNotFound: 'データベース {{ .name }} は存在しません'\nExportIP: 'ログインIP'\nExportArea: '地域'\nExportPort: 'ポート'\nExportAuthMode: 'ログイン方式'\nExportUser: 'ユーザー'\nExportStatus: 'ログイン状態'\nExportDate: '時間'\nErrFileCanNotRead: 'このファイルはプレビューをサポートしていません'\nErrFileToLarge: 'ファイルは 10M より大きいため開けません'\nErrPathNotFound: 'ディレクトリが存在しません'\nErrMovePathFailed: 'ターゲット パスに元のパスを含めることはできません'\nErrLinkPathNotFound: 'ターゲット パスが存在しません'\nErrFileIsExist: 'ファイルまたはフォルダーは既に存在します'\nErrFileUpload: '{{ .name }} はファイル {{ .detail }} のアップロードに失敗しました'\nErrFileDownloadDir: 'ダウンロード フォルダーはサポートされていません'\nErrCmdNotFound: '{{ .name }} コマンドが存在しません。まずこのコマンドをホストにインストールしてください'\nErrSourcePathNotFound: 'ソース ディレクトリが存在しません'\nErrFavoriteExist: 'このパスはすでにお気に入りに登録されています'\nErrInvalidChar: '不正な文字は許可されません'\nErrPathNotDelete: '選択されたディレクトリは削除できません'\nErrLogFileToLarge: 'ログファイルが500MBを超えており、開くことができません'\nFileDropFailed: 'ファイル {{ .name }} のクリーンアップに失敗しました: {{ .err }}'\nFileDropSuccess: 'ファイル {{ .name }} のクリーンアップに成功しました、{{ .count }} ファイルを削除、{{ .size }} のディスク領域を解放'\nFileDropSum: 'ファイルのクリーンアップが完了しました、合計 {{ .count }} ファイルを削除、合計 {{ .size }} のディスク領域を解放'\nErrBadDecrypt: '圧縮パッケージの解凍パスワードが間違っています。解凍に失敗しました。確認後再試行！'\nErrAliasIsExist: 'エイリアスがすでに存在します'\nErrBackupMatch: 'バックアップ ファイルは、現在の Web サイト データ {{ .detail }} の一部と一致しません'\nErrBackupExist: 'バックアップ ファイル内のソース データの対応する部分が存在しません {{ .detail }}'\nErrPHPResource: 'ローカルオペレーティング環境は切り替えをサポートしていません'\nErrPathPermission: 'インデックス ディレクトリに 1000:1000 以外の権限を持つフォルダーが検出されました。これにより、Web サイトでアクセス拒否エラーが発生する可能性があります。上記の保存ボタンをクリックしてください。'\nErrDomainIsUsed: 'ドメイン名はウェブサイト [{{ .name }}] で既に使用されています'\nErrDomainFormat: '{{ .name }} ドメイン名の形式が正しくありません'\nErrDefaultAlias: 'デフォルトは予約済みのコードです。別のコードを使用してください'\nErrParentWebsite: 'まずサブサイト {{ .name }} を削除する必要があります'\nErrBuildDirNotFound: 'ビルド ディレクトリが存在しません'\nErrImageNotExist: 'オペレーティング環境 {{ .name }} イメージが存在しません。オペレーティング環境を再編集してください'\nErrProxyIsUsed: 'ロードバランシングはリバースプロキシによって使用されているため、削除できません'\nErrSSLValid: '証明書ファイルが異常です、証明書の状態を確認してください！'\nErrWebsiteDir: 'ウェブサイトディレクトリ内のディレクトリを選択してください。'\nErrComposerFileNotFound: 'composer.json ファイルが存在しません'\nErrRuntimeNoPort: 'ランタイム環境にポートが設定されていません。先にランタイム環境を編集してください。'\nStatus: 'ステータス'\nstart: '開始'\nstop: '停止'\nrestart: '再起動'\nkill: '強制停止'\npause: '一時停止'\nunpause: '再開'\nremove: '削除'\ndelete: '削除'\nErrDefaultWebsite: 'デフォルト Web サイトが既に設定されています。設定する前にキャンセルしてください！'\nSSL: '証明書'\nSet: '設定'\nErrSSLCannotDelete: '{{ .name }} 証明書は Web サイトで使用されているため、削除できません'\nErrAccountCannotDelete: 'アカウントは証明書に関連付けられているため、削除できません'\nErrSSLApply: '証明書の更新は成功しましたが、openresty の再ロードに失敗しました。設定を確認してください。'\nErrEmailIsExist: 'メールボックスは既に存在します'\nErrSSLKeyNotFound: '秘密鍵ファイルが存在しません'\nErrSSLCertificateNotFound: '証明書ファイルが存在しません'\nErrSSLKeyFormat: '秘密鍵ファイルの検証に失敗しました'\nErrSSLCertificateFormat: '証明書ファイルの形式が正しくありません。pem 形式を使用してください'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid または EabHmacKey は空白にできません'\nErrOpenrestyNotFound: 'Http モードでは、まず Openresty をインストールする必要があります'\nApplySSLStart: '証明書の申請を開始します。ドメイン名 [{{ .domain }}] 申請方法 [{{ .type }}]'\ndnsAccount: 'DNS 自動'\ndnsManual: 'DNS マニュアル'\nhttp: 'HTTP'\nApplySSLFailed: '[{{ .domain }}] 証明書の申請に失敗しました、{{ .detail }}'\nApplySSLSuccess: '[{{ .domain }}] 証明書の申請に成功しました。'\nDNSAccountName: 'DNS アカウント [{{ .name }}] ベンダー [{{ .type }}]'\nPushDirLog: '証明書がディレクトリ [{{ .path }}] {{ .status }} にプッシュされました'\nErrDeleteCAWithSSL: '現在の組織には発行済みの証明書があり、削除できません。'\nErrDeleteWithPanelSSL: 'パネル SSL 構成はこの証明書を使用しているため、削除できません'\nErrDefaultCA: 'デフォルトの権限を削除できません'\nApplyWebSiteSSLLog: '{{ .name }} ウェブサイト証明書の更新を開始しています'\nErrUpdateWebsiteSSL: '{{ .name }} Web サイト証明書の更新に失敗しました: {{ .err }}'\nApplyWebSiteSSLSuccess: 'Web サイトの証明書を正常に更新しました'\nErrExecShell: 'スクリプト {{ .err }} の実行に失敗しました'\nExecShellStart: 'スクリプトの実行を開始します'\nExecShellSuccess: 'スクリプトの実行が成功しました'\nStartUpdateSystemSSL: 'システム証明書の更新を開始します'\nUpdateSystemSSLSuccess: 'システム証明書を正常に更新しました'\nErrWildcardDomain: 'HTTP モードでワイルドカード ドメイン名証明書を申請できません'\nErrApplySSLCanNotDelete: '申請中の証明書 {{ .name }} は削除できません。しばらくしてからもう一度お試しください。'\nStartPushSSLToNode: '証明書をノードにプッシュ開始'\nPushSSLToNodeFailed: 'ノードに証明書をプッシュ失敗: {{ .err }}'\nPushSSLToNodeSuccess: 'ノードに証明書をプッシュ成功'\nErrUserIsExist: '現在のユーザーは既に存在します。再入力してください'\nErrDatabaseIsExist: '現在のデータベースは既に存在します。再入力してください'\nErrExecTimeOut: 'SQL 実行がタイムアウトしました。データベースを確認してください'\nErrRemoteExist: 'この名前のリモート データベースは既に存在します。変更してもう一度お試しください'\nErrLocalExist: '名前はローカル データベースに既に存在します。変更してもう一度お試しください'\nRemoteBackup: 'リモートデータベースをバックアップするには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください...'\nRemoteRecover: 'リモートデータベースを復元するには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください...'\nArg: '引数'\nErrTypeOfRedis: 'リカバリ ファイルの種類が現在の永続化方法と一致しません。変更して再試行してください'\nErrInUsed: '{{ .detail }} は使用中であり、削除できません！'\nErrObjectInUsed: 'このオブジェクトは使用中であり、削除できません！'\nErrObjectBeDependent: 'このイメージは他のイメージに依存されているため、削除できません！'\nErrPortRules: 'ポート番号が一致しません。再入力してください。'\nErrPgImagePull: 'イメージのプルがタイムアウトしました。イメージのアクセラレーションを設定するか、{{ .name }} イメージを手動でプルして再試行してください'\nPruneHelper: '今回のクリーンアップ {{ .name }} でディスク領域 {{ .size }} を解放しました'\nImageRemoveHelper: 'イメージ{{ .name }}を削除し、{{ .size }}のディスク領域を解放しました'\nBuildCache: 'ビルドキャッシュ'\nVolume: 'ストレージボリューム'\nNetwork: 'ネットワーク'\nPruneStart: 'クリーンアップ中、しばらくお待ちください...'\nContainerBackupPrepare: 'コンテナバックアップ作業領域を準備'\nContainerBackupStop: 'バックアップ前にコンテナを停止'\nContainerBackupInspect: 'コンテナ inspect とネットワーク情報をバックアップ'\nContainerBackupMounts: 'コンテナのマウントデータをバックアップ'\nContainerBackupMeta: 'コンテナバックアップのメタデータを書き込み'\nContainerBackupStart: 'バックアップ後にコンテナを起動'\nContainerRecoverPrepare: 'コンテナ復元作業領域を準備'\nContainerRecoverExtract: 'コンテナバックアップを展開'\nContainerRecoverParse: 'コンテナバックアップメタデータを読み込み'\nContainerRecoverCreate: 'コンテナを再作成'\nContainerRecoverMounts: 'コンテナマウントを復元'\nContainerRecoverStart: '復元済みコンテナを起動'\nContainerRecoverCleanup: 'コンテナ復元作業領域をクリーンアップ'\nComposeBackupPrepare: 'Compose バックアップ作業領域を準備'\nComposeBackupStop: 'バックアップ前に Compose を停止'\nComposeBackupFiles: 'Compose ファイルをバックアップ'\nComposeBackupContainers: 'Compose コンテナをバックアップ'\nComposeBackupMeta: 'Compose バックアップのメタデータを書き込み'\nComposeBackupStart: 'バックアップ後に Compose を起動'\nComposeBackupCleanup: 'Compose バックアップ作業領域をクリーンアップ'\nComposeRecoverPrepare: 'Compose 復元作業領域を準備'\nComposeRecoverExtract: 'Compose バックアップを展開'\nComposeRecoverMeta: 'Compose バックアップメタデータを読み込み'\nComposeRecoverMetaLogName: 'Compose 復元メタ情報: 名前={{ .name }}'\nComposeRecoverMetaLogPath: 'Compose 復元メタ情報: バックアップパス={{ .backupPath }} 復元先={{ .targetDir }}'\nComposeRecoverMetaLogCount: 'Compose 復元メタ情報: ファイル={{ .files }} コンテナ={{ .containers }}'\nComposeRecoverFiles: 'Compose ファイルを復元'\nComposeRecoverContainers: 'Compose コンテナを復元'\nComposeRecoverRecord: 'Compose 記録を保存'\nComposeRecoverCleanup: 'Compose 復元作業領域をクリーンアップ'\nErrFileNotExist: '{{ .detail }} ファイルが存在しません。ソース ファイルの整合性を確認してください。'\nErrImageBuildErr: 'イメージのビルドに失敗しました'\nErrImageExist: 'イメージはすでに存在します！イメージ名を変更してください。'\nErrDelWithWebsite: 'オペレーティング環境は既に Web サイトに関連付けられているため、削除できません'\nErrRuntimeStart: '起動に失敗しました'\nErrPackageJsonNotFound: 'package.json ファイルが存在しません'\nErrScriptsNotFound: 'スクリプト構成項目が package.json に見つかりませんでした'\nErrContainerNameNotFound: 'コンテナ名を取得できません。.env ファイルを確認してください'\nErrNodeModulesNotFound: 'node_modules フォルダが存在しません。ランタイム環境を編集するか、ランタイム環境が正常に起動するまでお待ちください。'\nErrContainerNameIsNull: 'コンテナ名が存在しません'\nErrPHPPortIsDefault: 'ポート9000はデフォルトポートです。修正してから再試行してください'\nErrPHPRuntimePortFailed: 'ポート {{ .name }} は現在のランタイム環境で使用されています。修正してから再試行してください'\nErrConfigNotFound: '構成ファイルが存在しません'\nErrConfigParse: '構成ファイルの形式が正しくありません'\nErrConfigIsNull: '構成ファイルは空にできません'\nErrConfigDirNotFound: '実行ディレクトリが存在しません'\nErrConfigAlreadyExist: '同じ名前の設定ファイルがすでに存在します'\nErrUserFindErr: 'ユーザー {{ .name }} の検索に失敗しました {{ .err }}'\nCutWebsiteLogSuccess: '{{ .name }} ウェブサイトのログが正常にカットされました。バックアップ パス {{ .path }}'\nHandleShell: 'スクリプト {{ .name }} を実行します'\nHandleCurl: 'URL {{ .name }} にアクセス'\nHandleNtpSync: 'システム時刻の同期'\nHandleSystemClean: 'システム キャッシュのクリーンアップ'\nSystemLog: 'システムログ'\nCutWebsiteLog: 'ウェブサイトログのローテーション'\nFileOrDir: 'ディレクトリ / ファイル'\nUploadFile: 'バックアップファイル {{ .file }} を {{ .backup }} にアップロード中'\nUpload: 'アップロード'\nIgnoreBackupErr: 'バックアップ失敗、エラー: {{ .detail }}、このエラーを無視します...'\nIgnoreUploadErr: 'アップロード失敗、エラー: {{ .detail }}、このエラーを無視します...'\nLoadBackupFailed: 'バックアップアカウントの接続取得に失敗、エラー: {{ .detail }}'\nInExecuting: '現在のタスクは実行中です。重複して実行しないでください！'\nNoSuchResource: 'データベースにバックアップ内容が見つかりませんでした。スキップします...'\nCleanLog: 'ログをクリーン'\nCleanLogByName: '{{ .name }} のログをクリーン'\nWafIpGroupNotFound: 'WAF IPグループが見つかりません。[高度な機能 - WAF - ブラックリスト/ホワイトリスト - IPグループ]に移動し、リモートダウンロード方式でIPグループを追加してください。'\nErrNotExistUser: '現在のユーザーは存在しません。変更してもう一度お試しください。'\nErrBanAction: '設定に失敗しました。現在の {{ .name }} サービスは利用できません。確認してもう一度お試しください。'\nErrClamdscanNotFound: 'clamdscan コマンドが検出されませんでした。インストールするにはドキュメントを参照してください。'\nTaskSwapSet: 'Swap設定'\nSetSwap: 'Swap設定 {{ .path }} - {{ .size }}'\nCreateSwap: 'Swapファイル作成'\nFormatSwap: 'Swapファイルフォーマット'\nEnableSwap: 'Swapファイル有効化'\nCleanTamperSetting: '履歴保護設定をクリーンアップ'\nSetTamperWithRules: 'ルールに従ってディレクトリ保護を開始'\nProtectDir: 'ディレクトリを保護 {{ .name }}'\nIgnoreFile: 'ファイルを保護 {{ .name }}'\nTamperSetSuccessful: '保護設定が完了し、以下のディレクトリの監視を開始します：'\nErrScope: 'この構成の変更はサポートされていません'\nErrStateChange: '状態の変更に失敗しました'\nErrRuleExist: 'ルールは既に存在します'\nErrRuleNotExist: 'ルールが存在しません'\nErrParseIP: 'IP 形式が間違っています'\nErrDefaultIP: 'デフォルトは予約名です。別の名前に変更してください'\nErrGroupInUse: 'IP グループはブラックリスト/ホワイトリストで使用されており、削除できません'\nErrIPGroupAclUse: 'IPグループはウェブサイト {{ .name }} のカスタムルールで使用されているため、削除できません'\nErrGroupExist: 'IP グループ名がすでに存在します'\nErrIPRange: 'IP 範囲が間違っています'\nErrIPExist: 'IP がすでに存在します'\nurlDefense: 'URL ルール'\nurlHelper: '禁止された URL'\ndirFilter: 'ディレクトリ フィルター'\nxss: 'XSS'\nphpExec: 'PHP スクリプトの実行'\noneWordTrojan: 'ワンワードトロイの木馬'\nappFilter: '危険なディレクトリフィルタリングを適用する'\nwebShell: 'ウェブシェル'\nargs: 'パラメータルール'\nprotocolFilter: 'プロトコルフィルタリング'\njavaFilter: 'Java 危険ファイル フィルター'\nscannerFilter: 'スキャナ フィルタ'\nescapeFilter: 'エスケープフィルター'\ncustomRule: 'カスタムルール'\nhttpMethod: 'HTTP メソッド フィルタリング'\nfileExt: 'ファイルアップロード制限'\ndefaultIpBlack: '悪意のある IP グループ'\ncookie: 'クッキールール'\nurlBlack: 'URL ブラックリスト'\nuaBlack: 'ユーザーエージェント ブラックリスト'\nattackCount: '攻撃頻度の制限'\nfileExtCheck: 'ファイルアップロード制限'\ngeoRestrict: '地域アクセス制限'\nunknownWebsite: '不正なドメイン名アクセス'\nnotFoundCount: '404 レート制限'\nheaderDefense: 'ヘッダールール'\ndefaultUaBlack: 'ユーザーエージェントルール'\nmethodWhite: 'HTTP ルール'\ncaptcha: '人間と機械による認証'\nfiveSeconds: '5秒検証'\nvulnCheck: '補足ルール'\nacl: 'カスタムルール'\nsql: 'SQL インジェクション'\ncc: 'アクセス頻度制限'\ndefaultUrlBlack: 'URL ルール'\nsqlInject: 'SQL インジェクション'\nErrDBNotExist: 'データベースが存在しません'\nallow: '許可'\ndeny: '拒否'\nOpenrestyNotFound: 'Openresty がインストールされていません'\nremoteIpIsNull: 'IPリストが空です'\nOpenrestyVersionErr: 'Openrestyのバージョンが低すぎます。Openrestyを1.27.1.2-2-2-focalにアップグレードしてください'\nErrFileTooLarge: 'ファイルが 1MB を超えているためアップロードできません'\nTaskStart: '{{ .name }} タスクが開始されました [START]'\nTaskEnd: '{{ .name }} タスクが完了しました [COMPLETED]'\nTaskFailed: '{{ .name }} タスクが失敗しました'\nTaskTimeout: '{{ .name }} がタイムアウトしました'\nTaskSuccess: '{{ .name }} タスクが成功しました'\nTaskRetry: '{{ .name }} 回目の再試行を開始します'\nSubTaskSuccess: '{{ .name }} が成功しました'\nSubTaskFailed: '{{ .name }} が失敗しました: {{ .err }}'\nTaskInstall: 'インストール'\nTaskUninstall: 'アンインストール'\nTaskCreate: '作成'\nTaskDelete: '削除'\nTaskUpgrade: 'アップグレード'\nTaskUpdate: '更新'\nTaskRestart: '再起動'\nTaskProtect: '保護'\nTaskBackup: 'バックアップ'\nTaskRecover: '回復'\nTaskRollback: 'ロールバック'\nTaskPull: 'プル'\nTaskCommit: 'コミット'\nTaskBuild: 'ビルド'\nTaskPush: 'プッシュ'\nTaskClean: 'クリーンアップ'\nTaskHandle: '実行'\nTaskImport: 'インポート'\nTaskExport: 'エクスポート'\nWebsite: 'ウェブサイト'\nApp: 'アプリケーション'\nRuntime: 'ランタイム環境'\nDatabase: 'データベース'\nConfigFTP: 'FTP ユーザー {{ .name }} を作成します'\nConfigOpenresty: 'Openresty 構成ファイルを作成する'\nInstallAppSuccess: 'アプリケーション {{ .name }} が正常にインストールされました'\nConfigRuntime: 'ランタイム環境を構成する'\nConfigApp: '構成アプリケーション'\nSuccessStatus: '{{ .name }} は成功しました'\nFailedStatus: '{{ .name }} が {{ .err }} に失敗しました'\nHandleLink: 'ハンドルアプリケーションの関連付け'\nHandleDatabaseApp: 'アプリケーション パラメータの処理'\nExecShell: '{{ .name }} スクリプトを実行する'\nPullImage: 'イメージをプル'\nStart: '開始'\nRun: '開始'\nStop: '停止'\nImage: '鏡'\nCompose: 'オーケストレーション'\nContainer: 'コンテナ'\nAppLink: 'リンクされたアプリケーション'\nEnableSSL: 'HTTPS を有効にする'\nAppStore: 'Appストア'\nTaskSync: '同期'\nLocalApp: 'ローカルアプリケーション'\nSubTask: 'サブタスク'\nRuntimeExtension: 'ランタイム環境拡張'\nTaskIsExecuting: 'タスクは実行中です'\nCustomAppstore: 'カスタム アプリケーション ウェアハウス'\nTaskExec: '実行'\nTaskBatch: '一括操作'\nFileConvert: 'ファイル形式の変換'\nClamscan: '{{ .name }} をスキャン'\nTaskScan: 'スキャン'\nOllamaModelPull: 'Ollama モデル {{ .name }} をプルします'\nOllamaModelSize: 'Ollama モデル {{ .name }} のサイズを取得します'\nSnapshot: 'スナップショット'\nSnapDBInfo: '1Panel データベース情報を書き込む'\nSnapCopy: 'ファイルとディレクトリ {{ .name }} をコピーします'\nSnapNewDB: 'データベース {{ .name }} 接続を初期化します'\nSnapDeleteOperationLog: '操作ログの削除'\nSnapDeleteLoginLog: 'アクセスログを削除する'\nSnapDeleteMonitor: '監視データを削除する'\nSnapRemoveSystemIP: 'システム IP を削除します'\nSnapBaseInfo: '1Panel の基本情報を書き込む'\nSnapInstallAppImageEmpty: 'アプリケーションイメージが選択されていないため、スキップします...'\nSnapInstallApp: '1Panelインストール済みアプリケーションのバックアップ'\nSnapDockerSave: 'インストールされたアプリケーションを圧縮する'\nSnapLocalBackup: '1Panel ローカル バックアップ ディレクトリをバックアップします'\nSnapCompressBackup: 'ローカル バックアップ ディレクトリを圧縮する'\nSnapPanelData: '1Panel データ ディレクトリをバックアップ'\nSnapCompressPanel: '圧縮データ ディレクトリ'\nSnapWebsite: '1Panel ウェブサイト ディレクトリのバックアップ'\nSnapCloseDBConn: 'データベース接続を閉じる'\nSnapCompress: 'スナップショット ファイルを作成する'\nSnapCompressFile: 'スナップショット ファイルを圧縮する'\nSnapCheckCompress: 'スナップショット圧縮ファイルをチェックする'\nSnapCompressSize: 'スナップショット ファイル サイズ {{ .name }}'\nSnapUpload: 'スナップショット ファイルをアップロード'\nSnapUploadTo: 'スナップショット ファイルを {{ .name }} にアップロードします'\nSnapUploadRes: 'スナップショット ファイルを {{ .name }} にアップロードします'\nSnapshotRecover: 'スナップショットの復元'\nRecoverDownload: 'スナップショット ファイルをダウンロード'\nDownload: 'ダウンロード'\nRecoverDownloadAccount: 'スナップショット ダウンロード バックアップ アカウント {{ .name }} を取得します'\nRecoverDecompress: 'スナップショット圧縮ファイルを解凍する'\nDecompress: '減圧'\nBackupBeforeRecover: 'スナップショットの前にシステム関連データをバックアップする'\nReadJson: 'スナップショット内の Json ファイルを読み取ります'\nReadJsonPath: 'スナップショット内の Json ファイル パスを取得します'\nReadJsonContent: 'Json ファイルを読み取ります'\nReadJsonMarshal: 'Json エスケープ処理'\nRecoverApp: 'インストールされたアプリを復元する'\nRecoverWebsite: 'ウェブサイトディレクトリを回復する'\nRecoverAppImage: 'スナップショットイメージのバックアップを復元する'\nRecoverCompose: '他のコンポーザーのコンテンツを復元する'\nRecoverComposeList: 'すべてのコンポーザーを復元する'\nRecoverComposeItem: '{{ .name }} の作成を回復する'\nRecoverAppEmpty: 'スナップショット ファイルにアプリケーション イメージのバックアップが見つかりませんでした'\nRecoverBaseData: '基本データとファイルを回復する'\nRecoverDaemonJsonEmpty: 'スナップショット ファイルと現在のマシンの両方に、コンテナー構成 daemon.json ファイルがありません'\nRecoverDaemonJson: 'コンテナ構成 daemon.json ファイルを復元する'\nRecoverDBData: 'データベース データを回復する'\nRecoverBackups: 'ローカル バックアップ ディレクトリを復元する'\nRecoverPanelData: 'リカバリデータディレクトリ'\nContainerImagePull: 'コンテナ イメージ {{ .name }} をプルします'\nContainerRemoveOld: '元のコンテナ {{ .name }} を削除します'\nContainerImageCheck: 'イメージが正常にプルされているかどうかを確認します'\nContainerLoadInfo: 'コンテナの基本情報を取得する'\nContainerRecreate: 'コンテナの更新に失敗しました。元のコンテナの復元を開始しています'\nContainerCreate: '新しいコンテナ {{ .name }} を作成します'\nContainerCreateFailed: 'コンテナの作成に失敗しました。失敗したコンテナを削除してください'\nContainerStartCheck: 'コンテナが起動されているかどうかを確認します'\nImageBuild: 'イメージビルド'\nImagePull: '画像をプル'\nImageRepoAuthFromDB: 'データベースからリポジトリ認証情報を取得する'\nImagePush: '画像をプッシュ'\nImageRenameTag: '画像タグを変更する'\nImageNewTag: '新しい画像タグ {{ .name }}'\nComposeCreate: 'コンポジションを作成する'\nBackupNginxConfig: 'ウェブサイトの OpenResty 構成ファイルをバックアップする'\nCompressFileSuccess: 'ディレクトリを正常に圧縮しました。{{ .name }} に圧縮されました'\nCompressDir: '圧縮ディレクトリ'\nDeCompressFile: 'ファイル {{ .name }} を解凍します'\nErrCheckValid: 'バックアップ ファイルの検証に失敗しました、{{ .name }}'\nRollback: 'ロールバック'\nwebsiteDir: 'ウェブサイトディレクトリ'\nRecoverFailedStartRollBack: 'リカバリに失敗しました。ロールバックを開始します'\nAppBackupFileIncomplete: 'バックアップ ファイルが不完全で、app.json または app.tar.gz ファイルが不足しています'\nAppAttributesNotMatch: 'アプリケーションの種類または名前が一致しません'\nErrAlertSync: 'アラーム情報の同期エラーです。確認してもう一度お試しください。'\nErrInstallExtension: 'インストールタスクが進行中です、タスクが終了するのを待ってください'\nPanelAlertTitle: 'パネルアラート通知'\nTestAlertTitle: 'テストメール - メール接続の確認'\nTestAlert: 'これはテストメールです。メール送信設定が正しく構成されているかを確認します。'\nLicenseExpirationAlert: 'あなたの {{ .node }}{{ .ip }} パネル、ライセンスは {{ .day }} 日後に期限切れになります。詳細はパネルにログインして'\nCronJobFailedAlert: 'あなたの {{ .node }}{{ .ip }} パネル、スケジュールタスク {{ .name }} の実行に失敗しました。詳細はパネルにログインして'\nClamAlert: 'あなたの {{ .node }}{{ .ip }} パネル、ウイルススキャンタスクで {{ .num }} 個の感染ファイルを検出しました。詳細はパネルにログインして'\nWebSiteAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のウェブサイトが {{ .day }} 日後に期限切れになります。詳細はパネルにログインして'\nSSLAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 枚のSSL証明書が {{ .day }} 日後に期限切れになります。詳細はパネルにログインして'\nDiskUsedAlert: 'あなたの {{ .node }}{{ .ip }} パネル、ディスク {{ .name }} の使用率が {{ .used }} に達しました。詳細はパネルにログインして'\nResourceAlert: 'あなたの {{ .node }}{{ .ip }} パネル、過去 {{ .time }} 分間の {{ .name }} 平均使用率は {{ .used }} です。詳細はパネルにログインして'\nPanelVersionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、最新バージョンがアップグレード可能です。詳細はパネルにログインして'\nPanelPwdExpirationAlert: 'あなたの {{ .node }}{{ .ip }} パネル、パネルパスワードは {{ .day }} 日後に期限切れになります。詳細はパネルにログインして'\nCommonAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .msg }}。詳細はパネルにログインして'\nNodeExceptionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のノードに異常が発生しています。詳細はパネルにログインして'\nLicenseExceptionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のライセンスに異常が発生しています。詳細はパネルにログインして'\nSSHAndPanelLoginAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .loginIp }} からの {{ .name }} ログインに異常があります。詳細はパネルにログインして'\nDeviceNotFound: 'デバイス {{ .name }} が見つかりません'\nDeviceIsMounted: 'デバイス {{ .name }} はマウントされています、まずアンマウントしてください'\nPartitionDiskErr: 'パーティションに失敗しました、{{ .err }}'\nFormatDiskErr: 'ディスクのフォーマットに失敗しました、{{ .err }}'\nMountDiskErr: 'ディスクのマウントに失敗しました、{{ .err }}'\nXfsNotFound: 'xfs ファイルシステムが検出されませんでした、最初に xfsprogs をインストールしてください'\n"
  },
  {
    "path": "agent/i18n/lang/ko.yaml",
    "content": "ErrInvalidParams: '잘못된 요청: {{ .detail }}'\nErrTokenParse: '토큰 오류: {{ .detail }}'\nErrInitialPassword: '원래 비밀번호가 틀립니다'\nErrInternalServer: '서버 오류: {{ .detail }}'\nErrRecordExist: '레코드가 이미 있습니다'\nErrRecordNotFound: '레코드를 찾을 수 없습니다'\nErrStructTransform: '형 변환 실패: {{ .err }}'\nErrNotLogin: '로그인하지 않음: {{ .detail }}'\nErrPasswordExpired: '비밀번호 만료: {{ .detail }}'\nErrNotSupportType: '지원하지 않는 타입: {{ .name }}'\nErrProxy: '요청 실패: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API 사용 중지: {{ .detail }}'\nErrApiConfigKeyInvalid: 'API 키가 잘못됨: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP 허용되지 않음: {{ .detail }}'\nErrApiConfigDisable: 'API 접근 금지: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: '타임스탬프 오류: {{ .detail }}'\nErrShutDown: '명령이 수동으로 중단되었습니다'\nErrMinQuickJump: '퀵 액세스를 하나 이상 추가하세요'\nErrMaxQuickJump: '퀵 액세스는 최대 4개'\nErrUsernameIsExist: '아이디가 이미 존재합니다'\nErrNameIsExist: '이름이 이미 존재합니다'\nErrDemoEnvironment: '데모 환경에서는 불가'\nErrCmdTimeout: '명령이 시간 초과했습니다'\nErrCmdIllegal: '명령어에 허용되지 않은 문자가 포함됨'\nErrPortExist: '포트 {{ .port }}는 {{ .type }} [{{ .name }}]에서 사용 중입니다'\nTYPE_APP: '응용 프로그램'\nTYPE_RUNTIME: '런타임 환경'\nTYPE_DOMAIN: '도메인 이름'\nErrTypePort: '포트 {{ .name }} 형식이 올바르지 않습니다'\nErrTypePortRange: '포트 범위는 1-65535 사이여야 합니다.'\nSuccess: '성공'\nFailed: '실패했습니다'\nSystemRestart: '시스템 재시작으로 인해 작업이 중단되었습니다'\nErrGroupIsDefault: '기본 그룹, 삭제할 수 없습니다'\nErrGroupIsInWebsiteUse: '그룹이 다른 웹사이트에서 사용 중이므로 삭제할 수 없습니다.'\nDecrypt: '복호화'\nErrAgentAccountBound: '계정이 에이전트에 묶여 있습니다'\nErrAgentAccountUnavailable: '계정 연결 불가: {{ .err }}'\nErrAgentProviderNotSupported: '지원되지 않는 에이전트 공급자'\nErrAgentAccountRequired: '먼저 에이전트 계정을 선택하세요'\nErrAgentAccountNotVerified: '에이전트 계정 미확인'\nErrAgentAccountModelsRequired: '최소 한 개의 모델을 구성해 주세요'\nErrAgentAccountSingleInitialModel: '계정 생성 시 초기 모델은 하나만 구성할 수 있습니다'\nErrAgentModelNotInAccount: '선택한 모델이 현재 계정에 구성되어 있지 않습니다'\nErrAgentModelInUse: '이 모델을 아직 사용 중인 에이전트가 있습니다: {{ .name }}'\nErrAgentBaseURLRequired: '기본 URL 필요'\nErrAgentApiKeyRequired: 'API 키 필요'\nErrAgentComposeRequired: 'Compose 내용 필요'\nErrAgentIDRequired: '에이전트 ID 필요'\nErrAgentAccountIDRequired: '계정 ID 필요'\nErrAgentLimitReached: '커뮤니티 에디션에서는 AI 에이전트를 최대 {{ .max }}개까지 생성할 수 있으며, Professional 에디션으로 업그레이드하면 수량 제한이 없습니다'\nLocalhost: '로컬 머신'\nErrBackupInUsed: '백업 계정이 예약에 사용 중'\nErrBackupCheck: '연결 테스트 실패: {{ .err }}'\nErrBackupLocalDelete: '로컬 백업은 삭제할 수 없습니다'\nErrBackupLocalCreate: '로컬 백업은 만들 수 없습니다'\nErrPortInUsed: '{{ .detail }} 포트가 이미 사용 중입니다'\nErrAppLimit: '설치된 애플리케이션 수가 한도를 초과했습니다'\nErrNotInstall: '응용 프로그램이 설치되지 않았습니다'\nErrPortInOtherApp: '{{ .port }} 포트는 이미 {{ .apps }} 애플리케이션에 의해 사용되고 있습니다'\nErrDbUserNotValid: '기존 데이터베이스, 사용자 이름 및 비밀번호가 일치하지 않습니다'\nErrUpdateBuWebsite: '응용 프로그램이 성공적으로 업데이트되었지만, 웹사이트 구성 파일 수정에 실패했습니다. 구성을 확인하세요'\nErr1PanelNetworkFailed: '기본 컨테이너 네트워크 생성에 실패했습니다 {{ .세부 사항 }}'\nErrFileParse: '응용 프로그램 docker-compose 파일 구문 분석에 실패했습니다'\nErrInstallDirNotFound: '설치 디렉토리가 존재하지 않습니다. 제거를 원하실 경우 강제 제거 를 선택해주세요.'\nAppStoreIsUpToDate: '앱 스토어가 이미 최신 버전입니다'\nLocalAppVersionNull: '{{ .name }} 애플리케이션이 버전과 동기화되지 않았습니다 앱 목록에 추가할 수 없습니다.'\nLocalAppVersionErr: '{{ .name }} 동기화 버전 {{ .version }}이 실패했습니다 {{ .err }}'\nErrFileNotFound: '{{ .name }} 파일이 존재하지 않습니다'\nErrFileParseApp: '{{ .name }} 파일 구문 분석에 실패했습니다 {{ .err }}'\nErrAppDirNull: '버전 폴더가 존재하지 않습니다'\nLocalAppErr: '애플리케이션 {{ .name }} 동기화에 실패했습니다 {{ .err }}'\nErrContainerName: '컨테이너 이름이 이미 존재합니다'\nErrCreateHttpClient: '요청 {{ .err }}을(를) 생성하지 못했습니다.'\nErrHttpReqTimeOut: '요청 시간이 초과되었습니다 {{ .err }}'\nErrHttpReqFailed: '요청이 실패했습니다 {{ .err }}'\nErrNoSuchHost: '요청한 서버 {{ .err }}을 찾을 수 없습니다'\nErrHttpReqNotFound: '요청한 리소스 {{ .err }}을 찾을 수 없습니다.'\nErrContainerNotFound: '{{ .name }} 컨테이너가 존재하지 않습니다'\nErrContainerMsg: '{{ .name }} 컨테이너가 비정상입니다. 자세한 내용은 컨테이너 페이지의 로그를 확인하세요.'\nErrAppBackup: '{{ .name }} 애플리케이션 백업에 실패했습니다 {{ .err }}'\nErrVersionTooLow: '현재 1Panel 버전이 너무 낮아 App Store를 업데이트할 수 없습니다. 작동하시기 전에 버전을 업그레이드하세요.'\nErrAppNameExist: '응용 프로그램 이름이 이미 존재합니다'\nAppStoreIsSyncing: 'App Store가 동기화 중입니다. 나중에 다시 시도하세요.'\nErrGetCompose: 'docker-compose.yml 파일을 가져오지 못했습니다 {{ .detail }}'\nErrAppWarn: '비정상적인 상태입니다. 로그를 확인해 주세요.'\nErrAppParamKey: '매개변수 {{ .name }} 필드가 비정상입니다.'\nErrAppUpgrade: '애플리케이션 {{ .name }} 업그레이드에 실패했습니다 {{ .err }}'\nAppRecover: '롤백 애플리케이션 {{ .name }}'\nPullImageStart: '이미지 [{{ .name }}] 가져오기 시작'\nPullImageSuccess: '이미지 가져오기 성공'\nAppStoreSyncSuccess: '앱스토어 동기화 성공'\nSyncAppDetail: '애플리케이션 구성 동기화'\nAppVersionNotMatch: '{{ .name }} 애플리케이션에 더 높은 1Panel 버전이 필요하므로 동기화를 건너뜁니다.'\nMoveSiteDir: '웹사이트 디렉토리 변경이 감지되었습니다. OpenResty 웹사이트 디렉토리를 {{ .name }} 로 마이그레이션해야 합니다'\nMoveSiteDirSuccess: '웹사이트 디렉토리 마이그레이션 성공'\nDeleteRuntimePHP: 'PHP 런타임 삭제'\nCustomAppStoreFileValid: '앱 스토어 패키지는.tar.gz 형식이어야 합니다.'\nPullImageTimeout: '이미지 가져오기 시간 초과, 이미지 가속을 높이거나 다른 이미지 가속으로 변경해 보세요.'\nErrAppIsDown: '{{ .name }} 애플리케이션 상태가 비정상적입니다. 확인해 주세요'\nErrCustomApps: '설치된 애플리케이션이 있습니다. 먼저 제거해 주세요'\nErrCustomRuntimes: '설치된 런타임 환경이 있습니다. 먼저 삭제해 주세요'\nErrAppVersionDeprecated: '{{ .name }} 응용 프로그램은 현재 1Panel 버전과 호환되지 않아 건너뛰었습니다'\nErrDockerFailed: 'Docker의 상태가 비정상입니다. 서비스 상태를 확인하세요'\nErrDockerComposeCmdNotFound: 'Docker Compose 명령이 없습니다. 호스트 머신에 먼저 이 명령을 설치하세요'\nUseExistImage: '이미지가 이미 존재하여 기존 이미지를 사용합니다'\nErrDatabaseNotFound: '데이터베이스 {{ .name }} 이(가) 존재하지 않습니다'\nExportIP: '로그인 IP'\nExportArea: '위치'\nExportPort: '포트'\nExportAuthMode: '로그인 방식'\nExportUser: '사용자'\nExportStatus: '로그인 상태'\nExportDate: '시간'\nErrFileCanNotRead: '이 파일은 미리보기를 지원하지 않습니다'\nErrFileToLarge: '파일이 10M보다 커서 열 수 없습니다'\nErrPathNotFound: '디렉토리가 존재하지 않습니다'\nErrMovePathFailed: '대상 경로에는 원래 경로가 포함될 수 없습니다'\nErrLinkPathNotFound: '대상 경로가 존재하지 않습니다'\nErrFileIsExist: '파일이나 폴더가 이미 존재합니다'\nErrFileUpload: '{{ .name }}이 파일 {{ .detail }}을 업로드하지 못했습니다.'\nErrFileDownloadDir: '다운로드 폴더가 지원되지 않습니다'\nErrCmdNotFound: '{{ .name }} 명령이 존재하지 않습니다. 먼저 호스트에 이 명령을 설치하세요'\nErrSourcePathNotFound: '소스 디렉토리가 존재하지 않습니다'\nErrFavoriteExist: '이 경로는 이미 즐겨찾기되었습니다'\nErrInvalidChar: '불법 문자는 허용되지 않습니다'\nErrPathNotDelete: '선택한 디렉토리를 삭제할 수 없습니다'\nErrLogFileToLarge: '로그 파일이 500MB를 초과하여 열 수 없습니다'\nFileDropFailed: '파일 {{ .name }} 정리 실패: {{ .err }}'\nFileDropSuccess: '파일 {{ .name }} 정리 성공, {{ .count }}개 파일 삭제, {{ .size }} 디스크 공간 확보'\nFileDropSum: '파일 정리 완료, 총 {{ .count }}개 파일 삭제, 총 {{ .size }} 디스크 공간 확보'\nErrBadDecrypt: '압축 패키지 암호 해제 비밀번호 오류, 압축 해제 실패, 확인 후 다시 시도하세요'\nErrAliasIsExist: '별칭이 이미 존재합니다'\nErrBackupMatch: '백업 파일이 현재 웹사이트 데이터 중 일부 {{ .detail }}와 일치하지 않습니다.'\nErrBackupExist: '백업 파일에 있는 소스 데이터의 해당 부분이 존재하지 않습니다 {{ .detail }}'\nErrPHPResource: '로컬 운영 환경이 전환을 지원하지 않습니다'\nErrPathPermission: '인덱스 디렉토리에서 1000:1000이 아닌 권한을 가진 폴더가 감지되었습니다. 이로 인해 웹사이트에서 액세스 거부 오류가 발생할 수 있습니다. 위의 저장 버튼을 클릭해주세요.'\nErrDomainIsUsed: '[{{ .name }}] 웹사이트에서 이미 도메인 이름을 사용하고 있습니다.'\nErrDomainFormat: '{{ .name }} 도메인 이름 형식이 올바르지 않습니다'\nErrDefaultAlias: '기본값은 예약된 코드입니다. 다른 코드를 사용하세요'\nErrParentWebsite: '먼저 하위 사이트 {{ .name }}을 삭제해야 합니다.'\nErrBuildDirNotFound: '빌드 디렉토리가 존재하지 않습니다'\nErrImageNotExist: '운영 환경 {{ .name }} 이미지가 존재하지 않습니다. 운영 환경을 다시 편집하세요.'\nErrProxyIsUsed: '로드 밸런싱이 역방향 프록시에 의해 사용되었으므로 삭제할 수 없습니다'\nErrSSLValid: '인증서 파일에 문제가 있습니다. 인증서 상태를 확인하세요'\nErrWebsiteDir: '웹사이트 디렉토리 내의 디렉토리를 선택하세요.'\nErrComposerFileNotFound: 'composer.json 파일이 존재하지 않습니다'\nErrRuntimeNoPort: '런타임 환경에 포트가 설정되지 않았습니다. 먼저 런타임 환경을 편집하세요.'\nStatus: '상태'\nstart: '시작'\nstop: '중지'\nrestart: '재시작'\nkill: '강제 중지'\npause: '일시 정지'\nunpause: '재개'\nremove: '삭제'\ndelete: '삭제'\nErrDefaultWebsite: '기본 웹사이트가 이미 설정되었습니다. 설정하기 전에 취소하세요'\nSSL: '인증서'\nSet: '설정'\nErrSSLCannotDelete: '{{ .name }} 인증서는 웹사이트에서 사용 중이므로 삭제할 수 없습니다.'\nErrAccountCannotDelete: '계정이 인증서와 연결되어 있어 삭제할 수 없습니다.'\nErrSSLApply: '인증서 갱신이 성공했으나, openresty 재로드에 실패했습니다. 구성을 확인해 주세요'\nErrEmailIsExist: '사서함이 이미 존재합니다'\nErrSSLKeyNotFound: '개인 키 파일이 존재하지 않습니다'\nErrSSLCertificateNotFound: '인증서 파일이 존재하지 않습니다'\nErrSSLKeyFormat: '개인 키 파일 검증에 실패했습니다'\nErrSSLCertificateFormat: '인증서 파일 형식이 올바르지 않습니다. pem 형식을 사용하세요.'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 또는 EabHmacKey는 비워둘 수 없습니다'\nErrOpenrestyNotFound: 'Http 모드를 사용하려면 먼저 Openresty를 설치해야 합니다.'\nApplySSLStart: '인증서 신청 시작, 도메인 이름 [{{ .domain }}] 신청 방법 [{{ .type }}]'\ndnsAccount: 'DNS 자동'\ndnsManual: 'DNS 매뉴얼'\nhttp: 'HTTP'\nApplySSLFailed: '[{{ .domain }}] 인증서 신청이 실패했습니다. {{ .detail }}'\nApplySSLSuccess: '[{{ .domain }}] 인증서 신청이 성공적으로 완료되었습니다'\nDNSAccountName: 'DNS 계정 [{{ .name }}] 공급업체 [{{ .type }}]'\nPushDirLog: '인증서가 [{{ .path }}] {{ .status }} 디렉토리로 푸시되었습니다.'\nErrDeleteCAWithSSL: '현재 조직에는 발급된 인증서가 있어 삭제할 수 없습니다.'\nErrDeleteWithPanelSSL: '패널 SSL 구성은 이 인증서를 사용하므로 삭제할 수 없습니다.'\nErrDefaultCA: '기본 권한을 삭제할 수 없습니다'\nApplyWebSiteSSLLog: '{{ .name }} 웹사이트 인증서 갱신 시작'\nErrUpdateWebsiteSSL: '{{ .name }} 웹사이트 인증서 업데이트에 실패했습니다: {{ .err }}'\nApplyWebSiteSSLSuccess: '웹사이트 인증서 업데이트 성공'\nErrExecShell: '스크립트 {{ .err }}을 실행하지 못했습니다.'\nExecShellStart: '스크립트 실행 시작'\nExecShellSuccess: '스크립트 실행 성공'\nStartUpdateSystemSSL: '시스템 인증서 업데이트 시작'\nUpdateSystemSSLSuccess: '시스템 인증서 업데이트가 성공적으로 완료되었습니다.'\nErrWildcardDomain: 'HTTP 모드에서 와일드카드 도메인 이름 인증서를 신청할 수 없습니다'\nErrApplySSLCanNotDelete: '신청 중인 인증서 {{ .name }}는 삭제할 수 없습니다. 나중에 재시도'\nStartPushSSLToNode: '인증서를 노드로 푸시 시작'\nPushSSLToNodeFailed: '노드로 인증서 푸시 실패: {{ .err }}'\nPushSSLToNodeSuccess: '노드로 인증서 푸시 성공'\nErrUserIsExist: '현재 사용자가 이미 존재합니다. 다시 입력하세요'\nErrDatabaseIsExist: '현재 데이터베이스가 이미 존재합니다. 다시 입력하세요'\nErrExecTimeOut: 'SQL 실행 시간이 초과되었습니다. 데이터베이스를 확인하십시오.'\nErrRemoteExist: '이 이름을 가진 원격 데이터베이스가 이미 존재합니다. 수정하고 다시 시도하세요'\nErrLocalExist: '이름이 로컬 데이터베이스에 이미 존재합니다. 이름을 수정하고 다시 시도하세요'\nRemoteBackup: '원격 데이터베이스를 백업하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요...'\nRemoteRecover: '원격 데이터베이스를 복원하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요...'\nArg: '인수'\nErrTypeOfRedis: '복구 파일 유형이 현재 지속성 방법과 일치하지 않습니다. 수정하고 다시 시도하세요'\nErrInUsed: '{{ .detail }}(이)가 사용 중이어서 삭제할 수 없습니다'\nErrObjectInUsed: '이 객체는 사용 중이어서 삭제할 수 없습니다'\nErrObjectBeDependent: '이 이미지는 다른 이미지에 의존되어 있어 삭제할 수 없습니다'\nErrPortRules: '포트 번호가 일치하지 않습니다. 다시 입력하세요'\nErrPgImagePull: '이미지 풀링 시간이 초과되었습니다. 이미지 가속을 구성하거나 {{ .name }} 이미지를 수동으로 풀링한 다음 다시 시도하세요.'\nPruneHelper: '이번 정리 {{ .name }}가 디스크 공간 {{ .size }}을(를) 확보했습니다'\nImageRemoveHelper: '이미지 {{ .name }} 삭제, {{ .size }} 디스크 공간 확보'\nBuildCache: '빌드 캐시'\nVolume: '스토리지 볼륨'\nNetwork: '네트워크'\nPruneStart: '정리 중입니다. 잠시만 기다려주세요...'\nContainerBackupPrepare: '컨테이너 백업 작업 공간 준비'\nContainerBackupStop: '백업 전에 컨테이너 중지'\nContainerBackupInspect: '컨테이너 inspect 및 네트워크 정보 백업'\nContainerBackupMounts: '컨테이너 마운트 데이터 백업'\nContainerBackupMeta: '컨테이너 백업 메타데이터 작성'\nContainerBackupStart: '백업 후 컨테이너 시작'\nContainerRecoverPrepare: '컨테이너 복원 작업 공간 준비'\nContainerRecoverExtract: '컨테이너 백업 파일 압축 해제'\nContainerRecoverParse: '컨테이너 백업 메타데이터 로드'\nContainerRecoverCreate: '컨테이너 재생성'\nContainerRecoverMounts: '컨테이너 마운트 데이터 복원'\nContainerRecoverStart: '복원된 컨테이너 시작'\nContainerRecoverCleanup: '컨테이너 복원 작업 공간 정리'\nComposeBackupPrepare: 'Compose 백업 작업 공간 준비'\nComposeBackupStop: '백업 전에 Compose 중지'\nComposeBackupFiles: 'Compose 파일 백업'\nComposeBackupContainers: 'Compose 컨테이너 백업'\nComposeBackupMeta: 'Compose 백업 메타데이터 작성'\nComposeBackupStart: '백업 후 Compose 시작'\nComposeBackupCleanup: 'Compose 백업 작업 공간 정리'\nComposeRecoverPrepare: 'Compose 복원 작업 공간 준비'\nComposeRecoverExtract: 'Compose 백업 파일 압축 해제'\nComposeRecoverMeta: 'Compose 백업 메타데이터 로드'\nComposeRecoverMetaLogName: 'Compose 복원 메타: 이름={{ .name }}'\nComposeRecoverMetaLogPath: 'Compose 복원 메타: 백업경로={{ .backupPath }} 대상경로={{ .targetDir }}'\nComposeRecoverMetaLogCount: 'Compose 복원 메타: 파일={{ .files }} 컨테이너={{ .containers }}'\nComposeRecoverFiles: 'Compose 파일 복원'\nComposeRecoverContainers: 'Compose 컨테이너 복원'\nComposeRecoverRecord: 'Compose 기록 저장'\nComposeRecoverCleanup: 'Compose 복원 작업 공간 정리'\nErrFileNotExist: '{{ .detail }} 파일이 존재하지 않습니다 소스 파일의 무결성을 확인하세요'\nErrImageBuildErr: '이미지 빌드 실패'\nErrImageExist: '이미지가 이미 존재합니다 이미지 이름을 수정하세요.'\nErrDelWithWebsite: '운영 환경이 이미 웹사이트와 연결되어 있어 삭제할 수 없습니다'\nErrRuntimeStart: '시작 실패'\nErrPackageJsonNotFound: 'package.json 파일이 존재하지 않습니다'\nErrScriptsNotFound: 'package.json에서 스크립트 구성 항목을 찾을 수 없습니다.'\nErrContainerNameNotFound: '컨테이너 이름을 가져올 수 없습니다..env 파일을 확인하세요.'\nErrNodeModulesNotFound: 'node_modules 폴더가 없습니다 런타임 환경을 편집하거나 런타임 환경이 성공적으로 시작될 때까지 기다리십시오'\nErrContainerNameIsNull: '컨테이너 이름이 존재하지 않습니다'\nErrPHPPortIsDefault: '9000 포트는 기본 포트입니다. 수정 후 다시 시도하세요'\nErrPHPRuntimePortFailed: '포트 {{ .name }} 는 현재 런타임 환경에서 이미 사용 중입니다. 수정 후 다시 시도하세요'\nErrConfigNotFound: '구성 파일이 존재하지 않습니다'\nErrConfigParse: '구성 파일 형식이 올바르지 않습니다'\nErrConfigIsNull: '구성 파일은 비어 있을 수 없습니다'\nErrConfigDirNotFound: '실행 디렉토리가 존재하지 않습니다'\nErrConfigAlreadyExist: '같은 이름의 구성 파일이 이미 존재합니다'\nErrUserFindErr: '사용자 {{ .name }} 검색에 실패했습니다 {{ .err }}'\nCutWebsiteLogSuccess: '{{ .name }} 웹사이트 로그가 성공적으로 잘렸습니다. 백업 경로 {{ .path }}'\nHandleShell: '스크립트 {{ .name }} 실행'\nHandleCurl: 'URL {{ .name }} 접근'\nHandleNtpSync: '시스템 시간 동기화'\nHandleSystemClean: '시스템 캐시 정리'\nSystemLog: '시스템 로그'\nCutWebsiteLog: '웹사이트 로그 회전'\nFileOrDir: '디렉터리 / 파일'\nUploadFile: '백업 파일 {{ .file }} 을(를) {{ .backup }}(으)로 업로드 중'\nUpload: '업로드'\nIgnoreBackupErr: '백업 실패, 오류: {{ .detail }}, 이 오류를 무시합니다...'\nIgnoreUploadErr: '업로드 실패, 오류: {{ .detail }}, 이 오류를 무시합니다...'\nLoadBackupFailed: '백업 계정 연결 획득 실패, 오류: {{ .detail }}'\nInExecuting: '현재 작업이 실행 중입니다. 중복 실행하지 마세요'\nNoSuchResource: '데이터베이스에서 백업 내용을 찾을 수 없어 건너뜁니다...'\nCleanLog: '로그 정리'\nCleanLogByName: '{{ .name }} 로그 정리'\nWafIpGroupNotFound: 'WAF IP 그룹을 찾을 수 없습니다. [고급 기능 - WAF - 블랙/화이트 목록 - IP 그룹]으로 이동하여 원격 다운로드 방식으로 IP 그룹을 추가하십시오.'\nErrNotExistUser: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요'\nErrBanAction: '설정에 실패했습니다. 현재 {{ .name }} 서비스를 사용할 수 없습니다. 확인하고 다시 시도하세요'\nErrClamdscanNotFound: 'clamdscan 명령이 감지되지 않았습니다. 설명서를 참조하여 설치하세요'\nTaskSwapSet: 'Swap 설정'\nSetSwap: 'Swap 설정 {{ .path }} - {{ .size }}'\nCreateSwap: 'Swap 파일 생성'\nFormatSwap: 'Swap 파일 포맷'\nEnableSwap: 'Swap 활성화'\nCleanTamperSetting: '히스토리 보호 설정 정리'\nSetTamperWithRules: '규칙에 따라 디렉토리 보호 시작'\nProtectDir: '디렉토리 보호 {{ .name }}'\nIgnoreFile: '파일 보호 {{ .name }}'\nTamperSetSuccessful: '보호 설정 완료, 다음 디렉토리 모니터링 시작:'\nErrScope: '이 구성을 수정하는 것은 지원되지 않습니다'\nErrStateChange: '상태 변경에 실패했습니다'\nErrRuleExist: '규칙이 이미 존재합니다'\nErrRuleNotExist: '규칙이 존재하지 않습니다'\nErrParseIP: '잘못된 IP 형식'\nErrDefaultIP: '기본값은 예약된 이름입니다. 다른 이름으로 변경해 주세요'\nErrGroupInUse: 'IP 그룹이 블랙리스트/화이트리스트에 사용 중이므로 삭제할 수 없습니다.'\nErrIPGroupAclUse: 'IP 그룹은 웹사이트 {{ .name }} 의 사용자 정의 규칙에 사용되므로 삭제할 수 없습니다'\nErrGroupExist: 'IP 그룹 이름이 이미 존재합니다'\nErrIPRange: '잘못된 IP 범위'\nErrIPExist: 'IP가 이미 존재합니다'\nurlDefense: 'URL 규칙'\nurlHelper: '금지된 URL'\ndirFilter: '디렉토리 필터'\nxss: 'XSS'\nphpExec: 'PHP 스크립트 실행'\noneWordTrojan: '한 단어 트로이'\nappFilter: '위험한 디렉토리 필터링 적용'\nwebShell: '웹쉘'\nargs: '매개변수 규칙'\nprotocolFilter: '프로토콜 필터링'\njavaFilter: 'Java 위험 파일 필터'\nscannerFilter: '스캐너 필터'\nescapeFilter: '탈출 필터'\ncustomRule: '사용자 정의 규칙'\nhttpMethod: 'HTTP 메서드 필터링'\nfileExt: '파일 업로드 제한'\ndefaultIpBlack: '악성 IP 그룹'\ncookie: '쿠키 규칙'\nurlBlack: 'URL 블랙리스트'\nuaBlack: '사용자 에이전트 블랙리스트'\nattackCount: '공격 빈도 제한'\nfileExtCheck: '파일 업로드 제한'\ngeoRestrict: '지역 접근 제한'\nunknownWebsite: '허가받지 않은 도메인 이름 접근'\nnotFoundCount: '404 속도 제한'\nheaderDefense: '헤더 규칙'\ndefaultUaBlack: '사용자 에이전트 규칙'\nmethodWhite: 'HTTP 규칙'\ncaptcha: '인간-기계 검증'\nfiveSeconds: '5초 검증'\nvulnCheck: '보충 규칙'\nacl: '사용자 정의 규칙'\nsql: 'SQL 주입'\ncc: '접근 주파수 제한'\ndefaultUrlBlack: 'URL 규칙'\nsqlInject: 'SQL 주입'\nErrDBNotExist: '데이터베이스가 존재하지 않습니다'\nallow: '허용하다'\ndeny: '거부하다'\nOpenrestyNotFound: 'Openresty가 설치되지 않았습니다'\nremoteIpIsNull: 'IP 목록이 비어 있습니다'\nOpenrestyVersionErr: 'Openresty 버전이 너무 낮습니다. Openresty를 1.27.1.2-2-2-focal로 업그레이드하세요'\nErrFileTooLarge: '파일이 1MB를 초과하여 업로드할 수 없습니다'\nTaskStart: '[START] {{ .name }} 작업이 시작됩니다.'\nTaskEnd: '{{ .name }} 작업이 완료되었습니다 [완료]'\nTaskFailed: '{{ .name }} 작업이 실패했습니다'\nTaskTimeout: '{{ .name }}의 시간이 초과되었습니다'\nTaskSuccess: '{{ .name }} 작업이 성공했습니다'\nTaskRetry: '{{ .name }}번째 재시도 시작'\nSubTaskSuccess: '{{ .name }}이 성공했습니다'\nSubTaskFailed: '{{ .name }}이 실패했습니다: {{ .err }}'\nTaskInstall: '설치'\nTaskUninstall: '제거'\nTaskCreate: '생성'\nTaskDelete: '삭제'\nTaskUpgrade: '업그레이드'\nTaskUpdate: '업데이트'\nTaskRestart: '다시 시작'\nTaskProtect: '보호'\nTaskBackup: '백업'\nTaskRecover: '복구'\nTaskRollback: '롤백'\nTaskPull: '당기기'\nTaskCommit: '커밋'\nTaskBuild: '빌드'\nTaskPush: '푸시'\nTaskClean: '정리'\nTaskHandle: '실행'\nTaskImport: '가져오기'\nTaskExport: '내보내기'\nWebsite: '웹사이트'\nApp: '애플리케이션'\nRuntime: '런타임 환경'\nDatabase: '데이터베이스'\nConfigFTP: 'FTP 사용자 {{ .name }} 생성'\nConfigOpenresty: 'Openresty 구성 파일 생성'\nInstallAppSuccess: '애플리케이션 {{ .name }}이 성공적으로 설치되었습니다.'\nConfigRuntime: '런타임 환경 구성'\nConfigApp: '구성 애플리케이션'\nSuccessStatus: '{{ .name }}이 성공했습니다'\nFailedStatus: '{{ .name }}이(가) {{ .err }}에 실패했습니다'\nHandleLink: '핸들 애플리케이션 연결'\nHandleDatabaseApp: '애플리케이션 매개변수 처리'\nExecShell: '{{ .name }} 스크립트 실행'\nPullImage: '이미지 가져오기'\nStart: '시작'\nRun: '시작'\nStop: '정지'\nImage: '거울'\nCompose: '오케스트레이션'\nContainer: '컨테이너'\nAppLink: '연결된 애플리케이션'\nEnableSSL: 'HTTPS 활성화'\nAppStore: '앱스토어'\nTaskSync: '동기화'\nLocalApp: '로컬 애플리케이션'\nSubTask: '하위 작업'\nRuntimeExtension: '런타임 환경 확장'\nTaskIsExecuting: '작업이 실행 중입니다'\nCustomAppstore: '사용자 정의 애플리케이션 웨어하우스'\nTaskExec: '실행'\nTaskBatch: '일괄 작업'\nFileConvert: '파일 형식 변환'\nClamscan: '{{ .name }} 스캔'\nTaskScan: '스캔'\nOllamaModelPull: 'Ollama 모델 {{ .name }}을(를) 끌어오세요'\nOllamaModelSize: 'Ollama 모델 {{ .name }}의 크기를 가져옵니다'\nSnapshot: '스냅샷'\nSnapDBInfo: '1Panel 데이터베이스 정보 쓰기'\nSnapCopy: '파일 및 디렉토리 {{ .name }} 복사'\nSnapNewDB: '데이터베이스 {{ .name }} 연결 초기화'\nSnapDeleteOperationLog: '작업 로그 삭제'\nSnapDeleteLoginLog: '액세스 로그 삭제'\nSnapDeleteMonitor: '모니터링 데이터 삭제'\nSnapRemoveSystemIP: '시스템 IP 제거'\nSnapBaseInfo: '1패널 기본 정보 쓰기'\nSnapInstallAppImageEmpty: '애플리케이션 이미지가 선택되지 않아 건너뜁니다...'\nSnapInstallApp: '1Panel 설치된 애플리케이션 백업'\nSnapDockerSave: '설치된 애플리케이션 압축'\nSnapLocalBackup: '1Panel 로컬 백업 디렉토리 백업'\nSnapCompressBackup: '로컬 백업 디렉토리 압축'\nSnapPanelData: '1Panel 데이터 디렉토리 백업'\nSnapCompressPanel: '압축 데이터 디렉토리'\nSnapWebsite: '1Panel 웹사이트 디렉토리 백업'\nSnapCloseDBConn: '데이터베이스 연결 닫기'\nSnapCompress: '스냅샷 파일 만들기'\nSnapCompressFile: '스냅샷 파일 압축'\nSnapCheckCompress: '스냅샷 압축 파일 확인'\nSnapCompressSize: '스냅샷 파일 크기 {{ .name }}'\nSnapUpload: '스냅샷 파일 업로드'\nSnapUploadTo: '스냅샷 파일을 {{ .name }}에 업로드'\nSnapUploadRes: '스냅샷 파일을 {{ .name }}에 업로드'\nSnapshotRecover: '스냅샷 복원'\nRecoverDownload: '스냅샷 파일 다운로드'\nDownload: '다운로드'\nRecoverDownloadAccount: '스냅샷 다운로드 백업 계정 {{ .name }} 가져오기'\nRecoverDecompress: '스냅샷 압축 파일 압축 해제'\nDecompress: '감압'\nBackupBeforeRecover: '스냅샷 전에 시스템 관련 데이터 백업'\nReadJson: '스냅샷에서 Json 파일을 읽습니다'\nReadJsonPath: '스냅샷의 Json 파일 경로 가져오기'\nReadJsonContent: 'Json 파일 읽기'\nReadJsonMarshal: 'Json 이스케이프 처리'\nRecoverApp: '설치된 앱 복원'\nRecoverWebsite: '웹사이트 디렉토리 복구'\nRecoverAppImage: '스냅샷 이미지 백업 복원'\nRecoverCompose: '다른 Composer 콘텐츠 복원'\nRecoverComposeList: '모든 작곡가를 복원합니다'\nRecoverComposeItem: '작성 {{ .name }} 복구'\nRecoverAppEmpty: '스냅샷 파일에서 애플리케이션 이미지 백업을 찾을 수 없습니다'\nRecoverBaseData: '기본 데이터 및 파일 복구'\nRecoverDaemonJsonEmpty: '스냅샷 파일과 현재 머신 모두 컨테이너 구성 daemon.json 파일이 없습니다.'\nRecoverDaemonJson: '컨테이너 구성 daemon.json 파일 복원'\nRecoverDBData: '데이터베이스 데이터 복구'\nRecoverBackups: '로컬 백업 디렉토리 복원'\nRecoverPanelData: '복구 데이터 디렉토리'\nContainerImagePull: '컨테이너 이미지 {{ .name }} 가져오기'\nContainerRemoveOld: '원래 컨테이너 {{ .name }} 제거'\nContainerImageCheck: '이미지가 정상적으로 당겨졌는지 확인'\nContainerLoadInfo: '기본 컨테이너 정보 가져오기'\nContainerRecreate: '컨테이너 업데이트에 실패했습니다. 이제 원래 컨테이너를 복원하기 시작합니다.'\nContainerCreate: '새로운 컨테이너 {{ .name }}를 만듭니다'\nContainerCreateFailed: '컨테이너 생성에 실패했습니다. 실패한 컨테이너를 삭제하세요'\nContainerStartCheck: '컨테이너가 시작되었는지 확인'\nImageBuild: '이미지 빌드'\nImagePull: '이미지 가져오기'\nImageRepoAuthFromDB: '데이터베이스에서 저장소 인증 정보 가져오기'\nImagePush: '이미지 푸시'\nImageRenameTag: '이미지 태그 수정'\nImageNewTag: '새로운 이미지 태그 {{ .name }}'\nComposeCreate: '작곡 만들기'\nBackupNginxConfig: '웹사이트 OpenResty 구성 파일 백업'\nCompressFileSuccess: '디렉토리 압축이 성공했습니다. {{ .name }}으로 압축되었습니다.'\nCompressDir: '압축 디렉토리'\nDeCompressFile: '파일 {{ .name }} 압축 해제'\nErrCheckValid: '백업 파일 검증에 실패했습니다, {{ .name }}'\nRollback: '롤백'\nwebsiteDir: '웹사이트 디렉토리'\nRecoverFailedStartRollBack: '복구 실패, 롤백 시작'\nAppBackupFileIncomplete: '백업 파일이 불완전하며 app.json 또는 app.tar.gz 파일이 없습니다.'\nAppAttributesNotMatch: '애플리케이션 유형 또는 이름이 일치하지 않습니다'\nErrAlertSync: '알람 정보 동기화 오류입니다. 확인하고 다시 시도하세요'\nErrInstallExtension: '이미 설치 작업이 진행 중입니다. 작업이 완료될 때까지 기다려 주세요.'\nPanelAlertTitle: '패널 알림 통지'\nTestAlertTitle: '테스트 이메일 - 이메일 연결 확인'\nTestAlert: '이것은 테스트 이메일이며 이메일 발송 설정이 올바른지 확인합니다.'\nLicenseExpirationAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 라이센스가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오.'\nCronJobFailedAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 예약 작업 {{ .name }} 실행에 실패했습니다. 자세한 내용은 패널에 로그인하십시오.'\nClamAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 바이러스 스캔 작업에서 {{ .num }}개의 감염 파일이 감지되었습니다. 자세한 내용은 패널에 로그인하십시오.'\nWebSiteAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 웹사이트가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오.'\nSSLAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 SSL 인증서가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오.'\nDiskUsedAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 디스크 {{ .name }} 사용률이 {{ .used }}에 도달했습니다. 자세한 내용은 패널에 로그인하십시오.'\nResourceAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 최근 {{ .time }}분간 {{ .name }} 평균 사용률은 {{ .used }}입니다. 자세한 내용은 패널에 로그인하십시오.'\nPanelVersionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 최신 버전으로 업그레이드할 수 있습니다. 자세한 내용은 패널에 로그인하십시오.'\nPanelPwdExpirationAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 패널 비밀번호가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오.'\nCommonAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .msg }}。자세한 내용은 패널에 로그인하십시오.'\nNodeExceptionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 노드에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'\nLicenseExceptionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 라이센스에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'\nSSHAndPanelLoginAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .loginIp }}에서의 {{ .name }} 로그인에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'\nDeviceNotFound: '장치 {{ .name }} 을(를) 찾을 수 없습니다'\nDeviceIsMounted: '장치 {{ .name }} 이(가) 마운트되었습니다, 먼저 마운트 해제하세요'\nPartitionDiskErr: '파티션 분할에 실패했습니다, {{ .err }}'\nFormatDiskErr: '디스크 포맷에 실패했습니다, {{ .err }}'\nMountDiskErr: '디스크 마운트에 실패했습니다, {{ .err }}'\nXfsNotFound: 'xfs 파일 시스템이 감지되지 않았습니다, 먼저 xfsprogs 를 설치하세요'\n"
  },
  {
    "path": "agent/i18n/lang/ms.yaml",
    "content": "ErrInvalidParams: 'Permintaan tidak sah: {{ .detail }}'\nErrTokenParse: 'Token tidak sah: {{ .detail }}'\nErrInitialPassword: 'Kata laluan asal salah'\nErrInternalServer: 'Ralat pelayan: {{ .detail }}'\nErrRecordExist: 'Rekod sudah wujud'\nErrRecordNotFound: 'Rekod tidak ditemui'\nErrStructTransform: 'Ralat penukaran: {{ .err }}'\nErrNotLogin: 'Belum log masuk: {{ .detail }}'\nErrPasswordExpired: 'Kata laluan tamat: {{ .detail }}'\nErrNotSupportType: 'Jenis tidak disokong: {{ .name }}'\nErrProxy: 'Permintaan gagal: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API dilumpuhkan: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Kunci API tidak sah: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP tidak dibenarkan: {{ .detail }}'\nErrApiConfigDisable: 'API tidak dibenarkan: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Cap masa tidak sah: {{ .detail }}'\nErrShutDown: 'Arahan diberhentikan manual'\nErrMinQuickJump: 'Tambah sekurang-kurangnya satu pintasan pantas'\nErrMaxQuickJump: 'Maksimum 4 pintasan pantas'\nErrUsernameIsExist: 'Nama pengguna sudah wujud'\nErrNameIsExist: 'Nama sudah wujud'\nErrDemoEnvironment: 'Tidak tersedia dalam demo'\nErrCmdTimeout: 'Arahan tamat masa'\nErrCmdIllegal: 'Arahan ada aksara tidak sah'\nErrPortExist: 'Port {{ .port }} sudah digunakan oleh {{ .type }} [{{ .name }}]'\nTYPE_APP: 'Aplikasi'\nTYPE_RUNTIME: 'Persekitaran masa jalan'\nTYPE_DOMAIN: 'Nama Domain'\nErrTypePort: 'Format ''Port {{ .name }} tidak betul'''\nErrTypePortRange: 'Julat port perlu antara 1-65535'\nSuccess: 'Kejayaan'\nFailed: 'Gagal'\nSystemRestart: 'Tugas terganggu kerana sistem mula semula'\nErrGroupIsDefault: 'Kumpulan lalai, tidak boleh dipadamkan'\nErrGroupIsInWebsiteUse: 'Kumpulan sedang digunakan oleh tapak web lain dan tidak boleh dipadamkan.'\nDecrypt: 'Dekripsi'\nErrAgentAccountBound: 'Akaun terikat kepada ejen'\nErrAgentAccountUnavailable: 'Sambungan akaun tidak tersedia: {{ .err }}'\nErrAgentProviderNotSupported: 'Penyedia ejen tidak disokong'\nErrAgentAccountRequired: 'Pilih akaun ejen terlebih dahulu'\nErrAgentAccountNotVerified: 'Akaun ejen belum disahkan'\nErrAgentAccountModelsRequired: 'Sila konfigur sekurang-kurangnya satu model'\nErrAgentAccountSingleInitialModel: 'Hanya satu model awal boleh dikonfigurasi semasa mencipta akaun'\nErrAgentModelNotInAccount: 'Model tidak dikonfigurasikan dalam akaun yang dipilih'\nErrAgentModelInUse: 'Masih ada ejen terikat yang menggunakan model ini: {{ .name }}'\nErrAgentBaseURLRequired: 'URL asas diperlukan'\nErrAgentApiKeyRequired: 'Kunci API diperlukan'\nErrAgentComposeRequired: 'Kandungan compose diperlukan'\nErrAgentIDRequired: 'ID ejen diperlukan'\nErrAgentAccountIDRequired: 'ID akaun diperlukan'\nErrAgentLimitReached: 'Edisi Community menyokong sehingga {{ .max }} ejen AI. Naik taraf ke edisi Professional untuk tiada had bilangan'\nLocalhost: 'Mesin Tempatan'\nErrBackupInUsed: 'Akaun sandaran sedang digunakan oleh tugas'\nErrBackupCheck: 'Ujian sambungan gagal: {{ .err }}'\nErrBackupLocalDelete: 'Tidak boleh padam sandaran tempatan'\nErrBackupLocalCreate: 'Tidak boleh buat sandaran tempatan'\nErrPortInUsed: 'Port {{ .detail }} sudah diduduki'\nErrAppLimit: 'Bilangan aplikasi yang dipasang telah melebihi had'\nErrNotInstall: 'Aplikasi tidak dipasang'\nErrPortInOtherApp: 'Port {{ .port }} sudah diduduki oleh aplikasi {{ .apps }}'\nErrDbUserNotValid: 'Pangkalan data sedia ada, nama pengguna dan kata laluan tidak sepadan'\nErrUpdateBuWebsite: 'Aplikasi telah berjaya dikemas kini, tetapi pengubahsuaian fail konfigurasi tapak web gagal. Sila semak konfigurasi'\nErr1PanelNetworkFailed: 'Pembuatan rangkaian kontena lalai gagal {{ .detail }}'\nErrFileParse: 'Penghuraian fail karang docker aplikasi gagal'\nErrInstallDirNotFound: 'Direktori pemasangan tidak wujud. Jika anda perlu menyahpasang, sila pilih Paksa Nyahpasang'\nAppStoreIsUpToDate: 'Gedung aplikasi sudah pun versi terkini'\nLocalAppVersionNull: 'Aplikasi ''{{ .name }} tidak disegerakkan ke versi Tidak dapat menambah pada senarai apl'''\nLocalAppVersionErr: '{{ .name }} versi penyegerakan {{ .version }} gagal {{ .err }}'\nErrFileNotFound: 'Fail {{ .name }} tidak wujud'\nErrFileParseApp: '{{ .name }} penghuraian fail gagal {{ .err }}'\nErrAppDirNull: 'Folder versi tidak wujud'\nLocalAppErr: 'Penyegerakan {{ .name }} aplikasi gagal {{ .err }}'\nErrContainerName: 'Nama kontena sudah wujud'\nErrCreateHttpClient: 'Gagal membuat permintaan {{ .err }}'\nErrHttpReqTimeOut: 'Permintaan tamat masa {{ .err }}'\nErrHttpReqFailed: 'Permintaan gagal {{ .err }}'\nErrNoSuchHost: 'Tidak dapat mencari pelayan yang diminta {{ .err }}'\nErrHttpReqNotFound: 'Sumber yang diminta {{ .err }} tidak dapat ditemui'\nErrContainerNotFound: 'Bekas {{ .name }} tidak wujud'\nErrContainerMsg: 'Bekas ''{{ .name }} adalah tidak normal. Sila semak log pada halaman kontena untuk butiran'''\nErrAppBackup: '{{ .name }} sandaran aplikasi gagal {{ .err }}'\nErrVersionTooLow: 'Versi 1Panel semasa terlalu rendah untuk mengemas kini App Store. Sila tingkatkan versi sebelum beroperasi.'\nErrAppNameExist: 'Nama aplikasi sudah wujud'\nAppStoreIsSyncing: 'App Store sedang menyegerak, sila cuba sebentar lagi'\nErrGetCompose: 'Gagal mendapatkan fail docker-compose.yml {{ .detail }}'\nErrAppWarn: 'Status tidak normal, sila semak log'\nErrAppParamKey: 'Parameter {{ .name }} medan tidak normal'\nErrAppUpgrade: 'Peningkatan {{ .name }} aplikasi gagal {{ .err }}'\nAppRecover: 'Aplikasi tarik balik {{ .name }}'\nPullImageStart: 'Mula tarik imej [{{ .name }}]'\nPullImageSuccess: 'Tarik imej berjaya'\nAppStoreSyncSuccess: 'Penyegerakan App Store berjaya'\nSyncAppDetail: 'Segerakkan konfigurasi aplikasi'\nAppVersionNotMatch: 'Aplikasi {{ .name }} memerlukan versi 1Panel yang lebih tinggi, melangkau penyegerakan'\nMoveSiteDir: 'Perubahan direktori laman web didapati. Perlu memindahkan direktori laman web OpenResty ke {{ .name }}'\nMoveSiteDirSuccess: 'Penghijrahan direktori tapak web yang berjaya'\nDeleteRuntimePHP: 'Padam masa jalan PHP'\nCustomAppStoreFileValid: 'Pakej gedung apl perlu dalam format.tar.gz'\nPullImageTimeout: 'Tarik tamat masa imej, sila cuba tingkatkan pecutan imej atau tukar kepada pecutan imej lain'\nErrAppIsDown: 'Status permohonan {{ .name }} tidak normal, sila semak'\nErrCustomApps: 'Ada aplikasi yang dipasang, sila nyahpasangnya dahulu'\nErrCustomRuntimes: 'Terdapat persekitaran masa jalan yang dipasang, sila padamkannya dahulu'\nErrAppVersionDeprecated: 'Aplikasi {{ .name }} tidak sesuai dengan versi 1Panel saat ini, dilewati'\nErrDockerFailed: 'Keadaan Docker tidak normal, sila periksa status perkhidmatan'\nErrDockerComposeCmdNotFound: 'Perintah Docker Compose tidak wujud, sila pasang perintah ini di mesin tuan terlebih dahulu'\nUseExistImage: 'Imej sudah wujud, menggunakan imej sedia ada'\nErrDatabaseNotFound: 'Pangkalan data {{ .name }} tidak wujud'\nExportIP: 'IP Log Masuk'\nExportArea: 'Lokasi'\nExportPort: 'Port'\nExportAuthMode: 'Kaedah Log Masuk'\nExportUser: 'Pengguna'\nExportStatus: 'Status Log Masuk'\nExportDate: 'Masa'\nErrFileCanNotRead: 'Fail ini tidak menyokong pratonton'\nErrFileToLarge: 'Fail lebih besar daripada 10M dan tidak boleh dibuka'\nErrPathNotFound: 'Direktori tidak wujud'\nErrMovePathFailed: 'Laluan sasaran tidak boleh mengandungi laluan asal'\nErrLinkPathNotFound: 'Laluan sasaran tidak wujud'\nErrFileIsExist: 'Fail atau folder sudah wujud'\nErrFileUpload: '{{ .name }} gagal memuat naik fail {{ .detail }}'\nErrFileDownloadDir: 'Folder muat turun tidak disokong'\nErrCmdNotFound: 'Arahan {{ .name }} tidak wujud, sila pasang arahan ini pada hos dahulu'\nErrSourcePathNotFound: 'Direktori sumber tidak wujud'\nErrFavoriteExist: 'Laluan ini telah digemari'\nErrInvalidChar: 'Aksara haram tidak dibenarkan'\nErrPathNotDelete: 'Direktori yang dipilih tidak boleh dipadamkan'\nErrLogFileToLarge: 'Fail log melebihi 500MB dan tidak boleh dibuka'\nFileDropFailed: 'Gagal membersihkan fail {{ .name }}: {{ .err }}'\nFileDropSuccess: 'Berjaya membersihkan fail {{ .name }}, {{ .count }} fail dibersihkan, {{ .size }} ruang cakera dibebaskan'\nFileDropSum: 'Pembersihan fail selesai, sejumlah {{ .count }} fail dibersihkan, sejumlah {{ .size }} ruang cakera dibebaskan'\nErrBadDecrypt: 'Kata laluan penyahsulitan pakej termampat salah, penyahmampatan gagal, sila periksa dan cuba lagi'\nErrAliasIsExist: 'Alias sudah wujud'\nErrBackupMatch: 'Fail sandaran tidak sepadan dengan beberapa data tapak web semasa {{ .detail }}'\nErrBackupExist: 'Bahagian sepadan data sumber dalam fail sandaran tidak wujud {{ .detail }}'\nErrPHPResource: 'Persekitaran operasi tempatan tidak menyokong penukaran'\nErrPathPermission: 'Folder dengan keizinan bukan 1000:1000 telah dikesan dalam direktori indeks, yang mungkin menyebabkan ralat dinafikan Akses pada tapak web. Sila klik butang Simpan di atas'\nErrDomainIsUsed: 'Nama domain telah digunakan oleh tapak web [{{ .name }}]'\nErrDomainFormat: 'Format nama domain {{ .name }} tidak betul'\nErrDefaultAlias: 'lalai ialah kod simpanan, sila gunakan kod lain'\nErrParentWebsite: 'Anda perlu memadamkan subtapak {{ .name }} dahulu'\nErrBuildDirNotFound: 'Direktori binaan tidak wujud'\nErrImageNotExist: 'Imej persekitaran operasi {{ .name }} tidak wujud, sila edit semula persekitaran pengendalian'\nErrProxyIsUsed: 'Pengimbang beban telah digunakan oleh pengganti terbalik, tidak boleh dipadamkan'\nErrSSLValid: 'Fail sijil bermasalah, sila periksa status sijil'\nErrWebsiteDir: 'Sila pilih direktori dalam direktori laman web.'\nErrComposerFileNotFound: 'Fail composer.json tidak wujud'\nErrRuntimeNoPort: 'Persekitaran runtime tidak diset dengan port, sila edit persekitaran runtime terlebih dahulu.'\nStatus: 'Status'\nstart: 'Mulakan'\nstop: 'Berhenti'\nrestart: 'Mulakan Semula'\nkill: 'Hentikan Paksa'\npause: 'Jeda'\nunpause: 'Sambung Semula'\nremove: 'Padam'\ndelete: 'Padam'\nErrDefaultWebsite: 'Laman web lalai telah ditetapkan, sila batalkan sebelum menetapkan'\nSSL: 'Sijil'\nSet: 'Tetapan'\nErrSSLCannotDelete: 'Sijil {{ .name }} sedang digunakan oleh tapak web dan tidak boleh dipadamkan'\nErrAccountCannotDelete: 'Akaun dikaitkan dengan sijil dan tidak boleh dipadamkan'\nErrSSLApply: 'Pembaharuan sijil berjaya, muat semula openresty gagal, sila semak konfigurasi'\nErrEmailIsExist: 'Peti mel sudah wujud'\nErrSSLKeyNotFound: 'Fail kunci peribadi tidak wujud'\nErrSSLCertificateNotFound: 'Fail sijil tidak wujud'\nErrSSLKeyFormat: 'Pengesahan fail kunci peribadi gagal'\nErrSSLCertificateFormat: 'Format fail sijil tidak betul, sila gunakan format pem'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid atau EabHmacKey tidak boleh kosong'\nErrOpenrestyNotFound: 'Mod Http memerlukan Openresty dipasang dahulu'\nApplySSLStart: 'Mula memohon sijil, nama domain [{{ .domain }}] kaedah permohonan [{{ .type }}]'\ndnsAccount: 'DNS Auto'\ndnsManual: 'Manual DNS'\nhttp: 'HTTP'\nApplySSLFailed: 'Permohonan untuk sijil [{{ .domain }}] gagal, {{ .detail }}'\nApplySSLSuccess: 'Berjaya memohon sijil [{{ .domain }}]'\nDNSAccountName: 'Akaun DNS [{{ .name }}] vendor [{{ .type }}]'\nPushDirLog: 'Sijil ditolak ke direktori [{{ .path }}] {{ .status }}'\nErrDeleteCAWithSSL: 'Organisasi semasa mempunyai sijil yang telah dikeluarkan dan tidak boleh dipadamkan.'\nErrDeleteWithPanelSSL: 'Konfigurasi SSL Panel menggunakan sijil ini dan tidak boleh dipadamkan'\nErrDefaultCA: 'Pihak berkuasa lalai tidak boleh dipadamkan'\nApplyWebSiteSSLLog: 'Mula memperbaharui sijil tapak web {{ .name }}'\nErrUpdateWebsiteSSL: '{{ .name }} kemas kini sijil tapak web gagal: {{ .err }}'\nApplyWebSiteSSLSuccess: 'Berjaya kemas kini sijil tapak web'\nErrExecShell: 'Gagal melaksanakan skrip {{ .err }}'\nExecShellStart: 'Mula melaksanakan skrip'\nExecShellSuccess: 'Pelaksanaan skrip berjaya'\nStartUpdateSystemSSL: 'Mula mengemas kini sijil sistem'\nUpdateSystemSSLSuccess: 'Berjaya kemas kini sijil sistem'\nErrWildcardDomain: 'Tidak dapat memohon sijil nama domain kad bebas dalam mod HTTP'\nErrApplySSLCanNotDelete: 'Sertifikat {{ .name }} yang sedang diajukan tidak dapat dihapus, sila cuba lagi kemudian.'\nStartPushSSLToNode: 'Mula menolak sijil ke nod'\nPushSSLToNodeFailed: 'Gagal menolak sijil ke nod: {{ .err }}'\nPushSSLToNodeSuccess: 'Berjaya menolak sijil ke nod'\nErrUserIsExist: 'Pengguna semasa sudah wujud, sila masukkan semula'\nErrDatabaseIsExist: 'Pangkalan data semasa sudah wujud, sila masukkan semula'\nErrExecTimeOut: 'Pelaksanaan SQL tamat masa, sila semak pangkalan data'\nErrRemoteExist: 'Pangkalan data jauh sudah wujud dengan nama ini, sila ubah suainya dan cuba lagi'\nErrLocalExist: 'Nama sudah wujud dalam pangkalan data tempatan, sila ubah suai dan cuba lagi'\nRemoteBackup: 'Untuk menyandarkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu...'\nRemoteRecover: 'Untuk memulihkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu...'\nArg: 'Hujah'\nErrTypeOfRedis: 'Jenis fail pemulihan tidak sepadan dengan kaedah kegigihan semasa, sila ubah suai dan cuba lagi'\nErrInUsed: '{{ .detail }} sedang digunakan dan tidak boleh dipadam'\nErrObjectInUsed: 'Objek ini sedang digunakan dan tidak boleh dipadam'\nErrObjectBeDependent: 'Imej ini digantungkan oleh imej lain dan tidak boleh dipadam'\nErrPortRules: 'Nombor port tidak sepadan, sila masukkan semula'\nErrPgImagePull: 'Tarikh imej tamat masa, sila konfigurasikan pecutan imej atau tarik imej {{ .name }} secara manual dan cuba lagi'\nPruneHelper: 'Pembersihan ini {{ .name }} membebaskan ruang cakera {{ .size }}'\nImageRemoveHelper: 'Padam imej {{ .name }}, membebaskan {{ .size }} ruang cakera'\nBuildCache: 'Cache binaan'\nVolume: 'Jilid storan'\nNetwork: 'Rangkaian'\nPruneStart: 'Pembersihan sedang dijalankan, sila tunggu...'\nContainerBackupPrepare: 'Sediakan ruang kerja sandaran kontena'\nContainerBackupStop: 'Hentikan kontena sebelum sandaran'\nContainerBackupInspect: 'Sandarkan inspect dan rangkaian kontena'\nContainerBackupMounts: 'Sandarkan mount kontena'\nContainerBackupMeta: 'Tulis metadata sandaran kontena'\nContainerBackupStart: 'Mulakan kontena selepas sandaran'\nContainerRecoverPrepare: 'Sediakan ruang kerja pemulihan kontena'\nContainerRecoverExtract: 'Ekstrak arkib sandaran kontena'\nContainerRecoverParse: 'Muat metadata sandaran kontena'\nContainerRecoverCreate: 'Cipta semula kontena'\nContainerRecoverMounts: 'Pulihkan mount kontena'\nContainerRecoverStart: 'Mulakan kontena dipulihkan'\nContainerRecoverCleanup: 'Bersihkan ruang kerja pemulihan kontena'\nComposeBackupPrepare: 'Sediakan ruang kerja sandaran compose'\nComposeBackupStop: 'Hentikan compose sebelum sandaran'\nComposeBackupFiles: 'Sandarkan fail compose'\nComposeBackupContainers: 'Sandarkan kontena compose'\nComposeBackupMeta: 'Tulis metadata sandaran compose'\nComposeBackupStart: 'Mulakan compose selepas sandaran'\nComposeBackupCleanup: 'Bersihkan ruang kerja sandaran compose'\nComposeRecoverPrepare: 'Sediakan ruang kerja pemulihan compose'\nComposeRecoverExtract: 'Ekstrak arkib sandaran compose'\nComposeRecoverMeta: 'Muat metadata sandaran compose'\nComposeRecoverMetaLogName: 'Metadata pemulihan compose: nama={{ .name }}'\nComposeRecoverMetaLogPath: 'Metadata pemulihan compose: laluanSandaran={{ .backupPath }} laluanSasaran={{ .targetDir }}'\nComposeRecoverMetaLogCount: 'Metadata pemulihan compose: fail={{ .files }} kontena={{ .containers }}'\nComposeRecoverFiles: 'Pulihkan fail compose'\nComposeRecoverContainers: 'Pulihkan kontena compose'\nComposeRecoverRecord: 'Simpan rekod compose'\nComposeRecoverCleanup: 'Bersihkan ruang kerja pemulihan compose'\nErrFileNotExist: 'Fail {{ .detail }} tidak wujud Sila semak integriti fail sumber'\nErrImageBuildErr: 'Pembinaan imej gagal'\nErrImageExist: 'Imej sudah wujud Sila ubah nama imej.'\nErrDelWithWebsite: 'Persekitaran pengendalian sudah dikaitkan dengan tapak web dan tidak boleh dipadamkan'\nErrRuntimeStart: 'Permulaan gagal'\nErrPackageJsonNotFound: 'fail package.json tidak wujud'\nErrScriptsNotFound: 'Item konfigurasi skrip tidak ditemui dalam package.json'\nErrContainerNameNotFound: 'Tidak dapat mendapatkan nama kontena, sila semak fail.env'\nErrNodeModulesNotFound: 'Folder node_modules tidak wujud Sila edit persekitaran masa jalan atau tunggu persekitaran masa jalan berjaya dimulakan'\nErrContainerNameIsNull: 'Nama kontena tidak wujud'\nErrPHPPortIsDefault: 'Порт 9000 является портом по умолчанию, пожалуйста, измените и попробуйте снова'\nErrPHPRuntimePortFailed: 'Порт {{ .name }} уже используется текущей средой выполнения, пожалуйста, измените и попробуйте снова'\nErrConfigNotFound: 'Fail konfigurasi tidak wujud'\nErrConfigParse: 'Format fail konfigurasi tidak betul'\nErrConfigIsNull: 'Fail konfigurasi tidak boleh kosong'\nErrConfigDirNotFound: 'Direktori berjalan tidak wujud'\nErrConfigAlreadyExist: 'Fail konfigurasi dengan nama yang sama sudah wujud'\nErrUserFindErr: 'Pengguna {{ .name }} carian gagal {{ .err }}'\nCutWebsiteLogSuccess: '{{ .name }} log tapak web berjaya dipotong, laluan sandaran {{ .path }}'\nHandleShell: 'Laksanakan skrip {{ .name }}'\nHandleCurl: 'Akses URL {{ .name }}'\nHandleNtpSync: 'Penyegerakan masa sistem'\nHandleSystemClean: 'Pembersihan cache sistem'\nSystemLog: 'Log Sistem'\nCutWebsiteLog: 'Putar Log Laman Web'\nFileOrDir: 'Direktori / Fail'\nUploadFile: 'Muat naik fail sandaran {{ .file }} ke {{ .backup }}'\nUpload: 'Muat Naik'\nIgnoreBackupErr: 'Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini...'\nIgnoreUploadErr: 'Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini...'\nLoadBackupFailed: 'Gagal mendapatkan sambungan akaun sandaran, ralat: {{ .detail }}'\nInExecuting: 'Tugas semasa sedang dilaksanakan, jangan ulangi pelaksanaan'\nNoSuchResource: 'Tiada kandungan sandaran ditemui dalam pangkalan data, dilangkau...'\nCleanLog: 'Bersihkan log'\nCleanLogByName: 'Bersihkan log {{ .name }}'\nWafIpGroupNotFound: 'Kumpulan IP WAF tidak ditemui. Sila pergi ke [Ciri Lanjutan - WAF - Senarai Hitam/Putih - Kumpulan IP] untuk menambah kumpulan IP melalui kaedah muat turun jauh.'\nErrNotExistUser: 'Pengguna semasa tidak wujud, sila ubah suai dan cuba lagi'\nErrBanAction: 'Tetapan gagal. Perkhidmatan {{ .name }} semasa tidak tersedia. Sila semak dan cuba lagi'\nErrClamdscanNotFound: 'Arahan clamdscan tidak dikesan, sila rujuk dokumentasi untuk memasangnya'\nTaskSwapSet: 'Tetapkan Swap'\nSetSwap: 'Tetapkan Swap {{ .path }} - {{ .size }}'\nCreateSwap: 'Buat Fail Swap'\nFormatSwap: 'Format Fail Swap'\nEnableSwap: 'Dayakan Swap'\nCleanTamperSetting: 'Bersihkan tetapan perlindungan sejarah'\nSetTamperWithRules: 'Mulakan perlindungan direktori mengikut peraturan'\nProtectDir: 'Lindungi direktori {{ .name }}'\nIgnoreFile: 'Lindungi fail {{ .name }}'\nTamperSetSuccessful: 'Tetapan perlindungan selesai, mula memantau direktori berikut:'\nErrScope: 'Mengubah suai konfigurasi ini tidak disokong'\nErrStateChange: 'Perubahan keadaan gagal'\nErrRuleExist: 'Peraturan sudah wujud'\nErrRuleNotExist: 'Peraturan tidak wujud'\nErrParseIP: 'Format IP salah'\nErrDefaultIP: 'default ialah nama simpanan, sila tukar kepada nama lain'\nErrGroupInUse: 'Kumpulan IP digunakan oleh senarai hitam/senarai putih dan tidak boleh dipadamkan'\nErrIPGroupAclUse: 'Kumpulan IP digunakan oleh peraturan tersuai tapak web {{ .name }}, tidak boleh dipadamkan'\nErrGroupExist: 'Nama kumpulan IP sudah wujud'\nErrIPRange: 'Julat IP salah'\nErrIPExist: 'IP sudah wujud'\nurlDefense: 'Peraturan URL'\nurlHelper: 'URL Terlarang'\ndirFilter: 'Penapis direktori'\nxss: 'XSS'\nphpExec: 'Pelaksanaan skrip PHP'\noneWordTrojan: 'One Word Trojan'\nappFilter: 'Gunakan penapisan direktori berbahaya'\nwebShell: 'Webshell'\nargs: 'Peraturan parameter'\nprotocolFilter: 'Penapisan protokol'\njavaFilter: 'Penapis fail berbahaya Java'\nscannerFilter: 'Penapis pengimbas'\nescapeFilter: 'penapis melarikan diri'\ncustomRule: 'Peraturan tersuai'\nhttpMethod: 'Penapisan kaedah HTTP'\nfileExt: 'Sekatan muat naik fail'\ndefaultIpBlack: 'Kumpulan IP berniat jahat'\ncookie: 'Peraturan Kuki'\nurlBlack: 'senarai hitam URL'\nuaBlack: 'Senarai hitam Ejen Pengguna'\nattackCount: 'Had kekerapan serangan'\nfileExtCheck: 'Sekatan muat naik fail'\ngeoRestrict: 'Sekatan akses serantau'\nunknownWebsite: 'Akses nama domain yang tidak dibenarkan'\nnotFoundCount: '404 Had Kadar'\nheaderDefense: 'Peraturan header'\ndefaultUaBlack: 'Peraturan Ejen Pengguna'\nmethodWhite: 'Peraturan HTTP'\ncaptcha: 'pengesahan mesin manusia'\nfiveSeconds: 'pengesahan 5 saat'\nvulnCheck: 'Peraturan tambahan'\nacl: 'Peraturan tersuai'\nsql: 'suntikan SQL'\ncc: 'Had kekerapan akses'\ndefaultUrlBlack: 'Peraturan URL'\nsqlInject: 'Suntikan SQL'\nErrDBNotExist: 'Pangkalan data tidak wujud'\nallow: 'membenarkan'\ndeny: 'menafikan'\nOpenrestyNotFound: 'Openresty tidak dipasang'\nremoteIpIsNull: 'Senarai IP kosong'\nOpenrestyVersionErr: 'Versi Openresty terlalu rendah, sila naik taraf Openresty ke 1.27.1.2-2-2-focal'\nErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'\nTaskStart: '{{ .name }} Task mula [MULA]'\nTaskEnd: '{{ .name }} Task selesai [SELESAI]'\nTaskFailed: 'tugas {{ .name }} gagal'\nTaskTimeout: '{{ .name }} tamat masa'\nTaskSuccess: '{{ .name }} Task berjaya'\nTaskRetry: 'Mulakan {{ .name }} cuba semula'\nSubTaskSuccess: '{{ .name }} berjaya'\nSubTaskFailed: '{{ .name }} gagal: {{ .err }}'\nTaskInstall: 'Pasang'\nTaskUninstall: 'Nyahpasang'\nTaskCreate: 'Buat'\nTaskDelete: 'Padam'\nTaskUpgrade: 'Naik taraf'\nTaskUpdate: 'Kemas kini'\nTaskRestart: 'Mulakan semula'\nTaskProtect: 'Lindungi'\nTaskBackup: 'Sandaran'\nTaskRecover: 'Pulihkan'\nTaskRollback: 'Rollback'\nTaskPull: 'Tarik'\nTaskCommit: 'Komit'\nTaskBuild: 'Bina'\nTaskPush: 'Tolak'\nTaskClean: 'Pembersihan'\nTaskHandle: 'Laksanakan'\nTaskImport: 'Import'\nTaskExport: 'Eksport'\nWebsite: 'Laman web'\nApp: 'Aplikasi'\nRuntime: 'Persekitaran masa jalan'\nDatabase: 'Pangkalan Data'\nConfigFTP: 'Buat pengguna FTP {{ .name }}'\nConfigOpenresty: 'Buat fail konfigurasi Openresty'\nInstallAppSuccess: 'Aplikasi {{ .name }} berjaya dipasang'\nConfigRuntime: 'Konfigurasikan persekitaran masa jalan'\nConfigApp: 'Aplikasi Konfigurasi'\nSuccessStatus: '{{ .name }} berjaya'\nFailedStatus: '{{ .name }} gagal {{ .err }}'\nHandleLink: 'Kendalikan persatuan aplikasi'\nHandleDatabaseApp: 'Mengendalikan parameter aplikasi'\nExecShell: 'Laksanakan skrip {{ .name }}'\nPullImage: 'Tarik imej'\nStart: 'Mula'\nRun: 'Mula'\nStop: 'Berhenti'\nImage: 'Cermin'\nCompose: 'Orkestrasi'\nContainer: 'Bekas'\nAppLink: 'Aplikasi Terpaut'\nEnableSSL: 'Dayakan HTTPS'\nAppStore: 'App Store'\nTaskSync: 'Segerakkan'\nLocalApp: 'Aplikasi Tempatan'\nSubTask: 'Subtugas'\nRuntimeExtension: 'Sambungan Persekitaran Runtime'\nTaskIsExecuting: 'Tugas sedang berjalan'\nCustomAppstore: 'Gudang aplikasi tersuai'\nTaskExec: 'Laksanakan'\nTaskBatch: 'Operasi Batch'\nFileConvert: 'Penukaran Format Fail'\nClamscan: 'Imbas {{ .name }}'\nTaskScan: 'Imbas'\nOllamaModelPull: 'Tarik model Ollama {{ .name }}'\nOllamaModelSize: 'Dapatkan saiz model Ollama {{ .name }}'\nSnapshot: 'Snapshot'\nSnapDBInfo: 'Tulis maklumat pangkalan data 1Panel'\nSnapCopy: 'Salin fail & direktori {{ .name }}'\nSnapNewDB: 'Mulakan sambungan {{ .name }} pangkalan data'\nSnapDeleteOperationLog: 'Padam log operasi'\nSnapDeleteLoginLog: 'Padam log akses'\nSnapDeleteMonitor: 'Padamkan data pemantauan'\nSnapRemoveSystemIP: 'Alih keluar IP sistem'\nSnapBaseInfo: 'Tulis maklumat asas 1Panel'\nSnapInstallAppImageEmpty: 'Tiada imej aplikasi dipilih, dilangkau...'\nSnapInstallApp: 'Sandaran aplikasi terpasang 1Panel'\nSnapDockerSave: 'Mampatkan aplikasi yang dipasang'\nSnapLocalBackup: 'Sandaran 1Panel direktori sandaran tempatan'\nSnapCompressBackup: 'Mampatkan direktori sandaran tempatan'\nSnapPanelData: 'Sandaran direktori data 1Panel'\nSnapCompressPanel: 'Direktori Data Mampat'\nSnapWebsite: 'Sandaran direktori tapak web 1Panel'\nSnapCloseDBConn: 'Tutup sambungan pangkalan data'\nSnapCompress: 'Buat fail syot kilat'\nSnapCompressFile: 'Mampatkan fail syot kilat'\nSnapCheckCompress: 'Semak fail mampatan syot kilat'\nSnapCompressSize: 'Saiz fail syot kilat {{ .name }}'\nSnapUpload: 'Muat naik fail syot kilat'\nSnapUploadTo: 'Muat naik fail syot kilat ke {{ .name }}'\nSnapUploadRes: 'Muat naik fail syot kilat ke {{ .name }}'\nSnapshotRecover: 'Snapshot Restore'\nRecoverDownload: 'Muat turun fail syot kilat'\nDownload: 'Muat turun'\nRecoverDownloadAccount: 'Dapatkan syot kilat muat turun akaun sandaran {{ .name }}'\nRecoverDecompress: 'Nyahmampatkan fail termampat syot kilat'\nDecompress: 'Penyahmampatan'\nBackupBeforeRecover: 'Sandarkan data berkaitan sistem sebelum syot kilat'\nReadJson: 'Baca fail Json dalam petikan'\nReadJsonPath: 'Dapatkan laluan fail Json dalam petikan'\nReadJsonContent: 'Baca fail Json'\nReadJsonMarshal: 'Pemprosesan melarikan diri Json'\nRecoverApp: 'Pulihkan apl yang dipasang'\nRecoverWebsite: 'Pulihkan direktori tapak web'\nRecoverAppImage: 'Pulihkan sandaran imej syot kilat'\nRecoverCompose: 'Pulihkan kandungan komposer lain'\nRecoverComposeList: 'Dapatkan semua komposer untuk dipulihkan'\nRecoverComposeItem: 'Recover compose {{ .name }}'\nRecoverAppEmpty: 'Tiada sandaran imej aplikasi ditemui dalam fail syot kilat'\nRecoverBaseData: 'Pulihkan data asas dan fail'\nRecoverDaemonJsonEmpty: 'Kedua-dua fail syot kilat dan mesin semasa tidak mempunyai fail daemon.json konfigurasi bekas'\nRecoverDaemonJson: 'Pulihkan fail daemon.json konfigurasi bekas'\nRecoverDBData: 'Pulihkan data pangkalan data'\nRecoverBackups: 'Pulihkan direktori sandaran tempatan'\nRecoverPanelData: 'Direktori data pemulihan'\nContainerImagePull: 'Tarik imej bekas {{ .name }}'\nContainerRemoveOld: 'Alih keluar bekas asal {{ .name }}'\nContainerImageCheck: 'Periksa sama ada imej ditarik seperti biasa'\nContainerLoadInfo: 'Dapatkan maklumat bekas asas'\nContainerRecreate: 'Kemas kini kontena gagal, kini mula memulihkan bekas asal'\nContainerCreate: 'Buat bekas baharu {{ .name }}'\nContainerCreateFailed: 'Penciptaan bekas gagal, padamkan bekas yang gagal'\nContainerStartCheck: 'Periksa sama ada bekas telah dimulakan'\nImageBuild: 'Membina Imej'\nImagePull: 'Tarik imej'\nImageRepoAuthFromDB: 'Dapatkan maklumat pengesahan repositori daripada pangkalan data'\nImagePush: 'Tolak imej'\nImageRenameTag: 'Ubah suai teg imej'\nImageNewTag: 'Teg imej baharu {{ .name }}'\nComposeCreate: 'Buat gubahan'\nBackupNginxConfig: 'Fail konfigurasi OpenResty tapak web sandaran'\nCompressFileSuccess: 'Mampatkan direktori berjaya, dimampatkan ke {{ .name }}'\nCompressDir: 'Direktori mampatan'\nDeCompressFile: 'Nyahmampatkan fail {{ .name }}'\nErrCheckValid: 'Pengesahan fail sandaran gagal, {{ .name }}'\nRollback: 'Undur'\nwebsiteDir: 'Direktori Laman Web'\nRecoverFailedStartRollBack: 'Pemulihan gagal, mulakan rollback'\nAppBackupFileIncomplete: 'Fail sandaran tidak lengkap dan tidak mempunyai fail app.json atau app.tar.gz'\nAppAttributesNotMatch: 'Jenis atau nama aplikasi tidak sepadan'\nErrAlertSync: 'Ralat penyegerakan maklumat penggera, sila semak dan cuba lagi'\nErrInstallExtension: 'Tugas pemasangan sudah sedang berjalan, silakan tunggu tugas selesai'\nPanelAlertTitle: 'Notifikasi Amaran Panel'\nTestAlertTitle: 'E-mel Ujian - Sahkan Sambungan E-mel'\nTestAlert: 'Ini adalah e-mel ujian untuk mengesahkan konfigurasi penghantaran e-mel anda betul.'\nLicenseExpirationAlert: 'Panel {{ .node }}{{ .ip }} Anda, lesen akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut.'\nCronJobFailedAlert: 'Panel {{ .node }}{{ .ip }} Anda, tugas jadual {{ .name }} gagal dilaksanakan. Sila log masuk ke panel untuk butiran lanjut.'\nClamAlert: 'Panel {{ .node }}{{ .ip }} Anda, tugas imbasan virus mengesan {{ .num }} fail yang dijangkiti. Sila log masuk ke panel untuk butiran lanjut.'\nWebSiteAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} laman web akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut.'\nSSLAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} sijil SSL akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut.'\nDiskUsedAlert: 'Panel {{ .node }}{{ .ip }} Anda, penggunaan cakera {{ .name }} telah mencapai {{ .used }}. Sila log masuk ke panel untuk butiran lanjut.'\nResourceAlert: 'Panel {{ .node }}{{ .ip }} Anda, kadar penggunaan purata {{ .name }} dalam tempoh {{ .time }} minit ialah {{ .used }}. Sila log masuk ke panel untuk butiran lanjut.'\nPanelVersionAlert: 'Panel {{ .node }}{{ .ip }} Anda, versi panel terkini tersedia untuk dinaik taraf. Sila log masuk ke panel untuk butiran lanjut.'\nPanelPwdExpirationAlert: 'Panel {{ .node }}{{ .ip }} Anda, kata laluan panel akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut.'\nCommonAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .msg }}. Sila log masuk ke panel untuk butiran lanjut.'\nNodeExceptionAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} nod tidak normal. Sila log masuk ke panel untuk butiran lanjut.'\nLicenseExceptionAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} lesen tidak normal. Sila log masuk ke panel untuk butiran lanjut.'\nSSHAndPanelLoginAlert: 'Panel {{ .node }}{{ .ip }} Anda, log masuk {{ .name }} dari {{ .loginIp }} tidak normal. Sila log masuk ke panel untuk butiran lanjut.'\nDeviceNotFound: 'Peranti {{ .name }} tidak ditemui'\nDeviceIsMounted: 'Peranti {{ .name }} telah dikaitkan, sila nyahkaitkan terlebih dahulu'\nPartitionDiskErr: 'Gagal membahagikan, {{ .err }}'\nFormatDiskErr: 'Gagal memformat cakera, {{ .err }}'\nMountDiskErr: 'Gagal mengkaitkan cakera, {{ .err }}'\nXfsNotFound: 'Sistem fail xfs tidak dikesan, sila pasang xfsprogs terlebih dahulu'\n"
  },
  {
    "path": "agent/i18n/lang/pt-BR.yaml",
    "content": "ErrInvalidParams: 'Requisição inválida: {{ .detail }}'\nErrTokenParse: 'Erro de token: {{ .detail }}'\nErrInitialPassword: 'Senha original incorreta'\nErrInternalServer: 'Erro interno: {{ .detail }}'\nErrRecordExist: 'Registro já existe'\nErrRecordNotFound: 'Registro não encontrado'\nErrStructTransform: 'Erro de conversão: {{ .err }}'\nErrNotLogin: 'Sem sessão: {{ .detail }}'\nErrPasswordExpired: 'Senha expirada: {{ .detail }}'\nErrNotSupportType: 'Tipo não suportado: {{ .name }}'\nErrProxy: 'Requisição falhou: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API desativada: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Chave API inválida: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP sem permissão: {{ .detail }}'\nErrApiConfigDisable: 'API bloqueada: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Timestamp inválido: {{ .detail }}'\nErrShutDown: 'Comando parado manualmente'\nErrMinQuickJump: 'Adicione pelo menos um atalho rápido'\nErrMaxQuickJump: 'Máximo 4 atalhos rápidos'\nErrUsernameIsExist: 'Usuário já existe'\nErrNameIsExist: 'Nome já existe'\nErrDemoEnvironment: 'Indisponível em demo'\nErrCmdTimeout: 'Comando expirou'\nErrCmdIllegal: 'Comando tem caracteres inválidos'\nErrPortExist: 'Porta {{ .port }} já ocupada por {{ .type }} [{{ .name }}]'\nTYPE_APP: 'Aplicativo'\nTYPE_RUNTIME: 'Ambiente de tempo de execução'\nTYPE_DOMAIN: 'Nome de domínio'\nErrTypePort: 'O formato da porta {{ .name }} está incorreto'\nErrTypePortRange: 'O intervalo de portas precisa estar entre 1-65535'\nSuccess: 'Sucesso'\nFailed: 'Falhou'\nSystemRestart: 'Tarefa interrompida devido à reinicialização do sistema'\nErrGroupIsDefault: 'Grupo padrão, não pode ser excluído'\nErrGroupIsInWebsiteUse: 'O grupo está sendo usado por outro site e não pode ser excluído.'\nDecrypt: 'Descriptografar'\nErrAgentAccountBound: 'Conta vinculada a agente'\nErrAgentAccountUnavailable: 'Conexão da conta indisponível: {{ .err }}'\nErrAgentProviderNotSupported: 'Provedor de agente não suportado'\nErrAgentAccountRequired: 'Selecione uma conta de agente primeiro'\nErrAgentAccountNotVerified: 'Conta de agente não verificada'\nErrAgentAccountModelsRequired: 'Configure pelo menos um modelo'\nErrAgentAccountSingleInitialModel: 'Apenas um modelo inicial pode ser configurado ao criar a conta'\nErrAgentModelNotInAccount: 'O modelo não está configurado na conta selecionada'\nErrAgentModelInUse: 'Ainda há um agente vinculado usando este modelo: {{ .name }}'\nErrAgentBaseURLRequired: 'URL base obrigatória'\nErrAgentApiKeyRequired: 'Chave API obrigatória'\nErrAgentComposeRequired: 'Conteúdo de compose obrigatório'\nErrAgentIDRequired: 'ID do agente obrigatório'\nErrAgentAccountIDRequired: 'ID da conta obrigatório'\nErrAgentLimitReached: 'A edição Community suporta até {{ .max }} agentes de IA. Atualize para a edição Professional para remover o limite'\nLocalhost: 'Máquina Local'\nErrBackupInUsed: 'Conta de backup em uso por tarefa'\nErrBackupCheck: 'Teste de conexão falhou: {{ .err }}'\nErrBackupLocalDelete: 'Não é permitido excluir contas locais'\nErrBackupLocalCreate: 'Não é permitido criar contas locais'\nErrPortInUsed: 'A porta {{ .detail }} já está ocupada'\nErrAppLimit: 'O número de aplicativos instalados excedeu o limite'\nErrNotInstall: 'Aplicativo não instalado'\nErrPortInOtherApp: 'A porta {{ .port }} já está ocupada pelo aplicativo {{ .apps }}'\nErrDbUserNotValid: 'Banco de dados existente, nome de usuário e senha não correspondem'\nErrUpdateBuWebsite: 'O aplicativo foi atualizado com sucesso, mas a modificação do arquivo de configuração do site falhou., verifique a configuração'\nErr1PanelNetworkFailed: 'Falha na criação da rede de contêiner padrão {{ .detalhe }}'\nErrFileParse: 'Falha na análise do arquivo docker-compose do aplicativo'\nErrInstallDirNotFound: 'O diretório de instalação não existe. Se precisar desinstalar, selecione Forçar desinstalação'\nAppStoreIsUpToDate: 'A loja de aplicativos já é a versão mais recente'\nLocalAppVersionNull: 'O aplicativo {{ .name }} não está sincronizado com a versão Não é possível adicionar à lista de aplicativos'\nLocalAppVersionErr: '{{ .name }} sincronização da versão {{ .version }} falhou {{ .err }}'\nErrFileNotFound: 'arquivo {{ .name }} não existe'\nErrFileParseApp: 'Falha na análise do arquivo {{ .name }} {{ .err }}'\nErrAppDirNull: 'A pasta da versão não existe'\nLocalAppErr: 'Falha na sincronização do aplicativo {{ .name }} {{ .err }}'\nErrContainerName: 'O nome do contêiner já existe'\nErrCreateHttpClient: 'Falha ao criar solicitação {{ .err }}'\nErrHttpReqTimeOut: 'Tempo limite da solicitação esgotado {{ .err }}'\nErrHttpReqFailed: 'Falha na solicitação {{ .err }}'\nErrNoSuchHost: 'Não foi possível encontrar o servidor solicitado {{ .err }}'\nErrHttpReqNotFound: 'O recurso solicitado {{ .err }} não pôde ser encontrado'\nErrContainerNotFound: '{{ .name }} container não existe'\nErrContainerMsg: 'O contêiner {{ .name }} está anormal., verifique o log na página do contêiner para obter detalhes.'\nErrAppBackup: '{{ .name }} falha no backup do aplicativo {{ .err }}'\nErrVersionTooLow: 'A versão atual do 1Panel é muito antiga para atualizar a App Store., atualize a versão antes de operar.'\nErrAppNameExist: 'O nome do aplicativo já existe'\nAppStoreIsSyncing: 'A App Store está sincronizando mais tarde'\nErrGetCompose: 'Falha ao obter o arquivo docker-compose.yml {{ .detail }}'\nErrAppWarn: 'Status anormal, verifique o log'\nErrAppParamKey: 'O campo de parâmetro {{ .name }} está anormal'\nErrAppUpgrade: 'Falha na atualização do aplicativo {{ .name }} {{ .err }}'\nAppRecover: 'Reverter aplicativo {{ .name }}'\nPullImageStart: 'Comece a extrair a imagem [{{ .name }}]'\nPullImageSuccess: 'Imagem retirada com sucesso'\nAppStoreSyncSuccess: 'Sincronização da App Store bem-sucedida'\nSyncAppDetail: 'Sincronizar configuração do aplicativo'\nAppVersionNotMatch: 'o aplicativo {{ .name }} requer uma versão superior do 1Panel, ignorando a sincronização'\nMoveSiteDir: 'Detecção de alteração no diretório do site. É necessário migrar o diretório do site OpenResty para {{ .name }}'\nMoveSiteDirSuccess: 'Migração bem-sucedida do diretório do site'\nDeleteRuntimePHP: 'Excluir tempo de execução do PHP'\nCustomAppStoreFileValid: 'Os pacotes da App Store precisam estar no formato.tar.gz'\nPullImageTimeout: 'Tempo limite para puxar imagem, tente aumentar a aceleração da imagem ou altere para outra aceleração de imagem'\nErrAppIsDown: 'O status do aplicativo {{ .name }} é anormal, verifique'\nErrCustomApps: 'Há um aplicativo instalado, desinstale-o primeiro'\nErrCustomRuntimes: 'Há um ambiente de execução instalado, exclua-o primeiro'\nErrAppVersionDeprecated: 'O aplicativo {{ .name }} não é compatível com a versão atual do 1Panel, ignorado'\nErrDockerFailed: 'O estado do Docker está anormal, verifique o status do serviço'\nErrDockerComposeCmdNotFound: 'O comando Docker Compose não existe, instale este comando na máquina host primeiro'\nUseExistImage: 'A imagem já existe, usando imagem existente'\nErrDatabaseNotFound: 'O banco de dados {{ .name }} não existe'\nExportIP: 'IP de Login'\nExportArea: 'Localização'\nExportPort: 'Porta'\nExportAuthMode: 'Método de Login'\nExportUser: 'Usuário'\nExportStatus: 'Status de Login'\nExportDate: 'Hora'\nErrFileCanNotRead: 'Este arquivo não suporta visualização'\nErrFileToLarge: 'O arquivo é maior que 10M e não pode ser aberto'\nErrPathNotFound: 'O diretório não existe'\nErrMovePathFailed: 'O caminho de destino não pode conter o caminho original'\nErrLinkPathNotFound: 'O caminho de destino não existe'\nErrFileIsExist: 'O arquivo ou pasta já existe'\nErrFileUpload: '{{ .name }} falhou ao carregar o arquivo {{ .detail }}'\nErrFileDownloadDir: 'A pasta de download não é suportada'\nErrCmdNotFound: 'O comando {{ .name }} não existe, instale este comando no host primeiro'\nErrSourcePathNotFound: 'O diretório de origem não existe'\nErrFavoriteExist: 'Este caminho já foi favorito'\nErrInvalidChar: 'Caracteres ilegais não são permitidos'\nErrPathNotDelete: 'O diretório selecionado não pode ser excluído'\nErrLogFileToLarge: 'Fail log melebihi 500MB, tidak boleh dibuka'\nFileDropFailed: 'Falha ao limpar arquivo {{ .name }}: {{ .err }}'\nFileDropSuccess: 'Arquivo {{ .name }} limpo com sucesso, {{ .count }} arquivos removidos, {{ .size }} de espaço em disco liberado'\nFileDropSum: 'Limpeza de arquivos concluída com sucesso, total de {{ .count }} arquivos removidos, total de {{ .size }} de espaço em disco liberado'\nErrBadDecrypt: 'Erro de senha de descriptografia do pacote compactado, descompactação falhou, verifique e'\nErrAliasIsExist: 'Alias já existe'\nErrBackupMatch: 'O arquivo de backup não corresponde a alguns dados atuais do site {{ .detail }}'\nErrBackupExist: 'A parte correspondente dos dados de origem no arquivo de backup não existe {{ .detail }}'\nErrPHPResource: 'O ambiente operacional local não suporta comutação'\nErrPathPermission: 'Uma pasta com permissões diferentes de 1000:1000 foi detectada no diretório de índice, o que pode causar um erro de acesso negado no site. Clique no botão Salvar acima'\nErrDomainIsUsed: 'O nome de domínio já está sendo usado pelo site [{{ .name }}]'\nErrDomainFormat: 'o formato do nome de domínio {{ .name }} está incorreto'\nErrDefaultAlias: 'padrão é um código reservado, use outro código'\nErrParentWebsite: 'Você precisa excluir o subsite {{ .name }} primeiro'\nErrBuildDirNotFound: 'O diretório de compilação não existe'\nErrImageNotExist: 'A imagem do ambiente operacional {{ .name }} não existe, edite novamente o ambiente operacional'\nErrProxyIsUsed: 'Balanceamento de carga foi usado por proxy reverso, não pode ser excluído'\nErrSSLValid: 'O arquivo do certificado está anormal, verifique o status do certificado'\nErrWebsiteDir: ', selecione um diretório dentro do diretório do site.'\nErrComposerFileNotFound: 'O arquivo composer.json não existe'\nErrRuntimeNoPort: 'O ambiente de tempo de execução não está configurado com uma porta, edite o ambiente de tempo de execução primeiro.'\nStatus: 'Status'\nstart: 'Iniciar'\nstop: 'Parar'\nrestart: 'Reiniciar'\nkill: 'Parar Forçadamente'\npause: 'Pausar'\nunpause: 'Retomar'\nremove: 'Excluir'\ndelete: 'Excluir'\nErrDefaultWebsite: 'O site padrão já foi definido, cancele-o antes de definir'\nSSL: 'Certificado'\nSet: 'Configurações'\nErrSSLCannotDelete: 'O certificado {{ .name }} está sendo usado por um site e não pode ser excluído'\nErrAccountCannotDelete: 'A conta está associada a um certificado e não pode ser excluída'\nErrSSLApply: 'Renovação do certificado bem-sucedida, falha na recarga do Openresty, verifique a configuração'\nErrEmailIsExist: 'A caixa de correio já existe'\nErrSSLKeyNotFound: 'O arquivo de chave privada não existe'\nErrSSLCertificateNotFound: 'O arquivo de certificado não existe'\nErrSSLKeyFormat: 'Falha na verificação do arquivo de chave privada'\nErrSSLCertificateFormat: 'O formato do arquivo de certificado está incorreto, use o formato pem'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid ou EabHmacKey não podem estar em branco'\nErrOpenrestyNotFound: 'O modo HTTP requer que o Openresty seja instalado primeiro'\nApplySSLStart: 'Comece a solicitar um certificado, nome de domínio [{{ .domain }}] método de solicitação [{{ .type }}]'\ndnsAccount: 'DNS Automático'\ndnsManual: 'Manual DNS'\nhttp: 'HTTP'\nApplySSLFailed: 'Falha na solicitação de certificado [{{ .domain }}], {{ .detail }}'\nApplySSLSuccess: 'Solicitação de certificado [{{ .domain }}] realizada com sucesso'\nDNSAccountName: 'Conta DNS [{{ .name }}] fornecedor [{{ .type }}]'\nPushDirLog: 'Certificado enviado para o diretório [{{ .path }}] {{ .status }}'\nErrDeleteCAWithSSL: 'A organização atual tem um certificado que foi emitido e não pode ser excluído.'\nErrDeleteWithPanelSSL: 'A configuração SSL do painel usa este certificado e não pode ser excluída'\nErrDefaultCA: 'A autoridade padrão não pode ser excluída'\nApplyWebSiteSSLLog: 'Iniciando a renovação do certificado do site {{ .name }}'\nErrUpdateWebsiteSSL: 'Falha na atualização do certificado do site {{ .name }}: {{ .err }}'\nApplyWebSiteSSLSuccess: 'Atualizar certificado do site com sucesso'\nErrExecShell: 'Falha ao executar o script {{ .err }}'\nExecShellStart: 'Iniciar execução do script'\nExecShellSuccess: 'Execução do script bem-sucedida'\nStartUpdateSystemSSL: 'Iniciar atualização do certificado do sistema'\nUpdateSystemSSLSuccess: 'Atualizar certificado do sistema com sucesso'\nErrWildcardDomain: 'Não é possível solicitar certificado de nome de domínio curinga no modo HTTP'\nErrApplySSLCanNotDelete: 'O certificado {{ .name }} que está sendo solicitado não pode ser excluído mais tarde.'\nStartPushSSLToNode: 'Iniciando o envio do certificado para o nó'\nPushSSLToNodeFailed: 'Falha ao enviar o certificado para o nó: {{ .err }}'\nPushSSLToNodeSuccess: 'Certificado enviado com sucesso para o nó'\nErrUserIsExist: 'O usuário atual já existe, digite novamente'\nErrDatabaseIsExist: 'O banco de dados atual já existe, digite novamente'\nErrExecTimeOut: 'Tempo limite de execução do SQL expirou, verifique o banco de dados'\nErrRemoteExist: 'O banco de dados remoto já existe com este nome, modifique-o e'\nErrLocalExist: 'O nome já existe no banco de dados local, modifique-o e'\nRemoteBackup: 'Para fazer backup do banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }} aguarde...'\nRemoteRecover: 'Para restaurar o banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }} aguarde...'\nArg: 'Argumento'\nErrTypeOfRedis: 'O tipo de arquivo de recuperação não corresponde ao método de persistência atual, modifique-o e'\nErrInUsed: '{{ .detail }} está em uso e não pode ser excluído'\nErrObjectInUsed: 'Este objeto está em uso e não pode ser excluído'\nErrObjectBeDependent: 'Esta imagem é dependida por outras imagens e não pode ser excluída'\nErrPortRules: 'O número da porta não corresponde, digite novamente'\nErrPgImagePull: 'Tempo limite para extração de imagem. Configure a aceleração de imagem ou extraia manualmente a imagem {{ .name }} e'\nPruneHelper: 'Esta limpeza {{ .name }} liberou espaço em disco {{ .size }}'\nImageRemoveHelper: 'Excluída a imagem {{ .name }}, liberando {{ .size }} de espaço em disco'\nBuildCache: 'Cache de construção'\nVolume: 'Volume de armazenamento'\nNetwork: 'Rede'\nPruneStart: 'Limpando em andamento aguarde...'\nContainerBackupPrepare: 'Preparar área de backup do contêiner'\nContainerBackupStop: 'Parar contêiner antes do backup'\nContainerBackupInspect: 'Fazer backup do inspect e rede do contêiner'\nContainerBackupMounts: 'Fazer backup dos mounts do contêiner'\nContainerBackupMeta: 'Escrever metadados do backup do contêiner'\nContainerBackupStart: 'Iniciar contêiner após o backup'\nContainerRecoverPrepare: 'Preparar área de restauração do contêiner'\nContainerRecoverExtract: 'Extrair arquivo de backup do contêiner'\nContainerRecoverParse: 'Carregar metadados do backup do contêiner'\nContainerRecoverCreate: 'Recriar contêiner'\nContainerRecoverMounts: 'Restaurar mounts do contêiner'\nContainerRecoverStart: 'Iniciar contêiner restaurado'\nContainerRecoverCleanup: 'Limpar área de restauração do contêiner'\nComposeBackupPrepare: 'Preparar área de backup do compose'\nComposeBackupStop: 'Parar compose antes do backup'\nComposeBackupFiles: 'Fazer backup dos arquivos do compose'\nComposeBackupContainers: 'Fazer backup dos contêineres do compose'\nComposeBackupMeta: 'Escrever metadados do backup do compose'\nComposeBackupStart: 'Iniciar compose após o backup'\nComposeBackupCleanup: 'Limpar área de backup do compose'\nComposeRecoverPrepare: 'Preparar área de restauração do compose'\nComposeRecoverExtract: 'Extrair arquivo de backup do compose'\nComposeRecoverMeta: 'Carregar metadados do backup do compose'\nComposeRecoverMetaLogName: 'Metadados da restauracao do compose: nome={{ .name }}'\nComposeRecoverMetaLogPath: 'Metadados da restauracao do compose: caminhoBackup={{ .backupPath }} caminhoDestino={{ .targetDir }}'\nComposeRecoverMetaLogCount: 'Metadados da restauracao do compose: arquivos={{ .files }} containers={{ .containers }}'\nComposeRecoverFiles: 'Restaurar arquivos do compose'\nComposeRecoverContainers: 'Restaurar contêineres do compose'\nComposeRecoverRecord: 'Salvar registro do compose'\nComposeRecoverCleanup: 'Limpar área de restauração do compose'\nErrFileNotExist: 'O arquivo {{ .detail }} não existe Verifique a integridade do arquivo de origem'\nErrImageBuildErr: 'Falha na criação da imagem'\nErrImageExist: 'A imagem já existe, modifique o nome da imagem.'\nErrDelWithWebsite: 'O ambiente operacional já está associado a um site e não pode ser excluído'\nErrRuntimeStart: 'Falha na inicialização'\nErrPackageJsonNotFound: 'o arquivo package.json não existe'\nErrScriptsNotFound: 'O item de configuração de scripts não foi encontrado em package.json'\nErrContainerNameNotFound: 'Não foi possível obter o nome do contêiner, verifique o arquivo.env'\nErrNodeModulesNotFound: 'A pasta node_modules não existe Edite o ambiente de execução ou aguarde até que o ambiente de execução inicie com sucesso'\nErrContainerNameIsNull: 'O nome do contêiner não existe'\nErrPHPPortIsDefault: 'Port 9000 adalah port lalai, sila ubah dan cuba lagi'\nErrPHPRuntimePortFailed: 'Port {{ .name }} telah digunakan oleh persekitaran runtime semasa, sila ubah dan cuba lagi'\nErrConfigNotFound: 'O arquivo de configuração não existe'\nErrConfigParse: 'O formato do arquivo de configuração está incorreto'\nErrConfigIsNull: 'O arquivo de configuração não pode estar vazio'\nErrConfigDirNotFound: 'O diretório de execução não existe'\nErrConfigAlreadyExist: 'Um arquivo de configuração com o mesmo nome já existe'\nErrUserFindErr: 'Falha na pesquisa do usuário {{ .name }} {{ .err }}'\nCutWebsiteLogSuccess: '{{ .name }} registro do site cortado com sucesso, caminho de backup {{ .path }}'\nHandleShell: 'Executar script {{ .name }}'\nHandleCurl: 'Acessar URL {{ .name }}'\nHandleNtpSync: 'Sincronização de hora do sistema'\nHandleSystemClean: 'Limpeza de cache do sistema'\nSystemLog: 'Log do Sistema'\nCutWebsiteLog: 'Rotacionar Log do Website'\nFileOrDir: 'Diretório / Arquivo'\nUploadFile: 'Enviando arquivo de backup {{ .file }} para {{ .backup }}'\nUpload: 'Enviar'\nIgnoreBackupErr: 'Backup falhou, erro: {{ .detail }}, ignorando este erro...'\nIgnoreUploadErr: 'Upload falhou, erro: {{ .detail }}, ignorando este erro...'\nLoadBackupFailed: 'Falha ao obter conexão da conta de backup, erro: {{ .detail }}'\nInExecuting: 'A tarefa atual está sendo executada, não repita a execução'\nNoSuchResource: 'Nenhum conteúdo de backup encontrado no banco de dados, ignorando...'\nCleanLog: 'Limpar log'\nCleanLogByName: 'Limpar log de {{ .name }}'\nWafIpGroupNotFound: 'Grupo de IP do WAF não encontrado., vá para [Recursos Avançados - WAF - Lista Branca/Preta - Grupo de IP] para adicionar um grupo de IP via método de download remoto.'\nErrNotExistUser: 'O usuário atual não existe, modifique e'\nErrBanAction: 'Falha na configuração. O serviço atual {{ .name }} não está disponível. Verifique e'\nErrClamdscanNotFound: 'O comando clamdscan não foi detectado, consulte a documentação para instalá-lo'\nTaskSwapSet: 'Configurar Swap'\nSetSwap: 'Configurar Swap {{ .path }} - {{ .size }}'\nCreateSwap: 'Criar Arquivo Swap'\nFormatSwap: 'Formatar Arquivo Swap'\nEnableSwap: 'Ativar Swap'\nCleanTamperSetting: 'Limpar configurações de proteção históricas'\nSetTamperWithRules: 'Iniciar proteção de diretório de acordo com as regras'\nProtectDir: 'Proteger diretório {{ .name }}'\nIgnoreFile: 'Proteger arquivo {{ .name }}'\nTamperSetSuccessful: 'Configurações de proteção concluídas, iniciando monitoramento dos seguintes diretórios:'\nErrScope: 'A modificação desta configuração não é suportada'\nErrStateChange: 'Falha na alteração de estado'\nErrRuleExist: 'A regra já existe'\nErrRuleNotExist: 'A regra não existe'\nErrParseIP: 'Formato de IP errado'\nErrDefaultIP: 'padrão é um nome reservado, altere-o para outro nome'\nErrGroupInUse: 'O grupo de IP é usado pela lista negra/lista branca e não pode ser excluído'\nErrIPGroupAclUse: 'O grupo de IP está sendo usado por regras personalizadas do site {{ .name }}, não pode ser excluído'\nErrGroupExist: 'O nome do grupo IP já existe'\nErrIPRange: 'Intervalo de IP errado'\nErrIPExist: 'IP já existe'\nurlDefense: 'regras de URL'\nurlHelper: 'URL proibido'\ndirFilter: 'Filtro de diretório'\nxss: 'XSS'\nphpExec: 'Execução de script PHP'\noneWordTrojan: 'Cavalo de Tróia de Uma Palavra'\nappFilter: 'Aplicar filtragem de diretório perigosa'\nwebShell: 'Webshell'\nargs: 'Regras de parâmetros'\nprotocolFilter: 'Filtragem de protocolo'\njavaFilter: 'Filtro de arquivo perigoso Java'\nscannerFilter: 'Filtro do scanner'\nescapeFilter: 'filtro de escape'\ncustomRule: 'Regra personalizada'\nhttpMethod: 'Filtragem de método HTTP'\nfileExt: 'Restrições de upload de arquivo'\ndefaultIpBlack: 'Grupo de IP malicioso'\ncookie: 'Regras de cookies'\nurlBlack: 'lista negra de URLs'\nuaBlack: 'Lista negra de agentes do usuário'\nattackCount: 'Limite de frequência de ataque'\nfileExtCheck: 'Restrições de upload de arquivo'\ngeoRestrict: 'Restrições de acesso regionais'\nunknownWebsite: 'Acesso não autorizado ao nome de domínio'\nnotFoundCount: 'Limite de taxa 404'\nheaderDefense: 'Regras de cabeçalho'\ndefaultUaBlack: 'Regras do agente do usuário'\nmethodWhite: 'regras HTTP'\ncaptcha: 'verificação homem-máquina'\nfiveSeconds: 'verificação de 5 segundos'\nvulnCheck: 'Regras suplementares'\nacl: 'Regras personalizadas'\nsql: 'injeção de SQL'\ncc: 'Limite de frequência de acesso'\ndefaultUrlBlack: 'regras de URL'\nsqlInject: 'injeção de SQL'\nErrDBNotExist: 'O banco de dados não existe'\nallow: 'permitir'\ndeny: 'negar'\nOpenrestyNotFound: 'Openresty não está instalado'\nremoteIpIsNull: 'A lista de IP está vazia'\nOpenrestyVersionErr: 'A versão do Openresty é muito baixa atualize o Openresty para 1.27.1.2-2-2-focal'\nErrFileTooLarge: 'O arquivo excede 1 MB e não pode ser enviado'\nTaskStart: '{{ .name }} A tarefa inicia [START]'\nTaskEnd: '{{ .name }} Tarefa concluída [CONCLUÍDA]'\nTaskFailed: '{{ .name }} tarefa falhou'\nTaskTimeout: '{{ .name }} expirou'\nTaskSuccess: '{{ .name }} Tarefa bem-sucedida'\nTaskRetry: 'Iniciar {{ .name }}ª nova tentativa'\nSubTaskSuccess: '{{ .name }} bem-sucedido'\nSubTaskFailed: '{{ .name }} falhou: {{ .err }}'\nTaskInstall: 'Instalar'\nTaskUninstall: 'Desinstalar'\nTaskCreate: 'Criar'\nTaskDelete: 'Excluir'\nTaskUpgrade: 'Atualizar'\nTaskUpdate: 'Atualizar'\nTaskRestart: 'Reiniciar'\nTaskProtect: 'Proteger'\nTaskBackup: 'Backup'\nTaskRecover: 'Recuperar'\nTaskRollback: 'Reverter'\nTaskPull: 'Puxar'\nTaskCommit: 'Commit'\nTaskBuild: 'Construir'\nTaskPush: 'Empurrar'\nTaskClean: 'Limpeza'\nTaskHandle: 'Executar'\nTaskImport: 'Importar'\nTaskExport: 'Exportar'\nWebsite: 'Site'\nApp: 'Aplicativo'\nRuntime: 'Ambiente de tempo de execução'\nDatabase: 'Banco de dados'\nConfigFTP: 'Criar usuário FTP {{ .name }}'\nConfigOpenresty: 'Criar arquivo de configuração Openresty'\nInstallAppSuccess: 'Aplicativo {{ .name }} instalado com sucesso'\nConfigRuntime: 'Configurar o ambiente de tempo de execução'\nConfigApp: 'Aplicativo de configuração'\nSuccessStatus: '{{ .name }} bem-sucedido'\nFailedStatus: '{{ .name }} falhou {{ .err }}'\nHandleLink: 'Manipular associação de aplicativos'\nHandleDatabaseApp: 'Manipulando parâmetros do aplicativo'\nExecShell: 'Executar script {{ .name }}'\nPullImage: 'Puxar imagem'\nStart: 'Iniciar'\nRun: 'Iniciar'\nStop: 'Pare'\nImage: 'Espelho'\nCompose: 'Orquestração'\nContainer: 'Recipiente'\nAppLink: 'Aplicativo vinculado'\nEnableSSL: 'Habilitar HTTPS'\nAppStore: 'Loja de aplicativos'\nTaskSync: 'Sincronizar'\nLocalApp: 'Aplicativo local'\nSubTask: 'Subtarefa'\nRuntimeExtension: 'Extensão do ambiente de tempo de execução'\nTaskIsExecuting: 'A tarefa está em execução'\nCustomAppstore: 'Armazém de aplicativos personalizados'\nTaskExec: 'Executar'\nTaskBatch: 'Operação em Lote'\nFileConvert: 'Conversão de Formato de Arquivo'\nClamscan: 'Escanear {{ .name }}'\nTaskScan: 'Escanear'\nOllamaModelPull: 'Puxar modelo Ollama {{ .name }}'\nOllamaModelSize: 'Obtenha o tamanho do modelo Ollama {{ .name }}'\nSnapshot: 'Instantâneo'\nSnapDBInfo: 'Escrever informações do banco de dados 1Panel'\nSnapCopy: 'Copiar arquivos e diretórios {{ .name }}'\nSnapNewDB: 'Inicializar conexão com o banco de dados {{ .name }}'\nSnapDeleteOperationLog: 'Excluir log de operação'\nSnapDeleteLoginLog: 'Excluir log de acesso'\nSnapDeleteMonitor: 'Excluir dados de monitoramento'\nSnapRemoveSystemIP: 'Remover IP do sistema'\nSnapBaseInfo: 'Escreva informações básicas do 1Panel'\nSnapInstallAppImageEmpty: 'Nenhuma imagem de aplicativo selecionada, pulando...'\nSnapInstallApp: 'Backup dos aplicativos instalados do 1Panel'\nSnapDockerSave: 'Compactar aplicativos instalados'\nSnapLocalBackup: 'Backup do diretório de backup local do 1Panel'\nSnapCompressBackup: 'Compactar diretório de backup local'\nSnapPanelData: 'Backup do diretório de dados do 1Panel'\nSnapCompressPanel: 'Diretório de dados compactados'\nSnapWebsite: 'Backup do diretório do site 1Panel'\nSnapCloseDBConn: 'Fechar a conexão com o banco de dados'\nSnapCompress: 'Criar arquivos de instantâneos'\nSnapCompressFile: 'Compactar arquivo de instantâneo'\nSnapCheckCompress: 'Verificar arquivo de compactação de instantâneo'\nSnapCompressSize: 'Tamanho do arquivo de instantâneo {{ .name }}'\nSnapUpload: 'Carregar arquivo de instantâneo'\nSnapUploadTo: 'Carregar arquivo de instantâneo para {{ .name }}'\nSnapUploadRes: 'Carregar arquivo de instantâneo para {{ .name }}'\nSnapshotRecover: 'Restauração de instantâneo'\nRecoverDownload: 'Baixar arquivo de instantâneo'\nDownload: 'Baixar'\nRecoverDownloadAccount: 'Obter conta de backup de download de instantâneo {{ .name }}'\nRecoverDecompress: 'Descompacte arquivos compactados de instantâneos'\nDecompress: 'Descompressão'\nBackupBeforeRecover: 'Fazer backup de dados relacionados ao sistema antes do snapshot'\nReadJson: 'Ler o arquivo Json no snapshot'\nReadJsonPath: 'Obtenha o caminho do arquivo Json no snapshot'\nReadJsonContent: 'Ler arquivo Json'\nReadJsonMarshal: 'Processamento de escape JSON'\nRecoverApp: 'Restaurar aplicativos instalados'\nRecoverWebsite: 'Recuperar diretório de sites'\nRecoverAppImage: 'Restaurar backup de imagem instantânea'\nRecoverCompose: 'Restaurar outro conteúdo do compositor'\nRecoverComposeList: 'Obter todos os compositores para serem restaurados'\nRecoverComposeItem: 'Recuperar composição {{ .name }}'\nRecoverAppEmpty: 'Nenhum backup de imagem de aplicativo foi encontrado no arquivo de instantâneo'\nRecoverBaseData: 'Recuperar dados e arquivos básicos'\nRecoverDaemonJsonEmpty: 'O arquivo de instantâneo e a máquina atual não têm o arquivo daemon.json de configuração do contêiner'\nRecoverDaemonJson: 'Restaurar arquivo daemon.json de configuração do contêiner'\nRecoverDBData: 'Recuperar dados do banco de dados'\nRecoverBackups: 'Restaurar diretório de backup local'\nRecoverPanelData: 'Diretório de dados de recuperação'\nContainerImagePull: 'Puxar imagem do contêiner {{ .name }}'\nContainerRemoveOld: 'Remover o contêiner original {{ .name }}'\nContainerImageCheck: 'Verifique se a imagem é extraída normalmente'\nContainerLoadInfo: 'Obter informações básicas do contêiner'\nContainerRecreate: 'A atualização do contêiner falhou, agora iniciando a restauração do contêiner original'\nContainerCreate: 'Criar um novo contêiner {{ .name }}'\nContainerCreateFailed: 'Falha na criação do contêiner, exclua o contêiner com falha'\nContainerStartCheck: 'Verifique se o contêiner foi iniciado'\nImageBuild: 'Construção de imagem'\nImagePull: 'Puxar imagem'\nImageRepoAuthFromDB: 'Obter informações de autenticação do repositório do banco de dados'\nImagePush: 'Enviar imagem'\nImageRenameTag: 'Modificar tag de imagem'\nImageNewTag: 'Nova tag de imagem {{ .name }}'\nComposeCreate: 'Criar uma composição'\nBackupNginxConfig: 'Fazer backup do arquivo de configuração do OpenResty do site'\nCompressFileSuccess: 'Diretório compactado com sucesso, compactado para {{ .name }}'\nCompressDir: 'Diretório de compressão'\nDeCompressFile: 'Descompacte o arquivo {{ .name }}'\nErrCheckValid: 'Falha na verificação do arquivo de backup, {{ .name }}'\nRollback: 'Reversão'\nwebsiteDir: 'Diretório de sites'\nRecoverFailedStartRollBack: 'Falha na recuperação, iniciar reversão'\nAppBackupFileIncomplete: 'O arquivo de backup está incompleto e não contém os arquivos app.json ou app.tar.gz'\nAppAttributesNotMatch: 'O tipo ou nome do aplicativo não corresponde'\nErrAlertSync: 'Erro de sincronização de informações de alarme, verifique e'\nErrInstallExtension: 'Já existe uma tarefa de instalação em andamento, aguarde a conclusão da tarefa'\nPanelAlertTitle: 'Notificação de Alerta do Painel'\nTestAlertTitle: 'E-mail de Teste - Verificar Conectividade de E-mail'\nTestAlert: 'Este é um e-mail de teste para verificar se sua configuração de envio de e-mails está correta.'\nLicenseExpirationAlert: 'Seu Painel {{ .node }}{{ .ip }}, a licença expirará em {{ .day }} dias. Faça login no painel para obter detalhes.'\nCronJobFailedAlert: 'Seu Painel {{ .node }}{{ .ip }}, a execução da tarefa agendada {{ .name }} falhou. Faça login no painel para obter detalhes.'\nClamAlert: 'Seu Painel {{ .node }}{{ .ip }}, a tarefa de verificação de vírus detectou {{ .num }} arquivos infectados. Faça login no painel para obter detalhes.'\nWebSiteAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} sites expirarão em {{ .day }} dias. Faça login no painel para obter detalhes.'\nSSLAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} certificados SSL expirarão em {{ .day }} dias. Faça login no painel para obter detalhes.'\nDiskUsedAlert: 'Seu Painel {{ .node }}{{ .ip }}, o uso do disco {{ .name }} atingiu {{ .used }}. Faça login no painel para obter detalhes.'\nResourceAlert: 'Seu Painel {{ .node }}{{ .ip }}, a taxa de uso médio do {{ .name }} nos últimos {{ .time }} minutos é {{ .used }}. Faça login no painel para obter detalhes.'\nPanelVersionAlert: 'Seu Painel {{ .node }}{{ .ip }}, a versão mais recente do painel está disponível para atualização. Faça login no painel para obter detalhes.'\nPanelPwdExpirationAlert: 'Seu Painel {{ .node }}{{ .ip }}, a senha do painel expirará em {{ .day }} dias. Faça login no painel para obter detalhes.'\nCommonAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .msg }}. Faça login no painel para obter detalhes.'\nNodeExceptionAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} nós estão anormais. Faça login no painel para obter detalhes.'\nLicenseExceptionAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} licenças estão anormais. Faça login no painel para obter detalhes.'\nSSHAndPanelLoginAlert: 'Seu Painel {{ .node }}{{ .ip }}, o login {{ .name }} a partir de {{ .loginIp }} é anormal. Faça login no painel para obter detalhes.'\nDeviceNotFound: 'Dispositivo {{ .name }} não encontrado'\nDeviceIsMounted: 'Dispositivo {{ .name }} está montado desmonte primeiro'\nPartitionDiskErr: 'Falha ao particionar, {{ .err }}'\nFormatDiskErr: 'Falha ao formatar disco, {{ .err }}'\nMountDiskErr: 'Falha ao montar disco, {{ .err }}'\nXfsNotFound: 'Sistema de arquivos xfs não detectado instale xfsprogs primeiro'\n"
  },
  {
    "path": "agent/i18n/lang/ru.yaml",
    "content": "ErrInvalidParams: 'Неверный запрос: {{ .detail }}'\nErrTokenParse: 'Ошибка токена: {{ .detail }}'\nErrInitialPassword: 'Исходный пароль неверен'\nErrInternalServer: 'Внутренняя ошибка: {{ .detail }}'\nErrRecordExist: 'Запись уже существует'\nErrRecordNotFound: 'Запись не найдена'\nErrStructTransform: 'Ошибка преобразования: {{ .err }}'\nErrNotLogin: 'Не авторизован: {{ .detail }}'\nErrPasswordExpired: 'Пароль истёк: {{ .detail }}'\nErrNotSupportType: 'Тип не поддерживается: {{ .name }}'\nErrProxy: 'Запрос не удался: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API отключено: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Неверный API-ключ: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP не в списке: {{ .detail }}'\nErrApiConfigDisable: 'Доступ по API запрещён: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Неверная метка времени: {{ .detail }}'\nErrShutDown: 'Команда остановлена вручную'\nErrMinQuickJump: 'Добавьте хотя бы одну быструю ссылку'\nErrMaxQuickJump: 'Не более 4 быстрых ссылок'\nErrUsernameIsExist: 'Имя пользователя занято'\nErrNameIsExist: 'Имя уже занято'\nErrDemoEnvironment: 'Недоступно в демо'\nErrCmdTimeout: 'Команда завершилась по тайм-ауту'\nErrCmdIllegal: 'Команда содержит недопустимые символы'\nErrPortExist: 'Порт {{ .port }} занят {{ .type }} [{{ .name }}]'\nTYPE_APP: 'Приложение'\nTYPE_RUNTIME: 'Среда выполнения'\nTYPE_DOMAIN: 'Доменное имя'\nErrTypePort: 'Формат порта {{ .name }} неверен'\nErrTypePortRange: 'Диапазон портов должен быть в пределах 1-65535'\nSuccess: 'Успех'\nFailed: 'Не удалось'\nSystemRestart: 'Задача прервана из-за перезапуска системы'\nErrGroupIsDefault: 'Группа по умолчанию, не может быть удалена'\nErrGroupIsInWebsiteUse: 'Группа используется другим веб-сайтом и не может быть удалена.'\nDecrypt: 'Расшифровать'\nErrAgentAccountBound: 'Акаунт привязан к агенту'\nErrAgentAccountUnavailable: 'Связь с аккаунтом недоступна: {{ .err }}'\nErrAgentProviderNotSupported: 'Провайдер агента не поддерживается'\nErrAgentAccountRequired: 'Выберите аккаунт агента'\nErrAgentAccountNotVerified: 'Аккаунт не подтверждён'\nErrAgentAccountModelsRequired: 'Настройте хотя бы одну модель'\nErrAgentAccountSingleInitialModel: 'При создании аккаунта можно указать только одну начальную модель'\nErrAgentModelNotInAccount: 'Модель не настроена в выбранном аккаунте'\nErrAgentModelInUse: 'Эта модель всё ещё используется агентом: {{ .name }}'\nErrAgentBaseURLRequired: 'Нужен базовый URL'\nErrAgentApiKeyRequired: 'Нужен API-ключ'\nErrAgentComposeRequired: 'Нужен compose'\nErrAgentIDRequired: 'Нужен ID агента'\nErrAgentAccountIDRequired: 'Нужен ID аккаунта'\nErrAgentLimitReached: 'Community Edition поддерживает до {{ .max }} AI-агентов. Перейдите на Professional Edition, чтобы снять ограничение'\nLocalhost: 'Локальная машина'\nErrBackupInUsed: 'Аккаунт бэкапа занят задачей'\nErrBackupCheck: 'Проверка подключения не удалась: {{ .err }}'\nErrBackupLocalDelete: 'Нельзя удалить локальные бэкапы'\nErrBackupLocalCreate: 'Нельзя создать локальные бэкапы'\nErrPortInUsed: '{{ .detail }} порт уже занят'\nErrAppLimit: 'Количество установленных приложений превысило лимит'\nErrNotInstall: 'Приложение не установлено'\nErrPortInOtherApp: 'Порт {{ .port }} уже занят приложением {{ .apps }}'\nErrDbUserNotValid: 'Существующая база данных, имя пользователя и пароль не совпадают'\nErrUpdateBuWebsite: 'Приложение успешно обновлено, но изменение файла конфигурации веб-сайта не удалось. Пожалуйста, проверьте конфигурацию'\nErr1PanelNetworkFailed: 'Создание сети контейнеров по умолчанию не удалось {{ .detail }}'\nErrFileParse: 'Ошибка анализа файла docker-compose приложения'\nErrInstallDirNotFound: 'Каталог установки не существует. Если вам необходимо удалить, выберите Принудительное удаление.'\nAppStoreIsUpToDate: 'В магазине приложений уже установлена последняя версия'\nLocalAppVersionNull: 'Приложение {{ .name }} не синхронизировано с версией Невозможно добавить в список приложений.'\nLocalAppVersionErr: 'Синхронизация версии {{ .version }} {{ .name }} не удалась {{ .err }}'\nErrFileNotFound: 'Файл {{ .name }} не существует'\nErrFileParseApp: '{{ .name }} анализ файла не удался {{ .err }}'\nErrAppDirNull: 'Папка версии не существует'\nLocalAppErr: 'Синхронизация приложения {{ .name }} не удалась {{ .err }}'\nErrContainerName: 'Имя контейнера уже существует'\nErrCreateHttpClient: 'Не удалось создать запрос {{ .err }}'\nErrHttpReqTimeOut: 'Истекло время ожидания запроса {{ .err }}'\nErrHttpReqFailed: 'Запрос не выполнен {{ .err }}'\nErrNoSuchHost: 'Не удалось найти запрошенный сервер {{ .err }}'\nErrHttpReqNotFound: 'Запрошенный ресурс {{ .err }} не найден'\nErrContainerNotFound: 'Контейнер {{ .name }} не существует'\nErrContainerMsg: 'Контейнер {{ .name }} ненормален. Подробности смотрите в журнале на странице контейнера.'\nErrAppBackup: '{{ .name }} резервное копирование приложения не удалось {{ .err }}'\nErrVersionTooLow: 'Текущая версия 1Panel слишком низкая для обновления App Store. Пожалуйста, обновите версию перед началом работы.'\nErrAppNameExist: 'Имя приложения уже существует'\nAppStoreIsSyncing: 'App Store синхронизируется, повторите попытку позже'\nErrGetCompose: 'Не удалось получить файл docker-compose.yml {{ .detail }}'\nErrAppWarn: 'Ненормальное состояние, проверьте журнал'\nErrAppParamKey: 'Поле параметра {{ .name }} ненормально'\nErrAppUpgrade: 'Обновление приложения {{ .name }} не удалось {{ .err }}'\nAppRecover: 'Откатить приложение {{ .name }}'\nPullImageStart: 'Начать извлечение изображения [{{ .name }}]'\nPullImageSuccess: 'Изображение извлечено успешно'\nAppStoreSyncSuccess: 'Синхронизация с App Store прошла успешно'\nSyncAppDetail: 'Синхронизировать конфигурацию приложения'\nAppVersionNotMatch: 'Приложению {{ .name }} требуется более высокая версия 1Panel, синхронизация пропускается'\nMoveSiteDir: 'Обнаружено изменение каталога сайта. Необходимо перенести каталог сайта OpenResty в {{ .name }}'\nMoveSiteDirSuccess: 'Успешная миграция каталога веб-сайта'\nDeleteRuntimePHP: 'Удалить среду выполнения PHP'\nCustomAppStoreFileValid: 'Пакеты магазина приложений должны быть в формате.tar.gz'\nPullImageTimeout: 'Истекло время ожидания извлечения изображения. Попробуйте увеличить ускорение изображения или выбрать другое ускорение изображения'\nErrAppIsDown: '{{ .name }} статус приложения ненормальный, проверьте'\nErrCustomApps: 'Установлено приложение. Сначала удалите его'\nErrCustomRuntimes: 'Установлена среда выполнения. Сначала удалите ее'\nErrAppVersionDeprecated: 'Приложение {{ .name }} несовместимо с текущей версией 1Panel, пропущено'\nErrDockerFailed: 'Состояние Docker аномально, проверьте состояние сервиса'\nErrDockerComposeCmdNotFound: 'Команда Docker Compose отсутствует, пожалуйста, установите эту команду на хост-машине сначала'\nUseExistImage: 'Образ уже существует, используется существующий образ'\nErrDatabaseNotFound: 'База данных {{ .name }} не существует'\nExportIP: 'IP входа'\nExportArea: 'Местоположение'\nExportPort: 'Порт'\nExportAuthMode: 'Способ входа'\nExportUser: 'Пользователь'\nExportStatus: 'Статус входа'\nExportDate: 'Время'\nErrFileCanNotRead: 'Этот файл не поддерживает предварительный просмотр'\nErrFileToLarge: 'Файл больше 10 МБ и не может быть открыт'\nErrPathNotFound: 'Каталог не существует'\nErrMovePathFailed: 'Целевой путь не может содержать исходный путь'\nErrLinkPathNotFound: 'Целевой путь не существует'\nErrFileIsExist: 'Файл или папка уже существует'\nErrFileUpload: '{{ .name }} не удалось загрузить файл {{ .detail }}'\nErrFileDownloadDir: 'Папка загрузки не поддерживается'\nErrCmdNotFound: 'Команда {{ .name }} не существует, сначала установите эту команду на хосте'\nErrSourcePathNotFound: 'Исходный каталог не существует'\nErrFavoriteExist: 'Этот путь уже добавлен в избранное'\nErrInvalidChar: 'Недопустимые символы не допускаются'\nErrPathNotDelete: 'Выбранный каталог не может быть удален'\nErrLogFileToLarge: 'Файл журнала превышает 500MB и не может быть открыт'\nFileDropFailed: 'Не удалось очистить файл {{ .name }}: {{ .err }}'\nFileDropSuccess: 'Файл {{ .name }} успешно очищен, удалено {{ .count }} файлов, освобождено {{ .size }} дискового пространства'\nFileDropSum: 'Очистка файлов завершена успешно, всего удалено {{ .count }} файлов, освобождено {{ .size }} дискового пространства'\nErrBadDecrypt: 'Ошибка пароля расшифровки сжатого пакета, распаковка не удалась, проверьте и повторите попытку'\nErrAliasIsExist: 'Псевдоним уже существует'\nErrBackupMatch: 'Файл резервной копии не соответствует некоторым текущим данным веб-сайта {{ .detail }}'\nErrBackupExist: 'Соответствующая часть исходных данных в файле резервной копии не существует {{ .detail }}'\nErrPHPResource: 'Локальная операционная среда не поддерживает переключение'\nErrPathPermission: 'В каталоге index обнаружена папка с разрешениями, отличными от 1000:1000, что может привести к ошибке Отказано в доступе на веб-сайте. Пожалуйста, нажмите кнопку Сохранить выше.'\nErrDomainIsUsed: 'Доменное имя уже используется веб-сайтом [{{ .name }}]'\nErrDomainFormat: 'Неверный формат доменного имени {{ .name }}'\nErrDefaultAlias: 'по умолчанию зарезервирован код, используйте другой код'\nErrParentWebsite: 'Сначала вам необходимо удалить дочерний сайт {{ .name }}'\nErrBuildDirNotFound: 'Каталог сборки не существует'\nErrImageNotExist: 'Образ операционной среды {{ .name }} не существует, пожалуйста, отредактируйте операционную среду заново'\nErrProxyIsUsed: 'Балансировка нагрузки используется обратным прокси, невозможно удалить'\nErrSSLValid: 'Файл сертификата аномален, проверьте статус сертификата'\nErrWebsiteDir: 'Пожалуйста, выберите директорию внутри директории сайта.'\nErrComposerFileNotFound: 'Файл composer.json не существует'\nErrRuntimeNoPort: 'Среда выполнения не настроена с портом, сначала отредактируйте среду выполнения.'\nStatus: 'Статус'\nstart: 'Запустить'\nstop: 'Остановить'\nrestart: 'Перезапустить'\nkill: 'Принудительно Остановить'\npause: 'Приостановить'\nunpause: 'Возобновить'\nremove: 'Удалить'\ndelete: 'Удалить'\nErrDefaultWebsite: 'Веб-сайт по умолчанию уже установлен, отмените его перед настройкой'\nSSL: 'Сертификат'\nSet: 'Настройки'\nErrSSLCannotDelete: 'Сертификат {{ .name }} используется веб-сайтом и не может быть удален'\nErrAccountCannotDelete: 'Учетная запись связана с сертификатом и не может быть удалена'\nErrSSLApply: 'Обновление сертификата прошло успешно, перезагрузка OpenResty не удалась, проверьте конфигурацию'\nErrEmailIsExist: 'Почтовый ящик уже существует'\nErrSSLKeyNotFound: 'Файл закрытого ключа не существует'\nErrSSLCertificateNotFound: 'Файл сертификата не существует'\nErrSSLKeyFormat: 'Проверка файла закрытого ключа не удалась'\nErrSSLCertificateFormat: 'Неверный формат файла сертификата, используйте формат pem'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid или EabHmacKey не могут быть пустыми'\nErrOpenrestyNotFound: 'Для режима HTTP необходимо сначала установить Openresty'\nApplySSLStart: 'Начать подачу заявки на сертификат, доменное имя [{{ .domain }}], метод подачи заявки [{{ .type }}]'\ndnsAccount: 'DNS Авто'\ndnsManual: 'Руководство по DNS'\nhttp: 'HTTP'\nApplySSLFailed: 'Заявка на сертификат [{{ .domain }}] не выполнена, {{ .detail }}'\nApplySSLSuccess: 'Заявка на сертификат [{{ .domain }}] успешно подана'\nDNSAccountName: 'DNS-аккаунт [{{ .name }}] поставщик [{{ .type }}]'\nPushDirLog: 'Сертификат перемещен в каталог [{{ .path }}] {{ .status }}'\nErrDeleteCAWithSSL: 'Текущая организация имеет сертификат, который был выдан и не может быть удален.'\nErrDeleteWithPanelSSL: 'Конфигурация SSL панели использует этот сертификат и не может быть удалена'\nErrDefaultCA: 'Невозможно удалить полномочия по умолчанию'\nApplyWebSiteSSLLog: 'Начинается обновление сертификата веб-сайта {{ .name }}'\nErrUpdateWebsiteSSL: 'Обновление сертификата веб-сайта {{ .name }} не удалось: {{ .err }}'\nApplyWebSiteSSLSuccess: 'Сертификат веб-сайта успешно обновлен'\nErrExecShell: 'Не удалось выполнить скрипт {{ .err }}'\nExecShellStart: 'Начать выполнение скрипта'\nExecShellSuccess: 'Скрипт выполнен успешно'\nStartUpdateSystemSSL: 'Начать обновление системного сертификата'\nUpdateSystemSSLSuccess: 'Сертификат системы обновлен успешно'\nErrWildcardDomain: 'Невозможно подать заявку на получение подстановочного сертификата доменного имени в режиме HTTP'\nErrApplySSLCanNotDelete: 'Нельзя удалить сертификат {{ .name }}, который находится в процессе выпуска. Пожалуйста, попробуйте позже.'\nStartPushSSLToNode: 'Начало отправки сертификата на узел'\nPushSSLToNodeFailed: 'Не удалось отправить сертификат на узел: {{ .err }}'\nPushSSLToNodeSuccess: 'Сертификат успешно отправлен на узел'\nErrUserIsExist: 'Текущий пользователь уже существует, пожалуйста, введите еще раз'\nErrDatabaseIsExist: 'Текущая база данных уже существует, пожалуйста, введите ее повторно'\nErrExecTimeOut: 'Время выполнения SQL истекло, проверьте базу данных'\nErrRemoteExist: 'Удаленная база данных с таким именем уже существует. Измените его и повторите попытку'\nErrLocalExist: 'Имя уже существует в локальной базе данных, измените его и повторите попытку'\nRemoteBackup: 'Для резервного копирования удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите...'\nRemoteRecover: 'Для восстановления удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите...'\nArg: 'Аргумент'\nErrTypeOfRedis: 'Тип файла восстановления не соответствует текущему методу сохранения. Измените его и повторите попытку'\nErrInUsed: '{{ .detail }} используется и не может быть удалено'\nErrObjectInUsed: 'Этот объект используется и не может быть удален'\nErrObjectBeDependent: 'Этот образ зависит от других образов и не может быть удален'\nErrPortRules: 'Номер порта не совпадает, введите заново'\nErrPgImagePull: 'Время извлечения изображения истекло. Настройте ускорение изображения или вручную извлеките изображение {{ .name }} и повторите попытку'\nPruneHelper: 'Эта очистка {{ .name }} освободила дисковое пространство {{ .size }}'\nImageRemoveHelper: 'Удалён образ {{ .name }}, освобождено {{ .size }} дискового пространства'\nBuildCache: 'Кэш сборки'\nVolume: 'Том хранилища'\nNetwork: 'Сеть'\nPruneStart: 'Очистка выполняется, пожалуйста, подождите...'\nContainerBackupPrepare: 'Подготовить рабочую область резервной копии контейнера'\nContainerBackupStop: 'Остановить контейнер перед резервным копированием'\nContainerBackupInspect: 'Сделать резервную копию inspect и сети контейнера'\nContainerBackupMounts: 'Сделать резервную копию точек монтирования контейнера'\nContainerBackupMeta: 'Записать метаданные резервной копии контейнера'\nContainerBackupStart: 'Запустить контейнер после резервного копирования'\nContainerRecoverPrepare: 'Подготовить рабочую область восстановления контейнера'\nContainerRecoverExtract: 'Распаковать архив резервной копии контейнера'\nContainerRecoverParse: 'Загрузить метаданные резервной копии контейнера'\nContainerRecoverCreate: 'Пересоздать контейнер'\nContainerRecoverMounts: 'Восстановить точки монтирования контейнера'\nContainerRecoverStart: 'Запустить восстановленный контейнер'\nContainerRecoverCleanup: 'Очистить рабочую область восстановления контейнера'\nComposeBackupPrepare: 'Подготовить рабочую область резервной копии compose'\nComposeBackupStop: 'Остановить compose перед резервным копированием'\nComposeBackupFiles: 'Сделать резервную копию файлов compose'\nComposeBackupContainers: 'Сделать резервную копию контейнеров compose'\nComposeBackupMeta: 'Записать метаданные резервной копии compose'\nComposeBackupStart: 'Запустить compose после резервного копирования'\nComposeBackupCleanup: 'Очистить рабочую область резервного копирования compose'\nComposeRecoverPrepare: 'Подготовить рабочую область восстановления compose'\nComposeRecoverExtract: 'Распаковать архив резервной копии compose'\nComposeRecoverMeta: 'Загрузить метаданные резервной копии compose'\nComposeRecoverMetaLogName: 'Метаданные восстановления compose: имя={{ .name }}'\nComposeRecoverMetaLogPath: 'Метаданные восстановления compose: путьБэкапа={{ .backupPath }} путьНазначения={{ .targetDir }}'\nComposeRecoverMetaLogCount: 'Метаданные восстановления compose: файлов={{ .files }} контейнеров={{ .containers }}'\nComposeRecoverFiles: 'Восстановить файлы compose'\nComposeRecoverContainers: 'Восстановить контейнеры compose'\nComposeRecoverRecord: 'Сохранить запись compose'\nComposeRecoverCleanup: 'Очистить рабочую область восстановления compose'\nErrFileNotExist: 'Файл {{ .detail }} не существует Проверьте целостность исходного файла'\nErrImageBuildErr: 'Сборка образа не удалась'\nErrImageExist: 'Изображение уже существует Пожалуйста, измените имя изображения.'\nErrDelWithWebsite: 'Операционная среда уже связана с веб-сайтом и не может быть удалена'\nErrRuntimeStart: 'Запуск не удался'\nErrPackageJsonNotFound: 'файл package.json не существует'\nErrScriptsNotFound: 'Элемент конфигурации скриптов не найден в package.json'\nErrContainerNameNotFound: 'Не удалось получить имя контейнера, проверьте файл.env'\nErrNodeModulesNotFound: 'Папка node_modules не существует Измените среду выполнения или дождитесь ее успешного запуска'\nErrContainerNameIsNull: 'Имя контейнера не существует'\nErrPHPPortIsDefault: 'A porta 9000 é a porta padrão, por favor, modifique e tente novamente'\nErrPHPRuntimePortFailed: 'A porta {{ .name }} já está sendo usada pelo ambiente de tempo de execução atual, por favor, modifique e tente novamente'\nErrConfigNotFound: 'Файл конфигурации не существует'\nErrConfigParse: 'Неверный формат файла конфигурации'\nErrConfigIsNull: 'Файл конфигурации не может быть пустым'\nErrConfigDirNotFound: 'Рабочий каталог не существует'\nErrConfigAlreadyExist: 'Файл конфигурации с таким именем уже существует'\nErrUserFindErr: 'Поиск пользователя {{ .name }} не удался {{ .err }}'\nCutWebsiteLogSuccess: 'Журнал веб-сайта {{ .name }} успешно вырезан, путь к резервной копии {{ .path }}'\nHandleShell: 'Выполнить скрипт {{ .name }}'\nHandleCurl: 'Доступ к URL {{ .name }}'\nHandleNtpSync: 'Синхронизация системного времени'\nHandleSystemClean: 'Очистка системного кэша'\nSystemLog: 'Системный лог'\nCutWebsiteLog: 'Ротация логов сайта'\nFileOrDir: 'Каталог / Файл'\nUploadFile: 'Загрузка файла резервной копии {{ .file }} в {{ .backup }}'\nUpload: 'Загрузить'\nIgnoreBackupErr: 'Ошибка резервного копирования: {{ .detail }}, игнорируем эту ошибку...'\nIgnoreUploadErr: 'Ошибка загрузки: {{ .detail }}, игнорируем эту ошибку...'\nLoadBackupFailed: 'Не удалось получить подключение к учетной записи резервной копии, ошибка: {{ .detail }}'\nInExecuting: 'Текущая задача выполняется, не повторяйте выполнение'\nNoSuchResource: 'В базе данных не найдено содержимое резервной копии, пропускаем...'\nCleanLog: 'Очистить журнал'\nCleanLogByName: 'Очистить журнал {{ .name }}'\nWafIpGroupNotFound: 'Группа IP WAF не найдена. Пожалуйста, перейдите в [Расширенные функции - WAF - Черный/Белый список - Группа IP], чтобы добавить группу IP с помощью метода удаленной загрузки.'\nErrNotExistUser: 'Текущий пользователь не существует, измените его и повторите попытку'\nErrBanAction: 'Настройка не удалась. Текущая служба {{ .name }} недоступна. Проверьте и повторите попытку'\nErrClamdscanNotFound: 'Команда clamdscan не обнаружена, обратитесь к документации, чтобы установить ее'\nTaskSwapSet: 'Настройка Swap'\nSetSwap: 'Настроить Swap {{ .path }} - {{ .size }}'\nCreateSwap: 'Создать Файл Swap'\nFormatSwap: 'Форматировать Файл Swap'\nEnableSwap: 'Включить Swap'\nCleanTamperSetting: 'Очистить исторические настройки защиты'\nSetTamperWithRules: 'Запустить защиту каталога согласно правилам'\nProtectDir: 'Защитить каталог {{ .name }}'\nIgnoreFile: 'Защитить файл {{ .name }}'\nTamperSetSuccessful: 'Настройки защиты завершены, начинаю мониторинг следующих каталогов:'\nErrScope: 'Изменение этой конфигурации не поддерживается'\nErrStateChange: 'He удалось изменить состояние'\nErrRuleExist: 'Правило уже существует'\nErrRuleNotExist: 'Правило не существует'\nErrParseIP: 'Неправильный формат IP'\nErrDefaultIP: 'по умолчанию это зарезервированное имя, пожалуйста, измените его на другое имя'\nErrGroupInUse: 'IP-группа используется черным/белым списком и не может быть удалена'\nErrIPGroupAclUse: 'Группа IP используется пользовательскими правилами сайта {{ .name }}, невозможно удалить'\nErrGroupExist: 'Имя группы IP уже существует'\nErrIPRange: 'Неверный диапазон IP-адресов'\nErrIPExist: 'IP-адрес уже существует'\nurlDefense: 'Правила URL'\nurlHelper: 'Запрещенный URL'\ndirFilter: 'Фильтр каталогов'\nxss: 'XSS'\nphpExec: 'Выполнение PHP-скрипта'\noneWordTrojan: 'Троян из одного слова'\nappFilter: 'Применить фильтрацию опасных каталогов'\nwebShell: 'Be6-оболочка'\nargs: 'Правила параметров'\nprotocolFilter: 'Фильтрация протоколов'\njavaFilter: 'Фильтр опасных файлов Java'\nscannerFilter: 'Фильтр сканера'\nescapeFilter: 'фильтр побега'\ncustomRule: 'Пользовательское правило'\nhttpMethod: 'Фильтрация метода HTTP'\nfileExt: 'Ограничения на загрузку файлов'\ndefaultIpBlack: 'Вредоносная группа IP'\ncookie: 'Правила использования файлов cookie'\nurlBlack: 'Черный список URL'\nuaBlack: 'Черный список User-Agent'\nattackCount: 'Ограничение частоты атак'\nfileExtCheck: 'Ограничения на загрузку файлов'\ngeoRestrict: 'Региональные ограничения доступа'\nunknownWebsite: 'Несанкционированный доступ к доменному имени'\nnotFoundCount: 'Ограничение скорости 404'\nheaderDefense: 'Правила заголовка'\ndefaultUaBlack: 'Правила User-Agent'\nmethodWhite: 'Правила HTTP'\ncaptcha: 'человеко-машинная верификация'\nfiveSeconds: '5 секунд проверки'\nvulnCheck: 'Дополнительные правила'\nacl: 'Пользовательские правила'\nsql: 'SQL-инъекция'\ncc: 'Ограничение частоты доступа'\ndefaultUrlBlack: 'Правила URL'\nsqlInject: 'SQL-инъекция'\nErrDBNotExist: 'База данных не существует'\nallow: 'разрешить'\ndeny: 'отрицать'\nOpenrestyNotFound: 'Openresty не установлен'\nremoteIpIsNull: 'Список IP пуст'\nOpenrestyVersionErr: 'Версия Openresty слишком низкая, пожалуйста, обновите Openresty до 1.27.1.2-2-2-focal'\nErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'\nTaskStart: '{{ .name }} Задача начинается [START]'\nTaskEnd: '{{ .name }} Задача выполнена [ЗАВЕРШЕНО]'\nTaskFailed: 'Задача {{ .name }} не выполнена'\nTaskTimeout: '{{ .name }} истекло время ожидания'\nTaskSuccess: '{{ .name }} Задача выполнена успешно'\nTaskRetry: 'Начать {{ .name }}-ю повторную попытку'\nSubTaskSuccess: '{{ .name }} выполнено успешно'\nSubTaskFailed: '{{ .name }} не удалось: {{ .err }}'\nTaskInstall: 'Установить'\nTaskUninstall: 'Удалить'\nTaskCreate: 'Создать'\nTaskDelete: 'Удалить'\nTaskUpgrade: 'Обновить'\nTaskUpdate: 'Обновить'\nTaskRestart: 'Перезапуск'\nTaskProtect: 'Защита'\nTaskBackup: 'Резервное копирование'\nTaskRecover: 'Восстановить'\nTaskRollback: 'Откат'\nTaskPull: 'Вытянуть'\nTaskCommit: 'Kоммит'\nTaskBuild: 'Сборка'\nTaskPush: 'Push'\nTaskClean: 'Очистка'\nTaskHandle: 'Выполнить'\nTaskImport: 'Импорт'\nTaskExport: 'Экспорт'\nWebsite: 'Be6-сайт'\nApp: 'Приложение'\nRuntime: 'Среда выполнения'\nDatabase: 'База данных'\nConfigFTP: 'Создать пользователя FTP {{ .name }}'\nConfigOpenresty: 'Создать файл конфигурации Openresty'\nInstallAppSuccess: 'Приложение {{ .name }} успешно установлено'\nConfigRuntime: 'Настройка среды выполнения'\nConfigApp: 'Приложение конфигурации'\nSuccessStatus: '{{ .name }} успешно'\nFailedStatus: '{{ .name }} не удалось {{ .err }}'\nHandleLink: 'Обработка ассоциации приложений'\nHandleDatabaseApp: 'Обработка параметров приложения'\nExecShell: 'Выполнить скрипт {{ .name }}'\nPullImage: 'Вытянуть изображение'\nStart: 'Старт'\nRun: 'Старт'\nStop: 'Стоп'\nImage: 'Зеркало'\nCompose: 'Оркестровка'\nContainer: 'Контейнер'\nAppLink: 'Связанное приложение'\nEnableSSL: 'Включить HTTPS'\nAppStore: 'Магазин приложений'\nTaskSync: 'Синхронизировать'\nLocalApp: 'Локальное приложение'\nSubTask: 'Подзадача'\nRuntimeExtension: 'Расширение среды выполнения'\nTaskIsExecuting: 'Задача выполняется'\nCustomAppstore: 'Хранилище пользовательских приложений'\nTaskExec: 'Выполнить'\nTaskBatch: 'Пакетная операция'\nFileConvert: 'Преобразование формата файла'\nClamscan: 'Сканировать {{ .name }}'\nTaskScan: 'Сканировать'\nOllamaModelPull: 'Вытянуть модель Ollama {{ .name }}'\nOllamaModelSize: 'Получить размер модели Ollama {{ .name }}'\nSnapshot: 'Снимок'\nSnapDBInfo: 'Запись информации о базе данных 1Panel'\nSnapCopy: 'Копировать файлы и каталоги {{ .name }}'\nSnapNewDB: 'Инициализировать соединение с базой данных {{ .name }}'\nSnapDeleteOperationLog: 'Удалить журнал операций'\nSnapDeleteLoginLog: 'Удалить журнал доступа'\nSnapDeleteMonitor: 'Удалить данные мониторинга'\nSnapRemoveSystemIP: 'Удалить системный IP'\nSnapBaseInfo: 'Записать основную информацию 1Panel'\nSnapInstallAppImageEmpty: 'Образы приложений не выбраны, пропускаем...'\nSnapInstallApp: 'Резервное копирование приложений, установленных в 1Panel'\nSnapDockerSave: 'Сжать установленные приложения'\nSnapLocalBackup: 'Резервное копирование локального каталога резервных копий 1Panel'\nSnapCompressBackup: 'Сжать локальный каталог резервных копий'\nSnapPanelData: 'Резервное копирование каталога данных 1Panel'\nSnapCompressPanel: 'Каталог сжатых данных'\nSnapWebsite: 'Резервное копирование каталога веб-сайта 1Panel'\nSnapCloseDBConn: 'Закрыть соединение с базой данных'\nSnapCompress: 'Создание файлов моментальных снимков'\nSnapCompressFile: 'Сжать файл моментального снимка'\nSnapCheckCompress: 'Проверить файл сжатия снимка'\nSnapCompressSize: 'Размер файла снимка {{ .name }}'\nSnapUpload: 'Загрузить файл снимка'\nSnapUploadTo: 'Загрузить файл снимка в {{ .name }}'\nSnapUploadRes: 'Загрузить файл снимка в {{ .name }}'\nSnapshotRecover: 'Восстановление моментального снимка'\nRecoverDownload: 'Загрузить файл снимка'\nDownload: 'Скачать'\nRecoverDownloadAccount: 'Получить резервную копию учетной записи для загрузки моментального снимка {{ .name }}'\nRecoverDecompress: 'Распаковка сжатых файлов моментальных снимков'\nDecompress: 'Декомпрессия'\nBackupBeforeRecover: 'Резервное копирование системных данных перед созданием снимка'\nReadJson: 'Прочитать файл JSON в снимке'\nReadJsonPath: 'Получить путь к файлу JSON в снимке'\nReadJsonContent: 'Прочитать файл Json'\nReadJsonMarshal: 'Обработка Json escape'\nRecoverApp: 'Восстановить установленные приложения'\nRecoverWebsite: 'Восстановить каталог веб-сайта'\nRecoverAppImage: 'Восстановить резервную копию образа снимка'\nRecoverCompose: 'Восстановить содержимое другого композитора'\nRecoverComposeList: 'Восстановить всех композиторов'\nRecoverComposeItem: 'Восстановить сообщение {{ .name }}'\nRecoverAppEmpty: 'В файле снимка не найдено резервной копии образа приложения'\nRecoverBaseData: 'Восстановить базовые данные и файлы'\nRecoverDaemonJsonEmpty: 'Ни файл снимка, ни текущая машина не имеют файла конфигурации контейнера daemon.json'\nRecoverDaemonJson: 'Восстановить файл конфигурации контейнера daemon.json'\nRecoverDBData: 'Восстановить данные базы данных'\nRecoverBackups: 'Восстановить локальный каталог резервных копий'\nRecoverPanelData: 'Каталог данных восстановления'\nContainerImagePull: 'Извлечь изображение контейнера {{ .name }}'\nContainerRemoveOld: 'Удалить исходный контейнер {{ .name }}'\nContainerImageCheck: 'Проверьте, нормально ли извлекается изображение'\nContainerLoadInfo: 'Получить основную информацию о контейнере'\nContainerRecreate: 'Обновление контейнера не удалось, сейчас начну восстанавливать исходный контейнер'\nContainerCreate: 'Создать новый контейнер {{ .name }}'\nContainerCreateFailed: 'Создание контейнера не удалось, удалите неудавшийся контейнер'\nContainerStartCheck: 'Проверить, запущен ли контейнер'\nImageBuild: 'Создание изображения'\nImagePull: 'Вытащить изображение'\nImageRepoAuthFromDB: 'Получить информацию об аутентификации репозитория из базы данных'\nImagePush: 'Отправить изображение'\nImageRenameTag: 'Изменить тег изображения'\nImageNewTag: 'Новый тег изображения {{ .name }}'\nComposeCreate: 'Создать композицию'\nBackupNginxConfig: 'Резервное копирование файла конфигурации OpenResty веб-сайта'\nCompressFileSuccess: 'Каталог успешно сжат, сжат в {{ .name }}'\nCompressDir: 'Каталог сжатия'\nDeCompressFile: 'Распаковать файл {{ .name }}'\nErrCheckValid: 'Проверка файла резервной копии не удалась, {{ .name }}'\nRollback: 'Откат'\nwebsiteDir: 'Каталог веб-сайтов'\nRecoverFailedStartRollBack: 'Восстановление не удалось, начать откат'\nAppBackupFileIncomplete: 'Файл резервной копии неполный и в нем отсутствуют файлы app.json или app.tar.gz'\nAppAttributesNotMatch: 'Тип или имя приложения не совпадают'\nErrAlertSync: 'Ошибка синхронизации информации о тревоге, проверьте и повторите попытку'\nErrInstallExtension: 'Уже выполняется задача установки, подождите, пока задача завершится'\nPanelAlertTitle: 'Уведомление панели'\nTestAlertTitle: 'Тестовое письмо - проверка подключения'\nTestAlert: 'Это тестовое письмо для проверки правильности настройки отправки писем.'\nLicenseExpirationAlert: 'Ваш панель {{ .node }}{{ .ip }}, лицензия истечет через {{ .day }} дней. Войдите в панель для получения деталей.'\nCronJobFailedAlert: 'Ваш панель {{ .node }}{{ .ip }}, выполнение запланированной задачи {{ .name }} не удалось. Войдите в панель для получения деталей.'\nClamAlert: 'Ваш панель {{ .node }}{{ .ip }}, задача сканирования на вирусы обнаружила {{ .num }} зараженных файлов. Войдите в панель для получения деталей.'\nWebSiteAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} веб-сайтов истечут через {{ .day }} дней. Войдите в панель для получения деталей.'\nSSLAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} сертификатов SSL истечут через {{ .day }} дней. Войдите в панель для получения деталей.'\nDiskUsedAlert: 'Ваш панель {{ .node }}{{ .ip }}, использование диска {{ .name }} достигло {{ .used }}. Войдите в панель для получения деталей.'\nResourceAlert: 'Ваш панель {{ .node }}{{ .ip }}, средний уровень использования {{ .name }} за {{ .time }} минут составляет {{ .used }}. Войдите в панель для получения деталей.'\nPanelVersionAlert: 'Ваш панель {{ .node }}{{ .ip }}, доступна последняя версия панели для обновления. Войдите в панель для получения деталей.'\nPanelPwdExpirationAlert: 'Ваш панель {{ .node }}{{ .ip }}, пароль панели истечет через {{ .day }} дней. Войдите в панель для получения деталей.'\nCommonAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .msg }}. Войдите в панель для получения деталей.'\nNodeExceptionAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} узлов работают с ошибками. Войдите в панель для получения деталей.'\nLicenseExceptionAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} лицензий имеют ошибки. Войдите в панель для получения деталей.'\nSSHAndPanelLoginAlert: 'Ваш панель {{ .node }}{{ .ip }}, вход {{ .name }} с адреса {{ .loginIp }} является аномальным. Войдите в панель для получения деталей.'\nDeviceNotFound: 'Устройство {{ .name }} не найдено'\nDeviceIsMounted: 'Устройство {{ .name }} подключено, сначала отключите'\nPartitionDiskErr: 'Не удалось разделить, {{ .err }}'\nFormatDiskErr: 'Не удалось отформатировать диск, {{ .err }}'\nMountDiskErr: 'Не удалось подключить диск, {{ .err }}'\nXfsNotFound: 'Файловая система xfs не обнаружена, сначала установите xfsprogs'\n"
  },
  {
    "path": "agent/i18n/lang/tr.yaml",
    "content": "ErrInvalidParams: 'İstek geçersiz: {{ .detail }}'\nErrTokenParse: 'Token hatası: {{ .detail }}'\nErrInitialPassword: 'Orijinal şifre yanlış'\nErrInternalServer: 'Sunucu hatası: {{ .detail }}'\nErrRecordExist: 'Kayıt zaten var'\nErrRecordNotFound: 'Kayıt bulunamadı'\nErrStructTransform: 'Dönüşüm hatası: {{ .err }}'\nErrNotLogin: 'Oturum açılmamış: {{ .detail }}'\nErrPasswordExpired: 'Şifre süresi doldu: {{ .detail }}'\nErrNotSupportType: 'Bu tür desteklenmiyor: {{ .name }}'\nErrProxy: 'İstek başarısız: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API kapalı: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Geçersiz API anahtarı: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP izinli değil: {{ .detail }}'\nErrApiConfigDisable: 'API erişimi kapalı: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Zaman damgası hatalı: {{ .detail }}'\nErrShutDown: 'Komut manuel durduruldu'\nErrMinQuickJump: 'En az bir hızlı erişim ekle'\nErrMaxQuickJump: '4 hızlı erişim hakkı var'\nErrUsernameIsExist: 'Kullanıcı adı mevcut'\nErrNameIsExist: 'İsim mevcut'\nErrDemoEnvironment: 'Demo modunda yok'\nErrCmdTimeout: 'Komut zaman aşımına uğradı'\nErrCmdIllegal: 'Komut geçersiz karakter içeriyor'\nErrPortExist: '{{ .port }} portu {{ .type }} [{{ .name }}] tarafından kullanılıyor'\nTYPE_APP: 'Uygulama'\nTYPE_RUNTIME: 'Çalışma ortamı'\nTYPE_DOMAIN: 'Alan Adı'\nErrTypePort: 'Port {{ .name }} formatı yanlış'\nErrTypePortRange: 'Port aralığının 1-65535 arasında olması gerekiyor'\nSuccess: 'Başarılı'\nFailed: 'Başarısız'\nSystemRestart: 'Sistem yeniden başlatması nedeniyle görev kesildi'\nErrGroupIsDefault: 'Varsayılan grup, silinemez'\nErrGroupIsInWebsiteUse: 'Grup başka bir web sitesi tarafından kullanılıyor ve silinemez.'\nDecrypt: 'Şifre Çöz'\nErrAgentAccountBound: 'Hesap bir ajana bağlı'\nErrAgentAccountUnavailable: 'Hesap bağlantısı yok: {{ .err }}'\nErrAgentProviderNotSupported: 'Ajans sağlayıcısı desteklenmiyor'\nErrAgentAccountRequired: 'Önce bir ajans hesabı seçin'\nErrAgentAccountNotVerified: 'Ajans hesabı doğrulanmadı'\nErrAgentAccountModelsRequired: 'Lütfen en az bir model yapılandırın'\nErrAgentAccountSingleInitialModel: 'Hesap oluştururken yalnızca bir başlangıç modeli yapılandırılabilir'\nErrAgentModelNotInAccount: 'Model seçilen hesapta yapılandırılmadı'\nErrAgentModelInUse: 'Bu modeli hâlâ kullanan bağlı bir ajan var: {{ .name }}'\nErrAgentBaseURLRequired: 'Temel URL gerekli'\nErrAgentApiKeyRequired: 'API anahtarı gerekli'\nErrAgentComposeRequired: 'Compose içeriği gerekli'\nErrAgentIDRequired: 'Ajans ID gerekli'\nErrAgentAccountIDRequired: 'Hesap ID gerekli'\nErrAgentLimitReached: 'Community sürümü en fazla {{ .max }} AI ajanını destekler. Sınırı kaldırmak için Professional sürüme yükseltin'\nLocalhost: 'Yerel Makine'\nErrBackupInUsed: 'Yedek hesabı görevde kullanılıyor'\nErrBackupCheck: 'Bağlantı testi başarısız: {{ .err }}'\nErrBackupLocalDelete: 'Yerel yedek silme yok'\nErrBackupLocalCreate: 'Yerel yedek oluşturma yok'\nErrPortInUsed: '{{ .detail }} portu zaten kullanılıyor'\nErrAppLimit: 'Yüklenen uygulama sayısı sınırı aştı'\nErrNotInstall: 'Uygulama yüklenmedi'\nErrPortInOtherApp: '{{ .port }} portu zaten {{ .apps }} uygulaması tarafından kullanılıyor'\nErrDbUserNotValid: 'Mevcut veritabanı, kullanıcı adı ve şifre eşleşmiyor'\nErrUpdateBuWebsite: 'Uygulama başarıyla güncellendi, ancak web sitesi yapılandırma dosyası değiştirme başarısız. Lütfen yapılandırmayı kontrol edin'\nErr1PanelNetworkFailed: 'Varsayılan konteyner ağ oluşturma başarısız {{ .detail }}'\nErrFileParse: 'Uygulama docker-compose dosya ayrıştırma başarısız'\nErrInstallDirNotFound: 'Kurulum dizini mevcut değil. Kaldırmanız gerekiyorsa, lütfen Zorla Kaldır seçeneğini seçin'\nAppStoreIsUpToDate: 'Uygulama mağazası zaten en son sürüm'\nLocalAppVersionNull: '{{ .name }} uygulaması sürüme senkronize edilmedi Uygulama listesine eklenemez'\nLocalAppVersionErr: '{{ .name }} senkronizasyon sürümü {{ .version }} başarısız {{ .err }}'\nErrFileNotFound: '{{ .name }} dosyası mevcut değil'\nErrFileParseApp: '{{ .name }} dosya ayrıştırma başarısız {{ .err }}'\nErrAppDirNull: 'Sürüm klasörü mevcut değil'\nLocalAppErr: 'Uygulama {{ .name }} senkronizasyonu başarısız {{ .err }}'\nErrContainerName: 'Konteyner adı zaten mevcut'\nErrCreateHttpClient: 'İstek oluşturma başarısız {{ .err }}'\nErrHttpReqTimeOut: 'İstek zaman aşımı {{ .err }}'\nErrHttpReqFailed: 'İstek başarısız {{ .err }}'\nErrNoSuchHost: 'İstenen sunucu bulunamıyor {{ .err }}'\nErrHttpReqNotFound: 'İstenen kaynak {{ .err }} bulunamadı'\nErrContainerNotFound: '{{ .name }} konteyneri mevcut değil'\nErrContainerMsg: '{{ .name }} konteyneri anormal. Lütfen ayrıntılar için konteyner sayfasındaki günlüğü kontrol edin'\nErrAppBackup: '{{ .name }} uygulama yedeklemesi başarısız {{ .err }}'\nErrVersionTooLow: 'Mevcut 1Panel sürümü Uygulama Mağazasını güncellemek için çok düşük. Lütfen işlemden önce sürümü yükseltin.'\nErrAppNameExist: 'Uygulama adı zaten mevcut'\nAppStoreIsSyncing: 'Uygulama Mağazası senkronize ediliyor, lütfen daha sonra tekrar deneyin'\nErrGetCompose: 'docker-compose.yml dosyası alınamadı {{ .detail }}'\nErrAppWarn: 'Anormal durum, lütfen günlüğü kontrol edin'\nErrAppParamKey: 'Parametre {{ .name }} alanı anormal'\nErrAppUpgrade: 'Uygulama {{ .name }} yükseltmesi başarısız {{ .err }}'\nAppRecover: 'Uygulama {{ .name }} geri alma'\nPullImageStart: 'Image [{{ .name }}] çekmeye başla'\nPullImageSuccess: 'Image çekme başarılı'\nAppStoreSyncSuccess: 'Uygulama Mağazası senkronizasyonu başarılı'\nSyncAppDetail: 'Uygulama yapılandırmasını senkronize et'\nAppVersionNotMatch: '{{ .name }} uygulaması daha yüksek 1Panel sürümü gerektiriyor, senkronizasyon atlanıyor'\nMoveSiteDir: 'Web sitesi dizini değişikliği tespit edildi. OpenResty web sitesi dizinini {{ .name }} adresine taşımak gerekiyor'\nMoveSiteDirSuccess: 'Web sitesi dizini taşıma başarılı'\nDeleteRuntimePHP: 'PHP çalışma zamanını sil'\nCustomAppStoreFileValid: 'Uygulama mağazası paketlerinin.tar.gz formatında olması gerekiyor'\nPullImageTimeout: 'Image çekme zaman aşımı, lütfen image hızlandırmayı artırmayı deneyin veya başka bir image hızlandırmaya geçin'\nErrAppIsDown: '{{ .name }} uygulama durumu anormal, lütfen kontrol edin'\nErrCustomApps: 'Yüklü bir uygulama var, lütfen önce kaldırın'\nErrCustomRuntimes: 'Yüklü bir çalışma ortamı var, lütfen önce silin'\nErrAppVersionDeprecated: '{{ .name }} uygulaması mevcut 1Panel sürümü ile uyumlu değil, atlandı'\nErrDockerFailed: 'Docker durumu anormal, lütfen servis durumunu kontrol edin'\nErrDockerComposeCmdNotFound: 'Docker Compose komutu mevcut değil, lütfen önce bu komutu host makinesine yükleyin'\nUseExistImage: 'Görüntü zaten mevcut, mevcut görüntü kullanılıyor'\nErrDatabaseNotFound: '{{ .name }} veritabanı mevcut değil'\nExportIP: 'Giriş IP'\nExportArea: 'Konum'\nExportPort: 'Port'\nExportAuthMode: 'Giriş Yöntemi'\nExportUser: 'Kullanıcı'\nExportStatus: 'Giriş Durumu'\nExportDate: 'Zaman'\nErrFileCanNotRead: 'Bu dosya önizlemeyi desteklemiyor'\nErrFileToLarge: 'Dosya 10M dan büyük ve açılamıyor'\nErrPathNotFound: 'Dizin mevcut değil'\nErrMovePathFailed: 'Hedef yol orijinal yolu içeremez'\nErrLinkPathNotFound: 'Hedef yol mevcut değil'\nErrFileIsExist: 'Dosya veya klasör zaten mevcut'\nErrFileUpload: '{{ .name }} dosya yükleme başarısız {{ .detail }}'\nErrFileDownloadDir: 'Klasör indirme desteklenmiyor'\nErrCmdNotFound: '{{ .name }} komutu mevcut değil, lütfen önce bu komutu host a yükleyin'\nErrSourcePathNotFound: 'Kaynak dizin mevcut değil'\nErrFavoriteExist: 'Bu yol zaten favorilere eklendi'\nErrInvalidChar: 'Yasak karakterlere izin verilmiyor'\nErrPathNotDelete: 'Seçilen dizin silinemez'\nErrLogFileToLarge: 'Günlük dosyası 500MB''ı aşıyor ve açılamıyor'\nFileDropFailed: '{{ .name }} dosyası temizlenemedi: {{ .err }}'\nFileDropSuccess: '{{ .name }} dosyası başarıyla temizlendi, {{ .count }} dosya silindi, {{ .size }} disk alanı boşaltıldı'\nFileDropSum: 'Dosya temizleme tamamlandı, toplam {{ .count }} dosya silindi, toplam {{ .size }} disk alanı boşaltıldı'\nErrBadDecrypt: 'Sıkıştırılmış paket şifre çözme parolası hatalı, açma başarısız, lütfen kontrol edip tekrar deneyin'\nErrAliasIsExist: 'Takma ad zaten mevcut'\nErrBackupMatch: 'Yedekleme dosyası mevcut web sitesi verilerinin bir kısmıyla eşleşmiyor {{ .detail }}'\nErrBackupExist: 'Yedekleme dosyasındaki kaynak verilerin karşılık gelen kısmı mevcut değil {{ .detail }}'\nErrPHPResource: 'Yerel işletim ortamı değiştirmeyi desteklemiyor'\nErrPathPermission: 'Dizin dizininde 1000:1000 olmayan izinlere sahip bir klasör tespit edildi, bu web sitesinde Erişim reddedildi hatasına neden olabilir. Lütfen yukarıdaki Kaydet düğmesine tıklayın'\nErrDomainIsUsed: 'Alan adı zaten [{{ .name }}] web sitesi tarafından kullanılıyor'\nErrDomainFormat: '{{ .name }} alan adı formatı yanlış'\nErrDefaultAlias: 'default ayrılmış bir kod, lütfen başka bir kod kullanın'\nErrParentWebsite: 'Önce {{ .name }} alt sitesini silmeniz gerekiyor'\nErrBuildDirNotFound: 'Yapı dizini mevcut değil'\nErrImageNotExist: 'İşletim ortamı {{ .name }} image mevcut değil, lütfen işletim ortamını yeniden düzenleyin'\nErrProxyIsUsed: 'Yük dengeleme ters proxy tarafından kullanıldı, silinemez'\nErrSSLValid: 'Sertifika dosyası anormal, lütfen sertifika durumunu kontrol edin'\nErrWebsiteDir: 'Lütfen web sitesi dizini içindeki bir dizin seçin.'\nErrComposerFileNotFound: 'composer.json dosyası mevcut değil'\nErrRuntimeNoPort: 'Çalışma zamanı ortamı bir porta sahip değil, lütfen önce çalışma zamanı ortamını düzenleyin.'\nStatus: 'Durum'\nstart: 'Başlat'\nstop: 'Durdur'\nrestart: 'Yeniden Başlat'\nkill: 'Zorla Durdur'\npause: 'Duraklat'\nunpause: 'Devam Et'\nremove: 'Sil'\ndelete: 'Sil'\nErrDefaultWebsite: 'Varsayılan bir web sitesi zaten ayarlanmış. Yeni bir tane ayarlamadan önce lütfen iptal edin'\nSSL: 'Sertifika'\nSet: 'Ayarlar'\nErrSSLCannotDelete: '{{ .name }} sertifikası bir web sitesi tarafından kullanılıyor ve silinemez'\nErrAccountCannotDelete: 'Hesap bir sertifikayla ilişkili ve silinemez'\nErrSSLApply: 'Sertifika yenileme başarılı, openresty yeniden yükleme başarısız, lütfen yapılandırmayı kontrol edin'\nErrEmailIsExist: 'Posta kutusu zaten mevcut'\nErrSSLKeyNotFound: 'Özel anahtar dosyası mevcut değil'\nErrSSLCertificateNotFound: 'Sertifika dosyası mevcut değil'\nErrSSLKeyFormat: 'Özel anahtar dosyası doğrulama başarısız'\nErrSSLCertificateFormat: 'Sertifika dosyası formatı yanlış, lütfen pem formatını kullanın'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid veya EabHmacKey boş olamaz'\nErrOpenrestyNotFound: 'Http modu önce Openresty yüklenmesini gerektiriyor'\nApplySSLStart: 'Sertifika başvurusu başlatılıyor, alan adı [{{ .domain }}] başvuru yöntemi [{{ .type }}]'\ndnsAccount: 'DNS Otomatik'\ndnsManual: 'DNS Manuel'\nhttp: 'HTTP'\nApplySSLFailed: '[{{ .domain }}] sertifika başvurusu başarısız, {{ .detail }}'\nApplySSLSuccess: '[{{ .domain }}] sertifika başvurusu başarılı'\nDNSAccountName: 'DNS hesabı [{{ .name }}] sağlayıcı [{{ .type }}]'\nPushDirLog: 'Sertifika [{{ .path }}] dizinine gönderildi {{ .status }}'\nErrDeleteCAWithSSL: 'Mevcut kuruluşun verilmiş bir sertifikası var ve silinemez.'\nErrDeleteWithPanelSSL: 'Panel SSL yapılandırması bu sertifikayı kullanıyor ve silinemez'\nErrDefaultCA: 'Varsayılan otorite silinemez'\nApplyWebSiteSSLLog: '{{ .name }} web sitesi sertifikası yenilemeyi başlatıyor'\nErrUpdateWebsiteSSL: '{{ .name }} web sitesi sertifika güncelleme başarısız: {{ .err }}'\nApplyWebSiteSSLSuccess: 'Web sitesi sertifikası güncelleme başarılı'\nErrExecShell: 'Betik yürütme başarısız {{ .err }}'\nExecShellStart: 'Betik yürütmeyi başlat'\nExecShellSuccess: 'Betik yürütme başarılı'\nStartUpdateSystemSSL: 'Sistem sertifikası güncellemeyi başlat'\nUpdateSystemSSLSuccess: 'Sistem sertifikası güncelleme başarılı'\nErrWildcardDomain: 'HTTP modunda joker alan adı sertifikası başvurusu yapılamıyor'\nErrApplySSLCanNotDelete: 'İşlemde olan sertifika {{ .name }} silinemez, lütfen daha sonra tekrar deneyin.'\nStartPushSSLToNode: 'Sertifika düğüme gönderilmeye başlandı'\nPushSSLToNodeFailed: 'Sertifika düğüme gönderilemedi: {{ .err }}'\nPushSSLToNodeSuccess: 'Sertifika düğüme başarıyla gönderildi'\nErrUserIsExist: 'Mevcut kullanıcı zaten mevcut, lütfen yeniden girin'\nErrDatabaseIsExist: 'Mevcut veritabanı zaten mevcut, lütfen yeniden girin'\nErrExecTimeOut: 'SQL yürütme zaman aşımı, lütfen veritabanını kontrol edin'\nErrRemoteExist: 'Uzak veritabanında bu adla zaten mevcut, lütfen değiştirin ve tekrar deneyin'\nErrLocalExist: 'Yerel veritabanında bu ad zaten mevcut, lütfen değiştirin ve tekrar deneyin'\nRemoteBackup: 'Uzak veritabanını yedeklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin...'\nRemoteRecover: 'Uzak veritabanını geri yüklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin...'\nArg: 'Argüman'\nErrTypeOfRedis: 'Kurtarma dosyası türü mevcut kalıcılık yöntemiyle eşleşmiyor, lütfen değiştirin ve tekrar deneyin'\nErrInUsed: '{{ .detail }} kullanımda ve silinemez'\nErrObjectInUsed: 'Bu nesne kullanımda ve silinemez'\nErrObjectBeDependent: 'Bu görüntü diğer görüntüler tarafından bağımlıdır ve silinemez'\nErrPortRules: 'Port numarası eşleşmiyor, lütfen yeniden girin'\nErrPgImagePull: 'Image çekme zaman aşımı, lütfen image hızlandırma yapılandırın veya manuel olarak {{ .name }} imageını çekin ve tekrar deneyin'\nPruneHelper: 'Bu temizlik {{ .name }} disk alanı {{ .size }} boşalttı'\nImageRemoveHelper: '{{ .name }} imajı silindi, {{ .size }} disk alanı boşalttı'\nBuildCache: 'Derleme önbelleği'\nVolume: 'Depolama hacmi'\nNetwork: 'Ağ'\nPruneStart: 'Temizlik devam ediyor, lütfen bekleyin...'\nContainerBackupPrepare: 'Konteyner yedekleme çalışma alanını hazırla'\nContainerBackupStop: 'Yedeklemeden önce konteyneri durdur'\nContainerBackupInspect: 'Konteyner inspect ve ağ bilgisini yedekle'\nContainerBackupMounts: 'Konteyner mount verilerini yedekle'\nContainerBackupMeta: 'Konteyner yedekleme meta verisini yaz'\nContainerBackupStart: 'Yedeklemeden sonra konteyneri başlat'\nContainerRecoverPrepare: 'Konteyner geri yükleme çalışma alanını hazırla'\nContainerRecoverExtract: 'Konteyner yedek arşivini çıkar'\nContainerRecoverParse: 'Konteyner yedek meta verisini yükle'\nContainerRecoverCreate: 'Konteyneri yeniden oluştur'\nContainerRecoverMounts: 'Konteyner mount verilerini geri yükle'\nContainerRecoverStart: 'Geri yüklenen konteyneri başlat'\nContainerRecoverCleanup: 'Konteyner geri yükleme çalışma alanını temizle'\nComposeBackupPrepare: 'Compose yedekleme çalışma alanını hazırla'\nComposeBackupStop: 'Yedeklemeden önce compose’u durdur'\nComposeBackupFiles: 'Compose dosyalarını yedekle'\nComposeBackupContainers: 'Compose konteynerlerini yedekle'\nComposeBackupMeta: 'Compose yedek meta verisini yaz'\nComposeBackupStart: 'Yedeklemeden sonra compose’u başlat'\nComposeBackupCleanup: 'Compose yedekleme çalışma alanını temizle'\nComposeRecoverPrepare: 'Compose geri yükleme çalışma alanını hazırla'\nComposeRecoverExtract: 'Compose yedek arşivini çıkar'\nComposeRecoverMeta: 'Compose yedek meta verisini yükle'\nComposeRecoverMetaLogName: 'Compose geri yükleme meta: ad={{ .name }}'\nComposeRecoverMetaLogPath: 'Compose geri yükleme meta: yedekYolu={{ .backupPath }} hedefYol={{ .targetDir }}'\nComposeRecoverMetaLogCount: 'Compose geri yükleme meta: dosya={{ .files }} konteyner={{ .containers }}'\nComposeRecoverFiles: 'Compose dosyalarını geri yükle'\nComposeRecoverContainers: 'Compose konteynerlerini geri yükle'\nComposeRecoverRecord: 'Compose kaydını sakla'\nComposeRecoverCleanup: 'Compose geri yükleme çalışma alanını temizle'\nErrFileNotExist: '{{ .detail }} dosyası mevcut değil Lütfen kaynak dosyanın bütünlüğünü kontrol edin'\nErrImageBuildErr: 'Image yapı başarısız'\nErrImageExist: 'Görüntü zaten var Lütfen görüntü adını değiştirin.'\nErrDelWithWebsite: 'İşletim ortamı zaten bir web sitesiyle ilişkili ve silinemez'\nErrRuntimeStart: 'Başlatma başarısız'\nErrPackageJsonNotFound: 'package.json dosyası mevcut değil'\nErrScriptsNotFound: 'package.json içinde scripts yapılandırma öğesi bulunamadı'\nErrContainerNameNotFound: 'Konteyner adı alınamıyor, lütfen.env dosyasını kontrol edin'\nErrNodeModulesNotFound: 'node_modules klasörü mevcut değil Lütfen çalışma ortamını düzenleyin veya çalışma ortamının başarıyla başlatılmasını bekleyin'\nErrContainerNameIsNull: 'Konteyner adı mevcut değil'\nErrPHPPortIsDefault: '9000 portu varsayılan port, lütfen değiştirin ve tekrar deneyin'\nErrPHPRuntimePortFailed: '{{ .name }} portu mevcut çalışma ortamı tarafından zaten kullanılıyor, lütfen değiştirin ve tekrar deneyin'\nErrConfigNotFound: 'Yapılandırma dosyası mevcut değil'\nErrConfigParse: 'Yapılandırma dosyası formatı yanlış'\nErrConfigIsNull: 'Yapılandırma dosyası boş olamaz'\nErrConfigDirNotFound: 'Çalışma dizini mevcut değil'\nErrConfigAlreadyExist: 'Aynı ada sahip bir yapılandırma dosyası zaten mevcut'\nErrUserFindErr: 'Kullanıcı {{ .name }} arama başarısız {{ .err }}'\nCutWebsiteLogSuccess: '{{ .name }} web sitesi günlüğü başarıyla kesildi, yedekleme yolu {{ .path }}'\nHandleShell: 'Betik {{ .name }} yürüt'\nHandleCurl: 'URL''ye Eriş {{ .name }}'\nHandleNtpSync: 'Sistem zaman senkronizasyonu'\nHandleSystemClean: 'Sistem önbellek temizliği'\nSystemLog: 'Sistem Günlüğü'\nCutWebsiteLog: 'Web Sitesi Günlüğünü Döndür'\nFileOrDir: 'Dizin / Dosya'\nUploadFile: 'Yedekleme dosyası {{ .file }} {{ .backup }} konumuna yükleniyor'\nUpload: 'Yükle'\nIgnoreBackupErr: 'Yedekleme başarısız, hata: {{ .detail }}, bu hata yoksayılıyor...'\nIgnoreUploadErr: 'Yükleme başarısız, hata: {{ .detail }}, bu hata yoksayılıyor...'\nLoadBackupFailed: 'Yedek hesap bağlantısı alınamadı, hata: {{ .detail }}'\nInExecuting: 'Mevcut görev yürütülüyor, lütfen tekrar yürütmeyin'\nNoSuchResource: 'Veritabanında yedek içeriği bulunamadı, atlanıyor...'\nCleanLog: 'Günlüğü temizle'\nCleanLogByName: '{{ .name }} günlüğünü temizle'\nWafIpGroupNotFound: 'WAF IP grubu bulunamadı. Lütfen [Gelişmiş Özellikler - WAF - Kara/Beyaz Liste - IP Grubu] bölümüne giderek uzaktan indirme yöntemiyle bir IP grubu ekleyin.'\nErrNotExistUser: 'Mevcut kullanıcı mevcut değil, lütfen değiştirin ve tekrar deneyin'\nErrBanAction: 'Ayarlama başarısız. Mevcut {{ .name }} servisi kullanılamıyor. Lütfen kontrol edin ve tekrar deneyin'\nErrClamdscanNotFound: 'clamdscan komutu tespit edilmedi, lütfen yüklemek için belgeleri inceleyin'\nTaskSwapSet: 'Swap Ayarla'\nSetSwap: 'Swap Ayarla {{ .path }} - {{ .size }}'\nCreateSwap: 'Swap Dosyası Oluştur'\nFormatSwap: 'Swap Dosyasını Biçimlendir'\nEnableSwap: 'Swap Etkinleştir'\nCleanTamperSetting: 'Geçmiş koruma ayarlarını temizle'\nSetTamperWithRules: 'Kurallara göre dizin korumasını başlat'\nProtectDir: 'Dizini koru {{ .name }}'\nIgnoreFile: 'Dosyayı koru {{ .name }}'\nTamperSetSuccessful: 'Koruma ayarları tamamlandı, aşağıdaki dizinler izlenmeye başlanıyor:'\nErrScope: 'Bu yapılandırmayı değiştirme desteklenmiyor'\nErrStateChange: 'Durum değiştirme başarısız'\nErrRuleExist: 'Kural zaten mevcut'\nErrRuleNotExist: 'Kural mevcut değil'\nErrParseIP: 'Yanlış IP formatı'\nErrDefaultIP: 'default ayrılmış bir isim, lütfen başka bir isme değiştirin'\nErrGroupInUse: 'IP grubu kara liste/beyaz liste tarafından kullanılıyor ve silinemez'\nErrIPGroupAclUse: 'IP grubu {{ .name }} web sitesinin özel kuralları tarafından kullanılıyor, silinemez'\nErrGroupExist: 'IP grup adı zaten mevcut'\nErrIPRange: 'Yanlış IP aralığı'\nErrIPExist: 'IP zaten mevcut'\nurlDefense: 'URL kuralları'\nurlHelper: 'Yasak URL'\ndirFilter: 'Dizin filtresi'\nxss: 'XSS'\nphpExec: 'PHP betik yürütme'\noneWordTrojan: 'Tek Kelime Truva Atı'\nappFilter: 'Tehlikeli dizin filtreleme uygula'\nwebShell: 'Webshell'\nargs: 'Parametre kuralları'\nprotocolFilter: 'Protokol filtreleme'\njavaFilter: 'Java tehlikeli dosya filtresi'\nscannerFilter: 'Tarayıcı filtresi'\nescapeFilter: 'kaçış filtresi'\ncustomRule: 'Özel kural'\nhttpMethod: 'HTTP yöntem filtreleme'\nfileExt: 'Dosya yükleme kısıtlamaları'\ndefaultIpBlack: 'Kötü niyetli IP grubu'\ncookie: 'Cookie Kuralları'\nurlBlack: 'URL kara listesi'\nuaBlack: 'User-Agent kara listesi'\nattackCount: 'Saldırı sıklığı sınırı'\nfileExtCheck: 'Dosya yükleme kısıtlamaları'\ngeoRestrict: 'Bölgesel erişim kısıtlamaları'\nunknownWebsite: 'Yetkisiz alan adı erişimi'\nnotFoundCount: '404 Hız Sınırı'\nheaderDefense: 'Başlık kuralları'\ndefaultUaBlack: 'User-Agent kuralları'\nmethodWhite: 'HTTP kuralları'\ncaptcha: 'insan-makine doğrulaması'\nfiveSeconds: '5 saniye doğrulaması'\nvulnCheck: 'Tamamlayıcı kurallar'\nacl: 'Özel kurallar'\nsql: 'SQL enjeksiyonu'\ncc: 'Erişim sıklığı sınırı'\ndefaultUrlBlack: 'URL kuralları'\nsqlInject: 'SQL enjeksiyonu'\nErrDBNotExist: 'Veritabanı mevcut değil'\nallow: 'izin ver'\ndeny: 'reddet'\nOpenrestyNotFound: 'Openresty yüklü değil'\nremoteIpIsNull: 'IP listesi boş'\nOpenrestyVersionErr: 'Openresty sürümü çok düşük, lütfen Openresty''i 1.27.1.2-2-2-focal olarak güncelleyin'\nErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'\nTaskStart: '{{ .name }} görevi başlıyor [BAŞLAT]'\nTaskEnd: '{{ .name }} Görev tamamlandı [TAMAMLANDI]'\nTaskFailed: '{{ .name }} görev başarısız'\nTaskTimeout: '{{ .name }} zaman aşımına uğradı'\nTaskSuccess: '{{ .name }} Görev başarılı'\nTaskRetry: '{{ .name }}. yeniden denemeyi başlat'\nSubTaskSuccess: '{{ .name }} başarılı'\nSubTaskFailed: '{{ .name }} başarısız: {{ .err }}'\nTaskInstall: 'Yükle'\nTaskUninstall: 'Kaldır'\nTaskCreate: 'Oluştur'\nTaskDelete: 'Sil'\nTaskUpgrade: 'Yükselt'\nTaskUpdate: 'Güncelle'\nTaskRestart: 'Yeniden Başlat'\nTaskProtect: 'Koru'\nTaskBackup: 'Yedekle'\nTaskRecover: 'Kurtar'\nTaskRollback: 'Geri Al'\nTaskPull: 'Çek'\nTaskCommit: 'işleme'\nTaskBuild: 'Yapı'\nTaskPush: 'Gönder'\nTaskClean: 'Temizleme'\nTaskHandle: 'Yürüt'\nTaskImport: 'İçe Aktar'\nTaskExport: 'Dışa Aktar'\nWebsite: 'Web Sitesi'\nApp: 'Uygulama'\nRuntime: 'Çalışma ortamı'\nDatabase: 'Veritabanı'\nConfigFTP: 'FTP kullanıcısı {{ .name }} oluştur'\nConfigOpenresty: 'Openresty yapılandırma dosyası oluştur'\nInstallAppSuccess: 'Uygulama {{ .name }} başarıyla yüklendi'\nConfigRuntime: 'Çalışma ortamını yapılandır'\nConfigApp: 'Uygulama Yapılandırması'\nSuccessStatus: '{{ .name }} başarılı'\nFailedStatus: '{{ .name }} başarısız {{ .err }}'\nHandleLink: 'Uygulama ilişkisini işle'\nHandleDatabaseApp: 'Uygulama parametrelerini işle'\nExecShell: '{{ .name }} betiğini yürüt'\nPullImage: 'Image çek'\nStart: 'Başlat'\nRun: 'Başlat'\nStop: 'Durdur'\nImage: 'Ayna'\nCompose: 'Düzenleme'\nContainer: 'Konteyner'\nAppLink: 'Bağlantılı Uygulama'\nEnableSSL: 'HTTPS Etkinleştir'\nAppStore: 'Uygulama Mağazası'\nTaskSync: 'Senkronize Et'\nLocalApp: 'Yerel Uygulama'\nSubTask: 'Alt görev'\nRuntimeExtension: 'Çalışma Ortamı Uzantısı'\nTaskIsExecuting: 'Görev çalışıyor'\nCustomAppstore: 'Özel uygulama deposu'\nTaskExec: 'Çalıştır'\nTaskBatch: 'Toplu İşlem'\nFileConvert: 'Dosya Formatı Dönüştürme'\nClamscan: '{{ .name }} Tara'\nTaskScan: 'Tara'\nOllamaModelPull: 'Ollama modeli {{ .name }} çek'\nOllamaModelSize: 'Ollama modeli {{ .name }} boyutunu al'\nSnapshot: 'Anlık Görüntü'\nSnapDBInfo: '1Panel veritabanı bilgilerini yaz'\nSnapCopy: 'Dosya ve dizinleri {{ .name }} kopyala'\nSnapNewDB: 'Veritabanı {{ .name }} bağlantısını başlat'\nSnapDeleteOperationLog: 'İşlem günlüğünü sil'\nSnapDeleteLoginLog: 'Erişim günlüğünü sil'\nSnapDeleteMonitor: 'İzleme verilerini sil'\nSnapRemoveSystemIP: 'Sistem IP sini kaldır'\nSnapBaseInfo: '1Panel temel bilgilerini yaz'\nSnapInstallAppImageEmpty: 'Uygulama imageı seçilmedi, atlanıyor...'\nSnapInstallApp: '1Panel yüklü uygulamaları yedekle'\nSnapDockerSave: 'Yüklü uygulamaları sıkıştır'\nSnapLocalBackup: '1Panel yerel yedekleme dizinini yedekle'\nSnapCompressBackup: 'Yerel yedekleme dizinini sıkıştır'\nSnapPanelData: '1Panel veri dizinini yedekle'\nSnapCompressPanel: 'Sıkıştırılmış Veri Dizini'\nSnapWebsite: '1Panel web sitesi dizinini yedekle'\nSnapCloseDBConn: 'Veritabanı bağlantısını kapat'\nSnapCompress: 'Anlık görüntü dosyaları oluştur'\nSnapCompressFile: 'Anlık görüntü dosyasını sıkıştır'\nSnapCheckCompress: 'Anlık görüntü sıkıştırma dosyasını kontrol et'\nSnapCompressSize: 'Anlık görüntü dosya boyutu {{ .name }}'\nSnapUpload: 'Anlık görüntü dosyasını yükle'\nSnapUploadTo: 'Anlık görüntü dosyasını {{ .name }} konumuna yükle'\nSnapUploadRes: 'Anlık görüntü dosyasını {{ .name }} konumuna yükle'\nSnapshotRecover: 'Anlık Görüntü Geri Yükleme'\nRecoverDownload: 'Anlık görüntü dosyasını indir'\nDownload: 'İndir'\nRecoverDownloadAccount: 'Anlık görüntü indirme yedek hesabı al {{ .name }}'\nRecoverDecompress: 'Anlık görüntü sıkıştırılmış dosyalarını açma'\nDecompress: 'Açma'\nBackupBeforeRecover: 'Anlık görüntü öncesi sistem ilgili verileri yedekleme'\nReadJson: 'Anlık görüntüdeki Json dosyasını oku'\nReadJsonPath: 'Anlık görüntüdeki Json dosya yolunu al'\nReadJsonContent: 'Json dosyasını oku'\nReadJsonMarshal: 'Json kaçış işlemesi'\nRecoverApp: 'Yüklü uygulamaları geri yükle'\nRecoverWebsite: 'Web sitesi dizinini kurtar'\nRecoverAppImage: 'Anlık görüntü imaj yedeklemesini geri yükle'\nRecoverCompose: 'Diğer besteci içeriğini geri yükle'\nRecoverComposeList: 'Geri yüklenecek tüm bestecileri al'\nRecoverComposeItem: 'Besteci geri yükle {{ .name }}'\nRecoverAppEmpty: 'Anlık görüntü dosyasında uygulama imaj yedeklemesi bulunamadı'\nRecoverBaseData: 'Temel veri ve dosyaları kurtar'\nRecoverDaemonJsonEmpty: 'Hem anlık görüntü dosyası hem de mevcut makine konteyner yapılandırma daemon.json dosyasına sahip değil'\nRecoverDaemonJson: 'Konteyner yapılandırma daemon.json dosyasını geri yükle'\nRecoverDBData: 'Veritabanı verilerini kurtar'\nRecoverBackups: 'Yerel yedekleme dizinini geri yükle'\nRecoverPanelData: 'Veri dizinini kurtar'\nContainerImagePull: 'Konteyner imajını çek {{ .name }}'\nContainerRemoveOld: 'Orijinal konteyneri kaldır {{ .name }}'\nContainerImageCheck: 'İmajın normal çekilip çekilmediğini kontrol et'\nContainerLoadInfo: 'Temel konteyner bilgilerini al'\nContainerRecreate: 'Konteyner güncellemesi başarısız, şimdi orijinal konteyneri geri yüklemeye başlıyor'\nContainerCreate: 'Yeni konteyner oluştur {{ .name }}'\nContainerCreateFailed: 'Konteyner oluşturma başarısız, başarısız konteyneri sil'\nContainerStartCheck: 'Konteynerin başlatılıp başlatılmadığını kontrol et'\nImageBuild: 'İmaj Oluşturma'\nImagePull: 'İmaj çek'\nImageRepoAuthFromDB: 'Veritabanından depo kimlik doğrulama bilgilerini al'\nImagePush: 'İmaj gönder'\nImageRenameTag: 'İmaj etiketini değiştir'\nImageNewTag: 'Yeni imaj etiketi {{ .name }}'\nComposeCreate: 'Kompozisyon oluştur'\nBackupNginxConfig: 'Web sitesi OpenResty yapılandırma dosyasını yedekle'\nCompressFileSuccess: 'Dizin başarıyla sıkıştırıldı, şuraya sıkıştırıldı {{ .name }}'\nCompressDir: 'Sıkıştırma dizini'\nDeCompressFile: 'Dosyayı aç {{ .name }}'\nErrCheckValid: 'Yedekleme dosyası doğrulaması başarısız, {{ .name }}'\nRollback: 'Geri alma'\nwebsiteDir: 'Web Sitesi Dizini'\nRecoverFailedStartRollBack: 'Kurtarma başarısız, geri almayı başlat'\nAppBackupFileIncomplete: 'Yedekleme dosyası eksik ve app.json veya app.tar.gz dosyalarından yoksun'\nAppAttributesNotMatch: 'Uygulama türü veya adı eşleşmiyor'\nErrAlertSync: 'Alarm bilgisi senkronizasyon hatası, lütfen kontrol edin ve tekrar deneyin'\nErrInstallExtension: 'Zaten devam eden bir kurulum görevi var, görevin bitmesini bekleyin'\nPanelAlertTitle: 'Panel Uyarı Bildirimi'\nTestAlertTitle: 'Test E-postası - E-posta Bağlantısını Doğrula'\nTestAlert: 'Bu, e-posta gönderim ayarlarınızın doğru yapılandırıldığını doğrulamak için gönderilen bir test e-postasıdır.'\nLicenseExpirationAlert: 'Paneliniz {{ .node }}{{ .ip }}, lisans {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın.'\nCronJobFailedAlert: 'Paneliniz {{ .node }}{{ .ip }}, zamanlanmış görev {{ .name }} çalıştırma başarısız oldu. Detaylar için paneline giriş yapın.'\nClamAlert: 'Paneliniz {{ .node }}{{ .ip }}, virüs tarama görevi {{ .num }} enfekte dosya tespit etti. Detaylar için paneline giriş yapın.'\nWebSiteAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} web sitesi {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın.'\nSSLAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} SSL sertifikası {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın.'\nDiskUsedAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .name }} diski kullanımı {{ .used }}''a ulaştı. Detaylar için paneline giriş yapın.'\nResourceAlert: 'Paneliniz {{ .node }}{{ .ip }}, son {{ .time }} dakika içindeki ortalama {{ .name }} kullanım oranı {{ .used }}''dır. Detaylar için paneline giriş yapın.'\nPanelVersionAlert: 'Paneliniz {{ .node }}{{ .ip }}, en son panel sürümü yükseltme için mevcut. Detaylar için paneline giriş yapın.'\nPanelPwdExpirationAlert: 'Paneliniz {{ .node }}{{ .ip }}, panel şifresi {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın.'\nCommonAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .msg }}. Detaylar için paneline giriş yapın.'\nNodeExceptionAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} düğüm anormal durumda. Detaylar için paneline giriş yapın.'\nLicenseExceptionAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} lisans anormal durumda. Detaylar için paneline giriş yapın.'\nSSHAndPanelLoginAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .loginIp }} adresinden {{ .name }} girişi anormal. Detaylar için paneline giriş yapın.'\nDeviceNotFound: 'Cihaz {{ .name }} bulunamadı'\nDeviceIsMounted: 'Cihaz {{ .name }} bağlandı, önce çıkarın'\nPartitionDiskErr: 'Bölümleme başarısız, {{ .err }}'\nFormatDiskErr: 'Disk biçimlendirme başarısız, {{ .err }}'\nMountDiskErr: 'Disk bağlama başarısız, {{ .err }}'\nXfsNotFound: 'XFS dosya sistemi algılanmadı, lütfen önce xfsprogs''i yükleyin'\n"
  },
  {
    "path": "agent/i18n/lang/zh-Hant.yaml",
    "content": "ErrInvalidParams: '請求參數錯誤: {{ .detail }}'\nErrTokenParse: 'Token 產生錯誤: {{ .detail }}'\nErrInitialPassword: '原密碼錯誤'\nErrInternalServer: '服務內部錯誤: {{ .detail }}'\nErrRecordExist: '記錄已存在'\nErrRecordNotFound: '記錄未能找到'\nErrStructTransform: '型別轉換失敗: {{ .err }}'\nErrNotLogin: '使用者未登入: {{ .detail }}'\nErrPasswordExpired: '目前密碼已過期: {{ .detail }}'\nErrNotSupportType: '系統暫不支援目前類型: {{ .name }}'\nErrProxy: '請求錯誤，請檢查該節點狀態: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API 介面禁止存取: {{ .detail }}'\nErrApiConfigKeyInvalid: 'API 介面金鑰錯誤: {{ .detail }}'\nErrApiConfigIPInvalid: '呼叫 API 介面 IP 不在白名單: {{ .detail }}'\nErrApiConfigDisable: '此介面禁止使用 API 介面呼叫: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'API 介面時間戳記錯誤: {{ .detail }}'\nErrShutDown: '指令被手動結束！'\nErrMinQuickJump: '請至少設定一個快速跳轉入口！'\nErrMaxQuickJump: '最多可設定四個快速跳轉入口！'\nErrUsernameIsExist: '使用者名稱已存在'\nErrNameIsExist: '名稱已存在'\nErrDemoEnvironment: '示範伺服器，禁止此操作!'\nErrCmdTimeout: '指令執行逾時！'\nErrCmdIllegal: '執行指令中存在不合法字元，請修改後重試！'\nErrPortExist: '{{ .port }} 埠已被{{ .type }} [{{ .name }}] 佔用'\nTYPE_APP: '應用'\nTYPE_RUNTIME: '運作環境'\nTYPE_DOMAIN: '網域名稱'\nErrTypePort: '埠{{ .name }} 格式錯誤'\nErrTypePortRange: '連接埠範圍需要在 1-65535 之間'\nSuccess: '成功'\nFailed: '失敗'\nSystemRestart: '系統重新啟動導致任務中斷'\nErrGroupIsDefault: '預設分組，無法刪除'\nErrGroupIsInWebsiteUse: '分組正在被其他網站使用，無法刪除'\nDecrypt: '解密'\nErrAgentAccountBound: '該帳號已綁定到智能體，無法刪除，請重試。'\nErrAgentAccountUnavailable: '帳號連線資訊不可用，錯誤：{{ .err }}，請重試'\nErrAgentProviderNotSupported: '暫不支援該智能體提供商，請重試'\nErrAgentAccountRequired: '請選擇智能體帳號後重試'\nErrAgentAccountNotVerified: '帳號未驗證通過，請重試'\nErrAgentAccountModelsRequired: '請至少配置一個模型'\nErrAgentAccountSingleInitialModel: '建立帳號時只能配置一個初始模型'\nErrAgentModelNotInAccount: '所選模型未配置在當前帳號中，請重試'\nErrAgentModelInUse: '仍有智能體正在使用該模型：{{ .name }}'\nErrAgentBaseURLRequired: 'Base URL 不可為空，請重試'\nErrAgentApiKeyRequired: 'API Key 不可為空，請重試'\nErrAgentComposeRequired: '自訂編排內容不可為空，請重試'\nErrAgentIDRequired: '智能體 ID 不可為空，請重試'\nErrAgentAccountIDRequired: '帳號 ID 不可為空，請重試'\nErrAgentLimitReached: '社群版最多支援建立 {{ .max }} 個智能體，升級至專業版則無數量限制'\nLocalhost: '本機'\nErrBackupInUsed: '此備份帳號已在排程任務中使用，無法刪除'\nErrBackupCheck: '備份帳號測試連線失敗{{ .err }}'\nErrBackupLocalDelete: '暫時不支援刪除本機伺服器備份帳號'\nErrBackupLocalCreate: '暫時不支援建立本機伺服器備份帳號'\nErrPortInUsed: '{{ .detail }} 連接埠已被佔用！'\nErrAppLimit: '應用程式超出安裝數量限制'\nErrNotInstall: '應用程式未安裝'\nErrPortInOtherApp: '{{ .port }} 連接埠已被應用程式{{ .apps }} 佔用！'\nErrDbUserNotValid: '存量資料庫，使用者名稱密碼不符！'\nErrUpdateBuWebsite: '應用程式更新成功，但網站設定檔修改失敗，請檢查設定！'\nErr1PanelNetworkFailed: '預設容器網路建立失敗！ {{ .detail }}'\nErrFileParse: '應用docker-compose 檔解析失敗!'\nErrInstallDirNotFound: '安裝目錄不存在，如需移除，請選擇強制移除'\nAppStoreIsUpToDate: '應用程式商店已經是最新版本'\nLocalAppVersionNull: '{{ .name }} 應用程式未同步到版本！無法加入到應用程式列表'\nLocalAppVersionErr: '{{ .name }} 同步版本{{ .version }} 失敗！ {{ .err }}'\nErrFileNotFound: '{{ .name }} 檔案不存在'\nErrFileParseApp: '{{ .name }} 檔案解析失敗{{ .err }}'\nErrAppDirNull: '版本資料夾不存在'\nLocalAppErr: '應用程式{{ .name }} 同步失敗！{{ .err }}'\nErrContainerName: '容器名稱已存在'\nErrCreateHttpClient: '建立請求失敗{{ .err }}'\nErrHttpReqTimeOut: '請求逾時{{ .err }}'\nErrHttpReqFailed: '請求失敗{{ .err }}'\nErrNoSuchHost: '無法找到請求的伺服器{{ .err }}'\nErrHttpReqNotFound: '無法找到要求的資源{{ .err }}'\nErrContainerNotFound: '{{ .name }} 容器不存在'\nErrContainerMsg: '{{ .name }} 容器異常，請在容器頁面上檢視日誌'\nErrAppBackup: '{{ .name }} 應用備份失敗 {{ .err }}'\nErrVersionTooLow: '目前 1Panel 版本過低，無法更新應用程式商店，請升級版本之後操作'\nErrAppNameExist: '應用程式名稱已存在'\nAppStoreIsSyncing: '應用程式商店正在同步中，請稍後重試'\nErrGetCompose: 'docker-compose.yml 檔案取得失敗！{{ .detail }}'\nErrAppWarn: '狀態異常，請檢視日誌'\nErrAppParamKey: '參數{{ .name }} 欄位異常'\nErrAppUpgrade: '應用程式{{ .name }} 升級失敗{{ .err }}'\nAppRecover: '應用程式{{ .name }} 回滾'\nPullImageStart: '開始拉取映像 [{{ .name }}]'\nPullImageSuccess: '映像拉取成功'\nAppStoreSyncSuccess: '應用程式商店同步成功'\nSyncAppDetail: '同步應用程式設定'\nAppVersionNotMatch: '{{ .name }} 應用程式需要更高的 1Panel 版本，跳過同步'\nMoveSiteDir: '偵測到網站目錄變更 需要遷移 OpenResty 網站目錄到 {{ .name }}'\nMoveSiteDirSuccess: '遷移網站目錄成功'\nDeleteRuntimePHP: '刪除PHP 執行環境'\nCustomAppStoreFileValid: '應用程式商店包需要.tar.gz 格式'\nPullImageTimeout: '拉取映像逾時,請嘗試增加映像加速或更換其他映像加速'\nErrAppIsDown: '{{ .name }} 應用程式狀態異常，請檢查'\nErrCustomApps: '存在已安裝的應用程式，請先解除安裝'\nErrCustomRuntimes: '存在已安裝的運作環境，請先刪除'\nErrAppVersionDeprecated: '{{ .name }} 應用不適配目前 1Panel 版本，跳過'\nErrDockerFailed: 'Docker 狀態異常，請檢查服務狀態'\nErrDockerComposeCmdNotFound: 'Docker Compose 指令不存在，請先在宿主機安裝此指令'\nUseExistImage: '映像已存在，使用現有映像'\nErrDatabaseNotFound: '資料庫 {{ .name }} 不存在'\nExportIP: '登入IP'\nExportArea: '歸屬地'\nExportPort: '埠'\nExportAuthMode: '登入方式'\nExportUser: '使用者'\nExportStatus: '登入狀態'\nExportDate: '時間'\nErrFileCanNotRead: '此檔案不支援預覽'\nErrFileToLarge: '檔案超過10M，無法開啟'\nErrPathNotFound: '目錄不存在'\nErrMovePathFailed: '目標路徑不可包含原路徑！'\nErrLinkPathNotFound: '目標路徑不存在!'\nErrFileIsExist: '檔案或資料夾已存在！'\nErrFileUpload: '{{ .name }} 上傳檔案失敗{{ .detail }}'\nErrFileDownloadDir: '不支援下載資料夾'\nErrCmdNotFound: '{{ .name }} 指令不存在，請先在宿主機安裝此指令'\nErrSourcePathNotFound: '來源目錄不存在'\nErrFavoriteExist: '已收藏此路徑'\nErrInvalidChar: '禁止使用無效字元'\nErrPathNotDelete: '所選目錄不可刪除'\nErrLogFileToLarge: '日誌檔案超過 500M，無法開啟'\nFileDropFailed: '清理檔案 {{ .name }} 失敗: {{ .err }}'\nFileDropSuccess: '清理檔案 {{ .name }} 成功，清理 {{ .count }} 個檔案，釋放 {{ .size }} 磁碟空間'\nFileDropSum: '檔案清理完成，共清理 {{ .count }} 個檔案，共釋放 {{ .size }} 磁碟空間'\nErrBadDecrypt: '壓縮檔解壓密碼錯誤，解壓失敗，請重試。'\nErrAliasIsExist: '代號已存在'\nErrBackupMatch: '該備份檔案與目前網站部分資料不符{{ .detail }}'\nErrBackupExist: '該備份檔案對應部分來源資料不存在{{ .detail }}'\nErrPHPResource: '本機執行環境不支援切換！'\nErrPathPermission: 'index 目錄下偵測到非 1000:1000 權限資料夾，可能導致網站存取 Access denied 錯誤，請點選上方儲存按鈕'\nErrDomainIsUsed: '網域名稱已被網站【{{ .name }}】使用'\nErrDomainFormat: '{{ .name }} 網域格式不正確'\nErrDefaultAlias: 'default 為保留代號，請使用其他代號'\nErrParentWebsite: '需要先移除子網站{{ .name }}'\nErrBuildDirNotFound: '建置目錄不存在'\nErrImageNotExist: '執行環境{{ .name }} 映像不存在，請重新編輯執行環境'\nErrProxyIsUsed: '負載均衡已被反向代理使用，無法刪除'\nErrSSLValid: '憑證檔案異常，請檢查憑證狀態！'\nErrWebsiteDir: '請選擇網站目錄下的目錄'\nErrComposerFileNotFound: 'composer.json 檔案不存在'\nErrRuntimeNoPort: '執行環境未設定埠，請先編輯執行環境'\nStatus: '狀態'\nstart: '開啟'\nstop: '關閉'\nrestart: '重新啟動'\nkill: '強制停止'\npause: '暫停'\nunpause: '恢復'\nremove: '刪除'\ndelete: '刪除'\nErrDefaultWebsite: '已經設定預設網站，請取消後再設定！'\nSSL: '憑證'\nSet: '設定'\nErrSSLCannotDelete: '{{ .name }} 憑證正在被網站使用，無法刪除'\nErrAccountCannotDelete: '帳號關聯憑證，無法刪除'\nErrSSLApply: '憑證續簽成功，openresty reload 失敗，請檢查設定！'\nErrEmailIsExist: '信箱已存在'\nErrSSLKeyNotFound: '私鑰檔案不存在'\nErrSSLCertificateNotFound: '憑證檔案不存在'\nErrSSLKeyFormat: '私鑰檔案驗證失敗'\nErrSSLCertificateFormat: '憑證檔案格式錯誤，請使用 pem 格式'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不可為空'\nErrOpenrestyNotFound: 'Http 模式需要先安裝OpenResty'\nApplySSLStart: '開始申請憑證，網域名稱[{{ .domain }}] 申請方式[{{ .type }}]'\ndnsAccount: 'DNS 自動'\ndnsManual: 'DNS 手排'\nhttp: 'HTTP'\nApplySSLFailed: '申請[{{ .domain }}] 憑證失敗， {{ .detail }}'\nApplySSLSuccess: '申請[{{ .domain }}] 憑證成功！'\nDNSAccountName: 'DNS 帳號[{{ .name }}] 廠商[{{ .type }}]'\nPushDirLog: '憑證推送到目錄[{{ .path }}] {{ .status }}'\nErrDeleteCAWithSSL: '目前機構下存在已簽發憑證，無法刪除'\nErrDeleteWithPanelSSL: '面板SSL 設定使用此憑證，無法刪除'\nErrDefaultCA: '預設機構不可刪除'\nApplyWebSiteSSLLog: '開始更新{{ .name }} 網站憑證'\nErrUpdateWebsiteSSL: '{{ .name }} 網站更新憑證失敗: {{ .err }}'\nApplyWebSiteSSLSuccess: '更新網站憑證成功'\nErrExecShell: '執行腳本失敗{{ .err }}'\nExecShellStart: '開始執行腳本'\nExecShellSuccess: '腳本執行成功'\nStartUpdateSystemSSL: '開始更新系統憑證'\nUpdateSystemSSLSuccess: '更新系統憑證成功'\nErrWildcardDomain: 'HTTP 模式無法申請泛網域憑證'\nErrApplySSLCanNotDelete: '正在申請的憑證 {{ .name }} 無法刪除，請稍後重試'\nStartPushSSLToNode: '開始推送憑證到節點'\nPushSSLToNodeFailed: '推送憑證到節點失敗: {{ .err }}'\nPushSSLToNodeSuccess: '推送憑證到節點成功'\nErrUserIsExist: '目前使用者已存在，請重新輸入'\nErrDatabaseIsExist: '目前資料庫已存在，請重新輸入'\nErrExecTimeOut: 'SQL 執行逾時，請檢查資料庫'\nErrRemoteExist: '遠端資料庫已存在該名稱，請修改後重試'\nErrLocalExist: '本機資料庫已存在該名稱，請修改後重試'\nRemoteBackup: '備份遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務，請稍候...'\nRemoteRecover: '復原遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務，請稍候...'\nArg: '參數'\nErrTypeOfRedis: '復原檔案類型與目前持久化方式不符，請修改後重試'\nErrInUsed: '{{ .detail }} 正被使用，無法刪除！'\nErrObjectInUsed: '該對象正被使用，無法刪除！'\nErrObjectBeDependent: '該映像正被其他映像所依賴，無法刪除！'\nErrPortRules: '連接埠數目不符，請重新輸入！'\nErrPgImagePull: '映像拉取逾時，請設定映像加速或手動拉取{{ .name }} 映像後重試'\nPruneHelper: '本次清理 {{ .name }} 釋放磁碟空間 {{ .size }}'\nImageRemoveHelper: '刪除映像 {{ .name }} ，釋放磁碟空間 {{ .size }}'\nBuildCache: '構建快取'\nVolume: '磁碟區'\nNetwork: '網路'\nPruneStart: '清理中，請稍候...'\nContainerBackupPrepare: '準備容器備份工作目錄'\nContainerBackupStop: '備份前停止容器'\nContainerBackupInspect: '備份容器 Inspect 與網路資訊'\nContainerBackupMounts: '備份容器掛載資料'\nContainerBackupMeta: '寫入容器備份中繼資訊'\nContainerBackupStart: '備份後啟動容器'\nContainerRecoverPrepare: '準備容器還原工作目錄'\nContainerRecoverExtract: '解壓容器備份檔案'\nContainerRecoverParse: '載入容器備份中繼資訊'\nContainerRecoverCreate: '重建容器'\nContainerRecoverMounts: '還原容器掛載資料'\nContainerRecoverStart: '啟動已還原容器'\nContainerRecoverCleanup: '清理容器還原工作目錄'\nComposeBackupPrepare: '準備編排備份工作目錄'\nComposeBackupStop: '備份前停止編排'\nComposeBackupFiles: '備份編排檔案'\nComposeBackupContainers: '備份編排容器'\nComposeBackupMeta: '寫入編排備份中繼資訊'\nComposeBackupStart: '備份後啟動編排'\nComposeBackupCleanup: '清理編排備份工作目錄'\nComposeRecoverPrepare: '準備編排還原工作目錄'\nComposeRecoverExtract: '解壓編排備份檔案'\nComposeRecoverMeta: '載入編排備份中繼資訊'\nComposeRecoverMetaLogName: '編排還原中繼資訊：名稱={{ .name }}'\nComposeRecoverMetaLogPath: '編排還原中繼資訊：備份路徑={{ .backupPath }} 目標路徑={{ .targetDir }}'\nComposeRecoverMetaLogCount: '編排還原中繼資訊：檔案數={{ .files }} 容器數={{ .containers }}'\nComposeRecoverFiles: '還原編排檔案'\nComposeRecoverContainers: '還原編排容器'\nComposeRecoverRecord: '保存編排記錄'\nComposeRecoverCleanup: '清理編排還原工作目錄'\nErrFileNotExist: '{{ .detail }} 檔案不存在！請檢查來源檔案完整性！'\nErrImageBuildErr: '映像build 失敗'\nErrImageExist: '映像已存在！請修改映像名稱。'\nErrDelWithWebsite: '執行環境已經關聯網站，無法刪除'\nErrRuntimeStart: '啟動失敗'\nErrPackageJsonNotFound: 'package.json 檔案不存在'\nErrScriptsNotFound: '沒有在 package.json 中找到 scripts 設定項'\nErrContainerNameNotFound: '無法取得容器名稱，請檢查.env 檔案'\nErrNodeModulesNotFound: 'node_modules 資料夾不存在！請編輯執行環境或等待執行環境啟動成功'\nErrContainerNameIsNull: '容器名稱不存在'\nErrPHPPortIsDefault: '9000 埠為預設埠，請修改後重試'\nErrPHPRuntimePortFailed: '{{ .name }} 埠已被目前執行環境使用，請修改後重試'\nErrConfigNotFound: '設定檔不存在'\nErrConfigParse: '設定檔格式有誤'\nErrConfigIsNull: '設定檔不允許為空'\nErrConfigDirNotFound: '執行目錄不存在'\nErrConfigAlreadyExist: '已存在同名設定檔'\nErrUserFindErr: '使用者{{ .name }} 尋找失敗{{ .err }}'\nCutWebsiteLogSuccess: '{{ .name }} 網站日誌切割成功，備份路徑{{ .path }}'\nHandleShell: '執行腳本{{ .name }}'\nHandleCurl: '存取 URL {{ .name }}'\nHandleNtpSync: '系統時間同步'\nHandleSystemClean: '系統快取清理'\nSystemLog: '系統日誌'\nCutWebsiteLog: '切割網站日誌'\nFileOrDir: '目錄 / 檔案'\nUploadFile: '上傳備份檔案 {{ .file }} 到 {{ .backup }}'\nUpload: '上傳'\nIgnoreBackupErr: '備份失敗，錯誤：{{ .detail }}，忽略本次錯誤...'\nIgnoreUploadErr: '上傳失敗，錯誤：{{ .detail }}，忽略本次錯誤...'\nLoadBackupFailed: '取得備份帳號連線失敗，錯誤：{{ .detail }}'\nInExecuting: '目前任務正在執行中，請勿重複執行！'\nNoSuchResource: '資料庫中未能查詢到備份內容，跳過...'\nCleanLog: '清理日誌'\nCleanLogByName: '清理 {{ .name }} 日誌'\nWafIpGroupNotFound: 'WAF IP 群組未找到，請先到【進階功能-WAF-黑白名單-IP群組】添加遠端下載方式的IP群組'\nErrNotExistUser: '目前使用者不存在，請修改後重試！'\nErrBanAction: '設定失敗，目前{{ .name }} 服務不可用，請重試'\nErrClamdscanNotFound: '未偵測到clamdscan 指令，請參考檔案安裝！'\nTaskSwapSet: '設定 Swap'\nSetSwap: '設定 Swap {{ .path }} - {{ .size }}'\nCreateSwap: '建立 Swap 檔案'\nFormatSwap: '格式化 Swap 檔案'\nEnableSwap: '啟用 Swap'\nCleanTamperSetting: '清理歷史防護設定'\nSetTamperWithRules: '根據規則啟動目錄防護'\nProtectDir: '防護目錄 {{ .name }}'\nIgnoreFile: '保護檔案 {{ .name }}'\nTamperSetSuccessful: '防護設定完成，開始監聽以下目錄：'\nErrScope: '不支援修改此設定'\nErrStateChange: '狀態修改失敗'\nErrRuleExist: '規則已存在'\nErrRuleNotExist: '規則不存在'\nErrParseIP: 'IP 格式錯誤'\nErrDefaultIP: 'default 為保留名稱，請更換其他名稱'\nErrGroupInUse: 'IP 群組被黑/白名單使用，無法刪除'\nErrIPGroupAclUse: 'IP 群組被網站 {{ .name }} 自訂規則使用，無法刪除'\nErrGroupExist: 'IP 群組名稱已存在'\nErrIPRange: 'IP 範圍錯誤'\nErrIPExist: 'IP 已存在'\nurlDefense: 'URL 規則'\nurlHelper: '禁止訪問的URL'\ndirFilter: '目錄過濾'\nxss: 'XSS'\nphpExec: 'PHP 腳本執行'\noneWordTrojan: '一句話木馬'\nappFilter: '套用危險目錄過濾'\nwebShell: 'WebShell'\nargs: '參數規則'\nprotocolFilter: '協定過濾'\njavaFilter: 'Java 危險檔案過濾'\nscannerFilter: '掃描器過濾'\nescapeFilter: '轉義過濾'\ncustomRule: '自訂規則'\nhttpMethod: 'HTTP 方法過濾'\nfileExt: '檔案上傳限制'\ndefaultIpBlack: '惡意IP 群組'\ncookie: 'Cookie 規則'\nurlBlack: 'URL 黑名單'\nuaBlack: 'User-Agent 黑名單'\nattackCount: '攻擊頻率限制'\nfileExtCheck: '檔案上傳限制'\ngeoRestrict: '地區訪問限制'\nunknownWebsite: '未授權網域存取'\nnotFoundCount: '404 頻率限制'\nheaderDefense: 'Header 規則'\ndefaultUaBlack: 'User-Agent 規則'\nmethodWhite: 'HTTP 規則'\ncaptcha: '人機驗證'\nfiveSeconds: '5 秒驗證'\nvulnCheck: '補充規則'\nacl: '自訂規則'\nsql: 'SQL 注入'\ncc: '訪問頻率限制'\ndefaultUrlBlack: 'URL 規則'\nsqlInject: 'SQL 注入'\nErrDBNotExist: '資料庫不存在'\nallow: '允許'\ndeny: '禁止'\nOpenrestyNotFound: 'OpenResty 未安裝'\nremoteIpIsNull: 'IP 列表為空'\nOpenrestyVersionErr: 'OpenResty 版本過低，請升級 OpenResty 至 1.27.1.2-2-2-focal'\nErrFileTooLarge: '檔案超過 1MB，無法上傳'\nTaskStart: '{{ .name }} 任務開始[START]'\nTaskEnd: '{{ .name }} 任務結束[COMPLETED]'\nTaskFailed: '{{ .name }} 任務失敗'\nTaskTimeout: '{{ .name }} 逾時'\nTaskSuccess: '{{ .name }} 任務成功'\nTaskRetry: '開始第{{ .name }} 次重試'\nSubTaskSuccess: '{{ .name }} 成功'\nSubTaskFailed: '{{ .name }} 失敗: {{ .err }}'\nTaskInstall: '安裝'\nTaskUninstall: '移除'\nTaskCreate: '建立'\nTaskDelete: '刪除'\nTaskUpgrade: '升級'\nTaskUpdate: '更新'\nTaskRestart: '重新啟動'\nTaskProtect: '防護'\nTaskBackup: '備份'\nTaskRecover: '復原'\nTaskRollback: '回滾'\nTaskPull: '拉取'\nTaskCommit: '製作'\nTaskBuild: '建置'\nTaskPush: '推送'\nTaskClean: '清理'\nTaskHandle: '執行'\nTaskImport: '匯入'\nTaskExport: '匯出'\nWebsite: '網站'\nApp: '應用程式'\nRuntime: '運作環境'\nDatabase: '資料庫'\nConfigFTP: '建立FTP 使用者{{ .name }}'\nConfigOpenresty: '建立OpenResty 設定檔'\nInstallAppSuccess: '應用程式{{ .name }} 安裝成功'\nConfigRuntime: '設定執行環境'\nConfigApp: '設定應用程式'\nSuccessStatus: '{{ .name }} 成功'\nFailedStatus: '{{ .name }} 失敗{{ .err }}'\nHandleLink: '處理應用程式關聯'\nHandleDatabaseApp: '處理應用程式參數'\nExecShell: '執行{{ .name }} 腳本'\nPullImage: '拉取映像'\nStart: '開始'\nRun: '啟動'\nStop: '停止'\nImage: '映像'\nCompose: '編排'\nContainer: '容器'\nAppLink: '關聯應用程式'\nEnableSSL: '開啟HTTPS'\nAppStore: '應用程式商店'\nTaskSync: '同步'\nLocalApp: '本機應用'\nSubTask: '子任務'\nRuntimeExtension: '執行環境擴充'\nTaskIsExecuting: '任務正在運作'\nCustomAppstore: '自訂應用程式倉庫'\nTaskExec: '執行'\nTaskBatch: '批次操作'\nFileConvert: '檔案格式轉換'\nClamscan: '掃描 {{ .name }}'\nTaskScan: '掃描'\nOllamaModelPull: '拉取 Ollama 模型{{ .name }}'\nOllamaModelSize: '取得 Ollama 模型{{ .name }} 大小'\nSnapshot: '快照'\nSnapDBInfo: '寫入1Panel 資料庫資訊'\nSnapCopy: '複製檔案&目錄{{ .name }}'\nSnapNewDB: '初始化資料庫{{ .name }} 連線'\nSnapDeleteOperationLog: '刪除操作日誌'\nSnapDeleteLoginLog: '刪除存取日誌'\nSnapDeleteMonitor: '刪除監控資料'\nSnapRemoveSystemIP: '移除系統IP'\nSnapBaseInfo: '寫入1Panel 基本資料'\nSnapInstallAppImageEmpty: '目前未勾選應用映像，跳過...'\nSnapInstallApp: '備份 1Panel 已安裝應用'\nSnapDockerSave: '壓縮已安裝應用'\nSnapLocalBackup: '備份1Panel 本機備份目錄'\nSnapCompressBackup: '壓縮本機備份目錄'\nSnapPanelData: '備份1Panel 資料目錄'\nSnapCompressPanel: '壓縮資料目錄'\nSnapWebsite: '備份1Panel 網站目錄'\nSnapCloseDBConn: '關閉資料庫連線'\nSnapCompress: '製作快照檔案'\nSnapCompressFile: '壓縮快照檔案'\nSnapCheckCompress: '檢查快照壓縮檔'\nSnapCompressSize: '快照檔案大小{{ .name }}'\nSnapUpload: '上傳快照檔案'\nSnapUploadTo: '上傳快照檔案到{{ .name }}'\nSnapUploadRes: '上傳快照檔案到{{ .name }}'\nSnapshotRecover: '快照復原'\nRecoverDownload: '下載快照檔案'\nDownload: '下載'\nRecoverDownloadAccount: '取得快照下載備份帳號{{ .name }}'\nRecoverDecompress: '解壓縮快照壓縮檔'\nDecompress: '解壓縮'\nBackupBeforeRecover: '快照前備份系統相關資料'\nReadJson: '讀取快照內JSON 檔案'\nReadJsonPath: '取得快照內JSON 檔案路徑'\nReadJsonContent: '讀取JSON 檔案'\nReadJsonMarshal: 'JSON 轉義處理'\nRecoverApp: '復原已安裝應用程式'\nRecoverWebsite: '復原網站目錄'\nRecoverAppImage: '復原快照映像備份'\nRecoverCompose: '復原其他編排內容'\nRecoverComposeList: '取得所有待復原編排'\nRecoverComposeItem: '復原編排{{ .name }}'\nRecoverAppEmpty: '快照檔案中未發現應用程式映像備份'\nRecoverBaseData: '復原基礎資料及檔案'\nRecoverDaemonJsonEmpty: '快照檔案及目前機器都不存在容器設定daemon.json 檔'\nRecoverDaemonJson: '復原容器設定daemon.json 檔'\nRecoverDBData: '復原資料庫資料'\nRecoverBackups: '還原本機備份目錄'\nRecoverPanelData: '復原資料目錄'\nContainerImagePull: '拉取容器映像{{ .name }}'\nContainerRemoveOld: '刪除原容器{{ .name }}'\nContainerImageCheck: '檢查映像是否正常拉取'\nContainerLoadInfo: '取得容器基本資訊'\nContainerRecreate: '容器更新失敗，現在開始復原原容器'\nContainerCreate: '建立新容器{{ .name }}'\nContainerCreateFailed: '容器建立失敗，刪除失敗容器'\nContainerStartCheck: '檢查容器是否已啟動'\nImageBuild: '映像建置'\nImagePull: '拉取映像'\nImageRepoAuthFromDB: '從資料庫取得倉庫認證資訊'\nImagePush: '推播映像'\nImageRenameTag: '修改映像Tag'\nImageNewTag: '新映像Tag {{ .name }}'\nComposeCreate: '建立編排'\nBackupNginxConfig: '備份網站OpenResty 設定檔'\nCompressFileSuccess: '壓縮目錄成功，壓縮為{{ .name }}'\nCompressDir: '壓縮目錄'\nDeCompressFile: '解壓縮檔案{{ .name }}'\nErrCheckValid: '驗證備份檔失敗，{{ .name }}'\nRollback: '回滾'\nwebsiteDir: '網站目錄'\nRecoverFailedStartRollBack: '復原失敗，開始回溯'\nAppBackupFileIncomplete: '備份檔案不完整缺少 app.json 或 app.tar.gz 檔案'\nAppAttributesNotMatch: '應用程式類型或名稱不一致'\nErrAlertSync: '警告訊息同步錯誤，請重試。'\nErrInstallExtension: '已有安裝任務正在進行，請等待任務結束'\nPanelAlertTitle: '面板警示通知'\nTestAlertTitle: '測試郵件 - 驗證信箱連通性'\nTestAlert: '這是一封測試郵件，用於確認您的郵件發送設定是否正確。'\nLicenseExpirationAlert: '您的 {{ .node }}{{ .ip }} 面板，授權將於 {{ .day }} 天後到期，詳情請登入面板檢視。'\nCronJobFailedAlert: '您的 {{ .node }}{{ .ip }} 面板，排程任務{{ .name }}執行失敗，詳情請登入面板檢視。'\nClamAlert: '您的 {{ .node }}{{ .ip }} 面板，病毒掃描任務偵測到 {{ .num }} 個感染檔案，詳情請登入面板檢視。'\nWebSiteAlert: '您的 {{ .node }}{{ .ip }} 面板，{{ .num }} 個網站將於 {{ .day }} 天後到期，詳情請登入面板檢視。'\nSSLAlert: '您的 {{ .node }}{{ .ip }} 面板，{{ .num }} 張SSL憑證將於 {{ .day }} 天後到期，詳情請登入面板檢視。'\nDiskUsedAlert: '您的 {{ .node }}{{ .ip }} 面板，磁碟 {{ .name }} 使用率已達 {{ .used }}，詳情請登入面板檢視。'\nResourceAlert: '您的 {{ .node }}{{ .ip }} 面板，過去 {{ .time }} 分鐘內 {{ .name }} 平均使用率為 {{ .used }}，詳情請登入面板檢視。'\nPanelVersionAlert: '您的 {{ .node }}{{ .ip }} 面板，已有最新版本可供升級，詳情請登入面板檢視。'\nPanelPwdExpirationAlert: '您的 {{ .node }}{{ .ip }} 面板，面板密碼將於 {{ .day }} 天後到期，詳情請登入面板檢視。'\nCommonAlert: '您的 {{ .node }}{{ .ip }} 面板，{{ .msg }}，詳情請登入面板檢視。'\nNodeExceptionAlert: '您的 {{ .node }}{{ .ip }} 面板，{{ .num }} 個節點出現異常，詳情請登入面板檢視。'\nLicenseExceptionAlert: '您的 {{ .node }}{{ .ip }} 面板，{{ .num }} 個授權出現異常，詳情請登入面板檢視。'\nSSHAndPanelLoginAlert: '您的 {{ .node }}{{ .ip }} 面板，來自 {{ .loginIp }} 的 {{ .name }} 登入出現異常，詳情請登入面板檢視。'\nDeviceNotFound: '裝置 {{ .name }} 未找到'\nDeviceIsMounted: '裝置 {{ .name }} 已掛載，請先移除'\nPartitionDiskErr: '分區失敗，{{ .err }}'\nFormatDiskErr: '格式化磁碟失敗，{{ .err }}'\nMountDiskErr: '掛載磁碟失敗，{{ .err }}'\nXfsNotFound: '未偵測到 xfs 檔案系統，請先安裝 xfsprogs'\n"
  },
  {
    "path": "agent/i18n/lang/zh.yaml",
    "content": "ErrInvalidParams: \"参数错误: {{ .detail }}\"\nErrTokenParse: \"Token 错误: {{ .detail }}\"\nErrInitialPassword: \"原密码错误\"\nErrInternalServer: \"服务错误: {{ .detail }}\"\nErrRecordExist: \"记录已存在\"\nErrRecordNotFound: \"记录不存在\"\nErrStructTransform: \"类型转换失败: {{ .err }}\"\nErrNotLogin: \"用户未登录: {{ .detail }}\"\nErrPasswordExpired: \"当前密码已过期: {{ .detail }}\"\nErrNotSupportType: \"不支持当前类型: {{ .name }}\"\nErrProxy: \"请求失败，请检查节点状态: {{ .detail }}\"\nErrApiConfigStatusInvalid: \"API 禁止访问: {{ .detail }}\"\nErrApiConfigKeyInvalid: \"API 接口密钥错误: {{ .detail }}\"\nErrApiConfigIPInvalid: \"调用 API 接口 IP 不在白名单: {{ .detail }}\"\nErrApiConfigDisable: \"此接口禁止 API 调用: {{ .detail }}\"\nErrApiConfigKeyTimeInvalid: \"API 时间戳错误: {{ .detail }}\"\nErrShutDown: \"命令已手动终止\"\n\nErrMinQuickJump: \"至少设置 1 个快速跳转入口\"\nErrMaxQuickJump: \"最多设置 4 个快速跳转入口\"\n\n#common\nErrUsernameIsExist: \"用户名已存在\"\nErrNameIsExist: \"名称已存在\"\nErrDemoEnvironment: \"演示环境，禁止操作\"\nErrCmdTimeout: \"命令超时\"\nErrCmdIllegal: \"命令含非法字符，请修改后重试\"\nErrPortExist: '{{ .port }} 端口已被 {{ .type }} [{{ .name }}] 占用'\nTYPE_APP: \"应用\"\nTYPE_RUNTIME: \"运行环境\"\nTYPE_DOMAIN: \"域名\"\nErrTypePort: '端口 {{ .name }} 格式错误'\nErrTypePortRange: '端口范围需要在 1-65535 之间'\nSuccess: \"成功\"\nFailed: \"失败\"\nSystemRestart: \"系统重启，任务中断\"\nErrGroupIsDefault: \"默认分组不可删除\"\nErrGroupIsInWebsiteUse: \"分组正在被其他网站使用，无法删除\"\nDecrypt: \"解密\"\n\n#agent\nErrAgentAccountBound: \"该账号已绑定智能体，无法删除\"\nErrAgentAccountUnavailable: \"账号连接信息不可用: {{ .err }}\"\nErrAgentProviderNotSupported: \"不支持该智能体提供商\"\nErrAgentAccountRequired: \"请选择智能体账号后重试\"\nErrAgentAccountNotVerified: \"账号未通过验证\"\nErrAgentAccountModelsRequired: \"请至少配置一个模型\"\nErrAgentAccountSingleInitialModel: \"创建账号时只能配置一个初始模型\"\nErrAgentModelNotInAccount: \"所选模型未配置在当前账号中\"\nErrAgentModelInUse: \"仍有智能体正在使用该模型: {{ .name }}\"\nErrAgentBaseURLRequired: \"Base URL 不能为空\"\nErrAgentApiKeyRequired: \"API Key 不能为空\"\nErrAgentComposeRequired: \"自定义编排内容不能为空\"\nErrAgentIDRequired: \"智能体 ID 不能为空\"\nErrAgentAccountIDRequired: \"账号 ID 不能为空\"\nErrAgentLimitReached: \"社区版最多支持创建 {{ .max }} 个智能体，升级至专业版无数量限制\"\n\n#backup\nLocalhost: '本机'\nErrBackupInUsed: \"该备份账号已在计划任务中使用，无法删除\"\nErrBackupCheck: \"备份账号测试连接失败 {{ .err }}\"\nErrBackupLocalDelete: \"暂不支持删除本地服务器备份账号\"\nErrBackupLocalCreate: \"暂不支持创建本地服务器备份账号\"\n\n#app\nErrPortInUsed: \"{{ .detail }} 端口已被占用！\"\nErrAppLimit: \"应用安装数量超限\"\nErrNotInstall: \"应用未安装\"\nErrPortInOtherApp: \"{{ .port }} 端口已被应用 {{ .apps }} 占用！\"\nErrDbUserNotValid: \"存量数据库用户名或密码不匹配\"\nErrUpdateBuWebsite: '应用更新成功，但网站配置文件修改失败，请检查配置'\nErr1PanelNetworkFailed: '默认容器网络创建失败！{{ .detail }}'\nErrFileParse: '应用 docker-compose 解析失败'\nErrInstallDirNotFound: '安装目录不存在，如需卸载请选强制卸载'\nAppStoreIsUpToDate: '应用商店已是最新版本'\nLocalAppVersionNull: '{{ .name }} 应用未同步到版本！无法添加到应用列表'\nLocalAppVersionErr: '{{ .name }} 同步版本 {{ .version }} 失败！{{ .err }}'\nErrFileNotFound: '{{ .name }} 文件不存在'\nErrFileParseApp: '{{ .name }} 文件解析失败 {{ .err }}'\nErrAppDirNull: '版本文件夹不存在'\nLocalAppErr: \"应用 {{ .name }} 同步失败！{{ .err }}\"\nErrContainerName: \"容器名称已存在\"\nErrCreateHttpClient: \"创建请求失败 {{ .err }}\"\nErrHttpReqTimeOut: \"请求超时 {{ .err }}\"\nErrHttpReqFailed: \"请求失败 {{ .err }}\"\nErrNoSuchHost: \"无法找到请求的服务器 {{ .err }}\"\nErrHttpReqNotFound: \"无法找到请求的资源 {{ .err }}\"\nErrContainerNotFound: '{{ .name }} 容器不存在'\nErrContainerMsg: '{{ .name }} 容器异常，具体请在容器页面查看日志'\nErrAppBackup: '{{ .name }} 应用备份失败 {{ .err }}'\nErrVersionTooLow: '当前 1Panel 版本过低，无法更新应用商店，请升级版本之后操作'\nErrAppNameExist: '应用名称已存在'\nAppStoreIsSyncing: '应用商店同步中，请稍后重试'\nErrGetCompose: \"docker-compose.yml 文件获取失败！{{ .detail }}\"\nErrAppWarn: \"状态异常，请查看日志\"\nErrAppParamKey: \"参数 {{ .name }} 字段异常\"\nErrAppUpgrade: \"应用 {{ .name }} 升级失败 {{ .err }}\"\nAppRecover: \"应用 {{ .name }} 回滚\"\nPullImageStart: \"开始拉取镜像 [{{ .name }}]\"\nPullImageSuccess: \"镜像拉取成功\"\nAppStoreSyncSuccess: \"应用商店同步成功\"\nSyncAppDetail: \"同步应用配置\"\nAppVersionNotMatch: \"{{ .name }} 应用需要更高的 1Panel 版本，跳过\"\nMoveSiteDir: \"检测到网站目录变更，需迁移 OpenResty 网站目录到 {{ .name }}\"\nMoveSiteDirSuccess: \"迁移网站目录成功\"\nDeleteRuntimePHP: \"删除 PHP 运行环境\"\nCustomAppStoreFileValid: \"应用商店包需要 .tar.gz 格式\"\nPullImageTimeout: \"拉取镜像超时，请调整镜像加速配置\"\nErrAppIsDown: \"{{ .name }} 应用状态异常，请检查\"\nErrCustomApps: \"存在已安装应用，请先卸载\"\nErrCustomRuntimes: \"存在已安装运行环境，请先删除\"\nErrAppVersionDeprecated: \"{{ .name }} 应用不适配当前 1Panel 版本，跳过\"\nErrDockerFailed: \"Docker 状态异常，请检查服务状态\"\nErrDockerComposeCmdNotFound: \"Docker Compose 命令不存在，请先在宿主机安装\"\nUseExistImage: \"镜像已存在，使用存量镜像\"\nErrDatabaseNotFound: \"数据库 {{ .name }} 不存在\"\n\n#ssh\nExportIP: \"登录 IP\"\nExportArea: \"归属地\"\nExportPort: \"端口\"\nExportAuthMode: \"登录方式\"\nExportUser: \"用户\"\nExportStatus: \"登录状态\"\nExportDate: \"时间\"\n\n#file\nErrFileCanNotRead: \"此文件不支持预览\"\nErrFileToLarge: \"文件超过 10M，无法打开\"\nErrPathNotFound: \"目录不存在\"\nErrMovePathFailed: \"目标路径不能包含原路径！\"\nErrLinkPathNotFound: \"目标路径不存在！\"\nErrFileIsExist: \"文件或文件夹已存在！\"\nErrFileUpload: \"{{ .name }} 上传文件失败 {{ .detail }}\"\nErrFileDownloadDir: \"不支持下载目录\"\nErrCmdNotFound: \"{{ .name }} 命令不存在，请先在宿主机安装此命令\"\nErrSourcePathNotFound:  \"源目录不存在\"\nErrFavoriteExist: \"已收藏此路径\"\nErrInvalidChar: \"禁止使用非法字符\"\nErrPathNotDelete: \"所选目录不可删除\"\nErrLogFileToLarge: \"日志文件超过 500M，无法打开\"\nFileDropFailed: \"清理文件 {{ .name }} 失败 {{ .err }}\"\nFileDropSuccess: \"清理 {{ .name }} 成功，文件 {{ .count }}，释放 {{ .size }}\"\nFileDropSum: \"文件清理完成，共 {{ .count }}，释放 {{ .size }}\"\nErrBadDecrypt: \"压缩包密码错误，解压失败，请检查后重试\"\n\n#website\nErrAliasIsExist: \"标识已存在\"\nErrBackupMatch: '该备份文件与当前网站部分数据不匹配 {{ .detail }}'\nErrBackupExist: '该备份文件对应部分源数据不存在 {{ .detail }}'\nErrPHPResource: '本地运行环境不支持切换！'\nErrPathPermission: 'index 目录存在非 1000:1000 权限目录，可能导致 Access denied，请点击上方保存'\nErrDomainIsUsed: \"域名已被网站【{{ .name }}】使用\"\nErrDomainFormat: \"{{ .name }} 域名格式不正确\"\nErrDefaultAlias: \"default 为保留代号，请使用其他代号\"\nErrParentWebsite: \"需要先删除子网站 {{ .name }}\"\nErrBuildDirNotFound: \"构建目录不存在\"\nErrImageNotExist: \"运行环境 {{ .name }} 镜像不存在，请重新编辑运行环境\"\nErrProxyIsUsed: \"负载均衡已被反向代理使用，无法删除\"\nErrSSLValid: '证书文件异常，请检查证书状态！'\nErrWebsiteDir: '请选择网站目录下的目录'\nErrComposerFileNotFound: 'composer.json 文件不存在'\nErrRuntimeNoPort: '运行环境未设置端口，请先编辑运行环境'\nStatus: '状态'\nstart: '开启'\nstop: '关闭'\nrestart: '重启'\nkill: '强制停止'\npause: '暂停'\nunpause: '恢复'\nremove: '删除'\ndelete: '删除'\nErrDefaultWebsite: \"已设置默认网站，请先取消再设置\"\nSSL: '证书'\nSet: '设置'\n\n#ssl\nErrSSLCannotDelete: \"{{ .name }} 证书正在被网站使用，无法删除\"\nErrAccountCannotDelete: \"账号关联证书，无法删除\"\nErrSSLApply: \"证书续签成功，但 OpenResty reload 失败，请检查配置\"\nErrEmailIsExist: '邮箱已存在'\nErrSSLKeyNotFound: '私钥文件不存在'\nErrSSLCertificateNotFound: '证书文件不存在'\nErrSSLKeyFormat: '私钥文件校验失败'\nErrSSLCertificateFormat: '证书文件格式错误，请使用 pem 格式'\nErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能为空'\nErrOpenrestyNotFound: 'Http 模式需要首先安装 Openresty'\nApplySSLStart: '开始申请证书，域名 [{{ .domain }}]，方式 [{{ .type }}]'\ndnsAccount: \"DNS 自动\"\ndnsManual: \"DNS 手动\"\nhttp: \"HTTP\"\nApplySSLFailed: '申请 [{{ .domain }}] 证书失败，{{ .detail }}'\nApplySSLSuccess: '申请 [{{ .domain }}] 证书成功'\nDNSAccountName: 'DNS 账号 [{{ .name }}] 厂商 [{{ .type }}]'\nPushDirLog: '证书推送到目录 [{{ .path }}] {{ .status }}'\nErrDeleteCAWithSSL: \"当前机构下存在已签发证书，无法删除\"\nErrDeleteWithPanelSSL: \"面板 SSL 配置使用此证书，无法删除\"\nErrDefaultCA: \"默认机构不能删除\"\nApplyWebSiteSSLLog: \"开始更新网站证书 {{ .name }}\"\nErrUpdateWebsiteSSL: \"{{ .name }} 网站更新证书失败: {{ .err }}\"\nApplyWebSiteSSLSuccess: \"更新网站证书成功\"\nErrExecShell: \"执行脚本失败 {{ .err }}\"\nExecShellStart: \"开始执行脚本\"\nExecShellSuccess: \"脚本执行成功\"\nStartUpdateSystemSSL: \"开始更新系统证书\"\nUpdateSystemSSLSuccess: \"更新系统证书成功\"\nErrWildcardDomain: \"HTTP 模式无法申请泛域名证书\"\nErrApplySSLCanNotDelete: \"正在申请的证书{{ .name }}无法删除，请稍后重试\"\nStartPushSSLToNode: \"开始推送证书到节点\"\nPushSSLToNodeFailed: \"推送证书到节点失败: {{ .err }}\"\nPushSSLToNodeSuccess: \"推送证书到节点成功\"\n\n#mysql\nErrUserIsExist: \"用户已存在，请重新输入\"\nErrDatabaseIsExist: \"数据库已存在，请重新输入\"\nErrExecTimeOut: \"SQL 执行超时，请检查数据库\"\nErrRemoteExist: \"远程数据库已存在该名称，请修改后重试\"\nErrLocalExist: \"本地数据库已存在该名称，请修改后重试\"\nRemoteBackup: \"备份远程数据库前需先用镜像 {{ .name }} 启动本地容器数据库服务，请稍候\"\nRemoteRecover: \"恢复远程数据库前需先用镜像 {{ .name }} 启动本地容器数据库服务，请稍候\"\nArg: \"参数\"\n\n#redis\nErrTypeOfRedis: \"恢复文件类型与当前持久化方式不符，请修改后重试\"\n\n#container\nErrInUsed: \"{{ .detail }} 正被使用，无法删除！\"\nErrObjectInUsed: \"该对象正被使用，无法删除！\"\nErrObjectBeDependent: \"该镜像正被其他镜像所依赖，无法删除！\"\nErrPortRules: \"端口数目不匹配，请重新输入！\"\nErrPgImagePull: \"镜像拉取超时，请配置镜像加速或手动拉取 {{ .name }} 后重试\"\nPruneHelper: \"本次清理 {{ .name }} 释放磁盘空间 {{ .size }}\"\nImageRemoveHelper: \"删除镜像 {{ .name }} ，释放磁盘空间 {{ .size }}\"\nBuildCache: \"构建缓存\"\nVolume: \"存储卷\"\nNetwork: \"网络\"\nPruneStart: \"清理中，请稍候...\"\nContainerBackupPrepare: \"准备容器备份工作目录\"\nContainerBackupStop: \"备份前停止容器\"\nContainerBackupInspect: \"备份容器 Inspect 与网络信息\"\nContainerBackupMounts: \"备份容器挂载数据\"\nContainerBackupMeta: \"写入容器备份元信息\"\nContainerBackupStart: \"备份后启动容器\"\nContainerRecoverPrepare: \"准备容器恢复工作目录\"\nContainerRecoverExtract: \"解压容器备份文件\"\nContainerRecoverParse: \"加载容器备份元信息\"\nContainerRecoverCreate: \"重建容器\"\nContainerRecoverMounts: \"恢复容器挂载数据\"\nContainerRecoverStart: \"启动已恢复容器\"\nContainerRecoverCleanup: \"清理容器恢复工作目录\"\nComposeBackupPrepare: \"准备编排备份工作目录\"\nComposeBackupStop: \"备份前停止编排\"\nComposeBackupFiles: \"备份编排文件\"\nComposeBackupContainers: \"备份编排容器\"\nComposeBackupMeta: \"写入编排备份元信息\"\nComposeBackupStart: \"备份后启动编排\"\nComposeBackupCleanup: \"清理编排备份工作目录\"\nComposeRecoverPrepare: \"准备编排恢复工作目录\"\nComposeRecoverExtract: \"解压编排备份文件\"\nComposeRecoverMeta: \"加载编排备份元信息\"\nComposeRecoverMetaLogName: \"编排恢复元信息：名称={{ .name }}\"\nComposeRecoverMetaLogPath: \"编排恢复元信息：备份路径={{ .backupPath }} 目标路径={{ .targetDir }}\"\nComposeRecoverMetaLogCount: \"编排恢复元信息：文件数={{ .files }} 容器数={{ .containers }}\"\nComposeRecoverFiles: \"恢复编排文件\"\nComposeRecoverContainers: \"恢复编排容器\"\nComposeRecoverRecord: \"保存编排记录\"\nComposeRecoverCleanup: \"清理编排恢复工作目录\"\n\n#runtime\nErrFileNotExist: \"{{ .detail }} 文件不存在！请检查源文件完整性！\"\nErrImageBuildErr: \"镜像 build 失败\"\nErrImageExist: \"镜像已存在！请修改镜像名称\"\nErrDelWithWebsite: \"运行环境已经关联网站，无法删除\"\nErrRuntimeStart: \"启动失败\"\nErrPackageJsonNotFound: \"package.json 文件不存在\"\nErrScriptsNotFound: \"没有在 package.json 中找到 scripts 配置项\"\nErrContainerNameNotFound: \"无法获取容器名称，请检查 .env 文件\"\nErrNodeModulesNotFound: \"node_modules 文件夹不存在！请编辑运行环境或等待运行环境启动成功\"\nErrContainerNameIsNull: \"容器名称不存在\"\nErrPHPPortIsDefault: \"9000 端口为默认端口，请修改后重试\"\nErrPHPRuntimePortFailed: \"{{ .name }} 端口已被当前运行环境使用，请修改后重试\"\n\n#tool\nErrConfigNotFound: \"配置文件不存在\"\nErrConfigParse: \"配置文件格式有误\"\nErrConfigIsNull: \"配置文件不允许为空\"\nErrConfigDirNotFound: \"运行目录不存在\"\nErrConfigAlreadyExist: \"已存在同名配置文件\"\nErrUserFindErr: \"用户 {{ .name }} 查找失败 {{ .err }}\"\n\n#cronjob\nCutWebsiteLogSuccess: \"{{ .name }} 网站日志切割成功，备份路径 {{ .path }}\"\nHandleShell: \"执行脚本 {{ .name }}\"\nHandleCurl: \"访问 URL {{ .name }}\"\nHandleNtpSync: \"系统时间同步\"\nHandleSystemClean: \"系统缓存清理\"\nSystemLog: \"系统日志\"\nCutWebsiteLog: \"切割网站日志\"\nFileOrDir: \"目录 / 文件\"\nUploadFile: \"上传备份文件 {{ .file }} 到 {{ .backup }}\"\nUpload: \"上传\"\nIgnoreBackupErr: \"备份失败，错误：{{ .detail }}，忽略本次错误...\"\nIgnoreUploadErr: \"上传失败，错误：{{ .detail }}，忽略本次错误...\"\nLoadBackupFailed: \"获取备份账号连接失败，错误：{{ .detail }}\"\nInExecuting: \"任务执行中，请勿重复执行\"\nNoSuchResource: \"数据库未查询到备份内容，跳过\"\nCleanLog: \"清理日志\"\nCleanLogByName: \"清理 {{ .name }} 日志\"\nWafIpGroupNotFound: \"WAF IP组未找到，请先到【高级功能-WAF-黑白名单-IP组】添加远程下载方式的IP组\"\n\n#toolbox\nErrNotExistUser: \"当前用户不存在，请修改后重试！\"\nErrBanAction: \"设置失败，{{ .name }} 服务不可用，请检查后重试\"\nErrClamdscanNotFound: \"未检测到 clamdscan 命令，请按文档安装\"\nTaskSwapSet: \"设置 Swap\"\nSetSwap: \"设置 Swap {{ .path }} - {{ .size }}\"\nCreateSwap: \"创建 Swap 文件\"\nFormatSwap: \"格式化 Swap 文件\"\nEnableSwap: \"启用 Swap\"\n\n#tamper\nCleanTamperSetting: \"清理历史防护设置\"\nSetTamperWithRules: \"根据规则启动目录防护\"\nProtectDir: \"防护目录 {{ .name }}\"\nIgnoreFile: \"保护文件 {{ .name }}\"\nTamperSetSuccessful: \"防护设置完成，开始监听以下目录：\"\n\n#waf\nErrScope: \"不支持修改此配置\"\nErrStateChange: \"状态修改失败\"\nErrRuleExist: \"规则已存在\"\nErrRuleNotExist: \"规则不存在\"\nErrParseIP: \"IP 格式错误\"\nErrDefaultIP: \"default 为保留名称，请更换其他名称\"\nErrGroupInUse: \"IP 组被黑/白名单使用，无法删除\"\nErrIPGroupAclUse: \"IP 组被网站 {{ .name }} 自定义规则使用，无法删除\"\nErrGroupExist: \"IP 组名称已存在\"\nErrIPRange: \"IP 范围错误\"\nErrIPExist: \"IP 已存在\"\nurlDefense: 'URL 规则'\nurlHelper: '禁止访问的 URL'\ndirFilter: '目录过滤'\nxss: 'XSS'\nphpExec: 'PHP 脚本执行'\noneWordTrojan: '一句话木马'\nappFilter: '应用危险目录过滤'\nwebShell: 'WebShell'\nargs: '参数规则'\nprotocolFilter: '协议过滤'\njavaFilter: 'Java 危险文件过滤'\nscannerFilter: '扫描器过滤'\nescapeFilter: '转义过滤'\ncustomRule: '自定义规则'\nhttpMethod: 'HTTP 方法过滤'\nfileExt: '文件上传限制'\ndefaultIpBlack: '恶意 IP 组'\ncookie: 'Cookie 规则'\nurlBlack: 'URL 黑名单'\nuaBlack: 'User-Agent 黑名单'\nattackCount: '攻击频率限制'\nfileExtCheck: '文件上传限制'\ngeoRestrict: '地区访问限制'\nunknownWebsite: '未授权域名访问'\nnotFoundCount: '404 频率限制'\nheaderDefense: 'Header 规则'\ndefaultUaBlack: 'User-Agent 规则'\nmethodWhite: 'HTTP 规则'\ncaptcha: '人机验证'\nfiveSeconds: '5 秒验证'\nvulnCheck: '补充规则'\nacl: '自定义规则'\nsql: 'SQL 注入'\ncc: '访问频率限制'\ndefaultUrlBlack: 'URL 规则'\nsqlInject: 'SQL 注入'\nErrDBNotExist: \"数据库不存在\"\nallow: \"允许\"\ndeny: \"禁止\"\nOpenrestyNotFound: \"Openresty 未安装\"\nremoteIpIsNull: \"IP 列表为空\"\nOpenrestyVersionErr: \"OpenResty 版本过低，请升级到 1.27.1.2-2-2-focal\"\nErrFileTooLarge: \"文件超过 1MB，无法上传\"\n\n#task\nTaskStart: \"{{ .name }} 任务开始 [START]\"\nTaskEnd: \"{{ .name }} 任务结束 [COMPLETED]\"\nTaskFailed: \"{{ .name }} 任务失败\"\nTaskTimeout: \"{{ .name }} 超时\"\nTaskSuccess: \"{{ .name }} 任务成功\"\nTaskRetry: \"开始第 {{ .name }} 次重试\"\nSubTaskSuccess: \"{{ .name }} 成功\"\nSubTaskFailed: \"{{ .name }} 失败: {{ .err }}\"\nTaskInstall: \"安装\"\nTaskUninstall: \"卸载\"\nTaskCreate: \"创建\"\nTaskDelete: \"删除\"\nTaskUpgrade: \"升级\"\nTaskUpdate: \"更新\"\nTaskRestart: \"重启\"\nTaskProtect: \"防护\"\nTaskBackup: \"备份\"\nTaskRecover: \"恢复\"\nTaskRollback: \"回滚\"\nTaskPull: \"拉取\"\nTaskCommit: \"制作\"\nTaskBuild: \"构建\"\nTaskPush: \"推送\"\nTaskClean: \"清理\"\nTaskHandle: \"执行\"\nTaskImport: \"导入\"\nTaskExport: \"导出\"\nWebsite: \"网站\"\nApp: \"应用\"\nRuntime: \"运行环境\"\nDatabase: \"数据库\"\nConfigFTP: \"创建 FTP 用户 {{ .name }}\"\nConfigOpenresty: \"创建 Openresty 配置文件\"\nInstallAppSuccess: \"应用 {{ .name }} 安装成功\"\nConfigRuntime: \"配置运行环境\"\nConfigApp: \"配置应用\"\nSuccessStatus: \"{{ .name }} 成功\"\nFailedStatus: \"{{ .name }} 失败 {{ .err }}\"\nHandleLink: \"处理应用关联\"\nHandleDatabaseApp: \"处理应用参数\"\nExecShell: \"执行 {{ .name }} 脚本\"\nPullImage: \"拉取镜像\"\nStart: \"开始\"\nRun: \"启动\"\nStop: \"停止\"\nImage: \"镜像\"\nCompose: \"编排\"\nContainer: \"容器\"\nAppLink: \"关联应用\"\nEnableSSL: \"开启 HTTPS\"\nAppStore: \"应用商店\"\nTaskSync: \"同步\"\nLocalApp: \"本地应用\"\nSubTask: \"子任务\"\nRuntimeExtension: \"运行环境扩展\"\nTaskIsExecuting: \"任务运行中\"\nCustomAppstore: \"自定义应用仓库\"\nTaskExec: \"执行\"\nTaskBatch: \"批量操作\"\nFileConvert: \"文件格式转换\"\n\n# task - clam\nClamscan: \"扫描 {{ .name }}\"\nTaskScan: \"扫描\"\n\n# task - ai\nOllamaModelPull: \"拉取 Ollama 模型 {{ .name }}\"\nOllamaModelSize: \"获取 Ollama 模型 {{ .name }} 大小\"\n\n# task - snapshot\nSnapshot: \"快照\"\nSnapDBInfo: \"写入 1Panel 数据库信息\"\nSnapCopy: \"复制文件/目录 {{ .name }}\"\nSnapNewDB: \"初始化数据库 {{ .name }} 连接\"\nSnapDeleteOperationLog: \"删除操作日志\"\nSnapDeleteLoginLog: \"删除访问日志\"\nSnapDeleteMonitor: \"删除监控数据\"\nSnapRemoveSystemIP: \"移除系统 IP\"\nSnapBaseInfo: \"写入 1Panel 基本信息\"\nSnapInstallAppImageEmpty: \"当前未勾选应用镜像，跳过...\"\nSnapInstallApp: \"备份 1Panel 已安装应用\"\nSnapDockerSave: \"压缩已安装应用\"\nSnapLocalBackup: \"备份 1Panel 本地备份目录\"\nSnapCompressBackup: \"压缩本地备份目录\"\nSnapPanelData: \"备份 1Panel 数据目录\"\nSnapCompressPanel: \"压缩数据目录\"\nSnapWebsite: \"备份 1Panel 网站目录\"\nSnapCloseDBConn: \"关闭数据库连接\"\nSnapCompress: \"制作快照文件\"\nSnapCompressFile: \"压缩快照文件\"\nSnapCheckCompress: \"检查快照压缩文件\"\nSnapCompressSize: \"快照文件大小 {{ .name }}\"\nSnapUpload: \"上传快照文件\"\nSnapUploadTo: \"上传快照文件到 {{ .name }}\"\nSnapUploadRes: \"上传快照文件到 {{ .name }}\"\n\nSnapshotRecover: \"快照恢复\"\nRecoverDownload: \"下载快照文件\"\nDownload: \"下载\"\nRecoverDownloadAccount: \"获取快照下载备份账号 {{ .name }}\"\nRecoverDecompress: \"解压快照压缩文件\"\nDecompress: \"解压\"\nBackupBeforeRecover: \"快照前备份系统相关数据\"\nReadJson: \"读取快照内 Json 文件\"\nReadJsonPath: \"获取快照内 Json 文件路径\"\nReadJsonContent: \"读取 Json 文件\"\nReadJsonMarshal: \"Json 转义处理\"\nRecoverApp: \"恢复已安装应用\"\nRecoverWebsite: \"恢复网站目录\"\nRecoverAppImage: \"恢复快照镜像备份\"\nRecoverCompose: \"恢复其他编排内容\"\nRecoverComposeList: \"获取所有待恢复编排\"\nRecoverComposeItem: \"恢复编排 {{ .name }}\"\nRecoverAppEmpty: \"快照文件中未发现应用镜像备份\"\nRecoverBaseData: \"恢复基础数据及文件\"\nRecoverDaemonJsonEmpty: \"快照文件及当前机器都不存在容器配置 daemon.json 文件\"\nRecoverDaemonJson: \"恢复容器配置 daemon.json 文件\"\nRecoverDBData: \"恢复数据库数据\"\nRecoverBackups: \"恢复本地备份目录\"\nRecoverPanelData: \"恢复数据目录\"\n\n# task - container\nContainerImagePull: \"拉取容器镜像 {{ .name }}\"\nContainerRemoveOld: \"删除原容器 {{ .name }}\"\nContainerImageCheck: \"检查镜像是否正常拉取\"\nContainerLoadInfo: \"获取容器基本信息\"\nContainerRecreate: \"容器更新失败，现在开始恢复原容器\"\nContainerCreate: \"创建新容器 {{ .name }}\"\nContainerCreateFailed: \"容器创建失败，删除失败容器\"\nContainerStartCheck: \"检查容器是否已启动\"\n\n# task - image\nImageBuild: \"镜像构建\"\nImagePull: \"拉取镜像\"\nImageRepoAuthFromDB: \"从数据库获取仓库认证信息\"\nImagePush: \"推送镜像\"\nImageRenameTag: \"修改镜像 Tag\"\nImageNewTag: \"新镜像 Tag {{ .name }}\"\nComposeCreate: \"创建编排\"\n\n# task - website\nBackupNginxConfig: \"备份网站 OpenResty 配置文件\"\nCompressFileSuccess: \"压缩目录成功，压缩为 {{ .name }}\"\nCompressDir: \"压缩目录\"\nDeCompressFile: \"解压文件 {{ .name }}\"\nErrCheckValid: \"校验备份文件失败，{{ .name }}\"\nRollback: \"回滚\"\nwebsiteDir: \"网站目录\"\nRecoverFailedStartRollBack: \"恢复失败，开始回滚\"\nAppBackupFileIncomplete: \"备份文件不完整，缺少 app.json 或 app.tar.gz 文件\"\nAppAttributesNotMatch: \"应用类型或名称不一致\"\n\n#alert\nErrAlertSync: \"告警信息同步错误，请检查后重试\"\n\n#task - runtime\nErrInstallExtension: \"已有安装任务正在进行，请等待任务结束\"\n\n# alert mail template\nPanelAlertTitle: \"面板告警通知\"\nTestAlertTitle: \"测试邮件 - 验证邮箱连通性\"\nTestAlert: \"这是一封测试邮件，用于验证邮箱发件配置。\"\nLicenseExpirationAlert: \"您的 {{ .node }}{{ .ip }} 面板，许可证将在 {{ .day }} 天后到期，请登录面板查看详情。\"\nCronJobFailedAlert: \"您的 {{ .node }}{{ .ip }} 面板，计划任务-{{ .name }}执行失败，请登录面板查看详情。\"\nClamAlert: \"您的 {{ .node }}{{ .ip }} 面板，病毒扫描任务发现 {{ .num }} 个感染文件，请登录面板查看详情。\"\nWebSiteAlert: \"您的 {{ .node }}{{ .ip }} 面板，有 {{ .num }} 个网站将在 {{ .day }} 天后到期，请登录面板查看详情。\"\nSSLAlert: \"您的 {{ .node }}{{ .ip }} 面板，有 {{ .num }} 张SSL证书将在 {{ .day }} 天后到期，请登录面板查看详情。\"\nDiskUsedAlert: \"您的 {{ .node }}{{ .ip }} 面板，磁盘 {{ .name }} 已使用 {{ .used }}，请登录面板查看详情。\"\nResourceAlert: \"您的 {{ .node }}{{ .ip }} 面板，平均 {{ .time }} 分钟内的 {{ .name }} 使用率为 {{ .used }}，请登录面板查看详情。\"\nPanelVersionAlert: \"您的 {{ .node }}{{ .ip }} 面板，有最新面板版本可供升级，请登录面板查看详情。\"\nPanelPwdExpirationAlert: \"您的 {{ .node }}{{ .ip }} 面板，面板密码将在 {{ .day }} 天后到期，请登录面板查看详情。\"\nCommonAlert: \"您的 {{ .node }}{{ .ip }} 面板，{{ .msg }}，请登录面板查看详情。\"\nNodeExceptionAlert: \"您的 {{ .node }}{{ .ip }} 面板，{{ .num }} 个节点存在异常，请登录面板查看详情。\"\nLicenseExceptionAlert: \"您的 {{ .node }}{{ .ip }} 面板，{{ .num }} 个许可证存在异常，请登录面板查看详情。\"\nSSHAndPanelLoginAlert: \"您的 {{ .node }}{{ .ip }} 面板，面板 {{ .name }} 登录 {{ .loginIp }} 异常，请登录面板查看详情。\"\n\n#disk\nDeviceNotFound: \"设备 {{ .name }} 未找到\"\nDeviceIsMounted: \"设备 {{ .name }} 已挂载，请先卸载\"\nPartitionDiskErr: \"分区失败，{{ .err }}\"\nFormatDiskErr: \"格式化磁盘失败，{{ .err }}\"\nMountDiskErr: \"挂载磁盘失败，{{ .err }}\"\nXfsNotFound: \"未检测到 xfs 文件系统，请先安装 xfsprogs\"\n"
  },
  {
    "path": "agent/init/app/app.go",
    "content": "package app\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n)\n\nfunc Init() {\n\tgo func() {\n\t\t_ = docker.CreateDefaultDockerNetwork()\n\t}()\n}\n"
  },
  {
    "path": "agent/init/business/business.go",
    "content": "package business\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\nfunc Init() {\n\tgo syncApp()\n\tgo syncInstalledApp()\n\tgo syncRuntime()\n\tgo syncSSL()\n\tgo syncTask()\n\tgo initAcmeAccount()\n\tgo checkDockerCompose()\n}\n\nfunc syncApp() {\n\tif global.CONF.Base.IsOffLine {\n\t\treturn\n\t}\n\tsetting, err := service.NewISettingService().GetSettingInfo()\n\tif err == nil && setting.AppStoreSyncStatus == constant.StatusSyncing {\n\t\t_ = service.NewISettingService().Update(\"AppStoreSyncStatus\", constant.StatusSyncSuccess)\n\t}\n\tif err := service.NewIAppService().SyncAppListFromRemote(\"\"); err != nil {\n\t\tglobal.LOG.Errorf(\"App Store synchronization failed\")\n\t\treturn\n\t}\n}\n\nfunc syncInstalledApp() {\n\tif err := service.NewIAppInstalledService().SyncAll(true); err != nil {\n\t\tglobal.LOG.Errorf(\"sync installed app error: %s\", err.Error())\n\t}\n}\n\nfunc syncRuntime() {\n\tif err := service.NewRuntimeService().SyncForRestart(); err != nil {\n\t\tglobal.LOG.Errorf(\"sync runtime status error : %s\", err.Error())\n\t}\n}\n\nfunc syncSSL() {\n\tif err := service.NewIWebsiteSSLService().SyncForRestart(); err != nil {\n\t\tglobal.LOG.Errorf(\"sync ssl status error : %s\", err.Error())\n\t}\n}\n\nfunc syncTask() {\n\tif err := service.NewITaskService().SyncForRestart(); err != nil {\n\t\tglobal.LOG.Errorf(\"sync task status error : %s\", err.Error())\n\t}\n}\n\nfunc initAcmeAccount() {\n\tacmeAccountService := service.NewIWebsiteAcmeAccountService()\n\tsearch := dto.PageInfo{\n\t\tPage:     1,\n\t\tPageSize: 10,\n\t}\n\tcount, _, _ := acmeAccountService.Page(search)\n\tif count == 0 {\n\t\tcreateAcmeAccount := request.WebsiteAcmeAccountCreate{\n\t\t\tEmail:   \"acme@1paneldev.com\",\n\t\t\tType:    \"letsencrypt\",\n\t\t\tKeyType: \"2048\",\n\t\t}\n\t\tsystemProxy, _ := service.NewISettingService().GetSystemProxy()\n\t\tif systemProxy.URL != \"\" {\n\t\t\tcreateAcmeAccount.UseProxy = true\n\t\t}\n\t\tif _, err := acmeAccountService.Create(createAcmeAccount); err != nil {\n\t\t\tglobal.LOG.Warningf(\"create acme account error: %s\", err.Error())\n\t\t}\n\t}\n}\n\nfunc checkDockerCompose() {\n\tdockerComposeCmd := common.GetDockerComposeCommand()\n\tif dockerComposeCmd == \"\" {\n\t\tglobal.LOG.Errorf(\"Docker Compose command not found, please install Docker Compose Plugin\")\n\t\treturn\n\t}\n\tglobal.CONF.DockerConfig.Command = dockerComposeCmd\n\tif err := service.NewISettingService().Update(\"DockerComposeCommand\", dockerComposeCmd); err != nil {\n\t\tglobal.LOG.Errorf(\"update docker compose command error: %s\", err.Error())\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "agent/init/cache/cache.go",
    "content": "package cache\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\tcachedb \"github.com/1Panel-dev/1Panel/agent/init/cache/db\"\n)\n\nfunc Init() {\n\tglobal.CACHE = cachedb.NewCacheDB()\n}\n"
  },
  {
    "path": "agent/init/cache/db/db.go",
    "content": "package badger_db\n\nimport (\n\t\"github.com/patrickmn/go-cache\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype Cache struct {\n\tdb *cache.Cache\n}\n\nfunc NewCacheDB() *Cache {\n\tdb := cache.New(5*time.Minute, 10*time.Minute)\n\treturn &Cache{\n\t\tdb: db,\n\t}\n}\n\nfunc (c *Cache) Set(key string, value interface{}) {\n\tc.db.Set(key, value, cache.DefaultExpiration)\n}\n\nfunc (c *Cache) SetWithTTL(key string, value interface{}, d time.Duration) {\n\tc.db.Set(key, value, d)\n}\nfunc (c *Cache) Del(key string) {\n\tc.db.Delete(key)\n}\n\nfunc (c *Cache) Clean() error {\n\treturn nil\n}\n\nfunc (c *Cache) Get(key string) string {\n\tobj, exist := c.db.Get(key)\n\tif !exist {\n\t\treturn \"\"\n\t}\n\treturn obj.(string)\n}\n\nfunc (c *Cache) PrefixScanKey(prefixStr string) []string {\n\tvar res []string\n\tvalues := c.db.Items()\n\tfor key := range values {\n\t\tif strings.HasPrefix(key, prefixStr) {\n\t\t\tres = append(res, key)\n\t\t}\n\t}\n\treturn res\n}\n"
  },
  {
    "path": "agent/init/db/db.go",
    "content": "package db\n\nimport (\n\t\"os\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\nfunc Init() {\n\tglobal.DB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, \"agent.db\"), \"agent\")\n\tglobal.TaskDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, \"task.db\"), \"task\")\n\tglobal.MonitorDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, \"monitor.db\"), \"monitor\")\n\tglobal.GPUMonitorDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, \"gpu_monitor.db\"), \"gpu_monitor\")\n\tglobal.AlertDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, \"alert.db\"), \"alert\")\n\n\tif _, err := os.Stat(path.Join(global.Dir.DbDir, \"core.db\")); err == nil {\n\t\tglobal.CoreDB = common.LoadDBConnByPath(path.Join(global.Dir.DbDir, \"core.db\"), \"core\")\n\t}\n}\n"
  },
  {
    "path": "agent/init/dir/dir.go",
    "content": "package dir\n\nimport (\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\nfunc Init() {\n\tfileOp := files.NewFileOp()\n\tbaseDir := global.CONF.Base.InstallDir\n\t_, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/docker/compose/\"))\n\n\tglobal.Dir.BaseDir, _ = fileOp.CreateDirWithPath(true, baseDir)\n\tglobal.Dir.DataDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel\"))\n\tglobal.Dir.DbDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/db\"))\n\tglobal.Dir.LogDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/log\"))\n\tglobal.Dir.TaskDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/log/task\"))\n\tglobal.Dir.TmpDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/tmp\"))\n\n\tglobal.Dir.AppDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/apps\"))\n\tglobal.Dir.ResourceDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/resource\"))\n\tglobal.Dir.IconCacheDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/resource/icon\"))\n\tglobal.Dir.AppResourceDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/resource/apps\"))\n\tglobal.Dir.AppInstallDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/apps\"))\n\tglobal.Dir.LocalAppResourceDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/resource/apps/local\"))\n\tglobal.Dir.LocalAppInstallDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/apps/local\"))\n\tglobal.Dir.RemoteAppResourceDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/resource/apps/remote\"))\n\tglobal.Dir.CustomAppResourceDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/resource/apps/custom\"))\n\tglobal.Dir.OfflineAppResourceDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/resource/offline\"))\n\tglobal.Dir.RuntimeDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/runtime\"))\n\tglobal.Dir.RecycleBinDir, _ = fileOp.CreateDirWithPath(true, \"/.1panel_clash\")\n\tglobal.Dir.SSLLogDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/log/ssl\"))\n\tglobal.Dir.McpDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/mcp\"))\n\tglobal.Dir.ConvertLogDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/log/convert\"))\n\tglobal.Dir.TensorRTLLMDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/ai/tensorrt_llm\"))\n\tglobal.Dir.FirewallDir, _ = fileOp.CreateDirWithPath(true, path.Join(baseDir, \"1panel/firewall\"))\n}\n"
  },
  {
    "path": "agent/init/firewall/firewall.go",
    "content": "package firewall\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall\"\n\tfirewallClient \"github.com/1Panel-dev/1Panel/agent/utils/firewall/client\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables\"\n)\n\nfunc Init() {\n\tif !needInit() {\n\t\treturn\n\t}\n\tInitPingStatus()\n\tglobal.LOG.Info(\"initializing firewall settings...\")\n\tclient, err := firewall.NewFirewallClient()\n\tif err != nil {\n\t\treturn\n\t}\n\tclientName := client.Name()\n\n\tsettingRepo := repo.NewISettingRepo()\n\tif clientName == \"ufw\" || clientName == \"iptables\" {\n\t\tif err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelForward, iptables.ForwardFileName); err != nil {\n\t\t\tglobal.LOG.Errorf(\"load forward rules from file failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tif err := iptables.LoadRulesFromFile(iptables.NatTab, iptables.Chain1PanelPreRouting, iptables.ForwardFileName1); err != nil {\n\t\t\tglobal.LOG.Errorf(\"load prerouting rules from file failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tif err := iptables.LoadRulesFromFile(iptables.NatTab, iptables.Chain1PanelPostRouting, iptables.ForwardFileName2); err != nil {\n\t\t\tglobal.LOG.Errorf(\"load postrouting rules from file failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tglobal.LOG.Infof(\"loaded iptables rules for forward from file successfully\")\n\n\t\tiptablesForwardStatus, _ := settingRepo.GetValueByKey(\"IptablesForwardStatus\")\n\t\tif iptablesForwardStatus == constant.StatusEnable {\n\t\t\tif err := firewallClient.EnableIptablesForward(); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"enable iptables forward failed, err: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tif clientName != \"iptables\" {\n\t\treturn\n\t}\n\tif err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.BasicBeforeFileName); err != nil {\n\t\tglobal.LOG.Errorf(\"load basic before rules from file failed, err: %v\", err)\n\t\treturn\n\t}\n\tif err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelBasic, iptables.BasicFileName); err != nil {\n\t\tglobal.LOG.Errorf(\"load basic rules from file failed, err: %v\", err)\n\t\treturn\n\t}\n\tif err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.BasicAfterFileName); err != nil {\n\t\tglobal.LOG.Errorf(\"load basic after rules from file failed, err: %v\", err)\n\t\treturn\n\t}\n\tpanelPort := service.LoadPanelPort()\n\tif len(panelPort) == 0 {\n\t\tglobal.LOG.Errorf(\"find 1panel service port failed\")\n\t\treturn\n\t}\n\tif err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, fmt.Sprintf(\"-p tcp -m tcp --dport %v -j ACCEPT\", panelPort)); err != nil {\n\t\tglobal.LOG.Errorf(\"add port accept rule %v failed, err: %v\", panelPort, err)\n\t\treturn\n\t}\n\tglobal.LOG.Infof(\"loaded iptables rules for basic from file successfully\")\n\tiptablesService := service.IptablesService{}\n\tiptablesStatus, _ := settingRepo.GetValueByKey(\"IptablesStatus\")\n\tif iptablesStatus == constant.StatusEnable {\n\t\tif err := iptablesService.Operate(dto.IptablesOp{Operate: \"bind-base-without-init\"}); err != nil {\n\t\t\tglobal.LOG.Errorf(\"bind base chains failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelInput, iptables.InputFileName); err != nil {\n\t\tglobal.LOG.Errorf(\"load input rules from file failed, err: %v\", err)\n\t\treturn\n\t}\n\tif err := iptables.LoadRulesFromFile(iptables.FilterTab, iptables.Chain1PanelOutput, iptables.OutputFileName); err != nil {\n\t\tglobal.LOG.Errorf(\"load output rules from file failed, err: %v\", err)\n\t\treturn\n\t}\n\tglobal.LOG.Infof(\"loaded iptables rules for input and output from file successfully\")\n\tiptablesInputStatus, _ := settingRepo.GetValueByKey(\"IptablesInputStatus\")\n\tif iptablesInputStatus == constant.StatusEnable {\n\t\tif err := iptablesService.Operate(dto.IptablesOp{Name: iptables.Chain1PanelInput, Operate: \"bind\"}); err != nil {\n\t\t\tglobal.LOG.Errorf(\"bind input chains failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n\tiptablesOutputStatus, _ := settingRepo.GetValueByKey(\"IptablesOutputStatus\")\n\tif iptablesOutputStatus == constant.StatusEnable {\n\t\tif err := iptablesService.Operate(dto.IptablesOp{Name: iptables.Chain1PanelOutput, Operate: \"bind\"}); err != nil {\n\t\t\tglobal.LOG.Errorf(\"bind output chains failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc needInit() bool {\n\tfile, err := os.OpenFile(\"/run/1panel_boot_mark\", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644)\n\tif err != nil {\n\t\tif os.IsExist(err) {\n\t\t\treturn false\n\t\t}\n\t\tglobal.LOG.Errorf(\"check boot mark file failed: %v\", err)\n\t\treturn true\n\t}\n\tdefer file.Close()\n\tfmt.Fprintf(file, \"Boot Mark for 1panel\\n\")\n\treturn true\n}\n\nfunc InitPingStatus() {\n\tglobal.LOG.Info(\"initializing ban ping status from settings...\")\n\tstatus := firewall.LoadPingStatus()\n\tstatusInDB, _ := repo.NewISettingRepo().GetValueByKey(\"BanPing\")\n\tif statusInDB == status {\n\t\treturn\n\t}\n\n\tenable := \"1\"\n\tif statusInDB == constant.StatusDisable {\n\t\tenable = \"0\"\n\t}\n\tif err := firewall.UpdatePingStatus(enable); err != nil {\n\t\tglobal.LOG.Errorf(\"initialize ping status failed: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "agent/init/hook/hook.go",
    "content": "package hook\n\nimport (\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/alert_push\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n)\n\nfunc Init() {\n\tinitGlobalData()\n\thandleCronjobStatus()\n\thandleClamStatus()\n\thandleRecordStatus()\n\thandleSnapStatus()\n\thandleOllamaModelStatus()\n\n\tloadLocalDir()\n\n\tinitDockerConf()\n\tinitAlertTask()\n\tinitMonitorDB()\n}\n\nfunc initGlobalData() {\n\tsettingRepo := repo.NewISettingRepo()\n\tif _, err := settingRepo.GetValueByKey(\"SystemStatus\"); err != nil {\n\t\t_ = settingRepo.Create(\"SystemStatus\", \"Free\")\n\t}\n\tif err := settingRepo.Update(\"SystemStatus\", \"Free\"); err != nil {\n\t\tglobal.LOG.Fatalf(\"init service before start failed, err: %v\", err)\n\t}\n\tnode, _ := xpack.LoadNodeInfo(false)\n\tif len(node.Version) != 0 {\n\t\t_ = settingRepo.Update(\"SystemVersion\", node.Version)\n\t}\n\tglobal.CONF.Base.Version = node.Version\n\tglobal.CONF.Base.Edition, _ = settingRepo.GetValueByKey(\"Edition\")\n\tglobal.CONF.Base.EncryptKey, _ = settingRepo.GetValueByKey(\"EncryptKey\")\n}\n\nfunc handleSnapStatus() {\n\t_ = global.DB.Model(&model.Snapshot{}).Where(\"status = ?\", \"OnSaveData\").\n\t\tUpdates(map[string]interface{}{\"status\": constant.StatusSuccess}).Error\n\n\t_ = global.DB.Model(&model.Snapshot{}).Where(\"status = ?\", constant.StatusWaiting).\n\t\tUpdates(map[string]interface{}{\n\t\t\t\"status\":  constant.StatusFailed,\n\t\t\t\"message\": constant.InterruptedMsg,\n\t\t}).Error\n\n\t_ = global.DB.Model(&model.Snapshot{}).Where(\"recover_status = ?\", constant.StatusWaiting).\n\t\tUpdates(map[string]interface{}{\n\t\t\t\"recover_status\":  constant.StatusFailed,\n\t\t\t\"recover_message\": constant.InterruptedMsg,\n\t\t}).Error\n\n\t_ = global.DB.Model(&model.Snapshot{}).Where(\"rollback_status = ?\", constant.StatusWaiting).\n\t\tUpdates(map[string]interface{}{\n\t\t\t\"rollback_status\":  constant.StatusFailed,\n\t\t\t\"rollback_message\": constant.InterruptedMsg,\n\t\t}).Error\n}\n\nfunc handleCronjobStatus() {\n\tvar jobRecords []model.JobRecords\n\t_ = global.DB.Model(&model.Cronjob{}).Where(\"is_executing = ?\", true).Updates(map[string]interface{}{\"is_executing\": false}).Error\n\t_ = global.DB.Where(\"status = ?\", constant.StatusWaiting).Find(&jobRecords).Error\n\tfor _, record := range jobRecords {\n\t\terr := global.DB.Model(&model.JobRecords{}).Where(\"status = ?\", constant.StatusWaiting).\n\t\t\tUpdates(map[string]interface{}{\n\t\t\t\t\"status\":  constant.StatusFailed,\n\t\t\t\t\"message\": constant.InterruptedMsg,\n\t\t\t}).Error\n\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"Failed to update job ID: %v, Error:%v\", record.ID, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tvar cronjob *model.Cronjob\n\t\t_ = global.DB.Where(\"id = ?\", record.CronjobID).First(&cronjob).Error\n\t\thandleCronJobAlert(cronjob)\n\t}\n}\n\nfunc handleClamStatus() {\n\t_ = global.DB.Model(&model.Clam{}).Where(\"is_executing = ?\", true).Updates(map[string]interface{}{\"is_executing\": false}).Error\n\t_ = global.DB.Model(&model.ClamRecord{}).Where(\"status = ?\", constant.StatusWaiting).Updates(map[string]interface{}{\n\t\t\"status\":  constant.StatusFailed,\n\t\t\"message\": constant.InterruptedMsg,\n\t}).Error\n}\n\nfunc handleRecordStatus() {\n\t_ = global.DB.Model(&model.BackupRecord{}).Where(\"status = ?\", constant.StatusWaiting).Updates(map[string]interface{}{\n\t\t\"status\":  constant.StatusFailed,\n\t\t\"message\": constant.InterruptedMsg,\n\t}).Error\n}\n\nfunc handleOllamaModelStatus() {\n\t_ = global.DB.Model(&model.OllamaModel{}).Where(\"status = ?\", constant.StatusWaiting).Updates(map[string]interface{}{\n\t\t\"status\":  constant.StatusCanceled,\n\t\t\"message\": constant.InterruptedMsg,\n\t}).Error\n}\n\nfunc handleCronJobAlert(cronjob *model.Cronjob) {\n\tpushAlert := dto.PushAlert{\n\t\tTaskName:  cronjob.Name,\n\t\tAlertType: cronjob.Type,\n\t\tEntryID:   cronjob.ID,\n\t\tParam:     cronjob.Type,\n\t}\n\t_ = alert_push.PushAlert(pushAlert)\n}\n\nfunc loadLocalDir() {\n\tvar account model.BackupAccount\n\tif err := global.DB.Where(\"`type` = ?\", constant.Local).First(&account).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"load local backup account info failed, err: %v\", err)\n\t\treturn\n\t}\n\tglobal.Dir.LocalBackupDir = account.BackupPath\n\n\tif _, err := os.Stat(account.BackupPath); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(account.BackupPath, os.ModePerm); err != nil {\n\t\t\tglobal.LOG.Errorf(\"mkdir %s failed, err: %v\", account.BackupPath, err)\n\t\t}\n\t}\n}\n\nfunc initDockerConf() {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"which docker\")\n\tif err != nil {\n\t\treturn\n\t}\n\tdockerPath := stdout\n\tif strings.Contains(dockerPath, \"snap\") {\n\t\tconstant.DaemonJsonPath = \"/var/snap/docker/current/config/daemon.json\"\n\t}\n}\n\nfunc initAlertTask() {\n\tservice.NewIAlertTaskHelper().ResetTask()\n}\n\nfunc initMonitorDB() {\n\t_ = global.MonitorDB.AutoMigrate(&model.MonitorBase{}, &model.MonitorNetwork{}, &model.MonitorGPU{}, &model.MonitorIO{})\n\t_ = global.TaskDB.AutoMigrate(&model.Task{})\n}\n"
  },
  {
    "path": "agent/init/lang/lang.go",
    "content": "package lang\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\nfunc Init() {\n\tgo initLang()\n}\n\nfunc initLang() {\n\tfileOp := files.NewFileOp()\n\tgeoPath := path.Join(global.CONF.Base.InstallDir, \"1panel/geo/GeoIP.mmdb\")\n\tisLangExist := fileOp.Stat(\"/usr/local/bin/lang/zh.sh\")\n\tisGeoExist := fileOp.Stat(geoPath)\n\tif isLangExist && isGeoExist {\n\t\treturn\n\t}\n\tupgradePath := path.Join(global.CONF.Base.InstallDir, \"1panel/tmp/upgrade\")\n\ttmpPath, err := loadRestorePath(upgradePath)\n\tupgradeDir := path.Join(upgradePath, tmpPath, \"downloads\")\n\tif err != nil || len(tmpPath) == 0 || !fileOp.Stat(upgradeDir) {\n\t\tif !isLangExist {\n\t\t\tdownloadLangFromRemote(fileOp)\n\t\t}\n\t\tif !isGeoExist {\n\t\t\tdownloadGeoFromRemote(fileOp, geoPath)\n\t\t}\n\t\treturn\n\t}\n\n\tfiles, _ := os.ReadDir(upgradeDir)\n\tif len(files) == 0 {\n\t\ttmpPath = \"no such file\"\n\t} else {\n\t\tfor _, item := range files {\n\t\t\tif item.IsDir() && strings.HasPrefix(item.Name(), \"1panel-\") {\n\t\t\t\ttmpPath = path.Join(upgradePath, tmpPath, \"downloads\", item.Name())\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif tmpPath == \"no such file\" || !fileOp.Stat(tmpPath) {\n\t\tif !isLangExist {\n\t\t\tdownloadLangFromRemote(fileOp)\n\t\t}\n\t\tif !isGeoExist {\n\t\t\tdownloadGeoFromRemote(fileOp, geoPath)\n\t\t}\n\t\treturn\n\t}\n\tif !isLangExist {\n\t\tif !fileOp.Stat(path.Join(tmpPath, \"lang\")) {\n\t\t\tdownloadLangFromRemote(fileOp)\n\t\t\treturn\n\t\t}\n\t\tif err := cmd.RunDefaultBashCf(\"cp -r %s %s\", path.Join(tmpPath, \"lang\"), \"/usr/local/bin/\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"load lang from package failed, %v\", err)\n\t\t\treturn\n\t\t}\n\t\tglobal.LOG.Info(\"init lang successful\")\n\t}\n\tif !isGeoExist {\n\t\tif !fileOp.Stat(path.Join(tmpPath, \"GeoIP.mmdb\")) {\n\t\t\tdownloadGeoFromRemote(fileOp, geoPath)\n\t\t\treturn\n\t\t}\n\t\tif err := cmd.RunDefaultBashCf(\"mkdir %s && cp %s %s/\", path.Dir(geoPath), path.Join(tmpPath, \"GeoIP.mmdb\"), path.Dir(geoPath)); err != nil {\n\t\t\tglobal.LOG.Errorf(\"load geo ip from package failed, %v\", err)\n\t\t\treturn\n\t\t}\n\t\tglobal.LOG.Info(\"init geo ip successful\")\n\t}\n}\n\nfunc loadRestorePath(upgradeDir string) (string, error) {\n\tif _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) {\n\t\treturn \"no such file\", nil\n\t}\n\tfiles, err := os.ReadDir(upgradeDir)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar folders []string\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tfolders = append(folders, file.Name())\n\t\t}\n\t}\n\tif len(folders) == 0 {\n\t\treturn \"no such file\", nil\n\t}\n\tsort.Slice(folders, func(i, j int) bool {\n\t\treturn folders[i] > folders[j]\n\t})\n\treturn folders[0], nil\n}\n\nfunc downloadLangFromRemote(fileOp files.FileOp) {\n\tpath := fmt.Sprintf(\"%s/language/lang.tar.gz\", global.RepoURL())\n\tif err := fileOp.DownloadFile(path, \"/usr/local/bin/lang.tar.gz\"); err != nil {\n\t\tglobal.LOG.Errorf(\"download lang.tar.gz failed, err: %v\", err)\n\t\treturn\n\t}\n\tif !fileOp.Stat(\"/usr/local/bin/lang.tar.gz\") {\n\t\tglobal.LOG.Error(\"download lang.tar.gz failed, no such file\")\n\t\treturn\n\t}\n\tif err := cmd.RunDefaultBashCf(\"tar zxvfC %s %s\", \"/usr/local/bin/lang.tar.gz\", \"/usr/local/bin/\"); err != nil {\n\t\tglobal.LOG.Errorf(\"decompress lang.tar.gz failed, %v\", err)\n\t\treturn\n\t}\n\t_ = os.Remove(\"/usr/local/bin/lang.tar.gz\")\n\tglobal.LOG.Info(\"download lang successful\")\n}\nfunc downloadGeoFromRemote(fileOp files.FileOp, targetPath string) {\n\t_ = os.MkdirAll(path.Dir(targetPath), os.ModePerm)\n\tpathItem := fmt.Sprintf(\"%s/geo/GeoIP.mmdb\", global.RepoURL())\n\tif err := fileOp.DownloadFile(pathItem, targetPath); err != nil {\n\t\tglobal.LOG.Errorf(\"download geo ip failed, err: %v\", err)\n\t\treturn\n\t}\n\tglobal.LOG.Info(\"download geo ip successful\")\n}\n"
  },
  {
    "path": "agent/init/log/log.go",
    "content": "package log\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/log\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\n\t\"github.com/sirupsen/logrus\"\n)\n\nconst (\n\tTimeFormat         = \"2006-01-02 15:04:05\"\n\tFileTImeFormat     = \"2006-01-02\"\n\tRollingTimePattern = \"0 0  * * *\"\n)\n\nfunc Init() {\n\tl := logrus.New()\n\tsetOutput(l, (global.CONF.Log))\n\tglobal.LOG = l\n\tglobal.LOG.Info(\"init logger successfully\")\n}\n\nfunc setOutput(logger *logrus.Logger, config global.LogConfig) {\n\twriter, err := log.NewWriterFromConfig(&log.Config{\n\t\tLogPath:            global.Dir.LogDir,\n\t\tFileName:           config.LogName,\n\t\tTimeTagFormat:      FileTImeFormat,\n\t\tMaxRemain:          config.MaxBackup,\n\t\tRollingTimePattern: RollingTimePattern,\n\t\tLogSuffix:          config.LogSuffix,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tlevel, err := logrus.ParseLevel(config.Level)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfileAndStdoutWriter := io.MultiWriter(writer, os.Stdout)\n\n\tlogger.SetOutput(fileAndStdoutWriter)\n\tlogger.SetLevel(level)\n\tlogger.SetFormatter(new(MineFormatter))\n}\n\ntype MineFormatter struct{}\n\nfunc (s *MineFormatter) Format(entry *logrus.Entry) ([]byte, error) {\n\tdetailInfo := \"\"\n\tif entry.Caller != nil {\n\t\tfunction := strings.ReplaceAll(entry.Caller.Function, \"github.com/1Panel-dev/1Panel/agent/\", \"\")\n\t\tdetailInfo = fmt.Sprintf(\"(%s: %d)\", function, entry.Caller.Line)\n\t}\n\tif len(entry.Data) == 0 {\n\t\tmsg := fmt.Sprintf(\"[%s] [%s] %s %s \\n\", time.Now().Format(TimeFormat), strings.ToUpper(entry.Level.String()), entry.Message, detailInfo)\n\t\treturn []byte(msg), nil\n\t}\n\tmsg := fmt.Sprintf(\"[%s] [%s] %s %s {%v} \\n\", time.Now().Format(TimeFormat), strings.ToUpper(entry.Level.String()), entry.Message, detailInfo, entry.Data)\n\treturn []byte(msg), nil\n}\n"
  },
  {
    "path": "agent/init/migration/migrate.go",
    "content": "package migration\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/migration/migrations\"\n\n\t\"github.com/go-gormigrate/gormigrate/v2\"\n)\n\nfunc Init() {\n\tInitAgentDB()\n\tInitTaskDB()\n\tglobal.LOG.Info(\"Migration run successfully\")\n}\n\nfunc InitAgentDB() {\n\tm := gormigrate.New(global.DB, gormigrate.DefaultOptions, []*gormigrate.Migration{\n\t\tmigrations.AddTable,\n\t\tmigrations.AddMonitorTable,\n\t\tmigrations.InitSetting,\n\t\tmigrations.InitImageRepo,\n\t\tmigrations.InitDefaultCA,\n\t\tmigrations.InitPHPExtensions,\n\t\tmigrations.InitBackup,\n\t\tmigrations.InitDefault,\n\t\tmigrations.UpdateWebsiteExpireDate,\n\t\tmigrations.UpdateRuntime,\n\t\tmigrations.AddSnapshotRule,\n\t\tmigrations.UpdatePHPRuntime,\n\t\tmigrations.AddSnapshotIgnore,\n\t\tmigrations.InitAppLauncher,\n\t\tmigrations.AddTableAlert,\n\t\tmigrations.InitAlertConfig,\n\t\tmigrations.AddMethodToAlertLog,\n\t\tmigrations.AddMethodToAlertTask,\n\t\tmigrations.UpdateMcpServer,\n\t\tmigrations.InitCronjobGroup,\n\t\tmigrations.AddColumnToAlert,\n\t\tmigrations.UpdateWebsiteSSL,\n\t\tmigrations.AddQuickJump,\n\t\tmigrations.UpdateMcpServerAddType,\n\t\tmigrations.InitLocalSSHConn,\n\t\tmigrations.InitLocalSSHShow,\n\t\tmigrations.InitRecordStatus,\n\t\tmigrations.AddShowNameForQuickJump,\n\t\tmigrations.AddAgentQuickJump,\n\t\tmigrations.AddTimeoutForClam,\n\t\tmigrations.UpdateCronjobSpec,\n\t\tmigrations.UpdateWebsiteSSLAddColumn,\n\t\tmigrations.AddTensorRTLLMModel,\n\t\tmigrations.UpdateMonitorInterval,\n\t\tmigrations.AddMonitorProcess,\n\t\tmigrations.UpdateCronJob,\n\t\tmigrations.UpdateTensorrtLLM,\n\t\tmigrations.AddIptablesFilterRuleTable,\n\t\tmigrations.AddCommonDescription,\n\t\tmigrations.UpdateDatabase,\n\t\tmigrations.AddGPUMonitor,\n\t\tmigrations.UpdateDatabaseMysql,\n\t\tmigrations.InitIptablesStatus,\n\t\tmigrations.UpdateWebsite,\n\t\tmigrations.AddisIPtoWebsiteSSL,\n\t\tmigrations.InitPingStatus,\n\t\tmigrations.UpdateApp,\n\t\tmigrations.AddCronjobArgs,\n\t\tmigrations.AddWebsiteAcmeAccountColumn,\n\t\tmigrations.AddAgentTables,\n\t\tmigrations.MigrateOpenclawAgents,\n\t\tmigrations.AddAgentCustomModelFields,\n\t\tmigrations.AddAppInstallSortOrder,\n\t\tmigrations.AddAgentAccountRememberAPIKey,\n\t\tmigrations.AddEditionSetting,\n\t\tmigrations.AddAgentTypeForAgents,\n\t\tmigrations.NormalizeAgentAccountVerifiedStatus,\n\t\tmigrations.NormalizeOllamaAccountAPIType,\n\t\tmigrations.RewriteOpenclawBundledCaddyfile,\n\t\tmigrations.InitAgentAccountModelPool,\n\t\tmigrations.AddHostTable,\n\t\tmigrations.AddAITerminalSettings,\n\t})\n\tif err := m.Migrate(); err != nil {\n\t\tglobal.LOG.Error(err)\n\t\tpanic(err)\n\t}\n}\n\nfunc InitTaskDB() {\n\tm := gormigrate.New(global.TaskDB, gormigrate.DefaultOptions, []*gormigrate.Migration{\n\t\tmigrations.AddTaskTable,\n\t})\n\tif err := m.Migrate(); err != nil {\n\t\tglobal.LOG.Error(err)\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "agent/init/migration/migrations/init.go",
    "content": "package migrations\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/user\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/request\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\tmigrationutils \"github.com/1Panel-dev/1Panel/agent/init/migration/migrations/utils\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/copier\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ssh\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\n\t\"github.com/go-gormigrate/gormigrate/v2\"\n\t\"gorm.io/gorm\"\n)\n\nvar AddTable = &gormigrate.Migration{\n\tID: \"20250930-add-table\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(\n\t\t\t&model.AppDetail{},\n\t\t\t&model.AppInstallResource{},\n\t\t\t&model.AppInstall{},\n\t\t\t&model.AppTag{},\n\t\t\t&model.Tag{},\n\t\t\t&model.App{},\n\t\t\t&model.AppLauncher{},\n\t\t\t&model.OllamaModel{},\n\t\t\t&model.BackupAccount{},\n\t\t\t&model.BackupRecord{},\n\t\t\t&model.Clam{},\n\t\t\t&model.ComposeTemplate{},\n\t\t\t&model.Compose{},\n\t\t\t&model.Cronjob{},\n\t\t\t&model.Database{},\n\t\t\t&model.DatabaseMysql{},\n\t\t\t&model.DatabasePostgresql{},\n\t\t\t&model.Favorite{},\n\t\t\t&model.Firewall{},\n\t\t\t&model.Host{},\n\t\t\t&model.Ftp{},\n\t\t\t&model.ImageRepo{},\n\t\t\t&model.ScriptLibrary{},\n\t\t\t&model.JobRecords{},\n\t\t\t&model.MonitorBase{},\n\t\t\t&model.MonitorIO{},\n\t\t\t&model.MonitorNetwork{},\n\t\t\t&model.PHPExtensions{},\n\t\t\t&model.Runtime{},\n\t\t\t&model.Setting{},\n\t\t\t&model.Snapshot{},\n\t\t\t&model.Tag{},\n\t\t\t&model.Website{},\n\t\t\t&model.WebsiteAcmeAccount{},\n\t\t\t&model.WebsiteCA{},\n\t\t\t&model.WebsiteDnsAccount{},\n\t\t\t&model.WebsiteDomain{},\n\t\t\t&model.WebsiteSSL{},\n\t\t\t&model.Group{},\n\t\t\t&model.AppIgnoreUpgrade{},\n\t\t\t&model.McpServer{},\n\t\t\t&model.RootCert{},\n\t\t\t&model.ClamRecord{},\n\t\t)\n\t},\n}\n\nvar AddMonitorTable = &gormigrate.Migration{\n\tID: \"20240813-add-monitor-table\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn global.MonitorDB.AutoMigrate(\n\t\t\t&model.MonitorBase{},\n\t\t\t&model.MonitorIO{},\n\t\t\t&model.MonitorNetwork{},\n\t\t)\n\t},\n}\n\nvar InitSetting = &gormigrate.Migration{\n\tID: \"20240722-init-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tglobal.CONF.Base.EncryptKey = common.RandStr(16)\n\t\tnodeInfo, err := xpack.LoadNodeInfo(true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"BaseDir\", Value: nodeInfo.BaseDir}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\titemKey, _ := encrypt.StringEncrypt(nodeInfo.ServerKey)\n\t\tif err := tx.Create(&model.Setting{Key: \"ServerKey\", Value: itemKey}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\titemCrt, _ := encrypt.StringEncrypt(nodeInfo.ServerCrt)\n\t\tif err := tx.Create(&model.Setting{Key: \"ServerCrt\", Value: itemCrt}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"NodeScope\", Value: nodeInfo.Scope}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"NodePort\", Value: fmt.Sprintf(\"%v\", nodeInfo.NodePort)}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SystemVersion\", Value: nodeInfo.Version}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"EncryptKey\", Value: global.CONF.Base.EncryptKey}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"DockerSockPath\", Value: \"unix:///var/run/docker.sock\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SystemStatus\", Value: \"Free\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tlang := common.LoadParamsWithoutPanic(\"LANGUAGE\")\n\t\tif err := tx.Create(&model.Setting{Key: \"Language\", Value: lang}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SystemIP\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"LocalTime\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"TimeZone\", Value: common.LoadTimeZoneByCmd()}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"NtpSite\", Value: \"pool.ntp.org\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"LastCleanTime\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"LastCleanSize\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"LastCleanData\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"DefaultNetwork\", Value: \"all\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"MonitorStatus\", Value: constant.StatusEnable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"MonitorStoreDays\", Value: \"7\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"MonitorInterval\", Value: \"5\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyType\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyUrl\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyPort\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyUser\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyPasswd\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"AppStoreVersion\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"AppStoreSyncStatus\", Value: \"SyncSuccess\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"AppStoreLastModified\", Value: \"0\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"FileRecycleBin\", Value: constant.StatusEnable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"LocalSSHConn\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t},\n}\n\nvar InitImageRepo = &gormigrate.Migration{\n\tID: \"20240722-init-imagerepo\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\titem := &model.ImageRepo{\n\t\t\tName:        \"Docker Hub\",\n\t\t\tProtocol:    \"https\",\n\t\t\tDownloadUrl: \"docker.io\",\n\t\t\tStatus:      constant.StatusSuccess,\n\t\t}\n\t\tif err := tx.Create(item).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitDefaultCA = &gormigrate.Migration{\n\tID: \"20240722-init-default-ca\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tcaService := service.NewIWebsiteCAService()\n\t\tif _, err := caService.Create(request.WebsiteCACreate{\n\t\t\tCommonName:       \"1Panel-CA\",\n\t\t\tCountry:          \"CN\",\n\t\t\tKeyType:          \"P256\",\n\t\t\tName:             \"1Panel\",\n\t\t\tOrganization:     \"FIT2CLOUD\",\n\t\t\tOrganizationUint: \"1Panel\",\n\t\t\tProvince:         \"Beijing\",\n\t\t\tCity:             \"Beijing\",\n\t\t}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitPHPExtensions = &gormigrate.Migration{\n\tID: \"20240722-add-php-extensions\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tphpExtensions := []model.PHPExtensions{\n\t\t\t{\n\t\t\t\tName:       \"Default\",\n\t\t\t\tExtensions: \"bcmath,ftp,gd,gettext,intl,mysqli,pcntl,pdo_mysql,shmop,soap,sockets,sysvsem,xmlrpc,zip\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:       \"WordPress\",\n\t\t\t\tExtensions: \"exif,igbinary,imagick,intl,zip,apcu,memcached,opcache,redis,shmop,mysqli,pdo_mysql,gd\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:       \"Flarum\",\n\t\t\t\tExtensions: \"curl,gd,pdo_mysql,mysqli,bz2,exif,yaf,imap\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:       \"SeaCMS\",\n\t\t\t\tExtensions: \"mysqli,pdo_mysql,gd,curl\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:       \"Dev\",\n\t\t\t\tExtensions: \"bcmath,ftp,gd,gettext,intl,mysqli,pcntl,pdo_mysql,shmop,soap,sockets,sysvsem,xmlrpc,zip,exif,igbinary,imagick,apcu,memcached,opcache,redis,bc,image,dom,iconv,mbstring,mysqlnd,openssl,pdo,tokenizer,xml,curl,bz2,yaf,imap,xdebug,swoole,pdo_pgsql,fileinfo,pgsql,calendar,gmp\",\n\t\t\t},\n\t\t}\n\n\t\tfor _, ext := range phpExtensions {\n\t\t\tif err := tx.Create(&ext).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddTaskTable = &gormigrate.Migration{\n\tID: \"20241226-add-task\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(\n\t\t\t&model.Task{},\n\t\t)\n\t},\n}\n\nvar InitBackup = &gormigrate.Migration{\n\tID: \"20241226-init-backup\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.BackupAccount{\n\t\t\tName:       \"localhost\",\n\t\t\tType:       \"LOCAL\",\n\t\t\tBackupPath: path.Join(global.Dir.DataDir, \"backup\"),\n\t\t}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitDefault = &gormigrate.Migration{\n\tID: \"20250301-init-default\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"website\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateWebsiteExpireDate = &gormigrate.Migration{\n\tID: \"20250304-update-website\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\ttargetDate := time.Date(9999, 12, 31, 0, 0, 0, 0, time.UTC)\n\n\t\tif err := tx.Model(&model.Website{}).\n\t\t\tWhere(\"expire_date = ?\", time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)).\n\t\t\tUpdate(\"expire_date\", targetDate).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateRuntime = &gormigrate.Migration{\n\tID: \"20250624-update-runtime\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(\n\t\t\t&model.Runtime{},\n\t\t)\n\t},\n}\n\nvar AddSnapshotRule = &gormigrate.Migration{\n\tID: \"20250703-add-snapshot-rule\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(\n\t\t\t&model.Cronjob{},\n\t\t)\n\t},\n}\nvar UpdatePHPRuntime = &gormigrate.Migration{\n\tID: \"20250702-update-php-runtime\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tservice.HandleOldPHPRuntime()\n\t\treturn nil\n\t},\n}\nvar AddSnapshotIgnore = &gormigrate.Migration{\n\tID: \"20250716-add-snapshot-ignore\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(\n\t\t\t&model.Snapshot{},\n\t\t)\n\t},\n}\n\nvar InitAppLauncher = &gormigrate.Migration{\n\tID: \"20250702-init-app-launcher\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tlaunchers := []string{\"openresty\", \"mysql\", \"halo\", \"redis\", \"maxkb\", \"wordpress\"}\n\t\tfor _, val := range launchers {\n\t\t\tvar item model.AppLauncher\n\t\t\t_ = tx.Model(&model.AppLauncher{}).Where(\"key = ?\", val).First(&item).Error\n\t\t\tif item.ID == 0 {\n\t\t\t\titem.Key = val\n\t\t\t\t_ = tx.Create(&item).Error\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddTableAlert = &gormigrate.Migration{\n\tID: \"20250122-add-table-alert\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn global.AlertDB.AutoMigrate(&model.Alert{}, &model.AlertTask{}, model.AlertLog{}, model.AlertConfig{})\n\t},\n}\n\nvar InitAlertConfig = &gormigrate.Migration{\n\tID: \"20250705-init-alert-config\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\trecords := []model.AlertConfig{\n\t\t\t{\n\t\t\t\tType:   \"sms\",\n\t\t\t\tTitle:  \"xpack.alert.smsConfig\",\n\t\t\t\tStatus: \"Enable\",\n\t\t\t\tConfig: `{\"alertDailyNum\":50}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tType:   \"common\",\n\t\t\t\tTitle:  \"xpack.alert.commonConfig\",\n\t\t\t\tStatus: \"Enable\",\n\t\t\t\tConfig: `{\"isOffline\":\"Disable\",\"alertSendTimeRange\":{\"noticeAlert\":{\"sendTimeRange\":\"08:00:00 - 23:59:59\",\"type\":[\"ssl\",\"siteEndTime\",\"panelPwdEndTime\",\"panelUpdate\"]},\"resourceAlert\":{\"sendTimeRange\":\"00:00:00 - 23:59:59\",\"type\":[\"clams\",\"cronJob\",\"cpu\",\"memory\",\"load\",\"disk\"]}}}`,\n\t\t\t},\n\t\t}\n\t\tfor _, r := range records {\n\t\t\tif err := global.AlertDB.Model(&model.AlertConfig{}).Create(&r).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddMethodToAlertLog = &gormigrate.Migration{\n\tID: \"20250713-add-method-to-alert_log\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := global.AlertDB.AutoMigrate(&model.AlertLog{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := global.AlertDB.Model(&model.AlertLog{}).Where(\"method IS NULL OR method = ''\").Update(\"method\", \"sms\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddMethodToAlertTask = &gormigrate.Migration{\n\tID: \"20250723-add-method-to-alert_task\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := global.AlertDB.AutoMigrate(&model.AlertTask{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := global.AlertDB.Model(&model.AlertTask{}).Where(\"method IS NULL OR method = ''\").Update(\"method\", \"sms\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateMcpServer = &gormigrate.Migration{\n\tID: \"20250729-update-mcp-server\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.McpServer{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.McpServer{}).Where(\"1=1\").Update(\"output_transport\", \"sse\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitCronjobGroup = &gormigrate.Migration{\n\tID: \"20250805-init-cronjob-group\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.Cronjob{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.Cronjob{}).Where(\"1=1\").Updates(map[string]interface{}{\"group_id\": 0}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddColumnToAlert = &gormigrate.Migration{\n\tID: \"20250729-add-column-to-alert\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := global.AlertDB.AutoMigrate(&model.Alert{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := global.AlertDB.Model(&model.Alert{}).\n\t\t\tWhere(\"advanced_params IS NULL\").\n\t\t\tUpdate(\"advanced_params\", \"\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateWebsiteSSL = &gormigrate.Migration{\n\tID: \"20250819-update-website-ssl\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddQuickJump = &gormigrate.Migration{\n\tID: \"20250901-add-quick-jump\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.QuickJump{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.QuickJump{Name: \"Website\", Title: \"menu.website\", Recommend: 10, IsShow: true, Router: \"/websites\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.QuickJump{Name: \"Database\", Title: \"home.database\", Recommend: 30, IsShow: true, Router: \"/databases\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.QuickJump{Name: \"Cronjob\", Title: \"menu.cronjob\", Recommend: 50, IsShow: true, Router: \"/cronjobs\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.QuickJump{Name: \"AppInstalled\", Title: \"home.appInstalled\", Recommend: 70, IsShow: true, Router: \"/apps/installed\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.QuickJump{Name: \"File\", Detail: \"/\", Title: \"home.quickDir\", Recommend: 90, IsShow: false, Router: \"/hosts/files\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateMcpServerAddType = &gormigrate.Migration{\n\tID: \"20250904-update-mcp-server\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.McpServer{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.McpServer{}).Where(\"1=1\").Update(\"type\", \"npx\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitLocalSSHConn = &gormigrate.Migration{\n\tID: \"20250905-init-local-ssh\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\titemPath := \"\"\n\t\tcurrentInfo, _ := user.Current()\n\t\tif len(currentInfo.HomeDir) == 0 {\n\t\t\titemPath = \"/root/.ssh/id_ed25519_1panel\"\n\t\t} else {\n\t\t\titemPath = path.Join(currentInfo.HomeDir, \".ssh/id_ed25519_1panel\")\n\t\t}\n\t\tif _, err := os.Stat(itemPath); err != nil {\n\t\t\t_ = service.NewISSHService().CreateRootCert(dto.RootCertOperate{EncryptionMode: \"ed25519\", Name: \"id_ed25519_1panel\", Description: \"1Panel Terminal\"})\n\t\t}\n\t\tprivateKey, _ := os.ReadFile(itemPath)\n\t\tconnWithKey := ssh.ConnInfo{\n\t\t\tAddr:       \"127.0.0.1\",\n\t\t\tUser:       \"root\",\n\t\t\tPort:       22,\n\t\t\tAuthMode:   \"key\",\n\t\t\tPrivateKey: privateKey,\n\t\t}\n\t\tif _, err := ssh.NewClient(connWithKey); err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tvar conn model.LocalConnInfo\n\t\t_ = copier.Copy(&conn, &connWithKey)\n\t\tconn.PrivateKey = string(privateKey)\n\t\tconn.PassPhrase = \"\"\n\t\tlocalConn, _ := json.Marshal(&conn)\n\t\tconnAfterEncrypt, _ := encrypt.StringEncrypt(string(localConn))\n\t\tif err := tx.Model(&model.Setting{}).Where(\"key = ?\", \"LocalSSHConn\").Updates(map[string]interface{}{\"value\": connAfterEncrypt}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitLocalSSHShow = &gormigrate.Migration{\n\tID: \"20250908-init-local-ssh-show\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Setting{Key: \"LocalSSHConnShow\", Value: constant.StatusEnable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitRecordStatus = &gormigrate.Migration{\n\tID: \"20250910-init-record-status\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.BackupRecord{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.BackupRecord{}).Where(\"1 == 1\").Updates(map[string]interface{}{\"status\": constant.StatusSuccess}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddShowNameForQuickJump = &gormigrate.Migration{\n\tID: \"20250918-add-show-name-for-quick-jump\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.QuickJump{})\n\t},\n}\n\nvar AddAgentQuickJump = &gormigrate.Migration{\n\tID: \"20260312-add-agent-quick-jump\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.QuickJump{}); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar quicks []model.QuickJump\n\t\tif err := tx.Find(&quicks).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar (\n\t\t\tcronjob  *model.QuickJump\n\t\t\tdatabase *model.QuickJump\n\t\t\tshowList []*model.QuickJump\n\t\t)\n\t\tfor i := range quicks {\n\t\t\tswitch quicks[i].Name {\n\t\t\tcase \"Cronjob\":\n\t\t\t\tcronjob = &quicks[i]\n\t\t\tcase \"Database\":\n\t\t\t\tdatabase = &quicks[i]\n\t\t\t}\n\t\t\tif quicks[i].IsShow {\n\t\t\t\tshowList = append(showList, &quicks[i])\n\t\t\t}\n\t\t}\n\n\t\tshowCount := len(showList)\n\t\tupdatedIDs := make(map[uint]struct{})\n\t\tkickedCronjob := false\n\t\tif showCount >= 4 && cronjob != nil && cronjob.IsShow {\n\t\t\tcronjob.IsShow = false\n\t\t\tupdatedIDs[cronjob.ID] = struct{}{}\n\t\t\tkickedCronjob = true\n\t\t}\n\t\tif !kickedCronjob && showCount >= 4 && database != nil && database.IsShow {\n\t\t\tdatabase.IsShow = false\n\t\t\tupdatedIDs[database.ID] = struct{}{}\n\t\t}\n\n\t\tfor _, item := range quicks {\n\t\t\tif _, ok := updatedIDs[item.ID]; !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := tx.Model(&model.QuickJump{}).Where(\"id = ?\", item.ID).Update(\"is_show\", item.IsShow).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn tx.Create(&model.QuickJump{\n\t\t\tName:      \"Agent\",\n\t\t\tTitle:     \"aiTools.agents.agents\",\n\t\t\tRecommend: 1,\n\t\t\tIsShow:    true,\n\t\t\tRouter:    \"/ai/agents/agent\",\n\t\t}).Error\n\t},\n}\n\nvar AddTimeoutForClam = &gormigrate.Migration{\n\tID: \"20250922-add-timeout-for-clam\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.Clam{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.Clam{}).Where(\"1 == 1\").Updates(map[string]interface{}{\"timeout\": 18000}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateCronjobSpec = &gormigrate.Migration{\n\tID: \"20250925-update-cronjob-spec\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar cronjobs []model.Cronjob\n\t\tif err := tx.Where(\"1 == 1\").Find(&cronjobs).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, item := range cronjobs {\n\t\t\tif !strings.Contains(item.Spec, \",\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := tx.Model(&model.Cronjob{}).Where(\"id = ?\", item.ID).Updates(\n\t\t\t\tmap[string]interface{}{\"spec\": strings.ReplaceAll(item.Spec, \",\", \"&&\")}).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateWebsiteSSLAddColumn = &gormigrate.Migration{\n\tID: \"20250928-update-website-ssl\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddTensorRTLLMModel = &gormigrate.Migration{\n\tID: \"20251018-add-tensorrt-llm-model\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.TensorRTLLM{})\n\t},\n}\n\nvar UpdateMonitorInterval = &gormigrate.Migration{\n\tID: \"20251026-update-monitor-interval\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar monitorInterval model.Setting\n\t\tif err := tx.Where(\"key = ?\", \"MonitorInterval\").First(&monitorInterval).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinterval, _ := strconv.Atoi(monitorInterval.Value)\n\t\tif interval == 0 {\n\t\t\tinterval = 300\n\t\t}\n\t\tif err := tx.Model(&model.Setting{}).\n\t\t\tWhere(\"key = ?\", \"MonitorInterval\").\n\t\t\tUpdates(map[string]interface{}{\"value\": fmt.Sprintf(\"%v\", interval*60)}).\n\t\t\tError; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"DefaultIO\", Value: \"all\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddIptablesFilterRuleTable = &gormigrate.Migration{\n\tID: \"20251106-add-iptables-filter-rule-table\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.Firewall{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar firewalls []model.Firewall\n\t\t_ = tx.Where(\"1 = 1\").Find(&firewalls).Error\n\n\t\tfirewallType := \"\"\n\t\tclient, err := firewall.NewFirewallClient()\n\t\tif err == nil {\n\t\t\tfirewallType = client.Name()\n\t\t}\n\t\tfor _, item := range firewalls {\n\t\t\tif err := tx.Model(&model.Firewall{}).\n\t\t\t\tWhere(\"id = ?\", item.ID).\n\t\t\t\tUpdates(map[string]interface{}{\"dst_port\": item.Port, \"src_ip\": item.Address, \"firewall_type\": firewallType}); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"update firewall failed, err: %v\", err)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddMonitorProcess = &gormigrate.Migration{\n\tID: \"20251030-add-monitor-process\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn global.MonitorDB.AutoMigrate(&model.MonitorBase{})\n\t},\n}\n\nvar UpdateCronJob = &gormigrate.Migration{\n\tID: \"20251105-update-cronjob\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.Cronjob{})\n\t},\n}\n\nvar UpdateTensorrtLLM = &gormigrate.Migration{\n\tID: \"20251110-update-tensorrt-llm\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.TensorRTLLM{})\n\t},\n}\n\nvar AddCommonDescription = &gormigrate.Migration{\n\tID: \"20251117-add-common-description\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.CommonDescription{})\n\t},\n}\n\nvar UpdateDatabase = &gormigrate.Migration{\n\tID: \"20251117-update-database\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.Database{})\n\t},\n}\n\nvar AddGPUMonitor = &gormigrate.Migration{\n\tID: \"20251122-add-gpu-monitor\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn global.GPUMonitorDB.AutoMigrate(&model.MonitorGPU{})\n\t},\n}\n\nvar UpdateDatabaseMysql = &gormigrate.Migration{\n\tID: \"20251126-update-database-mysql\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.DatabaseMysql{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar data []model.DatabaseMysql\n\t\t_ = tx.Where(\"1 = 1\").Find(&data).Error\n\t\tfor _, item := range data {\n\t\t\tif len(item.Collation) == 0 {\n\t\t\t\tcollation := \"\"\n\t\t\t\tswitch item.Format {\n\t\t\t\tcase \"utf8\":\n\t\t\t\t\tcollation = \"utf8_general_ci\"\n\t\t\t\tcase \"utf8mb4\":\n\t\t\t\t\tcollation = \"utf8mb4_general_ci\"\n\t\t\t\tcase \"gbk\":\n\t\t\t\t\tcollation = \"gbk_chinese_ci\"\n\t\t\t\tcase \"big5\":\n\t\t\t\t\tcollation = \"big5_chinese_ci\"\n\t\t\t\tdefault:\n\t\t\t\t\tcollation = \"utf8mb4_general_ci\"\n\t\t\t\t}\n\t\t\t\t_ = tx.Model(&model.DatabaseMysql{}).Where(\"id = ?\", item.ID).Updates(map[string]interface{}{\"collation\": collation}).Error\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitIptablesStatus = &gormigrate.Migration{\n\tID: \"20251201-init-iptables-status\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Setting{Key: \"IptablesStatus\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"IptablesForwardStatus\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"IptablesInputStatus\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"IptablesOutputStatus\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateWebsite = &gormigrate.Migration{\n\tID: \"20251203-update-website\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.Website{})\n\t},\n}\n\nvar AddisIPtoWebsiteSSL = &gormigrate.Migration{\n\tID: \"20251223-update-website-ssl\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.WebsiteSSL{})\n\t},\n}\n\nvar InitPingStatus = &gormigrate.Migration{\n\tID: \"20251201-init-ping-status\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tstatus := firewall.LoadPingStatus()\n\t\tif err := tx.Create(&model.Setting{Key: \"BanPing\", Value: status}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateApp = &gormigrate.Migration{\n\tID: \"20251228-update-app\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.App{})\n\t},\n}\n\nvar AddCronjobArgs = &gormigrate.Migration{\n\tID: \"20260106-add-cronjob-args\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.Cronjob{})\n\t},\n}\n\nvar AddWebsiteAcmeAccountColumn = &gormigrate.Migration{\n\tID: \"20260110-add-website-acme-account\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.WebsiteAcmeAccount{})\n\t},\n}\n\nvar AddAgentTables = &gormigrate.Migration{\n\tID: \"20260205-add-agent-tables\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(\n\t\t\t&model.Agent{},\n\t\t\t&model.AgentAccount{},\n\t\t)\n\t},\n}\n\nvar MigrateOpenclawAgents = &gormigrate.Migration{\n\tID: \"20260207-migrate-openclaw-agents\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar installs []model.AppInstall\n\t\tif err := tx.Preload(\"App\").Find(&installs).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, install := range installs {\n\t\t\tappKey := install.App.Key\n\t\t\tif appKey == \"\" || install.App.Resource == \"\" {\n\t\t\t\tvar app model.App\n\t\t\t\tif err := tx.First(&app, install.AppId).Error; err == nil {\n\t\t\t\t\tinstall.App = app\n\t\t\t\t\tappKey = app.Key\n\t\t\t\t}\n\t\t\t}\n\t\t\tif appKey != constant.AppOpenclaw {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar count int64\n\t\t\tif err := tx.Model(&model.Agent{}).Where(\"app_install_id = ?\", install.ID).Count(&count).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif count > 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tenvMap := map[string]interface{}{}\n\t\t\tif strings.TrimSpace(install.Env) != \"\" {\n\t\t\t\t_ = json.Unmarshal([]byte(install.Env), &envMap)\n\t\t\t}\n\t\t\tconfigPath := path.Join(install.GetPath(), \"data\", \"conf\", \"openclaw.json\")\n\t\t\tcfgMeta := migrationutils.OpenclawMeta{}\n\t\t\tif fileData, err := os.ReadFile(configPath); err == nil {\n\t\t\t\tcfgMeta = migrationutils.ParseOpenclawMeta(fileData)\n\t\t\t}\n\t\t\tprovider := strings.ToLower(migrationutils.GetEnvStr(envMap, \"PROVIDER\"))\n\t\t\tif provider == \"\" {\n\t\t\t\tprovider = strings.ToLower(cfgMeta.Provider)\n\t\t\t}\n\t\t\tif provider == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tprovider = migrationutils.NormalizeOpenclawProvider(provider, cfgMeta.BaseURL)\n\t\t\tmodelName := migrationutils.GetEnvStr(envMap, \"MODEL\")\n\t\t\tif modelName == \"\" {\n\t\t\t\tmodelName = cfgMeta.Model\n\t\t\t}\n\t\t\tbaseURL := migrationutils.GetEnvStr(envMap, \"BASE_URL\")\n\t\t\tif baseURL == \"\" {\n\t\t\t\tbaseURL = cfgMeta.BaseURL\n\t\t\t}\n\t\t\tapiKey := migrationutils.GetEnvStr(envMap, \"API_KEY\")\n\t\t\tif apiKey == \"\" {\n\t\t\t\tapiKey = cfgMeta.APIKey\n\t\t\t}\n\t\t\ttoken := migrationutils.GetEnvStr(envMap, \"OPENCLAW_GATEWAY_TOKEN\")\n\t\t\tif token == \"\" {\n\t\t\t\ttoken = cfgMeta.Token\n\t\t\t}\n\t\t\tif provider != \"ollama\" {\n\t\t\t\tif baseURL == \"\" {\n\t\t\t\t\tif defaultURL, ok := migrationutils.DefaultBaseURL(provider); ok {\n\t\t\t\t\t\tbaseURL = defaultURL\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif provider == \"ollama\" && baseURL == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar account model.AgentAccount\n\t\t\terr := tx.Where(\"provider = ? AND api_key = ? AND base_url = ?\", provider, apiKey, baseURL).First(&account).Error\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\t\taccount = model.AgentAccount{\n\t\t\t\t\t\tProvider: provider,\n\t\t\t\t\t\tName:     install.Name,\n\t\t\t\t\t\tAPIKey:   apiKey,\n\t\t\t\t\t\tBaseURL:  baseURL,\n\t\t\t\t\t\tVerified: apiKey != \"\" || provider == \"ollama\",\n\t\t\t\t\t}\n\t\t\t\t\tif err := tx.Create(&account).Error; err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tagent := model.Agent{\n\t\t\t\tName:         install.Name,\n\t\t\t\tProvider:     provider,\n\t\t\t\tModel:        modelName,\n\t\t\t\tBaseURL:      baseURL,\n\t\t\t\tAPIKey:       apiKey,\n\t\t\t\tToken:        token,\n\t\t\t\tStatus:       install.Status,\n\t\t\t\tMessage:      install.Message,\n\t\t\t\tAppInstallID: install.ID,\n\t\t\t\tAccountID:    account.ID,\n\t\t\t\tConfigPath:   configPath,\n\t\t\t}\n\t\t\tif err := tx.Create(&agent).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddAgentCustomModelFields = &gormigrate.Migration{\n\tID: \"20260224-add-agent-custom-model-fields\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.Agent{}, &model.AgentAccount{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.AgentAccount{}).Where(\"api_type = '' OR api_type IS NULL\").Update(\"api_type\", \"openai-completions\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.Agent{}).Where(\"api_type = '' OR api_type IS NULL\").Update(\"api_type\", \"openai-completions\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddAppInstallSortOrder = &gormigrate.Migration{\n\tID: \"20260222-add-app-install-sort-order\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(&model.AppInstall{})\n\t},\n}\n\nvar AddAgentAccountRememberAPIKey = &gormigrate.Migration{\n\tID: \"20260225-add-agent-account-remember-api-key\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn tx.Model(&model.AgentAccount{}).Where(\"remember_api_key IS NULL\").Update(\"remember_api_key\", true).Error\n\t},\n}\n\nvar AddEditionSetting = &gormigrate.Migration{\n\tID: \"20260224-add-edition-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar setting model.Setting\n\t\tedition := common.LoadParamsWithoutPanic(\"PANEL_EDITION\")\n\t\tif err := tx.Where(\"key = ?\", \"Edition\").First(&setting).Error; err != nil {\n\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\treturn tx.Create(&model.Setting{Key: \"Edition\", Value: edition}).Error\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif setting.Value == \"\" {\n\t\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"Edition\").Update(\"value\", edition).Error\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddAgentTypeForAgents = &gormigrate.Migration{\n\tID: \"20260302-add-agent-type-for-agents\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.Agent{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.Agent{}).Where(\"agent_type = '' OR agent_type IS NULL\").Update(\"agent_type\", constant.AppOpenclaw).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn tx.Exec(\n\t\t\t\"UPDATE agents SET agent_type = ? WHERE app_install_id IN (SELECT ai.id FROM app_installs ai JOIN apps a ON ai.app_id = a.id WHERE a.key = ?)\",\n\t\t\tconstant.AppCopaw,\n\t\t\tconstant.AppCopaw,\n\t\t).Error\n\t},\n}\n\nvar NormalizeAgentAccountVerifiedStatus = &gormigrate.Migration{\n\tID: \"20260303-normalize-agent-account-verified-status\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn tx.Model(&model.AgentAccount{}).\n\t\t\tWhere(\"provider IN ?\", []string{\"custom\", \"ollama\", \"kimi-coding\"}).\n\t\t\tUpdate(\"verified\", false).Error\n\t},\n}\n\nvar NormalizeOllamaAccountAPIType = &gormigrate.Migration{\n\tID: \"20260304-normalize-ollama-account-api-type\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn tx.Model(&model.AgentAccount{}).\n\t\t\tWhere(\"provider = ?\", \"ollama\").\n\t\t\tUpdate(\"api_type\", \"openai-responses\").Error\n\t},\n}\n\nvar RewriteOpenclawBundledCaddyfile = &gormigrate.Migration{\n\tID: \"20260318-rewrite-openclaw-bundled-caddyfile\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn migrationutils.RewriteOpenclawBundledCaddyfile(tx)\n\t},\n}\n\nvar InitAgentAccountModelPool = &gormigrate.Migration{\n\tID: \"20260319-init-agent-account-model-pool\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.AgentAccountModel{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn migrationutils.MigrateAgentAccountModelPool(tx)\n\t},\n}\n\nvar AddHostTable = &gormigrate.Migration{\n\tID: \"20260318-add-host-table\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.AutoMigrate(&model.Host{}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif global.CoreDB == nil || !global.CoreDB.Migrator().HasTable(\"hosts\") {\n\t\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"host\", IsDefault: true}).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\tvar encryptSetting model.Setting\n\t\tif err := global.CoreDB.Where(\"key = ?\", \"EncryptKey\").First(&encryptSetting).Error; err != nil {\n\t\t\tglobal.LOG.Errorf(\"failed to get encrypt key from core db, err: %v\", err)\n\t\t\treturn nil\n\t\t}\n\t\tcoreEncryptKey := strings.TrimSpace(encryptSetting.Value)\n\t\tif coreEncryptKey == \"\" {\n\t\t\tglobal.LOG.Error(\"encrypt key from core db is empty\")\n\t\t\treturn nil\n\t\t}\n\n\t\tgroupIDMap := make(map[uint]uint)\n\t\tdefaultGroupID := uint(0)\n\t\tvar coreGroups []model.Group\n\t\tif err := global.CoreDB.Where(\"type = ?\", \"host\").Order(\"id asc\").Find(&coreGroups).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, coreGroup := range coreGroups {\n\t\t\tagentGroup := model.Group{\n\t\t\t\tName:      coreGroup.Name,\n\t\t\t\tType:      \"host\",\n\t\t\t\tIsDefault: coreGroup.IsDefault,\n\t\t\t}\n\t\t\tif agentGroup.IsDefault {\n\t\t\t\tdefaultGroupID = coreGroup.ID\n\t\t\t}\n\t\t\tif err := tx.Create(&agentGroup).Error; err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"failed to create group, group id: %v, err: %v\", coreGroup.ID, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgroupIDMap[coreGroup.ID] = agentGroup.ID\n\t\t}\n\n\t\tvar coreHosts []model.Host\n\t\tif err := global.CoreDB.Order(\"id asc\").Find(&coreHosts).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, coreHost := range coreHosts {\n\t\t\tpassword, err := encrypt.StringDecryptWithKey(coreHost.Password, coreEncryptKey)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"failed to decrypt host password, host id: %v, err: %v\", coreHost.ID, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tprivateKey, err := encrypt.StringDecryptWithKey(coreHost.PrivateKey, coreEncryptKey)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"failed to decrypt host private key, host id: %v, err: %v\", coreHost.ID, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpassPhrase, err := encrypt.StringDecryptWithKey(coreHost.PassPhrase, coreEncryptKey)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"failed to decrypt host pass phrase, host id: %v, err: %v\", coreHost.ID, err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tencryptedPassword, err := encrypt.StringEncrypt(password)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"failed to encrypt host password, host id: %v, err: %v\", coreHost.ID, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tencryptedPrivateKey, err := encrypt.StringEncrypt(privateKey)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"failed to encrypt host private key, host id: %v, err: %v\", coreHost.ID, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tencryptedPassPhrase, err := encrypt.StringEncrypt(passPhrase)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"failed to encrypt host pass phrase, host id: %v, err: %v\", coreHost.ID, err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tgroupID := defaultGroupID\n\t\t\tif mappedGroupID, ok := groupIDMap[coreHost.GroupID]; ok && mappedGroupID != 0 {\n\t\t\t\tgroupID = mappedGroupID\n\t\t\t}\n\t\t\thost := model.Host{\n\t\t\t\tGroupID:          groupID,\n\t\t\t\tName:             coreHost.Name,\n\t\t\t\tAddr:             coreHost.Addr,\n\t\t\t\tPort:             coreHost.Port,\n\t\t\t\tUser:             coreHost.User,\n\t\t\t\tAuthMode:         coreHost.AuthMode,\n\t\t\t\tPassword:         encryptedPassword,\n\t\t\t\tPrivateKey:       encryptedPrivateKey,\n\t\t\t\tPassPhrase:       encryptedPassPhrase,\n\t\t\t\tRememberPassword: coreHost.RememberPassword,\n\t\t\t\tDescription:      coreHost.Description,\n\t\t\t}\n\t\t\tif err := tx.Create(&host).Error; err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"failed to create host, host id: %v, err: %v\", coreHost.ID, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddAITerminalSettings = &gormigrate.Migration{\n\tID: \"20260318-add-ai-terminal-settings\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Setting{Key: \"AIStatus\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"AIAccountID\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"AIPrefix\", Value: \"#\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn tx.Create(&model.Setting{\n\t\t\tKey:   \"AIRiskCommands\",\n\t\t\tValue: \"[\\\"rm -rf\\\",\\\"mkfs\\\",\\\"dd if=\\\",\\\"curl | sh\\\",\\\"wget | sh\\\",\\\"chmod -R 777 /\\\",\\\"shutdown\\\",\\\"reboot\\\",\\\"poweroff\\\",\\\"init 0\\\",\\\":(){ :|:& };:\\\"]\",\n\t\t}).Error\n\t},\n}\n"
  },
  {
    "path": "agent/init/migration/migrations/utils/agent_account_model_pool.go",
    "content": "package utils\n\nimport (\n\t\"encoding/json\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/service\"\n\n\t\"gorm.io/gorm\"\n)\n\ntype legacyAgentAccountModelPoolSource struct {\n\tmodel.AgentAccount\n\tLegacyModel  string `gorm:\"column:model\"`\n\tLegacyModels string `gorm:\"column:models\"`\n}\n\nfunc MigrateAgentAccountModelPool(tx *gorm.DB) error {\n\tvar accounts []legacyAgentAccountModelPoolSource\n\tif err := tx.Table(model.AgentAccount{}.TableName()).Find(&accounts).Error; err != nil {\n\t\treturn err\n\t}\n\tfor _, account := range accounts {\n\t\tcount := int64(0)\n\t\tif err := tx.Model(&model.AgentAccountModel{}).Where(\"account_id = ?\", account.ID).Count(&count).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif count > 0 {\n\t\t\tcontinue\n\t\t}\n\t\tmodels, err := buildMigratedAgentAccountModels(tx, &account)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(models) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfor index, item := range models {\n\t\t\tinputPayload, err := json.Marshal(item.Input)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\trecord := &model.AgentAccountModel{\n\t\t\t\tAccountID:     account.ID,\n\t\t\t\tModel:         item.ID,\n\t\t\t\tName:          item.Name,\n\t\t\t\tContextWindow: item.ContextWindow,\n\t\t\t\tMaxTokens:     item.MaxTokens,\n\t\t\t\tReasoning:     item.Reasoning,\n\t\t\t\tInput:         string(inputPayload),\n\t\t\t\tSortOrder:     index + 1,\n\t\t\t}\n\t\t\tif err := tx.Create(record).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc buildMigratedAgentAccountModels(tx *gorm.DB, account *legacyAgentAccountModelPoolSource) ([]dto.AgentAccountModel, error) {\n\tif account == nil {\n\t\treturn nil, nil\n\t}\n\tbaseAccount := account.AgentAccount\n\trequested := make([]dto.AgentAccountModel, 0)\n\tif strings.TrimSpace(account.LegacyModels) != \"\" {\n\t\tif err := json.Unmarshal([]byte(account.LegacyModels), &requested); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tseen := make(map[string]struct{}, len(requested))\n\tfor _, item := range requested {\n\t\ttarget := strings.TrimSpace(item.ID)\n\t\tif target == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tseen[target] = struct{}{}\n\t}\n\tappendModel := func(modelID string) {\n\t\ttarget := strings.TrimSpace(modelID)\n\t\tif target == \"\" {\n\t\t\treturn\n\t\t}\n\t\tif _, ok := seen[target]; ok {\n\t\t\treturn\n\t\t}\n\t\tseen[target] = struct{}{}\n\t\trequested = append(requested, dto.AgentAccountModel{ID: target})\n\t}\n\tappendModel(account.LegacyModel)\n\tif account.ID > 0 {\n\t\tvar agents []model.Agent\n\t\tif err := tx.Where(\"account_id = ?\", account.ID).Find(&agents).Error; err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, agent := range agents {\n\t\t\tappendModel(agent.Model)\n\t\t}\n\t}\n\tmodels, err := service.MergeCatalogAgentAccountModelsForMigration(&baseAccount, requested)\n\tif err != nil {\n\t\tif strings.TrimSpace(err.Error()) == \"model is required\" {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn models, nil\n}\n"
  },
  {
    "path": "agent/init/migration/migrations/utils/openclaw_agent.go",
    "content": "package utils\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\n\tprovidercatalog \"github.com/1Panel-dev/1Panel/agent/app/provider\"\n)\n\ntype OpenclawMeta struct {\n\tProvider string\n\tModel    string\n\tBaseURL  string\n\tAPIKey   string\n\tToken    string\n}\n\nfunc GetEnvStr(envMap map[string]interface{}, key string) string {\n\tif envMap == nil {\n\t\treturn \"\"\n\t}\n\tif value, ok := envMap[key]; ok {\n\t\tswitch v := value.(type) {\n\t\tcase string:\n\t\t\treturn strings.TrimSpace(v)\n\t\tdefault:\n\t\t\treturn strings.TrimSpace(fmt.Sprintf(\"%v\", v))\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc ParseOpenclawMeta(fileData []byte) OpenclawMeta {\n\tmeta := OpenclawMeta{}\n\tcontent := map[string]interface{}{}\n\tif err := json.Unmarshal(fileData, &content); err != nil {\n\t\treturn meta\n\t}\n\n\tmeta.Token = getNestedString(content, \"gateway\", \"auth\", \"token\")\n\tmeta.Model = getNestedString(content, \"agents\", \"defaults\", \"model\", \"primary\")\n\n\tproviderKey := \"\"\n\tif parts := strings.SplitN(meta.Model, \"/\", 2); len(parts) == 2 {\n\t\tproviderKey = strings.TrimSpace(parts[0])\n\t}\n\n\tproviders := getNestedMap(content, \"models\", \"providers\")\n\tproviderConfig := map[string]interface{}{}\n\tif providerKey != \"\" {\n\t\tif cfg, ok := providers[providerKey].(map[string]interface{}); ok {\n\t\t\tproviderConfig = cfg\n\t\t}\n\t}\n\tif len(providerConfig) == 0 && len(providers) == 1 {\n\t\tfor key, value := range providers {\n\t\t\tif cfg, ok := value.(map[string]interface{}); ok {\n\t\t\t\tproviderKey = key\n\t\t\t\tproviderConfig = cfg\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tmeta.Provider = providerKey\n\tmeta.BaseURL = getString(providerConfig, \"baseUrl\")\n\tmeta.APIKey = getString(providerConfig, \"apiKey\")\n\n\tif meta.Model == \"\" && providerKey != \"\" {\n\t\tif modelID := getProviderFirstModelID(providerConfig); modelID != \"\" {\n\t\t\tmeta.Model = providerKey + \"/\" + modelID\n\t\t}\n\t}\n\n\treturn meta\n}\n\nfunc NormalizeOpenclawProvider(provider, baseURL string) string {\n\tp := strings.ToLower(strings.TrimSpace(provider))\n\tbase := strings.ToLower(strings.TrimSpace(baseURL))\n\tswitch p {\n\tcase \"minimax-portal\":\n\t\treturn \"minimax\"\n\tcase \"moonshot\":\n\t\tif strings.Contains(base, \"moonshot.cn\") {\n\t\t\treturn \"kimi\"\n\t\t}\n\t\treturn \"moonshot\"\n\tdefault:\n\t\treturn p\n\t}\n}\n\nfunc DefaultBaseURL(provider string) (string, bool) {\n\treturn providercatalog.DefaultBaseURL(provider)\n}\n\nfunc getNestedMap(data map[string]interface{}, keys ...string) map[string]interface{} {\n\tcurrent := data\n\tfor _, key := range keys {\n\t\tnext, ok := current[key].(map[string]interface{})\n\t\tif !ok {\n\t\t\treturn map[string]interface{}{}\n\t\t}\n\t\tcurrent = next\n\t}\n\treturn current\n}\n\nfunc getNestedString(data map[string]interface{}, keys ...string) string {\n\tcurrent := data\n\tfor i, key := range keys {\n\t\tvalue, ok := current[key]\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\t\tif i == len(keys)-1 {\n\t\t\tswitch v := value.(type) {\n\t\t\tcase string:\n\t\t\t\treturn strings.TrimSpace(v)\n\t\t\tdefault:\n\t\t\t\treturn strings.TrimSpace(fmt.Sprintf(\"%v\", v))\n\t\t\t}\n\t\t}\n\t\tnext, ok := value.(map[string]interface{})\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\t\tcurrent = next\n\t}\n\treturn \"\"\n}\n\nfunc getString(data map[string]interface{}, key string) string {\n\tif data == nil {\n\t\treturn \"\"\n\t}\n\tvalue, ok := data[key]\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tswitch v := value.(type) {\n\tcase string:\n\t\treturn strings.TrimSpace(v)\n\tdefault:\n\t\treturn strings.TrimSpace(fmt.Sprintf(\"%v\", v))\n\t}\n}\n\nfunc getProviderFirstModelID(data map[string]interface{}) string {\n\tif data == nil {\n\t\treturn \"\"\n\t}\n\trawModels, ok := data[\"models\"].([]interface{})\n\tif !ok || len(rawModels) == 0 {\n\t\treturn \"\"\n\t}\n\tfirst, ok := rawModels[0].(map[string]interface{})\n\tif !ok {\n\t\treturn \"\"\n\t}\n\treturn getString(first, \"id\")\n}\n"
  },
  {
    "path": "agent/init/migration/migrations/utils/openclaw_caddyfile.go",
    "content": "package utils\n\nimport (\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\topenclawutil \"github.com/1Panel-dev/1Panel/agent/utils/openclaw\"\n\n\t\"gorm.io/gorm\"\n)\n\nconst (\n\topenclawVersionWithBundledCaddyMigration = \"2026.3.13\"\n)\n\nfunc RewriteOpenclawCaddyfileForVersion(tx *gorm.DB, version string) error {\n\ttargetVersion := strings.TrimSpace(version)\n\tif targetVersion == \"\" {\n\t\treturn nil\n\t}\n\tvar installs []model.AppInstall\n\tif err := tx.Preload(\"App\").Find(&installs).Error; err != nil {\n\t\treturn err\n\t}\n\tfor _, install := range installs {\n\t\tif install.App.Key != constant.AppOpenclaw {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.TrimSpace(install.Version) != targetVersion {\n\t\t\tcontinue\n\t\t}\n\t\tif err := openclawutil.WriteCatchAllCaddyfile(install.GetPath()); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc RewriteOpenclawBundledCaddyfile(tx *gorm.DB) error {\n\treturn RewriteOpenclawCaddyfileForVersion(tx, openclawVersionWithBundledCaddyMigration)\n}\n"
  },
  {
    "path": "agent/init/router/router.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/middleware\"\n\trou \"github.com/1Panel-dev/1Panel/agent/router\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nvar (\n\tRouter *gin.Engine\n)\n\nfunc Routers() *gin.Engine {\n\tRouter = gin.Default()\n\tRouter.Use(i18n.UseI18n())\n\n\tPrivateGroup := Router.Group(\"/api/v2\")\n\tif !global.IsMaster {\n\t\tPrivateGroup.Use(middleware.Certificate())\n\t}\n\tPrivateGroup.Use(middleware.OperationResolveMeta())\n\tfor _, router := range rou.RouterGroupApp {\n\t\trouter.InitRouter(PrivateGroup)\n\t}\n\tPrivateGroup.GET(\"/health/check\", v2.ApiGroupApp.BaseApi.CheckHealth)\n\n\treturn Router\n}\n"
  },
  {
    "path": "agent/init/validator/validator.go",
    "content": "package validator\n\nimport (\n\t\"unicode\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\n\t\"github.com/go-playground/validator/v10\"\n)\n\nfunc Init() {\n\tvalidator := validator.New()\n\tif err := validator.RegisterValidation(\"name\", checkNamePattern); err != nil {\n\t\tpanic(err)\n\t}\n\tif err := validator.RegisterValidation(\"ip\", checkIpPattern); err != nil {\n\t\tpanic(err)\n\t}\n\tif err := validator.RegisterValidation(\"password\", checkPasswordPattern); err != nil {\n\t\tpanic(err)\n\t}\n\tglobal.VALID = validator\n}\n\nfunc checkNamePattern(fl validator.FieldLevel) bool {\n\tvalue := fl.Field().String()\n\treturn re.GetRegex(re.ValidatorNamePattern).MatchString(value)\n}\n\nfunc checkIpPattern(fl validator.FieldLevel) bool {\n\tvalue := fl.Field().String()\n\treturn re.GetRegex(re.ValidatorIPPattern).MatchString(value)\n}\n\nfunc checkPasswordPattern(fl validator.FieldLevel) bool {\n\tvalue := fl.Field().String()\n\tif len(value) < 8 || len(value) > 30 {\n\t\treturn false\n\t}\n\n\thasNum := false\n\thasLetter := false\n\tfor _, r := range value {\n\t\tif unicode.IsLetter(r) && !hasLetter {\n\t\t\thasLetter = true\n\t\t}\n\t\tif unicode.IsNumber(r) && !hasNum {\n\t\t\thasNum = true\n\t\t}\n\t\tif hasLetter && hasNum {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "agent/init/viper/viper.go",
    "content": "package viper\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/conf\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc Init() {\n\tmode := \"\"\n\tfileOp := files.NewFileOp()\n\tv := viper.NewWithOptions()\n\tv.SetConfigType(\"yaml\")\n\n\tconfig := global.ServerConfig{}\n\tif err := yaml.Unmarshal(conf.AppYaml, &config); err != nil {\n\t\tpanic(err)\n\t}\n\tif config.Base.Mode != \"\" {\n\t\tmode = config.Base.Mode\n\t}\n\tif mode == \"dev\" && fileOp.Stat(\"/opt/1panel/conf/app.yaml\") {\n\t\tv.SetConfigName(\"app\")\n\t\tv.AddConfigPath(path.Join(\"/opt/1panel/conf\"))\n\t\tif err := v.ReadInConfig(); err != nil {\n\t\t\tpanic(fmt.Errorf(\"Fatal error config file: %s \\n\", err))\n\t\t}\n\t} else {\n\t\treader := bytes.NewReader(conf.AppYaml)\n\t\tif err := v.ReadConfig(reader); err != nil {\n\t\t\tpanic(fmt.Errorf(\"Fatal error config file: %s \\n\", err))\n\t\t}\n\t}\n\tv.OnConfigChange(func(e fsnotify.Event) {\n\t\tif err := v.Unmarshal(&global.CONF); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t})\n\tserverConfig := global.ServerConfig{}\n\tif err := v.Unmarshal(&serverConfig); err != nil {\n\t\tpanic(err)\n\t}\n\n\tglobal.CONF = serverConfig\n\n\tinitBaseInfo()\n\tglobal.Viper = v\n}\n\nfunc initBaseInfo() {\n\tnodeInfo, err := xpack.LoadNodeInfo(true)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tglobal.CONF.Base.InstallDir = nodeInfo.BaseDir\n}\n"
  },
  {
    "path": "agent/log/config.go",
    "content": "package log\n\nimport (\n\t\"errors\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n)\n\nvar (\n\tBufferSize         = 0x100000\n\tDefaultFileMode    = os.FileMode(constant.DirPerm)\n\tDefaultFileFlag    = os.O_RDWR | os.O_CREATE | os.O_APPEND\n\tErrInvalidArgument = errors.New(\"error argument invalid\")\n\tQueueSize          = 1024\n\tErrClosed          = errors.New(\"error write on close\")\n)\n\ntype Config struct {\n\tTimeTagFormat      string\n\tLogPath            string\n\tFileName           string\n\tLogSuffix          string\n\tMaxRemain          int\n\tRollingTimePattern string\n}\n\ntype Manager interface {\n\tFire() chan string\n\tClose()\n}\n\ntype RollingWriter interface {\n\tio.Writer\n\tClose() error\n}\n\nfunc FilePath(c *Config) (filepath string) {\n\tfilepath = path.Join(c.LogPath, c.FileName) + c.LogSuffix\n\treturn\n}\n"
  },
  {
    "path": "agent/log/dup_write_darwin.go",
    "content": "package log\n\nimport (\n\t\"golang.org/x/sys/unix\"\n\t\"os\"\n\t\"runtime\"\n)\n\nvar stdErrFileHandler *os.File\n\nfunc dupWrite(file *os.File) error {\n\tstdErrFileHandler = file\n\tif err := unix.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {\n\t\treturn err\n\t}\n\truntime.SetFinalizer(stdErrFileHandler, func(fd *os.File) {\n\t\tfd.Close()\n\t})\n\treturn nil\n}\n"
  },
  {
    "path": "agent/log/dup_write_linux.go",
    "content": "package log\n\nimport (\n\t\"golang.org/x/sys/unix\"\n\t\"os\"\n\t\"runtime\"\n)\n\nvar stdErrFileHandler *os.File\n\nfunc dupWrite(file *os.File) error {\n\tstdErrFileHandler = file\n\tif err := unix.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {\n\t\treturn err\n\t}\n\truntime.SetFinalizer(stdErrFileHandler, func(fd *os.File) {\n\t\tfd.Close()\n\t})\n\treturn nil\n}\n"
  },
  {
    "path": "agent/log/dup_write_windows.go",
    "content": "package log\n\nimport (\n\t\"os\"\n)\n\nfunc dupWrite(file *os.File) error {\n\treturn nil\n}\n"
  },
  {
    "path": "agent/log/manager.go",
    "content": "package log\n\nimport (\n\t\"github.com/robfig/cron/v3\"\n\t\"path\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype manager struct {\n\tstartAt time.Time\n\tfire    chan string\n\tcr      *cron.Cron\n\tcontext chan int\n\twg      sync.WaitGroup\n\tlock    sync.Mutex\n}\n\nfunc (m *manager) Fire() chan string {\n\treturn m.fire\n}\n\nfunc (m *manager) Close() {\n\tclose(m.context)\n\tm.cr.Stop()\n}\n\nfunc NewManager(c *Config) (Manager, error) {\n\tm := &manager{\n\t\tstartAt: time.Now(),\n\t\tcr:      cron.New(),\n\t\tfire:    make(chan string),\n\t\tcontext: make(chan int),\n\t\twg:      sync.WaitGroup{},\n\t}\n\n\tif _, err := m.cr.AddFunc(c.RollingTimePattern, func() {\n\t\tm.fire <- m.GenLogFileName(c)\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\tm.cr.Start()\n\n\treturn m, nil\n}\n\nfunc (m *manager) GenLogFileName(c *Config) (filename string) {\n\tm.lock.Lock()\n\tfilename = path.Join(c.LogPath, c.FileName+\"-\"+m.startAt.Format(c.TimeTagFormat)) + c.LogSuffix\n\tm.startAt = time.Now()\n\tm.lock.Unlock()\n\treturn\n}\n"
  },
  {
    "path": "agent/log/writer.go",
    "content": "package log\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\ntype Writer struct {\n\tm             Manager\n\tfile          *os.File\n\tabsPath       string\n\tfire          chan string\n\tcf            *Config\n\trollingfilech chan string\n}\n\ntype AsynchronousWriter struct {\n\tWriter\n\tctx     chan int\n\tqueue   chan []byte\n\terrChan chan error\n\tclosed  int32\n\twg      sync.WaitGroup\n}\n\nfunc (w *AsynchronousWriter) Close() error {\n\tif atomic.CompareAndSwapInt32(&w.closed, 0, 1) {\n\t\tclose(w.ctx)\n\t\tw.onClose()\n\n\t\tfunc() {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tglobal.LOG.Error(r)\n\t\t\t\t}\n\t\t\t}()\n\t\t\tw.m.Close()\n\t\t}()\n\t\treturn w.file.Close()\n\t}\n\treturn ErrClosed\n}\n\nfunc (w *AsynchronousWriter) onClose() {\n\tvar err error\n\tfor {\n\t\tselect {\n\t\tcase b := <-w.queue:\n\t\t\tif _, err = w.file.Write(b); err != nil {\n\t\t\t\tselect {\n\t\t\t\tcase w.errChan <- err:\n\t\t\t\tdefault:\n\t\t\t\t\t_asyncBufferPool.Put(&b)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\t_asyncBufferPool.Put(&b)\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nvar _asyncBufferPool = sync.Pool{\n\tNew: func() interface{} {\n\t\treturn make([]byte, BufferSize)\n\t},\n}\n\nfunc NewWriterFromConfig(c *Config) (RollingWriter, error) {\n\tif c.LogPath == \"\" || c.FileName == \"\" {\n\t\treturn nil, ErrInvalidArgument\n\t}\n\tif err := os.MkdirAll(c.LogPath, 0700); err != nil {\n\t\treturn nil, err\n\t}\n\tfilepath := FilePath(c)\n\tfile, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_APPEND, constant.FilePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := dupWrite(file); err != nil {\n\t\treturn nil, err\n\t}\n\tmng, err := NewManager(c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar rollingWriter RollingWriter\n\twriter := Writer{\n\t\tm:       mng,\n\t\tfile:    file,\n\t\tabsPath: filepath,\n\t\tfire:    mng.Fire(),\n\t\tcf:      c,\n\t}\n\tif c.MaxRemain > 0 {\n\t\twriter.rollingfilech = make(chan string, c.MaxRemain)\n\t\tdir, err := os.ReadDir(c.LogPath)\n\t\tif err != nil {\n\t\t\tmng.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfiles := make([]string, 0, 10)\n\t\tfor _, fi := range dir {\n\t\t\tif fi.IsDir() {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfileName := c.FileName\n\t\t\tif strings.Contains(fi.Name(), fileName) && strings.Contains(fi.Name(), c.LogSuffix) {\n\t\t\t\tstart := strings.Index(fi.Name(), \"-\")\n\t\t\t\tend := strings.Index(fi.Name(), c.LogSuffix)\n\t\t\t\tname := fi.Name()\n\t\t\t\tif start > 0 && end > 0 {\n\t\t\t\t\t_, err := time.Parse(c.TimeTagFormat, name[start+1:end])\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tfiles = append(files, fi.Name())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsort.Slice(files, func(i, j int) bool {\n\t\t\tt1Start := strings.Index(files[i], \"-\")\n\t\t\tt1End := strings.Index(files[i], c.LogSuffix)\n\t\t\tt2Start := strings.Index(files[i], \"-\")\n\t\t\tt2End := strings.Index(files[i], c.LogSuffix)\n\t\t\tt1, _ := time.Parse(c.TimeTagFormat, files[i][t1Start+1:t1End])\n\t\t\tt2, _ := time.Parse(c.TimeTagFormat, files[j][t2Start+1:t2End])\n\t\t\treturn t1.Before(t2)\n\t\t})\n\n\t\tfor _, file := range files {\n\t\tretry:\n\t\t\tselect {\n\t\t\tcase writer.rollingfilech <- path.Join(c.LogPath, file):\n\t\t\tdefault:\n\t\t\t\twriter.DoRemove()\n\t\t\t\tgoto retry\n\t\t\t}\n\t\t}\n\t}\n\n\twr := &AsynchronousWriter{\n\t\tctx:     make(chan int),\n\t\tqueue:   make(chan []byte, QueueSize),\n\t\terrChan: make(chan error, QueueSize),\n\t\twg:      sync.WaitGroup{},\n\t\tclosed:  0,\n\t\tWriter:  writer,\n\t}\n\n\twr.wg.Add(1)\n\tgo wr.writer()\n\twr.wg.Wait()\n\trollingWriter = wr\n\n\treturn rollingWriter, nil\n}\n\nfunc (w *AsynchronousWriter) writer() {\n\tvar err error\n\tw.wg.Done()\n\tfor {\n\t\tselect {\n\t\tcase filename := <-w.fire:\n\t\t\tif err = w.Reopen(filename); err != nil && len(w.errChan) < cap(w.errChan) {\n\t\t\t\tw.errChan <- err\n\t\t\t}\n\t\tcase b := <-w.queue:\n\t\t\tif _, err = w.file.Write(b); err != nil && len(w.errChan) < cap(w.errChan) {\n\t\t\t\tw.errChan <- err\n\t\t\t}\n\t\t\t_asyncBufferPool.Put(&b)\n\t\tcase <-w.ctx:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (w *Writer) DoRemove() {\n\tfile := <-w.rollingfilech\n\tif err := os.Remove(file); err != nil {\n\t\tlog.Println(\"error in remove log file\", file, err)\n\t}\n}\n\nfunc (w *Writer) Write(b []byte) (int, error) {\n\tvar ok = false\n\tfor !ok {\n\t\tselect {\n\t\tcase filename := <-w.fire:\n\t\t\tif err := w.Reopen(filename); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\tdefault:\n\t\t\tok = true\n\t\t}\n\t}\n\n\tfp := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&w.file)))\n\tfile := (*os.File)(fp)\n\treturn file.Write(b)\n}\n\nfunc (w *Writer) Reopen(file string) error {\n\tfileInfo, err := w.file.Stat()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif fileInfo.Size() == 0 {\n\t\treturn nil\n\t}\n\n\tw.file.Close()\n\tif err := os.Rename(w.absPath, file); err != nil {\n\t\treturn err\n\t}\n\tnewFile, err := os.OpenFile(w.absPath, DefaultFileFlag, DefaultFileMode)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tw.file = newFile\n\n\tgo func() {\n\t\tif w.cf.MaxRemain > 0 {\n\t\tretry:\n\t\t\tselect {\n\t\t\tcase w.rollingfilech <- file:\n\t\t\tdefault:\n\t\t\t\tw.DoRemove()\n\t\t\t\tgoto retry\n\t\t\t}\n\t\t}\n\t}()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/middleware/certificate.go",
    "content": "package middleware\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc Certificate() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tif global.IsMaster {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tif !xpack.ValidateCertificate(c) {\n\t\t\tCloseDirectly(c)\n\t\t\treturn\n\t\t}\n\t\tconn := c.Request.Header.Get(\"Connection\")\n\t\tif conn == \"Upgrade\" {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tmasterProxyID := c.Request.Header.Get(\"Proxy-Id\")\n\t\tproxyID, err := cmd.RunDefaultWithStdoutBashC(\"cat /etc/1panel/.nodeProxyID\")\n\t\tif err == nil && len(proxyID) != 0 && strings.TrimSpace(proxyID) != strings.TrimSpace(masterProxyID) {\n\t\t\thelper.InternalServer(c, fmt.Errorf(\"err proxy id\"))\n\t\t\treturn\n\t\t}\n\t\tc.Next()\n\t}\n}\n\nfunc CloseDirectly(c *gin.Context) {\n\thijacker, ok := c.Writer.(http.Hijacker)\n\tif !ok {\n\t\tc.AbortWithStatus(http.StatusForbidden)\n\t\treturn\n\t}\n\tconn, _, err := hijacker.Hijack()\n\tif err != nil {\n\t\tc.AbortWithStatus(http.StatusForbidden)\n\t\treturn\n\t}\n\t_ = conn.(*net.TCPConn).SetLinger(0)\n\tconn.Close()\n}\n"
  },
  {
    "path": "agent/middleware/operation.go",
    "content": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"path\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/cmd/server/docs\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/glebarez/sqlite\"\n\t\"gorm.io/gorm\"\n)\n\nconst (\n\theaderNeedOperationResolve = \"X-Need-Op-Resolve\"\n\theaderOperationResolved    = \"X-Op-Resolved\"\n)\n\nvar (\n\tlogMetaOnce    sync.Once\n\tlogMetaData    map[string]operationMeta\n\tlogMetaLoadErr error\n)\n\ntype operationMeta struct {\n\tBodyKeys        []string       `json:\"bodyKeys\"`\n\tBeforeFunctions []functionInfo `json:\"beforeFunctions\"`\n}\n\ntype functionInfo struct {\n\tInputColumn  string `json:\"input_column\"`\n\tInputValue   string `json:\"input_value\"`\n\tIsList       bool   `json:\"isList\"`\n\tDB           string `json:\"db\"`\n\tOutputColumn string `json:\"output_column\"`\n\tOutputValue  string `json:\"output_value\"`\n}\n\nfunc OperationResolveMeta() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tif c.GetHeader(headerNeedOperationResolve) != \"1\" {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tmetaMap, err := loadOperationMeta()\n\t\tif err != nil {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\treqPath := strings.TrimPrefix(c.Request.URL.Path, \"/api/v2\")\n\t\tmeta, ok := metaMap[reqPath]\n\t\tif !ok || len(meta.BeforeFunctions) == 0 {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tvalues := make(map[string]interface{})\n\t\tif len(meta.BodyKeys) > 0 {\n\t\t\tbody, err := io.ReadAll(c.Request.Body)\n\t\t\tif err == nil {\n\t\t\t\tc.Request.Body = io.NopCloser(bytes.NewBuffer(body))\n\t\t\t\tbodyMap := make(map[string]interface{})\n\t\t\t\tif strings.Contains(c.Request.Header.Get(\"Content-Type\"), \"multipart/form-data\") {\n\t\t\t\t\tbodyMap, _ = parseMultipart(body, c.Request.Header.Get(\"Content-Type\"))\n\t\t\t\t} else {\n\t\t\t\t\tdecoder := json.NewDecoder(bytes.NewReader(body))\n\t\t\t\t\tdecoder.UseNumber()\n\t\t\t\t\t_ = decoder.Decode(&bodyMap)\n\t\t\t\t}\n\t\t\t\tfor _, key := range meta.BodyKeys {\n\t\t\t\t\tif value, ok := bodyMap[key]; ok {\n\t\t\t\t\t\tvalues[key] = value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tresolved, err := resolveOperationValues(reqPath, values, meta.BeforeFunctions)\n\t\tif err == nil && len(resolved) > 0 {\n\t\t\tif data, err := json.Marshal(resolved); err == nil {\n\t\t\t\tc.Header(headerOperationResolved, base64.RawURLEncoding.EncodeToString(data))\n\t\t\t}\n\t\t}\n\t\tc.Next()\n\t}\n}\n\nfunc loadOperationMeta() (map[string]operationMeta, error) {\n\tlogMetaOnce.Do(func() {\n\t\tlogMetaData = make(map[string]operationMeta)\n\t\tlogMetaLoadErr = json.Unmarshal(docs.XLogJson, &logMetaData)\n\t})\n\treturn logMetaData, logMetaLoadErr\n}\n\nfunc parseMultipart(formData []byte, contentType string) (map[string]interface{}, error) {\n\td, params, err := mime.ParseMediaType(contentType)\n\tif err != nil || d != \"multipart/form-data\" {\n\t\treturn nil, http.ErrNotMultipart\n\t}\n\tboundary, ok := params[\"boundary\"]\n\tif !ok {\n\t\treturn nil, http.ErrMissingBoundary\n\t}\n\treader := multipart.NewReader(bytes.NewReader(formData), boundary)\n\tret := make(map[string]interface{})\n\n\tf, err := reader.ReadForm(32 << 20)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor k, v := range f.Value {\n\t\tif len(v) > 0 {\n\t\t\tret[k] = v[0]\n\t\t}\n\t}\n\tfor k, v := range f.File {\n\t\tif len(v) > 0 {\n\t\t\tret[k] = v[0].Filename\n\t\t}\n\t}\n\treturn ret, nil\n}\n\nfunc resolveOperationValues(pathItem string, values map[string]interface{}, beforeFunctions []functionInfo) (map[string]string, error) {\n\tdbItem, err := newResolveDB(pathItem)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeResolveDB(dbItem)\n\n\tresolved := make(map[string]string)\n\tfor _, funcs := range beforeFunctions {\n\t\tif !isSafeIdentifier(funcs.DB) || !isSafeIdentifier(funcs.InputColumn) || !isSafeIdentifier(funcs.OutputColumn) {\n\t\t\tcontinue\n\t\t}\n\t\tfor key, value := range values {\n\t\t\tif funcs.InputValue != key {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar names []string\n\t\t\tif funcs.IsList {\n\t\t\t\tsql := fmt.Sprintf(\"SELECT %s FROM %s where %s in (?);\", funcs.OutputColumn, funcs.DB, funcs.InputColumn)\n\t\t\t\t_ = dbItem.Raw(sql, value).Scan(&names)\n\t\t\t} else {\n\t\t\t\tsql := fmt.Sprintf(\"SELECT %s FROM %s where %s = ?;\", funcs.OutputColumn, funcs.DB, funcs.InputColumn)\n\t\t\t\t_ = dbItem.Raw(sql, value).Scan(&names)\n\t\t\t}\n\t\t\toutputValue := strings.Join(names, \",\")\n\t\t\tresolved[funcs.OutputValue] = outputValue\n\t\t\tvalues[funcs.OutputValue] = outputValue\n\t\t\tbreak\n\t\t}\n\t}\n\treturn resolved, nil\n}\n\nfunc newResolveDB(pathItem string) (*gorm.DB, error) {\n\tdbFile := \"\"\n\tswitch {\n\tcase strings.HasPrefix(pathItem, \"/core\"):\n\t\tdbFile = path.Join(global.CONF.Base.InstallDir, \"1panel/db/core.db\")\n\tcase strings.HasPrefix(pathItem, \"/xpack\"):\n\t\tdbFile = path.Join(global.CONF.Base.InstallDir, \"1panel/db/xpack.db\")\n\tdefault:\n\t\tdbFile = path.Join(global.CONF.Base.InstallDir, \"1panel/db/agent.db\")\n\t}\n\n\tdb, err := gorm.Open(sqlite.Open(dbFile), &gorm.Config{\n\t\tDisableForeignKeyConstraintWhenMigrating: true,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsqlDB.SetMaxOpenConns(4)\n\tsqlDB.SetMaxIdleConns(1)\n\tsqlDB.SetConnMaxIdleTime(15 * time.Minute)\n\tsqlDB.SetConnMaxLifetime(time.Hour)\n\treturn db, nil\n}\n\nfunc closeResolveDB(db *gorm.DB) {\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn\n\t}\n\t_ = sqlDB.Close()\n}\n\nfunc isSafeIdentifier(val string) bool {\n\treturn re.GetRegex(re.SQLIdentifierPattern).MatchString(val)\n}\n"
  },
  {
    "path": "agent/router/backup.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype BackupRouter struct{}\n\nfunc (s *BackupRouter) InitRouter(Router *gin.RouterGroup) {\n\tbackupRouter := Router.Group(\"backups\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tbackupRouter.GET(\"/check/:name\", baseApi.CheckBackupUsed)\n\t\tbackupRouter.GET(\"/options\", baseApi.LoadBackupOptions)\n\t\tbackupRouter.POST(\"/search\", baseApi.SearchBackup)\n\n\t\tbackupRouter.GET(\"/local\", baseApi.GetLocalDir)\n\t\tbackupRouter.POST(\"/refresh/token\", baseApi.RefreshToken)\n\t\tbackupRouter.POST(\"/buckets\", baseApi.ListBuckets)\n\t\tbackupRouter.POST(\"\", baseApi.CreateBackup)\n\t\tbackupRouter.POST(\"/conn/check\", baseApi.CheckBackup)\n\t\tbackupRouter.POST(\"/del\", baseApi.DeleteBackup)\n\t\tbackupRouter.POST(\"/update\", baseApi.UpdateBackup)\n\n\t\tbackupRouter.POST(\"/backup\", baseApi.Backup)\n\t\tbackupRouter.POST(\"/recover\", baseApi.Recover)\n\t\tbackupRouter.POST(\"/upload\", baseApi.UploadForRecover)\n\t\tbackupRouter.POST(\"/recover/byupload\", baseApi.RecoverByUpload)\n\t\tbackupRouter.POST(\"/search/files\", baseApi.LoadFilesFromBackup)\n\t\tbackupRouter.POST(\"/record/search\", baseApi.SearchBackupRecords)\n\t\tbackupRouter.POST(\"/record/size\", baseApi.LoadBackupRecordSize)\n\t\tbackupRouter.POST(\"/record/search/bycronjob\", baseApi.SearchBackupRecordsByCronjob)\n\t\tbackupRouter.POST(\"/record/download\", baseApi.DownloadRecord)\n\t\tbackupRouter.POST(\"/record/del\", baseApi.DeleteBackupRecord)\n\t\tbackupRouter.POST(\"/record/description/update\", baseApi.UpdateRecordDescription)\n\t}\n}\n"
  },
  {
    "path": "agent/router/common.go",
    "content": "package router\n\nfunc commonGroups() []CommonRouter {\n\treturn []CommonRouter{\n\t\t&DashboardRouter{},\n\t\t&HostRouter{},\n\t\t&ContainerRouter{},\n\t\t&LogRouter{},\n\t\t&FileRouter{},\n\t\t&ToolboxRouter{},\n\t\t&CronjobRouter{},\n\t\t&BackupRouter{},\n\t\t&SettingRouter{},\n\t\t&AppRouter{},\n\t\t&WebsiteRouter{},\n\t\t&WebsiteDnsAccountRouter{},\n\t\t&WebsiteAcmeAccountRouter{},\n\t\t&WebsiteSSLRouter{},\n\t\t&DatabaseRouter{},\n\t\t&NginxRouter{},\n\t\t&RuntimeRouter{},\n\t\t&ProcessRouter{},\n\t\t&WebsiteCARouter{},\n\t\t&AIToolsRouter{},\n\t\t&GroupRouter{},\n\t\t&AlertRouter{},\n\t}\n}\n"
  },
  {
    "path": "agent/router/entry.go",
    "content": "//go:build !xpack\n\npackage router\n\nfunc RouterGroups() []CommonRouter {\n\treturn commonGroups()\n}\n\nvar RouterGroupApp = RouterGroups()\n"
  },
  {
    "path": "agent/router/ro_ai.go",
    "content": "package router\n\nimport (\n\tv1 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype AIToolsRouter struct {\n}\n\nfunc (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {\n\taiToolsRouter := Router.Group(\"ai\")\n\n\tbaseApi := v1.ApiGroupApp.BaseApi\n\t{\n\t\taiToolsRouter.POST(\"/ollama/close\", baseApi.CloseOllamaModel)\n\t\taiToolsRouter.POST(\"/ollama/model\", baseApi.CreateOllamaModel)\n\t\taiToolsRouter.POST(\"/ollama/model/recreate\", baseApi.RecreateOllamaModel)\n\t\taiToolsRouter.POST(\"/ollama/model/search\", baseApi.SearchOllamaModel)\n\t\taiToolsRouter.POST(\"/ollama/model/sync\", baseApi.SyncOllamaModel)\n\t\taiToolsRouter.POST(\"/ollama/model/load\", baseApi.LoadOllamaModelDetail)\n\t\taiToolsRouter.POST(\"/ollama/model/del\", baseApi.DeleteOllamaModel)\n\t\taiToolsRouter.GET(\"/gpu/load\", baseApi.LoadGpuInfo)\n\t\taiToolsRouter.POST(\"/domain/bind\", baseApi.BindDomain)\n\t\taiToolsRouter.POST(\"/domain/get\", baseApi.GetBindDomain)\n\t\taiToolsRouter.POST(\"/domain/update\", baseApi.UpdateBindDomain)\n\n\t\taiToolsRouter.POST(\"/mcp/search\", baseApi.PageMcpServers)\n\t\taiToolsRouter.POST(\"/mcp/server\", baseApi.CreateMcpServer)\n\t\taiToolsRouter.POST(\"/mcp/server/update\", baseApi.UpdateMcpServer)\n\t\taiToolsRouter.POST(\"/mcp/server/del\", baseApi.DeleteMcpServer)\n\t\taiToolsRouter.POST(\"/mcp/server/op\", baseApi.OperateMcpServer)\n\t\taiToolsRouter.POST(\"/mcp/domain/bind\", baseApi.BindMcpDomain)\n\t\taiToolsRouter.GET(\"/mcp/domain/get\", baseApi.GetMcpBindDomain)\n\t\taiToolsRouter.POST(\"/mcp/domain/update\", baseApi.UpdateMcpBindDomain)\n\n\t\taiToolsRouter.POST(\"/tensorrt/search\", baseApi.PageTensorRTLLMs)\n\t\taiToolsRouter.POST(\"/tensorrt/create\", baseApi.CreateTensorRTLLM)\n\t\taiToolsRouter.POST(\"/tensorrt/update\", baseApi.UpdateTensorRTLLM)\n\t\taiToolsRouter.POST(\"/tensorrt/delete\", baseApi.DeleteTensorRTLLM)\n\t\taiToolsRouter.POST(\"/tensorrt/operate\", baseApi.OperateTensorRTLLM)\n\n\t\taiToolsRouter.POST(\"/agents\", baseApi.CreateAgent)\n\t\taiToolsRouter.POST(\"/agents/search\", baseApi.PageAgents)\n\t\taiToolsRouter.POST(\"/agents/delete\", baseApi.DeleteAgent)\n\t\taiToolsRouter.POST(\"/agents/token/reset\", baseApi.ResetAgentToken)\n\t\taiToolsRouter.POST(\"/agents/model/update\", baseApi.UpdateAgentModelConfig)\n\t\taiToolsRouter.GET(\"/agents/providers\", baseApi.GetAgentProviders)\n\t\taiToolsRouter.POST(\"/agents/accounts\", baseApi.CreateAgentAccount)\n\t\taiToolsRouter.POST(\"/agents/accounts/update\", baseApi.UpdateAgentAccount)\n\t\taiToolsRouter.POST(\"/agents/accounts/search\", baseApi.PageAgentAccounts)\n\t\taiToolsRouter.POST(\"/agents/accounts/models\", baseApi.GetAgentAccountModels)\n\t\taiToolsRouter.POST(\"/agents/accounts/models/create\", baseApi.CreateAgentAccountModel)\n\t\taiToolsRouter.POST(\"/agents/accounts/models/update\", baseApi.UpdateAgentAccountModel)\n\t\taiToolsRouter.POST(\"/agents/accounts/models/delete\", baseApi.DeleteAgentAccountModel)\n\t\taiToolsRouter.POST(\"/agents/accounts/verify\", baseApi.VerifyAgentAccount)\n\t\taiToolsRouter.POST(\"/agents/accounts/delete\", baseApi.DeleteAgentAccount)\n\t\taiToolsRouter.POST(\"/agents/channel/feishu/get\", baseApi.GetAgentFeishuConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/feishu/update\", baseApi.UpdateAgentFeishuConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/telegram/get\", baseApi.GetAgentTelegramConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/telegram/update\", baseApi.UpdateAgentTelegramConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/discord/get\", baseApi.GetAgentDiscordConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/discord/update\", baseApi.UpdateAgentDiscordConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/wecom/get\", baseApi.GetAgentWecomConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/wecom/update\", baseApi.UpdateAgentWecomConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/dingtalk/get\", baseApi.GetAgentDingTalkConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/dingtalk/update\", baseApi.UpdateAgentDingTalkConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/qqbot/get\", baseApi.GetAgentQQBotConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/qqbot/update\", baseApi.UpdateAgentQQBotConfig)\n\t\taiToolsRouter.POST(\"/agents/plugin/install\", baseApi.InstallAgentPlugin)\n\t\taiToolsRouter.POST(\"/agents/plugin/check\", baseApi.CheckAgentPlugin)\n\t\taiToolsRouter.POST(\"/agents/security/get\", baseApi.GetAgentSecurityConfig)\n\t\taiToolsRouter.POST(\"/agents/security/update\", baseApi.UpdateAgentSecurityConfig)\n\t\taiToolsRouter.POST(\"/agents/other/get\", baseApi.GetAgentOtherConfig)\n\t\taiToolsRouter.POST(\"/agents/other/update\", baseApi.UpdateAgentOtherConfig)\n\t\taiToolsRouter.POST(\"/agents/channel/pairing/approve\", baseApi.ApproveAgentChannelPairing)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_alert.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype AlertRouter struct {\n}\n\nfunc (a *AlertRouter) InitRouter(Router *gin.RouterGroup) {\n\talertRouter := Router.Group(\"alert\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\talertRouter.POST(\"\", baseApi.CreateAlert)\n\t\talertRouter.POST(\"/update\", baseApi.UpdateAlert)\n\t\talertRouter.POST(\"/search\", baseApi.PageAlert)\n\t\talertRouter.POST(\"/status\", baseApi.UpdateAlertStatus)\n\t\talertRouter.POST(\"/del\", baseApi.DeleteAlert)\n\t\talertRouter.GET(\"/disks/list\", baseApi.GetDisks)\n\t\talertRouter.POST(\"/logs/search\", baseApi.PageAlertLogs)\n\t\talertRouter.POST(\"/logs/clean\", baseApi.CleanAlertLogs)\n\t\talertRouter.GET(\"/clams/list\", baseApi.GetClams)\n\t\talertRouter.POST(\"/cronjob/list\", baseApi.GetCronJobs)\n\n\t\talertRouter.POST(\"/config/update\", baseApi.UpdateAlertConfig)\n\t\talertRouter.POST(\"/config/info\", baseApi.GetAlertConfig)\n\t\talertRouter.POST(\"/config/del\", baseApi.DeleteAlertConfig)\n\t\talertRouter.POST(\"/config/test\", baseApi.TestAlertConfig)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_app.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype AppRouter struct {\n}\n\nfunc (a *AppRouter) InitRouter(Router *gin.RouterGroup) {\n\tappRouter := Router.Group(\"apps\")\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tappRouter.POST(\"/sync/remote\", baseApi.SyncApp)\n\t\tappRouter.POST(\"/sync/local\", baseApi.SyncLocalApp)\n\t\tappRouter.GET(\"/checkupdate\", baseApi.GetAppListUpdate)\n\t\tappRouter.POST(\"/search\", baseApi.SearchApp)\n\t\tappRouter.GET(\"/:key\", baseApi.GetApp)\n\t\tappRouter.GET(\"/detail/:appId/:version/:type\", baseApi.GetAppDetail)\n\t\tappRouter.GET(\"/detail/node/:appKey/:version\", baseApi.GetAppDetailForNode)\n\t\tappRouter.GET(\"/details/:id\", baseApi.GetAppDetailByID)\n\t\tappRouter.POST(\"/install\", baseApi.InstallApp)\n\t\tappRouter.GET(\"/tags\", baseApi.GetAppTags)\n\t\tappRouter.GET(\"/icon/:key\", baseApi.GetAppIcon)\n\n\t\tappRouter.POST(\"/installed/check\", baseApi.CheckAppInstalled)\n\t\tappRouter.POST(\"/installed/loadport\", baseApi.LoadPort)\n\t\tappRouter.POST(\"/installed/conninfo\", baseApi.LoadConnInfo)\n\t\tappRouter.GET(\"/installed/delete/check/:appInstallId\", baseApi.DeleteCheck)\n\t\tappRouter.POST(\"/installed/search\", baseApi.SearchAppInstalled)\n\t\tappRouter.GET(\"/installed/list\", baseApi.ListAppInstalled)\n\t\tappRouter.POST(\"/installed/op\", baseApi.OperateInstalled)\n\t\tappRouter.POST(\"/installed/sync\", baseApi.SyncInstalled)\n\t\tappRouter.POST(\"/installed/port/change\", baseApi.ChangeAppPort)\n\t\tappRouter.GET(\"/services/:key\", baseApi.GetServices)\n\t\tappRouter.POST(\"/installed/conf\", baseApi.GetDefaultConfig)\n\t\tappRouter.GET(\"/installed/params/:appInstallId\", baseApi.GetParams)\n\t\tappRouter.POST(\"/installed/params/update\", baseApi.UpdateInstalled)\n\t\tappRouter.POST(\"/installed/update/versions\", baseApi.GetUpdateVersions)\n\t\tappRouter.POST(\"/installed/config/update\", baseApi.UpdateAppConfig)\n\t\tappRouter.POST(\"/installed/sort/update\", baseApi.UpdateAppInstallSort)\n\t\tappRouter.GET(\"/installed/info/:appInstallId\", baseApi.GetAppInstallInfo)\n\n\t\tappRouter.POST(\"/installed/ignore\", baseApi.IgnoreAppUpgrade)\n\t\tappRouter.GET(\"/ignored/detail\", baseApi.ListAppIgnored)\n\t\tappRouter.POST(\"/ignored/cancel\", baseApi.CancelIgnoreAppUpgrade)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_container.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype ContainerRouter struct{}\n\nfunc (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {\n\tbaRouter := Router.Group(\"containers\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tbaRouter.GET(\"/exec\", baseApi.ContainerWsSSH)\n\t\tbaRouter.GET(\"/stats/:id\", baseApi.ContainerStats)\n\n\t\tbaRouter.POST(\"\", baseApi.ContainerCreate)\n\t\tbaRouter.POST(\"/update\", baseApi.ContainerUpdate)\n\t\tbaRouter.POST(\"/upgrade\", baseApi.ContainerUpgrade)\n\t\tbaRouter.POST(\"/info\", baseApi.ContainerInfo)\n\t\tbaRouter.POST(\"/search\", baseApi.SearchContainer)\n\t\tbaRouter.POST(\"/list\", baseApi.ListContainer)\n\t\tbaRouter.POST(\"/list/byimage\", baseApi.ListContainerByImage)\n\t\tbaRouter.GET(\"/status\", baseApi.LoadContainerStatus)\n\t\tbaRouter.GET(\"/list/stats\", baseApi.ContainerListStats)\n\t\tbaRouter.POST(\"/item/stats\", baseApi.ContainerItemStats)\n\t\tbaRouter.GET(\"/search/log\", baseApi.ContainerStreamLogs)\n\t\tbaRouter.POST(\"/download/log\", baseApi.DownloadContainerLogs)\n\t\tbaRouter.GET(\"/limit\", baseApi.LoadResourceLimit)\n\t\tbaRouter.POST(\"/clean/log\", baseApi.CleanContainerLog)\n\t\tbaRouter.POST(\"/inspect\", baseApi.Inspect)\n\t\tbaRouter.POST(\"/rename\", baseApi.ContainerRename)\n\t\tbaRouter.POST(\"/commit\", baseApi.ContainerCommit)\n\t\tbaRouter.POST(\"/operate\", baseApi.ContainerOperation)\n\t\tbaRouter.POST(\"/prune\", baseApi.ContainerPrune)\n\n\t\tbaRouter.POST(\"/users\", baseApi.LoadContainerUsers)\n\t\tbaRouter.POST(\"/files/search\", baseApi.ListContainerFiles)\n\t\tbaRouter.POST(\"/files/upload\", baseApi.UploadContainerFile)\n\t\tbaRouter.POST(\"/files/content\", baseApi.GetContainerFileContent)\n\t\tbaRouter.POST(\"/files/size\", baseApi.GetContainerFileSize)\n\t\tbaRouter.POST(\"/files/del\", baseApi.DeleteContainerFile)\n\t\tbaRouter.POST(\"/files/download\", baseApi.DownloadContainerFile)\n\n\t\tbaRouter.GET(\"/repo\", baseApi.ListRepo)\n\t\tbaRouter.POST(\"/repo/status\", baseApi.CheckRepoStatus)\n\t\tbaRouter.POST(\"/repo/search\", baseApi.SearchRepo)\n\t\tbaRouter.POST(\"/repo/update\", baseApi.UpdateRepo)\n\t\tbaRouter.POST(\"/repo\", baseApi.CreateRepo)\n\t\tbaRouter.POST(\"/repo/del\", baseApi.DeleteRepo)\n\n\t\tbaRouter.POST(\"/compose/search\", baseApi.SearchCompose)\n\t\tbaRouter.POST(\"/compose\", baseApi.CreateCompose)\n\t\tbaRouter.POST(\"/compose/env\", baseApi.LoadComposeEnv)\n\t\tbaRouter.POST(\"/compose/test\", baseApi.TestCompose)\n\t\tbaRouter.POST(\"/compose/operate\", baseApi.OperatorCompose)\n\t\tbaRouter.POST(\"/compose/clean/log\", baseApi.CleanComposeLog)\n\t\tbaRouter.POST(\"/compose/update\", baseApi.ComposeUpdate)\n\n\t\tbaRouter.GET(\"/template\", baseApi.ListComposeTemplate)\n\t\tbaRouter.POST(\"/template/search\", baseApi.SearchComposeTemplate)\n\t\tbaRouter.POST(\"/template/update\", baseApi.UpdateComposeTemplate)\n\t\tbaRouter.POST(\"/template/batch\", baseApi.BatchComposeTemplate)\n\t\tbaRouter.POST(\"/template\", baseApi.CreateComposeTemplate)\n\t\tbaRouter.POST(\"/template/del\", baseApi.DeleteComposeTemplate)\n\n\t\tbaRouter.GET(\"/image\", baseApi.ListImage)\n\t\tbaRouter.GET(\"/image/all\", baseApi.ListAllImage)\n\t\tbaRouter.POST(\"/image/search\", baseApi.SearchImage)\n\t\tbaRouter.POST(\"/image/pull\", baseApi.ImagePull)\n\t\tbaRouter.POST(\"/image/push\", baseApi.ImagePush)\n\t\tbaRouter.POST(\"/image/save\", baseApi.ImageSave)\n\t\tbaRouter.POST(\"/image/load\", baseApi.ImageLoad)\n\t\tbaRouter.POST(\"/image/remove\", baseApi.ImageRemove)\n\t\tbaRouter.POST(\"/image/tag\", baseApi.ImageTag)\n\t\tbaRouter.POST(\"/image/build\", baseApi.ImageBuild)\n\n\t\tbaRouter.GET(\"/network\", baseApi.ListNetwork)\n\t\tbaRouter.POST(\"/network/del\", baseApi.DeleteNetwork)\n\t\tbaRouter.POST(\"/network/search\", baseApi.SearchNetwork)\n\t\tbaRouter.POST(\"/network\", baseApi.CreateNetwork)\n\t\tbaRouter.GET(\"/volume\", baseApi.ListVolume)\n\t\tbaRouter.POST(\"/volume/del\", baseApi.DeleteVolume)\n\t\tbaRouter.POST(\"/volume/search\", baseApi.SearchVolume)\n\t\tbaRouter.POST(\"/volume\", baseApi.CreateVolume)\n\n\t\tbaRouter.GET(\"/daemonjson\", baseApi.LoadDaemonJson)\n\t\tbaRouter.GET(\"/daemonjson/file\", baseApi.LoadDaemonJsonFile)\n\t\tbaRouter.GET(\"/docker/status\", baseApi.LoadDockerStatus)\n\t\tbaRouter.POST(\"/docker/operate\", baseApi.OperateDocker)\n\t\tbaRouter.POST(\"/daemonjson/update\", baseApi.UpdateDaemonJson)\n\t\tbaRouter.POST(\"/logoption/update\", baseApi.UpdateLogOption)\n\t\tbaRouter.POST(\"/ipv6option/update\", baseApi.UpdateIpv6Option)\n\t\tbaRouter.POST(\"/daemonjson/update/byfile\", baseApi.UpdateDaemonJsonByFile)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_cronjob.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype CronjobRouter struct{}\n\nfunc (s *CronjobRouter) InitRouter(Router *gin.RouterGroup) {\n\tcmdRouter := Router.Group(\"cronjobs\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tcmdRouter.POST(\"\", baseApi.CreateCronjob)\n\t\tcmdRouter.POST(\"/next\", baseApi.LoadNextHandle)\n\t\tcmdRouter.POST(\"/import\", baseApi.ImportCronjob)\n\t\tcmdRouter.POST(\"/export\", baseApi.ExportCronjob)\n\t\tcmdRouter.POST(\"/load/info\", baseApi.LoadCronjobInfo)\n\t\tcmdRouter.GET(\"/script/options\", baseApi.LoadScriptOptions)\n\t\tcmdRouter.POST(\"/del\", baseApi.DeleteCronjob)\n\t\tcmdRouter.POST(\"/stop\", baseApi.StopCronJob)\n\t\tcmdRouter.POST(\"/update\", baseApi.UpdateCronjob)\n\t\tcmdRouter.POST(\"/group/update\", baseApi.UpdateCronjobGroup)\n\t\tcmdRouter.POST(\"/status\", baseApi.UpdateCronjobStatus)\n\t\tcmdRouter.POST(\"/handle\", baseApi.HandleOnce)\n\t\tcmdRouter.POST(\"/search\", baseApi.SearchCronjob)\n\t\tcmdRouter.POST(\"/search/records\", baseApi.SearchJobRecords)\n\t\tcmdRouter.POST(\"/records/log\", baseApi.LoadRecordLog)\n\t\tcmdRouter.POST(\"/records/clean\", baseApi.CleanRecord)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_dashboard.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype DashboardRouter struct{}\n\nfunc (s *DashboardRouter) InitRouter(Router *gin.RouterGroup) {\n\tcmdRouter := Router.Group(\"dashboard\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tcmdRouter.GET(\"/base/os\", baseApi.LoadDashboardOsInfo)\n\t\tcmdRouter.GET(\"/quick/option\", baseApi.LoadQuickOption)\n\t\tcmdRouter.POST(\"/quick/change\", baseApi.UpdateQuickJump)\n\t\tcmdRouter.GET(\"/app/launcher\", baseApi.LoadAppLauncher)\n\t\tcmdRouter.POST(\"/app/launcher/show\", baseApi.UpdateAppLauncher)\n\t\tcmdRouter.POST(\"/app/launcher/option\", baseApi.LoadAppLauncherOption)\n\t\tcmdRouter.GET(\"/base/:ioOption/:netOption\", baseApi.LoadDashboardBaseInfo)\n\t\tcmdRouter.GET(\"/current/node\", baseApi.LoadCurrentInfoForNode)\n\t\tcmdRouter.GET(\"/current/:ioOption/:netOption\", baseApi.LoadDashboardCurrentInfo)\n\t\tcmdRouter.GET(\"/current/top/cpu\", baseApi.LoadDashboardTopCPU)\n\t\tcmdRouter.GET(\"/current/top/mem\", baseApi.LoadDashboardTopMem)\n\t\tcmdRouter.POST(\"/system/restart/:operation\", baseApi.SystemRestart)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_database.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype DatabaseRouter struct{}\n\nfunc (s *DatabaseRouter) InitRouter(Router *gin.RouterGroup) {\n\tcmdRouter := Router.Group(\"databases\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tcmdRouter.POST(\"/common/info\", baseApi.LoadDBBaseInfo)\n\t\tcmdRouter.POST(\"/common/load/file\", baseApi.LoadDBFile)\n\t\tcmdRouter.POST(\"/common/update/conf\", baseApi.UpdateDBConfByFile)\n\n\t\tcmdRouter.POST(\"\", baseApi.CreateMysql)\n\t\tcmdRouter.POST(\"/bind\", baseApi.BindUser)\n\t\tcmdRouter.POST(\"load\", baseApi.LoadDBFromRemote)\n\t\tcmdRouter.POST(\"/change/access\", baseApi.ChangeMysqlAccess)\n\t\tcmdRouter.POST(\"/change/password\", baseApi.ChangeMysqlPassword)\n\t\tcmdRouter.POST(\"/del/check\", baseApi.DeleteCheckMysql)\n\t\tcmdRouter.POST(\"/del\", baseApi.DeleteMysql)\n\t\tcmdRouter.POST(\"/description/update\", baseApi.UpdateMysqlDescription)\n\t\tcmdRouter.POST(\"/variables/update\", baseApi.UpdateMysqlVariables)\n\t\tcmdRouter.POST(\"/search\", baseApi.SearchMysql)\n\t\tcmdRouter.POST(\"/variables\", baseApi.LoadVariables)\n\t\tcmdRouter.POST(\"/status\", baseApi.LoadStatus)\n\t\tcmdRouter.POST(\"/remote\", baseApi.LoadRemoteAccess)\n\t\tcmdRouter.POST(\"/format/options\", baseApi.ListDBFormatCollationOptions)\n\n\t\tcmdRouter.POST(\"/redis/persistence/conf\", baseApi.LoadPersistenceConf)\n\t\tcmdRouter.POST(\"/redis/status\", baseApi.LoadRedisStatus)\n\t\tcmdRouter.POST(\"/redis/conf\", baseApi.LoadRedisConf)\n\t\tcmdRouter.GET(\"/redis/check\", baseApi.CheckHasCli)\n\t\tcmdRouter.POST(\"/redis/install/cli\", baseApi.InstallCli)\n\t\tcmdRouter.POST(\"/redis/password\", baseApi.ChangeRedisPassword)\n\t\tcmdRouter.POST(\"/redis/conf/update\", baseApi.UpdateRedisConf)\n\t\tcmdRouter.POST(\"/redis/persistence/update\", baseApi.UpdateRedisPersistenceConf)\n\n\t\tcmdRouter.POST(\"/db/check\", baseApi.CheckDatabase)\n\t\tcmdRouter.POST(\"/db\", baseApi.CreateDatabase)\n\t\tcmdRouter.GET(\"/db/:name\", baseApi.GetDatabase)\n\t\tcmdRouter.GET(\"/db/list/:type\", baseApi.ListDatabase)\n\t\tcmdRouter.GET(\"/db/item/:type\", baseApi.LoadDatabaseItems)\n\t\tcmdRouter.POST(\"/db/update\", baseApi.UpdateDatabase)\n\t\tcmdRouter.POST(\"/db/search\", baseApi.SearchDatabase)\n\t\tcmdRouter.POST(\"/db/del/check\", baseApi.DeleteCheckDatabase)\n\t\tcmdRouter.POST(\"/db/del\", baseApi.DeleteDatabase)\n\n\t\tcmdRouter.POST(\"/pg\", baseApi.CreatePostgresql)\n\t\tcmdRouter.POST(\"/pg/search\", baseApi.SearchPostgresql)\n\t\tcmdRouter.POST(\"/pg/:database/load\", baseApi.LoadPostgresqlDBFromRemote)\n\t\tcmdRouter.POST(\"/pg/bind\", baseApi.BindPostgresqlUser)\n\t\tcmdRouter.POST(\"/pg/del/check\", baseApi.DeleteCheckPostgresql)\n\t\tcmdRouter.POST(\"/pg/del\", baseApi.DeletePostgresql)\n\t\tcmdRouter.POST(\"/pg/privileges\", baseApi.ChangePostgresqlPrivileges)\n\t\tcmdRouter.POST(\"/pg/password\", baseApi.ChangePostgresqlPassword)\n\t\tcmdRouter.POST(\"/pg/description\", baseApi.UpdatePostgresqlDescription)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_file.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype FileRouter struct {\n}\n\nfunc (f *FileRouter) InitRouter(Router *gin.RouterGroup) {\n\tfileRouter := Router.Group(\"files\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tfileRouter.POST(\"/search\", baseApi.ListFiles)\n\t\tfileRouter.POST(\"/upload/search\", baseApi.SearchUploadWithPage)\n\t\tfileRouter.POST(\"/tree\", baseApi.GetFileTree)\n\t\tfileRouter.POST(\"\", baseApi.CreateFile)\n\t\tfileRouter.POST(\"/del\", baseApi.DeleteFile)\n\t\tfileRouter.POST(\"/batch/del\", baseApi.BatchDeleteFile)\n\t\tfileRouter.POST(\"/mode\", baseApi.ChangeFileMode)\n\t\tfileRouter.POST(\"/owner\", baseApi.ChangeFileOwner)\n\t\tfileRouter.POST(\"/compress\", baseApi.CompressFile)\n\t\tfileRouter.POST(\"/decompress\", baseApi.DeCompressFile)\n\t\tfileRouter.POST(\"/content\", baseApi.GetContent)\n\t\tfileRouter.POST(\"/preview\", baseApi.PreviewContent)\n\t\tfileRouter.POST(\"/save\", baseApi.SaveContent)\n\t\tfileRouter.POST(\"/remarks\", baseApi.BatchGetFileRemarks)\n\t\tfileRouter.POST(\"/remark\", baseApi.SetFileRemark)\n\t\tfileRouter.POST(\"/check\", baseApi.CheckFile)\n\t\tfileRouter.POST(\"/batch/check\", baseApi.BatchCheckFiles)\n\t\tfileRouter.POST(\"/upload\", baseApi.UploadFiles)\n\t\tfileRouter.POST(\"/chunkupload\", baseApi.UploadChunkFiles)\n\t\tfileRouter.POST(\"/rename\", baseApi.ChangeFileName)\n\t\tfileRouter.POST(\"/wget\", baseApi.WgetFile)\n\t\tfileRouter.POST(\"/move\", baseApi.MoveFile)\n\t\tfileRouter.GET(\"/download\", baseApi.Download)\n\t\tfileRouter.POST(\"/chunkdownload\", baseApi.DownloadChunkFiles)\n\t\tfileRouter.POST(\"/size\", baseApi.Size)\n\t\tfileRouter.POST(\"/depth/size\", baseApi.DepthDirSize)\n\t\tfileRouter.GET(\"/wget/process\", baseApi.WgetProcess)\n\t\tfileRouter.GET(\"/wget/process/keys\", baseApi.ProcessKeys)\n\t\tfileRouter.POST(\"/read\", baseApi.ReadFileByLine)\n\t\tfileRouter.POST(\"/batch/role\", baseApi.BatchChangeModeAndOwner)\n\n\t\tfileRouter.POST(\"/recycle/search\", baseApi.SearchRecycleBinFile)\n\t\tfileRouter.POST(\"/recycle/reduce\", baseApi.ReduceRecycleBinFile)\n\t\tfileRouter.POST(\"/recycle/clear\", baseApi.ClearRecycleBinFile)\n\t\tfileRouter.GET(\"/recycle/status\", baseApi.GetRecycleStatus)\n\n\t\tfileRouter.POST(\"/favorite/search\", baseApi.SearchFavorite)\n\t\tfileRouter.POST(\"/favorite\", baseApi.CreateFavorite)\n\t\tfileRouter.POST(\"/favorite/del\", baseApi.DeleteFavorite)\n\n\t\tfileRouter.GET(\"/path/:type\", baseApi.GetPathByType)\n\t\tfileRouter.POST(\"/mount\", baseApi.GetHostMount)\n\t\tfileRouter.POST(\"/user/group\", baseApi.GetUsersAndGroups)\n\t\tfileRouter.POST(\"/convert\", baseApi.ConvertFile)\n\t\tfileRouter.POST(\"/convert/log\", baseApi.ConvertLog)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_group.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype GroupRouter struct {\n}\n\nfunc (a *GroupRouter) InitRouter(Router *gin.RouterGroup) {\n\tgroupRouter := Router.Group(\"groups\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tgroupRouter.POST(\"\", baseApi.CreateGroup)\n\t\tgroupRouter.POST(\"/del\", baseApi.DeleteGroup)\n\t\tgroupRouter.POST(\"/update\", baseApi.UpdateGroup)\n\t\tgroupRouter.POST(\"/search\", baseApi.ListGroup)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_host.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype HostRouter struct{}\n\nfunc (s *HostRouter) InitRouter(Router *gin.RouterGroup) {\n\thostRouter := Router.Group(\"hosts\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\thostRouter.POST(\"\", baseApi.CreateHost)\n\t\thostRouter.POST(\"/info\", baseApi.GetHostByID)\n\t\thostRouter.POST(\"/del\", baseApi.DeleteHost)\n\t\thostRouter.POST(\"/update\", baseApi.UpdateHost)\n\t\thostRouter.POST(\"/update/group\", baseApi.UpdateHostGroup)\n\t\thostRouter.POST(\"/search\", baseApi.SearchHost)\n\t\thostRouter.POST(\"/tree\", baseApi.HostTree)\n\t\thostRouter.POST(\"/test/byinfo\", baseApi.TestByInfo)\n\t\thostRouter.POST(\"/test/byid\", baseApi.TestByID)\n\n\t\thostRouter.POST(\"/firewall/base\", baseApi.LoadFirewallBaseInfo)\n\t\thostRouter.POST(\"/firewall/search\", baseApi.SearchFirewallRule)\n\t\thostRouter.POST(\"/firewall/operate\", baseApi.OperateFirewall)\n\t\thostRouter.POST(\"/firewall/port\", baseApi.OperatePortRule)\n\t\thostRouter.POST(\"/firewall/forward\", baseApi.OperateForwardRule)\n\t\thostRouter.POST(\"/firewall/ip\", baseApi.OperateIPRule)\n\t\thostRouter.POST(\"/firewall/batch\", baseApi.BatchOperateRule)\n\t\thostRouter.POST(\"/firewall/update/port\", baseApi.UpdatePortRule)\n\t\thostRouter.POST(\"/firewall/update/addr\", baseApi.UpdateAddrRule)\n\t\thostRouter.POST(\"/firewall/update/description\", baseApi.UpdateFirewallDescription)\n\n\t\thostRouter.POST(\"/firewall/filter/rule/search\", baseApi.SearchFilterRules)\n\t\thostRouter.POST(\"/firewall/filter/rule/operate\", baseApi.OperateFilterRule)\n\t\thostRouter.POST(\"/firewall/filter/rule/batch\", baseApi.BatchOperateFilterRule)\n\t\thostRouter.POST(\"/firewall/filter/operate\", baseApi.OperateFilterChain)\n\t\thostRouter.POST(\"/firewall/filter/chain/status\", baseApi.LoadChainStatus)\n\n\t\thostRouter.POST(\"/monitor/search\", baseApi.LoadMonitor)\n\t\thostRouter.POST(\"/monitor/gpu/search\", baseApi.LoadGPUMonitor)\n\t\thostRouter.POST(\"/monitor/clean\", baseApi.CleanMonitor)\n\t\thostRouter.GET(\"/monitor/gpuoptions\", baseApi.GetCPUOptions)\n\t\thostRouter.GET(\"/monitor/netoptions\", baseApi.GetNetworkOptions)\n\t\thostRouter.GET(\"/monitor/iooptions\", baseApi.GetIOOptions)\n\t\thostRouter.GET(\"/monitor/setting\", baseApi.LoadMonitorSetting)\n\t\thostRouter.POST(\"/monitor/setting/update\", baseApi.UpdateMonitorSetting)\n\n\t\thostRouter.POST(\"/ssh/search\", baseApi.GetSSHInfo)\n\t\thostRouter.POST(\"/ssh/update\", baseApi.UpdateSSH)\n\t\thostRouter.POST(\"/ssh/log\", baseApi.LoadSSHLogs)\n\t\thostRouter.POST(\"/ssh/log/export\", baseApi.ExportSSHLogs)\n\t\thostRouter.POST(\"/ssh/operate\", baseApi.OperateSSH)\n\t\thostRouter.POST(\"/ssh/file\", baseApi.LoadSSHFile)\n\t\thostRouter.POST(\"/ssh/file/update\", baseApi.UpdateSSHByFile)\n\n\t\thostRouter.POST(\"/ssh/cert\", baseApi.CreateRootCert)\n\t\thostRouter.POST(\"/ssh/cert/update\", baseApi.EditRootCert)\n\t\thostRouter.POST(\"/ssh/cert/sync\", baseApi.SyncRootCert)\n\t\thostRouter.POST(\"/ssh/cert/search\", baseApi.SearchRootCert)\n\t\thostRouter.POST(\"/ssh/cert/delete\", baseApi.DeleteRootCert)\n\n\t\thostRouter.POST(\"/tool\", baseApi.GetToolStatus)\n\t\thostRouter.POST(\"/tool/init\", baseApi.InitToolConfig)\n\t\thostRouter.POST(\"/tool/operate\", baseApi.OperateTool)\n\t\thostRouter.POST(\"/tool/config\", baseApi.OperateToolConfig)\n\t\thostRouter.POST(\"/tool/supervisor/process\", baseApi.OperateProcess)\n\t\thostRouter.GET(\"/tool/supervisor/process\", baseApi.GetProcess)\n\t\thostRouter.POST(\"/tool/supervisor/process/file\", baseApi.GetProcessFile)\n\n\t\thostRouter.GET(\"/terminal\", baseApi.WsSSH)\n\n\t\thostRouter.GET(\"/disks\", baseApi.GetCompleteDiskInfo)\n\t\thostRouter.POST(\"/disks/partition\", baseApi.PartitionDisk)\n\t\thostRouter.POST(\"/disks/mount\", baseApi.MountDisk)\n\t\thostRouter.POST(\"/disks/unmount\", baseApi.UnmountDisk)\n\n\t\thostRouter.GET(\"/components/:name\", baseApi.CheckComponentExistence)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_log.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype LogRouter struct{}\n\nfunc (s *LogRouter) InitRouter(Router *gin.RouterGroup) {\n\toperationRouter := Router.Group(\"logs\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\toperationRouter.GET(\"/system/files\", baseApi.GetSystemFiles)\n\t\toperationRouter.POST(\"/tasks/search\", baseApi.PageTasks)\n\t\toperationRouter.GET(\"/tasks/executing/count\", baseApi.CountExecutingTasks)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_nginx.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype NginxRouter struct {\n}\n\nfunc (a *NginxRouter) InitRouter(Router *gin.RouterGroup) {\n\tgroupRouter := Router.Group(\"openresty\")\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tgroupRouter.GET(\"\", baseApi.GetNginx)\n\t\tgroupRouter.POST(\"/scope\", baseApi.GetNginxConfigByScope)\n\t\tgroupRouter.POST(\"/update\", baseApi.UpdateNginxConfigByScope)\n\t\tgroupRouter.GET(\"/status\", baseApi.GetNginxStatus)\n\t\tgroupRouter.POST(\"/file\", baseApi.UpdateNginxFile)\n\t\tgroupRouter.POST(\"/build\", baseApi.BuildNginx)\n\t\tgroupRouter.POST(\"/modules/update\", baseApi.UpdateNginxModule)\n\t\tgroupRouter.GET(\"/modules\", baseApi.GetNginxModules)\n\t\tgroupRouter.POST(\"/https\", baseApi.OperateDefaultHTTPs)\n\t\tgroupRouter.GET(\"/https\", baseApi.GetDefaultHTTPsStatus)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_process.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype ProcessRouter struct {\n}\n\nfunc (f *ProcessRouter) InitRouter(Router *gin.RouterGroup) {\n\tprocessRouter := Router.Group(\"process\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tprocessRouter.GET(\"/ws\", baseApi.ProcessWs)\n\t\tprocessRouter.POST(\"/stop\", baseApi.StopProcess)\n\t\tprocessRouter.POST(\"/listening\", baseApi.GetListeningProcess)\n\t\tprocessRouter.GET(\"/:pid\", baseApi.GetProcessInfoByPID)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_router.go",
    "content": "package router\n\nimport \"github.com/gin-gonic/gin\"\n\ntype CommonRouter interface {\n\tInitRouter(Router *gin.RouterGroup)\n}\n"
  },
  {
    "path": "agent/router/ro_runtime.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype RuntimeRouter struct {\n}\n\nfunc (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) {\n\tgroupRouter := Router.Group(\"runtimes\")\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tgroupRouter.GET(\"/installed/delete/check/:id\", baseApi.DeleteRuntimeCheck)\n\t\tgroupRouter.POST(\"/search\", baseApi.SearchRuntimes)\n\t\tgroupRouter.POST(\"\", baseApi.CreateRuntime)\n\t\tgroupRouter.POST(\"/del\", baseApi.DeleteRuntime)\n\t\tgroupRouter.POST(\"/update\", baseApi.UpdateRuntime)\n\t\tgroupRouter.GET(\"/:id\", baseApi.GetRuntime)\n\t\tgroupRouter.POST(\"/sync\", baseApi.SyncStatus)\n\n\t\tgroupRouter.POST(\"/node/package\", baseApi.GetNodePackageRunScript)\n\t\tgroupRouter.POST(\"/operate\", baseApi.OperateRuntime)\n\t\tgroupRouter.POST(\"/node/modules\", baseApi.GetNodeModules)\n\t\tgroupRouter.POST(\"/node/modules/operate\", baseApi.OperateNodeModules)\n\n\t\tgroupRouter.POST(\"/php/extensions/search\", baseApi.PagePHPExtensions)\n\t\tgroupRouter.POST(\"/php/extensions\", baseApi.CreatePHPExtensions)\n\t\tgroupRouter.POST(\"/php/extensions/update\", baseApi.UpdatePHPExtensions)\n\t\tgroupRouter.POST(\"/php/extensions/del\", baseApi.DeletePHPExtensions)\n\n\t\tgroupRouter.GET(\"/php/:id/extensions\", baseApi.GetRuntimeExtension)\n\t\tgroupRouter.POST(\"/php/extensions/install\", baseApi.InstallPHPExtension)\n\t\tgroupRouter.POST(\"/php/extensions/uninstall\", baseApi.UnInstallPHPExtension)\n\n\t\tgroupRouter.GET(\"/php/config/:id\", baseApi.GetPHPConfig)\n\t\tgroupRouter.POST(\"/php/config\", baseApi.UpdatePHPConfig)\n\t\tgroupRouter.POST(\"/php/update\", baseApi.UpdatePHPFile)\n\t\tgroupRouter.POST(\"/php/file\", baseApi.GetPHPConfigFile)\n\t\tgroupRouter.POST(\"/php/fpm/config\", baseApi.UpdateFPMConfig)\n\t\tgroupRouter.GET(\"/php/fpm/config/:id\", baseApi.GetFPMConfig)\n\t\tgroupRouter.GET(\"/php/fpm/status/:id\", baseApi.GetFPMStatus)\n\n\t\tgroupRouter.POST(\"/php/container/update\", baseApi.UpdatePHPContainer)\n\t\tgroupRouter.GET(\"/php/container/:id\", baseApi.GetPHPContainerConfig)\n\n\t\tgroupRouter.GET(\"/supervisor/process/:id\", baseApi.GetSupervisorProcess)\n\t\tgroupRouter.POST(\"/supervisor/process\", baseApi.OperateSupervisorProcess)\n\t\tgroupRouter.POST(\"/supervisor/process/file\", baseApi.OperateSupervisorProcessFile)\n\n\t\tgroupRouter.POST(\"/remark\", baseApi.UpdateRuntimeRemark)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_setting.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype SettingRouter struct{}\n\nfunc (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {\n\tsettingRouter := Router.Group(\"settings\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tsettingRouter.POST(\"/search\", baseApi.GetSettingInfo)\n\t\tsettingRouter.POST(\"/terminal/ai/search\", baseApi.GetTerminalAISettingInfo)\n\t\tsettingRouter.GET(\"/search/available\", baseApi.GetSystemAvailable)\n\t\tsettingRouter.POST(\"/update\", baseApi.UpdateSetting)\n\t\tsettingRouter.POST(\"/terminal/ai/update\", baseApi.UpdateTerminalAISetting)\n\t\tsettingRouter.GET(\"/get/:key\", baseApi.GetSettingByKey)\n\n\t\tsettingRouter.POST(\"/description/save\", baseApi.SaveDescription)\n\n\t\tsettingRouter.GET(\"/snapshot/load\", baseApi.LoadSnapshotData)\n\t\tsettingRouter.POST(\"/snapshot\", baseApi.CreateSnapshot)\n\t\tsettingRouter.POST(\"/snapshot/recreate\", baseApi.RecreateSnapshot)\n\t\tsettingRouter.POST(\"/snapshot/search\", baseApi.SearchSnapshot)\n\t\tsettingRouter.POST(\"/snapshot/import\", baseApi.ImportSnapshot)\n\t\tsettingRouter.POST(\"/snapshot/del\", baseApi.DeleteSnapshot)\n\t\tsettingRouter.POST(\"/snapshot/recover\", baseApi.RecoverSnapshot)\n\t\tsettingRouter.POST(\"/snapshot/rollback\", baseApi.RollbackSnapshot)\n\t\tsettingRouter.POST(\"/snapshot/description/update\", baseApi.UpdateSnapDescription)\n\n\t\tsettingRouter.GET(\"/basedir\", baseApi.LoadBaseDir)\n\n\t\tsettingRouter.POST(\"/ssh/check\", baseApi.CheckLocalConn)\n\t\tsettingRouter.GET(\"/ssh/conn\", baseApi.LoadLocalConn)\n\t\tsettingRouter.POST(\"/ssh/default\", baseApi.SetDefaultIsConn)\n\t\tsettingRouter.POST(\"/ssh\", baseApi.SaveLocalConn)\n\t\tsettingRouter.POST(\"/ssh/check/info\", baseApi.CheckLocalConnByInfo)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_toolbox.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype ToolboxRouter struct{}\n\nfunc (s *ToolboxRouter) InitRouter(Router *gin.RouterGroup) {\n\ttoolboxRouter := Router.Group(\"toolbox\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\ttoolboxRouter.POST(\"/device/base\", baseApi.LoadDeviceBaseInfo)\n\t\ttoolboxRouter.GET(\"/device/users\", baseApi.LoadUsers)\n\t\ttoolboxRouter.GET(\"/device/zone/options\", baseApi.LoadTimeOption)\n\t\ttoolboxRouter.POST(\"/device/update/conf\", baseApi.UpdateDeviceConf)\n\t\ttoolboxRouter.POST(\"/device/update/host\", baseApi.UpdateDeviceHost)\n\t\ttoolboxRouter.POST(\"/device/update/passwd\", baseApi.UpdateDevicePasswd)\n\t\ttoolboxRouter.POST(\"/device/update/swap\", baseApi.UpdateDeviceSwap)\n\t\ttoolboxRouter.POST(\"/device/update/byconf\", baseApi.UpdateDeviceByFile)\n\t\ttoolboxRouter.POST(\"/device/check/dns\", baseApi.CheckDNS)\n\t\ttoolboxRouter.POST(\"/device/conf\", baseApi.LoadDeviceConf)\n\n\t\ttoolboxRouter.POST(\"/scan\", baseApi.ScanSystem)\n\t\ttoolboxRouter.POST(\"/clean\", baseApi.SystemClean)\n\n\t\ttoolboxRouter.GET(\"/fail2ban/base\", baseApi.LoadFail2BanBaseInfo)\n\t\ttoolboxRouter.GET(\"/fail2ban/load/conf\", baseApi.LoadFail2BanConf)\n\t\ttoolboxRouter.POST(\"/fail2ban/search\", baseApi.SearchFail2Ban)\n\t\ttoolboxRouter.POST(\"/fail2ban/operate\", baseApi.OperateFail2Ban)\n\t\ttoolboxRouter.POST(\"/fail2ban/operate/sshd\", baseApi.OperateSSHD)\n\t\ttoolboxRouter.POST(\"/fail2ban/update\", baseApi.UpdateFail2BanConf)\n\t\ttoolboxRouter.POST(\"/fail2ban/update/byconf\", baseApi.UpdateFail2BanConfByFile)\n\n\t\ttoolboxRouter.GET(\"/ftp/base\", baseApi.LoadFtpBaseInfo)\n\t\ttoolboxRouter.POST(\"/ftp/log/search\", baseApi.LoadFtpLogInfo)\n\t\ttoolboxRouter.POST(\"/ftp/operate\", baseApi.OperateFtp)\n\t\ttoolboxRouter.POST(\"/ftp/search\", baseApi.SearchFtp)\n\t\ttoolboxRouter.POST(\"/ftp\", baseApi.CreateFtp)\n\t\ttoolboxRouter.POST(\"/ftp/update\", baseApi.UpdateFtp)\n\t\ttoolboxRouter.POST(\"/ftp/del\", baseApi.DeleteFtp)\n\t\ttoolboxRouter.POST(\"/ftp/sync\", baseApi.SyncFtp)\n\n\t\ttoolboxRouter.POST(\"/clam/search\", baseApi.SearchClam)\n\t\ttoolboxRouter.POST(\"/clam/record/search\", baseApi.SearchClamRecord)\n\t\ttoolboxRouter.POST(\"/clam/record/clean\", baseApi.CleanClamRecord)\n\t\ttoolboxRouter.POST(\"/clam/file/search\", baseApi.SearchClamFile)\n\t\ttoolboxRouter.POST(\"/clam/file/update\", baseApi.UpdateFile)\n\t\ttoolboxRouter.POST(\"/clam\", baseApi.CreateClam)\n\t\ttoolboxRouter.POST(\"/clam/base\", baseApi.LoadClamBaseInfo)\n\t\ttoolboxRouter.POST(\"/clam/operate\", baseApi.OperateClam)\n\t\ttoolboxRouter.POST(\"/clam/update\", baseApi.UpdateClam)\n\t\ttoolboxRouter.POST(\"/clam/status/update\", baseApi.UpdateClamStatus)\n\t\ttoolboxRouter.POST(\"/clam/del\", baseApi.DeleteClam)\n\t\ttoolboxRouter.POST(\"/clam/handle\", baseApi.HandleClamScan)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_website.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype WebsiteRouter struct {\n}\n\nfunc (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {\n\twebsiteRouter := Router.Group(\"websites\")\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\twebsiteRouter.POST(\"/search\", baseApi.PageWebsite)\n\t\twebsiteRouter.GET(\"/list\", baseApi.GetWebsites)\n\t\twebsiteRouter.POST(\"\", baseApi.CreateWebsite)\n\t\twebsiteRouter.POST(\"/operate\", baseApi.OpWebsite)\n\t\twebsiteRouter.POST(\"/log\", baseApi.OpWebsiteLog)\n\t\twebsiteRouter.POST(\"/check\", baseApi.CreateWebsiteCheck)\n\t\twebsiteRouter.POST(\"/options\", baseApi.GetWebsiteOptions)\n\t\twebsiteRouter.POST(\"/update\", baseApi.UpdateWebsite)\n\t\twebsiteRouter.GET(\"/:id\", baseApi.GetWebsite)\n\t\twebsiteRouter.POST(\"/del\", baseApi.DeleteWebsite)\n\t\twebsiteRouter.POST(\"/default/server\", baseApi.ChangeDefaultServer)\n\t\twebsiteRouter.POST(\"/group/change\", baseApi.ChangeWebsiteGroup)\n\n\t\twebsiteRouter.POST(\"/batch/operate\", baseApi.BatchOpWebsites)\n\t\twebsiteRouter.POST(\"/batch/group\", baseApi.BatchSetWebsiteGroup)\n\t\twebsiteRouter.POST(\"/batch/ssl\", baseApi.BatchSetHttps)\n\n\t\twebsiteRouter.GET(\"/domains/:websiteId\", baseApi.GetWebDomains)\n\t\twebsiteRouter.POST(\"/domains/del\", baseApi.DeleteWebDomain)\n\t\twebsiteRouter.POST(\"/domains\", baseApi.CreateWebDomain)\n\t\twebsiteRouter.POST(\"/domains/update\", baseApi.UpdateWebDomain)\n\n\t\twebsiteRouter.GET(\"/:id/config/:type\", baseApi.GetWebsiteNginx)\n\t\twebsiteRouter.POST(\"/config\", baseApi.GetNginxConfig)\n\t\twebsiteRouter.POST(\"/config/update\", baseApi.UpdateNginxConfig)\n\t\twebsiteRouter.POST(\"/nginx/update\", baseApi.UpdateWebsiteNginxConfig)\n\n\t\twebsiteRouter.GET(\"/:id/https\", baseApi.GetHTTPSConfig)\n\t\twebsiteRouter.POST(\"/:id/https\", baseApi.UpdateHTTPSConfig)\n\n\t\twebsiteRouter.POST(\"/rewrite\", baseApi.GetRewriteConfig)\n\t\twebsiteRouter.POST(\"/rewrite/update\", baseApi.UpdateRewriteConfig)\n\t\twebsiteRouter.POST(\"/rewrite/custom\", baseApi.OperateCustomRewrite)\n\t\twebsiteRouter.GET(\"/rewrite/custom\", baseApi.ListCustomRewrite)\n\n\t\twebsiteRouter.POST(\"/dir/update\", baseApi.UpdateSiteDir)\n\t\twebsiteRouter.POST(\"/dir/permission\", baseApi.UpdateSiteDirPermission)\n\t\twebsiteRouter.POST(\"/dir\", baseApi.GetDirConfig)\n\n\t\twebsiteRouter.POST(\"/proxies\", baseApi.GetProxyConfig)\n\t\twebsiteRouter.POST(\"/proxies/update\", baseApi.UpdateProxyConfig)\n\t\twebsiteRouter.POST(\"/proxies/delete\", baseApi.DeleteProxyConfig)\n\t\twebsiteRouter.POST(\"/proxies/status\", baseApi.UpdateProxyConfigStatus)\n\t\twebsiteRouter.POST(\"/proxies/file\", baseApi.UpdateProxyConfigFile)\n\t\twebsiteRouter.POST(\"/proxy/config\", baseApi.UpdateProxyCache)\n\t\twebsiteRouter.GET(\"/proxy/config/:id\", baseApi.GetProxyCache)\n\t\twebsiteRouter.POST(\"/proxy/clear\", baseApi.ClearProxyCache)\n\n\t\twebsiteRouter.POST(\"/auths\", baseApi.GetAuthConfig)\n\t\twebsiteRouter.POST(\"/auths/update\", baseApi.UpdateAuthConfig)\n\t\twebsiteRouter.POST(\"/auths/path\", baseApi.GetPathAuthConfig)\n\t\twebsiteRouter.POST(\"/auths/path/update\", baseApi.UpdatePathAuthConfig)\n\n\t\twebsiteRouter.GET(\"/cors/:id\", baseApi.GetCORSConfig)\n\t\twebsiteRouter.POST(\"/cors/update\", baseApi.UpdateCORSConfig)\n\n\t\twebsiteRouter.POST(\"/leech\", baseApi.GetAntiLeech)\n\t\twebsiteRouter.POST(\"/leech/update\", baseApi.UpdateAntiLeech)\n\n\t\twebsiteRouter.POST(\"/redirect/update\", baseApi.UpdateRedirectConfig)\n\t\twebsiteRouter.POST(\"/redirect\", baseApi.GetRedirectConfig)\n\t\twebsiteRouter.POST(\"/redirect/file\", baseApi.UpdateRedirectConfigFile)\n\n\t\twebsiteRouter.GET(\"/default/html/:type\", baseApi.GetDefaultHtml)\n\t\twebsiteRouter.POST(\"/default/html/update\", baseApi.UpdateDefaultHtml)\n\n\t\twebsiteRouter.GET(\"/:id/lbs\", baseApi.GetLoadBalances)\n\t\twebsiteRouter.POST(\"/lbs/create\", baseApi.CreateLoadBalance)\n\t\twebsiteRouter.POST(\"/lbs/del\", baseApi.DeleteLoadBalance)\n\t\twebsiteRouter.POST(\"/lbs/update\", baseApi.UpdateLoadBalance)\n\t\twebsiteRouter.POST(\"/lbs/file\", baseApi.UpdateLoadBalanceFile)\n\n\t\twebsiteRouter.POST(\"/php/version\", baseApi.ChangePHPVersion)\n\n\t\twebsiteRouter.POST(\"/realip/config\", baseApi.SetRealIPConfig)\n\t\twebsiteRouter.GET(\"/realip/config/:id\", baseApi.GetRealIPConfig)\n\n\t\twebsiteRouter.GET(\"/resource/:id\", baseApi.GetWebsiteResource)\n\t\twebsiteRouter.GET(\"/databases\", baseApi.GetWebsiteDatabase)\n\t\twebsiteRouter.POST(\"/databases\", baseApi.ChangeWebsiteDatabase)\n\n\t\twebsiteRouter.POST(\"/crosssite\", baseApi.OperateCrossSiteAccess)\n\n\t\twebsiteRouter.POST(\"/exec/composer\", baseApi.ExecComposer)\n\t\twebsiteRouter.POST(\"/stream/update\", baseApi.UpdateStreamConfig)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_website_acme_account.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype WebsiteAcmeAccountRouter struct {\n}\n\nfunc (a *WebsiteAcmeAccountRouter) InitRouter(Router *gin.RouterGroup) {\n\tgroupRouter := Router.Group(\"websites/acme\")\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tgroupRouter.POST(\"/search\", baseApi.PageWebsiteAcmeAccount)\n\t\tgroupRouter.POST(\"\", baseApi.CreateWebsiteAcmeAccount)\n\t\tgroupRouter.POST(\"/del\", baseApi.DeleteWebsiteAcmeAccount)\n\t\tgroupRouter.POST(\"/update\", baseApi.UpdateWebsiteAcmeAccount)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_website_ca.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype WebsiteCARouter struct {\n}\n\nfunc (a *WebsiteCARouter) InitRouter(Router *gin.RouterGroup) {\n\tgroupRouter := Router.Group(\"websites/ca\")\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tgroupRouter.POST(\"/search\", baseApi.PageWebsiteCA)\n\t\tgroupRouter.POST(\"\", baseApi.CreateWebsiteCA)\n\t\tgroupRouter.POST(\"/del\", baseApi.DeleteWebsiteCA)\n\t\tgroupRouter.POST(\"/obtain\", baseApi.ObtainWebsiteCA)\n\t\tgroupRouter.POST(\"/renew\", baseApi.RenewWebsiteCA)\n\t\tgroupRouter.GET(\"/:id\", baseApi.GetWebsiteCA)\n\t\tgroupRouter.POST(\"/download\", baseApi.DownloadCAFile)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_website_dns_account.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype WebsiteDnsAccountRouter struct {\n}\n\nfunc (a *WebsiteDnsAccountRouter) InitRouter(Router *gin.RouterGroup) {\n\tgroupRouter := Router.Group(\"websites/dns\")\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tgroupRouter.POST(\"/search\", baseApi.PageWebsiteDnsAccount)\n\t\tgroupRouter.POST(\"\", baseApi.CreateWebsiteDnsAccount)\n\t\tgroupRouter.POST(\"/update\", baseApi.UpdateWebsiteDnsAccount)\n\t\tgroupRouter.POST(\"/del\", baseApi.DeleteWebsiteDnsAccount)\n\t}\n}\n"
  },
  {
    "path": "agent/router/ro_website_ssl.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/agent/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype WebsiteSSLRouter struct {\n}\n\nfunc (a *WebsiteSSLRouter) InitRouter(Router *gin.RouterGroup) {\n\tgroupRouter := Router.Group(\"websites/ssl\")\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tgroupRouter.POST(\"/search\", baseApi.PageWebsiteSSL)\n\t\tgroupRouter.POST(\"/list\", baseApi.ListWebsiteSSL)\n\t\tgroupRouter.POST(\"\", baseApi.CreateWebsiteSSL)\n\t\tgroupRouter.POST(\"/resolve\", baseApi.GetDNSResolve)\n\t\tgroupRouter.POST(\"/del\", baseApi.DeleteWebsiteSSL)\n\t\tgroupRouter.GET(\"/website/:websiteId\", baseApi.GetWebsiteSSLByWebsiteId)\n\t\tgroupRouter.GET(\"/:id\", baseApi.GetWebsiteSSLById)\n\t\tgroupRouter.POST(\"/update\", baseApi.UpdateWebsiteSSL)\n\t\tgroupRouter.POST(\"/upload\", baseApi.UploadWebsiteSSL)\n\t\tgroupRouter.POST(\"/obtain\", baseApi.ApplyWebsiteSSL)\n\t\tgroupRouter.POST(\"/download\", baseApi.DownloadWebsiteSSL)\n\t\tgroupRouter.POST(\"/import\", baseApi.ImportMasterSSL)\n\t\tgroupRouter.POST(\"/upload/file\", baseApi.UploadSSLFile)\n\t}\n}\n"
  },
  {
    "path": "agent/server/init.go",
    "content": "//go:build !xpack\n\npackage server\n\nfunc InitOthers() {\n}\n"
  },
  {
    "path": "agent/server/server.go",
    "content": "package server\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/gin-gonic/gin\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/cron\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/app\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/business\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/cache\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/db\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/dir\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/firewall\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/hook\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/lang\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/log\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/migration\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/router\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/validator\"\n\t\"github.com/1Panel-dev/1Panel/agent/init/viper\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n)\n\nfunc Start() {\n\tre.Init()\n\tviper.Init()\n\tdir.Init()\n\tlog.Init()\n\tdb.Init()\n\tmigration.Init()\n\ti18n.Init()\n\tcache.Init()\n\tapp.Init()\n\tlang.Init()\n\tvalidator.Init()\n\tcron.Run()\n\thook.Init()\n\tgo firewall.Init()\n\tInitOthers()\n\n\trootRouter := router.Routers()\n\n\tserver := &http.Server{\n\t\tHandler: rootRouter,\n\t}\n\n\tif global.CONF.Base.Mode != \"stable\" {\n\t\tgin.SetMode(gin.DebugMode)\n\t} else {\n\t\tgin.SetMode(gin.ReleaseMode)\n\t}\n\n\tif global.IsMaster {\n\t\t_ = os.Remove(\"/etc/1panel/agent.sock\")\n\t\t_ = os.Mkdir(\"/etc/1panel\", constant.DirPerm)\n\t\tlistener, err := net.Listen(\"unix\", \"/etc/1panel/agent.sock\")\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tbusiness.Init()\n\t\t_ = server.Serve(listener)\n\t\treturn\n\t} else {\n\t\tserver.Addr = fmt.Sprintf(\"0.0.0.0:%s\", global.CONF.Base.Port)\n\t\tsettingRepo := repo.NewISettingRepo()\n\t\tcertItem, err := settingRepo.Get(settingRepo.WithByKey(\"ServerCrt\"))\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tcert, _ := encrypt.StringDecrypt(certItem.Value)\n\t\tkeyItem, err := settingRepo.Get(settingRepo.WithByKey(\"ServerKey\"))\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tkey, _ := encrypt.StringDecrypt(keyItem.Value)\n\t\ttlsCert, err := tls.X509KeyPair([]byte(cert), []byte(key))\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"failed to load X.509 key pair: %s\\n\", err)\n\t\t\treturn\n\t\t}\n\n\t\tserver.TLSConfig = &tls.Config{\n\t\t\tCertificates: []tls.Certificate{tlsCert},\n\t\t\tClientAuth:   tls.RequireAndVerifyClientCert,\n\t\t}\n\t\tcaItem, _ := settingRepo.GetValueByKey(\"RootCrt\")\n\t\tif len(caItem) != 0 {\n\t\t\tcaCertPool := x509.NewCertPool()\n\t\t\trootCrt, _ := encrypt.StringDecrypt(caItem)\n\t\t\tcaCertPool.AppendCertsFromPEM([]byte(rootCrt))\n\t\t\tserver.TLSConfig.ClientCAs = caCertPool\n\t\t}\n\t\tbusiness.Init()\n\t\tglobal.LOG.Infof(\"listen at https://0.0.0.0:%s\", global.CONF.Base.Port)\n\t\tif err := server.ListenAndServeTLS(\"\", \"\"); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "agent/utils/ai_tools/gpu/common/gpu_info.go",
    "content": "package common\n\ntype GpuInfo struct {\n\tCudaVersion   string `json:\"cudaVersion\"`\n\tDriverVersion string `json:\"driverVersion\"`\n\tType          string `json:\"type\"`\n\n\tGPUs []GPU `json:\"gpu\"`\n}\n\ntype GPU struct {\n\tIndex           uint   `json:\"index\"`\n\tProductName     string `json:\"productName\"`\n\tPersistenceMode string `json:\"persistenceMode\"`\n\tBusID           string `json:\"busID\"`\n\tDisplayActive   string `json:\"displayActive\"`\n\tECC             string `json:\"ecc\"`\n\tFanSpeed        string `json:\"fanSpeed\"`\n\n\tTemperature      string    `json:\"temperature\"`\n\tPerformanceState string    `json:\"performanceState\"`\n\tPowerDraw        string    `json:\"powerDraw\"`\n\tMaxPowerLimit    string    `json:\"maxPowerLimit\"`\n\tMemUsed          string    `json:\"memUsed\"`\n\tMemTotal         string    `json:\"memTotal\"`\n\tGPUUtil          string    `json:\"gpuUtil\"`\n\tComputeMode      string    `json:\"computeMode\"`\n\tMigMode          string    `json:\"migMode\"`\n\tProcesses        []Process `json:\"processes\"`\n}\n\ntype Process struct {\n\tPid         string `json:\"pid\"`\n\tType        string `json:\"type\"`\n\tProcessName string `json:\"processName\"`\n\tUsedMemory  string `json:\"usedMemory\"`\n}\n"
  },
  {
    "path": "agent/utils/ai_tools/gpu/gpu.go",
    "content": "package gpu\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"encoding/xml\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/schema\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype NvidiaSMI struct{}\n\nfunc New() (bool, NvidiaSMI) {\n\treturn cmd.Which(\"nvidia-smi\"), NvidiaSMI{}\n}\n\nfunc (n NvidiaSMI) LoadGpuInfo() (*common.GpuInfo, error) {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))\n\titemData, err := cmdMgr.RunWithStdoutBashC(\"nvidia-smi -q -x\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"calling nvidia-smi failed, %v\", err)\n\t}\n\tdata := []byte(itemData)\n\tversion := \"v11\"\n\n\tbuf := bytes.NewBuffer(data)\n\tdecoder := xml.NewDecoder(buf)\n\tfor {\n\t\ttoken, err := decoder.Token()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"reading token failed: %w\", err)\n\t\t}\n\t\td, ok := token.(xml.Directive)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tdirective := string(d)\n\t\tif !strings.HasPrefix(directive, \"DOCTYPE\") {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Split(directive, \" \")\n\t\ts := strings.Trim(parts[len(parts)-1], \"\\\" \")\n\t\tif strings.HasPrefix(s, \"nvsmi_device_\") && strings.HasSuffix(s, \".dtd\") {\n\t\t\tversion = strings.TrimSuffix(strings.TrimPrefix(s, \"nvsmi_device_\"), \".dtd\")\n\t\t} else {\n\t\t\tglobal.LOG.Debugf(\"Cannot find schema version in %q\", directive)\n\t\t}\n\t\tbreak\n\t}\n\n\treturn schema.Parse(data, version)\n}\n"
  },
  {
    "path": "agent/utils/ai_tools/gpu/schema/parser.go",
    "content": "package schema\n\nimport (\n\t\"encoding/xml\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common\"\n)\n\nfunc Parse(buf []byte, version string) (*common.GpuInfo, error) {\n\tvar (\n\t\ts    smi\n\t\tinfo common.GpuInfo\n\t)\n\tif err := xml.Unmarshal(buf, &s); err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo.Type = \"nvidia\"\n\tinfo.CudaVersion = s.CudaVersion\n\tinfo.DriverVersion = s.DriverVersion\n\tif len(s.Gpu) == 0 {\n\t\treturn &info, nil\n\t}\n\tfor i := 0; i < len(s.Gpu); i++ {\n\t\tvar gpuItem common.GPU\n\t\tgpuItem.Index = uint(i)\n\t\tgpuItem.ProductName = s.Gpu[i].ProductName\n\t\tgpuItem.PersistenceMode = s.Gpu[i].PersistenceMode\n\t\tgpuItem.BusID = s.Gpu[i].ID\n\t\tgpuItem.DisplayActive = s.Gpu[i].DisplayActive\n\t\tgpuItem.ECC = s.Gpu[i].EccErrors.Volatile.DramUncorrectable\n\t\tgpuItem.FanSpeed = s.Gpu[i].FanSpeed\n\n\t\tgpuItem.Temperature = s.Gpu[i].Temperature.GpuTemp\n\t\tgpuItem.PerformanceState = s.Gpu[i].PerformanceState\n\t\tif version == \"v12\" || version == \"v13\" {\n\t\t\tgpuItem.PowerDraw = s.Gpu[i].GpuPowerReadings.PowerDraw\n\t\t\tif len(gpuItem.PowerDraw) == 0 {\n\t\t\t\tgpuItem.PowerDraw = s.Gpu[i].GpuPowerReadings.InstantPowerDraw\n\t\t\t}\n\t\t\tgpuItem.MaxPowerLimit = s.Gpu[i].GpuPowerReadings.CurrentPowerLimit\n\t\t} else {\n\t\t\tgpuItem.PowerDraw = s.Gpu[i].PowerReadings.PowerDraw\n\t\t\tgpuItem.MaxPowerLimit = s.Gpu[i].PowerReadings.MaxPowerLimit\n\t\t}\n\t\tgpuItem.MemUsed = s.Gpu[i].FbMemoryUsage.Used\n\t\tgpuItem.MemTotal = s.Gpu[i].FbMemoryUsage.Total\n\t\tgpuItem.GPUUtil = s.Gpu[i].Utilization.GpuUtil\n\t\tgpuItem.ComputeMode = s.Gpu[i].ComputeMode\n\t\tgpuItem.MigMode = s.Gpu[i].MigMode.CurrentMig\n\n\t\tfor _, process := range s.Gpu[i].Processes.ProcessInfo {\n\t\t\tgpuItem.Processes = append(gpuItem.Processes, common.Process{\n\t\t\t\tPid:         process.Pid,\n\t\t\t\tType:        process.Type,\n\t\t\t\tProcessName: process.ProcessName,\n\t\t\t\tUsedMemory:  process.UsedMemory,\n\t\t\t})\n\t\t}\n\t\tinfo.GPUs = append(info.GPUs, gpuItem)\n\t}\n\treturn &info, nil\n}\n"
  },
  {
    "path": "agent/utils/ai_tools/gpu/schema/types.go",
    "content": "package schema\n\ntype smi struct {\n\tAttachedGpus  string `xml:\"attached_gpus\"`\n\tCudaVersion   string `xml:\"cuda_version\"`\n\tDriverVersion string `xml:\"driver_version\"`\n\tGpu           []struct {\n\t\tID                       string   `xml:\"id,attr\"`\n\t\tAccountedProcesses       struct{} `xml:\"accounted_processes\"`\n\t\tAccountingMode           string   `xml:\"accounting_mode\"`\n\t\tAccountingModeBufferSize string   `xml:\"accounting_mode_buffer_size\"`\n\t\tAddressingMode           string   `xml:\"addressing_mode\"`\n\t\tApplicationsClocks       struct {\n\t\t\tGraphicsClock string `xml:\"graphics_clock\"`\n\t\t\tMemClock      string `xml:\"mem_clock\"`\n\t\t} `xml:\"applications_clocks\"`\n\t\tBar1MemoryUsage struct {\n\t\t\tFree  string `xml:\"free\"`\n\t\t\tTotal string `xml:\"total\"`\n\t\t\tUsed  string `xml:\"used\"`\n\t\t} `xml:\"bar1_memory_usage\"`\n\t\tBoardID                string `xml:\"board_id\"`\n\t\tBoardPartNumber        string `xml:\"board_part_number\"`\n\t\tCcProtectedMemoryUsage struct {\n\t\t\tFree  string `xml:\"free\"`\n\t\t\tTotal string `xml:\"total\"`\n\t\t\tUsed  string `xml:\"used\"`\n\t\t} `xml:\"cc_protected_memory_usage\"`\n\t\tClockPolicy struct {\n\t\t\tAutoBoost        string `xml:\"auto_boost\"`\n\t\t\tAutoBoostDefault string `xml:\"auto_boost_default\"`\n\t\t} `xml:\"clock_policy\"`\n\t\tClocks struct {\n\t\t\tGraphicsClock string `xml:\"graphics_clock\"`\n\t\t\tMemClock      string `xml:\"mem_clock\"`\n\t\t\tSmClock       string `xml:\"sm_clock\"`\n\t\t\tVideoClock    string `xml:\"video_clock\"`\n\t\t} `xml:\"clocks\"`\n\t\tClocksEventReasons struct {\n\t\t\tClocksEventReasonApplicationsClocksSetting string `xml:\"clocks_event_reason_applications_clocks_setting\"`\n\t\t\tClocksEventReasonDisplayClocksSetting      string `xml:\"clocks_event_reason_display_clocks_setting\"`\n\t\t\tClocksEventReasonGpuIdle                   string `xml:\"clocks_event_reason_gpu_idle\"`\n\t\t\tClocksEventReasonHwPowerBrakeSlowdown      string `xml:\"clocks_event_reason_hw_power_brake_slowdown\"`\n\t\t\tClocksEventReasonHwSlowdown                string `xml:\"clocks_event_reason_hw_slowdown\"`\n\t\t\tClocksEventReasonHwThermalSlowdown         string `xml:\"clocks_event_reason_hw_thermal_slowdown\"`\n\t\t\tClocksEventReasonSwPowerCap                string `xml:\"clocks_event_reason_sw_power_cap\"`\n\t\t\tClocksEventReasonSwThermalSlowdown         string `xml:\"clocks_event_reason_sw_thermal_slowdown\"`\n\t\t\tClocksEventReasonSyncBoost                 string `xml:\"clocks_event_reason_sync_boost\"`\n\t\t} `xml:\"clocks_event_reasons\"`\n\t\tComputeMode               string `xml:\"compute_mode\"`\n\t\tDefaultApplicationsClocks struct {\n\t\t\tGraphicsClock string `xml:\"graphics_clock\"`\n\t\t\tMemClock      string `xml:\"mem_clock\"`\n\t\t} `xml:\"default_applications_clocks\"`\n\t\tDeferredClocks struct {\n\t\t\tMemClock string `xml:\"mem_clock\"`\n\t\t} `xml:\"deferred_clocks\"`\n\t\tDisplayActive string `xml:\"display_active\"`\n\t\tDisplayMode   string `xml:\"display_mode\"`\n\t\tDriverModel   struct {\n\t\t\tCurrentDm string `xml:\"current_dm\"`\n\t\t\tPendingDm string `xml:\"pending_dm\"`\n\t\t} `xml:\"driver_model\"`\n\t\tEccErrors struct {\n\t\t\tAggregate struct {\n\t\t\t\tDramCorrectable   string `xml:\"dram_correctable\"`\n\t\t\t\tDramUncorrectable string `xml:\"dram_uncorrectable\"`\n\t\t\t\tSramCorrectable   string `xml:\"sram_correctable\"`\n\t\t\t\tSramUncorrectable string `xml:\"sram_uncorrectable\"`\n\t\t\t} `xml:\"aggregate\"`\n\t\t\tVolatile struct {\n\t\t\t\tDramCorrectable   string `xml:\"dram_correctable\"`\n\t\t\t\tDramUncorrectable string `xml:\"dram_uncorrectable\"`\n\t\t\t\tSramCorrectable   string `xml:\"sram_correctable\"`\n\t\t\t\tSramUncorrectable string `xml:\"sram_uncorrectable\"`\n\t\t\t} `xml:\"volatile\"`\n\t\t} `xml:\"ecc_errors\"`\n\t\tEccMode struct {\n\t\t\tCurrentEcc string `xml:\"current_ecc\"`\n\t\t\tPendingEcc string `xml:\"pending_ecc\"`\n\t\t} `xml:\"ecc_mode\"`\n\t\tEncoderStats struct {\n\t\t\tAverageFps     string `xml:\"average_fps\"`\n\t\t\tAverageLatency string `xml:\"average_latency\"`\n\t\t\tSessionCount   string `xml:\"session_count\"`\n\t\t} `xml:\"encoder_stats\"`\n\t\tFabric struct {\n\t\t\tState  string `xml:\"state\"`\n\t\t\tStatus string `xml:\"status\"`\n\t\t} `xml:\"fabric\"`\n\t\tFanSpeed      string `xml:\"fan_speed\"`\n\t\tFbMemoryUsage struct {\n\t\t\tFree     string `xml:\"free\"`\n\t\t\tReserved string `xml:\"reserved\"`\n\t\t\tTotal    string `xml:\"total\"`\n\t\t\tUsed     string `xml:\"used\"`\n\t\t} `xml:\"fb_memory_usage\"`\n\t\tFbcStats struct {\n\t\t\tAverageFps     string `xml:\"average_fps\"`\n\t\t\tAverageLatency string `xml:\"average_latency\"`\n\t\t\tSessionCount   string `xml:\"session_count\"`\n\t\t} `xml:\"fbc_stats\"`\n\t\tGpuFruPartNumber string `xml:\"gpu_fru_part_number\"`\n\t\tGpuModuleID      string `xml:\"gpu_module_id\"`\n\t\tGpuOperationMode struct {\n\t\t\tCurrentGom string `xml:\"current_gom\"`\n\t\t\tPendingGom string `xml:\"pending_gom\"`\n\t\t} `xml:\"gpu_operation_mode\"`\n\t\tGpuPartNumber    string `xml:\"gpu_part_number\"`\n\t\tGpuPowerReadings struct {\n\t\t\tInstantPowerDraw    string `xml:\"instant_power_draw\"`\n\t\t\tCurrentPowerLimit   string `xml:\"current_power_limit\"`\n\t\t\tDefaultPowerLimit   string `xml:\"default_power_limit\"`\n\t\t\tMaxPowerLimit       string `xml:\"max_power_limit\"`\n\t\t\tMinPowerLimit       string `xml:\"min_power_limit\"`\n\t\t\tPowerDraw           string `xml:\"power_draw\"`\n\t\t\tPowerState          string `xml:\"power_state\"`\n\t\t\tRequestedPowerLimit string `xml:\"requested_power_limit\"`\n\t\t} `xml:\"gpu_power_readings\"`\n\t\tGpuResetStatus struct {\n\t\t\tDrainAndResetRecommended string `xml:\"drain_and_reset_recommended\"`\n\t\t\tResetRequired            string `xml:\"reset_required\"`\n\t\t} `xml:\"gpu_reset_status\"`\n\t\tGpuVirtualizationMode struct {\n\t\t\tHostVgpuMode       string `xml:\"host_vgpu_mode\"`\n\t\t\tVirtualizationMode string `xml:\"virtualization_mode\"`\n\t\t} `xml:\"gpu_virtualization_mode\"`\n\t\tGspFirmwareVersion string `xml:\"gsp_firmware_version\"`\n\t\tIbmnpu             struct {\n\t\t\tRelaxedOrderingMode string `xml:\"relaxed_ordering_mode\"`\n\t\t} `xml:\"ibmnpu\"`\n\t\tInforomVersion struct {\n\t\t\tEccObject  string `xml:\"ecc_object\"`\n\t\t\tImgVersion string `xml:\"img_version\"`\n\t\t\tOemObject  string `xml:\"oem_object\"`\n\t\t\tPwrObject  string `xml:\"pwr_object\"`\n\t\t} `xml:\"inforom_version\"`\n\t\tMaxClocks struct {\n\t\t\tGraphicsClock string `xml:\"graphics_clock\"`\n\t\t\tMemClock      string `xml:\"mem_clock\"`\n\t\t\tSmClock       string `xml:\"sm_clock\"`\n\t\t\tVideoClock    string `xml:\"video_clock\"`\n\t\t} `xml:\"max_clocks\"`\n\t\tMaxCustomerBoostClocks struct {\n\t\t\tGraphicsClock string `xml:\"graphics_clock\"`\n\t\t} `xml:\"max_customer_boost_clocks\"`\n\t\tMigDevices struct {\n\t\t\tMigDevice []struct {\n\t\t\t\tIndex             string `xml:\"index\"`\n\t\t\t\tGpuInstanceID     string `xml:\"gpu_instance_id\"`\n\t\t\t\tComputeInstanceID string `xml:\"compute_instance_id\"`\n\t\t\t\tEccErrorCount     struct {\n\t\t\t\t\tText          string `xml:\",chardata\" json:\"text\"`\n\t\t\t\t\tVolatileCount struct {\n\t\t\t\t\t\tSramUncorrectable string `xml:\"sram_uncorrectable\"`\n\t\t\t\t\t} `xml:\"volatile_count\" json:\"volatile_count\"`\n\t\t\t\t} `xml:\"ecc_error_count\" json:\"ecc_error_count\"`\n\t\t\t\tFbMemoryUsage struct {\n\t\t\t\t\tTotal    string `xml:\"total\"`\n\t\t\t\t\tReserved string `xml:\"reserved\"`\n\t\t\t\t\tUsed     string `xml:\"used\"`\n\t\t\t\t\tFree     string `xml:\"free\"`\n\t\t\t\t} `xml:\"fb_memory_usage\" json:\"fb_memory_usage\"`\n\t\t\t\tBar1MemoryUsage struct {\n\t\t\t\t\tTotal string `xml:\"total\"`\n\t\t\t\t\tUsed  string `xml:\"used\"`\n\t\t\t\t\tFree  string `xml:\"free\"`\n\t\t\t\t} `xml:\"bar1_memory_usage\" json:\"bar1_memory_usage\"`\n\t\t\t} `xml:\"mig_device\" json:\"mig_device\"`\n\t\t} `xml:\"mig_devices\" json:\"mig_devices\"`\n\t\tMigMode struct {\n\t\t\tCurrentMig string `xml:\"current_mig\"`\n\t\t\tPendingMig string `xml:\"pending_mig\"`\n\t\t} `xml:\"mig_mode\"`\n\t\tMinorNumber         string `xml:\"minor_number\"`\n\t\tModulePowerReadings struct {\n\t\t\tCurrentPowerLimit   string `xml:\"current_power_limit\"`\n\t\t\tDefaultPowerLimit   string `xml:\"default_power_limit\"`\n\t\t\tMaxPowerLimit       string `xml:\"max_power_limit\"`\n\t\t\tMinPowerLimit       string `xml:\"min_power_limit\"`\n\t\t\tPowerDraw           string `xml:\"power_draw\"`\n\t\t\tPowerState          string `xml:\"power_state\"`\n\t\t\tRequestedPowerLimit string `xml:\"requested_power_limit\"`\n\t\t} `xml:\"module_power_readings\"`\n\t\tMultigpuBoard string `xml:\"multigpu_board\"`\n\t\tPci           struct {\n\t\t\tAtomicCapsInbound  string `xml:\"atomic_caps_inbound\"`\n\t\t\tAtomicCapsOutbound string `xml:\"atomic_caps_outbound\"`\n\t\t\tPciBridgeChip      struct {\n\t\t\t\tBridgeChipFw   string `xml:\"bridge_chip_fw\"`\n\t\t\t\tBridgeChipType string `xml:\"bridge_chip_type\"`\n\t\t\t} `xml:\"pci_bridge_chip\"`\n\t\t\tPciBus         string `xml:\"pci_bus\"`\n\t\t\tPciBusID       string `xml:\"pci_bus_id\"`\n\t\t\tPciDevice      string `xml:\"pci_device\"`\n\t\t\tPciDeviceID    string `xml:\"pci_device_id\"`\n\t\t\tPciDomain      string `xml:\"pci_domain\"`\n\t\t\tPciGpuLinkInfo struct {\n\t\t\t\tLinkWidths struct {\n\t\t\t\t\tCurrentLinkWidth string `xml:\"current_link_width\"`\n\t\t\t\t\tMaxLinkWidth     string `xml:\"max_link_width\"`\n\t\t\t\t} `xml:\"link_widths\"`\n\t\t\t\tPcieGen struct {\n\t\t\t\t\tCurrentLinkGen       string `xml:\"current_link_gen\"`\n\t\t\t\t\tDeviceCurrentLinkGen string `xml:\"device_current_link_gen\"`\n\t\t\t\t\tMaxDeviceLinkGen     string `xml:\"max_device_link_gen\"`\n\t\t\t\t\tMaxHostLinkGen       string `xml:\"max_host_link_gen\"`\n\t\t\t\t\tMaxLinkGen           string `xml:\"max_link_gen\"`\n\t\t\t\t} `xml:\"pcie_gen\"`\n\t\t\t} `xml:\"pci_gpu_link_info\"`\n\t\t\tPciSubSystemID        string `xml:\"pci_sub_system_id\"`\n\t\t\tReplayCounter         string `xml:\"replay_counter\"`\n\t\t\tReplayRolloverCounter string `xml:\"replay_rollover_counter\"`\n\t\t\tRxUtil                string `xml:\"rx_util\"`\n\t\t\tTxUtil                string `xml:\"tx_util\"`\n\t\t} `xml:\"pci\"`\n\t\tPerformanceState string `xml:\"performance_state\"`\n\t\tPersistenceMode  string `xml:\"persistence_mode\"`\n\t\tPowerReadings    struct {\n\t\t\tPowerState         string `xml:\"power_state\"`\n\t\t\tPowerManagement    string `xml:\"power_management\"`\n\t\t\tPowerDraw          string `xml:\"power_draw\"`\n\t\t\tPowerLimit         string `xml:\"power_limit\"`\n\t\t\tDefaultPowerLimit  string `xml:\"default_power_limit\"`\n\t\t\tEnforcedPowerLimit string `xml:\"enforced_power_limit\"`\n\t\t\tMinPowerLimit      string `xml:\"min_power_limit\"`\n\t\t\tMaxPowerLimit      string `xml:\"max_power_limit\"`\n\t\t} `xml:\"power_readings\"`\n\t\tProcesses struct {\n\t\t\tProcessInfo []struct {\n\t\t\t\tPid         string `xml:\"pid\"`\n\t\t\t\tType        string `xml:\"type\"`\n\t\t\t\tProcessName string `xml:\"process_name\"`\n\t\t\t\tUsedMemory  string `xml:\"used_memory\"`\n\t\t\t} `xml:\"process_info\"`\n\t\t} `xml:\"processes\"`\n\t\tProductArchitecture string `xml:\"product_architecture\"`\n\t\tProductBrand        string `xml:\"product_brand\"`\n\t\tProductName         string `xml:\"product_name\"`\n\t\tRemappedRows        struct {\n\t\t\t// Manually added\n\t\t\tCorrectable   string `xml:\"remapped_row_corr\"`\n\t\t\tUncorrectable string `xml:\"remapped_row_unc\"`\n\t\t\tPending       string `xml:\"remapped_row_pending\"`\n\t\t\tFailure       string `xml:\"remapped_row_failure\"`\n\t\t} `xml:\"remapped_rows\"`\n\t\tRetiredPages struct {\n\t\t\tDoubleBitRetirement struct {\n\t\t\t\tRetiredCount    string `xml:\"retired_count\"`\n\t\t\t\tRetiredPagelist string `xml:\"retired_pagelist\"`\n\t\t\t} `xml:\"double_bit_retirement\"`\n\t\t\tMultipleSingleBitRetirement struct {\n\t\t\t\tRetiredCount    string `xml:\"retired_count\"`\n\t\t\t\tRetiredPagelist string `xml:\"retired_pagelist\"`\n\t\t\t} `xml:\"multiple_single_bit_retirement\"`\n\t\t\tPendingBlacklist  string `xml:\"pending_blacklist\"`\n\t\t\tPendingRetirement string `xml:\"pending_retirement\"`\n\t\t} `xml:\"retired_pages\"`\n\t\tSerial          string `xml:\"serial\"`\n\t\tSupportedClocks struct {\n\t\t\tSupportedMemClock []struct {\n\t\t\t\tSupportedGraphicsClock []string `xml:\"supported_graphics_clock\"`\n\t\t\t\tValue                  string   `xml:\"value\"`\n\t\t\t} `xml:\"supported_mem_clock\"`\n\t\t} `xml:\"supported_clocks\"`\n\t\tSupportedGpuTargetTemp struct {\n\t\t\tGpuTargetTempMax string `xml:\"gpu_target_temp_max\"`\n\t\t\tGpuTargetTempMin string `xml:\"gpu_target_temp_min\"`\n\t\t} `xml:\"supported_gpu_target_temp\"`\n\t\tTemperature struct {\n\t\t\tGpuTargetTemperature   string `xml:\"gpu_target_temperature\"`\n\t\t\tGpuTemp                string `xml:\"gpu_temp\"`\n\t\t\tGpuTempMaxGpuThreshold string `xml:\"gpu_temp_max_gpu_threshold\"`\n\t\t\tGpuTempMaxMemThreshold string `xml:\"gpu_temp_max_mem_threshold\"`\n\t\t\tGpuTempMaxThreshold    string `xml:\"gpu_temp_max_threshold\"`\n\t\t\tGpuTempSlowThreshold   string `xml:\"gpu_temp_slow_threshold\"`\n\t\t\tGpuTempTlimit          string `xml:\"gpu_temp_tlimit\"`\n\t\t\tMemoryTemp             string `xml:\"memory_temp\"`\n\t\t} `xml:\"temperature\"`\n\t\tUtilization struct {\n\t\t\tDecoderUtil string `xml:\"decoder_util\"`\n\t\t\tEncoderUtil string `xml:\"encoder_util\"`\n\t\t\tGpuUtil     string `xml:\"gpu_util\"`\n\t\t\tJpegUtil    string `xml:\"jpeg_util\"`\n\t\t\tMemoryUtil  string `xml:\"memory_util\"`\n\t\t\tOfaUtil     string `xml:\"ofa_util\"`\n\t\t} `xml:\"utilization\"`\n\t\tUUID         string `xml:\"uuid\"`\n\t\tVbiosVersion string `xml:\"vbios_version\"`\n\t\tVoltage      struct {\n\t\t\tGraphicsVolt string `xml:\"graphics_volt\"`\n\t\t} `xml:\"voltage\"`\n\t} `xml:\"gpu\"`\n\tTimestamp string `xml:\"timestamp\"`\n}\n"
  },
  {
    "path": "agent/utils/ai_tools/xpu/types.go",
    "content": "package xpu\n\ntype DeviceUtilByProc struct {\n\tDeviceID      int     `json:\"device_id\"`\n\tMemSize       float64 `json:\"mem_size\"`\n\tProcessID     int     `json:\"process_id\"`\n\tProcessName   string  `json:\"process_name\"`\n\tSharedMemSize float64 `json:\"shared_mem_size\"`\n}\n\ntype DeviceUtilByProcList struct {\n\tDeviceUtilByProcList []DeviceUtilByProc `json:\"device_util_by_proc_list\"`\n}\n\ntype Device struct {\n\tDeviceFunctionType string `json:\"device_function_type\"`\n\tDeviceID           int    `json:\"device_id\"`\n\tDeviceName         string `json:\"device_name\"`\n\tDeviceType         string `json:\"device_type\"`\n\tDrmDevice          string `json:\"drm_device\"`\n\tPciBdfAddress      string `json:\"pci_bdf_address\"`\n\tPciDeviceID        string `json:\"pci_device_id\"`\n\tUUID               string `json:\"uuid\"`\n\tVendorName         string `json:\"vendor_name\"`\n\n\tMemoryPhysicalSizeByte string `json:\"memory_physical_size_byte\"`\n\tMemoryFreeSizeByte     string `json:\"memory_free_size_byte\"`\n\tDriverVersion          string `json:\"driver_version\"`\n}\n\ntype DeviceInfo struct {\n\tDeviceList []Device `json:\"device_list\"`\n}\n\ntype DeviceLevelMetric struct {\n\tMetricsType string  `json:\"metrics_type\"`\n\tValue       float64 `json:\"value\"`\n}\n\ntype DeviceStats struct {\n\tDeviceID    int                 `json:\"device_id\"`\n\tDeviceLevel []DeviceLevelMetric `json:\"device_level\"`\n}\n"
  },
  {
    "path": "agent/utils/ai_tools/xpu/xpu.go",
    "content": "package xpu\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype XpuSMI struct{}\n\nfunc New() (bool, XpuSMI) {\n\treturn cmd.Which(\"xpu-smi\"), XpuSMI{}\n}\n\nfunc (x XpuSMI) loadDeviceData(device Device, wg *sync.WaitGroup, res *[]XPUSimpleInfo, mu *sync.Mutex) {\n\tdefer wg.Done()\n\n\tvar xpu XPUSimpleInfo\n\txpu.DeviceID = device.DeviceID\n\txpu.DeviceName = device.DeviceName\n\n\tvar xpuData, statsData string\n\tvar xpuErr, statsErr error\n\n\tvar wgCmd sync.WaitGroup\n\twgCmd.Add(2)\n\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))\n\tgo func() {\n\t\tdefer wgCmd.Done()\n\t\txpuData, xpuErr = cmdMgr.RunWithStdoutBashCf(\"xpu-smi discovery -d %d -j\", device.DeviceID)\n\t}()\n\n\tgo func() {\n\t\tdefer wgCmd.Done()\n\t\tstatsData, statsErr = cmdMgr.RunWithStdoutBashCf(\"xpu-smi stats -d %d -j\", device.DeviceID)\n\t}()\n\n\twgCmd.Wait()\n\n\tif xpuErr != nil {\n\t\tglobal.LOG.Errorf(\"calling xpu-smi discovery failed for device %d, %v\", device.DeviceID, xpuErr)\n\t\treturn\n\t}\n\n\tvar info Device\n\tif err := json.Unmarshal([]byte(xpuData), &info); err != nil {\n\t\tglobal.LOG.Errorf(\"xpuData json unmarshal failed for device %d, err: %v\", device.DeviceID, err)\n\t\treturn\n\t}\n\n\tbytes, err := strconv.ParseInt(info.MemoryPhysicalSizeByte, 10, 64)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Error parsing memory size for device %d, err: %v\", device.DeviceID, err)\n\t\treturn\n\t}\n\txpu.Memory = fmt.Sprintf(\"%.1f MB\", float64(bytes)/(1024*1024))\n\n\tif statsErr != nil {\n\t\tglobal.LOG.Errorf(\"calling xpu-smi stats failed for device %d, err: %v\", device.DeviceID, statsErr)\n\t\treturn\n\t}\n\n\tvar stats DeviceStats\n\tif err := json.Unmarshal([]byte(statsData), &stats); err != nil {\n\t\tglobal.LOG.Errorf(\"statsData json unmarshal failed for device %d, err: %v\", device.DeviceID, err)\n\t\treturn\n\t}\n\n\tfor _, stat := range stats.DeviceLevel {\n\t\tswitch stat.MetricsType {\n\t\tcase \"XPUM_STATS_POWER\":\n\t\t\txpu.Power = fmt.Sprintf(\"%.1fW\", stat.Value)\n\t\tcase \"XPUM_STATS_GPU_CORE_TEMPERATURE\":\n\t\t\txpu.Temperature = fmt.Sprintf(\"%.1f°C\", stat.Value)\n\t\tcase \"XPUM_STATS_MEMORY_USED\":\n\t\t\txpu.MemoryUsed = fmt.Sprintf(\"%.1fMB\", stat.Value)\n\t\tcase \"XPUM_STATS_MEMORY_UTILIZATION\":\n\t\t\txpu.MemoryUtil = fmt.Sprintf(\"%.1f%%\", stat.Value)\n\t\t}\n\t}\n\n\tmu.Lock()\n\t*res = append(*res, xpu)\n\tmu.Unlock()\n}\n\nfunc (x XpuSMI) LoadDashData() ([]XPUSimpleInfo, error) {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))\n\tdata, err := cmdMgr.RunWithStdoutBashC(\"xpu-smi discovery -j\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"calling xpu-smi failed, %v\", err)\n\t}\n\n\tvar deviceInfo DeviceInfo\n\tif err := json.Unmarshal([]byte(data), &deviceInfo); err != nil {\n\t\treturn nil, fmt.Errorf(\"deviceInfo json unmarshal failed, err: %w\", err)\n\t}\n\n\tvar res []XPUSimpleInfo\n\tvar wg sync.WaitGroup\n\tvar mu sync.Mutex\n\n\tfor _, device := range deviceInfo.DeviceList {\n\t\twg.Add(1)\n\t\tgo x.loadDeviceData(device, &wg, &res, &mu)\n\t}\n\n\twg.Wait()\n\n\tsort.Slice(res, func(i, j int) bool {\n\t\treturn res[i].DeviceID < res[j].DeviceID\n\t})\n\treturn res, nil\n}\n\nfunc (x XpuSMI) LoadGpuInfo() (*XpuInfo, error) {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))\n\tdata, err := cmdMgr.RunWithStdoutBashC(\"xpu-smi discovery -j\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"calling xpu-smi  failed, %v\", err)\n\t}\n\tvar deviceInfo DeviceInfo\n\tif err := json.Unmarshal([]byte(data), &deviceInfo); err != nil {\n\t\treturn nil, fmt.Errorf(\"deviceInfo json unmarshal failed, err: %w\", err)\n\t}\n\tres := &XpuInfo{\n\t\tType: \"xpu\",\n\t}\n\n\tvar wg sync.WaitGroup\n\tvar mu sync.Mutex\n\n\tfor _, device := range deviceInfo.DeviceList {\n\t\twg.Add(1)\n\t\tgo x.loadDeviceInfo(device, &wg, res, &mu)\n\t}\n\n\twg.Wait()\n\n\tprocessData, err := cmdMgr.RunWithStdoutBashC(\"xpu-smi ps -j\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"calling xpu-smi ps failed, %s\", err)\n\t}\n\tvar psList DeviceUtilByProcList\n\tif err := json.Unmarshal([]byte(processData), &psList); err != nil {\n\t\treturn nil, fmt.Errorf(\"processData json unmarshal failed, err: %w\", err)\n\t}\n\tfor _, ps := range psList.DeviceUtilByProcList {\n\t\tprocess := Process{\n\t\t\tPID:     ps.ProcessID,\n\t\t\tCommand: ps.ProcessName,\n\t\t}\n\t\tif ps.SharedMemSize > 0 {\n\t\t\tprocess.SHR = fmt.Sprintf(\"%.1f MB\", ps.SharedMemSize/1024)\n\t\t}\n\t\tif ps.MemSize > 0 {\n\t\t\tprocess.Memory = fmt.Sprintf(\"%.1f MB\", ps.MemSize/1024)\n\t\t}\n\t\tfor index, xpu := range res.Xpu {\n\t\t\tif xpu.Basic.DeviceID == ps.DeviceID {\n\t\t\t\tres.Xpu[index].Processes = append(res.Xpu[index].Processes, process)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn res, nil\n}\n\nfunc (x XpuSMI) loadDeviceInfo(device Device, wg *sync.WaitGroup, res *XpuInfo, mu *sync.Mutex) {\n\tdefer wg.Done()\n\n\txpu := Xpu{\n\t\tBasic: Basic{\n\t\t\tDeviceID:      device.DeviceID,\n\t\t\tDeviceName:    device.DeviceName,\n\t\t\tVendorName:    device.VendorName,\n\t\t\tPciBdfAddress: device.PciBdfAddress,\n\t\t},\n\t}\n\n\tvar xpuData, statsData string\n\tvar xpuErr, statsErr error\n\n\tvar wgCmd sync.WaitGroup\n\twgCmd.Add(2)\n\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))\n\tgo func() {\n\t\tdefer wgCmd.Done()\n\t\txpuData, xpuErr = cmdMgr.RunWithStdoutBashCf(\"xpu-smi discovery -d %d -j\", device.DeviceID)\n\t}()\n\n\tgo func() {\n\t\tdefer wgCmd.Done()\n\t\tstatsData, statsErr = cmdMgr.RunWithStdoutBashCf(\"xpu-smi stats -d %d -j\", device.DeviceID)\n\t}()\n\n\twgCmd.Wait()\n\n\tif xpuErr != nil {\n\t\tglobal.LOG.Errorf(\"calling xpu-smi discovery failed for device %d, %v\", device.DeviceID, xpuErr)\n\t\treturn\n\t}\n\n\tvar info Device\n\tif err := json.Unmarshal([]byte(xpuData), &info); err != nil {\n\t\tglobal.LOG.Errorf(\"xpuData json unmarshal failed for device %d, err: %v\", device.DeviceID, err)\n\t\treturn\n\t}\n\n\tres.DriverVersion = info.DriverVersion\n\txpu.Basic.DriverVersion = info.DriverVersion\n\n\tbytes, err := strconv.ParseInt(info.MemoryPhysicalSizeByte, 10, 64)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"Error parsing memory size for device %d, err: %v\", device.DeviceID, err)\n\t\treturn\n\t}\n\txpu.Basic.Memory = fmt.Sprintf(\"%.1f MB\", float64(bytes)/(1024*1024))\n\txpu.Basic.FreeMemory = info.MemoryFreeSizeByte\n\n\tif statsErr != nil {\n\t\tglobal.LOG.Errorf(\"calling xpu-smi stats failed for device %d, err: %v\", device.DeviceID, statsErr)\n\t\treturn\n\t}\n\n\tvar stats DeviceStats\n\tif err := json.Unmarshal([]byte(statsData), &stats); err != nil {\n\t\tglobal.LOG.Errorf(\"statsData json unmarshal failed for device %d, err: %v\", device.DeviceID, err)\n\t\treturn\n\t}\n\n\tfor _, stat := range stats.DeviceLevel {\n\t\tswitch stat.MetricsType {\n\t\tcase \"XPUM_STATS_POWER\":\n\t\t\txpu.Stats.Power = fmt.Sprintf(\"%.1fW\", stat.Value)\n\t\tcase \"XPUM_STATS_GPU_FREQUENCY\":\n\t\t\txpu.Stats.Frequency = fmt.Sprintf(\"%.1fMHz\", stat.Value)\n\t\tcase \"XPUM_STATS_GPU_CORE_TEMPERATURE\":\n\t\t\txpu.Stats.Temperature = fmt.Sprintf(\"%.1f°C\", stat.Value)\n\t\tcase \"XPUM_STATS_MEMORY_USED\":\n\t\t\txpu.Stats.MemoryUsed = fmt.Sprintf(\"%.1fMB\", stat.Value)\n\t\tcase \"XPUM_STATS_MEMORY_UTILIZATION\":\n\t\t\txpu.Stats.MemoryUtil = fmt.Sprintf(\"%.1f%%\", stat.Value)\n\t\t}\n\t}\n\n\tmu.Lock()\n\tres.Xpu = append(res.Xpu, xpu)\n\tmu.Unlock()\n}\n"
  },
  {
    "path": "agent/utils/ai_tools/xpu/xpu_info.go",
    "content": "package xpu\n\ntype XpuInfo struct {\n\tType          string `json:\"type\"`\n\tDriverVersion string `json:\"driverVersion\"`\n\n\tXpu []Xpu `json:\"xpu\"`\n}\n\ntype Xpu struct {\n\tBasic     Basic     `json:\"basic\"`\n\tStats     Stats     `json:\"stats\"`\n\tProcesses []Process `json:\"processes\"`\n}\n\ntype Basic struct {\n\tDeviceID      int    `json:\"deviceID\"`\n\tDeviceName    string `json:\"deviceName\"`\n\tVendorName    string `json:\"vendorName\"`\n\tDriverVersion string `json:\"driverVersion\"`\n\tMemory        string `json:\"memory\"`\n\tFreeMemory    string `json:\"freeMemory\"`\n\tPciBdfAddress string `json:\"pciBdfAddress\"`\n}\n\ntype Stats struct {\n\tPower       string `json:\"power\"`\n\tFrequency   string `json:\"frequency\"`\n\tTemperature string `json:\"temperature\"`\n\tMemoryUsed  string `json:\"memoryUsed\"`\n\tMemoryUtil  string `json:\"memoryUtil\"`\n}\n\ntype Process struct {\n\tPID     int    `json:\"pid\"`\n\tCommand string `json:\"command\"`\n\tSHR     string `json:\"shr\"`\n\tMemory  string `json:\"memory\"`\n}\n\ntype XPUSimpleInfo struct {\n\tDeviceID    int    `json:\"deviceID\"`\n\tDeviceName  string `json:\"deviceName\"`\n\tMemory      string `json:\"memory\"`\n\tTemperature string `json:\"temperature\"`\n\tMemoryUsed  string `json:\"memoryUsed\"`\n\tPower       string `json:\"power\"`\n\tMemoryUtil  string `json:\"memoryUtil\"`\n}\n"
  },
  {
    "path": "agent/utils/alert/alert.go",
    "content": "package alert\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/email\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/psutil\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"github.com/jinzhu/copier\"\n\t\"mime\"\n\tnetwork \"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar cronJobAlertTypes = []string{\"shell\", \"app\", \"website\", \"database\", \"directory\", \"log\", \"snapshot\", \"curl\", \"cutWebsiteLog\", \"clean\", \"ntp\"}\n\nfunc CreateTaskScanEmailAlertLog(alert dto.AlertDTO, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {\n\tparams := CreateAlertParams(GetCronJobTypeName(pushAlert.Param))\n\talertDetail := ProcessAlertDetail(alert, pushAlert.TaskName, params, method)\n\talertRule := ProcessAlertRule(alert)\n\tcreate.AlertRule = alertRule\n\tcreate.AlertDetail = alertDetail\n\treturn CreateEmailAlertLog(create, alert, params, transport, agentInfo)\n}\n\nfunc CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params []dto.Param, transport *http.Transport, agentInfo *dto.AgentInfo) error {\n\tvar alertLog model.AlertLog\n\talertRepo := repo.NewIAlertRepo()\n\tconfig, err := alertRepo.GetConfig(alertRepo.WithByType(constant.CommonConfig))\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar cfg dto.AlertCommonConfig\n\terr = json.Unmarshal([]byte(config.Config), &cfg)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcreate.Method = constant.Email\n\temailConfig, err := alertRepo.GetConfig(alertRepo.WithByType(constant.EmailConfig))\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar emailInfo dto.AlertEmailConfig\n\terr = json.Unmarshal([]byte(emailConfig.Config), &emailInfo)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif emailInfo.Host == \"\" {\n\t\tcreate.Message = \"email config is required\"\n\t\tcreate.Status = constant.AlertError\n\t\treturn SaveAlertLog(create, &alertLog)\n\t}\n\tif !global.IsMaster && cfg.IsOffline == constant.StatusEnable {\n\t\tcreate.Status = constant.AlertPushing\n\t\treturn SaveAlertLog(create, &alertLog)\n\t} else {\n\t\tusername := emailInfo.UserName\n\t\tif username == \"\" {\n\t\t\tusername = emailInfo.Sender\n\t\t}\n\t\tencodedDisplayName := mime.BEncoding.Encode(\"UTF-8\", emailInfo.DisplayName)\n\t\tsmtpConfig := email.SMTPConfig{\n\t\t\tHost:       emailInfo.Host,\n\t\t\tPort:       emailInfo.Port,\n\t\t\tSender:     emailInfo.Sender,\n\t\t\tUsername:   username,\n\t\t\tPassword:   emailInfo.Password,\n\t\t\tFrom:       fmt.Sprintf(`\"%s\" <%s>`, encodedDisplayName, emailInfo.Sender),\n\t\t\tEncryption: emailInfo.Encryption,\n\t\t\tRecipient:  emailInfo.Recipient,\n\t\t}\n\t\tcontent := GetSendContent(alert.Type, params, agentInfo)\n\t\tif content == \"\" {\n\t\t\tcontent = i18n.GetMsgWithMap(\"CommonAlert\", map[string]interface{}{\"msg\": alert.Title})\n\t\t}\n\t\tmsg := email.EmailMessage{\n\t\t\tSubject: i18n.GetMsgByKey(\"PanelAlertTitle\"),\n\t\t\tBody:    content,\n\t\t\tIsHTML:  true,\n\t\t}\n\n\t\tif err = email.SendMail(smtpConfig, msg, transport); err != nil {\n\t\t\tcreate.Message = err.Error()\n\t\t\tcreate.Status = constant.AlertError\n\t\t\treturn SaveAlertLog(create, &alertLog)\n\t\t}\n\t\tcreate.Status = constant.AlertSuccess\n\t\treturn SaveAlertLog(create, &alertLog)\n\t}\n}\n\nfunc SaveAlertLog(create dto.AlertLogCreate, alertLog *model.AlertLog) error {\n\talertRepo := repo.NewIAlertRepo()\n\tif err := copier.Copy(&alertLog, &create); err != nil {\n\t\treturn buserr.WithErr(\"ErrStructTransform\", err)\n\t}\n\n\tif err := alertRepo.CreateLog(alertLog); err != nil {\n\t\tglobal.LOG.Errorf(\"Error creating alert logs, err: %v\", err)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc CreateNewAlertTask(quota, alertType, quotaType, method string) {\n\talertRepo := repo.NewIAlertRepo()\n\ttaskBase := model.AlertTask{\n\t\tType:      alertType,\n\t\tQuota:     quota,\n\t\tQuotaType: quotaType,\n\t\tMethod:    method,\n\t}\n\terr := alertRepo.CreateAlertTask(&taskBase)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error creating alert tasks, err: %v\", err)\n\t}\n\tglobal.LOG.Infof(\"%s alert %s push completed\", alertType, method)\n}\n\nfunc ProcessAlertDetail(alert dto.AlertDTO, project string, params []dto.Param, method string) string {\n\talertDetail := dto.AlertDetail{\n\t\tType:    GetCronJobType(alert.Type),\n\t\tSubType: alert.Type,\n\t\tTitle:   alert.Title,\n\t\tMethod:  method,\n\t\tProject: project,\n\t\tParams:  params,\n\t}\n\tmarshal, err := json.Marshal(alertDetail)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error processing alert detail, err: %v\", err)\n\t\treturn \"\"\n\t}\n\treturn string(marshal)\n}\n\nfunc ProcessAlertRule(alert dto.AlertDTO) string {\n\tmarshal, err := json.Marshal(alert)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error processing alert rule, err: %v\", err)\n\t\treturn \"\"\n\t}\n\treturn string(marshal)\n}\n\nfunc GetCronJobType(alertType string) string {\n\tfor _, at := range cronJobAlertTypes {\n\t\tif at == alertType {\n\t\t\treturn \"cronJob\"\n\t\t}\n\t}\n\treturn alertType\n}\n\nfunc GetCronJobTypeName(cronJobType string) string {\n\tmodule := cronJobType\n\tswitch cronJobType {\n\tcase \"shell\":\n\t\tmodule = \"Shell 脚本\"\n\tcase \"app\":\n\t\tmodule = \"备份应用\"\n\tcase \"website\":\n\t\tmodule = \"备份网站\"\n\tcase \"database\":\n\t\tmodule = \"备份数据库\"\n\tcase \"log\":\n\t\tmodule = \"备份日志\"\n\tcase \"directory\":\n\t\tmodule = \"备份目录\"\n\tcase \"curl\":\n\t\tmodule = \"访问 URL\"\n\tcase \"cutWebsiteLog\":\n\t\tmodule = \"切割网站日志\"\n\tcase \"clean\":\n\t\tmodule = \"缓存清理\"\n\tcase \"snapshot\":\n\t\tmodule = \"系统快照\"\n\tcase \"ntp\":\n\t\tmodule = \"同步服务器时间\"\n\tdefault:\n\t}\n\treturn module\n}\n\nfunc CreateAlertParams(param string) []dto.Param {\n\treturn []dto.Param{\n\t\t{\n\t\t\tIndex: \"1\",\n\t\t\tKey:   \"param\",\n\t\t\tValue: param,\n\t\t},\n\t}\n}\n\nvar checkTaskMutex sync.Mutex\n\nfunc CheckSMSSendLimit(method string) bool {\n\talertRepo := repo.NewIAlertRepo()\n\tconfig, err := alertRepo.GetConfig(alertRepo.WithByType(constant.SMSConfig))\n\tif err != nil {\n\t\treturn false\n\t}\n\tvar cfg dto.AlertSmsConfig\n\tcfg, err = ParseAlertSmsConfig(config.Config)\n\tif err != nil {\n\t\treturn false\n\t}\n\tlimitCount, err := strconv.ParseUint(cfg.AlertDailyNum, 10, 64)\n\tif err != nil {\n\t\treturn false\n\t}\n\tcheckTaskMutex.Lock()\n\tdefer checkTaskMutex.Unlock()\n\ttodayCount, err := alertRepo.GetLicensePushCount(method)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"error getting license push count info, err: %v\", err)\n\t\treturn false\n\t}\n\tif todayCount >= uint(limitCount) {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\ntype Settings struct {\n\tNoticeAlert   Category `json:\"noticeAlert\"`\n\tResourceAlert Category `json:\"resourceAlert\"`\n}\n\ntype Category struct {\n\tSendTimeRange string   `json:\"sendTimeRange\"`\n\tType          []string `json:\"type\"`\n}\n\nfunc CheckSendTimeRange(alertType string) bool {\n\talertRepo := repo.NewIAlertRepo()\n\tconfig, err := alertRepo.GetConfig(alertRepo.WithByType(constant.CommonConfig))\n\tif err != nil {\n\t\treturn false\n\t}\n\tvar cfg dto.AlertCommonConfig\n\terr = json.Unmarshal([]byte(config.Config), &cfg)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tvar timeRange string\n\tif contains(cfg.AlertSendTimeRange.NoticeAlert.Type, alertType) {\n\t\ttimeRange = cfg.AlertSendTimeRange.NoticeAlert.SendTimeRange\n\t} else if contains(cfg.AlertSendTimeRange.ResourceAlert.Type, alertType) {\n\t\ttimeRange = cfg.AlertSendTimeRange.ResourceAlert.SendTimeRange\n\t} else {\n\t\tglobal.LOG.Warnf(\"Alert type not found in sendTimeRange: %s\", alertType)\n\t\treturn false\n\t}\n\n\tif !isWithinTimeRange(timeRange) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc contains(arr []string, target string) bool {\n\tfor _, item := range arr {\n\t\tif item == target {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc isWithinTimeRange(savedTimeString string) bool {\n\tnow := time.Now()\n\ttimeParts := strings.Split(savedTimeString, \" - \")\n\tif len(timeParts) != 2 {\n\t\tglobal.LOG.Info(\"Time range string format error, should be: 'HH:MM:SS - HH:MM:SS'\")\n\t\treturn false\n\t}\n\tstartTime, err1 := time.Parse(\"15:04:05\", strings.TrimSpace(timeParts[0]))\n\tendTime, err2 := time.Parse(\"15:04:05\", strings.TrimSpace(timeParts[1]))\n\tif err1 != nil || err2 != nil {\n\t\tglobal.LOG.Infof(\"Invalid time format in range: %s, errors: %v, %v\", savedTimeString, err1, err2)\n\t\treturn false\n\t}\n\n\tskipTime := time.Date(now.Year(), now.Month(), now.Day(), startTime.Hour(), startTime.Minute(), startTime.Second(), 0, now.Location())\n\tendSkipTime := time.Date(now.Year(), now.Month(), now.Day(), endTime.Hour(), endTime.Minute(), endTime.Second(), 0, now.Location())\n\n\tif endSkipTime.Before(skipTime) {\n\t\treturn now.After(skipTime) || now.Before(endSkipTime)\n\t}\n\treturn now.After(skipTime) && now.Before(endSkipTime)\n}\n\nfunc GetSendContent(alertType string, params []dto.Param, agentInfo *dto.AgentInfo) string {\n\tswitch GetCronJobType(alertType) {\n\tcase \"ssl\":\n\t\treturn i18n.GetMsgWithMap(\"SSLAlert\", map[string]interface{}{\"num\": getValueByIndex(params, \"1\"), \"day\": getValueByIndex(params, \"2\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"siteEndTime\":\n\t\treturn i18n.GetMsgWithMap(\"WebSiteAlert\", map[string]interface{}{\"num\": getValueByIndex(params, \"1\"), \"day\": getValueByIndex(params, \"2\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"panelPwdEndTime\":\n\t\treturn i18n.GetMsgWithMap(\"PanelPwdExpirationAlert\", map[string]interface{}{\"day\": getValueByIndex(params, \"1\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"licenseTime\":\n\t\treturn i18n.GetMsgWithMap(\"LicenseExpirationAlert\", map[string]interface{}{\"day\": getValueByIndex(params, \"1\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"panelUpdate\":\n\t\treturn i18n.GetMsgWithMap(\"PanelVersionAlert\", map[string]interface{}{\"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"cpu\":\n\t\treturn i18n.GetMsgWithMap(\"ResourceAlert\", map[string]interface{}{\"time\": getValueByIndex(params, \"1\"), \"name\": getValueByIndex(params, \"2\"), \"used\": getValueByIndex(params, \"3\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"memory\":\n\t\treturn i18n.GetMsgWithMap(\"ResourceAlert\", map[string]interface{}{\"time\": getValueByIndex(params, \"1\"), \"name\": getValueByIndex(params, \"2\"), \"used\": getValueByIndex(params, \"3\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"load\":\n\t\treturn i18n.GetMsgWithMap(\"ResourceAlert\", map[string]interface{}{\"time\": getValueByIndex(params, \"1\"), \"name\": getValueByIndex(params, \"2\"), \"used\": getValueByIndex(params, \"3\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"disk\":\n\t\treturn i18n.GetMsgWithMap(\"DiskUsedAlert\", map[string]interface{}{\"name\": getValueByIndex(params, \"1\"), \"used\": getValueByIndex(params, \"2\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"cronJob\":\n\t\treturn i18n.GetMsgWithMap(\"CronJobFailedAlert\", map[string]interface{}{\"name\": getValueByIndex(params, \"1\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"clams\":\n\t\treturn i18n.GetMsgWithMap(\"ClamAlert\", map[string]interface{}{\"num\": getValueByIndex(params, \"1\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"panelLogin\":\n\t\treturn i18n.GetMsgWithMap(\"SSHAndPanelLoginAlert\", map[string]interface{}{\"name\": getValueByIndex(params, \"1\"), \"loginIp\": getValueByIndex(params, \"2\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"sshLogin\":\n\t\treturn i18n.GetMsgWithMap(\"SSHAndPanelLoginAlert\", map[string]interface{}{\"name\": getValueByIndex(params, \"1\"), \"loginIp\": getValueByIndex(params, \"2\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"panelIpLogin\":\n\t\treturn i18n.GetMsgWithMap(\"SSHAndPanelLoginAlert\", map[string]interface{}{\"name\": getValueByIndex(params, \"1\"), \"loginIp\": getValueByIndex(params, \"2\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"sshIpLogin\":\n\t\treturn i18n.GetMsgWithMap(\"SSHAndPanelLoginAlert\", map[string]interface{}{\"name\": getValueByIndex(params, \"1\"), \"loginIp\": getValueByIndex(params, \"2\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"nodeException\":\n\t\treturn i18n.GetMsgWithMap(\"NodeExceptionAlert\", map[string]interface{}{\"num\": getValueByIndex(params, \"1\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tcase \"licenseException\":\n\t\treturn i18n.GetMsgWithMap(\"LicenseExceptionAlert\", map[string]interface{}{\"num\": getValueByIndex(params, \"1\"), \"node\": getNodeName(agentInfo), \"ip\": getNodeIp(agentInfo)})\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc getValueByIndex(params []dto.Param, index string) string {\n\tfor _, p := range params {\n\t\tif p.Index == index {\n\t\t\treturn p.Value\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc CountRecentFailedLoginLogs(minutes uint, failCount uint) (int, bool, error) {\n\tnow := time.Now()\n\tstartTime := now.Add(-time.Duration(minutes) * time.Minute)\n\tdb := global.CoreDB.Model(&model.LoginLog{})\n\tvar count int64\n\terr := db.Where(\"created_at >= ? AND status = ?\", startTime, constant.StatusFailed).\n\t\tCount(&count).Error\n\tif err != nil {\n\t\treturn 0, false, err\n\t}\n\treturn int(count), int(count) >= int(failCount), nil\n}\n\nfunc FindRecentSuccessLoginsNotInWhitelist(minutes int, whitelist []string) ([]model.LoginLog, error) {\n\tnow := time.Now()\n\tstartTime := now.Add(-time.Duration(minutes) * time.Minute)\n\n\twhitelistMap := make(map[string]struct{})\n\tfor _, ip := range whitelist {\n\t\twhitelistMap[ip] = struct{}{}\n\t}\n\n\tvar logs []model.LoginLog\n\terr := global.CoreDB.Model(&model.LoginLog{}).\n\t\tWhere(\"created_at >= ? AND status = ?\", startTime, constant.StatusSuccess).\n\t\tFind(&logs).Error\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar abnormalLogs []model.LoginLog\n\tfor _, log := range logs {\n\t\tif _, ok := whitelistMap[log.IP]; !ok {\n\t\t\tabnormalLogs = append(abnormalLogs, log)\n\t\t}\n\t}\n\treturn abnormalLogs, nil\n}\n\nfunc CountRecentFailedSSHLog(minutes uint, maxAllowed uint) (int, bool, error) {\n\tlines, err := grepSSHLog([]string{\"Failed password\", \"Invalid user\", \"authentication failure\"})\n\tif err != nil {\n\t\treturn 0, false, err\n\t}\n\n\tthresholdTime := time.Now().Add(-time.Duration(minutes) * time.Minute)\n\tcount := 0\n\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tt, err := parseLogTime(line)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif t.After(thresholdTime) {\n\t\t\tcount++\n\t\t}\n\t}\n\treturn count, count >= int(maxAllowed), nil\n}\n\nfunc FindRecentSuccessLoginNotInWhitelist(minutes int, whitelist []string) ([]string, error) {\n\tlines, err := grepSSHLog([]string{\"Accepted password\", \"Accepted publickey\"})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tthresholdTime := time.Now().Add(-time.Duration(minutes) * time.Minute)\n\tvar abnormalLogins []string\n\n\twhitelistMap := make(map[string]struct{}, len(whitelist))\n\tfor _, ip := range whitelist {\n\t\twhitelistMap[ip] = struct{}{}\n\t}\n\n\tipRegex := re.GetRegex(re.AlertIPPattern)\n\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tt, err := parseLogTime(line)\n\t\tif err != nil || t.Before(thresholdTime) {\n\t\t\tcontinue\n\t\t}\n\n\t\tmatch := ipRegex.FindStringSubmatch(line)\n\t\tif len(match) >= 2 {\n\t\t\tip := match[1]\n\t\t\tif _, ok := whitelistMap[ip]; !ok {\n\t\t\t\tabnormalLogins = append(abnormalLogins, fmt.Sprintf(\"%s-%s\", ip, t.Format(\"2006-01-02 15:04:05\")))\n\t\t\t}\n\t\t}\n\t}\n\n\treturn abnormalLogins, nil\n}\n\nfunc findGrepPath() (string, error) {\n\tpath, err := exec.LookPath(\"grep\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"grep not found in PATH: %w\", err)\n\t}\n\treturn path, nil\n}\n\nfunc grepSSHLog(keywords []string) ([]string, error) {\n\tlogFiles := []string{\"/var/log/secure\", \"/var/log/auth.log\"}\n\tvar results []string\n\tseen := make(map[string]struct{})\n\n\tgrepPath, err := findGrepPath()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"find grep failed: %w\", err)\n\t}\n\n\tfor _, logFile := range logFiles {\n\t\tif _, err := os.Stat(logFile); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, keyword := range keywords {\n\t\t\tcmd := exec.Command(grepPath, \"-a\", keyword, logFile)\n\t\t\toutput, err := cmd.Output()\n\t\t\tif err != nil {\n\t\t\t\tvar exitErr *exec.ExitError\n\t\t\t\tif errors.As(err, &exitErr) {\n\t\t\t\t\tif exitErr.ExitCode() == 1 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn nil, fmt.Errorf(\"read log file fail [%s]: %w\", logFile, err)\n\t\t\t}\n\n\t\t\tlines := strings.Split(string(output), \"\\n\")\n\t\t\tfor _, line := range lines {\n\t\t\t\tline = strings.TrimSpace(line)\n\t\t\t\tif line != \"\" {\n\t\t\t\t\tif _, exists := seen[line]; !exists {\n\t\t\t\t\t\tresults = append(results, line)\n\t\t\t\t\t\tseen[line] = struct{}{}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results, nil\n}\n\nfunc parseLogTime(line string) (time.Time, error) {\n\tif len(line) < 15 {\n\t\treturn time.Time{}, nil\n\t}\n\ttimeStr := line[:15]\n\tparsedTime, err := time.ParseInLocation(\"Jan 2 15:04:05\", timeStr, time.Local)\n\tif err != nil {\n\t\treturn time.Time{}, nil\n\t}\n\treturn parsedTime.AddDate(time.Now().Year(), 0, 0), nil\n}\n\nfunc getNodeName(agentInfo *dto.AgentInfo) string {\n\tvar nodeName string\n\tif agentInfo != nil && agentInfo.NodeName != \"\" {\n\t\tnodeName = agentInfo.NodeName\n\t}\n\n\treturn formatWithFallback(nodeName, getFallbackHostname)\n}\n\nfunc getNodeIp(agentInfo *dto.AgentInfo) string {\n\tvar nodeIP string\n\tif agentInfo != nil && agentInfo.NodeAddr != \"\" && agentInfo.NodeAddr != \"127.0.0.1\" {\n\t\tnodeIP = agentInfo.NodeAddr\n\t}\n\n\treturn formatWithFallback(nodeIP, getFallbackIP)\n}\n\nfunc formatWithFallback(value string, fallback func() string) string {\n\tvalue = strings.TrimSpace(value)\n\tif value == \"\" {\n\t\tvalue = strings.TrimSpace(fallback())\n\t}\n\n\tif value == \"\" {\n\t\treturn \"\"\n\t}\n\n\treturn fmt.Sprintf(\"「%s」\", value)\n}\n\nfunc getFallbackHostname() string {\n\thostInfo, err := psutil.HOST.GetHostInfo(false)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn hostInfo.Hostname\n}\n\nfunc getFallbackIP() string {\n\tif systemIP, err := repo.NewISettingRepo().GetValueByKey(\"SystemIP\"); err == nil && systemIP != \"\" {\n\t\treturn systemIP\n\t}\n\treturn loadOutboundIP()\n}\n\nfunc loadOutboundIP() string {\n\tconn, err := network.Dial(\"udp\", \"8.8.8.8:80\")\n\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tdefer conn.Close()\n\tlocalAddr := conn.LocalAddr().(*network.UDPAddr)\n\treturn localAddr.IP.String()\n}\n\nfunc ParseAlertSmsConfig(configJSON string) (dto.AlertSmsConfig, error) {\n\tvar tempMap map[string]interface{}\n\terr := json.Unmarshal([]byte(configJSON), &tempMap)\n\tif err != nil {\n\t\treturn dto.AlertSmsConfig{}, err\n\t}\n\n\tvar cfg dto.AlertSmsConfig\n\tif phone, ok := tempMap[\"phone\"].(string); ok {\n\t\tcfg.Phone = phone\n\t}\n\n\tswitch v := tempMap[\"alertDailyNum\"].(type) {\n\tcase float64:\n\t\tcfg.AlertDailyNum = strconv.FormatFloat(v, 'f', 0, 64)\n\tcase string:\n\t\tcfg.AlertDailyNum = v\n\tdefault:\n\t\tcfg.AlertDailyNum = \"50\"\n\t}\n\n\treturn cfg, nil\n}\n"
  },
  {
    "path": "agent/utils/alert_push/alert_push.go",
    "content": "package alert_push\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\talertUtil \"github.com/1Panel-dev/1Panel/agent/utils/alert\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\t\"github.com/jinzhu/copier\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc PushAlert(pushAlert dto.PushAlert) error {\n\tif !alertUtil.CheckSendTimeRange(alertUtil.GetCronJobType(pushAlert.AlertType)) {\n\t\treturn nil\n\t}\n\n\talertRepo := repo.NewIAlertRepo()\n\talertInfo, err := alertRepo.Get(alertRepo.WithByType(pushAlert.AlertType), alertRepo.WithByProject(strconv.Itoa(int(pushAlert.EntryID))), repo.WithByStatus(constant.AlertEnable))\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar alert dto.AlertDTO\n\t_ = copier.Copy(&alert, &alertInfo)\n\n\tmethods := strings.Split(alert.Method, \",\")\n\tfor _, m := range methods {\n\t\tm = strings.TrimSpace(m)\n\t\tswitch m {\n\t\tcase constant.SMS:\n\t\t\tif !alertUtil.CheckSMSSendLimit(constant.SMS) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttodayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.SMS)\n\t\t\tif err != nil || alert.SendCount <= todayCount {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar create = dto.AlertLogCreate{\n\t\t\t\tType:    alertUtil.GetCronJobType(alert.Type),\n\t\t\t\tAlertId: alert.ID,\n\t\t\t\tCount:   todayCount + 1,\n\t\t\t}\n\t\t\terr = xpack.CreateTaskScanSMSAlertLog(alert, alert.Type, create, pushAlert, constant.SMS)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"%s alert sms push failed: %v\", alert.Type, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\talertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.SMS)\n\t\tcase constant.Email:\n\t\t\ttodayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Email)\n\t\t\tif err != nil || alert.SendCount <= todayCount {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar create = dto.AlertLogCreate{\n\t\t\t\tType:    alertUtil.GetCronJobType(alert.Type),\n\t\t\t\tAlertId: alert.ID,\n\t\t\t\tCount:   todayCount + 1,\n\t\t\t}\n\t\t\ttransport := xpack.LoadRequestTransport()\n\t\t\tagentInfo, _ := xpack.GetAgentInfo()\n\t\t\terr = alertUtil.CreateTaskScanEmailAlertLog(alert, create, pushAlert, constant.Email, transport, agentInfo)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"%s alert email push failed: %v\", alert.Type, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\talertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Email)\n\t\tcase constant.WeCom, constant.DingTalk, constant.FeiShu:\n\t\t\ttodayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), m)\n\t\t\tif err != nil || alert.SendCount <= todayCount {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar create = dto.AlertLogCreate{\n\t\t\t\tType:    alertUtil.GetCronJobType(alert.Type),\n\t\t\t\tAlertId: alert.ID,\n\t\t\t\tCount:   todayCount + 1,\n\t\t\t}\n\t\t\ttransport := xpack.LoadRequestTransport()\n\t\t\tagentInfo, _ := xpack.GetAgentInfo()\n\t\t\terr = xpack.CreateTaskScanWebhookAlertLog(alert, alert.Type, create, pushAlert, m, transport, agentInfo)\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"%s alert %s webhook push failed: %v\", alert.Type, m, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\talertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), m)\n\t\tdefault:\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/appicon/appicon.go",
    "content": "package appicon\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\nconst IconPrefix = \"app_\"\n\nfunc IsIconFile(icon string) bool {\n\treturn strings.HasPrefix(icon, IconPrefix)\n}\n\nfunc ParseIconField(icon string) (fileName, etag string) {\n\tif !IsIconFile(icon) {\n\t\treturn \"\", \"\"\n\t}\n\tparts := strings.SplitN(icon, \"?\", 2)\n\tfileName = parts[0]\n\tif len(parts) == 2 {\n\t\tvalues, err := url.ParseQuery(parts[1])\n\t\tif err == nil {\n\t\t\tetag = values.Get(\"etag\")\n\t\t}\n\t}\n\treturn\n}\n\nfunc BuildIconField(fileName, etag string) string {\n\tif etag == \"\" {\n\t\treturn fileName\n\t}\n\treturn fmt.Sprintf(\"%s?etag=%s\", fileName, url.QueryEscape(etag))\n}\n\nfunc GetIconFilePath(fileName string) string {\n\treturn path.Join(global.Dir.IconCacheDir, fileName)\n}\n\nfunc BuildIconFileName(appKey, ext string) string {\n\treturn fmt.Sprintf(\"%s%s.%s\", IconPrefix, appKey, ext)\n}\n\nconst ContentTypePNG = \"image/png\"\n\nfunc WriteIconFile(appKey string, data []byte) (fileName string, err error) {\n\tfileName = BuildIconFileName(appKey, \"png\")\n\tfilePath := GetIconFilePath(fileName)\n\n\t_ = CleanOldIconFiles(appKey)\n\n\terr = os.WriteFile(filePath, data, 0644)\n\treturn\n}\n\nfunc CleanOldIconFiles(appKey string) error {\n\tpattern := path.Join(global.Dir.IconCacheDir, fmt.Sprintf(\"%s%s.*\", IconPrefix, appKey))\n\tmatches, err := filepath.Glob(pattern)\n\tif err != nil {\n\t\treturn err\n\t}\n\tkeepFileName := BuildIconFileName(appKey, \"png\")\n\tfor _, match := range matches {\n\t\tbaseName := filepath.Base(match)\n\t\tif baseName == keepFileName {\n\t\t\tcontinue\n\t\t}\n\t\t_ = os.Remove(match)\n\t}\n\treturn nil\n}\n\nfunc ReadIconFile(fileName string) ([]byte, error) {\n\tfilePath := GetIconFilePath(fileName)\n\treturn os.ReadFile(filePath)\n}\n\nfunc IconFileExists(fileName string) bool {\n\tfilePath := GetIconFilePath(fileName)\n\t_, err := os.Stat(filePath)\n\treturn err == nil\n}\n\nfunc GetETagFromIconField(icon string) string {\n\t_, etag := ParseIconField(icon)\n\treturn etag\n}\n"
  },
  {
    "path": "agent/utils/clam/clam.go",
    "content": "package clam\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/robfig/cron/v3\"\n)\n\nfunc AddScanTask(taskItem *task.Task, clam model.Clam, timeNow string) {\n\ttaskItem.AddSubTask(i18n.GetWithName(\"Clamscan\", clam.Path), func(t *task.Task) error {\n\t\tstrategy := \"\"\n\t\tswitch clam.InfectedStrategy {\n\t\tcase \"remove\":\n\t\t\tstrategy = \"--remove\"\n\t\tcase \"move\", \"copy\":\n\t\t\tdir := path.Join(clam.InfectedDir, \"1panel-infected\", clam.Name, timeNow)\n\t\t\ttaskItem.Log(\"infected dir: \" + dir)\n\t\t\tif _, err := os.Stat(dir); err != nil {\n\t\t\t\t_ = os.MkdirAll(dir, os.ModePerm)\n\t\t\t}\n\t\t\tstrategy = fmt.Sprintf(\"--%s=%s\", clam.InfectedStrategy, dir)\n\t\t}\n\t\ttaskItem.Logf(\"clamdscan --fdpass %s %s\", strategy, clam.Path)\n\t\tmgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(time.Duration(clam.Timeout)*time.Second), cmd.WithTask(*taskItem))\n\t\tif err := mgr.RunBashCf(\"clamdscan --fdpass %s %s\", strategy, clam.Path); err != nil {\n\t\t\treturn fmt.Errorf(\"clamdscan failed, %v\", err)\n\t\t}\n\t\treturn nil\n\t}, nil)\n}\n\nfunc AnalysisFromLog(pathItem string, record *model.ClamRecord) {\n\tfile, err := os.ReadFile(pathItem)\n\tif err != nil {\n\t\treturn\n\t}\n\tlines := strings.Split(string(file), \"\\n\")\n\tfor _, line := range lines {\n\t\tif len(line) < 20 {\n\t\t\tcontinue\n\t\t}\n\t\tline = line[20:]\n\t\tswitch {\n\t\tcase strings.HasPrefix(line, \"Infected files: \"):\n\t\t\trecord.InfectedFiles = strings.TrimPrefix(line, \"Infected files: \")\n\t\tcase strings.HasPrefix(line, \"Total errors: \"):\n\t\t\trecord.TotalError = strings.TrimPrefix(line, \"Total errors: \")\n\t\tcase strings.HasPrefix(line, \"Time: \"):\n\t\t\trecord.ScanTime = strings.TrimPrefix(line, \"Time: \")\n\t\t}\n\t}\n}\n\nfunc CheckWithStopAll(withCheck bool, clamRepo repo.IClamRepo) bool {\n\tif withCheck {\n\t\tisExist, _ := controller.CheckExist(\"clam\")\n\t\tif !isExist {\n\t\t\treturn false\n\t\t}\n\t\tisActive, _ := controller.CheckActive(\"clam\")\n\t\tif isActive {\n\t\t\treturn true\n\t\t}\n\t}\n\tclams, _ := clamRepo.List(repo.WithByStatus(constant.StatusEnable))\n\tfor i := 0; i < len(clams); i++ {\n\t\tglobal.Cron.Remove(cron.EntryID(clams[i].EntryID))\n\t\t_ = clamRepo.Update(clams[i].ID, map[string]interface{}{\"status\": constant.StatusDisable, \"entry_id\": 0})\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/ali.go",
    "content": "package client\n\nimport (\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/go-resty/resty/v2\"\n)\n\ntype aliClient struct {\n\ttoken   string\n\tdriveID string\n}\n\nfunc NewALIClient(vars map[string]interface{}) (*aliClient, error) {\n\trefresh_token := loadParamFromVars(\"refresh_token\", vars)\n\tdrive_id := loadParamFromVars(\"drive_id\", vars)\n\n\ttoken, err := loadToken(refresh_token)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &aliClient{token: token, driveID: drive_id}, nil\n}\n\nfunc (a aliClient) ListBuckets() ([]interface{}, error) {\n\treturn nil, nil\n}\n\nfunc (a aliClient) Exist(pathItem string) (bool, error) {\n\tpathItem = path.Join(\"root\", pathItem)\n\tif _, err := a.loadFileWithName(pathItem); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (a aliClient) Size(pathItem string) (int64, error) {\n\tpathItem = path.Join(\"root\", pathItem)\n\tfileInfo, err := a.loadFileWithName(pathItem)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn int64(fileInfo.Size), nil\n}\n\nfunc (a aliClient) Delete(pathItem string) (bool, error) {\n\tpathItem = path.Join(\"root\", pathItem)\n\tfileInfo, err := a.loadFileWithName(pathItem)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\tdata := map[string]interface{}{\n\t\t\"drive_id\": a.driveID,\n\t\t\"file_id\":  fileInfo.FileID,\n\t}\n\turl := \"https://api.alipan.com/v2/file/delete\"\n\tresp, err := client.R().\n\t\tSetHeader(\"Authorization\", a.token).\n\t\tSetBody(data).\n\t\tPost(url)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif resp.StatusCode() != 204 {\n\t\treturn false, fmt.Errorf(\"delete file %s failed, err: %v\", pathItem, string(resp.Body()))\n\t}\n\treturn true, nil\n}\n\nfunc (a aliClient) Upload(src, target string) (bool, error) {\n\ttarget = path.Join(\"/root\", target)\n\tparentID := \"root\"\n\tvar err error\n\tif path.Dir(target) != \"/root\" {\n\t\tparentID, err = a.mkdirWithPath(path.Dir(target))\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tfile, err := os.Open(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\tfileInfo, err := file.Stat()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdata := map[string]interface{}{\n\t\t\"drive_id\":        a.driveID,\n\t\t\"part_info_list\":  makePartInfoList(fileInfo.Size()),\n\t\t\"parent_file_id\":  parentID,\n\t\t\"name\":            path.Base(src),\n\t\t\"type\":            \"file\",\n\t\t\"size\":            fileInfo.Size(),\n\t\t\"check_name_mode\": \"auto_rename\",\n\t}\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\turl := \"https://api.alipan.com/v2/file/create\"\n\n\tresp, err := client.R().\n\t\tSetHeader(\"Authorization\", a.token).\n\t\tSetBody(data).\n\t\tPost(url)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tvar createResp createFileResp\n\tif err := json.Unmarshal(resp.Body(), &createResp); err != nil {\n\t\treturn false, err\n\t}\n\tfor _, part := range createResp.PartInfoList {\n\t\terr = a.uploadPart(part.UploadURL, io.LimitReader(file, 1024*1024*1024))\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif err := a.completeUpload(createResp.UploadID, createResp.FileID); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (a aliClient) Download(src, target string) (bool, error) {\n\tsrc = path.Join(\"/root\", src)\n\tfileInfo, err := a.loadFileWithName(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\tif fileInfo.Size > 100*1024*1024 {\n\t\treturn false, fmt.Errorf(\"The translation file %s exceeds 100MB, please download it through the client.\", src)\n\t}\n\tdata := map[string]interface{}{\n\t\t\"drive_id\": a.driveID,\n\t\t\"file_id\":  fileInfo.FileID,\n\t}\n\turl := \"https://api.aliyundrive.com/v2/file/get_download_url\"\n\tresp, err := client.R().\n\t\tSetHeader(\"Authorization\", a.token).\n\t\tSetBody(data).\n\t\tPost(url)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif resp.StatusCode() != 200 {\n\t\treturn false, fmt.Errorf(\"download file %s failed, err: %v\", src, string(resp.Body()))\n\t}\n\tvar respItem downloadResp\n\tif err := json.Unmarshal(resp.Body(), &respItem); err != nil {\n\t\treturn false, err\n\t}\n\tif err := a.handleDownload(respItem.URL, target); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (a *aliClient) ListObjects(src string) ([]string, error) {\n\tif len(src) == 0 || src == \"root\" || src == \"/root\" {\n\t\tsrc = \"root\"\n\t} else {\n\t\tsrc = path.Join(\"/root\", src)\n\t}\n\tfileInfos, err := a.loadDirWithPath(src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar names []string\n\tfor _, item := range fileInfos {\n\t\tnames = append(names, item.Name)\n\t}\n\treturn names, nil\n}\n\nfunc (a aliClient) loadFileWithName(pathItem string) (fileInfo, error) {\n\tpathItems := strings.Split(pathItem, \"/\")\n\tvar (\n\t\tfileInfos []fileInfo\n\t\terr       error\n\t)\n\tparentID := \"root\"\n\tfor i := 0; i < len(pathItems); i++ {\n\t\tif len(pathItems[i]) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfileInfos, err = a.loadFileWithParentID(parentID)\n\t\tif err != nil {\n\t\t\treturn fileInfo{}, err\n\t\t}\n\t\tisEnd := false\n\t\tif i == len(pathItems)-2 {\n\t\t\tisEnd = true\n\t\t}\n\t\texist := false\n\t\tfor _, item := range fileInfos {\n\t\t\tif item.Name == pathItems[i+1] {\n\t\t\t\tif isEnd {\n\t\t\t\t\treturn item, nil\n\t\t\t\t} else {\n\t\t\t\t\tparentID = item.FileID\n\t\t\t\t\texist = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !exist {\n\t\t\treturn fileInfo{}, errors.New(\"no such file or dir\")\n\t\t}\n\n\t}\n\treturn fileInfo{}, errors.New(\"no such file or dir\")\n}\n\nfunc (a aliClient) loadDirWithPath(path string) ([]fileInfo, error) {\n\tpathItems := strings.Split(path, \"/\")\n\tvar (\n\t\tfileInfos []fileInfo\n\t\terr       error\n\t)\n\tparentID := \"root\"\n\tfor i := 0; i < len(pathItems); i++ {\n\t\tif len(pathItems[i]) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfileInfos, err = a.loadFileWithParentID(parentID)\n\t\tif err != nil {\n\t\t\treturn fileInfos, err\n\t\t}\n\t\tif i == len(pathItems)-1 {\n\t\t\treturn fileInfos, nil\n\t\t}\n\t\texist := false\n\t\tfor _, item := range fileInfos {\n\t\t\tif item.Name == pathItems[i+1] {\n\t\t\t\tparentID = item.FileID\n\t\t\t\texist = true\n\t\t\t}\n\t\t}\n\t\tif !exist {\n\t\t\treturn nil, errors.New(\"no such file or dir\")\n\t\t}\n\t}\n\treturn fileInfos, errors.New(\"no such file or dir\")\n}\n\nfunc (a aliClient) loadFileWithParentID(parentID string) ([]fileInfo, error) {\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\tdata := map[string]interface{}{\n\t\t\"drive_id\":       a.driveID,\n\t\t\"fields\":         \"*\",\n\t\t\"limit\":          100,\n\t\t\"parent_file_id\": parentID,\n\t}\n\turl := \"https://api.aliyundrive.com/adrive/v3/file/list\"\n\tresp, err := client.R().\n\t\tSetHeader(\"Authorization\", a.token).\n\t\tSetBody(data).\n\t\tPost(url)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif resp.StatusCode() != 200 {\n\t\treturn nil, fmt.Errorf(\"load file list failed, code: %v, err: %v\", resp.StatusCode(), string(resp.Body()))\n\t}\n\tvar fileResp fileResp\n\tif err := json.Unmarshal(resp.Body(), &fileResp); err != nil {\n\t\treturn nil, err\n\t}\n\treturn fileResp.Items, nil\n}\n\nfunc (a aliClient) mkdirWithPath(target string) (string, error) {\n\tpathItems := strings.Split(target, \"/\")\n\tvar (\n\t\tfileInfos []fileInfo\n\t\terr       error\n\t)\n\tparentID := \"root\"\n\tfor i := 0; i < len(pathItems); i++ {\n\t\tif len(pathItems[i]) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfileInfos, err = a.loadFileWithParentID(parentID)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tisEnd := false\n\t\tif i == len(pathItems)-2 {\n\t\t\tisEnd = true\n\t\t}\n\t\texist := false\n\t\tfor _, item := range fileInfos {\n\t\t\tif item.Name == pathItems[i+1] {\n\t\t\t\tparentID = item.FileID\n\t\t\t\tif isEnd {\n\t\t\t\t\treturn item.FileID, nil\n\t\t\t\t} else {\n\t\t\t\t\texist = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !exist {\n\t\t\tparentID, err = a.mkdir(parentID, pathItems[i+1])\n\t\t\tif err != nil {\n\t\t\t\treturn parentID, err\n\t\t\t}\n\t\t\tif isEnd {\n\t\t\t\treturn parentID, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\", errors.New(\"mkdir failed.\")\n}\n\nfunc (a aliClient) mkdir(parentID, name string) (string, error) {\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\tdata := map[string]interface{}{\n\t\t\"drive_id\":       a.driveID,\n\t\t\"name\":           name,\n\t\t\"type\":           \"folder\",\n\t\t\"limit\":          100,\n\t\t\"parent_file_id\": parentID,\n\t}\n\turl := \"https://api.aliyundrive.com/adrive/v2/file/createWithFolders\"\n\tresp, err := client.R().\n\t\tSetHeader(\"Authorization\", a.token).\n\t\tSetBody(data).\n\t\tPost(url)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif resp.StatusCode() != 201 {\n\t\treturn \"\", fmt.Errorf(\"mkdir %s failed, code: %v, err: %v\", name, resp.StatusCode(), string(resp.Body()))\n\t}\n\tvar mkdirResp mkdirResp\n\tif err := json.Unmarshal(resp.Body(), &mkdirResp); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn mkdirResp.FileID, nil\n}\n\ntype fileResp struct {\n\tItems []fileInfo `json:\"items\"`\n}\ntype fileInfo struct {\n\tFileID string `json:\"file_id\"`\n\tName   string `json:\"name\"`\n\tSize   int    `json:\"size\"`\n}\n\ntype mkdirResp struct {\n\tFileID string `json:\"file_id\"`\n}\n\ntype partInfo struct {\n\tPartNumber        int    `json:\"part_number\"`\n\tUploadURL         string `json:\"upload_url\"`\n\tInternalUploadURL string `json:\"internal_upload_url\"`\n\tContentType       string `json:\"content_type\"`\n}\n\nfunc makePartInfoList(size int64) []*partInfo {\n\tvar res []*partInfo\n\tmaxPartSize := int64(1024 * 1024 * 1024)\n\tpartInfoNum := int(size / maxPartSize)\n\tif size%maxPartSize > 0 {\n\t\tpartInfoNum += 1\n\t}\n\n\tfor i := 0; i < partInfoNum; i++ {\n\t\tres = append(res, &partInfo{PartNumber: i + 1})\n\t}\n\n\treturn res\n}\n\ntype createFileResp struct {\n\tType         string      `json:\"type\"`\n\tRapidUpload  bool        `json:\"rapid_upload\"`\n\tDomainId     string      `json:\"domain_id\"`\n\tDriveId      string      `json:\"drive_id\"`\n\tFileName     string      `json:\"file_name\"`\n\tEncryptMode  string      `json:\"encrypt_mode\"`\n\tLocation     string      `json:\"location\"`\n\tUploadID     string      `json:\"upload_id\"`\n\tFileID       string      `json:\"file_id\"`\n\tPartInfoList []*partInfo `json:\"part_info_list,omitempty\"`\n}\n\nfunc (a aliClient) uploadPart(uri string, reader io.Reader) error {\n\treq, err := http.NewRequest(http.MethodPut, uri, reader)\n\tif err != nil {\n\t\treturn err\n\t}\n\tclient := &http.Client{}\n\tdefer client.CloseIdleConnections()\n\tresponse, err := client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer response.Body.Close()\n\tif response.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"handle upload park file with url failed, code: %v\", response.StatusCode)\n\t}\n\n\treturn nil\n}\n\ntype downloadResp struct {\n\tURL string `json:\"url\"`\n}\n\nfunc (a aliClient) handleDownload(uri string, target string) error {\n\treq, err := http.NewRequest(http.MethodGet, uri, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Add(\"Authorization\", a.token)\n\treq.Header.Add(\"origin\", \"https://www.aliyundrive.com\")\n\treq.Header.Add(\"referer\", \"https://www.aliyundrive.com/\")\n\tclient := &http.Client{}\n\tdefer client.CloseIdleConnections()\n\tresponse, err := client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer response.Body.Close()\n\tif response.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"handle download with url failed, code: %v\", response.StatusCode)\n\t}\n\tif _, err := os.Stat(path.Dir(target)); err != nil {\n\t\t_ = os.MkdirAll(path.Dir(target), os.ModePerm)\n\t}\n\tout, err := os.Create(target)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer out.Close()\n\tif _, err = io.Copy(out, response.Body); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (a *aliClient) completeUpload(uploadID, fileID string) error {\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\tdata := map[string]interface{}{\n\t\t\"drive_id\":  a.driveID,\n\t\t\"upload_id\": uploadID,\n\t\t\"file_id\":   fileID,\n\t}\n\n\turl := \"https://api.aliyundrive.com/v2/file/complete\"\n\tresp, err := client.R().\n\t\tSetHeader(\"Authorization\", a.token).\n\t\tSetBody(data).\n\t\tPost(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resp.StatusCode() != 200 {\n\t\treturn fmt.Errorf(\"complete upload failed, err: %v\", string(resp.Body()))\n\t}\n\n\treturn nil\n}\n\ntype tokenResp struct {\n\tRefreshToken string `json:\"refresh_token\"`\n\tAccessToken  string `json:\"access_token\"`\n}\n\nfunc loadToken(refresh_token string) (string, error) {\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\tdata := map[string]interface{}{\n\t\t\"grant_type\":    \"refresh_token\",\n\t\t\"refresh_token\": refresh_token,\n\t}\n\n\turl := \"https://api.aliyundrive.com/token/refresh\"\n\tresp, err := client.R().\n\t\tSetBody(data).\n\t\tPost(url)\n\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"load account token failed, err: %v\", err)\n\t}\n\tif resp.StatusCode() != 200 {\n\t\treturn \"\", fmt.Errorf(\"load account token failed, code: %v\", resp.StatusCode())\n\t}\n\tvar respItem tokenResp\n\tif err := json.Unmarshal(resp.Body(), &respItem); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn respItem.AccessToken, nil\n}\n\nfunc RefreshALIToken(varMap map[string]interface{}) (string, error) {\n\trefresh_token := loadParamFromVars(\"refresh_token\", varMap)\n\tif len(refresh_token) == 0 {\n\t\treturn \"\", errors.New(\"no such refresh token find in db\")\n\t}\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\tdata := map[string]interface{}{\n\t\t\"grant_type\":    \"refresh_token\",\n\t\t\"refresh_token\": refresh_token,\n\t}\n\n\turl := \"https://api.aliyundrive.com/token/refresh\"\n\tresp, err := client.R().\n\t\tSetBody(data).\n\t\tPost(url)\n\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"load account token failed, err: %v\", err)\n\t}\n\tif resp.StatusCode() != 200 {\n\t\treturn \"\", fmt.Errorf(\"load account token failed, code: %v\", resp.StatusCode())\n\t}\n\tvar respItem tokenResp\n\tif err := json.Unmarshal(resp.Body(), &respItem); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn respItem.RefreshToken, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/cos.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\n\tcosSDK \"github.com/tencentyun/cos-go-sdk-v5\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n)\n\ntype cosClient struct {\n\tscType           string\n\tclient           *cosSDK.Client\n\tclientWithBucket *cosSDK.Client\n}\n\nfunc NewCosClient(vars map[string]interface{}) (*cosClient, error) {\n\tregion := loadParamFromVars(\"region\", vars)\n\tendpoint := loadParamFromVars(\"endpoint\", vars)\n\taccessKey := loadParamFromVars(\"accessKey\", vars)\n\tsecretKey := loadParamFromVars(\"secretKey\", vars)\n\tbucket := loadParamFromVars(\"bucket\", vars)\n\tscType := loadParamFromVars(\"scType\", vars)\n\tif len(scType) == 0 {\n\t\tscType = \"Standard\"\n\t}\n\n\tendpointType := \"cos\"\n\tif len(endpoint) != 0 {\n\t\tif re.GetRegex(re.CosDualStackPattern).MatchString(endpoint) {\n\t\t\tendpointType = \"cos-dualstack\"\n\t\t}\n\t}\n\n\tu, _ := url.Parse(fmt.Sprintf(\"https://%s.%s.myqcloud.com\", endpointType, region))\n\tb := &cosSDK.BaseURL{BucketURL: u}\n\tclient := cosSDK.NewClient(b, &http.Client{\n\t\tTransport: &cosSDK.AuthorizationTransport{\n\t\t\tSecretID:  accessKey,\n\t\t\tSecretKey: secretKey,\n\t\t},\n\t})\n\n\tif len(bucket) != 0 {\n\t\tu2, _ := url.Parse(fmt.Sprintf(\"https://%s.%s.%s.myqcloud.com\", bucket, endpointType, region))\n\t\tb2 := &cosSDK.BaseURL{BucketURL: u2}\n\t\tclientWithBucket := cosSDK.NewClient(b2, &http.Client{\n\t\t\tTransport: &cosSDK.AuthorizationTransport{\n\t\t\t\tSecretID:  accessKey,\n\t\t\t\tSecretKey: secretKey,\n\t\t\t},\n\t\t})\n\t\treturn &cosClient{client: client, clientWithBucket: clientWithBucket, scType: scType}, nil\n\t}\n\n\treturn &cosClient{client: client, clientWithBucket: nil, scType: scType}, nil\n}\n\nfunc (c cosClient) ListBuckets() ([]interface{}, error) {\n\tbuckets, _, err := c.client.Service.Get(context.Background())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []interface{}\n\tfor _, bucket := range buckets.Buckets {\n\t\tdatas = append(datas, bucket.Name)\n\t}\n\treturn datas, nil\n}\n\nfunc (c cosClient) Exist(path string) (bool, error) {\n\texist, err := c.clientWithBucket.Object.IsExist(context.Background(), path)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn exist, nil\n}\n\nfunc (c cosClient) Size(path string) (int64, error) {\n\tdata, _, err := c.clientWithBucket.Bucket.Get(context.Background(), &cosSDK.BucketGetOptions{Prefix: path})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif len(data.Contents) == 0 {\n\t\treturn 0, fmt.Errorf(\"no such file %s\", path)\n\t}\n\treturn data.Contents[0].Size, nil\n}\n\nfunc (c cosClient) Delete(path string) (bool, error) {\n\tif _, err := c.clientWithBucket.Object.Delete(context.Background(), path); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (c cosClient) Upload(src, target string) (bool, error) {\n\tfileInfo, err := os.Stat(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif fileInfo.Size() > 5368709120 {\n\t\topt := &cosSDK.MultiUploadOptions{\n\t\t\tOptIni: &cosSDK.InitiateMultipartUploadOptions{\n\t\t\t\tACLHeaderOptions: nil,\n\t\t\t\tObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{\n\t\t\t\t\tXCosStorageClass: c.scType,\n\t\t\t\t},\n\t\t\t},\n\t\t\tPartSize: 200,\n\t\t}\n\t\tif _, _, err := c.clientWithBucket.Object.MultiUpload(\n\t\t\tcontext.Background(), target, src, opt,\n\t\t); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\treturn true, nil\n\t}\n\tif _, err := c.clientWithBucket.Object.PutFromFile(context.Background(), target, src, &cosSDK.ObjectPutOptions{\n\t\tACLHeaderOptions: nil,\n\t\tObjectPutHeaderOptions: &cosSDK.ObjectPutHeaderOptions{\n\t\t\tXCosStorageClass: c.scType,\n\t\t},\n\t}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (c cosClient) Download(src, target string) (bool, error) {\n\tif _, err := c.clientWithBucket.Object.Download(context.Background(), src, target, &cosSDK.MultiDownloadOptions{}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (c cosClient) ListObjects(prefix string) ([]string, error) {\n\tdatas, _, err := c.clientWithBucket.Bucket.Get(context.Background(), &cosSDK.BucketGetOptions{Prefix: prefix})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar result []string\n\tfor _, item := range datas.Contents {\n\t\tresult = append(result, item.Key)\n\t}\n\treturn result, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/google_drive.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/go-resty/resty/v2\"\n)\n\ntype googleDriveClient struct {\n\taccessToken string\n}\n\nfunc NewGoogleDriveClient(vars map[string]interface{}) (*googleDriveClient, error) {\n\taccessToken, err := RefreshGoogleToken(\"refresh_token\", \"accessToken\", vars)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &googleDriveClient{accessToken: accessToken}, nil\n}\n\nfunc (g *googleDriveClient) ListBuckets() ([]interface{}, error) {\n\treturn nil, nil\n}\n\nfunc (g *googleDriveClient) Exist(pathItem string) (bool, error) {\n\tpathItem = path.Join(\"root\", pathItem)\n\tif _, err := g.loadFileWithName(pathItem); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (g *googleDriveClient) Size(pathItem string) (int64, error) {\n\tpathItem = path.Join(\"root\", pathItem)\n\tfileInfo, err := g.loadFileWithName(pathItem)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tsize, _ := strconv.ParseInt(fileInfo.Size, 10, 64)\n\treturn size, nil\n}\n\nfunc (g *googleDriveClient) Delete(pathItem string) (bool, error) {\n\tpathItem = path.Join(\"root\", pathItem)\n\tfileInfo, err := g.loadFileWithName(pathItem)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif len(fileInfo.ID) == 0 {\n\t\treturn false, fmt.Errorf(\"no such file %s\", pathItem)\n\t}\n\tif _, err = g.googleRequest(\"https://www.googleapis.com/drive/v3/files/\"+fileInfo.ID, http.MethodDelete, nil, nil); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (g *googleDriveClient) Upload(src, target string) (bool, error) {\n\ttarget = path.Join(\"/root\", target)\n\tparentID := \"root\"\n\tvar err error\n\tif path.Dir(target) != \"/root\" {\n\t\tparentID, err = g.mkdirWithPath(path.Dir(target))\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tfile, err := os.Open(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\tfileInfo, err := file.Stat()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tdata := map[string]interface{}{\n\t\t\"name\":    fileInfo.Name(),\n\t\t\"parents\": []string{parentID},\n\t}\n\turlItem := \"https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true\"\n\tclient := resty.New()\n\tresp, err := client.R().\n\t\tSetHeader(\"Authorization\", \"Bearer \"+g.accessToken).\n\t\tSetBody(data).\n\t\tPost(urlItem)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tuploadUrl := resp.Header().Get(\"location\")\n\n\tbuf := make([]byte, 5*1024*1024)\n\tvar offset int64 = 0\n\tfor {\n\t\tn, err := file.Read(buf)\n\t\tif err != nil && err != io.EOF {\n\t\t\treturn false, fmt.Errorf(\"read file failed, err: %v\", err)\n\t\t}\n\t\tif n == 0 {\n\t\t\tbreak\n\t\t}\n\t\tchunk := buf[:n]\n\t\tstart := offset\n\t\tend := offset + int64(n) - 1\n\t\tif err = uploadChunk(client, uploadUrl, chunk, start, end, fileInfo.Size()); err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\toffset += int64(n)\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn true, nil\n}\n\nfunc uploadChunk(client *resty.Client, uploadURL string, chunk []byte, start, end, total int64) error {\n\tcontentRange := fmt.Sprintf(\"bytes %d-%d/%d\", start, end, total)\n\n\tresp, err := client.R().\n\t\tSetHeader(\"Content-Length\", strconv.Itoa(len(chunk))).\n\t\tSetHeader(\"Content-Range\", contentRange).\n\t\tSetBody(chunk).\n\t\tPut(uploadURL)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"new request for upload chunk failed: %v\", err)\n\t}\n\n\tif resp.StatusCode() != 200 && resp.StatusCode() != 308 {\n\t\treturn fmt.Errorf(\"upload chunk failed: %s, %s\", resp.Status(), string(resp.Body()))\n\t}\n\n\treturn nil\n}\n\nfunc (g *googleDriveClient) Download(src, target string) (bool, error) {\n\tsrc = path.Join(\"/root\", src)\n\tfileInfo, err := g.loadFileWithName(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\turl := fmt.Sprintf(\"https://www.googleapis.com/drive/v3/files/%s?alt=media&acknowledgeAbuse=true\", fileInfo.ID)\n\tif err := g.handleDownload(url, target); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (g *googleDriveClient) ListObjects(src string) ([]string, error) {\n\tif len(src) == 0 || src == \"root\" || src == \"/root\" {\n\t\tsrc = \"root\"\n\t} else {\n\t\tsrc = path.Join(\"/root\", src)\n\t}\n\tfileInfos, err := g.loadDirWithPath(src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar names []string\n\tfor _, item := range fileInfos {\n\t\tnames = append(names, item.Name)\n\t}\n\treturn names, nil\n}\n\ntype googleFileResp struct {\n\tFiles []googleFile `json:\"files\"`\n}\ntype googleFile struct {\n\tID   string `json:\"id\"`\n\tName string `json:\"name\"`\n\tSize string `json:\"size\"`\n}\n\nfunc (g *googleDriveClient) mkdirWithPath(target string) (string, error) {\n\tpathItems := strings.Split(target, \"/\")\n\tvar (\n\t\tfileInfos []googleFile\n\t\terr       error\n\t)\n\tparentID := \"root\"\n\tfor i := 0; i < len(pathItems); i++ {\n\t\tif len(pathItems[i]) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfileInfos, err = g.loadFileWithParentID(parentID)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tisEnd := false\n\t\tif i == len(pathItems)-2 {\n\t\t\tisEnd = true\n\t\t}\n\t\texist := false\n\t\tfor _, item := range fileInfos {\n\t\t\tif item.Name == pathItems[i+1] {\n\t\t\t\tparentID = item.ID\n\t\t\t\tif isEnd {\n\t\t\t\t\treturn item.ID, nil\n\t\t\t\t} else {\n\t\t\t\t\texist = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !exist {\n\t\t\tparentID, err = g.mkdir(parentID, pathItems[i+1])\n\t\t\tif err != nil {\n\t\t\t\treturn parentID, err\n\t\t\t}\n\t\t\tif isEnd {\n\t\t\t\treturn parentID, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\", errors.New(\"mkdir failed.\")\n}\n\ntype googleMkdirRes struct {\n\tID string `json:\"id\"`\n}\n\nfunc (g *googleDriveClient) mkdir(parentID, name string) (string, error) {\n\tdata := map[string]interface{}{\n\t\t\"name\":     name,\n\t\t\"parents\":  []string{parentID},\n\t\t\"mimeType\": \"application/vnd.google-apps.folder\",\n\t}\n\tres, err := g.googleRequest(\"https://www.googleapis.com/drive/v3/files\", http.MethodPost, func(req *resty.Request) {\n\t\treq.SetBody(data)\n\t}, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar mkdirResp googleMkdirRes\n\tif err := json.Unmarshal(res, &mkdirResp); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn mkdirResp.ID, nil\n}\n\nfunc (g *googleDriveClient) handleDownload(urlItem string, target string) error {\n\treq, err := http.NewRequest(http.MethodGet, urlItem, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq.Header.Add(\"Authorization\", \"Bearer \"+g.accessToken)\n\tclient := &http.Client{}\n\tdefer client.CloseIdleConnections()\n\tresponse, err := client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer response.Body.Close()\n\tif response.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"handle download with url failed, code: %v\", response.StatusCode)\n\t}\n\tif _, err := os.Stat(path.Dir(target)); err != nil {\n\t\t_ = os.MkdirAll(path.Dir(target), os.ModePerm)\n\t}\n\tout, err := os.Create(target)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer out.Close()\n\tif _, err = io.Copy(out, response.Body); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (g *googleDriveClient) loadFileWithName(pathItem string) (googleFile, error) {\n\tpathItems := strings.Split(pathItem, \"/\")\n\tvar (\n\t\tfileInfos []googleFile\n\t\terr       error\n\t)\n\tparentID := \"root\"\n\tfor i := 0; i < len(pathItems); i++ {\n\t\tif len(pathItems[i]) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfileInfos, err = g.loadFileWithParentID(parentID)\n\t\tif err != nil {\n\t\t\treturn googleFile{}, err\n\t\t}\n\t\tisEnd := false\n\t\tif i == len(pathItems)-2 {\n\t\t\tisEnd = true\n\t\t}\n\t\texist := false\n\t\tfor _, item := range fileInfos {\n\t\t\tif item.Name == pathItems[i+1] {\n\t\t\t\tif isEnd {\n\t\t\t\t\treturn item, nil\n\t\t\t\t} else {\n\t\t\t\t\tparentID = item.ID\n\t\t\t\t\texist = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !exist {\n\t\t\treturn googleFile{}, errors.New(\"no such file or dir\")\n\t\t}\n\n\t}\n\treturn googleFile{}, errors.New(\"no such file or dir\")\n}\n\nfunc (g *googleDriveClient) loadFileWithParentID(parentID string) ([]googleFile, error) {\n\tquery := map[string]string{\n\t\t\"fields\": \"files(id,name,mimeType,size)\",\n\t\t\"q\":      fmt.Sprintf(\"'%s' in parents and trashed = false\", parentID),\n\t}\n\n\tres, err := g.googleRequest(\"https://www.googleapis.com/drive/v3/files\", http.MethodGet, func(req *resty.Request) {\n\t\treq.SetQueryParams(query)\n\t}, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar fileResp googleFileResp\n\tif err := json.Unmarshal(res, &fileResp); err != nil {\n\t\treturn nil, err\n\t}\n\treturn fileResp.Files, nil\n}\n\nfunc (g googleDriveClient) loadDirWithPath(path string) ([]googleFile, error) {\n\tpathItems := strings.Split(path, \"/\")\n\tvar (\n\t\tfileInfos []googleFile\n\t\terr       error\n\t)\n\tparentID := \"root\"\n\tfor i := 0; i < len(pathItems); i++ {\n\t\tif len(pathItems[i]) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfileInfos, err = g.loadFileWithParentID(parentID)\n\t\tif err != nil {\n\t\t\treturn fileInfos, err\n\t\t}\n\t\tif i == len(pathItems)-1 {\n\t\t\treturn fileInfos, nil\n\t\t}\n\t\texist := false\n\t\tfor _, item := range fileInfos {\n\t\t\tif item.Name == pathItems[i+1] {\n\t\t\t\tparentID = item.ID\n\t\t\t\texist = true\n\t\t\t}\n\t\t}\n\t\tif !exist {\n\t\t\treturn nil, errors.New(\"no such file or dir\")\n\t\t}\n\t}\n\treturn fileInfos, errors.New(\"no such file or dir\")\n}\n\ntype reqCallback func(req *resty.Request)\n\nfunc (g *googleDriveClient) googleRequest(urlItem, method string, callback reqCallback, resp interface{}) ([]byte, error) {\n\tclient := resty.New()\n\treq := client.R()\n\treq.SetHeader(\"Authorization\", \"Bearer \"+g.accessToken)\n\tif callback != nil {\n\t\tcallback(req)\n\t}\n\tif resp != nil {\n\t\treq.SetResult(req)\n\t}\n\tres, err := req.Execute(method, urlItem)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif res.StatusCode() == 401 {\n\t\t// refresh and retry\n\t\treturn nil, fmt.Errorf(\"request for %s failed, code: %v\", urlItem, res.StatusCode())\n\t}\n\tif res.StatusCode() > 300 {\n\t\treturn nil, fmt.Errorf(\"request for %s failed, err: %v\", urlItem, res.StatusCode())\n\t}\n\treturn res.Body(), nil\n}\n\ntype googleTokenRes struct {\n\tAccessToken  string `json:\"access_token\"`\n\tRefreshToken string `json:\"refresh_token\"`\n}\n\nfunc RefreshGoogleToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) {\n\tclient := resty.New()\n\tdata := map[string]interface{}{\n\t\t\"client_id\":     loadParamFromVars(\"client_id\", varMap),\n\t\t\"client_secret\": loadParamFromVars(\"client_secret\", varMap),\n\t\t\"redirect_uri\":  loadParamFromVars(\"redirect_uri\", varMap),\n\t}\n\tif grantType == \"refresh_token\" {\n\t\tdata[\"grant_type\"] = \"refresh_token\"\n\t\tdata[\"refresh_token\"] = loadParamFromVars(\"refresh_token\", varMap)\n\t} else {\n\t\tdata[\"grant_type\"] = \"authorization_code\"\n\t\tdata[\"code\"] = loadParamFromVars(\"code\", varMap)\n\t}\n\turlItem := \"https://www.googleapis.com/oauth2/v4/token\"\n\tresp, err := client.R().\n\t\tSetBody(data).\n\t\tPost(urlItem)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"load account token failed, err: %v\", err)\n\t}\n\n\tif resp.StatusCode() != 200 {\n\t\treturn \"\", fmt.Errorf(\"load account token failed, code: %v\", resp.StatusCode())\n\t}\n\tvar respItem googleTokenRes\n\tif err := json.Unmarshal(resp.Body(), &respItem); err != nil {\n\t\treturn \"\", err\n\t}\n\tif tokenType == \"accessToken\" {\n\t\treturn respItem.AccessToken, nil\n\t}\n\treturn respItem.RefreshToken, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/auth.go",
    "content": "package webdav\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n)\n\ntype AuthFactory func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error)\n\ntype Authorizer interface {\n\tNewAuthenticator(body io.Reader) (Authenticator, io.Reader)\n\tAddAuthenticator(key string, fn AuthFactory)\n}\n\ntype Authenticator interface {\n\tAuthorize(c *http.Client, rq *http.Request, path string) error\n\tVerify(c *http.Client, rs *http.Response, path string) (redo bool, err error)\n\tClone() Authenticator\n\tio.Closer\n}\n\ntype authfactory struct {\n\tkey    string\n\tcreate AuthFactory\n}\n\ntype authorizer struct {\n\tfactories  []authfactory\n\tdefAuthMux sync.Mutex\n\tdefAuth    Authenticator\n}\n\ntype authShim struct {\n\tfactory AuthFactory\n\tbody    io.Reader\n\tauth    Authenticator\n}\n\ntype negoAuth struct {\n\tauths                   []Authenticator\n\tsetDefaultAuthenticator func(auth Authenticator)\n}\n\ntype nullAuth struct{}\n\ntype noAuth struct{}\n\nfunc NewAutoAuth(login string, secret string) Authorizer {\n\tfmap := make([]authfactory, 0)\n\taz := &authorizer{factories: fmap, defAuthMux: sync.Mutex{}, defAuth: &nullAuth{}}\n\n\taz.AddAuthenticator(\"basic\", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {\n\t\treturn &BasicAuth{user: login, pw: secret}, nil\n\t})\n\n\taz.AddAuthenticator(\"digest\", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {\n\t\treturn NewDigestAuth(login, secret, rs)\n\t})\n\n\taz.AddAuthenticator(\"passport1.4\", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {\n\t\treturn NewPassportAuth(c, login, secret, rs.Request.URL.String(), &rs.Header)\n\t})\n\n\treturn az\n}\n\nfunc (a *authorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) {\n\tvar retryBuf io.Reader = body\n\tif body != nil {\n\t\tif _, ok := retryBuf.(io.Seeker); ok {\n\t\t\tbody = io.NopCloser(body)\n\t\t} else {\n\t\t\tbuff := &bytes.Buffer{}\n\t\t\tretryBuf = buff\n\t\t\tbody = io.TeeReader(body, buff)\n\t\t}\n\t}\n\ta.defAuthMux.Lock()\n\tdefAuth := a.defAuth.Clone()\n\ta.defAuthMux.Unlock()\n\n\treturn &authShim{factory: a.factory, body: retryBuf, auth: defAuth}, body\n}\n\nfunc (a *authorizer) AddAuthenticator(key string, fn AuthFactory) {\n\tkey = strings.ToLower(key)\n\tfor _, f := range a.factories {\n\t\tif f.key == key {\n\t\t\tpanic(\"Authenticator exists: \" + key)\n\t\t}\n\t}\n\ta.factories = append(a.factories, authfactory{key, fn})\n}\n\nfunc (a *authorizer) factory(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {\n\theaders := rs.Header.Values(\"Www-Authenticate\")\n\tif len(headers) > 0 {\n\t\tauths := make([]Authenticator, 0)\n\t\tfor _, f := range a.factories {\n\t\t\tfor _, header := range headers {\n\t\t\t\theaderLower := strings.ToLower(header)\n\t\t\t\tif strings.Contains(headerLower, f.key) {\n\t\t\t\t\trs.Header.Set(\"Www-Authenticate\", header)\n\t\t\t\t\tif auth, err = f.create(c, rs, path); err == nil {\n\t\t\t\t\t\tauths = append(auths, auth)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch len(auths) {\n\t\tcase 0:\n\t\t\treturn nil, NewPathError(\"NoAuthenticator\", path, rs.StatusCode)\n\t\tcase 1:\n\t\t\tauth = auths[0]\n\t\tdefault:\n\t\t\tauth = &negoAuth{auths: auths, setDefaultAuthenticator: a.setDefaultAuthenticator}\n\t\t}\n\t} else {\n\t\tauth = &noAuth{}\n\t}\n\n\ta.setDefaultAuthenticator(auth)\n\n\treturn auth, nil\n}\n\nfunc (a *authorizer) setDefaultAuthenticator(auth Authenticator) {\n\ta.defAuthMux.Lock()\n\ta.defAuth.Close()\n\ta.defAuth = auth\n\ta.defAuthMux.Unlock()\n}\n\nfunc (s *authShim) Authorize(c *http.Client, rq *http.Request, path string) error {\n\tif err := s.auth.Authorize(c, rq, path); err != nil {\n\t\treturn err\n\t}\n\tbody := s.body\n\trq.GetBody = func() (io.ReadCloser, error) {\n\t\tif body != nil {\n\t\t\tif sk, ok := body.(io.Seeker); ok {\n\t\t\t\tif _, err := sk.Seek(0, io.SeekStart); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn io.NopCloser(body), nil\n\t\t}\n\t\treturn nil, nil\n\t}\n\treturn nil\n}\n\nfunc (s *authShim) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {\n\tredo, err = s.auth.Verify(c, rs, path)\n\tif err != nil && errors.Is(err, ErrAuthChanged) {\n\t\tif auth, aerr := s.factory(c, rs, path); aerr == nil {\n\t\t\ts.auth.Close()\n\t\t\ts.auth = auth\n\t\t\treturn true, nil\n\t\t} else {\n\t\t\treturn false, aerr\n\t\t}\n\t}\n\treturn\n}\n\nfunc (s *authShim) Close() error {\n\ts.auth.Close()\n\ts.auth, s.factory = nil, nil\n\tif s.body != nil {\n\t\tif closer, ok := s.body.(io.Closer); ok {\n\t\t\treturn closer.Close()\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s *authShim) Clone() Authenticator {\n\treturn &noAuth{}\n}\n\nfunc (s *authShim) String() string {\n\treturn \"AuthShim\"\n}\n\nfunc (n *negoAuth) Authorize(c *http.Client, rq *http.Request, path string) error {\n\tif len(n.auths) == 0 {\n\t\treturn NewPathError(\"NoAuthenticator\", path, 400)\n\t}\n\treturn n.auths[0].Authorize(c, rq, path)\n}\n\nfunc (n *negoAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {\n\tif len(n.auths) == 0 {\n\t\treturn false, NewPathError(\"NoAuthenticator\", path, 400)\n\t}\n\tredo, err = n.auths[0].Verify(c, rs, path)\n\tif err != nil {\n\t\tif len(n.auths) > 1 {\n\t\t\tn.auths[0].Close()\n\t\t\tn.auths = n.auths[1:]\n\t\t\treturn true, nil\n\t\t}\n\t} else if redo {\n\t\treturn\n\t} else {\n\t\tauth := n.auths[0]\n\t\tn.auths = n.auths[1:]\n\t\tn.setDefaultAuthenticator(auth)\n\t\treturn\n\t}\n\n\treturn false, NewPathError(\"NoAuthenticator\", path, rs.StatusCode)\n}\n\nfunc (n *negoAuth) Close() error {\n\tfor _, a := range n.auths {\n\t\ta.Close()\n\t}\n\tn.setDefaultAuthenticator = nil\n\treturn nil\n}\n\nfunc (n *negoAuth) Clone() Authenticator {\n\tauths := make([]Authenticator, len(n.auths))\n\tfor i, e := range n.auths {\n\t\tauths[i] = e.Clone()\n\t}\n\treturn &negoAuth{auths: auths, setDefaultAuthenticator: n.setDefaultAuthenticator}\n}\n\nfunc (n *negoAuth) String() string {\n\treturn \"NegoAuth\"\n}\n\nfunc (n *noAuth) Authorize(c *http.Client, rq *http.Request, path string) error {\n\treturn nil\n}\n\nfunc (n *noAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {\n\tif \"\" != rs.Header.Get(\"Www-Authenticate\") {\n\t\terr = ErrAuthChanged\n\t}\n\treturn\n}\n\nfunc (n *noAuth) Close() error {\n\treturn nil\n}\n\nfunc (n *noAuth) Clone() Authenticator {\n\treturn n\n}\n\nfunc (n *noAuth) String() string {\n\treturn \"NoAuth\"\n}\n\nfunc (n *nullAuth) Authorize(c *http.Client, rq *http.Request, path string) error {\n\trq.Header.Set(XInhibitRedirect, \"1\")\n\treturn nil\n}\n\nfunc (n *nullAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {\n\treturn true, ErrAuthChanged\n}\n\nfunc (n *nullAuth) Close() error {\n\treturn nil\n}\n\nfunc (n *nullAuth) Clone() Authenticator {\n\treturn n\n}\n\nfunc (n *nullAuth) String() string {\n\treturn \"NullAuth\"\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/auth_basic.go",
    "content": "package webdav\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\ntype BasicAuth struct {\n\tuser string\n\tpw   string\n}\n\nfunc (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error {\n\trq.SetBasicAuth(b.user, b.pw)\n\treturn nil\n}\n\nfunc (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {\n\tif rs.StatusCode == 401 {\n\t\terr = NewPathError(\"Authorize\", path, rs.StatusCode)\n\t}\n\treturn\n}\n\nfunc (b *BasicAuth) Close() error {\n\treturn nil\n}\n\nfunc (b *BasicAuth) Clone() Authenticator {\n\t// no copy due to read only access\n\treturn b\n}\n\nfunc (b *BasicAuth) String() string {\n\treturn fmt.Sprintf(\"BasicAuth login: %s\", b.user)\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/auth_digest.go",
    "content": "package webdav\n\nimport (\n\t\"crypto/md5\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"net/http\"\n\t\"strings\"\n)\n\ntype DigestAuth struct {\n\tuser        string\n\tpw          string\n\tdigestParts map[string]string\n}\n\nfunc NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error) {\n\treturn &DigestAuth{user: login, pw: secret, digestParts: digestParts(rs)}, nil\n}\n\nfunc (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error {\n\td.digestParts[\"uri\"] = path\n\td.digestParts[\"method\"] = rq.Method\n\td.digestParts[\"username\"] = d.user\n\td.digestParts[\"password\"] = d.pw\n\trq.Header.Set(\"Authorization\", getDigestAuthorization(d.digestParts))\n\treturn nil\n}\n\nfunc (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {\n\tif rs.StatusCode == 401 {\n\t\tif isStaled(rs) {\n\t\t\tredo = true\n\t\t\terr = ErrAuthChanged\n\t\t} else {\n\t\t\terr = NewPathError(\"Authorize\", path, rs.StatusCode)\n\t\t}\n\t}\n\treturn\n}\n\nfunc (d *DigestAuth) Close() error {\n\treturn nil\n}\n\nfunc (d *DigestAuth) Clone() Authenticator {\n\tvar parts map[string]string\n\tif parts = maps.Clone(parts); parts == nil {\n\t\tparts = make(map[string]string)\n\t}\n\treturn &DigestAuth{user: d.user, pw: d.pw, digestParts: parts}\n}\n\nfunc (d *DigestAuth) String() string {\n\treturn fmt.Sprintf(\"DigestAuth login: %s\", d.user)\n}\n\nfunc digestParts(resp *http.Response) map[string]string {\n\tresult := map[string]string{}\n\tif len(resp.Header[\"Www-Authenticate\"]) > 0 {\n\t\twantedHeaders := []string{\"nonce\", \"realm\", \"qop\", \"opaque\", \"algorithm\", \"entityBody\"}\n\t\tresponseHeaders := strings.Split(resp.Header[\"Www-Authenticate\"][0], \",\")\n\t\tfor _, r := range responseHeaders {\n\t\t\tfor _, w := range wantedHeaders {\n\t\t\t\tif strings.Contains(r, w) {\n\t\t\t\t\tresult[w] = strings.Trim(\n\t\t\t\t\t\tstrings.SplitN(r, `=`, 2)[1],\n\t\t\t\t\t\t`\"`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n\nfunc getMD5(text string) string {\n\thasher := md5.New()\n\thasher.Write([]byte(text))\n\treturn hex.EncodeToString(hasher.Sum(nil))\n}\n\nfunc getCnonce() string {\n\tb := make([]byte, 8)\n\tio.ReadFull(rand.Reader, b)\n\treturn fmt.Sprintf(\"%x\", b)[:16]\n}\n\nfunc getDigestAuthorization(digestParts map[string]string) string {\n\td := digestParts\n\n\tvar (\n\t\tha1        string\n\t\tha2        string\n\t\tnonceCount = 00000001\n\t\tcnonce     = getCnonce()\n\t\tresponse   string\n\t)\n\n\tswitch d[\"algorithm\"] {\n\tcase \"MD5\", \"\":\n\t\tha1 = getMD5(d[\"username\"] + \":\" + d[\"realm\"] + \":\" + d[\"password\"])\n\tcase \"MD5-sess\":\n\t\tha1 = getMD5(\n\t\t\tfmt.Sprintf(\"%s:%v:%s\",\n\t\t\t\tgetMD5(d[\"username\"]+\":\"+d[\"realm\"]+\":\"+d[\"password\"]),\n\t\t\t\tnonceCount,\n\t\t\t\tcnonce,\n\t\t\t),\n\t\t)\n\t}\n\n\tswitch d[\"qop\"] {\n\tcase \"auth\", \"\":\n\t\tha2 = getMD5(d[\"method\"] + \":\" + d[\"uri\"])\n\tcase \"auth-int\":\n\t\tif d[\"entityBody\"] != \"\" {\n\t\t\tha2 = getMD5(d[\"method\"] + \":\" + d[\"uri\"] + \":\" + getMD5(d[\"entityBody\"]))\n\t\t}\n\t}\n\n\tswitch d[\"qop\"] {\n\tcase \"\":\n\t\tresponse = getMD5(\n\t\t\tfmt.Sprintf(\"%s:%s:%s\",\n\t\t\t\tha1,\n\t\t\t\td[\"nonce\"],\n\t\t\t\tha2,\n\t\t\t),\n\t\t)\n\tcase \"auth\", \"auth-int\":\n\t\tresponse = getMD5(\n\t\t\tfmt.Sprintf(\"%s:%s:%v:%s:%s:%s\",\n\t\t\t\tha1,\n\t\t\t\td[\"nonce\"],\n\t\t\t\tnonceCount,\n\t\t\t\tcnonce,\n\t\t\t\td[\"qop\"],\n\t\t\t\tha2,\n\t\t\t),\n\t\t)\n\t}\n\n\tauthorization := fmt.Sprintf(`Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=%v, cnonce=\"%s\", response=\"%s\"`,\n\t\td[\"username\"], d[\"realm\"], d[\"nonce\"], d[\"uri\"], nonceCount, cnonce, response)\n\n\tif d[\"qop\"] != \"\" {\n\t\tauthorization += fmt.Sprintf(`, qop=%s`, d[\"qop\"])\n\t}\n\n\tif d[\"opaque\"] != \"\" {\n\t\tauthorization += fmt.Sprintf(`, opaque=\"%s\"`, d[\"opaque\"])\n\t}\n\n\treturn authorization\n}\n\nfunc isStaled(rs *http.Response) bool {\n\theader := rs.Header.Get(\"Www-Authenticate\")\n\tif len(header) > 0 {\n\t\tdirectives := strings.Split(header, \",\")\n\t\tfor i := range directives {\n\t\t\tname, value, _ := strings.Cut(strings.Trim(directives[i], \" \"), \"=\")\n\t\t\tif strings.EqualFold(name, \"stale\") {\n\t\t\t\treturn strings.EqualFold(value, \"true\")\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/auth_passport.go",
    "content": "package webdav\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype PassportAuth struct {\n\tuser            string\n\tpw              string\n\tcookies         []http.Cookie\n\tinhibitRedirect bool\n}\n\nfunc NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error) {\n\tp := &PassportAuth{\n\t\tuser:            user,\n\t\tpw:              pw,\n\t\tinhibitRedirect: true,\n\t}\n\terr := p.genCookies(c, partnerURL, header)\n\treturn p, err\n}\n\nfunc (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error {\n\tif p.inhibitRedirect {\n\t\trq.Header.Set(XInhibitRedirect, \"1\")\n\t} else {\n\t\tp.inhibitRedirect = true\n\t}\n\tfor _, cookie := range p.cookies {\n\t\trq.AddCookie(&cookie)\n\t}\n\treturn nil\n}\n\nfunc (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {\n\tswitch rs.StatusCode {\n\tcase 301, 302, 307, 308:\n\t\tredo = true\n\t\tif rs.Header.Get(\"Www-Authenticate\") != \"\" {\n\t\t\terr = p.genCookies(c, rs.Request.URL.String(), &rs.Header)\n\t\t} else {\n\t\t\tp.inhibitRedirect = false\n\t\t}\n\tcase 401:\n\t\terr = NewPathError(\"Authorize\", path, rs.StatusCode)\n\t}\n\treturn\n}\n\nfunc (p *PassportAuth) Close() error {\n\treturn nil\n}\n\nfunc (p *PassportAuth) Clone() Authenticator {\n\tclonedCookies := make([]http.Cookie, len(p.cookies))\n\tcopy(clonedCookies, p.cookies)\n\n\treturn &PassportAuth{\n\t\tuser:            p.user,\n\t\tpw:              p.pw,\n\t\tcookies:         clonedCookies,\n\t\tinhibitRedirect: true,\n\t}\n}\n\nfunc (p *PassportAuth) String() string {\n\treturn fmt.Sprintf(\"PassportAuth login: %s\", p.user)\n}\n\nfunc (p *PassportAuth) genCookies(c *http.Client, partnerUrl string, header *http.Header) error {\n\tbaseAuthenticationServer := header.Get(\"Location\")\n\tbaseAuthenticationServerURL, err := url.Parse(baseAuthenticationServer)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tauthenticationServerUrl := url.URL{\n\t\tScheme: baseAuthenticationServerURL.Scheme,\n\t\tHost:   baseAuthenticationServerURL.Host,\n\t\tPath:   \"/login2.srf\",\n\t}\n\n\tpartnerServerChallenge := strings.Split(header.Get(\"Www-Authenticate\"), \" \")[1]\n\n\treq := http.Request{\n\t\tMethod: \"GET\",\n\t\tURL:    &authenticationServerUrl,\n\t\tHeader: http.Header{\n\t\t\t\"Authorization\": []string{\"Passport1.4 sign-in=\" + url.QueryEscape(p.user) + \",pwd=\" + url.QueryEscape(p.pw) + \",OrgVerb=GET,OrgUrl=\" + partnerUrl + \",\" + partnerServerChallenge},\n\t\t},\n\t}\n\n\trs, err := c.Do(&req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tio.Copy(io.Discard, rs.Body)\n\trs.Body.Close()\n\tif rs.StatusCode != 200 {\n\t\treturn NewPathError(\"Authorize\", \"/\", rs.StatusCode)\n\t}\n\n\ttokenResponseHeader := rs.Header.Get(\"Authentication-Info\")\n\tif tokenResponseHeader == \"\" {\n\t\treturn NewPathError(\"Authorize\", \"/\", 401)\n\t}\n\ttokenResponseHeaderList := strings.Split(tokenResponseHeader, \",\")\n\ttoken := \"\"\n\tfor _, tokenResponseHeader := range tokenResponseHeaderList {\n\t\tif strings.HasPrefix(tokenResponseHeader, \"from-PP='\") {\n\t\t\ttoken = tokenResponseHeader\n\t\t\tbreak\n\t\t}\n\t}\n\tif token == \"\" {\n\t\treturn NewPathError(\"Authorize\", \"/\", 401)\n\t}\n\n\torigUrl, err := url.Parse(partnerUrl)\n\tif err != nil {\n\t\treturn err\n\t}\n\treq = http.Request{\n\t\tMethod: \"GET\",\n\t\tURL:    origUrl,\n\t\tHeader: http.Header{\n\t\t\t\"Authorization\": []string{\"Passport1.4 \" + token},\n\t\t},\n\t}\n\n\trs, err = c.Do(&req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tio.Copy(io.Discard, rs.Body)\n\trs.Body.Close()\n\tif rs.StatusCode != 200 && rs.StatusCode != 302 {\n\t\treturn NewPathError(\"Authorize\", \"/\", rs.StatusCode)\n\t}\n\n\tcookies := rs.Header.Values(\"Set-Cookie\")\n\tp.cookies = make([]http.Cookie, len(cookies))\n\tfor i, cookie := range cookies {\n\t\tcookieParts := strings.Split(cookie, \";\")\n\t\tcookieName := strings.Split(cookieParts[0], \"=\")[0]\n\t\tcookieValue := strings.Split(cookieParts[0], \"=\")[1]\n\n\t\tp.cookies[i] = http.Cookie{\n\t\t\tName:  cookieName,\n\t\t\tValue: cookieValue,\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/errors.go",
    "content": "package webdav\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n)\n\nvar ErrAuthChanged = errors.New(\"authentication failed, change algorithm\")\n\nvar ErrTooManyRedirects = errors.New(\"stopped after 10 redirects\")\n\ntype StatusError struct {\n\tStatus int\n}\n\nfunc (se StatusError) Error() string {\n\treturn fmt.Sprintf(\"%d\", se.Status)\n}\n\nfunc NewPathError(op string, path string, statusCode int) error {\n\treturn &os.PathError{\n\t\tOp:   op,\n\t\tPath: path,\n\t\tErr:  StatusError{statusCode},\n\t}\n}\n\nfunc NewPathErrorErr(op string, path string, err error) error {\n\treturn &os.PathError{\n\t\tOp:   op,\n\t\tPath: path,\n\t\tErr:  err,\n\t}\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/file.go",
    "content": "package webdav\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n)\n\ntype File struct {\n\tpath        string\n\tname        string\n\tcontentType string\n\tsize        int64\n\tmodified    time.Time\n\tetag        string\n\tisdir       bool\n}\n\nfunc (f File) Name() string {\n\treturn f.name\n}\n\nfunc (f File) ContentType() string {\n\treturn f.contentType\n}\n\nfunc (f File) Size() int64 {\n\treturn f.size\n}\n\nfunc (f File) Mode() os.FileMode {\n\tif f.isdir {\n\t\treturn 0775 | os.ModeDir\n\t}\n\n\treturn 0664\n}\n\nfunc (f File) ModTime() time.Time {\n\treturn f.modified\n}\n\nfunc (f File) ETag() string {\n\treturn f.etag\n}\n\nfunc (f File) IsDir() bool {\n\treturn f.isdir\n}\n\nfunc (f File) Sys() interface{} {\n\treturn nil\n}\n\nfunc (f File) String() string {\n\tif f.isdir {\n\t\treturn fmt.Sprintf(\"Dir : '%s' - '%s'\", f.path, f.name)\n\t}\n\n\treturn fmt.Sprintf(\"File: '%s' SIZE: %d MODIFIED: %s ETAG: %s CTYPE: %s\", f.path, f.size, f.modified.String(), f.etag, f.contentType)\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/reques.go",
    "content": "package webdav\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (rs *http.Response, err error) {\n\tvar redo bool\n\tvar r *http.Request\n\tvar uri = PathEscape(Join(c.root, path))\n\tauth, body := c.auth.NewAuthenticator(body)\n\tdefer auth.Close()\n\n\tfor {\n\t\tif r, err = http.NewRequest(method, uri, body); err != nil {\n\t\t\terr = fmt.Errorf(\"handle request with uri: %s, method: %s failed, err: %v\", uri, method, err)\n\t\t\treturn\n\t\t}\n\n\t\tfor k, vals := range c.headers {\n\t\t\tfor _, v := range vals {\n\t\t\t\tr.Header.Add(k, v)\n\t\t\t}\n\t\t}\n\n\t\tif err = auth.Authorize(c.c, r, path); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif intercept != nil {\n\t\t\tintercept(r)\n\t\t}\n\n\t\tif rs, err = c.c.Do(r); err != nil {\n\t\t\terr = fmt.Errorf(\"do request for resp with uri: %s, method: %s failed, err: %v\", uri, method, err)\n\t\t\treturn\n\t\t}\n\n\t\tif redo, err = auth.Verify(c.c, rs, path); err != nil {\n\t\t\trs.Body.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tif redo {\n\t\t\trs.Body.Close()\n\t\t\tif body, err = r.GetBody(); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\n\treturn rs, err\n}\n\nfunc (c *Client) propfind(path string, self bool, body string, resp interface{}, parse func(resp interface{}) error) error {\n\trs, err := c.req(\"PROPFIND\", path, strings.NewReader(body), func(rq *http.Request) {\n\t\tif self {\n\t\t\trq.Header.Add(\"Depth\", \"0\")\n\t\t} else {\n\t\t\trq.Header.Add(\"Depth\", \"1\")\n\t\t}\n\t\trq.Header.Add(\"Content-Type\", \"application/xml;charset=UTF-8\")\n\t\trq.Header.Add(\"Accept\", \"application/xml,text/xml\")\n\t\trq.Header.Add(\"Accept-Charset\", \"utf-8\")\n\t\t// TODO add support for 'gzip,deflate;q=0.8,q=0.7'\n\t\trq.Header.Add(\"Accept-Encoding\", \"\")\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rs.Body.Close()\n\n\tif rs.StatusCode != 207 {\n\t\treturn NewPathError(\"PROPFIND\", path, rs.StatusCode)\n\t}\n\n\treturn parseXML(rs.Body, resp, parse)\n}\n\nfunc (c *Client) put(path string, stream io.Reader, contentLength int64) (status int, err error) {\n\trs, err := c.req(\"PUT\", path, stream, func(r *http.Request) {\n\t\tr.ContentLength = contentLength\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer rs.Body.Close()\n\n\tstatus = rs.StatusCode\n\treturn\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/utils.go",
    "content": "package webdav\n\nimport (\n\t\"encoding/xml\"\n\t\"io\"\n\t\"net/url\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc PathEscape(path string) string {\n\ts := strings.Split(path, \"/\")\n\tfor i, e := range s {\n\t\ts[i] = url.PathEscape(e)\n\t}\n\treturn strings.Join(s, \"/\")\n}\n\nfunc FixSlash(s string) string {\n\tif !strings.HasSuffix(s, \"/\") {\n\t\ts += \"/\"\n\t}\n\treturn s\n}\n\nfunc SplitPathToHierarchy(fullPath string) []string {\n\tcleanPath := path.Clean(fullPath)\n\tparts := strings.Split(cleanPath, \"/\")\n\n\tvar result []string\n\tcurrentPath := \"\"\n\n\tfor _, part := range parts {\n\t\tif part == \"\" {\n\t\t\tcurrentPath = \"/\"\n\t\t\tresult = append(result, currentPath)\n\t\t\tcontinue\n\t\t}\n\n\t\tif currentPath == \"/\" {\n\t\t\tcurrentPath = path.Join(currentPath, part)\n\t\t} else {\n\t\t\tcurrentPath = path.Join(currentPath, part)\n\t\t}\n\n\t\tresult = append(result, currentPath)\n\t}\n\n\treturn result\n}\n\nfunc FixSlashes(s string) string {\n\tif !strings.HasPrefix(s, \"/\") {\n\t\ts = \"/\" + s\n\t}\n\n\treturn FixSlash(s)\n}\n\nfunc Join(path0 string, path1 string) string {\n\treturn strings.TrimSuffix(path0, \"/\") + \"/\" + strings.TrimPrefix(path1, \"/\")\n}\n\nfunc parseInt64(s *string) int64 {\n\tif n, e := strconv.ParseInt(*s, 10, 64); e == nil {\n\t\treturn n\n\t}\n\treturn 0\n}\n\nfunc parseXML(data io.Reader, resp interface{}, parse func(resp interface{}) error) error {\n\tdecoder := xml.NewDecoder(data)\n\tfor t, _ := decoder.Token(); t != nil; t, _ = decoder.Token() {\n\t\tswitch se := t.(type) {\n\t\tcase xml.StartElement:\n\t\t\tif se.Name.Local == \"response\" {\n\t\t\t\tif e := decoder.DecodeElement(resp, &se); e == nil {\n\t\t\t\t\tif err := parse(resp); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper/webdav/webdav.go",
    "content": "package webdav\n\nimport (\n\t\"bytes\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nconst XInhibitRedirect = \"X-Gowebdav-Inhibit-Redirect\"\nconst template = `<d:propfind xmlns:d='DAV:'>\n<d:prop>\n\t<d:displayname/>\n\t<d:resourcetype/>\n\t<d:getcontentlength/>\n</d:prop>\n</d:propfind>`\n\ntype Client struct {\n\troot    string\n\theaders http.Header\n\tc       *http.Client\n\tauth    Authorizer\n}\n\nfunc NewClient(uri, user, pw string) *Client {\n\treturn NewAuthClient(uri, NewAutoAuth(user, pw))\n}\n\nfunc NewAuthClient(uri string, auth Authorizer) *Client {\n\tc := &http.Client{\n\t\tCheckRedirect: func(rq *http.Request, via []*http.Request) error {\n\t\t\tif len(via) >= 10 {\n\t\t\t\treturn ErrTooManyRedirects\n\t\t\t}\n\t\t\tif via[0].Header.Get(XInhibitRedirect) != \"\" {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\treturn &Client{root: FixSlash(uri), headers: make(http.Header), c: c, auth: auth}\n}\n\nfunc (c *Client) SetTransport(transport http.RoundTripper) {\n\tc.c.Transport = transport\n}\n\nfunc (c *Client) Connect() error {\n\trs, err := c.req(\"OPTIONS\", \"/\", nil, func(rq *http.Request) { rq.Header.Add(\"Depth\", \"0\") })\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rs.Body.Close()\n\n\tif rs.StatusCode != 200 && rs.StatusCode != 204 {\n\t\treturn fmt.Errorf(\"check conn failed, code: %d, err: %v\", rs.StatusCode, rs.Status)\n\t}\n\n\treturn nil\n}\n\ntype props struct {\n\tStatus string   `xml:\"DAV: status\"`\n\tName   string   `xml:\"DAV: prop>displayname,omitempty\"`\n\tType   xml.Name `xml:\"DAV: prop>resourcetype>collection,omitempty\"`\n\tSize   string   `xml:\"DAV: prop>getcontentlength,omitempty\"`\n}\n\ntype response struct {\n\tHref  string  `xml:\"DAV: href\"`\n\tProps []props `xml:\"DAV: propstat\"`\n}\n\nfunc getProps(r *response, status string) *props {\n\tfor _, prop := range r.Props {\n\t\tif strings.Contains(prop.Status, status) {\n\t\t\treturn &prop\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *Client) ReadDir(path string) ([]os.FileInfo, error) {\n\tpath = FixSlashes(path)\n\tfiles := make([]os.FileInfo, 0)\n\tskipSelf := true\n\tparse := func(resp interface{}) error {\n\t\tr := resp.(*response)\n\n\t\tif skipSelf {\n\t\t\tskipSelf = false\n\t\t\tif p := getProps(r, \"200\"); p != nil && p.Type.Local == \"collection\" {\n\t\t\t\tr.Props = nil\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn NewPathError(\"ReadDir\", path, 405)\n\t\t}\n\n\t\tif p := getProps(r, \"200\"); p != nil {\n\t\t\tf := new(File)\n\t\t\tif ps, err := url.PathUnescape(r.Href); err == nil {\n\t\t\t\tf.name = filepath.Base(ps)\n\t\t\t} else {\n\t\t\t\tf.name = p.Name\n\t\t\t}\n\t\t\tf.path = path + f.name\n\t\t\tif p.Type.Local == \"collection\" {\n\t\t\t\tf.path += \"/\"\n\t\t\t\tf.size = 0\n\t\t\t\tf.isdir = true\n\t\t\t} else {\n\t\t\t\tf.size = parseInt64(&p.Size)\n\t\t\t\tf.isdir = false\n\t\t\t}\n\n\t\t\tfiles = append(files, *f)\n\t\t}\n\n\t\tr.Props = nil\n\t\treturn nil\n\t}\n\n\tif err := c.propfind(path, false, template, &response{}, parse); err != nil {\n\t\tif _, ok := err.(*os.PathError); !ok {\n\t\t\treturn files, fmt.Errorf(\"load files from %s failed, err: %v\", path, err)\n\t\t}\n\t}\n\treturn files, nil\n}\n\nfunc (c *Client) Stat(path string) (os.FileInfo, error) {\n\tvar f *File\n\tparse := func(resp interface{}) error {\n\t\tr := resp.(*response)\n\t\tif p := getProps(r, \"200\"); p != nil && f == nil {\n\t\t\tf = new(File)\n\t\t\tf.name = p.Name\n\t\t\tf.path = path\n\n\t\t\tif p.Type.Local == \"collection\" {\n\t\t\t\tif !strings.HasSuffix(f.path, \"/\") {\n\t\t\t\t\tf.path += \"/\"\n\t\t\t\t}\n\t\t\t\tf.size = 0\n\t\t\t\tf.isdir = true\n\t\t\t} else {\n\t\t\t\tf.size = parseInt64(&p.Size)\n\t\t\t\tf.isdir = false\n\t\t\t}\n\t\t}\n\n\t\tr.Props = nil\n\t\treturn nil\n\t}\n\n\tif err := c.propfind(path, true, template, &response{}, parse); err != nil {\n\t\tif _, ok := err.(*os.PathError); !ok {\n\t\t\treturn f, fmt.Errorf(\"load file %s failed, path err: %v\", path, err)\n\t\t}\n\t\treturn f, fmt.Errorf(\"load file %s failed, err: %v\", path, err)\n\t}\n\treturn f, nil\n}\n\nfunc (c *Client) RemoveAll(path string) error {\n\trs, err := c.req(\"DELETE\", path, nil, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"handle remove file %s failed, err: %s\", path, err)\n\t}\n\tdefer rs.Body.Close()\n\tif rs.StatusCode == 200 || rs.StatusCode == 204 || rs.StatusCode == 404 {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"handle remove file %s failed, code: %d, err: %s\", path, rs.StatusCode, rs.Status)\n}\n\nfunc (c *Client) MkdirAll(path string, _ os.FileMode) (err error) {\n\tparentPath := filepath.Dir(path)\n\tif parentPath == \".\" || parentPath == \"/\" {\n\t\treturn nil\n\t}\n\n\tpaths := SplitPathToHierarchy(parentPath)\n\tfor _, item := range paths {\n\t\titemFile, err := c.Stat(item)\n\t\tif err == nil && itemFile.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\trs, err := c.req(\"MKCOL\", item, nil, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", item, err)\n\t\t}\n\t\tdefer rs.Body.Close()\n\t\tif rs.StatusCode != 201 && rs.StatusCode != 200 {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, code: %d,  err: %v\", item, rs.StatusCode, rs.Status)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *Client) ReadStream(path string) (io.ReadCloser, error) {\n\trs, err := c.req(\"GET\", path, nil, nil)\n\tif err != nil {\n\t\treturn nil, NewPathErrorErr(\"ReadStream\", path, err)\n\t}\n\n\tif rs.StatusCode == 200 {\n\t\treturn rs.Body, nil\n\t}\n\n\trs.Body.Close()\n\treturn nil, NewPathError(\"ReadStream\", path, rs.StatusCode)\n}\n\nfunc (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err error) {\n\terr = c.MkdirAll(path, 0755)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcontentLength := int64(0)\n\tif seeker, ok := stream.(io.Seeker); ok {\n\t\tcontentLength, err = seeker.Seek(0, io.SeekEnd)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t_, err = seeker.Seek(0, io.SeekStart)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tbuffer := bytes.NewBuffer(make([]byte, 0, 1024*1024 /* 1MB */))\n\n\t\tcontentLength, err = io.Copy(buffer, stream)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstream = buffer\n\t}\n\n\ts, err := c.put(path, stream, contentLength)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch s {\n\tcase 200, 201, 204:\n\t\treturn nil\n\n\tdefault:\n\t\treturn NewPathError(\"WriteStream\", path, s)\n\t}\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/helper.go",
    "content": "package client\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\nfunc loadParamFromVars(key string, vars map[string]interface{}) string {\n\tif _, ok := vars[key]; !ok {\n\t\tif key != \"bucket\" && key != \"port\" && key != \"authMode\" && key != \"passPhrase\" {\n\t\t\tglobal.LOG.Errorf(\"load param %s from vars failed, err: not exist!\", key)\n\t\t}\n\t\treturn \"\"\n\t}\n\n\treturn fmt.Sprintf(\"%v\", vars[key])\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/kodo.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/qiniu/go-sdk/v7/auth\"\n\t\"github.com/qiniu/go-sdk/v7/storage\"\n)\n\ntype kodoClient struct {\n\tbucket  string\n\tdomain  string\n\ttimeout string\n\tauth    *auth.Credentials\n\tclient  *storage.BucketManager\n}\n\nfunc NewKodoClient(vars map[string]interface{}) (*kodoClient, error) {\n\taccessKey := loadParamFromVars(\"accessKey\", vars)\n\tsecretKey := loadParamFromVars(\"secretKey\", vars)\n\tbucket := loadParamFromVars(\"bucket\", vars)\n\tdomain := loadParamFromVars(\"domain\", vars)\n\ttimeout := loadParamFromVars(\"timeout\", vars)\n\tif timeout == \"\" {\n\t\ttimeout = \"1\"\n\t}\n\tconn := auth.New(accessKey, secretKey)\n\tcfg := storage.Config{\n\t\tUseHTTPS: false,\n\t}\n\tbucketManager := storage.NewBucketManager(conn, &cfg)\n\n\treturn &kodoClient{client: bucketManager, auth: conn, bucket: bucket, domain: domain, timeout: timeout}, nil\n}\n\nfunc (k kodoClient) ListBuckets() ([]interface{}, error) {\n\tbuckets, err := k.client.Buckets(true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []interface{}\n\tfor _, bucket := range buckets {\n\t\tdatas = append(datas, bucket)\n\t}\n\treturn datas, nil\n}\n\nfunc (k kodoClient) Exist(path string) (bool, error) {\n\tif _, err := k.client.Stat(k.bucket, path); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (k kodoClient) Size(path string) (int64, error) {\n\tfile, err := k.client.Stat(k.bucket, path)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn file.Fsize, nil\n}\n\nfunc (k kodoClient) Delete(path string) (bool, error) {\n\tif err := k.client.Delete(k.bucket, path); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (k kodoClient) Upload(src, target string) (bool, error) {\n\n\tint64Value, _ := strconv.ParseInt(k.timeout, 10, 64)\n\tunixTimestamp := int64Value * 3600\n\n\tputPolicy := storage.PutPolicy{\n\t\tScope:   k.bucket,\n\t\tExpires: uint64(unixTimestamp),\n\t}\n\tupToken := putPolicy.UploadToken(k.auth)\n\tcfg := storage.Config{UseHTTPS: true, UseCdnDomains: false}\n\tresumeUploader := storage.NewResumeUploaderV2(&cfg)\n\tret := storage.PutRet{}\n\tputExtra := storage.RputV2Extra{}\n\tif err := resumeUploader.PutFile(context.Background(), &ret, upToken, target, src, &putExtra); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (k kodoClient) Download(src, target string) (bool, error) {\n\tdeadline := time.Now().Add(time.Second * 3600).Unix()\n\tprivateAccessURL := storage.MakePrivateURL(k.auth, k.domain, src, deadline)\n\n\tfo := files.NewFileOp()\n\tif err := fo.DownloadFile(privateAccessURL, target); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (k kodoClient) ListObjects(prefix string) ([]string, error) {\n\tvar result []string\n\tmarker := \"\"\n\tfor {\n\t\tentries, _, nextMarker, hashNext, err := k.client.ListFiles(k.bucket, prefix, \"\", marker, 1000)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, entry := range entries {\n\t\t\tresult = append(result, entry.Key)\n\t\t}\n\t\tif hashNext {\n\t\t\tmarker = nextMarker\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn result, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/local.go",
    "content": "package client\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\ntype localClient struct{}\n\nfunc NewLocalClient(vars map[string]interface{}) (*localClient, error) {\n\treturn &localClient{}, nil\n}\n\nfunc (c localClient) ListBuckets() ([]interface{}, error) {\n\treturn nil, nil\n}\n\nfunc (c localClient) Exist(file string) (bool, error) {\n\t_, err := os.Stat(file)\n\treturn err == nil, err\n}\n\nfunc (c localClient) Size(file string) (int64, error) {\n\tfileInfo, err := os.Stat(file)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn fileInfo.Size(), nil\n}\n\nfunc (c localClient) Delete(file string) (bool, error) {\n\tif err := os.RemoveAll(file); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (c localClient) Upload(src, target string) (bool, error) {\n\tfileOp := files.NewFileOp()\n\tif _, err := os.Stat(path.Dir(target)); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\tif err = os.MkdirAll(path.Dir(target), os.ModePerm); err != nil {\n\t\t\t\treturn false, err\n\t\t\t}\n\t\t} else {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif err := fileOp.CopyAndReName(src, target, \"\", true); err != nil {\n\t\treturn false, fmt.Errorf(\"cp file failed, err: %v\", err)\n\t}\n\treturn true, nil\n}\n\nfunc (c localClient) Download(src, target string) (bool, error) {\n\tfileOp := files.NewFileOp()\n\tif _, err := os.Stat(path.Dir(target)); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\tif err = os.MkdirAll(path.Dir(target), os.ModePerm); err != nil {\n\t\t\t\treturn false, err\n\t\t\t}\n\t\t} else {\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif err := fileOp.CopyAndReName(src, target, \"\", true); err != nil {\n\t\treturn false, fmt.Errorf(\"cp file failed, err: %v\", err)\n\t}\n\treturn true, nil\n}\n\nfunc (c localClient) ListObjects(prefix string) ([]string, error) {\n\tvar files []string\n\tif _, err := os.Stat(prefix); err != nil {\n\t\treturn files, nil\n\t}\n\tlist, err := os.ReadDir(prefix)\n\tif err != nil {\n\t\treturn files, err\n\t}\n\tfor i := 0; i < len(list); i++ {\n\t\tif !list[i].IsDir() {\n\t\t\tfiles = append(files, list[i].Name())\n\t\t}\n\t}\n\n\treturn files, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/minio.go",
    "content": "package client\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/minio/minio-go/v7\"\n\t\"github.com/minio/minio-go/v7/pkg/credentials\"\n)\n\ntype minIoClient struct {\n\tbucket string\n\tclient *minio.Client\n}\n\nfunc NewMinIoClient(vars map[string]interface{}) (*minIoClient, error) {\n\tendpoint := loadParamFromVars(\"endpoint\", vars)\n\taccessKeyID := loadParamFromVars(\"accessKey\", vars)\n\tsecretAccessKey := loadParamFromVars(\"secretKey\", vars)\n\tbucket := loadParamFromVars(\"bucket\", vars)\n\tssl := strings.Split(endpoint, \":\")[0]\n\tif len(ssl) == 0 || (ssl != \"https\" && ssl != \"http\") {\n\t\treturn nil, buserr.WithDetail(\"ErrInvalidParams\", \"ssl\", fmt.Errorf(\"no such proto in ssl: %s\", ssl))\n\t}\n\n\tsecure := false\n\ttlsConfig := &tls.Config{}\n\tif ssl == \"https\" {\n\t\tsecure = true\n\t\ttlsConfig.InsecureSkipVerify = true\n\t}\n\tvar transport http.RoundTripper = &http.Transport{\n\t\tTLSClientConfig: tlsConfig,\n\t}\n\tclient, err := minio.New(strings.ReplaceAll(endpoint, ssl+\"://\", \"\"), &minio.Options{\n\t\tCreds:     credentials.NewStaticV4(accessKeyID, secretAccessKey, \"\"),\n\t\tSecure:    secure,\n\t\tTransport: transport,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &minIoClient{bucket: bucket, client: client}, nil\n}\n\nfunc (m minIoClient) ListBuckets() ([]interface{}, error) {\n\tbuckets, err := m.client.ListBuckets(context.Background())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar result []interface{}\n\tfor _, bucket := range buckets {\n\t\tresult = append(result, bucket.Name)\n\t}\n\treturn result, err\n}\n\nfunc (m minIoClient) Exist(path string) (bool, error) {\n\tif _, err := m.client.GetObject(context.Background(), m.bucket, path, minio.GetObjectOptions{}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (m minIoClient) Size(path string) (int64, error) {\n\tobj, err := m.client.GetObject(context.Background(), m.bucket, path, minio.GetObjectOptions{})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tfile, err := obj.Stat()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn file.Size, nil\n}\n\nfunc (m minIoClient) Delete(path string) (bool, error) {\n\tobject, err := m.client.GetObject(context.Background(), m.bucket, path, minio.GetObjectOptions{})\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tinfo, err := object.Stat()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif err = m.client.RemoveObject(context.Background(), m.bucket, path, minio.RemoveObjectOptions{\n\t\tGovernanceBypass: true,\n\t\tVersionID:        info.VersionID,\n\t}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (m minIoClient) Upload(src, target string) (bool, error) {\n\tfile, err := os.Open(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\n\tfileStat, err := file.Stat()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\t_, err = m.client.PutObject(context.Background(), m.bucket, target, file, fileStat.Size(), minio.PutObjectOptions{ContentType: \"application/octet-stream\"})\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (m minIoClient) Download(src, target string) (bool, error) {\n\tobject, err := m.client.GetObject(context.Background(), m.bucket, src, minio.GetObjectOptions{})\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer object.Close()\n\tlocalFile, err := os.Create(target)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer localFile.Close()\n\tif _, err = io.Copy(localFile, object); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (m minIoClient) ListObjects(prefix string) ([]string, error) {\n\topts := minio.ListObjectsOptions{\n\t\tRecursive: true,\n\t\tPrefix:    prefix,\n\t}\n\n\tvar result []string\n\tfor object := range m.client.ListObjects(context.Background(), m.bucket, opts) {\n\t\tif object.Err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tresult = append(result, object.Key)\n\t}\n\treturn result, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/onedrive.go",
    "content": "package client\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\todsdk \"github.com/goh-chunlin/go-onedrive/onedrive\"\n\t\"golang.org/x/oauth2\"\n)\n\ntype oneDriveClient struct {\n\tclient odsdk.Client\n}\n\nfunc NewOneDriveClient(vars map[string]interface{}) (*oneDriveClient, error) {\n\ttoken, err := RefreshToken(\"refresh_token\", \"accessToken\", vars)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tisCN := loadParamFromVars(\"isCN\", vars)\n\tctx := context.Background()\n\tts := oauth2.StaticTokenSource(\n\t\t&oauth2.Token{AccessToken: token},\n\t)\n\ttc := oauth2.NewClient(ctx, ts)\n\n\tclient := odsdk.NewClient(tc)\n\tif isCN == \"true\" {\n\t\tclient.BaseURL, _ = url.Parse(\"https://microsoftgraph.chinacloudapi.cn/v1.0/\")\n\t}\n\treturn &oneDriveClient{client: *client}, nil\n}\n\nfunc (o oneDriveClient) ListBuckets() ([]interface{}, error) {\n\treturn nil, nil\n}\n\nfunc (o oneDriveClient) Exist(path string) (bool, error) {\n\tpath = \"/\" + strings.TrimPrefix(path, \"/\")\n\tfileID, err := o.loadIDByPath(path)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn len(fileID) != 0, nil\n}\n\nfunc (o oneDriveClient) Size(path string) (int64, error) {\n\tpath = \"/\" + strings.TrimPrefix(path, \"/\")\n\tpathItem := \"root:\" + path\n\tif path == \"/\" {\n\t\tpathItem = \"root\"\n\t}\n\treq, err := o.client.NewRequest(\"GET\", fmt.Sprintf(\"me/drive/%s\", pathItem), nil)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"new request for file id failed, err: %v\", err)\n\t}\n\tvar driveItem myDriverItem\n\tif err := o.client.Do(context.Background(), req, false, &driveItem); err != nil {\n\t\treturn 0, fmt.Errorf(\"do request for file id failed, err: %v\", err)\n\t}\n\n\treturn driveItem.Size, nil\n}\n\ntype myDriverItem struct {\n\tName string `json:\"name\"`\n\tId   string `json:\"id\"`\n\tSize int64  `json:\"size\"`\n}\n\nfunc (o oneDriveClient) Delete(path string) (bool, error) {\n\tpath = \"/\" + strings.TrimPrefix(path, \"/\")\n\treq, err := o.client.NewRequest(\"DELETE\", fmt.Sprintf(\"me/drive/root:%s\", path), nil)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"new request for delete file failed, err: %v \\n\", err)\n\t}\n\tif err := o.client.Do(context.Background(), req, false, nil); err != nil {\n\t\treturn false, fmt.Errorf(\"do request for delete file failed, err: %v \\n\", err)\n\t}\n\n\treturn true, nil\n}\n\nfunc (o oneDriveClient) Upload(src, target string) (bool, error) {\n\ttarget = \"/\" + strings.TrimPrefix(target, \"/\")\n\tif _, err := o.loadIDByPath(path.Dir(target)); err != nil {\n\t\tif !strings.Contains(err.Error(), \"itemNotFound\") {\n\t\t\treturn false, err\n\t\t}\n\t\tif err := o.createFolder(path.Dir(target)); err != nil {\n\t\t\treturn false, fmt.Errorf(\"create dir before upload failed, err: %v\", err)\n\t\t}\n\t}\n\n\tctx := context.Background()\n\tfolderID, err := o.loadIDByPath(path.Dir(target))\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tfileInfo, err := os.Stat(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif fileInfo.IsDir() {\n\t\treturn false, errors.New(\"only file is allowed to be uploaded here\")\n\t}\n\tvar isOk bool\n\tif fileInfo.Size() < 4*1024*1024 {\n\t\tisOk, err = o.upSmall(src, folderID, fileInfo.Size())\n\t} else {\n\t\tisOk, err = o.upBig(ctx, src, folderID, fileInfo.Size())\n\t}\n\treturn isOk, err\n}\n\nfunc (o oneDriveClient) Download(src, target string) (bool, error) {\n\tsrc = \"/\" + strings.TrimPrefix(src, \"/\")\n\treq, err := o.client.NewRequest(\"GET\", fmt.Sprintf(\"me/drive/root:%s\", src), nil)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"new request for file id failed, err: %v\", err)\n\t}\n\tvar driveItem *odsdk.DriveItem\n\tif err := o.client.Do(context.Background(), req, false, &driveItem); err != nil {\n\t\treturn false, fmt.Errorf(\"do request for file id failed, err: %v\", err)\n\t}\n\n\tresp, err := http.Get(driveItem.DownloadURL)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer resp.Body.Close()\n\n\tout, err := os.Create(target)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer out.Close()\n\tbuffer := make([]byte, 2*1024*1024)\n\n\t_, err = io.CopyBuffer(out, resp.Body, buffer)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn true, nil\n}\n\nfunc (o *oneDriveClient) ListObjects(prefix string) ([]string, error) {\n\tprefix = \"/\" + strings.TrimPrefix(prefix, \"/\")\n\tfolderID, err := o.loadIDByPath(prefix)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treq, err := o.client.NewRequest(\"GET\", fmt.Sprintf(\"me/drive/items/%s/children\", folderID), nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"new request for list failed, err: %v\", err)\n\t}\n\tvar driveItems *odsdk.OneDriveDriveItemsResponse\n\tif err := o.client.Do(context.Background(), req, false, &driveItems); err != nil {\n\t\treturn nil, fmt.Errorf(\"do request for list failed, err: %v\", err)\n\t}\n\n\tvar itemList []string\n\tfor _, item := range driveItems.DriveItems {\n\t\titemList = append(itemList, item.Name)\n\t}\n\treturn itemList, nil\n}\n\nfunc (o *oneDriveClient) loadIDByPath(path string) (string, error) {\n\tpathItem := \"root:\" + path\n\tif path == \"/\" {\n\t\tpathItem = \"root\"\n\t}\n\treq, err := o.client.NewRequest(\"GET\", fmt.Sprintf(\"me/drive/%s\", pathItem), nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"new request for file id failed, err: %v\", err)\n\t}\n\tvar driveItem *odsdk.DriveItem\n\tif err := o.client.Do(context.Background(), req, false, &driveItem); err != nil {\n\t\treturn \"\", fmt.Errorf(\"do request for file id failed, err: %v\", err)\n\t}\n\treturn driveItem.Id, nil\n}\n\nfunc RefreshToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) {\n\tdata := url.Values{}\n\tisCN := loadParamFromVars(\"isCN\", varMap)\n\tdata.Set(\"client_id\", loadParamFromVars(\"client_id\", varMap))\n\tdata.Set(\"client_secret\", loadParamFromVars(\"client_secret\", varMap))\n\tif grantType == \"refresh_token\" {\n\t\tdata.Set(\"grant_type\", \"refresh_token\")\n\t\tdata.Set(\"refresh_token\", loadParamFromVars(\"refresh_token\", varMap))\n\t} else {\n\t\tdata.Set(\"grant_type\", \"authorization_code\")\n\t\tdata.Set(\"code\", loadParamFromVars(\"code\", varMap))\n\t}\n\tdata.Set(\"redirect_uri\", loadParamFromVars(\"redirect_uri\", varMap))\n\tclient := &http.Client{}\n\tdefer client.CloseIdleConnections()\n\turl := \"https://login.microsoftonline.com/common/oauth2/v2.0/token\"\n\tif isCN == \"true\" {\n\t\turl = \"https://login.chinacloudapi.cn/common/oauth2/v2.0/token\"\n\t}\n\treq, err := http.NewRequest(\"POST\", url, strings.NewReader(data.Encode()))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"new http post client for access token failed, err: %v\", err)\n\t}\n\treq.Header.Add(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"request for access token failed, err: %v\", err)\n\t}\n\tdefer resp.Body.Close()\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"read data from response body failed, err: %v\", err)\n\t}\n\n\ttokenMap := map[string]interface{}{}\n\tif err := json.Unmarshal(respBody, &tokenMap); err != nil {\n\t\treturn \"\", fmt.Errorf(\"unmarshal data from response body failed, err: %v\", err)\n\t}\n\tif tokenType == \"accessToken\" {\n\t\taccessToken, ok := tokenMap[\"access_token\"].(string)\n\t\tif !ok {\n\t\t\treturn \"\", errors.New(\"no such access token in response\")\n\t\t}\n\t\ttokenMap = nil\n\t\treturn accessToken, nil\n\t}\n\trefreshToken, ok := tokenMap[\"refresh_token\"].(string)\n\tif !ok {\n\t\treturn \"\", errors.New(\"no such access token in response\")\n\t}\n\ttokenMap = nil\n\treturn refreshToken, nil\n}\n\nfunc (o *oneDriveClient) createFolder(parent string) error {\n\tif _, err := o.loadIDByPath(path.Dir(parent)); err != nil {\n\t\tif !strings.Contains(err.Error(), \"itemNotFound\") {\n\t\t\treturn err\n\t\t}\n\t\t_ = o.createFolder(path.Dir(parent))\n\t}\n\titem2, err := o.loadIDByPath(path.Dir(parent))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err := o.client.DriveItems.CreateNewFolder(context.Background(), \"\", item2, path.Base(parent)); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\ntype NewUploadSessionCreationRequest struct {\n\tConflictBehavior string `json:\"@microsoft.graph.conflictBehavior,omitempty\"`\n}\ntype NewUploadSessionCreationResponse struct {\n\tUploadURL          string `json:\"uploadUrl\"`\n\tExpirationDateTime string `json:\"expirationDateTime\"`\n}\ntype UploadSessionUploadResponse struct {\n\tExpirationDateTime string   `json:\"expirationDateTime\"`\n\tNextExpectedRanges []string `json:\"nextExpectedRanges\"`\n\tDriveItem\n}\ntype DriveItem struct {\n\tName        string `json:\"name\"`\n\tId          string `json:\"id\"`\n\tDownloadURL string `json:\"@microsoft.graph.downloadUrl\"`\n\tDescription string `json:\"description\"`\n\tSize        int64  `json:\"size\"`\n\tWebURL      string `json:\"webUrl\"`\n}\n\nfunc (o *oneDriveClient) NewSessionFileUploadRequest(absoluteUrl string, grandOffset, grandTotalSize int64, byteReader *bytes.Reader) (*http.Request, error) {\n\tapiUrl, err := o.client.BaseURL.Parse(absoluteUrl)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tabsoluteUrl = apiUrl.String()\n\tcontentLength := byteReader.Size()\n\treq, err := http.NewRequest(\"PUT\", absoluteUrl, byteReader)\n\treq.Header.Set(\"Content-Length\", strconv.FormatInt(contentLength, 10))\n\tpreliminaryLength := grandOffset\n\tpreliminaryRange := grandOffset + contentLength - 1\n\tif preliminaryRange >= grandTotalSize {\n\t\tpreliminaryRange = grandTotalSize - 1\n\t\tpreliminaryLength = preliminaryRange - grandOffset + 1\n\t}\n\treq.Header.Set(\"Content-Range\", fmt.Sprintf(\"bytes %d-%d/%d\", preliminaryLength, preliminaryRange, grandTotalSize))\n\n\treturn req, err\n}\n\nfunc (o *oneDriveClient) upSmall(srcPath, folderID string, fileSize int64) (bool, error) {\n\tfile, err := os.Open(srcPath)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\n\tbuffer := make([]byte, fileSize)\n\t_, _ = file.Read(buffer)\n\tfileReader := bytes.NewReader(buffer)\n\tapiURL := fmt.Sprintf(\"me/drive/items/%s:/%s:/content?@microsoft.graph.conflictBehavior=rename\", url.PathEscape(folderID), path.Base(srcPath))\n\n\tmimeType := files.GetMimeType(srcPath)\n\treq, err := o.client.NewFileUploadRequest(apiURL, mimeType, fileReader)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tvar response *DriveItem\n\tif err := o.client.Do(context.Background(), req, false, &response); err != nil {\n\t\treturn false, fmt.Errorf(\"do request for list failed, err: %v\", err)\n\t}\n\treturn true, nil\n}\n\nfunc (o *oneDriveClient) upBig(ctx context.Context, srcPath, folderID string, fileSize int64) (bool, error) {\n\tfile, err := os.Open(srcPath)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\n\tapiURL := fmt.Sprintf(\"me/drive/items/%s:/%s:/createUploadSession\", url.PathEscape(folderID), path.Base(srcPath))\n\tsessionCreationRequestInside := NewUploadSessionCreationRequest{\n\t\tConflictBehavior: \"rename\",\n\t}\n\n\tsessionCreationRequest := struct {\n\t\tItem        NewUploadSessionCreationRequest `json:\"item\"`\n\t\tDeferCommit bool                            `json:\"deferCommit\"`\n\t}{sessionCreationRequestInside, false}\n\n\tsessionCreationReq, err := o.client.NewRequest(\"POST\", apiURL, sessionCreationRequest)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tvar sessionCreationResp *NewUploadSessionCreationResponse\n\terr = o.client.Do(ctx, sessionCreationReq, false, &sessionCreationResp)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"session creation failed %w\", err)\n\t}\n\n\tfileSessionUploadUrl := sessionCreationResp.UploadURL\n\n\tsizePerSplit := int64(5 * 1024 * 1024)\n\tbuffer := make([]byte, 5*1024*1024)\n\tsplitCount := fileSize / sizePerSplit\n\tif fileSize%sizePerSplit != 0 {\n\t\tsplitCount += 1\n\t}\n\tbfReader := bufio.NewReader(file)\n\thttpClient := http.Client{\n\t\tTimeout: time.Minute * 10,\n\t\tTransport: &http.Transport{\n\t\t\tProxy:           http.ProxyFromEnvironment,\n\t\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t},\n\t}\n\tfor splitNow := int64(0); splitNow < splitCount; splitNow++ {\n\t\tlength, err := bfReader.Read(buffer)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tif int64(length) < sizePerSplit {\n\t\t\tbufferLast := buffer[:length]\n\t\t\tbuffer = bufferLast\n\t\t}\n\t\tsessionFileUploadReq, err := o.NewSessionFileUploadRequest(fileSessionUploadUrl, splitNow*sizePerSplit, fileSize, bytes.NewReader(buffer))\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tres, err := httpClient.Do(sessionFileUploadReq)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tres.Body.Close()\n\t\tif res.StatusCode != 201 && res.StatusCode != 202 && res.StatusCode != 200 {\n\t\t\tdata, _ := io.ReadAll(res.Body)\n\t\t\treturn false, errors.New(string(data))\n\t\t}\n\t}\n\treturn true, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/oss.go",
    "content": "package client\n\nimport (\n\t\"fmt\"\n\n\tosssdk \"github.com/aliyun/aliyun-oss-go-sdk/oss\"\n)\n\ntype ossClient struct {\n\tscType    string\n\tbucketStr string\n\tclient    osssdk.Client\n}\n\nfunc NewOssClient(vars map[string]interface{}) (*ossClient, error) {\n\tendpoint := loadParamFromVars(\"endpoint\", vars)\n\taccessKey := loadParamFromVars(\"accessKey\", vars)\n\tsecretKey := loadParamFromVars(\"secretKey\", vars)\n\tbucketStr := loadParamFromVars(\"bucket\", vars)\n\tscType := loadParamFromVars(\"scType\", vars)\n\tif len(scType) == 0 {\n\t\tscType = \"Standard\"\n\t}\n\tclient, err := osssdk.New(endpoint, accessKey, secretKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &ossClient{scType: scType, bucketStr: bucketStr, client: *client}, nil\n}\n\nfunc (o ossClient) ListBuckets() ([]interface{}, error) {\n\tresponse, err := o.client.ListBuckets()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar result []interface{}\n\tfor _, bucket := range response.Buckets {\n\t\tresult = append(result, bucket.Name)\n\t}\n\treturn result, err\n}\n\nfunc (o ossClient) Exist(path string) (bool, error) {\n\tbucket, err := o.client.Bucket(o.bucketStr)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn bucket.IsObjectExist(path)\n}\n\nfunc (o ossClient) Size(path string) (int64, error) {\n\tbucket, err := o.client.Bucket(o.bucketStr)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tlor, err := bucket.ListObjectsV2(osssdk.Prefix(path))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif len(lor.Objects) == 0 {\n\t\treturn 0, fmt.Errorf(\"no such file %s\", path)\n\t}\n\treturn lor.Objects[0].Size, nil\n}\n\nfunc (o ossClient) Delete(path string) (bool, error) {\n\tbucket, err := o.client.Bucket(o.bucketStr)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif err := bucket.DeleteObject(path); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (o ossClient) Upload(src, target string) (bool, error) {\n\tbucket, err := o.client.Bucket(o.bucketStr)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif err := bucket.UploadFile(target, src,\n\t\t200*1024*1024,\n\t\tosssdk.Routines(5),\n\t\tosssdk.Checkpoint(true, \"\"),\n\t\tosssdk.ObjectStorageClass(osssdk.StorageClassType(o.scType))); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (o ossClient) Download(src, target string) (bool, error) {\n\tbucket, err := o.client.Bucket(o.bucketStr)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif err := bucket.DownloadFile(src, target, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, \"\")); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (o *ossClient) ListObjects(prefix string) ([]string, error) {\n\tbucket, err := o.client.Bucket(o.bucketStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlor, err := bucket.ListObjectsV2(osssdk.Prefix(prefix))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar result []string\n\tfor _, obj := range lor.Objects {\n\t\tresult = append(result, obj.Key)\n\t}\n\treturn result, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/s3.go",
    "content": "package client\n\nimport (\n\t\"os\"\n\n\t\"github.com/aws/aws-sdk-go/aws\"\n\t\"github.com/aws/aws-sdk-go/aws/awserr\"\n\t\"github.com/aws/aws-sdk-go/aws/credentials\"\n\t\"github.com/aws/aws-sdk-go/aws/session\"\n\t\"github.com/aws/aws-sdk-go/service/s3\"\n\t\"github.com/aws/aws-sdk-go/service/s3/s3manager\"\n)\n\ntype s3Client struct {\n\tscType string\n\tbucket string\n\tSess   session.Session\n}\n\nfunc NewS3Client(vars map[string]interface{}) (*s3Client, error) {\n\taccessKey := loadParamFromVars(\"accessKey\", vars)\n\tsecretKey := loadParamFromVars(\"secretKey\", vars)\n\tendpoint := loadParamFromVars(\"endpoint\", vars)\n\tregion := loadParamFromVars(\"region\", vars)\n\tbucket := loadParamFromVars(\"bucket\", vars)\n\tscType := loadParamFromVars(\"scType\", vars)\n\tif len(scType) == 0 {\n\t\tscType = \"Standard\"\n\t}\n\tmode := loadParamFromVars(\"mode\", vars)\n\tif len(mode) == 0 {\n\t\tmode = \"virtual hosted\"\n\t}\n\tsess, err := session.NewSession(&aws.Config{\n\t\tCredentials: credentials.NewStaticCredentials(accessKey, secretKey, \"\"),\n\t\tEndpoint:    aws.String(endpoint),\n\t\tRegion:      aws.String(region),\n\t\tDisableSSL:  aws.Bool(true), S3ForcePathStyle: aws.Bool(mode == \"path\"),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &s3Client{scType: scType, bucket: bucket, Sess: *sess}, nil\n}\n\nfunc (s s3Client) ListBuckets() ([]interface{}, error) {\n\tvar result []interface{}\n\tsvc := s3.New(&s.Sess)\n\tres, err := svc.ListBuckets(nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, b := range res.Buckets {\n\t\tresult = append(result, b.Name)\n\t}\n\treturn result, nil\n}\n\nfunc (s s3Client) Exist(path string) (bool, error) {\n\tsvc := s3.New(&s.Sess)\n\tif _, err := svc.HeadObject(&s3.HeadObjectInput{\n\t\tBucket: &s.bucket,\n\t\tKey:    &path,\n\t}); err != nil {\n\t\tif aerr, ok := err.(awserr.RequestFailure); ok {\n\t\t\tif aerr.StatusCode() == 404 {\n\t\t\t\treturn false, nil\n\t\t\t}\n\t\t} else {\n\t\t\treturn false, aerr\n\t\t}\n\t}\n\treturn true, nil\n}\n\nfunc (s *s3Client) Size(path string) (int64, error) {\n\tsvc := s3.New(&s.Sess)\n\tfile, err := svc.GetObject(&s3.GetObjectInput{\n\t\tBucket: &s.bucket,\n\t\tKey:    &path,\n\t})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn *file.ContentLength, nil\n}\n\nfunc (s s3Client) Delete(path string) (bool, error) {\n\tsvc := s3.New(&s.Sess)\n\tif _, err := svc.DeleteObject(&s3.DeleteObjectInput{Bucket: aws.String(s.bucket), Key: aws.String(path)}); err != nil {\n\t\treturn false, err\n\t}\n\tif err := svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{\n\t\tBucket: aws.String(s.bucket),\n\t\tKey:    aws.String(path),\n\t}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s s3Client) Upload(src, target string) (bool, error) {\n\tfileInfo, err := os.Stat(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tfile, err := os.Open(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\n\tuploader := s3manager.NewUploader(&s.Sess)\n\tif fileInfo.Size() > s3manager.MaxUploadParts*s3manager.DefaultUploadPartSize {\n\t\tuploader.PartSize = fileInfo.Size() / (s3manager.MaxUploadParts - 1)\n\t}\n\tif _, err := uploader.Upload(&s3manager.UploadInput{\n\t\tBucket:       aws.String(s.bucket),\n\t\tKey:          aws.String(target),\n\t\tBody:         file,\n\t\tStorageClass: &s.scType,\n\t}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s s3Client) Download(src, target string) (bool, error) {\n\tif _, err := os.Stat(target); err == nil {\n\t\t_ = os.Remove(target)\n\t}\n\tfile, err := os.Create(target)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\tdownloader := s3manager.NewDownloader(&s.Sess)\n\tif _, err = downloader.Download(file, &s3.GetObjectInput{\n\t\tBucket: aws.String(s.bucket),\n\t\tKey:    aws.String(src),\n\t}); err != nil {\n\t\tos.Remove(target)\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s *s3Client) ListObjects(prefix string) ([]string, error) {\n\tsvc := s3.New(&s.Sess)\n\tvar result []string\n\toutputs, err := svc.ListObjects(&s3.ListObjectsInput{\n\t\tBucket: &s.bucket,\n\t\tPrefix: &prefix,\n\t})\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tfor _, item := range outputs.Contents {\n\t\tresult = append(result, *item.Key)\n\t}\n\treturn result, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/sftp.go",
    "content": "package client\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/pkg/sftp\"\n\t\"golang.org/x/crypto/ssh\"\n)\n\ntype sftpClient struct {\n\tconnInfo string\n\tconfig   *ssh.ClientConfig\n}\n\nfunc NewSftpClient(vars map[string]interface{}) (*sftpClient, error) {\n\taddress := loadParamFromVars(\"address\", vars)\n\tport := loadParamFromVars(\"port\", vars)\n\tif len(port) == 0 {\n\t\tglobal.LOG.Errorf(\"load param port from vars failed, err: not exist!\")\n\t}\n\tauthMode := loadParamFromVars(\"authMode\", vars)\n\tpassPhrase := loadParamFromVars(\"passPhrase\", vars)\n\tusername := loadParamFromVars(\"username\", vars)\n\tpassword := loadParamFromVars(\"password\", vars)\n\n\tvar auth []ssh.AuthMethod\n\tif authMode == \"key\" {\n\t\tvar signer ssh.Signer\n\t\tvar err error\n\t\tif len(passPhrase) != 0 {\n\t\t\tsigner, err = ssh.ParsePrivateKeyWithPassphrase([]byte(password), []byte(passPhrase))\n\t\t} else {\n\t\t\tsigner, err = ssh.ParsePrivateKey([]byte(password))\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tauth = []ssh.AuthMethod{ssh.PublicKeys(signer)}\n\t} else {\n\t\tauth = []ssh.AuthMethod{ssh.Password(password)}\n\t}\n\tclientConfig := &ssh.ClientConfig{\n\t\tUser:    username,\n\t\tAuth:    auth,\n\t\tTimeout: 30 * time.Second,\n\t\tHostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {\n\t\t\treturn nil\n\t\t},\n\t}\n\taddr := net.JoinHostPort(address, port)\n\tif _, err := ssh.Dial(\"tcp\", addr, clientConfig); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &sftpClient{connInfo: addr, config: clientConfig}, nil\n}\n\nfunc (s sftpClient) Upload(src, target string) (bool, error) {\n\tsshClient, err := ssh.Dial(\"tcp\", s.connInfo, s.config)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer sshClient.Close()\n\tclient, err := sftp.NewClient(sshClient)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer client.Close()\n\n\tsrcFile, err := os.Open(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer srcFile.Close()\n\n\ttargetDir, _ := path.Split(target)\n\tif len(targetDir) != 0 {\n\t\tif _, err = client.Stat(targetDir); err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tif err = client.MkdirAll(targetDir); err != nil {\n\t\t\t\t\treturn false, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn false, err\n\t\t\t}\n\t\t}\n\t}\n\tdstFile, err := client.Create(target)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer dstFile.Close()\n\n\tif _, err := io.Copy(dstFile, srcFile); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s sftpClient) ListBuckets() ([]interface{}, error) {\n\tvar result []interface{}\n\treturn result, nil\n}\n\nfunc (s sftpClient) Download(src, target string) (bool, error) {\n\tsshClient, err := ssh.Dial(\"tcp\", s.connInfo, s.config)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := sftp.NewClient(sshClient)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer client.Close()\n\tdefer sshClient.Close()\n\n\tsrcFile, err := client.Open(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer srcFile.Close()\n\n\tdstFile, err := os.Create(target)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer dstFile.Close()\n\n\tif _, err = io.Copy(dstFile, srcFile); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, err\n}\n\nfunc (s sftpClient) Exist(filePath string) (bool, error) {\n\tsshClient, err := ssh.Dial(\"tcp\", s.connInfo, s.config)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := sftp.NewClient(sshClient)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer client.Close()\n\tdefer sshClient.Close()\n\n\tsrcFile, err := client.Open(filePath)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn false, nil\n\t\t} else {\n\t\t\treturn false, err\n\t\t}\n\t}\n\tdefer srcFile.Close()\n\treturn true, err\n}\n\nfunc (s sftpClient) Size(filePath string) (int64, error) {\n\tsshClient, err := ssh.Dial(\"tcp\", s.connInfo, s.config)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tclient, err := sftp.NewClient(sshClient)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer client.Close()\n\tdefer sshClient.Close()\n\n\tfiles, err := client.Stat(filePath)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn files.Size(), nil\n}\n\nfunc (s sftpClient) Delete(filePath string) (bool, error) {\n\tsshClient, err := ssh.Dial(\"tcp\", s.connInfo, s.config)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := sftp.NewClient(sshClient)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer client.Close()\n\tdefer sshClient.Close()\n\n\tif err := client.Remove(filePath); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s sftpClient) ListObjects(prefix string) ([]string, error) {\n\tsshClient, err := ssh.Dial(\"tcp\", s.connInfo, s.config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclient, err := sftp.NewClient(sshClient)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer client.Close()\n\tdefer sshClient.Close()\n\n\tfiles, err := client.ReadDir(prefix)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar result []string\n\tfor _, file := range files {\n\t\tresult = append(result, file.Name())\n\t}\n\treturn result, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/up.go",
    "content": "package client\n\nimport (\n\t\"path\"\n\n\t\"github.com/upyun/go-sdk/upyun\"\n)\n\ntype upClient struct {\n\tbucket string\n\tclient *upyun.UpYun\n}\n\nfunc NewUpClient(vars map[string]interface{}) (*upClient, error) {\n\toperator := loadParamFromVars(\"operator\", vars)\n\tpassword := loadParamFromVars(\"password\", vars)\n\tbucket := loadParamFromVars(\"bucket\", vars)\n\tclient := upyun.NewUpYun(&upyun.UpYunConfig{\n\t\tBucket:   bucket,\n\t\tOperator: operator,\n\t\tPassword: password,\n\t})\n\n\treturn &upClient{bucket: bucket, client: client}, nil\n}\n\nfunc (o upClient) ListBuckets() ([]interface{}, error) {\n\tvar result []interface{}\n\treturn result, nil\n}\n\nfunc (s upClient) Upload(src, target string) (bool, error) {\n\tif _, err := s.client.GetInfo(path.Dir(src)); err != nil {\n\t\t_ = s.client.Mkdir(path.Dir(target))\n\t}\n\tif err := s.client.Put(&upyun.PutObjectConfig{\n\t\tPath:            target,\n\t\tLocalPath:       src,\n\t\tUseResumeUpload: true,\n\t}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s upClient) Size(path string) (int64, error) {\n\tfileInfo, err := s.client.GetInfo(path)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn fileInfo.Size, nil\n}\n\nfunc (s upClient) Delete(path string) (bool, error) {\n\tif err := s.client.Delete(&upyun.DeleteObjectConfig{\n\t\tPath: path,\n\t}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s upClient) Exist(filePath string) (bool, error) {\n\tif _, err := s.client.GetInfo(filePath); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s upClient) Download(src, target string) (bool, error) {\n\tif _, err := s.client.Get(&upyun.GetObjectConfig{\n\t\tPath:      src,\n\t\tLocalPath: target,\n\t}); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s *upClient) ListObjects(prefix string) ([]string, error) {\n\tobjsChan := make(chan *upyun.FileInfo, 10)\n\tif err := s.client.List(&upyun.GetObjectsConfig{\n\t\tPath:         prefix,\n\t\tObjectsChan:  objsChan,\n\t\tMaxListTries: 1,\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\tvar files []string\n\tfor obj := range objsChan {\n\t\tfiles = append(files, obj.Name)\n\t}\n\treturn files, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/client/webdav.go",
    "content": "package client\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage/client/helper/webdav\"\n)\n\ntype webDAVClient struct {\n\tBucket string\n\tclient *webdav.Client\n}\n\nfunc NewWebDAVClient(vars map[string]interface{}) (*webDAVClient, error) {\n\taddress := loadParamFromVars(\"address\", vars)\n\tport := loadParamFromVars(\"port\", vars)\n\tpassword := loadParamFromVars(\"password\", vars)\n\tusername := loadParamFromVars(\"username\", vars)\n\tbucket := loadParamFromVars(\"bucket\", vars)\n\n\turl := fmt.Sprintf(\"%s:%s\", address, port)\n\tif len(port) == 0 {\n\t\turl = address\n\t}\n\tclient := webdav.NewClient(url, username, password)\n\ttlsConfig := &tls.Config{}\n\tif strings.HasPrefix(address, \"https\") {\n\t\ttlsConfig.InsecureSkipVerify = true\n\t}\n\tvar transport http.RoundTripper = &http.Transport{\n\t\tTLSClientConfig: tlsConfig,\n\t}\n\tclient.SetTransport(transport)\n\tif err := client.Connect(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &webDAVClient{Bucket: bucket, client: client}, nil\n}\n\nfunc (s webDAVClient) Upload(src, target string) (bool, error) {\n\ttargetFilePath := path.Join(s.Bucket, target)\n\tsrcFile, err := os.Open(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer srcFile.Close()\n\n\tif err := s.client.WriteStream(targetFilePath, srcFile, constant.DirPerm); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s webDAVClient) ListBuckets() ([]interface{}, error) {\n\tvar result []interface{}\n\treturn result, nil\n}\n\nfunc (s webDAVClient) Download(src, target string) (bool, error) {\n\tsrcPath := path.Join(s.Bucket, src)\n\tinfo, err := s.client.Stat(srcPath)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\ttargetStat, err := os.Stat(target)\n\tif err == nil {\n\t\tif info.Size() == targetStat.Size() {\n\t\t\treturn true, nil\n\t\t}\n\t}\n\tfile, err := os.Create(target)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer file.Close()\n\treader, _ := s.client.ReadStream(srcPath)\n\tif _, err := io.Copy(file, reader); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, err\n}\n\nfunc (s webDAVClient) Exist(pathItem string) (bool, error) {\n\tif _, err := s.client.Stat(path.Join(s.Bucket, pathItem)); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s webDAVClient) Size(pathItem string) (int64, error) {\n\tfile, err := s.client.Stat(path.Join(s.Bucket, pathItem))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn file.Size(), nil\n}\n\nfunc (s webDAVClient) Delete(pathItem string) (bool, error) {\n\tif err := s.client.RemoveAll(path.Join(s.Bucket, pathItem)); err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (s webDAVClient) ListObjects(prefix string) ([]string, error) {\n\tfiles, err := s.client.ReadDir(path.Join(s.Bucket, prefix))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar result []string\n\tfor _, file := range files {\n\t\tresult = append(result, file.Name())\n\t}\n\treturn result, nil\n}\n"
  },
  {
    "path": "agent/utils/cloud_storage/cloud_storage_client.go",
    "content": "package cloud_storage\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage/client\"\n)\n\ntype CloudStorageClient interface {\n\tListBuckets() ([]interface{}, error)\n\tListObjects(prefix string) ([]string, error)\n\tExist(path string) (bool, error)\n\tDelete(path string) (bool, error)\n\tUpload(src, target string) (bool, error)\n\tDownload(src, target string) (bool, error)\n\n\tSize(path string) (int64, error)\n}\n\nfunc NewCloudStorageClient(backupType string, vars map[string]interface{}) (CloudStorageClient, error) {\n\tswitch backupType {\n\tcase constant.Local:\n\t\treturn client.NewLocalClient(vars)\n\tcase constant.S3:\n\t\treturn client.NewS3Client(vars)\n\tcase constant.OSS:\n\t\treturn client.NewOssClient(vars)\n\tcase constant.Sftp:\n\t\treturn client.NewSftpClient(vars)\n\tcase constant.WebDAV:\n\t\treturn client.NewWebDAVClient(vars)\n\tcase constant.MinIo:\n\t\treturn client.NewMinIoClient(vars)\n\tcase constant.Cos:\n\t\treturn client.NewCosClient(vars)\n\tcase constant.Kodo:\n\t\treturn client.NewKodoClient(vars)\n\tcase constant.OneDrive:\n\t\treturn client.NewOneDriveClient(vars)\n\tcase constant.UPYUN:\n\t\treturn client.NewUpClient(vars)\n\tcase constant.ALIYUN:\n\t\treturn client.NewALIClient(vars)\n\tcase constant.GoogleDrive:\n\t\treturn client.NewGoogleDriveClient(vars)\n\tdefault:\n\t\treturn nil, buserr.WithName(\"ErrNotSupportType\", backupType)\n\t}\n}\n"
  },
  {
    "path": "agent/utils/cmd/cmd.go",
    "content": "package cmd\n\nimport (\n\t\"os/exec\"\n\t\"strings\"\n)\n\nfunc CheckIllegal(args ...string) bool {\n\tif args == nil {\n\t\treturn false\n\t}\n\tfor _, arg := range args {\n\t\tif strings.Contains(arg, \"&\") || strings.Contains(arg, \"|\") || strings.Contains(arg, \";\") ||\n\t\t\tstrings.Contains(arg, \"$\") || strings.Contains(arg, \"'\") || strings.Contains(arg, \"`\") ||\n\t\t\tstrings.Contains(arg, \"(\") || strings.Contains(arg, \")\") || strings.Contains(arg, \"\\\"\") ||\n\t\t\tstrings.Contains(arg, \"\\n\") || strings.Contains(arg, \"\\r\") || strings.Contains(arg, \">\") || strings.Contains(arg, \"<\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc SudoHandleCmd() string {\n\tcmd := exec.Command(\"sudo\", \"-n\", \"ls\")\n\tif err := cmd.Run(); err == nil {\n\t\treturn \"sudo \"\n\t}\n\treturn \"\"\n}\n\nfunc Which(name string) bool {\n\tstdout, err := RunDefaultWithStdoutBashCf(\"which %s\", name)\n\tif err != nil || (len(strings.ReplaceAll(stdout, \"\\n\", \"\")) == 0) {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "agent/utils/cmd/cmdx.go",
    "content": "package cmd\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n)\n\ntype CommandHelper struct {\n\tcontext      context.Context\n\tworkDir      string\n\toutputFile   string\n\tscriptPath   string\n\ttimeout      time.Duration\n\ttaskItem     *task.Task\n\tlogger       *log.Logger\n\tIgnoreExist1 bool\n}\n\ntype Option func(*CommandHelper)\n\nfunc NewCommandMgr(opts ...Option) *CommandHelper {\n\ts := &CommandHelper{}\n\tfor _, opt := range opts {\n\t\topt(s)\n\t}\n\treturn s\n}\n\nfunc RunDefaultBashC(command string) error {\n\tmgr := NewCommandMgr()\n\treturn mgr.RunBashC(command)\n}\nfunc RunDefaultBashCf(command string, arg ...interface{}) error {\n\tmgr := NewCommandMgr()\n\treturn mgr.RunBashCf(command, arg...)\n}\nfunc RunDefaultWithStdoutBashC(command string) (string, error) {\n\tmgr := NewCommandMgr(WithTimeout(20 * time.Second))\n\treturn mgr.RunWithStdoutBashC(command)\n}\nfunc RunDefaultWithStdoutBashCf(command string, arg ...interface{}) (string, error) {\n\tmgr := NewCommandMgr(WithTimeout(20 * time.Second))\n\treturn mgr.RunWithStdoutBashCf(command, arg...)\n}\nfunc RunDefaultWithStdoutBashCfAndTimeOut(command string, timeout time.Duration, arg ...interface{}) (string, error) {\n\tmgr := NewCommandMgr(WithTimeout(timeout))\n\treturn mgr.RunWithStdoutBashCf(command, arg...)\n}\n\nfunc (c *CommandHelper) Run(name string, arg ...string) error {\n\t_, err := c.run(name, arg...)\n\treturn err\n}\nfunc (c *CommandHelper) RunBashCWithArgs(arg ...string) error {\n\targ = append([]string{\"-c\"}, arg...)\n\t_, err := c.run(\"bash\", arg...)\n\treturn err\n}\n\nfunc (c *CommandHelper) RunBashC(command string) error {\n\tif _, err := c.run(\"bash\", \"-c\", command); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\nfunc (c *CommandHelper) RunBashCf(command string, arg ...interface{}) error {\n\tif _, err := c.run(\"bash\", \"-c\", fmt.Sprintf(command, arg...)); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *CommandHelper) RunWithStdout(name string, arg ...string) (string, error) {\n\treturn c.run(name, arg...)\n}\nfunc (c *CommandHelper) RunWithStdoutBashC(command string) (string, error) {\n\treturn c.run(\"bash\", \"-c\", command)\n}\nfunc (c *CommandHelper) RunWithStdoutBashCf(command string, arg ...interface{}) (string, error) {\n\treturn c.run(\"bash\", \"-c\", fmt.Sprintf(command, arg...))\n}\n\nfunc (c *CommandHelper) run(name string, arg ...string) (string, error) {\n\tvar cmd *exec.Cmd\n\tvar newContext context.Context\n\tvar cancel context.CancelFunc\n\n\tif c.timeout != 0 {\n\t\tif c.context == nil {\n\t\t\tnewContext, cancel = context.WithTimeout(context.Background(), c.timeout)\n\t\t\tdefer cancel()\n\t\t} else {\n\t\t\tnewContext, cancel = context.WithTimeout(c.context, c.timeout)\n\t\t\tdefer cancel()\n\t\t}\n\t\tcmd = exec.CommandContext(newContext, name, arg...)\n\t} else {\n\t\tif c.context == nil {\n\t\t\tnewContext = context.Background()\n\t\t\tcmd = exec.Command(name, arg...)\n\t\t} else {\n\t\t\tnewContext = c.context\n\t\t\tcmd = exec.CommandContext(c.context, name, arg...)\n\t\t}\n\t}\n\tcmd.SysProcAttr = &syscall.SysProcAttr{\n\t\tSetpgid: true,\n\t}\n\n\tcustomWriter := &CustomWriter{taskItem: c.taskItem}\n\tvar stdout, stderr bytes.Buffer\n\tif c.taskItem != nil {\n\t\tcmd.Stdout = customWriter\n\t\tcmd.Stderr = customWriter\n\t} else if c.logger != nil {\n\t\tcmd.Stdout = c.logger.Writer()\n\t\tcmd.Stderr = c.logger.Writer()\n\t} else if len(c.outputFile) != 0 {\n\t\tfile, err := os.OpenFile(c.outputFile, os.O_WRONLY|os.O_CREATE, constant.FilePerm)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tdefer file.Close()\n\t\tcmd.Stdout = file\n\t\tcmd.Stderr = file\n\t} else if len(c.scriptPath) != 0 {\n\t\tcmd = exec.Command(\"bash\", c.scriptPath)\n\t\tcmd.Stdout = &stdout\n\t\tcmd.Stderr = &stderr\n\t} else {\n\t\tcmd.Stdout = &stdout\n\t\tcmd.Stderr = &stderr\n\t}\n\tenv := os.Environ()\n\tcmd.Env = env\n\tif len(c.workDir) != 0 {\n\t\tcmd.Dir = c.workDir\n\t}\n\n\tif err := cmd.Start(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"cmd.Start() failed with '%s'\\n\", err)\n\t}\n\tif c.taskItem != nil {\n\t\tdefer customWriter.Flush()\n\t}\n\n\tdone := make(chan error, 1)\n\tgo func() {\n\t\tdone <- cmd.Wait()\n\t}()\n\tselect {\n\tcase err := <-done:\n\t\tif err != nil {\n\t\t\treturn handleErr(stdout, stderr, c.IgnoreExist1, err)\n\t\t}\n\t\treturn stdout.String(), nil\n\tcase <-newContext.Done():\n\t\tif cmd.Process != nil && cmd.Process.Pid > 0 {\n\t\t\tsyscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)\n\t\t}\n\t\tvar err error\n\t\tswitch newContext.Err() {\n\t\tcase context.DeadlineExceeded:\n\t\t\terr = buserr.New(\"ErrCmdTimeout\")\n\t\tcase context.Canceled:\n\t\t\terr = buserr.New(\"ErrShutDown\")\n\t\tdefault:\n\t\t\terr = newContext.Err()\n\t\t}\n\t\t<-done\n\t\treturn \"\", err\n\t}\n}\n\nfunc WithContext(ctx context.Context) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.context = ctx\n\t}\n}\nfunc WithOutputFile(outputFile string) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.outputFile = outputFile\n\t}\n}\nfunc WithTimeout(timeout time.Duration) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.timeout = timeout\n\t}\n}\nfunc WithLogger(logger *log.Logger) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.logger = logger\n\t}\n}\nfunc WithTask(taskItem task.Task) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.taskItem = &taskItem\n\t}\n}\nfunc WithWorkDir(workDir string) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.workDir = workDir\n\t}\n}\nfunc WithScriptPath(scriptPath string) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.scriptPath = scriptPath\n\t}\n}\nfunc WithIgnoreExist1() Option {\n\treturn func(s *CommandHelper) {\n\t\ts.IgnoreExist1 = true\n\t}\n}\n\ntype CustomWriter struct {\n\ttaskItem *task.Task\n\tbuffer   bytes.Buffer\n}\n\nfunc (cw *CustomWriter) Write(p []byte) (n int, err error) {\n\tcw.buffer.Write(p)\n\tlines := strings.Split(cw.buffer.String(), \"\\n\")\n\n\tfor i := 0; i < len(lines)-1; i++ {\n\t\tcw.taskItem.Log(lines[i])\n\t}\n\tcw.buffer.Reset()\n\tcw.buffer.WriteString(lines[len(lines)-1])\n\n\treturn len(p), nil\n}\nfunc (cw *CustomWriter) Flush() {\n\tif cw.buffer.Len() > 0 {\n\t\tcw.taskItem.Log(cw.buffer.String())\n\t\tcw.buffer.Reset()\n\t}\n}\n\nfunc handleErr(stdout, stderr bytes.Buffer, ignoreExist1 bool, err error) (string, error) {\n\tvar exitError *exec.ExitError\n\tif ignoreExist1 && errors.As(err, &exitError) {\n\t\tif status, ok := exitError.Sys().(syscall.WaitStatus); ok {\n\t\t\tif status.ExitStatus() == 1 {\n\t\t\t\treturn \"\", nil\n\t\t\t}\n\t\t}\n\t}\n\toutItem := stdout.String()\n\terrItem := stderr.String()\n\tif len(errItem) != 0 && len(outItem) != 0 {\n\t\treturn outItem, fmt.Errorf(\"stdout: %s; stderr: %s, err: %v\", outItem, errItem, err)\n\t}\n\tif len(errItem) != 0 {\n\t\treturn outItem, fmt.Errorf(\"stderr: %s, err: %v\", errItem, err)\n\t}\n\tif len(outItem) != 0 {\n\t\treturn outItem, fmt.Errorf(\"stdout: %s, err: %v\", outItem, err)\n\t}\n\treturn \"\", err\n}\n"
  },
  {
    "path": "agent/utils/common/common.go",
    "content": "package common\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/big\"\n\t\"net\"\n\t\"os/exec\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n\n\t\"github.com/gin-gonic/gin\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n\t\"golang.org/x/net/idna\"\n)\n\nfunc CompareVersion(version1, version2 string) bool {\n\tv1s := extractNumbers(version1)\n\tv2s := extractNumbers(version2)\n\n\tmaxLen := max(len(v1s), len(v2s))\n\tv1s = append(v1s, make([]string, maxLen-len(v1s))...)\n\tv2s = append(v2s, make([]string, maxLen-len(v2s))...)\n\n\tfor i := 0; i < maxLen; i++ {\n\t\tv1, err1 := strconv.Atoi(v1s[i])\n\t\tv2, err2 := strconv.Atoi(v2s[i])\n\t\tif err1 != nil {\n\t\t\tv1 = 0\n\t\t}\n\t\tif err2 != nil {\n\t\t\tv2 = 0\n\t\t}\n\t\tif v1 != v2 {\n\t\t\treturn v1 > v2\n\t\t}\n\t}\n\treturn false\n}\n\nfunc CompareAppVersion(version1, version2 string) bool {\n\tv1s := extractNumbers(version1)\n\tv2s := extractNumbers(version2)\n\n\tmaxLen := max(len(v1s), len(v2s))\n\tv1s = append(v1s, make([]string, maxLen-len(v1s))...)\n\tv2s = append(v2s, make([]string, maxLen-len(v2s))...)\n\n\tfor i := 0; i < maxLen; i++ {\n\t\tv1, err1 := strconv.Atoi(v1s[i])\n\t\tv2, err2 := strconv.Atoi(v2s[i])\n\t\tif err1 != nil {\n\t\t\tv1 = 0\n\t\t}\n\t\tif err2 != nil {\n\t\t\tv2 = 0\n\t\t}\n\t\tif v1 > v2 {\n\t\t\treturn true\n\t\t}\n\t\tif v1 < v2 {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc ComparePanelVersion(version1, version2 string) bool {\n\tif version1 == version2 {\n\t\treturn false\n\t}\n\tversion1s := SplitStr(version1, \".\", \"-\")\n\tversion2s := SplitStr(version2, \".\", \"-\")\n\n\tif len(version2s) > len(version1s) {\n\t\tfor i := 0; i < len(version2s)-len(version1s); i++ {\n\t\t\tversion1s = append(version1s, \"0\")\n\t\t}\n\t}\n\tif len(version1s) > len(version2s) {\n\t\tfor i := 0; i < len(version1s)-len(version2s); i++ {\n\t\t\tversion2s = append(version2s, \"0\")\n\t\t}\n\t}\n\n\tn := min(len(version1s), len(version2s))\n\tfor i := 0; i < n; i++ {\n\t\tif version1s[i] == version2s[i] {\n\t\t\tcontinue\n\t\t} else {\n\t\t\tv1, err1 := strconv.Atoi(version1s[i])\n\t\t\tif err1 != nil {\n\t\t\t\treturn version1s[i] > version2s[i]\n\t\t\t}\n\t\t\tv2, err2 := strconv.Atoi(version2s[i])\n\t\t\tif err2 != nil {\n\t\t\t\treturn version1s[i] > version2s[i]\n\t\t\t}\n\t\t\treturn v1 > v2\n\t\t}\n\t}\n\treturn true\n}\n\nfunc extractNumbers(version string) []string {\n\tvar numbers []string\n\tstart := -1\n\tfor i, r := range version {\n\t\tif isDigit(r) {\n\t\t\tif start == -1 {\n\t\t\t\tstart = i\n\t\t\t}\n\t\t} else {\n\t\t\tif start != -1 {\n\t\t\t\tnumbers = append(numbers, version[start:i])\n\t\t\t\tstart = -1\n\t\t\t}\n\t\t}\n\t}\n\tif start != -1 {\n\t\tnumbers = append(numbers, version[start:])\n\t}\n\treturn numbers\n}\n\nfunc isDigit(r rune) bool {\n\treturn r >= '0' && r <= '9'\n}\n\nfunc GetSortedVersions(versions []string) []string {\n\tsort.Slice(versions, func(i, j int) bool {\n\t\treturn CompareVersion(versions[i], versions[j])\n\t})\n\treturn versions\n}\n\nfunc IsCrossVersion(version1, version2 string) bool {\n\tversion1s := strings.Split(version1, \".\")\n\tversion2s := strings.Split(version2, \".\")\n\tv1num, _ := strconv.Atoi(version1s[0])\n\tv2num, _ := strconv.Atoi(version2s[0])\n\treturn v2num > v1num\n}\n\nfunc GetUuid() string {\n\tb := make([]byte, 16)\n\t_, _ = io.ReadFull(rand.Reader, b)\n\tb[6] = (b[6] & 0x0f) | 0x40\n\tb[8] = (b[8] & 0x3f) | 0x80\n\treturn fmt.Sprintf(\"%x-%x-%x-%x-%x\", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])\n}\n\nvar letters = []rune(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\")\n\nfunc RandStr(n int) string {\n\tb := make([]rune, n)\n\tmax := big.NewInt(int64(len(letters)))\n\tfor i := range b {\n\t\tnum, err := rand.Int(rand.Reader, max)\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\tb[i] = letters[num.Int64()]\n\t}\n\treturn string(b)\n}\n\nfunc RandStrAndNum(n int) string {\n\tconst charset = \"abcdefghijklmnopqrstuvwxyz0123456789\"\n\tb := make([]byte, n)\n\tmax := big.NewInt(int64(len(charset)))\n\tfor i := range b {\n\t\tnum, err := rand.Int(rand.Reader, max)\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\tb[i] = charset[num.Int64()]\n\t}\n\treturn string(b)\n}\n\nfunc ScanPort(port int) bool {\n\tln, err := net.Listen(\"tcp\", \":\"+strconv.Itoa(port))\n\tif err != nil {\n\t\treturn true\n\t}\n\tdefer func() { _ = ln.Close() }()\n\treturn false\n}\n\nfunc ScanUDPPort(port int) bool {\n\tln, err := net.ListenUDP(\"udp\", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port})\n\tif err != nil {\n\t\treturn true\n\t}\n\tdefer func() { _ = ln.Close() }()\n\treturn false\n}\n\nfunc ScanPortWithProto(port int, proto string) bool {\n\tif proto == \"udp\" {\n\t\treturn ScanUDPPort(port)\n\t}\n\treturn ScanPort(port)\n}\n\nfunc ScanPortWithIP(ip string, port int) bool {\n\tif len(ip) == 0 || ip == \"0.0.0.0\" || ip == \"::\" {\n\t\treturn ScanPort(port)\n\t}\n\taddress := net.JoinHostPort(ip, fmt.Sprintf(\"%d\", port))\n\ttimeout := time.Second * 2\n\tconn, err := net.DialTimeout(\"tcp\", address, timeout)\n\n\tif err != nil {\n\t\tif opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\tdefer func() { _ = conn.Close() }()\n\treturn true\n}\n\nfunc IsNum(s string) bool {\n\t_, err := strconv.ParseFloat(s, 64)\n\treturn err == nil\n}\n\nfunc RemoveRepeatElement(a interface{}) (ret []interface{}) {\n\tva := reflect.ValueOf(a)\n\tfor i := 0; i < va.Len(); i++ {\n\t\tif i > 0 && reflect.DeepEqual(va.Index(i-1).Interface(), va.Index(i).Interface()) {\n\t\t\tcontinue\n\t\t}\n\t\tret = append(ret, va.Index(i).Interface())\n\t}\n\treturn ret\n}\n\nfunc RemoveRepeatStr(list []string) (ret []string) {\n\tmapItem := make(map[string]struct{})\n\tfor _, item := range list {\n\t\tmapItem[item] = struct{}{}\n\t}\n\tfor key := range mapItem {\n\t\tret = append(ret, key)\n\t}\n\treturn ret\n}\n\nfunc LoadSizeUnit(value float64) string {\n\tval := int64(value)\n\tif val%1024 != 0 {\n\t\treturn fmt.Sprintf(\"%v\", val)\n\t}\n\tif val > 1048576 {\n\t\treturn fmt.Sprintf(\"%vM\", val/1048576)\n\t}\n\tif val > 1024 {\n\t\treturn fmt.Sprintf(\"%vK\", val/1024)\n\t}\n\treturn fmt.Sprintf(\"%v\", val)\n}\n\nfunc LoadSizeUnit2F(value float64) string {\n\tif value > 1073741824 {\n\t\treturn fmt.Sprintf(\"%.2fG\", value/1073741824)\n\t}\n\tif value > 1048576 {\n\t\treturn fmt.Sprintf(\"%.2fM\", value/1048576)\n\t}\n\tif value > 1024 {\n\t\treturn fmt.Sprintf(\"%.2fK\", value/1024)\n\t}\n\treturn fmt.Sprintf(\"%.2f\", value)\n}\n\nfunc LoadTimeZoneByCmd() string {\n\tloc := time.Now().Location().String()\n\tif _, err := time.LoadLocation(loc); err != nil {\n\t\tloc = \"Asia/Shanghai\"\n\t}\n\tstd, err := cmd.RunDefaultWithStdoutBashC(\"timedatectl | grep 'Time zone'\")\n\tif err != nil {\n\t\treturn loc\n\t}\n\tfields := strings.Fields(string(std))\n\tif len(fields) != 5 {\n\t\treturn loc\n\t}\n\tif _, err := time.LoadLocation(fields[2]); err != nil {\n\t\treturn loc\n\t}\n\treturn fields[2]\n}\n\nfunc IsValidDomain(domain string) bool {\n\treturn re.GetRegex(re.DomainPattern).MatchString(domain)\n}\n\nfunc IsValidNginxServerName(serverName string) bool {\n\treturn re.GetRegex(re.NginxServerNamePattern).MatchString(serverName)\n}\n\nfunc ContainsChinese(text string) bool {\n\tfor _, char := range text {\n\t\tif unicode.Is(unicode.Han, char) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc PunycodeEncode(text string) (string, error) {\n\tencoder := idna.New()\n\tascii, err := encoder.ToASCII(text)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn ascii, nil\n}\n\nfunc SplitStr(str string, spi ...string) []string {\n\tlists := []string{str}\n\tvar results []string\n\tfor _, s := range spi {\n\t\tresults = []string{}\n\t\tfor _, list := range lists {\n\t\t\tresults = append(results, strings.Split(list, s)...)\n\t\t}\n\t\tlists = results\n\t}\n\treturn results\n}\n\nfunc IsValidIP(ip string) bool {\n\treturn net.ParseIP(ip) != nil\n}\n\nconst (\n\tb  = uint64(1)\n\tkb = 1024 * b\n\tmb = 1024 * kb\n\tgb = 1024 * mb\n)\n\nfunc FormatBytes(bytes uint64) string {\n\tswitch {\n\tcase bytes < kb:\n\t\treturn fmt.Sprintf(\"%dB\", bytes)\n\tcase bytes < mb:\n\t\treturn fmt.Sprintf(\"%.2fKB\", float64(bytes)/float64(kb))\n\tcase bytes < gb:\n\t\treturn fmt.Sprintf(\"%.2fMB\", float64(bytes)/float64(mb))\n\tdefault:\n\t\treturn fmt.Sprintf(\"%.2fGB\", float64(bytes)/float64(gb))\n\t}\n}\n\nfunc FormatPercent(percent float64) string {\n\treturn fmt.Sprintf(\"%.2f%%\", percent)\n}\n\nfunc GetLang(c *gin.Context) string {\n\tlang := c.GetHeader(\"Accept-Language\")\n\tif lang == \"\" {\n\t\tlang = \"en\"\n\t}\n\treturn lang\n}\n\nfunc HandleIPList(content string) ([]string, error) {\n\tipList := strings.Split(content, \"\\n\")\n\tvar res []string\n\tfor _, ip := range ipList {\n\t\tif ip == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif net.ParseIP(ip) != nil {\n\t\t\tres = append(res, ip)\n\t\t\tcontinue\n\t\t}\n\t\tif _, _, err := net.ParseCIDR(ip); err != nil {\n\t\t\treturn nil, buserr.New(\"ErrParseIP\")\n\t\t}\n\t\tres = append(res, ip)\n\t}\n\treturn res, nil\n}\n\nfunc GetSystemVersion(versionString string) string {\n\tmatch := re.GetRegex(re.VersionPattern).FindStringSubmatch(versionString)\n\tif len(match) > 1 {\n\t\treturn match[1]\n\t}\n\treturn \"\"\n}\n\nfunc isCommandAvailable(name string, args ...string) bool {\n\texecCmd := exec.Command(name, args...)\n\terr := execCmd.Run()\n\treturn err == nil\n}\n\nfunc GetDockerComposeCommand() string {\n\tif isCommandAvailable(\"docker\", \"compose\", \"version\") {\n\t\treturn \"docker compose\"\n\t}\n\tif isCommandAvailable(\"docker-compose\", \"version\") {\n\t\treturn \"docker-compose\"\n\t}\n\treturn \"\"\n}\n\nfunc LoadParams(param string) string {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2\", param)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tinfo := strings.ReplaceAll(stdout, \"\\n\", \"\")\n\tif len(info) == 0 || info == `\"\"` {\n\t\tpanic(fmt.Sprintf(\"error `%s` find in /usr/local/bin/1pctl\", param))\n\t}\n\treturn info\n}\nfunc LoadParamsWithoutPanic(param string) string {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2\", param)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strings.ReplaceAll(stdout, \"\\n\", \"\")\n}\n"
  },
  {
    "path": "agent/utils/common/sqlite.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/glebarez/sqlite\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n)\n\nfunc LoadDBConnByPath(fullPath, dbName string) *gorm.DB {\n\tif _, err := createDirWhenNotExist(true, path.Dir(fullPath)); err != nil {\n\t\tpanic(fmt.Errorf(\"init db dir failed, err: %v\", err))\n\t}\n\tif _, err := os.Stat(fullPath); err != nil {\n\t\tf, err := os.Create(fullPath)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(\"init %s db file failed, err: %v\", dbName, err))\n\t\t}\n\t\t_ = f.Close()\n\t}\n\n\tdb, err := GetDBWithPath(fullPath)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn db\n}\n\nfunc LoadDBConnByPathWithErr(fullPath, dbName string) (*gorm.DB, error) {\n\tif _, err := createDirWhenNotExist(true, path.Dir(fullPath)); err != nil {\n\t\treturn nil, fmt.Errorf(\"init db dir failed, err: %v\", err)\n\t}\n\tif _, err := os.Stat(fullPath); err != nil {\n\t\tf, err := os.Create(fullPath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"init %s db file failed, err: %v\", dbName, err)\n\t\t}\n\t\t_ = f.Close()\n\t}\n\n\tdb, err := GetDBWithPath(fullPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"init %s db failed, err: %v\", dbName, err)\n\t}\n\treturn db, nil\n}\n\nfunc CloseDB(db *gorm.DB) {\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn\n\t}\n\t_ = sqlDB.Close()\n}\n\nfunc GetDBWithPath(dbPath string) (*gorm.DB, error) {\n\tdb, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{\n\t\tDisableForeignKeyConstraintWhenMigrating: true,\n\t\tLogger:                                   newLogger(),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsqlDB.SetMaxOpenConns(4)\n\tsqlDB.SetMaxIdleConns(1)\n\tsqlDB.SetConnMaxLifetime(0)\n\tsqlDB.SetConnMaxIdleTime(0)\n\n\tif err := db.Exec(\"PRAGMA journal_mode = WAL;\").Error; err != nil {\n\t\treturn nil, err\n\t}\n\tif err := db.Exec(\"PRAGMA synchronous = NORMAL;\").Error; err != nil {\n\t\treturn nil, err\n\t}\n\tif err := db.Exec(\"PRAGMA busy_timeout = 5000;\").Error; err != nil {\n\t\treturn nil, err\n\t}\n\tif err := db.Exec(\"PRAGMA temp_store = MEMORY;\").Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn db, nil\n}\n\nfunc newLogger() logger.Interface {\n\treturn logger.New(\n\t\tlog.New(os.Stdout, \"\\r\\n\", log.LstdFlags),\n\t\tlogger.Config{\n\t\t\tSlowThreshold:             time.Second,\n\t\t\tLogLevel:                  logger.Silent,\n\t\t\tIgnoreRecordNotFoundError: true,\n\t\t\tColorful:                  false,\n\t\t},\n\t)\n}\n\nfunc createDirWhenNotExist(isDir bool, pathItem string) (string, error) {\n\tcheckPath := pathItem\n\tif !isDir {\n\t\tcheckPath = path.Dir(pathItem)\n\t}\n\tif _, err := os.Stat(checkPath); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(checkPath, os.ModePerm); err != nil {\n\t\t\tglobal.LOG.Errorf(\"mkdir %s failed, err: %v\", checkPath, err)\n\t\t\treturn pathItem, err\n\t\t}\n\t}\n\treturn pathItem, nil\n}\n"
  },
  {
    "path": "agent/utils/compose/compose.go",
    "content": "package compose\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n)\n\nfunc checkCmd() error {\n\tif global.CONF.DockerConfig.Command == \"\" {\n\t\tdockerComposeCmd := common.GetDockerComposeCommand()\n\t\tif dockerComposeCmd == \"\" {\n\t\t\treturn buserr.New(\"ErrDockerComposeCmdNotFound\")\n\t\t}\n\t\tglobal.CONF.DockerConfig.Command = dockerComposeCmd\n\t}\n\treturn nil\n}\n\nfunc getComposeBaseCmd() (string, []string) {\n\tcmdStr := strings.TrimSpace(global.CONF.DockerConfig.Command)\n\tparts := strings.Fields(cmdStr)\n\tif len(parts) == 0 {\n\t\treturn \"\", nil\n\t}\n\treturn parts[0], parts[1:]\n}\n\nfunc Up(filePath string) (string, error) {\n\tif err := checkCmd(); err != nil {\n\t\treturn \"\", err\n\t}\n\tbase, extra := getComposeBaseCmd()\n\targs := append(extra, strings.Fields(loadFiles(filePath))...)\n\targs = append(args, \"up\", \"-d\")\n\treturn cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)\n}\n\nfunc UpWithTask(filePath string, task *task.Task, forcePull bool) error {\n\tif err := pullComposeImages(filePath, forcePull, task); err != nil {\n\t\treturn err\n\t}\n\tbase, extra := getComposeBaseCmd()\n\targs := append(extra, strings.Fields(loadFiles(filePath))...)\n\targs = append(args, \"up\", \"-d\")\n\treturn cmd.NewCommandMgr(cmd.WithTask(*task)).Run(base, args...)\n}\n\nfunc pullComposeImages(filePath string, forcePull bool, task *task.Task) error {\n\timages, err := GetComposeImages(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdockerCLi, err := docker.NewClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, image := range images {\n\t\tif !forcePull {\n\t\t\tif exist, _ := dockerCLi.ImageExists(image); exist {\n\t\t\t\tif task != nil {\n\t\t\t\t\ttask.Log(i18n.GetMsgByKey(\"UseExistImage\"))\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif task != nil {\n\t\t\ttask.Log(i18n.GetWithName(\"PullImageStart\", image))\n\t\t}\n\t\tpullErr := error(nil)\n\t\tif task != nil {\n\t\t\tpullErr = dockerCLi.PullImageWithProcess(task, image)\n\t\t} else {\n\t\t\tpullErr = docker.PullImage(image)\n\t\t}\n\t\tif pullErr != nil {\n\t\t\terrMsg := \"\"\n\t\t\terrOur := pullErr.Error()\n\t\t\tif errOur != \"\" {\n\t\t\t\tif strings.Contains(errOur, \"no such host\") {\n\t\t\t\t\terrMsg = i18n.GetMsgByKey(\"ErrNoSuchHost\") + \":\"\n\t\t\t\t}\n\t\t\t\tif strings.Contains(errOur, \"Error response from daemon\") {\n\t\t\t\t\terrMsg = i18n.GetMsgByKey(\"PullImageTimeout\") + \":\"\n\t\t\t\t}\n\t\t\t}\n\t\t\tmessage := errMsg + errOur\n\t\t\tinstallErr := errors.New(message)\n\t\t\tif task != nil {\n\t\t\t\ttask.LogFailedWithErr(i18n.GetMsgByKey(\"PullImage\"), installErr)\n\t\t\t}\n\t\t\tif exist, _ := dockerCLi.ImageExists(image); !exist {\n\t\t\t\treturn installErr\n\t\t\t}\n\t\t\tif task != nil {\n\t\t\t\ttask.Log(i18n.GetMsgByKey(\"UseExistImage\"))\n\t\t\t}\n\t\t} else if task != nil {\n\t\t\ttask.Log(i18n.GetMsgByKey(\"PullImageSuccess\"))\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc GetComposeImages(filePath string) ([]string, error) {\n\timages, err := getComposeImagesByCommand(filePath)\n\tif err == nil {\n\t\treturn images, nil\n\t}\n\n\tcontent, readErr := os.ReadFile(filePath)\n\tif readErr != nil {\n\t\treturn nil, readErr\n\t}\n\tenv, _ := os.ReadFile(path.Join(path.Dir(filePath), \".env\"))\n\timages, parseErr := docker.GetImagesFromDockerCompose(env, content)\n\tif parseErr != nil {\n\t\treturn nil, fmt.Errorf(\"get compose images failed, cmd err: %v, parse err: %v\", err, parseErr)\n\t}\n\treturn images, nil\n}\n\nfunc getComposeImagesByCommand(filePath string) ([]string, error) {\n\tif err := checkCmd(); err != nil {\n\t\treturn nil, err\n\t}\n\tbase, extra := getComposeBaseCmd()\n\targs := append(extra, strings.Fields(loadFiles(filePath))...)\n\targs = append(args, \"config\", \"--format\", \"json\", \"--no-normalize\")\n\tstdout, err := cmd.NewCommandMgr(cmd.WithTimeout(5*time.Minute)).\n\t\tRunWithStdout(base, args...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"run compose config --format json --no-normalize failed, std: %s, err: %v\", stdout, err)\n\t}\n\n\tvar composeConfig struct {\n\t\tServices map[string]struct {\n\t\t\tImage string `json:\"image\"`\n\t\t} `json:\"services\"`\n\t}\n\tif err = json.Unmarshal([]byte(stdout), &composeConfig); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse compose config json failed, std: %s, err: %v\", stdout, err)\n\t}\n\n\tvar images []string\n\tseen := make(map[string]struct{})\n\tfor _, service := range composeConfig.Services {\n\t\timage := strings.TrimSpace(service.Image)\n\t\tif image == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[image]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[image] = struct{}{}\n\t\timages = append(images, image)\n\t}\n\tif len(images) == 0 {\n\t\treturn nil, errors.New(\"no images found from compose config json\")\n\t}\n\treturn images, nil\n}\n\nfunc Down(filePath string) (string, error) {\n\tif err := checkCmd(); err != nil {\n\t\treturn \"\", err\n\t}\n\tbase, extra := getComposeBaseCmd()\n\targs := append(extra, strings.Fields(loadFiles(filePath))...)\n\targs = append(args, \"down\", \"--remove-orphans\")\n\treturn cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)\n}\n\nfunc Stop(filePath string) (string, error) {\n\tif err := checkCmd(); err != nil {\n\t\treturn \"\", err\n\t}\n\tbase, extra := getComposeBaseCmd()\n\targs := append(extra, strings.Fields(loadFiles(filePath))...)\n\targs = append(args, \"stop\")\n\treturn cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)\n}\n\nfunc Restart(filePath string) (string, error) {\n\tif err := checkCmd(); err != nil {\n\t\treturn \"\", err\n\t}\n\tbase, extra := getComposeBaseCmd()\n\targs := append(extra, strings.Fields(loadFiles(filePath))...)\n\targs = append(args, \"restart\")\n\treturn cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)\n}\n\nfunc Operate(filePath, operation string) (string, error) {\n\tif err := checkCmd(); err != nil {\n\t\treturn \"\", err\n\t}\n\tbase, extra := getComposeBaseCmd()\n\targs := append(extra, strings.Fields(loadFiles(filePath))...)\n\targs = append(args, operation)\n\treturn cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)\n}\n\nfunc DownAndUp(filePath string) (string, error) {\n\tif err := checkCmd(); err != nil {\n\t\treturn \"\", err\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Minute))\n\tbase, extra := getComposeBaseCmd()\n\targsDown := append(extra, strings.Fields(loadFiles(filePath))...)\n\targsDown = append(argsDown, \"down\")\n\tstdout, err := cmdMgr.RunWithStdout(base, argsDown...)\n\tif err != nil {\n\t\treturn stdout, err\n\t}\n\targsUp := append(extra, strings.Fields(loadFiles(filePath))...)\n\targsUp = append(argsUp, \"up\", \"-d\")\n\tstdout, err = cmdMgr.RunWithStdout(base, argsUp...)\n\treturn stdout, err\n}\n\nfunc loadFiles(filePath string) string {\n\tvar fileItem []string\n\tfor _, item := range strings.Split(filePath, \",\") {\n\t\tif len(item) != 0 {\n\t\t\tfileItem = append(fileItem, fmt.Sprintf(\"-f %s\", item))\n\t\t}\n\t}\n\treturn strings.Join(fileItem, \" \")\n}\n"
  },
  {
    "path": "agent/utils/controller/controller.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller/manager\"\n)\n\ntype Controller interface {\n\tName() string\n\tIsActive(serviceName string) (bool, error)\n\tIsEnable(serviceName string) (bool, error)\n\tIsExist(serviceName string) (bool, error)\n\tStatus(serviceName string) (string, error)\n\n\tOperate(operate, serviceName string) error\n\n\tReload() error\n}\n\nfunc New() (Controller, error) {\n\tmanagerOptions := []string{\"systemctl\", \"rc-service\", \"service\"}\n\tfor _, item := range managerOptions {\n\t\tif _, err := exec.LookPath(item); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tswitch item {\n\t\tcase \"systemctl\":\n\t\t\treturn manager.NewSystemd(), nil\n\t\tcase \"rc-service\":\n\t\t\treturn manager.NewOpenrc(), nil\n\t\tcase \"service\":\n\t\t\treturn manager.NewSysvinit(), nil\n\t\t}\n\t}\n\treturn nil, errors.New(\"not support such manager initializatio\")\n}\n\nfunc Handle(operate, serviceName string) error {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn client.Operate(operate, service)\n}\nfunc HandleStart(serviceName string) error {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn Handle(\"start\", service)\n}\nfunc HandleStop(serviceName string) error {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn Handle(\"stop\", service)\n}\nfunc HandleRestart(serviceName string) error {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn Handle(\"restart\", service)\n}\n\nfunc CheckExist(serviceName string) (bool, error) {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tb, er := client.IsExist(service)\n\treturn b, er\n}\nfunc CheckActive(serviceName string) (bool, error) {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn client.IsActive(service)\n}\nfunc CheckEnable(serviceName string) (bool, error) {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn client.IsEnable(service)\n}\n\nfunc Reload() error {\n\tclient, err := New()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn client.Reload()\n}\n\nfunc RestartPanel(core, agent, reload bool) {\n\tclient, err := New()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load client for controller failed, err: %v\", err)\n\t\treturn\n\t}\n\tif reload {\n\t\tif err := client.Reload(); err != nil {\n\t\t\tglobal.LOG.Errorf(\"restart 1panel service failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n\tif core {\n\t\tif err := client.Operate(\"restart\", \"1panel-core\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"restart 1panel core service failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n\tif agent {\n\t\tif err := client.Operate(\"restart\", \"1panel-agent\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"restart 1panel agent service failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc LoadServiceName(keyword string) (string, error) {\n\tclient, err := New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tprocessedName := loadProcessedName(client.Name(), keyword)\n\texist, _ := client.IsExist(processedName)\n\tif exist {\n\t\treturn processedName, nil\n\t}\n\talistName := loadFromPredefined(client, keyword)\n\tif len(alistName) != 0 {\n\t\treturn alistName, nil\n\t}\n\treturn \"\", fmt.Errorf(\"find such service for %s failed\", keyword)\n}\n\nfunc loadProcessedName(mgr, keyword string) string {\n\tkeyword = strings.ToLower(keyword)\n\tif strings.HasSuffix(keyword, \".service.socket\") {\n\t\tkeyword = strings.TrimSuffix(keyword, \".service.socket\") + \".socket\"\n\t}\n\tif mgr != \"systemd\" {\n\t\tkeyword = strings.TrimSuffix(keyword, \".service\")\n\t\treturn keyword\n\t}\n\tif !strings.HasSuffix(keyword, \".service\") && !strings.HasSuffix(keyword, \".socket\") {\n\t\tkeyword += \".service\"\n\t}\n\treturn keyword\n}\n\nfunc loadFromPredefined(mgr Controller, keyword string) string {\n\tpredefinedMap := map[string][]string{\n\t\t\"clam\":         {\"clamav-daemon.service\", \"clamd@scan.service\", \"clamd\"},\n\t\t\"freshclam\":    {\"clamav-freshclam.service\", \"freshclam.service\"},\n\t\t\"fail2ban\":     {\"fail2ban.service\", \"fail2ban\"},\n\t\t\"supervisor\":   {\"supervisord.service\", \"supervisor.service\", \"supervisord\", \"supervisor\"},\n\t\t\"ssh\":          {\"sshd.service\", \"ssh.service\", \"sshd\", \"ssh\"},\n\t\t\"1panel-core\":  {\"1panel-core.service\"},\n\t\t\"1panel-agent\": {\"1panel-agent.service\"},\n\t\t\"docker\":       {\"docker.service\", \"dockerd\"},\n\t\t\"iptables\":     {\"iptables\", \"iptables-services\"},\n\t}\n\tif val, ok := predefinedMap[keyword]; ok {\n\t\tfor _, item := range val {\n\t\t\tif exist, _ := mgr.IsExist(item); exist {\n\t\t\t\treturn item\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// GetServicePath returns the configuration file path for the specified service.\n// If serviceName is empty, it returns the default directory path based on the system's service manager.\n// For non-empty serviceName, it retrieves the exact service file path.\n// Parameters:\n//   - serviceName: Name of the service. If empty, returns the default directory.\n//\n// Returns:\n//   - string: The service configuration file path.\n//   - error: Error if the service manager is unsupported or command execution fails.\nfunc GetServicePath(serviceName string) (string, error) {\n\tif serviceName == \"\" {\n\t\tclient, err := New()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tswitch client.Name() {\n\t\tcase \"systemd\":\n\t\t\treturn \"/etc/systemd/system/\", nil\n\t\tcase \"openrc\", \"sysvinit\":\n\t\t\treturn \"/etc/init.d/\", nil\n\t\tdefault:\n\t\t\treturn \"\", fmt.Errorf(\"unsupported manager: %s\", client.Name())\n\t\t}\n\t}\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tswitch client.Name() {\n\tcase \"systemd\":\n\t\tstdout, err := exec.Command(\"systemctl\", \"show\", \"-p\", \"FragmentPath\", service).Output()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tparts := strings.SplitN(string(stdout), \"=\", 2)\n\t\tif len(parts) != 2 {\n\t\t\treturn \"\", fmt.Errorf(\"unexpected output: %s\", string(stdout))\n\t\t}\n\t\treturn strings.TrimSpace(parts[1]), nil\n\tcase \"openrc\", \"sysvinit\":\n\t\treturn fmt.Sprintf(\"/etc/init.d/%s\", service), nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported manager: %s\", client.Name())\n\t}\n}\n\nfunc SelectInitScript(keyword string) (string, error) {\n\tclient, err := New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tswitch client.Name() {\n\tcase \"systemd\":\n\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".service\"\n\tcase \"openrc\":\n\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".openrc\"\n\tcase \"sysvinit\":\n\t\tif _, err := os.Stat(\"/etc/rc.common\"); err == nil {\n\t\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".prod\"\n\t\t} else {\n\t\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".init\"\n\t\t}\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported manager: %s\", client.Name())\n\t}\n\treturn keyword, nil\n}\n\nfunc GetScriptName(keyword string) (string, error) {\n\tclient, err := New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tswitch client.Name() {\n\tcase \"systemd\":\n\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".service\"\n\tcase \"openrc\", \"sysvinit\":\n\t\tlastDotIdx := strings.LastIndex(keyword, \".\")\n\t\tif lastDotIdx != -1 {\n\t\t\tkeyword = keyword[:lastDotIdx]\n\t\t}\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported manager: %s\", client.Name())\n\t}\n\treturn keyword, nil\n}\n"
  },
  {
    "path": "agent/utils/controller/manager/common.go",
    "content": "package manager\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\nfunc handlerErr(out string, err error) error {\n\tif err != nil {\n\t\tif out != \"\" {\n\t\t\treturn errors.New(out)\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc run(name string, args ...string) (string, error) {\n\treturn cmd.NewCommandMgr(cmd.WithTimeout(10*time.Second)).RunWithStdoutBashCf(\"LANGUAGE=en_US:en %s %s\", name, strings.Join(args, \" \"))\n}\n"
  },
  {
    "path": "agent/utils/controller/manager/openrc.go",
    "content": "package manager\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype Openrc struct{ toolCmd string }\n\nfunc NewOpenrc() *Openrc {\n\treturn &Openrc{toolCmd: \"rc-service\"}\n}\n\nfunc (s *Openrc) Name() string {\n\treturn \"openrc\"\n}\nfunc (s *Openrc) IsActive(serviceName string) (bool, error) {\n\tout, err := cmd.RunDefaultWithStdoutBashCf(\"if service %s status >/dev/null 2>&1; then echo 'active'; else echo 'inactive'; fi\", serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn out == \"active\\n\", nil\n}\nfunc (s *Openrc) IsEnable(serviceName string) (bool, error) {\n\tout, err := cmd.RunDefaultWithStdoutBashCf(\"if ls /etc/rc*.d/S*%s >/dev/null 2>&1; then echo 'enabled'; else echo 'disabled'; fi\", serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn out == \"enabled\\n\", nil\n}\nfunc (s *Openrc) IsExist(serviceName string) (bool, error) {\n\t_, err := os.Stat(filepath.Join(\"/etc/init.d\", serviceName))\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn false, nil\n\t\t}\n\t\treturn false, fmt.Errorf(\"stat /etc/init.d/%s failed: %w\", serviceName, err)\n\t}\n\treturn true, nil\n}\nfunc (s *Openrc) Status(serviceName string) (string, error) {\n\treturn run(s.toolCmd, serviceName, \"status\")\n}\n\nfunc (s *Openrc) Operate(operate, serviceName string) error {\n\tswitch operate {\n\tcase \"enable\":\n\t\treturn handlerErr(run(\"rc-update\", \"add\", serviceName, \"default\"))\n\tcase \"disable\":\n\t\treturn handlerErr(run(\"rc-update\", \"del\", serviceName, \"default\"))\n\tdefault:\n\t\treturn handlerErr(run(s.toolCmd, serviceName, operate))\n\t}\n}\n\nfunc (s *Openrc) Reload() error {\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/controller/manager/snap.go",
    "content": "package manager\n\nimport (\n\t\"strings\"\n)\n\ntype Snap struct{ toolCmd string }\n\nfunc NewSnap() *Snap {\n\treturn &Snap{toolCmd: \"snap\"}\n}\n\nfunc (s *Snap) IsExist(serviceName string) bool {\n\tout, err := run(s.toolCmd, \"services\")\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn strings.Contains(out, serviceName)\n}\n\nfunc (s *Snap) IsActive(serviceName string) bool {\n\tout, err := run(s.toolCmd, \"services\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tlines := strings.Split(out, \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.Contains(line, serviceName) && strings.Contains(line, \"active\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (s *Snap) IsEnable(serviceName string) bool {\n\tout, err := run(s.toolCmd, \"services\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tlines := strings.Split(out, \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.Contains(line, serviceName) && strings.Contains(line, \"enabled\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (s *Snap) Operate(operate, serviceName string) error {\n\tif s.IsExist(serviceName) {\n\t\treturn handlerErr(run(s.toolCmd, operate, serviceName))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/controller/manager/systemd.go",
    "content": "package manager\n\nimport (\n\t\"strings\"\n)\n\ntype Systemd struct{ toolCmd string }\n\nfunc NewSystemd() *Systemd {\n\treturn &Systemd{toolCmd: \"systemctl\"}\n}\n\nfunc (s *Systemd) Name() string {\n\treturn \"systemd\"\n}\nfunc (s *Systemd) IsActive(serviceName string) (bool, error) {\n\tout, err := run(s.toolCmd, \"is-active\", serviceName)\n\tif err != nil && out != \"inactive\\n\" {\n\t\tif NewSnap().IsActive(serviceName) {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, err\n\t}\n\treturn out == \"active\\n\", nil\n}\n\nfunc (s *Systemd) IsEnable(serviceName string) (bool, error) {\n\tout, err := run(s.toolCmd, \"is-enabled\", serviceName)\n\tif out == \"alias\\n\" && serviceName == \"sshd.service\" {\n\t\treturn s.IsEnable(\"ssh\")\n\t}\n\tif err != nil && out != \"disabled\\n\" {\n\t\tif NewSnap().IsEnable(serviceName) {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, err\n\t}\n\treturn out == \"enabled\\n\", nil\n}\n\nfunc (s *Systemd) IsExist(serviceName string) (bool, error) {\n\tout, err := run(s.toolCmd, \"is-enabled\", serviceName)\n\tif err != nil && out != \"enabled\\n\" {\n\t\tif strings.Contains(out, \"disabled\") {\n\t\t\treturn true, err\n\t\t}\n\t\tif NewSnap().IsExist(serviceName) {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, err\n\t}\n\treturn true, err\n}\n\nfunc (s *Systemd) Status(serviceName string) (string, error) {\n\treturn run(s.toolCmd, \"status\", serviceName)\n}\nfunc (s *Systemd) Operate(operate, serviceName string) error {\n\tout, err := run(s.toolCmd, operate, serviceName)\n\tif err != nil {\n\t\tif serviceName == \"sshd\" && strings.Contains(out, \"alias name or linked unit file\") {\n\t\t\treturn s.Operate(operate, \"ssh\")\n\t\t}\n\t\tif err := NewSnap().Operate(operate, serviceName); err == nil {\n\t\t\treturn nil\n\t\t}\n\t\treturn handlerErr(run(s.toolCmd, operate, serviceName))\n\t}\n\treturn nil\n}\n\nfunc (s *Systemd) Reload() error {\n\tout, err := run(s.toolCmd, \"daemon-reload\")\n\treturn handlerErr(out, err)\n}\n"
  },
  {
    "path": "agent/utils/controller/manager/sysvinit.go",
    "content": "package manager\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype Sysvinit struct{ toolCmd string }\n\nfunc NewSysvinit() *Sysvinit {\n\treturn &Sysvinit{toolCmd: \"service\"}\n}\n\nfunc (s *Sysvinit) Name() string {\n\treturn \"sysvinit\"\n}\nfunc (s *Sysvinit) IsActive(serviceName string) (bool, error) {\n\tout, err := cmd.RunDefaultWithStdoutBashCf(\"if service %s status >/dev/null 2>&1; then echo 'active'; else echo 'inactive'; fi\", serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn out == \"active\\n\", nil\n}\nfunc (s *Sysvinit) IsEnable(serviceName string) (bool, error) {\n\tout, err := cmd.RunDefaultWithStdoutBashCf(\"if ls /etc/rc*.d/S*%s >/dev/null 2>&1; then echo 'enabled'; else echo 'disabled'; fi\", serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn out == \"enabled\\n\", nil\n}\nfunc (s *Sysvinit) IsExist(serviceName string) (bool, error) {\n\t_, err := os.Stat(filepath.Join(\"/etc/init.d\", serviceName))\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn false, nil\n\t\t}\n\t\treturn false, fmt.Errorf(\"stat /etc/init.d/%s failed: %w\", serviceName, err)\n\t}\n\treturn true, nil\n}\nfunc (s *Sysvinit) Status(serviceName string) (string, error) {\n\treturn run(s.toolCmd, serviceName, \"status\")\n}\n\nfunc (s *Sysvinit) Operate(operate, serviceName string) error {\n\treturn handlerErr(run(s.toolCmd, serviceName, operate))\n}\n\nfunc (s *Sysvinit) Reload() error {\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/convert/convert.go",
    "content": "package convert\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto/response\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype FormatOption struct {\n\tType  string\n\tCodec string\n}\n\nvar FormatMap = map[string]FormatOption{\n\t// images\n\t\"png\":  {Type: \"image\", Codec: \"png\"},\n\t\"jpg\":  {Type: \"image\", Codec: \"mjpeg\"},\n\t\"jpeg\": {Type: \"image\", Codec: \"mjpeg\"},\n\t\"webp\": {Type: \"image\", Codec: \"libwebp\"},\n\t\"gif\":  {Type: \"image\", Codec: \"gif\"},\n\t\"bmp\":  {Type: \"image\", Codec: \"bmp\"},\n\t\"tiff\": {Type: \"image\", Codec: \"tiff\"},\n\n\t// videos\n\t\"mp4\": {Type: \"video\", Codec: \"libx264\"},\n\t\"avi\": {Type: \"video\", Codec: \"libx264\"},\n\t\"mov\": {Type: \"video\", Codec: \"libx264\"},\n\t\"mkv\": {Type: \"video\", Codec: \"libx264\"},\n\n\t// audios\n\t\"mp3\":  {Type: \"audio\", Codec: \"libmp3lame\"},\n\t\"wav\":  {Type: \"audio\", Codec: \"pcm_s16le\"},\n\t\"flac\": {Type: \"audio\", Codec: \"flac\"},\n\t\"aac\":  {Type: \"audio\", Codec: \"aac\"},\n}\n\nfunc hasFfmpeg() (string, bool) {\n\tffmpegPath, err := exec.LookPath(\"ffmpeg\")\n\treturn ffmpegPath, err == nil\n}\n\nfunc MediaFile(inputFile, outputFile, outputFormat string, deleteSource bool) (state string, err error) {\n\tstatus := \"FAILED\"\n\tmsg := \"\"\n\tffmpegPath, flag := hasFfmpeg()\n\tif !flag {\n\t\treturn status, fmt.Errorf(\"ffmpeg not found, cannot convert file\")\n\t}\n\tlogFile, logErr := os.OpenFile(filepath.Join(global.Dir.ConvertLogDir, \"convert.log\"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)\n\tallLogFile, allErr := os.OpenFile(filepath.Join(global.Dir.ConvertLogDir, \"convert-all.log\"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)\n\n\tif logErr != nil || allErr != nil {\n\t\treturn status, fmt.Errorf(\"cannot open log file: %w\", err)\n\t}\n\tdefer logFile.Close()\n\targs, fileType, err := buildFFmpegArgs(inputFile, outputFile, outputFormat)\n\tif err != nil {\n\t\treturn status, fmt.Errorf(\"FFmpeg args failed: %w\", err)\n\t}\n\tctx := context.Background()\n\tvar cancel context.CancelFunc\n\tctx, cancel = context.WithTimeout(ctx, 5*time.Minute)\n\tdefer cancel()\n\tcmdr := exec.CommandContext(ctx, ffmpegPath, args...)\n\tcmdr.Env = append(os.Environ(),\n\t\t\"PATH=\"+filepath.Dir(ffmpegPath)+\":\"+os.Getenv(\"PATH\"),\n\t\t\"LD_LIBRARY_PATH=/usr/local/lib:\"+os.Getenv(\"LD_LIBRARY_PATH\"),\n\t)\n\tvar buf bytes.Buffer\n\tcmdr.Stdout = &buf\n\tcmdr.Stderr = &buf\n\terr = cmdr.Run()\n\tlogStr := buf.String()\n\n\tstat, statErr := os.Stat(outputFile)\n\tif err != nil || statErr != nil || stat.Size() == 0 {\n\t\tstatus = \"FAILED\"\n\t\tmsg = extractFFmpegError(logStr)\n\t\t_ = os.Remove(outputFile)\n\t} else {\n\t\tstatus = \"SUCCESS\"\n\t\tmsg = \"SUCCESS\"\n\t}\n\n\tentry := response.FileConvertLog{\n\t\tDate:    time.Now().Format(\"2006-01-02 15:04:05\"),\n\t\tType:    fileType,\n\t\tLog:     fmt.Sprintf(\"%s -> %s\", inputFile, outputFile),\n\t\tStatus:  status,\n\t\tMessage: msg,\n\t}\n\t_ = appendJSONLog(logFile, entry)\n\n\tallLogEntry := fmt.Sprintf(\"[%s] %s %s -> %s [%s]: %s\\n\",\n\t\ttime.Now().Format(\"2006-01-02 15:04:05\"),\n\t\tfileType, inputFile, outputFile, status, logStr)\n\t_ = appendLog(allLogFile, allLogEntry)\n\tif err == nil && deleteSource {\n\t\t_ = os.Remove(inputFile)\n\t}\n\treturn status, nil\n}\n\nfunc buildFFmpegArgs(inputFile, outputFile, outputFormat string) ([]string, string, error) {\n\targs := []string{\"-y\", \"-i\", inputFile}\n\topt, ok := FormatMap[outputFormat]\n\tif !ok {\n\t\treturn nil, \"\", fmt.Errorf(\"unsupported format: %s\", outputFormat)\n\t}\n\n\tswitch opt.Type {\n\tcase \"image\":\n\t\tswitch outputFormat {\n\t\tcase \"webp\":\n\t\t\targs = append(args, \"-c:v\", \"libwebp\", \"-lossless\", \"0\", \"-q:v\", \"75\")\n\t\tcase \"png\", \"gif\", \"jpg\", \"jpeg\", \"bmp\", \"tiff\":\n\t\t\targs = append(args, \"-c:v\", opt.Codec)\n\t\t}\n\n\tcase \"video\":\n\t\targs = append(args, \"-c:v\", opt.Codec, \"-preset\", \"fast\", \"-crf\", \"23\", \"-c:a\", \"aac\", \"-b:a\", \"192k\")\n\n\tcase \"audio\":\n\t\targs = append(args, \"-c:a\", opt.Codec, \"-b:a\", \"192k\")\n\n\tdefault:\n\t\treturn nil, opt.Type, fmt.Errorf(\"unsupported media type: %s\", opt.Type)\n\t}\n\n\targs = append(args, outputFile)\n\treturn args, opt.Type, nil\n}\n\nfunc appendLog(f *os.File, content string) error {\n\t_, err := f.WriteString(content)\n\treturn err\n}\n\nfunc appendJSONLog(f *os.File, entry response.FileConvertLog) error {\n\tdata, err := json.Marshal(entry)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err := f.WriteString(string(data) + \"\\n\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc extractFFmpegError(logStr string) string {\n\tpriority := []string{\"Error\", \"Invalid\", \"failed\", \"No \"}\n\tmatches := make(map[string]string)\n\tlines := strings.Split(strings.TrimSpace(logStr), \"\\n\")\n\tfor i := 0; i < len(lines); i++ {\n\t\tline := strings.TrimSpace(lines[i])\n\t\tfor _, kw := range priority {\n\t\t\tif _, ok := matches[kw]; !ok && strings.Contains(line, kw) {\n\t\t\t\tmatches[kw] = line\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, kw := range priority {\n\t\tif line, ok := matches[kw]; ok {\n\t\t\treturn line\n\t\t}\n\t}\n\n\tif len(lines) > 0 {\n\t\treturn lines[len(lines)-1]\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "agent/utils/copier/copier.go",
    "content": "package copier\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/pkg/errors\"\n)\n\nfunc Copy(to, from interface{}) error {\n\tb, err := json.Marshal(from)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"marshal from data err\")\n\t}\n\tif err = json.Unmarshal(b, to); err != nil {\n\t\treturn errors.Wrap(err, \"unmarshal to data err\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/csv_export/ssh_log.go",
    "content": "package csvexport\n\nimport (\n\t\"encoding/csv\"\n\t\"os\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n)\n\nfunc ExportSSHLogs(filename string, logs []dto.SSHHistory) error {\n\tfile, err := os.Create(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\twriter := csv.NewWriter(file)\n\tdefer writer.Flush()\n\n\tif err := writer.Write([]string{\n\t\ti18n.GetMsgByKey(\"ExportIP\"),\n\t\ti18n.GetMsgByKey(\"ExportArea\"),\n\t\ti18n.GetMsgByKey(\"ExportPort\"),\n\t\ti18n.GetMsgByKey(\"ExportAuthMode\"),\n\t\ti18n.GetMsgByKey(\"ExportUser\"),\n\t\ti18n.GetMsgByKey(\"ExportStatus\"),\n\t\ti18n.GetMsgByKey(\"ExportDate\"),\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\tfor _, log := range logs {\n\t\trecord := []string{\n\t\t\tlog.Address,\n\t\t\tlog.Area,\n\t\t\tlog.Port,\n\t\t\tlog.AuthMode,\n\t\t\tlog.User,\n\t\t\tlog.Status,\n\t\t\tlog.Date.Format(constant.DateTimeLayout),\n\t\t}\n\t\tif err := writer.Write(record); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/docker/compose.go",
    "content": "package docker\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/compose-spec/compose-go/v2/loader\"\n\t\"github.com/compose-spec/compose-go/v2/types\"\n\t\"github.com/docker/compose/v2/pkg/api\"\n\t\"github.com/joho/godotenv\"\n\t\"gopkg.in/yaml.v3\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n)\n\ntype ComposeService struct {\n\tapi.Compose\n}\n\nfunc GetComposeProject(projectName, workDir string, yml []byte, env []byte, skipNormalization bool) (*types.Project, error) {\n\tvar configFiles []types.ConfigFile\n\tconfigFiles = append(configFiles, types.ConfigFile{\n\t\tFilename: \"docker-compose.yml\",\n\t\tContent:  yml},\n\t)\n\tenvMap, err := godotenv.UnmarshalBytes(env)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdetails := types.ConfigDetails{\n\t\tWorkingDir:  workDir,\n\t\tConfigFiles: configFiles,\n\t\tEnvironment: envMap,\n\t}\n\tprojectName = strings.ToLower(projectName)\n\tprojectName = re.GetRegex(re.ComposeDisallowedCharsPattern).ReplaceAllString(projectName, \"\")\n\tproject, err := loader.LoadWithContext(context.Background(), details, func(options *loader.Options) {\n\t\toptions.SetProjectName(projectName, true)\n\t\toptions.ResolvePaths = true\n\t\toptions.SkipNormalization = skipNormalization\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tproject.ComposeFiles = []string{path.Join(workDir, \"docker-compose.yml\")}\n\treturn project, nil\n}\n\ntype ComposeProject struct {\n\tVersion  string\n\tServices map[string]Service `yaml:\"services\"`\n}\n\ntype Service struct {\n\tImage       string      `yaml:\"image\"`\n\tEnvironment Environment `yaml:\"environment\"`\n\tVolumes     []string    `yaml:\"volumes\"`\n\tExtraHosts  []string    `yaml:\"extra_hosts\"`\n\tRestart     string      `yaml:\"restart\"`\n}\n\ntype Environment struct {\n\tVariables map[string]string\n}\n\nfunc (e *Environment) UnmarshalYAML(value *yaml.Node) error {\n\te.Variables = make(map[string]string)\n\tswitch value.Kind {\n\tcase yaml.MappingNode:\n\t\tfor i := 0; i < len(value.Content); i += 2 {\n\t\t\tkey := value.Content[i].Value\n\t\t\tval := value.Content[i+1].Value\n\t\t\te.Variables[key] = val\n\t\t}\n\tcase yaml.SequenceNode:\n\t\tfor _, item := range value.Content {\n\t\t\tvar kv string\n\t\t\tif err := item.Decode(&kv); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tparts := strings.SplitN(kv, \"=\", 2)\n\t\t\tif len(parts) == 2 {\n\t\t\t\te.Variables[parts[0]] = parts[1]\n\t\t\t} else {\n\t\t\t\te.Variables[parts[0]] = \"\"\n\t\t\t}\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported environment format\")\n\t}\n\treturn nil\n}\n\nfunc GetImagesFromDockerCompose(env, yml []byte) ([]string, error) {\n\tenvVars, err := loadEnvFile(env)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load env failed: %v\", err)\n\t}\n\n\tvar compose ComposeProject\n\tif err := yaml.Unmarshal(yml, &compose); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse docker-compose file failed: %v\", err)\n\t}\n\n\tvar images []string\n\tfor _, service := range compose.Services {\n\t\tif service.Image != \"\" {\n\t\t\tresolvedImage := replaceEnvVars(service.Image, envVars)\n\t\t\timages = append(images, resolvedImage)\n\t\t}\n\t}\n\n\treturn images, nil\n}\n\nfunc loadEnvFile(env []byte) (map[string]string, error) {\n\tenvVars := make(map[string]string)\n\n\tscanner := bufio.NewScanner(bytes.NewReader(env))\n\tfor scanner.Scan() {\n\t\tline := strings.TrimSpace(scanner.Text())\n\n\t\tif line == \"\" || strings.HasPrefix(line, \"#\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tparts := strings.SplitN(line, \"=\", 2)\n\t\tif len(parts) == 2 {\n\t\t\tkey := strings.TrimSpace(parts[0])\n\t\t\tvalue := strings.TrimSpace(parts[1])\n\t\t\tvalue = strings.Trim(value, `\"'`)\n\t\t\tenvVars[key] = value\n\t\t}\n\t}\n\n\treturn envVars, scanner.Err()\n}\n\nfunc replaceEnvVars(input string, envVars map[string]string) string {\n\treturn re.GetRegex(re.ComposeEnvVarPattern).ReplaceAllStringFunc(input, func(match string) string {\n\t\tvarName := match[2 : len(match)-1]\n\t\tif value, exists := envVars[varName]; exists {\n\t\t\treturn value\n\t\t}\n\t\treturn match\n\t})\n}\n"
  },
  {
    "path": "agent/utils/docker/docker.go",
    "content": "package docker\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/docker/cli/cli/config\"\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/api/types/filters\"\n\t\"github.com/docker/docker/api/types/image\"\n\t\"github.com/docker/docker/api/types/network\"\n\t\"github.com/docker/docker/api/types/registry\"\n\t\"github.com/docker/docker/client\"\n)\n\nfunc NewDockerClient() (*client.Client, error) {\n\tvar settingItem model.Setting\n\t_ = global.DB.Where(\"key = ?\", \"DockerSockPath\").First(&settingItem).Error\n\tif len(settingItem.Value) == 0 {\n\t\tsettingItem.Value = \"unix:///var/run/docker.sock\"\n\t}\n\tcli, err := client.NewClientWithOpts(client.FromEnv, client.WithHost(settingItem.Value), client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cli, nil\n}\n\nfunc NewClient() (Client, error) {\n\tvar settingItem model.Setting\n\t_ = global.DB.Where(\"key = ?\", \"DockerSockPath\").First(&settingItem).Error\n\tif len(settingItem.Value) == 0 {\n\t\tsettingItem.Value = \"unix:///var/run/docker.sock\"\n\t}\n\tcli, err := client.NewClientWithOpts(client.FromEnv, client.WithHost(settingItem.Value), client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\treturn Client{}, err\n\t}\n\n\treturn Client{\n\t\tcli: cli,\n\t}, nil\n}\n\nfunc NewClientWithExist(cli *client.Client) Client {\n\treturn Client{\n\t\tcli: cli,\n\t}\n}\n\ntype Client struct {\n\tcli *client.Client\n}\n\nfunc (c Client) Close() {\n\t_ = c.cli.Close()\n}\n\nfunc (c Client) ListContainersByName(names []string) ([]container.Summary, error) {\n\tvar (\n\t\toptions  container.ListOptions\n\t\tnamesMap = make(map[string]bool)\n\t\tres      []container.Summary\n\t)\n\toptions.All = true\n\tif len(names) > 0 {\n\t\tvar array []filters.KeyValuePair\n\t\tfor _, n := range names {\n\t\t\tnamesMap[\"/\"+n] = true\n\t\t\tarray = append(array, filters.Arg(\"name\", n))\n\t\t}\n\t\toptions.Filters = filters.NewArgs(array...)\n\t}\n\tcontainers, err := c.cli.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, con := range containers {\n\t\tif _, ok := namesMap[con.Names[0]]; ok {\n\t\t\tres = append(res, con)\n\t\t}\n\t}\n\treturn res, nil\n}\nfunc (c Client) ListAllContainers() ([]container.Summary, error) {\n\tvar (\n\t\toptions container.ListOptions\n\t)\n\toptions.All = true\n\tcontainers, err := c.cli.ContainerList(context.Background(), options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn containers, nil\n}\n\nfunc (c Client) CreateNetwork(name string) error {\n\t_, err := c.cli.NetworkCreate(context.Background(), name, network.CreateOptions{\n\t\tDriver:     \"bridge\",\n\t\tEnableIPv6: new(bool),\n\t})\n\treturn err\n}\n\nfunc (c Client) DeleteImage(imageID string) error {\n\tif _, err := c.cli.ImageRemove(context.Background(), imageID, image.RemoveOptions{Force: true}); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\nfunc (c Client) ImageExists(imageID string) (bool, error) {\n\t_, err := c.cli.ImageInspect(context.Background(), imageID)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn true, nil\n}\n\nfunc (c Client) GetImageIDByName(imageName string) (string, error) {\n\tfilter := filters.NewArgs()\n\tfilter.Add(\"reference\", imageName)\n\tlist, err := c.cli.ImageList(context.Background(), image.ListOptions{\n\t\tFilters: filter,\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(list) > 0 {\n\t\treturn list[0].ID, nil\n\t}\n\treturn \"\", nil\n}\n\nfunc (c Client) NetworkExist(name string) bool {\n\tvar options network.ListOptions\n\toptions.Filters = filters.NewArgs(filters.Arg(\"name\", name))\n\tnetworks, err := c.cli.NetworkList(context.Background(), options)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn len(networks) > 0\n}\n\nfunc CreateDefaultDockerNetwork() error {\n\tcli, err := NewClient()\n\tif err != nil {\n\t\tglobal.LOG.Warnf(\"init docker client error %s\", err.Error())\n\t\treturn err\n\t}\n\n\tdefer cli.Close()\n\tif !cli.NetworkExist(\"1panel-network\") {\n\t\tif err := cli.CreateNetwork(\"1panel-network\"); err != nil {\n\t\t\tglobal.LOG.Warnf(\"create default docker network  error %s\", err.Error())\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc setLog(id, newLastLine string, task *task.Task) error {\n\tdata, err := os.ReadFile(task.Task.LogFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read file: %v\", err)\n\t}\n\tlines := strings.Split(string(data), \"\\n\")\n\texist := false\n\tfor index, line := range lines {\n\t\tif strings.Contains(line, id) {\n\t\t\ttimeStr := time.Now().Format(\"2006/01/02 15:04:05\")\n\t\t\tlines[index] = timeStr + \" \" + newLastLine\n\t\t\texist = true\n\t\t\tbreak\n\t\t} else {\n\t\t\tlines[index] = strings.TrimSpace(lines[index])\n\t\t}\n\t}\n\tif !exist {\n\t\ttask.Log(newLastLine)\n\t\treturn nil\n\t}\n\toutput := strings.Join(lines, \"\\n\")\n\t_ = os.WriteFile(task.Task.LogFile, []byte(output), os.ModePerm)\n\treturn nil\n}\n\nfunc (c Client) PullImageWithProcessAndOptions(task *task.Task, imageName string, options image.PullOptions) error {\n\tout, err := c.cli.ImagePull(context.Background(), imageName, options)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer out.Close()\n\tdecoder := json.NewDecoder(out)\n\tfor {\n\t\tvar progress map[string]interface{}\n\t\tif err = decoder.Decode(&progress); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tstatus, _ := progress[\"status\"].(string)\n\t\tif status == \"Downloading\" || status == \"Extracting\" {\n\t\t\tlogProcess(progress, task)\n\t\t}\n\t\tif status == \"Pull complete\" || status == \"Download complete\" {\n\t\t\tid, _ := progress[\"id\"].(string)\n\t\t\tprogressStr := fmt.Sprintf(\"%s %s\", status, id)\n\t\t\t_ = setLog(id, progressStr, task)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c Client) PushImageWithProcessAndOptions(task *task.Task, imageName string, options image.PushOptions) error {\n\tout, err := c.cli.ImagePush(context.Background(), imageName, options)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer out.Close()\n\tdecoder := json.NewDecoder(out)\n\tfor {\n\t\tvar progress map[string]interface{}\n\t\tif err = decoder.Decode(&progress); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif msg, ok := progress[\"errorDetail\"]; ok {\n\t\t\treturn fmt.Errorf(\"image push failed, err: %v\", msg)\n\t\t}\n\t\tif msg, ok := progress[\"error\"]; ok {\n\t\t\treturn fmt.Errorf(\"image push failed, err: %v\", msg)\n\t\t}\n\t\tstatus, _ := progress[\"status\"].(string)\n\t\tswitch status {\n\t\tcase \"Pushing\":\n\t\t\tlogProcess(progress, task)\n\t\tcase \"Pushed\":\n\t\t\tid, _ := progress[\"id\"].(string)\n\t\t\tprogressStr := fmt.Sprintf(\"%s %s\", status, id)\n\t\t\t_ = setLog(id, progressStr, task)\n\t\tdefault:\n\t\t\tprogressStr, _ := json.Marshal(progress)\n\t\t\ttask.Log(string(progressStr))\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c Client) BuildImageWithProcessAndOptions(task *task.Task, tar io.ReadCloser, options types.ImageBuildOptions) error {\n\tout, err := c.cli.ImageBuild(context.Background(), tar, options)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer out.Body.Close()\n\tdecoder := json.NewDecoder(out.Body)\n\tfor {\n\t\tvar progress map[string]interface{}\n\t\tif err = decoder.Decode(&progress); err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif msg, ok := progress[\"errorDetail\"]; ok {\n\t\t\treturn fmt.Errorf(\"image build failed, err: %v\", msg)\n\t\t}\n\t\tif msg, ok := progress[\"error\"]; ok {\n\t\t\treturn fmt.Errorf(\"image build failed, err: %v\", msg)\n\t\t}\n\t\tstatus, _ := progress[\"status\"].(string)\n\t\tstream, _ := progress[\"stream\"].(string)\n\t\tif len(status) == 0 && len(stream) != 0 {\n\t\t\tif stream != \"\\n\" {\n\t\t\t\ttask.Log(stream)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tswitch status {\n\t\tcase \"Downloading\", \"Extracting\":\n\t\t\tlogProcess(progress, task)\n\t\tcase \"Pull complete\", \"Download complete\", \"Verifying Checksum\":\n\t\t\tid, _ := progress[\"id\"].(string)\n\t\t\tprogressStr := fmt.Sprintf(\"%s %s\", status, id)\n\t\t\t_ = setLog(id, progressStr, task)\n\t\tdefault:\n\t\t\tprogressStr, _ := json.Marshal(progress)\n\t\t\ttask.Log(string(progressStr))\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c Client) PullImageWithProcess(task *task.Task, imageName string) error {\n\toptions := image.PullOptions{}\n\tif authStr, ok := loadRegistryAuthFromDockerConfig(imageName); ok {\n\t\toptions.RegistryAuth = authStr\n\t}\n\treturn c.PullImageWithProcessAndOptions(task, imageName, options)\n}\n\nfunc logProcess(progress map[string]interface{}, task *task.Task) {\n\tstatus, _ := progress[\"status\"].(string)\n\tid, _ := progress[\"id\"].(string)\n\tprogressItem, _ := progress[\"progress\"].(string)\n\tprogressStr := \"\"\n\tprogressStr = fmt.Sprintf(\"%s %s %s\", status, id, progressItem)\n\t_ = setLog(id, progressStr, task)\n}\n\nfunc PullImage(imageName string) error {\n\tcli, err := NewDockerClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cli.Close()\n\toptions := image.PullOptions{}\n\tif authStr, ok := loadRegistryAuthFromDockerConfig(imageName); ok {\n\t\toptions.RegistryAuth = authStr\n\t}\n\tif _, err := cli.ImagePull(context.Background(), imageName, options); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc loadRegistryAuthFromDockerConfig(imageName string) (string, bool) {\n\tregistryHost, hasRegistry := extractRegistryHost(imageName)\n\tcfg := config.LoadDefaultConfigFile(io.Discard)\n\tif cfg == nil {\n\t\treturn \"\", false\n\t}\n\tcandidates := make([]string, 0)\n\tif hasRegistry {\n\t\tcandidates = append(candidates, registryHost, \"https://\"+registryHost, \"http://\"+registryHost)\n\t}\n\tif !hasRegistry || isDockerHubRegistry(registryHost) {\n\t\tcandidates = append(candidates,\n\t\t\t\"https://index.docker.io/v1/\",\n\t\t\t\"index.docker.io\",\n\t\t\t\"docker.io\",\n\t\t\t\"registry-1.docker.io\",\n\t\t\t\"https://registry-1.docker.io\",\n\t\t)\n\t}\n\tseen := make(map[string]struct{})\n\tfor _, key := range candidates {\n\t\tif key == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[key]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[key] = struct{}{}\n\t\tauth, err := cfg.GetAuthConfig(key)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif auth.Username == \"\" && auth.Password == \"\" && auth.Auth == \"\" && auth.IdentityToken == \"\" && auth.RegistryToken == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tauthStr, err := registry.EncodeAuthConfig(registry.AuthConfig{\n\t\t\tUsername:      auth.Username,\n\t\t\tPassword:      auth.Password,\n\t\t\tAuth:          auth.Auth,\n\t\t\tServerAddress: auth.ServerAddress,\n\t\t\tIdentityToken: auth.IdentityToken,\n\t\t\tRegistryToken: auth.RegistryToken,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn \"\", false\n\t\t}\n\t\treturn authStr, true\n\t}\n\treturn \"\", false\n}\n\nfunc isDockerHubRegistry(host string) bool {\n\tswitch normalizeRegistryHost(host) {\n\tcase \"docker.io\", \"index.docker.io\", \"registry-1.docker.io\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc extractRegistryHost(imageName string) (string, bool) {\n\tparts := strings.Split(imageName, \"/\")\n\tif len(parts) < 2 {\n\t\treturn \"\", false\n\t}\n\tfirst := parts[0]\n\tif strings.Contains(first, \".\") || strings.Contains(first, \":\") || first == \"localhost\" {\n\t\treturn normalizeRegistryHost(first), true\n\t}\n\treturn \"\", false\n}\n\nfunc normalizeRegistryHost(registryKey string) string {\n\tkey := strings.TrimSpace(registryKey)\n\tif key == \"\" {\n\t\treturn \"\"\n\t}\n\tkey = strings.TrimPrefix(key, \"http://\")\n\tkey = strings.TrimPrefix(key, \"https://\")\n\tkey = strings.Trim(key, \"/\")\n\tif strings.Contains(key, \"/\") {\n\t\tkey = strings.SplitN(key, \"/\", 2)[0]\n\t}\n\treturn strings.ToLower(key)\n}\n"
  },
  {
    "path": "agent/utils/email/smtp_sender.go",
    "content": "package email\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"mime\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/smtp\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype SMTPConfig struct {\n\tHost       string\n\tPort       int\n\tSender     string\n\tUsername   string\n\tPassword   string\n\tFrom       string\n\tEncryption string\n\tRecipient  string\n}\n\ntype EmailMessage struct {\n\tSubject string\n\tBody    string\n\tIsHTML  bool\n}\n\ntype loginAuth struct {\n\tusername, password string\n}\n\nfunc LoginAuth(username, password string) smtp.Auth {\n\treturn &loginAuth{username, password}\n}\n\nfunc (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {\n\treturn \"LOGIN\", []byte{}, nil\n}\n\nfunc (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {\n\tif more {\n\t\tswitch string(fromServer) {\n\t\tcase \"Username:\":\n\t\t\treturn []byte(a.username), nil\n\t\tcase \"Password:\":\n\t\t\treturn []byte(a.password), nil\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown server challenge: %s\", fromServer)\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nfunc SendMail(config SMTPConfig, message EmailMessage, transport *http.Transport) error {\n\tif err := validateConfig(config); err != nil {\n\t\treturn err\n\t}\n\n\taddr := fmt.Sprintf(\"%s:%d\", config.Host, config.Port)\n\ttoList := parseRecipients(config.Recipient)\n\n\tmsg, err := buildMessage(config, message, toList)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch strings.ToLower(config.Encryption) {\n\tcase \"ssl\":\n\t\treturn sendWithSSL(config, addr, toList, msg, transport)\n\tcase \"starttls\", \"tls\":\n\t\treturn sendWithStartTLS(config, addr, toList, msg, transport)\n\tcase \"none\":\n\t\treturn sendPlaintext(config, addr, toList, msg, transport)\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported encryption type: %s\", config.Encryption)\n\t}\n}\n\nfunc validateConfig(config SMTPConfig) error {\n\tif config.Host == \"\" {\n\t\treturn fmt.Errorf(\"SMTP host is required\")\n\t}\n\tif config.Port <= 0 {\n\t\treturn fmt.Errorf(\"invalid SMTP port: %d\", config.Port)\n\t}\n\tif config.Username == \"\" {\n\t\treturn fmt.Errorf(\"SMTP username is required\")\n\t}\n\tif config.Password == \"\" {\n\t\treturn fmt.Errorf(\"SMTP password is required\")\n\t}\n\tif config.From == \"\" {\n\t\treturn fmt.Errorf(\"SMTP from address is required\")\n\t}\n\tif config.Recipient == \"\" {\n\t\treturn fmt.Errorf(\"SMTP recipient is required\")\n\t}\n\tif !isValidEncryption(config.Encryption) {\n\t\treturn fmt.Errorf(\"invalid encryption type: %s. Allowed: ssl, starttls, none\", config.Encryption)\n\t}\n\treturn nil\n}\n\nfunc isValidEncryption(enc string) bool {\n\tenc = strings.ToLower(enc)\n\treturn enc == \"ssl\" || enc == \"starttls\" || enc == \"none\" || enc == \"tls\"\n}\n\nfunc parseRecipients(recipient string) []string {\n\ttoList := strings.Split(recipient, \",\")\n\tfor i := range toList {\n\t\ttoList[i] = strings.TrimSpace(toList[i])\n\t}\n\treturn toList\n}\n\nfunc buildMessage(config SMTPConfig, message EmailMessage, toList []string) (string, error) {\n\theaders := make(map[string]string)\n\theaders[\"From\"] = config.From\n\tencodedSubject := mime.BEncoding.Encode(\"UTF-8\", message.Subject)\n\theaders[\"Subject\"] = encodedSubject\n\theaders[\"To\"] = strings.Join(toList, \",\")\n\theaders[\"Date\"] = time.Now().UTC().Format(time.RFC1123Z)\n\n\tif message.IsHTML {\n\t\theaders[\"MIME-version\"] = \"1.0\"\n\t\theaders[\"Content-Type\"] = \"text/html; charset=\\\"UTF-8\\\"\"\n\t} else {\n\t\theaders[\"Content-Type\"] = \"text/plain; charset=\\\"UTF-8\\\"\"\n\t}\n\n\tvar msg strings.Builder\n\tfor k, v := range headers {\n\t\tif !isValidHeader(k, v) {\n\t\t\treturn \"\", fmt.Errorf(\"invalid header: %s: %s\", k, v)\n\t\t}\n\t\tmsg.WriteString(fmt.Sprintf(\"%s: %s\\r\\n\", k, v))\n\t}\n\tmsg.WriteString(\"\\r\\n\" + message.Body)\n\n\treturn msg.String(), nil\n}\n\nfunc isValidHeader(key, value string) bool {\n\treturn !strings.ContainsAny(key, \"\\r\\n\") && !strings.ContainsAny(value, \"\\r\\n\")\n}\n\nfunc sendWithSSL(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error {\n\tvar err error\n\tvar conn net.Conn\n\tif transport != nil && transport.DialContext != nil {\n\t\tconn, err = transport.DialContext(context.Background(), \"tcp\", addr)\n\t} else {\n\t\tconn, err = net.Dial(\"tcp\", addr)\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to SMTP server: %w\", err)\n\t}\n\tdefer conn.Close()\n\ttlsConfig := &tls.Config{\n\t\tServerName: config.Host,\n\t}\n\ttlsConn := tls.Client(conn, tlsConfig)\n\tif err := tlsConn.Handshake(); err != nil {\n\t\treturn fmt.Errorf(\"TLS handshake failed: %w\", err)\n\t}\n\n\tclient, err := smtp.NewClient(tlsConn, config.Host)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create SMTP client: %w\", err)\n\t}\n\tdefer client.Quit()\n\tif err := tryAuth(client, config.Username, config.Password, config.Host); err != nil {\n\t\treturn fmt.Errorf(\"authentication failed: %w\", err)\n\t}\n\treturn sendEmailWithClient(client, config, toList, msg)\n}\n\nfunc sendWithStartTLS(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error {\n\tvar err error\n\tvar conn net.Conn\n\tif transport != nil && transport.DialContext != nil {\n\t\tconn, err = transport.DialContext(context.Background(), \"tcp\", addr)\n\t} else {\n\t\tconn, err = net.Dial(\"tcp\", addr)\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to SMTP server: %w\", err)\n\t}\n\tdefer conn.Close()\n\n\tclient, err := smtp.NewClient(conn, config.Host)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create SMTP client: %w\", err)\n\t}\n\tdefer client.Quit()\n\n\tif err = client.StartTLS(&tls.Config{ServerName: config.Host}); err != nil {\n\t\treturn fmt.Errorf(\"failed to start TLS: %w\", err)\n\t}\n\tif err := tryAuth(client, config.Username, config.Password, config.Host); err != nil {\n\t\treturn fmt.Errorf(\"authentication failed: %w\", err)\n\t}\n\treturn sendEmailWithClient(client, config, toList, msg)\n}\n\nfunc sendPlaintext(config SMTPConfig, addr string, toList []string, msg string, transport *http.Transport) error {\n\tvar err error\n\tvar conn net.Conn\n\tif transport != nil && transport.DialContext != nil {\n\t\tconn, err = transport.DialContext(context.Background(), \"tcp\", addr)\n\t} else {\n\t\tconn, err = net.Dial(\"tcp\", addr)\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to connect to SMTP server: %w\", err)\n\t}\n\tdefer conn.Close()\n\n\tclient, err := smtp.NewClient(conn, config.Host)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create SMTP client: %w\", err)\n\t}\n\n\treturn sendEmailWithClient(client, config, toList, msg)\n}\n\nfunc sendEmailWithClient(client *smtp.Client, config SMTPConfig, toList []string, msg string) error {\n\tif err := client.Mail(config.Sender); err != nil {\n\t\treturn fmt.Errorf(\"setting sender failed: %w\", err)\n\t}\n\tfor _, addr := range toList {\n\t\tif err := client.Rcpt(addr); err != nil {\n\t\t\treturn fmt.Errorf(\"adding recipient %s failed: %w\", addr, err)\n\t\t}\n\t}\n\tw, err := client.Data()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"preparing data failed: %w\", err)\n\t}\n\tdefer w.Close()\n\n\tif _, err := w.Write([]byte(msg)); err != nil {\n\t\treturn fmt.Errorf(\"writing message failed: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc tryAuth(client *smtp.Client, username, password, host string) error {\n\tok, authCap := client.Extension(\"AUTH\")\n\tif !ok {\n\t\treturn fmt.Errorf(\"server does not support AUTH\")\n\t}\n\tauthCap = strings.ToUpper(authCap)\n\tif strings.Contains(authCap, \"PLAIN\") {\n\t\tauth := smtp.PlainAuth(\"\", username, password, host)\n\t\tif err := client.Auth(auth); err != nil {\n\t\t\treturn fmt.Errorf(\"plain auth failed: %w\", err)\n\t\t}\n\t\treturn nil\n\t}\n\tif strings.Contains(authCap, \"LOGIN\") {\n\t\tif err := client.Auth(LoginAuth(username, password)); err != nil {\n\t\t\treturn fmt.Errorf(\"login auth failed: %w\", err)\n\t\t}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"no supported auth mechanism, server supports: %s\", authCap)\n}\n"
  },
  {
    "path": "agent/utils/encrypt/encrypt.go",
    "content": "package encrypt\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\nfunc StringEncryptWithBase64(text string) (string, error) {\n\taccessKeyItem, err := base64.StdEncoding.DecodeString(text)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tencryptKeyItem, err := StringEncrypt(string(accessKeyItem))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn encryptKeyItem, nil\n}\n\nfunc StringEncryptWithKey(text, key string) (string, error) {\n\tif len(text) == 0 {\n\t\treturn \"\", nil\n\t}\n\tif len(key) < 16 {\n\t\tfor len(key) < 16 {\n\t\t\tkey += \"u\"\n\t\t}\n\t} else {\n\t\tkey = key[:16]\n\t}\n\tpass := []byte(text)\n\txpass, err := aesEncryptWithSalt([]byte(key), pass)\n\tif err == nil {\n\t\tpass64 := base64.StdEncoding.EncodeToString(xpass)\n\t\treturn pass64, err\n\t}\n\treturn \"\", err\n}\n\nfunc StringEncrypt(text string) (string, error) {\n\tif len(text) == 0 {\n\t\treturn \"\", nil\n\t}\n\tif len(global.CONF.Base.EncryptKey) == 0 {\n\t\tvar encryptSetting model.Setting\n\t\tif err := global.DB.Where(\"key = ?\", \"EncryptKey\").First(&encryptSetting).Error; err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tglobal.CONF.Base.EncryptKey = encryptSetting.Value\n\t}\n\tkey := global.CONF.Base.EncryptKey\n\treturn StringEncryptWithKey(text, key)\n}\n\nfunc StringDecryptWithBase64(text string) (string, error) {\n\tdecryptItem, err := StringDecrypt(text)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.StdEncoding.EncodeToString([]byte(decryptItem)), nil\n}\n\nfunc StringDecryptWithKey(text, key string) (string, error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"A panic occurred during string decrypt with key, error message: %v\", r)\n\t\t}\n\t}()\n\tif len(text) == 0 {\n\t\treturn \"\", nil\n\t}\n\tif len(key) < 16 {\n\t\tfor len(key) < 16 {\n\t\t\tkey += \"u\"\n\t\t}\n\t} else {\n\t\tkey = key[:16]\n\t}\n\tbytesPass, err := base64.StdEncoding.DecodeString(text)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar tpass []byte\n\ttpass, err = aesDecryptWithSalt([]byte(key), bytesPass)\n\tif err == nil {\n\t\tresult := string(tpass[:])\n\t\treturn result, err\n\t}\n\treturn \"\", err\n}\n\nfunc StringDecrypt(text string) (string, error) {\n\tif len(text) == 0 {\n\t\treturn \"\", nil\n\t}\n\tif len(global.CONF.Base.EncryptKey) == 0 {\n\t\tvar encryptSetting model.Setting\n\t\tif err := global.DB.Where(\"key = ?\", \"EncryptKey\").First(&encryptSetting).Error; err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tglobal.CONF.Base.EncryptKey = encryptSetting.Value\n\t}\n\tkey := global.CONF.Base.EncryptKey\n\treturn StringDecryptWithKey(text, key)\n}\n\nfunc padding(plaintext []byte, blockSize int) []byte {\n\tpadding := blockSize - len(plaintext)%blockSize\n\tpadtext := bytes.Repeat([]byte{byte(padding)}, padding)\n\treturn append(plaintext, padtext...)\n}\n\nfunc unPadding(origData []byte) ([]byte, error) {\n\tlength := len(origData)\n\tif length == 0 {\n\t\treturn nil, fmt.Errorf(\"invalid padding size\")\n\t}\n\n\tunpadding := int(origData[length-1])\n\tif unpadding == 0 || unpadding > length {\n\t\treturn nil, fmt.Errorf(\"invalid padding\")\n\t}\n\n\tfor i := 0; i < unpadding; i++ {\n\t\tif origData[length-1-i] != byte(unpadding) {\n\t\t\treturn nil, fmt.Errorf(\"invalid padding\")\n\t\t}\n\t}\n\n\treturn origData[:(length - unpadding)], nil\n}\n\nfunc aesEncryptWithSalt(key, plaintext []byte) ([]byte, error) {\n\tplaintext = padding(plaintext, aes.BlockSize)\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tciphertext := make([]byte, aes.BlockSize+len(plaintext))\n\tiv := ciphertext[0:aes.BlockSize]\n\tif _, err := io.ReadFull(rand.Reader, iv); err != nil {\n\t\treturn nil, err\n\t}\n\tcbc := cipher.NewCBCEncrypter(block, iv)\n\tcbc.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)\n\treturn ciphertext, nil\n}\nfunc aesDecryptWithSalt(key, ciphertext []byte) ([]byte, error) {\n\tvar block cipher.Block\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(ciphertext) < aes.BlockSize {\n\t\treturn nil, fmt.Errorf(\"iciphertext too short\")\n\t}\n\tiv := ciphertext[:aes.BlockSize]\n\tciphertext = ciphertext[aes.BlockSize:]\n\tcbc := cipher.NewCBCDecrypter(block, iv)\n\tcbc.CryptBlocks(ciphertext, ciphertext)\n\n\tunpadded, err := unPadding(ciphertext)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn unpadded, nil\n}\n"
  },
  {
    "path": "agent/utils/env/env.go",
    "content": "package env\n\nimport (\n\t\"fmt\"\n\t\"github.com/joho/godotenv\"\n\t\"os\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc Write(envMap map[string]string, filename string) error {\n\tcontent, err := Marshal(envMap)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfile, err := os.Create(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\t_, err = file.WriteString(content + \"\\n\")\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn file.Sync()\n}\n\nfunc Marshal(envMap map[string]string) (string, error) {\n\tlines := make([]string, 0, len(envMap))\n\tfor k, v := range envMap {\n\t\tif d, err := strconv.Atoi(v); err == nil && !isStartWithZero(v) {\n\t\t\tlines = append(lines, fmt.Sprintf(`%s=%d`, k, d))\n\t\t} else if shouldUseSingleQuotes(v) {\n\t\t\tlines = append(lines, fmt.Sprintf(`%s='%s'`, k, v))\n\t\t} else {\n\t\t\tlines = append(lines, fmt.Sprintf(`%s=\"%s\"`, k, escapeDoubleQuotedEnvValue(v)))\n\t\t}\n\t}\n\treturn strings.Join(lines, \"\\n\"), nil\n}\n\nfunc WriteWithOrder(envMap map[string]string, filename string, orders []string) error {\n\tcontent, err := MarshalWithOrder(envMap, orders)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfile, err := os.Create(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\t_, err = file.WriteString(content + \"\\n\")\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn file.Sync()\n}\n\nfunc MarshalWithOrder(envMap map[string]string, orders []string) (string, error) {\n\tlines := make([]string, 0, len(envMap))\n\tfor _, k := range orders {\n\t\tif v, ok := envMap[k]; ok {\n\t\t\tlines = append(lines, formatEnvLine(k, v))\n\t\t}\n\t}\n\n\textraKeys := make([]string, 0)\n\tfor k := range envMap {\n\t\tfound := false\n\t\tfor _, okk := range orders {\n\t\t\tif k == okk {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\textraKeys = append(extraKeys, k)\n\t\t}\n\t}\n\tsort.Strings(extraKeys)\n\tfor _, k := range extraKeys {\n\t\tlines = append(lines, formatEnvLine(k, envMap[k]))\n\t}\n\treturn strings.Join(lines, \"\\n\"), nil\n}\n\nfunc formatEnvLine(k, v string) string {\n\tif d, err := strconv.Atoi(v); err == nil && !isStartWithZero(v) {\n\t\treturn fmt.Sprintf(`%s=%d`, k, d)\n\t} else if shouldUseSingleQuotes(v) {\n\t\treturn fmt.Sprintf(`%s='%s'`, k, v)\n\t} else {\n\t\treturn fmt.Sprintf(`%s=\"%s\"`, k, escapeDoubleQuotedEnvValue(v))\n\t}\n}\n\nfunc GetEnvValueByKey(envPath, key string) (string, error) {\n\tenvMap, err := godotenv.Read(envPath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvalue, ok := envMap[key]\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"key %s not found in %s\", key, envPath)\n\t}\n\treturn value, nil\n}\n\nfunc isStartWithZero(value string) bool {\n\tif strings.HasPrefix(value, \"0\") && len(value) > 1 {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc hasEvenDoubleQuotes(s string) bool {\n\tcount := 0\n\tfor _, ch := range s {\n\t\tif ch == '\"' {\n\t\t\tcount++\n\t\t}\n\t}\n\treturn count%2 == 0\n}\n\nfunc shouldUseSingleQuotes(s string) bool {\n\treturn hasEvenDoubleQuotes(s) && !strings.Contains(s, `\\`) && !strings.Contains(s, `'`)\n}\n\nfunc escapeDoubleQuotedEnvValue(s string) string {\n\treplacer := strings.NewReplacer(\n\t\t`\\`, `\\\\`,\n\t\t`\"`, `\\\"`,\n\t\t\"\\n\", `\\n`,\n\t)\n\treturn replacer.Replace(s)\n}\n"
  },
  {
    "path": "agent/utils/files/archiver.go",
    "content": "package files\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype ShellArchiver interface {\n\tExtract(filePath, dstDir string, secret string) error\n\tCompress(sourcePaths []string, dstFile string, secret string) error\n}\n\nfunc NewShellArchiver(compressType CompressType) (ShellArchiver, error) {\n\tswitch compressType {\n\tcase Tar:\n\t\tif err := checkCmdAvailability(\"tar\"); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn NewTarArchiver(compressType), nil\n\tcase TarGz:\n\t\treturn NewTarGzArchiver(), nil\n\tcase Zip:\n\t\tif err := checkCmdAvailability(\"zip\"); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn NewZipArchiver(), nil\n\tcase Rar:\n\t\tif err := checkCmdAvailability(\"unrar\"); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn NewRarArchiver(), nil\n\tcase X7z:\n\t\tif err := checkCmdAvailability(\"7z\"); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn NewX7zArchiver(), nil\n\tdefault:\n\t\treturn nil, buserr.New(\"unsupported compress type\")\n\t}\n}\n\nfunc checkCmdAvailability(cmdStr string) error {\n\tif cmd.Which(cmdStr) {\n\t\treturn nil\n\t}\n\treturn buserr.WithName(\"ErrCmdNotFound\", cmdStr)\n}\n"
  },
  {
    "path": "agent/utils/files/file_op.go",
    "content": "package files\n\nimport (\n\t\"archive/zip\"\n\t\"bufio\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/req_helper\"\n\tcZip \"github.com/klauspost/compress/zip\"\n\t\"golang.org/x/text/encoding/simplifiedchinese\"\n\t\"golang.org/x/text/transform\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/mholt/archiver/v4\"\n\t\"github.com/spf13/afero\"\n)\n\nconst (\n\tcmdDefaultTimeout   = 10 * time.Second\n\tcmdRecursiveTimeout = 5 * time.Minute\n)\n\nvar protectedPaths = []string{\n\t\"/\",\n\t\"/bin\",\n\t\"/sbin\",\n\t\"/etc\",\n\t\"/boot\",\n\t\"/usr\",\n\t\"/lib\",\n\t\"/lib64\",\n\t\"/dev\",\n\t\"/proc\",\n\t\"/sys\",\n\t\"/root\",\n}\n\nfunc IsProtected(path string) bool {\n\treal, err := filepath.EvalSymlinks(path)\n\tif err == nil {\n\t\tpath = real\n\t}\n\n\tabs, err := filepath.Abs(path)\n\tif err == nil {\n\t\tpath = abs\n\t}\n\n\tfor _, p := range protectedPaths {\n\t\tif path == p {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\ntype FileOp struct {\n\tFs afero.Fs\n}\n\nfunc NewFileOp() FileOp {\n\treturn FileOp{\n\t\tFs: afero.NewOsFs(),\n\t}\n}\n\nfunc (f FileOp) OpenFile(dst string) (fs.File, error) {\n\treturn f.Fs.Open(dst)\n}\n\nfunc (f FileOp) GetContent(dst string) ([]byte, error) {\n\tafs := &afero.Afero{Fs: f.Fs}\n\tcByte, err := afs.ReadFile(dst)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cByte, nil\n}\n\nfunc (f FileOp) CreateDir(dst string, mode fs.FileMode) error {\n\treturn f.Fs.MkdirAll(dst, mode)\n}\n\nfunc (f FileOp) CreateDirWithMode(dst string, mode fs.FileMode) error {\n\tif err := f.Fs.MkdirAll(dst, mode); err != nil {\n\t\treturn err\n\t}\n\treturn f.ChmodRWithMode(dst, mode, true)\n}\nfunc (f FileOp) CreateDirWithPath(isDir bool, pathItem string) (string, error) {\n\tcheckPath := pathItem\n\tif !isDir {\n\t\tcheckPath = path.Dir(pathItem)\n\t}\n\tif !f.Stat(checkPath) {\n\t\tif err := f.CreateDir(checkPath, os.ModePerm); err != nil {\n\t\t\treturn pathItem, err\n\t\t}\n\t}\n\treturn pathItem, nil\n}\n\nfunc (f FileOp) CreateFile(dst string) error {\n\tif _, err := f.Fs.Create(dst); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) CreateFileWithMode(dst string, mode fs.FileMode) error {\n\tfile, err := f.Fs.OpenFile(dst, os.O_CREATE, mode)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn file.Close()\n}\n\nfunc (f FileOp) LinkFile(source string, dst string, isSymlink bool) error {\n\tif isSymlink {\n\t\tosFs := afero.OsFs{}\n\t\treturn osFs.SymlinkIfPossible(source, dst)\n\t} else {\n\t\treturn os.Link(source, dst)\n\t}\n}\n\nfunc (f FileOp) DeleteDir(dst string) error {\n\tif IsProtected(dst) {\n\t\treturn buserr.New(\"ErrPathNotDelete\")\n\t}\n\treturn f.Fs.RemoveAll(dst)\n}\n\nfunc (f FileOp) Stat(dst string) bool {\n\tinfo, _ := f.Fs.Stat(dst)\n\treturn info != nil\n}\n\nfunc (f FileOp) DeleteFile(dst string) error {\n\tif IsProtected(dst) {\n\t\treturn buserr.New(\"ErrPathNotDelete\")\n\t}\n\treturn f.Fs.Remove(dst)\n}\n\nfunc (f FileOp) CleanDir(dst string) error {\n\tif IsProtected(dst) {\n\t\treturn buserr.New(\"ErrPathNotDelete\")\n\t}\n\treturn cmd.RunDefaultBashCf(\"rm -rf %s/*\", dst)\n}\n\nfunc (f FileOp) RmRf(dst string) error {\n\tif IsProtected(dst) {\n\t\treturn buserr.New(\"ErrPathNotDelete\")\n\t}\n\treturn cmd.RunDefaultBashCf(\"rm -rf %s\", dst)\n}\n\nfunc (f FileOp) WriteFile(dst string, in io.Reader, mode fs.FileMode) error {\n\tfile, err := f.Fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\tif _, err = io.Copy(file, in); err != nil {\n\t\treturn err\n\t}\n\n\tif _, err = file.Stat(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) SaveFile(dst string, content string, mode fs.FileMode) error {\n\tif !f.Stat(path.Dir(dst)) {\n\t\t_ = f.CreateDir(path.Dir(dst), mode.Perm())\n\t}\n\tfile, err := f.Fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.WriteString(content)\n\twrite.Flush()\n\treturn nil\n}\n\nfunc (f FileOp) SaveFileWithByte(dst string, content []byte, mode fs.FileMode) error {\n\tif !f.Stat(path.Dir(dst)) {\n\t\t_ = f.CreateDir(path.Dir(dst), mode.Perm())\n\t}\n\tfile, err := f.Fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\twrite := bufio.NewWriter(file)\n\t_, _ = write.Write(content)\n\twrite.Flush()\n\treturn nil\n}\n\nfunc (f FileOp) ChownR(dst string, uid string, gid string, sub bool) error {\n\tcmdStr := fmt.Sprintf(`%s chown %s:%s \"%s\"`, cmd.SudoHandleCmd(), uid, gid, dst)\n\tif sub {\n\t\tcmdStr = fmt.Sprintf(`chown -R %s:%s \"%s\"`, uid, gid, dst)\n\t}\n\ttimeout := cmdDefaultTimeout\n\tif sub {\n\t\ttimeout = cmdRecursiveTimeout\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))\n\tif err := cmdMgr.RunBashC(cmdStr); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) ChmodR(dst string, mode int64, sub bool) error {\n\tcmdStr := fmt.Sprintf(`%s chmod %v \"%s\"`, cmd.SudoHandleCmd(), fmt.Sprintf(\"%04o\", mode), dst)\n\tif sub {\n\t\tcmdStr = fmt.Sprintf(`%s chmod -R %v \"%s\"`, cmd.SudoHandleCmd(), fmt.Sprintf(\"%04o\", mode), dst)\n\t}\n\ttimeout := cmdDefaultTimeout\n\tif sub {\n\t\ttimeout = cmdRecursiveTimeout\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))\n\tif err := cmdMgr.RunBashC(cmdStr); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) ChmodRWithMode(dst string, mode fs.FileMode, sub bool) error {\n\tcmdStr := fmt.Sprintf(`%s chmod %v \"%s\"`, cmd.SudoHandleCmd(), fmt.Sprintf(\"%o\", mode.Perm()), dst)\n\tif sub {\n\t\tcmdStr = fmt.Sprintf(`%s chmod -R %v \"%s\"`, cmd.SudoHandleCmd(), fmt.Sprintf(\"%o\", mode.Perm()), dst)\n\t}\n\ttimeout := cmdDefaultTimeout\n\tif sub {\n\t\ttimeout = cmdRecursiveTimeout\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))\n\tif err := cmdMgr.RunBashC(cmdStr); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) ChownRPaths(paths []string, uid string, gid string, sub bool) error {\n\tif len(paths) == 0 {\n\t\treturn nil\n\t}\n\tif len(paths) == 1 {\n\t\treturn f.ChownR(paths[0], uid, gid, sub)\n\t}\n\tquoted := make([]string, len(paths))\n\tfor i, p := range paths {\n\t\tquoted[i] = fmt.Sprintf(`\"%s\"`, p)\n\t}\n\targs := strings.Join(quoted, \" \")\n\tvar cmdStr string\n\tif sub {\n\t\tcmdStr = fmt.Sprintf(`chown -R %s:%s %s`, uid, gid, args)\n\t} else {\n\t\tcmdStr = fmt.Sprintf(`%s chown %s:%s %s`, cmd.SudoHandleCmd(), uid, gid, args)\n\t}\n\ttimeout := cmdDefaultTimeout\n\tif sub {\n\t\ttimeout = cmdRecursiveTimeout\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))\n\tif err := cmdMgr.RunBashC(cmdStr); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) ChmodRPaths(paths []string, mode int64, sub bool) error {\n\tif len(paths) == 0 {\n\t\treturn nil\n\t}\n\tif len(paths) == 1 {\n\t\treturn f.ChmodR(paths[0], mode, sub)\n\t}\n\tquoted := make([]string, len(paths))\n\tfor i, p := range paths {\n\t\tquoted[i] = fmt.Sprintf(`\"%s\"`, p)\n\t}\n\targs := strings.Join(quoted, \" \")\n\tmodeStr := fmt.Sprintf(\"%04o\", mode)\n\tvar cmdStr string\n\tif sub {\n\t\tcmdStr = fmt.Sprintf(`%s chmod -R %s %s`, cmd.SudoHandleCmd(), modeStr, args)\n\t} else {\n\t\tcmdStr = fmt.Sprintf(`%s chmod %s %s`, cmd.SudoHandleCmd(), modeStr, args)\n\t}\n\ttimeout := cmdDefaultTimeout\n\tif sub {\n\t\ttimeout = cmdRecursiveTimeout\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(timeout))\n\tif err := cmdMgr.RunBashC(cmdStr); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) Rename(oldName string, newName string) error {\n\treturn f.Fs.Rename(oldName, newName)\n}\n\ntype WriteCounter struct {\n\tTotal   uint64\n\tWritten uint64\n\tKey     string\n\tName    string\n}\n\ntype Process struct {\n\tTotal   uint64  `json:\"total\"`\n\tWritten uint64  `json:\"written\"`\n\tPercent float64 `json:\"percent\"`\n\tName    string  `json:\"name\"`\n}\n\nfunc (w *WriteCounter) Write(p []byte) (n int, err error) {\n\tn = len(p)\n\tw.Written += uint64(n)\n\tw.SaveProcess()\n\treturn n, nil\n}\n\nfunc (w *WriteCounter) SaveProcess() {\n\tpercentValue := 0.0\n\tif w.Total > 0 {\n\t\tpercent := float64(w.Written) / float64(w.Total) * 100\n\t\tpercentValue, _ = strconv.ParseFloat(fmt.Sprintf(\"%.2f\", percent), 64)\n\t}\n\tprocess := Process{\n\t\tTotal:   w.Total,\n\t\tWritten: w.Written,\n\t\tPercent: percentValue,\n\t\tName:    w.Name,\n\t}\n\tby, _ := json.Marshal(process)\n\tif percentValue < 100 {\n\t\tglobal.CACHE.Set(w.Key, string(by))\n\t} else {\n\t\tglobal.CACHE.SetWithTTL(w.Key, string(by), time.Second*time.Duration(10))\n\t}\n}\n\nfunc (f FileOp) DownloadFileWithProcess(url, dst, key string, ignoreCertificate bool) error {\n\tclient := &http.Client{}\n\tif ignoreCertificate {\n\t\tclient.Transport = &http.Transport{\n\t\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t}\n\t}\n\tdefer client.CloseIdleConnections()\n\trequest, err := http.NewRequest(\"GET\", url, nil)\n\tif err != nil {\n\t\treturn nil\n\t}\n\trequest.Header.Set(\"Accept-Encoding\", \"identity\")\n\tresp, err := client.Do(request)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"get download file [%s] error, err %s\", dst, err.Error())\n\t\treturn err\n\t}\n\tout, err := os.Create(dst)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"create download file [%s] error, err %s\", dst, err.Error())\n\t\treturn err\n\t}\n\tgo func() {\n\t\tcounter := &WriteCounter{}\n\t\tcounter.Key = key\n\t\tif resp.ContentLength > 0 {\n\t\t\tcounter.Total = uint64(resp.ContentLength)\n\t\t}\n\t\tcounter.Name = filepath.Base(dst)\n\t\tif _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil {\n\t\t\tglobal.LOG.Errorf(\"save download file [%s] error, err %s\", dst, err.Error())\n\t\t}\n\t\tout.Close()\n\t\tresp.Body.Close()\n\n\t\tvalue := global.CACHE.Get(counter.Key)\n\t\tprocess := &Process{}\n\t\t_ = json.Unmarshal([]byte(value), process)\n\t\tprocess.Percent = 100\n\t\tprocess.Name = counter.Name\n\t\tprocess.Total = process.Written\n\t\tby, _ := json.Marshal(process)\n\t\tglobal.CACHE.Set(counter.Key, string(by))\n\t}()\n\treturn nil\n}\n\nfunc (f FileOp) DownloadFile(url, dst string) error {\n\tresp, err := req_helper.HandleGet(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tout, err := os.Create(dst)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create download file [%s] error, err %s\", dst, err.Error())\n\t}\n\tdefer out.Close()\n\n\tif _, err = io.Copy(out, resp.Body); err != nil {\n\t\treturn fmt.Errorf(\"save download file [%s] error, err %s\", dst, err.Error())\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) Cut(oldPaths []string, dst, name string, cover bool) error {\n\tif len(oldPaths) == 0 {\n\t\treturn nil\n\t}\n\tvar dstPath string\n\tcoverFlag := \"\"\n\tif name != \"\" {\n\t\tdstPath = filepath.Join(dst, name)\n\t\tif f.Stat(dstPath) {\n\t\t\tdstPath = dst\n\t\t}\n\t\tif cover {\n\t\t\tcoverFlag = \"-f\"\n\t\t}\n\t} else {\n\t\tdstPath = dst\n\t\tcoverFlag = \"-f\"\n\t}\n\tvar quotedPaths []string\n\tfor _, p := range oldPaths {\n\t\tquotedPaths = append(quotedPaths, fmt.Sprintf(\"'%s'\", p))\n\t}\n\tmvCommand := fmt.Sprintf(\"mv %s %s '%s'\", coverFlag, strings.Join(quotedPaths, \" \"), dstPath)\n\tif err := cmd.RunDefaultBashCf(mvCommand); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) Mv(oldPath, dstPath string) error {\n\tif err := cmd.RunDefaultBashCf(`mv '%s' '%s'`, oldPath, dstPath); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) Copy(src, dst string) error {\n\tif src = path.Clean(\"/\" + src); src == \"\" {\n\t\treturn os.ErrNotExist\n\t}\n\tif dst = path.Clean(\"/\" + dst); dst == \"\" {\n\t\treturn os.ErrNotExist\n\t}\n\tif src == \"/\" || dst == \"/\" {\n\t\treturn os.ErrInvalid\n\t}\n\tif dst == src {\n\t\treturn os.ErrInvalid\n\t}\n\tinfo, err := f.Fs.Stat(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif info.IsDir() {\n\t\treturn f.CopyDir(src, dst)\n\t}\n\treturn f.CopyFile(src, dst)\n}\n\nfunc (f FileOp) CopyAndReName(src, dst, name string, cover bool) error {\n\tif src = path.Clean(\"/\" + src); src == \"\" {\n\t\treturn os.ErrNotExist\n\t}\n\tif dst = path.Clean(\"/\" + dst); dst == \"\" {\n\t\treturn os.ErrNotExist\n\t}\n\tif src == \"/\" || dst == src {\n\t\treturn os.ErrInvalid\n\t}\n\n\tsrcInfo, err := f.Fs.Stat(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif name != \"\" && !cover {\n\t\tif f.Stat(filepath.Join(dst, name)) {\n\t\t\treturn buserr.New(\"ErrFileIsExist\")\n\t\t}\n\t}\n\n\tif srcInfo.IsDir() {\n\t\tdstPath := dst\n\t\tif name != \"\" && !cover {\n\t\t\tdstPath = filepath.Join(dst, name)\n\t\t}\n\t\treturn cmd.RunDefaultBashCf(`cp -rfp '%s' '%s'`, src, dstPath)\n\t} else {\n\t\tdstPath := filepath.Join(dst, name)\n\t\tif cover {\n\t\t\tdstPath = dst\n\t\t}\n\t\treturn cmd.RunDefaultBashCf(`cp -fp '%s' '%s'`, src, dstPath)\n\t}\n}\n\nfunc (f FileOp) CopyDirWithNewName(src, dst, newName string) error {\n\tif newName == \".\" || newName == \"\" {\n\t\treturn cmd.RunDefaultBashCf(`cp -rfp '%s'/. '%s'`, src, dst)\n\t}\n\tdstDir := filepath.Join(dst, newName)\n\treturn cmd.RunDefaultBashCf(`cp -rfp '%s' '%s'`, src, dstDir)\n}\n\nfunc (f FileOp) CopyDir(src, dst string) error {\n\tsrcInfo, err := f.Fs.Stat(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdstDir := filepath.Join(dst, srcInfo.Name())\n\tif err = f.Fs.MkdirAll(dstDir, srcInfo.Mode()); err != nil {\n\t\treturn err\n\t}\n\treturn cmd.NewCommandMgr(cmd.WithIgnoreExist1()).RunBashCf(`cp -rfp '%s' '%s'`, src, dst+\"/\")\n}\n\nfunc (f FileOp) CopyDirWithExclude(src, dst string, excludeNames []string) error {\n\tsrcInfo, err := f.Fs.Stat(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdstDir := filepath.Join(dst, srcInfo.Name())\n\tif err = f.Fs.MkdirAll(dstDir, srcInfo.Mode()); err != nil {\n\t\treturn err\n\t}\n\tif len(excludeNames) == 0 {\n\t\treturn cmd.NewCommandMgr(cmd.WithIgnoreExist1()).RunBashCf(`cp -rfp '%s' '%s'`, src, dst+\"/\")\n\t}\n\ttmpFiles, err := os.ReadDir(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, item := range tmpFiles {\n\t\tisExclude := false\n\t\tfor _, name := range excludeNames {\n\t\t\tif item.Name() == name {\n\t\t\t\tisExclude = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif isExclude {\n\t\t\tcontinue\n\t\t}\n\t\tif item.IsDir() {\n\t\t\tif err := f.CopyDir(path.Join(src, item.Name()), dstDir); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif err := f.CopyFile(path.Join(src, item.Name()), dstDir); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (f FileOp) CopyFile(src, dst string) error {\n\tdst = filepath.Clean(dst) + string(filepath.Separator)\n\treturn cmd.NewCommandMgr(cmd.WithIgnoreExist1()).RunBashCf(`cp -fp '%s' '%s'`, src, dst+\"/\")\n}\n\nfunc (f FileOp) GetDirSize(path string) (int64, error) {\n\tduCmd := exec.Command(\"du\", \"-s\", path)\n\toutput, err := duCmd.Output()\n\tif err == nil {\n\t\tfields := strings.Fields(string(output))\n\t\tif len(fields) == 2 {\n\t\t\tvar cmdSize int64\n\t\t\t_, err = fmt.Sscanf(fields[0], \"%d\", &cmdSize)\n\t\t\tif err == nil {\n\t\t\t\treturn cmdSize * 1024, nil\n\t\t\t}\n\t\t}\n\t}\n\n\tvar size int64\n\terr = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !info.IsDir() {\n\t\t\tsize += info.Size()\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn size, nil\n}\n\ntype DirSize struct {\n\tPath string `json:\"path\"`\n\tSize int64  `json:\"size\"`\n}\n\nfunc (f FileOp) GetDepthDirSize(path string) ([]DirSize, error) {\n\tvar result []DirSize\n\tsizeMap := make(map[string]int64)\n\tduCmd := exec.Command(\"du\", \"-k\", \"--max-depth=1\", \"--exclude=proc\", path)\n\toutput, err := duCmd.Output()\n\tif err == nil {\n\t\tparseDUOutput(output, sizeMap)\n\t} else {\n\t\tcalculateDirSizeFallback(path, sizeMap)\n\t}\n\n\tfor dir, size := range sizeMap {\n\t\tresult = append(result, DirSize{\n\t\t\tPath: dir,\n\t\t\tSize: size,\n\t\t})\n\t}\n\n\treturn result, nil\n}\n\nfunc parseDUOutput(output []byte, sizeMap map[string]int64) {\n\tlines := strings.Split(string(output), \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.TrimSpace(line) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tfields := strings.Fields(line)\n\t\tif len(fields) == 2 {\n\t\t\tif sizeKB, err := strconv.ParseInt(fields[0], 10, 64); err == nil {\n\t\t\t\tdir := fields[1]\n\t\t\t\tsizeMap[dir] = sizeKB * 1024\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc calculateDirSizeFallback(path string, sizeMap map[string]int64) {\n\t_ = filepath.Walk(path, func(p string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tif !info.IsDir() {\n\t\t\trel, err := filepath.Rel(path, p)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tparts := strings.Split(rel, string(os.PathSeparator))\n\t\t\tvar topLevel string\n\t\t\tif len(parts) == 0 || parts[0] == \".\" {\n\t\t\t\ttopLevel = path\n\t\t\t} else {\n\t\t\t\ttopLevel = filepath.Join(path, parts[0])\n\t\t\t}\n\t\t\tsizeMap[topLevel] += info.Size()\n\t\t}\n\t\treturn nil\n\t})\n}\n\nfunc getFormat(cType CompressType) archiver.CompressedArchive {\n\tformat := archiver.CompressedArchive{}\n\tswitch cType {\n\tcase Tar:\n\t\tformat.Archival = archiver.Tar{}\n\tcase TarGz, Gz, Tgz:\n\t\tformat.Compression = archiver.Gz{}\n\t\tformat.Archival = archiver.Tar{}\n\tcase SdkTarGz:\n\t\tformat.Compression = archiver.Gz{}\n\t\tformat.Archival = archiver.Tar{}\n\tcase SdkZip, Zip:\n\t\tformat.Archival = archiver.Zip{\n\t\t\tCompression: zip.Deflate,\n\t\t}\n\tcase Bz2, TarBz2:\n\t\tformat.Compression = archiver.Bz2{}\n\t\tformat.Archival = archiver.Tar{}\n\tcase Xz, TarXz:\n\t\tformat.Compression = archiver.Xz{}\n\t\tformat.Archival = archiver.Tar{}\n\t}\n\treturn format\n}\n\nfunc (f FileOp) Compress(srcRiles []string, dst string, name string, cType CompressType, secret string) error {\n\tformat := getFormat(cType)\n\n\tfileMaps := make(map[string]string, len(srcRiles))\n\tfor _, s := range srcRiles {\n\t\tbase := filepath.Base(s)\n\t\tfileMaps[s] = base\n\t}\n\n\tif !f.Stat(dst) {\n\t\t_ = f.CreateDir(dst, constant.DirPerm)\n\t}\n\n\tfiles, err := archiver.FilesFromDisk(nil, fileMaps)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdstFile := filepath.Join(dst, name)\n\tout, err := f.Fs.Create(dstFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch cType {\n\tcase Zip:\n\t\tif err := ZipFile(files, out); err == nil {\n\t\t\treturn nil\n\t\t}\n\t\t_ = f.DeleteFile(dstFile)\n\t\treturn NewZipArchiver().Compress(srcRiles, dstFile, \"\")\n\tcase TarGz:\n\t\terr = NewTarGzArchiver().Compress(srcRiles, dstFile, secret)\n\t\tif err != nil {\n\t\t\t_ = f.DeleteFile(dstFile)\n\t\t\treturn err\n\t\t}\n\tcase Rar:\n\t\terr = NewRarArchiver().Compress(srcRiles, dstFile, secret)\n\t\tif err != nil {\n\t\t\t_ = f.DeleteFile(dstFile)\n\t\t\treturn err\n\t\t}\n\tcase X7z:\n\t\terr = NewX7zArchiver().Compress(srcRiles, dstFile, secret)\n\t\tif err != nil {\n\t\t\t_ = f.DeleteFile(dstFile)\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\terr = format.Archive(context.Background(), out, files)\n\t\tif err != nil {\n\t\t\t_ = f.DeleteFile(dstFile)\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc isIgnoreFile(name string) bool {\n\treturn strings.HasPrefix(name, \"__MACOSX\") || strings.HasSuffix(name, \".DS_Store\") || strings.HasPrefix(name, \"._\")\n}\n\nfunc decodeGBK(input string) (string, error) {\n\tdecoder := simplifiedchinese.GBK.NewDecoder()\n\tdecoded, _, err := transform.String(decoder, input)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn decoded, nil\n}\n\nfunc (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType) error {\n\tformat := getFormat(cType)\n\tif cType == Gz {\n\t\terr := f.DecompressGzFile(srcFile, dst)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\n\ttype dirEntry struct {\n\t\tpath    string\n\t\tmodTime time.Time\n\t}\n\tvar dirs []dirEntry\n\n\thandler := func(ctx context.Context, archFile archiver.File) error {\n\t\tinfo := archFile.FileInfo\n\t\tif isIgnoreFile(archFile.Name()) {\n\t\t\treturn nil\n\t\t}\n\t\tfileName := archFile.NameInArchive\n\t\tvar err error\n\t\tif header, ok := archFile.Header.(cZip.FileHeader); ok {\n\t\t\tif header.NonUTF8 && header.Flags == 0 {\n\t\t\t\tfileName, err = decodeGBK(fileName)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfilePath := filepath.Join(dst, fileName)\n\t\tif archFile.FileInfo.IsDir() {\n\t\t\tif err := f.Fs.MkdirAll(filePath, info.Mode()); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdirs = append(dirs, dirEntry{path: filePath, modTime: info.ModTime()})\n\t\t\treturn nil\n\t\t} else {\n\t\t\tparentDir := path.Dir(filePath)\n\t\t\tif !f.Stat(parentDir) {\n\t\t\t\tif err := f.Fs.MkdirAll(parentDir, info.Mode()); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfr, err := archFile.Open()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer fr.Close()\n\t\tfw, err := f.Fs.OpenFile(filePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, info.Mode())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer fw.Close()\n\t\tif _, err := io.Copy(fw, fr); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = os.Chtimes(filePath, info.ModTime(), info.ModTime())\n\t\treturn nil\n\t}\n\tinput, err := f.Fs.Open(srcFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := format.Extract(context.Background(), input, nil, handler); err != nil {\n\t\treturn err\n\t}\n\tfor i := len(dirs) - 1; i >= 0; i-- {\n\t\t_ = os.Chtimes(dirs[i].path, dirs[i].modTime, dirs[i].modTime)\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) Decompress(srcFile string, dst string, cType CompressType, secret string) error {\n\tif cType == Tar || cType == Zip || cType == TarGz || cType == Rar || cType == X7z {\n\t\tshellArchiver, err := NewShellArchiver(cType)\n\t\tif !f.Stat(dst) {\n\t\t\t_ = f.CreateDir(dst, 0755)\n\t\t}\n\t\tif err == nil {\n\t\t\tif err = shellArchiver.Extract(srcFile, dst, secret); err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif cType == TarGz {\n\t\t\t\tif strings.Contains(err.Error(), \"bad decrypt\") {\n\t\t\t\t\treturn buserr.New(\"ErrBadDecrypt\")\n\t\t\t\t}\n\t\t\t\tif err := shellArchiver.Extract(srcFile, dst, \"-\"); strings.Contains(err.Error(), \"bad decrypt\") {\n\t\t\t\t\treturn buserr.New(\"ErrBadDecrypt\")\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif cType == Rar || cType == X7z {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn f.decompressWithSDK(srcFile, dst, cType)\n}\n\nfunc ZipFile(files []archiver.File, dst afero.File) error {\n\tzw := zip.NewWriter(dst)\n\tdefer zw.Close()\n\n\tfor _, file := range files {\n\t\thdr, err := zip.FileInfoHeader(file)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\thdr.Method = zip.Deflate\n\t\thdr.Name = file.NameInArchive\n\t\tif file.IsDir() {\n\t\t\tif !strings.HasSuffix(hdr.Name, \"/\") {\n\t\t\t\thdr.Name += \"/\"\n\t\t\t}\n\t\t}\n\t\tw, err := zw.CreateHeader(hdr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif file.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tif file.LinkTarget != \"\" {\n\t\t\t_, err = w.Write([]byte(filepath.ToSlash(file.LinkTarget)))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tfileReader, err := file.Open()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_, err = io.Copy(w, fileReader)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (f FileOp) DecompressGzFile(srcFile, dst string) error {\n\tin, err := f.Fs.Open(srcFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open source file failed: %w\", err)\n\t}\n\tdefer in.Close()\n\n\tgr, err := gzip.NewReader(in)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"gzip reader creation failed: %w\", err)\n\t}\n\tdefer gr.Close()\n\n\toutName := strings.TrimSuffix(filepath.Base(srcFile), \".gz\")\n\toutPath := filepath.Join(dst, outName)\n\tparentDir := filepath.Dir(outPath)\n\tif !f.Stat(parentDir) {\n\t\tif err := f.Fs.MkdirAll(parentDir, 0755); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfw, err := f.Fs.OpenFile(outPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create output file failed: %w\", err)\n\t}\n\tdefer fw.Close()\n\n\tif _, err := io.Copy(fw, gr); err != nil {\n\t\treturn fmt.Errorf(\"copy content failed: %w\", err)\n\t}\n\n\tif !gr.ModTime.IsZero() {\n\t\t_ = os.Chtimes(outPath, gr.ModTime, gr.ModTime)\n\t}\n\n\treturn nil\n}\n\nfunc (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules string) error {\n\tif !f.Stat(path.Dir(dst)) {\n\t\tif err := f.Fs.MkdirAll(path.Dir(dst), constant.FilePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tworkdir := src\n\tsrcItem := \".\"\n\tif withDir {\n\t\tworkdir = path.Dir(src)\n\t\tsrcItem = path.Base(src)\n\t}\n\tcommands := \"\"\n\n\texMap := make(map[string]struct{})\n\texStr := \"\"\n\texcludes := strings.Split(exclusionRules, \",\")\n\tfor _, exclude := range excludes {\n\t\tif len(exclude) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(exclude, \"/\") {\n\t\t\texclude, _ = filepath.Rel(src, exclude)\n\t\t}\n\t\tif _, ok := exMap[exclude]; ok {\n\t\t\tcontinue\n\t\t}\n\t\texStr += fmt.Sprintf(\" --exclude '%s'\", exclude)\n\t\texMap[exclude] = struct{}{}\n\t}\n\n\tif len(secret) != 0 {\n\t\tcommands = fmt.Sprintf(\"tar %s -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s\", exStr, srcItem, secret, dst)\n\t\tglobal.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(\" '%s' \", secret), \" ****** \"))\n\t} else {\n\t\tcommands = fmt.Sprintf(\"tar -zcf %s %s %s\", dst, exStr, srcItem)\n\t\tglobal.LOG.Debug(commands)\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(workdir), cmd.WithIgnoreExist1())\n\treturn cmdMgr.RunBashC(commands)\n}\n\nfunc (f FileOp) TarGzFilesWithCompressPro(list []string, dst, secret string) error {\n\tif !f.Stat(path.Dir(dst)) {\n\t\tif err := f.Fs.MkdirAll(path.Dir(dst), constant.FilePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar filelist []string\n\tfor _, item := range list {\n\t\tfilelist = append(filelist, \"-C '\"+path.Dir(item)+\"' '\"+path.Base(item)+\"' \")\n\t}\n\tcommands := \"\"\n\tif len(secret) != 0 {\n\t\tcommands = fmt.Sprintf(\"tar -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s\", strings.Join(filelist, \" \"), secret, dst)\n\t\tglobal.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(\" '%s' \", secret), \" ****** \"))\n\t} else {\n\t\tcommands = fmt.Sprintf(\"tar -zcf %s %s\", dst, strings.Join(filelist, \" \"))\n\t\tglobal.LOG.Debug(commands)\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1())\n\treturn cmdMgr.RunBashC(commands)\n}\n\nfunc (f FileOp) TarGzExtractPro(src, dst string, secret string) error {\n\tif _, err := os.Stat(path.Dir(dst)); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(path.Dir(dst), os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tcommands := \"\"\n\tif len(secret) != 0 {\n\t\tcommands = fmt.Sprintf(\"openssl enc -d -aes-256-cbc -salt -k '%s' -in %s | tar -zxf - > /root/log\", secret, src)\n\t\tglobal.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(\" '%s' \", secret), \" ****** \"))\n\t} else {\n\t\tcommands = fmt.Sprintf(\"tar zxvf %s\", src)\n\t\tglobal.LOG.Debug(commands)\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(dst), cmd.WithIgnoreExist1())\n\treturn cmdMgr.RunBashC(commands)\n}\nfunc CopyCustomAppFile(srcPath, dstPath string) error {\n\tif _, err := os.Stat(srcPath); os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"source file does not exist: %s\", srcPath)\n\t}\n\n\tdestDir := path.Dir(dstPath)\n\tif err := os.MkdirAll(destDir, 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create destination directory %s: %v\", destDir, err)\n\t}\n\n\tsource, err := os.Open(srcPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open source file %s: %v\", srcPath, err)\n\t}\n\tdefer source.Close()\n\n\ttempFile, err := os.CreateTemp(destDir, \"temp_*.tar.gz\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create temporary file in %s: %v\", destDir, err)\n\t}\n\tdefer os.Remove(tempFile.Name())\n\tdefer tempFile.Close()\n\n\tif _, err = io.Copy(tempFile, source); err != nil {\n\t\treturn fmt.Errorf(\"failed to copy file contents: %v\", err)\n\t}\n\n\ttempFile.Close()\n\tsource.Close()\n\n\tif err = os.Rename(tempFile.Name(), dstPath); err != nil {\n\t\treturn fmt.Errorf(\"failed to rename temporary file to %s: %v\", dstPath, err)\n\t}\n\treturn nil\n}\n\nfunc OpensslEncrypt(filePath, secret string) error {\n\ttmpName := path.Join(path.Dir(filePath), \"tmp_\"+path.Base(filePath))\n\tif err := cmd.RunDefaultBashCf(\"MY_PASS='%s' openssl enc -aes-256-cbc -salt -pass env:MY_PASS -in %s -out %s\", secret, filePath, tmpName); err != nil {\n\t\t_ = os.Remove(tmpName)\n\t\treturn err\n\t}\n\treturn os.Rename(tmpName, filePath)\n}\n\nfunc OpensslDecrypt(filePath, secret string) error {\n\ttmpName := path.Join(path.Dir(filePath), \"tmp_\"+path.Base(filePath))\n\tif err := cmd.RunDefaultBashCf(\"MY_PASS='%s' openssl enc -aes-256-cbc -d -salt -pass env:MY_PASS -in %s -out %s\", secret, filePath, tmpName); err != nil {\n\t\tif strings.Contains(err.Error(), \"bad decrypt\") || strings.Contains(err.Error(), \"bad magic number\") {\n\t\t\treturn buserr.New(\"ErrBadDecrypt\")\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/files/fileinfo.go",
    "content": "package files\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\n\t\"github.com/spf13/afero\"\n)\n\ntype FileInfo struct {\n\tFs         afero.Fs    `json:\"-\"`\n\tPath       string      `json:\"path\"`\n\tName       string      `json:\"name\"`\n\tUser       string      `json:\"user\"`\n\tGroup      string      `json:\"group\"`\n\tUid        string      `json:\"uid\"`\n\tGid        string      `json:\"gid\"`\n\tExtension  string      `json:\"extension\"`\n\tContent    string      `json:\"content\"`\n\tSize       int64       `json:\"size\"`\n\tIsDir      bool        `json:\"isDir\"`\n\tIsSymlink  bool        `json:\"isSymlink\"`\n\tIsHidden   bool        `json:\"isHidden\"`\n\tLinkPath   string      `json:\"linkPath\"`\n\tType       string      `json:\"type\"`\n\tMode       string      `json:\"mode\"`\n\tMimeType   string      `json:\"mimeType\"`\n\tUpdateTime time.Time   `json:\"updateTime\"`\n\tModTime    time.Time   `json:\"modTime\"`\n\tFileMode   os.FileMode `json:\"-\"`\n\tItems      []*FileInfo `json:\"items\"`\n\tItemTotal  int         `json:\"itemTotal\"`\n\tFavoriteID uint        `json:\"favoriteID\"`\n\tIsDetail   bool        `json:\"isDetail\"`\n}\n\ntype FileOption struct {\n\tPath       string `json:\"path\"`\n\tSearch     string `json:\"search\"`\n\tContainSub bool   `json:\"containSub\"`\n\tExpand     bool   `json:\"expand\"`\n\tDir        bool   `json:\"dir\"`\n\tShowHidden bool   `json:\"showHidden\"`\n\tPage       int    `json:\"page\"`\n\tPageSize   int    `json:\"pageSize\"`\n\tSortBy     string `json:\"sortBy\"`\n\tSortOrder  string `json:\"sortOrder\"`\n\tIsDetail   bool   `json:\"isDetail\"`\n}\n\ntype FileSearchInfo struct {\n\tPath string `json:\"path\"`\n\tfs.FileInfo\n}\n\nfunc NewFileInfo(op FileOption) (*FileInfo, error) {\n\tvar appFs = afero.NewOsFs()\n\n\tinfo, err := appFs.Stat(op.Path)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, buserr.New(\"ErrLinkPathNotFound\")\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tfile := &FileInfo{\n\t\tFs:        appFs,\n\t\tPath:      op.Path,\n\t\tName:      info.Name(),\n\t\tIsDir:     info.IsDir(),\n\t\tFileMode:  info.Mode(),\n\t\tModTime:   info.ModTime(),\n\t\tSize:      info.Size(),\n\t\tIsSymlink: IsSymlink(info.Mode()),\n\t\tExtension: filepath.Ext(info.Name()),\n\t\tIsHidden:  IsHidden(op.Path),\n\t\tMode:      fmt.Sprintf(\"%04o\", info.Mode().Perm()),\n\t\tUser:      GetUsername(info.Sys().(*syscall.Stat_t).Uid),\n\t\tUid:       strconv.FormatUint(uint64(info.Sys().(*syscall.Stat_t).Uid), 10),\n\t\tGid:       strconv.FormatUint(uint64(info.Sys().(*syscall.Stat_t).Gid), 10),\n\t\tGroup:     GetGroup(info.Sys().(*syscall.Stat_t).Gid),\n\t\tMimeType:  GetMimeType(op.Path),\n\t\tIsDetail:  op.IsDetail,\n\t}\n\tfavoriteRepo := repo.NewIFavoriteRepo()\n\tfavorite, _ := favoriteRepo.GetFirst(favoriteRepo.WithByPath(op.Path))\n\tif favorite.ID > 0 {\n\t\tfile.FavoriteID = favorite.ID\n\t}\n\n\tif file.IsSymlink {\n\t\tlinkPath := GetSymlink(op.Path)\n\t\tif !filepath.IsAbs(linkPath) {\n\t\t\tdir := filepath.Dir(op.Path)\n\t\t\tvar err error\n\t\t\tlinkPath, err = filepath.Abs(filepath.Join(dir, linkPath))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tfile.LinkPath = linkPath\n\t\ttargetInfo, err := appFs.Stat(linkPath)\n\t\tif err != nil {\n\t\t\tfile.IsDir = false\n\t\t\tfile.Mode = \"-\"\n\t\t\tfile.User = \"-\"\n\t\t\tfile.Group = \"-\"\n\t\t} else {\n\t\t\tfile.IsDir = targetInfo.IsDir()\n\t\t}\n\t\tfile.Extension = filepath.Ext(file.LinkPath)\n\t}\n\tif op.Expand {\n\t\tif err := handleExpansion(file, op); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn file, nil\n}\n\nfunc handleExpansion(file *FileInfo, op FileOption) error {\n\tif file.IsDir {\n\t\treturn file.listChildren(op)\n\t}\n\n\tif !file.IsDetail {\n\t\treturn file.getContent()\n\t}\n\n\treturn nil\n}\n\nfunc (f *FileInfo) search(search string, count int) (files []FileSearchInfo, total int, err error) {\n\tcmd := exec.Command(\"find\", f.Path, \"-name\", fmt.Sprintf(\"*%s*\", search))\n\toutput, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn\n\t}\n\tif err = cmd.Start(); err != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\t_ = cmd.Wait()\n\t\t_ = cmd.Process.Kill()\n\t}()\n\n\tscanner := bufio.NewScanner(output)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tinfo, err := os.Stat(line)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\ttotal++\n\t\tif total > count {\n\t\t\tcontinue\n\t\t}\n\t\tfiles = append(files, FileSearchInfo{\n\t\t\tPath:     line,\n\t\t\tFileInfo: info,\n\t\t})\n\t}\n\tif err = scanner.Err(); err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc sortFileList(list []FileSearchInfo, sortBy, sortOrder string) {\n\tswitch sortBy {\n\tcase \"name\":\n\t\tif sortOrder == \"ascending\" {\n\t\t\tsort.Slice(list, func(i, j int) bool {\n\t\t\t\treturn list[i].Name() < list[j].Name()\n\t\t\t})\n\t\t} else {\n\t\t\tsort.Slice(list, func(i, j int) bool {\n\t\t\t\treturn list[i].Name() > list[j].Name()\n\t\t\t})\n\t\t}\n\tcase \"size\":\n\t\tif sortOrder == \"ascending\" {\n\t\t\tsort.Slice(list, func(i, j int) bool {\n\t\t\t\treturn list[i].Size() < list[j].Size()\n\t\t\t})\n\t\t} else {\n\t\t\tsort.Slice(list, func(i, j int) bool {\n\t\t\t\treturn list[i].Size() > list[j].Size()\n\t\t\t})\n\t\t}\n\tcase \"modTime\":\n\t\tif sortOrder == \"ascending\" {\n\t\t\tsort.Slice(list, func(i, j int) bool {\n\t\t\t\treturn list[i].ModTime().Before(list[j].ModTime())\n\t\t\t})\n\t\t} else {\n\t\t\tsort.Slice(list, func(i, j int) bool {\n\t\t\t\treturn list[i].ModTime().After(list[j].ModTime())\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc (f *FileInfo) listChildren(option FileOption) error {\n\tvar (\n\t\tfiles []FileSearchInfo\n\t\terr   error\n\t\ttotal int\n\t)\n\n\tif option.Search != \"\" && option.ContainSub {\n\t\tfiles, total, err = f.search(option.Search, option.Page*option.PageSize)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tfiles, err = f.getFiles(option)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\titems, err := f.processFiles(files, option)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif option.ContainSub {\n\t\tf.ItemTotal = total\n\t}\n\tstart := (option.Page - 1) * option.PageSize\n\tend := option.PageSize + start\n\tvar result []*FileInfo\n\tif start < 0 || start > f.ItemTotal || end < 0 || start > end {\n\t\tresult = items\n\t} else {\n\t\tif end > f.ItemTotal {\n\t\t\tresult = items[start:]\n\t\t} else {\n\t\t\tresult = items[start:end]\n\t\t}\n\t}\n\n\tf.Items = result\n\treturn nil\n}\n\nfunc (f *FileInfo) getFiles(option FileOption) ([]FileSearchInfo, error) {\n\tinfos, err := os.ReadDir(f.Path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar (\n\t\tdirs     []FileSearchInfo\n\t\tfileList []FileSearchInfo\n\t)\n\n\tfor _, file := range infos {\n\t\tfileInfo, err := file.Info()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tinfo := FileSearchInfo{\n\t\t\tPath:     f.Path,\n\t\t\tFileInfo: fileInfo,\n\t\t}\n\t\tif file.IsDir() {\n\t\t\tdirs = append(dirs, info)\n\t\t} else {\n\t\t\tfileList = append(fileList, info)\n\t\t}\n\t}\n\n\tsortFileList(dirs, option.SortBy, option.SortOrder)\n\tsortFileList(fileList, option.SortBy, option.SortOrder)\n\n\treturn append(dirs, fileList...), nil\n}\n\nfunc (f *FileInfo) processFiles(files []FileSearchInfo, option FileOption) ([]*FileInfo, error) {\n\tvar items []*FileInfo\n\n\tfor _, df := range files {\n\t\tif shouldSkipFile(df, option) {\n\t\t\tcontinue\n\t\t}\n\n\t\tname, fPath := f.getFilePathAndName(option, df)\n\n\t\tif !option.ShowHidden && IsHidden(name) {\n\t\t\tcontinue\n\t\t}\n\t\tf.ItemTotal++\n\n\t\tisSymlink, isInvalidLink := f.checkSymlink(df)\n\n\t\tfile := &FileInfo{\n\t\t\tFs:        f.Fs,\n\t\t\tName:      name,\n\t\t\tSize:      df.Size(),\n\t\t\tModTime:   df.ModTime(),\n\t\t\tFileMode:  df.Mode(),\n\t\t\tIsDir:     df.IsDir(),\n\t\t\tIsSymlink: isSymlink,\n\t\t\tIsHidden:  IsHidden(fPath),\n\t\t\tExtension: filepath.Ext(name),\n\t\t\tPath:      fPath,\n\t\t\tMode:      fmt.Sprintf(\"%04o\", df.Mode().Perm()),\n\t\t\tUser:      GetUsername(df.Sys().(*syscall.Stat_t).Uid),\n\t\t\tGroup:     GetGroup(df.Sys().(*syscall.Stat_t).Gid),\n\t\t\tUid:       strconv.FormatUint(uint64(df.Sys().(*syscall.Stat_t).Uid), 10),\n\t\t\tGid:       strconv.FormatUint(uint64(df.Sys().(*syscall.Stat_t).Gid), 10),\n\t\t}\n\t\tfavoriteRepo := repo.NewIFavoriteRepo()\n\t\tfavorite, _ := favoriteRepo.GetFirst(favoriteRepo.WithByPath(fPath))\n\t\tif favorite.ID > 0 {\n\t\t\tfile.FavoriteID = favorite.ID\n\t\t}\n\t\tif isSymlink {\n\t\t\tlinkPath := GetSymlink(fPath)\n\t\t\tif !filepath.IsAbs(linkPath) {\n\t\t\t\tdir := filepath.Dir(fPath)\n\t\t\t\tvar err error\n\t\t\t\tlinkPath, err = filepath.Abs(filepath.Join(dir, linkPath))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\tfile.LinkPath = linkPath\n\t\t\ttargetInfo, err := file.Fs.Stat(linkPath)\n\t\t\tif err != nil {\n\t\t\t\tfile.IsDir = false\n\t\t\t\tfile.Mode = \"-\"\n\t\t\t\tfile.User = \"-\"\n\t\t\t\tfile.Group = \"-\"\n\t\t\t} else {\n\t\t\t\tfile.IsDir = targetInfo.IsDir()\n\t\t\t}\n\t\t\tfile.Extension = filepath.Ext(file.LinkPath)\n\t\t}\n\t\tif df.Size() > 0 {\n\t\t\tfile.MimeType = GetMimeType(fPath)\n\t\t}\n\t\tif isInvalidLink {\n\t\t\tfile.Type = \"invalid_link\"\n\t\t}\n\t\titems = append(items, file)\n\t}\n\n\treturn items, nil\n}\n\nfunc shouldSkipFile(df FileSearchInfo, option FileOption) bool {\n\tif option.Dir && !df.IsDir() {\n\t\treturn true\n\t}\n\n\tif option.Search != \"\" && !option.ContainSub {\n\t\tlowerName := strings.ToLower(df.Name())\n\t\tlowerSearch := strings.ToLower(option.Search)\n\t\tif !strings.Contains(lowerName, lowerSearch) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (f *FileInfo) getFilePathAndName(option FileOption, df FileSearchInfo) (string, string) {\n\tname := df.Name()\n\tfPath := path.Join(df.Path, df.Name())\n\n\tif option.Search != \"\" && option.ContainSub {\n\t\tfPath = df.Path\n\t\tname = strings.TrimPrefix(strings.TrimPrefix(fPath, f.Path), \"/\")\n\t}\n\n\treturn name, fPath\n}\n\nfunc (f *FileInfo) checkSymlink(df FileSearchInfo) (bool, bool) {\n\tisSymlink := false\n\tisInvalidLink := false\n\n\tif IsSymlink(df.Mode()) {\n\t\tisSymlink = true\n\t\tinfo, err := f.Fs.Stat(path.Join(df.Path, df.Name()))\n\t\tif err == nil {\n\t\t\tdf.FileInfo = info\n\t\t} else {\n\t\t\tisInvalidLink = true\n\t\t}\n\t}\n\n\treturn isSymlink, isInvalidLink\n}\n\nfunc (f *FileInfo) getContent() error {\n\tif IsBlockDevice(f.FileMode) {\n\t\treturn buserr.New(\"ErrFileCanNotRead\")\n\t}\n\tif f.Size > 10*1024*1024 {\n\t\treturn buserr.New(\"ErrFileToLarge\")\n\t}\n\tafs := &afero.Afero{Fs: f.Fs}\n\tcByte, err := afs.ReadFile(f.Path)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tif len(cByte) > 0 && DetectBinary(cByte) {\n\t\treturn buserr.New(\"ErrFileCanNotRead\")\n\t}\n\tf.Content = string(cByte)\n\treturn nil\n}\n\nfunc DetectBinary(buf []byte) bool {\n\tmimeType := http.DetectContentType(buf)\n\tif !strings.HasPrefix(mimeType, \"text/\") {\n\t\twhiteByte := 0\n\t\tn := min(1024, len(buf))\n\t\tfor i := 0; i < n; i++ {\n\t\t\tif (buf[i] >= 0x20) || buf[i] == 9 || buf[i] == 10 || buf[i] == 13 {\n\t\t\t\twhiteByte++\n\t\t\t} else if buf[i] <= 6 || (buf[i] >= 14 && buf[i] <= 31) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn whiteByte < 1\n\t}\n\treturn false\n\n}\n\nfunc min(x, y int) int {\n\tif x < y {\n\t\treturn x\n\t}\n\treturn y\n}\n\ntype CompressType string\n\nconst (\n\tZip      CompressType = \"zip\"\n\tGz       CompressType = \"gz\"\n\tBz2      CompressType = \"bz2\"\n\tTarBz2   CompressType = \"tar.bz2\"\n\tTar      CompressType = \"tar\"\n\tTarGz    CompressType = \"tar.gz\"\n\tTgz      CompressType = \"tgz\"\n\tXz       CompressType = \"xz\"\n\tTarXz    CompressType = \"tar.xz\"\n\tSdkZip   CompressType = \"sdkZip\"\n\tSdkTarGz CompressType = \"sdkTarGz\"\n\tRar      CompressType = \"rar\"\n\tX7z      CompressType = \"7z\"\n)\n"
  },
  {
    "path": "agent/utils/files/rar.go",
    "content": "package files\n\nimport (\n\t\"fmt\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\ntype RarArchiver struct {\n}\n\nfunc NewRarArchiver() ShellArchiver {\n\treturn &RarArchiver{}\n}\n\nfunc (z RarArchiver) Extract(filePath, dstDir string, _ string) error {\n\tif err := checkCmdAvailability(\"unrar\"); err != nil {\n\t\treturn err\n\t}\n\treturn cmd.RunDefaultBashCf(\"unrar x -y -o+ %q %q\", filePath, dstDir)\n}\n\nfunc (z RarArchiver) Compress(sourcePaths []string, dstFile string, _ string) (err error) {\n\tif err = checkCmdAvailability(\"rar\"); err != nil {\n\t\treturn err\n\t}\n\ttmpFile := path.Join(global.Dir.TmpDir, fmt.Sprintf(\"%s%s.rar\", common.RandStr(50), time.Now().Format(constant.DateTimeSlimLayout)))\n\top := NewFileOp()\n\tdefer func() {\n\t\t_ = op.DeleteFile(tmpFile)\n\t\tif err != nil {\n\t\t\t_ = op.DeleteFile(dstFile)\n\t\t}\n\t}()\n\n\tbaseDir := path.Dir(sourcePaths[0])\n\trelativePaths := make([]string, len(sourcePaths))\n\tfor i, sp := range sourcePaths {\n\t\trelativePaths[i] = path.Base(sp)\n\t}\n\n\tcmdArgs := append([]string{\"a\", \"-r\", tmpFile}, relativePaths...)\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(baseDir))\n\tif err = cmdMgr.Run(\"rar\", cmdArgs...); err != nil {\n\t\treturn err\n\t}\n\n\tif err = op.Mv(tmpFile, dstFile); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/files/tar.go",
    "content": "package files\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype TarArchiver struct {\n\tCmd          string\n\tCompressType CompressType\n}\n\nfunc NewTarArchiver(compressType CompressType) ShellArchiver {\n\treturn &TarArchiver{\n\t\tCmd:          \"tar\",\n\t\tCompressType: compressType,\n\t}\n}\n\nfunc (t TarArchiver) Extract(FilePath string, dstDir string, secret string) error {\n\treturn cmd.RunDefaultBashCf(\"%s %s \\\"%s\\\" -C \\\"%s\\\"\", t.Cmd, t.getOptionStr(\"extract\"), FilePath, dstDir)\n}\n\nfunc (t TarArchiver) Compress(sourcePaths []string, dstFile string, secret string) error {\n\treturn nil\n}\n\nfunc (t TarArchiver) getOptionStr(Option string) string {\n\tswitch t.CompressType {\n\tcase Tar:\n\t\tif Option == \"compress\" {\n\t\t\treturn \"cvf\"\n\t\t} else {\n\t\t\treturn \"xf\"\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "agent/utils/files/tar_gz.go",
    "content": "package files\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype TarGzArchiver struct {\n}\n\nfunc NewTarGzArchiver() ShellArchiver {\n\treturn &TarGzArchiver{}\n}\n\nfunc (t TarGzArchiver) Extract(filePath, dstDir string, secret string) error {\n\tif err := os.MkdirAll(dstDir, 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create destination dir: %w\", err)\n\t}\n\tvar err error\n\tcommands := \"\"\n\tif len(secret) != 0 {\n\t\textraCmd := fmt.Sprintf(\"openssl enc -d -aes-256-cbc -k '%s' -in '%s' | \", secret, filePath)\n\t\tcommands = fmt.Sprintf(\"%s tar -zxvf - -C '%s' > /dev/null 2>&1\", extraCmd, dstDir)\n\t\tglobal.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(\" %s \", secret), \"******\"))\n\t} else {\n\t\tcommands = fmt.Sprintf(\"tar -zxvf '%s' -C '%s' > /dev/null 2>&1\", filePath, dstDir)\n\t\tglobal.LOG.Debug(commands)\n\t}\n\tif err = cmd.RunDefaultBashC(commands); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (t TarGzArchiver) Compress(sourcePaths []string, dstFile string, secret string) error {\n\tvar itemDirs []string\n\tfor _, item := range sourcePaths {\n\t\titemDirs = append(itemDirs, fmt.Sprintf(\"\\\"%s\\\"\", filepath.Base(item)))\n\t}\n\titemDir := strings.Join(itemDirs, \" \")\n\taheadDir := filepath.Dir(sourcePaths[0])\n\tif len(aheadDir) == 0 {\n\t\taheadDir = \"/\"\n\t}\n\tcommands := \"\"\n\tif len(secret) != 0 {\n\t\textraCmd := fmt.Sprintf(\"| openssl enc -aes-256-cbc -salt -k '%s' -out '%s'\", secret, dstFile)\n\t\tcommands = fmt.Sprintf(\"tar -zcf - -C \\\"%s\\\" %s %s\", aheadDir, itemDir, extraCmd)\n\t\tglobal.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(\" '%s' \", secret), \" ****** \"))\n\t} else {\n\t\tcommands = fmt.Sprintf(\"tar -zcf \\\"%s\\\" -C \\\"%s\\\" %s\", dstFile, aheadDir, itemDir)\n\t\tglobal.LOG.Debug(commands)\n\t}\n\tif err := cmd.RunDefaultBashC(commands); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/files/utils.go",
    "content": "package files\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/req_helper\"\n\t\"golang.org/x/text/encoding\"\n\t\"golang.org/x/text/encoding/charmap\"\n\t\"golang.org/x/text/encoding/japanese\"\n\t\"golang.org/x/text/encoding/korean\"\n\t\"golang.org/x/text/encoding/simplifiedchinese\"\n\t\"golang.org/x/text/encoding/traditionalchinese\"\n\t\"golang.org/x/text/encoding/unicode\"\n)\n\nconst (\n\tMaxReadFileSize = 512 * 1024 * 1024\n\ttailBufSize     = int64(32768)\n)\n\nfunc IsSymlink(mode os.FileMode) bool {\n\treturn mode&os.ModeSymlink != 0\n}\n\nfunc IsBlockDevice(mode os.FileMode) bool {\n\treturn mode&os.ModeDevice != 0 && mode&os.ModeCharDevice == 0\n}\n\nfunc GetMimeType(path string) string {\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tdefer file.Close()\n\n\tbuffer := make([]byte, 512)\n\t_, err = file.Read(buffer)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tmimeType := http.DetectContentType(buffer)\n\treturn mimeType\n}\n\nfunc GetSymlink(path string) string {\n\tlinkPath, err := os.Readlink(path)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn linkPath\n}\n\nfunc GetUsername(uid uint32) string {\n\tusr, err := user.LookupId(strconv.Itoa(int(uid)))\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn usr.Username\n}\n\nfunc GetGroup(gid uint32) string {\n\tusr, err := user.LookupGroupId(strconv.Itoa(int(gid)))\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn usr.Name\n}\n\nconst dotCharacter = 46\n\nfunc IsHidden(path string) bool {\n\tbase := filepath.Base(path)\n\treturn len(base) > 1 && base[0] == dotCharacter\n}\n\nvar readerPool = sync.Pool{\n\tNew: func() interface{} {\n\t\treturn bufio.NewReaderSize(nil, 8192)\n\t},\n}\n\nvar tailBufPool = sync.Pool{\n\tNew: func() interface{} {\n\t\tbuf := make([]byte, tailBufSize)\n\t\treturn &buf\n\t},\n}\n\nfunc readLineTrimmed(reader *bufio.Reader) (string, error) {\n\tline, err := reader.ReadString('\\n')\n\tif err == io.EOF {\n\t\tif len(line) == 0 {\n\t\t\treturn \"\", io.EOF\n\t\t}\n\t\terr = nil\n\t}\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tline = strings.TrimSuffix(line, \"\\n\")\n\tline = strings.TrimSuffix(line, \"\\r\")\n\treturn line, nil\n}\n\nfunc TailFromEnd(filename string, lines int) ([]string, error) {\n\tfile, err := os.Open(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\n\tstat, err := file.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfileSize := stat.Size()\n\n\tbufPtr := tailBufPool.Get().(*[]byte)\n\tbuf := *bufPtr\n\tdefer tailBufPool.Put(bufPtr)\n\n\tvar result []string\n\tvar leftover string\n\n\tfor offset := fileSize; offset > 0 && len(result) < lines; {\n\t\treadSize := tailBufSize\n\t\tif offset < tailBufSize {\n\t\t\treadSize = offset\n\t\t}\n\t\toffset -= readSize\n\n\t\t_, err := file.ReadAt(buf[:readSize], offset)\n\t\tif err != nil && err != io.EOF {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdata := string(buf[:readSize]) + leftover\n\t\tlinesInChunk := strings.Split(data, \"\\n\")\n\n\t\tif offset > 0 {\n\t\t\tleftover = linesInChunk[0]\n\t\t\tlinesInChunk = linesInChunk[1:]\n\t\t} else {\n\t\t\tleftover = \"\"\n\t\t}\n\n\t\tfor i := len(linesInChunk) - 1; i >= 0; i-- {\n\t\t\tif len(result) >= lines {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif i == len(linesInChunk)-1 && linesInChunk[i] == \"\" && len(result) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// 反插数据\n\t\t\tresult = append(result, linesInChunk[i])\n\t\t}\n\t}\n\n\tif leftover != \"\" && len(result) < lines {\n\t\tresult = append(result, leftover)\n\t}\n\n\tif len(result) > lines {\n\t\tresult = result[:lines]\n\t}\n\n\t// 反转数据\n\tfor i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {\n\t\tresult[i], result[j] = result[j], result[i]\n\t}\n\n\treturn result, nil\n}\n\nfunc ReadFileByLine(filename string, page, pageSize int, latest bool) (res *dto.LogFileRes, err error) {\n\tif !NewFileOp().Stat(filename) {\n\t\treturn\n\t}\n\tif pageSize <= 0 {\n\t\terr = fmt.Errorf(\"pageSize must be positive\")\n\t\treturn\n\t}\n\tfile, err := os.Open(filename)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer file.Close()\n\n\tfi, err := file.Stat()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif fi.Size() > MaxReadFileSize {\n\t\terr = buserr.New(\"ErrLogFileToLarge\")\n\t\treturn\n\t}\n\n\tres = &dto.LogFileRes{}\n\treader := readerPool.Get().(*bufio.Reader)\n\treader.Reset(file)\n\tdefer readerPool.Put(reader)\n\n\tif latest {\n\t\tringBuf := make([]string, pageSize)\n\t\twriteIdx := 0\n\t\ttotalLines := 0\n\n\t\tfor {\n\t\t\tline, readErr := readLineTrimmed(reader)\n\t\t\tif readErr == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif readErr != nil {\n\t\t\t\terr = readErr\n\t\t\t\treturn\n\t\t\t}\n\t\t\tringBuf[writeIdx%pageSize] = line\n\t\t\twriteIdx++\n\t\t\ttotalLines++\n\t\t}\n\n\t\tif totalLines == 0 {\n\t\t\tres.Lines = []string{}\n\t\t\tres.TotalLines = 0\n\t\t\tres.TotalPages = 0\n\t\t\tres.IsEndOfFile = true\n\t\t\treturn\n\t\t}\n\n\t\ttotal := (totalLines + pageSize - 1) / pageSize\n\t\tres.TotalPages = total\n\t\tres.TotalLines = totalLines\n\n\t\tlastPageSize := totalLines % pageSize\n\t\tif lastPageSize == 0 {\n\t\t\tlastPageSize = pageSize\n\t\t}\n\t\tif lastPageSize > totalLines {\n\t\t\tlastPageSize = totalLines\n\t\t}\n\n\t\tresult := make([]string, 0, lastPageSize)\n\t\tstartIdx := writeIdx - lastPageSize\n\t\tfor i := 0; i < lastPageSize; i++ {\n\t\t\tidx := (startIdx + i) % pageSize\n\t\t\tresult = append(result, ringBuf[idx])\n\t\t}\n\t\tres.Lines = result\n\t\tres.IsEndOfFile = true\n\t} else {\n\t\tstartLine := (page - 1) * pageSize\n\t\tendLine := startLine + pageSize\n\t\tcurrentLine := 0\n\t\tlines := make([]string, 0, pageSize)\n\n\t\tfor {\n\t\t\tline, readErr := readLineTrimmed(reader)\n\t\t\tif readErr == io.EOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif readErr != nil {\n\t\t\t\terr = readErr\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif currentLine >= startLine && currentLine < endLine {\n\t\t\t\tlines = append(lines, line)\n\t\t\t}\n\t\t\tcurrentLine++\n\t\t}\n\n\t\tres.Lines = lines\n\t\tres.TotalLines = currentLine\n\t\ttotal := (currentLine + pageSize - 1) / pageSize\n\t\tres.TotalPages = total\n\t\tres.IsEndOfFile = page >= total\n\t}\n\n\treturn\n}\n\nfunc GetParentMode(path string) (os.FileMode, error) {\n\tabsPath, err := filepath.Abs(path)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tfor {\n\t\tfileInfo, err := os.Stat(absPath)\n\t\tif err == nil {\n\t\t\treturn fileInfo.Mode() & os.ModePerm, nil\n\t\t}\n\t\tif !os.IsNotExist(err) {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tparentDir := filepath.Dir(absPath)\n\t\tif parentDir == absPath {\n\t\t\treturn 0, fmt.Errorf(\"no existing directory found in the path: %s\", path)\n\t\t}\n\t\tabsPath = parentDir\n\t}\n}\n\nfunc IsInvalidChar(name string) bool {\n\treturn strings.Contains(name, \"&\")\n}\n\nfunc IsEmptyDir(dir string) bool {\n\tf, err := os.Open(dir)\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer f.Close()\n\t_, err = f.Readdirnames(1)\n\treturn err == io.EOF\n}\n\nfunc DownloadFileWithProxy(url, dst string) error {\n\tresp, cancel, err := req_helper.RequestFile(url, http.MethodGet, constant.TimeOut5m)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cancel()\n\tdefer resp.Close()\n\n\tout, err := os.Create(dst)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create download file [%s] error, err %s\", dst, err.Error())\n\t}\n\tdefer out.Close()\n\n\tif _, err = io.Copy(out, resp); err != nil {\n\t\treturn fmt.Errorf(\"save download file [%s] error, err %s\", dst, err.Error())\n\t}\n\treturn nil\n}\n\nfunc GetDecoderByName(name string) encoding.Encoding {\n\tswitch strings.ToLower(name) {\n\tcase \"gbk\":\n\t\treturn simplifiedchinese.GBK\n\tcase \"gb18030\":\n\t\treturn simplifiedchinese.GB18030\n\tcase \"big5\":\n\t\treturn traditionalchinese.Big5\n\tcase \"euc-jp\":\n\t\treturn japanese.EUCJP\n\tcase \"iso-2022-jp\":\n\t\treturn japanese.ISO2022JP\n\tcase \"shift_jis\":\n\t\treturn japanese.ShiftJIS\n\tcase \"euc-kr\":\n\t\treturn korean.EUCKR\n\tcase \"utf-16be\":\n\t\treturn unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM)\n\tcase \"utf-16le\":\n\t\treturn unicode.UTF16(unicode.LittleEndian, unicode.ExpectBOM)\n\tcase \"windows-1250\":\n\t\treturn charmap.Windows1250\n\tcase \"windows-1251\":\n\t\treturn charmap.Windows1251\n\tcase \"windows-1252\":\n\t\treturn charmap.Windows1252\n\tcase \"windows-1253\":\n\t\treturn charmap.Windows1253\n\tcase \"windows-1254\":\n\t\treturn charmap.Windows1254\n\tcase \"windows-1255\":\n\t\treturn charmap.Windows1255\n\tcase \"windows-1256\":\n\t\treturn charmap.Windows1256\n\tcase \"windows-1257\":\n\t\treturn charmap.Windows1257\n\tcase \"windows-1258\":\n\t\treturn charmap.Windows1258\n\tcase \"iso-8859-1\":\n\t\treturn charmap.ISO8859_1\n\tcase \"iso-8859-2\":\n\t\treturn charmap.ISO8859_2\n\tcase \"iso-8859-3\":\n\t\treturn charmap.ISO8859_3\n\tcase \"iso-8859-4\":\n\t\treturn charmap.ISO8859_4\n\tcase \"iso-8859-5\":\n\t\treturn charmap.ISO8859_5\n\tcase \"iso-8859-6\":\n\t\treturn charmap.ISO8859_6\n\tcase \"iso-8859-7\":\n\t\treturn charmap.ISO8859_7\n\tcase \"iso-8859-8\":\n\t\treturn charmap.ISO8859_8\n\tcase \"iso-8859-9\":\n\t\treturn charmap.ISO8859_9\n\tcase \"iso-8859-13\":\n\t\treturn charmap.ISO8859_13\n\tcase \"iso-8859-15\":\n\t\treturn charmap.ISO8859_15\n\tdefault:\n\t\treturn encoding.Nop\n\t}\n}\n"
  },
  {
    "path": "agent/utils/files/x7z.go",
    "content": "package files\n\nimport (\n\t\"fmt\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\ntype X7zArchiver struct {\n}\n\nfunc NewX7zArchiver() ShellArchiver {\n\treturn &X7zArchiver{}\n}\n\nfunc (z X7zArchiver) Extract(filePath, dstDir string, _ string) error {\n\tif err := checkCmdAvailability(\"7z\"); err != nil {\n\t\treturn err\n\t}\n\treturn cmd.RunDefaultBashCf(\"7z x -y -o%q %q\", dstDir, filePath)\n}\n\nfunc (z X7zArchiver) Compress(sourcePaths []string, dstFile string, _ string) (err error) {\n\tif err = checkCmdAvailability(\"7z\"); err != nil {\n\t\treturn err\n\t}\n\ttmpFile := path.Join(global.Dir.TmpDir, fmt.Sprintf(\"%s%s.7z\", common.RandStr(50), time.Now().Format(constant.DateTimeSlimLayout)))\n\top := NewFileOp()\n\tdefer func() {\n\t\t_ = op.DeleteFile(tmpFile)\n\t\tif err != nil {\n\t\t\t_ = op.DeleteFile(dstFile)\n\t\t}\n\t}()\n\n\tbaseDir := path.Dir(sourcePaths[0])\n\trelativePaths := make([]string, len(sourcePaths))\n\tfor i, sp := range sourcePaths {\n\t\trelativePaths[i] = path.Base(sp)\n\t}\n\n\tcmdArgs := append([]string{\"a\", \"-r\", tmpFile}, relativePaths...)\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(baseDir))\n\tif err = cmdMgr.Run(\"7z\", cmdArgs...); err != nil {\n\t\treturn err\n\t}\n\n\tif err = op.Mv(tmpFile, dstFile); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/files/zip.go",
    "content": "package files\n\nimport (\n\t\"fmt\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\ntype ZipArchiver struct {\n}\n\nfunc NewZipArchiver() ShellArchiver {\n\treturn &ZipArchiver{}\n}\n\nfunc (z ZipArchiver) Extract(filePath, dstDir string, secret string) error {\n\tif err := checkCmdAvailability(\"unzip\"); err != nil {\n\t\treturn err\n\t}\n\treturn cmd.RunDefaultBashCf(\"unzip -qo %s -d %s\", filePath, dstDir)\n}\n\nfunc (z ZipArchiver) Compress(sourcePaths []string, dstFile string, _ string) error {\n\tvar err error\n\ttmpFile := path.Join(global.Dir.TmpDir, fmt.Sprintf(\"%s%s.zip\", common.RandStr(50), time.Now().Format(constant.DateTimeSlimLayout)))\n\top := NewFileOp()\n\tdefer func() {\n\t\t_ = op.DeleteFile(tmpFile)\n\t\tif err != nil {\n\t\t\t_ = op.DeleteFile(dstFile)\n\t\t}\n\t}()\n\tbaseDir := path.Dir(sourcePaths[0])\n\trelativePaths := make([]string, len(sourcePaths))\n\tfor i, sp := range sourcePaths {\n\t\trelativePaths[i] = path.Base(sp)\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(baseDir))\n\tif err = cmdMgr.Run(\"zip\", \"-qr\", tmpFile, strings.Join(relativePaths, \" \")); err != nil {\n\t\treturn err\n\t}\n\tif err = op.Mv(tmpFile, dstFile); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/firewall/client/firewalld.go",
    "content": "package client\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n)\n\ntype Firewall struct{}\n\nfunc NewFirewalld() (*Firewall, error) {\n\treturn &Firewall{}, nil\n}\n\nfunc (f *Firewall) Name() string {\n\treturn \"firewalld\"\n}\n\nfunc (f *Firewall) Status() (bool, error) {\n\tstdout, _ := cmd.RunDefaultWithStdoutBashC(\"LANGUAGE=en_US:en firewall-cmd --state\")\n\treturn stdout == \"running\\n\", nil\n}\n\nfunc (f *Firewall) Version() (string, error) {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"LANGUAGE=en_US:en firewall-cmd --version\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"load the firewall version failed, %v\", err)\n\t}\n\treturn strings.ReplaceAll(stdout, \"\\n \", \"\"), nil\n}\n\nfunc (f *Firewall) Start() error {\n\tif err := controller.HandleStart(\"firewalld\"); err != nil {\n\t\treturn fmt.Errorf(\"enable the firewall failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (f *Firewall) Stop() error {\n\tif err := controller.HandleStop(\"firewalld\"); err != nil {\n\t\treturn fmt.Errorf(\"stop the firewall failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (f *Firewall) Restart() error {\n\tif err := controller.HandleRestart(\"firewalld\"); err != nil {\n\t\treturn fmt.Errorf(\"restart the firewall failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (f *Firewall) Reload() error {\n\tif err := cmd.RunDefaultBashC(\"firewall-cmd --reload\"); err != nil {\n\t\treturn fmt.Errorf(\"reload firewall failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (f *Firewall) ListPort() ([]FireInfo, error) {\n\tvar wg sync.WaitGroup\n\tvar datas []FireInfo\n\twg.Add(2)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"firewall-cmd --zone=public --list-ports\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tports := strings.Split(strings.ReplaceAll(stdout, \"\\n\", \"\"), \" \")\n\t\tfor _, port := range ports {\n\t\t\tif len(port) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar itemPort FireInfo\n\t\t\tif strings.Contains(port, \"/\") {\n\t\t\t\titemPort.Port = strings.Split(port, \"/\")[0]\n\t\t\t\titemPort.Protocol = strings.Split(port, \"/\")[1]\n\t\t\t}\n\t\t\titemPort.Strategy = \"accept\"\n\t\t\tdatas = append(datas, itemPort)\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tstdout1, err := cmd.RunDefaultWithStdoutBashC(\"firewall-cmd --zone=public --list-rich-rules\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\trules := strings.Split(stdout1, \"\\n\")\n\t\tfor _, rule := range rules {\n\t\t\tif len(rule) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\titemRule := f.loadInfo(rule)\n\t\t\tif len(itemRule.Port) != 0 && (itemRule.Family == \"ipv4\" || (itemRule.Family == \"ipv6\" && len(itemRule.Address) != 0)) {\n\t\t\t\tdatas = append(datas, itemRule)\n\t\t\t}\n\t\t}\n\t}()\n\twg.Wait()\n\treturn datas, nil\n}\n\nfunc (f *Firewall) ListForward() ([]FireInfo, error) {\n\tif err := f.EnableForward(); err != nil {\n\t\tglobal.LOG.Errorf(\"init port forward failed, err: %v\", err)\n\t}\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"firewall-cmd --zone=public --list-forward-ports\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []FireInfo\n\tfor _, line := range strings.Split(stdout, \"\\n\") {\n\t\tline = strings.TrimSpace(line)\n\t\tparts := strings.Split(line, \":\")\n\t\tif len(parts) < 4 {\n\t\t\tcontinue\n\t\t}\n\t\tif parts[3] == \"toaddr=\" {\n\t\t\tparts[3] = \"127.0.0.1\"\n\t\t}\n\t\tdatas = append(datas, FireInfo{\n\t\t\tPort:       strings.TrimPrefix(parts[0], \"port=\"),\n\t\t\tProtocol:   strings.TrimPrefix(parts[1], \"proto=\"),\n\t\t\tTargetIP:   strings.TrimPrefix(parts[3], \"toaddr=\"),\n\t\t\tTargetPort: strings.TrimPrefix(parts[2], \"toport=\"),\n\t\t})\n\t}\n\treturn datas, nil\n}\n\nfunc (f *Firewall) ListAddress() ([]FireInfo, error) {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"firewall-cmd --zone=public --list-rich-rules\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar datas []FireInfo\n\trules := strings.Split(stdout, \"\\n\")\n\tfor _, rule := range rules {\n\t\tif len(rule) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\titemRule := f.loadInfo(rule)\n\t\tif len(itemRule.Port) == 0 && len(itemRule.Address) != 0 {\n\t\t\tdatas = append(datas, itemRule)\n\t\t}\n\t}\n\treturn datas, nil\n}\n\nfunc (f *Firewall) Port(port FireInfo, operation string) error {\n\tif cmd.CheckIllegal(operation, port.Protocol, port.Port) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\n\tif err := cmd.RunDefaultBashCf(\"firewall-cmd --zone=public --%s-port=%s/%s --permanent\", operation, port.Port, port.Protocol); err != nil {\n\t\treturn fmt.Errorf(\"%s (port: %s/%s strategy: %s) failed, %v\", operation, port.Port, port.Protocol, port.Strategy, err)\n\t}\n\treturn nil\n}\n\nfunc (f *Firewall) RichRules(rule FireInfo, operation string) error {\n\tif cmd.CheckIllegal(operation, rule.Address, rule.Protocol, rule.Port, rule.Strategy) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\truleStr := \"rule family=ipv4 \"\n\tif strings.Contains(rule.Address, \":\") {\n\t\truleStr = \"rule family=ipv6 \"\n\t}\n\tif len(rule.Address) != 0 {\n\t\truleStr += fmt.Sprintf(\"source address=%s \", rule.Address)\n\t}\n\tif len(rule.Port) != 0 {\n\t\truleStr += fmt.Sprintf(\"port port=%s \", rule.Port)\n\t}\n\tif len(rule.Protocol) != 0 {\n\t\truleStr += fmt.Sprintf(\"protocol=%s \", rule.Protocol)\n\t}\n\truleStr += rule.Strategy\n\tif err := cmd.RunDefaultBashCf(\"firewall-cmd --zone=public --%s-rich-rule '%s' --permanent\", operation, ruleStr); err != nil {\n\t\treturn fmt.Errorf(\"%s rich rules (%s) failed, %v\", operation, ruleStr, err)\n\t}\n\tif len(rule.Address) == 0 {\n\t\tif err := cmd.RunDefaultBashCf(\"firewall-cmd --zone=public --%s-rich-rule '%s' --permanent\", operation, strings.ReplaceAll(ruleStr, \"family=ipv4 \", \"family=ipv6 \")); err != nil {\n\t\t\treturn fmt.Errorf(\"%s rich rules (%s) failed, %v\", operation, strings.ReplaceAll(ruleStr, \"family=ipv4 \", \"family=ipv6 \"), err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (f *Firewall) PortForward(info Forward, operation string) error {\n\tif cmd.CheckIllegal(operation, info.Port, info.Protocol, info.TargetIP, info.TargetPort) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\truleStr := fmt.Sprintf(\"firewall-cmd --zone=public --%s-forward-port=port=%s:proto=%s:toport=%s --permanent\", operation, info.Port, info.Protocol, info.TargetPort)\n\tif info.TargetIP != \"\" && info.TargetIP != \"127.0.0.1\" && info.TargetIP != \"localhost\" {\n\t\truleStr = fmt.Sprintf(\"firewall-cmd --zone=public --%s-forward-port=port=%s:proto=%s:toaddr=%s:toport=%s --permanent\", operation, info.Port, info.Protocol, info.TargetIP, info.TargetPort)\n\t}\n\n\tif err := cmd.RunDefaultBashC(ruleStr); err != nil {\n\t\treturn fmt.Errorf(\"%s port forward failed, %s\", operation, err)\n\t}\n\tif err := f.Reload(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f *Firewall) loadInfo(line string) FireInfo {\n\tvar itemRule FireInfo\n\truleInfo := strings.Split(strings.ReplaceAll(line, \"\\\"\", \"\"), \" \")\n\tfor _, item := range ruleInfo {\n\t\tswitch {\n\t\tcase strings.Contains(item, \"family=\"):\n\t\t\titemRule.Family = strings.ReplaceAll(item, \"family=\", \"\")\n\t\tcase strings.Contains(item, \"ipset=\"):\n\t\t\titemRule.Address = strings.ReplaceAll(item, \"ipset=\", \"\")\n\t\tcase strings.Contains(item, \"address=\"):\n\t\t\titemRule.Address = strings.ReplaceAll(item, \"address=\", \"\")\n\t\tcase strings.Contains(item, \"port=\"):\n\t\t\titemRule.Port = strings.ReplaceAll(item, \"port=\", \"\")\n\t\tcase strings.Contains(item, \"protocol=\"):\n\t\t\titemRule.Protocol = strings.ReplaceAll(item, \"protocol=\", \"\")\n\t\tcase item == \"accept\" || item == \"drop\" || item == \"reject\":\n\t\t\titemRule.Strategy = item\n\t\t}\n\t}\n\treturn itemRule\n}\n\nfunc (f *Firewall) EnableForward() error {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"firewall-cmd --zone=public --query-masquerade\")\n\tif err != nil {\n\t\tif strings.HasSuffix(strings.TrimSpace(stdout), \"no\") {\n\t\t\tif err := cmd.RunDefaultBashC(\"firewall-cmd --zone=public --add-masquerade --permanent\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn f.Reload()\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/firewall/client/info.go",
    "content": "package client\n\ntype FireInfo struct {\n\tID       uint   `json:\"id\"`\n\tChain    string `json:\"chain\"`\n\tFamily   string `json:\"family\"`  // ipv4 ipv6\n\tAddress  string `json:\"address\"` // Anywhere\n\tPort     string `json:\"port\"`\n\tProtocol string `json:\"protocol\"` // tcp udp tcp/udp\n\tStrategy string `json:\"strategy\"` // accept drop\n\n\tNum        string `json:\"num\"`\n\tTargetIP   string `json:\"targetIP\"`\n\tTargetPort string `json:\"targetPort\"`\n\tInterface  string `json:\"interface\"`\n\n\tUsedStatus  string `json:\"usedStatus\"`\n\tDescription string `json:\"description\"`\n}\n\ntype Forward struct {\n\tNum        string `json:\"num\"`\n\tProtocol   string `json:\"protocol\"`\n\tPort       string `json:\"port\"`\n\tTargetIP   string `json:\"targetIP\"`\n\tTargetPort string `json:\"targetPort\"`\n\tInterface  string `json:\"interface\"`\n}\n"
  },
  {
    "path": "agent/utils/firewall/client/iptables/common.go",
    "content": "package iptables\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\nconst (\n\tChain1PanelPreRouting  = \"1PANEL_PREROUTING\"\n\tChain1PanelPostRouting = \"1PANEL_POSTROUTING\"\n\tChain1PanelForward     = \"1PANEL_FORWARD\"\n\tChainInput             = \"INPUT\"\n\tChainOutput            = \"OUTPUT\"\n\tChain1PanelInput       = \"1PANEL_INPUT\"\n\tChain1PanelOutput      = \"1PANEL_OUTPUT\"\n\tChain1PanelBasicBefore = \"1PANEL_BASIC_BEFORE\"\n\tChain1PanelBasic       = \"1PANEL_BASIC\"\n\tChain1PanelBasicAfter  = \"1PANEL_BASIC_AFTER\"\n)\n\nconst (\n\tEstablishedRule = \"-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment 'ESTABLISHED Whitelist'\"\n\tIoRuleIn        = \"-i lo -j ACCEPT -m comment --comment 'Loopback Whitelist'\"\n\tDropAllTcp      = \"-p tcp -j DROP\"\n\tDropAllUdp      = \"-p udp -j DROP\"\n\tAllowSSH        = \"-p tcp --dport ssh -j ACCEPT\"\n)\n\nconst (\n\tACCEPT   = \"ACCEPT\"\n\tDROP     = \"DROP\"\n\tREJECT   = \"REJECT\"\n\tANYWHERE = \"anywhere\"\n)\n\nconst (\n\tFilterTab = \"filter\"\n\tNatTab    = \"nat\"\n)\n\nfunc RunWithStd(tab, rule string) (string, error) {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(60*time.Second))\n\tstdout, err := cmdMgr.RunWithStdoutBashCf(\"%s iptables -w -t %s %s\", cmd.SudoHandleCmd(), tab, rule)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"iptables command failed [table=%s, rule=%s]: %v\", tab, rule, err)\n\t\treturn stdout, err\n\t}\n\treturn stdout, nil\n}\nfunc RunWithoutIgnore(tab, rule string) (string, error) {\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(60 * time.Second))\n\tstdout, err := cmdMgr.RunWithStdoutBashCf(\"%s iptables -t %s %s\", cmd.SudoHandleCmd(), tab, rule)\n\tif err != nil {\n\t\treturn stdout, err\n\t}\n\treturn stdout, nil\n}\nfunc Run(tab, rule string) error {\n\tif _, err := RunWithStd(tab, rule); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc NewChain(tab, chain string) error {\n\treturn Run(tab, \"-N \"+chain)\n}\n\nfunc ClearChain(tab, chain string) error {\n\treturn Run(tab, \"-F \"+chain)\n}\n\nfunc AddRule(tab, chain, rule string) error {\n\tif CheckRuleExist(tab, chain, rule) {\n\t\treturn nil\n\t}\n\treturn Run(tab, fmt.Sprintf(\"-A %s %s\", chain, rule))\n}\nfunc DeleteRule(tab, chain, rule string) error {\n\treturn Run(tab, fmt.Sprintf(\"-D %s %s\", chain, rule))\n}\n\nfunc CheckChainExist(tab, chain string) (bool, error) {\n\tstdout, err := RunWithStd(tab, fmt.Sprintf(\"-S | grep -w 'N %s'\", chain))\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"check chain %s from tab %s exist failed, err: %v\", chain, tab, err)\n\t\treturn false, fmt.Errorf(\"check chain %s from tab %s exist failed, err: %v\", chain, tab, err)\n\t}\n\tif strings.TrimSpace(stdout) == \"\" {\n\t\treturn false, nil\n\t}\n\treturn true, nil\n}\nfunc CheckChainBind(tab, parentChain, chain string) (bool, error) {\n\tstdout, err := RunWithStd(tab, fmt.Sprintf(\"-S %s | grep -- '-j %s'\", parentChain, chain))\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"check chain %s from tab %s is bind to %s failed, err: %v\", chain, tab, parentChain, err)\n\t\treturn false, fmt.Errorf(\"check chain %s from tab %s is bind to %s failed, err: %v\", chain, tab, parentChain, err)\n\t}\n\tif strings.TrimSpace(stdout) == \"\" {\n\t\treturn false, nil\n\t}\n\treturn true, nil\n}\nfunc CheckRuleExist(tab, chain, rule string) bool {\n\t_, err := RunWithoutIgnore(tab, fmt.Sprintf(\"-C %s %s\", chain, rule))\n\treturn err == nil\n}\n\nfunc AddChain(tab, chain string) error {\n\texists, err := CheckChainExist(tab, chain)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"check chain %s exist from tab %s failed, err: %w\", chain, tab, err)\n\t}\n\tif !exists {\n\t\tif err := NewChain(tab, chain); err != nil {\n\t\t\treturn fmt.Errorf(\"add chain %s for tab %s failed, err: %w\", tab, chain, err)\n\t\t}\n\t}\n\treturn nil\n}\nfunc BindChain(tab, targetChain, chain string, position int) error {\n\tline, err := FindChainNum(tab, targetChain, chain)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"find chain %s number from %s failed, err: %w\", chain, targetChain, err)\n\t}\n\tif line == 0 {\n\t\tif err := Run(tab, fmt.Sprintf(\"-I %s %d -j %s\", targetChain, position, chain)); err != nil {\n\t\t\treturn fmt.Errorf(\"bind chain %s to %s failed, err: %w\", chain, targetChain, err)\n\t\t}\n\t}\n\treturn nil\n}\nfunc UnbindChain(tab, targetChain, chain string) error {\n\tline, err := FindChainNum(tab, targetChain, chain)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"find chain %s number from %s failed, err: %w\", chain, targetChain, err)\n\t}\n\tif line != 0 {\n\t\treturn Run(tab, fmt.Sprintf(\"-D %s %v\", targetChain, line))\n\t}\n\treturn nil\n}\n\nfunc FindChainNum(tab, targetChain, chain string) (int, error) {\n\tstdout, err := RunWithStd(tab, fmt.Sprintf(\"-L %s --line-numbers -n | grep -w %s\", targetChain, chain))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to list rules in chain %s: %w\", targetChain, err)\n\t}\n\n\tlineItem := strings.TrimSpace(stdout)\n\tlines := strings.Split(lineItem, \"\\n\")\n\tfor _, line := range lines {\n\t\tfields := strings.Fields(line)\n\t\tif len(fields) < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tif fields[1] == chain {\n\t\t\titemNum, err := strconv.Atoi(fields[0])\n\t\t\treturn itemNum, err\n\t\t}\n\t}\n\treturn 0, nil\n}\n\nfunc AddChainWithAppend(tab, parentChain, chain string) error {\n\texists, err := CheckChainExist(tab, chain)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to check chain %s: %w\", chain, err)\n\t}\n\tif !exists {\n\t\tif err := NewChain(tab, chain); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create chain %s: %w\", chain, err)\n\t\t}\n\t}\n\tisBind, err := CheckChainBind(tab, parentChain, chain)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"check chain %s bind to %s failed, err: %w\", parentChain, chain, err)\n\t}\n\tif !isBind {\n\t\tif err := AppendChain(tab, parentChain, chain); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to append %s to %s: %w\", chain, parentChain, err)\n\t\t}\n\t}\n\treturn nil\n}\nfunc AppendChain(tab string, parentChain, chain string) error {\n\treturn Run(tab, fmt.Sprintf(\"-A %s -j %s\", parentChain, chain))\n}\n"
  },
  {
    "path": "agent/utils/firewall/client/iptables/filter.go",
    "content": "package iptables\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype FilterRules struct {\n\tID          uint   `json:\"id\"`\n\tChain       string `json:\"chain\"`\n\tProtocol    string `json:\"protocol\"`\n\tSrcPort     string `json:\"srcPort\"`\n\tDstPort     string `json:\"dstPort\"`\n\tSrcIP       string `json:\"srcIP\"`\n\tDstIP       string `json:\"dstIP\"`\n\tStrategy    string `json:\"strategy\"`\n\tDescription string `json:\"description\"`\n}\n\nfunc AddFilterRule(chain string, policy FilterRules) error {\n\tif err := validateRuleSafety(policy, chain); err != nil {\n\t\treturn err\n\t}\n\tiptablesArg := fmt.Sprintf(\"-A %s\", chain)\n\tif policy.Protocol != \"\" {\n\t\tiptablesArg += fmt.Sprintf(\" -p %s\", policy.Protocol)\n\t}\n\tif len(policy.SrcPort) != 0 {\n\t\tiptablesArg += fmt.Sprintf(\" --sport %s\", policy.SrcPort)\n\t}\n\tif len(policy.DstPort) != 0 {\n\t\tiptablesArg += fmt.Sprintf(\" --dport %s\", policy.DstPort)\n\t}\n\tif policy.SrcIP != \"\" {\n\t\tiptablesArg += fmt.Sprintf(\" -s %s\", policy.SrcIP)\n\t}\n\tif policy.DstIP != \"\" {\n\t\tiptablesArg += fmt.Sprintf(\" -d %s\", policy.DstIP)\n\t}\n\tiptablesArg += fmt.Sprintf(\" -j %s\", policy.Strategy)\n\n\treturn Run(FilterTab, iptablesArg)\n}\n\nfunc DeleteFilterRule(chain string, policy FilterRules) error {\n\tiptablesArg := fmt.Sprintf(\"-D %s\", chain)\n\tif policy.Protocol != \"\" {\n\t\tiptablesArg += fmt.Sprintf(\" -p %s\", policy.Protocol)\n\t}\n\tif len(policy.SrcPort) != 0 {\n\t\tiptablesArg += fmt.Sprintf(\" --sport %s\", policy.SrcPort)\n\t}\n\tif len(policy.DstPort) != 0 {\n\t\tiptablesArg += fmt.Sprintf(\" --dport %s\", policy.DstPort)\n\t}\n\tif policy.SrcIP != \"\" {\n\t\tiptablesArg += fmt.Sprintf(\" -s %s\", policy.SrcIP)\n\t}\n\tif policy.DstIP != \"\" {\n\t\tiptablesArg += fmt.Sprintf(\" -d %s\", policy.DstIP)\n\t}\n\tiptablesArg += fmt.Sprintf(\" -j %s\", policy.Strategy)\n\n\treturn Run(FilterTab, iptablesArg)\n}\n\nfunc ReadFilterRulesByChain(chain string) ([]FilterRules, error) {\n\tvar rules []FilterRules\n\tif cmd.CheckIllegal(chain) {\n\t\treturn rules, buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(20*time.Second))\n\tstdout, err := cmdMgr.RunWithStdoutBashCf(\"%s iptables -w -t %s -nL %s\", cmd.SudoHandleCmd(), FilterTab, chain)\n\tif err != nil {\n\t\treturn rules, fmt.Errorf(\"load filter fules by chain %s failed, %v\", chain, err)\n\t}\n\tlines := strings.Split(stdout, \"\\n\")\n\tfor i := 0; i < len(lines); i++ {\n\t\tfields := strings.Fields(lines[i])\n\t\tif len(fields) < 5 {\n\t\t\tcontinue\n\t\t}\n\t\tstrategy := strings.ToLower(fields[0])\n\t\tif strategy != \"accept\" && strategy != \"drop\" && strategy != \"reject\" {\n\t\t\tcontinue\n\t\t}\n\t\titemRule := FilterRules{\n\t\t\tChain:    chain,\n\t\t\tProtocol: loadProtocol(fields[1]),\n\t\t\tSrcPort:  loadPort(\"src\", fields),\n\t\t\tDstPort:  loadPort(\"dst\", fields),\n\t\t\tSrcIP:    loadIP(fields[3]),\n\t\t\tDstIP:    loadIP(fields[4]),\n\t\t\tStrategy: strategy,\n\t\t}\n\t\trules = append(rules, itemRule)\n\t}\n\treturn rules, nil\n}\n\nfunc LoadDefaultStrategy(chain string) (string, error) {\n\tif cmd.CheckIllegal(chain) {\n\t\treturn \"\", buserr.New(\"ErrCmdIllegal\")\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(20*time.Second))\n\tstdout, err := cmdMgr.RunWithStdoutBashCf(\"%s iptables -w -t %s -L %s\", cmd.SudoHandleCmd(), FilterTab, chain)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"load filter fules by chain %s failed, %v\", chain, err)\n\t}\n\tlines := strings.Split(stdout, \"\\n\")\n\tfor i := len(lines) - 1; i > 0; i-- {\n\t\tfields := strings.Fields(lines[i])\n\t\tif len(fields) < 5 {\n\t\t\tcontinue\n\t\t}\n\t\tif fields[0] == \"DROP\" && fields[1] == \"all\" && fields[3] == ANYWHERE && fields[4] == ANYWHERE {\n\t\t\treturn DROP, nil\n\t\t}\n\t}\n\treturn ACCEPT, nil\n}\n\nfunc LoadInitStatus(clientName, tab string) (bool, bool) {\n\tif clientName == \"firewalld\" {\n\t\treturn true, true\n\t}\n\tif clientName == \"ufw\" && tab != \"forward\" {\n\t\treturn true, true\n\t}\n\tswitch tab {\n\tcase \"base\":\n\t\tfilterRules, err := RunWithStd(FilterTab, \"-S\")\n\t\tif err != nil {\n\t\t\treturn false, false\n\t\t}\n\t\tlines := strings.Split(filterRules, \"\\n\")\n\t\tinitRules := []string{\n\t\t\t\"-N \" + Chain1PanelBasicBefore,\n\t\t\t\"-N \" + Chain1PanelBasic,\n\t\t\t\"-N \" + Chain1PanelBasicAfter,\n\t\t\tfmt.Sprintf(\"-A %s %s -j ACCEPT\", Chain1PanelBasicBefore, strings.ReplaceAll(strings.ReplaceAll(IoRuleIn, \"'\", \"\\\"\"), \" -j ACCEPT\", \"\")),\n\t\t\tfmt.Sprintf(\"-A %s %s -j ACCEPT\", Chain1PanelBasicBefore, strings.ReplaceAll(strings.ReplaceAll(EstablishedRule, \"'\", \"\\\"\"), \" -j ACCEPT\", \"\")),\n\t\t\tfmt.Sprintf(\"-A %s %s\", Chain1PanelBasicAfter, DropAllTcp),\n\t\t\tfmt.Sprintf(\"-A %s %s\", Chain1PanelBasicAfter, DropAllUdp),\n\t\t}\n\t\tbindRules := []string{\n\t\t\tfmt.Sprintf(\"-A %s -j %s\", ChainInput, Chain1PanelBasicBefore),\n\t\t\tfmt.Sprintf(\"-A %s -j %s\", ChainInput, Chain1PanelBasic),\n\t\t\tfmt.Sprintf(\"-A %s -j %s\", ChainInput, Chain1PanelBasicAfter),\n\t\t}\n\t\treturn checkWithInitAndBind(initRules, bindRules, lines)\n\tcase \"advance\":\n\t\tfilterRules, err := RunWithStd(FilterTab, \"-S\")\n\t\tif err != nil {\n\t\t\treturn false, false\n\t\t}\n\t\tlines := strings.Split(filterRules, \"\\n\")\n\t\tinitRules := []string{\n\t\t\t\"-N \" + Chain1PanelInput,\n\t\t\t\"-N \" + Chain1PanelOutput,\n\t\t}\n\t\tbindRules := []string{\n\t\t\tfmt.Sprintf(\"-A %s -j %s\", ChainInput, Chain1PanelInput),\n\t\t\tfmt.Sprintf(\"-A %s -j %s\", ChainOutput, Chain1PanelOutput),\n\t\t}\n\t\treturn checkWithInitAndBind(initRules, bindRules, lines)\n\tcase \"forward\":\n\t\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"cat /proc/sys/net/ipv4/ip_forward\")\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"check /proc/sys/net/ipv4/ip_forward failed, err: %v\", err)\n\t\t\treturn false, false\n\t\t}\n\t\tif strings.TrimSpace(stdout) == \"0\" {\n\t\t\treturn false, false\n\t\t}\n\t\tnatRules, err := RunWithStd(NatTab, \"-S\")\n\t\tif err != nil {\n\t\t\treturn false, false\n\t\t}\n\t\tlines := strings.Split(natRules, \"\\n\")\n\t\tinitRules := []string{\n\t\t\t\"-N \" + Chain1PanelPreRouting,\n\t\t\t\"-N \" + Chain1PanelPostRouting,\n\t\t}\n\t\tbindRules := []string{\n\t\t\tfmt.Sprintf(\"-A PREROUTING -j %s\", Chain1PanelPreRouting),\n\t\t\tfmt.Sprintf(\"-A POSTROUTING -j %s\", Chain1PanelPostRouting),\n\t\t}\n\t\tisNatInit, isNatBind := checkWithInitAndBind(initRules, bindRules, lines)\n\t\tif !isNatInit {\n\t\t\treturn false, false\n\t\t}\n\t\tfilterRules, err := RunWithStd(FilterTab, \"-S\")\n\t\tif err != nil {\n\t\t\treturn false, false\n\t\t}\n\t\tfilterLines := strings.Split(filterRules, \"\\n\")\n\t\tfilterInitRules := []string{\"-N \" + Chain1PanelForward}\n\t\tfilterBindRules := []string{fmt.Sprintf(\"-A FORWARD -j %s\", Chain1PanelForward)}\n\t\tisFilterInit, isFilterBind := checkWithInitAndBind(filterInitRules, filterBindRules, filterLines)\n\t\treturn isNatInit && isFilterInit, isNatBind && isFilterBind\n\tdefault:\n\t\treturn false, false\n\t}\n}\n\nfunc checkWithInitAndBind(initRules, bindRules []string, lines []string) (bool, bool) {\n\tfor _, rule := range initRules {\n\t\tfound := false\n\t\tfor _, line := range lines {\n\t\t\tif strings.TrimSpace(line) == strings.TrimSpace(rule) {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tglobal.LOG.Debugf(\"not found init rule: %s\", rule)\n\t\t\treturn false, false\n\t\t}\n\t}\n\tfor _, rule := range bindRules {\n\t\tfound := false\n\t\tfor _, line := range lines {\n\t\t\tif strings.TrimSpace(line) == strings.TrimSpace(rule) {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tglobal.LOG.Debugf(\"not found bind rule: %s\", rule)\n\t\t\treturn true, false\n\t\t}\n\t}\n\treturn true, true\n}\n\nfunc loadPort(position string, portStr []string) string {\n\tif len(portStr) < 7 {\n\t\treturn \"\"\n\t}\n\n\tvar portItem string\n\tif strings.Contains(portStr[6], \"spt:\") && position == \"src\" {\n\t\tportItem = strings.ReplaceAll(portStr[6], \"spt:\", \"\")\n\t}\n\tif strings.Contains(portStr[6], \"dpt:\") && position == \"dst\" {\n\t\tportItem = strings.ReplaceAll(portStr[6], \"dpt:\", \"\")\n\t}\n\tif strings.Contains(portStr[6], \"spts:\") && position == \"src\" {\n\t\tportItem = strings.ReplaceAll(portStr[6], \"spts:\", \"\")\n\t}\n\tif strings.Contains(portStr[6], \"dpts:\") && position == \"dst\" {\n\t\tportItem = strings.ReplaceAll(portStr[6], \"dpts:\", \"\")\n\t}\n\tportItem = strings.ReplaceAll(portItem, \":\", \"-\")\n\treturn portItem\n}\n\nfunc loadIP(ipStr string) string {\n\tif ipStr == ANYWHERE || ipStr == \"0.0.0.0/0\" {\n\t\treturn \"\"\n\t}\n\treturn ipStr\n}\n\nfunc loadProtocol(protocol string) string {\n\tswitch protocol {\n\tcase \"0\":\n\t\treturn \"all\"\n\tcase \"1\":\n\t\treturn \"icmp\"\n\tcase \"6\":\n\t\treturn \"tcp\"\n\tcase \"17\":\n\t\treturn \"udp\"\n\tdefault:\n\t\treturn protocol\n\t}\n}\n\nfunc validateRuleSafety(rule FilterRules, chain string) error {\n\tif strings.ToUpper(rule.Strategy) != \"DROP\" {\n\t\treturn nil\n\t}\n\n\tif chain == ChainInput || chain == Chain1PanelInput || chain == Chain1PanelBasic {\n\t\tif rule.SrcIP == \"0.0.0.0/0\" && len(rule.SrcPort) == 0 && len(rule.DstPort) == 0 {\n\t\t\treturn fmt.Errorf(\"unsafe DROP is not allowed\")\n\t\t}\n\t}\n\n\tif chain == ChainOutput || chain == Chain1PanelOutput || chain == Chain1PanelBasicAfter {\n\t\tif rule.DstIP == \"0.0.0.0/0\" && len(rule.DstPort) == 0 && len(rule.SrcPort) == 0 {\n\t\t\treturn fmt.Errorf(\"unsafe DROP is not allowed\")\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/firewall/client/iptables/forward.go",
    "content": "package iptables\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc AddForward(protocol, srcPort, dest, destPort, iface string, save bool) error {\n\tsrcPort = strings.ReplaceAll(srcPort, \"-\", \":\")\n\titemDstPort := strings.ReplaceAll(destPort, \"-\", \":\")\n\tif dest != \"\" && dest != \"127.0.0.1\" && dest != \"localhost\" {\n\t\tiptablesArg := fmt.Sprintf(\"-A %s\", Chain1PanelPreRouting)\n\t\tif iface != \"\" {\n\t\t\tiptablesArg += fmt.Sprintf(\" -i %s\", iface)\n\t\t}\n\t\tiptablesArg += fmt.Sprintf(\" -p %s --dport %s -j DNAT --to-destination %s:%s\", protocol, srcPort, dest, destPort)\n\t\tif err := Run(NatTab, iptablesArg); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := Run(NatTab, fmt.Sprintf(\"-A %s -d %s -p %s --dport %s -j MASQUERADE\", Chain1PanelPostRouting, dest, protocol, itemDstPort)); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := Run(FilterTab, fmt.Sprintf(\"-A %s -d %s -p %s --dport %s -j ACCEPT\", Chain1PanelForward, dest, protocol, itemDstPort)); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := Run(FilterTab, fmt.Sprintf(\"-A %s -s %s -p %s --sport %s -j ACCEPT\", Chain1PanelForward, dest, protocol, itemDstPort)); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tiptablesArg := fmt.Sprintf(\"-A %s\", Chain1PanelPreRouting)\n\t\tif iface != \"\" {\n\t\t\tiptablesArg += fmt.Sprintf(\" -i %s\", iface)\n\t\t}\n\t\tiptablesArg += fmt.Sprintf(\" -p %s --dport %s -j REDIRECT --to-port %s\", protocol, srcPort, destPort)\n\t\tif err := Run(NatTab, iptablesArg); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc DeleteForward(num string, protocol, srcPort, dest, destPort, iface string) error {\n\titemDstPort := strings.ReplaceAll(destPort, \"-\", \":\")\n\tif err := Run(NatTab, fmt.Sprintf(\"-D %s %s\", Chain1PanelPreRouting, num)); err != nil {\n\t\treturn err\n\t}\n\n\tif dest != \"\" && dest != \"127.0.0.1\" && dest != \"localhost\" {\n\t\tif err := Run(NatTab, fmt.Sprintf(\"-D %s -d %s -p %s --dport %s -j MASQUERADE\", Chain1PanelPostRouting, dest, protocol, itemDstPort)); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := Run(FilterTab, fmt.Sprintf(\"-D %s -d %s -p %s --dport %s -j ACCEPT\", Chain1PanelForward, dest, protocol, itemDstPort)); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := Run(FilterTab, fmt.Sprintf(\"-D %s -s %s -p %s --sport %s -j ACCEPT\", Chain1PanelForward, dest, protocol, itemDstPort)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc ListForward(chain ...string) ([]IptablesNatInfo, error) {\n\tif len(chain) == 0 {\n\t\tchain = append(chain, Chain1PanelPreRouting)\n\t}\n\tstdout, err := RunWithStd(NatTab, fmt.Sprintf(\"-nvL %s --line-numbers\", chain[0]))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar forwardList []IptablesNatInfo\n\tlines := strings.Split(stdout, \"\\n\")\n\tfor i := 0; i < len(lines); i++ {\n\t\tfields := strings.Fields(lines[i])\n\t\tif len(fields) < 13 {\n\t\t\tcontinue\n\t\t}\n\t\titem := IptablesNatInfo{\n\t\t\tNum:      fields[0],\n\t\t\tProtocol: loadProtocol(fields[4]),\n\t\t\tInIface:  fields[6],\n\t\t\tOutIface: fields[7],\n\t\t\tSource:   fields[8],\n\t\t\tSrcPort:  loadNatSrcPort(fields[11]),\n\t\t}\n\t\tif len(fields) == 15 && fields[13] == \"ports\" {\n\t\t\titem.DestPort = fields[14]\n\t\t}\n\t\tif len(fields) == 13 && strings.HasPrefix(fields[12], \"to:\") {\n\t\t\tparts := strings.Split(fields[12], \":\")\n\t\t\tif len(parts) > 2 {\n\t\t\t\titem.DestPort = parts[2]\n\t\t\t\titem.Destination = parts[1]\n\t\t\t}\n\t\t}\n\t\tif len(item.Destination) == 0 {\n\t\t\titem.Destination = \"127.0.0.1\"\n\t\t}\n\t\tforwardList = append(forwardList, item)\n\t}\n\n\treturn forwardList, nil\n}\n\nfunc loadNatSrcPort(portStr string) string {\n\tvar portItem string\n\tif strings.Contains(portStr, \"dpt:\") {\n\t\tportItem = strings.ReplaceAll(portStr, \"dpt:\", \"\")\n\t}\n\tif strings.Contains(portStr, \"dpts:\") {\n\t\tportItem = strings.ReplaceAll(portStr, \"dpts:\", \"\")\n\t}\n\tportItem = strings.ReplaceAll(portItem, \":\", \"-\")\n\treturn portItem\n}\n\ntype IptablesNatInfo struct {\n\tNum         string `json:\"num\"`\n\tProtocol    string `json:\"protocol\"`\n\tInIface     string `json:\"inIface\"`\n\tOutIface    string `json:\"outIface\"`\n\tSource      string `json:\"source\"`\n\tDestination string `json:\"destination\"`\n\tSrcPort     string `json:\"srcPort\"`\n\tDestPort    string `json:\"destPort\"`\n}\n"
  },
  {
    "path": "agent/utils/firewall/client/iptables/persistence.go",
    "content": "package iptables\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\nconst (\n\tBasicBeforeFileName = \"1panel_basic_before.rules\"\n\tBasicFileName       = \"1panel_basic.rules\"\n\tBasicAfterFileName  = \"1panel_basic_after.rules\"\n\tInputFileName       = \"1panel_input.rules\"\n\tOutputFileName      = \"1panel_out.rules\"\n\tForwardFileName     = \"1panel_forward.rules\"\n\tForwardFileName1    = \"1panel_forward_pre.rules\"\n\tForwardFileName2    = \"1panel_forward_post.rules\"\n)\n\nfunc SaveRulesToFile(tab, chain, fileName string) error {\n\trulesFile := path.Join(global.Dir.FirewallDir, fileName)\n\n\tstdout, err := RunWithStd(tab, fmt.Sprintf(\"-S %s\", chain))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list %s rules: %w\", chain, err)\n\t}\n\tvar rules []string\n\tlines := strings.Split(stdout, \"\\n\")\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif strings.HasPrefix(line, fmt.Sprintf(\"-A %s\", chain)) {\n\t\t\trules = append(rules, line)\n\t\t}\n\t}\n\n\tfile, err := os.Create(rulesFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create rules file: %w\", err)\n\t}\n\tdefer file.Close()\n\n\twriter := bufio.NewWriter(file)\n\tfor _, rule := range rules {\n\t\t_, err := writer.WriteString(rule + \"\\n\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write rule to file: %w\", err)\n\t\t}\n\t}\n\n\tif err := writer.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"failed to flush rules to file: %w\", err)\n\t}\n\n\tglobal.LOG.Infof(\"persistence rules to %s successful\", rulesFile)\n\treturn nil\n}\n\nfunc LoadRulesFromFile(tab, chain, fileName string) error {\n\tif err := AddChain(tab, chain); err != nil {\n\t\tglobal.LOG.Errorf(\"create chain %s failed: %v\", chain, err)\n\t\treturn err\n\t}\n\trulesFile := path.Join(global.Dir.FirewallDir, fileName)\n\tif _, err := os.Stat(rulesFile); os.IsNotExist(err) {\n\t\treturn nil\n\t}\n\tdata, err := os.ReadFile(rulesFile)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"read rules from file %s failed, err: %v\", rulesFile, err)\n\t\treturn err\n\t}\n\trules := strings.Split(string(data), \"\\n\")\n\tif err := ClearChain(tab, chain); err != nil {\n\t\tglobal.LOG.Warnf(\"clear existing rules from %s failed, err: %v\", chain, err)\n\t}\n\n\tfor _, rule := range rules {\n\t\tif strings.HasPrefix(rule, fmt.Sprintf(\"-A %s\", chain)) {\n\t\t\tif err := Run(tab, rule); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"apply rule '%s' failed, err: %v\", rule, err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/firewall/client/iptables.go",
    "content": "package client\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables\"\n)\n\ntype Iptables struct{}\n\nfunc NewIptables() (*Iptables, error) {\n\treturn &Iptables{}, nil\n}\n\nfunc (i *Iptables) Name() string {\n\treturn \"iptables\"\n}\n\nfunc (i *Iptables) Status() (bool, error) {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"iptables -L -n | head -1\")\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn strings.Contains(stdout, \"Chain\"), nil\n}\n\nfunc (i *Iptables) Start() error {\n\treturn nil\n}\n\nfunc (i *Iptables) Stop() error {\n\treturn nil\n}\n\nfunc (i *Iptables) Restart() error {\n\treturn nil\n}\n\nfunc (i *Iptables) Reload() error {\n\treturn nil\n}\n\nfunc (i *Iptables) Version() (string, error) {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"iptables --version\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to get iptables version: %w\", err)\n\t}\n\tparts := strings.Fields(stdout)\n\tif len(parts) >= 2 {\n\t\treturn strings.TrimPrefix(parts[1], \"v\"), nil\n\t}\n\treturn strings.TrimSpace(stdout), nil\n}\n\nfunc (i *Iptables) ListPort() ([]FireInfo, error) {\n\tvar datas []FireInfo\n\tbasicRules, err := iptables.ReadFilterRulesByChain(iptables.Chain1PanelBasic)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbeforeRules, _ := iptables.ReadFilterRulesByChain(iptables.Chain1PanelBasicBefore)\n\tbasicRules = append(basicRules, beforeRules...)\n\tfor _, item := range basicRules {\n\t\tif len(item.DstPort) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif item.Strategy == \"drop\" || item.Strategy == \"reject\" {\n\t\t\titem.Strategy = \"drop\"\n\t\t}\n\n\t\tdatas = append(datas, FireInfo{\n\t\t\tChain:    item.Chain,\n\t\t\tAddress:  item.SrcIP,\n\t\t\tProtocol: item.Protocol,\n\t\t\tPort:     item.DstPort,\n\t\t\tStrategy: item.Strategy,\n\t\t\tFamily:   \"ipv4\",\n\t\t})\n\t}\n\n\treturn datas, nil\n}\n\nfunc (i *Iptables) ListAddress() ([]FireInfo, error) {\n\tvar datas []FireInfo\n\tbasicRules, err := iptables.ReadFilterRulesByChain(iptables.Chain1PanelBasic)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, item := range basicRules {\n\t\tif len(item.DstPort) != 0 || len(item.SrcPort) != 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif item.Strategy == \"drop\" || item.Strategy == \"reject\" {\n\t\t\titem.Strategy = \"drop\"\n\t\t}\n\t\tdatas = append(datas, FireInfo{\n\t\t\tAddress:  item.SrcIP,\n\t\t\tStrategy: item.Strategy,\n\t\t\tFamily:   \"ipv4\",\n\t\t})\n\t}\n\treturn datas, nil\n}\n\nfunc (i *Iptables) Port(port FireInfo, operation string) error {\n\tif operation != \"add\" && operation != \"remove\" {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif len(port.Chain) == 0 {\n\t\tport.Chain = iptables.Chain1PanelBasic\n\t}\n\n\tportSpec, err := normalizePortSpec(port.Port)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tprotocol := port.Protocol\n\tif protocol == \"\" {\n\t\tprotocol = \"tcp\"\n\t}\n\n\taction := \"ACCEPT\"\n\tif port.Strategy == \"drop\" {\n\t\taction = \"DROP\"\n\t}\n\n\truleArgs := []string{fmt.Sprintf(\"-p %s\", protocol)}\n\truleArgs = append(ruleArgs, fmt.Sprintf(\"--dport %s\", portSpec), fmt.Sprintf(\"-j %s\", action))\n\truleSpec := strings.Join(ruleArgs, \" \")\n\tif operation == \"add\" {\n\t\tif err := iptables.AddRule(iptables.FilterTab, port.Chain, ruleSpec); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif err := iptables.DeleteRule(iptables.FilterTab, port.Chain, ruleSpec); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tname := iptables.BasicFileName\n\tif port.Chain == iptables.Chain1PanelBasicBefore {\n\t\tname = iptables.BasicBeforeFileName\n\t}\n\tif err := iptables.SaveRulesToFile(iptables.FilterTab, port.Chain, name); err != nil {\n\t\tglobal.LOG.Errorf(\"persistence for %s failed, err: %v\", iptables.Chain1PanelBasic, err)\n\t}\n\treturn nil\n}\n\nfunc (i *Iptables) RichRules(rule FireInfo, operation string) error {\n\tif operation != \"add\" && operation != \"remove\" {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif len(rule.Chain) == 0 {\n\t\trule.Chain = iptables.Chain1PanelBasic\n\t}\n\n\taddress := strings.TrimSpace(rule.Address)\n\tif strings.EqualFold(address, \"Anywhere\") {\n\t\taddress = \"\"\n\t}\n\n\taction := \"ACCEPT\"\n\tif rule.Strategy == \"drop\" {\n\t\taction = \"DROP\"\n\t}\n\n\tvar ruleArgs []string\n\tif address != \"\" {\n\t\truleArgs = append(ruleArgs, fmt.Sprintf(\"-s %s\", address))\n\t}\n\n\tprotocol := strings.TrimSpace(rule.Protocol)\n\tif rule.Port != \"\" && protocol == \"\" {\n\t\tprotocol = \"tcp\"\n\t}\n\n\tif protocol != \"\" {\n\t\truleArgs = append(ruleArgs, fmt.Sprintf(\"-p %s\", protocol))\n\t}\n\n\tif rule.Port != \"\" {\n\t\tportSegment, err := normalizePortSpec(rule.Port)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif protocol == \"\" {\n\t\t\treturn fmt.Errorf(\"protocol is required when specifying a port\")\n\t\t}\n\t\truleArgs = append(ruleArgs, fmt.Sprintf(\"--dport %s\", portSegment))\n\t}\n\n\truleArgs = append(ruleArgs, fmt.Sprintf(\"-j %s\", action))\n\truleSpec := strings.Join(ruleArgs, \" \")\n\tif operation == \"add\" {\n\t\tif err := iptables.AddRule(iptables.FilterTab, rule.Chain, ruleSpec); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif err := iptables.DeleteRule(iptables.FilterTab, rule.Chain, ruleSpec); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tname := iptables.BasicFileName\n\tif rule.Chain == iptables.Chain1PanelBasicBefore {\n\t\tname = iptables.BasicBeforeFileName\n\t}\n\tif err := iptables.SaveRulesToFile(iptables.FilterTab, rule.Chain, name); err != nil {\n\t\tglobal.LOG.Errorf(\"persistence for %s failed, err: %v\", iptables.Chain1PanelBasic, err)\n\t}\n\treturn nil\n}\n\nfunc (i *Iptables) PortForward(info Forward, operation string) error {\n\treturn iptablesPortForward(info, operation)\n}\n\nfunc (i *Iptables) EnableForward() error {\n\treturn EnableIptablesForward()\n}\n\nfunc (i *Iptables) ListForward() ([]FireInfo, error) {\n\treturn iptablesListForward()\n}\n\nfunc EnableIptablesForward() error {\n\tif err := cmd.RunDefaultBashC(\"echo 1 > /proc/sys/net/ipv4/ip_forward\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to enable IP forwarding: %w\", err)\n\t}\n\t_ = cmd.RunDefaultBashC(\"grep -q '^net.ipv4.ip_forward' /etc/sysctl.conf || echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf\")\n\t_ = cmd.RunDefaultBashC(\"sysctl -p\")\n\n\tif err := iptables.AddChainWithAppend(iptables.NatTab, \"PREROUTING\", iptables.Chain1PanelPreRouting); err != nil {\n\t\treturn err\n\t}\n\tif err := iptables.AddChainWithAppend(iptables.NatTab, \"POSTROUTING\", iptables.Chain1PanelPostRouting); err != nil {\n\t\treturn err\n\t}\n\tif err := iptables.AddChainWithAppend(iptables.FilterTab, \"FORWARD\", iptables.Chain1PanelForward); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc iptablesPortForward(info Forward, operation string) error {\n\tif operation != \"add\" && operation != \"remove\" {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif info.Protocol == \"\" || info.Port == \"\" || info.TargetPort == \"\" {\n\t\treturn fmt.Errorf(\"protocol, port, and target port are required\")\n\t}\n\tif operation == \"add\" {\n\t\tif err := iptables.AddForward(info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface, true); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif err := iptables.DeleteForward(info.Num, info.Protocol, info.Port, info.TargetIP, info.TargetPort, info.Interface); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tforwardPersistence()\n\treturn nil\n}\n\nfunc forwardPersistence() {\n\tif err := iptables.SaveRulesToFile(iptables.FilterTab, iptables.Chain1PanelForward, iptables.ForwardFileName); err != nil {\n\t\tglobal.LOG.Errorf(\"persistence for %s failed, err: %v\", iptables.Chain1PanelForward, err)\n\t}\n\tif err := iptables.SaveRulesToFile(iptables.NatTab, iptables.Chain1PanelPreRouting, iptables.ForwardFileName1); err != nil {\n\t\tglobal.LOG.Errorf(\"persistence for %s failed, err: %v\", iptables.Chain1PanelPreRouting, err)\n\t}\n\tif err := iptables.SaveRulesToFile(iptables.NatTab, iptables.Chain1PanelPostRouting, iptables.ForwardFileName2); err != nil {\n\t\tglobal.LOG.Errorf(\"persistence for %s failed, err: %v\", iptables.Chain1PanelPostRouting, err)\n\t}\n}\n\nfunc iptablesListForward() ([]FireInfo, error) {\n\tnatList, err := iptables.ListForward(iptables.Chain1PanelPreRouting)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list NAT rules: %w\", err)\n\t}\n\n\tvar datas []FireInfo\n\tfor _, nat := range natList {\n\t\tdatas = append(datas, FireInfo{\n\t\t\tNum:        nat.Num,\n\t\t\tProtocol:   nat.Protocol,\n\t\t\tPort:       strings.TrimPrefix(nat.SrcPort, \":\"),\n\t\t\tTargetIP:   nat.Destination,\n\t\t\tTargetPort: strings.TrimPrefix(nat.DestPort, \":\"),\n\t\t\tInterface:  nat.InIface,\n\t\t})\n\t}\n\n\treturn datas, nil\n}\n\nfunc parsePort(portStr string) (int, error) {\n\tport, err := strconv.Atoi(portStr)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid port number: %s\", portStr)\n\t}\n\tif port < 1 || port > 65535 {\n\t\treturn 0, fmt.Errorf(\"port out of range: %d\", port)\n\t}\n\treturn port, nil\n}\n\nfunc normalizePortSpec(port string) (string, error) {\n\tvalue := strings.TrimSpace(port)\n\tif value == \"\" {\n\t\treturn \"\", fmt.Errorf(\"port is required\")\n\t}\n\n\tseparator := \"\"\n\tif strings.Contains(value, \"-\") {\n\t\tseparator = \"-\"\n\t} else if strings.Contains(value, \":\") {\n\t\tseparator = \":\"\n\t}\n\n\tif separator != \"\" {\n\t\tparts := strings.Split(value, separator)\n\t\tif len(parts) != 2 {\n\t\t\treturn \"\", fmt.Errorf(\"invalid port range: %s\", port)\n\t\t}\n\t\tstart, err := parsePort(strings.TrimSpace(parts[0]))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tend, err := parsePort(strings.TrimSpace(parts[1]))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif start > end {\n\t\t\treturn \"\", fmt.Errorf(\"invalid port range: %d-%d\", start, end)\n\t\t}\n\t\treturn fmt.Sprintf(\"%d:%d\", start, end), nil\n\t}\n\n\tsingle, err := parsePort(value)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn fmt.Sprintf(\"%d\", single), nil\n}\n"
  },
  {
    "path": "agent/utils/firewall/client/ufw.go",
    "content": "package client\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\ntype Ufw struct {\n\tCmdStr string\n}\n\nfunc NewUfw() (*Ufw, error) {\n\tvar ufw Ufw\n\tufw.CmdStr = fmt.Sprintf(\"LANGUAGE=en_US:en %s ufw\", cmd.SudoHandleCmd())\n\treturn &ufw, nil\n}\n\nfunc (f *Ufw) Name() string {\n\treturn \"ufw\"\n}\n\nfunc (f *Ufw) Status() (bool, error) {\n\tstdout, _ := cmd.RunDefaultWithStdoutBashCf(\"%s status | grep Status\", f.CmdStr)\n\tif stdout == \"Status: active\\n\" {\n\t\treturn true, nil\n\t}\n\tstdout1, _ := cmd.RunDefaultWithStdoutBashCf(\"%s status | grep 状态\", f.CmdStr)\n\tif stdout1 == \"状态： 激活\\n\" {\n\t\treturn true, nil\n\t}\n\treturn false, nil\n}\n\nfunc (f *Ufw) Version() (string, error) {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"%s version | grep ufw\", f.CmdStr)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"load the firewall status failed, %v\", err)\n\t}\n\tinfo := strings.ReplaceAll(stdout, \"\\n\", \"\")\n\treturn strings.ReplaceAll(info, \"ufw \", \"\"), nil\n}\n\nfunc (f *Ufw) Start() error {\n\tif err := cmd.RunDefaultBashCf(\"echo y | %s enable\", f.CmdStr); err != nil {\n\t\treturn fmt.Errorf(\"enable the firewall failed, %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (f *Ufw) Stop() error {\n\tif err := cmd.RunDefaultBashCf(\"%s disable\", f.CmdStr); err != nil {\n\t\treturn fmt.Errorf(\"stop the firewall failed, %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (f *Ufw) Restart() error {\n\tif err := f.Stop(); err != nil {\n\t\treturn err\n\t}\n\tif err := f.Start(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f *Ufw) Reload() error {\n\treturn nil\n}\n\nfunc (f *Ufw) ListPort() ([]FireInfo, error) {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"%s status verbose\", f.CmdStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tportInfos := strings.Split(stdout, \"\\n\")\n\tvar datas []FireInfo\n\tisStart := false\n\tfor _, line := range portInfos {\n\t\tif strings.HasPrefix(line, \"-\") {\n\t\t\tisStart = true\n\t\t\tcontinue\n\t\t}\n\t\tif !isStart {\n\t\t\tcontinue\n\t\t}\n\t\titemFire := f.loadInfo(line, \"port\")\n\t\tif len(itemFire.Port) != 0 && itemFire.Port != \"Anywhere\" && !strings.Contains(itemFire.Port, \".\") {\n\t\t\titemFire.Port = strings.ReplaceAll(itemFire.Port, \":\", \"-\")\n\t\t\tdatas = append(datas, itemFire)\n\t\t}\n\t}\n\treturn datas, nil\n}\n\nfunc (f *Ufw) ListAddress() ([]FireInfo, error) {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"%s status verbose\", f.CmdStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tportInfos := strings.Split(stdout, \"\\n\")\n\tvar datas []FireInfo\n\tisStart := false\n\tfor _, line := range portInfos {\n\t\tif strings.HasPrefix(line, \"-\") {\n\t\t\tisStart = true\n\t\t\tcontinue\n\t\t}\n\t\tif !isStart {\n\t\t\tcontinue\n\t\t}\n\t\tif !strings.Contains(line, \" IN\") {\n\t\t\tcontinue\n\t\t}\n\t\titemFire := f.loadInfo(line, \"address\")\n\t\tif strings.Contains(itemFire.Port, \".\") {\n\t\t\titemFire.Address += (\"-\" + itemFire.Port)\n\t\t\titemFire.Port = \"\"\n\t\t}\n\t\tif len(itemFire.Port) == 0 && len(itemFire.Address) != 0 {\n\t\t\tdatas = append(datas, itemFire)\n\t\t}\n\t}\n\treturn datas, nil\n}\n\nfunc (f *Ufw) Port(port FireInfo, operation string) error {\n\tswitch port.Strategy {\n\tcase \"accept\":\n\t\tport.Strategy = \"allow\"\n\tcase \"drop\":\n\t\tport.Strategy = \"deny\"\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported strategy %s\", port.Strategy)\n\t}\n\tif cmd.CheckIllegal(port.Protocol, port.Port) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\n\tcommand := fmt.Sprintf(\"%s %s %s\", f.CmdStr, port.Strategy, port.Port)\n\tif operation == \"remove\" {\n\t\tcommand = fmt.Sprintf(\"%s delete %s %s\", f.CmdStr, port.Strategy, port.Port)\n\t}\n\tif len(port.Protocol) != 0 {\n\t\tcommand += fmt.Sprintf(\"/%s\", port.Protocol)\n\t}\n\tif err := cmd.RunDefaultBashC(command); err != nil {\n\t\treturn fmt.Errorf(\"%s (%s) failed, %v\", operation, command, err)\n\t}\n\treturn nil\n}\n\nfunc (f *Ufw) RichRules(rule FireInfo, operation string) error {\n\tswitch rule.Strategy {\n\tcase \"accept\":\n\t\trule.Strategy = \"allow\"\n\tcase \"drop\":\n\t\trule.Strategy = \"deny\"\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported strategy %s\", rule.Strategy)\n\t}\n\n\tif cmd.CheckIllegal(operation, rule.Protocol, rule.Address, rule.Port) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\n\tinsertNum := f.loadInsertNum(rule, operation)\n\truleStr := fmt.Sprintf(\"%s insert %d %s \", f.CmdStr, insertNum, rule.Strategy)\n\tif operation == \"remove\" {\n\t\truleStr = fmt.Sprintf(\"%s delete %s \", f.CmdStr, rule.Strategy)\n\t}\n\tif len(rule.Protocol) != 0 {\n\t\truleStr += fmt.Sprintf(\"proto %s \", rule.Protocol)\n\t}\n\tif strings.Contains(rule.Address, \"-\") {\n\t\truleStr += fmt.Sprintf(\"from %s to %s \", strings.Split(rule.Address, \"-\")[0], strings.Split(rule.Address, \"-\")[1])\n\t} else {\n\t\truleStr += fmt.Sprintf(\"from %s \", rule.Address)\n\t}\n\tif len(rule.Port) != 0 {\n\t\truleStr += fmt.Sprintf(\"to any port %s \", rule.Port)\n\t}\n\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(ruleStr)\n\tif err != nil {\n\t\tif strings.Contains(stdout, \"ERROR: Invalid position\") || strings.Contains(stdout, \"ERROR: 无效位置\") {\n\t\t\tif err := cmd.RunDefaultBashC(strings.ReplaceAll(ruleStr, \"insert 1 \", \"\")); err != nil {\n\t\t\t\treturn fmt.Errorf(\"%s rich rules (%s), failed, %v\", operation, ruleStr, err)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"%s rich rules (%s), failed, %v\", operation, ruleStr, err)\n\t}\n\treturn nil\n}\n\nfunc (f *Ufw) PortForward(info Forward, operation string) error {\n\treturn iptablesPortForward(info, operation)\n}\n\nfunc (f *Ufw) EnableForward() error {\n\treturn EnableIptablesForward()\n}\n\nfunc (f *Ufw) ListForward() ([]FireInfo, error) {\n\treturn iptablesListForward()\n}\n\nfunc (f *Ufw) loadInfo(line string, fireType string) FireInfo {\n\tfields := strings.Fields(line)\n\tvar itemInfo FireInfo\n\tif strings.Contains(line, \"LIMIT\") || strings.Contains(line, \"ALLOW FWD\") {\n\t\treturn itemInfo\n\t}\n\tif len(fields) < 4 {\n\t\treturn itemInfo\n\t}\n\tif fields[1] == \"(v6)\" && fireType == \"port\" {\n\t\treturn itemInfo\n\t}\n\tif fields[0] == \"Anywhere\" && fireType != \"port\" {\n\t\titemInfo.Strategy = \"drop\"\n\t\tif fields[1] == \"ALLOW\" {\n\t\t\titemInfo.Strategy = \"accept\"\n\t\t}\n\t\tif fields[1] == \"(v6)\" {\n\t\t\tif fields[2] == \"ALLOW\" {\n\t\t\t\titemInfo.Strategy = \"accept\"\n\t\t\t}\n\t\t\titemInfo.Address = fields[4]\n\t\t} else {\n\t\t\titemInfo.Address = fields[3]\n\t\t}\n\t\treturn itemInfo\n\t}\n\tif strings.Contains(fields[0], \"/\") {\n\t\titemInfo.Port = strings.Split(fields[0], \"/\")[0]\n\t\titemInfo.Protocol = strings.Split(fields[0], \"/\")[1]\n\t} else {\n\t\titemInfo.Port = fields[0]\n\t\titemInfo.Protocol = \"tcp/udp\"\n\t}\n\titemInfo.Family = \"ipv4\"\n\tif fields[1] == \"ALLOW\" {\n\t\titemInfo.Strategy = \"accept\"\n\t} else {\n\t\titemInfo.Strategy = \"drop\"\n\t}\n\titemInfo.Address = fields[3]\n\n\treturn itemInfo\n}\n\nfunc (f *Ufw) loadInsertNum(rule FireInfo, operation string) int {\n\tif !strings.Contains(rule.Address, \":\") || operation == \"remove\" {\n\t\treturn 1\n\t}\n\trules, err := cmd.RunDefaultWithStdoutBashCf(\"%s status numbered\", f.CmdStr)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load ufw rules failed, err: %v\", err)\n\t\treturn 1\n\t}\n\tlines := strings.Split(rules, \"\\n\")\n\ti := 1\n\tfor _, item := range lines {\n\t\tfields := strings.Fields(item)\n\t\tif len(fields) < 4 {\n\t\t\tcontinue\n\t\t}\n\t\tif !strings.Contains(item, \"(v6)\") {\n\t\t\ti++\n\t\t}\n\t}\n\treturn i\n}\n"
  },
  {
    "path": "agent/utils/firewall/client.go",
    "content": "package firewall\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/firewall/client\"\n)\n\ntype FirewallClient interface {\n\tName() string // ufw firewalld\n\tStart() error\n\tStop() error\n\tRestart() error\n\tReload() error\n\tStatus() (bool, error)\n\tVersion() (string, error)\n\n\tListPort() ([]client.FireInfo, error)\n\tListForward() ([]client.FireInfo, error)\n\tListAddress() ([]client.FireInfo, error)\n\n\tPort(port client.FireInfo, operation string) error\n\tRichRules(rule client.FireInfo, operation string) error\n\tPortForward(info client.Forward, operation string) error\n\n\tEnableForward() error\n}\n\nfunc NewFirewallClient() (FirewallClient, error) {\n\tfirewalld := cmd.Which(\"firewalld\")\n\tufw := cmd.Which(\"ufw\")\n\n\tif firewalld && ufw {\n\t\treturn nil, errors.New(\"It is detected that the system has both firewalld and ufw services. To avoid conflicts, please uninstall and try again!\")\n\t}\n\tif firewalld {\n\t\treturn client.NewFirewalld()\n\t}\n\tif ufw {\n\t\treturn client.NewUfw()\n\t}\n\n\tiptables := cmd.Which(\"iptables\")\n\tif iptables {\n\t\treturn client.NewIptables()\n\t}\n\treturn nil, errors.New(\"No system firewall service detected (firewalld/ufw/iptables), please check and try again!\")\n}\n\nfunc LoadPingStatus() string {\n\tdata, err := os.ReadFile(\"/proc/sys/net/ipv4/icmp_echo_ignore_all\")\n\tif err != nil {\n\t\treturn constant.StatusNone\n\t}\n\tv6Data, v6err := os.ReadFile(\"/proc/sys/net/ipv6/icmp/echo_ignore_all\")\n\tif v6err != nil {\n\t\tif strings.TrimSpace(string(data)) == \"1\" {\n\t\t\treturn constant.StatusEnable\n\t\t}\n\t\treturn constant.StatusDisable\n\t} else {\n\t\tif strings.TrimSpace(string(data)) == \"1\" && strings.TrimSpace(string(v6Data)) == \"1\" {\n\t\t\treturn constant.StatusEnable\n\t\t}\n\t\treturn constant.StatusDisable\n\t}\n}\n\nfunc UpdatePingStatus(enable string) error {\n\tconst confPath = \"/etc/sysctl.conf\"\n\tconst panelSysctlPath = \"/etc/sysctl.d/98-onepanel.conf\"\n\n\tvar targetPath string\n\tvar applyCmd string\n\n\tif _, err := os.Stat(confPath); os.IsNotExist(err) {\n\t\ttargetPath = panelSysctlPath\n\t\tapplyCmd = fmt.Sprintf(\"%s sysctl --system\", cmd.SudoHandleCmd())\n\t\tif err := cmd.RunDefaultBashCf(\"%s mkdir -p /etc/sysctl.d\", cmd.SudoHandleCmd()); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create directory /etc/sysctl.d: %v\", err)\n\t\t}\n\t} else {\n\t\ttargetPath = confPath\n\t\tapplyCmd = fmt.Sprintf(\"%s sysctl -p\", cmd.SudoHandleCmd())\n\t}\n\n\tlineBytes, err := os.ReadFile(targetPath)\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"failed to read %s: %v\", targetPath, err)\n\t}\n\n\tif err := cmd.RunDefaultBashCf(\"echo %s | %s tee /proc/sys/net/ipv4/icmp_echo_ignore_all > /dev/null\", enable, cmd.SudoHandleCmd()); err != nil {\n\t\treturn fmt.Errorf(\"failed to apply ipv4 ping status temporarily: %v\", err)\n\t}\n\n\tvar hasIpv6 bool\n\tif _, err := os.Stat(\"/proc/sys/net/ipv6/icmp/echo_ignore_all\"); err == nil {\n\t\thasIpv6 = true\n\t\tif err := cmd.RunDefaultBashCf(\"echo %s | %s tee /proc/sys/net/ipv6/icmp/echo_ignore_all > /dev/null\", enable, cmd.SudoHandleCmd()); err != nil {\n\t\t\tglobal.LOG.Warnf(\"failed to apply ipv6 ping status temporarily: %v\", err)\n\t\t}\n\t}\n\n\tvar files []string\n\tif err == nil {\n\t\tfiles = strings.Split(string(lineBytes), \"\\n\")\n\t}\n\n\tvar newFiles []string\n\thasIPv4Line, hasIPv6Line := false, false\n\n\tfor _, line := range files {\n\t\tif strings.HasPrefix(strings.TrimSpace(line), \"net.ipv4.icmp_echo_ignore_all\") {\n\t\t\tnewFiles = append(newFiles, \"net.ipv4.icmp_echo_ignore_all=\"+enable)\n\t\t\thasIPv4Line = true\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(strings.TrimSpace(line), \"net.ipv6.icmp.echo_ignore_all\") {\n\t\t\tnewFiles = append(newFiles, \"net.ipv6.icmp.echo_ignore_all=\"+enable)\n\t\t\thasIPv6Line = true\n\t\t\tcontinue\n\t\t}\n\t\tnewFiles = append(newFiles, line)\n\t}\n\n\tif !hasIPv4Line {\n\t\tnewFiles = append(newFiles, \"net.ipv4.icmp_echo_ignore_all=\"+enable)\n\t}\n\tif hasIpv6 && !hasIPv6Line {\n\t\tnewFiles = append(newFiles, \"net.ipv6.icmp.echo_ignore_all=\"+enable)\n\t}\n\n\tfile, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open %s: %v\", targetPath, err)\n\t}\n\tdefer file.Close()\n\n\tif _, err = file.WriteString(strings.Join(newFiles, \"\\n\")); err != nil {\n\t\treturn fmt.Errorf(\"failed to write to %s: %v\", targetPath, err)\n\t}\n\n\tif err := cmd.RunDefaultBashC(applyCmd); err != nil {\n\t\tglobal.LOG.Warnf(\"failed to apply persistent config with '%s': %v\", applyCmd, err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/geo/geo.go",
    "content": "package geo\n\nimport (\n\t\"net\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/oschwald/maxminddb-golang\"\n)\n\ntype Location struct {\n\tEn string `maxminddb:\"en\"`\n\tZh string `maxminddb:\"zh\"`\n}\n\ntype LocationRes struct {\n\tIso       string   `maxminddb:\"iso\"`\n\tCountry   Location `maxminddb:\"country\"`\n\tLatitude  float64  `maxminddb:\"latitude\"`\n\tLongitude float64  `maxminddb:\"longitude\"`\n\tProvince  Location `maxminddb:\"province\"`\n}\n\nfunc NewGeo() (*maxminddb.Reader, error) {\n\tgeoPath := path.Join(global.Dir.DataDir, \"geo\", \"GeoIP.mmdb\")\n\treturn maxminddb.Open(geoPath)\n}\n\nfunc GetIPLocation(reader *maxminddb.Reader, ip, lang string) (string, error) {\n\tvar err error\n\tvar geoLocation LocationRes\n\tif reader == nil {\n\t\tgeoPath := path.Join(global.Dir.DataDir, \"geo\", \"GeoIP.mmdb\")\n\t\treader, err = maxminddb.Open(geoPath)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tipNet := net.ParseIP(ip)\n\terr = reader.Lookup(ipNet, &geoLocation)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif lang == \"zh\" {\n\t\treturn geoLocation.Country.Zh + \" \" + geoLocation.Province.Zh, nil\n\t}\n\treturn geoLocation.Country.En + \" \" + geoLocation.Province.En, nil\n}\n"
  },
  {
    "path": "agent/utils/ini_conf/ini.go",
    "content": "package ini_conf\n\nimport \"gopkg.in/ini.v1\"\n\nfunc GetIniValue(filePath, Group, Key string) (string, error) {\n\tcfg, err := ini.Load(filePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tservice, err := cfg.GetSection(Group)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tstartKey, err := service.GetKey(Key)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn startKey.Value(), nil\n}\n"
  },
  {
    "path": "agent/utils/mysql/client/info.go",
    "content": "package client\n\nimport (\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"database/sql\"\n\t\"errors\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/go-sql-driver/mysql\"\n)\n\ntype DBInfo struct {\n\tType     string `json:\"type\"`\n\tFrom     string `json:\"from\"`\n\tDatabase string `json:\"database\"`\n\tAddress  string `json:\"address\"`\n\tPort     uint   `json:\"port\"`\n\tUsername string `json:\"userName\"`\n\tPassword string `json:\"password\"`\n\n\tSSL        bool   `json:\"ssl\"`\n\tRootCert   string `json:\"rootCert\"`\n\tClientKey  string `json:\"clientKey\"`\n\tClientCert string `json:\"clientCert\"`\n\tSkipVerify bool   `json:\"skipVerify\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype CreateInfo struct {\n\tName       string `json:\"name\"`\n\tFormat     string `json:\"format\"`\n\tCollation  string `json:\"collation\"`\n\tVersion    string `json:\"version\"`\n\tUsername   string `json:\"userName\"`\n\tPassword   string `json:\"password\"`\n\tPermission string `json:\"permission\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype DeleteInfo struct {\n\tName       string `json:\"name\"`\n\tVersion    string `json:\"version\"`\n\tUsername   string `json:\"userName\"`\n\tPermission string `json:\"permission\"`\n\n\tForceDelete bool `json:\"forceDelete\"`\n\tTimeout     uint `json:\"timeout\"` // second\n}\n\ntype PasswordChangeInfo struct {\n\tName       string `json:\"name\"`\n\tVersion    string `json:\"version\"`\n\tUsername   string `json:\"userName\"`\n\tPassword   string `json:\"password\"`\n\tPermission string `json:\"permission\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype AccessChangeInfo struct {\n\tName          string `json:\"name\"`\n\tVersion       string `json:\"version\"`\n\tUsername      string `json:\"userName\"`\n\tPassword      string `json:\"password\"`\n\tOldPermission string `json:\"oldPermission\"`\n\tPermission    string `json:\"permission\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype BackupInfo struct {\n\tName      string   `json:\"name\"`\n\tType      string   `json:\"type\"`\n\tVersion   string   `json:\"version\"`\n\tFormat    string   `json:\"format\"`\n\tTargetDir string   `json:\"targetDir\"`\n\tFileName  string   `json:\"fileName\"`\n\tArgs      []string `json:\"args\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype FormatCollation struct {\n\tFormat    sql.NullString `json:\"format\" gorm:\"column:CHARACTER_SET_NAME\"`\n\tCollation sql.NullString `json:\"collation\" gorm:\"column:COLLATION_NAME\"`\n}\n\ntype RecoverInfo struct {\n\tName       string `json:\"name\"`\n\tType       string `json:\"type\"`\n\tVersion    string `json:\"version\"`\n\tFormat     string `json:\"format\"`\n\tSourceFile string `json:\"sourceFile\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype SyncDBInfo struct {\n\tName       string `json:\"name\"`\n\tFrom       string `json:\"from\"`\n\tMysqlName  string `json:\"mysqlName\"`\n\tFormat     string `json:\"format\"`\n\tCollation  string `json:\"collation\"`\n\tUsername   string `json:\"username\"`\n\tPassword   string `json:\"password\"`\n\tPermission string `json:\"permission\"`\n}\n\nfunc ConnWithSSL(ssl, skipVerify bool, clientKey, clientCert, rootCert string) (string, error) {\n\tif !ssl {\n\t\treturn \"\", nil\n\t}\n\ttlsConfig := &tls.Config{\n\t\tInsecureSkipVerify: skipVerify,\n\t}\n\tif len(rootCert) != 0 {\n\t\tpool := x509.NewCertPool()\n\t\tif ok := pool.AppendCertsFromPEM([]byte(rootCert)); !ok {\n\t\t\tglobal.LOG.Error(\"append certs from pem failed\")\n\t\t\treturn \"\", errors.New(\"unable to append root cert to pool\")\n\t\t}\n\t\ttlsConfig.RootCAs = pool\n\t}\n\tif len(clientCert) != 0 && len(clientKey) != 0 {\n\t\tcert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\ttlsConfig.Certificates = []tls.Certificate{cert}\n\t}\n\tif err := mysql.RegisterTLSConfig(\"cloudsql\", tlsConfig); err != nil {\n\t\tglobal.LOG.Errorf(\"register tls config failed, err: %v\", err)\n\t\treturn \"\", err\n\t}\n\treturn \"&tls=cloudsql\", nil\n}\n"
  },
  {
    "path": "agent/utils/mysql/client/local.go",
    "content": "package client\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\ntype Local struct {\n\tType          string\n\tPrefixCommand []string\n\tDatabase      string\n\tPassword      string\n\tContainerName string\n}\n\nfunc NewLocal(command []string, dbType, containerName, password, database string) *Local {\n\treturn &Local{Type: dbType, PrefixCommand: command, ContainerName: containerName, Password: password, Database: database}\n}\n\nfunc (r *Local) Create(info CreateInfo) error {\n\tcreateSql := fmt.Sprintf(\"create database `%s` default character set %s collate %s\", info.Name, info.Format, info.Collation)\n\tif len(info.Collation) == 0 {\n\t\tcreateSql = fmt.Sprintf(\"create database `%s` default character set %s\", info.Name, info.Format)\n\t}\n\tif err := r.ExecSQL(createSql, info.Timeout); err != nil {\n\t\tif strings.Contains(strings.ToLower(err.Error()), \"error 1007\") {\n\t\t\treturn buserr.New(\"ErrDatabaseIsExist\")\n\t\t}\n\t\treturn err\n\t}\n\n\tif err := r.CreateUser(info, true); err != nil {\n\t\t_ = r.ExecSQL(fmt.Sprintf(\"drop database if exists `%s`\", info.Name), info.Timeout)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error {\n\tvar userlist []string\n\tif strings.Contains(info.Permission, \",\") {\n\t\tips := strings.Split(info.Permission, \",\")\n\t\tfor _, ip := range ips {\n\t\t\tif len(ip) != 0 {\n\t\t\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, ip))\n\t\t\t}\n\t\t}\n\t} else {\n\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, info.Permission))\n\t}\n\n\tfor _, user := range userlist {\n\t\tif err := r.ExecSQL(fmt.Sprintf(\"create user %s identified by '%s';\", user, info.Password), info.Timeout); err != nil {\n\t\t\tif strings.Contains(strings.ToLower(err.Error()), \"error 1396\") {\n\t\t\t\treturn buserr.New(\"ErrUserIsExist\")\n\t\t\t}\n\t\t\tif withDeleteDB {\n\t\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\t\tName:        info.Name,\n\t\t\t\t\tVersion:     info.Version,\n\t\t\t\t\tUsername:    info.Username,\n\t\t\t\t\tPermission:  info.Permission,\n\t\t\t\t\tForceDelete: true,\n\t\t\t\t\tTimeout:     300})\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tgrantStr := fmt.Sprintf(\"grant all privileges on `%s`.* to %s\", info.Name, user)\n\t\tif info.Name == \"*\" {\n\t\t\tgrantStr = fmt.Sprintf(\"grant all privileges on *.* to %s\", user)\n\t\t}\n\t\tif strings.HasPrefix(info.Version, \"5.7\") || strings.HasPrefix(info.Version, \"5.6\") {\n\t\t\tgrantStr = fmt.Sprintf(\"%s identified by '%s' with grant option;\", grantStr, info.Password)\n\t\t} else {\n\t\t\tgrantStr = grantStr + \" with grant option;\"\n\t\t}\n\t\tif err := r.ExecSQL(grantStr, info.Timeout); err != nil {\n\t\t\tif withDeleteDB {\n\t\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\t\tName:        info.Name,\n\t\t\t\t\tVersion:     info.Version,\n\t\t\t\t\tUsername:    info.Username,\n\t\t\t\t\tPermission:  info.Permission,\n\t\t\t\t\tForceDelete: true,\n\t\t\t\t\tTimeout:     300})\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (r *Local) Delete(info DeleteInfo) error {\n\tvar userlist []string\n\tif strings.Contains(info.Permission, \",\") {\n\t\tips := strings.Split(info.Permission, \",\")\n\t\tfor _, ip := range ips {\n\t\t\tif len(ip) != 0 {\n\t\t\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, ip))\n\t\t\t}\n\t\t}\n\t} else {\n\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, info.Permission))\n\t}\n\n\tfor _, user := range userlist {\n\t\tif strings.HasPrefix(info.Version, \"5.6\") {\n\t\t\tif err := r.ExecSQL(fmt.Sprintf(\"drop user %s\", user), info.Timeout); err != nil && !info.ForceDelete {\n\t\t\t\treturn fmt.Errorf(\"drop user failed, err: %v\", err)\n\t\t\t}\n\t\t} else {\n\t\t\tif err := r.ExecSQL(fmt.Sprintf(\"drop user if exists %s\", user), info.Timeout); err != nil && !info.ForceDelete {\n\t\t\t\treturn fmt.Errorf(\"drop user failed, err: %v\", err)\n\t\t\t}\n\t\t}\n\t}\n\tif len(info.Name) != 0 {\n\t\tif err := r.ExecSQL(fmt.Sprintf(\"drop database if exists `%s`\", info.Name), info.Timeout); err != nil && !info.ForceDelete {\n\t\t\treturn fmt.Errorf(\"drop database failed, err: %v\", err)\n\t\t}\n\t}\n\tif !info.ForceDelete {\n\t\tglobal.LOG.Info(\"execute delete database sql successful, now start to drop uploads and records\")\n\t}\n\n\treturn nil\n}\n\nfunc (r *Local) ChangePassword(info PasswordChangeInfo) error {\n\tif info.Username != \"root\" {\n\t\tvar userlist []string\n\t\tif strings.Contains(info.Permission, \",\") {\n\t\t\tips := strings.Split(info.Permission, \",\")\n\t\t\tfor _, ip := range ips {\n\t\t\t\tif len(ip) != 0 {\n\t\t\t\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, ip))\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, info.Permission))\n\t\t}\n\n\t\tfor _, user := range userlist {\n\t\t\tpasswordChangeSql := fmt.Sprintf(\"set password for %s = password('%s')\", user, info.Password)\n\t\t\tif !strings.HasPrefix(info.Version, \"5.7\") && !strings.HasPrefix(info.Version, \"5.6\") {\n\t\t\t\tpasswordChangeSql = fmt.Sprintf(\"ALTER USER %s IDENTIFIED BY '%s';\", user, info.Password)\n\t\t\t}\n\t\t\tif err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\thosts, err := r.ExecSQLForRows(\"select host from mysql.user where user='root';\", info.Timeout)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, host := range hosts {\n\t\tif host == \"%\" || host == \"localhost\" {\n\t\t\tpasswordRootChangeCMD := fmt.Sprintf(\"set password for 'root'@'%s' = password('%s')\", host, info.Password)\n\t\t\tif !strings.HasPrefix(info.Version, \"5.7\") && !strings.HasPrefix(info.Version, \"5.6\") {\n\t\t\t\tpasswordRootChangeCMD = fmt.Sprintf(\"alter user 'root'@'%s' identified by '%s';\", host, info.Password)\n\t\t\t}\n\t\t\tif err := r.ExecSQL(passwordRootChangeCMD, info.Timeout); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (r *Local) ChangeAccess(info AccessChangeInfo) error {\n\tif info.Username == \"root\" {\n\t\tinfo.OldPermission = \"%\"\n\t\tinfo.Name = \"*\"\n\t\tinfo.Password = r.Password\n\t}\n\tif info.Permission != info.OldPermission {\n\t\tif err := r.Delete(DeleteInfo{\n\t\t\tVersion:     info.Version,\n\t\t\tUsername:    info.Username,\n\t\t\tPermission:  info.OldPermission,\n\t\t\tForceDelete: true,\n\t\t\tTimeout:     300}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif info.Username == \"root\" {\n\t\t\treturn nil\n\t\t}\n\t}\n\tif err := r.CreateUser(CreateInfo{\n\t\tName:       info.Name,\n\t\tVersion:    info.Version,\n\t\tUsername:   info.Username,\n\t\tPassword:   info.Password,\n\t\tPermission: info.Permission,\n\t\tTimeout:    info.Timeout,\n\t}, false); err != nil {\n\t\treturn err\n\t}\n\tif err := r.ExecSQL(\"flush privileges\", 300); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (r *Local) Backup(info BackupInfo) error {\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(info.TargetDir) {\n\t\tif err := os.MkdirAll(info.TargetDir, os.ModePerm); err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", info.TargetDir, err)\n\t\t}\n\t}\n\toutfile, err := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, constant.DirPerm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open file %s failed, err: %v\", path.Join(info.TargetDir, info.FileName), err)\n\t}\n\tdefer func() { _ = outfile.Close() }()\n\tdumpCmd := \"mysqldump\"\n\tif r.Type == constant.AppMariaDB {\n\t\tdumpCmd = \"mariadb-dump\"\n\t}\n\tglobal.LOG.Infof(\"start to %s | gzip > %s.gzip, args: %v\", dumpCmd, info.TargetDir+\"/\"+info.FileName, info.Args)\n\n\tinfo.Args = append(info.Args, \"--routines\")\n\titemArgs := common.RemoveRepeatStr(info.Args)\n\targs := []string{\"exec\", r.ContainerName, dumpCmd, \"-uroot\", \"-p\" + r.Password, \"--default-character-set=\" + info.Format}\n\tfor _, arg := range itemArgs {\n\t\tif len(arg) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\targs = append(args, arg)\n\t}\n\targs = append(args, info.Name)\n\tcmd := exec.Command(\"docker\", args...)\n\tvar stderr bytes.Buffer\n\tcmd.Stderr = &stderr\n\n\tgzipCmd := exec.Command(\"gzip\", \"-cf\")\n\tgzipCmd.Stdin, _ = cmd.StdoutPipe()\n\tgzipCmd.Stdout = outfile\n\t_ = gzipCmd.Start()\n\n\tif err := cmd.Run(); err != nil {\n\t\treturn fmt.Errorf(\"handle backup database failed, err: %v\", stderr.String())\n\t}\n\t_ = gzipCmd.Wait()\n\treturn nil\n}\n\nfunc (r *Local) Recover(info RecoverInfo) error {\n\tfi, _ := os.Open(info.SourceFile)\n\tdefer func() { _ = fi.Close() }()\n\tmysqlCli := r.Type\n\tif mysqlCli == \"mysql-cluster\" {\n\t\tmysqlCli = \"mysql\"\n\t}\n\n\tcmd := exec.Command(\"docker\", \"exec\", \"-i\", r.ContainerName, mysqlCli, \"-uroot\", \"-p\"+r.Password, \"--default-character-set=\"+info.Format, info.Name)\n\tif strings.HasSuffix(info.SourceFile, \".gz\") {\n\t\tgzipFile, err := os.Open(info.SourceFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() { _ = gzipFile.Close() }()\n\t\tgzipReader, err := gzip.NewReader(gzipFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() { _ = gzipReader.Close() }()\n\t\tcmd.Stdin = gzipReader\n\t} else {\n\t\tcmd.Stdin = fi\n\t}\n\tstdout, err := cmd.CombinedOutput()\n\tstdStr := strings.ReplaceAll(string(stdout), \"mysql: [Warning] Using a password on the command line interface can be insecure.\\n\", \"\")\n\tif err != nil || strings.HasPrefix(stdStr, \"ERROR \") {\n\t\treturn errors.New(stdStr)\n\t}\n\n\treturn nil\n}\n\nfunc (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {\n\tvar datas []SyncDBInfo\n\tlines, err := r.ExecSQLForRows(\"SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA\", 300)\n\tif err != nil {\n\t\treturn datas, err\n\t}\n\tfor _, line := range lines {\n\t\tparts := strings.Fields(line)\n\t\tif len(parts) != 3 {\n\t\t\tcontinue\n\t\t}\n\t\tif parts[0] == \"SCHEMA_NAME\" || parts[0] == \"information_schema\" || parts[0] == \"mysql\" || parts[0] == \"performance_schema\" || parts[0] == \"sys\" || parts[0] == \"__recycle_bin__\" || parts[0] == \"recycle_bin\" {\n\t\t\tcontinue\n\t\t}\n\t\tdataItem := SyncDBInfo{\n\t\t\tName:      parts[0],\n\t\t\tFrom:      \"local\",\n\t\t\tMysqlName: r.Database,\n\t\t\tFormat:    parts[1],\n\t\t\tCollation: parts[2],\n\t\t}\n\t\tuserLines, err := r.ExecSQLForRows(fmt.Sprintf(\"select user,host from mysql.db where db = '%s'\", parts[0]), 300)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Debugf(\"sync user of db %s failed, err: %v\", parts[0], err)\n\t\t\tdataItem.Permission = \"%\"\n\t\t\tdatas = append(datas, dataItem)\n\t\t\tcontinue\n\t\t}\n\n\t\tvar permissionItem []string\n\t\tisLocal := true\n\t\ti := 0\n\t\tfor _, userline := range userLines {\n\t\t\tuserparts := strings.Fields(userline)\n\t\t\tif len(userparts) != 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif userparts[0] == \"root\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif i == 0 {\n\t\t\t\tdataItem.Username = userparts[0]\n\t\t\t}\n\t\t\tdataItem.Username = userparts[0]\n\t\t\tif dataItem.Username == userparts[0] && userparts[1] == \"%\" {\n\t\t\t\tisLocal = false\n\t\t\t\tdataItem.Permission = \"%\"\n\t\t\t} else if dataItem.Username == userparts[0] && userparts[1] != \"localhost\" {\n\t\t\t\tisLocal = false\n\t\t\t\tpermissionItem = append(permissionItem, userparts[1])\n\t\t\t}\n\t\t}\n\t\tif len(dataItem.Username) == 0 {\n\t\t\tdataItem.Permission = \"%\"\n\t\t} else {\n\t\t\tif isLocal {\n\t\t\t\tdataItem.Permission = \"localhost\"\n\t\t\t}\n\t\t\tif len(dataItem.Permission) == 0 {\n\t\t\t\tdataItem.Permission = strings.Join(permissionItem, \",\")\n\t\t\t}\n\t\t}\n\t\tdatas = append(datas, dataItem)\n\t}\n\treturn datas, nil\n}\n\nfunc (r *Local) Close() {}\n\nfunc (r *Local) ExecSQL(command string, timeout uint) error {\n\titemCommand := r.PrefixCommand[:]\n\titemCommand = append(itemCommand, command)\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\tcmd := exec.CommandContext(ctx, \"docker\", itemCommand...)\n\tstdout, err := cmd.CombinedOutput()\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn buserr.New(\"ErrExecTimeOut\")\n\t}\n\tstdStr := strings.ReplaceAll(string(stdout), \"mysql: [Warning] Using a password on the command line interface can be insecure.\\n\", \"\")\n\tif err != nil || strings.HasPrefix(string(stdStr), \"ERROR \") {\n\t\treturn errors.New(stdStr)\n\t}\n\treturn nil\n}\n\nfunc (r *Local) ExecSQLForRows(command string, timeout uint) ([]string, error) {\n\titemCommand := r.PrefixCommand[:]\n\titemCommand = append(itemCommand, command)\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\tcmd := exec.CommandContext(ctx, \"docker\", itemCommand...)\n\tstdout, err := cmd.CombinedOutput()\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn nil, buserr.New(\"ErrExecTimeOut\")\n\t}\n\tstdStr := strings.ReplaceAll(string(stdout), \"mysql: [Warning] Using a password on the command line interface can be insecure.\\n\", \"\")\n\tif err != nil || strings.HasPrefix(string(stdStr), \"ERROR \") {\n\t\treturn nil, errors.New(stdStr)\n\t}\n\treturn strings.Split(stdStr, \"\\n\"), nil\n}\n\nfunc (r *Local) LoadFormatCollation(timeout uint) ([]dto.MysqlFormatCollationOption, error) {\n\tstd, err := r.ExecSQLForRows(\"SELECT CHARACTER_SET_NAME, COLLATION_NAME FROM INFORMATION_SCHEMA.COLLATIONS ORDER BY CHARACTER_SET_NAME, COLLATION_NAME;\", timeout)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tformatMap := make(map[string][]string)\n\tfor _, item := range std {\n\t\tif strings.ToLower(item) == \"character_set_name\\tcollation_name\" {\n\t\t\tcontinue\n\t\t}\n\t\tparts := strings.Split(item, \"\\t\")\n\t\tif len(parts) != 2 {\n\t\t\tcontinue\n\t\t}\n\t\tif parts[0] == \"NULL\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := formatMap[parts[0]]; !ok {\n\t\t\tformatMap[parts[0]] = []string{parts[1]}\n\t\t} else {\n\t\t\tformatMap[parts[0]] = append(formatMap[parts[0]], parts[1])\n\t\t}\n\t}\n\toptions := []dto.MysqlFormatCollationOption{}\n\tfor key, val := range formatMap {\n\t\toptions = append(options, dto.MysqlFormatCollationOption{\n\t\t\tFormat:     key,\n\t\t\tCollations: val,\n\t\t})\n\t}\n\treturn options, nil\n}\n"
  },
  {
    "path": "agent/utils/mysql/client/remote.go",
    "content": "package client\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/docker/docker/api/types/image\"\n\t\"github.com/docker/docker/client\"\n)\n\ntype Remote struct {\n\tType     string\n\tClient   *sql.DB\n\tDatabase string\n\tUser     string\n\tPassword string\n\tAddress  string\n\tPort     uint\n\n\tSSL        bool\n\tRootCert   string\n\tClientKey  string\n\tClientCert string\n\tSkipVerify bool\n}\n\nfunc NewRemote(db Remote) *Remote {\n\treturn &db\n}\n\nfunc (r *Remote) Create(info CreateInfo) error {\n\tcreateSql := fmt.Sprintf(\"create database `%s` default character set %s collate %s\", info.Name, info.Format, info.Collation)\n\tif len(info.Collation) == 0 {\n\t\tcreateSql = fmt.Sprintf(\"create database `%s` default character set %s\", info.Name, info.Format)\n\t}\n\tif err := r.ExecSQL(createSql, info.Timeout); err != nil {\n\t\tif strings.Contains(strings.ToLower(err.Error()), \"error 1007\") {\n\t\t\treturn buserr.New(\"ErrDatabaseIsExist\")\n\t\t}\n\t\treturn err\n\t}\n\n\tif err := r.CreateUser(info, true); err != nil {\n\t\t_ = r.ExecSQL(fmt.Sprintf(\"drop database if exists `%s`\", info.Name), info.Timeout)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (r *Remote) CreateUser(info CreateInfo, withDeleteDB bool) error {\n\tvar userlist []string\n\tif strings.Contains(info.Permission, \",\") {\n\t\tips := strings.Split(info.Permission, \",\")\n\t\tfor _, ip := range ips {\n\t\t\tif len(ip) != 0 {\n\t\t\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, ip))\n\t\t\t}\n\t\t}\n\t} else {\n\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, info.Permission))\n\t}\n\n\tfor _, user := range userlist {\n\t\tif err := r.ExecSQL(fmt.Sprintf(\"create user %s identified by '%s';\", user, info.Password), info.Timeout); err != nil {\n\t\t\tif strings.Contains(strings.ToLower(err.Error()), \"error 1396\") {\n\t\t\t\treturn buserr.New(\"ErrUserIsExist\")\n\t\t\t}\n\t\t\tif withDeleteDB {\n\t\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\t\tName:        info.Name,\n\t\t\t\t\tVersion:     info.Version,\n\t\t\t\t\tUsername:    info.Username,\n\t\t\t\t\tPermission:  info.Permission,\n\t\t\t\t\tForceDelete: true,\n\t\t\t\t\tTimeout:     300})\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tgrantStr := fmt.Sprintf(\"grant all privileges on `%s`.* to %s\", info.Name, user)\n\t\tif info.Name == \"*\" {\n\t\t\tgrantStr = fmt.Sprintf(\"grant all privileges on *.* to %s\", user)\n\t\t}\n\t\tif strings.HasPrefix(info.Version, \"5.7\") || strings.HasPrefix(info.Version, \"5.6\") {\n\t\t\tgrantStr = fmt.Sprintf(\"%s identified by '%s' with grant option;\", grantStr, info.Password)\n\t\t} else {\n\t\t\tgrantStr = grantStr + \" with grant option;\"\n\t\t}\n\t\tif err := r.ExecSQL(grantStr, info.Timeout); err != nil {\n\t\t\tif withDeleteDB {\n\t\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\t\tName:        info.Name,\n\t\t\t\t\tVersion:     info.Version,\n\t\t\t\t\tUsername:    info.Username,\n\t\t\t\t\tPermission:  info.Permission,\n\t\t\t\t\tForceDelete: true,\n\t\t\t\t\tTimeout:     300})\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (r *Remote) Delete(info DeleteInfo) error {\n\tvar userlist []string\n\tif strings.Contains(info.Permission, \",\") {\n\t\tips := strings.Split(info.Permission, \",\")\n\t\tfor _, ip := range ips {\n\t\t\tif len(ip) != 0 {\n\t\t\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, ip))\n\t\t\t}\n\t\t}\n\t} else {\n\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, info.Permission))\n\t}\n\n\tfor _, user := range userlist {\n\t\tif strings.HasPrefix(info.Version, \"5.6\") {\n\t\t\tif err := r.ExecSQL(fmt.Sprintf(\"drop user %s\", user), info.Timeout); err != nil && !info.ForceDelete {\n\t\t\t\treturn fmt.Errorf(\"drop user failed, err: %v\", err)\n\t\t\t}\n\t\t} else {\n\t\t\tif err := r.ExecSQL(fmt.Sprintf(\"drop user if exists %s\", user), info.Timeout); err != nil && !info.ForceDelete {\n\t\t\t\treturn fmt.Errorf(\"drop user failed, err: %v\", err)\n\t\t\t}\n\t\t}\n\t}\n\tif len(info.Name) != 0 {\n\t\tif err := r.ExecSQL(fmt.Sprintf(\"drop database if exists `%s`\", info.Name), info.Timeout); err != nil && !info.ForceDelete {\n\t\t\treturn fmt.Errorf(\"drop database failed, err: %v\", err)\n\t\t}\n\t}\n\tif !info.ForceDelete {\n\t\tglobal.LOG.Info(\"execute delete database sql successful, now start to drop uploads and records\")\n\t}\n\n\treturn nil\n}\n\nfunc (r *Remote) ChangePassword(info PasswordChangeInfo) error {\n\tif info.Username != \"root\" {\n\t\tvar userlist []string\n\t\tif strings.Contains(info.Permission, \",\") {\n\t\t\tips := strings.Split(info.Permission, \",\")\n\t\t\tfor _, ip := range ips {\n\t\t\t\tif len(ip) != 0 {\n\t\t\t\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, ip))\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tuserlist = append(userlist, fmt.Sprintf(\"'%s'@'%s'\", info.Username, info.Permission))\n\t\t}\n\n\t\tfor _, user := range userlist {\n\t\t\tpasswordChangeSql := fmt.Sprintf(\"set password for %s = password('%s')\", user, info.Password)\n\t\t\tif !strings.HasPrefix(info.Version, \"5.7\") && !strings.HasPrefix(info.Version, \"5.6\") {\n\t\t\t\tpasswordChangeSql = fmt.Sprintf(\"ALTER USER %s IDENTIFIED BY '%s';\", user, info.Password)\n\t\t\t}\n\t\t\tif err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\thosts, err := r.ExecSQLForHosts(info.Timeout)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, host := range hosts {\n\t\tif host == \"%\" || host == \"localhost\" {\n\t\t\tpasswordRootChangeCMD := fmt.Sprintf(\"set password for 'root'@'%s' = password('%s')\", host, info.Password)\n\t\t\tif !strings.HasPrefix(info.Version, \"5.7\") && !strings.HasPrefix(info.Version, \"5.6\") {\n\t\t\t\tpasswordRootChangeCMD = fmt.Sprintf(\"alter user 'root'@'%s' identified by '%s';\", host, info.Password)\n\t\t\t}\n\t\t\tif err := r.ExecSQL(passwordRootChangeCMD, info.Timeout); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (r *Remote) ChangeAccess(info AccessChangeInfo) error {\n\tif info.Username == \"root\" {\n\t\tinfo.OldPermission = \"%\"\n\t\tinfo.Name = \"*\"\n\t\tinfo.Password = r.Password\n\t}\n\tif info.Permission != info.OldPermission {\n\t\tif err := r.Delete(DeleteInfo{\n\t\t\tVersion:     info.Version,\n\t\t\tUsername:    info.Username,\n\t\t\tPermission:  info.OldPermission,\n\t\t\tForceDelete: true,\n\t\t\tTimeout:     300}); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif info.Username == \"root\" {\n\t\t\treturn nil\n\t\t}\n\t}\n\tif err := r.CreateUser(CreateInfo{\n\t\tName:       info.Name,\n\t\tVersion:    info.Version,\n\t\tUsername:   info.Username,\n\t\tPassword:   info.Password,\n\t\tPermission: info.Permission,\n\t\tTimeout:    info.Timeout,\n\t}, false); err != nil {\n\t\treturn err\n\t}\n\tif err := r.ExecSQL(\"flush privileges\", 300); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (r *Remote) Backup(info BackupInfo) error {\n\tif cmd.CheckIllegal(r.Password, r.Address, r.User, info.Name, info.Format) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(info.TargetDir) {\n\t\tif err := os.MkdirAll(info.TargetDir, os.ModePerm); err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", info.TargetDir, err)\n\t\t}\n\t}\n\toutfile, err := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, constant.DirPerm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open file %s failed, err: %v\", path.Join(info.TargetDir, info.FileName), err)\n\t}\n\tdefer func() { _ = outfile.Close() }()\n\tdumpCmd := \"mysqldump\"\n\tif r.Type == constant.AppMariaDB {\n\t\tdumpCmd = \"mariadb-dump\"\n\t}\n\tglobal.LOG.Infof(\"start to %s | gzip > %s.gzip\", dumpCmd, info.TargetDir+\"/\"+info.FileName)\n\timage, err := loadImage(info.Type, info.Version)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo.Args = append(info.Args, \"--routines\")\n\titemArgs := common.RemoveRepeatStr(info.Args)\n\tvar args []string\n\tfor _, arg := range itemArgs {\n\t\tif len(arg) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\targs = append(args, arg)\n\t}\n\n\tbackupCmd := fmt.Sprintf(\"docker run --rm --net=host -i %s /bin/bash -c '%s %s -h %s -P %d -u%s -p%s %s --default-character-set=%s %s'\",\n\t\timage, dumpCmd, strings.Join(args, \" \"), r.Address, r.Port, r.User, r.Password, sslSkip(info.Version, r.Type), info.Format, info.Name)\n\n\tglobal.LOG.Debug(strings.ReplaceAll(backupCmd, r.Password, \"******\"))\n\tcmd := exec.Command(\"bash\", \"-c\", backupCmd)\n\tvar stderr bytes.Buffer\n\tcmd.Stderr = &stderr\n\n\tgzipCmd := exec.Command(\"gzip\", \"-cf\")\n\tgzipCmd.Stdin, _ = cmd.StdoutPipe()\n\tgzipCmd.Stdout = outfile\n\n\t_ = gzipCmd.Start()\n\tif err := cmd.Run(); err != nil {\n\t\treturn fmt.Errorf(\"handle backup database failed, err: %v\", stderr.String())\n\t}\n\t_ = gzipCmd.Wait()\n\treturn nil\n}\n\nfunc (r *Remote) Recover(info RecoverInfo) error {\n\tif cmd.CheckIllegal(r.Password, r.Address, r.User, info.Name, info.Format) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tfi, _ := os.Open(info.SourceFile)\n\tdefer func() { _ = fi.Close() }()\n\n\timage, err := loadImage(info.Type, info.Version)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trecoverCmd := fmt.Sprintf(\"docker run --rm --net=host -i %s /bin/bash -c '%s -h %s -P %d -u%s -p%s %s --default-character-set=%s %s'\",\n\t\timage, r.Type, r.Address, r.Port, r.User, r.Password, sslSkip(info.Version, r.Type), info.Format, info.Name)\n\n\tglobal.LOG.Debug(strings.ReplaceAll(recoverCmd, r.Password, \"******\"))\n\tcmd := exec.Command(\"bash\", \"-c\", recoverCmd)\n\n\tif strings.HasSuffix(info.SourceFile, \".gz\") {\n\t\tgzipFile, err := os.Open(info.SourceFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() { _ = gzipFile.Close() }()\n\t\tgzipReader, err := gzip.NewReader(gzipFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() { _ = gzipReader.Close() }()\n\t\tcmd.Stdin = gzipReader\n\t} else {\n\t\tcmd.Stdin = fi\n\t}\n\tstdout, err := cmd.CombinedOutput()\n\tstdStr := strings.ReplaceAll(string(stdout), \"mysql: [Warning] Using a password on the command line interface can be insecure.\\n\", \"\")\n\tif err != nil || strings.HasPrefix(string(stdStr), \"ERROR \") {\n\t\treturn errors.New(stdStr)\n\t}\n\n\treturn nil\n}\n\nfunc (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {\n\tvar datas []SyncDBInfo\n\trows, err := r.Client.Query(\"select schema_name, default_character_set_name, default_collation_name from information_schema.SCHEMATA\")\n\tif err != nil {\n\t\treturn datas, err\n\t}\n\tdefer func() { _ = rows.Close() }()\n\n\tfor rows.Next() {\n\t\tvar dbName, charsetName, collation string\n\t\tif err = rows.Scan(&dbName, &charsetName, &collation); err != nil {\n\t\t\treturn datas, err\n\t\t}\n\t\tif dbName == \"information_schema\" || dbName == \"mysql\" || dbName == \"performance_schema\" || dbName == \"sys\" || dbName == \"__recycle_bin__\" || dbName == \"recycle_bin\" {\n\t\t\tcontinue\n\t\t}\n\t\tdataItem := SyncDBInfo{\n\t\t\tName:      dbName,\n\t\t\tFrom:      \"remote\",\n\t\t\tMysqlName: r.Database,\n\t\t\tFormat:    charsetName,\n\t\t\tCollation: collation,\n\t\t}\n\t\tuserRows, err := r.Client.Query(\"select user,host from mysql.db where db = ?\", dbName)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Debugf(\"sync user of db %s failed, err: %v\", dbName, err)\n\t\t\tdataItem.Permission = \"%\"\n\t\t\tdatas = append(datas, dataItem)\n\t\t\tcontinue\n\t\t}\n\n\t\tvar permissionItem []string\n\t\tisLocal := true\n\t\ti := 0\n\t\tfor userRows.Next() {\n\t\t\tvar user, host string\n\t\t\tif err = userRows.Scan(&user, &host); err != nil {\n\t\t\t\treturn datas, err\n\t\t\t}\n\t\t\tif user == \"root\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif i == 0 {\n\t\t\t\tdataItem.Username = user\n\t\t\t}\n\t\t\tif dataItem.Username == user && host == \"%\" {\n\t\t\t\tisLocal = false\n\t\t\t\tdataItem.Permission = \"%\"\n\t\t\t} else if dataItem.Username == user && host != \"localhost\" {\n\t\t\t\tisLocal = false\n\t\t\t\tpermissionItem = append(permissionItem, host)\n\t\t\t}\n\t\t\ti++\n\t\t}\n\t\tif len(dataItem.Username) == 0 {\n\t\t\tdataItem.Permission = \"%\"\n\t\t} else {\n\t\t\tif isLocal {\n\t\t\t\tdataItem.Permission = \"localhost\"\n\t\t\t}\n\t\t\tif len(dataItem.Permission) == 0 {\n\t\t\t\tdataItem.Permission = strings.Join(permissionItem, \",\")\n\t\t\t}\n\t\t}\n\t\tdatas = append(datas, dataItem)\n\t}\n\tif err = rows.Err(); err != nil {\n\t\treturn datas, err\n\t}\n\treturn datas, nil\n}\n\nfunc (r *Remote) Close() {\n\t_ = r.Client.Close()\n}\n\nfunc (r *Remote) ExecSQL(command string, timeout uint) error {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\n\tif _, err := r.Client.ExecContext(ctx, command); err != nil {\n\t\treturn err\n\t}\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn buserr.New(\"ErrExecTimeOut\")\n\t}\n\n\treturn nil\n}\n\nfunc (r *Remote) LoadFormatCollation(timeout uint) ([]dto.MysqlFormatCollationOption, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\n\trows, err := r.Client.QueryContext(ctx, \"SELECT CHARACTER_SET_NAME, COLLATION_NAME FROM INFORMATION_SCHEMA.COLLATIONS ORDER BY CHARACTER_SET_NAME, COLLATION_NAME;\")\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn nil, buserr.New(\"ErrExecTimeOut\")\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() { _ = rows.Close() }()\n\n\tformatMap := make(map[string][]string)\n\tfor rows.Next() {\n\t\tvar item FormatCollation\n\t\tif err := rows.Scan(&item.Format, &item.Collation); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !item.Format.Valid {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := formatMap[item.Format.String]; !ok {\n\t\t\tformatMap[item.Format.String] = []string{item.Collation.String}\n\t\t} else {\n\t\t\tformatMap[item.Format.String] = append(formatMap[item.Format.String], item.Collation.String)\n\t\t}\n\t}\n\toptions := []dto.MysqlFormatCollationOption{}\n\tfor key, val := range formatMap {\n\t\toptions = append(options, dto.MysqlFormatCollationOption{\n\t\t\tFormat:     key,\n\t\t\tCollations: val,\n\t\t})\n\t}\n\n\treturn options, nil\n}\n\nfunc (r *Remote) ExecSQLForHosts(timeout uint) ([]string, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\n\tresults, err := r.Client.QueryContext(ctx, \"select host from mysql.user where user='root';\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn nil, buserr.New(\"ErrExecTimeOut\")\n\t}\n\tvar rows []string\n\tdefer func() { _ = results.Close() }()\n\tfor results.Next() {\n\t\tvar host string\n\t\tif err := results.Scan(&host); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\trows = append(rows, host)\n\t}\n\treturn rows, nil\n}\n\nfunc loadImage(dbType, version string) (string, error) {\n\tcli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\timages, err := cli.ImageList(context.Background(), image.ListOptions{})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfor _, image := range images {\n\t\tfor _, tag := range image.RepoTags {\n\t\t\tif !strings.HasPrefix(tag, dbType+\":\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif dbType == \"mariadb\" && strings.HasPrefix(tag, \"mariadb:\") {\n\t\t\t\treturn tag, nil\n\t\t\t}\n\t\t\tif strings.HasPrefix(version, \"5.6\") && strings.HasPrefix(tag, \"mysql:5.6\") {\n\t\t\t\treturn tag, nil\n\t\t\t}\n\t\t\tif strings.HasPrefix(version, \"5.7\") && strings.HasPrefix(tag, \"mysql:5.7\") {\n\t\t\t\treturn tag, nil\n\t\t\t}\n\t\t\tif strings.HasPrefix(version, \"8.\") && strings.HasPrefix(tag, \"mysql:8.\") {\n\t\t\t\treturn tag, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn loadVersion(dbType, version), nil\n}\n\nfunc loadVersion(dbType string, version string) string {\n\tif dbType == \"mariadb\" {\n\t\treturn \"mariadb:11.3.2 \"\n\t}\n\tif strings.HasPrefix(version, \"5.6\") {\n\t\treturn \"mysql:5.6.51\"\n\t}\n\tif strings.HasPrefix(version, \"5.7\") {\n\t\treturn \"mysql:5.7.44\"\n\t}\n\treturn \"mysql:8.2.0\"\n}\n\nfunc sslSkip(version, dbType string) string {\n\tif dbType == constant.AppMariaDB || strings.HasPrefix(version, \"5.6\") || strings.HasPrefix(version, \"5.7\") {\n\t\treturn \"--skip-ssl\"\n\t}\n\treturn \"--ssl-mode=DISABLED\"\n}\n"
  },
  {
    "path": "agent/utils/mysql/client.go",
    "content": "package mysql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/mysql/client\"\n)\n\ntype MysqlClient interface {\n\tCreate(info client.CreateInfo) error\n\tCreateUser(info client.CreateInfo, withDeleteDB bool) error\n\tDelete(info client.DeleteInfo) error\n\n\tChangePassword(info client.PasswordChangeInfo) error\n\tChangeAccess(info client.AccessChangeInfo) error\n\n\tBackup(info client.BackupInfo) error\n\tRecover(info client.RecoverInfo) error\n\n\tLoadFormatCollation(timeout uint) ([]dto.MysqlFormatCollationOption, error)\n\tSyncDB(version string) ([]client.SyncDBInfo, error)\n\tClose()\n}\n\nfunc NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {\n\tif conn.From == \"local\" {\n\t\tmysqlCli := conn.Type\n\t\tif mysqlCli == \"mysql-cluster\" {\n\t\t\tmysqlCli = \"mysql\"\n\t\t}\n\t\tconnArgs := []string{\"exec\", conn.Address, mysqlCli, \"-u\" + conn.Username, \"-p\" + conn.Password, \"-e\"}\n\t\treturn client.NewLocal(connArgs, conn.Type, conn.Address, conn.Password, conn.Database), nil\n\t}\n\n\tif strings.Contains(conn.Address, \":\") {\n\t\tconn.Address = fmt.Sprintf(\"[%s]\", conn.Address)\n\t}\n\n\ttlsItem, err := client.ConnWithSSL(conn.SSL, conn.SkipVerify, conn.ClientKey, conn.ClientCert, conn.RootCert)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconnArgs := fmt.Sprintf(\"%s:%s@tcp(%s:%d)/?charset=utf8%s\", conn.Username, conn.Password, conn.Address, conn.Port, tlsItem)\n\tdb, err := sql.Open(\"mysql\", connArgs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(conn.Timeout)*time.Second)\n\tdefer cancel()\n\tif err := db.PingContext(ctx); err != nil {\n\t\tglobal.LOG.Errorf(\"test mysql conn failed, err: %v\", err)\n\t\treturn nil, err\n\t}\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn nil, buserr.New(\"ErrExecTimeOut\")\n\t}\n\n\treturn client.NewRemote(client.Remote{\n\t\tType:     conn.Type,\n\t\tClient:   db,\n\t\tDatabase: conn.Database,\n\t\tUser:     conn.Username,\n\t\tPassword: conn.Password,\n\t\tAddress:  conn.Address,\n\t\tPort:     conn.Port,\n\n\t\tSSL:        conn.SSL,\n\t\tRootCert:   conn.RootCert,\n\t\tClientKey:  conn.ClientKey,\n\t\tClientCert: conn.ClientCert,\n\t\tSkipVerify: conn.SkipVerify,\n\t}), nil\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/block.go",
    "content": "package components\n\ntype Block struct {\n\tLine        int\n\tComment     string\n\tDirectives  []IDirective\n\tIsLuaBlock  bool\n\tLiteralCode string\n}\n\nfunc (b *Block) GetDirectives() []IDirective {\n\treturn b.Directives\n}\n\nfunc (b *Block) GetComment() string {\n\treturn b.Comment\n}\n\nfunc (b *Block) GetLine() int {\n\treturn b.Line\n}\n\nfunc (b *Block) GetCodeBlock() string {\n\treturn b.LiteralCode\n}\n\nfunc (b *Block) FindDirectives(directiveName string) []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tfor _, directive := range b.GetDirectives() {\n\t\tif directive.GetName() == directiveName {\n\t\t\tdirectives = append(directives, directive)\n\t\t}\n\t\tif directive.GetBlock() != nil {\n\t\t\tdirectives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)\n\t\t}\n\t}\n\n\treturn directives\n}\n\nfunc (b *Block) AppendDirectives(directives ...IDirective) {\n\tb.Directives = append(b.Directives, directives...)\n}\n\nfunc (b *Block) UpdateDirective(key string, params []string) {\n\tif key == \"\" || len(params) == 0 {\n\t\treturn\n\t}\n\tdirectives := b.GetDirectives()\n\tindex := -1\n\tfor i, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif !(len(oldParams) > 0 && oldParams[0] == params[0]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tnewDirective := &Directive{\n\t\tName:       key,\n\t\tParameters: params,\n\t}\n\tif index > -1 {\n\t\tdirectives[index] = newDirective\n\t} else {\n\t\tdirectives = append(directives, newDirective)\n\t}\n\tb.Directives = directives\n}\n\nfunc (b *Block) RemoveDirective(key string, params []string) {\n\tdirectives := b.GetDirectives()\n\tvar newDirectives []IDirective\n\tfor _, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) && len(params) > 0 {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif oldParams[0] == params[0] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tnewDirectives = append(newDirectives, dir)\n\t}\n\tb.Directives = newDirectives\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/comment.go",
    "content": "package components\n\ntype Comment struct {\n\tDetail string\n\tLine   int\n}\n\nfunc (c *Comment) GetName() string {\n\treturn \"\"\n}\n\nfunc (c *Comment) GetParameters() []string {\n\treturn []string{}\n}\n\nfunc (c *Comment) GetBlock() IBlock {\n\treturn nil\n}\n\nfunc (c *Comment) GetComment() string {\n\treturn c.Detail\n}\n\nfunc (c *Comment) GetLine() int {\n\treturn c.Line\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/config.go",
    "content": "package components\n\ntype Config struct {\n\t*Block\n\tFilePath string\n}\n\nfunc (c *Config) FindServers() []*Server {\n\tvar servers []*Server\n\tdirectives := c.Block.FindDirectives(\"server\")\n\tfor _, directive := range directives {\n\t\tservers = append(servers, directive.(*Server))\n\t}\n\treturn servers\n}\n\nfunc (c *Config) FindHttp() *Http {\n\tvar http *Http\n\tdirectives := c.Block.FindDirectives(\"http\")\n\tif len(directives) > 0 {\n\t\thttp = directives[0].(*Http)\n\t}\n\n\treturn http\n}\n\nfunc (c *Config) FindUpstreams() []*Upstream {\n\tvar upstreams []*Upstream\n\tdirectives := c.Block.FindDirectives(\"upstream\")\n\tfor _, directive := range directives {\n\t\tupstreams = append(upstreams, directive.(*Upstream))\n\t}\n\treturn upstreams\n}\n\nvar repeatKeys = map[string]struct {\n}{\n\t\"limit_conn\":       {},\n\t\"limit_conn_zone\":  {},\n\t\"set\":              {},\n\t\"if\":               {},\n\t\"proxy_set_header\": {},\n\t\"location\":         {},\n\t\"include\":          {},\n\t\"sub_filter\":       {},\n\t\"add_header\":       {},\n\t\"set_real_ip_from\": {},\n\t\"error_page\":       {},\n}\n\nfunc IsRepeatKey(key string) bool {\n\tif _, ok := repeatKeys[key]; ok {\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/directive.go",
    "content": "package components\n\ntype Directive struct {\n\tLine       int\n\tBlock      IBlock\n\tName       string\n\tComment    string\n\tParameters []string\n}\n\nfunc (d *Directive) GetComment() string {\n\treturn d.Comment\n}\n\nfunc (d *Directive) GetName() string {\n\treturn d.Name\n}\n\nfunc (d *Directive) GetParameters() []string {\n\treturn d.Parameters\n}\n\nfunc (d *Directive) GetBlock() IBlock {\n\treturn d.Block\n}\n\nfunc (d *Directive) GetLine() int {\n\treturn d.Line\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/http.go",
    "content": "package components\n\nimport (\n\t\"errors\"\n)\n\ntype Http struct {\n\tComment    string\n\tServers    []*Server\n\tDirectives []IDirective\n\tLine       int\n}\n\nfunc (h *Http) GetCodeBlock() string {\n\treturn \"\"\n}\n\nfunc (h *Http) GetComment() string {\n\treturn h.Comment\n}\n\nfunc NewHttp(directive IDirective) (*Http, error) {\n\tif block := directive.GetBlock(); block != nil {\n\t\thttp := &Http{\n\t\t\tLine:       directive.GetBlock().GetLine(),\n\t\t\tServers:    []*Server{},\n\t\t\tDirectives: []IDirective{},\n\t\t\tComment:    block.GetComment(),\n\t\t}\n\t\tfor _, directive := range block.GetDirectives() {\n\t\t\tif server, ok := directive.(*Server); ok {\n\t\t\t\thttp.Servers = append(http.Servers, server)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thttp.Directives = append(http.Directives, directive)\n\t\t}\n\n\t\treturn http, nil\n\t}\n\treturn nil, errors.New(\"http directive must have a block\")\n}\n\nfunc (h *Http) GetName() string {\n\treturn \"http\"\n}\n\nfunc (h *Http) GetParameters() []string {\n\treturn []string{}\n}\n\nfunc (h *Http) GetDirectives() []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tdirectives = append(directives, h.Directives...)\n\tfor _, directive := range h.Servers {\n\t\tdirectives = append(directives, directive)\n\t}\n\treturn directives\n}\n\nfunc (h *Http) FindDirectives(directiveName string) []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tfor _, directive := range h.GetDirectives() {\n\t\tif directive.GetName() == directiveName {\n\t\t\tdirectives = append(directives, directive)\n\t\t}\n\t\tif directive.GetBlock() != nil {\n\t\t\tdirectives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)\n\t\t}\n\t}\n\n\treturn directives\n}\n\nfunc (h *Http) UpdateDirective(key string, params []string) {\n\tif key == \"\" || len(params) == 0 {\n\t\treturn\n\t}\n\tdirectives := h.GetDirectives()\n\tindex := -1\n\tfor i, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif !(len(oldParams) > 0 && oldParams[0] == params[0]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tnewDirective := &Directive{\n\t\tName:       key,\n\t\tParameters: params,\n\t}\n\tif index > -1 {\n\t\tdirectives[index] = newDirective\n\t} else {\n\t\tdirectives = append(directives, newDirective)\n\t}\n\th.Directives = directives\n}\n\nfunc (h *Http) RemoveDirective(key string, params []string) {\n\tdirectives := h.GetDirectives()\n\tvar newDirectives []IDirective\n\tfor _, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) && len(params) > 0 {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif oldParams[0] == params[0] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tnewDirectives = append(newDirectives, dir)\n\t}\n\th.Directives = newDirectives\n}\n\nfunc (h *Http) GetBlock() IBlock {\n\treturn h\n}\n\nfunc (h *Http) GetLine() int {\n\treturn h.Line\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/location.go",
    "content": "package components\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/re\"\n)\n\ntype Location struct {\n\tModifier         string\n\tMatch            string\n\tCache            bool\n\tProxyPass        string\n\tHost             string\n\tCacheTime        int\n\tCacheUint        string\n\tComment          string\n\tDirectives       []IDirective\n\tLine             int\n\tParameters       []string\n\tReplaces         map[string]string\n\tServerCacheTime  int\n\tServerCacheUint  string\n\tCors             bool\n\tAllowMethods     string\n\tAllowHeaders     string\n\tAllowOrigins     string\n\tAllowCredentials bool\n\tPreflight        bool\n}\n\nfunc (l *Location) GetCodeBlock() string {\n\treturn \"\"\n}\n\nfunc NewLocation(directive IDirective) *Location {\n\tlocation := &Location{\n\t\tModifier: \"\",\n\t\tMatch:    \"\",\n\t}\n\tdirectives := make([]IDirective, 0)\n\tif len(directive.GetParameters()) == 0 {\n\t\tpanic(\"no enough parameter for location\")\n\t}\n\tfor _, dir := range directive.GetBlock().GetDirectives() {\n\t\tdirectives = append(directives, dir)\n\t\tparams := dir.GetParameters()\n\t\tswitch dir.GetName() {\n\t\tcase \"proxy_pass\":\n\t\t\tlocation.ProxyPass = params[0]\n\t\tcase \"proxy_set_header\":\n\t\t\tif params[0] == \"Host\" {\n\t\t\t\tlocation.Host = params[1]\n\t\t\t}\n\t\tcase \"proxy_cache\":\n\t\t\tlocation.Cache = true\n\t\tcase \"if\":\n\t\t\tif params[0] == \"(\" && params[1] == \"$uri\" && params[2] == \"~*\" {\n\t\t\t\tdirs := dir.GetBlock().GetDirectives()\n\t\t\t\tfor _, di := range dirs {\n\t\t\t\t\tif di.GetName() == \"expires\" {\n\t\t\t\t\t\tmatches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(di.GetParameters()[0])\n\t\t\t\t\t\tif matches == nil {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcacheTime, err := strconv.Atoi(matches[1])\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tunit := matches[2]\n\t\t\t\t\t\tlocation.CacheUint = unit\n\t\t\t\t\t\tlocation.CacheTime = cacheTime\n\t\t\t\t\t}\n\t\t\t\t\tif di.GetName() == \"add_header\" && len(di.GetParameters()) >= 2 {\n\t\t\t\t\t\tif di.GetParameters()[0] == \"Cache-Control\" && di.GetParameters()[1] == \"no-cache\" {\n\t\t\t\t\t\t\tlocation.CacheTime = -1\n\t\t\t\t\t\t\tlocation.CacheUint = \"\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif params[0] == \"(\" && params[1] == \"$request_method\" && params[2] == `=` && params[3] == `'OPTIONS'` && params[4] == \")\" {\n\t\t\t\tlocation.Preflight = true\n\t\t\t}\n\t\tcase \"proxy_cache_valid\":\n\t\t\ttimeParam := params[len(params)-1]\n\t\t\tmatches := re.GetRegex(re.NumberWordPattern).FindStringSubmatch(timeParam)\n\t\t\tif matches == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tcacheTime, err := strconv.Atoi(matches[1])\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tunit := matches[2]\n\n\t\t\tlocation.ServerCacheTime = cacheTime\n\t\t\tlocation.ServerCacheUint = unit\n\t\tcase \"sub_filter\":\n\t\t\tif location.Replaces == nil {\n\t\t\t\tlocation.Replaces = make(map[string]string, 0)\n\t\t\t}\n\t\t\tlocation.Replaces[strings.Trim(params[0], \"\\\"\")] = strings.Trim(params[1], \"\\\"\")\n\t\tcase \"add_header\":\n\t\t\tif params[0] == \"Access-Control-Allow-Origin\" {\n\t\t\t\tlocation.Cors = true\n\t\t\t\tlocation.AllowOrigins = params[1]\n\t\t\t}\n\t\t\tif params[0] == \"Access-Control-Allow-Methods\" {\n\t\t\t\tlocation.AllowMethods = params[1]\n\t\t\t}\n\t\t\tif params[0] == \"Access-Control-Allow-Headers\" {\n\t\t\t\tlocation.AllowHeaders = params[1]\n\t\t\t}\n\t\t\tif params[0] == \"Access-Control-Allow-Credentials\" && params[1] == \"true\" {\n\t\t\t\tlocation.AllowCredentials = true\n\t\t\t}\n\t\t}\n\t}\n\n\tparams := directive.GetParameters()\n\tif len(params) == 1 {\n\t\tlocation.Match = params[0]\n\t} else if len(params) == 2 {\n\t\tlocation.Match = params[1]\n\t\tlocation.Modifier = params[0]\n\t}\n\tlocation.Parameters = directive.GetParameters()\n\tlocation.Line = directive.GetLine()\n\tlocation.Comment = directive.GetComment()\n\tlocation.Directives = directives\n\treturn location\n}\n\nfunc (l *Location) GetName() string {\n\treturn \"location\"\n}\n\nfunc (l *Location) GetParameters() []string {\n\treturn l.Parameters\n}\n\nfunc (l *Location) GetBlock() IBlock {\n\treturn l\n}\n\nfunc (l *Location) GetComment() string {\n\treturn l.Comment\n}\n\nfunc (l *Location) GetLine() int {\n\treturn l.Line\n}\n\nfunc (l *Location) GetDirectives() []IDirective {\n\treturn l.Directives\n}\n\nfunc (l *Location) FindDirectives(directiveName string) []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tfor _, directive := range l.Directives {\n\t\tif directive.GetName() == directiveName {\n\t\t\tdirectives = append(directives, directive)\n\t\t}\n\t\tif directive.GetBlock() != nil {\n\t\t\tdirectives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)\n\t\t}\n\t}\n\treturn directives\n}\n\nfunc (l *Location) UpdateDirective(key string, params []string) {\n\tif key == \"\" || len(params) == 0 {\n\t\treturn\n\t}\n\tdirectives := l.Directives\n\tindex := -1\n\tfor i, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif !(len(oldParams) > 0 && oldParams[0] == params[0]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tnewDirective := &Directive{\n\t\tName:       key,\n\t\tParameters: params,\n\t}\n\tif index > -1 {\n\t\tdirectives[index] = newDirective\n\t} else {\n\t\tdirectives = append(directives, newDirective)\n\t}\n\tl.Directives = directives\n}\n\n// RemoveDirective removes a directive by its name and optional FIRST parameter match\nfunc (l *Location) RemoveDirective(key string, params []string) {\n\tdirectives := l.Directives\n\tvar newDirectives []IDirective\n\tfor _, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif len(params) > 0 {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif oldParams[0] == params[0] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tnewDirectives = append(newDirectives, dir)\n\t}\n\tl.Directives = newDirectives\n}\n\n// RemoveDirectiveByFullParams removes a directive by its name and full parameter match\nfunc (l *Location) RemoveDirectiveByFullParams(key string, params []string) {\n\tdirectives := l.Directives\n\tvar newDirectives []IDirective\n\tfor _, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\toldParams := dir.GetParameters()\n\t\t\tif len(oldParams) == len(params) {\n\t\t\t\tallMatch := true\n\t\t\t\tfor i, param := range params {\n\t\t\t\t\tif oldParams[i] != param {\n\t\t\t\t\t\tallMatch = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif allMatch {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tnewDirectives = append(newDirectives, dir)\n\t}\n\tl.Directives = newDirectives\n}\n\nfunc (l *Location) ChangePath(Modifier string, Match string) {\n\tif Match != \"\" && Modifier != \"\" {\n\t\tl.Parameters = []string{Modifier, Match}\n\t}\n\tif Match != \"\" && Modifier == \"\" {\n\t\tl.Parameters = []string{Match}\n\t}\n\tl.Modifier = Modifier\n\tl.Match = Match\n}\n\nfunc (l *Location) AddBrowserCache(cacheTime int, cacheUint string) {\n\tl.RemoveDirective(\"add_header\", []string{\"Cache-Control\", \"no-cache\"})\n\tl.RemoveDirectiveByFullParams(\"if\", []string{\"(\", \"$uri\", \"~*\", `\"\\.(gif|png|jpg|css|js|woff|woff2)$\"`, \")\"})\n\tl.RemoveDirectiveByFullParams(\"if\", []string{\"(\", \"$uri\", \"~*\", `\"\\.(gif|png|jpg|css|js|woff|woff2|jpeg|svg|webp|avif)$\"`, \")\"})\n\tdirectives := l.GetDirectives()\n\tnewDir := &Directive{\n\t\tName:       \"if\",\n\t\tParameters: []string{\"(\", \"$uri\", \"~*\", `\"\\.(gif|png|jpg|css|js|woff|woff2|jpeg|svg|webp|avif)$\"`, \")\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\tblock.Directives = append(block.Directives, &Directive{\n\t\tName:       \"expires\",\n\t\tParameters: []string{strconv.Itoa(cacheTime) + cacheUint},\n\t})\n\tnewDir.Block = block\n\tdirectives = append(directives, newDir)\n\tl.Directives = directives\n\tl.CacheTime = cacheTime\n\tl.CacheUint = cacheUint\n}\n\nfunc (l *Location) AddBroswerNoCache() {\n\tl.RemoveDirective(\"add_header\", []string{\"Cache-Control\", \"no-cache\"})\n\tl.RemoveDirectiveByFullParams(\"if\", []string{\"(\", \"$uri\", \"~*\", `\"\\.(gif|png|jpg|css|js|woff|woff2)$\"`, \")\"})\n\tl.RemoveDirectiveByFullParams(\"if\", []string{\"(\", \"$uri\", \"~*\", `\"\\.(gif|png|jpg|css|js|woff|woff2|jpeg|svg|webp|avif)$\"`, \")\"})\n\tdirectives := l.GetDirectives()\n\tnewDir := &Directive{\n\t\tName:       \"if\",\n\t\tParameters: []string{\"(\", \"$uri\", \"~*\", `\"\\.(gif|png|jpg|css|js|woff|woff2|jpeg|svg|webp|avif)$\"`, \")\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\tblock.Directives = append(block.Directives, &Directive{\n\t\tName:       \"add_header\",\n\t\tParameters: []string{\"Cache-Control\", \"no-cache\"},\n\t})\n\tnewDir.Block = block\n\tdirectives = append(directives, newDir)\n\tl.Directives = directives\n\tl.CacheTime = -1\n\tl.CacheUint = \"s\"\n}\n\nfunc (l *Location) AddServerCache(cacheKey string, serverCacheTime int, serverCacheUint string) {\n\tl.UpdateDirective(\"proxy_ignore_headers\", []string{\"Set-Cookie\", \"Cache-Control\", \"expires\"})\n\tl.UpdateDirective(\"proxy_cache\", []string{cacheKey})\n\tl.UpdateDirective(\"proxy_cache_key\", []string{\"$host$uri$is_args$args\"})\n\tl.UpdateDirective(\"proxy_cache_valid\", []string{\"200\", \"304\", \"301\", \"302\", strconv.Itoa(serverCacheTime) + serverCacheUint})\n\tl.Cache = true\n\tl.ServerCacheTime = serverCacheTime\n\tl.ServerCacheUint = serverCacheUint\n}\n\nfunc (l *Location) RemoveBrowserCache() {\n\tl.RemoveDirectiveByFullParams(\"if\", []string{\"(\", \"$uri\", \"~*\", `\"\\.(gif|png|jpg|css|js|woff|woff2)$\"`, \")\"})\n\tl.RemoveDirectiveByFullParams(\"if\", []string{\"(\", \"$uri\", \"~*\", `\"\\.(gif|png|jpg|css|js|woff|woff2|jpeg|svg|webp|avif)$\"`, \")\"})\n\tl.RemoveDirective(\"add_header\", []string{\"Cache-Control\", \"no-cache\"})\n\tl.CacheTime = 0\n\tl.CacheUint = \"\"\n}\n\nfunc (l *Location) RemoveServerCache(cacheKey string) {\n\tl.RemoveDirective(\"proxy_ignore_headers\", []string{\"Set-Cookie\", \"Cache-Control\", \"expires\"})\n\tl.RemoveDirective(\"proxy_cache\", []string{cacheKey})\n\tl.RemoveDirective(\"proxy_cache_key\", []string{\"$host$uri$is_args$args\"})\n\tl.RemoveDirective(\"proxy_cache_valid\", []string{\"200\"})\n\tl.Cache = false\n\tl.ServerCacheTime = 0\n\tl.ServerCacheUint = \"\"\n}\n\nfunc (l *Location) AddSubFilter(subFilters map[string]string) {\n\tl.RemoveDirective(\"sub_filter\", []string{})\n\tl.Replaces = subFilters\n\tfor k, v := range subFilters {\n\t\tl.UpdateDirective(\"sub_filter\", []string{fmt.Sprintf(`\"%s\"`, k), fmt.Sprintf(`\"%s\"`, v)})\n\t}\n\tl.UpdateDirective(\"proxy_set_header\", []string{\"Accept-Encoding\", `\"\"`})\n\tl.UpdateDirective(\"sub_filter_once\", []string{\"off\"})\n\tl.UpdateDirective(\"sub_filter_types\", []string{\"*\"})\n}\n\nfunc (l *Location) RemoveSubFilter() {\n\tl.RemoveDirective(\"sub_filter\", []string{})\n\tl.RemoveDirective(\"proxy_set_header\", []string{\"Accept-Encoding\", `\"\"`})\n\tl.RemoveDirective(\"sub_filter_once\", []string{\"off\"})\n\tl.RemoveDirective(\"sub_filter_types\", []string{\"*\"})\n\tl.Replaces = nil\n}\n\nfunc (l *Location) AddCorsOption() {\n\tl.RemoveCorsOption()\n\tnewDir := &Directive{\n\t\tName:       \"if\",\n\t\tParameters: []string{\"(\", \"$request_method\", \"=\", \"'OPTIONS'\", \")\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\tblock.AppendDirectives(&Directive{\n\t\tName:       \"add_header\",\n\t\tParameters: []string{\"Access-Control-Max-Age\", \"1728000\"},\n\t})\n\tblock.AppendDirectives(&Directive{\n\t\tName:       \"add_header\",\n\t\tParameters: []string{\"Content-Type\", \"'text/plain;charset=UTF-8'\"},\n\t})\n\tblock.AppendDirectives(&Directive{\n\t\tName:       \"add_header\",\n\t\tParameters: []string{\"Content-Length\", \"0\"},\n\t})\n\tblock.AppendDirectives(&Directive{\n\t\tName:       \"return\",\n\t\tParameters: []string{\"204\"},\n\t})\n\tnewDir.Block = block\n\tdirectives := l.GetDirectives()\n\tdirectives = append(directives, newDir)\n\tl.Directives = directives\n}\n\nfunc (l *Location) RemoveCorsOption() {\n\tl.RemoveDirectiveByFullParams(\"if\", []string{\"(\", \"$request_method\", \"=\", \"'OPTIONS'\", \")\"})\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/lua_block.go",
    "content": "package components\n\nimport (\n\t\"fmt\"\n)\n\ntype LuaBlock struct {\n\tDirectives []IDirective\n\tName       string\n\tComment    string\n\tLuaCode    string\n\tLine       int\n}\n\nfunc NewLuaBlock(directive IDirective) (*LuaBlock, error) {\n\tif block := directive.GetBlock(); block != nil {\n\t\tlb := &LuaBlock{\n\t\t\tDirectives: []IDirective{},\n\t\t\tName:       directive.GetName(),\n\t\t\tLuaCode:    block.GetCodeBlock(),\n\t\t}\n\n\t\tlb.Directives = append(lb.Directives, block.GetDirectives()...)\n\t\treturn lb, nil\n\t}\n\treturn nil, fmt.Errorf(\"%s must have a block\", directive.GetName())\n}\n\nfunc (lb *LuaBlock) GetName() string {\n\treturn lb.Name\n}\n\nfunc (lb *LuaBlock) GetParameters() []string {\n\treturn []string{}\n}\n\nfunc (lb *LuaBlock) GetDirectives() []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tdirectives = append(directives, lb.Directives...)\n\treturn directives\n}\n\nfunc (lb *LuaBlock) FindDirectives(directiveName string) []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tfor _, directive := range lb.GetDirectives() {\n\t\tif directive.GetName() == directiveName {\n\t\t\tdirectives = append(directives, directive)\n\t\t}\n\t\tif directive.GetBlock() != nil {\n\t\t\tdirectives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)\n\t\t}\n\t}\n\n\treturn directives\n}\n\nfunc (lb *LuaBlock) GetCodeBlock() string {\n\treturn lb.LuaCode\n}\n\nfunc (lb *LuaBlock) GetBlock() IBlock {\n\treturn lb\n}\n\nfunc (lb *LuaBlock) GetComment() string {\n\treturn lb.Comment\n}\n\nfunc (lb *LuaBlock) RemoveDirective(key string, params []string) {\n\tdirectives := lb.Directives\n\tvar newDirectives []IDirective\n\tfor _, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif len(params) > 0 {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif oldParams[0] == params[0] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tnewDirectives = append(newDirectives, dir)\n\t}\n\tlb.Directives = newDirectives\n}\n\nfunc (lb *LuaBlock) UpdateDirective(key string, params []string) {\n\tif key == \"\" || len(params) == 0 {\n\t\treturn\n\t}\n\tdirectives := lb.Directives\n\tindex := -1\n\tfor i, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif !(len(oldParams) > 0 && oldParams[0] == params[0]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tnewDirective := &Directive{\n\t\tName:       key,\n\t\tParameters: params,\n\t}\n\tif index > -1 {\n\t\tdirectives[index] = newDirective\n\t} else {\n\t\tdirectives = append(directives, newDirective)\n\t}\n\tlb.Directives = directives\n}\n\nfunc (lb *LuaBlock) GetLine() int {\n\treturn lb.Line\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/server.go",
    "content": "package components\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Server struct {\n\tComment          string\n\tListens          []*ServerListen\n\tDirectives       []IDirective\n\tLine             int\n\tCors             bool\n\tAllowMethods     string\n\tAllowHeaders     string\n\tAllowOrigins     string\n\tAllowCredentials bool\n\tPreflight        bool\n}\n\nfunc (s *Server) GetCodeBlock() string {\n\treturn \"\"\n}\n\nfunc NewServer(directive IDirective) (*Server, error) {\n\tserver := &Server{}\n\tif block := directive.GetBlock(); block != nil {\n\t\tserver.Line = directive.GetBlock().GetLine()\n\t\tserver.Comment = block.GetComment()\n\t\tdirectives := block.GetDirectives()\n\t\tfor _, dir := range directives {\n\t\t\tswitch dir.GetName() {\n\t\t\tcase \"listen\":\n\t\t\t\tserver.Listens = append(server.Listens, NewServerListen(dir.GetParameters(), dir.GetLine()))\n\t\t\tcase \"add_header\":\n\t\t\t\tparams := dir.GetParameters()\n\t\t\t\tif params[0] == \"Access-Control-Allow-Origin\" {\n\t\t\t\t\tserver.Cors = true\n\t\t\t\t\tserver.AllowOrigins = params[1]\n\t\t\t\t}\n\t\t\t\tif params[0] == \"Access-Control-Allow-Methods\" {\n\t\t\t\t\tserver.AllowMethods = params[1]\n\t\t\t\t}\n\t\t\t\tif params[0] == \"Access-Control-Allow-Headers\" {\n\t\t\t\t\tserver.AllowHeaders = params[1]\n\t\t\t\t}\n\t\t\t\tif params[0] == \"Access-Control-Allow-Credentials\" && params[1] == \"true\" {\n\t\t\t\t\tserver.AllowCredentials = true\n\t\t\t\t}\n\t\t\t\tserver.Directives = append(server.Directives, dir)\n\t\t\tcase \"if\":\n\t\t\t\tparams := dir.GetParameters()\n\t\t\t\tif params[0] == \"(\" && params[1] == \"$request_method\" && params[2] == `=` && params[3] == `'OPTIONS'` && params[4] == \")\" {\n\t\t\t\t\tserver.Preflight = true\n\t\t\t\t}\n\t\t\t\tserver.Directives = append(server.Directives, dir)\n\t\t\tdefault:\n\t\t\t\tserver.Directives = append(server.Directives, dir)\n\t\t\t}\n\t\t}\n\t\treturn server, nil\n\t}\n\treturn nil, errors.New(\"server directive must have a block\")\n}\n\nfunc (s *Server) GetName() string {\n\treturn \"server\"\n}\n\nfunc (s *Server) GetParameters() []string {\n\treturn []string{}\n}\n\nfunc (s *Server) GetBlock() IBlock {\n\treturn s\n}\n\nfunc (s *Server) GetComment() string {\n\treturn s.Comment\n}\n\nfunc (s *Server) GetDirectives() []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tfor _, ls := range s.Listens {\n\t\tdirectives = append(directives, ls)\n\t}\n\tdirectives = append(directives, s.Directives...)\n\treturn directives\n}\n\nfunc (s *Server) FindDirectives(directiveName string) []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tfor _, directive := range s.Directives {\n\t\tif directive.GetName() == directiveName {\n\t\t\tdirectives = append(directives, directive)\n\t\t}\n\t\tif directive.GetBlock() != nil {\n\t\t\tdirectives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)\n\t\t}\n\t}\n\tif directiveName == \"listen\" {\n\t\tfor _, listen := range s.Listens {\n\t\t\tparams := []string{listen.Bind}\n\t\t\tparams = append(params, listen.Parameters...)\n\t\t\tif listen.DefaultServer != \"\" {\n\t\t\t\tparams = append(params, DefaultServer)\n\t\t\t}\n\t\t\tdirectives = append(directives, &Directive{\n\t\t\t\tName:       \"listen\",\n\t\t\t\tParameters: params,\n\t\t\t})\n\t\t}\n\t}\n\treturn directives\n}\n\nfunc (s *Server) UpdateDirective(key string, params []string) {\n\tif key == \"\" || len(params) == 0 {\n\t\treturn\n\t}\n\tif key == \"listen\" {\n\t\tdefaultServer := false\n\t\tparamLen := len(params)\n\t\tif paramLen > 0 && params[paramLen-1] == \"default_server\" {\n\t\t\tparams = params[:paramLen-1]\n\t\t\tdefaultServer = true\n\t\t}\n\t\ts.UpdateListen(params[0], defaultServer, params[1:]...)\n\t\treturn\n\t}\n\n\tdirectives := s.Directives\n\tindex := -1\n\tfor i, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif !(len(oldParams) > 0 && oldParams[0] == params[0]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tnewDirective := &Directive{\n\t\tName:       key,\n\t\tParameters: params,\n\t}\n\tif index > -1 {\n\t\tdirectives[index] = newDirective\n\t} else {\n\t\tdirectives = append(directives, newDirective)\n\t}\n\ts.Directives = directives\n}\n\nfunc (s *Server) RemoveDirective(key string, params []string) {\n\tdirectives := s.Directives\n\tvar newDirectives []IDirective\n\tfor _, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif len(params) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\toldParams := dir.GetParameters()\n\t\t\tif key == \"location\" {\n\t\t\t\tif len(params) == len(oldParams) {\n\t\t\t\t\texist := true\n\t\t\t\t\tfor i := range params {\n\t\t\t\t\t\tif params[i] != oldParams[i] {\n\t\t\t\t\t\t\texist = false\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif exist {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif oldParams[0] == params[0] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tnewDirectives = append(newDirectives, dir)\n\t}\n\ts.Directives = newDirectives\n}\n\nfunc (s *Server) GetLine() int {\n\treturn s.Line\n}\n\nfunc (s *Server) AddListen(bind string, defaultServer bool, params ...string) {\n\tlisten := &ServerListen{\n\t\tBind:       bind,\n\t\tParameters: params,\n\t}\n\tif defaultServer {\n\t\tlisten.DefaultServer = DefaultServer\n\t}\n\ts.Listens = append(s.Listens, listen)\n}\n\nfunc isSameArray(arr1, arr2 []string) bool {\n\tset1 := make(map[string]struct{})\n\tfor _, v := range arr1 {\n\t\tset1[v] = struct{}{}\n\t}\n\tfor _, v := range arr2 {\n\t\tif _, exists := set1[v]; !exists {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (s *Server) UpdateListen(bind string, defaultServer bool, params ...string) {\n\tlisten := &ServerListen{\n\t\tBind:       bind,\n\t\tParameters: params,\n\t}\n\tif defaultServer {\n\t\tlisten.DefaultServer = DefaultServer\n\t}\n\tvar newListens []*ServerListen\n\texist := false\n\tfor _, li := range s.Listens {\n\t\tif li.Bind == bind && isSameArray(li.Parameters, params) {\n\t\t\texist = true\n\t\t\tnewListens = append(newListens, listen)\n\t\t} else {\n\t\t\tnewListens = append(newListens, li)\n\t\t}\n\t}\n\tif !exist {\n\t\tnewListens = append(newListens, listen)\n\t}\n\n\ts.Listens = newListens\n}\n\nfunc (s *Server) DeleteListen(bind string) {\n\tvar newListens []*ServerListen\n\tfor _, li := range s.Listens {\n\t\tif li.Bind != bind {\n\t\t\tnewListens = append(newListens, li)\n\t\t}\n\t}\n\ts.Listens = newListens\n}\n\nfunc (s *Server) RemoveListen(bind string, params ...string) {\n\tvar newListens []*ServerListen\n\tfor _, li := range s.Listens {\n\t\tif li.Bind == bind && isSameArray(li.Parameters, params) {\n\t\t\tcontinue\n\t\t}\n\t\tnewListens = append(newListens, li)\n\t}\n\ts.Listens = newListens\n}\n\nfunc (s *Server) DeleteServerName(name string) {\n\tvar names []string\n\tdirs := s.FindDirectives(\"server_name\")\n\tparams := dirs[0].GetParameters()\n\tfor _, param := range params {\n\t\tif param != name {\n\t\t\tnames = append(names, param)\n\t\t}\n\t}\n\ts.UpdateServerName(names)\n}\n\nfunc (s *Server) AddServerName(name string) {\n\tdirs := s.FindDirectives(\"server_name\")\n\tparams := dirs[0].GetParameters()\n\tparams = append(params, name)\n\ts.UpdateServerName(params)\n}\n\nfunc (s *Server) UpdateServerName(names []string) {\n\ts.UpdateDirective(\"server_name\", names)\n}\n\nfunc (s *Server) UpdateRoot(path string) {\n\ts.UpdateDirective(\"root\", []string{path})\n}\n\nfunc (s *Server) UpdateRootProxyForAi(proxy []string) {\n\tnewDir := Directive{\n\t\tName:       \"location\",\n\t\tParameters: []string{\"/\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\tblock.AppendDirectives(\n\t\t&Directive{\n\t\t\tName: \"proxy_buffering\",\n\t\t\tParameters: []string{\n\t\t\t\t\"off\",\n\t\t\t},\n\t\t},\n\t\t&Directive{\n\t\t\tName: \"proxy_cache\",\n\t\t\tParameters: []string{\n\t\t\t\t\"off\",\n\t\t\t},\n\t\t},\n\t\t&Directive{\n\t\t\tName: \"proxy_http_version\",\n\t\t\tParameters: []string{\n\t\t\t\t\"1.1\",\n\t\t\t},\n\t\t},\n\t\t&Directive{\n\t\t\tName: \"proxy_set_header\",\n\t\t\tParameters: []string{\n\t\t\t\t\"Connection\", \"''\",\n\t\t\t},\n\t\t},\n\t\t&Directive{\n\t\t\tName: \"chunked_transfer_encoding\",\n\t\t\tParameters: []string{\n\t\t\t\t\"off\",\n\t\t\t},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_pass\",\n\t\t\tParameters: proxy,\n\t\t},\n\t)\n\n\tnewDir.Block = block\n\ts.UpdateDirectiveBySecondKey(\"location\", \"/\", newDir)\n}\n\nfunc (s *Server) UpdateRootLocation() {\n\tnewDir := Directive{\n\t\tName:       \"location\",\n\t\tParameters: []string{\"/\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\tblock.AppendDirectives(&Directive{\n\t\tName:       \"root\",\n\t\tParameters: []string{\"index.html\"},\n\t})\n\tnewDir.Block = block\n}\n\nfunc (s *Server) UpdateRootProxy(proxy []string) {\n\thttpsProxy := len(proxy) > 0 && strings.HasPrefix(strings.ToLower(strings.TrimSpace(proxy[0])), \"https://\")\n\tnewDir := Directive{\n\t\tName:       \"location\",\n\t\tParameters: []string{\"/\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\n\tblock.AppendDirectives(\n\t\t&Directive{\n\t\t\tName:       \"proxy_set_header\",\n\t\t\tParameters: []string{\"Host\", \"$host\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_set_header\",\n\t\t\tParameters: []string{\"X-Forwarded-For\", \"$proxy_add_x_forwarded_for\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_set_header\",\n\t\t\tParameters: []string{\"X-Forwarded-Host\", \"$server_name\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_set_header\",\n\t\t\tParameters: []string{\"X-Real-IP\", \"$remote_addr\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_set_header\",\n\t\t\tParameters: []string{\"X-Forwarded-Proto\", \"$scheme\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_set_header\",\n\t\t\tParameters: []string{\"Connection\", \"upgrade\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_set_header\",\n\t\t\tParameters: []string{\"Upgrade\", \"$http_upgrade\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_http_version\",\n\t\t\tParameters: []string{\"1.1\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_ssl_server_name\",\n\t\t\tParameters: []string{map[bool]string{true: \"on\", false: \"off\"}[httpsProxy]},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_ssl_name\",\n\t\t\tParameters: []string{\"$proxy_host\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"proxy_pass\",\n\t\t\tParameters: proxy,\n\t\t},\n\t)\n\n\tnewDir.Block = block\n\ts.UpdateDirectiveBySecondKey(\"location\", \"/\", newDir)\n}\n\nfunc (s *Server) UpdatePHPProxy(proxy []string, localPath string) {\n\tnewDir := Directive{\n\t\tName:       \"location\",\n\t\tParameters: []string{\"~ [^/]\\\\.php(/|$)\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\tblock.AppendDirectives(\n\t\t&Directive{\n\t\t\tName:       \"fastcgi_pass\",\n\t\t\tParameters: proxy,\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"include\",\n\t\t\tParameters: []string{\"fastcgi-php.conf\"},\n\t\t},\n\t\t&Directive{\n\t\t\tName:       \"include\",\n\t\t\tParameters: []string{\"fastcgi_params\"},\n\t\t},\n\t)\n\tif localPath == \"\" {\n\t\tblock.AppendDirectives(&Directive{\n\t\t\tName:       \"set\",\n\t\t\tParameters: []string{\"$real_script_name\", \"$fastcgi_script_name\"},\n\t\t})\n\t\tifDir := &Directive{\n\t\t\tName:       \"if\",\n\t\t\tParameters: []string{\"($fastcgi_script_name ~ \\\"^(.+?\\\\.php)(/.+)$\\\")\"},\n\t\t}\n\t\tifDir.Block = &Block{\n\t\t\tDirectives: []IDirective{\n\t\t\t\t&Directive{\n\t\t\t\t\tName:       \"set\",\n\t\t\t\t\tParameters: []string{\"$real_script_name\", \"$1\"},\n\t\t\t\t},\n\t\t\t\t&Directive{\n\t\t\t\t\tName:       \"set\",\n\t\t\t\t\tParameters: []string{\"$path_info\", \"$2\"},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tblock.AppendDirectives(\n\t\t\tifDir,\n\t\t\t&Directive{\n\t\t\t\tName:       \"fastcgi_param\",\n\t\t\t\tParameters: []string{\"SCRIPT_FILENAME\", \"$document_root$real_script_name\"},\n\t\t\t},\n\t\t\t&Directive{\n\t\t\t\tName:       \"fastcgi_param\",\n\t\t\t\tParameters: []string{\"SCRIPT_NAME\", \"$real_script_name\"},\n\t\t\t},\n\t\t\t&Directive{\n\t\t\t\tName:       \"fastcgi_param\",\n\t\t\t\tParameters: []string{\"PATH_INFO\", \"$path_info\"},\n\t\t\t})\n\n\t} else {\n\t\tblock.AppendDirectives(&Directive{\n\t\t\tName:       \"fastcgi_param\",\n\t\t\tParameters: []string{\"SCRIPT_FILENAME\", localPath},\n\t\t})\n\t}\n\tnewDir.Block = block\n\ts.UpdateDirectiveBySecondKey(\"location\", \"~ [^/]\\\\.php(/|$)\", newDir)\n}\n\nfunc (s *Server) UpdateDirectiveBySecondKey(name string, key string, directive Directive) {\n\tdirectives := s.Directives\n\tindex := -1\n\tfor i, dir := range directives {\n\t\tif dir.GetName() == name && dir.GetParameters()[0] == key {\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif index > -1 {\n\t\tdirectives[index] = &directive\n\t} else {\n\t\tdirectives = append(directives, &directive)\n\t}\n\ts.Directives = directives\n}\n\nfunc (s *Server) RemoveListenByBind(bind string) {\n\tvar listens []*ServerListen\n\tfor _, listen := range s.Listens {\n\t\tif listen.Bind != bind {\n\t\t\tlistens = append(listens, listen)\n\t\t}\n\t}\n\ts.Listens = listens\n}\n\nfunc (s *Server) AddHTTP2HTTPS(httpsPort int) {\n\tnewDir := Directive{\n\t\tName:       \"if\",\n\t\tParameters: []string{\"($scheme = http)\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\tif httpsPort == 443 {\n\t\tblock.AppendDirectives(&Directive{\n\t\t\tName:       \"return\",\n\t\t\tParameters: []string{\"301\", \"https://$host$request_uri\"},\n\t\t})\n\t} else {\n\t\tblock.AppendDirectives(&Directive{\n\t\t\tName:       \"return\",\n\t\t\tParameters: []string{\"301\", fmt.Sprintf(\"https://$host:%d$request_uri\", httpsPort)},\n\t\t})\n\t}\n\n\tnewDir.Block = block\n\ts.UpdateDirectiveBySecondKey(\"if\", \"($scheme\", newDir)\n}\n\nfunc (s *Server) UpdateAllowIPs(ips []string) {\n\tindex := -1\n\tfor i, directive := range s.Directives {\n\t\tif directive.GetName() == \"location\" && directive.GetParameters()[0] == \"/\" {\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tipDirectives := make([]IDirective, 0)\n\tfor _, ip := range ips {\n\t\tipDirectives = append(ipDirectives, &Directive{\n\t\t\tName:       \"allow\",\n\t\t\tParameters: []string{ip},\n\t\t})\n\t}\n\tipDirectives = append(ipDirectives, &Directive{\n\t\tName:       \"deny\",\n\t\tParameters: []string{\"all\"},\n\t})\n\tif index != -1 {\n\t\tnewDirectives := append(ipDirectives, s.Directives[index:]...)\n\t\ts.Directives = append(s.Directives[:index], newDirectives...)\n\t} else {\n\t\ts.Directives = append(s.Directives, ipDirectives...)\n\t}\n}\n\nfunc (s *Server) AddCorsOption() {\n\tnewDir := &Directive{\n\t\tName:       \"if\",\n\t\tParameters: []string{\"(\", \"$request_method\", \"=\", \"'OPTIONS'\", \")\"},\n\t\tBlock:      &Block{},\n\t}\n\tblock := &Block{}\n\tblock.AppendDirectives(&Directive{\n\t\tName:       \"return\",\n\t\tParameters: []string{\"204\"},\n\t})\n\tnewDir.Block = block\n\tdirectives := s.GetDirectives()\n\tnewDirectives := make([]IDirective, 0)\n\tfor _, dir := range directives {\n\t\tif dir.GetName() != \"listen\" {\n\t\t\tnewDirectives = append(newDirectives, dir)\n\t\t}\n\t}\n\tnewDirectives = append(newDirectives, newDir)\n\ts.Directives = newDirectives\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/server_listen.go",
    "content": "package components\n\nimport (\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\nconst DefaultServer = \"default_server\"\n\ntype ServerListen struct {\n\tBind          string\n\tDefaultServer string\n\tParameters    []string\n\tComment       string\n\tLine          int\n}\n\nfunc NewServerListen(params []string, line int) *ServerListen {\n\tserver := &ServerListen{\n\t\tParameters: []string{},\n\t\tLine:       line,\n\t}\n\tfor _, param := range params {\n\t\tif isBind(param) {\n\t\t\tserver.Bind = param\n\t\t} else if param == DefaultServer {\n\t\t\tserver.DefaultServer = DefaultServer\n\t\t} else {\n\t\t\tserver.Parameters = append(server.Parameters, param)\n\t\t}\n\t}\n\treturn server\n}\n\nfunc isBind(param string) bool {\n\tif common.IsNum(param) {\n\t\treturn true\n\t}\n\tif strings.Contains(param, \"*\") || strings.Contains(param, \":\") || strings.Contains(param, \".\") {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (sl *ServerListen) GetName() string {\n\treturn \"listen\"\n}\n\nfunc (sl *ServerListen) GetBlock() IBlock {\n\treturn nil\n}\n\nfunc (sl *ServerListen) GetParameters() []string {\n\tparams := []string{sl.Bind}\n\tparams = append(params, sl.Parameters...)\n\tparams = append(params, sl.DefaultServer)\n\treturn params\n}\n\nfunc (sl *ServerListen) GetComment() string {\n\treturn sl.Comment\n}\n\nfunc (sl *ServerListen) AddDefaultServer() {\n\tsl.DefaultServer = DefaultServer\n}\n\nfunc (sl *ServerListen) RemoveDefaultServe() {\n\tsl.DefaultServer = \"\"\n}\n\nfunc (sl *ServerListen) GetLine() int {\n\treturn sl.Line\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/statement.go",
    "content": "package components\n\ntype IBlock interface {\n\tGetDirectives() []IDirective\n\tFindDirectives(directiveName string) []IDirective\n\tRemoveDirective(name string, params []string)\n\tUpdateDirective(name string, params []string)\n\tGetComment() string\n\tGetLine() int\n\tGetCodeBlock() string\n}\n\ntype IDirective interface {\n\tGetName() string\n\tGetParameters() []string\n\tGetBlock() IBlock\n\tGetComment() string\n\tGetLine() int\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/upstream.go",
    "content": "package components\n\nimport (\n\t\"errors\"\n)\n\ntype Upstream struct {\n\tUpstreamName    string\n\tUpstreamServers []*UpstreamServer\n\tDirectives      []IDirective\n\tComment         string\n\tLine            int\n}\n\nfunc (us *Upstream) GetCodeBlock() string {\n\treturn \"\"\n}\n\nfunc (us *Upstream) GetName() string {\n\treturn \"upstream\"\n}\n\nfunc (us *Upstream) GetParameters() []string {\n\treturn []string{us.UpstreamName}\n}\n\nfunc (us *Upstream) GetBlock() IBlock {\n\treturn us\n}\n\nfunc (us *Upstream) GetComment() string {\n\treturn us.Comment\n}\n\nfunc (us *Upstream) GetDirectives() []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tdirectives = append(directives, us.Directives...)\n\tfor _, uss := range us.UpstreamServers {\n\t\tdirectives = append(directives, uss)\n\t}\n\treturn directives\n}\n\nfunc NewUpstream(directive IDirective) (*Upstream, error) {\n\tparameters := directive.GetParameters()\n\tus := &Upstream{\n\t\tUpstreamName: parameters[0],\n\t\tLine:         directive.GetLine(),\n\t}\n\n\tif block := directive.GetBlock(); block != nil {\n\t\tus.Comment = block.GetComment()\n\t\tfor _, d := range block.GetDirectives() {\n\t\t\tif d.GetName() == \"server\" {\n\t\t\t\tus.UpstreamServers = append(us.UpstreamServers, NewUpstreamServer(d))\n\t\t\t} else {\n\t\t\t\tus.Directives = append(us.Directives, d)\n\t\t\t}\n\t\t}\n\t\treturn us, nil\n\t}\n\n\treturn nil, errors.New(\"missing upstream block\")\n}\n\nfunc (us *Upstream) AddServer(server *UpstreamServer) {\n\tus.UpstreamServers = append(us.UpstreamServers, server)\n}\n\nfunc (us *Upstream) FindDirectives(directiveName string) []IDirective {\n\tdirectives := make([]IDirective, 0)\n\tfor _, directive := range us.Directives {\n\t\tif directive.GetName() == directiveName {\n\t\t\tdirectives = append(directives, directive)\n\t\t}\n\t\tif directive.GetBlock() != nil {\n\t\t\tdirectives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)\n\t\t}\n\t}\n\n\treturn directives\n}\n\nfunc (us *Upstream) UpdateDirective(key string, params []string) {\n\tif key == \"\" {\n\t\treturn\n\t}\n\tdirectives := us.Directives\n\tindex := -1\n\tfor i, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif !(len(oldParams) > 0 && oldParams[0] == params[0]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tnewDirective := &Directive{\n\t\tName:       key,\n\t\tParameters: params,\n\t}\n\tif index > -1 {\n\t\tdirectives[index] = newDirective\n\t} else {\n\t\tdirectives = append(directives, newDirective)\n\t}\n\tus.Directives = directives\n}\n\nfunc (us *Upstream) RemoveDirective(key string, params []string) {\n\tdirectives := us.Directives\n\tvar newDirectives []IDirective\n\tfor _, dir := range directives {\n\t\tif dir.GetName() == key {\n\t\t\tif IsRepeatKey(key) && len(params) > 0 {\n\t\t\t\toldParams := dir.GetParameters()\n\t\t\t\tif oldParams[0] == params[0] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tnewDirectives = append(newDirectives, dir)\n\t}\n\tus.Directives = newDirectives\n}\n\nfunc (us *Upstream) GetLine() int {\n\treturn us.Line\n}\n"
  },
  {
    "path": "agent/utils/nginx/components/upstream_server.go",
    "content": "package components\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n)\n\ntype UpstreamServer struct {\n\tComment    string\n\tAddress    string\n\tFlags      []string\n\tParameters map[string]string\n\tLine       int\n}\n\nfunc (uss *UpstreamServer) GetName() string {\n\treturn \"server\"\n}\n\nfunc (uss *UpstreamServer) GetBlock() IBlock {\n\treturn nil\n}\n\nfunc (uss *UpstreamServer) GetParameters() []string {\n\treturn uss.GetDirective().Parameters\n}\n\nfunc (uss *UpstreamServer) GetComment() string {\n\treturn uss.Comment\n}\n\nfunc (uss *UpstreamServer) GetDirective() *Directive {\n\tdirective := &Directive{\n\t\tName:       \"server\",\n\t\tParameters: make([]string, 0),\n\t\tBlock:      nil,\n\t}\n\n\tdirective.Parameters = append(directive.Parameters, uss.Address)\n\n\tparamNames := make([]string, 0)\n\tfor k := range uss.Parameters {\n\t\tparamNames = append(paramNames, k)\n\t}\n\tsort.Strings(paramNames)\n\n\tfor _, k := range paramNames {\n\t\tdirective.Parameters = append(directive.Parameters, fmt.Sprintf(\"%s=%s\", k, uss.Parameters[k]))\n\t}\n\n\tdirective.Parameters = append(directive.Parameters, uss.Flags...)\n\n\treturn directive\n}\n\nfunc NewUpstreamServer(directive IDirective) *UpstreamServer {\n\tuss := &UpstreamServer{\n\t\tComment:    directive.GetComment(),\n\t\tFlags:      make([]string, 0),\n\t\tParameters: make(map[string]string, 0),\n\t\tLine:       directive.GetLine(),\n\t}\n\n\tfor i, parameter := range directive.GetParameters() {\n\t\tif i == 0 {\n\t\t\tuss.Address = parameter\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(parameter, \"=\") {\n\t\t\ts := strings.SplitN(parameter, \"=\", 2)\n\t\t\tuss.Parameters[s[0]] = s[1]\n\t\t} else {\n\t\t\tuss.Flags = append(uss.Flags, parameter)\n\t\t}\n\t}\n\n\treturn uss\n}\n\nfunc (uss *UpstreamServer) GetLine() int {\n\treturn uss.Line\n}\n"
  },
  {
    "path": "agent/utils/nginx/dumper.go",
    "content": "package nginx\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n)\n\nvar (\n\tIndentedStyle = &Style{\n\t\tSpaceBeforeBlocks: false,\n\t\tStartIndent:       0,\n\t\tIndent:            4,\n\t}\n)\n\ntype Style struct {\n\tSpaceBeforeBlocks bool\n\tStartIndent       int\n\tIndent            int\n}\n\nfunc (s *Style) Iterate() *Style {\n\tnewStyle := &Style{\n\t\tSpaceBeforeBlocks: s.SpaceBeforeBlocks,\n\t\tStartIndent:       s.StartIndent + s.Indent,\n\t\tIndent:            s.Indent,\n\t}\n\treturn newStyle\n}\n\nfunc DumpDirective(d components.IDirective, style *Style) string {\n\tvar buf bytes.Buffer\n\n\tif style.SpaceBeforeBlocks && d.GetBlock() != nil {\n\t\tbuf.WriteString(\"\\n\")\n\t}\n\tbuf.WriteString(fmt.Sprintf(\"%s%s\", strings.Repeat(\" \", style.StartIndent), d.GetName()))\n\tif len(d.GetParameters()) > 0 {\n\t\tbuf.WriteString(fmt.Sprintf(\" %s\", strings.Join(d.GetParameters(), \" \")))\n\t}\n\tif d.GetBlock() == nil {\n\t\tif d.GetName() != \"\" {\n\t\t\tbuf.WriteRune(';')\n\t\t\tbuf.WriteString(\" \")\n\t\t}\n\t\tif d.GetComment() != \"\" {\n\t\t\tbuf.WriteString(d.GetComment())\n\t\t}\n\t} else {\n\t\tbuf.WriteString(\" {\")\n\t\tif d.GetComment() != \"\" {\n\t\t\tbuf.WriteString(\" \")\n\t\t\tbuf.WriteString(d.GetComment())\n\t\t}\n\t\tbuf.WriteString(\"\\n\")\n\t\tbuf.WriteString(DumpBlock(d.GetBlock(), style.Iterate(), d.GetBlock().GetLine()))\n\t\tbuf.WriteString(fmt.Sprintf(\"\\n%s}\", strings.Repeat(\" \", style.StartIndent)))\n\t}\n\treturn buf.String()\n}\n\nfunc DumpBlock(b components.IBlock, style *Style, startLine int) string {\n\tvar buf bytes.Buffer\n\n\tif b.GetCodeBlock() != \"\" {\n\t\tluaLines := strings.Split(b.GetCodeBlock(), \"\\n\")\n\t\tfor i, line := range luaLines {\n\t\t\tif strings.Replace(line, \" \", \"\", -1) == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tbuf.WriteString(line)\n\t\t\tif i != len(luaLines)-1 {\n\t\t\t\tbuf.WriteString(\"\\n\")\n\t\t\t}\n\t\t}\n\t\treturn buf.String()\n\t}\n\n\tline := startLine\n\tif b.GetLine() > startLine {\n\t\tfor i := 0; i < b.GetLine()-startLine; i++ {\n\t\t\tbuf.WriteString(\"\\n\")\n\t\t}\n\t\tline = b.GetLine()\n\t}\n\n\tdirectives := b.GetDirectives()\n\n\tvar sortDirectives []components.IDirective\n\tvar proxyIncludes []components.IDirective\n\tfor _, directive := range directives {\n\t\tif directive.GetName() == \"include\" && strings.Contains(strings.Join(directive.GetParameters(), \" \"), \"/proxy/\") {\n\t\t\tproxyIncludes = append(proxyIncludes, directive)\n\t\t} else {\n\t\t\tsortDirectives = append(sortDirectives, directive)\n\t\t}\n\t}\n\tdirectives = append(sortDirectives, proxyIncludes...)\n\n\tfor i, directive := range directives {\n\n\t\tif directive.GetLine() > line {\n\t\t\tfor i := 0; i < b.GetLine()-line; i++ {\n\t\t\t\tbuf.WriteString(\"\\n\")\n\t\t\t}\n\t\t\tline = b.GetLine()\n\t\t}\n\n\t\tbuf.WriteString(DumpDirective(directive, style))\n\t\tif i != len(directives)-1 {\n\t\t\tbuf.WriteString(\"\\n\")\n\t\t}\n\t}\n\treturn buf.String()\n}\n\nfunc DumpConfig(c *components.Config, style *Style) string {\n\treturn DumpBlock(c.Block, style, 1)\n}\n\nfunc WriteConfig(c *components.Config, style *Style) error {\n\treturn os.WriteFile(c.FilePath, []byte(DumpConfig(c, style)), constant.DirPerm)\n}\n"
  },
  {
    "path": "agent/utils/nginx/parser/flag/flag.go",
    "content": "package flag\n\ntype Type int\n\nconst (\n\tEOF Type = iota\n\tEol\n\tKeyword\n\tQuotedString\n\tVariable\n\tBlockStart\n\tBlockEnd\n\tSemicolon\n\tComment\n\tIllegal\n\tRegex\n\tLuaCode\n)\n\nvar (\n\tFlagName = map[Type]string{\n\t\tQuotedString: \"QuotedString\",\n\t\tEOF:          \"Eof\",\n\t\tKeyword:      \"Keyword\",\n\t\tVariable:     \"Variable\",\n\t\tBlockStart:   \"BlockStart\",\n\t\tBlockEnd:     \"BlockEnd\",\n\t\tSemicolon:    \"Semicolon\",\n\t\tComment:      \"Comment\",\n\t\tIllegal:      \"Illegal\",\n\t\tRegex:        \"Regex\",\n\t}\n)\n\nfunc (tt Type) String() string {\n\treturn FlagName[tt]\n}\n\ntype Flag struct {\n\tType    Type\n\tLiteral string\n\tLine    int\n\tColumn  int\n}\n\nfunc (t Flag) Lit(literal string) Flag {\n\tt.Literal = literal\n\treturn t\n}\n\ntype Flags []Flag\n\nfunc (t Flag) Is(typ Type) bool {\n\treturn t.Type == typ\n}\n\nfunc (t Flag) IsParameterEligible() bool {\n\treturn t.Is(Keyword) || t.Is(QuotedString) || t.Is(Variable) || t.Is(Regex)\n}\n"
  },
  {
    "path": "agent/utils/nginx/parser/lexer.go",
    "content": "package parser\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser/flag\"\n)\n\ntype lexer struct {\n\treader     *bufio.Reader\n\tfile       string\n\tline       int\n\tcolumn     int\n\tinLuaBlock bool\n\tLatest     flag.Flag\n}\n\nfunc lex(content string) *lexer {\n\treturn newLexer(bytes.NewBuffer([]byte(content)))\n}\n\nfunc newLexer(r io.Reader) *lexer {\n\treturn &lexer{\n\t\tline:   1,\n\t\treader: bufio.NewReader(r),\n\t}\n}\n\nfunc (s *lexer) scan() flag.Flag {\n\ts.Latest = s.getNextFlag()\n\treturn s.Latest\n}\n\n//func (s *lexer) all() flag.Flags {\n//\ttokens := make([]flag.Flag, 0)\n//\tfor {\n//\t\tv := s.scan()\n//\t\tif v.Type == flag.EOF || v.Type == -1 {\n//\t\t\tbreak\n//\t\t}\n//\t\ttokens = append(tokens, v)\n//\t}\n//\treturn tokens\n//}\n\nfunc (s *lexer) getNextFlag() flag.Flag {\n\tif s.inLuaBlock {\n\t\ts.inLuaBlock = false\n\t\tflag := s.scanLuaCode()\n\t\treturn flag\n\t}\nretoFlag:\n\tch := s.peek()\n\tswitch {\n\tcase isSpace(ch):\n\t\ts.skipWhitespace()\n\t\tgoto retoFlag\n\tcase isEOF(ch):\n\t\treturn s.NewToken(flag.EOF).Lit(string(s.read()))\n\tcase ch == ';':\n\t\treturn s.NewToken(flag.Semicolon).Lit(string(s.read()))\n\tcase ch == '{':\n\t\tif isLuaBlock(s.Latest) {\n\t\t\ts.inLuaBlock = true\n\t\t}\n\t\treturn s.NewToken(flag.BlockStart).Lit(string(s.read()))\n\tcase ch == '}':\n\t\treturn s.NewToken(flag.BlockEnd).Lit(string(s.read()))\n\tcase ch == '#':\n\t\treturn s.scanComment()\n\tcase ch == '$':\n\t\treturn s.scanVariable()\n\tcase isQuote(ch):\n\t\treturn s.scanQuotedString(ch)\n\tdefault:\n\t\treturn s.scanKeyword()\n\t}\n}\n\nfunc (s *lexer) scanLuaCode() flag.Flag {\n\tret := s.NewToken(flag.LuaCode)\n\tstack := make([]rune, 0, 50)\n\tcode := strings.Builder{}\n\n\tfor {\n\t\tch := s.read()\n\t\tif ch == rune(flag.EOF) {\n\t\t\tpanic(\"unexpected end of file while scanning a string, maybe an unclosed lua code?\")\n\t\t}\n\t\tif ch == '#' {\n\t\t\tcode.WriteRune(ch)\n\t\t\tcode.WriteString(s.readUntil(isEndOfLine))\n\t\t\tcontinue\n\t\t} else if ch == '}' {\n\t\t\tif len(stack) == 0 {\n\t\t\t\t_ = s.reader.UnreadRune()\n\t\t\t\treturn ret.Lit(strings.TrimRight(strings.Trim(code.String(), \"\\n\"), \"\\n  \"))\n\t\t\t}\n\t\t\tif stack[len(stack)-1] == '{' {\n\t\t\t\tstack = stack[0 : len(stack)-1]\n\t\t\t}\n\t\t} else if ch == '{' {\n\t\t\tstack = append(stack, ch)\n\t\t}\n\t\tcode.WriteRune(ch)\n\t}\n}\n\nfunc (s *lexer) peek() rune {\n\tr, _, _ := s.reader.ReadRune()\n\t_ = s.reader.UnreadRune()\n\treturn r\n}\n\ntype runeCheck func(rune) bool\n\nfunc (s *lexer) readUntil(until runeCheck) string {\n\tvar buf bytes.Buffer\n\tbuf.WriteRune(s.read())\n\n\tfor {\n\t\tif ch := s.peek(); isEOF(ch) {\n\t\t\tbreak\n\t\t} else if until(ch) {\n\t\t\tbreak\n\t\t} else {\n\t\t\tbuf.WriteRune(s.read())\n\t\t}\n\t}\n\n\treturn buf.String()\n}\n\nfunc (s *lexer) NewToken(tokenType flag.Type) flag.Flag {\n\treturn flag.Flag{\n\t\tType:   tokenType,\n\t\tLine:   s.line,\n\t\tColumn: s.column,\n\t}\n}\n\nfunc (s *lexer) readWhile(while runeCheck) string {\n\tvar buf bytes.Buffer\n\tbuf.WriteRune(s.read())\n\n\tfor {\n\t\tif ch := s.peek(); while(ch) {\n\t\t\tbuf.WriteRune(s.read())\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn buf.String()\n}\n\nfunc (s *lexer) skipWhitespace() {\n\ts.readWhile(isSpace)\n}\n\nfunc (s *lexer) scanComment() flag.Flag {\n\treturn s.NewToken(flag.Comment).Lit(s.readUntil(isEndOfLine))\n}\n\nfunc (s *lexer) scanQuotedString(delimiter rune) flag.Flag {\n\tvar buf bytes.Buffer\n\ttok := s.NewToken(flag.QuotedString)\n\t_, _ = buf.WriteRune(s.read())\n\tfor {\n\t\tch := s.read()\n\n\t\tif ch == rune(flag.EOF) {\n\t\t\tpanic(\"unexpected end of file while scanning a string, maybe an unclosed quote?\")\n\t\t}\n\n\t\tif ch == '\\\\' && (s.peek() == delimiter) {\n\t\t\tbuf.WriteRune(ch)\n\t\t\tbuf.WriteRune(s.read())\n\t\t\tcontinue\n\t\t}\n\n\t\t_, _ = buf.WriteRune(ch)\n\t\tif ch == delimiter {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn tok.Lit(buf.String())\n}\n\nfunc (s *lexer) scanKeyword() flag.Flag {\n\tvar buf bytes.Buffer\n\ttok := s.NewToken(flag.Keyword)\n\tprev := s.read()\n\tbuf.WriteRune(prev)\n\tfor {\n\t\tch := s.peek()\n\n\t\tif isSpace(ch) || isEOF(ch) || ch == ';' {\n\t\t\tbreak\n\t\t}\n\n\t\tif ch == '{' {\n\t\t\tif prev == '$' {\n\t\t\t\tbuf.WriteString(s.readUntil(func(r rune) bool {\n\t\t\t\t\treturn r == '}'\n\t\t\t\t}))\n\t\t\t\tbuf.WriteRune(s.read()) //consume latest '}'\n\t\t\t} else {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tbuf.WriteRune(s.read())\n\t}\n\n\treturn tok.Lit(buf.String())\n}\n\nfunc (s *lexer) scanVariable() flag.Flag {\n\treturn s.NewToken(flag.Variable).Lit(s.readUntil(isKeywordTerminator))\n}\n\nfunc (s *lexer) read() rune {\n\tch, _, err := s.reader.ReadRune()\n\tif err != nil {\n\t\treturn rune(flag.EOF)\n\t}\n\n\tif ch == '\\n' {\n\t\ts.column = 1\n\t\ts.line++\n\t} else {\n\t\ts.column++\n\t}\n\treturn ch\n}\n\nfunc isQuote(ch rune) bool {\n\treturn ch == '\"' || ch == '\\'' || ch == '`'\n}\n\nfunc isKeywordTerminator(ch rune) bool {\n\treturn isSpace(ch) || isEndOfLine(ch) || ch == '{' || ch == ';'\n}\n\nfunc isSpace(ch rune) bool {\n\treturn ch == ' ' || ch == '\\t' || isEndOfLine(ch)\n}\n\nfunc isEOF(ch rune) bool {\n\treturn ch == rune(flag.EOF)\n}\n\nfunc isEndOfLine(ch rune) bool {\n\treturn ch == '\\r' || ch == '\\n'\n}\n\nfunc isLuaBlock(t flag.Flag) bool {\n\treturn t.Type == flag.Keyword && strings.HasSuffix(t.Literal, \"_by_lua_block\")\n}\n"
  },
  {
    "path": "agent/utils/nginx/parser/parser.go",
    "content": "package parser\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\tcomponents \"github.com/1Panel-dev/1Panel/agent/utils/nginx/components\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser/flag\"\n)\n\ntype Parser struct {\n\tlexer             *lexer\n\tcurrentToken      flag.Flag\n\tfollowingToken    flag.Flag\n\tblockWrappers     map[string]func(*components.Directive) (components.IDirective, error)\n\tdirectiveWrappers map[string]func(*components.Directive) components.IDirective\n}\n\nfunc NewStringParser(str string) *Parser {\n\treturn NewParserFromLexer(lex(str))\n}\n\nfunc NewParser(filePath string) (*Parser, error) {\n\tf, err := os.Open(filePath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\tl := newLexer(bufio.NewReader(f))\n\tl.file = filePath\n\tp := NewParserFromLexer(l)\n\treturn p, nil\n}\n\nfunc NewParserFromLexer(lexer *lexer) *Parser {\n\tparser := &Parser{\n\t\tlexer: lexer,\n\t}\n\n\tparser.nextToken()\n\tparser.nextToken()\n\n\tparser.blockWrappers = map[string]func(*components.Directive) (components.IDirective, error){\n\t\t\"http\": func(directive *components.Directive) (components.IDirective, error) {\n\t\t\treturn parser.wrapHttp(directive), nil\n\t\t},\n\t\t\"server\": func(directive *components.Directive) (components.IDirective, error) {\n\t\t\treturn parser.wrapServer(directive), nil\n\t\t},\n\t\t\"location\": func(directive *components.Directive) (components.IDirective, error) {\n\t\t\treturn parser.wrapLocation(directive), nil\n\t\t},\n\t\t\"upstream\": func(directive *components.Directive) (components.IDirective, error) {\n\t\t\treturn parser.wrapUpstream(directive), nil\n\t\t},\n\t\t\"_by_lua_block\": func(directive *components.Directive) (components.IDirective, error) {\n\t\t\treturn parser.wrapLuaBlock(directive)\n\t\t},\n\t}\n\n\tparser.directiveWrappers = map[string]func(*components.Directive) components.IDirective{\n\t\t\"server\": func(directive *components.Directive) components.IDirective {\n\t\t\treturn parser.parseUpstreamServer(directive)\n\t\t},\n\t}\n\n\treturn parser\n}\n\nfunc (p *Parser) nextToken() {\n\tp.currentToken = p.followingToken\n\tp.followingToken = p.lexer.scan()\n}\n\nfunc (p *Parser) curTokenIs(t flag.Type) bool {\n\treturn p.currentToken.Type == t\n}\n\nfunc (p *Parser) followingTokenIs(t flag.Type) bool {\n\treturn p.followingToken.Type == t\n}\n\nfunc (p *Parser) Parse() (*components.Config, error) {\n\tparsedBlock, err := p.parseBlock(false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc := &components.Config{\n\t\tFilePath: p.lexer.file,\n\t\tBlock:    parsedBlock,\n\t}\n\treturn c, err\n}\n\nfunc (p *Parser) parseBlock(inBlock bool) (*components.Block, error) {\n\tcontext := &components.Block{\n\t\tComment:    \"\",\n\t\tDirectives: make([]components.IDirective, 0),\n\t\tLine:       p.currentToken.Line,\n\t}\n\nparsingloop:\n\tfor {\n\t\tswitch {\n\t\tcase p.curTokenIs(flag.EOF):\n\t\t\tif inBlock {\n\t\t\t\treturn nil, errors.New(\"unexpected eof in block\")\n\t\t\t}\n\t\t\tbreak parsingloop\n\t\tcase p.curTokenIs(flag.BlockEnd):\n\t\t\tbreak parsingloop\n\t\tcase p.curTokenIs(flag.LuaCode):\n\t\t\tcontext.IsLuaBlock = true\n\t\t\tcontext.LiteralCode = p.currentToken.Literal\n\t\tcase p.curTokenIs(flag.Keyword) || p.curTokenIs(flag.QuotedString):\n\t\t\ts, err := p.parseStatement()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcontext.Directives = append(context.Directives, s)\n\t\tcase p.curTokenIs(flag.Comment):\n\t\t\tcontext.Directives = append(context.Directives, &components.Comment{\n\t\t\t\tDetail: p.currentToken.Literal,\n\t\t\t\tLine:   p.currentToken.Line,\n\t\t\t})\n\t\t}\n\t\tp.nextToken()\n\t}\n\n\treturn context, nil\n}\n\nfunc (p *Parser) parseStatement() (components.IDirective, error) {\n\td := &components.Directive{\n\t\tName: p.currentToken.Literal,\n\t\tLine: p.currentToken.Line,\n\t}\n\n\tfor p.nextToken(); p.currentToken.IsParameterEligible(); p.nextToken() {\n\t\td.Parameters = append(d.Parameters, p.currentToken.Literal)\n\t}\n\n\tif p.curTokenIs(flag.Semicolon) {\n\t\tif dw, ok := p.directiveWrappers[d.Name]; ok {\n\t\t\treturn dw(d), nil\n\t\t}\n\t\tif p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {\n\t\t\td.Comment = p.followingToken.Literal\n\t\t\tp.nextToken()\n\t\t}\n\t\treturn d, nil\n\t}\n\n\tif p.curTokenIs(flag.BlockStart) {\n\t\tinLineComment := \"\"\n\t\tif p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {\n\t\t\tinLineComment = p.followingToken.Literal\n\t\t\tp.nextToken()\n\t\t\tp.nextToken()\n\t\t}\n\t\tblock, err := p.parseBlock(false)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tblock.Comment = inLineComment\n\t\td.Block = block\n\n\t\tif strings.HasSuffix(d.Name, \"_by_lua_block\") {\n\t\t\treturn p.blockWrappers[\"_by_lua_block\"](d)\n\t\t}\n\n\t\tif bw, ok := p.blockWrappers[d.Name]; ok {\n\t\t\treturn bw(d)\n\t\t}\n\t\treturn d, nil\n\t}\n\n\tpanic(fmt.Errorf(\"unexpected token %s (%s) on line %d, column %d\", p.currentToken.Type.String(), p.currentToken.Literal, p.currentToken.Line, p.currentToken.Column))\n}\n\nfunc (p *Parser) wrapLocation(directive *components.Directive) *components.Location {\n\treturn components.NewLocation(directive)\n}\n\nfunc (p *Parser) wrapServer(directive *components.Directive) *components.Server {\n\ts, _ := components.NewServer(directive)\n\treturn s\n}\n\nfunc (p *Parser) wrapUpstream(directive *components.Directive) *components.Upstream {\n\ts, _ := components.NewUpstream(directive)\n\treturn s\n}\n\nfunc (p *Parser) wrapHttp(directive *components.Directive) *components.Http {\n\th, _ := components.NewHttp(directive)\n\treturn h\n}\n\nfunc (p *Parser) wrapLuaBlock(directive *components.Directive) (*components.LuaBlock, error) {\n\treturn components.NewLuaBlock(directive)\n}\n\nfunc (p *Parser) parseUpstreamServer(directive *components.Directive) *components.UpstreamServer {\n\treturn components.NewUpstreamServer(directive)\n}\n"
  },
  {
    "path": "agent/utils/ntp/ntp.go",
    "content": "package ntp\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"net\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n)\n\nconst ntpEpochOffset = 2208988800\n\ntype packet struct {\n\tSettings       uint8\n\tStratum        uint8\n\tPoll           int8\n\tPrecision      int8\n\tRootDelay      uint32\n\tRootDispersion uint32\n\tReferenceID    uint32\n\tRefTimeSec     uint32\n\tRefTimeFrac    uint32\n\tOrigTimeSec    uint32\n\tOrigTimeFrac   uint32\n\tRxTimeSec      uint32\n\tRxTimeFrac     uint32\n\tTxTimeSec      uint32\n\tTxTimeFrac     uint32\n}\n\nfunc GetRemoteTime(site string) (time.Time, error) {\n\tconn, err := net.Dial(\"udp\", site+\":123\")\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"failed to connect: %v\", err)\n\t}\n\tdefer conn.Close()\n\tif err := conn.SetDeadline(time.Now().Add(15 * time.Second)); err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"failed to set deadline: %v\", err)\n\t}\n\n\treq := &packet{Settings: 0x1B}\n\n\tif err := binary.Write(conn, binary.BigEndian, req); err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"failed to set request: %v\", err)\n\t}\n\n\trsp := &packet{}\n\tif err := binary.Read(conn, binary.BigEndian, rsp); err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"failed to read server response: %v\", err)\n\t}\n\n\tsecs := float64(rsp.TxTimeSec) - ntpEpochOffset\n\tnanos := (int64(rsp.TxTimeFrac) * 1e9) >> 32\n\n\tshowtime := time.Unix(int64(secs), nanos)\n\n\treturn showtime, nil\n}\n\nfunc UpdateSystemTime(dateTime string) error {\n\tsystem := runtime.GOOS\n\tif system == \"linux\" {\n\t\tif err := cmd.RunDefaultBashCf(`%s date -s \"%s\"`, cmd.SudoHandleCmd(), dateTime); err != nil {\n\t\t\treturn fmt.Errorf(\"update system time failed, %v\", err)\n\t\t}\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"the current system architecture %v does not support synchronization\", system)\n}\n\nfunc UpdateSystemTimeZone(timezone string) error {\n\tsystem := runtime.GOOS\n\tif system == \"linux\" {\n\t\tif err := cmd.RunDefaultBashCf(`%s timedatectl set-timezone \"%s\"`, cmd.SudoHandleCmd(), timezone); err != nil {\n\t\t\treturn fmt.Errorf(\"update system time zone failed, %v\", err)\n\t\t}\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"the current system architecture %v does not support synchronization\", system)\n}\n"
  },
  {
    "path": "agent/utils/openclaw/caddyfile.go",
    "content": "package openclaw\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n)\n\nconst (\n\tgatewayPort   = 18789\n\tcaddyPort     = 8443\n\tcaddyDataPerm = 0o777\n)\n\nfunc BuildCatchAllCaddyfile() string {\n\treturn fmt.Sprintf(`{\n    admin off\n    auto_https disable_redirects\n    skip_install_trust\n    storage file_system {\n        root /data/caddy\n    }\n}\n\nhttps://:%d {\n    bind 0.0.0.0\n    tls internal {\n        on_demand\n    }\n    reverse_proxy 127.0.0.1:%d\n}\n`, caddyPort, gatewayPort)\n}\n\nfunc WriteCatchAllCaddyfile(installPath string) error {\n\tcaddyDir := path.Join(installPath, \"data\", \"caddy\")\n\tcaddyDataDir := path.Join(caddyDir, \"data\")\n\tif err := os.MkdirAll(caddyDataDir, constant.DirPerm); err != nil {\n\t\treturn err\n\t}\n\tif err := os.Chmod(caddyDataDir, caddyDataPerm); err != nil {\n\t\treturn err\n\t}\n\treturn os.WriteFile(path.Join(caddyDir, \"Caddyfile\"), []byte(BuildCatchAllCaddyfile()), constant.FilePerm)\n}\n"
  },
  {
    "path": "agent/utils/postgresql/client/info.go",
    "content": "package client\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/agent/app/task\"\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n)\n\ntype DBInfo struct {\n\tFrom      string `json:\"from\"`\n\tDatabase  string `json:\"database\"`\n\tAddress   string `json:\"address\"`\n\tPort      uint   `json:\"port\"`\n\tInitialDB string `json:\"initialDB\"`\n\tUsername  string `json:\"userName\"`\n\tPassword  string `json:\"password\"`\n\tAppKey    string `json:\"appKey\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype CreateInfo struct {\n\tName      string `json:\"name\"`\n\tUsername  string `json:\"userName\"`\n\tPassword  string `json:\"password\"`\n\tSuperUser bool   `json:\"superUser\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype Privileges struct {\n\tUsername  string `json:\"userName\"`\n\tSuperUser bool   `json:\"superUser\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype DeleteInfo struct {\n\tName     string `json:\"name\"`\n\tUsername string `json:\"userName\"`\n\n\tForceDelete bool `json:\"forceDelete\"`\n\tTimeout     uint `json:\"timeout\"` // second\n}\n\ntype PasswordChangeInfo struct {\n\tUsername string `json:\"userName\"`\n\tPassword string `json:\"password\"`\n\n\tTimeout uint `json:\"timeout\"` // second\n}\n\ntype BackupInfo struct {\n\tDatabase  string `json:\"database\"`\n\tName      string `json:\"name\"`\n\tTargetDir string `json:\"targetDir\"`\n\tFileName  string `json:\"fileName\"`\n\n\tTask    *task.Task `json:\"-\"`\n\tTimeout uint       `json:\"timeout\"` // second\n}\n\ntype RecoverInfo struct {\n\tDatabase   string `json:\"database\"`\n\tName       string `json:\"name\"`\n\tSourceFile string `json:\"sourceFile\"`\n\tUsername   string `json:\"username\"`\n\n\tTask    *task.Task `json:\"-\"`\n\tTimeout uint       `json:\"timeout\"` // second\n}\n\ntype SyncDBInfo struct {\n\tName           string `json:\"name\"`\n\tFrom           string `json:\"from\"`\n\tPostgresqlName string `json:\"postgresqlName\"`\n}\ntype Status struct {\n\tUptime              string `json:\"uptime\"`\n\tVersion             string `json:\"version\"`\n\tMaxConnections      string `json:\"max_connections\"`\n\tAutovacuum          string `json:\"autovacuum\"`\n\tCurrentConnections  string `json:\"current_connections\"`\n\tHitRatio            string `json:\"hit_ratio\"`\n\tSharedBuffers       string `json:\"shared_buffers\"`\n\tBuffersClean        string `json:\"buffers_clean\"`\n\tMaxwrittenClean     string `json:\"maxwritten_clean\"`\n\tBuffersBackendFsync string `json:\"buffers_backend_fsync\"`\n}\n"
  },
  {
    "path": "agent/utils/postgresql/client/local.go",
    "content": "package client\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n)\n\ntype Local struct {\n\tPrefixCommand []string\n\tDatabase      string\n\tUsername      string\n\tPassword      string\n\tContainerName string\n}\n\nfunc NewLocal(command []string, containerName, username, password, database string) *Local {\n\treturn &Local{PrefixCommand: command, ContainerName: containerName, Username: username, Password: password, Database: database}\n}\n\nfunc (r *Local) Create(info CreateInfo) error {\n\tcreateSql := fmt.Sprintf(\"CREATE DATABASE \\\"%s\\\"\", info.Name)\n\tif err := r.ExecSQL(createSql, info.Timeout); err != nil {\n\t\tif strings.Contains(strings.ToLower(err.Error()), \"already exists\") {\n\t\t\treturn buserr.New(\"ErrDatabaseIsExist\")\n\t\t}\n\t\treturn err\n\t}\n\n\tif err := r.CreateUser(info, true); err != nil {\n\t\t_ = r.ExecSQL(fmt.Sprintf(\"DROP DATABASE \\\"%s\\\"\", info.Name), info.Timeout)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (r *Local) ChangePrivileges(info Privileges) error {\n\tsuper := \"SUPERUSER\"\n\tif !info.SuperUser {\n\t\tsuper = \"NOSUPERUSER\"\n\t}\n\tchangeSql := fmt.Sprintf(\"ALTER USER \\\"%s\\\" WITH %s\", info.Username, super)\n\treturn r.ExecSQL(changeSql, info.Timeout)\n}\n\nfunc (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error {\n\tcreateSql := fmt.Sprintf(\"CREATE USER \\\"%s\\\" WITH PASSWORD '%s'\", info.Username, info.Password)\n\tif err := r.ExecSQL(createSql, info.Timeout); err != nil {\n\t\tif strings.Contains(strings.ToLower(err.Error()), \"already exists\") {\n\t\t\treturn buserr.New(\"ErrUserIsExist\")\n\t\t}\n\t\tif withDeleteDB {\n\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\tName:        info.Name,\n\t\t\t\tUsername:    info.Username,\n\t\t\t\tForceDelete: true,\n\t\t\t\tTimeout:     300})\n\t\t}\n\t\treturn err\n\t}\n\tif info.SuperUser {\n\t\tif err := r.ChangePrivileges(Privileges{SuperUser: true, Username: info.Username, Timeout: info.Timeout}); err != nil {\n\t\t\tif withDeleteDB {\n\t\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\t\tName:        info.Name,\n\t\t\t\t\tUsername:    info.Username,\n\t\t\t\t\tForceDelete: true,\n\t\t\t\t\tTimeout:     300})\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\tgrantStr := fmt.Sprintf(\"GRANT ALL PRIVILEGES ON DATABASE \\\"%s\\\" TO \\\"%s\\\"\", info.Name, info.Username)\n\tif err := r.ExecSQL(grantStr, info.Timeout); err != nil {\n\t\tif withDeleteDB {\n\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\tName:        info.Name,\n\t\t\t\tUsername:    info.Username,\n\t\t\t\tForceDelete: true,\n\t\t\t\tTimeout:     300})\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (r *Local) Delete(info DeleteInfo) error {\n\tif len(info.Name) != 0 {\n\t\tinUse, err := r.isDatabaseInUse(info.Name, info.Timeout)\n\t\tif err != nil && !info.ForceDelete {\n\t\t\treturn fmt.Errorf(\"check database connections failed, err: %v\", err)\n\t\t}\n\t\tif inUse && !info.ForceDelete {\n\t\t\treturn buserr.WithDetail(\"ErrInUsed\", info.Name, nil)\n\t\t}\n\t\tdropSql := fmt.Sprintf(\"DROP DATABASE \\\"%s\\\"\", info.Name)\n\t\tif err := r.ExecSQL(dropSql, info.Timeout); err != nil && !info.ForceDelete {\n\t\t\treturn fmt.Errorf(\"drop database failed, err: %v\", err)\n\t\t}\n\t}\n\tdropSql := fmt.Sprintf(\"DROP USER \\\"%s\\\"\", info.Username)\n\tif err := r.ExecSQL(dropSql, info.Timeout); err != nil && !info.ForceDelete {\n\t\treturn fmt.Errorf(\"drop user failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (r *Local) ChangePassword(info PasswordChangeInfo) error {\n\tchangeSql := fmt.Sprintf(\"ALTER USER \\\"%s\\\" WITH PASSWORD '%s'\", info.Username, info.Password)\n\tif err := r.ExecSQL(changeSql, info.Timeout); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (r *Local) Backup(info BackupInfo) error {\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(info.TargetDir) {\n\t\tif err := os.MkdirAll(info.TargetDir, os.ModePerm); err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", info.TargetDir, err)\n\t\t}\n\t}\n\toutfile, err := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, constant.DirPerm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"open file %s failed, err: %v\", path.Join(info.TargetDir, info.FileName), err)\n\t}\n\tdefer outfile.Close()\n\tglobal.LOG.Infof(\"start to pg_dump | gzip > %s.gzip\", info.TargetDir+\"/\"+info.FileName)\n\n\tcmd := exec.Command(\"docker\", \"exec\", \"-i\", r.ContainerName,\n\t\t\"sh\", \"-c\",\n\t\tfmt.Sprintf(\"PGPASSWORD=%s pg_dump -F c -U %s -d %s\", r.Password, r.Username, info.Name),\n\t)\n\tvar stderr bytes.Buffer\n\tcmd.Stderr = &stderr\n\n\tgzipCmd := exec.Command(\"gzip\", \"-cf\")\n\tgzipCmd.Stdin, _ = cmd.StdoutPipe()\n\tgzipCmd.Stdout = outfile\n\t_ = gzipCmd.Start()\n\n\tif err := cmd.Run(); err != nil {\n\t\treturn fmt.Errorf(\"handle backup database failed, err: %v\", stderr.String())\n\t}\n\t_ = gzipCmd.Wait()\n\treturn nil\n}\n\nfunc (r *Local) Recover(info RecoverInfo) error {\n\tfi, _ := os.Open(info.SourceFile)\n\tdefer fi.Close()\n\n\tcmd := exec.Command(\"docker\", \"exec\", \"-i\", r.ContainerName, \"sh\", \"-c\",\n\t\tfmt.Sprintf(\"PGPASSWORD=%s pg_restore -F c -c --if-exists --no-owner -U %s -d %s\", r.Password, r.Username, info.Name),\n\t)\n\tif strings.HasSuffix(info.SourceFile, \".gz\") {\n\t\tgzipFile, err := os.Open(info.SourceFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer gzipFile.Close()\n\t\tgzipReader, err := gzip.NewReader(gzipFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer gzipReader.Close()\n\t\tcmd.Stdin = gzipReader\n\t} else {\n\t\tcmd.Stdin = fi\n\t}\n\tstdout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\tif strings.HasPrefix(string(stdout), \"ERROR \") {\n\t\t\treturn errors.New(string(stdout))\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (r *Local) SyncDB() ([]SyncDBInfo, error) {\n\tvar datas []SyncDBInfo\n\tlines, err := r.ExecSQLForRows(\"SELECT datname FROM pg_database\", 300)\n\tif err != nil {\n\t\treturn datas, err\n\t}\n\tfor _, line := range lines {\n\t\titemLine := strings.TrimLeft(line, \" \")\n\t\tif len(itemLine) == 0 || itemLine == \"template1\" || itemLine == \"template0\" || itemLine == r.Username {\n\t\t\tcontinue\n\t\t}\n\t\tdatas = append(datas, SyncDBInfo{Name: itemLine, From: \"local\", PostgresqlName: r.Database})\n\t}\n\treturn datas, nil\n}\n\nfunc (r *Local) Close() {}\n\nfunc (r *Local) isDatabaseInUse(name string, timeout uint) (bool, error) {\n\tescapedName := strings.ReplaceAll(name, \"'\", \"''\")\n\tcheckSQL := fmt.Sprintf(\n\t\t\"SELECT COUNT(*) FROM pg_stat_activity WHERE datname='%s' AND pid <> pg_backend_pid()\",\n\t\tescapedName,\n\t)\n\tlines, err := r.ExecSQLForRows(checkSQL, timeout)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tfor _, line := range lines {\n\t\tcountStr := strings.TrimSpace(line)\n\t\tif len(countStr) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tcount, parseErr := strconv.Atoi(countStr)\n\t\tif parseErr != nil {\n\t\t\treturn false, parseErr\n\t\t}\n\t\treturn count > 0, nil\n\t}\n\treturn false, nil\n}\n\nfunc (r *Local) ExecSQL(command string, timeout uint) error {\n\titemCommand := r.PrefixCommand[:]\n\titemCommand = append(itemCommand, command)\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\tcmd := exec.CommandContext(ctx, \"docker\", itemCommand...)\n\tstdout, err := cmd.CombinedOutput()\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn buserr.New(\"ErrExecTimeOut\")\n\t}\n\tif err != nil || strings.HasPrefix(string(stdout), \"ERROR \") {\n\t\treturn errors.New(string(stdout))\n\t}\n\treturn nil\n}\n\nfunc (r *Local) ExecSQLForRows(command string, timeout uint) ([]string, error) {\n\titemCommand := r.PrefixCommand[:]\n\titemCommand = append(itemCommand, command)\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\tcmd := exec.CommandContext(ctx, \"docker\", itemCommand...)\n\tstdout, err := cmd.CombinedOutput()\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn nil, buserr.New(\"ErrExecTimeOut\")\n\t}\n\tif err != nil || strings.HasPrefix(string(stdout), \"ERROR \") {\n\t\treturn nil, errors.New(string(stdout))\n\t}\n\treturn strings.Split(string(stdout), \"\\n\"), nil\n}\n"
  },
  {
    "path": "agent/utils/postgresql/client/remote.go",
    "content": "package client\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/i18n\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/docker/docker/api/types/image\"\n\t\"github.com/pkg/errors\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/docker\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n)\n\ntype Remote struct {\n\tClient   *sql.DB\n\tFrom     string\n\tDatabase string\n\tUser     string\n\tPassword string\n\tAddress  string\n\tPort     uint\n}\n\nfunc NewRemote(db Remote) *Remote {\n\treturn &db\n}\nfunc (r *Remote) Create(info CreateInfo) error {\n\tcreateSql := fmt.Sprintf(\"CREATE DATABASE \\\"%s\\\"\", info.Name)\n\tif err := r.ExecSQL(createSql, info.Timeout); err != nil {\n\t\tif strings.Contains(strings.ToLower(err.Error()), \"already exists\") {\n\t\t\treturn buserr.New(\"ErrDatabaseIsExist\")\n\t\t}\n\t\treturn err\n\t}\n\tif err := r.CreateUser(info, true); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (r *Remote) CreateUser(info CreateInfo, withDeleteDB bool) error {\n\tcreateSql := fmt.Sprintf(\"CREATE USER \\\"%s\\\" WITH PASSWORD '%s'\", info.Username, info.Password)\n\tif err := r.ExecSQL(createSql, info.Timeout); err != nil {\n\t\tif strings.Contains(strings.ToLower(err.Error()), \"already exists\") {\n\t\t\treturn buserr.New(\"ErrUserIsExist\")\n\t\t}\n\t\tif withDeleteDB {\n\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\tName:        info.Name,\n\t\t\t\tUsername:    info.Username,\n\t\t\t\tForceDelete: true,\n\t\t\t\tTimeout:     300})\n\t\t}\n\t\treturn err\n\t}\n\tif info.SuperUser {\n\t\tif err := r.ChangePrivileges(Privileges{SuperUser: true, Username: info.Username, Timeout: info.Timeout}); err != nil {\n\t\t\tif withDeleteDB {\n\t\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\t\tName:        info.Name,\n\t\t\t\t\tUsername:    info.Username,\n\t\t\t\t\tForceDelete: true,\n\t\t\t\t\tTimeout:     300})\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\tgrantSql := fmt.Sprintf(\"GRANT ALL PRIVILEGES ON DATABASE \\\"%s\\\" TO \\\"%s\\\"\", info.Name, info.Username)\n\tif err := r.ExecSQL(grantSql, info.Timeout); err != nil {\n\t\tif withDeleteDB {\n\t\t\t_ = r.Delete(DeleteInfo{\n\t\t\t\tName:        info.Name,\n\t\t\t\tUsername:    info.Username,\n\t\t\t\tForceDelete: true,\n\t\t\t\tTimeout:     300})\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (r *Remote) Delete(info DeleteInfo) error {\n\tif len(info.Name) != 0 {\n\t\tinUse, err := r.isDatabaseInUse(info.Name, info.Timeout)\n\t\tif err != nil && !info.ForceDelete {\n\t\t\treturn fmt.Errorf(\"check database connections failed, err: %v\", err)\n\t\t}\n\t\tif inUse && !info.ForceDelete {\n\t\t\treturn buserr.WithDetail(\"ErrInUsed\", info.Name, nil)\n\t\t}\n\t\tdropSql := fmt.Sprintf(\"DROP DATABASE \\\"%s\\\"\", info.Name)\n\t\tif err := r.ExecSQL(dropSql, info.Timeout); err != nil && !info.ForceDelete {\n\t\t\treturn fmt.Errorf(\"drop database failed, err: %v\", err)\n\t\t}\n\t}\n\tdropSql := fmt.Sprintf(\"DROP USER \\\"%s\\\"\", info.Username)\n\tif err := r.ExecSQL(dropSql, info.Timeout); err != nil && !info.ForceDelete {\n\t\treturn fmt.Errorf(\"drop user failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (r *Remote) isDatabaseInUse(name string, timeout uint) (bool, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\n\tvar count int\n\tif err := r.Client.QueryRowContext(\n\t\tctx,\n\t\t\"SELECT COUNT(*) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()\",\n\t\tname,\n\t).Scan(&count); err != nil {\n\t\treturn false, err\n\t}\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn false, buserr.New(\"ErrExecTimeOut\")\n\t}\n\treturn count > 0, nil\n}\n\nfunc (r *Remote) ChangePrivileges(info Privileges) error {\n\tsuper := \"SUPERUSER\"\n\tif !info.SuperUser {\n\t\tsuper = \"NOSUPERUSER\"\n\t}\n\treturn r.ExecSQL(fmt.Sprintf(\"ALTER USER \\\"%s\\\" WITH %s\", info.Username, super), info.Timeout)\n}\n\nfunc (r *Remote) ChangePassword(info PasswordChangeInfo) error {\n\treturn r.ExecSQL(fmt.Sprintf(\"ALTER USER \\\"%s\\\" WITH ENCRYPTED PASSWORD '%s'\", info.Username, info.Password), info.Timeout)\n}\n\nfunc (r *Remote) Backup(info BackupInfo) error {\n\tif cmd.CheckIllegal(r.Password, r.Address, r.User, info.Name) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\timageTag, err := loadImageTag(info.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo.Task.Log(i18n.GetWithName(\"RemoteBackup\", imageTag))\n\tfileOp := files.NewFileOp()\n\tif !fileOp.Stat(info.TargetDir) {\n\t\tif err := os.MkdirAll(info.TargetDir, os.ModePerm); err != nil {\n\t\t\treturn fmt.Errorf(\"mkdir %s failed, err: %v\", info.TargetDir, err)\n\t\t}\n\t}\n\tfileNameItem := info.TargetDir + \"/\" + strings.TrimSuffix(info.FileName, \".gz\")\n\tbackupCommand := exec.Command(\"bash\", \"-c\",\n\t\tfmt.Sprintf(\"docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD='\\\\''%s'\\\\'' pg_dump  -h %s -p %d --no-owner -Fc -U %s %s' > %s\",\n\t\t\timageTag, r.Password, r.Address, r.Port, r.User, info.Name, fileNameItem))\n\t_ = backupCommand.Run()\n\tb := make([]byte, 5)\n\tn := []byte{80, 71, 68, 77, 80}\n\thandle, err := os.OpenFile(fileNameItem, os.O_RDONLY, os.ModePerm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"backup file not found,err:%v\", err)\n\t}\n\tdefer handle.Close()\n\t_, _ = handle.Read(b)\n\tif string(b) != string(n) {\n\t\terrBytes, _ := os.ReadFile(fileNameItem)\n\t\treturn fmt.Errorf(\"backup failed, err: %s\", string(errBytes))\n\t}\n\n\tgzipCmd := exec.Command(\"gzip\", fileNameItem)\n\tstdout, err := gzipCmd.CombinedOutput()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"gzip file %s failed, stdout: %v, err: %v\", strings.TrimSuffix(info.FileName, \".gz\"), string(stdout), err)\n\t}\n\treturn nil\n}\n\nfunc (r *Remote) Recover(info RecoverInfo) error {\n\tif cmd.CheckIllegal(r.Password, r.Address, r.User, info.Name, info.Username) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\timageTag, err := loadImageTag(info.Database)\n\tif err != nil {\n\t\treturn err\n\t}\n\tinfo.Task.Log(i18n.GetWithName(\"RemoteRecover\", imageTag))\n\tfileName := info.SourceFile\n\tif strings.HasSuffix(info.SourceFile, \".sql.gz\") {\n\t\tfileName = strings.TrimSuffix(info.SourceFile, \".gz\")\n\t\tgzipCmd := exec.Command(\"gunzip\", info.SourceFile)\n\t\tstdout, err := gzipCmd.CombinedOutput()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"gunzip file %s failed, stdout: %v, err: %v\", info.SourceFile, string(stdout), err)\n\t\t}\n\t\tdefer func() {\n\t\t\tgzipCmd := exec.Command(\"gzip\", fileName)\n\t\t\t_, _ = gzipCmd.CombinedOutput()\n\t\t}()\n\t}\n\trecoverCommand := exec.Command(\"bash\", \"-c\",\n\t\tfmt.Sprintf(\"docker run --rm --net=host -i %s /bin/bash -c 'PGPASSWORD='\\\\''%s'\\\\'' pg_restore -h %s -p %d --verbose --clean --no-privileges --no-owner -Fc -c  --if-exists --no-owner -U %s -d %s --role=%s' < %s\",\n\t\t\timageTag, r.Password, r.Address, r.Port, r.User, info.Name, info.Username, fileName))\n\tpipe, _ := recoverCommand.StdoutPipe()\n\tstderrPipe, _ := recoverCommand.StderrPipe()\n\tdefer pipe.Close()\n\tdefer stderrPipe.Close()\n\tif err := recoverCommand.Start(); err != nil {\n\t\treturn err\n\t}\n\treader := bufio.NewReader(pipe)\n\tfor {\n\t\treadString, err := reader.ReadString('\\n')\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\tall, _ := io.ReadAll(stderrPipe)\n\t\t\tglobal.LOG.Errorf(\"[PostgreSQL] DB:[%s] Recover Error: %s\", info.Name, string(all))\n\t\t\treturn err\n\t\t}\n\t\tglobal.LOG.Infof(\"[PostgreSQL] DB:[%s] Restoring: %s\", info.Name, readString)\n\t}\n\n\treturn nil\n}\n\nfunc (r *Remote) SyncDB() ([]SyncDBInfo, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), 300*time.Second)\n\tdefer cancel()\n\n\tvar datas []SyncDBInfo\n\trows, err := r.Client.Query(\"SELECT datname FROM pg_database;\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\tfor rows.Next() {\n\t\tvar dbName string\n\t\tif err := rows.Scan(&dbName); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif len(dbName) == 0 || dbName == \"template1\" || dbName == \"template0\" || dbName == r.User {\n\t\t\tcontinue\n\t\t}\n\t\tdatas = append(datas, SyncDBInfo{Name: dbName, From: r.From, PostgresqlName: r.Database})\n\t}\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn nil, buserr.New(\"ErrExecTimeOut\")\n\t}\n\treturn datas, nil\n}\n\nfunc (r *Remote) Close() {\n\t_ = r.Client.Close()\n}\n\nfunc (r *Remote) ExecSQL(command string, timeout uint) error {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\n\tif _, err := r.Client.ExecContext(ctx, command); err != nil {\n\t\treturn err\n\t}\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn buserr.New(\"ErrExecTimeOut\")\n\t}\n\n\treturn nil\n}\n\nfunc loadImageTag(database string) (string, error) {\n\tvar db model.Database\n\tif err := global.DB.Model(&model.Database{}).Where(\"name = ?\", database).First(&db).Error; err != nil {\n\t\treturn \"\", fmt.Errorf(\"load database %s info failed, err: %v\", database, err)\n\t}\n\n\tclient, err := docker.NewDockerClient()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create docker client failed, err: %v\", err)\n\t}\n\tdefer client.Close()\n\timages, _ := client.ImageList(context.Background(), image.ListOptions{})\n\tfor _, image := range images {\n\t\tfor _, tag := range image.RepoTags {\n\t\t\tif strings.HasPrefix(tag, \"postgres:\"+strings.TrimSuffix(db.Version, \"x\")) {\n\t\t\t\treturn tag, nil\n\t\t\t}\n\t\t}\n\t}\n\treturn \"postgres:\" + strings.ReplaceAll(db.Version, \".x\", \"-alpine\"), nil\n}\n"
  },
  {
    "path": "agent/utils/postgresql/client.go",
    "content": "package postgresql\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client\"\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n)\n\ntype PostgresqlClient interface {\n\tCreate(info client.CreateInfo) error\n\tCreateUser(info client.CreateInfo, withDeleteDB bool) error\n\tDelete(info client.DeleteInfo) error\n\tChangePrivileges(info client.Privileges) error\n\tChangePassword(info client.PasswordChangeInfo) error\n\n\tBackup(info client.BackupInfo) error\n\tRecover(info client.RecoverInfo) error\n\tSyncDB() ([]client.SyncDBInfo, error)\n\tClose()\n}\n\nfunc NewPostgresqlClient(conn client.DBInfo) (PostgresqlClient, error) {\n\tif conn.From == \"local\" {\n\t\tconnArgs := []string{\"exec\", \"-e\", fmt.Sprintf(\"PGPASSWORD=%s\", conn.Password), conn.Address, \"psql\", \"-t\", \"-U\", conn.Username, \"-c\"}\n\t\treturn client.NewLocal(connArgs, conn.Address, conn.Username, conn.Password, conn.Database), nil\n\t}\n\tescapedUsername := url.QueryEscape(conn.Username)\n\tescapedPassword := url.QueryEscape(conn.Password)\n\tif len(conn.InitialDB) == 0 {\n\t\tconn.InitialDB = escapedUsername\n\t}\n\tconnArgs := fmt.Sprintf(\"postgres://%s:%s@%s:%d/%s?sslmode=disable\", escapedUsername, escapedPassword, conn.Address, conn.Port, conn.InitialDB)\n\tdb, err := sql.Open(\"pgx\", connArgs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(conn.Timeout)*time.Second)\n\tdefer cancel()\n\tif err := db.PingContext(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\tif errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\treturn nil, buserr.New(\"ErrExecTimeOut\")\n\t}\n\n\treturn client.NewRemote(client.Remote{\n\t\tClient:   db,\n\t\tFrom:     \"remote\",\n\t\tDatabase: conn.Database,\n\t\tUser:     conn.Username,\n\t\tPassword: conn.Password,\n\t\tAddress:  conn.Address,\n\t\tPort:     conn.Port,\n\t}), nil\n}\n"
  },
  {
    "path": "agent/utils/psutil/cpu.go",
    "content": "package psutil\n\nimport (\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/shirou/gopsutil/v4/cpu\"\n)\n\nconst (\n\tresetInterval = 1 * time.Minute\n\tfastInterval  = 3 * time.Second\n)\n\ntype CPUStat struct {\n\tIdle  uint64\n\tTotal uint64\n}\n\ntype CPUDetailedStat struct {\n\tUser      uint64\n\tNice      uint64\n\tSystem    uint64\n\tIdle      uint64\n\tIowait    uint64\n\tIrq       uint64\n\tSoftirq   uint64\n\tSteal     uint64\n\tGuest     uint64\n\tGuestNice uint64\n\tTotal     uint64\n}\n\ntype CPUDetailedPercent struct {\n\tUser    float64 `json:\"user\"`\n\tSystem  float64 `json:\"system\"`\n\tNice    float64 `json:\"nice\"`\n\tIdle    float64 `json:\"idle\"`\n\tIowait  float64 `json:\"iowait\"`\n\tIrq     float64 `json:\"irq\"`\n\tSoftirq float64 `json:\"softirq\"`\n\tSteal   float64 `json:\"steal\"`\n}\n\nfunc (c *CPUDetailedPercent) GetCPUDetailedPercent() []float64 {\n\treturn []float64{c.User, c.System, c.Nice, c.Idle, c.Iowait, c.Irq, c.Softirq, c.Steal}\n}\n\ntype CPUUsageState struct {\n\tmu             sync.Mutex\n\tlastTotalStat  *CPUStat\n\tlastPerCPUStat []CPUStat\n\tlastDetailStat *CPUDetailedStat\n\tlastSampleTime time.Time\n\n\tcachedTotalUsage      float64\n\tcachedPerCore         []float64\n\tcachedDetailedPercent CPUDetailedPercent\n}\n\ntype CPUInfoState struct {\n\tmu               sync.RWMutex\n\tinitialized      bool\n\tcachedInfo       []cpu.InfoStat\n\tcachedPhysCores  int\n\tcachedLogicCores int\n}\n\nfunc (c *CPUUsageState) GetCPUUsage() (float64, []float64, []float64) {\n\tc.mu.Lock()\n\n\tnow := time.Now()\n\n\tif !c.lastSampleTime.IsZero() && now.Sub(c.lastSampleTime) < fastInterval {\n\t\tresult := c.cachedTotalUsage\n\t\tperCore := c.cachedPerCore\n\t\tdetailed := c.cachedDetailedPercent\n\t\tc.mu.Unlock()\n\t\treturn result, perCore, detailed.GetCPUDetailedPercent()\n\t}\n\n\tneedReset := c.lastSampleTime.IsZero() || now.Sub(c.lastSampleTime) >= resetInterval\n\tc.mu.Unlock()\n\n\tif needReset {\n\t\tfirstTotal, firstDetail, firstPer := readAllCPUStat()\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\tsecondTotal, secondDetail, secondPer := readAllCPUStat()\n\n\t\ttotalUsage := calcCPUPercent(firstTotal, secondTotal)\n\t\tdetailedPercent := calcCPUDetailedPercent(firstDetail, secondDetail)\n\n\t\tperCore := make([]float64, len(secondPer))\n\t\tfor i := range secondPer {\n\t\t\tperCore[i] = calcCPUPercent(firstPer[i], secondPer[i])\n\t\t}\n\n\t\tc.mu.Lock()\n\t\tc.cachedTotalUsage = totalUsage\n\t\tc.cachedPerCore = perCore\n\t\tc.cachedDetailedPercent = detailedPercent\n\t\tc.lastTotalStat = &secondTotal\n\t\tc.lastDetailStat = &secondDetail\n\t\tc.lastPerCPUStat = secondPer\n\t\tc.lastSampleTime = time.Now()\n\t\tc.mu.Unlock()\n\n\t\treturn totalUsage, perCore, detailedPercent.GetCPUDetailedPercent()\n\t}\n\n\tcurTotal, curDetail, curPer := readAllCPUStat()\n\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\ttotalUsage := calcCPUPercent(*c.lastTotalStat, curTotal)\n\tdetailedPercent := calcCPUDetailedPercent(*c.lastDetailStat, curDetail)\n\n\tif len(c.cachedPerCore) != len(curPer) {\n\t\tc.cachedPerCore = make([]float64, len(curPer))\n\t}\n\tfor i := range curPer {\n\t\tc.cachedPerCore[i] = calcCPUPercent(c.lastPerCPUStat[i], curPer[i])\n\t}\n\n\tc.cachedTotalUsage = totalUsage\n\tc.cachedDetailedPercent = detailedPercent\n\tc.lastTotalStat = &curTotal\n\tc.lastDetailStat = &curDetail\n\tc.lastPerCPUStat = curPer\n\tc.lastSampleTime = time.Now()\n\n\treturn totalUsage, c.cachedPerCore, detailedPercent.GetCPUDetailedPercent()\n}\n\nfunc (c *CPUUsageState) NumCPU() int {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\treturn len(c.cachedPerCore)\n}\n\nfunc (c *CPUInfoState) GetCPUInfo(forceRefresh bool) ([]cpu.InfoStat, error) {\n\tc.mu.RLock()\n\tif c.initialized && c.cachedInfo != nil && !forceRefresh {\n\t\tdefer c.mu.RUnlock()\n\t\treturn c.cachedInfo, nil\n\t}\n\tc.mu.RUnlock()\n\n\tinfo, err := cpu.Info()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tc.mu.Lock()\n\tc.cachedInfo = info\n\tc.initialized = true\n\tc.mu.Unlock()\n\n\treturn info, nil\n}\n\nfunc (c *CPUInfoState) GetPhysicalCores(forceRefresh bool) (int, error) {\n\tc.mu.RLock()\n\tif c.initialized && c.cachedPhysCores > 0 && !forceRefresh {\n\t\tdefer c.mu.RUnlock()\n\t\treturn c.cachedPhysCores, nil\n\t}\n\tc.mu.RUnlock()\n\n\tcores, err := cpu.Counts(false)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tc.mu.Lock()\n\tc.cachedPhysCores = cores\n\tc.initialized = true\n\tc.mu.Unlock()\n\n\treturn cores, nil\n}\n\nfunc (c *CPUInfoState) GetLogicalCores(forceRefresh bool) (int, error) {\n\tc.mu.RLock()\n\tif c.initialized && c.cachedLogicCores > 0 && !forceRefresh {\n\t\tdefer c.mu.RUnlock()\n\t\treturn c.cachedLogicCores, nil\n\t}\n\tc.mu.RUnlock()\n\n\tcores, err := cpu.Counts(true)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tc.mu.Lock()\n\tc.cachedLogicCores = cores\n\tc.initialized = true\n\tc.mu.Unlock()\n\n\treturn cores, nil\n}\n\nfunc readProcStat() ([]byte, error) {\n\treturn os.ReadFile(\"/proc/stat\")\n}\n\nfunc parseCPUFields(line string) []uint64 {\n\tfields := strings.Fields(line)\n\tif len(fields) <= 1 {\n\t\treturn nil\n\t}\n\tfields = fields[1:]\n\n\tnums := make([]uint64, len(fields))\n\tfor i, f := range fields {\n\t\tv, _ := strconv.ParseUint(f, 10, 64)\n\t\tnums[i] = v\n\t}\n\treturn nums\n}\n\nfunc calcIdleAndTotal(nums []uint64) (idle, total uint64) {\n\tif len(nums) < 5 {\n\t\treturn 0, 0\n\t}\n\tidle = nums[3] + nums[4]\n\tfor _, v := range nums {\n\t\ttotal += v\n\t}\n\treturn\n}\n\nfunc readAllCPUStat() (CPUStat, CPUDetailedStat, []CPUStat) {\n\tdata, err := readProcStat()\n\tif err != nil {\n\t\treturn CPUStat{}, CPUDetailedStat{}, nil\n\t}\n\n\tlines := strings.Split(string(data), \"\\n\")\n\tif len(lines) == 0 {\n\t\treturn CPUStat{}, CPUDetailedStat{}, nil\n\t}\n\n\tfirstLine := lines[0]\n\tnums := parseCPUFields(firstLine)\n\n\tidle, total := calcIdleAndTotal(nums)\n\tcpuStat := CPUStat{Idle: idle, Total: total}\n\n\tif len(nums) < 10 {\n\t\tpadded := make([]uint64, 10)\n\t\tcopy(padded, nums)\n\t\tnums = padded\n\t}\n\tdetailedStat := CPUDetailedStat{\n\t\tUser:      nums[0],\n\t\tNice:      nums[1],\n\t\tSystem:    nums[2],\n\t\tIdle:      nums[3],\n\t\tIowait:    nums[4],\n\t\tIrq:       nums[5],\n\t\tSoftirq:   nums[6],\n\t\tSteal:     nums[7],\n\t\tGuest:     nums[8],\n\t\tGuestNice: nums[9],\n\t}\n\tdetailedStat.Total = detailedStat.User + detailedStat.Nice + detailedStat.System +\n\t\tdetailedStat.Idle + detailedStat.Iowait + detailedStat.Irq + detailedStat.Softirq + detailedStat.Steal\n\n\tvar perCPUStats []CPUStat\n\tfor _, line := range lines[1:] {\n\t\tif !strings.HasPrefix(line, \"cpu\") {\n\t\t\tcontinue\n\t\t}\n\t\tif len(line) < 4 || line[3] < '0' || line[3] > '9' {\n\t\t\tcontinue\n\t\t}\n\n\t\tperNums := parseCPUFields(line)\n\t\tperIdle, perTotal := calcIdleAndTotal(perNums)\n\t\tperCPUStats = append(perCPUStats, CPUStat{Idle: perIdle, Total: perTotal})\n\t}\n\n\treturn cpuStat, detailedStat, perCPUStats\n}\n\nfunc calcCPUPercent(prev, cur CPUStat) float64 {\n\tdeltaIdle := float64(cur.Idle - prev.Idle)\n\tdeltaTotal := float64(cur.Total - prev.Total)\n\tif deltaTotal <= 0 {\n\t\treturn 0\n\t}\n\treturn (1 - deltaIdle/deltaTotal) * 100\n}\n\nfunc calcCPUDetailedPercent(prev, cur CPUDetailedStat) CPUDetailedPercent {\n\tdeltaTotal := float64(cur.Total - prev.Total)\n\tif deltaTotal <= 0 {\n\t\treturn CPUDetailedPercent{Idle: 100}\n\t}\n\n\treturn CPUDetailedPercent{\n\t\tUser:    float64(cur.User-prev.User) / deltaTotal * 100,\n\t\tSystem:  float64(cur.System-prev.System) / deltaTotal * 100,\n\t\tNice:    float64(cur.Nice-prev.Nice) / deltaTotal * 100,\n\t\tIdle:    float64(cur.Idle-prev.Idle) / deltaTotal * 100,\n\t\tIowait:  float64(cur.Iowait-prev.Iowait) / deltaTotal * 100,\n\t\tIrq:     float64(cur.Irq-prev.Irq) / deltaTotal * 100,\n\t\tSoftirq: float64(cur.Softirq-prev.Softirq) / deltaTotal * 100,\n\t\tSteal:   float64(cur.Steal-prev.Steal) / deltaTotal * 100,\n\t}\n}\n"
  },
  {
    "path": "agent/utils/psutil/disk.go",
    "content": "package psutil\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/shirou/gopsutil/v4/disk\"\n)\n\nconst (\n\tdiskUsageCacheInterval     = 30 * time.Second\n\tdiskPartitionCacheInterval = 10 * time.Minute\n)\n\ntype DiskUsageEntry struct {\n\tlastSampleTime time.Time\n\tcachedUsage    *disk.UsageStat\n}\n\ntype DiskState struct {\n\tusageMu    sync.RWMutex\n\tusageCache map[string]*DiskUsageEntry\n\n\tpartitionMu       sync.RWMutex\n\tlastPartitionTime time.Time\n\tcachedPartitions  []disk.PartitionStat\n}\n\nfunc (d *DiskState) GetUsage(path string, forceRefresh bool) (*disk.UsageStat, error) {\n\td.usageMu.RLock()\n\tif entry, ok := d.usageCache[path]; ok {\n\t\tif time.Since(entry.lastSampleTime) < diskUsageCacheInterval && !forceRefresh {\n\t\t\tdefer d.usageMu.RUnlock()\n\t\t\treturn entry.cachedUsage, nil\n\t\t}\n\t}\n\td.usageMu.RUnlock()\n\n\tusage, err := disk.Usage(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\td.usageMu.Lock()\n\tif d.usageCache == nil {\n\t\td.usageCache = make(map[string]*DiskUsageEntry)\n\t}\n\td.usageCache[path] = &DiskUsageEntry{\n\t\tlastSampleTime: time.Now(),\n\t\tcachedUsage:    usage,\n\t}\n\td.usageMu.Unlock()\n\n\treturn usage, nil\n}\n\nfunc (d *DiskState) GetPartitions(all bool, forceRefresh bool) ([]disk.PartitionStat, error) {\n\td.partitionMu.RLock()\n\tif d.cachedPartitions != nil && time.Since(d.lastPartitionTime) < diskPartitionCacheInterval && !forceRefresh {\n\t\tdefer d.partitionMu.RUnlock()\n\t\treturn d.cachedPartitions, nil\n\t}\n\td.partitionMu.RUnlock()\n\n\tpartitions, err := disk.Partitions(all)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\td.partitionMu.Lock()\n\td.cachedPartitions = partitions\n\td.lastPartitionTime = time.Now()\n\td.partitionMu.Unlock()\n\n\treturn partitions, nil\n}\n\nfunc (d *DiskState) ClearUsageCache(path string) {\n\td.usageMu.Lock()\n\tdelete(d.usageCache, path)\n\td.usageMu.Unlock()\n}\n\nfunc (d *DiskState) ClearAllCache() {\n\td.usageMu.Lock()\n\td.usageCache = make(map[string]*DiskUsageEntry)\n\td.usageMu.Unlock()\n\n\td.partitionMu.Lock()\n\td.cachedPartitions = nil\n\td.partitionMu.Unlock()\n}\n"
  },
  {
    "path": "agent/utils/psutil/global.go",
    "content": "package psutil\n\nvar CPU = &CPUUsageState{}\nvar CPUInfo = &CPUInfoState{}\nvar HOST = &HostInfoState{}\nvar DISK = &DiskState{}\n"
  },
  {
    "path": "agent/utils/psutil/host.go",
    "content": "package psutil\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/shirou/gopsutil/v4/host\"\n)\n\nconst hostRefreshInterval = 4 * time.Hour\n\ntype HostInfoState struct {\n\tmu             sync.RWMutex\n\tlastSampleTime time.Time\n\n\tcachedInfo   *host.InfoStat\n\tcachedDistro string\n}\n\nfunc (h *HostInfoState) GetHostInfo(forceRefresh bool) (*host.InfoStat, error) {\n\th.mu.RLock()\n\tif h.cachedInfo != nil && time.Since(h.lastSampleTime) < hostRefreshInterval && !forceRefresh {\n\t\tdefer h.mu.RUnlock()\n\t\treturn h.cachedInfo, nil\n\t}\n\th.mu.RUnlock()\n\n\thostInfo, err := host.Info()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\th.mu.Lock()\n\th.cachedInfo = hostInfo\n\th.lastSampleTime = time.Now()\n\th.mu.Unlock()\n\n\treturn hostInfo, nil\n}\n\nfunc (h *HostInfoState) GetDistro() string {\n\tif h.cachedDistro == \"\" {\n\t\th.cachedDistro = detectLinuxDistro()\n\t}\n\treturn h.cachedDistro\n}\n\nfunc detectLinuxDistro() string {\n\tdistroFiles := []string{\n\t\t\"/etc/os-release\",\n\t\t\"/usr/lib/os-release\",\n\t}\n\n\tvar targetFile string\n\tfor _, f := range distroFiles {\n\t\tif _, err := os.Stat(f); err == nil {\n\t\t\ttargetFile = f\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif targetFile != \"\" {\n\t\tdata, err := os.ReadFile(targetFile)\n\t\tif err == nil {\n\t\t\tcontent := string(data)\n\t\t\tfor _, line := range strings.Split(content, \"\\n\") {\n\t\t\t\tidx := strings.Index(line, \"=\")\n\t\t\t\tif idx == -1 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tkey := line[:idx]\n\t\t\t\tif key == \"PRETTY_NAME\" {\n\t\t\t\t\td := strings.Trim(line[idx+1:], \"\\\"\")\n\t\t\t\t\tif strings.Contains(d, \"(\") && strings.Contains(d, \")\") {\n\t\t\t\t\t\td = d[:strings.LastIndex(d, \"(\")]\n\t\t\t\t\t}\n\t\t\t\t\treturn strings.TrimSpace(d)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif osInfo, err := host.Info(); err == nil {\n\t\treturn fmt.Sprintf(\"%s %s\", osInfo.Platform, osInfo.PlatformVersion)\n\t}\n\n\treturn \"Linux\"\n}\n"
  },
  {
    "path": "agent/utils/re/re.go",
    "content": "package re\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n)\n\nconst (\n\tNumberAlphaPattern                 = `(\\d+)([A-Za-z]+)`\n\tComposeDisallowedCharsPattern      = `[^a-z0-9_-]+`\n\tComposeEnvVarPattern               = `\\$\\{([^}]+)\\}`\n\tDiskKeyValuePattern                = `([A-Za-z0-9_]+)=(\"([^\"\\\\]|\\\\.)*\"|[^ \\t]+)`\n\tValidatorNamePattern               = `^[a-zA-Z\\p{Han}]{1}[a-zA-Z0-9_\\p{Han}]{0,30}$`\n\tValidatorIPPattern                 = `^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$`\n\tDomainPattern                      = `^([\\w\\p{Han}\\-\\*]{1,100}\\.){1,10}([\\w\\p{Han}\\-]{1,24}|[\\w\\p{Han}\\-]{1,24}\\.[\\w\\p{Han}\\-]{1,24})(:\\d{1,5})?$`\n\tNginxServerNamePattern             = `^(?:\\*|[\\w\\p{Han}-]{1,63})(?:\\.(?:\\*|[\\w\\p{Han}-]{1,63}))*$`\n\tProxyCacheZonePattern              = `keys_zone=proxy_cache_zone_of_[\\w.]+:(\\d+)([kmgt]?)`\n\tProxyCacheMaxSizePattern           = `max_size=([0-9.]+)([kmgt]?)`\n\tProxyCacheMaxSizeValidationPattern = `max_size=\\d+(\\.\\d+)?[kmgt]?`\n\tProxyCacheInactivePattern          = `inactive=(\\d+)([smhd])`\n\tNumberWordPattern                  = `^(\\d+)(\\w+)$`\n\tTrailingDigitsPattern              = `_(\\d+)$`\n\tAlertIPPattern                     = `from\\s+([0-9.]+)\\s+port\\s+(\\d+)`\n\tCosDualStackPattern                = `.*cos-dualstack\\..*`\n\tVersionPattern                     = `v(\\d+\\.\\d+\\.\\d+)`\n\tPhpAssignmentPattern               = `^\\s*([a-z_]+)\\s*=\\s*(.*)$`\n\tDurationWithOptionalUnitPattern    = `^(\\d+)([smhdw]?)$`\n\tMysqlGroupPattern                  = `\\[*\\]`\n\tAnsiEscapePattern                  = \"\\x1b\\\\[[0-9;?]*[A-Za-z]|\\x1b=|\\x1b>\"\n\tRecycleBinFilePattern              = `_1p_file_1p_(.+)_p_(\\d+)_(\\d+)`\n\tOrderByValidationPattern           = `^[a-zA-Z_][a-zA-Z0-9_]*$`\n\tSQLIdentifierPattern               = `^[A-Za-z_][A-Za-z0-9_]*$`\n\tNginxHostPattern                   = `^[a-zA-Z0-9.-]+(:[0-9]+)?$`\n\tNginxPathPattern                   = `^/[a-zA-Z0-9._/\\-]*$`\n)\n\nvar regexMap = make(map[string]*regexp.Regexp)\n\nfunc Init() {\n\tpatterns := []string{\n\t\tNumberAlphaPattern,\n\t\tComposeDisallowedCharsPattern,\n\t\tComposeEnvVarPattern,\n\t\tDiskKeyValuePattern,\n\t\tValidatorNamePattern,\n\t\tValidatorIPPattern,\n\t\tDomainPattern,\n\t\tNginxServerNamePattern,\n\t\tProxyCacheZonePattern,\n\t\tProxyCacheMaxSizePattern,\n\t\tProxyCacheMaxSizeValidationPattern,\n\t\tProxyCacheInactivePattern,\n\t\tNumberWordPattern,\n\t\tTrailingDigitsPattern,\n\t\tAlertIPPattern,\n\t\tCosDualStackPattern,\n\t\tVersionPattern,\n\t\tPhpAssignmentPattern,\n\t\tDurationWithOptionalUnitPattern,\n\t\tMysqlGroupPattern,\n\t\tAnsiEscapePattern,\n\t\tRecycleBinFilePattern,\n\t\tOrderByValidationPattern,\n\t\tSQLIdentifierPattern,\n\t\tNginxHostPattern,\n\t\tNginxPathPattern,\n\t}\n\n\tfor _, pattern := range patterns {\n\t\tregexMap[pattern] = regexp.MustCompile(pattern)\n\t}\n}\n\nfunc GetRegex(pattern string) *regexp.Regexp {\n\tregex, exists := regexMap[pattern]\n\tif !exists {\n\t\tpanic(fmt.Sprintf(\"regex pattern not found: %s\", pattern))\n\t}\n\treturn regex\n}\n\nfunc RegisterRegex(pattern string) {\n\tregexMap[pattern] = regexp.MustCompile(pattern)\n}\n"
  },
  {
    "path": "agent/utils/redis/redis.go",
    "content": "package redis\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/go-redis/redis\"\n)\n\ntype DBInfo struct {\n\tAddress  string `json:\"address\"`\n\tPort     uint   `json:\"port\"`\n\tPassword string `json:\"password\"`\n\tTimeout  uint   `json:\"timeout\"`\n}\n\nfunc NewRedisClient(conn DBInfo) (*redis.Client, error) {\n\tclient := redis.NewClient(&redis.Options{\n\t\tAddr:        fmt.Sprintf(\"%s:%v\", conn.Address, conn.Port),\n\t\tPassword:    conn.Password,\n\t\tDB:          0,\n\t\tDialTimeout: time.Duration(conn.Timeout) * time.Second,\n\t})\n\n\tif _, err := client.Ping().Result(); err != nil {\n\t\tglobal.LOG.Errorf(\"check redis conn failed, err: %v\", err)\n\t\treturn client, err\n\t}\n\treturn client, nil\n}\n"
  },
  {
    "path": "agent/utils/req_helper/core.go",
    "content": "package req_helper\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"net/http\"\n)\n\nfunc PostLocalCore(url string) error {\n\tvar serverPortSetting model.Setting\n\t_ = global.CoreDB.Model(&model.Setting{}).Where(\"key = ?\", \"ServerPort\").First(&serverPortSetting).Error\n\tvar sslStatusSetting model.Setting\n\t_ = global.CoreDB.Model(&model.Setting{}).Where(\"key = ?\", \"SSL\").First(&sslStatusSetting).Error\n\n\tvar prefix string\n\tif sslStatusSetting.Value == \"Disable\" {\n\t\tprefix = \"http\"\n\t} else {\n\t\tprefix = \"https\"\n\t}\n\n\treloadURL := fmt.Sprintf(\"%s://127.0.0.1:%s/api/v2%s\", prefix, serverPortSetting.Value, url)\n\treq, err := http.NewRequest(\"POST\", reloadURL, bytes.NewBuffer([]byte{}))\n\tif err != nil {\n\t\treturn err\n\t}\n\ttr := &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t}\n\tclient := &http.Client{\n\t\tTransport: tr,\n\t}\n\tdefer client.CloseIdleConnections()\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/req_helper/request.go",
    "content": "package req_helper\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/utils/xpack\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n)\n\nfunc HandleGet(url string) (*http.Response, error) {\n\tclient := &http.Client{\n\t\tTimeout: time.Second * 300,\n\t}\n\tclient.Transport = loadRequestTransport()\n\tdefer client.CloseIdleConnections()\n\n\treq, err := http.NewRequestWithContext(context.Background(), \"GET\", url, nil)\n\tif err != nil {\n\t\treturn nil, buserr.WithMap(\"ErrCreateHttpClient\", map[string]interface{}{\"err\": err.Error()}, err)\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tif errors.Is(err, context.DeadlineExceeded) {\n\t\t\treturn nil, buserr.WithMap(\"ErrHttpReqTimeOut\", map[string]interface{}{\"err\": err.Error()}, err)\n\t\t} else {\n\t\t\tif strings.Contains(err.Error(), \"no such host\") {\n\t\t\t\treturn nil, buserr.New(\"ErrNoSuchHost\")\n\t\t\t}\n\t\t\treturn nil, buserr.WithMap(\"ErrHttpReqFailed\", map[string]interface{}{\"err\": err.Error()}, err)\n\t\t}\n\t}\n\tif resp.StatusCode == 404 {\n\t\treturn nil, buserr.New(\"ErrHttpReqNotFound\")\n\t}\n\n\treturn resp, nil\n}\n\nfunc HandleRequest(url, method string, timeout int) (int, []byte, error) {\n\ttransport := xpack.LoadRequestTransport()\n\tclient := http.Client{Timeout: time.Duration(timeout) * time.Second, Transport: transport}\n\treturn HandleRequestWithClient(&client, url, method, timeout)\n}\n\nfunc HandleRequestWithClient(client *http.Client, url, method string, timeout int) (int, []byte, error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"handle request failed, error message: %v\", r)\n\t\t\treturn\n\t\t}\n\t}()\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\trequest, err := http.NewRequestWithContext(ctx, method, url, nil)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, err := client.Do(request)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn 0, nil, errors.New(resp.Status)\n\t}\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\treturn resp.StatusCode, body, nil\n}\n\ntype RequestResponse struct {\n\tStatusCode int\n\tBody       []byte\n\tHeader     http.Header\n}\n\nfunc HandleRequestWithHeaders(client *http.Client, url, method string, timeout int, reqHeaders map[string]string) (*RequestResponse, error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"handle request failed, error message: %v\", r)\n\t\t}\n\t}()\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\trequest, err := http.NewRequestWithContext(ctx, method, url, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor k, v := range reqHeaders {\n\t\trequest.Header.Set(k, v)\n\t}\n\tresp, err := client.Do(request)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &RequestResponse{\n\t\tStatusCode: resp.StatusCode,\n\t\tBody:       body,\n\t\tHeader:     resp.Header,\n\t}, nil\n}\n\nfunc RequestFile(url, method string, timeout int) (io.ReadCloser, context.CancelFunc, error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"handle request failed, error message: %v\", r)\n\t\t\treturn\n\t\t}\n\t}()\n\ttransport := xpack.LoadRequestTransport()\n\tclient := http.Client{Timeout: time.Duration(timeout) * time.Second, Transport: transport}\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\trequest, err := http.NewRequestWithContext(ctx, method, url, nil)\n\tif err != nil {\n\t\treturn nil, cancel, err\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, err := client.Do(request)\n\tif err != nil {\n\t\treturn nil, cancel, err\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, cancel, errors.New(resp.Status)\n\t}\n\treturn resp.Body, cancel, nil\n}\n\nfunc loadRequestTransport() *http.Transport {\n\treturn &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\tDialContext: (&net.Dialer{\n\t\t\tTimeout:   60 * time.Second,\n\t\t\tKeepAlive: 60 * time.Second,\n\t\t}).DialContext,\n\t\tTLSHandshakeTimeout:   5 * time.Second,\n\t\tResponseHeaderTimeout: 10 * time.Second,\n\t\tIdleConnTimeout:       15 * time.Second,\n\t}\n}\n"
  },
  {
    "path": "agent/utils/ssh/ssh.go",
    "content": "package ssh\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\tgossh \"golang.org/x/crypto/ssh\"\n)\n\ntype ConnInfo struct {\n\tUser        string        `json:\"user\"`\n\tAddr        string        `json:\"addr\"`\n\tPort        int           `json:\"port\"`\n\tAuthMode    string        `json:\"authMode\"`\n\tPassword    string        `json:\"password\"`\n\tPrivateKey  []byte        `json:\"privateKey\"`\n\tPassPhrase  []byte        `json:\"passPhrase\"`\n\tDialTimeOut time.Duration `json:\"dialTimeOut\"`\n}\n\ntype SSHClient struct {\n\tClient *gossh.Client `json:\"client\"`\n}\n\nfunc NewClient(c ConnInfo) (*SSHClient, error) {\n\tconfig := &gossh.ClientConfig{}\n\tconfig.SetDefaults()\n\n\taddr := net.JoinHostPort(c.Addr, fmt.Sprintf(\"%d\", c.Port))\n\tconfig.User = c.User\n\tif c.AuthMode == \"password\" {\n\t\tconfig.Auth = []gossh.AuthMethod{gossh.Password(c.Password)}\n\t} else {\n\t\tsigner, err := makePrivateKeySigner(c.PrivateKey, c.PassPhrase)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tconfig.Auth = []gossh.AuthMethod{gossh.PublicKeys(signer)}\n\t}\n\tif c.DialTimeOut == 0 {\n\t\tc.DialTimeOut = 5 * time.Second\n\t}\n\tconfig.Timeout = c.DialTimeOut\n\n\tconfig.HostKeyCallback = gossh.InsecureIgnoreHostKey()\n\tproto := \"tcp\"\n\tif strings.Contains(c.Addr, \":\") {\n\t\tproto = \"tcp6\"\n\t}\n\tclient, err := gossh.Dial(proto, addr, config)\n\tif nil != err {\n\t\treturn nil, err\n\t}\n\treturn &SSHClient{Client: client}, nil\n}\n\nfunc (c *SSHClient) Close() {\n\t_ = c.Client.Close()\n}\n\nfunc makePrivateKeySigner(privateKey []byte, passPhrase []byte) (gossh.Signer, error) {\n\tif len(passPhrase) != 0 {\n\t\treturn gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)\n\t}\n\treturn gossh.ParsePrivateKey(privateKey)\n}\n"
  },
  {
    "path": "agent/utils/ssl/acme.go",
    "content": "package ssl\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/json\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"golang.org/x/crypto/acme\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/go-acme/lego/v4/certcrypto\"\n\t\"github.com/go-acme/lego/v4/lego\"\n\t\"github.com/go-acme/lego/v4/registration\"\n)\n\nvar Orders = make(map[uint]*acme.Order)\n\ntype zeroSSLRes struct {\n\tSuccess    bool   `json:\"success\"`\n\tEabKid     string `json:\"eab_kid\"`\n\tEabHmacKey string `json:\"eab_hmac_key\"`\n}\n\ntype KeyType = certcrypto.KeyType\n\nconst (\n\tKeyEC256   = certcrypto.EC256\n\tKeyEC384   = certcrypto.EC384\n\tKeyRSA2048 = certcrypto.RSA2048\n\tKeyRSA3072 = certcrypto.RSA3072\n\tKeyRSA4096 = certcrypto.RSA4096\n)\n\ntype AcmeUser struct {\n\tEmail        string\n\tRegistration *registration.Resource\n\tKey          crypto.PrivateKey\n}\n\nfunc (u *AcmeUser) GetEmail() string {\n\treturn u.Email\n}\n\nfunc (u *AcmeUser) GetRegistration() *registration.Resource {\n\treturn u.Registration\n}\nfunc (u *AcmeUser) GetPrivateKey() crypto.PrivateKey {\n\treturn u.Key\n}\n\nfunc GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) {\n\tvar (\n\t\tmarshal []byte\n\t\tblock   *pem.Block\n\t\terr     error\n\t)\n\n\tswitch keyType {\n\tcase KeyEC256, KeyEC384:\n\t\tkey := priKey.(*ecdsa.PrivateKey)\n\t\tmarshal, err = x509.MarshalECPrivateKey(key)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tblock = &pem.Block{\n\t\t\tType:  \"EC PRIVATE KEY\",\n\t\t\tBytes: marshal,\n\t\t}\n\tcase KeyRSA2048, KeyRSA3072, KeyRSA4096:\n\t\tkey := priKey.(*rsa.PrivateKey)\n\t\tmarshal = x509.MarshalPKCS1PrivateKey(key)\n\t\tblock = &pem.Block{\n\t\t\tType:  \"RSA PRIVATE KEY\",\n\t\t\tBytes: marshal,\n\t\t}\n\t}\n\n\treturn pem.EncodeToMemory(block), nil\n}\n\nfunc NewRegisterClient(acmeAccount *model.WebsiteAcmeAccount, proxy *dto.SystemProxy) (*AcmeClient, error) {\n\tvar (\n\t\tpriKey crypto.PrivateKey\n\t\terr    error\n\t)\n\n\tif acmeAccount.PrivateKey != \"\" {\n\t\tswitch KeyType(acmeAccount.KeyType) {\n\t\tcase KeyEC256, KeyEC384:\n\t\t\tblock, _ := pem.Decode([]byte(acmeAccount.PrivateKey))\n\t\t\tpriKey, err = x509.ParseECPrivateKey(block.Bytes)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase KeyRSA2048, KeyRSA3072, KeyRSA4096:\n\t\t\tblock, _ := pem.Decode([]byte(acmeAccount.PrivateKey))\n\t\t\tpriKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\tpriKey, err = certcrypto.GeneratePrivateKey(KeyType(acmeAccount.KeyType))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tmyUser := &AcmeUser{\n\t\tEmail: acmeAccount.Email,\n\t\tKey:   priKey,\n\t}\n\tconfig := NewConfigWithProxy(myUser, acmeAccount.Type, acmeAccount.CaDirURL, proxy)\n\tclient, err := lego.NewClient(config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar reg *registration.Resource\n\tif acmeAccount.Type == \"zerossl\" || acmeAccount.Type == \"google\" || acmeAccount.Type == \"freessl\" || (acmeAccount.Type == \"custom\" && acmeAccount.UseEAB) {\n\t\tif acmeAccount.Type == \"zerossl\" {\n\t\t\tvar res *zeroSSLRes\n\t\t\tres, err = getZeroSSLEabCredentials(acmeAccount.Email)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif res.Success {\n\t\t\t\tacmeAccount.EabKid = res.EabKid\n\t\t\t\tacmeAccount.EabHmacKey = res.EabHmacKey\n\t\t\t} else {\n\t\t\t\treturn nil, fmt.Errorf(\"get zero ssl eab credentials failed\")\n\t\t\t}\n\t\t}\n\n\t\teabOptions := registration.RegisterEABOptions{\n\t\t\tTermsOfServiceAgreed: true,\n\t\t\tKid:                  acmeAccount.EabKid,\n\t\t\tHmacEncoded:          acmeAccount.EabHmacKey,\n\t\t}\n\t\treg, err = client.Registration.RegisterWithExternalAccountBinding(eabOptions)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\treg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tmyUser.Registration = reg\n\n\tacmeClient := &AcmeClient{\n\t\tUser:   myUser,\n\t\tClient: client,\n\t\tConfig: config,\n\t}\n\n\treturn acmeClient, nil\n}\n\nfunc getCaDirURL(accountType, customCaURL string) string {\n\tvar caDirURL string\n\tswitch accountType {\n\tcase \"letsencrypt\":\n\t\tcaDirURL = \"https://acme-v02.api.letsencrypt.org/directory\"\n\tcase \"zerossl\":\n\t\tcaDirURL = \"https://acme.zerossl.com/v2/DV90\"\n\tcase \"buypass\":\n\t\tcaDirURL = \"https://api.buypass.com/acme/directory\"\n\tcase \"google\":\n\t\tcaDirURL = \"https://dv.acme-v02.api.pki.goog/directory\"\n\tcase \"freessl\":\n\t\tcaDirURL = \"https://acmepro.freessl.cn/v2/DV\"\n\tcase \"custom\":\n\t\tcaDirURL = customCaURL\n\t}\n\treturn caDirURL\n}\n\nfunc NewConfigWithProxy(user registration.User, accountType, customCaURL string, systemProxy *dto.SystemProxy) *lego.Config {\n\tvar (\n\t\tcaDirURL      string\n\t\tproxyURL      string\n\t\tproxyUser     string\n\t\tproxyPassword string\n\t)\n\tcaDirURL = getCaDirURL(accountType, customCaURL)\n\tif systemProxy != nil {\n\t\tproxyURL = fmt.Sprintf(\"%s://%s:%s\", systemProxy.Type, systemProxy.URL, systemProxy.Port)\n\t\tproxyUser = systemProxy.User\n\t\tproxyPassword = systemProxy.Password\n\t}\n\treturn &lego.Config{\n\t\tCADirURL:   caDirURL,\n\t\tUserAgent:  \"1Panel\",\n\t\tUser:       user,\n\t\tHTTPClient: createHTTPClientWithProxy(proxyURL, proxyUser, proxyPassword),\n\t\tCertificate: lego.CertificateConfig{\n\t\t\tKeyType: certcrypto.RSA2048,\n\t\t\tTimeout: 60 * time.Second,\n\t\t},\n\t}\n}\n\nfunc initCertPool() *x509.CertPool {\n\tcustomCACertsPath := os.Getenv(\"LEGO_CA_CERTIFICATES\")\n\tif customCACertsPath == \"\" {\n\t\treturn nil\n\t}\n\n\tuseSystemCertPool, _ := strconv.ParseBool(os.Getenv(\"LEGO_CA_SYSTEM_CERT_POOL\"))\n\n\tcaCerts := strings.Split(customCACertsPath, string(os.PathListSeparator))\n\n\tcertPool, err := lego.CreateCertPool(caCerts, useSystemCertPool)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"create certificates pool: %v\", err))\n\t}\n\n\treturn certPool\n}\n\nfunc createHTTPClientWithProxy(proxyURL, username, password string) *http.Client {\n\tvar proxyFunc func(*http.Request) (*url.URL, error)\n\tif proxyURL != \"\" {\n\t\tparsedURL, err := url.Parse(proxyURL)\n\t\tif err != nil {\n\t\t\tproxyFunc = http.ProxyFromEnvironment\n\t\t} else {\n\t\t\tif username != \"\" && password != \"\" {\n\t\t\t\tparsedURL.User = url.UserPassword(username, password)\n\t\t\t} else if username != \"\" {\n\t\t\t\tparsedURL.User = url.User(username)\n\t\t\t}\n\t\t\tproxyFunc = http.ProxyURL(parsedURL)\n\t\t}\n\t} else {\n\t\tproxyFunc = func(_ *http.Request) (*url.URL, error) {\n\t\t\treturn nil, nil\n\t\t}\n\t}\n\n\treturn &http.Client{\n\t\tTimeout: 2 * time.Minute,\n\t\tTransport: &http.Transport{\n\t\t\tProxy: proxyFunc,\n\t\t\tDialContext: (&net.Dialer{\n\t\t\t\tTimeout:   60 * time.Second,\n\t\t\t\tKeepAlive: 60 * time.Second,\n\t\t\t}).DialContext,\n\t\t\tTLSHandshakeTimeout:   60 * time.Second,\n\t\t\tResponseHeaderTimeout: 60 * time.Second,\n\t\t\tTLSClientConfig: &tls.Config{\n\t\t\t\tServerName: os.Getenv(\"LEGO_CA_SERVER_NAME\"),\n\t\t\t\tRootCAs:    initCertPool(),\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc getZeroSSLEabCredentials(email string) (*zeroSSLRes, error) {\n\tbaseURL := \"https://api.zerossl.com/acme/eab-credentials-email\"\n\tparams := url.Values{}\n\tparams.Add(\"email\", email)\n\trequestURL := fmt.Sprintf(\"%s?%s\", baseURL, params.Encode())\n\n\treq, err := http.NewRequest(\"POST\", requestURL, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tdefer client.CloseIdleConnections()\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"server returned non-200 status: %d %s\", resp.StatusCode, http.StatusText(resp.StatusCode))\n\t}\n\n\tvar result zeroSSLRes\n\terr = json.Unmarshal(body, &result)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &result, nil\n}\n\nfunc GetPrivateKeyByType(keyType, sslPrivateKey string) (crypto.PrivateKey, error) {\n\tvar (\n\t\tprivateKey crypto.PrivateKey\n\t\terr        error\n\t)\n\tkType := KeyType(keyType)\n\tif sslPrivateKey == \"\" {\n\t\tprivateKey, err = certcrypto.GeneratePrivateKey(kType)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn privateKey, nil\n\t}\n\tblock, _ := pem.Decode([]byte(sslPrivateKey))\n\tif block == nil {\n\t\treturn nil, buserr.New(\"invalid PEM block\")\n\t}\n\tvar privKey crypto.PrivateKey\n\tswitch kType {\n\tcase certcrypto.EC256, certcrypto.EC384:\n\t\tprivKey, err = x509.ParseECPrivateKey(block.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase certcrypto.RSA2048, certcrypto.RSA3072, certcrypto.RSA4096:\n\t\tprivKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tprivateKey = privKey\n\treturn privateKey, nil\n}\n\nfunc getWebsiteSSLDomains(websiteSSL *model.WebsiteSSL) []string {\n\tdomains := []string{websiteSSL.PrimaryDomain}\n\tif websiteSSL.Domains != \"\" {\n\t\tdomains = append(domains, strings.Split(websiteSSL.Domains, \",\")...)\n\t}\n\treturn domains\n}\n\nconst (\n\tmaxRetryAttempts = 3\n\tretryDelayOn503  = 30 * time.Second\n)\n\n// isHTTP503Error checks if an error is an HTTP 503 Service Unavailable error\nfunc isHTTP503Error(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\n\t// Check for golang.org/x/crypto/acme.Error (used in manual_client.go)\n\tvar acmeErr *acme.Error\n\tif errors.As(err, &acmeErr) {\n\t\treturn acmeErr.StatusCode == http.StatusServiceUnavailable\n\t}\n\n\t// Check error message for 503 (fallback for lego library errors)\n\terrMsg := err.Error()\n\treturn strings.Contains(errMsg, \"503\") ||\n\t\tstrings.Contains(errMsg, \"Service busy\")\n}\n"
  },
  {
    "path": "agent/utils/ssl/client.go",
    "content": "package ssl\n\nimport (\n\t\"crypto\"\n\t\"crypto/rand\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/go-acme/lego/v4/certificate\"\n\t\"github.com/go-acme/lego/v4/challenge/dns01\"\n\t\"github.com/go-acme/lego/v4/lego\"\n\t\"github.com/go-acme/lego/v4/providers/http/webroot\"\n\t\"github.com/pkg/errors\"\n)\n\ntype AcmeClientOption func(*AcmeClientOptions)\n\ntype AcmeClientOptions struct {\n\tSystemProxy *dto.SystemProxy\n}\n\ntype AcmeClient struct {\n\tConfig   *lego.Config\n\tClient   *lego.Client\n\tUser     *AcmeUser\n\tProxyURL string\n}\n\nfunc NewAcmeClient(acmeAccount *model.WebsiteAcmeAccount, systemProxy *dto.SystemProxy) (*AcmeClient, error) {\n\tif acmeAccount.Email == \"\" {\n\t\treturn nil, errors.New(\"email can not blank\")\n\t}\n\n\tclient, err := NewRegisterClient(acmeAccount, systemProxy)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn client, nil\n}\n\nfunc (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.WebsiteSSL) error {\n\tp, err := getDNSProviderConfig(dnsType, params)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar nameservers []string\n\tif websiteSSL.Nameserver1 != \"\" {\n\t\tnameservers = append(nameservers, websiteSSL.Nameserver1)\n\t}\n\tif websiteSSL.Nameserver2 != \"\" {\n\t\tnameservers = append(nameservers, websiteSSL.Nameserver2)\n\t}\n\tif websiteSSL.DisableCNAME {\n\t\t_ = os.Setenv(\"LEGO_DISABLE_CNAME_SUPPORT\", \"true\")\n\t} else {\n\t\t_ = os.Setenv(\"LEGO_DISABLE_CNAME_SUPPORT\", \"false\")\n\t}\n\n\treturn c.Client.Challenge.SetDNS01Provider(p,\n\t\tdns01.CondOption(len(nameservers) > 0,\n\t\t\tdns01.AddRecursiveNameservers(nameservers)),\n\t\tdns01.CondOption(websiteSSL.SkipDNS,\n\t\t\tdns01.DisableAuthoritativeNssPropagationRequirement()),\n\t\tdns01.AddDNSTimeout(dnsTimeOut),\n\t)\n}\n\nfunc (c *AcmeClient) UseHTTP(path string) error {\n\thttpProvider, err := webroot.NewHTTPProvider(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = c.Client.Challenge.SetHTTP01Provider(httpProvider)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *AcmeClient) ObtainSSL(domains []string, privateKey crypto.PrivateKey) (certificate.Resource, error) {\n\trequest := certificate.ObtainRequest{\n\t\tDomains:    domains,\n\t\tBundle:     true,\n\t\tPrivateKey: privateKey,\n\t}\n\n\tvar certificates *certificate.Resource\n\tvar err error\n\n\tfor attempt := 1; attempt <= maxRetryAttempts; attempt++ {\n\t\tcertificates, err = c.Client.Certificate.Obtain(request)\n\t\tif err == nil {\n\t\t\treturn *certificates, nil\n\t\t}\n\n\t\tif isHTTP503Error(err) && attempt < maxRetryAttempts {\n\t\t\tglobal.LOG.Warnf(\"ACME server returned 503, retrying in %v (attempt %d/%d)\",\n\t\t\t\tretryDelayOn503, attempt, maxRetryAttempts)\n\t\t\ttime.Sleep(retryDelayOn503)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Non-503 error or final attempt, return error\n\t\treturn certificate.Resource{}, err\n\t}\n\n\treturn certificate.Resource{}, err\n}\n\nfunc (c *AcmeClient) ObtainIPSSL(ipAddress string, privKey crypto.PrivateKey) (certificate.Resource, error) {\n\tcsrTemplate := &x509.CertificateRequest{\n\t\tSubject: pkix.Name{\n\t\t\tCommonName: \"\",\n\t\t},\n\t\tIPAddresses: []net.IP{\n\t\t\tnet.ParseIP(ipAddress),\n\t\t},\n\t}\n\tcsrDER, err := x509.CreateCertificateRequest(\n\t\trand.Reader,\n\t\tcsrTemplate,\n\t\tprivKey,\n\t)\n\tif err != nil {\n\t\treturn certificate.Resource{}, err\n\t}\n\tcsr, err := x509.ParseCertificateRequest(csrDER)\n\tif err != nil {\n\t\treturn certificate.Resource{}, err\n\t}\n\treq := certificate.ObtainForCSRRequest{\n\t\tCSR:        csr,\n\t\tPrivateKey: privKey,\n\t\tProfile:    \"shortlived\",\n\t\tBundle:     true,\n\t}\n\n\tvar certificates *certificate.Resource\n\tfor attempt := 1; attempt <= maxRetryAttempts; attempt++ {\n\t\tcertificates, err = c.Client.Certificate.ObtainForCSR(req)\n\t\tif err == nil {\n\t\t\treturn *certificates, nil\n\t\t}\n\n\t\tif isHTTP503Error(err) && attempt < maxRetryAttempts {\n\t\t\tglobal.LOG.Warnf(\"ACME server returned 503 for IP SSL, retrying in %v (attempt %d/%d)\",\n\t\t\t\tretryDelayOn503, attempt, maxRetryAttempts)\n\t\t\ttime.Sleep(retryDelayOn503)\n\t\t\tcontinue\n\t\t}\n\n\t\treturn certificate.Resource{}, err\n\t}\n\n\treturn certificate.Resource{}, err\n}\n\nfunc (c *AcmeClient) RevokeSSL(pemSSL []byte) error {\n\treturn c.Client.Certificate.Revoke(pemSSL)\n}\n"
  },
  {
    "path": "agent/utils/ssl/dns_provider.go",
    "content": "package ssl\n\nimport (\n\t\"encoding/json\"\n\t\"github.com/go-acme/lego/v4/challenge\"\n\t\"github.com/go-acme/lego/v4/providers/dns/acmedns\"\n\t\"github.com/go-acme/lego/v4/providers/dns/alidns\"\n\t\"github.com/go-acme/lego/v4/providers/dns/aliesa\"\n\t\"github.com/go-acme/lego/v4/providers/dns/baiducloud\"\n\t\"github.com/go-acme/lego/v4/providers/dns/clouddns\"\n\t\"github.com/go-acme/lego/v4/providers/dns/cloudflare\"\n\t\"github.com/go-acme/lego/v4/providers/dns/cloudns\"\n\t\"github.com/go-acme/lego/v4/providers/dns/dnspod\"\n\t\"github.com/go-acme/lego/v4/providers/dns/dynu\"\n\t\"github.com/go-acme/lego/v4/providers/dns/freemyip\"\n\t\"github.com/go-acme/lego/v4/providers/dns/godaddy\"\n\t\"github.com/go-acme/lego/v4/providers/dns/huaweicloud\"\n\t\"github.com/go-acme/lego/v4/providers/dns/namecheap\"\n\t\"github.com/go-acme/lego/v4/providers/dns/namedotcom\"\n\t\"github.com/go-acme/lego/v4/providers/dns/namesilo\"\n\t\"github.com/go-acme/lego/v4/providers/dns/ovh\"\n\t\"github.com/go-acme/lego/v4/providers/dns/porkbun\"\n\t\"github.com/go-acme/lego/v4/providers/dns/rainyun\"\n\t\"github.com/go-acme/lego/v4/providers/dns/regru\"\n\t\"github.com/go-acme/lego/v4/providers/dns/route53\"\n\t\"github.com/go-acme/lego/v4/providers/dns/spaceship\"\n\t\"github.com/go-acme/lego/v4/providers/dns/technitium\"\n\t\"github.com/go-acme/lego/v4/providers/dns/tencentcloud\"\n\t\"github.com/go-acme/lego/v4/providers/dns/vercel\"\n\t\"github.com/go-acme/lego/v4/providers/dns/volcengine\"\n\t\"github.com/go-acme/lego/v4/providers/dns/westcn\"\n\t\"time\"\n)\n\ntype DnsType string\n\nconst (\n\tDnsPod       DnsType = \"DnsPod\"\n\tAliYun       DnsType = \"AliYun\"\n\tAliESA       DnsType = \"AliESA\"\n\tAWSRoute53   DnsType = \"AWSRoute53\"\n\tCloudFlare   DnsType = \"CloudFlare\"\n\tCloudDns     DnsType = \"CloudDns\"\n\tNameSilo     DnsType = \"NameSilo\"\n\tNameCheap    DnsType = \"NameCheap\"\n\tNameCom      DnsType = \"NameCom\"\n\tGodaddy      DnsType = \"Godaddy\"\n\tTencentCloud DnsType = \"TencentCloud\"\n\tRainYun      DnsType = \"RainYun\"\n\tVolcengine   DnsType = \"Volcengine\"\n\tHuaweiCloud  DnsType = \"HuaweiCloud\"\n\tFreeMyIP     DnsType = \"FreeMyIP\"\n\tVercel       DnsType = \"Vercel\"\n\tSpaceship    DnsType = \"Spaceship\"\n\tWestCN       DnsType = \"WestCN\"\n\tClouDNS      DnsType = \"ClouDNS\"\n\tRegRu        DnsType = \"RegRu\"\n\tDynu         DnsType = \"Dynu\"\n\tBaiduCloud   DnsType = \"BaiduCloud\"\n\tOvh          DnsType = \"Ovh\"\n\tAcmeDNS      DnsType = \"AcmeDNS\"\n\tPorkBun      DnsType = \"PorkBun\"\n\tTechnitium\t DnsType = \"Technitium\"\n)\n\ntype DNSParam struct {\n\tID           string `json:\"id\"`\n\tToken        string `json:\"token\"`\n\tAccessKey    string `json:\"accessKey\"`\n\tSecretKey    string `json:\"secretKey\"`\n\tEmail        string `json:\"email\"`\n\tAPIkey       string `json:\"apiKey\"`\n\tAPIUser      string `json:\"apiUser\"`\n\tAPISecret    string `json:\"apiSecret\"`\n\tSecretID     string `json:\"secretID\"`\n\tClientID     string `json:\"clientID\"`\n\tPassword     string `json:\"password\"`\n\tRegion       string `json:\"region\"`\n\tUsername     string `json:\"username\"`\n\tAuthID       string `json:\"authID\"`\n\tSubAuthID    string `json:\"subAuthID\"`\n\tAuthPassword string `json:\"authPassword\"`\n\tEndpoint     string `json:\"endpoint\"`\n\tAccessToken  string `json:\"accessToken\"`\n\tBaseURL      string `json:\"baseURL\"`\n}\n\nvar (\n\tpropagationTimeout = 30 * time.Minute\n\tpollingInterval    = 10 * time.Second\n\tttl                = 3600\n\tdnsTimeOut         = 30 * time.Minute\n\tmanualDnsTimeout   = 10 * time.Minute\n)\n\nfunc getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, error) {\n\tvar (\n\t\tparam DNSParam\n\t\tp     challenge.Provider\n\t\terr   error\n\t)\n\tif err := json.Unmarshal([]byte(params), &param); err != nil {\n\t\treturn nil, err\n\t}\n\tswitch dnsType {\n\tcase DnsPod:\n\t\tconfig := dnspod.NewDefaultConfig()\n\t\tconfig.LoginToken = param.ID + \",\" + param.Token\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = dnspod.NewDNSProviderConfig(config)\n\tcase AliYun:\n\t\tconfig := alidns.NewDefaultConfig()\n\t\tconfig.SecretKey = param.SecretKey\n\t\tconfig.APIKey = param.AccessKey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = alidns.NewDNSProviderConfig(config)\n\tcase AliESA:\n\t\tconfig := aliesa.NewDefaultConfig()\n\t\tconfig.SecretKey = param.SecretKey\n\t\tconfig.APIKey = param.AccessKey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = aliesa.NewDNSProviderConfig(config)\n\tcase AWSRoute53:\n\t\tconfig := route53.NewDefaultConfig()\n\t\tconfig.AccessKeyID = param.AccessKey\n\t\tconfig.SecretAccessKey = param.SecretKey\n\t\tconfig.Region = param.Region\n\t\tif config.Region == \"\" {\n\t\t\tconfig.Region = \"us-east-1\"\n\t\t}\n\t\tconfig.HostedZoneID = param.Endpoint\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = route53.NewDNSProviderConfig(config)\n\tcase CloudFlare:\n\t\tconfig := cloudflare.NewDefaultConfig()\n\t\tconfig.AuthEmail = param.Email\n\t\tconfig.AuthToken = param.APIkey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = cloudflare.NewDNSProviderConfig(config)\n\tcase CloudDns:\n\t\tconfig := clouddns.NewDefaultConfig()\n\t\tconfig.ClientID = param.ClientID\n\t\tconfig.Email = param.Email\n\t\tconfig.Password = param.Password\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = clouddns.NewDNSProviderConfig(config)\n\tcase NameCheap:\n\t\tconfig := namecheap.NewDefaultConfig()\n\t\tconfig.APIKey = param.APIkey\n\t\tconfig.APIUser = param.APIUser\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = namecheap.NewDNSProviderConfig(config)\n\tcase NameSilo:\n\t\tconfig := namesilo.NewDefaultConfig()\n\t\tconfig.APIKey = param.APIkey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = namesilo.NewDNSProviderConfig(config)\n\tcase Godaddy:\n\t\tconfig := godaddy.NewDefaultConfig()\n\t\tconfig.APIKey = param.APIkey\n\t\tconfig.APISecret = param.APISecret\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = godaddy.NewDNSProviderConfig(config)\n\tcase NameCom:\n\t\tconfig := namedotcom.NewDefaultConfig()\n\t\tconfig.APIToken = param.Token\n\t\tconfig.Username = param.APIUser\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = namedotcom.NewDNSProviderConfig(config)\n\tcase TencentCloud:\n\t\tconfig := tencentcloud.NewDefaultConfig()\n\t\tconfig.SecretID = param.SecretID\n\t\tconfig.SecretKey = param.SecretKey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = tencentcloud.NewDNSProviderConfig(config)\n\tcase RainYun:\n\t\tconfig := rainyun.NewDefaultConfig()\n\t\tconfig.APIKey = param.APIkey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = rainyun.NewDNSProviderConfig(config)\n\tcase Volcengine:\n\t\tconfig := volcengine.NewDefaultConfig()\n\t\tconfig.SecretKey = param.SecretKey\n\t\tconfig.AccessKey = param.AccessKey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = volcengine.NewDNSProviderConfig(config)\n\tcase HuaweiCloud:\n\t\tconfig := huaweicloud.NewDefaultConfig()\n\t\tconfig.AccessKeyID = param.AccessKey\n\t\tconfig.SecretAccessKey = param.SecretKey\n\t\tconfig.Region = param.Region\n\t\tif config.Region == \"\" {\n\t\t\tconfig.Region = \"cn-north-1\"\n\t\t}\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = int32(ttl)\n\t\tp, err = huaweicloud.NewDNSProviderConfig(config)\n\tcase FreeMyIP:\n\t\tconfig := freemyip.NewDefaultConfig()\n\t\tconfig.Token = param.Token\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tp, err = freemyip.NewDNSProviderConfig(config)\n\tcase Vercel:\n\t\tconfig := vercel.NewDefaultConfig()\n\t\tconfig.AuthToken = param.Token\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tp, err = vercel.NewDNSProviderConfig(config)\n\tcase Spaceship:\n\t\tconfig := spaceship.NewDefaultConfig()\n\t\tconfig.APIKey = param.APIkey\n\t\tconfig.APISecret = param.APISecret\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = spaceship.NewDNSProviderConfig(config)\n\tcase WestCN:\n\t\tconfig := westcn.NewDefaultConfig()\n\t\tconfig.Username = param.Username\n\t\tconfig.Password = param.Password\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = westcn.NewDNSProviderConfig(config)\n\tcase ClouDNS:\n\t\tconfig := cloudns.NewDefaultConfig()\n\t\tconfig.AuthID = param.AuthID\n\t\tconfig.SubAuthID = param.SubAuthID\n\t\tconfig.AuthPassword = param.AuthPassword\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = cloudns.NewDNSProviderConfig(config)\n\tcase RegRu:\n\t\tconfig := regru.NewDefaultConfig()\n\t\tconfig.Username = param.Username\n\t\tconfig.Password = param.Password\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = regru.NewDNSProviderConfig(config)\n\tcase Dynu:\n\t\tconfig := dynu.NewDefaultConfig()\n\t\tconfig.APIKey = param.APIkey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = dynu.NewDNSProviderConfig(config)\n\tcase BaiduCloud:\n\t\tconfig := baiducloud.NewDefaultConfig()\n\t\tconfig.AccessKeyID = param.AccessKey\n\t\tconfig.SecretAccessKey = param.SecretKey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = baiducloud.NewDNSProviderConfig(config)\n\tcase Ovh:\n\t\tconfig := ovh.NewDefaultConfig()\n\t\tconfig.APIEndpoint = param.Endpoint\n\t\tconfig.AccessToken = param.AccessToken\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = ovh.NewDNSProviderConfig(config)\n\tcase AcmeDNS:\n\t\tconfig := acmedns.NewDefaultConfig()\n\t\tconfig.APIBase = param.Endpoint\n\t\tconfig.StorageBaseURL = param.BaseURL\n\t\tp, err = acmedns.NewDNSProviderConfig(config)\n\tcase PorkBun:\n\t\tconfig := porkbun.NewDefaultConfig()\n\t\tconfig.APIKey = param.APIkey\n\t\tconfig.SecretAPIKey = param.SecretKey\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = porkbun.NewDNSProviderConfig(config)\n\tcase Technitium:\n\t\tconfig := technitium.NewDefaultConfig()\n\t\tconfig.BaseURL = param.BaseURL\n\t\tconfig.APIToken = param.Token\n\t\tconfig.PropagationTimeout = propagationTimeout\n\t\tconfig.PollingInterval = pollingInterval\n\t\tconfig.TTL = ttl\n\t\tp, err = technitium.NewDNSProviderConfig(config)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn p, nil\n}\n"
  },
  {
    "path": "agent/utils/ssl/manual_client.go",
    "content": "package ssl\n\nimport (\n\t\"context\"\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/go-acme/lego/v4/certificate\"\n\t\"github.com/miekg/dns\"\n\t\"golang.org/x/crypto/acme\"\n\t\"log\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype ManualClient struct {\n\tclient  *acme.Client\n\taccount *model.WebsiteAcmeAccount\n\tlogger  *log.Logger\n}\n\ntype RequestCertRequest struct {\n\tWebsiteSSL *model.WebsiteSSL\n}\n\nfunc NewCustomAcmeClient(acmeAccount *model.WebsiteAcmeAccount, logger *log.Logger) (*ManualClient, error) {\n\tvar (\n\t\tkey crypto.PrivateKey\n\t\terr error\n\t)\n\tswitch KeyType(acmeAccount.KeyType) {\n\tcase KeyEC256, KeyEC384:\n\t\tblock, _ := pem.Decode([]byte(acmeAccount.PrivateKey))\n\t\tkey, err = x509.ParseECPrivateKey(block.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase KeyRSA2048, KeyRSA3072, KeyRSA4096:\n\t\tblock, _ := pem.Decode([]byte(acmeAccount.PrivateKey))\n\t\tkey, err = x509.ParsePKCS1PrivateKey(block.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif logger == nil {\n\t\tlogger = log.Default()\n\t}\n\n\tclient := &acme.Client{\n\t\tKey:          key.(crypto.Signer),\n\t\tDirectoryURL: getCaDirURL(acmeAccount.Type, acmeAccount.CaDirURL),\n\t}\n\treturn &ManualClient{\n\t\tclient:  client,\n\t\taccount: acmeAccount,\n\t\tlogger:  logger,\n\t}, nil\n}\n\ntype Resolve struct {\n\tKey   string\n\tValue string\n\tErr   string\n}\n\nfunc (c *ManualClient) GetDNSResolve(ctx context.Context, websiteSSL *model.WebsiteSSL) (map[string]Resolve, error) {\n\tvar order *acme.Order\n\tvar err error\n\n\t// Check if we have an existing valid order for this SSL\n\texistingOrder, exists := Orders[websiteSSL.ID]\n\tif exists && existingOrder != nil {\n\t\t// Verify the order is still valid (not expired and still pending)\n\t\t// If Expires is zero, order is still valid (ACME doesn't always set expiry immediately)\n\t\tisNotExpired := existingOrder.Expires.IsZero() || existingOrder.Expires.After(time.Now())\n\t\tif isNotExpired && (existingOrder.Status == acme.StatusPending || existingOrder.Status == acme.StatusReady) {\n\t\t\t// Try to reuse the existing order\n\t\t\trecords, err := c.extractDNSChallenges(ctx, existingOrder)\n\t\t\tif err == nil && len(records) > 0 {\n\t\t\t\treturn records, nil\n\t\t\t}\n\t\t\t// If extraction failed, fall through to create a new order\n\t\t}\n\t\t// Existing order is expired or invalid, remove it\n\t\tdelete(Orders, websiteSSL.ID)\n\t}\n\n\t// Create a new order\n\torder, err = c.client.AuthorizeOrder(ctx, acme.DomainIDs(getWebsiteSSLDomains(websiteSSL)...))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tOrders[websiteSSL.ID] = order\n\n\treturn c.extractDNSChallenges(ctx, order)\n}\n\n// extractDNSChallenges extracts DNS-01 challenge values from an ACME order\nfunc (c *ManualClient) extractDNSChallenges(ctx context.Context, order *acme.Order) (map[string]Resolve, error) {\n\trecords := make(map[string]Resolve)\n\n\tfor _, authzURL := range order.AuthzURLs {\n\t\tauthz, err := c.client.GetAuthorization(ctx, authzURL)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdomain := authz.Identifier.Value\n\n\t\tvar dnsChallenge *acme.Challenge\n\t\tfor _, challenge := range authz.Challenges {\n\t\t\tif challenge.Type == \"dns-01\" {\n\t\t\t\tdnsChallenge = challenge\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif dnsChallenge == nil {\n\t\t\treturn nil, fmt.Errorf(\"no DNS-01 challenge found for domain %s\", domain)\n\t\t}\n\n\t\ttxtValue, err := c.client.DNS01ChallengeRecord(dnsChallenge.Token)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Use different map key for wildcard vs non-wildcard to avoid overwriting\n\t\t// Both use the same DNS record name (_acme-challenge.domain) but different values\n\t\tmapKey := domain\n\t\tif authz.Wildcard {\n\t\t\tmapKey = \"*.\" + domain\n\t\t}\n\n\t\trecords[mapKey] = Resolve{\n\t\t\tKey:   fmt.Sprintf(\"_acme-challenge.%s\", domain),\n\t\t\tValue: txtValue,\n\t\t}\n\t}\n\treturn records, nil\n}\n\nfunc queryDNSRecords(domain string) (map[string][]string, error) {\n\trecordName := fmt.Sprintf(\"_acme-challenge.%s\", domain)\n\ttxts, err := net.LookupTXT(recordName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trecords := make(map[string][]string)\n\tif len(txts) > 0 {\n\t\trecords[recordName] = txts\n\t}\n\treturn records, nil\n}\n\nfunc (c *ManualClient) handleAuthorization(ctx context.Context, authzURL string, nameservers []string) error {\n\tauthz, err := c.client.GetAuthorization(ctx, authzURL)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get authorization: %v\", err)\n\t}\n\n\tdomain := authz.Identifier.Value\n\tc.logger.Printf(\"[INFO] [%s] AuthURL: %s\", domain, authzURL)\n\n\tif authz.Status == acme.StatusValid {\n\t\treturn nil\n\t}\n\n\tvar dnsChallenge *acme.Challenge\n\tfor _, challenge := range authz.Challenges {\n\t\tif challenge.Type == \"dns-01\" {\n\t\t\tdnsChallenge = challenge\n\t\t\tbreak\n\t\t}\n\t}\n\n\tc.logger.Printf(\"[INFO] [%s] acme: use dns-01 solver\", domain)\n\tif dnsChallenge == nil {\n\t\treturn fmt.Errorf(\"no DNS-01 challenge found for domain %s\", domain)\n\t}\n\n\tdeadline := time.Now().Add(manualDnsTimeout)\n\texpectedRecord, err := c.client.DNS01ChallengeRecord(dnsChallenge.Token)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to compute DNS challenge record: %v\", err)\n\t}\n\tc.logger.Printf(\"[INFO] [%s] acme: Checking TXT record  %s\", domain, expectedRecord)\n\n\tfor {\n\t\tc.logger.Printf(\"[INFO] [%s] acme: Checking DNS record propagation.\", domain)\n\t\tvar currentRecords map[string][]string\n\t\tvar queryErr error\n\t\tif len(nameservers) == 0 {\n\t\t\tcurrentRecords, queryErr = queryDNSRecords(domain)\n\t\t} else {\n\t\t\tvar errs []string\n\t\t\tfor _, nameserver := range nameservers {\n\t\t\t\tcurrentRecords, queryErr = queryDNSRecordsWithResolver(ctx, c.logger, domain, nameserver)\n\t\t\t\terrs = append(errs, fmt.Sprintf(\"%s: %v\", nameserver, queryErr))\n\t\t\t}\n\t\t\tif queryErr != nil && len(errs) > 0 {\n\t\t\t\tqueryErr = fmt.Errorf(\"all nameservers failed: %s\", strings.Join(errs, \"; \"))\n\t\t\t}\n\t\t}\n\t\tif currentRecords == nil && queryErr != nil {\n\t\t\treturn fmt.Errorf(\"failed to query DNS records: %v\", queryErr)\n\t\t}\n\t\trecordName := fmt.Sprintf(\"_acme-challenge.%s\", domain)\n\t\tprovidedRecords, exists := currentRecords[recordName]\n\t\t// Check if expected record is in any of the TXT values\n\t\tfound := false\n\t\tif exists {\n\t\t\tfor _, record := range providedRecords {\n\t\t\t\tif record == expectedRecord {\n\t\t\t\t\tfound = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif found {\n\t\t\tbreak\n\t\t}\n\t\tif time.Now().After(deadline) {\n\t\t\tif !exists || len(providedRecords) == 0 {\n\t\t\t\treturn fmt.Errorf(\"TXT record not provided for domain %s after retrying\", domain)\n\t\t\t}\n\t\t\tc.logger.Printf(\"[INFO] [%s] TXT record mismatch for %s: expected %s, got %v\", domain, domain, expectedRecord, providedRecords)\n\t\t\treturn fmt.Errorf(\"TXT record mismatch for %s: expected %s, got %v\", domain, expectedRecord, providedRecords)\n\t\t}\n\t\ttime.Sleep(pollingInterval)\n\t}\n\n\t_, err = c.client.Accept(ctx, dnsChallenge)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to accept challenge: %v\", err)\n\t}\n\tfor {\n\t\ttime.Sleep(pollingInterval)\n\t\tauthz, err = c.client.GetAuthorization(ctx, authzURL)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get authorization while polling: %v\", err)\n\t\t}\n\t\tif authz.Status == acme.StatusValid {\n\t\t\tbreak\n\t\t} else if authz.Status == acme.StatusInvalid {\n\t\t\treturn fmt.Errorf(\"authorization failed for domain %s\", domain)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *ManualClient) createCSR(keyType string, privateKey string, domains []string) ([]byte, crypto.PrivateKey, error) {\n\tvar certKey crypto.PrivateKey\n\tvar err error\n\tcertKey, err = GetPrivateKeyByType(keyType, privateKey)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\ttemplate := x509.CertificateRequest{\n\t\tSubject:  pkix.Name{CommonName: domains[0]},\n\t\tDNSNames: domains,\n\t}\n\tcsrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, certKey)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn csrBytes, certKey, nil\n}\n\nfunc (c *ManualClient) encodePrivateKey(key crypto.PrivateKey) (string, error) {\n\tvar keyBytes []byte\n\tvar keyType string\n\tvar err error\n\n\tswitch k := key.(type) {\n\tcase *ecdsa.PrivateKey:\n\t\tkeyBytes, err = x509.MarshalECPrivateKey(k)\n\t\tkeyType = \"EC PRIVATE KEY\"\n\tcase *rsa.PrivateKey:\n\t\tkeyBytes = x509.MarshalPKCS1PrivateKey(k)\n\t\tkeyType = \"RSA PRIVATE KEY\"\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported key type\")\n\t}\n\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tblock := &pem.Block{\n\t\tType:  keyType,\n\t\tBytes: keyBytes,\n\t}\n\n\treturn string(pem.EncodeToMemory(block)), nil\n}\n\nfunc (c *ManualClient) RequestCertificate(ctx context.Context, websiteSSL *model.WebsiteSSL) (certificate.Resource, error) {\n\tvar res certificate.Resource\n\tdomains := []string{websiteSSL.PrimaryDomain}\n\tif websiteSSL.Domains != \"\" {\n\t\tdomains = append(domains, strings.Split(websiteSSL.Domains, \",\")...)\n\t}\n\n\tc.logger.Printf(\"[INFO] Requesting certificate for domains: %v\\n\", domains)\n\tcsr, certKey, err := c.createCSR(websiteSSL.KeyType, websiteSSL.PrivateKey, domains)\n\tif err != nil {\n\t\treturn res, err\n\t}\n\n\torder, ok := Orders[websiteSSL.ID]\n\tif !ok {\n\t\treturn res, fmt.Errorf(\"order not found\")\n\t}\n\tdefer delete(Orders, websiteSSL.ID)\n\n\tfor _, authzURL := range order.AuthzURLs {\n\t\tif err := c.handleAuthorization(ctx, authzURL, getNameservers(*websiteSSL)); err != nil {\n\t\t\treturn res, err\n\t\t}\n\t}\n\n\tc.logger.Printf(\"[INFO] acme: Validations succeeded; requesting certificates\")\n\torder, err = c.client.WaitOrder(ctx, order.URI)\n\tif err != nil {\n\t\treturn res, err\n\t}\n\n\tif order.Status != acme.StatusReady {\n\t\treturn res, fmt.Errorf(\"order not ready: %s\", order.Status)\n\t}\n\n\tcertBytes, certURL, err := c.client.CreateOrderCert(ctx, order.FinalizeURL, csr, true)\n\tif err != nil {\n\t\treturn res, fmt.Errorf(\"failed to finalize order: %v\", err)\n\t}\n\n\tprivateKeyPEM, err := c.encodePrivateKey(certKey)\n\tif err != nil {\n\t\treturn res, err\n\t}\n\n\tvar certPEM []byte\n\tfor _, cert := range certBytes {\n\t\tblock := &pem.Block{\n\t\t\tType:  \"CERTIFICATE\",\n\t\t\tBytes: cert,\n\t\t}\n\t\tcertPEM = append(certPEM, pem.EncodeToMemory(block)...)\n\t}\n\tc.logger.Printf(\"[INFO] acme: Server responded with a certificate.\")\n\tresource := certificate.Resource{\n\t\tDomain:        domains[0],\n\t\tCertURL:       certURL,\n\t\tCertStableURL: certURL,\n\t\tPrivateKey:    []byte(privateKeyPEM),\n\t\tCertificate:   certPEM,\n\t\tCSR:           csr,\n\t}\n\treturn resource, nil\n}\n\nfunc getNameservers(websiteSSL model.WebsiteSSL) []string {\n\tvar nameservers []string\n\tif websiteSSL.Nameserver1 != \"\" {\n\t\tnameservers = append(nameservers, handleNameserver(websiteSSL.Nameserver1))\n\t}\n\tif websiteSSL.Nameserver2 != \"\" {\n\t\tnameservers = append(nameservers, handleNameserver(websiteSSL.Nameserver2))\n\t}\n\treturn nameservers\n}\n\nfunc handleNameserver(nameserver string) string {\n\tif strings.Contains(nameserver, \":\") {\n\t\treturn nameserver\n\t}\n\treturn fmt.Sprintf(\"%s:53\", nameserver)\n}\n\nfunc queryDNSRecordsWithResolver(ctx context.Context, logger *log.Logger, domain string, dnsServer string) (map[string][]string, error) {\n\trecordName := fmt.Sprintf(\"_acme-challenge.%s\", domain)\n\tc := new(dns.Client)\n\tc.Timeout = 10 * time.Second\n\tc.Net = \"udp\"\n\n\tm := new(dns.Msg)\n\tm.SetQuestion(dns.Fqdn(recordName), dns.TypeTXT)\n\tm.RecursionDesired = true\n\n\tr, _, err := c.ExchangeContext(ctx, m, dnsServer)\n\tif isNetworkError(err) {\n\t\tlogger.Printf(\"[WARN] Network error occurred while querying DNS: %v\", err)\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"DNS query failed: %w\", err)\n\t}\n\n\tif r.Rcode == dns.RcodeNameError {\n\t\tlogger.Printf(\"[INFO] DNS record does not exist yet (NXDOMAIN)\")\n\t\treturn nil, nil\n\t}\n\n\tif r.Rcode != dns.RcodeSuccess {\n\t\treturn nil, fmt.Errorf(\"DNS query failed with code: %s\", dns.RcodeToString[r.Rcode])\n\t}\n\n\trecords := make(map[string][]string)\n\tvar txtValues []string\n\n\tfor _, answer := range r.Answer {\n\t\tif txt, ok := answer.(*dns.TXT); ok {\n\t\t\tif len(txt.Txt) > 0 {\n\t\t\t\ttxtValues = append(txtValues, txt.Txt[0])\n\t\t\t}\n\t\t}\n\t}\n\tif len(txtValues) > 0 {\n\t\trecords[recordName] = txtValues\n\t}\n\n\treturn records, nil\n}\n\nfunc isNetworkError(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\treturn strings.Contains(err.Error(), \"i/o timeout\") ||\n\t\tstrings.Contains(err.Error(), \"connection refused\") ||\n\t\tstrings.Contains(err.Error(), \"network is unreachable\") ||\n\t\tstrings.Contains(err.Error(), \"no route to host\")\n}\n"
  },
  {
    "path": "agent/utils/terminal/ai/client.go",
    "content": "package ai\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\tprovidercatalog \"github.com/1Panel-dev/1Panel/agent/app/provider\"\n)\n\nconst (\n\tdefaultTimeout        = 30 * time.Second\n\tdefaultUserAgent      = \"1panel-terminal-ai/1.0\"\n\tdefaultAnthropicToken = 1024\n)\n\ntype Client interface {\n\tChatCompletion(ctx context.Context, req ChatCompletionRequest) (*ChatCompletionResponse, error)\n}\n\ntype ClientConfig struct {\n\tProvider   string\n\tBaseURL    string\n\tAPIKey     string\n\tModel      string\n\tAPIType    string\n\tMaxTokens  int\n\tTimeout    time.Duration\n\tHTTPClient *http.Client\n}\n\ntype GeneratorConfig struct {\n\tProvider  string\n\tBaseURL   string\n\tAPIKey    string\n\tModel     string\n\tAPIType   string\n\tMaxTokens int\n}\n\ntype ChatMessage struct {\n\tRole    string `json:\"role\"`\n\tContent string `json:\"content\"`\n}\n\ntype ChatCompletionRequest struct {\n\tMessages    []ChatMessage `json:\"messages\"`\n\tMaxTokens   int           `json:\"max_tokens,omitempty\"`\n\tTemperature *float64      `json:\"temperature,omitempty\"`\n\tTopP        *float64      `json:\"top_p,omitempty\"`\n}\n\ntype ChatCompletionResponse struct {\n\tID      string        `json:\"id\"`\n\tModel   string        `json:\"model\"`\n\tContent string        `json:\"content\"`\n\tRawText string        `json:\"rawText\"`\n\tUsage   ResponseUsage `json:\"usage\"`\n}\n\ntype ResponseUsage struct {\n\tPromptTokens     int `json:\"promptTokens\"`\n\tCompletionTokens int `json:\"completionTokens\"`\n\tTotalTokens      int `json:\"totalTokens\"`\n}\n\ntype terminalAIClient struct {\n\tconfig     ClientConfig\n\thttpClient *http.Client\n}\n\nfunc NewClient(cfg ClientConfig) (Client, error) {\n\tproviderKey := strings.ToLower(strings.TrimSpace(cfg.Provider))\n\tif providerKey == \"\" {\n\t\tproviderKey = \"custom\"\n\t}\n\tif strings.TrimSpace(cfg.Model) == \"\" {\n\t\treturn nil, fmt.Errorf(\"model is required\")\n\t}\n\tif strings.TrimSpace(cfg.APIKey) == \"\" && providerKey != \"ollama\" {\n\t\treturn nil, fmt.Errorf(\"api key is required\")\n\t}\n\tbaseURL := normalizeBaseURL(providerKey, cfg.BaseURL)\n\tif baseURL == \"\" {\n\t\treturn nil, fmt.Errorf(\"base url is required\")\n\t}\n\tcfg.Provider = providerKey\n\tcfg.BaseURL = baseURL\n\tcfg.APIType = normalizeClientAPIType(providerKey, cfg.APIType)\n\tif cfg.Timeout <= 0 {\n\t\tcfg.Timeout = defaultTimeout\n\t}\n\tclient := cfg.HTTPClient\n\tif client == nil {\n\t\tclient = &http.Client{Timeout: cfg.Timeout}\n\t}\n\treturn &terminalAIClient{\n\t\tconfig:     cfg,\n\t\thttpClient: client,\n\t}, nil\n}\n\ntype ClientOption func(*ClientConfig)\n\nfunc WithBaseURL(baseURL string) ClientOption {\n\treturn func(cfg *ClientConfig) {\n\t\tcfg.BaseURL = baseURL\n\t}\n}\n\nfunc WithTimeout(timeout time.Duration) ClientOption {\n\treturn func(cfg *ClientConfig) {\n\t\tcfg.Timeout = timeout\n\t}\n}\n\nfunc WithHTTPClient(client *http.Client) ClientOption {\n\treturn func(cfg *ClientConfig) {\n\t\tcfg.HTTPClient = client\n\t}\n}\n\nfunc (c *terminalAIClient) ChatCompletion(ctx context.Context, req ChatCompletionRequest) (*ChatCompletionResponse, error) {\n\tif len(req.Messages) == 0 {\n\t\treturn nil, fmt.Errorf(\"messages are required\")\n\t}\n\tswitch c.config.APIType {\n\tcase \"anthropic-messages\":\n\t\treturn c.chatCompletionAnthropic(ctx, req)\n\tcase \"gemini-generate-content\":\n\t\treturn c.chatCompletionGemini(ctx, req)\n\tcase \"openai-responses\":\n\t\treturn c.chatCompletionOpenAIResponses(ctx, req)\n\tdefault:\n\t\treturn c.chatCompletionOpenAI(ctx, req)\n\t}\n}\n\nfunc (c *terminalAIClient) chatCompletionOpenAI(ctx context.Context, req ChatCompletionRequest) (*ChatCompletionResponse, error) {\n\tpayload := openAIChatCompletionRequest{\n\t\tModel:       normalizeModelID(c.config.Model),\n\t\tMessages:    req.Messages,\n\t\tMaxTokens:   firstPositive(req.MaxTokens, c.config.MaxTokens),\n\t\tTemperature: req.Temperature,\n\t\tTopP:        req.TopP,\n\t}\n\tbody, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal request: %w\", err)\n\t}\n\thttpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, buildChatCompletionsURL(c.config.BaseURL), bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create request: %w\", err)\n\t}\n\thttpReq.Header.Set(\"Authorization\", \"Bearer \"+strings.TrimSpace(c.config.APIKey))\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\thttpReq.Header.Set(\"Accept\", \"application/json\")\n\thttpReq.Header.Set(\"User-Agent\", defaultUserAgent)\n\n\trespBody, err := c.do(httpReq)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar completionResp openAIChatCompletionResponse\n\tif err := json.Unmarshal(respBody, &completionResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode response: %w\", err)\n\t}\n\tcontent := extractAssistantContent(completionResp)\n\treturn &ChatCompletionResponse{\n\t\tID:      completionResp.ID,\n\t\tModel:   firstNonEmptyString(c.config.Model, completionResp.Model),\n\t\tContent: content,\n\t\tRawText: strings.TrimSpace(string(respBody)),\n\t\tUsage: ResponseUsage{\n\t\t\tPromptTokens:     completionResp.Usage.PromptTokens,\n\t\t\tCompletionTokens: completionResp.Usage.CompletionTokens,\n\t\t\tTotalTokens:      completionResp.Usage.TotalTokens,\n\t\t},\n\t}, nil\n}\n\nfunc (c *terminalAIClient) chatCompletionOpenAIResponses(ctx context.Context, req ChatCompletionRequest) (*ChatCompletionResponse, error) {\n\tpayload := openAIResponsesRequest{\n\t\tModel:           normalizeModelID(c.config.Model),\n\t\tInput:           toResponsesInput(req.Messages),\n\t\tMaxOutputTokens: firstPositive(req.MaxTokens, c.config.MaxTokens),\n\t\tTemperature:     req.Temperature,\n\t\tTopP:            req.TopP,\n\t}\n\tbody, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal request: %w\", err)\n\t}\n\thttpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, buildResponsesURL(c.config.BaseURL), bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create request: %w\", err)\n\t}\n\tif strings.TrimSpace(c.config.APIKey) != \"\" {\n\t\thttpReq.Header.Set(\"Authorization\", \"Bearer \"+strings.TrimSpace(c.config.APIKey))\n\t}\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\thttpReq.Header.Set(\"Accept\", \"application/json\")\n\thttpReq.Header.Set(\"User-Agent\", defaultUserAgent)\n\n\trespBody, err := c.do(httpReq)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar completionResp openAIResponsesResponse\n\tif err := json.Unmarshal(respBody, &completionResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode response: %w\", err)\n\t}\n\tcontent := extractResponsesContent(completionResp)\n\treturn &ChatCompletionResponse{\n\t\tID:      completionResp.ID,\n\t\tModel:   firstNonEmptyString(c.config.Model, completionResp.Model),\n\t\tContent: content,\n\t\tRawText: strings.TrimSpace(string(respBody)),\n\t\tUsage: ResponseUsage{\n\t\t\tPromptTokens:     completionResp.Usage.InputTokens,\n\t\t\tCompletionTokens: completionResp.Usage.OutputTokens,\n\t\t\tTotalTokens:      completionResp.Usage.TotalTokens,\n\t\t},\n\t}, nil\n}\n\nfunc (c *terminalAIClient) chatCompletionAnthropic(ctx context.Context, req ChatCompletionRequest) (*ChatCompletionResponse, error) {\n\tsystem, messages := toAnthropicMessages(req.Messages)\n\tpayload := anthropicMessagesRequest{\n\t\tModel:       normalizeModelID(c.config.Model),\n\t\tSystem:      system,\n\t\tMessages:    messages,\n\t\tMaxTokens:   firstPositive(req.MaxTokens, c.config.MaxTokens, defaultAnthropicToken),\n\t\tTemperature: req.Temperature,\n\t\tTopP:        req.TopP,\n\t}\n\tbody, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal request: %w\", err)\n\t}\n\thttpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, buildAnthropicMessagesURL(c.config.BaseURL), bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create request: %w\", err)\n\t}\n\thttpReq.Header.Set(\"x-api-key\", strings.TrimSpace(c.config.APIKey))\n\thttpReq.Header.Set(\"anthropic-version\", \"2023-06-01\")\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\thttpReq.Header.Set(\"Accept\", \"application/json\")\n\thttpReq.Header.Set(\"User-Agent\", defaultUserAgent)\n\n\trespBody, err := c.do(httpReq)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar completionResp anthropicMessagesResponse\n\tif err := json.Unmarshal(respBody, &completionResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode response: %w\", err)\n\t}\n\tcontent := extractAnthropicContent(completionResp)\n\treturn &ChatCompletionResponse{\n\t\tID:      completionResp.ID,\n\t\tModel:   firstNonEmptyString(c.config.Model, completionResp.Model),\n\t\tContent: content,\n\t\tRawText: strings.TrimSpace(string(respBody)),\n\t\tUsage: ResponseUsage{\n\t\t\tPromptTokens:     completionResp.Usage.InputTokens,\n\t\t\tCompletionTokens: completionResp.Usage.OutputTokens,\n\t\t\tTotalTokens:      completionResp.Usage.InputTokens + completionResp.Usage.OutputTokens,\n\t\t},\n\t}, nil\n}\n\nfunc (c *terminalAIClient) chatCompletionGemini(ctx context.Context, req ChatCompletionRequest) (*ChatCompletionResponse, error) {\n\tpayload := geminiGenerateContentRequest{\n\t\tContents: toGeminiContents(req.Messages),\n\t\tGenerationConfig: &geminiGenerationConfig{\n\t\t\tMaxOutputTokens: firstPositive(req.MaxTokens, c.config.MaxTokens),\n\t\t\tTemperature:     req.Temperature,\n\t\t\tTopP:            req.TopP,\n\t\t},\n\t}\n\tbody, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal request: %w\", err)\n\t}\n\thttpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, buildGeminiGenerateContentURL(c.config.BaseURL, normalizeModelID(c.config.Model)), bytes.NewReader(body))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create request: %w\", err)\n\t}\n\thttpReq.Header.Set(\"x-goog-api-key\", strings.TrimSpace(c.config.APIKey))\n\thttpReq.Header.Set(\"Content-Type\", \"application/json\")\n\thttpReq.Header.Set(\"Accept\", \"application/json\")\n\thttpReq.Header.Set(\"User-Agent\", defaultUserAgent)\n\n\trespBody, err := c.do(httpReq)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar completionResp geminiGenerateContentResponse\n\tif err := json.Unmarshal(respBody, &completionResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode response: %w\", err)\n\t}\n\tcontent := extractGeminiContent(completionResp)\n\treturn &ChatCompletionResponse{\n\t\tModel:   firstNonEmptyString(c.config.Model, completionResp.ModelVersion),\n\t\tContent: content,\n\t\tRawText: strings.TrimSpace(string(respBody)),\n\t\tUsage: ResponseUsage{\n\t\t\tPromptTokens:     completionResp.Usage.PromptTokenCount,\n\t\t\tCompletionTokens: completionResp.Usage.CandidatesTokenCount,\n\t\t\tTotalTokens:      completionResp.Usage.TotalTokenCount,\n\t\t},\n\t}, nil\n}\n\nfunc (c *terminalAIClient) do(httpReq *http.Request) ([]byte, error) {\n\tresp, err := c.httpClient.Do(httpReq)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"do request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read response: %w\", err)\n\t}\n\tif resp.StatusCode >= http.StatusBadRequest {\n\t\treturn nil, parseProviderError(resp.StatusCode, respBody)\n\t}\n\treturn respBody, nil\n}\n\ntype openAIChatCompletionRequest struct {\n\tModel       string        `json:\"model\"`\n\tMessages    []ChatMessage `json:\"messages\"`\n\tMaxTokens   int           `json:\"max_tokens,omitempty\"`\n\tTemperature *float64      `json:\"temperature,omitempty\"`\n\tTopP        *float64      `json:\"top_p,omitempty\"`\n}\n\ntype openAIChatCompletionResponse struct {\n\tID      string `json:\"id\"`\n\tModel   string `json:\"model\"`\n\tChoices []struct {\n\t\tMessage ChatMessage `json:\"message\"`\n\t\tText    string      `json:\"text\"`\n\t} `json:\"choices\"`\n\tUsage struct {\n\t\tPromptTokens     int `json:\"prompt_tokens\"`\n\t\tCompletionTokens int `json:\"completion_tokens\"`\n\t\tTotalTokens      int `json:\"total_tokens\"`\n\t} `json:\"usage\"`\n}\n\ntype openAIResponsesRequest struct {\n\tModel           string                 `json:\"model\"`\n\tInput           []openAIResponsesInput `json:\"input\"`\n\tMaxOutputTokens int                    `json:\"max_output_tokens,omitempty\"`\n\tTemperature     *float64               `json:\"temperature,omitempty\"`\n\tTopP            *float64               `json:\"top_p,omitempty\"`\n}\n\ntype openAIResponsesInput struct {\n\tRole    string                     `json:\"role\"`\n\tContent []openAIResponsesInputPart `json:\"content\"`\n}\n\ntype openAIResponsesInputPart struct {\n\tType string `json:\"type\"`\n\tText string `json:\"text\"`\n}\n\ntype openAIResponsesResponse struct {\n\tID     string `json:\"id\"`\n\tModel  string `json:\"model\"`\n\tOutput []struct {\n\t\tContent []struct {\n\t\t\tType string `json:\"type\"`\n\t\t\tText string `json:\"text\"`\n\t\t} `json:\"content\"`\n\t} `json:\"output\"`\n\tOutputText string `json:\"output_text\"`\n\tUsage      struct {\n\t\tInputTokens  int `json:\"input_tokens\"`\n\t\tOutputTokens int `json:\"output_tokens\"`\n\t\tTotalTokens  int `json:\"total_tokens\"`\n\t} `json:\"usage\"`\n}\n\ntype anthropicMessagesRequest struct {\n\tModel       string             `json:\"model\"`\n\tSystem      string             `json:\"system,omitempty\"`\n\tMessages    []anthropicMessage `json:\"messages\"`\n\tMaxTokens   int                `json:\"max_tokens\"`\n\tTemperature *float64           `json:\"temperature,omitempty\"`\n\tTopP        *float64           `json:\"top_p,omitempty\"`\n}\n\ntype anthropicMessage struct {\n\tRole    string `json:\"role\"`\n\tContent string `json:\"content\"`\n}\n\ntype anthropicMessagesResponse struct {\n\tID      string `json:\"id\"`\n\tModel   string `json:\"model\"`\n\tContent []struct {\n\t\tType string `json:\"type\"`\n\t\tText string `json:\"text\"`\n\t} `json:\"content\"`\n\tUsage struct {\n\t\tInputTokens  int `json:\"input_tokens\"`\n\t\tOutputTokens int `json:\"output_tokens\"`\n\t} `json:\"usage\"`\n}\n\ntype geminiGenerateContentRequest struct {\n\tContents         []geminiContent         `json:\"contents\"`\n\tGenerationConfig *geminiGenerationConfig `json:\"generationConfig,omitempty\"`\n}\n\ntype geminiContent struct {\n\tRole  string              `json:\"role,omitempty\"`\n\tParts []geminiContentPart `json:\"parts\"`\n}\n\ntype geminiContentPart struct {\n\tText string `json:\"text\"`\n}\n\ntype geminiGenerationConfig struct {\n\tMaxOutputTokens int      `json:\"maxOutputTokens,omitempty\"`\n\tTemperature     *float64 `json:\"temperature,omitempty\"`\n\tTopP            *float64 `json:\"topP,omitempty\"`\n}\n\ntype geminiGenerateContentResponse struct {\n\tCandidates []struct {\n\t\tContent geminiContent `json:\"content\"`\n\t} `json:\"candidates\"`\n\tModelVersion string `json:\"modelVersion\"`\n\tUsage        struct {\n\t\tPromptTokenCount     int `json:\"promptTokenCount\"`\n\t\tCandidatesTokenCount int `json:\"candidatesTokenCount\"`\n\t\tTotalTokenCount      int `json:\"totalTokenCount\"`\n\t} `json:\"usageMetadata\"`\n}\n\ntype openAIErrorResponse struct {\n\tError struct {\n\t\tMessage string `json:\"message\"`\n\t\tType    string `json:\"type\"`\n\t\tCode    string `json:\"code\"`\n\t} `json:\"error\"`\n}\n\nfunc normalizeBaseURL(provider, rawBaseURL string) string {\n\tbaseURL := strings.TrimSpace(rawBaseURL)\n\tif baseURL == \"\" {\n\t\tdefaultBaseURL, ok := providercatalog.DefaultBaseURL(provider)\n\t\tif ok {\n\t\t\tbaseURL = defaultBaseURL\n\t\t}\n\t}\n\treturn strings.TrimRight(baseURL, \"/\")\n}\n\nfunc buildChatCompletionsURL(baseURL string) string {\n\tbaseURL = strings.TrimRight(strings.TrimSpace(baseURL), \"/\")\n\tif strings.HasSuffix(baseURL, \"/chat/completions\") {\n\t\treturn baseURL\n\t}\n\tparsed, err := url.Parse(baseURL)\n\tif err != nil {\n\t\treturn baseURL + \"/chat/completions\"\n\t}\n\tswitch {\n\tcase strings.HasSuffix(parsed.Path, \"/v1\"):\n\t\tparsed.Path += \"/chat/completions\"\n\tcase strings.HasSuffix(parsed.Path, \"/v1beta\"):\n\t\tparsed.Path += \"/chat/completions\"\n\tdefault:\n\t\tparsed.Path = strings.TrimRight(parsed.Path, \"/\") + \"/v1/chat/completions\"\n\t}\n\treturn strings.TrimRight(parsed.String(), \"/\")\n}\n\nfunc buildResponsesURL(baseURL string) string {\n\tbaseURL = strings.TrimRight(strings.TrimSpace(baseURL), \"/\")\n\tif strings.HasSuffix(baseURL, \"/responses\") {\n\t\treturn baseURL\n\t}\n\tparsed, err := url.Parse(baseURL)\n\tif err != nil {\n\t\treturn baseURL + \"/responses\"\n\t}\n\tif strings.HasSuffix(parsed.Path, \"/v1\") {\n\t\tparsed.Path += \"/responses\"\n\t} else {\n\t\tparsed.Path = strings.TrimRight(parsed.Path, \"/\") + \"/v1/responses\"\n\t}\n\treturn strings.TrimRight(parsed.String(), \"/\")\n}\n\nfunc buildAnthropicMessagesURL(baseURL string) string {\n\tbaseURL = strings.TrimRight(strings.TrimSpace(baseURL), \"/\")\n\tif strings.HasSuffix(baseURL, \"/messages\") {\n\t\treturn baseURL\n\t}\n\tparsed, err := url.Parse(baseURL)\n\tif err != nil {\n\t\treturn baseURL + \"/messages\"\n\t}\n\tif strings.HasSuffix(parsed.Path, \"/v1\") {\n\t\tparsed.Path += \"/messages\"\n\t} else {\n\t\tparsed.Path = strings.TrimRight(parsed.Path, \"/\") + \"/v1/messages\"\n\t}\n\treturn strings.TrimRight(parsed.String(), \"/\")\n}\n\nfunc buildGeminiGenerateContentURL(baseURL, model string) string {\n\tbaseURL = strings.TrimRight(strings.TrimSpace(baseURL), \"/\")\n\tmodel = strings.TrimSpace(model)\n\tif model == \"\" {\n\t\tmodel = \"gemini-3-flash-preview\"\n\t}\n\tparsed, err := url.Parse(baseURL)\n\tif err != nil {\n\t\treturn strings.TrimRight(baseURL, \"/\") + \"/v1beta/models/\" + model + \":generateContent\"\n\t}\n\tif strings.Contains(parsed.Path, \"/models/\") && strings.HasSuffix(parsed.Path, \":generateContent\") {\n\t\treturn parsed.String()\n\t}\n\tif strings.Contains(parsed.Path, \"/v1beta\") {\n\t\tparsed.Path = strings.TrimRight(parsed.Path, \"/\") + \"/models/\" + model + \":generateContent\"\n\t} else {\n\t\tparsed.Path = strings.TrimRight(parsed.Path, \"/\") + \"/v1beta/models/\" + model + \":generateContent\"\n\t}\n\treturn parsed.String()\n}\n\nfunc normalizeModelID(model string) string {\n\tmodel = strings.TrimSpace(model)\n\tif parts := strings.SplitN(model, \"/\", 2); len(parts) == 2 {\n\t\treturn parts[1]\n\t}\n\treturn model\n}\n\nfunc normalizeClientAPIType(provider, apiType string) string {\n\tswitch strings.ToLower(strings.TrimSpace(provider)) {\n\tcase \"anthropic\", \"kimi-coding\", \"minimax\":\n\t\treturn \"anthropic-messages\"\n\tcase \"gemini\":\n\t\treturn \"gemini-generate-content\"\n\tcase \"ollama\":\n\t\ttrim := strings.ToLower(strings.TrimSpace(apiType))\n\t\tif trim == \"openai-completions\" {\n\t\t\treturn trim\n\t\t}\n\t\treturn \"openai-responses\"\n\tdefault:\n\t\ttrim := strings.ToLower(strings.TrimSpace(apiType))\n\t\tif trim == \"\" {\n\t\t\treturn \"openai-completions\"\n\t\t}\n\t\treturn trim\n\t}\n}\n\nfunc extractAssistantContent(resp openAIChatCompletionResponse) string {\n\tif len(resp.Choices) == 0 {\n\t\treturn \"\"\n\t}\n\tif content := strings.TrimSpace(resp.Choices[0].Message.Content); content != \"\" {\n\t\treturn content\n\t}\n\treturn strings.TrimSpace(resp.Choices[0].Text)\n}\n\nfunc extractResponsesContent(resp openAIResponsesResponse) string {\n\tif text := strings.TrimSpace(resp.OutputText); text != \"\" {\n\t\treturn text\n\t}\n\tfor _, item := range resp.Output {\n\t\tfor _, content := range item.Content {\n\t\t\tif strings.TrimSpace(content.Text) != \"\" {\n\t\t\t\treturn strings.TrimSpace(content.Text)\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc extractAnthropicContent(resp anthropicMessagesResponse) string {\n\tvar parts []string\n\tfor _, item := range resp.Content {\n\t\tif strings.TrimSpace(item.Text) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tparts = append(parts, strings.TrimSpace(item.Text))\n\t}\n\treturn strings.TrimSpace(strings.Join(parts, \"\\n\"))\n}\n\nfunc extractGeminiContent(resp geminiGenerateContentResponse) string {\n\tif len(resp.Candidates) == 0 {\n\t\treturn \"\"\n\t}\n\tvar parts []string\n\tfor _, part := range resp.Candidates[0].Content.Parts {\n\t\tif strings.TrimSpace(part.Text) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tparts = append(parts, strings.TrimSpace(part.Text))\n\t}\n\treturn strings.TrimSpace(strings.Join(parts, \"\\n\"))\n}\n\nfunc toResponsesInput(messages []ChatMessage) []openAIResponsesInput {\n\tresult := make([]openAIResponsesInput, 0, len(messages))\n\tfor _, message := range messages {\n\t\tif strings.TrimSpace(message.Content) == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tresult = append(result, openAIResponsesInput{\n\t\t\tRole: normalizeRole(message.Role),\n\t\t\tContent: []openAIResponsesInputPart{{\n\t\t\t\tType: \"input_text\",\n\t\t\t\tText: message.Content,\n\t\t\t}},\n\t\t})\n\t}\n\treturn result\n}\n\nfunc toAnthropicMessages(messages []ChatMessage) (string, []anthropicMessage) {\n\tvar systemParts []string\n\tresult := make([]anthropicMessage, 0, len(messages))\n\tfor _, message := range messages {\n\t\tcontent := strings.TrimSpace(message.Content)\n\t\tif content == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\trole := normalizeRole(message.Role)\n\t\tif role == \"system\" {\n\t\t\tsystemParts = append(systemParts, content)\n\t\t\tcontinue\n\t\t}\n\t\tif role != \"assistant\" {\n\t\t\trole = \"user\"\n\t\t}\n\t\tresult = append(result, anthropicMessage{\n\t\t\tRole:    role,\n\t\t\tContent: content,\n\t\t})\n\t}\n\tif len(result) == 0 && len(systemParts) > 0 {\n\t\tresult = append(result, anthropicMessage{\n\t\t\tRole:    \"user\",\n\t\t\tContent: \"Generate one shell command only.\",\n\t\t})\n\t}\n\treturn strings.Join(systemParts, \"\\n\\n\"), result\n}\n\nfunc toGeminiContents(messages []ChatMessage) []geminiContent {\n\tresult := make([]geminiContent, 0, len(messages))\n\tfor _, message := range messages {\n\t\tcontent := strings.TrimSpace(message.Content)\n\t\tif content == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\trole := normalizeRole(message.Role)\n\t\tif role == \"assistant\" {\n\t\t\trole = \"model\"\n\t\t} else {\n\t\t\trole = \"user\"\n\t\t}\n\t\tresult = append(result, geminiContent{\n\t\t\tRole: role,\n\t\t\tParts: []geminiContentPart{{\n\t\t\t\tText: content,\n\t\t\t}},\n\t\t})\n\t}\n\treturn result\n}\n\nfunc normalizeRole(role string) string {\n\tswitch strings.ToLower(strings.TrimSpace(role)) {\n\tcase \"assistant\", \"model\":\n\t\treturn \"assistant\"\n\tcase \"system\":\n\t\treturn \"system\"\n\tdefault:\n\t\treturn \"user\"\n\t}\n}\n\nfunc parseProviderError(statusCode int, body []byte) error {\n\tvar errResp openAIErrorResponse\n\tif err := json.Unmarshal(body, &errResp); err == nil && strings.TrimSpace(errResp.Error.Message) != \"\" {\n\t\tif strings.TrimSpace(errResp.Error.Code) != \"\" {\n\t\t\treturn fmt.Errorf(\"provider returned %d: %s (%s)\", statusCode, errResp.Error.Message, errResp.Error.Code)\n\t\t}\n\t\treturn fmt.Errorf(\"provider returned %d: %s\", statusCode, errResp.Error.Message)\n\t}\n\n\tvar generic map[string]interface{}\n\tif err := json.Unmarshal(body, &generic); err == nil {\n\t\tif message := extractErrorMessage(generic); message != \"\" {\n\t\t\treturn fmt.Errorf(\"provider returned %d: %s\", statusCode, message)\n\t\t}\n\t}\n\n\tmessage := strings.TrimSpace(string(body))\n\tif message == \"\" {\n\t\tmessage = http.StatusText(statusCode)\n\t}\n\tif !utf8.ValidString(message) {\n\t\tmessage = http.StatusText(statusCode)\n\t}\n\treturn fmt.Errorf(\"provider returned %d: %s\", statusCode, message)\n}\n\nfunc extractErrorMessage(value map[string]interface{}) string {\n\tif errorValue, ok := value[\"error\"]; ok {\n\t\tswitch typed := errorValue.(type) {\n\t\tcase string:\n\t\t\treturn strings.TrimSpace(typed)\n\t\tcase map[string]interface{}:\n\t\t\tif msg, ok := typed[\"message\"].(string); ok {\n\t\t\t\treturn strings.TrimSpace(msg)\n\t\t\t}\n\t\t\tif msg, ok := typed[\"status\"].(string); ok {\n\t\t\t\treturn strings.TrimSpace(msg)\n\t\t\t}\n\t\t}\n\t}\n\tif msg, ok := value[\"message\"].(string); ok {\n\t\treturn strings.TrimSpace(msg)\n\t}\n\treturn \"\"\n}\n\nfunc firstPositive(values ...int) int {\n\tfor _, value := range values {\n\t\tif value > 0 {\n\t\t\treturn value\n\t\t}\n\t}\n\treturn 0\n}\n\nfunc firstNonEmptyString(values ...string) string {\n\tfor _, value := range values {\n\t\tif strings.TrimSpace(value) != \"\" {\n\t\t\treturn strings.TrimSpace(value)\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "agent/utils/terminal/ai/command_generator.go",
    "content": "package ai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype CommandGenerator struct {\n\tclient Client\n}\n\ntype CommandGenerateRequest struct {\n\tInput          string\n\tShell          string\n\tWorkingDir     string\n\tOS             string\n\tRecentCommands []string\n\tDirectoryHints []string\n}\n\ntype CommandGenerateResponse struct {\n\tCommand  string\n\tModel    string\n\tProvider string\n\tRawText  string\n\tUsage    ResponseUsage\n}\n\nfunc NewCommandGeneratorFromConfig(cfg GeneratorConfig) (*CommandGenerator, error) {\n\tclient, err := NewClient(ClientConfig{\n\t\tProvider:  cfg.Provider,\n\t\tBaseURL:   cfg.BaseURL,\n\t\tAPIKey:    cfg.APIKey,\n\t\tModel:     cfg.Model,\n\t\tAPIType:   cfg.APIType,\n\t\tMaxTokens: cfg.MaxTokens,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewCommandGenerator(client)\n}\n\nfunc NewCommandGenerator(client Client) (*CommandGenerator, error) {\n\tif client == nil {\n\t\treturn nil, fmt.Errorf(\"client is required\")\n\t}\n\treturn &CommandGenerator{client: client}, nil\n}\n\nfunc (g *CommandGenerator) Generate(ctx context.Context, req CommandGenerateRequest) (*CommandGenerateResponse, error) {\n\tif strings.TrimSpace(req.Input) == \"\" {\n\t\treturn nil, fmt.Errorf(\"input is required\")\n\t}\n\n\tresp, err := g.client.ChatCompletion(ctx, ChatCompletionRequest{\n\t\tMessages: []ChatMessage{\n\t\t\t{Role: \"system\", Content: buildCommandSystemPrompt()},\n\t\t\t{Role: \"user\", Content: buildCommandUserPrompt(req)},\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcommand := sanitizeCommand(resp.Content)\n\tif command == \"\" {\n\t\treturn nil, fmt.Errorf(\"model returned empty command\")\n\t}\n\n\treturn &CommandGenerateResponse{\n\t\tCommand:  command,\n\t\tModel:    resp.Model,\n\t\tProvider: providerNameFromModel(resp.Model),\n\t\tRawText:  resp.RawText,\n\t\tUsage:    resp.Usage,\n\t}, nil\n}\n\nfunc buildCommandSystemPrompt() string {\n\treturn strings.Join([]string{\n\t\t\"You are a shell command generator.\",\n\t\t\"Return exactly one command suitable for direct execution in the user's shell.\",\n\t\t\"Do not include markdown, code fences, explanations, numbering, comments, or backticks.\",\n\t\t\"If multiple commands are required, join them with shell operators in a single line.\",\n\t\t\"Prefer safe, non-destructive commands unless the user explicitly asks for destructive behavior.\",\n\t\t\"Preserve the user's language when filenames or arguments are ambiguous, but output only the command.\",\n\t}, \"\\n\")\n}\n\nfunc buildCommandUserPrompt(req CommandGenerateRequest) string {\n\tvar sections []string\n\tsections = append(sections, \"Task:\\n\"+strings.TrimSpace(req.Input))\n\n\tvar env []string\n\tif shell := strings.TrimSpace(req.Shell); shell != \"\" {\n\t\tenv = append(env, \"Shell: \"+shell)\n\t}\n\tif wd := strings.TrimSpace(req.WorkingDir); wd != \"\" {\n\t\tenv = append(env, \"Working directory: \"+wd)\n\t}\n\tif osName := strings.TrimSpace(req.OS); osName != \"\" {\n\t\tenv = append(env, \"Operating system: \"+osName)\n\t}\n\tif len(env) > 0 {\n\t\tsections = append(sections, \"Environment:\\n\"+strings.Join(env, \"\\n\"))\n\t}\n\n\tif block := formatBulletBlock(req.DirectoryHints); block != \"\" {\n\t\tsections = append(sections, \"Directory hints:\\n\"+block)\n\t}\n\tif block := formatBulletBlock(req.RecentCommands); block != \"\" {\n\t\tsections = append(sections, \"Recent commands:\\n\"+block)\n\t}\n\n\tsections = append(sections, \"Output requirement:\\nReturn one shell command only.\")\n\treturn strings.Join(sections, \"\\n\\n\")\n}\n\nfunc formatBulletBlock(values []string) string {\n\tvar lines []string\n\tfor _, value := range values {\n\t\tvalue = strings.TrimSpace(value)\n\t\tif value == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tlines = append(lines, \"- \"+value)\n\t}\n\treturn strings.Join(lines, \"\\n\")\n}\n\nfunc sanitizeCommand(raw string) string {\n\tcommand := strings.TrimSpace(raw)\n\tif command == \"\" {\n\t\treturn \"\"\n\t}\n\tcommand = strings.TrimPrefix(command, \"```sh\")\n\tcommand = strings.TrimPrefix(command, \"```bash\")\n\tcommand = strings.TrimPrefix(command, \"```zsh\")\n\tcommand = strings.TrimPrefix(command, \"```shell\")\n\tcommand = strings.TrimPrefix(command, \"```\")\n\tcommand = strings.TrimSuffix(command, \"```\")\n\tcommand = strings.TrimSpace(command)\n\n\tlines := strings.Split(command, \"\\n\")\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(line, \"#\") {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.HasPrefix(strings.ToLower(line), \"command:\") {\n\t\t\tline = strings.TrimSpace(line[len(\"command:\"):])\n\t\t}\n\t\treturn strings.Trim(line, \"` \")\n\t}\n\treturn \"\"\n}\n\nfunc providerNameFromModel(model string) string {\n\tmodel = strings.TrimSpace(model)\n\tif model == \"\" {\n\t\treturn \"\"\n\t}\n\tif parts := strings.SplitN(model, \"/\", 2); len(parts) == 2 {\n\t\treturn parts[0]\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "agent/utils/terminal/ai/config_runtime.go",
    "content": "package ai\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\tprovidercatalog \"github.com/1Panel-dev/1Panel/agent/app/provider\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"gorm.io/gorm\"\n)\n\nvar agentAccountRepo = repo.NewIAgentAccountRepo()\nvar agentAccountModelRepo = repo.NewIAgentAccountModelRepo()\n\ntype TerminalRuntimeSettings struct {\n\tAccountID    uint\n\tPrefix       string\n\tRiskCommands []string\n}\n\nvar defaultRiskCommands = []string{\n\t\"rm -rf\",\n\t\"mkfs\",\n\t\"dd if=\",\n\t\"curl | sh\",\n\t\"wget | sh\",\n\t\"chmod -R 777 /\",\n\t\"shutdown\",\n\t\"reboot\",\n\t\"poweroff\",\n\t\"init 0\",\n\t\":(){ :|:& };:\",\n}\n\nfunc ResolveGeneratorConfig(accountID uint) (GeneratorConfig, time.Duration, error) {\n\taccount, err := loadAgentAccount(accountID)\n\tif err != nil {\n\t\treturn GeneratorConfig{}, 0, err\n\t}\n\n\tprovider := strings.ToLower(strings.TrimSpace(account.Provider))\n\tif provider == \"\" {\n\t\treturn GeneratorConfig{}, 0, fmt.Errorf(\"agent account provider is required\")\n\t}\n\tmodel, maxTokens, err := resolveAccountModelConfig(account.ID, provider)\n\tif err != nil {\n\t\treturn GeneratorConfig{}, 0, err\n\t}\n\tbaseURL := strings.TrimSpace(account.BaseURL)\n\tif baseURL == \"\" {\n\t\tif defaultURL, ok := providercatalog.DefaultBaseURL(provider); ok {\n\t\t\tbaseURL = defaultURL\n\t\t}\n\t}\n\tapiKey := strings.TrimSpace(account.APIKey)\n\tif apiKey == \"\" {\n\t\tapiKey = lookupProviderAPIKey(provider)\n\t}\n\tif apiKey == \"\" && provider != \"ollama\" {\n\t\treturn GeneratorConfig{}, 0, fmt.Errorf(\"agent account api key is required\")\n\t}\n\treturn GeneratorConfig{\n\t\tProvider:  provider,\n\t\tBaseURL:   baseURL,\n\t\tAPIKey:    strings.TrimSpace(apiKey),\n\t\tModel:     model,\n\t\tAPIType:   strings.TrimSpace(account.APIType),\n\t\tMaxTokens: maxTokens,\n\t}, 30 * time.Second, nil\n}\n\nfunc lookupProviderAPIKey(provider string) string {\n\tenvKey := providercatalog.EnvKey(provider)\n\tif envKey == \"\" {\n\t\treturn \"\"\n\t}\n\treturn strings.TrimSpace(os.Getenv(envKey))\n}\n\nfunc defaultModelForProvider(provider string) (string, int) {\n\tmeta, ok := providercatalog.Get(provider)\n\tif !ok || len(meta.Models) == 0 {\n\t\treturn \"\", 0\n\t}\n\treturn meta.Models[0].ID, meta.Models[0].MaxTokens\n}\n\nfunc resolveAccountModelConfig(accountID uint, provider string) (string, int, error) {\n\tif accountID > 0 {\n\t\trows, err := agentAccountModelRepo.List(repo.WithByAccountID(accountID), repo.WithOrderAsc(\"sort_order\"), repo.WithOrderAsc(\"id\"))\n\t\tif err != nil {\n\t\t\treturn \"\", 0, err\n\t\t}\n\t\tif len(rows) > 0 {\n\t\t\treturn strings.TrimSpace(rows[0].Model), rows[0].MaxTokens, nil\n\t\t}\n\t}\n\tmodel, maxTokens := defaultModelForProvider(provider)\n\treturn model, maxTokens, nil\n}\n\nfunc ResolveGeneratorConfigFromAgentSettings() (GeneratorConfig, uint, time.Duration, error) {\n\tstatus, err := loadAgentSettingValue(\"AIStatus\")\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn GeneratorConfig{}, 0, 0, err\n\t}\n\tif !strings.EqualFold(strings.TrimSpace(status), \"Enable\") {\n\t\treturn GeneratorConfig{}, 0, 0, os.ErrNotExist\n\t}\n\taccountValue, err := loadAgentSettingValue(\"AIAccountID\")\n\tif err != nil {\n\t\treturn GeneratorConfig{}, 0, 0, err\n\t}\n\taccountID, err := strconv.ParseUint(strings.TrimSpace(accountValue), 10, 64)\n\tif err != nil || accountID == 0 {\n\t\treturn GeneratorConfig{}, 0, 0, os.ErrNotExist\n\t}\n\tconfig, timeout, err := ResolveGeneratorConfig(uint(accountID))\n\treturn config, uint(accountID), timeout, err\n}\n\nfunc LoadTerminalRuntimeSettings() (TerminalRuntimeSettings, GeneratorConfig, time.Duration, error) {\n\tconfig, accountID, timeout, err := ResolveGeneratorConfigFromAgentSettings()\n\tif err != nil {\n\t\treturn TerminalRuntimeSettings{}, GeneratorConfig{}, 0, err\n\t}\n\tprefix, err := loadAgentSettingValue(\"AIPrefix\")\n\tif err != nil && !os.IsNotExist(err) {\n\t\treturn TerminalRuntimeSettings{}, GeneratorConfig{}, 0, err\n\t}\n\tif strings.TrimSpace(prefix) == \"\" {\n\t\tprefix = \"#\"\n\t}\n\triskCommands, err := loadRiskCommands()\n\tif err != nil {\n\t\treturn TerminalRuntimeSettings{}, GeneratorConfig{}, 0, err\n\t}\n\treturn TerminalRuntimeSettings{\n\t\tAccountID:    accountID,\n\t\tPrefix:       strings.TrimSpace(prefix),\n\t\tRiskCommands: riskCommands,\n\t}, config, timeout, nil\n}\n\nfunc loadAgentAccount(accountID uint) (*model.AgentAccount, error) {\n\tif accountID == 0 {\n\t\treturn nil, os.ErrNotExist\n\t}\n\taccount, err := agentAccountRepo.GetFirst(repo.WithByID(accountID))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn account, nil\n}\n\nfunc loadAgentSettingValue(key string) (string, error) {\n\tvar setting model.Setting\n\tif err := global.DB.Where(\"key = ?\", key).First(&setting).Error; err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn \"\", os.ErrNotExist\n\t\t}\n\t\treturn \"\", err\n\t}\n\treturn setting.Value, nil\n}\n\nfunc loadRiskCommands() ([]string, error) {\n\tvalue, err := loadAgentSettingValue(\"AIRiskCommands\")\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn append([]string(nil), defaultRiskCommands...), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tif strings.TrimSpace(value) == \"\" {\n\t\treturn append([]string(nil), defaultRiskCommands...), nil\n\t}\n\tvar commands []string\n\tif err := json.Unmarshal([]byte(value), &commands); err != nil {\n\t\treturn nil, err\n\t}\n\treturn normalizeRiskCommands(commands), nil\n}\n\nfunc normalizeRiskCommands(commands []string) []string {\n\tif len(commands) == 0 {\n\t\treturn append([]string(nil), defaultRiskCommands...)\n\t}\n\tseen := make(map[string]struct{}, len(commands))\n\tresult := make([]string, 0, len(commands))\n\tfor _, command := range commands {\n\t\tcommand = strings.TrimSpace(command)\n\t\tif command == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[command]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[command] = struct{}{}\n\t\tresult = append(result, command)\n\t}\n\tif len(result) == 0 {\n\t\treturn append([]string(nil), defaultRiskCommands...)\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "agent/utils/terminal/ai/validate.go",
    "content": "package ai\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst validateTimeout = 15 * time.Second\n\nfunc ValidateTerminalAccount(accountID uint) error {\n\tif accountID == 0 {\n\t\treturn fmt.Errorf(\"ai account is required\")\n\t}\n\n\tcfg, _, err := ResolveGeneratorConfig(accountID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient, err := NewClient(ClientConfig{\n\t\tProvider:  cfg.Provider,\n\t\tBaseURL:   cfg.BaseURL,\n\t\tAPIKey:    cfg.APIKey,\n\t\tModel:     cfg.Model,\n\t\tAPIType:   cfg.APIType,\n\t\tMaxTokens: cfg.MaxTokens,\n\t\tTimeout:   validateTimeout,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), validateTimeout)\n\tdefer cancel()\n\n\tresp, err := client.ChatCompletion(ctx, ChatCompletionRequest{\n\t\tMessages: []ChatMessage{\n\t\t\t{Role: \"system\", Content: \"You are a shell command generator. Return exactly one shell command.\"},\n\t\t\t{Role: \"user\", Content: \"Print current directory.\"},\n\t\t},\n\t\tMaxTokens: 16,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resp == nil || strings.TrimSpace(resp.Content) == \"\" {\n\t\treturn fmt.Errorf(\"terminal ai account returned empty response\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/terminal/ai_interceptor.go",
    "content": "package terminal\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\tterminalai \"github.com/1Panel-dev/1Panel/agent/utils/terminal/ai\"\n)\n\nconst lineClearControl = 21\n\ntype aiInputInterceptor struct {\n\tconfig  terminalai.GeneratorConfig\n\ttimeout time.Duration\n\tshell   string\n\tprefix  string\n\n\tmu             sync.Mutex\n\tcurrentLine    []byte\n\trecentCommands []string\n\triskCommands   []string\n\tinEscapeSeq    bool\n}\n\nfunc newAIInputInterceptor(shell string) *aiInputInterceptor {\n\tsettings, config, timeout, err := terminalai.LoadTerminalRuntimeSettings()\n\tif err != nil {\n\t\tif !os.IsNotExist(err) {\n\t\t\tglobal.LOG.Warnf(\"load terminal ai config failed: %v\", err)\n\t\t}\n\t\treturn nil\n\t}\n\tif strings.TrimSpace(config.APIKey) == \"\" && !strings.EqualFold(strings.TrimSpace(config.Provider), \"ollama\") {\n\t\treturn nil\n\t}\n\treturn &aiInputInterceptor{\n\t\tconfig:       config,\n\t\ttimeout:      timeout,\n\t\tshell:        strings.TrimSpace(shell),\n\t\tprefix:       settings.Prefix,\n\t\triskCommands: append([]string(nil), settings.RiskCommands...),\n\t}\n}\n\nfunc (i *aiInputInterceptor) refreshSettings() error {\n\tsettings, config, timeout, err := terminalai.LoadTerminalRuntimeSettings()\n\tif err != nil {\n\t\treturn err\n\t}\n\ti.mu.Lock()\n\tdefer i.mu.Unlock()\n\ti.config = config\n\ti.timeout = timeout\n\ti.prefix = settings.Prefix\n\ti.riskCommands = append([]string(nil), settings.RiskCommands...)\n\treturn nil\n}\n\nfunc (i *aiInputInterceptor) HandleEnter() (string, bool) {\n\tif i == nil {\n\t\treturn \"\", false\n\t}\n\tif err := i.refreshSettings(); err != nil {\n\t\tif !os.IsNotExist(err) {\n\t\t\tglobal.LOG.Warnf(\"refresh terminal ai config failed: %v\", err)\n\t\t}\n\t\treturn \"\", false\n\t}\n\ti.mu.Lock()\n\tline := sanitizeInputLine(string(i.currentLine))\n\ti.currentLine = nil\n\ti.inEscapeSeq = false\n\trecentCommands := append([]string(nil), i.recentCommands...)\n\ti.mu.Unlock()\n\n\tif !strings.HasPrefix(line, i.prefix) {\n\t\tif line != \"\" {\n\t\t\ti.pushRecentCommand(line)\n\t\t}\n\t\treturn \"\", false\n\t}\n\tprompt := strings.TrimSpace(strings.TrimPrefix(line, i.prefix))\n\tif prompt == \"\" {\n\t\treturn \"\", false\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), i.timeout)\n\tdefer cancel()\n\tgenerator, err := terminalai.NewCommandGeneratorFromConfig(i.config)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"create terminal ai generator failed: %v\", err)\n\t\treturn \"\", false\n\t}\n\tresp, err := generator.Generate(ctx, terminalai.CommandGenerateRequest{\n\t\tInput:          prompt,\n\t\tShell:          firstNonEmpty(i.shell, filepath.Base(strings.TrimSpace(os.Getenv(\"SHELL\")))),\n\t\tOS:             runtime.GOOS,\n\t\tRecentCommands: recentCommands,\n\t})\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"generate terminal ai command failed: %v\", err)\n\t\treturn \"\", false\n\t}\n\tif i.isRiskCommand(resp.Command) {\n\t\treturn \": # blocked risky command: \" + resp.Command, true\n\t}\n\treturn resp.Command, strings.TrimSpace(resp.Command) != \"\"\n}\n\nfunc (i *aiInputInterceptor) TrackInput(data []byte) {\n\tif i == nil || len(data) == 0 {\n\t\treturn\n\t}\n\ti.mu.Lock()\n\tdefer i.mu.Unlock()\n\tfor _, b := range data {\n\t\tif i.inEscapeSeq {\n\t\t\tif isEscapeSequenceTerminator(b) {\n\t\t\t\ti.inEscapeSeq = false\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tswitch b {\n\t\tcase '\\r', '\\n':\n\t\t\ti.currentLine = nil\n\t\tcase 0x08, 0x7f:\n\t\t\ti.currentLine = trimLastRuneBytes(i.currentLine)\n\t\tcase lineClearControl:\n\t\t\ti.currentLine = nil\n\t\tcase 0x1b:\n\t\t\t// Ignore ANSI escape sequences such as arrow keys and bracketed paste markers.\n\t\t\ti.inEscapeSeq = true\n\t\tdefault:\n\t\t\tif b < 0x20 && b != '\\t' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ti.currentLine = append(i.currentLine, b)\n\t\t}\n\t}\n}\n\nfunc (i *aiInputInterceptor) pushRecentCommand(command string) {\n\tif i == nil {\n\t\treturn\n\t}\n\tcommand = strings.TrimSpace(command)\n\tif command == \"\" || strings.HasPrefix(command, i.prefix) {\n\t\treturn\n\t}\n\ti.mu.Lock()\n\tdefer i.mu.Unlock()\n\ti.recentCommands = append([]string{command}, i.recentCommands...)\n\tif len(i.recentCommands) > 8 {\n\t\ti.recentCommands = i.recentCommands[:8]\n\t}\n}\n\nfunc isEnterInput(data []byte) bool {\n\tif len(data) == 1 && (data[0] == '\\r' || data[0] == '\\n') {\n\t\treturn true\n\t}\n\treturn len(data) == 2 && data[0] == '\\r' && data[1] == '\\n'\n}\n\nfunc firstNonEmpty(values ...string) string {\n\tfor _, value := range values {\n\t\tif strings.TrimSpace(value) != \"\" {\n\t\t\treturn strings.TrimSpace(value)\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc sanitizeInputLine(raw string) string {\n\traw = strings.TrimSpace(raw)\n\tif raw == \"\" {\n\t\treturn \"\"\n\t}\n\tvar builder strings.Builder\n\tbuilder.Grow(len(raw))\n\tfor _, r := range raw {\n\t\tswitch {\n\t\tcase unicode.IsControl(r) && r != '\\t' && r != ' ':\n\t\t\tcontinue\n\t\tcase unicode.In(r, unicode.Cf):\n\t\t\tcontinue\n\t\tcase r == '\\u00a0' || r == '\\u2007' || r == '\\u202f' || r == '\\u3000':\n\t\t\tbuilder.WriteRune(' ')\n\t\tdefault:\n\t\t\tbuilder.WriteRune(r)\n\t\t}\n\t}\n\treturn strings.TrimSpace(builder.String())\n}\n\nfunc trimLastRuneBytes(data []byte) []byte {\n\tif len(data) == 0 {\n\t\treturn data\n\t}\n\t_, size := utf8.DecodeLastRune(data)\n\tif size <= 0 || size > len(data) {\n\t\treturn data[:len(data)-1]\n\t}\n\treturn data[:len(data)-size]\n}\n\nfunc isEscapeSequenceTerminator(b byte) bool {\n\treturn b >= 0x40 && b <= 0x7e\n}\n\nfunc (i *aiInputInterceptor) isRiskCommand(command string) bool {\n\tcommand = strings.ToLower(strings.TrimSpace(command))\n\tif command == \"\" {\n\t\treturn false\n\t}\n\tfor _, riskCommand := range i.riskCommands {\n\t\triskCommand = strings.ToLower(strings.TrimSpace(riskCommand))\n\t\tif riskCommand == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Contains(command, riskCommand) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "agent/utils/terminal/local_cmd.go",
    "content": "package terminal\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"syscall\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/creack/pty\"\n\t\"github.com/pkg/errors\"\n)\n\nconst (\n\tDefaultCloseSignal  = syscall.SIGINT\n\tDefaultCloseTimeout = 10 * time.Second\n)\n\ntype LocalCommand struct {\n\tcloseSignal  syscall.Signal\n\tcloseTimeout time.Duration\n\n\tcmd *exec.Cmd\n\tpty *os.File\n}\n\nfunc NewCommand(name string, arg ...string) (*LocalCommand, error) {\n\tcmd := exec.Command(name, arg...)\n\tif term := os.Getenv(\"TERM\"); term != \"\" {\n\t\tcmd.Env = append(os.Environ(), \"TERM=\"+term)\n\t} else {\n\t\tcmd.Env = append(os.Environ(), \"TERM=xterm\")\n\t}\n\thomeDir, _ := os.UserHomeDir()\n\tcmd.Dir = homeDir\n\n\tpty, err := pty.Start(cmd)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to start command\")\n\t}\n\n\tlcmd := &LocalCommand{\n\t\tcloseSignal:  DefaultCloseSignal,\n\t\tcloseTimeout: DefaultCloseTimeout,\n\n\t\tcmd: cmd,\n\t\tpty: pty,\n\t}\n\n\treturn lcmd, nil\n}\n\nfunc (lcmd *LocalCommand) Read(p []byte) (n int, err error) {\n\treturn lcmd.pty.Read(p)\n}\n\nfunc (lcmd *LocalCommand) Write(p []byte) (n int, err error) {\n\treturn lcmd.pty.Write(p)\n}\n\nfunc (lcmd *LocalCommand) Close() error {\n\tif lcmd.pty != nil {\n\t\tlcmd.pty.Write([]byte{3})\n\t\ttime.Sleep(50 * time.Millisecond)\n\n\t\tlcmd.pty.Write([]byte{4})\n\t\ttime.Sleep(50 * time.Millisecond)\n\n\t\tlcmd.pty.Write([]byte(\"exit\\n\"))\n\t\ttime.Sleep(50 * time.Millisecond)\n\t}\n\tif lcmd.cmd != nil && lcmd.cmd.Process != nil {\n\t\tlcmd.cmd.Process.Signal(syscall.SIGTERM)\n\t\ttime.Sleep(50 * time.Millisecond)\n\n\t\tif lcmd.cmd.ProcessState == nil || !lcmd.cmd.ProcessState.Exited() {\n\t\t\tlcmd.cmd.Process.Kill()\n\t\t}\n\t}\n\t_ = lcmd.pty.Close()\n\treturn nil\n}\n\nfunc (lcmd *LocalCommand) ResizeTerminal(width int, height int) error {\n\twindow := struct {\n\t\trow uint16\n\t\tcol uint16\n\t\tx   uint16\n\t\ty   uint16\n\t}{\n\t\tuint16(height),\n\t\tuint16(width),\n\t\t0,\n\t\t0,\n\t}\n\t_, _, errno := syscall.Syscall(\n\t\tsyscall.SYS_IOCTL,\n\t\tlcmd.pty.Fd(),\n\t\tsyscall.TIOCSWINSZ,\n\t\tuintptr(unsafe.Pointer(&window)),\n\t)\n\tif errno != 0 {\n\t\treturn errno\n\t} else {\n\t\treturn nil\n\t}\n}\n\nfunc (lcmd *LocalCommand) Wait(quitChan chan bool) {\n\tif err := lcmd.cmd.Wait(); err != nil {\n\t\tglobal.LOG.Errorf(\"ssh session wait failed, err: %v\", err)\n\t\tsetQuit(quitChan)\n\t}\n\tsetQuit(quitChan)\n}\n"
  },
  {
    "path": "agent/utils/terminal/ws_local_session.go",
    "content": "package terminal\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"sync\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/gorilla/websocket\"\n\t\"github.com/pkg/errors\"\n)\n\ntype LocalWsSession struct {\n\tslave  *LocalCommand\n\twsConn *websocket.Conn\n\n\tallowCtrlC    bool\n\twriteMutex    sync.Mutex\n\taiInterceptor *aiInputInterceptor\n}\n\nfunc NewLocalWsSession(cols, rows int, wsConn *websocket.Conn, slave *LocalCommand, allowCtrlC bool) (*LocalWsSession, error) {\n\tif err := slave.ResizeTerminal(cols, rows); err != nil {\n\t\tglobal.LOG.Errorf(\"ssh pty change windows size failed, err: %v\", err)\n\t}\n\n\treturn &LocalWsSession{\n\t\tslave:  slave,\n\t\twsConn: wsConn,\n\n\t\tallowCtrlC:    allowCtrlC,\n\t\taiInterceptor: newAIInputInterceptor(\"\"),\n\t}, nil\n}\n\nfunc (sws *LocalWsSession) Start(quitChan chan bool) {\n\tgo sws.handleSlaveEvent(quitChan)\n\tgo sws.receiveWsMsg(quitChan)\n}\n\nfunc (sws *LocalWsSession) handleSlaveEvent(exitCh chan bool) {\n\tdefer setQuit(exitCh)\n\tdefer global.LOG.Debug(\"thread of handle slave event has exited now\")\n\n\tbuffer := make([]byte, 1024)\n\tfor {\n\t\tselect {\n\t\tcase <-exitCh:\n\t\t\treturn\n\t\tdefault:\n\t\t\tn, _ := sws.slave.Read(buffer)\n\t\t\t_ = sws.masterWrite(buffer[:n])\n\t\t}\n\t}\n}\n\nfunc (sws *LocalWsSession) masterWrite(data []byte) error {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"A panic occurred during write ws message to master, error message: %v\", r)\n\t\t}\n\t}()\n\tsws.writeMutex.Lock()\n\tdefer sws.writeMutex.Unlock()\n\twsData, err := json.Marshal(WsMsg{\n\t\tType: WsMsgCmd,\n\t\tData: base64.StdEncoding.EncodeToString(data),\n\t})\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"failed to encoding to json\")\n\t}\n\terr = sws.wsConn.WriteMessage(websocket.TextMessage, wsData)\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"failed to write to master\")\n\t}\n\treturn nil\n}\n\nfunc (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tsetQuit(exitCh)\n\t\t\tglobal.LOG.Errorf(\"A panic occurred during receive ws message, error message: %v\", r)\n\t\t}\n\t}()\n\twsConn := sws.wsConn\n\tdefer setQuit(exitCh)\n\tdefer global.LOG.Debug(\"thread of receive ws msg has exited now\")\n\tfor {\n\t\tselect {\n\t\tcase <-exitCh:\n\t\t\treturn\n\t\tdefault:\n\t\t\t_, wsData, err := wsConn.ReadMessage()\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"reading webSocket message failed, err: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tmsgObj := WsMsg{}\n\t\t\t_ = json.Unmarshal(wsData, &msgObj)\n\t\t\tswitch msgObj.Type {\n\t\t\tcase WsMsgResize:\n\t\t\t\tif msgObj.Cols > 0 && msgObj.Rows > 0 {\n\t\t\t\t\tif err := sws.slave.ResizeTerminal(msgObj.Cols, msgObj.Rows); err != nil {\n\t\t\t\t\t\tglobal.LOG.Errorf(\"ssh pty change windows size failed, err: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase WsMsgCmd:\n\t\t\t\tdecodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Data)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"websock cmd string base64 decoding failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\tif isEnterInput(decodeBytes) {\n\t\t\t\t\tif generated, ok := sws.aiInterceptor.HandleEnter(); ok {\n\t\t\t\t\t\tsws.sendWebsocketInputCommandToSshSessionStdinPipe(append([]byte{lineClearControl}, []byte(generated)...))\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsws.aiInterceptor.TrackInput(decodeBytes)\n\t\t\t\t}\n\t\t\t\tsws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes)\n\t\t\tcase WsMsgHeartbeat:\n\t\t\t\terr = wsConn.WriteMessage(websocket.TextMessage, wsData)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"ssh sending heartbeat to webSocket failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (sws *LocalWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {\n\tif _, err := sws.slave.Write(cmdBytes); err != nil {\n\t\tglobal.LOG.Errorf(\"ws cmd bytes write to ssh.stdin pipe failed, err: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "agent/utils/terminal/ws_session.go",
    "content": "package terminal\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/gorilla/websocket\"\n\t\"golang.org/x/crypto/ssh\"\n)\n\ntype safeBuffer struct {\n\tbuffer bytes.Buffer\n\tmu     sync.Mutex\n}\n\nfunc (w *safeBuffer) Write(p []byte) (int, error) {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\treturn w.buffer.Write(p)\n}\nfunc (w *safeBuffer) Bytes() []byte {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\treturn w.buffer.Bytes()\n}\nfunc (w *safeBuffer) Reset() {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\tw.buffer.Reset()\n}\n\nconst (\n\tWsMsgCmd       = \"cmd\"\n\tWsMsgResize    = \"resize\"\n\tWsMsgHeartbeat = \"heartbeat\"\n)\n\ntype WsMsg struct {\n\tType      string `json:\"type\"`\n\tData      string `json:\"data,omitempty\"`      // WsMsgCmd\n\tCols      int    `json:\"cols,omitempty\"`      // WsMsgResize\n\tRows      int    `json:\"rows,omitempty\"`      // WsMsgResize\n\tTimestamp int    `json:\"timestamp,omitempty\"` // WsMsgHeartbeat\n}\n\ntype LogicSshWsSession struct {\n\tstdinPipe       io.WriteCloser\n\tcomboOutput     *safeBuffer\n\tlogBuff         *safeBuffer\n\tinputFilterBuff *safeBuffer\n\tsession         *ssh.Session\n\twsConn          *websocket.Conn\n\tisAdmin         bool\n\tIsFlagged       bool\n\taiInterceptor   *aiInputInterceptor\n}\n\nfunc NewLogicSshWsSession(cols, rows int, sshClient *ssh.Client, wsConn *websocket.Conn, initCmd string) (*LogicSshWsSession, error) {\n\tsshSession, err := sshClient.NewSession()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstdinP, err := sshSession.StdinPipe()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcomboWriter := new(safeBuffer)\n\tlogBuf := new(safeBuffer)\n\tinputBuf := new(safeBuffer)\n\tsshSession.Stdout = comboWriter\n\tsshSession.Stderr = comboWriter\n\n\tmodes := ssh.TerminalModes{\n\t\tssh.ECHO:          1,\n\t\tssh.TTY_OP_ISPEED: 14400,\n\t\tssh.TTY_OP_OSPEED: 14400,\n\t}\n\tif err := sshSession.RequestPty(\"xterm\", rows, cols, modes); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := sshSession.Shell(); err != nil {\n\t\treturn nil, err\n\t}\n\tif len(initCmd) != 0 {\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\t_, _ = stdinP.Write([]byte(initCmd + \"\\n\"))\n\t}\n\treturn &LogicSshWsSession{\n\t\tstdinPipe:       stdinP,\n\t\tcomboOutput:     comboWriter,\n\t\tlogBuff:         logBuf,\n\t\tinputFilterBuff: inputBuf,\n\t\tsession:         sshSession,\n\t\twsConn:          wsConn,\n\t\tisAdmin:         true,\n\t\tIsFlagged:       false,\n\t\taiInterceptor:   newAIInputInterceptor(\"\"),\n\t}, nil\n}\n\nfunc (sws *LogicSshWsSession) Close() {\n\tif sws.session != nil {\n\t\tsws.session.Close()\n\t}\n\tif sws.logBuff != nil {\n\t\tsws.logBuff = nil\n\t}\n\tif sws.comboOutput != nil {\n\t\tsws.comboOutput = nil\n\t}\n}\n\nfunc (sws *LogicSshWsSession) Start(quitChan chan bool) {\n\tgo sws.receiveWsMsg(quitChan)\n\tgo sws.sendComboOutput(quitChan)\n}\n\nfunc (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"[A panic occurred during receive ws message, error message: %v\", r)\n\t\t}\n\t}()\n\twsConn := sws.wsConn\n\tdefer setQuit(exitCh)\n\tfor {\n\t\tselect {\n\t\tcase <-exitCh:\n\t\t\treturn\n\t\tdefault:\n\t\t\t_, wsData, err := wsConn.ReadMessage()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tmsgObj := WsMsg{}\n\t\t\t_ = json.Unmarshal(wsData, &msgObj)\n\t\t\tswitch msgObj.Type {\n\t\t\tcase WsMsgResize:\n\t\t\t\tif msgObj.Cols > 0 && msgObj.Rows > 0 {\n\t\t\t\t\tif err := sws.session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {\n\t\t\t\t\t\tglobal.LOG.Errorf(\"ssh pty change windows size failed, err: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase WsMsgCmd:\n\t\t\t\tdecodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Data)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"websock cmd string base64 decoding failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\tif isEnterInput(decodeBytes) {\n\t\t\t\t\tif generated, ok := sws.aiInterceptor.HandleEnter(); ok {\n\t\t\t\t\t\tsws.sendWebsocketInputCommandToSshSessionStdinPipe(append([]byte{lineClearControl}, []byte(generated)...))\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsws.aiInterceptor.TrackInput(decodeBytes)\n\t\t\t\t}\n\t\t\t\tsws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes)\n\t\t\tcase WsMsgHeartbeat:\n\t\t\t\terr = wsConn.WriteMessage(websocket.TextMessage, wsData)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"ssh sending heartbeat to webSocket failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (sws *LogicSshWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {\n\tif _, err := sws.stdinPipe.Write(cmdBytes); err != nil {\n\t\tglobal.LOG.Errorf(\"ws cmd bytes write to ssh.stdin pipe failed, err: %v\", err)\n\t}\n}\n\nfunc (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) {\n\twsConn := sws.wsConn\n\tdefer setQuit(exitCh)\n\n\ttick := time.NewTicker(time.Millisecond * time.Duration(60))\n\tdefer tick.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-tick.C:\n\t\t\tif sws.comboOutput == nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tbs := sws.comboOutput.Bytes()\n\t\t\tif len(bs) > 0 {\n\t\t\t\twsData, err := json.Marshal(WsMsg{\n\t\t\t\t\tType: WsMsgCmd,\n\t\t\t\t\tData: base64.StdEncoding.EncodeToString(bs),\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"encoding combo output to json failed, err: %v\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\terr = wsConn.WriteMessage(websocket.TextMessage, wsData)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"ssh sending combo output to webSocket failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\t_, err = sws.logBuff.Write(bs)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"combo output to log buffer failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\tsws.comboOutput.buffer.Reset()\n\t\t\t}\n\t\t\tif string(bs) == string([]byte{13, 10, 108, 111, 103, 111, 117, 116, 13, 10}) {\n\t\t\t\tsws.Close()\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase <-exitCh:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (sws *LogicSshWsSession) Wait(quitChan chan bool) {\n\tif err := sws.session.Wait(); err != nil {\n\t\tsetQuit(quitChan)\n\t}\n}\n\nfunc setQuit(ch chan bool) {\n\tch <- true\n}\n"
  },
  {
    "path": "agent/utils/toolbox/fail2ban.go",
    "content": "package toolbox\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n)\n\ntype Fail2ban struct{}\n\nconst defaultPath = \"/etc/fail2ban/jail.local\"\n\ntype FirewallClient interface {\n\tStatus() (bool, bool, bool)\n\tVersion() (string, error)\n\tOperate(operate string) error\n\tOperateSSHD(operate, ip string) error\n}\n\nfunc NewFail2Ban() (*Fail2ban, error) {\n\tisExist, _ := controller.CheckExist(\"fail2ban.service\")\n\tif isExist {\n\t\tif _, err := os.Stat(defaultPath); err != nil {\n\t\t\tif err := initLocalFile(); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif err := controller.HandleRestart(\"fail2ban.service\"); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"restart fail2ban failed, err: %v\", err)\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\treturn &Fail2ban{}, nil\n}\n\nfunc (f *Fail2ban) Status() (bool, bool, bool) {\n\tisEnable, _ := controller.CheckEnable(\"fail2ban.service\")\n\tisActive, _ := controller.CheckActive(\"fail2ban.service\")\n\tisExist, _ := controller.CheckExist(\"fail2ban.service\")\n\n\treturn isEnable, isActive, isExist\n}\n\nfunc (f *Fail2ban) Version() string {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"fail2ban-client version\")\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load the fail2ban version failed, %v\", err)\n\t\treturn \"-\"\n\t}\n\treturn strings.ReplaceAll(stdout, \"\\n\", \"\")\n}\n\nfunc (f *Fail2ban) Operate(operate string) error {\n\tswitch operate {\n\tcase \"start\", \"restart\", \"stop\", \"enable\", \"disable\":\n\t\tif err := controller.Handle(operate, \"fail2ban.service\"); err != nil {\n\t\t\treturn fmt.Errorf(\"%s the fail2ban.service failed, err: %v\", operate, err)\n\t\t}\n\t\treturn nil\n\tcase \"reload\":\n\t\tif err := cmd.RunDefaultBashC(\"fail2ban-client reload\"); err != nil {\n\t\t\treturn fmt.Errorf(\"fail2ban-client reload, %v\", err)\n\t\t}\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"not support such operation: %v\", operate)\n\t}\n}\n\nfunc (f *Fail2ban) ReBanIPs(ips []string) error {\n\tipItems, _ := f.ListBanned()\n\tstdout, err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdout(\"fail2ban-client\", \"unban\", \"--all\")\n\tif err != nil {\n\t\tstdout1, err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdout(\"fail2ban-client\", \"set\", \"sshd\", \"banip\", strings.Join(ipItems, \" \"))\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"rebanip after fail2ban-client unban --all failed, err: %s\", stdout1)\n\t\t}\n\t\treturn fmt.Errorf(\"fail2ban-client unban --all failed, err: %s\", stdout)\n\t}\n\tstdout, err = cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdout(\"fail2ban-client\", \"set\", \"sshd\", \"banip\", strings.Join(ips, \" \"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"handle `fail2ban-client set sshd banip %s` failed, err: %s\", strings.Join(ips, \" \"), stdout)\n\t}\n\treturn nil\n}\n\nfunc (f *Fail2ban) ListBanned() ([]string, error) {\n\tvar lists []string\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"fail2ban-client status sshd | grep 'Banned IP list:'\")\n\tif err != nil {\n\t\treturn lists, err\n\t}\n\titemList := strings.Split(strings.Trim(stdout, \"\\n\"), \"Banned IP list:\")\n\tif len(itemList) != 2 {\n\t\treturn lists, nil\n\t}\n\n\tips := strings.Fields(itemList[1])\n\tfor _, item := range ips {\n\t\tif len(item) != 0 {\n\t\t\tlists = append(lists, item)\n\t\t}\n\t}\n\treturn lists, nil\n}\n\nfunc (f *Fail2ban) ListIgnore() ([]string, error) {\n\tvar lists []string\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"fail2ban-client get sshd ignoreip\")\n\tif err != nil {\n\t\treturn lists, err\n\t}\n\tstdout = strings.ReplaceAll(stdout, \"|\", \"\")\n\tstdout = strings.ReplaceAll(stdout, \"`\", \"\")\n\tstdout = strings.ReplaceAll(stdout, \"\\n\", \"\")\n\taddrs := strings.Split(stdout, \"-\")\n\tfor _, addr := range addrs {\n\t\tif !strings.HasPrefix(addr, \" \") {\n\t\t\tcontinue\n\t\t}\n\t\tlists = append(lists, strings.ReplaceAll(addr, \" \", \"\"))\n\t}\n\treturn lists, nil\n}\n\nfunc initLocalFile() error {\n\tf, err := os.Create(defaultPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\tinitFile := `#DEFAULT-START\n[DEFAULT]\nbantime = 600\nfindtime = 300\nmaxretry = 5\nbanaction = $banaction\naction = %(action_mwl)s\n#DEFAULT-END\n\n[sshd]\nignoreip = 127.0.0.1/8\nenabled = true\nfilter = sshd\nport = 22\nmaxretry = 5\nfindtime = 300\nbantime = 600\nbanaction = $banaction\naction = %(action_mwl)s\nlogpath = $logpath`\n\n\tbanaction := \"\"\n\tif active, _ := controller.CheckActive(\"firewalld\"); active {\n\t\tbanaction = \"firewallcmd-ipset\"\n\t} else if active, _ := controller.CheckActive(\"ufw\"); active {\n\t\tbanaction = \"ufw\"\n\t} else {\n\t\tbanaction = \"iptables-allports\"\n\t}\n\tinitFile = strings.ReplaceAll(initFile, \"$banaction\", banaction)\n\n\tlogPath := \"\"\n\tif _, err := os.Stat(\"/var/log/secure\"); err == nil {\n\t\tlogPath = \"/var/log/secure\"\n\t} else {\n\t\tlogPath = \"/var/log/auth.log\"\n\t}\n\tinitFile = strings.ReplaceAll(initFile, \"$logpath\", logPath)\n\tif err := os.WriteFile(defaultPath, []byte(initFile), 0640); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "agent/utils/toolbox/helper/sha512_crypt.go",
    "content": "package helper\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"crypto/sha512\"\n\t\"errors\"\n\t\"strconv\"\n)\n\nconst (\n\tSaltLenMin    = 1\n\tSaltLenMax    = 16\n\tRoundsMin     = 1000\n\tRoundsMax     = 999999999\n\tRoundsDefault = 5000\n)\n\nvar _rounds = []byte(\"rounds=\")\n\nfunc Generate(key []byte) (string, error) {\n\tvar rounds int\n\tvar isRoundsDef bool\n\n\tsalt := generateWRounds()\n\tmagicPrefix := []byte(\"$6$\")\n\tif !bytes.HasPrefix(salt, magicPrefix) {\n\t\treturn \"\", errors.New(\"invalid magic prefix\")\n\t}\n\n\tsaltItem := bytes.Split(salt, []byte{'$'})\n\tif len(saltItem) < 3 {\n\t\treturn \"\", errors.New(\"invalid salt format\")\n\t}\n\n\tif bytes.HasPrefix(saltItem[2], _rounds) {\n\t\tisRoundsDef = true\n\t\tpr, err := strconv.ParseInt(string(saltItem[2][7:]), 10, 32)\n\t\tif err != nil {\n\t\t\treturn \"\", errors.New(\"invalid rounds\")\n\t\t}\n\t\trounds = int(pr)\n\t\tif rounds < RoundsMin {\n\t\t\trounds = RoundsMin\n\t\t} else if rounds > RoundsMax {\n\t\t\trounds = RoundsMax\n\t\t}\n\t\tsalt = saltItem[3]\n\t} else {\n\t\trounds = RoundsDefault\n\t\tsalt = saltItem[2]\n\t}\n\n\tif len(salt) > SaltLenMax {\n\t\tsalt = salt[0:SaltLenMax]\n\t}\n\n\tAlternate := sha512.New()\n\tAlternate.Write(key)\n\tAlternate.Write(salt)\n\tAlternate.Write(key)\n\tAlternateSum := Alternate.Sum(nil)\n\n\tA := sha512.New()\n\tA.Write(key)\n\tA.Write(salt)\n\ti := len(key)\n\tfor ; i > 64; i -= 64 {\n\t\tA.Write(AlternateSum)\n\t}\n\tA.Write(AlternateSum[0:i])\n\n\tfor i = len(key); i > 0; i >>= 1 {\n\t\tif (i & 1) != 0 {\n\t\t\tA.Write(AlternateSum)\n\t\t} else {\n\t\t\tA.Write(key)\n\t\t}\n\t}\n\tA_sum := A.Sum(nil)\n\n\tP := sha512.New()\n\tfor i = 0; i < len(key); i++ {\n\t\tP.Write(key)\n\t}\n\tP_sum := P.Sum(nil)\n\tP_seq := make([]byte, 0, len(key))\n\tfor i = len(key); i > 64; i -= 64 {\n\t\tP_seq = append(P_seq, P_sum...)\n\t}\n\tP_seq = append(P_seq, P_sum[0:i]...)\n\n\tS := sha512.New()\n\tfor i = 0; i < (16 + int(A_sum[0])); i++ {\n\t\tS.Write(salt)\n\t}\n\tS_sum := S.Sum(nil)\n\tS_seq := make([]byte, 0, len(salt))\n\tfor i = len(salt); i > 64; i -= 64 {\n\t\tS_seq = append(S_seq, S_sum...)\n\t}\n\tS_seq = append(S_seq, S_sum[0:i]...)\n\n\tC_sum := A_sum\n\n\tfor i = 0; i < rounds; i++ {\n\t\tC := sha512.New()\n\t\tif (i & 1) != 0 {\n\t\t\tC.Write(P_seq)\n\t\t} else {\n\t\t\tC.Write(C_sum)\n\t\t}\n\t\tif (i % 3) != 0 {\n\t\t\tC.Write(S_seq)\n\t\t}\n\t\tif (i % 7) != 0 {\n\t\t\tC.Write(P_seq)\n\t\t}\n\t\tif (i & 1) != 0 {\n\t\t\tC.Write(C_sum)\n\t\t} else {\n\t\t\tC.Write(P_seq)\n\t\t}\n\n\t\tC_sum = C.Sum(nil)\n\t}\n\n\tout := make([]byte, 0, 123)\n\tout = append(out, magicPrefix...)\n\tif isRoundsDef {\n\t\tout = append(out, []byte(\"rounds=\"+strconv.Itoa(rounds)+\"$\")...)\n\t}\n\tout = append(out, salt...)\n\tout = append(out, '$')\n\tout = append(out, base64_24Bit([]byte{\n\t\tC_sum[42], C_sum[21], C_sum[0],\n\t\tC_sum[1], C_sum[43], C_sum[22],\n\t\tC_sum[23], C_sum[2], C_sum[44],\n\t\tC_sum[45], C_sum[24], C_sum[3],\n\t\tC_sum[4], C_sum[46], C_sum[25],\n\t\tC_sum[26], C_sum[5], C_sum[47],\n\t\tC_sum[48], C_sum[27], C_sum[6],\n\t\tC_sum[7], C_sum[49], C_sum[28],\n\t\tC_sum[29], C_sum[8], C_sum[50],\n\t\tC_sum[51], C_sum[30], C_sum[9],\n\t\tC_sum[10], C_sum[52], C_sum[31],\n\t\tC_sum[32], C_sum[11], C_sum[53],\n\t\tC_sum[54], C_sum[33], C_sum[12],\n\t\tC_sum[13], C_sum[55], C_sum[34],\n\t\tC_sum[35], C_sum[14], C_sum[56],\n\t\tC_sum[57], C_sum[36], C_sum[15],\n\t\tC_sum[16], C_sum[58], C_sum[37],\n\t\tC_sum[38], C_sum[17], C_sum[59],\n\t\tC_sum[60], C_sum[39], C_sum[18],\n\t\tC_sum[19], C_sum[61], C_sum[40],\n\t\tC_sum[41], C_sum[20], C_sum[62],\n\t\tC_sum[63],\n\t})...)\n\n\tA.Reset()\n\tAlternate.Reset()\n\tP.Reset()\n\tfor i = 0; i < len(A_sum); i++ {\n\t\tA_sum[i] = 0\n\t}\n\tfor i = 0; i < len(AlternateSum); i++ {\n\t\tAlternateSum[i] = 0\n\t}\n\tfor i = 0; i < len(P_seq); i++ {\n\t\tP_seq[i] = 0\n\t}\n\n\treturn string(out), nil\n}\n\nfunc generateWRounds() []byte {\n\tsalt := make([]byte, 16)\n\t_, _ = rand.Read(salt)\n\n\tmagicPrefix := \"$6$\"\n\tout := make([]byte, len(magicPrefix)+5000)\n\tcopy(out, magicPrefix)\n\tcopy(out[len(magicPrefix):], base64_24Bit(salt))\n\treturn out\n}\n\nfunc base64_24Bit(src []byte) (hash []byte) {\n\tif len(src) == 0 {\n\t\treturn []byte{}\n\t}\n\talphabet := \"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\n\n\thashSize := (len(src) * 8) / 6\n\tif (len(src) % 6) != 0 {\n\t\thashSize += 1\n\t}\n\thash = make([]byte, hashSize)\n\n\tdst := hash\n\tfor len(src) > 0 {\n\t\tswitch len(src) {\n\t\tdefault:\n\t\t\tdst[0] = alphabet[src[0]&0x3f]\n\t\t\tdst[1] = alphabet[((src[0]>>6)|(src[1]<<2))&0x3f]\n\t\t\tdst[2] = alphabet[((src[1]>>4)|(src[2]<<4))&0x3f]\n\t\t\tdst[3] = alphabet[(src[2]>>2)&0x3f]\n\t\t\tsrc = src[3:]\n\t\t\tdst = dst[4:]\n\t\tcase 2:\n\t\t\tdst[0] = alphabet[src[0]&0x3f]\n\t\t\tdst[1] = alphabet[((src[0]>>6)|(src[1]<<2))&0x3f]\n\t\t\tdst[2] = alphabet[(src[1]>>4)&0x3f]\n\t\t\tsrc = src[2:]\n\t\t\tdst = dst[3:]\n\t\tcase 1:\n\t\t\tdst[0] = alphabet[src[0]&0x3f]\n\t\t\tdst[1] = alphabet[(src[0]>>6)&0x3f]\n\t\t\tsrc = src[1:]\n\t\t\tdst = dst[2:]\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "agent/utils/toolbox/pure-ftpd.go",
    "content": "package toolbox\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/user\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/toolbox/helper\"\n)\n\ntype Ftp struct {\n\tDefaultUser  string\n\tDefaultGroup string\n}\n\ntype FtpList struct {\n\tUser   string\n\tPath   string\n\tStatus string\n}\n\ntype FtpLog struct {\n\tIP        string `json:\"ip\"`\n\tUser      string `json:\"user\"`\n\tTime      string `json:\"time\"`\n\tOperation string `json:\"operation\"`\n\tStatus    string `json:\"status\"`\n\tSize      string `json:\"size\"`\n}\n\ntype FtpClient interface {\n\tStatus() (bool, bool)\n\tOperate(operate string) error\n\tLoadList() ([]FtpList, error)\n\tUserAdd(username, path, passwd string) error\n\tUserDel(username string) error\n\tSetPasswd(username, passwd string) error\n\tReload() error\n\tLoadLogs() ([]FtpLog, error)\n}\n\nfunc NewFtpClient() (*Ftp, error) {\n\tuserItem, err := user.LookupId(\"1000\")\n\tif err == nil {\n\t\tgroupItem, err := user.LookupGroupId(userItem.Gid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &Ftp{DefaultUser: userItem.Username, DefaultGroup: groupItem.Name}, err\n\t}\n\tif err.Error() != user.UnknownUserIdError(1000).Error() {\n\t\treturn nil, err\n\t}\n\n\tgroupItem, err := user.LookupGroupId(\"1000\")\n\tif err == nil {\n\t\tif err := cmd.RunDefaultBashCf(\"useradd -u 1000 -g %s %s\", groupItem.Name, \"1panel\"); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &Ftp{DefaultUser: \"1panel\", DefaultGroup: groupItem.Name}, nil\n\t}\n\tif err.Error() != user.UnknownGroupIdError(\"1000\").Error() {\n\t\treturn nil, err\n\t}\n\tif err := cmd.RunDefaultBashC(\"groupadd -g 1000 1panel\"); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := cmd.RunDefaultBashC(\"useradd -u 1000 -g 1panel 1panel\"); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Ftp{DefaultUser: \"1panel\", DefaultGroup: \"1panel\"}, nil\n}\n\nfunc (f *Ftp) Status() (bool, bool) {\n\tisActive, _ := controller.CheckActive(\"pure-ftpd.service\")\n\tisExist, _ := controller.CheckExist(\"pure-ftpd.service\")\n\n\treturn isActive, isExist\n}\n\nfunc (f *Ftp) Operate(operate string) error {\n\tswitch operate {\n\tcase \"start\", \"restart\", \"stop\":\n\t\tif err := controller.Handle(operate, \"pure-ftpd.service\"); err != nil {\n\t\t\treturn fmt.Errorf(\"%s the pure-ftpd.service failed, err: %v\", operate, err)\n\t\t}\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"not support such operation: %v\", operate)\n\t}\n}\n\nfunc (f *Ftp) UserAdd(username, passwd, path string) error {\n\tif cmd.CheckIllegal(username, path) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tentry, err := generatePureFtpEntrySimple(username, passwd, path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"generate pure-ftpd entry failed, err: %v\", err)\n\t}\n\tpwdFile, err := os.OpenFile(\"/etc/pure-ftpd/pureftpd.passwd\", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer pwdFile.Close()\n\n\t_, err = pwdFile.WriteString(\"\\n\" + entry + \"\\n\")\n\tif err != nil {\n\t\treturn err\n\t}\n\t_ = f.Reload()\n\tif err := cmd.RunDefaultBashCf(\"chown -R %s:%s %s\", f.DefaultUser, f.DefaultGroup, path); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f *Ftp) UserDel(username string) error {\n\tif cmd.CheckIllegal(username) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif err := cmd.RunDefaultBashCf(\"pure-pw userdel %s\", username); err != nil {\n\t\treturn err\n\t}\n\t_ = f.Reload()\n\treturn nil\n}\n\nfunc (f *Ftp) SetPasswd(username, passwd string) error {\n\thashedPassword, err := helper.Generate([]byte(passwd))\n\tif err != nil {\n\t\treturn err\n\t}\n\tpwdFile, err := os.Open(\"/etc/pure-ftpd/pureftpd.passwd\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer pwdFile.Close()\n\n\tvar entrys []string\n\tscanner := bufio.NewScanner(pwdFile)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tuserEntry := strings.Split(line, \":\")\n\t\tif len(userEntry) < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tif userEntry[0] == username {\n\t\t\tuserEntry[1] = string(hashedPassword)\n\t\t\tline = strings.Join(userEntry, \":\")\n\t\t}\n\t\tentrys = append(entrys, line)\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\tpwdFile.Close()\n\n\tpwdFile, err = os.Create(\"/etc/pure-ftpd/pureftpd.passwd\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer pwdFile.Close()\n\n\tfor _, entry := range entrys {\n\t\t_, err := pwdFile.WriteString(entry + \"\\n\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (f *Ftp) SetPath(username, path string) error {\n\tif cmd.CheckIllegal(username, path) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tif err := cmd.RunDefaultBashCf(\"pure-pw usermod %s -d %s\", username, path); err != nil {\n\t\treturn err\n\t}\n\tif err := cmd.RunDefaultBashCf(\"chown -R %s:%s %s\", f.DefaultUser, f.DefaultGroup, path); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f *Ftp) SetStatus(username, status string) error {\n\tif cmd.CheckIllegal(username, status) {\n\t\treturn buserr.New(\"ErrCmdIllegal\")\n\t}\n\tstatusItem := \"''\"\n\tif status == constant.StatusDisable {\n\t\tstatusItem = \"1\"\n\t}\n\tif err := cmd.RunDefaultBashCf(\"pure-pw usermod %s -r %s\", username, statusItem); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f *Ftp) LoadList() ([]FtpList, error) {\n\tstd, err := cmd.RunDefaultWithStdoutBashC(\"pure-pw list\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar lists []FtpList\n\tlines := strings.Split(std, \"\\n\")\n\tfor _, line := range lines {\n\t\tparts := strings.Fields(line)\n\t\tif len(parts) < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tstd2, err := cmd.RunDefaultWithStdoutBashCf(\"pure-pw  show %s | grep 'Allowed client IPs :'\", parts[0])\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"handle pure-pw show %s failed, %v\", parts[0], err)\n\t\t\tcontinue\n\t\t}\n\t\tstatus := constant.StatusDisable\n\t\titemStd := strings.ReplaceAll(std2, \"\\n\", \"\")\n\t\tif len(strings.TrimSpace(strings.ReplaceAll(itemStd, \"Allowed client IPs :\", \"\"))) == 0 {\n\t\t\tstatus = constant.StatusEnable\n\t\t}\n\t\tlists = append(lists, FtpList{User: parts[0], Path: strings.ReplaceAll(parts[1], \"/./\", \"\"), Status: status})\n\t}\n\treturn lists, nil\n}\n\nfunc (f *Ftp) Reload() error {\n\tif err := cmd.RunDefaultBashC(\"pure-pw mkdb\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (f *Ftp) LoadLogs(user, operation string) ([]FtpLog, error) {\n\tvar logs []FtpLog\n\tlogItem := \"\"\n\tif _, err := os.Stat(\"/etc/pure-ftpd/conf\"); err != nil && os.IsNotExist(err) {\n\t\tstd, err := cmd.RunDefaultWithStdoutBashC(\"cat /etc/pure-ftpd/pure-ftpd.conf | grep AltLog | grep clf:\")\n\t\tlogItem = \"/var/log/pureftpd.log\"\n\t\tif err == nil && !strings.HasPrefix(std, \"#\") {\n\t\t\tlogItem = std\n\t\t}\n\t} else {\n\t\tif err != nil {\n\t\t\treturn logs, err\n\t\t}\n\t\tstd, err := cmd.RunDefaultWithStdoutBashC(\"cat /etc/pure-ftpd/conf/AltLog\")\n\t\tlogItem = \"/var/log/pure-ftpd/transfer.log\"\n\t\tif err != nil && !strings.HasPrefix(std, \"#\") {\n\t\t\tlogItem = std\n\t\t}\n\t}\n\n\tlogItem = strings.ReplaceAll(logItem, \"AltLog\", \"\")\n\tlogItem = strings.ReplaceAll(logItem, \"clf:\", \"\")\n\tlogItem = strings.ReplaceAll(logItem, \"\\n\", \"\")\n\tlogPath := strings.Trim(logItem, \" \")\n\n\tlogDir := path.Dir(logPath)\n\tfilesItem, err := os.ReadDir(logDir)\n\tif err != nil {\n\t\treturn logs, err\n\t}\n\tvar fileList []string\n\tfor i := 0; i < len(filesItem); i++ {\n\t\tif filesItem[i].IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\titemPath := path.Join(logDir, filesItem[i].Name())\n\t\tif !strings.HasSuffix(itemPath, \".gz\") {\n\t\t\tfileList = append(fileList, itemPath)\n\t\t\tcontinue\n\t\t}\n\t\titemFileName := strings.TrimSuffix(itemPath, \".gz\")\n\t\tif _, err := os.Stat(itemFileName); err != nil && os.IsNotExist(err) {\n\t\t\tif err := handleGunzip(itemPath); err == nil {\n\t\t\t\tfileList = append(fileList, itemFileName)\n\t\t\t}\n\t\t}\n\t}\n\tlogs = loadLogsByFiles(fileList, user, operation)\n\treturn logs, nil\n}\n\nfunc handleGunzip(path string) error {\n\tif err := cmd.RunDefaultBashCf(\"gunzip %s\", path); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc loadLogsByFiles(fileList []string, user, operation string) []FtpLog {\n\tvar logs []FtpLog\n\tlayout := \"02/Jan/2006:15:04:05-0700\"\n\tfor _, file := range fileList {\n\t\tdata, err := os.ReadFile(file)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tlines := strings.Split(string(data), \"\\n\")\n\t\tfor _, line := range lines {\n\t\t\tparts := strings.Fields(line)\n\t\t\tif len(parts) < 9 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (len(user) != 0 && parts[2] != user) || (len(operation) != 0 && parts[5] != fmt.Sprintf(\"\\\"%s\", operation)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttimeStr := parts[3] + parts[4]\n\t\t\ttimeStr = strings.ReplaceAll(timeStr, \"[\", \"\")\n\t\t\ttimeStr = strings.ReplaceAll(timeStr, \"]\", \"\")\n\t\t\ttimeItem, err := time.Parse(layout, timeStr)\n\t\t\tif err == nil {\n\t\t\t\ttimeStr = timeItem.Format(constant.DateTimeLayout)\n\t\t\t}\n\t\t\toperateStr := parts[5] + parts[6]\n\t\t\tlogs = append(logs, FtpLog{\n\t\t\t\tIP:        parts[0],\n\t\t\t\tUser:      parts[2],\n\t\t\t\tTime:      timeStr,\n\t\t\t\tOperation: operateStr,\n\t\t\t\tStatus:    parts[7],\n\t\t\t\tSize:      parts[8],\n\t\t\t})\n\t\t}\n\t}\n\treturn logs\n}\n\nfunc generatePureFtpEntrySimple(username, password, path string) (string, error) {\n\tpasswdAfterSha512, err := helper.Generate([]byte(password))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn fmt.Sprintf(\"%s:%s:1000:1000::%s/./::::::::::::\", username, passwdAfterSha512, path), nil\n}\n"
  },
  {
    "path": "agent/utils/version/version.go",
    "content": "package version\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/constant\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n)\n\nfunc GetUpgradeVersionInfo() (*dto.UpgradeInfo, error) {\n\tvar upgrade dto.UpgradeInfo\n\tvar currentVersion model.Setting\n\tif err := global.CoreDB.Model(&model.Setting{}).Where(\"key = ?\", \"SystemVersion\").First(&currentVersion).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"load %s from db setting failed, err: %v\", \"SystemVersion\", err)\n\t\treturn nil, err\n\t}\n\tvar developerMode model.Setting\n\tif err := global.CoreDB.Model(&model.Setting{}).Where(\"key = ?\", \"DeveloperMode\").First(&developerMode).Error; err != nil {\n\t\tglobal.LOG.Errorf(\"load %s from db setting failed, err: %v\", \"DeveloperMode\", err)\n\t\treturn nil, err\n\t}\n\n\tupgrade.TestVersion, upgrade.NewVersion, upgrade.LatestVersion = loadVersionByMode(developerMode.Value, currentVersion.Value)\n\tvar itemVersion string\n\tif len(upgrade.NewVersion) != 0 {\n\t\titemVersion = upgrade.NewVersion\n\t}\n\tif (global.CONF.Base.Mode == \"dev\" || developerMode.Value == constant.StatusEnable) && len(upgrade.TestVersion) != 0 {\n\t\titemVersion = upgrade.TestVersion\n\t}\n\tif len(upgrade.LatestVersion) != 0 {\n\t\titemVersion = upgrade.LatestVersion\n\t}\n\tif len(itemVersion) == 0 {\n\t\treturn &upgrade, nil\n\t}\n\tmode := global.CONF.Base.Mode\n\tif strings.Contains(itemVersion, \"beta\") {\n\t\tmode = \"beta\"\n\t}\n\tnotes, err := loadReleaseNotes(fmt.Sprintf(\"%s/%s/%s/release/1panel-%s-release-notes\", global.RepoURL(), mode, itemVersion, itemVersion))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load releases-notes of version %s failed, err: %v\", itemVersion, err)\n\t}\n\tupgrade.ReleaseNote = notes\n\treturn &upgrade, nil\n}\n\nfunc loadVersionByMode(developer, currentVersion string) (string, string, string) {\n\tvar current, latest string\n\tif global.CONF.Base.Mode == \"dev\" {\n\t\tbetaVersionLatest := loadVersion(true, currentVersion, \"beta\")\n\t\tdevVersionLatest := loadVersion(true, currentVersion, \"dev\")\n\t\tif common.ComparePanelVersion(betaVersionLatest, devVersionLatest) {\n\t\t\treturn betaVersionLatest, \"\", \"\"\n\t\t}\n\t\treturn devVersionLatest, \"\", \"\"\n\t}\n\n\tbetaVersionLatest := \"\"\n\tlatest = loadVersion(true, currentVersion, \"stable\")\n\tcurrent = loadVersion(false, currentVersion, \"stable\")\n\tif developer == constant.StatusEnable {\n\t\tbetaVersionLatest = loadVersion(true, currentVersion, \"beta\")\n\t}\n\tif current != latest {\n\t\treturn betaVersionLatest, current, latest\n\t}\n\n\tversionPart := strings.Split(current, \".\")\n\tif len(versionPart) < 3 {\n\t\treturn betaVersionLatest, current, latest\n\t}\n\tnum, _ := strconv.Atoi(versionPart[1])\n\tif num == 0 {\n\t\treturn betaVersionLatest, current, latest\n\t}\n\tif num >= 10 {\n\t\tif current[:6] == currentVersion[:6] {\n\t\t\treturn betaVersionLatest, current, \"\"\n\t\t}\n\t\treturn betaVersionLatest, \"\", latest\n\t}\n\tif current[:5] == currentVersion[:5] {\n\t\treturn betaVersionLatest, current, \"\"\n\t}\n\treturn betaVersionLatest, \"\", latest\n}\n\nfunc loadVersion(isLatest bool, currentVersion, mode string) string {\n\tpath := fmt.Sprintf(\"%s/%s/latest\", global.RepoURL(), mode)\n\tif !isLatest {\n\t\tpath = fmt.Sprintf(\"%s/%s/latest.current\", global.RepoURL(), mode)\n\t}\n\t_, latestVersionRes, err := HandleRequest(path, http.MethodGet, constant.TimeOut20s)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load latest version from oss failed, err: %v\", err)\n\t\treturn \"\"\n\t}\n\tversion := string(latestVersionRes)\n\tif strings.Contains(version, \"<\") {\n\t\tglobal.LOG.Errorf(\"load latest version from oss failed, err: %v\", version)\n\t\treturn \"\"\n\t}\n\tif isLatest {\n\t\treturn checkVersion(version, currentVersion)\n\t}\n\n\tversionMap := make(map[string]string)\n\tif err := json.Unmarshal(latestVersionRes, &versionMap); err != nil {\n\t\tglobal.LOG.Errorf(\"load latest version from oss failed (error unmarshal), err: %v\", err)\n\t\treturn \"\"\n\t}\n\n\tversionPart := strings.Split(currentVersion, \".\")\n\tif len(versionPart) < 3 {\n\t\tglobal.LOG.Errorf(\"current version is error format: %s\", currentVersion)\n\t\treturn \"\"\n\t}\n\tnum, _ := strconv.Atoi(versionPart[1])\n\tif num >= 10 {\n\t\tif version, ok := versionMap[currentVersion[0:5]]; ok {\n\t\t\treturn checkVersion(version, currentVersion)\n\t\t}\n\t\treturn \"\"\n\t}\n\tif version, ok := versionMap[currentVersion[0:4]]; ok {\n\t\treturn checkVersion(version, currentVersion)\n\t}\n\treturn \"\"\n}\n\nfunc checkVersion(v2, v1 string) string {\n\taddSuffix := false\n\tif !strings.Contains(v1, \"-\") {\n\t\tv1 = v1 + \"-lts\"\n\t}\n\tif !strings.Contains(v2, \"-\") {\n\t\taddSuffix = true\n\t\tv2 = v2 + \"-lts\"\n\t}\n\tif common.ComparePanelVersion(v2, v1) {\n\t\tif addSuffix {\n\t\t\treturn strings.TrimSuffix(v2, \"-lts\")\n\t\t}\n\t\treturn v2\n\t}\n\treturn \"\"\n}\n\nfunc loadReleaseNotes(path string) (string, error) {\n\t_, releaseNotes, err := HandleRequest(path, http.MethodGet, constant.TimeOut20s)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(releaseNotes), nil\n}\n\nfunc HandleRequest(url, method string, timeout int) (int, []byte, error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"handle request failed, error message: %v\", r)\n\t\t\treturn\n\t\t}\n\t}()\n\n\ttransport := loadRequestTransport()\n\tclient := http.Client{Timeout: time.Duration(timeout) * time.Second, Transport: transport}\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\trequest, err := http.NewRequestWithContext(ctx, method, url, nil)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, err := client.Do(request)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn 0, nil, errors.New(resp.Status)\n\t}\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\treturn resp.StatusCode, body, nil\n}\n\nfunc loadRequestTransport() *http.Transport {\n\treturn &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\tDialContext: (&net.Dialer{\n\t\t\tTimeout:   60 * time.Second,\n\t\t\tKeepAlive: 60 * time.Second,\n\t\t}).DialContext,\n\t\tTLSHandshakeTimeout:   5 * time.Second,\n\t\tResponseHeaderTimeout: 10 * time.Second,\n\t\tIdleConnTimeout:       15 * time.Second,\n\t}\n}\n"
  },
  {
    "path": "agent/utils/websocket/client.go",
    "content": "package websocket\n\nimport (\n\t\"sync/atomic\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nconst MaxMessageQuenue = 32\n\ntype Client struct {\n\tID     string\n\tSocket *websocket.Conn\n\tMsg    chan []byte\n\tclosed atomic.Bool\n}\n\nfunc NewWsClient(ID string, socket *websocket.Conn) *Client {\n\treturn &Client{\n\t\tID:     ID,\n\t\tSocket: socket,\n\t\tMsg:    make(chan []byte, MaxMessageQuenue),\n\t}\n}\n\nfunc (c *Client) Read() {\n\tdefer func() {\n\t\tc.closed.Store(true)\n\t\tclose(c.Msg)\n\t}()\n\tfor {\n\t\t_, message, err := c.Socket.ReadMessage()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tProcessData(c, message)\n\t}\n}\n\nfunc (c *Client) Write() {\n\tdefer c.Socket.Close()\n\tfor {\n\t\tmessage, ok := <-c.Msg\n\t\tif !ok {\n\t\t\treturn\n\t\t}\n\t\t_ = c.Socket.WriteMessage(websocket.TextMessage, message)\n\t}\n}\n\nfunc (c *Client) Send(res []byte) {\n\tif c.closed.Load() {\n\t\treturn\n\t}\n\tselect {\n\tcase c.Msg <- res:\n\tdefault:\n\t}\n}\n"
  },
  {
    "path": "agent/utils/websocket/process_data.go",
    "content": "package websocket\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/files\"\n\t\"github.com/shirou/gopsutil/v4/host\"\n\t\"github.com/shirou/gopsutil/v4/net\"\n\t\"github.com/shirou/gopsutil/v4/process\"\n)\n\nconst defaultTimeout = 10 * time.Second\n\ntype WsInput struct {\n\tType string `json:\"type\"`\n\tDownloadProgress\n\tPsProcessConfig\n\tSSHSessionConfig\n\tNetConfig\n}\n\ntype DownloadProgress struct {\n\tKeys []string `json:\"keys\"`\n}\n\ntype PsProcessConfig struct {\n\tPid      int32  `json:\"pid\"`\n\tName     string `json:\"name\"`\n\tUsername string `json:\"username\"`\n}\n\ntype SSHSessionConfig struct {\n\tLoginUser string `json:\"loginUser\"`\n\tLoginIP   string `json:\"loginIP\"`\n}\n\ntype NetConfig struct {\n\tPort        uint32 `json:\"port\"`\n\tProcessName string `json:\"processName\"`\n\tProcessID   int32  `json:\"processID\"`\n}\n\ntype PsProcessData struct {\n\tPID            int32  `json:\"PID\"`\n\tName           string `json:\"name\"`\n\tPPID           int32  `json:\"PPID\"`\n\tUsername       string `json:\"username\"`\n\tStatus         string `json:\"status\"`\n\tStartTime      string `json:\"startTime\"`\n\tNumThreads     int32  `json:\"numThreads\"`\n\tNumConnections int    `json:\"numConnections\"`\n\tCpuPercent     string `json:\"cpuPercent\"`\n\n\tDiskRead  string `json:\"diskRead\"`\n\tDiskWrite string `json:\"diskWrite\"`\n\tCmdLine   string `json:\"cmdLine\"`\n\n\tRss    string `json:\"rss\"`\n\tVMS    string `json:\"vms\"`\n\tHWM    string `json:\"hwm\"`\n\tData   string `json:\"data\"`\n\tStack  string `json:\"stack\"`\n\tLocked string `json:\"locked\"`\n\tSwap   string `json:\"swap\"`\n\tDirty  string `json:\"dirty\"`\n\tPSS    string `json:\"pss\"`\n\tUSS    string `json:\"uss\"`\n\tShared string `json:\"shared\"`\n\tText   string `json:\"text\"`\n\n\tCpuValue float64 `json:\"cpuValue\"`\n\tRssValue uint64  `json:\"rssValue\"`\n\n\tEnvs []string `json:\"envs\"`\n\n\tOpenFiles []process.OpenFilesStat `json:\"openFiles\"`\n\tConnects  []ProcessConnect        `json:\"connects\"`\n}\n\ntype ProcessConnect struct {\n\tType   string   `json:\"type\"`\n\tStatus string   `json:\"status\"`\n\tLaddr  net.Addr `json:\"localaddr\"`\n\tRaddr  net.Addr `json:\"remoteaddr\"`\n\tPID    int32    `json:\"PID\"`\n\tName   string   `json:\"name\"`\n}\n\ntype ProcessConnects []ProcessConnect\n\ntype sshSession struct {\n\tUsername  string `json:\"username\"`\n\tPID       int32  `json:\"PID\"`\n\tTerminal  string `json:\"terminal\"`\n\tHost      string `json:\"host\"`\n\tLoginTime string `json:\"loginTime\"`\n}\n\nfunc ProcessData(c *Client, inputMsg []byte) {\n\twsInput := &WsInput{}\n\terr := json.Unmarshal(inputMsg, wsInput)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"unmarshal wsInput error,err %s\", err.Error())\n\t\treturn\n\t}\n\tswitch wsInput.Type {\n\tcase \"wget\":\n\t\tres, err := getDownloadProcess(wsInput.DownloadProgress)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tc.Send(res)\n\tcase \"ps\":\n\t\tres, err := getProcessData(wsInput.PsProcessConfig)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tc.Send(res)\n\tcase \"ssh\":\n\t\tres, err := getSSHSessions(wsInput.SSHSessionConfig)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tc.Send(res)\n\tcase \"net\":\n\t\tres, err := getNetConnections(wsInput.NetConfig)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tc.Send(res)\n\t}\n\n}\n\nfunc getDownloadProcess(progress DownloadProgress) (res []byte, err error) {\n\tvar result []files.Process\n\tfor _, k := range progress.Keys {\n\t\tvalue := global.CACHE.Get(k)\n\t\tif value == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"get cache error,err value is nil\")\n\t\t}\n\t\tdownloadProcess := &files.Process{}\n\t\t_ = json.Unmarshal([]byte(value), downloadProcess)\n\t\tresult = append(result, *downloadProcess)\n\t\tif downloadProcess.Percent == 100 {\n\t\t\tglobal.CACHE.Del(k)\n\t\t}\n\t}\n\tres, err = json.Marshal(result)\n\treturn\n}\n\nfunc handleProcessData(proc *process.Process, processConfig *PsProcessConfig, pidConnections map[int32][]net.ConnectionStat) *PsProcessData {\n\tif processConfig.Pid > 0 && processConfig.Pid != proc.Pid {\n\t\treturn nil\n\t}\n\tprocData := PsProcessData{\n\t\tPID: proc.Pid,\n\t}\n\tif procName, err := proc.Name(); err == nil {\n\t\tprocData.Name = procName\n\t} else {\n\t\tprocData.Name = \"<UNKNOWN>\"\n\t}\n\tif processConfig.Name != \"\" && !strings.Contains(procData.Name, processConfig.Name) {\n\t\treturn nil\n\t}\n\tif username, err := proc.Username(); err == nil {\n\t\tprocData.Username = username\n\t}\n\tif processConfig.Username != \"\" && !strings.Contains(procData.Username, processConfig.Username) {\n\t\treturn nil\n\t}\n\tprocData.PPID, _ = proc.Ppid()\n\tstatusArray, _ := proc.Status()\n\tif len(statusArray) > 0 {\n\t\tprocData.Status = strings.Join(statusArray, \",\")\n\t}\n\tcreateTime, procErr := proc.CreateTime()\n\tif procErr == nil {\n\t\tt := time.Unix(createTime/1000, 0)\n\t\tprocData.StartTime = t.Format(\"2006-1-2 15:04:05\")\n\t}\n\tprocData.NumThreads, _ = proc.NumThreads()\n\tprocData.CpuValue, _ = proc.CPUPercent()\n\tprocData.CpuPercent = fmt.Sprintf(\"%.2f%%\", procData.CpuValue)\n\n\tif memInfo, err := proc.MemoryInfo(); err == nil {\n\t\tprocData.RssValue = memInfo.RSS\n\t\tprocData.Rss = common.FormatBytes(memInfo.RSS)\n\t} else {\n\t\tprocData.RssValue = 0\n\t}\n\n\tif connections, ok := pidConnections[proc.Pid]; ok {\n\t\tprocData.NumConnections = len(connections)\n\t}\n\n\treturn &procData\n}\n\nfunc getProcessData(processConfig PsProcessConfig) (res []byte, err error) {\n\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\n\tdefer cancel()\n\n\tprocesses, err := process.ProcessesWithContext(ctx)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconnections, err := net.ConnectionsMaxWithContext(ctx, \"all\", 32768)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tpidConnections := make(map[int32][]net.ConnectionStat, len(processes))\n\tfor _, conn := range connections {\n\t\tif conn.Pid == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tpidConnections[conn.Pid] = append(pidConnections[conn.Pid], conn)\n\t}\n\n\tresult := make([]PsProcessData, 0, len(processes))\n\n\tfor _, proc := range processes {\n\t\tprocData := handleProcessData(proc, &processConfig, pidConnections)\n\t\tif procData != nil {\n\t\t\tresult = append(result, *procData)\n\t\t}\n\t}\n\n\tres, err = json.Marshal(result)\n\treturn\n}\n\nfunc getSSHSessions(config SSHSessionConfig) (res []byte, err error) {\n\tvar (\n\t\tresult    []sshSession\n\t\tusers     []host.UserStat\n\t\tprocesses []*process.Process\n\t)\n\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\n\tdefer cancel()\n\n\tusers, err = host.UsersWithContext(ctx)\n\tif err != nil {\n\t\tres, err = json.Marshal(result)\n\t\treturn\n\t}\n\n\tusersByHost := make(map[string][]host.UserStat)\n\tfor _, user := range users {\n\t\tif user.Host == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif config.LoginUser != \"\" && !strings.Contains(user.User, config.LoginUser) {\n\t\t\tcontinue\n\t\t}\n\t\tif config.LoginIP != \"\" && !strings.Contains(user.Host, config.LoginIP) {\n\t\t\tcontinue\n\t\t}\n\t\tusersByHost[user.Host] = append(usersByHost[user.Host], user)\n\t}\n\n\tif len(usersByHost) == 0 {\n\t\tres, err = json.Marshal(result)\n\t\treturn\n\t}\n\n\tprocesses, err = process.ProcessesWithContext(ctx)\n\tif err != nil {\n\t\tres, err = json.Marshal(result)\n\t\treturn\n\t}\n\n\tfor _, proc := range processes {\n\t\tname, _ := proc.Name()\n\t\tif name != \"sshd\" || proc.Pid == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tconnections, _ := proc.Connections()\n\t\tif len(connections) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tcmdline, cmdErr := proc.Cmdline()\n\t\tif cmdErr != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, conn := range connections {\n\t\t\tmatchedUsers, exists := usersByHost[conn.Raddr.IP]\n\t\t\tif !exists {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor _, user := range matchedUsers {\n\t\t\t\tif strings.Contains(cmdline, user.Terminal) {\n\t\t\t\t\tt := time.Unix(int64(user.Started), 0)\n\t\t\t\t\tresult = append(result, sshSession{\n\t\t\t\t\t\tUsername:  user.User,\n\t\t\t\t\t\tHost:      user.Host,\n\t\t\t\t\t\tTerminal:  user.Terminal,\n\t\t\t\t\t\tPID:       proc.Pid,\n\t\t\t\t\t\tLoginTime: t.Format(\"2006-1-2 15:04:05\"),\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tres, err = json.Marshal(result)\n\treturn\n}\n\nfunc getNetConnections(config NetConfig) (res []byte, err error) {\n\tresult := make([]ProcessConnect, 0, 1024)\n\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\n\tdefer cancel()\n\n\tconnections, err := net.ConnectionsMaxWithContext(ctx, \"all\", 32768)\n\tif err != nil {\n\t\tres, _ = json.Marshal(result)\n\t\treturn\n\t}\n\n\tpidConnectionsMap := make(map[int32][]net.ConnectionStat, 256)\n\tpidNameMap := make(map[int32]string, 256)\n\n\tfor _, conn := range connections {\n\t\tif conn.Family != 2 && conn.Family != 10 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif conn.Pid == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif config.ProcessID > 0 && conn.Pid != config.ProcessID {\n\t\t\tcontinue\n\t\t}\n\n\t\tif config.Port > 0 && conn.Laddr.Port != config.Port && conn.Raddr.Port != config.Port {\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, exists := pidNameMap[conn.Pid]; !exists {\n\t\t\tpName, _ := getProcessNameWithContext(ctx, conn.Pid)\n\t\t\tif pName == \"\" {\n\t\t\t\tpName = \"<UNKNOWN>\"\n\t\t\t}\n\t\t\tpidNameMap[conn.Pid] = pName\n\t\t}\n\n\t\tpidConnectionsMap[conn.Pid] = append(pidConnectionsMap[conn.Pid], conn)\n\t}\n\n\tfor pid, connections := range pidConnectionsMap {\n\t\tpName := pidNameMap[pid]\n\t\tif config.ProcessName != \"\" && !strings.Contains(pName, config.ProcessName) {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, conn := range connections {\n\t\t\tresult = append(result, ProcessConnect{\n\t\t\t\tType:   getConnectionType(conn.Type, conn.Family),\n\t\t\t\tStatus: conn.Status,\n\t\t\t\tLaddr:  conn.Laddr,\n\t\t\t\tRaddr:  conn.Raddr,\n\t\t\t\tPID:    conn.Pid,\n\t\t\t\tName:   pName,\n\t\t\t})\n\t\t}\n\t}\n\n\tres, err = json.Marshal(result)\n\treturn\n}\n\nfunc getProcessNameWithContext(ctx context.Context, pid int32) (string, error) {\n\tdata, err := os.ReadFile(fmt.Sprintf(\"/proc/%d/comm\", pid))\n\tif err == nil && len(data) > 0 {\n\t\treturn strings.TrimSpace(string(data)), nil\n\t}\n\tp, err := process.NewProcessWithContext(ctx, pid)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn p.Name()\n}\n\nfunc getConnectionType(connType uint32, family uint32) string {\n\tswitch {\n\tcase connType == 1 && family == 2:\n\t\treturn \"tcp\"\n\tcase connType == 1 && family == 10:\n\t\treturn \"tcp6\"\n\tcase connType == 2 && family == 2:\n\t\treturn \"udp\"\n\tcase connType == 2 && family == 10:\n\t\treturn \"udp6\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n"
  },
  {
    "path": "agent/utils/xpack/community.go",
    "content": "//go:build !xpack\n\npackage xpack\n\nimport (\n\t\"crypto/tls\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/agent/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/agent/app/model\"\n\t\"github.com/1Panel-dev/1Panel/agent/buserr\"\n\t\"github.com/1Panel-dev/1Panel/agent/global\"\n\t\"github.com/1Panel-dev/1Panel/agent/utils/common\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc RemoveTamper(website string) {}\n\nfunc StartClam(startClam *model.Clam, isUpdate bool) (int, error) {\n\treturn 0, buserr.New(\"ErrXpackNotFound\")\n}\n\nfunc LoadNodeInfo(isBase bool) (model.NodeInfo, error) {\n\tvar info model.NodeInfo\n\tinfo.BaseDir = common.LoadParams(\"BASE_DIR\")\n\tinfo.Version = common.LoadParams(\"ORIGINAL_VERSION\")\n\tinfo.Scope = \"master\"\n\tglobal.IsMaster = true\n\treturn info, nil\n}\n\nfunc GetImagePrefix() string {\n\treturn \"\"\n}\n\nfunc IsUseCustomApp() bool {\n\treturn false\n}\n\nfunc IsXpack() bool {\n\treturn false\n}\n\nfunc CreateTaskScanSMSAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string) error {\n\treturn nil\n}\n\nfunc CreateSMSAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string) error {\n\treturn nil\n}\n\nfunc CreateTaskScanWebhookAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {\n\treturn nil\n}\n\nfunc CreateWebhookAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {\n\treturn nil\n}\n\nfunc GetLicenseErrorAlert() (uint, error) {\n\treturn 0, nil\n}\n\nfunc GetNodeErrorAlert() (uint, error) {\n\treturn 0, nil\n}\n\nfunc LoadRequestTransport() *http.Transport {\n\treturn &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\tDialContext: (&net.Dialer{\n\t\t\tTimeout:   60 * time.Second,\n\t\t\tKeepAlive: 60 * time.Second,\n\t\t}).DialContext,\n\t\tTLSHandshakeTimeout:   5 * time.Second,\n\t\tResponseHeaderTimeout: 10 * time.Second,\n\t\tIdleConnTimeout:       15 * time.Second,\n\t}\n}\n\nfunc ValidateCertificate(c *gin.Context) bool {\n\treturn true\n}\n\nfunc PushSSLToNode(websiteSSL *model.WebsiteSSL) error {\n\treturn nil\n}\n\nfunc GetAgentInfo() (*dto.AgentInfo, error) {\n\treturn nil, nil\n}\n"
  },
  {
    "path": "ci/script.sh",
    "content": "#!/bin/bash\n\nset -e\n\ncommand -v wget >/dev/null || {\n  echo \"wget not found, please install it and try again.\"\n  exit 1\n}\n\nif [ ! -f \"1pctl\" ]; then\n  wget https://github.com/1Panel-dev/installer/raw/v2/1pctl\nfi\n\nif [ ! -f \"install.sh\" ]; then\n  wget https://github.com/1Panel-dev/installer/raw/v2/install.sh\nfi\n\nif [ ! -d \"initscript\" ]; then\n  wget https://github.com/1Panel-dev/installer/raw/v2/initscript/1panel-core.service\n  wget https://github.com/1Panel-dev/installer/raw/v2/initscript/1panel-agent.service\n  mkdir -p initscript && cd initscript\n  for file in 1panel-core.init 1panel-agent.init 1panel-core.openrc 1panel-agent.openrc 1panel-core.procd 1panel-agent.procd 1panel-core.service 1panel-agent.service; do\n    wget -q https://github.com/1Panel-dev/installer/raw/v2/initscript/$file\n  done\n  cd ..\nfi\n\nif [ ! -d \"lang\" ]; then\n  mkdir -p lang && cd lang\n  for lang in en fa pt-BR ru zh; do\n    wget -q https://github.com/1Panel-dev/installer/raw/v2/lang/$lang.sh\n  done\n  cd ..\nfi\n\nif [ ! -f \"GeoIP.mmdb\" ]; then\n  wget https://resource.fit2cloud.com/1panel/package/v2/geo/GeoIP.mmdb\nfi\n\nchmod 755 1pctl install.sh\n"
  },
  {
    "path": "core/app/api/v2/auth.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/captcha\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype BaseApi struct{}\n\n// @Tags Auth\n// @Summary User login\n// @Accept json\n// @Param EntranceCode header string true \"安全入口 base64 加密串\"\n// @Param request body dto.Login true \"request\"\n// @Success 200 {object} dto.UserLoginInfo\n// @Router /core/auth/login [post]\nfunc (b *BaseApi) Login(c *gin.Context) {\n\tvar req dto.Login\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tip := common.GetRealClientIP(c)\n\tneedCaptcha := global.IPTracker.NeedCaptcha(ip)\n\tif needCaptcha {\n\t\tif errMsg := captcha.VerifyCode(req.CaptchaID, req.Captcha); errMsg != \"\" {\n\t\t\thelper.BadAuth(c, errMsg, nil)\n\t\t\treturn\n\t\t}\n\t}\n\n\tentranceItem := c.Request.Header.Get(\"EntranceCode\")\n\tvar entrance []byte\n\tif len(entranceItem) != 0 {\n\t\tentrance, _ = base64.StdEncoding.DecodeString(entranceItem)\n\t}\n\tif len(entrance) == 0 {\n\t\tcookieValue, err := c.Cookie(\"SecurityEntrance\")\n\t\tif err == nil {\n\t\t\tentrance, _ = base64.StdEncoding.DecodeString(cookieValue)\n\t\t}\n\t}\n\n\tuser, msgKey, err := authService.Login(c, req, string(entrance))\n\tif user == nil || user.MfaStatus != constant.StatusEnable {\n\t\tgo saveLoginLogs(c, wrapLoginErr(msgKey, err))\n\t}\n\tif msgKey == \"ErrAuth\" || msgKey == \"ErrEntrance\" {\n\t\tif msgKey == \"ErrAuth\" {\n\t\t\tglobal.IPTracker.SetNeedCaptcha(ip)\n\t\t}\n\t\thelper.BadAuth(c, msgKey, err)\n\t\treturn\n\t}\n\tif err != nil {\n\t\tglobal.IPTracker.SetNeedCaptcha(ip)\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tglobal.IPTracker.Clear(ip)\n\thelper.SuccessWithData(c, user)\n}\n\n// @Tags Auth\n// @Summary User login with mfa\n// @Accept json\n// @Param request body dto.MFALogin true \"request\"\n// @Success 200 {object} dto.UserLoginInfo\n// @Router /core/auth/mfalogin [post]\n// @Header 200 {string} EntranceCode \"安全入口\"\nfunc (b *BaseApi) MFALogin(c *gin.Context) {\n\tvar req dto.MFALogin\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tentranceItem := c.Request.Header.Get(\"EntranceCode\")\n\tvar entrance []byte\n\tif len(entranceItem) != 0 {\n\t\tentrance, _ = base64.StdEncoding.DecodeString(entranceItem)\n\t}\n\n\tuser, msgKey, err := authService.MFALogin(c, req, string(entrance))\n\tgo saveLoginLogs(c, wrapLoginErr(msgKey, err))\n\tif msgKey == \"ErrAuth\" || msgKey == \"ErrMFA\" {\n\t\thelper.BadAuth(c, msgKey, err)\n\t\treturn\n\t}\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, user)\n}\n\n// @Tags Auth\n// @Summary User login with passkey\n// @Success 200 {object} dto.PasskeyBeginResponse\n// @Router /core/auth/passkey/begin [post]\nfunc (b *BaseApi) PasskeyBeginLogin(c *gin.Context) {\n\tentrance := loadEntranceFromRequest(c)\n\tres, msgKey, err := authService.PasskeyBeginLogin(c, entrance)\n\tif msgKey != \"\" {\n\t\tif msgKey == \"ErrEntrance\" {\n\t\t\thelper.BadAuth(c, msgKey, err)\n\t\t\treturn\n\t\t}\n\t\tif msgKey == \"ErrPasskeyNotConfigured\" {\n\t\t\thelper.ErrorWithDetail(c, http.StatusNotFound, msgKey, err)\n\t\t\treturn\n\t\t}\n\t\thelper.ErrorWithDetail(c, http.StatusBadRequest, msgKey, err)\n\t\treturn\n\t}\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags Auth\n// @Summary User login with passkey\n// @Success 200 {object} dto.UserLoginInfo\n// @Router /core/auth/passkey/finish [post]\nfunc (b *BaseApi) PasskeyFinishLogin(c *gin.Context) {\n\tsessionID := c.GetHeader(\"Passkey-Session\")\n\tentrance := loadEntranceFromRequest(c)\n\tuser, msgKey, err := authService.PasskeyFinishLogin(c, sessionID, entrance)\n\tgo saveLoginLogs(c, wrapLoginErr(msgKey, err))\n\tif msgKey == \"ErrAuth\" || msgKey == \"ErrEntrance\" {\n\t\tif msgKey == \"ErrAuth\" {\n\t\t\tglobal.IPTracker.SetNeedCaptcha(common.GetRealClientIP(c))\n\t\t}\n\t\thelper.BadAuth(c, msgKey, err)\n\t\treturn\n\t}\n\tif msgKey != \"\" {\n\t\thelper.ErrorWithDetail(c, http.StatusBadRequest, msgKey, err)\n\t\treturn\n\t}\n\tif err != nil {\n\t\tglobal.IPTracker.SetNeedCaptcha(common.GetRealClientIP(c))\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tglobal.IPTracker.Clear(common.GetRealClientIP(c))\n\thelper.SuccessWithData(c, user)\n}\n\n// @Tags Auth\n// @Summary User logout\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/auth/logout [post]\nfunc (b *BaseApi) LogOut(c *gin.Context) {\n\tif err := authService.LogOut(c); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Auth\n// @Summary Load captcha\n// @Success 200 {object} dto.CaptchaResponse\n// @Router /core/auth/captcha [get]\nfunc (b *BaseApi) Captcha(c *gin.Context) {\n\tcaptcha, err := captcha.CreateCaptcha()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, captcha)\n}\n\nfunc (b *BaseApi) GetWelcomePage(c *gin.Context) {\n\tcount, _, _ := logService.PageLoginLog(c, dto.SearchLgLogWithPage{PageInfo: dto.PageInfo{Page: 1, PageSize: 10}})\n\tif count != 1 {\n\t\thelper.Success(c)\n\t\treturn\n\t}\n\tfile, err := os.ReadFile(path.Join(global.CONF.Base.InstallDir, \"1panel/welcome/index.html\"))\n\tif err != nil {\n\t\thelper.Success(c)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, string(file))\n}\n\n// @Tags Auth\n// @Summary Get Setting For Login\n// @Success 200 {object} dto.SystemSetting\n// @Router /core/auth/setting [get]\nfunc (b *BaseApi) GetLoginSetting(c *gin.Context) {\n\tsettingInfo, err := settingService.GetSettingInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tip := common.GetRealClientIP(c)\n\tneedCaptcha := global.IPTracker.NeedCaptcha(ip)\n\tres := &dto.LoginSetting{\n\t\tIsDemo:      global.CONF.Base.IsDemo,\n\t\tIsIntl:      global.CONF.Base.Edition == \"intl\",\n\t\tIsFxplay:    global.CONF.Base.IsFxplay,\n\t\tIsOffLine:   global.CONF.Base.IsOffLine,\n\t\tLanguage:    settingInfo.Language,\n\t\tMenuTabs:    settingInfo.MenuTabs,\n\t\tPanelName:   settingInfo.PanelName,\n\t\tTheme:       settingInfo.Theme,\n\t\tNeedCaptcha: needCaptcha,\n\t}\n\tres.PasskeySetting = authService.PasskeyStatus(c)\n\thelper.SuccessWithData(c, res)\n}\n\nfunc saveLoginLogs(c *gin.Context, err error) {\n\tvar logs model.LoginLog\n\tif err != nil {\n\t\tlogs.Status = constant.StatusFailed\n\t\tlogs.Message = err.Error()\n\t} else {\n\t\tlogs.Status = constant.StatusSuccess\n\t}\n\tlogs.IP = c.ClientIP()\n\tlogs.Agent = c.GetHeader(\"User-Agent\")\n\t_ = logService.CreateLoginLog(logs)\n}\n\nfunc wrapLoginErr(msgKey string, err error) error {\n\tif err != nil {\n\t\treturn err\n\t}\n\tif msgKey == \"ErrAuth\" || msgKey == \"ErrEntrance\" {\n\t\treturn buserr.New(msgKey)\n\t}\n\treturn nil\n}\n\nfunc loadEntranceFromRequest(c *gin.Context) string {\n\tentranceItem := c.Request.Header.Get(\"EntranceCode\")\n\tvar entrance []byte\n\tif len(entranceItem) != 0 {\n\t\tentrance, _ = base64.StdEncoding.DecodeString(entranceItem)\n\t}\n\tif len(entrance) == 0 {\n\t\tcookieValue, err := c.Cookie(\"SecurityEntrance\")\n\t\tif err == nil {\n\t\t\tentrance, _ = base64.StdEncoding.DecodeString(cookieValue)\n\t\t}\n\t}\n\treturn string(entrance)\n}\n"
  },
  {
    "path": "core/app/api/v2/backup.go",
    "content": "package v2\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Backup Account\n// @Summary Create backup account\n// @Accept json\n// @Param request body dto.BackupOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/backups [post]\n// @x-panel-log {\"bodyKeys\":[\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建备份账号 [type]\",\"formatEN\":\"create backup account [type]\"}\nfunc (b *BaseApi) CreateBackup(c *gin.Context) {\n\tvar req dto.BackupOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Refresh token\n// @Accept json\n// @Param request body dto.OperateByName true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/backups/refresh/token [post]\nfunc (b *BaseApi) RefreshToken(c *gin.Context) {\n\tvar req dto.OperateByName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := backupService.RefreshToken(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Load backup account base info\n// @Accept json\n// @Success 200 {object} dto.BackupClientInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/backups/client/:clientType [get]\nfunc (b *BaseApi) LoadBackupClientInfo(c *gin.Context) {\n\tclientType, ok := c.Params.Get(\"clientType\")\n\tif !ok {\n\t\thelper.BadRequest(c, fmt.Errorf(\"error %s in path\", \"clientType\"))\n\t\treturn\n\t}\n\tdata, err := backupService.LoadBackupClientInfo(clientType)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags Backup Account\n// @Summary Delete backup account\n// @Accept json\n// @Param request body dto.OperateByName true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/backups/del [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"删除备份账号 [name]\",\"formatEN\":\"delete backup account [name]\"}\nfunc (b *BaseApi) DeleteBackup(c *gin.Context) {\n\tvar req dto.OperateByName\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupService.Delete(req.Name); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Backup Account\n// @Summary Update backup account\n// @Accept json\n// @Param request body dto.BackupOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/backups/update [post]\n// @x-panel-log {\"bodyKeys\":[\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新备份账号 [types]\",\"formatEN\":\"update backup account [types]\"}\nfunc (b *BaseApi) UpdateBackup(c *gin.Context) {\n\tvar req dto.BackupOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := backupService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "core/app/api/v2/command.go",
    "content": "package v2\n\nimport (\n\t\"encoding/csv\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Command\n// @Summary Upload command csv for list\n// @Success 200 {string} path\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands/import [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"上传快速命令文件\",\"formatEN\":\"upload quick commands with csv\"}\nfunc (b *BaseApi) UploadCommandCsv(c *gin.Context) {\n\tform, err := c.MultipartForm()\n\tif err != nil {\n\t\thelper.BadRequest(c, err)\n\t\treturn\n\t}\n\tfiles := form.File[\"file\"]\n\tif len(files) == 0 {\n\t\thelper.BadRequest(c, errors.New(\"no such files\"))\n\t\treturn\n\t}\n\tuploadFile, _ := files[0].Open()\n\treader := csv.NewReader(uploadFile)\n\tif _, err := reader.Read(); err != nil {\n\t\thelper.BadRequest(c, fmt.Errorf(\"read title failed, err: %v\", err))\n\t\treturn\n\t}\n\tgroupRepo := repo.NewIGroupRepo()\n\tgroup, _ := groupRepo.Get(repo.WithByType(\"command\"), groupRepo.WithByDefault(true))\n\tvar commands []dto.CommandInfo\n\tfor {\n\t\trecord, err := reader.Read()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, fmt.Errorf(\"read content failed, err: %v\", err))\n\t\t\treturn\n\t\t}\n\t\tif len(record) >= 2 {\n\t\t\tcommands = append(commands, dto.CommandInfo{\n\t\t\t\tName:        record[0],\n\t\t\t\tType:        \"command\",\n\t\t\t\tGroupID:     group.ID,\n\t\t\t\tCommand:     record[1],\n\t\t\t\tGroupBelong: group.Name,\n\t\t\t})\n\t\t}\n\t}\n\n\thelper.SuccessWithData(c, commands)\n}\n\n// @Tags Command\n// @Summary Export command\n// @Success 200 {string} path\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands/export [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"导出快速命令\",\"formatEN\":\"export quick commands\"}\nfunc (b *BaseApi) ExportCommands(c *gin.Context) {\n\tfile, err := commandService.Export()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, file)\n}\n\n// @Tags Command\n// @Summary Import command\n// @Success 200 {string} path\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands/import [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"导入快速命令\",\"formatEN\":\"import quick commands\"}\nfunc (b *BaseApi) ImportCommands(c *gin.Context) {\n\tvar req dto.CommandImport\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tgroupRepo := repo.NewIGroupRepo()\n\tgroup, _ := groupRepo.Get(repo.WithByType(\"command\"), groupRepo.WithByDefault(true))\n\tfor _, item := range req.Items {\n\t\tif item.GroupID == 0 {\n\t\t\titem.GroupID = group.ID\n\t\t}\n\t\t_ = commandService.Create(item)\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Command\n// @Summary Create command\n// @Accept json\n// @Param request body dto.CommandOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"command\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建快捷命令 [name][command]\",\"formatEN\":\"create quick command [name][command]\"}\nfunc (b *BaseApi) CreateCommand(c *gin.Context) {\n\tvar req dto.CommandOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := commandService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Command\n// @Summary Page commands\n// @Accept json\n// @Param request body dto.SearchWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands/search [post]\nfunc (b *BaseApi) SearchCommand(c *gin.Context) {\n\tvar req dto.SearchCommandWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := commandService.SearchWithPage(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Command\n// @Summary Tree commands\n// @Accept json\n// @Param request body dto.OperateByType true \"request\"\n// @Success 200 {array} dto.CommandTree\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands/tree [get]\nfunc (b *BaseApi) SearchCommandTree(c *gin.Context) {\n\tvar req dto.OperateByType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tlist, err := commandService.SearchForTree(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Command\n// @Summary List commands\n// @Accept json\n// @Param request body dto.OperateByType true \"request\"\n// @Success 200 {object} dto.CommandInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands/command [get]\nfunc (b *BaseApi) ListCommand(c *gin.Context) {\n\tvar req dto.OperateByType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tlist, err := commandService.List(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags Command\n// @Summary Delete command\n// @Accept json\n// @Param request body dto.OperateByIDs true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"commands\",\"output_column\":\"name\",\"output_value\":\"names\"}],\"formatZH\":\"删除快捷命令 [names]\",\"formatEN\":\"delete quick command [names]\"}\nfunc (b *BaseApi) DeleteCommand(c *gin.Context) {\n\tvar req dto.OperateByIDs\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := commandService.Delete(req.IDs); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Command\n// @Summary Update command\n// @Accept json\n// @Param request body dto.CommandOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/commands/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新快捷命令 [name]\",\"formatEN\":\"update quick command [name]\"}\nfunc (b *BaseApi) UpdateCommand(c *gin.Context) {\n\tvar req dto.CommandOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := commandService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "core/app/api/v2/entry.go",
    "content": "package v2\n\nimport \"github.com/1Panel-dev/1Panel/core/app/service\"\n\ntype ApiGroup struct {\n\tBaseApi\n}\n\nvar ApiGroupApp = new(ApiGroup)\n\nvar (\n\tauthService    = service.NewIAuthService()\n\tbackupService  = service.NewIBackupService()\n\tsettingService = service.NewISettingService()\n\tlogService     = service.NewILogService()\n\tupgradeService = service.NewIUpgradeService()\n\tgroupService   = service.NewIGroupService()\n\tcommandService = service.NewICommandService()\n\tscriptService  = service.NewIScriptService()\n)\n"
  },
  {
    "path": "core/app/api/v2/group.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags System Group\n// @Summary Create group\n// @Accept json\n// @Param request body dto.GroupCreate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/groups [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"创建组 [name][type]\",\"formatEN\":\"create group [name][type]\"}\nfunc (b *BaseApi) CreateGroup(c *gin.Context) {\n\tvar req dto.GroupCreate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := groupService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Group\n// @Summary Delete group\n// @Accept json\n// @Param request body dto.OperateByID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/groups/del [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"groups\",\"output_column\":\"name\",\"output_value\":\"name\"},{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"groups\",\"output_column\":\"type\",\"output_value\":\"type\"}],\"formatZH\":\"删除组 [type][name]\",\"formatEN\":\"delete group [type][name]\"}\nfunc (b *BaseApi) DeleteGroup(c *gin.Context) {\n\tvar req dto.OperateByID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := groupService.Delete(req.ID); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Group\n// @Summary Update group\n// @Accept json\n// @Param request body dto.GroupUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/groups/update [post]\n// @x-panel-log {\"bodyKeys\":[\"name\",\"type\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新组 [name][type]\",\"formatEN\":\"update group [name][type]\"}\nfunc (b *BaseApi) UpdateGroup(c *gin.Context) {\n\tvar req dto.GroupUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := groupService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Group\n// @Summary List groups\n// @Accept json\n// @Param request body dto.GroupSearch true \"request\"\n// @Success 200 {array} dto.OperateByType\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/groups/search [post]\nfunc (b *BaseApi) ListGroup(c *gin.Context) {\n\tvar req dto.OperateByType\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tlist, err := groupService.List(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, list)\n}\n"
  },
  {
    "path": "core/app/api/v2/helper/helper.go",
    "content": "package helper\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/res\"\n\t\"net/http\"\n\t\"strconv\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc ErrorWithDetail(ctx *gin.Context, code int, msgKey string, err error) {\n\tres := dto.Response{\n\t\tCode:    code,\n\t\tMessage: \"\",\n\t}\n\tif msgKey == \"ErrCaptchaCode\" || msgKey == \"ErrAuth\" {\n\t\tres.Code = 401\n\t\tres.Message = msgKey\n\t}\n\tres.Message = i18n.GetMsgWithMap(msgKey, map[string]interface{}{\"detail\": err})\n\tctx.JSON(http.StatusOK, res)\n\tctx.Abort()\n}\n\nfunc InternalServer(ctx *gin.Context, err error) {\n\tErrorWithDetail(ctx, http.StatusInternalServerError, \"ErrInternalServer\", err)\n}\n\nfunc BadRequest(ctx *gin.Context, err error) {\n\tErrorWithDetail(ctx, http.StatusBadRequest, \"ErrInvalidParams\", err)\n}\n\nfunc BadAuth(ctx *gin.Context, msgKey string, err error) {\n\tErrorWithDetail(ctx, http.StatusUnauthorized, msgKey, err)\n}\n\nfunc SuccessWithData(ctx *gin.Context, data interface{}) {\n\tif data == nil {\n\t\tdata = gin.H{}\n\t}\n\tres := dto.Response{\n\t\tCode: http.StatusOK,\n\t\tData: data,\n\t}\n\tctx.JSON(http.StatusOK, res)\n\tctx.Abort()\n}\n\nfunc Success(ctx *gin.Context) {\n\tres := dto.Response{\n\t\tCode:    http.StatusOK,\n\t\tMessage: \"success\",\n\t}\n\tctx.JSON(http.StatusOK, res)\n\tctx.Abort()\n}\n\nfunc CheckBindAndValidate(req interface{}, c *gin.Context) error {\n\tif err := c.ShouldBindJSON(req); err != nil {\n\t\tErrorWithDetail(c, http.StatusBadRequest, \"ErrInvalidParams\", err)\n\t\treturn err\n\t}\n\tif err := global.VALID.Struct(req); err != nil {\n\t\tErrorWithDetail(c, http.StatusBadRequest, \"ErrInvalidParams\", err)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc ErrWithHtml(ctx *gin.Context, code int, scope string) {\n\tif code == 444 {\n\t\tctx.String(444, \"\")\n\t\tctx.Abort()\n\t\treturn\n\t}\n\tfile := fmt.Sprintf(\"html/%d.html\", code)\n\tif code == 200 && scope != \"\" {\n\t\tfile = fmt.Sprintf(\"html/200_%s.html\", scope)\n\t}\n\tdata, err := res.ErrorMsg.ReadFile(file)\n\tif err != nil {\n\t\tctx.String(http.StatusInternalServerError, \"Internal Server Error\")\n\t\tctx.Abort()\n\t\treturn\n\t}\n\tctx.Data(code, \"text/html; charset=utf-8\", data)\n\tctx.Abort()\n}\n\nfunc GetParamID(c *gin.Context) (uint, error) {\n\tidParam, ok := c.Params.Get(\"id\")\n\tif !ok {\n\t\treturn 0, errors.New(\"error id in path\")\n\t}\n\tintNum, _ := strconv.Atoi(idParam)\n\treturn uint(intNum), nil\n}\n"
  },
  {
    "path": "core/app/api/v2/logs.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags Logs\n// @Summary Page login logs\n// @Accept json\n// @Param request body dto.SearchLgLogWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/logs/login [post]\nfunc (b *BaseApi) GetLoginLogs(c *gin.Context) {\n\tvar req dto.SearchLgLogWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := logService.PageLoginLog(c, req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Logs\n// @Summary Page operation logs\n// @Accept json\n// @Param request body dto.SearchOpLogWithPage true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/logs/operation [post]\nfunc (b *BaseApi) GetOperationLogs(c *gin.Context) {\n\tvar req dto.SearchOpLogWithPage\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := logService.PageOperationLog(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags Logs\n// @Summary Clean operation logs\n// @Accept json\n// @Param request body dto.CleanLog true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/logs/clean [post]\n// @x-panel-log {\"bodyKeys\":[\"logType\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"清空 [logType] 日志信息\",\"formatEN\":\"Clean the [logType] log information\"}\nfunc (b *BaseApi) CleanLogs(c *gin.Context) {\n\tvar req dto.CleanLog\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := logService.CleanLogs(req.LogType); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "core/app/api/v2/script_library.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/service\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/ssh\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/terminal\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/gorilla/websocket\"\n\t\"github.com/pkg/errors\"\n)\n\n// @Tags ScriptLibrary\n// @Summary Add script\n// @Accept json\n// @Param request body dto.ScriptOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/script [post]\n// @x-panel-log {\"bodyKeys\":[\"name\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"添加脚本库脚本 [name]\",\"formatEN\":\"add script [name]\"}\nfunc (b *BaseApi) CreateScript(c *gin.Context) {\n\tvar req dto.ScriptOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := scriptService.Create(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags ScriptLibrary\n// @Summary Page script\n// @Accept json\n// @Param request body dto.SearchPageWithGroup true \"request\"\n// @Success 200 {object} dto.PageResult\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/script/search [post]\nfunc (b *BaseApi) SearchScript(c *gin.Context) {\n\tvar req dto.SearchPageWithGroup\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\ttotal, list, err := scriptService.Search(c, req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, dto.PageResult{\n\t\tItems: list,\n\t\tTotal: total,\n\t})\n}\n\n// @Tags ScriptLibrary\n// @Summary Delete script\n// @Accept json\n// @Param request body dto.OperateByIDs true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/script/del [post]\n// @x-panel-log {\"bodyKeys\":[\"ids\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"ids\",\"isList\":true,\"db\":\"script_librarys\",\"output_column\":\"name\",\"output_value\":\"names\"}],\"formatZH\":\"删除脚本库脚本 [names]\",\"formatEN\":\"delete script [names]\"}\nfunc (b *BaseApi) DeleteScript(c *gin.Context) {\n\tvar req dto.OperateByIDs\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := scriptService.Delete(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags ScriptLibrary\n// @Summary Sync script from remote\n// @Accept json\n// @Param request body dto.OperateByTaskID true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/script/sync [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"同步脚本库脚本\",\"formatEN\":\"sync scripts\"}\nfunc (b *BaseApi) SyncScript(c *gin.Context) {\n\tvar req dto.OperateByTaskID\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := scriptService.Sync(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags ScriptLibrary\n// @Summary Update script\n// @Accept json\n// @Param request body dto.ScriptOperate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/script/update [post]\n// @x-panel-log {\"bodyKeys\":[\"id\"],\"paramKeys\":[],\"BeforeFunctions\":[{\"input_column\":\"id\",\"input_value\":\"id\",\"isList\":false,\"db\":\"cronjobs\",\"output_column\":\"name\",\"output_value\":\"name\"}],\"formatZH\":\"更新脚本库脚本 [name]\",\"formatEN\":\"update script [name]\"}\nfunc (b *BaseApi) UpdateScript(c *gin.Context) {\n\tvar req dto.ScriptOperate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := scriptService.Update(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) RunScript(c *gin.Context) {\n\twsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"gin context http handler failed, err: %v\", err)\n\t\treturn\n\t}\n\tdefer wsConn.Close()\n\n\tif global.CONF.Base.IsDemo {\n\t\tif wshandleError(wsConn, errors.New(\"   demo server, prohibit this operation!\")) {\n\t\t\treturn\n\t\t}\n\t}\n\n\tcols, err := strconv.Atoi(c.DefaultQuery(\"cols\", \"80\"))\n\tif wshandleError(wsConn, errors.WithMessage(err, \"invalid param cols in request\")) {\n\t\treturn\n\t}\n\trows, err := strconv.Atoi(c.DefaultQuery(\"rows\", \"40\"))\n\tif wshandleError(wsConn, errors.WithMessage(err, \"invalid param rows in request\")) {\n\t\treturn\n\t}\n\tscriptID := c.Query(\"script_id\")\n\tcurrentNode := c.Query(\"operateNode\")\n\tintNum, _ := strconv.Atoi(scriptID)\n\tif intNum == 0 {\n\t\tif wshandleError(wsConn, fmt.Errorf(\"   no such script %v in library, please check and try again!\", scriptID)) {\n\t\t\treturn\n\t\t}\n\t}\n\tscriptItem, err := service.LoadScriptInfo(uint(intNum))\n\tif wshandleError(wsConn, err) {\n\t\treturn\n\t}\n\n\tquitChan := make(chan bool, 3)\n\tif currentNode == \"local\" {\n\t\tslave, err := terminal.NewCommand(scriptItem.Script)\n\t\tif wshandleError(wsConn, err) {\n\t\t\treturn\n\t\t}\n\t\tdefer slave.Close()\n\n\t\ttty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)\n\t\tif wshandleError(wsConn, err) {\n\t\t\treturn\n\t\t}\n\n\t\tquitChan := make(chan bool, 3)\n\t\ttty.Start(quitChan)\n\t\tgo slave.Wait(quitChan)\n\t} else {\n\t\tconnInfo, _, err := xpack.LoadNodeInfo(currentNode)\n\t\tif wshandleError(wsConn, errors.WithMessage(err, \"invalid param rows in request\")) {\n\t\t\treturn\n\t\t}\n\n\t\tfileName := \"\"\n\t\tvar translations = make(map[string]string)\n\t\t_ = json.Unmarshal([]byte(scriptItem.Name), &translations)\n\t\tif name, ok := translations[\"en\"]; ok {\n\t\t\tfileName = strings.ReplaceAll(name, \" \", \"_\")\n\t\t} else {\n\t\t\tfileName = strings.ReplaceAll(scriptItem.Name, \" \", \"_\")\n\t\t}\n\t\tclient, err := ssh.NewClient(*connInfo)\n\t\tif wshandleError(wsConn, errors.WithMessage(err, \"set up the connection failed. Please check the host information\")) {\n\t\t\treturn\n\t\t}\n\t\tsudoItem := client.SudoHandleCmd()\n\t\tdefer func() {\n\t\t\t_, _ = client.Runf(\"%s rm -rf %s\", sudoItem, fileName)\n\t\t\tclient.Close()\n\t\t}()\n\t\tstd, err := client.Runf(\"%s touch %s && %s chmod 777 %s && %s cat > %s <<'MYMARKER'\\n%s\\nMYMARKER\\n\", sudoItem, fileName, sudoItem, fileName, sudoItem, fileName, scriptItem.Script)\n\t\tif wshandleError(wsConn, errors.WithMessage(err, fmt.Sprintf(\"touch script file failed, err: %s. Please check and retry\", std))) {\n\t\t\treturn\n\t\t}\n\t\tinitCmd := fmt.Sprintf(\"%s bash %s\", sudoItem, fileName)\n\n\t\tsws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, initCmd)\n\t\tif wshandleError(wsConn, err) {\n\t\t\treturn\n\t\t}\n\t\tdefer sws.Close()\n\t\tsws.Start(quitChan)\n\t\tgo sws.Wait(quitChan)\n\t}\n\n\t<-quitChan\n\n\tglobal.LOG.Info(\"websocket finished\")\n\tglobal.LOG.Info(\"websocket finished\")\n\tdt := time.Now().Add(time.Second)\n\t_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)\n}\n\nvar upGrader = websocket.Upgrader{\n\tReadBufferSize:  4096,\n\tWriteBufferSize: 16384,\n\tCheckOrigin: func(r *http.Request) bool {\n\t\treturn true\n\t},\n}\n\nfunc wshandleError(ws *websocket.Conn, err error) bool {\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"handler ws faled:, err: %v\", err)\n\t\tdt := time.Now().Add(time.Second)\n\t\tif ctlerr := ws.WriteControl(websocket.CloseMessage, []byte(err.Error()), dt); ctlerr != nil {\n\t\t\twsData, marshalErr := json.Marshal(terminal.WsMsg{\n\t\t\t\tType: terminal.WsMsgCmd,\n\t\t\t\tData: base64.StdEncoding.EncodeToString([]byte(err.Error())),\n\t\t\t})\n\t\t\tif marshalErr != nil {\n\t\t\t\t_ = ws.WriteMessage(websocket.TextMessage, []byte(\"{\\\"type\\\":\\\"cmd\\\",\\\"data\\\":\\\"failed to encoding to json\\\"}\"))\n\t\t\t} else {\n\t\t\t\t_ = ws.WriteMessage(websocket.TextMessage, wsData)\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/app/api/v2/setting.go",
    "content": "package v2\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/mfa\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags System Setting\n// @Summary Load system setting info\n// @Success 200 {object} dto.SettingInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/search [post]\nfunc (b *BaseApi) GetSettingInfo(c *gin.Context) {\n\tsetting, err := settingService.GetSettingInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, setting)\n}\n\n// @Tags System Setting\n// @Summary Load system setting by key\n// @Success 200 {string} info\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/by [post]\nfunc (b *BaseApi) GetSettingByKey(c *gin.Context) {\n\tvar req dto.SettingKey\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tsetting, err := repo.NewISettingRepo().GetValueByKey(req.Key)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, setting)\n}\n\n// @Tags System Setting\n// @Summary Load system terminal setting info\n// @Success 200 {object} dto.TerminalInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/terminal/search [post]\nfunc (b *BaseApi) GetTerminalSettingInfo(c *gin.Context) {\n\tsetting, err := settingService.GetTerminalInfo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, setting)\n}\n\n// @Tags System Setting\n// @Summary Load system available status\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/search/available [get]\nfunc (b *BaseApi) GetSystemAvailable(c *gin.Context) {\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update system setting\n// @Accept json\n// @Param request body dto.SettingUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/update [post]\n// @x-panel-log {\"bodyKeys\":[\"key\",\"value\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改系统配置 [key] => [value]\",\"formatEN\":\"update system setting [key] => [value]\"}\nfunc (b *BaseApi) UpdateSetting(c *gin.Context) {\n\tvar req dto.SettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif req.Key == \"SecurityEntrance\" {\n\t\tif !checkEntrancePattern(req.Value) {\n\t\t\thelper.ErrorWithDetail(c, http.StatusBadRequest, \"ErrInvalidParams\", buserr.WithName(\"ErrEntranceFormat\", req.Value))\n\t\t\treturn\n\t\t}\n\t}\n\tif req.Key == \"PasskeyTrustedProxies\" {\n\t\tvalue, err := normalizePasskeyTrustedProxies(req.Value)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.Value = value\n\t}\n\n\tif err := settingService.Update(req.Key, req.Value); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\tif req.Key == \"SecurityEntrance\" {\n\t\tentranceValue := base64.StdEncoding.EncodeToString([]byte(req.Value))\n\t\tc.SetCookie(\"SecurityEntrance\", entranceValue, 0, \"\", \"\", false, true)\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update system terminal setting\n// @Accept json\n// @Param request body dto.TerminalInfo true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/terminal/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改系统终端配置\",\"formatEN\":\"update system terminal setting\"}\nfunc (b *BaseApi) UpdateTerminalSetting(c *gin.Context) {\n\tvar req dto.TerminalInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.UpdateTerminal(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update proxy setting\n// @Accept json\n// @Param request body dto.ProxyUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/proxy/update [post]\n// @x-panel-log {\"bodyKeys\":[\"proxyUrl\",\"proxyPort\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"服务器代理配置 [proxyPort]:[proxyPort]\",\"formatEN\":\"set proxy [proxyPort]:[proxyPort].\"}\nfunc (b *BaseApi) UpdateProxy(c *gin.Context) {\n\tvar req dto.ProxyUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif len(req.ProxyPasswd) != 0 && len(req.ProxyType) != 0 {\n\t\tpass, err := base64.StdEncoding.DecodeString(req.ProxyPasswd)\n\t\tif err != nil {\n\t\t\thelper.BadRequest(c, err)\n\t\t\treturn\n\t\t}\n\t\treq.ProxyPasswd = string(pass)\n\t}\n\n\tif err := settingService.UpdateProxy(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update system setting\n// @Accept json\n// @Param request body dto.SettingUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/menu/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"隐藏高级功能菜单\",\"formatEN\":\"Hide advanced feature menu.\"}\nfunc (b *BaseApi) UpdateMenu(c *gin.Context) {\n\tvar req dto.SettingUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.Update(req.Key, req.Value); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags Menu Setting\n// @Summary Default menu\n// @Accept json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/menu/default [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"初始化菜单\",\"formatEN\":\"Init menu.\"}\nfunc (b *BaseApi) DefaultMenu(c *gin.Context) {\n\tif err := settingService.DefaultMenu(); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update system password\n// @Accept json\n// @Param request body dto.PasswordUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/password/update [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改系统密码\",\"formatEN\":\"update system password\"}\nfunc (b *BaseApi) UpdatePassword(c *gin.Context) {\n\tvar req dto.PasswordUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.UpdatePassword(c, req.OldPassword, req.NewPassword); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update system ssl\n// @Accept json\n// @Param request body dto.SSLUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/ssl/update [post]\n// @x-panel-log {\"bodyKeys\":[\"ssl\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改系统 ssl => [ssl]\",\"formatEN\":\"update system ssl => [ssl]\"}\nfunc (b *BaseApi) UpdateSSL(c *gin.Context) {\n\tvar req dto.SSLUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.UpdateSSL(c, req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Load system cert info\n// @Success 200 {object} dto.SSLInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/ssl/info [get]\nfunc (b *BaseApi) LoadFromCert(c *gin.Context) {\n\tinfo, err := settingService.LoadFromCert()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, info)\n}\n\n// @Tags System Setting\n// @Summary Download system cert\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/ssl/download [post]\nfunc (b *BaseApi) DownloadSSL(c *gin.Context) {\n\tpathItem := path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.crt\")\n\tif _, err := os.Stat(pathItem); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\tc.File(pathItem)\n}\n\n// @Tags System Setting\n// @Summary Load system address\n// @Accept json\n// @Success 200 {array} string\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/interface [get]\nfunc (b *BaseApi) LoadInterfaceAddr(c *gin.Context) {\n\tdata, err := settingService.LoadInterfaceAddr()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, data)\n}\n\n// @Tags System Setting\n// @Summary Update system bind info\n// @Accept json\n// @Param request body dto.BindInfo true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/bind/update [post]\n// @x-panel-log {\"bodyKeys\":[\"ipv6\", \"bindAddress\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改系统监听信息 => ipv6: [ipv6], 监听 IP: [bindAddress]\",\"formatEN\":\"update system bind info => ipv6: [ipv6], 监听 IP: [bindAddress]\"}\nfunc (b *BaseApi) UpdateBindInfo(c *gin.Context) {\n\tvar req dto.BindInfo\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.UpdateBindInfo(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Update system port\n// @Accept json\n// @Param request body dto.PortUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/port/update [post]\n// @x-panel-log {\"bodyKeys\":[\"serverPort\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"修改系统端口 => [serverPort]\",\"formatEN\":\"update system port => [serverPort]\"}\nfunc (b *BaseApi) UpdatePort(c *gin.Context) {\n\tvar req dto.PortUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.UpdatePort(req.ServerPort); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Reset system password expired\n// @Accept json\n// @Param request body dto.PasswordUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/expired/handle [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"重置过期密码\",\"formatEN\":\"reset an expired Password\"}\nfunc (b *BaseApi) HandlePasswordExpired(c *gin.Context) {\n\tvar req dto.PasswordUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.HandlePasswordExpired(c, req.OldPassword, req.NewPassword); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Load mfa info\n// @Accept json\n// @Param request body dto.MfaCredential true \"request\"\n// @Success 200 {object} mfa.Otp\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/mfa [post]\nfunc (b *BaseApi) LoadMFA(c *gin.Context) {\n\tvar req dto.MfaRequest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\totp, err := mfa.GetOtp(\"admin\", req.Title, req.Interval)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.SuccessWithData(c, otp)\n}\n\n// @Tags System Setting\n// @Summary Bind mfa\n// @Accept json\n// @Param request body dto.MfaCredential true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/mfa/bind [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"mfa 绑定\",\"formatEN\":\"bind mfa\"}\nfunc (b *BaseApi) MFABind(c *gin.Context) {\n\tvar req dto.MfaCredential\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tsuccess := mfa.ValidCode(req.Code, req.Interval, req.Secret)\n\tif !success {\n\t\thelper.InternalServer(c, errors.New(\"code is not valid\"))\n\t\treturn\n\t}\n\n\tif err := settingService.Update(\"MFAInterval\", req.Interval); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\tif err := settingService.Update(\"MFAStatus\", constant.StatusEnable); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\tif err := settingService.Update(\"MFASecret\", req.Secret); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary Begin passkey registration\n// @Accept json\n// @Param request body dto.PasskeyRegisterRequest true \"request\"\n// @Success 200 {object} dto.PasskeyBeginResponse\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/passkey/register/begin [post]\nfunc (b *BaseApi) PasskeyRegisterBegin(c *gin.Context) {\n\tvar req dto.PasskeyRegisterRequest\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tres, msgKey, err := authService.PasskeyBeginRegister(c, req.Name)\n\tif msgKey != \"\" {\n\t\thelper.ErrorWithDetail(c, http.StatusBadRequest, msgKey, err)\n\t\treturn\n\t}\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags System Setting\n// @Summary Finish passkey registration\n// @Accept json\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/passkey/register/finish [post]\nfunc (b *BaseApi) PasskeyRegisterFinish(c *gin.Context) {\n\tsessionID := c.GetHeader(\"Passkey-Session\")\n\tmsgKey, err := authService.PasskeyFinishRegister(c, sessionID)\n\tif msgKey != \"\" {\n\t\thelper.ErrorWithDetail(c, http.StatusBadRequest, msgKey, err)\n\t\treturn\n\t}\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary List passkeys\n// @Success 200 {array} dto.PasskeyInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/passkey/list [get]\nfunc (b *BaseApi) PasskeyList(c *gin.Context) {\n\tlist, err := authService.PasskeyList()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, list)\n}\n\n// @Tags System Setting\n// @Summary Delete passkey\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/passkey/{id} [delete]\nfunc (b *BaseApi) PasskeyDelete(c *gin.Context) {\n\tid := c.Param(\"id\")\n\tif id == \"\" {\n\t\thelper.BadRequest(c, errors.New(\"passkey id is required\"))\n\t\treturn\n\t}\n\tif err := authService.PasskeyDelete(id); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc (b *BaseApi) ReloadSSL(c *gin.Context) {\n\tclientIP := c.ClientIP()\n\tif clientIP != \"127.0.0.1\" {\n\t\thelper.InternalServer(c, errors.New(\"only localhost can reload ssl\"))\n\t\treturn\n\t}\n\tif err := settingService.UpdateSystemSSL(); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags System Setting\n// @Summary generate api key\n// @Accept json\n// @Success 200 {string} key\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/api/config/generate/key [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"生成 API 接口密钥\",\"formatEN\":\"generate api key\"}\nfunc (b *BaseApi) GenerateApiKey(c *gin.Context) {\n\tpanelToken := c.GetHeader(\"1Panel-Token\")\n\tif panelToken != \"\" {\n\t\thelper.BadAuth(c, \"ErrApiConfigDisable\", nil)\n\t\treturn\n\t}\n\tapiKey, err := settingService.GenerateApiKey()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, apiKey)\n}\n\n// @Tags System Setting\n// @Summary Update api config\n// @Accept json\n// @Param request body dto.ApiInterfaceConfig true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/api/config/update [post]\n// @x-panel-log {\"bodyKeys\":[\"ipWhiteList\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新 API 接口配置 => IP 白名单: [ipWhiteList]\",\"formatEN\":\"update api config => IP White List: [ipWhiteList]\"}\nfunc (b *BaseApi) UpdateApiConfig(c *gin.Context) {\n\tpanelToken := c.GetHeader(\"1Panel-Token\")\n\tif panelToken != \"\" {\n\t\thelper.BadAuth(c, \"ErrApiConfigDisable\", nil)\n\t\treturn\n\t}\n\tvar req dto.ApiInterfaceConfig\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := settingService.UpdateApiConfig(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Update appstore config\n// @Accept json\n// @Param request body dto.AppstoreUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/apps/store/update [post]\nfunc (b *BaseApi) UpdateAppstoreConfig(c *gin.Context) {\n\tvar req dto.AppstoreUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\terr := settingService.UpdateAppstoreConfig(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\n// @Tags App\n// @Summary Get appstore config\n// @Success 200 {object} dto.AppstoreConfig\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/apps/store/config [get]\nfunc (b *BaseApi) GetAppstoreConfig(c *gin.Context) {\n\tres, err := settingService.GetAppstoreConfig()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, res)\n}\n\n// @Tags System Setting\n// @Summary Load dashboard memo\n// @Success 200 {string} memo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/memo [get]\nfunc (b *BaseApi) GetMemo(c *gin.Context) {\n\tmemo, err := settingService.GetMemo()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, memo)\n}\n\n// @Tags System Setting\n// @Summary Update dashboard memo\n// @Accept json\n// @Param request body dto.MemoUpdate true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/memo [post]\n// @x-panel-log {\"bodyKeys\":[],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新仪表盘备忘录\",\"formatEN\":\"update dashboard memo\"}\nfunc (b *BaseApi) UpdateMemo(c *gin.Context) {\n\tvar req dto.MemoUpdate\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\tif err := settingService.UpdateMemo(req.Content); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n\nfunc checkEntrancePattern(val string) bool {\n\tif len(val) == 0 {\n\t\treturn true\n\t}\n\tresult, _ := regexp.MatchString(\"^[a-zA-Z0-9]{5,116}$\", val)\n\tif !result {\n\t\treturn false\n\t}\n\tlowerVal := strings.ToLower(val)\n\tfor key := range constant.WebUrlMap {\n\t\tif key == \"/\" || !strings.HasPrefix(key, \"/\") {\n\t\t\tcontinue\n\t\t}\n\t\tif strings.Count(key, \"/\") != 1 {\n\t\t\tcontinue\n\t\t}\n\t\tsegment := strings.ToLower(strings.TrimPrefix(key, \"/\"))\n\t\tif len(segment) < 5 {\n\t\t\tcontinue\n\t\t}\n\t\tif lowerVal == segment {\n\t\t\treturn false\n\t\t}\n\t}\n\tassetsList := [2]string{\"public\", \"assets\"}\n\tfor _, item := range assetsList {\n\t\tif lowerVal == item {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc normalizePasskeyTrustedProxies(value string) (string, error) {\n\tif strings.TrimSpace(value) == \"\" {\n\t\treturn \"\", nil\n\t}\n\tlines := strings.Split(value, \"\\n\")\n\tvalidLines := make([]string, 0, len(lines))\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tvalidLines = append(validLines, line)\n\t}\n\tif len(validLines) == 0 {\n\t\treturn \"\", nil\n\t}\n\tnormalized := strings.Join(validLines, \"\\n\")\n\tif _, err := common.HandleIPList(normalized); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn normalized, nil\n}\n"
  },
  {
    "path": "core/app/api/v2/upgrade.go",
    "content": "package v2\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/gin-gonic/gin\"\n)\n\n// @Tags System Setting\n// @Summary Load upgrade info\n// @Success 200 {object} dto.UpgradeInfo\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/upgrade [get]\nfunc (b *BaseApi) GetUpgradeInfo(c *gin.Context) {\n\tinfo, err := upgradeService.SearchUpgrade()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, info)\n}\n\n// @Tags System Setting\n// @Summary Load upgrade notes\n// @Success 200 {array} dto.ReleasesNotes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/upgrade/releases [get]\nfunc (b *BaseApi) LoadRelease(c *gin.Context) {\n\tnotes, err := upgradeService.LoadRelease()\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, notes)\n}\n\n// @Tags System Setting\n// @Summary Load release notes by version\n// @Accept json\n// @Param request body dto.Upgrade true \"request\"\n// @Success 200 {string} notes\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/upgrade/notes [post]\nfunc (b *BaseApi) GetNotesByVersion(c *gin.Context) {\n\tvar req dto.Upgrade\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tnotes, err := upgradeService.LoadNotes(req)\n\tif err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.SuccessWithData(c, notes)\n}\n\n// @Tags System Setting\n// @Summary Upgrade\n// @Accept json\n// @Param request body dto.Upgrade true \"request\"\n// @Success 200\n// @Security ApiKeyAuth\n// @Security Timestamp\n// @Router /core/settings/upgrade [post]\n// @x-panel-log {\"bodyKeys\":[\"version\"],\"paramKeys\":[],\"BeforeFunctions\":[],\"formatZH\":\"更新系统 => [version]\",\"formatEN\":\"upgrade system => [version]\"}\nfunc (b *BaseApi) Upgrade(c *gin.Context) {\n\tvar req dto.Upgrade\n\tif err := helper.CheckBindAndValidate(&req, c); err != nil {\n\t\treturn\n\t}\n\n\tif err := upgradeService.Upgrade(req); err != nil {\n\t\thelper.InternalServer(c, err)\n\t\treturn\n\t}\n\thelper.Success(c)\n}\n"
  },
  {
    "path": "core/app/dto/auth.go",
    "content": "package dto\n\ntype CaptchaResponse struct {\n\tCaptchaID string `json:\"captchaID\"`\n\tImagePath string `json:\"imagePath\"`\n}\n\ntype UserLoginInfo struct {\n\tName      string `json:\"name\"`\n\tToken     string `json:\"token\"`\n\tMfaStatus string `json:\"mfaStatus\"`\n}\n\ntype PasskeyBeginResponse struct {\n\tSessionID string      `json:\"sessionId\"`\n\tPublicKey interface{} `json:\"publicKey\"`\n}\n\ntype MfaRequest struct {\n\tTitle    string `json:\"title\" validate:\"required\"`\n\tInterval int    `json:\"interval\" validate:\"required\"`\n}\n\ntype MfaCredential struct {\n\tSecret   string `json:\"secret\" validate:\"required\"`\n\tCode     string `json:\"code\" validate:\"required\"`\n\tInterval string `json:\"interval\" validate:\"required\"`\n}\n\ntype Login struct {\n\tName      string `json:\"name\" validate:\"required\"`\n\tPassword  string `json:\"password\" validate:\"required\"`\n\tCaptcha   string `json:\"captcha\"`\n\tCaptchaID string `json:\"captchaID\"`\n\tLanguage  string `json:\"language\" validate:\"required,oneof=zh en 'zh-Hant' ko ja ru ms 'pt-BR' tr 'es-ES'\"`\n}\n\ntype MFALogin struct {\n\tName     string `json:\"name\" validate:\"required\"`\n\tPassword string `json:\"password\" validate:\"required\"`\n\tCode     string `json:\"code\" validate:\"required\"`\n}\n\ntype SystemSetting struct {\n\tIsDemo   bool   `json:\"isDemo\"`\n\tLanguage string `json:\"language\"`\n\tIsIntl   bool   `json:\"isIntl\"`\n}\n"
  },
  {
    "path": "core/app/dto/backup.go",
    "content": "package dto\n\nimport \"time\"\n\ntype SyncToAgent struct {\n\tName      string `json:\"name\" validate:\"required\"`\n\tOperation string `json:\"operation\" validate:\"required,oneof=create delere update\"`\n\tData      string `json:\"data\"`\n}\n\ntype BackupOperate struct {\n\tID         uint   `json:\"id\"`\n\tName       string `json:\"name\"`\n\tType       string `json:\"type\" validate:\"required\"`\n\tIsPublic   bool   `json:\"isPublic\"`\n\tBucket     string `json:\"bucket\"`\n\tAccessKey  string `json:\"accessKey\"`\n\tCredential string `json:\"credential\"`\n\tBackupPath string `json:\"backupPath\"`\n\tVars       string `json:\"vars\" validate:\"required\"`\n\n\tRememberAuth bool `json:\"rememberAuth\"`\n}\n\ntype BackupInfo struct {\n\tID         uint      `json:\"id\"`\n\tName       string    `json:\"name\"`\n\tType       string    `json:\"type\"`\n\tIsPublic   bool      `json:\"isPublic\"`\n\tBucket     string    `json:\"bucket\"`\n\tAccessKey  string    `json:\"accessKey\"`\n\tCredential string    `json:\"credential\"`\n\tBackupPath string    `json:\"backupPath\"`\n\tVars       string    `json:\"vars\"`\n\tCreatedAt  time.Time `json:\"createdAt\"`\n\n\tRememberAuth bool `json:\"rememberAuth\"`\n}\n\ntype BackupClientInfo struct {\n\tClientID     string `json:\"client_id\"`\n\tClientSecret string `json:\"client_secret\"`\n\tRedirectUri  string `json:\"redirect_uri\"`\n}\n\ntype ForBuckets struct {\n\tType       string `json:\"type\" validate:\"required\"`\n\tAccessKey  string `json:\"accessKey\"`\n\tCredential string `json:\"credential\" validate:\"required\"`\n\tVars       string `json:\"vars\" validate:\"required\"`\n}\n"
  },
  {
    "path": "core/app/dto/command.go",
    "content": "package dto\n\ntype SearchCommandWithPage struct {\n\tPageInfo\n\tOrderBy string `json:\"orderBy\" validate:\"required,oneof=name command createdAt\"`\n\tOrder   string `json:\"order\" validate:\"required,oneof=null ascending descending\"`\n\tGroupID uint   `json:\"groupID\"`\n\tType    string `json:\"type\" validate:\"required,oneof=redis command\"`\n\tInfo    string `json:\"info\"`\n}\n\ntype CommandImport struct {\n\tItems []CommandOperate `json:\"items\"`\n}\n\ntype CommandOperate struct {\n\tID          uint   `json:\"id\"`\n\tType        string `json:\"type\"`\n\tGroupID     uint   `json:\"groupID\"`\n\tGroupBelong string `json:\"groupBelong\"`\n\tName        string `json:\"name\" validate:\"required\"`\n\tCommand     string `json:\"command\" validate:\"required\"`\n}\n\ntype CommandInfo struct {\n\tID          uint   `json:\"id\"`\n\tGroupID     uint   `json:\"groupID\"`\n\tName        string `json:\"name\"`\n\tType        string `json:\"type\"`\n\tCommand     string `json:\"command\"`\n\tGroupBelong string `json:\"groupBelong\"`\n}\n\ntype CommandTree struct {\n\tLabel    string        `json:\"label\"`\n\tValue    string        `json:\"value\"`\n\tChildren []CommandTree `json:\"children\"`\n}\n\ntype CommandDelete struct {\n\tType string `json:\"type\" validate:\"required,oneof=redis command\"`\n\tIDs  []uint `json:\"ids\"`\n}\n"
  },
  {
    "path": "core/app/dto/common.go",
    "content": "package dto\n\ntype SearchWithPage struct {\n\tPageInfo\n\tInfo string `json:\"info\"`\n}\n\ntype SearchPageWithType struct {\n\tPageInfo\n\tType string `json:\"type\"`\n\tInfo string `json:\"info\"`\n}\n\ntype SearchPageWithGroup struct {\n\tPageInfo\n\tGroupID uint   `json:\"groupID\"`\n\tInfo    string `json:\"info\"`\n}\n\ntype PageInfo struct {\n\tPage     int `json:\"page\" validate:\"required,number\"`\n\tPageSize int `json:\"pageSize\" validate:\"required,number\"`\n}\n\ntype PageResult struct {\n\tTotal int64       `json:\"total\"`\n\tItems interface{} `json:\"items\"`\n}\n\ntype Response struct {\n\tCode    int         `json:\"code\"`\n\tMessage string      `json:\"message\"`\n\tData    interface{} `json:\"data\"`\n}\n\ntype Options struct {\n\tOption string `json:\"option\"`\n}\n\ntype OperateByType struct {\n\tType string `json:\"type\"`\n}\n\ntype OperateByName struct {\n\tName string `json:\"name\"`\n}\n\ntype OperateByID struct {\n\tID uint `json:\"id\"`\n}\ntype OperateByIDs struct {\n\tIDs []uint `json:\"ids\"`\n}\ntype OperateByTaskID struct {\n\tTaskID string `json:\"taskID\"`\n}\n"
  },
  {
    "path": "core/app/dto/group.go",
    "content": "package dto\n\ntype GroupCreate struct {\n\tID   uint   `json:\"id\"`\n\tName string `json:\"name\" validate:\"required\"`\n\tType string `json:\"type\" validate:\"required\"`\n}\n\ntype GroupSearch struct {\n\tType string `json:\"type\" validate:\"required\"`\n}\n\ntype GroupUpdate struct {\n\tID        uint   `json:\"id\"`\n\tName      string `json:\"name\"`\n\tType      string `json:\"type\" validate:\"required\"`\n\tIsDefault bool   `json:\"isDefault\"`\n}\n\ntype GroupInfo struct {\n\tID        uint   `json:\"id\"`\n\tName      string `json:\"name\"`\n\tType      string `json:\"type\"`\n\tIsDefault bool   `json:\"isDefault\"`\n}\n"
  },
  {
    "path": "core/app/dto/logs.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n)\n\ntype OperationLog struct {\n\tID        uint   `json:\"id\"`\n\tSource    string `json:\"source\"`\n\tNode      string `json:\"node\"`\n\tIP        string `json:\"ip\"`\n\tPath      string `json:\"path\"`\n\tMethod    string `json:\"method\"`\n\tUserAgent string `json:\"userAgent\"`\n\n\tLatency time.Duration `json:\"latency\"`\n\tStatus  string        `json:\"status\"`\n\tMessage string        `json:\"message\"`\n\n\tDetailZH  string    `json:\"detailZH\"`\n\tDetailEN  string    `json:\"detailEN\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n}\n\ntype SearchOpLogWithPage struct {\n\tPageInfo\n\tSource    string `json:\"source\"`\n\tStatus    string `json:\"status\"`\n\tNode      string `json:\"node\"`\n\tOperation string `json:\"operation\"`\n}\n\ntype SearchLgLogWithPage struct {\n\tPageInfo\n\tIP     string `json:\"ip\"`\n\tStatus string `json:\"status\"`\n}\n\ntype LoginLog struct {\n\tID        uint      `json:\"id\"`\n\tIP        string    `json:\"ip\"`\n\tAddress   string    `json:\"address\"`\n\tAgent     string    `json:\"agent\"`\n\tStatus    string    `json:\"status\"`\n\tMessage   string    `json:\"message\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n}\n\ntype CleanLog struct {\n\tLogType string `json:\"logType\" validate:\"required,oneof=login operation\"`\n}\n"
  },
  {
    "path": "core/app/dto/script_library.go",
    "content": "package dto\n\nimport \"time\"\n\ntype ScriptInfo struct {\n\tID            uint      `json:\"id\"`\n\tName          string    `json:\"name\"`\n\tIsInteractive bool      `json:\"isInteractive\"`\n\tLable         string    `json:\"lable\"`\n\tScript        string    `json:\"script\"`\n\tGroupList     []uint    `json:\"groupList\"`\n\tGroupBelong   []string  `json:\"groupBelong\"`\n\tIsSystem      bool      `json:\"isSystem\"`\n\tDescription   string    `json:\"description\"`\n\tCreatedAt     time.Time `json:\"createdAt\"`\n}\n\ntype ScriptOperate struct {\n\tID            uint   `json:\"id\"`\n\tIsInteractive bool   `json:\"isInteractive\"`\n\tName          string `json:\"name\"`\n\tScript        string `json:\"script\"`\n\tGroups        string `json:\"groups\"`\n\tDescription   string `json:\"description\"`\n}\n"
  },
  {
    "path": "core/app/dto/setting.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n)\n\ntype SettingInfo struct {\n\tUserName            string `json:\"userName\"`\n\tSystemVersion       string `json:\"systemVersion\"`\n\tDeveloperMode       string `json:\"developerMode\"`\n\tUpgradeBackupCopies string `json:\"upgradeBackupCopies\"`\n\n\tSessionTimeout string `json:\"sessionTimeout\"`\n\tPort           string `json:\"port\"`\n\tIpv6           string `json:\"ipv6\"`\n\tBindAddress    string `json:\"bindAddress\"`\n\tPanelName      string `json:\"panelName\"`\n\tEdition        string `json:\"edition\"`\n\tTheme          string `json:\"theme\"`\n\tMenuTabs       string `json:\"menuTabs\"`\n\tLanguage       string `json:\"language\"`\n\tDocSource      string `json:\"docSource\"`\n\n\tServerPort                 string `json:\"serverPort\"`\n\tSSL                        string `json:\"ssl\"`\n\tSSLType                    string `json:\"sslType\"`\n\tBindDomain                 string `json:\"bindDomain\"`\n\tPasskeyTrustedProxies      string `json:\"passkeyTrustedProxies\"`\n\tAllowIPs                   string `json:\"allowIPs\"`\n\tSecurityEntrance           string `json:\"securityEntrance\"`\n\tDashboardMemoVisible       string `json:\"dashboardMemoVisible\"`\n\tDashboardSimpleNodeVisible string `json:\"dashboardSimpleNodeVisible\"`\n\tExpirationDays             string `json:\"expirationDays\"`\n\tExpirationTime             string `json:\"expirationTime\"`\n\tComplexityVerification     string `json:\"complexityVerification\"`\n\tMFAStatus                  string `json:\"mfaStatus\"`\n\tMFAInterval                string `json:\"mfaInterval\"`\n\n\tAppStoreVersion      string `json:\"appStoreVersion\"`\n\tAppStoreLastModified string `json:\"appStoreLastModified\"`\n\tAppStoreSyncStatus   string `json:\"appStoreSyncStatus\"`\n\n\tHideMenu      string `json:\"hideMenu\"`\n\tNoAuthSetting string `json:\"noAuthSetting\"`\n\n\tProxyUrl        string `json:\"proxyUrl\"`\n\tProxyType       string `json:\"proxyType\"`\n\tProxyPort       string `json:\"proxyPort\"`\n\tProxyUser       string `json:\"proxyUser\"`\n\tProxyPasswd     string `json:\"proxyPasswd\"`\n\tProxyPasswdKeep string `json:\"proxyPasswdKeep\"`\n\n\tApiInterfaceStatus string `json:\"apiInterfaceStatus\"`\n\tApiKey             string `json:\"apiKey\"`\n\tIpWhiteList        string `json:\"ipWhiteList\"`\n\tApiKeyValidityTime string `json:\"apiKeyValidityTime\"`\n}\n\ntype SettingKey struct {\n\tKey string `json:\"key\" validate:\"required,oneof=ScriptSync\"`\n}\n\ntype SettingUpdate struct {\n\tKey   string `json:\"key\" validate:\"required\"`\n\tValue string `json:\"value\"`\n}\n\ntype SSLUpdate struct {\n\tSSLType string `json:\"sslType\" validate:\"required,oneof=self select import import-paste import-local\"`\n\tDomain  string `json:\"domain\"`\n\tSSL     string `json:\"ssl\" validate:\"required,oneof=Enable Disable Mux\"`\n\tCert    string `json:\"cert\"`\n\tKey     string `json:\"key\"`\n\tSSLID   uint   `json:\"sslID\"`\n}\ntype SSLInfo struct {\n\tDomain   string `json:\"domain\"`\n\tTimeout  string `json:\"timeout\"`\n\tRootPath string `json:\"rootPath\"`\n\tCert     string `json:\"cert\"`\n\tKey      string `json:\"key\"`\n\tSSLID    uint   `json:\"sslID\"`\n}\n\ntype PasswordUpdate struct {\n\tOldPassword string `json:\"oldPassword\" validate:\"required\"`\n\tNewPassword string `json:\"newPassword\" validate:\"required\"`\n}\n\ntype PortUpdate struct {\n\tServerPort uint `json:\"serverPort\" validate:\"required,number,max=65535,min=1\"`\n}\n\ntype SnapshotCreate struct {\n\tID              uint   `json:\"id\"`\n\tFrom            string `json:\"from\" validate:\"required\"`\n\tDefaultDownload string `json:\"defaultDownload\" validate:\"required\"`\n\tDescription     string `json:\"description\" validate:\"max=256\"`\n\tSecret          string `json:\"secret\"`\n}\ntype SnapshotRecover struct {\n\tIsNew      bool   `json:\"isNew\"`\n\tReDownload bool   `json:\"reDownload\"`\n\tID         uint   `json:\"id\" validate:\"required\"`\n\tSecret     string `json:\"secret\"`\n}\ntype SnapshotBatchDelete struct {\n\tDeleteWithFile bool   `json:\"deleteWithFile\"`\n\tIds            []uint `json:\"ids\" validate:\"required\"`\n}\ntype SnapshotImport struct {\n\tFrom        string   `json:\"from\"`\n\tNames       []string `json:\"names\"`\n\tDescription string   `json:\"description\" validate:\"max=256\"`\n}\ntype SnapshotInfo struct {\n\tID              uint      `json:\"id\"`\n\tName            string    `json:\"name\"`\n\tDescription     string    `json:\"description\" validate:\"max=256\"`\n\tFrom            string    `json:\"from\"`\n\tDefaultDownload string    `json:\"defaultDownload\"`\n\tStatus          string    `json:\"status\"`\n\tMessage         string    `json:\"message\"`\n\tCreatedAt       time.Time `json:\"createdAt\"`\n\tVersion         string    `json:\"version\"`\n\tSize            int64     `json:\"size\"`\n\n\tInterruptStep    string `json:\"interruptStep\"`\n\tRecoverStatus    string `json:\"recoverStatus\"`\n\tRecoverMessage   string `json:\"recoverMessage\"`\n\tLastRecoveredAt  string `json:\"lastRecoveredAt\"`\n\tRollbackStatus   string `json:\"rollbackStatus\"`\n\tRollbackMessage  string `json:\"rollbackMessage\"`\n\tLastRollbackedAt string `json:\"lastRollbackedAt\"`\n}\n\ntype UpgradeInfo struct {\n\tTestVersion   string `json:\"testVersion\"`\n\tNewVersion    string `json:\"newVersion\"`\n\tLatestVersion string `json:\"latestVersion\"`\n\tReleaseNote   string `json:\"releaseNote\"`\n}\n\ntype SyncTime struct {\n\tNtpSite string `json:\"ntpSite\" validate:\"required\"`\n}\n\ntype BindInfo struct {\n\tIpv6        string `json:\"ipv6\" validate:\"required,oneof=Enable Disable\"`\n\tBindAddress string `json:\"bindAddress\" validate:\"required\"`\n}\n\ntype Upgrade struct {\n\tVersion string `json:\"version\" validate:\"required\"`\n}\n\ntype ReleasesNotes struct {\n\tVersion           string `json:\"version\"`\n\tCreatedAt         string `json:\"createdAt\"`\n\tContent           string `json:\"content\"`\n\tNewCount          int    `json:\"newCount\"`\n\tOptimizationCount int    `json:\"optimizationCount\"`\n\tFixCount          int    `json:\"fixCount\"`\n}\n\ntype ProxyUpdate struct {\n\tProxyUrl          string `json:\"proxyUrl\"`\n\tProxyType         string `json:\"proxyType\"`\n\tProxyPort         string `json:\"proxyPort\"`\n\tProxyUser         string `json:\"proxyUser\"`\n\tProxyPasswd       string `json:\"proxyPasswd\"`\n\tProxyPasswdKeep   string `json:\"proxyPasswdKeep\"`\n\tProxyDocker       bool   `json:\"proxyDocker\"`\n\tWithDockerRestart bool   `json:\"withDockerRestart\"`\n}\n\ntype CleanData struct {\n\tSystemClean    []CleanTree `json:\"systemClean\"`\n\tUploadClean    []CleanTree `json:\"uploadClean\"`\n\tDownloadClean  []CleanTree `json:\"downloadClean\"`\n\tSystemLogClean []CleanTree `json:\"systemLogClean\"`\n\tContainerClean []CleanTree `json:\"containerClean\"`\n}\n\ntype CleanTree struct {\n\tID       string      `json:\"id\"`\n\tLabel    string      `json:\"label\"`\n\tChildren []CleanTree `json:\"children\"`\n\n\tType string `json:\"type\"`\n\tName string `json:\"name\"`\n\n\tSize        uint64 `json:\"size\"`\n\tIsCheck     bool   `json:\"isCheck\"`\n\tIsRecommend bool   `json:\"isRecommend\"`\n}\n\ntype Clean struct {\n\tTreeType string `json:\"treeType\"`\n\tName     string `json:\"name\"`\n\tSize     uint64 `json:\"size\"`\n}\n\ntype ShowMenu struct {\n\tID       string     `json:\"id\"`\n\tLabel    string     `json:\"label\"`\n\tDisabled bool       `json:\"disabled\"`\n\tIsShow   bool       `json:\"isShow\"`\n\tTitle    string     `json:\"title\"`\n\tPath     string     `json:\"path,omitempty\"`\n\tSort     int        `json:\"sort\"`\n\tChildren []ShowMenu `json:\"children,omitempty\"`\n}\n\ntype MenuLabelSort struct {\n\tLabel string `json:\"label\"`\n\tSort  int    `json:\"sort\"`\n}\n\ntype ApiInterfaceConfig struct {\n\tApiInterfaceStatus string `json:\"apiInterfaceStatus\"`\n\tApiKey             string `json:\"apiKey\"`\n\tIpWhiteList        string `json:\"ipWhiteList\"`\n\tApiKeyValidityTime string `json:\"apiKeyValidityTime\"`\n}\n\ntype TerminalInfo struct {\n\tLineHeight        string `json:\"lineHeight\"`\n\tLetterSpacing     string `json:\"letterSpacing\"`\n\tFontSize          string `json:\"fontSize\"`\n\tFontFamily        string `json:\"fontFamily\"`\n\tBackgroundColor   string `json:\"backgroundColor\"`\n\tForegroundColor   string `json:\"foregroundColor\"`\n\tCursorBlink       string `json:\"cursorBlink\"`\n\tCursorStyle       string `json:\"cursorStyle\"`\n\tScrollback        string `json:\"scrollback\"`\n\tScrollSensitivity string `json:\"scrollSensitivity\"`\n}\n\ntype AppstoreUpdate struct {\n\tScope  string `json:\"scope\" validate:\"required,oneof=UninstallDeleteImage UpgradeBackup UninstallDeleteBackup\"`\n\tStatus string `json:\"status\"  validate:\"required,oneof=Disable Enable\"`\n}\ntype AppstoreConfig struct {\n\tUninstallDeleteImage  string `json:\"uninstallDeleteImage\"`\n\tUpgradeBackup         string `json:\"upgradeBackup\"`\n\tUninstallDeleteBackup string `json:\"uninstallDeleteBackup\"`\n}\n\ntype LoginSetting struct {\n\tIsDemo         bool   `json:\"isDemo\"`\n\tIsIntl         bool   `json:\"isIntl\"`\n\tIsOffLine      bool   `json:\"isOffLine\"`\n\tIsFxplay       bool   `json:\"isFxplay\"`\n\tLanguage       string `json:\"language\"`\n\tMenuTabs       string `json:\"menuTabs\"`\n\tPanelName      string `json:\"panelName\"`\n\tTheme          string `json:\"theme\"`\n\tNeedCaptcha    bool   `json:\"needCaptcha\"`\n\tPasskeySetting bool   `json:\"passkeySetting\"`\n}\n\ntype PasskeyRegisterRequest struct {\n\tName string `json:\"name\" validate:\"required\"`\n}\n\ntype PasskeyInfo struct {\n\tID         string `json:\"id\"`\n\tName       string `json:\"name\"`\n\tCreatedAt  string `json:\"createdAt\"`\n\tLastUsedAt string `json:\"lastUsedAt\"`\n}\n\ntype MemoUpdate struct {\n\tContent string `json:\"content\" validate:\"max=500\"`\n}\n"
  },
  {
    "path": "core/app/model/agent.go",
    "content": "package model\n\nimport (\n\t\"time\"\n)\n\ntype WebsiteSSL struct {\n\tBaseModel\n\tPrimaryDomain string    `json:\"primaryDomain\"`\n\tPrivateKey    string    `json:\"privateKey\"`\n\tPem           string    `json:\"pem\"`\n\tDomains       string    `json:\"domains\"`\n\tCertURL       string    `json:\"certURL\"`\n\tType          string    `json:\"type\"`\n\tProvider      string    `json:\"provider\"`\n\tOrganization  string    `json:\"organization\"`\n\tDnsAccountID  uint      `json:\"dnsAccountId\"`\n\tAcmeAccountID uint      `gorm:\"column:acme_account_id\" json:\"acmeAccountId\" `\n\tCaID          uint      `json:\"caId\"`\n\tAutoRenew     bool      `json:\"autoRenew\"`\n\tExpireDate    time.Time `json:\"expireDate\"`\n\tStartDate     time.Time `json:\"startDate\"`\n\tStatus        string    `json:\"status\"`\n\tMessage       string    `json:\"message\"`\n\tKeyType       string    `json:\"keyType\"`\n\tPushDir       bool      `json:\"pushDir\"`\n\tDir           string    `json:\"dir\"`\n\tDescription   string    `json:\"description\"`\n\tSkipDNS       bool      `json:\"skipDNS\"`\n\tNameserver1   string    `json:\"nameserver1\"`\n\tNameserver2   string    `json:\"nameserver2\"`\n\tDisableCNAME  bool      `json:\"disableCNAME\"`\n\tExecShell     bool      `json:\"execShell\"`\n\tShell         string    `json:\"shell\"`\n}\n\nfunc (w WebsiteSSL) TableName() string {\n\treturn \"website_ssls\"\n}\n\ntype WebsiteCA struct {\n\tBaseModel\n\tCSR        string `gorm:\"not null;\" json:\"csr\"`\n\tName       string `gorm:\"not null;\" json:\"name\"`\n\tPrivateKey string `gorm:\"not null\" json:\"privateKey\"`\n\tKeyType    string `gorm:\"not null;default:2048\" json:\"keyType\"`\n}\n"
  },
  {
    "path": "core/app/model/alert.go",
    "content": "package model\n\ntype AlertConfig struct {\n\tBaseModel\n\tType   string `json:\"type\"`\n\tTitle  string `json:\"title\"`\n\tStatus string `json:\"status\"`\n\tConfig string `json:\"config\"`\n}\n"
  },
  {
    "path": "core/app/model/backup.go",
    "content": "package model\n\ntype BackupAccount struct {\n\tBaseModel\n\tName       string `gorm:\"not null;default:''\" json:\"name\"`\n\tType       string `gorm:\"not null;default:''\" json:\"type\"`\n\tIsPublic   bool   `json:\"isPublic\"`\n\tBucket     string `json:\"bucket\"`\n\tAccessKey  string `json:\"accessKey\"`\n\tCredential string `json:\"credential\"`\n\tBackupPath string `json:\"backupPath\"`\n\tVars       string `json:\"vars\"`\n\n\tRememberAuth bool `json:\"rememberAuth\"`\n}\n"
  },
  {
    "path": "core/app/model/base.go",
    "content": "package model\n\nimport \"time\"\n\ntype BaseModel struct {\n\tID        uint      `gorm:\"primarykey;AUTO_INCREMENT\" json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n}\n"
  },
  {
    "path": "core/app/model/command.go",
    "content": "package model\n\ntype Command struct {\n\tBaseModel\n\tType    string `gorm:\"not null\" json:\"type\"`\n\tName    string `gorm:\"not null\" json:\"name\"`\n\tGroupID uint   `gorm:\"not null\" json:\"groupID\"`\n\tCommand string `gorm:\"not null\" json:\"command\"`\n}\n"
  },
  {
    "path": "core/app/model/group.go",
    "content": "package model\n\ntype Group struct {\n\tBaseModel\n\tIsDefault bool   `json:\"isDefault\"`\n\tName      string `json:\"name\"`\n\tType      string `json:\"type\"`\n}\n"
  },
  {
    "path": "core/app/model/logs.go",
    "content": "package model\n\nimport (\n\t\"time\"\n)\n\ntype OperationLog struct {\n\tBaseModel\n\tSource    string `json:\"source\"`\n\tIP        string `json:\"ip\"`\n\tNode      string `json:\"node\"`\n\tPath      string `json:\"path\"`\n\tMethod    string `json:\"method\"`\n\tUserAgent string `json:\"userAgent\"`\n\n\tLatency time.Duration `json:\"latency\"`\n\tStatus  string        `json:\"status\"`\n\tMessage string        `json:\"message\"`\n\n\tDetailZH string `json:\"detailZH\"`\n\tDetailEN string `json:\"detailEN\"`\n}\n\ntype LoginLog struct {\n\tBaseModel\n\tIP      string `json:\"ip\"`\n\tAddress string `json:\"address\"`\n\tAgent   string `json:\"agent\"`\n\tStatus  string `json:\"status\"`\n\tMessage string `json:\"message\"`\n}\n"
  },
  {
    "path": "core/app/model/script_library.go",
    "content": "package model\n\ntype ScriptLibrary struct {\n\tBaseModel\n\tName          string `json:\"name\" gorm:\"not null;\"`\n\tIsInteractive bool   `json:\"isInteractive\"`\n\tScript        string `json:\"script\" gorm:\"not null;\"`\n\tGroups        string `json:\"groups\"`\n\tIsSystem      bool   `json:\"isSystem\"`\n\tDescription   string `json:\"description\"`\n}\n"
  },
  {
    "path": "core/app/model/setting.go",
    "content": "package model\n\ntype Setting struct {\n\tBaseModel\n\tKey   string `json:\"key\" gorm:\"not null;\"`\n\tValue string `json:\"value\"`\n\tAbout string `json:\"about\"`\n}\n"
  },
  {
    "path": "core/app/model/task.go",
    "content": "package model\n\nimport \"time\"\n\ntype Task struct {\n\tID             string    `gorm:\"primarykey;\" json:\"id\"`\n\tName           string    `json:\"name\"`\n\tType           string    `json:\"type\"`\n\tOperate        string    `json:\"operate\"`\n\tLogFile        string    `json:\"logFile\"`\n\tStatus         string    `json:\"status\"`\n\tErrorMsg       string    `json:\"errorMsg\"`\n\tOperationLogID uint      `json:\"operationLogID\"`\n\tResourceID     uint      `json:\"resourceID\"`\n\tCurrentStep    string    `json:\"currentStep\"`\n\tEndAt          time.Time `json:\"endAt\"`\n\tCreatedAt      time.Time `json:\"createdAt\"`\n}\n"
  },
  {
    "path": "core/app/model/upgrade_log.go",
    "content": "package model\n\ntype UpgradeLog struct {\n\tBaseModel\n\tNodeID     uint   `json:\"nodeID\"`\n\tOldVersion string `json:\"oldVersion\"`\n\tNewVersion string `json:\"newVersion\"`\n\tBackupFile string `json:\"backupFile\"`\n}\n"
  },
  {
    "path": "core/app/repo/agent.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n)\n\ntype AgentRepo struct{}\n\ntype IAgentRepo interface {\n\tGetWebsiteSSL(opts ...global.DBOption) (model.WebsiteSSL, error)\n\tGetCA(opts ...global.DBOption) (model.WebsiteCA, error)\n}\n\nfunc NewIAgentRepo() IAgentRepo {\n\treturn &AgentRepo{}\n}\n\nfunc (a *AgentRepo) GetWebsiteSSL(opts ...global.DBOption) (model.WebsiteSSL, error) {\n\tvar ssl model.WebsiteSSL\n\tdb := global.AgentDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&ssl).Error\n\treturn ssl, err\n}\n\nfunc (a *AgentRepo) GetCA(opts ...global.DBOption) (model.WebsiteCA, error) {\n\tvar ca model.WebsiteCA\n\tdb := global.AgentDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&ca).Error\n\treturn ca, err\n}\n"
  },
  {
    "path": "core/app/repo/backup.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n)\n\ntype BackupRepo struct{}\n\ntype IBackupRepo interface {\n\tGet(opts ...global.DBOption) (model.BackupAccount, error)\n\tList(opts ...global.DBOption) ([]model.BackupAccount, error)\n\tPage(limit, offset int, opts ...global.DBOption) (int64, []model.BackupAccount, error)\n\tCreate(backup *model.BackupAccount) error\n\tSave(backup *model.BackupAccount) error\n\tDelete(opts ...global.DBOption) error\n}\n\nfunc NewIBackupRepo() IBackupRepo {\n\treturn &BackupRepo{}\n}\n\nfunc (u *BackupRepo) Get(opts ...global.DBOption) (model.BackupAccount, error) {\n\tvar backup model.BackupAccount\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&backup).Error\n\treturn backup, err\n}\n\nfunc (u *BackupRepo) Page(page, size int, opts ...global.DBOption) (int64, []model.BackupAccount, error) {\n\tvar ops []model.BackupAccount\n\tdb := global.DB.Model(&model.BackupAccount{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error\n\treturn count, ops, err\n}\n\nfunc (u *BackupRepo) List(opts ...global.DBOption) ([]model.BackupAccount, error) {\n\tvar ops []model.BackupAccount\n\tdb := global.DB.Model(&model.BackupAccount{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&ops).Error\n\treturn ops, err\n}\n\nfunc (u *BackupRepo) Create(backup *model.BackupAccount) error {\n\treturn global.DB.Create(backup).Error\n}\n\nfunc (u *BackupRepo) Save(backup *model.BackupAccount) error {\n\treturn global.DB.Save(backup).Error\n}\n\nfunc (u *BackupRepo) Delete(opts ...global.DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.BackupAccount{}).Error\n}\n"
  },
  {
    "path": "core/app/repo/command.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype CommandRepo struct{}\n\ntype ICommandRepo interface {\n\tList(opts ...global.DBOption) ([]model.Command, error)\n\tPage(limit, offset int, opts ...global.DBOption) (int64, []model.Command, error)\n\tCreate(command *model.Command) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tUpdateGroup(group, newGroup uint) error\n\tDelete(opts ...global.DBOption) error\n\tGet(opts ...global.DBOption) (model.Command, error)\n\n\tWithByInfo(info string) global.DBOption\n}\n\nfunc NewICommandRepo() ICommandRepo {\n\treturn &CommandRepo{}\n}\n\nfunc (u *CommandRepo) Get(opts ...global.DBOption) (model.Command, error) {\n\tvar command model.Command\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&command).Error\n\treturn command, err\n}\n\nfunc (u *CommandRepo) Page(page, size int, opts ...global.DBOption) (int64, []model.Command, error) {\n\tvar users []model.Command\n\tdb := global.DB.Model(&model.Command{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error\n\treturn count, users, err\n}\n\nfunc (u *CommandRepo) List(opts ...global.DBOption) ([]model.Command, error) {\n\tvar commands []model.Command\n\tdb := global.DB.Model(&model.Command{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&commands).Error\n\treturn commands, err\n}\n\nfunc (u *CommandRepo) Create(command *model.Command) error {\n\treturn global.DB.Create(command).Error\n}\n\nfunc (u *CommandRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Command{}).Where(\"id = ?\", id).Updates(vars).Error\n}\nfunc (h *CommandRepo) UpdateGroup(group, newGroup uint) error {\n\treturn global.DB.Model(&model.Command{}).Where(\"group_id = ?\", group).Updates(map[string]interface{}{\"group_id\": newGroup}).Error\n}\n\nfunc (u *CommandRepo) Delete(opts ...global.DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Command{}).Error\n}\n\nfunc (c *CommandRepo) WithByInfo(info string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(info) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"name like ? or command like ?\", \"%\"+info+\"%\", \"%\"+info+\"%\")\n\t}\n}\n"
  },
  {
    "path": "core/app/repo/common.go",
    "content": "package repo\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/re\"\n\t\"gorm.io/gorm\"\n)\n\nfunc WithByID(id uint) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id = ?\", id)\n\t}\n}\nfunc WithByGroupID(id uint) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"group_id = ?\", id)\n\t}\n}\n\nfunc WithByIDs(ids []uint) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id in (?)\", ids)\n\t}\n}\nfunc WithByName(name string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"`name` = ?\", name)\n\t}\n}\nfunc WithoutByName(name string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"`name` != ?\", name)\n\t}\n}\n\nfunc WithByType(ty string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"`type` = ?\", ty)\n\t}\n}\nfunc WithByAddr(addr string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"addr = ?\", addr)\n\t}\n}\nfunc WithByKey(key string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"key = ?\", key)\n\t}\n}\nfunc WithByStatus(status string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"status = ?\", status)\n\t}\n}\nfunc WithByNode(node string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"node = ?\", node)\n\t}\n}\n\nfunc WithOrderDesc(orderBy string) global.DBOption {\n\treturn WithOrderRuleBy(orderBy, constant.OrderDesc)\n}\n\nfunc WithOrderAsc(orderBy string) global.DBOption {\n\treturn WithOrderRuleBy(orderBy, constant.OrderAsc)\n}\n\nfunc WithOrderRuleBy(orderBy, order string) global.DBOption {\n\tif orderBy == \"createdAt\" {\n\t\torderBy = \"created_at\"\n\t}\n\tif !re.GetRegex(re.OrderByValidationPattern).MatchString(orderBy) {\n\t\torderBy = \"created_at\"\n\t}\n\tswitch order {\n\tcase constant.OrderDesc:\n\t\torder = \"desc\"\n\tcase constant.OrderAsc:\n\t\torder = \"asc\"\n\tdefault:\n\t\torderBy = \"created_at\"\n\t\torder = \"desc\"\n\t}\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Order(fmt.Sprintf(\"%s %s\", orderBy, order))\n\t}\n}\n"
  },
  {
    "path": "core/app/repo/group.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype GroupRepo struct{}\n\ntype IGroupRepo interface {\n\tGet(opts ...global.DBOption) (model.Group, error)\n\tGetList(opts ...global.DBOption) ([]model.Group, error)\n\tCreate(group *model.Group) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tDelete(opts ...global.DBOption) error\n\n\tWithByDefault(isDefault bool) global.DBOption\n\tCancelDefault(groupType string) error\n}\n\nfunc NewIGroupRepo() IGroupRepo {\n\treturn &GroupRepo{}\n}\n\nfunc (c *GroupRepo) WithByDefault(isDefault bool) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"is_default = ?\", isDefault)\n\t}\n}\n\nfunc (u *GroupRepo) Get(opts ...global.DBOption) (model.Group, error) {\n\tvar group model.Group\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&group).Error\n\treturn group, err\n}\n\nfunc (u *GroupRepo) GetList(opts ...global.DBOption) ([]model.Group, error) {\n\tvar groups []model.Group\n\tdb := global.DB.Model(&model.Group{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&groups).Error\n\treturn groups, err\n}\n\nfunc (u *GroupRepo) Create(group *model.Group) error {\n\treturn global.DB.Create(group).Error\n}\n\nfunc (u *GroupRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.Group{}).Where(\"id = ?\", id).Updates(vars).Error\n}\n\nfunc (u *GroupRepo) Delete(opts ...global.DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.Group{}).Error\n}\n\nfunc (u *GroupRepo) CancelDefault(groupType string) error {\n\treturn global.DB.Model(&model.Group{}).\n\t\tWhere(\"is_default = ? AND type = ?\", 1, groupType).\n\t\tUpdates(map[string]interface{}{\"is_default\": 0}).Error\n}\n"
  },
  {
    "path": "core/app/repo/logs.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype LogRepo struct{}\n\ntype ILogRepo interface {\n\tCleanLogin() error\n\tCreateLoginLog(user *model.LoginLog) error\n\tPageLoginLog(limit, offset int, opts ...global.DBOption) (int64, []model.LoginLog, error)\n\n\tCleanOperation() error\n\tCreateOperationLog(user *model.OperationLog) error\n\tPageOperationLog(limit, offset int, opts ...global.DBOption) (int64, []model.OperationLog, error)\n\n\tWithByIP(ip string) global.DBOption\n\tWithBySource(source string) global.DBOption\n\tWithByLikeOperation(operation string) global.DBOption\n}\n\nfunc NewILogRepo() ILogRepo {\n\treturn &LogRepo{}\n}\n\nfunc (u *LogRepo) CleanLogin() error {\n\treturn global.DB.Exec(\"delete from login_logs;\").Error\n}\n\nfunc (u *LogRepo) CreateLoginLog(log *model.LoginLog) error {\n\treturn global.DB.Create(log).Error\n}\n\nfunc (u *LogRepo) PageLoginLog(page, size int, opts ...global.DBOption) (int64, []model.LoginLog, error) {\n\tvar ops []model.LoginLog\n\tdb := global.DB.Model(&model.LoginLog{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error\n\treturn count, ops, err\n}\n\nfunc (u *LogRepo) CleanOperation() error {\n\treturn global.DB.Exec(\"delete from operation_logs\").Error\n}\n\nfunc (u *LogRepo) CreateOperationLog(log *model.OperationLog) error {\n\treturn global.DB.Create(log).Error\n}\n\nfunc (u *LogRepo) PageOperationLog(page, size int, opts ...global.DBOption) (int64, []model.OperationLog, error) {\n\tvar ops []model.OperationLog\n\tdb := global.DB.Model(&model.OperationLog{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error\n\treturn count, ops, err\n}\n\nfunc (c *LogRepo) WithByStatus(status string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(status) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"status = ?\", status)\n\t}\n}\nfunc (c *LogRepo) WithBySource(source string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(source) == 0 {\n\t\t\treturn g\n\t\t}\n\t\treturn g.Where(\"source = ?\", source)\n\t}\n}\nfunc (c *LogRepo) WithByIP(ip string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"ip LIKE ?\", \"%\"+ip+\"%\")\n\t}\n}\n\nfunc (c *LogRepo) WithByLikeOperation(operation string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\tif len(operation) == 0 {\n\t\t\treturn g\n\t\t}\n\t\tinfoStr := \"%\" + operation + \"%\"\n\t\treturn g.Where(\"detail_zh LIKE ? OR detail_en LIKE ?\", infoStr, infoStr)\n\t}\n}\n"
  },
  {
    "path": "core/app/repo/script_library.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype IScriptRepo interface {\n\tGet(opts ...global.DBOption) (model.ScriptLibrary, error)\n\tGetList(opts ...global.DBOption) ([]model.ScriptLibrary, error)\n\tCreate(script *model.ScriptLibrary) error\n\tUpdate(id uint, vars map[string]interface{}) error\n\tUpdateGroup(group, newGroup uint) error\n\tPage(limit, offset int, opts ...global.DBOption) (int64, []model.ScriptLibrary, error)\n\tDelete(opts ...global.DBOption) error\n\tSyncAll(scripts []model.ScriptLibrary) error\n\n\tWithByInfo(info string) global.DBOption\n}\n\nfunc NewIScriptRepo() IScriptRepo {\n\treturn &ScriptRepo{}\n}\n\ntype ScriptRepo struct{}\n\nfunc (u *ScriptRepo) Get(opts ...global.DBOption) (model.ScriptLibrary, error) {\n\tvar ScriptLibrary model.ScriptLibrary\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&ScriptLibrary).Error\n\treturn ScriptLibrary, err\n}\n\nfunc (u *ScriptRepo) GetList(opts ...global.DBOption) ([]model.ScriptLibrary, error) {\n\tvar scripts []model.ScriptLibrary\n\tdb := global.DB.Model(&model.ScriptLibrary{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&scripts).Error\n\treturn scripts, err\n}\n\nfunc (u *ScriptRepo) Page(page, size int, opts ...global.DBOption) (int64, []model.ScriptLibrary, error) {\n\tvar users []model.ScriptLibrary\n\tdb := global.DB.Model(&model.ScriptLibrary{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error\n\treturn count, users, err\n}\n\nfunc (u *ScriptRepo) Create(ScriptLibrary *model.ScriptLibrary) error {\n\treturn global.DB.Create(ScriptLibrary).Error\n}\n\nfunc (u *ScriptRepo) Update(id uint, vars map[string]interface{}) error {\n\treturn global.DB.Model(&model.ScriptLibrary{}).Where(\"id = ?\", id).Updates(vars).Error\n}\nfunc (u *ScriptRepo) UpdateGroup(group, newGroup uint) error {\n\treturn global.DB.Model(&model.ScriptLibrary{}).Where(\"group_id = ?\", group).Updates(map[string]interface{}{\"group_id\": newGroup}).Error\n}\n\nfunc (u *ScriptRepo) Delete(opts ...global.DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.ScriptLibrary{}).Error\n}\n\nfunc (u *ScriptRepo) SyncAll(scripts []model.ScriptLibrary) error {\n\ttx := global.DB.Begin()\n\tif err := tx.Where(\"is_system = ?\", 1).Delete(&model.ScriptLibrary{}).Error; err != nil {\n\t\ttx.Rollback()\n\t\treturn err\n\t}\n\tif err := tx.Save(&scripts).Error; err != nil {\n\t\ttx.Rollback()\n\t\treturn err\n\t}\n\ttx.Commit()\n\treturn nil\n}\n\nfunc (u *ScriptRepo) WithByInfo(info string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"name LIKE ? OR description LIKE ?\", \"%\"+info+\"%\", \"%\"+info+\"%\")\n\t}\n}\n"
  },
  {
    "path": "core/app/repo/setting.go",
    "content": "package repo\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/init/migration/helper\"\n\t\"github.com/patrickmn/go-cache\"\n\t\"gorm.io/gorm\"\n)\n\ntype SettingRepo struct{}\n\nvar (\n\tsettingCache = cache.New(5*time.Minute, 10*time.Minute)\n\tsettingTTL   = 5 * time.Minute\n)\n\ntype ISettingRepo interface {\n\tList(opts ...global.DBOption) ([]model.Setting, error)\n\tGet(opts ...global.DBOption) (model.Setting, error)\n\tGetValueByKey(key string) (string, error)\n\tCreate(key, value string) error\n\tUpdate(key, value string) error\n\tUpdateOrCreate(key, value string) error\n\tDefaultMenu() error\n}\n\nfunc NewISettingRepo() ISettingRepo {\n\treturn &SettingRepo{}\n}\n\nfunc (u *SettingRepo) List(opts ...global.DBOption) ([]model.Setting, error) {\n\tvar settings []model.Setting\n\tdb := global.DB.Model(&model.Setting{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&settings).Error\n\treturn settings, err\n}\n\nfunc (u *SettingRepo) Create(key, value string) error {\n\tsetting := &model.Setting{\n\t\tKey:   key,\n\t\tValue: value,\n\t}\n\tif err := global.DB.Create(setting).Error; err != nil {\n\t\treturn err\n\t}\n\tsettingCache.Set(key, value, settingTTL)\n\treturn nil\n}\n\nfunc (u *SettingRepo) Get(opts ...global.DBOption) (model.Setting, error) {\n\tvar settings model.Setting\n\tdb := global.DB.Model(&model.Setting{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\n\terr := db.First(&settings).Error\n\tif err == nil && settings.Key != \"\" {\n\t\tsettingCache.Set(settings.Key, settings.Value, settingTTL)\n\t}\n\treturn settings, err\n}\n\nfunc (u *SettingRepo) GetValueByKey(key string) (string, error) {\n\tif val, found := settingCache.Get(key); found {\n\t\treturn val.(string), nil\n\t}\n\n\tvar setting model.Setting\n\tif err := global.DB.Model(&model.Setting{}).Where(\"key = ?\", key).First(&setting).Error; err != nil {\n\t\treturn \"\", err\n\t}\n\tsettingCache.Set(key, setting.Value, settingTTL)\n\treturn setting.Value, nil\n}\n\nfunc (u *SettingRepo) Update(key, value string) error {\n\tif err := global.DB.Model(&model.Setting{}).Where(\"key = ?\", key).Updates(map[string]interface{}{\"value\": value}).Error; err != nil {\n\t\treturn err\n\t}\n\tsettingCache.Set(key, value, settingTTL)\n\treturn nil\n}\n\nfunc (u *SettingRepo) UpdateOrCreate(key, value string) error {\n\tvar setting model.Setting\n\tresult := global.DB.Where(\"key = ?\", key).First(&setting)\n\tif result.Error != nil {\n\t\tif errors.Is(result.Error, gorm.ErrRecordNotFound) {\n\t\t\tif err := global.DB.Create(&model.Setting{Key: key, Value: value}).Error; err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tsettingCache.Set(key, value, settingTTL)\n\t\t\treturn nil\n\t\t}\n\t\treturn result.Error\n\t}\n\tif err := global.DB.Model(&setting).UpdateColumn(\"value\", value).Error; err != nil {\n\t\treturn err\n\t}\n\tsettingCache.Set(key, value, settingTTL)\n\treturn nil\n}\n\nfunc (u *SettingRepo) DefaultMenu() error {\n\tmenus := helper.LoadMenus()\n\tif err := global.DB.Model(&model.Setting{}).\n\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\tUpdate(\"value\", menus).Error; err != nil {\n\t\treturn err\n\t}\n\tsettingCache.Set(\"HideMenu\", menus, settingTTL)\n\treturn nil\n}\n"
  },
  {
    "path": "core/app/repo/task.go",
    "content": "package repo\n\nimport (\n\t\"context\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype TaskRepo struct {\n}\n\ntype ITaskRepo interface {\n\tSave(ctx context.Context, task *model.Task) error\n\tGetFirst(opts ...global.DBOption) (model.Task, error)\n\tPage(page, size int, opts ...global.DBOption) (int64, []model.Task, error)\n\tUpdate(ctx context.Context, task *model.Task) error\n\n\tWithByID(id string) global.DBOption\n\tWithResourceID(id uint) global.DBOption\n\tWithOperate(taskOperate string) global.DBOption\n}\n\nfunc NewITaskRepo() ITaskRepo {\n\treturn &TaskRepo{}\n}\n\nfunc getTaskDb(opts ...global.DBOption) *gorm.DB {\n\tdb := global.TaskDB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db\n}\n\nfunc getTaskTx(ctx context.Context, opts ...global.DBOption) *gorm.DB {\n\ttx, ok := ctx.Value(\"db\").(*gorm.DB)\n\tif ok {\n\t\tfor _, opt := range opts {\n\t\t\ttx = opt(tx)\n\t\t}\n\t\treturn tx\n\t}\n\treturn getTaskDb(opts...)\n}\n\nfunc (t TaskRepo) WithByID(id string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"id = ?\", id)\n\t}\n}\n\nfunc (t TaskRepo) WithOperate(taskOperate string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"operate = ?\", taskOperate)\n\t}\n}\n\nfunc (t TaskRepo) WithResourceID(id uint) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"resource_id = ?\", id)\n\t}\n}\n\nfunc (t TaskRepo) Save(ctx context.Context, task *model.Task) error {\n\treturn getTaskTx(ctx).Save(&task).Error\n}\n\nfunc (t TaskRepo) GetFirst(opts ...global.DBOption) (model.Task, error) {\n\tvar task model.Task\n\tdb := getTaskDb(opts...).Model(&model.Task{})\n\tif err := db.First(&task).Error; err != nil {\n\t\treturn task, err\n\t}\n\treturn task, nil\n}\n\nfunc (t TaskRepo) Page(page, size int, opts ...global.DBOption) (int64, []model.Task, error) {\n\tvar tasks []model.Task\n\tdb := getTaskDb(opts...).Model(&model.Task{})\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&tasks).Error\n\treturn count, tasks, err\n}\n\nfunc (t TaskRepo) Update(ctx context.Context, task *model.Task) error {\n\treturn getTaskTx(ctx).Save(&task).Error\n}\n"
  },
  {
    "path": "core/app/repo/upgrade_log.go",
    "content": "package repo\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"gorm.io/gorm\"\n)\n\ntype UpgradeLogRepo struct{}\n\ntype IUpgradeLogRepo interface {\n\tGet(opts ...global.DBOption) (model.UpgradeLog, error)\n\tList(opts ...global.DBOption) ([]model.UpgradeLog, error)\n\tCreate(log *model.UpgradeLog) error\n\tPage(limit, offset int, opts ...global.DBOption) (int64, []model.UpgradeLog, error)\n\tDelete(opts ...global.DBOption) error\n\n\tWithByNodeID(nodeID uint) global.DBOption\n\tWithByUpgradeVersion(oldVersion, newVersion string) global.DBOption\n}\n\nfunc NewIUpgradeLogRepo() IUpgradeLogRepo {\n\treturn &UpgradeLogRepo{}\n}\n\nfunc (u *UpgradeLogRepo) Get(opts ...global.DBOption) (model.UpgradeLog, error) {\n\tvar log model.UpgradeLog\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.First(&log).Error\n\treturn log, err\n}\n\nfunc (u *UpgradeLogRepo) List(opts ...global.DBOption) ([]model.UpgradeLog, error) {\n\tvar logs []model.UpgradeLog\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\terr := db.Find(&logs).Error\n\treturn logs, err\n}\n\nfunc (u *UpgradeLogRepo) Clean() error {\n\treturn global.DB.Exec(\"delete from upgrade_logs;\").Error\n}\n\nfunc (u *UpgradeLogRepo) Create(log *model.UpgradeLog) error {\n\treturn global.DB.Create(log).Error\n}\n\nfunc (u *UpgradeLogRepo) Save(log *model.UpgradeLog) error {\n\treturn global.DB.Save(log).Error\n}\n\nfunc (u *UpgradeLogRepo) Delete(opts ...global.DBOption) error {\n\tdb := global.DB\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\treturn db.Delete(&model.UpgradeLog{}).Error\n}\n\nfunc (u *UpgradeLogRepo) Page(page, size int, opts ...global.DBOption) (int64, []model.UpgradeLog, error) {\n\tvar ops []model.UpgradeLog\n\tdb := global.DB.Model(&model.UpgradeLog{})\n\tfor _, opt := range opts {\n\t\tdb = opt(db)\n\t}\n\tcount := int64(0)\n\tdb = db.Count(&count)\n\terr := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error\n\treturn count, ops, err\n}\n\nfunc (c *UpgradeLogRepo) WithByNodeID(nodeID uint) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"node_id = ?\", nodeID)\n\t}\n}\n\nfunc (c *UpgradeLogRepo) WithByUpgradeVersion(oldVersion, newVersion string) global.DBOption {\n\treturn func(g *gorm.DB) *gorm.DB {\n\t\treturn g.Where(\"old_version = ? AND new_version = ?\", oldVersion, newVersion)\n\t}\n}\n"
  },
  {
    "path": "core/app/service/auth.go",
    "content": "package service\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/mfa\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/passkey\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n\t\"gorm.io/gorm\"\n)\n\ntype AuthService struct{}\n\ntype IAuthService interface {\n\tGetResponsePage() (string, error)\n\tVerifyCode(code string) (bool, error)\n\tLogin(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error)\n\tLogOut(c *gin.Context) error\n\tMFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error)\n\tPasskeyBeginLogin(c *gin.Context, entrance string) (*dto.PasskeyBeginResponse, string, error)\n\tPasskeyFinishLogin(c *gin.Context, sessionID, entrance string) (*dto.UserLoginInfo, string, error)\n\tPasskeyBeginRegister(c *gin.Context, name string) (*dto.PasskeyBeginResponse, string, error)\n\tPasskeyFinishRegister(c *gin.Context, sessionID string) (string, error)\n\tPasskeyList() ([]dto.PasskeyInfo, error)\n\tPasskeyDelete(id string) error\n\tPasskeyStatus(c *gin.Context) bool\n\tGetSecurityEntrance() string\n\tIsLogin(c *gin.Context) bool\n}\n\nfunc NewIAuthService() IAuthService {\n\treturn &AuthService{}\n}\n\nfunc (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error) {\n\tnameSetting, err := settingRepo.Get(repo.WithByKey(\"UserName\"))\n\tif err != nil {\n\t\treturn nil, \"\", buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif nameSetting.Value != info.Name {\n\t\treturn nil, \"ErrAuth\", buserr.New(\"ErrAuth\")\n\t}\n\tif err = checkPassword(info.Password); err != nil {\n\t\treturn nil, \"ErrAuth\", err\n\t}\n\tentranceSetting, err := settingRepo.Get(repo.WithByKey(\"SecurityEntrance\"))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {\n\t\treturn nil, \"ErrEntrance\", buserr.New(\"ErrEntrance\")\n\t}\n\tmfa, err := settingRepo.Get(repo.WithByKey(\"MFAStatus\"))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif err = settingRepo.Update(\"Language\", info.Language); err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif mfa.Value == constant.StatusEnable {\n\t\treturn &dto.UserLoginInfo{Name: nameSetting.Value, MfaStatus: mfa.Value}, \"\", nil\n\t}\n\tres, err := u.generateSession(c, info.Name)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif entrance != \"\" {\n\t\tentranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))\n\t\tc.SetCookie(\"SecurityEntrance\", entranceValue, 0, \"\", \"\", false, true)\n\t}\n\treturn res, \"\", nil\n}\n\nfunc (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error) {\n\tnameSetting, err := settingRepo.Get(repo.WithByKey(\"UserName\"))\n\tif err != nil {\n\t\treturn nil, \"\", buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif nameSetting.Value != info.Name {\n\t\treturn nil, \"ErrAuth\", nil\n\t}\n\tif err = checkPassword(info.Password); err != nil {\n\t\treturn nil, \"ErrAuth\", err\n\t}\n\tentranceSetting, err := settingRepo.Get(repo.WithByKey(\"SecurityEntrance\"))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {\n\t\treturn nil, \"\", buserr.New(\"ErrEntrance\")\n\t}\n\tmfaSecret, err := settingRepo.Get(repo.WithByKey(\"MFASecret\"))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tmfaInterval, err := settingRepo.Get(repo.WithByKey(\"MFAInterval\"))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tsuccess := mfa.ValidCode(info.Code, mfaInterval.Value, mfaSecret.Value)\n\tif !success {\n\t\treturn nil, \"ErrMFA\", nil\n\t}\n\tres, err := u.generateSession(c, info.Name)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif entrance != \"\" {\n\t\tentranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))\n\t\tc.SetCookie(\"SecurityEntrance\", entranceValue, 0, \"\", \"\", false, true)\n\t}\n\treturn res, \"\", nil\n}\n\nfunc (u *AuthService) generateSession(c *gin.Context, name string) (*dto.UserLoginInfo, error) {\n\tsetting, err := settingRepo.Get(repo.WithByKey(\"SessionTimeout\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\thttpsSetting, err := settingRepo.Get(repo.WithByKey(\"SSL\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlifeTime, err := strconv.Atoi(setting.Value)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsessionUser, err := global.SESSION.Get(c)\n\tif err != nil {\n\t\terr := global.SESSION.Set(c, sessionUser, httpsSetting.Value == constant.StatusEnable, lifeTime)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &dto.UserLoginInfo{Name: name}, nil\n\t}\n\tif err := global.SESSION.Set(c, sessionUser, httpsSetting.Value == constant.StatusEnable, lifeTime); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &dto.UserLoginInfo{Name: name}, nil\n}\n\nfunc (u *AuthService) LogOut(c *gin.Context) error {\n\thttpsSetting, err := settingRepo.Get(repo.WithByKey(\"SSL\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tsID, _ := c.Cookie(constant.SessionName)\n\tif sID != \"\" {\n\t\tc.SetCookie(constant.SessionName, sID, -1, \"\", \"\", httpsSetting.Value == constant.StatusEnable, true)\n\t\terr := global.SESSION.Delete(c)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *AuthService) VerifyCode(code string) (bool, error) {\n\tsetting, err := settingRepo.Get(repo.WithByKey(\"SecurityEntrance\"))\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn hmac.Equal([]byte(setting.Value), []byte(code)), nil\n}\n\nfunc (u *AuthService) GetResponsePage() (string, error) {\n\tpageCode, err := settingRepo.Get(repo.WithByKey(\"NoAuthSetting\"))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn pageCode.Value, nil\n}\n\nfunc (u *AuthService) GetSecurityEntrance() string {\n\tstatus, err := settingRepo.Get(repo.WithByKey(\"SecurityEntrance\"))\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tif len(status.Value) == 0 {\n\t\treturn \"\"\n\t}\n\treturn status.Value\n}\n\nfunc (u *AuthService) IsLogin(c *gin.Context) bool {\n\t_, err := global.SESSION.Get(c)\n\treturn err == nil\n}\n\nfunc (u *AuthService) PasskeyStatus(c *gin.Context) bool {\n\tenabled, err := u.passkeyEnabled(c)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"passkey enabled check failed, err: %v\", err)\n\t\tenabled = false\n\t}\n\tconfigured, err := u.passkeyConfigured()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"passkey config check failed, err: %v\", err)\n\t\tconfigured = false\n\t}\n\treturn enabled && configured\n}\n\nfunc (u *AuthService) PasskeyBeginLogin(c *gin.Context, entrance string) (*dto.PasskeyBeginResponse, string, error) {\n\tif err := u.checkEntrance(entrance); err != nil {\n\t\treturn nil, \"ErrEntrance\", err\n\t}\n\n\tconfig, msgKey, err := u.passkeyConfig(c)\n\tif err != nil {\n\t\treturn nil, msgKey, err\n\t}\n\n\trecords, err := loadPasskeyCredentialRecords()\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif len(records) == 0 {\n\t\treturn nil, \"ErrPasskeyNotConfigured\", buserr.New(\"ErrPasskeyNotConfigured\")\n\t}\n\n\tuser, err := u.passkeyUser(records, true)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\twa, err := webauthn.New(config)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tassertion, sessionData, err := wa.BeginLogin(user)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tpasskeySessions := passkey.GetPasskeySessionStore()\n\tsessionID := passkeySessions.Set(passkey.PasskeySessionKindLogin, \"\", *sessionData)\n\treturn &dto.PasskeyBeginResponse{SessionID: sessionID, PublicKey: assertion.Response}, \"\", nil\n}\n\nfunc (u *AuthService) PasskeyFinishLogin(c *gin.Context, sessionID, entrance string) (*dto.UserLoginInfo, string, error) {\n\tif sessionID == \"\" {\n\t\treturn nil, \"ErrPasskeySession\", buserr.New(\"ErrPasskeySession\")\n\t}\n\n\tif err := u.checkEntrance(entrance); err != nil {\n\t\treturn nil, \"ErrEntrance\", err\n\t}\n\n\tconfig, msgKey, err := u.passkeyConfig(c)\n\tif err != nil {\n\t\treturn nil, msgKey, err\n\t}\n\n\tpasskeySessions := passkey.GetPasskeySessionStore()\n\tsession, ok := passkeySessions.Get(sessionID)\n\tif !ok || session.Kind != passkey.PasskeySessionKindLogin {\n\t\treturn nil, \"ErrPasskeySession\", buserr.New(\"ErrPasskeySession\")\n\t}\n\tpasskeySessions.Delete(sessionID)\n\n\trecords, err := loadPasskeyCredentialRecords()\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif len(records) == 0 {\n\t\treturn nil, \"ErrPasskeyNotConfigured\", buserr.New(\"ErrPasskeyNotConfigured\")\n\t}\n\n\tuser, err := u.passkeyUser(records, true)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\twa, err := webauthn.New(config)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tcredential, err := wa.FinishLogin(user, session.Session, c.Request)\n\tif err != nil {\n\t\treturn nil, \"ErrAuth\", err\n\t}\n\n\tif err := updatePasskeyCredentialRecord(records, credential); err != nil {\n\t\treturn nil, \"ErrAuth\", err\n\t}\n\tif err := savePasskeyCredentialRecords(records); err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tuserSetting, err := settingRepo.Get(repo.WithByKey(\"UserName\"))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tres, err := u.generateSession(c, userSetting.Value)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif entrance != \"\" {\n\t\tentranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))\n\t\tc.SetCookie(\"SecurityEntrance\", entranceValue, 0, \"\", \"\", false, true)\n\t}\n\treturn res, \"\", nil\n}\n\nfunc (u *AuthService) PasskeyBeginRegister(c *gin.Context, name string) (*dto.PasskeyBeginResponse, string, error) {\n\tconfig, msgKey, err := u.passkeyConfig(c)\n\tif err != nil {\n\t\treturn nil, msgKey, err\n\t}\n\trecords, err := loadPasskeyCredentialRecords()\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif len(records) >= passkey.PasskeyMaxCredentials {\n\t\treturn nil, \"ErrPasskeyLimit\", buserr.New(\"ErrPasskeyLimit\")\n\t}\n\tuser, err := u.passkeyUser(records, true)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\twa, err := webauthn.New(config)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\texclusions := make([]protocol.CredentialDescriptor, len(user.Credentials))\n\tfor i, credential := range user.Credentials {\n\t\texclusions[i] = credential.Descriptor()\n\t}\n\tcreation, sessionData, err := wa.BeginRegistration(user, webauthn.WithExclusions(exclusions))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tpasskeySessions := passkey.GetPasskeySessionStore()\n\tsessionID := passkeySessions.Set(passkey.PasskeySessionKindRegister, strings.TrimSpace(name), *sessionData)\n\treturn &dto.PasskeyBeginResponse{SessionID: sessionID, PublicKey: creation.Response}, \"\", nil\n}\n\nfunc (u *AuthService) PasskeyFinishRegister(c *gin.Context, sessionID string) (string, error) {\n\tif sessionID == \"\" {\n\t\treturn \"ErrPasskeySession\", buserr.New(\"ErrPasskeySession\")\n\t}\n\tconfig, msgKey, err := u.passkeyConfig(c)\n\tif err != nil {\n\t\treturn msgKey, err\n\t}\n\n\tpasskeySessions := passkey.GetPasskeySessionStore()\n\tsession, ok := passkeySessions.Get(sessionID)\n\tif !ok || session.Kind != passkey.PasskeySessionKindRegister {\n\t\treturn \"ErrPasskeySession\", buserr.New(\"ErrPasskeySession\")\n\t}\n\n\tpasskeySessions.Delete(sessionID)\n\n\trecords, err := loadPasskeyCredentialRecords()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(records) >= passkey.PasskeyMaxCredentials {\n\t\treturn \"ErrPasskeyLimit\", buserr.New(\"ErrPasskeyLimit\")\n\t}\n\n\tuser, err := u.passkeyUser(records, true)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\twa, err := webauthn.New(config)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tcredential, err := wa.FinishRegistration(user, session.Session, c.Request)\n\tif err != nil {\n\t\treturn \"ErrPasskeyVerify\", err\n\t}\n\n\tif passkeyCredentialExists(records, credential.ID) {\n\t\treturn \"ErrPasskeyDuplicate\", buserr.New(\"ErrPasskeyDuplicate\")\n\t}\n\n\tdisplayName := strings.TrimSpace(session.Name)\n\tif displayName == \"\" {\n\t\tdisplayName = fmt.Sprintf(\"%s-%s\", passkey.PasskeyCredentialNameDefault, time.Now().Format(\"20060102150405\"))\n\t}\n\n\trecords = append(records, passkey.PasskeyCredentialRecord{\n\t\tID:         base64.RawURLEncoding.EncodeToString(credential.ID),\n\t\tName:       displayName,\n\t\tCreatedAt:  time.Now().Format(constant.DateTimeLayout),\n\t\tLastUsedAt: \"\",\n\t\tFlagsValue: credentialFlagsValue(credential.Flags),\n\t\tCredential: *credential,\n\t})\n\n\tif err := savePasskeyCredentialRecords(records); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn \"\", nil\n}\n\nfunc (u *AuthService) PasskeyList() ([]dto.PasskeyInfo, error) {\n\trecords, err := loadPasskeyCredentialRecords()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlist := make([]dto.PasskeyInfo, 0, len(records))\n\tfor _, record := range records {\n\t\tlist = append(list, dto.PasskeyInfo{\n\t\t\tID:         record.ID,\n\t\t\tName:       record.Name,\n\t\t\tCreatedAt:  record.CreatedAt,\n\t\t\tLastUsedAt: record.LastUsedAt,\n\t\t})\n\t}\n\treturn list, nil\n}\n\nfunc (u *AuthService) PasskeyDelete(id string) error {\n\trecords, err := loadPasskeyCredentialRecords()\n\tif err != nil {\n\t\treturn err\n\t}\n\tindex := -1\n\tfor i, record := range records {\n\t\tif record.ID == id {\n\t\t\tindex = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif index == -1 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\trecords = append(records[:index], records[index+1:]...)\n\treturn savePasskeyCredentialRecords(records)\n}\n\nfunc (u *AuthService) passkeyEnabled(c *gin.Context) (bool, error) {\n\treturn strings.EqualFold(passkeyRequestScheme(c), \"https\"), nil\n}\n\nfunc (u *AuthService) passkeyConfigured() (bool, error) {\n\tbindDomain, err := settingRepo.Get(repo.WithByKey(\"BindDomain\"))\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif strings.TrimSpace(bindDomain.Value) == \"\" {\n\t\treturn false, nil\n\t}\n\trecords, err := loadPasskeyCredentialRecords()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn len(records) > 0, nil\n}\n\nfunc (u *AuthService) passkeyUser(records []passkey.PasskeyCredentialRecord, allowCreate bool) (*passkey.PasskeyUser, error) {\n\tuserID, err := u.passkeyUserID(allowCreate)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnameSetting, err := settingRepo.Get(repo.WithByKey(\"UserName\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcredentials := make([]webauthn.Credential, len(records))\n\tfor i, record := range records {\n\t\tcredentials[i] = record.Credential\n\t}\n\treturn &passkey.PasskeyUser{\n\t\tID:          userID,\n\t\tName:        nameSetting.Value,\n\t\tDisplayName: nameSetting.Value,\n\t\tCredentials: credentials,\n\t}, nil\n}\n\nfunc (u *AuthService) passkeyUserID(allowCreate bool) ([]byte, error) {\n\tsetting, err := settingRepo.Get(repo.WithByKey(passkey.PasskeyUserIDSettingKey))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif setting.Value == \"\" {\n\t\tif !allowCreate {\n\t\t\treturn nil, buserr.New(\"ErrPasskeyNotConfigured\")\n\t\t}\n\t\traw := make([]byte, 32)\n\t\tif _, err := rand.Read(raw); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tencoded := base64.RawURLEncoding.EncodeToString(raw)\n\t\tif err := settingRepo.Update(passkey.PasskeyUserIDSettingKey, encoded); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn raw, nil\n\t}\n\traw, err := base64.RawURLEncoding.DecodeString(setting.Value)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn raw, nil\n}\n\nfunc (u *AuthService) passkeyConfig(c *gin.Context) (*webauthn.Config, string, error) {\n\tenabled, err := u.passkeyEnabled(c)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif !enabled {\n\t\treturn nil, \"ErrPasskeyDisabled\", buserr.New(\"ErrPasskeyDisabled\")\n\t}\n\torigin, rpID, err := passkeyOriginAndRPID(c)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tpanelName, err := settingRepo.Get(repo.WithByKey(\"PanelName\"))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\treturn &webauthn.Config{\n\t\tRPID:          rpID,\n\t\tRPDisplayName: panelName.Value,\n\t\tRPOrigins:     []string{origin},\n\t\tAuthenticatorSelection: protocol.AuthenticatorSelection{\n\t\t\tUserVerification: protocol.VerificationRequired,\n\t\t},\n\t}, \"\", nil\n}\n\nfunc (u *AuthService) checkEntrance(entrance string) error {\n\tentranceSetting, err := settingRepo.Get(repo.WithByKey(\"SecurityEntrance\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {\n\t\treturn buserr.New(\"ErrEntrance\")\n\t}\n\treturn nil\n}\n\nfunc loadPasskeyCredentialRecords() ([]passkey.PasskeyCredentialRecord, error) {\n\tsetting, err := settingRepo.Get(repo.WithByKey(passkey.PasskeyCredentialSettingKey))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif setting.Value == \"\" {\n\t\treturn []passkey.PasskeyCredentialRecord{}, nil\n\t}\n\tdecrypted, err := encrypt.StringDecrypt(setting.Value)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar records []passkey.PasskeyCredentialRecord\n\tif err := json.Unmarshal([]byte(decrypted), &records); err != nil {\n\t\treturn nil, err\n\t}\n\tfor i := range records {\n\t\trecords[i].Credential.Flags = webauthn.NewCredentialFlags(protocol.AuthenticatorFlags(records[i].FlagsValue))\n\t}\n\treturn records, nil\n}\n\nfunc savePasskeyCredentialRecords(records []passkey.PasskeyCredentialRecord) error {\n\tif len(records) == 0 {\n\t\treturn settingRepo.Update(passkey.PasskeyCredentialSettingKey, \"\")\n\t}\n\tfor i := range records {\n\t\trecords[i].FlagsValue = credentialFlagsValue(records[i].Credential.Flags)\n\t}\n\traw, err := json.Marshal(records)\n\tif err != nil {\n\t\treturn err\n\t}\n\tencrypted, err := encrypt.StringEncrypt(string(raw))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn settingRepo.Update(passkey.PasskeyCredentialSettingKey, encrypted)\n}\n\nfunc passkeyCredentialExists(records []passkey.PasskeyCredentialRecord, credentialID []byte) bool {\n\tencoded := base64.RawURLEncoding.EncodeToString(credentialID)\n\tfor _, record := range records {\n\t\tif record.ID == encoded {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc updatePasskeyCredentialRecord(records []passkey.PasskeyCredentialRecord, credential *webauthn.Credential) error {\n\tencoded := base64.RawURLEncoding.EncodeToString(credential.ID)\n\tfor i := range records {\n\t\tif records[i].ID == encoded {\n\t\t\trecords[i].Credential = *credential\n\t\t\trecords[i].FlagsValue = credentialFlagsValue(credential.Flags)\n\t\t\trecords[i].LastUsedAt = time.Now().Format(constant.DateTimeLayout)\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn buserr.New(\"ErrPasskeyNotConfigured\")\n}\n\nfunc credentialFlagsValue(flags webauthn.CredentialFlags) uint8 {\n\tvar value protocol.AuthenticatorFlags\n\tif flags.UserPresent {\n\t\tvalue |= protocol.FlagUserPresent\n\t}\n\tif flags.UserVerified {\n\t\tvalue |= protocol.FlagUserVerified\n\t}\n\tif flags.BackupEligible {\n\t\tvalue |= protocol.FlagBackupEligible\n\t}\n\tif flags.BackupState {\n\t\tvalue |= protocol.FlagBackupState\n\t}\n\treturn uint8(value)\n}\n\nfunc passkeyOriginAndRPID(c *gin.Context) (string, string, error) {\n\thost := passkeyRequestHost(c)\n\tif host == \"\" {\n\t\treturn \"\", \"\", fmt.Errorf(\"missing request host\")\n\t}\n\tscheme := passkeyRequestScheme(c)\n\torigin := fmt.Sprintf(\"%s://%s\", scheme, host)\n\n\tbindDomain, err := settingRepo.Get(repo.WithByKey(\"BindDomain\"))\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\tbindDomainValue := strings.TrimSpace(bindDomain.Value)\n\tif bindDomainValue == \"\" {\n\t\treturn \"\", \"\", buserr.New(\"ErrPasskeyNotConfigured\")\n\t}\n\thostDomain := stripHostPort(host)\n\tbindDomainValue = stripHostPort(bindDomainValue)\n\tif hostDomain == \"\" || !strings.EqualFold(hostDomain, bindDomainValue) {\n\t\treturn \"\", \"\", buserr.New(\"ErrPasskeyDisabled\")\n\t}\n\treturn origin, bindDomainValue, nil\n}\n\nfunc passkeyRequestHost(c *gin.Context) string {\n\thost := c.Request.Host\n\tif strings.Contains(host, \",\") {\n\t\thost = strings.TrimSpace(strings.Split(host, \",\")[0])\n\t}\n\treturn strings.TrimSpace(host)\n}\n\nfunc passkeyRequestScheme(c *gin.Context) string {\n\tif c.Request.TLS != nil {\n\t\treturn \"https\"\n\t}\n\tif !passkeyIsFromTrustedProxy(c) {\n\t\treturn \"http\"\n\t}\n\tif proto := passkeyForwardedProto(c.GetHeader(\"Forwarded\")); proto != \"\" {\n\t\treturn proto\n\t}\n\tif proto := passkeyXForwardedProto(c.GetHeader(\"X-Forwarded-Proto\")); proto != \"\" {\n\t\treturn proto\n\t}\n\treturn \"http\"\n}\n\nfunc passkeyIsFromTrustedProxy(c *gin.Context) bool {\n\tremoteIP := passkeyRemoteIP(c.Request.RemoteAddr)\n\tif remoteIP == nil {\n\t\treturn false\n\t}\n\tproxies, err := loadPasskeyTrustedProxies()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load passkey trusted proxies failed, err: %v\", err)\n\t\treturn false\n\t}\n\tfor _, cidr := range proxies {\n\t\tif cidr.Contains(remoteIP) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc passkeyRemoteIP(remoteAddr string) net.IP {\n\tif host, _, err := net.SplitHostPort(strings.TrimSpace(remoteAddr)); err == nil {\n\t\treturn net.ParseIP(host)\n\t}\n\treturn net.ParseIP(strings.TrimSpace(remoteAddr))\n}\n\nfunc loadPasskeyTrustedProxies() ([]*net.IPNet, error) {\n\tsetting, err := settingRepo.Get(repo.WithByKey(\"PasskeyTrustedProxies\"))\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn parsePasskeyTrustedProxies(\"127.0.0.1\\n::1\")\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn parsePasskeyTrustedProxies(setting.Value)\n}\n\nfunc parsePasskeyTrustedProxies(value string) ([]*net.IPNet, error) {\n\tlines := strings.Split(strings.TrimSpace(value), \"\\n\")\n\tif len(lines) == 0 {\n\t\treturn []*net.IPNet{}, nil\n\t}\n\tcidrs := make([]*net.IPNet, 0, len(lines))\n\tfor _, line := range lines {\n\t\titem := strings.TrimSpace(line)\n\t\tif item == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif ip := net.ParseIP(item); ip != nil {\n\t\t\tif ip.To4() != nil {\n\t\t\t\titem += \"/32\"\n\t\t\t} else {\n\t\t\t\titem += \"/128\"\n\t\t\t}\n\t\t}\n\t\t_, ipNet, err := net.ParseCIDR(item)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcidrs = append(cidrs, ipNet)\n\t}\n\treturn cidrs, nil\n}\n\nfunc passkeyForwardedProto(value string) string {\n\tif value == \"\" {\n\t\treturn \"\"\n\t}\n\tfor _, item := range strings.Split(value, \",\") {\n\t\tfor _, part := range strings.Split(item, \";\") {\n\t\t\tkv := strings.SplitN(strings.TrimSpace(part), \"=\", 2)\n\t\t\tif len(kv) != 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif !strings.EqualFold(strings.TrimSpace(kv[0]), \"proto\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tproto := strings.ToLower(strings.Trim(strings.TrimSpace(kv[1]), `\"`))\n\t\t\tif proto == \"http\" || proto == \"https\" {\n\t\t\t\treturn proto\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc passkeyXForwardedProto(value string) string {\n\tif value == \"\" {\n\t\treturn \"\"\n\t}\n\tproto := strings.ToLower(strings.TrimSpace(strings.Split(value, \",\")[0]))\n\tif proto == \"http\" || proto == \"https\" {\n\t\treturn proto\n\t}\n\treturn \"\"\n}\n\nfunc stripHostPort(hostport string) string {\n\tif hostport == \"\" {\n\t\treturn hostport\n\t}\n\thostport = strings.TrimSpace(hostport)\n\tif host, _, err := net.SplitHostPort(hostport); err == nil {\n\t\treturn strings.Trim(host, \"[]\")\n\t}\n\treturn strings.Trim(hostport, \"[]\")\n}\n\nfunc checkPassword(password string) error {\n\tpriKey, _ := settingRepo.Get(repo.WithByKey(\"PASSWORD_PRIVATE_KEY\"))\n\n\tprivateKey, err := encrypt.ParseRSAPrivateKey(priKey.Value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tloginPassword, err := encrypt.DecryptPassword(password, privateKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpasswordSetting, err := settingRepo.Get(repo.WithByKey(\"Password\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\texistPassword, err := encrypt.StringDecrypt(passwordSetting.Value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !hmac.Equal([]byte(loginPassword), []byte(existPassword)) {\n\t\treturn buserr.New(\"ErrAuth\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/app/service/backup.go",
    "content": "package service\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cloud_storage\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/req_helper/proxy_local\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype BackupService struct{}\n\ntype IBackupService interface {\n\tLoadBackupClientInfo(clientType string) (dto.BackupClientInfo, error)\n\tCreate(backupDto dto.BackupOperate) error\n\tUpdate(req dto.BackupOperate) error\n\tDelete(name string) error\n\tRefreshToken(req dto.OperateByName) error\n}\n\nfunc NewIBackupService() IBackupService {\n\treturn &BackupService{}\n}\n\nfunc (u *BackupService) LoadBackupClientInfo(clientType string) (dto.BackupClientInfo, error) {\n\tvar data dto.BackupClientInfo\n\tclientIDKey := \"OneDriveID\"\n\tclientIDSc := \"OneDriveSc\"\n\tdata.RedirectUri = constant.OneDriveRedirectURI\n\tclientID, err := settingRepo.Get(repo.WithByKey(clientIDKey))\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tidItem, err := base64.StdEncoding.DecodeString(clientID.Value)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tdata.ClientID = string(idItem)\n\tclientSecret, err := settingRepo.Get(repo.WithByKey(clientIDSc))\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tsecretItem, err := base64.StdEncoding.DecodeString(clientSecret.Value)\n\tif err != nil {\n\t\treturn data, err\n\t}\n\tdata.ClientSecret = string(secretItem)\n\n\treturn data, err\n}\n\nfunc (u *BackupService) Create(req dto.BackupOperate) error {\n\tif !req.IsPublic {\n\t\treturn buserr.New(\"ErrBackupPublic\")\n\t}\n\tbackup, _ := backupRepo.Get(repo.WithByName(req.Name))\n\tif backup.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tif req.Type != constant.Sftp && req.BackupPath != \"/\" {\n\t\treq.BackupPath = strings.TrimPrefix(req.BackupPath, \"/\")\n\t}\n\tif err := copier.Copy(&backup, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\titemAccessKey, err := base64.StdEncoding.DecodeString(backup.AccessKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackup.AccessKey = string(itemAccessKey)\n\titemCredential, err := base64.StdEncoding.DecodeString(backup.Credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackup.Credential = string(itemCredential)\n\n\tbackup.AccessKey, err = encrypt.StringEncrypt(backup.AccessKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbackup.Credential, err = encrypt.StringEncrypt(backup.Credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := backupRepo.Create(&backup); err != nil {\n\t\treturn err\n\t}\n\tif err := xpack.Sync(constant.SyncBackupAccounts); err != nil {\n\t\tglobal.LOG.Errorf(\"sync backup account to node failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) Delete(name string) error {\n\tbackup, _ := backupRepo.Get(repo.WithByName(name))\n\tif backup.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif !backup.IsPublic {\n\t\treturn buserr.New(\"ErrBackupPublic\")\n\t}\n\tif backup.Type == constant.Local {\n\t\treturn buserr.New(\"ErrBackupLocal\")\n\t}\n\tif _, err := proxy_local.NewLocalClient(fmt.Sprintf(\"/api/v2/backups/check/%s\", name), http.MethodGet, nil, nil); err != nil {\n\t\tglobal.LOG.Errorf(\"check used of local cronjob failed, err: %v\", err)\n\t\treturn buserr.New(\"ErrBackupInUsed\")\n\t}\n\tif err := xpack.CheckBackupUsed(name); err != nil {\n\t\tglobal.LOG.Errorf(\"check used of node cronjob failed, err: %v\", err)\n\t\treturn buserr.New(\"ErrBackupInUsed\")\n\t}\n\n\tif err := backupRepo.Delete(repo.WithByName(name)); err != nil {\n\t\treturn err\n\t}\n\tif err := xpack.Sync(constant.SyncBackupAccounts); err != nil {\n\t\tglobal.LOG.Errorf(\"sync backup account to node failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) Update(req dto.BackupOperate) error {\n\tbackup, _ := backupRepo.Get(repo.WithByName(req.Name))\n\tif backup.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif !backup.IsPublic {\n\t\treturn buserr.New(\"ErrBackupPublic\")\n\t}\n\tif backup.Type == constant.Local {\n\t\treturn buserr.New(\"ErrBackupLocal\")\n\t}\n\tif req.Type != constant.Sftp && req.BackupPath != \"/\" {\n\t\treq.BackupPath = strings.TrimPrefix(req.BackupPath, \"/\")\n\t}\n\tvar newBackup model.BackupAccount\n\tif err := copier.Copy(&newBackup, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tnewBackup.ID = backup.ID\n\titemAccessKey, err := base64.StdEncoding.DecodeString(newBackup.AccessKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewBackup.AccessKey = string(itemAccessKey)\n\titemCredential, err := base64.StdEncoding.DecodeString(newBackup.Credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewBackup.Credential = string(itemCredential)\n\tnewBackup.AccessKey, err = encrypt.StringEncrypt(newBackup.AccessKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewBackup.Credential, err = encrypt.StringEncrypt(newBackup.Credential)\n\tif err != nil {\n\t\treturn err\n\t}\n\tnewBackup.ID = backup.ID\n\tnewBackup.CreatedAt = backup.CreatedAt\n\tnewBackup.UpdatedAt = backup.UpdatedAt\n\tif err := backupRepo.Save(&newBackup); err != nil {\n\t\treturn err\n\t}\n\tif err := xpack.Sync(constant.SyncBackupAccounts); err != nil {\n\t\tglobal.LOG.Errorf(\"sync backup account to node failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *BackupService) RefreshToken(req dto.OperateByName) error {\n\tbackup, _ := backupRepo.Get(repo.WithByName(req.Name))\n\tif backup.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif !backup.IsPublic {\n\t\treturn buserr.New(\"ErrBackupPublic\")\n\t}\n\tvarMap := make(map[string]interface{})\n\tif err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {\n\t\treturn fmt.Errorf(\"failed to refresh %s - %s token, please retry, err: %v\", backup.Type, backup.Name, err)\n\t}\n\tvar (\n\t\trefreshToken string\n\t\terr          error\n\t)\n\tswitch backup.Type {\n\tcase constant.OneDrive:\n\t\trefreshToken, err = cloud_storage.RefreshToken(\"refresh_token\", \"refreshToken\", varMap)\n\tcase constant.ALIYUN:\n\t\trefreshToken, err = cloud_storage.RefreshALIToken(varMap)\n\t}\n\tif err != nil {\n\t\tvarMap[\"refresh_status\"] = constant.StatusFailed\n\t\tvarMap[\"refresh_msg\"] = err.Error()\n\t\treturn fmt.Errorf(\"failed to refresh %s-%s token, please retry, err: %v\", backup.Type, backup.Name, err)\n\t}\n\tvarMap[\"refresh_status\"] = constant.StatusSuccess\n\tvarMap[\"refresh_time\"] = time.Now().Format(constant.DateTimeLayout)\n\tvarMap[\"refresh_token\"] = refreshToken\n\n\tvarsItem, _ := json.Marshal(varMap)\n\tbackup.Vars = string(varsItem)\n\tif err := backupRepo.Save(&backup); err != nil {\n\t\treturn err\n\t}\n\tif err := xpack.Sync(constant.SyncBackupAccounts); err != nil {\n\t\tglobal.LOG.Errorf(\"sync backup account to node failed, err: %v\", err)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/app/service/command.go",
    "content": "package service\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/csv\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype CommandService struct{}\n\ntype ICommandService interface {\n\tList(req dto.OperateByType) ([]dto.CommandInfo, error)\n\tSearchForTree(req dto.OperateByType) ([]dto.CommandTree, error)\n\tSearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error)\n\tCreate(req dto.CommandOperate) error\n\tUpdate(req dto.CommandOperate) error\n\tDelete(ids []uint) error\n\n\tExport() (string, error)\n}\n\nfunc NewICommandService() ICommandService {\n\treturn &CommandService{}\n}\n\nfunc (u *CommandService) List(req dto.OperateByType) ([]dto.CommandInfo, error) {\n\tcommands, err := commandRepo.List(repo.WithOrderAsc(\"name\"), repo.WithByType(req.Type))\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tvar dtoCommands []dto.CommandInfo\n\tfor _, command := range commands {\n\t\tvar item dto.CommandInfo\n\t\tif err := copier.Copy(&item, &command); err != nil {\n\t\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdtoCommands = append(dtoCommands, item)\n\t}\n\treturn dtoCommands, err\n}\n\nfunc (u *CommandService) SearchForTree(req dto.OperateByType) ([]dto.CommandTree, error) {\n\tcmdList, err := commandRepo.List(repo.WithOrderAsc(\"name\"), repo.WithByType(req.Type))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgroups, err := groupRepo.GetList(repo.WithByType(req.Type))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar lists []dto.CommandTree\n\tfor _, group := range groups {\n\t\tvar data dto.CommandTree\n\t\tdata.Label = group.Name\n\t\tdata.Value = group.Name\n\t\tfor _, cmd := range cmdList {\n\t\t\tif cmd.GroupID == group.ID {\n\t\t\t\tdata.Children = append(data.Children, dto.CommandTree{Label: cmd.Name, Value: cmd.Command})\n\t\t\t}\n\t\t}\n\t\tif len(data.Children) != 0 {\n\t\t\tlists = append(lists, data)\n\t\t}\n\t}\n\treturn lists, err\n}\n\nfunc (u *CommandService) SearchWithPage(req dto.SearchCommandWithPage) (int64, interface{}, error) {\n\toptions := []global.DBOption{\n\t\trepo.WithOrderRuleBy(req.OrderBy, req.Order),\n\t\trepo.WithByType(req.Type),\n\t}\n\tif len(req.Info) != 0 {\n\t\toptions = append(options, commandRepo.WithByInfo(req.Info))\n\t}\n\tif req.GroupID != 0 {\n\t\toptions = append(options, repo.WithByGroupID(req.GroupID))\n\t}\n\ttotal, commands, err := commandRepo.Page(req.Page, req.PageSize, options...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tgroups, _ := groupRepo.GetList(repo.WithByType(req.Type))\n\tvar dtoCommands []dto.CommandInfo\n\tfor _, command := range commands {\n\t\tvar item dto.CommandInfo\n\t\tif err := copier.Copy(&item, &command); err != nil {\n\t\t\treturn 0, nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tfor _, group := range groups {\n\t\t\tif command.GroupID == group.ID {\n\t\t\t\titem.GroupBelong = group.Name\n\t\t\t\titem.GroupID = group.ID\n\t\t\t}\n\t\t}\n\t\tdtoCommands = append(dtoCommands, item)\n\t}\n\treturn total, dtoCommands, err\n}\n\nfunc (u *CommandService) Export() (string, error) {\n\tcommands, err := commandRepo.List(repo.WithByType(\"command\"))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar list []csv.CommandTemplate\n\tfor _, item := range commands {\n\t\tlist = append(list, csv.CommandTemplate{\n\t\t\tName:    item.Name,\n\t\t\tCommand: item.Command,\n\t\t})\n\t}\n\ttmpFileName := path.Join(global.CONF.Base.InstallDir, \"1panel/tmp/export/commands\", fmt.Sprintf(\"1panel-commands-%s.csv\", time.Now().Format(constant.DateTimeSlimLayout)))\n\tif _, err := os.Stat(path.Dir(tmpFileName)); err != nil {\n\t\t_ = os.MkdirAll(path.Dir(tmpFileName), constant.DirPerm)\n\t}\n\tif err := csv.ExportCommands(tmpFileName, list); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn tmpFileName, err\n}\n\nfunc (u *CommandService) Create(req dto.CommandOperate) error {\n\tcommand, _ := commandRepo.Get(repo.WithByName(req.Name), repo.WithByType(req.Type))\n\tif command.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tif err := copier.Copy(&command, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif err := commandRepo.Create(&command); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *CommandService) Delete(ids []uint) error {\n\tif len(ids) == 1 {\n\t\tcommand, _ := commandRepo.Get(repo.WithByID(ids[0]))\n\t\tif command.ID == 0 {\n\t\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t\t}\n\t\treturn commandRepo.Delete(repo.WithByID(ids[0]))\n\t}\n\treturn commandRepo.Delete(repo.WithByIDs(ids))\n}\n\nfunc (u *CommandService) Update(req dto.CommandOperate) error {\n\tcommand, _ := commandRepo.Get(repo.WithByName(req.Name), repo.WithByType(req.Type))\n\tif command.ID != 0 && command.ID != req.ID {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tupMap := make(map[string]interface{})\n\tupMap[\"name\"] = req.Name\n\tupMap[\"group_id\"] = req.GroupID\n\tupMap[\"command\"] = req.Command\n\treturn commandRepo.Update(req.ID, upMap)\n}\n"
  },
  {
    "path": "core/app/service/entry.go",
    "content": "package service\n\nimport \"github.com/1Panel-dev/1Panel/core/app/repo\"\n\nvar (\n\tcommandRepo    = repo.NewICommandRepo()\n\tsettingRepo    = repo.NewISettingRepo()\n\tbackupRepo     = repo.NewIBackupRepo()\n\tlogRepo        = repo.NewILogRepo()\n\tgroupRepo      = repo.NewIGroupRepo()\n\tupgradeLogRepo = repo.NewIUpgradeLogRepo()\n\n\tagentRepo  = repo.NewIAgentRepo()\n\tscriptRepo = repo.NewIScriptRepo()\n)\n"
  },
  {
    "path": "core/app/service/group.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/req_helper/proxy_local\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype GroupService struct{}\n\ntype IGroupService interface {\n\tList(req dto.OperateByType) ([]dto.GroupInfo, error)\n\tCreate(req dto.GroupCreate) error\n\tUpdate(req dto.GroupUpdate) error\n\tDelete(id uint) error\n}\n\nfunc NewIGroupService() IGroupService {\n\treturn &GroupService{}\n}\n\nfunc (u *GroupService) List(req dto.OperateByType) ([]dto.GroupInfo, error) {\n\toptions := []global.DBOption{\n\t\trepo.WithOrderDesc(\"is_default\"),\n\t\trepo.WithOrderDesc(\"created_at\"),\n\t}\n\tif len(req.Type) != 0 {\n\t\toptions = append(options, repo.WithByType(req.Type))\n\t}\n\tvar (\n\t\tgroups []model.Group\n\t\terr    error\n\t)\n\tgroups, err = groupRepo.GetList(options...)\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tvar dtoUsers []dto.GroupInfo\n\tfor _, group := range groups {\n\t\tvar item dto.GroupInfo\n\t\tif err := copier.Copy(&item, &group); err != nil {\n\t\t\treturn nil, buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t\t}\n\t\tdtoUsers = append(dtoUsers, item)\n\t}\n\treturn dtoUsers, err\n}\n\nfunc (u *GroupService) Create(req dto.GroupCreate) error {\n\tgroup, _ := groupRepo.Get(repo.WithByName(req.Name), repo.WithByType(req.Type))\n\tif group.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tif err := copier.Copy(&group, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif err := groupRepo.Create(&group); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *GroupService) Delete(id uint) error {\n\tgroup, _ := groupRepo.Get(repo.WithByID(id))\n\tif group.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tif group.Type == \"script\" {\n\t\tlist, _ := scriptRepo.GetList()\n\t\tif len(list) == 0 {\n\t\t\treturn groupRepo.Delete(repo.WithByID(id))\n\t\t}\n\t\tfor _, itemData := range list {\n\t\t\tgroupIDs := strings.Split(itemData.Groups, \",\")\n\t\t\tfor _, idItem := range groupIDs {\n\t\t\t\tgroupID, _ := strconv.Atoi(idItem)\n\t\t\t\tif uint(groupID) == id {\n\t\t\t\t\treturn buserr.New(\"ErrGroupIsInUse\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn groupRepo.Delete(repo.WithByID(id))\n\t}\n\tif group.IsDefault {\n\t\treturn buserr.New(\"ErrGroupIsDefault\")\n\t}\n\tdefaultGroup, err := groupRepo.Get(repo.WithByType(group.Type), groupRepo.WithByDefault(true))\n\tif err != nil {\n\t\treturn err\n\t}\n\tswitch group.Type {\n\tcase \"script\":\n\t\terr = scriptRepo.UpdateGroup(id, defaultGroup.ID)\n\tcase \"command\":\n\t\terr = commandRepo.UpdateGroup(id, defaultGroup.ID)\n\tcase \"node\":\n\t\terr = xpack.UpdateGroup(\"node\", id, defaultGroup.ID)\n\tcase \"website\":\n\t\tbodyItem := []byte(fmt.Sprintf(`{\"Group\":%v, \"NewGroup\":%v}`, id, defaultGroup.ID))\n\t\tif _, err := proxy_local.NewLocalClient(\"/api/v2/websites/group/change\", http.MethodPost, bytes.NewReader(bodyItem), nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := xpack.UpdateGroup(\"node\", id, defaultGroup.ID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn groupRepo.Delete(repo.WithByID(id))\n}\n\nfunc (u *GroupService) Update(req dto.GroupUpdate) error {\n\tif req.IsDefault {\n\t\tif err := groupRepo.CancelDefault(req.Type); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tupMap := make(map[string]interface{})\n\tupMap[\"name\"] = req.Name\n\tupMap[\"is_default\"] = req.IsDefault\n\n\treturn groupRepo.Update(req.ID, upMap)\n}\n"
  },
  {
    "path": "core/app/service/logs.go",
    "content": "package service\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\tgeo2 \"github.com/1Panel-dev/1Panel/core/utils/geo\"\n\t\"github.com/gin-gonic/gin\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/jinzhu/copier\"\n)\n\ntype LogService struct{}\n\nconst logs = \"https://resource.fit2cloud.com/installation-log.sh\"\n\ntype ILogService interface {\n\tCreateLoginLog(operation model.LoginLog) error\n\tPageLoginLog(ctx *gin.Context, search dto.SearchLgLogWithPage) (int64, interface{}, error)\n\n\tCreateOperationLog(operation *model.OperationLog) error\n\tPageOperationLog(search dto.SearchOpLogWithPage) (int64, interface{}, error)\n\n\tCleanLogs(logtype string) error\n}\n\nfunc NewILogService() ILogService {\n\treturn &LogService{}\n}\n\nfunc (u *LogService) CreateLoginLog(operation model.LoginLog) error {\n\treturn logRepo.CreateLoginLog(&operation)\n}\n\nfunc (u *LogService) PageLoginLog(ctx *gin.Context, req dto.SearchLgLogWithPage) (int64, interface{}, error) {\n\toptions := []global.DBOption{\n\t\trepo.WithOrderDesc(\"created_at\"),\n\t}\n\tif len(req.IP) != 0 {\n\t\toptions = append(options, logRepo.WithByIP(req.IP))\n\t}\n\tif len(req.Status) != 0 {\n\t\toptions = append(options, repo.WithByStatus(req.Status))\n\t}\n\ttotal, ops, err := logRepo.PageLoginLog(\n\t\treq.Page,\n\t\treq.PageSize,\n\t\toptions...,\n\t)\n\tvar dtoOps []dto.LoginLog\n\tgeoDB, _ := geo2.NewGeo()\n\tfor _, op := range ops {\n\t\tvar item dto.LoginLog\n\t\tif err := copier.Copy(&item, &op); err != nil {\n\t\t\treturn 0, nil, buserr.WithErr(\"ErrTransform\", err)\n\t\t}\n\t\tif geoDB != nil {\n\t\t\titem.Address, _ = geo2.GetIPLocation(geoDB, item.IP, common.GetLang(ctx))\n\t\t}\n\t\tdtoOps = append(dtoOps, item)\n\t}\n\treturn total, dtoOps, err\n}\n\nfunc (u *LogService) CreateOperationLog(operation *model.OperationLog) error {\n\treturn logRepo.CreateOperationLog(operation)\n}\n\nfunc (u *LogService) PageOperationLog(req dto.SearchOpLogWithPage) (int64, interface{}, error) {\n\toptions := []global.DBOption{\n\t\trepo.WithOrderDesc(\"created_at\"),\n\t\tlogRepo.WithByLikeOperation(req.Operation),\n\t}\n\tif len(req.Source) != 0 {\n\t\toptions = append(options, logRepo.WithBySource(req.Source))\n\t}\n\tif len(req.Status) != 0 {\n\t\toptions = append(options, repo.WithByStatus(req.Status))\n\t}\n\tif len(req.Node) != 0 {\n\t\toptions = append(options, repo.WithByNode(req.Node))\n\t}\n\n\ttotal, ops, err := logRepo.PageOperationLog(\n\t\treq.Page,\n\t\treq.PageSize,\n\t\toptions...,\n\t)\n\tvar dtoOps []dto.OperationLog\n\tfor _, op := range ops {\n\t\tvar item dto.OperationLog\n\t\tif err := copier.Copy(&item, &op); err != nil {\n\t\t\treturn 0, nil, buserr.WithErr(\"ErrTransform\", err)\n\t\t}\n\t\tdtoOps = append(dtoOps, item)\n\t}\n\treturn total, dtoOps, err\n}\n\nfunc (u *LogService) CleanLogs(logtype string) error {\n\tif logtype == \"operation\" {\n\t\treturn logRepo.CleanOperation()\n\t}\n\treturn logRepo.CleanLogin()\n}\n\nfunc writeLogs(version string) {\n\t_, _ = cmd.RunDefaultWithStdoutBashCf(\"curl -sfL %s | sh -s 1p upgrade %s\", logs, version)\n}\n"
  },
  {
    "path": "core/app/service/script_library.go",
    "content": "package service\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/app/task\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/req_helper\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/jinzhu/copier\"\n\t\"gopkg.in/yaml.v2\"\n)\n\ntype ScriptService struct{}\n\ntype IScriptService interface {\n\tRun()\n\tSearch(ctx *gin.Context, req dto.SearchPageWithGroup) (int64, interface{}, error)\n\tCreate(req dto.ScriptOperate) error\n\tUpdate(req dto.ScriptOperate) error\n\tDelete(ids dto.OperateByIDs) error\n\tSync(req dto.OperateByTaskID) error\n}\n\nfunc NewIScriptService() IScriptService {\n\treturn &ScriptService{}\n}\n\nfunc (u *ScriptService) Search(ctx *gin.Context, req dto.SearchPageWithGroup) (int64, interface{}, error) {\n\toptions := []global.DBOption{repo.WithOrderDesc(\"created_at\")}\n\tif len(req.Info) != 0 {\n\t\toptions = append(options, scriptRepo.WithByInfo(req.Info))\n\t}\n\tlist, err := scriptRepo.GetList(options...)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tgroups, _ := groupRepo.GetList(repo.WithByType(\"script\"))\n\tgroupMap := make(map[uint]string)\n\tfor _, item := range groups {\n\t\tgroupMap[item.ID] = item.Name\n\t}\n\tvar data []dto.ScriptInfo\n\tfor _, itemData := range list {\n\t\tvar item dto.ScriptInfo\n\t\tif err := copier.Copy(&item, &itemData); err != nil {\n\t\t\tglobal.LOG.Errorf(\"copy scripts to dto backup info failed, err: %v\", err)\n\t\t}\n\t\tif item.IsSystem {\n\t\t\tlang := strings.ToLower(common.GetLang(ctx))\n\t\t\tvar nameMap = make(map[string]string)\n\t\t\t_ = json.Unmarshal([]byte(item.Name), &nameMap)\n\t\t\tvar descriptionMap = make(map[string]string)\n\t\t\t_ = json.Unmarshal([]byte(item.Description), &descriptionMap)\n\t\t\tif val, ok := nameMap[lang]; ok {\n\t\t\t\titem.Name = val\n\t\t\t}\n\t\t\tif val, ok := descriptionMap[lang]; ok {\n\t\t\t\titem.Description = val\n\t\t\t}\n\t\t}\n\t\tmatchGroup := false\n\t\tgroupIDs := strings.Split(itemData.Groups, \",\")\n\t\tfor _, idItem := range groupIDs {\n\t\t\tid, _ := strconv.Atoi(idItem)\n\t\t\tif id == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif uint(id) == req.GroupID {\n\t\t\t\tmatchGroup = true\n\t\t\t}\n\t\t\titem.GroupList = append(item.GroupList, uint(id))\n\t\t\titem.GroupBelong = append(item.GroupBelong, groupMap[uint(id)])\n\t\t}\n\t\tif req.GroupID == 0 {\n\t\t\tdata = append(data, item)\n\t\t\tcontinue\n\t\t}\n\t\tif matchGroup {\n\t\t\tdata = append(data, item)\n\t\t}\n\t}\n\tvar records []dto.ScriptInfo\n\ttotal, start, end := len(data), (req.Page-1)*req.PageSize, req.Page*req.PageSize\n\tif start > total {\n\t\trecords = make([]dto.ScriptInfo, 0)\n\t} else {\n\t\tif end >= total {\n\t\t\tend = total\n\t\t}\n\t\trecords = data[start:end]\n\t}\n\treturn int64(total), records, nil\n}\n\nfunc (u *ScriptService) Create(req dto.ScriptOperate) error {\n\titemData, _ := scriptRepo.Get(repo.WithByName(req.Name))\n\tif itemData.ID != 0 {\n\t\treturn buserr.New(\"ErrRecordExist\")\n\t}\n\tif err := copier.Copy(&itemData, &req); err != nil {\n\t\treturn buserr.WithDetail(\"ErrStructTransform\", err.Error(), nil)\n\t}\n\tif err := scriptRepo.Create(&itemData); err != nil {\n\t\treturn err\n\t}\n\tif req.IsInteractive {\n\t\treturn nil\n\t}\n\tif err := xpack.Sync(constant.SyncScripts); err != nil {\n\t\tglobal.LOG.Errorf(\"sync scripts to node failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *ScriptService) Delete(req dto.OperateByIDs) error {\n\tfor _, item := range req.IDs {\n\t\tscriptItem, _ := scriptRepo.Get(repo.WithByID(item))\n\t\tif scriptItem.ID == 0 || scriptItem.IsSystem {\n\t\t\tcontinue\n\t\t}\n\t\tif err := scriptRepo.Delete(repo.WithByID(item)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := xpack.Sync(constant.SyncScripts); err != nil {\n\t\tglobal.LOG.Errorf(\"sync scripts to node failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *ScriptService) Update(req dto.ScriptOperate) error {\n\titemData, _ := scriptRepo.Get(repo.WithByID(req.ID))\n\tif itemData.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tupdateMap := make(map[string]interface{})\n\tupdateMap[\"name\"] = req.Name\n\tupdateMap[\"script\"] = req.Script\n\tupdateMap[\"groups\"] = req.Groups\n\tupdateMap[\"is_interactive\"] = req.IsInteractive\n\tupdateMap[\"description\"] = req.Description\n\tif err := scriptRepo.Update(req.ID, updateMap); err != nil {\n\t\treturn err\n\t}\n\tif err := xpack.Sync(constant.SyncScripts); err != nil {\n\t\tglobal.LOG.Errorf(\"sync scripts to node failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc StartSync() {\n\tif global.ScriptSyncJobID != 0 {\n\t\tglobal.Cron.Remove(global.ScriptSyncJobID)\n\t}\n\tservice := NewIScriptService()\n\tscriptSync, _ := repo.NewISettingRepo().GetValueByKey(\"ScriptSync\")\n\tif !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {\n\t\tminuteRand, err := rand.Int(rand.Reader, big.NewInt(60))\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"generate random minute failed: %v\", err)\n\t\t\tminuteRand = big.NewInt(0)\n\t\t}\n\t\thourRand, err := rand.Int(rand.Reader, big.NewInt(3))\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"generate random hour failed: %v\", err)\n\t\t\thourRand = big.NewInt(0)\n\t\t}\n\t\tid, err := global.Cron.AddJob(fmt.Sprintf(\"%v %v * * *\", minuteRand.Int64(), hourRand.Int64()), service)\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"[core] can not add script sync corn job: %s\", err.Error())\n\t\t}\n\t\tglobal.LOG.Info(\"add job for script library sync successful\")\n\t\tglobal.ScriptSyncJobID = id\n\t}\n}\nfunc (u *ScriptService) Run() {\n\tif err := u.Sync(dto.OperateByTaskID{}); err != nil {\n\t\tglobal.LOG.Errorf(\"sync scripts from remote failed, err: %v\", err)\n\t}\n}\n\nfunc LoadScriptInfo(id uint) (model.ScriptLibrary, error) {\n\treturn scriptRepo.Get(repo.WithByID(id))\n}\n\nfunc (u *ScriptService) Sync(req dto.OperateByTaskID) error {\n\tif global.CONF.Base.IsOffLine {\n\t\treturn nil\n\t}\n\tsyncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey(\"RemoteScriptLibrary\"), task.TaskSync, task.TaskScopeScript, req.TaskID, 0)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"create sync task failed %v\", err)\n\t\treturn err\n\t}\n\n\tsyncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey(\"RemoteScriptLibrary\"), task.TaskSync, task.TaskScopeScript), func(t *task.Task) (err error) {\n\t\tversionUrl := fmt.Sprintf(\"%s/scripts/version.txt\", global.ResourceURL())\n\t\t_, versionRes, err := req_helper.HandleRequestWithProxy(versionUrl, http.MethodGet, constant.TimeOut20s)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"load scripts version from remote failed, err: %v\", err)\n\t\t}\n\t\tvar scriptSetting model.Setting\n\t\t_ = global.DB.Where(\"key = ?\", \"ScriptVersion\").First(&scriptSetting).Error\n\t\tlocalVersion := strings.ReplaceAll(string(versionRes), \"\\n\", \"\")\n\t\tremoteVersion := strings.ReplaceAll(scriptSetting.Value, \"\\n\", \"\")\n\n\t\tif localVersion == remoteVersion {\n\t\t\tsyncTask.Log(i18n.GetMsgByKey(\"ScriptSyncSkip\"))\n\t\t\treturn nil\n\t\t}\n\n\t\tdataUrl := fmt.Sprintf(\"%s/scripts/data.yaml\", global.ResourceURL())\n\t\t_, dataRes, err := req_helper.HandleRequestWithProxy(dataUrl, http.MethodGet, constant.TimeOut20s)\n\t\tsyncTask.LogWithStatus(i18n.GetMsgByKey(\"DownloadData\"), err)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"load scripts data.yaml from remote failed, err: %v\", err)\n\t\t}\n\n\t\tvar scripts Scripts\n\t\tif err = yaml.Unmarshal(dataRes, &scripts); err != nil {\n\t\t\treturn fmt.Errorf(\"the format of data.yaml is err: %v\", err)\n\t\t}\n\n\t\ttmpDir := path.Join(global.CONF.Base.InstallDir, \"1panel/tmp/script\")\n\t\tif _, err := os.Stat(tmpDir); err != nil {\n\t\t\t_ = os.MkdirAll(tmpDir, 0755)\n\t\t}\n\t\tscriptsUrl := fmt.Sprintf(\"%s/scripts/scripts.tar.gz\", global.ResourceURL())\n\t\terr = files.DownloadFileWithProxyStream(scriptsUrl, tmpDir+\"/scripts.tar.gz\")\n\t\tsyncTask.LogWithStatus(i18n.GetMsgByKey(\"DownloadPackage\"), err)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"download scripts.tar.gz failed, err: %v\", err)\n\t\t}\n\n\t\tif err := files.HandleUnTar(tmpDir+\"/scripts.tar.gz\", tmpDir, \"\"); err != nil {\n\t\t\treturn fmt.Errorf(\"handle decompress scripts.tar.gz failed, err: %v\", err)\n\t\t}\n\t\tvar scriptsForDB []model.ScriptLibrary\n\t\tfor _, item := range scripts.Scripts.Sh {\n\t\t\titemName, _ := json.Marshal(item.Name)\n\t\t\titemDescription, _ := json.Marshal(item.Description)\n\t\t\tshell, _ := os.ReadFile(fmt.Sprintf(\"%s/scripts/sh/%s.sh\", tmpDir, item.Key))\n\t\t\tscriptItem := model.ScriptLibrary{\n\t\t\t\tName:          string(itemName),\n\t\t\t\tIsInteractive: item.Interactive,\n\t\t\t\tIsSystem:      true,\n\t\t\t\tScript:        string(shell),\n\t\t\t\tDescription:   string(itemDescription),\n\t\t\t}\n\t\t\tscriptsForDB = append(scriptsForDB, scriptItem)\n\t\t}\n\n\t\tsyncTask.Log(i18n.GetMsgByKey(\"AnalyticCompletion\"))\n\t\tif err := scriptRepo.SyncAll(scriptsForDB); err != nil {\n\t\t\treturn fmt.Errorf(\"sync script with db failed, err: %v\", err)\n\t\t}\n\t\t_ = os.RemoveAll(tmpDir)\n\t\tif err := global.DB.Model(&model.Setting{}).Where(\"key = ?\", \"ScriptVersion\").Updates(map[string]interface{}{\"value\": string(versionRes)}).Error; err != nil {\n\t\t\treturn fmt.Errorf(\"update script version in db failed, err: %v\", err)\n\t\t}\n\t\tif err := xpack.Sync(constant.SyncScripts); err != nil {\n\t\t\tglobal.LOG.Errorf(\"sync scripts to node failed, err: %v\", err)\n\t\t}\n\t\treturn nil\n\t}, nil)\n\n\tif err := syncTask.Execute(); err != nil {\n\t\treturn fmt.Errorf(\"sync scripts from remote failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\ntype Scripts struct {\n\tScripts ScriptDetail `json:\"scripts\"`\n}\n\ntype ScriptDetail struct {\n\tSh []ScriptHelper `json:\"sh\"`\n}\n\ntype ScriptHelper struct {\n\tKey         string            `json:\"key\"`\n\tSort        uint              `json:\"sort\"`\n\tGroups      string            `json:\"groups\"`\n\tName        map[string]string `json:\"name\"`\n\tInteractive bool              `json:\"interactive\"`\n\tDescription map[string]string `json:\"description\"`\n}\n"
  },
  {
    "path": "core/app/service/setting.go",
    "content": "package service\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/json\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/encrypt\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/firewall\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/passkey\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/req_helper/proxy_local\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n\t\"github.com/gin-gonic/gin\"\n\t\"golang.org/x/net/proxy\"\n)\n\ntype SettingService struct{}\n\ntype ISettingService interface {\n\tGetSettingInfo() (*dto.SettingInfo, error)\n\tLoadInterfaceAddr() ([]string, error)\n\tUpdate(key, value string) error\n\tUpdatePassword(c *gin.Context, old, new string) error\n\tUpdatePort(port uint) error\n\tUpdateBindInfo(req dto.BindInfo) error\n\tUpdateSSL(c *gin.Context, req dto.SSLUpdate) error\n\tLoadFromCert() (*dto.SSLInfo, error)\n\tHandlePasswordExpired(c *gin.Context, old, new string) error\n\tGenerateApiKey() (string, error)\n\tUpdateApiConfig(req dto.ApiInterfaceConfig) error\n\n\tUpdateProxy(req dto.ProxyUpdate) error\n\n\tGetTerminalInfo() (*dto.TerminalInfo, error)\n\tUpdateTerminal(req dto.TerminalInfo) error\n\n\tUpdateSystemSSL() error\n\tGenerateRSAKey() error\n\n\tUpdateAppstoreConfig(req dto.AppstoreUpdate) error\n\tGetAppstoreConfig() (*dto.AppstoreConfig, error)\n\tDefaultMenu() error\n\n\tGetMemo() (string, error)\n\tUpdateMemo(content string) error\n}\n\nfunc NewISettingService() ISettingService {\n\treturn &SettingService{}\n}\n\nfunc (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {\n\tsetting, err := settingRepo.List()\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tsettingMap := make(map[string]string)\n\tfor _, set := range setting {\n\t\tsettingMap[set.Key] = set.Value\n\t}\n\tif hideMenu, ok := settingMap[\"HideMenu\"]; ok && len(hideMenu) > 0 {\n\t\tvar menus []dto.ShowMenu\n\t\tif err := json.Unmarshal([]byte(hideMenu), &menus); err == nil {\n\t\t\tsortShowMenus(menus)\n\t\t\tif sortedBytes, err := json.Marshal(menus); err == nil {\n\t\t\t\tsettingMap[\"HideMenu\"] = string(sortedBytes)\n\t\t\t}\n\t\t}\n\t}\n\tvar info dto.SettingInfo\n\tarr, err := json.Marshal(settingMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := json.Unmarshal(arr, &info); err != nil {\n\t\treturn nil, err\n\t}\n\tif info.Edition == \"\" {\n\t\tinfo.Edition = \"cn\"\n\t\t_ = settingRepo.UpdateOrCreate(\"Edition\", info.Edition)\n\t}\n\tif info.ProxyPasswdKeep != constant.StatusEnable {\n\t\tinfo.ProxyPasswd = \"\"\n\t} else {\n\t\tinfo.ProxyPasswd, _ = encrypt.StringDecrypt(info.ProxyPasswd)\n\t}\n\n\treturn &info, err\n}\n\nfunc sortShowMenus(menus []dto.ShowMenu) {\n\tfor i := range menus {\n\t\tif len(menus[i].Children) > 0 {\n\t\t\tsortShowMenus(menus[i].Children)\n\t\t}\n\t}\n\tsort.SliceStable(menus, func(i, j int) bool {\n\t\tif menus[i].Sort == menus[j].Sort {\n\t\t\treturn menus[i].ID < menus[j].ID\n\t\t}\n\t\treturn menus[i].Sort < menus[j].Sort\n\t})\n}\n\nfunc (u *SettingService) Update(key, value string) error {\n\toldVal, err := settingRepo.Get(repo.WithByKey(key))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif oldVal.Value == value {\n\t\treturn nil\n\t}\n\tswitch key {\n\tcase \"AppStoreLastModified\":\n\t\texist, _ := settingRepo.Get(repo.WithByKey(\"AppStoreLastModified\"))\n\t\tif exist.ID == 0 {\n\t\t\t_ = settingRepo.Create(\"AppStoreLastModified\", value)\n\t\t\treturn nil\n\t\t}\n\tcase \"HideMenu\":\n\t\tvar menus []dto.ShowMenu\n\t\tif err := json.Unmarshal([]byte(value), &menus); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor i := 0; i < len(menus); i++ {\n\t\t\tif menus[i].Label == \"Home-Menu\" || menus[i].Label == \"App-Menu\" || menus[i].Label == \"Setting-Menu\" {\n\t\t\t\tmenus[i].IsShow = true\n\t\t\t}\n\t\t}\n\t\tmenuItem, err := json.Marshal(&menus)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvalue = string(menuItem)\n\t}\n\n\tif err := settingRepo.Update(key, value); err != nil {\n\t\treturn err\n\t}\n\n\tswitch key {\n\tcase \"ExpirationDays\":\n\t\ttimeout, err := strconv.Atoi(value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := settingRepo.Update(\"ExpirationTime\", time.Now().AddDate(0, 0, timeout).Format(constant.DateTimeLayout)); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"BindDomain\":\n\t\tif len(value) != 0 {\n\t\t\t_ = global.SESSION.Clean()\n\t\t}\n\t\tif err := u.clearPasskeySettings(); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"UserName\", \"Password\":\n\t\t_ = global.SESSION.Clean()\n\tcase \"Language\":\n\t\ti18n.SetCachedDBLanguage(value)\n\t\tif err := xpack.Sync(constant.SyncLanguage); err != nil {\n\t\t\tglobal.LOG.Errorf(\"sync language to node failed, err: %v\", err)\n\t\t}\n\tcase \"UpgradeBackupCopies\":\n\t\tdropBackupCopies()\n\tcase \"ScriptSync\":\n\t\tif value == constant.StatusEnable {\n\t\t\tStartSync()\n\t\t} else {\n\t\t\tglobal.Cron.Remove(global.ScriptSyncJobID)\n\t\t}\n\tcase \"Edition\":\n\t\tglobal.CONF.Base.Edition = value\n\t\tif err := xpack.Sync(constant.SyncEdition); err != nil {\n\t\t\tglobal.LOG.Errorf(\"sync edition to node failed, err: %v\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (u *SettingService) LoadInterfaceAddr() ([]string, error) {\n\taddrMap := make(map[string]struct{})\n\taddrs, err := net.InterfaceAddrs()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, addr := range addrs {\n\t\tipNet, ok := addr.(*net.IPNet)\n\t\tif ok && ipNet.IP.To16() != nil {\n\t\t\taddrMap[ipNet.IP.String()] = struct{}{}\n\t\t}\n\t}\n\tvar data []string\n\tfor key := range addrMap {\n\t\tdata = append(data, key)\n\t}\n\treturn data, nil\n}\n\nfunc (u *SettingService) UpdateBindInfo(req dto.BindInfo) error {\n\tif err := settingRepo.Update(\"Ipv6\", req.Ipv6); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.Update(\"BindAddress\", req.BindAddress); err != nil {\n\t\treturn err\n\t}\n\tgo func() {\n\t\ttime.Sleep(1 * time.Second)\n\t\tcontroller.RestartPanel(true, false, false)\n\t}()\n\treturn nil\n}\n\nfunc (u *SettingService) UpdateProxy(req dto.ProxyUpdate) error {\n\tif req.ProxyType == \"https\" || req.ProxyType == \"http\" {\n\t\treq.ProxyUrl = req.ProxyType + \"://\" + req.ProxyUrl\n\t}\n\tif err := checkProxy(req); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.Update(\"ProxyUrl\", req.ProxyUrl); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.Update(\"ProxyType\", req.ProxyType); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.Update(\"ProxyPort\", req.ProxyPort); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.Update(\"ProxyUser\", req.ProxyUser); err != nil {\n\t\treturn err\n\t}\n\tpass, _ := encrypt.StringEncrypt(req.ProxyPasswd)\n\tif err := settingRepo.Update(\"ProxyPasswd\", pass); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.Update(\"ProxyPasswdKeep\", req.ProxyPasswdKeep); err != nil {\n\t\treturn err\n\t}\n\tif err := xpack.ProxyDocker(loadDockerProxy(req)); err != nil {\n\t\treturn err\n\t}\n\tsyncScope := constant.SyncSystemProxy\n\tif req.WithDockerRestart {\n\t\tsyncScope = constant.SyncSystemProxyWithRestartDocker\n\t}\n\tif err := xpack.Sync(syncScope); err != nil {\n\t\tglobal.LOG.Errorf(\"sync proxy to node failed, err: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *SettingService) UpdatePort(port uint) error {\n\tif common.ScanPort(int(port)) {\n\t\treturn buserr.WithDetail(\"ErrPortInUsed\", port, nil)\n\t}\n\toldPort, err := settingRepo.Get(repo.WithByKey(\"ServerPort\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif oldPort.Value == fmt.Sprintf(\"%v\", port) {\n\t\treturn nil\n\t}\n\tif err := firewall.UpdatePort(oldPort.Value, fmt.Sprintf(\"%v\", port)); err != nil {\n\t\treturn err\n\t}\n\n\tif err := settingRepo.Update(\"ServerPort\", strconv.Itoa(int(port))); err != nil {\n\t\treturn err\n\t}\n\tif err := u.clearPasskeySettings(); err != nil {\n\t\treturn err\n\t}\n\tgo func() {\n\t\ttime.Sleep(1 * time.Second)\n\t\tcontroller.RestartPanel(true, false, false)\n\t}()\n\treturn nil\n}\n\nfunc (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {\n\tsecretDir := path.Join(global.CONF.Base.InstallDir, \"1panel/secret\")\n\tif req.SSL == constant.StatusDisable {\n\t\tc.SetCookie(constant.SessionName, \"\", -1, \"/\", \"\", false, true)\n\t\tif err := settingRepo.Update(\"SSL\", constant.StatusDisable); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := settingRepo.Update(\"SSLType\", \"self\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_ = os.Remove(path.Join(secretDir, \"server.crt\"))\n\t\t_ = os.Remove(path.Join(secretDir, \"server.key\"))\n\t\tgo func() {\n\t\t\ttime.Sleep(1 * time.Second)\n\t\t\tcontroller.RestartPanel(true, false, false)\n\t\t}()\n\t\treturn nil\n\t}\n\tif _, err := os.Stat(secretDir); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(secretDir, os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := settingRepo.Update(\"SSLType\", req.SSLType); err != nil {\n\t\treturn err\n\t}\n\tvar (\n\t\tsecret string\n\t\tkey    string\n\t)\n\n\tswitch req.SSLType {\n\tcase \"import-paste\":\n\t\tsecret = req.Cert\n\t\tkey = req.Key\n\tcase \"import-local\":\n\t\tkeyFile, err := os.ReadFile(req.Key)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tkey = string(keyFile)\n\t\tcertFile, err := os.ReadFile(req.Cert)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsecret = string(certFile)\n\tcase \"select\":\n\t\tssl, err := agentRepo.GetWebsiteSSL(repo.WithByID(req.SSLID))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsecret = ssl.Pem\n\t\tkey = ssl.PrivateKey\n\t\tif err := settingRepo.Update(\"SSLID\", strconv.Itoa(int(req.SSLID))); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"self\":\n\t\tca, err := agentRepo.GetCA(repo.WithByName(\"1Panel\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tparams := make(map[string]interface{})\n\t\tparams[\"domains\"] = req.Domain\n\t\tparams[\"time\"] = 10\n\t\tparams[\"unit\"] = \"year\"\n\t\tparams[\"keyType\"] = \"P256\"\n\t\tparams[\"id\"] = ca.ID\n\t\tjsonData, err := json.Marshal(params)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres, err := proxy_local.NewLocalClient(\"/api/v2/websites/ca/obtain\", http.MethodPost, bytes.NewReader(jsonData), nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tjsonBytes, err := json.Marshal(res)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar ssl model.WebsiteSSL\n\t\tif err := json.Unmarshal(jsonBytes, &ssl); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsecret = ssl.Pem\n\t\tkey = ssl.PrivateKey\n\t\tif err := settingRepo.Update(\"SSLID\", strconv.Itoa(int(ssl.ID))); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif err := os.WriteFile(path.Join(secretDir, \"server.crt.tmp\"), []byte(secret), 0600); err != nil {\n\t\treturn err\n\t}\n\tif err := os.WriteFile(path.Join(secretDir, \"server.key.tmp\"), []byte(key), 0600); err != nil {\n\t\treturn err\n\t}\n\tif err := checkCertValid(); err != nil {\n\t\treturn err\n\t}\n\tif err := os.Rename(path.Join(secretDir, \"server.crt.tmp\"), path.Join(secretDir, \"server.crt\")); err != nil {\n\t\treturn err\n\t}\n\tif err := os.Rename(path.Join(secretDir, \"server.key.tmp\"), path.Join(secretDir, \"server.key\")); err != nil {\n\t\treturn err\n\t}\n\tstatus, _ := settingRepo.GetValueByKey(\"SSL\")\n\tif req.SSL != status {\n\t\tgo func() {\n\t\t\ttime.Sleep(1 * time.Second)\n\t\t\tcontroller.RestartPanel(true, false, false)\n\t\t}()\n\t}\n\tif err := settingRepo.Update(\"SSL\", req.SSL); err != nil {\n\t\treturn err\n\t}\n\treturn u.UpdateSystemSSL()\n}\n\nfunc (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {\n\tssl, err := settingRepo.Get(repo.WithByKey(\"SSL\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif ssl.Value == constant.StatusDisable {\n\t\treturn &dto.SSLInfo{}, nil\n\t}\n\tsslType, err := settingRepo.Get(repo.WithByKey(\"SSLType\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar data dto.SSLInfo\n\tswitch sslType.Value {\n\tcase \"self\":\n\t\tdata, err = loadInfoFromCert()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase \"import-paste\", \"import-local\":\n\t\tdata, err = loadInfoFromCert()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif _, err := os.Stat(path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.crt\")); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"load server.crt file failed, err: %v\", err)\n\t\t}\n\t\tcertFile, _ := os.ReadFile(path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.crt\"))\n\t\tdata.Cert = string(certFile)\n\n\t\tif _, err := os.Stat(path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.key\")); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"load server.key file failed, err: %v\", err)\n\t\t}\n\t\tkeyFile, _ := os.ReadFile(path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.key\"))\n\t\tdata.Key = string(keyFile)\n\tcase \"select\":\n\t\tsslID, err := settingRepo.Get(repo.WithByKey(\"SSLID\"))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tid, _ := strconv.Atoi(sslID.Value)\n\t\tssl, err := agentRepo.GetWebsiteSSL(repo.WithByID(uint(id)))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdata.Domain = ssl.PrimaryDomain\n\t\tdata.SSLID = uint(id)\n\t\tdata.Timeout = ssl.ExpireDate.Format(constant.DateTimeLayout)\n\t}\n\treturn &data, nil\n}\n\nfunc (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error {\n\tsetting, err := settingRepo.Get(repo.WithByKey(\"Password\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tpasswordFromDB, err := encrypt.StringDecrypt(setting.Value)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif passwordFromDB == old {\n\t\tnewPassword, err := encrypt.StringEncrypt(new)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := settingRepo.Update(\"Password\", newPassword); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\texpiredSetting, err := settingRepo.Get(repo.WithByKey(\"ExpirationDays\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttimeout, _ := strconv.Atoi(expiredSetting.Value)\n\t\tif err := settingRepo.Update(\"ExpirationTime\", time.Now().AddDate(0, 0, timeout).Format(constant.DateTimeLayout)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\treturn buserr.New(\"ErrInitialPassword\")\n}\n\nfunc (u *SettingService) GetTerminalInfo() (*dto.TerminalInfo, error) {\n\tsetting, err := settingRepo.List()\n\tif err != nil {\n\t\treturn nil, buserr.New(\"ErrRecordNotFound\")\n\t}\n\tsettingMap := make(map[string]string)\n\tfor _, set := range setting {\n\t\tsettingMap[set.Key] = set.Value\n\t}\n\tvar info dto.TerminalInfo\n\tarr, err := json.Marshal(settingMap)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := json.Unmarshal(arr, &info); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &info, err\n}\nfunc (u *SettingService) UpdateTerminal(req dto.TerminalInfo) error {\n\tif err := settingRepo.UpdateOrCreate(\"LineHeight\", req.LineHeight); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"LetterSpacing\", req.LetterSpacing); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"FontSize\", req.FontSize); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"FontFamily\", req.FontFamily); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"CursorBlink\", req.CursorBlink); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"BackgroundColor\", req.BackgroundColor); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"ForegroundColor\", req.ForegroundColor); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"CursorBlink\", req.CursorBlink); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"CursorStyle\", req.CursorStyle); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"Scrollback\", req.Scrollback); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.UpdateOrCreate(\"ScrollSensitivity\", req.ScrollSensitivity); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {\n\tif err := u.HandlePasswordExpired(c, old, new); err != nil {\n\t\treturn err\n\t}\n\t_ = global.SESSION.Clean()\n\treturn nil\n}\n\nfunc (u *SettingService) clearPasskeySettings() error {\n\tif err := settingRepo.Update(passkey.PasskeyUserIDSettingKey, \"\"); err != nil {\n\t\treturn err\n\t}\n\tif err := settingRepo.Update(passkey.PasskeyCredentialSettingKey, \"\"); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *SettingService) UpdateSystemSSL() error {\n\tcertPath := path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.crt\")\n\tkeyPath := path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.key\")\n\tcertificate, err := os.ReadFile(certPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tkey, err := os.ReadFile(keyPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcert, err := tls.X509KeyPair(certificate, key)\n\tif err != nil {\n\t\treturn err\n\t}\n\tconstant.CertStore.Store(&cert)\n\treturn nil\n}\n\nfunc (u *SettingService) GenerateApiKey() (string, error) {\n\tapiKey := common.RandStr(32)\n\tif err := settingRepo.Update(\"ApiKey\", apiKey); err != nil {\n\t\treturn global.Api.ApiKey, err\n\t}\n\tglobal.Api.ApiKey = apiKey\n\treturn apiKey, nil\n}\n\nfunc (u *SettingService) UpdateApiConfig(req dto.ApiInterfaceConfig) error {\n\tif err := settingRepo.UpdateOrCreate(\"ApiInterfaceStatus\", req.ApiInterfaceStatus); err != nil {\n\t\treturn err\n\t}\n\tglobal.Api.ApiInterfaceStatus = req.ApiInterfaceStatus\n\tif err := settingRepo.UpdateOrCreate(\"ApiKey\", req.ApiKey); err != nil {\n\t\treturn err\n\t}\n\tglobal.Api.ApiKey = req.ApiKey\n\tif err := settingRepo.UpdateOrCreate(\"IpWhiteList\", req.IpWhiteList); err != nil {\n\t\treturn err\n\t}\n\tglobal.Api.IpWhiteList = req.IpWhiteList\n\tif err := settingRepo.UpdateOrCreate(\"ApiKeyValidityTime\", req.ApiKeyValidityTime); err != nil {\n\t\treturn err\n\t}\n\tglobal.Api.ApiKeyValidityTime = req.ApiKeyValidityTime\n\treturn nil\n}\n\nfunc loadInfoFromCert() (dto.SSLInfo, error) {\n\tvar info dto.SSLInfo\n\tcertFile := path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.crt\")\n\tif _, err := os.Stat(certFile); err != nil {\n\t\treturn info, err\n\t}\n\tcertData, err := os.ReadFile(certFile)\n\tif err != nil {\n\t\treturn info, err\n\t}\n\tcertBlock, _ := pem.Decode(certData)\n\tif certBlock == nil {\n\t\treturn info, err\n\t}\n\tcertObj, err := x509.ParseCertificate(certBlock.Bytes)\n\tif err != nil {\n\t\treturn info, err\n\t}\n\tvar domains []string\n\tif len(certObj.IPAddresses) != 0 {\n\t\tfor _, ip := range certObj.IPAddresses {\n\t\t\tdomains = append(domains, ip.String())\n\t\t}\n\t}\n\tif len(certObj.DNSNames) != 0 {\n\t\tdomains = append(domains, certObj.DNSNames...)\n\t}\n\treturn dto.SSLInfo{\n\t\tDomain:   strings.Join(domains, \",\"),\n\t\tTimeout:  certObj.NotAfter.Format(constant.DateTimeLayout),\n\t\tRootPath: path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.crt\"),\n\t}, nil\n}\n\nfunc checkCertValid() error {\n\tcertificate, err := os.ReadFile(path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.crt.tmp\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tkey, err := os.ReadFile(path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.key.tmp\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = tls.X509KeyPair(certificate, key); err != nil {\n\t\treturn err\n\t}\n\tcertBlock, _ := pem.Decode(certificate)\n\tif certBlock == nil {\n\t\treturn err\n\t}\n\tif _, err := x509.ParseCertificate(certBlock.Bytes); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (u *SettingService) GenerateRSAKey() error {\n\tpriKey, _ := settingRepo.Get(repo.WithByKey(\"PASSWORD_PRIVATE_KEY\"))\n\tpubKey, _ := settingRepo.Get(repo.WithByKey(\"PASSWORD_PUBLIC_KEY\"))\n\tif priKey.Value != \"\" && pubKey.Value != \"\" {\n\t\treturn nil\n\t}\n\tprivateKey, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\treturn err\n\t}\n\tprivateKeyPEM := encrypt.ExportPrivateKeyToPEM(privateKey)\n\tpublicKeyPEM, err := encrypt.ExportPublicKeyToPEM(&privateKey.PublicKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = settingRepo.UpdateOrCreate(\"PASSWORD_PRIVATE_KEY\", privateKeyPEM)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = settingRepo.UpdateOrCreate(\"PASSWORD_PUBLIC_KEY\", publicKeyPEM)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *SettingService) UpdateAppstoreConfig(req dto.AppstoreUpdate) error {\n\treturn settingRepo.UpdateOrCreate(req.Scope, req.Status)\n}\n\nfunc (u *SettingService) GetAppstoreConfig() (*dto.AppstoreConfig, error) {\n\tres := &dto.AppstoreConfig{}\n\tres.UninstallDeleteImage, _ = settingRepo.GetValueByKey(\"UninstallDeleteImage\")\n\tif res.UninstallDeleteImage == \"\" {\n\t\tres.UninstallDeleteImage = \"False\"\n\t}\n\tres.UpgradeBackup, _ = settingRepo.GetValueByKey(\"UpgradeBackup\")\n\tif res.UpgradeBackup == \"\" {\n\t\tres.UpgradeBackup = \"False\"\n\t}\n\tres.UninstallDeleteBackup, _ = settingRepo.GetValueByKey(\"UninstallDeleteBackup\")\n\tif res.UninstallDeleteBackup == \"\" {\n\t\tres.UninstallDeleteBackup = \"False\"\n\t}\n\treturn res, nil\n}\n\nfunc loadDockerProxy(req dto.ProxyUpdate) string {\n\tif req.ProxyType == \"\" || req.ProxyType == \"close\" || !req.ProxyDocker {\n\t\treturn \"\"\n\t}\n\tvar account string\n\tif req.ProxyUser != \"\" {\n\t\taccount = req.ProxyUser\n\t\tif req.ProxyPasswd != \"\" {\n\t\t\taccount += \":\" + req.ProxyPasswd\n\t\t}\n\t\taccount += \"@\"\n\t}\n\n\treturn fmt.Sprintf(\"%s://%s%s:%s\", req.ProxyType, account, strings.ReplaceAll(req.ProxyUrl, req.ProxyType+\"://\", \"\"), req.ProxyPort)\n}\n\nfunc checkProxy(req dto.ProxyUpdate) error {\n\tvar transport http.Transport\n\tproxyItem := fmt.Sprintf(\"%s:%s\", req.ProxyUrl, req.ProxyPort)\n\tswitch req.ProxyType {\n\tcase \"http\", \"https\":\n\t\tproxyURL, err := url.Parse(proxyItem)\n\t\tif err != nil {\n\t\t\treturn buserr.WithErr(\"ErrProxySetting\", fmt.Errorf(\"parse url %s failed, err: %v\", proxyItem, err))\n\t\t}\n\t\tif len(req.ProxyUser) != 0 {\n\t\t\tproxyURL.User = url.UserPassword(req.ProxyUser, req.ProxyPasswd)\n\t\t}\n\t\ttransport = http.Transport{Proxy: http.ProxyURL(proxyURL), TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}\n\tcase \"socks5\":\n\t\tvar auth *proxy.Auth\n\t\tif len(req.ProxyUser) == 0 {\n\t\t\tauth = nil\n\t\t} else {\n\t\t\tauth = &proxy.Auth{User: req.ProxyUser, Password: req.ProxyPasswd}\n\t\t}\n\t\tdialer, err := proxy.SOCKS5(\"tcp\", proxyItem, auth, proxy.Direct)\n\t\tif err != nil {\n\t\t\treturn buserr.WithErr(\"ErrProxySetting\", fmt.Errorf(\"new socks5 proxy failed, err: %v\", err))\n\t\t}\n\t\tdialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\treturn dialer.Dial(network, addr)\n\t\t}\n\t\ttransport = http.Transport{DialContext: dialContext}\n\tcase \"\", \"close\":\n\tdefault:\n\t\treturn buserr.WithDetail(\"ErrNotSupportType\", req.ProxyType, nil)\n\t}\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"handle request failed, error message: %v\", r)\n\t\t\treturn\n\t\t}\n\t}()\n\n\tclient := http.Client{Timeout: 3 * time.Second, Transport: &transport}\n\tctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)\n\tdefer cancel()\n\trequest, err := http.NewRequestWithContext(ctx, http.MethodGet, \"https://1panel.cn/\", nil)\n\tif err != nil {\n\t\treturn buserr.WithErr(\"ErrProxySetting\", err)\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, err := client.Do(request)\n\tif err != nil {\n\t\treturn buserr.WithErr(\"ErrProxySetting\", err)\n\t}\n\tif _, err := io.ReadAll(resp.Body); err != nil {\n\t\treturn buserr.WithErr(\"ErrProxySetting\", err)\n\t}\n\tdefer resp.Body.Close()\n\treturn nil\n}\n\nfunc (u *SettingService) DefaultMenu() error {\n\treturn settingRepo.DefaultMenu()\n}\n\nfunc (u *SettingService) GetMemo() (string, error) {\n\tmemo, err := settingRepo.GetValueByKey(\"DashboardMemo\")\n\tif err != nil {\n\t\treturn \"\", nil\n\t}\n\treturn memo, nil\n}\n\nfunc (u *SettingService) UpdateMemo(content string) error {\n\treturn settingRepo.UpdateOrCreate(\"DashboardMemo\", content)\n}\n"
  },
  {
    "path": "core/app/service/upgrade.go",
    "content": "package service\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/files\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/req_helper\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n)\n\ntype serviceInfo struct {\n\tbasePath     string\n\tcoreName     string\n\tagentName    string\n\tselCoreName  string\n\tselAgentName string\n}\n\nfunc loadServiceInfo() (serviceInfo, error) {\n\tbasePath, err := controller.GetServicePath(\"\")\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"get service path failed: %v\", err)\n\t\treturn serviceInfo{}, err\n\t}\n\tcoreName, err := controller.LoadServiceName(\"1panel-core\")\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load core service name failed: %v\", err)\n\t\treturn serviceInfo{}, err\n\t}\n\tagentName, err := controller.LoadServiceName(\"1panel-agent\")\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load agent service name failed: %v\", err)\n\t\treturn serviceInfo{}, err\n\t}\n\tselCoreName, err := controller.SelectInitScript(\"1panel-core\")\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"select core init script failed: %v\", err)\n\t\treturn serviceInfo{}, err\n\t}\n\tselAgentName, err := controller.SelectInitScript(\"1panel-agent\")\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"select agent init script failed: %v\", err)\n\t\treturn serviceInfo{}, err\n\t}\n\treturn serviceInfo{\n\t\tbasePath:     basePath,\n\t\tcoreName:     coreName,\n\t\tagentName:    agentName,\n\t\tselCoreName:  selCoreName,\n\t\tselAgentName: selAgentName,\n\t}, nil\n}\n\ntype UpgradeService struct{}\n\ntype IUpgradeService interface {\n\tUpgrade(req dto.Upgrade) error\n\tRollback(req dto.OperateByID) error\n\tLoadNotes(req dto.Upgrade) (string, error)\n\tSearchUpgrade() (*dto.UpgradeInfo, error)\n\tLoadRelease() ([]dto.ReleasesNotes, error)\n}\n\nfunc NewIUpgradeService() IUpgradeService {\n\treturn &UpgradeService{}\n}\n\nfunc (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {\n\tif global.CONF.Base.IsOffLine {\n\t\treturn &dto.UpgradeInfo{}, nil\n\t}\n\tvar upgrade dto.UpgradeInfo\n\tcurrentVersion, err := settingRepo.Get(repo.WithByKey(\"SystemVersion\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tDeveloperMode, err := settingRepo.Get(repo.WithByKey(\"DeveloperMode\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tupgrade.TestVersion, upgrade.NewVersion, upgrade.LatestVersion = u.loadVersionByMode(DeveloperMode.Value, currentVersion.Value)\n\tvar itemVersion string\n\tif len(upgrade.NewVersion) != 0 {\n\t\titemVersion = upgrade.NewVersion\n\t}\n\tif (global.CONF.Base.Mode == \"dev\" || DeveloperMode.Value == constant.StatusEnable) && len(upgrade.TestVersion) != 0 {\n\t\titemVersion = upgrade.TestVersion\n\t}\n\tif len(upgrade.LatestVersion) != 0 {\n\t\titemVersion = upgrade.LatestVersion\n\t}\n\tif len(itemVersion) == 0 {\n\t\treturn &upgrade, nil\n\t}\n\tmode := global.CONF.Base.Mode\n\tif strings.Contains(itemVersion, \"beta\") {\n\t\tmode = \"beta\"\n\t}\n\tif strings.HasPrefix(upgrade.TestVersion, upgrade.LatestVersion+\"-beta\") {\n\t\tupgrade.TestVersion = \"\"\n\t}\n\tnotes, err := u.loadReleaseNotes(fmt.Sprintf(\"%s/%s/%s/release/1panel-%s-release-notes\", global.RepoURL(), mode, itemVersion, itemVersion))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load releases-notes of version %s failed, err: %v\", itemVersion, err)\n\t}\n\tupgrade.ReleaseNote = notes\n\treturn &upgrade, nil\n}\n\nfunc (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {\n\tmode := global.CONF.Base.Mode\n\tif strings.Contains(req.Version, \"beta\") {\n\t\tmode = \"beta\"\n\t}\n\tnotes, err := u.loadReleaseNotes(fmt.Sprintf(\"%s/%s/%s/release/1panel-%s-release-notes\", global.RepoURL(), mode, req.Version, req.Version))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"load releases-notes of version %s failed, err: %v\", req.Version, err)\n\t}\n\treturn notes, nil\n}\n\nfunc (u *UpgradeService) Upgrade(req dto.Upgrade) error {\n\tglobal.LOG.Info(\"start to upgrade now...\")\n\tbaseDir := path.Join(global.CONF.Base.InstallDir, fmt.Sprintf(\"1panel/tmp/upgrade/%s\", req.Version))\n\tdownloadDir := path.Join(baseDir, \"downloads\")\n\t_ = os.RemoveAll(baseDir)\n\toriginalDir := path.Join(baseDir, \"original\")\n\tif err := os.MkdirAll(downloadDir, os.ModePerm); err != nil {\n\t\treturn err\n\t}\n\tif err := os.MkdirAll(originalDir, os.ModePerm); err != nil {\n\t\treturn err\n\t}\n\titemArch, err := loadArch()\n\tif err != nil {\n\t\treturn err\n\t}\n\tsvcInfo, err := loadServiceInfo()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmode := global.CONF.Base.Mode\n\tif strings.Contains(req.Version, \"beta\") {\n\t\tmode = \"beta\"\n\t}\n\tdownloadPath := fmt.Sprintf(\"%s/%s/%s/release\", global.RepoURL(), mode, req.Version)\n\tfileName := fmt.Sprintf(\"1panel-%s-%s-%s.tar.gz\", req.Version, \"linux\", itemArch)\n\t_ = settingRepo.Update(\"SystemStatus\", \"Upgrading\")\n\tgo func() {\n\t\toldLang := common.LoadParams(\"LANGUAGE\")\n\t\tif err := files.DownloadFileWithProxyStream(downloadPath+\"/\"+fileName, downloadDir+\"/\"+fileName); err != nil {\n\t\t\tglobal.LOG.Errorf(\"download service file failed, err: %v\", err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\treturn\n\t\t}\n\t\tglobal.LOG.Info(\"download all file successful!\")\n\t\tdefer func() {\n\t\t\t_ = os.Remove(downloadDir)\n\t\t}()\n\t\tif err := files.HandleUnTar(downloadDir+\"/\"+fileName, downloadDir, \"\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"decompress file failed, err: %v\", err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\treturn\n\t\t}\n\t\ttmpDir := downloadDir + \"/\" + strings.ReplaceAll(fileName, \".tar.gz\", \"\")\n\n\t\tif err := u.handleBackup(originalDir, svcInfo); err != nil {\n\t\t\tglobal.LOG.Errorf(\"handle backup original file failed, err: %v\", err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\treturn\n\t\t}\n\t\titemLog := model.UpgradeLog{NodeID: 0, OldVersion: global.CONF.Base.Version, NewVersion: req.Version, BackupFile: baseDir}\n\t\t_ = upgradeLogRepo.Create(&itemLog)\n\n\t\tglobal.LOG.Info(\"backup original data successful, now start to upgrade!\")\n\n\t\tif err := files.CopyFileWithRename(path.Join(tmpDir, \"1panel-core\"), \"/usr/local/bin/1panel-core\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"upgrade 1panel-core failed, err: %v\", err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\tu.handleRollback(originalDir, 1, svcInfo)\n\t\t\treturn\n\t\t}\n\t\tif err := files.CopyFileWithRename(path.Join(tmpDir, \"1panel-agent\"), \"/usr/local/bin/1panel-agent\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"upgrade 1panel-agent failed, err: %v\", err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\tu.handleRollback(originalDir, 1, svcInfo)\n\t\t\treturn\n\t\t}\n\n\t\tif err := files.CopyItem(false, true, path.Join(tmpDir, \"1pctl\"), \"/usr/local/bin\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"upgrade 1pctl failed, err: %v\", err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\tu.handleRollback(originalDir, 2, svcInfo)\n\t\t\treturn\n\t\t}\n\t\tif _, err := cmd.RunDefaultWithStdoutBashCf(\"sed -i -e 's#BASE_DIR=.*#BASE_DIR=%s#g' /usr/local/bin/1pctl\", global.CONF.Base.InstallDir); err != nil {\n\t\t\tglobal.LOG.Errorf(\"upgrade basedir in 1pctl failed, err: %v\", err)\n\t\t\tu.handleRollback(originalDir, 2, svcInfo)\n\t\t\treturn\n\t\t}\n\t\tif _, err := cmd.RunDefaultWithStdoutBashCf(\"sed -i -e 's#LANGUAGE=.*#LANGUAGE=%s#g' /usr/local/bin/1pctl\", oldLang); err != nil {\n\t\t\tglobal.LOG.Errorf(\"upgrade basedir in 1pctl failed, err: %v\", err)\n\t\t\tu.handleRollback(originalDir, 2, svcInfo)\n\t\t\treturn\n\t\t}\n\t\tinitScriptPath := path.Join(tmpDir, \"initscript\")\n\n\t\tif err := files.CopyItem(false, true, path.Join(initScriptPath, svcInfo.selCoreName), svcInfo.basePath); err != nil {\n\t\t\tglobal.LOG.Errorf(\"upgrade %s failed, err: %v\", svcInfo.coreName, err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\tu.handleRollback(originalDir, 3, svcInfo)\n\t\t\treturn\n\t\t}\n\t\tif err := files.CopyItem(false, true, path.Join(initScriptPath, svcInfo.selAgentName), svcInfo.basePath); err != nil {\n\t\t\tglobal.LOG.Errorf(\"upgrade %s failed, err: %v\", svcInfo.agentName, err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\tu.handleRollback(originalDir, 3, svcInfo)\n\t\t\treturn\n\t\t}\n\n\t\tif err := files.CopyItem(true, true, path.Join(tmpDir, \"lang\"), \"/usr/local/bin\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"Update language files failed: %v\", err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\tu.handleRollback(originalDir, 4, svcInfo)\n\t\t}\n\t\tif err := files.CopyItem(false, true, path.Join(tmpDir, \"GeoIP.mmdb\"), path.Join(global.CONF.Base.InstallDir, \"1panel/geo\")); err != nil {\n\t\t\tglobal.LOG.Warnf(\"Update GeoIP database failed: %v\", err)\n\t\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\t\t\tu.handleRollback(originalDir, 4, svcInfo)\n\t\t}\n\n\t\tglobal.LOG.Info(\"upgrade successful!\")\n\t\tdropBackupCopies()\n\t\txpack.AutoUpgradeWithMaster()\n\t\tgo writeLogs(req.Version)\n\t\t_ = settingRepo.Update(\"SystemVersion\", req.Version)\n\t\t_ = global.AgentDB.Model(&model.Setting{}).Where(\"key = ?\", \"SystemVersion\").Updates(map[string]interface{}{\"value\": req.Version}).Error\n\t\tglobal.CONF.Base.Version = req.Version\n\t\t_ = os.RemoveAll(downloadDir)\n\t\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\n\t\tcontroller.RestartPanel(true, true, true)\n\t}()\n\treturn nil\n}\n\nfunc (u *UpgradeService) Rollback(req dto.OperateByID) error {\n\tlog, _ := upgradeLogRepo.Get(repo.WithByID(req.ID))\n\tif log.ID == 0 {\n\t\treturn buserr.New(\"ErrRecordNotFound\")\n\t}\n\tsvcInfo, err := loadServiceInfo()\n\tif err != nil {\n\t\treturn err\n\t}\n\tu.handleRollback(log.BackupFile, 3, svcInfo)\n\treturn nil\n}\n\ntype noteHelper struct {\n\tDocs []noteDetailHelper `json:\"docs\"`\n}\ntype noteDetailHelper struct {\n\tLocation string `json:\"location\"`\n\tText     string `json:\"text\"`\n\tTitle    string `json:\"title\"`\n}\n\nfunc (u *UpgradeService) LoadRelease() ([]dto.ReleasesNotes, error) {\n\tdocSource, _ := settingRepo.GetValueByKey(\"DocSource\")\n\tlang, _ := settingRepo.GetValueByKey(\"Language\")\n\tvar notes []dto.ReleasesNotes\n\turl := \"https://1panel.cn/docs/v2/search/search_index.json\"\n\tuseIntlDocs := false\n\tlang = strings.ToLower(strings.TrimSpace(lang))\n\tif docSource == \"withByRegion\" {\n\t\tuseIntlDocs = global.CONF.Base.Edition == \"intl\"\n\t} else {\n\t\tuseIntlDocs = lang != \"zh\"\n\t}\n\tif useIntlDocs {\n\t\turl = \"https://docs.1panel.pro/v2/search/search_index.json\"\n\t}\n\tresp, err := req_helper.HandleGet(url)\n\tif err != nil {\n\t\treturn notes, err\n\t}\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn notes, err\n\t}\n\tdefer resp.Body.Close()\n\tvar nodeItem noteHelper\n\tif err := json.Unmarshal(body, &nodeItem); err != nil {\n\t\treturn notes, err\n\t}\n\tfor _, item := range nodeItem.Docs {\n\t\tif !strings.HasPrefix(item.Location, \"changelog/#v\") {\n\t\t\tcontinue\n\t\t}\n\t\titemNote := analyzeDoc(item.Title, item.Text)\n\t\tif len(itemNote.CreatedAt) != 0 {\n\t\t\tnotes = append(notes, analyzeDoc(item.Title, item.Text))\n\t\t}\n\t}\n\n\treturn notes, nil\n}\n\nfunc analyzeDoc(version, content string) dto.ReleasesNotes {\n\tvar item dto.ReleasesNotes\n\tparts := strings.Split(content, \"<p>\")\n\tif len(parts) < 3 {\n\t\treturn item\n\t}\n\titem.CreatedAt = strings.ReplaceAll(strings.TrimSpace(parts[1]), \"</p>\", \"\")\n\tfor i := 1; i < len(parts); i++ {\n\t\tif strings.Contains(parts[i], \"问题修复\") || strings.Contains(parts[i], \"Bug Fixes\") {\n\t\t\titem.FixCount = strings.Count(parts[i], \"<li>\")\n\t\t}\n\t\tif strings.Contains(parts[i], \"新增功能\") || strings.Contains(parts[i], \"New Features\") {\n\t\t\titem.NewCount = strings.Count(parts[i], \"<li>\")\n\t\t}\n\t\tif strings.Contains(parts[i], \"功能优化\") || strings.Contains(parts[i], \"Improvements\") {\n\t\t\titem.OptimizationCount = strings.Count(parts[i], \"<li>\")\n\t\t}\n\t}\n\titem.Content = strings.Replace(content, fmt.Sprintf(\"<p>%s</p>\", item.CreatedAt), \"\", 1)\n\titem.Version = version\n\treturn item\n}\n\nfunc (u *UpgradeService) handleBackup(originalDir string, svcInfo serviceInfo) error {\n\tif err := files.CopyItem(false, true, \"/usr/local/bin/1panel-core\", originalDir); err != nil {\n\t\treturn err\n\t}\n\tif err := files.CopyItem(false, true, \"/usr/local/bin/1panel-agent\", originalDir); err != nil {\n\t\treturn err\n\t}\n\tif err := files.CopyItem(false, true, \"/usr/local/bin/1pctl\", originalDir); err != nil {\n\t\treturn err\n\t}\n\tif err := files.CopyItem(true, true, \"/usr/local/bin/lang\", originalDir); err != nil {\n\t\treturn err\n\t}\n\tif err := files.CopyItem(false, true, path.Join(svcInfo.basePath, svcInfo.coreName), originalDir); err != nil {\n\t\treturn err\n\t}\n\tif err := files.CopyItem(false, true, path.Join(svcInfo.basePath, svcInfo.agentName), originalDir); err != nil {\n\t\treturn err\n\t}\n\tif err := files.CopyItem(true, true, path.Join(global.CONF.Base.InstallDir, \"1panel/db\"), originalDir); err != nil {\n\t\treturn err\n\t}\n\tif err := files.CopyItem(false, true, path.Join(global.CONF.Base.InstallDir, \"1panel/geo/GeoIP.mmdb\"), originalDir); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (u *UpgradeService) handleRollback(originalDir string, errStep int, svcInfo serviceInfo) {\n\t_ = settingRepo.Update(\"SystemStatus\", \"Free\")\n\tdbPath := path.Join(global.CONF.Base.InstallDir, \"1panel\")\n\tif _, err := os.Stat(path.Join(originalDir, \"db\")); err == nil {\n\t\tif err := files.CopyItem(true, true, path.Join(originalDir, \"db\"), dbPath); err != nil {\n\t\t\tglobal.LOG.Errorf(\"rollback 1panel db failed, err: %v\", err)\n\t\t}\n\t}\n\tif err := files.CopyItem(false, true, path.Join(originalDir, \"1panel-core\"), \"/usr/local/bin\"); err != nil {\n\t\tglobal.LOG.Errorf(\"rollback 1panel-core failed, err: %v\", err)\n\t}\n\tif err := files.CopyItem(false, true, path.Join(originalDir, \"1panel-agent\"), \"/usr/local/bin\"); err != nil {\n\t\tglobal.LOG.Errorf(\"rollback 1panel-agent failed, err: %v\", err)\n\t}\n\tif errStep == 1 {\n\t\treturn\n\t}\n\tif err := files.CopyItem(false, true, path.Join(originalDir, \"1pctl\"), \"/usr/local/bin\"); err != nil {\n\t\tglobal.LOG.Errorf(\"rollback 1pctl failed, err: %v\", err)\n\t}\n\tif errStep == 2 {\n\t\treturn\n\t}\n\tif err := files.CopyItem(false, true, path.Join(originalDir, svcInfo.coreName), svcInfo.basePath); err != nil {\n\t\tglobal.LOG.Errorf(\"rollback %s failed, err: %v\", svcInfo.coreName, err)\n\t}\n\tif err := files.CopyItem(false, true, path.Join(originalDir, svcInfo.agentName), svcInfo.basePath); err != nil {\n\t\tglobal.LOG.Errorf(\"rollback %s failed, err: %v\", svcInfo.agentName, err)\n\t}\n\tif errStep == 3 {\n\t\treturn\n\t}\n\tif err := files.CopyItem(true, true, path.Join(originalDir, \"lang\"), \"/usr/local/bin\"); err != nil {\n\t\tglobal.LOG.Errorf(\"rollback language files failed, err: %v\", err)\n\t}\n\tif err := files.CopyItem(false, true, path.Join(originalDir, \"GeoIP.mmdb\"), path.Join(global.CONF.Base.InstallDir, \"1panel/geo\")); err != nil {\n\t\tglobal.LOG.Errorf(\"rollback GeoIP database failed, err: %v\", err)\n\t}\n}\n\nfunc (u *UpgradeService) loadVersionByMode(developer, currentVersion string) (string, string, string) {\n\tvar current, latest string\n\tif global.CONF.Base.Mode == \"dev\" {\n\t\tdevVersionLatest := u.loadVersion(true, currentVersion, \"dev\")\n\t\treturn devVersionLatest, \"\", \"\"\n\t}\n\n\tbetaVersionLatest := \"\"\n\tlatest = u.loadVersion(true, currentVersion, \"stable\")\n\tcurrent = u.loadVersion(false, currentVersion, \"stable\")\n\tif developer == constant.StatusEnable {\n\t\tbetaVersionLatest = u.loadVersion(true, currentVersion, \"beta\")\n\t}\n\tif current != latest {\n\t\treturn betaVersionLatest, current, latest\n\t}\n\n\tversionPart := strings.Split(current, \".\")\n\tif len(versionPart) < 3 {\n\t\treturn betaVersionLatest, \"\", latest\n\t}\n\tnum, _ := strconv.Atoi(versionPart[1])\n\tif num == 0 {\n\t\treturn betaVersionLatest, \"\", latest\n\t}\n\tif num >= 10 {\n\t\tif current[:6] == currentVersion[:6] {\n\t\t\treturn betaVersionLatest, current, \"\"\n\t\t}\n\t\treturn betaVersionLatest, \"\", latest\n\t}\n\tif current[:5] == currentVersion[:5] {\n\t\treturn betaVersionLatest, \"\", \"\"\n\t}\n\treturn betaVersionLatest, \"\", latest\n}\n\nfunc (u *UpgradeService) loadVersion(isLatest bool, currentVersion, mode string) string {\n\tpath := fmt.Sprintf(\"%s/%s/latest\", global.RepoURL(), mode)\n\tif !isLatest {\n\t\tpath = fmt.Sprintf(\"%s/%s/latest.current\", global.RepoURL(), mode)\n\t}\n\t_, latestVersionRes, err := req_helper.HandleRequestWithProxy(path, http.MethodGet, constant.TimeOut20s)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load latest version from oss failed, err: %v\", err)\n\t\treturn \"\"\n\t}\n\tversion := string(latestVersionRes)\n\tif strings.Contains(version, \"<\") {\n\t\tglobal.LOG.Errorf(\"load latest version from oss failed, err: %v\", version)\n\t\treturn \"\"\n\t}\n\tif isLatest {\n\t\treturn u.checkVersion(version, currentVersion)\n\t}\n\n\tversionMap := make(map[string]string)\n\tif err := json.Unmarshal(latestVersionRes, &versionMap); err != nil {\n\t\tglobal.LOG.Errorf(\"load latest version from oss failed (error unmarshal), err: %v\", err)\n\t\treturn \"\"\n\t}\n\n\tversionPart := strings.Split(currentVersion, \".\")\n\tif len(versionPart) < 3 {\n\t\tglobal.LOG.Errorf(\"current version is error format: %s\", currentVersion)\n\t\treturn \"\"\n\t}\n\tnum, _ := strconv.Atoi(versionPart[1])\n\tif num >= 10 {\n\t\tif version, ok := versionMap[currentVersion[0:5]]; ok {\n\t\t\treturn u.checkVersion(version, currentVersion)\n\t\t}\n\t\treturn \"\"\n\t}\n\tif version, ok := versionMap[currentVersion[0:4]]; ok {\n\t\treturn u.checkVersion(version, currentVersion)\n\t}\n\treturn \"\"\n}\n\nfunc (u *UpgradeService) checkVersion(v2, v1 string) string {\n\taddSuffix := false\n\tif !strings.Contains(v1, \"-\") {\n\t\tv1 = v1 + \"-lts\"\n\t}\n\tif !strings.Contains(v2, \"-\") {\n\t\taddSuffix = true\n\t\tv2 = v2 + \"-lts\"\n\t}\n\tif common.ComparePanelVersion(v2, v1) {\n\t\tif addSuffix {\n\t\t\treturn strings.TrimSuffix(v2, \"-lts\")\n\t\t}\n\t\treturn v2\n\t}\n\treturn \"\"\n}\n\nfunc (u *UpgradeService) loadReleaseNotes(path string) (string, error) {\n\t_, releaseNotes, err := req_helper.HandleRequestWithProxy(path, http.MethodGet, constant.TimeOut20s)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(releaseNotes), nil\n}\n\nfunc loadArch() (string, error) {\n\tstd, err := cmd.RunDefaultWithStdoutBashC(\"uname -a\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"std: %s, err: %s\", std, err.Error())\n\t}\n\tif strings.Contains(std, \"x86_64\") {\n\t\treturn \"amd64\", nil\n\t}\n\tif strings.Contains(std, \"arm64\") || strings.Contains(std, \"aarch64\") {\n\t\treturn \"arm64\", nil\n\t}\n\tif strings.Contains(std, \"armv7l\") {\n\t\treturn \"armv7\", nil\n\t}\n\tif strings.Contains(std, \"ppc64le\") {\n\t\treturn \"ppc64le\", nil\n\t}\n\tif strings.Contains(std, \"s390x\") {\n\t\treturn \"s390x\", nil\n\t}\n\tif strings.Contains(std, \"riscv64\") {\n\t\treturn \"riscv64\", nil\n\t}\n\treturn \"\", fmt.Errorf(\"unsupported such arch: %s\", std)\n}\n\nfunc dropBackupCopies() {\n\tbackupCopies, _ := settingRepo.GetValueByKey(\"UpgradeBackupCopies\")\n\tcopies, _ := strconv.Atoi(backupCopies)\n\tif copies == 0 {\n\t\treturn\n\t}\n\tbackupDir := path.Join(global.CONF.Base.InstallDir, \"1panel/tmp/upgrade\")\n\tupgradeDir, err := os.ReadDir(backupDir)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"read upgrade dir failed, err: %v\", err)\n\t\treturn\n\t}\n\tvar versions []string\n\tfor _, item := range upgradeDir {\n\t\tif item.IsDir() && strings.HasPrefix(item.Name(), \"v\") {\n\t\t\tversions = append(versions, item.Name())\n\t\t}\n\t}\n\tif len(versions) <= copies {\n\t\treturn\n\t}\n\tsort.Slice(versions, func(i, j int) bool {\n\t\treturn common.ComparePanelVersion(versions[i], versions[j])\n\t})\n\tfor i := copies; i < len(versions); i++ {\n\t\t_ = os.RemoveAll(backupDir + \"/\" + versions[i])\n\t}\n}\n"
  },
  {
    "path": "core/app/task/task.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/google/uuid\"\n\t\"github.com/sirupsen/logrus\"\n)\n\ntype ActionFunc func(*Task) error\ntype RollbackFunc func(*Task)\n\ntype Task struct {\n\tName              string\n\tTaskID            string\n\tLogger            *logrus.Logger\n\tSubTasks          []*SubTask\n\tRollbacks         []RollbackFunc\n\tlogFile           *os.File\n\ttaskRepo          repo.ITaskRepo\n\tTask              *model.Task\n\tParentID          string\n\tCancelWhenTimeout bool\n}\n\ntype SubTask struct {\n\tRootTask          *Task\n\tName              string\n\tStepAlias         string\n\tRetry             int\n\tTimeout           time.Duration\n\tAction            ActionFunc\n\tRollback          RollbackFunc\n\tError             error\n\tIgnoreErr         bool\n\tCancelWhenTimeout bool\n}\n\nconst (\n\tTaskUpgrade        = \"TaskUpgrade\"\n\tTaskAddNode        = \"TaskAddNode\"\n\tTaskSync           = \"TaskSync\"\n\tTaskSyncForNode    = \"TaskSyncForNode\"\n\tTaskRsync          = \"TaskRsync\"\n\tTaskInstallCluster = \"TaskInstallCluster\"\n\tTaskCreateCluster  = \"TaskCreateCluster\"\n\tTaskBackup         = \"TaskBackup\"\n\tTaskPush           = \"TaskPush\"\n)\n\nconst (\n\tTaskScopeSystem     = \"System\"\n\tTaskScopeScript     = \"ScriptLibrary\"\n\tTaskScopeNodeFile   = \"NodeFile\"\n\tTaskScopeAppBackup  = \"AppBackup\"\n\tTaskScopeCluster    = \"Cluster\"\n\tTaskScopeAppInstall = \"AppInstallTask\"\n)\n\nfunc GetTaskName(resourceName, operate, scope string) string {\n\treturn fmt.Sprintf(\"%s%s [%s]\", i18n.GetMsgByKey(operate), i18n.GetMsgByKey(scope), resourceName)\n}\n\nfunc NewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) {\n\treturn NewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID)\n}\n\nfunc NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {\n\tif taskID == \"\" {\n\t\ttaskID = uuid.New().String()\n\t}\n\tlogItem := path.Join(global.CONF.Base.InstallDir, \"1panel/log/task\")\n\tlogDir := path.Join(logItem, taskScope)\n\tif _, err := os.Stat(logDir); os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(logDir, constant.DirPerm); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create log directory: %w\", err)\n\t\t}\n\t}\n\tlogPath := path.Join(logItem, taskScope, taskID+\".log\")\n\tlogger := logrus.New()\n\tlogger.SetFormatter(&SimpleFormatter{})\n\tlogFile, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, constant.FilePerm)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open log file: %w\", err)\n\t}\n\tlogger.SetOutput(logFile)\n\ttaskModel := &model.Task{\n\t\tID:         taskID,\n\t\tName:       name,\n\t\tType:       taskScope,\n\t\tLogFile:    logPath,\n\t\tStatus:     constant.StatusExecuting,\n\t\tResourceID: resourceID,\n\t\tOperate:    operate,\n\t}\n\ttaskRepo := repo.NewITaskRepo()\n\ttask := &Task{Name: name, logFile: logFile, Logger: logger, taskRepo: taskRepo, Task: taskModel}\n\treturn task, nil\n}\n\nfunc (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {\n\tsubTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (t *Task) AddSubTaskWithAlias(key string, action ActionFunc, rollback RollbackFunc) {\n\tsubTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), StepAlias: key, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {\n\tsubTask := &SubTask{RootTask: t, Name: name, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (t *Task) AddSubTaskWithIgnoreErr(name string, action ActionFunc) {\n\tsubTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: nil, IgnoreErr: true}\n\tt.SubTasks = append(t.SubTasks, subTask)\n}\n\nfunc (s *SubTask) Execute() error {\n\tsubTaskName := s.Name\n\tif s.Name == \"\" {\n\t\tsubTaskName = i18n.GetMsgByKey(\"SubTask\")\n\t}\n\ts.RootTask.LogStart(subTaskName)\n\tvar err error\n\tfor i := 0; i < s.Retry+1; i++ {\n\t\tif i > 0 {\n\t\t\ts.RootTask.Log(i18n.GetWithName(\"TaskRetry\", strconv.Itoa(i)))\n\t\t}\n\t\tctx, cancel := context.WithTimeout(context.Background(), s.Timeout)\n\t\tdefer cancel()\n\n\t\tdone := make(chan error)\n\t\tgo func() {\n\t\t\tdone <- s.Action(s.RootTask)\n\t\t}()\n\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\ts.RootTask.Log(i18n.GetWithName(\"TaskTimeout\", subTaskName))\n\t\t\tif s.CancelWhenTimeout {\n\t\t\t\treturn buserr.New(i18n.GetWithName(\"TaskTimeout\", subTaskName))\n\t\t\t}\n\t\tcase err = <-done:\n\t\t\tif err != nil {\n\t\t\t\ts.RootTask.Log(i18n.GetWithNameAndErr(\"SubTaskFailed\", subTaskName, err))\n\t\t\t} else {\n\t\t\t\ts.RootTask.Log(i18n.GetWithName(\"SubTaskSuccess\", subTaskName))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tif i == s.Retry {\n\t\t\tif s.Rollback != nil {\n\t\t\t\ts.Rollback(s.RootTask)\n\t\t\t}\n\t\t}\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\treturn err\n}\n\nfunc (t *Task) updateTask(task *model.Task) {\n\t_ = t.taskRepo.Update(context.Background(), task)\n}\n\nfunc (t *Task) Execute() error {\n\tif err := t.taskRepo.Save(context.Background(), t.Task); err != nil {\n\t\treturn err\n\t}\n\tvar err error\n\tt.Log(i18n.GetWithName(\"TaskStart\", t.Name))\n\tfor _, subTask := range t.SubTasks {\n\t\tsubTask.CancelWhenTimeout = t.CancelWhenTimeout\n\t\tt.Task.CurrentStep = subTask.StepAlias\n\t\tt.updateTask(t.Task)\n\t\tif err = subTask.Execute(); err == nil {\n\t\t\tif subTask.Rollback != nil {\n\t\t\t\tt.Rollbacks = append(t.Rollbacks, subTask.Rollback)\n\t\t\t}\n\t\t} else {\n\t\t\tif subTask.IgnoreErr {\n\t\t\t\terr = nil\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tt.Task.ErrorMsg = err.Error()\n\t\t\tt.Task.Status = constant.StatusFailed\n\t\t\tfor _, rollback := range t.Rollbacks {\n\t\t\t\trollback(t)\n\t\t\t}\n\t\t\tt.updateTask(t.Task)\n\t\t\tbreak\n\t\t}\n\t}\n\tif t.Task.Status == constant.StatusExecuting {\n\t\tt.Task.Status = constant.StatusSuccess\n\t\tt.Log(i18n.GetWithName(\"TaskSuccess\", t.Name))\n\t} else {\n\t\tt.Log(i18n.GetWithName(\"TaskFailed\", t.Name))\n\t}\n\tt.Log(\"[TASK-END]\")\n\tt.Task.EndAt = time.Now()\n\tt.updateTask(t.Task)\n\t_ = t.logFile.Close()\n\treturn err\n}\n\nfunc (t *Task) DeleteLogFile() {\n\t_ = os.Remove(t.Task.LogFile)\n}\n\nfunc (t *Task) LogWithStatus(msg string, err error) {\n\tif err != nil {\n\t\tt.Logger.Print(i18n.GetWithNameAndErr(\"FailedStatus\", msg, err))\n\t} else {\n\t\tt.Logger.Print(i18n.GetWithName(\"SuccessStatus\", msg))\n\t}\n}\n\nfunc (t *Task) Log(msg string) {\n\tt.Logger.Print(msg)\n}\n\nfunc (t *Task) Logf(format string, v ...any) {\n\tt.Logger.Printf(format, v...)\n}\n\nfunc (t *Task) LogFailed(msg string) {\n\tt.Logger.Print(msg + i18n.GetMsgByKey(\"Failed\"))\n}\n\nfunc (t *Task) LogFailedWithErr(msg string, err error) {\n\tt.Logger.Printf(\"%s %s : %s\", msg, i18n.GetMsgByKey(\"Failed\"), err.Error())\n}\n\nfunc (t *Task) LogSuccess(msg string) {\n\tt.Logger.Print(msg + i18n.GetMsgByKey(\"Success\"))\n}\nfunc (t *Task) LogSuccessF(format string, v ...any) {\n\tt.Logger.Print(fmt.Sprintf(format, v...) + i18n.GetMsgByKey(\"Success\"))\n}\n\nfunc (t *Task) LogStart(msg string) {\n\tt.Logger.Printf(\"%s%s\", i18n.GetMsgByKey(\"Start\"), msg)\n}\n\nfunc (t *Task) LogWithOps(operate, msg string) {\n\tt.Logger.Printf(\"%s%s\", i18n.GetMsgByKey(operate), msg)\n}\n\nfunc (t *Task) LogSuccessWithOps(operate, msg string) {\n\tt.Logger.Printf(\"%s%s%s\", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey(\"Success\"))\n}\n\nfunc (t *Task) LogFailedWithOps(operate, msg string, err error) {\n\tt.Logger.Printf(\"%s%s%s : %s \", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey(\"Failed\"), err.Error())\n}\n\ntype SimpleFormatter struct{}\n\nfunc (f *SimpleFormatter) Format(entry *logrus.Entry) ([]byte, error) {\n\ttimestamp := entry.Time.Format(\"2006/01/02 15:04:05\")\n\tmessage := fmt.Sprintf(\"%s %s\\n\", timestamp, entry.Message)\n\treturn []byte(message), nil\n}\n"
  },
  {
    "path": "core/buserr/errors.go",
    "content": "package buserr\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/pkg/errors\"\n)\n\ntype BusinessError struct {\n\tMsg    string\n\tDetail interface{}\n\tMap    map[string]interface{}\n\tErr    error\n}\n\nfunc (e BusinessError) Error() string {\n\tcontent := \"\"\n\tif e.Detail != nil {\n\t\tcontent = i18n.GetErrMsg(e.Msg, map[string]interface{}{\"detail\": e.Detail})\n\t} else if e.Map != nil {\n\t\tcontent = i18n.GetErrMsg(e.Msg, e.Map)\n\t} else {\n\t\tcontent = i18n.GetErrMsg(e.Msg, nil)\n\t}\n\tif content == \"\" {\n\t\tif e.Err != nil {\n\t\t\treturn e.Err.Error()\n\t\t}\n\t\treturn errors.New(e.Msg).Error()\n\t}\n\treturn content\n}\n\nfunc New(key string, opts ...Option) BusinessError {\n\tbe := BusinessError{\n\t\tMsg: key,\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(&be)\n\t}\n\n\treturn be\n}\n\nfunc WithErr(Key string, err error) BusinessError {\n\tparamMap := map[string]interface{}{}\n\tif err != nil {\n\t\tparamMap[\"err\"] = err\n\t}\n\treturn BusinessError{\n\t\tMsg: Key,\n\t\tMap: paramMap,\n\t\tErr: err,\n\t}\n}\n\nfunc WithDetail(Key string, detail interface{}, err error) BusinessError {\n\treturn BusinessError{\n\t\tMsg:    Key,\n\t\tDetail: detail,\n\t\tErr:    err,\n\t}\n}\n\nfunc WithMap(Key string, maps map[string]interface{}, err error) BusinessError {\n\treturn BusinessError{\n\t\tMsg: Key,\n\t\tMap: maps,\n\t\tErr: err,\n\t}\n}\n\nfunc WithName(Key string, name string) BusinessError {\n\tparamMap := map[string]interface{}{}\n\tif name != \"\" {\n\t\tparamMap[\"name\"] = name\n\t}\n\treturn BusinessError{\n\t\tMsg: Key,\n\t\tMap: paramMap,\n\t}\n}\n\ntype Option func(*BusinessError)\n\nfunc WithNameOption(name string) Option {\n\treturn func(be *BusinessError) {\n\t\tif name != \"\" {\n\t\t\tif be.Map == nil {\n\t\t\t\tbe.Map = make(map[string]interface{})\n\t\t\t}\n\t\t\tbe.Map[\"name\"] = name\n\t\t}\n\t}\n}\n\nfunc WithErrOption(err error) Option {\n\treturn func(be *BusinessError) {\n\t\tbe.Err = err\n\t\tif err != nil {\n\t\t\tif be.Map == nil {\n\t\t\t\tbe.Map = make(map[string]interface{})\n\t\t\t}\n\t\t\tbe.Map[\"err\"] = err\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/buserr/multi_err.go",
    "content": "package buserr\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sort\"\n)\n\ntype MultiErr map[string]error\n\nfunc (e MultiErr) Error() string {\n\tvar keys []string\n\tfor key := range e {\n\t\tkeys = append(keys, key)\n\t}\n\tsort.Strings(keys)\n\n\tbuffer := bytes.NewBufferString(\"\")\n\tfor _, key := range keys {\n\t\tbuffer.WriteString(fmt.Sprintf(\"[%s] %s\\n\", key, e[key]))\n\t}\n\treturn buffer.String()\n}\n"
  },
  {
    "path": "core/cmd/server/app/app_config.go",
    "content": "package app\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed app_config.yml\nvar Config []byte\n\n//go:embed logo.png\nvar Logo []byte\n\n//go:embed app_param.yml\nvar Param []byte\n"
  },
  {
    "path": "core/cmd/server/app/app_config.yml",
    "content": "additionalProperties:\n  key:    #应用的 key ，仅限英文，用于在 Linux 创建文件夹\n  name:   #应用名称\n  tags:\n    - Tool #应用标签，可以有多个，请参照下方的标签列表\n  shortDescZh:  #应用中文描述，不要超过30个字\n  shortDescEn:  #应用英文描述\n  type: tool  #应用类型，区别于应用分类，只能有一个，请参照下方的类型列表\n  crossVersionUpdate:   #是否可以跨大版本升级\n  limit:  #应用安装数量限制，0 代表无限制\n  website:  #官网地址\n  github:  #github 地址\n  description:\n    en:\n    zh: #应用中文描述，不要超过30个字\n    zh-Hant:\n    ja:\n    ms:\n    pt-br:\n    ru:\n    ko: "
  },
  {
    "path": "core/cmd/server/app/app_param.yml",
    "content": "additionalProperties:\n  formFields:\n    - default: 8080\n      edit: true\n      envKey: PANEL_APP_PORT_HTTP\n      labelEn: Port\n      labelZh: 端口\n      required: true\n      rule: paramPort\n      type: number\n"
  },
  {
    "path": "core/cmd/server/cmd/app.go",
    "content": "package cmd\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/app\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar (\n\tappKey     string\n\tappVersion string\n)\n\nfunc init() {\n\tappCmd.SetHelpFunc(func(c *cobra.Command, s []string) {\n\t\ti18n.UseI18nForCmd(language)\n\t\tloadAppHelper()\n\t})\n\tinitCmd.SetHelpFunc(func(c *cobra.Command, s []string) {\n\t\ti18n.UseI18nForCmd(language)\n\t\tloadAppInitHelper()\n\t})\n\tinitCmd.Flags().StringVarP(&appKey, \"key\", \"k\", \"\", \"\")\n\tinitCmd.Flags().StringVarP(&appVersion, \"version\", \"v\", \"\", \"\")\n\tappCmd.AddCommand(initCmd)\n\tRootCmd.AddCommand(appCmd)\n}\n\nvar appCmd = &cobra.Command{\n\tUse: \"app\",\n}\n\nvar initCmd = &cobra.Command{\n\tUse: \"init\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl app init\"}))\n\t\t\treturn nil\n\t\t}\n\t\tif len(args) > 0 {\n\t\t\tappKey = args[0]\n\t\t\tif len(args) > 1 {\n\t\t\t\tappVersion = args[1]\n\t\t\t}\n\t\t}\n\t\tif appKey == \"\" {\n\t\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"AppMissKey\"))\n\t\t\treturn nil\n\t\t}\n\t\tif appVersion == \"\" {\n\t\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"AppMissVersion\"))\n\t\t\treturn nil\n\t\t}\n\t\tappKeyPath := fmt.Sprintf(\"./%s\", appKey)\n\t\tif err := createFolder(appKeyPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tconfigYamlPath := fmt.Sprintf(\"%s/data.yml\", appKeyPath)\n\t\tif err := createFile(configYamlPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := writeFile(configYamlPath, bytes.NewReader(app.Config)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treadMePath := fmt.Sprintf(\"%s/README.md\", appKeyPath)\n\t\tif err := createFile(readMePath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tlogoPath := fmt.Sprintf(\"%s/logo.png\", appKeyPath)\n\t\tif err := createFile(logoPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := writeFile(logoPath, bytes.NewReader(app.Logo)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tversionPath := fmt.Sprintf(\"%s/%s\", appKeyPath, appVersion)\n\t\tif _, err := os.Stat(versionPath); err == nil {\n\t\t\treturn errors.New(i18n.GetMsgByKeyForCmd(\"AppVersionExist\"))\n\t\t}\n\t\tif err := createFolder(versionPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tversionParamPath := fmt.Sprintf(\"%s/%s\", versionPath, \"data.yml\")\n\t\tif err := createFile(versionParamPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := writeFile(versionParamPath, bytes.NewReader(app.Param)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdockerComposeYamlPath := fmt.Sprintf(\"%s/%s\", versionPath, \"docker-compose.yml\")\n\t\tif err := createFile(dockerComposeYamlPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"AppCreateSuccessful\"))\n\t\treturn nil\n\t},\n}\n\nfunc createFile(filePath string) error {\n\tif _, err := os.Stat(filePath); err == nil {\n\t\treturn nil\n\t}\n\tfile, err := os.Create(filePath)\n\tif err != nil {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"AppCreateFileErr\", map[string]interface{}{\"name\": filePath, \"err\": err.Error()}))\n\t\treturn err\n\t}\n\tdefer file.Close()\n\treturn nil\n}\n\nfunc createFolder(dirPath string) error {\n\tif _, err := os.Stat(dirPath); err == nil {\n\t\treturn nil\n\t}\n\tif err := os.MkdirAll(dirPath, 0755); err != nil {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"AppCreateDirErr\", map[string]interface{}{\"name\": dirPath, \"err\": err.Error()}))\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc writeFile(filePath string, in io.Reader) error {\n\tdata, err := io.ReadAll(in)\n\tif err != nil {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"AppWriteErr\", map[string]interface{}{\"name\": filePath, \"err\": err.Error()}))\n\t\treturn err\n\t}\n\tif err := os.WriteFile(filePath, data, 0755); err != nil {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"AppWriteErr\", map[string]interface{}{\"name\": filePath, \"err\": err.Error()}))\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc loadAppHelper() {\n\tfmt.Println(i18n.GetMsgByKeyForCmd(\"AppCommands\"))\n\tfmt.Println(\"\\nUsage:\\n  1panel app [command]\\n\\nAvailable Commands:\")\n\tfmt.Println(\"\\n  init        \" + i18n.GetMsgByKeyForCmd(\"AppInit\"))\n\tfmt.Println(\"\\nFlags:\\n  -h, --help             help for app\")\n\tfmt.Println(\"  -k, --key string       \" + i18n.GetMsgByKeyForCmd(\"AppKeyVal\"))\n\tfmt.Println(\"  -v, --version string   \" + i18n.GetMsgByKeyForCmd(\"AppVersion\"))\n\tfmt.Println(\"\\nUse \\\"1panel app [command] --help\\\" for more information about a command.\")\n}\n\nfunc loadAppInitHelper() {\n\tfmt.Println(i18n.GetMsgByKeyForCmd(\"AppInit\"))\n\tfmt.Println(\"\\nUsage:\\n  1panel app init [flags]\")\n\tfmt.Println(\"\\nFlags:\\n  -h, --help             help for app\")\n\tfmt.Println(\"  -k, --key string       \" + i18n.GetMsgByKeyForCmd(\"AppKeyVal\"))\n\tfmt.Println(\"  -v, --version string   \" + i18n.GetMsgByKeyForCmd(\"AppVersion\"))\n}\n"
  },
  {
    "path": "core/cmd/server/cmd/listen-ip.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc init() {\n\tlistenCmd.SetHelpFunc(func(c *cobra.Command, s []string) {\n\t\ti18n.UseI18nForCmd(language)\n\t\tloadListenIPHelper()\n\t})\n\n\tRootCmd.AddCommand(listenCmd)\n\tlistenCmd.AddCommand(listenIpv4Cmd)\n\tlistenCmd.AddCommand(listenIpv6Cmd)\n}\n\nvar listenCmd = &cobra.Command{\n\tUse: \"listen-ip\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tloadListenIPHelper()\n\t\treturn nil\n\t},\n}\n\nvar listenIpv4Cmd = &cobra.Command{\n\tUse: \"ipv4\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\treturn updateBindInfo(\"ipv4\")\n\t},\n}\nvar listenIpv6Cmd = &cobra.Command{\n\tUse: \"ipv6\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\treturn updateBindInfo(\"ipv6\")\n\t},\n}\n\nfunc updateBindInfo(protocol string) error {\n\tif !isRoot() {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl listen-ip ipv6\"}))\n\t\treturn nil\n\t}\n\tdb, err := loadDBConn(\"core.db\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tipv6 := constant.StatusDisable\n\ttcp := \"tcp4\"\n\taddress := \"0.0.0.0\"\n\tif protocol == \"ipv6\" {\n\t\tipv6 = constant.StatusEnable\n\t\ttcp = \"tcp6\"\n\t\taddress = \"::\"\n\t}\n\tif err := setSettingByKey(db, \"Ipv6\", ipv6); err != nil {\n\t\treturn err\n\t}\n\tif err := setSettingByKey(db, \"BindAddress\", address); err != nil {\n\t\treturn err\n\t}\n\tfmt.Println(i18n.GetMsgWithMapForCmd(\"ListenChangeSuccessful\", map[string]interface{}{\"value\": fmt.Sprintf(\" %s [%s]\", tcp, address)}))\n\treturn nil\n}\n\nfunc loadListenIPHelper() {\n\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UpdateCommands\"))\n\tfmt.Println(\"\\nUsage:\\n  1panel listen-ip [command]\\n\\nAvailable Commands:\")\n\tfmt.Println(\"\\n  ipv4        \" + i18n.GetMsgByKeyForCmd(\"ListenIPv4\"))\n\tfmt.Println(\"  ipv6        \" + i18n.GetMsgByKeyForCmd(\"ListenIPv6\"))\n\tfmt.Println(\"\\nFlags:\\n  -h, --help   help for listen-ip\")\n\tfmt.Println(\"\\nUse \\\"1panel listen-ip [command] --help\\\" for more information about a command.\")\n}\n"
  },
  {
    "path": "core/cmd/server/cmd/reset.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/passkey\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc init() {\n\tresetCmd.SetHelpFunc(func(c *cobra.Command, s []string) {\n\t\ti18n.UseI18nForCmd(language)\n\t\tloadResetHelper()\n\t})\n\n\tRootCmd.AddCommand(resetCmd)\n\tresetCmd.AddCommand(resetMFACmd)\n\tresetCmd.AddCommand(resetSSLCmd)\n\tresetCmd.AddCommand(resetEntranceCmd)\n\tresetCmd.AddCommand(resetBindIpsCmd)\n\tresetCmd.AddCommand(resetDomainCmd)\n\tresetCmd.AddCommand(resetPasskeyCmd)\n}\n\nvar resetCmd = &cobra.Command{\n\tUse: \"reset\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tloadResetHelper()\n\t\treturn nil\n\t},\n}\n\nvar resetMFACmd = &cobra.Command{\n\tUse: \"mfa\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl reset mfa\"}))\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn setSettingByKey(db, \"MFAStatus\", constant.StatusDisable)\n\t},\n}\nvar resetSSLCmd = &cobra.Command{\n\tUse: \"https\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl reset https\"}))\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := setSettingByKey(db, \"SSL\", constant.StatusDisable); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\nvar resetEntranceCmd = &cobra.Command{\n\tUse: \"entrance\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl reset entrance\"}))\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn setSettingByKey(db, \"SecurityEntrance\", \"\")\n\t},\n}\nvar resetBindIpsCmd = &cobra.Command{\n\tUse: \"ips\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl reset ips\"}))\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := setSettingByKey(db, \"AllowIPs\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\nvar resetDomainCmd = &cobra.Command{\n\tUse: \"domain\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl reset domain\"}))\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := setSettingByKey(db, \"BindDomain\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar resetPasskeyCmd = &cobra.Command{\n\tUse: \"passkey\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl reset passkey\"}))\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := setSettingByKey(db, passkey.PasskeyUserIDSettingKey, \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn setSettingByKey(db, passkey.PasskeyCredentialSettingKey, \"\")\n\t},\n}\n\nfunc loadResetHelper() {\n\tfmt.Println(i18n.GetMsgByKeyForCmd(\"ResetCommands\"))\n\tfmt.Println(\"\\nUsage:\\n  1panel reset [command]\\n\\nAvailable Commands:\")\n\tfmt.Println(\"\\n  domain      \" + i18n.GetMsgByKeyForCmd(\"ResetDomain\"))\n\tfmt.Println(\"  entrance    \" + i18n.GetMsgByKeyForCmd(\"ResetEntrance\"))\n\tfmt.Println(\"  https       \" + i18n.GetMsgByKeyForCmd(\"ResetHttps\"))\n\tfmt.Println(\"  ips         \" + i18n.GetMsgByKeyForCmd(\"ResetIPs\"))\n\tfmt.Println(\"  mfa         \" + i18n.GetMsgByKeyForCmd(\"ResetMFA\"))\n\tfmt.Println(\"  passkey     \" + i18n.GetMsgByKeyForCmd(\"ResetPasskey\"))\n\tfmt.Println(\"\\nFlags:\\n  -h, --help   help for reset\")\n\tfmt.Println(\"\\nUse \\\"1panel reset [command] --help\\\" for more information about a command.\")\n}\n"
  },
  {
    "path": "core/cmd/server/cmd/restore.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\tcmdUtils \"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/controller\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/files\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nfunc init() {\n\tRootCmd.AddCommand(restoreCmd)\n}\n\nvar restoreCmd = &cobra.Command{\n\tUse: \"restore\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl restore\"}))\n\t\t\treturn nil\n\t\t}\n\t\tstdout, err := cmdUtils.RunDefaultWithStdoutBashC(\"grep '^BASE_DIR=' /usr/local/bin/1pctl | cut -d'=' -f2\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"handle load `BASE_DIR` failed, err: %v\", err)\n\t\t}\n\t\tbaseDir := strings.ReplaceAll(stdout, \"\\n\", \"\")\n\t\tupgradeDir := path.Join(baseDir, \"1panel\", \"tmp\", \"upgrade\")\n\n\t\ttmpPath, err := loadRestorePath(upgradeDir)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif tmpPath == \"no such file\" {\n\t\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"RestoreNoSuchFile\"))\n\t\t\treturn nil\n\t\t}\n\t\ttmpPath = path.Join(upgradeDir, tmpPath, \"original\")\n\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"RestoreStep1\", map[string]interface{}{\"name\": tmpPath}))\n\t\tif err := files.CopyItem(false, true, path.Join(tmpPath, \"1panel-agent\"), \"/usr/local/bin\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := files.CopyItem(false, true, path.Join(tmpPath, \"1panel-core\"), \"/usr/local/bin\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := files.CopyItem(true, true, path.Join(tmpPath, \"lang\"), \"/usr/local/bin\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := files.CopyItem(false, true, path.Join(tmpPath, \"GeoIP.mmdb\"), path.Join(baseDir, \"1panel/geo\")); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsudo := cmdUtils.SudoHandleCmd()\n\t\t_, _ = cmdUtils.RunDefaultWithStdoutBashCf(\"%s chmod 755 /usr/local/bin/1panel-agent /usr/local/bin/1panel-core\", sudo)\n\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"RestoreStep2\"))\n\t\tif err := files.CopyItem(false, true, path.Join(tmpPath, \"1pctl\"), \"/usr/local/bin\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, _ = cmdUtils.RunDefaultWithStdoutBashCf(\"%s chmod 755 /usr/local/bin/1pctl\", sudo)\n\t\t_, _ = cmdUtils.RunDefaultWithStdoutBashCf(\"cp -r %s /usr/local/bin\", path.Join(tmpPath, \"lang\"))\n\t\tgeoPath := path.Join(global.CONF.Base.InstallDir, \"1panel/geo\")\n\t\t_, _ = cmdUtils.RunDefaultWithStdoutBashCf(\"mkdir %s && cp %s %s/\", geoPath, path.Join(tmpPath, \"GeoIP.mmdb\"), geoPath)\n\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"RestoreStep3\"))\n\t\tsvcBasePath, _ := controller.GetServicePath(\"\")\n\t\tsvcCoreName, _ := controller.LoadServiceName(\"1panel-core\")\n\t\tselCoreName, _ := controller.SelectInitScript(\"1panel-core\")\n\t\tscriptCoreName, _ := controller.GetScriptName(\"1panel-core\")\n\t\tsvcAgentName, _ := controller.LoadServiceName(\"1panel-agent\")\n\t\tselAgentName, _ := controller.SelectInitScript(\"1panel-agent\")\n\t\tscriptAgentName, _ := controller.GetScriptName(\"1panel-agent\")\n\t\tif err := files.CopyItem(false, true, path.Join(tmpPath, svcCoreName), svcBasePath); err != nil {\n\t\t\tif err := files.CopyItem(false, true, path.Join(tmpPath, selCoreName), path.Join(svcBasePath, scriptCoreName)); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif err := files.CopyItem(false, true, path.Join(tmpPath, svcAgentName), svcBasePath); err != nil {\n\t\t\tif err := files.CopyItem(false, true, path.Join(tmpPath, selAgentName), path.Join(svcBasePath, scriptAgentName)); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"RestoreStep4\"))\n\t\tif _, err := os.Stat(path.Join(tmpPath, \"db\")); err == nil {\n\t\t\tdbPath := path.Join(baseDir, \"1panel\")\n\t\t\ttargetDBDir := path.Join(dbPath, \"db\")\n\t\t\tif err := os.RemoveAll(targetDBDir); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"rollback 1panel db cleanup failed, err: %v\", err)\n\t\t\t}\n\t\t\tif err := files.CopyItem(true, true, path.Join(tmpPath, \"db\"), dbPath); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"rollback 1panel db failed, err: %v\", err)\n\t\t\t}\n\t\t\tif err := cleanOrphanSQLiteSidecars(targetDBDir); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"rollback 1panel db sidecar cleanup failed, err: %v\", err)\n\t\t\t}\n\t\t}\n\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"RestoreStep5\"))\n\t\tversion := loadRollbackVersion(tmpPath)\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"RestoreSuccessful\", map[string]interface{}{\"version\": version}))\n\n\t\tcontroller.RestartPanel(true, true, true)\n\t\treturn nil\n\t},\n}\n\nfunc loadRestorePath(upgradeDir string) (string, error) {\n\tif _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) {\n\t\treturn \"no such file\", nil\n\t}\n\tfiles, err := os.ReadDir(upgradeDir)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar folders []string\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tfolders = append(folders, file.Name())\n\t\t}\n\t}\n\tif len(folders) == 0 {\n\t\treturn \"no such file\", nil\n\t}\n\tsort.Slice(folders, func(i, j int) bool {\n\t\treturn common.ComparePanelVersion(folders[i], folders[j])\n\t})\n\treturn folders[0], nil\n}\n\nfunc loadRollbackVersion(upgradeDir string) string {\n\tstdout, err := cmdUtils.RunDefaultWithStdoutBashCf(\"grep '^ORIGINAL_VERSION=' %s/1pctl | cut -d'=' -f2\", upgradeDir)\n\tif err != nil {\n\t\treturn \"-\"\n\t}\n\tinfo := strings.ReplaceAll(stdout, \"\\n\", \"\")\n\tif len(info) == 0 || info == `\"\"` {\n\t\treturn \"-\"\n\t}\n\treturn info\n}\n\nfunc cleanOrphanSQLiteSidecars(dbDir string) error {\n\tentries, err := os.ReadDir(dbDir)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tname := entry.Name()\n\t\tvar mainDB string\n\t\tswitch {\n\t\tcase strings.HasSuffix(name, \".db-wal\"):\n\t\t\tmainDB = strings.TrimSuffix(name, \"-wal\")\n\t\tcase strings.HasSuffix(name, \".db-shm\"):\n\t\t\tmainDB = strings.TrimSuffix(name, \"-shm\")\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, statErr := os.Stat(path.Join(dbDir, mainDB)); statErr != nil {\n\t\t\tif !os.IsNotExist(statErr) {\n\t\t\t\treturn statErr\n\t\t\t}\n\t\t\tif removeErr := os.Remove(path.Join(dbDir, name)); removeErr != nil && !os.IsNotExist(removeErr) {\n\t\t\t\treturn removeErr\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/cmd/server/cmd/root.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"os/user\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/server\"\n\tcmdUtils \"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/glebarez/sqlite\"\n\t\"github.com/spf13/cobra\"\n\t\"gorm.io/gorm\"\n)\n\nvar language string\n\nfunc init() {\n\tRootCmd.PersistentFlags().StringVarP(&language, \"language\", \"l\", \"en\", \"Set the language\")\n}\n\nvar RootCmd = &cobra.Command{\n\tUse: \"1panel\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\tserver.Start()\n\t\treturn nil\n\t},\n}\n\ntype setting struct {\n\tID        uint      `gorm:\"primarykey;AUTO_INCREMENT\" json:\"id\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n\tKey       string    `json:\"key\" gorm:\"type:varchar(256);not null;\"`\n\tValue     string    `json:\"value\" gorm:\"type:varchar(256)\"`\n\tAbout     string    `json:\"about\" gorm:\"type:longText\"`\n}\n\nfunc loadDBConn(dbName string) (*gorm.DB, error) {\n\tstdout, err := cmdUtils.RunDefaultWithStdoutBashC(\"grep '^BASE_DIR=' /usr/local/bin/1pctl | cut -d'=' -f2\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"handle load `BASE_DIR` failed, err: %v\", err)\n\t}\n\tbaseDir := strings.ReplaceAll(stdout, \"\\n\", \"\")\n\tif len(baseDir) == 0 {\n\t\treturn nil, fmt.Errorf(\"error `BASE_DIR` find in /usr/local/bin/1pctl \\n\")\n\t}\n\tif strings.HasSuffix(baseDir, \"/\") {\n\t\tbaseDir = baseDir[:strings.LastIndex(baseDir, \"/\")]\n\t}\n\n\tdb, err := gorm.Open(sqlite.Open(path.Join(baseDir, \"1panel/db\", dbName)), &gorm.Config{})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"init my db conn failed, err: %v \\n\", err)\n\t}\n\treturn db, nil\n}\n\nfunc getSettingByKey(db *gorm.DB, key string) string {\n\tvar setting setting\n\t_ = db.Where(\"key = ?\", key).First(&setting).Error\n\treturn setting.Value\n}\n\ntype LoginLog struct{}\n\nfunc isDefault(db *gorm.DB) bool {\n\tlogCount := int64(0)\n\t_ = db.Model(&LoginLog{}).Where(\"status = ?\", \"Success\").Count(&logCount).Error\n\treturn logCount == 0\n}\n\nfunc setSettingByKey(db *gorm.DB, key, value string) error {\n\treturn db.Model(&setting{}).Where(\"key = ?\", key).Updates(map[string]interface{}{\"value\": value}).Error\n}\n\nfunc isRoot() bool {\n\tcurrentUser, err := user.Current()\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn currentUser.Uid == \"0\"\n}\n"
  },
  {
    "path": "core/cmd/server/cmd/update.go",
    "content": "package cmd\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/encrypt\"\n\t\"github.com/spf13/cobra\"\n\t\"golang.org/x/term\"\n)\n\nfunc init() {\n\tupdateCmd.SetHelpFunc(func(c *cobra.Command, s []string) {\n\t\ti18n.UseI18nForCmd(language)\n\t\tloadUpdateHelper()\n\t})\n\n\tRootCmd.AddCommand(updateCmd)\n\tupdateCmd.AddCommand(updateUserName)\n\tupdateCmd.AddCommand(updatePassword)\n\tupdateCmd.AddCommand(updatePort)\n\n\tupdateCmd.AddCommand(updateVersion)\n}\n\nvar updateCmd = &cobra.Command{\n\tUse: \"update\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tloadUpdateHelper()\n\t\treturn nil\n\t},\n}\n\nvar updateUserName = &cobra.Command{\n\tUse:   \"username\",\n\tShort: i18n.GetMsgByKeyForCmd(\"UpdateUser\"),\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl update username\"}))\n\t\t\treturn nil\n\t\t}\n\t\tusername()\n\t\treturn nil\n\t},\n}\nvar updatePassword = &cobra.Command{\n\tUse:   \"password\",\n\tShort: i18n.GetMsgByKeyForCmd(\"UpdatePassword\"),\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl update password\"}))\n\t\t\treturn nil\n\t\t}\n\t\tpassword()\n\t\treturn nil\n\t},\n}\nvar updatePort = &cobra.Command{\n\tUse:   \"port\",\n\tShort: i18n.GetMsgByKeyForCmd(\"UpdatePort\"),\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl update port\"}))\n\t\t\treturn nil\n\t\t}\n\t\tport()\n\t\treturn nil\n\t},\n}\nvar updateVersion = &cobra.Command{\n\tUse:  \"version\",\n\tArgs: cobra.ExactArgs(1),\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl update version\"}))\n\t\t\treturn nil\n\t\t}\n\t\tversion := args[0]\n\t\tif len(version) == 0 || !strings.HasPrefix(version, \"v2.\") {\n\t\t\tfmt.Println(\"err version in param input\")\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"DBConnErr\", map[string]interface{}{\"err\": err.Error()}))\n\t\t\treturn err\n\t\t}\n\t\tif err := setSettingByKey(db, \"SystemVersion\", version); err != nil {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdateUserErr\", map[string]interface{}{\"err\": err.Error()}))\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nfunc username() {\n\treader := bufio.NewReader(os.Stdin)\n\tfmt.Print(i18n.GetMsgByKeyForCmd(\"UpdateUser\") + \": \")\n\tnewUsername, _ := reader.ReadString('\\n')\n\tnewUsername = strings.Trim(newUsername, \"\\n\")\n\tif len(newUsername) == 0 {\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UpdateUserNull\"))\n\t\treturn\n\t}\n\tif strings.Contains(newUsername, \" \") {\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UpdateUserBlank\"))\n\t\treturn\n\t}\n\tresult, err := regexp.MatchString(\"^[a-zA-Z0-9_\\u4e00-\\u9fa5]{3,30}$\", newUsername)\n\tif !result || err != nil {\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UpdateUserFormat\"))\n\t\treturn\n\t}\n\n\tdb, err := loadDBConn(\"core.db\")\n\tif err != nil {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"DBConnErr\", map[string]interface{}{\"err\": err.Error()}))\n\t\treturn\n\t}\n\tif err := setSettingByKey(db, \"UserName\", newUsername); err != nil {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdateUserErr\", map[string]interface{}{\"err\": err.Error()}))\n\t\treturn\n\t}\n\n\tfmt.Println(\"\\n\" + i18n.GetMsgByKeyForCmd(\"UpdateSuccessful\"))\n\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdateUserResult\", map[string]interface{}{\"name\": newUsername}))\n}\n\nfunc password() {\n\tfmt.Print(i18n.GetMsgByKeyForCmd(\"UpdatePassword\") + \": \")\n\tbytePassword, err := term.ReadPassword(int(os.Stdin.Fd()))\n\tif err != nil {\n\t\tfmt.Println(\"\\n\" + i18n.GetMsgWithMapForCmd(\"UpdatePasswordRead\", map[string]interface{}{\"err\": err.Error()}))\n\t\treturn\n\t}\n\tnewPassword := string(bytePassword)\n\tnewPassword = strings.Trim(newPassword, \"\\n\")\n\n\tif len(newPassword) == 0 {\n\t\tfmt.Println(\"\\n\", i18n.GetMsgByKeyForCmd(\"UpdatePasswordNull\"))\n\t\treturn\n\t}\n\tif strings.Contains(newPassword, \" \") {\n\t\tfmt.Println(\"\\n\" + i18n.GetMsgByKeyForCmd(\"UpdateUPasswordBlank\"))\n\t\treturn\n\t}\n\tdb, err := loadDBConn(\"core.db\")\n\tif err != nil {\n\t\tfmt.Println(\"\\n\" + i18n.GetMsgWithMapForCmd(\"DBConnErr\", map[string]interface{}{\"err\": err.Error()}))\n\t\treturn\n\t}\n\tcomplexSetting := getSettingByKey(db, \"ComplexityVerification\")\n\tif complexSetting == constant.StatusEnable {\n\t\tif isValidPassword(\"newPassword\") {\n\t\t\tfmt.Println(\"\\n\" + i18n.GetMsgByKeyForCmd(\"UpdatePasswordFormat\"))\n\t\t\treturn\n\t\t}\n\t}\n\tif len(newPassword) < 6 {\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UpdatePasswordLen\"))\n\t\treturn\n\t}\n\n\tfmt.Print(\"\\n\" + i18n.GetMsgByKeyForCmd(\"UpdatePasswordRe\"))\n\tbyteConfirmPassword, err := term.ReadPassword(int(os.Stdin.Fd()))\n\tif err != nil {\n\t\tfmt.Println(\"\\n\" + i18n.GetMsgWithMapForCmd(\"UpdatePasswordRead\", map[string]interface{}{\"err\": err.Error()}))\n\t\treturn\n\t}\n\tconfirmPassword := string(byteConfirmPassword)\n\tconfirmPassword = strings.Trim(confirmPassword, \"\\n\")\n\n\tif newPassword != confirmPassword {\n\t\tfmt.Println(\"\\n\", i18n.GetMsgByKeyForCmd(\"UpdatePasswordSame\"))\n\t\treturn\n\t}\n\n\tp := \"\"\n\tencryptSetting := getSettingByKey(db, \"EncryptKey\")\n\tif len(encryptSetting) == 16 {\n\t\tglobal.CONF.Base.EncryptKey = encryptSetting\n\t\tp, _ = encrypt.StringEncrypt(newPassword)\n\t} else {\n\t\tp = newPassword\n\t}\n\tif err := setSettingByKey(db, \"Password\", p); err != nil {\n\t\tfmt.Println(\"\\n\", i18n.GetMsgWithMapForCmd(\"UpdatePortErr\", map[string]interface{}{\"err\": err.Error()}))\n\t\treturn\n\t}\n\tusername := getSettingByKey(db, \"UserName\")\n\n\tfmt.Println(\"\\n\" + i18n.GetMsgByKeyForCmd(\"UpdateSuccessful\"))\n\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdateUserResult\", map[string]interface{}{\"name\": username}))\n\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdatePasswordResult\", map[string]interface{}{\"name\": string(newPassword)}))\n}\n\nfunc port() {\n\treader := bufio.NewReader(os.Stdin)\n\tfmt.Print(i18n.GetMsgByKeyForCmd(\"UpdatePort\") + \": \")\n\n\tnewPortStr, _ := reader.ReadString('\\n')\n\tnewPortStr = strings.Trim(newPortStr, \"\\n\")\n\tnewPort, err := strconv.Atoi(strings.TrimSpace(newPortStr))\n\tif err != nil || newPort < 1 || newPort > 65535 {\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UpdatePortFormat\"))\n\t\treturn\n\t}\n\tif common.ScanPort(newPort) {\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UpdatePortUsed\"))\n\t\treturn\n\t}\n\tdb, err := loadDBConn(\"core.db\")\n\tif err != nil {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"DBConnErr\", map[string]interface{}{\"err\": err.Error()}))\n\t\treturn\n\t}\n\tif err := setSettingByKey(db, \"ServerPort\", newPortStr); err != nil {\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdatePortErr\", map[string]interface{}{\"err\": err.Error()}))\n\t\treturn\n\t}\n\n\tfmt.Println(\"\\n\" + i18n.GetMsgByKeyForCmd(\"UpdateSuccessful\"))\n\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdatePortResult\", map[string]interface{}{\"name\": newPortStr}))\n\n\tstd, err := cmd.RunDefaultWithStdoutBashC(\"1pctl restart core\")\n\tif err != nil {\n\t\tfmt.Println(std)\n\t}\n}\nfunc isValidPassword(password string) bool {\n\tnumCount := 0\n\talphaCount := 0\n\tspecialCount := 0\n\n\tfor _, char := range password {\n\t\tswitch {\n\t\tcase unicode.IsDigit(char):\n\t\t\tnumCount++\n\t\tcase unicode.IsLetter(char):\n\t\t\talphaCount++\n\t\tcase isSpecialChar(char):\n\t\t\tspecialCount++\n\t\t}\n\t}\n\n\tif len(password) < 8 || len(password) > 30 {\n\t\treturn false\n\t}\n\tif (numCount == 0 && alphaCount == 0) || (alphaCount == 0 && specialCount == 0) || (numCount == 0 && specialCount == 0) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc isSpecialChar(char rune) bool {\n\tspecialChars := \"!@#$%*_,.?\"\n\treturn unicode.IsPunct(char) && contains(specialChars, char)\n}\n\nfunc contains(specialChars string, char rune) bool {\n\tfor _, c := range specialChars {\n\t\tif c == char {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc loadUpdateHelper() {\n\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UpdateCommands\"))\n\tfmt.Println(\"\\nUsage:\\n  1panel update [command]\\n\\nAvailable Commands:\")\n\tfmt.Println(\"\\n  password    \" + i18n.GetMsgByKeyForCmd(\"UpdatePassword\"))\n\tfmt.Println(\"  port        \" + i18n.GetMsgByKeyForCmd(\"UpdatePort\"))\n\tfmt.Println(\"  username    \" + i18n.GetMsgByKeyForCmd(\"UpdateUser\"))\n\tfmt.Println(\"\\nFlags:\\n  -h, --help   help for update\")\n\tfmt.Println(\"\\nUse \\\"1panel update [command] --help\\\" for more information about a command.\")\n}\n"
  },
  {
    "path": "core/cmd/server/cmd/user-info.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/encrypt\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc init() {\n\tRootCmd.AddCommand(userinfoCmd)\n}\n\nvar userinfoCmd = &cobra.Command{\n\tUse: \"user-info\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl user-info\"}))\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"init my db conn failed, err: %v \\n\", err)\n\t\t}\n\t\tagentDB, err := loadDBConn(\"agent.db\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"init my agent db conn failed, err: %v \\n\", err)\n\t\t}\n\t\tuser := getSettingByKey(db, \"UserName\")\n\t\tpass := \"********\"\n\t\tif isDefault(db) {\n\t\t\tencryptSetting := getSettingByKey(db, \"EncryptKey\")\n\t\t\tpass = getSettingByKey(db, \"Password\")\n\t\t\tif len(encryptSetting) == 16 {\n\t\t\t\tglobal.CONF.Base.EncryptKey = encryptSetting\n\t\t\t\tpass, _ = encrypt.StringDecrypt(pass)\n\t\t\t}\n\t\t}\n\t\tport := getSettingByKey(db, \"ServerPort\")\n\t\tssl := getSettingByKey(db, \"SSL\")\n\t\tentrance := getSettingByKey(db, \"SecurityEntrance\")\n\t\taddress := getSettingByKey(agentDB, \"SystemIP\")\n\t\tdomain := getSettingByKey(db, \"BindDomain\")\n\t\tif len(domain) != 0 {\n\t\t\taddress = domain\n\t\t}\n\n\t\tprotocol := \"http\"\n\t\tif ssl == constant.StatusEnable {\n\t\t\tprotocol = \"https\"\n\t\t}\n\t\tif address == \"\" {\n\t\t\taddress = \"$LOCAL_IP\"\n\t\t}\n\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UserInfoAddr\") + fmt.Sprintf(\"%s://%s:%s/%s \", protocol, address, port, entrance))\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdateUserResult\", map[string]interface{}{\"name\": user}))\n\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"UpdatePasswordResult\", map[string]interface{}{\"name\": pass}))\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"UserInfoPassHelp\") + \"1pctl update password\")\n\t\treturn nil\n\t},\n}\n"
  },
  {
    "path": "core/cmd/server/cmd/version.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/conf\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"gopkg.in/yaml.v3\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nfunc init() {\n\tRootCmd.AddCommand(versionCmd)\n}\n\nvar versionCmd = &cobra.Command{\n\tUse: \"version\",\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\ti18n.UseI18nForCmd(language)\n\t\tif !isRoot() {\n\t\t\tfmt.Println(i18n.GetMsgWithMapForCmd(\"SudoHelper\", map[string]interface{}{\"cmd\": \"sudo 1pctl version\"}))\n\t\t\treturn nil\n\t\t}\n\t\tdb, err := loadDBConn(\"core.db\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tversion := getSettingByKey(db, \"SystemVersion\")\n\n\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"SystemVersion\") + version)\n\t\tconfig := global.ServerConfig{}\n\t\tif err := yaml.Unmarshal(conf.AppYaml, &config); err != nil {\n\t\t\treturn fmt.Errorf(\"unmarshal conf.App.Yaml failed, err: %v\", err)\n\t\t} else {\n\t\t\tfmt.Println(i18n.GetMsgByKeyForCmd(\"SystemMode\") + config.Base.Mode)\n\t\t}\n\t\treturn nil\n\t},\n}\n"
  },
  {
    "path": "core/cmd/server/conf/app.yaml",
    "content": "base:\n  install_dir: /opt\n  mode: dev\n  is_demo: false\n  is_offline: false\n  is_fxplay: false\n  port: 9999\n  username: admin\n  password: admin123\n  version: v2.0.0\n\nlog:\n  level: debug\n  time_zone: Asia/Shanghai\n  log_name: 1Panel-Core\n  log_suffix: .log\n  max_backup: 10"
  },
  {
    "path": "core/cmd/server/conf/conf.go",
    "content": "package conf\n\nimport _ \"embed\"\n\n//go:embed app.yaml\nvar AppYaml []byte\n"
  },
  {
    "path": "core/cmd/server/docs/docs.go",
    "content": "package docs\n\nimport \"github.com/swaggo/swag\"\n\nconst docTemplate = `{\n\t\"swagger\": \"2.0\",\n\t\"info\": {\n\t\t\"contact\": {},\n\t\t\"description\": \"Top-Rated Web-based Linux Server Management Tool\",\n\t\t\"license\": {\n\t\t\t\"name\": \"GPL-3.0\",\n\t\t\t\"url\": \"https://www.gnu.org/licenses/gpl-3.0.html\"\n\t\t},\n\t\t\"termsOfService\": \"http://swagger.io/terms/\",\n\t\t\"title\": \"1Panel\",\n\t\t\"version\": \"2.0\"\n\t},\n\t\"host\": \"\",\n\t\"basePath\": \"/api/v2\",\n\t\"paths\": {\n\t\t\"/ai/agents\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentCreateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentItem\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Agent\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountCreateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Agent account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts/delete\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete Agent account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Agent accounts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts/verify\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountVerifyReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Verify Agent account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/browser/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentBrowserConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentBrowserConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Browser config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/browser/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentBrowserConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Browser config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/discord/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentDiscordConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentDiscordConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Discord channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/discord/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentDiscordConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Discord channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/feishu/approve\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentFeishuPairingApproveReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Approve Agent Feishu pairing code\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/feishu/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentFeishuConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentFeishuConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Feishu channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/feishu/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentFeishuConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Feishu channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/pairing/approve\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentChannelPairingApproveReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Approve Agent channel pairing code\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/qqbot/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentQQBotConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentQQBotConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent QQ Bot channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/qqbot/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentQQBotConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent QQ Bot channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/telegram/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentTelegramConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentTelegramConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Telegram channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/telegram/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentTelegramConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Telegram channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/delete\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete Agent\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/model/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentModelConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent model config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/other/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentOtherConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentOtherConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Other config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/other/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentOtherConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Other config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/plugin/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentPluginCheckReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentPluginStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check Agent plugin installation status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/plugin/install\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentPluginInstallReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Install Agent plugin\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/providers\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ProviderInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Providers\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Agents\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/token/reset\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentTokenResetReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Reset Agent token\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/domain/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaBindDomain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/domain/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaBindDomainReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaBindDomainRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get bind domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/gpu/load\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load gpu / xpu info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/domain/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpBindDomain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind Domain for mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/domain/get\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.McpBindDomainRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get bin Domain for mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/domain/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpBindDomainUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update bind Domain for mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.McpServersRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List mcp servers\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/server\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/server/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/server/op\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/server/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/close\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Close Ollama model conn\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"close conn for Ollama model [name]\",\n\t\t\t\t\t\"formatZH\": \"关闭 Ollama 模型连接 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Ollama model\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"add Ollama model [name]\",\n\t\t\t\t\t\"formatZH\": \"添加 Ollama 模型 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ForceDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete Ollama model\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"ollama_models\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"remove Ollama model [names]\",\n\t\t\t\t\t\"formatZH\": \"删除 Ollama 模型 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/load\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Ollama models\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/recreate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Rereate Ollama model\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"re-add Ollama model [name]\",\n\t\t\t\t\t\"formatZH\": \"添加 Ollama 模型重试 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Ollama models\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelDropList\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync Ollama model list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"sync Ollama model list\",\n\t\t\t\t\t\"formatZH\": \"同步 Ollama 模型列表\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/:key\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app key\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"key\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/checkupdate\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppUpdateRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get app list update\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/detail/:appId/:version/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app 版本\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"version\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app 类型\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"version\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppDetailDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app detail by appid\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/detail/node/:appKey/:version\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app key\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app version\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"version\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppDetailSimpleDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app detail by appkey and version\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/details/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppDetailDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get app detail by id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/icon/:key\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"app icon\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get app icon by app_id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/ignored/cancel\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ReqWithID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Cancel Ignore Upgrade App\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Cancel ignore application upgrade\",\n\t\t\t\t\t\"formatZH\": \"取消忽略应用升级\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/ignored/detail\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.AppIgnoreUpgrade\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List Upgrade Ignored App\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/install\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstallCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.AppInstall\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Install app\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Install app [name]\",\n\t\t\t\t\t\"formatZH\": \"安装应用 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstalledInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppInstalledCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check app installed\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search default config by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/config/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppConfigUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update app config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"installID\",\n\t\t\t\t\t\t\"webUI\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Application config update [installID]\",\n\t\t\t\t\t\"formatZH\": \"应用配置更新 [installID]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/conninfo\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DatabaseConn\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app password by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/delete/check/:appInstallId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"App install id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appInstallId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppResource\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before delete\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/ignore\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppIgnoreUpgradeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Ignore Upgrade App\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Ignore application upgrade\",\n\t\t\t\t\t\"formatZH\": \"忽略应用升级\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/info/:appInstallId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"App install id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appInstallId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppInstallInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get app install info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/list\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppInstallInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List app installed\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/loadport\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app port by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/op\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstalledOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate installed app\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"app_id\",\n\t\t\t\t\t\t\t\"output_value\": \"appId\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"appName\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"apps\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"appId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"key\",\n\t\t\t\t\t\t\t\"output_value\": \"appKey\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"installId\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] App [appKey][appName]\",\n\t\t\t\t\t\"formatZH\": \"[operate] 应用 [appKey][appName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/params/:appInstallId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appInstallId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search params by appInstallId\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/params/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstalledUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change app params\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"installId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Application param update [name]\",\n\t\t\t\t\t\"formatZH\": \"应用参数修改 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/port/change\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PortUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change app port\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"port\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Application port update [key]-[name] =\\u003e [port]\",\n\t\t\t\t\t\"formatZH\": \"应用端口修改 [key]-[name] =\\u003e [port]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstalledSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page app installed\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync app installed\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Sync the list of installed apps\",\n\t\t\t\t\t\"formatZH\": \"同步已安装应用列表\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/update/versions\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appInstallId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppVersion\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app update version by install id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List apps\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/services/:key\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"key\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppService\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app service by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/sync/local\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync local  app list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"App store synchronization\",\n\t\t\t\t\t\"formatZH\": \"应用商店同步\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/sync/remote\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync remote app list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"App store synchronization\",\n\t\t\t\t\t\"formatZH\": \"应用商店同步\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create backup account [type]\",\n\t\t\t\t\t\"formatZH\": \"创建备份账号 [type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/backup\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommonBackup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Backup system data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"detailName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"backup [type] data [name][detailName]\",\n\t\t\t\t\t\"formatZH\": \"备份 [type] 数据 [name][detailName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/buckets\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ForBuckets\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List buckets\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"backup_accounts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\t\t\t\"output_value\": \"types\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete backup account [types]\",\n\t\t\t\t\t\"formatZH\": \"删除备份账号 [types]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/local\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"get local backup dir\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/options\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load backup account options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete backup record\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"backup_records\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"file_name\",\n\t\t\t\t\t\t\t\"output_value\": \"files\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete backup records [files]\",\n\t\t\t\t\t\"formatZH\": \"删除备份记录 [files]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/description/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update backup record description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DownloadRecord\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download backup record\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"source\",\n\t\t\t\t\t\t\"fileName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"download backup records [source][fileName]\",\n\t\t\t\t\t\"formatZH\": \"下载备份记录 [source][fileName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RecordSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page backup records\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/search/bycronjob\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RecordSearchByCronjob\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page backup records by cronjob\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/size\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchForSize\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RecordFileSize\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load backup record size\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/recover\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommonRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Recover system data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"detailName\",\n\t\t\t\t\t\t\"file\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"recover [type] data [name][detailName] from [file]\",\n\t\t\t\t\t\"formatZH\": \"从 [file] 恢复 [type] 数据 [name][detailName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/recover/byupload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommonRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Recover system data by upload\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"detailName\",\n\t\t\t\t\t\t\"file\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"recover [type] data [name][detailName] from [file]\",\n\t\t\t\t\t\"formatZH\": \"从 [file] 恢复 [type] 数据 [name][detailName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/refresh/token\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Refresh token\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchPageWithType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search backup accounts with page\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/search/files\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List files from backup accounts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update backup account [types]\",\n\t\t\t\t\t\"formatZH\": \"更新备份账号 [types]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/upload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UploadForRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload file for recover\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"filePath\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"upload backup file [filePath]\",\n\t\t\t\t\t\"formatZH\": \"上传备份文件 [filePath]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"image\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create container [name][image]\",\n\t\t\t\t\t\"formatZH\": \"创建容器 [name][image]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/clean/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean container log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean container [name] logs\",\n\t\t\t\t\t\"formatZH\": \"清理容器 [name] 日志\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/commit\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerCommit\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Commit Container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create compose\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create compose [name]\",\n\t\t\t\t\t\"formatZH\": \"创建 compose [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/clean/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeLogClean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean compose log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean compose [name] logs\",\n\t\t\t\t\t\"formatZH\": \"清理容器编排 [name] 日志\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/env\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FilePath\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load compose environment variables\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeOperation\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate compose\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"compose [operation] [name]\",\n\t\t\t\t\t\"formatZH\": \"compose [operation] [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page composes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/test\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Test compose\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"check compose [name]\",\n\t\t\t\t\t\"formatZH\": \"检测 compose [name] 格式\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update compose\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update compose information [name]\",\n\t\t\t\t\t\"formatZH\": \"更新 compose [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/daemonjson\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DaemonJsonConf\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load docker daemon.json\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/daemonjson/file\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load docker daemon.json\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/daemonjson/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update docker daemon.json\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Updated configuration [key]\",\n\t\t\t\t\t\"formatZH\": \"更新配置 [key]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/daemonjson/update/byfile\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DaemonJsonUpdateByFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update docker daemon.json by upload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Updated configuration file\",\n\t\t\t\t\t\"formatZH\": \"更新配置文件\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/docker/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DockerOperation\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate docker\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] docker service\",\n\t\t\t\t\t\"formatZH\": \"docker 服务 [operation]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/docker/status\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DockerStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load docker status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/content\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileContent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get container file content\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileBatchDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete container file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download container file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List container files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/size\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"int\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get container file size\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/upload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"multipart/form-data\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"containerID\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"containerID\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"path\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"path\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"file\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"file\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload container file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/image\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Options\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"load images options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/all\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List all images\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/build\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageBuild\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Build image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"build image [name]\",\n\t\t\t\t\t\"formatZH\": \"构建镜像 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/load\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageLoad\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"load image from [path]\",\n\t\t\t\t\t\"formatZH\": \"从 [path] 加载镜像\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/pull\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImagePull\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Pull image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"repoID\",\n\t\t\t\t\t\t\"imageName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"image pull [reponame][imageName]\",\n\t\t\t\t\t\"formatZH\": \"镜像拉取 [reponame][imageName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/push\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImagePush\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Push image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"repoID\",\n\t\t\t\t\t\t\"tagName\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"push [tagName] to [reponame][name]\",\n\t\t\t\t\t\"formatZH\": \"[tagName] 推送到 [reponame][name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/remove\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"remove image [names]\",\n\t\t\t\t\t\"formatZH\": \"移除镜像 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/save\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageSave\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Save image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"tagName\",\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"save [tagName] as [path]/[name]\",\n\t\t\t\t\t\"formatZH\": \"保留 [tagName] 为 [path]/[name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageImage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page images\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/tag\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageTag\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Tag image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"repoID\",\n\t\t\t\t\t\t\"targetName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"tag image [reponame][targetName]\",\n\t\t\t\t\t\"formatZH\": \"tag 镜像 [reponame][targetName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/inspect\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.InspectReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Container inspect\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/ipv6option/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LogOption\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update docker daemon.json ipv6 option\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Updated the ipv6 option\",\n\t\t\t\t\t\"formatZH\": \"更新 ipv6 配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/item/stats\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerItemStats\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container stats size\"\n\t\t\t}\n\t\t},\n\t\t\"/containers/limit\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ResourceLimit\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container limits\"\n\t\t\t}\n\t\t},\n\t\t\"/containers/list\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOptions\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List containers\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/list/byimage\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOptions\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List containers by image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/list/stats\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerListStats\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container stats\"\n\t\t\t}\n\t\t},\n\t\t\"/containers/logoption/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LogOption\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update docker daemon.json log option\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Updated the log option\",\n\t\t\t\t\t\"formatZH\": \"更新日志配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/network\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Options\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List networks\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Network\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NetworkCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create network\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Network\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create container network [name]\",\n\t\t\t\t\t\"formatZH\": \"创建容器网络 name\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/network/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete network\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Network\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete container network [names]\",\n\t\t\t\t\t\"formatZH\": \"删除容器网络 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/network/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page networks\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Network\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOperation\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\",\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"container [operation] [names]\",\n\t\t\t\t\t\"formatZH\": \"容器 [names] 执行 [operation]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/prune\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerPrune\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"pruneType\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean container [pruneType]\",\n\t\t\t\t\t\"formatZH\": \"清理容器 [pruneType]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/rename\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerRename\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Rename Container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"newName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"rename container [name] =\\u003e [newName]\",\n\t\t\t\t\t\"formatZH\": \"容器重命名 [name] =\\u003e [newName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageRepoOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List image repos\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageRepoDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create image repo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create image repo [name]\",\n\t\t\t\t\t\"formatZH\": \"创建镜像仓库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete image repo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete image repo [name]\",\n\t\t\t\t\t\"formatZH\": \"删除镜像仓库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page image repos\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load repo status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageRepoUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update image repo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update image repo information [name]\",\n\t\t\t\t\t\"formatZH\": \"更新镜像仓库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageContainer\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page containers\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/search/log\": {\n\t\t\t\"get\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"容器名称\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"container\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"时间筛选\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"since\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"是否追踪\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"follow\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"显示行号\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"tail\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"是否显示时间\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"timestamp\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Container logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/stats/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"容器id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerStats\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Container stats\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/status\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load containers status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/template\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List compose templates\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create compose template\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create compose template [name]\",\n\t\t\t\t\t\"formatZH\": \"创建 compose 模版 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/template/batch\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateBatch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bacth compose template\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"batch import compose templates\",\n\t\t\t\t\t\"formatZH\": \"批量导入编排模版\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/template/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete compose template\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"compose_templates\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete compose template [names]\",\n\t\t\t\t\t\"formatZH\": \"删除 compose 模版 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/template/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page compose templates\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/template/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update compose template\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"compose_templates\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update compose template information [name]\",\n\t\t\t\t\t\"formatZH\": \"更新 compose 模版 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"image\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update container [name][image]\",\n\t\t\t\t\t\"formatZH\": \"更新容器 [name][image]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/upgrade\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerUpgrade\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upgrade container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\",\n\t\t\t\t\t\t\"image\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"upgrade container image [names][image]\",\n\t\t\t\t\t\"formatZH\": \"更新容器镜像 [names][image]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/users\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container users\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/volume\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Options\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List volumes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Volume\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.VolumeCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create volume\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Volume\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create container volume [name]\",\n\t\t\t\t\t\"formatZH\": \"创建容器存储卷 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/volume/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete volume\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Volume\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete container volume [names]\",\n\t\t\t\t\t\"formatZH\": \"删除容器存储卷 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/volume/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page volumes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Volume\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/captcha\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CaptchaResponse\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"Load captcha\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/login\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"安全入口 base64 加密串\",\n\t\t\t\t\t\t\"in\": \"header\",\n\t\t\t\t\t\t\"name\": \"EntranceCode\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Login\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UserLoginInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"User login\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/logout\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"User logout\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/mfalogin\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MFALogin\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"headers\": {\n\t\t\t\t\t\t\t\"EntranceCode\": {\n\t\t\t\t\t\t\t\t\"description\": \"安全入口\",\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UserLoginInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"User login with mfa\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/passkey/begin\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasskeyBeginResponse\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"User login with passkey\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/passkey/finish\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UserLoginInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"User login with passkey\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/setting\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SystemSetting\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"Get Setting For Login\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/backups\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create backup account [type]\",\n\t\t\t\t\t\"formatZH\": \"创建备份账号 [type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/backups/client/:clientType\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupClientInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load backup account base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/backups/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete backup account [name]\",\n\t\t\t\t\t\"formatZH\": \"删除备份账号 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/backups/refresh/token\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Refresh token\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/backups/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update backup account [types]\",\n\t\t\t\t\t\"formatZH\": \"更新备份账号 [types]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"command\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create quick command [name][command]\",\n\t\t\t\t\t\"formatZH\": \"创建快捷命令 [name][command]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/command\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List commands\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByIDs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"commands\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete quick command [names]\",\n\t\t\t\t\t\"formatZH\": \"删除快捷命令 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/export\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Export command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"export quick commands\",\n\t\t\t\t\t\"formatZH\": \"导出快速命令\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/import\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Import command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"import quick commands\",\n\t\t\t\t\t\"formatZH\": \"导入快速命令\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page commands\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/tree\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandTree\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Tree commands\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update quick command [name]\",\n\t\t\t\t\t\"formatZH\": \"更新快捷命令 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/groups\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create group [name][type]\",\n\t\t\t\t\t\"formatZH\": \"创建组 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/groups/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\t\t\t\"output_value\": \"type\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete group [type][name]\",\n\t\t\t\t\t\"formatZH\": \"删除组 [type][name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/groups/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByType\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List groups\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/groups/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update group [name][type]\",\n\t\t\t\t\t\"formatZH\": \"更新组 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create host\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"addr\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create host [name][addr]\",\n\t\t\t\t\t\"formatZH\": \"创建主机 [name][addr]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByIDs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete host\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"hosts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"addr\",\n\t\t\t\t\t\t\t\"output_value\": \"addrs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete host [addrs]\",\n\t\t\t\t\t\"formatZH\": \"删除主机 [addrs]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get host info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchPageWithGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page host\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/test/byid/:id\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Test host conn by host id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/test/byinfo\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostConnTest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Test host conn by info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/tree\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchForTree\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostTree\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host tree\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"addr\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update host [name][addr]\",\n\t\t\t\t\t\"formatZH\": \"更新主机信息 [name][addr]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/update/group\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeHostGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"hosts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"addr\",\n\t\t\t\t\t\t\t\"output_value\": \"addr\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"group\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"change host [addr] group =\\u003e [group]\",\n\t\t\t\t\t\"formatZH\": \"切换主机[addr]分组 =\\u003e [group]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/logs/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanLog\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean operation logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Logs\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"logType\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Clean the [logType] log information\",\n\t\t\t\t\t\"formatZH\": \"清空 [logType] 日志信息\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/logs/login\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchLgLogWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page login logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Logs\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/logs/operation\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchOpLogWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page operation logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Logs\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/script\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ScriptOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Add script\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"add script [name]\",\n\t\t\t\t\t\"formatZH\": \"添加脚本库脚本 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/script/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByIDs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete script\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"script_librarys\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete script [names]\",\n\t\t\t\t\t\"formatZH\": \"删除脚本库脚本 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/script/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchPageWithGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page script\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/script/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByTaskID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync script from remote\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"sync scripts\",\n\t\t\t\t\t\"formatZH\": \"同步脚本库脚本\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/script/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ScriptOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update script\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update script [name]\",\n\t\t\t\t\t\"formatZH\": \"更新脚本库脚本 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/api/config/generate/key\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"generate api key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"generate api key\",\n\t\t\t\t\t\"formatZH\": \"生成 API 接口密钥\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/api/config/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ApiInterfaceConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update api config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ipWhiteList\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update api config =\\u003e IP White List: [ipWhiteList]\",\n\t\t\t\t\t\"formatZH\": \"更新 API 接口配置 =\\u003e IP 白名单: [ipWhiteList]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/apps/store/config\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppstoreConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get appstore config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/apps/store/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppstoreUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update appstore config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/bind/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BindInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system bind info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ipv6\",\n\t\t\t\t\t\t\"bindAddress\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system bind info =\\u003e ipv6: [ipv6], 监听 IP: [bindAddress]\",\n\t\t\t\t\t\"formatZH\": \"修改系统监听信息 =\\u003e ipv6: [ipv6], 监听 IP: [bindAddress]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/by\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system setting by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/expired/handle\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasswordUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Reset system password expired\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"reset an expired Password\",\n\t\t\t\t\t\"formatZH\": \"重置过期密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/interface\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system address\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/memo\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load dashboard memo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MemoUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update dashboard memo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update dashboard memo\",\n\t\t\t\t\t\"formatZH\": \"更新仪表盘备忘录\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/menu/default\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Default menu\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Menu Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Init menu.\",\n\t\t\t\t\t\"formatZH\": \"初始化菜单\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/menu/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Hide advanced feature menu.\",\n\t\t\t\t\t\"formatZH\": \"隐藏高级功能菜单\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/mfa\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MfaCredential\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/mfa.Otp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mfa info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/mfa/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MfaCredential\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind mfa\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"bind mfa\",\n\t\t\t\t\t\"formatZH\": \"mfa 绑定\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/passkey/list\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasskeyInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List passkeys\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/passkey/register/begin\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasskeyRegisterRequest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasskeyBeginResponse\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Begin passkey registration\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/passkey/register/finish\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Finish passkey registration\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/passkey/{id}\": {\n\t\t\t\"delete\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete passkey\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/password/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasswordUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system password\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update system password\",\n\t\t\t\t\t\"formatZH\": \"修改系统密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/port/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system port\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"serverPort\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system port =\\u003e [serverPort]\",\n\t\t\t\t\t\"formatZH\": \"修改系统端口 =\\u003e [serverPort]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/proxy/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ProxyUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update proxy setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"proxyUrl\",\n\t\t\t\t\t\t\"proxyPort\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"set proxy [proxyPort]:[proxyPort].\",\n\t\t\t\t\t\"formatZH\": \"服务器代理配置 [proxyPort]:[proxyPort]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system setting info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/search/available\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system available status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/ssl/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download system cert\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/ssl/info\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSLInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system cert info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/ssl/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSLUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ssl\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system ssl =\\u003e [ssl]\",\n\t\t\t\t\t\"formatZH\": \"修改系统 ssl =\\u003e [ssl]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/terminal/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TerminalInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system terminal setting info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/terminal/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TerminalInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system terminal setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update system terminal setting\",\n\t\t\t\t\t\"formatZH\": \"修改系统终端配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system setting [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改系统配置 [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/upgrade\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpgradeInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load upgrade info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Upgrade\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upgrade\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"version\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"upgrade system =\\u003e [version]\",\n\t\t\t\t\t\"formatZH\": \"更新系统 =\\u003e [version]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/upgrade/notes\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Upgrade\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load release notes by version\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/upgrade/releases\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ReleasesNotes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load upgrade notes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create cronjob\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create cronjob [type][name]\",\n\t\t\t\t\t\"formatZH\": \"创建计划任务 [type][name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobBatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete cronjob\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete cronjob [names]\",\n\t\t\t\t\t\"formatZH\": \"删除计划任务 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/export\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByIDs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Export cronjob list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/group/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update cronjob group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update cronjob group [name]\",\n\t\t\t\t\t\"formatZH\": \"更新计划任务分组 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/handle\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Handle cronjob once\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"manually execute the cronjob [name]\",\n\t\t\t\t\t\"formatZH\": \"手动执行计划任务 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/import\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobImport\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Import cronjob list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/load/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load cronjob info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/next\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobSpec\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load cronjob spec time\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/records/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobClean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean job records\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"cronjobID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"cronjobID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean cronjob [name] records\",\n\t\t\t\t\t\"formatZH\": \"清空计划任务记录 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/records/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load Cronjob record log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/script/options\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ScriptOptions\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load script options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageCronjob\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page cronjobs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/search/records\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchRecord\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page job records\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobUpdateStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update cronjob status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"status\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"change the status of cronjob [name] to [status].\",\n\t\t\t\t\t\"formatZH\": \"修改计划任务 [name] 状态为 [status]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/stop\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Handle stop job\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update cronjob\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update cronjob [name]\",\n\t\t\t\t\t\"formatZH\": \"更新计划任务 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/app/launcher\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppLauncher\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load app launcher\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/app/launcher/option\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchByFilter\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LauncherOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load app launcher options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/app/launcher/show\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update app Launcher\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"app launcher [key] =\\u003e show: [value]\",\n\t\t\t\t\t\"formatZH\": \"首页应用 [key] =\\u003e 显示：[value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/base/:ioOption/:netOption\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"ioOption\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"netOption\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DashboardBase\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load dashboard base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/base/os\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OsInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load os info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/current/:ioOption/:netOption\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"ioOption\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"netOption\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DashboardCurrent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load dashboard current info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/current/node\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NodeCurrent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load dashboard current info for node\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/current/top/cpu\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Process\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load top cpu processes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/current/top/mem\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Process\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load top memory processes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/quick/change\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeQuicks\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update quick jump\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"change quick jump\",\n\t\t\t\t\t\"formatZH\": \"切换快速跳转\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/quick/option\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.QuickJump\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load quick jump options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/system/restart/:operation\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"operation\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"System restart\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlDBCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create mysql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create mysql database [name]\",\n\t\t\t\t\t\"formatZH\": \"创建 mysql 数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BindUser\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind user of mysql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"database\",\n\t\t\t\t\t\t\"username\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"bind mysql database [database] [username]\",\n\t\t\t\t\t\"formatZH\": \"绑定 mysql 数据库名 [database] [username]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/change/access\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeDBInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change mysql access\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update database [name] access\",\n\t\t\t\t\t\"formatZH\": \"更新数据库 [name] 访问权限\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/change/password\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeDBInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change mysql password\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update database [name] password\",\n\t\t\t\t\t\"formatZH\": \"更新数据库 [name] 密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/common/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DBBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Common\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/common/load/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load Database conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Common\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/common/update/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DBConfUpdateByFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update conf by upload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Common\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"database\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update the [type] [database] database configuration information\",\n\t\t\t\t\t\"formatZH\": \"更新 [type] 数据库 [database] 配置信息\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/db\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create database [name][type]\",\n\t\t\t\t\t\"formatZH\": \"创建远程数据库 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/:name\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"check if database [name][type] is connectable\",\n\t\t\t\t\t\"formatZH\": \"检测远程数据库 [name][type] 连接性\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"databases\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete database [names]\",\n\t\t\t\t\t\"formatZH\": \"删除远程数据库 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/del/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before delete remote database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/item/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseItem\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/list/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update database [name]\",\n\t\t\t\t\t\"formatZH\": \"更新远程数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlDBDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete mysql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete mysql database [name]\",\n\t\t\t\t\t\"formatZH\": \"删除 mysql 数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/del/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlDBDeleteCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before delete mysql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/description/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update mysql database description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"description\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"The description of the mysql database [name] is modified =\\u003e [description]\",\n\t\t\t\t\t\"formatZH\": \"mysql 数据库 [name] 描述信息修改 [description]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/format/options\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlFormatCollationOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List mysql database format collation options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/load\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlLoadDB\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mysql database from remote\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlDBCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create postgresql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create postgresql database [name]\",\n\t\t\t\t\t\"formatZH\": \"创建 postgresql 数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/:database/load\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlLoadDB\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load postgresql database from remote\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlBindUser\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind postgresql user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"username\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"bind postgresql database [name] user [username]\",\n\t\t\t\t\t\"formatZH\": \"绑定 postgresql 数据库 [name] 用户 [username]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlDBDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete postgresql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete postgresql database [name]\",\n\t\t\t\t\t\"formatZH\": \"删除 postgresql 数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/del/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlDBDeleteCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before delete postgresql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/description\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update postgresql database description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"description\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"The description of the postgresql database [name] is modified =\\u003e [description]\",\n\t\t\t\t\t\"formatZH\": \"postgresql 数据库 [name] 描述信息修改 [description]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/password\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeDBInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change postgresql password\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update database [name] password\",\n\t\t\t\t\t\"formatZH\": \"更新数据库 [name] 密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/privileges\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeDBInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change postgresql privileges\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"database\",\n\t\t\t\t\t\t\"username\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update [user] privileges of database [database]\",\n\t\t\t\t\t\"formatZH\": \"更新数据库 [database] 用户 [username] 权限\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlDBSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page postgresql databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LoadRedisStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisConf\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load redis conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/conf/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisConfUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update redis conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update the redis database configuration information\",\n\t\t\t\t\t\"formatZH\": \"更新 redis 数据库配置信息\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/install/cli\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Install redis-cli\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/password\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeRedisPass\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change redis password\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"change the password of the redis database\",\n\t\t\t\t\t\"formatZH\": \"修改 redis 数据库密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/persistence/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LoadRedisStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisPersistence\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load redis persistence conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/persistence/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisConfPersistenceUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update redis persistence conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"redis database persistence configuration update\",\n\t\t\t\t\t\"formatZH\": \"redis 数据库持久化配置更新\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LoadRedisStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load redis status info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/remote\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mysql remote access\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlDBSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page mysql databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mysql status info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/variables\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlVariables\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mysql variables info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/variables/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlVariablesUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update mysql variables\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"adjust mysql database performance parameters\",\n\t\t\t\t\t\"formatZH\": \"调整 mysql 数据库性能参数\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create dir or file [path]\",\n\t\t\t\t\t\"formatZH\": \"创建文件/文件夹 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/batch/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FilePathsCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.ExistFileInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch check file exist\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/batch/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileBatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch delete file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"paths\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Batch delete dir or file [paths]\",\n\t\t\t\t\t\"formatZH\": \"批量删除文件/文件夹 [paths]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/batch/role\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRoleReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch change file mode and owner\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"paths\",\n\t\t\t\t\t\t\"mode\",\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"group\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Batch change file mode and owner [paths] =\\u003e [mode]/[user]/[group]\",\n\t\t\t\t\t\"formatZH\": \"批量修改文件权限和用户/组 [paths] =\\u003e [mode]/[user]/[group]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FilePathCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check file exist\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/chunkdownload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileDownload\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Chunk Download file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Download file [name]\",\n\t\t\t\t\t\"formatZH\": \"下载文件 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/chunkupload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"file\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"ChunkUpload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/compress\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileCompress\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Compress file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Compress file [name]\",\n\t\t\t\t\t\"formatZH\": \"压缩文件 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/content\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileContentReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load file content\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Load file content [path]\",\n\t\t\t\t\t\"formatZH\": \"获取文件内容 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/convert\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileConvert\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Convert file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/convert/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Convert file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/decompress\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileDeCompress\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Decompress file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Decompress file [path]\",\n\t\t\t\t\t\"formatZH\": \"解压 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete dir or file [path]\",\n\t\t\t\t\t\"formatZH\": \"删除文件/文件夹 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/depth/size\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DirSizeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DepthDirSizeRes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Multi file size\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Multi file size [path]\",\n\t\t\t\t\t\"formatZH\": \"获取目录及其第一层子目录文件夹大小 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/download\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/favorite\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FavoriteCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.Favorite\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create favorite\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"收藏文件/文件夹 [path]\",\n\t\t\t\t\t\"formatZH\": \"收藏文件/文件夹 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/favorite/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FavoriteDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete favorite\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"favorites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"path\",\n\t\t\t\t\t\t\t\"output_value\": \"path\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete avorite [path]\",\n\t\t\t\t\t\"formatZH\": \"删除收藏 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/favorite/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List favorites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/mode\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change file mode\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"mode\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Change mode [paths] =\\u003e [mode]\",\n\t\t\t\t\t\"formatZH\": \"修改权限 [paths] =\\u003e [mode]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/mount\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DiskInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"system mount\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/move\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileMove\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Move file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"oldPaths\",\n\t\t\t\t\t\t\"newPath\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Move [oldPaths] =\\u003e [newPath]\",\n\t\t\t\t\t\"formatZH\": \"移动文件 [oldPaths] =\\u003e [newPath]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/owner\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRoleUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change file owner\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"group\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Change owner [paths] =\\u003e [user]/[group]\",\n\t\t\t\t\t\"formatZH\": \"修改用户/组 [paths] =\\u003e [user]/[group]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/preview\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileContentReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Preview file content\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Preview file content [path]\",\n\t\t\t\t\t\"formatZH\": \"预览文件内容 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/read\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileReadByLineReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileLineContent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Read file by Line\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/recycle/clear\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clear RecycleBin files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"清空回收站\",\n\t\t\t\t\t\"formatZH\": \"清空回收站\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/recycle/reduce\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RecycleBinReduce\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Reduce RecycleBin files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Reduce RecycleBin file [name]\",\n\t\t\t\t\t\"formatZH\": \"还原回收站文件 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/recycle/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List RecycleBin files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/recycle/status\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get RecycleBin status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/remark\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRemarkUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Set file remark\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/remarks\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRemarkBatch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileRemarksRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch get file remarks\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/rename\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRename\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change file name\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"oldName\",\n\t\t\t\t\t\t\"newName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Rename [oldName] =\\u003e [newName]\",\n\t\t\t\t\t\"formatZH\": \"重命名 [oldName] =\\u003e [newName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/save\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileEdit\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update file content\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update file content [path]\",\n\t\t\t\t\t\"formatZH\": \"更新文件内容 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileOption\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/size\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DirSizeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DirSizeRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load file size\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Load file size [path]\",\n\t\t\t\t\t\"formatZH\": \"获取文件夹大小 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/tree\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileOption\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileTree\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load files tree\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/upload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"file\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"file\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Upload file [path]/[file]\",\n\t\t\t\t\t\"formatZH\": \"上传文件 [path]/[file]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/upload/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.SearchUploadWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/user/group\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.UserGroupResponse\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"system user and group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/wget\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileWget\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileWgetRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Wget file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"url\",\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Download url =\\u003e [path]/[name]\",\n\t\t\t\t\t\"formatZH\": \"下载 url =\\u003e [path]/[name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/groups\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create group [name][type]\",\n\t\t\t\t\t\"formatZH\": \"创建组 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/groups/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\t\t\t\"output_value\": \"type\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete group [type][name]\",\n\t\t\t\t\t\"formatZH\": \"删除组 [type][name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/groups/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByType\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List groups\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/groups/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update group [name][type]\",\n\t\t\t\t\t\"formatZH\": \"更新组 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/components/{name}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"Component name to check (e.g., rsync, docker)\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"name\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.ComponentInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check if a system component exists\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/disks\": {\n\t\t\t\"get\": {\n\t\t\t\t\"description\": \"Get information about all disks including partitioned and unpartitioned disks\",\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.CompleteDiskInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get complete disk information\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Disk Management\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/disks/mount\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"description\": \"Mount partition to specified mount point\",\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"mount request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DiskMountRequest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"Disk mounted successfully\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Mount disk\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Disk Management\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"device\",\n\t\t\t\t\t\t\"mountPoint\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Mount disk [device] to [mountPoint]\",\n\t\t\t\t\t\"formatZH\": \"挂载磁盘 [device] 到 [mountPoint]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/disks/partition\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"description\": \"Create partition and format disk with specified filesystem\",\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"partition request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DiskPartitionRequest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"Partition created successfully\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Partition disk\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Disk Management\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"device\",\n\t\t\t\t\t\t\"filesystem\",\n\t\t\t\t\t\t\"mountPoint\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Partition disk [device] with filesystem [filesystem], mount point [mountPoint]\",\n\t\t\t\t\t\"formatZH\": \"对磁盘 [device] 进行分区，文件系统 [filesystem]，挂载点 [mountPoint]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/disks/unmount\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"description\": \"Unmount partition from mount point\",\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"unmount request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DiskUnmountRequest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"Disk unmounted successfully\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Unmount disk\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Disk Management\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"device\",\n\t\t\t\t\t\t\"mountPoint\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Unmount disk [device] from [mountPoint]\",\n\t\t\t\t\t\"formatZH\": \"卸载磁盘 [device] 从 [mountPoint]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/base\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FirewallBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load firewall base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/batch\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchRuleOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch operate rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/chain/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"load chain status with name\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.IptablesOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Apply/Unload/Init iptables filter\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] iptables filter firewall\",\n\t\t\t\t\t\"formatZH\": \"[operate] iptables filter 防火墙\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/rule/batch\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.IptablesBatchOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch operate iptables filter rules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/rule/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.IptablesRuleOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate iptables filter rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\",\n\t\t\t\t\t\t\"chain\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] filter rule to [chain]\",\n\t\t\t\t\t\"formatZH\": \"[operation] filter规则到 [chain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchPageWithType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"search iptables filter rules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/forward\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ForwardRuleOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate forward rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update port forward rules\",\n\t\t\t\t\t\"formatZH\": \"更新端口转发规则\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/ip\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AddrRuleOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Ip rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"strategy\",\n\t\t\t\t\t\t\"address\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create address rules [strategy][address]\",\n\t\t\t\t\t\"formatZH\": \"添加 ip 规则 [strategy] [address]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FirewallOperation\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate firewall\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] firewall\",\n\t\t\t\t\t\"formatZH\": \"[operation] 防火墙\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/port\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"port\",\n\t\t\t\t\t\t\"strategy\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create port rules [strategy][port]\",\n\t\t\t\t\t\"formatZH\": \"添加端口规则 [strategy] [port]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RuleSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page firewall rules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/update/addr\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AddrRuleUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Ip rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/update/description\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateFirewallDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update rule description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/update/port\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update port rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean monitor data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"clean monitor datas\",\n\t\t\t\t\t\"formatZH\": \"清空监控数据\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/gpu/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorGPUSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorGPUData\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load monitor data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorData\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load monitor data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/setting\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorSetting\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load monitor setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/setting/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorSettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update monitor setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update default monitor [name]-[value]\",\n\t\t\t\t\t\"formatZH\": \"修改默认监控网卡 [name]-[value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RootCertOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Generate host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"generate SSH secret\",\n\t\t\t\t\t\"formatZH\": \"生成 SSH 密钥 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert/delete\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ForceDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"delete SSH secret\",\n\t\t\t\t\t\"formatZH\": \"删除 SSH 密钥 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sycn host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"sync SSH secret\",\n\t\t\t\t\t\"formatZH\": \"同步 SSH 密钥 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RootCertOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"generate SSH secret\",\n\t\t\t\t\t\"formatZH\": \"生成 SSH 密钥 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host SSH conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/file/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHConf\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host SSH setting by file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update SSH conf [key]\",\n\t\t\t\t\t\"formatZH\": \"修改 SSH 配置文件 [key]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchSSHLog\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host SSH logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/log/export\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchSSHLog\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Export host SSH logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Operate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate SSH\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] SSH\",\n\t\t\t\t\t\"formatZH\": \"[operation] SSH \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host SSH setting info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host SSH setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"newValue\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update SSH setting [key] =\\u003e [newValue]\",\n\t\t\t\t\t\"formatZH\": \"修改 SSH 配置 [key] =\\u003e [newValue]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.HostToolReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.HostToolRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get tool status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.HostToolConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.HostToolConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get tool config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] tool config\",\n\t\t\t\t\t\"formatZH\": \"[operate] 主机工具配置文件 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/init\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.HostToolCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Host tool Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create [type] config\",\n\t\t\t\t\t\"formatZH\": \"创建 [type] 配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.HostToolReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate tool\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] [type]\",\n\t\t\t\t\t\"formatZH\": \"[operate] [type] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/supervisor/process\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.SupervisorProcessConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Supervisor process config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.SupervisorProcessConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Supervisor process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] process\",\n\t\t\t\t\t\"formatZH\": \"[operate] 守护进程 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/supervisor/process/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.SupervisorProcessFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Supervisor process config file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] Supervisor Process Config file\",\n\t\t\t\t\t\"formatZH\": \"[operate] Supervisor 进程文件 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/logs/system/files\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system log files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Logs\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/logs/tasks/executing/count\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get the number of executing tasks\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"TaskLog\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/logs/tasks/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchTaskLogReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page task logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"TaskLog\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load OpenResty conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/build\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxBuildReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Build OpenResty\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Build OpenResty\",\n\t\t\t\t\t\"formatZH\": \"构建 OpenResty\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/openresty/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxConfigFileUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update OpenResty conf by upload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Update nginx conf\",\n\t\t\t\t\t\"formatZH\": \"更新 nginx 配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/openresty/https\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxConfigRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get default HTTPs status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxDefaultHTTPSUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate default HTTPs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/modules\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxBuildConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get OpenResty modules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/modules/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxModuleUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update OpenResty module\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Update OpenResty module\",\n\t\t\t\t\t\"formatZH\": \"更新 OpenResty 模块\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/openresty/scope\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxScopeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxParam\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load partial OpenResty conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/status\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load OpenResty status info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxConfigUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update OpenResty conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update nginx conf [domain]\",\n\t\t\t\t\t\"formatZH\": \"更新 nginx 配置 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/process/listening\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Listening Process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Process\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/process/stop\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ProcessReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Stop Process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Process\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"PID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"结束进程 [PID]\",\n\t\t\t\t\t\"formatZH\": \"结束进程 [PID]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/process/{pid}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"PID\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"pid\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Process Info By PID\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Process\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.Runtime\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create runtime [name]\",\n\t\t\t\t\t\"formatZH\": \"创建运行环境 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.RuntimeDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete runtime [name]\",\n\t\t\t\t\t\"formatZH\": \"删除运行环境 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/installed/delete/check/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppResource\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/node/modules\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NodeModuleReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NodeModule\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Node modules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/node/modules/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NodeModuleReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Node modules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/node/package\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NodePackageReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.PackageScripts\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Node package scripts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Operate runtime [name]\",\n\t\t\t\t\t\"formatZH\": \"操作运行环境 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/:id/extensions\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.PHPExtensionRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get php runtime extension\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPConfigUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update runtime php conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[domain] PHP conf update\",\n\t\t\t\t\t\"formatZH\": \"[domain] PHP 配置修改\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/config/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.PHPConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load php runtime conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/container/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPContainerConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get PHP container config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/container/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPContainerConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update PHP container config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionsCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Extensions\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"PHP Extensions\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionsDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete Extensions\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"PHP Extensions\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/install\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionInstallReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Install php extension\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionsSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Extensions\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"PHP Extensions\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/uninstall\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionInstallReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"UnInstall php extension\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionsUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Extensions\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"PHP Extensions\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get php conf file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/fpm/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FPMConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update fpm config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/fpm/config/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FPMConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get fpm config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/fpm/status/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get PHP runtime status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPFileUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update php conf file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Nginx conf update [domain]\",\n\t\t\t\t\t\"formatZH\": \"php 配置修改 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/remark\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeRemark\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update runtime remark\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List runtimes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/supervisor/process\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPSupervisorProcessConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate supervisor process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/supervisor/process/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.SupervisorProcessConfig\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get supervisor process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/supervisor/process/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPSupervisorProcessFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate supervisor process file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync runtime status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update runtime [name]\",\n\t\t\t\t\t\"formatZH\": \"更新运行环境 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/basedir\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load local backup dir\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/description/save\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommonDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Save common description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/get/{key}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"key\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"key\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system setting by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system setting info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/search/available\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system available status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create system snapshot\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"from\",\n\t\t\t\t\t\t\"description\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create system backup [description] to [from]\",\n\t\t\t\t\t\"formatZH\": \"创建系统快照 [description] 到 [from]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotBatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete system backup\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete system backup [name]\",\n\t\t\t\t\t\"formatZH\": \"删除系统快照 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/description/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update snapshot description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"description\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"The description of the snapshot [name] is modified =\\u003e [description]\",\n\t\t\t\t\t\"formatZH\": \"快照 [name] 描述信息修改 [description]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/import\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotImport\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Import system snapshot\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"from\",\n\t\t\t\t\t\t\"names\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Sync system snapshots [names] from [from]\",\n\t\t\t\t\t\"formatZH\": \"从 [from] 同步系统快照 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/load\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotData\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system snapshot data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/recover\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Recover system backup\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Recover from system backup [name]\",\n\t\t\t\t\t\"formatZH\": \"从系统快照 [name] 恢复\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/recreate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Recreate system snapshot\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"recrete the snapshot [name]\",\n\t\t\t\t\t\"formatZH\": \"重试创建快照 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/rollback\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Rollback system backup\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Rollback from system backup [name]\",\n\t\t\t\t\t\"formatZH\": \"从系统快照 [name] 回滚\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageSnapshot\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page system snapshot\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/ssh\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Save local conn info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/ssh/check/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check local conn info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/ssh/conn\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHConnData\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load local conn\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/ssh/conn/default\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHDefaultConn\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update local is conn\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"defaultConn\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system default conn [defaultConn]\",\n\t\t\t\t\t\"formatZH\": \"本地终端默认连接 [defaultConn]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system setting [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改系统配置 [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create clam [name][path]\",\n\t\t\t\t\t\"formatZH\": \"创建扫描规则 [name][path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/base\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load clam base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete clam [names]\",\n\t\t\t\t\t\"formatZH\": \"删除扫描规则 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/file/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load clam file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/file/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateByNameAndFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update clam file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/handle\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Handle clam scan\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"handle clam scan [name]\",\n\t\t\t\t\t\"formatZH\": \"执行病毒扫描 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Operate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] FTP\",\n\t\t\t\t\t\"formatZH\": \"[operation] Clam\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/record/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean clam record\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean clam record [name]\",\n\t\t\t\t\t\"formatZH\": \"清空扫描报告 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/record/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamLogSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page clam record\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchClamWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/status/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamUpdateStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update clam status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"status\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"change the status of clam [name] to [status].\",\n\t\t\t\t\t\"formatZH\": \"修改扫描规则 [name] 状态为 [status]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update clam [name][path]\",\n\t\t\t\t\t\"formatZH\": \"修改扫描规则 [name][path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Clean\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean system\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Clean system junk files\",\n\t\t\t\t\t\"formatZH\": \"清理系统垃圾文件\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/base\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DeviceBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load device base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/check/dns\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check device DNS conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"load conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/byconf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateByNameAndFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device conf by file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update device conf [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改主机参数 [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/host\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device hosts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update device host [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改主机 Host [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/passwd\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangePasswd\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device passwd\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/swap\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SwapHelper\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device swap\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] device swap [path]\",\n\t\t\t\t\t\"formatZH\": \"[operate] 主机 swap [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/users\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load user list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/zone/options\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"list time zone options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/base\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Fail2BanBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load fail2ban base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/load/conf\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load fail2ban conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Operate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate fail2ban\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] Fail2ban\",\n\t\t\t\t\t\"formatZH\": \"[operation] Fail2ban\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/operate/sshd\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Fail2BanSet\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate sshd of fail2ban\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Fail2BanSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page fail2ban ip list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Fail2BanUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update fail2ban conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update fail2ban conf [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改 Fail2ban 配置 [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/update/byconf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateByFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update fail2ban conf by file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FtpCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create FTP [user][path]\",\n\t\t\t\t\t\"formatZH\": \"创建 FTP 账户 [user][path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/base\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FtpBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load FTP base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"ftps\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"user\",\n\t\t\t\t\t\t\t\"output_value\": \"users\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete FTP users [users]\",\n\t\t\t\t\t\"formatZH\": \"删除 FTP 账户 [users]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/log/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FtpLogSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load FTP operation log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Operate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate FTP\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] FTP\",\n\t\t\t\t\t\"formatZH\": \"[operation] FTP\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"sync FTP users\",\n\t\t\t\t\t\"formatZH\": \"同步 FTP 账户\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FtpUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update FTP [user][path]\",\n\t\t\t\t\t\"formatZH\": \"修改 FTP 账户 [user][path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/scan\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanData\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Scan system\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"scan System Junk Files\",\n\t\t\t\t\t\"formatZH\": \"扫描系统垃圾文件\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"alias\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website [alias]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 [alias]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website by id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/:id/config/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website nginx by id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Nginx\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/:id/https\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteHTTPS\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load https conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website HTTPS\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteHTTPSOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteHTTPS\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update https conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website HTTPS\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update website https [domain] conf\",\n\t\t\t\t\t\"formatZH\": \"更新网站 [domain] https 配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/acme\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteAcmeAccountCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteAcmeAccountDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website acme account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Acme\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"email\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website acme [email]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 acme [email]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/acme/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteResourceReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website acme account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Acme\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_acme_accounts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"email\",\n\t\t\t\t\t\t\t\"output_value\": \"email\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete website acme [email]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 acme [email]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/acme/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page website acme accounts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Acme\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/acme/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteAcmeAccountUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteAcmeAccountDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website acme account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Acme\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_acme_accounts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"email\",\n\t\t\t\t\t\t\t\"output_value\": \"email\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update acme [email]\",\n\t\t\t\t\t\"formatZH\": \"更新 acme [email]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/auths\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxAuthReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxAuthRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AuthBasic conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/auths/path\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxAuthReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxPathAuthRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AuthBasic conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/auths/path/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxPathAuthUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AuthBasic conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/auths/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxAuthUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AuthBasic conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/batch/group\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.BatchWebsiteGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch set website group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/batch/https\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.BatchWebsiteHttps\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch set HTTPS for websites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/batch/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.BatchWebsiteOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch operate websites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCACreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCACreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website ca\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website ca [name]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 ca [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCommonReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website ca\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete website ca [name]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 ca [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteResourceReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download CA file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"download ca file [name]\",\n\t\t\t\t\t\"formatZH\": \"下载 CA 证书文件 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/obtain\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCAObtain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Obtain SSL\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Obtain SSL [name]\",\n\t\t\t\t\t\"formatZH\": \"自签 SSL 证书 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/renew\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCAObtain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Obtain SSL\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Obtain SSL [name]\",\n\t\t\t\t\t\"formatZH\": \"自签 SSL 证书 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCASearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page website ca\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteCADTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website ca\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteInstallCheckReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsitePreInstallCheck\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before create website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxScopeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteNginxConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load nginx conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Nginx\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/config/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxConfigUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update nginx conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Nginx\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Nginx conf update [domain]\",\n\t\t\t\t\t\"formatZH\": \"nginx 配置修改 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/cors/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.CorsConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update CORS Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/cors/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.CorsConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get CORS Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/crosssite\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.CrossSiteAccessOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Cross Site Access\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/databases\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.Database\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ChangeDatabase\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change website database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/default/html/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteHtmlRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get default html\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/default/html/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteHtmlUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update default html\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update default html\",\n\t\t\t\t\t\"formatZH\": \"更新默认 html\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/default/server\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDefaultUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change default server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Change default server =\\u003e [domain]\",\n\t\t\t\t\t\"formatZH\": \"修改默认 server =\\u003e [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete website [domain]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dir\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCommonReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteDirConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website dir\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/dir/permission\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteUpdateDirPermission\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Site Dir permission\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update  domain [domain] dir permission\",\n\t\t\t\t\t\"formatZH\": \"更新网站 [domain] 目录权限\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dir/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteUpdateDir\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Site Dir\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update  domain [domain] dir\",\n\t\t\t\t\t\"formatZH\": \"更新网站 [domain] 目录\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dns\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDnsAccountCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website dns account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website DNS\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website dns [name]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 dns [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dns/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteResourceReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website dns account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website DNS\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_dns_accounts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete website dns [name]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 dns [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dns/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page website dns accounts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website DNS\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/dns/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDnsAccountUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website dns account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website DNS\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update website dns [name]\",\n\t\t\t\t\t\"formatZH\": \"更新网站 dns [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/domains\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomainCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDomain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Domain\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"domain\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create domain [domain]\",\n\t\t\t\t\t\"formatZH\": \"创建域名 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/domains/:websiteId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"websiteId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDomain\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website domains by websiteId\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Domain\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/domains/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomainDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Domain\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_domains\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete domain [domain]\",\n\t\t\t\t\t\"formatZH\": \"删除域名 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/domains/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomainUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Domain\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_domains\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update domain [domain]\",\n\t\t\t\t\t\"formatZH\": \"更新域名 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/exec/composer\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExecComposerReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Exec Composer\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCommonReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstream\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website upstreams\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs/create\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLBCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website upstream\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLBDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website upstream\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLBUpdateFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website upstream file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLBUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website upstream\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/leech\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxCommonReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxAntiLeechRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AntiLeech conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/leech/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxAntiLeechUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update AntiLeech\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/list\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteDTO\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List websites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLogReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteLog\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate website log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[domain][operate] logs\",\n\t\t\t\t\t\"formatZH\": \"[domain][operate] 日志\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/nginx/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteNginxUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website nginx conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Nginx\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[domain] Nginx conf update\",\n\t\t\t\t\t\"formatZH\": \"[domain] Nginx 配置修改\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] website [domain]\",\n\t\t\t\t\t\"formatZH\": \"[operate] 网站 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/options\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List website names\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/php/version\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsitePHPVersionReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update php version\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website PHP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"php version update [domain]\",\n\t\t\t\t\t\"formatZH\": \"php 版本变更 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyConfig\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get proxy conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies/delete\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyDel\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete proxy config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete domain [domain] proxy config [name]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 [domain] 反向代理配置 [name] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxProxyUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update proxy file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update domain [domain] proxy config file [name]\",\n\t\t\t\t\t\"formatZH\": \"修改网站 [domain] 反向代理配置文件 [name] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyStatusUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update proxy config status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"status\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update domain [domain] proxy config [name] status [status]\",\n\t\t\t\t\t\"formatZH\": \"修改网站 [domain] 反向代理配置 [name] 状态 [status] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update proxy conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update [operate] domain [domain] proxy config [name]\",\n\t\t\t\t\t\"formatZH\": \"修改 [operate] 网站 [domain] 反向代理配置 [name] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxy/clear\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clear Website proxy cache\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Clear nginx proxy cache\",\n\t\t\t\t\t\"formatZH\": \"清理 Openresty 代理缓存\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxy/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxProxyCacheUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"update website proxy cache config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxy/config/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxProxyCache\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website proxy cache config\"\n\t\t\t}\n\t\t},\n\t\t\"/websites/realip/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteRealIP\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Set Real IP\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Modify the real IP configuration of [domain] website\",\n\t\t\t\t\t\"formatZH\": \"修改 [domain] 网站真实IP配置 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/realip/config/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteRealIP\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Real IP Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/redirect\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxRedirectConfig\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get redirect conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/redirect/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxRedirectUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update redirect file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Nginx conf redirect file update [domain]\",\n\t\t\t\t\t\"formatZH\": \"更新重定向文件 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/redirect/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxRedirectReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update redirect conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update domain [domain] redirect config\",\n\t\t\t\t\t\"formatZH\": \"修改网站 [domain] 重定向配置 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/resource/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.Resource\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website resource\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/rewrite\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxRewriteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxRewriteRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get rewrite conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/rewrite/custom\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List custom rewrite\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.CustomRewriteOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate custom rewrite\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/rewrite/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxRewriteUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update rewrite conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Nginx conf rewrite update [domain]\",\n\t\t\t\t\t\"formatZH\": \"伪静态配置修改 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page websites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"primaryDomain\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website ssl [primaryDomain]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 ssl [primaryDomain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteSSLDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website ssl by id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteBatchDelReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete ssl [domain]\",\n\t\t\t\t\t\"formatZH\": \"删除 ssl [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteResourceReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download SSL  file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"download ssl file [domain]\",\n\t\t\t\t\t\"formatZH\": \"下载证书文件 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/list\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLListReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteSSLDTO\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/obtain\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLApply\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Apply  ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"apply ssl [domain]\",\n\t\t\t\t\t\"formatZH\": \"申请证书  [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/resolve\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDNSReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteDNSRes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Resolve website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteSSLDTO\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update ssl config [domain]\",\n\t\t\t\t\t\"formatZH\": \"更新证书设置 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/upload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLUpload\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Upload ssl [type]\",\n\t\t\t\t\t\"formatZH\": \"上传 ssl [type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/upload/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"multipart/form-data\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"type\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"type\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"description\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"description\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"sslID\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"sslID\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"privateKeyFile\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"privateKeyFile\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"certificateFile\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"certificateFile\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload SSL file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Upload ssl file [type]\",\n\t\t\t\t\t\"formatZH\": \"上传 ssl 文件 [type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/website/:websiteId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"websiteId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteSSLDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website ssl by website id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/stream/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.StreamUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Stream Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"primaryDomain\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update website [primaryDomain]\",\n\t\t\t\t\t\"formatZH\": \"更新网站 [primaryDomain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\t\"definitions\": {\n\t\t\"dto.AddrRuleOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"accept\",\n\t\t\t\t\t\t\"drop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"address\",\n\t\t\t\t\"operation\",\n\t\t\t\t\"strategy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AddrRuleUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"newRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.AddrRuleOperate\"\n\t\t\t\t},\n\t\t\t\t\"oldRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.AddrRuleOperate\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountCreateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"contextWindow\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxTokens\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberApiKey\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"apiKey\",\n\t\t\t\t\"name\",\n\t\t\t\t\"provider\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountDeleteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"contextWindow\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxTokens\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberApiKey\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"syncAgents\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"apiKey\",\n\t\t\t\t\"id\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountVerifyReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"apiKey\",\n\t\t\t\t\"provider\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentBrowserConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"defaultProfile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"executablePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"headless\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"noSandbox\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentBrowserConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentBrowserConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"defaultProfile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"headless\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"noSandbox\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"defaultProfile\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentChannelPairingApproveReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pairingCode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"feishu\",\n\t\t\t\t\t\t\"telegram\",\n\t\t\t\t\t\t\"discord\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"pairingCode\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentCreateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"agentType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"bridgePort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"contextWindow\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"maxTokens\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUIPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appVersion\",\n\t\t\t\t\"name\",\n\t\t\t\t\"webUIPort\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentDeleteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentDiscordConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"groupPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentDiscordConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentDiscordConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"groupPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"open\",\n\t\t\t\t\t\t\"allowlist\",\n\t\t\t\t\t\t\"disabled\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"dmPolicy\",\n\t\t\t\t\"groupPolicy\",\n\t\t\t\t\"token\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentFeishuConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appSecret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"botName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentFeishuConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentFeishuConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appSecret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"botName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"appId\",\n\t\t\t\t\"appSecret\",\n\t\t\t\t\"botName\",\n\t\t\t\t\"dmPolicy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentFeishuPairingApproveReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pairingCode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"pairingCode\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentItem\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"agentType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appInstallId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"bridgePort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"configPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"contextWindow\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxTokens\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"providerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"upgradable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"webUIPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentModelConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"accountId\",\n\t\t\t\t\"agentId\",\n\t\t\t\t\"model\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentOtherConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"userTimezone\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentOtherConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentOtherConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"userTimezone\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"userTimezone\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentPluginCheckReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"qqbot\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentPluginInstallReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"qqbot\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"taskID\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentPluginStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentQQBotConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientSecret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentQQBotConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentQQBotConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientSecret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"appId\",\n\t\t\t\t\"clientSecret\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentTelegramConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"botToken\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentTelegramConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentTelegramConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"botToken\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"botToken\",\n\t\t\t\t\"dmPolicy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentTokenResetReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ApiInterfaceConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"apiInterfaceStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiKeyValidityTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipWhiteList\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppConfigVersion\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"additionalProperties\": {},\n\t\t\t\t\"downloadCallBackUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppDefine\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppProperty\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"readMe\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"versions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppConfigVersion\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppInstallInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppLauncher\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detail\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.InstallDetail\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isInstall\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isRecommend\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppList\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.ExtraProperties\"\n\t\t\t\t},\n\t\t\t\t\"apps\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppDefine\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"valid\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"violations\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppProperty\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"Required\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"architectures\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"batchInstallSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"crossVersionUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deprecated\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.Locale\"\n\t\t\t\t},\n\t\t\t\t\"document\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"github\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryRequired\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"shortDescEn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescZh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"website\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppResource\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppVersion\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppstoreConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"uninstallDeleteBackup\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uninstallDeleteImage\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"upgradeBackup\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppstoreUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"UninstallDeleteImage\",\n\t\t\t\t\t\t\"UpgradeBackup\",\n\t\t\t\t\t\t\"UninstallDeleteBackup\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Disable\",\n\t\t\t\t\t\t\"Enable\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"scope\",\n\t\t\t\t\"status\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BackupClientInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"client_id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"client_secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirect_uri\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BackupOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accessKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"backupPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"bucket\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"credential\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isPublic\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberAuth\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"vars\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\",\n\t\t\t\t\"vars\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BackupOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isPublic\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BatchDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"force\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"names\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"names\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BatchDeleteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BatchRuleOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"rules\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleOperate\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BindInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"bindAddress\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipv6\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Enable\",\n\t\t\t\t\t\t\"Disable\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"bindAddress\",\n\t\t\t\t\"ipv6\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BindUser\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"db\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"permission\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"db\",\n\t\t\t\t\"password\",\n\t\t\t\t\"permission\",\n\t\t\t\t\"username\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CaptchaResponse\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"captchaID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"imagePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeDBInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"mysql-cluster\",\n\t\t\t\t\t\t\"postgresql-cluster\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"from\",\n\t\t\t\t\"type\",\n\t\t\t\t\"value\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeGroup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"groupID\",\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeHostGroup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"groupID\",\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangePasswd\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"passwd\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeQuicks\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"quicks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.QuickJump\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeRedisPass\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"freshIsActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"freshIsExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"freshVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alertCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"alertMethod\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alertTitle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"infectedDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"infectedStrategy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"removeInfected\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamLogSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"clamID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"endTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"startTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alertCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"alertMethod\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alertTitle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"infectedDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"infectedStrategy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamUpdateStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Clean\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"treeType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CleanData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backupClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"containerClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"downloadClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"systemClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"systemLogClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"uploadClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CleanLog\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"logType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"login\",\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"logType\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CleanTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isCheck\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isRecommend\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommandInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupBelong\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommandOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupBelong\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"command\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommandTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommonBackup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"args\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fileName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"app\",\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"website\",\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"mysql-cluster\",\n\t\t\t\t\t\t\"postgresql-cluster\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommonDescription\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detailType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isPinned\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommonRecover\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backupRecordID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"app\",\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"website\",\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"mysql-cluster\",\n\t\t\t\t\t\t\"postgresql-cluster\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"downloadAccountID\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"forcePull\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"edit\",\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"template\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"template\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"from\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeLogClean\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeOperation\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"force\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"up\",\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"stop\",\n\t\t\t\t\t\t\"down\",\n\t\t\t\t\t\t\"delete\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withFile\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeTemplateBatch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"templates\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateCreate\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"templates\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeTemplateCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeTemplateInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeTemplateUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detailPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"forcePull\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"name\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerCommit\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"author\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"comment\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newImageName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pause\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"containerID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerFileBatchDeleteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"minItems\": 1,\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"containerID\",\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerFileContent\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isBinary\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"truncated\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerFileInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isLink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"linkTo\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"containerID\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerItemStats\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"buildCacheReclaimable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"buildCacheUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerReclaimable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"imageReclaimable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"imageUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sizeRootFs\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sizeRw\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"volumeReclaimable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"volumeUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerListStats\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuTotalUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryCache\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"percpuUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"systemUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerNetwork\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ipv4\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipv6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"macAddr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"network\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRemove\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cmd\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"cpuShares\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dns\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"domainName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"entrypoint\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"forcePull\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"labels\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nanoCPUs\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"networks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerNetwork\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"openStdin\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"privileged\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"publishAllPorts\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tty\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.VolumeHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"workingDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"image\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerOperation\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"names\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"up\",\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"stop\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"kill\",\n\t\t\t\t\t\t\"pause\",\n\t\t\t\t\t\t\"unpause\",\n\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"names\",\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerOptions\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"state\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerPrune\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"pruneType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"container\",\n\t\t\t\t\t\t\"image\",\n\t\t\t\t\t\t\"volume\",\n\t\t\t\t\t\t\"network\",\n\t\t\t\t\t\t\"buildcache\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withTagAll\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"pruneType\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerRename\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"newName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerStats\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cache\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"ioRead\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"ioWrite\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"networkRX\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"networkTX\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"shotTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"composeCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"composeTemplateCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"created\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dead\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"exited\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"imageCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"networkCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"paused\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"removing\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"repoCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"restarting\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"running\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"volumeCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerUpgrade\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forcePull\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"names\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"image\",\n\t\t\t\t\"names\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobBatchDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cleanData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cleanRemoteData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobClean\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cleanData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cleanRemoteData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cronjobID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"cronjobID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobImport\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cronjobs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobTrans\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alertCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"alertMethod\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alertTitle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"args\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"exclusionRules\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"executor\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ignoreErr\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"retainCopies\": {\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"retryTimes\": {\n\t\t\t\t\t\"minimum\": 0,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"scopes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scriptID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"scriptMode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"snapshotRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotRule\"\n\t\t\t\t},\n\t\t\t\t\"sourceAccountIDs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sourceDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specCustom\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"website\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"spec\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobSpec\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"spec\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobTrans\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alertCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"alertMethod\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alertTitle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apps\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TransHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"args\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbName\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TransHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadAccount\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"exclusionRules\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"executor\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ignoreErr\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"retainCopies\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"retryTimes\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scriptMode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scriptName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"snapshotRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotTransHelper\"\n\t\t\t\t},\n\t\t\t\t\"sourceAccounts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"sourceDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specCustom\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websites\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobUpdateStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"status\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DBBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DBConfUpdateByFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"mysql-cluster\",\n\t\t\t\t\t\t\"postgresql-cluster\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DaemonJsonConf\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cgroupDriver\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"experimental\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"fixedCidrV6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"insecureRegistries\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"ip6Tables\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"iptables\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ipv6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSwarm\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"liveRestore\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"logMaxFile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logMaxSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"registryMirrors\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DaemonJsonUpdateByFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DashboardBase\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appInstalledNumber\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuCores\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuLogicalCores\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuMhz\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuModelName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cronjobNumber\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"currentInfo\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.DashboardCurrent\"\n\t\t\t\t},\n\t\t\t\t\"databaseNumber\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipV4Addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"kernelArch\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"kernelVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"os\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platform\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platformFamily\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platformVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"prettyDistro\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"quickJump\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.QuickJump\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"systemProxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"virtualizationSystem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteNumber\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DashboardCurrent\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cpuDetailedPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"cpuPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"cpuTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuUsed\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"diskData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DiskInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"gpuData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GPUInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"ioCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ioReadBytes\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ioReadTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ioWriteBytes\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ioWriteTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"load1\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load15\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load5\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"loadUsagePercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryCache\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryFree\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryShard\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"netBytesRecv\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"netBytesSent\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"procs\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"shotTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"timeSinceUptime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"topCPUItems\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Process\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"topMemItems\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Process\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"uptime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"xpuData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.XPUInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DataTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isCheck\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDisable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isLocal\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"relationItemID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"initialDB\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"rootCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipVerify\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"from\",\n\t\t\t\t\"name\",\n\t\t\t\t\"type\",\n\t\t\t\t\"username\",\n\t\t\t\t\"version\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"initialDB\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"rootCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipVerify\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseItem\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"initialDB\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"rootCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipVerify\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\",\n\t\t\t\t\"username\",\n\t\t\t\t\"version\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DeviceBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dns\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"localTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ntp\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"swapDetails\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SwapHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"timeZone\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DiskInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"free\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"inodesFree\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"inodesTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"inodesUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"inodesUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"usedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DockerOperation\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"stop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DockerStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DownloadRecord\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"downloadAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"fileDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fileName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"downloadAccountID\",\n\t\t\t\t\"fileDir\",\n\t\t\t\t\"fileName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ExtraHost\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ip\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ExtraProperties\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Tag\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Fail2BanBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"banAction\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"banTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"findTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isEnable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"logPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxRetry\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Fail2BanSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"banned\",\n\t\t\t\t\t\t\"ignore\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"status\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Fail2BanSet\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ips\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"banned\",\n\t\t\t\t\t\t\"ignore\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Fail2BanUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"port\",\n\t\t\t\t\t\t\"bantime\",\n\t\t\t\t\t\t\"findtime\",\n\t\t\t\t\t\t\"maxretry\",\n\t\t\t\t\t\t\"banaction\",\n\t\t\t\t\t\t\"logpath\",\n\t\t\t\t\t\t\"port\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FilePath\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FirewallBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isBind\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isInit\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pingStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FirewallOperation\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"stop\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"disableBanPing\",\n\t\t\t\t\t\t\"enableBanPing\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withDockerRestart\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ForBuckets\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accessKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"credential\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"vars\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"credential\",\n\t\t\t\t\"type\",\n\t\t\t\t\"vars\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ForceDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ForwardRuleOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"rules\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\"interface\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"num\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"operation\": {\n\t\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"port\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"protocol\": {\n\t\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\t\"tcp\",\n\t\t\t\t\t\t\t\t\t\"udp\",\n\t\t\t\t\t\t\t\t\t\"tcp/udp\"\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"targetIP\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"targetPort\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\"operation\",\n\t\t\t\t\t\t\t\"port\",\n\t\t\t\t\t\t\t\"protocol\",\n\t\t\t\t\t\t\t\"targetPort\"\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FtpBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FtpCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"password\",\n\t\t\t\t\"path\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FtpLogSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FtpUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"password\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GPUInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"fanSpeed\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuUtil\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"index\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxPowerLimit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memTotal\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memUsed\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsage\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"performanceState\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"powerDraw\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"powerUsage\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"productName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"temperature\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GPUProcess\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"pid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"processName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"usedMemory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GroupCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GroupSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GroupUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isDefault\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostConnTest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"authMode\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"password\",\n\t\t\t\t\t\t\"key\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"maximum\": 65535,\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"addr\",\n\t\t\t\t\"port\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"host\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ip\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"authMode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupBelong\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberPassword\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"authMode\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"password\",\n\t\t\t\t\t\t\"key\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"maximum\": 65535,\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberPassword\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"addr\",\n\t\t\t\t\"port\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TreeChild\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageBuild\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"args\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"dockerfile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"dockerfile\",\n\t\t\t\t\"from\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isPinned\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isUsed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageLoad\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImagePull\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"imageName\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"repoID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"imageName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImagePush\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"repoID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"tagName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"repoID\",\n\t\t\t\t\"tagName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageRepoDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageRepoOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"downloadUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageRepoUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"auth\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"downloadUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageSave\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tagName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"path\",\n\t\t\t\t\"tagName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageTag\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"sourceID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"sourceID\",\n\t\t\t\t\"tags\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.InspectReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.InstallDetail\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"httpPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"httpsPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"installID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.IptablesBatchOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"rules\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.IptablesRuleOp\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.IptablesOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"1PANEL_INPUT\",\n\t\t\t\t\t\t\"1PANEL_OUTPUT\",\n\t\t\t\t\t\t\"1PANEL_BASIC\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"init-base\",\n\t\t\t\t\t\t\"init-forward\",\n\t\t\t\t\t\t\"init-advance\",\n\t\t\t\t\t\t\"bind-base\",\n\t\t\t\t\t\t\"unbind-base\",\n\t\t\t\t\t\t\"bind\",\n\t\t\t\t\t\t\"unbind\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.IptablesRuleOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"chain\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"1PANEL_BASIC\",\n\t\t\t\t\t\t\"1PANEL_BASIC_BEFORE\",\n\t\t\t\t\t\t\"1PANEL_INPUT\",\n\t\t\t\t\t\t\"1PANEL_OUTPUT\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dstIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dstPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"srcIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"srcPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"accept\",\n\t\t\t\t\t\t\"drop\",\n\t\t\t\t\t\t\"reject\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"chain\",\n\t\t\t\t\"operation\",\n\t\t\t\t\"strategy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.LauncherOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isShow\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.LoadRedisStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Locale\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"en\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"es-es\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ja\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ko\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ms\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pt-br\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ru\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"zh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"zh-hant\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.LogOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"logMaxFile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logMaxSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Login\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"captcha\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"captchaID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"language\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"zh\",\n\t\t\t\t\t\t\"en\",\n\t\t\t\t\t\t\"zh-Hant\",\n\t\t\t\t\t\t\"ko\",\n\t\t\t\t\t\t\"ja\",\n\t\t\t\t\t\t\"ru\",\n\t\t\t\t\t\t\"ms\",\n\t\t\t\t\t\t\"pt-BR\",\n\t\t\t\t\t\t\"tr\",\n\t\t\t\t\t\t\"es-ES\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"language\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MFALogin\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"code\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"code\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MemoUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"maxLength\": 500,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MfaCredential\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"code\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"interval\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"code\",\n\t\t\t\t\"interval\",\n\t\t\t\t\"secret\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"date\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"param\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"items\": {},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorGPUData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"date\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"gpuProcesses\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GPUProcess\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"gpuValue\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"memoryPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"memoryTotal\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsed\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"powerPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"powerTotal\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"powerUsed\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"processCount\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"speedValue\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"temperatureValue\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorGPUSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"endTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"productName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"startTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"endTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"io\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"network\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"param\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"all\",\n\t\t\t\t\t\t\"cpu\",\n\t\t\t\t\t\t\"memory\",\n\t\t\t\t\t\t\"load\",\n\t\t\t\t\t\t\"io\",\n\t\t\t\t\t\t\"network\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"startTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"param\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorSetting\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"defaultIO\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultNetwork\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorInterval\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorStoreDays\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorSettingUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"MonitorStatus\",\n\t\t\t\t\t\t\"MonitorStoreDays\",\n\t\t\t\t\t\t\"MonitorInterval\",\n\t\t\t\t\t\t\"DefaultNetwork\",\n\t\t\t\t\t\t\"DefaultIO\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlDBCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"collation\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"format\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"permission\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"format\",\n\t\t\t\t\"from\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\",\n\t\t\t\t\"permission\",\n\t\t\t\t\"username\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlDBDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"mysql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlDBDeleteCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"mysql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlDBSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlFormatCollationOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"collations\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"format\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlLoadDB\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"mysql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"from\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"Aborted_clients\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Aborted_connects\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Bytes_received\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Bytes_sent\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Com_commit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Com_rollback\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Connections\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Created_tmp_disk_tables\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Created_tmp_tables\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"File\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Innodb_buffer_pool_pages_dirty\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Innodb_buffer_pool_read_requests\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Innodb_buffer_pool_reads\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Key_read_requests\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Key_reads\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Key_write_requests\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Key_writes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Max_used_connections\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Open_tables\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Opened_files\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Opened_tables\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Position\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Qcache_hits\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Qcache_inserts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Questions\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Run\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Select_full_join\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Select_range_check\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Sort_merge_passes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Table_locks_waited\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Threads_cached\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Threads_connected\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Threads_created\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Threads_running\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Uptime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlVariables\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"binlog_cache_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"innodb_buffer_pool_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"innodb_log_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"join_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"key_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"long_query_time\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"max_connections\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"max_heap_table_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"query_cache_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"query_cache_type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"read_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"read_rnd_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"slow_query_log\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sort_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"table_open_cache\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"thread_cache_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"thread_stack\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tmp_table_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlVariablesUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"mysql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"variables\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlVariablesUpdateHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlVariablesUpdateHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"param\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NetworkCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"auxAddress\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"auxAddressV6\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"driver\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gateway\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gatewayV6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipRange\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipRangeV6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipv4\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ipv6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"labels\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"options\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"subnet\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"subnetV6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"driver\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NginxAuth\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NginxKey\": {\n\t\t\t\"enum\": [\n\t\t\t\t\"index\",\n\t\t\t\t\"limit-conn\",\n\t\t\t\t\"ssl\",\n\t\t\t\t\"cache\",\n\t\t\t\t\"http-per\",\n\t\t\t\t\"proxy-cache\"\n\t\t\t],\n\t\t\t\"type\": \"string\",\n\t\t\t\"x-enum-varnames\": [\n\t\t\t\t\"Index\",\n\t\t\t\t\"LimitConn\",\n\t\t\t\t\"SSL\",\n\t\t\t\t\"CACHE\",\n\t\t\t\t\"HttpPer\",\n\t\t\t\t\"ProxyCache\"\n\t\t\t]\n\t\t},\n\t\t\"dto.NginxUpstream\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NginxUpstreamServer\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"failTimeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"failTimeoutUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"flag\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxConns\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxFails\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"server\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"weight\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NodeCurrent\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cpuDetailedPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"cpuTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuUsed\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load1\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load15\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load5\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"loadUsagePercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaBindDomain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appInstallID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipList\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appInstallID\",\n\t\t\t\t\"domain\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaBindDomainReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appInstallID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appInstallID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaBindDomainRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"allowIPs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"connUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaModelDropList\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaModelName\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Operate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByID\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByIDs\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByName\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByTaskID\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByType\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperationWithName\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperationWithNameAndType\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Options\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"option\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OsInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"diskSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"kernelArch\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"kernelVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"os\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platform\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platformFamily\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"prettyDistro\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageContainer\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"excludeAppStore\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"filters\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\"state\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"state\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"all\",\n\t\t\t\t\t\t\"created\",\n\t\t\t\t\t\t\"running\",\n\t\t\t\t\t\t\"paused\",\n\t\t\t\t\t\t\"restarting\",\n\t\t\t\t\t\t\"removing\",\n\t\t\t\t\t\t\"exited\",\n\t\t\t\t\t\t\"dead\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"state\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageCronjob\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupIDs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"status\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageImage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"size\",\n\t\t\t\t\t\t\"tags\",\n\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\"isUsed\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageResult\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"items\": {},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageSnapshot\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PasskeyBeginResponse\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"publicKey\": {},\n\t\t\t\t\"sessionId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PasskeyInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastUsedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PasskeyRegisterRequest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PasswordUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"newPassword\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"oldPassword\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"newPassword\",\n\t\t\t\t\"oldPassword\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PortHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hostPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PortRuleOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"chain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"tcp\",\n\t\t\t\t\t\t\"udp\",\n\t\t\t\t\t\t\"tcp/udp\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"accept\",\n\t\t\t\t\t\t\"drop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\",\n\t\t\t\t\"port\",\n\t\t\t\t\"protocol\",\n\t\t\t\t\"strategy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PortRuleUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"newRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleOperate\"\n\t\t\t\t},\n\t\t\t\t\"oldRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleOperate\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PortUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"serverPort\": {\n\t\t\t\t\t\"maximum\": 65535,\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"serverPort\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlBindUser\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"superUser\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\",\n\t\t\t\t\"username\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlDBCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"format\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"superUser\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"from\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\",\n\t\t\t\t\"username\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlDBDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"postgresql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlDBDeleteCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"postgresql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlDBSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlLoadDB\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"postgresql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"from\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Process\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cmd\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"percent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"pid\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ProviderInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"displayName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"models\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ProviderModelInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ProviderModelInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ProxyUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"proxyDocker\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"proxyPasswd\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyPasswdKeep\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyUser\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withDockerRestart\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.QuickJump\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isShow\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"router\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"title\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RecordFileSize\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RecordSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RecordSearchByCronjob\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cronjobID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"cronjobID\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisConf\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxclients\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxmemory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"requirepass\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisConfPersistenceUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appendfsync\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appendonly\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"save\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"aof\",\n\t\t\t\t\t\t\"rbd\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"dbType\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisConfUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxclients\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxmemory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"dbType\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisPersistence\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appendfsync\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appendonly\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"save\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"connected_clients\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"instantaneous_ops_per_sec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keyspace_hits\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keyspace_misses\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"latest_fork_usec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mem_fragmentation_ratio\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tcp_port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"total_commands_processed\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"total_connections_received\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uptime_in_days\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used_memory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used_memory_peak\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used_memory_rss\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ReleasesNotes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fixCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"newCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"optimizationCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ResourceLimit\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cpu\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RootCertOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"encryptionMode\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"rsa\",\n\t\t\t\t\t\t\"ed25519\",\n\t\t\t\t\t\t\"ecdsa\",\n\t\t\t\t\t\t\"dsa\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"publicKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"encryptionMode\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RuleSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHConf\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHConnData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"authMode\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"password\",\n\t\t\t\t\t\t\"key\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"localSSHConnShow\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"maximum\": 65535,\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"addr\",\n\t\t\t\t\"port\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHDefaultConn\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"defaultConn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withReset\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoStart\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"currentUser\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"listenAddress\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passwordAuthentication\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"permitRootLogin\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pubkeyAuthentication\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"useDNS\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newValue\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"oldValue\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSLInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rootPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSLUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Enable\",\n\t\t\t\t\t\t\"Disable\",\n\t\t\t\t\t\t\"Mux\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sslType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"self\",\n\t\t\t\t\t\t\"select\",\n\t\t\t\t\t\t\"import\",\n\t\t\t\t\t\t\"import-paste\",\n\t\t\t\t\t\t\"import-local\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ssl\",\n\t\t\t\t\"sslType\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ScriptOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groups\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isInteractive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ScriptOptions\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchByFilter\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"filter\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchClamWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"status\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchForSize\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cronjobID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchForTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchLgLogWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ip\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchOpLogWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"node\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchPageWithGroup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchPageWithType\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchRecord\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cronjobID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"endTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"startTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchSSHLog\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"Status\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Success\",\n\t\t\t\t\t\t\"Failed\",\n\t\t\t\t\t\t\"All\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"Status\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchTaskLogReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SettingInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appStoreLastModified\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appStoreSyncStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appStoreVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultIO\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultNetwork\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerSockPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fileRecycleBin\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastCleanData\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastCleanSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastCleanTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"localTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorInterval\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorStoreDays\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ntpSite\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"systemIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"systemVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeZone\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SettingUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotBatchDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"deleteWithFile\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"backupData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ignoreFiles\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"interruptStep\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"panelData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sourceAccountIDs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"withDockerConf\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withLoginLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withMonitorData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withOperationLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withSystemLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withTaskLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"downloadAccountID\",\n\t\t\t\t\"sourceAccountIDs\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"backupData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"ignoreFiles\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"panelData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"withDockerConf\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withLoginLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withMonitorData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withOperationLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withSystemLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withTaskLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotImport\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backupAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"names\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotRecover\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isNew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"reDownload\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotRule\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ignoreAppIDs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"withImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotTransHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ignoreApps\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TransHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"withImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SwapHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isNew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SystemSetting\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDemo\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isIntl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"language\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Tag\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"locales\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.Locale\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.TerminalInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backgroundColor\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cursorBlink\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cursorStyle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fontFamily\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fontSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"foregroundColor\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"letterSpacing\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lineHeight\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scrollSensitivity\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scrollback\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.TransHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.TreeChild\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpdateByFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpdateByNameAndFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpdateDescription\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpdateFirewallDescription\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"chain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dstIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dstPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"srcIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"srcPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"accept\",\n\t\t\t\t\t\t\"drop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"strategy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Upgrade\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"version\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpgradeInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"latestVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"releaseNote\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"testVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UploadForRecover\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"filePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"targetDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UserLoginInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"mfaStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.VolumeCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"driver\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"labels\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"options\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"driver\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.VolumeHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shared\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sourceDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.XPUInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"deviceID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"deviceName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsed\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memoryUtil\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"power\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"temperature\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"files.FileInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"extension\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favoriteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"gid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isDetail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isHidden\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSymlink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"itemTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/files.FileInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"linkPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mimeType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updateTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"mfa.Otp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"qrImage\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.App\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"architectures\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"batchInstallSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"crossVersionUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"document\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"github\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryRequired\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"readMe\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"required\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"requiredPanelVersion\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescEn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescZh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"website\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.AppIgnoreUpgrade\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.AppInstall\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"app\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.App\"\n\t\t\t\t},\n\t\t\t\t\"appDetailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"httpPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"httpsPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"param\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serviceName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sortOrder\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.Favorite\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isTxt\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.Runtime\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"workDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.Website\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"IPV6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"accessLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appInstallId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultServer\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDomain\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"errorLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ftpId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"parentWebsiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rewrite\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"runtimeID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"siteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamPorts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webSiteGroupId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"webSiteSSL\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteSSL\"\n\t\t\t\t},\n\t\t\t\t\"webSiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.WebsiteAcmeAccount\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"caDirURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabHmacKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabKid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"email\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"useEAB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"useProxy\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.WebsiteDnsAccount\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.WebsiteDomain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.WebsiteSSL\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteAcmeAccount\"\n\t\t\t\t},\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"caId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"certPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"disableCNAME\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDnsAccount\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isIP\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"masterSslId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver1\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver2\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organization\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipDNS\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"startDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websites\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/model.Website\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppConfigUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"installID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"installID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppIgnoreUpgradeReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"all\",\n\t\t\t\t\t\t\"version\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appID\",\n\t\t\t\t\"scope\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstallCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"appDetailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuConfig\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"always\",\n\t\t\t\t\t\t\"unless-stopped\",\n\t\t\t\t\t\t\"no\",\n\t\t\t\t\t\t\"on-failure\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"services\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appDetailId\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstalledInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstalledOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"backupId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deleteDB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deleteImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"detailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"installId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"installId\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstalledSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"all\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"checkUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sync\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"unused\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"update\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstalledUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuConfig\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"installId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"always\",\n\t\t\t\t\t\t\"unless-stopped\",\n\t\t\t\t\t\t\"no\",\n\t\t\t\t\t\t\"on-failure\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"installId\",\n\t\t\t\t\"params\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"showCurrentArch\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.BatchWebsiteGroup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"groupID\",\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.BatchWebsiteHttps\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"SSLProtocol\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificatePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hsts\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hstsIncludeSubDomains\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"http3\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"HTTPSOnly\",\n\t\t\t\t\t\t\"HTTPAlso\",\n\t\t\t\t\t\t\"HTTPToHTTPS\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpsPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"importType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"existed\",\n\t\t\t\t\t\t\"auto\",\n\t\t\t\t\t\t\"manual\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\",\n\t\t\t\t\"taskID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.BatchWebsiteOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\",\n\t\t\t\t\"operate\",\n\t\t\t\t\"taskID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ChangeDatabase\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"databaseID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"databaseType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.CorsConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"allowCredentials\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowHeaders\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowMethods\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowOrigins\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cors\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"preflight\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.CorsConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"allowCredentials\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowHeaders\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowMethods\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowOrigins\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cors\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"preflight\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.CrossSiteAccessOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Enable\",\n\t\t\t\t\t\t\"Disable\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.CustomRewriteOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"create\",\n\t\t\t\t\t\t\"delete\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.DirSizeReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.DiskMountRequest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoMount\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"filesystem\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"ext4\",\n\t\t\t\t\t\t\"xfs\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"noFail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"device\",\n\t\t\t\t\"filesystem\",\n\t\t\t\t\"mountPoint\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.DiskPartitionRequest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoMount\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"filesystem\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"ext4\",\n\t\t\t\t\t\t\"xfs\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"device\",\n\t\t\t\t\"filesystem\",\n\t\t\t\t\"mountPoint\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.DiskUnmountRequest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"mountPoint\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.Environment\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ExecComposerReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"extCommand\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mirror\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"command\",\n\t\t\t\t\"dir\",\n\t\t\t\t\"mirror\",\n\t\t\t\t\"taskID\",\n\t\t\t\t\"user\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ExposedPort\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hostPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ExtraHost\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ip\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FPMConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"params\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FavoriteCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FavoriteDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileBatchDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileCompress\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dst\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"files\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"replace\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"dst\",\n\t\t\t\t\"files\",\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileContentReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDetail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileConvert\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"extension\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"inputFile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"outputFormat\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"extension\",\n\t\t\t\t\"inputFile\",\n\t\t\t\t\"outputFormat\",\n\t\t\t\t\"path\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isLink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSymlink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"linkPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sub\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileDeCompress\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dst\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"dst\",\n\t\t\t\t\"path\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileDownload\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"compress\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"paths\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileEdit\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileMove\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cover\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"coverPaths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"oldPaths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"newPath\",\n\t\t\t\t\"oldPaths\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containSub\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expand\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDetail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"search\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"showHidden\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"sortBy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sortOrder\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FilePathCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withInit\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FilePathsCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileReadByLineReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"latest\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"resourceID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskOperate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRemarkBatch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRemarkUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRename\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"newName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"oldName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"newName\",\n\t\t\t\t\"oldName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRoleReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"sub\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"group\",\n\t\t\t\t\"mode\",\n\t\t\t\t\"paths\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRoleUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sub\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"group\",\n\t\t\t\t\"path\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileWget\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ignoreCertificate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"path\",\n\t\t\t\t\"url\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.HostToolConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"get\",\n\t\t\t\t\t\t\"set\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"supervisord\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.HostToolCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"configPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serviceName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.HostToolReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"status\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"stop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"supervisord\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpBindDomain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipList\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"domain\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpBindDomainUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ipList\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"outputTransport\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamableHttpPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"command\",\n\t\t\t\t\"name\",\n\t\t\t\t\"outputTransport\",\n\t\t\t\t\"port\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sync\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"outputTransport\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamableHttpPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"command\",\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"outputTransport\",\n\t\t\t\t\"port\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NewAppInstall\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuConfig\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"always\",\n\t\t\t\t\t\t\"unless-stopped\",\n\t\t\t\t\t\t\"no\",\n\t\t\t\t\t\t\"on-failure\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxAntiLeechUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"blocked\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cache\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cacheTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheUint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"extends\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logEnable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"noneRef\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"return\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serverNames\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxAuthReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxAuthUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxBuildReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"mirror\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"mirror\",\n\t\t\t\t\"taskID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxCommonReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxConfigFileUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxConfigUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"update\",\n\t\t\t\t\t\t\"delete\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxKey\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxDefaultHTTPSUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"enable\",\n\t\t\t\t\t\t\"disable\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslRejectHandshake\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxModuleUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"create\",\n\t\t\t\t\t\t\"delete\",\n\t\t\t\t\t\t\"update\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"packages\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxPathAuthUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxProxyCacheUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cacheExpire\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheExpireUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cacheLimit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheLimitUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"open\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shareCache\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"shareCacheUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"cacheExpire\",\n\t\t\t\t\"cacheExpireUnit\",\n\t\t\t\t\"cacheLimit\",\n\t\t\t\t\"cacheLimitUnit\",\n\t\t\t\t\"shareCache\",\n\t\t\t\t\"shareCacheUnit\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxProxyUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxRedirectReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"keepPath\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirect\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirectRoot\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\",\n\t\t\t\t\"redirect\",\n\t\t\t\t\"target\",\n\t\t\t\t\"type\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxRedirectUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxRewriteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxRewriteUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxScopeReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxKey\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"scope\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NodeModuleReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NodePackageReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPConfigUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"disableFunctions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxExecutionTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uploadMaxSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"scope\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPContainerConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExposedPort\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionInstallReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ID\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionsCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"extensions\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"extensions\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionsDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionsSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"all\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionsUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"extensions\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"extensions\",\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPFileUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPSupervisorProcessConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRestart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"autoStart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environment\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"numprocs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPSupervisorProcessFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"out.log\",\n\t\t\t\t\t\t\"err.log\",\n\t\t\t\t\t\t\"config\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"get\",\n\t\t\t\t\t\t\"clear\",\n\t\t\t\t\t\t\"update\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"file\",\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PortUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ProcessReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"PID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"PID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RecycleBinReduce\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"from\",\n\t\t\t\t\"rName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ReqWithID\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"clean\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExposedPort\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"install\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeRemark\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"clean\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExposedPort\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"install\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"rebuild\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.SearchUploadWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.StreamUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"streamPorts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"udp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.SupervisorProcessConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRestart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"autoStart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environment\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"numprocs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.SupervisorProcessFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"out.log\",\n\t\t\t\t\t\t\"err.log\",\n\t\t\t\t\t\t\"config\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"get\",\n\t\t\t\t\t\t\"clear\",\n\t\t\t\t\t\t\"update\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"file\",\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.Volume\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteAcmeAccountCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"caDirURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabHmacKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabKid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"email\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"P256\",\n\t\t\t\t\t\t\"P384\",\n\t\t\t\t\t\t\"2048\",\n\t\t\t\t\t\t\"3072\",\n\t\t\t\t\t\t\"4096\",\n\t\t\t\t\t\t\"8192\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"letsencrypt\",\n\t\t\t\t\t\t\"zerossl\",\n\t\t\t\t\t\t\"buypass\",\n\t\t\t\t\t\t\"google\",\n\t\t\t\t\t\t\"custom\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"useEAB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"useProxy\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"email\",\n\t\t\t\t\"keyType\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteAcmeAccountUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"useProxy\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteBatchDelReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCACreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"city\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"commonName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"country\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"P256\",\n\t\t\t\t\t\t\"P384\",\n\t\t\t\t\t\t\"2048\",\n\t\t\t\t\t\t\"3072\",\n\t\t\t\t\t\t\"4096\",\n\t\t\t\t\t\t\"8192\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organization\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organizationUint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"province\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"commonName\",\n\t\t\t\t\"country\",\n\t\t\t\t\"keyType\",\n\t\t\t\t\"name\",\n\t\t\t\t\"organization\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCAObtain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"P256\",\n\t\t\t\t\t\t\"P384\",\n\t\t\t\t\t\t\"2048\",\n\t\t\t\t\t\t\"3072\",\n\t\t\t\t\t\t\"4096\",\n\t\t\t\t\t\t\"8192\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"renew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"time\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"unit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"domains\",\n\t\t\t\t\"id\",\n\t\t\t\t\"keyType\",\n\t\t\t\t\"time\",\n\t\t\t\t\"unit\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCASearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCommonReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"IPV6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appInstall\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/request.NewAppInstall\"\n\t\t\t\t},\n\t\t\t\t\"appInstallID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"new\",\n\t\t\t\t\t\t\"installed\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createDb\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dbFormat\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbHost\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbPassword\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbUser\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomain\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"enableSSL\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ftpPassword\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ftpUser\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"parentWebsiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"runtimeID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"siteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamPorts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"udp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"webSiteGroupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteSSLID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"alias\",\n\t\t\t\t\"type\",\n\t\t\t\t\"webSiteGroupID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDNSReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"acmeAccountId\",\n\t\t\t\t\"websiteSSLId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDefaultUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"deleteApp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deleteDB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDnsAccountCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"authorization\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"authorization\",\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDnsAccountUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"authorization\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"authorization\",\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDomain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"domain\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDomainCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomain\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"domains\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDomainDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDomainUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteHTTPSOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"SSLProtocol\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificatePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hsts\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hstsIncludeSubDomains\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"http3\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"HTTPSOnly\",\n\t\t\t\t\t\t\"HTTPAlso\",\n\t\t\t\t\t\t\"HTTPToHTTPS\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpsPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"importType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"existed\",\n\t\t\t\t\t\t\"auto\",\n\t\t\t\t\t\t\"manual\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteHtmlUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sync\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteInstallCheckReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"InstallIds\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLBCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLBDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLBUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLBUpdateFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLogReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"logType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"logType\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteNginxUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsitePHPVersionReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"runtimeID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteProxyConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"allowCredentials\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowHeaders\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowMethods\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowOrigins\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cache\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cacheTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cors\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"filePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"match\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modifier\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"preflight\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"proxyHost\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyPass\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxySSLName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"replaces\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"serverCacheTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"serverCacheUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sni\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"match\",\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\",\n\t\t\t\t\"proxyHost\",\n\t\t\t\t\"proxyPass\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteProxyDel\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteProxyReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteProxyStatusUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"status\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteRealIP\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ipFrom\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipHeader\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipOther\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"open\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteResourceReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLApply\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"disableLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"nameservers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"skipDNSCheck\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"apply\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"disableCNAME\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isIp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver1\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver2\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"otherDomains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipDNS\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"acmeAccountId\",\n\t\t\t\t\"primaryDomain\",\n\t\t\t\t\"provider\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLListReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"created_at\",\n\t\t\t\t\t\t\"expire_date\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"apply\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"disableCNAME\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver1\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver2\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"otherDomains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipDNS\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"primaryDomain\",\n\t\t\t\t\"provider\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLUpload\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"certificate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificatePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"paste\",\n\t\t\t\t\t\t\"local\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"primary_domain\",\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"status\",\n\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\"expire_date\",\n\t\t\t\t\t\t\"created_at\",\n\t\t\t\t\t\t\"favorite\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteGroupId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"IPV6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webSiteGroupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"primaryDomain\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteUpdateDir\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"siteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"siteDir\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteUpdateDirPermission\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"group\",\n\t\t\t\t\"id\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuConfig\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppParam\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"rawCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"always\",\n\t\t\t\t\t\t\"unless-stopped\",\n\t\t\t\t\t\t\"no\",\n\t\t\t\t\t\t\"on-failure\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"architectures\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"batchInstallSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"crossVersionUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"document\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"github\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryRequired\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"readMe\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"required\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"requiredPanelVersion\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescEn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescZh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.TagDTO\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"versions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"website\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppDetailDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"architectures\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadCallBackUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"lastVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memoryRequired\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"params\": {},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"update\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppDetailSimpleDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppInstalledCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"app\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appInstallId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"httpsPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"installPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"lastBackupAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppItem\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"batchInstallSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppParam\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"edit\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.Locale\"\n\t\t\t\t},\n\t\t\t\t\"labelEn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"labelZh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"multiple\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"required\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"rule\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"showValue\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {},\n\t\t\t\t\"values\": {}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppItem\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppService\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"config\": {},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppUpdateRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appList\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppList\"\n\t\t\t\t},\n\t\t\t\t\"appStoreLastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"canUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSyncing\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.CompleteDiskInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"disks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DiskInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"systemDisks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DiskInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"totalCapacity\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"totalDisks\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"unpartitionedDisks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DiskBasicInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.ComponentInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"error\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"exists\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.Database\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"databaseName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DatabaseConn\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"serviceName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DepthDirSizeRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DirSizeRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"size\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DiskBasicInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"avail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"diskType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"filesystem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isMounted\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isRemovable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSystem\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serial\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"usePercent\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"used\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DiskInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"avail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"diskType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"filesystem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isMounted\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isRemovable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSystem\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"partitions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DiskBasicInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"serial\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"usePercent\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"used\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.ExistFileInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"modTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"extension\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favoriteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"gid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isDetail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isHidden\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSymlink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"itemTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/files.FileInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"linkPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mimeType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updateTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileLineContent\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"end\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"lines\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"totalLines\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileRemarksRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"remarks\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extension\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileWgetRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.HostToolConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.HostToolRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"config\": {},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.McpBindDomainRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"allowIPs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"connUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.McpServerDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"outputTransport\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamableHttpPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.McpServersRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.McpServerDTO\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxAntiLeechRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"blocked\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cache\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cacheTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheUint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"extends\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logEnable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"noneRef\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"return\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serverNames\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxAuthRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxAuth\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxBuildConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"mirror\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modules\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxModule\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxConfigRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"https\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"sslRejectHandshake\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxModule\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"packages\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxParam\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxPathAuthRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxProxyCache\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cacheExpire\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheExpireUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cacheLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cacheLimitUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"open\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shareCache\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"shareCacheUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxRedirectConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"filePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keepPath\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirect\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirectRoot\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxRewriteRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accepts\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"active\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"handled\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"reading\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"requests\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"waiting\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"writing\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NodeModule\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"license\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.PHPConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"disableFunctions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"maxExecutionTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"uploadMaxSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.PHPExtensionRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"extensions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"supportExtensions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.SupportExtension\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.PackageScripts\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.ProcessStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"PID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"msg\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uptime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.Resource\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detail\": {},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resourceID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.RuntimeDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appParams\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppParam\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"container\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExposedPort\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.SupervisorProcessConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRestart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"autoStart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environment\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"msg\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"numprocs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.ProcessStatus\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.SupportExtension\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"check\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"versions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.TagDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.UserGroupResponse\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groups\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"users\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.UserInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.UserInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteAcmeAccountDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"caDirURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabHmacKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabKid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"email\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"useEAB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"useProxy\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteCADTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"city\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"commonName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"country\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"csr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organization\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organizationUint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"province\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteDNSRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"err\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resolve\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"IPV6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"accessLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"accessLogPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appInstallId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultServer\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDomain\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"errorLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"errorLogPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ftpId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"openBaseDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"parentWebsiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rewrite\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"runtimeID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"runtimeName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"runtimeType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"siteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sitePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamPorts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"udp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webSiteGroupId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"webSiteSSL\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteSSL\"\n\t\t\t\t},\n\t\t\t\t\"webSiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteDirConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dirs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"msg\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"userGroup\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteHTTPS\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"SSL\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteSSL\"\n\t\t\t\t},\n\t\t\t\t\"SSLProtocol\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hsts\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hstsIncludeSubDomains\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"http3\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpsPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpsPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteHtmlRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteLog\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"end\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteNginxConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxParam\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsitePreInstallCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteRealIP\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ipFrom\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipHeader\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipOther\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"open\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteSSLDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteAcmeAccount\"\n\t\t\t\t},\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"caId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"certPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"disableCNAME\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDnsAccount\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isIP\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"masterSslId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver1\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver2\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organization\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipDNS\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"startDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websites\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/model.Website\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t}\n\t}\n}`\n\nvar SwaggerInfo = &swag.Spec{\n\tVersion:          \"2.0\",\n\tHost:             \"localhost\",\n\tBasePath:         \"/api/v2\",\n\tSchemes:          []string{},\n\tTitle:            \"1Panel\",\n\tDescription:      \"Top-Rated Web-based Linux Server Management Tool\",\n\tInfoInstanceName: \"swagger\",\n\tSwaggerTemplate:  docTemplate,\n\tLeftDelim:        \"{{\",\n\tRightDelim:       \"}}\",\n}\n\nfunc init() {\n\tswag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)\n}"
  },
  {
    "path": "core/cmd/server/docs/swagger.go",
    "content": "package docs\n\nimport _ \"embed\"\n\n//go:embed x-log.json\nvar XLogJson []byte\n"
  },
  {
    "path": "core/cmd/server/docs/swagger.json",
    "content": "{\n\t\"swagger\": \"2.0\",\n\t\"info\": {\n\t\t\"contact\": {},\n\t\t\"description\": \"Top-Rated Web-based Linux Server Management Tool\",\n\t\t\"license\": {\n\t\t\t\"name\": \"GPL-3.0\",\n\t\t\t\"url\": \"https://www.gnu.org/licenses/gpl-3.0.html\"\n\t\t},\n\t\t\"termsOfService\": \"http://swagger.io/terms/\",\n\t\t\"title\": \"1Panel\",\n\t\t\"version\": \"2.0\"\n\t},\n\t\"host\": \"\",\n\t\"basePath\": \"/api/v2\",\n\t\"paths\": {\n\t\t\"/ai/agents\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentCreateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentItem\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Agent\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountCreateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Agent account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts/delete\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete Agent account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Agent accounts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/accounts/verify\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentAccountVerifyReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Verify Agent account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/browser/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentBrowserConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentBrowserConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Browser config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/browser/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentBrowserConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Browser config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/discord/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentDiscordConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentDiscordConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Discord channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/discord/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentDiscordConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Discord channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/feishu/approve\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentFeishuPairingApproveReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Approve Agent Feishu pairing code\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/feishu/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentFeishuConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentFeishuConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Feishu channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/feishu/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentFeishuConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Feishu channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/pairing/approve\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentChannelPairingApproveReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Approve Agent channel pairing code\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/qqbot/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentQQBotConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentQQBotConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent QQ Bot channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/qqbot/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentQQBotConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent QQ Bot channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/telegram/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentTelegramConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentTelegramConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Telegram channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/channel/telegram/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentTelegramConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Telegram channel config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/delete\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete Agent\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/model/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentModelConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent model config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/other/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentOtherConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentOtherConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Agent Other config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/other/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentOtherConfigUpdateReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Agent Other config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/plugin/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentPluginCheckReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentPluginStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check Agent plugin installation status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/plugin/install\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentPluginInstallReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Install Agent plugin\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/providers\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ProviderInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Providers\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Agents\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/agents/token/reset\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AgentTokenResetReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Reset Agent token\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/domain/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaBindDomain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/domain/get\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaBindDomainReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaBindDomainRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get bind domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/gpu/load\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load gpu / xpu info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/domain/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpBindDomain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind Domain for mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/domain/get\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.McpBindDomainRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get bin Domain for mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/domain/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpBindDomainUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update bind Domain for mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.McpServersRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List mcp servers\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/server\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/server/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/server/op\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/mcp/server/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.McpServerUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update mcp server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"McpServer\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/close\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Close Ollama model conn\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"close conn for Ollama model [name]\",\n\t\t\t\t\t\"formatZH\": \"关闭 Ollama 模型连接 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Ollama model\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"add Ollama model [name]\",\n\t\t\t\t\t\"formatZH\": \"添加 Ollama 模型 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ForceDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete Ollama model\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"ollama_models\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"remove Ollama model [names]\",\n\t\t\t\t\t\"formatZH\": \"删除 Ollama 模型 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/load\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Ollama models\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/recreate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Rereate Ollama model\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"re-add Ollama model [name]\",\n\t\t\t\t\t\"formatZH\": \"添加 Ollama 模型重试 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Ollama models\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/ai/ollama/model/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OllamaModelDropList\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync Ollama model list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"AI\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"sync Ollama model list\",\n\t\t\t\t\t\"formatZH\": \"同步 Ollama 模型列表\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/:key\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app key\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"key\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/checkupdate\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppUpdateRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get app list update\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/detail/:appId/:version/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app 版本\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"version\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app 类型\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"version\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppDetailDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app detail by appid\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/detail/node/:appKey/:version\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app key\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app version\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"version\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppDetailSimpleDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app detail by appkey and version\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/details/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppDetailDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get app detail by id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/icon/:key\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"app id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"app icon\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get app icon by app_id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/ignored/cancel\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ReqWithID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Cancel Ignore Upgrade App\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Cancel ignore application upgrade\",\n\t\t\t\t\t\"formatZH\": \"取消忽略应用升级\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/ignored/detail\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.AppIgnoreUpgrade\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List Upgrade Ignored App\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/install\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstallCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.AppInstall\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Install app\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Install app [name]\",\n\t\t\t\t\t\"formatZH\": \"安装应用 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstalledInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppInstalledCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check app installed\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search default config by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/config/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppConfigUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update app config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"installID\",\n\t\t\t\t\t\t\"webUI\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Application config update [installID]\",\n\t\t\t\t\t\"formatZH\": \"应用配置更新 [installID]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/conninfo\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DatabaseConn\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app password by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/delete/check/:appInstallId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"App install id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appInstallId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppResource\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before delete\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/ignore\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppIgnoreUpgradeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Ignore Upgrade App\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Ignore application upgrade\",\n\t\t\t\t\t\"formatZH\": \"忽略应用升级\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/info/:appInstallId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"App install id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appInstallId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppInstallInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get app install info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/list\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppInstallInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List app installed\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/loadport\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app port by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/op\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstalledOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate installed app\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"app_id\",\n\t\t\t\t\t\t\t\"output_value\": \"appId\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"appName\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"apps\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"appId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"key\",\n\t\t\t\t\t\t\t\"output_value\": \"appKey\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"installId\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] App [appKey][appName]\",\n\t\t\t\t\t\"formatZH\": \"[operate] 应用 [appKey][appName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/params/:appInstallId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appInstallId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search params by appInstallId\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/params/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstalledUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change app params\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"installId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Application param update [name]\",\n\t\t\t\t\t\"formatZH\": \"应用参数修改 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/port/change\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PortUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change app port\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"port\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Application port update [key]-[name] =\\u003e [port]\",\n\t\t\t\t\t\"formatZH\": \"应用端口修改 [key]-[name] =\\u003e [port]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppInstalledSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page app installed\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync app installed\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Sync the list of installed apps\",\n\t\t\t\t\t\"formatZH\": \"同步已安装应用列表\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/installed/update/versions\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"appInstallId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppVersion\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app update version by install id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.AppSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List apps\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/services/:key\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"key\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppService\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search app service by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/apps/sync/local\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync local  app list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"App store synchronization\",\n\t\t\t\t\t\"formatZH\": \"应用商店同步\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/apps/sync/remote\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync remote app list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"App store synchronization\",\n\t\t\t\t\t\"formatZH\": \"应用商店同步\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create backup account [type]\",\n\t\t\t\t\t\"formatZH\": \"创建备份账号 [type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/backup\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommonBackup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Backup system data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"detailName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"backup [type] data [name][detailName]\",\n\t\t\t\t\t\"formatZH\": \"备份 [type] 数据 [name][detailName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/buckets\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ForBuckets\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List buckets\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"backup_accounts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\t\t\t\"output_value\": \"types\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete backup account [types]\",\n\t\t\t\t\t\"formatZH\": \"删除备份账号 [types]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/local\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"get local backup dir\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/options\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load backup account options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete backup record\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"backup_records\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"file_name\",\n\t\t\t\t\t\t\t\"output_value\": \"files\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete backup records [files]\",\n\t\t\t\t\t\"formatZH\": \"删除备份记录 [files]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/description/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update backup record description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DownloadRecord\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download backup record\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"source\",\n\t\t\t\t\t\t\"fileName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"download backup records [source][fileName]\",\n\t\t\t\t\t\"formatZH\": \"下载备份记录 [source][fileName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RecordSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page backup records\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/search/bycronjob\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RecordSearchByCronjob\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page backup records by cronjob\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/record/size\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchForSize\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RecordFileSize\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load backup record size\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/recover\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommonRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Recover system data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"detailName\",\n\t\t\t\t\t\t\"file\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"recover [type] data [name][detailName] from [file]\",\n\t\t\t\t\t\"formatZH\": \"从 [file] 恢复 [type] 数据 [name][detailName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/recover/byupload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommonRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Recover system data by upload\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"detailName\",\n\t\t\t\t\t\t\"file\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"recover [type] data [name][detailName] from [file]\",\n\t\t\t\t\t\"formatZH\": \"从 [file] 恢复 [type] 数据 [name][detailName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/refresh/token\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Refresh token\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchPageWithType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search backup accounts with page\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/search/files\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List files from backup accounts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/backups/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update backup account [types]\",\n\t\t\t\t\t\"formatZH\": \"更新备份账号 [types]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/backups/upload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UploadForRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload file for recover\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"filePath\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"upload backup file [filePath]\",\n\t\t\t\t\t\"formatZH\": \"上传备份文件 [filePath]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"image\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create container [name][image]\",\n\t\t\t\t\t\"formatZH\": \"创建容器 [name][image]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/clean/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean container log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean container [name] logs\",\n\t\t\t\t\t\"formatZH\": \"清理容器 [name] 日志\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/commit\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerCommit\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Commit Container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create compose\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create compose [name]\",\n\t\t\t\t\t\"formatZH\": \"创建 compose [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/clean/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeLogClean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean compose log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean compose [name] logs\",\n\t\t\t\t\t\"formatZH\": \"清理容器编排 [name] 日志\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/env\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FilePath\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load compose environment variables\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeOperation\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate compose\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"compose [operation] [name]\",\n\t\t\t\t\t\"formatZH\": \"compose [operation] [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page composes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/test\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Test compose\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"check compose [name]\",\n\t\t\t\t\t\"formatZH\": \"检测 compose [name] 格式\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/compose/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update compose\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update compose information [name]\",\n\t\t\t\t\t\"formatZH\": \"更新 compose [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/daemonjson\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DaemonJsonConf\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load docker daemon.json\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/daemonjson/file\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load docker daemon.json\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/daemonjson/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update docker daemon.json\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Updated configuration [key]\",\n\t\t\t\t\t\"formatZH\": \"更新配置 [key]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/daemonjson/update/byfile\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DaemonJsonUpdateByFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update docker daemon.json by upload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Updated configuration file\",\n\t\t\t\t\t\"formatZH\": \"更新配置文件\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/docker/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DockerOperation\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate docker\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] docker service\",\n\t\t\t\t\t\"formatZH\": \"docker 服务 [operation]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/docker/status\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DockerStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load docker status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/content\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileContent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get container file content\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileBatchDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete container file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download container file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List container files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/size\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"int\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get container file size\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/files/upload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"multipart/form-data\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"containerID\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"containerID\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"path\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"path\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"file\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"file\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload container file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/image\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Options\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"load images options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/all\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List all images\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/build\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageBuild\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Build image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"build image [name]\",\n\t\t\t\t\t\"formatZH\": \"构建镜像 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/load\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageLoad\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"load image from [path]\",\n\t\t\t\t\t\"formatZH\": \"从 [path] 加载镜像\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/pull\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImagePull\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Pull image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"repoID\",\n\t\t\t\t\t\t\"imageName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"image pull [reponame][imageName]\",\n\t\t\t\t\t\"formatZH\": \"镜像拉取 [reponame][imageName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/push\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImagePush\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Push image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"repoID\",\n\t\t\t\t\t\t\"tagName\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"push [tagName] to [reponame][name]\",\n\t\t\t\t\t\"formatZH\": \"[tagName] 推送到 [reponame][name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/remove\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"remove image [names]\",\n\t\t\t\t\t\"formatZH\": \"移除镜像 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/save\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageSave\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Save image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"tagName\",\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"save [tagName] as [path]/[name]\",\n\t\t\t\t\t\"formatZH\": \"保留 [tagName] 为 [path]/[name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageImage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page images\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/image/tag\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageTag\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Tag image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"repoID\",\n\t\t\t\t\t\t\"targetName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"tag image [reponame][targetName]\",\n\t\t\t\t\t\"formatZH\": \"tag 镜像 [reponame][targetName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/inspect\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.InspectReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Container inspect\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/ipv6option/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LogOption\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update docker daemon.json ipv6 option\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Updated the ipv6 option\",\n\t\t\t\t\t\"formatZH\": \"更新 ipv6 配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/item/stats\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerItemStats\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container stats size\"\n\t\t\t}\n\t\t},\n\t\t\"/containers/limit\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ResourceLimit\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container limits\"\n\t\t\t}\n\t\t},\n\t\t\"/containers/list\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOptions\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List containers\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/list/byimage\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOptions\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List containers by image\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/list/stats\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerListStats\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container stats\"\n\t\t\t}\n\t\t},\n\t\t\"/containers/logoption/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LogOption\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update docker daemon.json log option\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Docker\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Updated the log option\",\n\t\t\t\t\t\"formatZH\": \"更新日志配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/network\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Options\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List networks\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Network\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NetworkCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create network\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Network\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create container network [name]\",\n\t\t\t\t\t\"formatZH\": \"创建容器网络 name\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/network/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete network\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Network\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete container network [names]\",\n\t\t\t\t\t\"formatZH\": \"删除容器网络 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/network/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page networks\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Network\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOperation\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\",\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"container [operation] [names]\",\n\t\t\t\t\t\"formatZH\": \"容器 [names] 执行 [operation]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/prune\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerPrune\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"pruneType\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean container [pruneType]\",\n\t\t\t\t\t\"formatZH\": \"清理容器 [pruneType]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/rename\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerRename\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Rename Container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"newName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"rename container [name] =\\u003e [newName]\",\n\t\t\t\t\t\"formatZH\": \"容器重命名 [name] =\\u003e [newName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageRepoOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List image repos\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageRepoDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create image repo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create image repo [name]\",\n\t\t\t\t\t\"formatZH\": \"创建镜像仓库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete image repo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete image repo [name]\",\n\t\t\t\t\t\"formatZH\": \"删除镜像仓库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page image repos\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load repo status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/repo/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ImageRepoUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update image repo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Image-repo\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update image repo information [name]\",\n\t\t\t\t\t\"formatZH\": \"更新镜像仓库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageContainer\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page containers\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/search/log\": {\n\t\t\t\"get\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"容器名称\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"container\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"时间筛选\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"since\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"是否追踪\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"follow\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"显示行号\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"tail\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"是否显示时间\",\n\t\t\t\t\t\t\"in\": \"query\",\n\t\t\t\t\t\t\"name\": \"timestamp\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Container logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/stats/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"容器id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerStats\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Container stats\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/status\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load containers status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/template\": {\n\t\t\t\"get\": {\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List compose templates\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create compose template\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create compose template [name]\",\n\t\t\t\t\t\"formatZH\": \"创建 compose 模版 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/template/batch\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateBatch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bacth compose template\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"batch import compose templates\",\n\t\t\t\t\t\"formatZH\": \"批量导入编排模版\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/template/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete compose template\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"compose_templates\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete compose template [names]\",\n\t\t\t\t\t\"formatZH\": \"删除 compose 模版 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/template/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page compose templates\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/template/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update compose template\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Compose-template\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"compose_templates\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update compose template information [name]\",\n\t\t\t\t\t\"formatZH\": \"更新 compose 模版 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"image\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update container [name][image]\",\n\t\t\t\t\t\"formatZH\": \"更新容器 [name][image]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/upgrade\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerUpgrade\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upgrade container\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\",\n\t\t\t\t\t\t\"image\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"upgrade container image [names][image]\",\n\t\t\t\t\t\"formatZH\": \"更新容器镜像 [names][image]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/users\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load container users\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/containers/volume\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Options\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List volumes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Volume\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.VolumeCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create volume\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Volume\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create container volume [name]\",\n\t\t\t\t\t\"formatZH\": \"创建容器存储卷 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/volume/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete volume\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Volume\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"names\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete container volume [names]\",\n\t\t\t\t\t\"formatZH\": \"删除容器存储卷 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/containers/volume/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page volumes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Container Volume\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/captcha\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CaptchaResponse\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"Load captcha\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/login\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"安全入口 base64 加密串\",\n\t\t\t\t\t\t\"in\": \"header\",\n\t\t\t\t\t\t\"name\": \"EntranceCode\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Login\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UserLoginInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"User login\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/logout\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"User logout\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/mfalogin\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MFALogin\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"headers\": {\n\t\t\t\t\t\t\t\"EntranceCode\": {\n\t\t\t\t\t\t\t\t\"description\": \"安全入口\",\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UserLoginInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"User login with mfa\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/passkey/begin\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasskeyBeginResponse\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"User login with passkey\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/passkey/finish\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UserLoginInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"User login with passkey\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/auth/setting\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SystemSetting\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"summary\": \"Get Setting For Login\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Auth\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/backups\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create backup account [type]\",\n\t\t\t\t\t\"formatZH\": \"创建备份账号 [type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/backups/client/:clientType\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupClientInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load backup account base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/backups/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete backup account [name]\",\n\t\t\t\t\t\"formatZH\": \"删除备份账号 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/backups/refresh/token\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Refresh token\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/backups/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BackupOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update backup account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Backup Account\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update backup account [types]\",\n\t\t\t\t\t\"formatZH\": \"更新备份账号 [types]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"command\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create quick command [name][command]\",\n\t\t\t\t\t\"formatZH\": \"创建快捷命令 [name][command]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/command\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List commands\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByIDs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"commands\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete quick command [names]\",\n\t\t\t\t\t\"formatZH\": \"删除快捷命令 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/export\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Export command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"export quick commands\",\n\t\t\t\t\t\"formatZH\": \"导出快速命令\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/import\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Import command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"import quick commands\",\n\t\t\t\t\t\"formatZH\": \"导入快速命令\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page commands\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/tree\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandTree\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Tree commands\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/commands/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update command\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Command\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update quick command [name]\",\n\t\t\t\t\t\"formatZH\": \"更新快捷命令 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/groups\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create group [name][type]\",\n\t\t\t\t\t\"formatZH\": \"创建组 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/groups/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\t\t\t\"output_value\": \"type\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete group [type][name]\",\n\t\t\t\t\t\"formatZH\": \"删除组 [type][name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/groups/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByType\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List groups\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/groups/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update group [name][type]\",\n\t\t\t\t\t\"formatZH\": \"更新组 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create host\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"addr\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create host [name][addr]\",\n\t\t\t\t\t\"formatZH\": \"创建主机 [name][addr]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByIDs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete host\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"hosts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"addr\",\n\t\t\t\t\t\t\t\"output_value\": \"addrs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete host [addrs]\",\n\t\t\t\t\t\"formatZH\": \"删除主机 [addrs]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get host info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchPageWithGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page host\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/test/byid/:id\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Test host conn by host id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/test/byinfo\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostConnTest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Test host conn by info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/tree\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchForTree\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostTree\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host tree\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"addr\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update host [name][addr]\",\n\t\t\t\t\t\"formatZH\": \"更新主机信息 [name][addr]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/hosts/update/group\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeHostGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"hosts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"addr\",\n\t\t\t\t\t\t\t\"output_value\": \"addr\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"group\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"change host [addr] group =\\u003e [group]\",\n\t\t\t\t\t\"formatZH\": \"切换主机[addr]分组 =\\u003e [group]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/logs/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanLog\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean operation logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Logs\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"logType\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Clean the [logType] log information\",\n\t\t\t\t\t\"formatZH\": \"清空 [logType] 日志信息\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/logs/login\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchLgLogWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page login logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Logs\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/logs/operation\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchOpLogWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page operation logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Logs\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/script\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ScriptOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Add script\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"add script [name]\",\n\t\t\t\t\t\"formatZH\": \"添加脚本库脚本 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/script/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByIDs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete script\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"script_librarys\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete script [names]\",\n\t\t\t\t\t\"formatZH\": \"删除脚本库脚本 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/script/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchPageWithGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page script\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/script/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByTaskID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync script from remote\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"sync scripts\",\n\t\t\t\t\t\"formatZH\": \"同步脚本库脚本\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/script/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ScriptOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update script\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"ScriptLibrary\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update script [name]\",\n\t\t\t\t\t\"formatZH\": \"更新脚本库脚本 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/api/config/generate/key\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"generate api key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"generate api key\",\n\t\t\t\t\t\"formatZH\": \"生成 API 接口密钥\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/api/config/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ApiInterfaceConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update api config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ipWhiteList\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update api config =\\u003e IP White List: [ipWhiteList]\",\n\t\t\t\t\t\"formatZH\": \"更新 API 接口配置 =\\u003e IP 白名单: [ipWhiteList]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/apps/store/config\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppstoreConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get appstore config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/apps/store/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppstoreUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update appstore config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"App\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/bind/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BindInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system bind info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ipv6\",\n\t\t\t\t\t\t\"bindAddress\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system bind info =\\u003e ipv6: [ipv6], 监听 IP: [bindAddress]\",\n\t\t\t\t\t\"formatZH\": \"修改系统监听信息 =\\u003e ipv6: [ipv6], 监听 IP: [bindAddress]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/by\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system setting by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/expired/handle\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasswordUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Reset system password expired\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"reset an expired Password\",\n\t\t\t\t\t\"formatZH\": \"重置过期密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/interface\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system address\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/memo\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load dashboard memo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MemoUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update dashboard memo\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update dashboard memo\",\n\t\t\t\t\t\"formatZH\": \"更新仪表盘备忘录\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/menu/default\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Default menu\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Menu Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Init menu.\",\n\t\t\t\t\t\"formatZH\": \"初始化菜单\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/menu/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Hide advanced feature menu.\",\n\t\t\t\t\t\"formatZH\": \"隐藏高级功能菜单\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/mfa\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MfaCredential\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/mfa.Otp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mfa info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/mfa/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MfaCredential\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind mfa\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"bind mfa\",\n\t\t\t\t\t\"formatZH\": \"mfa 绑定\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/passkey/list\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasskeyInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List passkeys\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/passkey/register/begin\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasskeyRegisterRequest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasskeyBeginResponse\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Begin passkey registration\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/passkey/register/finish\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Finish passkey registration\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/passkey/{id}\": {\n\t\t\t\"delete\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete passkey\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/password/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PasswordUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system password\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update system password\",\n\t\t\t\t\t\"formatZH\": \"修改系统密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/port/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system port\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"serverPort\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system port =\\u003e [serverPort]\",\n\t\t\t\t\t\"formatZH\": \"修改系统端口 =\\u003e [serverPort]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/proxy/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ProxyUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update proxy setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"proxyUrl\",\n\t\t\t\t\t\t\"proxyPort\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"set proxy [proxyPort]:[proxyPort].\",\n\t\t\t\t\t\"formatZH\": \"服务器代理配置 [proxyPort]:[proxyPort]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system setting info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/search/available\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system available status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/ssl/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download system cert\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/ssl/info\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSLInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system cert info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/ssl/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSLUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ssl\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system ssl =\\u003e [ssl]\",\n\t\t\t\t\t\"formatZH\": \"修改系统 ssl =\\u003e [ssl]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/terminal/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TerminalInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system terminal setting info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/terminal/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TerminalInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system terminal setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update system terminal setting\",\n\t\t\t\t\t\"formatZH\": \"修改系统终端配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system setting [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改系统配置 [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/upgrade\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpgradeInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load upgrade info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Upgrade\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upgrade\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"version\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"upgrade system =\\u003e [version]\",\n\t\t\t\t\t\"formatZH\": \"更新系统 =\\u003e [version]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/upgrade/notes\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Upgrade\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load release notes by version\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/core/settings/upgrade/releases\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ReleasesNotes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load upgrade notes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create cronjob\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create cronjob [type][name]\",\n\t\t\t\t\t\"formatZH\": \"创建计划任务 [type][name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobBatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete cronjob\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete cronjob [names]\",\n\t\t\t\t\t\"formatZH\": \"删除计划任务 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/export\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByIDs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Export cronjob list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/group/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update cronjob group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update cronjob group [name]\",\n\t\t\t\t\t\"formatZH\": \"更新计划任务分组 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/handle\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Handle cronjob once\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"manually execute the cronjob [name]\",\n\t\t\t\t\t\"formatZH\": \"手动执行计划任务 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/import\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobImport\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Import cronjob list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/load/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load cronjob info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/next\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobSpec\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load cronjob spec time\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/records/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobClean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean job records\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"cronjobID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"cronjobID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean cronjob [name] records\",\n\t\t\t\t\t\"formatZH\": \"清空计划任务记录 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/records/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load Cronjob record log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/script/options\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ScriptOptions\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load script options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageCronjob\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page cronjobs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/search/records\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchRecord\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page job records\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobUpdateStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update cronjob status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"status\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"change the status of cronjob [name] to [status].\",\n\t\t\t\t\t\"formatZH\": \"修改计划任务 [name] 状态为 [status]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/stop\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Handle stop job\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/cronjobs/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update cronjob\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Cronjob\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update cronjob [name]\",\n\t\t\t\t\t\"formatZH\": \"更新计划任务 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/app/launcher\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppLauncher\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load app launcher\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/app/launcher/option\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchByFilter\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LauncherOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load app launcher options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/app/launcher/show\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update app Launcher\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"app launcher [key] =\\u003e show: [value]\",\n\t\t\t\t\t\"formatZH\": \"首页应用 [key] =\\u003e 显示：[value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/base/:ioOption/:netOption\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"ioOption\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"netOption\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DashboardBase\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load dashboard base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/base/os\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OsInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load os info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/current/:ioOption/:netOption\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"ioOption\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"netOption\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DashboardCurrent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load dashboard current info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/current/node\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NodeCurrent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load dashboard current info for node\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/current/top/cpu\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Process\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load top cpu processes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/current/top/mem\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Process\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load top memory processes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/quick/change\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeQuicks\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update quick jump\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"change quick jump\",\n\t\t\t\t\t\"formatZH\": \"切换快速跳转\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/quick/option\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.QuickJump\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load quick jump options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/dashboard/system/restart/:operation\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"operation\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"System restart\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Dashboard\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlDBCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create mysql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create mysql database [name]\",\n\t\t\t\t\t\"formatZH\": \"创建 mysql 数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BindUser\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind user of mysql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"database\",\n\t\t\t\t\t\t\"username\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"bind mysql database [database] [username]\",\n\t\t\t\t\t\"formatZH\": \"绑定 mysql 数据库名 [database] [username]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/change/access\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeDBInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change mysql access\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update database [name] access\",\n\t\t\t\t\t\"formatZH\": \"更新数据库 [name] 访问权限\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/change/password\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeDBInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change mysql password\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update database [name] password\",\n\t\t\t\t\t\"formatZH\": \"更新数据库 [name] 密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/common/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DBBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Common\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/common/load/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load Database conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Common\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/common/update/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DBConfUpdateByFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update conf by upload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Common\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"database\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update the [type] [database] database configuration information\",\n\t\t\t\t\t\"formatZH\": \"更新 [type] 数据库 [database] 配置信息\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/db\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create database [name][type]\",\n\t\t\t\t\t\"formatZH\": \"创建远程数据库 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/:name\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"check if database [name][type] is connectable\",\n\t\t\t\t\t\"formatZH\": \"检测远程数据库 [name][type] 连接性\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"databases\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete database [names]\",\n\t\t\t\t\t\"formatZH\": \"删除远程数据库 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/del/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before delete remote database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/item/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseItem\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/list/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/db/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DatabaseUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update database [name]\",\n\t\t\t\t\t\"formatZH\": \"更新远程数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlDBDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete mysql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete mysql database [name]\",\n\t\t\t\t\t\"formatZH\": \"删除 mysql 数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/del/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlDBDeleteCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before delete mysql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/description/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update mysql database description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"description\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"The description of the mysql database [name] is modified =\\u003e [description]\",\n\t\t\t\t\t\"formatZH\": \"mysql 数据库 [name] 描述信息修改 [description]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/format/options\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlFormatCollationOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List mysql database format collation options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/load\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlLoadDB\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mysql database from remote\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlDBCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create postgresql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create postgresql database [name]\",\n\t\t\t\t\t\"formatZH\": \"创建 postgresql 数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/:database/load\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlLoadDB\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load postgresql database from remote\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/bind\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlBindUser\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Bind postgresql user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"username\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"bind postgresql database [name] user [username]\",\n\t\t\t\t\t\"formatZH\": \"绑定 postgresql 数据库 [name] 用户 [username]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlDBDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete postgresql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete postgresql database [name]\",\n\t\t\t\t\t\"formatZH\": \"删除 postgresql 数据库 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/del/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlDBDeleteCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before delete postgresql database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/description\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update postgresql database description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"description\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"The description of the postgresql database [name] is modified =\\u003e [description]\",\n\t\t\t\t\t\"formatZH\": \"postgresql 数据库 [name] 描述信息修改 [description]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/password\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeDBInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change postgresql password\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update database [name] password\",\n\t\t\t\t\t\"formatZH\": \"更新数据库 [name] 密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/privileges\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeDBInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change postgresql privileges\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"database\",\n\t\t\t\t\t\t\"username\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update [user] privileges of database [database]\",\n\t\t\t\t\t\"formatZH\": \"更新数据库 [database] 用户 [username] 权限\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/pg/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PostgresqlDBSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page postgresql databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database PostgreSQL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LoadRedisStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisConf\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load redis conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/conf/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisConfUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update redis conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update the redis database configuration information\",\n\t\t\t\t\t\"formatZH\": \"更新 redis 数据库配置信息\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/install/cli\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Install redis-cli\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/password\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangeRedisPass\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change redis password\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"change the password of the redis database\",\n\t\t\t\t\t\"formatZH\": \"修改 redis 数据库密码\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/persistence/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LoadRedisStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisPersistence\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load redis persistence conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/persistence/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisConfPersistenceUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update redis persistence conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"redis database persistence configuration update\",\n\t\t\t\t\t\"formatZH\": \"redis 数据库持久化配置更新\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/databases/redis/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.LoadRedisStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RedisStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load redis status info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Redis\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/remote\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mysql remote access\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlDBSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page mysql databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mysql status info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/variables\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithNameAndType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlVariables\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load mysql variables info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/databases/variables/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlVariablesUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update mysql variables\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Database Mysql\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"adjust mysql database performance parameters\",\n\t\t\t\t\t\"formatZH\": \"调整 mysql 数据库性能参数\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create dir or file [path]\",\n\t\t\t\t\t\"formatZH\": \"创建文件/文件夹 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/batch/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FilePathsCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.ExistFileInfo\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch check file exist\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/batch/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileBatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch delete file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"paths\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Batch delete dir or file [paths]\",\n\t\t\t\t\t\"formatZH\": \"批量删除文件/文件夹 [paths]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/batch/role\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRoleReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch change file mode and owner\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"paths\",\n\t\t\t\t\t\t\"mode\",\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"group\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Batch change file mode and owner [paths] =\\u003e [mode]/[user]/[group]\",\n\t\t\t\t\t\"formatZH\": \"批量修改文件权限和用户/组 [paths] =\\u003e [mode]/[user]/[group]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FilePathCheck\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check file exist\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/chunkdownload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileDownload\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Chunk Download file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Download file [name]\",\n\t\t\t\t\t\"formatZH\": \"下载文件 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/chunkupload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"file\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"ChunkUpload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/compress\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileCompress\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Compress file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Compress file [name]\",\n\t\t\t\t\t\"formatZH\": \"压缩文件 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/content\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileContentReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load file content\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Load file content [path]\",\n\t\t\t\t\t\"formatZH\": \"获取文件内容 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/convert\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileConvert\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Convert file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/convert/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Convert file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/decompress\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileDeCompress\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Decompress file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Decompress file [path]\",\n\t\t\t\t\t\"formatZH\": \"解压 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete dir or file [path]\",\n\t\t\t\t\t\"formatZH\": \"删除文件/文件夹 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/depth/size\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DirSizeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DepthDirSizeRes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Multi file size\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Multi file size [path]\",\n\t\t\t\t\t\"formatZH\": \"获取目录及其第一层子目录文件夹大小 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/download\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/favorite\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FavoriteCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.Favorite\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create favorite\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"收藏文件/文件夹 [path]\",\n\t\t\t\t\t\"formatZH\": \"收藏文件/文件夹 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/favorite/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FavoriteDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete favorite\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"favorites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"path\",\n\t\t\t\t\t\t\t\"output_value\": \"path\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete avorite [path]\",\n\t\t\t\t\t\"formatZH\": \"删除收藏 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/favorite/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List favorites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/mode\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change file mode\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"mode\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Change mode [paths] =\\u003e [mode]\",\n\t\t\t\t\t\"formatZH\": \"修改权限 [paths] =\\u003e [mode]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/mount\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DiskInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"system mount\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/move\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileMove\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Move file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"oldPaths\",\n\t\t\t\t\t\t\"newPath\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Move [oldPaths] =\\u003e [newPath]\",\n\t\t\t\t\t\"formatZH\": \"移动文件 [oldPaths] =\\u003e [newPath]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/owner\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRoleUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change file owner\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"group\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Change owner [paths] =\\u003e [user]/[group]\",\n\t\t\t\t\t\"formatZH\": \"修改用户/组 [paths] =\\u003e [user]/[group]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/preview\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileContentReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Preview file content\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Preview file content [path]\",\n\t\t\t\t\t\"formatZH\": \"预览文件内容 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/read\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileReadByLineReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileLineContent\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Read file by Line\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/recycle/clear\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clear RecycleBin files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"清空回收站\",\n\t\t\t\t\t\"formatZH\": \"清空回收站\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/recycle/reduce\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RecycleBinReduce\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Reduce RecycleBin files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Reduce RecycleBin file [name]\",\n\t\t\t\t\t\"formatZH\": \"还原回收站文件 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/recycle/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List RecycleBin files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/recycle/status\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get RecycleBin status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/remark\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRemarkUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Set file remark\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/remarks\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRemarkBatch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileRemarksRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch get file remarks\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/rename\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileRename\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change file name\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"oldName\",\n\t\t\t\t\t\t\"newName\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Rename [oldName] =\\u003e [newName]\",\n\t\t\t\t\t\"formatZH\": \"重命名 [oldName] =\\u003e [newName]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/save\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileEdit\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update file content\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update file content [path]\",\n\t\t\t\t\t\"formatZH\": \"更新文件内容 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileOption\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/size\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DirSizeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DirSizeRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load file size\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Load file size [path]\",\n\t\t\t\t\t\"formatZH\": \"获取文件夹大小 [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/tree\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileOption\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileTree\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load files tree\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/upload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"file\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"file\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Upload file [path]/[file]\",\n\t\t\t\t\t\"formatZH\": \"上传文件 [path]/[file]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/files/upload/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.SearchUploadWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/user/group\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.UserGroupResponse\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"system user and group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/files/wget\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FileWget\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileWgetRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Wget file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"File\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"url\",\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Download url =\\u003e [path]/[name]\",\n\t\t\t\t\t\"formatZH\": \"下载 url =\\u003e [path]/[name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/groups\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create group [name][type]\",\n\t\t\t\t\t\"formatZH\": \"创建组 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/groups/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\t\t\t\"output_value\": \"type\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete group [type][name]\",\n\t\t\t\t\t\"formatZH\": \"删除组 [type][name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/groups/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByType\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List groups\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/groups/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GroupUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Group\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update group [name][type]\",\n\t\t\t\t\t\"formatZH\": \"更新组 [name][type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/components/{name}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"Component name to check (e.g., rsync, docker)\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"name\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.ComponentInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check if a system component exists\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/disks\": {\n\t\t\t\"get\": {\n\t\t\t\t\"description\": \"Get information about all disks including partitioned and unpartitioned disks\",\n\t\t\t\t\"produces\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.CompleteDiskInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get complete disk information\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Disk Management\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/disks/mount\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"description\": \"Mount partition to specified mount point\",\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"mount request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DiskMountRequest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"Disk mounted successfully\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Mount disk\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Disk Management\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"device\",\n\t\t\t\t\t\t\"mountPoint\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Mount disk [device] to [mountPoint]\",\n\t\t\t\t\t\"formatZH\": \"挂载磁盘 [device] 到 [mountPoint]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/disks/partition\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"description\": \"Create partition and format disk with specified filesystem\",\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"partition request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DiskPartitionRequest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"Partition created successfully\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Partition disk\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Disk Management\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"device\",\n\t\t\t\t\t\t\"filesystem\",\n\t\t\t\t\t\t\"mountPoint\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Partition disk [device] with filesystem [filesystem], mount point [mountPoint]\",\n\t\t\t\t\t\"formatZH\": \"对磁盘 [device] 进行分区，文件系统 [filesystem]，挂载点 [mountPoint]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/disks/unmount\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"description\": \"Unmount partition from mount point\",\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"unmount request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.DiskUnmountRequest\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"Disk unmounted successfully\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Unmount disk\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Disk Management\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"device\",\n\t\t\t\t\t\t\"mountPoint\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Unmount disk [device] from [mountPoint]\",\n\t\t\t\t\t\"formatZH\": \"卸载磁盘 [device] 从 [mountPoint]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/base\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FirewallBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load firewall base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/batch\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchRuleOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch operate rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/chain/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"load chain status with name\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.IptablesOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Apply/Unload/Init iptables filter\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] iptables filter firewall\",\n\t\t\t\t\t\"formatZH\": \"[operate] iptables filter 防火墙\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/rule/batch\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.IptablesBatchOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch operate iptables filter rules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/rule/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.IptablesRuleOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate iptables filter rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\",\n\t\t\t\t\t\t\"chain\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] filter rule to [chain]\",\n\t\t\t\t\t\"formatZH\": \"[operation] filter规则到 [chain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/filter/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchPageWithType\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"search iptables filter rules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/forward\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ForwardRuleOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate forward rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"update port forward rules\",\n\t\t\t\t\t\"formatZH\": \"更新端口转发规则\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/ip\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AddrRuleOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Ip rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"strategy\",\n\t\t\t\t\t\t\"address\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create address rules [strategy][address]\",\n\t\t\t\t\t\"formatZH\": \"添加 ip 规则 [strategy] [address]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FirewallOperation\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate firewall\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] firewall\",\n\t\t\t\t\t\"formatZH\": \"[operation] 防火墙\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/port\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"port\",\n\t\t\t\t\t\t\"strategy\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create port rules [strategy][port]\",\n\t\t\t\t\t\"formatZH\": \"添加端口规则 [strategy] [port]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RuleSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page firewall rules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/update/addr\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AddrRuleUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Ip rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/update/description\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateFirewallDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update rule description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/firewall/update/port\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update port rule\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Firewall\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean monitor data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"clean monitor datas\",\n\t\t\t\t\t\"formatZH\": \"清空监控数据\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/gpu/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorGPUSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorGPUData\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load monitor data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorData\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load monitor data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/setting\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorSetting\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load monitor setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/monitor/setting/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MonitorSettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update monitor setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Monitor\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update default monitor [name]-[value]\",\n\t\t\t\t\t\"formatZH\": \"修改默认监控网卡 [name]-[value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RootCertOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Generate host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"generate SSH secret\",\n\t\t\t\t\t\"formatZH\": \"生成 SSH 密钥 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert/delete\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ForceDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"delete SSH secret\",\n\t\t\t\t\t\"formatZH\": \"删除 SSH 密钥 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sycn host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"sync SSH secret\",\n\t\t\t\t\t\"formatZH\": \"同步 SSH 密钥 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/cert/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.RootCertOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host SSH secret\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"generate SSH secret\",\n\t\t\t\t\t\"formatZH\": \"生成 SSH 密钥 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host SSH conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/file/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHConf\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host SSH setting by file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update SSH conf [key]\",\n\t\t\t\t\t\"formatZH\": \"修改 SSH 配置文件 [key]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchSSHLog\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host SSH logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/log/export\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchSSHLog\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Export host SSH logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Operate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate SSH\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] SSH\",\n\t\t\t\t\t\"formatZH\": \"[operation] SSH \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load host SSH setting info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/ssh/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update host SSH setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"SSH\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"newValue\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update SSH setting [key] =\\u003e [newValue]\",\n\t\t\t\t\t\"formatZH\": \"修改 SSH 配置 [key] =\\u003e [newValue]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.HostToolReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.HostToolRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get tool status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.HostToolConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.HostToolConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get tool config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] tool config\",\n\t\t\t\t\t\"formatZH\": \"[operate] 主机工具配置文件 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/init\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.HostToolCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Host tool Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create [type] config\",\n\t\t\t\t\t\"formatZH\": \"创建 [type] 配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.HostToolReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate tool\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] [type]\",\n\t\t\t\t\t\"formatZH\": \"[operate] [type] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/supervisor/process\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.SupervisorProcessConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Supervisor process config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.SupervisorProcessConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Supervisor process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] process\",\n\t\t\t\t\t\"formatZH\": \"[operate] 守护进程 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/hosts/tool/supervisor/process/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.SupervisorProcessFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Supervisor process config file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Host tool\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] Supervisor Process Config file\",\n\t\t\t\t\t\"formatZH\": \"[operate] Supervisor 进程文件 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/logs/system/files\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system log files\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Logs\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/logs/tasks/executing/count\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get the number of executing tasks\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"TaskLog\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/logs/tasks/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchTaskLogReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page task logs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"TaskLog\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load OpenResty conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/build\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxBuildReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Build OpenResty\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Build OpenResty\",\n\t\t\t\t\t\"formatZH\": \"构建 OpenResty\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/openresty/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxConfigFileUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update OpenResty conf by upload file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Update nginx conf\",\n\t\t\t\t\t\"formatZH\": \"更新 nginx 配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/openresty/https\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxConfigRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get default HTTPs status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxDefaultHTTPSUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate default HTTPs\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/modules\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxBuildConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get OpenResty modules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/modules/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxModuleUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update OpenResty module\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Update OpenResty module\",\n\t\t\t\t\t\"formatZH\": \"更新 OpenResty 模块\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/openresty/scope\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxScopeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxParam\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load partial OpenResty conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/status\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load OpenResty status info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/openresty/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxConfigUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update OpenResty conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"OpenResty\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update nginx conf [domain]\",\n\t\t\t\t\t\"formatZH\": \"更新 nginx 配置 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/process/listening\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Listening Process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Process\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/process/stop\": {\n\t\t\t\"post\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ProcessReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Stop Process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Process\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"PID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"结束进程 [PID]\",\n\t\t\t\t\t\"formatZH\": \"结束进程 [PID]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/process/{pid}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"PID\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"pid\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Process Info By PID\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Process\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.Runtime\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create runtime [name]\",\n\t\t\t\t\t\"formatZH\": \"创建运行环境 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.RuntimeDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete runtime [name]\",\n\t\t\t\t\t\"formatZH\": \"删除运行环境 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/installed/delete/check/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppResource\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/node/modules\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NodeModuleReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NodeModule\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Node modules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/node/modules/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NodeModuleReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Node modules\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/node/package\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NodePackageReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.PackageScripts\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Node package scripts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Operate runtime [name]\",\n\t\t\t\t\t\"formatZH\": \"操作运行环境 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/:id/extensions\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.PHPExtensionRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get php runtime extension\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPConfigUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update runtime php conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[domain] PHP conf update\",\n\t\t\t\t\t\"formatZH\": \"[domain] PHP 配置修改\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/config/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.PHPConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load php runtime conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/container/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPContainerConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get PHP container config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/container/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPContainerConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update PHP container config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionsCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create Extensions\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"PHP Extensions\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionsDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete Extensions\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"PHP Extensions\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/install\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionInstallReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Install php extension\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionsSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page Extensions\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"PHP Extensions\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/uninstall\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionInstallReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"UnInstall php extension\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/extensions/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPExtensionsUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Extensions\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"PHP Extensions\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get php conf file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/fpm/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FPMConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update fpm config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/fpm/config/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.FPMConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get fpm config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/fpm/status/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get PHP runtime status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/php/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPFileUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update php conf file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Nginx conf update [domain]\",\n\t\t\t\t\t\"formatZH\": \"php 配置修改 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/remark\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeRemark\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update runtime remark\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List runtimes\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/supervisor/process\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPSupervisorProcessConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate supervisor process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/supervisor/process/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.SupervisorProcessConfig\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get supervisor process\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/supervisor/process/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.PHPSupervisorProcessFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate supervisor process file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync runtime status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/runtimes/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.RuntimeUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update runtime\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Runtime\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update runtime [name]\",\n\t\t\t\t\t\"formatZH\": \"更新运行环境 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/basedir\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load local backup dir\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/description/save\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommonDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Save common description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/get/{key}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"key\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"key\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system setting by key\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system setting info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/search/available\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system available status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create system snapshot\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"from\",\n\t\t\t\t\t\t\"description\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create system backup [description] to [from]\",\n\t\t\t\t\t\"formatZH\": \"创建系统快照 [description] 到 [from]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotBatchDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete system backup\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete system backup [name]\",\n\t\t\t\t\t\"formatZH\": \"删除系统快照 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/description/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateDescription\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update snapshot description\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"description\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"The description of the snapshot [name] is modified =\\u003e [description]\",\n\t\t\t\t\t\"formatZH\": \"快照 [name] 描述信息修改 [description]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/import\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotImport\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Import system snapshot\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"from\",\n\t\t\t\t\t\t\"names\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Sync system snapshots [names] from [from]\",\n\t\t\t\t\t\"formatZH\": \"从 [from] 同步系统快照 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/load\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotData\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load system snapshot data\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/recover\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Recover system backup\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Recover from system backup [name]\",\n\t\t\t\t\t\"formatZH\": \"从系统快照 [name] 恢复\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/recreate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Recreate system snapshot\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"recrete the snapshot [name]\",\n\t\t\t\t\t\"formatZH\": \"重试创建快照 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/rollback\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotRecover\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Rollback system backup\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Rollback from system backup [name]\",\n\t\t\t\t\t\"formatZH\": \"从系统快照 [name] 回滚\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/snapshot/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageSnapshot\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page system snapshot\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/ssh\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Save local conn info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/ssh/check/info\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check local conn info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/ssh/conn\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHConnData\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load local conn\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/settings/ssh/conn/default\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SSHDefaultConn\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update local is conn\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"defaultConn\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system default conn [defaultConn]\",\n\t\t\t\t\t\"formatZH\": \"本地终端默认连接 [defaultConn]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/settings/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update system setting\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"System Setting\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update system setting [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改系统配置 [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create clam [name][path]\",\n\t\t\t\t\t\"formatZH\": \"创建扫描规则 [name][path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/base\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load clam base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"names\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete clam [names]\",\n\t\t\t\t\t\"formatZH\": \"删除扫描规则 [names]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/file/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamFileReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load clam file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/file/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateByNameAndFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update clam file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/handle\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Handle clam scan\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"handle clam scan [name]\",\n\t\t\t\t\t\"formatZH\": \"执行病毒扫描 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Operate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] FTP\",\n\t\t\t\t\t\"formatZH\": \"[operation] Clam\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/record/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperateByID\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean clam record\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"clean clam record [name]\",\n\t\t\t\t\t\"formatZH\": \"清空扫描报告 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/record/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamLogSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page clam record\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchClamWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/status/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamUpdateStatus\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update clam status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"status\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"change the status of clam [name] to [status].\",\n\t\t\t\t\t\"formatZH\": \"修改扫描规则 [name] 状态为 [status]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clam/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ClamUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update clam\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Clam\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update clam [name][path]\",\n\t\t\t\t\t\"formatZH\": \"修改扫描规则 [name][path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/clean\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Clean\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clean system\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Clean system junk files\",\n\t\t\t\t\t\"formatZH\": \"清理系统垃圾文件\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/base\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DeviceBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load device base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/check/dns\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check device DNS conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.OperationWithName\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"load conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/byconf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateByNameAndFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device conf by file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/conf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update device conf [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改主机参数 [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/host\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device hosts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update device host [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改主机 Host [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/passwd\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ChangePasswd\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device passwd\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/update/swap\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SwapHelper\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update device swap\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operate\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] device swap [path]\",\n\t\t\t\t\t\"formatZH\": \"[operate] 主机 swap [path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/users\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load user list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/device/zone/options\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"list time zone options\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/base\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Fail2BanBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load fail2ban base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/load/conf\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load fail2ban conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Operate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate fail2ban\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] Fail2ban\",\n\t\t\t\t\t\"formatZH\": \"[operation] Fail2ban\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/operate/sshd\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Fail2BanSet\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate sshd of fail2ban\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Fail2BanSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page fail2ban ip list\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Fail2BanUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update fail2ban conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t\t\"value\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update fail2ban conf [key] =\\u003e [value]\",\n\t\t\t\t\t\"formatZH\": \"修改 Fail2ban 配置 [key] =\\u003e [value]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/fail2ban/update/byconf\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.UpdateByFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update fail2ban conf by file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Fail2ban\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FtpCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"create FTP [user][path]\",\n\t\t\t\t\t\"formatZH\": \"创建 FTP 账户 [user][path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/base\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FtpBaseInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load FTP base info\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"ftps\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"user\",\n\t\t\t\t\t\t\t\"output_value\": \"users\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"delete FTP users [users]\",\n\t\t\t\t\t\"formatZH\": \"删除 FTP 账户 [users]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/log/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FtpLogSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load FTP operation log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Operate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate FTP\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operation] FTP\",\n\t\t\t\t\t\"formatZH\": \"[operation] FTP\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SearchWithPage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/sync\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.BatchDeleteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Sync FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"sync FTP users\",\n\t\t\t\t\t\"formatZH\": \"同步 FTP 账户\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/ftp/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.FtpUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update FTP user\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"FTP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"path\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"update FTP [user][path]\",\n\t\t\t\t\t\"formatZH\": \"修改 FTP 账户 [user][path]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/toolbox/scan\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanData\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Scan system\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Device\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"scan System Junk Files\",\n\t\t\t\t\t\"formatZH\": \"扫描系统垃圾文件\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"alias\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website [alias]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 [alias]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website by id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/:id/config/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website nginx by id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Nginx\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/:id/https\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteHTTPS\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load https conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website HTTPS\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteHTTPSOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteHTTPS\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update https conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website HTTPS\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update website https [domain] conf\",\n\t\t\t\t\t\"formatZH\": \"更新网站 [domain] https 配置\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/acme\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteAcmeAccountCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteAcmeAccountDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website acme account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Acme\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"email\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website acme [email]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 acme [email]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/acme/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteResourceReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website acme account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Acme\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_acme_accounts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"email\",\n\t\t\t\t\t\t\t\"output_value\": \"email\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete website acme [email]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 acme [email]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/acme/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page website acme accounts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Acme\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/acme/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteAcmeAccountUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteAcmeAccountDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website acme account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Acme\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_acme_accounts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"email\",\n\t\t\t\t\t\t\t\"output_value\": \"email\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update acme [email]\",\n\t\t\t\t\t\"formatZH\": \"更新 acme [email]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/auths\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxAuthReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxAuthRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AuthBasic conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/auths/path\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxAuthReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxPathAuthRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AuthBasic conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/auths/path/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxPathAuthUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AuthBasic conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/auths/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxAuthUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AuthBasic conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/batch/group\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.BatchWebsiteGroup\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch set website group\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/batch/https\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.BatchWebsiteHttps\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch set HTTPS for websites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/batch/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.BatchWebsiteOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Batch operate websites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCACreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCACreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website ca\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website ca [name]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 ca [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCommonReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website ca\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete website ca [name]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 ca [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteResourceReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download CA file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"download ca file [name]\",\n\t\t\t\t\t\"formatZH\": \"下载 CA 证书文件 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/obtain\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCAObtain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Obtain SSL\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Obtain SSL [name]\",\n\t\t\t\t\t\"formatZH\": \"自签 SSL 证书 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/renew\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCAObtain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Obtain SSL\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Obtain SSL [name]\",\n\t\t\t\t\t\"formatZH\": \"自签 SSL 证书 [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCASearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page website ca\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ca/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteCADTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website ca\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website CA\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/check\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteInstallCheckReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsitePreInstallCheck\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Check before create website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxScopeReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteNginxConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Load nginx conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Nginx\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/config/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxConfigUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update nginx conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Nginx\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Nginx conf update [domain]\",\n\t\t\t\t\t\"formatZH\": \"nginx 配置修改 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/cors/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.CorsConfigReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update CORS Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/cors/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.CorsConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get CORS Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/crosssite\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.CrossSiteAccessOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate Cross Site Access\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/databases\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.Database\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get databases\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ChangeDatabase\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change website database\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/default/html/:type\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteHtmlRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get default html\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/default/html/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteHtmlUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update default html\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update default html\",\n\t\t\t\t\t\"formatZH\": \"更新默认 html\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/default/server\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDefaultUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Change default server\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Change default server =\\u003e [domain]\",\n\t\t\t\t\t\"formatZH\": \"修改默认 server =\\u003e [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete website [domain]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dir\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCommonReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteDirConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website dir\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/dir/permission\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteUpdateDirPermission\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Site Dir permission\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update  domain [domain] dir permission\",\n\t\t\t\t\t\"formatZH\": \"更新网站 [domain] 目录权限\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dir/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteUpdateDir\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Site Dir\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update  domain [domain] dir\",\n\t\t\t\t\t\"formatZH\": \"更新网站 [domain] 目录\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dns\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDnsAccountCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website dns account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website DNS\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website dns [name]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 dns [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dns/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteResourceReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website dns account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website DNS\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_dns_accounts\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\t\t\t\"output_value\": \"name\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete website dns [name]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 dns [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/dns/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageInfo\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page website dns accounts\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website DNS\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/dns/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDnsAccountUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website dns account\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website DNS\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update website dns [name]\",\n\t\t\t\t\t\"formatZH\": \"更新网站 dns [name]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/domains\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomainCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDomain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Domain\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"domain\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create domain [domain]\",\n\t\t\t\t\t\"formatZH\": \"创建域名 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/domains/:websiteId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"websiteId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDomain\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website domains by websiteId\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Domain\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/domains/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomainDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Domain\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_domains\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete domain [domain]\",\n\t\t\t\t\t\"formatZH\": \"删除域名 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/domains/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomainUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website domain\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Domain\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_domains\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update domain [domain]\",\n\t\t\t\t\t\"formatZH\": \"更新域名 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/exec/composer\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExecComposerReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Exec Composer\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteCommonReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstream\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website upstreams\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs/create\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLBCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website upstream\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLBDelete\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website upstream\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLBUpdateFile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website upstream file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/lbs/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLBUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website upstream\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/leech\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxCommonReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxAntiLeechRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get AntiLeech conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/leech/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxAntiLeechUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update AntiLeech\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/list\": {\n\t\t\t\"get\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteDTO\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List websites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/log\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteLogReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteLog\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate website log\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[domain][operate] logs\",\n\t\t\t\t\t\"formatZH\": \"[domain][operate] 日志\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/nginx/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteNginxUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website nginx conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website Nginx\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[domain] Nginx conf update\",\n\t\t\t\t\t\"formatZH\": \"[domain] Nginx 配置修改\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/operate\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteOp\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"[operate] website [domain]\",\n\t\t\t\t\t\"formatZH\": \"[operate] 网站 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/options\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteOption\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List website names\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/php/version\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsitePHPVersionReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update php version\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website PHP\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteId\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"php version update [domain]\",\n\t\t\t\t\t\"formatZH\": \"php 版本变更 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyConfig\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get proxy conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies/delete\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyDel\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete proxy config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete domain [domain] proxy config [name]\",\n\t\t\t\t\t\"formatZH\": \"删除网站 [domain] 反向代理配置 [name] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxProxyUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update proxy file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\",\n\t\t\t\t\t\t\"name\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update domain [domain] proxy config file [name]\",\n\t\t\t\t\t\"formatZH\": \"修改网站 [domain] 反向代理配置文件 [name] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies/status\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyStatusUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update proxy config status\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"status\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update domain [domain] proxy config [name] status [status]\",\n\t\t\t\t\t\"formatZH\": \"修改网站 [domain] 反向代理配置 [name] 状态 [status] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxies/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyConfig\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update proxy conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"operate\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update [operate] domain [domain] proxy config [name]\",\n\t\t\t\t\t\"formatZH\": \"修改 [operate] 网站 [domain] 反向代理配置 [name] \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxy/clear\": {\n\t\t\t\"post\": {\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Clear Website proxy cache\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [],\n\t\t\t\t\t\"formatEN\": \"Clear nginx proxy cache\",\n\t\t\t\t\t\"formatZH\": \"清理 Openresty 代理缓存\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxy/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxProxyCacheUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"update website proxy cache config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/proxy/config/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxProxyCache\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website proxy cache config\"\n\t\t\t}\n\t\t},\n\t\t\"/websites/realip/config\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteRealIP\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Set Real IP\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Modify the real IP configuration of [domain] website\",\n\t\t\t\t\t\"formatZH\": \"修改 [domain] 网站真实IP配置 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/realip/config/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteRealIP\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get Real IP Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/redirect\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteProxyReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxRedirectConfig\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get redirect conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/redirect/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxRedirectUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update redirect file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Nginx conf redirect file update [domain]\",\n\t\t\t\t\t\"formatZH\": \"更新重定向文件 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/redirect/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxRedirectReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update redirect conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update domain [domain] redirect config\",\n\t\t\t\t\t\"formatZH\": \"修改网站 [domain] 重定向配置 \",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/resource/{id}\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"id\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.Resource\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get website resource\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/rewrite\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxRewriteReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxRewriteRes\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Get rewrite conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/rewrite/custom\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List custom rewrite\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.CustomRewriteOperate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Operate custom rewrite\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/rewrite/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.NginxRewriteUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update rewrite conf\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"websiteID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Nginx conf rewrite update [domain]\",\n\t\t\t\t\t\"formatZH\": \"伪静态配置修改 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PageResult\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page websites\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLCreate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Create website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"primaryDomain\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Create website ssl [primaryDomain]\",\n\t\t\t\t\t\"formatZH\": \"创建网站 ssl [primaryDomain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/:id\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"id\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteSSLDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website ssl by id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/del\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteBatchDelReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Delete website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\t\t\t\"isList\": true,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ids\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Delete ssl [domain]\",\n\t\t\t\t\t\"formatZH\": \"删除 ssl [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/download\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteResourceReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Download SSL  file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"download ssl file [domain]\",\n\t\t\t\t\t\"formatZH\": \"下载证书文件 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/list\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLListReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteSSLDTO\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"List website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/obtain\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLApply\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Apply  ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"ID\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"ID\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"apply ssl [domain]\",\n\t\t\t\t\t\"formatZH\": \"申请证书  [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/resolve\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDNSReq\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteDNSRes\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Resolve website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/search\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLSearch\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteSSLDTO\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Page website ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\t\t\t\"isList\": false,\n\t\t\t\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\t\t\t\"output_value\": \"domain\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"id\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update ssl config [domain]\",\n\t\t\t\t\t\"formatZH\": \"更新证书设置 [domain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/upload\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteSSLUpload\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload ssl\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Upload ssl [type]\",\n\t\t\t\t\t\"formatZH\": \"上传 ssl [type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/upload/file\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"multipart/form-data\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"type\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"type\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"description\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"description\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"sslID\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"sslID\",\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"privateKeyFile\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"privateKeyFile\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"certificateFile\",\n\t\t\t\t\t\t\"in\": \"formData\",\n\t\t\t\t\t\t\"name\": \"certificateFile\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"file\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Upload SSL file\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Upload ssl file [type]\",\n\t\t\t\t\t\"formatZH\": \"上传 ssl 文件 [type]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"/websites/ssl/website/:websiteId\": {\n\t\t\t\"get\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"path\",\n\t\t\t\t\t\t\"name\": \"websiteId\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\",\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/response.WebsiteSSLDTO\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Search website ssl by website id\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website SSL\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/stream/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.StreamUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update Stream Config\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t]\n\t\t\t}\n\t\t},\n\t\t\"/websites/update\": {\n\t\t\t\"post\": {\n\t\t\t\t\"consumes\": [\n\t\t\t\t\t\"application/json\"\n\t\t\t\t],\n\t\t\t\t\"parameters\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"description\": \"request\",\n\t\t\t\t\t\t\"in\": \"body\",\n\t\t\t\t\t\t\"name\": \"request\",\n\t\t\t\t\t\t\"required\": true,\n\t\t\t\t\t\t\"schema\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteUpdate\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"responses\": {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\"description\": \"OK\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"security\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"ApiKeyAuth\": []\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Timestamp\": []\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"summary\": \"Update website\",\n\t\t\t\t\"tags\": [\n\t\t\t\t\t\"Website\"\n\t\t\t\t],\n\t\t\t\t\"x-panel-log\": {\n\t\t\t\t\t\"BeforeFunctions\": [],\n\t\t\t\t\t\"bodyKeys\": [\n\t\t\t\t\t\t\"primaryDomain\"\n\t\t\t\t\t],\n\t\t\t\t\t\"formatEN\": \"Update website [primaryDomain]\",\n\t\t\t\t\t\"formatZH\": \"更新网站 [primaryDomain]\",\n\t\t\t\t\t\"paramKeys\": []\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\t\"definitions\": {\n\t\t\"dto.AddrRuleOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"accept\",\n\t\t\t\t\t\t\"drop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"address\",\n\t\t\t\t\"operation\",\n\t\t\t\t\"strategy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AddrRuleUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"newRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.AddrRuleOperate\"\n\t\t\t\t},\n\t\t\t\t\"oldRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.AddrRuleOperate\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountCreateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"contextWindow\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxTokens\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberApiKey\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"apiKey\",\n\t\t\t\t\"name\",\n\t\t\t\t\"provider\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountDeleteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"contextWindow\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxTokens\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberApiKey\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"syncAgents\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"apiKey\",\n\t\t\t\t\"id\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentAccountVerifyReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"apiKey\",\n\t\t\t\t\"provider\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentBrowserConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"defaultProfile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"executablePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"headless\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"noSandbox\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentBrowserConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentBrowserConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"defaultProfile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"headless\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"noSandbox\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"defaultProfile\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentChannelPairingApproveReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pairingCode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"feishu\",\n\t\t\t\t\t\t\"telegram\",\n\t\t\t\t\t\t\"discord\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"pairingCode\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentCreateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"agentType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"bridgePort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"contextWindow\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"maxTokens\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUIPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appVersion\",\n\t\t\t\t\"name\",\n\t\t\t\t\"webUIPort\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentDeleteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentDiscordConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"groupPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentDiscordConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentDiscordConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"groupPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"open\",\n\t\t\t\t\t\t\"allowlist\",\n\t\t\t\t\t\t\"disabled\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"dmPolicy\",\n\t\t\t\t\"groupPolicy\",\n\t\t\t\t\"token\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentFeishuConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appSecret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"botName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentFeishuConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentFeishuConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appSecret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"botName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"appId\",\n\t\t\t\t\"appSecret\",\n\t\t\t\t\"botName\",\n\t\t\t\t\"dmPolicy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentFeishuPairingApproveReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pairingCode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"pairingCode\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentItem\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"agentType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appInstallId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"bridgePort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"configPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"contextWindow\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxTokens\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"providerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"upgradable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"webUIPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentModelConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"accountId\",\n\t\t\t\t\"agentId\",\n\t\t\t\t\"model\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentOtherConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"userTimezone\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentOtherConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentOtherConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"userTimezone\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"userTimezone\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentPluginCheckReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"qqbot\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentPluginInstallReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"qqbot\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"taskID\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentPluginStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentQQBotConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientSecret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentQQBotConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentQQBotConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientSecret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"appId\",\n\t\t\t\t\"clientSecret\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentTelegramConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"botToken\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentTelegramConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentTelegramConfigUpdateReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"agentId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"botToken\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dmPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enabled\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"agentId\",\n\t\t\t\t\"botToken\",\n\t\t\t\t\"dmPolicy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AgentTokenResetReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ApiInterfaceConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"apiInterfaceStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apiKeyValidityTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipWhiteList\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppConfigVersion\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"additionalProperties\": {},\n\t\t\t\t\"downloadCallBackUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppDefine\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppProperty\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"readMe\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"versions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppConfigVersion\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppInstallInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppLauncher\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detail\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.InstallDetail\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isInstall\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isRecommend\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppList\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.ExtraProperties\"\n\t\t\t\t},\n\t\t\t\t\"apps\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppDefine\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"valid\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"violations\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppProperty\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"Required\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"architectures\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"batchInstallSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"crossVersionUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deprecated\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.Locale\"\n\t\t\t\t},\n\t\t\t\t\"document\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"github\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryRequired\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"shortDescEn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescZh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"website\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppResource\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppVersion\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppstoreConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"uninstallDeleteBackup\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uninstallDeleteImage\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"upgradeBackup\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.AppstoreUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"UninstallDeleteImage\",\n\t\t\t\t\t\t\"UpgradeBackup\",\n\t\t\t\t\t\t\"UninstallDeleteBackup\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Disable\",\n\t\t\t\t\t\t\"Enable\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"scope\",\n\t\t\t\t\"status\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BackupClientInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"client_id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"client_secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirect_uri\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BackupOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accessKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"backupPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"bucket\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"credential\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isPublic\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberAuth\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"vars\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\",\n\t\t\t\t\"vars\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BackupOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isPublic\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BatchDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"force\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"names\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"names\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BatchDeleteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BatchRuleOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"rules\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleOperate\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BindInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"bindAddress\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipv6\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Enable\",\n\t\t\t\t\t\t\"Disable\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"bindAddress\",\n\t\t\t\t\"ipv6\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.BindUser\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"db\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"permission\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"db\",\n\t\t\t\t\"password\",\n\t\t\t\t\"permission\",\n\t\t\t\t\"username\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CaptchaResponse\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"captchaID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"imagePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeDBInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"mysql-cluster\",\n\t\t\t\t\t\t\"postgresql-cluster\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"from\",\n\t\t\t\t\"type\",\n\t\t\t\t\"value\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeGroup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"groupID\",\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeHostGroup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"groupID\",\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangePasswd\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"passwd\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeQuicks\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"quicks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.QuickJump\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ChangeRedisPass\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"freshIsActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"freshIsExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"freshVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alertCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"alertMethod\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alertTitle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"infectedDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"infectedStrategy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"removeInfected\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamLogSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"clamID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"endTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"startTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alertCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"alertMethod\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alertTitle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"infectedDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"infectedStrategy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ClamUpdateStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Clean\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"treeType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CleanData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backupClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"containerClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"downloadClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"systemClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"systemLogClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"uploadClean\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CleanLog\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"logType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"login\",\n\t\t\t\t\t\t\"operation\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"logType\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CleanTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CleanTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isCheck\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isRecommend\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommandInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupBelong\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommandOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupBelong\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"command\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommandTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CommandTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommonBackup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"args\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fileName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"app\",\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"website\",\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"mysql-cluster\",\n\t\t\t\t\t\t\"postgresql-cluster\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommonDescription\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detailType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isPinned\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CommonRecover\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backupRecordID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"app\",\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"website\",\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"mysql-cluster\",\n\t\t\t\t\t\t\"postgresql-cluster\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"downloadAccountID\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"forcePull\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"edit\",\n\t\t\t\t\t\t\"path\",\n\t\t\t\t\t\t\"template\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"template\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"from\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeLogClean\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeOperation\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"force\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"up\",\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"stop\",\n\t\t\t\t\t\t\"down\",\n\t\t\t\t\t\t\"delete\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withFile\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeTemplateBatch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"templates\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ComposeTemplateCreate\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"templates\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeTemplateCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeTemplateInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeTemplateUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ComposeUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detailPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"forcePull\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"name\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerCommit\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"author\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"comment\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newImageName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pause\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"containerID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerFileBatchDeleteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"minItems\": 1,\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"containerID\",\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerFileContent\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isBinary\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"truncated\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerFileInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isLink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"linkTo\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"containerID\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerItemStats\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"buildCacheReclaimable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"buildCacheUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerReclaimable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"imageReclaimable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"imageUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sizeRootFs\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sizeRw\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"volumeReclaimable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"volumeUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerListStats\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuTotalUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryCache\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"percpuUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"systemUsage\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerNetwork\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ipv4\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipv6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"macAddr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"network\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRemove\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cmd\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"cpuShares\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dns\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"domainName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"entrypoint\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"forcePull\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"labels\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nanoCPUs\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"networks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ContainerNetwork\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"openStdin\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"privileged\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"publishAllPorts\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tty\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.VolumeHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"workingDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"image\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerOperation\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"names\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"up\",\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"stop\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"kill\",\n\t\t\t\t\t\t\"pause\",\n\t\t\t\t\t\t\"unpause\",\n\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"names\",\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerOptions\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"state\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerPrune\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"pruneType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"container\",\n\t\t\t\t\t\t\"image\",\n\t\t\t\t\t\t\"volume\",\n\t\t\t\t\t\t\"network\",\n\t\t\t\t\t\t\"buildcache\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withTagAll\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"pruneType\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerRename\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"newName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerStats\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cache\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"ioRead\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"ioWrite\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"networkRX\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"networkTX\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"shotTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"composeCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"composeTemplateCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"created\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dead\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"exited\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"imageCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"networkCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"paused\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"removing\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"repoCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"restarting\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"running\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"volumeCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ContainerUpgrade\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forcePull\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"names\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"image\",\n\t\t\t\t\"names\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobBatchDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cleanData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cleanRemoteData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobClean\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cleanData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cleanRemoteData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cronjobID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"cronjobID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobImport\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cronjobs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.CronjobTrans\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alertCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"alertMethod\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alertTitle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"args\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"exclusionRules\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"executor\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ignoreErr\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"retainCopies\": {\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"retryTimes\": {\n\t\t\t\t\t\"minimum\": 0,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"scopes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scriptID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"scriptMode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"snapshotRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotRule\"\n\t\t\t\t},\n\t\t\t\t\"sourceAccountIDs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sourceDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specCustom\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"website\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"spec\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobSpec\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"spec\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobTrans\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alertCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"alertMethod\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alertTitle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"apps\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TransHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"args\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbName\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TransHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadAccount\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"exclusionRules\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"executor\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ignoreErr\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"retainCopies\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"retryTimes\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scriptMode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scriptName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"snapshotRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.SnapshotTransHelper\"\n\t\t\t\t},\n\t\t\t\t\"sourceAccounts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"sourceDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"spec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specCustom\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websites\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.CronjobUpdateStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"status\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DBBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DBConfUpdateByFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"mysql-cluster\",\n\t\t\t\t\t\t\"postgresql-cluster\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DaemonJsonConf\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cgroupDriver\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"experimental\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"fixedCidrV6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"insecureRegistries\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"ip6Tables\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"iptables\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ipv6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSwarm\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"liveRestore\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"logMaxFile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logMaxSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"registryMirrors\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DaemonJsonUpdateByFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DashboardBase\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appInstalledNumber\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuCores\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuLogicalCores\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuMhz\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuModelName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cronjobNumber\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"currentInfo\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.DashboardCurrent\"\n\t\t\t\t},\n\t\t\t\t\"databaseNumber\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipV4Addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"kernelArch\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"kernelVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"os\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platform\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platformFamily\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platformVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"prettyDistro\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"quickJump\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.QuickJump\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"systemProxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"virtualizationSystem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteNumber\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DashboardCurrent\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cpuDetailedPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"cpuPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"cpuTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuUsed\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"diskData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DiskInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"gpuData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GPUInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"ioCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ioReadBytes\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ioReadTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ioWriteBytes\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ioWriteTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"load1\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load15\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load5\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"loadUsagePercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryCache\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryFree\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryShard\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"netBytesRecv\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"netBytesSent\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"procs\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"shotTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"timeSinceUptime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"topCPUItems\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Process\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"topMemItems\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Process\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"uptime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"xpuData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.XPUInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DataTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isCheck\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDisable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isLocal\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"relationItemID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"initialDB\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"rootCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipVerify\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"from\",\n\t\t\t\t\"name\",\n\t\t\t\t\"type\",\n\t\t\t\t\"username\",\n\t\t\t\t\"version\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"initialDB\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"rootCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipVerify\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseItem\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DatabaseUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"clientKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"initialDB\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"rootCert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipVerify\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\",\n\t\t\t\t\"username\",\n\t\t\t\t\"version\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DeviceBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dns\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.HostHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"localTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ntp\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"swapDetails\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SwapHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"timeZone\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DiskInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"free\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"inodesFree\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"inodesTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"inodesUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"inodesUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"usedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DockerOperation\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"stop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DockerStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.DownloadRecord\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"downloadAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"fileDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fileName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"downloadAccountID\",\n\t\t\t\t\"fileDir\",\n\t\t\t\t\"fileName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ExtraHost\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ip\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ExtraProperties\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.Tag\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Fail2BanBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"banAction\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"banTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"findTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isEnable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"logPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxRetry\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Fail2BanSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"banned\",\n\t\t\t\t\t\t\"ignore\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"status\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Fail2BanSet\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ips\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"banned\",\n\t\t\t\t\t\t\"ignore\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Fail2BanUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"port\",\n\t\t\t\t\t\t\"bantime\",\n\t\t\t\t\t\t\"findtime\",\n\t\t\t\t\t\t\"maxretry\",\n\t\t\t\t\t\t\"banaction\",\n\t\t\t\t\t\t\"logpath\",\n\t\t\t\t\t\t\"port\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FilePath\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FirewallBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isBind\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isInit\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pingStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FirewallOperation\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"stop\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"disableBanPing\",\n\t\t\t\t\t\t\"enableBanPing\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withDockerRestart\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ForBuckets\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accessKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"credential\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"vars\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"credential\",\n\t\t\t\t\"type\",\n\t\t\t\t\"vars\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ForceDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ForwardRuleOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"rules\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\"interface\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"num\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"operation\": {\n\t\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"port\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"protocol\": {\n\t\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\t\"tcp\",\n\t\t\t\t\t\t\t\t\t\"udp\",\n\t\t\t\t\t\t\t\t\t\"tcp/udp\"\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"targetIP\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"targetPort\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\"operation\",\n\t\t\t\t\t\t\t\"port\",\n\t\t\t\t\t\t\t\"protocol\",\n\t\t\t\t\t\t\t\"targetPort\"\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FtpBaseInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FtpCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"password\",\n\t\t\t\t\"path\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FtpLogSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.FtpUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"password\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GPUInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"fanSpeed\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuUtil\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"index\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxPowerLimit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memTotal\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memUsed\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsage\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"performanceState\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"powerDraw\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"powerUsage\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"productName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"temperature\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GPUProcess\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"pid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"processName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"usedMemory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GroupCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GroupSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.GroupUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isDefault\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostConnTest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"authMode\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"password\",\n\t\t\t\t\t\t\"key\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"maximum\": 65535,\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"addr\",\n\t\t\t\t\"port\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"host\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ip\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"authMode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupBelong\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberPassword\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"authMode\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"password\",\n\t\t\t\t\t\t\"key\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"maximum\": 65535,\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rememberPassword\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"addr\",\n\t\t\t\t\"port\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.HostTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TreeChild\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageBuild\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"args\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"dockerfile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"dockerfile\",\n\t\t\t\t\"from\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isPinned\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isUsed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageLoad\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImagePull\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"imageName\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"repoID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"imageName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImagePush\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"repoID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"tagName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"repoID\",\n\t\t\t\t\"tagName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageRepoDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageRepoOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"downloadUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageRepoUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"auth\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"downloadUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageSave\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tagName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"path\",\n\t\t\t\t\"tagName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ImageTag\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"sourceID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"sourceID\",\n\t\t\t\t\"tags\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.InspectReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.InstallDetail\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"httpPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"httpsPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"installID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.IptablesBatchOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"rules\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.IptablesRuleOp\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.IptablesOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"1PANEL_INPUT\",\n\t\t\t\t\t\t\"1PANEL_OUTPUT\",\n\t\t\t\t\t\t\"1PANEL_BASIC\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"init-base\",\n\t\t\t\t\t\t\"init-forward\",\n\t\t\t\t\t\t\"init-advance\",\n\t\t\t\t\t\t\"bind-base\",\n\t\t\t\t\t\t\"unbind-base\",\n\t\t\t\t\t\t\"bind\",\n\t\t\t\t\t\t\"unbind\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.IptablesRuleOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"chain\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"1PANEL_BASIC\",\n\t\t\t\t\t\t\"1PANEL_BASIC_BEFORE\",\n\t\t\t\t\t\t\"1PANEL_INPUT\",\n\t\t\t\t\t\t\"1PANEL_OUTPUT\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dstIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dstPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"srcIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"srcPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"accept\",\n\t\t\t\t\t\t\"drop\",\n\t\t\t\t\t\t\"reject\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"chain\",\n\t\t\t\t\"operation\",\n\t\t\t\t\"strategy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.LauncherOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isShow\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.LoadRedisStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Locale\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"en\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"es-es\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ja\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ko\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ms\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pt-br\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ru\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"zh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"zh-hant\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.LogOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"logMaxFile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logMaxSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Login\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"captcha\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"captchaID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"language\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"zh\",\n\t\t\t\t\t\t\"en\",\n\t\t\t\t\t\t\"zh-Hant\",\n\t\t\t\t\t\t\"ko\",\n\t\t\t\t\t\t\"ja\",\n\t\t\t\t\t\t\"ru\",\n\t\t\t\t\t\t\"ms\",\n\t\t\t\t\t\t\"pt-BR\",\n\t\t\t\t\t\t\"tr\",\n\t\t\t\t\t\t\"es-ES\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"language\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MFALogin\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"code\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"code\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MemoUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"maxLength\": 500,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MfaCredential\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"code\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"interval\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"code\",\n\t\t\t\t\"interval\",\n\t\t\t\t\"secret\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"date\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"param\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"items\": {},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorGPUData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"date\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"gpuProcesses\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.GPUProcess\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"gpuValue\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"memoryPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"memoryTotal\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsed\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"powerPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"powerTotal\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"powerUsed\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"processCount\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"speedValue\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"temperatureValue\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorGPUSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"endTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"productName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"startTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"endTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"io\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"network\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"param\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"all\",\n\t\t\t\t\t\t\"cpu\",\n\t\t\t\t\t\t\"memory\",\n\t\t\t\t\t\t\"load\",\n\t\t\t\t\t\t\"io\",\n\t\t\t\t\t\t\"network\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"startTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"param\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorSetting\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"defaultIO\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultNetwork\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorInterval\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorStoreDays\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MonitorSettingUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"MonitorStatus\",\n\t\t\t\t\t\t\"MonitorStoreDays\",\n\t\t\t\t\t\t\"MonitorInterval\",\n\t\t\t\t\t\t\"DefaultNetwork\",\n\t\t\t\t\t\t\"DefaultIO\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlDBCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"collation\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"format\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"permission\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"format\",\n\t\t\t\t\"from\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\",\n\t\t\t\t\"permission\",\n\t\t\t\t\"username\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlDBDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"mysql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlDBDeleteCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"mysql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlDBSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlFormatCollationOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"collations\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"format\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlLoadDB\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"mysql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"from\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"Aborted_clients\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Aborted_connects\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Bytes_received\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Bytes_sent\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Com_commit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Com_rollback\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Connections\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Created_tmp_disk_tables\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Created_tmp_tables\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"File\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Innodb_buffer_pool_pages_dirty\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Innodb_buffer_pool_read_requests\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Innodb_buffer_pool_reads\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Key_read_requests\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Key_reads\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Key_write_requests\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Key_writes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Max_used_connections\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Open_tables\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Opened_files\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Opened_tables\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Position\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Qcache_hits\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Qcache_inserts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Questions\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Run\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Select_full_join\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Select_range_check\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Sort_merge_passes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Table_locks_waited\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Threads_cached\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Threads_connected\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Threads_created\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Threads_running\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"Uptime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlVariables\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"binlog_cache_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"innodb_buffer_pool_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"innodb_log_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"join_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"key_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"long_query_time\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"max_connections\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"max_heap_table_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"query_cache_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"query_cache_type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"read_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"read_rnd_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"slow_query_log\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sort_buffer_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"table_open_cache\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"thread_cache_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"thread_stack\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tmp_table_size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlVariablesUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"mysql\",\n\t\t\t\t\t\t\"mariadb\",\n\t\t\t\t\t\t\"mysql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"variables\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.MysqlVariablesUpdateHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.MysqlVariablesUpdateHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"param\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NetworkCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"auxAddress\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"auxAddressV6\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.SettingUpdate\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"driver\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gateway\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gatewayV6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipRange\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipRangeV6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipv4\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ipv6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"labels\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"options\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"subnet\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"subnetV6\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"driver\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NginxAuth\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NginxKey\": {\n\t\t\t\"enum\": [\n\t\t\t\t\"index\",\n\t\t\t\t\"limit-conn\",\n\t\t\t\t\"ssl\",\n\t\t\t\t\"cache\",\n\t\t\t\t\"http-per\",\n\t\t\t\t\"proxy-cache\"\n\t\t\t],\n\t\t\t\"type\": \"string\",\n\t\t\t\"x-enum-varnames\": [\n\t\t\t\t\"Index\",\n\t\t\t\t\"LimitConn\",\n\t\t\t\t\"SSL\",\n\t\t\t\t\"CACHE\",\n\t\t\t\t\"HttpPer\",\n\t\t\t\t\"ProxyCache\"\n\t\t\t]\n\t\t},\n\t\t\"dto.NginxUpstream\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NginxUpstreamServer\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"failTimeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"failTimeoutUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"flag\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxConns\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxFails\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"server\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"weight\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.NodeCurrent\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cpuDetailedPercent\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"cpuTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cpuUsed\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cpuUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load1\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load15\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"load5\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"loadUsagePercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryAvailable\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsed\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"swapMemoryUsedPercent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaBindDomain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appInstallID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipList\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appInstallID\",\n\t\t\t\t\"domain\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaBindDomainReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appInstallID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appInstallID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaBindDomainRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"allowIPs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"connUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaModelDropList\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OllamaModelName\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Operate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByID\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByIDs\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByName\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByTaskID\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperateByType\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperationWithName\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OperationWithNameAndType\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Options\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"option\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.OsInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"diskSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"kernelArch\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"kernelVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"os\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platform\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"platformFamily\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"prettyDistro\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageContainer\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"excludeAppStore\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"filters\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\"state\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"state\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"all\",\n\t\t\t\t\t\t\"created\",\n\t\t\t\t\t\t\"running\",\n\t\t\t\t\t\t\"paused\",\n\t\t\t\t\t\t\"restarting\",\n\t\t\t\t\t\t\"removing\",\n\t\t\t\t\t\t\"exited\",\n\t\t\t\t\t\t\"dead\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"state\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageCronjob\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupIDs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"status\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageImage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"size\",\n\t\t\t\t\t\t\"tags\",\n\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\"isUsed\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageResult\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"items\": {},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PageSnapshot\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PasskeyBeginResponse\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"publicKey\": {},\n\t\t\t\t\"sessionId\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PasskeyInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastUsedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PasskeyRegisterRequest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PasswordUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"newPassword\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"oldPassword\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"newPassword\",\n\t\t\t\t\"oldPassword\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PortHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hostPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PortRuleOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"address\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"chain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"remove\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"tcp\",\n\t\t\t\t\t\t\"udp\",\n\t\t\t\t\t\t\"tcp/udp\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"accept\",\n\t\t\t\t\t\t\"drop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\",\n\t\t\t\t\"port\",\n\t\t\t\t\"protocol\",\n\t\t\t\t\"strategy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PortRuleUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"newRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleOperate\"\n\t\t\t\t},\n\t\t\t\t\"oldRule\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.PortRuleOperate\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PortUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"serverPort\": {\n\t\t\t\t\t\"maximum\": 65535,\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"serverPort\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlBindUser\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"superUser\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\",\n\t\t\t\t\"username\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlDBCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"format\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"superUser\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"from\",\n\t\t\t\t\"name\",\n\t\t\t\t\"password\",\n\t\t\t\t\"username\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlDBDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"postgresql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlDBDeleteCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"postgresql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlDBSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.PostgresqlLoadDB\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"local\",\n\t\t\t\t\t\t\"remote\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"postgresql\",\n\t\t\t\t\t\t\"postgresql-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"from\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Process\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cmd\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"percent\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"pid\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ProviderInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"displayName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"models\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.ProviderModelInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ProviderModelInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ProxyUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"proxyDocker\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"proxyPasswd\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyPasswdKeep\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyUser\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withDockerRestart\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.QuickJump\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"detail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isShow\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"router\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"title\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RecordFileSize\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RecordSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RecordSearchByCronjob\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cronjobID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"cronjobID\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisConf\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxclients\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxmemory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"requirepass\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisConfPersistenceUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appendfsync\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appendonly\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"save\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"aof\",\n\t\t\t\t\t\t\"rbd\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"dbType\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisConfUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"redis\",\n\t\t\t\t\t\t\"redis-cluster\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxclients\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"maxmemory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\",\n\t\t\t\t\"dbType\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisPersistence\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appendfsync\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appendonly\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"save\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RedisStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"connected_clients\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"database\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"instantaneous_ops_per_sec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keyspace_hits\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keyspace_misses\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"latest_fork_usec\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mem_fragmentation_ratio\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tcp_port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"total_commands_processed\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"total_connections_received\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uptime_in_days\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used_memory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used_memory_peak\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used_memory_rss\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"database\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ReleasesNotes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fixCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"newCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"optimizationCount\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ResourceLimit\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cpu\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RootCertOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"encryptionMode\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"rsa\",\n\t\t\t\t\t\t\"ed25519\",\n\t\t\t\t\t\t\"ecdsa\",\n\t\t\t\t\t\t\"dsa\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"publicKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"encryptionMode\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.RuleSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHConf\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHConnData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"addr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"authMode\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"password\",\n\t\t\t\t\t\t\"key\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"localSSHConnShow\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passPhrase\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"maximum\": 65535,\n\t\t\t\t\t\"minimum\": 1,\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"addr\",\n\t\t\t\t\"port\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHDefaultConn\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"defaultConn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withReset\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoStart\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"currentUser\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isActive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"listenAddress\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"passwordAuthentication\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"permitRootLogin\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pubkeyAuthentication\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"useDNS\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSHUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newValue\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"oldValue\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSLInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rootPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SSLUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cert\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Enable\",\n\t\t\t\t\t\t\"Disable\",\n\t\t\t\t\t\t\"Mux\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sslType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"self\",\n\t\t\t\t\t\t\"select\",\n\t\t\t\t\t\t\"import\",\n\t\t\t\t\t\t\"import-paste\",\n\t\t\t\t\t\t\"import-local\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ssl\",\n\t\t\t\t\"sslType\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ScriptOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"groups\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isInteractive\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.ScriptOptions\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchByFilter\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"filter\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchClamWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"status\",\n\t\t\t\t\t\t\"createdAt\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchForSize\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cronjobID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchForTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchLgLogWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ip\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchOpLogWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"node\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchPageWithGroup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchPageWithType\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchRecord\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cronjobID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"endTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"startTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchSSHLog\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"Status\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Success\",\n\t\t\t\t\t\t\"Failed\",\n\t\t\t\t\t\t\"All\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"Status\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchTaskLogReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SearchWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"info\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SettingInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appStoreLastModified\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appStoreSyncStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appStoreVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultIO\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultNetwork\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerSockPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fileRecycleBin\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastCleanData\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastCleanSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastCleanTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"localTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorInterval\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"monitorStoreDays\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ntpSite\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"systemIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"systemVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeZone\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SettingUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotBatchDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"deleteWithFile\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"backupData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ignoreFiles\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"interruptStep\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"panelData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sourceAccountIDs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"timeout\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"withDockerConf\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withLoginLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withMonitorData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withOperationLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withSystemLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withTaskLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"downloadAccountID\",\n\t\t\t\t\"sourceAccountIDs\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotData\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"backupData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"ignoreFiles\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"panelData\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.DataTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"withDockerConf\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withLoginLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withMonitorData\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withOperationLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withSystemLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"withTaskLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotImport\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backupAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"names\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotRecover\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isNew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"reDownload\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotRule\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ignoreAppIDs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"withImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SnapshotTransHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ignoreApps\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.TransHelper\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"withImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SwapHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isNew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"used\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.SystemSetting\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDemo\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isIntl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"language\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Tag\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"locales\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.Locale\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.TerminalInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backgroundColor\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cursorBlink\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cursorStyle\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fontFamily\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"fontSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"foregroundColor\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"letterSpacing\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lineHeight\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scrollSensitivity\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scrollback\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.TransHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detailName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.TreeChild\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpdateByFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpdateByNameAndFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpdateDescription\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"maxLength\": 256,\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpdateFirewallDescription\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"chain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dstIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dstPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"srcIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"srcPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"strategy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"accept\",\n\t\t\t\t\t\t\"drop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"strategy\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.Upgrade\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"version\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UpgradeInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"latestVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"releaseNote\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"testVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UploadForRecover\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"filePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"targetDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.UserLoginInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"mfaStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"token\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.VolumeCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"driver\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"labels\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"options\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"driver\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.VolumeHelper\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shared\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sourceDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"dto.XPUInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"deviceID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"deviceName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memory\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memoryUsed\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memoryUtil\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"power\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"temperature\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"files.FileInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"extension\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favoriteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"gid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isDetail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isHidden\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSymlink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"itemTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/files.FileInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"linkPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mimeType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updateTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"mfa.Otp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"qrImage\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.App\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"architectures\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"batchInstallSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"crossVersionUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"document\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"github\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryRequired\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"readMe\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"required\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"requiredPanelVersion\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescEn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescZh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"website\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.AppIgnoreUpgrade\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.AppInstall\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"app\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.App\"\n\t\t\t\t},\n\t\t\t\t\"appDetailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"httpPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"httpsPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"param\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serviceName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sortOrder\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.Favorite\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isTxt\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.Runtime\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"workDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.Website\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"IPV6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"accessLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appInstallId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultServer\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDomain\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"errorLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ftpId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"parentWebsiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rewrite\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"runtimeID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"siteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamPorts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webSiteGroupId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"webSiteSSL\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteSSL\"\n\t\t\t\t},\n\t\t\t\t\"webSiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.WebsiteAcmeAccount\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"caDirURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabHmacKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabKid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"email\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"useEAB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"useProxy\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.WebsiteDnsAccount\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.WebsiteDomain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"model.WebsiteSSL\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteAcmeAccount\"\n\t\t\t\t},\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"caId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"certPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"disableCNAME\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDnsAccount\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isIP\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"masterSslId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver1\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver2\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organization\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipDNS\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"startDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websites\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/model.Website\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppConfigUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"installID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"installID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppIgnoreUpgradeReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"all\",\n\t\t\t\t\t\t\"version\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appID\",\n\t\t\t\t\"scope\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstallCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"appDetailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuConfig\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"always\",\n\t\t\t\t\t\t\"unless-stopped\",\n\t\t\t\t\t\t\"no\",\n\t\t\t\t\t\t\"on-failure\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"services\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"appDetailId\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstalledInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"key\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstalledOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"backupId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deleteDB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deleteImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"detailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"installId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"installId\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstalledSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"all\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"checkUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sync\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"unused\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"update\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppInstalledUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuConfig\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"installId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"always\",\n\t\t\t\t\t\t\"unless-stopped\",\n\t\t\t\t\t\t\"no\",\n\t\t\t\t\t\t\"on-failure\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"installId\",\n\t\t\t\t\"params\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.AppSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"showCurrentArch\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.BatchWebsiteGroup\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"groupID\",\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.BatchWebsiteHttps\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"SSLProtocol\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificatePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hsts\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hstsIncludeSubDomains\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"http3\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"HTTPSOnly\",\n\t\t\t\t\t\t\"HTTPAlso\",\n\t\t\t\t\t\t\"HTTPToHTTPS\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpsPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"importType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"existed\",\n\t\t\t\t\t\t\"auto\",\n\t\t\t\t\t\t\"manual\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\",\n\t\t\t\t\"taskID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.BatchWebsiteOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\",\n\t\t\t\t\"operate\",\n\t\t\t\t\"taskID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ChangeDatabase\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"databaseID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"databaseType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.CorsConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"allowCredentials\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowHeaders\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowMethods\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowOrigins\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cors\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"preflight\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.CorsConfigReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"allowCredentials\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowHeaders\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowMethods\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowOrigins\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cors\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"preflight\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.CrossSiteAccessOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operation\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"Enable\",\n\t\t\t\t\t\t\"Disable\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operation\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.CustomRewriteOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"create\",\n\t\t\t\t\t\t\"delete\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.DirSizeReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.DiskMountRequest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoMount\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"filesystem\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"ext4\",\n\t\t\t\t\t\t\"xfs\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"noFail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"device\",\n\t\t\t\t\"filesystem\",\n\t\t\t\t\"mountPoint\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.DiskPartitionRequest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoMount\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"filesystem\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"ext4\",\n\t\t\t\t\t\t\"xfs\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"device\",\n\t\t\t\t\"filesystem\",\n\t\t\t\t\"mountPoint\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.DiskUnmountRequest\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"mountPoint\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.Environment\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ExecComposerReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"extCommand\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mirror\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"command\",\n\t\t\t\t\"dir\",\n\t\t\t\t\"mirror\",\n\t\t\t\t\"taskID\",\n\t\t\t\t\"user\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ExposedPort\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"hostPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ExtraHost\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"hostname\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ip\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FPMConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"params\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FavoriteCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FavoriteDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileBatchDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileCompress\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dst\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"files\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"replace\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"dst\",\n\t\t\t\t\"files\",\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileContentReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDetail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileConvert\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"extension\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"inputFile\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"outputFormat\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"extension\",\n\t\t\t\t\"inputFile\",\n\t\t\t\t\"outputFormat\",\n\t\t\t\t\"path\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isLink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSymlink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"linkPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sub\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileDeCompress\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dst\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"secret\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"dst\",\n\t\t\t\t\"path\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileDownload\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"compress\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"paths\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileEdit\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileMove\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cover\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"coverPaths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"newPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"oldPaths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"newPath\",\n\t\t\t\t\"oldPaths\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containSub\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expand\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDetail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"search\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"showHidden\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"sortBy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sortOrder\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FilePathCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"withInit\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FilePathsCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileReadByLineReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"latest\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"resourceID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskOperate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRemarkBatch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"paths\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRemarkUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRename\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"newName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"oldName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"newName\",\n\t\t\t\t\"oldName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRoleReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"paths\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"sub\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"group\",\n\t\t\t\t\"mode\",\n\t\t\t\t\"paths\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileRoleUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sub\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"group\",\n\t\t\t\t\"path\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.FileWget\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ignoreCertificate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"path\",\n\t\t\t\t\"url\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.HostToolConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"get\",\n\t\t\t\t\t\t\"set\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"supervisord\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.HostToolCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"configPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serviceName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.HostToolReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"status\",\n\t\t\t\t\t\t\"restart\",\n\t\t\t\t\t\t\"start\",\n\t\t\t\t\t\t\"stop\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"supervisord\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpBindDomain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipList\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"domain\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpBindDomainUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ipList\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"outputTransport\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamableHttpPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"command\",\n\t\t\t\t\"name\",\n\t\t\t\t\"outputTransport\",\n\t\t\t\t\"port\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"sync\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.McpServerUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"outputTransport\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamableHttpPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"command\",\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"outputTransport\",\n\t\t\t\t\"port\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NewAppInstall\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuConfig\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"always\",\n\t\t\t\t\t\t\"unless-stopped\",\n\t\t\t\t\t\t\"no\",\n\t\t\t\t\t\t\"on-failure\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxAntiLeechUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"blocked\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cache\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cacheTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheUint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"extends\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logEnable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"noneRef\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"return\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serverNames\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxAuthReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxAuthUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxBuildReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"mirror\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"mirror\",\n\t\t\t\t\"taskID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxCommonReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxConfigFileUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"backup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxConfigUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"add\",\n\t\t\t\t\t\t\"update\",\n\t\t\t\t\t\t\"delete\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxKey\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxDefaultHTTPSUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"enable\",\n\t\t\t\t\t\t\"disable\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslRejectHandshake\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxModuleUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"create\",\n\t\t\t\t\t\t\"delete\",\n\t\t\t\t\t\t\"update\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"packages\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxPathAuthUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"operate\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxProxyCacheUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cacheExpire\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheExpireUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cacheLimit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheLimitUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"open\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shareCache\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"shareCacheUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"cacheExpire\",\n\t\t\t\t\"cacheExpireUnit\",\n\t\t\t\t\"cacheLimit\",\n\t\t\t\t\"cacheLimitUnit\",\n\t\t\t\t\"shareCache\",\n\t\t\t\t\"shareCacheUnit\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxProxyUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxRedirectReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"keepPath\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirect\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirectRoot\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\",\n\t\t\t\t\"redirect\",\n\t\t\t\t\"target\",\n\t\t\t\t\"type\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxRedirectUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxRewriteReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxRewriteUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NginxScopeReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxKey\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"scope\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NodeModuleReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.NodePackageReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPConfigUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"disableFunctions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"maxExecutionTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uploadMaxSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"scope\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPContainerConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExposedPort\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionInstallReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ID\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionsCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"extensions\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"extensions\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionsDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionsSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"all\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPExtensionsUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"extensions\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"extensions\",\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPFileUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"id\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPSupervisorProcessConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRestart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"autoStart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environment\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"numprocs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PHPSupervisorProcessFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"out.log\",\n\t\t\t\t\t\t\"err.log\",\n\t\t\t\t\t\t\"config\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"get\",\n\t\t\t\t\t\t\"clear\",\n\t\t\t\t\t\t\"update\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"file\",\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.PortUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ProcessReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"PID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"PID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RecycleBinReduce\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"from\",\n\t\t\t\t\"rName\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.ReqWithID\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"clean\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExposedPort\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"install\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeOperate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeRemark\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.RuntimeUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"clean\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExposedPort\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"install\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"rebuild\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.SearchUploadWithPage\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\",\n\t\t\t\t\"path\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.StreamUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"streamPorts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"udp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.SupervisorProcessConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRestart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"autoStart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environment\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"numprocs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.SupervisorProcessFileReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"out.log\",\n\t\t\t\t\t\t\"err.log\",\n\t\t\t\t\t\t\"config\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"get\",\n\t\t\t\t\t\t\"clear\",\n\t\t\t\t\t\t\"update\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"file\",\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.Volume\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteAcmeAccountCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"caDirURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabHmacKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabKid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"email\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"P256\",\n\t\t\t\t\t\t\"P384\",\n\t\t\t\t\t\t\"2048\",\n\t\t\t\t\t\t\"3072\",\n\t\t\t\t\t\t\"4096\",\n\t\t\t\t\t\t\"8192\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"letsencrypt\",\n\t\t\t\t\t\t\"zerossl\",\n\t\t\t\t\t\t\"buypass\",\n\t\t\t\t\t\t\"google\",\n\t\t\t\t\t\t\"custom\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"useEAB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"useProxy\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"email\",\n\t\t\t\t\"keyType\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteAcmeAccountUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"useProxy\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteBatchDelReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ids\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ids\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCACreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"city\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"commonName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"country\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"P256\",\n\t\t\t\t\t\t\"P384\",\n\t\t\t\t\t\t\"2048\",\n\t\t\t\t\t\t\"3072\",\n\t\t\t\t\t\t\"4096\",\n\t\t\t\t\t\t\"8192\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organization\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organizationUint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"province\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"commonName\",\n\t\t\t\t\"country\",\n\t\t\t\t\"keyType\",\n\t\t\t\t\"name\",\n\t\t\t\t\"organization\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCAObtain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"P256\",\n\t\t\t\t\t\t\"P384\",\n\t\t\t\t\t\t\"2048\",\n\t\t\t\t\t\t\"3072\",\n\t\t\t\t\t\t\"4096\",\n\t\t\t\t\t\t\"8192\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"renew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"time\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"unit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"domains\",\n\t\t\t\t\"id\",\n\t\t\t\t\"keyType\",\n\t\t\t\t\"time\",\n\t\t\t\t\"unit\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCASearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCommonReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"IPV6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appInstall\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/request.NewAppInstall\"\n\t\t\t\t},\n\t\t\t\t\"appInstallID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appType\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"new\",\n\t\t\t\t\t\t\"installed\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createDb\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dbFormat\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbHost\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbPassword\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbUser\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomain\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"enableSSL\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ftpPassword\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ftpUser\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"parentWebsiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"runtimeID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"siteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamPorts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"udp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"webSiteGroupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteSSLID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"alias\",\n\t\t\t\t\"type\",\n\t\t\t\t\"webSiteGroupID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDNSReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"acmeAccountId\",\n\t\t\t\t\"websiteSSLId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDefaultUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"deleteApp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deleteBackup\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"deleteDB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"forceDelete\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDnsAccountCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"authorization\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"authorization\",\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDnsAccountUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"authorization\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"authorization\",\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDomain\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"domain\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDomainCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.WebsiteDomain\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"domains\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDomainDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteDomainUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssl\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteHTTPSOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"SSLProtocol\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificatePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hsts\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hstsIncludeSubDomains\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"http3\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"HTTPSOnly\",\n\t\t\t\t\t\t\"HTTPAlso\",\n\t\t\t\t\t\t\"HTTPToHTTPS\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpsPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"importType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"existed\",\n\t\t\t\t\t\t\"auto\",\n\t\t\t\t\t\t\"manual\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteId\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteHtmlUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sync\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteInstallCheckReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"InstallIds\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLBCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLBDelete\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLBUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLBUpdateFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"name\",\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteLogReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"logType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"logType\",\n\t\t\t\t\"operate\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteNginxUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"content\",\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteOp\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsitePHPVersionReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"runtimeID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteProxyConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"allowCredentials\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowHeaders\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowMethods\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"allowOrigins\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cache\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cacheTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cors\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"filePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"match\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modifier\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"operate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"preflight\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"proxyHost\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyPass\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxySSLName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"replaces\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"serverCacheTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"serverCacheUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sni\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"match\",\n\t\t\t\t\"name\",\n\t\t\t\t\"operate\",\n\t\t\t\t\"proxyHost\",\n\t\t\t\t\"proxyPass\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteProxyDel\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteProxyReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteProxyStatusUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"status\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteRealIP\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ipFrom\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipHeader\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipOther\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"open\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteResourceReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLApply\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"disableLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"nameservers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"skipDNSCheck\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"ID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLCreate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"apply\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"disableCNAME\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isIp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver1\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver2\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"otherDomains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipDNS\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"acmeAccountId\",\n\t\t\t\t\"primaryDomain\",\n\t\t\t\t\"provider\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLListReq\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"created_at\",\n\t\t\t\t\t\t\"expire_date\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"apply\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"disableCNAME\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver1\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver2\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"otherDomains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipDNS\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"primaryDomain\",\n\t\t\t\t\"provider\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSSLUpload\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"certificate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certificatePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"paste\",\n\t\t\t\t\t\t\"local\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteSearch\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"null\",\n\t\t\t\t\t\t\"ascending\",\n\t\t\t\t\t\t\"descending\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"orderBy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"primary_domain\",\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"status\",\n\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\"expire_date\",\n\t\t\t\t\t\t\"created_at\",\n\t\t\t\t\t\t\"favorite\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"page\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"pageSize\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteGroupId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"order\",\n\t\t\t\t\"orderBy\",\n\t\t\t\t\"page\",\n\t\t\t\t\"pageSize\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteUpdate\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"IPV6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webSiteGroupID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"primaryDomain\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteUpdateDir\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"siteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"siteDir\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"request.WebsiteUpdateDirPermission\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"group\",\n\t\t\t\t\"id\",\n\t\t\t\t\"user\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"advanced\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"allowPort\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cpuQuota\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"editCompose\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuConfig\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"memoryLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"memoryUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppParam\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"pullImage\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"rawCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"restartPolicy\": {\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"always\",\n\t\t\t\t\t\t\"unless-stopped\",\n\t\t\t\t\t\t\"no\",\n\t\t\t\t\t\t\"on-failure\"\n\t\t\t\t\t],\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"specifyIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webUI\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"architectures\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"batchInstallSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"crossVersionUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"document\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"github\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"memoryRequired\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"readMe\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"required\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"requiredPanelVersion\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescEn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"shortDescZh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.TagDTO\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"versions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"website\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppDetailDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"architectures\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadCallBackUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"downloadUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hostMode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"lastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"lastVersion\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"memoryRequired\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"params\": {},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"update\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppDetailSimpleDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppInstalledCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"app\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appInstallId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"httpsPort\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"installPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isExist\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"lastBackupAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppItem\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"batchInstallSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"gpuSupport\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"limit\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"recommend\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppParam\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"edit\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.Locale\"\n\t\t\t\t},\n\t\t\t\t\"labelEn\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"labelZh\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"multiple\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"required\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"rule\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"showValue\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {},\n\t\t\t\t\"values\": {}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppItem\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppService\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"config\": {},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.AppUpdateRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appList\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/dto.AppList\"\n\t\t\t\t},\n\t\t\t\t\"appStoreLastModified\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"canUpdate\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSyncing\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.CompleteDiskInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"disks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DiskInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"systemDisks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DiskInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"totalCapacity\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"totalDisks\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"unpartitionedDisks\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DiskBasicInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.ComponentInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"error\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"exists\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.Database\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"databaseName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DatabaseConn\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"password\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"serviceName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DepthDirSizeRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DirSizeRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"size\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DiskBasicInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"avail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"diskType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"filesystem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isMounted\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isRemovable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSystem\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serial\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"usePercent\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"used\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.DiskInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"avail\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"device\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"diskType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"filesystem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isMounted\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isRemovable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSystem\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"model\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mountPoint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"partitions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.DiskBasicInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"serial\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"usePercent\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"used\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.ExistFileInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"modTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"extension\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favoriteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"gid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isDetail\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isHidden\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"isSymlink\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"itemTotal\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/files.FileInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"linkPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mimeType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"mode\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"size\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updateTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileLineContent\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"end\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"lines\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"scope\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"taskStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"totalLines\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileRemarksRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"remarks\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileTree\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"children\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.FileTree\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extension\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"isDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.FileWgetRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.HostToolConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.HostToolRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"config\": {},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.McpBindDomainRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccountID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"allowIPs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"connUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sslID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.McpServerDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"baseUrl\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dockerCompose\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"env\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"hostIP\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"outputTransport\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"ssePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamableHttpPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.McpServersRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.McpServerDTO\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"total\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxAntiLeechRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"blocked\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cache\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"cacheTime\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheUint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"extends\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logEnable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"noneRef\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"return\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"serverNames\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxAuthRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxAuth\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxBuildConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"mirror\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"modules\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxModule\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxConfigRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"https\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"sslRejectHandshake\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxFile\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxModule\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"packages\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxParam\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxPathAuthRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxProxyCache\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"cacheExpire\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"cacheExpireUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cacheLimit\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"cacheLimitUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"open\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shareCache\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"shareCacheUnit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxRedirectConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"filePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"keepPath\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirect\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"redirectRoot\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxRewriteRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NginxStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"accepts\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"active\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"handled\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"reading\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"requests\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"waiting\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"writing\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.NodeModule\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"license\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.PHPConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"disableFunctions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"maxExecutionTime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"uploadMaxSize\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.PHPExtensionRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"extensions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"supportExtensions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.SupportExtension\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.PackageScripts\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"script\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.ProcessStatus\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"PID\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"msg\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"uptime\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.Resource\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"detail\": {},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resourceID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.RuntimeDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appDetailID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appParams\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.AppParam\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"codeDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"container\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"containerStatus\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environments\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Environment\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"exposedPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExposedPort\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"extraHosts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.ExtraHost\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"image\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"additionalProperties\": true,\n\t\t\t\t\t\"type\": \"object\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"port\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"source\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"volumes\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/request.Volume\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.SupervisorProcessConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"autoRestart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"autoStart\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"command\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"environment\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"msg\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"numprocs\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.ProcessStatus\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.SupportExtension\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"check\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"file\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"installed\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"versions\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.TagDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"key\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.UserGroupResponse\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"groups\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"users\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.UserInfo\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.UserInfo\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"username\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteAcmeAccountDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"caDirURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabHmacKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"eabKid\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"email\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"useEAB\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"useProxy\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteCADTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"city\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"commonName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"country\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"csr\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organization\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organizationUint\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"province\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteDNSRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"domain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"err\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"resolve\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"value\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"IPV6\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"accessLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"accessLogPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"appInstallId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"appName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dbID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"dbType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"defaultServer\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDomain\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"errorLog\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"errorLogPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"favorite\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"ftpId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"openBaseDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"parentWebsiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"protocol\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxy\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"proxyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"remark\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"rewrite\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"runtimeID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"runtimeName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"runtimeType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"servers\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/dto.NginxUpstreamServer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"siteDir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"sitePath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"streamPorts\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"udp\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"webSiteGroupId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"webSiteSSL\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteSSL\"\n\t\t\t\t},\n\t\t\t\t\"webSiteSSLId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteDirConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"dirs\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"msg\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"user\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"userGroup\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteHTTPS\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"SSL\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteSSL\"\n\t\t\t\t},\n\t\t\t\t\"SSLProtocol\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t},\n\t\t\t\t\"algorithm\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hsts\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"hstsIncludeSubDomains\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"http3\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"httpConfig\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpsPort\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"httpsPorts\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteHtmlRes\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteLog\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"content\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"end\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"path\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteNginxConfig\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"enable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"params\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/response.NginxParam\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteOption\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"alias\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsitePreInstallCheck\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"appName\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteRealIP\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"ipFrom\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipHeader\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"ipOther\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"open\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"websiteID\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"websiteID\"\n\t\t\t],\n\t\t\t\"type\": \"object\"\n\t\t},\n\t\t\"response.WebsiteSSLDTO\": {\n\t\t\t\"properties\": {\n\t\t\t\t\"acmeAccount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteAcmeAccount\"\n\t\t\t\t},\n\t\t\t\t\"acmeAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"autoRenew\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"caId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"certPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"certURL\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"createdAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"dir\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"disableCNAME\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/model.WebsiteDnsAccount\"\n\t\t\t\t},\n\t\t\t\t\"dnsAccountId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"domains\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"execShell\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"expireDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"isIP\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"keyType\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"logPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"masterSslId\": {\n\t\t\t\t\t\"type\": \"integer\"\n\t\t\t\t},\n\t\t\t\t\"message\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver1\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nameserver2\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"nodes\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"organization\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pem\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"primaryDomain\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"privateKeyPath\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"provider\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"pushDir\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"pushNode\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"shell\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"skipDNS\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"startDate\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"status\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"updatedAt\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"websites\": {\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/model.Website\"\n\t\t\t\t\t},\n\t\t\t\t\t\"type\": \"array\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"type\": \"object\"\n\t\t}\n\t}\n}"
  },
  {
    "path": "core/cmd/server/docs/swagger_test.go",
    "content": "package docs\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestGenerateXlog(t *testing.T) {\n\tworkDir := \"/usr/songliu/dev-v2/1Panel\"\n\tfset := token.NewFileSet()\n\n\tapiDirs := []string{workDir + \"/agent/app/api/v2\", workDir + \"/core/app/api/v2\", workDir + \"/agent/xpack/app/api/v2\", workDir + \"/core/xpack/app/api/v2\"}\n\n\txlogMap := make(map[string]operationJson)\n\tfor _, dir := range apiDirs {\n\t\tentries, _ := os.ReadDir(dir)\n\t\tfor _, info := range entries {\n\t\t\tif info.IsDir() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfileItem, err := parser.ParseFile(fset, path.Join(dir, info.Name()), nil, parser.ParseComments)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, decl := range fileItem.Decls {\n\t\t\t\tswitch d := decl.(type) {\n\t\t\t\tcase *ast.FuncDecl:\n\t\t\t\t\tif d.Doc != nil {\n\t\t\t\t\t\trouterContent := \"\"\n\t\t\t\t\t\tlogContent := \"\"\n\t\t\t\t\t\tfor _, comment := range d.Doc.List {\n\t\t\t\t\t\t\tif strings.HasPrefix(comment.Text, \"// @Router\") {\n\t\t\t\t\t\t\t\trouterContent = replaceStr(comment.Text, \"// @Router\", \"[post]\", \"[get]\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif strings.HasPrefix(comment.Text, \"// @x-panel-log\") {\n\t\t\t\t\t\t\t\tlogContent = replaceStr(comment.Text, \"// @x-panel-log\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif len(routerContent) != 0 && len(logContent) != 0 {\n\t\t\t\t\t\t\tvar item operationJson\n\t\t\t\t\t\t\tif err := json.Unmarshal([]byte(logContent), &item); err != nil {\n\t\t\t\t\t\t\t\tpanic(fmt.Sprintf(\"json unmarshal failed, err: %v\", err))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\txlogMap[routerContent] = item\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tnewJson, err := json.MarshalIndent(xlogMap, \"\", \"\\t\")\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"json marshal for new file failed, err: %v\", err))\n\t}\n\tif err := os.WriteFile(\"x-log.json\", newJson, 0640); err != nil {\n\t\tpanic(fmt.Sprintf(\"write core x-log.json failed, err: %v\", err))\n\t}\n\tif err := os.WriteFile(workDir+\"/agent/cmd/server/docs/x-log.json\", newJson, 0640); err != nil {\n\t\tpanic(fmt.Sprintf(\"write agent x-log.json failed, err: %v\", err))\n\t}\n}\n\nfunc TestGenerateSwaggerDoc(t *testing.T) {\n\tworkDir := \"/usr/songliu/dev-v2/1Panel\"\n\tswagBin := \"/root/go/bin/swag\"\n\n\tcmd1 := exec.Command(swagBin, \"init\", \"-o\", workDir+\"/core/cmd/server/docs/docs_agent\", \"-d\", workDir+\"/agent\", \"-g\", \"../agent/cmd/server/main.go\")\n\tcmd1.Dir = workDir\n\tstd1, err := cmd1.CombinedOutput()\n\tif err != nil {\n\t\tfmt.Printf(\"generate swagger doc of agent failed, std1: %v, err: %v\", string(std1), err)\n\t\treturn\n\t}\n\tcmd2 := exec.Command(swagBin, \"init\", \"-o\", workDir+\"/core/cmd/server/docs/docs_core\", \"-d\", workDir+\"/core\", \"-g\", \"./cmd/server/main.go\")\n\tcmd2.Dir = workDir\n\tstd2, err := cmd2.CombinedOutput()\n\tif err != nil {\n\t\tfmt.Printf(\"generate swagger doc of core failed, std1: %v, err: %v\", string(std2), err)\n\t\treturn\n\t}\n\n\tagentJson := workDir + \"/core/cmd/server/docs/docs_agent/swagger.json\"\n\tagentFile, err := os.ReadFile(agentJson)\n\tif err != nil {\n\t\tfmt.Printf(\"read file docs_agent failed, err: %v\", err)\n\t\treturn\n\t}\n\tvar agentSwagger Swagger\n\tif err := json.Unmarshal(agentFile, &agentSwagger); err != nil {\n\t\tfmt.Printf(\"agent json unmarshal failed, err: %v\", err)\n\t\treturn\n\t}\n\n\tcoreJson := workDir + \"/core/cmd/server/docs/docs_core/swagger.json\"\n\tcoreFile, err := os.ReadFile(coreJson)\n\tif err != nil {\n\t\tfmt.Printf(\"read file docs_core failed, err: %v\", err)\n\t\treturn\n\t}\n\tvar coreSwagger Swagger\n\tif err := json.Unmarshal(coreFile, &coreSwagger); err != nil {\n\t\tfmt.Printf(\"core json unmarshal failed, err: %v\", err)\n\t\treturn\n\t}\n\n\tnewSwagger := Swagger{\n\t\tSwagger:     agentSwagger.Swagger,\n\t\tInfo:        agentSwagger.Info,\n\t\tHost:        agentSwagger.Host,\n\t\tBasePath:    agentSwagger.BasePath,\n\t\tPaths:       agentSwagger.Paths,\n\t\tDefinitions: agentSwagger.Definitions,\n\t}\n\n\tfor key, val := range coreSwagger.Paths {\n\t\tif _, ok := newSwagger.Paths[key]; !ok {\n\t\t\tnewSwagger.Paths[key] = val\n\t\t}\n\t}\n\tfor key, val := range coreSwagger.Definitions {\n\t\tif _, ok := newSwagger.Definitions[key]; !ok {\n\t\t\tnewSwagger.Definitions[key] = val\n\t\t}\n\t}\n\n\tnewJson, err := json.MarshalIndent(newSwagger, \"\", \"\\t\")\n\tif err != nil {\n\t\tfmt.Printf(\"json marshal for new file failed, err: %v\", err)\n\t\treturn\n\t}\n\tif err := os.WriteFile(\"swagger.json\", newJson, 0640); err != nil {\n\t\tfmt.Printf(\"write new swagger.json failed, err: %v\", err)\n\t\treturn\n\t}\n\tdocTemplate := strings.ReplaceAll(loadDefaultDocs(), \"const docTemplate = \\\"aa\\\"\", fmt.Sprintf(\"const docTemplate = `%s`\", string(newJson)))\n\tif err := os.WriteFile(workDir+\"/core/cmd/server/docs/docs.go\", []byte(docTemplate), 0640); err != nil {\n\t\tfmt.Printf(\"write new docs.go failed, err: %v\", err)\n\t\treturn\n\t}\n\n\t_ = os.RemoveAll(workDir + \"/core/cmd/server/docs/docs_agent\")\n\t_ = os.RemoveAll(workDir + \"/core/cmd/server/docs/docs_core\")\n}\n\ntype Swagger struct {\n\tSwagger     string                 `json:\"swagger\"`\n\tInfo        interface{}            `json:\"info\"`\n\tHost        string                 `json:\"host\"`\n\tBasePath    string                 `json:\"basePath\"`\n\tPaths       map[string]interface{} `json:\"paths\"`\n\tDefinitions map[string]interface{} `json:\"definitions\"`\n}\n\nfunc loadDefaultDocs() string {\n\treturn `package docs\n\nimport \"github.com/swaggo/swag\"\n\nconst docTemplate = \"aa\"\n\nvar SwaggerInfo = &swag.Spec{\n\tVersion:          \"2.0\",\n\tHost:             \"localhost\",\n\tBasePath:         \"/api/v2\",\n\tSchemes:          []string{},\n\tTitle:            \"1Panel\",\n\tDescription:      \"Top-Rated Web-based Linux Server Management Tool\",\n\tInfoInstanceName: \"swagger\",\n\tSwaggerTemplate:  docTemplate,\n\tLeftDelim:        \"{{\",\n\tRightDelim:       \"}}\",\n}\n\nfunc init() {\n\tswag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)\n}`\n}\n\nfunc replaceStr(val string, rep ...string) string {\n\tfor _, item := range rep {\n\t\tval = strings.ReplaceAll(val, item, \"\")\n\t}\n\tval = strings.TrimSpace(val)\n\treturn val\n}\n\ntype operationJson struct {\n\tBodyKeys        []string       `json:\"bodyKeys\"`\n\tParamKeys       []string       `json:\"paramKeys\"`\n\tBeforeFunctions []functionInfo `json:\"beforeFunctions\"`\n\tFormatZH        string         `json:\"formatZH\"`\n\tFormatEN        string         `json:\"formatEN\"`\n}\ntype functionInfo struct {\n\tInputColumn  string `json:\"input_column\"`\n\tInputValue   string `json:\"input_value\"`\n\tIsList       bool   `json:\"isList\"`\n\tDB           string `json:\"db\"`\n\tOutputColumn string `json:\"output_column\"`\n\tOutputValue  string `json:\"output_value\"`\n}\n"
  },
  {
    "path": "core/cmd/server/docs/x-log.json",
    "content": "{\n\t\"/ai/ollama/close\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"关闭 Ollama 模型连接 [name]\",\n\t\t\"formatEN\": \"close conn for Ollama model [name]\"\n\t},\n\t\"/ai/ollama/model\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 Ollama 模型 [name]\",\n\t\t\"formatEN\": \"add Ollama model [name]\"\n\t},\n\t\"/ai/ollama/model/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"ollama_models\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 Ollama 模型 [names]\",\n\t\t\"formatEN\": \"remove Ollama model [names]\"\n\t},\n\t\"/ai/ollama/model/recreate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 Ollama 模型重试 [name]\",\n\t\t\"formatEN\": \"re-add Ollama model [name]\"\n\t},\n\t\"/ai/ollama/model/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步 Ollama 模型列表\",\n\t\t\"formatEN\": \"sync Ollama model list\"\n\t},\n\t\"/apps/ignored/cancel\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"取消忽略应用升级\",\n\t\t\"formatEN\": \"Cancel ignore application upgrade\"\n\t},\n\t\"/apps/install\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"安装应用 [name]\",\n\t\t\"formatEN\": \"Install app [name]\"\n\t},\n\t\"/apps/installed/config/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"installID\",\n\t\t\t\"webUI\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"应用配置更新 [installID]\",\n\t\t\"formatEN\": \"Application config update [installID]\"\n\t},\n\t\"/apps/installed/ignore\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"忽略应用升级\",\n\t\t\"formatEN\": \"Ignore application upgrade\"\n\t},\n\t\"/apps/installed/op\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"installId\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\"output_column\": \"app_id\",\n\t\t\t\t\"output_value\": \"appId\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"appName\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"appId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"apps\",\n\t\t\t\t\"output_column\": \"key\",\n\t\t\t\t\"output_value\": \"appKey\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[operate] 应用 [appKey][appName]\",\n\t\t\"formatEN\": \"[operate] App [appKey][appName]\"\n\t},\n\t\"/apps/installed/params/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"installId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"installId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"app_installs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"应用参数修改 [name]\",\n\t\t\"formatEN\": \"Application param update [name]\"\n\t},\n\t\"/apps/installed/port/change\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"name\",\n\t\t\t\"port\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"应用端口修改 [key]-[name] =\\u003e [port]\",\n\t\t\"formatEN\": \"Application port update [key]-[name] =\\u003e [port]\"\n\t},\n\t\"/apps/installed/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步已安装应用列表\",\n\t\t\"formatEN\": \"Sync the list of installed apps\"\n\t},\n\t\"/apps/sync/local\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"应用商店同步\",\n\t\t\"formatEN\": \"App store synchronization\"\n\t},\n\t\"/apps/sync/remote\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"应用商店同步\",\n\t\t\"formatEN\": \"App store synchronization\"\n\t},\n\t\"/backups\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建备份账号 [type]\",\n\t\t\"formatEN\": \"create backup account [type]\"\n\t},\n\t\"/backups/backup\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"name\",\n\t\t\t\"detailName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"备份 [type] 数据 [name][detailName]\",\n\t\t\"formatEN\": \"backup [type] data [name][detailName]\"\n\t},\n\t\"/backups/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"backup_accounts\",\n\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\"output_value\": \"types\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除备份账号 [types]\",\n\t\t\"formatEN\": \"delete backup account [types]\"\n\t},\n\t\"/backups/record/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"backup_records\",\n\t\t\t\t\"output_column\": \"file_name\",\n\t\t\t\t\"output_value\": \"files\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除备份记录 [files]\",\n\t\t\"formatEN\": \"delete backup records [files]\"\n\t},\n\t\"/backups/record/download\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"source\",\n\t\t\t\"fileName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"下载备份记录 [source][fileName]\",\n\t\t\"formatEN\": \"download backup records [source][fileName]\"\n\t},\n\t\"/backups/recover\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"name\",\n\t\t\t\"detailName\",\n\t\t\t\"file\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"从 [file] 恢复 [type] 数据 [name][detailName]\",\n\t\t\"formatEN\": \"recover [type] data [name][detailName] from [file]\"\n\t},\n\t\"/backups/recover/byupload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"name\",\n\t\t\t\"detailName\",\n\t\t\t\"file\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"从 [file] 恢复 [type] 数据 [name][detailName]\",\n\t\t\"formatEN\": \"recover [type] data [name][detailName] from [file]\"\n\t},\n\t\"/backups/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新备份账号 [types]\",\n\t\t\"formatEN\": \"update backup account [types]\"\n\t},\n\t\"/backups/upload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"filePath\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"上传备份文件 [filePath]\",\n\t\t\"formatEN\": \"upload backup file [filePath]\"\n\t},\n\t\"/containers\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"image\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建容器 [name][image]\",\n\t\t\"formatEN\": \"create container [name][image]\"\n\t},\n\t\"/containers/clean/log\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理容器 [name] 日志\",\n\t\t\"formatEN\": \"clean container [name] logs\"\n\t},\n\t\"/containers/compose\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 compose [name]\",\n\t\t\"formatEN\": \"create compose [name]\"\n\t},\n\t\"/containers/compose/clean/log\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理容器编排 [name] 日志\",\n\t\t\"formatEN\": \"clean compose [name] logs\"\n\t},\n\t\"/containers/compose/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"compose [operation] [name]\",\n\t\t\"formatEN\": \"compose [operation] [name]\"\n\t},\n\t\"/containers/compose/test\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"检测 compose [name] 格式\",\n\t\t\"formatEN\": \"check compose [name]\"\n\t},\n\t\"/containers/compose/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 compose [name]\",\n\t\t\"formatEN\": \"update compose information [name]\"\n\t},\n\t\"/containers/daemonjson/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新配置 [key]\",\n\t\t\"formatEN\": \"Updated configuration [key]\"\n\t},\n\t\"/containers/daemonjson/update/byfile\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新配置文件\",\n\t\t\"formatEN\": \"Updated configuration file\"\n\t},\n\t\"/containers/docker/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"docker 服务 [operation]\",\n\t\t\"formatEN\": \"[operation] docker service\"\n\t},\n\t\"/containers/files/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"containerID\",\n\t\t\t\"paths\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除容器 [containerID] 文件 [paths]\",\n\t\t\"formatEN\": \"Delete files [paths] in container [containerID]\"\n\t},\n\t\"/containers/files/download\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"containerID\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"下载容器 [containerID] 文件 [path]\",\n\t\t\"formatEN\": \"Download file [path] from container [containerID]\"\n\t},\n\t\"/containers/files/upload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"containerID\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"容器 [containerID] 上传文件到 [path]\",\n\t\t\"formatEN\": \"Upload file to [path] in container [containerID]\"\n\t},\n\t\"/containers/image/build\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"构建镜像 [name]\",\n\t\t\"formatEN\": \"build image [name]\"\n\t},\n\t\"/containers/image/load\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"从 [path] 加载镜像\",\n\t\t\"formatEN\": \"load image from [path]\"\n\t},\n\t\"/containers/image/pull\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"repoID\",\n\t\t\t\"imageName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"镜像拉取 [reponame][imageName]\",\n\t\t\"formatEN\": \"image pull [reponame][imageName]\"\n\t},\n\t\"/containers/image/push\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"repoID\",\n\t\t\t\"tagName\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[tagName] 推送到 [reponame][name]\",\n\t\t\"formatEN\": \"push [tagName] to [reponame][name]\"\n\t},\n\t\"/containers/image/remove\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"移除镜像 [names]\",\n\t\t\"formatEN\": \"remove image [names]\"\n\t},\n\t\"/containers/image/save\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"tagName\",\n\t\t\t\"path\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"保留 [tagName] 为 [path]/[name]\",\n\t\t\"formatEN\": \"save [tagName] as [path]/[name]\"\n\t},\n\t\"/containers/image/tag\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"repoID\",\n\t\t\t\"targetName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"repoID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"reponame\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"tag 镜像 [reponame][targetName]\",\n\t\t\"formatEN\": \"tag image [reponame][targetName]\"\n\t},\n\t\"/containers/ipv6option/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 ipv6 配置\",\n\t\t\"formatEN\": \"Updated the ipv6 option\"\n\t},\n\t\"/containers/logoption/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新日志配置\",\n\t\t\"formatEN\": \"Updated the log option\"\n\t},\n\t\"/containers/network\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建容器网络 name\",\n\t\t\"formatEN\": \"create container network [name]\"\n\t},\n\t\"/containers/network/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除容器网络 [names]\",\n\t\t\"formatEN\": \"delete container network [names]\"\n\t},\n\t\"/containers/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\",\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"容器 [names] 执行 [operation]\",\n\t\t\"formatEN\": \"container [operation] [names]\"\n\t},\n\t\"/containers/prune\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"pruneType\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理容器 [pruneType]\",\n\t\t\"formatEN\": \"clean container [pruneType]\"\n\t},\n\t\"/containers/rename\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"newName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"容器重命名 [name] =\\u003e [newName]\",\n\t\t\"formatEN\": \"rename container [name] =\\u003e [newName]\"\n\t},\n\t\"/containers/repo\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建镜像仓库 [name]\",\n\t\t\"formatEN\": \"create image repo [name]\"\n\t},\n\t\"/containers/repo/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除镜像仓库 [name]\",\n\t\t\"formatEN\": \"delete image repo [name]\"\n\t},\n\t\"/containers/repo/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"image_repos\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新镜像仓库 [name]\",\n\t\t\"formatEN\": \"update image repo information [name]\"\n\t},\n\t\"/containers/template\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 compose 模版 [name]\",\n\t\t\"formatEN\": \"create compose template [name]\"\n\t},\n\t\"/containers/template/batch\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"批量导入编排模版\",\n\t\t\"formatEN\": \"batch import compose templates\"\n\t},\n\t\"/containers/template/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"compose_templates\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 compose 模版 [names]\",\n\t\t\"formatEN\": \"delete compose template [names]\"\n\t},\n\t\"/containers/template/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"compose_templates\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新 compose 模版 [name]\",\n\t\t\"formatEN\": \"update compose template information [name]\"\n\t},\n\t\"/containers/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"image\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新容器 [name][image]\",\n\t\t\"formatEN\": \"update container [name][image]\"\n\t},\n\t\"/containers/upgrade\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\",\n\t\t\t\"image\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新容器镜像 [names][image]\",\n\t\t\"formatEN\": \"upgrade container image [names][image]\"\n\t},\n\t\"/containers/volume\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建容器存储卷 [name]\",\n\t\t\"formatEN\": \"create container volume [name]\"\n\t},\n\t\"/containers/volume/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"names\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除容器存储卷 [names]\",\n\t\t\"formatEN\": \"delete container volume [names]\"\n\t},\n\t\"/core/backups\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建备份账号 [type]\",\n\t\t\"formatEN\": \"create backup account [type]\"\n\t},\n\t\"/core/backups/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除备份账号 [name]\",\n\t\t\"formatEN\": \"delete backup account [name]\"\n\t},\n\t\"/core/backups/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新备份账号 [types]\",\n\t\t\"formatEN\": \"update backup account [types]\"\n\t},\n\t\"/core/commands\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"command\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建快捷命令 [name][command]\",\n\t\t\"formatEN\": \"create quick command [name][command]\"\n\t},\n\t\"/core/commands/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"commands\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除快捷命令 [names]\",\n\t\t\"formatEN\": \"delete quick command [names]\"\n\t},\n\t\"/core/commands/export\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"导出快速命令\",\n\t\t\"formatEN\": \"export quick commands\"\n\t},\n\t\"/core/commands/import\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"导入快速命令\",\n\t\t\"formatEN\": \"import quick commands\"\n\t},\n\t\"/core/commands/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新快捷命令 [name]\",\n\t\t\"formatEN\": \"update quick command [name]\"\n\t},\n\t\"/core/groups\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建组 [name][type]\",\n\t\t\"formatEN\": \"create group [name][type]\"\n\t},\n\t\"/core/groups/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\"output_value\": \"type\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除组 [type][name]\",\n\t\t\"formatEN\": \"delete group [type][name]\"\n\t},\n\t\"/core/groups/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新组 [name][type]\",\n\t\t\"formatEN\": \"update group [name][type]\"\n\t},\n\t\"/core/hosts\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"addr\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建主机 [name][addr]\",\n\t\t\"formatEN\": \"create host [name][addr]\"\n\t},\n\t\"/core/hosts/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"hosts\",\n\t\t\t\t\"output_column\": \"addr\",\n\t\t\t\t\"output_value\": \"addrs\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除主机 [addrs]\",\n\t\t\"formatEN\": \"delete host [addrs]\"\n\t},\n\t\"/core/hosts/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"addr\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新主机信息 [name][addr]\",\n\t\t\"formatEN\": \"update host [name][addr]\"\n\t},\n\t\"/core/hosts/update/group\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"group\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"hosts\",\n\t\t\t\t\"output_column\": \"addr\",\n\t\t\t\t\"output_value\": \"addr\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"切换主机[addr]分组 =\\u003e [group]\",\n\t\t\"formatEN\": \"change host [addr] group =\\u003e [group]\"\n\t},\n\t\"/core/licenses/add\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加许可证\",\n\t\t\"formatEN\": \"import license\"\n\t},\n\t\"/core/licenses/bind\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"绑定许可证\",\n\t\t\"formatEN\": \"bind license\"\n\t},\n\t\"/core/licenses/bind/free\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改许可证免费节点绑定\",\n\t\t\"formatEN\": \"change bind of free node for license\"\n\t},\n\t\"/core/licenses/del\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除许可证\",\n\t\t\"formatEN\": \"delete license\"\n\t},\n\t\"/core/licenses/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步许可证信息\",\n\t\t\"formatEN\": \"sync license info\"\n\t},\n\t\"/core/licenses/unbind\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"解绑许可证\",\n\t\t\"formatEN\": \"unbind license\"\n\t},\n\t\"/core/licenses/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"替换许可证\",\n\t\t\"formatEN\": \"change license\"\n\t},\n\t\"/core/logs/clean\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"logType\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空 [logType] 日志信息\",\n\t\t\"formatEN\": \"Clean the [logType] log information\"\n\t},\n\t\"/core/script\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加脚本库脚本 [name]\",\n\t\t\"formatEN\": \"add script [name]\"\n\t},\n\t\"/core/script/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"script_librarys\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除脚本库脚本 [names]\",\n\t\t\"formatEN\": \"delete script [names]\"\n\t},\n\t\"/core/script/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步脚本库脚本\",\n\t\t\"formatEN\": \"sync scripts\"\n\t},\n\t\"/core/script/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新脚本库脚本 [name]\",\n\t\t\"formatEN\": \"update script [name]\"\n\t},\n\t\"/core/settings/api/config/generate/key\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"生成 API 接口密钥\",\n\t\t\"formatEN\": \"generate api key\"\n\t},\n\t\"/core/settings/api/config/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ipWhiteList\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 API 接口配置 =\\u003e IP 白名单: [ipWhiteList]\",\n\t\t\"formatEN\": \"update api config =\\u003e IP White List: [ipWhiteList]\"\n\t},\n\t\"/core/settings/bind/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ipv6\",\n\t\t\t\"bindAddress\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统监听信息 =\\u003e ipv6: [ipv6], 监听 IP: [bindAddress]\",\n\t\t\"formatEN\": \"update system bind info =\\u003e ipv6: [ipv6], 监听 IP: [bindAddress]\"\n\t},\n\t\"/core/settings/expired/handle\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"重置过期密码\",\n\t\t\"formatEN\": \"reset an expired Password\"\n\t},\n\t\"/core/settings/memo\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新仪表盘备忘录\",\n\t\t\"formatEN\": \"update dashboard memo\"\n\t},\n\t\"/core/settings/menu/default\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"初始化菜单\",\n\t\t\"formatEN\": \"Init menu.\"\n\t},\n\t\"/core/settings/menu/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"隐藏高级功能菜单\",\n\t\t\"formatEN\": \"Hide advanced feature menu.\"\n\t},\n\t\"/core/settings/mfa/bind\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"mfa 绑定\",\n\t\t\"formatEN\": \"bind mfa\"\n\t},\n\t\"/core/settings/password/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统密码\",\n\t\t\"formatEN\": \"update system password\"\n\t},\n\t\"/core/settings/port/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"serverPort\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统端口 =\\u003e [serverPort]\",\n\t\t\"formatEN\": \"update system port =\\u003e [serverPort]\"\n\t},\n\t\"/core/settings/proxy/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"proxyUrl\",\n\t\t\t\"proxyPort\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"服务器代理配置 [proxyPort]:[proxyPort]\",\n\t\t\"formatEN\": \"set proxy [proxyPort]:[proxyPort].\"\n\t},\n\t\"/core/settings/ssl/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ssl\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统 ssl =\\u003e [ssl]\",\n\t\t\"formatEN\": \"update system ssl =\\u003e [ssl]\"\n\t},\n\t\"/core/settings/terminal/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统终端配置\",\n\t\t\"formatEN\": \"update system terminal setting\"\n\t},\n\t\"/core/settings/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统配置 [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update system setting [key] =\\u003e [value]\"\n\t},\n\t\"/core/settings/upgrade\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"version\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新系统 =\\u003e [version]\",\n\t\t\"formatEN\": \"upgrade system =\\u003e [version]\"\n\t},\n\t\"/core/xpack/nodes\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加节点 [name]\",\n\t\t\"formatEN\": \"add node [name]\"\n\t},\n\t\"/core/xpack/nodes/backup\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"备份主节点数据\",\n\t\t\"formatEN\": \"backup master data\"\n\t},\n\t\"/core/xpack/nodes/backup/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改主节点备份设置\",\n\t\t\"formatEN\": \"update master backup setting\"\n\t},\n\t\"/core/xpack/nodes/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除节点 [names]\",\n\t\t\"formatEN\": \"delete node [names]\"\n\t},\n\t\"/core/xpack/nodes/fix\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修复节点 [name]\",\n\t\t\"formatEN\": \"fix node [name]\"\n\t},\n\t\"/core/xpack/nodes/rollback\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"nodeID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"nodeID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"回滚节点 [name]\",\n\t\t\"formatEN\": \"rollback node [name]\"\n\t},\n\t\"/core/xpack/nodes/simple\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加节点 [name]\",\n\t\t\"formatEN\": \"add node [name]\"\n\t},\n\t\"/core/xpack/nodes/simple/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"simple_nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除节点 [names]\",\n\t\t\"formatEN\": \"delete node [names]\"\n\t},\n\t\"/core/xpack/nodes/simple/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [name]\",\n\t\t\"formatEN\": \"update node [name]\"\n\t},\n\t\"/core/xpack/nodes/simple/update/base\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [name] 基础信息\",\n\t\t\"formatEN\": \"update base info for node [name]\"\n\t},\n\t\"/core/xpack/nodes/sync\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"同步节点数据 [name]\",\n\t\t\"formatEN\": \"sync node [name]\"\n\t},\n\t\"/core/xpack/nodes/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [name]\",\n\t\t\"formatEN\": \"update node [name]\"\n\t},\n\t\"/core/xpack/nodes/update/base\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [name] 基础信息\",\n\t\t\"formatEN\": \"update base info for node [name]\"\n\t},\n\t\"/core/xpack/nodes/upgrade\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"升级节点 [name]\",\n\t\t\"formatEN\": \"upgrade node [name]\"\n\t},\n\t\"/core/xpack/nodes/upgrade/log/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"nodeID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"nodeID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"nodes\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除节点更新日志 [name]\",\n\t\t\"formatEN\": \"delete node upgrade log [name]\"\n\t},\n\t\"/cronjobs\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建计划任务 [type][name]\",\n\t\t\"formatEN\": \"create cronjob [type][name]\"\n\t},\n\t\"/cronjobs/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除计划任务 [names]\",\n\t\t\"formatEN\": \"delete cronjob [names]\"\n\t},\n\t\"/cronjobs/group/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新计划任务分组 [name]\",\n\t\t\"formatEN\": \"update cronjob group [name]\"\n\t},\n\t\"/cronjobs/handle\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"手动执行计划任务 [name]\",\n\t\t\"formatEN\": \"manually execute the cronjob [name]\"\n\t},\n\t\"/cronjobs/records/clean\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"cronjobID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"cronjobID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"清空计划任务记录 [name]\",\n\t\t\"formatEN\": \"clean cronjob [name] records\"\n\t},\n\t\"/cronjobs/status\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"status\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改计划任务 [name] 状态为 [status]\",\n\t\t\"formatEN\": \"change the status of cronjob [name] to [status].\"\n\t},\n\t\"/cronjobs/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"cronjobs\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新计划任务 [name]\",\n\t\t\"formatEN\": \"update cronjob [name]\"\n\t},\n\t\"/dashboard/app/launcher/show\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"首页应用 [key] =\\u003e 显示：[value]\",\n\t\t\"formatEN\": \"app launcher [key] =\\u003e show: [value]\"\n\t},\n\t\"/dashboard/quick/change\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"切换快速跳转\",\n\t\t\"formatEN\": \"change quick jump\"\n\t},\n\t\"/databases\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 mysql 数据库 [name]\",\n\t\t\"formatEN\": \"create mysql database [name]\"\n\t},\n\t\"/databases/bind\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"database\",\n\t\t\t\"username\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"绑定 mysql 数据库名 [database] [username]\",\n\t\t\"formatEN\": \"bind mysql database [database] [username]\"\n\t},\n\t\"/databases/change/access\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新数据库 [name] 访问权限\",\n\t\t\"formatEN\": \"Update database [name] access\"\n\t},\n\t\"/databases/change/password\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新数据库 [name] 密码\",\n\t\t\"formatEN\": \"Update database [name] password\"\n\t},\n\t\"/databases/common/update/conf\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\",\n\t\t\t\"database\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [type] 数据库 [database] 配置信息\",\n\t\t\"formatEN\": \"update the [type] [database] database configuration information\"\n\t},\n\t\"/databases/db\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建远程数据库 [name][type]\",\n\t\t\"formatEN\": \"create database [name][type]\"\n\t},\n\t\"/databases/db/check\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"检测远程数据库 [name][type] 连接性\",\n\t\t\"formatEN\": \"check if database [name][type] is connectable\"\n\t},\n\t\"/databases/db/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"databases\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除远程数据库 [names]\",\n\t\t\"formatEN\": \"delete database [names]\"\n\t},\n\t\"/databases/db/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新远程数据库 [name]\",\n\t\t\"formatEN\": \"update database [name]\"\n\t},\n\t\"/databases/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 mysql 数据库 [name]\",\n\t\t\"formatEN\": \"delete mysql database [name]\"\n\t},\n\t\"/databases/description/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"description\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_mysqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"mysql 数据库 [name] 描述信息修改 [description]\",\n\t\t\"formatEN\": \"The description of the mysql database [name] is modified =\\u003e [description]\"\n\t},\n\t\"/databases/pg\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 postgresql 数据库 [name]\",\n\t\t\"formatEN\": \"create postgresql database [name]\"\n\t},\n\t\"/databases/pg/bind\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"username\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"绑定 postgresql 数据库 [name] 用户 [username]\",\n\t\t\"formatEN\": \"bind postgresql database [name] user [username]\"\n\t},\n\t\"/databases/pg/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 postgresql 数据库 [name]\",\n\t\t\"formatEN\": \"delete postgresql database [name]\"\n\t},\n\t\"/databases/pg/description\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"description\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"postgresql 数据库 [name] 描述信息修改 [description]\",\n\t\t\"formatEN\": \"The description of the postgresql database [name] is modified =\\u003e [description]\"\n\t},\n\t\"/databases/pg/password\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"database_postgresqls\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新数据库 [name] 密码\",\n\t\t\"formatEN\": \"Update database [name] password\"\n\t},\n\t\"/databases/pg/privileges\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"database\",\n\t\t\t\"username\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新数据库 [database] 用户 [username] 权限\",\n\t\t\"formatEN\": \"Update [user] privileges of database [database]\"\n\t},\n\t\"/databases/redis/conf/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 redis 数据库配置信息\",\n\t\t\"formatEN\": \"update the redis database configuration information\"\n\t},\n\t\"/databases/redis/password\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 redis 数据库密码\",\n\t\t\"formatEN\": \"change the password of the redis database\"\n\t},\n\t\"/databases/redis/persistence/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"redis 数据库持久化配置更新\",\n\t\t\"formatEN\": \"redis database persistence configuration update\"\n\t},\n\t\"/databases/variables/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"调整 mysql 数据库性能参数\",\n\t\t\"formatEN\": \"adjust mysql database performance parameters\"\n\t},\n\t\"/files\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建文件/文件夹 [path]\",\n\t\t\"formatEN\": \"Create dir or file [path]\"\n\t},\n\t\"/files/batch/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"paths\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"批量删除文件/文件夹 [paths]\",\n\t\t\"formatEN\": \"Batch delete dir or file [paths]\"\n\t},\n\t\"/files/batch/role\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"paths\",\n\t\t\t\"mode\",\n\t\t\t\"user\",\n\t\t\t\"group\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"批量修改文件权限和用户/组 [paths] =\\u003e [mode]/[user]/[group]\",\n\t\t\"formatEN\": \"Batch change file mode and owner [paths] =\\u003e [mode]/[user]/[group]\"\n\t},\n\t\"/files/chunkdownload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"下载文件 [name]\",\n\t\t\"formatEN\": \"Download file [name]\"\n\t},\n\t\"/files/compress\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"压缩文件 [name]\",\n\t\t\"formatEN\": \"Compress file [name]\"\n\t},\n\t\"/files/content\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"获取文件内容 [path]\",\n\t\t\"formatEN\": \"Load file content [path]\"\n\t},\n\t\"/files/decompress\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"解压 [path]\",\n\t\t\"formatEN\": \"Decompress file [path]\"\n\t},\n\t\"/files/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除文件/文件夹 [path]\",\n\t\t\"formatEN\": \"Delete dir or file [path]\"\n\t},\n\t\"/files/depth/size\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"获取目录及其第一层子目录文件夹大小 [path]\",\n\t\t\"formatEN\": \"Multi file size [path]\"\n\t},\n\t\"/files/favorite\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"收藏文件/文件夹 [path]\",\n\t\t\"formatEN\": \"收藏文件/文件夹 [path]\"\n\t},\n\t\"/files/favorite/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"favorites\",\n\t\t\t\t\"output_column\": \"path\",\n\t\t\t\t\"output_value\": \"path\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除收藏 [path]\",\n\t\t\"formatEN\": \"delete avorite [path]\"\n\t},\n\t\"/files/mode\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\",\n\t\t\t\"mode\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改权限 [paths] =\\u003e [mode]\",\n\t\t\"formatEN\": \"Change mode [paths] =\\u003e [mode]\"\n\t},\n\t\"/files/move\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"oldPaths\",\n\t\t\t\"newPath\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"移动文件 [oldPaths] =\\u003e [newPath]\",\n\t\t\"formatEN\": \"Move [oldPaths] =\\u003e [newPath]\"\n\t},\n\t\"/files/owner\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\",\n\t\t\t\"user\",\n\t\t\t\"group\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改用户/组 [paths] =\\u003e [user]/[group]\",\n\t\t\"formatEN\": \"Change owner [paths] =\\u003e [user]/[group]\"\n\t},\n\t\"/files/preview\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"预览文件内容 [path]\",\n\t\t\"formatEN\": \"Preview file content [path]\"\n\t},\n\t\"/files/recycle/clear\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空回收站\",\n\t\t\"formatEN\": \"清空回收站\"\n\t},\n\t\"/files/recycle/reduce\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"还原回收站文件 [name]\",\n\t\t\"formatEN\": \"Reduce RecycleBin file [name]\"\n\t},\n\t\"/files/rename\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"oldName\",\n\t\t\t\"newName\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"重命名 [oldName] =\\u003e [newName]\",\n\t\t\"formatEN\": \"Rename [oldName] =\\u003e [newName]\"\n\t},\n\t\"/files/save\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新文件内容 [path]\",\n\t\t\"formatEN\": \"Update file content [path]\"\n\t},\n\t\"/files/size\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"获取文件夹大小 [path]\",\n\t\t\"formatEN\": \"Load file size [path]\"\n\t},\n\t\"/files/upload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"path\",\n\t\t\t\"file\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"上传文件 [path]/[file]\",\n\t\t\"formatEN\": \"Upload file [path]/[file]\"\n\t},\n\t\"/files/wget\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"url\",\n\t\t\t\"path\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"下载 url =\\u003e [path]/[name]\",\n\t\t\"formatEN\": \"Download url =\\u003e [path]/[name]\"\n\t},\n\t\"/groups\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建组 [name][type]\",\n\t\t\"formatEN\": \"create group [name][type]\"\n\t},\n\t\"/groups/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"groups\",\n\t\t\t\t\"output_column\": \"type\",\n\t\t\t\t\"output_value\": \"type\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除组 [type][name]\",\n\t\t\"formatEN\": \"delete group [type][name]\"\n\t},\n\t\"/groups/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新组 [name][type]\",\n\t\t\"formatEN\": \"update group [name][type]\"\n\t},\n\t\"/hosts/disks/mount\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"device\",\n\t\t\t\"mountPoint\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"挂载磁盘 [device] 到 [mountPoint]\",\n\t\t\"formatEN\": \"Mount disk [device] to [mountPoint]\"\n\t},\n\t\"/hosts/disks/partition\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"device\",\n\t\t\t\"filesystem\",\n\t\t\t\"mountPoint\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"对磁盘 [device] 进行分区，文件系统 [filesystem]，挂载点 [mountPoint]\",\n\t\t\"formatEN\": \"Partition disk [device] with filesystem [filesystem], mount point [mountPoint]\"\n\t},\n\t\"/hosts/disks/unmount\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"device\",\n\t\t\t\"mountPoint\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"卸载磁盘 [device] 从 [mountPoint]\",\n\t\t\"formatEN\": \"Unmount disk [device] from [mountPoint]\"\n\t},\n\t\"/hosts/firewall/filter/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] iptables filter 防火墙\",\n\t\t\"formatEN\": \"[operate] iptables filter firewall\"\n\t},\n\t\"/hosts/firewall/filter/rule/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\",\n\t\t\t\"chain\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] filter规则到 [chain]\",\n\t\t\"formatEN\": \"[operation] filter rule to [chain]\"\n\t},\n\t\"/hosts/firewall/forward\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新端口转发规则\",\n\t\t\"formatEN\": \"update port forward rules\"\n\t},\n\t\"/hosts/firewall/ip\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"strategy\",\n\t\t\t\"address\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 ip 规则 [strategy] [address]\",\n\t\t\"formatEN\": \"create address rules [strategy][address]\"\n\t},\n\t\"/hosts/firewall/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] 防火墙\",\n\t\t\"formatEN\": \"[operation] firewall\"\n\t},\n\t\"/hosts/firewall/port\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"port\",\n\t\t\t\"strategy\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加端口规则 [strategy] [port]\",\n\t\t\"formatEN\": \"create port rules [strategy][port]\"\n\t},\n\t\"/hosts/monitor/clean\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空监控数据\",\n\t\t\"formatEN\": \"clean monitor datas\"\n\t},\n\t\"/hosts/monitor/setting/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改默认监控网卡 [name]-[value]\",\n\t\t\"formatEN\": \"update default monitor [name]-[value]\"\n\t},\n\t\"/hosts/ssh/cert\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"生成 SSH 密钥 \",\n\t\t\"formatEN\": \"generate SSH secret\"\n\t},\n\t\"/hosts/ssh/cert/delete\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除 SSH 密钥 \",\n\t\t\"formatEN\": \"delete SSH secret\"\n\t},\n\t\"/hosts/ssh/cert/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步 SSH 密钥 \",\n\t\t\"formatEN\": \"sync SSH secret\"\n\t},\n\t\"/hosts/ssh/cert/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"生成 SSH 密钥 \",\n\t\t\"formatEN\": \"generate SSH secret\"\n\t},\n\t\"/hosts/ssh/file/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 SSH 配置文件 [key]\",\n\t\t\"formatEN\": \"update SSH conf [key]\"\n\t},\n\t\"/hosts/ssh/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] SSH \",\n\t\t\"formatEN\": \"[operation] SSH\"\n\t},\n\t\"/hosts/ssh/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"newValue\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 SSH 配置 [key] =\\u003e [newValue]\",\n\t\t\"formatEN\": \"update SSH setting [key] =\\u003e [newValue]\"\n\t},\n\t\"/hosts/tool/config\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] 主机工具配置文件 \",\n\t\t\"formatEN\": \"[operate] tool config\"\n\t},\n\t\"/hosts/tool/init\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 [type] 配置\",\n\t\t\"formatEN\": \"create [type] config\"\n\t},\n\t\"/hosts/tool/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] [type] \",\n\t\t\"formatEN\": \"[operate] [type]\"\n\t},\n\t\"/hosts/tool/supervisor/process\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] 守护进程 \",\n\t\t\"formatEN\": \"[operate] process\"\n\t},\n\t\"/hosts/tool/supervisor/process/file\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] Supervisor 进程文件 \",\n\t\t\"formatEN\": \"[operate] Supervisor Process Config file\"\n\t},\n\t\"/openresty/build\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"构建 OpenResty\",\n\t\t\"formatEN\": \"Build OpenResty\"\n\t},\n\t\"/openresty/file\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 nginx 配置\",\n\t\t\"formatEN\": \"Update nginx conf\"\n\t},\n\t\"/openresty/modules/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 OpenResty 模块\",\n\t\t\"formatEN\": \"Update OpenResty module\"\n\t},\n\t\"/openresty/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新 nginx 配置 [domain]\",\n\t\t\"formatEN\": \"Update nginx conf [domain]\"\n\t},\n\t\"/process/stop\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"PID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"结束进程 [PID]\",\n\t\t\"formatEN\": \"结束进程 [PID]\"\n\t},\n\t\"/runtimes\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建运行环境 [name]\",\n\t\t\"formatEN\": \"Create runtime [name]\"\n\t},\n\t\"/runtimes/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除运行环境 [name]\",\n\t\t\"formatEN\": \"Delete runtime [name]\"\n\t},\n\t\"/runtimes/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"操作运行环境 [name]\",\n\t\t\"formatEN\": \"Operate runtime [name]\"\n\t},\n\t\"/runtimes/php/config\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[domain] PHP 配置修改\",\n\t\t\"formatEN\": \"[domain] PHP conf update\"\n\t},\n\t\"/runtimes/php/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"php 配置修改 [domain]\",\n\t\t\"formatEN\": \"Nginx conf update [domain]\"\n\t},\n\t\"/runtimes/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新运行环境 [name]\",\n\t\t\"formatEN\": \"Update runtime [name]\"\n\t},\n\t\"/settings/snapshot\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"from\",\n\t\t\t\"description\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建系统快照 [description] 到 [from]\",\n\t\t\"formatEN\": \"Create system backup [description] to [from]\"\n\t},\n\t\"/settings/snapshot/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除系统快照 [name]\",\n\t\t\"formatEN\": \"Delete system backup [name]\"\n\t},\n\t\"/settings/snapshot/description/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"description\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"快照 [name] 描述信息修改 [description]\",\n\t\t\"formatEN\": \"The description of the snapshot [name] is modified =\\u003e [description]\"\n\t},\n\t\"/settings/snapshot/import\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"from\",\n\t\t\t\"names\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"从 [from] 同步系统快照 [names]\",\n\t\t\"formatEN\": \"Sync system snapshots [names] from [from]\"\n\t},\n\t\"/settings/snapshot/recover\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"从系统快照 [name] 恢复\",\n\t\t\"formatEN\": \"Recover from system backup [name]\"\n\t},\n\t\"/settings/snapshot/recreate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"重试创建快照 [name]\",\n\t\t\"formatEN\": \"recrete the snapshot [name]\"\n\t},\n\t\"/settings/snapshot/rollback\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"snapshots\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"从系统快照 [name] 回滚\",\n\t\t\"formatEN\": \"Rollback from system backup [name]\"\n\t},\n\t\"/settings/ssh/conn/default\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"defaultConn\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"本地终端默认连接 [defaultConn]\",\n\t\t\"formatEN\": \"update system default conn [defaultConn]\"\n\t},\n\t\"/settings/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改系统配置 [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update system setting [key] =\\u003e [value]\"\n\t},\n\t\"/toolbox/clam\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建扫描规则 [name][path]\",\n\t\t\"formatEN\": \"create clam [name][path]\"\n\t},\n\t\"/toolbox/clam/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"names\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除扫描规则 [names]\",\n\t\t\"formatEN\": \"delete clam [names]\"\n\t},\n\t\"/toolbox/clam/handle\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"执行病毒扫描 [name]\",\n\t\t\"formatEN\": \"handle clam scan [name]\"\n\t},\n\t\"/toolbox/clam/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] Clam\",\n\t\t\"formatEN\": \"[operation] FTP\"\n\t},\n\t\"/toolbox/clam/record/clean\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"清空扫描报告 [name]\",\n\t\t\"formatEN\": \"clean clam record [name]\"\n\t},\n\t\"/toolbox/clam/status/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"status\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"clams\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改扫描规则 [name] 状态为 [status]\",\n\t\t\"formatEN\": \"change the status of clam [name] to [status].\"\n\t},\n\t\"/toolbox/clam/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改扫描规则 [name][path]\",\n\t\t\"formatEN\": \"update clam [name][path]\"\n\t},\n\t\"/toolbox/clean\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理系统垃圾文件\",\n\t\t\"formatEN\": \"Clean system junk files\"\n\t},\n\t\"/toolbox/device/update/conf\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改主机参数 [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update device conf [key] =\\u003e [value]\"\n\t},\n\t\"/toolbox/device/update/host\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改主机 Host [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update device host [key] =\\u003e [value]\"\n\t},\n\t\"/toolbox/device/update/swap\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operate\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operate] 主机 swap [path]\",\n\t\t\"formatEN\": \"[operate] device swap [path]\"\n\t},\n\t\"/toolbox/fail2ban/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] Fail2ban\",\n\t\t\"formatEN\": \"[operation] Fail2ban\"\n\t},\n\t\"/toolbox/fail2ban/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"key\",\n\t\t\t\"value\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 Fail2ban 配置 [key] =\\u003e [value]\",\n\t\t\"formatEN\": \"update fail2ban conf [key] =\\u003e [value]\"\n\t},\n\t\"/toolbox/ftp\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"user\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 FTP 账户 [user][path]\",\n\t\t\"formatEN\": \"create FTP [user][path]\"\n\t},\n\t\"/toolbox/ftp/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"ftps\",\n\t\t\t\t\"output_column\": \"user\",\n\t\t\t\t\"output_value\": \"users\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 FTP 账户 [users]\",\n\t\t\"formatEN\": \"delete FTP users [users]\"\n\t},\n\t\"/toolbox/ftp/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"operation\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"[operation] FTP\",\n\t\t\"formatEN\": \"[operation] FTP\"\n\t},\n\t\"/toolbox/ftp/sync\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"同步 FTP 账户\",\n\t\t\"formatEN\": \"sync FTP users\"\n\t},\n\t\"/toolbox/ftp/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"user\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"修改 FTP 账户 [user][path]\",\n\t\t\"formatEN\": \"update FTP [user][path]\"\n\t},\n\t\"/toolbox/scan\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"扫描系统垃圾文件\",\n\t\t\"formatEN\": \"scan System Junk Files\"\n\t},\n\t\"/websites\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"alias\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 [alias]\",\n\t\t\"formatEN\": \"Create website [alias]\"\n\t},\n\t\"/websites/:id/https\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新网站 [domain] https 配置\",\n\t\t\"formatEN\": \"Update website https [domain] conf\"\n\t},\n\t\"/websites/acme\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"email\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 acme [email]\",\n\t\t\"formatEN\": \"Create website acme [email]\"\n\t},\n\t\"/websites/acme/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_acme_accounts\",\n\t\t\t\t\"output_column\": \"email\",\n\t\t\t\t\"output_value\": \"email\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 acme [email]\",\n\t\t\"formatEN\": \"Delete website acme [email]\"\n\t},\n\t\"/websites/acme/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_acme_accounts\",\n\t\t\t\t\"output_column\": \"email\",\n\t\t\t\t\"output_value\": \"email\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新 acme [email]\",\n\t\t\"formatEN\": \"Update acme [email]\"\n\t},\n\t\"/websites/ca\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 ca [name]\",\n\t\t\"formatEN\": \"Create website ca [name]\"\n\t},\n\t\"/websites/ca/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 ca [name]\",\n\t\t\"formatEN\": \"Delete website ca [name]\"\n\t},\n\t\"/websites/ca/download\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"下载 CA 证书文件 [name]\",\n\t\t\"formatEN\": \"download ca file [name]\"\n\t},\n\t\"/websites/ca/obtain\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"自签 SSL 证书 [name]\",\n\t\t\"formatEN\": \"Obtain SSL [name]\"\n\t},\n\t\"/websites/ca/renew\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_cas\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"自签 SSL 证书 [name]\",\n\t\t\"formatEN\": \"Obtain SSL [name]\"\n\t},\n\t\"/websites/config/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"nginx 配置修改 [domain]\",\n\t\t\"formatEN\": \"Nginx conf update [domain]\"\n\t},\n\t\"/websites/default/html/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新默认 html\",\n\t\t\"formatEN\": \"Update default html\"\n\t},\n\t\"/websites/default/server\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改默认 server =\\u003e [domain]\",\n\t\t\"formatEN\": \"Change default server =\\u003e [domain]\"\n\t},\n\t\"/websites/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 [domain]\",\n\t\t\"formatEN\": \"Delete website [domain]\"\n\t},\n\t\"/websites/dir/permission\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新网站 [domain] 目录权限\",\n\t\t\"formatEN\": \"Update  domain [domain] dir permission\"\n\t},\n\t\"/websites/dir/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新网站 [domain] 目录\",\n\t\t\"formatEN\": \"Update  domain [domain] dir\"\n\t},\n\t\"/websites/dns\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 dns [name]\",\n\t\t\"formatEN\": \"Create website dns [name]\"\n\t},\n\t\"/websites/dns/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_dns_accounts\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 dns [name]\",\n\t\t\"formatEN\": \"Delete website dns [name]\"\n\t},\n\t\"/websites/dns/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站 dns [name]\",\n\t\t\"formatEN\": \"Update website dns [name]\"\n\t},\n\t\"/websites/domains\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"domain\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建域名 [domain]\",\n\t\t\"formatEN\": \"Create domain [domain]\"\n\t},\n\t\"/websites/domains/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_domains\",\n\t\t\t\t\"output_column\": \"domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除域名 [domain]\",\n\t\t\"formatEN\": \"Delete domain [domain]\"\n\t},\n\t\"/websites/domains/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_domains\",\n\t\t\t\t\"output_column\": \"domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新域名 [domain]\",\n\t\t\"formatEN\": \"Update domain [domain]\"\n\t},\n\t\"/websites/log\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[domain][operate] 日志\",\n\t\t\"formatEN\": \"[domain][operate] logs\"\n\t},\n\t\"/websites/nginx/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[domain] Nginx 配置修改\",\n\t\t\"formatEN\": \"[domain] Nginx conf update\"\n\t},\n\t\"/websites/operate\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"[operate] 网站 [domain]\",\n\t\t\"formatEN\": \"[operate] website [domain]\"\n\t},\n\t\"/websites/php/version\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteId\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteId\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"php 版本变更 [domain]\",\n\t\t\"formatEN\": \"php version update [domain]\"\n\t},\n\t\"/websites/proxies/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除网站 [domain] 反向代理配置 [name] \",\n\t\t\"formatEN\": \"Delete domain [domain] proxy config [name]\"\n\t},\n\t\"/websites/proxies/file\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\",\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改网站 [domain] 反向代理配置文件 [name] \",\n\t\t\"formatEN\": \"Update domain [domain] proxy config file [name]\"\n\t},\n\t\"/websites/proxies/status\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"name\",\n\t\t\t\"status\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改网站 [domain] 反向代理配置 [name] 状态 [status] \",\n\t\t\"formatEN\": \"Update domain [domain] proxy config [name] status [status]\"\n\t},\n\t\"/websites/proxies/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\",\n\t\t\t\"name\",\n\t\t\t\"operate\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改 [operate] 网站 [domain] 反向代理配置 [name] \",\n\t\t\"formatEN\": \"Update [operate] domain [domain] proxy config [name]\"\n\t},\n\t\"/websites/proxy/clear\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清理 Openresty 代理缓存\",\n\t\t\"formatEN\": \"Clear nginx proxy cache\"\n\t},\n\t\"/websites/realip/config\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改 [domain] 网站真实IP配置 \",\n\t\t\"formatEN\": \"Modify the real IP configuration of [domain] website\"\n\t},\n\t\"/websites/redirect/file\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新重定向文件 [domain]\",\n\t\t\"formatEN\": \"Nginx conf redirect file update [domain]\"\n\t},\n\t\"/websites/redirect/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"修改网站 [domain] 重定向配置 \",\n\t\t\"formatEN\": \"Update domain [domain] redirect config\"\n\t},\n\t\"/websites/rewrite/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"websiteID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"websiteID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"websites\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"伪静态配置修改 [domain]\",\n\t\t\"formatEN\": \"Nginx conf rewrite update [domain]\"\n\t},\n\t\"/websites/ssl\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"primaryDomain\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建网站 ssl [primaryDomain]\",\n\t\t\"formatEN\": \"Create website ssl [primaryDomain]\"\n\t},\n\t\"/websites/ssl/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ids\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ids\",\n\t\t\t\t\"isList\": true,\n\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除 ssl [domain]\",\n\t\t\"formatEN\": \"Delete ssl [domain]\"\n\t},\n\t\"/websites/ssl/download\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"下载证书文件 [domain]\",\n\t\t\"formatEN\": \"download ssl file [domain]\"\n\t},\n\t\"/websites/ssl/obtain\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ID\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"ID\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"申请证书  [domain]\",\n\t\t\"formatEN\": \"apply ssl [domain]\"\n\t},\n\t\"/websites/ssl/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"website_ssls\",\n\t\t\t\t\"output_column\": \"primary_domain\",\n\t\t\t\t\"output_value\": \"domain\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"更新证书设置 [domain]\",\n\t\t\"formatEN\": \"Update ssl config [domain]\"\n\t},\n\t\"/websites/ssl/upload\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"上传 ssl [type]\",\n\t\t\"formatEN\": \"Upload ssl [type]\"\n\t},\n\t\"/websites/ssl/upload/file\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"上传 ssl 文件 [type]\",\n\t\t\"formatEN\": \"Upload ssl file [type]\"\n\t},\n\t\"/websites/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"primaryDomain\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站 [primaryDomain]\",\n\t\t\"formatEN\": \"Update website [primaryDomain]\"\n\t},\n\t\"/xpack/monitor/config/global\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新全局设置\",\n\t\t\"formatEN\": \"update global config\"\n\t},\n\t\"/xpack/monitor/log/clear\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空日志\",\n\t\t\"formatEN\": \"clear log\"\n\t},\n\t\"/xpack/monitor/site/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站设置\",\n\t\t\"formatEN\": \"update website config\"\n\t},\n\t\"/xpack/tampers/template\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\",\n\t\t\t\"content\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"保存防篡改模版 [name] - [content]\",\n\t\t\"formatEN\": \"save tamper template info  [name] - [content]\"\n\t},\n\t\"/xpack/tampers/template/del\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"id\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [\n\t\t\t{\n\t\t\t\t\"input_column\": \"id\",\n\t\t\t\t\"input_value\": \"id\",\n\t\t\t\t\"isList\": false,\n\t\t\t\t\"db\": \"tampers\",\n\t\t\t\t\"output_column\": \"name\",\n\t\t\t\t\"output_value\": \"name\"\n\t\t\t}\n\t\t],\n\t\t\"formatZH\": \"删除防篡改模版 [name]\",\n\t\t\"formatEN\": \"delete tamper template [name]\"\n\t},\n\t\"/xpack/tampers/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"website\",\n\t\t\t\"status\",\n\t\t\t\"path\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新防篡改信息 [website][path] =\\u003e [status]\",\n\t\t\"formatEN\": \"update tamper info [website][path] =\\u003e [status]\"\n\t},\n\t\"/xpack/waf/acl/create\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建自定义规则 [name]\",\n\t\t\"formatEN\": \"create acl [name]\"\n\t},\n\t\"/xpack/waf/acl/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除自定义规则 [name]\",\n\t\t\"formatEN\": \"delete acl [name]\"\n\t},\n\t\"/xpack/waf/acl/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新自定义规则 [name]\",\n\t\t\"formatEN\": \"update acl [name]\"\n\t},\n\t\"/xpack/waf/block/remove\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ip\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"解封 IP [ip]\",\n\t\t\"formatEN\": \"unblock ip [ip]\"\n\t},\n\t\"/xpack/waf/cdn/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 CDN 配置\",\n\t\t\"formatEN\": \"update CDN config\"\n\t},\n\t\"/xpack/waf/config/global/state\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\",\n\t\t\t\"state\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [state]全局配置[scope]\",\n\t\t\"formatEN\": \"update [state] global config [scope]\"\n\t},\n\t\"/xpack/waf/config/website/state\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\",\n\t\t\t\"state\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 [state]网站配置[scope]\",\n\t\t\"formatEN\": \"update [state] website config [scope]\"\n\t},\n\t\"/xpack/waf/html/revert\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"回滚拦截页面 [name]\",\n\t\t\"formatEN\": \"revert html res [name]\"\n\t},\n\t\"/xpack/waf/html/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新拦截页面 [name]\",\n\t\t\"formatEN\": \"update html res [name]\"\n\t},\n\t\"/xpack/waf/ip/default\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"ip\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 IP [ip] 到默认[type]组\",\n\t\t\"formatEN\": \"add ip [ip] to default[type]group\"\n\t},\n\t\"/xpack/waf/ip/group/create\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 IP 组 [name]\",\n\t\t\"formatEN\": \"create ip group [name]\"\n\t},\n\t\"/xpack/waf/ip/group/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除 IP 组 [name]\",\n\t\t\"formatEN\": \"delete ip group [name]\"\n\t},\n\t\"/xpack/waf/ip/group/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"name\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 IP 组 [name]\",\n\t\t\"formatEN\": \"update ip group [name]\"\n\t},\n\t\"/xpack/waf/location/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新[type]地址库信息\",\n\t\t\"formatEN\": \"update [type] location\"\n\t},\n\t\"/xpack/waf/log/clear\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"清空 WAF 日志\",\n\t\t\"formatEN\": \"clear waf log\"\n\t},\n\t\"/xpack/waf/rule/cc\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新[scope]频率访问限制\",\n\t\t\"formatEN\": \"update [scope] CC config\"\n\t},\n\t\"/xpack/waf/rule/common/create\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建规则 [scope]\",\n\t\t\"formatEN\": \"create rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/common/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除规则 [scope]\",\n\t\t\"formatEN\": \"delete rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/common/export\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"导出规则 [scope]\",\n\t\t\"formatEN\": \"export rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/common/import\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"导入规则 [scope]\",\n\t\t\"formatEN\": \"import rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/common/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新规则 [scope]\",\n\t\t\"formatEN\": \"update rule [scope]\"\n\t},\n\t\"/xpack/waf/rule/geo\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新地区访问限制\",\n\t\t\"formatEN\": \"update geo restrict\"\n\t},\n\t\"/xpack/waf/rule/ip/create\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建[scope] IP 规则\",\n\t\t\"formatEN\": \"create [scope] IP rule\"\n\t},\n\t\"/xpack/waf/rule/ip/delete\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除[scope] IP 规则\",\n\t\t\"formatEN\": \"delete [scope] IP rule\"\n\t},\n\t\"/xpack/waf/rule/ip/update\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"scope\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新[scope] IP 规则\",\n\t\t\"formatEN\": \"update [scope] IP rule\"\n\t},\n\t\"/xpack/waf/rule/log\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新日志配置\",\n\t\t\"formatEN\": \"update waf log config\"\n\t},\n\t\"/xpack/waf/rule/urlcc/create\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"创建 URL 频率访问限制规则\",\n\t\t\"formatEN\": \"create URL CC rule\"\n\t},\n\t\"/xpack/waf/rule/urlcc/delete\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"删除 URL 频率访问限制规则\",\n\t\t\"formatEN\": \"delete URL CC rule\"\n\t},\n\t\"/xpack/waf/rule/urlcc/update\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新 URL 频率访问限制规则\",\n\t\t\"formatEN\": \"update URL CC rule\"\n\t},\n\t\"/xpack/waf/url/default\": {\n\t\t\"bodyKeys\": [\n\t\t\t\"url\",\n\t\t\t\"type\"\n\t\t],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"添加 URL [url] 到默认[type]组\",\n\t\t\"formatEN\": \"add URL [url] to default[type]group\"\n\t},\n\t\"/xpack/website/rule/cc\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站频率访问限制\",\n\t\t\"formatEN\": \"update website CC config\"\n\t},\n\t\"/xpack/website/rule/geo\": {\n\t\t\"bodyKeys\": [],\n\t\t\"paramKeys\": [],\n\t\t\"beforeFunctions\": [],\n\t\t\"formatZH\": \"更新网站地区访问限制\",\n\t\t\"formatEN\": \"update geo restrict\"\n\t}\n}"
  },
  {
    "path": "core/cmd/server/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/cmd\"\n\t_ \"github.com/1Panel-dev/1Panel/core/cmd/server/docs\"\n)\n\n// @title 1Panel\n// @version 2.0\n// @description Top-Rated Web-based Linux Server Management Tool\n// @termsOfService http://swagger.io/terms/\n// @license.name GPL-3.0\n// @license.url https://www.gnu.org/licenses/gpl-3.0.html\n// @BasePath /api/v2\n// @schemes http https\n\n// @securityDefinitions.apikey ApiKeyAuth\n// @description Custom Token Format, Format: md5('1panel' + API-Key + UnixTimestamp).\n// @description ```\n// @description eg:\n// @description curl -X GET \"http://{host}:{port}/api/v2/toolbox/device/base\" \\\n// @description -H \"1Panel-Token: <1panel_token>\" \\\n// @description -H \"1Panel-Timestamp: <current_unix_timestamp>\"\n// @description ```\n// @description - `1Panel-Token` is the key for the panel API Key.\n// @type apiKey\n// @in Header\n// @name 1Panel-Token\n// @securityDefinitions.apikey Timestamp\n// @type apiKey\n// @in header\n// @name 1Panel-Timestamp\n// @description - `1Panel-Timestamp` is the Unix timestamp of the current time in seconds.\n\nfunc main() {\n\tif err := cmd.RootCmd.Execute(); err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "core/cmd/server/res/error_msg.go",
    "content": "package res\n\nimport \"embed\"\n\n//go:embed html/*\nvar ErrorMsg embed.FS\n"
  },
  {
    "path": "core/cmd/server/res/html/200.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Access Temporarily Unavailable</title>\n    <style>\n        body {\n            font-family: Arial, sans-serif;\n            text-align: center;\n            background-color: #f9f9f9;\n            margin: 0;\n            padding: 0;\n            color: #333;\n        }\n        .container {\n            max-width: 600px;\n            margin: 50px auto;\n            padding: 20px;\n            background: #fff;\n            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);\n            border-radius: 8px;\n        }\n        .icon img {\n            width: 100px;\n            height: auto;\n        }\n        h1 {\n            font-size: 24px;\n            color: #555;\n        }\n        p {\n            font-size: 16px;\n            color: #666;\n            line-height: 1.5;\n        }\n        .command {\n            font-family: monospace;\n            background: #f0f0f0;\n            padding: 5px 10px;\n            border-radius: 4px;\n            display: inline-block;\n            margin-top: 10px;\n        }\n    </style>\n</head>\n<body>\n<div class=\"container\">\n    <h1>Access Temporarily Unavailable</h1>\n    <p>The current environment has enabled secure login access.</p>\n    <p>Run the following SSH command to view the panel login URL:</p>\n    <p class=\"command\">1pctl user-info</p>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "core/cmd/server/res/html/200_err_domain.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Access Temporarily Unavailable</title>\n    <style>\n        body {\n            font-family: Arial, sans-serif;\n            text-align: center;\n            background-color: #f9f9f9;\n            margin: 0;\n            padding: 0;\n            color: #333;\n        }\n        .container {\n            max-width: 600px;\n            margin: 50px auto;\n            padding: 20px;\n            background: #fff;\n            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);\n            border-radius: 8px;\n        }\n        .icon img {\n            width: 100px;\n            height: auto;\n        }\n        h1 {\n            font-size: 24px;\n            color: #555;\n        }\n        p {\n            font-size: 16px;\n            color: #666;\n            line-height: 1.5;\n        }\n        .command {\n            font-family: monospace;\n            background: #f0f0f0;\n            padding: 5px 10px;\n            border-radius: 4px;\n            display: inline-block;\n            margin-top: 10px;\n        }\n    </style>\n</head>\n<body>\n<div class=\"container\">\n    <h1>Access Temporarily Unavailable</h1>\n    <p>The current environment has enabled domain name binding.</p>\n    <p>Run the following SSH command to reset the binding information:</p>\n    <p class=\"command\">1pctl reset domain</p>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "core/cmd/server/res/html/200_err_ip_limit.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Access Temporarily Unavailable</title>\n    <style>\n        body {\n            font-family: Arial, sans-serif;\n            text-align: center;\n            background-color: #f9f9f9;\n            margin: 0;\n            padding: 0;\n            color: #333;\n        }\n        .container {\n            max-width: 600px;\n            margin: 50px auto;\n            padding: 20px;\n            background: #fff;\n            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);\n            border-radius: 8px;\n        }\n        .icon img {\n            width: 100px;\n            height: auto;\n        }\n        h1 {\n            font-size: 24px;\n            color: #555;\n        }\n        p {\n            font-size: 16px;\n            color: #666;\n            line-height: 1.5;\n        }\n        .command {\n            font-family: monospace;\n            background: #f0f0f0;\n            padding: 5px 10px;\n            border-radius: 4px;\n            display: inline-block;\n            margin-top: 10px;\n        }\n    </style>\n</head>\n<body>\n<div class=\"container\">\n    <h1>Access Temporarily Unavailable</h1>\n    <p>The current environment has enabled authorized IP access.</p>\n    <p>Run the following SSH command to reset the binding information:</p>\n    <p class=\"command\">1pctl reset ips</p>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "core/cmd/server/res/html/400.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>400 Bad Request</title></head>\n<body>\n<center><h1>400 Bad Request</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "core/cmd/server/res/html/401.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>401 Unauthorized</title></head>\n<body>\n<center><h1>401 Unauthorized</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "core/cmd/server/res/html/403.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>403 Forbidden</title></head>\n<body>\n<center><h1>403 Forbidden</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "core/cmd/server/res/html/404.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>404 Not Found</title></head>\n<body>\n<center><h1>404 Not Found</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "core/cmd/server/res/html/408.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>408 Request Timeout</title></head>\n<body>\n<center><h1>408 Request Timeout</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "core/cmd/server/res/html/416.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>416 Requested Not Satisfiable</title></head>\n<body>\n<center><h1>416 Requested Not Satisfiable</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "core/cmd/server/res/html/500.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>Internal Server Error</title></head>\n<body>\n<center><h1>Internal Server Error</h1></center>\n<hr><center>nginx</center>\n</body>"
  },
  {
    "path": "core/cmd/server/web/static/china.json",
    "content": "{\n    \"type\": \"FeatureCollection\",\n    \"features\": [\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 110000,\n                \"name\": \"Beijing\",\n                \"center\": [116.405285, 39.904989],\n                \"centroid\": [116.41995, 40.18994],\n                \"childrenNum\": 16,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 0,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [117.348611, 40.581141],\n                            [117.389879, 40.561593],\n                            [117.429915, 40.576141],\n                            [117.412669, 40.605226],\n                            [117.467487, 40.649738],\n                            [117.467487, 40.649738],\n                            [117.501364, 40.636569],\n                            [117.514914, 40.660181],\n                            [117.493973, 40.675161],\n                            [117.408973, 40.686961],\n                            [117.342451, 40.673799],\n                            [117.319662, 40.657911],\n                            [117.278394, 40.664267],\n                            [117.208177, 40.694675],\n                            [117.117018, 40.70012],\n                            [117.11209, 40.707379],\n                            [117.012308, 40.693767],\n                            [116.964881, 40.709647],\n                            [116.926692, 40.745022],\n                            [116.924229, 40.773581],\n                            [116.848468, 40.839264],\n                            [116.81336, 40.848319],\n                            [116.759773, 40.889954],\n                            [116.713577, 40.909858],\n                            [116.722201, 40.927495],\n                            [116.677853, 40.970888],\n                            [116.698795, 41.021477],\n                            [116.688324, 41.044501],\n                            [116.647672, 41.059394],\n                            [116.615643, 41.053076],\n                            [116.623034, 41.021026],\n                            [116.598397, 40.974503],\n                            [116.5676, 40.992574],\n                            [116.519557, 40.98128],\n                            [116.519557, 40.98128],\n                            [116.455499, 40.980828],\n                            [116.447492, 40.953715],\n                            [116.477057, 40.899907],\n                            [116.398216, 40.90624],\n                            [116.370499, 40.94377],\n                            [116.339702, 40.929303],\n                            [116.334159, 40.90443],\n                            [116.438253, 40.81934],\n                            [116.46597, 40.774487],\n                            [116.453651, 40.765876],\n                            [116.316912, 40.772221],\n                            [116.311369, 40.754996],\n                            [116.273181, 40.762703],\n                            [116.247311, 40.791707],\n                            [116.22021, 40.744115],\n                            [116.204812, 40.740035],\n                            [116.171551, 40.695582],\n                            [116.162928, 40.662451],\n                            [116.133979, 40.666536],\n                            [116.09887, 40.630665],\n                            [116.005247, 40.583868],\n                            [115.982457, 40.578868],\n                            [115.971986, 40.6025],\n                            [115.907929, 40.617493],\n                            [115.885139, 40.595229],\n                            [115.827857, 40.587504],\n                            [115.819849, 40.55932],\n                            [115.784741, 40.55841],\n                            [115.755176, 40.540221],\n                            [115.736082, 40.503372],\n                            [115.781045, 40.49336],\n                            [115.771806, 40.443734],\n                            [115.864197, 40.359422],\n                            [115.917784, 40.354405],\n                            [115.95166, 40.281852],\n                            [115.968907, 40.264045],\n                            [115.89869, 40.234354],\n                            [115.870356, 40.185909],\n                            [115.855574, 40.188652],\n                            [115.847567, 40.147036],\n                            [115.806299, 40.15344],\n                            [115.773654, 40.176307],\n                            [115.75456, 40.145663],\n                            [115.75456, 40.145663],\n                            [115.599959, 40.119583],\n                            [115.59072, 40.096239],\n                            [115.527278, 40.076092],\n                            [115.485394, 40.040364],\n                            [115.454597, 40.029825],\n                            [115.450286, 39.992697],\n                            [115.428728, 39.984443],\n                            [115.426264, 39.950502],\n                            [115.481083, 39.935819],\n                            [115.522967, 39.899099],\n                            [115.515575, 39.892212],\n                            [115.515575, 39.892212],\n                            [115.526046, 39.87568],\n                            [115.514344, 39.837549],\n                            [115.567314, 39.816407],\n                            [115.552532, 39.794799],\n                            [115.50572, 39.784222],\n                            [115.483547, 39.798477],\n                            [115.483547, 39.798477],\n                            [115.443511, 39.785601],\n                            [115.439815, 39.752022],\n                            [115.486626, 39.741899],\n                            [115.491554, 39.670074],\n                            [115.478619, 39.650723],\n                            [115.478619, 39.650723],\n                            [115.522351, 39.640124],\n                            [115.518039, 39.597252],\n                            [115.545756, 39.618922],\n                            [115.587024, 39.589873],\n                            [115.633836, 39.599557],\n                            [115.633836, 39.599557],\n                            [115.667712, 39.615234],\n                            [115.698509, 39.577881],\n                            [115.698509, 39.577881],\n                            [115.699125, 39.570039],\n                            [115.699125, 39.570039],\n                            [115.716988, 39.56035],\n                            [115.716988, 39.56035],\n                            [115.718835, 39.553891],\n                            [115.718835, 39.553891],\n                            [115.720683, 39.551122],\n                            [115.720683, 39.551122],\n                            [115.722531, 39.5442],\n                            [115.721299, 39.543738],\n                            [115.722531, 39.5442],\n                            [115.722531, 39.543738],\n                            [115.721299, 39.543738],\n                            [115.722531, 39.543738],\n                            [115.724995, 39.5442],\n                            [115.724995, 39.5442],\n                            [115.738545, 39.540046],\n                            [115.738545, 39.539585],\n                            [115.738545, 39.540046],\n                            [115.738545, 39.539585],\n                            [115.752712, 39.515581],\n                            [115.806299, 39.510041],\n                            [115.806299, 39.510041],\n                            [115.821081, 39.522968],\n                            [115.821081, 39.522968],\n                            [115.828473, 39.541431],\n                            [115.867893, 39.546507],\n                            [115.867893, 39.546507],\n                            [115.91532, 39.582955],\n                            [115.91532, 39.582955],\n                            [115.910393, 39.600479],\n                            [115.910393, 39.600479],\n                            [115.957204, 39.560812],\n                            [115.978146, 39.595868],\n                            [115.995392, 39.576958],\n                            [116.026189, 39.587567],\n                            [116.036044, 39.571884],\n                            [116.09887, 39.575113],\n                            [116.130283, 39.567732],\n                            [116.151841, 39.583416],\n                            [116.198652, 39.589412],\n                            [116.240536, 39.564041],\n                            [116.257782, 39.500344],\n                            [116.307057, 39.488337],\n                            [116.337854, 39.455536],\n                            [116.361876, 39.455074],\n                            [116.361876, 39.455074],\n                            [116.434557, 39.442597],\n                            [116.454883, 39.453226],\n                            [116.444412, 39.482332],\n                            [116.411767, 39.482794],\n                            [116.401912, 39.528046],\n                            [116.443796, 39.510041],\n                            [116.437637, 39.526661],\n                            [116.478289, 39.535431],\n                            [116.473361, 39.552968],\n                            [116.50847, 39.551122],\n                            [116.524484, 39.596329],\n                            [116.592237, 39.621227],\n                            [116.592237, 39.621227],\n                            [116.620571, 39.601863],\n                            [116.664918, 39.605552],\n                            [116.723432, 39.59264],\n                            [116.724048, 39.59264],\n                            [116.723432, 39.59264],\n                            [116.724048, 39.59264],\n                            [116.726512, 39.595407],\n                            [116.726512, 39.595407],\n                            [116.709266, 39.618],\n                            [116.748686, 39.619844],\n                            [116.79057, 39.595868],\n                            [116.812128, 39.615695],\n                            [116.8497, 39.66777],\n                            [116.906366, 39.677444],\n                            [116.90575, 39.688037],\n                            [116.889736, 39.687576],\n                            [116.887272, 39.72533],\n                            [116.916837, 39.731314],\n                            [116.902055, 39.763523],\n                            [116.949482, 39.778703],\n                            [116.918069, 39.84628],\n                            [116.907598, 39.832494],\n                            [116.865714, 39.843982],\n                            [116.812128, 39.889916],\n                            [116.78441, 39.891294],\n                            [116.782563, 39.947749],\n                            [116.757925, 39.967934],\n                            [116.781331, 40.034866],\n                            [116.820135, 40.02845],\n                            [116.831222, 40.051359],\n                            [116.867562, 40.041739],\n                            [116.927924, 40.055024],\n                            [116.945171, 40.04128],\n                            [117.025243, 40.030283],\n                            [117.051728, 40.059605],\n                            [117.105315, 40.074261],\n                            [117.105315, 40.074261],\n                            [117.140423, 40.064185],\n                            [117.159517, 40.077008],\n                            [117.204481, 40.069681],\n                            [117.210024, 40.082045],\n                            [117.224191, 40.094865],\n                            [117.224191, 40.094865],\n                            [117.254988, 40.114548],\n                            [117.254988, 40.114548],\n                            [117.254988, 40.114548],\n                            [117.274082, 40.105852],\n                            [117.307343, 40.136971],\n                            [117.349227, 40.136513],\n                            [117.367089, 40.172649],\n                            [117.367089, 40.173106],\n                            [117.367089, 40.173106],\n                            [117.367089, 40.172649],\n                            [117.383719, 40.188195],\n                            [117.389879, 40.227958],\n                            [117.351075, 40.229786],\n                            [117.331365, 40.289613],\n                            [117.295024, 40.2782],\n                            [117.271618, 40.325211],\n                            [117.271618, 40.325211],\n                            [117.243285, 40.369453],\n                            [117.226039, 40.368997],\n                            [117.234046, 40.417312],\n                            [117.263611, 40.442367],\n                            [117.208793, 40.501552],\n                            [117.262995, 40.512927],\n                            [117.247597, 40.539766],\n                            [117.269771, 40.560684],\n                            [117.348611, 40.581141],\n                            [117.348611, 40.581141]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 120000,\n                \"name\": \"Tianjin\",\n                \"center\": [117.190182, 39.125596],\n                \"centroid\": [117.347043, 39.288036],\n                \"childrenNum\": 16,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 1,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [117.765602, 39.400527],\n                            [117.846906, 39.407926],\n                            [117.871543, 39.411625],\n                            [117.870311, 39.455074],\n                            [117.899877, 39.474479],\n                            [117.912195, 39.517428],\n                            [117.912195, 39.517428],\n                            [117.904804, 39.533585],\n                            [117.933753, 39.574191],\n                            [117.868464, 39.59679],\n                            [117.829659, 39.589873],\n                            [117.766834, 39.598635],\n                            [117.753899, 39.579726],\n                            [117.753899, 39.579726],\n                            [117.745276, 39.547892],\n                            [117.715711, 39.529892],\n                            [117.707088, 39.576036],\n                            [117.684914, 39.58895],\n                            [117.654117, 39.575113],\n                            [117.637486, 39.603246],\n                            [117.66274, 39.636437],\n                            [117.668899, 39.666849],\n                            [117.627015, 39.703693],\n                            [117.57774, 39.726711],\n                            [117.595603, 39.74604],\n                            [117.56111, 39.754782],\n                            [117.546327, 39.775943],\n                            [117.561726, 39.799856],\n                            [117.529081, 39.859144],\n                            [117.529081, 39.859144],\n                            [117.508139, 39.901854],\n                            [117.508139, 39.901854],\n                            [117.512451, 39.90874],\n                            [117.512451, 39.90874],\n                            [117.513067, 39.910576],\n                            [117.513067, 39.910576],\n                            [117.514914, 39.946832],\n                            [117.534625, 39.954631],\n                            [117.546327, 39.999116],\n                            [117.594987, 39.994531],\n                            [117.594987, 39.994531],\n                            [117.614697, 39.97252],\n                            [117.671363, 39.973896],\n                            [117.691073, 39.984902],\n                            [117.756363, 39.965181],\n                            [117.781616, 39.966558],\n                            [117.781616, 39.966558],\n                            [117.795167, 39.996823],\n                            [117.795167, 39.996823],\n                            [117.793319, 40.005534],\n                            [117.793319, 40.005534],\n                            [117.768681, 40.022034],\n                            [117.768681, 40.022034],\n                            [117.744044, 40.018368],\n                            [117.74774, 40.047236],\n                            [117.776073, 40.059605],\n                            [117.752667, 40.081588],\n                            [117.71879, 40.082045],\n                            [117.71879, 40.082045],\n                            [117.675059, 40.082045],\n                            [117.655965, 40.109514],\n                            [117.655965, 40.109514],\n                            [117.654117, 40.114548],\n                            [117.654117, 40.114548],\n                            [117.651653, 40.122786],\n                            [117.651653, 40.122786],\n                            [117.613465, 40.158014],\n                            [117.613465, 40.158014],\n                            [117.609769, 40.160301],\n                            [117.609769, 40.160301],\n                            [117.576508, 40.178593],\n                            [117.571581, 40.219276],\n                            [117.548791, 40.232527],\n                            [117.505059, 40.227044],\n                            [117.450241, 40.252627],\n                            [117.415748, 40.248973],\n                            [117.389879, 40.227958],\n                            [117.383719, 40.188195],\n                            [117.367089, 40.172649],\n                            [117.367089, 40.173106],\n                            [117.367089, 40.173106],\n                            [117.367089, 40.172649],\n                            [117.349227, 40.136513],\n                            [117.307343, 40.136971],\n                            [117.274082, 40.105852],\n                            [117.254988, 40.114548],\n                            [117.254988, 40.114548],\n                            [117.254988, 40.114548],\n                            [117.224191, 40.094865],\n                            [117.224191, 40.094865],\n                            [117.210024, 40.082045],\n                            [117.192162, 40.066475],\n                            [117.198322, 39.992697],\n                            [117.150894, 39.944996],\n                            [117.162597, 39.876598],\n                            [117.162597, 39.876598],\n                            [117.227887, 39.852712],\n                            [117.247597, 39.860981],\n                            [117.251908, 39.834332],\n                            [117.192162, 39.832953],\n                            [117.156438, 39.817326],\n                            [117.15767, 39.796638],\n                            [117.205713, 39.763984],\n                            [117.161981, 39.748801],\n                            [117.165061, 39.718886],\n                            [117.165061, 39.718886],\n                            [117.177996, 39.645194],\n                            [117.152742, 39.623532],\n                            [117.10901, 39.625375],\n                            [117.10901, 39.625375],\n                            [117.016004, 39.653949],\n                            [116.983359, 39.638742],\n                            [116.983359, 39.638742],\n                            [116.964265, 39.64335],\n                            [116.948866, 39.680668],\n                            [116.948866, 39.680668],\n                            [116.944555, 39.695405],\n                            [116.944555, 39.695405],\n                            [116.932236, 39.706456],\n                            [116.932236, 39.706456],\n                            [116.90575, 39.688037],\n                            [116.906366, 39.677444],\n                            [116.8497, 39.66777],\n                            [116.812128, 39.615695],\n                            [116.808432, 39.576497],\n                            [116.78749, 39.554352],\n                            [116.819519, 39.528507],\n                            [116.820751, 39.482332],\n                            [116.785026, 39.465702],\n                            [116.832454, 39.435664],\n                            [116.876185, 39.43474],\n                            [116.839845, 39.413474],\n                            [116.840461, 39.378326],\n                            [116.818287, 39.3737],\n                            [116.829374, 39.338994],\n                            [116.870642, 39.357506],\n                            [116.889736, 39.338068],\n                            [116.87249, 39.291304],\n                            [116.881729, 39.225966],\n                            [116.881729, 39.225966],\n                            [116.855859, 39.215766],\n                            [116.870026, 39.153607],\n                            [116.909446, 39.150822],\n                            [116.912526, 39.110898],\n                            [116.91191, 39.111362],\n                            [116.91191, 39.111362],\n                            [116.912526, 39.110898],\n                            [116.871874, 39.054688],\n                            [116.812744, 39.05097],\n                            [116.812744, 39.05097],\n                            [116.783179, 39.05097],\n                            [116.783179, 39.05097],\n                            [116.754229, 39.034701],\n                            [116.754229, 39.034701],\n                            [116.754845, 39.003084],\n                            [116.72836, 38.975174],\n                            [116.708034, 38.931892],\n                            [116.722201, 38.896968],\n                            [116.723432, 38.852706],\n                            [116.75115, 38.831264],\n                            [116.737599, 38.784629],\n                            [116.746222, 38.754299],\n                            [116.794265, 38.744498],\n                            [116.794265, 38.744498],\n                            [116.858939, 38.741231],\n                            [116.877417, 38.680522],\n                            [116.948866, 38.689398],\n                            [116.950714, 38.689398],\n                            [116.95133, 38.689398],\n                            [116.950714, 38.689398],\n                            [116.948866, 38.689398],\n                            [116.95133, 38.689398],\n                            [117.038793, 38.688464],\n                            [117.068358, 38.680522],\n                            [117.055424, 38.639398],\n                            [117.070822, 38.608072],\n                            [117.109626, 38.584685],\n                            [117.150894, 38.617892],\n                            [117.183539, 38.61836],\n                            [117.183539, 38.61836],\n                            [117.213104, 38.639866],\n                            [117.213104, 38.639866],\n                            [117.258684, 38.608072],\n                            [117.258684, 38.608072],\n                            [117.238358, 38.580943],\n                            [117.25314, 38.556143],\n                            [117.368937, 38.564566],\n                            [117.432379, 38.601524],\n                            [117.47919, 38.616489],\n                            [117.55803, 38.613683],\n                            [117.639334, 38.626776],\n                            [117.65658, 38.66043],\n                            [117.729261, 38.680055],\n                            [117.740964, 38.700141],\n                            [117.740964, 38.753833],\n                            [117.671363, 38.772032],\n                            [117.646725, 38.788827],\n                            [117.64611, 38.828933],\n                            [117.752051, 38.847579],\n                            [117.778536, 38.869016],\n                            [117.847522, 38.855502],\n                            [117.875855, 38.920252],\n                            [117.898029, 38.948649],\n                            [117.855529, 38.957492],\n                            [117.837667, 39.057011],\n                            [117.871543, 39.122506],\n                            [117.96455, 39.172631],\n                            [117.977485, 39.206028],\n                            [118.032919, 39.219939],\n                            [118.034767, 39.218548],\n                            [118.064948, 39.231065],\n                            [118.064948, 39.256094],\n                            [118.036615, 39.264898],\n                            [118.024296, 39.289451],\n                            [118.024296, 39.289451],\n                            [117.982412, 39.298714],\n                            [117.982412, 39.298714],\n                            [117.979333, 39.300566],\n                            [117.979333, 39.300566],\n                            [117.973173, 39.312143],\n                            [117.973173, 39.312143],\n                            [117.965782, 39.314921],\n                            [117.965782, 39.314921],\n                            [117.919587, 39.318162],\n                            [117.919587, 39.318162],\n                            [117.88879, 39.332051],\n                            [117.854913, 39.328348],\n                            [117.854297, 39.328348],\n                            [117.854913, 39.328348],\n                            [117.854297, 39.328348],\n                            [117.850601, 39.363984],\n                            [117.850601, 39.363984],\n                            [117.810565, 39.354729],\n                            [117.805022, 39.373237],\n                            [117.784696, 39.376938],\n                            [117.74466, 39.354729],\n                            [117.670747, 39.357969],\n                            [117.669515, 39.322792],\n                            [117.594987, 39.349176],\n                            [117.536472, 39.338068],\n                            [117.521074, 39.357043],\n                            [117.570965, 39.404689],\n                            [117.601146, 39.419485],\n                            [117.614081, 39.407001],\n                            [117.668899, 39.412087],\n                            [117.673211, 39.386652],\n                            [117.699696, 39.407463],\n                            [117.765602, 39.400527]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.805022, 39.373237],\n                            [117.852449, 39.380639],\n                            [117.846906, 39.407926],\n                            [117.765602, 39.400527],\n                            [117.784696, 39.376938],\n                            [117.805022, 39.373237]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 130000,\n                \"name\": \"Hebei\",\n                \"center\": [114.502461, 38.045474],\n                \"childrenNum\": 11,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 2,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [117.467487, 40.649738],\n                            [117.412669, 40.605226],\n                            [117.429915, 40.576141],\n                            [117.389879, 40.561593],\n                            [117.348611, 40.581141],\n                            [117.348611, 40.581141],\n                            [117.269771, 40.560684],\n                            [117.247597, 40.539766],\n                            [117.262995, 40.512927],\n                            [117.208793, 40.501552],\n                            [117.263611, 40.442367],\n                            [117.234046, 40.417312],\n                            [117.226039, 40.368997],\n                            [117.243285, 40.369453],\n                            [117.271618, 40.325211],\n                            [117.271618, 40.325211],\n                            [117.295024, 40.2782],\n                            [117.331365, 40.289613],\n                            [117.351075, 40.229786],\n                            [117.389879, 40.227958],\n                            [117.415748, 40.248973],\n                            [117.450241, 40.252627],\n                            [117.505059, 40.227044],\n                            [117.548791, 40.232527],\n                            [117.571581, 40.219276],\n                            [117.576508, 40.178593],\n                            [117.609769, 40.160301],\n                            [117.609769, 40.160301],\n                            [117.613465, 40.158014],\n                            [117.613465, 40.158014],\n                            [117.651653, 40.122786],\n                            [117.651653, 40.122786],\n                            [117.654117, 40.114548],\n                            [117.654117, 40.114548],\n                            [117.655965, 40.109514],\n                            [117.655965, 40.109514],\n                            [117.675059, 40.082045],\n                            [117.71879, 40.082045],\n                            [117.71879, 40.082045],\n                            [117.752667, 40.081588],\n                            [117.776073, 40.059605],\n                            [117.74774, 40.047236],\n                            [117.744044, 40.018368],\n                            [117.768681, 40.022034],\n                            [117.768681, 40.022034],\n                            [117.793319, 40.005534],\n                            [117.793319, 40.005534],\n                            [117.795167, 39.996823],\n                            [117.795167, 39.996823],\n                            [117.781616, 39.966558],\n                            [117.781616, 39.966558],\n                            [117.756363, 39.965181],\n                            [117.691073, 39.984902],\n                            [117.671363, 39.973896],\n                            [117.614697, 39.97252],\n                            [117.594987, 39.994531],\n                            [117.594987, 39.994531],\n                            [117.546327, 39.999116],\n                            [117.534625, 39.954631],\n                            [117.514914, 39.946832],\n                            [117.513067, 39.910576],\n                            [117.513067, 39.910576],\n                            [117.512451, 39.90874],\n                            [117.512451, 39.90874],\n                            [117.508139, 39.901854],\n                            [117.508139, 39.901854],\n                            [117.529081, 39.859144],\n                            [117.529081, 39.859144],\n                            [117.561726, 39.799856],\n                            [117.546327, 39.775943],\n                            [117.56111, 39.754782],\n                            [117.595603, 39.74604],\n                            [117.57774, 39.726711],\n                            [117.627015, 39.703693],\n                            [117.668899, 39.666849],\n                            [117.66274, 39.636437],\n                            [117.637486, 39.603246],\n                            [117.654117, 39.575113],\n                            [117.684914, 39.58895],\n                            [117.707088, 39.576036],\n                            [117.715711, 39.529892],\n                            [117.745276, 39.547892],\n                            [117.753899, 39.579726],\n                            [117.753899, 39.579726],\n                            [117.766834, 39.598635],\n                            [117.829659, 39.589873],\n                            [117.868464, 39.59679],\n                            [117.933753, 39.574191],\n                            [117.904804, 39.533585],\n                            [117.912195, 39.517428],\n                            [117.912195, 39.517428],\n                            [117.899877, 39.474479],\n                            [117.870311, 39.455074],\n                            [117.871543, 39.411625],\n                            [117.846906, 39.407926],\n                            [117.852449, 39.380639],\n                            [117.805022, 39.373237],\n                            [117.810565, 39.354729],\n                            [117.850601, 39.363984],\n                            [117.850601, 39.363984],\n                            [117.854297, 39.328348],\n                            [117.854913, 39.328348],\n                            [117.854297, 39.328348],\n                            [117.854913, 39.328348],\n                            [117.88879, 39.332051],\n                            [117.919587, 39.318162],\n                            [117.919587, 39.318162],\n                            [117.965782, 39.314921],\n                            [117.965782, 39.314921],\n                            [117.973173, 39.312143],\n                            [117.973173, 39.312143],\n                            [117.979333, 39.300566],\n                            [117.979333, 39.300566],\n                            [117.982412, 39.298714],\n                            [117.982412, 39.298714],\n                            [118.024296, 39.289451],\n                            [118.024296, 39.289451],\n                            [118.036615, 39.264898],\n                            [118.064948, 39.256094],\n                            [118.064948, 39.231065],\n                            [118.034767, 39.218548],\n                            [118.026144, 39.201854],\n                            [118.070492, 39.213911],\n                            [118.077883, 39.201854],\n                            [118.12531, 39.182838],\n                            [118.162883, 39.136433],\n                            [118.1906, 39.080708],\n                            [118.225092, 39.034701],\n                            [118.319331, 39.009594],\n                            [118.366143, 39.016104],\n                            [118.377845, 38.971917],\n                            [118.491178, 38.909077],\n                            [118.539837, 38.910008],\n                            [118.604511, 38.971452],\n                            [118.570634, 38.999363],\n                            [118.533062, 39.090928],\n                            [118.588497, 39.107648],\n                            [118.578642, 39.130863],\n                            [118.637156, 39.157319],\n                            [118.76096, 39.133648],\n                            [118.814546, 39.138754],\n                            [118.857662, 39.162888],\n                            [118.897082, 39.151286],\n                            [118.920488, 39.171703],\n                            [118.951285, 39.178662],\n                            [118.896466, 39.139683],\n                            [118.890307, 39.118792],\n                            [118.926031, 39.123435],\n                            [118.97777, 39.163352],\n                            [119.023966, 39.187012],\n                            [119.038132, 39.211593],\n                            [119.096031, 39.24219],\n                            [119.121284, 39.281576],\n                            [119.185342, 39.342234],\n                            [119.272805, 39.363521],\n                            [119.317153, 39.4107],\n                            [119.316537, 39.437051],\n                            [119.269726, 39.498497],\n                            [119.366428, 39.734996],\n                            [119.474217, 39.813189],\n                            [119.536427, 39.809052],\n                            [119.520413, 39.840306],\n                            [119.540739, 39.888079],\n                            [119.588166, 39.910576],\n                            [119.620195, 39.904609],\n                            [119.642369, 39.925264],\n                            [119.681789, 39.922511],\n                            [119.726137, 39.940867],\n                            [119.787115, 39.950502],\n                            [119.820375, 39.979399],\n                            [119.842549, 39.956007],\n                            [119.872114, 39.960594],\n                            [119.854252, 39.98857],\n                            [119.845629, 40.000949],\n                            [119.845629, 40.000949],\n                            [119.854252, 40.033033],\n                            [119.81668, 40.050443],\n                            [119.81668, 40.050443],\n                            [119.787115, 40.041739],\n                            [119.787115, 40.041739],\n                            [119.783419, 40.046778],\n                            [119.783419, 40.046778],\n                            [119.772332, 40.08113],\n                            [119.736608, 40.104936],\n                            [119.760629, 40.136056],\n                            [119.745847, 40.207851],\n                            [119.716898, 40.195966],\n                            [119.671934, 40.23938],\n                            [119.639289, 40.231613],\n                            [119.639289, 40.231613],\n                            [119.651608, 40.271808],\n                            [119.598021, 40.334335],\n                            [119.586934, 40.375381],\n                            [119.604797, 40.455119],\n                            [119.553674, 40.502007],\n                            [119.572152, 40.523846],\n                            [119.559217, 40.547952],\n                            [119.503783, 40.553864],\n                            [119.477913, 40.533399],\n                            [119.429254, 40.540221],\n                            [119.30237, 40.530215],\n                            [119.256175, 40.543404],\n                            [119.22045, 40.569322],\n                            [119.230921, 40.603863],\n                            [119.177951, 40.609315],\n                            [119.162552, 40.600228],\n                            [119.14469, 40.632482],\n                            [119.184726, 40.680153],\n                            [119.165632, 40.69286],\n                            [119.115125, 40.666536],\n                            [119.054763, 40.664721],\n                            [119.028277, 40.692406],\n                            [119.011031, 40.687414],\n                            [118.96114, 40.72008],\n                            [118.950053, 40.747743],\n                            [118.895234, 40.75409],\n                            [118.907553, 40.775394],\n                            [118.878604, 40.783098],\n                            [118.845959, 40.822057],\n                            [118.873061, 40.847866],\n                            [118.90201, 40.960946],\n                            [118.916792, 40.969984],\n                            [118.977154, 40.959138],\n                            [118.977154, 40.959138],\n                            [119.00056, 40.967273],\n                            [119.013495, 41.007479],\n                            [118.951901, 41.018317],\n                            [118.937118, 41.052625],\n                            [118.964836, 41.079246],\n                            [119.037516, 41.067516],\n                            [119.080632, 41.095936],\n                            [119.081248, 41.131555],\n                            [119.126212, 41.138767],\n                            [119.189038, 41.198234],\n                            [119.169943, 41.222996],\n                            [119.204436, 41.222546],\n                            [119.209364, 41.244599],\n                            [119.2494, 41.279689],\n                            [119.239545, 41.31431],\n                            [119.211827, 41.308016],\n                            [119.197661, 41.282837],\n                            [119.168712, 41.294978],\n                            [119.092951, 41.293629],\n                            [118.980234, 41.305769],\n                            [118.949437, 41.317906],\n                            [118.890923, 41.300823],\n                            [118.844727, 41.342622],\n                            [118.843496, 41.374516],\n                            [118.770199, 41.352956],\n                            [118.741866, 41.324198],\n                            [118.677192, 41.35026],\n                            [118.629765, 41.346666],\n                            [118.528135, 41.355202],\n                            [118.412338, 41.331838],\n                            [118.380309, 41.312062],\n                            [118.348896, 41.342622],\n                            [118.361215, 41.384844],\n                            [118.348896, 41.428384],\n                            [118.327338, 41.450816],\n                            [118.271904, 41.471446],\n                            [118.315636, 41.512688],\n                            [118.302701, 41.55256],\n                            [118.215237, 41.59554],\n                            [118.206614, 41.650566],\n                            [118.159187, 41.67605],\n                            [118.155491, 41.712694],\n                            [118.132702, 41.733241],\n                            [118.140093, 41.784134],\n                            [118.178281, 41.814917],\n                            [118.236179, 41.80778],\n                            [118.247266, 41.773869],\n                            [118.29223, 41.772976],\n                            [118.335346, 41.845241],\n                            [118.340273, 41.87243],\n                            [118.268824, 41.930336],\n                            [118.306396, 41.940131],\n                            [118.313788, 41.98819],\n                            [118.291614, 42.007759],\n                            [118.239875, 42.024655],\n                            [118.286686, 42.033991],\n                            [118.296541, 42.057545],\n                            [118.27252, 42.083312],\n                            [118.239259, 42.092639],\n                            [118.212774, 42.081091],\n                            [118.220165, 42.058434],\n                            [118.194296, 42.031324],\n                            [118.116687, 42.037102],\n                            [118.155491, 42.081091],\n                            [118.097593, 42.105072],\n                            [118.089586, 42.12283],\n                            [118.106216, 42.172082],\n                            [118.033535, 42.199132],\n                            [117.977485, 42.229716],\n                            [117.974405, 42.25054],\n                            [118.047702, 42.280656],\n                            [118.060021, 42.298364],\n                            [118.008898, 42.346595],\n                            [118.024296, 42.385064],\n                            [117.997811, 42.416884],\n                            [117.874007, 42.510038],\n                            [117.856761, 42.539148],\n                            [117.797631, 42.585431],\n                            [117.801326, 42.612744],\n                            [117.779768, 42.61847],\n                            [117.708935, 42.588515],\n                            [117.667051, 42.582347],\n                            [117.60053, 42.603054],\n                            [117.537088, 42.603054],\n                            [117.530313, 42.590278],\n                            [117.475494, 42.602613],\n                            [117.435458, 42.585431],\n                            [117.434226, 42.557224],\n                            [117.387415, 42.517537],\n                            [117.387415, 42.517537],\n                            [117.410205, 42.519743],\n                            [117.413284, 42.471645],\n                            [117.390495, 42.461933],\n                            [117.332596, 42.46105],\n                            [117.332596, 42.46105],\n                            [117.275314, 42.481797],\n                            [117.275314, 42.481797],\n                            [117.188467, 42.468114],\n                            [117.188467, 42.468114],\n                            [117.135496, 42.468996],\n                            [117.09546, 42.484004],\n                            [117.080061, 42.463699],\n                            [117.080061, 42.463699],\n                            [117.01662, 42.456193],\n                            [117.01662, 42.456193],\n                            [117.009228, 42.44957],\n                            [117.009228, 42.44957],\n                            [117.005533, 42.43367],\n                            [117.005533, 42.43367],\n                            [116.99075, 42.425719],\n                            [116.99075, 42.425719],\n                            [116.974736, 42.426603],\n                            [116.974736, 42.426603],\n                            [116.97104, 42.427486],\n                            [116.97104, 42.427486],\n                            [116.944555, 42.415116],\n                            [116.944555, 42.415116],\n                            [116.936547, 42.410256],\n                            [116.936547, 42.410256],\n                            [116.921765, 42.403628],\n                            [116.921765, 42.403628],\n                            [116.910062, 42.395231],\n                            [116.910062, 42.395231],\n                            [116.910678, 42.394789],\n                            [116.910678, 42.394789],\n                            [116.886656, 42.366496],\n                            [116.897743, 42.297479],\n                            [116.918685, 42.229716],\n                            [116.903287, 42.190708],\n                            [116.789338, 42.200462],\n                            [116.825062, 42.155669],\n                            [116.850316, 42.156556],\n                            [116.890352, 42.092639],\n                            [116.879881, 42.018431],\n                            [116.796113, 41.977958],\n                            [116.748686, 41.984186],\n                            [116.727744, 41.951259],\n                            [116.66923, 41.947698],\n                            [116.639049, 41.929891],\n                            [116.597165, 41.935679],\n                            [116.553433, 41.928555],\n                            [116.510933, 41.974399],\n                            [116.4826, 41.975734],\n                            [116.453651, 41.945917],\n                            [116.393289, 41.942802],\n                            [116.414231, 41.982407],\n                            [116.373579, 42.009983],\n                            [116.310137, 41.997086],\n                            [116.298434, 41.96817],\n                            [116.223906, 41.932562],\n                            [116.212819, 41.885352],\n                            [116.194341, 41.861734],\n                            [116.122892, 41.861734],\n                            [116.106877, 41.831419],\n                            [116.129051, 41.805996],\n                            [116.09887, 41.776547],\n                            [116.034196, 41.782795],\n                            [116.007095, 41.79752],\n                            [116.007095, 41.797966],\n                            [116.007095, 41.79752],\n                            [116.007095, 41.797966],\n                            [115.994776, 41.828743],\n                            [115.954124, 41.874213],\n                            [115.916552, 41.945027],\n                            [115.85311, 41.927665],\n                            [115.834632, 41.93835],\n                            [115.811226, 41.912525],\n                            [115.726227, 41.870202],\n                            [115.688038, 41.867528],\n                            [115.654162, 41.829189],\n                            [115.57409, 41.80555],\n                            [115.519887, 41.76762],\n                            [115.488474, 41.760924],\n                            [115.42996, 41.728775],\n                            [115.346808, 41.712247],\n                            [115.319091, 41.691693],\n                            [115.360975, 41.661297],\n                            [115.345576, 41.635807],\n                            [115.377605, 41.603148],\n                            [115.310468, 41.592854],\n                            [115.290142, 41.622835],\n                            [115.26612, 41.616124],\n                            [115.256881, 41.580768],\n                            [115.20391, 41.571367],\n                            [115.195287, 41.602253],\n                            [115.0992, 41.62373],\n                            [115.056085, 41.602253],\n                            [115.016049, 41.615229],\n                            [114.860832, 41.60091],\n                            [114.895325, 41.636255],\n                            [114.902716, 41.695715],\n                            [114.89594, 41.76762],\n                            [114.868839, 41.813579],\n                            [114.922426, 41.825175],\n                            [114.939056, 41.846132],\n                            [114.923658, 41.871093],\n                            [114.915035, 41.960605],\n                            [114.9021, 42.015763],\n                            [114.860832, 42.054879],\n                            [114.86268, 42.097967],\n                            [114.825723, 42.139695],\n                            [114.79431, 42.149457],\n                            [114.789383, 42.130819],\n                            [114.75489, 42.115727],\n                            [114.675434, 42.12061],\n                            [114.647717, 42.109512],\n                            [114.560254, 42.132595],\n                            [114.510978, 42.110844],\n                            [114.502355, 42.06732],\n                            [114.480181, 42.064654],\n                            [114.467863, 42.025989],\n                            [114.511594, 41.981962],\n                            [114.478334, 41.951704],\n                            [114.419203, 41.942356],\n                            [114.352066, 41.953484],\n                            [114.343443, 41.926774],\n                            [114.282465, 41.863517],\n                            [114.200545, 41.789934],\n                            [114.215328, 41.75646],\n                            [114.206704, 41.7386],\n                            [114.237501, 41.698843],\n                            [114.215328, 41.68499],\n                            [114.259059, 41.623282],\n                            [114.226414, 41.616572],\n                            [114.221487, 41.582111],\n                            [114.230726, 41.513584],\n                            [114.101379, 41.537779],\n                            [114.032394, 41.529715],\n                            [113.976959, 41.505966],\n                            [113.953553, 41.483553],\n                            [113.933227, 41.487139],\n                            [113.919677, 41.454404],\n                            [113.877793, 41.431076],\n                            [113.871017, 41.413126],\n                            [113.94493, 41.392477],\n                            [113.92522, 41.325546],\n                            [113.899351, 41.316108],\n                            [113.914749, 41.294529],\n                            [113.95109, 41.282837],\n                            [113.971416, 41.239649],\n                            [113.992357, 41.269794],\n                            [114.016379, 41.231999],\n                            [113.996669, 41.19238],\n                            [113.960945, 41.171211],\n                            [113.920293, 41.172112],\n                            [113.877793, 41.115777],\n                            [113.819279, 41.09774],\n                            [113.868554, 41.06887],\n                            [113.973263, 40.983087],\n                            [113.994821, 40.938798],\n                            [114.057647, 40.925234],\n                            [114.041633, 40.917546],\n                            [114.055183, 40.867782],\n                            [114.073661, 40.857372],\n                            [114.044712, 40.830661],\n                            [114.080437, 40.790348],\n                            [114.104458, 40.797597],\n                            [114.103227, 40.770861],\n                            [114.134639, 40.737314],\n                            [114.162357, 40.71373],\n                            [114.183299, 40.67153],\n                            [114.236269, 40.607043],\n                            [114.283081, 40.590685],\n                            [114.273842, 40.552954],\n                            [114.293552, 40.55159],\n                            [114.282465, 40.494725],\n                            [114.267066, 40.474242],\n                            [114.299711, 40.44009],\n                            [114.286161, 40.425057],\n                            [114.31203, 40.372645],\n                            [114.381015, 40.36307],\n                            [114.390254, 40.351213],\n                            [114.438914, 40.371733],\n                            [114.481413, 40.34802],\n                            [114.530688, 40.345283],\n                            [114.510978, 40.302851],\n                            [114.46971, 40.268155],\n                            [114.406269, 40.246232],\n                            [114.362537, 40.249886],\n                            [114.292936, 40.230242],\n                            [114.255364, 40.236182],\n                            [114.235654, 40.198252],\n                            [114.180219, 40.191395],\n                            [114.135871, 40.175392],\n                            [114.097683, 40.193681],\n                            [114.073046, 40.168533],\n                            [114.073046, 40.168533],\n                            [114.101995, 40.099901],\n                            [114.086596, 40.071513],\n                            [114.045944, 40.056856],\n                            [114.018227, 40.103563],\n                            [113.989278, 40.11226],\n                            [113.959097, 40.033491],\n                            [113.910438, 40.015618],\n                            [114.029314, 39.985819],\n                            [114.028082, 39.959218],\n                            [114.047176, 39.916085],\n                            [114.067502, 39.922511],\n                            [114.17406, 39.897722],\n                            [114.212248, 39.918839],\n                            [114.229494, 39.899558],\n                            [114.204241, 39.885324],\n                            [114.215943, 39.8619],\n                            [114.286776, 39.871087],\n                            [114.285545, 39.858225],\n                            [114.395182, 39.867412],\n                            [114.406885, 39.833413],\n                            [114.390254, 39.819165],\n                            [114.41674, 39.775943],\n                            [114.409964, 39.761683],\n                            [114.408117, 39.652106],\n                            [114.431522, 39.613851],\n                            [114.49558, 39.608318],\n                            [114.51529, 39.564964],\n                            [114.568877, 39.573729],\n                            [114.532536, 39.486027],\n                            [114.501739, 39.476789],\n                            [114.496812, 39.438437],\n                            [114.469095, 39.400989],\n                            [114.466631, 39.329736],\n                            [114.430906, 39.307513],\n                            [114.437066, 39.259337],\n                            [114.416124, 39.242654],\n                            [114.47587, 39.21623],\n                            [114.443841, 39.174023],\n                            [114.388406, 39.176807],\n                            [114.360689, 39.134112],\n                            [114.369928, 39.107648],\n                            [114.345907, 39.075133],\n                            [114.252284, 39.073739],\n                            [114.180835, 39.049111],\n                            [114.157429, 39.061194],\n                            [114.10877, 39.052364],\n                            [114.082901, 39.09325],\n                            [114.082901, 39.09325],\n                            [114.064422, 39.094179],\n                            [114.050872, 39.135969],\n                            [114.006524, 39.122971],\n                            [113.994821, 39.095572],\n                            [113.961561, 39.100681],\n                            [113.930148, 39.063517],\n                            [113.898119, 39.067699],\n                            [113.80696, 38.989595],\n                            [113.776779, 38.986804],\n                            [113.76754, 38.959819],\n                            [113.776163, 38.885788],\n                            [113.795257, 38.860628],\n                            [113.855619, 38.828933],\n                            [113.836525, 38.795824],\n                            [113.839605, 38.7585],\n                            [113.802648, 38.763166],\n                            [113.775547, 38.709949],\n                            [113.720728, 38.713218],\n                            [113.70225, 38.651551],\n                            [113.612939, 38.645942],\n                            [113.603084, 38.587024],\n                            [113.561816, 38.558483],\n                            [113.546417, 38.492936],\n                            [113.583374, 38.459671],\n                            [113.537794, 38.417952],\n                            [113.525475, 38.383245],\n                            [113.557504, 38.343359],\n                            [113.54457, 38.270569],\n                            [113.570439, 38.237202],\n                            [113.598772, 38.22733],\n                            [113.64312, 38.232031],\n                            [113.678844, 38.20523],\n                            [113.711489, 38.213695],\n                            [113.720728, 38.174656],\n                            [113.797105, 38.162894],\n                            [113.831597, 38.16854],\n                            [113.811271, 38.117707],\n                            [113.876561, 38.055059],\n                            [113.872249, 37.990471],\n                            [113.901198, 37.984811],\n                            [113.936307, 37.922993],\n                            [113.959097, 37.906468],\n                            [113.976959, 37.816696],\n                            [114.006524, 37.813386],\n                            [114.044712, 37.761834],\n                            [113.996669, 37.730128],\n                            [113.993589, 37.706932],\n                            [114.068118, 37.721608],\n                            [114.12848, 37.698409],\n                            [114.139567, 37.675676],\n                            [114.115545, 37.619761],\n                            [114.118625, 37.59084],\n                            [114.036705, 37.494037],\n                            [114.014531, 37.42468],\n                            [113.973879, 37.40329],\n                            [113.962792, 37.355734],\n                            [113.90243, 37.310052],\n                            [113.886416, 37.239095],\n                            [113.853155, 37.215269],\n                            [113.832213, 37.167594],\n                            [113.773083, 37.151855],\n                            [113.773699, 37.107004],\n                            [113.758301, 37.075497],\n                            [113.788482, 37.059739],\n                            [113.771851, 37.016745],\n                            [113.791561, 36.98759],\n                            [113.76138, 36.956034],\n                            [113.792793, 36.894796],\n                            [113.773083, 36.85506],\n                            [113.731815, 36.858891],\n                            [113.731815, 36.878521],\n                            [113.696707, 36.882351],\n                            [113.676381, 36.855539],\n                            [113.680692, 36.789907],\n                            [113.600004, 36.752995],\n                            [113.549497, 36.752515],\n                            [113.535946, 36.732373],\n                            [113.499606, 36.740527],\n                            [113.465113, 36.707908],\n                            [113.506997, 36.705029],\n                            [113.476816, 36.655114],\n                            [113.486671, 36.635427],\n                            [113.54457, 36.62342],\n                            [113.539642, 36.594116],\n                            [113.569823, 36.585947],\n                            [113.588917, 36.547974],\n                            [113.559968, 36.528741],\n                            [113.554425, 36.494589],\n                            [113.587069, 36.460904],\n                            [113.635729, 36.451277],\n                            [113.670221, 36.425278],\n                            [113.708409, 36.423352],\n                            [113.731199, 36.363135],\n                            [113.755221, 36.366026],\n                            [113.813119, 36.332285],\n                            [113.856851, 36.329392],\n                            [113.84946, 36.347711],\n                            [113.882104, 36.353977],\n                            [113.911054, 36.314927],\n                            [113.962792, 36.353977],\n                            [113.981887, 36.31782],\n                            [114.002828, 36.334214],\n                            [114.056415, 36.329392],\n                            [114.04348, 36.303353],\n                            [114.080437, 36.269585],\n                            [114.129096, 36.280199],\n                            [114.175907, 36.264759],\n                            [114.170364, 36.245938],\n                            [114.170364, 36.245938],\n                            [114.203009, 36.245456],\n                            [114.2104, 36.272962],\n                            [114.241197, 36.251247],\n                            [114.257827, 36.263794],\n                            [114.299095, 36.245938],\n                            [114.345291, 36.255591],\n                            [114.356378, 36.230492],\n                            [114.408117, 36.224699],\n                            [114.417356, 36.205868],\n                            [114.466015, 36.197658],\n                            [114.480181, 36.177855],\n                            [114.533152, 36.171575],\n                            [114.586739, 36.141133],\n                            [114.588587, 36.118414],\n                            [114.640326, 36.137266],\n                            [114.720398, 36.140166],\n                            [114.734564, 36.15563],\n                            [114.771521, 36.124699],\n                            [114.857752, 36.127599],\n                            [114.858368, 36.144516],\n                            [114.912571, 36.140649],\n                            [114.926737, 36.089403],\n                            [114.914419, 36.052155],\n                            [114.998186, 36.069572],\n                            [115.04623, 36.112613],\n                            [115.048693, 36.161912],\n                            [115.06286, 36.178338],\n                            [115.104744, 36.172058],\n                            [115.12507, 36.209731],\n                            [115.1842, 36.193312],\n                            [115.201446, 36.210214],\n                            [115.201446, 36.210214],\n                            [115.202678, 36.209248],\n                            [115.202678, 36.209248],\n                            [115.202678, 36.208765],\n                            [115.202678, 36.208765],\n                            [115.242098, 36.19138],\n                            [115.279055, 36.13775],\n                            [115.30246, 36.127599],\n                            [115.312931, 36.088436],\n                            [115.365902, 36.099074],\n                            [115.376989, 36.128083],\n                            [115.450902, 36.152248],\n                            [115.465068, 36.170125],\n                            [115.483547, 36.148865],\n                            [115.474923, 36.248352],\n                            [115.466916, 36.258969],\n                            [115.466916, 36.258969],\n                            [115.462605, 36.276339],\n                            [115.417025, 36.292742],\n                            [115.423185, 36.32216],\n                            [115.366518, 36.30914],\n                            [115.368982, 36.342409],\n                            [115.340033, 36.398307],\n                            [115.297533, 36.413239],\n                            [115.317243, 36.454166],\n                            [115.291374, 36.460423],\n                            [115.272895, 36.497476],\n                            [115.33141, 36.550378],\n                            [115.355431, 36.627262],\n                            [115.365902, 36.621979],\n                            [115.420105, 36.686795],\n                            [115.451518, 36.702151],\n                            [115.479851, 36.760187],\n                            [115.524815, 36.763543],\n                            [115.683727, 36.808117],\n                            [115.71206, 36.883308],\n                            [115.75764, 36.902453],\n                            [115.79706, 36.968945],\n                            [115.776734, 36.992848],\n                            [115.85619, 37.060694],\n                            [115.888219, 37.112254],\n                            [115.879596, 37.150901],\n                            [115.91224, 37.177132],\n                            [115.909777, 37.20669],\n                            [115.969523, 37.239572],\n                            [115.975682, 37.337179],\n                            [116.024341, 37.360015],\n                            [116.085935, 37.373809],\n                            [116.106261, 37.368577],\n                            [116.169087, 37.384271],\n                            [116.193109, 37.365723],\n                            [116.236224, 37.361442],\n                            [116.2855, 37.404241],\n                            [116.226369, 37.428007],\n                            [116.243, 37.447965],\n                            [116.224522, 37.479791],\n                            [116.240536, 37.489764],\n                            [116.240536, 37.489764],\n                            [116.27626, 37.466967],\n                            [116.290427, 37.484065],\n                            [116.278724, 37.524895],\n                            [116.295355, 37.554316],\n                            [116.336007, 37.581355],\n                            [116.36742, 37.566177],\n                            [116.379738, 37.522047],\n                            [116.38097, 37.522522],\n                            [116.379738, 37.522047],\n                            [116.38097, 37.522522],\n                            [116.433941, 37.473142],\n                            [116.448108, 37.503059],\n                            [116.4826, 37.521573],\n                            [116.575607, 37.610754],\n                            [116.604556, 37.624975],\n                            [116.66307, 37.686096],\n                            [116.679085, 37.728708],\n                            [116.724664, 37.744327],\n                            [116.753613, 37.77035],\n                            [116.753613, 37.793054],\n                            [116.804736, 37.848837],\n                            [116.837997, 37.835132],\n                            [116.919301, 37.846002],\n                            [117.027091, 37.832296],\n                            [117.074518, 37.848837],\n                            [117.150278, 37.839385],\n                            [117.185387, 37.849783],\n                            [117.271618, 37.839858],\n                            [117.320278, 37.861596],\n                            [117.400966, 37.844584],\n                            [117.438538, 37.854035],\n                            [117.481038, 37.914967],\n                            [117.513067, 37.94329],\n                            [117.524154, 37.989527],\n                            [117.557414, 38.046105],\n                            [117.557414, 38.046105],\n                            [117.586979, 38.071551],\n                            [117.704624, 38.076262],\n                            [117.746508, 38.12524],\n                            [117.771145, 38.134655],\n                            [117.766834, 38.158658],\n                            [117.789007, 38.180772],\n                            [117.808718, 38.22827],\n                            [117.848754, 38.255062],\n                            [117.895565, 38.301572],\n                            [117.948536, 38.346644],\n                            [117.957775, 38.376208],\n                            [117.937449, 38.387936],\n                            [117.84629, 38.368232],\n                            [117.781, 38.373862],\n                            [117.730493, 38.424985],\n                            [117.72495, 38.457328],\n                            [117.678754, 38.477008],\n                            [117.644878, 38.52759],\n                            [117.68553, 38.539293],\n                            [117.638102, 38.54491],\n                            [117.639334, 38.626776],\n                            [117.55803, 38.613683],\n                            [117.47919, 38.616489],\n                            [117.432379, 38.601524],\n                            [117.368937, 38.564566],\n                            [117.25314, 38.556143],\n                            [117.238358, 38.580943],\n                            [117.258684, 38.608072],\n                            [117.258684, 38.608072],\n                            [117.213104, 38.639866],\n                            [117.213104, 38.639866],\n                            [117.183539, 38.61836],\n                            [117.183539, 38.61836],\n                            [117.150894, 38.617892],\n                            [117.109626, 38.584685],\n                            [117.070822, 38.608072],\n                            [117.055424, 38.639398],\n                            [117.068358, 38.680522],\n                            [117.038793, 38.688464],\n                            [116.95133, 38.689398],\n                            [116.948866, 38.689398],\n                            [116.950714, 38.689398],\n                            [116.95133, 38.689398],\n                            [116.950714, 38.689398],\n                            [116.948866, 38.689398],\n                            [116.877417, 38.680522],\n                            [116.858939, 38.741231],\n                            [116.794265, 38.744498],\n                            [116.794265, 38.744498],\n                            [116.746222, 38.754299],\n                            [116.737599, 38.784629],\n                            [116.75115, 38.831264],\n                            [116.723432, 38.852706],\n                            [116.722201, 38.896968],\n                            [116.708034, 38.931892],\n                            [116.72836, 38.975174],\n                            [116.754845, 39.003084],\n                            [116.754229, 39.034701],\n                            [116.754229, 39.034701],\n                            [116.783179, 39.05097],\n                            [116.783179, 39.05097],\n                            [116.812744, 39.05097],\n                            [116.812744, 39.05097],\n                            [116.871874, 39.054688],\n                            [116.912526, 39.110898],\n                            [116.91191, 39.111362],\n                            [116.91191, 39.111362],\n                            [116.912526, 39.110898],\n                            [116.909446, 39.150822],\n                            [116.870026, 39.153607],\n                            [116.855859, 39.215766],\n                            [116.881729, 39.225966],\n                            [116.881729, 39.225966],\n                            [116.87249, 39.291304],\n                            [116.889736, 39.338068],\n                            [116.870642, 39.357506],\n                            [116.829374, 39.338994],\n                            [116.818287, 39.3737],\n                            [116.840461, 39.378326],\n                            [116.839845, 39.413474],\n                            [116.876185, 39.43474],\n                            [116.832454, 39.435664],\n                            [116.785026, 39.465702],\n                            [116.820751, 39.482332],\n                            [116.819519, 39.528507],\n                            [116.78749, 39.554352],\n                            [116.808432, 39.576497],\n                            [116.812128, 39.615695],\n                            [116.79057, 39.595868],\n                            [116.748686, 39.619844],\n                            [116.709266, 39.618],\n                            [116.726512, 39.595407],\n                            [116.726512, 39.595407],\n                            [116.724048, 39.59264],\n                            [116.723432, 39.59264],\n                            [116.724048, 39.59264],\n                            [116.723432, 39.59264],\n                            [116.664918, 39.605552],\n                            [116.620571, 39.601863],\n                            [116.592237, 39.621227],\n                            [116.592237, 39.621227],\n                            [116.524484, 39.596329],\n                            [116.50847, 39.551122],\n                            [116.473361, 39.552968],\n                            [116.478289, 39.535431],\n                            [116.437637, 39.526661],\n                            [116.443796, 39.510041],\n                            [116.401912, 39.528046],\n                            [116.411767, 39.482794],\n                            [116.444412, 39.482332],\n                            [116.454883, 39.453226],\n                            [116.434557, 39.442597],\n                            [116.361876, 39.455074],\n                            [116.361876, 39.455074],\n                            [116.337854, 39.455536],\n                            [116.307057, 39.488337],\n                            [116.257782, 39.500344],\n                            [116.240536, 39.564041],\n                            [116.198652, 39.589412],\n                            [116.151841, 39.583416],\n                            [116.130283, 39.567732],\n                            [116.09887, 39.575113],\n                            [116.036044, 39.571884],\n                            [116.026189, 39.587567],\n                            [115.995392, 39.576958],\n                            [115.978146, 39.595868],\n                            [115.957204, 39.560812],\n                            [115.910393, 39.600479],\n                            [115.910393, 39.600479],\n                            [115.91532, 39.582955],\n                            [115.91532, 39.582955],\n                            [115.867893, 39.546507],\n                            [115.867893, 39.546507],\n                            [115.828473, 39.541431],\n                            [115.821081, 39.522968],\n                            [115.821081, 39.522968],\n                            [115.806299, 39.510041],\n                            [115.806299, 39.510041],\n                            [115.752712, 39.515581],\n                            [115.738545, 39.539585],\n                            [115.738545, 39.540046],\n                            [115.738545, 39.539585],\n                            [115.738545, 39.540046],\n                            [115.724995, 39.5442],\n                            [115.724995, 39.5442],\n                            [115.722531, 39.543738],\n                            [115.721299, 39.543738],\n                            [115.722531, 39.543738],\n                            [115.722531, 39.5442],\n                            [115.721299, 39.543738],\n                            [115.722531, 39.5442],\n                            [115.720683, 39.551122],\n                            [115.720683, 39.551122],\n                            [115.718835, 39.553891],\n                            [115.718835, 39.553891],\n                            [115.716988, 39.56035],\n                            [115.716988, 39.56035],\n                            [115.699125, 39.570039],\n                            [115.699125, 39.570039],\n                            [115.698509, 39.577881],\n                            [115.698509, 39.577881],\n                            [115.667712, 39.615234],\n                            [115.633836, 39.599557],\n                            [115.633836, 39.599557],\n                            [115.587024, 39.589873],\n                            [115.545756, 39.618922],\n                            [115.518039, 39.597252],\n                            [115.522351, 39.640124],\n                            [115.478619, 39.650723],\n                            [115.478619, 39.650723],\n                            [115.491554, 39.670074],\n                            [115.486626, 39.741899],\n                            [115.439815, 39.752022],\n                            [115.443511, 39.785601],\n                            [115.483547, 39.798477],\n                            [115.483547, 39.798477],\n                            [115.50572, 39.784222],\n                            [115.552532, 39.794799],\n                            [115.567314, 39.816407],\n                            [115.514344, 39.837549],\n                            [115.526046, 39.87568],\n                            [115.515575, 39.892212],\n                            [115.515575, 39.892212],\n                            [115.522967, 39.899099],\n                            [115.481083, 39.935819],\n                            [115.426264, 39.950502],\n                            [115.428728, 39.984443],\n                            [115.450286, 39.992697],\n                            [115.454597, 40.029825],\n                            [115.485394, 40.040364],\n                            [115.527278, 40.076092],\n                            [115.59072, 40.096239],\n                            [115.599959, 40.119583],\n                            [115.75456, 40.145663],\n                            [115.75456, 40.145663],\n                            [115.773654, 40.176307],\n                            [115.806299, 40.15344],\n                            [115.847567, 40.147036],\n                            [115.855574, 40.188652],\n                            [115.870356, 40.185909],\n                            [115.89869, 40.234354],\n                            [115.968907, 40.264045],\n                            [115.95166, 40.281852],\n                            [115.917784, 40.354405],\n                            [115.864197, 40.359422],\n                            [115.771806, 40.443734],\n                            [115.781045, 40.49336],\n                            [115.736082, 40.503372],\n                            [115.755176, 40.540221],\n                            [115.784741, 40.55841],\n                            [115.819849, 40.55932],\n                            [115.827857, 40.587504],\n                            [115.885139, 40.595229],\n                            [115.907929, 40.617493],\n                            [115.971986, 40.6025],\n                            [115.982457, 40.578868],\n                            [116.005247, 40.583868],\n                            [116.09887, 40.630665],\n                            [116.133979, 40.666536],\n                            [116.162928, 40.662451],\n                            [116.171551, 40.695582],\n                            [116.204812, 40.740035],\n                            [116.22021, 40.744115],\n                            [116.247311, 40.791707],\n                            [116.273181, 40.762703],\n                            [116.311369, 40.754996],\n                            [116.316912, 40.772221],\n                            [116.453651, 40.765876],\n                            [116.46597, 40.774487],\n                            [116.438253, 40.81934],\n                            [116.334159, 40.90443],\n                            [116.339702, 40.929303],\n                            [116.370499, 40.94377],\n                            [116.398216, 40.90624],\n                            [116.477057, 40.899907],\n                            [116.447492, 40.953715],\n                            [116.455499, 40.980828],\n                            [116.519557, 40.98128],\n                            [116.519557, 40.98128],\n                            [116.5676, 40.992574],\n                            [116.598397, 40.974503],\n                            [116.623034, 41.021026],\n                            [116.615643, 41.053076],\n                            [116.647672, 41.059394],\n                            [116.688324, 41.044501],\n                            [116.698795, 41.021477],\n                            [116.677853, 40.970888],\n                            [116.722201, 40.927495],\n                            [116.713577, 40.909858],\n                            [116.759773, 40.889954],\n                            [116.81336, 40.848319],\n                            [116.848468, 40.839264],\n                            [116.924229, 40.773581],\n                            [116.926692, 40.745022],\n                            [116.964881, 40.709647],\n                            [117.012308, 40.693767],\n                            [117.11209, 40.707379],\n                            [117.117018, 40.70012],\n                            [117.208177, 40.694675],\n                            [117.278394, 40.664267],\n                            [117.319662, 40.657911],\n                            [117.342451, 40.673799],\n                            [117.408973, 40.686961],\n                            [117.493973, 40.675161],\n                            [117.514914, 40.660181],\n                            [117.501364, 40.636569],\n                            [117.467487, 40.649738],\n                            [117.467487, 40.649738]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.210024, 40.082045],\n                            [117.204481, 40.069681],\n                            [117.159517, 40.077008],\n                            [117.140423, 40.064185],\n                            [117.105315, 40.074261],\n                            [117.105315, 40.074261],\n                            [117.051728, 40.059605],\n                            [117.025243, 40.030283],\n                            [116.945171, 40.04128],\n                            [116.927924, 40.055024],\n                            [116.867562, 40.041739],\n                            [116.831222, 40.051359],\n                            [116.820135, 40.02845],\n                            [116.781331, 40.034866],\n                            [116.757925, 39.967934],\n                            [116.782563, 39.947749],\n                            [116.78441, 39.891294],\n                            [116.812128, 39.889916],\n                            [116.865714, 39.843982],\n                            [116.907598, 39.832494],\n                            [116.918069, 39.84628],\n                            [116.949482, 39.778703],\n                            [116.902055, 39.763523],\n                            [116.916837, 39.731314],\n                            [116.887272, 39.72533],\n                            [116.889736, 39.687576],\n                            [116.90575, 39.688037],\n                            [116.932236, 39.706456],\n                            [116.932236, 39.706456],\n                            [116.944555, 39.695405],\n                            [116.944555, 39.695405],\n                            [116.948866, 39.680668],\n                            [116.948866, 39.680668],\n                            [116.964265, 39.64335],\n                            [116.983359, 39.638742],\n                            [116.983359, 39.638742],\n                            [117.016004, 39.653949],\n                            [117.10901, 39.625375],\n                            [117.10901, 39.625375],\n                            [117.152742, 39.623532],\n                            [117.177996, 39.645194],\n                            [117.165061, 39.718886],\n                            [117.165061, 39.718886],\n                            [117.161981, 39.748801],\n                            [117.205713, 39.763984],\n                            [117.15767, 39.796638],\n                            [117.156438, 39.817326],\n                            [117.192162, 39.832953],\n                            [117.251908, 39.834332],\n                            [117.247597, 39.860981],\n                            [117.227887, 39.852712],\n                            [117.162597, 39.876598],\n                            [117.162597, 39.876598],\n                            [117.150894, 39.944996],\n                            [117.198322, 39.992697],\n                            [117.192162, 40.066475],\n                            [117.210024, 40.082045]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.784696, 39.376938],\n                            [117.765602, 39.400527],\n                            [117.699696, 39.407463],\n                            [117.673211, 39.386652],\n                            [117.668899, 39.412087],\n                            [117.614081, 39.407001],\n                            [117.601146, 39.419485],\n                            [117.570965, 39.404689],\n                            [117.521074, 39.357043],\n                            [117.536472, 39.338068],\n                            [117.594987, 39.349176],\n                            [117.669515, 39.322792],\n                            [117.670747, 39.357969],\n                            [117.74466, 39.354729],\n                            [117.784696, 39.376938]\n                        ]\n                    ],\n                    [\n                        [\n                            [118.869365, 39.142932],\n                            [118.82009, 39.108576],\n                            [118.857662, 39.098824],\n                            [118.869365, 39.142932]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 140000,\n                \"name\": \"Shanxi\",\n                \"center\": [112.549248, 37.857014],\n                \"centroid\": [112.304436, 37.618179],\n                \"childrenNum\": 11,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 3,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [110.379257, 34.600612],\n                            [110.424837, 34.588295],\n                            [110.488279, 34.610956],\n                            [110.533242, 34.583368],\n                            [110.610851, 34.607508],\n                            [110.710017, 34.605045],\n                            [110.749437, 34.65232],\n                            [110.791937, 34.649858],\n                            [110.824582, 34.615881],\n                            [110.883712, 34.64395],\n                            [110.903422, 34.669056],\n                            [110.920052, 34.730068],\n                            [110.976103, 34.706456],\n                            [111.035233, 34.740887],\n                            [111.118385, 34.756623],\n                            [111.148566, 34.807742],\n                            [111.232949, 34.789559],\n                            [111.255123, 34.819535],\n                            [111.29208, 34.806759],\n                            [111.345666, 34.831816],\n                            [111.389398, 34.815113],\n                            [111.439289, 34.838202],\n                            [111.502731, 34.829851],\n                            [111.543999, 34.853428],\n                            [111.570484, 34.843114],\n                            [111.592042, 34.881416],\n                            [111.617911, 34.894671],\n                            [111.646861, 34.938836],\n                            [111.681969, 34.9511],\n                            [111.664107, 34.984449],\n                            [111.740483, 35.00455],\n                            [111.807005, 35.032977],\n                            [111.810084, 35.062374],\n                            [111.933272, 35.083435],\n                            [111.97762, 35.067272],\n                            [112.018888, 35.068742],\n                            [112.039214, 35.045717],\n                            [112.062004, 35.056005],\n                            [112.05646, 35.098615],\n                            [112.066315, 35.153437],\n                            [112.03983, 35.194039],\n                            [112.078634, 35.219467],\n                            [112.058924, 35.280069],\n                            [112.13838, 35.271275],\n                            [112.21722, 35.253195],\n                            [112.242474, 35.234622],\n                            [112.304684, 35.251728],\n                            [112.288053, 35.219956],\n                            [112.36751, 35.219956],\n                            [112.390915, 35.239021],\n                            [112.513487, 35.218489],\n                            [112.637291, 35.225822],\n                            [112.628052, 35.263457],\n                            [112.720443, 35.206265],\n                            [112.772798, 35.207732],\n                            [112.822073, 35.258082],\n                            [112.884283, 35.243909],\n                            [112.934174, 35.262968],\n                            [112.936022, 35.284466],\n                            [112.992072, 35.29619],\n                            [112.985913, 35.33965],\n                            [112.996384, 35.362104],\n                            [113.067217, 35.353806],\n                            [113.126347, 35.332327],\n                            [113.149137, 35.350878],\n                            [113.165151, 35.412845],\n                            [113.185477, 35.409431],\n                            [113.189789, 35.44893],\n                            [113.243375, 35.449418],\n                            [113.304353, 35.426989],\n                            [113.31236, 35.481101],\n                            [113.348085, 35.468429],\n                            [113.391817, 35.506925],\n                            [113.439244, 35.507412],\n                            [113.49899, 35.532254],\n                            [113.513773, 35.57364],\n                            [113.55812, 35.621816],\n                            [113.547649, 35.656835],\n                            [113.578446, 35.633491],\n                            [113.625258, 35.632518],\n                            [113.622794, 35.674825],\n                            [113.592613, 35.691838],\n                            [113.587685, 35.736542],\n                            [113.604932, 35.797727],\n                            [113.582758, 35.818111],\n                            [113.660982, 35.837035],\n                            [113.637576, 35.870019],\n                            [113.654207, 35.931586],\n                            [113.648663, 35.994073],\n                            [113.678844, 35.985841],\n                            [113.694859, 36.026991],\n                            [113.660366, 36.034735],\n                            [113.68562, 36.056026],\n                            [113.671453, 36.115514],\n                            [113.655439, 36.125182],\n                            [113.712721, 36.129533],\n                            [113.705946, 36.148865],\n                            [113.651127, 36.174473],\n                            [113.697939, 36.181719],\n                            [113.681924, 36.216491],\n                            [113.716417, 36.262347],\n                            [113.712105, 36.303353],\n                            [113.736127, 36.324571],\n                            [113.731199, 36.363135],\n                            [113.708409, 36.423352],\n                            [113.670221, 36.425278],\n                            [113.635729, 36.451277],\n                            [113.587069, 36.460904],\n                            [113.554425, 36.494589],\n                            [113.559968, 36.528741],\n                            [113.588917, 36.547974],\n                            [113.569823, 36.585947],\n                            [113.539642, 36.594116],\n                            [113.54457, 36.62342],\n                            [113.486671, 36.635427],\n                            [113.476816, 36.655114],\n                            [113.506997, 36.705029],\n                            [113.465113, 36.707908],\n                            [113.499606, 36.740527],\n                            [113.535946, 36.732373],\n                            [113.549497, 36.752515],\n                            [113.600004, 36.752995],\n                            [113.680692, 36.789907],\n                            [113.676381, 36.855539],\n                            [113.696707, 36.882351],\n                            [113.731815, 36.878521],\n                            [113.731815, 36.858891],\n                            [113.773083, 36.85506],\n                            [113.792793, 36.894796],\n                            [113.76138, 36.956034],\n                            [113.791561, 36.98759],\n                            [113.771851, 37.016745],\n                            [113.788482, 37.059739],\n                            [113.758301, 37.075497],\n                            [113.773699, 37.107004],\n                            [113.773083, 37.151855],\n                            [113.832213, 37.167594],\n                            [113.853155, 37.215269],\n                            [113.886416, 37.239095],\n                            [113.90243, 37.310052],\n                            [113.962792, 37.355734],\n                            [113.973879, 37.40329],\n                            [114.014531, 37.42468],\n                            [114.036705, 37.494037],\n                            [114.118625, 37.59084],\n                            [114.115545, 37.619761],\n                            [114.139567, 37.675676],\n                            [114.12848, 37.698409],\n                            [114.068118, 37.721608],\n                            [113.993589, 37.706932],\n                            [113.996669, 37.730128],\n                            [114.044712, 37.761834],\n                            [114.006524, 37.813386],\n                            [113.976959, 37.816696],\n                            [113.959097, 37.906468],\n                            [113.936307, 37.922993],\n                            [113.901198, 37.984811],\n                            [113.872249, 37.990471],\n                            [113.876561, 38.055059],\n                            [113.811271, 38.117707],\n                            [113.831597, 38.16854],\n                            [113.797105, 38.162894],\n                            [113.720728, 38.174656],\n                            [113.711489, 38.213695],\n                            [113.678844, 38.20523],\n                            [113.64312, 38.232031],\n                            [113.598772, 38.22733],\n                            [113.570439, 38.237202],\n                            [113.54457, 38.270569],\n                            [113.557504, 38.343359],\n                            [113.525475, 38.383245],\n                            [113.537794, 38.417952],\n                            [113.583374, 38.459671],\n                            [113.546417, 38.492936],\n                            [113.561816, 38.558483],\n                            [113.603084, 38.587024],\n                            [113.612939, 38.645942],\n                            [113.70225, 38.651551],\n                            [113.720728, 38.713218],\n                            [113.775547, 38.709949],\n                            [113.802648, 38.763166],\n                            [113.839605, 38.7585],\n                            [113.836525, 38.795824],\n                            [113.855619, 38.828933],\n                            [113.795257, 38.860628],\n                            [113.776163, 38.885788],\n                            [113.76754, 38.959819],\n                            [113.776779, 38.986804],\n                            [113.80696, 38.989595],\n                            [113.898119, 39.067699],\n                            [113.930148, 39.063517],\n                            [113.961561, 39.100681],\n                            [113.994821, 39.095572],\n                            [114.006524, 39.122971],\n                            [114.050872, 39.135969],\n                            [114.064422, 39.094179],\n                            [114.082901, 39.09325],\n                            [114.082901, 39.09325],\n                            [114.10877, 39.052364],\n                            [114.157429, 39.061194],\n                            [114.180835, 39.049111],\n                            [114.252284, 39.073739],\n                            [114.345907, 39.075133],\n                            [114.369928, 39.107648],\n                            [114.360689, 39.134112],\n                            [114.388406, 39.176807],\n                            [114.443841, 39.174023],\n                            [114.47587, 39.21623],\n                            [114.416124, 39.242654],\n                            [114.437066, 39.259337],\n                            [114.430906, 39.307513],\n                            [114.466631, 39.329736],\n                            [114.469095, 39.400989],\n                            [114.496812, 39.438437],\n                            [114.501739, 39.476789],\n                            [114.532536, 39.486027],\n                            [114.568877, 39.573729],\n                            [114.51529, 39.564964],\n                            [114.49558, 39.608318],\n                            [114.431522, 39.613851],\n                            [114.408117, 39.652106],\n                            [114.409964, 39.761683],\n                            [114.41674, 39.775943],\n                            [114.390254, 39.819165],\n                            [114.406885, 39.833413],\n                            [114.395182, 39.867412],\n                            [114.285545, 39.858225],\n                            [114.286776, 39.871087],\n                            [114.215943, 39.8619],\n                            [114.204241, 39.885324],\n                            [114.229494, 39.899558],\n                            [114.212248, 39.918839],\n                            [114.17406, 39.897722],\n                            [114.067502, 39.922511],\n                            [114.047176, 39.916085],\n                            [114.028082, 39.959218],\n                            [114.029314, 39.985819],\n                            [113.910438, 40.015618],\n                            [113.959097, 40.033491],\n                            [113.989278, 40.11226],\n                            [114.018227, 40.103563],\n                            [114.045944, 40.056856],\n                            [114.086596, 40.071513],\n                            [114.101995, 40.099901],\n                            [114.073046, 40.168533],\n                            [114.073046, 40.168533],\n                            [114.097683, 40.193681],\n                            [114.135871, 40.175392],\n                            [114.180219, 40.191395],\n                            [114.235654, 40.198252],\n                            [114.255364, 40.236182],\n                            [114.292936, 40.230242],\n                            [114.362537, 40.249886],\n                            [114.406269, 40.246232],\n                            [114.46971, 40.268155],\n                            [114.510978, 40.302851],\n                            [114.530688, 40.345283],\n                            [114.481413, 40.34802],\n                            [114.438914, 40.371733],\n                            [114.390254, 40.351213],\n                            [114.381015, 40.36307],\n                            [114.31203, 40.372645],\n                            [114.286161, 40.425057],\n                            [114.299711, 40.44009],\n                            [114.267066, 40.474242],\n                            [114.282465, 40.494725],\n                            [114.293552, 40.55159],\n                            [114.273842, 40.552954],\n                            [114.283081, 40.590685],\n                            [114.236269, 40.607043],\n                            [114.183299, 40.67153],\n                            [114.162357, 40.71373],\n                            [114.134639, 40.737314],\n                            [114.084748, 40.729605],\n                            [114.063806, 40.706925],\n                            [114.07243, 40.679246],\n                            [114.041633, 40.608861],\n                            [114.076741, 40.575686],\n                            [114.080437, 40.547952],\n                            [114.061959, 40.52885],\n                            [114.011452, 40.515657],\n                            [113.948626, 40.514747],\n                            [113.890112, 40.466503],\n                            [113.850691, 40.460583],\n                            [113.794641, 40.517932],\n                            [113.763228, 40.473787],\n                            [113.688699, 40.448288],\n                            [113.559968, 40.348476],\n                            [113.500222, 40.334335],\n                            [113.387505, 40.319279],\n                            [113.316672, 40.319736],\n                            [113.27602, 40.388601],\n                            [113.251382, 40.413211],\n                            [113.083231, 40.374925],\n                            [113.03334, 40.368997],\n                            [112.898449, 40.329317],\n                            [112.848558, 40.206937],\n                            [112.744464, 40.167161],\n                            [112.712436, 40.178593],\n                            [112.6299, 40.235725],\n                            [112.511639, 40.269068],\n                            [112.456205, 40.300112],\n                            [112.418017, 40.295091],\n                            [112.349031, 40.257194],\n                            [112.310227, 40.256281],\n                            [112.299756, 40.21105],\n                            [112.232619, 40.169905],\n                            [112.232003, 40.133311],\n                            [112.183344, 40.083877],\n                            [112.182112, 40.061437],\n                            [112.142076, 40.027076],\n                            [112.133453, 40.001866],\n                            [112.07617, 39.919298],\n                            [112.042294, 39.886243],\n                            [112.012729, 39.827438],\n                            [111.970229, 39.796638],\n                            [111.959758, 39.692642],\n                            [111.925265, 39.66731],\n                            [111.9382, 39.623071],\n                            [111.87907, 39.606013],\n                            [111.842729, 39.620305],\n                            [111.783599, 39.58895],\n                            [111.722621, 39.606013],\n                            [111.659179, 39.641507],\n                            [111.625303, 39.633672],\n                            [111.525521, 39.662242],\n                            [111.497187, 39.661781],\n                            [111.445448, 39.640124],\n                            [111.460847, 39.606935],\n                            [111.441137, 39.59679],\n                            [111.422043, 39.539123],\n                            [111.431282, 39.508656],\n                            [111.372152, 39.479099],\n                            [111.358601, 39.432428],\n                            [111.337043, 39.420872],\n                            [111.171971, 39.423183],\n                            [111.143022, 39.407926],\n                            [111.125776, 39.366297],\n                            [111.159037, 39.362596],\n                            [111.155341, 39.338531],\n                            [111.186138, 39.35149],\n                            [111.179363, 39.326959],\n                            [111.202152, 39.305197],\n                            [111.247732, 39.302419],\n                            [111.213239, 39.257021],\n                            [111.219399, 39.244044],\n                            [111.163348, 39.152678],\n                            [111.173819, 39.135041],\n                            [111.147334, 39.100681],\n                            [111.138095, 39.064447],\n                            [111.094363, 39.030053],\n                            [111.038313, 39.020289],\n                            [110.998276, 38.998433],\n                            [110.980414, 38.970056],\n                            [111.009979, 38.932823],\n                            [111.016755, 38.889981],\n                            [110.995813, 38.868084],\n                            [111.009363, 38.847579],\n                            [110.965016, 38.755699],\n                            [110.915125, 38.704345],\n                            [110.916357, 38.673981],\n                            [110.880632, 38.626776],\n                            [110.898494, 38.587024],\n                            [110.920052, 38.581878],\n                            [110.907733, 38.521035],\n                            [110.870777, 38.510265],\n                            [110.874473, 38.453579],\n                            [110.840596, 38.439986],\n                            [110.796864, 38.453579],\n                            [110.77777, 38.440924],\n                            [110.746973, 38.366355],\n                            [110.701394, 38.353215],\n                            [110.661358, 38.308617],\n                            [110.601612, 38.308147],\n                            [110.57759, 38.297345],\n                            [110.565887, 38.215105],\n                            [110.528315, 38.211814],\n                            [110.509221, 38.192061],\n                            [110.519692, 38.130889],\n                            [110.501829, 38.097929],\n                            [110.507989, 38.013107],\n                            [110.528315, 37.990471],\n                            [110.522771, 37.955088],\n                            [110.59422, 37.922049],\n                            [110.680452, 37.790216],\n                            [110.735886, 37.77035],\n                            [110.750669, 37.736281],\n                            [110.716792, 37.728708],\n                            [110.706321, 37.705511],\n                            [110.775306, 37.680886],\n                            [110.793169, 37.650567],\n                            [110.763604, 37.639668],\n                            [110.771611, 37.594634],\n                            [110.795017, 37.558586],\n                            [110.770995, 37.538184],\n                            [110.759292, 37.474567],\n                            [110.740198, 37.44939],\n                            [110.644111, 37.435135],\n                            [110.630561, 37.372858],\n                            [110.641648, 37.360015],\n                            [110.695234, 37.34955],\n                            [110.678604, 37.317668],\n                            [110.690307, 37.287201],\n                            [110.661974, 37.281963],\n                            [110.651503, 37.256722],\n                            [110.590525, 37.187145],\n                            [110.53509, 37.138021],\n                            [110.535706, 37.115118],\n                            [110.49567, 37.086956],\n                            [110.460561, 37.044932],\n                            [110.417446, 37.027257],\n                            [110.426685, 37.008621],\n                            [110.382953, 37.022001],\n                            [110.381721, 37.002408],\n                            [110.424221, 36.963685],\n                            [110.408823, 36.892403],\n                            [110.376178, 36.882351],\n                            [110.424221, 36.855539],\n                            [110.406975, 36.824886],\n                            [110.423605, 36.818179],\n                            [110.407591, 36.776007],\n                            [110.447011, 36.737649],\n                            [110.438388, 36.685835],\n                            [110.402663, 36.697352],\n                            [110.394656, 36.676716],\n                            [110.426685, 36.657514],\n                            [110.447627, 36.621018],\n                            [110.496902, 36.582102],\n                            [110.488895, 36.556628],\n                            [110.503677, 36.488335],\n                            [110.47288, 36.453203],\n                            [110.489511, 36.430094],\n                            [110.487047, 36.393972],\n                            [110.459946, 36.327946],\n                            [110.474112, 36.306729],\n                            [110.474112, 36.248352],\n                            [110.45625, 36.22663],\n                            [110.447011, 36.164328],\n                            [110.467953, 36.074893],\n                            [110.491974, 36.034735],\n                            [110.49259, 35.994073],\n                            [110.516612, 35.971796],\n                            [110.502445, 35.947575],\n                            [110.516612, 35.918501],\n                            [110.511684, 35.879718],\n                            [110.549257, 35.877778],\n                            [110.550489, 35.838005],\n                            [110.571431, 35.800639],\n                            [110.57759, 35.701559],\n                            [110.609619, 35.632031],\n                            [110.589293, 35.602355],\n                            [110.567735, 35.539559],\n                            [110.531394, 35.511309],\n                            [110.477808, 35.413821],\n                            [110.45009, 35.327933],\n                            [110.374946, 35.251728],\n                            [110.378642, 35.210666],\n                            [110.364475, 35.197952],\n                            [110.373714, 35.134351],\n                            [110.320743, 35.00504],\n                            [110.262229, 34.944233],\n                            [110.230816, 34.880925],\n                            [110.246831, 34.789068],\n                            [110.243135, 34.725641],\n                            [110.229584, 34.692679],\n                            [110.269004, 34.629671],\n                            [110.29549, 34.610956],\n                            [110.379257, 34.600612]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 150000,\n                \"name\": \"Inner Mongolia\",\n                \"center\": [111.670801, 40.818311],\n                \"centroid\": [114.077429, 44.331087],\n                \"childrenNum\": 12,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 4,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [97.172903, 42.795257],\n                        [97.371235, 42.457076],\n                        [97.500582, 42.243894],\n                        [97.653335, 41.986856],\n                        [97.84674, 41.656379],\n                        [97.613915, 41.477276],\n                        [97.629314, 41.440498],\n                        [97.903407, 41.168057],\n                        [97.971776, 41.09774],\n                        [98.142391, 41.001607],\n                        [98.184891, 40.988056],\n                        [98.25018, 40.93925],\n                        [98.333332, 40.918903],\n                        [98.344419, 40.568413],\n                        [98.627751, 40.677884],\n                        [98.569853, 40.746836],\n                        [98.668403, 40.773128],\n                        [98.689345, 40.691952],\n                        [98.72199, 40.657911],\n                        [98.762642, 40.639748],\n                        [98.802678, 40.607043],\n                        [98.80699, 40.660181],\n                        [98.790975, 40.705564],\n                        [98.984996, 40.782644],\n                        [99.041662, 40.693767],\n                        [99.102025, 40.676522],\n                        [99.12543, 40.715091],\n                        [99.172858, 40.747289],\n                        [99.174705, 40.858278],\n                        [99.565827, 40.846961],\n                        [99.673, 40.93292],\n                        [99.985897, 40.909858],\n                        [100.057346, 40.908049],\n                        [100.107853, 40.875475],\n                        [100.224882, 40.727337],\n                        [100.237201, 40.716905],\n                        [100.242744, 40.618855],\n                        [100.169447, 40.541131],\n                        [100.169447, 40.277743],\n                        [100.007455, 40.20008],\n                        [99.955716, 40.150695],\n                        [99.927383, 40.063727],\n                        [99.841152, 40.013326],\n                        [99.751225, 40.006909],\n                        [99.714268, 39.972061],\n                        [99.533182, 39.891753],\n                        [99.491298, 39.884406],\n                        [99.459885, 39.898181],\n                        [99.440791, 39.885783],\n                        [99.469124, 39.875221],\n                        [99.672384, 39.888079],\n                        [99.822058, 39.860063],\n                        [99.904593, 39.785601],\n                        [99.958796, 39.769504],\n                        [100.040716, 39.757083],\n                        [100.128179, 39.702312],\n                        [100.250135, 39.685274],\n                        [100.314193, 39.606935],\n                        [100.301258, 39.572345],\n                        [100.326512, 39.509118],\n                        [100.44354, 39.485565],\n                        [100.500823, 39.481408],\n                        [100.498975, 39.400527],\n                        [100.606764, 39.387577],\n                        [100.707778, 39.404689],\n                        [100.842053, 39.405614],\n                        [100.842669, 39.199999],\n                        [100.864227, 39.106719],\n                        [100.829118, 39.075133],\n                        [100.835278, 39.025869],\n                        [100.875314, 39.002619],\n                        [100.901799, 39.030053],\n                        [100.961545, 39.005874],\n                        [100.969553, 38.946788],\n                        [101.117378, 38.975174],\n                        [101.228863, 39.020754],\n                        [101.198682, 38.943064],\n                        [101.237486, 38.907214],\n                        [101.24303, 38.860628],\n                        [101.33542, 38.847113],\n                        [101.34158, 38.822406],\n                        [101.307087, 38.80282],\n                        [101.331109, 38.777164],\n                        [101.412413, 38.764099],\n                        [101.562702, 38.713218],\n                        [101.601506, 38.65529],\n                        [101.672955, 38.6908],\n                        [101.777049, 38.66043],\n                        [101.873751, 38.733761],\n                        [101.941505, 38.808883],\n                        [102.075164, 38.891378],\n                        [102.045599, 38.904885],\n                        [101.955055, 38.985874],\n                        [101.926106, 39.000758],\n                        [101.833715, 39.08907],\n                        [101.902701, 39.111827],\n                        [102.012338, 39.127149],\n                        [102.050526, 39.141075],\n                        [102.276576, 39.188868],\n                        [102.3548, 39.231993],\n                        [102.45335, 39.255167],\n                        [102.579002, 39.183301],\n                        [102.616574, 39.171703],\n                        [102.883892, 39.120649],\n                        [103.007696, 39.099753],\n                        [103.133347, 39.192579],\n                        [103.188166, 39.215302],\n                        [103.259615, 39.263971],\n                        [103.344615, 39.331588],\n                        [103.428998, 39.353341],\n                        [103.595302, 39.386652],\n                        [103.728961, 39.430117],\n                        [103.85338, 39.461543],\n                        [103.955626, 39.456923],\n                        [104.089901, 39.419947],\n                        [104.073271, 39.351953],\n                        [104.047401, 39.297788],\n                        [104.171205, 39.160567],\n                        [104.207546, 39.083495],\n                        [104.190915, 39.042139],\n                        [104.196459, 38.9882],\n                        [104.173053, 38.94446],\n                        [104.044322, 38.895105],\n                        [104.011677, 38.85923],\n                        [103.85954, 38.64454],\n                        [103.416063, 38.404821],\n                        [103.465339, 38.353215],\n                        [103.507838, 38.280905],\n                        [103.53494, 38.156776],\n                        [103.368636, 38.08898],\n                        [103.362477, 38.037621],\n                        [103.40744, 37.860651],\n                        [103.627947, 37.797783],\n                        [103.683381, 37.777919],\n                        [103.841062, 37.64725],\n                        [103.874938, 37.604117],\n                        [103.935916, 37.572818],\n                        [104.089285, 37.465067],\n                        [104.183524, 37.406618],\n                        [104.237727, 37.411847],\n                        [104.287002, 37.428007],\n                        [104.322726, 37.44844],\n                        [104.407726, 37.464592],\n                        [104.419429, 37.511604],\n                        [104.433595, 37.515402],\n                        [104.623305, 37.522522],\n                        [104.805007, 37.539133],\n                        [104.866601, 37.566651],\n                        [105.027977, 37.580881],\n                        [105.111128, 37.633981],\n                        [105.187505, 37.657674],\n                        [105.221998, 37.677097],\n                        [105.315004, 37.702197],\n                        [105.4037, 37.710246],\n                        [105.467141, 37.695094],\n                        [105.598952, 37.699356],\n                        [105.616199, 37.722555],\n                        [105.622358, 37.777919],\n                        [105.677177, 37.771769],\n                        [105.760944, 37.799674],\n                        [105.80406, 37.862068],\n                        [105.799749, 37.939986],\n                        [105.840401, 38.004147],\n                        [105.780655, 38.084741],\n                        [105.76772, 38.121474],\n                        [105.775111, 38.186887],\n                        [105.802828, 38.220277],\n                        [105.842248, 38.240962],\n                        [105.86627, 38.296406],\n                        [105.821307, 38.366824],\n                        [105.835473, 38.387467],\n                        [105.827466, 38.432486],\n                        [105.850872, 38.443736],\n                        [105.836705, 38.476071],\n                        [105.863806, 38.53508],\n                        [105.856415, 38.569714],\n                        [105.874277, 38.593105],\n                        [105.852719, 38.641735],\n                        [105.894603, 38.696405],\n                        [105.88598, 38.716953],\n                        [105.908154, 38.737496],\n                        [105.909386, 38.791159],\n                        [105.992538, 38.857366],\n                        [105.97098, 38.909077],\n                        [106.021487, 38.953769],\n                        [106.060907, 38.96866],\n                        [106.087392, 39.006339],\n                        [106.078153, 39.026333],\n                        [106.096631, 39.084889],\n                        [106.145907, 39.153142],\n                        [106.170544, 39.163352],\n                        [106.192718, 39.142932],\n                        [106.251232, 39.131327],\n                        [106.285109, 39.146181],\n                        [106.29558, 39.167992],\n                        [106.280181, 39.262118],\n                        [106.402753, 39.291767],\n                        [106.511774, 39.272311],\n                        [106.525325, 39.308439],\n                        [106.556122, 39.322329],\n                        [106.602318, 39.37555],\n                        [106.643586, 39.357969],\n                        [106.683622, 39.357506],\n                        [106.751375, 39.381564],\n                        [106.781556, 39.371849],\n                        [106.806809, 39.318625],\n                        [106.806193, 39.277407],\n                        [106.790795, 39.241263],\n                        [106.795723, 39.214375],\n                        [106.825288, 39.19397],\n                        [106.859164, 39.107648],\n                        [106.878874, 39.091392],\n                        [106.933693, 39.076527],\n                        [106.96757, 39.054688],\n                        [106.971881, 39.026333],\n                        [106.954019, 38.941202],\n                        [106.837606, 38.847579],\n                        [106.756302, 38.748699],\n                        [106.709491, 38.718821],\n                        [106.66268, 38.601524],\n                        [106.647897, 38.470917],\n                        [106.599854, 38.389812],\n                        [106.482209, 38.319417],\n                        [106.555506, 38.263521],\n                        [106.627571, 38.232501],\n                        [106.654672, 38.22921],\n                        [106.737824, 38.197706],\n                        [106.779092, 38.171833],\n                        [106.858548, 38.156306],\n                        [106.942316, 38.132302],\n                        [107.010069, 38.120532],\n                        [107.051337, 38.122886],\n                        [107.071047, 38.138892],\n                        [107.119091, 38.134185],\n                        [107.138801, 38.161011],\n                        [107.19054, 38.153953],\n                        [107.240431, 38.111586],\n                        [107.33159, 38.086625],\n                        [107.3938, 38.014993],\n                        [107.440611, 37.995659],\n                        [107.411662, 37.948009],\n                        [107.448618, 37.933378],\n                        [107.49235, 37.944706],\n                        [107.560719, 37.893717],\n                        [107.65003, 37.86443],\n                        [107.684523, 37.888522],\n                        [107.732566, 37.84931],\n                        [107.842819, 37.828987],\n                        [107.884703, 37.808186],\n                        [107.982022, 37.787378],\n                        [107.993109, 37.735335],\n                        [108.025753, 37.696041],\n                        [108.012819, 37.66857],\n                        [108.025137, 37.649619],\n                        [108.055318, 37.652462],\n                        [108.134159, 37.622131],\n                        [108.193905, 37.638246],\n                        [108.205608, 37.655779],\n                        [108.24626, 37.665728],\n                        [108.293071, 37.656726],\n                        [108.301078, 37.640616],\n                        [108.422418, 37.648672],\n                        [108.485244, 37.678044],\n                        [108.532671, 37.690832],\n                        [108.628142, 37.651988],\n                        [108.699591, 37.669518],\n                        [108.720533, 37.683728],\n                        [108.777815, 37.683728],\n                        [108.791982, 37.700303],\n                        [108.784591, 37.764673],\n                        [108.799989, 37.784068],\n                        [108.791982, 37.872934],\n                        [108.798141, 37.93385],\n                        [108.82709, 37.989056],\n                        [108.797525, 38.04799],\n                        [108.830786, 38.049875],\n                        [108.883141, 38.01405],\n                        [108.893612, 37.978207],\n                        [108.93488, 37.922521],\n                        [108.9743, 37.931962],\n                        [108.982923, 37.964053],\n                        [109.018648, 37.971602],\n                        [109.037742, 38.021593],\n                        [109.06977, 38.023008],\n                        [109.050676, 38.055059],\n                        [109.069155, 38.091336],\n                        [108.964445, 38.154894],\n                        [108.938575, 38.207582],\n                        [108.976148, 38.245192],\n                        [108.961981, 38.26493],\n                        [109.007561, 38.359316],\n                        [109.051292, 38.385122],\n                        [109.054372, 38.433892],\n                        [109.128901, 38.480288],\n                        [109.175712, 38.518694],\n                        [109.196654, 38.552867],\n                        [109.276726, 38.623035],\n                        [109.331545, 38.597783],\n                        [109.367269, 38.627711],\n                        [109.329081, 38.66043],\n                        [109.338936, 38.701542],\n                        [109.404226, 38.720689],\n                        [109.444262, 38.782763],\n                        [109.511399, 38.833595],\n                        [109.549587, 38.805618],\n                        [109.624116, 38.85457],\n                        [109.672159, 38.928167],\n                        [109.685094, 38.968195],\n                        [109.665384, 38.981687],\n                        [109.72513, 39.018429],\n                        [109.762086, 39.057476],\n                        [109.793499, 39.074204],\n                        [109.851397, 39.122971],\n                        [109.890818, 39.103932],\n                        [109.92223, 39.107183],\n                        [109.893897, 39.141075],\n                        [109.961035, 39.191651],\n                        [109.871723, 39.243581],\n                        [109.90252, 39.271848],\n                        [109.962267, 39.212056],\n                        [110.041107, 39.21623],\n                        [110.109476, 39.249606],\n                        [110.217881, 39.281113],\n                        [110.184005, 39.355192],\n                        [110.161831, 39.387115],\n                        [110.136577, 39.39174],\n                        [110.12549, 39.432891],\n                        [110.152592, 39.45415],\n                        [110.243751, 39.423645],\n                        [110.257917, 39.407001],\n                        [110.385417, 39.310291],\n                        [110.429764, 39.341308],\n                        [110.434692, 39.381101],\n                        [110.482735, 39.360745],\n                        [110.524003, 39.382952],\n                        [110.559728, 39.351027],\n                        [110.566503, 39.320014],\n                        [110.596684, 39.282966],\n                        [110.626249, 39.266751],\n                        [110.702626, 39.273701],\n                        [110.731575, 39.30705],\n                        [110.73835, 39.348713],\n                        [110.782698, 39.38804],\n                        [110.869545, 39.494341],\n                        [110.891103, 39.509118],\n                        [110.958856, 39.519275],\n                        [111.017371, 39.552045],\n                        [111.101138, 39.559428],\n                        [111.136863, 39.587106],\n                        [111.154725, 39.569116],\n                        [111.148566, 39.531277],\n                        [111.10545, 39.497573],\n                        [111.10545, 39.472631],\n                        [111.058639, 39.447681],\n                        [111.064182, 39.400989],\n                        [111.098059, 39.401914],\n                        [111.087588, 39.376013],\n                        [111.125776, 39.366297],\n                        [111.143022, 39.407926],\n                        [111.171971, 39.423183],\n                        [111.337043, 39.420872],\n                        [111.358601, 39.432428],\n                        [111.372152, 39.479099],\n                        [111.431282, 39.508656],\n                        [111.422043, 39.539123],\n                        [111.441137, 39.59679],\n                        [111.460847, 39.606935],\n                        [111.445448, 39.640124],\n                        [111.497187, 39.661781],\n                        [111.525521, 39.662242],\n                        [111.625303, 39.633672],\n                        [111.659179, 39.641507],\n                        [111.722621, 39.606013],\n                        [111.783599, 39.58895],\n                        [111.842729, 39.620305],\n                        [111.87907, 39.606013],\n                        [111.9382, 39.623071],\n                        [111.925265, 39.66731],\n                        [111.959758, 39.692642],\n                        [111.970229, 39.796638],\n                        [112.012729, 39.827438],\n                        [112.042294, 39.886243],\n                        [112.07617, 39.919298],\n                        [112.133453, 40.001866],\n                        [112.142076, 40.027076],\n                        [112.182112, 40.061437],\n                        [112.183344, 40.083877],\n                        [112.232003, 40.133311],\n                        [112.232619, 40.169905],\n                        [112.299756, 40.21105],\n                        [112.310227, 40.256281],\n                        [112.349031, 40.257194],\n                        [112.418017, 40.295091],\n                        [112.456205, 40.300112],\n                        [112.511639, 40.269068],\n                        [112.6299, 40.235725],\n                        [112.712436, 40.178593],\n                        [112.744464, 40.167161],\n                        [112.848558, 40.206937],\n                        [112.898449, 40.329317],\n                        [113.03334, 40.368997],\n                        [113.083231, 40.374925],\n                        [113.251382, 40.413211],\n                        [113.27602, 40.388601],\n                        [113.316672, 40.319736],\n                        [113.387505, 40.319279],\n                        [113.500222, 40.334335],\n                        [113.559968, 40.348476],\n                        [113.688699, 40.448288],\n                        [113.763228, 40.473787],\n                        [113.794641, 40.517932],\n                        [113.850691, 40.460583],\n                        [113.890112, 40.466503],\n                        [113.948626, 40.514747],\n                        [114.011452, 40.515657],\n                        [114.061959, 40.52885],\n                        [114.080437, 40.547952],\n                        [114.076741, 40.575686],\n                        [114.041633, 40.608861],\n                        [114.07243, 40.679246],\n                        [114.063806, 40.706925],\n                        [114.084748, 40.729605],\n                        [114.134639, 40.737314],\n                        [114.103227, 40.770861],\n                        [114.104458, 40.797597],\n                        [114.080437, 40.790348],\n                        [114.044712, 40.830661],\n                        [114.073661, 40.857372],\n                        [114.055183, 40.867782],\n                        [114.041633, 40.917546],\n                        [114.057647, 40.925234],\n                        [113.994821, 40.938798],\n                        [113.973263, 40.983087],\n                        [113.868554, 41.06887],\n                        [113.819279, 41.09774],\n                        [113.877793, 41.115777],\n                        [113.920293, 41.172112],\n                        [113.960945, 41.171211],\n                        [113.996669, 41.19238],\n                        [114.016379, 41.231999],\n                        [113.992357, 41.269794],\n                        [113.971416, 41.239649],\n                        [113.95109, 41.282837],\n                        [113.914749, 41.294529],\n                        [113.899351, 41.316108],\n                        [113.92522, 41.325546],\n                        [113.94493, 41.392477],\n                        [113.871017, 41.413126],\n                        [113.877793, 41.431076],\n                        [113.919677, 41.454404],\n                        [113.933227, 41.487139],\n                        [113.953553, 41.483553],\n                        [113.976959, 41.505966],\n                        [114.032394, 41.529715],\n                        [114.101379, 41.537779],\n                        [114.230726, 41.513584],\n                        [114.221487, 41.582111],\n                        [114.226414, 41.616572],\n                        [114.259059, 41.623282],\n                        [114.215328, 41.68499],\n                        [114.237501, 41.698843],\n                        [114.206704, 41.7386],\n                        [114.215328, 41.75646],\n                        [114.200545, 41.789934],\n                        [114.282465, 41.863517],\n                        [114.343443, 41.926774],\n                        [114.352066, 41.953484],\n                        [114.419203, 41.942356],\n                        [114.478334, 41.951704],\n                        [114.511594, 41.981962],\n                        [114.467863, 42.025989],\n                        [114.480181, 42.064654],\n                        [114.502355, 42.06732],\n                        [114.510978, 42.110844],\n                        [114.560254, 42.132595],\n                        [114.647717, 42.109512],\n                        [114.675434, 42.12061],\n                        [114.75489, 42.115727],\n                        [114.789383, 42.130819],\n                        [114.79431, 42.149457],\n                        [114.825723, 42.139695],\n                        [114.86268, 42.097967],\n                        [114.860832, 42.054879],\n                        [114.9021, 42.015763],\n                        [114.915035, 41.960605],\n                        [114.923658, 41.871093],\n                        [114.939056, 41.846132],\n                        [114.922426, 41.825175],\n                        [114.868839, 41.813579],\n                        [114.89594, 41.76762],\n                        [114.902716, 41.695715],\n                        [114.895325, 41.636255],\n                        [114.860832, 41.60091],\n                        [115.016049, 41.615229],\n                        [115.056085, 41.602253],\n                        [115.0992, 41.62373],\n                        [115.195287, 41.602253],\n                        [115.20391, 41.571367],\n                        [115.256881, 41.580768],\n                        [115.26612, 41.616124],\n                        [115.290142, 41.622835],\n                        [115.310468, 41.592854],\n                        [115.377605, 41.603148],\n                        [115.345576, 41.635807],\n                        [115.360975, 41.661297],\n                        [115.319091, 41.691693],\n                        [115.346808, 41.712247],\n                        [115.42996, 41.728775],\n                        [115.488474, 41.760924],\n                        [115.519887, 41.76762],\n                        [115.57409, 41.80555],\n                        [115.654162, 41.829189],\n                        [115.688038, 41.867528],\n                        [115.726227, 41.870202],\n                        [115.811226, 41.912525],\n                        [115.834632, 41.93835],\n                        [115.85311, 41.927665],\n                        [115.916552, 41.945027],\n                        [115.954124, 41.874213],\n                        [115.994776, 41.828743],\n                        [116.007095, 41.797966],\n                        [116.007095, 41.79752],\n                        [116.034196, 41.782795],\n                        [116.09887, 41.776547],\n                        [116.129051, 41.805996],\n                        [116.106877, 41.831419],\n                        [116.122892, 41.861734],\n                        [116.194341, 41.861734],\n                        [116.212819, 41.885352],\n                        [116.223906, 41.932562],\n                        [116.298434, 41.96817],\n                        [116.310137, 41.997086],\n                        [116.373579, 42.009983],\n                        [116.414231, 41.982407],\n                        [116.393289, 41.942802],\n                        [116.453651, 41.945917],\n                        [116.4826, 41.975734],\n                        [116.510933, 41.974399],\n                        [116.553433, 41.928555],\n                        [116.597165, 41.935679],\n                        [116.639049, 41.929891],\n                        [116.66923, 41.947698],\n                        [116.727744, 41.951259],\n                        [116.748686, 41.984186],\n                        [116.796113, 41.977958],\n                        [116.879881, 42.018431],\n                        [116.890352, 42.092639],\n                        [116.850316, 42.156556],\n                        [116.825062, 42.155669],\n                        [116.789338, 42.200462],\n                        [116.903287, 42.190708],\n                        [116.918685, 42.229716],\n                        [116.897743, 42.297479],\n                        [116.886656, 42.366496],\n                        [116.910678, 42.394789],\n                        [116.910062, 42.395231],\n                        [116.921765, 42.403628],\n                        [116.936547, 42.410256],\n                        [116.944555, 42.415116],\n                        [116.97104, 42.427486],\n                        [116.974736, 42.426603],\n                        [116.99075, 42.425719],\n                        [117.005533, 42.43367],\n                        [117.009228, 42.44957],\n                        [117.01662, 42.456193],\n                        [117.080061, 42.463699],\n                        [117.09546, 42.484004],\n                        [117.135496, 42.468996],\n                        [117.188467, 42.468114],\n                        [117.275314, 42.481797],\n                        [117.332596, 42.46105],\n                        [117.390495, 42.461933],\n                        [117.413284, 42.471645],\n                        [117.410205, 42.519743],\n                        [117.387415, 42.517537],\n                        [117.434226, 42.557224],\n                        [117.435458, 42.585431],\n                        [117.475494, 42.602613],\n                        [117.530313, 42.590278],\n                        [117.537088, 42.603054],\n                        [117.60053, 42.603054],\n                        [117.667051, 42.582347],\n                        [117.708935, 42.588515],\n                        [117.779768, 42.61847],\n                        [117.801326, 42.612744],\n                        [117.797631, 42.585431],\n                        [117.856761, 42.539148],\n                        [117.874007, 42.510038],\n                        [117.997811, 42.416884],\n                        [118.024296, 42.385064],\n                        [118.008898, 42.346595],\n                        [118.060021, 42.298364],\n                        [118.047702, 42.280656],\n                        [117.974405, 42.25054],\n                        [117.977485, 42.229716],\n                        [118.033535, 42.199132],\n                        [118.106216, 42.172082],\n                        [118.089586, 42.12283],\n                        [118.097593, 42.105072],\n                        [118.155491, 42.081091],\n                        [118.116687, 42.037102],\n                        [118.194296, 42.031324],\n                        [118.220165, 42.058434],\n                        [118.212774, 42.081091],\n                        [118.239259, 42.092639],\n                        [118.27252, 42.083312],\n                        [118.296541, 42.057545],\n                        [118.286686, 42.033991],\n                        [118.239875, 42.024655],\n                        [118.291614, 42.007759],\n                        [118.313788, 41.98819],\n                        [118.306396, 41.940131],\n                        [118.268824, 41.930336],\n                        [118.340273, 41.87243],\n                        [118.335346, 41.845241],\n                        [118.29223, 41.772976],\n                        [118.247266, 41.773869],\n                        [118.236179, 41.80778],\n                        [118.178281, 41.814917],\n                        [118.140093, 41.784134],\n                        [118.132702, 41.733241],\n                        [118.155491, 41.712694],\n                        [118.159187, 41.67605],\n                        [118.206614, 41.650566],\n                        [118.215237, 41.59554],\n                        [118.302701, 41.55256],\n                        [118.315636, 41.512688],\n                        [118.271904, 41.471446],\n                        [118.327338, 41.450816],\n                        [118.348896, 41.428384],\n                        [118.361215, 41.384844],\n                        [118.348896, 41.342622],\n                        [118.380309, 41.312062],\n                        [118.412338, 41.331838],\n                        [118.528135, 41.355202],\n                        [118.629765, 41.346666],\n                        [118.677192, 41.35026],\n                        [118.741866, 41.324198],\n                        [118.770199, 41.352956],\n                        [118.843496, 41.374516],\n                        [118.844727, 41.342622],\n                        [118.890923, 41.300823],\n                        [118.949437, 41.317906],\n                        [118.980234, 41.305769],\n                        [119.092951, 41.293629],\n                        [119.168712, 41.294978],\n                        [119.197661, 41.282837],\n                        [119.211827, 41.308016],\n                        [119.239545, 41.31431],\n                        [119.296211, 41.325097],\n                        [119.330704, 41.385293],\n                        [119.309762, 41.405944],\n                        [119.376283, 41.422102],\n                        [119.378131, 41.459787],\n                        [119.401537, 41.472343],\n                        [119.406464, 41.503276],\n                        [119.361501, 41.545841],\n                        [119.362116, 41.566442],\n                        [119.420015, 41.567785],\n                        [119.415703, 41.590169],\n                        [119.342406, 41.617914],\n                        [119.307914, 41.657273],\n                        [119.299907, 41.705545],\n                        [119.319001, 41.727435],\n                        [119.317769, 41.764049],\n                        [119.292515, 41.790827],\n                        [119.312841, 41.80555],\n                        [119.334399, 41.871539],\n                        [119.323312, 41.889807],\n                        [119.340559, 41.926774],\n                        [119.323928, 41.937014],\n                        [119.324544, 41.969505],\n                        [119.375667, 42.023322],\n                        [119.384906, 42.08953],\n                        [119.352261, 42.118391],\n                        [119.314689, 42.119723],\n                        [119.30853, 42.147239],\n                        [119.286972, 42.154781],\n                        [119.277733, 42.185387],\n                        [119.237697, 42.200905],\n                        [119.274037, 42.239021],\n                        [119.280197, 42.260728],\n                        [119.34795, 42.300578],\n                        [119.432949, 42.317396],\n                        [119.482841, 42.347037],\n                        [119.502551, 42.388159],\n                        [119.540123, 42.363401],\n                        [119.572152, 42.359421],\n                        [119.571536, 42.335536],\n                        [119.539507, 42.297922],\n                        [119.557985, 42.289068],\n                        [119.609108, 42.276671],\n                        [119.617115, 42.252755],\n                        [119.679941, 42.240793],\n                        [119.744615, 42.211545],\n                        [119.841933, 42.215534],\n                        [119.854868, 42.170308],\n                        [119.837622, 42.135257],\n                        [119.845629, 42.097079],\n                        [119.87581, 42.077982],\n                        [119.897368, 42.030879],\n                        [119.921389, 42.014429],\n                        [119.924469, 41.98908],\n                        [119.950954, 41.974399],\n                        [119.954034, 41.923212],\n                        [119.989759, 41.899163],\n                        [120.023019, 41.816701],\n                        [120.041498, 41.818932],\n                        [120.050737, 41.776101],\n                        [120.024867, 41.737707],\n                        [120.035954, 41.708226],\n                        [120.096316, 41.697056],\n                        [120.1382, 41.729221],\n                        [120.127113, 41.77253],\n                        [120.183164, 41.826513],\n                        [120.188707, 41.848361],\n                        [120.215808, 41.853265],\n                        [120.251533, 41.884016],\n                        [120.286641, 41.880005],\n                        [120.290337, 41.897381],\n                        [120.260156, 41.904062],\n                        [120.271859, 41.925439],\n                        [120.318054, 41.93746],\n                        [120.309431, 41.951704],\n                        [120.373489, 41.994862],\n                        [120.399358, 41.984631],\n                        [120.456641, 42.016208],\n                        [120.450481, 42.057101],\n                        [120.493597, 42.073539],\n                        [120.466496, 42.105516],\n                        [120.56751, 42.152119],\n                        [120.58414, 42.167203],\n                        [120.624792, 42.154338],\n                        [120.72211, 42.203565],\n                        [120.745516, 42.223512],\n                        [120.79048, 42.218636],\n                        [120.820661, 42.227943],\n                        [120.8299, 42.252755],\n                        [120.883487, 42.242565],\n                        [120.883487, 42.269585],\n                        [120.933994, 42.27977],\n                        [120.992508, 42.264714],\n                        [121.028848, 42.242565],\n                        [121.070732, 42.254083],\n                        [121.087978, 42.278885],\n                        [121.120623, 42.280656],\n                        [121.133558, 42.300135],\n                        [121.184681, 42.333324],\n                        [121.218558, 42.371802],\n                        [121.285079, 42.387717],\n                        [121.314644, 42.42837],\n                        [121.304789, 42.435879],\n                        [121.386093, 42.474294],\n                        [121.434752, 42.475176],\n                        [121.4791, 42.49636],\n                        [121.506201, 42.482239],\n                        [121.570875, 42.487093],\n                        [121.607831, 42.516214],\n                        [121.604136, 42.495037],\n                        [121.66573, 42.437204],\n                        [121.69899, 42.438529],\n                        [121.747649, 42.484887],\n                        [121.803084, 42.514891],\n                        [121.817867, 42.504303],\n                        [121.831417, 42.533856],\n                        [121.844352, 42.522389],\n                        [121.889931, 42.556784],\n                        [121.921344, 42.605697],\n                        [121.915801, 42.656332],\n                        [121.94167, 42.666014],\n                        [121.939207, 42.688453],\n                        [122.018663, 42.69901],\n                        [122.062394, 42.723635],\n                        [122.072865, 42.710444],\n                        [122.160945, 42.684934],\n                        [122.204676, 42.685374],\n                        [122.204676, 42.732867],\n                        [122.261343, 42.695931],\n                        [122.324785, 42.684934],\n                        [122.338951, 42.669975],\n                        [122.396234, 42.684054],\n                        [122.396234, 42.707366],\n                        [122.460907, 42.755282],\n                        [122.439349, 42.770221],\n                        [122.371596, 42.776371],\n                        [122.35127, 42.830378],\n                        [122.436886, 42.843105],\n                        [122.556378, 42.827745],\n                        [122.576088, 42.819405],\n                        [122.580399, 42.789987],\n                        [122.624747, 42.773296],\n                        [122.653696, 42.78252],\n                        [122.733152, 42.786034],\n                        [122.73808, 42.77066],\n                        [122.786123, 42.757479],\n                        [122.848949, 42.712203],\n                        [122.883442, 42.751766],\n                        [122.887137, 42.770221],\n                        [122.925941, 42.772417],\n                        [122.945651, 42.753524],\n                        [122.980144, 42.777689],\n                        [123.058368, 42.768903],\n                        [123.118114, 42.801405],\n                        [123.227752, 42.831695],\n                        [123.169853, 42.859777],\n                        [123.188947, 42.895739],\n                        [123.18402, 42.925983],\n                        [123.259165, 42.993431],\n                        [123.323222, 43.000872],\n                        [123.434707, 43.027565],\n                        [123.474743, 43.042438],\n                        [123.536337, 43.007],\n                        [123.572678, 43.003498],\n                        [123.580685, 43.036314],\n                        [123.631192, 43.088346],\n                        [123.636119, 43.141644],\n                        [123.666916, 43.179623],\n                        [123.645974, 43.208855],\n                        [123.676771, 43.223684],\n                        [123.664453, 43.264663],\n                        [123.698329, 43.272071],\n                        [123.703873, 43.37047],\n                        [123.608402, 43.366119],\n                        [123.54496, 43.415262],\n                        [123.519707, 43.402219],\n                        [123.486446, 43.44525],\n                        [123.442098, 43.437863],\n                        [123.419925, 43.410046],\n                        [123.382968, 43.469143],\n                        [123.36449, 43.483475],\n                        [123.315831, 43.492159],\n                        [123.329998, 43.519071],\n                        [123.304744, 43.550742],\n                        [123.360179, 43.567223],\n                        [123.452569, 43.545971],\n                        [123.461193, 43.568523],\n                        [123.434091, 43.575461],\n                        [123.421157, 43.598435],\n                        [123.5117, 43.592801],\n                        [123.510468, 43.624867],\n                        [123.536953, 43.633964],\n                        [123.518475, 43.682024],\n                        [123.520323, 43.708419],\n                        [123.48275, 43.737396],\n                        [123.498149, 43.771114],\n                        [123.461809, 43.822518],\n                        [123.467968, 43.853599],\n                        [123.397135, 43.954929],\n                        [123.37065, 43.970006],\n                        [123.400831, 43.979481],\n                        [123.365722, 44.013922],\n                        [123.331229, 44.028984],\n                        [123.32815, 44.084035],\n                        [123.350939, 44.092633],\n                        [123.362642, 44.133452],\n                        [123.386664, 44.161794],\n                        [123.323838, 44.179823],\n                        [123.286882, 44.211574],\n                        [123.277027, 44.25274],\n                        [123.196955, 44.34483],\n                        [123.128585, 44.367081],\n                        [123.114419, 44.40258],\n                        [123.142136, 44.428228],\n                        [123.125506, 44.455147],\n                        [123.137209, 44.486322],\n                        [123.12489, 44.5098],\n                        [123.06576, 44.505959],\n                        [123.025108, 44.493153],\n                        [122.85634, 44.398304],\n                        [122.76087, 44.369648],\n                        [122.702971, 44.319145],\n                        [122.675254, 44.285738],\n                        [122.641993, 44.283595],\n                        [122.515726, 44.251025],\n                        [122.483081, 44.236877],\n                        [122.319241, 44.233018],\n                        [122.271198, 44.255741],\n                        [122.291524, 44.310152],\n                        [122.294604, 44.41113],\n                        [122.28598, 44.477783],\n                        [122.228082, 44.480345],\n                        [122.224386, 44.526016],\n                        [122.196053, 44.559712],\n                        [122.13138, 44.577619],\n                        [122.113517, 44.615546],\n                        [122.103046, 44.67388],\n                        [122.117213, 44.701961],\n                        [122.161561, 44.728328],\n                        [122.152322, 44.744057],\n                        [122.10243, 44.736406],\n                        [122.110438, 44.767856],\n                        [122.142467, 44.753833],\n                        [122.168952, 44.770405],\n                        [122.099967, 44.7823],\n                        [122.098119, 44.81882],\n                        [122.04946, 44.912985],\n                        [122.079025, 44.914256],\n                        [122.087032, 44.95281],\n                        [122.074713, 45.006573],\n                        [122.098735, 45.02138],\n                        [122.119677, 45.068739],\n                        [122.109822, 45.142236],\n                        [122.143082, 45.183167],\n                        [122.192358, 45.180636],\n                        [122.22993, 45.206784],\n                        [122.239169, 45.276313],\n                        [122.147394, 45.295682],\n                        [122.146778, 45.374352],\n                        [122.180039, 45.409655],\n                        [122.168336, 45.439897],\n                        [122.064242, 45.472641],\n                        [122.002648, 45.507882],\n                        [121.993409, 45.552741],\n                        [121.966308, 45.596308],\n                        [121.995873, 45.59882],\n                        [122.003264, 45.623102],\n                        [121.970004, 45.692956],\n                        [121.934279, 45.71051],\n                        [121.867142, 45.719703],\n                        [121.812323, 45.704659],\n                        [121.811091, 45.687103],\n                        [121.713773, 45.701734],\n                        [121.666345, 45.727641],\n                        [121.644172, 45.752284],\n                        [121.657106, 45.770238],\n                        [121.697142, 45.76314],\n                        [121.754425, 45.794862],\n                        [121.766744, 45.830318],\n                        [121.769823, 45.84366],\n                        [121.817251, 45.875336],\n                        [121.805548, 45.900746],\n                        [121.821562, 45.918235],\n                        [121.809243, 45.961102],\n                        [121.761816, 45.998947],\n                        [121.819098, 46.023054],\n                        [121.843736, 46.024301],\n                        [121.864062, 46.002272],\n                        [121.923808, 46.004767],\n                        [121.92812, 45.988552],\n                        [122.040221, 45.959022],\n                        [122.085184, 45.912406],\n                        [122.091344, 45.882002],\n                        [122.200981, 45.857],\n                        [122.236705, 45.831569],\n                        [122.253952, 45.7982],\n                        [122.301379, 45.813218],\n                        [122.337719, 45.859917],\n                        [122.372828, 45.856166],\n                        [122.362357, 45.917403],\n                        [122.446125, 45.916986],\n                        [122.496016, 45.85825],\n                        [122.504639, 45.786933],\n                        [122.522501, 45.786933],\n                        [122.556378, 45.82156],\n                        [122.603189, 45.778169],\n                        [122.640761, 45.771072],\n                        [122.650001, 45.731401],\n                        [122.671558, 45.70048],\n                        [122.741775, 45.705077],\n                        [122.751015, 45.735996],\n                        [122.792283, 45.766063],\n                        [122.752246, 45.834905],\n                        [122.772572, 45.856583],\n                        [122.80029, 45.856583],\n                        [122.828623, 45.912406],\n                        [122.792898, 46.073313],\n                        [123.04605, 46.099878],\n                        [123.070071, 46.123527],\n                        [123.112571, 46.130163],\n                        [123.102716, 46.172037],\n                        [123.127354, 46.174523],\n                        [123.128585, 46.210565],\n                        [123.178476, 46.248239],\n                        [123.142136, 46.298293],\n                        [123.089781, 46.347888],\n                        [123.011557, 46.434984],\n                        [123.010325, 46.524823],\n                        [123.002318, 46.574624],\n                        [123.052825, 46.579972],\n                        [123.04605, 46.617803],\n                        [123.077462, 46.622324],\n                        [123.098404, 46.603002],\n                        [123.18094, 46.614103],\n                        [123.228368, 46.588198],\n                        [123.279491, 46.616981],\n                        [123.276411, 46.660947],\n                        [123.318295, 46.662179],\n                        [123.366338, 46.677784],\n                        [123.474743, 46.686817],\n                        [123.603475, 46.68928],\n                        [123.631808, 46.728675],\n                        [123.629344, 46.813524],\n                        [123.580069, 46.827447],\n                        [123.625648, 46.847508],\n                        [123.599163, 46.868378],\n                        [123.605322, 46.891286],\n                        [123.576989, 46.891286],\n                        [123.575757, 46.845461],\n                        [123.562823, 46.82581],\n                        [123.506772, 46.827038],\n                        [123.483366, 46.84587],\n                        [123.52833, 46.944836],\n                        [123.487678, 46.959951],\n                        [123.42362, 46.934212],\n                        [123.337389, 46.988943],\n                        [123.301664, 46.999965],\n                        [123.304128, 46.964852],\n                        [123.360179, 46.970978],\n                        [123.404526, 46.935438],\n                        [123.40699, 46.906416],\n                        [123.374345, 46.837683],\n                        [123.341084, 46.826628],\n                        [123.295505, 46.865105],\n                        [123.221592, 46.850373],\n                        [123.22344, 46.821305],\n                        [123.198802, 46.803283],\n                        [123.163694, 46.74016],\n                        [123.103332, 46.734828],\n                        [123.076846, 46.745082],\n                        [123.026339, 46.718829],\n                        [123.00355, 46.730726],\n                        [122.996774, 46.761483],\n                        [122.906847, 46.80738],\n                        [122.893913, 46.895376],\n                        [122.895144, 46.960359],\n                        [122.83971, 46.937072],\n                        [122.791051, 46.941567],\n                        [122.798442, 46.9575],\n                        [122.77442, 46.973837],\n                        [122.778116, 47.002822],\n                        [122.845869, 47.046881],\n                        [122.852645, 47.072158],\n                        [122.821232, 47.065636],\n                        [122.710363, 47.093349],\n                        [122.679566, 47.094164],\n                        [122.615508, 47.124306],\n                        [122.582863, 47.158092],\n                        [122.531124, 47.198771],\n                        [122.498479, 47.255262],\n                        [122.462755, 47.27841],\n                        [122.441197, 47.310476],\n                        [122.418407, 47.350632],\n                        [122.507103, 47.401291],\n                        [122.543443, 47.495589],\n                        [122.59395, 47.54732],\n                        [122.765181, 47.614333],\n                        [122.848949, 47.67441],\n                        [122.926557, 47.697777],\n                        [123.041122, 47.746492],\n                        [123.161846, 47.781892],\n                        [123.214201, 47.824502],\n                        [123.256085, 47.876711],\n                        [123.300432, 47.953723],\n                        [123.537569, 48.021816],\n                        [123.579453, 48.045427],\n                        [123.705105, 48.152142],\n                        [123.746373, 48.197638],\n                        [123.862785, 48.271782],\n                        [124.019234, 48.39313],\n                        [124.07898, 48.43603],\n                        [124.136878, 48.463023],\n                        [124.25945, 48.536385],\n                        [124.314269, 48.503881],\n                        [124.302566, 48.456673],\n                        [124.330283, 48.435633],\n                        [124.309957, 48.413393],\n                        [124.331515, 48.380015],\n                        [124.317964, 48.35099],\n                        [124.353689, 48.315978],\n                        [124.365392, 48.283731],\n                        [124.422058, 48.245884],\n                        [124.412819, 48.219175],\n                        [124.418978, 48.181679],\n                        [124.475029, 48.173698],\n                        [124.471333, 48.133373],\n                        [124.430065, 48.12099],\n                        [124.415899, 48.08782],\n                        [124.46579, 48.098213],\n                        [124.478108, 48.123387],\n                        [124.505826, 48.124985],\n                        [124.529847, 48.146951],\n                        [124.512601, 48.164518],\n                        [124.547094, 48.200829],\n                        [124.579122, 48.262221],\n                        [124.558796, 48.268197],\n                        [124.579738, 48.297269],\n                        [124.540934, 48.335476],\n                        [124.547094, 48.35775],\n                        [124.51876, 48.378027],\n                        [124.52492, 48.426897],\n                        [124.507674, 48.445558],\n                        [124.555717, 48.467784],\n                        [124.533543, 48.515379],\n                        [124.548941, 48.535593],\n                        [124.520608, 48.556195],\n                        [124.579122, 48.596582],\n                        [124.601912, 48.632587],\n                        [124.624702, 48.701755],\n                        [124.612383, 48.747945],\n                        [124.656115, 48.783842],\n                        [124.644412, 48.80789],\n                        [124.654267, 48.83429],\n                        [124.697383, 48.841775],\n                        [124.715861, 48.885475],\n                        [124.709086, 48.920487],\n                        [124.744194, 48.920487],\n                        [124.756513, 48.967262],\n                        [124.808252, 49.020666],\n                        [124.828578, 49.077933],\n                        [124.809484, 49.115943],\n                        [124.847672, 49.129651],\n                        [124.860607, 49.166448],\n                        [124.906802, 49.184054],\n                        [124.983179, 49.162535],\n                        [125.039845, 49.17623],\n                        [125.034302, 49.157056],\n                        [125.117453, 49.126127],\n                        [125.158721, 49.144921],\n                        [125.187671, 49.186792],\n                        [125.219699, 49.189139],\n                        [125.227707, 49.248947],\n                        [125.214772, 49.277066],\n                        [125.261583, 49.322336],\n                        [125.256656, 49.359769],\n                        [125.277598, 49.379644],\n                        [125.25604, 49.395227],\n                        [125.256656, 49.437275],\n                        [125.270822, 49.454395],\n                        [125.228323, 49.487063],\n                        [125.211076, 49.539908],\n                        [125.233866, 49.536801],\n                        [125.23017, 49.595411],\n                        [125.205533, 49.593859],\n                        [125.16796, 49.629923],\n                        [125.15441, 49.616741],\n                        [125.127308, 49.655113],\n                        [125.132236, 49.672157],\n                        [125.164881, 49.669446],\n                        [125.189518, 49.652401],\n                        [125.185207, 49.634574],\n                        [125.219699, 49.669058],\n                        [125.225243, 49.726349],\n                        [125.204301, 49.734086],\n                        [125.221547, 49.754969],\n                        [125.222779, 49.799026],\n                        [125.177815, 49.829533],\n                        [125.239409, 49.844587],\n                        [125.225243, 49.867351],\n                        [125.245569, 49.87198],\n                        [125.212924, 49.907452],\n                        [125.225859, 49.922481],\n                        [125.199373, 49.935194],\n                        [125.190134, 49.959841],\n                        [125.231402, 49.957531],\n                        [125.241873, 49.987938],\n                        [125.278214, 49.996402],\n                        [125.297924, 50.014481],\n                        [125.283757, 50.036012],\n                        [125.25296, 50.041393],\n                        [125.289916, 50.057917],\n                        [125.315786, 50.04562],\n                        [125.328105, 50.065985],\n                        [125.283757, 50.070211],\n                        [125.287453, 50.093636],\n                        [125.258504, 50.103618],\n                        [125.27883, 50.127411],\n                        [125.311474, 50.140453],\n                        [125.376148, 50.137385],\n                        [125.335496, 50.161161],\n                        [125.382923, 50.172278],\n                        [125.39093, 50.199868],\n                        [125.417416, 50.195654],\n                        [125.448829, 50.216338],\n                        [125.442053, 50.260357],\n                        [125.466075, 50.266861],\n                        [125.463611, 50.295925],\n                        [125.530749, 50.331085],\n                        [125.520278, 50.3498],\n                        [125.546763, 50.358965],\n                        [125.522126, 50.404759],\n                        [125.536292, 50.420014],\n                        [125.567089, 50.402852],\n                        [125.583104, 50.409717],\n                        [125.562162, 50.438314],\n                        [125.580024, 50.449366],\n                        [125.627451, 50.443268],\n                        [125.654553, 50.471082],\n                        [125.699516, 50.487078],\n                        [125.740784, 50.523237],\n                        [125.754335, 50.506874],\n                        [125.770349, 50.531227],\n                        [125.794987, 50.532748],\n                        [125.829479, 50.56165],\n                        [125.807921, 50.60383],\n                        [125.814697, 50.62092],\n                        [125.793139, 50.643316],\n                        [125.804226, 50.658874],\n                        [125.789443, 50.679735],\n                        [125.825784, 50.70362],\n                        [125.78082, 50.725598],\n                        [125.795603, 50.738856],\n                        [125.758646, 50.746809],\n                        [125.804226, 50.773309],\n                        [125.828863, 50.756654],\n                        [125.846726, 50.769524],\n                        [125.836255, 50.793363],\n                        [125.890457, 50.805845],\n                        [125.878138, 50.816812],\n                        [125.913247, 50.825885],\n                        [125.939732, 50.85423],\n                        [125.961906, 50.901054],\n                        [125.997631, 50.872738],\n                        [125.996399, 50.906715],\n                        [126.02042, 50.927466],\n                        [126.042594, 50.92558],\n                        [126.068464, 50.967434],\n                        [126.041978, 50.981753],\n                        [126.033971, 51.011132],\n                        [126.059225, 51.043503],\n                        [125.976073, 51.084498],\n                        [125.993935, 51.119072],\n                        [125.970529, 51.123955],\n                        [125.946508, 51.108176],\n                        [125.909551, 51.138977],\n                        [125.864588, 51.146487],\n                        [125.850421, 51.21364],\n                        [125.819008, 51.227134],\n                        [125.761726, 51.226385],\n                        [125.76111, 51.261976],\n                        [125.740784, 51.27583],\n                        [125.700132, 51.327465],\n                        [125.626219, 51.380163],\n                        [125.623756, 51.387633],\n                        [125.62314, 51.398089],\n                        [125.600966, 51.410409],\n                        [125.60035, 51.413396],\n                        [125.595422, 51.416755],\n                        [125.559082, 51.461521],\n                        [125.528285, 51.488359],\n                        [125.424807, 51.562827],\n                        [125.38046, 51.585516],\n                        [125.35151, 51.623801],\n                        [125.316402, 51.610052],\n                        [125.289301, 51.633831],\n                        [125.228938, 51.640517],\n                        [125.214772, 51.627888],\n                        [125.175968, 51.639403],\n                        [125.130388, 51.635317],\n                        [125.12854, 51.659083],\n                        [125.098975, 51.658341],\n                        [125.060171, 51.59667],\n                        [125.073106, 51.553526],\n                        [125.047236, 51.529704],\n                        [125.004737, 51.529332],\n                        [124.983795, 51.508478],\n                        [124.928976, 51.498419],\n                        [124.917889, 51.474196],\n                        [124.942527, 51.447349],\n                        [124.885244, 51.40817],\n                        [124.864302, 51.37979],\n                        [124.783614, 51.392115],\n                        [124.76452, 51.38726],\n                        [124.752817, 51.35812],\n                        [124.693687, 51.3327],\n                        [124.62655, 51.327465],\n                        [124.58713, 51.363725],\n                        [124.555717, 51.375307],\n                        [124.490427, 51.380537],\n                        [124.478108, 51.36223],\n                        [124.443616, 51.35812],\n                        [124.426985, 51.331953],\n                        [124.430065, 51.301281],\n                        [124.406659, 51.272086],\n                        [124.339522, 51.293422],\n                        [124.297638, 51.298661],\n                        [124.271769, 51.308389],\n                        [124.239124, 51.344664],\n                        [124.192313, 51.33943],\n                        [124.128255, 51.347281],\n                        [124.090067, 51.3413],\n                        [124.071588, 51.320734],\n                        [123.994596, 51.322604],\n                        [123.939777, 51.313253],\n                        [123.926227, 51.300532],\n                        [123.887423, 51.320734],\n                        [123.842459, 51.367462],\n                        [123.794416, 51.361109],\n                        [123.711264, 51.398089],\n                        [123.660141, 51.342795],\n                        [123.661989, 51.319237],\n                        [123.582533, 51.306893],\n                        [123.582533, 51.294545],\n                        [123.46304, 51.286686],\n                        [123.440251, 51.270963],\n                        [123.414381, 51.278825],\n                        [123.376809, 51.266844],\n                        [123.339853, 51.27246],\n                        [123.294273, 51.254111],\n                        [123.231447, 51.268716],\n                        [123.231447, 51.279199],\n                        [123.127969, 51.297913],\n                        [123.069455, 51.321108],\n                        [123.002934, 51.31213],\n                        [122.965977, 51.345786],\n                        [122.965977, 51.386886],\n                        [122.946267, 51.405183],\n                        [122.903768, 51.415262],\n                        [122.900072, 51.445112],\n                        [122.871123, 51.455181],\n                        [122.854492, 51.477551],\n                        [122.880362, 51.511085],\n                        [122.858804, 51.524864],\n                        [122.880362, 51.537894],\n                        [122.874202, 51.561339],\n                        [122.832935, 51.581797],\n                        [122.85634, 51.606707],\n                        [122.820616, 51.633088],\n                        [122.816304, 51.655371],\n                        [122.778732, 51.698048],\n                        [122.749167, 51.746613],\n                        [122.771957, 51.779579],\n                        [122.732536, 51.832495],\n                        [122.725761, 51.87833],\n                        [122.706051, 51.890151],\n                        [122.729457, 51.919321],\n                        [122.726377, 51.978709],\n                        [122.683877, 51.974654],\n                        [122.664783, 51.99861],\n                        [122.650616, 52.058997],\n                        [122.625363, 52.067459],\n                        [122.643841, 52.111585],\n                        [122.629059, 52.13657],\n                        [122.690653, 52.140243],\n                        [122.73808, 52.153464],\n                        [122.769493, 52.179893],\n                        [122.766413, 52.232705],\n                        [122.787355, 52.252494],\n                        [122.76087, 52.26678],\n                        [122.710979, 52.256157],\n                        [122.67895, 52.276667],\n                        [122.585943, 52.266413],\n                        [122.560689, 52.282526],\n                        [122.478153, 52.29607],\n                        [122.484313, 52.341432],\n                        [122.447356, 52.394052],\n                        [122.419023, 52.375057],\n                        [122.378987, 52.395512],\n                        [122.367284, 52.413768],\n                        [122.342031, 52.414133],\n                        [122.326016, 52.459374],\n                        [122.310618, 52.475416],\n                        [122.207756, 52.469218],\n                        [122.178191, 52.48963],\n                        [122.168952, 52.513674],\n                        [122.140003, 52.510032],\n                        [122.142467, 52.495096],\n                        [122.107358, 52.452445],\n                        [122.080873, 52.440407],\n                        [122.091344, 52.427272],\n                        [122.040837, 52.413038],\n                        [122.035909, 52.377615],\n                        [121.976779, 52.343626],\n                        [121.94783, 52.298266],\n                        [121.901018, 52.280695],\n                        [121.841272, 52.282526],\n                        [121.769207, 52.308147],\n                        [121.714389, 52.318025],\n                        [121.715621, 52.342894],\n                        [121.658338, 52.3904],\n                        [121.678664, 52.419973],\n                        [121.63986, 52.44442],\n                        [121.590585, 52.443326],\n                        [121.565331, 52.460468],\n                        [121.519136, 52.456821],\n                        [121.495114, 52.484892],\n                        [121.474172, 52.482706],\n                        [121.416274, 52.499468],\n                        [121.411963, 52.52205],\n                        [121.353448, 52.534793],\n                        [121.323883, 52.573727],\n                        [121.280151, 52.586819],\n                        [121.225333, 52.577364],\n                        [121.182217, 52.59918],\n                        [121.237036, 52.619167],\n                        [121.29247, 52.651855],\n                        [121.309717, 52.676173],\n                        [121.373158, 52.683067],\n                        [121.455078, 52.73528],\n                        [121.476636, 52.772225],\n                        [121.511129, 52.779104],\n                        [121.537614, 52.801542],\n                        [121.591201, 52.824693],\n                        [121.620766, 52.853251],\n                        [121.604136, 52.872401],\n                        [121.610295, 52.892264],\n                        [121.66265, 52.912478],\n                        [121.677432, 52.948192],\n                        [121.715621, 52.997926],\n                        [121.785838, 53.018451],\n                        [121.817867, 53.061631],\n                        [121.775367, 53.089674],\n                        [121.784606, 53.104408],\n                        [121.753193, 53.147501],\n                        [121.722396, 53.145706],\n                        [121.665114, 53.170467],\n                        [121.660186, 53.195213],\n                        [121.67928, 53.199515],\n                        [121.679896, 53.240722],\n                        [121.642324, 53.262564],\n                        [121.615222, 53.258984],\n                        [121.575802, 53.29155],\n                        [121.504969, 53.323018],\n                        [121.499426, 53.337314],\n                        [121.416274, 53.319443],\n                        [121.336818, 53.325877],\n                        [121.308485, 53.301565],\n                        [121.227797, 53.280459],\n                        [121.155732, 53.285468],\n                        [121.129246, 53.277238],\n                        [121.098449, 53.306929],\n                        [121.055334, 53.29155],\n                        [120.950624, 53.29763],\n                        [120.936457, 53.28833],\n                        [120.882871, 53.294411],\n                        [120.867472, 53.278669],\n                        [120.820661, 53.269007],\n                        [120.838523, 53.239648],\n                        [120.821893, 53.241797],\n                        [120.736277, 53.204892],\n                        [120.690698, 53.174771],\n                        [120.687002, 53.142476],\n                        [120.659901, 53.137091],\n                        [120.643886, 53.106923],\n                        [120.562582, 53.082845],\n                        [120.529321, 53.045803],\n                        [120.452945, 53.01017],\n                        [120.411061, 52.957927],\n                        [120.363018, 52.94134],\n                        [120.350699, 52.906343],\n                        [120.295265, 52.891542],\n                        [120.297112, 52.869872],\n                        [120.222584, 52.84277],\n                        [120.181316, 52.806969],\n                        [120.14128, 52.813119],\n                        [120.101244, 52.788877],\n                        [120.031642, 52.773674],\n                        [120.071063, 52.70628],\n                        [120.035338, 52.646409],\n                        [120.049505, 52.598453],\n                        [120.07599, 52.586092],\n                        [120.125265, 52.586819],\n                        [120.194866, 52.578819],\n                        [120.289721, 52.623527],\n                        [120.396895, 52.616261],\n                        [120.462184, 52.64532],\n                        [120.483742, 52.630066],\n                        [120.56135, 52.595544],\n                        [120.605082, 52.589364],\n                        [120.62664, 52.570818],\n                        [120.658669, 52.56718],\n                        [120.690698, 52.547532],\n                        [120.734429, 52.536977],\n                        [120.687002, 52.511489],\n                        [120.706712, 52.492909],\n                        [120.68269, 52.464479],\n                        [120.688234, 52.427637],\n                        [120.64943, 52.3904],\n                        [120.653741, 52.371038],\n                        [120.62356, 52.361172],\n                        [120.627256, 52.323878],\n                        [120.653741, 52.302658],\n                        [120.695625, 52.290214],\n                        [120.715951, 52.261286],\n                        [120.755371, 52.258355],\n                        [120.745516, 52.20594],\n                        [120.786784, 52.15787],\n                        [120.760299, 52.136937],\n                        [120.76769, 52.10938],\n                        [120.753523, 52.085483],\n                        [120.717183, 52.072978],\n                        [120.690698, 52.047221],\n                        [120.691929, 52.026973],\n                        [120.717799, 52.015556],\n                        [120.704864, 51.983501],\n                        [120.66298, 51.958061],\n                        [120.656821, 51.926333],\n                        [120.548416, 51.907877],\n                        [120.549032, 51.882394],\n                        [120.481278, 51.885719],\n                        [120.480046, 51.855049],\n                        [120.40059, 51.833605],\n                        [120.40675, 51.81659],\n                        [120.363634, 51.789945],\n                        [120.317438, 51.785873],\n                        [120.294649, 51.752171],\n                        [120.226279, 51.717703],\n                        [120.172693, 51.679868],\n                        [120.087077, 51.678013],\n                        [120.100628, 51.649058],\n                        [120.05936, 51.634203],\n                        [120.035954, 51.583657],\n                        [120.052584, 51.560967],\n                        [120.017476, 51.52114],\n                        [119.985447, 51.505125],\n                        [119.982367, 51.482396],\n                        [120.002693, 51.459283],\n                        [119.982983, 51.445112],\n                        [119.97128, 51.40033],\n                        [119.910918, 51.390994],\n                        [119.914614, 51.374187],\n                        [119.946643, 51.360736],\n                        [119.883817, 51.336813],\n                        [119.885049, 51.302777],\n                        [119.811136, 51.281071],\n                        [119.828383, 51.263099],\n                        [119.797586, 51.243622],\n                        [119.821607, 51.21439],\n                        [119.784035, 51.22601],\n                        [119.760629, 51.212516],\n                        [119.788346, 51.174636],\n                        [119.771716, 51.124331],\n                        [119.752622, 51.117193],\n                        [119.764325, 51.092017],\n                        [119.719361, 51.075099],\n                        [119.726753, 51.051028],\n                        [119.678093, 51.016404],\n                        [119.630666, 51.00925],\n                        [119.598637, 50.984767],\n                        [119.569688, 50.933879],\n                        [119.491464, 50.87878],\n                        [119.498855, 50.827776],\n                        [119.515485, 50.814165],\n                        [119.496391, 50.771795],\n                        [119.506862, 50.763846],\n                        [119.450196, 50.695281],\n                        [119.430486, 50.684286],\n                        [119.385522, 50.682769],\n                        [119.394145, 50.667219],\n                        [119.361501, 50.632689],\n                        [119.298059, 50.616743],\n                        [119.281428, 50.601551],\n                        [119.295595, 50.573814],\n                        [119.264182, 50.536933],\n                        [119.262334, 50.490124],\n                        [119.250631, 50.448604],\n                        [119.22353, 50.441363],\n                        [119.217371, 50.414675],\n                        [119.165016, 50.422683],\n                        [119.125596, 50.389118],\n                        [119.176719, 50.378814],\n                        [119.155777, 50.364691],\n                        [119.188422, 50.347509],\n                        [119.232153, 50.365455],\n                        [119.259871, 50.345218],\n                        [119.277117, 50.366218],\n                        [119.322696, 50.352474],\n                        [119.358421, 50.358965],\n                        [119.381827, 50.324208],\n                        [119.35103, 50.303953],\n                        [119.339943, 50.244668],\n                        [119.319001, 50.220933],\n                        [119.358421, 50.197953],\n                        [119.339327, 50.192206],\n                        [119.350414, 50.166145],\n                        [119.309762, 50.161161],\n                        [119.290052, 50.121655],\n                        [119.236465, 50.075204],\n                        [119.190269, 50.087877],\n                        [119.193965, 50.069826],\n                        [119.163168, 50.027554],\n                        [119.12498, 50.019095],\n                        [119.090487, 49.985629],\n                        [118.982082, 49.979087],\n                        [118.964836, 49.988708],\n                        [118.791757, 49.955606],\n                        [118.761576, 49.959456],\n                        [118.739402, 49.946364],\n                        [118.672264, 49.955991],\n                        [118.605127, 49.926719],\n                        [118.574946, 49.931342],\n                        [118.531214, 49.887791],\n                        [118.485019, 49.866194],\n                        [118.483787, 49.830691],\n                        [118.443751, 49.835709],\n                        [118.385853, 49.827217],\n                        [118.398787, 49.802502],\n                        [118.384005, 49.783958],\n                        [118.315636, 49.766953],\n                        [118.284223, 49.743755],\n                        [118.220781, 49.729831],\n                        [118.211542, 49.690744],\n                        [118.156723, 49.660149],\n                        [118.129622, 49.669446],\n                        [118.082811, 49.616741],\n                        [118.011362, 49.614803],\n                        [117.995963, 49.623332],\n                        [117.950999, 49.596187],\n                        [117.866, 49.591532],\n                        [117.849369, 49.551557],\n                        [117.809333, 49.521263],\n                        [117.638102, 49.574847],\n                        [117.485349, 49.633024],\n                        [117.278394, 49.636512],\n                        [117.068974, 49.695389],\n                        [116.736367, 49.847674],\n                        [116.717889, 49.847288],\n                        [116.428397, 49.430659],\n                        [116.048363, 48.873274],\n                        [116.077928, 48.822471],\n                        [116.069305, 48.811437],\n                        [115.83032, 48.560156],\n                        [115.799523, 48.514982],\n                        [115.822929, 48.259432],\n                        [115.81061, 48.257042],\n                        [115.529126, 48.155336],\n                        [115.545141, 48.134971],\n                        [115.539597, 48.104607],\n                        [115.580249, 47.921649],\n                        [115.939342, 47.683275],\n                        [115.968291, 47.689721],\n                        [116.111189, 47.811642],\n                        [116.130283, 47.823296],\n                        [116.26579, 47.876711],\n                        [116.453035, 47.837358],\n                        [116.669846, 47.890758],\n                        [116.791186, 47.89758],\n                        [116.879265, 47.893968],\n                        [117.094844, 47.8241],\n                        [117.384335, 47.641356],\n                        [117.493357, 47.758563],\n                        [117.519226, 47.761782],\n                        [117.529081, 47.782697],\n                        [117.813645, 48.016212],\n                        [117.886942, 48.025418],\n                        [117.96147, 48.011007],\n                        [118.052014, 48.01421],\n                        [118.107448, 48.031021],\n                        [118.124694, 48.047427],\n                        [118.150564, 48.036224],\n                        [118.238643, 48.041826],\n                        [118.238027, 48.031422],\n                        [118.284839, 48.011007],\n                        [118.351976, 48.006203],\n                        [118.37415, 48.016612],\n                        [118.422193, 48.01461],\n                        [118.441903, 47.995791],\n                        [118.568171, 47.992187],\n                        [118.773278, 47.771034],\n                        [119.134219, 47.664335],\n                        [119.152081, 47.540453],\n                        [119.205052, 47.520249],\n                        [119.365812, 47.47739],\n                        [119.32208, 47.42721],\n                        [119.365812, 47.423161],\n                        [119.386138, 47.397645],\n                        [119.437877, 47.378602],\n                        [119.450812, 47.353065],\n                        [119.559217, 47.303172],\n                        [119.56784, 47.248357],\n                        [119.627586, 47.247544],\n                        [119.716282, 47.195518],\n                        [119.763093, 47.13082],\n                        [119.806825, 47.055037],\n                        [119.79081, 47.04525],\n                        [119.795122, 47.013024],\n                        [119.845013, 46.964852],\n                        [119.859795, 46.917046],\n                        [119.926933, 46.903963],\n                        [119.920157, 46.853238],\n                        [119.936172, 46.790173],\n                        [119.917078, 46.758203],\n                        [119.93494, 46.712674],\n                        [119.911534, 46.669572],\n                        [119.859179, 46.669572],\n                        [119.804361, 46.68189],\n                        [119.8136, 46.66834],\n                        [119.783419, 46.626023],\n                        [119.739687, 46.615336],\n                        [119.677477, 46.584908],\n                        [119.682405, 46.605058],\n                        [119.656535, 46.625612],\n                        [119.598637, 46.618214],\n                        [119.557985, 46.633832],\n                        [119.491464, 46.629311],\n                        [119.431718, 46.638763],\n                        [119.374435, 46.603414],\n                        [119.357805, 46.619447],\n                        [119.325776, 46.608759],\n                        [119.26295, 46.649034],\n                        [119.20074, 46.648213],\n                        [119.152081, 46.658072],\n                        [119.123132, 46.642872],\n                        [119.073857, 46.676552],\n                        [119.011647, 46.745902],\n                        [118.951285, 46.722111],\n                        [118.912481, 46.733188],\n                        [118.914329, 46.77501],\n                        [118.845343, 46.771731],\n                        [118.788061, 46.717598],\n                        [118.788061, 46.687227],\n                        [118.677192, 46.6979],\n                        [118.639004, 46.721291],\n                        [118.586033, 46.692975],\n                        [118.446831, 46.704467],\n                        [118.41049, 46.728265],\n                        [118.316252, 46.73934],\n                        [118.274984, 46.715957],\n                        [118.238643, 46.709392],\n                        [118.192448, 46.682711],\n                        [118.124078, 46.678195],\n                        [118.04647, 46.631366],\n                        [117.992883, 46.631366],\n                        [117.982412, 46.614925],\n                        [117.914659, 46.607936],\n                        [117.868464, 46.575447],\n                        [117.870927, 46.549935],\n                        [117.813645, 46.530588],\n                        [117.769913, 46.537586],\n                        [117.748355, 46.521941],\n                        [117.704008, 46.516587],\n                        [117.641182, 46.558166],\n                        [117.622704, 46.596012],\n                        [117.596218, 46.603414],\n                        [117.49582, 46.600535],\n                        [117.42006, 46.582029],\n                        [117.447777, 46.528117],\n                        [117.392343, 46.463023],\n                        [117.375712, 46.416421],\n                        [117.383719, 46.394962],\n                        [117.372017, 46.36028],\n                        [117.247597, 46.366888],\n                        [117.097308, 46.356976],\n                        [116.876801, 46.375559],\n                        [116.834302, 46.384229],\n                        [116.81336, 46.355737],\n                        [116.745606, 46.327642],\n                        [116.673541, 46.325163],\n                        [116.585462, 46.292504],\n                        [116.573143, 46.258998],\n                        [116.536187, 46.23251],\n                        [116.439484, 46.137628],\n                        [116.414231, 46.133896],\n                        [116.271949, 45.966926],\n                        [116.243, 45.876169],\n                        [116.288579, 45.839074],\n                        [116.278108, 45.831152],\n                        [116.286731, 45.775247],\n                        [116.260862, 45.776082],\n                        [116.22329, 45.747273],\n                        [116.217746, 45.72221],\n                        [116.17463, 45.688775],\n                        [116.1155, 45.679577],\n                        [116.035428, 45.685013],\n                        [116.026805, 45.661177],\n                        [115.936878, 45.632727],\n                        [115.864197, 45.572853],\n                        [115.699741, 45.45963],\n                        [115.586408, 45.440317],\n                        [115.36467, 45.392427],\n                        [115.178041, 45.396209],\n                        [114.983404, 45.379397],\n                        [114.920578, 45.386122],\n                        [114.745035, 45.438217],\n                        [114.600906, 45.403773],\n                        [114.551014, 45.387383],\n                        [114.539928, 45.325985],\n                        [114.519602, 45.283893],\n                        [114.459855, 45.21353],\n                        [114.409348, 45.179371],\n                        [114.347139, 45.119436],\n                        [114.313262, 45.107189],\n                        [114.19069, 45.036607],\n                        [114.158045, 44.994301],\n                        [114.116777, 44.957045],\n                        [114.065038, 44.931206],\n                        [113.907358, 44.915104],\n                        [113.861778, 44.863377],\n                        [113.798953, 44.849377],\n                        [113.712105, 44.788247],\n                        [113.631417, 44.745333],\n                        [113.540874, 44.759358],\n                        [113.503918, 44.777628],\n                        [113.11526, 44.799714],\n                        [113.037652, 44.822641],\n                        [112.937869, 44.840042],\n                        [112.850406, 44.840466],\n                        [112.712436, 44.879494],\n                        [112.599719, 44.930783],\n                        [112.540589, 45.001072],\n                        [112.438959, 45.071697],\n                        [112.396459, 45.064512],\n                        [112.113743, 45.072965],\n                        [112.071243, 45.096206],\n                        [112.002874, 45.090713],\n                        [111.903707, 45.052252],\n                        [111.764505, 44.969325],\n                        [111.69244, 44.859983],\n                        [111.624687, 44.778477],\n                        [111.585267, 44.705789],\n                        [111.560629, 44.647062],\n                        [111.569868, 44.57634],\n                        [111.530448, 44.55033],\n                        [111.514434, 44.507666],\n                        [111.478709, 44.488884],\n                        [111.427586, 44.394455],\n                        [111.415883, 44.35724],\n                        [111.428818, 44.319573],\n                        [111.507042, 44.294305],\n                        [111.534144, 44.26217],\n                        [111.541535, 44.206855],\n                        [111.559397, 44.171238],\n                        [111.662875, 44.061247],\n                        [111.702295, 44.034147],\n                        [111.773128, 44.010479],\n                        [111.870447, 43.940279],\n                        [111.959758, 43.823382],\n                        [111.970845, 43.748205],\n                        [111.951135, 43.693275],\n                        [111.891388, 43.6738],\n                        [111.79407, 43.672068],\n                        [111.606209, 43.513863],\n                        [111.564325, 43.490422],\n                        [111.456535, 43.494329],\n                        [111.400485, 43.472618],\n                        [111.354289, 43.436125],\n                        [111.183674, 43.396132],\n                        [111.151029, 43.38004],\n                        [111.069725, 43.357852],\n                        [111.02045, 43.329998],\n                        [110.82027, 43.149067],\n                        [110.769763, 43.099272],\n                        [110.736502, 43.089657],\n                        [110.687227, 43.036314],\n                        [110.689691, 43.02144],\n                        [110.631177, 42.936061],\n                        [110.469801, 42.839156],\n                        [110.437156, 42.781203],\n                        [110.34846, 42.742098],\n                        [110.139657, 42.674815],\n                        [110.108244, 42.642687],\n                        [109.906216, 42.635643],\n                        [109.733753, 42.579262],\n                        [109.683862, 42.558988],\n                        [109.544044, 42.472528],\n                        [109.486761, 42.458842],\n                        [109.291509, 42.435879],\n                        [109.026039, 42.458401],\n                        [108.983539, 42.449128],\n                        [108.845569, 42.395673],\n                        [108.798757, 42.415116],\n                        [108.705134, 42.413349],\n                        [108.532671, 42.442945],\n                        [108.298614, 42.438529],\n                        [108.238252, 42.460167],\n                        [108.089195, 42.436321],\n                        [108.022058, 42.433229],\n                        [107.986949, 42.413349],\n                        [107.939522, 42.403628],\n                        [107.736262, 42.415116],\n                        [107.57427, 42.412907],\n                        [107.501589, 42.456635],\n                        [107.46648, 42.458842],\n                        [107.303872, 42.412465],\n                        [107.271844, 42.364285],\n                        [107.051337, 42.319166],\n                        [106.785867, 42.291281],\n                        [106.612789, 42.241679],\n                        [106.372572, 42.161436],\n                        [106.344855, 42.149457],\n                        [106.01348, 42.032213],\n                        [105.74185, 41.949033],\n                        [105.589713, 41.888471],\n                        [105.385221, 41.797073],\n                        [105.291599, 41.749763],\n                        [105.230621, 41.751103],\n                        [105.009498, 41.583007],\n                        [104.923267, 41.654143],\n                        [104.803775, 41.652355],\n                        [104.68921, 41.6452],\n                        [104.524138, 41.661745],\n                        [104.530298, 41.875104],\n                        [104.418813, 41.860397],\n                        [104.30856, 41.840782],\n                        [104.080046, 41.805104],\n                        [103.868779, 41.802427],\n                        [103.454868, 41.877332],\n                        [103.418527, 41.882233],\n                        [103.20726, 41.96283],\n                        [103.021862, 42.028212],\n                        [102.712045, 42.153007],\n                        [102.621502, 42.154338],\n                        [102.540814, 42.162323],\n                        [102.449039, 42.144133],\n                        [102.093642, 42.223512],\n                        [102.070236, 42.232374],\n                        [101.877447, 42.432345],\n                        [101.803534, 42.503861],\n                        [101.770274, 42.509597],\n                        [101.557775, 42.529887],\n                        [101.291689, 42.586312],\n                        [100.862995, 42.671295],\n                        [100.826655, 42.675255],\n                        [100.32528, 42.690213],\n                        [100.272309, 42.636523],\n                        [100.004376, 42.648849],\n                        [99.969267, 42.647969],\n                        [99.51224, 42.568244],\n                        [98.962822, 42.607018],\n                        [98.546447, 42.638284],\n                        [98.195362, 42.653251],\n                        [97.831958, 42.706047],\n                        [97.28254, 42.782081],\n                        [97.172903, 42.795257]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 210000,\n                \"name\": \"Liaoning\",\n                \"center\": [123.429096, 41.796767],\n                \"centroid\": [122.604994, 41.299712],\n                \"childrenNum\": 14,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 5,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [123.534489, 39.788361],\n                            [123.546808, 39.756163],\n                            [123.579453, 39.781002],\n                            [123.612714, 39.775023],\n                            [123.642279, 39.796178],\n                            [123.645358, 39.823761],\n                            [123.674924, 39.826979],\n                            [123.687858, 39.808132],\n                            [123.795032, 39.822842],\n                            [123.812278, 39.831115],\n                            [123.95148, 39.817786],\n                            [124.002603, 39.800316],\n                            [124.103001, 39.823302],\n                            [124.099306, 39.777323],\n                            [124.151045, 39.74558],\n                            [124.173218, 39.841225],\n                            [124.214486, 39.865116],\n                            [124.215102, 39.883487],\n                            [124.21695, 39.894049],\n                            [124.218182, 39.895885],\n                            [124.219414, 39.899099],\n                            [124.241588, 39.928477],\n                            [124.286551, 39.931689],\n                            [124.288399, 39.962888],\n                            [124.349377, 39.989029],\n                            [124.372167, 40.021576],\n                            [124.336442, 40.049985],\n                            [124.346913, 40.079756],\n                            [124.428217, 40.144291],\n                            [124.457782, 40.177679],\n                            [124.490427, 40.18408],\n                            [124.513833, 40.218362],\n                            [124.515065, 40.22019],\n                            [124.62655, 40.291896],\n                            [124.722636, 40.321561],\n                            [124.739267, 40.371733],\n                            [124.834121, 40.423235],\n                            [124.913578, 40.481981],\n                            [124.945606, 40.45603],\n                            [124.985642, 40.475153],\n                            [125.044157, 40.466503],\n                            [125.042925, 40.483802],\n                            [125.004737, 40.496091],\n                            [125.015823, 40.533853],\n                            [125.076801, 40.562048],\n                            [125.113758, 40.569322],\n                            [125.181511, 40.611132],\n                            [125.262815, 40.620218],\n                            [125.279445, 40.655187],\n                            [125.305315, 40.661089],\n                            [125.329337, 40.643835],\n                            [125.375532, 40.658365],\n                            [125.422343, 40.635661],\n                            [125.418648, 40.673345],\n                            [125.453756, 40.676522],\n                            [125.459916, 40.707379],\n                            [125.49564, 40.728697],\n                            [125.544915, 40.729605],\n                            [125.551075, 40.761796],\n                            [125.585567, 40.788535],\n                            [125.61698, 40.763609],\n                            [125.685349, 40.769048],\n                            [125.67611, 40.788082],\n                            [125.641002, 40.798503],\n                            [125.648393, 40.826133],\n                            [125.707523, 40.866877],\n                            [125.687813, 40.897645],\n                            [125.652089, 40.91619],\n                            [125.584335, 40.891764],\n                            [125.589263, 40.931112],\n                            [125.635458, 40.94151],\n                            [125.650241, 40.970888],\n                            [125.674879, 40.974503],\n                            [125.684118, 41.021929],\n                            [125.726617, 41.055332],\n                            [125.739552, 41.08917],\n                            [125.712451, 41.095485],\n                            [125.734009, 41.125695],\n                            [125.759878, 41.132908],\n                            [125.791291, 41.167607],\n                            [125.73832, 41.178418],\n                            [125.758646, 41.232449],\n                            [125.749407, 41.245499],\n                            [125.695205, 41.244599],\n                            [125.685349, 41.273842],\n                            [125.646545, 41.264396],\n                            [125.642234, 41.296327],\n                            [125.62006, 41.318355],\n                            [125.637306, 41.34442],\n                            [125.610205, 41.365084],\n                            [125.589879, 41.359245],\n                            [125.581256, 41.396517],\n                            [125.547995, 41.401006],\n                            [125.534444, 41.428833],\n                            [125.533212, 41.479069],\n                            [125.493176, 41.509103],\n                            [125.507343, 41.534195],\n                            [125.479626, 41.544946],\n                            [125.450061, 41.597777],\n                            [125.461148, 41.642516],\n                            [125.446981, 41.67605],\n                            [125.412488, 41.691246],\n                            [125.344119, 41.672474],\n                            [125.317018, 41.676944],\n                            [125.332416, 41.711354],\n                            [125.336112, 41.768067],\n                            [125.336112, 41.768067],\n                            [125.323177, 41.771191],\n                            [125.323177, 41.771191],\n                            [125.319482, 41.776993],\n                            [125.319482, 41.776993],\n                            [125.294844, 41.822945],\n                            [125.307779, 41.924548],\n                            [125.35151, 41.92811],\n                            [125.291764, 41.958825],\n                            [125.29854, 41.974399],\n                            [125.369989, 42.002868],\n                            [125.363213, 42.017097],\n                            [125.416184, 42.063766],\n                            [125.414336, 42.101964],\n                            [125.446365, 42.098411],\n                            [125.490097, 42.136145],\n                            [125.458068, 42.160105],\n                            [125.458068, 42.160105],\n                            [125.41372, 42.156112],\n                            [125.368141, 42.182726],\n                            [125.357054, 42.145464],\n                            [125.305931, 42.146351],\n                            [125.312706, 42.197359],\n                            [125.280677, 42.175187],\n                            [125.312706, 42.219966],\n                            [125.27575, 42.231045],\n                            [125.27575, 42.266928],\n                            [125.299156, 42.289953],\n                            [125.264047, 42.312528],\n                            [125.224011, 42.30102],\n                            [125.175352, 42.308102],\n                            [125.167345, 42.351903],\n                            [125.203685, 42.366938],\n                            [125.185823, 42.38197],\n                            [125.186439, 42.427928],\n                            [125.140243, 42.44692],\n                            [125.150098, 42.458842],\n                            [125.105135, 42.490624],\n                            [125.068794, 42.499449],\n                            [125.090968, 42.515773],\n                            [125.066946, 42.534738],\n                            [125.089736, 42.567803],\n                            [125.082961, 42.591159],\n                            [125.097127, 42.622433],\n                            [125.038613, 42.615387],\n                            [125.010896, 42.63212],\n                            [125.014592, 42.666014],\n                            [124.99057, 42.677455],\n                            [124.968396, 42.722756],\n                            [124.996729, 42.745174],\n                            [124.975171, 42.802722],\n                            [124.92836, 42.819844],\n                            [124.897563, 42.787791],\n                            [124.874157, 42.789987],\n                            [124.856911, 42.824234],\n                            [124.84952, 42.882585],\n                            [124.87231, 42.962344],\n                            [124.869846, 42.988178],\n                            [124.840897, 43.032377],\n                            [124.88894, 43.074796],\n                            [124.882781, 43.13422],\n                            [124.785462, 43.117185],\n                            [124.755281, 43.074359],\n                            [124.719557, 43.069987],\n                            [124.686912, 43.051185],\n                            [124.677673, 43.002185],\n                            [124.658579, 42.972854],\n                            [124.635173, 42.972854],\n                            [124.632093, 42.949642],\n                            [124.607456, 42.937376],\n                            [124.586514, 42.905384],\n                            [124.466406, 42.847054],\n                            [124.435609, 42.880831],\n                            [124.371551, 42.880831],\n                            [124.38079, 42.912835],\n                            [124.431913, 42.930803],\n                            [124.442384, 42.958841],\n                            [124.42329, 42.975482],\n                            [124.369703, 42.972854],\n                            [124.333363, 42.997371],\n                            [124.425754, 43.076107],\n                            [124.366007, 43.121554],\n                            [124.273617, 43.17875],\n                            [124.287167, 43.207983],\n                            [124.27608, 43.233278],\n                            [124.228653, 43.235022],\n                            [124.215102, 43.255947],\n                            [124.168291, 43.244177],\n                            [124.114088, 43.247229],\n                            [124.117168, 43.2773],\n                            [124.099306, 43.292983],\n                            [124.032784, 43.280786],\n                            [123.964415, 43.34088],\n                            [123.896046, 43.361333],\n                            [123.881263, 43.392218],\n                            [123.881263, 43.392218],\n                            [123.852314, 43.406133],\n                            [123.857858, 43.459153],\n                            [123.857858, 43.459153],\n                            [123.79688, 43.489988],\n                            [123.747604, 43.472184],\n                            [123.749452, 43.439167],\n                            [123.710032, 43.417001],\n                            [123.703873, 43.37047],\n                            [123.698329, 43.272071],\n                            [123.664453, 43.264663],\n                            [123.676771, 43.223684],\n                            [123.645974, 43.208855],\n                            [123.666916, 43.179623],\n                            [123.636119, 43.141644],\n                            [123.631192, 43.088346],\n                            [123.580685, 43.036314],\n                            [123.572678, 43.003498],\n                            [123.536337, 43.007],\n                            [123.474743, 43.042438],\n                            [123.434707, 43.027565],\n                            [123.323222, 43.000872],\n                            [123.259165, 42.993431],\n                            [123.18402, 42.925983],\n                            [123.188947, 42.895739],\n                            [123.169853, 42.859777],\n                            [123.227752, 42.831695],\n                            [123.118114, 42.801405],\n                            [123.058368, 42.768903],\n                            [122.980144, 42.777689],\n                            [122.945651, 42.753524],\n                            [122.925941, 42.772417],\n                            [122.887137, 42.770221],\n                            [122.883442, 42.751766],\n                            [122.883442, 42.751766],\n                            [122.848949, 42.712203],\n                            [122.848949, 42.712203],\n                            [122.786123, 42.757479],\n                            [122.73808, 42.77066],\n                            [122.733152, 42.786034],\n                            [122.653696, 42.78252],\n                            [122.624747, 42.773296],\n                            [122.580399, 42.789987],\n                            [122.576088, 42.819405],\n                            [122.556378, 42.827745],\n                            [122.436886, 42.843105],\n                            [122.35127, 42.830378],\n                            [122.371596, 42.776371],\n                            [122.439349, 42.770221],\n                            [122.460907, 42.755282],\n                            [122.396234, 42.707366],\n                            [122.396234, 42.684054],\n                            [122.338951, 42.669975],\n                            [122.324785, 42.684934],\n                            [122.261343, 42.695931],\n                            [122.204676, 42.732867],\n                            [122.204676, 42.685374],\n                            [122.160945, 42.684934],\n                            [122.072865, 42.710444],\n                            [122.062394, 42.723635],\n                            [122.018663, 42.69901],\n                            [121.939207, 42.688453],\n                            [121.94167, 42.666014],\n                            [121.915801, 42.656332],\n                            [121.921344, 42.605697],\n                            [121.889931, 42.556784],\n                            [121.844352, 42.522389],\n                            [121.831417, 42.533856],\n                            [121.817867, 42.504303],\n                            [121.803084, 42.514891],\n                            [121.747649, 42.484887],\n                            [121.69899, 42.438529],\n                            [121.66573, 42.437204],\n                            [121.604136, 42.495037],\n                            [121.607831, 42.516214],\n                            [121.570875, 42.487093],\n                            [121.506201, 42.482239],\n                            [121.4791, 42.49636],\n                            [121.434752, 42.475176],\n                            [121.386093, 42.474294],\n                            [121.304789, 42.435879],\n                            [121.314644, 42.42837],\n                            [121.285079, 42.387717],\n                            [121.218558, 42.371802],\n                            [121.184681, 42.333324],\n                            [121.133558, 42.300135],\n                            [121.120623, 42.280656],\n                            [121.087978, 42.278885],\n                            [121.070732, 42.254083],\n                            [121.028848, 42.242565],\n                            [120.992508, 42.264714],\n                            [120.933994, 42.27977],\n                            [120.883487, 42.269585],\n                            [120.883487, 42.269585],\n                            [120.883487, 42.242565],\n                            [120.8299, 42.252755],\n                            [120.820661, 42.227943],\n                            [120.79048, 42.218636],\n                            [120.745516, 42.223512],\n                            [120.72211, 42.203565],\n                            [120.624792, 42.154338],\n                            [120.58414, 42.167203],\n                            [120.56751, 42.152119],\n                            [120.466496, 42.105516],\n                            [120.493597, 42.073539],\n                            [120.450481, 42.057101],\n                            [120.456641, 42.016208],\n                            [120.399358, 41.984631],\n                            [120.373489, 41.994862],\n                            [120.309431, 41.951704],\n                            [120.318054, 41.93746],\n                            [120.271859, 41.925439],\n                            [120.260156, 41.904062],\n                            [120.290337, 41.897381],\n                            [120.286641, 41.880005],\n                            [120.251533, 41.884016],\n                            [120.215808, 41.853265],\n                            [120.188707, 41.848361],\n                            [120.183164, 41.826513],\n                            [120.127113, 41.77253],\n                            [120.1382, 41.729221],\n                            [120.096316, 41.697056],\n                            [120.035954, 41.708226],\n                            [120.024867, 41.737707],\n                            [120.050737, 41.776101],\n                            [120.041498, 41.818932],\n                            [120.023019, 41.816701],\n                            [119.989759, 41.899163],\n                            [119.954034, 41.923212],\n                            [119.950954, 41.974399],\n                            [119.924469, 41.98908],\n                            [119.921389, 42.014429],\n                            [119.897368, 42.030879],\n                            [119.87581, 42.077982],\n                            [119.845629, 42.097079],\n                            [119.837622, 42.135257],\n                            [119.854868, 42.170308],\n                            [119.841933, 42.215534],\n                            [119.744615, 42.211545],\n                            [119.679941, 42.240793],\n                            [119.617115, 42.252755],\n                            [119.609108, 42.276671],\n                            [119.557985, 42.289068],\n                            [119.557985, 42.289068],\n                            [119.539507, 42.297922],\n                            [119.571536, 42.335536],\n                            [119.572152, 42.359421],\n                            [119.540123, 42.363401],\n                            [119.502551, 42.388159],\n                            [119.482841, 42.347037],\n                            [119.432949, 42.317396],\n                            [119.34795, 42.300578],\n                            [119.280197, 42.260728],\n                            [119.274037, 42.239021],\n                            [119.237697, 42.200905],\n                            [119.277733, 42.185387],\n                            [119.286972, 42.154781],\n                            [119.30853, 42.147239],\n                            [119.314689, 42.119723],\n                            [119.352261, 42.118391],\n                            [119.384906, 42.08953],\n                            [119.375667, 42.023322],\n                            [119.324544, 41.969505],\n                            [119.323928, 41.937014],\n                            [119.340559, 41.926774],\n                            [119.323312, 41.889807],\n                            [119.334399, 41.871539],\n                            [119.312841, 41.80555],\n                            [119.292515, 41.790827],\n                            [119.317769, 41.764049],\n                            [119.319001, 41.727435],\n                            [119.299907, 41.705545],\n                            [119.307914, 41.657273],\n                            [119.342406, 41.617914],\n                            [119.415703, 41.590169],\n                            [119.420015, 41.567785],\n                            [119.362116, 41.566442],\n                            [119.361501, 41.545841],\n                            [119.406464, 41.503276],\n                            [119.401537, 41.472343],\n                            [119.378131, 41.459787],\n                            [119.376283, 41.422102],\n                            [119.309762, 41.405944],\n                            [119.330704, 41.385293],\n                            [119.296211, 41.325097],\n                            [119.239545, 41.31431],\n                            [119.2494, 41.279689],\n                            [119.209364, 41.244599],\n                            [119.204436, 41.222546],\n                            [119.169943, 41.222996],\n                            [119.189038, 41.198234],\n                            [119.126212, 41.138767],\n                            [119.081248, 41.131555],\n                            [119.080632, 41.095936],\n                            [119.037516, 41.067516],\n                            [118.964836, 41.079246],\n                            [118.937118, 41.052625],\n                            [118.951901, 41.018317],\n                            [119.013495, 41.007479],\n                            [119.00056, 40.967273],\n                            [118.977154, 40.959138],\n                            [118.977154, 40.959138],\n                            [118.916792, 40.969984],\n                            [118.90201, 40.960946],\n                            [118.873061, 40.847866],\n                            [118.845959, 40.822057],\n                            [118.878604, 40.783098],\n                            [118.907553, 40.775394],\n                            [118.895234, 40.75409],\n                            [118.950053, 40.747743],\n                            [118.96114, 40.72008],\n                            [119.011031, 40.687414],\n                            [119.028277, 40.692406],\n                            [119.054763, 40.664721],\n                            [119.115125, 40.666536],\n                            [119.165632, 40.69286],\n                            [119.184726, 40.680153],\n                            [119.14469, 40.632482],\n                            [119.162552, 40.600228],\n                            [119.177951, 40.609315],\n                            [119.230921, 40.603863],\n                            [119.22045, 40.569322],\n                            [119.256175, 40.543404],\n                            [119.30237, 40.530215],\n                            [119.429254, 40.540221],\n                            [119.477913, 40.533399],\n                            [119.503783, 40.553864],\n                            [119.559217, 40.547952],\n                            [119.572152, 40.523846],\n                            [119.553674, 40.502007],\n                            [119.604797, 40.455119],\n                            [119.586934, 40.375381],\n                            [119.598021, 40.334335],\n                            [119.651608, 40.271808],\n                            [119.639289, 40.231613],\n                            [119.639289, 40.231613],\n                            [119.671934, 40.23938],\n                            [119.716898, 40.195966],\n                            [119.745847, 40.207851],\n                            [119.760629, 40.136056],\n                            [119.736608, 40.104936],\n                            [119.772332, 40.08113],\n                            [119.783419, 40.046778],\n                            [119.783419, 40.046778],\n                            [119.787115, 40.041739],\n                            [119.787115, 40.041739],\n                            [119.81668, 40.050443],\n                            [119.81668, 40.050443],\n                            [119.854252, 40.033033],\n                            [119.845629, 40.000949],\n                            [119.845629, 40.000949],\n                            [119.854252, 39.98857],\n                            [119.91831, 39.989946],\n                            [119.941715, 40.009659],\n                            [119.947259, 40.040364],\n                            [120.092005, 40.077466],\n                            [120.134504, 40.074719],\n                            [120.161606, 40.096239],\n                            [120.273091, 40.127362],\n                            [120.371641, 40.174478],\n                            [120.451097, 40.177679],\n                            [120.491749, 40.20008],\n                            [120.523778, 40.256737],\n                            [120.52193, 40.304676],\n                            [120.537329, 40.325211],\n                            [120.602618, 40.36079],\n                            [120.596459, 40.399084],\n                            [120.617401, 40.41959],\n                            [120.616169, 40.444645],\n                            [120.619249, 40.460128],\n                            [120.666676, 40.467413],\n                            [120.693777, 40.505647],\n                            [120.72211, 40.515657],\n                            [120.72827, 40.539311],\n                            [120.822509, 40.59432],\n                            [120.837291, 40.644289],\n                            [120.8299, 40.671076],\n                            [120.861313, 40.684692],\n                            [120.939537, 40.686507],\n                            [120.983269, 40.712822],\n                            [121.032544, 40.709193],\n                            [121.028848, 40.746382],\n                            [120.991276, 40.744115],\n                            [120.980189, 40.766329],\n                            [120.994356, 40.790801],\n                            [120.971566, 40.805751],\n                            [121.00729, 40.807563],\n                            [121.010986, 40.784457],\n                            [121.086747, 40.79805],\n                            [121.076892, 40.815716],\n                            [121.096602, 40.839717],\n                            [121.126167, 40.86914],\n                            [121.177906, 40.873665],\n                            [121.23642, 40.851035],\n                            [121.290622, 40.851488],\n                            [121.439064, 40.830208],\n                            [121.440296, 40.88181],\n                            [121.499426, 40.880001],\n                            [121.526527, 40.85194],\n                            [121.55486, 40.849677],\n                            [121.553013, 40.817528],\n                            [121.576418, 40.837906],\n                            [121.626309, 40.844244],\n                            [121.682976, 40.829755],\n                            [121.732251, 40.846961],\n                            [121.735331, 40.862351],\n                            [121.778446, 40.886787],\n                            [121.816019, 40.894931],\n                            [121.84312, 40.831567],\n                            [121.883772, 40.802127],\n                            [121.934279, 40.79805],\n                            [121.936127, 40.711462],\n                            [121.951525, 40.680607],\n                            [122.025438, 40.674253],\n                            [122.06609, 40.64883],\n                            [122.122141, 40.657457],\n                            [122.148626, 40.671983],\n                            [122.133843, 40.614313],\n                            [122.150474, 40.588413],\n                            [122.245944, 40.519752],\n                            [122.231162, 40.505192],\n                            [122.265038, 40.48016],\n                            [122.221923, 40.481071],\n                            [122.240401, 40.461039],\n                            [122.250872, 40.445555],\n                            [122.229314, 40.424146],\n                            [122.186814, 40.422779],\n                            [122.198517, 40.382219],\n                            [122.152322, 40.357597],\n                            [122.135691, 40.374925],\n                            [122.111054, 40.348932],\n                            [122.138155, 40.338897],\n                            [122.110438, 40.315629],\n                            [122.079641, 40.332967],\n                            [122.040221, 40.322017],\n                            [122.039605, 40.260391],\n                            [122.02667, 40.244862],\n                            [121.940438, 40.242121],\n                            [121.950293, 40.204194],\n                            [121.98109, 40.173106],\n                            [122.003264, 40.172191],\n                            [121.995257, 40.128277],\n                            [121.956453, 40.133311],\n                            [121.910257, 40.072887],\n                            [121.824642, 40.025701],\n                            [121.796309, 39.999116],\n                            [121.779062, 39.942702],\n                            [121.76428, 39.933525],\n                            [121.699606, 39.937196],\n                            [121.626925, 39.882569],\n                            [121.572107, 39.865116],\n                            [121.541926, 39.874302],\n                            [121.530223, 39.851334],\n                            [121.472325, 39.802155],\n                            [121.487107, 39.760303],\n                            [121.45939, 39.747881],\n                            [121.502506, 39.703233],\n                            [121.482796, 39.659478],\n                            [121.451999, 39.658095],\n                            [121.450151, 39.624914],\n                            [121.325731, 39.601402],\n                            [121.299246, 39.606013],\n                            [121.263521, 39.589873],\n                            [121.226565, 39.554814],\n                            [121.224717, 39.519275],\n                            [121.268449, 39.482794],\n                            [121.286927, 39.507271],\n                            [121.301709, 39.476327],\n                            [121.245659, 39.456923],\n                            [121.270296, 39.434277],\n                            [121.246891, 39.421334],\n                            [121.245659, 39.389427],\n                            [121.270296, 39.374162],\n                            [121.307869, 39.391277],\n                            [121.324499, 39.371386],\n                            [121.35468, 39.377863],\n                            [121.432904, 39.357506],\n                            [121.435984, 39.329736],\n                            [121.466781, 39.320014],\n                            [121.474788, 39.296398],\n                            [121.508665, 39.29223],\n                            [121.51544, 39.286672],\n                            [121.562252, 39.322792],\n                            [121.621382, 39.326033],\n                            [121.72486, 39.364447],\n                            [121.711925, 39.33992],\n                            [121.7187, 39.320477],\n                            [121.667577, 39.310754],\n                            [121.672505, 39.275554],\n                            [121.623846, 39.285745],\n                            [121.589353, 39.263044],\n                            [121.631237, 39.22643],\n                            [121.591201, 39.228748],\n                            [121.586889, 39.193506],\n                            [121.604136, 39.166136],\n                            [121.639244, 39.166136],\n                            [121.68236, 39.117863],\n                            [121.631853, 39.077921],\n                            [121.605983, 39.080708],\n                            [121.642324, 39.11972],\n                            [121.590585, 39.154999],\n                            [121.562252, 39.127149],\n                            [121.599208, 39.098824],\n                            [121.581962, 39.075598],\n                            [121.508049, 39.034237],\n                            [121.431057, 39.027263],\n                            [121.370695, 39.060264],\n                            [121.317108, 39.012384],\n                            [121.341129, 38.980757],\n                            [121.275224, 38.971917],\n                            [121.204391, 38.941202],\n                            [121.180369, 38.959819],\n                            [121.128014, 38.958888],\n                            [121.08921, 38.922115],\n                            [121.094138, 38.894173],\n                            [121.129862, 38.879266],\n                            [121.110768, 38.862026],\n                            [121.12863, 38.799089],\n                            [121.112, 38.776231],\n                            [121.13787, 38.723023],\n                            [121.198848, 38.721623],\n                            [121.259825, 38.786495],\n                            [121.280767, 38.786961],\n                            [121.288775, 38.78976],\n                            [121.315876, 38.793958],\n                            [121.359608, 38.822406],\n                            [121.399028, 38.812613],\n                            [121.509897, 38.817743],\n                            [121.564715, 38.874607],\n                            [121.618302, 38.862492],\n                            [121.675585, 38.86156],\n                            [121.708845, 38.872744],\n                            [121.719316, 38.920252],\n                            [121.655874, 38.946788],\n                            [121.618918, 38.950046],\n                            [121.66265, 38.966333],\n                            [121.671273, 39.010059],\n                            [121.73841, 38.998898],\n                            [121.756889, 39.025869],\n                            [121.790149, 39.022614],\n                            [121.804932, 38.970986],\n                            [121.863446, 38.942598],\n                            [121.920728, 38.969591],\n                            [121.905946, 38.997503],\n                            [121.852975, 39.035631],\n                            [121.8887, 39.027263],\n                            [121.929352, 39.024939],\n                            [121.907178, 39.055617],\n                            [121.923192, 39.053758],\n                            [121.963228, 39.030053],\n                            [122.013735, 39.073275],\n                            [122.061778, 39.060264],\n                            [122.071634, 39.074204],\n                            [122.048228, 39.101146],\n                            [122.088264, 39.112291],\n                            [122.127684, 39.144788],\n                            [122.167104, 39.158711],\n                            [122.123988, 39.172631],\n                            [122.117213, 39.213911],\n                            [122.160329, 39.238019],\n                            [122.242865, 39.267678],\n                            [122.274893, 39.322329],\n                            [122.30877, 39.346399],\n                            [122.366053, 39.370461],\n                            [122.412864, 39.411625],\n                            [122.455364, 39.408388],\n                            [122.467682, 39.403301],\n                            [122.51203, 39.413474],\n                            [122.532972, 39.419947],\n                            [122.581631, 39.464316],\n                            [122.637066, 39.488799],\n                            [122.649385, 39.516505],\n                            [122.682645, 39.514658],\n                            [122.808913, 39.559889],\n                            [122.847101, 39.581571],\n                            [122.860652, 39.604629],\n                            [122.941956, 39.604629],\n                            [122.972753, 39.594946],\n                            [122.978912, 39.616156],\n                            [123.021412, 39.64335],\n                            [123.010941, 39.655331],\n                            [123.103332, 39.676983],\n                            [123.146448, 39.647037],\n                            [123.166774, 39.674219],\n                            [123.212969, 39.665928],\n                            [123.215433, 39.696786],\n                            [123.253005, 39.689879],\n                            [123.286882, 39.704154],\n                            [123.270251, 39.714743],\n                            [123.274563, 39.753862],\n                            [123.350939, 39.750641],\n                            [123.388512, 39.74742],\n                            [123.392823, 39.723949],\n                            [123.477823, 39.74696],\n                            [123.521555, 39.772724],\n                            [123.534489, 39.788361]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.63953, 39.286209],\n                            [122.593334, 39.278334],\n                            [122.539131, 39.308439],\n                            [122.50895, 39.290377],\n                            [122.57732, 39.269994],\n                            [122.67895, 39.268605],\n                            [122.673406, 39.269531],\n                            [122.662935, 39.273701],\n                            [122.655544, 39.277407],\n                            [122.640761, 39.288061],\n                            [122.63953, 39.286209]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.318625, 39.170775],\n                            [122.345111, 39.144788],\n                            [122.366053, 39.174951],\n                            [122.398697, 39.16196],\n                            [122.383299, 39.190723],\n                            [122.393154, 39.213448],\n                            [122.343263, 39.203246],\n                            [122.322321, 39.177271],\n                            [122.322937, 39.174487],\n                            [122.319241, 39.172167],\n                            [122.318625, 39.170775]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.691884, 39.23292],\n                            [122.696812, 39.206492],\n                            [122.751631, 39.229675],\n                            [122.740544, 39.248679],\n                            [122.635834, 39.241727],\n                            [122.628443, 39.231993],\n                            [122.690037, 39.234774],\n                            [122.691268, 39.23431],\n                            [122.691884, 39.23292]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.738696, 39.034701],\n                            [122.704819, 39.044463],\n                            [122.733152, 39.014244],\n                            [122.75779, 39.009594],\n                            [122.739312, 39.036561],\n                            [122.738696, 39.034701]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.022644, 39.546507],\n                            [122.96105, 39.551122],\n                            [122.945035, 39.520198],\n                            [122.995542, 39.495264],\n                            [123.036194, 39.533123],\n                            [123.022644, 39.546507]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.503407, 39.241263],\n                            [122.502175, 39.224112],\n                            [122.547755, 39.229211],\n                            [122.503407, 39.241263]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.786784, 40.473787],\n                            [120.83298, 40.491995],\n                            [120.8299, 40.516112],\n                            [120.805262, 40.525666],\n                            [120.774465, 40.48016],\n                            [120.786784, 40.473787]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.086702, 39.426881],\n                            [123.090397, 39.450915],\n                            [123.054057, 39.457847],\n                            [123.086702, 39.426881]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.160614, 39.025404],\n                            [123.205578, 39.057011],\n                            [123.20065, 39.077921],\n                            [123.145832, 39.091857],\n                            [123.143984, 39.038885],\n                            [123.160614, 39.025404]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.716807, 39.74512],\n                            [123.756843, 39.754322],\n                            [123.719887, 39.763063],\n                            [123.716807, 39.74512]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 220000,\n                \"name\": \"Jilin\",\n                \"center\": [125.3245, 43.886841],\n                \"centroid\": [126.171208, 43.703954],\n                \"childrenNum\": 9,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 6,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [129.601492, 42.415116],\n                            [129.601492, 42.422627],\n                            [129.591021, 42.447803],\n                            [129.627361, 42.462816],\n                            [129.651999, 42.426603],\n                            [129.704354, 42.427045],\n                            [129.748701, 42.471204],\n                            [129.738846, 42.500332],\n                            [129.749933, 42.546644],\n                            [129.746237, 42.58455],\n                            [129.786889, 42.615387],\n                            [129.754245, 42.645768],\n                            [129.796744, 42.681854],\n                            [129.767179, 42.707806],\n                            [129.78381, 42.762752],\n                            [129.810911, 42.795257],\n                            [129.816454, 42.851003],\n                            [129.835549, 42.866796],\n                            [129.846636, 42.918533],\n                            [129.874969, 42.923792],\n                            [129.856491, 42.951833],\n                            [129.868193, 42.97373],\n                            [129.903918, 42.968475],\n                            [129.897143, 43.001748],\n                            [129.954425, 43.010938],\n                            [129.963664, 42.978547],\n                            [130.002468, 42.981174],\n                            [130.027106, 42.9676],\n                            [130.072685, 42.971541],\n                            [130.10841, 42.989929],\n                            [130.144134, 42.976357],\n                            [130.120729, 42.954461],\n                            [130.127504, 42.932556],\n                            [130.10225, 42.922916],\n                            [130.136127, 42.90363],\n                            [130.17062, 42.912397],\n                            [130.21004, 42.902315],\n                            [130.258083, 42.90626],\n                            [130.277793, 42.892232],\n                            [130.258083, 42.860655],\n                            [130.245148, 42.799209],\n                            [130.242069, 42.738582],\n                            [130.257467, 42.710884],\n                            [130.290112, 42.702968],\n                            [130.333228, 42.64973],\n                            [130.373264, 42.630799],\n                            [130.388046, 42.603054],\n                            [130.420691, 42.617148],\n                            [130.44656, 42.607459],\n                            [130.423771, 42.574855],\n                            [130.435474, 42.553257],\n                            [130.476125, 42.570007],\n                            [130.459495, 42.588075],\n                            [130.482285, 42.626837],\n                            [130.522937, 42.622433],\n                            [130.520473, 42.593362],\n                            [130.558661, 42.495919],\n                            [130.585763, 42.485328],\n                            [130.581451, 42.435437],\n                            [130.645509, 42.426603],\n                            [130.600545, 42.450453],\n                            [130.599929, 42.486211],\n                            [130.565437, 42.506509],\n                            [130.570364, 42.557224],\n                            [130.622719, 42.573092],\n                            [130.633806, 42.603494],\n                            [130.592538, 42.671295],\n                            [130.521089, 42.702089],\n                            [130.464423, 42.688453],\n                            [130.425003, 42.706926],\n                            [130.40714, 42.731548],\n                            [130.46627, 42.772417],\n                            [130.532792, 42.787352],\n                            [130.562357, 42.815015],\n                            [130.603625, 42.819405],\n                            [130.665835, 42.847932],\n                            [130.708335, 42.846615],\n                            [130.719422, 42.831695],\n                            [130.75453, 42.845738],\n                            [130.784095, 42.842227],\n                            [130.801957, 42.879515],\n                            [130.845073, 42.881269],\n                            [130.890653, 42.852758],\n                            [130.912826, 42.870744],\n                            [130.949783, 42.876884],\n                            [130.981812, 42.857145],\n                            [131.043406, 42.862848],\n                            [131.017536, 42.915027],\n                            [131.034167, 42.929051],\n                            [131.114855, 42.915027],\n                            [131.145652, 42.9365],\n                            [131.151195, 42.968475],\n                            [131.115471, 42.975482],\n                            [131.11855, 43.007875],\n                            [131.102536, 43.021002],\n                            [131.120398, 43.068238],\n                            [131.171521, 43.06955],\n                            [131.173985, 43.111506],\n                            [131.207861, 43.1316],\n                            [131.218948, 43.191405],\n                            [131.201086, 43.203185],\n                            [131.206014, 43.237202],\n                            [131.255289, 43.265099],\n                            [131.269455, 43.297775],\n                            [131.275615, 43.369165],\n                            [131.314419, 43.392653],\n                            [131.295941, 43.441774],\n                            [131.314419, 43.461325],\n                            [131.31873, 43.499539],\n                            [131.304564, 43.502144],\n                            [131.294093, 43.470012],\n                            [131.234963, 43.475224],\n                            [131.201086, 43.442209],\n                            [131.175217, 43.444816],\n                            [131.142572, 43.425695],\n                            [131.026775, 43.508655],\n                            [130.959638, 43.48608],\n                            [130.907283, 43.434387],\n                            [130.864167, 43.437863],\n                            [130.841378, 43.454374],\n                            [130.822899, 43.503446],\n                            [130.776704, 43.52341],\n                            [130.727429, 43.560284],\n                            [130.671378, 43.565054],\n                            [130.665835, 43.583698],\n                            [130.623335, 43.589767],\n                            [130.630726, 43.622268],\n                            [130.57098, 43.626167],\n                            [130.57098, 43.626167],\n                            [130.501995, 43.636563],\n                            [130.488444, 43.65605],\n                            [130.437937, 43.646091],\n                            [130.412684, 43.652586],\n                            [130.394206, 43.703227],\n                            [130.423155, 43.745179],\n                            [130.382503, 43.777164],\n                            [130.381887, 43.817768],\n                            [130.362793, 43.844967],\n                            [130.386198, 43.85403],\n                            [130.368336, 43.894151],\n                            [130.381887, 43.910106],\n                            [130.338155, 43.963975],\n                            [130.364025, 43.992399],\n                            [130.365256, 44.044042],\n                            [130.319061, 44.03974],\n                            [130.307358, 44.002731],\n                            [130.27225, 43.981634],\n                            [130.262395, 43.949328],\n                            [130.208192, 43.948466],\n                            [130.153373, 43.915711],\n                            [130.143518, 43.878624],\n                            [130.116417, 43.878192],\n                            [130.110873, 43.852735],\n                            [130.079461, 43.835039],\n                            [130.027722, 43.851872],\n                            [130.009243, 43.889407],\n                            [130.022794, 43.917866],\n                            [130.017867, 43.961821],\n                            [129.979062, 44.015644],\n                            [129.951345, 44.027263],\n                            [129.907614, 44.023821],\n                            [129.881128, 44.000148],\n                            [129.868193, 44.012631],\n                            [129.802904, 43.964837],\n                            [129.780114, 43.892857],\n                            [129.739462, 43.895876],\n                            [129.743158, 43.876035],\n                            [129.699426, 43.8838],\n                            [129.650767, 43.873016],\n                            [129.529427, 43.870427],\n                            [129.467833, 43.874741],\n                            [129.449971, 43.850578],\n                            [129.417942, 43.843672],\n                            [129.406855, 43.819496],\n                            [129.348341, 43.798333],\n                            [129.30892, 43.812155],\n                            [129.289826, 43.797038],\n                            [129.254718, 43.819496],\n                            [129.211602, 43.784509],\n                            [129.232544, 43.709284],\n                            [129.214066, 43.695006],\n                            [129.217146, 43.648689],\n                            [129.232544, 43.635263],\n                            [129.23008, 43.593234],\n                            [129.169102, 43.561585],\n                            [129.145081, 43.570258],\n                            [129.093958, 43.547706],\n                            [129.037907, 43.540332],\n                            [129.013886, 43.522976],\n                            [128.962763, 43.53903],\n                            [128.949828, 43.553779],\n                            [128.878379, 43.539898],\n                            [128.834647, 43.587599],\n                            [128.821097, 43.637429],\n                            [128.78722, 43.686784],\n                            [128.768126, 43.732207],\n                            [128.729322, 43.736964],\n                            [128.760119, 43.755554],\n                            [128.739177, 43.806972],\n                            [128.719467, 43.816905],\n                            [128.760734, 43.857482],\n                            [128.729938, 43.889838],\n                            [128.696061, 43.903207],\n                            [128.636315, 43.891132],\n                            [128.64001, 43.948035],\n                            [128.610445, 43.960529],\n                            [128.584576, 43.990246],\n                            [128.574721, 44.047914],\n                            [128.529141, 44.112401],\n                            [128.471859, 44.157501],\n                            [128.450301, 44.203423],\n                            [128.471859, 44.247596],\n                            [128.453997, 44.257884],\n                            [128.472475, 44.320001],\n                            [128.446605, 44.339694],\n                            [128.475555, 44.346114],\n                            [128.481714, 44.375637],\n                            [128.457076, 44.409848],\n                            [128.463236, 44.431647],\n                            [128.427511, 44.473512],\n                            [128.397946, 44.483761],\n                            [128.372693, 44.514495],\n                            [128.295084, 44.480772],\n                            [128.293237, 44.467961],\n                            [128.228563, 44.445748],\n                            [128.211317, 44.431647],\n                            [128.172512, 44.34697],\n                            [128.137404, 44.357668],\n                            [128.094904, 44.354673],\n                            [128.074578, 44.370075],\n                            [128.049941, 44.349965],\n                            [128.065339, 44.307155],\n                            [128.101679, 44.293449],\n                            [128.064107, 44.251454],\n                            [128.104143, 44.230017],\n                            [128.09244, 44.181539],\n                            [128.060411, 44.168663],\n                            [128.088129, 44.158359],\n                            [128.091208, 44.133022],\n                            [128.042549, 44.103807],\n                            [127.950158, 44.088334],\n                            [127.912586, 44.064687],\n                            [127.862695, 44.062967],\n                            [127.846065, 44.081886],\n                            [127.808492, 44.086615],\n                            [127.783239, 44.071997],\n                            [127.729036, 44.09908],\n                            [127.735811, 44.11412],\n                            [127.712406, 44.199133],\n                            [127.681609, 44.166946],\n                            [127.641573, 44.193555],\n                            [127.626174, 44.187977],\n                            [127.59045, 44.227872],\n                            [127.623711, 44.278025],\n                            [127.579363, 44.310581],\n                            [127.486356, 44.410275],\n                            [127.50853, 44.437202],\n                            [127.463566, 44.484615],\n                            [127.465414, 44.516628],\n                            [127.485124, 44.528576],\n                            [127.536247, 44.522176],\n                            [127.570124, 44.55033],\n                            [127.557189, 44.575488],\n                            [127.392733, 44.632158],\n                            [127.275705, 44.640249],\n                            [127.261538, 44.61299],\n                            [127.214111, 44.624917],\n                            [127.228893, 44.642804],\n                            [127.182082, 44.644507],\n                            [127.138966, 44.607451],\n                            [127.094619, 44.615972],\n                            [127.089691, 44.593816],\n                            [127.049655, 44.566961],\n                            [127.041648, 44.591258],\n                            [127.044112, 44.653874],\n                            [127.030561, 44.673454],\n                            [127.041032, 44.712169],\n                            [126.9973, 44.764882],\n                            [126.984366, 44.823914],\n                            [126.999764, 44.87398],\n                            [127.021938, 44.898997],\n                            [127.073061, 44.907051],\n                            [127.092771, 44.94688],\n                            [127.050271, 45.004034],\n                            [127.018242, 45.024341],\n                            [126.984981, 45.067893],\n                            [126.970815, 45.070852],\n                            [126.96404, 45.132104],\n                            [126.85625, 45.145613],\n                            [126.792808, 45.135481],\n                            [126.787265, 45.159118],\n                            [126.732446, 45.187385],\n                            [126.685635, 45.187807],\n                            [126.640055, 45.214373],\n                            [126.644983, 45.225334],\n                            [126.569222, 45.252725],\n                            [126.540273, 45.23882],\n                            [126.519331, 45.248091],\n                            [126.402919, 45.222805],\n                            [126.356107, 45.185698],\n                            [126.293282, 45.180214],\n                            [126.285274, 45.162494],\n                            [126.235383, 45.140125],\n                            [126.225528, 45.154054],\n                            [126.166398, 45.13337],\n                            [126.142992, 45.147723],\n                            [126.091869, 45.149411],\n                            [126.047522, 45.170933],\n                            [125.998247, 45.162072],\n                            [125.992703, 45.192447],\n                            [125.957595, 45.201303],\n                            [125.915095, 45.196664],\n                            [125.849805, 45.23882],\n                            [125.823936, 45.237978],\n                            [125.815929, 45.264942],\n                            [125.761726, 45.291472],\n                            [125.726001, 45.336503],\n                            [125.695205, 45.352066],\n                            [125.712451, 45.389485],\n                            [125.711835, 45.477677],\n                            [125.687813, 45.514173],\n                            [125.660096, 45.507043],\n                            [125.61698, 45.517947],\n                            [125.583104, 45.491942],\n                            [125.497488, 45.469283],\n                            [125.480242, 45.486488],\n                            [125.424807, 45.485649],\n                            [125.434662, 45.462988],\n                            [125.398322, 45.416797],\n                            [125.361981, 45.392847],\n                            [125.319482, 45.422678],\n                            [125.301619, 45.402092],\n                            [125.248649, 45.417637],\n                            [125.189518, 45.39915],\n                            [125.137779, 45.409655],\n                            [125.097127, 45.38276],\n                            [125.06633, 45.39915],\n                            [125.08912, 45.420998],\n                            [125.0497, 45.428558],\n                            [125.025678, 45.493201],\n                            [124.961005, 45.495299],\n                            [124.936983, 45.53388],\n                            [124.911114, 45.535976],\n                            [124.884628, 45.495299],\n                            [124.886476, 45.442836],\n                            [124.839665, 45.455852],\n                            [124.792853, 45.436958],\n                            [124.776223, 45.468024],\n                            [124.729412, 45.444096],\n                            [124.690607, 45.452493],\n                            [124.625318, 45.437377],\n                            [124.575427, 45.451234],\n                            [124.579738, 45.424358],\n                            [124.544014, 45.411756],\n                            [124.507058, 45.424778],\n                            [124.480572, 45.456271],\n                            [124.398652, 45.440737],\n                            [124.374015, 45.45795],\n                            [124.352457, 45.496557],\n                            [124.369087, 45.512915],\n                            [124.348761, 45.546874],\n                            [124.287783, 45.539329],\n                            [124.264377, 45.555256],\n                            [124.273001, 45.584163],\n                            [124.238508, 45.591702],\n                            [124.226805, 45.633564],\n                            [124.162132, 45.616404],\n                            [124.128255, 45.641933],\n                            [124.147349, 45.665359],\n                            [124.122096, 45.669123],\n                            [124.13503, 45.690448],\n                            [124.10177, 45.700898],\n                            [124.098074, 45.722628],\n                            [124.054342, 45.751449],\n                            [124.014922, 45.749779],\n                            [124.001987, 45.770655],\n                            [124.064197, 45.802372],\n                            [124.03648, 45.83824],\n                            [124.067277, 45.840325],\n                            [124.061118, 45.886168],\n                            [123.996444, 45.906993],\n                            [123.968727, 45.936551],\n                            [123.973654, 45.973997],\n                            [124.011842, 45.981899],\n                            [123.989053, 46.011833],\n                            [124.040176, 46.01973],\n                            [124.034016, 46.045074],\n                            [124.009995, 46.057534],\n                            [124.015538, 46.088257],\n                            [123.99398, 46.101123],\n                            [124.01677, 46.118549],\n                            [123.991516, 46.143019],\n                            [124.001987, 46.166649],\n                            [123.971806, 46.170379],\n                            [123.956408, 46.206009],\n                            [123.979814, 46.228784],\n                            [123.952096, 46.256516],\n                            [123.960103, 46.288369],\n                            [123.936082, 46.286715],\n                            [123.917604, 46.25693],\n                            [123.896046, 46.303668],\n                            [123.84985, 46.302428],\n                            [123.775938, 46.263136],\n                            [123.726047, 46.255688],\n                            [123.673692, 46.258585],\n                            [123.604706, 46.251964],\n                            [123.569598, 46.223816],\n                            [123.569598, 46.223816],\n                            [123.499381, 46.259826],\n                            [123.452569, 46.233338],\n                            [123.430396, 46.243687],\n                            [123.357099, 46.232096],\n                            [123.357099, 46.232096],\n                            [123.320758, 46.254447],\n                            [123.286266, 46.250308],\n                            [123.248078, 46.273065],\n                            [123.178476, 46.248239],\n                            [123.128585, 46.210565],\n                            [123.127354, 46.174523],\n                            [123.102716, 46.172037],\n                            [123.112571, 46.130163],\n                            [123.070071, 46.123527],\n                            [123.04605, 46.099878],\n                            [122.792898, 46.073313],\n                            [122.828623, 45.912406],\n                            [122.80029, 45.856583],\n                            [122.772572, 45.856583],\n                            [122.752246, 45.834905],\n                            [122.792283, 45.766063],\n                            [122.751015, 45.735996],\n                            [122.741775, 45.705077],\n                            [122.671558, 45.70048],\n                            [122.650001, 45.731401],\n                            [122.640761, 45.771072],\n                            [122.603189, 45.778169],\n                            [122.556378, 45.82156],\n                            [122.522501, 45.786933],\n                            [122.504639, 45.786933],\n                            [122.496016, 45.85825],\n                            [122.446125, 45.916986],\n                            [122.362357, 45.917403],\n                            [122.372828, 45.856166],\n                            [122.337719, 45.859917],\n                            [122.301379, 45.813218],\n                            [122.253952, 45.7982],\n                            [122.236705, 45.831569],\n                            [122.200981, 45.857],\n                            [122.091344, 45.882002],\n                            [122.085184, 45.912406],\n                            [122.040221, 45.959022],\n                            [121.92812, 45.988552],\n                            [121.923808, 46.004767],\n                            [121.864062, 46.002272],\n                            [121.843736, 46.024301],\n                            [121.819098, 46.023054],\n                            [121.761816, 45.998947],\n                            [121.809243, 45.961102],\n                            [121.821562, 45.918235],\n                            [121.805548, 45.900746],\n                            [121.817251, 45.875336],\n                            [121.769823, 45.84366],\n                            [121.766744, 45.830318],\n                            [121.766744, 45.830318],\n                            [121.754425, 45.794862],\n                            [121.697142, 45.76314],\n                            [121.657106, 45.770238],\n                            [121.644172, 45.752284],\n                            [121.666345, 45.727641],\n                            [121.713773, 45.701734],\n                            [121.811091, 45.687103],\n                            [121.812323, 45.704659],\n                            [121.867142, 45.719703],\n                            [121.934279, 45.71051],\n                            [121.970004, 45.692956],\n                            [122.003264, 45.623102],\n                            [121.995873, 45.59882],\n                            [121.966308, 45.596308],\n                            [121.993409, 45.552741],\n                            [122.002648, 45.507882],\n                            [122.064242, 45.472641],\n                            [122.168336, 45.439897],\n                            [122.180039, 45.409655],\n                            [122.146778, 45.374352],\n                            [122.147394, 45.295682],\n                            [122.239169, 45.276313],\n                            [122.22993, 45.206784],\n                            [122.192358, 45.180636],\n                            [122.143082, 45.183167],\n                            [122.109822, 45.142236],\n                            [122.119677, 45.068739],\n                            [122.098735, 45.02138],\n                            [122.074713, 45.006573],\n                            [122.087032, 44.95281],\n                            [122.079025, 44.914256],\n                            [122.04946, 44.912985],\n                            [122.098119, 44.81882],\n                            [122.099967, 44.7823],\n                            [122.168952, 44.770405],\n                            [122.142467, 44.753833],\n                            [122.110438, 44.767856],\n                            [122.10243, 44.736406],\n                            [122.152322, 44.744057],\n                            [122.161561, 44.728328],\n                            [122.117213, 44.701961],\n                            [122.103046, 44.67388],\n                            [122.113517, 44.615546],\n                            [122.13138, 44.577619],\n                            [122.196053, 44.559712],\n                            [122.224386, 44.526016],\n                            [122.228082, 44.480345],\n                            [122.28598, 44.477783],\n                            [122.294604, 44.41113],\n                            [122.291524, 44.310152],\n                            [122.271198, 44.255741],\n                            [122.319241, 44.233018],\n                            [122.483081, 44.236877],\n                            [122.515726, 44.251025],\n                            [122.641993, 44.283595],\n                            [122.675254, 44.285738],\n                            [122.702971, 44.319145],\n                            [122.76087, 44.369648],\n                            [122.85634, 44.398304],\n                            [123.025108, 44.493153],\n                            [123.06576, 44.505959],\n                            [123.12489, 44.5098],\n                            [123.137209, 44.486322],\n                            [123.125506, 44.455147],\n                            [123.142136, 44.428228],\n                            [123.114419, 44.40258],\n                            [123.128585, 44.367081],\n                            [123.196955, 44.34483],\n                            [123.277027, 44.25274],\n                            [123.286882, 44.211574],\n                            [123.323838, 44.179823],\n                            [123.386664, 44.161794],\n                            [123.362642, 44.133452],\n                            [123.350939, 44.092633],\n                            [123.32815, 44.084035],\n                            [123.331229, 44.028984],\n                            [123.365722, 44.013922],\n                            [123.400831, 43.979481],\n                            [123.37065, 43.970006],\n                            [123.397135, 43.954929],\n                            [123.467968, 43.853599],\n                            [123.461809, 43.822518],\n                            [123.498149, 43.771114],\n                            [123.48275, 43.737396],\n                            [123.520323, 43.708419],\n                            [123.518475, 43.682024],\n                            [123.536953, 43.633964],\n                            [123.510468, 43.624867],\n                            [123.5117, 43.592801],\n                            [123.421157, 43.598435],\n                            [123.434091, 43.575461],\n                            [123.461193, 43.568523],\n                            [123.452569, 43.545971],\n                            [123.452569, 43.545971],\n                            [123.360179, 43.567223],\n                            [123.304744, 43.550742],\n                            [123.329998, 43.519071],\n                            [123.315831, 43.492159],\n                            [123.36449, 43.483475],\n                            [123.382968, 43.469143],\n                            [123.419925, 43.410046],\n                            [123.442098, 43.437863],\n                            [123.486446, 43.44525],\n                            [123.519707, 43.402219],\n                            [123.54496, 43.415262],\n                            [123.608402, 43.366119],\n                            [123.703873, 43.37047],\n                            [123.710032, 43.417001],\n                            [123.749452, 43.439167],\n                            [123.747604, 43.472184],\n                            [123.79688, 43.489988],\n                            [123.857858, 43.459153],\n                            [123.857858, 43.459153],\n                            [123.852314, 43.406133],\n                            [123.881263, 43.392218],\n                            [123.881263, 43.392218],\n                            [123.896046, 43.361333],\n                            [123.964415, 43.34088],\n                            [124.032784, 43.280786],\n                            [124.099306, 43.292983],\n                            [124.117168, 43.2773],\n                            [124.114088, 43.247229],\n                            [124.168291, 43.244177],\n                            [124.215102, 43.255947],\n                            [124.228653, 43.235022],\n                            [124.27608, 43.233278],\n                            [124.287167, 43.207983],\n                            [124.273617, 43.17875],\n                            [124.366007, 43.121554],\n                            [124.425754, 43.076107],\n                            [124.333363, 42.997371],\n                            [124.369703, 42.972854],\n                            [124.42329, 42.975482],\n                            [124.442384, 42.958841],\n                            [124.431913, 42.930803],\n                            [124.38079, 42.912835],\n                            [124.371551, 42.880831],\n                            [124.435609, 42.880831],\n                            [124.466406, 42.847054],\n                            [124.586514, 42.905384],\n                            [124.607456, 42.937376],\n                            [124.632093, 42.949642],\n                            [124.635173, 42.972854],\n                            [124.658579, 42.972854],\n                            [124.677673, 43.002185],\n                            [124.686912, 43.051185],\n                            [124.719557, 43.069987],\n                            [124.755281, 43.074359],\n                            [124.785462, 43.117185],\n                            [124.882781, 43.13422],\n                            [124.88894, 43.074796],\n                            [124.840897, 43.032377],\n                            [124.869846, 42.988178],\n                            [124.87231, 42.962344],\n                            [124.84952, 42.882585],\n                            [124.856911, 42.824234],\n                            [124.874157, 42.789987],\n                            [124.897563, 42.787791],\n                            [124.92836, 42.819844],\n                            [124.975171, 42.802722],\n                            [124.996729, 42.745174],\n                            [124.968396, 42.722756],\n                            [124.99057, 42.677455],\n                            [125.014592, 42.666014],\n                            [125.010896, 42.63212],\n                            [125.038613, 42.615387],\n                            [125.097127, 42.622433],\n                            [125.082961, 42.591159],\n                            [125.089736, 42.567803],\n                            [125.066946, 42.534738],\n                            [125.090968, 42.515773],\n                            [125.068794, 42.499449],\n                            [125.105135, 42.490624],\n                            [125.150098, 42.458842],\n                            [125.140243, 42.44692],\n                            [125.186439, 42.427928],\n                            [125.185823, 42.38197],\n                            [125.203685, 42.366938],\n                            [125.167345, 42.351903],\n                            [125.175352, 42.308102],\n                            [125.224011, 42.30102],\n                            [125.264047, 42.312528],\n                            [125.299156, 42.289953],\n                            [125.27575, 42.266928],\n                            [125.27575, 42.231045],\n                            [125.312706, 42.219966],\n                            [125.280677, 42.175187],\n                            [125.312706, 42.197359],\n                            [125.305931, 42.146351],\n                            [125.357054, 42.145464],\n                            [125.368141, 42.182726],\n                            [125.41372, 42.156112],\n                            [125.458068, 42.160105],\n                            [125.458068, 42.160105],\n                            [125.490097, 42.136145],\n                            [125.446365, 42.098411],\n                            [125.414336, 42.101964],\n                            [125.416184, 42.063766],\n                            [125.363213, 42.017097],\n                            [125.369989, 42.002868],\n                            [125.29854, 41.974399],\n                            [125.291764, 41.958825],\n                            [125.35151, 41.92811],\n                            [125.307779, 41.924548],\n                            [125.294844, 41.822945],\n                            [125.319482, 41.776993],\n                            [125.319482, 41.776993],\n                            [125.323177, 41.771191],\n                            [125.323177, 41.771191],\n                            [125.336112, 41.768067],\n                            [125.336112, 41.768067],\n                            [125.332416, 41.711354],\n                            [125.317018, 41.676944],\n                            [125.344119, 41.672474],\n                            [125.412488, 41.691246],\n                            [125.446981, 41.67605],\n                            [125.461148, 41.642516],\n                            [125.450061, 41.597777],\n                            [125.479626, 41.544946],\n                            [125.507343, 41.534195],\n                            [125.493176, 41.509103],\n                            [125.533212, 41.479069],\n                            [125.534444, 41.428833],\n                            [125.547995, 41.401006],\n                            [125.581256, 41.396517],\n                            [125.589879, 41.359245],\n                            [125.610205, 41.365084],\n                            [125.637306, 41.34442],\n                            [125.62006, 41.318355],\n                            [125.642234, 41.296327],\n                            [125.646545, 41.264396],\n                            [125.685349, 41.273842],\n                            [125.695205, 41.244599],\n                            [125.749407, 41.245499],\n                            [125.758646, 41.232449],\n                            [125.73832, 41.178418],\n                            [125.791291, 41.167607],\n                            [125.759878, 41.132908],\n                            [125.734009, 41.125695],\n                            [125.712451, 41.095485],\n                            [125.739552, 41.08917],\n                            [125.726617, 41.055332],\n                            [125.684118, 41.021929],\n                            [125.674879, 40.974503],\n                            [125.650241, 40.970888],\n                            [125.635458, 40.94151],\n                            [125.589263, 40.931112],\n                            [125.584335, 40.891764],\n                            [125.652089, 40.91619],\n                            [125.687813, 40.897645],\n                            [125.707523, 40.866877],\n                            [125.778356, 40.897645],\n                            [125.817161, 40.866877],\n                            [125.860892, 40.888597],\n                            [125.875059, 40.908501],\n                            [125.921254, 40.882715],\n                            [125.959442, 40.88181],\n                            [126.008102, 40.936537],\n                            [126.041362, 40.928851],\n                            [126.051833, 40.96185],\n                            [126.08263, 40.976762],\n                            [126.066, 40.997542],\n                            [126.1085, 41.011995],\n                            [126.099877, 41.036376],\n                            [126.133753, 41.063906],\n                            [126.124514, 41.092327],\n                            [126.16763, 41.094583],\n                            [126.187956, 41.113072],\n                            [126.188572, 41.114875],\n                            [126.295129, 41.171661],\n                            [126.332086, 41.236949],\n                            [126.35426, 41.244599],\n                            [126.373354, 41.289133],\n                            [126.437411, 41.353405],\n                            [126.497158, 41.374965],\n                            [126.524259, 41.349362],\n                            [126.539041, 41.366881],\n                            [126.497158, 41.406842],\n                            [126.559983, 41.548081],\n                            [126.582773, 41.563307],\n                            [126.564295, 41.608965],\n                            [126.592628, 41.624624],\n                            [126.608027, 41.669345],\n                            [126.644983, 41.661297],\n                            [126.688099, 41.674262],\n                            [126.724439, 41.710907],\n                            [126.690562, 41.728328],\n                            [126.694874, 41.751103],\n                            [126.723207, 41.753335],\n                            [126.8002, 41.702865],\n                            [126.809439, 41.749317],\n                            [126.848243, 41.734134],\n                            [126.85625, 41.760031],\n                            [126.887047, 41.791719],\n                            [126.931395, 41.812687],\n                            [126.952953, 41.804212],\n                            [126.940018, 41.773423],\n                            [126.979438, 41.776993],\n                            [127.005923, 41.749317],\n                            [127.050887, 41.744852],\n                            [127.057662, 41.703758],\n                            [127.037952, 41.676944],\n                            [127.103242, 41.647883],\n                            [127.093387, 41.629993],\n                            [127.127263, 41.622388],\n                            [127.135887, 41.600463],\n                            [127.178386, 41.600015],\n                            [127.125416, 41.566442],\n                            [127.11864, 41.540018],\n                            [127.164836, 41.542706],\n                            [127.188241, 41.527475],\n                            [127.241212, 41.520754],\n                            [127.28864, 41.501932],\n                            [127.253531, 41.486691],\n                            [127.296031, 41.486243],\n                            [127.360704, 41.466065],\n                            [127.360088, 41.479518],\n                            [127.405668, 41.478621],\n                            [127.419835, 41.460235],\n                            [127.459255, 41.461581],\n                            [127.465414, 41.479069],\n                            [127.526392, 41.467859],\n                            [127.547334, 41.477276],\n                            [127.563964, 41.432871],\n                            [127.618783, 41.432871],\n                            [127.636645, 41.413575],\n                            [127.684073, 41.422999],\n                            [127.780159, 41.427038],\n                            [127.854688, 41.420755],\n                            [127.86947, 41.4037],\n                            [127.882405, 41.448124],\n                            [127.909506, 41.42973],\n                            [127.93168, 41.444984],\n                            [127.970484, 41.438704],\n                            [127.991426, 41.421204],\n                            [128.000049, 41.442741],\n                            [128.040085, 41.393375],\n                            [128.110919, 41.393375],\n                            [128.090593, 41.374516],\n                            [128.114614, 41.364186],\n                            [128.169433, 41.404149],\n                            [128.203925, 41.410882],\n                            [128.243345, 41.477276],\n                            [128.238418, 41.497898],\n                            [128.301244, 41.540018],\n                            [128.317874, 41.575844],\n                            [128.30186, 41.627756],\n                            [128.248889, 41.681414],\n                            [128.208853, 41.688565],\n                            [128.163889, 41.721628],\n                            [128.147875, 41.78101],\n                            [128.112766, 41.793504],\n                            [128.104143, 41.843457],\n                            [128.115846, 41.896935],\n                            [128.106607, 41.949923],\n                            [128.033926, 42.000199],\n                            [128.090593, 42.022877],\n                            [128.294468, 42.026434],\n                            [128.405338, 42.018876],\n                            [128.466316, 42.020654],\n                            [128.49896, 42.000644],\n                            [128.598127, 42.007315],\n                            [128.60675, 42.02999],\n                            [128.637547, 42.035324],\n                            [128.658489, 42.018876],\n                            [128.70222, 42.02021],\n                            [128.737945, 42.050435],\n                            [128.779213, 42.033546],\n                            [128.795227, 42.042436],\n                            [128.898089, 42.016653],\n                            [128.952908, 42.025545],\n                            [128.954755, 42.083756],\n                            [128.971386, 42.097079],\n                            [129.008958, 42.09175],\n                            [129.039139, 42.107736],\n                            [129.048378, 42.137476],\n                            [129.113668, 42.140583],\n                            [129.166639, 42.188047],\n                            [129.215914, 42.208442],\n                            [129.209138, 42.237692],\n                            [129.181421, 42.242122],\n                            [129.183269, 42.262056],\n                            [129.215914, 42.265157],\n                            [129.231312, 42.283755],\n                            [129.208522, 42.293052],\n                            [129.260261, 42.335536],\n                            [129.231312, 42.356325],\n                            [129.240551, 42.376223],\n                            [129.326167, 42.389927],\n                            [129.30892, 42.403628],\n                            [129.331094, 42.429695],\n                            [129.356348, 42.427045],\n                            [129.342181, 42.441179],\n                            [129.368051, 42.459284],\n                            [129.366203, 42.428811],\n                            [129.392688, 42.42837],\n                            [129.400695, 42.449128],\n                            [129.452434, 42.441179],\n                            [129.49863, 42.412023],\n                            [129.546057, 42.361632],\n                            [129.578086, 42.380202],\n                            [129.569463, 42.399208],\n                            [129.601492, 42.415116]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 230000,\n                \"name\": \"Heilongjiang\",\n                \"center\": [126.642464, 45.756967],\n                \"centroid\": [127.693027, 48.040465],\n                \"childrenNum\": 13,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 7,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [123.569598, 46.223816],\n                            [123.604706, 46.251964],\n                            [123.673692, 46.258585],\n                            [123.726047, 46.255688],\n                            [123.775938, 46.263136],\n                            [123.84985, 46.302428],\n                            [123.896046, 46.303668],\n                            [123.917604, 46.25693],\n                            [123.936082, 46.286715],\n                            [123.960103, 46.288369],\n                            [123.952096, 46.256516],\n                            [123.979814, 46.228784],\n                            [123.956408, 46.206009],\n                            [123.971806, 46.170379],\n                            [124.001987, 46.166649],\n                            [123.991516, 46.143019],\n                            [124.01677, 46.118549],\n                            [123.99398, 46.101123],\n                            [124.015538, 46.088257],\n                            [124.009995, 46.057534],\n                            [124.034016, 46.045074],\n                            [124.040176, 46.01973],\n                            [123.989053, 46.011833],\n                            [124.011842, 45.981899],\n                            [123.973654, 45.973997],\n                            [123.968727, 45.936551],\n                            [123.996444, 45.906993],\n                            [124.061118, 45.886168],\n                            [124.067277, 45.840325],\n                            [124.03648, 45.83824],\n                            [124.064197, 45.802372],\n                            [124.001987, 45.770655],\n                            [124.014922, 45.749779],\n                            [124.054342, 45.751449],\n                            [124.098074, 45.722628],\n                            [124.10177, 45.700898],\n                            [124.13503, 45.690448],\n                            [124.122096, 45.669123],\n                            [124.147349, 45.665359],\n                            [124.128255, 45.641933],\n                            [124.162132, 45.616404],\n                            [124.226805, 45.633564],\n                            [124.238508, 45.591702],\n                            [124.273001, 45.584163],\n                            [124.264377, 45.555256],\n                            [124.287783, 45.539329],\n                            [124.348761, 45.546874],\n                            [124.369087, 45.512915],\n                            [124.352457, 45.496557],\n                            [124.374015, 45.45795],\n                            [124.398652, 45.440737],\n                            [124.480572, 45.456271],\n                            [124.507058, 45.424778],\n                            [124.544014, 45.411756],\n                            [124.579738, 45.424358],\n                            [124.575427, 45.451234],\n                            [124.625318, 45.437377],\n                            [124.690607, 45.452493],\n                            [124.729412, 45.444096],\n                            [124.776223, 45.468024],\n                            [124.792853, 45.436958],\n                            [124.839665, 45.455852],\n                            [124.886476, 45.442836],\n                            [124.884628, 45.495299],\n                            [124.911114, 45.535976],\n                            [124.936983, 45.53388],\n                            [124.961005, 45.495299],\n                            [125.025678, 45.493201],\n                            [125.0497, 45.428558],\n                            [125.08912, 45.420998],\n                            [125.06633, 45.39915],\n                            [125.097127, 45.38276],\n                            [125.137779, 45.409655],\n                            [125.189518, 45.39915],\n                            [125.248649, 45.417637],\n                            [125.301619, 45.402092],\n                            [125.319482, 45.422678],\n                            [125.361981, 45.392847],\n                            [125.398322, 45.416797],\n                            [125.434662, 45.462988],\n                            [125.424807, 45.485649],\n                            [125.480242, 45.486488],\n                            [125.497488, 45.469283],\n                            [125.583104, 45.491942],\n                            [125.61698, 45.517947],\n                            [125.660096, 45.507043],\n                            [125.687813, 45.514173],\n                            [125.711835, 45.477677],\n                            [125.712451, 45.389485],\n                            [125.695205, 45.352066],\n                            [125.726001, 45.336503],\n                            [125.761726, 45.291472],\n                            [125.815929, 45.264942],\n                            [125.823936, 45.237978],\n                            [125.849805, 45.23882],\n                            [125.915095, 45.196664],\n                            [125.957595, 45.201303],\n                            [125.992703, 45.192447],\n                            [125.998247, 45.162072],\n                            [126.047522, 45.170933],\n                            [126.091869, 45.149411],\n                            [126.142992, 45.147723],\n                            [126.166398, 45.13337],\n                            [126.225528, 45.154054],\n                            [126.235383, 45.140125],\n                            [126.285274, 45.162494],\n                            [126.293282, 45.180214],\n                            [126.356107, 45.185698],\n                            [126.402919, 45.222805],\n                            [126.519331, 45.248091],\n                            [126.540273, 45.23882],\n                            [126.569222, 45.252725],\n                            [126.644983, 45.225334],\n                            [126.640055, 45.214373],\n                            [126.685635, 45.187807],\n                            [126.732446, 45.187385],\n                            [126.787265, 45.159118],\n                            [126.792808, 45.135481],\n                            [126.85625, 45.145613],\n                            [126.96404, 45.132104],\n                            [126.970815, 45.070852],\n                            [126.984981, 45.067893],\n                            [127.018242, 45.024341],\n                            [127.050271, 45.004034],\n                            [127.092771, 44.94688],\n                            [127.073061, 44.907051],\n                            [127.021938, 44.898997],\n                            [126.999764, 44.87398],\n                            [126.984366, 44.823914],\n                            [126.9973, 44.764882],\n                            [127.041032, 44.712169],\n                            [127.030561, 44.673454],\n                            [127.044112, 44.653874],\n                            [127.041648, 44.591258],\n                            [127.049655, 44.566961],\n                            [127.089691, 44.593816],\n                            [127.094619, 44.615972],\n                            [127.138966, 44.607451],\n                            [127.182082, 44.644507],\n                            [127.228893, 44.642804],\n                            [127.214111, 44.624917],\n                            [127.261538, 44.61299],\n                            [127.275705, 44.640249],\n                            [127.392733, 44.632158],\n                            [127.557189, 44.575488],\n                            [127.570124, 44.55033],\n                            [127.536247, 44.522176],\n                            [127.485124, 44.528576],\n                            [127.465414, 44.516628],\n                            [127.463566, 44.484615],\n                            [127.50853, 44.437202],\n                            [127.486356, 44.410275],\n                            [127.579363, 44.310581],\n                            [127.623711, 44.278025],\n                            [127.59045, 44.227872],\n                            [127.626174, 44.187977],\n                            [127.641573, 44.193555],\n                            [127.681609, 44.166946],\n                            [127.712406, 44.199133],\n                            [127.735811, 44.11412],\n                            [127.729036, 44.09908],\n                            [127.783239, 44.071997],\n                            [127.808492, 44.086615],\n                            [127.846065, 44.081886],\n                            [127.862695, 44.062967],\n                            [127.912586, 44.064687],\n                            [127.950158, 44.088334],\n                            [128.042549, 44.103807],\n                            [128.091208, 44.133022],\n                            [128.088129, 44.158359],\n                            [128.060411, 44.168663],\n                            [128.09244, 44.181539],\n                            [128.104143, 44.230017],\n                            [128.064107, 44.251454],\n                            [128.101679, 44.293449],\n                            [128.065339, 44.307155],\n                            [128.049941, 44.349965],\n                            [128.074578, 44.370075],\n                            [128.094904, 44.354673],\n                            [128.137404, 44.357668],\n                            [128.172512, 44.34697],\n                            [128.211317, 44.431647],\n                            [128.228563, 44.445748],\n                            [128.293237, 44.467961],\n                            [128.295084, 44.480772],\n                            [128.372693, 44.514495],\n                            [128.397946, 44.483761],\n                            [128.427511, 44.473512],\n                            [128.463236, 44.431647],\n                            [128.457076, 44.409848],\n                            [128.481714, 44.375637],\n                            [128.475555, 44.346114],\n                            [128.446605, 44.339694],\n                            [128.472475, 44.320001],\n                            [128.453997, 44.257884],\n                            [128.471859, 44.247596],\n                            [128.450301, 44.203423],\n                            [128.471859, 44.157501],\n                            [128.529141, 44.112401],\n                            [128.574721, 44.047914],\n                            [128.584576, 43.990246],\n                            [128.610445, 43.960529],\n                            [128.64001, 43.948035],\n                            [128.636315, 43.891132],\n                            [128.696061, 43.903207],\n                            [128.729938, 43.889838],\n                            [128.760734, 43.857482],\n                            [128.719467, 43.816905],\n                            [128.739177, 43.806972],\n                            [128.760119, 43.755554],\n                            [128.729322, 43.736964],\n                            [128.768126, 43.732207],\n                            [128.78722, 43.686784],\n                            [128.821097, 43.637429],\n                            [128.834647, 43.587599],\n                            [128.878379, 43.539898],\n                            [128.949828, 43.553779],\n                            [128.962763, 43.53903],\n                            [129.013886, 43.522976],\n                            [129.037907, 43.540332],\n                            [129.093958, 43.547706],\n                            [129.145081, 43.570258],\n                            [129.169102, 43.561585],\n                            [129.23008, 43.593234],\n                            [129.232544, 43.635263],\n                            [129.217146, 43.648689],\n                            [129.214066, 43.695006],\n                            [129.232544, 43.709284],\n                            [129.211602, 43.784509],\n                            [129.254718, 43.819496],\n                            [129.289826, 43.797038],\n                            [129.30892, 43.812155],\n                            [129.348341, 43.798333],\n                            [129.406855, 43.819496],\n                            [129.417942, 43.843672],\n                            [129.449971, 43.850578],\n                            [129.467833, 43.874741],\n                            [129.529427, 43.870427],\n                            [129.650767, 43.873016],\n                            [129.699426, 43.8838],\n                            [129.743158, 43.876035],\n                            [129.739462, 43.895876],\n                            [129.780114, 43.892857],\n                            [129.802904, 43.964837],\n                            [129.868193, 44.012631],\n                            [129.881128, 44.000148],\n                            [129.907614, 44.023821],\n                            [129.951345, 44.027263],\n                            [129.979062, 44.015644],\n                            [130.017867, 43.961821],\n                            [130.022794, 43.917866],\n                            [130.009243, 43.889407],\n                            [130.027722, 43.851872],\n                            [130.079461, 43.835039],\n                            [130.110873, 43.852735],\n                            [130.116417, 43.878192],\n                            [130.143518, 43.878624],\n                            [130.153373, 43.915711],\n                            [130.208192, 43.948466],\n                            [130.262395, 43.949328],\n                            [130.27225, 43.981634],\n                            [130.307358, 44.002731],\n                            [130.319061, 44.03974],\n                            [130.365256, 44.044042],\n                            [130.364025, 43.992399],\n                            [130.338155, 43.963975],\n                            [130.381887, 43.910106],\n                            [130.368336, 43.894151],\n                            [130.386198, 43.85403],\n                            [130.362793, 43.844967],\n                            [130.381887, 43.817768],\n                            [130.382503, 43.777164],\n                            [130.423155, 43.745179],\n                            [130.394206, 43.703227],\n                            [130.412684, 43.652586],\n                            [130.437937, 43.646091],\n                            [130.488444, 43.65605],\n                            [130.501995, 43.636563],\n                            [130.57098, 43.626167],\n                            [130.57098, 43.626167],\n                            [130.630726, 43.622268],\n                            [130.623335, 43.589767],\n                            [130.665835, 43.583698],\n                            [130.671378, 43.565054],\n                            [130.727429, 43.560284],\n                            [130.776704, 43.52341],\n                            [130.822899, 43.503446],\n                            [130.841378, 43.454374],\n                            [130.864167, 43.437863],\n                            [130.907283, 43.434387],\n                            [130.959638, 43.48608],\n                            [131.026775, 43.508655],\n                            [131.142572, 43.425695],\n                            [131.175217, 43.444816],\n                            [131.201086, 43.442209],\n                            [131.234963, 43.475224],\n                            [131.294093, 43.470012],\n                            [131.304564, 43.502144],\n                            [131.276847, 43.495632],\n                            [131.20047, 43.532089],\n                            [131.222028, 43.593234],\n                            [131.216485, 43.613169],\n                            [131.239274, 43.670337],\n                            [131.221412, 43.682024],\n                            [131.215869, 43.72745],\n                            [131.232499, 43.742585],\n                            [131.213405, 43.801357],\n                            [131.2171, 43.836334],\n                            [131.254057, 43.893289],\n                            [131.26268, 43.948897],\n                            [131.245434, 43.95579],\n                            [131.26576, 44.034578],\n                            [131.28239, 44.035868],\n                            [131.287318, 44.03802],\n                            [131.293477, 44.043182],\n                            [131.310723, 44.046623],\n                            [131.111775, 44.710042],\n                            [131.090833, 44.717272],\n                            [131.093297, 44.746183],\n                            [131.069275, 44.759783],\n                            [131.064348, 44.786973],\n                            [131.016304, 44.789521],\n                            [131.015688, 44.814999],\n                            [130.972573, 44.820094],\n                            [130.965181, 44.85065],\n                            [131.07913, 44.881614],\n                            [131.10192, 44.898997],\n                            [131.090217, 44.924427],\n                            [131.16105, 44.948151],\n                            [131.20355, 44.932901],\n                            [131.207861, 44.913833],\n                            [131.263296, 44.929935],\n                            [131.274999, 44.919766],\n                            [131.313803, 44.950692],\n                            [131.313803, 44.965938],\n                            [131.355071, 44.990068],\n                            [131.380324, 44.978216],\n                            [131.409889, 44.985836],\n                            [131.464708, 44.963397],\n                            [131.501664, 44.977793],\n                            [131.484418, 44.99557],\n                            [131.529382, 45.012073],\n                            [131.566338, 45.045487],\n                            [131.63286, 45.075078],\n                            [131.695685, 45.132104],\n                            [131.687678, 45.1511],\n                            [131.650722, 45.159962],\n                            [131.681519, 45.215217],\n                            [131.721555, 45.234606],\n                            [131.759127, 45.213952],\n                            [131.79362, 45.211844],\n                            [131.788692, 45.245984],\n                            [131.825649, 45.291472],\n                            [131.82996, 45.311677],\n                            [131.887858, 45.342393],\n                            [131.917423, 45.339448],\n                            [131.93159, 45.287683],\n                            [131.976554, 45.277156],\n                            [132.003655, 45.25441],\n                            [132.17427, 45.216903],\n                            [132.394161, 45.16376],\n                            [132.76434, 45.081417],\n                            [132.867202, 45.061976],\n                            [132.916477, 45.031109],\n                            [132.954049, 45.023072],\n                            [132.98731, 45.043373],\n                            [133.035969, 45.054366],\n                            [133.070462, 45.097051],\n                            [133.089556, 45.097473],\n                            [133.107418, 45.124504],\n                            [133.139447, 45.127459],\n                            [133.129592, 45.211422],\n                            [133.095715, 45.246827],\n                            [133.110498, 45.266627],\n                            [133.097563, 45.284735],\n                            [133.128976, 45.336924],\n                            [133.119121, 45.352908],\n                            [133.144991, 45.367205],\n                            [133.143759, 45.430658],\n                            [133.164701, 45.437377],\n                            [133.170244, 45.465506],\n                            [133.203505, 45.516689],\n                            [133.246005, 45.517528],\n                            [133.333468, 45.562379],\n                            [133.342707, 45.554836],\n                            [133.393214, 45.580393],\n                            [133.423395, 45.584163],\n                            [133.412924, 45.618079],\n                            [133.471438, 45.631053],\n                            [133.448649, 45.647372],\n                            [133.485605, 45.658667],\n                            [133.484989, 45.691702],\n                            [133.445569, 45.705077],\n                            [133.454192, 45.731819],\n                            [133.486837, 45.740173],\n                            [133.469591, 45.777751],\n                            [133.505315, 45.785681],\n                            [133.469591, 45.799451],\n                            [133.467743, 45.834905],\n                            [133.494228, 45.840325],\n                            [133.491764, 45.867002],\n                            [133.51209, 45.887001],\n                            [133.55459, 45.893249],\n                            [133.583539, 45.868669],\n                            [133.618032, 45.903662],\n                            [133.614952, 45.942794],\n                            [133.676546, 45.94321],\n                            [133.681474, 45.986473],\n                            [133.740604, 46.048812],\n                            [133.745531, 46.075389],\n                            [133.690713, 46.133896],\n                            [133.706111, 46.163333],\n                            [133.764626, 46.17328],\n                            [133.794807, 46.193583],\n                            [133.814517, 46.230854],\n                            [133.849625, 46.203939],\n                            [133.87919, 46.233752],\n                            [133.867487, 46.250722],\n                            [133.909987, 46.254447],\n                            [133.91861, 46.280924],\n                            [133.908139, 46.308216],\n                            [133.922922, 46.330948],\n                            [133.869335, 46.338386],\n                            [133.876726, 46.362345],\n                            [133.940784, 46.38134],\n                            [133.948791, 46.401153],\n                            [133.902596, 46.446119],\n                            [133.852089, 46.450242],\n                            [133.849625, 46.475389],\n                            [133.890893, 46.525235],\n                            [133.919842, 46.596012],\n                            [134.011001, 46.637941],\n                            [134.030711, 46.708981],\n                            [134.033175, 46.759023],\n                            [134.052885, 46.779928],\n                            [134.025168, 46.810657],\n                            [134.041182, 46.848326],\n                            [134.042414, 46.886787],\n                            [134.076291, 46.938298],\n                            [134.063972, 46.979962],\n                            [134.10216, 47.005678],\n                            [134.118175, 47.061968],\n                            [134.142812, 47.093349],\n                            [134.222268, 47.105164],\n                            [134.232739, 47.134892],\n                            [134.230276, 47.182097],\n                            [134.210566, 47.210155],\n                            [134.156979, 47.248357],\n                            [134.177305, 47.326299],\n                            [134.203174, 47.347389],\n                            [134.263536, 47.371307],\n                            [134.266616, 47.391974],\n                            [134.307268, 47.428829],\n                            [134.339297, 47.439759],\n                            [134.490202, 47.446235],\n                            [134.522847, 47.468086],\n                            [134.568426, 47.478199],\n                            [134.576434, 47.519036],\n                            [134.627556, 47.546512],\n                            [134.678064, 47.588507],\n                            [134.689766, 47.63813],\n                            [134.779694, 47.7159],\n                            [134.772918, 47.763391],\n                            [134.678679, 47.819278],\n                            [134.670056, 47.864667],\n                            [134.677448, 47.884738],\n                            [134.658969, 47.901191],\n                            [134.607846, 47.909214],\n                            [134.599839, 47.947711],\n                            [134.55426, 47.982173],\n                            [134.551796, 48.032622],\n                            [134.632484, 48.099412],\n                            [134.67252, 48.170505],\n                            [134.679295, 48.256245],\n                            [134.77107, 48.288908],\n                            [134.864077, 48.332293],\n                            [135.009439, 48.365703],\n                            [135.090743, 48.403461],\n                            [135.09567, 48.437618],\n                            [135.068569, 48.459451],\n                            [135.035924, 48.440795],\n                            [134.996504, 48.439603],\n                            [134.927519, 48.451513],\n                            [134.886867, 48.437618],\n                            [134.848679, 48.393925],\n                            [134.820961, 48.37604],\n                            [134.764295, 48.370076],\n                            [134.704549, 48.405448],\n                            [134.640491, 48.409818],\n                            [134.578281, 48.405448],\n                            [134.501905, 48.418954],\n                            [134.438463, 48.405448],\n                            [134.369478, 48.382797],\n                            [134.20379, 48.3824],\n                            [134.150819, 48.346217],\n                            [134.116327, 48.333089],\n                            [134.0689, 48.338659],\n                            [134.029479, 48.327519],\n                            [133.995603, 48.303639],\n                            [133.940784, 48.302047],\n                            [133.876111, 48.282536],\n                            [133.824372, 48.277359],\n                            [133.791111, 48.261026],\n                            [133.740604, 48.254651],\n                            [133.693177, 48.186866],\n                            [133.667307, 48.183275],\n                            [133.59709, 48.194846],\n                            [133.573068, 48.182078],\n                            [133.545967, 48.121389],\n                            [133.451728, 48.112999],\n                            [133.407997, 48.124585],\n                            [133.302055, 48.103009],\n                            [133.239845, 48.126583],\n                            [133.182563, 48.135769],\n                            [133.130208, 48.134971],\n                            [133.053216, 48.110202],\n                            [133.02673, 48.085421],\n                            [133.016259, 48.054228],\n                            [132.992238, 48.035424],\n                            [132.883216, 48.002599],\n                            [132.819159, 47.936887],\n                            [132.769268, 47.93849],\n                            [132.723072, 47.962941],\n                            [132.691043, 47.962941],\n                            [132.661478, 47.944905],\n                            [132.662094, 47.922451],\n                            [132.687348, 47.88514],\n                            [132.662094, 47.854227],\n                            [132.621442, 47.82852],\n                            [132.599268, 47.792347],\n                            [132.6005, 47.740858],\n                            [132.558, 47.718316],\n                            [132.469305, 47.726368],\n                            [132.371987, 47.765402],\n                            [132.325175, 47.762184],\n                            [132.288835, 47.742065],\n                            [132.272205, 47.718718],\n                            [132.242639, 47.70986],\n                            [132.19706, 47.714289],\n                            [132.157024, 47.70543],\n                            [132.086191, 47.703013],\n                            [132.000575, 47.712276],\n                            [131.976554, 47.673201],\n                            [131.900793, 47.685692],\n                            [131.825649, 47.677231],\n                            [131.741881, 47.706638],\n                            [131.690142, 47.707041],\n                            [131.641483, 47.663932],\n                            [131.59036, 47.660707],\n                            [131.568186, 47.682469],\n                            [131.559563, 47.724757],\n                            [131.543548, 47.736028],\n                            [131.456085, 47.747297],\n                            [131.359998, 47.730796],\n                            [131.273767, 47.738846],\n                            [131.236811, 47.733211],\n                            [131.183224, 47.702611],\n                            [131.115471, 47.689721],\n                            [131.029855, 47.694555],\n                            [130.983659, 47.713081],\n                            [130.966413, 47.733211],\n                            [130.961486, 47.828118],\n                            [130.891269, 47.927263],\n                            [130.870943, 47.943301],\n                            [130.770544, 47.998194],\n                            [130.737284, 48.034223],\n                            [130.699711, 48.044227],\n                            [130.666451, 48.105007],\n                            [130.673842, 48.12818],\n                            [130.765617, 48.18926],\n                            [130.769313, 48.231136],\n                            [130.787791, 48.256643],\n                            [130.817972, 48.265409],\n                            [130.845073, 48.296473],\n                            [130.81982, 48.341444],\n                            [130.785327, 48.357353],\n                            [130.747755, 48.404256],\n                            [130.745907, 48.449131],\n                            [130.776704, 48.480084],\n                            [130.767465, 48.507846],\n                            [130.711414, 48.511414],\n                            [130.647357, 48.484844],\n                            [130.620871, 48.49595],\n                            [130.615944, 48.575601],\n                            [130.605473, 48.594207],\n                            [130.538335, 48.612016],\n                            [130.538951, 48.635751],\n                            [130.576524, 48.688719],\n                            [130.622103, 48.783842],\n                            [130.689856, 48.849651],\n                            [130.680617, 48.881146],\n                            [130.609168, 48.881146],\n                            [130.559277, 48.861071],\n                            [130.501995, 48.865795],\n                            [130.471198, 48.905541],\n                            [130.412068, 48.905148],\n                            [130.279641, 48.866976],\n                            [130.237757, 48.868551],\n                            [130.219895, 48.893739],\n                            [130.113337, 48.956653],\n                            [130.059135, 48.979047],\n                            [130.020946, 49.021058],\n                            [129.937179, 49.040285],\n                            [129.9187, 49.060681],\n                            [129.934715, 49.078717],\n                            [129.913157, 49.1085],\n                            [129.866962, 49.113985],\n                            [129.855259, 49.133567],\n                            [129.864498, 49.158621],\n                            [129.847867, 49.181316],\n                            [129.784426, 49.184054],\n                            [129.753629, 49.208692],\n                            [129.761636, 49.25754],\n                            [129.730223, 49.288387],\n                            [129.696962, 49.298535],\n                            [129.604571, 49.279018],\n                            [129.562687, 49.299706],\n                            [129.546057, 49.395227],\n                            [129.51834, 49.423652],\n                            [129.448739, 49.441167],\n                            [129.390224, 49.432605],\n                            [129.374826, 49.414309],\n                            [129.379138, 49.367175],\n                            [129.358196, 49.355871],\n                            [129.320623, 49.3586],\n                            [129.266421, 49.396006],\n                            [129.215298, 49.399122],\n                            [129.180805, 49.386657],\n                            [129.143849, 49.357431],\n                            [129.084719, 49.359769],\n                            [129.061929, 49.374189],\n                            [129.013886, 49.457119],\n                            [128.932582, 49.46801],\n                            [128.871604, 49.492506],\n                            [128.792147, 49.473065],\n                            [128.76135, 49.482009],\n                            [128.763198, 49.515824],\n                            [128.813089, 49.558157],\n                            [128.802618, 49.58222],\n                            [128.744104, 49.595023],\n                            [128.715155, 49.564756],\n                            [128.656025, 49.577564],\n                            [128.619684, 49.593471],\n                            [128.537764, 49.604332],\n                            [128.500192, 49.593859],\n                            [128.389939, 49.58998],\n                            [128.343128, 49.544956],\n                            [128.287077, 49.566309],\n                            [128.243345, 49.563203],\n                            [128.185447, 49.53952],\n                            [128.122005, 49.55311],\n                            [128.070882, 49.556604],\n                            [128.001281, 49.592307],\n                            [127.949542, 49.596187],\n                            [127.897804, 49.579116],\n                            [127.815268, 49.593859],\n                            [127.782007, 49.630698],\n                            [127.705015, 49.665185],\n                            [127.677913, 49.697712],\n                            [127.674833, 49.764247],\n                            [127.653892, 49.780094],\n                            [127.583059, 49.786277],\n                            [127.531936, 49.826059],\n                            [127.529472, 49.864265],\n                            [127.547334, 49.928645],\n                            [127.543638, 49.944438],\n                            [127.495595, 49.994479],\n                            [127.501755, 50.056764],\n                            [127.58737, 50.137768],\n                            [127.60708, 50.178794],\n                            [127.603385, 50.239309],\n                            [127.44632, 50.270686],\n                            [127.371791, 50.29669],\n                            [127.332371, 50.340634],\n                            [127.369944, 50.403996],\n                            [127.3644, 50.438314],\n                            [127.30527, 50.45432],\n                            [127.293567, 50.46575],\n                            [127.323132, 50.52552],\n                            [127.36132, 50.547582],\n                            [127.370559, 50.581415],\n                            [127.294799, 50.663426],\n                            [127.28864, 50.699451],\n                            [127.305886, 50.733932],\n                            [127.295415, 50.755139],\n                            [127.236285, 50.781256],\n                            [127.143894, 50.910111],\n                            [127.113713, 50.93765],\n                            [127.052119, 50.962911],\n                            [126.985597, 51.029202],\n                            [126.922772, 51.061937],\n                            [126.917844, 51.138977],\n                            [126.899982, 51.200518],\n                            [126.926467, 51.246244],\n                            [126.976358, 51.291551],\n                            [126.98375, 51.318863],\n                            [126.970815, 51.332327],\n                            [126.887047, 51.321856],\n                            [126.877808, 51.300906],\n                            [126.908605, 51.283691],\n                            [126.92154, 51.259729],\n                            [126.908605, 51.246619],\n                            [126.863025, 51.248492],\n                            [126.820526, 51.281071],\n                            [126.813134, 51.311756],\n                            [126.837156, 51.345038],\n                            [126.904293, 51.340552],\n                            [126.930163, 51.359241],\n                            [126.908605, 51.407423],\n                            [126.835308, 51.413769],\n                            [126.791577, 51.432428],\n                            [126.784185, 51.448095],\n                            [126.812518, 51.493948],\n                            [126.843931, 51.521885],\n                            [126.837156, 51.536033],\n                            [126.69549, 51.57845],\n                            [126.67886, 51.602246],\n                            [126.741069, 51.642374],\n                            [126.723823, 51.679126],\n                            [126.734294, 51.711399],\n                            [126.724439, 51.7266],\n                            [126.6727, 51.73179],\n                            [126.658534, 51.762544],\n                            [126.622809, 51.777357],\n                            [126.580925, 51.824728],\n                            [126.555056, 51.874266],\n                            [126.510092, 51.922274],\n                            [126.462665, 51.948471],\n                            [126.468208, 51.982395],\n                            [126.447882, 52.009294],\n                            [126.450962, 52.027709],\n                            [126.487918, 52.041699],\n                            [126.514404, 52.037282],\n                            [126.563679, 52.119302],\n                            [126.556288, 52.136203],\n                            [126.499005, 52.16044],\n                            [126.457121, 52.165212],\n                            [126.403535, 52.185031],\n                            [126.34502, 52.192002],\n                            [126.306832, 52.205574],\n                            [126.312992, 52.235271],\n                            [126.357955, 52.264216],\n                            [126.401071, 52.279597],\n                            [126.436795, 52.277034],\n                            [126.4331, 52.298632],\n                            [126.327774, 52.310342],\n                            [126.320999, 52.342163],\n                            [126.348716, 52.357882],\n                            [126.353644, 52.389304],\n                            [126.326542, 52.424353],\n                            [126.268644, 52.475051],\n                            [126.205202, 52.466302],\n                            [126.192883, 52.492181],\n                            [126.213209, 52.525327],\n                            [126.147304, 52.573],\n                            [126.066616, 52.603905],\n                            [126.055529, 52.582455],\n                            [126.030891, 52.576273],\n                            [125.989008, 52.603178],\n                            [125.968682, 52.630429],\n                            [125.971145, 52.654033],\n                            [125.995783, 52.675085],\n                            [126.061688, 52.673271],\n                            [126.072775, 52.691048],\n                            [126.044442, 52.739628],\n                            [126.112195, 52.757016],\n                            [126.116507, 52.768243],\n                            [126.052449, 52.800095],\n                            [126.02042, 52.795753],\n                            [125.985312, 52.758465],\n                            [125.966834, 52.759914],\n                            [125.937269, 52.786705],\n                            [125.923718, 52.815651],\n                            [125.855349, 52.866259],\n                            [125.854117, 52.891542],\n                            [125.827631, 52.899123],\n                            [125.772197, 52.89804],\n                            [125.751255, 52.88143],\n                            [125.722306, 52.880347],\n                            [125.678574, 52.86084],\n                            [125.666871, 52.869872],\n                            [125.665023, 52.913561],\n                            [125.737088, 52.943504],\n                            [125.742632, 52.993964],\n                            [125.684118, 53.00801],\n                            [125.643466, 53.039686],\n                            [125.640386, 53.06199],\n                            [125.613901, 53.083564],\n                            [125.588647, 53.081047],\n                            [125.530749, 53.0512],\n                            [125.504263, 53.061271],\n                            [125.503647, 53.095424],\n                            [125.452524, 53.107641],\n                            [125.343503, 53.14463],\n                            [125.315786, 53.144989],\n                            [125.252344, 53.18051],\n                            [125.195062, 53.198439],\n                            [125.142091, 53.204175],\n                            [125.038613, 53.202741],\n                            [124.970244, 53.194137],\n                            [124.887708, 53.164368],\n                            [124.909266, 53.118059],\n                            [124.87231, 53.099018],\n                            [124.832889, 53.145347],\n                            [124.787926, 53.140681],\n                            [124.734339, 53.146783],\n                            [124.712165, 53.162574],\n                            [124.720789, 53.192344],\n                            [124.678905, 53.207043],\n                            [124.590209, 53.208476],\n                            [124.563108, 53.201666],\n                            [124.496587, 53.207759],\n                            [124.487348, 53.217436],\n                            [124.435609, 53.223886],\n                            [124.412203, 53.248601],\n                            [124.375863, 53.258984],\n                            [124.327819, 53.331954],\n                            [124.239124, 53.379817],\n                            [124.19416, 53.37339],\n                            [124.125791, 53.348033],\n                            [124.058038, 53.404085],\n                            [124.01369, 53.403371],\n                            [123.985973, 53.434401],\n                            [123.865249, 53.489627],\n                            [123.797495, 53.489983],\n                            [123.746373, 53.500308],\n                            [123.698329, 53.498528],\n                            [123.668764, 53.533756],\n                            [123.620721, 53.550115],\n                            [123.58746, 53.546915],\n                            [123.569598, 53.505291],\n                            [123.53141, 53.507071],\n                            [123.557895, 53.531978],\n                            [123.546808, 53.551537],\n                            [123.517243, 53.558292],\n                            [123.490758, 53.542648],\n                            [123.510468, 53.509206],\n                            [123.499381, 53.497816],\n                            [123.47228, 53.509206],\n                            [123.454417, 53.536602],\n                            [123.394055, 53.538024],\n                            [123.309672, 53.56078],\n                            [123.274563, 53.563269],\n                            [123.231447, 53.549404],\n                            [123.179092, 53.509918],\n                            [123.137209, 53.498172],\n                            [123.093477, 53.508138],\n                            [123.052209, 53.506715],\n                            [122.943804, 53.483929],\n                            [122.894528, 53.462914],\n                            [122.826775, 53.457213],\n                            [122.763949, 53.463626],\n                            [122.673406, 53.459351],\n                            [122.608117, 53.465408],\n                            [122.5379, 53.453293],\n                            [122.496016, 53.458638],\n                            [122.435038, 53.444739],\n                            [122.37406, 53.47467],\n                            [122.350038, 53.505647],\n                            [122.266886, 53.470039],\n                            [122.227466, 53.461845],\n                            [122.161561, 53.468614],\n                            [122.111054, 53.426913],\n                            [122.077177, 53.422277],\n                            [122.026054, 53.428339],\n                            [121.875765, 53.426556],\n                            [121.816019, 53.41336],\n                            [121.754425, 53.389454],\n                            [121.697758, 53.392666],\n                            [121.589969, 53.350891],\n                            [121.499426, 53.337314],\n                            [121.504969, 53.323018],\n                            [121.575802, 53.29155],\n                            [121.615222, 53.258984],\n                            [121.642324, 53.262564],\n                            [121.679896, 53.240722],\n                            [121.67928, 53.199515],\n                            [121.660186, 53.195213],\n                            [121.665114, 53.170467],\n                            [121.722396, 53.145706],\n                            [121.753193, 53.147501],\n                            [121.784606, 53.104408],\n                            [121.775367, 53.089674],\n                            [121.817867, 53.061631],\n                            [121.785838, 53.018451],\n                            [121.715621, 52.997926],\n                            [121.677432, 52.948192],\n                            [121.66265, 52.912478],\n                            [121.610295, 52.892264],\n                            [121.604136, 52.872401],\n                            [121.620766, 52.853251],\n                            [121.591201, 52.824693],\n                            [121.537614, 52.801542],\n                            [121.511129, 52.779104],\n                            [121.476636, 52.772225],\n                            [121.455078, 52.73528],\n                            [121.373158, 52.683067],\n                            [121.309717, 52.676173],\n                            [121.29247, 52.651855],\n                            [121.237036, 52.619167],\n                            [121.182217, 52.59918],\n                            [121.225333, 52.577364],\n                            [121.280151, 52.586819],\n                            [121.323883, 52.573727],\n                            [121.353448, 52.534793],\n                            [121.411963, 52.52205],\n                            [121.416274, 52.499468],\n                            [121.474172, 52.482706],\n                            [121.495114, 52.484892],\n                            [121.519136, 52.456821],\n                            [121.565331, 52.460468],\n                            [121.590585, 52.443326],\n                            [121.63986, 52.44442],\n                            [121.678664, 52.419973],\n                            [121.658338, 52.3904],\n                            [121.715621, 52.342894],\n                            [121.714389, 52.318025],\n                            [121.769207, 52.308147],\n                            [121.841272, 52.282526],\n                            [121.901018, 52.280695],\n                            [121.94783, 52.298266],\n                            [121.976779, 52.343626],\n                            [122.035909, 52.377615],\n                            [122.040837, 52.413038],\n                            [122.091344, 52.427272],\n                            [122.080873, 52.440407],\n                            [122.107358, 52.452445],\n                            [122.142467, 52.495096],\n                            [122.140003, 52.510032],\n                            [122.168952, 52.513674],\n                            [122.178191, 52.48963],\n                            [122.207756, 52.469218],\n                            [122.310618, 52.475416],\n                            [122.326016, 52.459374],\n                            [122.342031, 52.414133],\n                            [122.367284, 52.413768],\n                            [122.378987, 52.395512],\n                            [122.419023, 52.375057],\n                            [122.447356, 52.394052],\n                            [122.484313, 52.341432],\n                            [122.478153, 52.29607],\n                            [122.560689, 52.282526],\n                            [122.585943, 52.266413],\n                            [122.67895, 52.276667],\n                            [122.710979, 52.256157],\n                            [122.76087, 52.26678],\n                            [122.787355, 52.252494],\n                            [122.766413, 52.232705],\n                            [122.769493, 52.179893],\n                            [122.73808, 52.153464],\n                            [122.690653, 52.140243],\n                            [122.629059, 52.13657],\n                            [122.643841, 52.111585],\n                            [122.625363, 52.067459],\n                            [122.650616, 52.058997],\n                            [122.664783, 51.99861],\n                            [122.683877, 51.974654],\n                            [122.726377, 51.978709],\n                            [122.729457, 51.919321],\n                            [122.706051, 51.890151],\n                            [122.725761, 51.87833],\n                            [122.732536, 51.832495],\n                            [122.771957, 51.779579],\n                            [122.749167, 51.746613],\n                            [122.778732, 51.698048],\n                            [122.816304, 51.655371],\n                            [122.820616, 51.633088],\n                            [122.85634, 51.606707],\n                            [122.832935, 51.581797],\n                            [122.874202, 51.561339],\n                            [122.880362, 51.537894],\n                            [122.858804, 51.524864],\n                            [122.880362, 51.511085],\n                            [122.854492, 51.477551],\n                            [122.871123, 51.455181],\n                            [122.900072, 51.445112],\n                            [122.903768, 51.415262],\n                            [122.946267, 51.405183],\n                            [122.965977, 51.386886],\n                            [122.965977, 51.345786],\n                            [123.002934, 51.31213],\n                            [123.069455, 51.321108],\n                            [123.127969, 51.297913],\n                            [123.231447, 51.279199],\n                            [123.231447, 51.268716],\n                            [123.294273, 51.254111],\n                            [123.339853, 51.27246],\n                            [123.376809, 51.266844],\n                            [123.414381, 51.278825],\n                            [123.440251, 51.270963],\n                            [123.46304, 51.286686],\n                            [123.582533, 51.294545],\n                            [123.582533, 51.306893],\n                            [123.661989, 51.319237],\n                            [123.660141, 51.342795],\n                            [123.711264, 51.398089],\n                            [123.794416, 51.361109],\n                            [123.842459, 51.367462],\n                            [123.887423, 51.320734],\n                            [123.926227, 51.300532],\n                            [123.939777, 51.313253],\n                            [123.994596, 51.322604],\n                            [124.071588, 51.320734],\n                            [124.090067, 51.3413],\n                            [124.128255, 51.347281],\n                            [124.192313, 51.33943],\n                            [124.239124, 51.344664],\n                            [124.271769, 51.308389],\n                            [124.297638, 51.298661],\n                            [124.339522, 51.293422],\n                            [124.406659, 51.272086],\n                            [124.430065, 51.301281],\n                            [124.426985, 51.331953],\n                            [124.443616, 51.35812],\n                            [124.478108, 51.36223],\n                            [124.490427, 51.380537],\n                            [124.555717, 51.375307],\n                            [124.58713, 51.363725],\n                            [124.62655, 51.327465],\n                            [124.693687, 51.3327],\n                            [124.752817, 51.35812],\n                            [124.76452, 51.38726],\n                            [124.783614, 51.392115],\n                            [124.864302, 51.37979],\n                            [124.885244, 51.40817],\n                            [124.942527, 51.447349],\n                            [124.917889, 51.474196],\n                            [124.928976, 51.498419],\n                            [124.983795, 51.508478],\n                            [125.004737, 51.529332],\n                            [125.047236, 51.529704],\n                            [125.073106, 51.553526],\n                            [125.060171, 51.59667],\n                            [125.098975, 51.658341],\n                            [125.12854, 51.659083],\n                            [125.130388, 51.635317],\n                            [125.175968, 51.639403],\n                            [125.214772, 51.627888],\n                            [125.228938, 51.640517],\n                            [125.289301, 51.633831],\n                            [125.316402, 51.610052],\n                            [125.35151, 51.623801],\n                            [125.38046, 51.585516],\n                            [125.424807, 51.562827],\n                            [125.528285, 51.488359],\n                            [125.559082, 51.461521],\n                            [125.559082, 51.461521],\n                            [125.595422, 51.416755],\n                            [125.595422, 51.416755],\n                            [125.60035, 51.413396],\n                            [125.60035, 51.413396],\n                            [125.600966, 51.410409],\n                            [125.600966, 51.410409],\n                            [125.62314, 51.398089],\n                            [125.62314, 51.398089],\n                            [125.623756, 51.387633],\n                            [125.623756, 51.387633],\n                            [125.626219, 51.380163],\n                            [125.626219, 51.380163],\n                            [125.700132, 51.327465],\n                            [125.700132, 51.327465],\n                            [125.740784, 51.27583],\n                            [125.740784, 51.27583],\n                            [125.76111, 51.261976],\n                            [125.76111, 51.261976],\n                            [125.761726, 51.226385],\n                            [125.819008, 51.227134],\n                            [125.850421, 51.21364],\n                            [125.864588, 51.146487],\n                            [125.909551, 51.138977],\n                            [125.946508, 51.108176],\n                            [125.970529, 51.123955],\n                            [125.993935, 51.119072],\n                            [125.976073, 51.084498],\n                            [126.059225, 51.043503],\n                            [126.033971, 51.011132],\n                            [126.041978, 50.981753],\n                            [126.068464, 50.967434],\n                            [126.042594, 50.92558],\n                            [126.02042, 50.927466],\n                            [125.996399, 50.906715],\n                            [125.997631, 50.872738],\n                            [125.961906, 50.901054],\n                            [125.939732, 50.85423],\n                            [125.913247, 50.825885],\n                            [125.878138, 50.816812],\n                            [125.890457, 50.805845],\n                            [125.836255, 50.793363],\n                            [125.846726, 50.769524],\n                            [125.828863, 50.756654],\n                            [125.804226, 50.773309],\n                            [125.758646, 50.746809],\n                            [125.795603, 50.738856],\n                            [125.78082, 50.725598],\n                            [125.825784, 50.70362],\n                            [125.789443, 50.679735],\n                            [125.804226, 50.658874],\n                            [125.793139, 50.643316],\n                            [125.814697, 50.62092],\n                            [125.807921, 50.60383],\n                            [125.829479, 50.56165],\n                            [125.794987, 50.532748],\n                            [125.770349, 50.531227],\n                            [125.754335, 50.506874],\n                            [125.740784, 50.523237],\n                            [125.699516, 50.487078],\n                            [125.654553, 50.471082],\n                            [125.627451, 50.443268],\n                            [125.580024, 50.449366],\n                            [125.562162, 50.438314],\n                            [125.583104, 50.409717],\n                            [125.567089, 50.402852],\n                            [125.536292, 50.420014],\n                            [125.522126, 50.404759],\n                            [125.546763, 50.358965],\n                            [125.520278, 50.3498],\n                            [125.530749, 50.331085],\n                            [125.463611, 50.295925],\n                            [125.466075, 50.266861],\n                            [125.442053, 50.260357],\n                            [125.448829, 50.216338],\n                            [125.417416, 50.195654],\n                            [125.39093, 50.199868],\n                            [125.382923, 50.172278],\n                            [125.335496, 50.161161],\n                            [125.376148, 50.137385],\n                            [125.311474, 50.140453],\n                            [125.27883, 50.127411],\n                            [125.258504, 50.103618],\n                            [125.287453, 50.093636],\n                            [125.283757, 50.070211],\n                            [125.328105, 50.065985],\n                            [125.315786, 50.04562],\n                            [125.289916, 50.057917],\n                            [125.25296, 50.041393],\n                            [125.283757, 50.036012],\n                            [125.297924, 50.014481],\n                            [125.278214, 49.996402],\n                            [125.241873, 49.987938],\n                            [125.231402, 49.957531],\n                            [125.190134, 49.959841],\n                            [125.199373, 49.935194],\n                            [125.225859, 49.922481],\n                            [125.212924, 49.907452],\n                            [125.245569, 49.87198],\n                            [125.225243, 49.867351],\n                            [125.239409, 49.844587],\n                            [125.177815, 49.829533],\n                            [125.222779, 49.799026],\n                            [125.221547, 49.754969],\n                            [125.204301, 49.734086],\n                            [125.225243, 49.726349],\n                            [125.219699, 49.669058],\n                            [125.185207, 49.634574],\n                            [125.189518, 49.652401],\n                            [125.164881, 49.669446],\n                            [125.132236, 49.672157],\n                            [125.127308, 49.655113],\n                            [125.15441, 49.616741],\n                            [125.16796, 49.629923],\n                            [125.205533, 49.593859],\n                            [125.23017, 49.595411],\n                            [125.233866, 49.536801],\n                            [125.211076, 49.539908],\n                            [125.228323, 49.487063],\n                            [125.270822, 49.454395],\n                            [125.256656, 49.437275],\n                            [125.25604, 49.395227],\n                            [125.277598, 49.379644],\n                            [125.256656, 49.359769],\n                            [125.261583, 49.322336],\n                            [125.214772, 49.277066],\n                            [125.227707, 49.248947],\n                            [125.219699, 49.189139],\n                            [125.187671, 49.186792],\n                            [125.158721, 49.144921],\n                            [125.117453, 49.126127],\n                            [125.034302, 49.157056],\n                            [125.039845, 49.17623],\n                            [124.983179, 49.162535],\n                            [124.906802, 49.184054],\n                            [124.860607, 49.166448],\n                            [124.847672, 49.129651],\n                            [124.809484, 49.115943],\n                            [124.828578, 49.077933],\n                            [124.808252, 49.020666],\n                            [124.756513, 48.967262],\n                            [124.744194, 48.920487],\n                            [124.709086, 48.920487],\n                            [124.715861, 48.885475],\n                            [124.697383, 48.841775],\n                            [124.654267, 48.83429],\n                            [124.644412, 48.80789],\n                            [124.656115, 48.783842],\n                            [124.612383, 48.747945],\n                            [124.624702, 48.701755],\n                            [124.601912, 48.632587],\n                            [124.579122, 48.596582],\n                            [124.520608, 48.556195],\n                            [124.548941, 48.535593],\n                            [124.533543, 48.515379],\n                            [124.555717, 48.467784],\n                            [124.507674, 48.445558],\n                            [124.52492, 48.426897],\n                            [124.51876, 48.378027],\n                            [124.547094, 48.35775],\n                            [124.540934, 48.335476],\n                            [124.579738, 48.297269],\n                            [124.558796, 48.268197],\n                            [124.579122, 48.262221],\n                            [124.547094, 48.200829],\n                            [124.512601, 48.164518],\n                            [124.529847, 48.146951],\n                            [124.505826, 48.124985],\n                            [124.478108, 48.123387],\n                            [124.46579, 48.098213],\n                            [124.415899, 48.08782],\n                            [124.430065, 48.12099],\n                            [124.471333, 48.133373],\n                            [124.475029, 48.173698],\n                            [124.418978, 48.181679],\n                            [124.412819, 48.219175],\n                            [124.422058, 48.245884],\n                            [124.365392, 48.283731],\n                            [124.353689, 48.315978],\n                            [124.317964, 48.35099],\n                            [124.331515, 48.380015],\n                            [124.309957, 48.413393],\n                            [124.330283, 48.435633],\n                            [124.302566, 48.456673],\n                            [124.314269, 48.503881],\n                            [124.25945, 48.536385],\n                            [124.25945, 48.536385],\n                            [124.136878, 48.463023],\n                            [124.07898, 48.43603],\n                            [124.019234, 48.39313],\n                            [123.862785, 48.271782],\n                            [123.746373, 48.197638],\n                            [123.705105, 48.152142],\n                            [123.579453, 48.045427],\n                            [123.537569, 48.021816],\n                            [123.300432, 47.953723],\n                            [123.256085, 47.876711],\n                            [123.214201, 47.824502],\n                            [123.161846, 47.781892],\n                            [123.041122, 47.746492],\n                            [122.926557, 47.697777],\n                            [122.848949, 47.67441],\n                            [122.765181, 47.614333],\n                            [122.59395, 47.54732],\n                            [122.543443, 47.495589],\n                            [122.507103, 47.401291],\n                            [122.418407, 47.350632],\n                            [122.441197, 47.310476],\n                            [122.441197, 47.310476],\n                            [122.462755, 47.27841],\n                            [122.498479, 47.255262],\n                            [122.531124, 47.198771],\n                            [122.582863, 47.158092],\n                            [122.582863, 47.158092],\n                            [122.615508, 47.124306],\n                            [122.679566, 47.094164],\n                            [122.710363, 47.093349],\n                            [122.710363, 47.093349],\n                            [122.821232, 47.065636],\n                            [122.852645, 47.072158],\n                            [122.845869, 47.046881],\n                            [122.778116, 47.002822],\n                            [122.77442, 46.973837],\n                            [122.798442, 46.9575],\n                            [122.791051, 46.941567],\n                            [122.83971, 46.937072],\n                            [122.895144, 46.960359],\n                            [122.893913, 46.895376],\n                            [122.906847, 46.80738],\n                            [122.996774, 46.761483],\n                            [123.00355, 46.730726],\n                            [123.026339, 46.718829],\n                            [123.076846, 46.745082],\n                            [123.103332, 46.734828],\n                            [123.163694, 46.74016],\n                            [123.198802, 46.803283],\n                            [123.22344, 46.821305],\n                            [123.221592, 46.850373],\n                            [123.295505, 46.865105],\n                            [123.341084, 46.826628],\n                            [123.374345, 46.837683],\n                            [123.40699, 46.906416],\n                            [123.404526, 46.935438],\n                            [123.360179, 46.970978],\n                            [123.304128, 46.964852],\n                            [123.301664, 46.999965],\n                            [123.337389, 46.988943],\n                            [123.42362, 46.934212],\n                            [123.487678, 46.959951],\n                            [123.52833, 46.944836],\n                            [123.483366, 46.84587],\n                            [123.506772, 46.827038],\n                            [123.562823, 46.82581],\n                            [123.575757, 46.845461],\n                            [123.576989, 46.891286],\n                            [123.605322, 46.891286],\n                            [123.599163, 46.868378],\n                            [123.625648, 46.847508],\n                            [123.580069, 46.827447],\n                            [123.629344, 46.813524],\n                            [123.631808, 46.728675],\n                            [123.603475, 46.68928],\n                            [123.474743, 46.686817],\n                            [123.366338, 46.677784],\n                            [123.318295, 46.662179],\n                            [123.276411, 46.660947],\n                            [123.279491, 46.616981],\n                            [123.228368, 46.588198],\n                            [123.18094, 46.614103],\n                            [123.098404, 46.603002],\n                            [123.077462, 46.622324],\n                            [123.04605, 46.617803],\n                            [123.052825, 46.579972],\n                            [123.002318, 46.574624],\n                            [123.010325, 46.524823],\n                            [123.011557, 46.434984],\n                            [123.089781, 46.347888],\n                            [123.142136, 46.298293],\n                            [123.178476, 46.248239],\n                            [123.248078, 46.273065],\n                            [123.286266, 46.250308],\n                            [123.320758, 46.254447],\n                            [123.357099, 46.232096],\n                            [123.357099, 46.232096],\n                            [123.430396, 46.243687],\n                            [123.452569, 46.233338],\n                            [123.499381, 46.259826],\n                            [123.569598, 46.223816],\n                            [123.569598, 46.223816]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 310000,\n                \"name\": \"Shanghai\",\n                \"center\": [121.472644, 31.231706],\n                \"centroid\": [121.438737, 31.072559],\n                \"childrenNum\": 16,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 8,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [120.901349, 31.017327],\n                            [120.940153, 31.010146],\n                            [120.949392, 31.030148],\n                            [120.989428, 31.01425],\n                            [121.000515, 30.938309],\n                            [120.993124, 30.889532],\n                            [121.020225, 30.872069],\n                            [120.991892, 30.837133],\n                            [121.038087, 30.814007],\n                            [121.060261, 30.845354],\n                            [121.097833, 30.857171],\n                            [121.13787, 30.826342],\n                            [121.123087, 30.77905],\n                            [121.174826, 30.771851],\n                            [121.21671, 30.785734],\n                            [121.232108, 30.755909],\n                            [121.272144, 30.723504],\n                            [121.274608, 30.677191],\n                            [121.362071, 30.679764],\n                            [121.426129, 30.730192],\n                            [121.517288, 30.775451],\n                            [121.601056, 30.805269],\n                            [121.681128, 30.818633],\n                            [121.904714, 30.814007],\n                            [121.943518, 30.776993],\n                            [121.970004, 30.789333],\n                            [121.954605, 30.825828],\n                            [121.994025, 30.862823],\n                            [121.990945, 30.96859],\n                            [121.977395, 31.016301],\n                            [121.946598, 31.066039],\n                            [121.809859, 31.196669],\n                            [121.722396, 31.3036],\n                            [121.599208, 31.37465],\n                            [121.520984, 31.394575],\n                            [121.404571, 31.479337],\n                            [121.343593, 31.511996],\n                            [121.301093, 31.49873],\n                            [121.301093, 31.49873],\n                            [121.247507, 31.476785],\n                            [121.241963, 31.493117],\n                            [121.174826, 31.44922],\n                            [121.143413, 31.392021],\n                            [121.113848, 31.37465],\n                            [121.130478, 31.343987],\n                            [121.142797, 31.275472],\n                            [121.090442, 31.291838],\n                            [121.060261, 31.245289],\n                            [121.076892, 31.158267],\n                            [121.018377, 31.134194],\n                            [120.930298, 31.141365],\n                            [120.881023, 31.134706],\n                            [120.859465, 31.100379],\n                            [120.890878, 31.094229],\n                            [120.901349, 31.017327]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.974931, 31.61704],\n                            [121.715005, 31.673592],\n                            [121.64294, 31.697527],\n                            [121.599824, 31.703128],\n                            [121.49881, 31.753012],\n                            [121.431673, 31.769295],\n                            [121.384861, 31.833382],\n                            [121.323267, 31.868458],\n                            [121.265369, 31.863883],\n                            [121.200079, 31.834907],\n                            [121.118775, 31.759119],\n                            [121.145261, 31.75403],\n                            [121.289391, 31.61653],\n                            [121.371926, 31.553314],\n                            [121.395332, 31.585437],\n                            [121.434136, 31.590535],\n                            [121.547469, 31.531382],\n                            [121.625693, 31.501792],\n                            [121.682976, 31.491075],\n                            [121.819098, 31.437987],\n                            [121.890547, 31.428795],\n                            [121.981706, 31.464024],\n                            [121.995873, 31.493117],\n                            [121.974931, 31.61704]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.795693, 31.330186],\n                            [121.792613, 31.363408],\n                            [121.742106, 31.407345],\n                            [121.585657, 31.454836],\n                            [121.567179, 31.48342],\n                            [121.520984, 31.494137],\n                            [121.509897, 31.4824],\n                            [121.572107, 31.435944],\n                            [121.727939, 31.35472],\n                            [121.76428, 31.31536],\n                            [121.785222, 31.31127],\n                            [121.795693, 31.330186]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.801852, 31.356765],\n                            [121.8037, 31.328652],\n                            [121.840656, 31.295418],\n                            [121.932431, 31.283144],\n                            [122.016199, 31.282121],\n                            [122.097503, 31.255522],\n                            [122.122756, 31.307179],\n                            [122.116597, 31.320984],\n                            [122.040837, 31.324051],\n                            [121.951525, 31.337343],\n                            [121.845584, 31.37465],\n                            [121.792613, 31.377715],\n                            [121.801852, 31.356765]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.626925, 31.445135],\n                            [121.631853, 31.456878],\n                            [121.579498, 31.479848],\n                            [121.626925, 31.445135]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.943518, 31.215608],\n                            [121.959533, 31.159291],\n                            [121.995873, 31.160828],\n                            [122.008808, 31.221238],\n                            [121.950909, 31.228915],\n                            [121.943518, 31.215608]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.88254, 31.240684],\n                            [121.909026, 31.195133],\n                            [121.923808, 31.234032],\n                            [121.88254, 31.240684]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 320000,\n                \"name\": \"Jiangsu\",\n                \"center\": [118.767413, 32.041544],\n                \"centroid\": [119.486506, 32.983991],\n                \"childrenNum\": 13,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 9,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [117.311654, 34.561686],\n                            [117.27285, 34.556757],\n                            [117.303647, 34.542463],\n                            [117.267923, 34.532603],\n                            [117.27285, 34.499565],\n                            [117.252524, 34.48674],\n                            [117.248213, 34.451216],\n                            [117.166293, 34.434435],\n                            [117.139191, 34.526687],\n                            [117.15151, 34.559222],\n                            [117.104083, 34.648874],\n                            [117.073286, 34.639026],\n                            [117.061583, 34.675947],\n                            [117.070206, 34.713835],\n                            [117.022163, 34.759081],\n                            [116.969192, 34.771864],\n                            [116.95133, 34.81069],\n                            [116.979047, 34.815113],\n                            [116.966113, 34.844588],\n                            [116.929156, 34.843114],\n                            [116.922381, 34.894671],\n                            [116.858323, 34.928533],\n                            [116.821983, 34.929515],\n                            [116.815823, 34.965324],\n                            [116.789338, 34.975133],\n                            [116.781331, 34.916757],\n                            [116.677853, 34.939327],\n                            [116.622418, 34.939818],\n                            [116.613795, 34.922645],\n                            [116.557745, 34.908905],\n                            [116.445028, 34.895652],\n                            [116.408071, 34.850972],\n                            [116.403144, 34.756131],\n                            [116.369267, 34.749247],\n                            [116.363724, 34.715311],\n                            [116.392057, 34.710391],\n                            [116.374195, 34.640011],\n                            [116.430245, 34.650843],\n                            [116.432709, 34.630163],\n                            [116.477057, 34.614896],\n                            [116.490607, 34.573513],\n                            [116.594085, 34.511894],\n                            [116.592237, 34.493646],\n                            [116.662454, 34.472927],\n                            [116.722816, 34.472434],\n                            [116.773939, 34.453683],\n                            [116.782563, 34.429993],\n                            [116.828142, 34.389012],\n                            [116.909446, 34.408271],\n                            [116.969192, 34.389012],\n                            [116.960569, 34.363821],\n                            [116.983359, 34.348011],\n                            [116.969192, 34.283753],\n                            [117.051112, 34.221425],\n                            [117.025243, 34.167469],\n                            [117.046801, 34.151622],\n                            [117.123793, 34.128342],\n                            [117.130568, 34.101586],\n                            [117.192162, 34.068873],\n                            [117.257452, 34.065899],\n                            [117.277162, 34.078787],\n                            [117.311654, 34.067882],\n                            [117.357234, 34.088205],\n                            [117.404045, 34.03218],\n                            [117.435458, 34.028212],\n                            [117.514914, 34.060941],\n                            [117.543248, 34.038627],\n                            [117.569117, 33.985051],\n                            [117.612849, 34.000433],\n                            [117.629479, 34.028708],\n                            [117.671363, 33.992494],\n                            [117.672595, 33.934916],\n                            [117.715095, 33.879287],\n                            [117.753899, 33.891211],\n                            [117.759442, 33.874318],\n                            [117.739732, 33.758467],\n                            [117.72495, 33.74951],\n                            [117.750203, 33.710688],\n                            [117.791471, 33.733585],\n                            [117.843826, 33.736074],\n                            [117.901724, 33.720146],\n                            [117.972557, 33.74951],\n                            [118.019985, 33.738562],\n                            [118.065564, 33.76593],\n                            [118.117919, 33.766427],\n                            [118.161035, 33.735576],\n                            [118.16781, 33.663381],\n                            [118.112376, 33.617045],\n                            [118.117919, 33.594615],\n                            [118.107448, 33.475391],\n                            [118.050782, 33.491863],\n                            [118.027376, 33.455421],\n                            [118.016905, 33.402978],\n                            [118.029224, 33.374995],\n                            [117.992883, 33.333005],\n                            [117.974405, 33.279487],\n                            [117.939297, 33.262475],\n                            [117.942376, 33.224936],\n                            [117.977485, 33.226437],\n                            [117.988572, 33.180869],\n                            [118.037231, 33.152314],\n                            [118.038463, 33.134776],\n                            [118.149332, 33.169348],\n                            [118.178281, 33.217926],\n                            [118.217085, 33.191888],\n                            [118.219549, 33.114227],\n                            [118.243571, 33.027967],\n                            [118.244803, 32.998359],\n                            [118.26944, 32.969242],\n                            [118.303933, 32.96874],\n                            [118.291614, 32.946143],\n                            [118.252194, 32.936601],\n                            [118.2331, 32.914498],\n                            [118.250346, 32.848157],\n                            [118.301469, 32.846145],\n                            [118.300237, 32.783275],\n                            [118.334114, 32.761637],\n                            [118.363063, 32.770695],\n                            [118.375382, 32.718849],\n                            [118.411106, 32.715828],\n                            [118.450526, 32.743518],\n                            [118.483787, 32.721367],\n                            [118.560163, 32.729926],\n                            [118.572482, 32.719856],\n                            [118.642699, 32.744525],\n                            [118.707373, 32.72036],\n                            [118.756648, 32.737477],\n                            [118.73817, 32.772708],\n                            [118.743097, 32.853184],\n                            [118.743097, 32.853184],\n                            [118.810235, 32.853687],\n                            [118.821322, 32.920527],\n                            [118.846575, 32.922034],\n                            [118.849039, 32.956689],\n                            [118.89585, 32.957694],\n                            [118.89585, 32.957694],\n                            [118.892771, 32.941121],\n                            [118.934039, 32.93861],\n                            [118.993169, 32.958196],\n                            [119.020886, 32.955685],\n                            [119.054763, 32.8748],\n                            [119.113277, 32.823014],\n                            [119.184726, 32.825529],\n                            [119.211827, 32.708275],\n                            [119.208748, 32.641276],\n                            [119.230921, 32.607001],\n                            [119.22045, 32.576748],\n                            [119.152697, 32.557582],\n                            [119.168096, 32.536394],\n                            [119.142226, 32.499556],\n                            [119.084944, 32.452602],\n                            [119.041212, 32.515201],\n                            [118.975923, 32.505108],\n                            [118.922336, 32.557078],\n                            [118.92172, 32.557078],\n                            [118.922336, 32.557078],\n                            [118.92172, 32.557078],\n                            [118.890923, 32.553042],\n                            [118.908169, 32.59238],\n                            [118.84288, 32.56767],\n                            [118.820706, 32.60448],\n                            [118.784981, 32.582295],\n                            [118.757264, 32.603976],\n                            [118.73509, 32.58885],\n                            [118.719076, 32.614059],\n                            [118.719076, 32.614059],\n                            [118.688895, 32.588346],\n                            [118.658714, 32.594397],\n                            [118.632844, 32.578261],\n                            [118.59712, 32.600951],\n                            [118.568787, 32.585825],\n                            [118.564475, 32.562122],\n                            [118.608823, 32.536899],\n                            [118.592192, 32.481383],\n                            [118.628533, 32.467751],\n                            [118.691359, 32.472295],\n                            [118.685199, 32.403604],\n                            [118.703061, 32.328792],\n                            [118.657482, 32.30148],\n                            [118.674728, 32.250375],\n                            [118.643931, 32.209875],\n                            [118.510888, 32.194176],\n                            [118.49549, 32.165304],\n                            [118.501033, 32.121726],\n                            [118.433896, 32.086746],\n                            [118.394476, 32.076098],\n                            [118.389548, 31.985281],\n                            [118.363679, 31.930443],\n                            [118.472084, 31.879639],\n                            [118.466541, 31.857784],\n                            [118.504729, 31.841516],\n                            [118.481939, 31.778453],\n                            [118.533678, 31.76726],\n                            [118.521975, 31.743343],\n                            [118.5577, 31.73011],\n                            [118.571866, 31.746397],\n                            [118.641467, 31.75861],\n                            [118.653786, 31.73011],\n                            [118.697518, 31.709747],\n                            [118.643315, 31.671555],\n                            [118.643315, 31.649651],\n                            [118.736322, 31.633347],\n                            [118.748025, 31.675629],\n                            [118.773894, 31.682759],\n                            [118.802844, 31.619078],\n                            [118.858894, 31.623665],\n                            [118.881684, 31.564023],\n                            [118.885995, 31.519139],\n                            [118.883532, 31.500261],\n                            [118.852119, 31.393553],\n                            [118.824401, 31.375672],\n                            [118.767735, 31.363919],\n                            [118.745561, 31.372606],\n                            [118.720924, 31.322518],\n                            [118.726467, 31.282121],\n                            [118.756648, 31.279564],\n                            [118.794836, 31.229426],\n                            [118.870597, 31.242219],\n                            [118.984546, 31.237102],\n                            [119.014727, 31.241707],\n                            [119.10527, 31.235055],\n                            [119.107118, 31.250917],\n                            [119.158241, 31.294907],\n                            [119.197661, 31.295418],\n                            [119.198277, 31.270357],\n                            [119.266646, 31.250405],\n                            [119.294363, 31.263195],\n                            [119.338095, 31.259103],\n                            [119.350414, 31.301043],\n                            [119.374435, 31.258591],\n                            [119.360269, 31.213049],\n                            [119.391682, 31.174142],\n                            [119.439109, 31.177214],\n                            [119.461283, 31.156219],\n                            [119.532732, 31.159291],\n                            [119.599869, 31.10909],\n                            [119.623891, 31.130096],\n                            [119.678093, 31.167997],\n                            [119.705811, 31.152634],\n                            [119.715666, 31.169533],\n                            [119.779723, 31.17875],\n                            [119.809904, 31.148536],\n                            [119.827151, 31.174142],\n                            [119.878274, 31.160828],\n                            [119.921389, 31.170045],\n                            [119.946027, 31.106016],\n                            [119.988527, 31.059375],\n                            [120.001461, 31.027071],\n                            [120.052584, 31.00553],\n                            [120.111099, 30.955761],\n                            [120.149903, 30.937283],\n                            [120.223816, 30.926502],\n                            [120.316206, 30.933689],\n                            [120.371025, 30.948575],\n                            [120.35809, 30.886964],\n                            [120.42338, 30.902884],\n                            [120.435083, 30.920855],\n                            [120.441858, 30.860768],\n                            [120.460336, 30.839702],\n                            [120.489285, 30.763624],\n                            [120.504684, 30.757967],\n                            [120.563814, 30.835592],\n                            [120.589684, 30.854089],\n                            [120.654973, 30.846896],\n                            [120.68269, 30.882342],\n                            [120.713487, 30.88491],\n                            [120.709176, 30.933176],\n                            [120.684538, 30.955247],\n                            [120.698089, 30.970643],\n                            [120.746132, 30.962432],\n                            [120.770154, 30.996809],\n                            [120.820661, 31.006556],\n                            [120.865624, 30.989627],\n                            [120.901349, 31.017327],\n                            [120.890878, 31.094229],\n                            [120.859465, 31.100379],\n                            [120.881023, 31.134706],\n                            [120.930298, 31.141365],\n                            [121.018377, 31.134194],\n                            [121.076892, 31.158267],\n                            [121.060261, 31.245289],\n                            [121.090442, 31.291838],\n                            [121.142797, 31.275472],\n                            [121.130478, 31.343987],\n                            [121.113848, 31.37465],\n                            [121.143413, 31.392021],\n                            [121.174826, 31.44922],\n                            [121.241963, 31.493117],\n                            [121.247507, 31.476785],\n                            [121.301093, 31.49873],\n                            [121.301093, 31.49873],\n                            [121.343593, 31.511996],\n                            [121.371926, 31.553314],\n                            [121.289391, 31.61653],\n                            [121.145261, 31.75403],\n                            [121.118775, 31.759119],\n                            [121.200079, 31.834907],\n                            [121.265369, 31.863883],\n                            [121.323267, 31.868458],\n                            [121.384861, 31.833382],\n                            [121.431673, 31.769295],\n                            [121.49881, 31.753012],\n                            [121.599824, 31.703128],\n                            [121.64294, 31.697527],\n                            [121.715005, 31.673592],\n                            [121.974931, 31.61704],\n                            [121.970004, 31.718911],\n                            [121.889315, 31.866425],\n                            [121.856055, 31.955328],\n                            [121.772287, 32.032984],\n                            [121.759352, 32.059362],\n                            [121.525295, 32.136423],\n                            [121.542542, 32.152132],\n                            [121.458774, 32.177462],\n                            [121.499426, 32.211394],\n                            [121.493882, 32.263533],\n                            [121.450151, 32.282256],\n                            [121.425513, 32.430885],\n                            [121.390405, 32.460682],\n                            [121.352216, 32.474315],\n                            [121.269681, 32.483402],\n                            [121.153268, 32.52933],\n                            [121.121855, 32.569183],\n                            [121.076892, 32.576243],\n                            [121.020225, 32.605489],\n                            [120.961711, 32.612042],\n                            [120.979573, 32.636236],\n                            [120.963559, 32.68259],\n                            [120.916131, 32.701225],\n                            [120.953088, 32.714318],\n                            [120.972182, 32.761134],\n                            [120.981421, 32.85972],\n                            [120.957399, 32.893395],\n                            [120.932762, 33.005887],\n                            [120.917979, 33.02596],\n                            [120.871784, 33.047032],\n                            [120.874247, 33.093672],\n                            [120.843451, 33.209915],\n                            [120.819429, 33.237951],\n                            [120.833595, 33.274984],\n                            [120.813885, 33.303499],\n                            [120.769538, 33.307],\n                            [120.741205, 33.337505],\n                            [120.717183, 33.436945],\n                            [120.680227, 33.520306],\n                            [120.622944, 33.615051],\n                            [120.611241, 33.627012],\n                            [120.583524, 33.668362],\n                            [120.534249, 33.782346],\n                            [120.48559, 33.859411],\n                            [120.367329, 34.091674],\n                            [120.347619, 34.179352],\n                            [120.314359, 34.255563],\n                            [120.311895, 34.306991],\n                            [120.103707, 34.391481],\n                            [119.962657, 34.459112],\n                            [119.811752, 34.485754],\n                            [119.781571, 34.515839],\n                            [119.641137, 34.569078],\n                            [119.610956, 34.592729],\n                            [119.569072, 34.615389],\n                            [119.465594, 34.672994],\n                            [119.525956, 34.73351],\n                            [119.456971, 34.748264],\n                            [119.381827, 34.752198],\n                            [119.494543, 34.754656],\n                            [119.497007, 34.754164],\n                            [119.439725, 34.785136],\n                            [119.440957, 34.769406],\n                            [119.378747, 34.764489],\n                            [119.312841, 34.774813],\n                            [119.272189, 34.797914],\n                            [119.238313, 34.799388],\n                            [119.217371, 34.827886],\n                            [119.202588, 34.890253],\n                            [119.214907, 34.925589],\n                            [119.211211, 34.981507],\n                            [119.238313, 35.048657],\n                            [119.285124, 35.068252],\n                            [119.291899, 35.028567],\n                            [119.307298, 35.032977],\n                            [119.292515, 35.068742],\n                            [119.306066, 35.076578],\n                            [119.286972, 35.115261],\n                            [119.250016, 35.124562],\n                            [119.217371, 35.106939],\n                            [119.137915, 35.096167],\n                            [119.114509, 35.055026],\n                            [119.027045, 35.055516],\n                            [118.942662, 35.040817],\n                            [118.928495, 35.051106],\n                            [118.86259, 35.025626],\n                            [118.860742, 34.944233],\n                            [118.805307, 34.87307],\n                            [118.80038, 34.843114],\n                            [118.772047, 34.794474],\n                            [118.739402, 34.792508],\n                            [118.719076, 34.745313],\n                            [118.764039, 34.740396],\n                            [118.783749, 34.723181],\n                            [118.739402, 34.693663],\n                            [118.690127, 34.678408],\n                            [118.664257, 34.693663],\n                            [118.607591, 34.694155],\n                            [118.601431, 34.714327],\n                            [118.545997, 34.705964],\n                            [118.460997, 34.656258],\n                            [118.473932, 34.623269],\n                            [118.439439, 34.626223],\n                            [118.424657, 34.595193],\n                            [118.439439, 34.507949],\n                            [118.416034, 34.473914],\n                            [118.404947, 34.427525],\n                            [118.379693, 34.415183],\n                            [118.290382, 34.424563],\n                            [118.277447, 34.404814],\n                            [118.220165, 34.405802],\n                            [118.217701, 34.379134],\n                            [118.179513, 34.379628],\n                            [118.177665, 34.45319],\n                            [118.132702, 34.483287],\n                            [118.16473, 34.50499],\n                            [118.185056, 34.543942],\n                            [118.079115, 34.569571],\n                            [118.114839, 34.614404],\n                            [118.084042, 34.655766],\n                            [118.053861, 34.650843],\n                            [117.951615, 34.678408],\n                            [117.909732, 34.670533],\n                            [117.902956, 34.644443],\n                            [117.793935, 34.651827],\n                            [117.791471, 34.583368],\n                            [117.801942, 34.518798],\n                            [117.684298, 34.547392],\n                            [117.659044, 34.501044],\n                            [117.609769, 34.490686],\n                            [117.592523, 34.462566],\n                            [117.53832, 34.467006],\n                            [117.465023, 34.484767],\n                            [117.402813, 34.550843],\n                            [117.402813, 34.569571],\n                            [117.370785, 34.584846],\n                            [117.325205, 34.573021],\n                            [117.325205, 34.573021],\n                            [117.32151, 34.566614],\n                            [117.32151, 34.566614],\n                            [117.311654, 34.561686],\n                            [117.311654, 34.561686]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 330000,\n                \"name\": \"Zhejiang\",\n                \"center\": [120.153576, 30.287459],\n                \"centroid\": [120.109913, 29.181466],\n                \"childrenNum\": 11,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 10,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [118.433896, 28.288335],\n                            [118.444367, 28.253548],\n                            [118.490562, 28.238259],\n                            [118.493026, 28.262509],\n                            [118.588497, 28.282538],\n                            [118.595272, 28.258292],\n                            [118.651322, 28.277267],\n                            [118.674728, 28.27147],\n                            [118.699366, 28.309939],\n                            [118.719692, 28.312047],\n                            [118.756032, 28.252493],\n                            [118.802228, 28.240368],\n                            [118.804075, 28.207675],\n                            [118.771431, 28.188687],\n                            [118.805923, 28.154923],\n                            [118.802228, 28.117453],\n                            [118.767735, 28.10584],\n                            [118.719076, 28.063601],\n                            [118.733858, 28.027684],\n                            [118.730163, 27.970615],\n                            [118.753568, 27.947885],\n                            [118.818242, 27.916689],\n                            [118.829329, 27.847921],\n                            [118.873677, 27.733563],\n                            [118.879836, 27.667859],\n                            [118.913713, 27.619616],\n                            [118.909401, 27.568168],\n                            [118.869365, 27.540047],\n                            [118.907553, 27.460952],\n                            [118.955597, 27.4498],\n                            [118.986393, 27.47582],\n                            [118.983314, 27.498649],\n                            [119.020886, 27.498118],\n                            [119.03998, 27.478475],\n                            [119.092335, 27.466262],\n                            [119.129907, 27.475289],\n                            [119.121284, 27.438115],\n                            [119.14777, 27.424836],\n                            [119.224146, 27.416868],\n                            [119.26911, 27.42218],\n                            [119.285124, 27.457766],\n                            [119.334399, 27.480067],\n                            [119.360269, 27.524657],\n                            [119.416935, 27.539517],\n                            [119.438493, 27.508734],\n                            [119.466826, 27.526249],\n                            [119.501935, 27.610601],\n                            [119.501319, 27.649837],\n                            [119.541971, 27.666799],\n                            [119.606028, 27.674749],\n                            [119.644217, 27.663619],\n                            [119.626354, 27.620676],\n                            [119.630666, 27.582491],\n                            [119.675014, 27.574534],\n                            [119.659615, 27.540578],\n                            [119.690412, 27.537394],\n                            [119.70889, 27.514042],\n                            [119.703347, 27.446613],\n                            [119.685485, 27.438646],\n                            [119.711354, 27.403054],\n                            [119.750774, 27.373829],\n                            [119.739687, 27.362668],\n                            [119.782187, 27.330241],\n                            [119.768636, 27.307909],\n                            [119.843165, 27.300464],\n                            [119.938636, 27.329709],\n                            [119.960194, 27.365857],\n                            [120.008237, 27.375423],\n                            [120.026099, 27.344063],\n                            [120.052584, 27.338747],\n                            [120.096316, 27.390302],\n                            [120.136968, 27.402523],\n                            [120.134504, 27.420055],\n                            [120.221352, 27.420055],\n                            [120.26262, 27.432804],\n                            [120.273091, 27.38924],\n                            [120.340844, 27.399867],\n                            [120.343924, 27.363199],\n                            [120.430155, 27.258976],\n                            [120.401822, 27.250996],\n                            [120.404286, 27.204166],\n                            [120.461568, 27.142407],\n                            [120.492365, 27.136016],\n                            [120.545952, 27.156785],\n                            [120.574901, 27.234501],\n                            [120.554575, 27.25206],\n                            [120.580444, 27.321203],\n                            [120.665444, 27.357884],\n                            [120.673451, 27.420055],\n                            [120.703016, 27.478475],\n                            [120.637111, 27.561271],\n                            [120.634647, 27.577186],\n                            [120.685154, 27.622797],\n                            [120.709176, 27.682699],\n                            [120.771386, 27.734623],\n                            [120.777545, 27.774873],\n                            [120.809574, 27.775402],\n                            [120.840371, 27.758986],\n                            [120.910588, 27.864852],\n                            [120.942001, 27.896592],\n                            [120.97403, 27.887071],\n                            [121.027616, 27.832574],\n                            [121.070116, 27.834162],\n                            [121.107688, 27.81352],\n                            [121.152036, 27.815638],\n                            [121.134174, 27.787051],\n                            [121.13479, 27.787051],\n                            [121.149572, 27.801345],\n                            [121.149572, 27.801875],\n                            [121.153268, 27.809815],\n                            [121.152652, 27.810344],\n                            [121.192072, 27.822518],\n                            [121.193304, 27.872259],\n                            [121.162507, 27.879136],\n                            [121.162507, 27.90717],\n                            [121.099681, 27.895005],\n                            [121.05595, 27.900294],\n                            [120.991892, 27.95],\n                            [121.015298, 27.981714],\n                            [121.059029, 28.096338],\n                            [121.108304, 28.139092],\n                            [121.121239, 28.12537],\n                            [121.140949, 28.031382],\n                            [121.176058, 28.022401],\n                            [121.261057, 28.034551],\n                            [121.299862, 28.067297],\n                            [121.328195, 28.134343],\n                            [121.373774, 28.133287],\n                            [121.402107, 28.197127],\n                            [121.45631, 28.250385],\n                            [121.488955, 28.301509],\n                            [121.538846, 28.299401],\n                            [121.571491, 28.279376],\n                            [121.580114, 28.240368],\n                            [121.627541, 28.251966],\n                            [121.669425, 28.33312],\n                            [121.660186, 28.355768],\n                            [121.634317, 28.347868],\n                            [121.658954, 28.392628],\n                            [121.692831, 28.407368],\n                            [121.671273, 28.472621],\n                            [121.646019, 28.511544],\n                            [121.634317, 28.562542],\n                            [121.596128, 28.575156],\n                            [121.557324, 28.645033],\n                            [121.540694, 28.655537],\n                            [121.646019, 28.682842],\n                            [121.689135, 28.719062],\n                            [121.704534, 28.804577],\n                            [121.687287, 28.863294],\n                            [121.774751, 28.863818],\n                            [121.772287, 28.898404],\n                            [121.743338, 28.954451],\n                            [121.711309, 28.985865],\n                            [121.712541, 29.028783],\n                            [121.658954, 29.058606],\n                            [121.660186, 29.118226],\n                            [121.616454, 29.143318],\n                            [121.608447, 29.168927],\n                            [121.715621, 29.125022],\n                            [121.750113, 29.136523],\n                            [121.767975, 29.166837],\n                            [121.780294, 29.10986],\n                            [121.811091, 29.10986],\n                            [121.85975, 29.086328],\n                            [121.884388, 29.105677],\n                            [121.966308, 29.052852],\n                            [121.970004, 29.092604],\n                            [121.988482, 29.110906],\n                            [121.986634, 29.154817],\n                            [121.948446, 29.193485],\n                            [121.971851, 29.193485],\n                            [121.966924, 29.249894],\n                            [122.002032, 29.260336],\n                            [122.000185, 29.278608],\n                            [121.94475, 29.28435],\n                            [121.958301, 29.334448],\n                            [121.936127, 29.348012],\n                            [121.937975, 29.384],\n                            [121.975547, 29.411113],\n                            [121.993409, 29.45229],\n                            [121.973083, 29.477821],\n                            [121.968772, 29.515846],\n                            [121.995257, 29.545007],\n                            [122.000185, 29.582486],\n                            [121.966308, 29.636078],\n                            [121.909641, 29.650122],\n                            [121.872685, 29.632437],\n                            [121.833265, 29.653242],\n                            [121.937359, 29.748373],\n                            [122.003264, 29.762401],\n                            [122.043916, 29.822647],\n                            [122.10243, 29.859504],\n                            [122.143082, 29.877668],\n                            [122.140003, 29.901535],\n                            [122.00696, 29.891678],\n                            [122.00388, 29.92021],\n                            [121.971235, 29.955476],\n                            [121.919497, 29.920729],\n                            [121.835113, 29.958068],\n                            [121.78399, 29.99332],\n                            [121.721164, 29.992802],\n                            [121.699606, 30.007832],\n                            [121.652795, 30.071037],\n                            [121.635548, 30.070002],\n                            [121.561636, 30.184395],\n                            [121.497578, 30.258861],\n                            [121.395332, 30.338435],\n                            [121.371926, 30.37097],\n                            [121.328195, 30.397299],\n                            [121.225333, 30.404526],\n                            [121.183449, 30.434458],\n                            [121.092906, 30.515952],\n                            [121.058413, 30.563888],\n                            [121.148956, 30.599953],\n                            [121.188992, 30.632916],\n                            [121.239499, 30.648878],\n                            [121.274608, 30.677191],\n                            [121.272144, 30.723504],\n                            [121.232108, 30.755909],\n                            [121.21671, 30.785734],\n                            [121.174826, 30.771851],\n                            [121.123087, 30.77905],\n                            [121.13787, 30.826342],\n                            [121.097833, 30.857171],\n                            [121.060261, 30.845354],\n                            [121.038087, 30.814007],\n                            [120.991892, 30.837133],\n                            [121.020225, 30.872069],\n                            [120.993124, 30.889532],\n                            [121.000515, 30.938309],\n                            [120.989428, 31.01425],\n                            [120.949392, 31.030148],\n                            [120.940153, 31.010146],\n                            [120.901349, 31.017327],\n                            [120.865624, 30.989627],\n                            [120.820661, 31.006556],\n                            [120.770154, 30.996809],\n                            [120.746132, 30.962432],\n                            [120.698089, 30.970643],\n                            [120.684538, 30.955247],\n                            [120.709176, 30.933176],\n                            [120.713487, 30.88491],\n                            [120.68269, 30.882342],\n                            [120.654973, 30.846896],\n                            [120.589684, 30.854089],\n                            [120.563814, 30.835592],\n                            [120.504684, 30.757967],\n                            [120.489285, 30.763624],\n                            [120.460336, 30.839702],\n                            [120.441858, 30.860768],\n                            [120.435083, 30.920855],\n                            [120.42338, 30.902884],\n                            [120.35809, 30.886964],\n                            [120.371025, 30.948575],\n                            [120.316206, 30.933689],\n                            [120.223816, 30.926502],\n                            [120.149903, 30.937283],\n                            [120.111099, 30.955761],\n                            [120.052584, 31.00553],\n                            [120.001461, 31.027071],\n                            [119.988527, 31.059375],\n                            [119.946027, 31.106016],\n                            [119.921389, 31.170045],\n                            [119.878274, 31.160828],\n                            [119.827151, 31.174142],\n                            [119.809904, 31.148536],\n                            [119.779723, 31.17875],\n                            [119.715666, 31.169533],\n                            [119.705811, 31.152634],\n                            [119.678093, 31.167997],\n                            [119.623891, 31.130096],\n                            [119.649144, 31.104991],\n                            [119.629434, 31.085517],\n                            [119.633746, 31.019379],\n                            [119.580159, 30.967051],\n                            [119.582007, 30.932149],\n                            [119.563529, 30.919315],\n                            [119.557369, 30.874124],\n                            [119.575847, 30.829939],\n                            [119.55429, 30.825828],\n                            [119.527188, 30.77905],\n                            [119.479761, 30.772365],\n                            [119.482841, 30.704467],\n                            [119.444652, 30.650422],\n                            [119.408312, 30.645274],\n                            [119.39045, 30.685941],\n                            [119.343022, 30.664322],\n                            [119.323312, 30.630341],\n                            [119.238929, 30.609225],\n                            [119.265414, 30.574709],\n                            [119.237081, 30.546881],\n                            [119.272189, 30.510281],\n                            [119.326392, 30.532964],\n                            [119.336247, 30.508734],\n                            [119.335015, 30.448389],\n                            [119.36766, 30.38491],\n                            [119.402768, 30.374584],\n                            [119.349182, 30.349281],\n                            [119.326392, 30.372002],\n                            [119.277117, 30.341018],\n                            [119.246936, 30.341018],\n                            [119.236465, 30.297106],\n                            [119.201356, 30.290905],\n                            [119.126828, 30.304856],\n                            [119.091719, 30.323972],\n                            [119.06277, 30.304856],\n                            [118.988857, 30.332237],\n                            [118.954365, 30.360126],\n                            [118.880452, 30.31519],\n                            [118.877988, 30.282637],\n                            [118.905089, 30.216464],\n                            [118.929727, 30.2025],\n                            [118.852735, 30.166805],\n                            [118.852119, 30.149729],\n                            [118.895234, 30.148694],\n                            [118.873677, 30.11505],\n                            [118.878604, 30.064822],\n                            [118.902626, 30.029078],\n                            [118.894619, 29.937845],\n                            [118.838568, 29.934733],\n                            [118.841032, 29.891159],\n                            [118.740634, 29.814859],\n                            [118.744945, 29.73902],\n                            [118.700598, 29.706277],\n                            [118.647011, 29.64336],\n                            [118.61991, 29.654282],\n                            [118.573714, 29.638159],\n                            [118.532446, 29.588731],\n                            [118.500417, 29.57572],\n                            [118.496106, 29.519492],\n                            [118.381541, 29.504909],\n                            [118.347664, 29.474174],\n                            [118.329802, 29.495012],\n                            [118.306396, 29.479384],\n                            [118.316252, 29.422581],\n                            [118.248498, 29.431443],\n                            [118.193064, 29.395472],\n                            [118.205382, 29.343839],\n                            [118.166578, 29.314099],\n                            [118.178281, 29.297921],\n                            [118.138861, 29.283828],\n                            [118.077883, 29.290614],\n                            [118.073571, 29.216993],\n                            [118.042159, 29.210202],\n                            [118.027992, 29.167882],\n                            [118.045238, 29.149068],\n                            [118.037847, 29.102017],\n                            [118.076035, 29.074822],\n                            [118.066796, 29.053898],\n                            [118.097593, 28.998952],\n                            [118.115455, 29.009944],\n                            [118.115455, 29.009944],\n                            [118.133933, 28.983771],\n                            [118.165346, 28.986912],\n                            [118.227556, 28.942406],\n                            [118.195527, 28.904167],\n                            [118.270056, 28.918836],\n                            [118.300237, 28.826075],\n                            [118.364295, 28.813491],\n                            [118.403099, 28.702791],\n                            [118.428352, 28.681267],\n                            [118.428352, 28.617193],\n                            [118.428352, 28.617193],\n                            [118.412338, 28.55676],\n                            [118.4302, 28.515225],\n                            [118.414802, 28.497344],\n                            [118.474548, 28.478934],\n                            [118.456686, 28.424738],\n                            [118.432048, 28.402104],\n                            [118.455454, 28.384204],\n                            [118.480091, 28.327325],\n                            [118.433896, 28.288335]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.163408, 29.988137],\n                            [122.239785, 29.962735],\n                            [122.279205, 29.937326],\n                            [122.322321, 29.940438],\n                            [122.341415, 29.976733],\n                            [122.343879, 30.020269],\n                            [122.310002, 30.039958],\n                            [122.290908, 30.074663],\n                            [122.301379, 30.086574],\n                            [122.293988, 30.100554],\n                            [122.152938, 30.113497],\n                            [122.095655, 30.158008],\n                            [122.048844, 30.147141],\n                            [121.955221, 30.183878],\n                            [121.934895, 30.161631],\n                            [121.983554, 30.100554],\n                            [121.989714, 30.077252],\n                            [121.978011, 30.059125],\n                            [122.027902, 29.991247],\n                            [122.106742, 30.005759],\n                            [122.118445, 29.986582],\n                            [122.163408, 29.988137]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.213915, 30.186464],\n                            [122.178807, 30.199396],\n                            [122.152938, 30.19112],\n                            [122.143698, 30.163183],\n                            [122.168336, 30.138343],\n                            [122.213915, 30.186464]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.229314, 29.711995],\n                            [122.210836, 29.700559],\n                            [122.269966, 29.685482],\n                            [122.231162, 29.710435],\n                            [122.229314, 29.711995]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.427646, 30.738422],\n                            [122.427031, 30.697777],\n                            [122.532972, 30.696748],\n                            [122.528045, 30.725047],\n                            [122.475074, 30.714243],\n                            [122.445509, 30.745109],\n                            [122.427646, 30.738422]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.162793, 30.329654],\n                            [122.058083, 30.291938],\n                            [122.154169, 30.244903],\n                            [122.231778, 30.234562],\n                            [122.247176, 30.30124],\n                            [122.228082, 30.329654],\n                            [122.191126, 30.329654],\n                            [122.176343, 30.351863],\n                            [122.162793, 30.329654]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.317393, 30.249556],\n                            [122.277973, 30.242835],\n                            [122.358661, 30.236113],\n                            [122.365437, 30.255242],\n                            [122.417175, 30.238699],\n                            [122.40732, 30.272817],\n                            [122.333408, 30.272817],\n                            [122.317393, 30.249556]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.026054, 29.178333],\n                            [122.013119, 29.151681],\n                            [122.056851, 29.158476],\n                            [122.075945, 29.176243],\n                            [122.036525, 29.20759],\n                            [122.026054, 29.178333]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.372212, 29.893234],\n                            [122.386379, 29.834069],\n                            [122.415944, 29.828877],\n                            [122.401777, 29.869884],\n                            [122.433806, 29.883376],\n                            [122.43319, 29.919173],\n                            [122.411632, 29.951846],\n                            [122.398081, 29.9394],\n                            [122.351886, 29.959105],\n                            [122.330944, 29.937845],\n                            [122.338951, 29.911911],\n                            [122.353734, 29.89946],\n                            [122.362973, 29.894272],\n                            [122.372212, 29.893234]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.43011, 30.408655],\n                            [122.432574, 30.445294],\n                            [122.37406, 30.461802],\n                            [122.277973, 30.471603],\n                            [122.281669, 30.418461],\n                            [122.318625, 30.407106],\n                            [122.352502, 30.422074],\n                            [122.43011, 30.408655]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.837577, 28.770484],\n                            [121.86283, 28.782024],\n                            [121.861598, 28.814016],\n                            [121.837577, 28.770484]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.265038, 29.84549],\n                            [122.221307, 29.832512],\n                            [122.248408, 29.804473],\n                            [122.310002, 29.766557],\n                            [122.325401, 29.781621],\n                            [122.299531, 29.819532],\n                            [122.319241, 29.829397],\n                            [122.265038, 29.84549]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.790765, 29.082144],\n                            [121.832649, 29.050236],\n                            [121.84312, 29.082144],\n                            [121.82033, 29.099402],\n                            [121.790765, 29.082144]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.201311, 27.623328],\n                            [121.197616, 27.618025],\n                            [121.198848, 27.616964],\n                            [121.203775, 27.625979],\n                            [121.201311, 27.623328]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.943518, 30.776993],\n                            [121.968156, 30.688514],\n                            [121.997105, 30.658659],\n                            [122.087032, 30.602014],\n                            [122.133227, 30.595317],\n                            [122.075329, 30.647848],\n                            [122.011271, 30.66947],\n                            [121.992793, 30.695204],\n                            [121.987866, 30.753338],\n                            [121.970004, 30.789333],\n                            [121.943518, 30.776993]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.889315, 28.471569],\n                            [121.918881, 28.497344],\n                            [121.881924, 28.502603],\n                            [121.889315, 28.471569]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.182503, 29.650642],\n                            [122.211452, 29.692241],\n                            [122.200365, 29.712515],\n                            [122.146778, 29.749412],\n                            [122.13138, 29.788893],\n                            [122.083952, 29.78318],\n                            [122.047612, 29.719791],\n                            [122.074097, 29.701599],\n                            [122.095655, 29.716673],\n                            [122.138155, 29.662083],\n                            [122.182503, 29.650642]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.461523, 29.944068],\n                            [122.459675, 29.944586],\n                            [122.460291, 29.947179],\n                            [122.451668, 29.943031],\n                            [122.451052, 29.940956],\n                            [122.450436, 29.940956],\n                            [122.449204, 29.9394],\n                            [122.4529, 29.936807],\n                            [122.452284, 29.935252],\n                            [122.45598, 29.926435],\n                            [122.457827, 29.927472],\n                            [122.462755, 29.927991],\n                            [122.467067, 29.928509],\n                            [122.459059, 29.938882],\n                            [122.461523, 29.944068]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.570544, 30.644244],\n                            [122.559457, 30.679764],\n                            [122.546523, 30.651967],\n                            [122.570544, 30.644244]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.869605, 28.423685],\n                            [121.910873, 28.44],\n                            [121.889931, 28.45105],\n                            [121.869605, 28.423685]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.065474, 30.179739],\n                            [122.055619, 30.200431],\n                            [122.017431, 30.186464],\n                            [122.025438, 30.161631],\n                            [122.065474, 30.179739]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.391306, 29.970512],\n                            [122.411632, 30.025969],\n                            [122.378371, 30.023896],\n                            [122.3679, 29.980361],\n                            [122.391306, 29.970512]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.850511, 29.977251],\n                            [121.874533, 29.964809],\n                            [121.933047, 29.994875],\n                            [121.924424, 30.052391],\n                            [121.88562, 30.094859],\n                            [121.848663, 30.101072],\n                            [121.84004, 30.047211],\n                            [121.844968, 29.982953],\n                            [121.850511, 29.977251]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.066421, 27.478475],\n                            [121.066421, 27.461483],\n                            [121.107073, 27.443958],\n                            [121.067036, 27.478475],\n                            [121.066421, 27.478475]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.952141, 29.187738],\n                            [121.979243, 29.160043],\n                            [121.976779, 29.191918],\n                            [121.952141, 29.187738]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.038373, 29.759284],\n                            [122.011271, 29.746294],\n                            [122.02975, 29.716673],\n                            [122.038373, 29.759284]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.940438, 30.114533],\n                            [121.910257, 30.089163],\n                            [121.945982, 30.064304],\n                            [121.962612, 30.106249],\n                            [121.940438, 30.114533]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.957685, 30.287804],\n                            [122.0008, 30.308473],\n                            [121.989098, 30.339985],\n                            [121.94167, 30.33327],\n                            [121.921344, 30.30744],\n                            [121.957685, 30.287804]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.192974, 29.965327],\n                            [122.163408, 29.988137],\n                            [122.152322, 29.97103],\n                            [122.154169, 29.97103],\n                            [122.155401, 29.970512],\n                            [122.18435, 29.955476],\n                            [122.192974, 29.965327]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.287828, 29.723949],\n                            [122.301379, 29.748373],\n                            [122.258263, 29.753569],\n                            [122.241633, 29.784738],\n                            [122.2133, 29.771752],\n                            [122.251488, 29.731225],\n                            [122.287828, 29.723949]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.134174, 27.787051],\n                            [121.134174, 27.785992],\n                            [121.13479, 27.787051],\n                            [121.134174, 27.787051]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.760254, 30.141966],\n                            [122.784275, 30.130062],\n                            [122.781196, 30.13265],\n                            [122.778116, 30.13679],\n                            [122.770725, 30.138861],\n                            [122.763333, 30.141966],\n                            [122.762101, 30.142484],\n                            [122.760254, 30.141966]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.264423, 30.269716],\n                            [122.253952, 30.237147],\n                            [122.315545, 30.250073],\n                            [122.300147, 30.271266],\n                            [122.264423, 30.269716]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.282901, 29.860542],\n                            [122.30877, 29.849642],\n                            [122.343263, 29.860542],\n                            [122.343263, 29.882857],\n                            [122.301379, 29.883895],\n                            [122.282901, 29.860542]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.781196, 30.694175],\n                            [122.799674, 30.716301],\n                            [122.778732, 30.729677],\n                            [122.757174, 30.713728],\n                            [122.781196, 30.694175]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.098449, 27.937311],\n                            [121.152652, 27.961629],\n                            [121.120623, 27.986471],\n                            [121.0695, 27.984357],\n                            [121.038087, 27.948942],\n                            [121.098449, 27.937311]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.185913, 27.963215],\n                            [121.237652, 27.988056],\n                            [121.197616, 28.000739],\n                            [121.17113, 27.978543],\n                            [121.185913, 27.963215]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.454132, 29.956513],\n                            [122.447972, 29.955994],\n                            [122.445509, 29.952365],\n                            [122.446741, 29.951327],\n                            [122.447972, 29.947698],\n                            [122.459059, 29.950809],\n                            [122.458443, 29.951846],\n                            [122.455364, 29.955994],\n                            [122.454132, 29.956513]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.836014, 30.698806],\n                            [122.831087, 30.728648],\n                            [122.807681, 30.714243],\n                            [122.836014, 30.698806]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.200365, 29.969475],\n                            [122.233626, 29.946661],\n                            [122.273662, 29.93214],\n                            [122.239785, 29.960142],\n                            [122.200365, 29.969475]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.029134, 29.954957],\n                            [122.043916, 29.930584],\n                            [122.058699, 29.955994],\n                            [122.029134, 29.954957]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.044247, 27.979072],\n                            [121.089826, 27.998625],\n                            [121.073812, 28.007608],\n                            [121.044247, 27.979072]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.471378, 29.927472],\n                            [122.470762, 29.925916],\n                            [122.473226, 29.925397],\n                            [122.47261, 29.927472],\n                            [122.471378, 29.927472]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.152322, 29.97103],\n                            [122.155401, 29.970512],\n                            [122.154169, 29.97103],\n                            [122.152322, 29.97103]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 340000,\n                \"name\": \"Anhui\",\n                \"center\": [117.283042, 31.86119],\n                \"centroid\": [117.226884, 31.849254],\n                \"childrenNum\": 16,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 11,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [116.599629, 34.014324],\n                            [116.641512, 33.978103],\n                            [116.64336, 33.896675],\n                            [116.631042, 33.887733],\n                            [116.566984, 33.9081],\n                            [116.558361, 33.881274],\n                            [116.486296, 33.869846],\n                            [116.437637, 33.846489],\n                            [116.437021, 33.801246],\n                            [116.408071, 33.805721],\n                            [116.393905, 33.782843],\n                            [116.316912, 33.771402],\n                            [116.263326, 33.730101],\n                            [116.230065, 33.735078],\n                            [116.155536, 33.709693],\n                            [116.132747, 33.751501],\n                            [116.100102, 33.782843],\n                            [116.074232, 33.781351],\n                            [116.055754, 33.804727],\n                            [116.05945, 33.860902],\n                            [115.982457, 33.917039],\n                            [116.00032, 33.965199],\n                            [115.95782, 34.007875],\n                            [115.904233, 34.009859],\n                            [115.876516, 34.028708],\n                            [115.877132, 34.002913],\n                            [115.85003, 34.004898],\n                            [115.846335, 34.028708],\n                            [115.809378, 34.062428],\n                            [115.768726, 34.061932],\n                            [115.736082, 34.076805],\n                            [115.705901, 34.059949],\n                            [115.658473, 34.061437],\n                            [115.642459, 34.03218],\n                            [115.60735, 34.030196],\n                            [115.579017, 33.974133],\n                            [115.577785, 33.950307],\n                            [115.547604, 33.874815],\n                            [115.631988, 33.869846],\n                            [115.614126, 33.775879],\n                            [115.576553, 33.787817],\n                            [115.563003, 33.772895],\n                            [115.601807, 33.718653],\n                            [115.601191, 33.658898],\n                            [115.639995, 33.585143],\n                            [115.564851, 33.576169],\n                            [115.511264, 33.55323],\n                            [115.463837, 33.567193],\n                            [115.422569, 33.557219],\n                            [115.394851, 33.506335],\n                            [115.366518, 33.5233],\n                            [115.345576, 33.502842],\n                            [115.345576, 33.449928],\n                            [115.324634, 33.457418],\n                            [115.315395, 33.431451],\n                            [115.328946, 33.403477],\n                            [115.313547, 33.376994],\n                            [115.341881, 33.370997],\n                            [115.365286, 33.336005],\n                            [115.361591, 33.298497],\n                            [115.335105, 33.297997],\n                            [115.340033, 33.260973],\n                            [115.300613, 33.204407],\n                            [115.303692, 33.149809],\n                            [115.289526, 33.131769],\n                            [115.245178, 33.135778],\n                            [115.194671, 33.120743],\n                            [115.168186, 33.088658],\n                            [115.041302, 33.086653],\n                            [114.990795, 33.102195],\n                            [114.966158, 33.147304],\n                            [114.932897, 33.153817],\n                            [114.902716, 33.129764],\n                            [114.897172, 33.086653],\n                            [114.913187, 33.083143],\n                            [114.925506, 33.016928],\n                            [114.891629, 33.020441],\n                            [114.883006, 32.990328],\n                            [114.916266, 32.971251],\n                            [114.943368, 32.935094],\n                            [115.009273, 32.940117],\n                            [115.035143, 32.932582],\n                            [115.029599, 32.906962],\n                            [115.139237, 32.897917],\n                            [115.155867, 32.864747],\n                            [115.197135, 32.856201],\n                            [115.189744, 32.812452],\n                            [115.211301, 32.785791],\n                            [115.189744, 32.770695],\n                            [115.179273, 32.726402],\n                            [115.182968, 32.666973],\n                            [115.20083, 32.591876],\n                            [115.24333, 32.593388],\n                            [115.267352, 32.578261],\n                            [115.30554, 32.583303],\n                            [115.304924, 32.553042],\n                            [115.411482, 32.575235],\n                            [115.409018, 32.549007],\n                            [115.497713, 32.492489],\n                            [115.5088, 32.468761],\n                            [115.510648, 32.468761],\n                            [115.510648, 32.468256],\n                            [115.510648, 32.467751],\n                            [115.509416, 32.466741],\n                            [115.522967, 32.441997],\n                            [115.57101, 32.419266],\n                            [115.604271, 32.425833],\n                            [115.626445, 32.40512],\n                            [115.657857, 32.428864],\n                            [115.667712, 32.409667],\n                            [115.704669, 32.495013],\n                            [115.742241, 32.476335],\n                            [115.771806, 32.505108],\n                            [115.789052, 32.468761],\n                            [115.861117, 32.537403],\n                            [115.891298, 32.576243],\n                            [115.910393, 32.567165],\n                            [115.8759, 32.542448],\n                            [115.845719, 32.501575],\n                            [115.883291, 32.487946],\n                            [115.865429, 32.458662],\n                            [115.899306, 32.390971],\n                            [115.912856, 32.227596],\n                            [115.941805, 32.166318],\n                            [115.922095, 32.049725],\n                            [115.928871, 32.003046],\n                            [115.909161, 31.94314],\n                            [115.920248, 31.920285],\n                            [115.894994, 31.8649],\n                            [115.893762, 31.832365],\n                            [115.914704, 31.814567],\n                            [115.886371, 31.776418],\n                            [115.851878, 31.786593],\n                            [115.808147, 31.770313],\n                            [115.808147, 31.770313],\n                            [115.767495, 31.78761],\n                            [115.731154, 31.76726],\n                            [115.676336, 31.778453],\n                            [115.553764, 31.69549],\n                            [115.534054, 31.698545],\n                            [115.495249, 31.673083],\n                            [115.476771, 31.643028],\n                            [115.485394, 31.608885],\n                            [115.439815, 31.588496],\n                            [115.415793, 31.525771],\n                            [115.371446, 31.495668],\n                            [115.389924, 31.450241],\n                            [115.373909, 31.405813],\n                            [115.393004, 31.389977],\n                            [115.372062, 31.349098],\n                            [115.40717, 31.337854],\n                            [115.443511, 31.344498],\n                            [115.473076, 31.265242],\n                            [115.507568, 31.267799],\n                            [115.539597, 31.231985],\n                            [115.540213, 31.194621],\n                            [115.585793, 31.143926],\n                            [115.603655, 31.17363],\n                            [115.655394, 31.211002],\n                            [115.700973, 31.201276],\n                            [115.778582, 31.112164],\n                            [115.797676, 31.128047],\n                            [115.837712, 31.127022],\n                            [115.867277, 31.147512],\n                            [115.887603, 31.10909],\n                            [115.939958, 31.071678],\n                            [115.938726, 31.04707],\n                            [116.006479, 31.034764],\n                            [116.015102, 31.011685],\n                            [116.058834, 31.012711],\n                            [116.071769, 30.956787],\n                            [116.03974, 30.957813],\n                            [115.976298, 30.931636],\n                            [115.932566, 30.889532],\n                            [115.865429, 30.864364],\n                            [115.848799, 30.828397],\n                            [115.863581, 30.815549],\n                            [115.851262, 30.756938],\n                            [115.782893, 30.751795],\n                            [115.762567, 30.685426],\n                            [115.81369, 30.637035],\n                            [115.819234, 30.597893],\n                            [115.848799, 30.602014],\n                            [115.876516, 30.582438],\n                            [115.887603, 30.542758],\n                            [115.910393, 30.519046],\n                            [115.894994, 30.452517],\n                            [115.921479, 30.416397],\n                            [115.885139, 30.379747],\n                            [115.91532, 30.337919],\n                            [115.903001, 30.31364],\n                            [115.985537, 30.290905],\n                            [115.997856, 30.252657],\n                            [116.065609, 30.204569],\n                            [116.055754, 30.180774],\n                            [116.088399, 30.110391],\n                            [116.078544, 30.062233],\n                            [116.091479, 30.036331],\n                            [116.073616, 29.969993],\n                            [116.128435, 29.897904],\n                            [116.13521, 29.819532],\n                            [116.172783, 29.828358],\n                            [116.227601, 29.816936],\n                            [116.250391, 29.785777],\n                            [116.280572, 29.788893],\n                            [116.342782, 29.835626],\n                            [116.467818, 29.896347],\n                            [116.525716, 29.897385],\n                            [116.552201, 29.909836],\n                            [116.585462, 30.045657],\n                            [116.620571, 30.073109],\n                            [116.666766, 30.076734],\n                            [116.720353, 30.053945],\n                            [116.747454, 30.057053],\n                            [116.783794, 30.030632],\n                            [116.802889, 29.99643],\n                            [116.830606, 30.004723],\n                            [116.83307, 29.95755],\n                            [116.868794, 29.980361],\n                            [116.900207, 29.949253],\n                            [116.882961, 29.893753],\n                            [116.780715, 29.792529],\n                            [116.762237, 29.802396],\n                            [116.673541, 29.709916],\n                            [116.698795, 29.707836],\n                            [116.70557, 29.69692],\n                            [116.706802, 29.6964],\n                            [116.704954, 29.688602],\n                            [116.680317, 29.681323],\n                            [116.651983, 29.637118],\n                            [116.716657, 29.590813],\n                            [116.721585, 29.564789],\n                            [116.760389, 29.599139],\n                            [116.780715, 29.569994],\n                            [116.849084, 29.57624],\n                            [116.873722, 29.609546],\n                            [116.939627, 29.648561],\n                            [116.974736, 29.657403],\n                            [116.996294, 29.683403],\n                            [117.041873, 29.680803],\n                            [117.112706, 29.711995],\n                            [117.108395, 29.75201],\n                            [117.136728, 29.775388],\n                            [117.123177, 29.798761],\n                            [117.073286, 29.831992],\n                            [117.127489, 29.86158],\n                            [117.129952, 29.89946],\n                            [117.171836, 29.920729],\n                            [117.2168, 29.926953],\n                            [117.246365, 29.915023],\n                            [117.261763, 29.880781],\n                            [117.25314, 29.834588],\n                            [117.29256, 29.822647],\n                            [117.338756, 29.848085],\n                            [117.359082, 29.812782],\n                            [117.382487, 29.840818],\n                            [117.415132, 29.85068],\n                            [117.408973, 29.802396],\n                            [117.455168, 29.749412],\n                            [117.453936, 29.688082],\n                            [117.490277, 29.660003],\n                            [117.530313, 29.654282],\n                            [117.523538, 29.630356],\n                            [117.543248, 29.588731],\n                            [117.608537, 29.591333],\n                            [117.647957, 29.614749],\n                            [117.678754, 29.595496],\n                            [117.690457, 29.555939],\n                            [117.729877, 29.550213],\n                            [117.795167, 29.570515],\n                            [117.872775, 29.54761],\n                            [117.933753, 29.549172],\n                            [118.00397, 29.578322],\n                            [118.042774, 29.566351],\n                            [118.050782, 29.542924],\n                            [118.095129, 29.534072],\n                            [118.143788, 29.489803],\n                            [118.127774, 29.47209],\n                            [118.136397, 29.418932],\n                            [118.193064, 29.395472],\n                            [118.248498, 29.431443],\n                            [118.316252, 29.422581],\n                            [118.306396, 29.479384],\n                            [118.329802, 29.495012],\n                            [118.347664, 29.474174],\n                            [118.381541, 29.504909],\n                            [118.496106, 29.519492],\n                            [118.500417, 29.57572],\n                            [118.532446, 29.588731],\n                            [118.573714, 29.638159],\n                            [118.61991, 29.654282],\n                            [118.647011, 29.64336],\n                            [118.700598, 29.706277],\n                            [118.744945, 29.73902],\n                            [118.740634, 29.814859],\n                            [118.841032, 29.891159],\n                            [118.838568, 29.934733],\n                            [118.894619, 29.937845],\n                            [118.902626, 30.029078],\n                            [118.878604, 30.064822],\n                            [118.873677, 30.11505],\n                            [118.895234, 30.148694],\n                            [118.852119, 30.149729],\n                            [118.852735, 30.166805],\n                            [118.929727, 30.2025],\n                            [118.905089, 30.216464],\n                            [118.877988, 30.282637],\n                            [118.880452, 30.31519],\n                            [118.954365, 30.360126],\n                            [118.988857, 30.332237],\n                            [119.06277, 30.304856],\n                            [119.091719, 30.323972],\n                            [119.126828, 30.304856],\n                            [119.201356, 30.290905],\n                            [119.236465, 30.297106],\n                            [119.246936, 30.341018],\n                            [119.277117, 30.341018],\n                            [119.326392, 30.372002],\n                            [119.349182, 30.349281],\n                            [119.402768, 30.374584],\n                            [119.36766, 30.38491],\n                            [119.335015, 30.448389],\n                            [119.336247, 30.508734],\n                            [119.326392, 30.532964],\n                            [119.272189, 30.510281],\n                            [119.237081, 30.546881],\n                            [119.265414, 30.574709],\n                            [119.238929, 30.609225],\n                            [119.323312, 30.630341],\n                            [119.343022, 30.664322],\n                            [119.39045, 30.685941],\n                            [119.408312, 30.645274],\n                            [119.444652, 30.650422],\n                            [119.482841, 30.704467],\n                            [119.479761, 30.772365],\n                            [119.527188, 30.77905],\n                            [119.55429, 30.825828],\n                            [119.575847, 30.829939],\n                            [119.557369, 30.874124],\n                            [119.563529, 30.919315],\n                            [119.582007, 30.932149],\n                            [119.580159, 30.967051],\n                            [119.633746, 31.019379],\n                            [119.629434, 31.085517],\n                            [119.649144, 31.104991],\n                            [119.623891, 31.130096],\n                            [119.599869, 31.10909],\n                            [119.532732, 31.159291],\n                            [119.461283, 31.156219],\n                            [119.439109, 31.177214],\n                            [119.391682, 31.174142],\n                            [119.360269, 31.213049],\n                            [119.374435, 31.258591],\n                            [119.350414, 31.301043],\n                            [119.338095, 31.259103],\n                            [119.294363, 31.263195],\n                            [119.266646, 31.250405],\n                            [119.198277, 31.270357],\n                            [119.197661, 31.295418],\n                            [119.158241, 31.294907],\n                            [119.107118, 31.250917],\n                            [119.10527, 31.235055],\n                            [119.014727, 31.241707],\n                            [118.984546, 31.237102],\n                            [118.870597, 31.242219],\n                            [118.794836, 31.229426],\n                            [118.756648, 31.279564],\n                            [118.726467, 31.282121],\n                            [118.720924, 31.322518],\n                            [118.745561, 31.372606],\n                            [118.767735, 31.363919],\n                            [118.824401, 31.375672],\n                            [118.852119, 31.393553],\n                            [118.883532, 31.500261],\n                            [118.857046, 31.506384],\n                            [118.865669, 31.519139],\n                            [118.885995, 31.519139],\n                            [118.881684, 31.564023],\n                            [118.858894, 31.623665],\n                            [118.802844, 31.619078],\n                            [118.773894, 31.682759],\n                            [118.748025, 31.675629],\n                            [118.736322, 31.633347],\n                            [118.643315, 31.649651],\n                            [118.643315, 31.671555],\n                            [118.697518, 31.709747],\n                            [118.653786, 31.73011],\n                            [118.641467, 31.75861],\n                            [118.571866, 31.746397],\n                            [118.5577, 31.73011],\n                            [118.521975, 31.743343],\n                            [118.533678, 31.76726],\n                            [118.481939, 31.778453],\n                            [118.504729, 31.841516],\n                            [118.466541, 31.857784],\n                            [118.472084, 31.879639],\n                            [118.363679, 31.930443],\n                            [118.389548, 31.985281],\n                            [118.394476, 32.076098],\n                            [118.433896, 32.086746],\n                            [118.501033, 32.121726],\n                            [118.49549, 32.165304],\n                            [118.510888, 32.194176],\n                            [118.643931, 32.209875],\n                            [118.674728, 32.250375],\n                            [118.657482, 32.30148],\n                            [118.703061, 32.328792],\n                            [118.685199, 32.403604],\n                            [118.691359, 32.472295],\n                            [118.628533, 32.467751],\n                            [118.592192, 32.481383],\n                            [118.608823, 32.536899],\n                            [118.564475, 32.562122],\n                            [118.568787, 32.585825],\n                            [118.59712, 32.600951],\n                            [118.632844, 32.578261],\n                            [118.658714, 32.594397],\n                            [118.688895, 32.588346],\n                            [118.719076, 32.614059],\n                            [118.719076, 32.614059],\n                            [118.73509, 32.58885],\n                            [118.757264, 32.603976],\n                            [118.784981, 32.582295],\n                            [118.820706, 32.60448],\n                            [118.84288, 32.56767],\n                            [118.908169, 32.59238],\n                            [118.890923, 32.553042],\n                            [118.92172, 32.557078],\n                            [118.922336, 32.557078],\n                            [118.92172, 32.557078],\n                            [118.922336, 32.557078],\n                            [118.975923, 32.505108],\n                            [119.041212, 32.515201],\n                            [119.084944, 32.452602],\n                            [119.142226, 32.499556],\n                            [119.168096, 32.536394],\n                            [119.152697, 32.557582],\n                            [119.22045, 32.576748],\n                            [119.230921, 32.607001],\n                            [119.208748, 32.641276],\n                            [119.211827, 32.708275],\n                            [119.184726, 32.825529],\n                            [119.113277, 32.823014],\n                            [119.054763, 32.8748],\n                            [119.020886, 32.955685],\n                            [118.993169, 32.958196],\n                            [118.934039, 32.93861],\n                            [118.892771, 32.941121],\n                            [118.89585, 32.957694],\n                            [118.89585, 32.957694],\n                            [118.849039, 32.956689],\n                            [118.846575, 32.922034],\n                            [118.821322, 32.920527],\n                            [118.810235, 32.853687],\n                            [118.743097, 32.853184],\n                            [118.743097, 32.853184],\n                            [118.73817, 32.772708],\n                            [118.756648, 32.737477],\n                            [118.707373, 32.72036],\n                            [118.642699, 32.744525],\n                            [118.572482, 32.719856],\n                            [118.560163, 32.729926],\n                            [118.483787, 32.721367],\n                            [118.450526, 32.743518],\n                            [118.411106, 32.715828],\n                            [118.375382, 32.718849],\n                            [118.363063, 32.770695],\n                            [118.334114, 32.761637],\n                            [118.300237, 32.783275],\n                            [118.301469, 32.846145],\n                            [118.250346, 32.848157],\n                            [118.2331, 32.914498],\n                            [118.252194, 32.936601],\n                            [118.291614, 32.946143],\n                            [118.303933, 32.96874],\n                            [118.26944, 32.969242],\n                            [118.244803, 32.998359],\n                            [118.243571, 33.027967],\n                            [118.219549, 33.114227],\n                            [118.217085, 33.191888],\n                            [118.178281, 33.217926],\n                            [118.149332, 33.169348],\n                            [118.038463, 33.134776],\n                            [118.037231, 33.152314],\n                            [117.988572, 33.180869],\n                            [117.977485, 33.226437],\n                            [117.942376, 33.224936],\n                            [117.939297, 33.262475],\n                            [117.974405, 33.279487],\n                            [117.992883, 33.333005],\n                            [118.029224, 33.374995],\n                            [118.016905, 33.402978],\n                            [118.027376, 33.455421],\n                            [118.050782, 33.491863],\n                            [118.107448, 33.475391],\n                            [118.117919, 33.594615],\n                            [118.112376, 33.617045],\n                            [118.16781, 33.663381],\n                            [118.161035, 33.735576],\n                            [118.117919, 33.766427],\n                            [118.065564, 33.76593],\n                            [118.019985, 33.738562],\n                            [117.972557, 33.74951],\n                            [117.901724, 33.720146],\n                            [117.843826, 33.736074],\n                            [117.791471, 33.733585],\n                            [117.750203, 33.710688],\n                            [117.72495, 33.74951],\n                            [117.739732, 33.758467],\n                            [117.759442, 33.874318],\n                            [117.753899, 33.891211],\n                            [117.715095, 33.879287],\n                            [117.672595, 33.934916],\n                            [117.671363, 33.992494],\n                            [117.629479, 34.028708],\n                            [117.612849, 34.000433],\n                            [117.569117, 33.985051],\n                            [117.543248, 34.038627],\n                            [117.514914, 34.060941],\n                            [117.435458, 34.028212],\n                            [117.404045, 34.03218],\n                            [117.357234, 34.088205],\n                            [117.311654, 34.067882],\n                            [117.277162, 34.078787],\n                            [117.257452, 34.065899],\n                            [117.192162, 34.068873],\n                            [117.130568, 34.101586],\n                            [117.123793, 34.128342],\n                            [117.046801, 34.151622],\n                            [117.025243, 34.167469],\n                            [117.051112, 34.221425],\n                            [116.969192, 34.283753],\n                            [116.983359, 34.348011],\n                            [116.960569, 34.363821],\n                            [116.969192, 34.389012],\n                            [116.909446, 34.408271],\n                            [116.828142, 34.389012],\n                            [116.782563, 34.429993],\n                            [116.773939, 34.453683],\n                            [116.722816, 34.472434],\n                            [116.662454, 34.472927],\n                            [116.592237, 34.493646],\n                            [116.594085, 34.511894],\n                            [116.490607, 34.573513],\n                            [116.477057, 34.614896],\n                            [116.432709, 34.630163],\n                            [116.430245, 34.650843],\n                            [116.374195, 34.640011],\n                            [116.334159, 34.620806],\n                            [116.32492, 34.601104],\n                            [116.286116, 34.608986],\n                            [116.247927, 34.551829],\n                            [116.196804, 34.575977],\n                            [116.191261, 34.535561],\n                            [116.204196, 34.508442],\n                            [116.178326, 34.496112],\n                            [116.162312, 34.459605],\n                            [116.178942, 34.430487],\n                            [116.215898, 34.403333],\n                            [116.213435, 34.382098],\n                            [116.255934, 34.376665],\n                            [116.301514, 34.342082],\n                            [116.357564, 34.319843],\n                            [116.372347, 34.26595],\n                            [116.409303, 34.273863],\n                            [116.409303, 34.273863],\n                            [116.456731, 34.268917],\n                            [116.516477, 34.296114],\n                            [116.562056, 34.285731],\n                            [116.582382, 34.266444],\n                            [116.545426, 34.241711],\n                            [116.542962, 34.203608],\n                            [116.565752, 34.16945],\n                            [116.536187, 34.151127],\n                            [116.52818, 34.122892],\n                            [116.576223, 34.068873],\n                            [116.576223, 34.068873],\n                            [116.599629, 34.014324],\n                            [116.599629, 34.014324]\n                        ]\n                    ],\n                    [\n                        [\n                            [118.865669, 31.519139],\n                            [118.857046, 31.506384],\n                            [118.883532, 31.500261],\n                            [118.885995, 31.519139],\n                            [118.865669, 31.519139]\n                        ]\n                    ],\n                    [\n                        [\n                            [116.698795, 29.707836],\n                            [116.673541, 29.709916],\n                            [116.653831, 29.694841],\n                            [116.680317, 29.681323],\n                            [116.704954, 29.688602],\n                            [116.706802, 29.6964],\n                            [116.70557, 29.69692],\n                            [116.698795, 29.707836]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.5088, 32.468761],\n                            [115.509416, 32.466741],\n                            [115.510648, 32.467751],\n                            [115.510648, 32.468256],\n                            [115.510648, 32.468761],\n                            [115.5088, 32.468761]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 350000,\n                \"name\": \"Fujian\",\n                \"center\": [119.306239, 26.075302],\n                \"centroid\": [118.006468, 26.069925],\n                \"childrenNum\": 9,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 12,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [119.004872, 24.970009],\n                            [118.989473, 24.973807],\n                            [119.023966, 25.04377],\n                            [119.016575, 25.058409],\n                            [118.974691, 25.024792],\n                            [118.945126, 25.028588],\n                            [118.892155, 25.092558],\n                            [118.974691, 25.115319],\n                            [118.951901, 25.15162],\n                            [118.985162, 25.168954],\n                            [118.985162, 25.19495],\n                            [118.942046, 25.211195],\n                            [118.940198, 25.21715],\n                            [118.943278, 25.221482],\n                            [118.903242, 25.239347],\n                            [118.900162, 25.242595],\n                            [118.919256, 25.248008],\n                            [118.91556, 25.256668],\n                            [118.918024, 25.25721],\n                            [118.956212, 25.272905],\n                            [118.996864, 25.266411],\n                            [118.975307, 25.237723],\n                            [118.990089, 25.20199],\n                            [119.055379, 25.219316],\n                            [119.074473, 25.211195],\n                            [119.054147, 25.168412],\n                            [119.032589, 25.17437],\n                            [119.028893, 25.139702],\n                            [119.06585, 25.102855],\n                            [119.075705, 25.099604],\n                            [119.134219, 25.106107],\n                            [119.107118, 25.075214],\n                            [119.119436, 25.012861],\n                            [119.146538, 25.056782],\n                            [119.165632, 25.145661],\n                            [119.137299, 25.15487],\n                            [119.108349, 25.193867],\n                            [119.131755, 25.223106],\n                            [119.190269, 25.175995],\n                            [119.231537, 25.188993],\n                            [119.26911, 25.159746],\n                            [119.314689, 25.190076],\n                            [119.294979, 25.237182],\n                            [119.331935, 25.230685],\n                            [119.380595, 25.250173],\n                            [119.333167, 25.287516],\n                            [119.299291, 25.328634],\n                            [119.247552, 25.333502],\n                            [119.240776, 25.316733],\n                            [119.218603, 25.368115],\n                            [119.14469, 25.388121],\n                            [119.151465, 25.426503],\n                            [119.191501, 25.424341],\n                            [119.232153, 25.442176],\n                            [119.219834, 25.468654],\n                            [119.256175, 25.488643],\n                            [119.275269, 25.476758],\n                            [119.26295, 25.428124],\n                            [119.288204, 25.410827],\n                            [119.353493, 25.411908],\n                            [119.343638, 25.472436],\n                            [119.359037, 25.521592],\n                            [119.400921, 25.493505],\n                            [119.45266, 25.493505],\n                            [119.438493, 25.412449],\n                            [119.463131, 25.448661],\n                            [119.491464, 25.443257],\n                            [119.48592, 25.418935],\n                            [119.507478, 25.396231],\n                            [119.486536, 25.369737],\n                            [119.548746, 25.365952],\n                            [119.578927, 25.400556],\n                            [119.555521, 25.429205],\n                            [119.577695, 25.445959],\n                            [119.59063, 25.398394],\n                            [119.582623, 25.374063],\n                            [119.597405, 25.334584],\n                            [119.649144, 25.342697],\n                            [119.665159, 25.3719],\n                            [119.656535, 25.396772],\n                            [119.670086, 25.435691],\n                            [119.622659, 25.434069],\n                            [119.675014, 25.468113],\n                            [119.682405, 25.445959],\n                            [119.688564, 25.441095],\n                            [119.773564, 25.395691],\n                            [119.764325, 25.433529],\n                            [119.804977, 25.457847],\n                            [119.866571, 25.455145],\n                            [119.864107, 25.469734],\n                            [119.862875, 25.474597],\n                            [119.811136, 25.507009],\n                            [119.81668, 25.532393],\n                            [119.861027, 25.531313],\n                            [119.883817, 25.546432],\n                            [119.831462, 25.579905],\n                            [119.843165, 25.597717],\n                            [119.790194, 25.614447],\n                            [119.785883, 25.66786],\n                            [119.700267, 25.616606],\n                            [119.683637, 25.592859],\n                            [119.716898, 25.551292],\n                            [119.715666, 25.51187],\n                            [119.680557, 25.497827],\n                            [119.675014, 25.475137],\n                            [119.634362, 25.475137],\n                            [119.611572, 25.519972],\n                            [119.616499, 25.556691],\n                            [119.586934, 25.59232],\n                            [119.534579, 25.585303],\n                            [119.541355, 25.6247],\n                            [119.478529, 25.631715],\n                            [119.472986, 25.662466],\n                            [119.543819, 25.684581],\n                            [119.602949, 25.68512],\n                            [119.602949, 25.714779],\n                            [119.626354, 25.723406],\n                            [119.628202, 25.87212],\n                            [119.638057, 25.889888],\n                            [119.69534, 25.904424],\n                            [119.723673, 26.011503],\n                            [119.700267, 26.032477],\n                            [119.668854, 26.026024],\n                            [119.654688, 26.090002],\n                            [119.618963, 26.11956],\n                            [119.604181, 26.168985],\n                            [119.664543, 26.202282],\n                            [119.676246, 26.262943],\n                            [119.7711, 26.285481],\n                            [119.802513, 26.268846],\n                            [119.806825, 26.307479],\n                            [119.845013, 26.323036],\n                            [119.862875, 26.307479],\n                            [119.904143, 26.308552],\n                            [119.95465, 26.352534],\n                            [119.946027, 26.374519],\n                            [119.893672, 26.355752],\n                            [119.835774, 26.434019],\n                            [119.83639, 26.454381],\n                            [119.788346, 26.583435],\n                            [119.740303, 26.610727],\n                            [119.670086, 26.618218],\n                            [119.605412, 26.595744],\n                            [119.577695, 26.622498],\n                            [119.619579, 26.649246],\n                            [119.637441, 26.703256],\n                            [119.664543, 26.726243],\n                            [119.711354, 26.686681],\n                            [119.833926, 26.690959],\n                            [119.864107, 26.671174],\n                            [119.873962, 26.642827],\n                            [119.908455, 26.661547],\n                            [119.899216, 26.693098],\n                            [119.938636, 26.747088],\n                            [119.942947, 26.784492],\n                            [120.052584, 26.786629],\n                            [120.061824, 26.768997],\n                            [119.99407, 26.720363],\n                            [119.969433, 26.686681],\n                            [119.972512, 26.654594],\n                            [119.949107, 26.624638],\n                            [119.901679, 26.624638],\n                            [119.851788, 26.595209],\n                            [119.828383, 26.524013],\n                            [119.867187, 26.509019],\n                            [119.947875, 26.56042],\n                            [119.93802, 26.576478],\n                            [119.967585, 26.597885],\n                            [120.007621, 26.595744],\n                            [120.063671, 26.627848],\n                            [120.093852, 26.613938],\n                            [120.1382, 26.638012],\n                            [120.110483, 26.692563],\n                            [120.162222, 26.717691],\n                            [120.151135, 26.750829],\n                            [120.106787, 26.752966],\n                            [120.136352, 26.797847],\n                            [120.103707, 26.794642],\n                            [120.102476, 26.82669],\n                            [120.073526, 26.823485],\n                            [120.054432, 26.863533],\n                            [120.117874, 26.882751],\n                            [120.126497, 26.920644],\n                            [120.130193, 26.917976],\n                            [120.1807, 26.920644],\n                            [120.233055, 26.907837],\n                            [120.25954, 26.982526],\n                            [120.279866, 26.987326],\n                            [120.275554, 27.027315],\n                            [120.29588, 27.035845],\n                            [120.282946, 27.089671],\n                            [120.391967, 27.081146],\n                            [120.403054, 27.10086],\n                            [120.461568, 27.142407],\n                            [120.404286, 27.204166],\n                            [120.401822, 27.250996],\n                            [120.430155, 27.258976],\n                            [120.343924, 27.363199],\n                            [120.340844, 27.399867],\n                            [120.273091, 27.38924],\n                            [120.26262, 27.432804],\n                            [120.221352, 27.420055],\n                            [120.134504, 27.420055],\n                            [120.136968, 27.402523],\n                            [120.096316, 27.390302],\n                            [120.052584, 27.338747],\n                            [120.026099, 27.344063],\n                            [120.008237, 27.375423],\n                            [119.960194, 27.365857],\n                            [119.938636, 27.329709],\n                            [119.843165, 27.300464],\n                            [119.768636, 27.307909],\n                            [119.782187, 27.330241],\n                            [119.739687, 27.362668],\n                            [119.750774, 27.373829],\n                            [119.711354, 27.403054],\n                            [119.685485, 27.438646],\n                            [119.703347, 27.446613],\n                            [119.70889, 27.514042],\n                            [119.690412, 27.537394],\n                            [119.659615, 27.540578],\n                            [119.675014, 27.574534],\n                            [119.630666, 27.582491],\n                            [119.626354, 27.620676],\n                            [119.644217, 27.663619],\n                            [119.606028, 27.674749],\n                            [119.541971, 27.666799],\n                            [119.501319, 27.649837],\n                            [119.501935, 27.610601],\n                            [119.466826, 27.526249],\n                            [119.438493, 27.508734],\n                            [119.416935, 27.539517],\n                            [119.360269, 27.524657],\n                            [119.334399, 27.480067],\n                            [119.285124, 27.457766],\n                            [119.26911, 27.42218],\n                            [119.224146, 27.416868],\n                            [119.14777, 27.424836],\n                            [119.121284, 27.438115],\n                            [119.129907, 27.475289],\n                            [119.092335, 27.466262],\n                            [119.03998, 27.478475],\n                            [119.020886, 27.498118],\n                            [118.983314, 27.498649],\n                            [118.986393, 27.47582],\n                            [118.955597, 27.4498],\n                            [118.907553, 27.460952],\n                            [118.869365, 27.540047],\n                            [118.909401, 27.568168],\n                            [118.913713, 27.619616],\n                            [118.879836, 27.667859],\n                            [118.873677, 27.733563],\n                            [118.829329, 27.847921],\n                            [118.818242, 27.916689],\n                            [118.753568, 27.947885],\n                            [118.730163, 27.970615],\n                            [118.733858, 28.027684],\n                            [118.719076, 28.063601],\n                            [118.767735, 28.10584],\n                            [118.802228, 28.117453],\n                            [118.805923, 28.154923],\n                            [118.771431, 28.188687],\n                            [118.804075, 28.207675],\n                            [118.802228, 28.240368],\n                            [118.756032, 28.252493],\n                            [118.719692, 28.312047],\n                            [118.699366, 28.309939],\n                            [118.674728, 28.27147],\n                            [118.651322, 28.277267],\n                            [118.595272, 28.258292],\n                            [118.588497, 28.282538],\n                            [118.493026, 28.262509],\n                            [118.490562, 28.238259],\n                            [118.444367, 28.253548],\n                            [118.433896, 28.288335],\n                            [118.424041, 28.291497],\n                            [118.314404, 28.221913],\n                            [118.339041, 28.193962],\n                            [118.375382, 28.186577],\n                            [118.361215, 28.155978],\n                            [118.356288, 28.091586],\n                            [118.242339, 28.075746],\n                            [118.199839, 28.049869],\n                            [118.153644, 28.062016],\n                            [118.120999, 28.041946],\n                            [118.129006, 28.017118],\n                            [118.094513, 28.003909],\n                            [118.096977, 27.970615],\n                            [117.999043, 27.991227],\n                            [117.965166, 27.962687],\n                            [117.942992, 27.974315],\n                            [117.910963, 27.949471],\n                            [117.856145, 27.94577],\n                            [117.78716, 27.896063],\n                            [117.788392, 27.855858],\n                            [117.740348, 27.800286],\n                            [117.704624, 27.834162],\n                            [117.68245, 27.823577],\n                            [117.649805, 27.851625],\n                            [117.609769, 27.863265],\n                            [117.556182, 27.966387],\n                            [117.52169, 27.982243],\n                            [117.477958, 27.930966],\n                            [117.453936, 27.939955],\n                            [117.407741, 27.893948],\n                            [117.366473, 27.88231],\n                            [117.341836, 27.855858],\n                            [117.334444, 27.8876],\n                            [117.280242, 27.871201],\n                            [117.276546, 27.847921],\n                            [117.303031, 27.833103],\n                            [117.296256, 27.764282],\n                            [117.245133, 27.71926],\n                            [117.205097, 27.714492],\n                            [117.204481, 27.683759],\n                            [117.174916, 27.677399],\n                            [117.114554, 27.692238],\n                            [117.096076, 27.667329],\n                            [117.11209, 27.645596],\n                            [117.094228, 27.627569],\n                            [117.065279, 27.665739],\n                            [117.040641, 27.669979],\n                            [117.003685, 27.625449],\n                            [117.024627, 27.592569],\n                            [117.01662, 27.563393],\n                            [117.054808, 27.5427],\n                            [117.076982, 27.566046],\n                            [117.103467, 27.533149],\n                            [117.110242, 27.458828],\n                            [117.133032, 27.42218],\n                            [117.107163, 27.393491],\n                            [117.104699, 27.330773],\n                            [117.140423, 27.322798],\n                            [117.136728, 27.303123],\n                            [117.171836, 27.29036],\n                            [117.149662, 27.241419],\n                            [117.044953, 27.146667],\n                            [117.05296, 27.100327],\n                            [116.967344, 27.061962],\n                            [116.936547, 27.019319],\n                            [116.910062, 27.034779],\n                            [116.851548, 27.009188],\n                            [116.817671, 27.018252],\n                            [116.679085, 26.978259],\n                            [116.632889, 26.933984],\n                            [116.602092, 26.888623],\n                            [116.548506, 26.84004],\n                            [116.543578, 26.803723],\n                            [116.557745, 26.773806],\n                            [116.515245, 26.720898],\n                            [116.520172, 26.684543],\n                            [116.566368, 26.650315],\n                            [116.553433, 26.575942],\n                            [116.539267, 26.559349],\n                            [116.597165, 26.512768],\n                            [116.610716, 26.476882],\n                            [116.638433, 26.477418],\n                            [116.608252, 26.429732],\n                            [116.601476, 26.372911],\n                            [116.553433, 26.365404],\n                            [116.553433, 26.400253],\n                            [116.519557, 26.410437],\n                            [116.499846, 26.361651],\n                            [116.459194, 26.345026],\n                            [116.437021, 26.308016],\n                            [116.412999, 26.297822],\n                            [116.385282, 26.238253],\n                            [116.400064, 26.202819],\n                            [116.392057, 26.171133],\n                            [116.435789, 26.159854],\n                            [116.476441, 26.172745],\n                            [116.489375, 26.113649],\n                            [116.384666, 26.030864],\n                            [116.360028, 25.991601],\n                            [116.369883, 25.963088],\n                            [116.326152, 25.956631],\n                            [116.303362, 25.924341],\n                            [116.258398, 25.902809],\n                            [116.225138, 25.908731],\n                            [116.17771, 25.894195],\n                            [116.132131, 25.860273],\n                            [116.131515, 25.824185],\n                            [116.18079, 25.778926],\n                            [116.129667, 25.758985],\n                            [116.106877, 25.701299],\n                            [116.067457, 25.703995],\n                            [116.068689, 25.646282],\n                            [116.041588, 25.62416],\n                            [116.063145, 25.56317],\n                            [116.040356, 25.548052],\n                            [116.03666, 25.514571],\n                            [116.005247, 25.490264],\n                            [116.023109, 25.435691],\n                            [115.992928, 25.374063],\n                            [116.008327, 25.319437],\n                            [115.987385, 25.290221],\n                            [115.949813, 25.292386],\n                            [115.930719, 25.236099],\n                            [115.855574, 25.20957],\n                            [115.860501, 25.165704],\n                            [115.888219, 25.128866],\n                            [115.880212, 25.092016],\n                            [115.908545, 25.084428],\n                            [115.928255, 25.050276],\n                            [115.873436, 25.019911],\n                            [115.925175, 24.960786],\n                            [115.870356, 24.959701],\n                            [115.89253, 24.936911],\n                            [115.907929, 24.923343],\n                            [115.985537, 24.899461],\n                            [116.015102, 24.905975],\n                            [116.068073, 24.850053],\n                            [116.153073, 24.846795],\n                            [116.191877, 24.877203],\n                            [116.221442, 24.829959],\n                            [116.251007, 24.82507],\n                            [116.244232, 24.793563],\n                            [116.297202, 24.801712],\n                            [116.345862, 24.828872],\n                            [116.363724, 24.87123],\n                            [116.395137, 24.877746],\n                            [116.417927, 24.840821],\n                            [116.381586, 24.82507],\n                            [116.375427, 24.803885],\n                            [116.419158, 24.767482],\n                            [116.416079, 24.744113],\n                            [116.44626, 24.714216],\n                            [116.485064, 24.720196],\n                            [116.517709, 24.652225],\n                            [116.506622, 24.621218],\n                            [116.530027, 24.604895],\n                            [116.570679, 24.621762],\n                            [116.600861, 24.654401],\n                            [116.623034, 24.64189],\n                            [116.667382, 24.658752],\n                            [116.777635, 24.679418],\n                            [116.815207, 24.654944],\n                            [116.761005, 24.583128],\n                            [116.759157, 24.545572],\n                            [116.796729, 24.502014],\n                            [116.83307, 24.496568],\n                            [116.860787, 24.460075],\n                            [116.839229, 24.442097],\n                            [116.903903, 24.369614],\n                            [116.895895, 24.350533],\n                            [116.919301, 24.321087],\n                            [116.914374, 24.287817],\n                            [116.938395, 24.28127],\n                            [116.933468, 24.220157],\n                            [116.956257, 24.216883],\n                            [116.998757, 24.179217],\n                            [116.9347, 24.126794],\n                            [116.930388, 24.064514],\n                            [116.953178, 24.008218],\n                            [116.981511, 23.999471],\n                            [116.976583, 23.931659],\n                            [116.955642, 23.922359],\n                            [116.981511, 23.855602],\n                            [117.012308, 23.855054],\n                            [117.019083, 23.801952],\n                            [117.048032, 23.758687],\n                            [117.055424, 23.694038],\n                            [117.123793, 23.647448],\n                            [117.147199, 23.654027],\n                            [117.192778, 23.629356],\n                            [117.192778, 23.5619],\n                            [117.291328, 23.571225],\n                            [117.302415, 23.550379],\n                            [117.387415, 23.555317],\n                            [117.463791, 23.584937],\n                            [117.454552, 23.628259],\n                            [117.493357, 23.642514],\n                            [117.501364, 23.70445],\n                            [117.54448, 23.715956],\n                            [117.601762, 23.70171],\n                            [117.660276, 23.789357],\n                            [117.651653, 23.815093],\n                            [117.671979, 23.878041],\n                            [117.691073, 23.888985],\n                            [117.762522, 23.886796],\n                            [117.792703, 23.906494],\n                            [117.807486, 23.947521],\n                            [117.864768, 24.004938],\n                            [117.910347, 24.012045],\n                            [117.927594, 24.039922],\n                            [117.936217, 24.100029],\n                            [118.000275, 24.152462],\n                            [118.019369, 24.197232],\n                            [118.074803, 24.225615],\n                            [118.115455, 24.229435],\n                            [118.158571, 24.269814],\n                            [118.112376, 24.357075],\n                            [118.081579, 24.35653],\n                            [118.088354, 24.408858],\n                            [118.048934, 24.418122],\n                            [118.084042, 24.528695],\n                            [118.121615, 24.570067],\n                            [118.150564, 24.583673],\n                            [118.169042, 24.559725],\n                            [118.242955, 24.51236],\n                            [118.375382, 24.536317],\n                            [118.363679, 24.567889],\n                            [118.444367, 24.614689],\n                            [118.512736, 24.60816],\n                            [118.557084, 24.572788],\n                            [118.558316, 24.51236],\n                            [118.614366, 24.521617],\n                            [118.680272, 24.58204],\n                            [118.687047, 24.63373],\n                            [118.661178, 24.622306],\n                            [118.652554, 24.653857],\n                            [118.670417, 24.679962],\n                            [118.703677, 24.665278],\n                            [118.778822, 24.743569],\n                            [118.786213, 24.77672],\n                            [118.650707, 24.808774],\n                            [118.647627, 24.843536],\n                            [118.702445, 24.865258],\n                            [118.69875, 24.848967],\n                            [118.748641, 24.84245],\n                            [118.807771, 24.870687],\n                            [118.834256, 24.854397],\n                            [118.864437, 24.887518],\n                            [118.933423, 24.870687],\n                            [118.988857, 24.878831],\n                            [118.987009, 24.898375],\n                            [118.932807, 24.906518],\n                            [118.91864, 24.932569],\n                            [118.945741, 24.954275],\n                            [119.014111, 24.941252],\n                            [119.032589, 24.961328],\n                            [119.032589, 24.961871],\n                            [119.007335, 24.963499],\n                            [119.004872, 24.970009]\n                        ]\n                    ],\n                    [\n                        [\n                            [118.412338, 24.514538],\n                            [118.374766, 24.458986],\n                            [118.318715, 24.486765],\n                            [118.298389, 24.477506],\n                            [118.31194, 24.424661],\n                            [118.282375, 24.413218],\n                            [118.329802, 24.382152],\n                            [118.353208, 24.415398],\n                            [118.405563, 24.427931],\n                            [118.457918, 24.412128],\n                            [118.477012, 24.437738],\n                            [118.451758, 24.506915],\n                            [118.412338, 24.514538]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.471138, 25.197116],\n                            [119.507478, 25.183036],\n                            [119.52534, 25.157579],\n                            [119.549362, 25.161912],\n                            [119.566608, 25.210112],\n                            [119.540739, 25.20199],\n                            [119.501319, 25.21715],\n                            [119.473601, 25.259916],\n                            [119.44342, 25.238806],\n                            [119.444036, 25.20199],\n                            [119.471138, 25.197116]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.580159, 25.627398],\n                            [119.611572, 25.669479],\n                            [119.580775, 25.650059],\n                            [119.580159, 25.627398]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.976824, 26.191005],\n                            [120.016244, 26.217316],\n                            [119.998998, 26.235569],\n                            [119.970665, 26.217852],\n                            [119.976824, 26.191005]\n                        ]\n                    ],\n                    [\n                        [\n                            [118.230636, 24.401228],\n                            [118.273752, 24.441007],\n                            [118.233716, 24.445911],\n                            [118.230636, 24.401228]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.906607, 26.68989],\n                            [119.926933, 26.664756],\n                            [119.950954, 26.692563],\n                            [119.906607, 26.68989]\n                        ]\n                    ],\n                    [\n                        [\n                            [118.204151, 24.504737],\n                            [118.191832, 24.536861],\n                            [118.14502, 24.560814],\n                            [118.093281, 24.540672],\n                            [118.068644, 24.463344],\n                            [118.084042, 24.435559],\n                            [118.143173, 24.420847],\n                            [118.19368, 24.463344],\n                            [118.204151, 24.504737]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.929397, 26.134067],\n                            [119.960194, 26.146961],\n                            [119.919542, 26.172208],\n                            [119.929397, 26.134067]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.642985, 26.129231],\n                            [119.665159, 26.155556],\n                            [119.62697, 26.173282],\n                            [119.606028, 26.15287],\n                            [119.642985, 26.129231]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.034106, 26.488667],\n                            [120.066751, 26.498308],\n                            [120.071679, 26.521336],\n                            [120.035954, 26.515981],\n                            [120.034106, 26.488667]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.662079, 25.646822],\n                            [119.673782, 25.632794],\n                            [119.718745, 25.634952],\n                            [119.716898, 25.664624],\n                            [119.662079, 25.646822]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.760629, 26.613402],\n                            [119.776644, 26.600025],\n                            [119.818527, 26.616613],\n                            [119.796354, 26.630523],\n                            [119.760629, 26.613402]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.135736, 26.550784],\n                            [120.167149, 26.571661],\n                            [120.153598, 26.604841],\n                            [120.117874, 26.568984],\n                            [120.135736, 26.550784]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.360554, 26.916909],\n                            [120.394431, 26.933984],\n                            [120.363018, 26.967592],\n                            [120.327909, 26.963858],\n                            [120.319286, 26.944654],\n                            [120.360554, 26.916909]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.150519, 26.798916],\n                            [120.140048, 26.795176],\n                            [120.163454, 26.798381],\n                            [120.161606, 26.803189],\n                            [120.150519, 26.798916]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.668238, 26.628383],\n                            [119.720593, 26.635873],\n                            [119.758781, 26.659408],\n                            [119.748926, 26.681334],\n                            [119.712586, 26.6685],\n                            [119.673782, 26.680799],\n                            [119.651608, 26.657269],\n                            [119.668238, 26.628383]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 360000,\n                \"name\": \"Jiangxi\",\n                \"center\": [115.892151, 28.676493],\n                \"centroid\": [115.732975, 27.636112],\n                \"childrenNum\": 11,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 13,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [118.193064, 29.395472],\n                            [118.136397, 29.418932],\n                            [118.127774, 29.47209],\n                            [118.143788, 29.489803],\n                            [118.095129, 29.534072],\n                            [118.050782, 29.542924],\n                            [118.042774, 29.566351],\n                            [118.00397, 29.578322],\n                            [117.933753, 29.549172],\n                            [117.872775, 29.54761],\n                            [117.795167, 29.570515],\n                            [117.729877, 29.550213],\n                            [117.690457, 29.555939],\n                            [117.678754, 29.595496],\n                            [117.647957, 29.614749],\n                            [117.608537, 29.591333],\n                            [117.543248, 29.588731],\n                            [117.523538, 29.630356],\n                            [117.530313, 29.654282],\n                            [117.490277, 29.660003],\n                            [117.453936, 29.688082],\n                            [117.455168, 29.749412],\n                            [117.408973, 29.802396],\n                            [117.415132, 29.85068],\n                            [117.382487, 29.840818],\n                            [117.359082, 29.812782],\n                            [117.338756, 29.848085],\n                            [117.29256, 29.822647],\n                            [117.25314, 29.834588],\n                            [117.261763, 29.880781],\n                            [117.246365, 29.915023],\n                            [117.2168, 29.926953],\n                            [117.171836, 29.920729],\n                            [117.129952, 29.89946],\n                            [117.127489, 29.86158],\n                            [117.073286, 29.831992],\n                            [117.123177, 29.798761],\n                            [117.136728, 29.775388],\n                            [117.108395, 29.75201],\n                            [117.112706, 29.711995],\n                            [117.041873, 29.680803],\n                            [116.996294, 29.683403],\n                            [116.974736, 29.657403],\n                            [116.939627, 29.648561],\n                            [116.873722, 29.609546],\n                            [116.849084, 29.57624],\n                            [116.780715, 29.569994],\n                            [116.760389, 29.599139],\n                            [116.721585, 29.564789],\n                            [116.716657, 29.590813],\n                            [116.651983, 29.637118],\n                            [116.680317, 29.681323],\n                            [116.653831, 29.694841],\n                            [116.673541, 29.709916],\n                            [116.762237, 29.802396],\n                            [116.780715, 29.792529],\n                            [116.882961, 29.893753],\n                            [116.900207, 29.949253],\n                            [116.868794, 29.980361],\n                            [116.83307, 29.95755],\n                            [116.830606, 30.004723],\n                            [116.802889, 29.99643],\n                            [116.783794, 30.030632],\n                            [116.747454, 30.057053],\n                            [116.720353, 30.053945],\n                            [116.666766, 30.076734],\n                            [116.620571, 30.073109],\n                            [116.585462, 30.045657],\n                            [116.552201, 29.909836],\n                            [116.525716, 29.897385],\n                            [116.467818, 29.896347],\n                            [116.342782, 29.835626],\n                            [116.280572, 29.788893],\n                            [116.250391, 29.785777],\n                            [116.227601, 29.816936],\n                            [116.172783, 29.828358],\n                            [116.13521, 29.819532],\n                            [116.087167, 29.795125],\n                            [116.049595, 29.761881],\n                            [115.965827, 29.724469],\n                            [115.909777, 29.723949],\n                            [115.837096, 29.748373],\n                            [115.762567, 29.793048],\n                            [115.706517, 29.837703],\n                            [115.667712, 29.850161],\n                            [115.611662, 29.841337],\n                            [115.51188, 29.840299],\n                            [115.479235, 29.811224],\n                            [115.470612, 29.739539],\n                            [115.412714, 29.688602],\n                            [115.355431, 29.649602],\n                            [115.304924, 29.637118],\n                            [115.28583, 29.618391],\n                            [115.250722, 29.660003],\n                            [115.176809, 29.654803],\n                            [115.113367, 29.684963],\n                            [115.117679, 29.655843],\n                            [115.143548, 29.645961],\n                            [115.120142, 29.597578],\n                            [115.157099, 29.584568],\n                            [115.154019, 29.510117],\n                            [115.086266, 29.525741],\n                            [115.087498, 29.560104],\n                            [115.033295, 29.546568],\n                            [115.00065, 29.572076],\n                            [114.947679, 29.542924],\n                            [114.966773, 29.522096],\n                            [114.940288, 29.493971],\n                            [114.900868, 29.505951],\n                            [114.860216, 29.476258],\n                            [114.888549, 29.436134],\n                            [114.918114, 29.454374],\n                            [114.90518, 29.473132],\n                            [114.935977, 29.486678],\n                            [114.947063, 29.465317],\n                            [114.931049, 29.422581],\n                            [114.895325, 29.397557],\n                            [114.866375, 29.404335],\n                            [114.812173, 29.383478],\n                            [114.784455, 29.386086],\n                            [114.759818, 29.363139],\n                            [114.740724, 29.386607],\n                            [114.67297, 29.395993],\n                            [114.621847, 29.379828],\n                            [114.589819, 29.352707],\n                            [114.519602, 29.325578],\n                            [114.466015, 29.324013],\n                            [114.440145, 29.341752],\n                            [114.376088, 29.322969],\n                            [114.341595, 29.327665],\n                            [114.307102, 29.365225],\n                            [114.259059, 29.343839],\n                            [114.252284, 29.23475],\n                            [114.169748, 29.216993],\n                            [114.063191, 29.204978],\n                            [114.034857, 29.152204],\n                            [113.98743, 29.126068],\n                            [113.952321, 29.092604],\n                            [113.94185, 29.047097],\n                            [113.961561, 28.999476],\n                            [113.955401, 28.978536],\n                            [113.973879, 28.937692],\n                            [114.008988, 28.955498],\n                            [114.005292, 28.917788],\n                            [114.028082, 28.891069],\n                            [114.060111, 28.902596],\n                            [114.056415, 28.872204],\n                            [114.076741, 28.834464],\n                            [114.124784, 28.843376],\n                            [114.153734, 28.829221],\n                            [114.137719, 28.779926],\n                            [114.157429, 28.761566],\n                            [114.122321, 28.623497],\n                            [114.132176, 28.607211],\n                            [114.08598, 28.558337],\n                            [114.138335, 28.533629],\n                            [114.15435, 28.507337],\n                            [114.218407, 28.48472],\n                            [114.217175, 28.466308],\n                            [114.172212, 28.432632],\n                            [114.214712, 28.403157],\n                            [114.252284, 28.395787],\n                            [114.2529, 28.319423],\n                            [114.198081, 28.29097],\n                            [114.182067, 28.249858],\n                            [114.143879, 28.246694],\n                            [114.109386, 28.205038],\n                            [114.107538, 28.182885],\n                            [114.068734, 28.171806],\n                            [114.012068, 28.174972],\n                            [113.992357, 28.161255],\n                            [114.025002, 28.080499],\n                            [114.047176, 28.057263],\n                            [114.025618, 28.031382],\n                            [113.970184, 28.041418],\n                            [113.966488, 28.017646],\n                            [113.936307, 28.018703],\n                            [113.914133, 27.991227],\n                            [113.864242, 28.004966],\n                            [113.845148, 27.971672],\n                            [113.822974, 27.982243],\n                            [113.752141, 27.93361],\n                            [113.72812, 27.874904],\n                            [113.756453, 27.860091],\n                            [113.763228, 27.799228],\n                            [113.69917, 27.740979],\n                            [113.696707, 27.71979],\n                            [113.652359, 27.663619],\n                            [113.607395, 27.625449],\n                            [113.608627, 27.585143],\n                            [113.579062, 27.545354],\n                            [113.583374, 27.524657],\n                            [113.627105, 27.49971],\n                            [113.591381, 27.467855],\n                            [113.59754, 27.428554],\n                            [113.632033, 27.40518],\n                            [113.605548, 27.38924],\n                            [113.616635, 27.345658],\n                            [113.657902, 27.347253],\n                            [113.699786, 27.331836],\n                            [113.72812, 27.350442],\n                            [113.872865, 27.384988],\n                            [113.872865, 27.346721],\n                            [113.854387, 27.30525],\n                            [113.872865, 27.289828],\n                            [113.846996, 27.222262],\n                            [113.779242, 27.137081],\n                            [113.771851, 27.096598],\n                            [113.803264, 27.099261],\n                            [113.824206, 27.036378],\n                            [113.86301, 27.018252],\n                            [113.892575, 26.964925],\n                            [113.927068, 26.948922],\n                            [113.890112, 26.895562],\n                            [113.877177, 26.859262],\n                            [113.835909, 26.806394],\n                            [113.853771, 26.769532],\n                            [113.860546, 26.664221],\n                            [113.912901, 26.613938],\n                            [113.996669, 26.615543],\n                            [114.019459, 26.587182],\n                            [114.10877, 26.56952],\n                            [114.07243, 26.480096],\n                            [114.110002, 26.482775],\n                            [114.090292, 26.455988],\n                            [114.085364, 26.406149],\n                            [114.062575, 26.406149],\n                            [114.030546, 26.376664],\n                            [114.047792, 26.337518],\n                            [114.021307, 26.288701],\n                            [114.029314, 26.266163],\n                            [113.978807, 26.237716],\n                            [113.972647, 26.20604],\n                            [113.949242, 26.192616],\n                            [113.962792, 26.150722],\n                            [114.013299, 26.184023],\n                            [114.088444, 26.168448],\n                            [114.102611, 26.187783],\n                            [114.181451, 26.214631],\n                            [114.216559, 26.203355],\n                            [114.237501, 26.152333],\n                            [114.188842, 26.121172],\n                            [114.10569, 26.097526],\n                            [114.121089, 26.085702],\n                            [114.087828, 26.06635],\n                            [114.044096, 26.076564],\n                            [114.008372, 26.015806],\n                            [114.028082, 25.98138],\n                            [114.028082, 25.893119],\n                            [113.971416, 25.836036],\n                            [113.961561, 25.77731],\n                            [113.920293, 25.741197],\n                            [113.913517, 25.701299],\n                            [113.957249, 25.611749],\n                            [113.983118, 25.599336],\n                            [113.986198, 25.529153],\n                            [113.962792, 25.528072],\n                            [113.94493, 25.441635],\n                            [114.003444, 25.442716],\n                            [113.983118, 25.415152],\n                            [114.050256, 25.36433],\n                            [114.029314, 25.328093],\n                            [114.017611, 25.273987],\n                            [114.039785, 25.250714],\n                            [114.055799, 25.277775],\n                            [114.083517, 25.275611],\n                            [114.115545, 25.302125],\n                            [114.190074, 25.316733],\n                            [114.204857, 25.29942],\n                            [114.260291, 25.291845],\n                            [114.2954, 25.299961],\n                            [114.31511, 25.33837],\n                            [114.382863, 25.317274],\n                            [114.43029, 25.343779],\n                            [114.438914, 25.376226],\n                            [114.477718, 25.37136],\n                            [114.541159, 25.416773],\n                            [114.599674, 25.385959],\n                            [114.63663, 25.324306],\n                            [114.714238, 25.315651],\n                            [114.743188, 25.274528],\n                            [114.73518, 25.225813],\n                            [114.693912, 25.213902],\n                            [114.685905, 25.173287],\n                            [114.73518, 25.155954],\n                            [114.735796, 25.121822],\n                            [114.664963, 25.10123],\n                            [114.640326, 25.074129],\n                            [114.604601, 25.083886],\n                            [114.561485, 25.077382],\n                            [114.532536, 25.022623],\n                            [114.506051, 24.999844],\n                            [114.45616, 24.99659],\n                            [114.454928, 24.977062],\n                            [114.395798, 24.951019],\n                            [114.403189, 24.877746],\n                            [114.378551, 24.861457],\n                            [114.342211, 24.807145],\n                            [114.336052, 24.749004],\n                            [114.281849, 24.724001],\n                            [114.27261, 24.700624],\n                            [114.169132, 24.689749],\n                            [114.19069, 24.656576],\n                            [114.258443, 24.641346],\n                            [114.289856, 24.619042],\n                            [114.300943, 24.578775],\n                            [114.363769, 24.582584],\n                            [114.391486, 24.563535],\n                            [114.403189, 24.497657],\n                            [114.429058, 24.48622],\n                            [114.534384, 24.559181],\n                            [114.589819, 24.537406],\n                            [114.627391, 24.576598],\n                            [114.664963, 24.583673],\n                            [114.704999, 24.525973],\n                            [114.73826, 24.565168],\n                            [114.729637, 24.608704],\n                            [114.781376, 24.613057],\n                            [114.827571, 24.588026],\n                            [114.846665, 24.602719],\n                            [114.868839, 24.562446],\n                            [114.893477, 24.582584],\n                            [114.909491, 24.661471],\n                            [114.940288, 24.650049],\n                            [115.00373, 24.679418],\n                            [115.024672, 24.669085],\n                            [115.057317, 24.703343],\n                            [115.083802, 24.699537],\n                            [115.104744, 24.667997],\n                            [115.1842, 24.711498],\n                            [115.258729, 24.728894],\n                            [115.269816, 24.749548],\n                            [115.306772, 24.758787],\n                            [115.358511, 24.735416],\n                            [115.372678, 24.774546],\n                            [115.412714, 24.79302],\n                            [115.476771, 24.762591],\n                            [115.522967, 24.702799],\n                            [115.555611, 24.683768],\n                            [115.569778, 24.622306],\n                            [115.605503, 24.62557],\n                            [115.671408, 24.604895],\n                            [115.68927, 24.545027],\n                            [115.752712, 24.546116],\n                            [115.785357, 24.567345],\n                            [115.843871, 24.562446],\n                            [115.840791, 24.584217],\n                            [115.797676, 24.628834],\n                            [115.780429, 24.663103],\n                            [115.801371, 24.705517],\n                            [115.769342, 24.708236],\n                            [115.756408, 24.749004],\n                            [115.776734, 24.774546],\n                            [115.764415, 24.791933],\n                            [115.790284, 24.856027],\n                            [115.807531, 24.862543],\n                            [115.824161, 24.909232],\n                            [115.863581, 24.891318],\n                            [115.861733, 24.863629],\n                            [115.907313, 24.879917],\n                            [115.885139, 24.898918],\n                            [115.89253, 24.936911],\n                            [115.870356, 24.959701],\n                            [115.925175, 24.960786],\n                            [115.873436, 25.019911],\n                            [115.928255, 25.050276],\n                            [115.908545, 25.084428],\n                            [115.880212, 25.092016],\n                            [115.888219, 25.128866],\n                            [115.860501, 25.165704],\n                            [115.855574, 25.20957],\n                            [115.930719, 25.236099],\n                            [115.949813, 25.292386],\n                            [115.987385, 25.290221],\n                            [116.008327, 25.319437],\n                            [115.992928, 25.374063],\n                            [116.023109, 25.435691],\n                            [116.005247, 25.490264],\n                            [116.03666, 25.514571],\n                            [116.040356, 25.548052],\n                            [116.063145, 25.56317],\n                            [116.041588, 25.62416],\n                            [116.068689, 25.646282],\n                            [116.067457, 25.703995],\n                            [116.106877, 25.701299],\n                            [116.129667, 25.758985],\n                            [116.18079, 25.778926],\n                            [116.131515, 25.824185],\n                            [116.132131, 25.860273],\n                            [116.17771, 25.894195],\n                            [116.225138, 25.908731],\n                            [116.258398, 25.902809],\n                            [116.303362, 25.924341],\n                            [116.326152, 25.956631],\n                            [116.369883, 25.963088],\n                            [116.360028, 25.991601],\n                            [116.384666, 26.030864],\n                            [116.489375, 26.113649],\n                            [116.476441, 26.172745],\n                            [116.435789, 26.159854],\n                            [116.392057, 26.171133],\n                            [116.400064, 26.202819],\n                            [116.385282, 26.238253],\n                            [116.412999, 26.297822],\n                            [116.437021, 26.308016],\n                            [116.459194, 26.345026],\n                            [116.499846, 26.361651],\n                            [116.519557, 26.410437],\n                            [116.553433, 26.400253],\n                            [116.553433, 26.365404],\n                            [116.601476, 26.372911],\n                            [116.608252, 26.429732],\n                            [116.638433, 26.477418],\n                            [116.610716, 26.476882],\n                            [116.597165, 26.512768],\n                            [116.539267, 26.559349],\n                            [116.553433, 26.575942],\n                            [116.566368, 26.650315],\n                            [116.520172, 26.684543],\n                            [116.515245, 26.720898],\n                            [116.557745, 26.773806],\n                            [116.543578, 26.803723],\n                            [116.548506, 26.84004],\n                            [116.602092, 26.888623],\n                            [116.632889, 26.933984],\n                            [116.679085, 26.978259],\n                            [116.817671, 27.018252],\n                            [116.851548, 27.009188],\n                            [116.910062, 27.034779],\n                            [116.936547, 27.019319],\n                            [116.967344, 27.061962],\n                            [117.05296, 27.100327],\n                            [117.044953, 27.146667],\n                            [117.149662, 27.241419],\n                            [117.171836, 27.29036],\n                            [117.136728, 27.303123],\n                            [117.140423, 27.322798],\n                            [117.104699, 27.330773],\n                            [117.107163, 27.393491],\n                            [117.133032, 27.42218],\n                            [117.110242, 27.458828],\n                            [117.103467, 27.533149],\n                            [117.076982, 27.566046],\n                            [117.054808, 27.5427],\n                            [117.01662, 27.563393],\n                            [117.024627, 27.592569],\n                            [117.003685, 27.625449],\n                            [117.040641, 27.669979],\n                            [117.065279, 27.665739],\n                            [117.094228, 27.627569],\n                            [117.11209, 27.645596],\n                            [117.096076, 27.667329],\n                            [117.114554, 27.692238],\n                            [117.174916, 27.677399],\n                            [117.204481, 27.683759],\n                            [117.205097, 27.714492],\n                            [117.245133, 27.71926],\n                            [117.296256, 27.764282],\n                            [117.303031, 27.833103],\n                            [117.276546, 27.847921],\n                            [117.280242, 27.871201],\n                            [117.334444, 27.8876],\n                            [117.341836, 27.855858],\n                            [117.366473, 27.88231],\n                            [117.407741, 27.893948],\n                            [117.453936, 27.939955],\n                            [117.477958, 27.930966],\n                            [117.52169, 27.982243],\n                            [117.556182, 27.966387],\n                            [117.609769, 27.863265],\n                            [117.649805, 27.851625],\n                            [117.68245, 27.823577],\n                            [117.704624, 27.834162],\n                            [117.740348, 27.800286],\n                            [117.788392, 27.855858],\n                            [117.78716, 27.896063],\n                            [117.856145, 27.94577],\n                            [117.910963, 27.949471],\n                            [117.942992, 27.974315],\n                            [117.965166, 27.962687],\n                            [117.999043, 27.991227],\n                            [118.096977, 27.970615],\n                            [118.094513, 28.003909],\n                            [118.129006, 28.017118],\n                            [118.120999, 28.041946],\n                            [118.153644, 28.062016],\n                            [118.199839, 28.049869],\n                            [118.242339, 28.075746],\n                            [118.356288, 28.091586],\n                            [118.361215, 28.155978],\n                            [118.375382, 28.186577],\n                            [118.339041, 28.193962],\n                            [118.314404, 28.221913],\n                            [118.424041, 28.291497],\n                            [118.433896, 28.288335],\n                            [118.480091, 28.327325],\n                            [118.455454, 28.384204],\n                            [118.432048, 28.402104],\n                            [118.456686, 28.424738],\n                            [118.474548, 28.478934],\n                            [118.414802, 28.497344],\n                            [118.4302, 28.515225],\n                            [118.412338, 28.55676],\n                            [118.428352, 28.617193],\n                            [118.428352, 28.617193],\n                            [118.428352, 28.681267],\n                            [118.403099, 28.702791],\n                            [118.364295, 28.813491],\n                            [118.300237, 28.826075],\n                            [118.270056, 28.918836],\n                            [118.195527, 28.904167],\n                            [118.227556, 28.942406],\n                            [118.165346, 28.986912],\n                            [118.133933, 28.983771],\n                            [118.115455, 29.009944],\n                            [118.115455, 29.009944],\n                            [118.097593, 28.998952],\n                            [118.066796, 29.053898],\n                            [118.076035, 29.074822],\n                            [118.037847, 29.102017],\n                            [118.045238, 29.149068],\n                            [118.027992, 29.167882],\n                            [118.042159, 29.210202],\n                            [118.073571, 29.216993],\n                            [118.077883, 29.290614],\n                            [118.138861, 29.283828],\n                            [118.178281, 29.297921],\n                            [118.166578, 29.314099],\n                            [118.205382, 29.343839],\n                            [118.193064, 29.395472]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 370000,\n                \"name\": \"Shandong\",\n                \"center\": [117.000923, 36.675807],\n                \"centroid\": [118.187759, 36.376092],\n                \"childrenNum\": 16,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 14,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [116.374195, 34.640011],\n                            [116.392057, 34.710391],\n                            [116.363724, 34.715311],\n                            [116.369267, 34.749247],\n                            [116.403144, 34.756131],\n                            [116.408071, 34.850972],\n                            [116.445028, 34.895652],\n                            [116.557745, 34.908905],\n                            [116.613795, 34.922645],\n                            [116.622418, 34.939818],\n                            [116.677853, 34.939327],\n                            [116.781331, 34.916757],\n                            [116.789338, 34.975133],\n                            [116.815823, 34.965324],\n                            [116.821983, 34.929515],\n                            [116.858323, 34.928533],\n                            [116.922381, 34.894671],\n                            [116.929156, 34.843114],\n                            [116.966113, 34.844588],\n                            [116.979047, 34.815113],\n                            [116.95133, 34.81069],\n                            [116.969192, 34.771864],\n                            [117.022163, 34.759081],\n                            [117.070206, 34.713835],\n                            [117.061583, 34.675947],\n                            [117.073286, 34.639026],\n                            [117.104083, 34.648874],\n                            [117.15151, 34.559222],\n                            [117.139191, 34.526687],\n                            [117.166293, 34.434435],\n                            [117.248213, 34.451216],\n                            [117.252524, 34.48674],\n                            [117.27285, 34.499565],\n                            [117.267923, 34.532603],\n                            [117.303647, 34.542463],\n                            [117.27285, 34.556757],\n                            [117.311654, 34.561686],\n                            [117.311654, 34.561686],\n                            [117.32151, 34.566614],\n                            [117.32151, 34.566614],\n                            [117.325205, 34.573021],\n                            [117.325205, 34.573021],\n                            [117.370785, 34.584846],\n                            [117.402813, 34.569571],\n                            [117.402813, 34.550843],\n                            [117.465023, 34.484767],\n                            [117.53832, 34.467006],\n                            [117.592523, 34.462566],\n                            [117.609769, 34.490686],\n                            [117.659044, 34.501044],\n                            [117.684298, 34.547392],\n                            [117.801942, 34.518798],\n                            [117.791471, 34.583368],\n                            [117.793935, 34.651827],\n                            [117.902956, 34.644443],\n                            [117.909732, 34.670533],\n                            [117.951615, 34.678408],\n                            [118.053861, 34.650843],\n                            [118.084042, 34.655766],\n                            [118.114839, 34.614404],\n                            [118.079115, 34.569571],\n                            [118.185056, 34.543942],\n                            [118.16473, 34.50499],\n                            [118.132702, 34.483287],\n                            [118.177665, 34.45319],\n                            [118.179513, 34.379628],\n                            [118.217701, 34.379134],\n                            [118.220165, 34.405802],\n                            [118.277447, 34.404814],\n                            [118.290382, 34.424563],\n                            [118.379693, 34.415183],\n                            [118.404947, 34.427525],\n                            [118.416034, 34.473914],\n                            [118.439439, 34.507949],\n                            [118.424657, 34.595193],\n                            [118.439439, 34.626223],\n                            [118.473932, 34.623269],\n                            [118.460997, 34.656258],\n                            [118.545997, 34.705964],\n                            [118.601431, 34.714327],\n                            [118.607591, 34.694155],\n                            [118.664257, 34.693663],\n                            [118.690127, 34.678408],\n                            [118.739402, 34.693663],\n                            [118.783749, 34.723181],\n                            [118.764039, 34.740396],\n                            [118.719076, 34.745313],\n                            [118.739402, 34.792508],\n                            [118.772047, 34.794474],\n                            [118.80038, 34.843114],\n                            [118.805307, 34.87307],\n                            [118.860742, 34.944233],\n                            [118.86259, 35.025626],\n                            [118.928495, 35.051106],\n                            [118.942662, 35.040817],\n                            [119.027045, 35.055516],\n                            [119.114509, 35.055026],\n                            [119.137915, 35.096167],\n                            [119.217371, 35.106939],\n                            [119.250016, 35.124562],\n                            [119.286972, 35.115261],\n                            [119.306066, 35.076578],\n                            [119.354109, 35.080007],\n                            [119.373819, 35.078538],\n                            [119.428022, 35.121136],\n                            [119.397841, 35.137777],\n                            [119.411392, 35.231689],\n                            [119.450812, 35.285443],\n                            [119.493312, 35.318655],\n                            [119.538275, 35.296678],\n                            [119.543819, 35.347949],\n                            [119.590014, 35.37284],\n                            [119.579543, 35.406504],\n                            [119.618963, 35.459655],\n                            [119.663311, 35.562931],\n                            [119.662079, 35.589215],\n                            [119.718129, 35.615492],\n                            [119.75139, 35.617924],\n                            [119.772332, 35.578995],\n                            [119.780339, 35.584835],\n                            [119.792658, 35.615492],\n                            [119.824071, 35.646136],\n                            [119.83023, 35.620357],\n                            [119.868419, 35.60868],\n                            [119.925085, 35.637382],\n                            [119.91215, 35.660725],\n                            [119.950339, 35.729741],\n                            [119.920157, 35.739943],\n                            [119.926317, 35.759856],\n                            [119.958346, 35.760342],\n                            [120.01378, 35.714193],\n                            [120.049505, 35.786562],\n                            [120.032258, 35.812288],\n                            [120.064287, 35.873414],\n                            [120.112331, 35.885052],\n                            [120.125265, 35.906868],\n                            [120.152983, 35.907353],\n                            [120.207801, 35.947575],\n                            [120.169613, 35.888446],\n                            [120.202258, 35.89184],\n                            [120.209033, 35.917531],\n                            [120.265699, 35.966468],\n                            [120.30512, 35.971796],\n                            [120.316206, 36.002304],\n                            [120.289721, 36.017311],\n                            [120.285409, 36.01247],\n                            [120.249069, 35.992136],\n                            [120.257076, 36.025055],\n                            [120.198562, 35.995525],\n                            [120.234902, 36.030863],\n                            [120.239214, 36.062316],\n                            [120.181316, 36.066669],\n                            [120.152367, 36.095206],\n                            [120.116642, 36.102943],\n                            [120.108635, 36.127599],\n                            [120.142512, 36.143549],\n                            [120.140664, 36.173507],\n                            [120.181316, 36.203936],\n                            [120.22012, 36.209248],\n                            [120.224432, 36.19138],\n                            [120.260772, 36.198624],\n                            [120.263236, 36.182202],\n                            [120.310047, 36.185101],\n                            [120.297112, 36.225664],\n                            [120.319902, 36.232423],\n                            [120.362402, 36.196209],\n                            [120.35809, 36.174956],\n                            [120.286025, 36.047317],\n                            [120.337764, 36.055058],\n                            [120.429539, 36.056994],\n                            [120.468959, 36.087952],\n                            [120.546568, 36.091821],\n                            [120.546568, 36.107778],\n                            [120.593995, 36.100525],\n                            [120.615553, 36.120348],\n                            [120.64327, 36.114547],\n                            [120.672835, 36.130016],\n                            [120.712255, 36.126632],\n                            [120.696857, 36.15563],\n                            [120.696857, 36.203936],\n                            [120.680843, 36.238698],\n                            [120.686386, 36.279234],\n                            [120.657437, 36.276339],\n                            [120.66298, 36.331803],\n                            [120.744284, 36.327946],\n                            [120.694393, 36.390118],\n                            [120.759683, 36.46283],\n                            [120.828668, 36.46668],\n                            [120.837291, 36.459942],\n                            [120.858849, 36.424797],\n                            [120.848994, 36.403124],\n                            [120.871784, 36.36699],\n                            [120.911204, 36.412276],\n                            [120.917979, 36.417573],\n                            [120.90874, 36.450315],\n                            [120.938305, 36.447908],\n                            [120.965407, 36.466199],\n                            [120.95432, 36.507578],\n                            [120.983269, 36.546051],\n                            [120.962327, 36.562877],\n                            [120.909972, 36.568645],\n                            [120.884718, 36.601323],\n                            [120.847146, 36.618617],\n                            [120.882255, 36.627262],\n                            [120.926602, 36.611892],\n                            [120.955551, 36.575855],\n                            [121.028848, 36.572971],\n                            [121.078123, 36.607568],\n                            [121.161275, 36.651273],\n                            [121.251818, 36.671436],\n                            [121.29863, 36.702151],\n                            [121.31218, 36.702151],\n                            [121.35776, 36.713186],\n                            [121.400876, 36.701191],\n                            [121.3941, 36.738129],\n                            [121.454462, 36.752515],\n                            [121.496962, 36.795179],\n                            [121.506817, 36.803805],\n                            [121.565331, 36.830635],\n                            [121.548701, 36.807638],\n                            [121.485259, 36.786073],\n                            [121.532071, 36.73621],\n                            [121.575186, 36.740047],\n                            [121.556092, 36.764502],\n                            [121.651563, 36.723739],\n                            [121.631853, 36.80093],\n                            [121.6762, 36.819137],\n                            [121.726092, 36.826323],\n                            [121.762432, 36.84644],\n                            [121.767975, 36.874691],\n                            [121.927504, 36.932597],\n                            [121.965076, 36.938337],\n                            [122.008808, 36.96225],\n                            [122.042684, 36.871819],\n                            [122.051923, 36.904846],\n                            [122.093191, 36.913938],\n                            [122.115981, 36.94025],\n                            [122.124604, 36.944077],\n                            [122.141235, 36.938337],\n                            [122.119677, 36.891924],\n                            [122.175727, 36.894317],\n                            [122.188662, 36.866073],\n                            [122.174495, 36.842609],\n                            [122.220691, 36.848835],\n                            [122.275509, 36.83734],\n                            [122.280437, 36.835904],\n                            [122.344495, 36.828239],\n                            [122.378371, 36.844525],\n                            [122.383915, 36.865595],\n                            [122.415944, 36.85937],\n                            [122.454748, 36.879],\n                            [122.452284, 36.88618],\n                            [122.434422, 36.914416],\n                            [122.483081, 36.913938],\n                            [122.48924, 36.886659],\n                            [122.532356, 36.901496],\n                            [122.55761, 36.968467],\n                            [122.544675, 37.004797],\n                            [122.583479, 37.037289],\n                            [122.575472, 37.054485],\n                            [122.494168, 37.033945],\n                            [122.467067, 37.037289],\n                            [122.478769, 37.058784],\n                            [122.484313, 37.128956],\n                            [122.533588, 37.153286],\n                            [122.581015, 37.147562],\n                            [122.573624, 37.176178],\n                            [122.624131, 37.190959],\n                            [122.592718, 37.261485],\n                            [122.567465, 37.25958],\n                            [122.573624, 37.296247],\n                            [122.611196, 37.339558],\n                            [122.607501, 37.364296],\n                            [122.650616, 37.388551],\n                            [122.6925, 37.373809],\n                            [122.714058, 37.392355],\n                            [122.701739, 37.418501],\n                            [122.67587, 37.413273],\n                            [122.641377, 37.428482],\n                            [122.553914, 37.407093],\n                            [122.4954, 37.413748],\n                            [122.487393, 37.43466],\n                            [122.41656, 37.414699],\n                            [122.337103, 37.414223],\n                            [122.281053, 37.430858],\n                            [122.287212, 37.445114],\n                            [122.25272, 37.467917],\n                            [122.194205, 37.456041],\n                            [122.166488, 37.438937],\n                            [122.131996, 37.49926],\n                            [122.163408, 37.519199],\n                            [122.150474, 37.557163],\n                            [122.08888, 37.554316],\n                            [122.075329, 37.540556],\n                            [122.017431, 37.531065],\n                            [121.997721, 37.494512],\n                            [121.923808, 37.473142],\n                            [121.772903, 37.466492],\n                            [121.66573, 37.473617],\n                            [121.635548, 37.494037],\n                            [121.575802, 37.460317],\n                            [121.571491, 37.441313],\n                            [121.477252, 37.475992],\n                            [121.460006, 37.522522],\n                            [121.400876, 37.557638],\n                            [121.395948, 37.589891],\n                            [121.435368, 37.592737],\n                            [121.391021, 37.625449],\n                            [121.349137, 37.635403],\n                            [121.358376, 37.597479],\n                            [121.304789, 37.582778],\n                            [121.217326, 37.582778],\n                            [121.17421, 37.597479],\n                            [121.148956, 37.626397],\n                            [121.161891, 37.646302],\n                            [121.142797, 37.661464],\n                            [121.160043, 37.698882],\n                            [121.136022, 37.723501],\n                            [121.037471, 37.718767],\n                            [120.994356, 37.759468],\n                            [120.943233, 37.785486],\n                            [120.940769, 37.819533],\n                            [120.874863, 37.833241],\n                            [120.845298, 37.826623],\n                            [120.839139, 37.82426],\n                            [120.733197, 37.833714],\n                            [120.656821, 37.793054],\n                            [120.634031, 37.796364],\n                            [120.590915, 37.7642],\n                            [120.517619, 37.750005],\n                            [120.454793, 37.757576],\n                            [120.367945, 37.697935],\n                            [120.227511, 37.693673],\n                            [120.22012, 37.671886],\n                            [120.269395, 37.658622],\n                            [120.272475, 37.636824],\n                            [120.215192, 37.621183],\n                            [120.208417, 37.588469],\n                            [120.246605, 37.556689],\n                            [120.222584, 37.532963],\n                            [120.144359, 37.481691],\n                            [120.086461, 37.465067],\n                            [120.064903, 37.448915],\n                            [120.010085, 37.442263],\n                            [119.949723, 37.419927],\n                            [119.926933, 37.386649],\n                            [119.843781, 37.376662],\n                            [119.837006, 37.346695],\n                            [119.883201, 37.311004],\n                            [119.89244, 37.263866],\n                            [119.865339, 37.233854],\n                            [119.83023, 37.225754],\n                            [119.808057, 37.196203],\n                            [119.740303, 37.133727],\n                            [119.687332, 37.143746],\n                            [119.678709, 37.158056],\n                            [119.576463, 37.127524],\n                            [119.489616, 37.134681],\n                            [119.428022, 37.125616],\n                            [119.361501, 37.125616],\n                            [119.327624, 37.115595],\n                            [119.301138, 37.139452],\n                            [119.298675, 37.197156],\n                            [119.2069, 37.223371],\n                            [119.190885, 37.25958],\n                            [119.204436, 37.280058],\n                            [119.136683, 37.230995],\n                            [119.12806, 37.254816],\n                            [119.091103, 37.257674],\n                            [119.084328, 37.239572],\n                            [119.054147, 37.254816],\n                            [119.03998, 37.30434],\n                            [119.001176, 37.31862],\n                            [118.942662, 37.497361],\n                            [118.939582, 37.527268],\n                            [118.988857, 37.620709],\n                            [119.023966, 37.642037],\n                            [119.153313, 37.655305],\n                            [119.236465, 37.651988],\n                            [119.262334, 37.660517],\n                            [119.280197, 37.692726],\n                            [119.309146, 37.805349],\n                            [119.291899, 37.869627],\n                            [119.24016, 37.878131],\n                            [119.212443, 37.838913],\n                            [119.16132, 37.81906],\n                            [119.12806, 37.847892],\n                            [119.110813, 37.921577],\n                            [119.001792, 37.99613],\n                            [118.974075, 38.094162],\n                            [118.908169, 38.139362],\n                            [118.811467, 38.157717],\n                            [118.703677, 38.151129],\n                            [118.626069, 38.138421],\n                            [118.607591, 38.129006],\n                            [118.597736, 38.079088],\n                            [118.552156, 38.05553],\n                            [118.534294, 38.063541],\n                            [118.517048, 38.088509],\n                            [118.504729, 38.11394],\n                            [118.44991, 38.124299],\n                            [118.431432, 38.106406],\n                            [118.404331, 38.121003],\n                            [118.331034, 38.12524],\n                            [118.217085, 38.146893],\n                            [118.177665, 38.186417],\n                            [118.112376, 38.210403],\n                            [118.045238, 38.214165],\n                            [118.018753, 38.202409],\n                            [117.896797, 38.279495],\n                            [117.895565, 38.301572],\n                            [117.848754, 38.255062],\n                            [117.808718, 38.22827],\n                            [117.789007, 38.180772],\n                            [117.766834, 38.158658],\n                            [117.771145, 38.134655],\n                            [117.746508, 38.12524],\n                            [117.704624, 38.076262],\n                            [117.586979, 38.071551],\n                            [117.557414, 38.046105],\n                            [117.557414, 38.046105],\n                            [117.524154, 37.989527],\n                            [117.513067, 37.94329],\n                            [117.481038, 37.914967],\n                            [117.438538, 37.854035],\n                            [117.400966, 37.844584],\n                            [117.320278, 37.861596],\n                            [117.271618, 37.839858],\n                            [117.185387, 37.849783],\n                            [117.150278, 37.839385],\n                            [117.074518, 37.848837],\n                            [117.027091, 37.832296],\n                            [116.919301, 37.846002],\n                            [116.837997, 37.835132],\n                            [116.804736, 37.848837],\n                            [116.753613, 37.793054],\n                            [116.753613, 37.77035],\n                            [116.724664, 37.744327],\n                            [116.679085, 37.728708],\n                            [116.66307, 37.686096],\n                            [116.604556, 37.624975],\n                            [116.575607, 37.610754],\n                            [116.4826, 37.521573],\n                            [116.448108, 37.503059],\n                            [116.433941, 37.473142],\n                            [116.38097, 37.522522],\n                            [116.379738, 37.522047],\n                            [116.38097, 37.522522],\n                            [116.379738, 37.522047],\n                            [116.36742, 37.566177],\n                            [116.336007, 37.581355],\n                            [116.295355, 37.554316],\n                            [116.278724, 37.524895],\n                            [116.290427, 37.484065],\n                            [116.27626, 37.466967],\n                            [116.240536, 37.489764],\n                            [116.240536, 37.489764],\n                            [116.224522, 37.479791],\n                            [116.243, 37.447965],\n                            [116.226369, 37.428007],\n                            [116.2855, 37.404241],\n                            [116.236224, 37.361442],\n                            [116.193109, 37.365723],\n                            [116.169087, 37.384271],\n                            [116.106261, 37.368577],\n                            [116.085935, 37.373809],\n                            [116.024341, 37.360015],\n                            [115.975682, 37.337179],\n                            [115.969523, 37.239572],\n                            [115.909777, 37.20669],\n                            [115.91224, 37.177132],\n                            [115.879596, 37.150901],\n                            [115.888219, 37.112254],\n                            [115.85619, 37.060694],\n                            [115.776734, 36.992848],\n                            [115.79706, 36.968945],\n                            [115.75764, 36.902453],\n                            [115.71206, 36.883308],\n                            [115.683727, 36.808117],\n                            [115.524815, 36.763543],\n                            [115.479851, 36.760187],\n                            [115.451518, 36.702151],\n                            [115.420105, 36.686795],\n                            [115.365902, 36.621979],\n                            [115.355431, 36.627262],\n                            [115.33141, 36.550378],\n                            [115.272895, 36.497476],\n                            [115.291374, 36.460423],\n                            [115.317243, 36.454166],\n                            [115.297533, 36.413239],\n                            [115.340033, 36.398307],\n                            [115.368982, 36.342409],\n                            [115.366518, 36.30914],\n                            [115.423185, 36.32216],\n                            [115.417025, 36.292742],\n                            [115.462605, 36.276339],\n                            [115.466916, 36.258969],\n                            [115.466916, 36.258969],\n                            [115.474923, 36.248352],\n                            [115.483547, 36.148865],\n                            [115.484163, 36.125666],\n                            [115.449054, 36.047317],\n                            [115.447822, 36.01247],\n                            [115.362822, 35.971796],\n                            [115.353583, 35.938854],\n                            [115.364054, 35.894264],\n                            [115.335105, 35.796756],\n                            [115.363438, 35.779765],\n                            [115.407786, 35.80889],\n                            [115.460141, 35.867594],\n                            [115.487858, 35.880688],\n                            [115.495249, 35.896203],\n                            [115.505104, 35.899112],\n                            [115.513112, 35.890385],\n                            [115.583945, 35.921893],\n                            [115.648618, 35.922863],\n                            [115.699125, 35.966468],\n                            [115.774886, 35.974702],\n                            [115.779813, 35.993588],\n                            [115.817386, 36.012954],\n                            [115.859886, 36.003756],\n                            [115.89869, 36.026507],\n                            [115.989849, 36.045381],\n                            [116.057602, 36.104877],\n                            [116.099486, 36.112129],\n                            [116.063145, 36.028927],\n                            [116.048979, 35.970343],\n                            [115.984921, 35.974218],\n                            [115.911624, 35.960171],\n                            [115.907929, 35.92674],\n                            [115.873436, 35.918985],\n                            [115.882675, 35.879718],\n                            [115.859886, 35.857894],\n                            [115.81677, 35.844312],\n                            [115.773654, 35.854014],\n                            [115.73485, 35.833154],\n                            [115.696046, 35.788989],\n                            [115.693582, 35.754028],\n                            [115.622749, 35.739457],\n                            [115.52851, 35.733628],\n                            [115.48601, 35.710306],\n                            [115.383148, 35.568772],\n                            [115.34496, 35.55368],\n                            [115.356047, 35.490359],\n                            [115.307388, 35.480126],\n                            [115.237171, 35.423087],\n                            [115.172497, 35.426501],\n                            [115.126302, 35.41821],\n                            [115.117679, 35.400163],\n                            [115.091809, 35.416259],\n                            [115.073947, 35.374304],\n                            [115.04315, 35.376744],\n                            [114.957534, 35.261014],\n                            [114.929201, 35.244886],\n                            [114.932281, 35.198441],\n                            [114.861448, 35.182301],\n                            [114.841738, 35.15099],\n                            [114.883006, 35.098615],\n                            [114.835578, 35.076578],\n                            [114.818948, 35.051596],\n                            [114.852209, 35.041797],\n                            [114.824492, 35.012393],\n                            [114.880542, 35.00357],\n                            [114.923658, 34.968757],\n                            [114.950759, 34.989843],\n                            [115.008041, 34.988372],\n                            [115.028983, 34.9717],\n                            [115.075179, 35.000628],\n                            [115.12815, 35.00455],\n                            [115.157099, 34.957968],\n                            [115.219309, 34.96042],\n                            [115.205142, 34.914303],\n                            [115.251953, 34.906451],\n                            [115.239019, 34.87798],\n                            [115.256265, 34.845079],\n                            [115.317243, 34.859321],\n                            [115.42688, 34.805285],\n                            [115.449054, 34.74433],\n                            [115.433655, 34.725149],\n                            [115.461373, 34.637057],\n                            [115.515575, 34.582383],\n                            [115.553148, 34.568586],\n                            [115.622749, 34.574499],\n                            [115.685575, 34.556265],\n                            [115.697278, 34.594207],\n                            [115.787821, 34.580905],\n                            [115.827241, 34.558236],\n                            [115.838328, 34.5676],\n                            [115.984305, 34.589281],\n                            [115.991081, 34.615389],\n                            [116.037276, 34.593222],\n                            [116.101334, 34.60603],\n                            [116.134594, 34.559715],\n                            [116.156768, 34.5538],\n                            [116.196804, 34.575977],\n                            [116.247927, 34.551829],\n                            [116.286116, 34.608986],\n                            [116.32492, 34.601104],\n                            [116.334159, 34.620806],\n                            [116.374195, 34.640011]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.729502, 37.947065],\n                            [120.721495, 37.917328],\n                            [120.76461, 37.895134],\n                            [120.76461, 37.923937],\n                            [120.729502, 37.947065]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.692545, 37.983867],\n                            [120.732581, 37.961694],\n                            [120.724574, 37.987641],\n                            [120.692545, 37.983867]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.990044, 36.413239],\n                            [120.978341, 36.428649],\n                            [120.950624, 36.414684],\n                            [120.990044, 36.413239]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.750444, 38.150188],\n                            [120.7874, 38.158658],\n                            [120.742436, 38.199116],\n                            [120.750444, 38.150188]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.918595, 38.345236],\n                            [120.914899, 38.373393],\n                            [120.895189, 38.36307],\n                            [120.918595, 38.345236]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.159142, 35.765198],\n                            [120.169613, 35.740428],\n                            [120.193019, 35.756942],\n                            [120.172077, 35.785591],\n                            [120.159142, 35.765198]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.62664, 37.94565],\n                            [120.631567, 37.981037],\n                            [120.602002, 37.978678],\n                            [120.62664, 37.94565]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.802183, 38.284193],\n                            [120.848378, 38.305799],\n                            [120.816349, 38.318008],\n                            [120.802183, 38.284193]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.489571, 37.577086],\n                            [121.489571, 37.577561],\n                            [121.489571, 37.578509],\n                            [121.488955, 37.578035],\n                            [121.489571, 37.577086]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.485875, 37.578509],\n                            [121.487723, 37.578035],\n                            [121.487723, 37.578509],\n                            [121.485875, 37.578509]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.487723, 37.578509],\n                            [121.487723, 37.577561],\n                            [121.488955, 37.578035],\n                            [121.488955, 37.578509],\n                            [121.488339, 37.578509],\n                            [121.487723, 37.578509]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.495249, 35.896203],\n                            [115.487858, 35.880688],\n                            [115.513112, 35.890385],\n                            [115.505104, 35.899112],\n                            [115.495249, 35.896203]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 410000,\n                \"name\": \"Henan\",\n                \"center\": [113.665412, 34.757975],\n                \"centroid\": [113.619717, 33.902648],\n                \"childrenNum\": 18,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 15,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [112.716747, 32.357612],\n                            [112.735841, 32.356095],\n                            [112.776493, 32.358623],\n                            [112.860877, 32.396024],\n                            [112.888594, 32.37682],\n                            [112.912, 32.390971],\n                            [112.992072, 32.378336],\n                            [113.000695, 32.41674],\n                            [113.025949, 32.425328],\n                            [113.078919, 32.394508],\n                            [113.107869, 32.398551],\n                            [113.118956, 32.375809],\n                            [113.155912, 32.380863],\n                            [113.158992, 32.410677],\n                            [113.211962, 32.431895],\n                            [113.2366, 32.407141],\n                            [113.333918, 32.336377],\n                            [113.317904, 32.327275],\n                            [113.353628, 32.294904],\n                            [113.376418, 32.298445],\n                            [113.428773, 32.270618],\n                            [113.511925, 32.316654],\n                            [113.624642, 32.36115],\n                            [113.650511, 32.412698],\n                            [113.700402, 32.420782],\n                            [113.735511, 32.410677],\n                            [113.76754, 32.370249],\n                            [113.753989, 32.328286],\n                            [113.768772, 32.30148],\n                            [113.768156, 32.284279],\n                            [113.758301, 32.27669],\n                            [113.749061, 32.272642],\n                            [113.73859, 32.255942],\n                            [113.752757, 32.215951],\n                            [113.782322, 32.184553],\n                            [113.750293, 32.11615],\n                            [113.722576, 32.12426],\n                            [113.728735, 32.083197],\n                            [113.791561, 32.036028],\n                            [113.757685, 31.98985],\n                            [113.817431, 31.964467],\n                            [113.805728, 31.929428],\n                            [113.832213, 31.918761],\n                            [113.830981, 31.87913],\n                            [113.854387, 31.843042],\n                            [113.893807, 31.847109],\n                            [113.914749, 31.877098],\n                            [113.957865, 31.852701],\n                            [113.952321, 31.793714],\n                            [113.988662, 31.749959],\n                            [114.017611, 31.770822],\n                            [114.086596, 31.782014],\n                            [114.121705, 31.809482],\n                            [114.134024, 31.843042],\n                            [114.191922, 31.852192],\n                            [114.235654, 31.833382],\n                            [114.292936, 31.752503],\n                            [114.350218, 31.755557],\n                            [114.403189, 31.746906],\n                            [114.443841, 31.728074],\n                            [114.530688, 31.742834],\n                            [114.549783, 31.766751],\n                            [114.586123, 31.762172],\n                            [114.57134, 31.660858],\n                            [114.547935, 31.623665],\n                            [114.560869, 31.560963],\n                            [114.572572, 31.553824],\n                            [114.61692, 31.585437],\n                            [114.641558, 31.582378],\n                            [114.696376, 31.525771],\n                            [114.778912, 31.520669],\n                            [114.789383, 31.480358],\n                            [114.830035, 31.45892],\n                            [114.870071, 31.479337],\n                            [114.884238, 31.469129],\n                            [114.962462, 31.494648],\n                            [114.995107, 31.471171],\n                            [115.022824, 31.527811],\n                            [115.096121, 31.508425],\n                            [115.114599, 31.530362],\n                            [115.106592, 31.567592],\n                            [115.12507, 31.599201],\n                            [115.16449, 31.604808],\n                            [115.212533, 31.555354],\n                            [115.235939, 31.555354],\n                            [115.218077, 31.515057],\n                            [115.211301, 31.442072],\n                            [115.252569, 31.421646],\n                            [115.250722, 31.392021],\n                            [115.301229, 31.383846],\n                            [115.338801, 31.40428],\n                            [115.373909, 31.405813],\n                            [115.389924, 31.450241],\n                            [115.371446, 31.495668],\n                            [115.415793, 31.525771],\n                            [115.439815, 31.588496],\n                            [115.485394, 31.608885],\n                            [115.476771, 31.643028],\n                            [115.495249, 31.673083],\n                            [115.534054, 31.698545],\n                            [115.553764, 31.69549],\n                            [115.676336, 31.778453],\n                            [115.731154, 31.76726],\n                            [115.767495, 31.78761],\n                            [115.808147, 31.770313],\n                            [115.808147, 31.770313],\n                            [115.851878, 31.786593],\n                            [115.886371, 31.776418],\n                            [115.914704, 31.814567],\n                            [115.893762, 31.832365],\n                            [115.894994, 31.8649],\n                            [115.920248, 31.920285],\n                            [115.909161, 31.94314],\n                            [115.928871, 32.003046],\n                            [115.922095, 32.049725],\n                            [115.941805, 32.166318],\n                            [115.912856, 32.227596],\n                            [115.899306, 32.390971],\n                            [115.865429, 32.458662],\n                            [115.883291, 32.487946],\n                            [115.845719, 32.501575],\n                            [115.8759, 32.542448],\n                            [115.910393, 32.567165],\n                            [115.891298, 32.576243],\n                            [115.861117, 32.537403],\n                            [115.789052, 32.468761],\n                            [115.771806, 32.505108],\n                            [115.742241, 32.476335],\n                            [115.704669, 32.495013],\n                            [115.667712, 32.409667],\n                            [115.657857, 32.428864],\n                            [115.626445, 32.40512],\n                            [115.604271, 32.425833],\n                            [115.57101, 32.419266],\n                            [115.522967, 32.441997],\n                            [115.509416, 32.466741],\n                            [115.5088, 32.468761],\n                            [115.497713, 32.492489],\n                            [115.409018, 32.549007],\n                            [115.411482, 32.575235],\n                            [115.304924, 32.553042],\n                            [115.30554, 32.583303],\n                            [115.267352, 32.578261],\n                            [115.24333, 32.593388],\n                            [115.20083, 32.591876],\n                            [115.182968, 32.666973],\n                            [115.179273, 32.726402],\n                            [115.189744, 32.770695],\n                            [115.211301, 32.785791],\n                            [115.189744, 32.812452],\n                            [115.197135, 32.856201],\n                            [115.155867, 32.864747],\n                            [115.139237, 32.897917],\n                            [115.029599, 32.906962],\n                            [115.035143, 32.932582],\n                            [115.009273, 32.940117],\n                            [114.943368, 32.935094],\n                            [114.916266, 32.971251],\n                            [114.883006, 32.990328],\n                            [114.891629, 33.020441],\n                            [114.925506, 33.016928],\n                            [114.913187, 33.083143],\n                            [114.897172, 33.086653],\n                            [114.902716, 33.129764],\n                            [114.932897, 33.153817],\n                            [114.966158, 33.147304],\n                            [114.990795, 33.102195],\n                            [115.041302, 33.086653],\n                            [115.168186, 33.088658],\n                            [115.194671, 33.120743],\n                            [115.245178, 33.135778],\n                            [115.289526, 33.131769],\n                            [115.303692, 33.149809],\n                            [115.300613, 33.204407],\n                            [115.340033, 33.260973],\n                            [115.335105, 33.297997],\n                            [115.361591, 33.298497],\n                            [115.365286, 33.336005],\n                            [115.341881, 33.370997],\n                            [115.313547, 33.376994],\n                            [115.328946, 33.403477],\n                            [115.315395, 33.431451],\n                            [115.324634, 33.457418],\n                            [115.345576, 33.449928],\n                            [115.345576, 33.502842],\n                            [115.366518, 33.5233],\n                            [115.394851, 33.506335],\n                            [115.422569, 33.557219],\n                            [115.463837, 33.567193],\n                            [115.511264, 33.55323],\n                            [115.564851, 33.576169],\n                            [115.639995, 33.585143],\n                            [115.601191, 33.658898],\n                            [115.601807, 33.718653],\n                            [115.563003, 33.772895],\n                            [115.576553, 33.787817],\n                            [115.614126, 33.775879],\n                            [115.631988, 33.869846],\n                            [115.547604, 33.874815],\n                            [115.577785, 33.950307],\n                            [115.579017, 33.974133],\n                            [115.60735, 34.030196],\n                            [115.642459, 34.03218],\n                            [115.658473, 34.061437],\n                            [115.705901, 34.059949],\n                            [115.736082, 34.076805],\n                            [115.768726, 34.061932],\n                            [115.809378, 34.062428],\n                            [115.846335, 34.028708],\n                            [115.85003, 34.004898],\n                            [115.877132, 34.002913],\n                            [115.876516, 34.028708],\n                            [115.904233, 34.009859],\n                            [115.95782, 34.007875],\n                            [116.00032, 33.965199],\n                            [115.982457, 33.917039],\n                            [116.05945, 33.860902],\n                            [116.055754, 33.804727],\n                            [116.074232, 33.781351],\n                            [116.100102, 33.782843],\n                            [116.132747, 33.751501],\n                            [116.155536, 33.709693],\n                            [116.230065, 33.735078],\n                            [116.263326, 33.730101],\n                            [116.316912, 33.771402],\n                            [116.393905, 33.782843],\n                            [116.408071, 33.805721],\n                            [116.437021, 33.801246],\n                            [116.437637, 33.846489],\n                            [116.486296, 33.869846],\n                            [116.558361, 33.881274],\n                            [116.566984, 33.9081],\n                            [116.631042, 33.887733],\n                            [116.64336, 33.896675],\n                            [116.641512, 33.978103],\n                            [116.599629, 34.014324],\n                            [116.599629, 34.014324],\n                            [116.576223, 34.068873],\n                            [116.576223, 34.068873],\n                            [116.52818, 34.122892],\n                            [116.536187, 34.151127],\n                            [116.565752, 34.16945],\n                            [116.542962, 34.203608],\n                            [116.545426, 34.241711],\n                            [116.582382, 34.266444],\n                            [116.562056, 34.285731],\n                            [116.516477, 34.296114],\n                            [116.456731, 34.268917],\n                            [116.409303, 34.273863],\n                            [116.409303, 34.273863],\n                            [116.372347, 34.26595],\n                            [116.357564, 34.319843],\n                            [116.301514, 34.342082],\n                            [116.255934, 34.376665],\n                            [116.213435, 34.382098],\n                            [116.215898, 34.403333],\n                            [116.178942, 34.430487],\n                            [116.162312, 34.459605],\n                            [116.178326, 34.496112],\n                            [116.204196, 34.508442],\n                            [116.191261, 34.535561],\n                            [116.196804, 34.575977],\n                            [116.156768, 34.5538],\n                            [116.134594, 34.559715],\n                            [116.101334, 34.60603],\n                            [116.037276, 34.593222],\n                            [115.991081, 34.615389],\n                            [115.984305, 34.589281],\n                            [115.838328, 34.5676],\n                            [115.827241, 34.558236],\n                            [115.787821, 34.580905],\n                            [115.697278, 34.594207],\n                            [115.685575, 34.556265],\n                            [115.622749, 34.574499],\n                            [115.553148, 34.568586],\n                            [115.515575, 34.582383],\n                            [115.461373, 34.637057],\n                            [115.433655, 34.725149],\n                            [115.449054, 34.74433],\n                            [115.42688, 34.805285],\n                            [115.317243, 34.859321],\n                            [115.256265, 34.845079],\n                            [115.239019, 34.87798],\n                            [115.251953, 34.906451],\n                            [115.205142, 34.914303],\n                            [115.219309, 34.96042],\n                            [115.157099, 34.957968],\n                            [115.12815, 35.00455],\n                            [115.075179, 35.000628],\n                            [115.028983, 34.9717],\n                            [115.008041, 34.988372],\n                            [114.950759, 34.989843],\n                            [114.923658, 34.968757],\n                            [114.880542, 35.00357],\n                            [114.824492, 35.012393],\n                            [114.852209, 35.041797],\n                            [114.818948, 35.051596],\n                            [114.835578, 35.076578],\n                            [114.883006, 35.098615],\n                            [114.841738, 35.15099],\n                            [114.861448, 35.182301],\n                            [114.932281, 35.198441],\n                            [114.929201, 35.244886],\n                            [114.957534, 35.261014],\n                            [115.04315, 35.376744],\n                            [115.073947, 35.374304],\n                            [115.091809, 35.416259],\n                            [115.117679, 35.400163],\n                            [115.126302, 35.41821],\n                            [115.172497, 35.426501],\n                            [115.237171, 35.423087],\n                            [115.307388, 35.480126],\n                            [115.356047, 35.490359],\n                            [115.34496, 35.55368],\n                            [115.383148, 35.568772],\n                            [115.48601, 35.710306],\n                            [115.52851, 35.733628],\n                            [115.622749, 35.739457],\n                            [115.693582, 35.754028],\n                            [115.696046, 35.788989],\n                            [115.73485, 35.833154],\n                            [115.773654, 35.854014],\n                            [115.81677, 35.844312],\n                            [115.859886, 35.857894],\n                            [115.882675, 35.879718],\n                            [115.873436, 35.918985],\n                            [115.907929, 35.92674],\n                            [115.911624, 35.960171],\n                            [115.984921, 35.974218],\n                            [116.048979, 35.970343],\n                            [116.063145, 36.028927],\n                            [116.099486, 36.112129],\n                            [116.057602, 36.104877],\n                            [115.989849, 36.045381],\n                            [115.89869, 36.026507],\n                            [115.859886, 36.003756],\n                            [115.817386, 36.012954],\n                            [115.779813, 35.993588],\n                            [115.774886, 35.974702],\n                            [115.699125, 35.966468],\n                            [115.648618, 35.922863],\n                            [115.583945, 35.921893],\n                            [115.513112, 35.890385],\n                            [115.487858, 35.880688],\n                            [115.460141, 35.867594],\n                            [115.407786, 35.80889],\n                            [115.363438, 35.779765],\n                            [115.335105, 35.796756],\n                            [115.364054, 35.894264],\n                            [115.353583, 35.938854],\n                            [115.362822, 35.971796],\n                            [115.447822, 36.01247],\n                            [115.449054, 36.047317],\n                            [115.484163, 36.125666],\n                            [115.483547, 36.148865],\n                            [115.465068, 36.170125],\n                            [115.450902, 36.152248],\n                            [115.376989, 36.128083],\n                            [115.365902, 36.099074],\n                            [115.312931, 36.088436],\n                            [115.30246, 36.127599],\n                            [115.279055, 36.13775],\n                            [115.242098, 36.19138],\n                            [115.202678, 36.208765],\n                            [115.202678, 36.208765],\n                            [115.202678, 36.209248],\n                            [115.202678, 36.209248],\n                            [115.201446, 36.210214],\n                            [115.201446, 36.210214],\n                            [115.1842, 36.193312],\n                            [115.12507, 36.209731],\n                            [115.104744, 36.172058],\n                            [115.06286, 36.178338],\n                            [115.048693, 36.161912],\n                            [115.04623, 36.112613],\n                            [114.998186, 36.069572],\n                            [114.914419, 36.052155],\n                            [114.926737, 36.089403],\n                            [114.912571, 36.140649],\n                            [114.858368, 36.144516],\n                            [114.857752, 36.127599],\n                            [114.771521, 36.124699],\n                            [114.734564, 36.15563],\n                            [114.720398, 36.140166],\n                            [114.640326, 36.137266],\n                            [114.588587, 36.118414],\n                            [114.586739, 36.141133],\n                            [114.533152, 36.171575],\n                            [114.480181, 36.177855],\n                            [114.466015, 36.197658],\n                            [114.417356, 36.205868],\n                            [114.408117, 36.224699],\n                            [114.356378, 36.230492],\n                            [114.345291, 36.255591],\n                            [114.299095, 36.245938],\n                            [114.257827, 36.263794],\n                            [114.241197, 36.251247],\n                            [114.2104, 36.272962],\n                            [114.203009, 36.245456],\n                            [114.170364, 36.245938],\n                            [114.170364, 36.245938],\n                            [114.175907, 36.264759],\n                            [114.129096, 36.280199],\n                            [114.080437, 36.269585],\n                            [114.04348, 36.303353],\n                            [114.056415, 36.329392],\n                            [114.002828, 36.334214],\n                            [113.981887, 36.31782],\n                            [113.962792, 36.353977],\n                            [113.911054, 36.314927],\n                            [113.882104, 36.353977],\n                            [113.84946, 36.347711],\n                            [113.856851, 36.329392],\n                            [113.813119, 36.332285],\n                            [113.755221, 36.366026],\n                            [113.731199, 36.363135],\n                            [113.736127, 36.324571],\n                            [113.712105, 36.303353],\n                            [113.716417, 36.262347],\n                            [113.681924, 36.216491],\n                            [113.697939, 36.181719],\n                            [113.651127, 36.174473],\n                            [113.705946, 36.148865],\n                            [113.712721, 36.129533],\n                            [113.655439, 36.125182],\n                            [113.671453, 36.115514],\n                            [113.68562, 36.056026],\n                            [113.660366, 36.034735],\n                            [113.694859, 36.026991],\n                            [113.678844, 35.985841],\n                            [113.648663, 35.994073],\n                            [113.654207, 35.931586],\n                            [113.637576, 35.870019],\n                            [113.660982, 35.837035],\n                            [113.582758, 35.818111],\n                            [113.604932, 35.797727],\n                            [113.587685, 35.736542],\n                            [113.592613, 35.691838],\n                            [113.622794, 35.674825],\n                            [113.625258, 35.632518],\n                            [113.578446, 35.633491],\n                            [113.547649, 35.656835],\n                            [113.55812, 35.621816],\n                            [113.513773, 35.57364],\n                            [113.49899, 35.532254],\n                            [113.439244, 35.507412],\n                            [113.391817, 35.506925],\n                            [113.348085, 35.468429],\n                            [113.31236, 35.481101],\n                            [113.304353, 35.426989],\n                            [113.243375, 35.449418],\n                            [113.189789, 35.44893],\n                            [113.185477, 35.409431],\n                            [113.165151, 35.412845],\n                            [113.149137, 35.350878],\n                            [113.126347, 35.332327],\n                            [113.067217, 35.353806],\n                            [112.996384, 35.362104],\n                            [112.985913, 35.33965],\n                            [112.992072, 35.29619],\n                            [112.936022, 35.284466],\n                            [112.934174, 35.262968],\n                            [112.884283, 35.243909],\n                            [112.822073, 35.258082],\n                            [112.772798, 35.207732],\n                            [112.720443, 35.206265],\n                            [112.628052, 35.263457],\n                            [112.637291, 35.225822],\n                            [112.513487, 35.218489],\n                            [112.390915, 35.239021],\n                            [112.36751, 35.219956],\n                            [112.288053, 35.219956],\n                            [112.304684, 35.251728],\n                            [112.242474, 35.234622],\n                            [112.21722, 35.253195],\n                            [112.13838, 35.271275],\n                            [112.058924, 35.280069],\n                            [112.078634, 35.219467],\n                            [112.03983, 35.194039],\n                            [112.066315, 35.153437],\n                            [112.05646, 35.098615],\n                            [112.062004, 35.056005],\n                            [112.039214, 35.045717],\n                            [112.018888, 35.068742],\n                            [111.97762, 35.067272],\n                            [111.933272, 35.083435],\n                            [111.810084, 35.062374],\n                            [111.807005, 35.032977],\n                            [111.740483, 35.00455],\n                            [111.664107, 34.984449],\n                            [111.681969, 34.9511],\n                            [111.646861, 34.938836],\n                            [111.617911, 34.894671],\n                            [111.592042, 34.881416],\n                            [111.570484, 34.843114],\n                            [111.543999, 34.853428],\n                            [111.502731, 34.829851],\n                            [111.439289, 34.838202],\n                            [111.389398, 34.815113],\n                            [111.345666, 34.831816],\n                            [111.29208, 34.806759],\n                            [111.255123, 34.819535],\n                            [111.232949, 34.789559],\n                            [111.148566, 34.807742],\n                            [111.118385, 34.756623],\n                            [111.035233, 34.740887],\n                            [110.976103, 34.706456],\n                            [110.920052, 34.730068],\n                            [110.903422, 34.669056],\n                            [110.883712, 34.64395],\n                            [110.824582, 34.615881],\n                            [110.791937, 34.649858],\n                            [110.749437, 34.65232],\n                            [110.710017, 34.605045],\n                            [110.610851, 34.607508],\n                            [110.533242, 34.583368],\n                            [110.488279, 34.610956],\n                            [110.424837, 34.588295],\n                            [110.379257, 34.600612],\n                            [110.366939, 34.566614],\n                            [110.404511, 34.557743],\n                            [110.372482, 34.544435],\n                            [110.360779, 34.516825],\n                            [110.403279, 34.433448],\n                            [110.403279, 34.433448],\n                            [110.473496, 34.393457],\n                            [110.503677, 34.33714],\n                            [110.451938, 34.292653],\n                            [110.428533, 34.288203],\n                            [110.43962, 34.243196],\n                            [110.507989, 34.217466],\n                            [110.55172, 34.213012],\n                            [110.55788, 34.193214],\n                            [110.621938, 34.177372],\n                            [110.642264, 34.161032],\n                            [110.61393, 34.113478],\n                            [110.591757, 34.101586],\n                            [110.587445, 34.023252],\n                            [110.620706, 34.035652],\n                            [110.671213, 33.966192],\n                            [110.665669, 33.937895],\n                            [110.627481, 33.925482],\n                            [110.628713, 33.910086],\n                            [110.587445, 33.887733],\n                            [110.612083, 33.852453],\n                            [110.66259, 33.85295],\n                            [110.712481, 33.833564],\n                            [110.74143, 33.798759],\n                            [110.782082, 33.796272],\n                            [110.81719, 33.751003],\n                            [110.831973, 33.713675],\n                            [110.823966, 33.685793],\n                            [110.878784, 33.634486],\n                            [110.966864, 33.609071],\n                            [111.00382, 33.578662],\n                            [111.002588, 33.535772],\n                            [111.02661, 33.478386],\n                            [111.02661, 33.467903],\n                            [110.996429, 33.435946],\n                            [111.025994, 33.375495],\n                            [111.025994, 33.330504],\n                            [110.984726, 33.255469],\n                            [111.046936, 33.202905],\n                            [111.045704, 33.169849],\n                            [111.08882, 33.181871],\n                            [111.12824, 33.15532],\n                            [111.146102, 33.12375],\n                            [111.179363, 33.115229],\n                            [111.192913, 33.071609],\n                            [111.152877, 33.039507],\n                            [111.221862, 33.042517],\n                            [111.258819, 33.006389],\n                            [111.273601, 32.971753],\n                            [111.242804, 32.930573],\n                            [111.255123, 32.883846],\n                            [111.276065, 32.903445],\n                            [111.293311, 32.859217],\n                            [111.380159, 32.829049],\n                            [111.41342, 32.757108],\n                            [111.475629, 32.760127],\n                            [111.458383, 32.726402],\n                            [111.513202, 32.674026],\n                            [111.530448, 32.628172],\n                            [111.577875, 32.593388],\n                            [111.640701, 32.634724],\n                            [111.646245, 32.605993],\n                            [111.713382, 32.606497],\n                            [111.808853, 32.536899],\n                            [111.858128, 32.528826],\n                            [111.890157, 32.503089],\n                            [111.948671, 32.51722],\n                            [111.975772, 32.471791],\n                            [112.014576, 32.450077],\n                            [112.063851, 32.474315],\n                            [112.081098, 32.425833],\n                            [112.155626, 32.377326],\n                            [112.150083, 32.411688],\n                            [112.172873, 32.385412],\n                            [112.206133, 32.392992],\n                            [112.328089, 32.321712],\n                            [112.360118, 32.3657],\n                            [112.390915, 32.37126],\n                            [112.448814, 32.34295],\n                            [112.477147, 32.380863],\n                            [112.530733, 32.37682],\n                            [112.545516, 32.404109],\n                            [112.589248, 32.381369],\n                            [112.612037, 32.386928],\n                            [112.645298, 32.368227],\n                            [112.716747, 32.357612]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.768156, 32.284279],\n                            [113.768772, 32.30148],\n                            [113.749061, 32.272642],\n                            [113.758301, 32.27669],\n                            [113.768156, 32.284279]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 420000,\n                \"name\": \"Hubei\",\n                \"center\": [114.298572, 30.584355],\n                \"centroid\": [112.271301, 30.987527],\n                \"childrenNum\": 17,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 16,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [111.045704, 33.169849],\n                            [111.034001, 33.177864],\n                            [111.035849, 33.187881],\n                            [111.046936, 33.202905],\n                            [110.984726, 33.255469],\n                            [110.960704, 33.253967],\n                            [110.9219, 33.203907],\n                            [110.865234, 33.213921],\n                            [110.828893, 33.201403],\n                            [110.824582, 33.158327],\n                            [110.753133, 33.15031],\n                            [110.702626, 33.097182],\n                            [110.650887, 33.157324],\n                            [110.623785, 33.143796],\n                            [110.59422, 33.168346],\n                            [110.57759, 33.250464],\n                            [110.54125, 33.255469],\n                            [110.471032, 33.171352],\n                            [110.398352, 33.176862],\n                            [110.398352, 33.176862],\n                            [110.372482, 33.186379],\n                            [110.33799, 33.160331],\n                            [110.285635, 33.171352],\n                            [110.218497, 33.163336],\n                            [110.164911, 33.209415],\n                            [110.031252, 33.191888],\n                            [109.999223, 33.212419],\n                            [109.973353, 33.203907],\n                            [109.916687, 33.229942],\n                            [109.852013, 33.247961],\n                            [109.813209, 33.236449],\n                            [109.732521, 33.231443],\n                            [109.693101, 33.254468],\n                            [109.649985, 33.251465],\n                            [109.619804, 33.275484],\n                            [109.60687, 33.235949],\n                            [109.514479, 33.237951],\n                            [109.498464, 33.207412],\n                            [109.438718, 33.152314],\n                            [109.468283, 33.140288],\n                            [109.522486, 33.138785],\n                            [109.576073, 33.110216],\n                            [109.688174, 33.116733],\n                            [109.704188, 33.101694],\n                            [109.794731, 33.067095],\n                            [109.785492, 32.987316],\n                            [109.76455, 32.909474],\n                            [109.789804, 32.882339],\n                            [109.847702, 32.893395],\n                            [109.856941, 32.910479],\n                            [109.907448, 32.903947],\n                            [109.927158, 32.887364],\n                            [109.988752, 32.886359],\n                            [110.051578, 32.851676],\n                            [110.105164, 32.832569],\n                            [110.142121, 32.802895],\n                            [110.127338, 32.77774],\n                            [110.159367, 32.767173],\n                            [110.156903, 32.683093],\n                            [110.206179, 32.633212],\n                            [110.153824, 32.593388],\n                            [110.124259, 32.616579],\n                            [110.090382, 32.617083],\n                            [110.084223, 32.580782],\n                            [110.017701, 32.546989],\n                            [109.97089, 32.577756],\n                            [109.910528, 32.592884],\n                            [109.816905, 32.577252],\n                            [109.746072, 32.594901],\n                            [109.726978, 32.608513],\n                            [109.631507, 32.599943],\n                            [109.619804, 32.56767],\n                            [109.637051, 32.540935],\n                            [109.575457, 32.506622],\n                            [109.526797, 32.43341],\n                            [109.529877, 32.405625],\n                            [109.502776, 32.38895],\n                            [109.513247, 32.342444],\n                            [109.495385, 32.300468],\n                            [109.528645, 32.270112],\n                            [109.550203, 32.225065],\n                            [109.592703, 32.219495],\n                            [109.604406, 32.199241],\n                            [109.58716, 32.161251],\n                            [109.621652, 32.106519],\n                            [109.590855, 32.047696],\n                            [109.590855, 32.012688],\n                            [109.631507, 31.962436],\n                            [109.62042, 31.928412],\n                            [109.584696, 31.900472],\n                            [109.60379, 31.885737],\n                            [109.633971, 31.824738],\n                            [109.633971, 31.804396],\n                            [109.592087, 31.789136],\n                            [109.585928, 31.726546],\n                            [109.622268, 31.711783],\n                            [109.683246, 31.719929],\n                            [109.731289, 31.700582],\n                            [109.737449, 31.628761],\n                            [109.76455, 31.602769],\n                            [109.745456, 31.598182],\n                            [109.727594, 31.548214],\n                            [109.837847, 31.555354],\n                            [109.894513, 31.519139],\n                            [109.969658, 31.508935],\n                            [109.94502, 31.47066],\n                            [109.98752, 31.474744],\n                            [110.036795, 31.436966],\n                            [110.054042, 31.410921],\n                            [110.118715, 31.409899],\n                            [110.161831, 31.314338],\n                            [110.155671, 31.279564],\n                            [110.180309, 31.179774],\n                            [110.200019, 31.158779],\n                            [110.180309, 31.121899],\n                            [110.147048, 31.116776],\n                            [110.119947, 31.088592],\n                            [110.120563, 31.0322],\n                            [110.140273, 31.030661],\n                            [110.140889, 30.987062],\n                            [110.172918, 30.978853],\n                            [110.153824, 30.953708],\n                            [110.151976, 30.911613],\n                            [110.082375, 30.799614],\n                            [110.048498, 30.800642],\n                            [110.019549, 30.829425],\n                            [110.008462, 30.883369],\n                            [109.943788, 30.878746],\n                            [109.894513, 30.899803],\n                            [109.828608, 30.864364],\n                            [109.780564, 30.848437],\n                            [109.701724, 30.783677],\n                            [109.656761, 30.760538],\n                            [109.661072, 30.738936],\n                            [109.625348, 30.702923],\n                            [109.590855, 30.69366],\n                            [109.574225, 30.646818],\n                            [109.543428, 30.63961],\n                            [109.535421, 30.664837],\n                            [109.435638, 30.595832],\n                            [109.418392, 30.559766],\n                            [109.35495, 30.487076],\n                            [109.337088, 30.521623],\n                            [109.36111, 30.551004],\n                            [109.314298, 30.599953],\n                            [109.299516, 30.630341],\n                            [109.245313, 30.580892],\n                            [109.191726, 30.545851],\n                            [109.191726, 30.545851],\n                            [109.143683, 30.521108],\n                            [109.103647, 30.565949],\n                            [109.09256, 30.578831],\n                            [109.106111, 30.61077],\n                            [109.111654, 30.646303],\n                            [109.071002, 30.640125],\n                            [109.042669, 30.655571],\n                            [109.006329, 30.626736],\n                            [108.971836, 30.627766],\n                            [108.893612, 30.565434],\n                            [108.838793, 30.503062],\n                            [108.808612, 30.491202],\n                            [108.789518, 30.513374],\n                            [108.743939, 30.494812],\n                            [108.698975, 30.54482],\n                            [108.688504, 30.58759],\n                            [108.642925, 30.578831],\n                            [108.6497, 30.53915],\n                            [108.56778, 30.468508],\n                            [108.556077, 30.487592],\n                            [108.512961, 30.501515],\n                            [108.472925, 30.487076],\n                            [108.42673, 30.492233],\n                            [108.411331, 30.438586],\n                            [108.430425, 30.416397],\n                            [108.402092, 30.376649],\n                            [108.431041, 30.354446],\n                            [108.460606, 30.35961],\n                            [108.501258, 30.314673],\n                            [108.524048, 30.309506],\n                            [108.54499, 30.269716],\n                            [108.581947, 30.255759],\n                            [108.551766, 30.1637],\n                            [108.56778, 30.157491],\n                            [108.546222, 30.104178],\n                            [108.513577, 30.057571],\n                            [108.532055, 30.051873],\n                            [108.536367, 29.983472],\n                            [108.517889, 29.9394],\n                            [108.516041, 29.885451],\n                            [108.467998, 29.864175],\n                            [108.433505, 29.880262],\n                            [108.371295, 29.841337],\n                            [108.424266, 29.815897],\n                            [108.422418, 29.772791],\n                            [108.442744, 29.778505],\n                            [108.437201, 29.741098],\n                            [108.460606, 29.741098],\n                            [108.504338, 29.707836],\n                            [108.504954, 29.728626],\n                            [108.548686, 29.749412],\n                            [108.52528, 29.770713],\n                            [108.556077, 29.818493],\n                            [108.601041, 29.863656],\n                            [108.658939, 29.854833],\n                            [108.680497, 29.800319],\n                            [108.676801, 29.749412],\n                            [108.690968, 29.689642],\n                            [108.752562, 29.649082],\n                            [108.786438, 29.691721],\n                            [108.797525, 29.660003],\n                            [108.781511, 29.635558],\n                            [108.844337, 29.658443],\n                            [108.888068, 29.628795],\n                            [108.870206, 29.596537],\n                            [108.901003, 29.604863],\n                            [108.913322, 29.574679],\n                            [108.878213, 29.539279],\n                            [108.888684, 29.502305],\n                            [108.866511, 29.470527],\n                            [108.884373, 29.440824],\n                            [108.927488, 29.435612],\n                            [108.934264, 29.399643],\n                            [108.919481, 29.3261],\n                            [108.983539, 29.332883],\n                            [108.999553, 29.36366],\n                            [109.034662, 29.360531],\n                            [109.060531, 29.403292],\n                            [109.11227, 29.361053],\n                            [109.106727, 29.288526],\n                            [109.141835, 29.270256],\n                            [109.110422, 29.21647],\n                            [109.139372, 29.168927],\n                            [109.162777, 29.180946],\n                            [109.215748, 29.145409],\n                            [109.232378, 29.119271],\n                            [109.274262, 29.121885],\n                            [109.261328, 29.161089],\n                            [109.275494, 29.202366],\n                            [109.257632, 29.222738],\n                            [109.312451, 29.25146],\n                            [109.352487, 29.284872],\n                            [109.343863, 29.369398],\n                            [109.391291, 29.372005],\n                            [109.368501, 29.413719],\n                            [109.418392, 29.453332],\n                            [109.415928, 29.497617],\n                            [109.436254, 29.488761],\n                            [109.433791, 29.530948],\n                            [109.458428, 29.513242],\n                            [109.467051, 29.560104],\n                            [109.488609, 29.553336],\n                            [109.516326, 29.626194],\n                            [109.558826, 29.606944],\n                            [109.578536, 29.629836],\n                            [109.651833, 29.625674],\n                            [109.664768, 29.599659],\n                            [109.717739, 29.615269],\n                            [109.701108, 29.636078],\n                            [109.714659, 29.673524],\n                            [109.760238, 29.689122],\n                            [109.755311, 29.733304],\n                            [109.779333, 29.757725],\n                            [109.869876, 29.774869],\n                            [109.908064, 29.763959],\n                            [109.941325, 29.774349],\n                            [110.02386, 29.769674],\n                            [110.113788, 29.789932],\n                            [110.160599, 29.753569],\n                            [110.219729, 29.746814],\n                            [110.289946, 29.6964],\n                            [110.302265, 29.661563],\n                            [110.339221, 29.668324],\n                            [110.372482, 29.633477],\n                            [110.447011, 29.664684],\n                            [110.467337, 29.713034],\n                            [110.507373, 29.692241],\n                            [110.562807, 29.712515],\n                            [110.642879, 29.775907],\n                            [110.60038, 29.839779],\n                            [110.549873, 29.848085],\n                            [110.538786, 29.895828],\n                            [110.49875, 29.91243],\n                            [110.517228, 29.961179],\n                            [110.557264, 29.988137],\n                            [110.491358, 30.019751],\n                            [110.497518, 30.055499],\n                            [110.531394, 30.061197],\n                            [110.600996, 30.054463],\n                            [110.650887, 30.07777],\n                            [110.712481, 30.033223],\n                            [110.756212, 30.054463],\n                            [110.746973, 30.112979],\n                            [110.851067, 30.126439],\n                            [110.924364, 30.111426],\n                            [110.929907, 30.063268],\n                            [111.031537, 30.048765],\n                            [111.242188, 30.040476],\n                            [111.266826, 30.01146],\n                            [111.3315, 29.970512],\n                            [111.342587, 29.944586],\n                            [111.382623, 29.95029],\n                            [111.394325, 29.912948],\n                            [111.436825, 29.930065],\n                            [111.475629, 29.918654],\n                            [111.527368, 29.925916],\n                            [111.553854, 29.894272],\n                            [111.669034, 29.888565],\n                            [111.669034, 29.888565],\n                            [111.705375, 29.890121],\n                            [111.723853, 29.909317],\n                            [111.723853, 29.909317],\n                            [111.75773, 29.92021],\n                            [111.8107, 29.901017],\n                            [111.861207, 29.856909],\n                            [111.899396, 29.855871],\n                            [111.899396, 29.855871],\n                            [111.925881, 29.836665],\n                            [111.965917, 29.832512],\n                            [111.95483, 29.796683],\n                            [112.008417, 29.778505],\n                            [112.07617, 29.743696],\n                            [112.065699, 29.681323],\n                            [112.089721, 29.685482],\n                            [112.111279, 29.659483],\n                            [112.178416, 29.656883],\n                            [112.202438, 29.633997],\n                            [112.244322, 29.659483],\n                            [112.233851, 29.61631],\n                            [112.303452, 29.585609],\n                            [112.281278, 29.536676],\n                            [112.291133, 29.517409],\n                            [112.333017, 29.545007],\n                            [112.368741, 29.541362],\n                            [112.424792, 29.598619],\n                            [112.439574, 29.633997],\n                            [112.499321, 29.629316],\n                            [112.54182, 29.60122],\n                            [112.572001, 29.624113],\n                            [112.640371, 29.607985],\n                            [112.650842, 29.592374],\n                            [112.693957, 29.601741],\n                            [112.714283, 29.648561],\n                            [112.733378, 29.645441],\n                            [112.788812, 29.681323],\n                            [112.79374, 29.735902],\n                            [112.861493, 29.78318],\n                            [112.894138, 29.783699],\n                            [112.902145, 29.79149],\n                            [112.929246, 29.77383],\n                            [112.923703, 29.766557],\n                            [112.926782, 29.692241],\n                            [112.944645, 29.682883],\n                            [112.974826, 29.732784],\n                            [113.025949, 29.772791],\n                            [113.005007, 29.693801],\n                            [112.915696, 29.620992],\n                            [112.912, 29.606944],\n                            [112.950188, 29.473132],\n                            [113.034572, 29.523658],\n                            [113.057362, 29.522616],\n                            [113.078304, 29.438218],\n                            [113.099861, 29.459585],\n                            [113.145441, 29.449163],\n                            [113.181781, 29.485636],\n                            [113.222433, 29.543965],\n                            [113.277252, 29.594976],\n                            [113.37765, 29.703158],\n                            [113.571671, 29.849123],\n                            [113.575367, 29.809147],\n                            [113.550729, 29.768115],\n                            [113.558736, 29.727067],\n                            [113.540258, 29.699519],\n                            [113.547033, 29.675603],\n                            [113.606164, 29.666764],\n                            [113.663446, 29.684443],\n                            [113.680692, 29.64336],\n                            [113.704098, 29.634518],\n                            [113.73859, 29.579363],\n                            [113.710257, 29.555419],\n                            [113.630801, 29.523137],\n                            [113.677613, 29.513763],\n                            [113.755221, 29.446557],\n                            [113.731199, 29.393907],\n                            [113.674533, 29.388172],\n                            [113.660982, 29.333405],\n                            [113.632033, 29.316186],\n                            [113.609859, 29.25146],\n                            [113.651743, 29.225872],\n                            [113.693011, 29.226394],\n                            [113.691779, 29.19662],\n                            [113.66283, 29.16945],\n                            [113.690547, 29.114566],\n                            [113.696091, 29.077437],\n                            [113.722576, 29.104631],\n                            [113.749677, 29.060699],\n                            [113.775547, 29.095219],\n                            [113.816199, 29.105154],\n                            [113.852539, 29.058606],\n                            [113.882104, 29.065407],\n                            [113.876561, 29.038202],\n                            [113.898119, 29.029307],\n                            [113.94185, 29.047097],\n                            [113.952321, 29.092604],\n                            [113.98743, 29.126068],\n                            [114.034857, 29.152204],\n                            [114.063191, 29.204978],\n                            [114.169748, 29.216993],\n                            [114.252284, 29.23475],\n                            [114.259059, 29.343839],\n                            [114.307102, 29.365225],\n                            [114.341595, 29.327665],\n                            [114.376088, 29.322969],\n                            [114.440145, 29.341752],\n                            [114.466015, 29.324013],\n                            [114.519602, 29.325578],\n                            [114.589819, 29.352707],\n                            [114.621847, 29.379828],\n                            [114.67297, 29.395993],\n                            [114.740724, 29.386607],\n                            [114.759818, 29.363139],\n                            [114.784455, 29.386086],\n                            [114.812173, 29.383478],\n                            [114.866375, 29.404335],\n                            [114.895325, 29.397557],\n                            [114.931049, 29.422581],\n                            [114.947063, 29.465317],\n                            [114.935977, 29.486678],\n                            [114.90518, 29.473132],\n                            [114.918114, 29.454374],\n                            [114.888549, 29.436134],\n                            [114.860216, 29.476258],\n                            [114.900868, 29.505951],\n                            [114.940288, 29.493971],\n                            [114.966773, 29.522096],\n                            [114.947679, 29.542924],\n                            [115.00065, 29.572076],\n                            [115.033295, 29.546568],\n                            [115.087498, 29.560104],\n                            [115.086266, 29.525741],\n                            [115.154019, 29.510117],\n                            [115.157099, 29.584568],\n                            [115.120142, 29.597578],\n                            [115.143548, 29.645961],\n                            [115.117679, 29.655843],\n                            [115.113367, 29.684963],\n                            [115.176809, 29.654803],\n                            [115.250722, 29.660003],\n                            [115.28583, 29.618391],\n                            [115.304924, 29.637118],\n                            [115.355431, 29.649602],\n                            [115.412714, 29.688602],\n                            [115.470612, 29.739539],\n                            [115.479235, 29.811224],\n                            [115.51188, 29.840299],\n                            [115.611662, 29.841337],\n                            [115.667712, 29.850161],\n                            [115.706517, 29.837703],\n                            [115.762567, 29.793048],\n                            [115.837096, 29.748373],\n                            [115.909777, 29.723949],\n                            [115.965827, 29.724469],\n                            [116.049595, 29.761881],\n                            [116.087167, 29.795125],\n                            [116.13521, 29.819532],\n                            [116.128435, 29.897904],\n                            [116.073616, 29.969993],\n                            [116.091479, 30.036331],\n                            [116.078544, 30.062233],\n                            [116.088399, 30.110391],\n                            [116.055754, 30.180774],\n                            [116.065609, 30.204569],\n                            [115.997856, 30.252657],\n                            [115.985537, 30.290905],\n                            [115.903001, 30.31364],\n                            [115.91532, 30.337919],\n                            [115.885139, 30.379747],\n                            [115.921479, 30.416397],\n                            [115.894994, 30.452517],\n                            [115.910393, 30.519046],\n                            [115.887603, 30.542758],\n                            [115.876516, 30.582438],\n                            [115.848799, 30.602014],\n                            [115.819234, 30.597893],\n                            [115.81369, 30.637035],\n                            [115.762567, 30.685426],\n                            [115.782893, 30.751795],\n                            [115.851262, 30.756938],\n                            [115.863581, 30.815549],\n                            [115.848799, 30.828397],\n                            [115.865429, 30.864364],\n                            [115.932566, 30.889532],\n                            [115.976298, 30.931636],\n                            [116.03974, 30.957813],\n                            [116.071769, 30.956787],\n                            [116.058834, 31.012711],\n                            [116.015102, 31.011685],\n                            [116.006479, 31.034764],\n                            [115.938726, 31.04707],\n                            [115.939958, 31.071678],\n                            [115.887603, 31.10909],\n                            [115.867277, 31.147512],\n                            [115.837712, 31.127022],\n                            [115.797676, 31.128047],\n                            [115.778582, 31.112164],\n                            [115.700973, 31.201276],\n                            [115.655394, 31.211002],\n                            [115.603655, 31.17363],\n                            [115.585793, 31.143926],\n                            [115.540213, 31.194621],\n                            [115.539597, 31.231985],\n                            [115.507568, 31.267799],\n                            [115.473076, 31.265242],\n                            [115.443511, 31.344498],\n                            [115.40717, 31.337854],\n                            [115.372062, 31.349098],\n                            [115.393004, 31.389977],\n                            [115.373909, 31.405813],\n                            [115.338801, 31.40428],\n                            [115.301229, 31.383846],\n                            [115.250722, 31.392021],\n                            [115.252569, 31.421646],\n                            [115.211301, 31.442072],\n                            [115.218077, 31.515057],\n                            [115.235939, 31.555354],\n                            [115.212533, 31.555354],\n                            [115.16449, 31.604808],\n                            [115.12507, 31.599201],\n                            [115.106592, 31.567592],\n                            [115.114599, 31.530362],\n                            [115.096121, 31.508425],\n                            [115.022824, 31.527811],\n                            [114.995107, 31.471171],\n                            [114.962462, 31.494648],\n                            [114.884238, 31.469129],\n                            [114.870071, 31.479337],\n                            [114.830035, 31.45892],\n                            [114.789383, 31.480358],\n                            [114.778912, 31.520669],\n                            [114.696376, 31.525771],\n                            [114.641558, 31.582378],\n                            [114.61692, 31.585437],\n                            [114.572572, 31.553824],\n                            [114.560869, 31.560963],\n                            [114.547935, 31.623665],\n                            [114.57134, 31.660858],\n                            [114.586123, 31.762172],\n                            [114.549783, 31.766751],\n                            [114.530688, 31.742834],\n                            [114.443841, 31.728074],\n                            [114.403189, 31.746906],\n                            [114.350218, 31.755557],\n                            [114.292936, 31.752503],\n                            [114.235654, 31.833382],\n                            [114.191922, 31.852192],\n                            [114.134024, 31.843042],\n                            [114.121705, 31.809482],\n                            [114.086596, 31.782014],\n                            [114.017611, 31.770822],\n                            [113.988662, 31.749959],\n                            [113.952321, 31.793714],\n                            [113.957865, 31.852701],\n                            [113.914749, 31.877098],\n                            [113.893807, 31.847109],\n                            [113.854387, 31.843042],\n                            [113.830981, 31.87913],\n                            [113.832213, 31.918761],\n                            [113.805728, 31.929428],\n                            [113.817431, 31.964467],\n                            [113.757685, 31.98985],\n                            [113.791561, 32.036028],\n                            [113.728735, 32.083197],\n                            [113.722576, 32.12426],\n                            [113.750293, 32.11615],\n                            [113.782322, 32.184553],\n                            [113.752757, 32.215951],\n                            [113.73859, 32.255942],\n                            [113.749061, 32.272642],\n                            [113.768772, 32.30148],\n                            [113.753989, 32.328286],\n                            [113.76754, 32.370249],\n                            [113.735511, 32.410677],\n                            [113.700402, 32.420782],\n                            [113.650511, 32.412698],\n                            [113.624642, 32.36115],\n                            [113.511925, 32.316654],\n                            [113.428773, 32.270618],\n                            [113.376418, 32.298445],\n                            [113.353628, 32.294904],\n                            [113.317904, 32.327275],\n                            [113.333918, 32.336377],\n                            [113.2366, 32.407141],\n                            [113.211962, 32.431895],\n                            [113.158992, 32.410677],\n                            [113.155912, 32.380863],\n                            [113.118956, 32.375809],\n                            [113.107869, 32.398551],\n                            [113.078919, 32.394508],\n                            [113.025949, 32.425328],\n                            [113.000695, 32.41674],\n                            [112.992072, 32.378336],\n                            [112.912, 32.390971],\n                            [112.888594, 32.37682],\n                            [112.860877, 32.396024],\n                            [112.776493, 32.358623],\n                            [112.735841, 32.356095],\n                            [112.733993, 32.356601],\n                            [112.724138, 32.358623],\n                            [112.716747, 32.357612],\n                            [112.645298, 32.368227],\n                            [112.612037, 32.386928],\n                            [112.589248, 32.381369],\n                            [112.545516, 32.404109],\n                            [112.530733, 32.37682],\n                            [112.477147, 32.380863],\n                            [112.448814, 32.34295],\n                            [112.390915, 32.37126],\n                            [112.360118, 32.3657],\n                            [112.328089, 32.321712],\n                            [112.206133, 32.392992],\n                            [112.172873, 32.385412],\n                            [112.150083, 32.411688],\n                            [112.155626, 32.377326],\n                            [112.081098, 32.425833],\n                            [112.063851, 32.474315],\n                            [112.014576, 32.450077],\n                            [111.975772, 32.471791],\n                            [111.948671, 32.51722],\n                            [111.890157, 32.503089],\n                            [111.858128, 32.528826],\n                            [111.808853, 32.536899],\n                            [111.713382, 32.606497],\n                            [111.646245, 32.605993],\n                            [111.640701, 32.634724],\n                            [111.577875, 32.593388],\n                            [111.530448, 32.628172],\n                            [111.513202, 32.674026],\n                            [111.458383, 32.726402],\n                            [111.475629, 32.760127],\n                            [111.41342, 32.757108],\n                            [111.380159, 32.829049],\n                            [111.293311, 32.859217],\n                            [111.276065, 32.903445],\n                            [111.255123, 32.883846],\n                            [111.242804, 32.930573],\n                            [111.273601, 32.971753],\n                            [111.258819, 33.006389],\n                            [111.221862, 33.042517],\n                            [111.152877, 33.039507],\n                            [111.192913, 33.071609],\n                            [111.179363, 33.115229],\n                            [111.146102, 33.12375],\n                            [111.12824, 33.15532],\n                            [111.08882, 33.181871],\n                            [111.045704, 33.169849]\n                        ]\n                    ],\n                    [\n                        [\n                            [109.106111, 30.570587],\n                            [109.101183, 30.579346],\n                            [109.09872, 30.579346],\n                            [109.106111, 30.570587]\n                        ]\n                    ],\n                    [\n                        [\n                            [111.046936, 33.202905],\n                            [111.035849, 33.187881],\n                            [111.034001, 33.177864],\n                            [111.045704, 33.169849],\n                            [111.046936, 33.202905]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.716747, 32.357612],\n                            [112.735841, 32.356095],\n                            [112.733993, 32.356601],\n                            [112.724138, 32.358623],\n                            [112.716747, 32.357612]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.902145, 29.79149],\n                            [112.894138, 29.783699],\n                            [112.923703, 29.766557],\n                            [112.929246, 29.77383],\n                            [112.902145, 29.79149]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 430000,\n                \"name\": \"Hunan\",\n                \"center\": [112.982279, 28.19409],\n                \"centroid\": [111.711649, 27.629216],\n                \"childrenNum\": 14,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 17,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [112.024431, 24.740308],\n                            [112.03367, 24.771286],\n                            [112.124214, 24.841364],\n                            [112.149467, 24.837019],\n                            [112.167329, 24.859828],\n                            [112.175337, 24.927685],\n                            [112.119902, 24.963499],\n                            [112.12175, 24.989538],\n                            [112.155626, 25.026419],\n                            [112.151931, 25.055698],\n                            [112.177184, 25.106649],\n                            [112.187039, 25.182494],\n                            [112.246785, 25.185202],\n                            [112.256025, 25.159204],\n                            [112.302836, 25.157037],\n                            [112.315771, 25.175453],\n                            [112.365046, 25.191701],\n                            [112.414937, 25.14241],\n                            [112.44327, 25.185744],\n                            [112.458053, 25.152162],\n                            [112.562762, 25.124531],\n                            [112.628052, 25.140785],\n                            [112.660081, 25.132658],\n                            [112.712436, 25.083344],\n                            [112.714899, 25.025876],\n                            [112.742001, 24.99876],\n                            [112.743233, 24.959701],\n                            [112.778341, 24.947764],\n                            [112.780805, 24.896747],\n                            [112.873812, 24.896747],\n                            [112.904609, 24.921715],\n                            [112.941565, 24.915745],\n                            [112.994536, 24.927142],\n                            [113.009934, 24.977604],\n                            [112.979137, 25.03401],\n                            [113.004391, 25.089306],\n                            [112.96805, 25.141869],\n                            [112.97421, 25.168412],\n                            [113.034572, 25.198199],\n                            [112.992688, 25.247467],\n                            [112.958195, 25.254503],\n                            [112.897833, 25.238264],\n                            [112.867036, 25.249632],\n                            [112.854718, 25.337829],\n                            [112.891058, 25.339993],\n                            [112.924319, 25.296714],\n                            [112.93479, 25.325929],\n                            [112.969898, 25.350269],\n                            [113.013014, 25.352432],\n                            [113.078304, 25.382174],\n                            [113.096782, 25.412449],\n                            [113.131274, 25.414611],\n                            [113.11834, 25.445418],\n                            [113.176854, 25.471355],\n                            [113.226129, 25.50971],\n                            [113.248919, 25.514031],\n                            [113.311129, 25.490264],\n                            [113.314208, 25.442716],\n                            [113.341926, 25.448661],\n                            [113.373338, 25.402719],\n                            [113.407215, 25.401637],\n                            [113.449715, 25.359463],\n                            [113.479896, 25.375145],\n                            [113.535946, 25.368656],\n                            [113.579062, 25.34432],\n                            [113.584606, 25.306453],\n                            [113.611707, 25.327552],\n                            [113.680076, 25.334584],\n                            [113.686852, 25.351891],\n                            [113.753373, 25.362707],\n                            [113.76446, 25.333502],\n                            [113.814967, 25.328634],\n                            [113.839605, 25.363248],\n                            [113.877177, 25.380552],\n                            [113.887032, 25.436772],\n                            [113.94493, 25.441635],\n                            [113.962792, 25.528072],\n                            [113.986198, 25.529153],\n                            [113.983118, 25.599336],\n                            [113.957249, 25.611749],\n                            [113.913517, 25.701299],\n                            [113.920293, 25.741197],\n                            [113.961561, 25.77731],\n                            [113.971416, 25.836036],\n                            [114.028082, 25.893119],\n                            [114.028082, 25.98138],\n                            [114.008372, 26.015806],\n                            [114.044096, 26.076564],\n                            [114.087828, 26.06635],\n                            [114.121089, 26.085702],\n                            [114.10569, 26.097526],\n                            [114.188842, 26.121172],\n                            [114.237501, 26.152333],\n                            [114.216559, 26.203355],\n                            [114.181451, 26.214631],\n                            [114.102611, 26.187783],\n                            [114.088444, 26.168448],\n                            [114.013299, 26.184023],\n                            [113.962792, 26.150722],\n                            [113.949242, 26.192616],\n                            [113.972647, 26.20604],\n                            [113.978807, 26.237716],\n                            [114.029314, 26.266163],\n                            [114.021307, 26.288701],\n                            [114.047792, 26.337518],\n                            [114.030546, 26.376664],\n                            [114.062575, 26.406149],\n                            [114.085364, 26.406149],\n                            [114.090292, 26.455988],\n                            [114.110002, 26.482775],\n                            [114.07243, 26.480096],\n                            [114.10877, 26.56952],\n                            [114.019459, 26.587182],\n                            [113.996669, 26.615543],\n                            [113.912901, 26.613938],\n                            [113.860546, 26.664221],\n                            [113.853771, 26.769532],\n                            [113.835909, 26.806394],\n                            [113.877177, 26.859262],\n                            [113.890112, 26.895562],\n                            [113.927068, 26.948922],\n                            [113.892575, 26.964925],\n                            [113.86301, 27.018252],\n                            [113.824206, 27.036378],\n                            [113.803264, 27.099261],\n                            [113.771851, 27.096598],\n                            [113.779242, 27.137081],\n                            [113.846996, 27.222262],\n                            [113.872865, 27.289828],\n                            [113.854387, 27.30525],\n                            [113.872865, 27.346721],\n                            [113.872865, 27.384988],\n                            [113.72812, 27.350442],\n                            [113.699786, 27.331836],\n                            [113.657902, 27.347253],\n                            [113.616635, 27.345658],\n                            [113.605548, 27.38924],\n                            [113.632033, 27.40518],\n                            [113.59754, 27.428554],\n                            [113.591381, 27.467855],\n                            [113.627105, 27.49971],\n                            [113.583374, 27.524657],\n                            [113.579062, 27.545354],\n                            [113.608627, 27.585143],\n                            [113.607395, 27.625449],\n                            [113.652359, 27.663619],\n                            [113.696707, 27.71979],\n                            [113.69917, 27.740979],\n                            [113.763228, 27.799228],\n                            [113.756453, 27.860091],\n                            [113.72812, 27.874904],\n                            [113.752141, 27.93361],\n                            [113.822974, 27.982243],\n                            [113.845148, 27.971672],\n                            [113.864242, 28.004966],\n                            [113.914133, 27.991227],\n                            [113.936307, 28.018703],\n                            [113.966488, 28.017646],\n                            [113.970184, 28.041418],\n                            [114.025618, 28.031382],\n                            [114.047176, 28.057263],\n                            [114.025002, 28.080499],\n                            [113.992357, 28.161255],\n                            [114.012068, 28.174972],\n                            [114.068734, 28.171806],\n                            [114.107538, 28.182885],\n                            [114.109386, 28.205038],\n                            [114.143879, 28.246694],\n                            [114.182067, 28.249858],\n                            [114.198081, 28.29097],\n                            [114.2529, 28.319423],\n                            [114.252284, 28.395787],\n                            [114.214712, 28.403157],\n                            [114.172212, 28.432632],\n                            [114.217175, 28.466308],\n                            [114.218407, 28.48472],\n                            [114.15435, 28.507337],\n                            [114.138335, 28.533629],\n                            [114.08598, 28.558337],\n                            [114.132176, 28.607211],\n                            [114.122321, 28.623497],\n                            [114.157429, 28.761566],\n                            [114.137719, 28.779926],\n                            [114.153734, 28.829221],\n                            [114.124784, 28.843376],\n                            [114.076741, 28.834464],\n                            [114.056415, 28.872204],\n                            [114.060111, 28.902596],\n                            [114.028082, 28.891069],\n                            [114.005292, 28.917788],\n                            [114.008988, 28.955498],\n                            [113.973879, 28.937692],\n                            [113.955401, 28.978536],\n                            [113.961561, 28.999476],\n                            [113.94185, 29.047097],\n                            [113.898119, 29.029307],\n                            [113.876561, 29.038202],\n                            [113.882104, 29.065407],\n                            [113.852539, 29.058606],\n                            [113.816199, 29.105154],\n                            [113.775547, 29.095219],\n                            [113.749677, 29.060699],\n                            [113.722576, 29.104631],\n                            [113.696091, 29.077437],\n                            [113.690547, 29.114566],\n                            [113.66283, 29.16945],\n                            [113.691779, 29.19662],\n                            [113.693011, 29.226394],\n                            [113.651743, 29.225872],\n                            [113.609859, 29.25146],\n                            [113.632033, 29.316186],\n                            [113.660982, 29.333405],\n                            [113.674533, 29.388172],\n                            [113.731199, 29.393907],\n                            [113.755221, 29.446557],\n                            [113.677613, 29.513763],\n                            [113.630801, 29.523137],\n                            [113.710257, 29.555419],\n                            [113.73859, 29.579363],\n                            [113.704098, 29.634518],\n                            [113.680692, 29.64336],\n                            [113.663446, 29.684443],\n                            [113.606164, 29.666764],\n                            [113.547033, 29.675603],\n                            [113.540258, 29.699519],\n                            [113.558736, 29.727067],\n                            [113.550729, 29.768115],\n                            [113.575367, 29.809147],\n                            [113.571671, 29.849123],\n                            [113.37765, 29.703158],\n                            [113.277252, 29.594976],\n                            [113.222433, 29.543965],\n                            [113.181781, 29.485636],\n                            [113.145441, 29.449163],\n                            [113.099861, 29.459585],\n                            [113.078304, 29.438218],\n                            [113.057362, 29.522616],\n                            [113.034572, 29.523658],\n                            [112.950188, 29.473132],\n                            [112.912, 29.606944],\n                            [112.915696, 29.620992],\n                            [113.005007, 29.693801],\n                            [113.025949, 29.772791],\n                            [112.974826, 29.732784],\n                            [112.944645, 29.682883],\n                            [112.926782, 29.692241],\n                            [112.923703, 29.766557],\n                            [112.894138, 29.783699],\n                            [112.861493, 29.78318],\n                            [112.79374, 29.735902],\n                            [112.788812, 29.681323],\n                            [112.733378, 29.645441],\n                            [112.714283, 29.648561],\n                            [112.693957, 29.601741],\n                            [112.650842, 29.592374],\n                            [112.640371, 29.607985],\n                            [112.572001, 29.624113],\n                            [112.54182, 29.60122],\n                            [112.499321, 29.629316],\n                            [112.439574, 29.633997],\n                            [112.424792, 29.598619],\n                            [112.368741, 29.541362],\n                            [112.333017, 29.545007],\n                            [112.291133, 29.517409],\n                            [112.281278, 29.536676],\n                            [112.303452, 29.585609],\n                            [112.233851, 29.61631],\n                            [112.244322, 29.659483],\n                            [112.202438, 29.633997],\n                            [112.178416, 29.656883],\n                            [112.111279, 29.659483],\n                            [112.089721, 29.685482],\n                            [112.065699, 29.681323],\n                            [112.07617, 29.743696],\n                            [112.008417, 29.778505],\n                            [111.95483, 29.796683],\n                            [111.965917, 29.832512],\n                            [111.925881, 29.836665],\n                            [111.899396, 29.855871],\n                            [111.899396, 29.855871],\n                            [111.861207, 29.856909],\n                            [111.8107, 29.901017],\n                            [111.75773, 29.92021],\n                            [111.723853, 29.909317],\n                            [111.723853, 29.909317],\n                            [111.705375, 29.890121],\n                            [111.669034, 29.888565],\n                            [111.669034, 29.888565],\n                            [111.553854, 29.894272],\n                            [111.527368, 29.925916],\n                            [111.475629, 29.918654],\n                            [111.436825, 29.930065],\n                            [111.394325, 29.912948],\n                            [111.382623, 29.95029],\n                            [111.342587, 29.944586],\n                            [111.3315, 29.970512],\n                            [111.266826, 30.01146],\n                            [111.242188, 30.040476],\n                            [111.031537, 30.048765],\n                            [110.929907, 30.063268],\n                            [110.924364, 30.111426],\n                            [110.851067, 30.126439],\n                            [110.746973, 30.112979],\n                            [110.756212, 30.054463],\n                            [110.712481, 30.033223],\n                            [110.650887, 30.07777],\n                            [110.600996, 30.054463],\n                            [110.531394, 30.061197],\n                            [110.497518, 30.055499],\n                            [110.491358, 30.019751],\n                            [110.557264, 29.988137],\n                            [110.517228, 29.961179],\n                            [110.49875, 29.91243],\n                            [110.538786, 29.895828],\n                            [110.549873, 29.848085],\n                            [110.60038, 29.839779],\n                            [110.642879, 29.775907],\n                            [110.562807, 29.712515],\n                            [110.507373, 29.692241],\n                            [110.467337, 29.713034],\n                            [110.447011, 29.664684],\n                            [110.372482, 29.633477],\n                            [110.339221, 29.668324],\n                            [110.302265, 29.661563],\n                            [110.289946, 29.6964],\n                            [110.219729, 29.746814],\n                            [110.160599, 29.753569],\n                            [110.113788, 29.789932],\n                            [110.02386, 29.769674],\n                            [109.941325, 29.774349],\n                            [109.908064, 29.763959],\n                            [109.869876, 29.774869],\n                            [109.779333, 29.757725],\n                            [109.755311, 29.733304],\n                            [109.760238, 29.689122],\n                            [109.714659, 29.673524],\n                            [109.701108, 29.636078],\n                            [109.717739, 29.615269],\n                            [109.664768, 29.599659],\n                            [109.651833, 29.625674],\n                            [109.578536, 29.629836],\n                            [109.558826, 29.606944],\n                            [109.516326, 29.626194],\n                            [109.488609, 29.553336],\n                            [109.467051, 29.560104],\n                            [109.458428, 29.513242],\n                            [109.433791, 29.530948],\n                            [109.436254, 29.488761],\n                            [109.415928, 29.497617],\n                            [109.418392, 29.453332],\n                            [109.368501, 29.413719],\n                            [109.391291, 29.372005],\n                            [109.343863, 29.369398],\n                            [109.352487, 29.284872],\n                            [109.312451, 29.25146],\n                            [109.257632, 29.222738],\n                            [109.275494, 29.202366],\n                            [109.261328, 29.161089],\n                            [109.274262, 29.121885],\n                            [109.232378, 29.119271],\n                            [109.240386, 29.086328],\n                            [109.312451, 29.066453],\n                            [109.319842, 29.042388],\n                            [109.294588, 29.015177],\n                            [109.292741, 28.987436],\n                            [109.261328, 28.952356],\n                            [109.235458, 28.882161],\n                            [109.246545, 28.80143],\n                            [109.241002, 28.776779],\n                            [109.2989, 28.7474],\n                            [109.294588, 28.722211],\n                            [109.252704, 28.691767],\n                            [109.271183, 28.671816],\n                            [109.192958, 28.636104],\n                            [109.201581, 28.597753],\n                            [109.235458, 28.61982],\n                            [109.252089, 28.606685],\n                            [109.306907, 28.62087],\n                            [109.319842, 28.579886],\n                            [109.273646, 28.53836],\n                            [109.274262, 28.494714],\n                            [109.260712, 28.46473],\n                            [109.264407, 28.392628],\n                            [109.289045, 28.373673],\n                            [109.268719, 28.33786],\n                            [109.275494, 28.313101],\n                            [109.317994, 28.277795],\n                            [109.33524, 28.293605],\n                            [109.388211, 28.268307],\n                            [109.367885, 28.254602],\n                            [109.340168, 28.19027],\n                            [109.33832, 28.141731],\n                            [109.314298, 28.103729],\n                            [109.298284, 28.036136],\n                            [109.335856, 28.063073],\n                            [109.378972, 28.034551],\n                            [109.362342, 28.007608],\n                            [109.319842, 27.988585],\n                            [109.30198, 27.956343],\n                            [109.32169, 27.868027],\n                            [109.346943, 27.838396],\n                            [109.332777, 27.782815],\n                            [109.37774, 27.736741],\n                            [109.366653, 27.721909],\n                            [109.414081, 27.725087],\n                            [109.470747, 27.680049],\n                            [109.45658, 27.673689],\n                            [109.470131, 27.62863],\n                            [109.451037, 27.586204],\n                            [109.461508, 27.567637],\n                            [109.404841, 27.55066],\n                            [109.303211, 27.47582],\n                            [109.300132, 27.423774],\n                            [109.245313, 27.41793],\n                            [109.202197, 27.450331],\n                            [109.167089, 27.41793],\n                            [109.141835, 27.448207],\n                            [109.142451, 27.418461],\n                            [109.103647, 27.336621],\n                            [109.044517, 27.331304],\n                            [109.053756, 27.293551],\n                            [108.983539, 27.26802],\n                            [108.963213, 27.235565],\n                            [108.907778, 27.204699],\n                            [108.926873, 27.160512],\n                            [108.878829, 27.106187],\n                            [108.79075, 27.084343],\n                            [108.877597, 27.01612],\n                            [108.942887, 27.017186],\n                            [108.942887, 27.017186],\n                            [108.940423, 27.044907],\n                            [109.007561, 27.08008],\n                            [109.032814, 27.104056],\n                            [109.128901, 27.122701],\n                            [109.101183, 27.06889],\n                            [109.165857, 27.066758],\n                            [109.21698, 27.114711],\n                            [109.239154, 27.14933],\n                            [109.264407, 27.131755],\n                            [109.33524, 27.139212],\n                            [109.358646, 27.153058],\n                            [109.415312, 27.154123],\n                            [109.441182, 27.117907],\n                            [109.472595, 27.134951],\n                            [109.454733, 27.069423],\n                            [109.486761, 27.053968],\n                            [109.497848, 27.079548],\n                            [109.520022, 27.058764],\n                            [109.555131, 26.946788],\n                            [109.436254, 26.892359],\n                            [109.452885, 26.861932],\n                            [109.486761, 26.895562],\n                            [109.509551, 26.877947],\n                            [109.513247, 26.84004],\n                            [109.497232, 26.815474],\n                            [109.522486, 26.749226],\n                            [109.528645, 26.743881],\n                            [109.554515, 26.73533],\n                            [109.597015, 26.756173],\n                            [109.568065, 26.726243],\n                            [109.528645, 26.743881],\n                            [109.52187, 26.749226],\n                            [109.486761, 26.759913],\n                            [109.447957, 26.759913],\n                            [109.407305, 26.719829],\n                            [109.35495, 26.693098],\n                            [109.283501, 26.698445],\n                            [109.306291, 26.661012],\n                            [109.334008, 26.646036],\n                            [109.35495, 26.658873],\n                            [109.390675, 26.598955],\n                            [109.407305, 26.533116],\n                            [109.381436, 26.518659],\n                            [109.385747, 26.493487],\n                            [109.362342, 26.472061],\n                            [109.38082, 26.454381],\n                            [109.319842, 26.418477],\n                            [109.29582, 26.350389],\n                            [109.271183, 26.327863],\n                            [109.285965, 26.295676],\n                            [109.325385, 26.29031],\n                            [109.351255, 26.264016],\n                            [109.369733, 26.277432],\n                            [109.442414, 26.289774],\n                            [109.467051, 26.313917],\n                            [109.439334, 26.238789],\n                            [109.47629, 26.148035],\n                            [109.513863, 26.128157],\n                            [109.502776, 26.096451],\n                            [109.449805, 26.101826],\n                            [109.452885, 26.055598],\n                            [109.48245, 26.029788],\n                            [109.513247, 25.998056],\n                            [109.560058, 26.021184],\n                            [109.588391, 26.019571],\n                            [109.635203, 26.047533],\n                            [109.649369, 26.016882],\n                            [109.730057, 25.989988],\n                            [109.710963, 25.954478],\n                            [109.693717, 25.959321],\n                            [109.67955, 25.921649],\n                            [109.685094, 25.880197],\n                            [109.768246, 25.890427],\n                            [109.779333, 25.866196],\n                            [109.811361, 25.877504],\n                            [109.826144, 25.911422],\n                            [109.806434, 25.973848],\n                            [109.782412, 25.996981],\n                            [109.814441, 26.041081],\n                            [109.864332, 26.027637],\n                            [109.898825, 26.095377],\n                            [109.904368, 26.135679],\n                            [109.970274, 26.195301],\n                            [110.03002, 26.166299],\n                            [110.099005, 26.168985],\n                            [110.100853, 26.132455],\n                            [110.065128, 26.050221],\n                            [110.100853, 26.020108],\n                            [110.168606, 26.028713],\n                            [110.181541, 26.060437],\n                            [110.24991, 26.010965],\n                            [110.257301, 25.961473],\n                            [110.325671, 25.975462],\n                            [110.373098, 26.088927],\n                            [110.437772, 26.153945],\n                            [110.477808, 26.179727],\n                            [110.495054, 26.166299],\n                            [110.546793, 26.233421],\n                            [110.552952, 26.283335],\n                            [110.584365, 26.296749],\n                            [110.612083, 26.333764],\n                            [110.643495, 26.308552],\n                            [110.673676, 26.317135],\n                            [110.721104, 26.294066],\n                            [110.742046, 26.313917],\n                            [110.73527, 26.270993],\n                            [110.759292, 26.248451],\n                            [110.836284, 26.255966],\n                            [110.939762, 26.286554],\n                            [110.926212, 26.320354],\n                            [110.944074, 26.326791],\n                            [110.94469, 26.373447],\n                            [110.974255, 26.385778],\n                            [111.008747, 26.35897],\n                            [111.008132, 26.336982],\n                            [111.090667, 26.308016],\n                            [111.208928, 26.30426],\n                            [111.204616, 26.276359],\n                            [111.228022, 26.261333],\n                            [111.277913, 26.272066],\n                            [111.293311, 26.222148],\n                            [111.271754, 26.217316],\n                            [111.274833, 26.183486],\n                            [111.258203, 26.151796],\n                            [111.26621, 26.095914],\n                            [111.244652, 26.078177],\n                            [111.267442, 26.058824],\n                            [111.235413, 26.048071],\n                            [111.189834, 25.953402],\n                            [111.230486, 25.916267],\n                            [111.251428, 25.864581],\n                            [111.29208, 25.854349],\n                            [111.297007, 25.874274],\n                            [111.346282, 25.906577],\n                            [111.376463, 25.906039],\n                            [111.383239, 25.881812],\n                            [111.460231, 25.885042],\n                            [111.4861, 25.859196],\n                            [111.43313, 25.84627],\n                            [111.442369, 25.77192],\n                            [111.399869, 25.744431],\n                            [111.30871, 25.720171],\n                            [111.309942, 25.645203],\n                            [111.343202, 25.602574],\n                            [111.324724, 25.564249],\n                            [111.32842, 25.521592],\n                            [111.279145, 25.42326],\n                            [111.210776, 25.363248],\n                            [111.184906, 25.367034],\n                            [111.138711, 25.303748],\n                            [111.103602, 25.285351],\n                            [111.112841, 25.21715],\n                            [110.998892, 25.161371],\n                            [110.98411, 25.101772],\n                            [110.951465, 25.04377],\n                            [110.968711, 24.975434],\n                            [111.009363, 24.921172],\n                            [111.100522, 24.945593],\n                            [111.101754, 25.035095],\n                            [111.139943, 25.042144],\n                            [111.200921, 25.074672],\n                            [111.221862, 25.106649],\n                            [111.274833, 25.151078],\n                            [111.321645, 25.105023],\n                            [111.36784, 25.108817],\n                            [111.375231, 25.128324],\n                            [111.435593, 25.093642],\n                            [111.416499, 25.047566],\n                            [111.467622, 25.02208],\n                            [111.460231, 24.992793],\n                            [111.43313, 24.979774],\n                            [111.434977, 24.951562],\n                            [111.470086, 24.92877],\n                            [111.447296, 24.892947],\n                            [111.449144, 24.857113],\n                            [111.479325, 24.797366],\n                            [111.461463, 24.728894],\n                            [111.431282, 24.687574],\n                            [111.451608, 24.665822],\n                            [111.499035, 24.667997],\n                            [111.526752, 24.637538],\n                            [111.570484, 24.64461],\n                            [111.588962, 24.690837],\n                            [111.641933, 24.684856],\n                            [111.637621, 24.715303],\n                            [111.666571, 24.760961],\n                            [111.708455, 24.788673],\n                            [111.783599, 24.785957],\n                            [111.814396, 24.770199],\n                            [111.868599, 24.771829],\n                            [111.875374, 24.756613],\n                            [111.929577, 24.75607],\n                            [111.951135, 24.769655],\n                            [112.024431, 24.740308]\n                        ]\n                    ],\n                    [\n                        [\n                            [109.528645, 26.743881],\n                            [109.522486, 26.749226],\n                            [109.52187, 26.749226],\n                            [109.528645, 26.743881]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 440000,\n                \"name\": \"Guangdong\",\n                \"center\": [113.280637, 23.125178],\n                \"centroid\": [113.429919, 23.334643],\n                \"childrenNum\": 21,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 18,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [113.558736, 22.212244],\n                            [113.594461, 22.228864],\n                            [113.595693, 22.304186],\n                            [113.617866, 22.315259],\n                            [113.604932, 22.339617],\n                            [113.627721, 22.349027],\n                            [113.669605, 22.416539],\n                            [113.66591, 22.438667],\n                            [113.624642, 22.443092],\n                            [113.608627, 22.408793],\n                            [113.573519, 22.41156],\n                            [113.631417, 22.475723],\n                            [113.668373, 22.4807],\n                            [113.691779, 22.514981],\n                            [113.740438, 22.534329],\n                            [113.717033, 22.645391],\n                            [113.678228, 22.726007],\n                            [113.733663, 22.736494],\n                            [113.758301, 22.683496],\n                            [113.765692, 22.665825],\n                            [113.803264, 22.593463],\n                            [113.856851, 22.539857],\n                            [113.869786, 22.459685],\n                            [113.893807, 22.442539],\n                            [113.952937, 22.486783],\n                            [113.954785, 22.491206],\n                            [113.976343, 22.510558],\n                            [114.031778, 22.503923],\n                            [114.082285, 22.512216],\n                            [114.095219, 22.534329],\n                            [114.156813, 22.543726],\n                            [114.166052, 22.559201],\n                            [114.222719, 22.553122],\n                            [114.232574, 22.539857],\n                            [114.294784, 22.563623],\n                            [114.321885, 22.587385],\n                            [114.381631, 22.60175],\n                            [114.427211, 22.589042],\n                            [114.472174, 22.522168],\n                            [114.476486, 22.459132],\n                            [114.506667, 22.438667],\n                            [114.549167, 22.465769],\n                            [114.611377, 22.481806],\n                            [114.628623, 22.513875],\n                            [114.614456, 22.545384],\n                            [114.568261, 22.560859],\n                            [114.559022, 22.583517],\n                            [114.603369, 22.638763],\n                            [114.579964, 22.661407],\n                            [114.51529, 22.655332],\n                            [114.567029, 22.685705],\n                            [114.591666, 22.690122],\n                            [114.601521, 22.730975],\n                            [114.689601, 22.7674],\n                            [114.709927, 22.787817],\n                            [114.749963, 22.764089],\n                            [114.73518, 22.724351],\n                            [114.728405, 22.651466],\n                            [114.743803, 22.632687],\n                            [114.746267, 22.581859],\n                            [114.866375, 22.591805],\n                            [114.88547, 22.538751],\n                            [114.922426, 22.549253],\n                            [114.927969, 22.621639],\n                            [114.945216, 22.645391],\n                            [115.039454, 22.713862],\n                            [115.02344, 22.726007],\n                            [115.053621, 22.747533],\n                            [115.076411, 22.788368],\n                            [115.154635, 22.80161],\n                            [115.190975, 22.77347],\n                            [115.190359, 22.818711],\n                            [115.236555, 22.82533],\n                            [115.230396, 22.776781],\n                            [115.319091, 22.783402],\n                            [115.338185, 22.776781],\n                            [115.349272, 22.712206],\n                            [115.381301, 22.684048],\n                            [115.430576, 22.684048],\n                            [115.471844, 22.697852],\n                            [115.575322, 22.650914],\n                            [115.565467, 22.684048],\n                            [115.609198, 22.753052],\n                            [115.541445, 22.755259],\n                            [115.570394, 22.786713],\n                            [115.583945, 22.82864],\n                            [115.654162, 22.865591],\n                            [115.696046, 22.84298],\n                            [115.760103, 22.834707],\n                            [115.788437, 22.809885],\n                            [115.796444, 22.739254],\n                            [115.829089, 22.734838],\n                            [115.883291, 22.78561],\n                            [115.931334, 22.802713],\n                            [115.965211, 22.800506],\n                            [115.99724, 22.826985],\n                            [116.05637, 22.844635],\n                            [116.104413, 22.816505],\n                            [116.14137, 22.835259],\n                            [116.239304, 22.921275],\n                            [116.259014, 22.932298],\n                            [116.302746, 22.951588],\n                            [116.382818, 22.91907],\n                            [116.449955, 22.936707],\n                            [116.50539, 22.930645],\n                            [116.544194, 22.996769],\n                            [116.576839, 23.014397],\n                            [116.557129, 23.056253],\n                            [116.566368, 23.088738],\n                            [116.550969, 23.109656],\n                            [116.566368, 23.134424],\n                            [116.665534, 23.158086],\n                            [116.701259, 23.198248],\n                            [116.74499, 23.215299],\n                            [116.806584, 23.200998],\n                            [116.821367, 23.240597],\n                            [116.798577, 23.244996],\n                            [116.782563, 23.313714],\n                            [116.871874, 23.4159],\n                            [116.871258, 23.416449],\n                            [116.874338, 23.447199],\n                            [116.874953, 23.447748],\n                            [116.895895, 23.476295],\n                            [116.888504, 23.501543],\n                            [116.92854, 23.530079],\n                            [116.963649, 23.507031],\n                            [117.01046, 23.502641],\n                            [117.044953, 23.539955],\n                            [117.085605, 23.536663],\n                            [117.192778, 23.5619],\n                            [117.192778, 23.629356],\n                            [117.147199, 23.654027],\n                            [117.123793, 23.647448],\n                            [117.055424, 23.694038],\n                            [117.048032, 23.758687],\n                            [117.019083, 23.801952],\n                            [117.012308, 23.855054],\n                            [116.981511, 23.855602],\n                            [116.955642, 23.922359],\n                            [116.976583, 23.931659],\n                            [116.981511, 23.999471],\n                            [116.953178, 24.008218],\n                            [116.930388, 24.064514],\n                            [116.9347, 24.126794],\n                            [116.998757, 24.179217],\n                            [116.956257, 24.216883],\n                            [116.933468, 24.220157],\n                            [116.938395, 24.28127],\n                            [116.914374, 24.287817],\n                            [116.919301, 24.321087],\n                            [116.895895, 24.350533],\n                            [116.903903, 24.369614],\n                            [116.839229, 24.442097],\n                            [116.860787, 24.460075],\n                            [116.83307, 24.496568],\n                            [116.796729, 24.502014],\n                            [116.759157, 24.545572],\n                            [116.761005, 24.583128],\n                            [116.815207, 24.654944],\n                            [116.777635, 24.679418],\n                            [116.667382, 24.658752],\n                            [116.623034, 24.64189],\n                            [116.600861, 24.654401],\n                            [116.570679, 24.621762],\n                            [116.530027, 24.604895],\n                            [116.506622, 24.621218],\n                            [116.517709, 24.652225],\n                            [116.485064, 24.720196],\n                            [116.44626, 24.714216],\n                            [116.416079, 24.744113],\n                            [116.419158, 24.767482],\n                            [116.375427, 24.803885],\n                            [116.381586, 24.82507],\n                            [116.417927, 24.840821],\n                            [116.395137, 24.877746],\n                            [116.363724, 24.87123],\n                            [116.345862, 24.828872],\n                            [116.297202, 24.801712],\n                            [116.244232, 24.793563],\n                            [116.251007, 24.82507],\n                            [116.221442, 24.829959],\n                            [116.191877, 24.877203],\n                            [116.153073, 24.846795],\n                            [116.068073, 24.850053],\n                            [116.015102, 24.905975],\n                            [115.985537, 24.899461],\n                            [115.907929, 24.923343],\n                            [115.89253, 24.936911],\n                            [115.885139, 24.898918],\n                            [115.907313, 24.879917],\n                            [115.861733, 24.863629],\n                            [115.863581, 24.891318],\n                            [115.824161, 24.909232],\n                            [115.807531, 24.862543],\n                            [115.790284, 24.856027],\n                            [115.764415, 24.791933],\n                            [115.776734, 24.774546],\n                            [115.756408, 24.749004],\n                            [115.769342, 24.708236],\n                            [115.801371, 24.705517],\n                            [115.780429, 24.663103],\n                            [115.797676, 24.628834],\n                            [115.840791, 24.584217],\n                            [115.843871, 24.562446],\n                            [115.785357, 24.567345],\n                            [115.752712, 24.546116],\n                            [115.68927, 24.545027],\n                            [115.671408, 24.604895],\n                            [115.605503, 24.62557],\n                            [115.569778, 24.622306],\n                            [115.555611, 24.683768],\n                            [115.522967, 24.702799],\n                            [115.476771, 24.762591],\n                            [115.412714, 24.79302],\n                            [115.372678, 24.774546],\n                            [115.358511, 24.735416],\n                            [115.306772, 24.758787],\n                            [115.269816, 24.749548],\n                            [115.258729, 24.728894],\n                            [115.1842, 24.711498],\n                            [115.104744, 24.667997],\n                            [115.083802, 24.699537],\n                            [115.057317, 24.703343],\n                            [115.024672, 24.669085],\n                            [115.00373, 24.679418],\n                            [114.940288, 24.650049],\n                            [114.909491, 24.661471],\n                            [114.893477, 24.582584],\n                            [114.868839, 24.562446],\n                            [114.846665, 24.602719],\n                            [114.827571, 24.588026],\n                            [114.781376, 24.613057],\n                            [114.729637, 24.608704],\n                            [114.73826, 24.565168],\n                            [114.704999, 24.525973],\n                            [114.664963, 24.583673],\n                            [114.627391, 24.576598],\n                            [114.589819, 24.537406],\n                            [114.534384, 24.559181],\n                            [114.429058, 24.48622],\n                            [114.403189, 24.497657],\n                            [114.391486, 24.563535],\n                            [114.363769, 24.582584],\n                            [114.300943, 24.578775],\n                            [114.289856, 24.619042],\n                            [114.258443, 24.641346],\n                            [114.19069, 24.656576],\n                            [114.169132, 24.689749],\n                            [114.27261, 24.700624],\n                            [114.281849, 24.724001],\n                            [114.336052, 24.749004],\n                            [114.342211, 24.807145],\n                            [114.378551, 24.861457],\n                            [114.403189, 24.877746],\n                            [114.395798, 24.951019],\n                            [114.454928, 24.977062],\n                            [114.45616, 24.99659],\n                            [114.506051, 24.999844],\n                            [114.532536, 25.022623],\n                            [114.561485, 25.077382],\n                            [114.604601, 25.083886],\n                            [114.640326, 25.074129],\n                            [114.664963, 25.10123],\n                            [114.735796, 25.121822],\n                            [114.73518, 25.155954],\n                            [114.685905, 25.173287],\n                            [114.693912, 25.213902],\n                            [114.73518, 25.225813],\n                            [114.743188, 25.274528],\n                            [114.714238, 25.315651],\n                            [114.63663, 25.324306],\n                            [114.599674, 25.385959],\n                            [114.541159, 25.416773],\n                            [114.477718, 25.37136],\n                            [114.438914, 25.376226],\n                            [114.43029, 25.343779],\n                            [114.382863, 25.317274],\n                            [114.31511, 25.33837],\n                            [114.2954, 25.299961],\n                            [114.260291, 25.291845],\n                            [114.204857, 25.29942],\n                            [114.190074, 25.316733],\n                            [114.115545, 25.302125],\n                            [114.083517, 25.275611],\n                            [114.055799, 25.277775],\n                            [114.039785, 25.250714],\n                            [114.017611, 25.273987],\n                            [114.029314, 25.328093],\n                            [114.050256, 25.36433],\n                            [113.983118, 25.415152],\n                            [114.003444, 25.442716],\n                            [113.94493, 25.441635],\n                            [113.887032, 25.436772],\n                            [113.877177, 25.380552],\n                            [113.839605, 25.363248],\n                            [113.814967, 25.328634],\n                            [113.76446, 25.333502],\n                            [113.753373, 25.362707],\n                            [113.686852, 25.351891],\n                            [113.680076, 25.334584],\n                            [113.611707, 25.327552],\n                            [113.584606, 25.306453],\n                            [113.579062, 25.34432],\n                            [113.535946, 25.368656],\n                            [113.479896, 25.375145],\n                            [113.449715, 25.359463],\n                            [113.407215, 25.401637],\n                            [113.373338, 25.402719],\n                            [113.341926, 25.448661],\n                            [113.314208, 25.442716],\n                            [113.311129, 25.490264],\n                            [113.248919, 25.514031],\n                            [113.226129, 25.50971],\n                            [113.176854, 25.471355],\n                            [113.11834, 25.445418],\n                            [113.131274, 25.414611],\n                            [113.096782, 25.412449],\n                            [113.078304, 25.382174],\n                            [113.013014, 25.352432],\n                            [112.969898, 25.350269],\n                            [112.93479, 25.325929],\n                            [112.924319, 25.296714],\n                            [112.891058, 25.339993],\n                            [112.854718, 25.337829],\n                            [112.867036, 25.249632],\n                            [112.897833, 25.238264],\n                            [112.958195, 25.254503],\n                            [112.992688, 25.247467],\n                            [113.034572, 25.198199],\n                            [112.97421, 25.168412],\n                            [112.96805, 25.141869],\n                            [113.004391, 25.089306],\n                            [112.979137, 25.03401],\n                            [113.009934, 24.977604],\n                            [112.994536, 24.927142],\n                            [112.941565, 24.915745],\n                            [112.904609, 24.921715],\n                            [112.873812, 24.896747],\n                            [112.780805, 24.896747],\n                            [112.778341, 24.947764],\n                            [112.743233, 24.959701],\n                            [112.742001, 24.99876],\n                            [112.714899, 25.025876],\n                            [112.712436, 25.083344],\n                            [112.660081, 25.132658],\n                            [112.628052, 25.140785],\n                            [112.562762, 25.124531],\n                            [112.458053, 25.152162],\n                            [112.44327, 25.185744],\n                            [112.414937, 25.14241],\n                            [112.365046, 25.191701],\n                            [112.315771, 25.175453],\n                            [112.302836, 25.157037],\n                            [112.256025, 25.159204],\n                            [112.246785, 25.185202],\n                            [112.187039, 25.182494],\n                            [112.177184, 25.106649],\n                            [112.151931, 25.055698],\n                            [112.155626, 25.026419],\n                            [112.12175, 24.989538],\n                            [112.119902, 24.963499],\n                            [112.175337, 24.927685],\n                            [112.167329, 24.859828],\n                            [112.149467, 24.837019],\n                            [112.124214, 24.841364],\n                            [112.03367, 24.771286],\n                            [112.024431, 24.740308],\n                            [111.961606, 24.721283],\n                            [111.939432, 24.686487],\n                            [111.953598, 24.64733],\n                            [111.927729, 24.629378],\n                            [111.936968, 24.595645],\n                            [111.972077, 24.578775],\n                            [112.007185, 24.534684],\n                            [112.009649, 24.503103],\n                            [111.985011, 24.467701],\n                            [112.025047, 24.438828],\n                            [112.057692, 24.387057],\n                            [112.05954, 24.339628],\n                            [112.026279, 24.294908],\n                            [111.990555, 24.279634],\n                            [111.986243, 24.25672],\n                            [111.958526, 24.263813],\n                            [111.912946, 24.221795],\n                            [111.877222, 24.227252],\n                            [111.871062, 24.176487],\n                            [111.886461, 24.163929],\n                            [111.878454, 24.109862],\n                            [111.92157, 24.012045],\n                            [111.940664, 23.987989],\n                            [111.911714, 23.943693],\n                            [111.854432, 23.947521],\n                            [111.845809, 23.904305],\n                            [111.812548, 23.887343],\n                            [111.824867, 23.832612],\n                            [111.8107, 23.80688],\n                            [111.722621, 23.823305],\n                            [111.683201, 23.822758],\n                            [111.683201, 23.822758],\n                            [111.654868, 23.833159],\n                            [111.627766, 23.78881],\n                            [111.621607, 23.725819],\n                            [111.666571, 23.718696],\n                            [111.614832, 23.65896],\n                            [111.615448, 23.639225],\n                            [111.555702, 23.64087],\n                            [111.487332, 23.626615],\n                            [111.479941, 23.532822],\n                            [111.428818, 23.466414],\n                            [111.399869, 23.469159],\n                            [111.383239, 23.399423],\n                            [111.389398, 23.375804],\n                            [111.363528, 23.340641],\n                            [111.376463, 23.30437],\n                            [111.353058, 23.284582],\n                            [111.36476, 23.240047],\n                            [111.388782, 23.210349],\n                            [111.38447, 23.16744],\n                            [111.365992, 23.14488],\n                            [111.377695, 23.082132],\n                            [111.402333, 23.066165],\n                            [111.43313, 23.073322],\n                            [111.433746, 23.036428],\n                            [111.389398, 23.005583],\n                            [111.403565, 22.99126],\n                            [111.362913, 22.967568],\n                            [111.374615, 22.938361],\n                            [111.358601, 22.889301],\n                            [111.218167, 22.748085],\n                            [111.185522, 22.735942],\n                            [111.118385, 22.744773],\n                            [111.058023, 22.729871],\n                            [111.089435, 22.695643],\n                            [111.055559, 22.648705],\n                            [110.997045, 22.631582],\n                            [110.958856, 22.636553],\n                            [110.950233, 22.61059],\n                            [110.896031, 22.613352],\n                            [110.897878, 22.591805],\n                            [110.812263, 22.576333],\n                            [110.778386, 22.585174],\n                            [110.749437, 22.556991],\n                            [110.762988, 22.518298],\n                            [110.740198, 22.498947],\n                            [110.74143, 22.464109],\n                            [110.688459, 22.477935],\n                            [110.712481, 22.440879],\n                            [110.711249, 22.369506],\n                            [110.74143, 22.361757],\n                            [110.749437, 22.329653],\n                            [110.787009, 22.28259],\n                            [110.759292, 22.274837],\n                            [110.725415, 22.29588],\n                            [110.687843, 22.249914],\n                            [110.646575, 22.220554],\n                            [110.678604, 22.172901],\n                            [110.629329, 22.149068],\n                            [110.598532, 22.162924],\n                            [110.602843, 22.18343],\n                            [110.55788, 22.196175],\n                            [110.505525, 22.14297],\n                            [110.456866, 22.189526],\n                            [110.414366, 22.208365],\n                            [110.378026, 22.164587],\n                            [110.34846, 22.195621],\n                            [110.326287, 22.152393],\n                            [110.364475, 22.125785],\n                            [110.35154, 22.097508],\n                            [110.359547, 22.015973],\n                            [110.352772, 21.97602],\n                            [110.374946, 21.967695],\n                            [110.374946, 21.967695],\n                            [110.378642, 21.939942],\n                            [110.378642, 21.939942],\n                            [110.391576, 21.89386],\n                            [110.337374, 21.887751],\n                            [110.290562, 21.917736],\n                            [110.283787, 21.892194],\n                            [110.224041, 21.882198],\n                            [110.224041, 21.882198],\n                            [110.212338, 21.886085],\n                            [110.212338, 21.886085],\n                            [110.196323, 21.899968],\n                            [110.12857, 21.902744],\n                            [110.101469, 21.86998],\n                            [110.050962, 21.857205],\n                            [109.999839, 21.881643],\n                            [109.94502, 21.84443],\n                            [109.940093, 21.769419],\n                            [109.916071, 21.668787],\n                            [109.888354, 21.652101],\n                            [109.888354, 21.652101],\n                            [109.839695, 21.636525],\n                            [109.786108, 21.637638],\n                            [109.778101, 21.670455],\n                            [109.742992, 21.616497],\n                            [109.754695, 21.556396],\n                            [109.788572, 21.490702],\n                            [109.785492, 21.45673],\n                            [109.819369, 21.445033],\n                            [109.894513, 21.442248],\n                            [109.904368, 21.429992],\n                            [109.868644, 21.365913],\n                            [109.770709, 21.359783],\n                            [109.757775, 21.346963],\n                            [109.763934, 21.226514],\n                            [109.674623, 21.136671],\n                            [109.674007, 21.067997],\n                            [109.655529, 20.929435],\n                            [109.664768, 20.862343],\n                            [109.711579, 20.774519],\n                            [109.730057, 20.719673],\n                            [109.74484, 20.621124],\n                            [109.793499, 20.615522],\n                            [109.813825, 20.574627],\n                            [109.811977, 20.541566],\n                            [109.839695, 20.489439],\n                            [109.888354, 20.475423],\n                            [109.895745, 20.42776],\n                            [109.864948, 20.40196],\n                            [109.861252, 20.376717],\n                            [109.916071, 20.316677],\n                            [109.909296, 20.236961],\n                            [109.929006, 20.211691],\n                            [109.993679, 20.254368],\n                            [110.082375, 20.258859],\n                            [110.118099, 20.219553],\n                            [110.168606, 20.219553],\n                            [110.220345, 20.25156],\n                            [110.296722, 20.249314],\n                            [110.349076, 20.258859],\n                            [110.384185, 20.293103],\n                            [110.425453, 20.291419],\n                            [110.452554, 20.311064],\n                            [110.491358, 20.373912],\n                            [110.54125, 20.42047],\n                            [110.550489, 20.47262],\n                            [110.499982, 20.572386],\n                            [110.487047, 20.640167],\n                            [110.466105, 20.680485],\n                            [110.411286, 20.670966],\n                            [110.392192, 20.682724],\n                            [110.407591, 20.731987],\n                            [110.393424, 20.816479],\n                            [110.350924, 20.84165],\n                            [110.327519, 20.847802],\n                            [110.269004, 20.839972],\n                            [110.209874, 20.860106],\n                            [110.184005, 20.891979],\n                            [110.180925, 20.98197],\n                            [110.204947, 21.003202],\n                            [110.208642, 21.050684],\n                            [110.241903, 21.016051],\n                            [110.24991, 21.045098],\n                            [110.296722, 21.093684],\n                            [110.39096, 21.124949],\n                            [110.422373, 21.190807],\n                            [110.451322, 21.186343],\n                            [110.501213, 21.217588],\n                            [110.534474, 21.204198],\n                            [110.626249, 21.215915],\n                            [110.65951, 21.239902],\n                            [110.713097, 21.3124],\n                            [110.768531, 21.364799],\n                            [110.796248, 21.37483],\n                            [110.888639, 21.367585],\n                            [110.929291, 21.375945],\n                            [111.034617, 21.438906],\n                            [111.103602, 21.455616],\n                            [111.171355, 21.458401],\n                            [111.28284, 21.485691],\n                            [111.276065, 21.443362],\n                            [111.250196, 21.45116],\n                            [111.257587, 21.41495],\n                            [111.28592, 21.41885],\n                            [111.353058, 21.464528],\n                            [111.382623, 21.495714],\n                            [111.444217, 21.514088],\n                            [111.494724, 21.501282],\n                            [111.521825, 21.517429],\n                            [111.560629, 21.50518],\n                            [111.609904, 21.530234],\n                            [111.650556, 21.512418],\n                            [111.677658, 21.529677],\n                            [111.693672, 21.590345],\n                            [111.736788, 21.609821],\n                            [111.794686, 21.61149],\n                            [111.832258, 21.578659],\n                            [111.810084, 21.555283],\n                            [111.887693, 21.578659],\n                            [111.941896, 21.607039],\n                            [111.972692, 21.603144],\n                            [112.026895, 21.633744],\n                            [111.997946, 21.657107],\n                            [111.954214, 21.667674],\n                            [111.956062, 21.710494],\n                            [112.036134, 21.761637],\n                            [112.136532, 21.793871],\n                            [112.192583, 21.789425],\n                            [112.196894, 21.736624],\n                            [112.236315, 21.727173],\n                            [112.238778, 21.702153],\n                            [112.353343, 21.707157],\n                            [112.415553, 21.734956],\n                            [112.427256, 21.789981],\n                            [112.445734, 21.803317],\n                            [112.497473, 21.785535],\n                            [112.535661, 21.753856],\n                            [112.647146, 21.758302],\n                            [112.68595, 21.810541],\n                            [112.792508, 21.921067],\n                            [112.841167, 21.920512],\n                            [112.893522, 21.84443],\n                            [112.929862, 21.838875],\n                            [112.989608, 21.869424],\n                            [113.047507, 21.956595],\n                            [113.053666, 22.012089],\n                            [113.032108, 22.04593],\n                            [113.045659, 22.088636],\n                            [113.086927, 22.12634],\n                            [113.091854, 22.065344],\n                            [113.142977, 22.012089],\n                            [113.1516, 21.979905],\n                            [113.235368, 21.887751],\n                            [113.266781, 21.871646],\n                            [113.319752, 21.909407],\n                            [113.330223, 21.96159],\n                            [113.442324, 22.009315],\n                            [113.45957, 22.043711],\n                            [113.527939, 22.073663],\n                            [113.567359, 22.075327],\n                            [113.554425, 22.107489],\n                            [113.554425, 22.142416],\n                            [113.534715, 22.174009],\n                            [113.53841, 22.209473],\n                            [113.558736, 22.212244]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.024627, 23.437865],\n                            [116.982743, 23.460924],\n                            [116.944555, 23.440061],\n                            [116.951946, 23.419744],\n                            [117.027091, 23.41535],\n                            [117.050496, 23.400522],\n                            [117.081909, 23.409309],\n                            [117.124409, 23.389537],\n                            [117.142887, 23.400522],\n                            [117.142887, 23.459826],\n                            [117.129336, 23.483431],\n                            [117.093612, 23.459277],\n                            [117.058503, 23.47355],\n                            [117.029554, 23.443356],\n                            [117.024627, 23.437865]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.853486, 21.740515],\n                            [112.876275, 21.772753],\n                            [112.840551, 21.776644],\n                            [112.782653, 21.739959],\n                            [112.724138, 21.719945],\n                            [112.70566, 21.679354],\n                            [112.734609, 21.666562],\n                            [112.780189, 21.671568],\n                            [112.730914, 21.613715],\n                            [112.775261, 21.564189],\n                            [112.817145, 21.590345],\n                            [112.798667, 21.610933],\n                            [112.821457, 21.655994],\n                            [112.804826, 21.686583],\n                            [112.83316, 21.736624],\n                            [112.853486, 21.740515]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.530733, 21.583667],\n                            [112.563378, 21.591458],\n                            [112.571385, 21.619835],\n                            [112.621277, 21.606482],\n                            [112.665624, 21.642644],\n                            [112.639139, 21.67268],\n                            [112.66624, 21.683803],\n                            [112.663776, 21.714386],\n                            [112.592327, 21.693256],\n                            [112.560299, 21.666562],\n                            [112.57077, 21.645982],\n                            [112.535045, 21.628737],\n                            [112.530733, 21.583667]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.231342, 22.016528],\n                            [114.311414, 22.041493],\n                            [114.302791, 22.050368],\n                            [114.239965, 22.03539],\n                            [114.231342, 22.016528]\n                        ]\n                    ],\n                    [\n                        [\n                            [110.43346, 21.171276],\n                            [110.489511, 21.138904],\n                            [110.508605, 21.140579],\n                            [110.544945, 21.083633],\n                            [110.582517, 21.094801],\n                            [110.632409, 21.210893],\n                            [110.589293, 21.194713],\n                            [110.525235, 21.190249],\n                            [110.499366, 21.213125],\n                            [110.445163, 21.184669],\n                            [110.431612, 21.180763],\n                            [110.43346, 21.171276]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.435263, 21.663781],\n                            [112.456205, 21.648763],\n                            [112.458669, 21.68992],\n                            [112.435263, 21.663781]\n                        ]\n                    ],\n                    [\n                        [\n                            [110.517844, 21.079166],\n                            [110.459946, 21.062971],\n                            [110.398352, 21.096476],\n                            [110.352772, 21.079724],\n                            [110.305961, 21.0881],\n                            [110.27578, 21.033369],\n                            [110.211106, 20.986999],\n                            [110.201251, 20.938378],\n                            [110.309656, 20.963529],\n                            [110.347845, 20.984763],\n                            [110.407591, 20.990351],\n                            [110.47288, 20.983087],\n                            [110.511684, 20.916578],\n                            [110.535706, 20.922727],\n                            [110.539402, 20.987557],\n                            [110.560344, 21.061295],\n                            [110.517844, 21.079166]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.765076, 21.962145],\n                            [113.774315, 21.998218],\n                            [113.74167, 21.991559],\n                            [113.765076, 21.962145]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.723192, 21.922177],\n                            [113.742902, 21.950489],\n                            [113.71888, 21.951599],\n                            [113.723192, 21.922177]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.142977, 21.831653],\n                            [113.162071, 21.853873],\n                            [113.203955, 21.861093],\n                            [113.167615, 21.876644],\n                            [113.136818, 21.868869],\n                            [113.142977, 21.831653]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.819894, 22.396068],\n                            [113.813735, 22.419858],\n                            [113.786634, 22.413773],\n                            [113.819894, 22.396068]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.190074, 21.986564],\n                            [114.229494, 21.995443],\n                            [114.180835, 22.00987],\n                            [114.190074, 21.986564]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.153734, 21.97491],\n                            [114.171596, 22.000437],\n                            [114.124169, 21.985455],\n                            [114.153734, 21.97491]\n                        ]\n                    ],\n                    [\n                        [\n                            [116.769628, 20.771721],\n                            [116.761005, 20.750456],\n                            [116.87249, 20.738143],\n                            [116.889736, 20.683284],\n                            [116.849084, 20.628405],\n                            [116.749302, 20.600958],\n                            [116.796113, 20.582471],\n                            [116.862635, 20.588633],\n                            [116.905135, 20.619443],\n                            [116.934084, 20.676565],\n                            [116.925461, 20.726949],\n                            [116.88604, 20.775638],\n                            [116.820135, 20.780674],\n                            [116.769628, 20.771721]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.025333, 21.847762],\n                            [113.045659, 21.882753],\n                            [113.007471, 21.869424],\n                            [113.025333, 21.847762]\n                        ]\n                    ],\n                    [\n                        [\n                            [110.405127, 20.678245],\n                            [110.437772, 20.677685],\n                            [110.414366, 20.710157],\n                            [110.405127, 20.678245]\n                        ]\n                    ],\n                    [\n                        [\n                            [110.644727, 20.935584],\n                            [110.584365, 20.948998],\n                            [110.548641, 20.908752],\n                            [110.562807, 20.861224],\n                            [110.611467, 20.860106],\n                            [110.646575, 20.917137],\n                            [110.644727, 20.935584]\n                        ]\n                    ],\n                    [\n                        [\n                            [110.556648, 20.32734],\n                            [110.593604, 20.360447],\n                            [110.586213, 20.381205],\n                            [110.556648, 20.32734]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.943037, 21.097592],\n                            [115.953508, 21.064088],\n                            [115.989233, 21.035603],\n                            [116.040356, 21.02052],\n                            [116.067457, 21.04063],\n                            [116.044051, 21.110434],\n                            [116.024341, 21.12439],\n                            [115.965211, 21.123832],\n                            [115.943037, 21.097592]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.926407, 20.981411],\n                            [115.939342, 20.945644],\n                            [115.970139, 20.919373],\n                            [115.999088, 20.922727],\n                            [116.000936, 20.948439],\n                            [115.954124, 20.99985],\n                            [115.926407, 20.981411]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.834632, 22.722695],\n                            [115.834632, 22.722143],\n                            [115.835248, 22.722695],\n                            [115.834632, 22.722695]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.834632, 22.723247],\n                            [115.834632, 22.722695],\n                            [115.835248, 22.722695],\n                            [115.834632, 22.723247]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 450000,\n                \"name\": \"Guangxi\",\n                \"center\": [108.320004, 22.82402],\n                \"centroid\": [108.7944, 23.833381],\n                \"childrenNum\": 14,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 19,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [109.48245, 26.029788],\n                            [109.473211, 26.006663],\n                            [109.408537, 25.967392],\n                            [109.435022, 25.93349],\n                            [109.396834, 25.900117],\n                            [109.359262, 25.836036],\n                            [109.339552, 25.83442],\n                            [109.327849, 25.76168],\n                            [109.340168, 25.731493],\n                            [109.296436, 25.71424],\n                            [109.207125, 25.740119],\n                            [109.206509, 25.788087],\n                            [109.147995, 25.741736],\n                            [109.13198, 25.762758],\n                            [109.143683, 25.795092],\n                            [109.095024, 25.80533],\n                            [109.077778, 25.776771],\n                            [109.048213, 25.790781],\n                            [108.989698, 25.778926],\n                            [108.999553, 25.765453],\n                            [108.963829, 25.732572],\n                            [108.940423, 25.740119],\n                            [108.896076, 25.71424],\n                            [108.900387, 25.682423],\n                            [108.953974, 25.686738],\n                            [108.953974, 25.686738],\n                            [109.007561, 25.734728],\n                            [109.043285, 25.738502],\n                            [109.07901, 25.72071],\n                            [109.075314, 25.693749],\n                            [109.030966, 25.629556],\n                            [109.051908, 25.566949],\n                            [109.088249, 25.550752],\n                            [109.024807, 25.51241],\n                            [108.949046, 25.557231],\n                            [108.8893, 25.543193],\n                            [108.890532, 25.556151],\n                            [108.826474, 25.550212],\n                            [108.814772, 25.526992],\n                            [108.781511, 25.554531],\n                            [108.799989, 25.576666],\n                            [108.783975, 25.628477],\n                            [108.724844, 25.634952],\n                            [108.68912, 25.623081],\n                            [108.68604, 25.587462],\n                            [108.660787, 25.584763],\n                            [108.658323, 25.550212],\n                            [108.68912, 25.533473],\n                            [108.634917, 25.520512],\n                            [108.6072, 25.491885],\n                            [108.600425, 25.432448],\n                            [108.62999, 25.335666],\n                            [108.625062, 25.308076],\n                            [108.589338, 25.335125],\n                            [108.585642, 25.365952],\n                            [108.471693, 25.458928],\n                            [108.418723, 25.443257],\n                            [108.400244, 25.491344],\n                            [108.359592, 25.513491],\n                            [108.348506, 25.536173],\n                            [108.308469, 25.525912],\n                            [108.280752, 25.48],\n                            [108.241332, 25.46217],\n                            [108.251803, 25.430286],\n                            [108.192673, 25.458928],\n                            [108.162492, 25.444878],\n                            [108.193289, 25.405421],\n                            [108.142782, 25.390825],\n                            [108.152021, 25.324306],\n                            [108.143398, 25.269658],\n                            [108.115065, 25.210112],\n                            [108.080572, 25.193867],\n                            [108.001732, 25.196574],\n                            [107.928435, 25.155954],\n                            [107.872384, 25.141327],\n                            [107.839124, 25.115861],\n                            [107.762747, 25.125073],\n                            [107.789233, 25.15487],\n                            [107.760283, 25.188451],\n                            [107.762131, 25.229061],\n                            [107.741805, 25.24043],\n                            [107.700537, 25.194408],\n                            [107.696226, 25.219858],\n                            [107.661733, 25.258833],\n                            [107.659885, 25.316192],\n                            [107.632168, 25.310241],\n                            [107.599523, 25.250714],\n                            [107.576734, 25.256668],\n                            [107.512676, 25.209029],\n                            [107.472024, 25.213902],\n                            [107.489886, 25.276693],\n                            [107.481263, 25.299961],\n                            [107.432604, 25.289139],\n                            [107.409198, 25.347024],\n                            [107.420901, 25.392987],\n                            [107.375937, 25.411908],\n                            [107.358691, 25.393528],\n                            [107.318039, 25.401637],\n                            [107.308184, 25.432988],\n                            [107.336517, 25.461089],\n                            [107.263836, 25.543193],\n                            [107.232423, 25.556691],\n                            [107.228728, 25.604733],\n                            [107.205322, 25.607971],\n                            [107.185612, 25.578825],\n                            [107.064272, 25.559391],\n                            [107.066736, 25.50917],\n                            [107.015613, 25.495666],\n                            [106.996519, 25.442716],\n                            [106.963874, 25.437852],\n                            [106.987896, 25.358922],\n                            [107.012533, 25.352973],\n                            [107.013765, 25.275611],\n                            [106.975577, 25.232851],\n                            [106.933077, 25.250714],\n                            [106.904128, 25.231768],\n                            [106.888113, 25.181953],\n                            [106.853005, 25.186827],\n                            [106.787715, 25.17112],\n                            [106.764926, 25.183036],\n                            [106.732281, 25.162454],\n                            [106.691013, 25.179245],\n                            [106.644817, 25.164621],\n                            [106.63989, 25.132658],\n                            [106.590615, 25.08768],\n                            [106.551195, 25.082802],\n                            [106.519782, 25.054072],\n                            [106.450181, 25.033468],\n                            [106.442173, 25.019369],\n                            [106.332536, 24.988454],\n                            [106.304819, 24.973807],\n                            [106.253696, 24.971094],\n                            [106.215508, 24.981944],\n                            [106.191486, 24.95319],\n                            [106.145291, 24.954275],\n                            [106.197645, 24.885889],\n                            [106.206269, 24.851139],\n                            [106.173008, 24.760417],\n                            [106.150218, 24.762591],\n                            [106.113878, 24.714216],\n                            [106.047356, 24.684312],\n                            [106.024566, 24.633186],\n                            [105.961741, 24.677786],\n                            [105.942031, 24.725088],\n                            [105.863806, 24.729437],\n                            [105.827466, 24.702799],\n                            [105.767104, 24.719109],\n                            [105.70551, 24.768569],\n                            [105.617431, 24.78161],\n                            [105.607576, 24.803885],\n                            [105.573083, 24.797366],\n                            [105.497322, 24.809318],\n                            [105.493011, 24.833217],\n                            [105.457286, 24.87123],\n                            [105.428337, 24.930941],\n                            [105.365511, 24.943423],\n                            [105.334099, 24.9266],\n                            [105.267577, 24.929313],\n                            [105.251563, 24.967296],\n                            [105.212758, 24.995505],\n                            [105.178266, 24.985199],\n                            [105.157324, 24.958616],\n                            [105.131454, 24.959701],\n                            [105.09573, 24.92877],\n                            [105.096346, 24.928228],\n                            [105.082179, 24.915745],\n                            [105.077868, 24.918459],\n                            [105.039064, 24.872859],\n                            [105.026745, 24.815836],\n                            [105.03352, 24.787586],\n                            [104.899245, 24.752809],\n                            [104.865985, 24.730524],\n                            [104.841963, 24.676155],\n                            [104.771746, 24.659839],\n                            [104.729246, 24.617953],\n                            [104.703377, 24.645698],\n                            [104.628848, 24.660927],\n                            [104.595587, 24.709323],\n                            [104.529682, 24.731611],\n                            [104.489646, 24.653313],\n                            [104.520443, 24.535228],\n                            [104.550008, 24.518894],\n                            [104.575877, 24.424661],\n                            [104.616529, 24.421937],\n                            [104.63008, 24.397958],\n                            [104.610986, 24.377246],\n                            [104.641783, 24.367979],\n                            [104.70892, 24.321087],\n                            [104.721239, 24.340173],\n                            [104.703377, 24.419757],\n                            [104.715695, 24.441552],\n                            [104.74834, 24.435559],\n                            [104.765587, 24.45953],\n                            [104.784681, 24.443732],\n                            [104.83642, 24.446456],\n                            [104.914028, 24.426296],\n                            [104.930042, 24.411038],\n                            [104.979933, 24.412673],\n                            [105.042759, 24.442097],\n                            [105.106817, 24.414853],\n                            [105.111744, 24.37234],\n                            [105.138846, 24.376701],\n                            [105.188121, 24.347261],\n                            [105.196744, 24.326541],\n                            [105.164715, 24.288362],\n                            [105.215222, 24.214699],\n                            [105.24294, 24.208695],\n                            [105.229389, 24.165567],\n                            [105.182577, 24.167205],\n                            [105.20044, 24.105491],\n                            [105.260186, 24.061236],\n                            [105.292831, 24.074896],\n                            [105.273121, 24.092927],\n                            [105.320548, 24.116416],\n                            [105.334099, 24.094566],\n                            [105.395692, 24.065607],\n                            [105.406163, 24.043748],\n                            [105.493011, 24.016965],\n                            [105.533663, 24.130071],\n                            [105.594641, 24.137718],\n                            [105.628518, 24.126794],\n                            [105.649459, 24.032816],\n                            [105.704278, 24.0667],\n                            [105.739387, 24.059596],\n                            [105.765256, 24.073804],\n                            [105.802212, 24.051945],\n                            [105.796669, 24.023524],\n                            [105.841633, 24.03063],\n                            [105.859495, 24.056864],\n                            [105.89214, 24.040468],\n                            [105.908154, 24.069432],\n                            [105.901995, 24.099482],\n                            [105.919241, 24.122425],\n                            [105.963589, 24.110954],\n                            [105.998081, 24.120786],\n                            [106.011632, 24.099482],\n                            [106.04982, 24.089649],\n                            [106.053516, 24.051399],\n                            [106.096631, 24.018058],\n                            [106.091088, 23.998924],\n                            [106.128044, 23.956819],\n                            [106.157609, 23.891174],\n                            [106.192718, 23.879135],\n                            [106.173008, 23.861622],\n                            [106.192102, 23.824947],\n                            [106.136667, 23.795381],\n                            [106.157609, 23.724175],\n                            [106.149602, 23.665538],\n                            [106.120653, 23.605229],\n                            [106.141595, 23.569579],\n                            [106.08616, 23.524043],\n                            [106.071994, 23.495506],\n                            [106.039965, 23.484529],\n                            [105.999929, 23.447748],\n                            [105.986378, 23.489469],\n                            [105.935871, 23.508678],\n                            [105.913081, 23.499348],\n                            [105.89214, 23.52514],\n                            [105.852103, 23.526786],\n                            [105.815763, 23.507031],\n                            [105.805908, 23.467512],\n                            [105.758481, 23.459826],\n                            [105.699966, 23.40162],\n                            [105.637757, 23.404366],\n                            [105.694423, 23.363168],\n                            [105.699966, 23.327453],\n                            [105.649459, 23.346136],\n                            [105.593409, 23.312614],\n                            [105.560148, 23.257093],\n                            [105.526272, 23.234548],\n                            [105.542902, 23.184495],\n                            [105.558916, 23.177893],\n                            [105.574931, 23.066165],\n                            [105.625438, 23.064513],\n                            [105.648844, 23.078828],\n                            [105.724604, 23.06231],\n                            [105.74185, 23.030921],\n                            [105.780039, 23.022659],\n                            [105.805908, 22.994565],\n                            [105.839169, 22.987403],\n                            [105.879205, 22.916865],\n                            [105.893987, 22.936707],\n                            [105.959277, 22.948832],\n                            [105.994385, 22.93781],\n                            [106.019639, 22.990709],\n                            [106.08616, 22.996218],\n                            [106.106486, 22.980792],\n                            [106.153914, 22.988505],\n                            [106.206885, 22.978588],\n                            [106.270326, 22.907494],\n                            [106.258007, 22.889852],\n                            [106.286957, 22.867245],\n                            [106.366413, 22.857871],\n                            [106.37134, 22.878273],\n                            [106.41384, 22.877171],\n                            [106.504383, 22.91025],\n                            [106.525941, 22.946628],\n                            [106.562282, 22.923479],\n                            [106.606013, 22.925684],\n                            [106.631267, 22.88103],\n                            [106.657136, 22.863385],\n                            [106.674998, 22.891506],\n                            [106.716882, 22.881582],\n                            [106.709491, 22.866142],\n                            [106.774781, 22.812643],\n                            [106.776012, 22.813746],\n                            [106.778476, 22.814298],\n                            [106.779092, 22.813746],\n                            [106.779708, 22.813195],\n                            [106.78094, 22.813195],\n                            [106.784636, 22.812643],\n                            [106.796338, 22.812091],\n                            [106.801882, 22.815401],\n                            [106.804346, 22.816505],\n                            [106.808657, 22.817608],\n                            [106.813585, 22.817608],\n                            [106.838838, 22.803265],\n                            [106.820976, 22.768504],\n                            [106.768621, 22.739254],\n                            [106.780324, 22.708894],\n                            [106.756302, 22.68957],\n                            [106.711955, 22.575228],\n                            [106.650361, 22.575228],\n                            [106.61402, 22.602303],\n                            [106.585071, 22.517192],\n                            [106.588151, 22.472958],\n                            [106.560434, 22.455813],\n                            [106.588767, 22.374486],\n                            [106.562897, 22.345706],\n                            [106.663296, 22.33076],\n                            [106.670071, 22.283144],\n                            [106.688549, 22.260438],\n                            [106.7021, 22.207257],\n                            [106.673151, 22.182322],\n                            [106.706411, 22.160707],\n                            [106.691629, 22.13521],\n                            [106.71565, 22.089745],\n                            [106.706411, 22.021521],\n                            [106.683006, 21.999882],\n                            [106.698404, 21.959925],\n                            [106.73844, 22.008205],\n                            [106.790179, 22.004876],\n                            [106.802498, 21.98157],\n                            [106.859164, 21.986009],\n                            [106.926302, 21.967695],\n                            [106.935541, 21.933836],\n                            [106.974345, 21.923288],\n                            [106.999598, 21.947714],\n                            [107.05996, 21.914959],\n                            [107.058729, 21.887196],\n                            [107.018693, 21.859427],\n                            [107.018077, 21.81943],\n                            [107.093837, 21.803317],\n                            [107.148656, 21.758858],\n                            [107.194851, 21.736624],\n                            [107.199163, 21.718833],\n                            [107.242279, 21.703265],\n                            [107.271844, 21.727173],\n                            [107.310648, 21.733844],\n                            [107.356843, 21.667674],\n                            [107.363619, 21.602031],\n                            [107.388256, 21.594241],\n                            [107.431372, 21.642088],\n                            [107.477567, 21.659888],\n                            [107.500973, 21.613715],\n                            [107.486806, 21.59591],\n                            [107.547168, 21.58645],\n                            [107.584741, 21.614828],\n                            [107.603219, 21.597579],\n                            [107.712856, 21.616497],\n                            [107.807711, 21.655438],\n                            [107.837892, 21.640419],\n                            [107.863761, 21.650988],\n                            [107.892095, 21.622617],\n                            [107.893942, 21.596466],\n                            [107.929051, 21.585893],\n                            [107.958, 21.534131],\n                            [108.034376, 21.545821],\n                            [108.108289, 21.508521],\n                            [108.193905, 21.519656],\n                            [108.156332, 21.55083],\n                            [108.205608, 21.597579],\n                            [108.241332, 21.599805],\n                            [108.249955, 21.561406],\n                            [108.210535, 21.505737],\n                            [108.230245, 21.491259],\n                            [108.330027, 21.540254],\n                            [108.397781, 21.533017],\n                            [108.492635, 21.554727],\n                            [108.591802, 21.677129],\n                            [108.626294, 21.67991],\n                            [108.658939, 21.643757],\n                            [108.678033, 21.659331],\n                            [108.735931, 21.628181],\n                            [108.734084, 21.626512],\n                            [108.745786, 21.602587],\n                            [108.801837, 21.626512],\n                            [108.83325, 21.610933],\n                            [108.881293, 21.627068],\n                            [108.937959, 21.589789],\n                            [109.093792, 21.579215],\n                            [109.09872, 21.571424],\n                            [109.110422, 21.568085],\n                            [109.138756, 21.567528],\n                            [109.142451, 21.511861],\n                            [109.074698, 21.489589],\n                            [109.039589, 21.457844],\n                            [109.046365, 21.424421],\n                            [109.095024, 21.419407],\n                            [109.138756, 21.388762],\n                            [109.186183, 21.390991],\n                            [109.245929, 21.425536],\n                            [109.41716, 21.438906],\n                            [109.484914, 21.453388],\n                            [109.529877, 21.437234],\n                            [109.540964, 21.466199],\n                            [109.576689, 21.493487],\n                            [109.604406, 21.523553],\n                            [109.612413, 21.556953],\n                            [109.654913, 21.493487],\n                            [109.704188, 21.462857],\n                            [109.785492, 21.45673],\n                            [109.788572, 21.490702],\n                            [109.754695, 21.556396],\n                            [109.742992, 21.616497],\n                            [109.778101, 21.670455],\n                            [109.786108, 21.637638],\n                            [109.839695, 21.636525],\n                            [109.888354, 21.652101],\n                            [109.888354, 21.652101],\n                            [109.916071, 21.668787],\n                            [109.940093, 21.769419],\n                            [109.94502, 21.84443],\n                            [109.999839, 21.881643],\n                            [110.050962, 21.857205],\n                            [110.101469, 21.86998],\n                            [110.12857, 21.902744],\n                            [110.196323, 21.899968],\n                            [110.212338, 21.886085],\n                            [110.212338, 21.886085],\n                            [110.224041, 21.882198],\n                            [110.224041, 21.882198],\n                            [110.283787, 21.892194],\n                            [110.290562, 21.917736],\n                            [110.337374, 21.887751],\n                            [110.391576, 21.89386],\n                            [110.378642, 21.939942],\n                            [110.378642, 21.939942],\n                            [110.374946, 21.967695],\n                            [110.374946, 21.967695],\n                            [110.352772, 21.97602],\n                            [110.359547, 22.015973],\n                            [110.35154, 22.097508],\n                            [110.364475, 22.125785],\n                            [110.326287, 22.152393],\n                            [110.34846, 22.195621],\n                            [110.378026, 22.164587],\n                            [110.414366, 22.208365],\n                            [110.456866, 22.189526],\n                            [110.505525, 22.14297],\n                            [110.55788, 22.196175],\n                            [110.602843, 22.18343],\n                            [110.598532, 22.162924],\n                            [110.629329, 22.149068],\n                            [110.678604, 22.172901],\n                            [110.646575, 22.220554],\n                            [110.687843, 22.249914],\n                            [110.725415, 22.29588],\n                            [110.759292, 22.274837],\n                            [110.787009, 22.28259],\n                            [110.749437, 22.329653],\n                            [110.74143, 22.361757],\n                            [110.711249, 22.369506],\n                            [110.712481, 22.440879],\n                            [110.688459, 22.477935],\n                            [110.74143, 22.464109],\n                            [110.740198, 22.498947],\n                            [110.762988, 22.518298],\n                            [110.749437, 22.556991],\n                            [110.778386, 22.585174],\n                            [110.812263, 22.576333],\n                            [110.897878, 22.591805],\n                            [110.896031, 22.613352],\n                            [110.950233, 22.61059],\n                            [110.958856, 22.636553],\n                            [110.997045, 22.631582],\n                            [111.055559, 22.648705],\n                            [111.089435, 22.695643],\n                            [111.058023, 22.729871],\n                            [111.118385, 22.744773],\n                            [111.185522, 22.735942],\n                            [111.218167, 22.748085],\n                            [111.358601, 22.889301],\n                            [111.374615, 22.938361],\n                            [111.362913, 22.967568],\n                            [111.403565, 22.99126],\n                            [111.389398, 23.005583],\n                            [111.433746, 23.036428],\n                            [111.43313, 23.073322],\n                            [111.402333, 23.066165],\n                            [111.377695, 23.082132],\n                            [111.365992, 23.14488],\n                            [111.38447, 23.16744],\n                            [111.388782, 23.210349],\n                            [111.36476, 23.240047],\n                            [111.353058, 23.284582],\n                            [111.376463, 23.30437],\n                            [111.363528, 23.340641],\n                            [111.389398, 23.375804],\n                            [111.383239, 23.399423],\n                            [111.399869, 23.469159],\n                            [111.428818, 23.466414],\n                            [111.479941, 23.532822],\n                            [111.487332, 23.626615],\n                            [111.555702, 23.64087],\n                            [111.615448, 23.639225],\n                            [111.614832, 23.65896],\n                            [111.666571, 23.718696],\n                            [111.621607, 23.725819],\n                            [111.627766, 23.78881],\n                            [111.654868, 23.833159],\n                            [111.683201, 23.822758],\n                            [111.683201, 23.822758],\n                            [111.722621, 23.823305],\n                            [111.8107, 23.80688],\n                            [111.824867, 23.832612],\n                            [111.812548, 23.887343],\n                            [111.845809, 23.904305],\n                            [111.854432, 23.947521],\n                            [111.911714, 23.943693],\n                            [111.940664, 23.987989],\n                            [111.92157, 24.012045],\n                            [111.878454, 24.109862],\n                            [111.886461, 24.163929],\n                            [111.871062, 24.176487],\n                            [111.877222, 24.227252],\n                            [111.912946, 24.221795],\n                            [111.958526, 24.263813],\n                            [111.986243, 24.25672],\n                            [111.990555, 24.279634],\n                            [112.026279, 24.294908],\n                            [112.05954, 24.339628],\n                            [112.057692, 24.387057],\n                            [112.025047, 24.438828],\n                            [111.985011, 24.467701],\n                            [112.009649, 24.503103],\n                            [112.007185, 24.534684],\n                            [111.972077, 24.578775],\n                            [111.936968, 24.595645],\n                            [111.927729, 24.629378],\n                            [111.953598, 24.64733],\n                            [111.939432, 24.686487],\n                            [111.961606, 24.721283],\n                            [112.024431, 24.740308],\n                            [111.951135, 24.769655],\n                            [111.929577, 24.75607],\n                            [111.875374, 24.756613],\n                            [111.868599, 24.771829],\n                            [111.814396, 24.770199],\n                            [111.783599, 24.785957],\n                            [111.708455, 24.788673],\n                            [111.666571, 24.760961],\n                            [111.637621, 24.715303],\n                            [111.641933, 24.684856],\n                            [111.588962, 24.690837],\n                            [111.570484, 24.64461],\n                            [111.526752, 24.637538],\n                            [111.499035, 24.667997],\n                            [111.451608, 24.665822],\n                            [111.431282, 24.687574],\n                            [111.461463, 24.728894],\n                            [111.479325, 24.797366],\n                            [111.449144, 24.857113],\n                            [111.447296, 24.892947],\n                            [111.470086, 24.92877],\n                            [111.434977, 24.951562],\n                            [111.43313, 24.979774],\n                            [111.460231, 24.992793],\n                            [111.467622, 25.02208],\n                            [111.416499, 25.047566],\n                            [111.435593, 25.093642],\n                            [111.375231, 25.128324],\n                            [111.36784, 25.108817],\n                            [111.321645, 25.105023],\n                            [111.274833, 25.151078],\n                            [111.221862, 25.106649],\n                            [111.200921, 25.074672],\n                            [111.139943, 25.042144],\n                            [111.101754, 25.035095],\n                            [111.100522, 24.945593],\n                            [111.009363, 24.921172],\n                            [110.968711, 24.975434],\n                            [110.951465, 25.04377],\n                            [110.98411, 25.101772],\n                            [110.998892, 25.161371],\n                            [111.112841, 25.21715],\n                            [111.103602, 25.285351],\n                            [111.138711, 25.303748],\n                            [111.184906, 25.367034],\n                            [111.210776, 25.363248],\n                            [111.279145, 25.42326],\n                            [111.32842, 25.521592],\n                            [111.324724, 25.564249],\n                            [111.343202, 25.602574],\n                            [111.309942, 25.645203],\n                            [111.30871, 25.720171],\n                            [111.399869, 25.744431],\n                            [111.442369, 25.77192],\n                            [111.43313, 25.84627],\n                            [111.4861, 25.859196],\n                            [111.460231, 25.885042],\n                            [111.383239, 25.881812],\n                            [111.376463, 25.906039],\n                            [111.346282, 25.906577],\n                            [111.297007, 25.874274],\n                            [111.29208, 25.854349],\n                            [111.251428, 25.864581],\n                            [111.230486, 25.916267],\n                            [111.189834, 25.953402],\n                            [111.235413, 26.048071],\n                            [111.267442, 26.058824],\n                            [111.244652, 26.078177],\n                            [111.26621, 26.095914],\n                            [111.258203, 26.151796],\n                            [111.274833, 26.183486],\n                            [111.271754, 26.217316],\n                            [111.293311, 26.222148],\n                            [111.277913, 26.272066],\n                            [111.228022, 26.261333],\n                            [111.204616, 26.276359],\n                            [111.208928, 26.30426],\n                            [111.090667, 26.308016],\n                            [111.008132, 26.336982],\n                            [111.008747, 26.35897],\n                            [110.974255, 26.385778],\n                            [110.94469, 26.373447],\n                            [110.944074, 26.326791],\n                            [110.926212, 26.320354],\n                            [110.939762, 26.286554],\n                            [110.836284, 26.255966],\n                            [110.759292, 26.248451],\n                            [110.73527, 26.270993],\n                            [110.742046, 26.313917],\n                            [110.721104, 26.294066],\n                            [110.673676, 26.317135],\n                            [110.643495, 26.308552],\n                            [110.612083, 26.333764],\n                            [110.584365, 26.296749],\n                            [110.552952, 26.283335],\n                            [110.546793, 26.233421],\n                            [110.495054, 26.166299],\n                            [110.477808, 26.179727],\n                            [110.437772, 26.153945],\n                            [110.373098, 26.088927],\n                            [110.325671, 25.975462],\n                            [110.257301, 25.961473],\n                            [110.24991, 26.010965],\n                            [110.181541, 26.060437],\n                            [110.168606, 26.028713],\n                            [110.100853, 26.020108],\n                            [110.065128, 26.050221],\n                            [110.100853, 26.132455],\n                            [110.099005, 26.168985],\n                            [110.03002, 26.166299],\n                            [109.970274, 26.195301],\n                            [109.904368, 26.135679],\n                            [109.898825, 26.095377],\n                            [109.864332, 26.027637],\n                            [109.814441, 26.041081],\n                            [109.782412, 25.996981],\n                            [109.806434, 25.973848],\n                            [109.826144, 25.911422],\n                            [109.811361, 25.877504],\n                            [109.779333, 25.866196],\n                            [109.768246, 25.890427],\n                            [109.685094, 25.880197],\n                            [109.67955, 25.921649],\n                            [109.693717, 25.959321],\n                            [109.710963, 25.954478],\n                            [109.730057, 25.989988],\n                            [109.649369, 26.016882],\n                            [109.635203, 26.047533],\n                            [109.588391, 26.019571],\n                            [109.560058, 26.021184],\n                            [109.513247, 25.998056],\n                            [109.48245, 26.029788]\n                        ]\n                    ],\n                    [\n                        [\n                            [105.096346, 24.928228],\n                            [105.09573, 24.92877],\n                            [105.077868, 24.918459],\n                            [105.082179, 24.915745],\n                            [105.096346, 24.928228]\n                        ]\n                    ],\n                    [\n                        [\n                            [109.088249, 21.014934],\n                            [109.11227, 21.02499],\n                            [109.117814, 21.017727],\n                            [109.144299, 21.041189],\n                            [109.138756, 21.067439],\n                            [109.09256, 21.057386],\n                            [109.088865, 21.031134],\n                            [109.088249, 21.014934]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 460000,\n                \"name\": \"Hainan\",\n                \"center\": [110.33119, 20.031971],\n                \"centroid\": [109.754859, 19.189767],\n                \"childrenNum\": 19,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 20,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [110.106396, 20.026812],\n                            [110.042339, 19.991384],\n                            [109.997375, 19.980136],\n                            [109.965346, 19.993634],\n                            [109.898825, 19.994196],\n                            [109.855093, 19.984073],\n                            [109.814441, 19.993072],\n                            [109.76147, 19.981261],\n                            [109.712195, 20.017253],\n                            [109.657993, 20.01163],\n                            [109.585312, 19.98801],\n                            [109.526797, 19.943573],\n                            [109.498464, 19.873236],\n                            [109.411001, 19.895184],\n                            [109.349407, 19.898561],\n                            [109.300748, 19.917693],\n                            [109.25948, 19.898561],\n                            [109.255784, 19.867045],\n                            [109.231147, 19.863105],\n                            [109.159082, 19.79048],\n                            [109.169553, 19.736411],\n                            [109.147379, 19.704863],\n                            [109.093792, 19.68965],\n                            [109.048829, 19.619764],\n                            [108.993394, 19.587065],\n                            [108.92872, 19.524468],\n                            [108.855424, 19.469182],\n                            [108.806148, 19.450561],\n                            [108.765496, 19.400894],\n                            [108.694047, 19.387346],\n                            [108.644772, 19.349518],\n                            [108.609048, 19.276661],\n                            [108.591186, 19.141592],\n                            [108.598577, 19.055633],\n                            [108.630606, 19.003017],\n                            [108.637997, 18.924346],\n                            [108.595497, 18.872256],\n                            [108.593033, 18.809386],\n                            [108.65278, 18.740258],\n                            [108.663866, 18.67337],\n                            [108.641077, 18.565614],\n                            [108.644772, 18.486738],\n                            [108.68912, 18.447571],\n                            [108.776583, 18.441894],\n                            [108.881293, 18.416344],\n                            [108.905315, 18.389087],\n                            [108.944735, 18.314107],\n                            [109.006329, 18.323198],\n                            [109.108575, 18.323766],\n                            [109.138756, 18.268081],\n                            [109.17448, 18.260125],\n                            [109.287813, 18.264671],\n                            [109.355566, 18.215221],\n                            [109.441182, 18.199303],\n                            [109.467051, 18.173718],\n                            [109.527413, 18.169169],\n                            [109.584696, 18.143579],\n                            [109.661688, 18.175424],\n                            [109.726362, 18.177698],\n                            [109.749767, 18.193618],\n                            [109.785492, 18.339672],\n                            [109.919767, 18.375457],\n                            [110.022629, 18.360121],\n                            [110.070672, 18.376025],\n                            [110.090382, 18.399309],\n                            [110.116867, 18.506602],\n                            [110.214186, 18.578662],\n                            [110.246215, 18.609859],\n                            [110.329366, 18.642185],\n                            [110.367555, 18.631977],\n                            [110.499366, 18.651824],\n                            [110.499366, 18.751592],\n                            [110.578206, 18.784458],\n                            [110.590525, 18.838841],\n                            [110.585597, 18.88075],\n                            [110.619474, 19.152334],\n                            [110.676756, 19.286264],\n                            [110.706321, 19.320153],\n                            [110.729727, 19.378878],\n                            [110.787009, 19.399765],\n                            [110.844292, 19.449996],\n                            [110.888023, 19.518827],\n                            [110.920668, 19.552668],\n                            [111.008747, 19.60398],\n                            [111.061718, 19.612436],\n                            [111.071573, 19.628784],\n                            [111.043856, 19.763448],\n                            [111.013675, 19.850159],\n                            [110.966248, 20.018377],\n                            [110.940994, 20.028499],\n                            [110.871393, 20.01163],\n                            [110.808567, 20.035808],\n                            [110.778386, 20.068415],\n                            [110.744509, 20.074036],\n                            [110.717408, 20.148778],\n                            [110.687843, 20.163947],\n                            [110.655814, 20.134169],\n                            [110.562191, 20.110006],\n                            [110.526467, 20.07516],\n                            [110.495054, 20.077408],\n                            [110.387265, 20.113378],\n                            [110.318279, 20.108882],\n                            [110.28933, 20.056047],\n                            [110.243135, 20.077408],\n                            [110.144585, 20.074598],\n                            [110.106396, 20.026812]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 500000,\n                \"name\": \"Chongqing\",\n                \"center\": [106.504962, 29.533155],\n                \"centroid\": [107.8839, 30.067297],\n                \"childrenNum\": 38,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 21,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [106.37442, 28.525742],\n                            [106.403369, 28.569901],\n                            [106.477282, 28.530474],\n                            [106.504999, 28.544669],\n                            [106.466811, 28.586193],\n                            [106.49268, 28.591448],\n                            [106.502535, 28.661313],\n                            [106.528405, 28.677591],\n                            [106.492064, 28.742153],\n                            [106.461883, 28.761041],\n                            [106.45326, 28.817162],\n                            [106.474202, 28.832891],\n                            [106.561666, 28.756319],\n                            [106.56105, 28.719062],\n                            [106.587535, 28.691767],\n                            [106.6171, 28.691242],\n                            [106.617716, 28.66709],\n                            [106.651593, 28.649235],\n                            [106.618332, 28.645033],\n                            [106.63681, 28.622972],\n                            [106.606629, 28.593024],\n                            [106.615252, 28.549401],\n                            [106.567825, 28.523638],\n                            [106.564745, 28.485247],\n                            [106.632499, 28.503655],\n                            [106.697788, 28.47683],\n                            [106.708259, 28.450524],\n                            [106.747063, 28.467361],\n                            [106.726121, 28.51838],\n                            [106.73844, 28.554657],\n                            [106.77786, 28.563068],\n                            [106.756918, 28.607211],\n                            [106.784636, 28.626649],\n                            [106.807425, 28.589346],\n                            [106.830831, 28.623497],\n                            [106.866556, 28.624548],\n                            [106.889345, 28.695966],\n                            [106.86594, 28.690192],\n                            [106.824056, 28.756319],\n                            [106.845614, 28.780975],\n                            [106.872099, 28.777304],\n                            [106.923222, 28.809821],\n                            [106.951555, 28.766812],\n                            [106.988512, 28.776254],\n                            [106.983584, 28.851239],\n                            [107.019308, 28.861722],\n                            [107.016229, 28.882685],\n                            [107.14188, 28.887925],\n                            [107.206554, 28.868535],\n                            [107.194851, 28.838134],\n                            [107.227496, 28.836037],\n                            [107.210866, 28.817686],\n                            [107.219489, 28.772582],\n                            [107.24659, 28.76209],\n                            [107.261373, 28.792514],\n                            [107.327894, 28.810869],\n                            [107.339597, 28.845997],\n                            [107.383945, 28.848618],\n                            [107.41351, 28.911502],\n                            [107.441227, 28.943977],\n                            [107.412894, 28.960211],\n                            [107.396879, 28.993718],\n                            [107.364235, 29.00942],\n                            [107.395647, 29.041341],\n                            [107.369778, 29.091558],\n                            [107.412278, 29.094696],\n                            [107.427676, 29.128682],\n                            [107.408582, 29.138091],\n                            [107.401807, 29.184603],\n                            [107.441227, 29.203934],\n                            [107.486806, 29.174153],\n                            [107.570574, 29.218037],\n                            [107.589052, 29.150113],\n                            [107.605683, 29.164747],\n                            [107.659885, 29.162656],\n                            [107.700537, 29.141228],\n                            [107.749197, 29.199754],\n                            [107.810791, 29.139137],\n                            [107.784921, 29.048143],\n                            [107.823725, 29.034016],\n                            [107.810175, 28.984295],\n                            [107.867457, 28.960211],\n                            [107.882855, 29.00628],\n                            [107.908725, 29.007327],\n                            [107.925971, 29.032446],\n                            [108.026369, 29.039772],\n                            [108.070717, 29.086328],\n                            [108.150173, 29.053375],\n                            [108.193289, 29.072207],\n                            [108.256115, 29.040295],\n                            [108.277673, 29.091558],\n                            [108.306622, 29.079006],\n                            [108.297999, 29.045527],\n                            [108.319556, 28.961258],\n                            [108.345426, 28.943453],\n                            [108.357745, 28.893165],\n                            [108.346658, 28.859625],\n                            [108.352817, 28.815589],\n                            [108.386078, 28.803003],\n                            [108.385462, 28.772058],\n                            [108.347274, 28.736381],\n                            [108.332491, 28.679166],\n                            [108.439049, 28.634003],\n                            [108.501258, 28.626649],\n                            [108.50249, 28.63768],\n                            [108.575787, 28.659738],\n                            [108.636149, 28.621396],\n                            [108.604736, 28.590922],\n                            [108.610896, 28.539412],\n                            [108.573939, 28.531],\n                            [108.586874, 28.463678],\n                            [108.609664, 28.43579],\n                            [108.609048, 28.407368],\n                            [108.576403, 28.38631],\n                            [108.580099, 28.343128],\n                            [108.611512, 28.324691],\n                            [108.667562, 28.334173],\n                            [108.656475, 28.359981],\n                            [108.697127, 28.401051],\n                            [108.688504, 28.422106],\n                            [108.640461, 28.456838],\n                            [108.657091, 28.47683],\n                            [108.700207, 28.48209],\n                            [108.709446, 28.501026],\n                            [108.746402, 28.45105],\n                            [108.780279, 28.42579],\n                            [108.759953, 28.389995],\n                            [108.783359, 28.380518],\n                            [108.761801, 28.304143],\n                            [108.726692, 28.282011],\n                            [108.738395, 28.228241],\n                            [108.772888, 28.212949],\n                            [108.821547, 28.245113],\n                            [108.855424, 28.199764],\n                            [108.89546, 28.219804],\n                            [108.923793, 28.217167],\n                            [108.929952, 28.19027],\n                            [109.005713, 28.162837],\n                            [109.026655, 28.220331],\n                            [109.086401, 28.184467],\n                            [109.101799, 28.202401],\n                            [109.081473, 28.247749],\n                            [109.117198, 28.277795],\n                            [109.152306, 28.349975],\n                            [109.153538, 28.417369],\n                            [109.191726, 28.471043],\n                            [109.23361, 28.474726],\n                            [109.274262, 28.494714],\n                            [109.273646, 28.53836],\n                            [109.319842, 28.579886],\n                            [109.306907, 28.62087],\n                            [109.252089, 28.606685],\n                            [109.235458, 28.61982],\n                            [109.201581, 28.597753],\n                            [109.192958, 28.636104],\n                            [109.271183, 28.671816],\n                            [109.252704, 28.691767],\n                            [109.294588, 28.722211],\n                            [109.2989, 28.7474],\n                            [109.241002, 28.776779],\n                            [109.246545, 28.80143],\n                            [109.235458, 28.882161],\n                            [109.261328, 28.952356],\n                            [109.292741, 28.987436],\n                            [109.294588, 29.015177],\n                            [109.319842, 29.042388],\n                            [109.312451, 29.066453],\n                            [109.240386, 29.086328],\n                            [109.232378, 29.119271],\n                            [109.215748, 29.145409],\n                            [109.162777, 29.180946],\n                            [109.139372, 29.168927],\n                            [109.110422, 29.21647],\n                            [109.141835, 29.270256],\n                            [109.106727, 29.288526],\n                            [109.11227, 29.361053],\n                            [109.060531, 29.403292],\n                            [109.034662, 29.360531],\n                            [108.999553, 29.36366],\n                            [108.983539, 29.332883],\n                            [108.919481, 29.3261],\n                            [108.934264, 29.399643],\n                            [108.927488, 29.435612],\n                            [108.884373, 29.440824],\n                            [108.866511, 29.470527],\n                            [108.888684, 29.502305],\n                            [108.878213, 29.539279],\n                            [108.913322, 29.574679],\n                            [108.901003, 29.604863],\n                            [108.870206, 29.596537],\n                            [108.888068, 29.628795],\n                            [108.844337, 29.658443],\n                            [108.781511, 29.635558],\n                            [108.797525, 29.660003],\n                            [108.786438, 29.691721],\n                            [108.752562, 29.649082],\n                            [108.690968, 29.689642],\n                            [108.676801, 29.749412],\n                            [108.680497, 29.800319],\n                            [108.658939, 29.854833],\n                            [108.601041, 29.863656],\n                            [108.556077, 29.818493],\n                            [108.52528, 29.770713],\n                            [108.548686, 29.749412],\n                            [108.504954, 29.728626],\n                            [108.504338, 29.707836],\n                            [108.460606, 29.741098],\n                            [108.437201, 29.741098],\n                            [108.442744, 29.778505],\n                            [108.422418, 29.772791],\n                            [108.424266, 29.815897],\n                            [108.371295, 29.841337],\n                            [108.433505, 29.880262],\n                            [108.467998, 29.864175],\n                            [108.516041, 29.885451],\n                            [108.517889, 29.9394],\n                            [108.536367, 29.983472],\n                            [108.532055, 30.051873],\n                            [108.513577, 30.057571],\n                            [108.546222, 30.104178],\n                            [108.56778, 30.157491],\n                            [108.551766, 30.1637],\n                            [108.581947, 30.255759],\n                            [108.54499, 30.269716],\n                            [108.524048, 30.309506],\n                            [108.501258, 30.314673],\n                            [108.460606, 30.35961],\n                            [108.431041, 30.354446],\n                            [108.402092, 30.376649],\n                            [108.430425, 30.416397],\n                            [108.411331, 30.438586],\n                            [108.42673, 30.492233],\n                            [108.472925, 30.487076],\n                            [108.512961, 30.501515],\n                            [108.556077, 30.487592],\n                            [108.56778, 30.468508],\n                            [108.6497, 30.53915],\n                            [108.642925, 30.578831],\n                            [108.688504, 30.58759],\n                            [108.698975, 30.54482],\n                            [108.743939, 30.494812],\n                            [108.789518, 30.513374],\n                            [108.808612, 30.491202],\n                            [108.838793, 30.503062],\n                            [108.893612, 30.565434],\n                            [108.971836, 30.627766],\n                            [109.006329, 30.626736],\n                            [109.042669, 30.655571],\n                            [109.071002, 30.640125],\n                            [109.111654, 30.646303],\n                            [109.106111, 30.61077],\n                            [109.105495, 30.585529],\n                            [109.102415, 30.580377],\n                            [109.101183, 30.579346],\n                            [109.106111, 30.570587],\n                            [109.103647, 30.565949],\n                            [109.143683, 30.521108],\n                            [109.191726, 30.545851],\n                            [109.191726, 30.545851],\n                            [109.245313, 30.580892],\n                            [109.299516, 30.630341],\n                            [109.314298, 30.599953],\n                            [109.36111, 30.551004],\n                            [109.337088, 30.521623],\n                            [109.35495, 30.487076],\n                            [109.418392, 30.559766],\n                            [109.435638, 30.595832],\n                            [109.535421, 30.664837],\n                            [109.543428, 30.63961],\n                            [109.574225, 30.646818],\n                            [109.590855, 30.69366],\n                            [109.625348, 30.702923],\n                            [109.661072, 30.738936],\n                            [109.656761, 30.760538],\n                            [109.701724, 30.783677],\n                            [109.780564, 30.848437],\n                            [109.828608, 30.864364],\n                            [109.894513, 30.899803],\n                            [109.943788, 30.878746],\n                            [110.008462, 30.883369],\n                            [110.019549, 30.829425],\n                            [110.048498, 30.800642],\n                            [110.082375, 30.799614],\n                            [110.151976, 30.911613],\n                            [110.153824, 30.953708],\n                            [110.172918, 30.978853],\n                            [110.140889, 30.987062],\n                            [110.140273, 31.030661],\n                            [110.120563, 31.0322],\n                            [110.119947, 31.088592],\n                            [110.147048, 31.116776],\n                            [110.180309, 31.121899],\n                            [110.200019, 31.158779],\n                            [110.180309, 31.179774],\n                            [110.155671, 31.279564],\n                            [110.161831, 31.314338],\n                            [110.118715, 31.409899],\n                            [110.054042, 31.410921],\n                            [110.036795, 31.436966],\n                            [109.98752, 31.474744],\n                            [109.94502, 31.47066],\n                            [109.969658, 31.508935],\n                            [109.894513, 31.519139],\n                            [109.837847, 31.555354],\n                            [109.727594, 31.548214],\n                            [109.745456, 31.598182],\n                            [109.76455, 31.602769],\n                            [109.737449, 31.628761],\n                            [109.731289, 31.700582],\n                            [109.683246, 31.719929],\n                            [109.622268, 31.711783],\n                            [109.585928, 31.726546],\n                            [109.549587, 31.73011],\n                            [109.502776, 31.716365],\n                            [109.446109, 31.722983],\n                            [109.381436, 31.705165],\n                            [109.281654, 31.716874],\n                            [109.282885, 31.743343],\n                            [109.253936, 31.759628],\n                            [109.279806, 31.776418],\n                            [109.27611, 31.79931],\n                            [109.195422, 31.817618],\n                            [109.191111, 31.85575],\n                            [109.123357, 31.892851],\n                            [109.085785, 31.929428],\n                            [108.986619, 31.980205],\n                            [108.902235, 31.984774],\n                            [108.837561, 32.039072],\n                            [108.78767, 32.04871],\n                            [108.75133, 32.076098],\n                            [108.734084, 32.106519],\n                            [108.676801, 32.10297],\n                            [108.585026, 32.17189],\n                            [108.543758, 32.177969],\n                            [108.509882, 32.201266],\n                            [108.480317, 32.182527],\n                            [108.399013, 32.194176],\n                            [108.370063, 32.172397],\n                            [108.379918, 32.154158],\n                            [108.379918, 32.154158],\n                            [108.379303, 32.153652],\n                            [108.379303, 32.153652],\n                            [108.399628, 32.147065],\n                            [108.452599, 32.090296],\n                            [108.42981, 32.061391],\n                            [108.372527, 32.077112],\n                            [108.344194, 32.067477],\n                            [108.362056, 32.035521],\n                            [108.329411, 32.020299],\n                            [108.370063, 31.988835],\n                            [108.351585, 31.971575],\n                            [108.307238, 31.997463],\n                            [108.259194, 31.967006],\n                            [108.343578, 31.860834],\n                            [108.386078, 31.854226],\n                            [108.391005, 31.829822],\n                            [108.429194, 31.809482],\n                            [108.455063, 31.814059],\n                            [108.462454, 31.780488],\n                            [108.535135, 31.757592],\n                            [108.50557, 31.734182],\n                            [108.514809, 31.693963],\n                            [108.546838, 31.665442],\n                            [108.519121, 31.665952],\n                            [108.468614, 31.636404],\n                            [108.442744, 31.633856],\n                            [108.390389, 31.591555],\n                            [108.386078, 31.544134],\n                            [108.339266, 31.539033],\n                            [108.344194, 31.512506],\n                            [108.254883, 31.49873],\n                            [108.233941, 31.506894],\n                            [108.191441, 31.492096],\n                            [108.193289, 31.467598],\n                            [108.224086, 31.464024],\n                            [108.216079, 31.41041],\n                            [108.153869, 31.371073],\n                            [108.185898, 31.336831],\n                            [108.095354, 31.268311],\n                            [108.038688, 31.252964],\n                            [108.031297, 31.217144],\n                            [108.07626, 31.231985],\n                            [108.089811, 31.204859],\n                            [108.025753, 31.116263],\n                            [108.009123, 31.109602],\n                            [108.026985, 31.061938],\n                            [108.060246, 31.052197],\n                            [108.00358, 31.025533],\n                            [107.983254, 30.983983],\n                            [107.942602, 30.989114],\n                            [107.948145, 30.918802],\n                            [107.994956, 30.908533],\n                            [107.956152, 30.882855],\n                            [107.851443, 30.792931],\n                            [107.788001, 30.81966],\n                            [107.763979, 30.817091],\n                            [107.760899, 30.862823],\n                            [107.739957, 30.884396],\n                            [107.693146, 30.875665],\n                            [107.645103, 30.821202],\n                            [107.57735, 30.847924],\n                            [107.515756, 30.854603],\n                            [107.483111, 30.838675],\n                            [107.498509, 30.809381],\n                            [107.454162, 30.771851],\n                            [107.454162, 30.771851],\n                            [107.424597, 30.74048],\n                            [107.458473, 30.704981],\n                            [107.477567, 30.664837],\n                            [107.516987, 30.644759],\n                            [107.485575, 30.598408],\n                            [107.427676, 30.547397],\n                            [107.443075, 30.53348],\n                            [107.408582, 30.521623],\n                            [107.368546, 30.468508],\n                            [107.338981, 30.386459],\n                            [107.288474, 30.337402],\n                            [107.257677, 30.267131],\n                            [107.221337, 30.213878],\n                            [107.103076, 30.090198],\n                            [107.080286, 30.094341],\n                            [107.084598, 30.063786],\n                            [107.058113, 30.043066],\n                            [107.055649, 30.040476],\n                            [107.054417, 30.040994],\n                            [107.053801, 30.043584],\n                            [107.02054, 30.036849],\n                            [106.981736, 30.08502],\n                            [106.976193, 30.083467],\n                            [106.94478, 30.037367],\n                            [106.913367, 30.025451],\n                            [106.862244, 30.033223],\n                            [106.83699, 30.049801],\n                            [106.825904, 30.03115],\n                            [106.825904, 30.03115],\n                            [106.785252, 30.01716],\n                            [106.732281, 30.027005],\n                            [106.724274, 30.058607],\n                            [106.699636, 30.074145],\n                            [106.700252, 30.111944],\n                            [106.672535, 30.122297],\n                            [106.677462, 30.156974],\n                            [106.631883, 30.186464],\n                            [106.611557, 30.235596],\n                            [106.612173, 30.235596],\n                            [106.611557, 30.235596],\n                            [106.612173, 30.235596],\n                            [106.612173, 30.235596],\n                            [106.612789, 30.235596],\n                            [106.612789, 30.235596],\n                            [106.642354, 30.246454],\n                            [106.611557, 30.292455],\n                            [106.560434, 30.31519],\n                            [106.545035, 30.296589],\n                            [106.49884, 30.295556],\n                            [106.43971, 30.308473],\n                            [106.428623, 30.254725],\n                            [106.401521, 30.242318],\n                            [106.349167, 30.24542],\n                            [106.334384, 30.225772],\n                            [106.306667, 30.238182],\n                            [106.296196, 30.205603],\n                            [106.264167, 30.20974],\n                            [106.260471, 30.19681],\n                            [106.232754, 30.185947],\n                            [106.180399, 30.233011],\n                            [106.168696, 30.303823],\n                            [106.132356, 30.323972],\n                            [106.132972, 30.30279],\n                            [106.07261, 30.333786],\n                            [106.031958, 30.373551],\n                            [105.943263, 30.372002],\n                            [105.900763, 30.405042],\n                            [105.84656, 30.410203],\n                            [105.825618, 30.436006],\n                            [105.792357, 30.427234],\n                            [105.760329, 30.384393],\n                            [105.754785, 30.342567],\n                            [105.714749, 30.322939],\n                            [105.720292, 30.252657],\n                            [105.720292, 30.252657],\n                            [105.670401, 30.254208],\n                            [105.624822, 30.275918],\n                            [105.619894, 30.234045],\n                            [105.662394, 30.210258],\n                            [105.642684, 30.186464],\n                            [105.56138, 30.183878],\n                            [105.550909, 30.179222],\n                            [105.536127, 30.152834],\n                            [105.596489, 30.159043],\n                            [105.574315, 30.130579],\n                            [105.580474, 30.129544],\n                            [105.582938, 30.127474],\n                            [105.582938, 30.12385],\n                            [105.642068, 30.101072],\n                            [105.638988, 30.076216],\n                            [105.676561, 30.06793],\n                            [105.687032, 30.038922],\n                            [105.719677, 30.042548],\n                            [105.753553, 30.018196],\n                            [105.723372, 29.975177],\n                            [105.730763, 29.95755],\n                            [105.70243, 29.924879],\n                            [105.717213, 29.893753],\n                            [105.738771, 29.891159],\n                            [105.707974, 29.840818],\n                            [105.610655, 29.837184],\n                            [105.582938, 29.819013],\n                            [105.574931, 29.744216],\n                            [105.529351, 29.707836],\n                            [105.481924, 29.718232],\n                            [105.476996, 29.674564],\n                            [105.419714, 29.688082],\n                            [105.38091, 29.628275],\n                            [105.347649, 29.621512],\n                            [105.332867, 29.592374],\n                            [105.296526, 29.571035],\n                            [105.305149, 29.53199],\n                            [105.337794, 29.459064],\n                            [105.334099, 29.441345],\n                            [105.387069, 29.455416],\n                            [105.387069, 29.455416],\n                            [105.399388, 29.43874],\n                            [105.372903, 29.421018],\n                            [105.426489, 29.419454],\n                            [105.441888, 29.400686],\n                            [105.418482, 29.352185],\n                            [105.42033, 29.31149],\n                            [105.465294, 29.322969],\n                            [105.459134, 29.288526],\n                            [105.513337, 29.283306],\n                            [105.521344, 29.264513],\n                            [105.557684, 29.278608],\n                            [105.631597, 29.280174],\n                            [105.647612, 29.253027],\n                            [105.695039, 29.287482],\n                            [105.712285, 29.219082],\n                            [105.703662, 29.176766],\n                            [105.728916, 29.134432],\n                            [105.752321, 29.129727],\n                            [105.728916, 29.1062],\n                            [105.757865, 29.069068],\n                            [105.74185, 29.039249],\n                            [105.766488, 29.013607],\n                            [105.762176, 28.9911],\n                            [105.801596, 28.958116],\n                            [105.797285, 28.936121],\n                            [105.830546, 28.944501],\n                            [105.852719, 28.927217],\n                            [105.910002, 28.920407],\n                            [105.969132, 28.965971],\n                            [106.001161, 28.973824],\n                            [106.040581, 28.955498],\n                            [106.049204, 28.906263],\n                            [106.070762, 28.919884],\n                            [106.101559, 28.898928],\n                            [106.14837, 28.901548],\n                            [106.173008, 28.920407],\n                            [106.206885, 28.904691],\n                            [106.264783, 28.845997],\n                            [106.245689, 28.817686],\n                            [106.267863, 28.779402],\n                            [106.274022, 28.739004],\n                            [106.305435, 28.704365],\n                            [106.304203, 28.64976],\n                            [106.346703, 28.583565],\n                            [106.33192, 28.55308],\n                            [106.37442, 28.525742]\n                        ]\n                    ],\n                    [\n                        [\n                            [109.105495, 30.585529],\n                            [109.106111, 30.61077],\n                            [109.09256, 30.578831],\n                            [109.09872, 30.579346],\n                            [109.101183, 30.579346],\n                            [109.102415, 30.580377],\n                            [109.105495, 30.585529]\n                        ]\n                    ],\n                    [\n                        [\n                            [105.582938, 30.12385],\n                            [105.582938, 30.127474],\n                            [105.580474, 30.129544],\n                            [105.574315, 30.130579],\n                            [105.582938, 30.12385]\n                        ]\n                    ],\n                    [\n                        [\n                            [109.09872, 30.579346],\n                            [109.09256, 30.578831],\n                            [109.103647, 30.565949],\n                            [109.106111, 30.570587],\n                            [109.09872, 30.579346]\n                        ]\n                    ],\n                    [\n                        [\n                            [107.058113, 30.043066],\n                            [107.053801, 30.043584],\n                            [107.054417, 30.040994],\n                            [107.055649, 30.040476],\n                            [107.058113, 30.043066]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 510000,\n                \"name\": \"Sichuan\",\n                \"center\": [104.065735, 30.659462],\n                \"centroid\": [102.693453, 30.674545],\n                \"childrenNum\": 21,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 22,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [101.167885, 27.198311],\n                            [101.170349, 27.175421],\n                            [101.145095, 27.103523],\n                            [101.157414, 27.094999],\n                            [101.136472, 27.023584],\n                            [101.228863, 26.981992],\n                            [101.227015, 26.959057],\n                            [101.264587, 26.955323],\n                            [101.267667, 26.903034],\n                            [101.311399, 26.903034],\n                            [101.365602, 26.883819],\n                            [101.399478, 26.841642],\n                            [101.358826, 26.771669],\n                            [101.387159, 26.753501],\n                            [101.389623, 26.723036],\n                            [101.435819, 26.740675],\n                            [101.458608, 26.731054],\n                            [101.445674, 26.77434],\n                            [101.466, 26.786629],\n                            [101.513427, 26.768463],\n                            [101.453065, 26.692563],\n                            [101.481398, 26.673313],\n                            [101.461072, 26.640687],\n                            [101.461688, 26.606447],\n                            [101.402558, 26.604841],\n                            [101.395783, 26.591998],\n                            [101.422884, 26.53151],\n                            [101.458608, 26.49563],\n                            [101.506652, 26.499915],\n                            [101.530057, 26.467239],\n                            [101.565782, 26.454381],\n                            [101.637847, 26.388995],\n                            [101.635383, 26.357361],\n                            [101.660636, 26.346635],\n                            [101.64031, 26.318745],\n                            [101.597195, 26.303187],\n                            [101.586108, 26.279579],\n                            [101.630455, 26.224832],\n                            [101.690202, 26.241473],\n                            [101.737013, 26.219463],\n                            [101.773353, 26.168448],\n                            [101.807846, 26.156093],\n                            [101.796759, 26.114723],\n                            [101.839875, 26.082477],\n                            [101.835563, 26.04592],\n                            [101.857737, 26.049146],\n                            [101.899621, 26.099139],\n                            [101.929186, 26.105588],\n                            [101.954439, 26.084627],\n                            [102.020961, 26.096451],\n                            [102.080091, 26.065275],\n                            [102.107808, 26.068501],\n                            [102.152156, 26.10935],\n                            [102.174946, 26.146961],\n                            [102.242699, 26.190468],\n                            [102.245163, 26.212483],\n                            [102.349257, 26.244694],\n                            [102.392372, 26.296749],\n                            [102.440416, 26.300505],\n                            [102.542046, 26.338591],\n                            [102.570995, 26.362723],\n                            [102.629509, 26.336982],\n                            [102.638748, 26.307479],\n                            [102.60056, 26.250598],\n                            [102.659074, 26.221611],\n                            [102.709581, 26.210336],\n                            [102.739762, 26.268846],\n                            [102.785342, 26.298895],\n                            [102.833385, 26.306406],\n                            [102.878964, 26.364332],\n                            [102.893131, 26.338591],\n                            [102.975667, 26.340736],\n                            [102.998457, 26.371839],\n                            [102.988602, 26.413117],\n                            [102.989833, 26.482775],\n                            [103.030485, 26.485989],\n                            [103.052659, 26.514374],\n                            [103.052659, 26.555602],\n                            [103.035413, 26.556673],\n                            [103.026174, 26.664221],\n                            [103.005232, 26.679195],\n                            [103.008312, 26.710741],\n                            [102.983674, 26.76686],\n                            [102.991681, 26.775409],\n                            [102.966428, 26.837904],\n                            [102.949181, 26.843244],\n                            [102.896211, 26.91264],\n                            [102.894979, 27.001724],\n                            [102.870957, 27.026782],\n                            [102.913457, 27.133886],\n                            [102.904218, 27.227584],\n                            [102.883276, 27.258444],\n                            [102.883892, 27.299401],\n                            [102.899906, 27.317481],\n                            [102.941174, 27.405711],\n                            [102.989833, 27.367983],\n                            [103.055739, 27.40943],\n                            [103.080992, 27.396679],\n                            [103.141355, 27.420586],\n                            [103.144434, 27.450331],\n                            [103.19063, 27.523596],\n                            [103.232514, 27.56976],\n                            [103.2861, 27.561802],\n                            [103.29226, 27.632872],\n                            [103.349542, 27.678459],\n                            [103.369868, 27.708664],\n                            [103.393274, 27.709194],\n                            [103.461027, 27.779638],\n                            [103.487512, 27.794992],\n                            [103.509686, 27.843687],\n                            [103.502295, 27.910343],\n                            [103.55465, 27.978543],\n                            [103.515846, 27.965329],\n                            [103.486281, 28.033495],\n                            [103.459179, 28.021345],\n                            [103.430846, 28.044587],\n                            [103.470266, 28.122204],\n                            [103.533092, 28.168641],\n                            [103.573128, 28.230877],\n                            [103.643961, 28.260401],\n                            [103.692004, 28.232459],\n                            [103.701859, 28.198709],\n                            [103.740048, 28.23615],\n                            [103.770845, 28.233514],\n                            [103.828743, 28.285173],\n                            [103.877402, 28.316262],\n                            [103.85338, 28.356822],\n                            [103.860156, 28.383677],\n                            [103.828743, 28.44],\n                            [103.829975, 28.459995],\n                            [103.781931, 28.525216],\n                            [103.802873, 28.563068],\n                            [103.838598, 28.587244],\n                            [103.833054, 28.605109],\n                            [103.850917, 28.66709],\n                            [103.887873, 28.61982],\n                            [103.910047, 28.631377],\n                            [103.953779, 28.600906],\n                            [104.05972, 28.6277],\n                            [104.09606, 28.603533],\n                            [104.117618, 28.634003],\n                            [104.170589, 28.642932],\n                            [104.230951, 28.635579],\n                            [104.252509, 28.660788],\n                            [104.277147, 28.631902],\n                            [104.314719, 28.615617],\n                            [104.372617, 28.649235],\n                            [104.425588, 28.626649],\n                            [104.417581, 28.598279],\n                            [104.375697, 28.5946],\n                            [104.355987, 28.555183],\n                            [104.323342, 28.540989],\n                            [104.260516, 28.536257],\n                            [104.267908, 28.499448],\n                            [104.254357, 28.403683],\n                            [104.282074, 28.343128],\n                            [104.314103, 28.306778],\n                            [104.343052, 28.334173],\n                            [104.384936, 28.329959],\n                            [104.392943, 28.291497],\n                            [104.420045, 28.269889],\n                            [104.44961, 28.269889],\n                            [104.462544, 28.241422],\n                            [104.442834, 28.211366],\n                            [104.402182, 28.202928],\n                            [104.406494, 28.173389],\n                            [104.444682, 28.16231],\n                            [104.448994, 28.113758],\n                            [104.40095, 28.091586],\n                            [104.373233, 28.051454],\n                            [104.304248, 28.050926],\n                            [104.30856, 28.036136],\n                            [104.362762, 28.012891],\n                            [104.40095, 27.952114],\n                            [104.44961, 27.927794],\n                            [104.508124, 27.878078],\n                            [104.52537, 27.889187],\n                            [104.573413, 27.840512],\n                            [104.607906, 27.857974],\n                            [104.63316, 27.850567],\n                            [104.676275, 27.880723],\n                            [104.743413, 27.901881],\n                            [104.761891, 27.884426],\n                            [104.796999, 27.901352],\n                            [104.842579, 27.900294],\n                            [104.888158, 27.914574],\n                            [104.918339, 27.938897],\n                            [104.903557, 27.962158],\n                            [104.975006, 28.020816],\n                            [104.980549, 28.063073],\n                            [105.002107, 28.064129],\n                            [105.061853, 28.096866],\n                            [105.119752, 28.07205],\n                            [105.168411, 28.071522],\n                            [105.186889, 28.054623],\n                            [105.167795, 28.021345],\n                            [105.186273, 27.995454],\n                            [105.218302, 27.990698],\n                            [105.247867, 28.009193],\n                            [105.270657, 27.99704],\n                            [105.284823, 27.935725],\n                            [105.233084, 27.895534],\n                            [105.25957, 27.827811],\n                            [105.313157, 27.810874],\n                            [105.273736, 27.794992],\n                            [105.293447, 27.770637],\n                            [105.290367, 27.712373],\n                            [105.308229, 27.704955],\n                            [105.353809, 27.748924],\n                            [105.44004, 27.775402],\n                            [105.508409, 27.769048],\n                            [105.560148, 27.71979],\n                            [105.605112, 27.715552],\n                            [105.62359, 27.666269],\n                            [105.664242, 27.683759],\n                            [105.720292, 27.683759],\n                            [105.722756, 27.706015],\n                            [105.76772, 27.7182],\n                            [105.848408, 27.707074],\n                            [105.868118, 27.732504],\n                            [105.922937, 27.746805],\n                            [105.92848, 27.729855],\n                            [105.985146, 27.749983],\n                            [106.023335, 27.746805],\n                            [106.063987, 27.776991],\n                            [106.120653, 27.779638],\n                            [106.193334, 27.75422],\n                            [106.242609, 27.767459],\n                            [106.306667, 27.808756],\n                            [106.337464, 27.859033],\n                            [106.325145, 27.898708],\n                            [106.304819, 27.899237],\n                            [106.307899, 27.936782],\n                            [106.328225, 27.952643],\n                            [106.286341, 28.007079],\n                            [106.246305, 28.011835],\n                            [106.266631, 28.066769],\n                            [106.206885, 28.134343],\n                            [106.145291, 28.162837],\n                            [106.093552, 28.162837],\n                            [105.975907, 28.107952],\n                            [105.943878, 28.143314],\n                            [105.895219, 28.119565],\n                            [105.860727, 28.159672],\n                            [105.889676, 28.237732],\n                            [105.848408, 28.255656],\n                            [105.824386, 28.306251],\n                            [105.78743, 28.335753],\n                            [105.76464, 28.308359],\n                            [105.76464, 28.308359],\n                            [105.737539, 28.30309],\n                            [105.730147, 28.271997],\n                            [105.68888, 28.284119],\n                            [105.639604, 28.324164],\n                            [105.655003, 28.362615],\n                            [105.643916, 28.431053],\n                            [105.612503, 28.438947],\n                            [105.62359, 28.517854],\n                            [105.68272, 28.534154],\n                            [105.693191, 28.58882],\n                            [105.712901, 28.586718],\n                            [105.74493, 28.616668],\n                            [105.757249, 28.590397],\n                            [105.78435, 28.610889],\n                            [105.808372, 28.599855],\n                            [105.884748, 28.595126],\n                            [105.889676, 28.670765],\n                            [105.937719, 28.686517],\n                            [105.966668, 28.761041],\n                            [106.001161, 28.743727],\n                            [106.030726, 28.694917],\n                            [106.085544, 28.681792],\n                            [106.103407, 28.636104],\n                            [106.14837, 28.642932],\n                            [106.17116, 28.629275],\n                            [106.184711, 28.58882],\n                            [106.254928, 28.539412],\n                            [106.2925, 28.537309],\n                            [106.304819, 28.505233],\n                            [106.349167, 28.473674],\n                            [106.379348, 28.479986],\n                            [106.37442, 28.525742],\n                            [106.33192, 28.55308],\n                            [106.346703, 28.583565],\n                            [106.304203, 28.64976],\n                            [106.305435, 28.704365],\n                            [106.274022, 28.739004],\n                            [106.267863, 28.779402],\n                            [106.245689, 28.817686],\n                            [106.264783, 28.845997],\n                            [106.206885, 28.904691],\n                            [106.173008, 28.920407],\n                            [106.14837, 28.901548],\n                            [106.101559, 28.898928],\n                            [106.070762, 28.919884],\n                            [106.049204, 28.906263],\n                            [106.040581, 28.955498],\n                            [106.001161, 28.973824],\n                            [105.969132, 28.965971],\n                            [105.910002, 28.920407],\n                            [105.852719, 28.927217],\n                            [105.830546, 28.944501],\n                            [105.797285, 28.936121],\n                            [105.801596, 28.958116],\n                            [105.762176, 28.9911],\n                            [105.766488, 29.013607],\n                            [105.74185, 29.039249],\n                            [105.757865, 29.069068],\n                            [105.728916, 29.1062],\n                            [105.752321, 29.129727],\n                            [105.728916, 29.134432],\n                            [105.703662, 29.176766],\n                            [105.712285, 29.219082],\n                            [105.695039, 29.287482],\n                            [105.647612, 29.253027],\n                            [105.631597, 29.280174],\n                            [105.557684, 29.278608],\n                            [105.521344, 29.264513],\n                            [105.513337, 29.283306],\n                            [105.459134, 29.288526],\n                            [105.465294, 29.322969],\n                            [105.42033, 29.31149],\n                            [105.418482, 29.352185],\n                            [105.441888, 29.400686],\n                            [105.426489, 29.419454],\n                            [105.372903, 29.421018],\n                            [105.399388, 29.43874],\n                            [105.387069, 29.455416],\n                            [105.387069, 29.455416],\n                            [105.334099, 29.441345],\n                            [105.337794, 29.459064],\n                            [105.305149, 29.53199],\n                            [105.296526, 29.571035],\n                            [105.332867, 29.592374],\n                            [105.347649, 29.621512],\n                            [105.38091, 29.628275],\n                            [105.419714, 29.688082],\n                            [105.476996, 29.674564],\n                            [105.481924, 29.718232],\n                            [105.529351, 29.707836],\n                            [105.574931, 29.744216],\n                            [105.582938, 29.819013],\n                            [105.610655, 29.837184],\n                            [105.707974, 29.840818],\n                            [105.738771, 29.891159],\n                            [105.717213, 29.893753],\n                            [105.70243, 29.924879],\n                            [105.730763, 29.95755],\n                            [105.723372, 29.975177],\n                            [105.753553, 30.018196],\n                            [105.719677, 30.042548],\n                            [105.687032, 30.038922],\n                            [105.676561, 30.06793],\n                            [105.638988, 30.076216],\n                            [105.642068, 30.101072],\n                            [105.582938, 30.12385],\n                            [105.574315, 30.130579],\n                            [105.596489, 30.159043],\n                            [105.536127, 30.152834],\n                            [105.550909, 30.179222],\n                            [105.556453, 30.187499],\n                            [105.558916, 30.18543],\n                            [105.56138, 30.183878],\n                            [105.642684, 30.186464],\n                            [105.662394, 30.210258],\n                            [105.619894, 30.234045],\n                            [105.624822, 30.275918],\n                            [105.670401, 30.254208],\n                            [105.720292, 30.252657],\n                            [105.720292, 30.252657],\n                            [105.714749, 30.322939],\n                            [105.754785, 30.342567],\n                            [105.760329, 30.384393],\n                            [105.792357, 30.427234],\n                            [105.825618, 30.436006],\n                            [105.84656, 30.410203],\n                            [105.900763, 30.405042],\n                            [105.943263, 30.372002],\n                            [106.031958, 30.373551],\n                            [106.07261, 30.333786],\n                            [106.132972, 30.30279],\n                            [106.132356, 30.323972],\n                            [106.168696, 30.303823],\n                            [106.180399, 30.233011],\n                            [106.232754, 30.185947],\n                            [106.260471, 30.19681],\n                            [106.260471, 30.204051],\n                            [106.260471, 30.207672],\n                            [106.264167, 30.20974],\n                            [106.296196, 30.205603],\n                            [106.306667, 30.238182],\n                            [106.334384, 30.225772],\n                            [106.349167, 30.24542],\n                            [106.401521, 30.242318],\n                            [106.428623, 30.254725],\n                            [106.43971, 30.308473],\n                            [106.49884, 30.295556],\n                            [106.545035, 30.296589],\n                            [106.560434, 30.31519],\n                            [106.611557, 30.292455],\n                            [106.642354, 30.246454],\n                            [106.612789, 30.235596],\n                            [106.612789, 30.235596],\n                            [106.612173, 30.235596],\n                            [106.612173, 30.235596],\n                            [106.611557, 30.235596],\n                            [106.612173, 30.235596],\n                            [106.611557, 30.235596],\n                            [106.631883, 30.186464],\n                            [106.677462, 30.156974],\n                            [106.672535, 30.122297],\n                            [106.700252, 30.111944],\n                            [106.699636, 30.074145],\n                            [106.724274, 30.058607],\n                            [106.732281, 30.027005],\n                            [106.785252, 30.01716],\n                            [106.825904, 30.03115],\n                            [106.825904, 30.03115],\n                            [106.83699, 30.049801],\n                            [106.862244, 30.033223],\n                            [106.913367, 30.025451],\n                            [106.94478, 30.037367],\n                            [106.976193, 30.083467],\n                            [106.975577, 30.088127],\n                            [106.976809, 30.088127],\n                            [106.977425, 30.087609],\n                            [106.978656, 30.087609],\n                            [106.979888, 30.088127],\n                            [106.980504, 30.087609],\n                            [106.981736, 30.08502],\n                            [107.02054, 30.036849],\n                            [107.053801, 30.043584],\n                            [107.058113, 30.043066],\n                            [107.084598, 30.063786],\n                            [107.080286, 30.094341],\n                            [107.103076, 30.090198],\n                            [107.221337, 30.213878],\n                            [107.257677, 30.267131],\n                            [107.288474, 30.337402],\n                            [107.338981, 30.386459],\n                            [107.368546, 30.468508],\n                            [107.408582, 30.521623],\n                            [107.443075, 30.53348],\n                            [107.427676, 30.547397],\n                            [107.485575, 30.598408],\n                            [107.516987, 30.644759],\n                            [107.477567, 30.664837],\n                            [107.458473, 30.704981],\n                            [107.424597, 30.74048],\n                            [107.454162, 30.771851],\n                            [107.454162, 30.771851],\n                            [107.498509, 30.809381],\n                            [107.483111, 30.838675],\n                            [107.515756, 30.854603],\n                            [107.57735, 30.847924],\n                            [107.645103, 30.821202],\n                            [107.693146, 30.875665],\n                            [107.739957, 30.884396],\n                            [107.760899, 30.862823],\n                            [107.763979, 30.817091],\n                            [107.788001, 30.81966],\n                            [107.851443, 30.792931],\n                            [107.956152, 30.882855],\n                            [107.994956, 30.908533],\n                            [107.948145, 30.918802],\n                            [107.942602, 30.989114],\n                            [107.983254, 30.983983],\n                            [108.00358, 31.025533],\n                            [108.060246, 31.052197],\n                            [108.026985, 31.061938],\n                            [108.009123, 31.109602],\n                            [108.025753, 31.116263],\n                            [108.089811, 31.204859],\n                            [108.07626, 31.231985],\n                            [108.031297, 31.217144],\n                            [108.038688, 31.252964],\n                            [108.095354, 31.268311],\n                            [108.185898, 31.336831],\n                            [108.153869, 31.371073],\n                            [108.216079, 31.41041],\n                            [108.224086, 31.464024],\n                            [108.193289, 31.467598],\n                            [108.191441, 31.492096],\n                            [108.233941, 31.506894],\n                            [108.254883, 31.49873],\n                            [108.344194, 31.512506],\n                            [108.339266, 31.539033],\n                            [108.386078, 31.544134],\n                            [108.390389, 31.591555],\n                            [108.442744, 31.633856],\n                            [108.468614, 31.636404],\n                            [108.519121, 31.665952],\n                            [108.546838, 31.665442],\n                            [108.514809, 31.693963],\n                            [108.50557, 31.734182],\n                            [108.535135, 31.757592],\n                            [108.462454, 31.780488],\n                            [108.455063, 31.814059],\n                            [108.429194, 31.809482],\n                            [108.391005, 31.829822],\n                            [108.386078, 31.854226],\n                            [108.343578, 31.860834],\n                            [108.259194, 31.967006],\n                            [108.307238, 31.997463],\n                            [108.351585, 31.971575],\n                            [108.370063, 31.988835],\n                            [108.329411, 32.020299],\n                            [108.362056, 32.035521],\n                            [108.344194, 32.067477],\n                            [108.372527, 32.077112],\n                            [108.42981, 32.061391],\n                            [108.452599, 32.090296],\n                            [108.399628, 32.147065],\n                            [108.379303, 32.153652],\n                            [108.379303, 32.153652],\n                            [108.379918, 32.154158],\n                            [108.379918, 32.154158],\n                            [108.370063, 32.172397],\n                            [108.399013, 32.194176],\n                            [108.480317, 32.182527],\n                            [108.509882, 32.201266],\n                            [108.507418, 32.245819],\n                            [108.469846, 32.270618],\n                            [108.414411, 32.252399],\n                            [108.389773, 32.263533],\n                            [108.310933, 32.232152],\n                            [108.240716, 32.274666],\n                            [108.179738, 32.221521],\n                            [108.156948, 32.239239],\n                            [108.143398, 32.219495],\n                            [108.086731, 32.233165],\n                            [108.018362, 32.2119],\n                            [108.024521, 32.177462],\n                            [107.979558, 32.146051],\n                            [107.924739, 32.197215],\n                            [107.890247, 32.214432],\n                            [107.864377, 32.201266],\n                            [107.812022, 32.247844],\n                            [107.753508, 32.338399],\n                            [107.707929, 32.331826],\n                            [107.680827, 32.397035],\n                            [107.648183, 32.413709],\n                            [107.598291, 32.411688],\n                            [107.527458, 32.38238],\n                            [107.489886, 32.425328],\n                            [107.456625, 32.41775],\n                            [107.460937, 32.453612],\n                            [107.438763, 32.465732],\n                            [107.436299, 32.529835],\n                            [107.382097, 32.54043],\n                            [107.356843, 32.506622],\n                            [107.313727, 32.489965],\n                            [107.287858, 32.457147],\n                            [107.263836, 32.403099],\n                            [107.212097, 32.428864],\n                            [107.189924, 32.468256],\n                            [107.127098, 32.482393],\n                            [107.080286, 32.542448],\n                            [107.108004, 32.600951],\n                            [107.098765, 32.649338],\n                            [107.05996, 32.686115],\n                            [107.066736, 32.708779],\n                            [107.012533, 32.721367],\n                            [106.912751, 32.704247],\n                            [106.903512, 32.721367],\n                            [106.854853, 32.724388],\n                            [106.82344, 32.705254],\n                            [106.793259, 32.712807],\n                            [106.783404, 32.735967],\n                            [106.733513, 32.739491],\n                            [106.670071, 32.694678],\n                            [106.626955, 32.682086],\n                            [106.585687, 32.68813],\n                            [106.517934, 32.668485],\n                            [106.498224, 32.649338],\n                            [106.451412, 32.65992],\n                            [106.421231, 32.616579],\n                            [106.389203, 32.62666],\n                            [106.347935, 32.671003],\n                            [106.301123, 32.680071],\n                            [106.267863, 32.673522],\n                            [106.254928, 32.693671],\n                            [106.17424, 32.6977],\n                            [106.120037, 32.719856],\n                            [106.071378, 32.758114],\n                            [106.07261, 32.76365],\n                            [106.093552, 32.82402],\n                            [106.071378, 32.828546],\n                            [106.044277, 32.864747],\n                            [106.011632, 32.829552],\n                            [105.969132, 32.849162],\n                            [105.93156, 32.826032],\n                            [105.893371, 32.838603],\n                            [105.849024, 32.817985],\n                            [105.825002, 32.824523],\n                            [105.822538, 32.770192],\n                            [105.779423, 32.750061],\n                            [105.768952, 32.767676],\n                            [105.719061, 32.759624],\n                            [105.677793, 32.726402],\n                            [105.596489, 32.69921],\n                            [105.585402, 32.728919],\n                            [105.563844, 32.724891],\n                            [105.555221, 32.794343],\n                            [105.534279, 32.790822],\n                            [105.524424, 32.847654],\n                            [105.495475, 32.873292],\n                            [105.49917, 32.911986],\n                            [105.467757, 32.930071],\n                            [105.414171, 32.922034],\n                            [105.408011, 32.885857],\n                            [105.38091, 32.876307],\n                            [105.396308, 32.85067],\n                            [105.396308, 32.85067],\n                            [105.427721, 32.784281],\n                            [105.454207, 32.767173],\n                            [105.448663, 32.732946],\n                            [105.368591, 32.712807],\n                            [105.347033, 32.68259],\n                            [105.297758, 32.656897],\n                            [105.263265, 32.652362],\n                            [105.219534, 32.666469],\n                            [105.215222, 32.63674],\n                            [105.185041, 32.617587],\n                            [105.111128, 32.593893],\n                            [105.0791, 32.637244],\n                            [105.026745, 32.650346],\n                            [104.925115, 32.607505],\n                            [104.881999, 32.600951],\n                            [104.845659, 32.653873],\n                            [104.820405, 32.662943],\n                            [104.795768, 32.643292],\n                            [104.739717, 32.635228],\n                            [104.696601, 32.673522],\n                            [104.643015, 32.661935],\n                            [104.592508, 32.695685],\n                            [104.582653, 32.722374],\n                            [104.526602, 32.728416],\n                            [104.51182, 32.753585],\n                            [104.458849, 32.748551],\n                            [104.363994, 32.822511],\n                            [104.294393, 32.835586],\n                            [104.277147, 32.90244],\n                            [104.288234, 32.942628],\n                            [104.345516, 32.940117],\n                            [104.378161, 32.953174],\n                            [104.383704, 32.994343],\n                            [104.426204, 33.010906],\n                            [104.391711, 33.035493],\n                            [104.337509, 33.038002],\n                            [104.378161, 33.109214],\n                            [104.351059, 33.158828],\n                            [104.32827, 33.223934],\n                            [104.323958, 33.26898],\n                            [104.303632, 33.304499],\n                            [104.333813, 33.315502],\n                            [104.386168, 33.298497],\n                            [104.420045, 33.327004],\n                            [104.373849, 33.345004],\n                            [104.292545, 33.336505],\n                            [104.272219, 33.391486],\n                            [104.22048, 33.404477],\n                            [104.213089, 33.446932],\n                            [104.180444, 33.472895],\n                            [104.155191, 33.542755],\n                            [104.176749, 33.5996],\n                            [104.103452, 33.663381],\n                            [104.046169, 33.686291],\n                            [103.980264, 33.670852],\n                            [103.861388, 33.682307],\n                            [103.778236, 33.658898],\n                            [103.690772, 33.69376],\n                            [103.667983, 33.685793],\n                            [103.645809, 33.708697],\n                            [103.593454, 33.716164],\n                            [103.563889, 33.699735],\n                            [103.552186, 33.671351],\n                            [103.520157, 33.678323],\n                            [103.545411, 33.719649],\n                            [103.518309, 33.807213],\n                            [103.464723, 33.80224],\n                            [103.434542, 33.752993],\n                            [103.35447, 33.743539],\n                            [103.278709, 33.774387],\n                            [103.284868, 33.80224],\n                            [103.24976, 33.814175],\n                            [103.228202, 33.79478],\n                            [103.165376, 33.805721],\n                            [103.153673, 33.819147],\n                            [103.181391, 33.900649],\n                            [103.16476, 33.929454],\n                            [103.1315, 33.931937],\n                            [103.120413, 33.953286],\n                            [103.157369, 33.998944],\n                            [103.147514, 34.036644],\n                            [103.119797, 34.03466],\n                            [103.129652, 34.065899],\n                            [103.178927, 34.079779],\n                            [103.121644, 34.112487],\n                            [103.124108, 34.162022],\n                            [103.100087, 34.181828],\n                            [103.052043, 34.195194],\n                            [103.005848, 34.184798],\n                            [102.973203, 34.205588],\n                            [102.977515, 34.252595],\n                            [102.949181, 34.292159],\n                            [102.911609, 34.312923],\n                            [102.85987, 34.301058],\n                            [102.856791, 34.270895],\n                            [102.798276, 34.272874],\n                            [102.779798, 34.236764],\n                            [102.728675, 34.235774],\n                            [102.694799, 34.198659],\n                            [102.664002, 34.192719],\n                            [102.651067, 34.165983],\n                            [102.598712, 34.14766],\n                            [102.655994, 34.113478],\n                            [102.649219, 34.080275],\n                            [102.615958, 34.099604],\n                            [102.511865, 34.086222],\n                            [102.471213, 34.072839],\n                            [102.437336, 34.087214],\n                            [102.406539, 34.033172],\n                            [102.392372, 33.971651],\n                            [102.345561, 33.969666],\n                            [102.315996, 33.993983],\n                            [102.287047, 33.977607],\n                            [102.248858, 33.98654],\n                            [102.226069, 33.963214],\n                            [102.16817, 33.983066],\n                            [102.136142, 33.965199],\n                            [102.25317, 33.861399],\n                            [102.261177, 33.821136],\n                            [102.243315, 33.786823],\n                            [102.296286, 33.783838],\n                            [102.324619, 33.754486],\n                            [102.284583, 33.719151],\n                            [102.342481, 33.725622],\n                            [102.31538, 33.665374],\n                            [102.346793, 33.605582],\n                            [102.440416, 33.574673],\n                            [102.477988, 33.543254],\n                            [102.446575, 33.53228],\n                            [102.461358, 33.501345],\n                            [102.462589, 33.449429],\n                            [102.447807, 33.454922],\n                            [102.392988, 33.404477],\n                            [102.368967, 33.41247],\n                            [102.310452, 33.397982],\n                            [102.296286, 33.413969],\n                            [102.258098, 33.409472],\n                            [102.218062, 33.349503],\n                            [102.192192, 33.337005],\n                            [102.217446, 33.247961],\n                            [102.200815, 33.223434],\n                            [102.160163, 33.242956],\n                            [102.144765, 33.273983],\n                            [102.117047, 33.288492],\n                            [102.08933, 33.227439],\n                            [102.08933, 33.204908],\n                            [102.054838, 33.189884],\n                            [101.99386, 33.1999],\n                            [101.935345, 33.186879],\n                            [101.921795, 33.153817],\n                            [101.887302, 33.135778],\n                            [101.865744, 33.103198],\n                            [101.825708, 33.119239],\n                            [101.841723, 33.184876],\n                            [101.83002, 33.213921],\n                            [101.770274, 33.248962],\n                            [101.769658, 33.26898],\n                            [101.877447, 33.314502],\n                            [101.887302, 33.383991],\n                            [101.915635, 33.425957],\n                            [101.946432, 33.442937],\n                            [101.906396, 33.48188],\n                            [101.907012, 33.539264],\n                            [101.884222, 33.578163],\n                            [101.844186, 33.602591],\n                            [101.831252, 33.554726],\n                            [101.783208, 33.556721],\n                            [101.769042, 33.538765],\n                            [101.777665, 33.533776],\n                            [101.769042, 33.45592],\n                            [101.695745, 33.433948],\n                            [101.663716, 33.383991],\n                            [101.64955, 33.323004],\n                            [101.677883, 33.297497],\n                            [101.735781, 33.279987],\n                            [101.709912, 33.21292],\n                            [101.653861, 33.162835],\n                            [101.661252, 33.135778],\n                            [101.633535, 33.101193],\n                            [101.557775, 33.167344],\n                            [101.515275, 33.192889],\n                            [101.487557, 33.226938],\n                            [101.403174, 33.225436],\n                            [101.386543, 33.207412],\n                            [101.393935, 33.157826],\n                            [101.381616, 33.153316],\n                            [101.297232, 33.262475],\n                            [101.217776, 33.256469],\n                            [101.182668, 33.26948],\n                            [101.156798, 33.236449],\n                            [101.124769, 33.221431],\n                            [101.11553, 33.194893],\n                            [101.169733, 33.10019],\n                            [101.143863, 33.086151],\n                            [101.146327, 33.056563],\n                            [101.184515, 33.041514],\n                            [101.171581, 33.009902],\n                            [101.183899, 32.984304],\n                            [101.129081, 32.989324],\n                            [101.134624, 32.95217],\n                            [101.124153, 32.909976],\n                            [101.178356, 32.892892],\n                            [101.223935, 32.855698],\n                            [101.237486, 32.825026],\n                            [101.22332, 32.725898],\n                            [101.157414, 32.661431],\n                            [101.124769, 32.658408],\n                            [101.077342, 32.68259],\n                            [101.030531, 32.660424],\n                            [100.99727, 32.627668],\n                            [100.956618, 32.621116],\n                            [100.93198, 32.600447],\n                            [100.887633, 32.632708],\n                            [100.834046, 32.648835],\n                            [100.77122, 32.643795],\n                            [100.690532, 32.678056],\n                            [100.71209, 32.645307],\n                            [100.710242, 32.610026],\n                            [100.673286, 32.628172],\n                            [100.661583, 32.616075],\n                            [100.657887, 32.546484],\n                            [100.645568, 32.526303],\n                            [100.603069, 32.553547],\n                            [100.54517, 32.569687],\n                            [100.516837, 32.632204],\n                            [100.470026, 32.694678],\n                            [100.450932, 32.694678],\n                            [100.420135, 32.73194],\n                            [100.378251, 32.698707],\n                            [100.399193, 32.756101],\n                            [100.339447, 32.719353],\n                            [100.258759, 32.742511],\n                            [100.231041, 32.696189],\n                            [100.229809, 32.650346],\n                            [100.208252, 32.606497],\n                            [100.189773, 32.630692],\n                            [100.109701, 32.640268],\n                            [100.088143, 32.668988],\n                            [100.139266, 32.724388],\n                            [100.117093, 32.802392],\n                            [100.123252, 32.837095],\n                            [100.064738, 32.895907],\n                            [100.029629, 32.895907],\n                            [100.038252, 32.929066],\n                            [99.956332, 32.948152],\n                            [99.947709, 32.986814],\n                            [99.877492, 33.045527],\n                            [99.877492, 32.993339],\n                            [99.851007, 32.941623],\n                            [99.805427, 32.940619],\n                            [99.788181, 32.956689],\n                            [99.764159, 32.924545],\n                            [99.791877, 32.883344],\n                            [99.766623, 32.826032],\n                            [99.760464, 32.769689],\n                            [99.717964, 32.732443],\n                            [99.700718, 32.76667],\n                            [99.646515, 32.774721],\n                            [99.640355, 32.790822],\n                            [99.589233, 32.789312],\n                            [99.558436, 32.839106],\n                            [99.45311, 32.862233],\n                            [99.376118, 32.899927],\n                            [99.353944, 32.885354],\n                            [99.268944, 32.878318],\n                            [99.24677, 32.924043],\n                            [99.235067, 32.982296],\n                            [99.214741, 32.991332],\n                            [99.196263, 33.035493],\n                            [99.124814, 33.046028],\n                            [99.090322, 33.079131],\n                            [99.024416, 33.094675],\n                            [99.014561, 33.081137],\n                            [98.971445, 33.098185],\n                            [98.967134, 33.115229],\n                            [98.92217, 33.118738],\n                            [98.858728, 33.150811],\n                            [98.804526, 33.219428],\n                            [98.802062, 33.270481],\n                            [98.759562, 33.276985],\n                            [98.779888, 33.370497],\n                            [98.736157, 33.406975],\n                            [98.742316, 33.477887],\n                            [98.725686, 33.503341],\n                            [98.678258, 33.522801],\n                            [98.648077, 33.548741],\n                            [98.652389, 33.595114],\n                            [98.622824, 33.610067],\n                            [98.61728, 33.637476],\n                            [98.6567, 33.64744],\n                            [98.610505, 33.682805],\n                            [98.582788, 33.731595],\n                            [98.539672, 33.746525],\n                            [98.51873, 33.77389],\n                            [98.494092, 33.768915],\n                            [98.492861, 33.796272],\n                            [98.463295, 33.848477],\n                            [98.434962, 33.843009],\n                            [98.407245, 33.867362],\n                            [98.425723, 33.913066],\n                            [98.415252, 33.956761],\n                            [98.440506, 33.981577],\n                            [98.428187, 34.029204],\n                            [98.396774, 34.053008],\n                            [98.399854, 34.085231],\n                            [98.344419, 34.094648],\n                            [98.258188, 34.083249],\n                            [98.206449, 34.08424],\n                            [98.158405, 34.107037],\n                            [98.098043, 34.122892],\n                            [98.028442, 34.122892],\n                            [97.95453, 34.190739],\n                            [97.898479, 34.209548],\n                            [97.8104, 34.207568],\n                            [97.796849, 34.199154],\n                            [97.796849, 34.199154],\n                            [97.789458, 34.182818],\n                            [97.789458, 34.182818],\n                            [97.766668, 34.158555],\n                            [97.665654, 34.126855],\n                            [97.70261, 34.036644],\n                            [97.652719, 33.998448],\n                            [97.660111, 33.956264],\n                            [97.629314, 33.919523],\n                            [97.601596, 33.929951],\n                            [97.52214, 33.903133],\n                            [97.503662, 33.912073],\n                            [97.460546, 33.887236],\n                            [97.395257, 33.889224],\n                            [97.398336, 33.848477],\n                            [97.371851, 33.842015],\n                            [97.373083, 33.817655],\n                            [97.406344, 33.795278],\n                            [97.422974, 33.754984],\n                            [97.418046, 33.728608],\n                            [97.435293, 33.682307],\n                            [97.415583, 33.605582],\n                            [97.450075, 33.582152],\n                            [97.523372, 33.577166],\n                            [97.511669, 33.520805],\n                            [97.552321, 33.465906],\n                            [97.625618, 33.461412],\n                            [97.674893, 33.432949],\n                            [97.754349, 33.409972],\n                            [97.676125, 33.341004],\n                            [97.622538, 33.337005],\n                            [97.607756, 33.263976],\n                            [97.548626, 33.203907],\n                            [97.487648, 33.168346],\n                            [97.498119, 33.137783],\n                            [97.487032, 33.107209],\n                            [97.517213, 33.097683],\n                            [97.542466, 33.035995],\n                            [97.499966, 33.011408],\n                            [97.523988, 32.988822],\n                            [97.438372, 32.976271],\n                            [97.375547, 32.956689],\n                            [97.347829, 32.895907],\n                            [97.376163, 32.886359],\n                            [97.392793, 32.828546],\n                            [97.386018, 32.77925],\n                            [97.429133, 32.714318],\n                            [97.42359, 32.70475],\n                            [97.48272, 32.654377],\n                            [97.535075, 32.638252],\n                            [97.543698, 32.62162],\n                            [97.607756, 32.614059],\n                            [97.616995, 32.586329],\n                            [97.700763, 32.53488],\n                            [97.730944, 32.527312],\n                            [97.795617, 32.521257],\n                            [97.80732, 32.50006],\n                            [97.863986, 32.499051],\n                            [97.880001, 32.486431],\n                            [97.940363, 32.482393],\n                            [98.079565, 32.415224],\n                            [98.107283, 32.391476],\n                            [98.125145, 32.401077],\n                            [98.218768, 32.342444],\n                            [98.208913, 32.318171],\n                            [98.23047, 32.262521],\n                            [98.218768, 32.234683],\n                            [98.260035, 32.208862],\n                            [98.303151, 32.121726],\n                            [98.357354, 32.087253],\n                            [98.404781, 32.045159],\n                            [98.402933, 32.026896],\n                            [98.434962, 32.007613],\n                            [98.432498, 31.922825],\n                            [98.399238, 31.895899],\n                            [98.426339, 31.856767],\n                            [98.414636, 31.832365],\n                            [98.461448, 31.800327],\n                            [98.508875, 31.751995],\n                            [98.516882, 31.717383],\n                            [98.545831, 31.717383],\n                            [98.553839, 31.660349],\n                            [98.619128, 31.591555],\n                            [98.651157, 31.57881],\n                            [98.696736, 31.538523],\n                            [98.714599, 31.508935],\n                            [98.844562, 31.429817],\n                            [98.84333, 31.416028],\n                            [98.887062, 31.37465],\n                            [98.810685, 31.306668],\n                            [98.805758, 31.279052],\n                            [98.773113, 31.249382],\n                            [98.691809, 31.333253],\n                            [98.643766, 31.338876],\n                            [98.616048, 31.3036],\n                            [98.60373, 31.257568],\n                            [98.62344, 31.221238],\n                            [98.602498, 31.192062],\n                            [98.675179, 31.15417],\n                            [98.710287, 31.1178],\n                            [98.712135, 31.082954],\n                            [98.736772, 31.049121],\n                            [98.774961, 31.031174],\n                            [98.806374, 30.995783],\n                            [98.797135, 30.948575],\n                            [98.774345, 30.908019],\n                            [98.797135, 30.87926],\n                            [98.850105, 30.849465],\n                            [98.904924, 30.782649],\n                            [98.957895, 30.765166],\n                            [98.963438, 30.728134],\n                            [98.907388, 30.698292],\n                            [98.92217, 30.609225],\n                            [98.939417, 30.598923],\n                            [98.926482, 30.569556],\n                            [98.932025, 30.521623],\n                            [98.965286, 30.449937],\n                            [98.967134, 30.33482],\n                            [98.986844, 30.280569],\n                            [98.970829, 30.260928],\n                            [98.993003, 30.215429],\n                            [98.9813, 30.182843],\n                            [98.989308, 30.151799],\n                            [99.044742, 30.079842],\n                            [99.036735, 30.053945],\n                            [99.055213, 29.958587],\n                            [99.068148, 29.931621],\n                            [99.0238, 29.846009],\n                            [99.018873, 29.792009],\n                            [98.992387, 29.677163],\n                            [99.014561, 29.607464],\n                            [99.052133, 29.563748],\n                            [99.044742, 29.520013],\n                            [99.066916, 29.421018],\n                            [99.058909, 29.417368],\n                            [99.075539, 29.316186],\n                            [99.114343, 29.243628],\n                            [99.113727, 29.221171],\n                            [99.105104, 29.162656],\n                            [99.118039, 29.100971],\n                            [99.113727, 29.07273],\n                            [99.132206, 28.94869],\n                            [99.123582, 28.890021],\n                            [99.103872, 28.841803],\n                            [99.114343, 28.765763],\n                            [99.134053, 28.734806],\n                            [99.126662, 28.698066],\n                            [99.147604, 28.640831],\n                            [99.183944, 28.58882],\n                            [99.170394, 28.566221],\n                            [99.191952, 28.494714],\n                            [99.187024, 28.44],\n                            [99.16485, 28.425264],\n                            [99.200575, 28.365774],\n                            [99.229524, 28.350502],\n                            [99.237531, 28.317842],\n                            [99.28927, 28.286227],\n                            [99.306516, 28.227714],\n                            [99.374886, 28.18183],\n                            [99.412458, 28.295186],\n                            [99.392748, 28.318369],\n                            [99.437095, 28.398419],\n                            [99.404451, 28.44421],\n                            [99.426625, 28.454207],\n                            [99.396444, 28.491032],\n                            [99.403219, 28.546246],\n                            [99.463581, 28.549401],\n                            [99.466045, 28.579886],\n                            [99.504233, 28.619294],\n                            [99.540573, 28.623497],\n                            [99.53195, 28.677591],\n                            [99.553508, 28.710664],\n                            [99.614486, 28.740054],\n                            [99.609559, 28.784122],\n                            [99.625573, 28.81454],\n                            [99.676696, 28.810345],\n                            [99.717964, 28.846521],\n                            [99.722275, 28.757369],\n                            [99.755536, 28.701216],\n                            [99.79434, 28.699116],\n                            [99.834992, 28.660788],\n                            [99.834376, 28.628225],\n                            [99.873181, 28.631902],\n                            [99.875644, 28.611939],\n                            [99.91876, 28.599329],\n                            [99.985281, 28.529422],\n                            [99.990209, 28.47683],\n                            [100.073977, 28.426317],\n                            [100.057346, 28.368934],\n                            [100.136803, 28.349975],\n                            [100.176223, 28.325218],\n                            [100.147274, 28.288862],\n                            [100.188541, 28.252493],\n                            [100.153433, 28.208202],\n                            [100.102926, 28.201873],\n                            [100.091223, 28.181302],\n                            [100.062274, 28.193962],\n                            [100.033325, 28.184467],\n                            [100.021006, 28.147008],\n                            [100.05673, 28.097922],\n                            [100.088759, 28.029269],\n                            [100.120788, 28.018703],\n                            [100.196549, 27.936254],\n                            [100.170063, 27.907699],\n                            [100.210715, 27.87702],\n                            [100.30865, 27.861149],\n                            [100.30865, 27.830457],\n                            [100.28586, 27.80611],\n                            [100.304954, 27.788639],\n                            [100.311729, 27.724028],\n                            [100.327744, 27.72032],\n                            [100.350534, 27.755809],\n                            [100.412127, 27.816167],\n                            [100.442924, 27.86644],\n                            [100.504518, 27.852154],\n                            [100.511294, 27.827811],\n                            [100.54517, 27.809286],\n                            [100.609228, 27.859033],\n                            [100.634482, 27.915631],\n                            [100.681293, 27.923035],\n                            [100.719481, 27.858503],\n                            [100.707162, 27.800816],\n                            [100.757053, 27.770107],\n                            [100.775532, 27.743098],\n                            [100.782307, 27.691708],\n                            [100.848212, 27.672099],\n                            [100.827886, 27.615904],\n                            [100.854988, 27.623858],\n                            [100.91227, 27.521473],\n                            [100.901183, 27.453517],\n                            [100.936908, 27.469448],\n                            [100.95169, 27.426961],\n                            [101.021907, 27.332899],\n                            [101.026219, 27.270679],\n                            [101.042233, 27.22173],\n                            [101.071798, 27.194585],\n                            [101.119226, 27.208957],\n                            [101.167885, 27.198311],\n                            [101.167885, 27.198311]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.264167, 30.20974],\n                            [106.260471, 30.207672],\n                            [106.260471, 30.204051],\n                            [106.260471, 30.19681],\n                            [106.264167, 30.20974]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.976809, 30.088127],\n                            [106.975577, 30.088127],\n                            [106.976193, 30.083467],\n                            [106.981736, 30.08502],\n                            [106.980504, 30.087609],\n                            [106.979888, 30.088127],\n                            [106.978656, 30.087609],\n                            [106.977425, 30.087609],\n                            [106.976809, 30.088127]\n                        ]\n                    ],\n                    [\n                        [\n                            [105.558916, 30.18543],\n                            [105.556453, 30.187499],\n                            [105.550909, 30.179222],\n                            [105.56138, 30.183878],\n                            [105.558916, 30.18543]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 520000,\n                \"name\": \"Guizhou\",\n                \"center\": [106.713478, 26.578343],\n                \"centroid\": [106.880455, 26.826368],\n                \"childrenNum\": 9,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 23,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [109.274262, 28.494714],\n                            [109.23361, 28.474726],\n                            [109.191726, 28.471043],\n                            [109.153538, 28.417369],\n                            [109.152306, 28.349975],\n                            [109.117198, 28.277795],\n                            [109.081473, 28.247749],\n                            [109.101799, 28.202401],\n                            [109.086401, 28.184467],\n                            [109.026655, 28.220331],\n                            [109.005713, 28.162837],\n                            [108.929952, 28.19027],\n                            [108.923793, 28.217167],\n                            [108.89546, 28.219804],\n                            [108.855424, 28.199764],\n                            [108.821547, 28.245113],\n                            [108.772888, 28.212949],\n                            [108.738395, 28.228241],\n                            [108.726692, 28.282011],\n                            [108.761801, 28.304143],\n                            [108.783359, 28.380518],\n                            [108.759953, 28.389995],\n                            [108.780279, 28.42579],\n                            [108.746402, 28.45105],\n                            [108.709446, 28.501026],\n                            [108.700207, 28.48209],\n                            [108.657091, 28.47683],\n                            [108.640461, 28.456838],\n                            [108.688504, 28.422106],\n                            [108.697127, 28.401051],\n                            [108.656475, 28.359981],\n                            [108.667562, 28.334173],\n                            [108.611512, 28.324691],\n                            [108.580099, 28.343128],\n                            [108.576403, 28.38631],\n                            [108.609048, 28.407368],\n                            [108.609664, 28.43579],\n                            [108.586874, 28.463678],\n                            [108.573939, 28.531],\n                            [108.610896, 28.539412],\n                            [108.604736, 28.590922],\n                            [108.636149, 28.621396],\n                            [108.575787, 28.659738],\n                            [108.50249, 28.63768],\n                            [108.501258, 28.626649],\n                            [108.439049, 28.634003],\n                            [108.332491, 28.679166],\n                            [108.347274, 28.736381],\n                            [108.385462, 28.772058],\n                            [108.386078, 28.803003],\n                            [108.352817, 28.815589],\n                            [108.346658, 28.859625],\n                            [108.357745, 28.893165],\n                            [108.345426, 28.943453],\n                            [108.319556, 28.961258],\n                            [108.297999, 29.045527],\n                            [108.306622, 29.079006],\n                            [108.277673, 29.091558],\n                            [108.256115, 29.040295],\n                            [108.193289, 29.072207],\n                            [108.150173, 29.053375],\n                            [108.070717, 29.086328],\n                            [108.026369, 29.039772],\n                            [107.925971, 29.032446],\n                            [107.908725, 29.007327],\n                            [107.882855, 29.00628],\n                            [107.867457, 28.960211],\n                            [107.810175, 28.984295],\n                            [107.823725, 29.034016],\n                            [107.784921, 29.048143],\n                            [107.810791, 29.139137],\n                            [107.749197, 29.199754],\n                            [107.700537, 29.141228],\n                            [107.659885, 29.162656],\n                            [107.605683, 29.164747],\n                            [107.589052, 29.150113],\n                            [107.570574, 29.218037],\n                            [107.486806, 29.174153],\n                            [107.441227, 29.203934],\n                            [107.401807, 29.184603],\n                            [107.408582, 29.138091],\n                            [107.427676, 29.128682],\n                            [107.412278, 29.094696],\n                            [107.369778, 29.091558],\n                            [107.395647, 29.041341],\n                            [107.364235, 29.00942],\n                            [107.396879, 28.993718],\n                            [107.412894, 28.960211],\n                            [107.441227, 28.943977],\n                            [107.41351, 28.911502],\n                            [107.383945, 28.848618],\n                            [107.339597, 28.845997],\n                            [107.327894, 28.810869],\n                            [107.261373, 28.792514],\n                            [107.24659, 28.76209],\n                            [107.219489, 28.772582],\n                            [107.210866, 28.817686],\n                            [107.227496, 28.836037],\n                            [107.194851, 28.838134],\n                            [107.206554, 28.868535],\n                            [107.14188, 28.887925],\n                            [107.016229, 28.882685],\n                            [107.019308, 28.861722],\n                            [106.983584, 28.851239],\n                            [106.988512, 28.776254],\n                            [106.951555, 28.766812],\n                            [106.923222, 28.809821],\n                            [106.872099, 28.777304],\n                            [106.845614, 28.780975],\n                            [106.824056, 28.756319],\n                            [106.86594, 28.690192],\n                            [106.889345, 28.695966],\n                            [106.866556, 28.624548],\n                            [106.830831, 28.623497],\n                            [106.807425, 28.589346],\n                            [106.784636, 28.626649],\n                            [106.756918, 28.607211],\n                            [106.77786, 28.563068],\n                            [106.73844, 28.554657],\n                            [106.726121, 28.51838],\n                            [106.747063, 28.467361],\n                            [106.708259, 28.450524],\n                            [106.697788, 28.47683],\n                            [106.632499, 28.503655],\n                            [106.564745, 28.485247],\n                            [106.567825, 28.523638],\n                            [106.615252, 28.549401],\n                            [106.606629, 28.593024],\n                            [106.63681, 28.622972],\n                            [106.618332, 28.645033],\n                            [106.651593, 28.649235],\n                            [106.617716, 28.66709],\n                            [106.6171, 28.691242],\n                            [106.587535, 28.691767],\n                            [106.56105, 28.719062],\n                            [106.561666, 28.756319],\n                            [106.474202, 28.832891],\n                            [106.45326, 28.817162],\n                            [106.461883, 28.761041],\n                            [106.492064, 28.742153],\n                            [106.528405, 28.677591],\n                            [106.502535, 28.661313],\n                            [106.49268, 28.591448],\n                            [106.466811, 28.586193],\n                            [106.504999, 28.544669],\n                            [106.477282, 28.530474],\n                            [106.403369, 28.569901],\n                            [106.37442, 28.525742],\n                            [106.379348, 28.479986],\n                            [106.349167, 28.473674],\n                            [106.304819, 28.505233],\n                            [106.2925, 28.537309],\n                            [106.254928, 28.539412],\n                            [106.184711, 28.58882],\n                            [106.17116, 28.629275],\n                            [106.14837, 28.642932],\n                            [106.103407, 28.636104],\n                            [106.085544, 28.681792],\n                            [106.030726, 28.694917],\n                            [106.001161, 28.743727],\n                            [105.966668, 28.761041],\n                            [105.937719, 28.686517],\n                            [105.889676, 28.670765],\n                            [105.884748, 28.595126],\n                            [105.808372, 28.599855],\n                            [105.78435, 28.610889],\n                            [105.757249, 28.590397],\n                            [105.74493, 28.616668],\n                            [105.712901, 28.586718],\n                            [105.693191, 28.58882],\n                            [105.68272, 28.534154],\n                            [105.62359, 28.517854],\n                            [105.612503, 28.438947],\n                            [105.643916, 28.431053],\n                            [105.655003, 28.362615],\n                            [105.639604, 28.324164],\n                            [105.68888, 28.284119],\n                            [105.730147, 28.271997],\n                            [105.737539, 28.30309],\n                            [105.76464, 28.308359],\n                            [105.76464, 28.308359],\n                            [105.78743, 28.335753],\n                            [105.824386, 28.306251],\n                            [105.848408, 28.255656],\n                            [105.889676, 28.237732],\n                            [105.860727, 28.159672],\n                            [105.895219, 28.119565],\n                            [105.943878, 28.143314],\n                            [105.975907, 28.107952],\n                            [106.093552, 28.162837],\n                            [106.145291, 28.162837],\n                            [106.206885, 28.134343],\n                            [106.266631, 28.066769],\n                            [106.246305, 28.011835],\n                            [106.286341, 28.007079],\n                            [106.328225, 27.952643],\n                            [106.307899, 27.936782],\n                            [106.304819, 27.899237],\n                            [106.325145, 27.898708],\n                            [106.337464, 27.859033],\n                            [106.306667, 27.808756],\n                            [106.242609, 27.767459],\n                            [106.193334, 27.75422],\n                            [106.120653, 27.779638],\n                            [106.063987, 27.776991],\n                            [106.023335, 27.746805],\n                            [105.985146, 27.749983],\n                            [105.92848, 27.729855],\n                            [105.922937, 27.746805],\n                            [105.868118, 27.732504],\n                            [105.848408, 27.707074],\n                            [105.76772, 27.7182],\n                            [105.722756, 27.706015],\n                            [105.720292, 27.683759],\n                            [105.664242, 27.683759],\n                            [105.62359, 27.666269],\n                            [105.605112, 27.715552],\n                            [105.560148, 27.71979],\n                            [105.508409, 27.769048],\n                            [105.44004, 27.775402],\n                            [105.353809, 27.748924],\n                            [105.308229, 27.704955],\n                            [105.29591, 27.631811],\n                            [105.304533, 27.611661],\n                            [105.25649, 27.582491],\n                            [105.232469, 27.546945],\n                            [105.260186, 27.514573],\n                            [105.234316, 27.489093],\n                            [105.233084, 27.436522],\n                            [105.182577, 27.367451],\n                            [105.184425, 27.392959],\n                            [105.120984, 27.418461],\n                            [105.068013, 27.418461],\n                            [105.01073, 27.379143],\n                            [104.913412, 27.327051],\n                            [104.871528, 27.290891],\n                            [104.851818, 27.299401],\n                            [104.856746, 27.332368],\n                            [104.824717, 27.3531],\n                            [104.77113, 27.317481],\n                            [104.7545, 27.345658],\n                            [104.611602, 27.306846],\n                            [104.570334, 27.331836],\n                            [104.539537, 27.327583],\n                            [104.497037, 27.414743],\n                            [104.467472, 27.414211],\n                            [104.363378, 27.467855],\n                            [104.30856, 27.407305],\n                            [104.295625, 27.37436],\n                            [104.247582, 27.336621],\n                            [104.248813, 27.291955],\n                            [104.210625, 27.297273],\n                            [104.173053, 27.263232],\n                            [104.113923, 27.338216],\n                            [104.084358, 27.330773],\n                            [104.01722, 27.383926],\n                            [104.015372, 27.429086],\n                            [103.956242, 27.425367],\n                            [103.932221, 27.443958],\n                            [103.905119, 27.38552],\n                            [103.903271, 27.347785],\n                            [103.874322, 27.331304],\n                            [103.865699, 27.28185],\n                            [103.80041, 27.26536],\n                            [103.801641, 27.250464],\n                            [103.748671, 27.210021],\n                            [103.696316, 27.126429],\n                            [103.63349, 27.12057],\n                            [103.620555, 27.096598],\n                            [103.652584, 27.092868],\n                            [103.659975, 27.065692],\n                            [103.614396, 27.079548],\n                            [103.601461, 27.061962],\n                            [103.623635, 27.035312],\n                            [103.623019, 27.007056],\n                            [103.675374, 27.051836],\n                            [103.704939, 27.049171],\n                            [103.73204, 27.018785],\n                            [103.753598, 26.963858],\n                            [103.775156, 26.951056],\n                            [103.763453, 26.905702],\n                            [103.779468, 26.87421],\n                            [103.722185, 26.851253],\n                            [103.705555, 26.794642],\n                            [103.725265, 26.742812],\n                            [103.773308, 26.716621],\n                            [103.759142, 26.689355],\n                            [103.748671, 26.623568],\n                            [103.763453, 26.585041],\n                            [103.815808, 26.55239],\n                            [103.819504, 26.529903],\n                            [103.865699, 26.512232],\n                            [103.953163, 26.521336],\n                            [104.008597, 26.511697],\n                            [104.067727, 26.51491],\n                            [104.068343, 26.573266],\n                            [104.121314, 26.638012],\n                            [104.160734, 26.646571],\n                            [104.222328, 26.620358],\n                            [104.268524, 26.617683],\n                            [104.274683, 26.633733],\n                            [104.313487, 26.612867],\n                            [104.353523, 26.620893],\n                            [104.398487, 26.686147],\n                            [104.424356, 26.709137],\n                            [104.468088, 26.644431],\n                            [104.459465, 26.602701],\n                            [104.488414, 26.579689],\n                            [104.556783, 26.590393],\n                            [104.579573, 26.568449],\n                            [104.57095, 26.524549],\n                            [104.598667, 26.520801],\n                            [104.638703, 26.477954],\n                            [104.631928, 26.451702],\n                            [104.665804, 26.434019],\n                            [104.664572, 26.397572],\n                            [104.684283, 26.3772],\n                            [104.659645, 26.335373],\n                            [104.592508, 26.317672],\n                            [104.542616, 26.253282],\n                            [104.548776, 26.226979],\n                            [104.518595, 26.165762],\n                            [104.52845, 26.114186],\n                            [104.499501, 26.070651],\n                            [104.460081, 26.085702],\n                            [104.470552, 26.009352],\n                            [104.438523, 25.92757],\n                            [104.414501, 25.909807],\n                            [104.441602, 25.868889],\n                            [104.42374, 25.841961],\n                            [104.397871, 25.76168],\n                            [104.370769, 25.730415],\n                            [104.328886, 25.760602],\n                            [104.310407, 25.647901],\n                            [104.332581, 25.598796],\n                            [104.389248, 25.595558],\n                            [104.428668, 25.576126],\n                            [104.436059, 25.520512],\n                            [104.418813, 25.499447],\n                            [104.434827, 25.472436],\n                            [104.44961, 25.495126],\n                            [104.483486, 25.494585],\n                            [104.524138, 25.526992],\n                            [104.556783, 25.524832],\n                            [104.543232, 25.400556],\n                            [104.566638, 25.402719],\n                            [104.615913, 25.364871],\n                            [104.646094, 25.356759],\n                            [104.639935, 25.295632],\n                            [104.689826, 25.296173],\n                            [104.736021, 25.268034],\n                            [104.816094, 25.262622],\n                            [104.826565, 25.235558],\n                            [104.806854, 25.224189],\n                            [104.822869, 25.170037],\n                            [104.801927, 25.163537],\n                            [104.753884, 25.214443],\n                            [104.724319, 25.195491],\n                            [104.732326, 25.167871],\n                            [104.695369, 25.122364],\n                            [104.685514, 25.078466],\n                            [104.619609, 25.060577],\n                            [104.684898, 25.054072],\n                            [104.713232, 24.996048],\n                            [104.663957, 24.964584],\n                            [104.635623, 24.903803],\n                            [104.586964, 24.872859],\n                            [104.539537, 24.813663],\n                            [104.542616, 24.75607],\n                            [104.529682, 24.731611],\n                            [104.595587, 24.709323],\n                            [104.628848, 24.660927],\n                            [104.703377, 24.645698],\n                            [104.729246, 24.617953],\n                            [104.771746, 24.659839],\n                            [104.841963, 24.676155],\n                            [104.865985, 24.730524],\n                            [104.899245, 24.752809],\n                            [105.03352, 24.787586],\n                            [105.026745, 24.815836],\n                            [105.039064, 24.872859],\n                            [105.077868, 24.918459],\n                            [105.09573, 24.92877],\n                            [105.131454, 24.959701],\n                            [105.157324, 24.958616],\n                            [105.178266, 24.985199],\n                            [105.212758, 24.995505],\n                            [105.251563, 24.967296],\n                            [105.267577, 24.929313],\n                            [105.334099, 24.9266],\n                            [105.365511, 24.943423],\n                            [105.428337, 24.930941],\n                            [105.457286, 24.87123],\n                            [105.493011, 24.833217],\n                            [105.497322, 24.809318],\n                            [105.573083, 24.797366],\n                            [105.607576, 24.803885],\n                            [105.617431, 24.78161],\n                            [105.70551, 24.768569],\n                            [105.767104, 24.719109],\n                            [105.827466, 24.702799],\n                            [105.863806, 24.729437],\n                            [105.942031, 24.725088],\n                            [105.961741, 24.677786],\n                            [106.024566, 24.633186],\n                            [106.047356, 24.684312],\n                            [106.113878, 24.714216],\n                            [106.150218, 24.762591],\n                            [106.173008, 24.760417],\n                            [106.206269, 24.851139],\n                            [106.197645, 24.885889],\n                            [106.145291, 24.954275],\n                            [106.191486, 24.95319],\n                            [106.215508, 24.981944],\n                            [106.253696, 24.971094],\n                            [106.304819, 24.973807],\n                            [106.332536, 24.988454],\n                            [106.442173, 25.019369],\n                            [106.450181, 25.033468],\n                            [106.519782, 25.054072],\n                            [106.551195, 25.082802],\n                            [106.590615, 25.08768],\n                            [106.63989, 25.132658],\n                            [106.644817, 25.164621],\n                            [106.691013, 25.179245],\n                            [106.732281, 25.162454],\n                            [106.764926, 25.183036],\n                            [106.787715, 25.17112],\n                            [106.853005, 25.186827],\n                            [106.888113, 25.181953],\n                            [106.904128, 25.231768],\n                            [106.933077, 25.250714],\n                            [106.975577, 25.232851],\n                            [107.013765, 25.275611],\n                            [107.012533, 25.352973],\n                            [106.987896, 25.358922],\n                            [106.963874, 25.437852],\n                            [106.996519, 25.442716],\n                            [107.015613, 25.495666],\n                            [107.066736, 25.50917],\n                            [107.064272, 25.559391],\n                            [107.185612, 25.578825],\n                            [107.205322, 25.607971],\n                            [107.228728, 25.604733],\n                            [107.232423, 25.556691],\n                            [107.263836, 25.543193],\n                            [107.336517, 25.461089],\n                            [107.308184, 25.432988],\n                            [107.318039, 25.401637],\n                            [107.358691, 25.393528],\n                            [107.375937, 25.411908],\n                            [107.420901, 25.392987],\n                            [107.409198, 25.347024],\n                            [107.432604, 25.289139],\n                            [107.481263, 25.299961],\n                            [107.489886, 25.276693],\n                            [107.472024, 25.213902],\n                            [107.512676, 25.209029],\n                            [107.576734, 25.256668],\n                            [107.599523, 25.250714],\n                            [107.632168, 25.310241],\n                            [107.659885, 25.316192],\n                            [107.661733, 25.258833],\n                            [107.696226, 25.219858],\n                            [107.700537, 25.194408],\n                            [107.741805, 25.24043],\n                            [107.762131, 25.229061],\n                            [107.760283, 25.188451],\n                            [107.789233, 25.15487],\n                            [107.762747, 25.125073],\n                            [107.839124, 25.115861],\n                            [107.872384, 25.141327],\n                            [107.928435, 25.155954],\n                            [108.001732, 25.196574],\n                            [108.080572, 25.193867],\n                            [108.115065, 25.210112],\n                            [108.143398, 25.269658],\n                            [108.152021, 25.324306],\n                            [108.142782, 25.390825],\n                            [108.193289, 25.405421],\n                            [108.162492, 25.444878],\n                            [108.192673, 25.458928],\n                            [108.251803, 25.430286],\n                            [108.241332, 25.46217],\n                            [108.280752, 25.48],\n                            [108.308469, 25.525912],\n                            [108.348506, 25.536173],\n                            [108.359592, 25.513491],\n                            [108.400244, 25.491344],\n                            [108.418723, 25.443257],\n                            [108.471693, 25.458928],\n                            [108.585642, 25.365952],\n                            [108.589338, 25.335125],\n                            [108.625062, 25.308076],\n                            [108.62999, 25.335666],\n                            [108.600425, 25.432448],\n                            [108.6072, 25.491885],\n                            [108.634917, 25.520512],\n                            [108.68912, 25.533473],\n                            [108.658323, 25.550212],\n                            [108.660787, 25.584763],\n                            [108.68604, 25.587462],\n                            [108.68912, 25.623081],\n                            [108.724844, 25.634952],\n                            [108.783975, 25.628477],\n                            [108.799989, 25.576666],\n                            [108.781511, 25.554531],\n                            [108.814772, 25.526992],\n                            [108.826474, 25.550212],\n                            [108.890532, 25.556151],\n                            [108.8893, 25.543193],\n                            [108.949046, 25.557231],\n                            [109.024807, 25.51241],\n                            [109.088249, 25.550752],\n                            [109.051908, 25.566949],\n                            [109.030966, 25.629556],\n                            [109.075314, 25.693749],\n                            [109.07901, 25.72071],\n                            [109.043285, 25.738502],\n                            [109.007561, 25.734728],\n                            [108.953974, 25.686738],\n                            [108.953974, 25.686738],\n                            [108.900387, 25.682423],\n                            [108.896076, 25.71424],\n                            [108.940423, 25.740119],\n                            [108.963829, 25.732572],\n                            [108.999553, 25.765453],\n                            [108.989698, 25.778926],\n                            [109.048213, 25.790781],\n                            [109.077778, 25.776771],\n                            [109.095024, 25.80533],\n                            [109.143683, 25.795092],\n                            [109.13198, 25.762758],\n                            [109.147995, 25.741736],\n                            [109.206509, 25.788087],\n                            [109.207125, 25.740119],\n                            [109.296436, 25.71424],\n                            [109.340168, 25.731493],\n                            [109.327849, 25.76168],\n                            [109.339552, 25.83442],\n                            [109.359262, 25.836036],\n                            [109.396834, 25.900117],\n                            [109.435022, 25.93349],\n                            [109.408537, 25.967392],\n                            [109.473211, 26.006663],\n                            [109.48245, 26.029788],\n                            [109.452885, 26.055598],\n                            [109.449805, 26.101826],\n                            [109.502776, 26.096451],\n                            [109.513863, 26.128157],\n                            [109.47629, 26.148035],\n                            [109.439334, 26.238789],\n                            [109.467051, 26.313917],\n                            [109.442414, 26.289774],\n                            [109.369733, 26.277432],\n                            [109.351255, 26.264016],\n                            [109.325385, 26.29031],\n                            [109.285965, 26.295676],\n                            [109.271183, 26.327863],\n                            [109.29582, 26.350389],\n                            [109.319842, 26.418477],\n                            [109.38082, 26.454381],\n                            [109.362342, 26.472061],\n                            [109.385747, 26.493487],\n                            [109.381436, 26.518659],\n                            [109.407305, 26.533116],\n                            [109.390675, 26.598955],\n                            [109.35495, 26.658873],\n                            [109.334008, 26.646036],\n                            [109.306291, 26.661012],\n                            [109.283501, 26.698445],\n                            [109.35495, 26.693098],\n                            [109.407305, 26.719829],\n                            [109.447957, 26.759913],\n                            [109.486761, 26.759913],\n                            [109.47629, 26.829894],\n                            [109.467051, 26.83203],\n                            [109.452885, 26.861932],\n                            [109.436254, 26.892359],\n                            [109.555131, 26.946788],\n                            [109.520022, 27.058764],\n                            [109.497848, 27.079548],\n                            [109.486761, 27.053968],\n                            [109.454733, 27.069423],\n                            [109.472595, 27.134951],\n                            [109.441182, 27.117907],\n                            [109.415312, 27.154123],\n                            [109.358646, 27.153058],\n                            [109.33524, 27.139212],\n                            [109.264407, 27.131755],\n                            [109.239154, 27.14933],\n                            [109.21698, 27.114711],\n                            [109.165857, 27.066758],\n                            [109.101183, 27.06889],\n                            [109.128901, 27.122701],\n                            [109.032814, 27.104056],\n                            [109.007561, 27.08008],\n                            [108.940423, 27.044907],\n                            [108.942887, 27.017186],\n                            [108.942887, 27.017186],\n                            [108.877597, 27.01612],\n                            [108.79075, 27.084343],\n                            [108.878829, 27.106187],\n                            [108.926873, 27.160512],\n                            [108.907778, 27.204699],\n                            [108.963213, 27.235565],\n                            [108.983539, 27.26802],\n                            [109.053756, 27.293551],\n                            [109.044517, 27.331304],\n                            [109.103647, 27.336621],\n                            [109.142451, 27.418461],\n                            [109.141835, 27.448207],\n                            [109.167089, 27.41793],\n                            [109.202197, 27.450331],\n                            [109.245313, 27.41793],\n                            [109.300132, 27.423774],\n                            [109.303211, 27.47582],\n                            [109.404841, 27.55066],\n                            [109.461508, 27.567637],\n                            [109.451037, 27.586204],\n                            [109.470131, 27.62863],\n                            [109.45658, 27.673689],\n                            [109.470747, 27.680049],\n                            [109.414081, 27.725087],\n                            [109.366653, 27.721909],\n                            [109.37774, 27.736741],\n                            [109.332777, 27.782815],\n                            [109.346943, 27.838396],\n                            [109.32169, 27.868027],\n                            [109.30198, 27.956343],\n                            [109.319842, 27.988585],\n                            [109.362342, 28.007608],\n                            [109.378972, 28.034551],\n                            [109.335856, 28.063073],\n                            [109.298284, 28.036136],\n                            [109.314298, 28.103729],\n                            [109.33832, 28.141731],\n                            [109.340168, 28.19027],\n                            [109.367885, 28.254602],\n                            [109.388211, 28.268307],\n                            [109.33524, 28.293605],\n                            [109.317994, 28.277795],\n                            [109.275494, 28.313101],\n                            [109.268719, 28.33786],\n                            [109.289045, 28.373673],\n                            [109.264407, 28.392628],\n                            [109.260712, 28.46473],\n                            [109.274262, 28.494714]\n                        ]\n                    ],\n                    [\n                        [\n                            [109.47629, 26.829894],\n                            [109.486761, 26.759913],\n                            [109.52187, 26.749226],\n                            [109.522486, 26.749226],\n                            [109.497232, 26.815474],\n                            [109.513247, 26.84004],\n                            [109.509551, 26.877947],\n                            [109.486761, 26.895562],\n                            [109.452885, 26.861932],\n                            [109.467051, 26.83203],\n                            [109.47629, 26.829894]\n                        ]\n                    ],\n                    [\n                        [\n                            [109.528645, 26.743881],\n                            [109.568065, 26.726243],\n                            [109.597015, 26.756173],\n                            [109.554515, 26.73533],\n                            [109.528645, 26.743881]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 530000,\n                \"name\": \"Yunnan\",\n                \"center\": [102.712251, 25.040609],\n                \"centroid\": [101.485106, 25.008643],\n                \"childrenNum\": 16,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 24,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [105.308229, 27.704955],\n                            [105.290367, 27.712373],\n                            [105.293447, 27.770637],\n                            [105.273736, 27.794992],\n                            [105.313157, 27.810874],\n                            [105.25957, 27.827811],\n                            [105.233084, 27.895534],\n                            [105.284823, 27.935725],\n                            [105.270657, 27.99704],\n                            [105.247867, 28.009193],\n                            [105.218302, 27.990698],\n                            [105.186273, 27.995454],\n                            [105.167795, 28.021345],\n                            [105.186889, 28.054623],\n                            [105.168411, 28.071522],\n                            [105.119752, 28.07205],\n                            [105.061853, 28.096866],\n                            [105.002107, 28.064129],\n                            [104.980549, 28.063073],\n                            [104.975006, 28.020816],\n                            [104.903557, 27.962158],\n                            [104.918339, 27.938897],\n                            [104.888158, 27.914574],\n                            [104.842579, 27.900294],\n                            [104.796999, 27.901352],\n                            [104.761891, 27.884426],\n                            [104.743413, 27.901881],\n                            [104.676275, 27.880723],\n                            [104.63316, 27.850567],\n                            [104.607906, 27.857974],\n                            [104.573413, 27.840512],\n                            [104.52537, 27.889187],\n                            [104.508124, 27.878078],\n                            [104.44961, 27.927794],\n                            [104.40095, 27.952114],\n                            [104.362762, 28.012891],\n                            [104.30856, 28.036136],\n                            [104.304248, 28.050926],\n                            [104.373233, 28.051454],\n                            [104.40095, 28.091586],\n                            [104.448994, 28.113758],\n                            [104.444682, 28.16231],\n                            [104.406494, 28.173389],\n                            [104.402182, 28.202928],\n                            [104.442834, 28.211366],\n                            [104.462544, 28.241422],\n                            [104.44961, 28.269889],\n                            [104.420045, 28.269889],\n                            [104.392943, 28.291497],\n                            [104.384936, 28.329959],\n                            [104.343052, 28.334173],\n                            [104.314103, 28.306778],\n                            [104.282074, 28.343128],\n                            [104.254357, 28.403683],\n                            [104.267908, 28.499448],\n                            [104.260516, 28.536257],\n                            [104.323342, 28.540989],\n                            [104.355987, 28.555183],\n                            [104.375697, 28.5946],\n                            [104.417581, 28.598279],\n                            [104.425588, 28.626649],\n                            [104.372617, 28.649235],\n                            [104.314719, 28.615617],\n                            [104.277147, 28.631902],\n                            [104.252509, 28.660788],\n                            [104.230951, 28.635579],\n                            [104.170589, 28.642932],\n                            [104.117618, 28.634003],\n                            [104.09606, 28.603533],\n                            [104.05972, 28.6277],\n                            [103.953779, 28.600906],\n                            [103.910047, 28.631377],\n                            [103.887873, 28.61982],\n                            [103.850917, 28.66709],\n                            [103.833054, 28.605109],\n                            [103.838598, 28.587244],\n                            [103.802873, 28.563068],\n                            [103.781931, 28.525216],\n                            [103.829975, 28.459995],\n                            [103.828743, 28.44],\n                            [103.860156, 28.383677],\n                            [103.85338, 28.356822],\n                            [103.877402, 28.316262],\n                            [103.828743, 28.285173],\n                            [103.770845, 28.233514],\n                            [103.740048, 28.23615],\n                            [103.701859, 28.198709],\n                            [103.692004, 28.232459],\n                            [103.643961, 28.260401],\n                            [103.573128, 28.230877],\n                            [103.533092, 28.168641],\n                            [103.470266, 28.122204],\n                            [103.430846, 28.044587],\n                            [103.459179, 28.021345],\n                            [103.486281, 28.033495],\n                            [103.515846, 27.965329],\n                            [103.55465, 27.978543],\n                            [103.502295, 27.910343],\n                            [103.509686, 27.843687],\n                            [103.487512, 27.794992],\n                            [103.461027, 27.779638],\n                            [103.393274, 27.709194],\n                            [103.369868, 27.708664],\n                            [103.349542, 27.678459],\n                            [103.29226, 27.632872],\n                            [103.2861, 27.561802],\n                            [103.232514, 27.56976],\n                            [103.19063, 27.523596],\n                            [103.144434, 27.450331],\n                            [103.141355, 27.420586],\n                            [103.080992, 27.396679],\n                            [103.055739, 27.40943],\n                            [102.989833, 27.367983],\n                            [102.941174, 27.405711],\n                            [102.899906, 27.317481],\n                            [102.883892, 27.299401],\n                            [102.883276, 27.258444],\n                            [102.904218, 27.227584],\n                            [102.913457, 27.133886],\n                            [102.870957, 27.026782],\n                            [102.894979, 27.001724],\n                            [102.896211, 26.91264],\n                            [102.949181, 26.843244],\n                            [102.966428, 26.837904],\n                            [102.991681, 26.775409],\n                            [102.983674, 26.76686],\n                            [103.008312, 26.710741],\n                            [103.005232, 26.679195],\n                            [103.026174, 26.664221],\n                            [103.035413, 26.556673],\n                            [103.052659, 26.555602],\n                            [103.052659, 26.514374],\n                            [103.030485, 26.485989],\n                            [102.989833, 26.482775],\n                            [102.988602, 26.413117],\n                            [102.998457, 26.371839],\n                            [102.975667, 26.340736],\n                            [102.893131, 26.338591],\n                            [102.878964, 26.364332],\n                            [102.833385, 26.306406],\n                            [102.785342, 26.298895],\n                            [102.739762, 26.268846],\n                            [102.709581, 26.210336],\n                            [102.659074, 26.221611],\n                            [102.60056, 26.250598],\n                            [102.638748, 26.307479],\n                            [102.629509, 26.336982],\n                            [102.570995, 26.362723],\n                            [102.542046, 26.338591],\n                            [102.440416, 26.300505],\n                            [102.392372, 26.296749],\n                            [102.349257, 26.244694],\n                            [102.245163, 26.212483],\n                            [102.242699, 26.190468],\n                            [102.174946, 26.146961],\n                            [102.152156, 26.10935],\n                            [102.107808, 26.068501],\n                            [102.080091, 26.065275],\n                            [102.020961, 26.096451],\n                            [101.954439, 26.084627],\n                            [101.929186, 26.105588],\n                            [101.899621, 26.099139],\n                            [101.857737, 26.049146],\n                            [101.835563, 26.04592],\n                            [101.839875, 26.082477],\n                            [101.796759, 26.114723],\n                            [101.807846, 26.156093],\n                            [101.773353, 26.168448],\n                            [101.737013, 26.219463],\n                            [101.690202, 26.241473],\n                            [101.630455, 26.224832],\n                            [101.586108, 26.279579],\n                            [101.597195, 26.303187],\n                            [101.64031, 26.318745],\n                            [101.660636, 26.346635],\n                            [101.635383, 26.357361],\n                            [101.637847, 26.388995],\n                            [101.565782, 26.454381],\n                            [101.530057, 26.467239],\n                            [101.506652, 26.499915],\n                            [101.458608, 26.49563],\n                            [101.422884, 26.53151],\n                            [101.395783, 26.591998],\n                            [101.402558, 26.604841],\n                            [101.461688, 26.606447],\n                            [101.461072, 26.640687],\n                            [101.481398, 26.673313],\n                            [101.453065, 26.692563],\n                            [101.513427, 26.768463],\n                            [101.466, 26.786629],\n                            [101.445674, 26.77434],\n                            [101.458608, 26.731054],\n                            [101.435819, 26.740675],\n                            [101.389623, 26.723036],\n                            [101.387159, 26.753501],\n                            [101.358826, 26.771669],\n                            [101.399478, 26.841642],\n                            [101.365602, 26.883819],\n                            [101.311399, 26.903034],\n                            [101.267667, 26.903034],\n                            [101.264587, 26.955323],\n                            [101.227015, 26.959057],\n                            [101.228863, 26.981992],\n                            [101.136472, 27.023584],\n                            [101.157414, 27.094999],\n                            [101.145095, 27.103523],\n                            [101.170349, 27.175421],\n                            [101.167885, 27.198311],\n                            [101.167885, 27.198311],\n                            [101.119226, 27.208957],\n                            [101.071798, 27.194585],\n                            [101.042233, 27.22173],\n                            [101.026219, 27.270679],\n                            [101.021907, 27.332899],\n                            [100.95169, 27.426961],\n                            [100.936908, 27.469448],\n                            [100.901183, 27.453517],\n                            [100.91227, 27.521473],\n                            [100.854988, 27.623858],\n                            [100.827886, 27.615904],\n                            [100.848212, 27.672099],\n                            [100.782307, 27.691708],\n                            [100.775532, 27.743098],\n                            [100.757053, 27.770107],\n                            [100.707162, 27.800816],\n                            [100.719481, 27.858503],\n                            [100.681293, 27.923035],\n                            [100.634482, 27.915631],\n                            [100.609228, 27.859033],\n                            [100.54517, 27.809286],\n                            [100.511294, 27.827811],\n                            [100.504518, 27.852154],\n                            [100.442924, 27.86644],\n                            [100.412127, 27.816167],\n                            [100.350534, 27.755809],\n                            [100.327744, 27.72032],\n                            [100.311729, 27.724028],\n                            [100.304954, 27.788639],\n                            [100.28586, 27.80611],\n                            [100.30865, 27.830457],\n                            [100.30865, 27.861149],\n                            [100.210715, 27.87702],\n                            [100.170063, 27.907699],\n                            [100.196549, 27.936254],\n                            [100.120788, 28.018703],\n                            [100.088759, 28.029269],\n                            [100.05673, 28.097922],\n                            [100.021006, 28.147008],\n                            [100.033325, 28.184467],\n                            [100.062274, 28.193962],\n                            [100.091223, 28.181302],\n                            [100.102926, 28.201873],\n                            [100.153433, 28.208202],\n                            [100.188541, 28.252493],\n                            [100.147274, 28.288862],\n                            [100.176223, 28.325218],\n                            [100.136803, 28.349975],\n                            [100.057346, 28.368934],\n                            [100.073977, 28.426317],\n                            [99.990209, 28.47683],\n                            [99.985281, 28.529422],\n                            [99.91876, 28.599329],\n                            [99.875644, 28.611939],\n                            [99.873181, 28.631902],\n                            [99.834376, 28.628225],\n                            [99.834992, 28.660788],\n                            [99.79434, 28.699116],\n                            [99.755536, 28.701216],\n                            [99.722275, 28.757369],\n                            [99.717964, 28.846521],\n                            [99.676696, 28.810345],\n                            [99.625573, 28.81454],\n                            [99.609559, 28.784122],\n                            [99.614486, 28.740054],\n                            [99.553508, 28.710664],\n                            [99.53195, 28.677591],\n                            [99.540573, 28.623497],\n                            [99.504233, 28.619294],\n                            [99.466045, 28.579886],\n                            [99.463581, 28.549401],\n                            [99.403219, 28.546246],\n                            [99.396444, 28.491032],\n                            [99.426625, 28.454207],\n                            [99.404451, 28.44421],\n                            [99.437095, 28.398419],\n                            [99.392748, 28.318369],\n                            [99.412458, 28.295186],\n                            [99.374886, 28.18183],\n                            [99.306516, 28.227714],\n                            [99.28927, 28.286227],\n                            [99.237531, 28.317842],\n                            [99.229524, 28.350502],\n                            [99.200575, 28.365774],\n                            [99.16485, 28.425264],\n                            [99.187024, 28.44],\n                            [99.191952, 28.494714],\n                            [99.170394, 28.566221],\n                            [99.183944, 28.58882],\n                            [99.147604, 28.640831],\n                            [99.126662, 28.698066],\n                            [99.134053, 28.734806],\n                            [99.114343, 28.765763],\n                            [99.103872, 28.841803],\n                            [99.123582, 28.890021],\n                            [99.132206, 28.94869],\n                            [99.113727, 29.07273],\n                            [99.118039, 29.100971],\n                            [99.105104, 29.162656],\n                            [99.113727, 29.221171],\n                            [99.037351, 29.20759],\n                            [99.024416, 29.188783],\n                            [98.9813, 29.204978],\n                            [98.960974, 29.165792],\n                            [98.967134, 29.128159],\n                            [98.991771, 29.105677],\n                            [99.013329, 29.036632],\n                            [98.925866, 28.978536],\n                            [98.917859, 28.886877],\n                            [98.973909, 28.864867],\n                            [98.972677, 28.832367],\n                            [98.922786, 28.823978],\n                            [98.912931, 28.800906],\n                            [98.852569, 28.798283],\n                            [98.827932, 28.821356],\n                            [98.821772, 28.920931],\n                            [98.786048, 28.998952],\n                            [98.757714, 29.004186],\n                            [98.70228, 28.9644],\n                            [98.655469, 28.976966],\n                            [98.624056, 28.95864],\n                            [98.6567, 28.910454],\n                            [98.643766, 28.895261],\n                            [98.668403, 28.843376],\n                            [98.652389, 28.817162],\n                            [98.683802, 28.740054],\n                            [98.666555, 28.712239],\n                            [98.594491, 28.667615],\n                            [98.637606, 28.552029],\n                            [98.619128, 28.50944],\n                            [98.625903, 28.489455],\n                            [98.673947, 28.478934],\n                            [98.693041, 28.43158],\n                            [98.740468, 28.348395],\n                            [98.746628, 28.321003],\n                            [98.710287, 28.288862],\n                            [98.712135, 28.229296],\n                            [98.649925, 28.200291],\n                            [98.625903, 28.165475],\n                            [98.559382, 28.182885],\n                            [98.494092, 28.141203],\n                            [98.464527, 28.151229],\n                            [98.428803, 28.104785],\n                            [98.389383, 28.114814],\n                            [98.389999, 28.16442],\n                            [98.370289, 28.18394],\n                            [98.37768, 28.246167],\n                            [98.353042, 28.293078],\n                            [98.317934, 28.324691],\n                            [98.301303, 28.384204],\n                            [98.208913, 28.358401],\n                            [98.207681, 28.330486],\n                            [98.231702, 28.314681],\n                            [98.266811, 28.242477],\n                            [98.21692, 28.212949],\n                            [98.169492, 28.206093],\n                            [98.17442, 28.163365],\n                            [98.139311, 28.142259],\n                            [98.160253, 28.101089],\n                            [98.133152, 27.990698],\n                            [98.143007, 27.948942],\n                            [98.187355, 27.939426],\n                            [98.205217, 27.889716],\n                            [98.169492, 27.851096],\n                            [98.215688, 27.810874],\n                            [98.234166, 27.690648],\n                            [98.283441, 27.654608],\n                            [98.310542, 27.583552],\n                            [98.317318, 27.51935],\n                            [98.337644, 27.508734],\n                            [98.388767, 27.515104],\n                            [98.429419, 27.549068],\n                            [98.430035, 27.653547],\n                            [98.444201, 27.665209],\n                            [98.474998, 27.634462],\n                            [98.53536, 27.620676],\n                            [98.554454, 27.646126],\n                            [98.587099, 27.587265],\n                            [98.583404, 27.571351],\n                            [98.650541, 27.567637],\n                            [98.662244, 27.586734],\n                            [98.706591, 27.553313],\n                            [98.685034, 27.484315],\n                            [98.704744, 27.462014],\n                            [98.686881, 27.425367],\n                            [98.702896, 27.412618],\n                            [98.706591, 27.362136],\n                            [98.741084, 27.330241],\n                            [98.734925, 27.287168],\n                            [98.717062, 27.271211],\n                            [98.723222, 27.221198],\n                            [98.696121, 27.211086],\n                            [98.713983, 27.139744],\n                            [98.712751, 27.075817],\n                            [98.765722, 27.05077],\n                            [98.762642, 27.018252],\n                            [98.732461, 27.002257],\n                            [98.757098, 26.877947],\n                            [98.730613, 26.851253],\n                            [98.762026, 26.798916],\n                            [98.746012, 26.696841],\n                            [98.770033, 26.690424],\n                            [98.762642, 26.660478],\n                            [98.781736, 26.620893],\n                            [98.773113, 26.578083],\n                            [98.753403, 26.559349],\n                            [98.757098, 26.491881],\n                            [98.741084, 26.432947],\n                            [98.750323, 26.424372],\n                            [98.733693, 26.350926],\n                            [98.681338, 26.308016],\n                            [98.672715, 26.239863],\n                            [98.713367, 26.231274],\n                            [98.735541, 26.185097],\n                            [98.712751, 26.156093],\n                            [98.720142, 26.127082],\n                            [98.661012, 26.087852],\n                            [98.656084, 26.139977],\n                            [98.632679, 26.145887],\n                            [98.575396, 26.118485],\n                            [98.602498, 26.054523],\n                            [98.614201, 25.968468],\n                            [98.637606, 25.971696],\n                            [98.686881, 25.925955],\n                            [98.705976, 25.855426],\n                            [98.677642, 25.816105],\n                            [98.640686, 25.798864],\n                            [98.553839, 25.845731],\n                            [98.529201, 25.840884],\n                            [98.476846, 25.77731],\n                            [98.461448, 25.735267],\n                            [98.457752, 25.682963],\n                            [98.409709, 25.664084],\n                            [98.402317, 25.593939],\n                            [98.326557, 25.566409],\n                            [98.314854, 25.543193],\n                            [98.247717, 25.607971],\n                            [98.170724, 25.620383],\n                            [98.189818, 25.569108],\n                            [98.163949, 25.524292],\n                            [98.131304, 25.51025],\n                            [98.15779, 25.457307],\n                            [98.137464, 25.381633],\n                            [98.101123, 25.388662],\n                            [98.099891, 25.354055],\n                            [98.06971, 25.311864],\n                            [98.006884, 25.298338],\n                            [98.0075, 25.279399],\n                            [97.940363, 25.214985],\n                            [97.904023, 25.216609],\n                            [97.875689, 25.25721],\n                            [97.839349, 25.27074],\n                            [97.796233, 25.155954],\n                            [97.743262, 25.078466],\n                            [97.719857, 25.080634],\n                            [97.727864, 25.04377],\n                            [97.716777, 24.978147],\n                            [97.729712, 24.908689],\n                            [97.785762, 24.876117],\n                            [97.797465, 24.845709],\n                            [97.765436, 24.823984],\n                            [97.680437, 24.827243],\n                            [97.652103, 24.790846],\n                            [97.569567, 24.765852],\n                            [97.547394, 24.739221],\n                            [97.569567, 24.708236],\n                            [97.570799, 24.602719],\n                            [97.554785, 24.490577],\n                            [97.530147, 24.443187],\n                            [97.588662, 24.435559],\n                            [97.669966, 24.452993],\n                            [97.679821, 24.401228],\n                            [97.716161, 24.358711],\n                            [97.662574, 24.339083],\n                            [97.665038, 24.296544],\n                            [97.721089, 24.295999],\n                            [97.767284, 24.258357],\n                            [97.729712, 24.227252],\n                            [97.72848, 24.183585],\n                            [97.754349, 24.163929],\n                            [97.748806, 24.160653],\n                            [97.743262, 24.159561],\n                            [97.730944, 24.113685],\n                            [97.700763, 24.093473],\n                            [97.697067, 24.092927],\n                            [97.637321, 24.04812],\n                            [97.628698, 24.004938],\n                            [97.572647, 23.983068],\n                            [97.529531, 23.943146],\n                            [97.5283, 23.926736],\n                            [97.618227, 23.888438],\n                            [97.640401, 23.866001],\n                            [97.647176, 23.840823],\n                            [97.684132, 23.876946],\n                            [97.718009, 23.867643],\n                            [97.72848, 23.895551],\n                            [97.763588, 23.907041],\n                            [97.795617, 23.951897],\n                            [97.8104, 23.943146],\n                            [97.863371, 23.978693],\n                            [97.896015, 23.974319],\n                            [97.902175, 24.014231],\n                            [97.984095, 24.031177],\n                            [97.995182, 24.04648],\n                            [98.091268, 24.085824],\n                            [98.096196, 24.08637],\n                            [98.123297, 24.092927],\n                            [98.125761, 24.092927],\n                            [98.132536, 24.09238],\n                            [98.19721, 24.09839],\n                            [98.219999, 24.113685],\n                            [98.343187, 24.098936],\n                            [98.37768, 24.114232],\n                            [98.48239, 24.122425],\n                            [98.487933, 24.123517],\n                            [98.547063, 24.128433],\n                            [98.593875, 24.08036],\n                            [98.646229, 24.106038],\n                            [98.681954, 24.100029],\n                            [98.71891, 24.127887],\n                            [98.818692, 24.133348],\n                            [98.841482, 24.126794],\n                            [98.876591, 24.15137],\n                            [98.895069, 24.098936],\n                            [98.807606, 24.025164],\n                            [98.773729, 24.022431],\n                            [98.727533, 23.970491],\n                            [98.701048, 23.981427],\n                            [98.673331, 23.960647],\n                            [98.701048, 23.946427],\n                            [98.68565, 23.90157],\n                            [98.701664, 23.834254],\n                            [98.669019, 23.800857],\n                            [98.696121, 23.784429],\n                            [98.784816, 23.781691],\n                            [98.824236, 23.727462],\n                            [98.811917, 23.703354],\n                            [98.835939, 23.683625],\n                            [98.847026, 23.632097],\n                            [98.882134, 23.620035],\n                            [98.882134, 23.595358],\n                            [98.844562, 23.578904],\n                            [98.80391, 23.540504],\n                            [98.826084, 23.470257],\n                            [98.874743, 23.483431],\n                            [98.912315, 23.426333],\n                            [98.920938, 23.360971],\n                            [98.872895, 23.329651],\n                            [98.906772, 23.331849],\n                            [98.936953, 23.309866],\n                            [98.928946, 23.26589],\n                            [98.889525, 23.209249],\n                            [98.906772, 23.185595],\n                            [99.002242, 23.160287],\n                            [99.057677, 23.164689],\n                            [99.048438, 23.11461],\n                            [99.106336, 23.086536],\n                            [99.187024, 23.100299],\n                            [99.255393, 23.077727],\n                            [99.281879, 23.101399],\n                            [99.3484, 23.12892],\n                            [99.380429, 23.099748],\n                            [99.440791, 23.079379],\n                            [99.477747, 23.083233],\n                            [99.528255, 23.065614],\n                            [99.517168, 23.006685],\n                            [99.533798, 22.961507],\n                            [99.563363, 22.925684],\n                            [99.531334, 22.897019],\n                            [99.446951, 22.934503],\n                            [99.43648, 22.913557],\n                            [99.462965, 22.844635],\n                            [99.401371, 22.826434],\n                            [99.385357, 22.761882],\n                            [99.326842, 22.751396],\n                            [99.31514, 22.737598],\n                            [99.339777, 22.708894],\n                            [99.385973, 22.57136],\n                            [99.359487, 22.535435],\n                            [99.382277, 22.493418],\n                            [99.297277, 22.41156],\n                            [99.251698, 22.393301],\n                            [99.278183, 22.34626],\n                            [99.233836, 22.296434],\n                            [99.235683, 22.250468],\n                            [99.207966, 22.232188],\n                            [99.175321, 22.185647],\n                            [99.188256, 22.162924],\n                            [99.156227, 22.159599],\n                            [99.219669, 22.110816],\n                            [99.294814, 22.109152],\n                            [99.35456, 22.095845],\n                            [99.400139, 22.100281],\n                            [99.486987, 22.128557],\n                            [99.516552, 22.099726],\n                            [99.562747, 22.113034],\n                            [99.578762, 22.098617],\n                            [99.581841, 22.103053],\n                            [99.648979, 22.100835],\n                            [99.696406, 22.067562],\n                            [99.762927, 22.068117],\n                            [99.870101, 22.029288],\n                            [99.871333, 22.067007],\n                            [99.972347, 22.053141],\n                            [99.965571, 22.014309],\n                            [100.000064, 21.973245],\n                            [99.982202, 21.919401],\n                            [99.960028, 21.907186],\n                            [99.944014, 21.821097],\n                            [99.991441, 21.703821],\n                            [100.049339, 21.669899],\n                            [100.094303, 21.702709],\n                            [100.131875, 21.699929],\n                            [100.169447, 21.663225],\n                            [100.107853, 21.585337],\n                            [100.123252, 21.565302],\n                            [100.131259, 21.504066],\n                            [100.168831, 21.482906],\n                            [100.184846, 21.516315],\n                            [100.206404, 21.509634],\n                            [100.235353, 21.466756],\n                            [100.298795, 21.477894],\n                            [100.349302, 21.528564],\n                            [100.437381, 21.533017],\n                            [100.48296, 21.458958],\n                            [100.526692, 21.471211],\n                            [100.579047, 21.451717],\n                            [100.691764, 21.510748],\n                            [100.730568, 21.518542],\n                            [100.753358, 21.555283],\n                            [100.789082, 21.570867],\n                            [100.804481, 21.609821],\n                            [100.847597, 21.634856],\n                            [100.870386, 21.67268],\n                            [100.896872, 21.68269],\n                            [100.899335, 21.684915],\n                            [100.936292, 21.694368],\n                            [100.937524, 21.693812],\n                            [101.015132, 21.707157],\n                            [101.089661, 21.773865],\n                            [101.123537, 21.771642],\n                            [101.111835, 21.746074],\n                            [101.116762, 21.691032],\n                            [101.153102, 21.669343],\n                            [101.169117, 21.590345],\n                            [101.146943, 21.560293],\n                            [101.209153, 21.55751],\n                            [101.210385, 21.509077],\n                            [101.225167, 21.499055],\n                            [101.193138, 21.473996],\n                            [101.194986, 21.424979],\n                            [101.142631, 21.409379],\n                            [101.183899, 21.334699],\n                            [101.244877, 21.302364],\n                            [101.246725, 21.275598],\n                            [101.222088, 21.234324],\n                            [101.290457, 21.17853],\n                            [101.387775, 21.225956],\n                            [101.439514, 21.227072],\n                            [101.532521, 21.252174],\n                            [101.601506, 21.233208],\n                            [101.588572, 21.191365],\n                            [101.605818, 21.172392],\n                            [101.672339, 21.194713],\n                            [101.703136, 21.14616],\n                            [101.76473, 21.147835],\n                            [101.794911, 21.208104],\n                            [101.834331, 21.204756],\n                            [101.833715, 21.252731],\n                            [101.791832, 21.285636],\n                            [101.745636, 21.297345],\n                            [101.730238, 21.336929],\n                            [101.749948, 21.409379],\n                            [101.741324, 21.482906],\n                            [101.772737, 21.512975],\n                            [101.755491, 21.538027],\n                            [101.754875, 21.58478],\n                            [101.804766, 21.577546],\n                            [101.828788, 21.617054],\n                            [101.807846, 21.644313],\n                            [101.780129, 21.640975],\n                            [101.76781, 21.716054],\n                            [101.747484, 21.729953],\n                            [101.771506, 21.833319],\n                            [101.740093, 21.845541],\n                            [101.735165, 21.875534],\n                            [101.700057, 21.897191],\n                            [101.701288, 21.938832],\n                            [101.666796, 21.934391],\n                            [101.606434, 21.967695],\n                            [101.626144, 22.005986],\n                            [101.573789, 22.115251],\n                            [101.602738, 22.131883],\n                            [101.596579, 22.161262],\n                            [101.547304, 22.238282],\n                            [101.56455, 22.269299],\n                            [101.625528, 22.28259],\n                            [101.671723, 22.372826],\n                            [101.648318, 22.400494],\n                            [101.672339, 22.47517],\n                            [101.715455, 22.477935],\n                            [101.774585, 22.506135],\n                            [101.824476, 22.45692],\n                            [101.823244, 22.42705],\n                            [101.862665, 22.389427],\n                            [101.901469, 22.384447],\n                            [101.907628, 22.437007],\n                            [101.978461, 22.427603],\n                            [102.046214, 22.458026],\n                            [102.131214, 22.430922],\n                            [102.145381, 22.397727],\n                            [102.179257, 22.430369],\n                            [102.270416, 22.419858],\n                            [102.25625, 22.457473],\n                            [102.322771, 22.554227],\n                            [102.356648, 22.563623],\n                            [102.404691, 22.629925],\n                            [102.384365, 22.679631],\n                            [102.43672, 22.699508],\n                            [102.45951, 22.762986],\n                            [102.510633, 22.774574],\n                            [102.551285, 22.743669],\n                            [102.569763, 22.701164],\n                            [102.607335, 22.730975],\n                            [102.657226, 22.687913],\n                            [102.688639, 22.70006],\n                            [102.80074, 22.620534],\n                            [102.82353, 22.623296],\n                            [102.880196, 22.586832],\n                            [102.892515, 22.533223],\n                            [102.930703, 22.482359],\n                            [102.986754, 22.477935],\n                            [103.030485, 22.441432],\n                            [103.081608, 22.454154],\n                            [103.071753, 22.488441],\n                            [103.183238, 22.558649],\n                            [103.161065, 22.590147],\n                            [103.195557, 22.648153],\n                            [103.220195, 22.643734],\n                            [103.283021, 22.678526],\n                            [103.288564, 22.732078],\n                            [103.321209, 22.777885],\n                            [103.323057, 22.807678],\n                            [103.375411, 22.794989],\n                            [103.441317, 22.753052],\n                            [103.436389, 22.6973],\n                            [103.457947, 22.658646],\n                            [103.50907, 22.601198],\n                            [103.529396, 22.59291],\n                            [103.580519, 22.66693],\n                            [103.567585, 22.701164],\n                            [103.642113, 22.794989],\n                            [103.740048, 22.709446],\n                            [103.743127, 22.697852],\n                            [103.766533, 22.688465],\n                            [103.825047, 22.615562],\n                            [103.863851, 22.584069],\n                            [103.875554, 22.565833],\n                            [103.894032, 22.564728],\n                            [103.964865, 22.502265],\n                            [104.009213, 22.517745],\n                            [104.009213, 22.575228],\n                            [104.022148, 22.593463],\n                            [104.04309, 22.67687],\n                            [104.045553, 22.728215],\n                            [104.089901, 22.768504],\n                            [104.117618, 22.808781],\n                            [104.224176, 22.826434],\n                            [104.261748, 22.841877],\n                            [104.274067, 22.828088],\n                            [104.256821, 22.77347],\n                            [104.272835, 22.73815],\n                            [104.323342, 22.728767],\n                            [104.375697, 22.690122],\n                            [104.422508, 22.734838],\n                            [104.498885, 22.774574],\n                            [104.527834, 22.814298],\n                            [104.596203, 22.846289],\n                            [104.674428, 22.817056],\n                            [104.737869, 22.825882],\n                            [104.732942, 22.852356],\n                            [104.760659, 22.862282],\n                            [104.772362, 22.893711],\n                            [104.846275, 22.926235],\n                            [104.860441, 22.970874],\n                            [104.821021, 23.032022],\n                            [104.804391, 23.110207],\n                            [104.874608, 23.123417],\n                            [104.882615, 23.163589],\n                            [104.912796, 23.175693],\n                            [104.949136, 23.152033],\n                            [104.958991, 23.188896],\n                            [105.093266, 23.260942],\n                            [105.122215, 23.247745],\n                            [105.181962, 23.279084],\n                            [105.238012, 23.26424],\n                            [105.260186, 23.31811],\n                            [105.325475, 23.390086],\n                            [105.353809, 23.362069],\n                            [105.372903, 23.317561],\n                            [105.416018, 23.283482],\n                            [105.445584, 23.292827],\n                            [105.50225, 23.202648],\n                            [105.542902, 23.184495],\n                            [105.526272, 23.234548],\n                            [105.560148, 23.257093],\n                            [105.593409, 23.312614],\n                            [105.649459, 23.346136],\n                            [105.699966, 23.327453],\n                            [105.694423, 23.363168],\n                            [105.637757, 23.404366],\n                            [105.699966, 23.40162],\n                            [105.758481, 23.459826],\n                            [105.805908, 23.467512],\n                            [105.815763, 23.507031],\n                            [105.852103, 23.526786],\n                            [105.89214, 23.52514],\n                            [105.913081, 23.499348],\n                            [105.935871, 23.508678],\n                            [105.986378, 23.489469],\n                            [105.999929, 23.447748],\n                            [106.039965, 23.484529],\n                            [106.071994, 23.495506],\n                            [106.08616, 23.524043],\n                            [106.141595, 23.569579],\n                            [106.120653, 23.605229],\n                            [106.149602, 23.665538],\n                            [106.157609, 23.724175],\n                            [106.136667, 23.795381],\n                            [106.192102, 23.824947],\n                            [106.173008, 23.861622],\n                            [106.192718, 23.879135],\n                            [106.157609, 23.891174],\n                            [106.128044, 23.956819],\n                            [106.091088, 23.998924],\n                            [106.096631, 24.018058],\n                            [106.053516, 24.051399],\n                            [106.04982, 24.089649],\n                            [106.011632, 24.099482],\n                            [105.998081, 24.120786],\n                            [105.963589, 24.110954],\n                            [105.919241, 24.122425],\n                            [105.901995, 24.099482],\n                            [105.908154, 24.069432],\n                            [105.89214, 24.040468],\n                            [105.859495, 24.056864],\n                            [105.841633, 24.03063],\n                            [105.796669, 24.023524],\n                            [105.802212, 24.051945],\n                            [105.765256, 24.073804],\n                            [105.739387, 24.059596],\n                            [105.704278, 24.0667],\n                            [105.649459, 24.032816],\n                            [105.628518, 24.126794],\n                            [105.594641, 24.137718],\n                            [105.533663, 24.130071],\n                            [105.493011, 24.016965],\n                            [105.406163, 24.043748],\n                            [105.395692, 24.065607],\n                            [105.334099, 24.094566],\n                            [105.320548, 24.116416],\n                            [105.273121, 24.092927],\n                            [105.292831, 24.074896],\n                            [105.260186, 24.061236],\n                            [105.20044, 24.105491],\n                            [105.182577, 24.167205],\n                            [105.229389, 24.165567],\n                            [105.24294, 24.208695],\n                            [105.215222, 24.214699],\n                            [105.164715, 24.288362],\n                            [105.196744, 24.326541],\n                            [105.188121, 24.347261],\n                            [105.138846, 24.376701],\n                            [105.111744, 24.37234],\n                            [105.106817, 24.414853],\n                            [105.042759, 24.442097],\n                            [104.979933, 24.412673],\n                            [104.930042, 24.411038],\n                            [104.914028, 24.426296],\n                            [104.83642, 24.446456],\n                            [104.784681, 24.443732],\n                            [104.765587, 24.45953],\n                            [104.74834, 24.435559],\n                            [104.715695, 24.441552],\n                            [104.703377, 24.419757],\n                            [104.721239, 24.340173],\n                            [104.70892, 24.321087],\n                            [104.641783, 24.367979],\n                            [104.610986, 24.377246],\n                            [104.63008, 24.397958],\n                            [104.616529, 24.421937],\n                            [104.575877, 24.424661],\n                            [104.550008, 24.518894],\n                            [104.520443, 24.535228],\n                            [104.489646, 24.653313],\n                            [104.529682, 24.731611],\n                            [104.542616, 24.75607],\n                            [104.539537, 24.813663],\n                            [104.586964, 24.872859],\n                            [104.635623, 24.903803],\n                            [104.663957, 24.964584],\n                            [104.713232, 24.996048],\n                            [104.684898, 25.054072],\n                            [104.619609, 25.060577],\n                            [104.685514, 25.078466],\n                            [104.695369, 25.122364],\n                            [104.732326, 25.167871],\n                            [104.724319, 25.195491],\n                            [104.753884, 25.214443],\n                            [104.801927, 25.163537],\n                            [104.822869, 25.170037],\n                            [104.806854, 25.224189],\n                            [104.826565, 25.235558],\n                            [104.816094, 25.262622],\n                            [104.736021, 25.268034],\n                            [104.689826, 25.296173],\n                            [104.639935, 25.295632],\n                            [104.646094, 25.356759],\n                            [104.615913, 25.364871],\n                            [104.566638, 25.402719],\n                            [104.543232, 25.400556],\n                            [104.556783, 25.524832],\n                            [104.524138, 25.526992],\n                            [104.483486, 25.494585],\n                            [104.44961, 25.495126],\n                            [104.434827, 25.472436],\n                            [104.418813, 25.499447],\n                            [104.436059, 25.520512],\n                            [104.428668, 25.576126],\n                            [104.389248, 25.595558],\n                            [104.332581, 25.598796],\n                            [104.310407, 25.647901],\n                            [104.328886, 25.760602],\n                            [104.370769, 25.730415],\n                            [104.397871, 25.76168],\n                            [104.42374, 25.841961],\n                            [104.441602, 25.868889],\n                            [104.414501, 25.909807],\n                            [104.438523, 25.92757],\n                            [104.470552, 26.009352],\n                            [104.460081, 26.085702],\n                            [104.499501, 26.070651],\n                            [104.52845, 26.114186],\n                            [104.518595, 26.165762],\n                            [104.548776, 26.226979],\n                            [104.542616, 26.253282],\n                            [104.592508, 26.317672],\n                            [104.659645, 26.335373],\n                            [104.684283, 26.3772],\n                            [104.664572, 26.397572],\n                            [104.665804, 26.434019],\n                            [104.631928, 26.451702],\n                            [104.638703, 26.477954],\n                            [104.598667, 26.520801],\n                            [104.57095, 26.524549],\n                            [104.579573, 26.568449],\n                            [104.556783, 26.590393],\n                            [104.488414, 26.579689],\n                            [104.459465, 26.602701],\n                            [104.468088, 26.644431],\n                            [104.424356, 26.709137],\n                            [104.398487, 26.686147],\n                            [104.353523, 26.620893],\n                            [104.313487, 26.612867],\n                            [104.274683, 26.633733],\n                            [104.268524, 26.617683],\n                            [104.222328, 26.620358],\n                            [104.160734, 26.646571],\n                            [104.121314, 26.638012],\n                            [104.068343, 26.573266],\n                            [104.067727, 26.51491],\n                            [104.008597, 26.511697],\n                            [103.953163, 26.521336],\n                            [103.865699, 26.512232],\n                            [103.819504, 26.529903],\n                            [103.815808, 26.55239],\n                            [103.763453, 26.585041],\n                            [103.748671, 26.623568],\n                            [103.759142, 26.689355],\n                            [103.773308, 26.716621],\n                            [103.725265, 26.742812],\n                            [103.705555, 26.794642],\n                            [103.722185, 26.851253],\n                            [103.779468, 26.87421],\n                            [103.763453, 26.905702],\n                            [103.775156, 26.951056],\n                            [103.753598, 26.963858],\n                            [103.73204, 27.018785],\n                            [103.704939, 27.049171],\n                            [103.675374, 27.051836],\n                            [103.623019, 27.007056],\n                            [103.623635, 27.035312],\n                            [103.601461, 27.061962],\n                            [103.614396, 27.079548],\n                            [103.659975, 27.065692],\n                            [103.652584, 27.092868],\n                            [103.620555, 27.096598],\n                            [103.63349, 27.12057],\n                            [103.696316, 27.126429],\n                            [103.748671, 27.210021],\n                            [103.801641, 27.250464],\n                            [103.80041, 27.26536],\n                            [103.865699, 27.28185],\n                            [103.874322, 27.331304],\n                            [103.903271, 27.347785],\n                            [103.905119, 27.38552],\n                            [103.932221, 27.443958],\n                            [103.956242, 27.425367],\n                            [104.015372, 27.429086],\n                            [104.01722, 27.383926],\n                            [104.084358, 27.330773],\n                            [104.113923, 27.338216],\n                            [104.173053, 27.263232],\n                            [104.210625, 27.297273],\n                            [104.248813, 27.291955],\n                            [104.247582, 27.336621],\n                            [104.295625, 27.37436],\n                            [104.30856, 27.407305],\n                            [104.363378, 27.467855],\n                            [104.467472, 27.414211],\n                            [104.497037, 27.414743],\n                            [104.539537, 27.327583],\n                            [104.570334, 27.331836],\n                            [104.611602, 27.306846],\n                            [104.7545, 27.345658],\n                            [104.77113, 27.317481],\n                            [104.824717, 27.3531],\n                            [104.856746, 27.332368],\n                            [104.851818, 27.299401],\n                            [104.871528, 27.290891],\n                            [104.913412, 27.327051],\n                            [105.01073, 27.379143],\n                            [105.068013, 27.418461],\n                            [105.120984, 27.418461],\n                            [105.184425, 27.392959],\n                            [105.182577, 27.367451],\n                            [105.233084, 27.436522],\n                            [105.234316, 27.489093],\n                            [105.260186, 27.514573],\n                            [105.232469, 27.546945],\n                            [105.25649, 27.582491],\n                            [105.304533, 27.611661],\n                            [105.29591, 27.631811],\n                            [105.308229, 27.704955]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 540000,\n                \"name\": \"Tibet\",\n                \"center\": [91.132212, 29.660361],\n                \"centroid\": [88.388277, 31.56375],\n                \"childrenNum\": 7,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 25,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [89.711414, 36.093272],\n                            [89.614711, 36.109712],\n                            [89.594385, 36.126632],\n                            [89.490291, 36.151281],\n                            [89.375727, 36.228078],\n                            [89.335075, 36.23725],\n                            [89.292575, 36.231457],\n                            [89.232213, 36.295636],\n                            [89.198952, 36.260417],\n                            [89.126887, 36.254626],\n                            [89.10225, 36.281164],\n                            [89.054822, 36.291777],\n                            [89.013554, 36.315409],\n                            [88.964279, 36.318785],\n                            [88.926091, 36.36458],\n                            [88.870657, 36.348193],\n                            [88.838628, 36.353496],\n                            [88.802903, 36.33807],\n                            [88.783809, 36.291777],\n                            [88.766563, 36.292259],\n                            [88.690186, 36.367954],\n                            [88.623665, 36.389636],\n                            [88.618121, 36.428168],\n                            [88.573158, 36.461386],\n                            [88.498629, 36.446463],\n                            [88.470912, 36.48208],\n                            [88.41055, 36.473418],\n                            [88.356963, 36.477268],\n                            [88.366202, 36.458016],\n                            [88.282434, 36.470049],\n                            [88.241782, 36.468605],\n                            [88.222688, 36.447426],\n                            [88.182652, 36.452721],\n                            [88.134609, 36.427205],\n                            [88.092109, 36.43539],\n                            [88.006494, 36.430575],\n                            [87.983088, 36.437797],\n                            [87.95845, 36.408423],\n                            [87.919646, 36.39349],\n                            [87.838342, 36.383855],\n                            [87.826023, 36.391563],\n                            [87.767509, 36.3747],\n                            [87.731785, 36.384818],\n                            [87.6203, 36.360243],\n                            [87.570409, 36.342409],\n                            [87.470626, 36.354459],\n                            [87.460155, 36.409868],\n                            [87.426895, 36.42576],\n                            [87.386859, 36.412757],\n                            [87.363453, 36.420463],\n                            [87.348055, 36.393008],\n                            [87.292004, 36.358797],\n                            [87.193454, 36.349158],\n                            [87.161425, 36.325535],\n                            [87.149106, 36.297565],\n                            [87.08628, 36.310587],\n                            [87.051788, 36.2966],\n                            [86.996353, 36.308658],\n                            [86.943998, 36.284058],\n                            [86.931064, 36.265242],\n                            [86.887332, 36.262829],\n                            [86.86331, 36.299977],\n                            [86.836209, 36.291294],\n                            [86.746282, 36.291777],\n                            [86.69947, 36.24449],\n                            [86.599072, 36.222285],\n                            [86.531935, 36.227113],\n                            [86.515305, 36.205385],\n                            [86.454943, 36.221319],\n                            [86.392733, 36.206834],\n                            [86.35824, 36.168676],\n                            [86.2794, 36.170608],\n                            [86.248603, 36.141616],\n                            [86.187625, 36.130983],\n                            [86.182081, 36.064734],\n                            [86.199944, 36.047801],\n                            [86.173458, 36.008113],\n                            [86.150668, 36.00424],\n                            [86.129111, 35.941761],\n                            [86.093386, 35.906868],\n                            [86.090306, 35.876809],\n                            [86.05335, 35.842857],\n                            [86.035488, 35.846738],\n                            [85.949256, 35.778794],\n                            [85.903677, 35.78462],\n                            [85.835308, 35.771996],\n                            [85.811286, 35.778794],\n                            [85.691178, 35.751114],\n                            [85.65299, 35.731199],\n                            [85.612953, 35.651486],\n                            [85.566142, 35.6403],\n                            [85.518715, 35.680658],\n                            [85.373969, 35.700101],\n                            [85.341324, 35.753543],\n                            [85.271107, 35.788989],\n                            [85.146071, 35.742371],\n                            [85.053065, 35.752086],\n                            [84.99455, 35.737028],\n                            [84.973608, 35.709334],\n                            [84.920022, 35.696213],\n                            [84.798066, 35.647595],\n                            [84.729081, 35.613546],\n                            [84.704443, 35.616951],\n                            [84.628067, 35.595055],\n                            [84.570168, 35.588242],\n                            [84.513502, 35.564391],\n                            [84.448828, 35.550272],\n                            [84.475929, 35.516181],\n                            [84.45314, 35.473303],\n                            [84.424191, 35.466479],\n                            [84.333032, 35.413821],\n                            [84.274517, 35.404065],\n                            [84.200605, 35.381135],\n                            [84.160569, 35.359663],\n                            [84.140859, 35.379184],\n                            [84.095895, 35.362592],\n                            [84.077417, 35.400163],\n                            [84.005968, 35.422599],\n                            [83.906186, 35.40309],\n                            [83.885244, 35.367472],\n                            [83.79778, 35.354783],\n                            [83.785462, 35.36308],\n                            [83.677672, 35.361128],\n                            [83.622238, 35.335256],\n                            [83.599448, 35.351366],\n                            [83.54155, 35.341603],\n                            [83.540318, 35.364056],\n                            [83.502745, 35.360639],\n                            [83.449159, 35.382111],\n                            [83.405427, 35.380648],\n                            [83.333978, 35.397236],\n                            [83.280391, 35.401138],\n                            [83.251442, 35.417722],\n                            [83.178145, 35.38943],\n                            [83.127022, 35.398699],\n                            [83.088834, 35.425526],\n                            [83.067892, 35.46258],\n                            [82.998907, 35.484512],\n                            [82.971806, 35.548324],\n                            [82.981661, 35.599922],\n                            [82.956407, 35.636409],\n                            [82.967494, 35.667532],\n                            [82.894813, 35.673852],\n                            [82.873871, 35.688922],\n                            [82.795031, 35.688436],\n                            [82.780249, 35.666073],\n                            [82.731589, 35.637868],\n                            [82.652133, 35.67288],\n                            [82.628727, 35.692324],\n                            [82.546192, 35.708362],\n                            [82.501844, 35.701073],\n                            [82.468583, 35.717595],\n                            [82.424852, 35.712736],\n                            [82.392823, 35.656349],\n                            [82.336156, 35.651486],\n                            [82.350323, 35.611113],\n                            [82.328149, 35.559523],\n                            [82.2992, 35.544916],\n                            [82.263475, 35.547837],\n                            [82.234526, 35.520565],\n                            [82.189563, 35.513258],\n                            [82.164925, 35.495719],\n                            [82.086701, 35.467454],\n                            [82.071302, 35.450393],\n                            [82.034346, 35.451855],\n                            [82.029419, 35.426013],\n                            [82.05344, 35.35039],\n                            [82.030034, 35.321585],\n                            [81.99123, 35.30547],\n                            [81.955506, 35.307423],\n                            [81.927789, 35.271275],\n                            [81.853876, 35.25857],\n                            [81.804601, 35.270786],\n                            [81.736847, 35.26248],\n                            [81.68634, 35.235599],\n                            [81.513261, 35.23511],\n                            [81.504638, 35.279092],\n                            [81.447972, 35.318167],\n                            [81.441196, 35.333303],\n                            [81.385762, 35.335256],\n                            [81.363588, 35.354783],\n                            [81.314313, 35.337209],\n                            [81.285364, 35.345508],\n                            [81.26627, 35.322562],\n                            [81.219458, 35.319144],\n                            [81.191741, 35.36552],\n                            [81.142466, 35.365032],\n                            [81.103662, 35.386015],\n                            [81.09935, 35.40748],\n                            [81.054387, 35.402602],\n                            [81.031597, 35.380648],\n                            [81.030981, 35.337209],\n                            [81.002648, 35.334768],\n                            [81.026053, 35.31133],\n                            [80.963844, 35.310842],\n                            [80.924423, 35.330862],\n                            [80.894242, 35.324027],\n                            [80.844351, 35.345508],\n                            [80.759968, 35.334768],\n                            [80.689135, 35.339162],\n                            [80.690982, 35.364544],\n                            [80.65649, 35.393821],\n                            [80.599823, 35.409431],\n                            [80.56841, 35.391381],\n                            [80.532686, 35.404553],\n                            [80.514824, 35.391869],\n                            [80.444607, 35.417235],\n                            [80.432904, 35.449418],\n                            [80.375006, 35.387966],\n                            [80.321419, 35.38699],\n                            [80.286926, 35.35283],\n                            [80.267832, 35.295701],\n                            [80.362687, 35.20871],\n                            [80.257977, 35.203331],\n                            [80.223484, 35.177409],\n                            [80.23026, 35.147565],\n                            [80.118159, 35.066293],\n                            [80.078123, 35.076578],\n                            [80.031311, 35.034447],\n                            [80.04363, 35.022196],\n                            [80.02392, 34.971209],\n                            [80.041782, 34.943252],\n                            [80.034391, 34.902033],\n                            [80.003594, 34.895162],\n                            [79.996819, 34.856375],\n                            [79.961094, 34.862759],\n                            [79.926602, 34.849499],\n                            [79.947544, 34.821008],\n                            [79.898268, 34.732035],\n                            [79.906892, 34.683821],\n                            [79.866856, 34.671517],\n                            [79.88595, 34.642965],\n                            [79.84345, 34.55725],\n                            [79.861312, 34.528166],\n                            [79.801566, 34.478847],\n                            [79.735661, 34.471447],\n                            [79.699936, 34.477861],\n                            [79.675914, 34.451216],\n                            [79.58106, 34.456151],\n                            [79.545335, 34.476381],\n                            [79.504683, 34.45467],\n                            [79.435082, 34.447761],\n                            [79.363017, 34.428018],\n                            [79.326677, 34.44332],\n                            [79.274322, 34.435916],\n                            [79.241677, 34.415183],\n                            [79.179467, 34.422588],\n                            [79.161605, 34.441345],\n                            [79.072294, 34.412714],\n                            [79.039033, 34.421601],\n                            [79.0107, 34.399877],\n                            [79.048888, 34.348506],\n                            [79.039649, 34.33467],\n                            [79.019939, 34.313417],\n                            [78.981751, 34.31836],\n                            [78.958345, 34.230827],\n                            [78.941099, 34.212022],\n                            [78.9257, 34.155584],\n                            [78.910302, 34.143202],\n                            [78.878273, 34.163012],\n                            [78.828998, 34.125369],\n                            [78.801897, 34.137258],\n                            [78.737223, 34.089692],\n                            [78.661462, 34.086718],\n                            [78.656535, 34.030196],\n                            [78.736607, 33.999937],\n                            [78.744614, 33.980585],\n                            [78.734143, 33.918529],\n                            [78.762476, 33.90959],\n                            [78.756317, 33.8773],\n                            [78.766172, 33.823124],\n                            [78.758165, 33.790802],\n                            [78.779723, 33.73259],\n                            [78.692259, 33.676331],\n                            [78.684868, 33.654415],\n                            [78.713201, 33.623025],\n                            [78.755085, 33.623025],\n                            [78.74215, 33.55323],\n                            [78.816679, 33.480882],\n                            [78.84994, 33.419963],\n                            [78.896751, 33.41247],\n                            [78.949722, 33.376495],\n                            [78.9682, 33.334505],\n                            [79.022403, 33.323504],\n                            [79.041497, 33.268479],\n                            [79.083997, 33.245459],\n                            [79.072294, 33.22844],\n                            [79.10925, 33.200401],\n                            [79.152366, 33.184375],\n                            [79.162221, 33.165841],\n                            [79.139431, 33.117735],\n                            [79.162837, 33.01191],\n                            [79.204721, 32.964724],\n                            [79.255844, 32.942628],\n                            [79.227511, 32.89038],\n                            [79.237982, 32.846145],\n                            [79.225047, 32.784281],\n                            [79.275554, 32.778746],\n                            [79.301423, 32.728919],\n                            [79.27309, 32.678056],\n                            [79.299575, 32.637244],\n                            [79.308199, 32.596918],\n                            [79.272474, 32.561113],\n                            [79.252148, 32.516715],\n                            [79.190554, 32.511669],\n                            [79.180083, 32.492994],\n                            [79.135736, 32.472295],\n                            [79.124649, 32.416235],\n                            [79.103091, 32.369744],\n                            [79.067982, 32.380863],\n                            [79.005772, 32.375304],\n                            [78.970664, 32.331826],\n                            [78.904142, 32.374798],\n                            [78.87273, 32.40512],\n                            [78.81052, 32.436441],\n                            [78.782186, 32.480373],\n                            [78.760629, 32.563635],\n                            [78.781571, 32.608009],\n                            [78.74215, 32.654881],\n                            [78.741534, 32.703743],\n                            [78.6861, 32.680071],\n                            [78.675013, 32.658408],\n                            [78.628202, 32.630188],\n                            [78.588782, 32.637748],\n                            [78.577695, 32.615067],\n                            [78.518564, 32.605993],\n                            [78.500086, 32.580782],\n                            [78.424942, 32.565652],\n                            [78.395377, 32.530339],\n                            [78.426174, 32.502584],\n                            [78.472985, 32.435431],\n                            [78.458818, 32.379853],\n                            [78.483456, 32.357106],\n                            [78.480992, 32.329297],\n                            [78.508709, 32.297939],\n                            [78.475449, 32.236708],\n                            [78.430485, 32.212407],\n                            [78.429869, 32.194683],\n                            [78.469905, 32.127808],\n                            [78.509941, 32.147065],\n                            [78.527188, 32.11463],\n                            [78.609107, 32.052768],\n                            [78.60726, 32.023851],\n                            [78.705194, 31.988835],\n                            [78.762476, 31.947203],\n                            [78.768636, 31.92638],\n                            [78.739687, 31.885228],\n                            [78.665158, 31.851684],\n                            [78.654687, 31.819144],\n                            [78.706426, 31.778453],\n                            [78.763092, 31.668499],\n                            [78.798817, 31.675629],\n                            [78.806824, 31.64099],\n                            [78.845628, 31.609905],\n                            [78.833925, 31.584927],\n                            [78.779723, 31.545154],\n                            [78.740303, 31.532912],\n                            [78.729832, 31.478316],\n                            [78.755701, 31.478316],\n                            [78.792041, 31.435944],\n                            [78.760013, 31.392531],\n                            [78.755085, 31.355742],\n                            [78.795121, 31.301043],\n                            [78.859179, 31.289281],\n                            [78.865338, 31.312804],\n                            [78.884432, 31.277006],\n                            [78.923852, 31.246824],\n                            [78.930628, 31.220726],\n                            [78.997765, 31.158779],\n                            [78.97436, 31.115751],\n                            [79.010084, 31.043994],\n                            [79.059359, 31.028097],\n                            [79.096931, 30.992192],\n                            [79.181931, 31.015788],\n                            [79.205953, 31.0004],\n                            [79.227511, 30.949088],\n                            [79.33222, 30.969103],\n                            [79.316206, 31.01784],\n                            [79.35809, 31.031174],\n                            [79.404901, 31.071678],\n                            [79.424611, 31.061425],\n                            [79.427075, 31.018353],\n                            [79.505915, 31.027584],\n                            [79.550879, 30.957813],\n                            [79.59769, 30.925989],\n                            [79.660516, 30.956787],\n                            [79.668523, 30.980392],\n                            [79.729501, 30.941389],\n                            [79.75845, 30.936769],\n                            [79.835443, 30.851006],\n                            [79.890877, 30.855116],\n                            [79.913051, 30.833022],\n                            [79.900732, 30.7991],\n                            [79.961094, 30.771337],\n                            [79.955551, 30.738422],\n                            [79.970333, 30.685941],\n                            [80.014065, 30.661748],\n                            [80.04363, 30.603559],\n                            [80.143412, 30.55822],\n                            [80.214245, 30.586044],\n                            [80.261673, 30.566465],\n                            [80.322035, 30.564403],\n                            [80.357759, 30.520592],\n                            [80.43044, 30.515952],\n                            [80.446454, 30.495327],\n                            [80.504969, 30.483466],\n                            [80.549316, 30.448905],\n                            [80.585041, 30.463866],\n                            [80.633084, 30.458707],\n                            [80.692214, 30.416913],\n                            [80.719316, 30.414848],\n                            [80.81725, 30.321389],\n                            [80.910873, 30.30279],\n                            [80.933662, 30.266614],\n                            [80.996488, 30.267648],\n                            [81.034677, 30.246971],\n                            [81.038372, 30.205086],\n                            [81.082104, 30.151281],\n                            [81.085799, 30.100554],\n                            [81.110437, 30.085538],\n                            [81.09627, 30.052909],\n                            [81.131995, 30.016124],\n                            [81.225618, 30.005759],\n                            [81.256415, 30.011978],\n                            [81.247792, 30.032705],\n                            [81.2829, 30.061197],\n                            [81.293371, 30.094859],\n                            [81.269349, 30.153351],\n                            [81.335871, 30.149729],\n                            [81.393769, 30.199396],\n                            [81.397465, 30.240767],\n                            [81.419023, 30.270232],\n                            [81.406088, 30.291938],\n                            [81.427646, 30.305373],\n                            [81.399929, 30.319323],\n                            [81.406088, 30.369421],\n                            [81.432573, 30.379231],\n                            [81.406704, 30.40401],\n                            [81.418407, 30.420525],\n                            [81.454131, 30.412268],\n                            [81.494783, 30.381296],\n                            [81.555761, 30.369421],\n                            [81.566232, 30.428782],\n                            [81.613044, 30.412784],\n                            [81.63029, 30.446842],\n                            [81.723913, 30.407623],\n                            [81.759021, 30.385426],\n                            [81.872354, 30.373035],\n                            [81.939491, 30.344633],\n                            [81.954274, 30.355995],\n                            [81.99123, 30.322939],\n                            [82.022027, 30.339468],\n                            [82.060215, 30.332237],\n                            [82.104563, 30.346182],\n                            [82.132896, 30.30434],\n                            [82.11873, 30.279019],\n                            [82.114418, 30.226806],\n                            [82.142135, 30.200948],\n                            [82.188947, 30.18543],\n                            [82.207425, 30.143519],\n                            [82.183403, 30.12178],\n                            [82.17786, 30.06793],\n                            [82.246845, 30.071555],\n                            [82.311519, 30.035813],\n                            [82.333693, 30.045138],\n                            [82.368185, 30.014051],\n                            [82.412533, 30.011978],\n                            [82.431011, 29.989692],\n                            [82.474743, 29.973622],\n                            [82.498148, 29.947698],\n                            [82.560974, 29.955476],\n                            [82.609017, 29.886489],\n                            [82.64351, 29.868846],\n                            [82.6238, 29.834588],\n                            [82.703872, 29.847566],\n                            [82.737749, 29.80655],\n                            [82.691553, 29.766037],\n                            [82.757459, 29.761881],\n                            [82.774089, 29.726548],\n                            [82.816589, 29.717192],\n                            [82.830756, 29.687562],\n                            [82.885574, 29.689122],\n                            [82.9484, 29.704718],\n                            [82.966878, 29.658963],\n                            [83.011226, 29.667804],\n                            [83.088834, 29.604863],\n                            [83.12887, 29.623593],\n                            [83.159667, 29.61735],\n                            [83.164595, 29.595496],\n                            [83.217565, 29.60018],\n                            [83.266841, 29.571035],\n                            [83.27608, 29.505951],\n                            [83.325355, 29.502826],\n                            [83.383253, 29.42206],\n                            [83.415898, 29.420496],\n                            [83.423289, 29.361053],\n                            [83.450391, 29.332883],\n                            [83.463941, 29.285916],\n                            [83.492274, 29.280174],\n                            [83.548941, 29.201322],\n                            [83.57789, 29.203934],\n                            [83.596368, 29.174153],\n                            [83.656114, 29.16736],\n                            [83.667201, 29.200277],\n                            [83.727563, 29.244672],\n                            [83.800244, 29.249372],\n                            [83.82057, 29.294267],\n                            [83.851367, 29.294789],\n                            [83.911729, 29.323491],\n                            [83.949301, 29.312533],\n                            [83.986874, 29.325057],\n                            [84.002272, 29.291658],\n                            [84.052163, 29.296877],\n                            [84.116837, 29.286438],\n                            [84.130388, 29.239972],\n                            [84.203068, 29.239972],\n                            [84.197525, 29.210202],\n                            [84.17104, 29.19453],\n                            [84.176583, 29.133909],\n                            [84.20738, 29.118749],\n                            [84.192597, 29.084236],\n                            [84.194445, 29.045004],\n                            [84.224626, 29.049189],\n                            [84.248648, 29.030353],\n                            [84.228322, 28.949738],\n                            [84.234481, 28.889497],\n                            [84.268358, 28.895261],\n                            [84.330568, 28.859101],\n                            [84.340423, 28.866963],\n                            [84.408176, 28.85386],\n                            [84.404481, 28.828173],\n                            [84.434046, 28.823978],\n                            [84.445133, 28.764189],\n                            [84.483321, 28.735331],\n                            [84.557233, 28.74635],\n                            [84.620059, 28.732182],\n                            [84.650856, 28.714338],\n                            [84.669334, 28.680742],\n                            [84.699515, 28.671816],\n                            [84.698284, 28.633478],\n                            [84.773428, 28.610363],\n                            [84.857196, 28.567798],\n                            [84.896616, 28.587244],\n                            [84.981616, 28.586193],\n                            [84.995782, 28.611414],\n                            [85.05676, 28.674441],\n                            [85.126361, 28.676016],\n                            [85.155926, 28.643983],\n                            [85.195963, 28.624022],\n                            [85.18426, 28.587244],\n                            [85.189803, 28.544669],\n                            [85.160238, 28.49261],\n                            [85.108499, 28.461047],\n                            [85.129441, 28.377885],\n                            [85.113427, 28.344708],\n                            [85.179948, 28.324164],\n                            [85.209513, 28.338914],\n                            [85.272339, 28.282538],\n                            [85.349947, 28.298347],\n                            [85.379512, 28.274105],\n                            [85.415853, 28.321003],\n                            [85.458969, 28.332593],\n                            [85.520563, 28.326798],\n                            [85.602483, 28.295712],\n                            [85.601251, 28.254075],\n                            [85.650526, 28.283592],\n                            [85.682555, 28.375779],\n                            [85.720743, 28.372093],\n                            [85.753388, 28.227714],\n                            [85.791576, 28.195544],\n                            [85.854402, 28.172334],\n                            [85.871648, 28.124843],\n                            [85.898749, 28.101617],\n                            [85.901213, 28.053566],\n                            [85.980053, 27.984357],\n                            [85.949256, 27.937311],\n                            [86.002227, 27.90717],\n                            [86.053966, 27.900823],\n                            [86.125415, 27.923035],\n                            [86.082915, 28.018175],\n                            [86.086611, 28.090002],\n                            [86.128495, 28.086835],\n                            [86.140198, 28.114814],\n                            [86.19132, 28.167058],\n                            [86.223965, 28.092642],\n                            [86.206103, 28.084195],\n                            [86.231972, 27.974315],\n                            [86.27324, 27.976958],\n                            [86.308965, 27.950528],\n                            [86.393349, 27.926736],\n                            [86.414906, 27.904526],\n                            [86.450015, 27.908757],\n                            [86.475884, 27.944713],\n                            [86.514689, 27.954757],\n                            [86.513457, 27.996511],\n                            [86.537478, 28.044587],\n                            [86.55842, 28.047757],\n                            [86.568891, 28.103201],\n                            [86.60092, 28.097922],\n                            [86.611391, 28.069938],\n                            [86.647732, 28.06941],\n                            [86.662514, 28.092114],\n                            [86.700086, 28.101617],\n                            [86.74813, 28.089474],\n                            [86.768456, 28.06941],\n                            [86.756753, 28.032967],\n                            [86.827586, 28.012363],\n                            [86.864542, 28.022401],\n                            [86.885484, 27.995983],\n                            [86.926752, 27.985942],\n                            [86.935375, 27.955286],\n                            [87.035157, 27.946299],\n                            [87.080737, 27.910872],\n                            [87.118309, 27.840512],\n                            [87.173744, 27.818284],\n                            [87.227946, 27.812991],\n                            [87.249504, 27.839454],\n                            [87.280917, 27.845275],\n                            [87.317258, 27.826753],\n                            [87.364069, 27.824106],\n                            [87.421967, 27.856916],\n                            [87.418272, 27.825694],\n                            [87.45954, 27.820931],\n                            [87.58088, 27.859562],\n                            [87.598126, 27.814579],\n                            [87.670191, 27.832045],\n                            [87.668343, 27.809815],\n                            [87.727473, 27.802933],\n                            [87.77798, 27.860091],\n                            [87.782292, 27.890774],\n                            [87.826639, 27.927794],\n                            [87.930733, 27.909285],\n                            [87.982472, 27.884426],\n                            [88.037291, 27.901881],\n                            [88.090877, 27.885484],\n                            [88.111819, 27.864852],\n                            [88.137689, 27.878607],\n                            [88.120442, 27.915103],\n                            [88.156783, 27.957929],\n                            [88.203594, 27.943127],\n                            [88.242398, 27.967444],\n                            [88.254101, 27.939426],\n                            [88.357579, 27.986471],\n                            [88.401311, 27.976958],\n                            [88.43334, 28.002852],\n                            [88.469064, 28.009721],\n                            [88.498013, 28.04089],\n                            [88.554064, 28.027684],\n                            [88.565151, 28.083139],\n                            [88.620585, 28.091586],\n                            [88.645223, 28.111119],\n                            [88.67602, 28.068353],\n                            [88.764099, 28.068353],\n                            [88.812142, 28.018175],\n                            [88.842939, 28.006023],\n                            [88.846635, 27.921448],\n                            [88.864497, 27.921448],\n                            [88.888519, 27.846863],\n                            [88.863265, 27.811932],\n                            [88.870657, 27.743098],\n                            [88.850331, 27.710783],\n                            [88.852178, 27.671039],\n                            [88.816454, 27.641354],\n                            [88.813374, 27.606889],\n                            [88.770874, 27.563924],\n                            [88.797976, 27.521473],\n                            [88.783193, 27.467324],\n                            [88.809063, 27.405711],\n                            [88.838012, 27.37808],\n                            [88.867577, 27.3818],\n                            [88.901453, 27.327583],\n                            [88.920548, 27.325456],\n                            [88.911924, 27.272807],\n                            [88.942105, 27.261636],\n                            [88.984605, 27.208957],\n                            [89.067757, 27.240354],\n                            [89.077612, 27.287168],\n                            [89.152757, 27.319076],\n                            [89.182938, 27.373829],\n                            [89.132431, 27.441302],\n                            [89.095474, 27.471572],\n                            [89.109025, 27.537925],\n                            [89.163228, 27.574534],\n                            [89.128735, 27.611131],\n                            [89.131815, 27.633402],\n                            [89.184786, 27.673689],\n                            [89.238988, 27.796581],\n                            [89.295655, 27.84845],\n                            [89.375727, 27.875962],\n                            [89.44348, 27.968501],\n                            [89.461958, 28.03191],\n                            [89.511233, 28.086307],\n                            [89.541414, 28.088418],\n                            [89.605472, 28.161782],\n                            [89.720037, 28.170224],\n                            [89.779167, 28.197127],\n                            [89.789638, 28.240895],\n                            [89.869094, 28.221386],\n                            [89.901739, 28.18183],\n                            [89.976268, 28.189215],\n                            [90.017536, 28.162837],\n                            [90.03355, 28.136981],\n                            [90.07297, 28.155451],\n                            [90.103151, 28.141731],\n                            [90.124709, 28.190797],\n                            [90.166593, 28.187632],\n                            [90.189999, 28.161782],\n                            [90.231882, 28.144897],\n                            [90.297172, 28.153868],\n                            [90.367389, 28.088946],\n                            [90.384019, 28.06096],\n                            [90.43699, 28.063073],\n                            [90.47949, 28.044587],\n                            [90.513983, 28.062016],\n                            [90.569417, 28.044059],\n                            [90.591591, 28.021345],\n                            [90.701844, 28.076274],\n                            [90.741264, 28.053038],\n                            [90.802242, 28.040362],\n                            [90.806554, 28.015005],\n                            [90.853365, 27.969029],\n                            [90.896481, 27.946299],\n                            [90.96177, 27.9537],\n                            [90.976553, 27.935725],\n                            [90.96485, 27.900294],\n                            [91.025828, 27.857445],\n                            [91.113292, 27.846333],\n                            [91.155175, 27.894476],\n                            [91.147784, 27.927794],\n                            [91.162567, 27.968501],\n                            [91.216153, 27.989113],\n                            [91.251878, 27.970615],\n                            [91.309776, 28.057791],\n                            [91.464993, 28.002852],\n                            [91.490246, 27.971672],\n                            [91.486551, 27.937311],\n                            [91.552456, 27.90717],\n                            [91.611586, 27.891303],\n                            [91.618978, 27.856916],\n                            [91.561079, 27.855329],\n                            [91.544449, 27.820401],\n                            [91.610355, 27.819343],\n                            [91.642383, 27.7664],\n                            [91.622673, 27.692238],\n                            [91.570934, 27.650897],\n                            [91.562311, 27.627569],\n                            [91.582637, 27.598933],\n                            [91.564775, 27.58196],\n                            [91.585101, 27.540578],\n                            [91.626985, 27.509265],\n                            [91.663325, 27.507142],\n                            [91.71876, 27.467324],\n                            [91.753868, 27.462545],\n                            [91.839484, 27.489624],\n                            [91.946657, 27.464138],\n                            [92.010715, 27.474758],\n                            [92.021802, 27.444489],\n                            [92.064918, 27.391365],\n                            [92.125896, 27.273339],\n                            [92.091403, 27.264296],\n                            [92.071077, 27.237694],\n                            [92.061222, 27.190327],\n                            [92.032273, 27.167967],\n                            [92.02673, 27.108318],\n                            [92.043976, 27.052902],\n                            [92.076005, 27.041175],\n                            [92.124664, 26.960124],\n                            [92.109265, 26.854991],\n                            [92.197961, 26.86994],\n                            [92.28604, 26.892359],\n                            [92.404916, 26.9025],\n                            [92.496691, 26.921711],\n                            [92.549046, 26.941453],\n                            [92.64698, 26.952656],\n                            [92.682089, 26.947855],\n                            [92.802813, 26.895028],\n                            [92.909371, 26.914241],\n                            [93.050421, 26.883819],\n                            [93.111399, 26.880082],\n                            [93.232739, 26.906769],\n                            [93.56781, 26.938252],\n                            [93.625092, 26.955323],\n                            [93.747048, 27.015587],\n                            [93.817265, 27.025183],\n                            [93.841903, 27.045973],\n                            [93.849294, 27.168499],\n                            [93.970634, 27.30525],\n                            [94.056866, 27.375423],\n                            [94.147409, 27.458297],\n                            [94.220705, 27.536333],\n                            [94.277372, 27.58143],\n                            [94.353132, 27.578778],\n                            [94.399944, 27.589386],\n                            [94.443675, 27.585143],\n                            [94.478168, 27.602116],\n                            [94.524979, 27.596282],\n                            [94.660486, 27.650367],\n                            [94.722696, 27.683759],\n                            [94.78121, 27.699127],\n                            [94.836645, 27.728796],\n                            [94.88592, 27.743098],\n                            [94.947514, 27.792345],\n                            [95.015267, 27.82887],\n                            [95.067006, 27.840512],\n                            [95.28628, 27.939955],\n                            [95.32878, 28.017646],\n                            [95.352802, 28.04089],\n                            [95.371896, 28.110063],\n                            [95.39715, 28.142259],\n                            [95.437802, 28.161782],\n                            [95.528345, 28.182885],\n                            [95.674322, 28.254075],\n                            [95.740228, 28.275159],\n                            [95.787655, 28.270416],\n                            [95.832003, 28.295186],\n                            [95.874502, 28.29782],\n                            [95.899756, 28.278322],\n                            [95.907763, 28.241422],\n                            [95.936096, 28.240368],\n                            [95.989067, 28.198181],\n                            [96.074683, 28.193434],\n                            [96.098088, 28.212421],\n                            [96.194175, 28.212949],\n                            [96.275479, 28.228241],\n                            [96.298269, 28.140148],\n                            [96.367254, 28.118509],\n                            [96.398667, 28.118509],\n                            [96.395587, 28.143842],\n                            [96.426384, 28.161782],\n                            [96.46334, 28.143314],\n                            [96.499681, 28.067297],\n                            [96.538485, 28.075218],\n                            [96.623485, 28.024514],\n                            [96.635188, 27.994926],\n                            [96.690622, 27.948942],\n                            [96.711564, 27.9574],\n                            [96.784245, 27.931495],\n                            [96.810114, 27.890245],\n                            [96.849534, 27.874375],\n                            [96.908049, 27.884426],\n                            [96.972722, 27.861149],\n                            [97.008447, 27.807698],\n                            [97.049099, 27.81405],\n                            [97.062649, 27.742568],\n                            [97.097758, 27.740979],\n                            [97.103301, 27.780697],\n                            [97.167975, 27.811932],\n                            [97.253591, 27.891832],\n                            [97.303482, 27.913516],\n                            [97.324424, 27.880723],\n                            [97.386634, 27.882839],\n                            [97.372467, 27.907699],\n                            [97.379242, 27.970087],\n                            [97.413119, 28.01342],\n                            [97.378626, 28.031382],\n                            [97.375547, 28.062545],\n                            [97.320728, 28.054095],\n                            [97.305945, 28.071522],\n                            [97.340438, 28.104785],\n                            [97.326887, 28.132759],\n                            [97.352757, 28.149646],\n                            [97.362612, 28.199236],\n                            [97.349677, 28.235623],\n                            [97.398336, 28.238786],\n                            [97.402032, 28.279903],\n                            [97.422358, 28.297293],\n                            [97.461162, 28.26778],\n                            [97.469169, 28.30309],\n                            [97.518445, 28.327852],\n                            [97.488879, 28.347341],\n                            [97.485184, 28.38631],\n                            [97.499966, 28.428948],\n                            [97.521524, 28.444736],\n                            [97.507974, 28.46473],\n                            [97.521524, 28.495766],\n                            [97.569567, 28.541515],\n                            [97.60406, 28.515225],\n                            [97.634857, 28.532051],\n                            [97.68598, 28.519958],\n                            [97.737103, 28.465782],\n                            [97.738335, 28.396313],\n                            [97.769748, 28.3742],\n                            [97.801161, 28.326798],\n                            [97.842429, 28.326798],\n                            [97.871378, 28.361561],\n                            [97.907718, 28.363141],\n                            [98.020435, 28.253548],\n                            [98.008116, 28.214003],\n                            [98.03337, 28.187105],\n                            [98.056775, 28.202401],\n                            [98.090036, 28.195544],\n                            [98.097427, 28.166531],\n                            [98.139311, 28.142259],\n                            [98.17442, 28.163365],\n                            [98.169492, 28.206093],\n                            [98.21692, 28.212949],\n                            [98.266811, 28.242477],\n                            [98.231702, 28.314681],\n                            [98.207681, 28.330486],\n                            [98.208913, 28.358401],\n                            [98.301303, 28.384204],\n                            [98.317934, 28.324691],\n                            [98.353042, 28.293078],\n                            [98.37768, 28.246167],\n                            [98.370289, 28.18394],\n                            [98.389999, 28.16442],\n                            [98.389383, 28.114814],\n                            [98.428803, 28.104785],\n                            [98.464527, 28.151229],\n                            [98.494092, 28.141203],\n                            [98.559382, 28.182885],\n                            [98.625903, 28.165475],\n                            [98.649925, 28.200291],\n                            [98.712135, 28.229296],\n                            [98.710287, 28.288862],\n                            [98.746628, 28.321003],\n                            [98.740468, 28.348395],\n                            [98.693041, 28.43158],\n                            [98.673947, 28.478934],\n                            [98.625903, 28.489455],\n                            [98.619128, 28.50944],\n                            [98.637606, 28.552029],\n                            [98.594491, 28.667615],\n                            [98.666555, 28.712239],\n                            [98.683802, 28.740054],\n                            [98.652389, 28.817162],\n                            [98.668403, 28.843376],\n                            [98.643766, 28.895261],\n                            [98.6567, 28.910454],\n                            [98.624056, 28.95864],\n                            [98.655469, 28.976966],\n                            [98.70228, 28.9644],\n                            [98.757714, 29.004186],\n                            [98.786048, 28.998952],\n                            [98.821772, 28.920931],\n                            [98.827932, 28.821356],\n                            [98.852569, 28.798283],\n                            [98.912931, 28.800906],\n                            [98.922786, 28.823978],\n                            [98.972677, 28.832367],\n                            [98.973909, 28.864867],\n                            [98.917859, 28.886877],\n                            [98.925866, 28.978536],\n                            [99.013329, 29.036632],\n                            [98.991771, 29.105677],\n                            [98.967134, 29.128159],\n                            [98.960974, 29.165792],\n                            [98.9813, 29.204978],\n                            [99.024416, 29.188783],\n                            [99.037351, 29.20759],\n                            [99.113727, 29.221171],\n                            [99.114343, 29.243628],\n                            [99.075539, 29.316186],\n                            [99.058909, 29.417368],\n                            [99.066916, 29.421018],\n                            [99.044742, 29.520013],\n                            [99.052133, 29.563748],\n                            [99.014561, 29.607464],\n                            [98.992387, 29.677163],\n                            [99.018873, 29.792009],\n                            [99.0238, 29.846009],\n                            [99.068148, 29.931621],\n                            [99.055213, 29.958587],\n                            [99.036735, 30.053945],\n                            [99.044742, 30.079842],\n                            [98.989308, 30.151799],\n                            [98.9813, 30.182843],\n                            [98.993003, 30.215429],\n                            [98.970829, 30.260928],\n                            [98.986844, 30.280569],\n                            [98.967134, 30.33482],\n                            [98.965286, 30.449937],\n                            [98.932025, 30.521623],\n                            [98.926482, 30.569556],\n                            [98.939417, 30.598923],\n                            [98.92217, 30.609225],\n                            [98.907388, 30.698292],\n                            [98.963438, 30.728134],\n                            [98.957895, 30.765166],\n                            [98.904924, 30.782649],\n                            [98.850105, 30.849465],\n                            [98.797135, 30.87926],\n                            [98.774345, 30.908019],\n                            [98.797135, 30.948575],\n                            [98.806374, 30.995783],\n                            [98.774961, 31.031174],\n                            [98.736772, 31.049121],\n                            [98.712135, 31.082954],\n                            [98.710287, 31.1178],\n                            [98.675179, 31.15417],\n                            [98.602498, 31.192062],\n                            [98.62344, 31.221238],\n                            [98.60373, 31.257568],\n                            [98.616048, 31.3036],\n                            [98.643766, 31.338876],\n                            [98.691809, 31.333253],\n                            [98.773113, 31.249382],\n                            [98.805758, 31.279052],\n                            [98.810685, 31.306668],\n                            [98.887062, 31.37465],\n                            [98.84333, 31.416028],\n                            [98.844562, 31.429817],\n                            [98.714599, 31.508935],\n                            [98.696736, 31.538523],\n                            [98.651157, 31.57881],\n                            [98.619128, 31.591555],\n                            [98.553839, 31.660349],\n                            [98.545831, 31.717383],\n                            [98.516882, 31.717383],\n                            [98.508875, 31.751995],\n                            [98.461448, 31.800327],\n                            [98.414636, 31.832365],\n                            [98.426339, 31.856767],\n                            [98.399238, 31.895899],\n                            [98.432498, 31.922825],\n                            [98.434962, 32.007613],\n                            [98.402933, 32.026896],\n                            [98.404781, 32.045159],\n                            [98.357354, 32.087253],\n                            [98.303151, 32.121726],\n                            [98.260035, 32.208862],\n                            [98.218768, 32.234683],\n                            [98.23047, 32.262521],\n                            [98.208913, 32.318171],\n                            [98.218768, 32.342444],\n                            [98.125145, 32.401077],\n                            [98.107283, 32.391476],\n                            [98.079565, 32.415224],\n                            [97.940363, 32.482393],\n                            [97.880001, 32.486431],\n                            [97.863986, 32.499051],\n                            [97.80732, 32.50006],\n                            [97.795617, 32.521257],\n                            [97.730944, 32.527312],\n                            [97.684132, 32.530339],\n                            [97.670582, 32.51722],\n                            [97.540618, 32.536899],\n                            [97.50243, 32.530844],\n                            [97.463626, 32.55506],\n                            [97.448843, 32.586833],\n                            [97.411887, 32.575235],\n                            [97.374315, 32.546484],\n                            [97.3583, 32.563635],\n                            [97.332431, 32.542448],\n                            [97.334895, 32.514192],\n                            [97.388481, 32.501575],\n                            [97.341054, 32.440987],\n                            [97.387865, 32.427349],\n                            [97.424822, 32.322723],\n                            [97.415583, 32.296421],\n                            [97.371235, 32.273148],\n                            [97.32196, 32.303503],\n                            [97.299786, 32.294904],\n                            [97.264062, 32.182527],\n                            [97.271453, 32.139971],\n                            [97.313953, 32.130342],\n                            [97.293011, 32.096887],\n                            [97.308409, 32.076605],\n                            [97.258518, 32.072041],\n                            [97.219714, 32.109054],\n                            [97.201852, 32.090296],\n                            [97.233881, 32.063927],\n                            [97.214786, 32.042623],\n                            [97.188301, 32.055304],\n                            [97.169823, 32.032984],\n                            [97.127323, 32.044145],\n                            [97.028773, 32.04871],\n                            [97.006599, 32.067984],\n                            [96.935766, 32.048203],\n                            [96.965947, 32.008628],\n                            [96.941925, 31.986297],\n                            [96.894498, 32.013703],\n                            [96.863085, 31.996448],\n                            [96.868629, 31.964975],\n                            [96.824281, 32.007613],\n                            [96.722651, 32.013195],\n                            [96.742977, 32.001016],\n                            [96.753448, 31.944156],\n                            [96.776238, 31.935015],\n                            [96.81073, 31.894375],\n                            [96.794716, 31.869474],\n                            [96.760223, 31.860325],\n                            [96.765767, 31.819144],\n                            [96.799027, 31.792188],\n                            [96.840295, 31.720438],\n                            [96.790404, 31.698545],\n                            [96.778701, 31.675629],\n                            [96.722651, 31.686833],\n                            [96.691854, 31.722474],\n                            [96.661057, 31.705674],\n                            [96.615477, 31.737236],\n                            [96.56805, 31.711783],\n                            [96.519391, 31.74945],\n                            [96.468884, 31.769804],\n                            [96.435623, 31.796258],\n                            [96.407906, 31.845583],\n                            [96.389428, 31.919777],\n                            [96.288414, 31.919777],\n                            [96.253305, 31.929936],\n                            [96.220044, 31.905553],\n                            [96.188632, 31.904028],\n                            [96.214501, 31.876589],\n                            [96.202798, 31.841008],\n                            [96.183088, 31.835924],\n                            [96.178161, 31.775401],\n                            [96.231131, 31.749959],\n                            [96.222508, 31.733164],\n                            [96.252073, 31.697527],\n                            [96.245298, 31.657802],\n                            [96.221892, 31.647613],\n                            [96.207726, 31.598691],\n                            [96.156603, 31.602769],\n                            [96.148595, 31.686324],\n                            [96.135661, 31.70211],\n                            [96.064828, 31.720438],\n                            [95.989067, 31.78761],\n                            [95.983524, 31.816601],\n                            [95.89914, 31.81711],\n                            [95.846169, 31.736218],\n                            [95.853561, 31.714329],\n                            [95.823995, 31.68225],\n                            [95.779648, 31.748941],\n                            [95.634286, 31.782523],\n                            [95.580083, 31.76726],\n                            [95.546823, 31.73978],\n                            [95.511714, 31.750468],\n                            [95.480301, 31.795749],\n                            [95.456896, 31.801853],\n                            [95.406389, 31.896915],\n                            [95.408852, 31.918761],\n                            [95.3682, 31.92892],\n                            [95.360809, 31.95939],\n                            [95.395918, 32.001523],\n                            [95.454432, 32.007613],\n                            [95.421171, 32.033999],\n                            [95.454432, 32.061898],\n                            [95.440265, 32.157705],\n                            [95.406389, 32.182021],\n                            [95.367584, 32.178982],\n                            [95.366968, 32.151118],\n                            [95.31523, 32.148585],\n                            [95.270266, 32.194683],\n                            [95.270266, 32.194683],\n                            [95.239469, 32.287315],\n                            [95.241317, 32.3207],\n                            [95.214216, 32.321712],\n                            [95.20744, 32.297433],\n                            [95.10581, 32.258979],\n                            [95.079325, 32.279726],\n                            [95.096571, 32.322217],\n                            [95.193274, 32.332331],\n                            [95.261643, 32.348006],\n                            [95.228382, 32.363678],\n                            [95.218527, 32.397035],\n                            [95.153853, 32.386423],\n                            [95.081789, 32.384907],\n                            [95.075013, 32.376315],\n                            [95.075013, 32.376315],\n                            [95.057151, 32.395014],\n                            [94.988166, 32.422802],\n                            [94.944434, 32.404109],\n                            [94.912405, 32.41573],\n                            [94.889616, 32.472295],\n                            [94.852043, 32.463712],\n                            [94.80708, 32.486431],\n                            [94.78737, 32.522266],\n                            [94.762116, 32.526303],\n                            [94.737479, 32.587338],\n                            [94.638312, 32.645307],\n                            [94.614291, 32.673522],\n                            [94.591501, 32.640772],\n                            [94.522516, 32.595909],\n                            [94.459074, 32.599439],\n                            [94.463386, 32.572209],\n                            [94.435052, 32.562626],\n                            [94.395016, 32.594397],\n                            [94.371611, 32.524789],\n                            [94.350053, 32.533871],\n                            [94.294002, 32.519743],\n                            [94.292154, 32.502584],\n                            [94.250886, 32.51722],\n                            [94.196684, 32.51621],\n                            [94.176974, 32.454117],\n                            [94.137554, 32.433915],\n                            [94.091974, 32.463207],\n                            [94.049474, 32.469771],\n                            [94.03038, 32.448057],\n                            [93.978641, 32.459672],\n                            [93.960163, 32.484917],\n                            [93.90904, 32.463207],\n                            [93.861613, 32.466237],\n                            [93.851142, 32.50965],\n                            [93.820345, 32.549511],\n                            [93.75136, 32.56313],\n                            [93.721795, 32.578261],\n                            [93.651577, 32.571705],\n                            [93.618933, 32.522771],\n                            [93.516687, 32.47583],\n                            [93.501904, 32.503593],\n                            [93.476651, 32.504603],\n                            [93.4631, 32.556069],\n                            [93.411977, 32.558086],\n                            [93.385492, 32.525294],\n                            [93.33868, 32.5712],\n                            [93.308499, 32.580278],\n                            [93.300492, 32.619604],\n                            [93.260456, 32.62666],\n                            [93.239514, 32.662439],\n                            [93.210565, 32.655385],\n                            [93.176688, 32.6705],\n                            [93.159442, 32.644803],\n                            [93.087993, 32.63674],\n                            [93.069515, 32.626156],\n                            [93.023935, 32.703239],\n                            [93.019624, 32.737477],\n                            [93.00053, 32.741001],\n                            [92.964189, 32.714821],\n                            [92.933392, 32.719353],\n                            [92.866871, 32.698203],\n                            [92.822523, 32.729926],\n                            [92.789262, 32.719856],\n                            [92.756618, 32.743014],\n                            [92.686401, 32.76516],\n                            [92.667922, 32.73194],\n                            [92.634662, 32.720863],\n                            [92.574916, 32.741001],\n                            [92.56814, 32.73194],\n                            [92.484372, 32.745028],\n                            [92.459119, 32.76365],\n                            [92.411076, 32.748048],\n                            [92.355641, 32.764657],\n                            [92.343938, 32.738484],\n                            [92.310062, 32.751571],\n                            [92.255243, 32.720863],\n                            [92.198577, 32.754591],\n                            [92.211511, 32.788306],\n                            [92.193649, 32.801889],\n                            [92.227526, 32.821003],\n                            [92.205352, 32.866255],\n                            [92.145606, 32.885857],\n                            [92.101874, 32.860222],\n                            [92.038432, 32.860725],\n                            [92.018722, 32.829552],\n                            [91.955897, 32.8205],\n                            [91.896766, 32.907967],\n                            [91.857962, 32.90244],\n                            [91.839484, 32.948152],\n                            [91.799448, 32.942126],\n                            [91.752637, 32.969242],\n                            [91.685499, 32.989324],\n                            [91.664557, 33.012913],\n                            [91.583253, 33.0375],\n                            [91.55492, 33.060074],\n                            [91.535826, 33.10019],\n                            [91.49579, 33.109214],\n                            [91.436044, 33.066092],\n                            [91.370138, 33.100691],\n                            [91.311624, 33.108211],\n                            [91.261733, 33.141291],\n                            [91.226624, 33.141792],\n                            [91.18782, 33.106206],\n                            [91.161335, 33.108712],\n                            [91.147784, 33.07211],\n                            [91.072024, 33.113224],\n                            [91.037531, 33.098686],\n                            [91.001807, 33.11573],\n                            [90.927894, 33.120241],\n                            [90.902024, 33.083143],\n                            [90.88293, 33.120241],\n                            [90.803474, 33.114227],\n                            [90.740032, 33.142293],\n                            [90.704308, 33.135778],\n                            [90.627315, 33.180368],\n                            [90.562642, 33.229441],\n                            [90.490577, 33.264977],\n                            [90.405577, 33.260473],\n                            [90.363077, 33.279487],\n                            [90.332896, 33.310501],\n                            [90.246665, 33.423959],\n                            [90.22018, 33.437943],\n                            [90.107463, 33.460913],\n                            [90.088984, 33.478885],\n                            [90.083441, 33.525295],\n                            [90.01076, 33.553728],\n                            [89.984275, 33.612061],\n                            [90.008296, 33.687785],\n                            [89.981195, 33.70322],\n                            [89.983659, 33.725622],\n                            [89.907282, 33.741051],\n                            [89.902355, 33.758467],\n                            [89.942391, 33.801246],\n                            [89.899891, 33.80771],\n                            [89.837065, 33.868853],\n                            [89.795181, 33.865374],\n                            [89.73174, 33.921509],\n                            [89.718805, 33.946832],\n                            [89.688008, 33.959739],\n                            [89.684928, 33.990013],\n                            [89.635037, 34.049537],\n                            [89.656595, 34.057966],\n                            [89.655979, 34.097126],\n                            [89.71203, 34.131809],\n                            [89.756993, 34.124874],\n                            [89.760073, 34.152613],\n                            [89.789638, 34.150632],\n                            [89.816739, 34.16945],\n                            [89.838297, 34.263477],\n                            [89.825362, 34.293642],\n                            [89.86663, 34.324785],\n                            [89.858623, 34.359375],\n                            [89.820435, 34.369255],\n                            [89.799493, 34.39642],\n                            [89.819819, 34.420614],\n                            [89.823515, 34.455657],\n                            [89.814891, 34.548871],\n                            [89.777935, 34.574499],\n                            [89.798877, 34.628686],\n                            [89.74837, 34.641981],\n                            [89.72558, 34.660689],\n                            [89.732356, 34.732035],\n                            [89.799493, 34.743838],\n                            [89.825978, 34.796931],\n                            [89.867862, 34.81069],\n                            [89.838913, 34.865705],\n                            [89.814891, 34.86816],\n                            [89.821051, 34.902033],\n                            [89.78779, 34.921664],\n                            [89.747138, 34.903506],\n                            [89.707102, 34.919701],\n                            [89.670146, 34.887798],\n                            [89.578987, 34.895162],\n                            [89.560509, 34.938836],\n                            [89.59069, 35.057965],\n                            [89.593153, 35.104491],\n                            [89.579603, 35.118688],\n                            [89.519241, 35.133862],\n                            [89.46935, 35.214577],\n                            [89.450255, 35.223867],\n                            [89.48598, 35.256616],\n                            [89.531559, 35.276161],\n                            [89.494603, 35.298632],\n                            [89.516161, 35.330862],\n                            [89.497067, 35.361128],\n                            [89.58761, 35.383575],\n                            [89.619639, 35.412357],\n                            [89.658443, 35.425526],\n                            [89.685544, 35.416259],\n                            [89.739131, 35.468429],\n                            [89.765, 35.482563],\n                            [89.740979, 35.507412],\n                            [89.720037, 35.501566],\n                            [89.699711, 35.544916],\n                            [89.71203, 35.581915],\n                            [89.75145, 35.580942],\n                            [89.765616, 35.599922],\n                            [89.726196, 35.648082],\n                            [89.748986, 35.66267],\n                            [89.747138, 35.7516],\n                            [89.782863, 35.773453],\n                            [89.767464, 35.799183],\n                            [89.801957, 35.848193],\n                            [89.778551, 35.861775],\n                            [89.707718, 35.849163],\n                            [89.654747, 35.848193],\n                            [89.62395, 35.859349],\n                            [89.550654, 35.856924],\n                            [89.554965, 35.873414],\n                            [89.489676, 35.903475],\n                            [89.428082, 35.917531],\n                            [89.434857, 35.992136],\n                            [89.404676, 36.016827],\n                            [89.417611, 36.044897],\n                            [89.474893, 36.022151],\n                            [89.605472, 36.038123],\n                            [89.688624, 36.091337],\n                            [89.711414, 36.093272]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 610000,\n                \"name\": \"Shaanxi\",\n                \"center\": [108.948024, 34.263161],\n                \"centroid\": [108.887114, 35.263661],\n                \"childrenNum\": 10,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 26,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [110.379257, 34.600612],\n                            [110.29549, 34.610956],\n                            [110.269004, 34.629671],\n                            [110.229584, 34.692679],\n                            [110.243135, 34.725641],\n                            [110.246831, 34.789068],\n                            [110.230816, 34.880925],\n                            [110.262229, 34.944233],\n                            [110.320743, 35.00504],\n                            [110.373714, 35.134351],\n                            [110.364475, 35.197952],\n                            [110.378642, 35.210666],\n                            [110.374946, 35.251728],\n                            [110.45009, 35.327933],\n                            [110.477808, 35.413821],\n                            [110.531394, 35.511309],\n                            [110.567735, 35.539559],\n                            [110.589293, 35.602355],\n                            [110.609619, 35.632031],\n                            [110.57759, 35.701559],\n                            [110.571431, 35.800639],\n                            [110.550489, 35.838005],\n                            [110.549257, 35.877778],\n                            [110.511684, 35.879718],\n                            [110.516612, 35.918501],\n                            [110.502445, 35.947575],\n                            [110.516612, 35.971796],\n                            [110.49259, 35.994073],\n                            [110.491974, 36.034735],\n                            [110.467953, 36.074893],\n                            [110.447011, 36.164328],\n                            [110.45625, 36.22663],\n                            [110.474112, 36.248352],\n                            [110.474112, 36.306729],\n                            [110.459946, 36.327946],\n                            [110.487047, 36.393972],\n                            [110.489511, 36.430094],\n                            [110.47288, 36.453203],\n                            [110.503677, 36.488335],\n                            [110.488895, 36.556628],\n                            [110.496902, 36.582102],\n                            [110.447627, 36.621018],\n                            [110.426685, 36.657514],\n                            [110.394656, 36.676716],\n                            [110.402663, 36.697352],\n                            [110.438388, 36.685835],\n                            [110.447011, 36.737649],\n                            [110.407591, 36.776007],\n                            [110.423605, 36.818179],\n                            [110.406975, 36.824886],\n                            [110.424221, 36.855539],\n                            [110.376178, 36.882351],\n                            [110.408823, 36.892403],\n                            [110.424221, 36.963685],\n                            [110.381721, 37.002408],\n                            [110.382953, 37.022001],\n                            [110.426685, 37.008621],\n                            [110.417446, 37.027257],\n                            [110.460561, 37.044932],\n                            [110.49567, 37.086956],\n                            [110.535706, 37.115118],\n                            [110.53509, 37.138021],\n                            [110.590525, 37.187145],\n                            [110.651503, 37.256722],\n                            [110.661974, 37.281963],\n                            [110.690307, 37.287201],\n                            [110.678604, 37.317668],\n                            [110.695234, 37.34955],\n                            [110.641648, 37.360015],\n                            [110.630561, 37.372858],\n                            [110.644111, 37.435135],\n                            [110.740198, 37.44939],\n                            [110.759292, 37.474567],\n                            [110.770995, 37.538184],\n                            [110.795017, 37.558586],\n                            [110.771611, 37.594634],\n                            [110.763604, 37.639668],\n                            [110.793169, 37.650567],\n                            [110.775306, 37.680886],\n                            [110.706321, 37.705511],\n                            [110.716792, 37.728708],\n                            [110.750669, 37.736281],\n                            [110.735886, 37.77035],\n                            [110.680452, 37.790216],\n                            [110.59422, 37.922049],\n                            [110.522771, 37.955088],\n                            [110.528315, 37.990471],\n                            [110.507989, 38.013107],\n                            [110.501829, 38.097929],\n                            [110.519692, 38.130889],\n                            [110.509221, 38.192061],\n                            [110.528315, 38.211814],\n                            [110.565887, 38.215105],\n                            [110.57759, 38.297345],\n                            [110.601612, 38.308147],\n                            [110.661358, 38.308617],\n                            [110.701394, 38.353215],\n                            [110.746973, 38.366355],\n                            [110.77777, 38.440924],\n                            [110.796864, 38.453579],\n                            [110.840596, 38.439986],\n                            [110.874473, 38.453579],\n                            [110.870777, 38.510265],\n                            [110.907733, 38.521035],\n                            [110.920052, 38.581878],\n                            [110.898494, 38.587024],\n                            [110.880632, 38.626776],\n                            [110.916357, 38.673981],\n                            [110.915125, 38.704345],\n                            [110.965016, 38.755699],\n                            [111.009363, 38.847579],\n                            [110.995813, 38.868084],\n                            [111.016755, 38.889981],\n                            [111.009979, 38.932823],\n                            [110.980414, 38.970056],\n                            [110.998276, 38.998433],\n                            [111.038313, 39.020289],\n                            [111.094363, 39.030053],\n                            [111.138095, 39.064447],\n                            [111.147334, 39.100681],\n                            [111.173819, 39.135041],\n                            [111.163348, 39.152678],\n                            [111.219399, 39.244044],\n                            [111.213239, 39.257021],\n                            [111.247732, 39.302419],\n                            [111.202152, 39.305197],\n                            [111.179363, 39.326959],\n                            [111.186138, 39.35149],\n                            [111.155341, 39.338531],\n                            [111.159037, 39.362596],\n                            [111.125776, 39.366297],\n                            [111.087588, 39.376013],\n                            [111.098059, 39.401914],\n                            [111.064182, 39.400989],\n                            [111.058639, 39.447681],\n                            [111.10545, 39.472631],\n                            [111.10545, 39.497573],\n                            [111.148566, 39.531277],\n                            [111.154725, 39.569116],\n                            [111.136863, 39.587106],\n                            [111.101138, 39.559428],\n                            [111.017371, 39.552045],\n                            [110.958856, 39.519275],\n                            [110.891103, 39.509118],\n                            [110.869545, 39.494341],\n                            [110.782698, 39.38804],\n                            [110.73835, 39.348713],\n                            [110.731575, 39.30705],\n                            [110.702626, 39.273701],\n                            [110.626249, 39.266751],\n                            [110.596684, 39.282966],\n                            [110.566503, 39.320014],\n                            [110.559728, 39.351027],\n                            [110.524003, 39.382952],\n                            [110.482735, 39.360745],\n                            [110.434692, 39.381101],\n                            [110.429764, 39.341308],\n                            [110.385417, 39.310291],\n                            [110.257917, 39.407001],\n                            [110.243751, 39.423645],\n                            [110.152592, 39.45415],\n                            [110.12549, 39.432891],\n                            [110.136577, 39.39174],\n                            [110.161831, 39.387115],\n                            [110.184005, 39.355192],\n                            [110.217881, 39.281113],\n                            [110.109476, 39.249606],\n                            [110.041107, 39.21623],\n                            [109.962267, 39.212056],\n                            [109.90252, 39.271848],\n                            [109.871723, 39.243581],\n                            [109.961035, 39.191651],\n                            [109.893897, 39.141075],\n                            [109.92223, 39.107183],\n                            [109.890818, 39.103932],\n                            [109.851397, 39.122971],\n                            [109.793499, 39.074204],\n                            [109.762086, 39.057476],\n                            [109.72513, 39.018429],\n                            [109.665384, 38.981687],\n                            [109.685094, 38.968195],\n                            [109.672159, 38.928167],\n                            [109.624116, 38.85457],\n                            [109.549587, 38.805618],\n                            [109.511399, 38.833595],\n                            [109.444262, 38.782763],\n                            [109.404226, 38.720689],\n                            [109.338936, 38.701542],\n                            [109.329081, 38.66043],\n                            [109.367269, 38.627711],\n                            [109.331545, 38.597783],\n                            [109.276726, 38.623035],\n                            [109.196654, 38.552867],\n                            [109.175712, 38.518694],\n                            [109.128901, 38.480288],\n                            [109.054372, 38.433892],\n                            [109.051292, 38.385122],\n                            [109.007561, 38.359316],\n                            [108.961981, 38.26493],\n                            [108.976148, 38.245192],\n                            [108.938575, 38.207582],\n                            [108.964445, 38.154894],\n                            [109.069155, 38.091336],\n                            [109.050676, 38.055059],\n                            [109.06977, 38.023008],\n                            [109.037742, 38.021593],\n                            [109.018648, 37.971602],\n                            [108.982923, 37.964053],\n                            [108.9743, 37.931962],\n                            [108.93488, 37.922521],\n                            [108.893612, 37.978207],\n                            [108.883141, 38.01405],\n                            [108.830786, 38.049875],\n                            [108.797525, 38.04799],\n                            [108.82709, 37.989056],\n                            [108.798141, 37.93385],\n                            [108.791982, 37.872934],\n                            [108.799989, 37.784068],\n                            [108.784591, 37.764673],\n                            [108.791982, 37.700303],\n                            [108.777815, 37.683728],\n                            [108.720533, 37.683728],\n                            [108.699591, 37.669518],\n                            [108.628142, 37.651988],\n                            [108.532671, 37.690832],\n                            [108.485244, 37.678044],\n                            [108.422418, 37.648672],\n                            [108.301078, 37.640616],\n                            [108.293071, 37.656726],\n                            [108.24626, 37.665728],\n                            [108.205608, 37.655779],\n                            [108.193905, 37.638246],\n                            [108.134159, 37.622131],\n                            [108.055318, 37.652462],\n                            [108.025137, 37.649619],\n                            [108.012819, 37.66857],\n                            [108.025753, 37.696041],\n                            [107.993109, 37.735335],\n                            [107.982022, 37.787378],\n                            [107.884703, 37.808186],\n                            [107.842819, 37.828987],\n                            [107.732566, 37.84931],\n                            [107.684523, 37.888522],\n                            [107.65003, 37.86443],\n                            [107.659269, 37.844112],\n                            [107.646335, 37.805349],\n                            [107.620465, 37.776026],\n                            [107.599523, 37.791162],\n                            [107.57119, 37.776499],\n                            [107.499125, 37.765619],\n                            [107.484959, 37.706458],\n                            [107.425828, 37.684201],\n                            [107.387024, 37.691305],\n                            [107.389488, 37.671413],\n                            [107.422133, 37.665254],\n                            [107.361155, 37.613125],\n                            [107.311264, 37.609806],\n                            [107.330358, 37.584201],\n                            [107.369162, 37.58752],\n                            [107.345756, 37.518725],\n                            [107.284162, 37.481691],\n                            [107.282931, 37.437036],\n                            [107.257677, 37.337179],\n                            [107.273075, 37.29101],\n                            [107.309416, 37.239095],\n                            [107.270612, 37.229089],\n                            [107.317423, 37.200017],\n                            [107.336517, 37.165687],\n                            [107.334669, 37.138975],\n                            [107.306952, 37.100799],\n                            [107.281083, 37.127047],\n                            [107.268764, 37.099367],\n                            [107.28601, 37.054963],\n                            [107.288474, 37.008143],\n                            [107.288474, 37.008143],\n                            [107.291554, 36.979463],\n                            [107.291554, 36.979463],\n                            [107.310032, 36.912502],\n                            [107.336517, 36.925899],\n                            [107.365466, 36.905324],\n                            [107.478183, 36.908196],\n                            [107.533618, 36.867031],\n                            [107.540393, 36.828718],\n                            [107.5909, 36.836382],\n                            [107.642023, 36.819137],\n                            [107.670356, 36.83303],\n                            [107.722095, 36.802367],\n                            [107.742421, 36.811951],\n                            [107.768291, 36.792783],\n                            [107.866841, 36.766899],\n                            [107.907493, 36.750118],\n                            [107.914268, 36.720861],\n                            [107.940754, 36.694953],\n                            [107.938906, 36.655594],\n                            [108.006659, 36.683435],\n                            [108.02329, 36.647912],\n                            [108.001732, 36.639269],\n                            [108.060862, 36.592194],\n                            [108.079956, 36.614294],\n                            [108.092891, 36.587388],\n                            [108.163724, 36.563839],\n                            [108.1976, 36.630144],\n                            [108.222854, 36.631105],\n                            [108.204992, 36.606607],\n                            [108.204992, 36.606607],\n                            [108.210535, 36.577296],\n                            [108.245644, 36.571048],\n                            [108.262274, 36.549417],\n                            [108.340498, 36.559032],\n                            [108.365136, 36.519603],\n                            [108.391621, 36.505654],\n                            [108.408252, 36.45946],\n                            [108.460606, 36.422871],\n                            [108.495099, 36.422389],\n                            [108.514809, 36.445501],\n                            [108.510498, 36.47438],\n                            [108.562852, 36.43876],\n                            [108.618903, 36.433946],\n                            [108.651548, 36.384818],\n                            [108.641693, 36.359279],\n                            [108.646004, 36.254143],\n                            [108.712526, 36.138716],\n                            [108.682345, 36.062316],\n                            [108.688504, 36.021183],\n                            [108.659555, 35.990683],\n                            [108.652164, 35.94806],\n                            [108.593649, 35.950967],\n                            [108.562852, 35.921409],\n                            [108.518505, 35.905414],\n                            [108.499411, 35.872444],\n                            [108.527744, 35.82442],\n                            [108.533903, 35.746257],\n                            [108.517889, 35.699615],\n                            [108.539447, 35.605761],\n                            [108.618287, 35.557088],\n                            [108.625678, 35.537124],\n                            [108.605968, 35.503028],\n                            [108.631222, 35.418698],\n                            [108.61028, 35.355271],\n                            [108.614591, 35.328909],\n                            [108.583178, 35.294724],\n                            [108.547454, 35.304981],\n                            [108.48894, 35.275184],\n                            [108.36144, 35.279581],\n                            [108.345426, 35.300586],\n                            [108.296767, 35.267855],\n                            [108.239484, 35.256127],\n                            [108.221622, 35.296678],\n                            [108.174811, 35.304981],\n                            [108.094739, 35.280069],\n                            [108.049159, 35.253683],\n                            [107.949993, 35.245375],\n                            [107.960464, 35.263457],\n                            [107.867457, 35.256127],\n                            [107.841587, 35.276649],\n                            [107.745501, 35.311819],\n                            [107.737494, 35.267366],\n                            [107.667277, 35.257104],\n                            [107.652494, 35.244886],\n                            [107.686371, 35.218],\n                            [107.715936, 35.168114],\n                            [107.727639, 35.120157],\n                            [107.769523, 35.064333],\n                            [107.769523, 35.064333],\n                            [107.773218, 35.060904],\n                            [107.773218, 35.060904],\n                            [107.814486, 35.024646],\n                            [107.846515, 35.024646],\n                            [107.863145, 34.999158],\n                            [107.842203, 34.979056],\n                            [107.741805, 34.953553],\n                            [107.675284, 34.9511],\n                            [107.638943, 34.935402],\n                            [107.619849, 34.964834],\n                            [107.564415, 34.968757],\n                            [107.523763, 34.909886],\n                            [107.455394, 34.916757],\n                            [107.400575, 34.932949],\n                            [107.369162, 34.917738],\n                            [107.350068, 34.93393],\n                            [107.286626, 34.931968],\n                            [107.252749, 34.880925],\n                            [107.189308, 34.893198],\n                            [107.162206, 34.944233],\n                            [107.119707, 34.950119],\n                            [107.089526, 34.976604],\n                            [107.08275, 35.024156],\n                            [107.012533, 35.029547],\n                            [106.990975, 35.068252],\n                            [106.950323, 35.066782],\n                            [106.901664, 35.094698],\n                            [106.838222, 35.080007],\n                            [106.710723, 35.100574],\n                            [106.706411, 35.081966],\n                            [106.615252, 35.071191],\n                            [106.577064, 35.089312],\n                            [106.541956, 35.083925],\n                            [106.52163, 35.027587],\n                            [106.494528, 35.006021],\n                            [106.494528, 35.006021],\n                            [106.484673, 34.983959],\n                            [106.493296, 34.941289],\n                            [106.527789, 34.876507],\n                            [106.556122, 34.861285],\n                            [106.550579, 34.82936],\n                            [106.575216, 34.769897],\n                            [106.539492, 34.745805],\n                            [106.505615, 34.746789],\n                            [106.487137, 34.715311],\n                            [106.456956, 34.703996],\n                            [106.442173, 34.675455],\n                            [106.471122, 34.634102],\n                            [106.419384, 34.643458],\n                            [106.314058, 34.578934],\n                            [106.341159, 34.568093],\n                            [106.334384, 34.517811],\n                            [106.455108, 34.531617],\n                            [106.514238, 34.511894],\n                            [106.513622, 34.498085],\n                            [106.558586, 34.48822],\n                            [106.610941, 34.454177],\n                            [106.638042, 34.391481],\n                            [106.717498, 34.369255],\n                            [106.691013, 34.337635],\n                            [106.705179, 34.299575],\n                            [106.68239, 34.256057],\n                            [106.652825, 34.24369],\n                            [106.63373, 34.260014],\n                            [106.589383, 34.253584],\n                            [106.577064, 34.280786],\n                            [106.526557, 34.292159],\n                            [106.496376, 34.238248],\n                            [106.5321, 34.254079],\n                            [106.55797, 34.229837],\n                            [106.585071, 34.149641],\n                            [106.560434, 34.109514],\n                            [106.501919, 34.105055],\n                            [106.505615, 34.056479],\n                            [106.471738, 34.024244],\n                            [106.474202, 33.970659],\n                            [106.41076, 33.909093],\n                            [106.428007, 33.866368],\n                            [106.475434, 33.875809],\n                            [106.491448, 33.834559],\n                            [106.461883, 33.789807],\n                            [106.488369, 33.757969],\n                            [106.482825, 33.707203],\n                            [106.534564, 33.695254],\n                            [106.575832, 33.631497],\n                            [106.58076, 33.576169],\n                            [106.540108, 33.512822],\n                            [106.456956, 33.532779],\n                            [106.447101, 33.613058],\n                            [106.384891, 33.612061],\n                            [106.35163, 33.587137],\n                            [106.303587, 33.604585],\n                            [106.237681, 33.564201],\n                            [106.187174, 33.546746],\n                            [106.108334, 33.569686],\n                            [106.117573, 33.602591],\n                            [106.086776, 33.617045],\n                            [106.047356, 33.610067],\n                            [105.971596, 33.613058],\n                            [105.940183, 33.570684],\n                            [105.902611, 33.556222],\n                            [105.871198, 33.511325],\n                            [105.842248, 33.489866],\n                            [105.831162, 33.451926],\n                            [105.837937, 33.410971],\n                            [105.827466, 33.379993],\n                            [105.709822, 33.382991],\n                            [105.755401, 33.329004],\n                            [105.752937, 33.291994],\n                            [105.791741, 33.278486],\n                            [105.799133, 33.258471],\n                            [105.862574, 33.234447],\n                            [105.917393, 33.237951],\n                            [105.965436, 33.204407],\n                            [105.968516, 33.154318],\n                            [105.93156, 33.178365],\n                            [105.897067, 33.146803],\n                            [105.923552, 33.147805],\n                            [105.934639, 33.112221],\n                            [105.914929, 33.066092],\n                            [105.926632, 33.042517],\n                            [105.917393, 32.993841],\n                            [105.861959, 32.939112],\n                            [105.82685, 32.950663],\n                            [105.735691, 32.905454],\n                            [105.656851, 32.895405],\n                            [105.638373, 32.879323],\n                            [105.590329, 32.87681],\n                            [105.565692, 32.906962],\n                            [105.528119, 32.919019],\n                            [105.49917, 32.911986],\n                            [105.495475, 32.873292],\n                            [105.524424, 32.847654],\n                            [105.534279, 32.790822],\n                            [105.555221, 32.794343],\n                            [105.563844, 32.724891],\n                            [105.585402, 32.728919],\n                            [105.596489, 32.69921],\n                            [105.677793, 32.726402],\n                            [105.719061, 32.759624],\n                            [105.768952, 32.767676],\n                            [105.779423, 32.750061],\n                            [105.822538, 32.770192],\n                            [105.825002, 32.824523],\n                            [105.849024, 32.817985],\n                            [105.893371, 32.838603],\n                            [105.93156, 32.826032],\n                            [105.969132, 32.849162],\n                            [106.011632, 32.829552],\n                            [106.044277, 32.864747],\n                            [106.071378, 32.828546],\n                            [106.093552, 32.82402],\n                            [106.07261, 32.76365],\n                            [106.076921, 32.76365],\n                            [106.076305, 32.759121],\n                            [106.071378, 32.758114],\n                            [106.120037, 32.719856],\n                            [106.17424, 32.6977],\n                            [106.254928, 32.693671],\n                            [106.267863, 32.673522],\n                            [106.301123, 32.680071],\n                            [106.347935, 32.671003],\n                            [106.389203, 32.62666],\n                            [106.421231, 32.616579],\n                            [106.451412, 32.65992],\n                            [106.498224, 32.649338],\n                            [106.517934, 32.668485],\n                            [106.585687, 32.68813],\n                            [106.626955, 32.682086],\n                            [106.670071, 32.694678],\n                            [106.733513, 32.739491],\n                            [106.783404, 32.735967],\n                            [106.793259, 32.712807],\n                            [106.82344, 32.705254],\n                            [106.854853, 32.724388],\n                            [106.903512, 32.721367],\n                            [106.912751, 32.704247],\n                            [107.012533, 32.721367],\n                            [107.066736, 32.708779],\n                            [107.05996, 32.686115],\n                            [107.098765, 32.649338],\n                            [107.108004, 32.600951],\n                            [107.080286, 32.542448],\n                            [107.127098, 32.482393],\n                            [107.189924, 32.468256],\n                            [107.212097, 32.428864],\n                            [107.263836, 32.403099],\n                            [107.287858, 32.457147],\n                            [107.313727, 32.489965],\n                            [107.356843, 32.506622],\n                            [107.382097, 32.54043],\n                            [107.436299, 32.529835],\n                            [107.438763, 32.465732],\n                            [107.460937, 32.453612],\n                            [107.456625, 32.41775],\n                            [107.489886, 32.425328],\n                            [107.527458, 32.38238],\n                            [107.598291, 32.411688],\n                            [107.648183, 32.413709],\n                            [107.680827, 32.397035],\n                            [107.707929, 32.331826],\n                            [107.753508, 32.338399],\n                            [107.812022, 32.247844],\n                            [107.864377, 32.201266],\n                            [107.890247, 32.214432],\n                            [107.924739, 32.197215],\n                            [107.979558, 32.146051],\n                            [108.024521, 32.177462],\n                            [108.018362, 32.2119],\n                            [108.086731, 32.233165],\n                            [108.143398, 32.219495],\n                            [108.156948, 32.239239],\n                            [108.179738, 32.221521],\n                            [108.240716, 32.274666],\n                            [108.310933, 32.232152],\n                            [108.389773, 32.263533],\n                            [108.414411, 32.252399],\n                            [108.469846, 32.270618],\n                            [108.507418, 32.245819],\n                            [108.509882, 32.201266],\n                            [108.543758, 32.177969],\n                            [108.585026, 32.17189],\n                            [108.676801, 32.10297],\n                            [108.734084, 32.106519],\n                            [108.75133, 32.076098],\n                            [108.78767, 32.04871],\n                            [108.837561, 32.039072],\n                            [108.902235, 31.984774],\n                            [108.986619, 31.980205],\n                            [109.085785, 31.929428],\n                            [109.123357, 31.892851],\n                            [109.191111, 31.85575],\n                            [109.195422, 31.817618],\n                            [109.27611, 31.79931],\n                            [109.279806, 31.776418],\n                            [109.253936, 31.759628],\n                            [109.282885, 31.743343],\n                            [109.281654, 31.716874],\n                            [109.381436, 31.705165],\n                            [109.446109, 31.722983],\n                            [109.502776, 31.716365],\n                            [109.549587, 31.73011],\n                            [109.585928, 31.726546],\n                            [109.592087, 31.789136],\n                            [109.633971, 31.804396],\n                            [109.633971, 31.824738],\n                            [109.60379, 31.885737],\n                            [109.584696, 31.900472],\n                            [109.62042, 31.928412],\n                            [109.631507, 31.962436],\n                            [109.590855, 32.012688],\n                            [109.590855, 32.047696],\n                            [109.621652, 32.106519],\n                            [109.58716, 32.161251],\n                            [109.604406, 32.199241],\n                            [109.592703, 32.219495],\n                            [109.550203, 32.225065],\n                            [109.528645, 32.270112],\n                            [109.495385, 32.300468],\n                            [109.513247, 32.342444],\n                            [109.502776, 32.38895],\n                            [109.529877, 32.405625],\n                            [109.526797, 32.43341],\n                            [109.575457, 32.506622],\n                            [109.637051, 32.540935],\n                            [109.619804, 32.56767],\n                            [109.631507, 32.599943],\n                            [109.726978, 32.608513],\n                            [109.746072, 32.594901],\n                            [109.816905, 32.577252],\n                            [109.910528, 32.592884],\n                            [109.97089, 32.577756],\n                            [110.017701, 32.546989],\n                            [110.084223, 32.580782],\n                            [110.090382, 32.617083],\n                            [110.124259, 32.616579],\n                            [110.153824, 32.593388],\n                            [110.206179, 32.633212],\n                            [110.156903, 32.683093],\n                            [110.159367, 32.767173],\n                            [110.127338, 32.77774],\n                            [110.142121, 32.802895],\n                            [110.105164, 32.832569],\n                            [110.051578, 32.851676],\n                            [109.988752, 32.886359],\n                            [109.927158, 32.887364],\n                            [109.907448, 32.903947],\n                            [109.856941, 32.910479],\n                            [109.847702, 32.893395],\n                            [109.789804, 32.882339],\n                            [109.76455, 32.909474],\n                            [109.785492, 32.987316],\n                            [109.794731, 33.067095],\n                            [109.704188, 33.101694],\n                            [109.688174, 33.116733],\n                            [109.576073, 33.110216],\n                            [109.522486, 33.138785],\n                            [109.468283, 33.140288],\n                            [109.438718, 33.152314],\n                            [109.498464, 33.207412],\n                            [109.514479, 33.237951],\n                            [109.60687, 33.235949],\n                            [109.619804, 33.275484],\n                            [109.649985, 33.251465],\n                            [109.693101, 33.254468],\n                            [109.732521, 33.231443],\n                            [109.813209, 33.236449],\n                            [109.852013, 33.247961],\n                            [109.916687, 33.229942],\n                            [109.973353, 33.203907],\n                            [109.999223, 33.212419],\n                            [110.031252, 33.191888],\n                            [110.164911, 33.209415],\n                            [110.218497, 33.163336],\n                            [110.285635, 33.171352],\n                            [110.33799, 33.160331],\n                            [110.372482, 33.186379],\n                            [110.398352, 33.176862],\n                            [110.398352, 33.176862],\n                            [110.471032, 33.171352],\n                            [110.54125, 33.255469],\n                            [110.57759, 33.250464],\n                            [110.59422, 33.168346],\n                            [110.623785, 33.143796],\n                            [110.650887, 33.157324],\n                            [110.702626, 33.097182],\n                            [110.753133, 33.15031],\n                            [110.824582, 33.158327],\n                            [110.828893, 33.201403],\n                            [110.865234, 33.213921],\n                            [110.9219, 33.203907],\n                            [110.960704, 33.253967],\n                            [110.984726, 33.255469],\n                            [111.025994, 33.330504],\n                            [111.025994, 33.375495],\n                            [110.996429, 33.435946],\n                            [111.02661, 33.467903],\n                            [111.021066, 33.471397],\n                            [111.021682, 33.476389],\n                            [111.02661, 33.478386],\n                            [111.002588, 33.535772],\n                            [111.00382, 33.578662],\n                            [110.966864, 33.609071],\n                            [110.878784, 33.634486],\n                            [110.823966, 33.685793],\n                            [110.831973, 33.713675],\n                            [110.81719, 33.751003],\n                            [110.782082, 33.796272],\n                            [110.74143, 33.798759],\n                            [110.712481, 33.833564],\n                            [110.66259, 33.85295],\n                            [110.612083, 33.852453],\n                            [110.587445, 33.887733],\n                            [110.628713, 33.910086],\n                            [110.627481, 33.925482],\n                            [110.665669, 33.937895],\n                            [110.671213, 33.966192],\n                            [110.620706, 34.035652],\n                            [110.587445, 34.023252],\n                            [110.591757, 34.101586],\n                            [110.61393, 34.113478],\n                            [110.642264, 34.161032],\n                            [110.621938, 34.177372],\n                            [110.55788, 34.193214],\n                            [110.55172, 34.213012],\n                            [110.507989, 34.217466],\n                            [110.43962, 34.243196],\n                            [110.428533, 34.288203],\n                            [110.451938, 34.292653],\n                            [110.503677, 34.33714],\n                            [110.473496, 34.393457],\n                            [110.403279, 34.433448],\n                            [110.403279, 34.433448],\n                            [110.360779, 34.516825],\n                            [110.372482, 34.544435],\n                            [110.404511, 34.557743],\n                            [110.366939, 34.566614],\n                            [110.379257, 34.600612]\n                        ]\n                    ],\n                    [\n                        [\n                            [111.02661, 33.478386],\n                            [111.021682, 33.476389],\n                            [111.021066, 33.471397],\n                            [111.02661, 33.467903],\n                            [111.02661, 33.478386]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.076921, 32.76365],\n                            [106.07261, 32.76365],\n                            [106.071378, 32.758114],\n                            [106.076305, 32.759121],\n                            [106.076921, 32.76365]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 620000,\n                \"name\": \"Gansu\",\n                \"center\": [103.823557, 36.058039],\n                \"childrenNum\": 14,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 27,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [106.506231, 35.737514],\n                            [106.504383, 35.736057],\n                            [106.498224, 35.732656],\n                            [106.49268, 35.732656],\n                            [106.434782, 35.688436],\n                            [106.460036, 35.643705],\n                            [106.47913, 35.575101],\n                            [106.460036, 35.578995],\n                            [106.440941, 35.52641],\n                            [106.465579, 35.481101],\n                            [106.490217, 35.480613],\n                            [106.483441, 35.450393],\n                            [106.503767, 35.415284],\n                            [106.501304, 35.364056],\n                            [106.472354, 35.310842],\n                            [106.415688, 35.276161],\n                            [106.368261, 35.273718],\n                            [106.363333, 35.238532],\n                            [106.319601, 35.265411],\n                            [106.241377, 35.358687],\n                            [106.237681, 35.409431],\n                            [106.196414, 35.409919],\n                            [106.173008, 35.437716],\n                            [106.129892, 35.393333],\n                            [106.113262, 35.361616],\n                            [106.083081, 35.421624],\n                            [106.073226, 35.420649],\n                            [106.067682, 35.436254],\n                            [106.073226, 35.447468],\n                            [106.071378, 35.449418],\n                            [106.06953, 35.458193],\n                            [106.071994, 35.463555],\n                            [106.054132, 35.45478],\n                            [106.034422, 35.469404],\n                            [106.002393, 35.438692],\n                            [105.894603, 35.413821],\n                            [105.897683, 35.451368],\n                            [106.048588, 35.488898],\n                            [106.047356, 35.498155],\n                            [106.023335, 35.49377],\n                            [106.017175, 35.519103],\n                            [105.900147, 35.54735],\n                            [105.868734, 35.540046],\n                            [105.847176, 35.490359],\n                            [105.816379, 35.575101],\n                            [105.800365, 35.564878],\n                            [105.762176, 35.602841],\n                            [105.759097, 35.634464],\n                            [105.713517, 35.650513],\n                            [105.722756, 35.673366],\n                            [105.690727, 35.698643],\n                            [105.723988, 35.725854],\n                            [105.740618, 35.698643],\n                            [105.759097, 35.724883],\n                            [105.70243, 35.733142],\n                            [105.667322, 35.749657],\n                            [105.595873, 35.715651],\n                            [105.481924, 35.727312],\n                            [105.457286, 35.771511],\n                            [105.432033, 35.787533],\n                            [105.428953, 35.819082],\n                            [105.408627, 35.822479],\n                            [105.38091, 35.792873],\n                            [105.371055, 35.844312],\n                            [105.39754, 35.857409],\n                            [105.350113, 35.875839],\n                            [105.324859, 35.941761],\n                            [105.343954, 36.033767],\n                            [105.406163, 36.074409],\n                            [105.430801, 36.10391],\n                            [105.491163, 36.101009],\n                            [105.515185, 36.147415],\n                            [105.478844, 36.213111],\n                            [105.460366, 36.223733],\n                            [105.45975, 36.268137],\n                            [105.476381, 36.293224],\n                            [105.455439, 36.321678],\n                            [105.425873, 36.330357],\n                            [105.401236, 36.369881],\n                            [105.398156, 36.430575],\n                            [105.363048, 36.443093],\n                            [105.362432, 36.496514],\n                            [105.322396, 36.535954],\n                            [105.281744, 36.522489],\n                            [105.252179, 36.553263],\n                            [105.2762, 36.563358],\n                            [105.261418, 36.602764],\n                            [105.22015, 36.631105],\n                            [105.225693, 36.664716],\n                            [105.201056, 36.700711],\n                            [105.218302, 36.730455],\n                            [105.272505, 36.739567],\n                            [105.275584, 36.752515],\n                            [105.319932, 36.742924],\n                            [105.340874, 36.764502],\n                            [105.334714, 36.80093],\n                            [105.303302, 36.820575],\n                            [105.279896, 36.86751],\n                            [105.244787, 36.894796],\n                            [105.178882, 36.892403],\n                            [105.185657, 36.942164],\n                            [105.165331, 36.99476],\n                            [105.128991, 36.996194],\n                            [105.05939, 37.022956],\n                            [105.03968, 37.007187],\n                            [105.004571, 37.035378],\n                            [104.95468, 37.040156],\n                            [104.954064, 37.077407],\n                            [104.914644, 37.097935],\n                            [104.888158, 37.15901],\n                            [104.864753, 37.17284],\n                            [104.85613, 37.211933],\n                            [104.776673, 37.246718],\n                            [104.717543, 37.208597],\n                            [104.638087, 37.201923],\n                            [104.600515, 37.242907],\n                            [104.624536, 37.298627],\n                            [104.651022, 37.290534],\n                            [104.673812, 37.317668],\n                            [104.713848, 37.329566],\n                            [104.662109, 37.367626],\n                            [104.679971, 37.408044],\n                            [104.521059, 37.43466],\n                            [104.499501, 37.421353],\n                            [104.448994, 37.42468],\n                            [104.437907, 37.445589],\n                            [104.365226, 37.418026],\n                            [104.298705, 37.414223],\n                            [104.287002, 37.428007],\n                            [104.237727, 37.411847],\n                            [104.183524, 37.406618],\n                            [104.089285, 37.465067],\n                            [103.935916, 37.572818],\n                            [103.874938, 37.604117],\n                            [103.841062, 37.64725],\n                            [103.683381, 37.777919],\n                            [103.627947, 37.797783],\n                            [103.40744, 37.860651],\n                            [103.362477, 38.037621],\n                            [103.368636, 38.08898],\n                            [103.53494, 38.156776],\n                            [103.507838, 38.280905],\n                            [103.465339, 38.353215],\n                            [103.416063, 38.404821],\n                            [103.85954, 38.64454],\n                            [104.011677, 38.85923],\n                            [104.044322, 38.895105],\n                            [104.173053, 38.94446],\n                            [104.196459, 38.9882],\n                            [104.190915, 39.042139],\n                            [104.207546, 39.083495],\n                            [104.171205, 39.160567],\n                            [104.047401, 39.297788],\n                            [104.073271, 39.351953],\n                            [104.089901, 39.419947],\n                            [103.955626, 39.456923],\n                            [103.85338, 39.461543],\n                            [103.728961, 39.430117],\n                            [103.595302, 39.386652],\n                            [103.428998, 39.353341],\n                            [103.344615, 39.331588],\n                            [103.259615, 39.263971],\n                            [103.188166, 39.215302],\n                            [103.133347, 39.192579],\n                            [103.007696, 39.099753],\n                            [102.883892, 39.120649],\n                            [102.616574, 39.171703],\n                            [102.579002, 39.183301],\n                            [102.45335, 39.255167],\n                            [102.3548, 39.231993],\n                            [102.276576, 39.188868],\n                            [102.050526, 39.141075],\n                            [102.012338, 39.127149],\n                            [101.902701, 39.111827],\n                            [101.833715, 39.08907],\n                            [101.926106, 39.000758],\n                            [101.955055, 38.985874],\n                            [102.045599, 38.904885],\n                            [102.075164, 38.891378],\n                            [101.941505, 38.808883],\n                            [101.873751, 38.733761],\n                            [101.777049, 38.66043],\n                            [101.672955, 38.6908],\n                            [101.601506, 38.65529],\n                            [101.562702, 38.713218],\n                            [101.412413, 38.764099],\n                            [101.331109, 38.777164],\n                            [101.307087, 38.80282],\n                            [101.34158, 38.822406],\n                            [101.33542, 38.847113],\n                            [101.24303, 38.860628],\n                            [101.237486, 38.907214],\n                            [101.198682, 38.943064],\n                            [101.228863, 39.020754],\n                            [101.117378, 38.975174],\n                            [100.969553, 38.946788],\n                            [100.961545, 39.005874],\n                            [100.901799, 39.030053],\n                            [100.875314, 39.002619],\n                            [100.835278, 39.025869],\n                            [100.829118, 39.075133],\n                            [100.864227, 39.106719],\n                            [100.842669, 39.199999],\n                            [100.842053, 39.405614],\n                            [100.707778, 39.404689],\n                            [100.606764, 39.387577],\n                            [100.498975, 39.400527],\n                            [100.500823, 39.481408],\n                            [100.44354, 39.485565],\n                            [100.326512, 39.509118],\n                            [100.301258, 39.572345],\n                            [100.314193, 39.606935],\n                            [100.250135, 39.685274],\n                            [100.128179, 39.702312],\n                            [100.040716, 39.757083],\n                            [99.958796, 39.769504],\n                            [99.904593, 39.785601],\n                            [99.822058, 39.860063],\n                            [99.672384, 39.888079],\n                            [99.469124, 39.875221],\n                            [99.440791, 39.885783],\n                            [99.459885, 39.898181],\n                            [99.491298, 39.884406],\n                            [99.533182, 39.891753],\n                            [99.714268, 39.972061],\n                            [99.751225, 40.006909],\n                            [99.841152, 40.013326],\n                            [99.927383, 40.063727],\n                            [99.955716, 40.150695],\n                            [100.007455, 40.20008],\n                            [100.169447, 40.277743],\n                            [100.169447, 40.541131],\n                            [100.242744, 40.618855],\n                            [100.237201, 40.716905],\n                            [100.224882, 40.727337],\n                            [100.107853, 40.875475],\n                            [100.057346, 40.908049],\n                            [99.985897, 40.909858],\n                            [99.673, 40.93292],\n                            [99.565827, 40.846961],\n                            [99.174705, 40.858278],\n                            [99.172858, 40.747289],\n                            [99.12543, 40.715091],\n                            [99.102025, 40.676522],\n                            [99.041662, 40.693767],\n                            [98.984996, 40.782644],\n                            [98.790975, 40.705564],\n                            [98.80699, 40.660181],\n                            [98.802678, 40.607043],\n                            [98.762642, 40.639748],\n                            [98.72199, 40.657911],\n                            [98.689345, 40.691952],\n                            [98.668403, 40.773128],\n                            [98.569853, 40.746836],\n                            [98.627751, 40.677884],\n                            [98.344419, 40.568413],\n                            [98.333332, 40.918903],\n                            [98.25018, 40.93925],\n                            [98.184891, 40.988056],\n                            [98.142391, 41.001607],\n                            [97.971776, 41.09774],\n                            [97.903407, 41.168057],\n                            [97.629314, 41.440498],\n                            [97.613915, 41.477276],\n                            [97.84674, 41.656379],\n                            [97.653335, 41.986856],\n                            [97.500582, 42.243894],\n                            [97.371235, 42.457076],\n                            [97.172903, 42.795257],\n                            [96.968411, 42.756161],\n                            [96.742361, 42.75704],\n                            [96.386348, 42.727592],\n                            [96.166458, 42.623314],\n                            [96.103632, 42.604375],\n                            [96.072219, 42.569566],\n                            [96.02356, 42.542675],\n                            [96.0174, 42.482239],\n                            [95.978596, 42.436762],\n                            [96.06606, 42.414674],\n                            [96.042038, 42.352787],\n                            [96.040806, 42.326688],\n                            [96.178161, 42.21775],\n                            [96.077147, 42.149457],\n                            [96.13874, 42.05399],\n                            [96.137509, 42.019765],\n                            [96.117183, 41.985966],\n                            [96.054973, 41.936124],\n                            [95.998306, 41.906289],\n                            [95.855408, 41.849699],\n                            [95.801206, 41.848361],\n                            [95.759322, 41.835878],\n                            [95.65646, 41.826067],\n                            [95.57146, 41.796181],\n                            [95.445193, 41.719841],\n                            [95.39407, 41.693481],\n                            [95.335556, 41.644305],\n                            [95.299831, 41.565994],\n                            [95.247476, 41.61344],\n                            [95.194505, 41.694821],\n                            [95.199433, 41.719395],\n                            [95.16494, 41.735474],\n                            [95.135991, 41.772976],\n                            [95.110738, 41.768513],\n                            [95.011572, 41.726541],\n                            [94.969072, 41.718948],\n                            [94.861898, 41.668451],\n                            [94.809543, 41.619256],\n                            [94.750413, 41.538227],\n                            [94.534219, 41.505966],\n                            [94.184365, 41.268444],\n                            [94.01067, 41.114875],\n                            [93.908424, 40.983539],\n                            [93.809874, 40.879548],\n                            [93.820961, 40.793519],\n                            [93.760599, 40.664721],\n                            [93.506216, 40.648376],\n                            [92.928465, 40.572504],\n                            [92.920458, 40.391792],\n                            [92.906907, 40.310609],\n                            [92.796654, 40.153897],\n                            [92.745531, 39.868331],\n                            [92.687632, 39.657174],\n                            [92.639589, 39.514196],\n                            [92.52564, 39.368611],\n                            [92.378431, 39.258411],\n                            [92.339011, 39.236628],\n                            [92.343938, 39.146181],\n                            [92.366112, 39.096037],\n                            [92.366728, 39.059335],\n                            [92.41046, 39.03842],\n                            [92.459119, 39.042604],\n                            [92.459119, 39.063982],\n                            [92.489916, 39.099753],\n                            [92.545966, 39.111362],\n                            [92.659299, 39.109969],\n                            [92.765857, 39.136898],\n                            [92.866871, 39.138754],\n                            [92.889045, 39.160103],\n                            [92.938936, 39.169848],\n                            [92.978356, 39.143396],\n                            [93.043029, 39.146645],\n                            [93.115094, 39.17959],\n                            [93.142196, 39.160567],\n                            [93.131725, 39.108112],\n                            [93.165601, 39.090928],\n                            [93.198246, 39.045857],\n                            [93.179152, 38.923977],\n                            [93.237666, 38.916062],\n                            [93.274007, 38.896036],\n                            [93.453245, 38.915596],\n                            [93.729186, 38.924443],\n                            [93.834511, 38.867618],\n                            [93.884403, 38.867618],\n                            [93.884403, 38.826136],\n                            [93.769838, 38.821007],\n                            [93.756287, 38.807484],\n                            [93.773533, 38.771099],\n                            [93.800019, 38.750566],\n                            [93.885018, 38.720689],\n                            [93.95154, 38.715086],\n                            [93.973098, 38.724891],\n                            [94.281067, 38.7599],\n                            [94.370379, 38.7627],\n                            [94.511429, 38.445142],\n                            [94.527443, 38.425922],\n                            [94.527443, 38.365416],\n                            [94.56132, 38.351807],\n                            [94.582878, 38.36917],\n                            [94.672805, 38.386998],\n                            [94.812623, 38.385591],\n                            [94.861282, 38.393565],\n                            [94.884072, 38.414669],\n                            [94.973999, 38.430142],\n                            [95.045448, 38.418889],\n                            [95.072549, 38.402476],\n                            [95.122441, 38.417014],\n                            [95.140919, 38.392158],\n                            [95.185266, 38.379492],\n                            [95.209904, 38.327868],\n                            [95.229614, 38.330685],\n                            [95.259179, 38.302981],\n                            [95.315846, 38.318947],\n                            [95.408236, 38.300163],\n                            [95.440881, 38.310965],\n                            [95.455664, 38.291709],\n                            [95.487693, 38.314721],\n                            [95.51849, 38.294997],\n                            [95.585011, 38.343359],\n                            [95.608417, 38.339134],\n                            [95.671858, 38.388405],\n                            [95.703887, 38.400131],\n                            [95.723597, 38.378554],\n                            [95.775952, 38.356031],\n                            [95.83693, 38.344298],\n                            [95.852945, 38.287481],\n                            [95.89606, 38.2903],\n                            [95.932401, 38.259291],\n                            [95.93856, 38.237202],\n                            [96.006929, 38.207582],\n                            [96.06606, 38.173245],\n                            [96.109175, 38.187358],\n                            [96.221892, 38.149246],\n                            [96.252689, 38.167599],\n                            [96.264392, 38.145952],\n                            [96.313051, 38.161952],\n                            [96.301964, 38.183124],\n                            [96.335841, 38.246132],\n                            [96.378341, 38.277146],\n                            [96.46334, 38.277616],\n                            [96.665369, 38.23015],\n                            [96.655514, 38.295936],\n                            [96.638883, 38.307208],\n                            [96.626564, 38.356031],\n                            [96.698013, 38.422172],\n                            [96.707868, 38.459203],\n                            [96.6666, 38.483567],\n                            [96.706637, 38.505582],\n                            [96.780549, 38.504177],\n                            [96.800259, 38.52759],\n                            [96.767614, 38.552399],\n                            [96.808882, 38.582346],\n                            [96.7941, 38.608072],\n                            [96.847071, 38.599186],\n                            [96.876636, 38.580475],\n                            [96.961019, 38.558015],\n                            [97.055874, 38.594508],\n                            [97.047251, 38.653888],\n                            [97.057722, 38.67258],\n                            [97.009063, 38.702477],\n                            [97.023229, 38.755699],\n                            [97.00044, 38.7613],\n                            [96.987505, 38.793025],\n                            [96.993664, 38.834993],\n                            [96.983809, 38.869016],\n                            [96.940693, 38.90768],\n                            [96.938846, 38.95563],\n                            [96.965331, 39.017034],\n                            [96.95794, 39.041674],\n                            [96.969643, 39.097895],\n                            [97.012142, 39.142004],\n                            [96.962251, 39.198144],\n                            [97.017686, 39.208347],\n                            [97.060186, 39.19768],\n                            [97.14149, 39.199999],\n                            [97.220946, 39.193042],\n                            [97.315185, 39.164744],\n                            [97.347213, 39.167528],\n                            [97.371235, 39.140611],\n                            [97.401416, 39.146645],\n                            [97.458698, 39.117863],\n                            [97.504894, 39.076527],\n                            [97.58127, 39.052364],\n                            [97.679205, 39.010524],\n                            [97.701379, 38.963076],\n                            [97.828878, 38.93003],\n                            [97.875689, 38.898365],\n                            [98.009348, 38.85923],\n                            [98.029058, 38.834061],\n                            [98.068478, 38.816344],\n                            [98.091884, 38.786495],\n                            [98.167645, 38.840121],\n                            [98.242173, 38.880664],\n                            [98.235398, 38.918855],\n                            [98.276666, 38.963541],\n                            [98.287753, 38.992386],\n                            [98.280977, 39.027263],\n                            [98.316702, 39.040744],\n                            [98.383839, 39.029588],\n                            [98.401086, 39.001688],\n                            [98.432498, 38.996107],\n                            [98.428187, 38.976104],\n                            [98.457752, 38.952838],\n                            [98.526737, 38.95563],\n                            [98.584635, 38.93003],\n                            [98.624056, 38.959353],\n                            [98.612353, 38.977035],\n                            [98.661628, 38.993782],\n                            [98.70536, 39.043533],\n                            [98.730613, 39.057011],\n                            [98.743548, 39.086747],\n                            [98.816845, 39.085818],\n                            [98.818076, 39.064911],\n                            [98.886446, 39.040744],\n                            [98.903076, 39.012384],\n                            [98.951735, 38.987735],\n                            [99.054597, 38.97657],\n                            [99.107568, 38.951907],\n                            [99.071843, 38.921184],\n                            [99.068764, 38.896968],\n                            [99.141445, 38.852706],\n                            [99.222133, 38.788827],\n                            [99.291118, 38.765966],\n                            [99.361951, 38.718354],\n                            [99.375502, 38.684727],\n                            [99.412458, 38.665571],\n                            [99.450646, 38.60433],\n                            [99.501769, 38.612281],\n                            [99.52887, 38.546314],\n                            [99.585537, 38.498556],\n                            [99.63974, 38.474666],\n                            [99.65945, 38.449361],\n                            [99.727203, 38.415607],\n                            [99.758, 38.410449],\n                            [99.826985, 38.370109],\n                            [99.960028, 38.320825],\n                            [100.001912, 38.315191],\n                            [100.049955, 38.283254],\n                            [100.071513, 38.284663],\n                            [100.117093, 38.253652],\n                            [100.126332, 38.231561],\n                            [100.182998, 38.222158],\n                            [100.159592, 38.291239],\n                            [100.163904, 38.328337],\n                            [100.136803, 38.33444],\n                            [100.093071, 38.407166],\n                            [100.022238, 38.432017],\n                            [100.001296, 38.467169],\n                            [100.025933, 38.507923],\n                            [100.064122, 38.518694],\n                            [100.086911, 38.492936],\n                            [100.113397, 38.497151],\n                            [100.163288, 38.461546],\n                            [100.24028, 38.441861],\n                            [100.259374, 38.366355],\n                            [100.301874, 38.388405],\n                            [100.331439, 38.337257],\n                            [100.318505, 38.329276],\n                            [100.396729, 38.293118],\n                            [100.424446, 38.307208],\n                            [100.432453, 38.275267],\n                            [100.459555, 38.2654],\n                            [100.474953, 38.288891],\n                            [100.516837, 38.272448],\n                            [100.545786, 38.247072],\n                            [100.595061, 38.242372],\n                            [100.619083, 38.26587],\n                            [100.71517, 38.253652],\n                            [100.752126, 38.238612],\n                            [100.825423, 38.158658],\n                            [100.860531, 38.148305],\n                            [100.913502, 38.17889],\n                            [100.93814, 38.16007],\n                            [100.91843, 38.129006],\n                            [100.922125, 38.084741],\n                            [100.888864, 38.056001],\n                            [100.895024, 38.013107],\n                            [100.91843, 37.999432],\n                            [100.964009, 38.011221],\n                            [101.077342, 37.941874],\n                            [101.103211, 37.946593],\n                            [101.114298, 37.92016],\n                            [101.152486, 37.891356],\n                            [101.159262, 37.86821],\n                            [101.202994, 37.84742],\n                            [101.276906, 37.83655],\n                            [101.362522, 37.791162],\n                            [101.382848, 37.822369],\n                            [101.459224, 37.86632],\n                            [101.551615, 37.835604],\n                            [101.598427, 37.827569],\n                            [101.670491, 37.754264],\n                            [101.659405, 37.733441],\n                            [101.791832, 37.696041],\n                            [101.815853, 37.654357],\n                            [101.854657, 37.664781],\n                            [101.873135, 37.686569],\n                            [101.946432, 37.728235],\n                            [101.998787, 37.724921],\n                            [102.036359, 37.685149],\n                            [102.048678, 37.651515],\n                            [102.035128, 37.627819],\n                            [102.102265, 37.582304],\n                            [102.131214, 37.54625],\n                            [102.103497, 37.482641],\n                            [102.125055, 37.48549],\n                            [102.176794, 37.458892],\n                            [102.19712, 37.420403],\n                            [102.299981, 37.391404],\n                            [102.29875, 37.370004],\n                            [102.368351, 37.327662],\n                            [102.428097, 37.308624],\n                            [102.419474, 37.294343],\n                            [102.45335, 37.271487],\n                            [102.457662, 37.248147],\n                            [102.490307, 37.223371],\n                            [102.533422, 37.217176],\n                            [102.578386, 37.17284],\n                            [102.599944, 37.174748],\n                            [102.642444, 37.099845],\n                            [102.583314, 37.104618],\n                            [102.488459, 37.078362],\n                            [102.506321, 37.019134],\n                            [102.450271, 36.968467],\n                            [102.499546, 36.954599],\n                            [102.526031, 36.928291],\n                            [102.56114, 36.91968],\n                            [102.587009, 36.869904],\n                            [102.639364, 36.852666],\n                            [102.720052, 36.767858],\n                            [102.692335, 36.775528],\n                            [102.639364, 36.732853],\n                            [102.612879, 36.738129],\n                            [102.601176, 36.710307],\n                            [102.630741, 36.650793],\n                            [102.684328, 36.619097],\n                            [102.724364, 36.613813],\n                            [102.714509, 36.599401],\n                            [102.761936, 36.568645],\n                            [102.734219, 36.562396],\n                            [102.753313, 36.525855],\n                            [102.793349, 36.497957],\n                            [102.771791, 36.47438],\n                            [102.829689, 36.365544],\n                            [102.831537, 36.365544],\n                            [102.838928, 36.345783],\n                            [102.836465, 36.344819],\n                            [102.845704, 36.331803],\n                            [102.896827, 36.331803],\n                            [102.922696, 36.298047],\n                            [103.024942, 36.256556],\n                            [103.021246, 36.232906],\n                            [103.066826, 36.216974],\n                            [103.048964, 36.199107],\n                            [102.986754, 36.193312],\n                            [102.965812, 36.151765],\n                            [102.948566, 36.150798],\n                            [102.941174, 36.104877],\n                            [102.882044, 36.082632],\n                            [102.932551, 36.048285],\n                            [102.968276, 36.044414],\n                            [102.951645, 36.021667],\n                            [102.971971, 35.995525],\n                            [102.942406, 35.92674],\n                            [102.954725, 35.858864],\n                            [102.94487, 35.829757],\n                            [102.914073, 35.845282],\n                            [102.81737, 35.850133],\n                            [102.787189, 35.862745],\n                            [102.739146, 35.821023],\n                            [102.715125, 35.815685],\n                            [102.686175, 35.771996],\n                            [102.707733, 35.70496],\n                            [102.744074, 35.657807],\n                            [102.7644, 35.653431],\n                            [102.763168, 35.612086],\n                            [102.808747, 35.560496],\n                            [102.746537, 35.545403],\n                            [102.729291, 35.523487],\n                            [102.782878, 35.527871],\n                            [102.743458, 35.494745],\n                            [102.695414, 35.528358],\n                            [102.570995, 35.548324],\n                            [102.531575, 35.580455],\n                            [102.503241, 35.585322],\n                            [102.49893, 35.545403],\n                            [102.437952, 35.455268],\n                            [102.447807, 35.437229],\n                            [102.408387, 35.409431],\n                            [102.314764, 35.434303],\n                            [102.293822, 35.424063],\n                            [102.287663, 35.36552],\n                            [102.317844, 35.343067],\n                            [102.311684, 35.31426],\n                            [102.280887, 35.303028],\n                            [102.3123, 35.282512],\n                            [102.370199, 35.263946],\n                            [102.365887, 35.235599],\n                            [102.404075, 35.179366],\n                            [102.346793, 35.164201],\n                            [102.310452, 35.128967],\n                            [102.29567, 35.071681],\n                            [102.252554, 35.048657],\n                            [102.218062, 35.057475],\n                            [102.211286, 35.034937],\n                            [102.176178, 35.032977],\n                            [102.157699, 35.010923],\n                            [102.133678, 35.014844],\n                            [102.094874, 34.986901],\n                            [102.048062, 34.910868],\n                            [102.068388, 34.887798],\n                            [101.985852, 34.90007],\n                            [101.916867, 34.873561],\n                            [101.923027, 34.835746],\n                            [101.917483, 34.705964],\n                            [101.919947, 34.621791],\n                            [101.934729, 34.58731],\n                            [101.956287, 34.582876],\n                            [101.97415, 34.548871],\n                            [102.001867, 34.538519],\n                            [102.093026, 34.536547],\n                            [102.139837, 34.50351],\n                            [102.155852, 34.507456],\n                            [102.169402, 34.457631],\n                            [102.205743, 34.407777],\n                            [102.259329, 34.355917],\n                            [102.237156, 34.34307],\n                            [102.237156, 34.34307],\n                            [102.186649, 34.352952],\n                            [102.149692, 34.271885],\n                            [102.067772, 34.293642],\n                            [102.062229, 34.227858],\n                            [102.01357, 34.218456],\n                            [102.030816, 34.190739],\n                            [102.003099, 34.162022],\n                            [101.965526, 34.167469],\n                            [101.955055, 34.109514],\n                            [101.897773, 34.133791],\n                            [101.874367, 34.130323],\n                            [101.851578, 34.153108],\n                            [101.836795, 34.124378],\n                            [101.788136, 34.131809],\n                            [101.764114, 34.122892],\n                            [101.736397, 34.080275],\n                            [101.718535, 34.083249],\n                            [101.703136, 34.119424],\n                            [101.674187, 34.110506],\n                            [101.6206, 34.178857],\n                            [101.53868, 34.212022],\n                            [101.492485, 34.195689],\n                            [101.482014, 34.218951],\n                            [101.417956, 34.227858],\n                            [101.369913, 34.248143],\n                            [101.327413, 34.24468],\n                            [101.325565, 34.268423],\n                            [101.268899, 34.278808],\n                            [101.228863, 34.298586],\n                            [101.235022, 34.325279],\n                            [101.193754, 34.336646],\n                            [101.178356, 34.320831],\n                            [101.098284, 34.329233],\n                            [101.054552, 34.322808],\n                            [100.986799, 34.374689],\n                            [100.951074, 34.38358],\n                            [100.895024, 34.375183],\n                            [100.868538, 34.332693],\n                            [100.821727, 34.317371],\n                            [100.798321, 34.260014],\n                            [100.809408, 34.247153],\n                            [100.764445, 34.178857],\n                            [100.806329, 34.155584],\n                            [100.848828, 34.089692],\n                            [100.870386, 34.083744],\n                            [100.880857, 34.036644],\n                            [100.93506, 33.990013],\n                            [100.927669, 33.975126],\n                            [100.965857, 33.946832],\n                            [100.994806, 33.891707],\n                            [101.023139, 33.896178],\n                            [101.054552, 33.863386],\n                            [101.153718, 33.8445],\n                            [101.153102, 33.823124],\n                            [101.190675, 33.791796],\n                            [101.186363, 33.741051],\n                            [101.162957, 33.719649],\n                            [101.177124, 33.685295],\n                            [101.166653, 33.659894],\n                            [101.217776, 33.669856],\n                            [101.23687, 33.685793],\n                            [101.302776, 33.657902],\n                            [101.385312, 33.644949],\n                            [101.424732, 33.655411],\n                            [101.428427, 33.680315],\n                            [101.501724, 33.702723],\n                            [101.58426, 33.674339],\n                            [101.585492, 33.645448],\n                            [101.616905, 33.598603],\n                            [101.611977, 33.565199],\n                            [101.622448, 33.502343],\n                            [101.718535, 33.494857],\n                            [101.748716, 33.505337],\n                            [101.769042, 33.538765],\n                            [101.783208, 33.556721],\n                            [101.831252, 33.554726],\n                            [101.844186, 33.602591],\n                            [101.884222, 33.578163],\n                            [101.907012, 33.539264],\n                            [101.906396, 33.48188],\n                            [101.946432, 33.442937],\n                            [101.915635, 33.425957],\n                            [101.887302, 33.383991],\n                            [101.877447, 33.314502],\n                            [101.769658, 33.26898],\n                            [101.770274, 33.248962],\n                            [101.83002, 33.213921],\n                            [101.841723, 33.184876],\n                            [101.825708, 33.119239],\n                            [101.865744, 33.103198],\n                            [101.887302, 33.135778],\n                            [101.921795, 33.153817],\n                            [101.935345, 33.186879],\n                            [101.99386, 33.1999],\n                            [102.054838, 33.189884],\n                            [102.08933, 33.204908],\n                            [102.08933, 33.227439],\n                            [102.117047, 33.288492],\n                            [102.144765, 33.273983],\n                            [102.160163, 33.242956],\n                            [102.200815, 33.223434],\n                            [102.217446, 33.247961],\n                            [102.192192, 33.337005],\n                            [102.218062, 33.349503],\n                            [102.258098, 33.409472],\n                            [102.296286, 33.413969],\n                            [102.310452, 33.397982],\n                            [102.368967, 33.41247],\n                            [102.392988, 33.404477],\n                            [102.447807, 33.454922],\n                            [102.462589, 33.449429],\n                            [102.461358, 33.501345],\n                            [102.446575, 33.53228],\n                            [102.477988, 33.543254],\n                            [102.440416, 33.574673],\n                            [102.346793, 33.605582],\n                            [102.31538, 33.665374],\n                            [102.342481, 33.725622],\n                            [102.284583, 33.719151],\n                            [102.324619, 33.754486],\n                            [102.296286, 33.783838],\n                            [102.243315, 33.786823],\n                            [102.261177, 33.821136],\n                            [102.25317, 33.861399],\n                            [102.136142, 33.965199],\n                            [102.16817, 33.983066],\n                            [102.226069, 33.963214],\n                            [102.248858, 33.98654],\n                            [102.287047, 33.977607],\n                            [102.315996, 33.993983],\n                            [102.345561, 33.969666],\n                            [102.392372, 33.971651],\n                            [102.406539, 34.033172],\n                            [102.437336, 34.087214],\n                            [102.471213, 34.072839],\n                            [102.511865, 34.086222],\n                            [102.615958, 34.099604],\n                            [102.649219, 34.080275],\n                            [102.655994, 34.113478],\n                            [102.598712, 34.14766],\n                            [102.651067, 34.165983],\n                            [102.664002, 34.192719],\n                            [102.694799, 34.198659],\n                            [102.728675, 34.235774],\n                            [102.779798, 34.236764],\n                            [102.798276, 34.272874],\n                            [102.856791, 34.270895],\n                            [102.85987, 34.301058],\n                            [102.911609, 34.312923],\n                            [102.949181, 34.292159],\n                            [102.977515, 34.252595],\n                            [102.973203, 34.205588],\n                            [103.005848, 34.184798],\n                            [103.052043, 34.195194],\n                            [103.100087, 34.181828],\n                            [103.124108, 34.162022],\n                            [103.121644, 34.112487],\n                            [103.178927, 34.079779],\n                            [103.129652, 34.065899],\n                            [103.119797, 34.03466],\n                            [103.147514, 34.036644],\n                            [103.157369, 33.998944],\n                            [103.120413, 33.953286],\n                            [103.1315, 33.931937],\n                            [103.16476, 33.929454],\n                            [103.181391, 33.900649],\n                            [103.153673, 33.819147],\n                            [103.165376, 33.805721],\n                            [103.228202, 33.79478],\n                            [103.24976, 33.814175],\n                            [103.284868, 33.80224],\n                            [103.278709, 33.774387],\n                            [103.35447, 33.743539],\n                            [103.434542, 33.752993],\n                            [103.464723, 33.80224],\n                            [103.518309, 33.807213],\n                            [103.545411, 33.719649],\n                            [103.520157, 33.678323],\n                            [103.552186, 33.671351],\n                            [103.563889, 33.699735],\n                            [103.593454, 33.716164],\n                            [103.645809, 33.708697],\n                            [103.667983, 33.685793],\n                            [103.690772, 33.69376],\n                            [103.778236, 33.658898],\n                            [103.861388, 33.682307],\n                            [103.980264, 33.670852],\n                            [104.046169, 33.686291],\n                            [104.103452, 33.663381],\n                            [104.176749, 33.5996],\n                            [104.155191, 33.542755],\n                            [104.180444, 33.472895],\n                            [104.213089, 33.446932],\n                            [104.22048, 33.404477],\n                            [104.272219, 33.391486],\n                            [104.292545, 33.336505],\n                            [104.373849, 33.345004],\n                            [104.420045, 33.327004],\n                            [104.386168, 33.298497],\n                            [104.333813, 33.315502],\n                            [104.303632, 33.304499],\n                            [104.323958, 33.26898],\n                            [104.32827, 33.223934],\n                            [104.351059, 33.158828],\n                            [104.378161, 33.109214],\n                            [104.337509, 33.038002],\n                            [104.391711, 33.035493],\n                            [104.426204, 33.010906],\n                            [104.383704, 32.994343],\n                            [104.378161, 32.953174],\n                            [104.345516, 32.940117],\n                            [104.288234, 32.942628],\n                            [104.277147, 32.90244],\n                            [104.294393, 32.835586],\n                            [104.363994, 32.822511],\n                            [104.458849, 32.748551],\n                            [104.51182, 32.753585],\n                            [104.526602, 32.728416],\n                            [104.582653, 32.722374],\n                            [104.592508, 32.695685],\n                            [104.643015, 32.661935],\n                            [104.696601, 32.673522],\n                            [104.739717, 32.635228],\n                            [104.795768, 32.643292],\n                            [104.820405, 32.662943],\n                            [104.845659, 32.653873],\n                            [104.881999, 32.600951],\n                            [104.925115, 32.607505],\n                            [105.026745, 32.650346],\n                            [105.0791, 32.637244],\n                            [105.111128, 32.593893],\n                            [105.185041, 32.617587],\n                            [105.215222, 32.63674],\n                            [105.219534, 32.666469],\n                            [105.263265, 32.652362],\n                            [105.297758, 32.656897],\n                            [105.347033, 32.68259],\n                            [105.368591, 32.712807],\n                            [105.448663, 32.732946],\n                            [105.454207, 32.767173],\n                            [105.427721, 32.784281],\n                            [105.396308, 32.85067],\n                            [105.396308, 32.85067],\n                            [105.38091, 32.876307],\n                            [105.408011, 32.885857],\n                            [105.414171, 32.922034],\n                            [105.467757, 32.930071],\n                            [105.49917, 32.911986],\n                            [105.528119, 32.919019],\n                            [105.565692, 32.906962],\n                            [105.590329, 32.87681],\n                            [105.638373, 32.879323],\n                            [105.656851, 32.895405],\n                            [105.735691, 32.905454],\n                            [105.82685, 32.950663],\n                            [105.861959, 32.939112],\n                            [105.917393, 32.993841],\n                            [105.926632, 33.042517],\n                            [105.914929, 33.066092],\n                            [105.934639, 33.112221],\n                            [105.923552, 33.147805],\n                            [105.897067, 33.146803],\n                            [105.93156, 33.178365],\n                            [105.968516, 33.154318],\n                            [105.965436, 33.204407],\n                            [105.917393, 33.237951],\n                            [105.862574, 33.234447],\n                            [105.799133, 33.258471],\n                            [105.791741, 33.278486],\n                            [105.752937, 33.291994],\n                            [105.755401, 33.329004],\n                            [105.709822, 33.382991],\n                            [105.827466, 33.379993],\n                            [105.837937, 33.410971],\n                            [105.831162, 33.451926],\n                            [105.842248, 33.489866],\n                            [105.871198, 33.511325],\n                            [105.902611, 33.556222],\n                            [105.940183, 33.570684],\n                            [105.971596, 33.613058],\n                            [106.047356, 33.610067],\n                            [106.086776, 33.617045],\n                            [106.117573, 33.602591],\n                            [106.108334, 33.569686],\n                            [106.187174, 33.546746],\n                            [106.237681, 33.564201],\n                            [106.303587, 33.604585],\n                            [106.35163, 33.587137],\n                            [106.384891, 33.612061],\n                            [106.447101, 33.613058],\n                            [106.456956, 33.532779],\n                            [106.540108, 33.512822],\n                            [106.58076, 33.576169],\n                            [106.575832, 33.631497],\n                            [106.534564, 33.695254],\n                            [106.482825, 33.707203],\n                            [106.488369, 33.757969],\n                            [106.461883, 33.789807],\n                            [106.491448, 33.834559],\n                            [106.475434, 33.875809],\n                            [106.428007, 33.866368],\n                            [106.41076, 33.909093],\n                            [106.474202, 33.970659],\n                            [106.471738, 34.024244],\n                            [106.505615, 34.056479],\n                            [106.501919, 34.105055],\n                            [106.560434, 34.109514],\n                            [106.585071, 34.149641],\n                            [106.55797, 34.229837],\n                            [106.5321, 34.254079],\n                            [106.496376, 34.238248],\n                            [106.526557, 34.292159],\n                            [106.577064, 34.280786],\n                            [106.589383, 34.253584],\n                            [106.63373, 34.260014],\n                            [106.652825, 34.24369],\n                            [106.68239, 34.256057],\n                            [106.705179, 34.299575],\n                            [106.691013, 34.337635],\n                            [106.717498, 34.369255],\n                            [106.638042, 34.391481],\n                            [106.610941, 34.454177],\n                            [106.558586, 34.48822],\n                            [106.513622, 34.498085],\n                            [106.514238, 34.511894],\n                            [106.455108, 34.531617],\n                            [106.334384, 34.517811],\n                            [106.341159, 34.568093],\n                            [106.314058, 34.578934],\n                            [106.419384, 34.643458],\n                            [106.471122, 34.634102],\n                            [106.442173, 34.675455],\n                            [106.456956, 34.703996],\n                            [106.487137, 34.715311],\n                            [106.505615, 34.746789],\n                            [106.539492, 34.745805],\n                            [106.575216, 34.769897],\n                            [106.550579, 34.82936],\n                            [106.556122, 34.861285],\n                            [106.527789, 34.876507],\n                            [106.493296, 34.941289],\n                            [106.484673, 34.983959],\n                            [106.494528, 35.006021],\n                            [106.494528, 35.006021],\n                            [106.52163, 35.027587],\n                            [106.541956, 35.083925],\n                            [106.577064, 35.089312],\n                            [106.615252, 35.071191],\n                            [106.706411, 35.081966],\n                            [106.710723, 35.100574],\n                            [106.838222, 35.080007],\n                            [106.901664, 35.094698],\n                            [106.950323, 35.066782],\n                            [106.990975, 35.068252],\n                            [107.012533, 35.029547],\n                            [107.08275, 35.024156],\n                            [107.089526, 34.976604],\n                            [107.119707, 34.950119],\n                            [107.162206, 34.944233],\n                            [107.189308, 34.893198],\n                            [107.252749, 34.880925],\n                            [107.286626, 34.931968],\n                            [107.350068, 34.93393],\n                            [107.369162, 34.917738],\n                            [107.400575, 34.932949],\n                            [107.455394, 34.916757],\n                            [107.523763, 34.909886],\n                            [107.564415, 34.968757],\n                            [107.619849, 34.964834],\n                            [107.638943, 34.935402],\n                            [107.675284, 34.9511],\n                            [107.741805, 34.953553],\n                            [107.842203, 34.979056],\n                            [107.863145, 34.999158],\n                            [107.846515, 35.024646],\n                            [107.814486, 35.024646],\n                            [107.773218, 35.060904],\n                            [107.773218, 35.060904],\n                            [107.769523, 35.064333],\n                            [107.769523, 35.064333],\n                            [107.727639, 35.120157],\n                            [107.715936, 35.168114],\n                            [107.686371, 35.218],\n                            [107.652494, 35.244886],\n                            [107.667277, 35.257104],\n                            [107.737494, 35.267366],\n                            [107.745501, 35.311819],\n                            [107.841587, 35.276649],\n                            [107.867457, 35.256127],\n                            [107.960464, 35.263457],\n                            [107.949993, 35.245375],\n                            [108.049159, 35.253683],\n                            [108.094739, 35.280069],\n                            [108.174811, 35.304981],\n                            [108.221622, 35.296678],\n                            [108.239484, 35.256127],\n                            [108.296767, 35.267855],\n                            [108.345426, 35.300586],\n                            [108.36144, 35.279581],\n                            [108.48894, 35.275184],\n                            [108.547454, 35.304981],\n                            [108.583178, 35.294724],\n                            [108.614591, 35.328909],\n                            [108.61028, 35.355271],\n                            [108.631222, 35.418698],\n                            [108.605968, 35.503028],\n                            [108.625678, 35.537124],\n                            [108.618287, 35.557088],\n                            [108.539447, 35.605761],\n                            [108.517889, 35.699615],\n                            [108.533903, 35.746257],\n                            [108.527744, 35.82442],\n                            [108.499411, 35.872444],\n                            [108.518505, 35.905414],\n                            [108.562852, 35.921409],\n                            [108.593649, 35.950967],\n                            [108.652164, 35.94806],\n                            [108.659555, 35.990683],\n                            [108.688504, 36.021183],\n                            [108.682345, 36.062316],\n                            [108.712526, 36.138716],\n                            [108.646004, 36.254143],\n                            [108.641693, 36.359279],\n                            [108.651548, 36.384818],\n                            [108.618903, 36.433946],\n                            [108.562852, 36.43876],\n                            [108.510498, 36.47438],\n                            [108.514809, 36.445501],\n                            [108.495099, 36.422389],\n                            [108.460606, 36.422871],\n                            [108.408252, 36.45946],\n                            [108.391621, 36.505654],\n                            [108.365136, 36.519603],\n                            [108.340498, 36.559032],\n                            [108.262274, 36.549417],\n                            [108.245644, 36.571048],\n                            [108.210535, 36.577296],\n                            [108.204992, 36.606607],\n                            [108.204992, 36.606607],\n                            [108.222854, 36.631105],\n                            [108.1976, 36.630144],\n                            [108.163724, 36.563839],\n                            [108.092891, 36.587388],\n                            [108.079956, 36.614294],\n                            [108.060862, 36.592194],\n                            [108.001732, 36.639269],\n                            [108.02329, 36.647912],\n                            [108.006659, 36.683435],\n                            [107.938906, 36.655594],\n                            [107.940754, 36.694953],\n                            [107.914268, 36.720861],\n                            [107.907493, 36.750118],\n                            [107.866841, 36.766899],\n                            [107.768291, 36.792783],\n                            [107.742421, 36.811951],\n                            [107.722095, 36.802367],\n                            [107.670356, 36.83303],\n                            [107.642023, 36.819137],\n                            [107.5909, 36.836382],\n                            [107.540393, 36.828718],\n                            [107.533618, 36.867031],\n                            [107.478183, 36.908196],\n                            [107.365466, 36.905324],\n                            [107.336517, 36.925899],\n                            [107.310032, 36.912502],\n                            [107.291554, 36.979463],\n                            [107.291554, 36.979463],\n                            [107.288474, 37.008143],\n                            [107.288474, 37.008143],\n                            [107.28601, 37.054963],\n                            [107.268764, 37.099367],\n                            [107.234887, 37.096503],\n                            [107.181916, 37.143269],\n                            [107.133873, 37.134681],\n                            [107.095685, 37.115595],\n                            [107.030395, 37.140883],\n                            [107.031011, 37.108436],\n                            [106.998367, 37.106527],\n                            [106.905976, 37.151378],\n                            [106.912135, 37.110345],\n                            [106.891193, 37.098413],\n                            [106.818512, 37.141838],\n                            [106.776012, 37.158056],\n                            [106.772933, 37.120367],\n                            [106.750143, 37.09889],\n                            [106.728585, 37.121321],\n                            [106.687933, 37.12991],\n                            [106.673151, 37.1113],\n                            [106.6171, 37.135158],\n                            [106.605397, 37.127524],\n                            [106.645433, 37.064992],\n                            [106.666991, 37.016745],\n                            [106.646665, 37.000496],\n                            [106.64297, 36.962729],\n                            [106.594926, 36.967988],\n                            [106.595542, 36.94025],\n                            [106.540108, 36.984244],\n                            [106.549347, 36.941685],\n                            [106.601702, 36.918244],\n                            [106.609709, 36.878521],\n                            [106.609709, 36.878521],\n                            [106.626955, 36.892403],\n                            [106.637426, 36.867031],\n                            [106.637426, 36.867031],\n                            [106.657752, 36.820575],\n                            [106.627571, 36.752995],\n                            [106.644817, 36.72278],\n                            [106.59431, 36.750118],\n                            [106.514238, 36.715584],\n                            [106.519782, 36.708868],\n                            [106.519782, 36.708868],\n                            [106.530869, 36.690154],\n                            [106.490833, 36.685835],\n                            [106.491448, 36.628703],\n                            [106.444637, 36.624861],\n                            [106.465579, 36.583063],\n                            [106.444637, 36.557109],\n                            [106.397826, 36.576816],\n                            [106.392282, 36.556628],\n                            [106.363949, 36.577296],\n                            [106.37134, 36.549417],\n                            [106.39721, 36.548455],\n                            [106.455724, 36.496995],\n                            [106.494528, 36.494589],\n                            [106.523477, 36.468605],\n                            [106.492064, 36.422389],\n                            [106.510543, 36.379037],\n                            [106.497608, 36.31348],\n                            [106.470507, 36.306246],\n                            [106.504383, 36.266207],\n                            [106.54134, 36.25366],\n                            [106.559202, 36.292259],\n                            [106.647897, 36.259451],\n                            [106.685469, 36.273445],\n                            [106.698404, 36.244008],\n                            [106.735976, 36.23725],\n                            [106.772933, 36.212628],\n                            [106.808657, 36.21118],\n                            [106.833295, 36.229044],\n                            [106.858548, 36.206834],\n                            [106.858548, 36.206834],\n                            [106.873947, 36.178338],\n                            [106.873947, 36.178338],\n                            [106.930613, 36.138716],\n                            [106.925686, 36.115997],\n                            [106.957715, 36.091337],\n                            [106.940468, 36.064734],\n                            [106.928149, 36.011502],\n                            [106.94786, 35.988262],\n                            [106.90228, 35.943699],\n                            [106.93862, 35.952905],\n                            [106.940468, 35.931101],\n                            [106.912751, 35.93207],\n                            [106.849925, 35.887476],\n                            [106.927534, 35.810346],\n                            [106.897353, 35.759856],\n                            [106.868403, 35.771996],\n                            [106.867171, 35.738485],\n                            [106.819128, 35.7448],\n                            [106.806193, 35.70982],\n                            [106.750759, 35.725369],\n                            [106.750759, 35.689408],\n                            [106.674998, 35.728284],\n                            [106.66268, 35.70739],\n                            [106.633115, 35.714679],\n                            [106.620796, 35.743829],\n                            [106.595542, 35.727312],\n                            [106.566593, 35.738971],\n                            [106.506231, 35.737514]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.047356, 35.498155],\n                            [106.048588, 35.488898],\n                            [106.054132, 35.45478],\n                            [106.071994, 35.463555],\n                            [106.078769, 35.509848],\n                            [106.047356, 35.498155]\n                        ]\n                    ],\n                    [\n                        [\n                            [102.831537, 36.365544],\n                            [102.829689, 36.365544],\n                            [102.836465, 36.344819],\n                            [102.838928, 36.345783],\n                            [102.831537, 36.365544]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.073226, 35.447468],\n                            [106.067682, 35.436254],\n                            [106.073226, 35.420649],\n                            [106.083081, 35.421624],\n                            [106.073226, 35.447468]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.504383, 35.736057],\n                            [106.506231, 35.737514],\n                            [106.49268, 35.732656],\n                            [106.498224, 35.732656],\n                            [106.504383, 35.736057]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 630000,\n                \"name\": \"Qinghai\",\n                \"center\": [101.778916, 36.623178],\n                \"centroid\": [96.043533, 35.726403],\n                \"childrenNum\": 8,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 28,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [102.829689, 36.365544],\n                            [102.771791, 36.47438],\n                            [102.793349, 36.497957],\n                            [102.753313, 36.525855],\n                            [102.734219, 36.562396],\n                            [102.761936, 36.568645],\n                            [102.714509, 36.599401],\n                            [102.724364, 36.613813],\n                            [102.684328, 36.619097],\n                            [102.630741, 36.650793],\n                            [102.601176, 36.710307],\n                            [102.612879, 36.738129],\n                            [102.639364, 36.732853],\n                            [102.692335, 36.775528],\n                            [102.720052, 36.767858],\n                            [102.639364, 36.852666],\n                            [102.587009, 36.869904],\n                            [102.56114, 36.91968],\n                            [102.526031, 36.928291],\n                            [102.499546, 36.954599],\n                            [102.450271, 36.968467],\n                            [102.506321, 37.019134],\n                            [102.488459, 37.078362],\n                            [102.583314, 37.104618],\n                            [102.642444, 37.099845],\n                            [102.599944, 37.174748],\n                            [102.578386, 37.17284],\n                            [102.533422, 37.217176],\n                            [102.490307, 37.223371],\n                            [102.457662, 37.248147],\n                            [102.45335, 37.271487],\n                            [102.419474, 37.294343],\n                            [102.428097, 37.308624],\n                            [102.368351, 37.327662],\n                            [102.29875, 37.370004],\n                            [102.299981, 37.391404],\n                            [102.19712, 37.420403],\n                            [102.176794, 37.458892],\n                            [102.125055, 37.48549],\n                            [102.103497, 37.482641],\n                            [102.131214, 37.54625],\n                            [102.102265, 37.582304],\n                            [102.035128, 37.627819],\n                            [102.048678, 37.651515],\n                            [102.036359, 37.685149],\n                            [101.998787, 37.724921],\n                            [101.946432, 37.728235],\n                            [101.873135, 37.686569],\n                            [101.854657, 37.664781],\n                            [101.815853, 37.654357],\n                            [101.791832, 37.696041],\n                            [101.659405, 37.733441],\n                            [101.670491, 37.754264],\n                            [101.598427, 37.827569],\n                            [101.551615, 37.835604],\n                            [101.459224, 37.86632],\n                            [101.382848, 37.822369],\n                            [101.362522, 37.791162],\n                            [101.276906, 37.83655],\n                            [101.202994, 37.84742],\n                            [101.159262, 37.86821],\n                            [101.152486, 37.891356],\n                            [101.114298, 37.92016],\n                            [101.103211, 37.946593],\n                            [101.077342, 37.941874],\n                            [100.964009, 38.011221],\n                            [100.91843, 37.999432],\n                            [100.895024, 38.013107],\n                            [100.888864, 38.056001],\n                            [100.922125, 38.084741],\n                            [100.91843, 38.129006],\n                            [100.93814, 38.16007],\n                            [100.913502, 38.17889],\n                            [100.860531, 38.148305],\n                            [100.825423, 38.158658],\n                            [100.752126, 38.238612],\n                            [100.71517, 38.253652],\n                            [100.619083, 38.26587],\n                            [100.595061, 38.242372],\n                            [100.545786, 38.247072],\n                            [100.516837, 38.272448],\n                            [100.474953, 38.288891],\n                            [100.459555, 38.2654],\n                            [100.432453, 38.275267],\n                            [100.424446, 38.307208],\n                            [100.396729, 38.293118],\n                            [100.318505, 38.329276],\n                            [100.331439, 38.337257],\n                            [100.301874, 38.388405],\n                            [100.259374, 38.366355],\n                            [100.24028, 38.441861],\n                            [100.163288, 38.461546],\n                            [100.113397, 38.497151],\n                            [100.086911, 38.492936],\n                            [100.064122, 38.518694],\n                            [100.025933, 38.507923],\n                            [100.001296, 38.467169],\n                            [100.022238, 38.432017],\n                            [100.093071, 38.407166],\n                            [100.136803, 38.33444],\n                            [100.163904, 38.328337],\n                            [100.159592, 38.291239],\n                            [100.182998, 38.222158],\n                            [100.126332, 38.231561],\n                            [100.117093, 38.253652],\n                            [100.071513, 38.284663],\n                            [100.049955, 38.283254],\n                            [100.001912, 38.315191],\n                            [99.960028, 38.320825],\n                            [99.826985, 38.370109],\n                            [99.758, 38.410449],\n                            [99.727203, 38.415607],\n                            [99.65945, 38.449361],\n                            [99.63974, 38.474666],\n                            [99.585537, 38.498556],\n                            [99.52887, 38.546314],\n                            [99.501769, 38.612281],\n                            [99.450646, 38.60433],\n                            [99.412458, 38.665571],\n                            [99.375502, 38.684727],\n                            [99.361951, 38.718354],\n                            [99.291118, 38.765966],\n                            [99.222133, 38.788827],\n                            [99.141445, 38.852706],\n                            [99.068764, 38.896968],\n                            [99.071843, 38.921184],\n                            [99.107568, 38.951907],\n                            [99.054597, 38.97657],\n                            [98.951735, 38.987735],\n                            [98.903076, 39.012384],\n                            [98.886446, 39.040744],\n                            [98.818076, 39.064911],\n                            [98.816845, 39.085818],\n                            [98.743548, 39.086747],\n                            [98.730613, 39.057011],\n                            [98.70536, 39.043533],\n                            [98.661628, 38.993782],\n                            [98.612353, 38.977035],\n                            [98.624056, 38.959353],\n                            [98.584635, 38.93003],\n                            [98.526737, 38.95563],\n                            [98.457752, 38.952838],\n                            [98.428187, 38.976104],\n                            [98.432498, 38.996107],\n                            [98.401086, 39.001688],\n                            [98.383839, 39.029588],\n                            [98.316702, 39.040744],\n                            [98.280977, 39.027263],\n                            [98.287753, 38.992386],\n                            [98.276666, 38.963541],\n                            [98.235398, 38.918855],\n                            [98.242173, 38.880664],\n                            [98.167645, 38.840121],\n                            [98.091884, 38.786495],\n                            [98.068478, 38.816344],\n                            [98.029058, 38.834061],\n                            [98.009348, 38.85923],\n                            [97.875689, 38.898365],\n                            [97.828878, 38.93003],\n                            [97.701379, 38.963076],\n                            [97.679205, 39.010524],\n                            [97.58127, 39.052364],\n                            [97.504894, 39.076527],\n                            [97.458698, 39.117863],\n                            [97.401416, 39.146645],\n                            [97.371235, 39.140611],\n                            [97.347213, 39.167528],\n                            [97.315185, 39.164744],\n                            [97.220946, 39.193042],\n                            [97.14149, 39.199999],\n                            [97.060186, 39.19768],\n                            [97.017686, 39.208347],\n                            [96.962251, 39.198144],\n                            [97.012142, 39.142004],\n                            [96.969643, 39.097895],\n                            [96.95794, 39.041674],\n                            [96.965331, 39.017034],\n                            [96.938846, 38.95563],\n                            [96.940693, 38.90768],\n                            [96.983809, 38.869016],\n                            [96.993664, 38.834993],\n                            [96.987505, 38.793025],\n                            [97.00044, 38.7613],\n                            [97.023229, 38.755699],\n                            [97.009063, 38.702477],\n                            [97.057722, 38.67258],\n                            [97.047251, 38.653888],\n                            [97.055874, 38.594508],\n                            [96.961019, 38.558015],\n                            [96.876636, 38.580475],\n                            [96.847071, 38.599186],\n                            [96.7941, 38.608072],\n                            [96.808882, 38.582346],\n                            [96.767614, 38.552399],\n                            [96.800259, 38.52759],\n                            [96.780549, 38.504177],\n                            [96.706637, 38.505582],\n                            [96.6666, 38.483567],\n                            [96.707868, 38.459203],\n                            [96.698013, 38.422172],\n                            [96.626564, 38.356031],\n                            [96.638883, 38.307208],\n                            [96.655514, 38.295936],\n                            [96.665369, 38.23015],\n                            [96.46334, 38.277616],\n                            [96.378341, 38.277146],\n                            [96.335841, 38.246132],\n                            [96.301964, 38.183124],\n                            [96.313051, 38.161952],\n                            [96.264392, 38.145952],\n                            [96.252689, 38.167599],\n                            [96.221892, 38.149246],\n                            [96.109175, 38.187358],\n                            [96.06606, 38.173245],\n                            [96.006929, 38.207582],\n                            [95.93856, 38.237202],\n                            [95.932401, 38.259291],\n                            [95.89606, 38.2903],\n                            [95.852945, 38.287481],\n                            [95.83693, 38.344298],\n                            [95.775952, 38.356031],\n                            [95.723597, 38.378554],\n                            [95.703887, 38.400131],\n                            [95.671858, 38.388405],\n                            [95.608417, 38.339134],\n                            [95.585011, 38.343359],\n                            [95.51849, 38.294997],\n                            [95.487693, 38.314721],\n                            [95.455664, 38.291709],\n                            [95.440881, 38.310965],\n                            [95.408236, 38.300163],\n                            [95.315846, 38.318947],\n                            [95.259179, 38.302981],\n                            [95.229614, 38.330685],\n                            [95.209904, 38.327868],\n                            [95.185266, 38.379492],\n                            [95.140919, 38.392158],\n                            [95.122441, 38.417014],\n                            [95.072549, 38.402476],\n                            [95.045448, 38.418889],\n                            [94.973999, 38.430142],\n                            [94.884072, 38.414669],\n                            [94.861282, 38.393565],\n                            [94.812623, 38.385591],\n                            [94.672805, 38.386998],\n                            [94.582878, 38.36917],\n                            [94.56132, 38.351807],\n                            [94.527443, 38.365416],\n                            [94.527443, 38.425922],\n                            [94.511429, 38.445142],\n                            [94.370379, 38.7627],\n                            [94.281067, 38.7599],\n                            [93.973098, 38.724891],\n                            [93.95154, 38.715086],\n                            [93.885018, 38.720689],\n                            [93.800019, 38.750566],\n                            [93.773533, 38.771099],\n                            [93.756287, 38.807484],\n                            [93.769838, 38.821007],\n                            [93.884403, 38.826136],\n                            [93.884403, 38.867618],\n                            [93.834511, 38.867618],\n                            [93.729186, 38.924443],\n                            [93.453245, 38.915596],\n                            [93.274007, 38.896036],\n                            [93.237666, 38.916062],\n                            [93.179152, 38.923977],\n                            [93.198246, 39.045857],\n                            [93.165601, 39.090928],\n                            [93.131725, 39.108112],\n                            [93.142196, 39.160567],\n                            [93.115094, 39.17959],\n                            [93.043029, 39.146645],\n                            [92.978356, 39.143396],\n                            [92.938936, 39.169848],\n                            [92.889045, 39.160103],\n                            [92.866871, 39.138754],\n                            [92.765857, 39.136898],\n                            [92.659299, 39.109969],\n                            [92.545966, 39.111362],\n                            [92.489916, 39.099753],\n                            [92.459119, 39.063982],\n                            [92.459119, 39.042604],\n                            [92.41046, 39.03842],\n                            [92.416003, 39.010524],\n                            [92.380279, 38.999828],\n                            [92.263866, 39.002153],\n                            [92.197961, 38.983548],\n                            [92.173323, 38.960749],\n                            [92.10865, 38.963541],\n                            [91.966368, 38.930961],\n                            [91.880752, 38.899297],\n                            [91.87952, 38.884391],\n                            [91.806223, 38.872744],\n                            [91.694738, 38.86622],\n                            [91.681188, 38.852706],\n                            [91.501333, 38.815411],\n                            [91.446515, 38.813546],\n                            [91.298689, 38.746365],\n                            [91.242639, 38.752433],\n                            [91.188436, 38.73096],\n                            [90.992567, 38.695003],\n                            [90.970394, 38.697806],\n                            [90.899561, 38.679588],\n                            [90.724634, 38.658094],\n                            [90.65996, 38.674449],\n                            [90.619308, 38.664636],\n                            [90.645794, 38.635191],\n                            [90.606374, 38.610878],\n                            [90.608837, 38.594508],\n                            [90.560794, 38.593573],\n                            [90.525685, 38.561291],\n                            [90.463476, 38.556611],\n                            [90.465323, 38.521971],\n                            [90.427135, 38.493873],\n                            [90.353222, 38.482162],\n                            [90.315034, 38.501835],\n                            [90.248513, 38.491531],\n                            [90.130868, 38.494341],\n                            [90.111774, 38.477945],\n                            [90.111774, 38.418889],\n                            [90.129636, 38.400131],\n                            [90.179528, 38.396848],\n                            [90.137644, 38.340543],\n                            [90.280542, 38.238142],\n                            [90.352607, 38.233441],\n                            [90.361846, 38.300163],\n                            [90.401882, 38.311434],\n                            [90.531229, 38.319886],\n                            [90.516446, 38.207111],\n                            [90.519526, 37.730601],\n                            [90.579272, 37.720661],\n                            [90.586663, 37.703144],\n                            [90.643946, 37.696988],\n                            [90.777605, 37.648672],\n                            [90.820104, 37.613599],\n                            [90.854597, 37.604117],\n                            [90.882314, 37.575664],\n                            [90.865684, 37.53059],\n                            [90.911879, 37.519674],\n                            [90.958075, 37.477891],\n                            [91.019669, 37.493088],\n                            [91.073256, 37.475992],\n                            [91.099741, 37.447965],\n                            [91.113292, 37.387124],\n                            [91.136081, 37.355734],\n                            [91.134849, 37.324331],\n                            [91.194596, 37.273868],\n                            [91.1909, 37.205737],\n                            [91.280211, 37.163779],\n                            [91.286371, 37.105095],\n                            [91.303617, 37.083136],\n                            [91.291298, 37.042544],\n                            [91.303617, 37.012444],\n                            [91.216153, 37.010054],\n                            [91.181045, 37.025345],\n                            [91.133618, 37.007665],\n                            [91.126842, 36.978507],\n                            [91.051698, 36.96751],\n                            [91.036915, 36.929727],\n                            [90.983944, 36.913459],\n                            [90.924198, 36.921115],\n                            [90.853981, 36.915373],\n                            [90.758511, 36.825844],\n                            [90.732025, 36.825844],\n                            [90.727098, 36.755872],\n                            [90.754815, 36.721341],\n                            [90.720938, 36.708868],\n                            [90.706156, 36.658955],\n                            [90.730793, 36.655594],\n                            [90.72217, 36.620058],\n                            [90.741264, 36.585947],\n                            [90.810865, 36.585466],\n                            [90.831191, 36.55807],\n                            [90.905104, 36.560474],\n                            [91.011662, 36.539801],\n                            [91.035683, 36.529703],\n                            [91.039995, 36.474861],\n                            [91.028292, 36.443093],\n                            [91.051698, 36.433946],\n                            [91.026444, 36.323607],\n                            [91.07264, 36.299012],\n                            [91.051698, 36.238215],\n                            [91.096045, 36.219871],\n                            [91.09235, 36.163844],\n                            [91.124994, 36.115514],\n                            [91.081263, 36.088436],\n                            [90.979017, 36.106811],\n                            [90.922966, 36.028927],\n                            [90.850285, 36.016827],\n                            [90.815793, 36.035703],\n                            [90.776373, 36.086501],\n                            [90.659344, 36.13485],\n                            [90.613149, 36.126632],\n                            [90.534925, 36.147899],\n                            [90.478258, 36.13195],\n                            [90.424055, 36.133883],\n                            [90.325505, 36.159496],\n                            [90.23681, 36.160462],\n                            [90.198006, 36.187516],\n                            [90.130252, 36.2078],\n                            [90.145651, 36.239181],\n                            [90.058188, 36.255591],\n                            [90.043405, 36.276822],\n                            [90.003369, 36.278752],\n                            [90.028006, 36.258486],\n                            [90.019999, 36.213594],\n                            [89.997825, 36.168193],\n                            [89.944855, 36.140649],\n                            [89.941159, 36.067637],\n                            [89.914058, 36.079246],\n                            [89.819819, 36.080697],\n                            [89.766848, 36.073925],\n                            [89.711414, 36.093272],\n                            [89.688624, 36.091337],\n                            [89.605472, 36.038123],\n                            [89.474893, 36.022151],\n                            [89.417611, 36.044897],\n                            [89.404676, 36.016827],\n                            [89.434857, 35.992136],\n                            [89.428082, 35.917531],\n                            [89.489676, 35.903475],\n                            [89.554965, 35.873414],\n                            [89.550654, 35.856924],\n                            [89.62395, 35.859349],\n                            [89.654747, 35.848193],\n                            [89.707718, 35.849163],\n                            [89.778551, 35.861775],\n                            [89.801957, 35.848193],\n                            [89.767464, 35.799183],\n                            [89.782863, 35.773453],\n                            [89.747138, 35.7516],\n                            [89.748986, 35.66267],\n                            [89.726196, 35.648082],\n                            [89.765616, 35.599922],\n                            [89.75145, 35.580942],\n                            [89.71203, 35.581915],\n                            [89.699711, 35.544916],\n                            [89.720037, 35.501566],\n                            [89.740979, 35.507412],\n                            [89.765, 35.482563],\n                            [89.739131, 35.468429],\n                            [89.685544, 35.416259],\n                            [89.658443, 35.425526],\n                            [89.619639, 35.412357],\n                            [89.58761, 35.383575],\n                            [89.497067, 35.361128],\n                            [89.516161, 35.330862],\n                            [89.494603, 35.298632],\n                            [89.531559, 35.276161],\n                            [89.48598, 35.256616],\n                            [89.450255, 35.223867],\n                            [89.46935, 35.214577],\n                            [89.519241, 35.133862],\n                            [89.579603, 35.118688],\n                            [89.593153, 35.104491],\n                            [89.59069, 35.057965],\n                            [89.560509, 34.938836],\n                            [89.578987, 34.895162],\n                            [89.670146, 34.887798],\n                            [89.707102, 34.919701],\n                            [89.747138, 34.903506],\n                            [89.78779, 34.921664],\n                            [89.821051, 34.902033],\n                            [89.814891, 34.86816],\n                            [89.838913, 34.865705],\n                            [89.867862, 34.81069],\n                            [89.825978, 34.796931],\n                            [89.799493, 34.743838],\n                            [89.732356, 34.732035],\n                            [89.72558, 34.660689],\n                            [89.74837, 34.641981],\n                            [89.798877, 34.628686],\n                            [89.777935, 34.574499],\n                            [89.814891, 34.548871],\n                            [89.823515, 34.455657],\n                            [89.819819, 34.420614],\n                            [89.799493, 34.39642],\n                            [89.820435, 34.369255],\n                            [89.858623, 34.359375],\n                            [89.86663, 34.324785],\n                            [89.825362, 34.293642],\n                            [89.838297, 34.263477],\n                            [89.816739, 34.16945],\n                            [89.789638, 34.150632],\n                            [89.760073, 34.152613],\n                            [89.756993, 34.124874],\n                            [89.71203, 34.131809],\n                            [89.655979, 34.097126],\n                            [89.656595, 34.057966],\n                            [89.635037, 34.049537],\n                            [89.684928, 33.990013],\n                            [89.688008, 33.959739],\n                            [89.718805, 33.946832],\n                            [89.73174, 33.921509],\n                            [89.795181, 33.865374],\n                            [89.837065, 33.868853],\n                            [89.899891, 33.80771],\n                            [89.942391, 33.801246],\n                            [89.902355, 33.758467],\n                            [89.907282, 33.741051],\n                            [89.983659, 33.725622],\n                            [89.981195, 33.70322],\n                            [90.008296, 33.687785],\n                            [89.984275, 33.612061],\n                            [90.01076, 33.553728],\n                            [90.083441, 33.525295],\n                            [90.088984, 33.478885],\n                            [90.107463, 33.460913],\n                            [90.22018, 33.437943],\n                            [90.246665, 33.423959],\n                            [90.332896, 33.310501],\n                            [90.363077, 33.279487],\n                            [90.405577, 33.260473],\n                            [90.490577, 33.264977],\n                            [90.562642, 33.229441],\n                            [90.627315, 33.180368],\n                            [90.704308, 33.135778],\n                            [90.740032, 33.142293],\n                            [90.803474, 33.114227],\n                            [90.88293, 33.120241],\n                            [90.902024, 33.083143],\n                            [90.927894, 33.120241],\n                            [91.001807, 33.11573],\n                            [91.037531, 33.098686],\n                            [91.072024, 33.113224],\n                            [91.147784, 33.07211],\n                            [91.161335, 33.108712],\n                            [91.18782, 33.106206],\n                            [91.226624, 33.141792],\n                            [91.261733, 33.141291],\n                            [91.311624, 33.108211],\n                            [91.370138, 33.100691],\n                            [91.436044, 33.066092],\n                            [91.49579, 33.109214],\n                            [91.535826, 33.10019],\n                            [91.55492, 33.060074],\n                            [91.583253, 33.0375],\n                            [91.664557, 33.012913],\n                            [91.685499, 32.989324],\n                            [91.752637, 32.969242],\n                            [91.799448, 32.942126],\n                            [91.839484, 32.948152],\n                            [91.857962, 32.90244],\n                            [91.896766, 32.907967],\n                            [91.955897, 32.8205],\n                            [92.018722, 32.829552],\n                            [92.038432, 32.860725],\n                            [92.101874, 32.860222],\n                            [92.145606, 32.885857],\n                            [92.205352, 32.866255],\n                            [92.227526, 32.821003],\n                            [92.193649, 32.801889],\n                            [92.211511, 32.788306],\n                            [92.198577, 32.754591],\n                            [92.255243, 32.720863],\n                            [92.310062, 32.751571],\n                            [92.343938, 32.738484],\n                            [92.355641, 32.764657],\n                            [92.411076, 32.748048],\n                            [92.459119, 32.76365],\n                            [92.484372, 32.745028],\n                            [92.56814, 32.73194],\n                            [92.574916, 32.741001],\n                            [92.634662, 32.720863],\n                            [92.667922, 32.73194],\n                            [92.686401, 32.76516],\n                            [92.756618, 32.743014],\n                            [92.789262, 32.719856],\n                            [92.822523, 32.729926],\n                            [92.866871, 32.698203],\n                            [92.933392, 32.719353],\n                            [92.964189, 32.714821],\n                            [93.00053, 32.741001],\n                            [93.019624, 32.737477],\n                            [93.023935, 32.703239],\n                            [93.069515, 32.626156],\n                            [93.087993, 32.63674],\n                            [93.159442, 32.644803],\n                            [93.176688, 32.6705],\n                            [93.210565, 32.655385],\n                            [93.239514, 32.662439],\n                            [93.260456, 32.62666],\n                            [93.300492, 32.619604],\n                            [93.308499, 32.580278],\n                            [93.33868, 32.5712],\n                            [93.385492, 32.525294],\n                            [93.411977, 32.558086],\n                            [93.4631, 32.556069],\n                            [93.476651, 32.504603],\n                            [93.501904, 32.503593],\n                            [93.516687, 32.47583],\n                            [93.618933, 32.522771],\n                            [93.651577, 32.571705],\n                            [93.721795, 32.578261],\n                            [93.75136, 32.56313],\n                            [93.820345, 32.549511],\n                            [93.851142, 32.50965],\n                            [93.861613, 32.466237],\n                            [93.90904, 32.463207],\n                            [93.960163, 32.484917],\n                            [93.978641, 32.459672],\n                            [94.03038, 32.448057],\n                            [94.049474, 32.469771],\n                            [94.091974, 32.463207],\n                            [94.137554, 32.433915],\n                            [94.176974, 32.454117],\n                            [94.196684, 32.51621],\n                            [94.250886, 32.51722],\n                            [94.292154, 32.502584],\n                            [94.294002, 32.519743],\n                            [94.350053, 32.533871],\n                            [94.371611, 32.524789],\n                            [94.395016, 32.594397],\n                            [94.435052, 32.562626],\n                            [94.463386, 32.572209],\n                            [94.459074, 32.599439],\n                            [94.522516, 32.595909],\n                            [94.591501, 32.640772],\n                            [94.614291, 32.673522],\n                            [94.638312, 32.645307],\n                            [94.737479, 32.587338],\n                            [94.762116, 32.526303],\n                            [94.78737, 32.522266],\n                            [94.80708, 32.486431],\n                            [94.852043, 32.463712],\n                            [94.889616, 32.472295],\n                            [94.912405, 32.41573],\n                            [94.944434, 32.404109],\n                            [94.988166, 32.422802],\n                            [95.057151, 32.395014],\n                            [95.075013, 32.376315],\n                            [95.075013, 32.376315],\n                            [95.081789, 32.384907],\n                            [95.153853, 32.386423],\n                            [95.218527, 32.397035],\n                            [95.228382, 32.363678],\n                            [95.261643, 32.348006],\n                            [95.193274, 32.332331],\n                            [95.096571, 32.322217],\n                            [95.079325, 32.279726],\n                            [95.10581, 32.258979],\n                            [95.20744, 32.297433],\n                            [95.214216, 32.321712],\n                            [95.241317, 32.3207],\n                            [95.239469, 32.287315],\n                            [95.270266, 32.194683],\n                            [95.270266, 32.194683],\n                            [95.31523, 32.148585],\n                            [95.366968, 32.151118],\n                            [95.367584, 32.178982],\n                            [95.406389, 32.182021],\n                            [95.440265, 32.157705],\n                            [95.454432, 32.061898],\n                            [95.421171, 32.033999],\n                            [95.454432, 32.007613],\n                            [95.395918, 32.001523],\n                            [95.360809, 31.95939],\n                            [95.3682, 31.92892],\n                            [95.408852, 31.918761],\n                            [95.406389, 31.896915],\n                            [95.456896, 31.801853],\n                            [95.480301, 31.795749],\n                            [95.511714, 31.750468],\n                            [95.546823, 31.73978],\n                            [95.580083, 31.76726],\n                            [95.634286, 31.782523],\n                            [95.779648, 31.748941],\n                            [95.823995, 31.68225],\n                            [95.853561, 31.714329],\n                            [95.846169, 31.736218],\n                            [95.89914, 31.81711],\n                            [95.983524, 31.816601],\n                            [95.989067, 31.78761],\n                            [96.064828, 31.720438],\n                            [96.135661, 31.70211],\n                            [96.148595, 31.686324],\n                            [96.156603, 31.602769],\n                            [96.207726, 31.598691],\n                            [96.221892, 31.647613],\n                            [96.245298, 31.657802],\n                            [96.252073, 31.697527],\n                            [96.222508, 31.733164],\n                            [96.231131, 31.749959],\n                            [96.178161, 31.775401],\n                            [96.183088, 31.835924],\n                            [96.202798, 31.841008],\n                            [96.214501, 31.876589],\n                            [96.188632, 31.904028],\n                            [96.220044, 31.905553],\n                            [96.253305, 31.929936],\n                            [96.288414, 31.919777],\n                            [96.389428, 31.919777],\n                            [96.407906, 31.845583],\n                            [96.435623, 31.796258],\n                            [96.468884, 31.769804],\n                            [96.519391, 31.74945],\n                            [96.56805, 31.711783],\n                            [96.615477, 31.737236],\n                            [96.661057, 31.705674],\n                            [96.691854, 31.722474],\n                            [96.722651, 31.686833],\n                            [96.778701, 31.675629],\n                            [96.790404, 31.698545],\n                            [96.840295, 31.720438],\n                            [96.799027, 31.792188],\n                            [96.765767, 31.819144],\n                            [96.760223, 31.860325],\n                            [96.794716, 31.869474],\n                            [96.81073, 31.894375],\n                            [96.776238, 31.935015],\n                            [96.753448, 31.944156],\n                            [96.742977, 32.001016],\n                            [96.722651, 32.013195],\n                            [96.824281, 32.007613],\n                            [96.868629, 31.964975],\n                            [96.863085, 31.996448],\n                            [96.894498, 32.013703],\n                            [96.941925, 31.986297],\n                            [96.965947, 32.008628],\n                            [96.935766, 32.048203],\n                            [97.006599, 32.067984],\n                            [97.028773, 32.04871],\n                            [97.127323, 32.044145],\n                            [97.169823, 32.032984],\n                            [97.188301, 32.055304],\n                            [97.214786, 32.042623],\n                            [97.233881, 32.063927],\n                            [97.201852, 32.090296],\n                            [97.219714, 32.109054],\n                            [97.258518, 32.072041],\n                            [97.308409, 32.076605],\n                            [97.293011, 32.096887],\n                            [97.313953, 32.130342],\n                            [97.271453, 32.139971],\n                            [97.264062, 32.182527],\n                            [97.299786, 32.294904],\n                            [97.32196, 32.303503],\n                            [97.371235, 32.273148],\n                            [97.415583, 32.296421],\n                            [97.424822, 32.322723],\n                            [97.387865, 32.427349],\n                            [97.341054, 32.440987],\n                            [97.388481, 32.501575],\n                            [97.334895, 32.514192],\n                            [97.332431, 32.542448],\n                            [97.3583, 32.563635],\n                            [97.374315, 32.546484],\n                            [97.411887, 32.575235],\n                            [97.448843, 32.586833],\n                            [97.463626, 32.55506],\n                            [97.50243, 32.530844],\n                            [97.540618, 32.536899],\n                            [97.670582, 32.51722],\n                            [97.684132, 32.530339],\n                            [97.730944, 32.527312],\n                            [97.700763, 32.53488],\n                            [97.616995, 32.586329],\n                            [97.607756, 32.614059],\n                            [97.543698, 32.62162],\n                            [97.535075, 32.638252],\n                            [97.48272, 32.654377],\n                            [97.42359, 32.70475],\n                            [97.429133, 32.714318],\n                            [97.386018, 32.77925],\n                            [97.392793, 32.828546],\n                            [97.376163, 32.886359],\n                            [97.347829, 32.895907],\n                            [97.375547, 32.956689],\n                            [97.438372, 32.976271],\n                            [97.523988, 32.988822],\n                            [97.499966, 33.011408],\n                            [97.542466, 33.035995],\n                            [97.517213, 33.097683],\n                            [97.487032, 33.107209],\n                            [97.498119, 33.137783],\n                            [97.487648, 33.168346],\n                            [97.548626, 33.203907],\n                            [97.607756, 33.263976],\n                            [97.622538, 33.337005],\n                            [97.676125, 33.341004],\n                            [97.754349, 33.409972],\n                            [97.674893, 33.432949],\n                            [97.625618, 33.461412],\n                            [97.552321, 33.465906],\n                            [97.511669, 33.520805],\n                            [97.523372, 33.577166],\n                            [97.450075, 33.582152],\n                            [97.415583, 33.605582],\n                            [97.435293, 33.682307],\n                            [97.418046, 33.728608],\n                            [97.422974, 33.754984],\n                            [97.406344, 33.795278],\n                            [97.373083, 33.817655],\n                            [97.371851, 33.842015],\n                            [97.398336, 33.848477],\n                            [97.395257, 33.889224],\n                            [97.460546, 33.887236],\n                            [97.503662, 33.912073],\n                            [97.52214, 33.903133],\n                            [97.601596, 33.929951],\n                            [97.629314, 33.919523],\n                            [97.660111, 33.956264],\n                            [97.652719, 33.998448],\n                            [97.70261, 34.036644],\n                            [97.665654, 34.126855],\n                            [97.766668, 34.158555],\n                            [97.789458, 34.182818],\n                            [97.789458, 34.182818],\n                            [97.796849, 34.199154],\n                            [97.796849, 34.199154],\n                            [97.8104, 34.207568],\n                            [97.898479, 34.209548],\n                            [97.95453, 34.190739],\n                            [98.028442, 34.122892],\n                            [98.098043, 34.122892],\n                            [98.158405, 34.107037],\n                            [98.206449, 34.08424],\n                            [98.258188, 34.083249],\n                            [98.344419, 34.094648],\n                            [98.399854, 34.085231],\n                            [98.396774, 34.053008],\n                            [98.428187, 34.029204],\n                            [98.440506, 33.981577],\n                            [98.415252, 33.956761],\n                            [98.425723, 33.913066],\n                            [98.407245, 33.867362],\n                            [98.434962, 33.843009],\n                            [98.463295, 33.848477],\n                            [98.492861, 33.796272],\n                            [98.494092, 33.768915],\n                            [98.51873, 33.77389],\n                            [98.539672, 33.746525],\n                            [98.582788, 33.731595],\n                            [98.610505, 33.682805],\n                            [98.6567, 33.64744],\n                            [98.61728, 33.637476],\n                            [98.622824, 33.610067],\n                            [98.652389, 33.595114],\n                            [98.648077, 33.548741],\n                            [98.678258, 33.522801],\n                            [98.725686, 33.503341],\n                            [98.742316, 33.477887],\n                            [98.736157, 33.406975],\n                            [98.779888, 33.370497],\n                            [98.759562, 33.276985],\n                            [98.802062, 33.270481],\n                            [98.804526, 33.219428],\n                            [98.858728, 33.150811],\n                            [98.92217, 33.118738],\n                            [98.967134, 33.115229],\n                            [98.971445, 33.098185],\n                            [99.014561, 33.081137],\n                            [99.024416, 33.094675],\n                            [99.090322, 33.079131],\n                            [99.124814, 33.046028],\n                            [99.196263, 33.035493],\n                            [99.214741, 32.991332],\n                            [99.235067, 32.982296],\n                            [99.24677, 32.924043],\n                            [99.268944, 32.878318],\n                            [99.353944, 32.885354],\n                            [99.376118, 32.899927],\n                            [99.45311, 32.862233],\n                            [99.558436, 32.839106],\n                            [99.589233, 32.789312],\n                            [99.640355, 32.790822],\n                            [99.646515, 32.774721],\n                            [99.700718, 32.76667],\n                            [99.717964, 32.732443],\n                            [99.760464, 32.769689],\n                            [99.766623, 32.826032],\n                            [99.791877, 32.883344],\n                            [99.764159, 32.924545],\n                            [99.788181, 32.956689],\n                            [99.805427, 32.940619],\n                            [99.851007, 32.941623],\n                            [99.877492, 32.993339],\n                            [99.877492, 33.045527],\n                            [99.947709, 32.986814],\n                            [99.956332, 32.948152],\n                            [100.038252, 32.929066],\n                            [100.029629, 32.895907],\n                            [100.064738, 32.895907],\n                            [100.123252, 32.837095],\n                            [100.117093, 32.802392],\n                            [100.139266, 32.724388],\n                            [100.088143, 32.668988],\n                            [100.109701, 32.640268],\n                            [100.189773, 32.630692],\n                            [100.208252, 32.606497],\n                            [100.229809, 32.650346],\n                            [100.231041, 32.696189],\n                            [100.258759, 32.742511],\n                            [100.339447, 32.719353],\n                            [100.399193, 32.756101],\n                            [100.378251, 32.698707],\n                            [100.420135, 32.73194],\n                            [100.450932, 32.694678],\n                            [100.470026, 32.694678],\n                            [100.516837, 32.632204],\n                            [100.54517, 32.569687],\n                            [100.603069, 32.553547],\n                            [100.645568, 32.526303],\n                            [100.657887, 32.546484],\n                            [100.661583, 32.616075],\n                            [100.673286, 32.628172],\n                            [100.710242, 32.610026],\n                            [100.71209, 32.645307],\n                            [100.690532, 32.678056],\n                            [100.77122, 32.643795],\n                            [100.834046, 32.648835],\n                            [100.887633, 32.632708],\n                            [100.93198, 32.600447],\n                            [100.956618, 32.621116],\n                            [100.99727, 32.627668],\n                            [101.030531, 32.660424],\n                            [101.077342, 32.68259],\n                            [101.124769, 32.658408],\n                            [101.157414, 32.661431],\n                            [101.22332, 32.725898],\n                            [101.237486, 32.825026],\n                            [101.223935, 32.855698],\n                            [101.178356, 32.892892],\n                            [101.124153, 32.909976],\n                            [101.134624, 32.95217],\n                            [101.129081, 32.989324],\n                            [101.183899, 32.984304],\n                            [101.171581, 33.009902],\n                            [101.184515, 33.041514],\n                            [101.146327, 33.056563],\n                            [101.143863, 33.086151],\n                            [101.169733, 33.10019],\n                            [101.11553, 33.194893],\n                            [101.124769, 33.221431],\n                            [101.156798, 33.236449],\n                            [101.182668, 33.26948],\n                            [101.217776, 33.256469],\n                            [101.297232, 33.262475],\n                            [101.381616, 33.153316],\n                            [101.393935, 33.157826],\n                            [101.386543, 33.207412],\n                            [101.403174, 33.225436],\n                            [101.487557, 33.226938],\n                            [101.515275, 33.192889],\n                            [101.557775, 33.167344],\n                            [101.633535, 33.101193],\n                            [101.661252, 33.135778],\n                            [101.653861, 33.162835],\n                            [101.709912, 33.21292],\n                            [101.735781, 33.279987],\n                            [101.677883, 33.297497],\n                            [101.64955, 33.323004],\n                            [101.663716, 33.383991],\n                            [101.695745, 33.433948],\n                            [101.769042, 33.45592],\n                            [101.777665, 33.533776],\n                            [101.769042, 33.538765],\n                            [101.748716, 33.505337],\n                            [101.718535, 33.494857],\n                            [101.622448, 33.502343],\n                            [101.611977, 33.565199],\n                            [101.616905, 33.598603],\n                            [101.585492, 33.645448],\n                            [101.58426, 33.674339],\n                            [101.501724, 33.702723],\n                            [101.428427, 33.680315],\n                            [101.424732, 33.655411],\n                            [101.385312, 33.644949],\n                            [101.302776, 33.657902],\n                            [101.23687, 33.685793],\n                            [101.217776, 33.669856],\n                            [101.166653, 33.659894],\n                            [101.177124, 33.685295],\n                            [101.162957, 33.719649],\n                            [101.186363, 33.741051],\n                            [101.190675, 33.791796],\n                            [101.153102, 33.823124],\n                            [101.153718, 33.8445],\n                            [101.054552, 33.863386],\n                            [101.023139, 33.896178],\n                            [100.994806, 33.891707],\n                            [100.965857, 33.946832],\n                            [100.927669, 33.975126],\n                            [100.93506, 33.990013],\n                            [100.880857, 34.036644],\n                            [100.870386, 34.083744],\n                            [100.848828, 34.089692],\n                            [100.806329, 34.155584],\n                            [100.764445, 34.178857],\n                            [100.809408, 34.247153],\n                            [100.798321, 34.260014],\n                            [100.821727, 34.317371],\n                            [100.868538, 34.332693],\n                            [100.895024, 34.375183],\n                            [100.951074, 34.38358],\n                            [100.986799, 34.374689],\n                            [101.054552, 34.322808],\n                            [101.098284, 34.329233],\n                            [101.178356, 34.320831],\n                            [101.193754, 34.336646],\n                            [101.235022, 34.325279],\n                            [101.228863, 34.298586],\n                            [101.268899, 34.278808],\n                            [101.325565, 34.268423],\n                            [101.327413, 34.24468],\n                            [101.369913, 34.248143],\n                            [101.417956, 34.227858],\n                            [101.482014, 34.218951],\n                            [101.492485, 34.195689],\n                            [101.53868, 34.212022],\n                            [101.6206, 34.178857],\n                            [101.674187, 34.110506],\n                            [101.703136, 34.119424],\n                            [101.718535, 34.083249],\n                            [101.736397, 34.080275],\n                            [101.764114, 34.122892],\n                            [101.788136, 34.131809],\n                            [101.836795, 34.124378],\n                            [101.851578, 34.153108],\n                            [101.874367, 34.130323],\n                            [101.897773, 34.133791],\n                            [101.955055, 34.109514],\n                            [101.965526, 34.167469],\n                            [102.003099, 34.162022],\n                            [102.030816, 34.190739],\n                            [102.01357, 34.218456],\n                            [102.062229, 34.227858],\n                            [102.067772, 34.293642],\n                            [102.149692, 34.271885],\n                            [102.186649, 34.352952],\n                            [102.237156, 34.34307],\n                            [102.237156, 34.34307],\n                            [102.259329, 34.355917],\n                            [102.205743, 34.407777],\n                            [102.169402, 34.457631],\n                            [102.155852, 34.507456],\n                            [102.139837, 34.50351],\n                            [102.093026, 34.536547],\n                            [102.001867, 34.538519],\n                            [101.97415, 34.548871],\n                            [101.956287, 34.582876],\n                            [101.934729, 34.58731],\n                            [101.919947, 34.621791],\n                            [101.917483, 34.705964],\n                            [101.923027, 34.835746],\n                            [101.916867, 34.873561],\n                            [101.985852, 34.90007],\n                            [102.068388, 34.887798],\n                            [102.048062, 34.910868],\n                            [102.094874, 34.986901],\n                            [102.133678, 35.014844],\n                            [102.157699, 35.010923],\n                            [102.176178, 35.032977],\n                            [102.211286, 35.034937],\n                            [102.218062, 35.057475],\n                            [102.252554, 35.048657],\n                            [102.29567, 35.071681],\n                            [102.310452, 35.128967],\n                            [102.346793, 35.164201],\n                            [102.404075, 35.179366],\n                            [102.365887, 35.235599],\n                            [102.370199, 35.263946],\n                            [102.3123, 35.282512],\n                            [102.280887, 35.303028],\n                            [102.311684, 35.31426],\n                            [102.317844, 35.343067],\n                            [102.287663, 35.36552],\n                            [102.293822, 35.424063],\n                            [102.314764, 35.434303],\n                            [102.408387, 35.409431],\n                            [102.447807, 35.437229],\n                            [102.437952, 35.455268],\n                            [102.49893, 35.545403],\n                            [102.503241, 35.585322],\n                            [102.531575, 35.580455],\n                            [102.570995, 35.548324],\n                            [102.695414, 35.528358],\n                            [102.743458, 35.494745],\n                            [102.782878, 35.527871],\n                            [102.729291, 35.523487],\n                            [102.746537, 35.545403],\n                            [102.808747, 35.560496],\n                            [102.763168, 35.612086],\n                            [102.7644, 35.653431],\n                            [102.744074, 35.657807],\n                            [102.707733, 35.70496],\n                            [102.686175, 35.771996],\n                            [102.715125, 35.815685],\n                            [102.739146, 35.821023],\n                            [102.787189, 35.862745],\n                            [102.81737, 35.850133],\n                            [102.914073, 35.845282],\n                            [102.94487, 35.829757],\n                            [102.954725, 35.858864],\n                            [102.942406, 35.92674],\n                            [102.971971, 35.995525],\n                            [102.951645, 36.021667],\n                            [102.968276, 36.044414],\n                            [102.932551, 36.048285],\n                            [102.882044, 36.082632],\n                            [102.941174, 36.104877],\n                            [102.948566, 36.150798],\n                            [102.965812, 36.151765],\n                            [102.986754, 36.193312],\n                            [103.048964, 36.199107],\n                            [103.066826, 36.216974],\n                            [103.021246, 36.232906],\n                            [103.024942, 36.256556],\n                            [102.922696, 36.298047],\n                            [102.896827, 36.331803],\n                            [102.845704, 36.331803],\n                            [102.836465, 36.344819],\n                            [102.829689, 36.365544]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 640000,\n                \"name\": \"Ningxia\",\n                \"center\": [106.278179, 38.46637],\n                \"centroid\": [106.169866, 37.291332],\n                \"childrenNum\": 5,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 29,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [107.268764, 37.099367],\n                            [107.281083, 37.127047],\n                            [107.306952, 37.100799],\n                            [107.334669, 37.138975],\n                            [107.336517, 37.165687],\n                            [107.317423, 37.200017],\n                            [107.270612, 37.229089],\n                            [107.309416, 37.239095],\n                            [107.273075, 37.29101],\n                            [107.257677, 37.337179],\n                            [107.282931, 37.437036],\n                            [107.284162, 37.481691],\n                            [107.345756, 37.518725],\n                            [107.369162, 37.58752],\n                            [107.330358, 37.584201],\n                            [107.311264, 37.609806],\n                            [107.361155, 37.613125],\n                            [107.422133, 37.665254],\n                            [107.389488, 37.671413],\n                            [107.387024, 37.691305],\n                            [107.425828, 37.684201],\n                            [107.484959, 37.706458],\n                            [107.499125, 37.765619],\n                            [107.57119, 37.776499],\n                            [107.599523, 37.791162],\n                            [107.620465, 37.776026],\n                            [107.646335, 37.805349],\n                            [107.659269, 37.844112],\n                            [107.65003, 37.86443],\n                            [107.560719, 37.893717],\n                            [107.49235, 37.944706],\n                            [107.448618, 37.933378],\n                            [107.411662, 37.948009],\n                            [107.440611, 37.995659],\n                            [107.3938, 38.014993],\n                            [107.33159, 38.086625],\n                            [107.240431, 38.111586],\n                            [107.19054, 38.153953],\n                            [107.138801, 38.161011],\n                            [107.119091, 38.134185],\n                            [107.071047, 38.138892],\n                            [107.051337, 38.122886],\n                            [107.010069, 38.120532],\n                            [106.942316, 38.132302],\n                            [106.858548, 38.156306],\n                            [106.779092, 38.171833],\n                            [106.737824, 38.197706],\n                            [106.654672, 38.22921],\n                            [106.627571, 38.232501],\n                            [106.555506, 38.263521],\n                            [106.482209, 38.319417],\n                            [106.599854, 38.389812],\n                            [106.647897, 38.470917],\n                            [106.66268, 38.601524],\n                            [106.709491, 38.718821],\n                            [106.756302, 38.748699],\n                            [106.837606, 38.847579],\n                            [106.954019, 38.941202],\n                            [106.971881, 39.026333],\n                            [106.96757, 39.054688],\n                            [106.933693, 39.076527],\n                            [106.878874, 39.091392],\n                            [106.859164, 39.107648],\n                            [106.825288, 39.19397],\n                            [106.795723, 39.214375],\n                            [106.790795, 39.241263],\n                            [106.806193, 39.277407],\n                            [106.806809, 39.318625],\n                            [106.781556, 39.371849],\n                            [106.751375, 39.381564],\n                            [106.683622, 39.357506],\n                            [106.643586, 39.357969],\n                            [106.602318, 39.37555],\n                            [106.556122, 39.322329],\n                            [106.525325, 39.308439],\n                            [106.511774, 39.272311],\n                            [106.402753, 39.291767],\n                            [106.280181, 39.262118],\n                            [106.29558, 39.167992],\n                            [106.285109, 39.146181],\n                            [106.251232, 39.131327],\n                            [106.192718, 39.142932],\n                            [106.170544, 39.163352],\n                            [106.145907, 39.153142],\n                            [106.096631, 39.084889],\n                            [106.078153, 39.026333],\n                            [106.087392, 39.006339],\n                            [106.060907, 38.96866],\n                            [106.021487, 38.953769],\n                            [105.97098, 38.909077],\n                            [105.992538, 38.857366],\n                            [105.909386, 38.791159],\n                            [105.908154, 38.737496],\n                            [105.88598, 38.716953],\n                            [105.894603, 38.696405],\n                            [105.852719, 38.641735],\n                            [105.874277, 38.593105],\n                            [105.856415, 38.569714],\n                            [105.863806, 38.53508],\n                            [105.836705, 38.476071],\n                            [105.850872, 38.443736],\n                            [105.827466, 38.432486],\n                            [105.835473, 38.387467],\n                            [105.821307, 38.366824],\n                            [105.86627, 38.296406],\n                            [105.842248, 38.240962],\n                            [105.802828, 38.220277],\n                            [105.775111, 38.186887],\n                            [105.76772, 38.121474],\n                            [105.780655, 38.084741],\n                            [105.840401, 38.004147],\n                            [105.799749, 37.939986],\n                            [105.80406, 37.862068],\n                            [105.760944, 37.799674],\n                            [105.677177, 37.771769],\n                            [105.622358, 37.777919],\n                            [105.616199, 37.722555],\n                            [105.598952, 37.699356],\n                            [105.467141, 37.695094],\n                            [105.4037, 37.710246],\n                            [105.315004, 37.702197],\n                            [105.221998, 37.677097],\n                            [105.187505, 37.657674],\n                            [105.111128, 37.633981],\n                            [105.027977, 37.580881],\n                            [104.866601, 37.566651],\n                            [104.805007, 37.539133],\n                            [104.623305, 37.522522],\n                            [104.433595, 37.515402],\n                            [104.419429, 37.511604],\n                            [104.407726, 37.464592],\n                            [104.322726, 37.44844],\n                            [104.287002, 37.428007],\n                            [104.298705, 37.414223],\n                            [104.365226, 37.418026],\n                            [104.437907, 37.445589],\n                            [104.448994, 37.42468],\n                            [104.499501, 37.421353],\n                            [104.521059, 37.43466],\n                            [104.679971, 37.408044],\n                            [104.662109, 37.367626],\n                            [104.713848, 37.329566],\n                            [104.673812, 37.317668],\n                            [104.651022, 37.290534],\n                            [104.624536, 37.298627],\n                            [104.600515, 37.242907],\n                            [104.638087, 37.201923],\n                            [104.717543, 37.208597],\n                            [104.776673, 37.246718],\n                            [104.85613, 37.211933],\n                            [104.864753, 37.17284],\n                            [104.888158, 37.15901],\n                            [104.914644, 37.097935],\n                            [104.954064, 37.077407],\n                            [104.95468, 37.040156],\n                            [105.004571, 37.035378],\n                            [105.03968, 37.007187],\n                            [105.05939, 37.022956],\n                            [105.128991, 36.996194],\n                            [105.165331, 36.99476],\n                            [105.185657, 36.942164],\n                            [105.178882, 36.892403],\n                            [105.244787, 36.894796],\n                            [105.279896, 36.86751],\n                            [105.303302, 36.820575],\n                            [105.334714, 36.80093],\n                            [105.340874, 36.764502],\n                            [105.319932, 36.742924],\n                            [105.275584, 36.752515],\n                            [105.272505, 36.739567],\n                            [105.218302, 36.730455],\n                            [105.201056, 36.700711],\n                            [105.225693, 36.664716],\n                            [105.22015, 36.631105],\n                            [105.261418, 36.602764],\n                            [105.2762, 36.563358],\n                            [105.252179, 36.553263],\n                            [105.281744, 36.522489],\n                            [105.322396, 36.535954],\n                            [105.362432, 36.496514],\n                            [105.363048, 36.443093],\n                            [105.398156, 36.430575],\n                            [105.401236, 36.369881],\n                            [105.425873, 36.330357],\n                            [105.455439, 36.321678],\n                            [105.476381, 36.293224],\n                            [105.45975, 36.268137],\n                            [105.460366, 36.223733],\n                            [105.478844, 36.213111],\n                            [105.515185, 36.147415],\n                            [105.491163, 36.101009],\n                            [105.430801, 36.10391],\n                            [105.406163, 36.074409],\n                            [105.343954, 36.033767],\n                            [105.324859, 35.941761],\n                            [105.350113, 35.875839],\n                            [105.39754, 35.857409],\n                            [105.371055, 35.844312],\n                            [105.38091, 35.792873],\n                            [105.408627, 35.822479],\n                            [105.428953, 35.819082],\n                            [105.432033, 35.787533],\n                            [105.457286, 35.771511],\n                            [105.481924, 35.727312],\n                            [105.595873, 35.715651],\n                            [105.667322, 35.749657],\n                            [105.70243, 35.733142],\n                            [105.759097, 35.724883],\n                            [105.740618, 35.698643],\n                            [105.723988, 35.725854],\n                            [105.690727, 35.698643],\n                            [105.722756, 35.673366],\n                            [105.713517, 35.650513],\n                            [105.759097, 35.634464],\n                            [105.762176, 35.602841],\n                            [105.800365, 35.564878],\n                            [105.816379, 35.575101],\n                            [105.847176, 35.490359],\n                            [105.868734, 35.540046],\n                            [105.900147, 35.54735],\n                            [106.017175, 35.519103],\n                            [106.023335, 35.49377],\n                            [106.047356, 35.498155],\n                            [106.078769, 35.509848],\n                            [106.071994, 35.463555],\n                            [106.06953, 35.458193],\n                            [106.073842, 35.45478],\n                            [106.073226, 35.450393],\n                            [106.071378, 35.449418],\n                            [106.073226, 35.447468],\n                            [106.083081, 35.421624],\n                            [106.113262, 35.361616],\n                            [106.129892, 35.393333],\n                            [106.173008, 35.437716],\n                            [106.196414, 35.409919],\n                            [106.237681, 35.409431],\n                            [106.241377, 35.358687],\n                            [106.319601, 35.265411],\n                            [106.363333, 35.238532],\n                            [106.368261, 35.273718],\n                            [106.415688, 35.276161],\n                            [106.472354, 35.310842],\n                            [106.501304, 35.364056],\n                            [106.503767, 35.415284],\n                            [106.483441, 35.450393],\n                            [106.490217, 35.480613],\n                            [106.465579, 35.481101],\n                            [106.440941, 35.52641],\n                            [106.460036, 35.578995],\n                            [106.47913, 35.575101],\n                            [106.460036, 35.643705],\n                            [106.434782, 35.688436],\n                            [106.49268, 35.732656],\n                            [106.506231, 35.737514],\n                            [106.566593, 35.738971],\n                            [106.595542, 35.727312],\n                            [106.620796, 35.743829],\n                            [106.633115, 35.714679],\n                            [106.66268, 35.70739],\n                            [106.674998, 35.728284],\n                            [106.750759, 35.689408],\n                            [106.750759, 35.725369],\n                            [106.806193, 35.70982],\n                            [106.819128, 35.7448],\n                            [106.867171, 35.738485],\n                            [106.868403, 35.771996],\n                            [106.897353, 35.759856],\n                            [106.927534, 35.810346],\n                            [106.849925, 35.887476],\n                            [106.912751, 35.93207],\n                            [106.940468, 35.931101],\n                            [106.93862, 35.952905],\n                            [106.90228, 35.943699],\n                            [106.94786, 35.988262],\n                            [106.928149, 36.011502],\n                            [106.940468, 36.064734],\n                            [106.957715, 36.091337],\n                            [106.925686, 36.115997],\n                            [106.930613, 36.138716],\n                            [106.873947, 36.178338],\n                            [106.873947, 36.178338],\n                            [106.858548, 36.206834],\n                            [106.858548, 36.206834],\n                            [106.833295, 36.229044],\n                            [106.808657, 36.21118],\n                            [106.772933, 36.212628],\n                            [106.735976, 36.23725],\n                            [106.698404, 36.244008],\n                            [106.685469, 36.273445],\n                            [106.647897, 36.259451],\n                            [106.559202, 36.292259],\n                            [106.54134, 36.25366],\n                            [106.504383, 36.266207],\n                            [106.470507, 36.306246],\n                            [106.497608, 36.31348],\n                            [106.510543, 36.379037],\n                            [106.492064, 36.422389],\n                            [106.523477, 36.468605],\n                            [106.494528, 36.494589],\n                            [106.455724, 36.496995],\n                            [106.39721, 36.548455],\n                            [106.37134, 36.549417],\n                            [106.363949, 36.577296],\n                            [106.392282, 36.556628],\n                            [106.397826, 36.576816],\n                            [106.444637, 36.557109],\n                            [106.465579, 36.583063],\n                            [106.444637, 36.624861],\n                            [106.491448, 36.628703],\n                            [106.490833, 36.685835],\n                            [106.530869, 36.690154],\n                            [106.519782, 36.708868],\n                            [106.519782, 36.708868],\n                            [106.514238, 36.715584],\n                            [106.59431, 36.750118],\n                            [106.644817, 36.72278],\n                            [106.627571, 36.752995],\n                            [106.657752, 36.820575],\n                            [106.637426, 36.867031],\n                            [106.637426, 36.867031],\n                            [106.626955, 36.892403],\n                            [106.609709, 36.878521],\n                            [106.609709, 36.878521],\n                            [106.601702, 36.918244],\n                            [106.549347, 36.941685],\n                            [106.540108, 36.984244],\n                            [106.595542, 36.94025],\n                            [106.594926, 36.967988],\n                            [106.64297, 36.962729],\n                            [106.646665, 37.000496],\n                            [106.666991, 37.016745],\n                            [106.645433, 37.064992],\n                            [106.605397, 37.127524],\n                            [106.6171, 37.135158],\n                            [106.673151, 37.1113],\n                            [106.687933, 37.12991],\n                            [106.728585, 37.121321],\n                            [106.750143, 37.09889],\n                            [106.772933, 37.120367],\n                            [106.776012, 37.158056],\n                            [106.818512, 37.141838],\n                            [106.891193, 37.098413],\n                            [106.912135, 37.110345],\n                            [106.905976, 37.151378],\n                            [106.998367, 37.106527],\n                            [107.031011, 37.108436],\n                            [107.030395, 37.140883],\n                            [107.095685, 37.115595],\n                            [107.133873, 37.134681],\n                            [107.181916, 37.143269],\n                            [107.234887, 37.096503],\n                            [107.268764, 37.099367]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.048588, 35.488898],\n                            [105.897683, 35.451368],\n                            [105.894603, 35.413821],\n                            [106.002393, 35.438692],\n                            [106.034422, 35.469404],\n                            [106.054132, 35.45478],\n                            [106.048588, 35.488898]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.073842, 35.45478],\n                            [106.06953, 35.458193],\n                            [106.071378, 35.449418],\n                            [106.073226, 35.450393],\n                            [106.073842, 35.45478]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 650000,\n                \"name\": \"Xinjiang\",\n                \"center\": [87.617733, 43.792818],\n                \"centroid\": [85.294711, 41.371801],\n                \"childrenNum\": 24,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 30,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [96.386348, 42.727592],\n                            [96.363558, 42.900562],\n                            [95.921314, 43.229789],\n                            [95.880046, 43.28035],\n                            [95.857872, 43.417436],\n                            [95.735916, 43.597569],\n                            [95.705735, 43.67077],\n                            [95.645373, 43.787966],\n                            [95.623199, 43.855756],\n                            [95.527113, 44.007466],\n                            [95.426099, 44.009618],\n                            [95.377439, 44.025972],\n                            [95.326932, 44.028554],\n                            [95.35157, 44.090054],\n                            [95.355882, 44.166087],\n                            [95.376208, 44.227444],\n                            [95.4107, 44.245024],\n                            [95.43041, 44.281882],\n                            [95.41378, 44.298589],\n                            [95.238853, 44.277169],\n                            [95.1286, 44.269884],\n                            [94.998637, 44.253169],\n                            [94.945666, 44.292592],\n                            [94.826174, 44.320001],\n                            [94.768275, 44.34055],\n                            [94.722696, 44.34055],\n                            [94.673421, 44.397021],\n                            [94.606283, 44.448311],\n                            [94.557008, 44.462408],\n                            [94.470777, 44.509373],\n                            [94.390705, 44.521749],\n                            [94.359292, 44.515775],\n                            [94.329727, 44.582734],\n                            [94.279836, 44.603617],\n                            [94.227481, 44.645785],\n                            [94.215162, 44.667921],\n                            [94.152336, 44.684944],\n                            [94.066105, 44.732154],\n                            [93.723642, 44.865498],\n                            [93.716251, 44.894334],\n                            [93.613389, 44.926546],\n                            [93.509296, 44.968055],\n                            [93.434767, 44.955351],\n                            [93.376869, 44.985412],\n                            [93.314659, 44.995147],\n                            [93.314043, 44.980333],\n                            [93.252449, 44.991761],\n                            [93.174225, 45.015458],\n                            [93.100312, 45.007419],\n                            [93.062124, 45.018419],\n                            [93.002377, 45.009958],\n                            [92.932776, 45.017573],\n                            [92.922921, 45.03703],\n                            [92.884117, 45.046756],\n                            [92.847777, 45.038721],\n                            [92.779407, 45.050561],\n                            [92.683937, 45.02561],\n                            [92.547814, 45.018419],\n                            [92.501003, 45.001072],\n                            [92.414155, 45.018419],\n                            [92.348866, 45.014188],\n                            [92.315605, 45.028994],\n                            [92.240461, 45.015881],\n                            [92.100026, 45.081417],\n                            [92.056911, 45.086911],\n                            [91.885679, 45.078882],\n                            [91.803144, 45.082685],\n                            [91.694738, 45.065357],\n                            [91.561695, 45.075501],\n                            [91.500101, 45.103809],\n                            [91.448978, 45.156586],\n                            [91.429268, 45.156586],\n                            [91.37753, 45.11099],\n                            [91.33503, 45.129571],\n                            [91.242023, 45.13717],\n                            [91.230936, 45.153632],\n                            [91.195827, 45.159118],\n                            [91.17119, 45.199616],\n                            [91.129922, 45.21606],\n                            [91.050466, 45.208892],\n                            [91.007966, 45.218589],\n                            [90.96177, 45.201303],\n                            [90.881698, 45.192025],\n                            [90.866916, 45.209314],\n                            [90.897713, 45.249776],\n                            [90.877387, 45.280946],\n                            [90.831807, 45.300313],\n                            [90.804706, 45.29484],\n                            [90.813329, 45.32851],\n                            [90.773909, 45.405874],\n                            [90.772677, 45.432338],\n                            [90.723402, 45.464667],\n                            [90.671047, 45.487747],\n                            [90.676591, 45.582488],\n                            [90.714779, 45.728895],\n                            [90.799778, 45.834905],\n                            [90.890937, 45.921566],\n                            [91.028292, 46.023054],\n                            [91.014741, 46.06667],\n                            [91.021517, 46.121038],\n                            [90.98456, 46.160431],\n                            [90.94822, 46.219262],\n                            [90.955611, 46.233752],\n                            [90.900177, 46.31235],\n                            [90.983328, 46.374734],\n                            [90.996263, 46.419309],\n                            [91.025828, 46.444057],\n                            [91.038147, 46.500936],\n                            [91.060937, 46.516999],\n                            [91.079415, 46.558989],\n                            [91.068328, 46.579149],\n                            [91.017821, 46.58244],\n                            [91.036299, 46.670393],\n                            [91.054161, 46.717598],\n                            [91.019053, 46.766402],\n                            [90.992567, 46.769682],\n                            [90.992567, 46.790583],\n                            [90.942676, 46.82581],\n                            [90.958075, 46.879425],\n                            [90.929742, 46.893331],\n                            [90.92235, 46.938707],\n                            [90.901408, 46.960768],\n                            [90.830575, 46.995883],\n                            [90.767134, 46.992617],\n                            [90.691989, 47.080717],\n                            [90.653801, 47.111681],\n                            [90.579888, 47.198364],\n                            [90.56141, 47.206903],\n                            [90.521374, 47.2845],\n                            [90.488113, 47.317374],\n                            [90.526301, 47.379007],\n                            [90.507823, 47.400076],\n                            [90.468403, 47.404937],\n                            [90.459164, 47.43895],\n                            [90.474562, 47.462422],\n                            [90.468403, 47.497611],\n                            [90.398186, 47.547724],\n                            [90.376012, 47.603036],\n                            [90.346447, 47.637324],\n                            [90.384635, 47.644179],\n                            [90.331665, 47.681663],\n                            [90.216484, 47.70543],\n                            [90.180144, 47.72516],\n                            [90.13518, 47.723147],\n                            [90.07605, 47.777469],\n                            [90.070506, 47.820483],\n                            [90.086521, 47.86547],\n                            [90.066195, 47.883534],\n                            [90.040941, 47.874704],\n                            [89.960253, 47.885942],\n                            [89.957789, 47.842982],\n                            [89.86971, 47.834144],\n                            [89.761921, 47.835751],\n                            [89.735435, 47.89758],\n                            [89.651052, 47.913627],\n                            [89.645508, 47.947711],\n                            [89.595617, 47.973359],\n                            [89.599313, 48.015811],\n                            [89.569132, 48.037825],\n                            [89.498299, 48.02822],\n                            [89.38127, 48.046227],\n                            [89.359712, 48.026219],\n                            [89.308589, 48.021816],\n                            [89.282104, 47.994189],\n                            [89.231597, 47.98017],\n                            [89.156452, 47.996992],\n                            [89.078228, 47.98698],\n                            [89.044967, 48.009806],\n                            [89.027105, 48.051028],\n                            [88.953808, 48.090618],\n                            [88.939026, 48.115396],\n                            [88.824461, 48.107005],\n                            [88.79736, 48.133772],\n                            [88.721599, 48.160526],\n                            [88.700657, 48.180881],\n                            [88.668628, 48.171303],\n                            [88.638447, 48.183674],\n                            [88.601491, 48.221567],\n                            [88.594716, 48.259831],\n                            [88.575006, 48.277757],\n                            [88.605803, 48.337863],\n                            [88.573774, 48.351785],\n                            [88.573158, 48.369679],\n                            [88.535586, 48.368884],\n                            [88.523267, 48.403461],\n                            [88.503557, 48.412996],\n                            [88.462289, 48.392335],\n                            [88.438267, 48.393528],\n                            [88.360659, 48.433251],\n                            [88.363123, 48.460641],\n                            [88.318159, 48.478497],\n                            [88.229464, 48.498329],\n                            [88.196819, 48.493967],\n                            [88.151855, 48.526478],\n                            [88.130297, 48.521721],\n                            [88.10874, 48.545895],\n                            [88.041602, 48.548272],\n                            [87.973233, 48.575997],\n                            [87.96153, 48.599353],\n                            [88.010805, 48.618742],\n                            [88.02682, 48.65315],\n                            [88.089645, 48.69504],\n                            [88.090877, 48.71992],\n                            [88.064392, 48.712813],\n                            [88.029283, 48.750313],\n                            [87.96153, 48.773588],\n                            [87.93874, 48.757809],\n                            [87.872219, 48.799612],\n                            [87.826639, 48.800795],\n                            [87.803234, 48.824835],\n                            [87.829103, 48.825623],\n                            [87.792147, 48.849258],\n                            [87.78106, 48.872094],\n                            [87.742256, 48.881146],\n                            [87.760118, 48.925992],\n                            [87.793995, 48.927565],\n                            [87.814321, 48.945256],\n                            [87.87653, 48.949186],\n                            [87.871603, 48.963726],\n                            [87.911639, 48.979833],\n                            [87.883922, 48.993971],\n                            [87.883306, 49.023806],\n                            [87.835263, 49.054406],\n                            [87.858052, 49.07362],\n                            [87.844502, 49.090084],\n                            [87.867291, 49.108892],\n                            [87.845733, 49.146096],\n                            [87.82048, 49.148445],\n                            [87.821096, 49.173883],\n                            [87.793379, 49.18249],\n                            [87.762582, 49.172709],\n                            [87.700372, 49.175839],\n                            [87.67635, 49.15549],\n                            [87.602437, 49.152359],\n                            [87.563017, 49.142572],\n                            [87.517438, 49.145704],\n                            [87.49588, 49.132001],\n                            [87.511894, 49.10184],\n                            [87.43675, 49.075188],\n                            [87.388707, 49.097921],\n                            [87.304939, 49.112418],\n                            [87.239033, 49.114376],\n                            [87.211932, 49.140615],\n                            [87.112766, 49.15549],\n                            [87.088128, 49.133567],\n                            [87.000049, 49.142572],\n                            [86.953853, 49.131218],\n                            [86.887948, 49.132001],\n                            [86.854071, 49.109284],\n                            [86.84976, 49.066563],\n                            [86.836209, 49.051269],\n                            [86.772151, 49.02773],\n                            [86.732115, 48.994757],\n                            [86.730267, 48.959797],\n                            [86.757985, 48.894919],\n                            [86.782006, 48.887049],\n                            [86.821426, 48.850439],\n                            [86.818963, 48.831139],\n                            [86.770303, 48.810255],\n                            [86.754289, 48.78463],\n                            [86.780774, 48.731369],\n                            [86.771535, 48.717156],\n                            [86.70255, 48.666195],\n                            [86.693311, 48.64366],\n                            [86.640956, 48.629027],\n                            [86.635413, 48.612016],\n                            [86.594761, 48.576789],\n                            [86.579978, 48.538763],\n                            [86.416138, 48.481671],\n                            [86.38103, 48.49357],\n                            [86.305269, 48.491984],\n                            [86.270161, 48.452307],\n                            [86.225813, 48.432456],\n                            [86.053966, 48.441192],\n                            [85.916612, 48.438015],\n                            [85.791576, 48.418954],\n                            [85.758315, 48.403064],\n                            [85.695489, 48.335078],\n                            [85.695489, 48.302445],\n                            [85.678243, 48.266205],\n                            [85.633895, 48.232731],\n                            [85.622193, 48.202824],\n                            [85.587084, 48.191654],\n                            [85.576613, 48.15853],\n                            [85.55136, 48.127781],\n                            [85.551975, 48.081423],\n                            [85.531649, 48.046227],\n                            [85.547048, 48.008205],\n                            [85.617881, 47.550552],\n                            [85.614801, 47.498015],\n                            [85.685018, 47.428829],\n                            [85.701649, 47.384275],\n                            [85.675779, 47.321837],\n                            [85.701033, 47.28856],\n                            [85.682555, 47.249982],\n                            [85.682555, 47.222757],\n                            [85.641903, 47.18413],\n                            [85.582772, 47.142626],\n                            [85.547048, 47.096609],\n                            [85.545816, 47.057891],\n                            [85.441106, 47.063191],\n                            [85.355491, 47.054629],\n                            [85.325926, 47.044842],\n                            [85.276651, 47.068898],\n                            [85.213825, 47.041172],\n                            [85.175637, 46.997924],\n                            [85.102956, 46.968936],\n                            [85.082014, 46.939933],\n                            [84.987159, 46.918272],\n                            [84.979768, 46.883106],\n                            [84.95513, 46.861013],\n                            [84.934188, 46.863878],\n                            [84.867051, 46.927673],\n                            [84.849189, 46.957092],\n                            [84.781435, 46.979962],\n                            [84.748175, 47.009759],\n                            [84.699515, 47.008535],\n                            [84.668718, 46.995067],\n                            [84.563393, 46.991801],\n                            [84.506726, 46.97302],\n                            [84.425422, 47.008943],\n                            [84.37122, 46.993434],\n                            [84.336727, 47.00527],\n                            [84.2893, 46.994658],\n                            [84.195061, 47.003638],\n                            [84.150098, 46.977512],\n                            [84.086656, 46.965261],\n                            [84.038613, 46.973428],\n                            [84.002888, 46.990576],\n                            [83.951765, 46.98731],\n                            [83.932671, 46.970161],\n                            [83.88586, 46.982003],\n                            [83.766367, 47.026896],\n                            [83.69923, 47.015472],\n                            [83.700462, 47.032199],\n                            [83.576042, 47.059114],\n                            [83.566803, 47.080717],\n                            [83.53847, 47.083977],\n                            [83.463325, 47.132042],\n                            [83.418978, 47.119012],\n                            [83.370318, 47.178436],\n                            [83.324739, 47.167858],\n                            [83.306261, 47.179656],\n                            [83.257602, 47.173147],\n                            [83.221877, 47.186977],\n                            [83.207094, 47.213814],\n                            [83.17445, 47.218286],\n                            [83.15474, 47.236168],\n                            [83.108544, 47.221944],\n                            [83.02724, 47.21544],\n                            [83.031552, 47.168265],\n                            [82.993364, 47.065229],\n                            [82.937929, 47.014248],\n                            [82.923762, 46.932169],\n                            [82.876335, 46.823762],\n                            [82.878183, 46.797138],\n                            [82.829524, 46.772551],\n                            [82.788872, 46.677784],\n                            [82.774089, 46.600124],\n                            [82.726662, 46.494756],\n                            [82.609017, 46.294985],\n                            [82.518474, 46.153798],\n                            [82.461808, 45.97982],\n                            [82.401446, 45.972333],\n                            [82.342932, 45.935303],\n                            [82.336156, 45.882418],\n                            [82.349707, 45.822811],\n                            [82.340468, 45.772742],\n                            [82.289961, 45.71636],\n                            [82.288729, 45.655321],\n                            [82.266555, 45.620172],\n                            [82.281954, 45.53891],\n                            [82.448257, 45.461309],\n                            [82.546808, 45.426038],\n                            [82.60101, 45.346178],\n                            [82.58746, 45.224069],\n                            [82.562822, 45.204676],\n                            [82.487061, 45.181058],\n                            [82.344779, 45.219011],\n                            [82.294272, 45.247669],\n                            [82.206809, 45.236713],\n                            [82.109491, 45.211422],\n                            [82.091012, 45.222383],\n                            [82.09594, 45.249776],\n                            [82.052824, 45.255674],\n                            [81.993078, 45.237978],\n                            [81.921013, 45.233342],\n                            [81.879745, 45.284314],\n                            [81.832318, 45.319673],\n                            [81.78797, 45.3836],\n                            [81.677101, 45.35459],\n                            [81.645072, 45.359216],\n                            [81.582863, 45.336503],\n                            [81.575471, 45.30789],\n                            [81.536667, 45.304101],\n                            [81.52866, 45.285999],\n                            [81.462754, 45.264099],\n                            [81.437501, 45.28263],\n                            [81.398697, 45.275471],\n                            [81.382066, 45.257781],\n                            [81.327864, 45.260729],\n                            [81.284748, 45.23882],\n                            [81.236705, 45.247248],\n                            [81.175111, 45.227863],\n                            [81.170183, 45.211001],\n                            [81.111669, 45.218168],\n                            [81.080872, 45.182745],\n                            [81.024821, 45.162916],\n                            [80.966307, 45.168402],\n                            [80.93551, 45.160384],\n                            [80.897938, 45.127459],\n                            [80.862214, 45.127037],\n                            [80.816634, 45.152788],\n                            [80.731634, 45.156164],\n                            [80.686055, 45.129148],\n                            [80.599207, 45.105921],\n                            [80.519135, 45.108878],\n                            [80.493882, 45.127037],\n                            [80.445839, 45.097895],\n                            [80.443991, 45.077614],\n                            [80.404571, 45.049293],\n                            [80.358375, 45.040836],\n                            [80.328194, 45.070007],\n                            [80.291854, 45.06578],\n                            [80.24381, 45.031532],\n                            [80.195767, 45.030686],\n                            [80.144644, 45.059017],\n                            [80.136021, 45.041259],\n                            [80.111999, 45.052675],\n                            [80.060876, 45.026033],\n                            [80.056565, 45.011227],\n                            [79.98142, 44.964244],\n                            [79.951855, 44.957892],\n                            [79.944464, 44.937985],\n                            [79.887798, 44.90917],\n                            [79.969102, 44.877797],\n                            [79.953703, 44.849377],\n                            [79.991891, 44.830281],\n                            [79.999283, 44.793768],\n                            [80.087978, 44.817122],\n                            [80.115695, 44.815424],\n                            [80.169898, 44.84471],\n                            [80.18776, 44.825612],\n                            [80.178521, 44.796741],\n                            [80.200695, 44.756808],\n                            [80.238883, 44.7228],\n                            [80.313412, 44.704938],\n                            [80.400259, 44.628751],\n                            [80.411962, 44.605321],\n                            [80.350368, 44.484615],\n                            [80.383013, 44.401297],\n                            [80.399027, 44.30587],\n                            [80.413194, 44.264741],\n                            [80.400875, 44.198704],\n                            [80.407034, 44.149772],\n                            [80.3941, 44.127009],\n                            [80.449534, 44.078017],\n                            [80.458773, 44.047054],\n                            [80.457541, 43.981203],\n                            [80.485259, 43.95579],\n                            [80.475404, 43.938124],\n                            [80.511128, 43.906657],\n                            [80.522215, 43.816473],\n                            [80.75504, 43.494329],\n                            [80.761199, 43.446554],\n                            [80.746417, 43.439167],\n                            [80.735946, 43.389609],\n                            [80.686055, 43.333916],\n                            [80.69283, 43.32042],\n                            [80.777214, 43.308227],\n                            [80.769207, 43.265535],\n                            [80.788917, 43.242433],\n                            [80.789533, 43.201876],\n                            [80.804315, 43.178314],\n                            [80.79446, 43.137277],\n                            [80.752576, 43.148194],\n                            [80.73225, 43.131163],\n                            [80.706997, 43.143828],\n                            [80.650946, 43.147321],\n                            [80.593048, 43.133347],\n                            [80.556092, 43.104515],\n                            [80.482795, 43.06955],\n                            [80.416889, 43.05687],\n                            [80.378701, 43.031502],\n                            [80.397795, 42.996933],\n                            [80.487106, 42.948766],\n                            [80.5912, 42.923354],\n                            [80.602903, 42.894424],\n                            [80.503737, 42.882146],\n                            [80.450766, 42.861971],\n                            [80.407034, 42.834767],\n                            [80.338049, 42.831695],\n                            [80.280151, 42.838278],\n                            [80.262289, 42.828623],\n                            [80.259209, 42.790865],\n                            [80.225948, 42.713083],\n                            [80.228412, 42.692852],\n                            [80.179753, 42.670415],\n                            [80.163738, 42.629919],\n                            [80.180985, 42.590718],\n                            [80.221637, 42.533415],\n                            [80.265368, 42.502097],\n                            [80.225948, 42.485769],\n                            [80.206238, 42.431462],\n                            [80.239499, 42.389927],\n                            [80.229028, 42.358536],\n                            [80.283847, 42.320493],\n                            [80.272144, 42.281984],\n                            [80.29247, 42.259842],\n                            [80.28631, 42.233261],\n                            [80.233339, 42.210215],\n                            [80.168666, 42.200462],\n                            [80.163738, 42.152563],\n                            [80.139717, 42.151232],\n                            [80.16805, 42.096635],\n                            [80.193303, 42.081535],\n                            [80.14218, 42.03488],\n                            [80.089826, 42.047325],\n                            [79.923522, 42.042436],\n                            [79.852689, 42.015319],\n                            [79.854537, 41.984186],\n                            [79.822508, 41.963275],\n                            [79.776313, 41.89248],\n                            [79.724574, 41.896935],\n                            [79.640806, 41.884907],\n                            [79.616784, 41.856385],\n                            [79.550879, 41.834094],\n                            [79.500988, 41.835432],\n                            [79.457256, 41.847915],\n                            [79.415372, 41.836769],\n                            [79.356242, 41.795735],\n                            [79.326061, 41.809565],\n                            [79.276786, 41.78101],\n                            [79.271858, 41.767174],\n                            [79.21704, 41.725648],\n                            [79.138199, 41.722968],\n                            [79.10925, 41.697503],\n                            [79.043345, 41.681414],\n                            [79.021787, 41.657273],\n                            [78.99407, 41.664427],\n                            [78.957729, 41.65146],\n                            [78.891824, 41.597777],\n                            [78.86657, 41.593749],\n                            [78.825302, 41.560173],\n                            [78.739071, 41.555695],\n                            [78.696571, 41.54181],\n                            [78.707042, 41.522098],\n                            [78.675629, 41.50238],\n                            [78.650375, 41.467411],\n                            [78.580774, 41.481759],\n                            [78.527188, 41.440947],\n                            [78.454507, 41.412228],\n                            [78.391681, 41.408189],\n                            [78.385522, 41.394721],\n                            [78.338094, 41.397415],\n                            [78.324544, 41.384395],\n                            [78.235232, 41.399211],\n                            [78.163783, 41.383497],\n                            [78.149617, 41.368228],\n                            [78.165015, 41.340825],\n                            [78.136682, 41.279239],\n                            [78.129291, 41.228398],\n                            [78.094798, 41.224347],\n                            [77.972842, 41.173013],\n                            [77.905089, 41.185174],\n                            [77.836104, 41.153189],\n                            [77.814546, 41.13426],\n                            [77.807155, 41.091876],\n                            [77.829328, 41.059394],\n                            [77.796068, 41.049014],\n                            [77.780669, 41.022832],\n                            [77.737553, 41.032313],\n                            [77.684583, 41.00793],\n                            [77.654402, 41.016059],\n                            [77.597119, 41.005221],\n                            [77.591576, 40.992122],\n                            [77.540453, 41.006575],\n                            [77.476395, 40.999349],\n                            [77.473931, 41.022832],\n                            [77.415417, 41.038633],\n                            [77.363062, 41.04089],\n                            [77.296541, 41.004769],\n                            [77.236795, 41.027798],\n                            [77.169041, 41.009285],\n                            [77.108063, 41.038181],\n                            [77.091433, 41.062553],\n                            [77.023064, 41.059394],\n                            [77.002122, 41.073381],\n                            [76.940528, 41.028701],\n                            [76.885709, 41.027347],\n                            [76.85368, 40.97631],\n                            [76.817956, 40.975406],\n                            [76.761905, 40.954167],\n                            [76.741579, 40.912119],\n                            [76.731724, 40.818887],\n                            [76.693536, 40.779472],\n                            [76.646725, 40.759983],\n                            [76.646725, 40.73686],\n                            [76.676906, 40.696036],\n                            [76.654732, 40.652917],\n                            [76.657196, 40.620218],\n                            [76.611, 40.601591],\n                            [76.601145, 40.578868],\n                            [76.556798, 40.542495],\n                            [76.543247, 40.513837],\n                            [76.539551, 40.464226],\n                            [76.508754, 40.429613],\n                            [76.470566, 40.422779],\n                            [76.442233, 40.391336],\n                            [76.390494, 40.37766],\n                            [76.381871, 40.39088],\n                            [76.333212, 40.343459],\n                            [76.327668, 40.391336],\n                            [76.283321, 40.415034],\n                            [76.279625, 40.439179],\n                            [76.22419, 40.401819],\n                            [76.176147, 40.381307],\n                            [76.144118, 40.393615],\n                            [76.081293, 40.39635],\n                            [76.048648, 40.388601],\n                            [76.048648, 40.357141],\n                            [76.026474, 40.355317],\n                            [75.986438, 40.381763],\n                            [75.932235, 40.339353],\n                            [75.921764, 40.291439],\n                            [75.890351, 40.30924],\n                            [75.84046, 40.312434],\n                            [75.831221, 40.327492],\n                            [75.785642, 40.301025],\n                            [75.739446, 40.299199],\n                            [75.709265, 40.280939],\n                            [75.688323, 40.343915],\n                            [75.669845, 40.363982],\n                            [75.686475, 40.418223],\n                            [75.717272, 40.443278],\n                            [75.733287, 40.474242],\n                            [75.646439, 40.516567],\n                            [75.631041, 40.548862],\n                            [75.627345, 40.605226],\n                            [75.636584, 40.624306],\n                            [75.599628, 40.659727],\n                            [75.550353, 40.64883],\n                            [75.467817, 40.599773],\n                            [75.432093, 40.563412],\n                            [75.355716, 40.537947],\n                            [75.292274, 40.483802],\n                            [75.268869, 40.483802],\n                            [75.242383, 40.448743],\n                            [75.206659, 40.447833],\n                            [75.13521, 40.463315],\n                            [75.102565, 40.44009],\n                            [75.051442, 40.449654],\n                            [75.021877, 40.466958],\n                            [74.995392, 40.455119],\n                            [74.963363, 40.464681],\n                            [74.891914, 40.507467],\n                            [74.844486, 40.521117],\n                            [74.819233, 40.505647],\n                            [74.814921, 40.461039],\n                            [74.795211, 40.443278],\n                            [74.908544, 40.338897],\n                            [74.862965, 40.32658],\n                            [74.824776, 40.344371],\n                            [74.700357, 40.346195],\n                            [74.697893, 40.310153],\n                            [74.673255, 40.278656],\n                            [74.618437, 40.27957],\n                            [74.577169, 40.260391],\n                            [74.534669, 40.207851],\n                            [74.485394, 40.182251],\n                            [74.433039, 40.13148],\n                            [74.356662, 40.089371],\n                            [74.316626, 40.106767],\n                            [74.280902, 40.09807],\n                            [74.26304, 40.125074],\n                            [74.126301, 40.104479],\n                            [74.113366, 40.086624],\n                            [74.023439, 40.085251],\n                            [74.008041, 40.050901],\n                            [73.943367, 40.016076],\n                            [73.980324, 40.004617],\n                            [73.910722, 39.934443],\n                            [73.907027, 39.873843],\n                            [73.845433, 39.831115],\n                            [73.841737, 39.756163],\n                            [73.905795, 39.741899],\n                            [73.924273, 39.722108],\n                            [73.953838, 39.600018],\n                            [73.916266, 39.586644],\n                            [73.914418, 39.564041],\n                            [73.883621, 39.540969],\n                            [73.893476, 39.528046],\n                            [73.868223, 39.482794],\n                            [73.836194, 39.472169],\n                            [73.745651, 39.462005],\n                            [73.6471, 39.474479],\n                            [73.61076, 39.465702],\n                            [73.592898, 39.412087],\n                            [73.502355, 39.383877],\n                            [73.554094, 39.350102],\n                            [73.554709, 39.295935],\n                            [73.542391, 39.269531],\n                            [73.564564, 39.266288],\n                            [73.580579, 39.237555],\n                            [73.623079, 39.235237],\n                            [73.639709, 39.220402],\n                            [73.657571, 39.166136],\n                            [73.688368, 39.154999],\n                            [73.719781, 39.108112],\n                            [73.720397, 39.071881],\n                            [73.743187, 39.029588],\n                            [73.780143, 39.026798],\n                            [73.820179, 39.041674],\n                            [73.839889, 39.008199],\n                            [73.846665, 38.962145],\n                            [73.826339, 38.916993],\n                            [73.767824, 38.941202],\n                            [73.742571, 38.933754],\n                            [73.70931, 38.893241],\n                            [73.699455, 38.857832],\n                            [73.729636, 38.837324],\n                            [73.769056, 38.775765],\n                            [73.757353, 38.719755],\n                            [73.809092, 38.634256],\n                            [73.799237, 38.610878],\n                            [73.852208, 38.584217],\n                            [73.89902, 38.579071],\n                            [73.926121, 38.536016],\n                            [74.011736, 38.52478],\n                            [74.034526, 38.541634],\n                            [74.090577, 38.542102],\n                            [74.068403, 38.585621],\n                            [74.088113, 38.610878],\n                            [74.11275, 38.611345],\n                            [74.147859, 38.676785],\n                            [74.229779, 38.656224],\n                            [74.353583, 38.655757],\n                            [74.421952, 38.647812],\n                            [74.455829, 38.632853],\n                            [74.506336, 38.637528],\n                            [74.546988, 38.607604],\n                            [74.613509, 38.593105],\n                            [74.639995, 38.599653],\n                            [74.717603, 38.542102],\n                            [74.78474, 38.538357],\n                            [74.821697, 38.491062],\n                            [74.862965, 38.484035],\n                            [74.868508, 38.403883],\n                            [74.834015, 38.361193],\n                            [74.789668, 38.324581],\n                            [74.806914, 38.285602],\n                            [74.793363, 38.271039],\n                            [74.816769, 38.215576],\n                            [74.80445, 38.167128],\n                            [74.821697, 38.10311],\n                            [74.879595, 38.021122],\n                            [74.92579, 38.01735],\n                            [74.911008, 37.966884],\n                            [74.919015, 37.908357],\n                            [74.936877, 37.876241],\n                            [74.917167, 37.845057],\n                            [74.989848, 37.797783],\n                            [75.006478, 37.770823],\n                            [74.949196, 37.725395],\n                            [74.923327, 37.717347],\n                            [74.920863, 37.684675],\n                            [74.891914, 37.668097],\n                            [74.940573, 37.559061],\n                            [75.000935, 37.53059],\n                            [75.002167, 37.511604],\n                            [75.035428, 37.500685],\n                            [75.078543, 37.511129],\n                            [75.090862, 37.486915],\n                            [75.129666, 37.459367],\n                            [75.153072, 37.414223],\n                            [75.125971, 37.388075],\n                            [75.140137, 37.355258],\n                            [75.125971, 37.322427],\n                            [75.078543, 37.318144],\n                            [75.018181, 37.293867],\n                            [74.927022, 37.277678],\n                            [74.911008, 37.233378],\n                            [74.816153, 37.216699],\n                            [74.800139, 37.248147],\n                            [74.753943, 37.281011],\n                            [74.727458, 37.282916],\n                            [74.665864, 37.23576],\n                            [74.642458, 37.261485],\n                            [74.598727, 37.258151],\n                            [74.578401, 37.231472],\n                            [74.54514, 37.2491],\n                            [74.511263, 37.240048],\n                            [74.477387, 37.19954],\n                            [74.487858, 37.161871],\n                            [74.465068, 37.147085],\n                            [74.496481, 37.116072],\n                            [74.498944, 37.072155],\n                            [74.530357, 37.082182],\n                            [74.56793, 37.032512],\n                            [74.617205, 37.043499],\n                            [74.632603, 37.066425],\n                            [74.70898, 37.084569],\n                            [74.739161, 37.028212],\n                            [74.792747, 37.027257],\n                            [74.806914, 37.054485],\n                            [74.84695, 37.056873],\n                            [74.84387, 37.0134],\n                            [74.86974, 36.990458],\n                            [74.893762, 36.939772],\n                            [74.938725, 36.94312],\n                            [74.927638, 36.978029],\n                            [75.005862, 36.99476],\n                            [75.032348, 37.016745],\n                            [75.063145, 37.006231],\n                            [75.172166, 37.013877],\n                            [75.16847, 36.991892],\n                            [75.244847, 36.963207],\n                            [75.288579, 36.974682],\n                            [75.345861, 36.960816],\n                            [75.413614, 36.954599],\n                            [75.396368, 36.904367],\n                            [75.430245, 36.873255],\n                            [75.434556, 36.83303],\n                            [75.425933, 36.778883],\n                            [75.458578, 36.720861],\n                            [75.504773, 36.743404],\n                            [75.536802, 36.729975],\n                            [75.537418, 36.773131],\n                            [75.588541, 36.762584],\n                            [75.634121, 36.771693],\n                            [75.724048, 36.750597],\n                            [75.8072, 36.707908],\n                            [75.871257, 36.666636],\n                            [75.947018, 36.590752],\n                            [75.924228, 36.566242],\n                            [75.991981, 36.505654],\n                            [76.035097, 36.409386],\n                            [75.991365, 36.35205],\n                            [75.998757, 36.312034],\n                            [76.055423, 36.252695],\n                            [76.060967, 36.225182],\n                            [76.011691, 36.229044],\n                            [76.016619, 36.165294],\n                            [75.96796, 36.159013],\n                            [75.936547, 36.13485],\n                            [75.949482, 36.070056],\n                            [75.982742, 36.031347],\n                            [76.028322, 36.016827],\n                            [76.044336, 36.026991],\n                            [76.097307, 36.022635],\n                            [76.117017, 35.975186],\n                            [76.16506, 35.908807],\n                            [76.146582, 35.839946],\n                            [76.160133, 35.82442],\n                            [76.221727, 35.823449],\n                            [76.228502, 35.837035],\n                            [76.298719, 35.841401],\n                            [76.365857, 35.82442],\n                            [76.369552, 35.86323],\n                            [76.431762, 35.851589],\n                            [76.471798, 35.886021],\n                            [76.51553, 35.881173],\n                            [76.55803, 35.923347],\n                            [76.59745, 35.895718],\n                            [76.579587, 35.866625],\n                            [76.587595, 35.840431],\n                            [76.566037, 35.819082],\n                            [76.593754, 35.771996],\n                            [76.69292, 35.747714],\n                            [76.769297, 35.653917],\n                            [76.848753, 35.668018],\n                            [76.906651, 35.615005],\n                            [76.967013, 35.591649],\n                            [76.99781, 35.611113],\n                            [77.072339, 35.591162],\n                            [77.093281, 35.569746],\n                            [77.195527, 35.519103],\n                            [77.307628, 35.540533],\n                            [77.331649, 35.530793],\n                            [77.355055, 35.494257],\n                            [77.396939, 35.467942],\n                            [77.451758, 35.46063],\n                            [77.518895, 35.482075],\n                            [77.578025, 35.47574],\n                            [77.590344, 35.460143],\n                            [77.639619, 35.45478],\n                            [77.657481, 35.477689],\n                            [77.690742, 35.448443],\n                            [77.735706, 35.461605],\n                            [77.757879, 35.497181],\n                            [77.797299, 35.491334],\n                            [77.816394, 35.518616],\n                            [77.85643, 35.487436],\n                            [77.870596, 35.495232],\n                            [77.914944, 35.465017],\n                            [77.917408, 35.490847],\n                            [77.951284, 35.478664],\n                            [78.009799, 35.491821],\n                            [78.029509, 35.469404],\n                            [78.048603, 35.491334],\n                            [78.140378, 35.494745],\n                            [78.113892, 35.466967],\n                            [78.107117, 35.437229],\n                            [78.046755, 35.384063],\n                            [78.013494, 35.366008],\n                            [78.020885, 35.315237],\n                            [78.01719, 35.228267],\n                            [78.060306, 35.180344],\n                            [78.062769, 35.114772],\n                            [78.078784, 35.100084],\n                            [78.124979, 35.108407],\n                            [78.150849, 35.069721],\n                            [78.123131, 35.036897],\n                            [78.160704, 34.990823],\n                            [78.201972, 34.974642],\n                            [78.182262, 34.936874],\n                            [78.206283, 34.891726],\n                            [78.237696, 34.882398],\n                            [78.230921, 34.776288],\n                            [78.21429, 34.760556],\n                            [78.213059, 34.717771],\n                            [78.267261, 34.705472],\n                            [78.265413, 34.651335],\n                            [78.280812, 34.623269],\n                            [78.346101, 34.60406],\n                            [78.397224, 34.605538],\n                            [78.427405, 34.594207],\n                            [78.436029, 34.543942],\n                            [78.492695, 34.578441],\n                            [78.542586, 34.574499],\n                            [78.559832, 34.55725],\n                            [78.562912, 34.51288],\n                            [78.58139, 34.505483],\n                            [78.634977, 34.538026],\n                            [78.708274, 34.522249],\n                            [78.715049, 34.502031],\n                            [78.758781, 34.481807],\n                            [78.742766, 34.45467],\n                            [78.809288, 34.432955],\n                            [78.878273, 34.391481],\n                            [78.899831, 34.354929],\n                            [78.958961, 34.386049],\n                            [78.973128, 34.362833],\n                            [79.039649, 34.33467],\n                            [79.048888, 34.348506],\n                            [79.0107, 34.399877],\n                            [79.039033, 34.421601],\n                            [79.072294, 34.412714],\n                            [79.161605, 34.441345],\n                            [79.179467, 34.422588],\n                            [79.241677, 34.415183],\n                            [79.274322, 34.435916],\n                            [79.326677, 34.44332],\n                            [79.363017, 34.428018],\n                            [79.435082, 34.447761],\n                            [79.504683, 34.45467],\n                            [79.545335, 34.476381],\n                            [79.58106, 34.456151],\n                            [79.675914, 34.451216],\n                            [79.699936, 34.477861],\n                            [79.735661, 34.471447],\n                            [79.801566, 34.478847],\n                            [79.861312, 34.528166],\n                            [79.84345, 34.55725],\n                            [79.88595, 34.642965],\n                            [79.866856, 34.671517],\n                            [79.906892, 34.683821],\n                            [79.898268, 34.732035],\n                            [79.947544, 34.821008],\n                            [79.926602, 34.849499],\n                            [79.961094, 34.862759],\n                            [79.996819, 34.856375],\n                            [80.003594, 34.895162],\n                            [80.034391, 34.902033],\n                            [80.041782, 34.943252],\n                            [80.02392, 34.971209],\n                            [80.04363, 35.022196],\n                            [80.031311, 35.034447],\n                            [80.078123, 35.076578],\n                            [80.118159, 35.066293],\n                            [80.23026, 35.147565],\n                            [80.223484, 35.177409],\n                            [80.257977, 35.203331],\n                            [80.362687, 35.20871],\n                            [80.267832, 35.295701],\n                            [80.286926, 35.35283],\n                            [80.321419, 35.38699],\n                            [80.375006, 35.387966],\n                            [80.432904, 35.449418],\n                            [80.444607, 35.417235],\n                            [80.514824, 35.391869],\n                            [80.532686, 35.404553],\n                            [80.56841, 35.391381],\n                            [80.599823, 35.409431],\n                            [80.65649, 35.393821],\n                            [80.690982, 35.364544],\n                            [80.689135, 35.339162],\n                            [80.759968, 35.334768],\n                            [80.844351, 35.345508],\n                            [80.894242, 35.324027],\n                            [80.924423, 35.330862],\n                            [80.963844, 35.310842],\n                            [81.026053, 35.31133],\n                            [81.002648, 35.334768],\n                            [81.030981, 35.337209],\n                            [81.031597, 35.380648],\n                            [81.054387, 35.402602],\n                            [81.09935, 35.40748],\n                            [81.103662, 35.386015],\n                            [81.142466, 35.365032],\n                            [81.191741, 35.36552],\n                            [81.219458, 35.319144],\n                            [81.26627, 35.322562],\n                            [81.285364, 35.345508],\n                            [81.314313, 35.337209],\n                            [81.363588, 35.354783],\n                            [81.385762, 35.335256],\n                            [81.441196, 35.333303],\n                            [81.447972, 35.318167],\n                            [81.504638, 35.279092],\n                            [81.513261, 35.23511],\n                            [81.68634, 35.235599],\n                            [81.736847, 35.26248],\n                            [81.804601, 35.270786],\n                            [81.853876, 35.25857],\n                            [81.927789, 35.271275],\n                            [81.955506, 35.307423],\n                            [81.99123, 35.30547],\n                            [82.030034, 35.321585],\n                            [82.05344, 35.35039],\n                            [82.029419, 35.426013],\n                            [82.034346, 35.451855],\n                            [82.071302, 35.450393],\n                            [82.086701, 35.467454],\n                            [82.164925, 35.495719],\n                            [82.189563, 35.513258],\n                            [82.234526, 35.520565],\n                            [82.263475, 35.547837],\n                            [82.2992, 35.544916],\n                            [82.328149, 35.559523],\n                            [82.350323, 35.611113],\n                            [82.336156, 35.651486],\n                            [82.392823, 35.656349],\n                            [82.424852, 35.712736],\n                            [82.468583, 35.717595],\n                            [82.501844, 35.701073],\n                            [82.546192, 35.708362],\n                            [82.628727, 35.692324],\n                            [82.652133, 35.67288],\n                            [82.731589, 35.637868],\n                            [82.780249, 35.666073],\n                            [82.795031, 35.688436],\n                            [82.873871, 35.688922],\n                            [82.894813, 35.673852],\n                            [82.967494, 35.667532],\n                            [82.956407, 35.636409],\n                            [82.981661, 35.599922],\n                            [82.971806, 35.548324],\n                            [82.998907, 35.484512],\n                            [83.067892, 35.46258],\n                            [83.088834, 35.425526],\n                            [83.127022, 35.398699],\n                            [83.178145, 35.38943],\n                            [83.251442, 35.417722],\n                            [83.280391, 35.401138],\n                            [83.333978, 35.397236],\n                            [83.405427, 35.380648],\n                            [83.449159, 35.382111],\n                            [83.502745, 35.360639],\n                            [83.540318, 35.364056],\n                            [83.54155, 35.341603],\n                            [83.599448, 35.351366],\n                            [83.622238, 35.335256],\n                            [83.677672, 35.361128],\n                            [83.785462, 35.36308],\n                            [83.79778, 35.354783],\n                            [83.885244, 35.367472],\n                            [83.906186, 35.40309],\n                            [84.005968, 35.422599],\n                            [84.077417, 35.400163],\n                            [84.095895, 35.362592],\n                            [84.140859, 35.379184],\n                            [84.160569, 35.359663],\n                            [84.200605, 35.381135],\n                            [84.274517, 35.404065],\n                            [84.333032, 35.413821],\n                            [84.424191, 35.466479],\n                            [84.45314, 35.473303],\n                            [84.475929, 35.516181],\n                            [84.448828, 35.550272],\n                            [84.513502, 35.564391],\n                            [84.570168, 35.588242],\n                            [84.628067, 35.595055],\n                            [84.704443, 35.616951],\n                            [84.729081, 35.613546],\n                            [84.798066, 35.647595],\n                            [84.920022, 35.696213],\n                            [84.973608, 35.709334],\n                            [84.99455, 35.737028],\n                            [85.053065, 35.752086],\n                            [85.146071, 35.742371],\n                            [85.271107, 35.788989],\n                            [85.341324, 35.753543],\n                            [85.373969, 35.700101],\n                            [85.518715, 35.680658],\n                            [85.566142, 35.6403],\n                            [85.612953, 35.651486],\n                            [85.65299, 35.731199],\n                            [85.691178, 35.751114],\n                            [85.811286, 35.778794],\n                            [85.835308, 35.771996],\n                            [85.903677, 35.78462],\n                            [85.949256, 35.778794],\n                            [86.035488, 35.846738],\n                            [86.05335, 35.842857],\n                            [86.090306, 35.876809],\n                            [86.093386, 35.906868],\n                            [86.129111, 35.941761],\n                            [86.150668, 36.00424],\n                            [86.173458, 36.008113],\n                            [86.199944, 36.047801],\n                            [86.182081, 36.064734],\n                            [86.187625, 36.130983],\n                            [86.248603, 36.141616],\n                            [86.2794, 36.170608],\n                            [86.35824, 36.168676],\n                            [86.392733, 36.206834],\n                            [86.454943, 36.221319],\n                            [86.515305, 36.205385],\n                            [86.531935, 36.227113],\n                            [86.599072, 36.222285],\n                            [86.69947, 36.24449],\n                            [86.746282, 36.291777],\n                            [86.836209, 36.291294],\n                            [86.86331, 36.299977],\n                            [86.887332, 36.262829],\n                            [86.931064, 36.265242],\n                            [86.943998, 36.284058],\n                            [86.996353, 36.308658],\n                            [87.051788, 36.2966],\n                            [87.08628, 36.310587],\n                            [87.149106, 36.297565],\n                            [87.161425, 36.325535],\n                            [87.193454, 36.349158],\n                            [87.292004, 36.358797],\n                            [87.348055, 36.393008],\n                            [87.363453, 36.420463],\n                            [87.386859, 36.412757],\n                            [87.426895, 36.42576],\n                            [87.460155, 36.409868],\n                            [87.470626, 36.354459],\n                            [87.570409, 36.342409],\n                            [87.6203, 36.360243],\n                            [87.731785, 36.384818],\n                            [87.767509, 36.3747],\n                            [87.826023, 36.391563],\n                            [87.838342, 36.383855],\n                            [87.919646, 36.39349],\n                            [87.95845, 36.408423],\n                            [87.983088, 36.437797],\n                            [88.006494, 36.430575],\n                            [88.092109, 36.43539],\n                            [88.134609, 36.427205],\n                            [88.182652, 36.452721],\n                            [88.222688, 36.447426],\n                            [88.241782, 36.468605],\n                            [88.282434, 36.470049],\n                            [88.366202, 36.458016],\n                            [88.356963, 36.477268],\n                            [88.41055, 36.473418],\n                            [88.470912, 36.48208],\n                            [88.498629, 36.446463],\n                            [88.573158, 36.461386],\n                            [88.618121, 36.428168],\n                            [88.623665, 36.389636],\n                            [88.690186, 36.367954],\n                            [88.766563, 36.292259],\n                            [88.783809, 36.291777],\n                            [88.802903, 36.33807],\n                            [88.838628, 36.353496],\n                            [88.870657, 36.348193],\n                            [88.926091, 36.36458],\n                            [88.964279, 36.318785],\n                            [89.013554, 36.315409],\n                            [89.054822, 36.291777],\n                            [89.10225, 36.281164],\n                            [89.126887, 36.254626],\n                            [89.198952, 36.260417],\n                            [89.232213, 36.295636],\n                            [89.292575, 36.231457],\n                            [89.335075, 36.23725],\n                            [89.375727, 36.228078],\n                            [89.490291, 36.151281],\n                            [89.594385, 36.126632],\n                            [89.614711, 36.109712],\n                            [89.711414, 36.093272],\n                            [89.766848, 36.073925],\n                            [89.819819, 36.080697],\n                            [89.914058, 36.079246],\n                            [89.941159, 36.067637],\n                            [89.944855, 36.140649],\n                            [89.997825, 36.168193],\n                            [90.019999, 36.213594],\n                            [90.028006, 36.258486],\n                            [90.003369, 36.278752],\n                            [90.043405, 36.276822],\n                            [90.058188, 36.255591],\n                            [90.145651, 36.239181],\n                            [90.130252, 36.2078],\n                            [90.198006, 36.187516],\n                            [90.23681, 36.160462],\n                            [90.325505, 36.159496],\n                            [90.424055, 36.133883],\n                            [90.478258, 36.13195],\n                            [90.534925, 36.147899],\n                            [90.613149, 36.126632],\n                            [90.659344, 36.13485],\n                            [90.776373, 36.086501],\n                            [90.815793, 36.035703],\n                            [90.850285, 36.016827],\n                            [90.922966, 36.028927],\n                            [90.979017, 36.106811],\n                            [91.081263, 36.088436],\n                            [91.124994, 36.115514],\n                            [91.09235, 36.163844],\n                            [91.096045, 36.219871],\n                            [91.051698, 36.238215],\n                            [91.07264, 36.299012],\n                            [91.026444, 36.323607],\n                            [91.051698, 36.433946],\n                            [91.028292, 36.443093],\n                            [91.039995, 36.474861],\n                            [91.035683, 36.529703],\n                            [91.011662, 36.539801],\n                            [90.905104, 36.560474],\n                            [90.831191, 36.55807],\n                            [90.810865, 36.585466],\n                            [90.741264, 36.585947],\n                            [90.72217, 36.620058],\n                            [90.730793, 36.655594],\n                            [90.706156, 36.658955],\n                            [90.720938, 36.708868],\n                            [90.754815, 36.721341],\n                            [90.727098, 36.755872],\n                            [90.732025, 36.825844],\n                            [90.758511, 36.825844],\n                            [90.853981, 36.915373],\n                            [90.924198, 36.921115],\n                            [90.983944, 36.913459],\n                            [91.036915, 36.929727],\n                            [91.051698, 36.96751],\n                            [91.126842, 36.978507],\n                            [91.133618, 37.007665],\n                            [91.181045, 37.025345],\n                            [91.216153, 37.010054],\n                            [91.303617, 37.012444],\n                            [91.291298, 37.042544],\n                            [91.303617, 37.083136],\n                            [91.286371, 37.105095],\n                            [91.280211, 37.163779],\n                            [91.1909, 37.205737],\n                            [91.194596, 37.273868],\n                            [91.134849, 37.324331],\n                            [91.136081, 37.355734],\n                            [91.113292, 37.387124],\n                            [91.099741, 37.447965],\n                            [91.073256, 37.475992],\n                            [91.019669, 37.493088],\n                            [90.958075, 37.477891],\n                            [90.911879, 37.519674],\n                            [90.865684, 37.53059],\n                            [90.882314, 37.575664],\n                            [90.854597, 37.604117],\n                            [90.820104, 37.613599],\n                            [90.777605, 37.648672],\n                            [90.643946, 37.696988],\n                            [90.586663, 37.703144],\n                            [90.579272, 37.720661],\n                            [90.519526, 37.730601],\n                            [90.516446, 38.207111],\n                            [90.531229, 38.319886],\n                            [90.401882, 38.311434],\n                            [90.361846, 38.300163],\n                            [90.352607, 38.233441],\n                            [90.280542, 38.238142],\n                            [90.137644, 38.340543],\n                            [90.179528, 38.396848],\n                            [90.129636, 38.400131],\n                            [90.111774, 38.418889],\n                            [90.111774, 38.477945],\n                            [90.130868, 38.494341],\n                            [90.248513, 38.491531],\n                            [90.315034, 38.501835],\n                            [90.353222, 38.482162],\n                            [90.427135, 38.493873],\n                            [90.465323, 38.521971],\n                            [90.463476, 38.556611],\n                            [90.525685, 38.561291],\n                            [90.560794, 38.593573],\n                            [90.608837, 38.594508],\n                            [90.606374, 38.610878],\n                            [90.645794, 38.635191],\n                            [90.619308, 38.664636],\n                            [90.65996, 38.674449],\n                            [90.724634, 38.658094],\n                            [90.899561, 38.679588],\n                            [90.970394, 38.697806],\n                            [90.992567, 38.695003],\n                            [91.188436, 38.73096],\n                            [91.242639, 38.752433],\n                            [91.298689, 38.746365],\n                            [91.446515, 38.813546],\n                            [91.501333, 38.815411],\n                            [91.681188, 38.852706],\n                            [91.694738, 38.86622],\n                            [91.806223, 38.872744],\n                            [91.87952, 38.884391],\n                            [91.880752, 38.899297],\n                            [91.966368, 38.930961],\n                            [92.10865, 38.963541],\n                            [92.173323, 38.960749],\n                            [92.197961, 38.983548],\n                            [92.263866, 39.002153],\n                            [92.380279, 38.999828],\n                            [92.416003, 39.010524],\n                            [92.41046, 39.03842],\n                            [92.366728, 39.059335],\n                            [92.366112, 39.096037],\n                            [92.343938, 39.146181],\n                            [92.339011, 39.236628],\n                            [92.378431, 39.258411],\n                            [92.52564, 39.368611],\n                            [92.639589, 39.514196],\n                            [92.687632, 39.657174],\n                            [92.745531, 39.868331],\n                            [92.796654, 40.153897],\n                            [92.906907, 40.310609],\n                            [92.920458, 40.391792],\n                            [92.928465, 40.572504],\n                            [93.506216, 40.648376],\n                            [93.760599, 40.664721],\n                            [93.820961, 40.793519],\n                            [93.809874, 40.879548],\n                            [93.908424, 40.983539],\n                            [94.01067, 41.114875],\n                            [94.184365, 41.268444],\n                            [94.534219, 41.505966],\n                            [94.750413, 41.538227],\n                            [94.809543, 41.619256],\n                            [94.861898, 41.668451],\n                            [94.969072, 41.718948],\n                            [95.011572, 41.726541],\n                            [95.110738, 41.768513],\n                            [95.135991, 41.772976],\n                            [95.16494, 41.735474],\n                            [95.199433, 41.719395],\n                            [95.194505, 41.694821],\n                            [95.247476, 41.61344],\n                            [95.299831, 41.565994],\n                            [95.335556, 41.644305],\n                            [95.39407, 41.693481],\n                            [95.445193, 41.719841],\n                            [95.57146, 41.796181],\n                            [95.65646, 41.826067],\n                            [95.759322, 41.835878],\n                            [95.801206, 41.848361],\n                            [95.855408, 41.849699],\n                            [95.998306, 41.906289],\n                            [96.054973, 41.936124],\n                            [96.117183, 41.985966],\n                            [96.137509, 42.019765],\n                            [96.13874, 42.05399],\n                            [96.077147, 42.149457],\n                            [96.178161, 42.21775],\n                            [96.040806, 42.326688],\n                            [96.042038, 42.352787],\n                            [96.06606, 42.414674],\n                            [95.978596, 42.436762],\n                            [96.0174, 42.482239],\n                            [96.02356, 42.542675],\n                            [96.072219, 42.569566],\n                            [96.103632, 42.604375],\n                            [96.166458, 42.623314],\n                            [96.386348, 42.727592]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 710000,\n                \"name\": \"Taiwan\",\n                \"center\": [121.509062, 25.044332],\n                \"centroid\": [120.971485, 23.749452],\n                \"childrenNum\": 0,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 31,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [120.443706, 22.441432],\n                            [120.517619, 22.408793],\n                            [120.569973, 22.361757],\n                            [120.640806, 22.241605],\n                            [120.659285, 22.154056],\n                            [120.661748, 22.067007],\n                            [120.651277, 22.033171],\n                            [120.667908, 21.983235],\n                            [120.701784, 21.927174],\n                            [120.743052, 21.915515],\n                            [120.781857, 21.923843],\n                            [120.854537, 21.883309],\n                            [120.873016, 21.897191],\n                            [120.86624, 21.984345],\n                            [120.907508, 22.033171],\n                            [120.912436, 22.086418],\n                            [120.903197, 22.12634],\n                            [120.914899, 22.302525],\n                            [120.981421, 22.528248],\n                            [121.014682, 22.584069],\n                            [121.03316, 22.650914],\n                            [121.078739, 22.669691],\n                            [121.170514, 22.723247],\n                            [121.21055, 22.770711],\n                            [121.237652, 22.836362],\n                            [121.276456, 22.877171],\n                            [121.324499, 22.945526],\n                            [121.35468, 23.00999],\n                            [121.370695, 23.084334],\n                            [121.409499, 23.1025],\n                            [121.430441, 23.137175],\n                            [121.415042, 23.196047],\n                            [121.440296, 23.271937],\n                            [121.479716, 23.322507],\n                            [121.497578, 23.419744],\n                            [121.5216, 23.483431],\n                            [121.522832, 23.538858],\n                            [121.587505, 23.760878],\n                            [121.621382, 23.920718],\n                            [121.65957, 24.007125],\n                            [121.63986, 24.064514],\n                            [121.643556, 24.097843],\n                            [121.678048, 24.133895],\n                            [121.689135, 24.174303],\n                            [121.809243, 24.339083],\n                            [121.82649, 24.423572],\n                            [121.867758, 24.47914],\n                            [121.88562, 24.529784],\n                            [121.892395, 24.617953],\n                            [121.86283, 24.671261],\n                            [121.841272, 24.734329],\n                            [121.844968, 24.836476],\n                            [121.933047, 24.938539],\n                            [122.012503, 25.001471],\n                            [121.98109, 25.030757],\n                            [121.947214, 25.031841],\n                            [121.917033, 25.138076],\n                            [121.841888, 25.135367],\n                            [121.782142, 25.160287],\n                            [121.745186, 25.161912],\n                            [121.707613, 25.191701],\n                            [121.700222, 25.226896],\n                            [121.655259, 25.242054],\n                            [121.62323, 25.29455],\n                            [121.585041, 25.309159],\n                            [121.53515, 25.307535],\n                            [121.444607, 25.27074],\n                            [121.413194, 25.238806],\n                            [121.371926, 25.159746],\n                            [121.319572, 25.140785],\n                            [121.209318, 25.12724],\n                            [121.132942, 25.078466],\n                            [121.102145, 25.075214],\n                            [121.024537, 25.040517],\n                            [121.009754, 24.993878],\n                            [120.961095, 24.940167],\n                            [120.914899, 24.864715],\n                            [120.89211, 24.767482],\n                            [120.82374, 24.688118],\n                            [120.762147, 24.658208],\n                            [120.68885, 24.600542],\n                            [120.642654, 24.490033],\n                            [120.589068, 24.43229],\n                            [120.546568, 24.370159],\n                            [120.520698, 24.311816],\n                            [120.470807, 24.242533],\n                            [120.451713, 24.182493],\n                            [120.391967, 24.118055],\n                            [120.316206, 23.984708],\n                            [120.278018, 23.92783],\n                            [120.245989, 23.840276],\n                            [120.175156, 23.807427],\n                            [120.102476, 23.701162],\n                            [120.095084, 23.58768],\n                            [120.12157, 23.504836],\n                            [120.108019, 23.341191],\n                            [120.081534, 23.291728],\n                            [120.018708, 23.073322],\n                            [120.029795, 23.048544],\n                            [120.133272, 23.000625],\n                            [120.149287, 22.896468],\n                            [120.20041, 22.721039],\n                            [120.274323, 22.560307],\n                            [120.297112, 22.531565],\n                            [120.443706, 22.441432]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.542782, 25.903886],\n                            [124.584666, 25.908731],\n                            [124.566804, 25.941563],\n                            [124.542782, 25.903886]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.445178, 25.726102],\n                            [123.469816, 25.712623],\n                            [123.50862, 25.722867],\n                            [123.512316, 25.755212],\n                            [123.479055, 25.768687],\n                            [123.445794, 25.749822],\n                            [123.445178, 25.726102]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.646064, 23.550928],\n                            [119.691028, 23.547087],\n                            [119.678093, 23.600294],\n                            [119.61034, 23.604132],\n                            [119.601717, 23.575613],\n                            [119.566608, 23.584937],\n                            [119.562297, 23.530627],\n                            [119.578927, 23.502641],\n                            [119.609108, 23.503738],\n                            [119.646064, 23.550928]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.666916, 25.914114],\n                            [123.706952, 25.91519],\n                            [123.689706, 25.939949],\n                            [123.666916, 25.914114]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.506246, 23.625518],\n                            [119.506246, 23.577259],\n                            [119.47237, 23.556962],\n                            [119.519181, 23.559705],\n                            [119.52534, 23.62497],\n                            [119.506246, 23.625518]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.497623, 23.38679],\n                            [119.495159, 23.349982],\n                            [119.516717, 23.349982],\n                            [119.497623, 23.38679]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.557369, 23.666634],\n                            [119.608492, 23.620035],\n                            [119.615268, 23.661153],\n                            [119.586318, 23.675952],\n                            [119.557369, 23.666634]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.066706, 25.6247],\n                            [122.087032, 25.61067],\n                            [122.092575, 25.639268],\n                            [122.066706, 25.6247]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.468013, 22.67687],\n                            [121.474788, 22.643734],\n                            [121.513592, 22.631582],\n                            [121.514824, 22.676318],\n                            [121.468013, 22.67687]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.510513, 22.086972],\n                            [121.507433, 22.048704],\n                            [121.533918, 22.022076],\n                            [121.594281, 21.995443],\n                            [121.604752, 22.022631],\n                            [121.575186, 22.037055],\n                            [121.575802, 22.0842],\n                            [121.510513, 22.086972]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.097503, 25.499987],\n                            [122.110438, 25.465952],\n                            [122.122141, 25.495666],\n                            [122.097503, 25.499987]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.421247, 23.216949],\n                            [119.436029, 23.186146],\n                            [119.453275, 23.216399],\n                            [119.421247, 23.216949]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.355011, 22.327439],\n                            [120.395663, 22.342385],\n                            [120.383344, 22.355669],\n                            [120.355011, 22.327439]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 810000,\n                \"name\": \"HongKong\",\n                \"center\": [114.173355, 22.320048],\n                \"centroid\": [114.134357, 22.377366],\n                \"childrenNum\": 18,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 32,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [114.031778, 22.503923],\n                            [114.000981, 22.491206],\n                            [113.977575, 22.45692],\n                            [113.918445, 22.418199],\n                            [113.920293, 22.367845],\n                            [113.951706, 22.355116],\n                            [113.956633, 22.359543],\n                            [113.980039, 22.366185],\n                            [114.026234, 22.34792],\n                            [113.955401, 22.298649],\n                            [113.969568, 22.321349],\n                            [113.898119, 22.308615],\n                            [113.889496, 22.271514],\n                            [113.8433, 22.229418],\n                            [113.84946, 22.191188],\n                            [113.899351, 22.215568],\n                            [113.935691, 22.205041],\n                            [113.981271, 22.229972],\n                            [113.996669, 22.206149],\n                            [114.026234, 22.229418],\n                            [114.004676, 22.239389],\n                            [114.02993, 22.263207],\n                            [114.034857, 22.300864],\n                            [114.069966, 22.326885],\n                            [114.121089, 22.320795],\n                            [114.145726, 22.300864],\n                            [114.120473, 22.272068],\n                            [114.164821, 22.226648],\n                            [114.200545, 22.232188],\n                            [114.203009, 22.206703],\n                            [114.265835, 22.200608],\n                            [114.248588, 22.274837],\n                            [114.262139, 22.294773],\n                            [114.284929, 22.263761],\n                            [114.313262, 22.264315],\n                            [114.315726, 22.299203],\n                            [114.315726, 22.299756],\n                            [114.278153, 22.328546],\n                            [114.283081, 22.386661],\n                            [114.322501, 22.385554],\n                            [114.323117, 22.385554],\n                            [114.323733, 22.385001],\n                            [114.323733, 22.384447],\n                            [114.356994, 22.340171],\n                            [114.394566, 22.361757],\n                            [114.385327, 22.41156],\n                            [114.406269, 22.432582],\n                            [114.406269, 22.433688],\n                            [114.376088, 22.436454],\n                            [114.325581, 22.479041],\n                            [114.278769, 22.435901],\n                            [114.220255, 22.427603],\n                            [114.205473, 22.449729],\n                            [114.23319, 22.466875],\n                            [114.2529, 22.445304],\n                            [114.340979, 22.50337],\n                            [114.309566, 22.497288],\n                            [114.28924, 22.52272],\n                            [114.263987, 22.541515],\n                            [114.263371, 22.541515],\n                            [114.260291, 22.547595],\n                            [114.232574, 22.528801],\n                            [114.232574, 22.539857],\n                            [114.222719, 22.553122],\n                            [114.166052, 22.559201],\n                            [114.156813, 22.543726],\n                            [114.095219, 22.534329],\n                            [114.082285, 22.512216],\n                            [114.031778, 22.503923]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.142647, 22.213906],\n                            [114.123553, 22.238836],\n                            [114.120473, 22.177888],\n                            [114.154965, 22.177888],\n                            [114.166668, 22.205041],\n                            [114.142647, 22.213906]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.305871, 22.372273],\n                            [114.313878, 22.340724],\n                            [114.332972, 22.353455],\n                            [114.305255, 22.372826],\n                            [114.305871, 22.372273]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.320037, 22.381127],\n                            [114.323733, 22.384447],\n                            [114.323733, 22.385001],\n                            [114.323117, 22.385554],\n                            [114.322501, 22.385554],\n                            [114.319421, 22.382234],\n                            [114.320037, 22.38168],\n                            [114.320037, 22.381127]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.305871, 22.369506],\n                            [114.305871, 22.372273],\n                            [114.305255, 22.372826],\n                            [114.305871, 22.369506]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.315726, 22.299203],\n                            [114.316958, 22.298649],\n                            [114.316342, 22.30031],\n                            [114.315726, 22.299756],\n                            [114.315726, 22.299203]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.319421, 22.382234],\n                            [114.320037, 22.381127],\n                            [114.320037, 22.38168],\n                            [114.319421, 22.382234]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.372392, 22.32301],\n                            [114.373008, 22.323564],\n                            [114.372392, 22.323564],\n                            [114.372392, 22.32301]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.323733, 22.297541],\n                            [114.324349, 22.297541],\n                            [114.323733, 22.298095],\n                            [114.323733, 22.297541]\n                        ]\n                    ]\n                ]\n            }\n        },\n        {\n            \"type\": \"Feature\",\n            \"properties\": {\n                \"adcode\": 820000,\n                \"name\": \"Macao\",\n                \"center\": [113.54909, 22.198951],\n                \"centroid\": [113.566988, 22.159307],\n                \"childrenNum\": 8,\n                \"level\": \"province\",\n                \"parent\": { \"adcode\": 100000 },\n                \"subFeatureIndex\": 33,\n                \"acroutes\": [100000]\n            },\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [113.554425, 22.107489],\n                            [113.6037, 22.132438],\n                            [113.575983, 22.194513],\n                            [113.558736, 22.212244],\n                            [113.53841, 22.209473],\n                            [113.534715, 22.174009],\n                            [113.554425, 22.142416],\n                            [113.554425, 22.107489]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.586453, 22.201162],\n                            [113.575983, 22.201162],\n                            [113.575983, 22.194513],\n                            [113.586453, 22.201162]\n                        ]\n                    ]\n                ]\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "core/cmd/server/web/static/world.json",
    "content": "{\n    \"type\": \"FeatureCollection\",\n    \"crs\": { \"type\": \"name\", \"properties\": { \"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\" } },\n    \"features\": [\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [47.97822265625001, 7.9970703125],\n                        [46.97822265625001, 7.9970703125],\n                        [43.98378906250002, 9.008837890624989],\n                        [43.482519531250006, 9.379492187499991],\n                        [43.181640625, 9.879980468749991],\n                        [42.84160156250002, 10.203076171874997],\n                        [42.65644531250001, 10.6],\n                        [42.92275390625002, 10.999316406249989],\n                        [43.24599609375002, 11.499804687499989],\n                        [43.85273437500001, 10.784277343749991],\n                        [44.38652343750002, 10.430224609374989],\n                        [44.94296875, 10.43671875],\n                        [45.81669921875002, 10.835888671874997],\n                        [46.565039062500006, 10.745996093749994],\n                        [47.40498046875001, 11.174023437499997],\n                        [48.01923828125001, 11.139355468749997],\n                        [48.57255859375002, 11.320507812499997],\n                        [48.938574218750006, 11.258447265624994],\n                        [50.11005859375001, 11.529296875],\n                        [50.79228515625002, 11.983691406249989],\n                        [51.2548828125, 11.830712890624994],\n                        [51.08427734375002, 11.335644531249997],\n                        [51.140625, 10.656884765624994],\n                        [51.031835937500006, 10.444775390624997],\n                        [51.19296875, 10.554638671874997],\n                        [51.390234375, 10.422607421875],\n                        [50.93007812500002, 10.33554687499999],\n                        [50.825, 9.428173828124997],\n                        [50.10283203125002, 8.199804687499991],\n                        [49.85205078125, 7.962548828124994],\n                        [49.234960937500006, 6.77734375],\n                        [49.04931640625, 6.173632812499989],\n                        [47.97529296875001, 4.497021484374997],\n                        [46.87880859375002, 3.28564453125],\n                        [46.05117187500002, 2.475146484374989],\n                        [44.92021484375002, 1.81015625],\n                        [43.71757812500002, 0.857861328124997],\n                        [41.97988281250002, -0.973046875],\n                        [41.53271484375, -1.6953125],\n                        [41.521875, -1.572265625],\n                        [41.42695312500001, -1.449511718750003],\n                        [41.24980468750002, -1.220507812500003],\n                        [40.97871093750001, -0.870312500000011],\n                        [40.964453125, 2.814648437499997],\n                        [41.341796875, 3.20166015625],\n                        [41.61347656250001, 3.590478515624994],\n                        [41.88398437500001, 3.977734375],\n                        [41.91533203125002, 4.031298828124989],\n                        [42.02412109375001, 4.137939453125],\n                        [42.85664062500001, 4.32421875],\n                        [43.12568359375001, 4.644482421874997],\n                        [43.58349609375, 4.85498046875],\n                        [43.988867187500006, 4.950537109374991],\n                        [44.940527343750006, 4.912011718749994],\n                        [47.97822265625001, 7.9970703125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Somalia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [9.579979133936737, 47.05856388629306],\n                        [9.409458596647225, 47.02019676540292],\n                        [9.46249431093294, 47.09010747968864],\n                        [9.46249431093294, 47.19858962254578],\n                        [9.527658197470123, 47.27026989773668],\n                        [9.579979133936737, 47.05856388629306]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Liechtenstein\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-8.683349609375, 27.77800740805682],\n                        [-13.038761787013554, 27.81190166624856],\n                        [-12.948925781249926, 27.914160156250034],\n                        [-11.552685546874955, 28.31010742187496],\n                        [-10.486474609374994, 29.06494140625],\n                        [-10.200585937499994, 29.380371093750057],\n                        [-9.667089843749949, 30.10927734375005],\n                        [-9.652929687499977, 30.447558593750045],\n                        [-9.875488281249943, 30.717919921874966],\n                        [-9.80869140624992, 31.42460937499996],\n                        [-9.347460937499932, 32.086376953124955],\n                        [-9.245849609375, 32.572460937499955],\n                        [-8.512841796874994, 33.25244140625003],\n                        [-6.900976562499949, 33.96904296874999],\n                        [-6.353125, 34.77607421875001],\n                        [-5.924804687499943, 35.78579101562502],\n                        [-5.277832031249943, 35.90273437500002],\n                        [-5.252685546874972, 35.61474609374997],\n                        [-4.628320312499966, 35.206396484375006],\n                        [-4.329980468749937, 35.161474609375006],\n                        [-3.693261718749994, 35.27998046874998],\n                        [-3.394726562499926, 35.21181640625005],\n                        [-2.972216796874989, 35.40727539062499],\n                        [-2.839941406249949, 35.127832031249994],\n                        [-2.731396484374955, 35.13520507812498],\n                        [-2.636816406249977, 35.11269531250002],\n                        [-2.423730468749994, 35.12348632812498],\n                        [-2.219628906249966, 35.10419921874998],\n                        [-1.795605468749926, 34.751904296874955],\n                        [-1.67919921875, 33.31865234375002],\n                        [-1.550732421874955, 33.073583984375006],\n                        [-1.510009765625, 32.877636718749955],\n                        [-1.45, 32.784814453124966],\n                        [-1.352148437499977, 32.70336914062497],\n                        [-1.29638671875, 32.67568359375002],\n                        [-1.188232421875, 32.608496093750006],\n                        [-1.111035156249983, 32.55229492187502],\n                        [-1.065527343749949, 32.46831054687496],\n                        [-1.16259765625, 32.399169921875],\n                        [-1.275341796874983, 32.089013671874966],\n                        [-2.863427734374937, 32.07470703124997],\n                        [-2.930859374999926, 32.04252929687499],\n                        [-2.988232421874983, 31.874218749999983],\n                        [-3.01738281249996, 31.834277343750017],\n                        [-3.439794921874949, 31.704541015624983],\n                        [-3.604589843749949, 31.686767578125],\n                        [-3.700244140624989, 31.70009765625005],\n                        [-3.768164062499977, 31.689550781250034],\n                        [-3.837109374999983, 31.512353515624994],\n                        [-3.833398437499937, 31.197802734375045],\n                        [-3.626904296874955, 31.000927734374983],\n                        [-4.148779296874977, 30.8095703125],\n                        [-4.322851562500006, 30.698876953124994],\n                        [-4.52915039062492, 30.62553710937499],\n                        [-4.778515624999926, 30.552392578124994],\n                        [-4.968261718749943, 30.465380859375045],\n                        [-5.061914062499937, 30.326416015625057],\n                        [-5.180126953124955, 30.166162109374994],\n                        [-5.293652343749983, 30.058642578125045],\n                        [-5.44877929687496, 29.956933593750023],\n                        [-6.00429687499999, 29.83125],\n                        [-6.479736328124943, 29.82036132812499],\n                        [-6.520556640624989, 29.659863281249983],\n                        [-6.59775390624992, 29.578955078125006],\n                        [-6.635351562499949, 29.568798828124983],\n                        [-6.755126953125, 29.583837890625034],\n                        [-6.855566406249949, 29.601611328125017],\n                        [-7.142431640624949, 29.61958007812504],\n                        [-7.427685546874983, 29.425],\n                        [-7.485742187499994, 29.392236328124994],\n                        [-8.659912109375, 28.718603515625063],\n                        [-8.683349609375, 27.900390625],\n                        [-8.683349609375, 27.77800740805682]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Morocco\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-13.038761787013554, 27.81190166624856],\n                        [-8.683349609375, 27.77800740805682],\n                        [-8.683349609375, 27.65644531250004],\n                        [-8.817822265624955, 27.65644531250004],\n                        [-8.817822265624951, 27.656445312499997],\n                        [-8.683349609375, 27.656445312499997],\n                        [-8.683349609375, 27.2859375],\n                        [-8.682861328125, 26.921337890624997],\n                        [-8.6826171875, 26.72314453125],\n                        [-8.682324218749983, 26.497705078124994],\n                        [-8.68212890625, 26.273193359375],\n                        [-8.68212890625, 26.10947265625],\n                        [-8.682226562499977, 25.995507812499994],\n                        [-12.016308593749983, 25.995410156250003],\n                        [-12.016308593749983, 25.740136718749994],\n                        [-12.016308593749983, 25.331689453124994],\n                        [-12.016308593749983, 25.059375],\n                        [-12.016308593749983, 24.923242187499994],\n                        [-12.016308593749983, 24.378662109375],\n                        [-12.016308593749983, 23.97021484375],\n                        [-12.0234375, 23.467578125],\n                        [-12.372900390624977, 23.318017578124994],\n                        [-12.559375, 23.290820312500003],\n                        [-12.620410156249989, 23.27133789062499],\n                        [-13.031494140625, 23.000244140625],\n                        [-13.153271484374983, 22.820507812499997],\n                        [-13.12702845982141, 22.703770926339278],\n                        [-13.136540684091575, 22.708182548616723],\n                        [-13.094335937499977, 22.495996093749994],\n                        [-13.051220703124983, 21.854785156250003],\n                        [-13.041748046875, 21.713818359374997],\n                        [-13.0322265625, 21.572070312500003],\n                        [-13.025097656249983, 21.466796875],\n                        [-13.016210937499977, 21.333935546874997],\n                        [-15.231201171875, 21.331298828125],\n                        [-16.964550781249983, 21.329248046874994],\n                        [-17.06396484375, 20.89882812499999],\n                        [-17.048046874999983, 20.80615234375],\n                        [-17.098779296874994, 20.856884765624997],\n                        [-16.930859374999983, 21.9],\n                        [-16.35874023437495, 22.594531250000045],\n                        [-16.21025390624999, 23.097900390625],\n                        [-15.789257812499926, 23.792871093750023],\n                        [-15.980712890624943, 23.670312500000023],\n                        [-15.899316406249966, 23.844433593749955],\n                        [-14.904296875000028, 24.719775390625017],\n                        [-14.794921874999943, 25.404150390625006],\n                        [-14.413867187499932, 26.25371093749999],\n                        [-13.57578125, 26.735107421875],\n                        [-13.175976562499983, 27.655712890624983],\n                        [-13.038761787013554, 27.81190166624856]\n                    ],\n                    [\n                        [-8.774365234374983, 27.460546875],\n                        [-8.794873046874983, 27.120703125000034],\n                        [-8.794873046874983, 27.120703125],\n                        [-8.774365234374983, 27.460546875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"W. Sahara\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [21.5625, 42.247509765625],\n                        [21.560839843750017, 42.24765625],\n                        [21.389550781250023, 42.21982421875],\n                        [21.28662109375, 42.100390625],\n                        [21.05976562500001, 42.171289062499994],\n                        [20.778125, 42.071044921875],\n                        [20.725, 41.87353515625],\n                        [20.566210937500017, 41.873681640624994],\n                        [20.485449218750006, 42.223388671875],\n                        [20.06396484375, 42.54726562499999],\n                        [20.054296875, 42.760058593749996],\n                        [20.344335937500006, 42.827929687499996],\n                        [20.40996305279786, 42.84373166741877],\n                        [20.344335937500063, 42.82792968750002],\n                        [19.670996093750006, 43.163964843749994],\n                        [19.21875, 43.449951171875],\n                        [19.196484375000068, 43.48500976562502],\n                        [19.19160156250004, 43.52104492187499],\n                        [19.19433593749997, 43.533300781250006],\n                        [19.495117187500057, 43.642871093750045],\n                        [19.245019531249994, 43.96503906250004],\n                        [19.583789062500017, 44.04345703125003],\n                        [19.118457031250074, 44.359960937500006],\n                        [19.348632812500057, 44.88090820312502],\n                        [19.007128906250045, 44.86918945312502],\n                        [19.062890625000023, 45.13720703125],\n                        [19.4, 45.2125],\n                        [19.004687500000074, 45.39951171875006],\n                        [19.064257812500045, 45.51499023437506],\n                        [18.839062499999983, 45.83574218750002],\n                        [18.905371093750006, 45.931738281250034],\n                        [19.421289062500023, 46.064453125],\n                        [19.61347656250001, 46.169189453125],\n                        [19.84443359375001, 46.145898437499966],\n                        [19.934082031250057, 46.161474609375034],\n                        [20.161425781250017, 46.14189453124996],\n                        [20.210156250000068, 46.12602539062502],\n                        [20.241796875000034, 46.10859375000001],\n                        [20.301367187500006, 46.05068359375002],\n                        [20.35859375000004, 45.975488281249994],\n                        [20.581152343749977, 45.86948242187506],\n                        [20.65273437499999, 45.779394531250006],\n                        [20.709277343750074, 45.735253906249994],\n                        [20.727832031250017, 45.73740234374998],\n                        [20.746875, 45.74897460937501],\n                        [20.76015625000005, 45.75810546875002],\n                        [20.775, 45.74980468750002],\n                        [20.794042968750006, 45.467871093750034],\n                        [21.431445312500017, 45.192529296874994],\n                        [21.465429687500006, 45.171875],\n                        [21.357031250000034, 44.99077148437502],\n                        [21.532324218750063, 44.900683593750045],\n                        [21.519921875000023, 44.88081054687498],\n                        [21.442187500000074, 44.87338867187498],\n                        [21.384375, 44.87006835937501],\n                        [21.357910156250057, 44.86181640625003],\n                        [21.36005859375004, 44.82666015624997],\n                        [21.52314453125004, 44.79008789062499],\n                        [21.63613281250005, 44.71044921875],\n                        [21.909277343750034, 44.666113281250034],\n                        [22.026953125, 44.61987304687503],\n                        [22.093066406250074, 44.541943359374955],\n                        [22.200976562500017, 44.560693359374966],\n                        [22.350683593750063, 44.676123046875034],\n                        [22.497656249999977, 44.70625],\n                        [22.64208984375, 44.65097656249998],\n                        [22.720898437499983, 44.605517578125045],\n                        [22.734375, 44.56992187499998],\n                        [22.700781250000063, 44.55551757812498],\n                        [22.620117187500057, 44.562353515625034],\n                        [22.554003906250017, 44.54033203124999],\n                        [22.49453125000005, 44.43544921875002],\n                        [22.687890625000023, 44.248291015625],\n                        [22.42080078125005, 44.00742187500006],\n                        [22.399023437500063, 43.96953125],\n                        [22.36542968750004, 43.86210937500002],\n                        [22.36962890625003, 43.78129882812499],\n                        [22.55458984375005, 43.45449218750002],\n                        [22.767578125, 43.35415039062502],\n                        [22.81972656250005, 43.300732421874955],\n                        [22.85957031250001, 43.252343749999966],\n                        [22.97685546874999, 43.18798828125],\n                        [22.799902343750006, 42.985742187499994],\n                        [22.706152343750006, 42.88393554687505],\n                        [22.466796875, 42.842480468749955],\n                        [22.53242187500004, 42.48120117187497],\n                        [22.523535156250006, 42.440966796875045],\n                        [22.44570312500005, 42.35913085937497],\n                        [22.42207031250004, 42.32885742187503],\n                        [22.344042968750045, 42.31396484375003],\n                        [22.23974609375003, 42.303110028468716],\n                        [21.81464843750001, 42.303125],\n                        [21.5625, 42.24750976562498],\n                        [21.5625, 42.247509765625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Serbia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [74.54140625000002, 37.02216796875],\n                        [74.03886718750002, 36.825732421874996],\n                        [73.116796875, 36.868554687499994],\n                        [72.24980468750002, 36.734716796875],\n                        [71.23291015625, 36.12177734375],\n                        [71.18505859375, 36.04208984375],\n                        [71.57197265625001, 35.546826171875],\n                        [71.62050781250002, 35.183007812499994],\n                        [70.965625, 34.53037109375],\n                        [71.095703125, 34.369433593749996],\n                        [71.05156250000002, 34.049707031249994],\n                        [70.65400390625001, 33.952294921874994],\n                        [69.8896484375, 34.007275390625],\n                        [70.26113281250002, 33.289013671875],\n                        [69.5015625, 33.020068359374996],\n                        [69.24140625000001, 32.433544921875],\n                        [69.279296875, 31.936816406249996],\n                        [68.86894531250002, 31.634228515624997],\n                        [68.59765625, 31.802978515625],\n                        [68.16103515625002, 31.802978515625],\n                        [67.57822265625, 31.506494140624994],\n                        [67.737890625, 31.343945312499997],\n                        [67.45283203125001, 31.234619140625],\n                        [66.82929687500001, 31.263671875],\n                        [66.346875, 30.802783203124996],\n                        [66.23125, 29.86572265625],\n                        [65.09550781250002, 29.559472656249994],\n                        [64.39375, 29.544335937499994],\n                        [64.09873046875, 29.391943359375],\n                        [63.56757812500001, 29.497998046874997],\n                        [62.4765625, 29.408349609374994],\n                        [62.0009765625, 29.530419921874994],\n                        [61.22441406250002, 29.749414062499994],\n                        [60.843359375, 29.858691406249996],\n                        [61.331640625, 30.363720703124997],\n                        [61.55947265625002, 30.599365234375],\n                        [61.7841796875, 30.831933593749994],\n                        [61.81083984375002, 30.91328125],\n                        [61.81425781250002, 31.072558593749996],\n                        [61.75507812500001, 31.285302734374994],\n                        [61.66015625, 31.382421875],\n                        [61.34648437500002, 31.421630859375],\n                        [61.11074218750002, 31.451123046874997],\n                        [60.854101562500006, 31.483251953125],\n                        [60.82070312500002, 31.495166015624996],\n                        [60.791601562500006, 31.660595703124997],\n                        [60.804296875, 31.73447265625],\n                        [60.7875, 31.877197265625],\n                        [60.78994140625002, 31.987109375],\n                        [60.827246093750006, 32.16796875],\n                        [60.82929687500001, 32.249414062499994],\n                        [60.71044921875, 32.6],\n                        [60.57656250000002, 32.994873046875],\n                        [60.560546875, 33.137841796874994],\n                        [60.9169921875, 33.505224609375],\n                        [60.573828125, 33.588330078125],\n                        [60.4859375, 33.7119140625],\n                        [60.48574218750002, 34.094775390624996],\n                        [60.642675781250006, 34.307177734374996],\n                        [60.88945312500002, 34.31943359375],\n                        [60.80390625000001, 34.418017578124996],\n                        [60.76259765625002, 34.475244140624994],\n                        [60.73613281250002, 34.491796875],\n                        [60.72626953125001, 34.51826171875],\n                        [60.73945312500001, 34.544726562499996],\n                        [60.80234375, 34.554638671875],\n                        [60.8453125, 34.587695312499996],\n                        [60.91474609375001, 34.633984375],\n                        [60.951171875, 34.653857421874996],\n                        [61.080078125, 34.855615234374994],\n                        [61.1, 35.272314453125],\n                        [61.18925781250002, 35.31201171875],\n                        [61.24550781250002, 35.474072265625],\n                        [61.27851562500001, 35.51376953125],\n                        [61.281835937500006, 35.55341796875],\n                        [61.26201171875002, 35.619580078125],\n                        [61.3447265625, 35.6294921875],\n                        [61.62099609375002, 35.43232421875],\n                        [62.08964843750002, 35.3796875],\n                        [62.30781250000001, 35.170800781249994],\n                        [62.688085937500006, 35.255322265625],\n                        [63.056640625, 35.44580078125],\n                        [63.08417968750001, 35.56806640625],\n                        [63.16972656250002, 35.678125],\n                        [63.129980468750006, 35.84619140625],\n                        [63.8625, 36.012353515624994],\n                        [64.184375, 36.14892578125],\n                        [64.51103515625002, 36.340673828125],\n                        [64.56582031250002, 36.427587890625],\n                        [64.6025390625, 36.554541015625],\n                        [64.78242187500001, 37.05927734375],\n                        [64.81630859375002, 37.132080078125],\n                        [64.95156250000002, 37.1935546875],\n                        [65.08964843750002, 37.237939453124994],\n                        [65.30361328125002, 37.24677734375],\n                        [65.55498046875002, 37.251171875],\n                        [65.76503906250002, 37.569140625],\n                        [66.471875, 37.3447265625],\n                        [66.52226562500002, 37.348486328125],\n                        [66.827734375, 37.3712890625],\n                        [67.06884765625, 37.334814453125],\n                        [67.19550781250001, 37.235205078125],\n                        [67.31972656250002, 37.2095703125],\n                        [67.44169921875002, 37.2580078125],\n                        [67.51728515625001, 37.266650390624996],\n                        [67.546484375, 37.235644531249996],\n                        [67.607421875, 37.222509765625],\n                        [67.7, 37.22724609375],\n                        [67.7529296875, 37.1998046875],\n                        [67.75898437500001, 37.172216796875],\n                        [67.76601562500002, 37.14013671875],\n                        [67.83447265625, 37.064208984375],\n                        [67.9580078125, 36.972021484375],\n                        [68.06777343750002, 36.9498046875],\n                        [68.26093750000001, 37.013085937499994],\n                        [68.284765625, 37.036328125],\n                        [68.29951171875001, 37.088427734374996],\n                        [68.38691406250001, 37.1375],\n                        [68.66914062500001, 37.2583984375],\n                        [68.7232421875, 37.268017578125],\n                        [68.78203125000002, 37.2580078125],\n                        [68.82373046875, 37.270703125],\n                        [68.8384765625, 37.30283203125],\n                        [68.85537109375002, 37.316845703125],\n                        [68.88525390625, 37.328076171875],\n                        [68.91181640625001, 37.333935546875],\n                        [68.96044921875, 37.325048828125],\n                        [69.18017578125, 37.15830078125],\n                        [69.26484375000001, 37.1083984375],\n                        [69.30390625000001, 37.116943359375],\n                        [69.35380859375002, 37.150048828124994],\n                        [69.41445312500002, 37.207763671875],\n                        [69.4296875, 37.290869140625],\n                        [69.39921875000002, 37.399316406249994],\n                        [69.42011718750001, 37.48671875],\n                        [69.49208984375002, 37.553076171875],\n                        [69.62578125000002, 37.594042968749996],\n                        [69.8208984375, 37.6095703125],\n                        [69.9849609375, 37.566162109375],\n                        [70.18867187500001, 37.582470703125],\n                        [70.25146484375, 37.66416015625],\n                        [70.25498046875, 37.765380859375],\n                        [70.19941406250001, 37.886035156249996],\n                        [70.21464843750002, 37.9244140625],\n                        [70.41777343750002, 38.075439453125],\n                        [70.7359375, 38.42255859375],\n                        [71.255859375, 38.306982421875],\n                        [71.33271484375001, 38.170263671875],\n                        [71.27851562500001, 37.918408203125],\n                        [71.319921875, 37.90185546875],\n                        [71.3896484375, 37.906298828124996],\n                        [71.48779296875, 37.931884765625],\n                        [71.55195312500001, 37.933154296874996],\n                        [71.58222656250001, 37.910107421875],\n                        [71.43291015625002, 37.1275390625],\n                        [71.530859375, 36.845117187499994],\n                        [71.665625, 36.696923828124994],\n                        [72.65742187500001, 37.029052734375],\n                        [72.8955078125, 37.267529296875],\n                        [73.21113281250001, 37.408496093749996],\n                        [73.38291015625, 37.462255859375],\n                        [73.48134765625002, 37.4716796875],\n                        [73.60468750000001, 37.446044921875],\n                        [73.65712890625002, 37.43046875],\n                        [73.72060546875002, 37.41875],\n                        [73.73378906250002, 37.37578125],\n                        [73.71728515625, 37.329443359375],\n                        [73.6275390625, 37.261572265625],\n                        [73.65351562500001, 37.23935546875],\n                        [73.749609375, 37.231787109375],\n                        [74.16708984375, 37.329443359375],\n                        [74.20351562500002, 37.372460937499994],\n                        [74.25966796875002, 37.415429687499994],\n                        [74.659375, 37.394482421875],\n                        [74.73056640625, 37.35703125],\n                        [74.83046875000002, 37.2859375],\n                        [74.89130859375001, 37.231640625],\n                        [74.84023437500002, 37.225048828125],\n                        [74.76738281250002, 37.249169921874994],\n                        [74.73896484375001, 37.28564453125],\n                        [74.72666015625, 37.29072265625],\n                        [74.6689453125, 37.26669921875],\n                        [74.55898437500002, 37.236621093749996],\n                        [74.37216796875, 37.15771484375],\n                        [74.37617187500001, 37.137353515624994],\n                        [74.49794921875002, 37.0572265625],\n                        [74.52646484375, 37.030664062499994],\n                        [74.54140625000002, 37.02216796875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Afghanistan\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [17.57958984375, -8.099023437500009],\n                            [17.643359375000017, -8.090722656250009],\n                            [18.00878906250003, -8.107617187499983],\n                            [18.56269531250001, -7.9359375],\n                            [18.944433593750063, -8.001464843750028],\n                            [19.142675781250034, -8.001464843750028],\n                            [19.34082031249997, -7.966601562500031],\n                            [19.369921875000045, -7.706542968749986],\n                            [19.371679687500063, -7.655078124999989],\n                            [19.47988281250008, -7.472167968750028],\n                            [19.48378906250008, -7.279492187500026],\n                            [19.527636718750017, -7.144433593749952],\n                            [19.87519531250004, -6.986328124999986],\n                            [19.99746093750008, -6.976464843750023],\n                            [20.190039062500063, -6.9462890625],\n                            [20.482226562500074, -6.915820312500017],\n                            [20.59003906250001, -6.919921874999957],\n                            [20.598730468750006, -6.935156249999949],\n                            [20.536914062500045, -7.121777343749955],\n                            [20.535839843749983, -7.182812499999955],\n                            [20.558398437500045, -7.244433593749989],\n                            [20.60781250000008, -7.277734375000023],\n                            [20.910937500000017, -7.281445312499983],\n                            [21.190332031250023, -7.284960937499989],\n                            [21.751074218750034, -7.305468749999989],\n                            [21.80605468750005, -7.32861328125],\n                            [21.905371093750034, -8.693359374999943],\n                            [21.813183593750068, -9.46875],\n                            [22.19775390625, -10.040625],\n                            [22.30703125000005, -10.691308593750023],\n                            [22.203515625000023, -10.829492187500009],\n                            [22.226171875, -11.121972656250009],\n                            [22.27880859375, -11.19414062499996],\n                            [22.314941406250057, -11.198632812499994],\n                            [22.39296875000005, -11.159472656250003],\n                            [22.486132812500045, -11.086718750000017],\n                            [22.56103515625003, -11.05585937500004],\n                            [22.814746093750017, -11.08027343750004],\n                            [23.076269531250006, -11.087890624999986],\n                            [23.463964843750034, -10.969335937499991],\n                            [23.83388671875008, -11.013671874999972],\n                            [23.96650390625001, -10.871777343750011],\n                            [23.98388671875, -11.725],\n                            [23.909375, -12.636132812500009],\n                            [23.886523437500045, -12.743261718749991],\n                            [23.882421875, -12.799023437499983],\n                            [23.968066406250045, -12.956933593749994],\n                            [23.962988281250006, -12.988476562500026],\n                            [23.843164062500023, -13.0009765625],\n                            [22.209570312500006, -13.0009765625],\n                            [21.97890625000008, -13.0009765625],\n                            [21.979101562500034, -13.798730468749994],\n                            [21.979296875000074, -14.11962890625],\n                            [21.979394531249994, -14.440527343750006],\n                            [21.97978515624999, -15.955566406250014],\n                            [22.193945312500006, -16.628125],\n                            [23.380664062500017, -17.640625],\n                            [22.32421875, -17.8375],\n                            [20.74550781250008, -18.019726562499983],\n                            [20.194335937500057, -17.86367187499999],\n                            [18.95527343750004, -17.80351562499999],\n                            [18.39638671875005, -17.3994140625],\n                            [16.14843750000003, -17.39023437499999],\n                            [14.017480468750023, -17.40888671874997],\n                            [13.475976562500023, -17.04003906249997],\n                            [13.179492187500017, -16.971679687499986],\n                            [12.548144531250017, -17.212695312499974],\n                            [12.35927734375008, -17.205859375],\n                            [12.318457031250006, -17.21337890625003],\n                            [12.213378906250028, -17.209960937500043],\n                            [12.013964843750074, -17.168554687500034],\n                            [11.902539062500011, -17.226562499999957],\n                            [11.743066406250023, -17.24921875000004],\n                            [11.780078125000017, -16.87128906249997],\n                            [11.818945312500034, -16.704101562500014],\n                            [11.750878906250023, -15.831933593749966],\n                            [12.016113281250057, -15.513671874999957],\n                            [12.55048828125004, -13.437792968750003],\n                            [12.983203124999989, -12.775683593750017],\n                            [13.4169921875, -12.52041015624998],\n                            [13.597949218750017, -12.286132812500028],\n                            [13.785351562499983, -11.81279296874996],\n                            [13.833593750000063, -10.9296875],\n                            [13.33222656250004, -9.998925781250009],\n                            [12.99853515625, -9.048046875],\n                            [13.358984375, -8.687207031250026],\n                            [13.378515625000063, -8.369726562500006],\n                            [12.82343750000004, -6.954785156249955],\n                            [12.283300781250063, -6.12431640624996],\n                            [13.184375, -5.85625],\n                            [13.346484375000017, -5.863378906250006],\n                            [13.978515625, -5.857226562500003],\n                            [16.315234375000074, -5.865625],\n                            [16.431445312500045, -5.90019531249996],\n                            [16.53710937499997, -5.9658203125],\n                            [16.697265625, -6.164257812500026],\n                            [16.74296874999999, -6.618457031250003],\n                            [16.813085937500063, -6.772558593749963],\n                            [16.919433593750057, -6.93398437499998],\n                            [16.98476562500005, -7.257421874999977],\n                            [17.57958984375, -8.099023437500009]\n                        ]\n                    ],\n                    [\n                        [\n                            [12.50146484375, -4.5875],\n                            [12.848144531250028, -4.428906249999983],\n                            [12.881054687500068, -4.445117187499989],\n                            [12.971386718750068, -4.551757812499957],\n                            [13.048046875000068, -4.619238281250034],\n                            [13.072753906250028, -4.634765625],\n                            [13.057324218750011, -4.651074218750026],\n                            [12.947460937500011, -4.695312499999986],\n                            [12.829687499999977, -4.73662109374996],\n                            [12.451464843750017, -5.071484374999969],\n                            [12.453222656250034, -5.090625],\n                            [12.52236328125008, -5.148925781250028],\n                            [12.484570312500011, -5.71875],\n                            [12.213671875000074, -5.758691406249994],\n                            [12.177148437499994, -5.324804687499977],\n                            [12.018359375000074, -5.004296874999966],\n                            [12.50146484375, -4.5875]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Angola\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [20.06396484375, 42.54726562499999],\n                        [20.485449218750006, 42.223388671875],\n                        [20.566210937500017, 41.873681640624994],\n                        [20.48896484375001, 41.272607421874994],\n                        [20.709277343750017, 40.928369140624994],\n                        [20.964257812500023, 40.849902343749996],\n                        [20.95576171875001, 40.775292968749994],\n                        [21.030859375, 40.622460937499994],\n                        [20.881640625000017, 40.467919921874994],\n                        [20.65742187500001, 40.1173828125],\n                        [20.4560546875, 40.065576171874994],\n                        [20.408007812500017, 40.049462890624994],\n                        [20.383691406250023, 40.0171875],\n                        [20.338476562500006, 39.991064453125],\n                        [20.311132812500006, 39.979443359375],\n                        [20.311328125000017, 39.95078125],\n                        [20.381640625000017, 39.841796875],\n                        [20.382421875, 39.802636718749994],\n                        [20.206835937500017, 39.653515625],\n                        [20.13105468750001, 39.66162109375],\n                        [20.05976562500001, 39.699121093749994],\n                        [20.022558593750006, 39.710693359375],\n                        [20.001269531250017, 39.709423828125],\n                        [19.851855468750017, 40.0435546875],\n                        [19.322265625, 40.407080078125],\n                        [19.45917968750001, 40.40537109375],\n                        [19.3375, 40.663818359375],\n                        [19.57568359375, 41.640429687499996],\n                        [19.577539062500023, 41.7875],\n                        [19.342382812500006, 41.869091796875],\n                        [19.280664062500023, 42.17255859375],\n                        [19.65449218750001, 42.628564453124994],\n                        [19.78828125000001, 42.476171875],\n                        [20.06396484375, 42.54726562499999]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Albania\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [19.66230468750001, 60.187158203124994],\n                            [19.53652343750005, 60.14497070312501],\n                            [19.551367187500063, 60.24384765625001],\n                            [19.66230468750001, 60.187158203124994]\n                        ]\n                    ],\n                    [\n                        [\n                            [19.989550781250074, 60.351171875],\n                            [20.258886718750063, 60.26127929687499],\n                            [19.799804687500057, 60.08173828125001],\n                            [19.68691406250005, 60.267626953125045],\n                            [19.84765625000003, 60.22055664062506],\n                            [19.823046875000074, 60.390185546875045],\n                            [19.989550781250074, 60.351171875]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Aland\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [1.7060546875, 42.503320312499994],\n                        [1.534082031250023, 42.441699218749996],\n                        [1.448828125, 42.437451171875],\n                        [1.428125, 42.46132812499999],\n                        [1.414843750000017, 42.548388671874996],\n                        [1.428320312500006, 42.5958984375],\n                        [1.501367187500023, 42.642724609374994],\n                        [1.568164062500017, 42.635009765625],\n                        [1.709863281250023, 42.604443359375],\n                        [1.739453125000011, 42.575927734375],\n                        [1.740234375, 42.55673828125],\n                        [1.713964843750006, 42.525634765625],\n                        [1.7060546875, 42.503320312499994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Andorra\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [53.927832031250006, 24.177197265624983],\n                            [53.63447265625004, 24.169775390624977],\n                            [53.83378906250002, 24.258935546875023],\n                            [53.927832031250006, 24.177197265624983]\n                        ]\n                    ],\n                    [\n                        [\n                            [53.3322265625001, 24.258593750000045],\n                            [53.19091796874997, 24.290917968749966],\n                            [53.412402343750074, 24.411035156250023],\n                            [53.3322265625001, 24.258593750000045]\n                        ]\n                    ],\n                    [\n                        [\n                            [56.29785156250003, 25.650683593750045],\n                            [56.38798828125002, 24.97919921875004],\n                            [56.06386718750005, 24.73876953125],\n                            [56.00058593750006, 24.953222656249977],\n                            [55.795703125000074, 24.868115234374955],\n                            [55.76083984375006, 24.24267578125],\n                            [55.92861328125005, 24.215136718750074],\n                            [55.98515625000002, 24.063378906249966],\n                            [55.4684570312501, 23.94111328125001],\n                            [55.53164062499999, 23.81904296875001],\n                            [55.1999023437501, 23.034765625000034],\n                            [55.185839843750074, 22.7041015625],\n                            [55.104296875000074, 22.621484375000023],\n                            [52.55507812500005, 22.932812499999955],\n                            [51.592578125000074, 24.07885742187503],\n                            [51.56835937500003, 24.286181640625074],\n                            [51.76757812500003, 24.25439453125],\n                            [51.84316406250005, 24.010888671875023],\n                            [52.118554687499994, 23.97109375],\n                            [52.64824218750002, 24.154638671875006],\n                            [53.80175781249997, 24.069482421874966],\n                            [54.14794921875003, 24.17119140624999],\n                            [54.39707031250006, 24.278173828125034],\n                            [54.74677734375004, 24.810449218750023],\n                            [55.94121093750002, 25.793994140625017],\n                            [56.08046875, 26.06264648437505],\n                            [56.16748046875003, 26.047460937499977],\n                            [56.144628906250006, 25.690527343750006],\n                            [56.29785156250003, 25.650683593750045]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"United Arab Emirates\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-64.54916992187498, -54.71621093749998],\n                            [-63.81542968749997, -54.725097656250014],\n                            [-64.637353515625, -54.90253906250001],\n                            [-64.75732421875, -54.82656249999999],\n                            [-64.54916992187498, -54.71621093749998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-68.65322265624994, -54.85361328124999],\n                            [-68.62993164062499, -52.65263671875004],\n                            [-68.24013671875, -53.08183593749999],\n                            [-68.43115234375, -53.0552734375],\n                            [-68.48852539062497, -53.260937499999976],\n                            [-68.16113281249997, -53.30644531250001],\n                            [-68.00849609374995, -53.5640625],\n                            [-67.29423828125002, -54.049804687500014],\n                            [-66.23564453124999, -54.53349609374997],\n                            [-65.17900390624993, -54.678125],\n                            [-65.47114257812495, -54.91464843749999],\n                            [-66.5111328125, -55.032128906249945],\n                            [-67.127099609375, -54.90380859375001],\n                            [-68.65322265624994, -54.85361328124999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-61.084716796875, -23.65644531250001],\n                            [-60.83984375000003, -23.85810546874997],\n                            [-59.89248046874994, -24.093554687499974],\n                            [-59.18725585937497, -24.56230468749999],\n                            [-57.82167968749994, -25.136425781249983],\n                            [-57.56313476562494, -25.473730468749963],\n                            [-57.943115234375, -26.05292968750001],\n                            [-58.18149414062498, -26.30742187499999],\n                            [-58.222070312499994, -26.65],\n                            [-58.618603515624955, -27.13212890624996],\n                            [-58.64174804687494, -27.196093750000017],\n                            [-58.60483398437498, -27.314355468750037],\n                            [-58.16826171874993, -27.27343749999997],\n                            [-56.437158203124966, -27.553808593749977],\n                            [-56.16406250000003, -27.321484374999983],\n                            [-55.95146484374996, -27.325683593749957],\n                            [-55.789990234374926, -27.416406249999966],\n                            [-55.71464843749996, -27.41484375],\n                            [-55.632910156250006, -27.35712890624997],\n                            [-55.59379882812502, -27.288085937500014],\n                            [-55.597265625, -27.207617187499963],\n                            [-55.56489257812498, -27.15],\n                            [-55.496728515624966, -27.11533203124999],\n                            [-55.45063476562498, -27.068359375000014],\n                            [-55.426660156249994, -27.00927734374997],\n                            [-55.13593750000001, -26.931152343749957],\n                            [-54.934472656249994, -26.70253906250001],\n                            [-54.677734375, -26.308789062499997],\n                            [-54.631933593750006, -26.005761718749994],\n                            [-54.615869140624994, -25.576074218750023],\n                            [-54.44394531249998, -25.625],\n                            [-54.15458984374999, -25.523046874999963],\n                            [-53.89116210937499, -25.66884765625001],\n                            [-53.668554687500006, -26.288183593749977],\n                            [-53.83818359375002, -27.121093750000014],\n                            [-54.32700195312495, -27.423535156249997],\n                            [-54.82910156250003, -27.55058593750003],\n                            [-55.10151367187501, -27.866796874999963],\n                            [-55.72548828125002, -28.20410156250003],\n                            [-55.68725585937497, -28.38164062499996],\n                            [-55.890527343749994, -28.370019531249994],\n                            [-56.938623046874994, -29.594824218750034],\n                            [-57.22465820312499, -29.782128906249994],\n                            [-57.40522460937501, -30.03388671875004],\n                            [-57.563867187499994, -30.139941406249974],\n                            [-57.60888671875003, -30.187792968750045],\n                            [-57.65087890624997, -30.295019531250034],\n                            [-57.71269531249996, -30.38447265624997],\n                            [-57.83120117187502, -30.495214843749963],\n                            [-57.87250976562501, -30.591015625000026],\n                            [-57.81059570312499, -30.85859375000001],\n                            [-57.88632812499998, -30.937402343749994],\n                            [-57.86840820312497, -31.104394531249994],\n                            [-57.89335937499999, -31.195312499999957],\n                            [-58.03339843750001, -31.416601562500006],\n                            [-58.053857421874994, -31.494921874999974],\n                            [-58.009667968749966, -31.534375],\n                            [-57.98798828124998, -31.576171875],\n                            [-58.00698242187494, -31.684960937499966],\n                            [-58.04233398437495, -31.769238281249997],\n                            [-58.16748046874997, -31.87265625],\n                            [-58.18901367187499, -31.924218750000037],\n                            [-58.16040039062503, -31.986523437500026],\n                            [-58.156347656250006, -32.0515625],\n                            [-58.17700195312494, -32.11904296875002],\n                            [-58.16479492187494, -32.18486328125002],\n                            [-58.119726562500006, -32.24892578125002],\n                            [-58.12304687499997, -32.321875],\n                            [-58.201171875, -32.471679687500014],\n                            [-58.219970703125, -32.563964843749986],\n                            [-58.17099609374998, -32.95927734374996],\n                            [-58.424462890624994, -33.11152343749998],\n                            [-58.54721679687498, -33.66347656249998],\n                            [-58.392480468749966, -34.192968750000034],\n                            [-58.52548828124998, -34.29619140625002],\n                            [-58.28334960937494, -34.68349609375005],\n                            [-57.54785156250003, -35.018945312499994],\n                            [-57.170654296875, -35.3625],\n                            [-57.35390624999994, -35.72031249999998],\n                            [-57.33544921875, -36.026757812499966],\n                            [-57.07617187499994, -36.296777343749994],\n                            [-56.74946289062501, -36.346484375],\n                            [-56.67202148437494, -36.85126953124998],\n                            [-57.546972656250034, -38.085644531250026],\n                            [-58.17919921874994, -38.435839843750045],\n                            [-59.82832031250001, -38.83818359375003],\n                            [-61.112207031249994, -38.99296875000003],\n                            [-61.84790039062497, -38.961816406249994],\n                            [-62.33476562499993, -38.80009765625],\n                            [-62.29506835937502, -39.24326171874996],\n                            [-62.053662109374955, -39.373828125],\n                            [-62.179345703124994, -39.38046875000002],\n                            [-62.076806640624966, -39.46152343750002],\n                            [-62.131542968749926, -39.82539062499998],\n                            [-62.28691406249996, -39.89531250000002],\n                            [-62.40185546875003, -40.19658203125002],\n                            [-62.24633789062494, -40.674609374999974],\n                            [-62.39501953124997, -40.89082031249997],\n                            [-62.95903320312493, -41.10966796875006],\n                            [-63.621777343749955, -41.15976562499996],\n                            [-64.86948242187503, -40.735839843750014],\n                            [-65.13339843749998, -40.88066406250003],\n                            [-64.98637695312496, -42.102050781249986],\n                            [-64.53774414062494, -42.25458984374998],\n                            [-64.57099609374998, -42.416015625],\n                            [-64.42041015625003, -42.43378906249998],\n                            [-64.10087890624993, -42.395117187500006],\n                            [-64.06118164062494, -42.266113281250014],\n                            [-64.228515625, -42.21826171874996],\n                            [-63.795556640624994, -42.113867187500006],\n                            [-63.6298828125, -42.28271484375003],\n                            [-63.61733398437502, -42.695800781249986],\n                            [-64.03476562499998, -42.88125],\n                            [-64.48784179687499, -42.51347656250006],\n                            [-64.97070312499997, -42.66630859375002],\n                            [-65.02690429687496, -42.75888671874996],\n                            [-64.31914062499999, -42.968945312500026],\n                            [-64.83994140624998, -43.18886718749998],\n                            [-65.25234374999997, -43.571875],\n                            [-65.26552734375, -44.2796875],\n                            [-65.64760742187502, -44.661425781250045],\n                            [-65.63876953125, -45.0078125],\n                            [-66.19013671874995, -44.96474609375002],\n                            [-66.94140625, -45.25732421875003],\n                            [-67.59956054687495, -46.05253906250003],\n                            [-67.5064453125, -46.44277343749995],\n                            [-66.77685546874994, -47.005859375],\n                            [-65.99853515625, -47.09375],\n                            [-65.73808593749999, -47.34492187499998],\n                            [-65.81430664062495, -47.63818359374996],\n                            [-66.22524414062502, -47.826757812500006],\n                            [-65.93422851562497, -47.826757812500006],\n                            [-65.81005859374997, -47.941113281250026],\n                            [-67.46630859375, -48.95175781250004],\n                            [-67.68486328125002, -49.2466796875],\n                            [-67.82597656249999, -49.91962890625005],\n                            [-68.2572265625, -50.104589843749984],\n                            [-68.66757812500003, -49.75253906250003],\n                            [-68.66162109374997, -49.93574218750005],\n                            [-68.97958984375, -50.003027343749984],\n                            [-68.59794921874996, -50.00947265624997],\n                            [-68.421875, -50.15791015625001],\n                            [-69.04477539062495, -50.49912109374998],\n                            [-69.35859374999993, -51.028125],\n                            [-69.20102539062498, -50.99365234375001],\n                            [-69.03530273437497, -51.48896484375002],\n                            [-69.46542968750003, -51.58447265625003],\n                            [-68.96533203125003, -51.67714843749999],\n                            [-68.443359375, -52.35664062500004],\n                            [-69.96025390624993, -52.00820312500002],\n                            [-71.91865234374995, -51.98955078125004],\n                            [-72.40766601562501, -51.54082031250002],\n                            [-72.34023437499997, -50.68183593749999],\n                            [-72.50981445312496, -50.607519531250034],\n                            [-73.15292968749998, -50.73828125000003],\n                            [-73.50126953124996, -50.125292968750024],\n                            [-73.55419921875, -49.463867187500014],\n                            [-73.46157226562497, -49.31386718750001],\n                            [-73.13525390625, -49.30068359374999],\n                            [-73.03364257812501, -49.014355468750004],\n                            [-72.65126953125, -48.84160156249998],\n                            [-72.582861328125, -48.47539062499999],\n                            [-72.35473632812497, -48.36582031250005],\n                            [-72.32832031250001, -48.11005859374998],\n                            [-72.517919921875, -47.87636718749998],\n                            [-72.34594726562497, -47.49267578124997],\n                            [-71.90498046875001, -47.201660156250014],\n                            [-71.94023437499999, -46.83125],\n                            [-71.69965820312501, -46.6513671875],\n                            [-71.87568359374998, -46.160546875],\n                            [-71.63154296874998, -45.95371093749998],\n                            [-71.74619140624998, -45.57890625],\n                            [-71.34931640624995, -45.33193359374995],\n                            [-71.5962890625, -44.97919921875004],\n                            [-72.04169921874998, -44.90419921875004],\n                            [-72.06372070312503, -44.771875],\n                            [-71.26113281250002, -44.763085937499966],\n                            [-71.15971679687496, -44.56025390625004],\n                            [-71.21259765624998, -44.44121093750003],\n                            [-71.82001953124993, -44.38310546875],\n                            [-71.68007812500002, -43.92958984374998],\n                            [-71.90498046875001, -43.34755859374998],\n                            [-71.750634765625, -43.237304687499986],\n                            [-72.14643554687498, -42.990039062499974],\n                            [-72.10820312499993, -42.25185546874995],\n                            [-71.75, -42.04677734375001],\n                            [-71.91127929687497, -41.650390624999986],\n                            [-71.93212890624994, -40.69169921874999],\n                            [-71.70898437499997, -40.381738281249994],\n                            [-71.81831054687493, -40.17666015624995],\n                            [-71.65976562499998, -40.02080078125],\n                            [-71.71992187499995, -39.63525390624997],\n                            [-71.53945312499997, -39.60244140624995],\n                            [-71.40156249999995, -38.93505859374996],\n                            [-70.858642578125, -38.60449218750003],\n                            [-71.16757812499998, -37.76230468749996],\n                            [-71.19218750000002, -36.84365234375004],\n                            [-71.05551757812498, -36.52373046874996],\n                            [-70.40478515625, -36.06171874999998],\n                            [-70.41572265625001, -35.52304687500002],\n                            [-70.55517578125, -35.246875],\n                            [-70.39316406250003, -35.146875],\n                            [-70.05205078124999, -34.30078124999997],\n                            [-69.85244140625, -34.224316406250026],\n                            [-69.81962890624999, -33.28378906249999],\n                            [-70.08486328125002, -33.20175781249998],\n                            [-70.02197265625, -32.88457031250002],\n                            [-70.36376953125, -32.08349609374997],\n                            [-70.25439453125, -31.957714843750026],\n                            [-70.585205078125, -31.569433593749963],\n                            [-70.51958007812493, -31.1484375],\n                            [-70.30908203124994, -31.02265625000004],\n                            [-70.15322265625, -30.360937499999963],\n                            [-69.95634765624996, -30.35820312500003],\n                            [-69.84428710937493, -30.175],\n                            [-69.95996093749997, -30.078320312500026],\n                            [-70.02680664062501, -29.324023437500017],\n                            [-69.82788085937497, -29.10322265624997],\n                            [-69.65693359374995, -28.413574218749986],\n                            [-69.17441406249998, -27.924707031250037],\n                            [-68.84633789062494, -27.153710937499994],\n                            [-68.59208984375002, -27.140039062499966],\n                            [-68.31865234374999, -26.973242187500006],\n                            [-68.59160156249999, -26.47041015624997],\n                            [-68.41450195312498, -26.153710937500023],\n                            [-68.59208984375002, -25.420019531250034],\n                            [-68.38422851562495, -25.091894531249977],\n                            [-68.56201171875, -24.74736328125003],\n                            [-68.25029296875002, -24.391992187500023],\n                            [-67.35620117187503, -24.033789062499963],\n                            [-67.00878906249994, -23.00136718750005],\n                            [-67.19487304687493, -22.821679687500037],\n                            [-66.99111328125, -22.509863281250006],\n                            [-66.71171874999999, -22.216308593749986],\n                            [-66.36518554687501, -22.113769531249957],\n                            [-66.32246093750001, -22.053125],\n                            [-66.28212890624997, -21.94746093750001],\n                            [-66.24760742187496, -21.83046875],\n                            [-66.22016601562495, -21.802539062499974],\n                            [-66.174658203125, -21.805664062499986],\n                            [-66.09858398437495, -21.83505859375002],\n                            [-66.05859375, -21.87949218750002],\n                            [-65.86015624999999, -22.019726562499983],\n                            [-65.77104492187493, -22.099609375000014],\n                            [-65.68618164062497, -22.11025390625005],\n                            [-65.51879882812497, -22.094531250000045],\n                            [-64.99262695312498, -22.109667968750017],\n                            [-64.60551757812499, -22.228808593750045],\n                            [-64.52363281250001, -22.37158203125],\n                            [-64.47773437499998, -22.485351562499986],\n                            [-64.44550781249998, -22.585351562500023],\n                            [-64.37397460937498, -22.761035156250017],\n                            [-64.32529296875, -22.82763671875],\n                            [-64.30791015624993, -22.7953125],\n                            [-64.26640625000002, -22.60332031249996],\n                            [-63.97612304687502, -22.072558593750003],\n                            [-63.92167968749993, -22.028613281250017],\n                            [-62.843359375, -21.997265625000026],\n                            [-62.62597656250003, -22.29042968749998],\n                            [-62.54155273437496, -22.349609374999957],\n                            [-62.37250976562498, -22.439160156249997],\n                            [-62.21416015624996, -22.612402343750034],\n                            [-61.798535156249955, -23.18203125],\n                            [-61.084716796875, -23.65644531250001]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Argentina\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [46.490625, 38.90668945312498],\n                        [46.1144531250001, 38.877783203125034],\n                        [45.977441406249994, 39.24389648437503],\n                        [45.76630859375004, 39.37846679687499],\n                        [45.78447265625002, 39.54560546875001],\n                        [45.456835937500074, 39.494482421875006],\n                        [45.15283203125003, 39.58266601562502],\n                        [45.03164062500005, 39.76513671874997],\n                        [44.76826171875004, 39.70351562500005],\n                        [44.28925781250004, 40.040380859375006],\n                        [43.66621093750004, 40.12636718750002],\n                        [43.56933593750003, 40.48237304687498],\n                        [43.72265624999997, 40.71953124999999],\n                        [43.43945312500003, 41.10712890625001],\n                        [44.077246093750006, 41.182519531249994],\n                        [44.81132812500002, 41.259375],\n                        [45.001367187499994, 41.29096679687498],\n                        [45.188574218750006, 41.14741210937504],\n                        [45.07050781250004, 41.075585937499966],\n                        [45.5875, 40.846923828125],\n                        [45.37890624999997, 40.67358398437506],\n                        [45.45439453125002, 40.532373046874966],\n                        [45.96464843750002, 40.233789062499966],\n                        [45.8859375000001, 40.024853515624955],\n                        [45.57978515625004, 39.9775390625],\n                        [46.202050781249994, 39.59448242187503],\n                        [46.48144531249997, 39.55517578125003],\n                        [46.36523437500003, 39.402490234374994],\n                        [46.584765625000074, 39.22368164062499],\n                        [46.400292968749994, 39.1921875],\n                        [46.490625, 38.90668945312498]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Armenia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-170.72626953125, -14.351171875],\n                        [-170.8205078125, -14.312109375],\n                        [-170.568115234375, -14.266796875000011],\n                        [-170.72626953125, -14.351171875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"American Samoa\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [69.2824218750001, -49.05888671875002],\n                            [69.16718750000004, -48.88291015624996],\n                            [69.36875, -48.89042968749998],\n                            [69.2824218750001, -49.05888671875002]\n                        ]\n                    ],\n                    [\n                        [\n                            [69.18486328125002, -49.10957031250004],\n                            [69.59277343749997, -48.97099609375005],\n                            [69.64404296875003, -49.11738281250003],\n                            [69.40507812500002, -49.18173828125],\n                            [69.5423828125, -49.25566406250005],\n                            [70.32021484375005, -49.05859374999996],\n                            [70.55546875000007, -49.201464843750024],\n                            [70.38613281250005, -49.433984374999966],\n                            [70.16582031250002, -49.34296874999998],\n                            [69.75996093750004, -49.430175781249986],\n                            [69.98642578125006, -49.58164062500003],\n                            [70.2477539062501, -49.53066406250003],\n                            [70.12431640625002, -49.70439453124999],\n                            [69.153125, -49.5296875],\n                            [68.99296875000007, -49.704980468750016],\n                            [68.81474609375002, -49.69960937499999],\n                            [68.88339843750006, -49.16494140624995],\n                            [68.76953125000003, -49.06591796875003],\n                            [69.00244140624997, -48.661230468750006],\n                            [69.13613281250005, -48.86103515625003],\n                            [69.05214843750005, -49.08193359375001],\n                            [69.18486328125002, -49.10957031250004]\n                        ]\n                    ],\n                    [\n                        [\n                            [51.83457031250006, -46.43994140625],\n                            [51.65927734375006, -46.37363281249999],\n                            [51.7418945312501, -46.32685546874997],\n                            [51.83457031250006, -46.43994140625]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Fr. S. Antarctic Lands\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-61.71606445312503, 17.037011718749994],\n                        [-61.85966796874996, 17.013330078124966],\n                        [-61.887109374999966, 17.09814453125],\n                        [-61.81728515624994, 17.168945312500057],\n                        [-61.71606445312503, 17.037011718749994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Antigua and Barb.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [147.35605468750006, -43.396972656250014],\n                            [147.30888671875007, -43.50078125000002],\n                            [147.10498046875003, -43.43115234374996],\n                            [147.28388671875004, -43.278906250000034],\n                            [147.35605468750006, -43.396972656250014]\n                        ]\n                    ],\n                    [\n                        [\n                            [145.04296875000003, -40.78671875],\n                            [145.28300781250002, -40.76992187500002],\n                            [146.31748046875006, -41.16347656250001],\n                            [146.72343750000002, -41.07802734375001],\n                            [146.84814453124997, -41.16806640624996],\n                            [146.98984375000006, -40.99238281249997],\n                            [147.45478515625004, -41.00166015624998],\n                            [147.62167968750012, -40.844726562499986],\n                            [147.87294921875005, -40.87255859374997],\n                            [147.96875, -40.779589843750045],\n                            [148.215234375, -40.85488281250002],\n                            [148.34257812500007, -42.21533203124997],\n                            [148.21367187500002, -41.97001953125],\n                            [147.92441406250006, -42.5724609375],\n                            [147.94541015625006, -43.18183593749997],\n                            [147.7858398437501, -43.22001953125002],\n                            [147.69892578125004, -43.12255859374997],\n                            [147.64794921874997, -43.02060546874999],\n                            [147.8, -42.928125],\n                            [147.57382812500006, -42.84570312499997],\n                            [147.4523437500001, -43.03339843750001],\n                            [147.29794921875006, -42.790917968749994],\n                            [147.24501953125005, -43.21591796874999],\n                            [146.99697265625005, -43.15634765625002],\n                            [147.07734375000004, -43.27587890625003],\n                            [146.87392578125, -43.6125],\n                            [146.54853515625004, -43.50888671874999],\n                            [146.04316406250004, -43.547167968749974],\n                            [145.99443359375007, -43.37607421875002],\n                            [146.20800781249997, -43.31621093749999],\n                            [145.8732421875001, -43.29238281250002],\n                            [145.48759765625002, -42.92666015625004],\n                            [145.19882812500006, -42.23085937500004],\n                            [145.46826171874997, -42.492871093750026],\n                            [145.51660156249997, -42.3544921875],\n                            [145.33105468750003, -42.14707031250002],\n                            [145.23486328124997, -42.19697265624997],\n                            [145.23818359375, -42.01962890624999],\n                            [144.76611328125003, -41.39003906249998],\n                            [144.64609375000006, -40.980859375],\n                            [144.71855468750002, -40.67226562500002],\n                            [145.04296875000003, -40.78671875]\n                        ]\n                    ],\n                    [\n                        [\n                            [148.23691406250006, -40.515136718749986],\n                            [148.18779296875007, -40.592578125000045],\n                            [148.11728515625012, -40.52148437499996],\n                            [148.23691406250006, -40.515136718749986]\n                        ]\n                    ],\n                    [\n                        [\n                            [144.784375, -40.506738281249966],\n                            [144.74804687499997, -40.589453125000034],\n                            [144.7833984375001, -40.434863281249974],\n                            [144.784375, -40.506738281249966]\n                        ]\n                    ],\n                    [\n                        [\n                            [148.32626953125006, -40.30693359375003],\n                            [148.40400390625004, -40.486523437500026],\n                            [148.02011718750012, -40.40419921874995],\n                            [148.32626953125006, -40.30693359375003]\n                        ]\n                    ],\n                    [\n                        [\n                            [148.000390625, -39.75761718750003],\n                            [148.29736328125003, -39.985742187499966],\n                            [148.31357421875012, -40.173535156250026],\n                            [148.10566406250004, -40.26210937499995],\n                            [147.76718750000012, -39.87031249999998],\n                            [148.000390625, -39.75761718750003]\n                        ]\n                    ],\n                    [\n                        [\n                            [143.92792968750004, -40.116113281249966],\n                            [143.83857421875004, -39.90410156250003],\n                            [144.00078125000007, -39.580175781250034],\n                            [144.14101562500005, -39.953808593750026],\n                            [143.92792968750004, -40.116113281249966]\n                        ]\n                    ],\n                    [\n                        [\n                            [145.31445312500003, -38.49082031249996],\n                            [145.35507812500012, -38.55703124999995],\n                            [145.12841796875003, -38.52763671875],\n                            [145.31445312500003, -38.49082031249996]\n                        ]\n                    ],\n                    [\n                        [\n                            [137.59648437500007, -35.73867187499998],\n                            [137.92890625000004, -35.72607421875],\n                            [138.12343750000005, -35.85234375],\n                            [137.67089843749997, -35.897949218750014],\n                            [137.44843750000004, -36.07480468749999],\n                            [137.20957031250012, -35.982421875],\n                            [136.7550781250001, -36.03310546875002],\n                            [136.540625, -35.89013671875003],\n                            [136.63867187499997, -35.74882812500002],\n                            [137.33408203125006, -35.59248046875004],\n                            [137.58496093749997, -35.620214843750006],\n                            [137.59648437500007, -35.73867187499998]\n                        ]\n                    ],\n                    [\n                        [\n                            [153.53876953125004, -27.436425781250037],\n                            [153.42656250000002, -27.70644531249998],\n                            [153.43544921875, -27.40537109375002],\n                            [153.53876953125004, -27.436425781250037]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.18300781250005, -26.053125],\n                            [112.96425781250005, -25.78310546875001],\n                            [112.94707031250002, -25.531542968750017],\n                            [113.18300781250005, -26.053125]\n                        ]\n                    ],\n                    [\n                        [\n                            [153.07744140625002, -25.75078125],\n                            [152.97666015625012, -25.551367187499963],\n                            [153.03808593750003, -25.193164062500003],\n                            [153.22753906249997, -25.00576171875001],\n                            [153.14375, -24.814843750000023],\n                            [153.25693359375012, -24.72890625],\n                            [153.35019531250012, -25.063085937499963],\n                            [153.07744140625002, -25.75078125]\n                        ]\n                    ],\n                    [\n                        [\n                            [151.14658203125006, -23.49082031250002],\n                            [151.24013671875, -23.529687500000037],\n                            [151.23828124999997, -23.77578125],\n                            [151.03330078125006, -23.530175781250037],\n                            [151.14658203125006, -23.49082031250002]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.44619140625005, -20.78779296875001],\n                            [115.31806640625004, -20.850585937500014],\n                            [115.43457031249997, -20.66796875000003],\n                            [115.44619140625005, -20.78779296875001]\n                        ]\n                    ],\n                    [\n                        [\n                            [149.04375, -20.29150390624997],\n                            [148.93886718750005, -20.283691406249986],\n                            [148.98105468750012, -20.153515625000026],\n                            [149.04375, -20.29150390624997]\n                        ]\n                    ],\n                    [\n                        [\n                            [146.27832031249997, -18.23125],\n                            [146.29882812499997, -18.48476562500005],\n                            [146.09882812500004, -18.251757812500003],\n                            [146.27832031249997, -18.23125]\n                        ]\n                    ],\n                    [\n                        [\n                            [139.45917968750004, -17.11455078124996],\n                            [139.49277343750006, -16.990429687499983],\n                            [139.57089843750006, -17.09443359375004],\n                            [139.45917968750004, -17.11455078124996]\n                        ]\n                    ],\n                    [\n                        [\n                            [139.50781250000003, -16.57304687499996],\n                            [139.1595703125, -16.74169921875003],\n                            [139.29296875000003, -16.467285156249986],\n                            [139.58789062499997, -16.39521484374997],\n                            [139.69775390624997, -16.514941406250017],\n                            [139.50781250000003, -16.57304687499996]\n                        ]\n                    ],\n                    [\n                        [\n                            [137.09365234375005, -15.778125],\n                            [136.94267578125002, -15.711718749999989],\n                            [137.00957031250007, -15.594824218749977],\n                            [137.09365234375005, -15.778125]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.59726562500006, -15.40195312500002],\n                            [124.52421875000002, -15.421484375],\n                            [124.51933593750002, -15.26748046874998],\n                            [124.59726562500006, -15.40195312500002]\n                        ]\n                    ],\n                    [\n                        [\n                            [125.19882812500006, -14.57949218749998],\n                            [125.0912109375, -14.59169921874998],\n                            [125.15996093750002, -14.456054687499972],\n                            [125.19882812500006, -14.57949218749998]\n                        ]\n                    ],\n                    [\n                        [\n                            [136.71464843750002, -13.803906249999983],\n                            [136.89082031250004, -13.786621093750014],\n                            [136.74531250000004, -14.072656250000023],\n                            [136.95078125000006, -14.184277343750026],\n                            [136.89433593750002, -14.293066406249977],\n                            [136.33544921875003, -14.211816406250037],\n                            [136.42470703125, -13.864843749999963],\n                            [136.6556640625, -13.675878906250006],\n                            [136.71464843750002, -13.803906249999983]\n                        ]\n                    ],\n                    [\n                        [\n                            [136.23740234375006, -13.824511718750003],\n                            [136.12265625000012, -13.816601562499983],\n                            [136.21542968750012, -13.664746093750054],\n                            [136.23740234375006, -13.824511718750003]\n                        ]\n                    ],\n                    [\n                        [\n                            [136.33867187500007, -11.602343749999989],\n                            [136.18027343750006, -11.676757812499957],\n                            [136.47929687500002, -11.465917968749991],\n                            [136.33867187500007, -11.602343749999989]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.45927734375007, -11.679296875000034],\n                            [130.60625, -11.816601562500026],\n                            [130.04326171875007, -11.787304687500011],\n                            [130.19755859375007, -11.658203125],\n                            [130.15283203124997, -11.477539062499972],\n                            [130.29492187499997, -11.33681640624998],\n                            [130.45927734375007, -11.679296875000034]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.6188476562501, -11.376074218749991],\n                            [131.02304687500006, -11.334375],\n                            [131.26826171875004, -11.18984375],\n                            [131.53857421874997, -11.436914062500037],\n                            [130.95097656250007, -11.926464843750026],\n                            [130.51191406250004, -11.617871093749955],\n                            [130.38457031250002, -11.1921875],\n                            [130.6188476562501, -11.376074218749991]\n                        ]\n                    ],\n                    [\n                        [\n                            [136.59853515625, -11.378906249999943],\n                            [136.52656250000004, -11.438867187499994],\n                            [136.78027343749997, -11.0125],\n                            [136.59853515625, -11.378906249999943]\n                        ]\n                    ],\n                    [\n                        [\n                            [132.59335937500006, -11.302832031249991],\n                            [132.48378906250005, -11.037304687499983],\n                            [132.57880859375004, -10.968847656249977],\n                            [132.59335937500006, -11.302832031249991]\n                        ]\n                    ],\n                    [\n                        [\n                            [143.17890625000004, -11.954492187499966],\n                            [143.11025390625, -12.303515625000017],\n                            [143.40156250000004, -12.639941406249989],\n                            [143.5866210937501, -13.443652343750031],\n                            [143.54843750000012, -13.74101562499996],\n                            [143.75634765625003, -14.348828124999969],\n                            [143.96181640625005, -14.462890625000028],\n                            [144.473046875, -14.231835937500023],\n                            [144.64804687500006, -14.492480468750017],\n                            [145.28769531250006, -14.943164062499989],\n                            [145.42607421875002, -16.406152343749966],\n                            [145.75478515625, -16.879492187500034],\n                            [145.91210937499997, -16.9125],\n                            [146.12587890625005, -17.63525390625],\n                            [146.03222656249997, -18.272851562500037],\n                            [146.3332031250001, -18.55371093749997],\n                            [146.38339843750006, -18.97705078124997],\n                            [147.13876953125006, -19.39316406250002],\n                            [147.41855468750012, -19.378125],\n                            [147.7423828125001, -19.770117187499977],\n                            [148.759375, -20.28955078125003],\n                            [148.88476562499997, -20.480859375],\n                            [148.72998046874997, -20.4677734375],\n                            [148.68369140625012, -20.58017578124999],\n                            [149.20488281250007, -21.125097656249977],\n                            [149.45410156249997, -21.57871093750002],\n                            [149.70390625000002, -22.440527343750006],\n                            [149.82246093750004, -22.389843749999983],\n                            [149.97441406250007, -22.55068359374998],\n                            [149.94189453125003, -22.30810546875003],\n                            [150.07617187500003, -22.16445312499998],\n                            [150.54130859375002, -22.55908203125],\n                            [150.56855468750004, -22.38398437500004],\n                            [150.67246093750012, -22.418164062499983],\n                            [150.84316406250005, -23.4580078125],\n                            [151.15380859375003, -23.784082031249994],\n                            [151.83164062500006, -24.12294921875001],\n                            [152.12988281250003, -24.59755859374998],\n                            [152.45634765625007, -24.802441406249983],\n                            [152.65429687499997, -25.201953125000017],\n                            [152.91347656250005, -25.432128906250014],\n                            [152.98496093750012, -25.816210937500003],\n                            [153.16494140625, -25.964160156250045],\n                            [153.11679687500006, -27.194433593750034],\n                            [153.57568359375003, -28.24052734374999],\n                            [153.6168945312501, -28.673046875],\n                            [153.03056640625002, -30.563378906249994],\n                            [152.94394531250012, -31.43486328124999],\n                            [152.5592773437501, -32.045703125],\n                            [152.4704101562501, -32.439062500000034],\n                            [152.13652343750002, -32.678125],\n                            [152.1642578125001, -32.75742187499996],\n                            [151.812890625, -32.90107421875001],\n                            [151.29208984375012, -33.580957031249966],\n                            [151.28027343750003, -33.92666015625005],\n                            [151.12480468750007, -34.00527343749998],\n                            [151.23154296875006, -34.0296875],\n                            [150.8712890625001, -34.49912109374996],\n                            [150.80458984375, -35.01289062500001],\n                            [150.19531249999997, -35.83359374999996],\n                            [149.93271484375012, -37.528515625000026],\n                            [149.480859375, -37.77119140625],\n                            [147.87675781250002, -37.93417968749998],\n                            [146.8568359375, -38.663476562499966],\n                            [146.21748046875004, -38.72744140625004],\n                            [146.33662109375004, -38.89423828125],\n                            [146.46660156250007, -38.84033203125003],\n                            [146.40000000000012, -39.14550781250003],\n                            [146.1583984375001, -38.86572265624996],\n                            [145.93535156250002, -38.90175781250002],\n                            [145.79082031250007, -38.66699218749997],\n                            [145.39726562500002, -38.53535156249998],\n                            [145.54218750000004, -38.39384765625002],\n                            [145.4757812500001, -38.24375],\n                            [145.29277343750002, -38.237597656249974],\n                            [144.95957031250012, -38.500781250000045],\n                            [144.71777343749997, -38.34033203125004],\n                            [144.91142578125007, -38.34404296874999],\n                            [145.11992187500007, -38.091308593750014],\n                            [144.89130859375004, -37.899804687499994],\n                            [144.39550781250003, -38.13691406249998],\n                            [144.6652343750001, -38.20996093750003],\n                            [143.53896484375005, -38.82089843749998],\n                            [142.45585937500002, -38.38632812499999],\n                            [141.725, -38.27138671875002],\n                            [141.5939453125001, -38.38779296875002],\n                            [141.42421875, -38.36347656250004],\n                            [141.0109375000001, -38.07695312500003],\n                            [140.39042968750007, -37.89667968749998],\n                            [139.78427734375012, -37.24580078124998],\n                            [139.85732421875, -36.662109375],\n                            [139.72900390625003, -36.37138671875002],\n                            [138.9689453125001, -35.58076171874997],\n                            [139.17802734375007, -35.52304687500002],\n                            [139.289453125, -35.61132812499997],\n                            [139.28251953125002, -35.375390624999966],\n                            [138.521875, -35.6423828125],\n                            [138.184375, -35.612695312499994],\n                            [138.5111328125, -35.02441406249996],\n                            [138.48994140625004, -34.76357421875002],\n                            [138.0892578125, -34.16982421875002],\n                            [137.69169921875002, -35.14296875000004],\n                            [136.88359375000007, -35.23974609375004],\n                            [137.01425781250012, -34.91582031250003],\n                            [137.39101562500005, -34.91328124999997],\n                            [137.49384765625004, -34.16113281250003],\n                            [137.9318359375001, -33.57910156250003],\n                            [137.85234375000007, -33.20078124999996],\n                            [137.99257812500005, -33.094238281250014],\n                            [137.78320312500003, -32.578125],\n                            [137.79091796875, -32.82324218749996],\n                            [137.44228515625, -33.1935546875],\n                            [137.23730468750003, -33.62949218749999],\n                            [136.43066406249997, -34.02998046875004],\n                            [135.891015625, -34.660937499999974],\n                            [135.96972656249997, -34.98183593749998],\n                            [135.7923828125, -34.863281249999986],\n                            [135.64755859375006, -34.93964843750001],\n                            [135.12304687499997, -34.58574218750003],\n                            [135.21679687499997, -34.48730468749996],\n                            [135.45, -34.58105468749996],\n                            [135.21894531250004, -33.959765625000045],\n                            [134.88876953125012, -33.62636718749998],\n                            [134.79101562499997, -33.32832031250001],\n                            [134.60771484375002, -33.19013671875001],\n                            [134.30126953124997, -33.16503906249996],\n                            [134.17353515625004, -32.979101562500006],\n                            [134.10039062500007, -32.748632812500034],\n                            [134.22714843750006, -32.73056640624999],\n                            [134.23417968750007, -32.54853515625004],\n                            [133.66533203125007, -32.207226562500054],\n                            [133.21210937500004, -32.18378906249998],\n                            [132.75742187500012, -31.95625],\n                            [132.21464843750002, -32.00712890624996],\n                            [131.14365234375006, -31.49570312500005],\n                            [130.78300781250002, -31.604003906249986],\n                            [129.1876953125001, -31.659960937500017],\n                            [127.31982421874997, -32.2640625],\n                            [125.91718750000004, -32.296972656250034],\n                            [124.75878906250003, -32.882714843749994],\n                            [124.24375, -33.01523437499999],\n                            [123.50683593749997, -33.916210937500054],\n                            [122.15097656250006, -33.99179687499999],\n                            [122.06113281250006, -33.874414062499966],\n                            [121.40507812500007, -33.826757812500034],\n                            [119.85410156250012, -33.97470703124998],\n                            [119.45058593750005, -34.368261718750034],\n                            [118.89531250000007, -34.47988281250004],\n                            [118.13554687500002, -34.98662109374999],\n                            [117.58193359375005, -35.09775390624998],\n                            [116.51718750000012, -34.98789062499998],\n                            [115.98671875000005, -34.795019531250034],\n                            [115.56503906250012, -34.42578125000003],\n                            [115.00878906250003, -34.25585937499997],\n                            [114.9938476562501, -33.51533203125],\n                            [115.3587890625, -33.63994140624999],\n                            [115.68300781250005, -33.19287109375003],\n                            [115.6984375000001, -31.694531250000054],\n                            [115.07792968750007, -30.560449218750023],\n                            [114.85683593750005, -29.14296875],\n                            [114.16513671875012, -28.08066406250002],\n                            [114.028125, -27.34726562499999],\n                            [113.18476562500004, -26.182226562499963],\n                            [113.32324218749997, -26.243847656249997],\n                            [113.35605468750012, -26.080468750000023],\n                            [113.58164062500006, -26.558105468749986],\n                            [113.73369140625002, -26.59511718749998],\n                            [113.83642578125003, -26.50058593749999],\n                            [113.85283203125007, -26.33212890625005],\n                            [113.39531250000002, -25.71328125],\n                            [113.4513671875001, -25.599121093750014],\n                            [113.7130859375001, -25.83076171875004],\n                            [113.72373046875006, -26.129785156250037],\n                            [113.85390625, -26.01445312499999],\n                            [113.99199218750007, -26.32148437500001],\n                            [114.09033203124997, -26.393652343749963],\n                            [114.21572265625, -26.289453124999966],\n                            [114.2142578125, -25.851562500000014],\n                            [113.41767578125004, -24.435644531250034],\n                            [113.48984375000012, -23.869628906250014],\n                            [113.7570312500001, -23.418164062500054],\n                            [113.79511718750004, -22.91455078125003],\n                            [113.68281250000004, -22.637792968749963],\n                            [114.02285156250005, -21.881445312499977],\n                            [114.12392578125005, -21.828613281249957],\n                            [114.14160156250003, -22.483105468749983],\n                            [114.37773437500007, -22.341503906249997],\n                            [114.70927734375002, -21.82343749999997],\n                            [115.45615234375012, -21.49169921874997],\n                            [116.0109375000001, -21.030371093749963],\n                            [116.7067382812501, -20.653808593749986],\n                            [117.40625, -20.72119140625003],\n                            [118.19921875000003, -20.37519531249997],\n                            [118.75146484374997, -20.261914062499983],\n                            [119.10449218749997, -19.995312500000026],\n                            [119.58593750000003, -20.03828125],\n                            [120.99794921875, -19.604394531249966],\n                            [121.33769531250002, -19.31992187500002],\n                            [121.83378906250002, -18.477050781249986],\n                            [122.34541015625004, -18.11191406250002],\n                            [122.14746093749997, -17.54902343750001],\n                            [122.2609375000001, -17.135742187500014],\n                            [122.72041015625004, -16.78769531249999],\n                            [122.97070312499997, -16.436816406250003],\n                            [123.56308593750006, -17.520898437499966],\n                            [123.59355468750007, -17.03037109375005],\n                            [123.83105468750003, -17.120800781249997],\n                            [123.8744140625, -16.918652343750026],\n                            [123.4904296875001, -16.49072265624997],\n                            [123.62597656249997, -16.416308593750003],\n                            [123.60703125000006, -16.224023437499994],\n                            [123.72890625, -16.192480468749963],\n                            [123.85917968750007, -16.38232421875],\n                            [124.04443359374997, -16.264941406249974],\n                            [124.30039062500006, -16.388281249999977],\n                            [124.77197265624997, -16.40263671874996],\n                            [124.40488281250006, -16.298925781249977],\n                            [124.41640625, -16.133496093750026],\n                            [124.5768554687501, -16.11367187499998],\n                            [124.64853515625012, -15.870214843750034],\n                            [124.50429687500005, -15.972460937499989],\n                            [124.38164062500002, -15.758203125000037],\n                            [124.43955078125012, -15.493554687500037],\n                            [124.56162109375012, -15.496289062499969],\n                            [124.69257812500004, -15.273632812499997],\n                            [125.06298828125003, -15.44228515624998],\n                            [125.0729492187501, -15.306738281249991],\n                            [124.90917968750003, -15.310058593749957],\n                            [124.83906250000004, -15.160742187500006],\n                            [125.03818359375012, -15.004101562499969],\n                            [125.35566406250004, -15.119824218750011],\n                            [125.17871093749997, -14.714746093749994],\n                            [125.57978515625004, -14.483203124999989],\n                            [125.62773437500002, -14.256640625000017],\n                            [125.70458984374997, -14.29140625],\n                            [125.66162109375003, -14.529492187500011],\n                            [125.81953125000004, -14.469140624999966],\n                            [125.890625, -14.61796875],\n                            [126.0207031250001, -14.49453125],\n                            [126.0539062500001, -13.977246093750026],\n                            [126.1842773437501, -14.00205078125002],\n                            [126.25849609375004, -14.163574218749972],\n                            [126.403125, -14.018945312499994],\n                            [126.5697265625, -14.160937499999974],\n                            [126.7806640625, -13.955175781249977],\n                            [126.77558593750004, -13.788476562500037],\n                            [126.90322265625, -13.744140624999972],\n                            [127.45761718750006, -14.031445312499969],\n                            [128.18046875000007, -14.711621093749983],\n                            [128.06943359375012, -15.329296874999969],\n                            [128.15546875000004, -15.225585937499972],\n                            [128.25468750000002, -15.298535156250011],\n                            [128.175, -15.043164062500026],\n                            [128.57578125000006, -14.774511718750006],\n                            [129.05820312500012, -14.884375],\n                            [129.21582031249997, -15.160253906249991],\n                            [129.26757812500003, -14.871484375000051],\n                            [129.63476562499997, -15.139746093749991],\n                            [129.637109375, -14.850976562500037],\n                            [129.84873046875012, -14.828906249999989],\n                            [129.60468750000004, -14.647070312499977],\n                            [129.69794921875004, -14.557421875000017],\n                            [129.37871093750002, -14.39248046874998],\n                            [129.70986328125, -13.979980468749972],\n                            [129.83886718749997, -13.572949218749997],\n                            [130.25976562500003, -13.30224609375],\n                            [130.1349609375001, -13.145507812499957],\n                            [130.1681640625001, -12.957421875],\n                            [130.39990234374997, -12.68789062499999],\n                            [130.61748046875007, -12.646875],\n                            [130.62265625000006, -12.43105468749998],\n                            [130.8673828125001, -12.557812499999955],\n                            [130.87382812500007, -12.367187500000028],\n                            [131.29160156250006, -12.067871093749972],\n                            [131.43828125000002, -12.27695312500002],\n                            [132.06406250000006, -12.28076171875],\n                            [132.25322265625007, -12.186035156249972],\n                            [132.41103515625, -12.295117187499997],\n                            [132.51054687500002, -12.134863281250034],\n                            [132.71279296875, -12.1234375],\n                            [132.63046875000012, -12.035156249999972],\n                            [132.67421875000005, -11.649023437499991],\n                            [132.47519531250006, -11.491503906249974],\n                            [132.07285156250006, -11.474707031250006],\n                            [131.82246093750004, -11.302441406249997],\n                            [131.96152343750006, -11.180859375000011],\n                            [132.15546875000004, -11.311132812499991],\n                            [132.33398437499997, -11.223535156249994],\n                            [132.6828125000001, -11.505566406249997],\n                            [132.96103515625012, -11.407324218749963],\n                            [133.18525390625004, -11.705664062499991],\n                            [133.90419921875, -11.832031249999972],\n                            [134.4173828125, -12.052734375],\n                            [134.73027343750002, -11.984375],\n                            [135.02968750000005, -12.19375],\n                            [135.2179687500001, -12.221679687499957],\n                            [135.92246093750012, -11.825781250000034],\n                            [135.70439453125007, -12.209863281250037],\n                            [136.00849609375004, -12.19140625],\n                            [136.08183593750007, -12.422460937500006],\n                            [136.26064453125, -12.433789062499997],\n                            [136.32851562500005, -12.305566406249994],\n                            [136.24990234375, -12.173046875],\n                            [136.44335937499997, -11.951464843749974],\n                            [136.7194335937501, -12.226464843749952],\n                            [136.89746093749997, -12.243554687499966],\n                            [136.94746093750004, -12.34990234374996],\n                            [136.53701171875, -12.784277343749991],\n                            [136.59433593750012, -13.003808593750051],\n                            [136.46103515625006, -13.225195312500034],\n                            [136.29414062500004, -13.137988281250031],\n                            [135.92734375000012, -13.304296874999977],\n                            [135.95449218750005, -13.934863281250017],\n                            [135.40517578125005, -14.758203124999966],\n                            [135.4533203125001, -14.923144531250003],\n                            [136.20537109375002, -15.403417968749963],\n                            [136.29140625000005, -15.570117187500003],\n                            [136.70488281250007, -15.685253906250011],\n                            [136.78466796874997, -15.89423828125004],\n                            [137.00214843750004, -15.878320312499994],\n                            [137.70371093750006, -16.233007812499963],\n                            [138.24501953125005, -16.718359374999977],\n                            [139.00986328125006, -16.899316406249994],\n                            [139.2484375, -17.328613281249957],\n                            [140.03583984375004, -17.702636718749957],\n                            [140.51113281250005, -17.62451171875003],\n                            [140.83046875, -17.414453125000037],\n                            [141.29140625, -16.46347656250002],\n                            [141.62548828124997, -15.056640625000014],\n                            [141.52294921875003, -14.470117187499994],\n                            [141.59433593750006, -14.152832031250014],\n                            [141.47255859375, -13.797558593750011],\n                            [141.64541015625, -13.259082031250003],\n                            [141.61357421875002, -12.943457031250006],\n                            [141.92978515625006, -12.73984375],\n                            [141.67773437500003, -12.491406250000011],\n                            [141.68857421875012, -12.351074218750028],\n                            [141.87050781250005, -11.9755859375],\n                            [141.96113281250004, -12.054296874999963],\n                            [142.168359375, -10.946582031249974],\n                            [142.45644531250005, -10.707324218749989],\n                            [142.60507812500012, -10.748242187499983],\n                            [142.55273437500003, -10.874414062500023],\n                            [142.7796875, -11.115332031249977],\n                            [142.87255859374997, -11.821386718750034],\n                            [143.17890625000004, -11.954492187499966]\n                        ]\n                    ],\n                    [\n                        [\n                            [142.2748046875, -10.704785156250011],\n                            [142.19140624999997, -10.762011718750031],\n                            [142.1310546875001, -10.640625],\n                            [142.19794921875004, -10.59199218750004],\n                            [142.2748046875, -10.704785156250011]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Australia\", \"childNum\": 30 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [16.953125, 48.598828125],\n                        [16.86542968750001, 48.3869140625],\n                        [17.147363281250023, 48.00595703125],\n                        [17.06660156250001, 47.707568359374996],\n                        [16.421289062500023, 47.674462890624994],\n                        [16.676562500000017, 47.536035156249994],\n                        [16.44287109375, 47.39951171875],\n                        [16.453417968750017, 47.006787109375],\n                        [16.093066406250017, 46.86328125],\n                        [15.957617187500006, 46.677636718749994],\n                        [14.893261718750011, 46.605908203125],\n                        [14.5498046875, 46.399707031249996],\n                        [13.7, 46.520263671875],\n                        [13.490039062500017, 46.555566406249994],\n                        [13.3515625, 46.557910156249996],\n                        [13.16875, 46.57265625],\n                        [12.479199218750011, 46.672509765624994],\n                        [12.38828125, 46.70263671875],\n                        [12.330078125, 46.759814453124996],\n                        [12.267968750000023, 46.835888671875],\n                        [12.154101562500017, 46.93525390625],\n                        [12.130761718750023, 46.98476562499999],\n                        [12.16552734375, 47.028173828125],\n                        [12.201269531250006, 47.060888671875],\n                        [12.197167968750023, 47.075],\n                        [12.16943359375, 47.08212890625],\n                        [11.775683593750017, 46.986083984375],\n                        [11.527539062500011, 46.997412109375],\n                        [11.433203125, 46.983056640624994],\n                        [11.244433593750017, 46.97568359375],\n                        [11.133886718750006, 46.936181640624994],\n                        [11.0634765625, 46.859130859375],\n                        [11.025097656250011, 46.79697265625],\n                        [10.993261718750006, 46.777001953124994],\n                        [10.92734375, 46.769482421875],\n                        [10.828906250000017, 46.775244140625],\n                        [10.759765625, 46.793310546875],\n                        [10.689257812500017, 46.84638671875],\n                        [10.579785156250011, 46.8537109375],\n                        [10.479394531250023, 46.855126953124994],\n                        [10.452832031250011, 46.86494140625],\n                        [10.45458984375, 46.8994140625],\n                        [10.414941406250023, 46.964404296874996],\n                        [10.349414062500017, 46.98476562499999],\n                        [10.133496093750011, 46.851513671875],\n                        [9.580273437500011, 47.057373046875],\n                        [9.527539062500011, 47.270751953125],\n                        [9.625878906250023, 47.467041015625],\n                        [9.524023437500006, 47.52421875],\n                        [9.748925781250023, 47.575537109375],\n                        [9.839160156250017, 47.552294921874996],\n                        [9.971582031250023, 47.505322265625],\n                        [10.034082031250023, 47.473583984375],\n                        [10.059863281250017, 47.449072265625],\n                        [10.066308593750023, 47.393359375],\n                        [10.200292968750006, 47.363427734374994],\n                        [10.183007812500023, 47.27880859375],\n                        [10.369140625, 47.366064453125],\n                        [10.40390625, 47.4169921875],\n                        [10.439453125, 47.5515625],\n                        [10.482812500000023, 47.541796875],\n                        [10.65869140625, 47.547216796875],\n                        [10.741601562500023, 47.52412109375],\n                        [10.873046875, 47.52021484375],\n                        [11.0419921875, 47.393115234374996],\n                        [12.185644531250006, 47.61953125],\n                        [12.203808593750011, 47.646728515625],\n                        [12.196875, 47.70908203125],\n                        [12.209277343750017, 47.71826171875],\n                        [12.268359375000017, 47.702734375],\n                        [12.353540736607165, 47.70264787946429],\n                        [12.492553013392856, 47.68551897321428],\n                        [12.685839843750017, 47.669335937499994],\n                        [12.771386718750023, 47.639404296875],\n                        [12.796191406250017, 47.60703125],\n                        [12.781152343750023, 47.5904296875],\n                        [12.7828125, 47.56416015625],\n                        [12.809375, 47.5421875],\n                        [12.87890625, 47.5064453125],\n                        [12.968066406250017, 47.47568359375],\n                        [13.014355468750011, 47.478076171874996],\n                        [13.031542968750017, 47.5080078125],\n                        [13.047949218750006, 47.579150390624996],\n                        [13.054101562500023, 47.655126953125],\n                        [12.897656250000011, 47.721875],\n                        [12.953515625000023, 47.890625],\n                        [12.760351562500006, 48.106982421874996],\n                        [13.215234375000023, 48.301904296874994],\n                        [13.322851562500006, 48.33125],\n                        [13.409375, 48.394140625],\n                        [13.459863281250023, 48.56455078125],\n                        [13.4716796875, 48.571826171874996],\n                        [13.486621093750017, 48.581835937499996],\n                        [13.636623883928596, 48.580904017857144],\n                        [13.785351562500011, 48.587451171874996],\n                        [13.798828125, 48.6216796875],\n                        [13.802929687500011, 48.747509765625],\n                        [13.814746093750017, 48.766943359375],\n                        [14.049121093750017, 48.602490234375],\n                        [14.691308593750023, 48.59921875],\n                        [15.066796875000023, 48.997851562499996],\n                        [16.057226562500006, 48.754785156249994],\n                        [16.543554687500006, 48.796240234375],\n                        [16.953125, 48.598828125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Austria\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [46.1144531250001, 38.877783203125034],\n                            [45.4796875000001, 39.00625],\n                            [44.81718750000002, 39.65043945312496],\n                            [44.76826171875004, 39.70351562500005],\n                            [45.03164062500005, 39.76513671874997],\n                            [45.15283203125003, 39.58266601562502],\n                            [45.456835937500074, 39.494482421875006],\n                            [45.78447265625002, 39.54560546875001],\n                            [45.76630859375004, 39.37846679687499],\n                            [45.977441406249994, 39.24389648437503],\n                            [46.1144531250001, 38.877783203125034]\n                        ]\n                    ],\n                    [\n                        [\n                            [48.572851562500006, 41.84448242187503],\n                            [49.45673828125004, 40.79985351562502],\n                            [49.77597656250006, 40.583984375],\n                            [50.18251953125005, 40.50478515625002],\n                            [50.3659179687501, 40.279492187499955],\n                            [49.91884765625005, 40.31640625000003],\n                            [49.55117187499999, 40.19414062499999],\n                            [49.3244140625001, 39.60834960937501],\n                            [49.36279296875003, 39.349560546874955],\n                            [49.16533203125002, 39.03027343750003],\n                            [49.013476562500074, 39.13398437500001],\n                            [48.85449218750003, 38.83881835937501],\n                            [48.86875, 38.43549804687498],\n                            [48.59267578125005, 38.41108398437498],\n                            [47.99648437499999, 38.85375976562503],\n                            [48.292089843750006, 39.01884765624999],\n                            [48.10439453125005, 39.241113281249994],\n                            [48.322167968749994, 39.39907226562502],\n                            [47.995898437500074, 39.683935546875034],\n                            [46.490625, 38.90668945312498],\n                            [46.400292968749994, 39.1921875],\n                            [46.584765625000074, 39.22368164062499],\n                            [46.36523437500003, 39.402490234374994],\n                            [46.48144531249997, 39.55517578125003],\n                            [46.202050781249994, 39.59448242187503],\n                            [45.57978515625004, 39.9775390625],\n                            [45.8859375000001, 40.024853515624955],\n                            [45.96464843750002, 40.233789062499966],\n                            [45.45439453125002, 40.532373046874966],\n                            [45.37890624999997, 40.67358398437506],\n                            [45.5875, 40.846923828125],\n                            [45.07050781250004, 41.075585937499966],\n                            [45.188574218750006, 41.14741210937504],\n                            [45.001367187499994, 41.29096679687498],\n                            [45.2171875, 41.423193359375006],\n                            [45.28095703125004, 41.449560546875034],\n                            [46.086523437500006, 41.183837890625],\n                            [46.43095703125002, 41.077050781249994],\n                            [46.534375, 41.08857421875004],\n                            [46.62636718750005, 41.15966796875006],\n                            [46.66240234375002, 41.24550781250002],\n                            [46.67255859375004, 41.28681640625001],\n                            [46.61894531250002, 41.34375],\n                            [46.30546875000002, 41.507714843749994],\n                            [46.18427734375004, 41.70214843749997],\n                            [46.42988281250004, 41.890966796875006],\n                            [46.74931640625002, 41.812597656250006],\n                            [47.31767578125002, 41.28242187500001],\n                            [47.79101562499997, 41.19926757812502],\n                            [48.572851562500006, 41.84448242187503]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Azerbaijan\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [30.553613281250023, -2.400097656250011],\n                        [30.53369140625, -2.42626953125],\n                        [30.441992187500006, -2.613476562500011],\n                        [30.424218750000023, -2.6416015625],\n                        [30.47333984375001, -2.6943359375],\n                        [30.42402343750001, -2.824023437500003],\n                        [30.433496093750023, -2.87451171875],\n                        [30.515039062500023, -2.917578125],\n                        [30.604296875000017, -2.935253906250011],\n                        [30.70947265625, -2.977246093750011],\n                        [30.7802734375, -2.98486328125],\n                        [30.811132812500006, -3.116406250000011],\n                        [30.79023437500001, -3.274609375000011],\n                        [30.4, -3.65390625],\n                        [29.947265625, -4.307324218750011],\n                        [29.7177734375, -4.455859375],\n                        [29.403222656250023, -4.449316406250006],\n                        [29.211816406250023, -3.833789062500003],\n                        [29.224414062500017, -3.053515625],\n                        [29.01435546875001, -2.72021484375],\n                        [29.10205078125, -2.595703125],\n                        [29.390234375, -2.80859375],\n                        [29.698046875000017, -2.794726562500003],\n                        [29.8681640625, -2.71640625],\n                        [29.93017578125, -2.339550781250011],\n                        [30.117285156250006, -2.416601562500006],\n                        [30.408496093750017, -2.31298828125],\n                        [30.553613281250023, -2.400097656250011]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Burundi\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [5.693554687500011, 50.774755859375006],\n                        [5.993945312500017, 50.75043945312504],\n                        [6.340917968750006, 50.451757812500034],\n                        [6.116503906250045, 50.120996093749966],\n                        [6.08906250000004, 50.15458984374996],\n                        [6.054785156249977, 50.154296875],\n                        [5.976269531250068, 50.167187499999955],\n                        [5.866894531250068, 50.08281250000002],\n                        [5.817382812500028, 50.01269531250003],\n                        [5.7880859375, 49.96123046875002],\n                        [5.744042968749994, 49.91962890624998],\n                        [5.789746093749983, 49.53828125000001],\n                        [5.50732421875, 49.51088867187502],\n                        [4.867578125000051, 49.78813476562502],\n                        [4.818652343750045, 50.153173828125034],\n                        [4.545019531250063, 49.96025390624999],\n                        [4.149316406250023, 49.971582031249994],\n                        [4.174609375000017, 50.24648437500005],\n                        [3.689355468750023, 50.30605468750002],\n                        [3.595410156250068, 50.47734374999999],\n                        [3.27333984375008, 50.53154296875002],\n                        [3.10683593750008, 50.779443359374994],\n                        [2.759375, 50.750634765624994],\n                        [2.52490234375, 51.097119140624955],\n                        [3.35009765625, 51.37768554687503],\n                        [3.43251953125008, 51.24575195312505],\n                        [3.902050781250011, 51.20766601562502],\n                        [4.226171875000034, 51.38647460937503],\n                        [5.03095703125004, 51.46909179687498],\n                        [5.214160156250045, 51.278955078124966],\n                        [5.796484375000034, 51.153076171875],\n                        [5.693554687500011, 50.774755859375006]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Belgium\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [3.595410156250011, 11.6962890625],\n                        [3.553906250000011, 11.631884765624989],\n                        [3.490527343750017, 11.49921875],\n                        [3.48779296875, 11.395410156249994],\n                        [3.638867187500011, 11.176855468749991],\n                        [3.65625, 11.154589843749989],\n                        [3.6953125, 11.1203125],\n                        [3.71640625, 11.07958984375],\n                        [3.7568359375, 10.76875],\n                        [3.83447265625, 10.607421875],\n                        [3.771777343750017, 10.417626953124994],\n                        [3.646582031250006, 10.408984374999989],\n                        [3.60205078125, 10.004541015624994],\n                        [3.3251953125, 9.778466796874994],\n                        [3.044921875, 9.083837890624991],\n                        [2.774804687500023, 9.048535156249997],\n                        [2.703125, 8.371826171875],\n                        [2.68603515625, 7.873730468749997],\n                        [2.719335937500006, 7.616259765624989],\n                        [2.7509765625, 7.541894531249994],\n                        [2.78515625, 7.476855468749989],\n                        [2.783984375000017, 7.443408203124989],\n                        [2.765820312500011, 7.422509765624994],\n                        [2.75048828125, 7.395068359374989],\n                        [2.756738281250023, 7.067919921874989],\n                        [2.721386718750011, 6.980273437499989],\n                        [2.731738281250017, 6.852832031249989],\n                        [2.7529296875, 6.771630859374994],\n                        [2.774609375000011, 6.711718749999989],\n                        [2.753710937500017, 6.661767578124994],\n                        [2.735644531250017, 6.595703125],\n                        [2.706445312500023, 6.369238281249991],\n                        [1.62265625, 6.216796875],\n                        [1.777929687500006, 6.294628906249997],\n                        [1.530957031250011, 6.992431640625],\n                        [1.624707031250011, 6.997314453125],\n                        [1.600195312500006, 9.050048828125],\n                        [1.3857421875, 9.361669921874991],\n                        [1.330078125, 9.996972656249994],\n                        [0.763378906250011, 10.386669921874997],\n                        [0.900488281250006, 10.993261718749991],\n                        [1.4267578125, 11.447119140624991],\n                        [1.980371093750023, 11.418408203124997],\n                        [2.38916015625, 11.897070312499991],\n                        [2.366015625000017, 12.221923828125],\n                        [2.805273437500006, 12.383837890624989],\n                        [3.595410156250011, 11.6962890625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Benin\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [0.217480468750011, 14.911474609374991],\n                        [0.163867187500017, 14.497216796874994],\n                        [0.382519531250011, 14.245800781249997],\n                        [0.42919921875, 13.972119140624997],\n                        [0.6181640625, 13.703417968750003],\n                        [1.201171875, 13.357519531249991],\n                        [0.988476562500011, 13.36484375],\n                        [0.9873046875, 13.041894531249994],\n                        [1.56494140625, 12.635400390624994],\n                        [2.104589843750006, 12.701269531249991],\n                        [2.226269531250011, 12.466064453125],\n                        [2.072949218750011, 12.309375],\n                        [2.38916015625, 11.897070312499991],\n                        [1.980371093750023, 11.418408203124997],\n                        [1.4267578125, 11.447119140624991],\n                        [0.900488281250006, 10.993261718749991],\n                        [0.49267578125, 10.954980468749994],\n                        [-0.068603515625, 11.115625],\n                        [-0.299462890624994, 11.166894531249994],\n                        [-0.627148437499983, 10.927392578124994],\n                        [-1.04248046875, 11.010058593749989],\n                        [-2.829931640624977, 10.998388671874991],\n                        [-2.914892578124977, 10.592333984374989],\n                        [-2.791162109374994, 10.432421874999989],\n                        [-2.780517578125, 9.745849609375],\n                        [-2.765966796874977, 9.658056640624991],\n                        [-2.706201171874994, 9.533935546875],\n                        [-2.695849609374989, 9.481347656249994],\n                        [-2.7171875, 9.457128906249991],\n                        [-2.7666015625, 9.424707031249994],\n                        [-2.816748046874977, 9.425830078124989],\n                        [-2.875146484374994, 9.500927734374997],\n                        [-2.90087890625, 9.534619140624997],\n                        [-2.948144531249994, 9.610742187499994],\n                        [-2.98828125, 9.687353515624991],\n                        [-3.042626953124994, 9.720898437499997],\n                        [-3.095800781249977, 9.752099609374994],\n                        [-3.160693359374989, 9.849169921874989],\n                        [-3.223535156249994, 9.895458984374997],\n                        [-3.289697265624994, 9.882226562499994],\n                        [-3.581152343749977, 9.92431640625],\n                        [-3.790625, 9.9171875],\n                        [-4.18115234375, 9.78173828125],\n                        [-4.267187499999977, 9.743261718749991],\n                        [-4.332226562499983, 9.645703125],\n                        [-4.406201171874983, 9.647998046874989],\n                        [-4.526611328125, 9.723486328124991],\n                        [-4.625830078124977, 9.713574218749997],\n                        [-4.721777343749977, 9.756542968749997],\n                        [-5.262304687499977, 10.319677734374991],\n                        [-5.523535156249977, 10.426025390625],\n                        [-5.490478515625, 11.042382812499994],\n                        [-5.250244140625, 11.375781249999989],\n                        [-5.288134765624989, 11.827929687499989],\n                        [-4.699316406249977, 12.076171875],\n                        [-4.4287109375, 12.337597656249997],\n                        [-4.480615234374994, 12.672216796874991],\n                        [-4.227099609374989, 12.793701171875],\n                        [-4.328710937499977, 13.119042968749994],\n                        [-4.151025390624994, 13.306201171875003],\n                        [-3.947314453124989, 13.402197265624991],\n                        [-3.527636718749989, 13.182714843749991],\n                        [-3.3017578125, 13.28076171875],\n                        [-3.248632812499977, 13.658349609374994],\n                        [-2.950830078124994, 13.6484375],\n                        [-2.873925781249994, 13.950732421875003],\n                        [-2.586718749999989, 14.227587890625003],\n                        [-2.113232421874983, 14.16845703125],\n                        [-1.97304687499999, 14.45654296875],\n                        [-1.049560546875, 14.81953125],\n                        [-0.760449218749983, 15.047753906249994],\n                        [-0.235888671874989, 15.059423828124991],\n                        [0.217480468750011, 14.911474609374991]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Burkina Faso\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [91.94921875000003, 21.50805664062503],\n                            [91.85947265625012, 21.532958984375057],\n                            [91.90771484374997, 21.722949218750017],\n                            [91.94921875000003, 21.50805664062503]\n                        ]\n                    ],\n                    [\n                        [\n                            [91.87382812500002, 21.832128906249977],\n                            [91.8375976562501, 21.750244140625],\n                            [91.85068359375012, 21.927050781250045],\n                            [91.87382812500002, 21.832128906249977]\n                        ]\n                    ],\n                    [\n                        [\n                            [91.15078125000005, 22.175195312499966],\n                            [91.04472656250002, 22.10517578125001],\n                            [91.0794921875, 22.519726562499983],\n                            [91.15078125000005, 22.175195312499966]\n                        ]\n                    ],\n                    [\n                        [\n                            [91.55673828125006, 22.38222656250005],\n                            [91.41132812500004, 22.475683593750006],\n                            [91.45605468749997, 22.61650390624999],\n                            [91.55673828125006, 22.38222656250005]\n                        ]\n                    ],\n                    [\n                        [\n                            [90.77763671875007, 22.089306640624983],\n                            [90.51503906250005, 22.06513671875001],\n                            [90.68046875000007, 22.327490234375006],\n                            [90.50292968749997, 22.835351562499994],\n                            [90.59648437500002, 22.863525390625057],\n                            [90.86816406250003, 22.48486328125],\n                            [90.77763671875007, 22.089306640624983]\n                        ]\n                    ],\n                    [\n                        [\n                            [88.94072265625002, 26.24536132812497],\n                            [88.97041015625004, 26.250878906250023],\n                            [88.95195312500002, 26.412109375],\n                            [89.01865234375012, 26.410253906249977],\n                            [89.28925781250004, 26.03759765625],\n                            [89.54990234375006, 26.005273437499994],\n                            [89.57275390625003, 26.13232421875003],\n                            [89.67089843750003, 26.21381835937504],\n                            [89.8229492187501, 25.94140625000003],\n                            [89.82490234375004, 25.56015625],\n                            [89.80087890625012, 25.33613281250001],\n                            [89.81406250000006, 25.305371093749955],\n                            [89.86630859375012, 25.293164062499955],\n                            [90.11962890625003, 25.21997070312497],\n                            [90.61308593750002, 25.16772460937497],\n                            [92.04970703125005, 25.16948242187499],\n                            [92.46835937500006, 24.94414062499999],\n                            [92.38496093750004, 24.848779296875023],\n                            [92.25126953125007, 24.895068359375045],\n                            [92.22832031250002, 24.88134765625],\n                            [92.22666015625012, 24.77099609374997],\n                            [92.11748046875002, 24.493945312500017],\n                            [92.06416015625004, 24.374365234375006],\n                            [91.84619140624997, 24.17529296875003],\n                            [91.72656250000003, 24.20507812499997],\n                            [91.35019531250012, 24.06049804687501],\n                            [91.16044921875007, 23.66064453125],\n                            [91.359375, 23.06835937500003],\n                            [91.43623046875004, 23.19990234375001],\n                            [91.55351562500002, 22.991552734375006],\n                            [91.61953125, 22.97968750000001],\n                            [91.75097656250003, 23.053515625000017],\n                            [91.75419921875007, 23.287304687499955],\n                            [91.79003906249997, 23.361035156249983],\n                            [91.937890625, 23.504687500000017],\n                            [91.92949218750007, 23.598242187499977],\n                            [91.92958984375, 23.68598632812501],\n                            [91.97851562500003, 23.691992187499977],\n                            [92.04404296875006, 23.677783203125017],\n                            [92.24609375000003, 23.683593750000057],\n                            [92.33378906250002, 23.242382812499955],\n                            [92.36162109375002, 22.929003906250074],\n                            [92.46445312500006, 22.734423828125045],\n                            [92.49140625000004, 22.685400390625006],\n                            [92.5612304687501, 22.04804687500001],\n                            [92.57490234375004, 21.978076171875045],\n                            [92.58281250000002, 21.940332031249994],\n                            [92.5934570312501, 21.46733398437499],\n                            [92.63164062500002, 21.306201171875045],\n                            [92.33056640624997, 21.439794921874977],\n                            [92.17958984375005, 21.293115234375023],\n                            [92.32412109375, 20.791845703125063],\n                            [92.0560546875, 21.1748046875],\n                            [91.86337890625012, 22.350488281249966],\n                            [91.7970703125001, 22.297460937500006],\n                            [91.48007812500006, 22.884814453125045],\n                            [91.2162109375, 22.642236328124994],\n                            [90.94560546875002, 22.597021484375034],\n                            [90.65625, 23.025488281250006],\n                            [90.60400390624997, 23.59135742187499],\n                            [90.55566406249997, 23.42153320312505],\n                            [90.26914062500012, 23.455859375000017],\n                            [90.59091796875012, 23.266406250000045],\n                            [90.43505859374997, 22.751904296874955],\n                            [90.61611328125, 22.362158203125034],\n                            [90.23056640625006, 21.82978515625004],\n                            [90.07119140625005, 21.887255859375017],\n                            [90.20957031250006, 22.156591796875006],\n                            [89.95419921875006, 22.022851562500023],\n                            [89.91806640625012, 22.11616210937501],\n                            [89.98515625000002, 22.466406250000063],\n                            [89.81191406250005, 21.983496093750006],\n                            [89.56855468750004, 21.767431640625034],\n                            [89.48320312500007, 22.275537109374994],\n                            [89.50058593750006, 21.914355468750045],\n                            [89.35371093750004, 21.72109375],\n                            [89.09394531250004, 21.872753906249983],\n                            [89.05, 22.274609374999983],\n                            [88.92070312500002, 22.632031249999955],\n                            [88.89970703125002, 22.843505859375057],\n                            [88.85058593749997, 23.040527343750057],\n                            [88.928125, 23.186621093750063],\n                            [88.72441406250002, 23.254980468750034],\n                            [88.69765625, 23.493017578125034],\n                            [88.63574218749997, 23.55],\n                            [88.56738281249997, 23.674414062500034],\n                            [88.69980468750006, 24.002539062500006],\n                            [88.71376953125, 24.069628906250017],\n                            [88.72656250000003, 24.186230468749955],\n                            [88.7335937500001, 24.23090820312501],\n                            [88.72353515625, 24.27490234375],\n                            [88.64228515625004, 24.325976562500017],\n                            [88.49853515625003, 24.34663085937504],\n                            [88.3375, 24.45385742187503],\n                            [88.225, 24.460644531249983],\n                            [88.14550781250003, 24.485791015624955],\n                            [88.07910156249997, 24.549902343750063],\n                            [88.02343750000003, 24.62783203125005],\n                            [88.03027343749997, 24.66445312500005],\n                            [88.0451171875001, 24.713037109374994],\n                            [88.1498046875, 24.914648437500034],\n                            [88.1888671875, 24.92060546875001],\n                            [88.27949218750004, 24.881933593750034],\n                            [88.31337890625005, 24.8818359375],\n                            [88.37294921875, 24.961523437499977],\n                            [88.45625, 25.18842773437504],\n                            [88.57382812500006, 25.18789062499999],\n                            [88.92978515625012, 25.222998046875063],\n                            [88.94414062500002, 25.290771484375],\n                            [88.85478515625002, 25.333544921875017],\n                            [88.76914062500006, 25.490478515625],\n                            [88.50244140624997, 25.537011718749994],\n                            [88.14746093749997, 25.811425781250023],\n                            [88.1066406250001, 25.841113281250045],\n                            [88.15078125000005, 26.08715820312497],\n                            [88.33398437499997, 26.257519531249955],\n                            [88.44042968749997, 26.369482421875034],\n                            [88.38623046875003, 26.471533203125034],\n                            [88.35146484375005, 26.482568359374966],\n                            [88.36992187500002, 26.564111328124994],\n                            [88.51826171875004, 26.517773437499955],\n                            [88.68281250000004, 26.291699218749983],\n                            [88.94072265625002, 26.24536132812497]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Bangladesh\", \"childNum\": 6 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [28.585351562500023, 43.742236328124996],\n                        [28.465429687500006, 43.389306640624994],\n                        [28.133691406250023, 43.39560546875],\n                        [27.92890625000001, 43.1861328125],\n                        [27.88886718750001, 42.74970703125],\n                        [27.484765625000023, 42.468066406249996],\n                        [28.014453125000017, 41.969042968749996],\n                        [27.47480468750001, 41.946875],\n                        [27.294921875, 42.079541015625],\n                        [27.24433593750001, 42.09326171875],\n                        [27.01171875, 42.058642578124996],\n                        [26.96875, 42.02685546875],\n                        [26.884863281250006, 41.991845703124994],\n                        [26.615332031250006, 41.964892578124996],\n                        [26.549707031250023, 41.896728515625],\n                        [26.51142578125001, 41.8263671875],\n                        [26.3603515625, 41.8015625],\n                        [26.327246093750006, 41.772802734375],\n                        [26.31796875, 41.744677734374996],\n                        [26.320898437500006, 41.716552734375],\n                        [26.200585937500023, 41.743798828124994],\n                        [26.107421875, 41.72568359375],\n                        [26.085546875, 41.704150390624996],\n                        [26.066015625, 41.673242187499994],\n                        [26.1435546875, 41.521533203124996],\n                        [26.155175781250023, 41.434863281249996],\n                        [26.135351562500006, 41.3857421875],\n                        [26.06640625, 41.35068359375],\n                        [25.92333984375, 41.311914062499994],\n                        [25.784960937500017, 41.330419921875],\n                        [25.52705078125001, 41.2998046875],\n                        [25.381933593750006, 41.26435546875],\n                        [25.25117187500001, 41.243554687499994],\n                        [24.773730468750017, 41.356103515624994],\n                        [24.595996093750017, 41.442724609375],\n                        [24.5693359375, 41.4673828125],\n                        [24.51826171875001, 41.552539062499996],\n                        [24.487890625, 41.555224609374996],\n                        [24.056054687500023, 41.527246093749994],\n                        [24.03291015625001, 41.469091796875],\n                        [24.011328125, 41.46005859375],\n                        [23.635156250000023, 41.386767578124996],\n                        [23.53583984375001, 41.386035156249996],\n                        [23.433398437500017, 41.398730468749996],\n                        [23.3720703125, 41.3896484375],\n                        [23.23984375, 41.3849609375],\n                        [23.15595703125001, 41.322070312499996],\n                        [22.916015625, 41.336279296875],\n                        [23.00361328125001, 41.73984375],\n                        [22.836816406250023, 41.993603515625],\n                        [22.344042968750017, 42.31396484375],\n                        [22.42207031250001, 42.328857421875],\n                        [22.445703125000023, 42.359130859375],\n                        [22.523535156250006, 42.440966796874996],\n                        [22.53242187500001, 42.481201171875],\n                        [22.524218750000017, 42.50390625],\n                        [22.43623046875001, 42.6291015625],\n                        [22.466796875, 42.84248046875],\n                        [22.799902343750006, 42.985742187499994],\n                        [22.976855468750017, 43.18798828125],\n                        [22.85957031250001, 43.25234375],\n                        [22.819726562500023, 43.300732421875],\n                        [22.767578125, 43.354150390624994],\n                        [22.554589843750023, 43.454492187499994],\n                        [22.36962890625, 43.781298828124996],\n                        [22.36542968750001, 43.862109375],\n                        [22.399023437500006, 43.96953125],\n                        [22.420800781250023, 44.007421875],\n                        [22.452529688228115, 44.0510441391688],\n                        [22.547921095934313, 44.113823956634434],\n                        [22.688564844478098, 44.254306249271906],\n                        [23.02851562500001, 44.077978515625],\n                        [22.868261718750006, 43.947900390624994],\n                        [22.919042968750006, 43.83447265625],\n                        [25.4970703125, 43.670800781249994],\n                        [26.2158203125, 44.007275390625],\n                        [27.0869140625, 44.167382812499994],\n                        [27.425390625, 44.0205078125],\n                        [27.88427734375, 43.987353515624996],\n                        [28.221972656250017, 43.772851562499994],\n                        [28.585351562500023, 43.742236328124996]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Bulgaria\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [50.60722656250002, 25.883105468750003],\n                        [50.57490234375001, 25.806787109374994],\n                        [50.465917968750006, 25.965527343749997],\n                        [50.46992187500001, 26.228955078124997],\n                        [50.5859375, 26.24072265625],\n                        [50.60722656250002, 25.883105468750003]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Bahrain\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-73.02685546874994, 21.19238281250003],\n                            [-73.16455078125003, 20.979150390625023],\n                            [-73.68115234375003, 20.9755859375],\n                            [-73.68037109374995, 21.103320312500017],\n                            [-73.52309570312497, 21.190820312499966],\n                            [-73.23535156249997, 21.15449218750004],\n                            [-73.05849609375, 21.313378906249994],\n                            [-73.02685546874994, 21.19238281250003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-73.041015625, 22.429052734375006],\n                            [-72.74726562500001, 22.32739257812497],\n                            [-73.16191406250002, 22.380712890625006],\n                            [-73.041015625, 22.429052734375006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.20673828124998, 22.213769531250023],\n                            [-74.27690429687499, 22.183691406250006],\n                            [-73.906396484375, 22.527441406250063],\n                            [-73.95419921874995, 22.71552734375001],\n                            [-73.84995117187503, 22.731054687500063],\n                            [-73.83652343749998, 22.538427734374977],\n                            [-74.20673828124998, 22.213769531250023]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.05751953124997, 22.723486328125034],\n                            [-74.27460937499995, 22.71166992187503],\n                            [-74.30703125, 22.83959960937497],\n                            [-74.05751953124997, 22.723486328125034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.84047851562494, 22.894335937500017],\n                            [-75.22333984374995, 23.165332031250074],\n                            [-75.13056640624998, 23.267919921875006],\n                            [-75.31596679687502, 23.668359374999966],\n                            [-74.84047851562494, 22.894335937500017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.66455078124997, 23.45014648437501],\n                            [-76.03710937500003, 23.60278320312503],\n                            [-76.01044921875001, 23.671386718750057],\n                            [-75.66455078124997, 23.45014648437501]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.42944335937497, 24.068066406249955],\n                            [-74.55092773437502, 23.96894531250001],\n                            [-74.52690429687502, 24.105078125000034],\n                            [-74.42944335937497, 24.068066406249955]\n                        ]\n                    ],\n                    [\n                        [\n                            [-77.65771484374994, 24.249462890624955],\n                            [-77.75527343750002, 24.163476562500023],\n                            [-77.61538085937494, 24.216357421875045],\n                            [-77.5615234375, 24.136816406250006],\n                            [-77.57373046875, 23.739160156249994],\n                            [-77.77128906249999, 23.752539062499977],\n                            [-77.99990234374994, 24.219824218750063],\n                            [-77.65771484374994, 24.249462890624955]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.30839843749999, 24.2],\n                            [-75.50322265624996, 24.139062500000023],\n                            [-75.40893554687503, 24.265771484374994],\n                            [-75.72666015625, 24.68935546875005],\n                            [-75.30839843749999, 24.2]\n                        ]\n                    ],\n                    [\n                        [\n                            [-77.34755859375, 25.013867187499983],\n                            [-77.56191406249997, 25.030029296875],\n                            [-77.27558593750001, 25.055761718750006],\n                            [-77.34755859375, 25.013867187499983]\n                        ]\n                    ],\n                    [\n                        [\n                            [-77.74384765625001, 24.70742187499999],\n                            [-77.74521484375, 24.463476562500034],\n                            [-78.04492187499997, 24.287451171875063],\n                            [-78.14580078125002, 24.493457031250017],\n                            [-78.36650390624993, 24.544189453125057],\n                            [-78.435302734375, 24.627587890624994],\n                            [-78.24272460937493, 24.65380859375],\n                            [-78.21137695312495, 25.191259765624977],\n                            [-77.97529296874998, 25.084814453125063],\n                            [-77.74384765625001, 24.70742187499999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-76.64882812499994, 25.487402343750006],\n                            [-76.34379882812496, 25.33203124999997],\n                            [-76.12661132812497, 25.14052734375005],\n                            [-76.16953125, 24.6494140625],\n                            [-76.319970703125, 24.81767578124999],\n                            [-76.21376953124994, 24.822460937499983],\n                            [-76.160400390625, 25.119335937499983],\n                            [-76.36928710937502, 25.312597656250006],\n                            [-76.62070312499998, 25.43164062500003],\n                            [-76.78066406249997, 25.426855468750006],\n                            [-76.71083984374997, 25.564892578124983],\n                            [-76.64882812499994, 25.487402343750006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-78.49287109375001, 26.729052734375017],\n                            [-77.92246093749998, 26.69111328125001],\n                            [-78.74365234374994, 26.50068359375004],\n                            [-78.98564453124996, 26.689501953125045],\n                            [-78.79804687500001, 26.58242187499999],\n                            [-78.59711914062493, 26.797949218750006],\n                            [-78.49287109375001, 26.729052734375017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-77.22563476562496, 25.904199218750023],\n                            [-77.40317382812498, 26.02470703124996],\n                            [-77.24677734374998, 26.156347656250034],\n                            [-77.238623046875, 26.561132812500006],\n                            [-77.510595703125, 26.845996093750045],\n                            [-77.94375, 26.90356445312503],\n                            [-77.53388671874995, 26.903417968750006],\n                            [-77.06635742187501, 26.530175781249994],\n                            [-77.03828124999998, 26.333447265624983],\n                            [-77.16728515624996, 26.240332031250006],\n                            [-77.22563476562496, 25.904199218750023]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Bahamas\", \"childNum\": 14 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [19.007128906250045, 44.86918945312502],\n                        [19.348632812500057, 44.88090820312502],\n                        [19.118457031250074, 44.359960937500006],\n                        [19.583789062500017, 44.04345703125003],\n                        [19.245019531249994, 43.96503906250004],\n                        [19.495117187500057, 43.642871093750045],\n                        [19.19433593749997, 43.533300781250006],\n                        [19.164355468750017, 43.53544921874999],\n                        [19.11279296874997, 43.52773437500002],\n                        [19.080078125000057, 43.51772460937502],\n                        [19.0283203125, 43.53251953125002],\n                        [18.97421875, 43.54233398437498],\n                        [18.95068359375, 43.52666015624999],\n                        [19.036718750000034, 43.35732421875002],\n                        [19.026660156250017, 43.292431640624955],\n                        [18.97871093750001, 43.28540039062503],\n                        [18.934667968750006, 43.339453125000034],\n                        [18.85107421875003, 43.34633789062502],\n                        [18.749218750000068, 43.283544921875006],\n                        [18.67421875000008, 43.230810546875006],\n                        [18.623632812500063, 43.027685546875034],\n                        [18.488476562500068, 43.01215820312498],\n                        [18.44384765625003, 42.96845703125004],\n                        [18.46601562500001, 42.777246093749994],\n                        [18.54589843750003, 42.64160156249997],\n                        [18.436328125000017, 42.559716796874994],\n                        [17.667578125000063, 42.897119140624994],\n                        [17.585156250000068, 42.93837890625005],\n                        [17.650488281250063, 43.006591796875],\n                        [17.27382812500005, 43.44575195312501],\n                        [16.300097656250017, 44.12451171875],\n                        [16.10341796875008, 44.52099609375006],\n                        [15.736621093750045, 44.76582031250001],\n                        [15.788085937500057, 45.17895507812497],\n                        [16.028320312500057, 45.18959960937502],\n                        [16.29335937500005, 45.00883789062496],\n                        [16.53066406250008, 45.21669921875002],\n                        [16.918652343749983, 45.27656249999998],\n                        [17.812792968750074, 45.078125],\n                        [18.66259765625, 45.07744140624999],\n                        [18.83642578125, 44.883251953124955],\n                        [19.007128906250045, 44.86918945312502]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Bosnia and Herz.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [28.14794921875, 56.142919921875],\n                        [28.284277343750006, 56.055908203125],\n                        [29.375, 55.938720703125],\n                        [29.353417968750023, 55.784375],\n                        [29.412988281250023, 55.724853515625],\n                        [29.482226562500017, 55.6845703125],\n                        [29.63007812500001, 55.751171875],\n                        [29.6845703125, 55.7697265625],\n                        [29.744140625, 55.77041015625],\n                        [29.82392578125001, 55.7951171875],\n                        [29.881640625000017, 55.832324218749996],\n                        [29.93701171875, 55.845263671874996],\n                        [30.04267578125001, 55.83642578125],\n                        [30.23359375000001, 55.84521484375],\n                        [30.625585937500006, 55.666259765625],\n                        [30.906835937500006, 55.57001953125],\n                        [30.90058593750001, 55.397412109375],\n                        [30.82099609375001, 55.3302734375],\n                        [30.810546875, 55.306982421875],\n                        [30.814453125, 55.2787109375],\n                        [30.87744140625, 55.2234375],\n                        [30.958886718750023, 55.13759765625],\n                        [30.97773437500001, 55.08779296875],\n                        [30.97773437500001, 55.05048828125],\n                        [30.829882812500017, 54.914990234375],\n                        [30.804492187500017, 54.8609375],\n                        [30.791015625, 54.806005859375],\n                        [30.798828125, 54.783251953124996],\n                        [30.984179687500017, 54.6958984375],\n                        [31.12128906250001, 54.648486328124996],\n                        [31.152148437500017, 54.625341796875],\n                        [31.074804687500006, 54.491796875],\n                        [31.18476562500001, 54.452978515625],\n                        [31.299121093750017, 54.29169921875],\n                        [31.403613281250017, 54.195947265625],\n                        [31.62841796875, 54.111181640625],\n                        [31.7919921875, 54.055908203125],\n                        [31.825976562500017, 54.030712890625],\n                        [31.837792968750023, 54.00078125],\n                        [31.825292968750006, 53.935009765625],\n                        [31.783007812500017, 53.85498046875],\n                        [31.754199218750017, 53.81044921875],\n                        [31.82080078125, 53.791943359375],\n                        [31.9921875, 53.796875],\n                        [32.20039062500001, 53.78125],\n                        [32.45097656250002, 53.6533203125],\n                        [32.70429687500001, 53.336328125],\n                        [32.64443359375002, 53.32890625],\n                        [32.57802734375002, 53.31240234375],\n                        [32.469335937500006, 53.2703125],\n                        [32.14199218750002, 53.091162109375],\n                        [31.849707031250006, 53.106201171875],\n                        [31.668261718750017, 53.200927734375],\n                        [31.417871093750023, 53.196044921875],\n                        [31.38837890625001, 53.184814453125],\n                        [31.364550781250017, 53.138964843749996],\n                        [31.30292968750001, 53.060888671875],\n                        [31.2587890625, 53.01669921875],\n                        [31.29511718750001, 52.989794921874996],\n                        [31.35302734375, 52.933447265625],\n                        [31.442773437500023, 52.86181640625],\n                        [31.53515625, 52.7982421875],\n                        [31.564843750000023, 52.759228515625],\n                        [31.585546875, 52.532470703125],\n                        [31.57734375000001, 52.312304687499996],\n                        [31.6015625, 52.284814453125],\n                        [31.64990234375, 52.26220703125],\n                        [31.690625, 52.220654296875],\n                        [31.758593750000017, 52.125830078125],\n                        [31.76337890625001, 52.10107421875],\n                        [31.57373046875, 52.10810546875],\n                        [31.345996093750017, 52.10537109375],\n                        [31.21796875000001, 52.050244140625],\n                        [30.98066406250001, 52.046191406249996],\n                        [30.845703125, 51.953076171875],\n                        [30.755273437500023, 51.895166015625],\n                        [30.667285156250017, 51.814111328125],\n                        [30.583886718750023, 51.68896484375],\n                        [30.533007812500017, 51.596337890624994],\n                        [30.56074218750001, 51.531494140625],\n                        [30.602343750000017, 51.471240234374996],\n                        [30.611718750000023, 51.40634765625],\n                        [30.63251953125001, 51.355419921875],\n                        [30.449511718750017, 51.274316406249994],\n                        [30.160742187500006, 51.477880859375],\n                        [29.346484375000017, 51.382568359375],\n                        [29.10205078125, 51.6275390625],\n                        [29.06074218750001, 51.625439453125],\n                        [29.013085937500023, 51.598925781249996],\n                        [28.97773437500001, 51.57177734375],\n                        [28.927539062500017, 51.562158203124994],\n                        [28.849511718750023, 51.540185546874994],\n                        [28.73125, 51.433398437499996],\n                        [28.690234375000017, 51.438867187499994],\n                        [28.647753906250017, 51.45654296875],\n                        [28.599023437500023, 51.542626953124994],\n                        [28.532031250000017, 51.562451171875],\n                        [27.85859375000001, 51.5923828125],\n                        [27.7, 51.477978515625],\n                        [27.689746093750017, 51.572412109374994],\n                        [27.296289062500023, 51.597412109375],\n                        [27.270117187500006, 51.613574218749996],\n                        [27.141992187500023, 51.75205078125],\n                        [27.074121093750023, 51.76083984375],\n                        [26.95283203125001, 51.75400390625],\n                        [26.7734375, 51.770703125],\n                        [25.785742187500006, 51.923828125],\n                        [24.361914062500006, 51.867529296875],\n                        [24.280078125000017, 51.774707031249996],\n                        [24.126855468750023, 51.6646484375],\n                        [23.978320312500017, 51.59130859375],\n                        [23.951171875, 51.58505859375],\n                        [23.8642578125, 51.623974609375],\n                        [23.79169921875001, 51.637109375],\n                        [23.706835937500017, 51.64130859375],\n                        [23.61376953125, 51.525390625],\n                        [23.605273437500017, 51.517919921875],\n                        [23.652441406250006, 52.040380859375],\n                        [23.175097656250017, 52.28662109375],\n                        [23.915429687500023, 52.770263671875],\n                        [23.484667968750017, 53.939794921875],\n                        [23.55908203125, 53.91982421875],\n                        [23.733691406250017, 53.912255859375],\n                        [24.191308593750023, 53.950439453125],\n                        [24.236621093750017, 53.919970703124996],\n                        [24.31796875, 53.89296875],\n                        [24.620703125, 53.979833984375],\n                        [24.768164062500006, 53.974658203124996],\n                        [24.78925781250001, 53.9982421875],\n                        [24.82568359375, 54.118994140625],\n                        [24.869531250000023, 54.145166015625],\n                        [25.04609375000001, 54.133056640625],\n                        [25.111425781250006, 54.154931640625],\n                        [25.179492187500017, 54.2142578125],\n                        [25.46113281250001, 54.292773437499996],\n                        [25.505664062500017, 54.264941406249996],\n                        [25.52734375, 54.215136718749996],\n                        [25.497363281250017, 54.175244140625],\n                        [25.573046875000017, 54.139892578125],\n                        [25.765234375, 54.17978515625],\n                        [25.702539062500023, 54.29296875],\n                        [25.61689453125001, 54.310107421874996],\n                        [25.557519531250023, 54.310693359375],\n                        [25.54736328125, 54.331835937499996],\n                        [25.56757812500001, 54.37705078125],\n                        [25.62031250000001, 54.460400390625],\n                        [25.68515625, 54.535791015625],\n                        [25.72480468750001, 54.564257812499996],\n                        [25.73164062500001, 54.590380859374996],\n                        [25.722460937500017, 54.71787109375],\n                        [25.859277343750023, 54.919287109375],\n                        [25.964453125, 54.94716796875],\n                        [26.09296875000001, 54.9623046875],\n                        [26.175195312500023, 55.003271484375],\n                        [26.250781250000017, 55.12451171875],\n                        [26.291796875000017, 55.139599609375],\n                        [26.601171875, 55.130175781249996],\n                        [26.6484375, 55.20419921875],\n                        [26.775683593750017, 55.273095703125],\n                        [26.760156250000023, 55.293359375],\n                        [26.68125, 55.306445312499996],\n                        [26.49531250000001, 55.318017578125],\n                        [26.457617187500006, 55.34248046875],\n                        [26.469531250000017, 55.371923828125],\n                        [26.51923828125001, 55.44814453125],\n                        [26.56660156250001, 55.546484375],\n                        [26.5908203125, 55.62265625],\n                        [26.593554687500017, 55.667529296874996],\n                        [27.052539062500017, 55.83056640625],\n                        [27.576757812500006, 55.798779296875],\n                        [28.14794921875, 56.142919921875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Belarus\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-87.8529296875, 17.4228515625],\n                            [-87.92998046874996, 17.283007812500017],\n                            [-87.826416015625, 17.546289062499994],\n                            [-87.8529296875, 17.4228515625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-88.89404296875, 15.890625],\n                            [-89.2328125, 15.888671875],\n                            [-89.16147460937503, 17.81484375],\n                            [-89.13354492187503, 17.970800781249977],\n                            [-88.80634765624998, 17.965527343749983],\n                            [-88.52299804687499, 18.445898437500063],\n                            [-88.29565429687494, 18.47241210937503],\n                            [-88.34926757812494, 18.358837890624983],\n                            [-88.1302734375, 18.350732421875023],\n                            [-88.08525390624999, 18.226123046875045],\n                            [-88.27172851562494, 17.60986328125],\n                            [-88.203466796875, 17.5166015625],\n                            [-88.31342773437501, 16.632763671874983],\n                            [-88.89404296875, 15.890625]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Belize\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-64.73027343749999, 32.29345703125],\n                        [-64.86284179687499, 32.273876953125],\n                        [-64.66831054687499, 32.38193359375],\n                        [-64.73027343749999, 32.29345703125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Bermuda\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-58.15976562499999, -20.164648437500006],\n                        [-58.18017578125, -19.81787109375],\n                        [-59.09052734375, -19.286230468750006],\n                        [-60.00737304687499, -19.29755859375001],\n                        [-61.7568359375, -19.6453125],\n                        [-62.276318359375, -20.5625],\n                        [-62.27666015624999, -21.066015625],\n                        [-62.65097656249999, -22.233691406250003],\n                        [-62.84335937499999, -21.99726562500001],\n                        [-63.92167968749999, -22.028613281250003],\n                        [-63.97612304687499, -22.072558593750003],\n                        [-64.26640624999999, -22.603320312500003],\n                        [-64.30791015624999, -22.7953125],\n                        [-64.32529296874999, -22.82763671875],\n                        [-64.373974609375, -22.761035156250003],\n                        [-64.4455078125, -22.58535156250001],\n                        [-64.477734375, -22.4853515625],\n                        [-64.5236328125, -22.37158203125],\n                        [-64.60551757812499, -22.228808593750003],\n                        [-64.992626953125, -22.109667968750003],\n                        [-65.518798828125, -22.09453125],\n                        [-65.686181640625, -22.11025390625001],\n                        [-65.77104492187499, -22.099609375],\n                        [-65.86015624999999, -22.01972656250001],\n                        [-66.05859375, -21.879492187500006],\n                        [-66.098583984375, -21.835058593750006],\n                        [-66.17465820312499, -21.8056640625],\n                        [-66.220166015625, -21.802539062500003],\n                        [-66.24760742187499, -21.83046875],\n                        [-66.28212890625, -21.94746093750001],\n                        [-66.3224609375, -22.053125],\n                        [-66.365185546875, -22.11376953125],\n                        [-66.71171874999999, -22.21630859375],\n                        [-66.99111328125, -22.509863281250006],\n                        [-67.19487304687499, -22.82167968750001],\n                        [-67.362255859375, -22.85517578125001],\n                        [-67.57993164062499, -22.891699218750006],\n                        [-67.79443359375, -22.879492187500006],\n                        [-67.87944335937499, -22.82294921875001],\n                        [-67.88173828125, -22.49335937500001],\n                        [-68.18642578125, -21.61855468750001],\n                        [-68.197021484375, -21.30029296875],\n                        [-68.558251953125, -20.901953125],\n                        [-68.484326171875, -20.62841796875],\n                        [-68.74516601562499, -20.45859375],\n                        [-68.75932617187499, -20.115527343750003],\n                        [-68.560693359375, -19.967089843750003],\n                        [-68.559375, -19.90234375],\n                        [-68.578271484375, -19.856542968750006],\n                        [-68.69619140625, -19.74072265625],\n                        [-68.69829101562499, -19.72109375],\n                        [-68.57529296874999, -19.56015625],\n                        [-68.462890625, -19.43281250000001],\n                        [-68.470166015625, -19.409960937500003],\n                        [-68.49199218749999, -19.381933593750006],\n                        [-68.85795898437499, -19.093359375],\n                        [-68.96831054687499, -18.96796875000001],\n                        [-68.97885742187499, -18.81298828125],\n                        [-69.026806640625, -18.65625],\n                        [-69.09228515625, -18.28242187500001],\n                        [-69.145458984375, -18.14404296875],\n                        [-69.0939453125, -18.05048828125001],\n                        [-69.28232421874999, -17.96484375],\n                        [-69.31337890625, -17.943164062500003],\n                        [-69.5109375, -17.50605468750001],\n                        [-69.51108398437499, -17.5048828125],\n                        [-69.510986328125, -17.46035156250001],\n                        [-69.521923828125, -17.388964843750003],\n                        [-69.645703125, -17.24853515625],\n                        [-69.62485351562499, -17.2001953125],\n                        [-69.020703125, -16.6421875],\n                        [-69.03291015625, -16.47597656250001],\n                        [-68.8427734375, -16.337890625],\n                        [-69.21757812499999, -16.14912109375001],\n                        [-69.4208984375, -15.640625],\n                        [-69.17246093749999, -15.236621093750003],\n                        [-69.37470703125, -14.962988281250006],\n                        [-69.35947265624999, -14.7953125],\n                        [-68.87089843749999, -14.169726562500003],\n                        [-69.07412109375, -13.682812500000011],\n                        [-68.97861328124999, -12.880078125000011],\n                        [-68.68525390625, -12.501953125],\n                        [-69.57861328125, -10.951757812500006],\n                        [-69.228515625, -10.955664062500006],\n                        [-68.84833984375, -11.011132812500009],\n                        [-68.678369140625, -11.11279296875],\n                        [-68.39799804687499, -11.01875],\n                        [-68.0716796875, -10.703125],\n                        [-67.99169921875, -10.674414062500006],\n                        [-67.83500976562499, -10.662792968750011],\n                        [-67.72177734374999, -10.68310546875],\n                        [-67.416943359375, -10.389843750000011],\n                        [-66.575341796875, -9.89990234375],\n                        [-65.396142578125, -9.71240234375],\n                        [-65.298583984375, -10.146777343750003],\n                        [-65.31308593749999, -10.253027343750006],\n                        [-65.395458984375, -10.392285156250011],\n                        [-65.4369140625, -10.449023437500003],\n                        [-65.44711914062499, -10.507421875],\n                        [-65.33403320312499, -10.892773437500011],\n                        [-65.32377929687499, -11.024804687500009],\n                        [-65.389892578125, -11.246289062500011],\n                        [-65.1857421875, -11.74951171875],\n                        [-64.783447265625, -12.059375],\n                        [-64.42050781249999, -12.439746093750003],\n                        [-63.68857421874999, -12.47802734375],\n                        [-63.3466796875, -12.680078125],\n                        [-63.06748046874999, -12.669140625000011],\n                        [-62.76547851562499, -12.997265625000011],\n                        [-62.11801757812499, -13.159765625],\n                        [-62.09477539062499, -13.241992187500003],\n                        [-61.944726562499994, -13.40625],\n                        [-61.87412109374999, -13.470410156250011],\n                        [-61.789941406249994, -13.525585937500011],\n                        [-61.57568359375, -13.524804687500009],\n                        [-61.51157226562499, -13.541210937500011],\n                        [-61.41606445312499, -13.526562500000011],\n                        [-61.129150390625, -13.49853515625],\n                        [-61.07700195312499, -13.48974609375],\n                        [-60.506591796875, -13.78984375],\n                        [-60.372705078124994, -14.41875],\n                        [-60.273339843749994, -15.088769531250009],\n                        [-60.402001953124994, -15.0927734375],\n                        [-60.583203125, -15.098339843750011],\n                        [-60.53046875, -15.143164062500006],\n                        [-60.38046875, -15.318261718750009],\n                        [-60.242333984374994, -15.479589843750006],\n                        [-60.20664062499999, -15.901953125],\n                        [-60.18720703125, -16.132128906250003],\n                        [-60.17558593749999, -16.269335937500003],\n                        [-58.53793945312499, -16.328222656250006],\n                        [-58.49658203125, -16.32666015625],\n                        [-58.42368164062499, -16.307910156250003],\n                        [-58.37539062499999, -16.28359375],\n                        [-58.345605468749994, -16.284375],\n                        [-58.35039062499999, -16.490820312500006],\n                        [-58.470605468749994, -16.650195312500003],\n                        [-58.478125, -16.70068359375],\n                        [-58.45981445312499, -16.910742187500006],\n                        [-58.417382812499994, -17.08056640625],\n                        [-58.39599609375, -17.23427734375001],\n                        [-58.34775390624999, -17.28212890625001],\n                        [-57.99091796875, -17.51289062500001],\n                        [-57.905029296875, -17.532324218750006],\n                        [-57.832470703125, -17.512109375],\n                        [-57.78886718749999, -17.573046875],\n                        [-57.780175781249994, -17.67177734375001],\n                        [-57.66166992187499, -17.947363281250006],\n                        [-57.58647460937499, -18.12226562500001],\n                        [-57.49565429687499, -18.214648437500003],\n                        [-57.57402343749999, -18.279296875],\n                        [-57.725, -18.733203125],\n                        [-57.783105468749994, -18.91425781250001],\n                        [-57.716796875, -19.044042968750006],\n                        [-58.131494140624994, -19.74453125],\n                        [-57.860742187499994, -19.979589843750006],\n                        [-57.887597656249994, -20.02041015625001],\n                        [-57.96015625, -20.04072265625001],\n                        [-58.021142578124994, -20.05517578125],\n                        [-58.09375, -20.15107421875001],\n                        [-58.15976562499999, -20.164648437500006]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Bolivia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-48.48588867187493, -27.76699218749998],\n                            [-48.554589843749994, -27.81220703125004],\n                            [-48.542187499999955, -27.57480468749999],\n                            [-48.41489257812495, -27.399609375],\n                            [-48.48588867187493, -27.76699218749998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-48.584423828124955, -26.401562499999983],\n                            [-48.665771484375, -26.289648437500006],\n                            [-48.53974609374998, -26.170312500000023],\n                            [-48.584423828124955, -26.401562499999983]\n                        ]\n                    ],\n                    [\n                        [\n                            [-45.26025390624997, -23.889160156249986],\n                            [-45.451416015625, -23.895605468749977],\n                            [-45.30234375, -23.727539062500014],\n                            [-45.26025390624997, -23.889160156249986]\n                        ]\n                    ],\n                    [\n                        [\n                            [-44.12929687499994, -23.14189453124999],\n                            [-44.36015624999999, -23.17207031250001],\n                            [-44.24287109374998, -23.074121093750037],\n                            [-44.12929687499994, -23.14189453124999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-38.90356445312497, -13.473437499999974],\n                            [-38.97758789062496, -13.523535156249963],\n                            [-39.02216796874998, -13.445605468749989],\n                            [-38.907128906249994, -13.401074218749983],\n                            [-38.90356445312497, -13.473437499999974]\n                        ]\n                    ],\n                    [\n                        [\n                            [-38.743847656249955, -13.097070312500037],\n                            [-38.668115234374966, -12.880175781249989],\n                            [-38.601171875, -12.99257812499998],\n                            [-38.743847656249955, -13.097070312500037]\n                        ]\n                    ],\n                    [\n                        [\n                            [-44.49931640625002, -2.939648437499983],\n                            [-44.597753906250006, -3.037597656249943],\n                            [-44.4814453125, -2.717578125000031],\n                            [-44.49931640625002, -2.939648437499983]\n                        ]\n                    ],\n                    [\n                        [\n                            [-44.88310546874996, -1.317871093749986],\n                            [-45.020849609375034, -1.372363281249974],\n                            [-44.978662109374966, -1.267285156249983],\n                            [-44.88310546874996, -1.317871093749986]\n                        ]\n                    ],\n                    [\n                        [\n                            [-51.83251953124997, -1.433789062499969],\n                            [-51.938378906249966, -1.452636718749986],\n                            [-51.680029296875006, -1.086132812500026],\n                            [-51.546044921874966, -0.649609375],\n                            [-51.25400390624998, -0.54140625],\n                            [-51.16074218749998, -0.666699218750011],\n                            [-51.27631835937498, -1.02177734374996],\n                            [-51.83251953124997, -1.433789062499969]\n                        ]\n                    ],\n                    [\n                        [\n                            [-49.62866210937497, -0.229199218749969],\n                            [-49.11699218749999, -0.163574218750014],\n                            [-48.39267578124995, -0.29736328125],\n                            [-48.83359375, -1.390039062500023],\n                            [-49.038476562499994, -1.5140625],\n                            [-49.17270507812498, -1.41259765625],\n                            [-49.233984375000034, -1.59951171874998],\n                            [-49.50664062499999, -1.511621093750023],\n                            [-49.587890625, -1.712402343749972],\n                            [-49.805126953124955, -1.790234375000026],\n                            [-50.06572265625002, -1.703808593749997],\n                            [-50.50761718749999, -1.787988281250009],\n                            [-50.759765625, -1.240234374999972],\n                            [-50.72949218749997, -1.126757812499946],\n                            [-50.57695312499999, -1.103125],\n                            [-50.709619140624994, -1.07773437499999],\n                            [-50.796093749999955, -0.90625],\n                            [-50.6455078125, -0.27285156249998],\n                            [-50.24824218749998, -0.11640625],\n                            [-49.62866210937497, -0.229199218749969]\n                        ]\n                    ],\n                    [\n                        [\n                            [-50.65288085937499, -0.131640624999989],\n                            [-50.926367187500034, -0.327343749999983],\n                            [-51.03808593749994, -0.225878906250003],\n                            [-50.84218750000002, -0.050195312500009],\n                            [-50.65288085937499, -0.131640624999989]\n                        ]\n                    ],\n                    [\n                        [\n                            [-49.44389648437499, -0.112402343749977],\n                            [-49.83007812499997, -0.093896484375023],\n                            [-49.50346679687496, 0.083691406250011],\n                            [-49.37231445312497, 0.001074218749963],\n                            [-49.44389648437499, -0.112402343749977]\n                        ]\n                    ],\n                    [\n                        [\n                            [-49.73823242187498, 0.26816406250002],\n                            [-49.917089843750006, -0.023193359375014],\n                            [-50.339453125, 0.043359375000051],\n                            [-50.27265624999998, 0.231738281249974],\n                            [-49.73823242187498, 0.26816406250002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-50.42612304687498, 0.139257812500048],\n                            [-50.44394531249998, -0.007666015624949],\n                            [-50.623925781249966, 0.054394531249983],\n                            [-50.372753906249955, 0.590869140625031],\n                            [-50.33227539062497, 0.259033203125028],\n                            [-50.42612304687498, 0.139257812500048]\n                        ]\n                    ],\n                    [\n                        [\n                            [-50.152929687500006, 0.393017578125054],\n                            [-50.26132812499998, 0.359179687500003],\n                            [-50.281689453124955, 0.51650390624998],\n                            [-50.05883789062503, 0.638037109374963],\n                            [-50.152929687500006, 0.393017578125054]\n                        ]\n                    ],\n                    [\n                        [\n                            [-50.29897460937502, 1.93852539062496],\n                            [-50.45610351562496, 1.910498046875034],\n                            [-50.49101562499996, 2.128613281249969],\n                            [-50.34199218749998, 2.14174804687498],\n                            [-50.29897460937502, 1.93852539062496]\n                        ]\n                    ],\n                    [\n                        [\n                            [-59.69970703125, 4.353515625],\n                            [-59.73857421874993, 4.226757812500026],\n                            [-59.62021484374998, 4.023144531250026],\n                            [-59.557763671874966, 3.960009765625031],\n                            [-59.551123046875034, 3.933544921874969],\n                            [-59.854394531249994, 3.5875],\n                            [-59.99433593749998, 2.689990234375031],\n                            [-59.88964843749997, 2.362939453125009],\n                            [-59.75522460937495, 2.27412109375004],\n                            [-59.74350585937498, 2.12163085937496],\n                            [-59.75175781249996, 1.962402343750028],\n                            [-59.75620117187498, 1.900634765624972],\n                            [-59.666601562500006, 1.746289062499969],\n                            [-59.53569335937499, 1.7],\n                            [-59.23120117187494, 1.376025390625031],\n                            [-58.82177734374994, 1.201220703125031],\n                            [-58.787207031250006, 1.208496093750014],\n                            [-58.73032226562498, 1.247509765625054],\n                            [-58.68461914062499, 1.28105468749996],\n                            [-58.511865234374966, 1.284667968749986],\n                            [-58.506054687499926, 1.438671875000011],\n                            [-58.39580078124993, 1.481738281249989],\n                            [-58.38037109375, 1.530224609375011],\n                            [-58.34067382812498, 1.587548828125051],\n                            [-58.03466796875, 1.520263671875014],\n                            [-57.9828125, 1.648437500000014],\n                            [-57.87343750000002, 1.667285156250045],\n                            [-57.79565429687497, 1.7],\n                            [-57.59443359375001, 1.704101562499986],\n                            [-57.54575195312495, 1.726074218750028],\n                            [-57.31748046874998, 1.963476562499991],\n                            [-57.27558593749998, 1.959228515625014],\n                            [-57.189599609374966, 1.981591796875037],\n                            [-57.11889648437494, 2.013964843749974],\n                            [-57.09267578125002, 2.005810546874997],\n                            [-57.03759765625, 1.936474609374997],\n                            [-56.96953124999999, 1.91640625],\n                            [-56.48281249999994, 1.942138671874986],\n                            [-56.019921874999966, 1.842236328124983],\n                            [-55.96333007812498, 1.85708007812498],\n                            [-55.929638671874955, 1.8875],\n                            [-55.92163085937503, 1.976660156250006],\n                            [-55.91533203124999, 2.039550781250028],\n                            [-55.96196289062496, 2.09511718749998],\n                            [-56.02006835937499, 2.15815429687504],\n                            [-56.073632812499994, 2.236767578124969],\n                            [-56.13769531249997, 2.259033203124986],\n                            [-56.12939453124997, 2.299511718749969],\n                            [-56.08779296875002, 2.341308593750043],\n                            [-56.045117187499955, 2.364404296875037],\n                            [-56.02036132812498, 2.392773437500054],\n                            [-55.993505859375006, 2.497509765624983],\n                            [-55.9755859375, 2.515966796875006],\n                            [-55.957470703124955, 2.52045898437504],\n                            [-55.730566406250006, 2.406152343750023],\n                            [-55.385351562500006, 2.440625],\n                            [-55.34399414062503, 2.488769531249972],\n                            [-55.28603515625002, 2.49965820312498],\n                            [-55.18769531249998, 2.547509765625037],\n                            [-55.114111328125006, 2.539208984375037],\n                            [-55.07031249999994, 2.548339843750028],\n                            [-55.005810546874955, 2.592968749999983],\n                            [-54.97866210937502, 2.597656250000043],\n                            [-54.968408203124966, 2.548339843750028],\n                            [-54.92656249999999, 2.497363281250045],\n                            [-54.876074218750006, 2.450390624999969],\n                            [-54.72221679687499, 2.441650390624972],\n                            [-54.69741210937502, 2.359814453124997],\n                            [-54.66186523437497, 2.327539062499994],\n                            [-54.61625976562499, 2.326757812500006],\n                            [-54.59194335937502, 2.313769531250031],\n                            [-54.55048828125001, 2.293066406249991],\n                            [-54.51508789062498, 2.245458984374963],\n                            [-54.43310546875, 2.207519531250057],\n                            [-54.13007812499998, 2.121044921875026],\n                            [-53.76777343749998, 2.354833984375048],\n                            [-52.90346679687502, 2.211523437499977],\n                            [-52.58300781250003, 2.528906249999977],\n                            [-52.327880859375, 3.18173828125002],\n                            [-51.65253906249998, 4.061279296874972],\n                            [-51.54707031250001, 4.31088867187502],\n                            [-51.219921874999955, 4.093603515624991],\n                            [-50.71440429687502, 2.134033203125],\n                            [-50.458886718749994, 1.829589843749972],\n                            [-49.957128906250006, 1.65986328125004],\n                            [-49.898876953124955, 1.16298828124998],\n                            [-50.29443359374997, 0.835742187500003],\n                            [-50.755078124999955, 0.222558593749966],\n                            [-51.28291015625001, -0.085205078125028],\n                            [-51.98081054687498, -1.367968749999974],\n                            [-52.22924804687497, -1.3625],\n                            [-52.664160156250034, -1.551757812500028],\n                            [-51.94755859374996, -1.586718749999946],\n                            [-50.89492187500002, -0.937597656249963],\n                            [-50.690039062500006, -1.761718749999986],\n                            [-50.40322265625002, -2.015527343750009],\n                            [-49.999218749999955, -1.831835937499974],\n                            [-49.71953125000002, -1.926367187499963],\n                            [-49.31367187500001, -1.731738281250003],\n                            [-49.63652343749996, -2.656933593750026],\n                            [-49.45751953125, -2.504589843749983],\n                            [-49.21103515624998, -1.916503906249986],\n                            [-48.99130859374998, -1.829785156249997],\n                            [-48.71000976562496, -1.487695312500023],\n                            [-48.46293945312499, -1.613964843749997],\n                            [-48.349804687499926, -1.482128906249955],\n                            [-48.46806640624996, -1.393847656250003],\n                            [-48.44980468749998, -1.145507812499943],\n                            [-48.11508789062498, -0.7375],\n                            [-47.557324218749955, -0.669921874999957],\n                            [-47.418652343749955, -0.765917968749974],\n                            [-47.39809570312502, -0.626660156250026],\n                            [-45.45859374999995, -1.35625],\n                            [-45.32915039062496, -1.71728515625],\n                            [-45.07636718749998, -1.466406249999949],\n                            [-44.72114257812498, -1.733496093750006],\n                            [-44.778515624999955, -1.798828125],\n                            [-44.651269531249966, -1.745800781250026],\n                            [-44.537792968749955, -2.052734374999943],\n                            [-44.75634765624997, -2.265527343749952],\n                            [-44.66240234375002, -2.373242187499955],\n                            [-44.435449218749966, -2.168066406249991],\n                            [-44.38183593749997, -2.365527343749989],\n                            [-44.52011718749998, -2.40546875000004],\n                            [-44.589013671874994, -2.573437499999983],\n                            [-44.72304687500002, -3.204785156249997],\n                            [-44.43754882812496, -2.944433593749977],\n                            [-44.228613281250006, -2.471289062499949],\n                            [-44.105566406250006, -2.493457031250031],\n                            [-44.19267578124999, -2.809570312499943],\n                            [-43.93291015624999, -2.583496093749986],\n                            [-43.45512695312499, -2.502050781250006],\n                            [-43.38007812499998, -2.376074218750006],\n                            [-42.93671874999998, -2.465039062500011],\n                            [-42.24960937499998, -2.7919921875],\n                            [-41.876171874999926, -2.746582031249986],\n                            [-41.479931640624955, -2.916503906249972],\n                            [-40.474560546874926, -2.795605468750026],\n                            [-39.96469726562498, -2.861523437499955],\n                            [-38.475781249999955, -3.717480468749997],\n                            [-38.04882812500003, -4.216406250000034],\n                            [-37.626318359375006, -4.592089843750003],\n                            [-37.30146484375001, -4.713085937499969],\n                            [-37.174658203125006, -4.912402343749974],\n                            [-36.590722656249966, -5.097558593749952],\n                            [-35.549414062500006, -5.129394531249957],\n                            [-35.39257812499994, -5.250878906250009],\n                            [-34.833886718749994, -7.024414062500014],\n                            [-34.83466796874998, -7.97148437499996],\n                            [-35.34086914062499, -9.230664062499983],\n                            [-35.76396484374993, -9.702539062500023],\n                            [-35.890820312499926, -9.687011718749957],\n                            [-35.88544921875001, -9.84765625],\n                            [-36.39833984374994, -10.484082031249983],\n                            [-36.768310546875, -10.671679687500017],\n                            [-37.18281249999998, -11.06845703125002],\n                            [-37.35600585937502, -11.403906249999977],\n                            [-37.35922851562495, -11.252539062499963],\n                            [-37.68872070312503, -12.1],\n                            [-38.019238281249955, -12.591308593750028],\n                            [-38.401757812499994, -12.966210937500023],\n                            [-38.69096679687502, -12.623925781250009],\n                            [-38.85175781250001, -12.790136718750034],\n                            [-38.76372070312502, -12.9072265625],\n                            [-38.835302734375034, -13.147167968750026],\n                            [-39.030908203124994, -13.365136718750023],\n                            [-39.08935546875, -13.588183593749989],\n                            [-38.988623046875006, -13.61503906249996],\n                            [-39.04814453124996, -14.043945312500028],\n                            [-38.94233398437498, -14.030664062499994],\n                            [-39.05957031249997, -14.654785156249957],\n                            [-38.88061523437503, -15.864257812499972],\n                            [-39.20288085937503, -17.178125],\n                            [-39.154003906249926, -17.70390625000003],\n                            [-39.650781249999966, -18.252343750000037],\n                            [-39.78330078124998, -19.571777343749986],\n                            [-40.001367187499994, -19.74199218750003],\n                            [-40.39594726562501, -20.56943359375002],\n                            [-40.78925781250001, -20.90605468750003],\n                            [-40.954541015624926, -21.237890624999963],\n                            [-41.04726562499999, -21.505664062499974],\n                            [-41.00029296875002, -21.99902343750003],\n                            [-41.70551757812498, -22.30966796874999],\n                            [-41.980419921874955, -22.580664062499963],\n                            [-42.042382812499966, -22.947070312500003],\n                            [-42.95830078124996, -22.96708984374999],\n                            [-43.154296875, -22.725195312500006],\n                            [-43.22416992187502, -22.991210937500014],\n                            [-43.898828124999966, -23.10146484375001],\n                            [-43.97382812499998, -23.057324218749983],\n                            [-43.675976562499955, -23.00947265625001],\n                            [-43.86616210937498, -22.910546875000023],\n                            [-44.63725585937496, -23.05546875],\n                            [-44.67382812499994, -23.206640625000034],\n                            [-44.56967773437495, -23.27402343749999],\n                            [-45.32539062499998, -23.59970703124999],\n                            [-45.464306640624955, -23.802539062500017],\n                            [-45.97207031250002, -23.795507812500006],\n                            [-46.86728515624998, -24.236328125000014],\n                            [-47.989160156249994, -25.03574218749999],\n                            [-47.92939453124998, -25.16826171874999],\n                            [-48.20273437499998, -25.41650390625003],\n                            [-48.18593749999994, -25.309863281249974],\n                            [-48.402490234374994, -25.27207031249999],\n                            [-48.47612304687499, -25.44296875],\n                            [-48.73173828124993, -25.36875],\n                            [-48.6921875, -25.49150390625003],\n                            [-48.40117187500002, -25.59736328125001],\n                            [-48.665771484375, -25.844335937499963],\n                            [-48.576318359374994, -25.935449218749966],\n                            [-48.61943359374996, -26.17939453125001],\n                            [-48.74829101562503, -26.26865234374999],\n                            [-48.55415039062498, -27.195996093749997],\n                            [-48.62080078124998, -28.075585937499966],\n                            [-48.799658203125006, -28.575292968749977],\n                            [-49.27128906249999, -28.87119140625005],\n                            [-49.745996093749966, -29.363183593749994],\n                            [-50.299511718749955, -30.42578125000003],\n                            [-50.92138671874997, -31.25839843750002],\n                            [-52.039208984374994, -32.11484374999996],\n                            [-52.063232421875, -31.830371093750017],\n                            [-51.68066406249994, -31.774609375000026],\n                            [-51.272167968749955, -31.476953125000037],\n                            [-51.16142578124996, -31.11884765625001],\n                            [-50.980078125000034, -31.09423828124997],\n                            [-50.94082031249994, -30.903710937499966],\n                            [-50.68930664062495, -30.70419921874999],\n                            [-50.71630859374994, -30.425976562499983],\n                            [-50.58193359375002, -30.438867187500037],\n                            [-50.56352539062499, -30.25361328125004],\n                            [-51.02495117187493, -30.36865234375003],\n                            [-51.29804687499998, -30.03486328124997],\n                            [-51.15727539062499, -30.364257812500014],\n                            [-51.283056640625034, -30.751562499999963],\n                            [-51.35908203124998, -30.674511718749983],\n                            [-51.506298828124955, -31.104492187500014],\n                            [-51.97246093749999, -31.383789062499986],\n                            [-52.19355468749998, -31.885546874999974],\n                            [-52.12739257812501, -32.1677734375],\n                            [-52.652246093749994, -33.137792968750006],\n                            [-53.37060546874997, -33.74218750000003],\n                            [-53.39755859374995, -33.737304687500014],\n                            [-53.46357421875001, -33.70986328125002],\n                            [-53.531347656250034, -33.65546875000004],\n                            [-53.531347656250034, -33.1708984375],\n                            [-53.511865234374966, -33.10869140625003],\n                            [-53.482861328124926, -33.068554687500026],\n                            [-53.39521484375001, -33.01035156249998],\n                            [-53.31010742187499, -32.927050781249974],\n                            [-53.21406249999998, -32.82109375],\n                            [-53.12558593749998, -32.73671875],\n                            [-53.15727539062496, -32.680078125],\n                            [-53.601708984374994, -32.40302734374997],\n                            [-53.76171875, -32.05683593749997],\n                            [-53.920605468749926, -31.95234375],\n                            [-54.220556640625034, -31.855175781249997],\n                            [-54.58764648437503, -31.48515625000003],\n                            [-55.036035156249994, -31.27900390625004],\n                            [-55.091162109375034, -31.31396484374997],\n                            [-55.173535156249926, -31.279589843749974],\n                            [-55.557324218749955, -30.8759765625],\n                            [-55.60302734375003, -30.85078125000001],\n                            [-55.62714843749998, -30.858105468749997],\n                            [-55.650488281250034, -30.89208984375],\n                            [-55.66523437500001, -30.92490234375002],\n                            [-55.807763671874994, -31.036718749999977],\n                            [-55.87368164062502, -31.069628906250017],\n                            [-55.95200195312498, -31.08085937499999],\n                            [-56.0046875, -31.079199218750006],\n                            [-56.01845703125002, -30.991894531249983],\n                            [-55.998974609374955, -30.837207031250003],\n                            [-56.4072265625, -30.44746093750001],\n                            [-56.83271484374998, -30.107226562499974],\n                            [-57.120507812499994, -30.144433593749994],\n                            [-57.21445312499995, -30.283398437499983],\n                            [-57.55229492187496, -30.261230468749986],\n                            [-57.60888671875003, -30.187792968750045],\n                            [-57.563867187499994, -30.139941406249974],\n                            [-57.40522460937501, -30.03388671875004],\n                            [-57.22465820312499, -29.782128906249994],\n                            [-56.938623046874994, -29.594824218750034],\n                            [-55.890527343749994, -28.370019531249994],\n                            [-55.68725585937497, -28.38164062499996],\n                            [-55.72548828125002, -28.20410156250003],\n                            [-55.10151367187501, -27.866796874999963],\n                            [-54.82910156250003, -27.55058593750003],\n                            [-54.32700195312495, -27.423535156249997],\n                            [-53.83818359375002, -27.121093750000014],\n                            [-53.668554687500006, -26.288183593749977],\n                            [-53.89116210937499, -25.66884765625001],\n                            [-54.15458984374999, -25.523046874999963],\n                            [-54.44394531249998, -25.625],\n                            [-54.615869140624994, -25.576074218750023],\n                            [-54.61054687499998, -25.432714843750034],\n                            [-54.47314453124997, -25.22021484375],\n                            [-54.43623046875001, -25.12128906250001],\n                            [-54.281005859375, -24.30605468750001],\n                            [-54.31826171874994, -24.128125],\n                            [-54.26689453124996, -24.06582031250001],\n                            [-54.241796875, -24.047265624999966],\n                            [-54.44023437500002, -23.90175781249998],\n                            [-54.62548828125, -23.8125],\n                            [-54.98266601562494, -23.974511718749966],\n                            [-55.081884765625006, -23.997656249999977],\n                            [-55.1943359375, -24.017480468750023],\n                            [-55.28691406249993, -24.00429687499999],\n                            [-55.366308593750034, -23.99101562499996],\n                            [-55.41591796875002, -23.95136718749997],\n                            [-55.4423828125, -23.86533203125002],\n                            [-55.4423828125, -23.792578125000034],\n                            [-55.458886718749966, -23.686718750000054],\n                            [-55.51845703124994, -23.627246093750017],\n                            [-55.53828124999998, -23.580957031249994],\n                            [-55.61767578125, -22.67148437499999],\n                            [-55.74663085937499, -22.51269531249997],\n                            [-55.753271484375006, -22.410156250000043],\n                            [-55.84916992187499, -22.307617187500014],\n                            [-55.991406249999926, -22.28115234375005],\n                            [-56.18984374999994, -22.28115234375005],\n                            [-56.246044921874926, -22.26464843749997],\n                            [-56.39487304687498, -22.092675781250023],\n                            [-56.44780273437502, -22.07617187500003],\n                            [-56.77519531249999, -22.261328125],\n                            [-57.955908203125034, -22.109179687500003],\n                            [-57.94267578124999, -21.79833984375],\n                            [-57.830224609374994, -20.99794921875001],\n                            [-57.91513671874998, -20.690332031249966],\n                            [-57.97905273437493, -20.65732421874999],\n                            [-58.00224609374996, -20.465429687499977],\n                            [-58.02539062499997, -20.41582031249999],\n                            [-58.05844726562495, -20.38613281249998],\n                            [-58.091503906249926, -20.33320312500004],\n                            [-58.124609375000034, -20.293457031250014],\n                            [-58.13779296874995, -20.237304687500043],\n                            [-58.15976562499998, -20.164648437499977],\n                            [-58.09375, -20.15107421874997],\n                            [-58.021142578124994, -20.05517578124997],\n                            [-57.96015625000001, -20.04072265625004],\n                            [-57.887597656249966, -20.020410156249994],\n                            [-57.860742187499994, -19.97958984375002],\n                            [-58.029931640624994, -19.83271484375004],\n                            [-58.131494140624994, -19.74453125],\n                            [-57.71679687499997, -19.044042968750034],\n                            [-57.73085937499999, -18.91718750000004],\n                            [-57.783105468749994, -18.91425781249997],\n                            [-57.725, -18.73320312500003],\n                            [-57.57402343749993, -18.279296875000014],\n                            [-57.49565429687496, -18.21464843749999],\n                            [-57.58647460937499, -18.122265625],\n                            [-57.66166992187493, -17.94736328124999],\n                            [-57.78017578125002, -17.67177734374998],\n                            [-57.78886718750002, -17.573046875000017],\n                            [-57.83247070312501, -17.512109375000037],\n                            [-57.90502929687497, -17.53232421874999],\n                            [-57.990917968749955, -17.512890625000026],\n                            [-58.20556640625, -17.363085937499974],\n                            [-58.347753906250006, -17.282128906249994],\n                            [-58.39599609374997, -17.234277343750023],\n                            [-58.417382812499994, -17.08056640624997],\n                            [-58.459814453125006, -16.910742187500006],\n                            [-58.478125, -16.70068359375003],\n                            [-58.470605468749994, -16.650195312500045],\n                            [-58.35039062500002, -16.49082031249999],\n                            [-58.34560546875002, -16.284375],\n                            [-58.375390624999966, -16.283593749999966],\n                            [-58.423681640625034, -16.30791015625003],\n                            [-58.49658203124994, -16.32666015625003],\n                            [-58.537939453125034, -16.32822265624999],\n                            [-60.17558593749996, -16.26933593749999],\n                            [-60.187207031249955, -16.132128906250017],\n                            [-60.206640625, -15.90195312500002],\n                            [-60.242333984374994, -15.479589843750034],\n                            [-60.38046874999998, -15.318261718750023],\n                            [-60.53046874999998, -15.143164062499977],\n                            [-60.58320312499998, -15.098339843749983],\n                            [-60.273339843749994, -15.088769531249994],\n                            [-60.372705078124994, -14.41875],\n                            [-60.506591796875, -13.78984375],\n                            [-61.077001953125034, -13.489746093750014],\n                            [-61.129150390625, -13.498535156250028],\n                            [-61.41606445312502, -13.526562499999969],\n                            [-61.511572265625006, -13.541210937500011],\n                            [-61.789941406249966, -13.525585937500026],\n                            [-61.87412109374998, -13.470410156249983],\n                            [-61.944726562499966, -13.40625],\n                            [-62.09477539062499, -13.241992187499989],\n                            [-62.118017578125006, -13.15976562500002],\n                            [-62.765478515625034, -12.99726562500004],\n                            [-63.01518554687502, -12.80556640624998],\n                            [-63.067480468750006, -12.669140624999983],\n                            [-63.34667968749994, -12.68007812499999],\n                            [-63.68857421874998, -12.478027343749957],\n                            [-64.42050781249995, -12.439746093749974],\n                            [-64.783447265625, -12.059375],\n                            [-65.18574218749998, -11.749511718749957],\n                            [-65.389892578125, -11.246289062500011],\n                            [-65.33403320312499, -10.892773437500026],\n                            [-65.44711914062503, -10.507421875000034],\n                            [-65.4369140625, -10.449023437499946],\n                            [-65.39545898437498, -10.392285156250026],\n                            [-65.31308593749998, -10.253027343749991],\n                            [-65.29858398437497, -10.146777343750017],\n                            [-65.39614257812494, -9.712402343749986],\n                            [-66.57534179687502, -9.899902343749986],\n                            [-67.41694335937495, -10.389843749999969],\n                            [-67.72177734374998, -10.683105468749943],\n                            [-67.83500976562496, -10.662792968749983],\n                            [-67.99169921875, -10.674414062499949],\n                            [-68.07167968749994, -10.703125],\n                            [-68.39799804687499, -11.01875],\n                            [-68.678369140625, -11.11279296875],\n                            [-68.84833984374998, -11.01113281249998],\n                            [-69.228515625, -10.955664062499963],\n                            [-69.46254882812497, -10.948144531250023],\n                            [-69.57861328125, -10.951757812499963],\n                            [-69.67402343749998, -10.9541015625],\n                            [-69.83979492187501, -10.93339843749996],\n                            [-69.96035156249997, -10.92988281250004],\n                            [-70.06630859374997, -10.982421875],\n                            [-70.22006835937503, -11.04765625],\n                            [-70.29038085937498, -11.064257812499974],\n                            [-70.34199218750001, -11.066699218750017],\n                            [-70.39228515624995, -11.058593749999972],\n                            [-70.45087890624998, -11.024804687500009],\n                            [-70.53325195312496, -10.946875],\n                            [-70.59653320312498, -10.976855468750017],\n                            [-70.642333984375, -11.010253906249986],\n                            [-70.59916992187499, -9.620507812500009],\n                            [-70.54111328124998, -9.4375],\n                            [-70.60791015625, -9.463671875000031],\n                            [-70.63691406249995, -9.478222656249969],\n                            [-71.041748046875, -9.81875],\n                            [-71.11528320312499, -9.852441406250009],\n                            [-71.33940429687499, -9.988574218750031],\n                            [-72.18159179687495, -10.003710937500003],\n                            [-72.37905273437497, -9.51015625],\n                            [-73.20942382812493, -9.411425781249946],\n                            [-73.08984375, -9.26572265625002],\n                            [-72.970361328125, -9.120117187500028],\n                            [-72.97402343750002, -8.9931640625],\n                            [-73.07050781249995, -8.8828125],\n                            [-73.203125, -8.719335937499991],\n                            [-73.30244140624995, -8.654003906250011],\n                            [-73.36040039062496, -8.479296875000031],\n                            [-73.39814453125001, -8.458984374999986],\n                            [-73.54912109374993, -8.34580078125002],\n                            [-73.73203125, -7.875390625],\n                            [-73.72041015624993, -7.782519531250017],\n                            [-73.76689453124999, -7.753515624999963],\n                            [-73.82207031249996, -7.738964843750026],\n                            [-73.89462890624998, -7.654785156250014],\n                            [-73.946875, -7.611230468750023],\n                            [-73.98173828124996, -7.58505859375002],\n                            [-74.00205078125003, -7.556054687499966],\n                            [-73.98173828124996, -7.535742187500006],\n                            [-73.95849609374994, -7.506640625000031],\n                            [-73.96430664062498, -7.378906250000028],\n                            [-73.74946289062498, -7.335351562500037],\n                            [-73.72041015624993, -7.309277343749969],\n                            [-73.758203125, -7.172753906249952],\n                            [-73.79301757812499, -7.135058593750003],\n                            [-73.75810546874999, -6.90576171875],\n                            [-73.137353515625, -6.4658203125],\n                            [-73.23554687500001, -6.098437500000017],\n                            [-73.209375, -6.028710937500023],\n                            [-73.16289062499996, -5.933398437499974],\n                            [-72.97988281249997, -5.634863281249991],\n                            [-72.88706054687498, -5.122753906250026],\n                            [-72.83193359374994, -5.09375],\n                            [-72.69873046874997, -5.067187499999989],\n                            [-72.60834960937495, -5.009570312499974],\n                            [-72.46899414062497, -4.901269531250023],\n                            [-72.35283203124993, -4.786035156249994],\n                            [-72.25678710937501, -4.74892578124998],\n                            [-71.8447265625, -4.504394531249986],\n                            [-70.97368164062499, -4.350488281249994],\n                            [-70.86601562499999, -4.229589843749963],\n                            [-70.79951171874995, -4.173339843749957],\n                            [-70.72158203124997, -4.15888671875004],\n                            [-70.53066406249997, -4.167578125000034],\n                            [-70.40463867187498, -4.150097656250026],\n                            [-70.34365234375, -4.193652343750017],\n                            [-70.31689453124994, -4.246972656250037],\n                            [-70.23916015625002, -4.30117187499998],\n                            [-70.12880859375, -4.286621093749943],\n                            [-70.05332031249998, -4.333105468750006],\n                            [-70.00395507812496, -4.327246093749963],\n                            [-69.97202148437503, -4.30117187499998],\n                            [-69.96591796875003, -4.2359375],\n                            [-69.94819335937498, -4.200585937500009],\n                            [-69.66904296875003, -2.667675781249997],\n                            [-69.40024414062498, -1.194921874999977],\n                            [-69.63398437500001, -0.50927734375],\n                            [-70.07050781249993, -0.13886718750004],\n                            [-70.05390624999993, 0.578613281250028],\n                            [-69.47211914062498, 0.72993164062504],\n                            [-69.15332031249994, 0.65878906250002],\n                            [-69.31181640624999, 1.050488281249969],\n                            [-69.85214843750003, 1.05952148437504],\n                            [-69.84858398437493, 1.708740234375043],\n                            [-68.17656249999999, 1.719824218749991],\n                            [-68.25595703125, 1.845507812500017],\n                            [-68.19379882812495, 1.987011718749983],\n                            [-67.93623046874998, 1.748486328124969],\n                            [-67.40043945312499, 2.116699218750028],\n                            [-67.11923828124998, 1.703613281249986],\n                            [-67.082275390625, 1.185400390625006],\n                            [-66.87602539062499, 1.223046875000037],\n                            [-66.34711914062498, 0.7671875],\n                            [-66.06005859375003, 0.78535156250004],\n                            [-65.68144531249999, 0.983447265624989],\n                            [-65.52299804687493, 0.843408203124966],\n                            [-65.55605468750002, 0.687988281250014],\n                            [-65.47338867187497, 0.691259765624977],\n                            [-65.10375976562497, 1.108105468749983],\n                            [-64.20502929687493, 1.52949218750004],\n                            [-64.00849609374995, 1.931591796874969],\n                            [-63.43251953124994, 2.155566406250045],\n                            [-63.389257812500006, 2.411914062500045],\n                            [-64.04658203124998, 2.502392578124997],\n                            [-64.22109375000002, 3.587402343749972],\n                            [-64.66899414062496, 4.01181640625002],\n                            [-64.788671875, 4.276025390625023],\n                            [-64.57636718750001, 4.139892578125],\n                            [-64.19248046874995, 4.126855468750009],\n                            [-64.02148437500003, 3.929101562500051],\n                            [-63.33867187500002, 3.943896484375045],\n                            [-62.85698242187502, 3.593457031249969],\n                            [-62.71210937499998, 4.01791992187502],\n                            [-62.41064453124994, 4.156738281249972],\n                            [-62.153125, 4.098388671874986],\n                            [-61.82084960937496, 4.197021484375],\n                            [-61.28007812500002, 4.516894531249974],\n                            [-61.00283203125002, 4.535253906249991],\n                            [-60.603857421875006, 4.94936523437498],\n                            [-60.671972656250034, 5.164355468749989],\n                            [-60.71196289062499, 5.191552734375023],\n                            [-60.742138671874926, 5.202050781250037],\n                            [-60.6513671875, 5.221142578125011],\n                            [-60.45952148437499, 5.188085937500034],\n                            [-60.40878906249998, 5.21015625],\n                            [-60.33520507812497, 5.199316406250006],\n                            [-60.241650390624926, 5.257958984374966],\n                            [-60.14204101562498, 5.238818359374974],\n                            [-59.990673828124955, 5.082861328124991],\n                            [-60.14863281249998, 4.533251953125031],\n                            [-59.69970703125, 4.353515625]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Brazil\", \"childNum\": 17 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-59.493310546874994, 13.081982421874997],\n                        [-59.611328125, 13.102099609374989],\n                        [-59.6466796875, 13.303125],\n                        [-59.427636718749994, 13.152783203124997],\n                        [-59.493310546874994, 13.081982421874997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Barbados\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [115.02675781250005, 4.899707031249989],\n                            [115.1400390625, 4.899755859374991],\n                            [115.290625, 4.352587890624989],\n                            [115.10703125000006, 4.390429687499974],\n                            [115.02675781250005, 4.899707031249989]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.02675781250005, 4.899707031249989],\n                            [114.74667968750006, 4.718066406250017],\n                            [114.84023437500005, 4.393212890625009],\n                            [114.65410156250007, 4.037646484375045],\n                            [114.0638671875, 4.592675781249966],\n                            [114.42441406250006, 4.660400390625],\n                            [114.99541015625002, 5.022363281250023],\n                            [115.02675781250005, 4.899707031249989]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Brunei\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [91.63193359375003, 27.759960937499997],\n                        [91.5947265625, 27.557666015624996],\n                        [91.74306640625002, 27.442529296874994],\n                        [91.85126953125001, 27.438623046874994],\n                        [91.95097656249999, 27.458300781249996],\n                        [91.99082031250003, 27.4501953125],\n                        [92.044921875, 27.364697265624997],\n                        [92.08339843750002, 27.290625],\n                        [92.03115234375002, 27.214306640624997],\n                        [92.00253906250003, 27.147363281249994],\n                        [91.99228515625003, 27.099902343749996],\n                        [91.99863281250003, 27.079296875],\n                        [92.03085937500003, 27.040820312499996],\n                        [92.06816406249999, 26.9751953125],\n                        [92.07343750000001, 26.91484375],\n                        [92.04970703125002, 26.874853515625],\n                        [91.99833984374999, 26.85498046875],\n                        [91.84208984374999, 26.852978515624997],\n                        [91.67158203125001, 26.802001953125],\n                        [91.517578125, 26.807324218749997],\n                        [91.45585937499999, 26.866894531249997],\n                        [91.4267578125, 26.867089843749994],\n                        [91.28652343750002, 26.789941406249994],\n                        [90.73964843750002, 26.771679687499997],\n                        [90.34589843750001, 26.890332031249997],\n                        [90.2060546875, 26.847509765625],\n                        [90.12294921875002, 26.754589843749997],\n                        [89.94316406249999, 26.723925781249996],\n                        [89.76386718750001, 26.7015625],\n                        [89.60996093750003, 26.719433593749997],\n                        [89.58613281250001, 26.778955078124994],\n                        [89.33212890625003, 26.8486328125],\n                        [89.14824218749999, 26.816162109375],\n                        [89.04091796875002, 26.865039062499996],\n                        [88.85761718750001, 26.961474609374996],\n                        [88.73876953125, 27.175585937499996],\n                        [88.76035156250003, 27.218115234375],\n                        [88.88164062499999, 27.2974609375],\n                        [88.89140624999999, 27.316064453124994],\n                        [88.94755859374999, 27.464013671874994],\n                        [89.48066406250001, 28.059960937499994],\n                        [89.53691406249999, 28.107421875],\n                        [89.65273437500002, 28.15830078125],\n                        [89.74980468749999, 28.188183593749997],\n                        [89.81689453125, 28.256298828124997],\n                        [89.89785156250002, 28.294140625],\n                        [89.98105468750003, 28.311181640624994],\n                        [90.34824218750003, 28.243945312499996],\n                        [90.36298828125001, 28.216503906249997],\n                        [90.33310546875003, 28.093994140625],\n                        [90.35273437500001, 28.080224609374994],\n                        [90.47734374999999, 28.070849609374996],\n                        [90.63007812500001, 28.078564453124997],\n                        [90.71572265625002, 28.071728515624997],\n                        [91.02080078124999, 27.970068359375],\n                        [91.07773437500003, 27.974462890625],\n                        [91.22587890624999, 28.071240234374997],\n                        [91.27304687500003, 28.078369140625],\n                        [91.30683593750001, 28.064013671874996],\n                        [91.36757812500002, 28.021630859374994],\n                        [91.64189453124999, 27.923242187499994],\n                        [91.63193359375003, 27.759960937499997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Bhutan\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [25.2587890625, -17.793554687500006],\n                        [25.242285156250006, -17.969042968750003],\n                        [25.939355468750023, -18.93867187500001],\n                        [26.168066406250006, -19.53828125000001],\n                        [27.17822265625, -20.10097656250001],\n                        [27.28076171875, -20.47871093750001],\n                        [27.679296875, -20.503027343750006],\n                        [27.66943359375, -21.064257812500003],\n                        [28.014062500000023, -21.55419921875],\n                        [29.02558593750001, -21.796875],\n                        [29.042382812500023, -22.018359375],\n                        [29.237207031250023, -22.07949218750001],\n                        [29.315234375000017, -22.15771484375],\n                        [29.36484375, -22.193945312500006],\n                        [29.1298828125, -22.21328125],\n                        [29.013476562500017, -22.278417968750006],\n                        [28.94580078125, -22.395117187500006],\n                        [28.83984375, -22.480859375],\n                        [28.21015625000001, -22.693652343750003],\n                        [27.812597656250006, -23.108007812500006],\n                        [27.7685546875, -23.14892578125],\n                        [27.085546875, -23.577929687500003],\n                        [26.835058593750006, -24.240820312500006],\n                        [26.617773437500006, -24.3955078125],\n                        [26.451757812500006, -24.58271484375001],\n                        [26.39716796875001, -24.613574218750003],\n                        [26.130859375, -24.671484375],\n                        [26.031835937500006, -24.702441406250003],\n                        [25.912109375, -24.74746093750001],\n                        [25.518164062500006, -25.66279296875001],\n                        [25.21337890625, -25.75625],\n                        [24.33056640625, -25.74287109375001],\n                        [24.19296875, -25.632910156250006],\n                        [23.969531250000017, -25.626074218750006],\n                        [23.89375, -25.600878906250003],\n                        [23.389257812500006, -25.29140625],\n                        [23.148730468750017, -25.288671875],\n                        [22.878808593750023, -25.45791015625001],\n                        [22.59765625, -26.13271484375001],\n                        [22.548632812500017, -26.17841796875001],\n                        [22.47089843750001, -26.219042968750003],\n                        [22.217578125000017, -26.38886718750001],\n                        [22.090917968750006, -26.580175781250006],\n                        [22.01093750000001, -26.635839843750006],\n                        [21.78828125000001, -26.710058593750006],\n                        [21.738085937500017, -26.80683593750001],\n                        [21.694726562500023, -26.840917968750006],\n                        [20.73984375, -26.84882812500001],\n                        [20.641406250000017, -26.7421875],\n                        [20.79316406250001, -25.915625],\n                        [20.4306640625, -25.147070312500006],\n                        [19.98046875, -24.77675781250001],\n                        [19.977343750000017, -22.00019531250001],\n                        [20.9794921875, -21.9619140625],\n                        [20.97412109375, -18.31884765625],\n                        [23.219335937500006, -17.99970703125001],\n                        [23.599707031250006, -18.4599609375],\n                        [24.243945312500017, -18.0234375],\n                        [24.530566406250017, -18.052734375],\n                        [24.909082031250023, -17.821386718750006],\n                        [25.2587890625, -17.793554687500006]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Botswana\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [22.86005859375001, 10.919677734375],\n                        [23.646289062500017, 9.822900390624994],\n                        [23.62265625, 9.340625],\n                        [23.46826171875, 9.11474609375],\n                        [23.53730468750001, 8.815820312499994],\n                        [24.147363281250023, 8.665625],\n                        [24.291406250000023, 8.29140625],\n                        [24.853320312500017, 8.137548828124991],\n                        [25.20039062500001, 7.807910156249989],\n                        [25.18134765625001, 7.557226562499991],\n                        [25.27890625, 7.427490234375],\n                        [26.36181640625, 6.635302734374989],\n                        [26.30859375, 6.455322265625],\n                        [26.514257812500006, 6.069238281249994],\n                        [27.143945312500023, 5.722949218749989],\n                        [27.4033203125, 5.109179687499989],\n                        [27.071875, 5.199755859374989],\n                        [26.822070312500017, 5.062402343749994],\n                        [25.52509765625001, 5.31210937499999],\n                        [25.065234375000017, 4.967431640624994],\n                        [24.31982421875, 4.994140625],\n                        [23.41718750000001, 4.663134765624989],\n                        [22.864550781250017, 4.723876953125],\n                        [22.422167968750017, 4.134960937499997],\n                        [20.55810546875, 4.462695312499989],\n                        [20.226367187500017, 4.829638671874989],\n                        [19.806542968750023, 5.089306640624997],\n                        [19.5009765625, 5.127490234374989],\n                        [19.06855468750001, 4.891406249999989],\n                        [18.594140625000023, 4.346240234374989],\n                        [18.6103515625, 3.478417968749994],\n                        [18.474414062500017, 3.622998046874997],\n                        [18.160937500000017, 3.499804687499989],\n                        [17.491601562500023, 3.687304687499989],\n                        [16.610742187500023, 3.50537109375],\n                        [16.468554687500017, 2.831738281249997],\n                        [16.183398437500017, 2.270068359374989],\n                        [16.0634765625, 2.90859375],\n                        [15.128710937500017, 3.826904296875],\n                        [15.063574218750006, 4.284863281249997],\n                        [14.73125, 4.602392578124991],\n                        [14.56298828125, 5.279931640624994],\n                        [14.616894531250011, 5.865136718749994],\n                        [14.43115234375, 6.038720703124994],\n                        [14.7392578125, 6.27978515625],\n                        [15.206738281250011, 7.206152343749991],\n                        [15.480078125, 7.523779296874991],\n                        [15.957617187500006, 7.507568359375],\n                        [16.37890625, 7.683544921874997],\n                        [16.545312500000023, 7.865478515625],\n                        [16.784765625, 7.550976562499997],\n                        [17.6494140625, 7.98359375],\n                        [18.56416015625001, 8.0458984375],\n                        [19.108691406250017, 8.656152343749994],\n                        [18.886035156250017, 8.836035156249991],\n                        [18.95625, 8.938867187499994],\n                        [20.342089843750017, 9.127099609374994],\n                        [20.773242187500017, 9.405664062499994],\n                        [21.682714843750006, 10.289843749999989],\n                        [21.771484375, 10.642822265625],\n                        [22.49384765625001, 10.996240234374994],\n                        [22.86005859375001, 10.919677734375]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Central African Rep.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-59.78759765624997, 43.939599609374994],\n                            [-60.11748046874996, 43.95336914062506],\n                            [-59.727148437500006, 44.002832031249994],\n                            [-59.78759765624997, 43.939599609374994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-66.7625, 44.68178710937502],\n                            [-66.8970703125, 44.62890625],\n                            [-66.80214843749994, 44.80537109374998],\n                            [-66.7625, 44.68178710937502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-60.961572265624966, 45.48994140625001],\n                            [-61.081738281249926, 45.55781249999998],\n                            [-60.91245117187498, 45.56728515625005],\n                            [-60.961572265624966, 45.48994140625001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-73.69531249999997, 45.58549804687502],\n                            [-73.85771484375002, 45.573583984375006],\n                            [-73.57236328124998, 45.69448242187502],\n                            [-73.69531249999997, 45.58549804687502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-73.56650390625003, 45.469091796875034],\n                            [-73.960546875, 45.44140624999997],\n                            [-73.68745117187498, 45.561425781249994],\n                            [-73.47607421874997, 45.704736328124994],\n                            [-73.56650390625003, 45.469091796875034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-61.10517578124998, 45.94472656250002],\n                            [-60.86523437499997, 45.983496093750006],\n                            [-61.05903320312501, 45.70336914062497],\n                            [-60.73789062499995, 45.75141601562498],\n                            [-60.46059570312494, 45.96870117187501],\n                            [-60.733300781249994, 45.956591796875045],\n                            [-60.297949218750034, 46.31123046874998],\n                            [-60.22646484374994, 46.19555664062506],\n                            [-59.86503906249993, 46.159521484375006],\n                            [-59.8421875, 45.941552734374994],\n                            [-60.67294921874995, 45.59082031250006],\n                            [-61.28369140624994, 45.573876953124966],\n                            [-61.44980468749995, 45.71621093750002],\n                            [-61.40864257812501, 46.17036132812498],\n                            [-60.87016601562499, 46.796777343749966],\n                            [-60.40820312500003, 47.00351562499998],\n                            [-60.332910156249966, 46.737011718749955],\n                            [-60.49453125000002, 46.270263671875],\n                            [-61.10517578124998, 45.94472656250002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-63.811279296875, 46.46870117187501],\n                            [-63.68144531249993, 46.561914062499994],\n                            [-63.12939453125, 46.422216796875034],\n                            [-62.02373046874999, 46.42158203125001],\n                            [-62.52607421875001, 46.20288085937503],\n                            [-62.531347656250034, 45.977294921875],\n                            [-63.02207031249998, 46.06660156249998],\n                            [-62.89453125000003, 46.12358398437496],\n                            [-63.056347656249955, 46.22392578124996],\n                            [-62.97846679687498, 46.31635742187498],\n                            [-63.21347656249998, 46.15986328124998],\n                            [-63.641015624999966, 46.23046874999997],\n                            [-63.758642578125034, 46.397607421874994],\n                            [-64.11083984375003, 46.425439453124994],\n                            [-64.13603515624999, 46.59970703125006],\n                            [-64.388037109375, 46.640869140625],\n                            [-63.99355468750002, 47.06157226562502],\n                            [-64.08789062499997, 46.77543945312499],\n                            [-63.811279296875, 46.46870117187501]\n                        ]\n                    ],\n                    [\n                        [\n                            [-61.91411132812496, 47.284521484375034],\n                            [-61.77255859374998, 47.25981445312499],\n                            [-62.00830078124994, 47.23427734375002],\n                            [-61.924707031249966, 47.425146484375006],\n                            [-61.3955078125, 47.63764648437504],\n                            [-61.91411132812496, 47.284521484375034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-54.227148437500034, 47.44135742187501],\n                            [-54.32597656250002, 47.408105468749994],\n                            [-54.12817382812494, 47.646826171875034],\n                            [-54.227148437500034, 47.44135742187501]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.70888671874997, 45.0038574218751],\n                            [-73.55810546875, 45.425097656250045],\n                            [-73.1595703125, 46.01005859375002],\n                            [-72.10927734374997, 46.55122070312504],\n                            [-71.26118164062495, 46.75625],\n                            [-70.51948242187501, 47.032519531250045],\n                            [-69.47104492187503, 47.96728515625006],\n                            [-68.23818359374994, 48.62641601562504],\n                            [-66.17817382812493, 49.21313476562503],\n                            [-64.83632812499994, 49.191748046875006],\n                            [-64.2162109375, 48.873632812500034],\n                            [-64.51372070312493, 48.84111328124999],\n                            [-64.24609374999994, 48.69111328124998],\n                            [-64.34882812500001, 48.423193359375034],\n                            [-65.259423828125, 48.02124023437503],\n                            [-65.92670898437495, 48.188867187499994],\n                            [-66.70439453125002, 48.0224609375],\n                            [-66.35961914062494, 48.06064453125006],\n                            [-65.84941406250002, 47.91103515625005],\n                            [-65.60722656249996, 47.67001953125006],\n                            [-65.00166015624995, 47.84682617187502],\n                            [-64.70322265625, 47.72485351562503],\n                            [-64.91220703125003, 47.36865234375003],\n                            [-65.31889648437502, 47.101220703124994],\n                            [-64.831396484375, 47.06079101562503],\n                            [-64.88251953124993, 46.822851562500034],\n                            [-64.54150390625, 46.240332031250034],\n                            [-63.91591796875002, 46.165820312500045],\n                            [-63.831933593749966, 46.107177734375],\n                            [-64.05639648437503, 46.021337890625006],\n                            [-63.70288085937494, 45.858007812500034],\n                            [-62.70068359374997, 45.740576171875006],\n                            [-62.750097656250006, 45.64824218750002],\n                            [-62.483056640624966, 45.62182617187506],\n                            [-61.955517578124955, 45.86816406249997],\n                            [-61.776513671874994, 45.655615234375006],\n                            [-61.49228515624998, 45.68701171875],\n                            [-61.350488281249966, 45.57368164062501],\n                            [-61.28198242187494, 45.441064453124994],\n                            [-61.46098632812502, 45.36669921875003],\n                            [-61.03154296875002, 45.29174804687506],\n                            [-63.306298828124994, 44.64257812500003],\n                            [-63.60400390624997, 44.68320312500006],\n                            [-63.60976562499999, 44.47998046875006],\n                            [-63.999707031249926, 44.64492187499999],\n                            [-64.10087890624993, 44.487451171874966],\n                            [-64.1669921875, 44.58666992187503],\n                            [-64.28608398437493, 44.55034179687499],\n                            [-64.27568359374993, 44.33408203124998],\n                            [-65.48168945312497, 43.51806640625],\n                            [-65.73813476562498, 43.56074218750001],\n                            [-65.88691406250001, 43.79521484374999],\n                            [-66.125732421875, 43.813818359375034],\n                            [-66.19306640624995, 44.143847656250045],\n                            [-65.86801757812498, 44.56879882812501],\n                            [-66.14638671875002, 44.43593750000005],\n                            [-66.090625, 44.50493164062499],\n                            [-64.44814453125, 45.33745117187502],\n                            [-64.13549804687497, 45.023046875],\n                            [-64.09316406249997, 45.21708984375002],\n                            [-63.368017578125034, 45.36479492187502],\n                            [-64.87314453124998, 45.35458984375006],\n                            [-64.31464843749998, 45.83569335937503],\n                            [-64.48222656250002, 45.80634765624998],\n                            [-64.63271484375002, 45.94663085937506],\n                            [-64.77851562499998, 45.63842773437497],\n                            [-65.88447265624995, 45.22290039062506],\n                            [-66.10976562500002, 45.316601562499955],\n                            [-66.02656249999995, 45.417578125],\n                            [-66.43984374999994, 45.09589843750001],\n                            [-66.87246093749997, 45.067285156249966],\n                            [-67.12485351562498, 45.16943359375],\n                            [-67.366943359375, 45.17377929687498],\n                            [-67.43266601562496, 45.603125],\n                            [-67.80224609374994, 45.7275390625],\n                            [-67.806787109375, 47.08281249999999],\n                            [-68.23549804687502, 47.34594726562503],\n                            [-68.93720703124998, 47.21123046875002],\n                            [-69.0501953125, 47.426611328125034],\n                            [-69.24287109374998, 47.46298828124998],\n                            [-70.00771484375002, 46.70893554687501],\n                            [-70.296240234375, 45.90610351562506],\n                            [-70.86503906249999, 45.27070312500001],\n                            [-71.327294921875, 45.29008789062496],\n                            [-71.51752929687495, 45.00756835937497],\n                            [-74.663232421875, 45.00390625000003],\n                            [-74.70888671874997, 45.0038574218751]\n                        ]\n                    ],\n                    [\n                        [\n                            [-126.09208984374995, 49.35400390625003],\n                            [-126.06401367187499, 49.26362304687501],\n                            [-126.22963867187498, 49.29565429687506],\n                            [-126.09208984374995, 49.35400390625003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-54.55439453125001, 49.5888671875],\n                            [-54.786523437499966, 49.496142578125045],\n                            [-54.86357421875002, 49.576074218749966],\n                            [-54.55439453125001, 49.5888671875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-54.093701171874955, 49.74443359374999],\n                            [-53.98066406250001, 49.66196289062498],\n                            [-54.28613281249997, 49.595361328124994],\n                            [-54.27763671875002, 49.71147460937502],\n                            [-54.093701171874955, 49.74443359374999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-126.64121093749999, 49.605810546875006],\n                            [-126.93857421874999, 49.71845703125004],\n                            [-126.92583007812497, 49.837744140625006],\n                            [-126.73813476562502, 49.84365234375005],\n                            [-126.64121093749999, 49.605810546875006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-61.801123046875034, 49.093896484374966],\n                            [-63.04150390624994, 49.224951171875034],\n                            [-64.485205078125, 49.88696289062497],\n                            [-64.13144531249995, 49.94165039062503],\n                            [-62.858544921874966, 49.70546875000005],\n                            [-61.817138671875, 49.28354492187498],\n                            [-61.69614257812495, 49.139013671875006],\n                            [-61.801123046875034, 49.093896484374966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-125.18413085937497, 50.09711914062498],\n                            [-125.301171875, 50.4140625],\n                            [-125.07402343750002, 50.22065429687501],\n                            [-125.18413085937497, 50.09711914062498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-127.19731445312495, 50.640380859375],\n                            [-125.48208007812501, 50.316796874999966],\n                            [-124.83061523437499, 49.53007812500002],\n                            [-123.99580078125, 49.22402343750002],\n                            [-123.49702148437498, 48.58208007812499],\n                            [-123.38989257812501, 48.67021484374999],\n                            [-123.31064453125003, 48.41103515625002],\n                            [-123.57314453124995, 48.32280273437499],\n                            [-123.91694335937501, 48.386572265625034],\n                            [-125.12070312500002, 48.76079101562496],\n                            [-124.84965820312496, 49.02827148437501],\n                            [-124.81264648437497, 49.212646484375],\n                            [-124.92734374999998, 49.01420898437499],\n                            [-125.489453125, 48.933789062499955],\n                            [-125.82851562499998, 49.09184570312499],\n                            [-125.64423828125001, 49.18579101562506],\n                            [-125.95166015625001, 49.24804687500003],\n                            [-125.93540039062499, 49.401464843750006],\n                            [-126.51914062499999, 49.396777343750045],\n                            [-126.54189453125001, 49.590478515624966],\n                            [-126.13408203124997, 49.672314453124955],\n                            [-126.52524414062499, 49.71958007812498],\n                            [-126.90332031250001, 49.94414062499999],\n                            [-127.114306640625, 49.879736328125034],\n                            [-127.24980468749999, 50.13798828124996],\n                            [-127.34941406249995, 50.05195312500001],\n                            [-127.46713867187503, 50.163427734375006],\n                            [-127.86391601562495, 50.12773437500002],\n                            [-127.90585937499998, 50.44521484375002],\n                            [-127.48652343749998, 50.404638671875034],\n                            [-127.46591796874996, 50.58310546875006],\n                            [-128.05834960937494, 50.498486328124955],\n                            [-128.34604492187503, 50.744238281250006],\n                            [-127.91806640624998, 50.86054687500001],\n                            [-127.19731445312495, 50.640380859375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-55.45874023437494, 51.53652343750005],\n                            [-55.58339843749994, 51.38857421875002],\n                            [-56.031103515625034, 51.328369140625],\n                            [-55.8, 51.033300781250034],\n                            [-56.732324218749966, 50.007714843749994],\n                            [-56.822167968749966, 49.613476562499955],\n                            [-56.179394531249955, 50.114990234375],\n                            [-56.161279296874994, 49.94013671874998],\n                            [-55.50292968749997, 49.98315429687503],\n                            [-56.14018554687496, 49.61914062500006],\n                            [-55.869824218749955, 49.67016601562506],\n                            [-56.08730468750002, 49.45195312499999],\n                            [-55.375927734374955, 49.48974609374997],\n                            [-55.34384765624998, 49.37290039062506],\n                            [-55.22954101562496, 49.508154296875006],\n                            [-55.35317382812502, 49.07944335937506],\n                            [-54.50219726562503, 49.52734375],\n                            [-54.44824218749997, 49.329443359375006],\n                            [-53.957714843749955, 49.44184570312498],\n                            [-53.61943359374996, 49.321630859375006],\n                            [-53.57343750000001, 49.141210937500034],\n                            [-54.16127929687494, 48.787695312500034],\n                            [-53.852880859375006, 48.81132812499996],\n                            [-53.966015624999955, 48.70668945312505],\n                            [-53.70634765624999, 48.65551757812503],\n                            [-54.11445312499998, 48.393603515625045],\n                            [-53.027587890625, 48.634716796874955],\n                            [-53.1357421875, 48.40185546875003],\n                            [-53.60976562500002, 48.20771484375001],\n                            [-53.56943359374998, 48.088085937499955],\n                            [-53.869580078124926, 48.019677734374966],\n                            [-53.63823242187496, 48.01464843750003],\n                            [-53.863671874999966, 47.787011718749994],\n                            [-53.67236328125, 47.64824218749999],\n                            [-53.28271484375, 47.99785156249996],\n                            [-52.86601562499993, 48.11298828124998],\n                            [-53.16982421875002, 47.51210937500005],\n                            [-52.945019531249955, 47.55283203124998],\n                            [-52.782421874999955, 47.769433593749966],\n                            [-52.653662109375034, 47.549414062500006],\n                            [-53.11484375, 46.65581054687502],\n                            [-53.32304687499996, 46.71835937499998],\n                            [-53.589794921874955, 46.638867187499955],\n                            [-53.59736328124998, 47.14599609374997],\n                            [-54.00957031249993, 46.839599609375],\n                            [-54.173730468749994, 46.88037109375003],\n                            [-53.84951171875002, 47.440332031249994],\n                            [-53.98901367187503, 47.756201171875034],\n                            [-54.191845703124955, 47.85981445312501],\n                            [-54.488134765625006, 47.40385742187502],\n                            [-54.47392578124996, 47.54707031249998],\n                            [-54.856640624999955, 47.385009765625],\n                            [-55.31572265624993, 46.905712890624955],\n                            [-55.78852539062498, 46.86723632812502],\n                            [-55.91923828124996, 47.01689453124996],\n                            [-55.49150390624996, 47.16064453125003],\n                            [-54.78461914062501, 47.664746093749955],\n                            [-55.366308593750034, 47.66108398437501],\n                            [-55.57612304687498, 47.46523437499999],\n                            [-56.12724609374999, 47.50283203125002],\n                            [-55.867089843749994, 47.592333984375045],\n                            [-55.85791015625, 47.81918945312498],\n                            [-56.774121093749955, 47.56499023437499],\n                            [-58.33686523437501, 47.73085937500002],\n                            [-59.11694335937494, 47.57070312499999],\n                            [-59.32065429687498, 47.736914062500006],\n                            [-59.272070312500034, 47.99555664062504],\n                            [-58.330224609374994, 48.52211914062502],\n                            [-59.16767578124998, 48.558496093749966],\n                            [-58.84179687500003, 48.74643554687498],\n                            [-58.906445312499955, 48.65019531249999],\n                            [-58.716455078124994, 48.59804687500002],\n                            [-58.403662109375034, 49.08432617187498],\n                            [-57.99052734374996, 48.987939453124966],\n                            [-58.09892578124993, 49.07744140624999],\n                            [-57.98007812499998, 49.229638671874994],\n                            [-58.19091796875003, 49.25874023437498],\n                            [-58.21337890625, 49.38666992187501],\n                            [-58.01582031249998, 49.54248046874997],\n                            [-57.79130859374999, 49.48999023437503],\n                            [-57.92617187499999, 49.700830078124994],\n                            [-57.4326171875, 50.50581054687504],\n                            [-57.179589843749966, 50.614843750000034],\n                            [-57.29799804687502, 50.69873046874997],\n                            [-57.03593750000002, 51.01083984374998],\n                            [-56.68242187500002, 51.332763671875],\n                            [-56.025585937499955, 51.56835937500006],\n                            [-55.6904296875, 51.471337890624994],\n                            [-55.666406249999966, 51.57890624999999],\n                            [-55.45874023437494, 51.53652343750005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-127.92465820312498, 51.47387695312497],\n                            [-128.14877929687498, 51.62670898437503],\n                            [-128.03173828125006, 51.708398437499966],\n                            [-127.92465820312498, 51.47387695312497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.38427734374997, 51.951953125000045],\n                            [-79.64375, 52.01005859374996],\n                            [-79.27128906249996, 52.086816406249966],\n                            [-79.38427734374997, 51.951953125000045]\n                        ]\n                    ],\n                    [\n                        [\n                            [-128.36875, 52.40087890625],\n                            [-128.43979492187503, 52.696386718750006],\n                            [-128.24726562499998, 52.784375],\n                            [-128.36875, 52.40087890625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-80.73168945312494, 52.74726562499998],\n                            [-82.03925781249998, 53.04990234374998],\n                            [-81.84731445312494, 53.18627929687497],\n                            [-81.135595703125, 53.20581054687503],\n                            [-80.73168945312494, 52.74726562499998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-131.7537109375, 53.195556640625],\n                            [-131.63466796874997, 52.92216796874999],\n                            [-131.97177734374998, 52.87983398437498],\n                            [-131.45522460937502, 52.70170898437502],\n                            [-131.59057617187494, 52.578222656250006],\n                            [-131.25971679687495, 52.415917968749966],\n                            [-131.31992187499998, 52.30307617187498],\n                            [-131.142626953125, 52.291113281250034],\n                            [-131.221533203125, 52.15361328124999],\n                            [-132.16508789062493, 52.783300781250034],\n                            [-132.14375, 52.99931640624999],\n                            [-132.54677734374997, 53.1375],\n                            [-131.7537109375, 53.195556640625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-128.55244140624998, 52.93974609375002],\n                            [-128.50991210937502, 52.51860351562502],\n                            [-128.678955078125, 52.289648437500006],\n                            [-128.74633789062494, 52.763378906249955],\n                            [-128.89980468749997, 52.67382812500003],\n                            [-129.175927734375, 52.964941406250006],\n                            [-129.033251953125, 53.27993164062505],\n                            [-128.63266601562498, 53.1125],\n                            [-128.55244140624998, 52.93974609375002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-129.167724609375, 53.11787109374998],\n                            [-129.32387695312502, 53.142138671875045],\n                            [-129.23818359374997, 53.33007812500006],\n                            [-129.167724609375, 53.11787109374998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-129.84858398437498, 53.167919921874955],\n                            [-130.51757812500003, 53.54423828124999],\n                            [-130.45200195312498, 53.63115234375002],\n                            [-129.94472656250002, 53.436376953125034],\n                            [-129.75483398437498, 53.244775390624994],\n                            [-129.84858398437498, 53.167919921874955]\n                        ]\n                    ],\n                    [\n                        [\n                            [-130.236279296875, 53.95854492187502],\n                            [-130.38422851562504, 53.84394531250001],\n                            [-130.703173828125, 53.892236328124994],\n                            [-130.44799804687497, 54.08901367187502],\n                            [-130.236279296875, 53.95854492187502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-132.65551757812503, 54.12749023437496],\n                            [-132.30336914062497, 54.098876953125],\n                            [-132.16611328124998, 53.95522460937505],\n                            [-132.53466796875, 53.651708984375034],\n                            [-132.18696289062504, 53.68481445312503],\n                            [-132.134423828125, 54.03427734374998],\n                            [-131.66762695312502, 54.14135742187503],\n                            [-131.957421875, 53.308691406250034],\n                            [-132.34726562500003, 53.18920898437503],\n                            [-132.747509765625, 53.310498046874955],\n                            [-132.425, 53.33696289062502],\n                            [-132.84501953125, 53.507714843749994],\n                            [-133.07949218749997, 53.837011718750034],\n                            [-133.04838867187493, 54.15893554687497],\n                            [-132.65551757812503, 54.12749023437496]\n                        ]\n                    ],\n                    [\n                        [\n                            [-130.92714843749997, 54.47905273437499],\n                            [-130.90683593750003, 54.63178710937504],\n                            [-130.75800781249998, 54.61376953125],\n                            [-130.92714843749997, 54.47905273437499]\n                        ]\n                    ],\n                    [\n                        [\n                            [-130.57534179687497, 54.769677734374966],\n                            [-130.2140625, 55.02587890625003],\n                            [-130.34941406249996, 54.814550781250034],\n                            [-130.57534179687497, 54.769677734374966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.97758789062499, 56.20703125000006],\n                            [-80.057470703125, 56.28735351562497],\n                            [-79.57973632812502, 56.466357421875045],\n                            [-79.97758789062499, 56.20703125000006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-78.93559570312496, 56.26606445312498],\n                            [-79.17548828124998, 55.88505859374999],\n                            [-79.18212890625, 56.21215820312503],\n                            [-79.4951171875, 55.87475585937503],\n                            [-79.76474609374995, 55.80678710937505],\n                            [-79.54472656249999, 56.12836914062501],\n                            [-79.9875, 55.89213867187502],\n                            [-79.45888671875, 56.53974609374998],\n                            [-79.53632812499995, 56.180078124999966],\n                            [-79.27241210937493, 56.600439453125006],\n                            [-78.93559570312496, 56.26606445312498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-61.743603515624955, 57.55458984375005],\n                            [-61.6375, 57.41606445312499],\n                            [-62.01123046875003, 57.54848632812505],\n                            [-61.743603515624955, 57.55458984375005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.71650390624998, 57.515527343749994],\n                            [-79.80844726562498, 57.44243164062502],\n                            [-79.74257812499997, 57.60795898437499],\n                            [-79.71650390624998, 57.515527343749994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-69.16005859375, 59.04023437500001],\n                            [-69.35283203125002, 58.96074218749999],\n                            [-69.30322265625003, 59.144873046875006],\n                            [-69.16005859375, 59.04023437500001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-64.40703125, 60.367089843749966],\n                            [-64.44194335937496, 60.2978515625],\n                            [-64.73793945312497, 60.37563476562502],\n                            [-64.83642578124997, 60.50102539062499],\n                            [-64.40703125, 60.367089843749966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-68.23378906250002, 60.24091796875001],\n                            [-68.36787109374998, 60.314746093750045],\n                            [-68.08759765624998, 60.58784179687501],\n                            [-67.81884765624994, 60.449511718750074],\n                            [-68.23378906250002, 60.24091796875001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-78.531640625, 60.72856445312499],\n                            [-78.66889648437498, 60.716894531250006],\n                            [-78.24169921875, 60.818652343750045],\n                            [-78.531640625, 60.72856445312499]\n                        ]\n                    ],\n                    [\n                        [\n                            [-64.83261718749998, 61.366064453125006],\n                            [-65.43212890625, 61.649511718750034],\n                            [-64.78964843750003, 61.662207031250034],\n                            [-64.83261718749998, 61.366064453125006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-65.03056640624999, 61.879052734374966],\n                            [-64.89658203124995, 61.73330078125005],\n                            [-65.23535156249997, 61.89770507812506],\n                            [-65.03056640624999, 61.879052734374966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.54531250000002, 62.41171875000006],\n                            [-79.28647460937495, 62.247656250000034],\n                            [-79.32392578124995, 62.02607421875001],\n                            [-79.81611328124995, 61.59462890625002],\n                            [-80.26518554687496, 61.818212890625006],\n                            [-80.26005859374996, 62.10903320312502],\n                            [-79.9267578125, 62.39287109375002],\n                            [-79.54531250000002, 62.41171875000006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-64.82382812499998, 62.558740234374994],\n                            [-64.46503906249998, 62.535937500000045],\n                            [-64.47832031250002, 62.417871093749966],\n                            [-64.901220703125, 62.421044921874994],\n                            [-64.82382812499998, 62.558740234374994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-70.33706054687497, 62.548730468749994],\n                            [-70.76606445312498, 62.596875],\n                            [-71.22011718750002, 62.873925781249966],\n                            [-70.44262695312497, 62.73378906250002],\n                            [-70.33706054687497, 62.548730468749994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-82.00048828124997, 62.95419921874998],\n                            [-82.02583007812498, 62.73007812499998],\n                            [-82.56826171875002, 62.403222656249994],\n                            [-83.01582031249998, 62.20991210937498],\n                            [-83.69887695312497, 62.16025390624998],\n                            [-83.91049804687498, 62.45415039062499],\n                            [-83.37641601562498, 62.904931640624994],\n                            [-82.00048828124997, 62.95419921874998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-77.87670898437497, 63.470556640625034],\n                            [-77.53271484374997, 63.233642578125],\n                            [-77.94243164062496, 63.11440429687502],\n                            [-78.536767578125, 63.423730468749994],\n                            [-77.87670898437497, 63.470556640625034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-76.67758789062503, 63.393945312499966],\n                            [-77.36474609374994, 63.588330078124955],\n                            [-77.13369140624997, 63.68203125000002],\n                            [-76.65244140624998, 63.503564453124994],\n                            [-76.67758789062503, 63.393945312499966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-84.91962890624995, 65.26108398437503],\n                            [-84.50112304687497, 65.45844726562501],\n                            [-84.08486328125, 65.21782226562502],\n                            [-82.05, 64.64428710937506],\n                            [-81.67612304687498, 64.21264648437503],\n                            [-81.88710937499997, 64.01640625000002],\n                            [-80.82895507812495, 64.08994140625],\n                            [-80.30205078124999, 63.76220703125003],\n                            [-81.04638671875003, 63.461572265624966],\n                            [-82.378125, 63.706787109375],\n                            [-82.46708984375002, 63.92695312500001],\n                            [-83.30395507812497, 64.14379882812506],\n                            [-84.63291015625, 63.30922851562502],\n                            [-85.39262695312496, 63.119677734375045],\n                            [-85.76894531249997, 63.70034179687502],\n                            [-87.15190429687499, 63.58564453125001],\n                            [-86.93203124999997, 63.90166015625002],\n                            [-86.252099609375, 64.13686523437497],\n                            [-86.37426757812503, 64.56582031249997],\n                            [-86.074609375, 65.533837890625],\n                            [-85.55468750000003, 65.91865234374995],\n                            [-85.17622070312501, 65.746875],\n                            [-85.23994140624993, 65.51030273437499],\n                            [-84.91962890624995, 65.26108398437503]\n                        ]\n                    ],\n                    [\n                        [\n                            [-84.67475585937498, 65.575],\n                            [-85.096337890625, 65.756201171875],\n                            [-85.14960937500001, 66.01538085937506],\n                            [-84.75737304687496, 65.85893554687505],\n                            [-84.67475585937498, 65.575]\n                        ]\n                    ],\n                    [\n                        [\n                            [-83.72597656249997, 65.796728515625],\n                            [-83.23374023437495, 65.71503906249995],\n                            [-83.332421875, 65.63105468749998],\n                            [-84.11826171874995, 65.77177734375007],\n                            [-84.40717773437501, 66.13100585937497],\n                            [-83.78696289062495, 65.96577148437498],\n                            [-83.72597656249997, 65.796728515625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-108.09272460937501, 67.00517578124999],\n                            [-107.80551757812493, 66.99858398437507],\n                            [-107.94394531249999, 66.8578125],\n                            [-108.09272460937501, 67.00517578124999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-62.681542968749966, 67.05629882812502],\n                            [-62.87163085937499, 67.06259765625006],\n                            [-62.41679687499996, 67.18847656250003],\n                            [-62.681542968749966, 67.05629882812502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-107.89985351562497, 67.40180664062495],\n                            [-107.95024414062503, 67.31821289062498],\n                            [-108.15224609374997, 67.429443359375],\n                            [-108.04897460937498, 67.664892578125],\n                            [-107.89985351562497, 67.40180664062495]\n                        ]\n                    ],\n                    [\n                        [\n                            [-73.621728515625, 67.783837890625],\n                            [-74.573388671875, 67.82866210937507],\n                            [-74.70654296875003, 68.06708984374995],\n                            [-73.49375, 68.00063476562502],\n                            [-73.40717773437498, 67.79306640625],\n                            [-73.621728515625, 67.783837890625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-86.59555664062498, 67.7359375],\n                            [-86.89252929687498, 67.836572265625],\n                            [-86.95981445312503, 68.10024414062497],\n                            [-86.70209960937501, 68.30561523437498],\n                            [-86.42114257812497, 68.18344726562503],\n                            [-86.59555664062498, 67.7359375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.67587890624998, 68.32250976562506],\n                            [-75.078125, 68.17314453124999],\n                            [-75.20195312499996, 67.45917968750001],\n                            [-75.78007812499996, 67.28354492187503],\n                            [-76.94418945312498, 67.25029296875002],\n                            [-77.30439453125001, 67.68510742187505],\n                            [-77.12587890624997, 67.94707031250002],\n                            [-76.59580078124998, 68.27895507812497],\n                            [-75.67587890624998, 68.32250976562506]\n                        ]\n                    ],\n                    [\n                        [\n                            [-78.98271484374999, 68.19282226562501],\n                            [-79.17475585937493, 68.26445312500002],\n                            [-78.95258789062495, 68.35302734375006],\n                            [-78.98271484374999, 68.19282226562501]\n                        ]\n                    ],\n                    [\n                        [\n                            [-104.54067382812497, 68.405908203125],\n                            [-105.05136718749999, 68.55903320312501],\n                            [-104.60200195312503, 68.56152343749997],\n                            [-104.54067382812497, 68.405908203125]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.880859375, 68.34868164062505],\n                            [-75.40024414062503, 68.52548828125],\n                            [-75.28740234374996, 68.68774414062503],\n                            [-74.98364257812497, 68.64760742187502],\n                            [-74.880859375, 68.34868164062505]\n                        ]\n                    ],\n                    [\n                        [\n                            [-101.84589843749994, 68.58632812499997],\n                            [-102.30815429687497, 68.681982421875],\n                            [-102.01337890624995, 68.82539062500001],\n                            [-101.73295898437495, 68.75341796875],\n                            [-101.84589843749994, 68.58632812499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [-100.21723632812497, 68.80668945312502],\n                            [-100.59653320312496, 68.76640625000007],\n                            [-100.56547851562495, 69.02680664062501],\n                            [-100.21723632812497, 68.80668945312502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-99.99467773437502, 69.01352539062503],\n                            [-100.19570312500002, 68.991455078125],\n                            [-100.153125, 69.12949218750003],\n                            [-99.99467773437502, 69.01352539062503]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.21064453124995, 68.845458984375],\n                            [-79.24267578125, 69.04926757812495],\n                            [-78.33256835937496, 69.38603515624999],\n                            [-78.77919921875, 68.95048828124999],\n                            [-79.21064453124995, 68.845458984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-90.1998046875, 69.419091796875],\n                            [-90.33027343749993, 69.252197265625],\n                            [-90.49204101562503, 69.369873046875],\n                            [-90.1998046875, 69.419091796875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-76.99536132812503, 69.14375],\n                            [-77.37939453125, 69.2740234375],\n                            [-77.18754882812502, 69.440087890625],\n                            [-76.66884765625002, 69.36616210937504],\n                            [-76.99536132812503, 69.14375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-101.171728515625, 69.39707031250003],\n                            [-101.31289062499998, 69.57607421875],\n                            [-101.00063476562497, 69.4619140625],\n                            [-101.171728515625, 69.39707031250003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-95.51367187499997, 69.57363281250002],\n                            [-95.43745117187498, 69.37846679687505],\n                            [-95.73012695312502, 69.34755859374997],\n                            [-95.80620117187499, 69.56049804687501],\n                            [-95.89345703125, 69.35175781250004],\n                            [-95.87583007812495, 69.60600585937505],\n                            [-95.51367187499997, 69.57363281250002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-67.91469726562494, 69.54096679687504],\n                            [-68.22138671874998, 69.61674804687502],\n                            [-67.908837890625, 69.68183593749995],\n                            [-67.91469726562494, 69.54096679687504]\n                        ]\n                    ],\n                    [\n                        [\n                            [-78.02910156249993, 69.71489257812502],\n                            [-78.03999023437495, 69.6083984375],\n                            [-78.84819335937502, 69.4828125],\n                            [-78.02910156249993, 69.71489257812502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.43066406250003, 69.78779296874995],\n                            [-79.55283203124995, 69.63085937500006],\n                            [-80.04750976562502, 69.63432617187505],\n                            [-79.97783203124993, 69.50966796874997],\n                            [-80.794775390625, 69.68925781250005],\n                            [-80.42421875000002, 69.797607421875],\n                            [-79.43066406250003, 69.78779296874995]\n                        ]\n                    ],\n                    [\n                        [\n                            [-97.439453125, 69.64267578125006],\n                            [-96.29995117187494, 69.34438476562505],\n                            [-95.7513671875, 68.89765624999998],\n                            [-95.26777343749998, 68.82607421874997],\n                            [-96.40156249999995, 68.47070312500003],\n                            [-97.47202148437498, 68.543701171875],\n                            [-98.320556640625, 68.84272460937498],\n                            [-98.70380859374993, 68.80278320312502],\n                            [-98.90449218749995, 68.93242187500005],\n                            [-99.25400390625002, 68.86318359374997],\n                            [-99.49467773437493, 68.95957031249998],\n                            [-99.455712890625, 69.13120117187503],\n                            [-98.45595703124997, 69.33466796875001],\n                            [-98.54599609375, 69.57290039062497],\n                            [-98.04135742187498, 69.456640625],\n                            [-98.20048828124996, 69.79697265625006],\n                            [-97.79072265624998, 69.86162109374999],\n                            [-97.439453125, 69.64267578125006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-86.91303710937501, 70.11323242187501],\n                            [-86.55766601562499, 69.99531249999995],\n                            [-87.3232421875, 70.08012695312502],\n                            [-86.91303710937501, 70.11323242187501]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.70888671874997, 45.0038574218751],\n                            [-74.76245117187494, 44.99907226562502],\n                            [-74.99614257812496, 44.970117187499966],\n                            [-75.40126953124997, 44.77226562499999],\n                            [-75.81933593749997, 44.468017578125],\n                            [-76.18579101562503, 44.24223632812502],\n                            [-76.819970703125, 43.62880859375011],\n                            [-77.59653320312492, 43.62861328125007],\n                            [-78.45825195312497, 43.63149414062511],\n                            [-78.72041015624993, 43.62495117187501],\n                            [-78.84555664062492, 43.58334960937506],\n                            [-79.171875, 43.466552734375085],\n                            [-79.0830566406249, 43.33139648437509],\n                            [-79.05922851562494, 43.27807617187506],\n                            [-79.066064453125, 43.10610351562502],\n                            [-79.02617187499996, 43.01733398437506],\n                            [-78.98076171874993, 42.98061523437502],\n                            [-78.91508789062496, 42.90913085937504],\n                            [-79.17373046875, 42.74853515625],\n                            [-80.24755859374991, 42.366015625000045],\n                            [-81.02822265624997, 42.247167968750006],\n                            [-81.50732421874997, 42.10346679687504],\n                            [-81.97416992187496, 41.88872070312499],\n                            [-82.43906249999989, 41.6748535156251],\n                            [-82.69003906249995, 41.675195312499994],\n                            [-83.141943359375, 41.97587890624996],\n                            [-83.10952148437497, 42.25068359375001],\n                            [-82.54531249999997, 42.62470703124998],\n                            [-82.19038085937495, 43.47407226562501],\n                            [-82.137841796875, 43.570898437500034],\n                            [-82.48505859374993, 45.08374023437503],\n                            [-82.55107421874987, 45.3473632812501],\n                            [-82.91933593749994, 45.51796875000002],\n                            [-83.59267578125, 45.81713867187506],\n                            [-83.46948242187503, 45.99467773437499],\n                            [-83.61596679687503, 46.116845703124994],\n                            [-83.97778320312494, 46.08491210937507],\n                            [-84.12319335937497, 46.50292968749997],\n                            [-84.44047851562496, 46.49814453125006],\n                            [-84.66577148437503, 46.54326171875002],\n                            [-84.87597656249994, 46.89990234375003],\n                            [-85.07006835937497, 46.97993164062498],\n                            [-85.65224609375, 47.21997070312503],\n                            [-86.67216796874996, 47.636425781249955],\n                            [-87.20800781249997, 47.848486328125006],\n                            [-87.74389648437497, 48.06054687500003],\n                            [-88.37817382812497, 48.30307617187506],\n                            [-89.45566406249992, 47.99624023437508],\n                            [-90.79731445312495, 48.13105468750001],\n                            [-91.04345703124991, 48.19370117187498],\n                            [-91.38720703124997, 48.05854492187498],\n                            [-92.00517578125002, 48.301855468750006],\n                            [-92.3484375, 48.276611328125],\n                            [-92.41459960937493, 48.276611328125],\n                            [-92.50058593749995, 48.43535156250002],\n                            [-92.83671875, 48.567773437499994],\n                            [-93.25795898437497, 48.62885742187501],\n                            [-93.37788085937498, 48.61655273437498],\n                            [-93.70771484374995, 48.525439453125074],\n                            [-93.85161132812496, 48.607275390625034],\n                            [-94.6208984374999, 48.7426269531251],\n                            [-94.71279296874997, 48.863427734374994],\n                            [-94.80346679687497, 49.0029296875],\n                            [-94.86040039062493, 49.258593750000045],\n                            [-94.85434570312495, 49.304589843749994],\n                            [-95.15527343749997, 49.3696777343751],\n                            [-95.16206054687493, 48.991748046875045],\n                            [-95.39790039062493, 48.99316406249997],\n                            [-96.25068359374993, 48.99316406249997],\n                            [-96.67705078124993, 48.99316406249997],\n                            [-97.52983398437493, 48.99316406249997],\n                            [-98.80898437499995, 48.99316406249997],\n                            [-104.77832031249997, 48.993115234375125],\n                            [-110.7476562499999, 48.993066406250136],\n                            [-116.71704101562493, 48.993066406250136],\n                            [-118.84892578124993, 48.993066406250136],\n                            [-119.27534179687494, 48.993066406250136],\n                            [-119.70170898437495, 48.99301757812495],\n                            [-120.98085937499995, 48.99301757812495],\n                            [-122.78876953124994, 48.99301757812495],\n                            [-122.82670898437495, 49.028417968750034],\n                            [-122.9241699218749, 49.07465820312504],\n                            [-122.96269531249993, 49.07460937500005],\n                            [-123.06328125, 48.97773437500001],\n                            [-123.22944335937493, 49.260498046875085],\n                            [-122.87910156249995, 49.39892578125003],\n                            [-123.27675781249997, 49.34394531250001],\n                            [-123.1875, 49.680322265624994],\n                            [-123.53056640624989, 49.39731445312506],\n                            [-124.02861328125002, 49.602880859375006],\n                            [-123.99262695312497, 49.736181640625006],\n                            [-123.81718749999993, 49.58657226562508],\n                            [-123.58247070312498, 49.68125],\n                            [-123.87441406250005, 49.736816406250114],\n                            [-123.82543945312493, 50.14423828124998],\n                            [-123.94589843749995, 50.18393554687509],\n                            [-123.9849121093749, 49.87558593749998],\n                            [-124.28125, 49.77211914062502],\n                            [-124.78237304687492, 50.02011718749992],\n                            [-125.05668945312495, 50.418652343750125],\n                            [-124.8598632812499, 50.872412109375006],\n                            [-125.05878906249993, 50.51386718749998],\n                            [-125.4763183593749, 50.49716796874995],\n                            [-125.53935546874996, 50.64902343749998],\n                            [-125.64130859374994, 50.46621093750005],\n                            [-126.09433593749995, 50.497607421875045],\n                            [-126.44746093750004, 50.58774414062492],\n                            [-125.90410156250002, 50.704931640625006],\n                            [-126.51435546875, 50.679394531250125],\n                            [-126.37460937499995, 50.83735351562498],\n                            [-126.5217773437499, 50.86606445312498],\n                            [-126.51733398437497, 51.0568359375001],\n                            [-126.63178710937494, 50.915136718750006],\n                            [-127.057568359375, 50.86752929687509],\n                            [-127.70810546875, 51.15117187499996],\n                            [-127.41967773437496, 51.608056640625136],\n                            [-126.69145507812502, 51.70341796875002],\n                            [-127.33872070312489, 51.70737304687495],\n                            [-127.66870117187497, 51.47758789062502],\n                            [-127.85053710937498, 51.67319335937509],\n                            [-127.79536132812493, 52.19101562500006],\n                            [-127.43793945312504, 52.356152343750125],\n                            [-127.24223632812496, 52.39511718750009],\n                            [-126.71396484374989, 52.060693359374994],\n                            [-127.19399414062498, 52.45766601562502],\n                            [-126.95136718749994, 52.7510253906251],\n                            [-127.01933593750002, 52.8424804687501],\n                            [-127.06621093749989, 52.65268554687498],\n                            [-127.79189453124994, 52.28935546875002],\n                            [-128.10224609374993, 51.78842773437495],\n                            [-128.3576171875, 52.1588867187501],\n                            [-128.0375, 52.318164062500045],\n                            [-127.94023437499996, 52.545166015625085],\n                            [-128.27153320312493, 52.3629882812501],\n                            [-128.05327148437487, 52.91069335937496],\n                            [-128.3650390624999, 52.82578125000006],\n                            [-128.52470703125002, 53.1406738281251],\n                            [-129.08090820312492, 53.36728515625006],\n                            [-129.1715820312499, 53.53359375000002],\n                            [-128.8545898437499, 53.70454101562504],\n                            [-128.90561523437492, 53.559326171875114],\n                            [-128.5421386718749, 53.420654296875114],\n                            [-128.13271484375002, 53.417773437500045],\n                            [-127.92783203125, 53.274707031250045],\n                            [-128.2072265624999, 53.483203125000074],\n                            [-128.67553710937494, 53.55458984375005],\n                            [-128.76367187500003, 53.746875],\n                            [-128.5321289062499, 53.85810546875007],\n                            [-128.959375, 53.84145507812505],\n                            [-129.2578613281249, 53.417968750000085],\n                            [-129.56372070312506, 53.251464843750114],\n                            [-130.33525390625002, 53.723925781250074],\n                            [-130.04331054687495, 54.13354492187503],\n                            [-129.62602539062493, 54.23027343750002],\n                            [-130.08422851562503, 54.18139648437503],\n                            [-130.4302734375, 54.42099609374998],\n                            [-129.56064453124995, 55.46254882812508],\n                            [-129.79516601562503, 55.559570312500114],\n                            [-130.04848632812494, 55.05727539062511],\n                            [-130.01406249999997, 55.950537109375006],\n                            [-130.09785156249995, 56.10927734375002],\n                            [-130.41313476562487, 56.12250976562507],\n                            [-130.47709960937496, 56.230566406250034],\n                            [-130.649072265625, 56.26367187500003],\n                            [-131.471875, 56.55673828125006],\n                            [-131.82426757812496, 56.58999023437508],\n                            [-131.86616210937495, 56.792822265625006],\n                            [-132.1042968749999, 56.85678710937509],\n                            [-132.062890625, 56.95336914062503],\n                            [-132.33798828124992, 57.07944335937498],\n                            [-132.27939453124998, 57.14536132812506],\n                            [-132.23217773437494, 57.198535156250074],\n                            [-132.30166015625005, 57.2763183593751],\n                            [-132.44248046874986, 57.40673828125003],\n                            [-132.55048828124995, 57.499902343749994],\n                            [-133.00141601562495, 57.948974609375],\n                            [-133.27529296875, 58.22285156250004],\n                            [-133.54638671874997, 58.50346679687499],\n                            [-134.21850585937503, 58.849902343750045],\n                            [-134.32963867187505, 58.93969726562506],\n                            [-134.39306640625, 59.009179687499994],\n                            [-134.67724609374997, 59.19926757812499],\n                            [-134.94375, 59.28828125000001],\n                            [-135.05102539062491, 59.57866210937502],\n                            [-135.36787109374998, 59.743310546874994],\n                            [-135.70258789062504, 59.72875976562506],\n                            [-136.3218261718749, 59.604833984375034],\n                            [-136.27797851562494, 59.48032226562506],\n                            [-136.46635742187493, 59.459082031250006],\n                            [-136.57875976562494, 59.15224609375002],\n                            [-136.81328125000002, 59.15004882812511],\n                            [-137.12622070312491, 59.04096679687507],\n                            [-137.2775390624999, 58.988183593749994],\n                            [-137.43857421874995, 58.903125],\n                            [-137.52089843749994, 58.91538085937506],\n                            [-137.59331054687493, 59.22626953124998],\n                            [-138.317626953125, 59.611132812500074],\n                            [-138.86875, 59.94575195312501],\n                            [-139.18515624999986, 60.083593750000034],\n                            [-139.13696289062494, 60.17270507812506],\n                            [-139.07924804687497, 60.279443359375136],\n                            [-139.07924804687497, 60.3437011718751],\n                            [-139.23476562499997, 60.339746093749994],\n                            [-139.67631835937505, 60.32832031249998],\n                            [-139.97329101562497, 60.183154296875074],\n                            [-140.45283203125004, 60.29970703125002],\n                            [-140.5254394531249, 60.21835937499995],\n                            [-140.76274414062505, 60.25913085937509],\n                            [-141.00214843750004, 60.300244140625125],\n                            [-141.00214843750004, 60.884667968749994],\n                            [-141.00214843750004, 61.761279296875045],\n                            [-141.00214843750004, 63.22226562499998],\n                            [-141.00214843750004, 64.09887695312506],\n                            [-141.00214843750004, 65.55991210937498],\n                            [-141.00214843750004, 66.43652343750006],\n                            [-141.00214843750004, 67.89755859374998],\n                            [-141.00214843750004, 68.77416992187506],\n                            [-141.00214843750004, 69.65078125000011],\n                            [-139.18154296874997, 69.51552734375008],\n                            [-137.25996093749998, 68.96411132812503],\n                            [-136.12236328124993, 68.88222656250002],\n                            [-135.258837890625, 68.68432617187503],\n                            [-135.93901367187487, 68.9741699218751],\n                            [-135.575537109375, 69.02695312500003],\n                            [-135.91020507812487, 69.11147460937502],\n                            [-135.6914550781249, 69.31118164062502],\n                            [-135.29282226562486, 69.30786132812506],\n                            [-135.1408203124999, 69.46782226562496],\n                            [-134.45683593749993, 69.47763671875],\n                            [-134.40893554687494, 69.68178710937502],\n                            [-133.87978515624997, 69.50771484375011],\n                            [-134.17431640624991, 69.25283203125005],\n                            [-133.16313476562496, 69.43388671874999],\n                            [-132.91533203125002, 69.62963867187506],\n                            [-132.40390625, 69.65874023437496],\n                            [-132.48847656249993, 69.73808593749996],\n                            [-132.16342773437498, 69.70498046875014],\n                            [-131.13637695312497, 69.90688476562505],\n                            [-130.66547851562495, 70.12705078124998],\n                            [-129.944970703125, 70.09091796875006],\n                            [-129.675634765625, 70.19296875000009],\n                            [-129.64829101562495, 69.9977539062501],\n                            [-130.83208007812487, 69.65146484375006],\n                            [-131.9377929687499, 69.5347167968751],\n                            [-132.8174804687499, 69.20576171875004],\n                            [-133.41831054687492, 68.84428710937493],\n                            [-133.138037109375, 68.74658203125011],\n                            [-133.33666992187497, 68.83525390625005],\n                            [-132.57763671874997, 68.84780273437514],\n                            [-132.71894531249998, 69.07919921875],\n                            [-131.78837890625002, 69.43198242187495],\n                            [-131.32470703124997, 69.36118164062509],\n                            [-131.06342773437504, 69.45068359375003],\n                            [-130.97065429687495, 69.20908203125],\n                            [-130.1176269531249, 69.720068359375],\n                            [-128.89892578124994, 69.96616210937506],\n                            [-129.15791015624995, 69.80009765624999],\n                            [-129.05434570312502, 69.70107421875005],\n                            [-128.85302734375003, 69.7510253906251],\n                            [-127.68378906249994, 70.26035156249995],\n                            [-128.17011718749998, 70.41845703125],\n                            [-127.99101562499992, 70.57382812500003],\n                            [-127.22597656249992, 70.29614257812497],\n                            [-126.25043945312495, 69.54526367187492],\n                            [-125.52495117187495, 69.35156250000009],\n                            [-125.171875, 69.42797851562503],\n                            [-125.35693359374991, 69.62597656250003],\n                            [-124.767919921875, 69.99003906249996],\n                            [-124.99038085937494, 70.02661132812511],\n                            [-124.55502929687488, 70.15122070312509],\n                            [-124.40693359374991, 69.76743164062506],\n                            [-124.12460937499995, 69.6899902343751],\n                            [-124.33808593749991, 69.36484374999995],\n                            [-123.5284179687499, 69.38935546874995],\n                            [-123.02578125, 69.81000976562504],\n                            [-122.07006835937499, 69.81616210937506],\n                            [-120.96245117187502, 69.66040039062511],\n                            [-120.13999023437488, 69.38056640625013],\n                            [-117.22695312499998, 68.913427734375],\n                            [-116.05947265625, 68.83701171875006],\n                            [-116.2434082031249, 68.9740722656251],\n                            [-115.44228515624994, 68.94091796875009],\n                            [-114.62016601562496, 68.74609375],\n                            [-113.96440429687495, 68.39907226562502],\n                            [-114.09594726562491, 68.26679687500007],\n                            [-114.76528320312494, 68.27021484375004],\n                            [-115.12705078124992, 68.13203124999995],\n                            [-115.43447265624994, 67.90234375000006],\n                            [-115.13320312499994, 67.819189453125],\n                            [-112.50302734374993, 67.6819335937501],\n                            [-110.9900390624999, 67.79082031250007],\n                            [-110.07392578124995, 67.99291992187506],\n                            [-109.63037109374991, 67.73271484374996],\n                            [-109.03803710937504, 67.69116210937503],\n                            [-108.85200195312497, 67.42197265625009],\n                            [-108.61333007812493, 67.59804687500008],\n                            [-107.98872070312495, 67.2563964843751],\n                            [-107.99130859374995, 67.09516601562513],\n                            [-108.49604492187493, 67.09228515625006],\n                            [-107.25947265624998, 66.39853515624995],\n                            [-107.71035156250001, 66.74003906250007],\n                            [-107.7250976562499, 66.98413085937506],\n                            [-107.15649414062497, 66.88173828124997],\n                            [-107.9583984375, 67.81860351562506],\n                            [-107.79829101562498, 68.03691406249996],\n                            [-106.42426757812491, 68.20058593750008],\n                            [-105.7501953125, 68.59228515625011],\n                            [-106.45805664062496, 68.51645507812495],\n                            [-106.60849609374988, 68.35737304687504],\n                            [-107.61933593749994, 68.3310546875],\n                            [-107.73417968749989, 68.17373046875011],\n                            [-108.3228027343749, 68.15410156250002],\n                            [-108.71811523437488, 68.29746093750009],\n                            [-108.31347656249996, 68.61079101562498],\n                            [-106.16445312499992, 68.91987304687507],\n                            [-105.68559570312489, 68.82817382812505],\n                            [-105.3774414062499, 68.413818359375],\n                            [-104.65317382812488, 68.23007812500003],\n                            [-104.48681640624991, 68.06318359374998],\n                            [-103.47412109374993, 68.11503906250005],\n                            [-102.32036132812489, 67.73564453125005],\n                            [-101.55498046874992, 67.69316406250007],\n                            [-100.21293945312489, 67.83857421875004],\n                            [-98.92045898437502, 67.72578124999998],\n                            [-98.41210937499991, 67.80717773437505],\n                            [-98.63154296875004, 68.0725585937501],\n                            [-97.45493164062486, 67.61699218750002],\n                            [-97.20654296874989, 67.85507812500003],\n                            [-97.73911132812495, 67.97817382812505],\n                            [-98.19252929687494, 67.92299804687502],\n                            [-98.65048828124989, 68.36352539062506],\n                            [-98.21855468750002, 68.31743164062507],\n                            [-97.7942382812499, 68.38759765625],\n                            [-97.9250976562499, 68.523681640625],\n                            [-97.41035156249993, 68.49653320312498],\n                            [-96.97670898437497, 68.25541992187505],\n                            [-96.43066406249991, 68.3105957031251],\n                            [-96.72207031250005, 68.03876953124998],\n                            [-95.9703125, 68.24912109375],\n                            [-96.36914062499991, 67.50976562500003],\n                            [-96.14145507812489, 67.27182617187503],\n                            [-95.71992187499998, 67.31679687500014],\n                            [-95.77768554687495, 67.18461914062505],\n                            [-95.41591796875005, 67.15556640624999],\n                            [-95.41889648437504, 67.01323242187493],\n                            [-96.42255859374995, 67.05175781249997],\n                            [-95.7875488281249, 66.616796875],\n                            [-96.03686523437489, 66.9375],\n                            [-95.39965820312503, 66.94946289062509],\n                            [-95.25874023437493, 67.26254882812492],\n                            [-95.65048828124986, 67.73745117187505],\n                            [-95.46069335937503, 68.02138671875],\n                            [-94.74443359374993, 68.07089843749995],\n                            [-93.44892578124998, 68.61889648437503],\n                            [-93.85244140624994, 69.00034179687495],\n                            [-94.06489257812495, 68.78476562500006],\n                            [-94.600439453125, 68.80322265625011],\n                            [-94.08364257812497, 69.12309570312507],\n                            [-94.254736328125, 69.31376953125002],\n                            [-93.61948242187492, 69.41699218750009],\n                            [-93.74853515624991, 69.2261230468751],\n                            [-93.5322753906249, 69.48090820312495],\n                            [-94.2708007812499, 69.45512695312505],\n                            [-94.63383789062496, 69.64965820312506],\n                            [-94.82250976562494, 69.577783203125],\n                            [-95.96494140624989, 69.80278320312499],\n                            [-96.5513671875, 70.21030273437506],\n                            [-96.29770507812492, 70.51137695312511],\n                            [-95.87861328124998, 70.54897460937514],\n                            [-95.88632812499986, 70.69428710937507],\n                            [-96.25800781249993, 70.64228515625013],\n                            [-96.54892578124995, 70.80874023437511],\n                            [-96.44658203124996, 71.23989257812502],\n                            [-96.06201171874997, 71.41386718749993],\n                            [-95.5642578124999, 71.33676757812503],\n                            [-95.40625, 71.49165039062498],\n                            [-95.87231445312494, 71.57314453125005],\n                            [-94.73486328124994, 71.98295898437507],\n                            [-94.30834960937491, 71.76489257812506],\n                            [-93.74628906249998, 71.742822265625],\n                            [-92.94868164062493, 71.26210937500011],\n                            [-92.98144531249994, 70.8522460937501],\n                            [-91.56406249999995, 70.1782714843751],\n                            [-92.32050781250004, 70.2353515625],\n                            [-92.51186523437494, 70.10385742187503],\n                            [-91.976708984375, 70.03867187500009],\n                            [-92.88779296874989, 69.66821289062511],\n                            [-92.31166992187494, 69.67290039062499],\n                            [-91.91196289062495, 69.53125],\n                            [-91.20180664062494, 69.64477539062494],\n                            [-91.43994140624997, 69.52568359375002],\n                            [-90.4155761718749, 69.45698242187507],\n                            [-90.89228515625004, 69.26728515624995],\n                            [-91.23720703125005, 69.28554687500014],\n                            [-90.47900390624994, 68.88115234374999],\n                            [-90.57363281250005, 68.47470703124998],\n                            [-90.20478515625004, 68.25747070312511],\n                            [-89.27954101562491, 69.25546875000003],\n                            [-88.22353515625, 68.91503906249997],\n                            [-87.81357421874986, 68.34570312499997],\n                            [-87.89267578125, 68.24814453125],\n                            [-88.34697265624993, 68.28828125000001],\n                            [-88.313818359375, 67.95034179687508],\n                            [-87.359375, 67.17724609374997],\n                            [-86.56079101562491, 67.48212890625007],\n                            [-85.64316406249992, 68.69970703124997],\n                            [-84.86757812499994, 68.77333984375005],\n                            [-85.10664062499995, 68.84404296875007],\n                            [-84.86220703125, 69.07397460937503],\n                            [-85.38676757812493, 69.23188476562504],\n                            [-85.50737304687487, 69.84526367187493],\n                            [-82.61835937499993, 69.69106445312514],\n                            [-82.39023437499989, 69.60087890625007],\n                            [-82.75483398437493, 69.49438476562506],\n                            [-82.30986328124996, 69.41000976562509],\n                            [-82.22753906249997, 69.24887695312495],\n                            [-81.37783203125005, 69.18564453125003],\n                            [-81.95791015624991, 68.88364257812498],\n                            [-81.38090820312496, 68.85004882812504],\n                            [-81.28154296874987, 68.65722656250003],\n                            [-81.91484374999993, 68.4587890625001],\n                            [-82.55268554687504, 68.44648437500007],\n                            [-82.22241210937489, 68.145263671875],\n                            [-82.0125, 68.19389648437496],\n                            [-81.97646484374997, 67.86201171875001],\n                            [-81.2943359375, 67.497412109375],\n                            [-81.46757812499996, 67.0698730468751],\n                            [-83.40644531249998, 66.37124023437508],\n                            [-84.53847656249994, 66.97280273437505],\n                            [-84.84575195312502, 67.02871093750008],\n                            [-85.11372070312498, 66.90693359375013],\n                            [-84.73774414062504, 66.93359375000006],\n                            [-84.223046875, 66.68247070312506],\n                            [-83.86904296875, 66.2135742187501],\n                            [-84.29306640624995, 66.29179687500005],\n                            [-84.628076171875, 66.20771484374998],\n                            [-85.603857421875, 66.56826171875005],\n                            [-86.708154296875, 66.52304687500009],\n                            [-86.68510742187502, 66.36040039062499],\n                            [-85.95874023437491, 66.11904296875002],\n                            [-87.45288085937503, 65.33896484375009],\n                            [-87.96997070312503, 65.34892578124999],\n                            [-89.7494140625, 65.93603515625006],\n                            [-89.88969726562487, 65.86855468749997],\n                            [-91.42724609374994, 65.94790039062497],\n                            [-91.04111328124989, 65.82983398437509],\n                            [-90.98344726562496, 65.91923828124999],\n                            [-89.92407226562497, 65.78027343750011],\n                            [-88.97402343749994, 65.34829101562502],\n                            [-87.02753906249995, 65.19809570312498],\n                            [-88.10561523437497, 64.18330078125001],\n                            [-88.81772460937489, 63.99223632812499],\n                            [-89.20063476562493, 64.11376953125006],\n                            [-89.13154296874998, 63.96850585937494],\n                            [-89.61582031249995, 64.030615234375],\n                            [-89.8113281249999, 64.18056640625],\n                            [-90.04165039062494, 64.14086914062509],\n                            [-89.85571289062497, 63.9569824218751],\n                            [-90.16816406250004, 63.978759765625085],\n                            [-90.15473632812498, 63.68964843749998],\n                            [-90.81191406249991, 63.580908203125034],\n                            [-91.98222656249996, 63.82241210937502],\n                            [-92.33842773437496, 63.787646484375045],\n                            [-93.69633789062493, 64.14716796875013],\n                            [-93.55981445312491, 63.865283203125074],\n                            [-93.27021484374998, 63.840869140625074],\n                            [-93.37851562499992, 63.94848632812497],\n                            [-92.15688476562491, 63.691699218750045],\n                            [-92.46508789062491, 63.55507812500011],\n                            [-91.84184570312496, 63.69755859374999],\n                            [-90.97006835937489, 63.442773437500136],\n                            [-90.69858398437492, 63.06386718750005],\n                            [-91.44897460937503, 62.804052734375034],\n                            [-92.3612792968749, 62.81938476562496],\n                            [-91.93583984374993, 62.59238281250009],\n                            [-92.55141601562491, 62.546728515625034],\n                            [-92.76596679687492, 62.34995117187509],\n                            [-92.52797851562494, 62.16840820312504],\n                            [-93.20537109374993, 62.364941406250125],\n                            [-92.90551757812503, 62.21513671874996],\n                            [-93.3330566406249, 61.93291015625002],\n                            [-93.58178710937494, 61.94204101562511],\n                            [-93.31201171874997, 61.76728515625004],\n                            [-93.91274414062497, 61.48144531250006],\n                            [-94.509375, 60.60454101562493],\n                            [-94.76171874999991, 60.498242187500125],\n                            [-94.78828124999998, 59.26787109374993],\n                            [-94.95732421874996, 59.068847656250085],\n                            [-94.28706054687493, 58.716015625000125],\n                            [-94.33222656249998, 58.297363281250114],\n                            [-94.12319335937494, 58.73671875000008],\n                            [-93.1787597656249, 58.72563476562496],\n                            [-92.43281249999993, 57.3203125],\n                            [-92.7981445312499, 56.921972656250034],\n                            [-90.89746093750003, 57.25693359375006],\n                            [-88.94848632812489, 56.85131835937503],\n                            [-88.07509765624997, 56.46728515624994],\n                            [-87.48242187499991, 56.021289062500045],\n                            [-85.55932617187491, 55.54018554687508],\n                            [-85.21801757812491, 55.348974609375034],\n                            [-85.3652832031249, 55.07929687499998],\n                            [-85.06093749999997, 55.285644531250085],\n                            [-83.91059570312493, 55.314648437499955],\n                            [-82.39326171874998, 55.067822265625125],\n                            [-82.219384765625, 54.8134765625],\n                            [-82.42416992187486, 54.2445800781251],\n                            [-82.14145507812492, 53.81762695312497],\n                            [-82.29155273437496, 53.03071289062507],\n                            [-81.5994140624999, 52.432617187500085],\n                            [-81.82788085937489, 52.22421875000009],\n                            [-81.46621093749994, 52.204492187500136],\n                            [-80.588037109375, 51.667236328125114],\n                            [-80.4433105468749, 51.38857421875002],\n                            [-80.85122070312497, 51.125],\n                            [-80.47832031249993, 51.30732421874998],\n                            [-80.10356445312487, 51.282861328125136],\n                            [-79.34790039062494, 50.76264648437504],\n                            [-79.737451171875, 51.186279296875],\n                            [-79.33867187500002, 51.62817382812497],\n                            [-79.04052734375003, 51.46376953125005],\n                            [-78.90317382812495, 51.200292968750034],\n                            [-78.73134765624994, 51.497460937499994],\n                            [-78.98164062499993, 51.774560546875136],\n                            [-78.44809570312495, 52.26137695312502],\n                            [-78.74414062499994, 52.65537109374998],\n                            [-79.10034179687497, 53.65664062500005],\n                            [-78.99604492187493, 54.00249023437499],\n                            [-79.241796875, 54.098876953125085],\n                            [-79.14672851562491, 54.16923828125002],\n                            [-79.71235351562495, 54.6718261718751],\n                            [-77.77529296874994, 55.291259765625],\n                            [-76.60405273437496, 56.19956054687495],\n                            [-76.52558593749998, 56.8917968750001],\n                            [-76.80981445312497, 57.65795898437506],\n                            [-77.15678710937496, 58.018896484375034],\n                            [-78.51508789062493, 58.68237304687503],\n                            [-77.76069335937498, 59.38002929687505],\n                            [-77.72617187499995, 59.67587890624992],\n                            [-77.34907226562495, 59.57895507812509],\n                            [-77.48530273437493, 59.684570312500114],\n                            [-77.28920898437494, 60.0220214843751],\n                            [-77.58588867187498, 60.088183593750074],\n                            [-77.45288085937497, 60.1458007812501],\n                            [-77.6814453124999, 60.427099609375034],\n                            [-77.503564453125, 60.54272460937497],\n                            [-77.7908203124999, 60.63984375000004],\n                            [-77.58955078124993, 60.808593750000114],\n                            [-78.18134765624995, 60.81914062499996],\n                            [-77.51435546874998, 61.55629882812505],\n                            [-78.02138671874997, 61.8320800781251],\n                            [-78.13339843749986, 62.28227539062496],\n                            [-77.372412109375, 62.572509765625114],\n                            [-75.81689453124991, 62.31586914062507],\n                            [-75.7898437499999, 62.17958984375002],\n                            [-75.3412109375, 62.312109375],\n                            [-74.63256835937497, 62.115673828125125],\n                            [-74.6458007812499, 62.21113281250004],\n                            [-73.70507812499991, 62.47314453124994],\n                            [-72.68696289062498, 62.12456054687499],\n                            [-72.771630859375, 61.840429687500006],\n                            [-72.50556640624998, 61.922656250000074],\n                            [-72.22612304687487, 61.83159179687499],\n                            [-72.04003906249991, 61.68027343750006],\n                            [-72.21586914062502, 61.58725585937495],\n                            [-71.86611328125, 61.68852539062499],\n                            [-71.63828124999995, 61.6171875],\n                            [-71.85439453124991, 61.43979492187492],\n                            [-71.42270507812489, 61.158935546875085],\n                            [-70.27929687499991, 61.06865234374999],\n                            [-69.99243164062491, 60.8564941406251],\n                            [-69.50332031249994, 61.04042968750011],\n                            [-69.40473632812493, 60.84677734375009],\n                            [-69.75947265624998, 60.440234375000045],\n                            [-69.67373046874994, 60.07587890625007],\n                            [-70.65483398437496, 60.02622070312506],\n                            [-69.73393554687493, 59.918017578125045],\n                            [-69.68188476562489, 59.34174804687507],\n                            [-69.3440429687499, 59.303076171875006],\n                            [-69.53164062499994, 58.86923828125009],\n                            [-69.64838867187493, 58.82080078125],\n                            [-69.78417968749994, 58.95571289062511],\n                            [-70.15434570312496, 58.76059570312498],\n                            [-69.78989257812486, 58.689306640625034],\n                            [-69.27109374999986, 58.88393554687505],\n                            [-68.69819335937495, 58.904541015625],\n                            [-68.38115234374993, 58.74350585937506],\n                            [-68.22939453124994, 58.48457031250007],\n                            [-68.35654296874989, 58.163232421875136],\n                            [-69.04082031249996, 57.902490234375136],\n                            [-68.41357421874997, 58.0517578125],\n                            [-68.02104492187493, 58.48530273437504],\n                            [-67.88828124999989, 58.29575195312495],\n                            [-68.06386718750005, 58.13896484374999],\n                            [-67.75595703124992, 58.4045898437501],\n                            [-67.6782714843749, 57.99111328125008],\n                            [-67.5696289062499, 58.21347656250006],\n                            [-66.72216796874991, 58.49101562499996],\n                            [-66.36240234374989, 58.791162109374994],\n                            [-66.0023925781249, 58.43120117187502],\n                            [-66.04306640624995, 58.82065429687495],\n                            [-65.72099609374996, 59.02377929687495],\n                            [-65.38354492187494, 59.06020507812508],\n                            [-65.7, 59.21333007812501],\n                            [-65.4117187499999, 59.31499023437496],\n                            [-65.47509765624994, 59.47031249999998],\n                            [-65.03823242187494, 59.38789062500007],\n                            [-65.40742187499993, 59.53935546875002],\n                            [-65.4333984374999, 59.776513671874994],\n                            [-65.02817382812495, 59.77070312500007],\n                            [-65.17172851562489, 59.90800781249996],\n                            [-64.81733398437498, 60.3310546875],\n                            [-64.49941406250005, 60.26826171875001],\n                            [-64.41958007812494, 60.17138671874997],\n                            [-64.76845703124997, 60.01210937500005],\n                            [-64.28349609374993, 60.06406249999998],\n                            [-64.22631835937491, 59.741210937500085],\n                            [-64.05605468750005, 59.82255859374996],\n                            [-63.7501953124999, 59.51259765625005],\n                            [-63.945458984374994, 59.380175781250074],\n                            [-63.775878906249915, 59.277148437500045],\n                            [-63.539892578124864, 59.332861328125034],\n                            [-63.41513671874995, 59.194384765625074],\n                            [-63.97114257812498, 59.053808593750034],\n                            [-63.24843749999991, 59.068310546874955],\n                            [-63.28212890624994, 58.86738281250007],\n                            [-63.05029296874997, 58.87817382812494],\n                            [-62.87387695312489, 58.67246093749998],\n                            [-63.537060546874926, 58.329931640625006],\n                            [-63.209960937499886, 58.46694335937502],\n                            [-62.593847656249864, 58.47402343750005],\n                            [-62.81206054687502, 58.20039062500007],\n                            [-63.26152343749993, 58.014697265625074],\n                            [-62.486230468749966, 58.15405273437506],\n                            [-62.30566406249997, 57.97226562499995],\n                            [-61.95864257812505, 57.91176757812508],\n                            [-61.9679687499999, 57.61191406250009],\n                            [-62.495556640624926, 57.489208984375125],\n                            [-61.92114257812497, 57.42080078125005],\n                            [-61.977441406249966, 57.24794921875002],\n                            [-61.33374023437494, 57.01059570312498],\n                            [-61.37163085937502, 56.68081054687511],\n                            [-62.497265624999926, 56.80170898437504],\n                            [-61.73774414062498, 56.52602539062502],\n                            [-61.940429687499886, 56.423583984375114],\n                            [-61.42529296874994, 56.360644531250074],\n                            [-61.713085937499955, 56.230957031250114],\n                            [-61.364697265624926, 56.2160156250001],\n                            [-61.30112304687495, 56.04716796874999],\n                            [-61.4495117187499, 55.99570312499998],\n                            [-61.08935546874997, 55.86635742187511],\n                            [-60.74326171874989, 55.94145507812493],\n                            [-60.56210937499995, 55.727001953125125],\n                            [-60.341015624999926, 55.78466796874997],\n                            [-60.40830078124995, 55.649560546874994],\n                            [-60.19238281249994, 55.4809082031251],\n                            [-60.617138671874955, 55.060205078124994],\n                            [-59.75878906249997, 55.3095703125],\n                            [-59.68906249999989, 55.19633789062502],\n                            [-59.43789062500005, 55.175927734375136],\n                            [-59.837792968749994, 54.813964843750114],\n                            [-59.25957031249996, 55.19995117187506],\n                            [-58.99711914062496, 55.149462890625074],\n                            [-58.780175781249994, 54.838378906250114],\n                            [-58.39814453124998, 54.77412109374998],\n                            [-57.96245117187493, 54.875732421875085],\n                            [-57.40449218750004, 54.59086914062496],\n                            [-57.69926757812496, 54.38657226562506],\n                            [-58.435205078124966, 54.228125],\n                            [-58.63320312499999, 54.04956054687497],\n                            [-59.8230468749999, 53.83442382812504],\n                            [-60.14492187499994, 53.59614257812498],\n                            [-60.395410156249994, 53.653320312500085],\n                            [-60.1002929687499, 53.48696289062511],\n                            [-60.329492187499966, 53.26611328125006],\n                            [-58.652050781249926, 53.97788085937495],\n                            [-57.935986328124955, 54.09116210937492],\n                            [-58.31748046874989, 54.11445312500007],\n                            [-58.192089843749926, 54.228173828125136],\n                            [-57.4160644531249, 54.162744140625136],\n                            [-57.134960937499926, 53.79184570312506],\n                            [-57.524072265624966, 53.61142578125006],\n                            [-57.331738281249955, 53.469091796875034],\n                            [-56.84086914062496, 53.73945312500004],\n                            [-56.46499023437505, 53.76503906250011],\n                            [-55.96611328125002, 53.4711425781251],\n                            [-55.79794921874995, 53.211962890625045],\n                            [-55.80283203124989, 52.64316406249998],\n                            [-56.324902343749926, 52.54453124999998],\n                            [-55.74648437499994, 52.4745605468751],\n                            [-55.7771484374999, 52.3642578125],\n                            [-56.01171874999997, 52.394482421875125],\n                            [-55.695214843749994, 52.13779296875006],\n                            [-56.97597656250005, 51.45766601562505],\n                            [-58.510351562500006, 51.295068359375136],\n                            [-59.88632812499992, 50.316406250000085],\n                            [-61.72485351562503, 50.10405273437499],\n                            [-61.91953124999989, 50.2328613281251],\n                            [-62.71542968749995, 50.30166015625008],\n                            [-66.49550781249991, 50.2118652343751],\n                            [-66.94116210937503, 49.993701171875045],\n                            [-67.37202148437495, 49.348437500000045],\n                            [-68.28193359374998, 49.197167968750136],\n                            [-69.67387695312496, 48.19916992187504],\n                            [-71.01826171874993, 48.455615234375045],\n                            [-69.86552734374993, 48.17226562500005],\n                            [-69.775, 48.09809570312504],\n                            [-69.9944335937499, 47.73989257812508],\n                            [-70.70585937499996, 47.13979492187505],\n                            [-71.26777343749995, 46.79594726562499],\n                            [-71.87958984374998, 46.68681640624996],\n                            [-72.98100585937493, 46.209716796875085],\n                            [-73.4766113281249, 45.738232421874955],\n                            [-74.03784179687494, 45.501855468750136],\n                            [-74.31508789062494, 45.531054687500045],\n                            [-73.97382812499995, 45.345117187499994],\n                            [-74.70888671874997, 45.0038574218751]\n                        ]\n                    ],\n                    [\n                        [\n                            [-96.78232421874998, 72.93662109375],\n                            [-97.0927734375, 72.99692382812503],\n                            [-96.86240234374995, 73.18881835937506],\n                            [-96.78232421874998, 72.93662109375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-114.52153320312502, 72.592919921875],\n                            [-113.57807617187501, 72.65209960937506],\n                            [-113.2923828125, 72.94980468750003],\n                            [-112.75361328125001, 72.98603515624995],\n                            [-111.26972656249994, 72.71372070312498],\n                            [-111.895166015625, 72.35610351562497],\n                            [-111.67509765625002, 72.30014648437503],\n                            [-110.20512695312495, 72.66127929687497],\n                            [-110.66083984374998, 73.00820312500002],\n                            [-110.00844726562494, 72.983642578125],\n                            [-108.75498046875002, 72.55107421874999],\n                            [-108.18823242187501, 71.72377929687502],\n                            [-107.812841796875, 71.62617187500004],\n                            [-107.30600585937496, 71.89467773437502],\n                            [-108.23740234374999, 73.14990234375003],\n                            [-108.029052734375, 73.34873046875003],\n                            [-106.48212890624998, 73.19619140624997],\n                            [-105.41513671874995, 72.788330078125],\n                            [-104.38593749999997, 71.57695312500005],\n                            [-104.51479492187502, 71.06425781250005],\n                            [-103.58457031249995, 70.63085937500003],\n                            [-103.07719726562497, 70.50883789062505],\n                            [-103.04956054687503, 70.65507812499999],\n                            [-101.67631835937495, 70.27827148437495],\n                            [-101.56240234375001, 70.135009765625],\n                            [-101.04267578125, 70.11079101562504],\n                            [-100.98237304687497, 69.67988281250001],\n                            [-101.483837890625, 69.85019531250006],\n                            [-101.64765624999997, 69.69853515625007],\n                            [-102.18212890624997, 69.845947265625],\n                            [-102.59589843749997, 69.71791992187502],\n                            [-102.62109374999996, 69.55151367187506],\n                            [-103.464892578125, 69.64448242187498],\n                            [-103.04892578124999, 69.47177734375006],\n                            [-103.12021484374995, 69.20458984374997],\n                            [-102.44677734374997, 69.476318359375],\n                            [-102.04594726562493, 69.46484374999997],\n                            [-101.85712890625001, 69.02397460937505],\n                            [-102.89506835937499, 68.8236328125],\n                            [-104.57143554687501, 68.87211914062502],\n                            [-105.105859375, 68.92041015625],\n                            [-105.019580078125, 69.08125],\n                            [-106.27016601562497, 69.19458007812497],\n                            [-106.65908203124997, 69.439599609375],\n                            [-107.43989257812497, 69.00214843749995],\n                            [-108.36499023437497, 68.93476562499998],\n                            [-109.47211914062501, 68.67670898437498],\n                            [-113.12773437500002, 68.49414062500003],\n                            [-113.61684570312501, 68.8384765625],\n                            [-113.69414062499995, 69.19501953124998],\n                            [-115.61811523437495, 69.28295898437506],\n                            [-116.51347656249993, 69.42460937500005],\n                            [-117.19541015624995, 70.05405273437503],\n                            [-114.59233398437497, 70.31245117187498],\n                            [-112.63789062499997, 70.225244140625],\n                            [-111.63256835937497, 70.30883789062497],\n                            [-113.75727539062503, 70.69072265625005],\n                            [-115.99091796874997, 70.586279296875],\n                            [-117.58706054687498, 70.62954101562502],\n                            [-118.2640625, 70.888330078125],\n                            [-118.26909179687493, 71.03471679687505],\n                            [-115.30341796874997, 71.49370117187505],\n                            [-117.93564453125003, 71.39208984375003],\n                            [-118.22646484374995, 71.46708984375005],\n                            [-117.742333984375, 71.65932617187502],\n                            [-118.58300781250003, 71.64902343749998],\n                            [-118.98769531249997, 71.7642578125],\n                            [-118.94462890624997, 71.98554687499995],\n                            [-118.21347656249998, 72.26289062499998],\n                            [-118.481298828125, 72.42768554687498],\n                            [-118.13310546874995, 72.63281250000003],\n                            [-114.63823242187499, 73.37265625000003],\n                            [-114.20639648437495, 73.29780273437495],\n                            [-114.05170898437497, 73.07099609375004],\n                            [-114.52153320312502, 72.592919921875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-105.28891601562499, 72.919921875],\n                            [-106.92153320312497, 73.479833984375],\n                            [-106.61396484375001, 73.69560546875002],\n                            [-105.31796874999995, 73.76713867187502],\n                            [-104.5875, 73.57807617187495],\n                            [-104.62172851562495, 73.3111328125],\n                            [-105.28891601562499, 72.919921875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.53730468749998, 73.65449218749998],\n                            [-78.2865234375, 73.66582031250007],\n                            [-77.20654296874997, 73.49956054687505],\n                            [-76.18339843749999, 72.84306640625005],\n                            [-77.83593750000003, 72.89682617187498],\n                            [-79.3193359375, 72.75771484375],\n                            [-79.820703125, 72.82631835937502],\n                            [-80.18330078124995, 73.22465820312499],\n                            [-80.77641601562502, 73.33417968750001],\n                            [-80.84887695312503, 73.72124023437499],\n                            [-79.53730468749998, 73.65449218749998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-86.58935546874997, 71.01079101562507],\n                            [-85.64384765624999, 71.15244140624998],\n                            [-85.09487304687497, 71.15195312500006],\n                            [-84.82373046874997, 71.02861328125005],\n                            [-84.69941406249995, 71.63144531250003],\n                            [-85.33906249999998, 71.69726562500003],\n                            [-85.91162109375, 71.98652343749998],\n                            [-85.321875, 72.23315429687506],\n                            [-84.28374023437499, 72.04448242187499],\n                            [-84.84199218749995, 72.30815429687505],\n                            [-84.62304687500003, 72.37656250000003],\n                            [-85.34111328124993, 72.42153320312497],\n                            [-85.64990234374997, 72.72216796875003],\n                            [-85.26210937500002, 72.95400390625],\n                            [-84.25664062499999, 72.79672851562503],\n                            [-85.454736328125, 73.10546875000003],\n                            [-84.41606445312496, 73.45649414062495],\n                            [-83.781884765625, 73.41689453125],\n                            [-83.72983398437495, 73.57587890624995],\n                            [-81.946142578125, 73.72983398437506],\n                            [-81.40615234374997, 73.634521484375],\n                            [-80.27724609375, 72.77016601562502],\n                            [-81.229345703125, 72.31171874999998],\n                            [-80.61147460937497, 72.450830078125],\n                            [-80.925146484375, 71.90766601562501],\n                            [-80.18193359374996, 72.20878906250007],\n                            [-79.884375, 72.17719726562501],\n                            [-80.10893554687499, 72.33217773437497],\n                            [-79.83129882812503, 72.44628906250003],\n                            [-79.000244140625, 72.27202148437507],\n                            [-79.00781250000003, 72.04291992187501],\n                            [-78.58510742187497, 71.880615234375],\n                            [-78.86274414062495, 72.100830078125],\n                            [-78.69926757812496, 72.35141601562498],\n                            [-77.51650390624997, 72.17778320312505],\n                            [-78.48427734374994, 72.47060546875002],\n                            [-77.75322265624996, 72.72475585937502],\n                            [-75.70429687499998, 72.57153320312497],\n                            [-75.05268554687493, 72.22636718749999],\n                            [-75.92280273437501, 71.71723632812501],\n                            [-74.90317382812503, 72.10048828125002],\n                            [-74.20932617187498, 71.978662109375],\n                            [-74.31572265624999, 71.84267578125],\n                            [-75.20478515625001, 71.70913085937497],\n                            [-74.70078125, 71.67558593750005],\n                            [-74.99619140624998, 71.21811523437503],\n                            [-74.48808593750002, 71.64838867187501],\n                            [-73.8140625, 71.77143554687495],\n                            [-74.197265625, 71.404150390625],\n                            [-73.71284179687498, 71.58759765624998],\n                            [-73.18061523437501, 71.282861328125],\n                            [-73.27822265625, 71.53798828125],\n                            [-72.901953125, 71.67778320312507],\n                            [-71.64067382812499, 71.51625976562502],\n                            [-71.22939453124997, 71.33876953125],\n                            [-71.49501953124997, 71.10512695312502],\n                            [-71.93793945312498, 71.09428710937502],\n                            [-72.63271484374994, 70.83076171874998],\n                            [-71.74252929687495, 71.046875],\n                            [-71.370849609375, 70.97514648437499],\n                            [-70.82607421874994, 71.10874023437503],\n                            [-70.67265625, 71.05219726562498],\n                            [-70.76171874999997, 70.79223632812503],\n                            [-71.89018554687502, 70.43154296875002],\n                            [-71.27587890625, 70.50029296874999],\n                            [-71.42944335937503, 70.12778320312503],\n                            [-70.97978515624999, 70.5810546875],\n                            [-69.94980468750003, 70.84501953125005],\n                            [-68.49575195312502, 70.61025390625],\n                            [-68.363525390625, 70.48125],\n                            [-70.05771484375, 70.042626953125],\n                            [-68.77822265625, 70.20356445312501],\n                            [-69.00830078124997, 69.97895507812501],\n                            [-68.74404296874997, 69.94140625],\n                            [-68.05908203124997, 70.317236328125],\n                            [-67.36367187499994, 70.03442382812503],\n                            [-67.22163085937495, 69.73071289062506],\n                            [-68.02041015625, 69.77006835937499],\n                            [-69.25078124999999, 69.51191406249998],\n                            [-68.51303710937498, 69.57729492187497],\n                            [-67.236962890625, 69.460107421875],\n                            [-66.71674804687495, 69.31186523437498],\n                            [-66.70742187500002, 69.16821289062503],\n                            [-68.40629882812499, 69.23222656250002],\n                            [-69.040625, 69.09799804687503],\n                            [-68.41552734375, 69.17207031250001],\n                            [-67.8326171875, 69.06596679687499],\n                            [-67.88320312500002, 68.78398437499999],\n                            [-69.31909179687497, 68.85698242187505],\n                            [-68.21040039062495, 68.702978515625],\n                            [-67.9384765625, 68.524169921875],\n                            [-66.74272460937502, 68.45776367187497],\n                            [-67.032958984375, 68.32607421874997],\n                            [-66.923095703125, 68.06572265625005],\n                            [-66.72900390624997, 68.12900390625006],\n                            [-66.66269531249995, 68.03442382812497],\n                            [-66.63095703124998, 68.21064453124998],\n                            [-66.21240234374997, 68.280419921875],\n                            [-66.44394531249998, 67.83383789062506],\n                            [-65.94238281250003, 68.07094726562505],\n                            [-65.86435546875003, 67.92285156249997],\n                            [-65.50908203124996, 67.96826171875],\n                            [-65.40126953125002, 67.67485351562499],\n                            [-65.41533203124996, 67.87924804687498],\n                            [-64.92231445312495, 68.03164062500002],\n                            [-65.02109375, 67.78754882812495],\n                            [-64.63779296875, 67.84023437500002],\n                            [-63.850195312500034, 67.56606445312502],\n                            [-64.00795898437502, 67.34731445312497],\n                            [-64.69995117187494, 67.35053710937501],\n                            [-63.83623046874993, 67.26411132812498],\n                            [-63.59160156250002, 67.3775390625],\n                            [-63.040136718750034, 67.235009765625],\n                            [-63.70156249999994, 66.82236328125003],\n                            [-62.962304687499966, 66.94926757812505],\n                            [-62.37973632812495, 66.90537109375],\n                            [-62.12358398437499, 67.046728515625],\n                            [-61.35341796874994, 66.689208984375],\n                            [-61.52783203124994, 66.55810546875003],\n                            [-62.12333984374993, 66.64306640625003],\n                            [-61.57080078125, 66.37290039062506],\n                            [-61.95634765624993, 66.30932617187497],\n                            [-62.553125, 66.40683593750003],\n                            [-62.53359374999994, 66.22700195312498],\n                            [-61.99160156250002, 66.03530273437502],\n                            [-62.624121093750006, 66.01625976562505],\n                            [-62.381982421874966, 65.83330078124999],\n                            [-62.65888671874998, 65.63994140625002],\n                            [-63.16894531249997, 65.65732421875],\n                            [-63.45874023437494, 65.85302734375],\n                            [-63.42089843749997, 65.70859374999998],\n                            [-63.651074218749955, 65.66098632812506],\n                            [-63.33745117187493, 65.61674804687502],\n                            [-63.36337890624998, 65.22973632812503],\n                            [-63.606591796874966, 64.92807617187503],\n                            [-64.345703125, 65.17241210937499],\n                            [-64.26967773437497, 65.40078124999997],\n                            [-64.55507812500002, 65.1166015625],\n                            [-65.401611328125, 65.764013671875],\n                            [-64.44536132812496, 66.31713867187497],\n                            [-65.0044921875, 66.07773437500003],\n                            [-65.82573242187499, 65.996923828125],\n                            [-65.65634765625003, 66.204736328125],\n                            [-66.06372070312497, 66.13271484374997],\n                            [-66.986328125, 66.62749023437505],\n                            [-67.07685546874995, 66.52548828125006],\n                            [-67.30732421874993, 66.5697265625],\n                            [-67.22539062499993, 66.31025390624998],\n                            [-67.88339843749995, 66.46743164062502],\n                            [-67.18320312499995, 66.03442382812503],\n                            [-67.350439453125, 65.92973632812502],\n                            [-67.82802734374997, 65.96518554687503],\n                            [-68.45991210937498, 66.249267578125],\n                            [-68.74892578125, 66.200048828125],\n                            [-68.21718750000002, 66.078857421875],\n                            [-68.18671874999993, 65.87099609375002],\n                            [-67.86645507812497, 65.773681640625],\n                            [-67.936767578125, 65.56489257812501],\n                            [-67.56962890624999, 65.64355468749997],\n                            [-67.11796874999999, 65.44038085937495],\n                            [-67.3365234375, 65.34658203125005],\n                            [-66.69741210937502, 64.81518554687506],\n                            [-66.63549804687503, 65.00034179687503],\n                            [-66.21464843749999, 64.72241210937497],\n                            [-65.93852539062496, 64.88574218750003],\n                            [-65.2748046875, 64.63154296875004],\n                            [-65.52934570312499, 64.50478515624997],\n                            [-65.074609375, 64.43666992187502],\n                            [-65.21298828125003, 64.30327148437502],\n                            [-65.580322265625, 64.29384765624997],\n                            [-65.16987304687495, 64.02817382812503],\n                            [-64.67846679687503, 64.027978515625],\n                            [-64.79814453124999, 63.91596679687498],\n                            [-64.4109375, 63.70634765625002],\n                            [-64.66464843749995, 63.24536132812497],\n                            [-65.19184570312498, 63.764257812500006],\n                            [-65.06894531249998, 63.26347656250002],\n                            [-64.67236328125003, 62.921972656250006],\n                            [-65.16279296875001, 62.93261718750003],\n                            [-65.10849609374998, 62.62646484375],\n                            [-66.22402343749994, 63.10717773437497],\n                            [-66.228662109375, 62.99096679687503],\n                            [-66.41445312500002, 63.027197265625034],\n                            [-66.65498046874998, 63.264746093750006],\n                            [-66.69746093749993, 63.069531249999955],\n                            [-67.89326171874993, 63.733740234375006],\n                            [-67.72255859374997, 63.422753906249966],\n                            [-68.49375, 63.725488281249994],\n                            [-68.91108398437498, 63.703222656250006],\n                            [-68.141259765625, 63.17231445312501],\n                            [-67.67597656249998, 63.093554687500045],\n                            [-67.73696289062497, 63.00957031249999],\n                            [-65.98017578125001, 62.20888671875002],\n                            [-66.12387695312498, 61.89306640625],\n                            [-68.53588867187503, 62.25561523437506],\n                            [-69.12558593749998, 62.423974609374966],\n                            [-69.604736328125, 62.76772460937502],\n                            [-70.23613281250002, 62.76337890625001],\n                            [-70.801416015625, 62.91049804687506],\n                            [-71.10576171874999, 63.00224609375002],\n                            [-70.94604492187497, 63.12070312499998],\n                            [-71.34726562499998, 63.066113281249955],\n                            [-71.99223632812493, 63.41616210937505],\n                            [-71.380859375, 63.580322265625],\n                            [-72.29013671874995, 63.72797851562498],\n                            [-72.17426757812498, 63.893408203125006],\n                            [-72.49843749999994, 63.82348632812497],\n                            [-73.45454101562495, 64.39926757812503],\n                            [-73.27128906250002, 64.58251953125],\n                            [-73.91035156249998, 64.578125],\n                            [-74.064794921875, 64.42465820312498],\n                            [-74.13046874999998, 64.6078125],\n                            [-74.46123046874996, 64.64467773437505],\n                            [-74.68139648437497, 64.8306640625],\n                            [-74.91943359374997, 64.76552734374997],\n                            [-74.69472656250002, 64.49658203124997],\n                            [-75.71503906249995, 64.52436523437495],\n                            [-75.76669921875, 64.39194335937498],\n                            [-76.85615234374998, 64.23764648437498],\n                            [-77.76049804687503, 64.36015624999999],\n                            [-78.04521484374993, 64.499267578125],\n                            [-78.09560546875, 64.93925781250002],\n                            [-77.36088867187496, 65.19653320312503],\n                            [-77.32670898437493, 65.453125],\n                            [-75.82832031249993, 65.22705078125003],\n                            [-75.45209960937495, 64.84160156250002],\n                            [-75.35712890624995, 65.00874023437495],\n                            [-75.79868164062503, 65.297509765625],\n                            [-75.16630859374999, 65.28393554687497],\n                            [-74.13847656250002, 65.50346679687502],\n                            [-73.55078125000003, 65.48525390625005],\n                            [-74.41640624999997, 66.16708984375003],\n                            [-73.03325195312502, 66.72817382812505],\n                            [-72.78881835937494, 67.030615234375],\n                            [-72.22001953124999, 67.25429687500002],\n                            [-73.28447265624993, 68.35698242187505],\n                            [-73.82050781249998, 68.36293945312502],\n                            [-73.82211914062495, 68.68598632812501],\n                            [-74.11796875000002, 68.70092773437506],\n                            [-73.9892578125, 68.54863281250002],\n                            [-74.2701171875, 68.54121093750001],\n                            [-74.89296875, 68.80815429687505],\n                            [-74.71669921874997, 69.04550781249998],\n                            [-76.58505859375, 68.69873046875003],\n                            [-76.55722656250003, 69.00947265625001],\n                            [-75.9537109375, 69.03081054687502],\n                            [-75.64775390625002, 69.212548828125],\n                            [-76.46494140624995, 69.46943359375001],\n                            [-76.23408203125001, 69.66210937500003],\n                            [-76.742333984375, 69.57290039062497],\n                            [-77.08994140625, 69.63510742187503],\n                            [-76.85859374999995, 69.775390625],\n                            [-77.591650390625, 69.84560546875002],\n                            [-77.77402343750003, 70.23852539062503],\n                            [-78.28281250000003, 70.229150390625],\n                            [-79.06640624999997, 70.60356445312507],\n                            [-79.40522460937498, 70.40073242187503],\n                            [-78.86284179687499, 70.24189453125001],\n                            [-78.88964843750003, 69.97749023437495],\n                            [-79.51542968749996, 69.88759765625005],\n                            [-81.65195312500003, 70.09462890625002],\n                            [-80.92172851562503, 69.73090820312501],\n                            [-81.56469726562503, 69.94272460937498],\n                            [-82.29384765624997, 69.83691406250003],\n                            [-83.14995117187493, 70.00908203125002],\n                            [-83.85908203124998, 69.96274414062498],\n                            [-85.43237304687497, 70.11137695312507],\n                            [-85.780029296875, 70.03666992187505],\n                            [-86.32202148437503, 70.14541015625],\n                            [-86.396875, 70.46533203124997],\n                            [-87.838134765625, 70.24658203125],\n                            [-88.78271484374997, 70.49448242187503],\n                            [-89.45590820312498, 71.06171874999995],\n                            [-87.84492187499995, 70.94438476562505],\n                            [-87.14008789062498, 71.01162109374997],\n                            [-89.80537109374993, 71.46230468750005],\n                            [-89.86152343750001, 72.41191406250005],\n                            [-88.70517578124998, 73.40327148437495],\n                            [-87.71977539062496, 73.72290039062497],\n                            [-85.95078124999998, 73.85014648437505],\n                            [-84.94677734375, 73.72163085937498],\n                            [-86.00053710937499, 73.31254882812505],\n                            [-86.65629882812502, 72.72402343750005],\n                            [-86.21845703124998, 71.89912109375004],\n                            [-85.02338867187495, 71.35322265625001],\n                            [-86.58935546874997, 71.01079101562507]\n                        ]\n                    ],\n                    [\n                        [\n                            [-100.00190429687497, 73.9458984375],\n                            [-99.15795898437499, 73.73159179687497],\n                            [-97.66997070312499, 73.88774414062499],\n                            [-97.1705078125, 73.82485351562497],\n                            [-97.001708984375, 73.66650390625003],\n                            [-97.62587890624997, 73.50229492187498],\n                            [-97.27250976562502, 73.38681640624998],\n                            [-98.42177734375002, 72.94101562500003],\n                            [-97.63632812499998, 73.02763671874999],\n                            [-97.128125, 72.62758789062502],\n                            [-96.59208984374996, 72.71025390624999],\n                            [-96.44560546874996, 72.55244140624998],\n                            [-96.80146484374998, 72.32241210937502],\n                            [-96.61342773437494, 71.83383789062506],\n                            [-97.58227539062497, 71.62968750000005],\n                            [-98.18134765624998, 71.66245117187503],\n                            [-98.32270507812501, 71.85234375000002],\n                            [-98.19863281249994, 71.44086914062501],\n                            [-98.66289062499993, 71.302099609375],\n                            [-99.22363281249996, 71.387109375],\n                            [-100.594482421875, 72.15234375000003],\n                            [-101.20854492187495, 72.31699218749998],\n                            [-101.72392578124996, 72.31489257812501],\n                            [-102.70874023437496, 72.76450195312503],\n                            [-102.20400390624998, 73.077294921875],\n                            [-101.27319335937497, 72.7216796875],\n                            [-100.48476562500002, 72.77294921874997],\n                            [-100.395703125, 72.97700195312498],\n                            [-100.128125, 72.90668945312495],\n                            [-100.53637695312497, 73.19785156250003],\n                            [-99.82514648437503, 73.2138671875],\n                            [-100.36611328125001, 73.359033203125],\n                            [-100.88935546875003, 73.27534179687501],\n                            [-101.52319335937501, 73.48637695312502],\n                            [-100.97578124999995, 73.59975585937502],\n                            [-100.5216796875, 73.44931640625],\n                            [-100.96298828125002, 73.79140625],\n                            [-99.99111328125, 73.79516601562503],\n                            [-100.00190429687497, 73.9458984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-98.270361328125, 73.86850585937498],\n                            [-98.97392578124997, 73.81206054687502],\n                            [-99.4169921875, 73.89541015625002],\n                            [-97.69824218749997, 74.10869140625005],\n                            [-98.270361328125, 73.86850585937498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-93.17084960937498, 74.16098632812506],\n                            [-92.22270507812502, 73.97236328124998],\n                            [-90.62744140625, 73.95170898437505],\n                            [-90.38139648437496, 73.82475585937502],\n                            [-92.11791992187497, 72.75380859375],\n                            [-94.21132812499997, 72.75693359375],\n                            [-93.77055664062496, 72.66821289062506],\n                            [-93.55517578124994, 72.42114257812497],\n                            [-94.03754882812498, 72.02875976562498],\n                            [-95.00786132812496, 72.01279296875],\n                            [-95.60214843749998, 72.88447265624995],\n                            [-95.63291015625003, 73.69545898437497],\n                            [-94.697607421875, 73.66357421874997],\n                            [-95.134130859375, 73.88125],\n                            [-94.97353515625, 74.04140625000002],\n                            [-93.17084960937498, 74.16098632812506]\n                        ]\n                    ],\n                    [\n                        [\n                            [-119.73632812499997, 74.11264648437498],\n                            [-119.20595703125002, 74.19799804687503],\n                            [-119.11796874999995, 74.01552734375],\n                            [-118.54399414062499, 74.24462890625003],\n                            [-117.51484375000001, 74.23173828124999],\n                            [-115.51069335937501, 73.61875],\n                            [-115.446875, 73.43886718750002],\n                            [-118.96157226562497, 72.68413085937499],\n                            [-119.51284179687501, 72.30268554687501],\n                            [-120.17988281250001, 72.21264648437506],\n                            [-120.61933593750001, 71.50576171875002],\n                            [-121.47216796875003, 71.38901367187503],\n                            [-121.74936523437502, 71.44477539062501],\n                            [-123.09565429687503, 71.09379882812502],\n                            [-124.00776367187494, 71.67744140624998],\n                            [-125.29667968749999, 71.973046875],\n                            [-125.84531250000002, 71.978662109375],\n                            [-123.79726562499997, 73.76816406250003],\n                            [-124.69624023437497, 74.34819335937499],\n                            [-121.50415039062497, 74.54511718749998],\n                            [-119.56264648437494, 74.23281250000002],\n                            [-119.73632812499997, 74.11264648437498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-97.35551757812496, 74.52631835937495],\n                            [-97.75, 74.51054687500005],\n                            [-97.41650390624994, 74.62656250000003],\n                            [-97.35551757812496, 74.52631835937495]\n                        ]\n                    ],\n                    [\n                        [\n                            [-95.306640625, 74.50541992187505],\n                            [-95.850732421875, 74.58247070312504],\n                            [-95.51020507812498, 74.63676757812499],\n                            [-95.306640625, 74.50541992187505]\n                        ]\n                    ],\n                    [\n                        [\n                            [-104.11992187499995, 75.03632812500004],\n                            [-104.88740234374998, 75.14775390624999],\n                            [-104.34619140624996, 75.42993164062503],\n                            [-103.64350585937497, 75.18657226562499],\n                            [-104.11992187499995, 75.03632812500004]\n                        ]\n                    ],\n                    [\n                        [\n                            [-93.54257812499995, 75.0279296875],\n                            [-93.57309570312495, 74.66884765625005],\n                            [-94.53452148437498, 74.63671874999997],\n                            [-96.59960937499997, 75.03178710937499],\n                            [-95.95463867187493, 75.44379882812501],\n                            [-94.878173828125, 75.63002929687502],\n                            [-93.90908203125002, 75.42250976562502],\n                            [-93.54257812499995, 75.0279296875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-96.07856445312495, 75.510107421875],\n                            [-96.91513671875003, 75.37968749999999],\n                            [-96.98281249999997, 75.50981445312505],\n                            [-96.367822265625, 75.65463867187506],\n                            [-96.07856445312495, 75.510107421875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-94.52656249999995, 75.74931640624999],\n                            [-94.901220703125, 75.93076171875],\n                            [-94.53789062499996, 75.99643554687506],\n                            [-94.52656249999995, 75.74931640624999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-118.328125, 75.57968749999998],\n                            [-118.81713867187503, 75.52211914062497],\n                            [-119.39458007812499, 75.617333984375],\n                            [-117.63369140624998, 76.11508789062498],\n                            [-118.328125, 75.57968749999998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.0630859375, 75.92587890624998],\n                            [-79.63876953124995, 75.84291992187505],\n                            [-79.00932617187499, 76.14589843750005],\n                            [-79.0630859375, 75.92587890624998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-102.22734374999995, 76.014892578125],\n                            [-102.00800781250003, 75.93940429687498],\n                            [-102.57958984375003, 75.78022460937498],\n                            [-103.31474609374996, 75.76420898437499],\n                            [-103.04150390624999, 75.91884765624997],\n                            [-103.98525390624997, 75.93310546875003],\n                            [-103.80078124999994, 76.03701171874997],\n                            [-104.24248046874996, 76.04697265625006],\n                            [-104.35063476562497, 76.18232421875001],\n                            [-102.72802734374999, 76.30703125],\n                            [-102.22734374999995, 76.014892578125]\n                        ]\n                    ],\n                    [\n                        [\n                            [-104.02285156249998, 76.58310546875003],\n                            [-103.05131835937495, 76.44985351562497],\n                            [-103.31137695312499, 76.34755859375],\n                            [-104.35751953124995, 76.33461914062502],\n                            [-104.58569335937499, 76.60649414062499],\n                            [-104.07451171875003, 76.66611328124998],\n                            [-104.02285156249998, 76.58310546875003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-97.70092773437497, 76.46650390624998],\n                            [-97.89052734374997, 75.7603515625],\n                            [-97.40751953124999, 75.67250976562497],\n                            [-97.33603515624998, 75.41982421875],\n                            [-97.65332031249997, 75.50776367187498],\n                            [-97.87822265624996, 75.41611328125003],\n                            [-97.67431640624997, 75.127294921875],\n                            [-98.04531249999997, 75.20083007812497],\n                            [-98.12094726562503, 75.03271484375],\n                            [-100.234375, 75.00771484374997],\n                            [-100.48349609374995, 75.18842773437501],\n                            [-100.14570312499995, 75.24614257812505],\n                            [-100.71191406250003, 75.40634765625],\n                            [-99.19458007812499, 75.698388671875],\n                            [-102.58740234375001, 75.51367187500003],\n                            [-102.79750976562501, 75.59965820312505],\n                            [-102.14472656249998, 75.87504882812502],\n                            [-100.97280273437498, 75.79843750000003],\n                            [-101.414990234375, 75.84584960937502],\n                            [-101.87211914062496, 76.08310546875003],\n                            [-101.52895507812495, 76.21728515625003],\n                            [-102.1046875, 76.33120117187505],\n                            [-101.41518554687495, 76.42490234375003],\n                            [-99.86547851562499, 75.92421875],\n                            [-100.11284179687502, 76.11723632812507],\n                            [-99.54106445312497, 76.14628906250005],\n                            [-100.41420898437495, 76.242529296875],\n                            [-99.97773437500003, 76.31245117187495],\n                            [-100.82973632812497, 76.52387695312495],\n                            [-99.8140625, 76.6322265625],\n                            [-98.89033203125, 76.46557617187497],\n                            [-98.71083984374994, 76.69384765625003],\n                            [-97.70092773437497, 76.46650390624998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-101.22612304687497, 76.57934570312497],\n                            [-101.61308593749995, 76.60458984375006],\n                            [-100.26914062499998, 76.73413085937497],\n                            [-101.22612304687497, 76.57934570312497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-108.29238281250001, 76.05712890625],\n                            [-107.72348632812502, 75.99541015625002],\n                            [-108.020703125, 75.80478515625],\n                            [-107.21621093749997, 75.89155273437501],\n                            [-106.91352539062503, 75.67963867187501],\n                            [-106.67700195312499, 76.02373046875002],\n                            [-105.63266601562493, 75.94536132812505],\n                            [-105.51948242187497, 75.63237304687505],\n                            [-106.09262695312495, 75.08945312500003],\n                            [-107.15341796874996, 74.9271484375],\n                            [-108.47475585937495, 74.94721679687501],\n                            [-108.83129882812501, 75.06489257812498],\n                            [-112.51933593749997, 74.41684570312503],\n                            [-113.67158203124997, 74.45302734375005],\n                            [-114.31269531250003, 74.71508789062506],\n                            [-112.835986328125, 74.9755859375],\n                            [-111.67109375, 75.01943359374997],\n                            [-111.09345703125001, 75.25629882812498],\n                            [-113.71176757812499, 75.06860351562503],\n                            [-113.85332031249996, 75.259375],\n                            [-113.46708984374996, 75.41611328125003],\n                            [-114.01650390624998, 75.43427734375001],\n                            [-114.16845703124994, 75.23950195312503],\n                            [-114.51381835937497, 75.27548828125],\n                            [-114.45175781250002, 75.08789062499997],\n                            [-115.02011718749999, 74.97617187500003],\n                            [-115.41318359374995, 75.11499023437497],\n                            [-115.72885742187496, 74.968115234375],\n                            [-116.47607421874996, 75.17177734375],\n                            [-117.56523437499997, 75.23334960937504],\n                            [-117.25761718750002, 75.45952148437502],\n                            [-116.07714843749996, 75.49296874999999],\n                            [-115.14184570312501, 75.67851562500005],\n                            [-116.42563476562498, 75.58535156249997],\n                            [-117.16362304687496, 75.64487304687503],\n                            [-116.80214843749995, 75.77158203124998],\n                            [-114.99150390625002, 75.896337890625],\n                            [-116.66455078124999, 75.95756835937505],\n                            [-116.20986328125, 76.19443359374998],\n                            [-114.77861328124999, 76.17260742187497],\n                            [-115.82216796874997, 76.27001953125003],\n                            [-114.99848632812503, 76.4974609375],\n                            [-114.19394531249999, 76.45146484375005],\n                            [-113.82348632812501, 76.20683593750002],\n                            [-112.69760742187496, 76.20170898437505],\n                            [-111.05268554687495, 75.54853515625001],\n                            [-108.94716796875, 75.54179687499999],\n                            [-108.94477539062495, 75.69897460937503],\n                            [-109.8705078125, 75.929052734375],\n                            [-109.48681640624999, 76.14467773437497],\n                            [-110.31445312500001, 76.369384765625],\n                            [-109.09824218749996, 76.811865234375],\n                            [-108.46699218749997, 76.73759765625007],\n                            [-108.29238281250001, 76.05712890625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-89.72646484374994, 76.50742187499998],\n                            [-90.55625, 76.73457031249998],\n                            [-90.13632812499995, 76.83696289062505],\n                            [-89.69541015625, 76.74116210937498],\n                            [-89.72646484374994, 76.50742187499998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-113.56069335937494, 76.74326171874998],\n                            [-114.83525390624999, 76.79467773437497],\n                            [-113.89165039062495, 76.89487304687503],\n                            [-113.56069335937494, 76.74326171874998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-94.29497070312493, 76.91245117187498],\n                            [-93.23002929687496, 76.77026367187497],\n                            [-93.53457031250002, 76.44770507812498],\n                            [-92.99536132812494, 76.62041015624999],\n                            [-91.305029296875, 76.68076171875003],\n                            [-90.54262695312494, 76.495751953125],\n                            [-91.41508789062496, 76.45585937500005],\n                            [-89.28452148437498, 76.30161132812506],\n                            [-89.40659179687498, 76.18916015624998],\n                            [-91.40732421874998, 76.22006835937506],\n                            [-89.27758789062497, 75.79506835937497],\n                            [-89.64604492187499, 75.5650390625],\n                            [-88.91669921874998, 75.45395507812503],\n                            [-88.64497070312495, 75.65844726562503],\n                            [-88.201318359375, 75.51201171875005],\n                            [-87.72973632812503, 75.57563476562495],\n                            [-87.53911132812502, 75.48486328125003],\n                            [-87.25693359374998, 75.61772460937499],\n                            [-85.95146484374993, 75.39501953125],\n                            [-85.97299804687498, 75.5287109375],\n                            [-83.931982421875, 75.81894531250003],\n                            [-83.23710937499993, 75.75083007812503],\n                            [-82.153662109375, 75.83105468750003],\n                            [-80.32197265624998, 75.62910156250001],\n                            [-79.50908203125002, 75.25981445312499],\n                            [-80.38198242187494, 75.03417968750003],\n                            [-79.40141601562502, 74.91762695312502],\n                            [-79.944482421875, 74.83364257812505],\n                            [-80.34775390624998, 74.90297851562505],\n                            [-80.26274414062499, 74.58447265625],\n                            [-81.94018554687494, 74.47270507812505],\n                            [-82.73579101562495, 74.53027343749997],\n                            [-83.5220703125, 74.90146484375],\n                            [-83.53188476562494, 74.58569335937497],\n                            [-84.42553710937503, 74.50810546875007],\n                            [-85.06142578125, 74.60693359375003],\n                            [-85.133447265625, 74.517431640625],\n                            [-85.44233398437495, 74.6005859375],\n                            [-85.80800781249994, 74.49897460937498],\n                            [-88.42304687499995, 74.49414062499997],\n                            [-88.53496093749993, 74.83173828125001],\n                            [-89.55869140624995, 74.55473632812507],\n                            [-90.55327148437499, 74.61274414062498],\n                            [-90.88022460937498, 74.8177734375],\n                            [-91.13457031250002, 74.64985351562498],\n                            [-91.54912109375002, 74.65556640624999],\n                            [-92.3892578125, 75.263330078125],\n                            [-92.18510742187499, 75.84653320312498],\n                            [-93.09174804687495, 76.35400390624997],\n                            [-95.27387695312498, 76.26440429687503],\n                            [-96.03969726562494, 76.48671875000002],\n                            [-95.65097656249998, 76.58466796874998],\n                            [-96.88071289062495, 76.73833007812505],\n                            [-96.40156249999995, 76.79721679687503],\n                            [-96.75830078124997, 76.97177734374998],\n                            [-95.84951171875002, 77.06621093750005],\n                            [-94.29497070312493, 76.91245117187498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-115.55126953125001, 77.36328125],\n                            [-116.32919921874996, 77.137060546875],\n                            [-115.81005859374999, 76.939111328125],\n                            [-116.25273437500002, 76.90141601562505],\n                            [-115.94628906250003, 76.71127929687503],\n                            [-116.99921874999995, 76.531591796875],\n                            [-117.23359375000001, 76.28154296875005],\n                            [-117.99296874999999, 76.40581054687505],\n                            [-117.88081054687497, 76.80507812500005],\n                            [-118.79140624999994, 76.51298828125005],\n                            [-119.080712890625, 76.12407226562505],\n                            [-119.58037109375, 76.32651367187498],\n                            [-119.52612304687496, 75.99721679687505],\n                            [-119.91289062499997, 75.85883789062501],\n                            [-120.40888671874995, 75.82563476562498],\n                            [-120.84838867187496, 76.18266601562499],\n                            [-121.21347656249999, 75.98369140625005],\n                            [-122.53305664062498, 75.95092773437503],\n                            [-122.59272460937497, 76.16206054687495],\n                            [-122.90278320312498, 76.13471679687498],\n                            [-122.51938476562503, 76.353173828125],\n                            [-121.56113281250003, 76.453466796875],\n                            [-119.09018554687496, 77.30507812500002],\n                            [-116.84355468749995, 77.33955078124995],\n                            [-117.03974609374995, 77.46513671875005],\n                            [-116.51132812500003, 77.54760742187497],\n                            [-115.55126953125001, 77.36328125]\n                        ]\n                    ],\n                    [\n                        [\n                            [-89.83325195312503, 77.26762695312505],\n                            [-90.22827148437503, 77.21245117187499],\n                            [-90.99321289062499, 77.32949218750002],\n                            [-91.01904296875003, 77.64389648437503],\n                            [-89.83896484375003, 77.49140624999998],\n                            [-89.83325195312503, 77.26762695312505]\n                        ]\n                    ],\n                    [\n                        [\n                            [-104.55815429687497, 77.14174804687497],\n                            [-105.21508789062496, 77.18208007812501],\n                            [-106.03559570312495, 77.73984375000006],\n                            [-105.58789062499997, 77.73598632812497],\n                            [-104.54223632812501, 77.33774414062503],\n                            [-104.55815429687497, 77.14174804687497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-95.484375, 77.79199218750003],\n                            [-93.30097656249995, 77.73979492187505],\n                            [-93.54394531249997, 77.466650390625],\n                            [-95.98706054687497, 77.484130859375],\n                            [-96.19458007812497, 77.70053710937503],\n                            [-95.484375, 77.79199218750003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-101.6935546875, 77.69658203125005],\n                            [-102.37783203124995, 77.728125],\n                            [-102.44770507812498, 77.88061523437506],\n                            [-101.19321289062493, 77.82978515624998],\n                            [-101.00205078124998, 77.73510742187497],\n                            [-101.6935546875, 77.69658203125005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-113.83247070312497, 77.75463867187506],\n                            [-114.28720703124998, 77.72148437500005],\n                            [-114.98041992187498, 77.91542968750002],\n                            [-114.33037109374997, 78.07753906250002],\n                            [-113.83247070312497, 77.75463867187506]\n                        ]\n                    ],\n                    [\n                        [\n                            [-110.45805664062496, 78.10322265625001],\n                            [-109.62226562499995, 78.07475585937499],\n                            [-110.865625, 77.834130859375],\n                            [-110.15273437500002, 77.76293945312506],\n                            [-110.19848632812501, 77.52451171874998],\n                            [-112.37265625000002, 77.36411132812498],\n                            [-113.16435546875002, 77.5302734375],\n                            [-113.21518554687498, 77.90351562500001],\n                            [-110.45805664062496, 78.10322265625001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-109.81596679687499, 78.65039062500003],\n                            [-109.48447265624995, 78.31640625],\n                            [-111.16918945312499, 78.38627929687505],\n                            [-111.51748046874997, 78.27470703125005],\n                            [-112.13125, 78.366064453125],\n                            [-113.22304687499998, 78.29790039062505],\n                            [-112.85585937499997, 78.46684570312502],\n                            [-110.877587890625, 78.73505859375004],\n                            [-109.81596679687499, 78.65039062500003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-96.20449218749994, 78.53129882812499],\n                            [-94.91538085937495, 78.39052734375002],\n                            [-95.32924804687497, 78.22504882812495],\n                            [-94.93427734374998, 78.07563476562498],\n                            [-96.98964843749994, 77.80600585937503],\n                            [-97.65815429687498, 78.090625],\n                            [-96.944677734375, 78.15185546874997],\n                            [-98.04951171874995, 78.325927734375],\n                            [-98.33261718749998, 78.77353515625006],\n                            [-97.38232421875, 78.78291015625001],\n                            [-96.20449218749994, 78.53129882812499]\n                        ]\n                    ],\n                    [\n                        [\n                            [-103.42602539062499, 79.315625],\n                            [-102.57617187499996, 78.87939453125003],\n                            [-101.70366210937502, 79.07890625000002],\n                            [-101.128125, 78.80166015625002],\n                            [-100.43549804687503, 78.8203125],\n                            [-99.60942382812495, 78.58305664062507],\n                            [-99.16640625000002, 77.85693359375003],\n                            [-100.27465820312503, 77.83271484374995],\n                            [-101.07412109375001, 78.19384765625],\n                            [-102.60698242187502, 78.24892578125002],\n                            [-102.73134765624995, 78.37104492187495],\n                            [-103.94658203124999, 78.26000976562497],\n                            [-104.76357421874998, 78.35166015625],\n                            [-104.90961914062498, 78.55263671875],\n                            [-103.57050781250003, 78.53984375000005],\n                            [-104.02084960937502, 78.63491210937497],\n                            [-103.37158203125, 78.73632812500003],\n                            [-104.18500976562498, 78.78129882812505],\n                            [-104.15195312499999, 78.989892578125],\n                            [-104.89550781249996, 78.80815429687502],\n                            [-104.74677734375003, 79.02709960937503],\n                            [-105.53564453124999, 79.03251953125007],\n                            [-105.51455078124995, 79.24248046875002],\n                            [-105.38769531249994, 79.32358398437503],\n                            [-103.42602539062499, 79.315625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-98.79160156249995, 79.98110351562505],\n                            [-98.94521484375, 79.72407226562498],\n                            [-100.05683593749997, 79.89824218750005],\n                            [-100.05327148437496, 80.093359375],\n                            [-99.15322265625001, 80.12421874999998],\n                            [-98.79160156249995, 79.98110351562505]\n                        ]\n                    ],\n                    [\n                        [\n                            [-91.88554687499999, 81.13286132812505],\n                            [-90.64301757812498, 80.59370117187498],\n                            [-89.23559570312494, 80.51064453125002],\n                            [-88.85732421874997, 80.16621093750001],\n                            [-88.19990234374998, 80.11147460937497],\n                            [-88.5248046875, 80.41801757812507],\n                            [-87.675, 80.37211914062505],\n                            [-87.92231445312501, 80.09770507812499],\n                            [-86.97719726562502, 79.89423828125001],\n                            [-87.29516601562494, 79.58017578124998],\n                            [-86.33696289062496, 79.63496093749995],\n                            [-86.00703124999998, 79.47944335937498],\n                            [-85.6478515625, 79.61142578125006],\n                            [-85.04213867187497, 79.2845703125],\n                            [-86.95717773437502, 78.97490234375005],\n                            [-87.61738281249995, 78.67631835937505],\n                            [-88.04018554687494, 78.99531250000004],\n                            [-87.98286132812498, 78.53706054687501],\n                            [-88.74160156250002, 78.58403320312499],\n                            [-88.82241210937497, 78.18588867187498],\n                            [-90.037109375, 78.60683593750002],\n                            [-89.52568359374999, 78.15961914062495],\n                            [-90.29721679687495, 78.32802734374997],\n                            [-90.614404296875, 78.14985351562501],\n                            [-92.35126953125001, 78.312890625],\n                            [-92.8482421875, 78.46010742187497],\n                            [-91.86689453124998, 78.54267578125001],\n                            [-93.26660156249997, 78.60830078124997],\n                            [-93.63442382812502, 78.75092773437498],\n                            [-93.15986328124998, 78.77563476562503],\n                            [-94.11459960937498, 78.92890625000001],\n                            [-92.54721679687495, 79.28261718750002],\n                            [-91.29990234375003, 79.372705078125],\n                            [-92.82192382812497, 79.44990234375001],\n                            [-93.93315429687496, 79.29072265624998],\n                            [-94.11030273437498, 79.40156250000001],\n                            [-95.10317382812502, 79.289892578125],\n                            [-95.66289062500002, 79.52734374999997],\n                            [-94.40185546874997, 79.736328125],\n                            [-95.73935546874995, 79.66015625000003],\n                            [-96.58906249999995, 79.91665039062497],\n                            [-96.77324218749999, 80.13579101562502],\n                            [-94.64589843749994, 80.04873046874997],\n                            [-94.26259765625002, 80.19487304687499],\n                            [-95.40507812499996, 80.13500976562506],\n                            [-96.39409179687493, 80.31503906250003],\n                            [-95.549072265625, 80.36660156249997],\n                            [-95.92695312499998, 80.72065429687498],\n                            [-93.92792968749995, 80.55917968750003],\n                            [-95.51474609375003, 80.83813476562503],\n                            [-94.98051757812499, 81.04965820312503],\n                            [-93.28671874999998, 81.10029296874998],\n                            [-94.22011718749997, 81.33076171875004],\n                            [-93.03466796874997, 81.3462890625],\n                            [-91.88554687499999, 81.13286132812505]\n                        ]\n                    ],\n                    [\n                        [\n                            [-69.4888671875, 83.01679687499998],\n                            [-66.42255859374998, 82.92685546875003],\n                            [-68.46933593749995, 82.65336914062502],\n                            [-65.29902343749995, 82.79960937500005],\n                            [-64.98388671874997, 82.90229492187501],\n                            [-64.50400390625, 82.77841796874998],\n                            [-63.641015624999966, 82.81259765625003],\n                            [-63.246777343749926, 82.4501953125],\n                            [-62.47519531249995, 82.51958007812502],\n                            [-61.392480468749994, 82.44189453125],\n                            [-61.61538085937502, 82.18442382812503],\n                            [-64.43579101562497, 81.74262695312501],\n                            [-66.62573242187497, 81.61640624999995],\n                            [-68.68852539062493, 81.29331054687503],\n                            [-64.78007812499993, 81.49287109375001],\n                            [-69.55068359375, 80.38325195312498],\n                            [-70.71259765625001, 80.53959960937505],\n                            [-70.264892578125, 80.23359374999998],\n                            [-72.05595703124996, 80.12324218749995],\n                            [-70.56840820312493, 80.09370117187498],\n                            [-71.387841796875, 79.76176757812505],\n                            [-72.43652343750003, 79.69438476562499],\n                            [-74.39448242187495, 79.87407226562499],\n                            [-73.47246093749996, 79.7564453125],\n                            [-73.36152343750001, 79.50400390625],\n                            [-75.50341796875, 79.41416015625],\n                            [-76.898828125, 79.5123046875],\n                            [-75.60273437499998, 79.23955078125005],\n                            [-74.48120117187503, 79.22949218750006],\n                            [-74.64091796874996, 79.03554687499997],\n                            [-78.58164062499998, 79.075],\n                            [-77.88276367187498, 78.9423828125],\n                            [-76.255859375, 79.00683593749997],\n                            [-74.486328125, 78.75009765624998],\n                            [-74.87861328124998, 78.54482421875],\n                            [-76.41611328124995, 78.51152343750005],\n                            [-75.19345703125, 78.327734375],\n                            [-75.86596679687497, 78.00981445312499],\n                            [-78.01259765624997, 77.94604492187506],\n                            [-78.07617187500003, 77.51904296875],\n                            [-78.70849609374997, 77.34213867187503],\n                            [-80.57304687499996, 77.31479492187506],\n                            [-81.65908203124997, 77.52543945312499],\n                            [-81.3013671875, 77.34404296875007],\n                            [-82.056787109375, 77.29653320312497],\n                            [-81.75634765624997, 77.20400390625005],\n                            [-79.49726562500001, 77.19609375000005],\n                            [-78.97919921874998, 76.89287109374999],\n                            [-78.28886718750002, 76.97797851562501],\n                            [-77.98330078124994, 76.75498046875006],\n                            [-78.284326171875, 76.57124023437501],\n                            [-80.79970703124997, 76.173583984375],\n                            [-80.97451171874994, 76.470068359375],\n                            [-81.71738281250003, 76.494970703125],\n                            [-82.52983398437499, 76.723291015625],\n                            [-82.23315429687494, 76.46582031250003],\n                            [-83.88569335937501, 76.453125],\n                            [-84.22377929687497, 76.67534179687499],\n                            [-84.27534179687498, 76.35654296875006],\n                            [-85.141259765625, 76.30458984375005],\n                            [-86.45371093750003, 76.58486328125002],\n                            [-86.68022460937499, 76.37661132812497],\n                            [-87.35419921874998, 76.44804687500005],\n                            [-87.48979492187499, 76.58583984374997],\n                            [-87.49755859374997, 76.38627929687499],\n                            [-88.39599609374997, 76.40527343750003],\n                            [-88.49584960937497, 76.77285156249997],\n                            [-88.54580078125002, 76.42089843750003],\n                            [-89.36962890624997, 76.474462890625],\n                            [-89.49975585937503, 76.82680664062502],\n                            [-88.39814453124995, 77.10395507812501],\n                            [-86.81225585937497, 77.18491210937498],\n                            [-87.68144531249996, 77.43637695312503],\n                            [-88.01699218750002, 77.78471679687505],\n                            [-86.75507812499998, 77.86372070312498],\n                            [-85.58847656249998, 77.46113281250004],\n                            [-84.73867187499997, 77.36103515624998],\n                            [-83.72128906249998, 77.41420898437497],\n                            [-82.7103515625, 77.84951171875002],\n                            [-82.5953125, 77.99213867187504],\n                            [-83.77939453125, 77.53261718750002],\n                            [-85.28935546874996, 77.55903320312498],\n                            [-85.54755859374998, 77.92768554687495],\n                            [-84.61542968749998, 78.19570312500002],\n                            [-84.22270507812499, 78.176025390625],\n                            [-84.91035156249993, 78.23969726562501],\n                            [-84.78320312499997, 78.52758789062506],\n                            [-85.5859375, 78.10957031249998],\n                            [-86.21777343750003, 78.08120117187497],\n                            [-85.92006835937494, 78.34287109374998],\n                            [-86.91323242187494, 78.126806640625],\n                            [-87.5517578125, 78.17661132812503],\n                            [-86.80791015624999, 78.77436523437495],\n                            [-85.00375976562495, 78.912255859375],\n                            [-83.27143554687501, 78.77031250000002],\n                            [-81.75009765624995, 78.97578124999995],\n                            [-82.43876953125002, 78.903662109375],\n                            [-84.41201171875002, 78.99658203125003],\n                            [-84.38359375000002, 79.1185546875],\n                            [-83.57587890624995, 79.05366210937501],\n                            [-86.42075195312498, 79.84521484374997],\n                            [-86.49853515625003, 80.25825195312501],\n                            [-83.72363281250003, 80.22895507812501],\n                            [-81.68837890625, 79.685791015625],\n                            [-80.47592773437498, 79.60625],\n                            [-80.12446289062495, 79.66948242187507],\n                            [-81.01015625000002, 79.693115234375],\n                            [-82.98701171874995, 80.32260742187498],\n                            [-76.86298828124995, 80.86479492187505],\n                            [-78.71621093749994, 80.95166015624997],\n                            [-76.88510742187503, 81.43027343750006],\n                            [-81.00703125000001, 80.6548828125],\n                            [-82.88432617187502, 80.57753906249997],\n                            [-82.22236328124998, 80.77231445312503],\n                            [-84.41782226562495, 80.52675781250002],\n                            [-86.250341796875, 80.56577148437506],\n                            [-86.60307617187499, 80.66401367187498],\n                            [-85.63930664062494, 80.92460937500007],\n                            [-83.288818359375, 81.14794921875],\n                            [-85.780859375, 81.03505859375],\n                            [-87.32988281250002, 80.669775390625],\n                            [-88.00366210937497, 80.675390625],\n                            [-89.16689453125, 80.94130859375],\n                            [-86.47675781249993, 81.03574218750006],\n                            [-84.94121093750002, 81.28623046875],\n                            [-87.27509765624995, 81.080810546875],\n                            [-89.623046875, 81.032470703125],\n                            [-89.94731445312499, 81.17265625000005],\n                            [-89.20869140624998, 81.25009765625003],\n                            [-89.67368164062503, 81.32861328125003],\n                            [-87.59702148437498, 81.52583007812498],\n                            [-88.47905273437502, 81.56464843749998],\n                            [-90.41630859374996, 81.40537109375003],\n                            [-89.82167968749997, 81.63486328124998],\n                            [-91.29238281250002, 81.57124023437498],\n                            [-91.64755859374998, 81.68383789062503],\n                            [-88.06318359375001, 82.09648437500007],\n                            [-87.01821289062502, 81.95874023437497],\n                            [-86.62680664062495, 82.05102539062503],\n                            [-85.04482421874997, 81.9828125],\n                            [-86.615625, 82.21855468750007],\n                            [-84.89682617187503, 82.44941406250001],\n                            [-82.63369140625002, 82.07729492187497],\n                            [-82.53691406250002, 82.24726562499995],\n                            [-79.465625, 81.85112304687499],\n                            [-82.44755859374993, 82.39501953125003],\n                            [-81.68115234375003, 82.51865234375],\n                            [-82.11684570312497, 82.62866210937503],\n                            [-80.8625, 82.57153320312503],\n                            [-81.01015625000002, 82.77905273437503],\n                            [-78.748779296875, 82.67939453124998],\n                            [-80.15493164062497, 82.91113281250003],\n                            [-77.61806640624997, 82.89584960937503],\n                            [-76.009375, 82.53515625],\n                            [-75.565625, 82.60854492187502],\n                            [-77.12490234374994, 83.00854492187497],\n                            [-74.41416015624995, 83.01313476562501],\n                            [-72.65869140625, 82.72163085937495],\n                            [-73.44189453124994, 82.90483398437499],\n                            [-72.811669921875, 83.08120117187502],\n                            [-71.98320312499996, 83.10141601562498],\n                            [-70.94038085937495, 82.90224609375],\n                            [-71.08481445312498, 83.08266601562497],\n                            [-69.96992187499995, 83.11611328125005],\n                            [-69.4888671875, 83.01679687499998]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Canada\", \"childNum\": 110 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [9.527658197470123, 47.27026989773668],\n                        [9.46249431093294, 47.19858962254578],\n                        [9.46249431093294, 47.09010747968864],\n                        [9.409458596647225, 47.02019676540292],\n                        [9.579979133936737, 47.05856388629306],\n                        [9.580273437500011, 47.057373046875],\n                        [10.133496093750011, 46.851513671875],\n                        [10.349414062500017, 46.98476562499999],\n                        [10.414941406250023, 46.964404296874996],\n                        [10.45458984375, 46.8994140625],\n                        [10.452832031250011, 46.86494140625],\n                        [10.406054687500017, 46.73486328125],\n                        [10.39794921875, 46.6650390625],\n                        [10.4306640625, 46.550048828125],\n                        [10.195507812500011, 46.62109375],\n                        [10.1375, 46.61435546875],\n                        [10.087011718750006, 46.599902343749996],\n                        [10.061230468750011, 46.546777343749994],\n                        [10.038281250000011, 46.483203125],\n                        [10.045605468750011, 46.447900390624994],\n                        [10.081933593750023, 46.420751953125],\n                        [10.109667968750017, 46.362841796874996],\n                        [10.128320312500023, 46.238232421875],\n                        [10.08056640625, 46.227978515625],\n                        [10.041015625, 46.238085937499996],\n                        [9.939257812500017, 46.36181640625],\n                        [9.884472656250011, 46.3677734375],\n                        [9.787792968750011, 46.346044921875],\n                        [9.639453125000017, 46.2958984375],\n                        [9.57958984375, 46.29609375],\n                        [9.528710937500023, 46.306201171874996],\n                        [9.427636718750023, 46.482324218749994],\n                        [9.399316406250023, 46.4806640625],\n                        [9.304394531250011, 46.495556640625],\n                        [9.203417968750017, 46.21923828125],\n                        [9.11874162946429, 46.014892578125],\n                        [8.97551618303573, 45.81677455357143],\n                        [8.74961495535715, 46.02246372767857],\n                        [8.818554687500011, 46.0771484375],\n                        [8.458398437500023, 46.245898437499996],\n                        [8.370703125, 46.445117187499996],\n                        [8.298535156250011, 46.40341796875],\n                        [8.23193359375, 46.341210937499994],\n                        [8.08154296875, 46.256005859374994],\n                        [7.9931640625, 46.015917968749996],\n                        [7.327929687500017, 45.912353515625],\n                        [7.129003906250006, 45.880419921874996],\n                        [7.055761718750006, 45.90380859375],\n                        [7.02109375, 45.92578125],\n                        [6.953710937500006, 46.017138671874996],\n                        [6.897265625000017, 46.0517578125],\n                        [6.772070312500006, 46.16513671875],\n                        [6.758105468750017, 46.415771484375],\n                        [6.578222656250006, 46.437353515625],\n                        [6.428906250000011, 46.430517578125],\n                        [6.321875, 46.393701171874994],\n                        [6.234667968750017, 46.3326171875],\n                        [6.199414062500011, 46.19306640625],\n                        [6.086621093750011, 46.147021484374996],\n                        [6.006640625000017, 46.142333984375],\n                        [5.971484375000017, 46.151220703125],\n                        [5.970019531250017, 46.214697265625],\n                        [6.0361328125, 46.238085937499996],\n                        [6.095898437500011, 46.27939453125],\n                        [6.129687500000017, 46.5669921875],\n                        [6.41015625, 46.755419921874996],\n                        [6.429003906250017, 46.832275390625],\n                        [6.45625, 46.94833984375],\n                        [6.624804687500017, 47.004345703125],\n                        [6.666894531250023, 47.026513671874994],\n                        [6.688085937500006, 47.058251953124994],\n                        [6.820703125000023, 47.16318359375],\n                        [6.952050781250023, 47.2671875],\n                        [6.978515625, 47.302050781249996],\n                        [7.000585937500006, 47.322509765625],\n                        [7.000585937500006, 47.339453125],\n                        [6.900390625, 47.39423828125],\n                        [6.968359375, 47.45322265625],\n                        [7.136035156250017, 47.48984375],\n                        [7.343164062500023, 47.43310546875],\n                        [7.615625, 47.592724609375],\n                        [8.454003906250023, 47.59619140625],\n                        [8.559472656250023, 47.6240234375],\n                        [8.570507812500011, 47.63779296875],\n                        [8.567089843750011, 47.651904296874996],\n                        [8.55234375, 47.659130859375],\n                        [8.451757812500006, 47.651806640625],\n                        [8.413281250000011, 47.6626953125],\n                        [8.403417968750006, 47.687792968749996],\n                        [8.435742187500011, 47.731347656249994],\n                        [8.572656250000023, 47.775634765625],\n                        [9.524023437500006, 47.52421875],\n                        [9.625878906250023, 47.467041015625],\n                        [9.527539062500011, 47.270751953125],\n                        [9.527658197470123, 47.27026989773668]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Switzerland\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-67.28886718749999, -55.776855468749964],\n                            [-67.55996093749997, -55.72480468750002],\n                            [-67.39736328124997, -55.58515625],\n                            [-67.28886718749999, -55.776855468749964]\n                        ]\n                    ],\n                    [\n                        [\n                            [-67.07993164062498, -55.15380859374996],\n                            [-67.33969726562495, -55.292578124999984],\n                            [-67.4947265625, -55.177441406249976],\n                            [-68.07001953124995, -55.22109374999999],\n                            [-68.30136718750003, -54.98066406250003],\n                            [-67.245263671875, -54.977636718750034],\n                            [-67.07993164062498, -55.15380859374996]\n                        ]\n                    ],\n                    [\n                        [\n                            [-69.70297851562503, -54.91904296875],\n                            [-68.90078125000002, -55.01777343750004],\n                            [-68.45800781249997, -54.95966796875002],\n                            [-68.61328124999997, -55.155566406250045],\n                            [-68.28266601562495, -55.25517578125],\n                            [-68.04833984375, -55.6431640625],\n                            [-68.86704101562498, -55.45019531250003],\n                            [-68.89008789062498, -55.2412109375],\n                            [-69.19262695312497, -55.171875],\n                            [-69.35922851562498, -55.300683593749945],\n                            [-69.18085937499995, -55.47480468749998],\n                            [-69.41181640624995, -55.44423828124997],\n                            [-69.97978515625002, -55.14746093749999],\n                            [-69.88442382812494, -54.88203125000001],\n                            [-69.70297851562503, -54.91904296875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-70.9916015625, -54.86796874999999],\n                            [-70.80483398437497, -54.96767578124996],\n                            [-70.41752929687493, -54.908886718749976],\n                            [-70.29785156249997, -55.11376953124997],\n                            [-70.47558593749994, -55.17705078124998],\n                            [-71.43720703125001, -54.88925781249997],\n                            [-70.9916015625, -54.86796874999999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-71.390478515625, -54.03281250000002],\n                            [-71.02192382812495, -54.111816406250036],\n                            [-71.14326171874998, -54.374023437499986],\n                            [-71.473291015625, -54.23115234375001],\n                            [-71.94853515624999, -54.300878906250006],\n                            [-72.21044921874997, -54.04775390624995],\n                            [-71.996484375, -53.884863281249984],\n                            [-71.390478515625, -54.03281250000002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-72.92324218749997, -53.481640625],\n                            [-72.88222656249997, -53.578320312499976],\n                            [-72.48227539062503, -53.58808593750001],\n                            [-72.20541992187503, -53.80742187500002],\n                            [-72.408544921875, -54.00380859374997],\n                            [-72.87099609375, -54.12656250000002],\n                            [-72.76376953125, -53.86484375],\n                            [-73.03945312499994, -53.83281250000004],\n                            [-73.08076171875001, -53.99804687499995],\n                            [-73.21064453125001, -53.98583984374995],\n                            [-73.31435546875, -53.72919921874998],\n                            [-73.845458984375, -53.54580078125001],\n                            [-73.44707031249993, -53.41005859374998],\n                            [-72.92324218749997, -53.481640625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.38574218749994, -52.92236328125001],\n                            [-73.65400390624998, -53.06982421875003],\n                            [-73.13520507812498, -53.35390625],\n                            [-73.56728515625, -53.3068359375],\n                            [-73.86694335937494, -53.096875],\n                            [-74.27021484374995, -53.08154296875002],\n                            [-74.71201171874998, -52.74873046874998],\n                            [-74.38574218749994, -52.92236328125001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-68.62993164062499, -52.65263671875004],\n                            [-68.65322265624994, -54.85361328124999],\n                            [-69.48627929687493, -54.85888671875],\n                            [-69.72343750000002, -54.71210937500003],\n                            [-70.49716796875, -54.80957031249999],\n                            [-71.83154296874997, -54.62617187500002],\n                            [-71.92773437500003, -54.52871093749997],\n                            [-71.80014648437498, -54.433984374999945],\n                            [-71.07993164062498, -54.444238281249994],\n                            [-70.79726562500002, -54.32724609374996],\n                            [-70.70112304687498, -54.48544921875004],\n                            [-70.31098632812498, -54.52851562500002],\n                            [-70.86308593749993, -54.11044921875003],\n                            [-70.86772460937499, -53.88417968750002],\n                            [-70.53129882812502, -53.627343750000016],\n                            [-70.37973632812495, -53.98671874999995],\n                            [-70.62983398437493, -54.005566406249976],\n                            [-70.53530273437494, -54.136132812500016],\n                            [-70.16899414062502, -54.37929687499999],\n                            [-69.74184570312494, -54.30585937500005],\n                            [-69.25317382812494, -54.557421875000045],\n                            [-69.04433593749997, -54.40673828124999],\n                            [-69.98813476562503, -54.10908203125001],\n                            [-70.15112304687503, -53.88808593750002],\n                            [-70.09111328124996, -53.72177734374998],\n                            [-69.35595703125003, -53.41630859375001],\n                            [-69.63701171874999, -53.33408203125004],\n                            [-70.32929687499998, -53.37763671875003],\n                            [-70.44335937499994, -53.085546875000034],\n                            [-70.130615234375, -52.942773437499994],\n                            [-70.38012695312494, -52.75195312500002],\n                            [-69.93544921874997, -52.82109374999998],\n                            [-69.41406249999997, -52.48623046874997],\n                            [-69.16704101562499, -52.66757812499997],\n                            [-68.78979492187497, -52.576757812500034],\n                            [-68.62993164062499, -52.65263671875004]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.82294921874993, -51.63017578125001],\n                            [-74.53681640624998, -51.96513671875004],\n                            [-74.69448242187497, -52.27919921874999],\n                            [-74.85180664062494, -52.27070312500003],\n                            [-75.10537109375, -51.78886718750001],\n                            [-74.82294921874993, -51.63017578125001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.55864257812499, -51.27705078125001],\n                            [-74.62036132812497, -51.395703125000026],\n                            [-75.04736328125, -51.39833984375003],\n                            [-75.28911132812496, -51.625390625000016],\n                            [-75.15366210937498, -51.278808593750014],\n                            [-74.73666992187503, -51.20761718749999],\n                            [-74.55864257812499, -51.27705078125001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.302001953125, -50.67998046875005],\n                            [-75.411376953125, -50.76435546875001],\n                            [-75.42763671875002, -50.48056640625002],\n                            [-75.11533203124998, -50.510449218749976],\n                            [-75.302001953125, -50.67998046875005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.05478515625, -50.29609375],\n                            [-75.44912109374997, -50.34335937500004],\n                            [-75.32666015624997, -50.01181640625],\n                            [-74.8759765625, -50.10996093750001],\n                            [-75.05478515625, -50.29609375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.106689453125, -48.83652343750001],\n                            [-75.38994140624999, -49.15917968750002],\n                            [-75.64116210937499, -49.195410156250034],\n                            [-75.48764648437498, -49.082421875000016],\n                            [-75.58310546874998, -48.85888671874995],\n                            [-75.106689453125, -48.83652343750001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.47617187499998, -49.14785156250002],\n                            [-74.59472656249997, -50.00664062500001],\n                            [-74.76298828124996, -50.01142578125001],\n                            [-74.88041992187502, -49.72587890625001],\n                            [-74.72382812499998, -49.42382812500003],\n                            [-74.960107421875, -49.533007812499974],\n                            [-75.06601562499998, -49.85234375000002],\n                            [-75.54980468749994, -49.79130859375002],\n                            [-75.30585937499998, -49.49404296875003],\n                            [-75.46748046874995, -49.35888671875003],\n                            [-75.08603515624998, -49.27021484375],\n                            [-75.21015624999995, -49.14804687499998],\n                            [-74.94921875, -48.960156249999976],\n                            [-74.89624023437503, -48.73320312500002],\n                            [-74.54609374999993, -48.76689453125004],\n                            [-74.47617187499998, -49.14785156250002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.51025390624997, -48.76347656250005],\n                            [-75.65092773437496, -48.58632812500002],\n                            [-75.57148437499993, -48.095898437500026],\n                            [-75.39140625000002, -48.01972656249997],\n                            [-75.15849609374999, -48.62265624999996],\n                            [-75.51025390624997, -48.76347656250005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.56728515625, -48.591992187500026],\n                            [-74.92304687499998, -48.62646484375003],\n                            [-75.21289062499997, -48.141699218750034],\n                            [-75.19829101562502, -47.974609375000014],\n                            [-74.895654296875, -47.839355468749986],\n                            [-74.56728515625, -48.591992187500026]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.11220703124997, -47.8376953125],\n                            [-75.26103515625002, -47.76386718749998],\n                            [-74.92646484374998, -47.72314453125003],\n                            [-75.11220703124997, -47.8376953125]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.31289062500002, -45.69150390625002],\n                            [-74.46552734374995, -45.757226562499994],\n                            [-74.68984375, -45.66259765625],\n                            [-74.310546875, -45.17265625000002],\n                            [-74.31289062500002, -45.69150390625002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-73.63217773437498, -44.82148437499997],\n                            [-73.81845703125, -44.65214843750002],\n                            [-73.72392578124993, -44.544238281249974],\n                            [-73.63217773437498, -44.82148437499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [-72.98613281249999, -44.780078124999974],\n                            [-73.22846679687498, -44.85996093749999],\n                            [-73.39707031249998, -44.77431640624995],\n                            [-73.44506835937497, -44.641015624999966],\n                            [-73.20771484374993, -44.33496093749997],\n                            [-72.7763671875, -44.50859374999999],\n                            [-72.98613281249999, -44.780078124999974]\n                        ]\n                    ],\n                    [\n                        [\n                            [-73.73535156249997, -44.39453125000003],\n                            [-74.00205078125003, -44.59091796874998],\n                            [-73.728173828125, -45.195898437500034],\n                            [-74.016259765625, -45.344921875000026],\n                            [-74.61777343749998, -44.64794921874996],\n                            [-74.50180664062498, -44.47353515624995],\n                            [-74.09721679687496, -44.38935546875004],\n                            [-73.99492187499999, -44.140234375],\n                            [-73.70322265624998, -44.27412109375001],\n                            [-73.73535156249997, -44.39453125000003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-73.81064453125003, -43.827246093750006],\n                            [-73.95566406249998, -43.921972656250034],\n                            [-74.14296874999997, -43.872167968750006],\n                            [-73.81064453125003, -43.827246093750006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-73.77338867187498, -43.3458984375],\n                            [-74.114404296875, -43.35791015624996],\n                            [-74.387353515625, -43.231640625],\n                            [-74.03666992187496, -41.79550781249998],\n                            [-73.52783203124997, -41.89628906249999],\n                            [-73.42290039062499, -42.192871093750014],\n                            [-73.47080078124998, -42.46630859375004],\n                            [-73.78925781249993, -42.58574218750003],\n                            [-73.43632812499996, -42.9365234375],\n                            [-73.74965820312494, -43.15908203124995],\n                            [-73.77338867187498, -43.3458984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-78.80415039062501, -33.646484374999986],\n                            [-78.98945312499993, -33.66171874999998],\n                            [-78.87744140625003, -33.57519531250003],\n                            [-78.80415039062501, -33.646484374999986]\n                        ]\n                    ],\n                    [\n                        [\n                            [-109.27998046874994, -27.14042968749996],\n                            [-109.434130859375, -27.171289062500023],\n                            [-109.39047851562499, -27.068359375000014],\n                            [-109.27998046874994, -27.14042968749996]\n                        ]\n                    ],\n                    [\n                        [\n                            [-67.19487304687493, -22.821679687500037],\n                            [-67.00878906249994, -23.00136718750005],\n                            [-67.35620117187503, -24.033789062499963],\n                            [-68.25029296875002, -24.391992187500023],\n                            [-68.56201171875, -24.74736328125003],\n                            [-68.38422851562495, -25.091894531249977],\n                            [-68.59208984375002, -25.420019531250034],\n                            [-68.41450195312498, -26.153710937500023],\n                            [-68.59160156249999, -26.47041015624997],\n                            [-68.31865234374999, -26.973242187500006],\n                            [-68.59208984375002, -27.140039062499966],\n                            [-68.84633789062494, -27.153710937499994],\n                            [-69.17441406249998, -27.924707031250037],\n                            [-69.65693359374995, -28.413574218749986],\n                            [-69.82788085937497, -29.10322265624997],\n                            [-70.02680664062501, -29.324023437500017],\n                            [-69.95996093749997, -30.078320312500026],\n                            [-69.84428710937493, -30.175],\n                            [-69.95634765624996, -30.35820312500003],\n                            [-70.15322265625, -30.360937499999963],\n                            [-70.30908203124994, -31.02265625000004],\n                            [-70.51958007812493, -31.1484375],\n                            [-70.585205078125, -31.569433593749963],\n                            [-70.25439453125, -31.957714843750026],\n                            [-70.36376953125, -32.08349609374997],\n                            [-70.02197265625, -32.88457031250002],\n                            [-70.08486328125002, -33.20175781249998],\n                            [-69.81962890624999, -33.28378906249999],\n                            [-69.85244140625, -34.224316406250026],\n                            [-70.05205078124999, -34.30078124999997],\n                            [-70.39316406250003, -35.146875],\n                            [-70.55517578125, -35.246875],\n                            [-70.41572265625001, -35.52304687500002],\n                            [-70.40478515625, -36.06171874999998],\n                            [-71.05551757812498, -36.52373046874996],\n                            [-71.19218750000002, -36.84365234375004],\n                            [-71.16757812499998, -37.76230468749996],\n                            [-70.858642578125, -38.60449218750003],\n                            [-71.40156249999995, -38.93505859374996],\n                            [-71.53945312499997, -39.60244140624995],\n                            [-71.71992187499995, -39.63525390624997],\n                            [-71.65976562499998, -40.02080078125],\n                            [-71.81831054687493, -40.17666015624995],\n                            [-71.70898437499997, -40.381738281249994],\n                            [-71.93212890624994, -40.69169921874999],\n                            [-71.91127929687497, -41.650390624999986],\n                            [-71.75, -42.04677734375001],\n                            [-72.10820312499993, -42.25185546874995],\n                            [-72.14643554687498, -42.990039062499974],\n                            [-71.750634765625, -43.237304687499986],\n                            [-71.90498046875001, -43.34755859374998],\n                            [-71.68007812500002, -43.92958984374998],\n                            [-71.82001953124993, -44.38310546875],\n                            [-71.21259765624998, -44.44121093750003],\n                            [-71.15971679687496, -44.56025390625004],\n                            [-71.26113281250002, -44.763085937499966],\n                            [-72.06372070312503, -44.771875],\n                            [-72.04169921874998, -44.90419921875004],\n                            [-71.5962890625, -44.97919921875004],\n                            [-71.34931640624995, -45.33193359374995],\n                            [-71.74619140624998, -45.57890625],\n                            [-71.63154296874998, -45.95371093749998],\n                            [-71.87568359374998, -46.160546875],\n                            [-71.69965820312501, -46.6513671875],\n                            [-71.94023437499999, -46.83125],\n                            [-71.90498046875001, -47.201660156250014],\n                            [-72.34594726562497, -47.49267578124997],\n                            [-72.517919921875, -47.87636718749998],\n                            [-72.32832031250001, -48.11005859374998],\n                            [-72.35473632812497, -48.36582031250005],\n                            [-72.582861328125, -48.47539062499999],\n                            [-72.65126953125, -48.84160156249998],\n                            [-73.03364257812501, -49.014355468750004],\n                            [-73.13525390625, -49.30068359374999],\n                            [-73.46157226562497, -49.31386718750001],\n                            [-73.55419921875, -49.463867187500014],\n                            [-73.50126953124996, -50.125292968750024],\n                            [-73.15292968749998, -50.73828125000003],\n                            [-72.50981445312496, -50.607519531250034],\n                            [-72.34023437499997, -50.68183593749999],\n                            [-72.40766601562501, -51.54082031250002],\n                            [-71.91865234374995, -51.98955078125004],\n                            [-69.96025390624993, -52.00820312500002],\n                            [-68.443359375, -52.35664062500004],\n                            [-69.24101562499996, -52.20546874999997],\n                            [-69.62031249999995, -52.46474609374995],\n                            [-70.79511718749995, -52.76875],\n                            [-70.99584960937497, -53.77929687499997],\n                            [-71.29775390625002, -53.88339843750004],\n                            [-72.1744140625, -53.632324218749964],\n                            [-72.41289062500002, -53.35019531250004],\n                            [-71.94169921874993, -53.23408203125001],\n                            [-71.89169921874998, -53.523535156250006],\n                            [-71.79145507812498, -53.48457031249997],\n                            [-71.74052734374999, -53.232617187499976],\n                            [-71.28896484375002, -53.03369140624995],\n                            [-71.22714843750003, -52.810644531249984],\n                            [-71.38774414062496, -52.76425781250004],\n                            [-72.27802734374998, -53.13232421874997],\n                            [-72.54892578125, -53.4607421875],\n                            [-73.05273437499997, -53.24345703125005],\n                            [-72.72768554687502, -52.7623046875],\n                            [-72.453466796875, -52.814453124999964],\n                            [-72.11757812499997, -52.65],\n                            [-71.51127929687502, -52.60537109375],\n                            [-72.22568359374998, -52.52099609374995],\n                            [-72.43769531250001, -52.62578124999998],\n                            [-72.71210937499995, -52.53554687499999],\n                            [-73.12246093749997, -53.073925781249976],\n                            [-73.64521484374998, -52.83701171875003],\n                            [-73.2408203125, -52.707128906250034],\n                            [-73.12392578125, -52.487988281249976],\n                            [-73.24414062499997, -52.62402343749998],\n                            [-73.58569335937503, -52.68574218750003],\n                            [-74.01445312499999, -52.63935546875],\n                            [-74.26494140624993, -52.1048828125],\n                            [-73.83447265625, -52.23398437500001],\n                            [-73.68432617187494, -52.07773437499998],\n                            [-73.26044921874993, -52.157812500000034],\n                            [-72.79501953124998, -51.94951171875005],\n                            [-72.57084960937496, -52.200097656249945],\n                            [-72.67705078125002, -52.38466796874998],\n                            [-72.52333984374997, -52.255468750000034],\n                            [-72.62460937499998, -51.94648437499997],\n                            [-72.48964843750002, -51.76367187500003],\n                            [-72.76123046875, -51.57324218749996],\n                            [-73.16875, -51.45390624999998],\n                            [-72.60004882812495, -51.79912109374997],\n                            [-73.51816406250003, -52.04101562499996],\n                            [-73.75263671874993, -51.795507812500034],\n                            [-74.19667968749997, -51.68056640624997],\n                            [-73.92978515624995, -51.61787109374999],\n                            [-73.93950195312499, -51.26630859375005],\n                            [-74.81474609374996, -51.06289062499999],\n                            [-75.09467773437495, -50.68125],\n                            [-74.68574218749995, -50.662011718749945],\n                            [-74.77587890625003, -50.46992187499998],\n                            [-74.64448242187498, -50.360937499999984],\n                            [-74.365576171875, -50.487890625],\n                            [-74.13940429687503, -50.81777343749997],\n                            [-73.80654296875, -50.93837890625003],\n                            [-73.654443359375, -50.49267578125],\n                            [-73.97802734375003, -50.827050781249994],\n                            [-74.18559570312493, -50.485351562500014],\n                            [-73.95034179687497, -50.510546875],\n                            [-74.62958984374998, -50.19404296875],\n                            [-74.333740234375, -49.97460937499997],\n                            [-73.95859374999998, -49.994726562499984],\n                            [-74.32392578124995, -49.783398437500004],\n                            [-74.29082031249996, -49.604101562499984],\n                            [-73.83637695312493, -49.609375],\n                            [-74.09443359374993, -49.42968749999998],\n                            [-73.93496093749994, -49.02089843750001],\n                            [-74.2212890625, -49.500585937500034],\n                            [-74.36655273437503, -49.40048828124998],\n                            [-74.34101562499998, -48.59570312499998],\n                            [-74.00908203124996, -48.475],\n                            [-74.47441406249999, -48.46396484374996],\n                            [-74.58466796874998, -47.999023437500014],\n                            [-73.39106445312498, -48.14589843750001],\n                            [-73.60991210937499, -47.993945312500045],\n                            [-73.71586914062499, -47.65546875000001],\n                            [-73.94086914062498, -47.92939453125004],\n                            [-74.22705078124994, -47.96894531250001],\n                            [-74.654931640625, -47.702246093750034],\n                            [-74.5337890625, -47.567675781249974],\n                            [-74.24296874999999, -47.67929687499998],\n                            [-74.13408203125002, -47.590820312499986],\n                            [-74.48266601562497, -47.43046875],\n                            [-74.15839843749998, -47.18251953125002],\n                            [-74.31357421874998, -46.78818359374998],\n                            [-74.45419921875003, -46.76679687499997],\n                            [-74.51225585937496, -46.88515625000002],\n                            [-75.00595703125, -46.74111328124998],\n                            [-74.98417968750002, -46.51210937499995],\n                            [-75.54033203124999, -46.69873046874996],\n                            [-75.43037109374995, -46.93457031249996],\n                            [-75.70639648437498, -46.70527343749997],\n                            [-74.924462890625, -46.159667968750014],\n                            [-75.06669921874993, -45.874902343749994],\n                            [-74.15786132812497, -45.7671875],\n                            [-74.122705078125, -45.49619140625002],\n                            [-73.95717773437494, -45.40439453124998],\n                            [-73.825, -45.446875],\n                            [-74.01992187500002, -46.055859375],\n                            [-74.39296875, -46.21738281250005],\n                            [-73.96757812500002, -46.15410156250003],\n                            [-73.87871093749993, -45.846875],\n                            [-73.73525390624994, -45.81171875],\n                            [-73.70815429687502, -46.070312500000014],\n                            [-73.94863281249997, -46.533105468749966],\n                            [-73.845361328125, -46.56601562500002],\n                            [-73.59184570312493, -45.89912109375004],\n                            [-73.73076171874999, -45.47998046875],\n                            [-73.26621093749995, -45.346191406250014],\n                            [-72.933837890625, -45.45234374999997],\n                            [-73.44497070312497, -45.23818359374995],\n                            [-73.36245117187502, -44.97822265625001],\n                            [-72.73896484375001, -44.73417968750003],\n                            [-72.680078125, -44.59394531249997],\n                            [-72.66386718749999, -44.43642578124995],\n                            [-73.26508789062498, -44.16865234375001],\n                            [-73.22446289062498, -43.89794921875003],\n                            [-73.06879882812495, -43.86201171874998],\n                            [-72.99658203125, -43.63154296875001],\n                            [-73.07597656250002, -43.323632812499994],\n                            [-72.75800781249998, -43.039453125],\n                            [-72.84804687500002, -42.66914062499997],\n                            [-72.77392578125003, -42.505175781250045],\n                            [-72.63183593750003, -42.509667968749994],\n                            [-72.77324218749996, -42.257714843749994],\n                            [-72.63105468749995, -42.199804687500006],\n                            [-72.412353515625, -42.388183593750014],\n                            [-72.49941406249997, -41.98085937499999],\n                            [-72.82407226562503, -41.90878906249996],\n                            [-72.36040039062499, -41.64912109375],\n                            [-72.31826171875, -41.49902343749997],\n                            [-72.54238281250002, -41.690625],\n                            [-72.95283203124995, -41.51474609374998],\n                            [-73.24179687499995, -41.78085937500002],\n                            [-73.62402343750003, -41.77363281249997],\n                            [-73.73515625000002, -41.74248046875002],\n                            [-73.62392578125, -41.581347656250045],\n                            [-73.81074218749995, -41.51748046875001],\n                            [-73.96586914062493, -41.118261718750034],\n                            [-73.67099609375, -39.96318359374999],\n                            [-73.41040039062503, -39.78916015624998],\n                            [-73.22646484375002, -39.22441406250003],\n                            [-73.52021484375001, -38.509375],\n                            [-73.46479492187498, -38.04033203125003],\n                            [-73.66181640624998, -37.69853515625003],\n                            [-73.66240234375002, -37.341015625000026],\n                            [-73.60166015624998, -37.18847656250003],\n                            [-73.21596679687502, -37.16689453124998],\n                            [-73.11806640624997, -36.68837890625002],\n                            [-72.58735351562493, -35.759667968749994],\n                            [-72.62392578125002, -35.5857421875],\n                            [-72.22377929687494, -35.096191406250014],\n                            [-72.00283203124997, -34.16533203125],\n                            [-71.66435546875002, -33.65263671875],\n                            [-71.74296875, -33.09511718750001],\n                            [-71.45224609374998, -32.65957031250001],\n                            [-71.70893554687495, -30.62802734375002],\n                            [-71.66948242187499, -30.33037109374996],\n                            [-71.40039062499997, -30.142968749999966],\n                            [-71.31572265624996, -29.649707031250017],\n                            [-71.51923828124993, -28.926464843750026],\n                            [-71.30673828124998, -28.672460937499963],\n                            [-71.08652343749998, -27.814453124999957],\n                            [-70.92578125, -27.588671874999974],\n                            [-70.64658203124998, -26.329394531250017],\n                            [-70.71372070312498, -25.78417968749997],\n                            [-70.44536132812502, -25.17265624999999],\n                            [-70.57412109374994, -24.644335937500003],\n                            [-70.39233398437494, -23.565917968749957],\n                            [-70.59335937499995, -23.255468750000034],\n                            [-70.56318359374995, -23.057031250000023],\n                            [-70.33168945312494, -22.848632812500014],\n                            [-70.08002929687501, -21.356835937500037],\n                            [-70.19702148437494, -20.725390625],\n                            [-70.15742187499995, -19.70585937500003],\n                            [-70.41826171874999, -18.345605468750023],\n                            [-69.92636718749998, -18.206054687500014],\n                            [-69.80258789062498, -17.990234375000014],\n                            [-69.85209960937493, -17.70380859375001],\n                            [-69.68476562499995, -17.649804687500023],\n                            [-69.58642578125, -17.57324218749997],\n                            [-69.51093749999998, -17.50605468749997],\n                            [-69.31337890624997, -17.943164062500017],\n                            [-69.28232421875003, -17.96484375],\n                            [-69.09394531249993, -18.05048828125004],\n                            [-69.14545898437495, -18.14404296875],\n                            [-69.09228515624994, -18.28242187500004],\n                            [-69.02680664062493, -18.65625],\n                            [-68.97885742187503, -18.81298828125003],\n                            [-68.96831054687502, -18.967968749999983],\n                            [-68.85795898437499, -19.09335937500005],\n                            [-68.62055664062495, -19.29667968749999],\n                            [-68.54785156249997, -19.341113281249974],\n                            [-68.49199218749996, -19.381933593750034],\n                            [-68.47016601562495, -19.409960937499974],\n                            [-68.46289062499997, -19.43281250000001],\n                            [-68.57529296874998, -19.56015625000002],\n                            [-68.69829101562499, -19.721093750000037],\n                            [-68.69619140625, -19.74072265625003],\n                            [-68.57827148437494, -19.856542968750006],\n                            [-68.559375, -19.902343750000014],\n                            [-68.56069335937502, -19.96708984374996],\n                            [-68.75932617187499, -20.115527343750003],\n                            [-68.74516601562493, -20.45859375],\n                            [-68.48432617187498, -20.628417968749957],\n                            [-68.55825195312497, -20.90195312499999],\n                            [-68.197021484375, -21.30029296874997],\n                            [-68.18642578124997, -21.618554687499966],\n                            [-67.88173828124997, -22.493359375000026],\n                            [-67.87944335937496, -22.822949218750026],\n                            [-67.57993164062495, -22.89169921874999],\n                            [-67.36225585937493, -22.85517578125001],\n                            [-67.19487304687493, -22.821679687500037]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Chile\", \"childNum\": 26 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [110.88876953125006, 19.99194335937497],\n                            [111.01367187500003, 19.65546875000001],\n                            [110.64091796875002, 19.291210937499955],\n                            [110.45126953125012, 18.747949218750023],\n                            [110.06738281249997, 18.447558593750045],\n                            [109.51933593750007, 18.21826171875003],\n                            [108.7015625, 18.535253906250034],\n                            [108.66552734375003, 19.304101562499994],\n                            [109.27666015625002, 19.761132812500023],\n                            [109.17744140625004, 19.768457031250023],\n                            [109.26347656250007, 19.882666015625006],\n                            [110.1715820312501, 20.053710937500057],\n                            [110.58818359375002, 19.976367187500017],\n                            [110.6517578125, 20.137744140625017],\n                            [110.88876953125006, 19.99194335937497]\n                        ]\n                    ],\n                    [\n                        [\n                            [110.38515625000005, 21.093164062499966],\n                            [110.52158203125006, 21.083105468750063],\n                            [110.50390625000003, 20.96772460937501],\n                            [110.28095703125004, 21.001171874999983],\n                            [110.38515625000005, 21.093164062499966]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.64375, 21.63964843750003],\n                            [112.525, 21.62304687500003],\n                            [112.64765625000004, 21.710253906250017],\n                            [112.64375, 21.63964843750003]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.79023437500004, 21.601855468750045],\n                            [112.78203125000007, 21.772265625000045],\n                            [112.86259765625002, 21.75263671875004],\n                            [112.79023437500004, 21.601855468750045]\n                        ]\n                    ],\n                    [\n                        [\n                            [118.1830078125, 24.496289062499983],\n                            [118.0905273437501, 24.446142578125063],\n                            [118.10380859375002, 24.552343750000034],\n                            [118.1830078125, 24.496289062499983]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.82089843750006, 25.45698242187504],\n                            [119.70029296875012, 25.432714843750063],\n                            [119.72255859375005, 25.638818359375023],\n                            [119.83837890625003, 25.591064453125],\n                            [119.82089843750006, 25.45698242187504]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.2513671875, 28.086425781250057],\n                            [121.13154296875004, 28.062597656250006],\n                            [121.20546875, 28.204394531250017],\n                            [121.2513671875, 28.086425781250057]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.29589843750003, 29.96342773437499],\n                            [122.02402343750012, 30.01333007812505],\n                            [121.96943359375004, 30.143115234375017],\n                            [122.28447265625007, 30.068017578124994],\n                            [122.29589843750003, 29.96342773437499]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.86269531250005, 31.492285156249977],\n                            [121.519921875, 31.549609375000017],\n                            [121.2111328125001, 31.80537109375001],\n                            [121.86269531250005, 31.492285156249977]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.52695312500012, 42.535400390625],\n                            [130.24667968750012, 42.744824218749955],\n                            [130.24033203125006, 42.891796874999955],\n                            [129.89824218750002, 42.998144531250034],\n                            [129.69785156250012, 42.448144531249994],\n                            [129.3136718750001, 42.41357421874997],\n                            [128.92343750000006, 42.038232421874966],\n                            [128.04521484375007, 41.9875],\n                            [128.28925781250004, 41.60742187500006],\n                            [128.14941406249997, 41.38774414062496],\n                            [127.17968750000003, 41.531347656250006],\n                            [126.95478515625004, 41.76948242187501],\n                            [126.74306640625, 41.724853515625],\n                            [125.98906250000002, 40.904638671875034],\n                            [124.8893554687501, 40.459814453125006],\n                            [124.36210937500002, 40.004052734374994],\n                            [124.10576171875002, 39.84101562499998],\n                            [123.65087890625003, 39.881591796875],\n                            [122.8400390625001, 39.600830078125],\n                            [121.98232421875, 39.05317382812498],\n                            [121.67724609374997, 39.00341796875006],\n                            [121.64990234375003, 38.865087890625034],\n                            [121.16357421874997, 38.73164062500001],\n                            [121.10673828125002, 38.920800781249994],\n                            [121.6798828125001, 39.10869140625002],\n                            [121.62763671875004, 39.22016601562498],\n                            [121.81845703125006, 39.38652343750002],\n                            [121.27548828125006, 39.38476562500003],\n                            [121.26748046875, 39.544677734375],\n                            [121.51757812499997, 39.638964843750045],\n                            [121.51738281250002, 39.84482421875006],\n                            [121.8009765625001, 39.950537109375006],\n                            [122.27500000000012, 40.541845703125034],\n                            [121.83486328125005, 40.97426757812502],\n                            [121.72929687500002, 40.84614257812504],\n                            [121.1745117187501, 40.901269531249994],\n                            [120.47910156250006, 40.23095703125003],\n                            [119.39111328125003, 39.75249023437499],\n                            [118.976953125, 39.182568359374955],\n                            [118.29785156249997, 39.067089843749955],\n                            [118.04091796875, 39.22675781249998],\n                            [117.86572265625003, 39.191259765625034],\n                            [117.61669921875003, 38.852880859375034],\n                            [117.5578125000001, 38.625146484374994],\n                            [117.76669921875012, 38.311669921874994],\n                            [118.01494140625007, 38.18339843749996],\n                            [118.94003906250006, 38.04277343750002],\n                            [119.08916015625007, 37.70073242187496],\n                            [118.95263671875003, 37.33115234374998],\n                            [119.28740234375002, 37.138281250000034],\n                            [119.76054687500007, 37.15507812499999],\n                            [120.31152343750003, 37.62270507812505],\n                            [120.2572265625, 37.67900390624996],\n                            [120.75, 37.83393554687501],\n                            [121.64023437500012, 37.46035156250002],\n                            [122.05664062500003, 37.528906250000034],\n                            [122.66699218750003, 37.40283203125003],\n                            [122.4466796875, 37.06811523437503],\n                            [122.51972656250004, 36.94682617187502],\n                            [122.34091796875012, 36.83222656250004],\n                            [121.93271484375006, 36.95947265625003],\n                            [121.05380859375006, 36.61137695312499],\n                            [120.81083984375007, 36.6328125],\n                            [120.89580078125007, 36.44414062500002],\n                            [120.71152343750006, 36.41328125000004],\n                            [120.6378906250001, 36.129931640625045],\n                            [120.39306640625003, 36.053857421874994],\n                            [120.32773437500006, 36.228173828124994],\n                            [120.18330078125004, 36.20244140624999],\n                            [120.094140625, 36.11889648437503],\n                            [120.28476562500006, 35.98442382812499],\n                            [119.42968749999997, 35.301416015624994],\n                            [119.16533203125002, 34.84882812499998],\n                            [119.20097656250002, 34.748437499999966],\n                            [120.26669921875006, 34.274023437500034],\n                            [120.87109374999997, 33.016503906249994],\n                            [120.8532226562501, 32.66137695312503],\n                            [121.34169921875005, 32.42504882812503],\n                            [121.40390625000006, 32.20625],\n                            [121.85634765625, 31.816455078125045],\n                            [121.86630859375006, 31.703564453124955],\n                            [121.68085937500004, 31.71215820312503],\n                            [121.351953125, 31.85878906250005],\n                            [120.97353515625, 31.86938476562497],\n                            [120.52011718750006, 32.10585937500002],\n                            [120.03593750000002, 31.93627929687503],\n                            [120.7155273437501, 31.983740234375006],\n                            [120.7877929687501, 31.81977539062501],\n                            [121.66064453124997, 31.319726562499994],\n                            [121.87792968750003, 30.91699218750003],\n                            [121.41894531249997, 30.789794921875057],\n                            [120.8214843750001, 30.354638671875023],\n                            [120.44980468750006, 30.38784179687505],\n                            [120.19462890625002, 30.241308593750034],\n                            [120.49453125, 30.303076171875006],\n                            [120.63339843750006, 30.133154296875034],\n                            [121.25800781250004, 30.30410156250005],\n                            [121.67792968750004, 29.979101562500006],\n                            [122.08291015625005, 29.870361328125057],\n                            [121.50625, 29.484570312499955],\n                            [121.94121093750002, 29.605908203124983],\n                            [121.91777343750007, 29.13500976562497],\n                            [121.71748046875004, 29.25634765625],\n                            [121.48710937500007, 29.193164062500017],\n                            [121.67968749999997, 28.953125],\n                            [121.54003906250003, 28.931884765625],\n                            [121.6625, 28.851416015625034],\n                            [121.47519531250006, 28.64140625],\n                            [121.60996093750006, 28.29213867187505],\n                            [121.27226562500002, 28.222119140624983],\n                            [121.14570312500004, 28.32666015624997],\n                            [120.95859375000006, 28.037011718750023],\n                            [120.74765625000006, 28.00996093750001],\n                            [120.83300781249997, 27.891455078125034],\n                            [120.58750000000012, 27.580761718749983],\n                            [120.60751953125012, 27.41240234374996],\n                            [120.2787109375, 27.097070312500023],\n                            [120.08671875000007, 26.67158203125004],\n                            [119.88222656250005, 26.610449218750006],\n                            [119.82421874999997, 26.84638671875001],\n                            [119.71044921874997, 26.728662109375023],\n                            [119.58818359375002, 26.784960937500045],\n                            [119.8810546875001, 26.33417968750004],\n                            [119.46308593750004, 26.05468750000003],\n                            [119.13945312500007, 26.12177734375001],\n                            [119.33203124999997, 25.94873046875003],\n                            [119.61875000000012, 26.003564453124994],\n                            [119.53945312500005, 25.59125976562504],\n                            [119.6224609375, 25.391162109375017],\n                            [119.180078125, 25.449804687499977],\n                            [119.285546875, 25.232226562500074],\n                            [118.97753906249997, 25.209277343750017],\n                            [118.90908203125005, 24.92890625000001],\n                            [118.63691406250004, 24.835546874999977],\n                            [118.65703125000002, 24.621435546874977],\n                            [118.0871093750001, 24.627001953125045],\n                            [118.00595703125006, 24.48198242187499],\n                            [117.84267578125005, 24.47431640625004],\n                            [118.0560546875, 24.24609374999997],\n                            [117.62822265625002, 23.836718750000074],\n                            [117.46640625000012, 23.84057617187497],\n                            [117.36767578124997, 23.58862304687497],\n                            [117.29082031250007, 23.71435546875],\n                            [117.08251953124997, 23.578759765625023],\n                            [116.91064453124997, 23.646679687499983],\n                            [116.86093750000006, 23.453076171874983],\n                            [116.62939453124997, 23.353857421875034],\n                            [116.69882812500006, 23.277783203124983],\n                            [116.53828125000004, 23.17968749999997],\n                            [116.47070312499997, 22.945898437500034],\n                            [116.25185546875005, 22.981347656249994],\n                            [115.85214843750006, 22.801562500000045],\n                            [115.64042968750002, 22.853417968750023],\n                            [115.49833984375002, 22.718847656250063],\n                            [115.19580078125003, 22.81728515625005],\n                            [114.85380859375007, 22.616796875000063],\n                            [114.65166015625002, 22.755273437500023],\n                            [114.55419921874997, 22.52890625],\n                            [114.26601562500005, 22.540966796874983],\n                            [114.01542968750007, 22.51191406250001],\n                            [113.61962890624997, 22.861425781249977],\n                            [113.6205078125, 23.12749023437499],\n                            [113.51972656250004, 23.102099609375074],\n                            [113.33105468749997, 22.912011718749966],\n                            [113.55302734375002, 22.594042968750045],\n                            [113.54912109375002, 22.225195312500034],\n                            [113.14902343750012, 22.075],\n                            [113.08876953125, 22.207958984374983],\n                            [112.95390625000007, 21.907324218750034],\n                            [112.80859374999997, 21.944628906250074],\n                            [112.58632812500005, 21.77685546875],\n                            [112.35966796875007, 21.97802734375003],\n                            [112.30498046875002, 21.74169921875003],\n                            [111.94394531250012, 21.84965820312499],\n                            [111.60273437500004, 21.55908203125003],\n                            [111.01689453125007, 21.51171874999997],\n                            [110.56718750000002, 21.21406250000001],\n                            [110.41093750000007, 21.33813476562497],\n                            [110.15400390625004, 20.944628906250017],\n                            [110.36542968750004, 20.837597656249955],\n                            [110.31308593750012, 20.67167968749999],\n                            [110.51152343750007, 20.51826171875001],\n                            [110.34472656249997, 20.29482421875005],\n                            [109.88251953125004, 20.364062500000045],\n                            [109.96835937500006, 20.448144531250023],\n                            [109.66259765625003, 20.91689453125005],\n                            [109.68125000000012, 21.13164062499999],\n                            [109.93076171875012, 21.480566406250034],\n                            [109.6869140625, 21.52460937500004],\n                            [109.56640624999997, 21.690576171874994],\n                            [109.54404296875012, 21.537939453125006],\n                            [109.14863281250004, 21.425537109375],\n                            [109.1017578125001, 21.59047851562505],\n                            [108.77167968750004, 21.63046875],\n                            [108.59375, 21.901025390624994],\n                            [108.47988281250005, 21.904638671875006],\n                            [108.50214843750004, 21.633447265624994],\n                            [108.32480468750006, 21.693505859374994],\n                            [108.24628906250004, 21.55839843749999],\n                            [107.97265624999997, 21.507958984375023],\n                            [107.75927734374997, 21.655029296875057],\n                            [107.35117187500012, 21.60888671874997],\n                            [106.97099609375002, 21.923925781250034],\n                            [106.66357421875003, 21.97890625000005],\n                            [106.55039062500006, 22.501367187499994],\n                            [106.78027343749997, 22.778906250000034],\n                            [106.54179687500007, 22.908349609375023],\n                            [106.2790039062501, 22.857470703125045],\n                            [106.14843749999997, 22.970068359375006],\n                            [105.8429687500001, 22.922802734374955],\n                            [105.27539062500003, 23.34521484375003],\n                            [104.86474609375003, 23.136376953125023],\n                            [104.68730468750002, 22.822216796874983],\n                            [104.37177734375004, 22.704052734374983],\n                            [104.14306640624997, 22.800146484375006],\n                            [103.94150390625006, 22.540087890625045],\n                            [103.62021484375006, 22.782031250000045],\n                            [103.49296875000007, 22.587988281250034],\n                            [103.32666015625003, 22.769775390625057],\n                            [102.98193359374997, 22.4482421875],\n                            [102.47089843750004, 22.75092773437501],\n                            [102.40644531250004, 22.70800781249997],\n                            [102.2370117187501, 22.466015624999983],\n                            [102.1759765625001, 22.414648437500006],\n                            [102.12744140624997, 22.379199218750045],\n                            [101.84179687500003, 22.38847656249999],\n                            [101.75996093750004, 22.490332031250034],\n                            [101.73876953124997, 22.495263671874994],\n                            [101.70751953125003, 22.486572265625],\n                            [101.67148437500006, 22.462304687500023],\n                            [101.64619140625004, 22.405419921874966],\n                            [101.61992187500002, 22.32744140624999],\n                            [101.56787109374997, 22.27636718749997],\n                            [101.52451171875006, 22.25366210937497],\n                            [101.7365234375001, 21.826513671874977],\n                            [101.74394531250007, 21.77797851562505],\n                            [101.74726562500004, 21.605761718750045],\n                            [101.72294921875007, 21.31494140625003],\n                            [101.80058593750007, 21.212597656249983],\n                            [101.78349609375007, 21.204150390625017],\n                            [101.728125, 21.156396484374994],\n                            [101.7047851562501, 21.15014648437503],\n                            [101.54238281250005, 21.23427734375005],\n                            [101.2814453125001, 21.184130859375045],\n                            [101.24785156250007, 21.197314453125045],\n                            [101.22441406250002, 21.223730468750034],\n                            [101.21181640625, 21.278222656250023],\n                            [101.2199218750001, 21.34243164062505],\n                            [101.17539062500006, 21.407519531250074],\n                            [101.19667968750005, 21.522070312500063],\n                            [101.1388671875001, 21.567480468749977],\n                            [101.07978515625004, 21.75585937499997],\n                            [100.60458984375012, 21.471777343750006],\n                            [100.14765625000004, 21.480517578125017],\n                            [99.94072265625007, 21.75874023437504],\n                            [99.9176757812501, 22.02802734375001],\n                            [99.19296875000006, 22.12597656249997],\n                            [99.50712890625002, 22.959130859374994],\n                            [99.41806640625006, 23.069238281250023],\n                            [98.86376953125003, 23.191259765625034],\n                            [98.8322265625001, 23.624365234374977],\n                            [98.67675781250003, 23.905078125000045],\n                            [98.83505859375006, 24.121191406250034],\n                            [98.2125, 24.110644531250017],\n                            [97.56455078125012, 23.911035156250023],\n                            [97.7082031250001, 24.228759765625],\n                            [97.53144531250004, 24.49169921875003],\n                            [97.58330078125002, 24.77480468750005],\n                            [97.73789062500006, 24.869873046875057],\n                            [97.8195312500001, 25.251855468749994],\n                            [98.01074218749997, 25.292529296875017],\n                            [98.14287109375007, 25.571093750000017],\n                            [98.33378906250007, 25.586767578125006],\n                            [98.65625, 25.86357421874999],\n                            [98.56406250000006, 26.072412109374994],\n                            [98.68554687499997, 26.189355468750023],\n                            [98.7384765625001, 26.785742187500006],\n                            [98.65117187500007, 27.572460937499983],\n                            [98.4525390625, 27.6572265625],\n                            [98.29882812499997, 27.550097656250045],\n                            [98.06162109375012, 28.185888671874977],\n                            [97.59921875000006, 28.51704101562504],\n                            [97.53789062500002, 28.510205078124983],\n                            [97.43144531250002, 28.353906250000023],\n                            [97.35644531249997, 28.254492187500006],\n                            [97.32158929493812, 28.217097107438057],\n                            [97.3027336276825, 28.08710519614969],\n                            [97.34382779482424, 27.982305259167095],\n                            [97.04929369561631, 27.76000444316393],\n                            [96.96494598325154, 27.699301564540924],\n                            [96.19423412199573, 28.04146177926422],\n                            [95.73730002295082, 28.117613231051525],\n                            [95.11298892962586, 27.748338353239472],\n                            [94.07167814294401, 27.588707868507477],\n                            [93.61247595136224, 27.323800298697016],\n                            [93.30681393470121, 26.786120363519142],\n                            [92.74319481218781, 26.833531317384058],\n                            [92.04974640832253, 26.874866505386724],\n                            [92.07342257335648, 26.915311275859864],\n                            [92.06813426293174, 26.9752569185349],\n                            [92.02985139563152, 27.03987087331446],\n                            [91.99856592104459, 27.079255842602592],\n                            [91.99177981607339, 27.100605151743654],\n                            [92.0025114452454, 27.147290053160265],\n                            [92.03101585307499, 27.214271359861193],\n                            [92.08387457645458, 27.29090135496722],\n                            [92.04520857607581, 27.364442429033787],\n                            [91.99069061380867, 27.450181624174498],\n                            [91.95099838734396, 27.45828799115413],\n                            [91.85276579410389, 27.438593286730903],\n                            [91.74366351462741, 27.442853010105477],\n                            [91.59505352446729, 27.557262710287986],\n                            [91.63193359375012, 27.759960937499983],\n                            [91.64189453125002, 27.923242187500023],\n                            [91.36259958579089, 28.02438066407592],\n                            [91.27304687500012, 28.078369140625],\n                            [91.22587890625007, 28.071240234374983],\n                            [91.07773437500012, 27.974462890624977],\n                            [91.02080078125002, 27.970068359374977],\n                            [90.71572265625, 28.071728515624983],\n                            [90.63007812500004, 28.078564453124955],\n                            [90.47734375000007, 28.07084960937499],\n                            [90.3527343750001, 28.080224609375023],\n                            [90.33310546875012, 28.093994140625],\n                            [90.36298828125004, 28.21650390625001],\n                            [90.34824218750006, 28.24394531249999],\n                            [90.22080078125006, 28.27773437500005],\n                            [90.10449218749997, 28.302050781250017],\n                            [89.98105468750006, 28.311181640625023],\n                            [89.8978515625, 28.29414062500001],\n                            [89.81689453125003, 28.25629882812501],\n                            [89.74980468750002, 28.18818359375001],\n                            [89.65273437500005, 28.158300781250034],\n                            [89.53691406250007, 28.10742187499997],\n                            [89.4806640625001, 28.059960937499994],\n                            [88.89140625000002, 27.316064453124966],\n                            [88.83251953125003, 27.36284179687499],\n                            [88.7648437500001, 27.429882812499983],\n                            [88.74902343749997, 27.521875],\n                            [88.82988281250002, 27.76738281249999],\n                            [88.84882812500004, 27.86865234375],\n                            [88.80371093750003, 28.006933593750034],\n                            [88.57792968750002, 28.093359375000034],\n                            [88.42597656250004, 28.01166992187501],\n                            [88.27519531250007, 27.968847656250006],\n                            [88.14111328125003, 27.94892578125001],\n                            [88.10898437500006, 27.933007812499966],\n                            [88.10976562500005, 27.870605468750057],\n                            [87.8607421875, 27.886083984375006],\n                            [87.62255859374997, 27.81518554687503],\n                            [87.29072265625004, 27.821923828124994],\n                            [87.14140625000002, 27.838330078124955],\n                            [87.02011718750006, 27.928662109374983],\n                            [86.9337890625001, 27.96845703125001],\n                            [86.84238281250012, 27.99916992187505],\n                            [86.750390625, 28.022070312500006],\n                            [86.71962890625005, 28.070654296875034],\n                            [86.69052734375006, 28.09492187500001],\n                            [86.61445312500004, 28.10302734374997],\n                            [86.55449218750007, 28.08520507812497],\n                            [86.51689453125007, 27.963525390624966],\n                            [86.40869140625003, 27.928662109374983],\n                            [86.32861328124997, 27.95952148437496],\n                            [86.2179687500001, 28.022070312500006],\n                            [86.13701171875002, 28.114355468750063],\n                            [86.07871093750006, 28.08359375],\n                            [86.0641601562501, 27.934716796874966],\n                            [85.99453125000005, 27.910400390625],\n                            [85.95410156249997, 27.92822265624997],\n                            [85.92167968750002, 27.989697265624983],\n                            [85.84023437500005, 28.135351562499977],\n                            [85.75947265625004, 28.220654296874955],\n                            [85.67832031250012, 28.277441406249977],\n                            [85.41064453125003, 28.27602539062505],\n                            [85.21210937500004, 28.292626953124966],\n                            [85.1224609375, 28.315966796875017],\n                            [85.08857421875004, 28.37226562500001],\n                            [85.121484375, 28.484277343750023],\n                            [85.16015624999997, 28.571875],\n                            [85.15908203125, 28.592236328124983],\n                            [85.1263671875, 28.602636718750063],\n                            [85.06914062500007, 28.60966796874999],\n                            [84.85507812500006, 28.553613281250023],\n                            [84.796875, 28.560205078125023],\n                            [84.2287109375001, 28.911767578124966],\n                            [84.17558593750002, 29.036376953125057],\n                            [84.12783203125005, 29.15629882812496],\n                            [84.10136718750002, 29.21997070312497],\n                            [84.02197265624997, 29.25385742187504],\n                            [83.93593750000005, 29.27949218750001],\n                            [83.58349609375003, 29.18359375000003],\n                            [83.15546875000004, 29.612646484375034],\n                            [82.22070312500003, 30.063867187500023],\n                            [82.04335937500005, 30.326757812500034],\n                            [81.8548828125, 30.362402343750006],\n                            [81.64189453125007, 30.3875],\n                            [81.4171875000001, 30.33759765625001],\n                            [81.25507812500004, 30.09331054687499],\n                            [81.17714843750005, 30.039892578125034],\n                            [80.98544921875006, 30.23710937499999],\n                            [80.87353515625003, 30.290576171875045],\n                            [80.19121093750002, 30.56840820312496],\n                            [80.20712890625006, 30.683740234375023],\n                            [79.92451171875004, 30.888769531250034],\n                            [79.66425781250004, 30.96523437499999],\n                            [79.38847656250007, 31.064208984375],\n                            [79.10712890625004, 31.402636718750017],\n                            [78.74355468750005, 31.323779296875017],\n                            [78.7550781250001, 31.55029296875],\n                            [78.69345703125006, 31.740380859374994],\n                            [78.72558593750003, 31.983789062500023],\n                            [78.49589843750002, 32.21577148437504],\n                            [78.4552734375001, 32.30034179687502],\n                            [78.41748046874997, 32.466699218749994],\n                            [78.38964843749997, 32.51987304687498],\n                            [78.73671875, 32.55839843750002],\n                            [78.75351562500012, 32.49926757812506],\n                            [78.91894531249997, 32.35820312500002],\n                            [79.16992187500003, 32.497216796874994],\n                            [79.14550781250003, 33.00146484375006],\n                            [79.10283203125007, 33.05253906249996],\n                            [79.13515625000005, 33.17192382812496],\n                            [79.1125, 33.22626953125001],\n                            [78.94843750000004, 33.346533203125006],\n                            [78.86503906250002, 33.43110351562501],\n                            [78.78378906250006, 33.80878906250004],\n                            [78.72666015625006, 34.013378906249955],\n                            [78.97060546875, 34.22822265625004],\n                            [78.93642578125, 34.35195312500002],\n                            [78.86484375000006, 34.39033203125001],\n                            [78.32695312500007, 34.60639648437498],\n                            [78.15849609375002, 34.94648437499998],\n                            [78.07578125000006, 35.13491210937502],\n                            [78.0426757812501, 35.47978515625002],\n                            [77.79941406250006, 35.49589843750002],\n                            [77.44648437500004, 35.47558593750006],\n                            [77.29482421875005, 35.508154296875034],\n                            [77.09003906250004, 35.55205078124999],\n                            [76.87890625000003, 35.61328125000003],\n                            [76.76689453125002, 35.661718750000034],\n                            [76.72753906250003, 35.67866210937504],\n                            [76.63183593749997, 35.729394531249966],\n                            [76.56347656249997, 35.77299804687499],\n                            [76.55126953124997, 35.887060546875034],\n                            [76.50205078125006, 35.87822265625002],\n                            [76.38574218750003, 35.837158203125],\n                            [76.25166015625004, 35.8109375],\n                            [76.17783203125012, 35.810546875],\n                            [76.14785156250005, 35.82900390625002],\n                            [76.07089843750006, 35.983007812500034],\n                            [75.91230468750004, 36.048974609374994],\n                            [75.97441406250007, 36.38242187500006],\n                            [75.9518554687501, 36.458105468750034],\n                            [75.9330078125, 36.52158203124998],\n                            [75.840234375, 36.64970703124999],\n                            [75.7721679687501, 36.694921875000034],\n                            [75.6671875000001, 36.741992187500045],\n                            [75.57373046874997, 36.75932617187502],\n                            [75.46025390625002, 36.725048828124955],\n                            [75.42421875000005, 36.73823242187498],\n                            [75.37685546875, 36.88369140625005],\n                            [75.34667968749997, 36.913476562499966],\n                            [75.05390625000004, 36.98715820312498],\n                            [74.94912109375, 36.96835937500006],\n                            [74.88925781250006, 36.95244140625002],\n                            [74.69218750000007, 37.035742187500006],\n                            [74.60058593749997, 37.03666992187502],\n                            [74.54140625, 37.02216796875001],\n                            [74.52646484375006, 37.03066406250005],\n                            [74.49794921875, 37.057226562500034],\n                            [74.37617187500004, 37.13735351562502],\n                            [74.37216796875006, 37.15771484375],\n                            [74.558984375, 37.23662109374999],\n                            [74.66894531250003, 37.266699218750006],\n                            [74.72666015625006, 37.29072265625001],\n                            [74.7389648437501, 37.28564453125003],\n                            [74.76738281250002, 37.249169921874966],\n                            [74.840234375, 37.22504882812504],\n                            [74.89130859375004, 37.231640624999955],\n                            [75.11875, 37.38569335937498],\n                            [74.8942382812501, 37.60141601562498],\n                            [74.81230468750002, 38.46030273437498],\n                            [74.27744140625, 38.659765625000034],\n                            [74.02558593750004, 38.53984375000002],\n                            [73.80166015625, 38.60688476562501],\n                            [73.69609375000007, 38.85429687499996],\n                            [73.8052734375, 38.968652343749994],\n                            [73.60732421875, 39.229199218749955],\n                            [73.63632812500006, 39.396679687499955],\n                            [73.63164062500007, 39.44887695312502],\n                            [73.82294921875004, 39.48896484375004],\n                            [73.90712890625, 39.578515624999966],\n                            [73.9146484375, 39.60649414062499],\n                            [73.88251953125004, 39.71455078124998],\n                            [73.83974609375005, 39.76284179687505],\n                            [73.8353515625, 39.800146484375006],\n                            [73.85625, 39.828662109375045],\n                            [73.88457031250002, 39.87792968750006],\n                            [73.93876953125002, 39.97880859374999],\n                            [73.99160156250005, 40.04311523437502],\n                            [74.83046875, 40.32851562499999],\n                            [74.80126953124997, 40.428515625000045],\n                            [74.83515625000004, 40.482617187499955],\n                            [74.865625, 40.493505859375034],\n                            [75.0044921875, 40.44951171874996],\n                            [75.11132812499997, 40.4541015625],\n                            [75.24101562500002, 40.48027343750002],\n                            [75.52080078125002, 40.627539062500006],\n                            [75.55556640625, 40.625195312499955],\n                            [75.6771484375, 40.305810546874994],\n                            [75.87197265625, 40.30322265625],\n                            [76.25830078124997, 40.43076171875006],\n                            [76.3185546875001, 40.352246093749955],\n                            [76.39638671875005, 40.389794921874966],\n                            [76.4801757812501, 40.44951171874996],\n                            [76.57792968750002, 40.577880859375],\n                            [76.62216796875006, 40.66235351562497],\n                            [76.6398437500001, 40.74223632812499],\n                            [76.66113281249997, 40.77963867187498],\n                            [76.70839843750005, 40.818115234375],\n                            [76.82402343750002, 40.982324218749966],\n                            [76.90771484374997, 41.02416992187497],\n                            [76.98662109375002, 41.039160156250006],\n                            [77.58173828125004, 40.99277343750006],\n                            [77.71933593750012, 41.024316406249994],\n                            [77.81523437500002, 41.05561523437498],\n                            [77.9564453125, 41.05068359375005],\n                            [78.1234375, 41.07563476562498],\n                            [78.34628906250012, 41.28144531249998],\n                            [78.36240234375012, 41.37163085937496],\n                            [78.44287109374997, 41.41752929687499],\n                            [78.742578125, 41.56005859375],\n                            [79.29355468750006, 41.78281249999998],\n                            [79.76611328124997, 41.89887695312501],\n                            [79.84042968750012, 41.99575195312502],\n                            [79.90966796875003, 42.014990234375034],\n                            [80.21621093750005, 42.03242187500004],\n                            [80.23515625000007, 42.04345703124997],\n                            [80.24619140625012, 42.05981445312503],\n                            [80.209375, 42.190039062500006],\n                            [80.20224609375012, 42.73447265624998],\n                            [80.53896484375005, 42.873486328124955],\n                            [80.39023437500006, 43.043115234374966],\n                            [80.78574218750006, 43.16157226562504],\n                            [80.35527343750002, 44.09726562500006],\n                            [80.48154296875006, 44.71464843749999],\n                            [79.871875, 44.88378906249997],\n                            [80.05917968750012, 45.006445312500006],\n                            [81.69199218750012, 45.34936523437497],\n                            [81.94492187500006, 45.16083984375001],\n                            [82.26660156249997, 45.21909179687498],\n                            [82.52148437500003, 45.12548828125],\n                            [82.61162109375007, 45.424267578124955],\n                            [82.31523437500002, 45.59492187499998],\n                            [83.02949218750004, 47.18593750000002],\n                            [84.016015625, 46.97050781250002],\n                            [84.66660156250006, 46.97236328125004],\n                            [84.78613281249997, 46.83071289062505],\n                            [85.484765625, 47.06352539062496],\n                            [85.65664062500005, 47.254638671875],\n                            [85.52597656250006, 47.915625],\n                            [85.7494140625, 48.38505859374999],\n                            [86.54941406250012, 48.52861328125002],\n                            [86.8083007812501, 49.04970703125002],\n                            [87.32285156250012, 49.085791015625006],\n                            [87.41669921875004, 49.07661132812501],\n                            [87.5158203125001, 49.122412109375006],\n                            [87.7625, 49.16582031249996],\n                            [87.81425781250002, 49.162304687499955],\n                            [87.87216796875012, 49.000146484374966],\n                            [87.74316406250003, 48.88164062499999],\n                            [87.83183593750007, 48.79165039062505],\n                            [88.02792968750006, 48.735595703125],\n                            [88.06005859375003, 48.707177734374966],\n                            [87.9796875000001, 48.55512695312498],\n                            [88.30996093750005, 48.47207031250002],\n                            [88.41396484375, 48.403417968750006],\n                            [88.51708984374997, 48.384472656249955],\n                            [88.56679687500005, 48.31743164062496],\n                            [88.57597656250007, 48.220166015624955],\n                            [88.68183593750004, 48.170556640624994],\n                            [88.83828125000005, 48.101708984374994],\n                            [88.91777343750007, 48.089013671874966],\n                            [89.04765625000007, 48.002539062500034],\n                            [89.47919921875004, 48.02905273437503],\n                            [89.5609375, 48.00395507812496],\n                            [89.778125, 47.82700195312498],\n                            [89.83134765625002, 47.82329101562502],\n                            [89.91044921875007, 47.844335937500034],\n                            [89.95869140625004, 47.88632812499998],\n                            [90.02792968750012, 47.877685546875],\n                            [90.1032226562501, 47.74541015624996],\n                            [90.19101562500012, 47.70209960937501],\n                            [90.31328125000007, 47.67617187499999],\n                            [90.33066406250006, 47.655175781249966],\n                            [90.42519531250005, 47.50410156250001],\n                            [90.49619140625012, 47.28515625],\n                            [90.64335937500007, 47.10029296874998],\n                            [90.71552734375004, 47.00385742187498],\n                            [90.7990234375001, 46.98515624999999],\n                            [90.86992187500002, 46.95449218750005],\n                            [90.91054687500005, 46.88325195312501],\n                            [90.9857421875, 46.7490234375],\n                            [90.9115234375, 46.270654296874994],\n                            [90.94755859375002, 46.17729492187499],\n                            [90.99677734375004, 46.10498046875],\n                            [91.00175781250007, 46.03579101562502],\n                            [90.6618164062501, 45.525244140625006],\n                            [90.87724609375002, 45.19609375000002],\n                            [91.05, 45.217431640624994],\n                            [91.584375, 45.07651367187498],\n                            [92.42382812499997, 45.008935546874994],\n                            [92.57890625000002, 45.01098632812506],\n                            [92.78789062500007, 45.035742187500034],\n                            [93.51621093750012, 44.944482421874994],\n                            [94.71201171875012, 44.35083007812503],\n                            [95.35029296875004, 44.27807617187503],\n                            [95.32558593750005, 44.03935546874999],\n                            [95.52558593750004, 43.953955078125006],\n                            [95.85957031250004, 43.27597656249998],\n                            [96.38544921875004, 42.72036132812502],\n                            [97.20566406250012, 42.78979492187506],\n                            [99.46787109375012, 42.568212890625034],\n                            [99.98378906250005, 42.67734375000006],\n                            [100.08632812500005, 42.67075195312506],\n                            [100.51904296875003, 42.61679687499998],\n                            [101.09199218750004, 42.55131835937496],\n                            [101.49531250000004, 42.53876953124998],\n                            [101.57910156249997, 42.52353515624998],\n                            [101.65996093750002, 42.50004882812499],\n                            [101.97294921875002, 42.21586914062502],\n                            [102.15664062500005, 42.158105468749966],\n                            [102.57519531249997, 42.09208984375002],\n                            [103.07285156250006, 42.00595703125006],\n                            [103.7111328125001, 41.75131835937506],\n                            [103.99726562500004, 41.796972656250034],\n                            [104.30517578124997, 41.84614257812501],\n                            [104.49824218750004, 41.87700195312499],\n                            [104.49824218750004, 41.65869140625],\n                            [104.86035156250003, 41.64375],\n                            [104.98203125000012, 41.59550781250002],\n                            [105.05058593750002, 41.61591796875001],\n                            [105.1154296875001, 41.66328124999998],\n                            [105.19707031250002, 41.738037109375],\n                            [105.31435546875005, 41.77089843750005],\n                            [105.86757812500005, 41.993994140625034],\n                            [106.77001953125003, 42.28872070312502],\n                            [108.17119140625002, 42.44731445312502],\n                            [108.68730468750002, 42.416113281250034],\n                            [109.33984374999997, 42.43837890625005],\n                            [109.44316406250002, 42.455957031249994],\n                            [110.40039062499997, 42.77368164062497],\n                            [111.00722656250005, 43.34140624999998],\n                            [111.878125, 43.68017578125],\n                            [111.93173828125012, 43.81494140625],\n                            [111.40224609375005, 44.367285156250006],\n                            [111.89804687500006, 45.064062500000034],\n                            [112.03261718750005, 45.08164062500006],\n                            [112.11289062500006, 45.06293945312498],\n                            [112.41132812500004, 45.05820312499998],\n                            [112.49931640625002, 45.01093750000004],\n                            [112.59677734375006, 44.917675781249955],\n                            [112.7067382812501, 44.883447265624994],\n                            [113.04941406250006, 44.81035156250002],\n                            [113.3009765625001, 44.79165039062502],\n                            [113.50791015625006, 44.76235351562502],\n                            [113.58701171875006, 44.745703125],\n                            [113.65263671875002, 44.76347656249999],\n                            [113.87705078125012, 44.89619140625001],\n                            [114.03027343749997, 44.942578124999955],\n                            [114.08027343750004, 44.97114257812501],\n                            [114.41914062500004, 45.20258789062501],\n                            [114.56015625000012, 45.38999023437498],\n                            [114.73876953124997, 45.41962890624998],\n                            [114.91923828125007, 45.378271484375006],\n                            [115.16259765624997, 45.390234375000034],\n                            [115.6810546875, 45.45825195312503],\n                            [116.19765625, 45.739355468750006],\n                            [116.240625, 45.795996093750006],\n                            [116.22910156250012, 45.84575195312502],\n                            [116.21298828125012, 45.88691406249998],\n                            [116.56259765625012, 46.28979492187497],\n                            [116.85908203125004, 46.387939453125],\n                            [117.3333984375, 46.36201171875004],\n                            [117.35693359375003, 46.391308593749955],\n                            [117.35634765625, 46.436669921874966],\n                            [117.39218750000012, 46.53759765625003],\n                            [117.40556640625007, 46.57089843750006],\n                            [117.43808593750012, 46.58623046874999],\n                            [117.546875, 46.58828125000005],\n                            [117.74121093749997, 46.51816406250006],\n                            [118.07128906249997, 46.666601562500006],\n                            [118.15683593750006, 46.678564453125034],\n                            [118.30869140625012, 46.71704101562497],\n                            [118.40439453125006, 46.70317382812499],\n                            [118.58046875, 46.69189453125],\n                            [118.64873046875002, 46.70166015625006],\n                            [118.72294921875007, 46.69189453125],\n                            [118.8439453125001, 46.76020507812498],\n                            [118.95712890625006, 46.73486328124997],\n                            [119.16210937499997, 46.638671875],\n                            [119.33183593750002, 46.61381835937499],\n                            [119.47402343750005, 46.626660156249955],\n                            [119.62021484375006, 46.60395507812504],\n                            [119.70664062500006, 46.60600585937502],\n                            [119.74746093750005, 46.62719726562497],\n                            [119.86718750000003, 46.67216796874999],\n                            [119.89785156250005, 46.857812499999966],\n                            [119.71113281250004, 47.15],\n                            [119.08193359375, 47.654150390625034],\n                            [119.01757812500003, 47.68535156249999],\n                            [118.88027343750005, 47.72509765625],\n                            [118.75996093750004, 47.75761718749996],\n                            [118.69052734375012, 47.822265625],\n                            [118.56777343750005, 47.94326171875005],\n                            [118.49843750000005, 47.98398437499998],\n                            [117.76835937500002, 47.98789062499998],\n                            [117.3507812500001, 47.65219726562498],\n                            [117.28593750000002, 47.666357421875034],\n                            [117.06972656250005, 47.80639648437506],\n                            [116.95166015624997, 47.836572265624966],\n                            [116.90117187500007, 47.85307617187496],\n                            [116.76054687500002, 47.869775390624994],\n                            [116.65195312500012, 47.86450195312497],\n                            [116.51347656250007, 47.839550781249955],\n                            [116.37822265625002, 47.84404296874999],\n                            [116.31718750000002, 47.85986328125],\n                            [116.2311523437501, 47.85820312500002],\n                            [116.07480468750012, 47.78955078125],\n                            [115.99384765625004, 47.71132812500005],\n                            [115.89824218750002, 47.68691406250005],\n                            [115.6164062500001, 47.874804687500045],\n                            [115.52509765625004, 48.13085937499997],\n                            [115.63945312500007, 48.18623046874998],\n                            [115.785546875, 48.24824218750001],\n                            [115.7965820312501, 48.346337890624994],\n                            [115.7916992187501, 48.455712890624994],\n                            [115.8205078125001, 48.57724609375006],\n                            [116.6833007812501, 49.82377929687499],\n                            [117.8734375, 49.51347656250002],\n                            [118.4515625, 49.84448242187503],\n                            [119.25986328125012, 50.06640625000003],\n                            [119.34628906250012, 50.278955078124994],\n                            [119.16367187500006, 50.40600585937503],\n                            [120.06689453125003, 51.60068359375006],\n                            [120.74980468750007, 52.096533203125006],\n                            [120.65615234375, 52.56665039062503],\n                            [120.0675781250001, 52.632910156250034],\n                            [120.09453125000007, 52.787207031250034],\n                            [120.98544921875012, 53.28457031250002],\n                            [123.6078125, 53.546533203124994],\n                            [124.81230468750002, 53.133837890625045],\n                            [125.075, 53.20366210937496],\n                            [125.64902343750012, 53.042285156250045],\n                            [126.34169921875, 52.36201171875001],\n                            [126.92480468749997, 51.10014648437496],\n                            [127.30703125000005, 50.70795898437501],\n                            [127.33720703125007, 50.35014648437502],\n                            [127.590234375, 50.20898437500003],\n                            [127.55078124999997, 49.801806640625045],\n                            [127.99960937500006, 49.56860351562506],\n                            [128.70400390625, 49.60014648437499],\n                            [129.0651367187501, 49.374658203124966],\n                            [129.49814453125012, 49.38881835937502],\n                            [130.1959960937501, 48.89165039062499],\n                            [130.553125, 48.861181640625006],\n                            [130.5521484375, 48.602490234374955],\n                            [130.80429687500012, 48.34150390624998],\n                            [130.7326171875001, 48.01923828124998],\n                            [130.96191406249997, 47.70932617187498],\n                            [132.47626953125004, 47.714990234374994],\n                            [132.7072265625001, 47.94726562500006],\n                            [133.14404296875003, 48.10566406249998],\n                            [133.46835937500006, 48.09716796875003],\n                            [134.29335937500005, 48.37343750000002],\n                            [134.66523437500004, 48.25390625],\n                            [134.56601562500006, 48.02250976562502],\n                            [134.75234375, 47.71542968749998],\n                            [134.1676757812501, 47.30219726562501],\n                            [133.86132812500003, 46.24775390625004],\n                            [133.43642578125, 45.60468750000004],\n                            [133.18603515625003, 45.49482421875004],\n                            [133.1134765625001, 45.130712890625006],\n                            [132.93603515624997, 45.029931640624994],\n                            [131.85185546875002, 45.32685546875001],\n                            [131.44687500000012, 44.984033203124966],\n                            [130.9816406250001, 44.844335937500034],\n                            [131.2552734375, 44.07158203124999],\n                            [131.25732421875003, 43.378076171874994],\n                            [131.06855468750004, 42.90224609375005],\n                            [130.42480468749997, 42.72705078124997],\n                            [130.52695312500012, 42.535400390625]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.9977539062501, 22.210498046875045],\n                            [113.83886718749997, 22.24169921875003],\n                            [114.04394531250003, 22.33339843750005],\n                            [113.9977539062501, 22.210498046875045]\n                        ]\n                    ],\n                    [\n                        [\n                            [114.01542968750007, 22.51191406250001],\n                            [114.26601562500005, 22.540966796874983],\n                            [114.26796875, 22.295556640624966],\n                            [113.93730468750002, 22.364990234375],\n                            [114.01542968750007, 22.51191406250001]\n                        ]\n                    ],\n                    [],\n                    [\n                        [\n                            [118.4074218750001, 24.522119140624994],\n                            [118.43271484375006, 24.414355468750074],\n                            [118.29511718750004, 24.436328125000017],\n                            [118.4074218750001, 24.522119140624994]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.00878906249997, 22.62036132812497],\n                            [120.83984375000003, 21.925],\n                            [120.2328125, 22.71791992187505],\n                            [120.0724609375001, 23.149755859375006],\n                            [120.13212890625007, 23.652929687500034],\n                            [121.040625, 25.032812500000034],\n                            [121.59365234375, 25.275341796874983],\n                            [121.92900390625002, 24.973730468749977],\n                            [121.39746093750003, 23.172509765625023],\n                            [121.00878906249997, 22.62036132812497]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"China\", \"childNum\": 15 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-5.262304687499977, 10.319677734374991],\n                        [-4.72177734374992, 9.756542968750026],\n                        [-4.625830078125006, 9.713574218749969],\n                        [-4.526611328124943, 9.723486328125034],\n                        [-4.406201171874926, 9.647998046875031],\n                        [-4.332226562499955, 9.645703125],\n                        [-4.18115234375, 9.78173828125],\n                        [-3.790625, 9.917187499999983],\n                        [-3.581152343749977, 9.924316406250014],\n                        [-3.289697265625023, 9.882226562500051],\n                        [-3.223535156249937, 9.895458984374997],\n                        [-3.160693359374932, 9.849169921874974],\n                        [-3.095800781249949, 9.752099609375009],\n                        [-3.042626953124937, 9.72089843750004],\n                        [-2.988281249999972, 9.687353515624963],\n                        [-2.900878906249943, 9.534619140625026],\n                        [-2.875146484374937, 9.500927734374997],\n                        [-2.816748046874949, 9.425830078124974],\n                        [-2.766601562499943, 9.424707031250009],\n                        [-2.7171875, 9.457128906250048],\n                        [-2.695849609374989, 9.481347656250009],\n                        [-2.686132812499977, 9.43173828125002],\n                        [-2.705761718749983, 9.351367187499989],\n                        [-2.74692382812492, 9.04511718750004],\n                        [-2.689892578124955, 9.02509765625004],\n                        [-2.649218750000017, 8.956591796875031],\n                        [-2.600390625000017, 8.800439453125023],\n                        [-2.505859375000028, 8.208740234375],\n                        [-2.538281249999955, 8.171630859374986],\n                        [-2.61171875, 8.147558593749963],\n                        [-2.619970703125006, 8.12109375],\n                        [-2.600976562499937, 8.082226562499983],\n                        [-2.613378906249977, 8.046679687500017],\n                        [-2.668847656249994, 8.022216796875014],\n                        [-2.789746093749955, 7.931933593750003],\n                        [-2.959082031249977, 7.454541015624997],\n                        [-3.227148437499977, 6.749121093749991],\n                        [-2.998291015624972, 5.711328125000051],\n                        [-2.793652343749955, 5.600097656250028],\n                        [-2.754980468749977, 5.432519531249994],\n                        [-2.815673828125, 5.153027343749997],\n                        [-3.168701171874972, 5.203027343749966],\n                        [-3.199951171874943, 5.3544921875],\n                        [-3.347558593749994, 5.13066406249996],\n                        [-4.120166015625017, 5.309716796875023],\n                        [-4.60888671875, 5.235888671875003],\n                        [-4.037207031249977, 5.23012695312498],\n                        [-4.899707031249932, 5.138330078125023],\n                        [-5.282373046874994, 5.210253906250017],\n                        [-5.36752929687492, 5.15078125],\n                        [-5.061816406249989, 5.13066406249996],\n                        [-5.913769531249926, 5.0109375],\n                        [-7.544970703124989, 4.351318359375],\n                        [-7.574658203124983, 4.572314453124989],\n                        [-7.585058593749977, 4.916748046875],\n                        [-7.39990234375, 5.550585937499989],\n                        [-7.454394531249989, 5.841308593749972],\n                        [-7.636132812499994, 5.90771484375],\n                        [-7.730371093749994, 5.919042968749991],\n                        [-7.800927734374994, 6.038916015624991],\n                        [-7.833251953125, 6.076367187499983],\n                        [-7.855517578125017, 6.150146484375],\n                        [-7.888623046875011, 6.234863281250028],\n                        [-7.981591796874937, 6.2861328125],\n                        [-8.287109375, 6.31904296875004],\n                        [-8.587890625, 6.490527343749989],\n                        [-8.324511718749989, 6.920019531249991],\n                        [-8.408740234374989, 7.411816406249997],\n                        [-8.429980468749989, 7.601855468749989],\n                        [-8.351757812499926, 7.590576171875],\n                        [-8.231884765624955, 7.556738281250034],\n                        [-8.205957031249994, 7.590234375000023],\n                        [-8.115429687499926, 7.760742187500028],\n                        [-8.126855468749937, 7.867724609374974],\n                        [-8.00986328124992, 8.078515625000023],\n                        [-8.048583984375, 8.169726562500045],\n                        [-8.140625, 8.181445312500031],\n                        [-8.217138671874949, 8.219677734375011],\n                        [-8.256103515625, 8.253710937500017],\n                        [-8.244140624999943, 8.407910156249983],\n                        [-8.236962890624994, 8.455664062500034],\n                        [-7.953125, 8.477734375],\n                        [-7.823583984374977, 8.467675781249994],\n                        [-7.738964843749983, 8.375244140624986],\n                        [-7.696093749999932, 8.375585937499977],\n                        [-7.71958007812492, 8.643017578125011],\n                        [-7.950976562499989, 8.786816406249997],\n                        [-7.938183593749983, 8.97978515624996],\n                        [-7.902099609375, 9.017089843750014],\n                        [-7.777978515624937, 9.080859375000031],\n                        [-7.799804687499943, 9.115039062499989],\n                        [-7.839404296875017, 9.151611328124972],\n                        [-7.918066406249949, 9.188525390625031],\n                        [-7.896191406249955, 9.415869140624991],\n                        [-8.136962890624972, 9.49570312499999],\n                        [-8.155175781249937, 9.973193359375017],\n                        [-7.990625, 10.1625],\n                        [-7.661132812500028, 10.427441406250011],\n                        [-7.385058593749989, 10.340136718749989],\n                        [-7.01708984375, 10.143261718750026],\n                        [-6.950341796874994, 10.342333984374989],\n                        [-6.693261718750023, 10.34946289062502],\n                        [-6.669335937499937, 10.39218750000002],\n                        [-6.69199218749992, 10.512011718750017],\n                        [-6.686132812499977, 10.578027343750051],\n                        [-6.676367187499949, 10.633789062500043],\n                        [-6.654150390624949, 10.65644531250004],\n                        [-6.482617187499983, 10.561230468749997],\n                        [-6.250244140625, 10.717919921875037],\n                        [-6.190673828124943, 10.400292968749994],\n                        [-6.192626953124972, 10.369433593750003],\n                        [-6.241308593749949, 10.279199218750009],\n                        [-6.238378906249977, 10.26162109374998],\n                        [-6.117187499999972, 10.201904296874986],\n                        [-6.034570312499937, 10.194824218750057],\n                        [-5.907568359375006, 10.307226562500034],\n                        [-5.896191406249983, 10.354736328125028],\n                        [-5.843847656249977, 10.389550781250023],\n                        [-5.694287109374983, 10.433203125000034],\n                        [-5.556591796874983, 10.439941406249986],\n                        [-5.382275390625011, 10.314013671875003],\n                        [-5.262304687499977, 10.319677734374991]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Côte d'Ivoire\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [15.480078125, 7.523779296874991],\n                        [15.206738281250011, 7.206152343749991],\n                        [14.7392578125, 6.27978515625],\n                        [14.43115234375, 6.038720703124994],\n                        [14.616894531250011, 5.865136718749994],\n                        [14.56298828125, 5.279931640624994],\n                        [14.73125, 4.602392578124991],\n                        [15.063574218750006, 4.284863281249997],\n                        [15.128710937500017, 3.826904296875],\n                        [16.0634765625, 2.90859375],\n                        [16.183398437500017, 2.270068359374989],\n                        [16.059375, 1.676220703124997],\n                        [15.741601562500023, 1.914990234374997],\n                        [14.902441406250006, 2.012304687499991],\n                        [14.578906250000017, 2.199121093749994],\n                        [13.293554687500006, 2.161572265624997],\n                        [13.2203125, 2.256445312499991],\n                        [11.558984375000023, 2.302197265624997],\n                        [11.348437500000017, 2.299707031249994],\n                        [11.328710937500006, 2.167431640624997],\n                        [11.096582031250023, 2.16748046875],\n                        [10.790917968750023, 2.16757812499999],\n                        [9.979882812500023, 2.167773437499989],\n                        [9.8701171875, 2.21328125],\n                        [9.8369140625, 2.242382812499997],\n                        [9.830371093750017, 2.275488281249991],\n                        [9.826171875, 2.297802734374997],\n                        [9.80078125, 2.304443359375],\n                        [9.82177734375, 2.539257812499997],\n                        [9.948437500000011, 3.079052734374997],\n                        [9.672070312500011, 3.53759765625],\n                        [9.765722656250006, 3.623828124999989],\n                        [9.642382812500017, 3.611767578124997],\n                        [9.55615234375, 3.798046875],\n                        [9.739648437500023, 3.852929687499994],\n                        [9.639941406250017, 3.96533203125],\n                        [9.688867187500023, 4.056396484375],\n                        [9.483691406250017, 4.066113281249997],\n                        [9.42529296875, 3.922314453124997],\n                        [9.000097656250006, 4.091601562499989],\n                        [8.918261718750017, 4.553759765624989],\n                        [8.660351562500011, 4.670996093749991],\n                        [8.65625, 4.516357421875],\n                        [8.53955078125, 4.571875],\n                        [8.715625, 5.046875],\n                        [8.997167968750006, 5.917724609375],\n                        [9.490234375, 6.418652343749997],\n                        [9.779882812500006, 6.76015625],\n                        [9.820703125000023, 6.783935546875],\n                        [9.874218750000011, 6.803271484374989],\n                        [10.038867187500017, 6.92138671875],\n                        [10.1435546875, 6.996435546874991],\n                        [10.167773437500017, 6.959179687499997],\n                        [10.185546875, 6.912792968749997],\n                        [10.205468750000023, 6.8916015625],\n                        [10.293066406250006, 6.876757812499989],\n                        [10.413183593750006, 6.877734374999989],\n                        [10.60625, 7.063085937499991],\n                        [10.954199218750006, 6.7765625],\n                        [11.032519531250017, 6.697900390624994],\n                        [11.1064453125, 6.457714843749997],\n                        [11.1533203125, 6.437939453124997],\n                        [11.2373046875, 6.450537109374991],\n                        [11.401757812500023, 6.533935546875],\n                        [11.551660156250023, 6.697265625],\n                        [11.580078125, 6.888867187499997],\n                        [11.657519531250017, 6.9515625],\n                        [11.861425781250006, 7.11640625],\n                        [11.767382812500017, 7.272265624999989],\n                        [11.809179687500006, 7.345068359374991],\n                        [12.016015625000023, 7.589746093749994],\n                        [12.2333984375, 8.282324218749991],\n                        [12.403515625000011, 8.595556640624991],\n                        [12.582714843750011, 8.624121093749991],\n                        [12.651562500000011, 8.667773437499989],\n                        [12.7822265625, 8.81787109375],\n                        [12.806542968750023, 8.886621093749994],\n                        [12.875683593750011, 9.303515624999989],\n                        [12.929492187500017, 9.42626953125],\n                        [13.19873046875, 9.563769531249989],\n                        [13.269921875000023, 10.036181640624989],\n                        [13.41455078125, 10.171435546874989],\n                        [13.535351562500011, 10.60507812499999],\n                        [13.699902343750011, 10.873144531249991],\n                        [13.89208984375, 11.140087890624997],\n                        [13.9814453125, 11.211865234374997],\n                        [14.056738281250006, 11.245019531249994],\n                        [14.143261718750011, 11.24853515625],\n                        [14.202343750000011, 11.268164062499991],\n                        [14.559765625000011, 11.492285156249991],\n                        [14.619726562500006, 12.150976562499991],\n                        [14.518945312500023, 12.298242187499994],\n                        [14.272851562500023, 12.356494140624989],\n                        [14.184863281250017, 12.447216796874997],\n                        [14.06396484375, 13.07851562499999],\n                        [14.244824218750011, 13.07734375],\n                        [14.461718750000017, 13.021777343749989],\n                        [14.847070312500023, 12.502099609374994],\n                        [15.08125, 11.845507812499989],\n                        [15.029882812500006, 11.11367187499999],\n                        [15.132226562500023, 10.648486328124989],\n                        [15.276074218750011, 10.357373046874997],\n                        [15.654882812500006, 10.0078125],\n                        [14.243261718750006, 9.979736328125],\n                        [13.977246093750011, 9.691552734374994],\n                        [14.332324218750017, 9.20351562499999],\n                        [15.1162109375, 8.557324218749997],\n                        [15.5498046875, 7.787890624999989],\n                        [15.480078125, 7.523779296874991]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Cameroon\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [27.4033203125, 5.109179687499989],\n                        [27.7880859375, 4.644677734374994],\n                        [28.19208984375001, 4.350244140624994],\n                        [28.427539062500017, 4.324169921874997],\n                        [28.72705078125, 4.504980468749991],\n                        [29.224902343750017, 4.391894531249989],\n                        [29.469628906250023, 4.61181640625],\n                        [29.676855468750006, 4.5869140625],\n                        [30.194921875, 3.98193359375],\n                        [30.50830078125, 3.835693359375],\n                        [30.586718750000017, 3.62421875],\n                        [30.757226562500023, 3.62421875],\n                        [30.83857421875001, 3.49072265625],\n                        [30.90644531250001, 3.408935546875],\n                        [30.754003906250006, 3.041796874999989],\n                        [30.8466796875, 2.847021484374991],\n                        [30.728613281250006, 2.455371093749989],\n                        [31.176367187500006, 2.270068359374989],\n                        [31.252734375000017, 2.044580078124994],\n                        [29.94287109375, 0.819238281249994],\n                        [29.934472656250023, 0.4990234375],\n                        [29.717675781250023, 0.098339843749997],\n                        [29.576953125000017, -1.387890625000011],\n                        [29.196582031250017, -1.719921875000011],\n                        [29.13154296875001, -2.195117187500003],\n                        [28.876367187500023, -2.400292968750009],\n                        [28.893945312500023, -2.635058593750003],\n                        [29.01435546875001, -2.72021484375],\n                        [29.224414062500017, -3.053515625],\n                        [29.211816406250023, -3.833789062500003],\n                        [29.403222656250023, -4.449316406250006],\n                        [29.404199218750023, -4.496679687500006],\n                        [29.32568359375, -4.835644531250011],\n                        [29.32343750000001, -4.898828125],\n                        [29.3427734375, -4.983105468750011],\n                        [29.542382812500023, -5.499804687500003],\n                        [29.594140625000023, -5.65078125],\n                        [29.60703125, -5.72265625],\n                        [29.59638671875001, -5.775976562500006],\n                        [29.490820312500006, -5.965429687500006],\n                        [29.480078125, -6.025],\n                        [29.50625, -6.172070312500011],\n                        [29.540820312500017, -6.313867187500009],\n                        [29.590625, -6.394433593750009],\n                        [29.70966796875001, -6.616894531250011],\n                        [29.798144531250017, -6.69189453125],\n                        [29.961816406250023, -6.803125],\n                        [30.10625, -6.9150390625],\n                        [30.212695312500017, -7.037890625],\n                        [30.31318359375001, -7.203710937500006],\n                        [30.40673828125, -7.460644531250011],\n                        [30.75117187500001, -8.193652343750003],\n                        [28.89814453125001, -8.485449218750006],\n                        [28.869531250000023, -8.785839843750011],\n                        [28.400683593750017, -9.224804687500011],\n                        [28.60419921875001, -9.678808593750006],\n                        [28.6455078125, -10.550195312500009],\n                        [28.383398437500006, -11.566699218750003],\n                        [28.482519531250006, -11.812109375],\n                        [29.064355468750023, -12.348828125000011],\n                        [29.48554687500001, -12.41845703125],\n                        [29.508203125000023, -12.228222656250011],\n                        [29.79511718750001, -12.155468750000011],\n                        [29.775195312500017, -13.438085937500006],\n                        [29.55419921875, -13.248925781250009],\n                        [29.20185546875001, -13.398339843750009],\n                        [29.014257812500006, -13.368847656250011],\n                        [28.730078125, -12.925488281250011],\n                        [28.550878906250006, -12.836132812500011],\n                        [28.412890625000017, -12.51806640625],\n                        [27.573828125, -12.22705078125],\n                        [27.1591796875, -11.579199218750006],\n                        [26.824023437500017, -11.965234375],\n                        [26.025976562500006, -11.89013671875],\n                        [25.349414062500017, -11.623046875],\n                        [25.28876953125001, -11.21240234375],\n                        [24.3779296875, -11.417089843750006],\n                        [24.36572265625, -11.1298828125],\n                        [23.96650390625001, -10.871777343750011],\n                        [23.901171875000017, -10.983203125],\n                        [23.833886718750023, -11.013671875],\n                        [23.463964843750006, -10.969335937500006],\n                        [23.076269531250006, -11.087890625],\n                        [22.814746093750017, -11.080273437500011],\n                        [22.56103515625, -11.055859375000011],\n                        [22.486132812500017, -11.08671875],\n                        [22.392968750000023, -11.159472656250003],\n                        [22.31494140625, -11.198632812500009],\n                        [22.27880859375, -11.194140625],\n                        [22.226171875, -11.121972656250009],\n                        [22.203515625000023, -10.829492187500009],\n                        [22.307031250000023, -10.691308593750009],\n                        [22.19775390625, -10.040625],\n                        [21.81318359375001, -9.46875],\n                        [21.905371093750006, -8.693359375],\n                        [21.806054687500023, -7.32861328125],\n                        [21.751074218750006, -7.30546875],\n                        [21.190332031250023, -7.284960937500003],\n                        [20.910937500000017, -7.281445312500011],\n                        [20.607812500000023, -7.277734375],\n                        [20.558398437500017, -7.244433593750003],\n                        [20.53583984375001, -7.182812500000011],\n                        [20.536914062500017, -7.121777343750011],\n                        [20.598730468750006, -6.93515625],\n                        [20.59003906250001, -6.919921875],\n                        [20.482226562500017, -6.915820312500003],\n                        [20.190039062500006, -6.9462890625],\n                        [19.997460937500023, -6.976464843750009],\n                        [19.87519531250001, -6.986328125],\n                        [19.527636718750017, -7.144433593750009],\n                        [19.483789062500023, -7.279492187500011],\n                        [19.479882812500023, -7.47216796875],\n                        [19.371679687500006, -7.655078125],\n                        [19.369921875000017, -7.70654296875],\n                        [19.3408203125, -7.966601562500003],\n                        [19.142675781250006, -8.00146484375],\n                        [18.944433593750006, -8.00146484375],\n                        [18.56269531250001, -7.9359375],\n                        [18.0087890625, -8.107617187500011],\n                        [17.643359375000017, -8.090722656250009],\n                        [17.57958984375, -8.099023437500009],\n                        [16.984765625000023, -7.257421875],\n                        [16.91943359375, -6.933984375],\n                        [16.813085937500006, -6.772558593750006],\n                        [16.742968750000017, -6.618457031250003],\n                        [16.697265625, -6.164257812500011],\n                        [16.537109375, -5.9658203125],\n                        [16.431445312500017, -5.900195312500003],\n                        [16.315234375000017, -5.865625],\n                        [13.978515625, -5.857226562500003],\n                        [13.346484375000017, -5.863378906250006],\n                        [13.184375, -5.85625],\n                        [12.452929687500017, -6.00048828125],\n                        [12.213671875000017, -5.758691406250009],\n                        [12.484570312500011, -5.71875],\n                        [12.451464843750017, -5.071484375000011],\n                        [12.502734375000017, -5.036914062500003],\n                        [12.573535156250017, -4.99658203125],\n                        [12.59619140625, -4.978417968750009],\n                        [12.8296875, -4.736621093750003],\n                        [12.947460937500011, -4.6953125],\n                        [13.057324218750011, -4.651074218750011],\n                        [13.07275390625, -4.634765625],\n                        [13.08740234375, -4.601953125],\n                        [13.136621093750023, -4.604296875],\n                        [13.414941406250023, -4.83740234375],\n                        [13.659570312500023, -4.721484375],\n                        [13.717089843750017, -4.454492187500009],\n                        [13.94091796875, -4.484667968750003],\n                        [14.358300781250023, -4.299414062500006],\n                        [14.449804687500006, -4.449511718750003],\n                        [14.365429687500011, -4.585546875],\n                        [14.410742187500006, -4.83125],\n                        [14.707910156250023, -4.881738281250009],\n                        [15.990039062500017, -3.766210937500006],\n                        [16.217382812500006, -3.0302734375],\n                        [16.21533203125, -2.177832031250006],\n                        [16.54072265625001, -1.840136718750003],\n                        [16.8798828125, -1.225878906250003],\n                        [17.752832031250023, -0.549023437500011],\n                        [18.072167968750023, 2.01328125],\n                        [18.49091796875001, 2.924414062499991],\n                        [18.6103515625, 3.478417968749994],\n                        [18.594140625000023, 4.346240234374989],\n                        [19.06855468750001, 4.891406249999989],\n                        [19.5009765625, 5.127490234374989],\n                        [19.806542968750023, 5.089306640624997],\n                        [20.226367187500017, 4.829638671874989],\n                        [20.55810546875, 4.462695312499989],\n                        [22.422167968750017, 4.134960937499997],\n                        [22.864550781250017, 4.723876953125],\n                        [23.41718750000001, 4.663134765624989],\n                        [24.31982421875, 4.994140625],\n                        [25.065234375000017, 4.967431640624994],\n                        [25.52509765625001, 5.31210937499999],\n                        [26.822070312500017, 5.062402343749994],\n                        [27.071875, 5.199755859374989],\n                        [27.4033203125, 5.109179687499989]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Dem. Rep. Congo\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [18.6103515625, 3.478417968749994],\n                        [18.49091796875001, 2.924414062499991],\n                        [18.072167968750023, 2.01328125],\n                        [17.752832031250023, -0.549023437500011],\n                        [16.8798828125, -1.225878906250003],\n                        [16.54072265625001, -1.840136718750003],\n                        [16.21533203125, -2.177832031250006],\n                        [16.217382812500006, -3.0302734375],\n                        [15.990039062500017, -3.766210937500006],\n                        [14.707910156250023, -4.881738281250009],\n                        [14.410742187500006, -4.83125],\n                        [14.365429687500011, -4.585546875],\n                        [14.449804687500006, -4.449511718750003],\n                        [14.358300781250023, -4.299414062500006],\n                        [13.94091796875, -4.484667968750003],\n                        [13.717089843750017, -4.454492187500009],\n                        [13.659570312500023, -4.721484375],\n                        [13.414941406250023, -4.83740234375],\n                        [13.136621093750023, -4.604296875],\n                        [13.08740234375, -4.601953125],\n                        [13.07275390625, -4.634765625],\n                        [13.048046875000011, -4.619238281250006],\n                        [12.971386718750011, -4.5517578125],\n                        [12.881054687500011, -4.445117187500003],\n                        [12.84814453125, -4.428906250000011],\n                        [12.50146484375, -4.5875],\n                        [12.018359375000017, -5.004296875],\n                        [11.777539062500011, -4.565820312500009],\n                        [11.130175781250017, -3.916308593750003],\n                        [11.234472656250006, -3.690820312500009],\n                        [11.504296875000023, -3.5203125],\n                        [11.685742187500011, -3.68203125],\n                        [11.8798828125, -3.665917968750009],\n                        [11.934179687500006, -3.318554687500011],\n                        [11.715429687500006, -3.176953125000011],\n                        [11.760156250000023, -2.983105468750011],\n                        [11.537792968750011, -2.83671875],\n                        [11.60546875, -2.342578125],\n                        [12.064453125, -2.41259765625],\n                        [12.446386718750006, -2.329980468750009],\n                        [12.43212890625, -1.928906250000011],\n                        [12.590429687500006, -1.826855468750011],\n                        [12.793554687500006, -1.931835937500011],\n                        [12.991992187500017, -2.313378906250009],\n                        [13.464941406250006, -2.395410156250009],\n                        [13.733789062500023, -2.138476562500003],\n                        [13.886914062500011, -2.465429687500006],\n                        [13.993847656250011, -2.490625],\n                        [14.199804687500006, -2.354199218750011],\n                        [14.162890625000017, -2.217578125],\n                        [14.383984375000011, -1.890039062500009],\n                        [14.47412109375, -0.573437500000011],\n                        [13.860058593750011, -0.203320312500011],\n                        [13.949609375000023, 0.353808593749989],\n                        [14.32421875, 0.62421875],\n                        [14.429882812500011, 0.901464843749991],\n                        [14.180859375000011, 1.370214843749991],\n                        [13.851367187500017, 1.41875],\n                        [13.21630859375, 1.2484375],\n                        [13.172167968750017, 1.78857421875],\n                        [13.293554687500006, 2.161572265624997],\n                        [14.578906250000017, 2.199121093749994],\n                        [14.902441406250006, 2.012304687499991],\n                        [15.741601562500023, 1.914990234374997],\n                        [16.059375, 1.676220703124997],\n                        [16.183398437500017, 2.270068359374989],\n                        [16.468554687500017, 2.831738281249997],\n                        [16.610742187500023, 3.50537109375],\n                        [17.491601562500023, 3.687304687499989],\n                        [18.160937500000017, 3.499804687499989],\n                        [18.474414062500017, 3.622998046874997],\n                        [18.6103515625, 3.478417968749994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Congo\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-71.31972656249997, 11.861914062500048],\n                        [-71.95810546875, 11.66640625],\n                        [-72.24848632812501, 11.196435546875009],\n                        [-72.690087890625, 10.835839843749994],\n                        [-72.86933593750001, 10.49125976562496],\n                        [-73.00654296874998, 9.789160156250006],\n                        [-73.36621093749997, 9.194140625000017],\n                        [-73.05839843749999, 9.259570312500031],\n                        [-72.79638671874997, 9.10898437499999],\n                        [-72.66542968749994, 8.62758789062498],\n                        [-72.39033203124995, 8.287060546874969],\n                        [-72.47197265624996, 7.524267578124991],\n                        [-72.20771484374995, 7.37026367187498],\n                        [-72.00664062499993, 7.032617187500023],\n                        [-71.12861328124993, 6.98671875],\n                        [-70.73715820312503, 7.090039062499997],\n                        [-70.12919921874999, 6.95361328125],\n                        [-69.42714843749997, 6.123974609374997],\n                        [-68.47177734375, 6.156542968749974],\n                        [-67.85917968749999, 6.289892578124963],\n                        [-67.48198242187499, 6.18027343750002],\n                        [-67.47387695312503, 5.929980468750003],\n                        [-67.82490234374995, 5.270458984375026],\n                        [-67.85527343750002, 4.506884765624989],\n                        [-67.66162109375, 3.864257812499986],\n                        [-67.3111328125, 3.41586914062502],\n                        [-67.85908203124998, 2.793603515624994],\n                        [-67.61870117187496, 2.793603515624994],\n                        [-67.21083984375, 2.390136718750043],\n                        [-66.87602539062499, 1.223046875000037],\n                        [-67.082275390625, 1.185400390625006],\n                        [-67.11923828124998, 1.703613281249986],\n                        [-67.40043945312499, 2.116699218750028],\n                        [-67.93623046874998, 1.748486328124969],\n                        [-68.19379882812495, 1.987011718749983],\n                        [-68.25595703125, 1.845507812500017],\n                        [-68.17656249999999, 1.719824218749991],\n                        [-69.84858398437493, 1.708740234375043],\n                        [-69.85214843750003, 1.05952148437504],\n                        [-69.31181640624999, 1.050488281249969],\n                        [-69.15332031249994, 0.65878906250002],\n                        [-69.47211914062498, 0.72993164062504],\n                        [-70.05390624999993, 0.578613281250028],\n                        [-70.07050781249993, -0.13886718750004],\n                        [-69.63398437500001, -0.50927734375],\n                        [-69.40024414062498, -1.194921874999977],\n                        [-69.66904296875003, -2.667675781249997],\n                        [-69.94819335937498, -4.200585937500009],\n                        [-69.96591796875003, -4.2359375],\n                        [-70.16752929687499, -4.050195312500009],\n                        [-70.24028320312496, -3.882714843749994],\n                        [-70.2984375, -3.844238281249972],\n                        [-70.33950195312502, -3.814355468750009],\n                        [-70.73510742187497, -3.781542968749989],\n                        [-70.09584960937494, -2.658203125000014],\n                        [-70.16474609374995, -2.639843750000011],\n                        [-70.24443359375002, -2.606542968749977],\n                        [-70.29462890624995, -2.552539062499989],\n                        [-70.57587890624995, -2.418261718749989],\n                        [-70.64799804687499, -2.405761718750014],\n                        [-70.70537109374996, -2.341992187499983],\n                        [-70.91455078125003, -2.218554687499974],\n                        [-70.96855468750002, -2.206835937499989],\n                        [-71.02729492187498, -2.225781250000026],\n                        [-71.11337890625003, -2.245410156250031],\n                        [-71.19638671874998, -2.313085937499963],\n                        [-71.39697265625, -2.334082031249977],\n                        [-71.55947265624997, -2.224218749999977],\n                        [-71.75253906249995, -2.15273437499998],\n                        [-71.80273437499997, -2.166308593749989],\n                        [-71.86728515624998, -2.227734374999983],\n                        [-71.932470703125, -2.288671874999963],\n                        [-71.98427734375, -2.326562499999952],\n                        [-72.21845703125001, -2.400488281250006],\n                        [-72.94111328124998, -2.394042968750028],\n                        [-72.9896484375, -2.33974609374998],\n                        [-73.15449218749993, -2.278222656249966],\n                        [-73.19697265624995, -1.830273437500011],\n                        [-73.49628906249993, -1.69306640625004],\n                        [-73.66430664062497, -1.248828124999946],\n                        [-73.86318359374997, -1.19667968749998],\n                        [-73.92695312500001, -1.125195312499983],\n                        [-73.98681640625003, -1.098144531249986],\n                        [-74.05439453124995, -1.028613281250031],\n                        [-74.18076171875, -0.997753906249955],\n                        [-74.24638671874999, -0.970605468750023],\n                        [-74.28388671874998, -0.927832031250006],\n                        [-74.33442382812498, -0.85087890624996],\n                        [-74.41787109375, -0.580664062499977],\n                        [-74.46518554687498, -0.517675781250034],\n                        [-74.51386718749993, -0.470117187500023],\n                        [-74.555078125, -0.429882812499997],\n                        [-74.61635742187494, -0.370019531249966],\n                        [-74.691650390625, -0.335253906249989],\n                        [-74.75537109375003, -0.298632812499989],\n                        [-74.78046874999998, -0.24453125],\n                        [-74.80175781249997, -0.200097656249994],\n                        [-75.13837890624998, -0.050488281249969],\n                        [-75.28447265624999, -0.10654296875002],\n                        [-75.77666015624999, 0.08925781249998],\n                        [-76.27060546874998, 0.439404296874997],\n                        [-76.49462890624997, 0.23544921875002],\n                        [-77.396337890625, 0.393896484374963],\n                        [-77.46767578124997, 0.636523437500017],\n                        [-77.702880859375, 0.837841796874997],\n                        [-78.1806640625, 0.968554687499974],\n                        [-78.85966796874996, 1.455371093750031],\n                        [-79.02543945312499, 1.623681640625037],\n                        [-78.79296874999994, 1.848730468749963],\n                        [-78.576904296875, 1.773779296874977],\n                        [-78.59169921875, 2.356640624999969],\n                        [-78.41689453125, 2.483496093749963],\n                        [-78.06665039062494, 2.509130859375034],\n                        [-77.81357421875, 2.716357421874974],\n                        [-77.076806640625, 3.913281250000026],\n                        [-77.26352539062503, 3.893212890625023],\n                        [-77.27802734374995, 4.058496093750023],\n                        [-77.35820312499996, 3.944726562500037],\n                        [-77.40874023437496, 4.24775390625004],\n                        [-77.52070312499993, 4.212792968750023],\n                        [-77.35351562499997, 4.398291015624977],\n                        [-77.28632812499995, 4.72172851562496],\n                        [-77.373291015625, 5.323974609375],\n                        [-77.53442382812497, 5.537109374999986],\n                        [-77.24926757812497, 5.780175781250037],\n                        [-77.46943359374995, 6.176757812500014],\n                        [-77.368798828125, 6.575585937499994],\n                        [-77.90117187499999, 7.229345703125048],\n                        [-77.76191406249995, 7.698828125000034],\n                        [-77.53828124999995, 7.56625976562502],\n                        [-77.19599609374995, 7.972460937500003],\n                        [-77.47851562499994, 8.498437500000037],\n                        [-77.37421874999993, 8.65830078125002],\n                        [-76.85185546875002, 8.09047851562498],\n                        [-76.924658203125, 7.973193359374974],\n                        [-76.78657226562493, 7.931591796875026],\n                        [-76.7720703125, 8.310546875000043],\n                        [-76.92045898437496, 8.573730468750014],\n                        [-76.27685546875, 8.989111328124991],\n                        [-76.02724609374997, 9.365771484374989],\n                        [-75.63935546874998, 9.450439453125014],\n                        [-75.680029296875, 9.729785156249989],\n                        [-75.53857421874997, 10.205175781250034],\n                        [-75.708349609375, 10.143408203124963],\n                        [-75.44599609374995, 10.610888671874989],\n                        [-74.84458007812498, 11.109716796875006],\n                        [-74.330224609375, 10.996679687499991],\n                        [-74.51625976562497, 10.8625],\n                        [-74.40087890625, 10.76523437499999],\n                        [-74.14291992187503, 11.320849609375031],\n                        [-73.31337890624997, 11.295751953124991],\n                        [-72.275, 11.88925781250002],\n                        [-72.13574218749994, 12.188574218749977],\n                        [-71.71455078124993, 12.41997070312496],\n                        [-71.26210937499997, 12.335302734375034],\n                        [-71.13730468750003, 12.04633789062504],\n                        [-71.31972656249997, 11.861914062500048]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Colombia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [43.788671875, -12.307031250000023],\n                            [43.85898437500006, -12.368261718749977],\n                            [43.66367187500006, -12.342871093749949],\n                            [43.63134765624997, -12.247070312499972],\n                            [43.788671875, -12.307031250000023]\n                        ]\n                    ],\n                    [\n                        [\n                            [44.476367187500074, -12.08154296875],\n                            [44.504980468750006, -12.356542968749991],\n                            [44.220117187499994, -12.171386718750014],\n                            [44.476367187500074, -12.08154296875]\n                        ]\n                    ],\n                    [\n                        [\n                            [43.46582031249997, -11.901269531249966],\n                            [43.226660156250006, -11.75185546874998],\n                            [43.2990234375001, -11.374511718750028],\n                            [43.39296875000005, -11.408593749999952],\n                            [43.46582031249997, -11.901269531249966]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Comoros\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-24.308251953124966, 14.856298828124991],\n                            [-24.44052734374992, 14.834814453124963],\n                            [-24.496875, 14.980273437500017],\n                            [-24.329492187499937, 15.019482421875011],\n                            [-24.308251953124966, 14.856298828124991]\n                        ]\n                    ],\n                    [\n                        [\n                            [-23.18212890624997, 15.136767578125017],\n                            [-23.210253906250017, 15.32353515625006],\n                            [-23.119335937499955, 15.26840820312502],\n                            [-23.18212890624997, 15.136767578125017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-23.444238281249994, 15.00795898437498],\n                            [-23.5046875, 14.916113281250006],\n                            [-23.70537109374999, 14.96132812499998],\n                            [-23.74809570312499, 15.328515625],\n                            [-23.444238281249994, 15.00795898437498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-22.917724609375, 16.237255859374955],\n                            [-22.69262695312497, 16.169042968750006],\n                            [-22.710107421874994, 16.043359374999966],\n                            [-22.95927734374996, 16.045117187499983],\n                            [-22.917724609375, 16.237255859374955]\n                        ]\n                    ],\n                    [\n                        [\n                            [-24.08769531249999, 16.62250976562501],\n                            [-24.03271484374997, 16.57202148437503],\n                            [-24.243066406250023, 16.599414062500017],\n                            [-24.32236328124992, 16.49311523437504],\n                            [-24.398095703124966, 16.61840820312497],\n                            [-24.08769531249999, 16.62250976562501]\n                        ]\n                    ],\n                    [\n                        [\n                            [-22.888330078124966, 16.659082031249994],\n                            [-22.980615234374937, 16.700878906249983],\n                            [-22.93291015624999, 16.84101562500004],\n                            [-22.888330078124966, 16.659082031249994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-24.88706054687495, 16.81811523437497],\n                            [-25.09306640624999, 16.83251953125],\n                            [-24.936474609374983, 16.92211914062503],\n                            [-24.88706054687495, 16.81811523437497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-25.169824218749994, 16.94648437500001],\n                            [-25.308300781249955, 16.93583984374999],\n                            [-25.337109374999955, 17.091015624999983],\n                            [-25.03466796875, 17.176464843749983],\n                            [-24.979687499999983, 17.09472656250003],\n                            [-25.169824218749994, 16.94648437500001]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Cape Verde\", \"childNum\": 8 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-83.6419921875, 10.917236328125],\n                        [-83.346826171875, 10.315380859374997],\n                        [-82.77841796874999, 9.66953125],\n                        [-82.56357421874999, 9.57666015625],\n                        [-82.56923828125, 9.55820312499999],\n                        [-82.58652343749999, 9.538818359375],\n                        [-82.64409179687499, 9.505859375],\n                        [-82.801025390625, 9.591796875],\n                        [-82.843994140625, 9.57080078125],\n                        [-82.86015624999999, 9.511474609375],\n                        [-82.88896484374999, 9.481005859374989],\n                        [-82.925048828125, 9.469042968749989],\n                        [-82.93984375, 9.449169921874997],\n                        [-82.94033203125, 9.060107421874989],\n                        [-82.88134765625, 9.055859375],\n                        [-82.78305664062499, 8.990283203124989],\n                        [-82.741162109375, 8.951708984374989],\n                        [-82.72783203124999, 8.916064453124989],\n                        [-82.91704101562499, 8.740332031249991],\n                        [-82.855712890625, 8.635302734374989],\n                        [-82.84477539062499, 8.489355468749991],\n                        [-82.86162109374999, 8.45351562499999],\n                        [-83.02734375, 8.337744140624991],\n                        [-82.879345703125, 8.070654296874991],\n                        [-83.12333984374999, 8.353076171874989],\n                        [-83.16240234374999, 8.588183593749989],\n                        [-83.4697265625, 8.706835937499989],\n                        [-83.29150390625, 8.406005859375],\n                        [-83.54375, 8.445849609374989],\n                        [-83.73408203125, 8.614453125],\n                        [-83.613720703125, 8.804052734374991],\n                        [-83.73691406249999, 9.150292968749994],\n                        [-84.58159179687499, 9.568359375],\n                        [-84.71494140624999, 9.8994140625],\n                        [-85.23564453124999, 10.242089843749994],\n                        [-85.2365234375, 10.107373046874997],\n                        [-84.88642578125, 9.820947265624994],\n                        [-85.07705078125, 9.60195312499999],\n                        [-85.31455078124999, 9.8109375],\n                        [-85.62485351562499, 9.902441406249991],\n                        [-85.84965820312499, 10.292041015624989],\n                        [-85.667236328125, 10.745019531249994],\n                        [-85.90800781249999, 10.897558593749991],\n                        [-85.7443359375, 11.06210937499999],\n                        [-85.5841796875, 11.189453125],\n                        [-84.9091796875, 10.9453125],\n                        [-84.6341796875, 11.045605468749997],\n                        [-83.91928710937499, 10.7353515625],\n                        [-83.71293945312499, 10.785888671875],\n                        [-83.6419921875, 10.917236328125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Costa Rica\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-82.56176757812503, 21.571679687500023],\n                            [-82.959619140625, 21.441308593750023],\n                            [-83.18378906250001, 21.59345703125004],\n                            [-82.97358398437498, 21.592285156250057],\n                            [-83.08251953124997, 21.791406250000023],\n                            [-82.99121093750003, 21.942724609375034],\n                            [-82.71455078124998, 21.890283203125023],\n                            [-82.56176757812503, 21.571679687500023]\n                        ]\n                    ],\n                    [\n                        [\n                            [-77.66899414062493, 21.951953125000045],\n                            [-77.91855468749998, 22.088085937499983],\n                            [-77.63369140624994, 22.054003906250074],\n                            [-77.66899414062493, 21.951953125000045]\n                        ]\n                    ],\n                    [\n                        [\n                            [-77.87939453125, 22.127539062500034],\n                            [-78.04165039062502, 22.201269531250034],\n                            [-77.99921874999998, 22.298730468749994],\n                            [-77.87939453125, 22.127539062500034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-81.83745117187499, 23.163037109374955],\n                            [-81.26235351562497, 23.156835937500034],\n                            [-81.14462890624998, 23.054931640625057],\n                            [-80.65014648437494, 23.10307617187499],\n                            [-80.36489257812502, 22.943408203125074],\n                            [-79.82026367187498, 22.887011718750045],\n                            [-79.27568359374999, 22.407617187499994],\n                            [-78.68647460937493, 22.366845703125023],\n                            [-77.63681640624995, 21.79736328125],\n                            [-77.49711914062502, 21.78833007812503],\n                            [-77.58315429687497, 21.889257812499977],\n                            [-77.49726562499995, 21.871630859375045],\n                            [-77.14414062499995, 21.643603515625017],\n                            [-77.36616210937498, 21.612646484375034],\n                            [-77.25288085937498, 21.483496093750006],\n                            [-77.0986328125, 21.589013671875023],\n                            [-76.86743164062497, 21.330419921875006],\n                            [-75.72294921874996, 21.111035156249983],\n                            [-75.59580078125, 20.99467773437499],\n                            [-75.72456054687493, 20.71455078125004],\n                            [-74.882568359375, 20.65063476562497],\n                            [-74.51313476562495, 20.384570312500045],\n                            [-74.16748046874997, 20.292187499999955],\n                            [-74.15371093750002, 20.168554687500006],\n                            [-75.11640624999995, 19.901416015625017],\n                            [-75.151611328125, 20.008349609375045],\n                            [-75.29047851562495, 19.893115234375017],\n                            [-76.15844726562497, 19.98974609374997],\n                            [-77.715087890625, 19.85546874999997],\n                            [-77.10380859374999, 20.407519531250017],\n                            [-77.22958984374995, 20.64375],\n                            [-78.11635742187497, 20.761865234374994],\n                            [-78.49077148437493, 21.05371093750003],\n                            [-78.72768554687497, 21.592724609374955],\n                            [-79.35742187500003, 21.58515625000001],\n                            [-80.23134765625, 21.872167968750063],\n                            [-80.48544921874998, 22.1234375],\n                            [-81.03564453124997, 22.073583984375063],\n                            [-81.18549804687495, 22.26796875000005],\n                            [-81.284375, 22.109423828125074],\n                            [-81.84941406249993, 22.21367187499999],\n                            [-82.077734375, 22.3876953125],\n                            [-81.71035156250002, 22.496679687500006],\n                            [-81.83881835937498, 22.672460937500034],\n                            [-82.73803710937497, 22.689257812500074],\n                            [-83.37963867187503, 22.222998046875034],\n                            [-83.90073242187495, 22.17011718750001],\n                            [-84.03095703124993, 21.94311523437503],\n                            [-84.502587890625, 21.776171875000045],\n                            [-84.50136718750002, 21.930273437499977],\n                            [-84.88720703125003, 21.856982421875074],\n                            [-84.32636718749998, 22.074316406250034],\n                            [-84.36127929687498, 22.37890625],\n                            [-84.04492187500003, 22.666015625000057],\n                            [-83.25781249999997, 22.967578125000017],\n                            [-81.83745117187499, 23.163037109374955]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Cuba\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-68.75107421874999, 12.059765625],\n                        [-68.9951171875, 12.141845703125],\n                        [-69.15888671875, 12.380273437499994],\n                        [-68.75107421874999, 12.059765625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Curaçao\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-81.36953124999997, 19.34887695312497],\n                            [-81.10712890624995, 19.305175781250057],\n                            [-81.40478515624994, 19.278417968750006],\n                            [-81.36953124999997, 19.34887695312497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-79.823388671875, 19.711914062500057],\n                            [-79.90620117187501, 19.702539062499994],\n                            [-79.74228515625, 19.757128906250017],\n                            [-79.823388671875, 19.711914062500057]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Cayman Is.\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [34.004492187500006, 35.065234375],\n                        [33.47578125000001, 35.000341796875],\n                        [33.3837890625, 35.1626953125],\n                        [32.91953125, 35.087841796875],\n                        [32.71269531250002, 35.171044921874994],\n                        [32.8798828125, 35.180566406249994],\n                        [32.94160156250001, 35.390429687499996],\n                        [33.60761718750001, 35.354150390624994],\n                        [34.55605468750002, 35.662060546875],\n                        [33.941992187500006, 35.292041015624996],\n                        [34.004492187500006, 35.065234375]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"N. Cyprus\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [32.71269531250002, 35.171044921874994],\n                        [32.91953125, 35.087841796875],\n                        [33.3837890625, 35.1626953125],\n                        [33.47578125000001, 35.000341796875],\n                        [34.004492187500006, 35.065234375],\n                        [34.05019531250002, 34.98837890625],\n                        [33.69941406250001, 34.969873046874994],\n                        [33.007910156250006, 34.569580078125],\n                        [32.44902343750002, 34.729443359375],\n                        [32.31718750000002, 34.9533203125],\n                        [32.30097656250001, 35.082958984375],\n                        [32.71269531250002, 35.171044921874994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Cyprus\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [14.809375, 50.858984375],\n                        [14.895800781250017, 50.861376953124996],\n                        [14.98291015625, 50.886572265625],\n                        [14.99375, 51.01435546875],\n                        [16.007226562500023, 50.611621093749996],\n                        [16.2822265625, 50.655615234375],\n                        [16.419726562500017, 50.573632812499994],\n                        [16.210351562500023, 50.423730468749994],\n                        [16.63916015625, 50.1021484375],\n                        [16.989648437500023, 50.2369140625],\n                        [16.88007812500001, 50.427050781249996],\n                        [17.41523437500001, 50.254785156249994],\n                        [17.702246093750006, 50.307177734374996],\n                        [17.627050781250006, 50.11640625],\n                        [17.874804687500017, 49.972265625],\n                        [18.0283203125, 50.03525390625],\n                        [18.562402343750023, 49.879345703125],\n                        [18.83222656250001, 49.510791015624996],\n                        [18.160937500000017, 49.257373046874996],\n                        [18.0859375, 49.06513671875],\n                        [17.75849609375001, 48.888134765625],\n                        [17.135644531250023, 48.841064453125],\n                        [16.953125, 48.598828125],\n                        [16.543554687500006, 48.796240234375],\n                        [16.057226562500006, 48.754785156249994],\n                        [15.066796875000023, 48.997851562499996],\n                        [14.691308593750023, 48.59921875],\n                        [14.049121093750017, 48.602490234375],\n                        [13.814746093750017, 48.766943359375],\n                        [13.769921875000023, 48.815966796874996],\n                        [13.684960937500023, 48.876708984375],\n                        [13.547656250000017, 48.95966796875],\n                        [13.440722656250017, 48.95556640625],\n                        [13.401171875000017, 48.977587890624996],\n                        [12.916699218750011, 49.33046875],\n                        [12.68115234375, 49.414501953125],\n                        [12.390527343750023, 49.739648437499994],\n                        [12.5125, 49.87744140625],\n                        [12.09921875, 50.310986328125],\n                        [12.134863281250006, 50.3109375],\n                        [12.1748046875, 50.288378906249996],\n                        [12.231152343750011, 50.244873046875],\n                        [12.27734375, 50.181445312499996],\n                        [12.3056640625, 50.205712890624994],\n                        [12.549023437500011, 50.393408203125],\n                        [13.016406250000017, 50.490380859374994],\n                        [13.18115234375, 50.510498046875],\n                        [14.369042968750023, 50.898730468749996],\n                        [14.319726562500023, 51.03779296875],\n                        [14.545703125000017, 50.993945312499996],\n                        [14.559667968750006, 50.954931640625],\n                        [14.59521484375, 50.918603515624994],\n                        [14.623828125000017, 50.91474609375],\n                        [14.613574218750017, 50.85556640625],\n                        [14.658203125, 50.8326171875],\n                        [14.723339843750011, 50.814697265625],\n                        [14.766503906250023, 50.818310546875],\n                        [14.797460937500006, 50.842333984374996],\n                        [14.809375, 50.858984375]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Czech Rep.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [14.1982421875, 53.919042968750034],\n                            [13.92578125, 53.879052734374966],\n                            [13.827734375, 54.12724609374999],\n                            [14.1982421875, 53.919042968750034]\n                        ]\n                    ],\n                    [\n                        [\n                            [13.709179687500011, 54.382714843749994],\n                            [13.707324218750074, 54.281152343749994],\n                            [13.190039062500034, 54.32563476562501],\n                            [13.336816406249994, 54.697119140625006],\n                            [13.65761718750008, 54.55957031249997],\n                            [13.709179687500011, 54.382714843749994]\n                        ]\n                    ],\n                    [\n                        [\n                            [9.739746093750028, 54.82553710937498],\n                            [10.022167968750011, 54.673925781250006],\n                            [9.86865234375, 54.47246093749999],\n                            [10.731542968750006, 54.31625976562506],\n                            [11.013378906250068, 54.37915039062497],\n                            [11.008593750000074, 54.18115234374997],\n                            [10.810742187500068, 54.075146484374955],\n                            [10.917773437500045, 53.99531250000004],\n                            [11.39960937500004, 53.94462890625002],\n                            [12.111328125, 54.168310546875006],\n                            [12.57539062500004, 54.467382812500006],\n                            [13.028613281250017, 54.411035156249994],\n                            [13.448046875000017, 54.14086914062503],\n                            [13.724218750000063, 54.153222656249966],\n                            [13.865527343750074, 53.85336914062498],\n                            [14.258886718750006, 53.729638671874994],\n                            [14.298730468750051, 53.55644531249999],\n                            [14.41455078125, 53.28349609374996],\n                            [14.412304687500011, 53.216748046874955],\n                            [14.410937500000074, 53.19902343749999],\n                            [14.368554687500051, 53.105566406250034],\n                            [14.293164062500068, 53.026757812499966],\n                            [14.138867187500068, 52.93286132812503],\n                            [14.128613281250011, 52.87822265625002],\n                            [14.253710937500017, 52.78251953124996],\n                            [14.514062500000023, 52.645605468750034],\n                            [14.619433593750017, 52.52851562499998],\n                            [14.569726562499994, 52.431103515624955],\n                            [14.554589843750023, 52.35966796874996],\n                            [14.573925781250068, 52.31416015625001],\n                            [14.615625, 52.277636718750045],\n                            [14.679882812500068, 52.25],\n                            [14.752539062500034, 52.08183593750002],\n                            [14.601660156250034, 51.832373046875006],\n                            [14.738671875000051, 51.62714843750004],\n                            [14.7109375, 51.54492187499997],\n                            [14.724707031250063, 51.523876953124955],\n                            [14.90595703125004, 51.463330078124955],\n                            [14.935546875000028, 51.435351562500045],\n                            [14.9638671875, 51.095117187499994],\n                            [14.917480468750057, 51.00874023437498],\n                            [14.814257812499989, 50.871630859375045],\n                            [14.809375, 50.858984375000034],\n                            [14.797460937500034, 50.84233398437502],\n                            [14.766503906250051, 50.81831054687501],\n                            [14.72333984375004, 50.81469726562497],\n                            [14.658203125, 50.832617187500006],\n                            [14.613574218750045, 50.85556640625006],\n                            [14.623828125000017, 50.91474609375004],\n                            [14.595214843750057, 50.91860351562502],\n                            [14.559667968750006, 50.954931640625034],\n                            [14.545703124999989, 50.99394531249999],\n                            [14.319726562500051, 51.037792968749955],\n                            [14.36904296875008, 50.89873046874996],\n                            [13.18115234375, 50.510498046875],\n                            [13.016406250000017, 50.490380859374994],\n                            [12.549023437500011, 50.393408203125034],\n                            [12.3056640625, 50.205712890624994],\n                            [12.27734375, 50.18144531250002],\n                            [12.231152343749983, 50.24487304687497],\n                            [12.174804687500057, 50.28837890624996],\n                            [12.134863281250006, 50.31093750000002],\n                            [12.099218750000034, 50.31098632812504],\n                            [12.089843749999972, 50.30175781250003],\n                            [12.089746093750051, 50.2685546875],\n                            [12.294598214285761, 50.13608119419641],\n                            [12.5125, 49.87744140625],\n                            [12.390527343750051, 49.739648437499994],\n                            [12.68115234375, 49.41450195312501],\n                            [12.91669921875004, 49.33046875000002],\n                            [13.401171875000074, 48.97758789062499],\n                            [13.440722656250045, 48.95556640625003],\n                            [13.547656250000074, 48.95966796874998],\n                            [13.684960937500051, 48.87670898437506],\n                            [13.769921875000051, 48.81596679687502],\n                            [13.814746093750017, 48.76694335937498],\n                            [13.802929687500011, 48.74750976562501],\n                            [13.798828124999972, 48.62167968750006],\n                            [13.785351562499983, 48.58745117187502],\n                            [13.486621093750074, 48.58183593750002],\n                            [13.471679687500028, 48.57182617187502],\n                            [13.459863281250023, 48.564550781250034],\n                            [13.409375, 48.39414062500006],\n                            [13.322851562500006, 48.33125],\n                            [13.215234375000023, 48.301904296874994],\n                            [12.760351562500063, 48.10698242187499],\n                            [12.95351562500008, 47.890625],\n                            [12.897656250000068, 47.721875],\n                            [13.054101562500051, 47.655126953125034],\n                            [13.047949218750034, 47.57915039062502],\n                            [13.031542968750074, 47.50800781250001],\n                            [13.01435546875004, 47.478076171875045],\n                            [12.968066406250017, 47.475683593750006],\n                            [12.878906250000057, 47.506445312500034],\n                            [12.809375, 47.542187499999955],\n                            [12.782812500000034, 47.56416015624998],\n                            [12.781152343750051, 47.590429687500006],\n                            [12.796191406249989, 47.60703125],\n                            [12.771386718750023, 47.63940429687503],\n                            [12.685839843750074, 47.66933593750002],\n                            [12.209277343750074, 47.71826171875003],\n                            [12.196875, 47.709082031250034],\n                            [12.203808593750011, 47.64672851562503],\n                            [12.185644531250063, 47.61953125],\n                            [11.041992187500028, 47.39311523437496],\n                            [10.98085937499999, 47.39814453125001],\n                            [10.893945312500051, 47.470458984375],\n                            [10.870605468750028, 47.500781250000045],\n                            [10.873046874999972, 47.52021484375001],\n                            [10.741601562500023, 47.52412109375001],\n                            [10.65869140625, 47.547216796875006],\n                            [10.482812500000051, 47.54179687499996],\n                            [10.439453125000028, 47.55156249999999],\n                            [10.403906250000063, 47.41699218750003],\n                            [10.369140625, 47.366064453125034],\n                            [10.18300781250008, 47.27880859375003],\n                            [10.200292968750063, 47.36342773437505],\n                            [10.066308593750023, 47.39335937500002],\n                            [10.064575892857171, 47.42369419642856],\n                            [10.059863281250045, 47.44907226562498],\n                            [10.034082031250023, 47.47358398437501],\n                            [9.971582031249994, 47.50532226562498],\n                            [9.839160156250017, 47.55229492187496],\n                            [9.748925781250023, 47.575537109375006],\n                            [9.524023437500034, 47.52421875000002],\n                            [8.572656250000023, 47.775634765625],\n                            [8.435742187500011, 47.73134765625002],\n                            [8.403417968750006, 47.687792968750045],\n                            [8.413281250000068, 47.66269531249998],\n                            [8.451757812500006, 47.65180664062498],\n                            [8.552343750000063, 47.65913085937498],\n                            [8.56708984375004, 47.65190429687502],\n                            [8.57050781250004, 47.63779296874998],\n                            [8.55947265625008, 47.62402343750003],\n                            [8.477636718750034, 47.61269531250002],\n                            [8.454003906249994, 47.59619140625003],\n                            [7.615625, 47.59272460937504],\n                            [7.616601562500023, 48.15678710937502],\n                            [8.134863281250006, 48.97358398437498],\n                            [7.450585937500051, 49.152197265625034],\n                            [6.735449218750006, 49.16059570312498],\n                            [6.344335937500006, 49.45273437499998],\n                            [6.4873046875, 49.798486328124994],\n                            [6.204882812500017, 49.915136718750034],\n                            [6.13818359375, 49.97431640625001],\n                            [6.10976562500008, 50.034375],\n                            [6.116503906250045, 50.120996093749966],\n                            [6.340917968750006, 50.451757812500034],\n                            [5.993945312500017, 50.75043945312504],\n                            [6.048437500000034, 50.90488281250006],\n                            [5.857519531250034, 51.030126953125006],\n                            [6.129980468750034, 51.14741210937501],\n                            [6.198828125000034, 51.45],\n                            [5.948730468750057, 51.80268554687501],\n                            [6.800390625, 51.96738281249998],\n                            [6.724511718749994, 52.080224609374966],\n                            [7.035156250000057, 52.38022460937498],\n                            [6.748828125000074, 52.464013671874994],\n                            [6.710742187500045, 52.61787109374998],\n                            [7.033007812500045, 52.65136718749997],\n                            [7.197265625000028, 53.28227539062499],\n                            [7.074316406250034, 53.477636718750006],\n                            [7.285253906250034, 53.68134765625001],\n                            [8.00927734375, 53.69072265624999],\n                            [8.108496093750063, 53.46767578125002],\n                            [8.245214843750006, 53.44531249999997],\n                            [8.333886718750051, 53.606201171875],\n                            [8.495214843750063, 53.39423828124998],\n                            [8.618945312500045, 53.875],\n                            [9.20556640625, 53.85595703124997],\n                            [9.783984375000074, 53.554638671874955],\n                            [9.31201171875, 53.859130859375],\n                            [8.92041015625, 53.96533203125006],\n                            [8.906640625000023, 54.26079101562502],\n                            [8.625781250000017, 54.35395507812501],\n                            [8.951855468750011, 54.46757812499996],\n                            [8.670312500000023, 54.903417968750034],\n                            [9.739746093750028, 54.82553710937498]\n                        ]\n                    ],\n                    [\n                        [\n                            [8.307714843750034, 54.786962890625034],\n                            [8.451464843750017, 55.05537109374998],\n                            [8.3798828125, 54.89985351562501],\n                            [8.629589843750068, 54.891748046874966],\n                            [8.307714843750034, 54.786962890625034]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Germany\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [43.24599609375002, 11.499804687499989],\n                        [42.92275390625002, 10.999316406249989],\n                        [42.557714843750006, 11.080761718749997],\n                        [41.79824218750002, 10.98046875],\n                        [41.79267578125001, 11.68603515625],\n                        [42.378515625, 12.46640625],\n                        [42.40859375000002, 12.494384765625],\n                        [42.45, 12.521337890624991],\n                        [42.47939453125002, 12.513623046874997],\n                        [42.703710937500006, 12.380322265624997],\n                        [42.76748046875002, 12.4228515625],\n                        [42.825292968750006, 12.5693359375],\n                        [42.86591796875001, 12.622802734375],\n                        [42.88330078125, 12.621289062499997],\n                        [43.00566406250002, 12.662304687499997],\n                        [43.11669921875, 12.70859375],\n                        [43.353515625, 12.367041015624991],\n                        [43.38027343750002, 12.091259765624997],\n                        [42.64003906250002, 11.560107421874989],\n                        [42.52177734375002, 11.572167968749994],\n                        [42.58378906250002, 11.496777343749997],\n                        [43.04277343750002, 11.588476562499991],\n                        [43.24599609375002, 11.499804687499989]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Djibouti\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-61.281689453125, 15.2490234375],\n                        [-61.37539062499999, 15.227294921875],\n                        [-61.45810546874999, 15.633105468750003],\n                        [-61.277246093749994, 15.526708984374991],\n                        [-61.281689453125, 15.2490234375]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Dominica\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [11.361425781250006, 54.891650390625045],\n                            [11.739550781250017, 54.80742187500002],\n                            [11.765917968750074, 54.67944335937506],\n                            [11.457421875000023, 54.628857421874955],\n                            [11.035546875000051, 54.77309570312505],\n                            [11.058593750000028, 54.940576171874966],\n                            [11.361425781250006, 54.891650390625045]\n                        ]\n                    ],\n                    [\n                        [\n                            [12.549218750000051, 54.96577148437504],\n                            [12.11884765625004, 54.91440429687506],\n                            [12.274023437500034, 55.064111328124994],\n                            [12.549218750000051, 54.96577148437504]\n                        ]\n                    ],\n                    [\n                        [\n                            [10.061230468750068, 54.88637695312502],\n                            [9.80625, 54.90600585937503],\n                            [9.78125, 55.06904296875001],\n                            [10.061230468750068, 54.88637695312502]\n                        ]\n                    ],\n                    [\n                        [\n                            [10.734082031250011, 54.750732421875],\n                            [10.621679687500006, 54.851416015625006],\n                            [10.95107421875008, 55.15620117187501],\n                            [10.734082031250011, 54.750732421875]\n                        ]\n                    ],\n                    [\n                        [\n                            [15.087695312500017, 55.021875],\n                            [14.684179687500063, 55.10224609375004],\n                            [14.765332031250068, 55.296728515625034],\n                            [15.132617187500017, 55.14453125000003],\n                            [15.087695312500017, 55.021875]\n                        ]\n                    ],\n                    [\n                        [\n                            [10.645117187500006, 55.60981445312498],\n                            [10.785253906250034, 55.13339843749998],\n                            [10.44277343750008, 55.04877929687498],\n                            [9.988769531250028, 55.163183593750006],\n                            [9.860644531250045, 55.515478515625034],\n                            [10.645117187500006, 55.60981445312498]\n                        ]\n                    ],\n                    [\n                        [\n                            [12.665722656250068, 55.596533203125006],\n                            [12.550878906250034, 55.55625],\n                            [12.59921875, 55.68022460937502],\n                            [12.665722656250068, 55.596533203125006]\n                        ]\n                    ],\n                    [\n                        [\n                            [12.56875, 55.785058593749966],\n                            [12.215039062500011, 55.46650390624998],\n                            [12.413085937500028, 55.28618164062502],\n                            [12.089941406250006, 55.18813476562505],\n                            [12.050390625000034, 54.81533203125002],\n                            [11.8623046875, 54.77260742187502],\n                            [11.653808593750057, 55.186914062499966],\n                            [11.286328125000068, 55.20444335937498],\n                            [10.978906250000051, 55.721533203125006],\n                            [11.322265625000028, 55.752539062500006],\n                            [11.627734375000074, 55.95688476562498],\n                            [11.819726562500023, 55.69765625000002],\n                            [11.86640625000004, 55.968164062499966],\n                            [12.218945312499983, 56.11865234374997],\n                            [12.578710937500006, 56.06406250000006],\n                            [12.56875, 55.785058593749966]\n                        ]\n                    ],\n                    [\n                        [\n                            [11.052148437500051, 57.25253906250006],\n                            [10.873828125000045, 57.26225585937499],\n                            [11.174511718750011, 57.322900390624994],\n                            [11.052148437500051, 57.25253906250006]\n                        ]\n                    ],\n                    [\n                        [\n                            [9.739746093750028, 54.82553710937498],\n                            [8.670312500000023, 54.903417968750034],\n                            [8.61591796875004, 55.41821289062503],\n                            [8.132128906250074, 55.59980468749998],\n                            [8.16396484375008, 56.60688476562498],\n                            [8.671679687500045, 56.49565429687496],\n                            [8.88808593750008, 56.73505859374998],\n                            [9.06708984375004, 56.79384765625005],\n                            [9.196386718750006, 56.70166015625],\n                            [9.2548828125, 57.01171875000003],\n                            [8.992773437499977, 57.01611328125003],\n                            [8.771972656250028, 56.72529296875004],\n                            [8.468359375, 56.66455078125],\n                            [8.284082031250023, 56.85234374999999],\n                            [8.618554687500051, 57.11127929687498],\n                            [9.43359375, 57.17431640625003],\n                            [9.96230468750008, 57.580957031249994],\n                            [10.609960937500034, 57.73691406249998],\n                            [10.282714843750057, 56.620507812499994],\n                            [10.926171875000051, 56.44326171875002],\n                            [10.753417968750028, 56.24199218749999],\n                            [10.31875, 56.212890625],\n                            [10.18300781250008, 55.86518554687504],\n                            [9.903710937500023, 55.84282226562502],\n                            [10.02363281250004, 55.76142578125004],\n                            [9.591113281250017, 55.49321289062502],\n                            [9.670996093750063, 55.26640624999999],\n                            [9.453710937500006, 55.03955078125006],\n                            [9.732324218750023, 54.96801757812506],\n                            [9.739746093750028, 54.82553710937498]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Denmark\", \"childNum\": 10, \"cp\": [10.2768332, 56.1773879] }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-71.647216796875, 19.195947265624994],\n                        [-71.746484375, 19.285839843749997],\n                        [-71.71147460937499, 19.486572265625],\n                        [-71.75742187499999, 19.688183593749997],\n                        [-71.779248046875, 19.718164062499994],\n                        [-71.6673828125, 19.8486328125],\n                        [-70.95415039062499, 19.913964843749994],\n                        [-70.19384765625, 19.63803710937499],\n                        [-69.95683593749999, 19.671875],\n                        [-69.739404296875, 19.29921875],\n                        [-69.23247070312499, 19.27182617187499],\n                        [-69.60595703125, 19.206494140624997],\n                        [-69.62363281249999, 19.117822265624994],\n                        [-68.684765625, 18.90478515625],\n                        [-68.33916015624999, 18.611523437499997],\n                        [-68.68740234375, 18.21494140624999],\n                        [-68.9349609375, 18.408007812500003],\n                        [-69.27451171874999, 18.43984375],\n                        [-69.770654296875, 18.443554687499997],\n                        [-70.479931640625, 18.21728515625],\n                        [-70.644677734375, 18.336230468750003],\n                        [-71.02783203125, 18.273193359375],\n                        [-71.43896484375, 17.63559570312499],\n                        [-71.63173828125, 17.773632812499997],\n                        [-71.768310546875, 18.03916015624999],\n                        [-71.76376953124999, 18.20395507812499],\n                        [-71.737255859375, 18.270800781250003],\n                        [-71.7619140625, 18.34130859375],\n                        [-71.87255859375, 18.416210937499997],\n                        [-71.940380859375, 18.512597656249994],\n                        [-72.000390625, 18.597900390625],\n                        [-71.98686523437499, 18.6103515625],\n                        [-71.86650390624999, 18.614160156249994],\n                        [-71.74321289062499, 18.73291015625],\n                        [-71.72705078125, 18.80322265625],\n                        [-71.733642578125, 18.856396484374997],\n                        [-71.80712890625, 18.987011718749997],\n                        [-71.647216796875, 19.195947265624994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Dominican Rep.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [8.207617187500006, 36.518945312499994],\n                        [8.348730468750006, 36.36796875],\n                        [8.318066406250011, 35.654931640624994],\n                        [8.31640625, 35.403125],\n                        [8.35986328125, 35.299609375],\n                        [8.394238281250011, 35.203857421875],\n                        [8.312109375, 35.084619140624994],\n                        [8.27685546875, 34.9794921875],\n                        [8.24560546875, 34.73408203125],\n                        [7.513867187500011, 34.080517578125],\n                        [7.534375, 33.717919921874994],\n                        [7.877246093750017, 33.172119140625],\n                        [8.1125, 33.055322265624994],\n                        [8.333398437500023, 32.543603515624994],\n                        [9.044042968750006, 32.07236328125],\n                        [9.160253906250006, 31.621337890625],\n                        [9.224023437500023, 31.373681640624994],\n                        [9.51875, 30.229394531249994],\n                        [9.310253906250011, 30.115234375],\n                        [9.805273437500006, 29.176953125],\n                        [9.916015625, 27.785693359374996],\n                        [9.74755859375, 27.330859375],\n                        [9.883203125000023, 26.630810546874997],\n                        [9.491406250000011, 26.333740234375],\n                        [9.4482421875, 26.067138671875],\n                        [10.000683593750011, 25.332080078125003],\n                        [10.255859375, 24.591015625],\n                        [10.395898437500023, 24.485595703125],\n                        [10.686132812500006, 24.55136718749999],\n                        [11.507617187500017, 24.314355468749994],\n                        [11.967871093750006, 23.517871093750003],\n                        [7.481738281250017, 20.873095703125003],\n                        [5.836621093750011, 19.479150390624994],\n                        [4.227636718750006, 19.142773437499997],\n                        [3.3564453125, 18.986621093750003],\n                        [3.119726562500006, 19.103173828124994],\n                        [3.255859375, 19.4109375],\n                        [3.130273437500023, 19.85019531249999],\n                        [1.685449218750023, 20.378369140624997],\n                        [1.610644531250017, 20.555566406249994],\n                        [1.165722656250011, 20.817431640625003],\n                        [1.1455078125, 21.102246093749997],\n                        [-1.947900390624994, 23.124804687500003],\n                        [-4.822607421874977, 24.99560546875],\n                        [-8.683349609375, 27.2859375],\n                        [-8.683349609375, 27.656445312499997],\n                        [-8.683349609375, 27.900390625],\n                        [-8.659912109375, 28.718603515625],\n                        [-7.485742187499994, 29.392236328124994],\n                        [-7.427685546874983, 29.425],\n                        [-7.142431640624977, 29.619580078124997],\n                        [-6.855566406249977, 29.601611328124996],\n                        [-6.755126953125, 29.583837890625],\n                        [-6.635351562499977, 29.568798828124997],\n                        [-6.597753906249977, 29.578955078125],\n                        [-6.520556640624989, 29.659863281249997],\n                        [-6.479736328125, 29.820361328124996],\n                        [-6.00429687499999, 29.83125],\n                        [-5.448779296874989, 29.956933593749994],\n                        [-5.293652343749983, 30.058642578124996],\n                        [-5.180126953124983, 30.166162109374994],\n                        [-4.96826171875, 30.465380859374996],\n                        [-4.778515624999983, 30.552392578124994],\n                        [-4.529150390624977, 30.625537109374996],\n                        [-4.322851562499977, 30.698876953124994],\n                        [-4.148779296874977, 30.8095703125],\n                        [-3.626904296874983, 31.000927734374997],\n                        [-3.833398437499994, 31.197802734374996],\n                        [-3.837109374999983, 31.512353515624994],\n                        [-3.768164062499977, 31.68955078125],\n                        [-3.700244140624989, 31.700097656249994],\n                        [-3.604589843749977, 31.686767578125],\n                        [-3.439794921874977, 31.704541015624997],\n                        [-3.017382812499989, 31.834277343749996],\n                        [-2.988232421874983, 31.87421875],\n                        [-2.930859374999983, 32.042529296874996],\n                        [-2.863427734374994, 32.07470703125],\n                        [-1.275341796874983, 32.089013671874994],\n                        [-1.16259765625, 32.399169921875],\n                        [-1.111035156249983, 32.552294921874996],\n                        [-1.188232421875, 32.60849609375],\n                        [-1.29638671875, 32.675683593749994],\n                        [-1.352148437499977, 32.703369140625],\n                        [-1.45, 32.784814453124994],\n                        [-1.510009765625, 32.87763671875],\n                        [-1.550732421874983, 33.073583984375],\n                        [-1.67919921875, 33.318652343749996],\n                        [-1.795605468749983, 34.751904296875],\n                        [-2.131787109374983, 34.970849609374994],\n                        [-2.190771484374977, 35.02978515625],\n                        [-2.219628906249994, 35.10419921875],\n                        [-1.673632812499989, 35.18310546875],\n                        [-0.426123046874977, 35.8615234375],\n                        [-0.048242187499994, 35.8328125],\n                        [0.312207031250011, 36.162353515625],\n                        [0.9716796875, 36.4439453125],\n                        [2.593359375, 36.60068359375],\n                        [2.972851562500011, 36.784472656249996],\n                        [3.779003906250011, 36.89619140625],\n                        [4.758105468750017, 36.896337890625],\n                        [5.29541015625, 36.648242187499996],\n                        [6.486523437500011, 37.085742187499996],\n                        [6.927539062500017, 36.91943359375],\n                        [7.238476562500011, 36.968505859375],\n                        [7.204296875000011, 37.0923828125],\n                        [7.910449218750017, 36.856347656249994],\n                        [8.576562500000023, 36.93720703125],\n                        [8.601269531250011, 36.833935546875],\n                        [8.207617187500006, 36.518945312499994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Algeria\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-80.131591796875, -2.973144531249957],\n                            [-80.27294921875003, -2.995898437499974],\n                            [-80.22368164062502, -2.753125],\n                            [-80.08076171874995, -2.668847656249966],\n                            [-79.90903320312495, -2.725585937499972],\n                            [-80.131591796875, -2.973144531249957]\n                        ]\n                    ],\n                    [\n                        [\n                            [-90.42392578125, -1.339941406250034],\n                            [-90.51953124999994, -1.299121093749974],\n                            [-90.47719726562494, -1.22099609374996],\n                            [-90.42392578125, -1.339941406250034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-89.41889648437498, -0.911035156249966],\n                            [-89.60859374999998, -0.888574218750009],\n                            [-89.28784179687503, -0.689843750000023],\n                            [-89.41889648437498, -0.911035156249966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-90.33486328125, -0.771582031249977],\n                            [-90.54213867187502, -0.676464843749955],\n                            [-90.53168945312493, -0.581445312499966],\n                            [-90.26938476562498, -0.48466796874996],\n                            [-90.19272460937498, -0.658789062500006],\n                            [-90.33486328125, -0.771582031249977]\n                        ]\n                    ],\n                    [\n                        [\n                            [-91.42597656249995, -0.460839843749994],\n                            [-91.61074218749994, -0.44394531250002],\n                            [-91.64667968749998, -0.284472656249946],\n                            [-91.46015625000001, -0.255664062500031],\n                            [-91.42597656249995, -0.460839843749994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-90.57392578124993, -0.333984375],\n                            [-90.8677734375, -0.271386718750037],\n                            [-90.78037109374998, -0.160449218749989],\n                            [-90.57392578124993, -0.333984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-91.27216796874998, 0.025146484374986],\n                            [-90.799658203125, -0.752050781249991],\n                            [-90.90551757812497, -0.94052734375002],\n                            [-91.13105468750001, -1.019628906249977],\n                            [-91.41904296874998, -0.996679687500006],\n                            [-91.49541015624999, -0.860937499999977],\n                            [-91.120947265625, -0.559082031250028],\n                            [-91.36918945312493, -0.287207031249977],\n                            [-91.42885742187502, -0.023388671874955],\n                            [-91.59682617187497, 0.002099609374994],\n                            [-91.36137695312496, 0.125830078124977],\n                            [-91.27216796874998, 0.025146484374986]\n                        ]\n                    ],\n                    [\n                        [\n                            [-78.90922851562502, 1.252783203124977],\n                            [-78.99169921875003, 1.293212890625043],\n                            [-78.89980468749997, 1.359765625],\n                            [-78.90922851562502, 1.252783203124977]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.28447265624999, -0.10654296875002],\n                            [-75.62626953124999, -0.122851562499974],\n                            [-75.63203125000001, -0.157617187500037],\n                            [-75.56059570312502, -0.200097656249994],\n                            [-75.49106445312498, -0.24833984374996],\n                            [-75.42470703124997, -0.408886718749983],\n                            [-75.259375, -0.59013671874996],\n                            [-75.24960937499998, -0.951855468750026],\n                            [-75.34819335937499, -0.966796874999957],\n                            [-75.38012695312503, -0.94023437499996],\n                            [-75.40805664062503, -0.92431640625],\n                            [-75.42041015624997, -0.962207031250003],\n                            [-75.570556640625, -1.53125],\n                            [-76.08979492187501, -2.133105468749974],\n                            [-76.6791015625, -2.562597656249991],\n                            [-77.860595703125, -2.981640625000011],\n                            [-78.240380859375, -3.472558593750009],\n                            [-78.345361328125, -3.397363281249966],\n                            [-78.64799804687499, -4.248144531250006],\n                            [-78.68603515625003, -4.562402343749994],\n                            [-78.86152343749998, -4.665039062499943],\n                            [-78.90761718749997, -4.714453124999977],\n                            [-78.92578125, -4.770703124999983],\n                            [-78.91420898437497, -4.818652343749974],\n                            [-78.919189453125, -4.858398437499986],\n                            [-78.97539062499999, -4.873242187499997],\n                            [-78.99526367187497, -4.908007812499974],\n                            [-79.03330078124998, -4.96914062499999],\n                            [-79.07626953125003, -4.990625],\n                            [-79.18666992187497, -4.958203124999983],\n                            [-79.26811523437493, -4.957617187499949],\n                            [-79.33095703124997, -4.92783203125002],\n                            [-79.39941406249997, -4.840039062499983],\n                            [-79.45576171874998, -4.766210937499949],\n                            [-79.50190429687495, -4.670605468750011],\n                            [-79.51616210937493, -4.539160156249963],\n                            [-79.57768554687496, -4.50058593750002],\n                            [-79.638525390625, -4.454882812500031],\n                            [-79.71098632812502, -4.467578124999946],\n                            [-79.79726562500002, -4.47636718749996],\n                            [-79.8451171875, -4.445898437499977],\n                            [-79.962890625, -4.390332031250026],\n                            [-80.06352539062499, -4.327539062500023],\n                            [-80.13955078125002, -4.296093750000011],\n                            [-80.19746093750001, -4.311035156249943],\n                            [-80.293359375, -4.416796875],\n                            [-80.38349609374998, -4.46367187499996],\n                            [-80.424169921875, -4.461425781250028],\n                            [-80.47856445312499, -4.430078125000037],\n                            [-80.48847656249995, -4.393652343749991],\n                            [-80.44384765625003, -4.335839843750023],\n                            [-80.35288085937495, -4.208496093750014],\n                            [-80.453759765625, -4.205175781249963],\n                            [-80.48847656249995, -4.165527343749972],\n                            [-80.49345703124999, -4.119140625000014],\n                            [-80.510009765625, -4.06953125000004],\n                            [-80.49013671874994, -4.010058593750003],\n                            [-80.43720703125001, -3.978613281249991],\n                            [-80.30327148437499, -4.005078124999969],\n                            [-80.26689453124993, -3.948828124999963],\n                            [-80.23051757812499, -3.924023437499969],\n                            [-80.19414062499996, -3.905859375],\n                            [-80.24375, -3.576757812500006],\n                            [-80.32465820312498, -3.387890625],\n                            [-79.96333007812501, -3.15771484375],\n                            [-79.72988281249997, -2.579101562499972],\n                            [-79.842138671875, -2.0673828125],\n                            [-79.92558593749996, -2.548535156249969],\n                            [-80.03017578124994, -2.556738281249949],\n                            [-80.00664062499993, -2.353808593750003],\n                            [-80.28471679687502, -2.706738281249955],\n                            [-80.93217773437493, -2.269140624999977],\n                            [-80.76059570312498, -1.934570312500028],\n                            [-80.90239257812499, -1.078906249999974],\n                            [-80.55390624999998, -0.847949218749989],\n                            [-80.45546875, -0.585449218749986],\n                            [-80.282373046875, -0.620507812500023],\n                            [-80.48227539062503, -0.368261718749963],\n                            [-80.046142578125, 0.155371093750048],\n                            [-80.08828124999997, 0.78476562500002],\n                            [-78.89965820312503, 1.20625],\n                            [-78.85966796874996, 1.455371093750031],\n                            [-78.1806640625, 0.968554687499974],\n                            [-77.702880859375, 0.837841796874997],\n                            [-77.46767578124997, 0.636523437500017],\n                            [-77.396337890625, 0.393896484374963],\n                            [-76.49462890624997, 0.23544921875002],\n                            [-76.27060546874998, 0.439404296874997],\n                            [-75.77666015624999, 0.08925781249998],\n                            [-75.28447265624999, -0.10654296875002]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Ecuador\", \"childNum\": 9 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [34.24531250000001, 31.208300781249996],\n                        [34.904296875, 29.47734375],\n                        [34.736425781250006, 29.27060546875],\n                        [34.39970703125002, 28.01601562499999],\n                        [34.22011718750002, 27.764306640624994],\n                        [33.76025390625, 28.04765625],\n                        [33.24775390625001, 28.567724609375],\n                        [32.56572265625002, 29.973974609375],\n                        [32.35976562500002, 29.630664062499996],\n                        [32.89824218750002, 28.565234375],\n                        [33.54707031250001, 27.898144531249997],\n                        [33.5498046875, 27.607373046874997],\n                        [33.84931640625001, 27.184912109375],\n                        [33.959082031250006, 26.6490234375],\n                        [35.19414062500002, 24.475146484375003],\n                        [35.78388671875001, 23.937792968750003],\n                        [35.54082031250002, 23.920654296875],\n                        [35.50439453125, 23.779296875],\n                        [35.697851562500006, 22.946191406249994],\n                        [36.22968750000001, 22.628808593749994],\n                        [36.87138671875002, 21.996728515624994],\n                        [31.434472656250023, 21.995849609375],\n                        [31.486132812500017, 22.14780273437499],\n                        [31.400292968750023, 22.202441406250003],\n                        [31.260644531250023, 22.00229492187499],\n                        [31.092675781250023, 21.994873046875],\n                        [28.036425781250017, 21.995361328125],\n                        [24.980273437500017, 21.995849609375],\n                        [24.980273437500017, 25.5888671875],\n                        [24.980273437500017, 29.181884765625],\n                        [24.703222656250006, 30.201074218749994],\n                        [24.96142578125, 30.678515625],\n                        [24.85273437500001, 31.334814453125],\n                        [25.150488281250006, 31.654980468749997],\n                        [25.382226562500023, 31.51279296875],\n                        [25.89326171875001, 31.620898437499996],\n                        [27.248046875, 31.377880859374997],\n                        [27.5400390625, 31.212695312499996],\n                        [28.51484375000001, 31.050439453124994],\n                        [29.072070312500017, 30.830273437499997],\n                        [29.929785156250006, 31.227490234374997],\n                        [30.22265625, 31.2583984375],\n                        [30.395117187500006, 31.4576171875],\n                        [30.92353515625001, 31.566845703124997],\n                        [30.56298828125, 31.4169921875],\n                        [31.001757812500017, 31.462792968749994],\n                        [31.082910156250023, 31.603320312499996],\n                        [31.5244140625, 31.458251953125],\n                        [31.888964843750017, 31.54140625],\n                        [32.13603515625002, 31.341064453125],\n                        [31.8921875, 31.482470703124996],\n                        [31.77109375, 31.292578125],\n                        [32.10175781250001, 31.092822265624996],\n                        [32.281835937500006, 31.200878906249997],\n                        [32.21621093750002, 31.29375],\n                        [32.60332031250002, 31.06875],\n                        [33.66650390625, 31.130419921874996],\n                        [34.19814453125002, 31.322607421875],\n                        [34.24531250000001, 31.208300781249996]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Egypt\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [40.141210937500006, 15.696142578125034],\n                            [40.399023437500006, 15.579882812500045],\n                            [39.975195312500006, 15.612451171875023],\n                            [39.94746093750004, 15.696142578125034],\n                            [40.07050781250004, 15.676611328125034],\n                            [39.93994140625003, 15.744531250000023],\n                            [39.9567382812501, 15.889404296875057],\n                            [40.141210937500006, 15.696142578125034]\n                        ]\n                    ],\n                    [\n                        [\n                            [40.07646484375002, 16.082421875000023],\n                            [40.11005859375004, 15.985742187500051],\n                            [39.99609375000003, 16.04267578125001],\n                            [40.07646484375002, 16.082421875000023]\n                        ]\n                    ],\n                    [\n                        [\n                            [40.938574218750006, 13.983105468749997],\n                            [40.82011718750002, 14.111669921874991],\n                            [40.22148437500002, 14.431152343749972],\n                            [39.531835937500006, 14.53671875],\n                            [39.198046875000074, 14.479394531250037],\n                            [39.1354492187501, 14.581884765625034],\n                            [39.07421874999997, 14.628222656249974],\n                            [39.02382812499999, 14.628222656249974],\n                            [38.99570312500006, 14.586865234374983],\n                            [38.81201171875003, 14.482324218750009],\n                            [38.50439453124997, 14.42441406250002],\n                            [38.43144531250002, 14.428613281249994],\n                            [38.221484375000074, 14.649658203124986],\n                            [38.002539062500006, 14.737109375000045],\n                            [37.94345703125006, 14.810546875],\n                            [37.884179687499994, 14.852294921874972],\n                            [37.82031250000003, 14.708496093749986],\n                            [37.70839843750005, 14.45722656250004],\n                            [37.64843750000003, 14.32255859375006],\n                            [37.571191406249994, 14.149072265624966],\n                            [37.546777343749994, 14.143847656249974],\n                            [37.507226562499994, 14.156396484375037],\n                            [37.257226562499994, 14.453759765625051],\n                            [37.024511718750006, 14.271972656250057],\n                            [36.81191406250005, 14.315039062500034],\n                            [36.67910156250005, 14.307568359375026],\n                            [36.542382812499994, 14.25820312499999],\n                            [36.52431640625005, 14.256835937499986],\n                            [36.492285156250006, 14.544335937500023],\n                            [36.470800781250006, 14.736474609375009],\n                            [36.448144531249994, 14.940087890625009],\n                            [36.42675781249997, 15.132080078125043],\n                            [36.566015625, 15.362109375],\n                            [36.9137695312501, 16.296191406250045],\n                            [36.887792968750006, 16.624658203124994],\n                            [36.9787109375001, 16.800585937500045],\n                            [36.9757812500001, 16.866552734375006],\n                            [36.99521484375006, 17.020556640625017],\n                            [37.00898437500004, 17.058886718750017],\n                            [37.06152343749997, 17.061279296875057],\n                            [37.16953125000006, 17.04140625],\n                            [37.41103515625005, 17.061718749999955],\n                            [37.452929687500074, 17.108691406250017],\n                            [37.51015625, 17.28813476562499],\n                            [37.54746093750006, 17.32412109375005],\n                            [37.78242187500004, 17.458007812500057],\n                            [38.253515625, 17.584765625000017],\n                            [38.26728515625004, 17.616699218750057],\n                            [38.28984375000002, 17.637011718750017],\n                            [38.34736328125004, 17.68359375],\n                            [38.37373046875004, 17.717333984375045],\n                            [38.42246093750006, 17.823925781249983],\n                            [38.60947265625006, 18.00507812500004],\n                            [39.03447265625002, 17.085546875000034],\n                            [39.298925781250006, 15.921093750000011],\n                            [39.78554687499999, 15.124853515624991],\n                            [39.86376953124997, 15.470312500000034],\n                            [40.20410156250003, 15.014111328124983],\n                            [41.17646484375004, 14.620312500000054],\n                            [41.65820312499997, 13.983056640624994],\n                            [42.24511718749997, 13.587646484374986],\n                            [42.39931640625005, 13.212597656249969],\n                            [42.522851562499994, 13.221484375],\n                            [42.796191406250074, 12.864257812500057],\n                            [42.96953125000002, 12.808349609375028],\n                            [42.99902343750003, 12.899511718750048],\n                            [43.08291015625005, 12.824609374999966],\n                            [43.11669921874997, 12.708593749999963],\n                            [43.00566406250002, 12.66230468750004],\n                            [42.88330078124997, 12.621289062500026],\n                            [42.86591796875004, 12.622802734374986],\n                            [42.82529296875006, 12.569335937500014],\n                            [42.767480468749994, 12.422851562500014],\n                            [42.70371093750006, 12.380322265625054],\n                            [42.479394531249994, 12.513623046875026],\n                            [42.45, 12.521337890625006],\n                            [42.40859375, 12.494384765625014],\n                            [42.37851562500006, 12.46640625],\n                            [42.28994140625005, 12.570214843750009],\n                            [42.225, 12.661962890624963],\n                            [42.13427734374997, 12.771435546874969],\n                            [41.95214843749997, 12.88232421875],\n                            [41.85957031250004, 13.025878906250028],\n                            [41.76503906250005, 13.183935546874991],\n                            [41.362890625, 13.499804687500031],\n                            [40.938574218750006, 13.983105468749997]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Eritrea\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-17.887939453125, 27.809570312500057],\n                            [-17.984765625000023, 27.646386718750023],\n                            [-18.160546874999937, 27.76147460937503],\n                            [-17.887939453125, 27.809570312500057]\n                        ]\n                    ],\n                    [\n                        [\n                            [-15.400585937499955, 28.147363281250023],\n                            [-15.436767578124972, 27.810693359375023],\n                            [-15.71030273437492, 27.784082031250023],\n                            [-15.809472656249966, 27.994482421874977],\n                            [-15.682763671874994, 28.15405273437497],\n                            [-15.400585937499955, 28.147363281250023]\n                        ]\n                    ],\n                    [\n                        [\n                            [-17.184667968749977, 28.02197265624997],\n                            [-17.324902343749955, 28.11767578125003],\n                            [-17.25859375, 28.203173828125045],\n                            [-17.103759765624943, 28.111132812500017],\n                            [-17.184667968749977, 28.02197265624997]\n                        ]\n                    ],\n                    [\n                        [\n                            [-16.33447265624997, 28.37993164062499],\n                            [-16.41821289062497, 28.15141601562496],\n                            [-16.65800781249999, 28.007177734374977],\n                            [-16.905322265625017, 28.33959960937503],\n                            [-16.12363281249992, 28.57597656249996],\n                            [-16.33447265624997, 28.37993164062499]\n                        ]\n                    ],\n                    [\n                        [\n                            [-14.196777343749943, 28.169287109375063],\n                            [-14.332617187500006, 28.056005859374977],\n                            [-14.49179687499992, 28.100927734374977],\n                            [-14.231982421875017, 28.21582031250003],\n                            [-14.003369140624983, 28.706689453125023],\n                            [-13.85722656249996, 28.73803710937503],\n                            [-13.928027343749989, 28.25346679687499],\n                            [-14.196777343749943, 28.169287109375063]\n                        ]\n                    ],\n                    [\n                        [\n                            [-17.83427734374999, 28.49321289062496],\n                            [-18.00078124999999, 28.758251953124955],\n                            [-17.928808593749977, 28.844580078125063],\n                            [-17.7265625, 28.724462890625006],\n                            [-17.83427734374999, 28.49321289062496]\n                        ]\n                    ],\n                    [\n                        [\n                            [-13.715966796874966, 28.911230468750034],\n                            [-13.85991210937496, 28.869091796874983],\n                            [-13.823632812499966, 29.013330078124966],\n                            [-13.463574218749955, 29.237207031250023],\n                            [-13.477929687499966, 29.00659179687503],\n                            [-13.715966796874966, 28.911230468750034]\n                        ]\n                    ],\n                    [\n                        [\n                            [1.593945312500068, 38.672070312499955],\n                            [1.40576171875, 38.670996093750006],\n                            [1.436328125000017, 38.768212890624994],\n                            [1.593945312500068, 38.672070312499955]\n                        ]\n                    ],\n                    [\n                        [\n                            [1.445214843750051, 38.91870117187503],\n                            [1.223339843750068, 38.90385742187502],\n                            [1.3486328125, 39.080810546875],\n                            [1.564453125, 39.12104492187504],\n                            [1.623632812499977, 39.03881835937497],\n                            [1.445214843750051, 38.91870117187503]\n                        ]\n                    ],\n                    [\n                        [\n                            [3.145312500000017, 39.79008789062499],\n                            [3.461816406250023, 39.69775390625003],\n                            [3.072851562500006, 39.30126953124997],\n                            [2.799804687500057, 39.38505859374999],\n                            [2.700585937500023, 39.54213867187502],\n                            [2.49951171875, 39.47788085937498],\n                            [2.37001953125008, 39.57207031249999],\n                            [3.15869140625, 39.97050781249999],\n                            [3.145312500000017, 39.79008789062499]\n                        ]\n                    ],\n                    [\n                        [\n                            [4.293652343750011, 39.84184570312499],\n                            [3.8671875, 39.958740234375],\n                            [3.853417968750051, 40.06303710937502],\n                            [4.22578125000004, 40.032373046874966],\n                            [4.293652343750011, 39.84184570312499]\n                        ]\n                    ],\n                    [\n                        [\n                            [-1.794042968749949, 43.407324218750006],\n                            [-1.410693359374932, 43.240087890625034],\n                            [-1.460839843749937, 43.05175781250006],\n                            [-1.300048828124943, 43.10097656250002],\n                            [-0.586425781249943, 42.798974609374966],\n                            [0.631640625000045, 42.689599609374994],\n                            [0.696875, 42.84511718750005],\n                            [1.428320312499977, 42.59589843749998],\n                            [1.414843750000074, 42.54838867187499],\n                            [1.448828124999977, 42.43745117187504],\n                            [1.534082031250051, 42.44169921875002],\n                            [1.7060546875, 42.50332031250005],\n                            [1.859765625000051, 42.457080078125045],\n                            [1.927929687500068, 42.42631835937499],\n                            [2.032714843750028, 42.353515625],\n                            [3.21142578125, 42.43115234375],\n                            [3.248046875, 41.94423828125002],\n                            [3.0048828125, 41.76743164062506],\n                            [2.082617187500063, 41.287402343750045],\n                            [1.032910156250068, 41.06206054687496],\n                            [0.714648437500074, 40.822851562500006],\n                            [0.891113281250057, 40.72236328125004],\n                            [0.59609375000008, 40.614501953125],\n                            [-0.327001953124949, 39.519873046875006],\n                            [-0.204931640624949, 39.062597656250034],\n                            [0.20156250000008, 38.75917968750002],\n                            [-0.520800781249989, 38.317285156249966],\n                            [-0.814648437500011, 37.76992187500002],\n                            [-0.721582031249966, 37.63105468749998],\n                            [-1.327539062499937, 37.561132812500034],\n                            [-1.640966796874949, 37.38696289062497],\n                            [-2.111523437499983, 36.77666015624999],\n                            [-4.366845703124994, 36.71811523437506],\n                            [-4.67412109374996, 36.506445312500006],\n                            [-5.171484374999949, 36.423779296874955],\n                            [-5.3609375, 36.134912109374994],\n                            [-5.62548828125, 36.02592773437499],\n                            [-6.040673828124937, 36.18842773437498],\n                            [-6.38413085937492, 36.63701171874996],\n                            [-6.216796875000028, 36.91357421875],\n                            [-6.396191406249983, 36.831640625],\n                            [-6.863769531250028, 37.27890625],\n                            [-7.406152343749937, 37.17944335937497],\n                            [-7.44394531249992, 37.72827148437497],\n                            [-6.957568359374932, 38.18789062499999],\n                            [-7.106396484374983, 38.181005859375006],\n                            [-7.343017578124943, 38.45742187500002],\n                            [-6.997949218749994, 39.05644531250002],\n                            [-7.53569335937496, 39.66157226562501],\n                            [-7.117675781249972, 39.681689453125045],\n                            [-6.975390624999932, 39.79838867187502],\n                            [-6.896093749999949, 40.02182617187506],\n                            [-7.032617187499966, 40.16791992187498],\n                            [-6.8101562499999, 40.343115234375034],\n                            [-6.928466796874972, 41.009130859375006],\n                            [-6.2125, 41.53203125],\n                            [-6.542187499999955, 41.672509765624994],\n                            [-6.61826171874992, 41.9423828125],\n                            [-7.147119140625023, 41.98115234374998],\n                            [-7.40361328124996, 41.833691406249955],\n                            [-8.152490234374937, 41.81196289062498],\n                            [-8.266064453124983, 42.13740234375001],\n                            [-8.777148437500017, 41.941064453124994],\n                            [-8.887207031249943, 42.105273437500045],\n                            [-8.690917968749943, 42.274169921875],\n                            [-8.815820312499966, 42.285253906250034],\n                            [-8.730029296874989, 42.411718750000034],\n                            [-8.8115234375, 42.64033203124998],\n                            [-9.033105468750023, 42.593847656250006],\n                            [-8.927197265624926, 42.79858398437497],\n                            [-9.235205078124977, 42.97690429687498],\n                            [-9.178076171874977, 43.17402343749998],\n                            [-8.248925781249937, 43.43940429687498],\n                            [-8.256738281249937, 43.57988281249999],\n                            [-8.004687499999932, 43.69438476562496],\n                            [-7.503613281249983, 43.73994140625001],\n                            [-7.060986328124955, 43.55395507812503],\n                            [-5.846679687499943, 43.645068359375045],\n                            [-4.52304687499992, 43.41572265625004],\n                            [-3.604638671874966, 43.51948242187504],\n                            [-3.045605468749926, 43.37158203125],\n                            [-2.875048828125017, 43.454443359375006],\n                            [-2.337109374999926, 43.32802734375002],\n                            [-1.794042968749949, 43.407324218750006]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Spain\", \"childNum\": 12, \"cp\": [-2.9366964, 40.3438963] }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [22.61738281250004, 58.62124023437502],\n                            [23.323242187500057, 58.45083007812502],\n                            [22.730273437500045, 58.23066406250001],\n                            [22.371679687499977, 58.217138671875006],\n                            [21.996875, 57.93134765624998],\n                            [22.187695312500068, 58.15434570312502],\n                            [21.88212890624999, 58.262353515624994],\n                            [21.862304687500057, 58.497167968750034],\n                            [22.61738281250004, 58.62124023437502]\n                        ]\n                    ],\n                    [\n                        [\n                            [23.343554687500017, 58.550341796875045],\n                            [23.10908203125004, 58.65922851562502],\n                            [23.332812500000045, 58.648583984374994],\n                            [23.343554687500017, 58.550341796875045]\n                        ]\n                    ],\n                    [\n                        [\n                            [22.923730468750023, 58.826904296875],\n                            [22.54218750000001, 58.68999023437499],\n                            [22.411035156250023, 58.863378906250034],\n                            [22.05625, 58.94360351562506],\n                            [22.6494140625, 59.08710937499998],\n                            [22.90986328125004, 58.99121093749997],\n                            [22.923730468750023, 58.826904296875]\n                        ]\n                    ],\n                    [\n                        [\n                            [28.0125, 59.484277343749966],\n                            [28.15107421875004, 59.374414062499966],\n                            [27.434179687500006, 58.787255859374994],\n                            [27.502441406250057, 58.221337890624994],\n                            [27.778515625000068, 57.87070312500006],\n                            [27.542089843750063, 57.799414062500006],\n                            [27.4, 57.66679687499999],\n                            [27.35195312500005, 57.528125],\n                            [26.96601562500001, 57.60913085937506],\n                            [26.532617187499994, 57.53100585937503],\n                            [26.29804687500001, 57.60107421875],\n                            [25.66015625, 57.920166015625],\n                            [25.27265625000001, 58.009375],\n                            [25.11103515625004, 58.06342773437498],\n                            [24.45888671875005, 57.907861328124994],\n                            [24.3625, 57.86616210937501],\n                            [24.322558593750074, 57.87060546875003],\n                            [24.529101562500045, 58.35424804687497],\n                            [24.114843750000034, 58.26611328125006],\n                            [23.767578125000057, 58.36083984374997],\n                            [23.50927734375003, 58.65854492187498],\n                            [23.680761718750063, 58.787158203125074],\n                            [23.43203125, 58.920654296875],\n                            [23.494433593750017, 59.19565429687498],\n                            [24.083398437500023, 59.29189453125005],\n                            [24.38037109375003, 59.47265625],\n                            [25.44375, 59.52114257812502],\n                            [25.50927734374997, 59.63901367187506],\n                            [26.974707031250006, 59.450634765624955],\n                            [28.0125, 59.484277343749966]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Estonia\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [38.43144531250002, 14.428613281249994],\n                        [38.50439453125, 14.424414062499991],\n                        [38.81201171875, 14.482324218749994],\n                        [38.995703125, 14.586865234374997],\n                        [39.02382812500002, 14.628222656250003],\n                        [39.07421875, 14.628222656250003],\n                        [39.13544921875001, 14.581884765624991],\n                        [39.19804687500002, 14.479394531249994],\n                        [39.531835937500006, 14.53671875],\n                        [40.22148437500002, 14.43115234375],\n                        [40.82011718750002, 14.111669921874991],\n                        [40.938574218750006, 13.983105468749997],\n                        [41.362890625, 13.499804687500003],\n                        [41.76503906250002, 13.183935546874991],\n                        [41.85957031250001, 13.02587890625],\n                        [41.9521484375, 12.88232421875],\n                        [42.13427734375, 12.771435546874997],\n                        [42.225, 12.661962890624991],\n                        [42.28994140625002, 12.570214843749994],\n                        [42.378515625, 12.46640625],\n                        [41.79267578125001, 11.68603515625],\n                        [41.79824218750002, 10.98046875],\n                        [42.557714843750006, 11.080761718749997],\n                        [42.92275390625002, 10.999316406249989],\n                        [42.65644531250001, 10.6],\n                        [42.84160156250002, 10.203076171874997],\n                        [43.181640625, 9.879980468749991],\n                        [43.482519531250006, 9.379492187499991],\n                        [43.98378906250002, 9.008837890624989],\n                        [46.97822265625001, 7.9970703125],\n                        [47.97822265625001, 7.9970703125],\n                        [44.940527343750006, 4.912011718749994],\n                        [43.988867187500006, 4.950537109374991],\n                        [43.58349609375, 4.85498046875],\n                        [43.12568359375001, 4.644482421874997],\n                        [42.85664062500001, 4.32421875],\n                        [42.02412109375001, 4.137939453125],\n                        [41.91533203125002, 4.031298828124989],\n                        [41.88398437500001, 3.977734375],\n                        [41.73769531250002, 3.979052734374989],\n                        [41.48193359375, 3.96328125],\n                        [41.37246093750002, 3.946191406249994],\n                        [41.22089843750001, 3.943554687499997],\n                        [41.02080078125002, 4.057470703124991],\n                        [40.765234375, 4.27304687499999],\n                        [39.84218750000002, 3.851464843749994],\n                        [39.79033203125002, 3.754248046874991],\n                        [39.65751953125002, 3.577832031249997],\n                        [39.49443359375002, 3.456103515624989],\n                        [38.608007812500006, 3.60009765625],\n                        [38.45156250000002, 3.604833984374991],\n                        [38.22529296875001, 3.618994140624991],\n                        [38.08613281250001, 3.64882812499999],\n                        [37.15458984375002, 4.254541015624994],\n                        [36.90556640625002, 4.411474609374991],\n                        [36.02197265625, 4.468115234374991],\n                        [35.76308593750002, 4.808007812499994],\n                        [35.75615234375002, 4.950488281249989],\n                        [35.779296875, 5.105566406249991],\n                        [35.80029296875, 5.156933593749997],\n                        [35.74501953125002, 5.343994140625],\n                        [35.325292968750006, 5.364892578124994],\n                        [35.2646484375, 5.412060546874997],\n                        [35.26386718750001, 5.457910156249994],\n                        [35.26835937500002, 5.492285156249991],\n                        [34.98359375000001, 5.858300781249994],\n                        [34.71064453125001, 6.660302734374994],\n                        [34.06425781250002, 7.225732421874994],\n                        [33.902441406250006, 7.509521484375],\n                        [32.99892578125002, 7.899511718749991],\n                        [33.28105468750002, 8.437255859375],\n                        [33.95332031250001, 8.443505859374994],\n                        [34.07275390625, 8.545263671874991],\n                        [34.078125, 9.461523437499991],\n                        [34.31123046875001, 10.190869140624997],\n                        [34.34394531250001, 10.658642578124997],\n                        [34.571875, 10.880175781249989],\n                        [34.77128906250002, 10.746191406249991],\n                        [34.93144531250002, 10.864794921874989],\n                        [35.1123046875, 11.816552734374994],\n                        [35.67021484375002, 12.623730468749997],\n                        [36.12519531250001, 12.75703125],\n                        [36.52431640625002, 14.2568359375],\n                        [36.54238281250002, 14.25820312499999],\n                        [36.67910156250002, 14.307568359374997],\n                        [36.81191406250002, 14.315039062499991],\n                        [37.024511718750006, 14.27197265625],\n                        [37.25722656250002, 14.453759765624994],\n                        [37.50722656250002, 14.156396484374994],\n                        [37.54677734375002, 14.143847656250003],\n                        [37.57119140625002, 14.149072265624994],\n                        [37.6484375, 14.322558593750003],\n                        [37.70839843750002, 14.457226562499997],\n                        [37.8203125, 14.70849609375],\n                        [37.88417968750002, 14.852294921875],\n                        [37.943457031250006, 14.810546875],\n                        [38.002539062500006, 14.737109375],\n                        [38.22148437500002, 14.649658203125],\n                        [38.43144531250002, 14.428613281249994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Ethiopia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [22.17509765624999, 60.370751953124994],\n                            [22.41552734375003, 60.30336914062505],\n                            [22.36054687500004, 60.165576171875045],\n                            [22.07714843750003, 60.286328124999955],\n                            [22.17509765624999, 60.370751953124994]\n                        ]\n                    ],\n                    [\n                        [\n                            [21.450878906250068, 60.529589843750045],\n                            [21.3, 60.47978515625002],\n                            [21.224707031250006, 60.62060546875003],\n                            [21.450878906250068, 60.529589843750045]\n                        ]\n                    ],\n                    [\n                        [\n                            [21.2177734375, 63.241308593750034],\n                            [21.415625, 63.19736328125006],\n                            [21.25341796875, 63.152001953124966],\n                            [21.08388671875008, 63.277539062499955],\n                            [21.2177734375, 63.241308593750034]\n                        ]\n                    ],\n                    [\n                        [\n                            [24.848242187500034, 64.99101562499999],\n                            [24.576562500000023, 65.04287109375],\n                            [24.970605468750023, 65.05532226562502],\n                            [24.848242187500034, 64.99101562499999]\n                        ]\n                    ],\n                    [\n                        [\n                            [28.96582031250003, 69.02197265625],\n                            [28.414062500000057, 68.90415039062506],\n                            [28.77285156250005, 68.84003906249995],\n                            [28.470703125000057, 68.48837890625],\n                            [28.685156250000034, 68.189794921875],\n                            [29.343847656250006, 68.06186523437506],\n                            [29.988085937500017, 67.66826171874999],\n                            [29.066210937500045, 66.89174804687497],\n                            [30.102734375000097, 65.72626953125004],\n                            [29.715917968750063, 65.62456054687502],\n                            [29.608007812500006, 65.248681640625],\n                            [29.826953125000017, 65.14506835937502],\n                            [29.60419921875004, 64.968408203125],\n                            [30.072851562500063, 64.76503906250005],\n                            [30.04189453125005, 64.44335937499997],\n                            [30.513769531250006, 64.2],\n                            [30.50390625000003, 64.02060546875],\n                            [29.991503906250074, 63.73515625000002],\n                            [31.180859375000097, 63.208300781250074],\n                            [31.533984375000017, 62.885400390624994],\n                            [31.18671875000004, 62.48139648437504],\n                            [29.69013671875004, 61.54609375000001],\n                            [27.797656250000074, 60.53613281250003],\n                            [26.53466796874997, 60.412890625000074],\n                            [26.56933593750003, 60.62456054687502],\n                            [26.377734375000074, 60.42407226562503],\n                            [25.955957031250023, 60.474218750000034],\n                            [26.03583984375004, 60.34150390625001],\n                            [25.75800781250004, 60.26752929687504],\n                            [25.65644531250004, 60.33320312499998],\n                            [24.44560546874999, 60.021289062500045],\n                            [23.46357421875004, 59.986230468749994],\n                            [23.021289062500074, 59.81601562500006],\n                            [23.19843750000001, 60.02182617187498],\n                            [22.911718750000063, 60.20971679687497],\n                            [22.749804687500017, 60.057275390624994],\n                            [22.462695312500045, 60.029199218749966],\n                            [22.5849609375, 60.380566406249955],\n                            [21.436035156250057, 60.596386718749955],\n                            [21.605957031250057, 61.59155273437503],\n                            [21.255957031250063, 61.98964843750005],\n                            [21.143847656250045, 62.73999023437506],\n                            [21.650976562500063, 63.039306640625],\n                            [21.545117187499983, 63.204296874999955],\n                            [22.31972656250005, 63.310449218749994],\n                            [22.532324218750034, 63.647851562499994],\n                            [23.598925781250074, 64.04091796874997],\n                            [24.557910156250045, 64.801025390625],\n                            [25.288183593750063, 64.8603515625],\n                            [25.34785156250004, 65.47924804687497],\n                            [24.674902343750006, 65.67070312499999],\n                            [24.628027343750034, 65.85917968750002],\n                            [24.15546875000004, 65.80527343750006],\n                            [23.700292968750034, 66.25263671874998],\n                            [23.988574218750045, 66.81054687500003],\n                            [23.64150390625005, 67.12939453124997],\n                            [23.733593750000068, 67.42290039062499],\n                            [23.454882812500045, 67.46025390625007],\n                            [23.63886718750004, 67.95439453125002],\n                            [22.854101562500034, 68.36733398437502],\n                            [21.99746093750005, 68.52060546874998],\n                            [20.622167968750006, 69.036865234375],\n                            [21.065722656250017, 69.04174804687503],\n                            [21.06611328125001, 69.21411132812497],\n                            [21.59375, 69.273583984375],\n                            [22.410937500000074, 68.719873046875],\n                            [23.324023437500017, 68.64897460937502],\n                            [23.85400390625, 68.80590820312503],\n                            [24.94140625000003, 68.59326171875006],\n                            [25.748339843750017, 68.99013671875],\n                            [26.07246093750004, 69.69155273437497],\n                            [26.525390625000057, 69.91503906250003],\n                            [27.127539062500063, 69.90649414062497],\n                            [27.747851562500045, 70.06484375],\n                            [29.14160156250003, 69.67143554687505],\n                            [29.33339843750005, 69.47299804687503],\n                            [28.846289062500006, 69.17690429687502],\n                            [28.96582031250003, 69.02197265625]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Finland\", \"childNum\": 5 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [178.48789062500018, -18.97412109375],\n                            [177.95869140624998, -19.121582031250014],\n                            [178.33427734375013, -18.93447265625001],\n                            [178.48789062500018, -18.97412109375]\n                        ]\n                    ],\n                    [\n                        [\n                            [179.34931640625015, -18.10234375000003],\n                            [179.25351562500018, -18.030566406249974],\n                            [179.30644531250013, -17.944042968750026],\n                            [179.34931640625015, -18.10234375000003]\n                        ]\n                    ],\n                    [\n                        [\n                            [178.28017578124994, -17.37197265625001],\n                            [178.59160156249996, -17.651464843750006],\n                            [178.66767578125004, -18.080859375],\n                            [177.95546875000005, -18.264062500000023],\n                            [177.32138671875, -18.077539062500037],\n                            [177.26396484375007, -17.86347656250004],\n                            [177.5044921875, -17.539550781250043],\n                            [177.81796875000012, -17.38847656249999],\n                            [178.28017578124994, -17.37197265625001]\n                        ]\n                    ],\n                    [\n                        [\n                            [180, -16.96308593750001],\n                            [179.89697265625003, -16.96406250000004],\n                            [180, -16.785742187500034],\n                            [180, -16.96308593750001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-179.97490234374996, -16.92480468750003],\n                            [-180, -16.96298828124999],\n                            [-180, -16.907812500000034],\n                            [-180, -16.82431640624999],\n                            [-180, -16.78554687499999],\n                            [-179.86098632812502, -16.68828124999999],\n                            [-179.97490234374996, -16.92480468750003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-179.92944335937503, -16.502832031250037],\n                            [-179.999951171875, -16.540039062499986],\n                            [-179.900927734375, -16.431542968749994],\n                            [-179.92944335937503, -16.502832031250037]\n                        ]\n                    ],\n                    [\n                        [\n                            [179.99921875000004, -16.168554687499977],\n                            [179.56416015625004, -16.636914062499997],\n                            [179.56816406249996, -16.747460937499966],\n                            [179.93037109375004, -16.51943359375005],\n                            [179.9279296875001, -16.74443359374996],\n                            [179.41933593750005, -16.80654296875001],\n                            [179.20234375000004, -16.71269531249999],\n                            [179.00683593750003, -16.90019531249999],\n                            [178.70664062500018, -16.97617187500002],\n                            [178.4974609375, -16.78789062500003],\n                            [178.58359375000012, -16.621875],\n                            [178.80507812499994, -16.631445312500034],\n                            [179.55175781250003, -16.249902343750023],\n                            [180, -16.15292968749999],\n                            [179.99921875000004, -16.168554687499977]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Fiji\", \"childNum\": 7 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-61.01875, -51.7857421875],\n                            [-60.87597656250003, -51.79423828125004],\n                            [-60.94755859374996, -51.94628906250002],\n                            [-61.14501953125003, -51.83945312500001],\n                            [-61.01875, -51.7857421875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-60.28623046874995, -51.461914062500014],\n                            [-59.38759765625002, -51.35996093750003],\n                            [-59.26806640625, -51.42753906250003],\n                            [-59.92138671874997, -51.969531250000045],\n                            [-60.246337890625, -51.98642578125003],\n                            [-60.35346679687498, -52.13994140625004],\n                            [-60.686376953125034, -52.18837890624996],\n                            [-60.96142578125003, -52.05732421874999],\n                            [-60.23847656249998, -51.771972656250036],\n                            [-60.58251953125, -51.71269531250004],\n                            [-60.24516601562493, -51.638867187500004],\n                            [-60.56845703124998, -51.357812499999945],\n                            [-60.28623046874995, -51.461914062500014]\n                        ]\n                    ],\n                    [\n                        [\n                            [-60.11171875000002, -51.39589843749998],\n                            [-60.275341796874955, -51.28056640625002],\n                            [-60.06982421875, -51.307910156249996],\n                            [-60.11171875000002, -51.39589843749998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-58.85019531249995, -51.26992187499998],\n                            [-58.42583007812502, -51.32421875000003],\n                            [-58.508935546874994, -51.48359375],\n                            [-58.271582031250034, -51.57470703124999],\n                            [-58.25922851562501, -51.417089843750034],\n                            [-57.976513671874955, -51.384375],\n                            [-57.80849609375002, -51.51796875],\n                            [-57.96044921874997, -51.58320312500003],\n                            [-57.79179687499999, -51.63613281249998],\n                            [-58.68349609375002, -51.93623046875001],\n                            [-58.65278320312498, -52.09921875],\n                            [-59.19584960937496, -52.01767578125],\n                            [-59.06801757812502, -52.17304687500003],\n                            [-59.341503906249955, -52.19599609375],\n                            [-59.395654296874966, -52.308007812499994],\n                            [-59.64873046875002, -52.134375],\n                            [-59.57080078124994, -51.92539062500003],\n                            [-59.05952148437498, -51.685449218749994],\n                            [-59.09663085937498, -51.49140624999998],\n                            [-58.85019531249995, -51.26992187499998]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Falkland Is.\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [55.79736328125003, -21.33935546875003],\n                            [55.36269531250005, -21.27363281250004],\n                            [55.23281250000005, -21.05839843749999],\n                            [55.311328125000074, -20.90410156249999],\n                            [55.661914062500074, -20.90625],\n                            [55.8390625000001, -21.13857421874998],\n                            [55.79736328125003, -21.33935546875003]\n                        ]\n                    ],\n                    [\n                        [\n                            [45.180273437500006, -12.97675781250004],\n                            [45.069433593750006, -12.895605468750034],\n                            [45.09238281250006, -12.653027343749997],\n                            [45.22314453124997, -12.752148437500026],\n                            [45.180273437500006, -12.97675781250004]\n                        ]\n                    ],\n                    [\n                        [\n                            [-51.65253906249998, 4.061279296874972],\n                            [-52.327880859375, 3.18173828125002],\n                            [-52.58300781250003, 2.528906249999977],\n                            [-52.90346679687502, 2.211523437499977],\n                            [-53.76777343749998, 2.354833984375048],\n                            [-54.13007812499998, 2.121044921875026],\n                            [-54.43310546875, 2.207519531250057],\n                            [-54.51508789062498, 2.245458984374963],\n                            [-54.55048828125001, 2.293066406249991],\n                            [-54.59194335937502, 2.313769531250031],\n                            [-54.61625976562499, 2.326757812500006],\n                            [-54.60473632812497, 2.335791015624991],\n                            [-54.56840820312502, 2.342578125000031],\n                            [-54.53593749999999, 2.343310546875003],\n                            [-54.48554687500001, 2.416113281250006],\n                            [-54.402001953124966, 2.46152343750002],\n                            [-54.25673828125002, 2.713720703124977],\n                            [-54.19550781249998, 2.817871093750057],\n                            [-54.17070312499999, 2.993603515624969],\n                            [-54.203125, 3.138183593750028],\n                            [-54.18803710937499, 3.178759765625031],\n                            [-54.063183593749955, 3.353320312499989],\n                            [-54.00957031249993, 3.448535156250017],\n                            [-54.03422851562499, 3.62939453125],\n                            [-54.350732421874994, 4.054101562500023],\n                            [-54.47968749999998, 4.836523437499991],\n                            [-53.91992187499997, 5.768994140624983],\n                            [-52.899316406249966, 5.425048828124986],\n                            [-52.29052734375003, 4.942187500000031],\n                            [-52.324609374999966, 4.770898437500037],\n                            [-52.21997070312494, 4.862792968750014],\n                            [-52.05810546875003, 4.717382812499963],\n                            [-52.00292968749997, 4.352294921875014],\n                            [-51.82753906250002, 4.635693359375026],\n                            [-51.65253906249998, 4.061279296874972]\n                        ]\n                    ],\n                    [\n                        [\n                            [-60.826269531250006, 14.494482421874991],\n                            [-61.063720703125, 14.467089843750017],\n                            [-61.01132812499998, 14.601904296875034],\n                            [-61.21333007812501, 14.848583984375011],\n                            [-60.927148437499966, 14.755175781249989],\n                            [-60.826269531250006, 14.494482421874991]\n                        ]\n                    ],\n                    [\n                        [\n                            [-61.23046875000003, 15.889941406250074],\n                            [-61.310742187499955, 15.894677734374966],\n                            [-61.25, 16.006298828124983],\n                            [-61.23046875000003, 15.889941406250074]\n                        ]\n                    ],\n                    [\n                        [\n                            [-61.58955078125001, 16.006933593750006],\n                            [-61.759423828124966, 16.062060546875045],\n                            [-61.74804687499997, 16.355273437500017],\n                            [-61.55234374999998, 16.270898437499966],\n                            [-61.58955078125001, 16.006933593750006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-61.3271484375, 16.230419921874983],\n                            [-61.522167968749955, 16.22802734375003],\n                            [-61.47119140624994, 16.506640625000045],\n                            [-61.17260742187497, 16.25610351562497],\n                            [-61.3271484375, 16.230419921874983]\n                        ]\n                    ],\n                    [\n                        [\n                            [9.480371093750023, 42.80541992187503],\n                            [9.550683593750051, 42.12973632812506],\n                            [9.186132812500034, 41.38491210937502],\n                            [8.80751953125008, 41.58837890625],\n                            [8.886816406249977, 41.70068359375003],\n                            [8.621875, 41.93071289062502],\n                            [8.700976562500045, 42.09560546875002],\n                            [8.565625, 42.35771484374996],\n                            [8.81484375000008, 42.60791015625003],\n                            [9.313378906250023, 42.71318359374999],\n                            [9.363183593750051, 43.01738281249996],\n                            [9.480371093750023, 42.80541992187503]\n                        ]\n                    ],\n                    [\n                        [\n                            [-1.17832031249992, 45.904052734375],\n                            [-1.213574218750011, 45.81660156250004],\n                            [-1.388671874999972, 46.05039062500006],\n                            [-1.17832031249992, 45.904052734375]\n                        ]\n                    ],\n                    [\n                        [\n                            [5.789746093749983, 49.53828125000001],\n                            [5.823437500000011, 49.50507812499998],\n                            [5.9013671875, 49.48974609374997],\n                            [5.928906250000011, 49.47753906249997],\n                            [5.959472656250028, 49.45463867187502],\n                            [6.01142578125004, 49.44545898437502],\n                            [6.074121093750023, 49.45463867187502],\n                            [6.119921875000017, 49.485205078125034],\n                            [6.181054687500051, 49.498925781249966],\n                            [6.344335937500006, 49.45273437499998],\n                            [6.735449218750006, 49.16059570312498],\n                            [7.450585937500051, 49.152197265625034],\n                            [8.134863281250006, 48.97358398437498],\n                            [7.616601562500023, 48.15678710937502],\n                            [7.615625, 47.59272460937504],\n                            [7.343164062499994, 47.43310546875003],\n                            [7.136035156249989, 47.489843750000034],\n                            [6.968359375000034, 47.453222656250034],\n                            [6.900390625000028, 47.39423828125001],\n                            [7.000585937500034, 47.339453125000034],\n                            [7.000585937500034, 47.32250976562506],\n                            [6.978515625000057, 47.30205078124996],\n                            [6.95205078125008, 47.26718750000006],\n                            [6.820703125000051, 47.163183593750006],\n                            [6.688085937500034, 47.05825195312505],\n                            [6.66689453125008, 47.026513671874966],\n                            [6.624804687500017, 47.00434570312498],\n                            [6.45625, 46.948339843750034],\n                            [6.438646763392874, 46.774418247767855],\n                            [6.129687500000045, 46.56699218750006],\n                            [6.118111049107182, 46.447459542410726],\n                            [6.095898437500011, 46.279394531250006],\n                            [5.970019531250045, 46.214697265625034],\n                            [5.971484375000074, 46.151220703125006],\n                            [6.006640625000045, 46.14233398437506],\n                            [6.086621093750068, 46.14702148437502],\n                            [6.19941406250004, 46.19306640624998],\n                            [6.234667968750045, 46.332617187500006],\n                            [6.321875, 46.39370117187502],\n                            [6.428906250000011, 46.43051757812506],\n                            [6.578222656250034, 46.437353515625034],\n                            [6.758105468750017, 46.41577148437497],\n                            [6.772070312500006, 46.16513671874998],\n                            [6.897265625000017, 46.05175781249997],\n                            [6.953710937500063, 46.017138671875045],\n                            [7.00390625, 45.95883789062506],\n                            [7.021093750000034, 45.92578124999997],\n                            [6.790917968750023, 45.740869140624966],\n                            [7.146386718750051, 45.381738281249994],\n                            [7.07832031250004, 45.23994140624998],\n                            [6.634765625000028, 45.06816406249996],\n                            [6.99267578125, 44.82729492187502],\n                            [6.900195312499989, 44.33574218749996],\n                            [7.318554687500068, 44.13798828125002],\n                            [7.637207031250057, 44.16484375],\n                            [7.4931640625, 43.767138671875045],\n                            [6.570214843750023, 43.199072265625034],\n                            [6.115917968750011, 43.07236328124998],\n                            [5.406542968750074, 43.228515625],\n                            [5.05976562500004, 43.44453125000004],\n                            [4.712109375000011, 43.373291015625],\n                            [3.910839843750011, 43.563085937500034],\n                            [3.258886718750063, 43.193212890625006],\n                            [3.051757812500057, 42.915136718750006],\n                            [3.21142578125, 42.43115234375],\n                            [2.032714843750028, 42.353515625],\n                            [1.927929687500068, 42.42631835937499],\n                            [1.859765625000051, 42.457080078125045],\n                            [1.7060546875, 42.50332031250005],\n                            [1.709863281250051, 42.604443359374955],\n                            [1.568164062500045, 42.63500976562506],\n                            [1.501367187500023, 42.64272460937502],\n                            [1.428320312499977, 42.59589843749998],\n                            [0.696875, 42.84511718750005],\n                            [0.631640625000045, 42.689599609374994],\n                            [-0.586425781249943, 42.798974609374966],\n                            [-1.300048828124943, 43.10097656250002],\n                            [-1.460839843749937, 43.05175781250006],\n                            [-1.410693359374932, 43.240087890625034],\n                            [-1.794042968749949, 43.407324218750006],\n                            [-1.484863281249943, 43.56376953124999],\n                            [-1.245507812499937, 44.55986328124999],\n                            [-1.07695312499996, 44.68984375],\n                            [-1.152880859374989, 44.764013671875006],\n                            [-1.245214843749977, 44.66669921874998],\n                            [-1.081005859374983, 45.532421874999955],\n                            [-0.548486328124966, 45.00058593750006],\n                            [-0.790771484375028, 45.46801757812497],\n                            [-1.195996093749983, 45.714453125],\n                            [-1.03173828125, 45.741064453125006],\n                            [-1.14628906249996, 46.311376953125034],\n                            [-1.786523437499937, 46.51484375000001],\n                            [-2.059375, 46.81030273437497],\n                            [-2.01889648437492, 47.03764648437502],\n                            [-2.197070312499989, 47.16293945312506],\n                            [-2.027587890625028, 47.27358398437502],\n                            [-1.742529296874949, 47.21596679687502],\n                            [-1.97539062499996, 47.31069335937505],\n                            [-2.503125, 47.31206054687496],\n                            [-2.427685546874983, 47.47089843749998],\n                            [-2.770312499999989, 47.513867187499955],\n                            [-2.787207031249949, 47.62553710937496],\n                            [-4.312109374999949, 47.82290039062502],\n                            [-4.678808593749949, 48.03950195312501],\n                            [-4.32944335937492, 48.169970703125045],\n                            [-4.577148437499943, 48.2900390625],\n                            [-4.241406249999926, 48.30366210937501],\n                            [-4.719384765624966, 48.363134765625034],\n                            [-4.7625, 48.45024414062502],\n                            [-4.531201171874983, 48.61997070312506],\n                            [-3.231445312499972, 48.84082031250003],\n                            [-2.692333984374983, 48.53681640624998],\n                            [-2.446191406249937, 48.64829101562506],\n                            [-2.00371093749996, 48.58208007812499],\n                            [-1.905712890624955, 48.69711914062506],\n                            [-1.376464843749972, 48.65258789062503],\n                            [-1.565478515624932, 48.805517578125034],\n                            [-1.583105468749977, 49.20239257812506],\n                            [-1.856445312499972, 49.68378906249998],\n                            [-1.258642578124949, 49.68017578125006],\n                            [-1.138525390624977, 49.38789062500001],\n                            [-0.163476562499937, 49.296777343749994],\n                            [0.41689453125008, 49.448388671874994],\n                            [0.129394531250028, 49.508447265624966],\n                            [0.186718749999983, 49.703027343749994],\n                            [1.245507812500051, 49.99824218750001],\n                            [1.5927734375, 50.25219726562506],\n                            [1.672265625000023, 50.885009765625],\n                            [2.52490234375, 51.097119140624955],\n                            [2.759375, 50.750634765624994],\n                            [3.10683593750008, 50.779443359374994],\n                            [3.27333984375008, 50.53154296875002],\n                            [3.595410156250068, 50.47734374999999],\n                            [3.689355468750023, 50.30605468750002],\n                            [4.174609375000017, 50.24648437500005],\n                            [4.149316406250023, 49.971582031249994],\n                            [4.545019531250063, 49.96025390624999],\n                            [4.818652343750045, 50.153173828125034],\n                            [4.867578125000051, 49.78813476562502],\n                            [5.50732421875, 49.51088867187502],\n                            [5.789746093749983, 49.53828125000001]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"France\", \"childNum\": 10, \"cp\": [2.8719426, 46.8222422] }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-7.186865234374949, 62.139306640624966],\n                            [-7.116796874999977, 62.046826171874955],\n                            [-7.379101562499926, 62.07480468749998],\n                            [-7.186865234374949, 62.139306640624966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-6.631054687499955, 62.22788085937498],\n                            [-6.655810546874932, 62.09360351562498],\n                            [-6.840527343749983, 62.119287109374994],\n                            [-6.725195312499949, 61.95146484374999],\n                            [-7.17216796874996, 62.28559570312501],\n                            [-6.631054687499955, 62.22788085937498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-6.406054687499932, 62.258642578125034],\n                            [-6.544140624999926, 62.20561523437499],\n                            [-6.554589843749994, 62.35566406250001],\n                            [-6.406054687499932, 62.258642578125034]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Faeroe Is.\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [158.31484375, 6.813671875],\n                            [158.18339843750002, 6.801269531250057],\n                            [158.13476562499997, 6.944824218749986],\n                            [158.29462890625004, 6.951074218750023],\n                            [158.31484375, 6.813671875]\n                        ]\n                    ],\n                    [\n                        [\n                            [138.14267578125006, 9.50068359375004],\n                            [138.06708984375004, 9.419042968750006],\n                            [138.18583984375007, 9.593310546874989],\n                            [138.14267578125006, 9.50068359375004]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Micronesia\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [13.293554687500006, 2.161572265624997],\n                        [13.172167968750017, 1.78857421875],\n                        [13.21630859375, 1.2484375],\n                        [13.851367187500017, 1.41875],\n                        [14.180859375000011, 1.370214843749991],\n                        [14.429882812500011, 0.901464843749991],\n                        [14.32421875, 0.62421875],\n                        [13.949609375000023, 0.353808593749989],\n                        [13.860058593750011, -0.203320312500011],\n                        [14.47412109375, -0.573437500000011],\n                        [14.383984375000011, -1.890039062500009],\n                        [14.162890625000017, -2.217578125],\n                        [14.199804687500006, -2.354199218750011],\n                        [13.993847656250011, -2.490625],\n                        [13.886914062500011, -2.465429687500006],\n                        [13.733789062500023, -2.138476562500003],\n                        [13.464941406250006, -2.395410156250009],\n                        [12.991992187500017, -2.313378906250009],\n                        [12.793554687500006, -1.931835937500011],\n                        [12.590429687500006, -1.826855468750011],\n                        [12.43212890625, -1.928906250000011],\n                        [12.446386718750006, -2.329980468750009],\n                        [12.064453125, -2.41259765625],\n                        [11.60546875, -2.342578125],\n                        [11.537792968750011, -2.83671875],\n                        [11.760156250000023, -2.983105468750011],\n                        [11.715429687500006, -3.176953125000011],\n                        [11.934179687500006, -3.318554687500011],\n                        [11.8798828125, -3.665917968750009],\n                        [11.685742187500011, -3.68203125],\n                        [11.504296875000023, -3.5203125],\n                        [11.234472656250006, -3.690820312500009],\n                        [11.130175781250017, -3.916308593750003],\n                        [10.34765625, -3.013085937500009],\n                        [9.722070312500023, -2.467578125],\n                        [10.06201171875, -2.549902343750006],\n                        [9.624609375, -2.367089843750009],\n                        [9.298925781250006, -1.903027343750011],\n                        [9.483203125000017, -1.894628906250006],\n                        [9.265625, -1.825097656250009],\n                        [9.036328125000011, -1.308886718750003],\n                        [9.31884765625, -1.632031250000011],\n                        [9.501074218750006, -1.55517578125],\n                        [9.295800781250023, -1.515234375],\n                        [9.3466796875, -1.325],\n                        [9.203808593750011, -1.382421875],\n                        [9.064648437500011, -1.29833984375],\n                        [8.703125, -0.591015625000011],\n                        [8.946386718750006, -0.688769531250003],\n                        [9.296679687500017, -0.351269531250011],\n                        [9.354882812500023, 0.343603515624991],\n                        [9.468164062500023, 0.15976562499999],\n                        [9.796777343750023, 0.044238281249989],\n                        [10.00146484375, 0.194970703124994],\n                        [9.546484375, 0.295947265624989],\n                        [9.324804687500006, 0.552099609374991],\n                        [9.495312500000011, 0.664843749999989],\n                        [9.617968750000017, 0.576513671874991],\n                        [9.5908203125, 1.031982421875],\n                        [9.636132812500023, 1.046679687499989],\n                        [9.676464843750011, 1.07470703125],\n                        [9.70458984375, 1.079980468749994],\n                        [9.760546875000017, 1.07470703125],\n                        [9.788671875, 1.025683593749989],\n                        [9.803906250000011, 0.998730468749997],\n                        [9.90673828125, 0.960107421874994],\n                        [11.335351562500023, 0.999707031249997],\n                        [11.332324218750017, 1.528369140624989],\n                        [11.328710937500006, 2.167431640624997],\n                        [11.348437500000017, 2.299707031249994],\n                        [11.558984375000023, 2.302197265624997],\n                        [13.2203125, 2.256445312499991],\n                        [13.293554687500006, 2.161572265624997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Gabon\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-1.065576171874966, 50.69023437500002],\n                            [-1.25146484375, 50.58881835937498],\n                            [-1.563427734374955, 50.666113281250006],\n                            [-1.31279296874996, 50.77348632812502],\n                            [-1.065576171874966, 50.69023437500002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-4.196777343749972, 53.321435546874966],\n                            [-4.04936523437496, 53.30576171874998],\n                            [-4.373046875, 53.13417968750002],\n                            [-4.56787109375, 53.386474609375],\n                            [-4.315087890625023, 53.41723632812503],\n                            [-4.196777343749972, 53.321435546874966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-6.218017578125, 54.08872070312506],\n                            [-6.649804687499937, 54.05864257812496],\n                            [-7.007714843749937, 54.40668945312501],\n                            [-7.324511718750017, 54.13344726562502],\n                            [-7.606542968750006, 54.14384765625002],\n                            [-8.118261718749977, 54.41425781250004],\n                            [-7.75439453125, 54.59492187499998],\n                            [-7.910595703124955, 54.698339843750006],\n                            [-7.55039062499992, 54.767968749999966],\n                            [-7.218652343749937, 55.09199218749998],\n                            [-6.475048828124955, 55.24101562499999],\n                            [-6.035791015624994, 55.14453125000003],\n                            [-5.71684570312496, 54.817480468750034],\n                            [-5.878613281249955, 54.64130859375001],\n                            [-5.582519531249943, 54.66342773437498],\n                            [-5.470410156249926, 54.500195312499955],\n                            [-5.671093749999955, 54.54975585937501],\n                            [-5.60678710937492, 54.272558593750034],\n                            [-6.019042968749972, 54.05126953124997],\n                            [-6.218017578125, 54.08872070312506]\n                        ]\n                    ],\n                    [\n                        [\n                            [-5.105419921875011, 55.448828125000034],\n                            [-5.331494140624955, 55.481054687500034],\n                            [-5.318115234375, 55.709179687499955],\n                            [-5.105419921875011, 55.448828125000034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-6.128906249999972, 55.93056640625002],\n                            [-6.055322265624994, 55.69531249999997],\n                            [-6.305078124999966, 55.60693359375],\n                            [-6.286425781249989, 55.77250976562499],\n                            [-6.491357421874994, 55.697314453125045],\n                            [-6.462841796874955, 55.808251953124994],\n                            [-6.128906249999972, 55.93056640625002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-5.970068359374949, 55.814550781250034],\n                            [-6.071972656250011, 55.893115234375045],\n                            [-5.72514648437496, 56.118554687499966],\n                            [-5.970068359374949, 55.814550781250034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-5.77788085937496, 56.344335937500034],\n                            [-6.313427734374983, 56.29365234375001],\n                            [-6.138867187499955, 56.490625],\n                            [-6.286328124999983, 56.61186523437502],\n                            [-6.102734374999955, 56.645654296874966],\n                            [-5.760839843749949, 56.49067382812501],\n                            [-5.77788085937496, 56.344335937500034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-7.249853515624977, 57.115332031250006],\n                            [-7.410546874999937, 57.38110351562506],\n                            [-7.26713867187496, 57.37177734375001],\n                            [-7.249853515624977, 57.115332031250006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-6.144726562499983, 57.50498046874998],\n                            [-6.135546874999989, 57.31425781250002],\n                            [-5.672460937499977, 57.252685546875],\n                            [-5.94907226562492, 57.045166015625],\n                            [-6.034375, 57.20122070312499],\n                            [-6.322705078124926, 57.20249023437498],\n                            [-6.761132812499994, 57.4423828125],\n                            [-6.305957031249989, 57.67197265624998],\n                            [-6.144726562499983, 57.50498046874998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-7.205566406250028, 57.682958984375006],\n                            [-7.182617187499972, 57.53330078125006],\n                            [-7.514746093749949, 57.60195312500002],\n                            [-7.205566406250028, 57.682958984375006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-6.198681640624983, 58.36328125000003],\n                            [-6.554589843749994, 58.092871093750006],\n                            [-6.425195312499937, 58.02128906249999],\n                            [-6.983105468749983, 57.75],\n                            [-7.083447265624926, 57.81376953124999],\n                            [-6.856835937499937, 57.92353515624998],\n                            [-7.085253906249932, 58.18217773437499],\n                            [-6.726464843749937, 58.189404296874955],\n                            [-6.776464843750006, 58.30151367187497],\n                            [-6.237451171874966, 58.50283203125005],\n                            [-6.198681640624983, 58.36328125000003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-3.109667968749932, 58.515478515625034],\n                            [-3.212353515624983, 58.32124023437501],\n                            [-3.99003906249996, 57.95903320312502],\n                            [-4.035595703124926, 57.85200195312498],\n                            [-3.857128906249983, 57.81855468750001],\n                            [-4.134521484375, 57.57773437500006],\n                            [-3.402783203124955, 57.708251953125],\n                            [-2.074072265624977, 57.70239257812506],\n                            [-1.780664062499994, 57.474023437499966],\n                            [-2.592675781249937, 56.56157226562499],\n                            [-3.309960937499966, 56.36347656250004],\n                            [-2.885156249999937, 56.397509765625045],\n                            [-2.674267578124955, 56.25341796875],\n                            [-3.362255859374955, 56.02763671875002],\n                            [-3.789062499999972, 56.09521484375],\n                            [-3.048730468749937, 55.951953125000045],\n                            [-2.599316406249955, 56.02729492187501],\n                            [-2.14707031249992, 55.90297851562502],\n                            [-1.655371093749949, 55.57036132812502],\n                            [-1.232421874999943, 54.703710937500034],\n                            [-0.084375, 54.118066406249994],\n                            [-0.20556640625, 54.021728515625],\n                            [0.115332031250006, 53.609277343749994],\n                            [-0.270019531249972, 53.73676757812504],\n                            [-0.659912109375, 53.72402343750002],\n                            [-0.293701171875, 53.69233398437504],\n                            [0.270996093750028, 53.33549804687499],\n                            [0.355761718750045, 53.15996093750002],\n                            [0.0458984375, 52.90561523437498],\n                            [0.279785156250028, 52.80869140625006],\n                            [0.55878906250004, 52.96694335937505],\n                            [1.05556640625008, 52.95898437500003],\n                            [1.656738281249972, 52.753710937500045],\n                            [1.74658203125, 52.46899414062503],\n                            [1.59140625, 52.11977539062502],\n                            [1.232421875000057, 51.97124023437496],\n                            [1.188476562500057, 51.803369140624966],\n                            [0.752246093750017, 51.729589843750034],\n                            [0.890917968750017, 51.571435546874966],\n                            [0.42451171875004, 51.465625],\n                            [1.414941406250023, 51.36328125],\n                            [1.397558593750034, 51.18203125000002],\n                            [0.960156250000011, 50.92587890624998],\n                            [0.299707031249994, 50.775976562500006],\n                            [-0.785253906249949, 50.76542968749999],\n                            [-1.416455078124955, 50.896875],\n                            [-1.334472656249943, 50.82080078124997],\n                            [-1.516748046874937, 50.747460937499966],\n                            [-2.031054687499932, 50.72539062499999],\n                            [-2.035839843749926, 50.603076171875045],\n                            [-2.999414062499937, 50.71660156249999],\n                            [-3.40458984374996, 50.63242187499998],\n                            [-3.679785156250006, 50.239941406249955],\n                            [-4.194580078124972, 50.39331054687503],\n                            [-4.727978515624926, 50.29047851562504],\n                            [-5.11850585937492, 50.038330078125],\n                            [-5.622119140624932, 50.05068359375002],\n                            [-4.188183593749926, 51.18852539062502],\n                            [-3.135986328124972, 51.20502929687501],\n                            [-2.433056640624926, 51.74072265625],\n                            [-3.293115234374994, 51.390429687500045],\n                            [-3.890771484374994, 51.591650390625006],\n                            [-4.234570312499955, 51.56909179687503],\n                            [-4.091015624999926, 51.65991210937506],\n                            [-4.38627929687496, 51.74106445312506],\n                            [-4.902294921874926, 51.626269531250045],\n                            [-5.168359374999937, 51.74072265625],\n                            [-5.183349609374972, 51.94965820312501],\n                            [-4.217724609374983, 52.277441406250006],\n                            [-3.980322265624949, 52.54174804687503],\n                            [-4.101464843750023, 52.915478515624955],\n                            [-4.683056640624926, 52.80615234374997],\n                            [-4.268554687499943, 53.14453125],\n                            [-3.427734374999972, 53.34067382812498],\n                            [-3.097558593749937, 53.260302734375045],\n                            [-3.064746093749932, 53.426855468750034],\n                            [-2.74951171875, 53.310205078124994],\n                            [-3.064599609374994, 53.512841796874966],\n                            [-2.84648437499996, 54.135302734375045],\n                            [-3.165966796874955, 54.12792968750006],\n                            [-3.56938476562496, 54.46757812499996],\n                            [-3.464599609374943, 54.77309570312505],\n                            [-3.036230468749977, 54.95307617187501],\n                            [-3.550439453124937, 54.94741210937502],\n                            [-3.957910156249994, 54.780957031249955],\n                            [-4.818066406249983, 54.84614257812501],\n                            [-4.911230468749949, 54.68945312500006],\n                            [-5.032324218749949, 54.76137695312505],\n                            [-5.172705078124949, 54.98588867187496],\n                            [-4.676757812499972, 55.50131835937498],\n                            [-4.871679687499977, 55.87392578125005],\n                            [-4.58408203124992, 55.93867187500001],\n                            [-4.844091796874949, 56.05117187499999],\n                            [-4.80029296875, 56.158349609374994],\n                            [-5.228222656249983, 55.886328125],\n                            [-5.084326171874977, 56.197460937499955],\n                            [-5.41044921874996, 55.995361328125],\n                            [-5.55644531249996, 55.389599609374955],\n                            [-5.730664062499926, 55.33413085937502],\n                            [-5.504492187499949, 55.80239257812502],\n                            [-5.609570312499955, 56.055273437500034],\n                            [-5.188378906249937, 56.75805664062503],\n                            [-5.652441406249977, 56.531982421875],\n                            [-6.133691406249966, 56.706689453124966],\n                            [-5.730615234374994, 56.853076171875045],\n                            [-5.86142578124992, 56.902685546875006],\n                            [-5.561914062499994, 57.23271484375002],\n                            [-5.794921874999972, 57.37880859375002],\n                            [-5.581787109374972, 57.546777343749966],\n                            [-5.744921874999989, 57.668310546875034],\n                            [-5.608349609374955, 57.88134765625],\n                            [-5.157226562499972, 57.88134765625],\n                            [-5.413183593750006, 58.06972656250002],\n                            [-5.338281250000023, 58.23872070312498],\n                            [-5.008300781250028, 58.262646484374955],\n                            [-5.016748046874966, 58.566552734374966],\n                            [-4.433251953124937, 58.51284179687505],\n                            [-3.25913085937492, 58.65],\n                            [-3.053076171874949, 58.63481445312502],\n                            [-3.109667968749932, 58.515478515625034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-3.057421874999932, 59.02963867187498],\n                            [-2.793017578124989, 58.906933593749955],\n                            [-3.331640624999949, 58.97124023437499],\n                            [-3.31035156249996, 59.13081054687498],\n                            [-3.057421874999932, 59.02963867187498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-1.30810546875, 60.5375],\n                            [-1.052441406249955, 60.44448242187502],\n                            [-1.299462890624994, 59.87866210937503],\n                            [-1.290917968749937, 60.153466796874966],\n                            [-1.663769531249983, 60.282519531250074],\n                            [-1.374609374999949, 60.33291015625002],\n                            [-1.571777343749972, 60.494433593750074],\n                            [-1.363964843750011, 60.60957031249998],\n                            [-1.30810546875, 60.5375]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"United Kingdom\", \"childNum\": 14, \"cp\": [-2.5830348, 54.4598409] }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [46.30546875000002, 41.507714843749994],\n                        [46.61894531250002, 41.34375],\n                        [46.67255859375001, 41.28681640625],\n                        [46.66240234375002, 41.245507812499994],\n                        [46.62636718750002, 41.15966796875],\n                        [46.534375, 41.08857421875],\n                        [46.43095703125002, 41.077050781249994],\n                        [46.086523437500006, 41.183837890625],\n                        [45.28095703125001, 41.449560546875],\n                        [45.21718750000002, 41.423193359375],\n                        [45.00136718750002, 41.290966796875],\n                        [44.97587890625002, 41.277490234374994],\n                        [44.81132812500002, 41.259375],\n                        [44.077246093750006, 41.182519531249994],\n                        [43.43339843750002, 41.155517578125],\n                        [43.20546875000002, 41.199169921875],\n                        [43.15283203125, 41.23642578125],\n                        [43.14101562500002, 41.26484375],\n                        [43.17128906250002, 41.287939453125],\n                        [43.149023437500006, 41.30712890625],\n                        [43.05712890625, 41.352832031249996],\n                        [42.90673828125, 41.466845703124996],\n                        [42.82167968750002, 41.4923828125],\n                        [42.78789062500002, 41.563720703125],\n                        [42.75410156250001, 41.57890625],\n                        [42.68242187500002, 41.585742187499996],\n                        [42.60683593750002, 41.57880859375],\n                        [42.590429687500006, 41.57070312499999],\n                        [42.5673828125, 41.55927734375],\n                        [42.46640625, 41.43984375],\n                        [41.92578125, 41.495654296874996],\n                        [41.82353515625002, 41.432373046875],\n                        [41.779394531250006, 41.44052734375],\n                        [41.701757812500006, 41.471582031249994],\n                        [41.57656250000002, 41.497314453125],\n                        [41.51005859375002, 41.517480468749994],\n                        [41.701757812500006, 41.705419921875],\n                        [41.76298828125002, 41.970019531249996],\n                        [41.48876953125, 42.659326171874994],\n                        [40.83662109375001, 43.0634765625],\n                        [40.46210937500001, 43.145703125],\n                        [39.97832031250002, 43.419824218749994],\n                        [40.02373046875002, 43.48486328125],\n                        [40.084570312500006, 43.553125],\n                        [40.648046875, 43.53388671875],\n                        [40.941992187500006, 43.41806640625],\n                        [41.083105468750006, 43.374462890625],\n                        [41.35820312500002, 43.333398437499994],\n                        [41.46074218750002, 43.276318359375],\n                        [41.58056640625, 43.21923828125],\n                        [42.76064453125002, 43.169580078124994],\n                        [42.99160156250002, 43.09150390625],\n                        [43.00019531250001, 43.049658203125],\n                        [43.08916015625002, 42.9890625],\n                        [43.55781250000001, 42.844482421875],\n                        [43.623046875, 42.80771484375],\n                        [43.78261718750002, 42.747021484375],\n                        [43.79873046875002, 42.727783203125],\n                        [43.79541015625, 42.702978515625],\n                        [43.74990234375002, 42.657519531249996],\n                        [43.738378906250006, 42.616992187499996],\n                        [43.759863281250006, 42.59384765625],\n                        [43.82597656250002, 42.571533203125],\n                        [43.95742187500002, 42.566552734374994],\n                        [44.00468750000002, 42.595605468749994],\n                        [44.10273437500001, 42.616357421874994],\n                        [44.32949218750002, 42.70351562499999],\n                        [44.505859375, 42.7486328125],\n                        [44.77109375, 42.616796875],\n                        [44.85048828125002, 42.746826171875],\n                        [44.87099609375002, 42.756396484374996],\n                        [44.943359375, 42.730273437499996],\n                        [45.07158203125002, 42.694140625],\n                        [45.160253906250006, 42.675],\n                        [45.34375, 42.52978515625],\n                        [45.56289062500002, 42.5357421875],\n                        [45.70527343750001, 42.498095703124996],\n                        [45.7275390625, 42.475048828125],\n                        [45.63427734375, 42.234716796875],\n                        [45.63857421875002, 42.205078125],\n                        [46.21269531250002, 41.989892578124994],\n                        [46.42988281250001, 41.890966796875],\n                        [46.18427734375001, 41.7021484375],\n                        [46.30546875000002, 41.507714843749994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Georgia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-0.068603515625, 11.115625],\n                        [0.009423828125023, 11.02099609375],\n                        [-0.08632812499999, 10.673046875],\n                        [0.380859375, 10.291845703124991],\n                        [0.264550781250023, 9.644726562499997],\n                        [0.342578125000017, 9.604150390624994],\n                        [0.2333984375, 9.463525390624994],\n                        [0.525683593750017, 9.398486328124989],\n                        [0.48876953125, 8.851464843749994],\n                        [0.37255859375, 8.75927734375],\n                        [0.686328125000017, 8.354882812499994],\n                        [0.5, 7.546875],\n                        [0.634765625, 7.353662109374994],\n                        [0.525585937500011, 6.850927734374991],\n                        [0.736914062500006, 6.452587890624997],\n                        [1.187207031250011, 6.089404296874989],\n                        [0.94970703125, 5.810253906249997],\n                        [0.259667968750023, 5.75732421875],\n                        [-2.001855468749994, 4.762451171875],\n                        [-3.114013671875, 5.088671874999989],\n                        [-2.815673828125, 5.153027343749997],\n                        [-2.754980468749977, 5.432519531249994],\n                        [-2.793652343749983, 5.60009765625],\n                        [-2.998291015625, 5.71132812499999],\n                        [-3.227148437499977, 6.749121093749991],\n                        [-2.959082031249977, 7.454541015624997],\n                        [-2.789746093749983, 7.931933593749989],\n                        [-2.668847656249994, 8.022216796875],\n                        [-2.613378906249977, 8.046679687499989],\n                        [-2.600976562499994, 8.082226562499997],\n                        [-2.619970703124977, 8.12109375],\n                        [-2.61171875, 8.147558593749991],\n                        [-2.538281249999983, 8.171630859375],\n                        [-2.505859375, 8.208740234375],\n                        [-2.600390624999989, 8.800439453124994],\n                        [-2.649218749999989, 8.956591796874989],\n                        [-2.689892578124983, 9.025097656249997],\n                        [-2.746923828124977, 9.045117187499997],\n                        [-2.705761718749983, 9.351367187499989],\n                        [-2.695849609374989, 9.481347656249994],\n                        [-2.706201171874994, 9.533935546875],\n                        [-2.765966796874977, 9.658056640624991],\n                        [-2.780517578125, 9.745849609375],\n                        [-2.791162109374994, 10.432421874999989],\n                        [-2.914892578124977, 10.592333984374989],\n                        [-2.829931640624977, 10.998388671874991],\n                        [-1.04248046875, 11.010058593749989],\n                        [-0.627148437499983, 10.927392578124994],\n                        [-0.299462890624994, 11.166894531249994],\n                        [-0.068603515625, 11.115625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Ghana\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-11.389404296875, 12.404394531249991],\n                        [-11.502197265625, 12.198632812499994],\n                        [-11.30517578125, 12.015429687499989],\n                        [-10.933203124999977, 12.205175781249991],\n                        [-10.709228515625, 11.898730468749989],\n                        [-10.274853515624983, 12.212646484375],\n                        [-9.754003906249977, 12.029931640624994],\n                        [-9.358105468749983, 12.255419921874989],\n                        [-9.395361328124977, 12.464648437499989],\n                        [-9.043066406249977, 12.40234375],\n                        [-8.818310546874983, 11.922509765624994],\n                        [-8.822021484375, 11.673242187499994],\n                        [-8.398535156249977, 11.366552734374991],\n                        [-8.666699218749983, 11.009472656249997],\n                        [-8.33740234375, 10.990625],\n                        [-8.266650390624989, 10.485986328124994],\n                        [-8.007275390624983, 10.321875],\n                        [-7.990625, 10.1625],\n                        [-8.155175781249994, 9.973193359374989],\n                        [-8.136962890625, 9.49570312499999],\n                        [-7.896191406249983, 9.415869140624991],\n                        [-7.918066406249977, 9.188525390624989],\n                        [-7.839404296874989, 9.151611328125],\n                        [-7.7998046875, 9.115039062499989],\n                        [-7.777978515624994, 9.080859374999989],\n                        [-7.902099609375, 9.01708984375],\n                        [-7.938183593749983, 8.979785156249989],\n                        [-7.950976562499989, 8.786816406249997],\n                        [-7.719580078124977, 8.643017578124997],\n                        [-7.696093749999989, 8.375585937499991],\n                        [-7.823583984374977, 8.467675781249994],\n                        [-7.953125, 8.477734375],\n                        [-8.236962890624994, 8.455664062499991],\n                        [-8.244140625, 8.407910156249997],\n                        [-8.256103515625, 8.253710937499989],\n                        [-8.217138671874977, 8.219677734374997],\n                        [-8.140625, 8.181445312499989],\n                        [-8.048583984375, 8.169726562499989],\n                        [-8.009863281249977, 8.07851562499999],\n                        [-8.126855468749994, 7.867724609374989],\n                        [-8.115429687499983, 7.7607421875],\n                        [-8.205957031249994, 7.59023437499999],\n                        [-8.231884765624983, 7.556738281249991],\n                        [-8.429980468749989, 7.601855468749989],\n                        [-8.486425781249977, 7.558496093749994],\n                        [-8.659765624999977, 7.688378906249994],\n                        [-8.8896484375, 7.2626953125],\n                        [-9.11757812499999, 7.215917968749991],\n                        [-9.463818359374983, 7.415869140624991],\n                        [-9.369140625, 7.703808593749997],\n                        [-9.518261718749983, 8.34609375],\n                        [-9.781982421875, 8.537695312499991],\n                        [-10.064355468749994, 8.429882812499997],\n                        [-10.147412109374983, 8.519726562499997],\n                        [-10.233056640624994, 8.488818359374989],\n                        [-10.283203125, 8.485156249999989],\n                        [-10.360058593749983, 8.495507812499994],\n                        [-10.394433593749994, 8.48095703125],\n                        [-10.496435546874977, 8.362109374999989],\n                        [-10.557714843749977, 8.315673828125],\n                        [-10.686962890624983, 8.321679687499994],\n                        [-10.712109374999983, 8.335253906249989],\n                        [-10.677343749999977, 8.400585937499997],\n                        [-10.500537109374989, 8.687548828124989],\n                        [-10.615966796875, 9.059179687499991],\n                        [-10.726855468749989, 9.081689453124994],\n                        [-10.747021484374983, 9.095263671874989],\n                        [-10.749951171874983, 9.122363281249989],\n                        [-10.687646484374994, 9.261132812499994],\n                        [-10.682714843749977, 9.289355468749989],\n                        [-10.758593749999989, 9.385351562499991],\n                        [-11.047460937499977, 9.786328125],\n                        [-11.180859374999983, 9.925341796874989],\n                        [-11.205664062499977, 9.977734375],\n                        [-11.273632812499983, 9.996533203124997],\n                        [-11.911083984374983, 9.993017578124991],\n                        [-12.142333984375, 9.87539062499999],\n                        [-12.427978515625, 9.898144531249997],\n                        [-12.557861328125, 9.704980468749994],\n                        [-12.755859375, 9.373583984374989],\n                        [-12.958789062499989, 9.263330078124994],\n                        [-13.077294921874994, 9.069628906249989],\n                        [-13.292675781249983, 9.04921875],\n                        [-13.436279296875, 9.4203125],\n                        [-13.691357421874983, 9.535791015624994],\n                        [-13.689794921874977, 9.927783203124989],\n                        [-13.820117187499989, 9.88720703125],\n                        [-14.045019531249977, 10.141259765624994],\n                        [-14.426904296874994, 10.248339843749989],\n                        [-14.609570312499983, 10.549853515624989],\n                        [-14.593505859375, 10.766699218749991],\n                        [-14.677343749999977, 10.68896484375],\n                        [-14.775927734374989, 10.931640625],\n                        [-14.88671875, 10.968066406249989],\n                        [-14.975, 10.803417968749997],\n                        [-15.051220703124983, 10.834570312499991],\n                        [-15.043017578124989, 10.940136718749997],\n                        [-14.9990234375, 10.9921875],\n                        [-14.944433593749977, 11.072167968749994],\n                        [-14.779296875, 11.405517578125],\n                        [-14.720263671874989, 11.48193359375],\n                        [-14.682958984374977, 11.508496093749997],\n                        [-14.604785156249989, 11.511621093749994],\n                        [-14.452441406249989, 11.556201171874989],\n                        [-14.327832031249983, 11.629785156249994],\n                        [-14.265576171874983, 11.659912109375],\n                        [-14.122314453125, 11.65195312499999],\n                        [-13.953222656249977, 11.664599609374989],\n                        [-13.732763671874977, 11.736035156249997],\n                        [-13.730664062499983, 11.959863281249994],\n                        [-13.737988281249983, 12.009667968749994],\n                        [-13.816308593749994, 12.054492187499989],\n                        [-13.948876953124994, 12.178173828124997],\n                        [-13.8875, 12.246875],\n                        [-13.759765625, 12.262353515624994],\n                        [-13.673535156249983, 12.478515625],\n                        [-13.732617187499983, 12.592822265624989],\n                        [-13.729248046875, 12.673925781249991],\n                        [-13.082910156249994, 12.633544921875],\n                        [-13.061279296875, 12.489990234375],\n                        [-12.930712890624989, 12.532275390624989],\n                        [-12.399072265624994, 12.340087890625],\n                        [-11.389404296875, 12.404394531249991]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Guinea\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-16.763330078124994, 13.064160156249997],\n                        [-16.824804687499977, 13.341064453125],\n                        [-16.669335937499994, 13.475],\n                        [-16.41337890624999, 13.269726562499997],\n                        [-15.427490234375, 13.46835937499999],\n                        [-16.135449218749983, 13.4482421875],\n                        [-16.351806640625, 13.34335937499999],\n                        [-16.56230468749999, 13.587304687499994],\n                        [-15.509667968749994, 13.586230468750003],\n                        [-15.426855468749977, 13.727001953124997],\n                        [-15.108349609374983, 13.81210937499999],\n                        [-14.405468749999983, 13.503710937500003],\n                        [-13.977392578124977, 13.54345703125],\n                        [-13.826708984374989, 13.4078125],\n                        [-14.246777343749983, 13.23583984375],\n                        [-15.151123046875, 13.556494140624991],\n                        [-15.286230468749977, 13.39599609375],\n                        [-15.814404296874983, 13.325146484374997],\n                        [-15.834277343749989, 13.156445312499997],\n                        [-16.648779296874977, 13.154150390624991],\n                        [-16.763330078124994, 13.064160156249997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Gambia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-15.895898437499966, 11.082470703124969],\n                            [-15.963964843749977, 11.05898437499998],\n                            [-15.937695312499955, 11.192773437499966],\n                            [-15.895898437499966, 11.082470703124969]\n                        ]\n                    ],\n                    [\n                        [\n                            [-16.11450195312503, 11.059423828124977],\n                            [-16.236425781249977, 11.113427734374966],\n                            [-16.06733398437501, 11.197216796874983],\n                            [-16.11450195312503, 11.059423828124977]\n                        ]\n                    ],\n                    [\n                        [\n                            [-15.901806640624926, 11.4658203125],\n                            [-16.02319335937497, 11.477148437499991],\n                            [-15.964550781249926, 11.59829101562498],\n                            [-15.901806640624926, 11.4658203125]\n                        ]\n                    ],\n                    [\n                        [\n                            [-15.986425781249949, 11.882031249999969],\n                            [-16.038330078124943, 11.759716796875011],\n                            [-16.15244140624992, 11.876806640624963],\n                            [-15.986425781249949, 11.882031249999969]\n                        ]\n                    ],\n                    [\n                        [\n                            [-13.759765625, 12.262353515624994],\n                            [-13.8875, 12.246875],\n                            [-13.948876953124966, 12.178173828124997],\n                            [-13.737988281250011, 12.009667968750037],\n                            [-13.730664062499926, 11.959863281250009],\n                            [-13.73276367187492, 11.736035156249983],\n                            [-13.953222656249977, 11.664599609374989],\n                            [-14.265576171874926, 11.659912109375014],\n                            [-14.327832031250011, 11.629785156250009],\n                            [-14.452441406249989, 11.556201171875017],\n                            [-14.604785156249932, 11.511621093749994],\n                            [-14.682958984374949, 11.508496093749983],\n                            [-14.720263671875017, 11.481933593749986],\n                            [-14.779296874999972, 11.405517578125057],\n                            [-14.944433593749949, 11.072167968749994],\n                            [-14.999023437499972, 10.992187500000043],\n                            [-15.04301757812496, 10.940136718750011],\n                            [-15.09375, 11.011035156249974],\n                            [-15.054589843749994, 11.141943359375006],\n                            [-15.222119140624926, 11.030908203125037],\n                            [-15.216699218749994, 11.15625],\n                            [-15.39311523437496, 11.217236328124983],\n                            [-15.354687499999955, 11.396337890624963],\n                            [-15.479492187499972, 11.410302734374966],\n                            [-15.072656249999937, 11.597802734374966],\n                            [-15.230371093750023, 11.686767578124972],\n                            [-15.412988281249994, 11.615234374999972],\n                            [-15.501904296875011, 11.723779296874966],\n                            [-15.467187499999937, 11.842822265624974],\n                            [-15.078271484374937, 11.968994140625014],\n                            [-15.941748046875006, 11.786621093749986],\n                            [-15.92021484374996, 11.93779296874996],\n                            [-16.138427734375, 11.917285156250045],\n                            [-16.32807617187501, 12.051611328124963],\n                            [-16.244580078124955, 12.237109375],\n                            [-16.43681640624996, 12.204150390625045],\n                            [-16.711816406249937, 12.354833984375006],\n                            [-16.656933593749955, 12.364355468749991],\n                            [-16.52133789062495, 12.348632812499986],\n                            [-16.41630859374996, 12.367675781250057],\n                            [-16.24150390624996, 12.443310546875011],\n                            [-16.144189453124937, 12.457421875000037],\n                            [-15.839550781249955, 12.437890624999966],\n                            [-15.57480468749992, 12.490380859375009],\n                            [-15.19609375, 12.679931640624986],\n                            [-14.3492187499999, 12.67641601562498],\n                            [-14.064843749999966, 12.675292968750014],\n                            [-13.729248046875, 12.673925781250006],\n                            [-13.732617187499983, 12.592822265625003],\n                            [-13.673535156249926, 12.478515624999986],\n                            [-13.759765625, 12.262353515624994]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Guinea-Bissau\", \"childNum\": 5 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [11.332324218750017, 1.528369140624989],\n                            [11.335351562500023, 0.999707031250011],\n                            [9.906738281250028, 0.960107421875037],\n                            [9.80390625000004, 0.998730468749997],\n                            [9.788671875000034, 1.025683593749974],\n                            [9.760546874999989, 1.074707031250014],\n                            [9.704589843750057, 1.079980468750023],\n                            [9.676464843750011, 1.074707031250014],\n                            [9.636132812500051, 1.046679687499989],\n                            [9.590820312500057, 1.031982421875014],\n                            [9.599414062500045, 1.054443359374972],\n                            [9.509863281250006, 1.114794921875017],\n                            [9.385937500000068, 1.13925781250002],\n                            [9.807031250000051, 1.927490234375028],\n                            [9.77968750000008, 2.068212890625006],\n                            [9.800781250000028, 2.304443359375],\n                            [9.826171875000057, 2.297802734374969],\n                            [9.8369140625, 2.242382812500054],\n                            [9.870117187500028, 2.21328125],\n                            [9.979882812499994, 2.167773437500045],\n                            [10.790917968750023, 2.167578125],\n                            [11.096582031250051, 2.167480468749986],\n                            [11.328710937500006, 2.167431640624969],\n                            [11.332324218750017, 1.528369140624989]\n                        ]\n                    ],\n                    [\n                        [\n                            [8.735742187500023, 3.758300781249972],\n                            [8.910058593750023, 3.758203125000051],\n                            [8.946093750000074, 3.627539062499977],\n                            [8.704003906250051, 3.223632812500028],\n                            [8.474902343749989, 3.264648437500043],\n                            [8.464648437500045, 3.450585937499994],\n                            [8.735742187500023, 3.758300781249972]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Eq. Guinea\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [23.852246093749983, 35.53544921874999],\n                            [24.166015625000057, 35.59521484375],\n                            [24.108984374999977, 35.49580078124998],\n                            [24.35400390625, 35.359472656250034],\n                            [25.73017578125004, 35.34858398437501],\n                            [25.791308593750074, 35.122851562500045],\n                            [26.32021484375008, 35.315136718749955],\n                            [26.165625, 35.018603515625045],\n                            [24.79980468750003, 34.93447265625002],\n                            [24.70888671875008, 35.08906250000001],\n                            [24.463671875000045, 35.160351562499955],\n                            [23.59277343749997, 35.257226562499966],\n                            [23.56982421875, 35.534765625000034],\n                            [23.67265624999999, 35.51391601562506],\n                            [23.736914062500034, 35.65551757812503],\n                            [23.852246093749983, 35.53544921874999]\n                        ]\n                    ],\n                    [\n                        [\n                            [27.17607421874999, 35.46528320312498],\n                            [27.070703125000023, 35.59775390624998],\n                            [27.22314453125, 35.820458984374966],\n                            [27.17607421874999, 35.46528320312498]\n                        ]\n                    ],\n                    [\n                        [\n                            [23.053808593750034, 36.18979492187498],\n                            [22.91083984375004, 36.220996093750045],\n                            [22.950488281250045, 36.38393554687502],\n                            [23.053808593750034, 36.18979492187498]\n                        ]\n                    ],\n                    [\n                        [\n                            [27.84277343750003, 35.929296875000034],\n                            [27.71552734375004, 35.95732421874996],\n                            [27.71630859375003, 36.17158203125001],\n                            [28.23183593750005, 36.43364257812502],\n                            [28.087792968750023, 36.06533203125002],\n                            [27.84277343750003, 35.929296875000034]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.48242187500003, 36.39262695312502],\n                            [25.37050781250005, 36.35893554687499],\n                            [25.408984375000074, 36.473730468750006],\n                            [25.48242187500003, 36.39262695312502]\n                        ]\n                    ],\n                    [\n                        [\n                            [26.46064453125001, 36.58540039062501],\n                            [26.270019531250057, 36.54692382812499],\n                            [26.370019531250023, 36.63857421875002],\n                            [26.46064453125001, 36.58540039062501]\n                        ]\n                    ],\n                    [\n                        [\n                            [26.94960937500005, 36.72709960937502],\n                            [27.214941406250006, 36.89863281249998],\n                            [27.352148437499977, 36.86889648437506],\n                            [26.94960937500005, 36.72709960937502]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.859375, 36.79042968750005],\n                            [25.74316406250003, 36.78974609374998],\n                            [26.06445312500003, 36.90273437500002],\n                            [25.859375, 36.79042968750005]\n                        ]\n                    ],\n                    [\n                        [\n                            [27.01972656250004, 36.95903320312502],\n                            [26.91992187500003, 36.94521484375005],\n                            [26.88867187499997, 37.087255859375034],\n                            [27.01972656250004, 36.95903320312502]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.278906250000034, 37.06840820312502],\n                            [25.105468750000057, 37.034960937500045],\n                            [25.235058593750068, 37.148535156250006],\n                            [25.278906250000034, 37.06840820312502]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.54589843749997, 36.96757812499999],\n                            [25.45673828125001, 36.9296875],\n                            [25.361914062500063, 37.07041015624998],\n                            [25.52529296875005, 37.19638671875006],\n                            [25.54589843749997, 36.96757812499999]\n                        ]\n                    ],\n                    [\n                        [\n                            [24.523535156250063, 37.125097656250006],\n                            [24.42480468750003, 37.131982421874994],\n                            [24.48378906250005, 37.21020507812503],\n                            [24.523535156250063, 37.125097656250006]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.402734375000023, 37.419140624999955],\n                            [25.312695312500068, 37.48930664062496],\n                            [25.462988281250063, 37.47109375],\n                            [25.402734375000023, 37.419140624999955]\n                        ]\n                    ],\n                    [\n                        [\n                            [26.029296875000057, 37.529394531250034],\n                            [26.086328125000023, 37.63491210937505],\n                            [26.351367187500017, 37.67431640625],\n                            [26.029296875000057, 37.529394531250034]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.255859375000057, 37.59960937500006],\n                            [25.156347656250034, 37.54506835937505],\n                            [24.99648437500005, 37.676904296874994],\n                            [25.255859375000057, 37.59960937500006]\n                        ]\n                    ],\n                    [\n                        [\n                            [24.35595703125003, 37.57685546875004],\n                            [24.28896484375005, 37.52827148437498],\n                            [24.37910156250004, 37.682714843750006],\n                            [24.35595703125003, 37.57685546875004]\n                        ]\n                    ],\n                    [\n                        [\n                            [26.82441406250004, 37.81142578125005],\n                            [27.05507812500005, 37.70927734375002],\n                            [26.84492187500004, 37.64472656250001],\n                            [26.58105468750003, 37.723730468750034],\n                            [26.82441406250004, 37.81142578125005]\n                        ]\n                    ],\n                    [\n                        [\n                            [20.888476562500074, 37.805371093749955],\n                            [20.993945312500074, 37.70800781250003],\n                            [20.81855468750004, 37.66474609375001],\n                            [20.61953125000008, 37.855029296875045],\n                            [20.691503906250006, 37.929541015625034],\n                            [20.888476562500074, 37.805371093749955]\n                        ]\n                    ],\n                    [\n                        [\n                            [24.991699218750057, 37.75961914062506],\n                            [24.962207031250074, 37.69238281250003],\n                            [24.7001953125, 37.961669921875],\n                            [24.956347656250045, 37.90478515625006],\n                            [24.991699218750057, 37.75961914062506]\n                        ]\n                    ],\n                    [\n                        [\n                            [20.61230468750003, 38.38334960937502],\n                            [20.761328125, 38.07055664062497],\n                            [20.523535156250063, 38.106640624999955],\n                            [20.4521484375, 38.23417968750002],\n                            [20.35253906250003, 38.179882812499955],\n                            [20.563183593750068, 38.474951171875034],\n                            [20.61230468750003, 38.38334960937502]\n                        ]\n                    ],\n                    [\n                        [\n                            [26.094042968750017, 38.21806640625002],\n                            [25.891894531250045, 38.243310546874994],\n                            [25.991406250000068, 38.353515625],\n                            [25.846093750000023, 38.57402343749996],\n                            [26.16035156250001, 38.54072265625001],\n                            [26.094042968750017, 38.21806640625002]\n                        ]\n                    ],\n                    [\n                        [\n                            [20.68671875000001, 38.60869140625002],\n                            [20.5546875, 38.58256835937502],\n                            [20.69414062499999, 38.84423828125003],\n                            [20.68671875000001, 38.60869140625002]\n                        ]\n                    ],\n                    [\n                        [\n                            [24.67470703125005, 38.80922851562502],\n                            [24.54101562499997, 38.788671875],\n                            [24.485644531250074, 38.980273437500045],\n                            [24.67470703125005, 38.80922851562502]\n                        ]\n                    ],\n                    [\n                        [\n                            [23.41542968750008, 38.958642578124994],\n                            [23.525, 38.8134765625],\n                            [24.127539062500034, 38.648486328125045],\n                            [24.27578125000005, 38.22001953124996],\n                            [24.58837890625003, 38.12397460937504],\n                            [24.53652343750005, 37.97973632812506],\n                            [24.212011718750006, 38.11752929687506],\n                            [24.040136718750006, 38.389990234375034],\n                            [23.65078125000008, 38.44306640625001],\n                            [23.25214843750004, 38.80122070312498],\n                            [22.870312500000068, 38.870507812499966],\n                            [23.258203125000023, 39.03134765625006],\n                            [23.41542968750008, 38.958642578124994]\n                        ]\n                    ],\n                    [\n                        [\n                            [26.41015625000003, 39.329443359375034],\n                            [26.59560546875005, 39.04882812499997],\n                            [26.488671875000023, 39.074804687500034],\n                            [26.46875, 38.97280273437502],\n                            [26.10791015625, 39.08105468749997],\n                            [26.273144531249983, 39.19755859374999],\n                            [26.072363281250034, 39.095605468749994],\n                            [25.84414062500008, 39.20004882812506],\n                            [26.16542968750008, 39.37353515625006],\n                            [26.41015625000003, 39.329443359375034]\n                        ]\n                    ],\n                    [\n                        [\n                            [20.077929687500045, 39.432714843750034],\n                            [19.883984375000068, 39.461523437500034],\n                            [19.646484375, 39.76708984375003],\n                            [19.926074218750017, 39.773730468750045],\n                            [19.8466796875, 39.66811523437502],\n                            [20.077929687500045, 39.432714843750034]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.43769531250004, 39.98330078125002],\n                            [25.357031250000063, 39.80810546875003],\n                            [25.24941406250005, 39.89414062500006],\n                            [25.06220703125004, 39.852392578125006],\n                            [25.05800781250005, 39.999658203124966],\n                            [25.43769531250004, 39.98330078125002]\n                        ]\n                    ],\n                    [\n                        [\n                            [24.774218750000074, 40.615185546874955],\n                            [24.515527343750023, 40.64702148437496],\n                            [24.623339843750045, 40.79291992187501],\n                            [24.774218750000074, 40.615185546874955]\n                        ]\n                    ],\n                    [\n                        [\n                            [26.03896484375008, 40.726757812499955],\n                            [25.10449218750003, 40.994726562500006],\n                            [24.792968750000057, 40.857519531250034],\n                            [24.47705078125, 40.94775390625003],\n                            [24.082324218750074, 40.72407226562504],\n                            [23.762792968750063, 40.74780273437497],\n                            [23.866796875000034, 40.41855468750006],\n                            [24.21279296875008, 40.32778320312502],\n                            [24.343359375000034, 40.14770507812503],\n                            [23.913183593750063, 40.35878906250005],\n                            [23.72792968750008, 40.329736328124994],\n                            [23.96748046875001, 40.11455078125002],\n                            [23.947070312500045, 39.96557617187506],\n                            [23.66455078125003, 40.22382812499998],\n                            [23.42626953125, 40.26396484374999],\n                            [23.62734375, 39.92407226562503],\n                            [22.896484375000057, 40.39990234374997],\n                            [22.92226562500008, 40.59086914062499],\n                            [22.629492187500034, 40.49555664062501],\n                            [22.59218750000005, 40.03691406250002],\n                            [23.327734374999977, 39.174902343750006],\n                            [23.15468750000008, 39.10146484375005],\n                            [23.16171875, 39.25776367187501],\n                            [22.92138671874997, 39.30634765625004],\n                            [22.886035156250074, 39.16997070312496],\n                            [23.066699218750017, 39.03793945312498],\n                            [22.569140625000074, 38.86748046874999],\n                            [23.25292968750003, 38.66123046875006],\n                            [23.68398437500008, 38.35244140625002],\n                            [23.96699218750001, 38.275],\n                            [24.024511718750006, 38.139794921874966],\n                            [24.01972656250001, 37.67773437499997],\n                            [23.50175781249999, 38.03486328124998],\n                            [23.03632812500004, 37.87836914062501],\n                            [23.48925781250003, 37.440185546875],\n                            [23.16152343750005, 37.333837890625006],\n                            [22.725390625000017, 37.542138671874966],\n                            [23.16015625000003, 36.448095703125034],\n                            [22.717187500000023, 36.79394531250006],\n                            [22.42773437500003, 36.47578124999998],\n                            [22.08046875000008, 37.028955078124966],\n                            [21.95556640625003, 36.990087890625034],\n                            [21.892382812500045, 36.73730468749997],\n                            [21.58291015625005, 37.080957031249994],\n                            [21.678906250000068, 37.38720703125003],\n                            [21.124707031250068, 37.89160156250003],\n                            [21.40371093750005, 38.19667968750002],\n                            [21.658398437500068, 38.17509765624996],\n                            [21.82470703125003, 38.328125],\n                            [22.846386718750068, 37.96757812499996],\n                            [23.18349609375008, 38.133691406249966],\n                            [22.421679687500045, 38.43852539062499],\n                            [22.319921875, 38.35683593750005],\n                            [21.96533203124997, 38.412451171875006],\n                            [21.47255859375005, 38.321386718750006],\n                            [21.3310546875, 38.48730468749997],\n                            [21.303320312500034, 38.373925781249966],\n                            [21.113183593750023, 38.38466796875002],\n                            [20.768554687500057, 38.874414062499966],\n                            [21.111621093750045, 38.89628906249999],\n                            [21.11835937500001, 39.029980468749955],\n                            [20.71337890625, 39.03515625000003],\n                            [20.300781250000057, 39.32709960937501],\n                            [20.19140625, 39.545800781249966],\n                            [20.099414062500074, 39.641259765624966],\n                            [20.001269531250074, 39.70942382812501],\n                            [20.022558593750063, 39.710693359375],\n                            [20.059765624999983, 39.69912109375002],\n                            [20.13105468750004, 39.66162109375003],\n                            [20.206835937500017, 39.65351562499998],\n                            [20.382421875, 39.802636718749994],\n                            [20.381640625000017, 39.84179687500006],\n                            [20.311328125000074, 39.95078125000006],\n                            [20.311132812500034, 39.97944335937504],\n                            [20.338476562500006, 39.991064453125006],\n                            [20.38369140625008, 40.0171875],\n                            [20.408007812500074, 40.049462890624994],\n                            [20.4560546875, 40.065576171874994],\n                            [20.657421875000068, 40.11738281249998],\n                            [20.881640625000017, 40.467919921874994],\n                            [21.030859375000034, 40.62246093750002],\n                            [20.95576171875001, 40.775292968749994],\n                            [20.96425781250005, 40.84990234374999],\n                            [21.575781250000034, 40.86894531249996],\n                            [21.627539062500006, 40.896337890625034],\n                            [21.77949218750004, 40.95043945312506],\n                            [21.99335937500001, 41.13095703125006],\n                            [22.18447265625005, 41.15864257812501],\n                            [22.49355468750005, 41.118505859375006],\n                            [22.603613281249977, 41.14018554687499],\n                            [22.724804687500068, 41.17851562499999],\n                            [22.78388671875004, 41.33198242187498],\n                            [23.155957031250068, 41.32207031249999],\n                            [23.239843750000034, 41.38496093750001],\n                            [23.372070312500057, 41.3896484375],\n                            [23.433398437500017, 41.39873046874999],\n                            [23.53583984375001, 41.38603515624999],\n                            [23.63515625000008, 41.386767578125045],\n                            [24.011328124999977, 41.460058593750034],\n                            [24.03291015625004, 41.469091796875034],\n                            [24.05605468750005, 41.527246093749966],\n                            [24.38671875, 41.523535156250006],\n                            [24.487890625, 41.55522460937499],\n                            [24.518261718750068, 41.55253906249996],\n                            [24.773730468750045, 41.356103515624994],\n                            [24.99355468750008, 41.36499023437503],\n                            [25.133398437500063, 41.31577148437506],\n                            [25.251171875000068, 41.243554687499994],\n                            [25.923339843750057, 41.311914062499966],\n                            [26.066406250000057, 41.35068359375006],\n                            [26.135351562499977, 41.3857421875],\n                            [26.155175781250023, 41.43486328124999],\n                            [26.143554687500057, 41.52153320312496],\n                            [26.085546875000063, 41.704150390625045],\n                            [26.10742187499997, 41.72568359374998],\n                            [26.20058593750005, 41.74379882812502],\n                            [26.320898437500034, 41.716552734375],\n                            [26.581347656250074, 41.60126953125004],\n                            [26.62490234375008, 41.401757812499994],\n                            [26.330664062499977, 41.23876953125],\n                            [26.331054687500057, 40.954492187499994],\n                            [26.03896484375008, 40.726757812499955]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Greece\", \"childNum\": 29 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-61.71552734375, 12.012646484374997],\n                        [-61.714990234374994, 12.18515625],\n                        [-61.60703125, 12.223291015624994],\n                        [-61.71552734375, 12.012646484374997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Grenada\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-46.266699218750006, 60.781396484374994],\n                            [-46.381542968749955, 60.66030273437502],\n                            [-46.7880859375, 60.758398437500034],\n                            [-46.205224609374994, 60.943505859374994],\n                            [-46.266699218750006, 60.781396484374994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-37.03125, 65.53198242187497],\n                            [-37.23842773437494, 65.60986328125003],\n                            [-37.047509765624966, 65.722265625],\n                            [-37.03125, 65.53198242187497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-51.01367187499994, 69.55249023437497],\n                            [-51.202050781249966, 69.525],\n                            [-51.33886718749994, 69.73203125000006],\n                            [-51.094580078125006, 69.92416992187503],\n                            [-50.67900390624999, 69.84853515625],\n                            [-51.01367187499994, 69.55249023437497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-52.73115234375001, 69.94472656250005],\n                            [-52.0453125, 69.8072265625],\n                            [-51.90019531249999, 69.60478515625007],\n                            [-53.57841796874996, 69.25664062500002],\n                            [-54.18271484374995, 69.40351562500001],\n                            [-53.65830078124998, 69.46513671875005],\n                            [-53.825, 69.54033203124999],\n                            [-54.91914062499998, 69.71362304687503],\n                            [-54.78789062499996, 69.94985351562502],\n                            [-54.322607421875034, 69.94189453125],\n                            [-54.83076171875001, 70.13295898437502],\n                            [-54.37163085937499, 70.31728515625],\n                            [-53.296728515625034, 70.20537109375002],\n                            [-52.73115234375001, 69.94472656250005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-51.67514648437498, 70.855224609375],\n                            [-52.11938476562497, 70.87065429687502],\n                            [-52.10673828124999, 70.96801757812497],\n                            [-51.67514648437498, 70.855224609375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-25.43232421875001, 70.92133789062495],\n                            [-25.402246093749994, 70.65268554687503],\n                            [-26.217871093749977, 70.45405273437498],\n                            [-26.604687499999926, 70.55336914062497],\n                            [-28.03525390624995, 70.48681640625],\n                            [-27.61723632812496, 70.91376953125001],\n                            [-26.621777343749955, 70.87563476562497],\n                            [-25.81889648437499, 71.04365234375001],\n                            [-25.43232421875001, 70.92133789062495]\n                        ]\n                    ],\n                    [\n                        [\n                            [-53.53520507812493, 71.04082031250005],\n                            [-53.9578125, 71.12773437499999],\n                            [-53.58447265625003, 71.29707031249995],\n                            [-53.53520507812493, 71.04082031250005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-55.01689453124999, 72.79111328125003],\n                            [-55.56660156249998, 72.56435546875002],\n                            [-56.214794921874955, 72.71918945312495],\n                            [-55.01689453124999, 72.79111328125003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-18.000537109374932, 75.40732421875003],\n                            [-17.391992187499937, 75.03691406250007],\n                            [-18.670800781249966, 75.00166015624998],\n                            [-18.856054687499977, 75.31914062500002],\n                            [-18.000537109374932, 75.40732421875003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-18.58261718749995, 76.042333984375],\n                            [-19.085351562499966, 76.43037109375001],\n                            [-18.882470703124937, 76.70380859375001],\n                            [-18.58261718749995, 76.042333984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-71.667333984375, 77.32529296874998],\n                            [-72.48955078124999, 77.43164062499997],\n                            [-71.43344726562495, 77.394384765625],\n                            [-71.667333984375, 77.32529296874998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-17.6125, 79.82587890624995],\n                            [-18.662011718749966, 79.72001953125005],\n                            [-19.13828125, 79.85234375000002],\n                            [-17.98291015625, 80.05517578125003],\n                            [-17.471386718749955, 80.02871093749997],\n                            [-17.6125, 79.82587890624995]\n                        ]\n                    ],\n                    [\n                        [\n                            [-44.86455078124999, 82.08364257812502],\n                            [-46.75190429687501, 82.34819335937502],\n                            [-47.27226562499996, 82.65693359375001],\n                            [-46.399169921875, 82.692138671875],\n                            [-44.91748046875003, 82.48051757812505],\n                            [-44.86455078124999, 82.08364257812502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-29.952880859375, 83.56484374999997],\n                            [-25.795068359374994, 83.26098632812497],\n                            [-31.99267578125, 83.0853515625],\n                            [-32.03271484374997, 82.98344726562502],\n                            [-25.12338867187495, 83.15961914062501],\n                            [-24.47031249999995, 82.87739257812498],\n                            [-21.582519531249943, 82.6341796875],\n                            [-23.118066406249966, 82.32470703125003],\n                            [-29.57939453124996, 82.16118164062502],\n                            [-29.887402343749983, 82.05483398437502],\n                            [-29.543847656249994, 81.93994140624997],\n                            [-27.839501953124966, 82.04887695312505],\n                            [-25.148828124999966, 82.001123046875],\n                            [-24.293066406249977, 81.70097656250005],\n                            [-23.103710937499983, 82.01181640625003],\n                            [-21.337988281249977, 82.068701171875],\n                            [-21.230517578125017, 81.60136718749999],\n                            [-23.11772460937499, 80.77817382812498],\n                            [-19.62993164062499, 81.63989257812503],\n                            [-17.456054687499943, 81.397705078125],\n                            [-16.12070312499995, 81.776611328125],\n                            [-14.241992187500017, 81.81386718750005],\n                            [-12.434423828125006, 81.68251953125002],\n                            [-11.430664062499972, 81.45683593750005],\n                            [-13.126220703124972, 81.08779296875],\n                            [-14.452343749999955, 80.99311523437498],\n                            [-14.503564453124994, 80.76328125000006],\n                            [-16.76059570312492, 80.573388671875],\n                            [-15.937255859374972, 80.42763671874997],\n                            [-16.48876953124997, 80.25195312499997],\n                            [-18.070947265624994, 80.17207031249995],\n                            [-19.429199218749943, 80.25771484375],\n                            [-20.150146484375, 80.01123046874997],\n                            [-18.99199218749996, 79.17836914062502],\n                            [-21.133740234374926, 78.65864257812501],\n                            [-21.729589843749977, 77.70854492187499],\n                            [-20.862597656249932, 77.91186523437503],\n                            [-19.490429687499983, 77.71889648437497],\n                            [-19.46752929687503, 77.56582031250005],\n                            [-20.162060546874926, 77.68984375],\n                            [-20.680810546875023, 77.61899414062503],\n                            [-20.23193359374997, 77.36840820312497],\n                            [-19.30029296874997, 77.22236328124995],\n                            [-18.442626953124943, 77.259375],\n                            [-18.51030273437496, 76.77817382812498],\n                            [-20.48671875, 76.92080078125],\n                            [-21.614697265624926, 76.68789062499997],\n                            [-22.18525390625001, 76.79409179687502],\n                            [-22.609326171874983, 76.70429687500004],\n                            [-21.877343749999966, 76.57348632812503],\n                            [-21.488232421874926, 76.271875],\n                            [-20.10361328124992, 76.21909179687503],\n                            [-19.508984374999926, 75.75751953124995],\n                            [-19.52636718750003, 75.18022460937505],\n                            [-20.484960937500006, 75.31425781249999],\n                            [-21.649316406249966, 75.02343749999997],\n                            [-22.232861328124926, 75.11972656249998],\n                            [-21.69511718749999, 74.96445312500003],\n                            [-20.985791015624983, 75.07436523437497],\n                            [-20.86157226562497, 74.63593750000001],\n                            [-20.41708984374995, 74.9751953125],\n                            [-19.98491210937499, 74.9751953125],\n                            [-19.287011718750023, 74.54638671875006],\n                            [-19.36914062499997, 74.28403320312498],\n                            [-20.256445312499977, 74.2828125],\n                            [-20.653125, 74.13735351562502],\n                            [-21.954931640624977, 74.24428710937497],\n                            [-21.942919921874932, 74.56572265624999],\n                            [-22.32158203124999, 74.30253906250002],\n                            [-22.134814453124932, 73.99047851562503],\n                            [-20.36728515624992, 73.8482421875],\n                            [-20.509667968749966, 73.49287109375001],\n                            [-22.346875, 73.26923828125001],\n                            [-23.23320312499999, 73.39770507812497],\n                            [-24.157714843749943, 73.76445312499999],\n                            [-24.67724609375, 73.602197265625],\n                            [-25.521289062500017, 73.85161132812499],\n                            [-24.79125976562497, 73.51127929687502],\n                            [-26.062304687500017, 73.25302734375],\n                            [-27.270410156250023, 73.43627929687503],\n                            [-26.541845703125006, 73.24897460937495],\n                            [-27.561621093750006, 73.13847656250002],\n                            [-27.348046875000023, 73.06782226562501],\n                            [-25.057031250000023, 73.396484375],\n                            [-24.132666015625006, 73.409375],\n                            [-22.036328124999955, 72.91845703125006],\n                            [-22.29321289062497, 72.11953125],\n                            [-24.06904296875001, 72.49873046874998],\n                            [-24.629980468749977, 73.03764648437499],\n                            [-26.657617187499966, 72.71582031249997],\n                            [-24.81333007812492, 72.90151367187497],\n                            [-24.65, 72.58251953125],\n                            [-25.117871093749983, 72.34697265625005],\n                            [-24.66684570312492, 72.437353515625],\n                            [-21.959667968749955, 71.74467773437502],\n                            [-22.479638671874937, 71.38344726562497],\n                            [-22.417578125, 71.24868164062505],\n                            [-22.29902343750001, 71.43232421874998],\n                            [-21.75224609374999, 71.47832031250002],\n                            [-21.522656249999926, 70.52622070312503],\n                            [-22.38413085937492, 70.46240234375],\n                            [-22.437011718749943, 70.860009765625],\n                            [-22.690673828124943, 70.43730468750002],\n                            [-23.327832031249983, 70.45097656250007],\n                            [-23.97138671875001, 70.64946289062499],\n                            [-24.562207031249926, 71.22353515624997],\n                            [-25.885156249999966, 71.571923828125],\n                            [-27.08720703124999, 71.6265625],\n                            [-27.107031250000034, 71.53266601562498],\n                            [-25.842724609374955, 71.48017578124995],\n                            [-25.74223632812499, 71.18359375],\n                            [-26.717919921874994, 70.95048828125005],\n                            [-28.39843749999997, 70.99291992187497],\n                            [-27.99218749999997, 70.89521484374998],\n                            [-28.06987304687499, 70.69902343750005],\n                            [-29.07207031249999, 70.444970703125],\n                            [-26.621777343749955, 70.46337890625],\n                            [-26.576806640625023, 70.35708007812502],\n                            [-27.560839843749932, 70.12446289062498],\n                            [-27.384179687500023, 69.9916015625],\n                            [-27.027734374999966, 70.20122070312499],\n                            [-25.529882812499977, 70.35317382812502],\n                            [-23.66733398437495, 70.139306640625],\n                            [-22.28447265624996, 70.12583007812498],\n                            [-22.287060546874955, 70.03339843749998],\n                            [-23.03364257812501, 69.90083007812498],\n                            [-23.04956054687497, 69.79272460937497],\n                            [-23.86572265624997, 69.73671875000002],\n                            [-23.739404296874994, 69.58862304687497],\n                            [-24.296679687500017, 69.58554687500006],\n                            [-24.295556640624966, 69.439306640625],\n                            [-25.188574218750006, 69.26054687500002],\n                            [-25.092431640624937, 69.16518554687502],\n                            [-25.697998046874943, 68.889892578125],\n                            [-26.48291015624997, 68.67592773437502],\n                            [-29.24951171874997, 68.29877929687501],\n                            [-29.86850585937495, 68.31157226562505],\n                            [-30.318115234375, 68.19331054687501],\n                            [-30.72001953124999, 68.25117187499998],\n                            [-30.610742187499994, 68.11791992187503],\n                            [-30.97856445312499, 68.06132812500005],\n                            [-32.32744140624999, 68.43730468749999],\n                            [-32.16455078125, 67.99111328125002],\n                            [-33.15698242187497, 67.62670898437506],\n                            [-34.1982421875, 66.65507812499999],\n                            [-35.18857421874995, 66.25029296875002],\n                            [-35.86723632812502, 66.44140624999997],\n                            [-35.630078124999926, 66.13994140625002],\n                            [-36.37919921874996, 65.830810546875],\n                            [-36.52724609375002, 66.00771484375],\n                            [-36.665185546874966, 65.79008789062507],\n                            [-37.06279296874996, 65.87143554687503],\n                            [-37.410058593749994, 65.65634765625],\n                            [-37.954785156249955, 65.63359375000007],\n                            [-37.278710937499994, 66.30439453124995],\n                            [-38.156640624999966, 66.38559570312498],\n                            [-37.75234375000002, 66.26152343750002],\n                            [-38.13994140625002, 65.90351562499998],\n                            [-38.52036132812498, 66.00966796875002],\n                            [-38.20336914062497, 65.71171874999999],\n                            [-40.17353515624998, 65.55615234375],\n                            [-39.57792968749996, 65.34077148437501],\n                            [-39.937255859375, 65.14160156250003],\n                            [-40.253125, 65.04887695312505],\n                            [-41.08442382812501, 65.10083007812497],\n                            [-40.966015624999955, 64.86884765624995],\n                            [-40.655468749999926, 64.91533203125002],\n                            [-40.18222656249998, 64.47993164062495],\n                            [-40.78173828125, 64.22177734375003],\n                            [-41.581005859374926, 64.29833984375],\n                            [-41.03056640624996, 64.12104492187504],\n                            [-40.61777343749998, 64.13173828125],\n                            [-40.550390625000034, 63.72524414062505],\n                            [-40.77519531249999, 63.53364257812501],\n                            [-41.04873046875002, 63.51381835937505],\n                            [-41.387890624999926, 63.06186523437498],\n                            [-41.84448242187497, 63.07026367187501],\n                            [-42.174511718749955, 63.20878906249999],\n                            [-41.63447265624998, 62.972460937500074],\n                            [-41.90898437499996, 62.73710937499999],\n                            [-42.94165039062503, 62.72021484375003],\n                            [-42.15297851562502, 62.568457031250006],\n                            [-42.32148437499998, 62.15273437500005],\n                            [-42.110205078125006, 61.857226562500074],\n                            [-42.58530273437498, 61.71748046875001],\n                            [-42.34736328125001, 61.61743164062497],\n                            [-42.717041015625, 60.767480468749994],\n                            [-43.04409179687502, 60.523681640625],\n                            [-43.92270507812495, 60.59536132812502],\n                            [-43.21298828124998, 60.390673828125074],\n                            [-43.122900390625006, 60.06123046875001],\n                            [-43.32011718749993, 59.928125],\n                            [-43.95502929687498, 60.025488281250006],\n                            [-43.65791015625001, 59.85864257812503],\n                            [-43.90654296874996, 59.815478515625045],\n                            [-44.11699218750002, 59.83193359375002],\n                            [-44.06547851562499, 59.92480468750003],\n                            [-44.412939453125006, 59.922607421875],\n                            [-44.22436523437494, 60.273535156250006],\n                            [-44.61328124999997, 60.01665039062499],\n                            [-45.37924804687495, 60.20292968750002],\n                            [-45.367773437500006, 60.37294921875002],\n                            [-44.97470703124995, 60.457226562499955],\n                            [-44.756738281249966, 60.66459960937502],\n                            [-45.38051757812494, 60.444921875],\n                            [-46.04663085937503, 60.61572265625],\n                            [-46.141943359375006, 60.776513671874994],\n                            [-45.87021484374998, 61.21831054687502],\n                            [-46.87446289062501, 60.81640625000003],\n                            [-48.180810546874966, 60.76923828125001],\n                            [-47.77031249999999, 60.99775390625001],\n                            [-48.386425781249926, 61.004736328125034],\n                            [-48.42817382812501, 61.18740234375002],\n                            [-48.92207031249998, 61.27744140624998],\n                            [-49.28906249999997, 61.58994140625006],\n                            [-49.380273437499994, 61.89018554687502],\n                            [-48.82871093749998, 62.0796875],\n                            [-49.62377929687494, 61.99858398437499],\n                            [-49.553466796875, 62.23271484374999],\n                            [-50.319238281249966, 62.473193359375045],\n                            [-50.298730468749966, 62.72197265625002],\n                            [-49.793115234374994, 63.04462890625004],\n                            [-50.39008789062501, 62.82202148437497],\n                            [-51.46884765624995, 63.64228515625001],\n                            [-51.547509765624994, 64.00610351562497],\n                            [-50.260693359374955, 64.21425781250002],\n                            [-50.48662109374996, 64.20888671875],\n                            [-50.43706054687499, 64.31284179687503],\n                            [-51.58491210937498, 64.10317382812502],\n                            [-51.70786132812498, 64.205078125],\n                            [-51.403759765624926, 64.46318359375002],\n                            [-50.49208984375002, 64.69316406250005],\n                            [-50.00898437500001, 64.44726562499997],\n                            [-50.12163085937493, 64.703759765625],\n                            [-50.51699218750002, 64.76650390625],\n                            [-50.96064453124998, 65.20112304687498],\n                            [-50.721582031249966, 64.79760742187503],\n                            [-51.22060546875002, 64.62846679687502],\n                            [-51.25537109375, 64.75810546875005],\n                            [-51.92260742187503, 64.21875],\n                            [-52.259033203125, 65.154931640625],\n                            [-52.537695312500034, 65.32880859374998],\n                            [-51.61914062500003, 65.71318359375002],\n                            [-51.091894531250006, 65.77578125],\n                            [-51.7234375, 65.723486328125],\n                            [-52.55126953125003, 65.46137695312498],\n                            [-52.760937499999926, 65.59082031249997],\n                            [-53.198974609375, 65.59404296875002],\n                            [-53.106347656249966, 65.97714843749998],\n                            [-53.39204101562498, 66.04833984375],\n                            [-51.225, 66.88154296875001],\n                            [-53.035791015624966, 66.20141601562503],\n                            [-53.538769531249955, 66.13935546874998],\n                            [-53.41875, 66.64853515624998],\n                            [-53.038281249999955, 66.82680664062497],\n                            [-52.38686523437502, 66.88115234375005],\n                            [-53.44360351562503, 66.924658203125],\n                            [-53.88442382812502, 67.13554687499999],\n                            [-53.79858398437494, 67.41816406250001],\n                            [-52.666455078124955, 67.74970703124995],\n                            [-50.613476562499955, 67.5279296875],\n                            [-51.171044921874966, 67.693603515625],\n                            [-50.96884765624998, 67.80664062500003],\n                            [-51.765234375000034, 67.73784179687505],\n                            [-52.34482421874998, 67.83691406249997],\n                            [-53.735205078125006, 67.54902343750004],\n                            [-53.151562499999926, 68.20776367187503],\n                            [-51.779980468749926, 68.05673828124998],\n                            [-51.456494140624926, 68.116064453125],\n                            [-51.21015625000001, 68.419921875],\n                            [-52.19853515624993, 68.22080078125],\n                            [-53.38315429687495, 68.29736328124997],\n                            [-53.03945312500002, 68.61088867187499],\n                            [-52.60458984374998, 68.70874023437503],\n                            [-51.62314453124995, 68.53481445312505],\n                            [-50.945703124999966, 68.68266601562505],\n                            [-50.807714843750006, 68.81699218749998],\n                            [-51.24941406250002, 68.73994140625001],\n                            [-51.084863281249994, 69.12827148437498],\n                            [-50.29736328124994, 69.17060546874998],\n                            [-51.07695312499996, 69.20947265625],\n                            [-50.291699218749955, 70.01445312500005],\n                            [-52.254638671875, 70.05893554687503],\n                            [-53.02304687499995, 70.30190429687497],\n                            [-54.01445312499996, 70.42167968750005],\n                            [-54.53076171875, 70.69926757812502],\n                            [-54.16582031249999, 70.82011718750005],\n                            [-52.801953124999955, 70.7505859375],\n                            [-50.87236328124993, 70.36489257812502],\n                            [-50.66328124999998, 70.417578125],\n                            [-51.32285156249998, 70.58876953124997],\n                            [-51.25659179687497, 70.85268554687502],\n                            [-51.77431640625002, 71.01044921875001],\n                            [-51.018945312499966, 71.001318359375],\n                            [-51.37666015625001, 71.11904296875],\n                            [-53.007568359375, 71.17998046874999],\n                            [-52.89184570312497, 71.457666015625],\n                            [-51.76992187500002, 71.67172851562498],\n                            [-53.44008789062502, 71.57900390625002],\n                            [-53.14453125000003, 71.80742187500002],\n                            [-53.65214843749996, 72.36264648437506],\n                            [-53.92773437499997, 72.31879882812501],\n                            [-53.47758789062502, 71.84995117187506],\n                            [-54.01992187500002, 71.657861328125],\n                            [-53.96298828124995, 71.45898437499997],\n                            [-54.6890625, 71.36723632812505],\n                            [-55.59404296874999, 71.55351562500005],\n                            [-55.315576171874994, 72.11069335937498],\n                            [-54.84013671874996, 72.35610351562497],\n                            [-55.581445312499994, 72.178857421875],\n                            [-55.63583984374998, 72.300439453125],\n                            [-55.29570312499996, 72.35439453124997],\n                            [-55.60170898437494, 72.453466796875],\n                            [-54.924951171874994, 72.57197265624998],\n                            [-54.737939453124994, 72.87250976562501],\n                            [-55.07309570312498, 73.01513671875003],\n                            [-55.28891601562498, 72.93320312500003],\n                            [-55.66855468749998, 73.00791015624998],\n                            [-55.288281249999955, 73.32709960937498],\n                            [-56.10405273437496, 73.55815429687499],\n                            [-55.83828125, 73.75971679687501],\n                            [-56.22539062499999, 74.12910156249995],\n                            [-57.23056640624995, 74.12529296875007],\n                            [-56.70634765625002, 74.21918945312501],\n                            [-56.717675781249994, 74.42924804687499],\n                            [-56.25546874999998, 74.52680664062498],\n                            [-58.56552734374998, 75.35273437500001],\n                            [-58.249658203124994, 75.50668945312503],\n                            [-58.51621093749995, 75.68906250000006],\n                            [-61.18823242187494, 76.157861328125],\n                            [-63.29130859374996, 76.35205078125003],\n                            [-63.84306640624999, 76.21713867187498],\n                            [-64.307275390625, 76.31650390624998],\n                            [-65.36992187499993, 76.13056640625004],\n                            [-65.87573242187494, 76.23833007812505],\n                            [-66.46577148437498, 76.13916015625],\n                            [-66.99257812500002, 76.21293945312502],\n                            [-66.67480468750003, 75.977392578125],\n                            [-68.14873046875002, 76.06704101562497],\n                            [-69.48408203125001, 76.39916992187503],\n                            [-68.1142578125, 76.65063476562503],\n                            [-69.67382812499994, 76.73588867187507],\n                            [-69.69423828125002, 76.98945312500004],\n                            [-70.613134765625, 76.82182617187499],\n                            [-71.14145507812498, 77.02866210937503],\n                            [-70.86284179687496, 77.175439453125],\n                            [-68.97832031250002, 77.19531250000006],\n                            [-68.13554687499999, 77.37958984375001],\n                            [-66.38945312499999, 77.28027343750003],\n                            [-66.69121093749999, 77.68120117187502],\n                            [-67.68808593749995, 77.523779296875],\n                            [-68.62153320312498, 77.60185546875002],\n                            [-69.35136718749999, 77.467138671875],\n                            [-70.53540039062497, 77.699560546875],\n                            [-70.11445312500001, 77.84135742187505],\n                            [-71.27163085937494, 77.81313476562497],\n                            [-72.81806640624995, 78.1943359375],\n                            [-72.47250976562498, 78.48203125],\n                            [-71.65131835937493, 78.62314453124998],\n                            [-68.99345703124999, 78.857421875],\n                            [-68.37705078124998, 79.037841796875],\n                            [-65.82553710937503, 79.17373046874997],\n                            [-64.79228515624993, 80.00063476562502],\n                            [-64.17915039062498, 80.09926757812497],\n                            [-66.84365234374997, 80.07622070312507],\n                            [-67.05063476562503, 80.384521484375],\n                            [-64.51552734374997, 81],\n                            [-63.72197265624993, 81.05732421875001],\n                            [-63.028662109375006, 80.88955078125002],\n                            [-62.90336914062496, 81.21835937500003],\n                            [-61.43598632812498, 81.13359375000002],\n                            [-60.842871093750034, 81.85537109374997],\n                            [-59.28193359374998, 81.88403320312503],\n                            [-56.615136718749994, 81.362890625],\n                            [-59.26181640624998, 82.00664062500005],\n                            [-54.54887695312496, 82.35063476562505],\n                            [-53.671337890624955, 82.16406249999997],\n                            [-53.55566406250003, 81.65327148437501],\n                            [-53.022558593750034, 82.32172851562504],\n                            [-50.894433593749994, 81.89521484375001],\n                            [-49.54106445312496, 81.91806640625003],\n                            [-50.93554687500003, 82.38281250000003],\n                            [-50.03710937499994, 82.472412109375],\n                            [-44.7294921875, 81.77983398437505],\n                            [-44.23886718749998, 82.3681640625],\n                            [-45.55654296875002, 82.74702148437498],\n                            [-41.87646484375, 82.680322265625],\n                            [-41.36962890625003, 82.75],\n                            [-46.136816406250006, 82.85883789062504],\n                            [-46.169042968750006, 83.06386718749997],\n                            [-45.41459960937496, 83.01767578124998],\n                            [-43.00927734375003, 83.26459960937501],\n                            [-41.300146484375006, 83.10078125000004],\n                            [-40.35683593750002, 83.332177734375],\n                            [-38.15625, 82.9986328125],\n                            [-38.74956054687496, 83.37084960937497],\n                            [-37.72333984374998, 83.49775390624998],\n                            [-29.952880859375, 83.56484374999997]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Greenland\", \"childNum\": 14 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-89.2328125, 15.888671875],\n                        [-88.89404296875, 15.890625],\n                        [-88.60336914062499, 15.76416015625],\n                        [-88.5939453125, 15.950292968749991],\n                        [-88.22832031249999, 15.72900390625],\n                        [-88.271435546875, 15.694873046875003],\n                        [-88.36455078124999, 15.616015625],\n                        [-88.68447265625, 15.360498046874994],\n                        [-88.96098632812499, 15.152441406249991],\n                        [-89.142578125, 15.072314453125003],\n                        [-89.22236328125, 14.866064453124991],\n                        [-89.16220703124999, 14.669238281250003],\n                        [-89.17177734375, 14.606884765624997],\n                        [-89.28671875, 14.529980468749997],\n                        [-89.36259765624999, 14.416015625],\n                        [-89.5736328125, 14.390087890624997],\n                        [-89.54716796874999, 14.241259765625003],\n                        [-90.04814453124999, 13.904052734375],\n                        [-90.09521484375, 13.736523437499997],\n                        [-90.60693359375, 13.929003906250003],\n                        [-91.37734375, 13.990185546874997],\n                        [-92.23515624999999, 14.54541015625],\n                        [-92.15854492187499, 14.963574218749997],\n                        [-92.14423828125, 15.001953125],\n                        [-92.09873046874999, 15.026757812499994],\n                        [-92.07480468749999, 15.07421875],\n                        [-92.187158203125, 15.320898437499991],\n                        [-92.08212890624999, 15.495556640624997],\n                        [-91.9572265625, 15.703222656249991],\n                        [-91.736572265625, 16.07016601562499],\n                        [-91.433984375, 16.070458984374994],\n                        [-90.97958984374999, 16.07080078125],\n                        [-90.70322265624999, 16.071044921875],\n                        [-90.52197265625, 16.071191406249994],\n                        [-90.44716796875, 16.072705078124997],\n                        [-90.45986328125, 16.162353515625],\n                        [-90.450146484375, 16.261376953124994],\n                        [-90.4169921875, 16.351318359375],\n                        [-90.4169921875, 16.39101562499999],\n                        [-90.47109375, 16.43955078124999],\n                        [-90.57578125, 16.467822265625003],\n                        [-90.63408203124999, 16.5107421875],\n                        [-90.634375, 16.565136718749997],\n                        [-90.65996093749999, 16.630908203125003],\n                        [-90.710693359375, 16.70810546874999],\n                        [-90.975830078125, 16.867822265624994],\n                        [-91.409619140625, 17.255859375],\n                        [-91.1955078125, 17.254101562499997],\n                        [-90.99296874999999, 17.25244140625],\n                        [-90.98916015625, 17.81640625],\n                        [-89.16147460937499, 17.81484375],\n                        [-89.2328125, 15.888671875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Guatemala\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [144.74179687500003, 13.25927734375],\n                        [144.64931640625002, 13.4287109375],\n                        [144.87539062500002, 13.614648437499994],\n                        [144.94082031250002, 13.5703125],\n                        [144.74179687500003, 13.25927734375]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Guam\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-57.194775390625, 5.5484375],\n                        [-57.3185546875, 5.335351562499994],\n                        [-57.20981445312499, 5.195410156249991],\n                        [-57.331005859375, 5.020166015624994],\n                        [-57.711083984374994, 4.991064453124991],\n                        [-57.91704101562499, 4.820410156249991],\n                        [-57.84599609374999, 4.668164062499997],\n                        [-58.05429687499999, 4.101660156249991],\n                        [-57.646728515625, 3.39453125],\n                        [-57.303662109375, 3.377099609374994],\n                        [-57.19736328124999, 2.853271484375],\n                        [-56.704345703125, 2.036474609374991],\n                        [-56.4828125, 1.942138671875],\n                        [-56.96953124999999, 1.91640625],\n                        [-57.03759765625, 1.936474609374997],\n                        [-57.092675781249994, 2.005810546874997],\n                        [-57.118896484375, 2.013964843749989],\n                        [-57.31748046874999, 1.963476562499991],\n                        [-57.41269531249999, 1.908935546875],\n                        [-57.500439453125, 1.77382812499999],\n                        [-57.54575195312499, 1.72607421875],\n                        [-57.59443359375, 1.7041015625],\n                        [-57.795654296875, 1.7],\n                        [-57.8734375, 1.667285156249989],\n                        [-57.9828125, 1.6484375],\n                        [-58.03466796875, 1.520263671875],\n                        [-58.34067382812499, 1.587548828124994],\n                        [-58.38037109375, 1.530224609374997],\n                        [-58.39580078124999, 1.481738281249989],\n                        [-58.5060546875, 1.438671875],\n                        [-58.511865234374994, 1.28466796875],\n                        [-58.68461914062499, 1.281054687499989],\n                        [-58.73032226562499, 1.247509765624997],\n                        [-58.78720703124999, 1.20849609375],\n                        [-58.82177734375, 1.201220703124989],\n                        [-59.231201171875, 1.376025390624989],\n                        [-59.53569335937499, 1.7],\n                        [-59.66660156249999, 1.746289062499997],\n                        [-59.66850585937499, 1.842333984374989],\n                        [-59.74072265625, 1.874169921874994],\n                        [-59.75620117187499, 1.900634765625],\n                        [-59.75522460937499, 2.274121093749997],\n                        [-59.8896484375, 2.362939453124994],\n                        [-59.9943359375, 2.689990234374989],\n                        [-59.854394531249994, 3.5875],\n                        [-59.55112304687499, 3.933544921874997],\n                        [-59.557763671874994, 3.960009765624989],\n                        [-59.62021484374999, 4.023144531249997],\n                        [-59.73857421874999, 4.226757812499997],\n                        [-59.69970703125, 4.353515625],\n                        [-60.1486328125, 4.533251953124989],\n                        [-59.990673828125, 5.082861328124991],\n                        [-60.142041015625, 5.238818359374989],\n                        [-60.241650390625, 5.257958984374994],\n                        [-60.335205078125, 5.199316406249991],\n                        [-60.45952148437499, 5.188085937499991],\n                        [-60.6513671875, 5.221142578124997],\n                        [-60.742138671875, 5.202050781249994],\n                        [-61.37680664062499, 5.906982421875],\n                        [-61.3908203125, 5.938769531249989],\n                        [-61.303125, 6.049511718749997],\n                        [-61.22495117187499, 6.129199218749989],\n                        [-61.15947265624999, 6.174414062499991],\n                        [-61.12871093749999, 6.214306640624997],\n                        [-61.152294921875, 6.385107421874991],\n                        [-61.151025390624994, 6.446533203125],\n                        [-61.181591796875, 6.513378906249997],\n                        [-61.20361328125, 6.58837890625],\n                        [-61.14560546874999, 6.69453125],\n                        [-60.717919921874994, 6.768310546875],\n                        [-60.35209960937499, 7.002880859374997],\n                        [-60.32207031249999, 7.092041015625],\n                        [-60.32548828124999, 7.133984375],\n                        [-60.34506835937499, 7.15],\n                        [-60.46494140624999, 7.166552734374989],\n                        [-60.523193359375, 7.143701171874994],\n                        [-60.583203125, 7.156201171874997],\n                        [-60.63330078125, 7.211083984374994],\n                        [-60.718652343749994, 7.535937499999989],\n                        [-60.513623046875, 7.813183593749997],\n                        [-60.032421875, 8.053564453124991],\n                        [-59.99072265625, 8.162011718749994],\n                        [-59.96484375, 8.191601562499997],\n                        [-59.849072265625, 8.248681640624994],\n                        [-59.83164062499999, 8.305957031249989],\n                        [-60.017529296875, 8.54931640625],\n                        [-59.20024414062499, 8.07460937499999],\n                        [-58.51108398437499, 7.39804687499999],\n                        [-58.48056640624999, 7.038134765624989],\n                        [-58.67294921874999, 6.390771484374994],\n                        [-58.414990234375, 6.85117187499999],\n                        [-57.982568359374994, 6.785888671875],\n                        [-57.54013671874999, 6.33154296875],\n                        [-57.2275390625, 6.178417968749997],\n                        [-57.194775390625, 5.5484375]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Guyana\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [73.70742187500002, -53.13710937499999],\n                        [73.46513671875002, -53.184179687500006],\n                        [73.25117187500001, -52.97578125000001],\n                        [73.83779296875002, -53.11279296875],\n                        [73.70742187500002, -53.13710937499999]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Heard I. and McDonald Is.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-83.635498046875, 14.876416015624997],\n                        [-84.53764648437496, 14.633398437499963],\n                        [-84.64594726562498, 14.661083984375011],\n                        [-84.86044921874998, 14.809765625000011],\n                        [-84.98515624999999, 14.752441406249972],\n                        [-85.059375, 14.582958984374997],\n                        [-85.20834960937498, 14.311816406250003],\n                        [-85.73393554687496, 13.85869140625006],\n                        [-85.75341796875, 13.852050781250028],\n                        [-85.78671874999995, 13.844433593749997],\n                        [-85.98378906249997, 13.965673828125006],\n                        [-86.04038085937503, 14.050146484374977],\n                        [-86.33173828124995, 13.770068359375031],\n                        [-86.37695312500003, 13.755664062500031],\n                        [-86.61025390624997, 13.774853515625026],\n                        [-86.73364257812494, 13.763476562500017],\n                        [-86.75898437499995, 13.746142578125045],\n                        [-86.77060546875003, 13.698730468749972],\n                        [-86.763525390625, 13.635253906250014],\n                        [-86.72958984375, 13.4072265625],\n                        [-86.710693359375, 13.31337890624998],\n                        [-86.72929687499996, 13.284375],\n                        [-86.79213867187497, 13.279785156249972],\n                        [-86.87353515624994, 13.266503906250023],\n                        [-86.918212890625, 13.223583984374983],\n                        [-87.00932617187499, 13.007812499999986],\n                        [-87.0591796875, 12.991455078125028],\n                        [-87.337255859375, 12.979248046875028],\n                        [-87.48911132812503, 13.352929687500051],\n                        [-87.814208984375, 13.399169921875057],\n                        [-87.781884765625, 13.521386718749994],\n                        [-87.71533203125003, 13.812695312500011],\n                        [-87.73144531250003, 13.841064453125014],\n                        [-87.80224609374997, 13.889990234375034],\n                        [-87.89199218749997, 13.894970703124983],\n                        [-87.99101562499996, 13.879638671874972],\n                        [-88.15102539062497, 13.987353515624974],\n                        [-88.44912109374994, 13.850976562499994],\n                        [-88.48266601562503, 13.854248046875043],\n                        [-88.49765624999998, 13.904541015624986],\n                        [-88.50434570312501, 13.964208984374963],\n                        [-88.51254882812498, 13.97895507812504],\n                        [-89.12050781249994, 14.370214843749991],\n                        [-89.36259765624996, 14.416015625],\n                        [-89.17177734375, 14.606884765624983],\n                        [-89.16220703125, 14.669238281249989],\n                        [-89.22236328125001, 14.86606445312502],\n                        [-89.142578125, 15.072314453125031],\n                        [-88.96098632812496, 15.15244140625002],\n                        [-88.68447265625002, 15.360498046875037],\n                        [-88.36455078124996, 15.616015625000045],\n                        [-88.27143554687498, 15.694873046875045],\n                        [-88.22832031249999, 15.729003906249972],\n                        [-88.131103515625, 15.701025390625034],\n                        [-87.87495117187495, 15.879345703124955],\n                        [-86.35664062499998, 15.783203125],\n                        [-85.93627929687497, 15.953417968750045],\n                        [-85.98564453124999, 16.02416992187497],\n                        [-85.48369140624996, 15.899511718749977],\n                        [-84.97373046874998, 15.989892578124994],\n                        [-84.55966796875, 15.802001953125],\n                        [-84.26142578124998, 15.822607421875034],\n                        [-83.765283203125, 15.405468750000054],\n                        [-83.972802734375, 15.519628906250034],\n                        [-84.11132812499997, 15.492431640625],\n                        [-84.09506835937503, 15.400927734375017],\n                        [-83.92744140624998, 15.394042968750028],\n                        [-83.76044921874998, 15.220361328124994],\n                        [-83.49794921874997, 15.222119140624997],\n                        [-83.64638671875, 15.368408203125043],\n                        [-83.36918945312493, 15.239990234375],\n                        [-83.29086914062498, 15.078906250000045],\n                        [-83.2255859375, 15.042285156250045],\n                        [-83.15751953124999, 14.993066406249966],\n                        [-83.41503906249994, 15.008056640625],\n                        [-83.5365234375, 14.977001953124983],\n                        [-83.635498046875, 14.876416015624997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Honduras\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [17.60781250000005, 42.76904296875],\n                            [17.744238281250063, 42.70034179687505],\n                            [17.34414062500008, 42.790380859375006],\n                            [17.60781250000005, 42.76904296875]\n                        ]\n                    ],\n                    [\n                        [\n                            [16.650683593750017, 42.99658203125],\n                            [17.188281250000045, 42.917041015625045],\n                            [16.850683593750006, 42.8955078125],\n                            [16.650683593750017, 42.99658203125]\n                        ]\n                    ],\n                    [\n                        [\n                            [17.667578125000063, 42.897119140624994],\n                            [18.436328125000017, 42.559716796874994],\n                            [18.517480468750023, 42.43291015624999],\n                            [17.823828125, 42.79741210937502],\n                            [17.045410156250057, 43.014892578125],\n                            [17.667578125000063, 42.897119140624994]\n                        ]\n                    ],\n                    [\n                        [\n                            [16.785253906250006, 43.270654296874966],\n                            [16.490332031250034, 43.28618164062502],\n                            [16.44892578125004, 43.38706054687506],\n                            [16.89130859375001, 43.314648437499955],\n                            [16.785253906250006, 43.270654296874966]\n                        ]\n                    ],\n                    [\n                        [\n                            [15.371386718750074, 43.973828124999955],\n                            [15.437207031250068, 43.899511718750006],\n                            [15.270019531250028, 44.01074218750003],\n                            [15.371386718750074, 43.973828124999955]\n                        ]\n                    ],\n                    [\n                        [\n                            [14.488085937500074, 44.66005859375005],\n                            [14.31240234375008, 44.90039062499997],\n                            [14.33125, 45.16499023437498],\n                            [14.488085937500074, 44.66005859375005]\n                        ]\n                    ],\n                    [\n                        [\n                            [14.810253906250068, 44.97705078124997],\n                            [14.45039062500004, 45.079199218750006],\n                            [14.571093750000017, 45.224755859374994],\n                            [14.810253906250068, 44.97705078124997]\n                        ]\n                    ],\n                    [\n                        [\n                            [18.905371093750006, 45.931738281250034],\n                            [18.839062499999983, 45.83574218750002],\n                            [19.064257812500045, 45.51499023437506],\n                            [19.004687500000074, 45.39951171875006],\n                            [19.4, 45.2125],\n                            [19.062890625000023, 45.13720703125],\n                            [19.007128906250045, 44.86918945312502],\n                            [18.83642578125, 44.883251953124955],\n                            [18.66259765625, 45.07744140624999],\n                            [17.812792968750074, 45.078125],\n                            [16.918652343749983, 45.27656249999998],\n                            [16.53066406250008, 45.21669921875002],\n                            [16.29335937500005, 45.00883789062496],\n                            [16.028320312500057, 45.18959960937502],\n                            [15.788085937500057, 45.17895507812497],\n                            [15.736621093750045, 44.76582031250001],\n                            [16.10341796875008, 44.52099609375006],\n                            [16.300097656250017, 44.12451171875],\n                            [17.27382812500005, 43.44575195312501],\n                            [17.650488281250063, 43.006591796875],\n                            [17.585156250000068, 42.93837890625005],\n                            [16.903125, 43.392431640625006],\n                            [16.393945312500023, 43.54335937500002],\n                            [15.985546875000068, 43.519775390625],\n                            [15.185839843750017, 44.17211914062503],\n                            [15.122949218749994, 44.256787109374955],\n                            [15.470996093750045, 44.27197265625003],\n                            [14.981347656250023, 44.60292968750005],\n                            [14.854589843750034, 45.08100585937501],\n                            [14.550488281249983, 45.297705078125006],\n                            [14.31269531250004, 45.33779296875002],\n                            [13.86074218750008, 44.83740234375003],\n                            [13.517187500000063, 45.481787109375034],\n                            [13.878710937500017, 45.428369140624994],\n                            [14.369921875000074, 45.48144531250006],\n                            [14.427343750000034, 45.50576171875002],\n                            [14.56884765625, 45.65722656249997],\n                            [14.591796875000057, 45.65126953125002],\n                            [14.649511718750006, 45.57148437500001],\n                            [14.793066406250034, 45.47822265625001],\n                            [14.95458984375, 45.499902343749994],\n                            [15.110449218750034, 45.450781250000034],\n                            [15.242089843750023, 45.44140624999997],\n                            [15.339453125000063, 45.46704101562506],\n                            [15.326660156250028, 45.502294921875034],\n                            [15.291210937500011, 45.541552734375045],\n                            [15.283593750000051, 45.5796875],\n                            [15.35371093750004, 45.659912109375],\n                            [15.27705078125004, 45.73261718749998],\n                            [15.652148437500074, 45.86215820312498],\n                            [15.675585937500045, 45.98369140624996],\n                            [15.666210937500011, 46.04848632812502],\n                            [15.596875, 46.10922851562506],\n                            [15.592578125000017, 46.139990234375006],\n                            [15.608984374999977, 46.171923828125045],\n                            [16.1064453125, 46.382226562499994],\n                            [16.32119140625005, 46.53461914062504],\n                            [16.42763671875005, 46.5244140625],\n                            [16.516210937499977, 46.499902343749966],\n                            [16.569921875, 46.48500976562505],\n                            [16.748046875000057, 46.41640625000002],\n                            [16.87148437500008, 46.33930664062504],\n                            [17.310644531250006, 45.99614257812502],\n                            [17.80712890625, 45.79042968750002],\n                            [18.358300781250023, 45.75302734375006],\n                            [18.533593750000023, 45.79614257812503],\n                            [18.56464843750004, 45.81328124999999],\n                            [18.666015625, 45.90747070312497],\n                            [18.905371093750006, 45.931738281250034]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Croatia\", \"childNum\": 8 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-72.80458984374997, 18.777685546875063],\n                            [-72.82221679687501, 18.707128906249977],\n                            [-73.07797851562498, 18.790917968749994],\n                            [-73.27641601562499, 18.95405273437501],\n                            [-72.80458984374997, 18.777685546875063]\n                        ]\n                    ],\n                    [\n                        [\n                            [-71.647216796875, 19.195947265624994],\n                            [-71.80712890624997, 18.987011718749983],\n                            [-71.733642578125, 18.85639648437501],\n                            [-71.72705078125, 18.80322265625003],\n                            [-71.74321289062502, 18.73291015625],\n                            [-71.86650390624999, 18.61416015625005],\n                            [-71.98686523437499, 18.61035156249997],\n                            [-72.000390625, 18.59790039062503],\n                            [-71.94038085937493, 18.51259765625005],\n                            [-71.87255859374997, 18.416210937499955],\n                            [-71.76191406249998, 18.34130859374997],\n                            [-71.73725585937495, 18.27080078124999],\n                            [-71.76831054687497, 18.039160156250063],\n                            [-71.85292968749997, 18.119140625],\n                            [-71.94609375, 18.186083984375045],\n                            [-72.05986328124993, 18.228564453125017],\n                            [-72.87666015624998, 18.151757812499994],\n                            [-73.38515625000002, 18.251171874999983],\n                            [-73.747314453125, 18.190234375000017],\n                            [-73.88496093749998, 18.041894531249994],\n                            [-74.478125, 18.45],\n                            [-74.3875, 18.624707031249983],\n                            [-74.22773437499998, 18.662695312499977],\n                            [-72.78935546874996, 18.434814453125],\n                            [-72.37607421874998, 18.57446289062503],\n                            [-72.34765624999994, 18.674951171874994],\n                            [-72.81108398437496, 19.071582031250074],\n                            [-72.70322265625, 19.441064453125023],\n                            [-73.43837890624994, 19.722119140624983],\n                            [-73.21777343750003, 19.88369140625005],\n                            [-72.63701171875002, 19.90087890625],\n                            [-72.21982421875003, 19.744628906250057],\n                            [-71.834716796875, 19.696728515624983],\n                            [-71.77924804687498, 19.718164062499994],\n                            [-71.75742187499998, 19.68818359375001],\n                            [-71.71147460937493, 19.486572265625057],\n                            [-71.74648437499997, 19.28583984375001],\n                            [-71.647216796875, 19.195947265624994]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Haiti\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [22.1318359375, 48.405322265624996],\n                        [22.253710937500017, 48.407373046874994],\n                        [22.582421875000023, 48.134033203125],\n                        [22.769140625, 48.109619140625],\n                        [22.87666015625001, 47.947265625],\n                        [21.99970703125001, 47.505029296874994],\n                        [21.121679687500006, 46.282421875],\n                        [20.76025390625, 46.246240234374994],\n                        [20.613671875000023, 46.13349609375],\n                        [20.508105468750017, 46.166943359375],\n                        [20.28095703125001, 46.1330078125],\n                        [20.241796875, 46.10859375],\n                        [20.21015625000001, 46.126025390624996],\n                        [20.161425781250017, 46.141894531249996],\n                        [19.93408203125, 46.161474609375],\n                        [19.84443359375001, 46.145898437499994],\n                        [19.61347656250001, 46.169189453125],\n                        [19.421289062500023, 46.064453125],\n                        [18.666015625, 45.907470703125],\n                        [18.56464843750001, 45.81328125],\n                        [18.533593750000023, 45.796142578125],\n                        [18.358300781250023, 45.75302734375],\n                        [17.80712890625, 45.790429687499994],\n                        [17.310644531250006, 45.996142578124996],\n                        [16.871484375000023, 46.339306640625],\n                        [16.748046875, 46.41640625],\n                        [16.569921875, 46.485009765624994],\n                        [16.516210937500006, 46.499902343749994],\n                        [16.283593750000023, 46.857275390625],\n                        [16.093066406250017, 46.86328125],\n                        [16.453417968750017, 47.006787109375],\n                        [16.44287109375, 47.39951171875],\n                        [16.676562500000017, 47.536035156249994],\n                        [16.421289062500023, 47.674462890624994],\n                        [17.06660156250001, 47.707568359374996],\n                        [17.147363281250023, 48.00595703125],\n                        [17.76191406250001, 47.770166015624994],\n                        [18.72421875, 47.787158203124996],\n                        [18.791894531250023, 48.000292968749996],\n                        [19.625390625000023, 48.223095703125],\n                        [19.95039062500001, 48.146630859374994],\n                        [20.333789062500017, 48.295556640624994],\n                        [20.490039062500017, 48.526904296874996],\n                        [21.45136718750001, 48.55224609375],\n                        [21.766992187500023, 48.3380859375],\n                        [22.1318359375, 48.405322265624996]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Hungary\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [122.9489257812501, -10.90927734375002],\n                            [122.82617187500003, -10.899121093749983],\n                            [122.84570312500003, -10.761816406249991],\n                            [123.37109375000003, -10.474902343749989],\n                            [123.41816406250004, -10.651269531250037],\n                            [122.9489257812501, -10.90927734375002]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.8830078125001, -10.590332031249957],\n                            [121.70468750000006, -10.5556640625],\n                            [121.99833984375002, -10.446972656249983],\n                            [121.8830078125001, -10.590332031249957]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.41621093750004, -10.302636718749966],\n                            [123.3255859375, -10.264160156249943],\n                            [123.45878906250002, -10.13994140624996],\n                            [123.41621093750004, -10.302636718749966]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.0125, -9.374707031250026],\n                            [120.78447265625002, -9.95703125],\n                            [120.83261718750006, -10.0375],\n                            [120.69804687500002, -10.206640624999949],\n                            [120.4391601562501, -10.294042968749991],\n                            [120.14482421875002, -10.200097656249952],\n                            [119.60107421874997, -9.773535156250006],\n                            [119.08544921875003, -9.706933593750023],\n                            [118.95878906250002, -9.519335937500003],\n                            [119.29589843749997, -9.3671875],\n                            [119.9420898437501, -9.301464843750026],\n                            [120.0125, -9.374707031250026]\n                        ]\n                    ],\n                    [\n                        [\n                            [125.06816406250002, -9.511914062499997],\n                            [124.42753906250002, -10.14863281250004],\n                            [123.7472656250001, -10.347167968749986],\n                            [123.60478515625002, -10.270117187500006],\n                            [123.71640625000012, -10.078613281249986],\n                            [123.5892578125, -9.966796875000028],\n                            [123.709375, -9.61484375],\n                            [124.0363281250001, -9.341601562500031],\n                            [124.28232421875012, -9.427929687500026],\n                            [124.44443359375012, -9.190332031250023],\n                            [124.92226562500005, -8.942480468749977],\n                            [124.93681640625007, -9.053417968750026],\n                            [125.14902343750012, -9.042578125000034],\n                            [125.10048828125, -9.189843750000023],\n                            [124.96015625000004, -9.213769531250009],\n                            [125.06816406250002, -9.511914062499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.60996093750012, -8.769824218749974],\n                            [115.48046875000003, -8.715429687500006],\n                            [115.56142578125, -8.669921874999972],\n                            [115.60996093750012, -8.769824218749974]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.97734375000002, -8.54521484374996],\n                            [122.88779296875006, -8.587304687500009],\n                            [123.01054687500002, -8.448339843750034],\n                            [123.153125, -8.475781250000026],\n                            [122.97734375000002, -8.54521484374996]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.46406250000004, -8.741015624999974],\n                            [119.38554687500002, -8.736035156250026],\n                            [119.4464843750001, -8.429199218749957],\n                            [119.55722656250012, -8.518847656250003],\n                            [119.46406250000004, -8.741015624999974]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.31748046875012, -8.354785156249974],\n                            [123.02500000000012, -8.395507812500014],\n                            [123.21708984375002, -8.235449218750006],\n                            [123.33603515625006, -8.269042968750014],\n                            [123.31748046875012, -8.354785156249974]\n                        ]\n                    ],\n                    [\n                        [\n                            [116.64082031250004, -8.613867187500006],\n                            [116.51425781250012, -8.820996093750011],\n                            [116.58652343750012, -8.886132812499966],\n                            [116.23935546875006, -8.912109375000014],\n                            [115.85732421875005, -8.787890625000017],\n                            [116.07646484375002, -8.744921874999974],\n                            [116.06113281250006, -8.437402343750023],\n                            [116.4015625000001, -8.204199218750034],\n                            [116.7189453125001, -8.336035156249977],\n                            [116.64082031250004, -8.613867187500006]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.28662109375003, -8.32949218749998],\n                            [124.14667968750004, -8.531445312499997],\n                            [123.92773437500003, -8.448925781249969],\n                            [124.23955078125002, -8.20341796874996],\n                            [124.28662109375003, -8.32949218749998]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.92480468750003, -8.2724609375],\n                            [123.55302734375007, -8.566796875],\n                            [123.23007812500006, -8.530664062500023],\n                            [123.47587890625007, -8.322265625000014],\n                            [123.39121093750012, -8.280468750000026],\n                            [123.77597656250006, -8.190429687499986],\n                            [123.92480468750003, -8.2724609375]\n                        ]\n                    ],\n                    [\n                        [\n                            [138.89511718750006, -8.388671874999957],\n                            [138.56337890625, -8.30908203125],\n                            [138.79619140625007, -8.173632812500017],\n                            [138.89511718750006, -8.388671874999957]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.55634765625004, -8.367285156249949],\n                            [117.49052734375007, -8.183398437499974],\n                            [117.66503906249997, -8.148242187500003],\n                            [117.55634765625004, -8.367285156249949]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.5755859375, -8.140820312499997],\n                            [125.05029296874997, -8.179589843749994],\n                            [125.13173828125, -8.326464843749989],\n                            [124.38066406250002, -8.41513671875002],\n                            [124.43066406249997, -8.18320312500002],\n                            [124.5755859375, -8.140820312499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [127.8234375000001, -8.098828124999969],\n                            [128.11923828125012, -8.17070312499996],\n                            [128.02353515625006, -8.255371093749972],\n                            [127.82089843750012, -8.190234375000031],\n                            [127.8234375000001, -8.098828124999969]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.7829101562501, -8.61171875],\n                            [121.65136718749997, -8.898730468749946],\n                            [121.41464843750006, -8.81484375],\n                            [121.32832031250004, -8.916894531250009],\n                            [121.03525390625012, -8.935449218749966],\n                            [120.55048828125004, -8.80185546875002],\n                            [119.909375, -8.857617187500011],\n                            [119.80791015625002, -8.697656250000023],\n                            [119.87480468750007, -8.419824218749994],\n                            [120.61025390625005, -8.24042968750004],\n                            [121.44453125000004, -8.57783203125004],\n                            [121.96650390625004, -8.455175781250006],\n                            [122.32324218749997, -8.628320312500023],\n                            [122.85048828125, -8.304394531250011],\n                            [122.91914062500004, -8.221875],\n                            [122.75859375000002, -8.185937499999952],\n                            [122.91699218749997, -8.105566406250006],\n                            [123.00595703125006, -8.329101562499986],\n                            [122.7829101562501, -8.61171875]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.86220703125, -8.31875],\n                            [130.77519531250002, -8.34990234374996],\n                            [131.02011718750012, -8.091308593749943],\n                            [131.17636718750006, -8.130761718749994],\n                            [130.86220703125, -8.31875]\n                        ]\n                    ],\n                    [\n                        [\n                            [118.24238281250004, -8.317773437499994],\n                            [118.61191406250006, -8.28066406249998],\n                            [118.71386718749997, -8.41494140624998],\n                            [118.926171875, -8.297656249999974],\n                            [119.12968750000002, -8.668164062499969],\n                            [118.74589843750002, -8.735449218749991],\n                            [118.83261718750012, -8.833398437499966],\n                            [118.47861328125012, -8.856445312499957],\n                            [118.37890625000003, -8.674609375000031],\n                            [118.18994140624997, -8.840527343749997],\n                            [117.06132812500002, -9.099023437499994],\n                            [116.78847656250005, -9.006347656250028],\n                            [116.83505859375012, -8.532421875000026],\n                            [117.16484375000007, -8.367187500000014],\n                            [117.56708984375004, -8.426367187499991],\n                            [117.80605468750005, -8.711132812500011],\n                            [117.96953125000002, -8.728027343749986],\n                            [118.23486328124997, -8.591894531249963],\n                            [117.81484375000005, -8.342089843749974],\n                            [117.7552734375, -8.149511718749991],\n                            [118.11748046875007, -8.12226562500004],\n                            [118.24238281250004, -8.317773437499994]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.44785156250012, -8.155175781249994],\n                            [115.70429687500004, -8.40712890624998],\n                            [115.14492187500005, -8.849023437500037],\n                            [115.05507812500005, -8.573046874999946],\n                            [114.61318359375, -8.37832031249998],\n                            [114.46757812500007, -8.166308593749946],\n                            [114.93847656249997, -8.18710937500002],\n                            [115.15400390625004, -8.065722656249974],\n                            [115.44785156250012, -8.155175781249994]\n                        ]\n                    ],\n                    [\n                        [\n                            [129.83886718749997, -7.954589843749986],\n                            [129.71347656250012, -8.04072265625004],\n                            [129.60898437500006, -7.803417968750011],\n                            [129.81298828124997, -7.819726562499952],\n                            [129.83886718749997, -7.954589843749986]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.80097656250004, -7.667871093750009],\n                            [126.4720703125, -7.950390625000011],\n                            [126.04003906250003, -7.885839843750006],\n                            [125.79824218750005, -7.984570312499969],\n                            [125.97529296875004, -7.663378906249989],\n                            [126.21367187500002, -7.706738281250026],\n                            [126.60957031250004, -7.571777343749972],\n                            [126.80097656250004, -7.667871093750009]\n                        ]\n                    ],\n                    [\n                        [\n                            [127.41943359375003, -7.623046875000028],\n                            [127.37070312500012, -7.512792968749949],\n                            [127.47519531250012, -7.531054687500031],\n                            [127.41943359375003, -7.623046875000028]\n                        ]\n                    ],\n                    [\n                        [\n                            [138.53535156250004, -8.273632812499969],\n                            [138.2962890625, -8.405175781250037],\n                            [137.65039062499997, -8.386132812499966],\n                            [138.08183593750002, -7.566210937500003],\n                            [138.29550781250012, -7.4384765625],\n                            [138.76982421875002, -7.390429687499974],\n                            [138.98906250000002, -7.696093749999989],\n                            [138.53535156250004, -8.273632812499969]\n                        ]\n                    ],\n                    [\n                        [\n                            [131.3255859375, -7.999511718749986],\n                            [131.11376953125003, -7.997363281249989],\n                            [131.13779296875012, -7.684863281250017],\n                            [131.64345703125, -7.11279296875],\n                            [131.73613281250007, -7.197070312500017],\n                            [131.64384765625002, -7.266894531249946],\n                            [131.62441406250005, -7.626171874999955],\n                            [131.3255859375, -7.999511718749986]\n                        ]\n                    ],\n                    [\n                        [\n                            [131.98203125000006, -7.202050781249966],\n                            [131.75078125000002, -7.116796875],\n                            [131.92226562500005, -7.104492187499986],\n                            [131.98203125000006, -7.202050781249966]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.6701171875001, -7.183300781249969],\n                            [128.52978515625003, -7.134570312499989],\n                            [128.62773437500007, -7.06875],\n                            [128.6701171875001, -7.183300781249969]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.77441406250003, -7.118945312500003],\n                            [120.64082031250004, -7.115820312499991],\n                            [120.63339843750006, -7.018261718750011],\n                            [120.77441406250003, -7.118945312500003]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.84453125000007, -7.105371093749994],\n                            [113.12695312499997, -7.224121093750028],\n                            [112.72587890625007, -7.072753906250014],\n                            [112.86806640625, -6.899902343749972],\n                            [113.06738281250003, -6.879980468749991],\n                            [113.97470703125012, -6.873046875],\n                            [114.0736328125, -6.960156249999983],\n                            [113.84453125000007, -7.105371093749994]\n                        ]\n                    ],\n                    [\n                        [\n                            [115.37705078125006, -6.97080078125002],\n                            [115.22031250000012, -6.952539062500037],\n                            [115.24052734375007, -6.861230468749994],\n                            [115.54609375000004, -6.938671874999955],\n                            [115.37705078125006, -6.97080078125002]\n                        ]\n                    ],\n                    [\n                        [\n                            [105.25283203125005, -6.640429687499946],\n                            [105.12138671875007, -6.614941406249997],\n                            [105.26054687500002, -6.523925781250014],\n                            [105.25283203125005, -6.640429687499946]\n                        ]\n                    ],\n                    [\n                        [\n                            [134.53681640625004, -6.442285156249994],\n                            [134.32275390624997, -6.84873046875002],\n                            [134.09082031249997, -6.833789062500003],\n                            [134.10703125000006, -6.471582031250009],\n                            [134.19462890625007, -6.459765625],\n                            [134.11464843750005, -6.190820312500009],\n                            [134.53681640625004, -6.442285156249994]\n                        ]\n                    ],\n                    [\n                        [\n                            [107.37392578125005, -6.007617187499989],\n                            [107.66679687500002, -6.215820312499957],\n                            [108.33017578125012, -6.286035156249966],\n                            [108.67783203125006, -6.790527343749972],\n                            [110.42626953124997, -6.947265625000028],\n                            [110.83476562500002, -6.424218749999952],\n                            [110.97226562500012, -6.435644531249977],\n                            [111.18154296875005, -6.686718749999969],\n                            [111.54033203125002, -6.648242187500031],\n                            [112.0873046875, -6.89335937499996],\n                            [112.53925781250004, -6.926464843749955],\n                            [112.64873046875007, -7.221289062499977],\n                            [112.7943359375, -7.304492187499974],\n                            [112.79453125000012, -7.55244140625004],\n                            [113.01357421875005, -7.657714843749986],\n                            [113.49765625000006, -7.723828124999955],\n                            [114.07070312500005, -7.633007812500011],\n                            [114.40927734375012, -7.79248046875],\n                            [114.38691406250004, -8.405175781250037],\n                            [114.58378906250002, -8.769628906250034],\n                            [113.25332031250005, -8.286718749999963],\n                            [112.67880859375006, -8.409179687499957],\n                            [111.50996093750004, -8.30507812499998],\n                            [110.60722656250002, -8.149414062499972],\n                            [109.28164062500005, -7.704882812500003],\n                            [108.74121093749997, -7.667089843750034],\n                            [108.45175781250006, -7.79697265625002],\n                            [107.91748046875003, -7.724121093750014],\n                            [107.28496093750007, -7.471679687500014],\n                            [106.45527343750004, -7.368652343749986],\n                            [106.51972656250004, -7.053710937499943],\n                            [106.19824218749997, -6.927832031249977],\n                            [105.25546875000012, -6.835253906250031],\n                            [105.37089843750002, -6.664355468750031],\n                            [105.48369140625007, -6.781542968750017],\n                            [105.65507812500002, -6.469531249999946],\n                            [105.78691406250002, -6.456933593749966],\n                            [105.86826171875006, -6.11640625000004],\n                            [106.075, -5.914160156249963],\n                            [106.82519531249997, -6.098242187499977],\n                            [107.0462890625, -5.90419921874998],\n                            [107.37392578125005, -6.007617187499989]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.52832031249997, -6.2984375],\n                            [120.48730468749997, -6.464843749999972],\n                            [120.47734375000007, -5.775292968750009],\n                            [120.52832031249997, -6.2984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.7194335937501, -5.81103515625],\n                            [112.58603515625006, -5.803613281249994],\n                            [112.69003906250006, -5.726171875000034],\n                            [112.7194335937501, -5.81103515625]\n                        ]\n                    ],\n                    [\n                        [\n                            [132.80712890625003, -5.850781250000011],\n                            [132.68144531250002, -5.91259765625],\n                            [132.63017578125002, -5.60703125],\n                            [132.80712890625003, -5.850781250000011]\n                        ]\n                    ],\n                    [\n                        [\n                            [134.74697265625, -5.707031249999957],\n                            [134.71416015625007, -6.29511718750004],\n                            [134.44111328125004, -6.334863281249966],\n                            [134.15488281250006, -6.06289062499998],\n                            [134.3019531250001, -6.009765624999986],\n                            [134.34306640625002, -5.833007812499943],\n                            [134.20537109375002, -5.707226562499997],\n                            [134.34130859375003, -5.712890624999986],\n                            [134.57080078124997, -5.42734375],\n                            [134.74697265625, -5.707031249999957]\n                        ]\n                    ],\n                    [\n                        [\n                            [132.92626953124997, -5.902050781249983],\n                            [132.84501953125002, -5.987988281249997],\n                            [133.13847656250002, -5.317871093749986],\n                            [133.11962890625003, -5.575976562499989],\n                            [132.92626953124997, -5.902050781249983]\n                        ]\n                    ],\n                    [\n                        [\n                            [102.36718750000003, -5.478710937499983],\n                            [102.1107421875, -5.32255859374996],\n                            [102.3717773437501, -5.366406250000011],\n                            [102.36718750000003, -5.478710937499983]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.62675781250007, -5.271582031249963],\n                            [123.58261718750006, -5.36738281250004],\n                            [123.54277343750002, -5.271093749999963],\n                            [123.62675781250007, -5.271582031249963]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.04296874999997, -5.437988281250028],\n                            [121.80849609375, -5.256152343750017],\n                            [121.91367187500012, -5.072265624999957],\n                            [122.04101562500003, -5.158789062499991],\n                            [122.04296874999997, -5.437988281250028]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.64511718750012, -5.26943359374998],\n                            [122.5638671875, -5.3875],\n                            [122.28310546875, -5.319531249999969],\n                            [122.39628906250002, -5.069824218749986],\n                            [122.36894531250007, -4.767187499999977],\n                            [122.70195312500002, -4.61865234375],\n                            [122.75986328125012, -4.933886718750003],\n                            [122.61406250000007, -5.138671874999986],\n                            [122.64511718750012, -5.26943359374998]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.17978515625006, -4.551171875000023],\n                            [123.195703125, -4.82265625],\n                            [123.05517578124997, -4.748242187500026],\n                            [122.97167968750003, -5.138476562500031],\n                            [123.18730468750007, -5.333007812499957],\n                            [122.96875, -5.405761718749943],\n                            [122.81210937500012, -5.671289062499952],\n                            [122.64501953124997, -5.663378906250031],\n                            [122.58642578124997, -5.488867187500006],\n                            [122.76650390625005, -5.210156249999983],\n                            [122.85332031250007, -4.618359375000026],\n                            [123.074609375, -4.38691406250004],\n                            [123.17978515625006, -4.551171875000023]\n                        ]\n                    ],\n                    [\n                        [\n                            [133.57080078124997, -4.245898437500003],\n                            [133.621875, -4.299316406249957],\n                            [133.32089843750006, -4.111035156249969],\n                            [133.57080078124997, -4.245898437500003]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.2423828125001, -4.112988281250011],\n                            [123.07617187499997, -4.227148437499991],\n                            [122.96904296875002, -4.029980468749969],\n                            [123.21191406250003, -3.997558593750028],\n                            [123.2423828125001, -4.112988281250011]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.56259765625012, -3.58544921875],\n                            [128.39160156250003, -3.637890625000026],\n                            [128.45156250000005, -3.514746093749991],\n                            [128.56259765625012, -3.58544921875]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.2755859375001, -3.67460937499996],\n                            [127.97802734374997, -3.770996093749972],\n                            [127.925, -3.69931640625002],\n                            [128.32910156249997, -3.51591796874996],\n                            [128.2755859375001, -3.67460937499996]\n                        ]\n                    ],\n                    [\n                        [\n                            [116.42412109375007, -3.464453124999963],\n                            [116.38779296875012, -3.636718749999972],\n                            [116.3265625, -3.539062499999972],\n                            [116.42412109375007, -3.464453124999963]\n                        ]\n                    ],\n                    [\n                        [\n                            [116.30332031250006, -3.868164062499957],\n                            [116.05878906250004, -4.006933593749991],\n                            [116.06357421875006, -3.457910156249952],\n                            [116.26972656250004, -3.251074218750006],\n                            [116.30332031250006, -3.868164062499957]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.86113281250007, -3.087890624999986],\n                            [127.22734375000007, -3.391015625],\n                            [127.22958984375006, -3.633007812500011],\n                            [126.68632812500007, -3.823632812500037],\n                            [126.21455078125004, -3.605175781250026],\n                            [126.05654296875, -3.420996093749991],\n                            [126.08828125, -3.105468750000014],\n                            [126.86113281250007, -3.087890624999986]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.88642578125004, -3.005273437500023],\n                            [106.7428710937501, -2.932812500000011],\n                            [106.91064453124997, -2.93398437499998],\n                            [106.88642578125004, -3.005273437500023]\n                        ]\n                    ],\n                    [\n                        [\n                            [129.75468750000007, -2.865820312500034],\n                            [130.3791015625001, -2.989355468749977],\n                            [130.56992187500006, -3.130859375000028],\n                            [130.85996093750006, -3.570312500000028],\n                            [130.805078125, -3.85771484374996],\n                            [129.844140625, -3.327148437499957],\n                            [129.51171875000003, -3.32851562499998],\n                            [129.46767578125005, -3.453222656249977],\n                            [128.8625, -3.234960937500006],\n                            [128.51660156249997, -3.449121093750037],\n                            [128.13203125000004, -3.157421875000026],\n                            [127.90234374999997, -3.496289062499955],\n                            [127.87792968749997, -3.222070312499966],\n                            [128.19853515625002, -2.865917968749969],\n                            [128.99111328125, -2.82851562499998],\n                            [129.17441406250006, -2.933496093749966],\n                            [129.48417968750002, -2.785742187499977],\n                            [129.75468750000007, -2.865820312500034]\n                        ]\n                    ],\n                    [\n                        [\n                            [100.42509765625007, -3.182910156249974],\n                            [100.46513671875007, -3.32851562499998],\n                            [100.20429687500004, -2.98681640625],\n                            [100.19853515625002, -2.785546875000023],\n                            [100.45458984375003, -3.001953124999972],\n                            [100.42509765625007, -3.182910156249974]\n                        ]\n                    ],\n                    [\n                        [\n                            [108.2072265625001, -2.997656249999977],\n                            [108.05527343750006, -3.22685546874996],\n                            [107.85820312500002, -3.086328125000023],\n                            [107.61445312500004, -3.209375],\n                            [107.56347656250003, -2.920117187499997],\n                            [107.66630859375002, -2.566308593750037],\n                            [107.83779296875005, -2.530273437499972],\n                            [108.21513671875002, -2.696972656250011],\n                            [108.29062500000012, -2.829980468750023],\n                            [108.2072265625001, -2.997656249999977]\n                        ]\n                    ],\n                    [\n                        [\n                            [100.20410156249997, -2.741015625000017],\n                            [100.01494140625007, -2.819726562499966],\n                            [99.98789062500006, -2.525390624999957],\n                            [100.20410156249997, -2.741015625000017]\n                        ]\n                    ],\n                    [\n                        [\n                            [99.84306640625007, -2.343066406250031],\n                            [99.60703125000012, -2.257519531250011],\n                            [99.57216796875005, -2.025781249999966],\n                            [99.84306640625007, -2.343066406250031]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.055078125, -2.451269531249963],\n                            [125.86289062500006, -2.077148437499943],\n                            [125.92275390625, -1.974804687499969],\n                            [126.055078125, -2.451269531249963]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.02421875000007, -1.789746093750011],\n                            [126.33173828125004, -1.822851562500006],\n                            [125.47919921875004, -1.940039062499991],\n                            [125.38720703124997, -1.843066406249946],\n                            [126.02421875000007, -1.789746093750011]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.35332031250007, -1.690527343749963],\n                            [130.41884765625, -1.971289062499963],\n                            [130.24804687500003, -2.047753906249994],\n                            [129.7376953125, -1.866894531250011],\n                            [130.35332031250007, -1.690527343749963]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.96953125000007, -1.70546875],\n                            [125.18789062500005, -1.712890624999986],\n                            [125.31406250000006, -1.877148437499969],\n                            [124.41777343750002, -2.005175781250031],\n                            [124.32968750000012, -1.858886718749972],\n                            [124.41757812500006, -1.659277343749991],\n                            [124.96953125000007, -1.70546875]\n                        ]\n                    ],\n                    [\n                        [\n                            [135.47421875000006, -1.591796875000014],\n                            [136.89257812500003, -1.799707031249994],\n                            [136.22812500000012, -1.893652343749949],\n                            [135.47421875000006, -1.591796875000014]\n                        ]\n                    ],\n                    [\n                        [\n                            [108.953125, -1.61962890625],\n                            [108.83789062499997, -1.661621093750028],\n                            [108.80371093750003, -1.567773437499994],\n                            [108.953125, -1.61962890625]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.04570312500002, -1.669433593750014],\n                            [106.36591796875004, -2.464843749999972],\n                            [106.81845703125006, -2.573339843749963],\n                            [106.6120117187501, -2.895507812499957],\n                            [106.66718750000004, -3.071777343749986],\n                            [105.99873046875004, -2.824902343749955],\n                            [105.7858398437501, -2.18134765625004],\n                            [105.13339843750012, -2.042578125],\n                            [105.45957031250006, -1.574707031249986],\n                            [105.58544921875003, -1.526757812499994],\n                            [105.7008789062501, -1.731054687499963],\n                            [105.7204101562501, -1.533886718750026],\n                            [105.91005859375, -1.504980468749991],\n                            [106.04570312500002, -1.669433593750014]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.59755859375, -1.704296875000011],\n                            [123.48251953125006, -1.681445312499974],\n                            [123.52851562500004, -1.502832031250009],\n                            [123.59755859375, -1.704296875000011]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.1530273437501, -1.66054687499998],\n                            [127.56162109375012, -1.728515624999972],\n                            [127.39501953125003, -1.589843749999972],\n                            [127.64667968750004, -1.332421875],\n                            [128.1530273437501, -1.66054687499998]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.2123046875, -1.171289062499966],\n                            [123.23779296874997, -1.389355468749983],\n                            [123.43476562500004, -1.236816406249986],\n                            [123.54726562500005, -1.337402343749957],\n                            [123.51191406250004, -1.447363281249977],\n                            [123.27490234374997, -1.437207031249955],\n                            [123.17294921875006, -1.616015624999974],\n                            [123.15039062500003, -1.304492187500003],\n                            [122.89042968750007, -1.58720703124996],\n                            [122.81083984375002, -1.432128906249986],\n                            [122.90800781250002, -1.182226562499963],\n                            [123.2123046875, -1.171289062499966]\n                        ]\n                    ],\n                    [\n                        [\n                            [109.71025390625007, -1.1806640625],\n                            [109.46367187500002, -1.277539062500026],\n                            [109.4759765625, -0.9853515625],\n                            [109.74335937500004, -1.039355468749989],\n                            [109.71025390625007, -1.1806640625]\n                        ]\n                    ],\n                    [\n                        [\n                            [134.96533203124997, -1.116015624999974],\n                            [134.86171875, -1.114160156249952],\n                            [134.82792968750002, -0.978808593750003],\n                            [134.99628906250004, -1.03408203124998],\n                            [134.96533203124997, -1.116015624999974]\n                        ]\n                    ],\n                    [\n                        [\n                            [99.16386718750007, -1.777929687500006],\n                            [98.82773437500006, -1.609960937499977],\n                            [98.60175781250004, -1.197851562499949],\n                            [98.67607421875007, -0.970507812500003],\n                            [98.93261718750003, -0.954003906250009],\n                            [99.2672851562501, -1.62773437499996],\n                            [99.16386718750007, -1.777929687500006]\n                        ]\n                    ],\n                    [\n                        [\n                            [131.00185546875005, -1.315527343750034],\n                            [130.78232421875006, -1.255468749999963],\n                            [130.67294921875006, -0.959765625000031],\n                            [131.03300781250007, -0.917578124999963],\n                            [131.00185546875005, -1.315527343750034]\n                        ]\n                    ],\n                    [\n                        [\n                            [135.38300781250004, -0.6513671875],\n                            [135.89355468749997, -0.725781249999969],\n                            [136.37529296875007, -1.094042968750031],\n                            [136.1647460937501, -1.214746093750023],\n                            [135.91503906250003, -1.178417968749997],\n                            [135.74707031249997, -0.823046874999974],\n                            [135.64570312500004, -0.881933593749991],\n                            [135.38300781250004, -0.6513671875]\n                        ]\n                    ],\n                    [\n                        [\n                            [127.30039062500012, -0.780957031250026],\n                            [127.1564453125001, -0.760937500000026],\n                            [127.20908203125006, -0.619335937499955],\n                            [127.30039062500012, -0.780957031250026]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.6266601562501, -0.528710937499966],\n                            [130.46542968750006, -0.486523437499983],\n                            [130.6159179687501, -0.417285156250003],\n                            [130.6266601562501, -0.528710937499966]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.86435546875012, -0.406835937500006],\n                            [121.88125, -0.502636718749983],\n                            [121.65527343749997, -0.526171874999989],\n                            [121.86435546875012, -0.406835937500006]\n                        ]\n                    ],\n                    [\n                        [\n                            [140.97343750000007, -2.609765625],\n                            [140.97353515625, -2.803417968750026],\n                            [140.975, -6.346093750000023],\n                            [140.86230468749997, -6.740039062499989],\n                            [140.97519531250006, -6.90537109375002],\n                            [140.97617187500012, -9.11875],\n                            [140.00292968749997, -8.19550781250004],\n                            [140.11699218750002, -7.923730468750009],\n                            [139.93476562500004, -8.101171875],\n                            [139.38564453125, -8.189062499999963],\n                            [139.24882812500002, -7.982421874999972],\n                            [138.890625, -8.237792968749943],\n                            [139.08798828125012, -7.587207031250017],\n                            [138.74794921875, -7.25146484375],\n                            [139.17685546875006, -7.1904296875],\n                            [138.84570312500003, -7.13632812499999],\n                            [138.60136718750007, -6.936523437499972],\n                            [138.86455078125007, -6.858398437499943],\n                            [138.43867187500004, -6.343359375],\n                            [138.2962890625, -5.94902343749996],\n                            [138.37460937500006, -5.84365234374998],\n                            [138.19960937500005, -5.80703125],\n                            [138.33964843750007, -5.675683593749966],\n                            [138.08710937500004, -5.70917968750004],\n                            [138.06083984375002, -5.46523437499998],\n                            [137.27978515624997, -4.945410156249949],\n                            [136.61884765625004, -4.81875],\n                            [135.97968750000004, -4.530859374999963],\n                            [135.19560546875007, -4.450683593749972],\n                            [134.67968749999997, -4.079101562499943],\n                            [134.70654296875003, -3.954785156250026],\n                            [134.88652343750007, -3.938476562499986],\n                            [134.26621093750012, -3.945800781249972],\n                            [134.14707031250006, -3.79677734374998],\n                            [133.97382812500004, -3.817968750000034],\n                            [133.67832031250006, -3.4794921875],\n                            [133.8415039062501, -3.054785156249991],\n                            [133.70039062500004, -3.0875],\n                            [133.653125, -3.364355468749991],\n                            [133.51816406250012, -3.411914062500003],\n                            [133.40087890625003, -3.899023437500034],\n                            [133.24873046875004, -4.062304687499989],\n                            [132.91445312500005, -4.05693359374996],\n                            [132.75390625000003, -3.703613281250014],\n                            [132.86972656250006, -3.550976562499997],\n                            [132.75136718750005, -3.294628906249997],\n                            [131.97119140624997, -2.788574218750014],\n                            [132.2306640625001, -2.680371093749997],\n                            [132.725, -2.789062500000028],\n                            [133.19101562500006, -2.43779296874996],\n                            [133.70009765625005, -2.624609375],\n                            [133.75332031250005, -2.450683593750014],\n                            [133.90488281250012, -2.390917968750003],\n                            [133.79101562500003, -2.293652343749997],\n                            [133.92158203125004, -2.102050781249957],\n                            [132.96279296875005, -2.272558593749963],\n                            [132.30761718749997, -2.24228515625002],\n                            [132.02343749999997, -1.99033203125002],\n                            [131.93037109375004, -1.559667968750034],\n                            [131.29375, -1.393457031250009],\n                            [130.99589843750007, -1.42470703124998],\n                            [131.1908203125, -1.165820312500003],\n                            [131.2572265625, -0.855468750000014],\n                            [131.80429687500006, -0.703808593750026],\n                            [132.39375, -0.355468750000028],\n                            [132.85644531250003, -0.417382812500023],\n                            [133.47265624999997, -0.726171874999963],\n                            [133.97451171875, -0.744335937500026],\n                            [134.11152343750004, -0.84677734375002],\n                            [134.07197265625004, -1.001855468749994],\n                            [134.25957031250007, -1.362988281250026],\n                            [134.105859375, -1.720996093749946],\n                            [134.19482421875003, -2.309082031249943],\n                            [134.45996093749997, -2.83232421874996],\n                            [134.48330078125, -2.583007812499972],\n                            [134.62744140624997, -2.536718749999963],\n                            [134.70214843749997, -2.933593749999986],\n                            [134.84335937500006, -2.909179687499986],\n                            [134.88681640625006, -3.209863281249966],\n                            [135.25156250000012, -3.368554687499966],\n                            [135.48662109375002, -3.34511718749998],\n                            [135.85917968750002, -2.99531250000004],\n                            [136.38994140625002, -2.273339843750037],\n                            [137.07207031250002, -2.105078124999949],\n                            [137.1710937500001, -2.025488281249991],\n                            [137.1234375, -1.840917968749963],\n                            [137.80625000000012, -1.483203125],\n                            [139.78955078125003, -2.34824218750002],\n                            [140.62255859374997, -2.44580078125],\n                            [140.74746093750005, -2.607128906249997],\n                            [140.97343750000007, -2.609765625]\n                        ]\n                    ],\n                    [\n                        [\n                            [104.47421875000012, -0.334667968749955],\n                            [104.59013671875002, -0.466601562500017],\n                            [104.36318359375, -0.658593749999966],\n                            [104.25712890625002, -0.463281249999966],\n                            [104.47421875000012, -0.334667968749955]\n                        ]\n                    ],\n                    [\n                        [\n                            [127.56699218750006, -0.318945312499949],\n                            [127.68242187500002, -0.46835937500002],\n                            [127.60498046874997, -0.610156249999946],\n                            [127.88017578125002, -0.808691406249991],\n                            [127.7611328125, -0.883691406249994],\n                            [127.62382812500002, -0.76601562499999],\n                            [127.46269531250002, -0.80595703124996],\n                            [127.46865234375, -0.64296875],\n                            [127.3, -0.500292968749946],\n                            [127.32509765625, -0.335839843750023],\n                            [127.45517578125012, -0.406347656249991],\n                            [127.56699218750006, -0.318945312499949]\n                        ]\n                    ],\n                    [\n                        [\n                            [127.24990234375005, -0.4953125],\n                            [127.11914062500003, -0.520507812499986],\n                            [127.12646484375003, -0.278613281250003],\n                            [127.29003906250003, -0.284375],\n                            [127.24990234375005, -0.4953125]\n                        ]\n                    ],\n                    [\n                        [\n                            [103.73652343750004, -0.347949218750003],\n                            [103.461328125, -0.357617187500011],\n                            [103.54892578125006, -0.227539062499986],\n                            [103.73652343750004, -0.347949218750003]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.81328125000007, -0.004101562500026],\n                            [131.27685546875003, -0.149804687499952],\n                            [131.33974609375005, -0.290332031249989],\n                            [131.00537109374997, -0.360742187500037],\n                            [130.62216796875006, -0.0859375],\n                            [130.89921875000002, -0.344433593749997],\n                            [130.7501953125001, -0.44384765625],\n                            [130.6886718750001, -0.296582031250011],\n                            [130.55078124999997, -0.366406250000026],\n                            [130.23662109375002, -0.209667968749983],\n                            [130.3625, -0.072851562500006],\n                            [130.81328125000007, -0.004101562500026]\n                        ]\n                    ],\n                    [\n                        [\n                            [98.45927734375007, -0.530468749999969],\n                            [98.30966796875012, -0.531835937499977],\n                            [98.4271484375, -0.226464843750037],\n                            [98.3229492187501, -0.000781249999974],\n                            [98.54414062500004, -0.257617187499989],\n                            [98.45927734375007, -0.530468749999969]\n                        ]\n                    ],\n                    [\n                        [\n                            [104.77861328125007, -0.175976562499955],\n                            [105.00537109374997, -0.282812499999963],\n                            [104.44707031250002, -0.189160156249983],\n                            [104.54267578125004, 0.01772460937498],\n                            [104.77861328125007, -0.175976562499955]\n                        ]\n                    ],\n                    [\n                        [\n                            [103.28447265625002, 0.541943359375011],\n                            [103.13955078125, 0.549072265625043],\n                            [103.18740234375, 0.699755859375017],\n                            [103.28447265625002, 0.541943359375011]\n                        ]\n                    ],\n                    [\n                        [\n                            [103.0275390625001, 0.746630859374974],\n                            [102.4904296875001, 0.856640625],\n                            [102.50664062500002, 1.088769531250037],\n                            [103.00244140624997, 0.859277343750009],\n                            [103.0275390625001, 0.746630859374974]\n                        ]\n                    ],\n                    [\n                        [\n                            [103.42392578125012, 1.048339843749972],\n                            [103.31542968750003, 1.071289062500028],\n                            [103.37998046875006, 1.133642578125034],\n                            [103.42392578125012, 1.048339843749972]\n                        ]\n                    ],\n                    [\n                        [\n                            [103.16640625000005, 0.870166015625003],\n                            [102.7018554687501, 1.0537109375],\n                            [102.72558593749997, 1.158837890625023],\n                            [102.99941406250005, 1.067773437500023],\n                            [103.16640625000005, 0.870166015625003]\n                        ]\n                    ],\n                    [\n                        [\n                            [104.02480468750005, 1.180566406250009],\n                            [104.13984375000004, 1.165576171874974],\n                            [104.06611328125004, 0.989550781249989],\n                            [103.93222656250012, 1.071386718749963],\n                            [104.02480468750005, 1.180566406250009]\n                        ]\n                    ],\n                    [\n                        [\n                            [104.58535156250005, 1.21611328124996],\n                            [104.66289062500002, 1.04951171875004],\n                            [104.57519531250003, 0.831933593750037],\n                            [104.43925781250002, 1.050439453125051],\n                            [104.25195312499997, 1.014892578125],\n                            [104.36181640624997, 1.18149414062502],\n                            [104.58535156250005, 1.21611328124996]\n                        ]\n                    ],\n                    [\n                        [\n                            [102.4271484375, 0.990136718750023],\n                            [102.27958984375002, 1.075683593750043],\n                            [102.25634765625003, 1.397070312499963],\n                            [102.44287109374997, 1.234228515625006],\n                            [102.4271484375, 0.990136718750023]\n                        ]\n                    ],\n                    [\n                        [\n                            [97.48154296875006, 1.465087890624972],\n                            [97.93193359375002, 0.973925781250003],\n                            [97.82041015625012, 0.564453124999986],\n                            [97.683984375, 0.596093750000037],\n                            [97.60390625000005, 0.83388671874998],\n                            [97.40537109375012, 0.946972656250026],\n                            [97.07919921875006, 1.425488281249983],\n                            [97.35595703124997, 1.539746093749997],\n                            [97.48154296875006, 1.465087890624972]\n                        ]\n                    ],\n                    [\n                        [\n                            [102.49189453125004, 1.459179687500011],\n                            [102.49941406250005, 1.330908203124991],\n                            [102.02402343750012, 1.607958984375031],\n                            [102.49189453125004, 1.459179687500011]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.88886718750004, 0.995312500000011],\n                            [124.42753906250002, 0.470605468750051],\n                            [123.75380859375, 0.305517578124991],\n                            [123.26542968750007, 0.326611328125026],\n                            [122.996875, 0.493505859375006],\n                            [121.01298828125002, 0.441699218750017],\n                            [120.57900390625, 0.5283203125],\n                            [120.19228515625, 0.268505859374997],\n                            [120.01328125000012, -0.196191406249994],\n                            [120.062890625, -0.555566406250023],\n                            [120.240625, -0.868261718749949],\n                            [120.51757812499997, -1.039453125],\n                            [120.66738281250005, -1.370117187499972],\n                            [121.14853515625012, -1.33945312500002],\n                            [121.5755859375, -0.828515625000023],\n                            [121.96962890625005, -0.933300781249969],\n                            [122.27998046875004, -0.757031250000026],\n                            [122.88876953125006, -0.755175781250003],\n                            [122.8294921875, -0.658886718750026],\n                            [123.17148437500012, -0.57070312499999],\n                            [123.37968750000002, -0.648535156249949],\n                            [123.43417968750006, -0.778222656249994],\n                            [123.37792968749997, -1.004101562500011],\n                            [122.90283203125003, -0.900976562499963],\n                            [122.25068359375004, -1.555273437500034],\n                            [121.8585937500001, -1.69326171874998],\n                            [121.65097656250006, -1.895410156249952],\n                            [121.35546874999997, -1.878222656250003],\n                            [122.29169921875004, -2.907617187500023],\n                            [122.39902343750006, -3.200878906249997],\n                            [122.25292968749997, -3.620410156250017],\n                            [122.68964843750004, -4.084472656249972],\n                            [122.84794921875002, -4.064550781250006],\n                            [122.8722656250001, -4.391992187500009],\n                            [122.71972656250003, -4.340722656249952],\n                            [122.11425781250003, -4.540234375000011],\n                            [122.03808593749997, -4.832421875000023],\n                            [121.58867187500007, -4.759570312500017],\n                            [121.48652343750004, -4.581054687499972],\n                            [121.61806640625, -4.092675781249952],\n                            [120.89179687500004, -3.520605468750034],\n                            [121.05429687500012, -3.167089843749949],\n                            [121.0521484375, -2.751660156249955],\n                            [120.87939453124997, -2.64560546875002],\n                            [120.65361328125002, -2.667578124999977],\n                            [120.26103515625007, -2.949316406249991],\n                            [120.43662109375012, -3.70732421874996],\n                            [120.42011718750004, -4.617382812500011],\n                            [120.27929687499997, -5.146093749999977],\n                            [120.4303710937501, -5.591015625000026],\n                            [119.9515625, -5.577636718749972],\n                            [119.71728515625003, -5.693359375000014],\n                            [119.55742187500007, -5.611035156250026],\n                            [119.36035156249997, -5.314160156250026],\n                            [119.59404296875007, -4.523144531249997],\n                            [119.62363281250006, -4.034375],\n                            [119.46748046875004, -3.512988281249989],\n                            [118.99462890624997, -3.537597656250028],\n                            [118.86767578124997, -3.39804687500002],\n                            [118.78330078125006, -2.720800781249977],\n                            [119.09218750000005, -2.482910156250014],\n                            [119.32187500000012, -1.929687500000014],\n                            [119.308984375, -1.408203125],\n                            [119.508203125, -0.906738281249972],\n                            [119.71132812500005, -0.680761718750034],\n                            [119.84433593750006, -0.861914062499991],\n                            [119.721875, -0.088476562499991],\n                            [119.865625, 0.040087890625003],\n                            [119.80927734375004, 0.238671875000051],\n                            [119.9132812500001, 0.445068359375],\n                            [120.26953125000003, 0.970800781249991],\n                            [120.60253906249997, 0.854394531249994],\n                            [120.86796875000007, 1.25283203124998],\n                            [121.0817382812501, 1.327636718750028],\n                            [121.40410156250002, 1.243603515624969],\n                            [121.59179687499997, 1.067968749999977],\n                            [122.43662109375006, 1.018066406250028],\n                            [122.83828125, 0.845703125],\n                            [123.06650390625006, 0.941796875000037],\n                            [123.93076171875006, 0.850439453124977],\n                            [124.53369140624997, 1.230468750000043],\n                            [124.94707031250002, 1.672167968749974],\n                            [125.11093750000012, 1.685693359374966],\n                            [125.2337890625, 1.502294921875006],\n                            [124.88886718750004, 0.995312500000011]\n                        ]\n                    ],\n                    [\n                        [\n                            [101.70810546875006, 2.078417968750045],\n                            [101.71943359375004, 1.789160156250006],\n                            [101.50078125000002, 1.733203124999974],\n                            [101.40966796875003, 2.021679687500026],\n                            [101.70810546875006, 2.078417968750045]\n                        ]\n                    ],\n                    [\n                        [\n                            [127.73271484375007, 0.848144531250043],\n                            [127.8810546875001, 0.832128906249977],\n                            [127.96728515624997, 1.042578125000048],\n                            [128.16074218750006, 1.1578125],\n                            [128.22246093750002, 1.400634765624986],\n                            [128.68837890625, 1.572558593750017],\n                            [128.70263671874997, 1.106396484374997],\n                            [128.29882812500003, 0.87680664062502],\n                            [128.26064453125, 0.733789062500023],\n                            [128.61123046875, 0.549951171875051],\n                            [128.89960937500004, 0.216259765625011],\n                            [127.9831054687501, 0.471875],\n                            [127.88740234375004, 0.298339843750043],\n                            [127.97783203125002, -0.24833984374996],\n                            [128.4254882812501, -0.892675781249949],\n                            [128.04638671875003, -0.706054687499943],\n                            [127.69160156250004, -0.241894531249983],\n                            [127.70869140625004, 0.288085937499986],\n                            [127.53710937500003, 0.610888671875031],\n                            [127.60800781250006, 0.848242187499977],\n                            [127.42851562500002, 1.139990234374991],\n                            [127.63173828125, 1.843701171875011],\n                            [128.03642578125002, 2.199023437500017],\n                            [127.88681640625012, 1.83295898437504],\n                            [128.0109375000001, 1.701220703125031],\n                            [128.01171874999997, 1.331738281249983],\n                            [127.65283203124997, 1.013867187499969],\n                            [127.73271484375007, 0.848144531250043]\n                        ]\n                    ],\n                    [\n                        [\n                            [97.3341796875001, 2.075634765625011],\n                            [97.10830078125, 2.216894531250006],\n                            [97.29140625, 2.200830078125023],\n                            [97.3341796875001, 2.075634765625011]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.45390625000002, 2.051757812500028],\n                            [128.29589843749997, 2.034716796875017],\n                            [128.2179687500001, 2.297460937499991],\n                            [128.60214843750012, 2.59760742187504],\n                            [128.68847656250003, 2.473681640625017],\n                            [128.62324218750004, 2.224414062500031],\n                            [128.45390625000002, 2.051757812500028]\n                        ]\n                    ],\n                    [\n                        [\n                            [96.46367187500002, 2.360009765625037],\n                            [95.80859374999997, 2.655615234375034],\n                            [95.7171875, 2.825976562500017],\n                            [95.89580078125007, 2.8890625],\n                            [96.41728515625007, 2.515185546875031],\n                            [96.46367187500002, 2.360009765625037]\n                        ]\n                    ],\n                    [\n                        [\n                            [108.8875, 2.905419921875037],\n                            [108.7865234375, 2.885644531250009],\n                            [108.88574218750003, 2.998974609374997],\n                            [108.8875, 2.905419921875037]\n                        ]\n                    ],\n                    [\n                        [\n                            [105.76035156250006, 2.863037109375014],\n                            [105.69218750000002, 3.0625],\n                            [105.83671875000007, 2.97651367187504],\n                            [105.76035156250006, 2.863037109375014]\n                        ]\n                    ],\n                    [\n                        [\n                            [106.28525390625006, 3.15712890624998],\n                            [106.28369140624997, 3.088232421874977],\n                            [106.20097656250002, 3.204882812500031],\n                            [106.28525390625006, 3.15712890624998]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.65839843750004, 3.280517578124986],\n                            [117.54785156250003, 3.43198242187502],\n                            [117.68085937500004, 3.407519531250017],\n                            [117.65839843750004, 3.280517578124986]\n                        ]\n                    ],\n                    [\n                        [\n                            [125.65810546875, 3.436035156250043],\n                            [125.51152343750007, 3.461132812500011],\n                            [125.46884765625006, 3.73325195312502],\n                            [125.65810546875, 3.436035156250043]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.88476562499997, 4.186132812500006],\n                            [117.92285156250003, 4.054296874999977],\n                            [117.73681640624997, 4.004003906250034],\n                            [117.64902343750012, 4.168994140624974],\n                            [117.88476562499997, 4.186132812500006]\n                        ]\n                    ],\n                    [\n                        [\n                            [108.31601562500006, 3.689648437500026],\n                            [108.10039062500002, 3.70454101562504],\n                            [108.24326171875006, 3.810351562500017],\n                            [108.00234375, 3.982861328124983],\n                            [108.24833984375002, 4.21713867187502],\n                            [108.39287109375007, 3.986181640625034],\n                            [108.31601562500006, 3.689648437500026]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.5744140625001, 4.17060546875004],\n                            [117.46533203124997, 4.076074218749966],\n                            [117.77724609375005, 3.689257812500031],\n                            [117.05595703125007, 3.622656249999963],\n                            [117.34628906250006, 3.426611328124991],\n                            [117.35244140625, 3.19375],\n                            [117.61064453125002, 3.064355468749994],\n                            [117.56914062500002, 2.92929687500002],\n                            [117.69765625, 2.887304687499991],\n                            [117.6388671875001, 2.825292968749963],\n                            [118.0666015625001, 2.317822265624969],\n                            [117.7892578125001, 2.026855468750014],\n                            [118.98496093750006, 0.982128906249983],\n                            [118.53476562500006, 0.813525390625017],\n                            [118.19609375000002, 0.874365234374977],\n                            [117.91162109374997, 1.098681640625017],\n                            [117.96425781250005, 0.889550781250051],\n                            [117.74511718749997, 0.72963867187498],\n                            [117.52216796875004, 0.235888671875017],\n                            [117.46289062500003, -0.323730468749957],\n                            [117.5625, -0.770898437500009],\n                            [116.91396484375, -1.223632812499972],\n                            [116.73984375000006, -1.044238281250017],\n                            [116.75341796874997, -1.327343749999955],\n                            [116.27548828125006, -1.784863281249997],\n                            [116.42431640625003, -1.784863281249997],\n                            [116.45195312500002, -1.923144531250017],\n                            [116.31396484374997, -2.139843750000011],\n                            [116.56542968749997, -2.299707031249994],\n                            [116.52929687499997, -2.51054687499996],\n                            [116.31679687500005, -2.55185546875002],\n                            [116.33066406250012, -2.902148437499974],\n                            [116.16630859375002, -2.934570312500014],\n                            [116.2572265625, -3.126367187500009],\n                            [115.95615234375012, -3.595019531250003],\n                            [114.6935546875001, -4.169726562500017],\n                            [114.5255859375001, -3.376660156250011],\n                            [114.44599609375004, -3.481835937500037],\n                            [114.34433593750012, -3.444433593749963],\n                            [114.34433593750012, -3.23515625],\n                            [114.23632812500003, -3.36113281249996],\n                            [114.0822265625001, -3.27890625],\n                            [113.70507812499997, -3.45527343750004],\n                            [113.6100585937501, -3.195703125],\n                            [113.34316406250005, -3.246484374999966],\n                            [113.03398437500002, -2.933496093749966],\n                            [112.97148437500002, -3.187109375000034],\n                            [112.75800781250004, -3.322167968750009],\n                            [112.60029296875004, -3.400488281249977],\n                            [112.28496093750002, -3.32099609375004],\n                            [111.85810546875004, -3.551855468750006],\n                            [111.82304687500007, -3.057226562499949],\n                            [111.69472656250005, -2.88945312499996],\n                            [110.93007812500005, -3.071093750000017],\n                            [110.82968750000012, -2.9951171875],\n                            [110.89931640625, -2.908593749999952],\n                            [110.703125, -3.020898437500009],\n                            [110.57402343750007, -2.89140625],\n                            [110.25605468750004, -2.966113281249946],\n                            [110.09658203125, -2.001367187499966],\n                            [109.95986328125, -1.862792968749972],\n                            [109.98330078125, -1.274804687499994],\n                            [109.78740234375007, -1.011328124999963],\n                            [109.25878906250003, -0.807421874999989],\n                            [109.37275390625004, -0.638183593749972],\n                            [109.12109375000003, -0.39091796874996],\n                            [109.2575195312501, 0.031152343750051],\n                            [108.94453125000004, 0.355664062499997],\n                            [108.91679687500007, 0.912646484375045],\n                            [108.95859375000006, 1.134619140624963],\n                            [109.1315429687501, 1.253857421875011],\n                            [109.01025390624997, 1.239648437500051],\n                            [109.07587890625004, 1.495898437500031],\n                            [109.37851562500006, 1.922705078125034],\n                            [109.62890625000003, 2.027539062499983],\n                            [109.53896484375, 1.89619140625004],\n                            [109.65400390625004, 1.614892578125023],\n                            [110.50576171875005, 0.861962890625023],\n                            [111.10136718750002, 1.050537109374986],\n                            [111.80898437500005, 1.011669921874969],\n                            [112.078515625, 1.143359374999974],\n                            [112.1857421875001, 1.4390625],\n                            [112.47617187500006, 1.559082031250028],\n                            [112.94296875000006, 1.566992187500034],\n                            [113.00654296875004, 1.433886718750003],\n                            [113.6222656250001, 1.2359375],\n                            [113.90234375000003, 1.434277343749997],\n                            [114.5125, 1.452001953124963],\n                            [114.83056640625003, 1.980029296874989],\n                            [114.78642578125002, 2.250488281250014],\n                            [115.1791015625, 2.523193359374972],\n                            [115.08076171875004, 2.63422851562504],\n                            [115.117578125, 2.89487304687502],\n                            [115.24697265625005, 3.025927734374989],\n                            [115.45439453125002, 3.034326171875009],\n                            [115.67880859375006, 4.193017578124994],\n                            [115.86074218750005, 4.348046875000037],\n                            [116.51474609375006, 4.370800781249969],\n                            [117.10058593750003, 4.337060546875023],\n                            [117.5744140625001, 4.17060546875004]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.81660156250004, 4.033496093750003],\n                            [126.70449218750005, 4.070996093749997],\n                            [126.81357421875006, 4.258496093750011],\n                            [126.72207031250005, 4.344189453124969],\n                            [126.75732421874997, 4.547900390624989],\n                            [126.9210937500001, 4.291015624999972],\n                            [126.81660156250004, 4.033496093750003]\n                        ]\n                    ],\n                    [\n                        [\n                            [96.49257812500005, 5.229345703124991],\n                            [97.54716796875002, 5.205859375],\n                            [98.2484375, 4.41455078125],\n                            [98.3073242187501, 4.09287109375002],\n                            [99.73232421875005, 3.183056640625026],\n                            [100.523828125, 2.18916015625004],\n                            [100.88789062500004, 1.948242187499986],\n                            [100.82822265625012, 2.242578125],\n                            [101.04619140625002, 2.257470703125023],\n                            [101.47666015625006, 1.693066406250054],\n                            [102.019921875, 1.442138671875],\n                            [102.38994140625007, 0.84199218750004],\n                            [103.03183593750006, 0.57890625],\n                            [103.0075195312501, 0.415332031249974],\n                            [102.55, 0.216455078124966],\n                            [103.33896484375012, 0.513720703125045],\n                            [103.67265625000007, 0.288916015624977],\n                            [103.78671875000012, 0.046972656249991],\n                            [103.42851562500007, -0.19179687499998],\n                            [103.40517578125005, -0.36220703124998],\n                            [103.5091796875, -0.465527343749969],\n                            [103.43857421875006, -0.575585937500009],\n                            [103.72109375, -0.886718749999986],\n                            [104.36054687500004, -1.038378906249974],\n                            [104.51591796875002, -1.81943359375002],\n                            [104.84521484375003, -2.092968749999969],\n                            [104.65078125000005, -2.595214843749972],\n                            [104.97080078125012, -2.370898437500017],\n                            [105.39697265624997, -2.380175781249946],\n                            [106.0443359375, -3.10625],\n                            [105.84375, -3.61367187499998],\n                            [105.93046875000007, -3.833007812499986],\n                            [105.83144531250005, -4.16289062499996],\n                            [105.88720703124997, -5.009570312499974],\n                            [105.74833984375007, -5.818261718749966],\n                            [105.34941406250007, -5.549511718750011],\n                            [105.08134765625002, -5.74550781249998],\n                            [104.63955078125005, -5.520410156250037],\n                            [104.68398437500005, -5.89267578125002],\n                            [104.60156249999997, -5.90458984374996],\n                            [103.8314453125, -5.079589843750028],\n                            [102.53769531250006, -4.152148437499989],\n                            [102.12753906250006, -3.599218749999963],\n                            [101.57861328124997, -3.166992187500014],\n                            [100.88955078125, -2.248535156249957],\n                            [100.85527343750002, -1.934179687499949],\n                            [100.30820312500006, -0.82666015625],\n                            [99.66982421875005, 0.045068359375037],\n                            [99.15917968749997, 0.351757812499997],\n                            [98.59531250000006, 1.864599609375006],\n                            [97.70078125000006, 2.358544921875009],\n                            [97.59082031249997, 2.846582031250037],\n                            [97.3913085937501, 2.975292968749969],\n                            [96.9689453125001, 3.575146484374969],\n                            [96.44472656250005, 3.81630859374998],\n                            [95.57861328125003, 4.661962890625048],\n                            [95.20664062500006, 5.284033203125034],\n                            [95.22783203125002, 5.564794921875034],\n                            [95.62890625000003, 5.609082031249997],\n                            [96.13330078125003, 5.294287109374991],\n                            [96.49257812500005, 5.229345703124991]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Indonesia\", \"childNum\": 107 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-4.412060546874983, 54.185351562499996],\n                        [-4.785351562499983, 54.073046875],\n                        [-4.424707031249994, 54.407177734375],\n                        [-4.412060546874983, 54.185351562499996]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Isle of Man\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [75.32221348233018, 32.28516356678968],\n                            [75.62496871116024, 32.28516356678968],\n                            [75.73585997688717, 32.78417426256088],\n                            [76.32728006076415, 32.87658365066666],\n                            [76.62299010270264, 33.32014871357439],\n                            [77.06655516561037, 33.301666835953235],\n                            [77.71342088235082, 32.6917648744551],\n                            [78.10154031239509, 32.87658365066666],\n                            [78.49194250885338, 32.53122786149202],\n                            [78.38964843749997, 32.51987304687498],\n                            [78.41748046874997, 32.466699218749994],\n                            [78.4552734375001, 32.30034179687502],\n                            [78.49589843750002, 32.21577148437504],\n                            [78.72558593750009, 31.983789062500023],\n                            [78.71972656250009, 31.887646484374983],\n                            [78.69345703125006, 31.740380859374994],\n                            [78.7550781250001, 31.55029296875],\n                            [78.74355468750005, 31.323779296875017],\n                            [79.10712890625004, 31.402636718750102],\n                            [79.38847656250013, 31.064208984375085],\n                            [79.66425781250004, 30.96523437499999],\n                            [79.92451171875004, 30.888769531250034],\n                            [80.20712890625006, 30.683740234375023],\n                            [80.19121093750002, 30.56840820312496],\n                            [80.87353515625003, 30.290576171875045],\n                            [80.98544921875006, 30.23710937499999],\n                            [81.01025390625014, 30.164501953125097],\n                            [80.96611328125002, 30.180029296875063],\n                            [80.90761718750005, 30.171923828125017],\n                            [80.84814453125009, 30.139746093750034],\n                            [80.81992187500012, 30.119335937499955],\n                            [80.68408203125014, 29.994335937500068],\n                            [80.54902343750015, 29.899804687499994],\n                            [80.40185546875003, 29.730273437500102],\n                            [80.31689453125014, 29.572070312500017],\n                            [80.25488281250009, 29.423339843750114],\n                            [80.25595703125006, 29.318017578125136],\n                            [80.23300781250006, 29.194628906250045],\n                            [80.16953125000012, 29.124316406250102],\n                            [80.13046875000006, 29.100390625000045],\n                            [80.08457031249995, 28.994189453125074],\n                            [80.05166015625, 28.870312500000068],\n                            [80.07070312500005, 28.830175781250063],\n                            [80.22656250000003, 28.723339843750125],\n                            [80.32480468750012, 28.66640625000008],\n                            [80.41855468749995, 28.61201171875001],\n                            [80.47910156250012, 28.604882812499994],\n                            [80.49580078125015, 28.635791015625074],\n                            [80.51787109375002, 28.665185546875023],\n                            [80.58701171875006, 28.64960937500004],\n                            [81.16894531250014, 28.335009765625074],\n                            [81.85263671875018, 27.867089843750136],\n                            [81.89687500000011, 27.87446289062504],\n                            [81.94521484375005, 27.89926757812495],\n                            [81.98769531250016, 27.91376953125004],\n                            [82.03701171875, 27.90058593750004],\n                            [82.11191406250006, 27.86494140625004],\n                            [82.28769531250018, 27.756542968749983],\n                            [82.45136718750004, 27.671826171874955],\n                            [82.62988281249997, 27.687060546875045],\n                            [82.67734375000006, 27.67343749999995],\n                            [82.71083984375005, 27.596679687500114],\n                            [82.73339843750003, 27.518994140625097],\n                            [83.28974609375004, 27.370996093750136],\n                            [83.36943359375002, 27.410253906249977],\n                            [83.38398437500004, 27.444824218750085],\n                            [83.44716796875011, 27.46533203125],\n                            [83.55166015625011, 27.456347656249932],\n                            [83.74697265625011, 27.395947265625068],\n                            [83.8288085937501, 27.377832031250108],\n                            [84.09101562499993, 27.491357421875136],\n                            [84.22978515625007, 27.427832031250006],\n                            [84.48085937500005, 27.348193359375102],\n                            [84.61015625000002, 27.298681640624977],\n                            [84.64072265625012, 27.249853515624977],\n                            [84.65478515625014, 27.20366210937499],\n                            [84.65380859375009, 27.09169921875008],\n                            [84.68535156250013, 27.041015625000057],\n                            [85.19179687500011, 26.766552734375097],\n                            [85.29296875000009, 26.741015625000045],\n                            [85.56845703125012, 26.839843750000114],\n                            [85.64843749999997, 26.829003906250023],\n                            [85.69990234375004, 26.781640624999966],\n                            [85.73730468750003, 26.639746093750034],\n                            [85.79453125000006, 26.60415039062505],\n                            [86.00732421875009, 26.64936523437504],\n                            [86.70136718750015, 26.435058593750057],\n                            [87.01640625000002, 26.555419921875085],\n                            [87.2874023437499, 26.360302734375125],\n                            [87.41357421875014, 26.42294921875009],\n                            [87.84921875000006, 26.43691406250008],\n                            [87.99511718750014, 26.38237304687499],\n                            [88.02695312500023, 26.395019531250085],\n                            [88.05488281250004, 26.43002929687492],\n                            [88.11152343750004, 26.58642578125],\n                            [88.1615234375, 26.724804687500125],\n                            [88.15722656250009, 26.807324218750068],\n                            [88.1110351562501, 26.928466796875057],\n                            [87.99316406250009, 27.086083984374994],\n                            [87.984375, 27.133935546874994],\n                            [88.14697265625014, 27.749218750000097],\n                            [88.15029296875011, 27.843310546875074],\n                            [88.10976562500005, 27.87060546874997],\n                            [88.10898437499995, 27.93300781250005],\n                            [88.14111328125003, 27.948925781250097],\n                            [88.27519531250013, 27.96884765625009],\n                            [88.42597656250015, 28.011669921875097],\n                            [88.57792968750002, 28.093359375000034],\n                            [88.80371093750003, 28.006933593750034],\n                            [88.74902343749997, 27.521875000000136],\n                            [88.7648437500001, 27.429882812500068],\n                            [88.83251953125003, 27.362841796875074],\n                            [88.89140625000002, 27.316064453125136],\n                            [88.88164062500007, 27.29746093750009],\n                            [88.76035156250006, 27.21811523437509],\n                            [88.73876953125009, 27.175585937499932],\n                            [88.85761718750015, 26.961474609375017],\n                            [89.14824218750002, 26.816162109375085],\n                            [89.33212890625018, 26.848632812500114],\n                            [89.58613281250004, 26.778955078125136],\n                            [89.60996093750012, 26.719433593750097],\n                            [89.71093750000009, 26.713916015625045],\n                            [89.76386718750004, 26.7015625],\n                            [89.94316406250013, 26.723925781249932],\n                            [90.12294921875011, 26.754589843749983],\n                            [90.20605468749997, 26.847509765625063],\n                            [90.34589843750004, 26.890332031250097],\n                            [90.73964843750005, 26.771679687500068],\n                            [91.2865234375, 26.78994140625008],\n                            [91.42675781249997, 26.867089843749966],\n                            [91.45585937500013, 26.866894531250125],\n                            [91.51757812500009, 26.807324218750068],\n                            [91.67158203124993, 26.80200195312503],\n                            [91.84208984375013, 26.852978515625125],\n                            [91.94375, 26.860839843750114],\n                            [91.99833984375013, 26.85498046875],\n                            [92.04970703125016, 26.87485351562495],\n                            [92.73155507489682, 26.833697862861648],\n                            [93.30975376159499, 26.784950522650554],\n                            [93.61047043679247, 27.32239435188504],\n                            [94.06979001484449, 27.589407158584788],\n                            [95.10800937321915, 27.749636881153737],\n                            [95.74000740838363, 28.116850432722256],\n                            [96.19577594042592, 28.04291597700983],\n                            [96.96279296875, 27.698291015625017],\n                            [96.88359375000013, 27.514843750000125],\n                            [96.90195312500012, 27.43959960937508],\n                            [97.10371093749993, 27.163330078125114],\n                            [97.10205078125003, 27.115429687500125],\n                            [96.95341796875013, 27.13330078125003],\n                            [96.79785156249997, 27.29619140624999],\n                            [96.19082031250005, 27.26127929687499],\n                            [95.20146484375007, 26.641406250000017],\n                            [95.05976562500015, 26.473974609375006],\n                            [95.06894531250006, 26.191113281250097],\n                            [95.10839843750014, 26.091406250000034],\n                            [95.12929687500011, 26.070410156250034],\n                            [95.13242187500006, 26.041259765624943],\n                            [94.99199218750002, 25.77045898437504],\n                            [94.66777343750007, 25.458886718749966],\n                            [94.55302734375013, 25.215722656249994],\n                            [94.70371093750012, 25.097851562499955],\n                            [94.49316406250003, 24.637646484374983],\n                            [94.37724609375002, 24.473730468750006],\n                            [94.29306640625012, 24.321875],\n                            [94.07480468750006, 23.8720703125],\n                            [93.68339843750007, 24.00654296875004],\n                            [93.45214843750003, 23.987402343750034],\n                            [93.32626953125006, 24.064208984375057],\n                            [93.36601562500007, 23.132519531249955],\n                            [93.34941406250007, 23.08496093750003],\n                            [93.20390625000002, 23.03701171875005],\n                            [93.07871093750018, 22.718212890625097],\n                            [93.16201171875, 22.360205078125006],\n                            [93.07060546875002, 22.20942382812501],\n                            [92.96455078125015, 22.003759765625034],\n                            [92.90947265625013, 21.988916015625023],\n                            [92.85429687500002, 22.010156250000108],\n                            [92.77138671875, 22.104785156250017],\n                            [92.68896484375009, 22.130957031250006],\n                            [92.63037109375014, 22.011328124999977],\n                            [92.57490234374993, 21.97807617187496],\n                            [92.5612304687501, 22.04804687500001],\n                            [92.49140625000004, 22.685400390625006],\n                            [92.46445312500006, 22.734423828125045],\n                            [92.36162109375002, 22.929003906250074],\n                            [92.33378906250002, 23.242382812499955],\n                            [92.24609375000003, 23.68359374999997],\n                            [92.04404296875006, 23.677783203125017],\n                            [91.97851562500003, 23.691992187500063],\n                            [91.92958984375011, 23.685986328125097],\n                            [91.92949218750019, 23.598242187499977],\n                            [91.93789062500011, 23.504687500000102],\n                            [91.75419921875013, 23.28730468750004],\n                            [91.75097656250003, 23.053515625000017],\n                            [91.55351562500013, 22.991552734375006],\n                            [91.43623046875004, 23.19990234375001],\n                            [91.359375, 23.06835937500003],\n                            [91.16044921875019, 23.660644531250085],\n                            [91.35019531250012, 24.06049804687501],\n                            [91.72656250000003, 24.20507812499997],\n                            [91.84619140624997, 24.175292968749943],\n                            [92.06416015625004, 24.374365234375006],\n                            [92.11748046875002, 24.493945312500017],\n                            [92.22666015625012, 24.77099609374997],\n                            [92.22832031250002, 24.881347656250085],\n                            [92.2512695312499, 24.895068359375045],\n                            [92.38496093750004, 24.848779296875023],\n                            [92.46835937500018, 24.944140625000074],\n                            [92.04970703125016, 25.16948242187499],\n                            [90.61308593750002, 25.16772460937497],\n                            [90.11962890625003, 25.21997070312497],\n                            [89.86630859375012, 25.293164062499955],\n                            [89.81406250000006, 25.305371093749955],\n                            [89.80087890625012, 25.33613281250001],\n                            [89.82490234375004, 25.56015625],\n                            [89.82294921875015, 25.94140625000003],\n                            [89.67089843750009, 26.213818359375125],\n                            [89.57275390625003, 26.13232421875003],\n                            [89.54990234375006, 26.00527343750008],\n                            [89.28925781250015, 26.037597656250085],\n                            [89.01865234375012, 26.410253906249977],\n                            [88.95195312500002, 26.412109375],\n                            [88.97041015625004, 26.250878906250023],\n                            [88.94072265625002, 26.24536132812497],\n                            [88.68281250000004, 26.291699218749983],\n                            [88.51826171875004, 26.51777343750004],\n                            [88.36992187500002, 26.56411132812508],\n                            [88.35146484375005, 26.482568359374966],\n                            [88.38623046875003, 26.471533203125034],\n                            [88.44042968749997, 26.369482421875034],\n                            [88.33398437499997, 26.257519531249955],\n                            [88.15078125000005, 26.087158203125057],\n                            [88.1066406250001, 25.841113281250045],\n                            [88.14746093749997, 25.811425781250023],\n                            [88.50244140625009, 25.53701171875008],\n                            [88.76914062500006, 25.490478515625],\n                            [88.85478515625002, 25.333544921875017],\n                            [88.94414062500002, 25.290771484375],\n                            [88.92978515625012, 25.222998046875063],\n                            [88.57382812500006, 25.18789062499999],\n                            [88.45625, 25.188427734375125],\n                            [88.37294921875016, 24.961523437500063],\n                            [88.31337890625011, 24.8818359375],\n                            [88.27949218750015, 24.881933593750034],\n                            [88.18886718750016, 24.920605468750097],\n                            [88.14980468750011, 24.91464843749995],\n                            [88.04511718750015, 24.71303710937508],\n                            [88.03027343750009, 24.664453125000136],\n                            [88.02343750000003, 24.627832031250136],\n                            [88.07910156250009, 24.549902343750063],\n                            [88.14550781250003, 24.485791015624955],\n                            [88.225, 24.460644531249983],\n                            [88.3375, 24.45385742187503],\n                            [88.49853515625003, 24.34663085937504],\n                            [88.64228515625015, 24.325976562500102],\n                            [88.72353515625011, 24.27490234375],\n                            [88.7335937500001, 24.230908203125097],\n                            [88.72656250000009, 24.18623046875004],\n                            [88.71376953125016, 24.069628906250102],\n                            [88.69980468750006, 24.00253906249992],\n                            [88.56738281250009, 23.674414062500034],\n                            [88.63574218749997, 23.55],\n                            [88.69765625, 23.493017578125034],\n                            [88.72441406250002, 23.254980468750034],\n                            [88.89707031250018, 23.21040039062501],\n                            [88.92812500000011, 23.186621093749977],\n                            [88.89970703125002, 22.843505859375057],\n                            [88.92070312500002, 22.632031249999955],\n                            [89.05, 22.274609374999983],\n                            [89.02792968750023, 21.937207031249983],\n                            [88.94931640625018, 21.937939453125125],\n                            [89.05166015625, 21.654101562500045],\n                            [88.85751953125012, 21.744677734375017],\n                            [88.74501953125011, 21.584375],\n                            [88.74023437500003, 22.005419921875017],\n                            [88.64160156250003, 22.121972656250136],\n                            [88.58466796875015, 21.659716796874932],\n                            [88.44599609375004, 21.614257812500085],\n                            [88.28750000000016, 21.758203125000108],\n                            [88.25371093750002, 21.622314453124943],\n                            [88.0568359375001, 21.694140625000017],\n                            [88.19628906249997, 22.139550781249994],\n                            [87.94140625000003, 22.374316406250045],\n                            [88.15927734375018, 22.12172851562508],\n                            [87.82373046875003, 21.727343750000045],\n                            [87.20068359375009, 21.544873046874983],\n                            [86.95410156250014, 21.365332031250006],\n                            [86.84228515625009, 21.106347656249994],\n                            [86.97548828125005, 20.70014648437501],\n                            [86.75039062500011, 20.313232421875057],\n                            [86.37656250000006, 20.006738281249966],\n                            [86.24521484375012, 20.05302734374999],\n                            [86.27949218750021, 19.919433593749943],\n                            [85.575, 19.69291992187499],\n                            [85.496875, 19.696923828125108],\n                            [85.50410156250004, 19.887695312500057],\n                            [85.24863281250006, 19.757666015625034],\n                            [85.18076171875018, 19.59487304687508],\n                            [85.44160156249993, 19.626562499999977],\n                            [84.77099609375009, 19.125390625000023],\n                            [84.10410156250018, 18.29267578125001],\n                            [82.35957031250004, 17.09619140624997],\n                            [82.25878906250014, 16.55986328124996],\n                            [81.76191406250015, 16.32949218750008],\n                            [81.28613281249997, 16.337060546875023],\n                            [80.97871093750004, 15.758349609375074],\n                            [80.64658203125006, 15.895019531250028],\n                            [80.29345703125014, 15.710742187499989],\n                            [80.0534179687501, 15.074023437499932],\n                            [80.17871093750003, 14.478320312500074],\n                            [80.11171875000005, 14.212207031250045],\n                            [80.30654296875016, 13.485058593750054],\n                            [80.15625, 13.713769531250108],\n                            [80.06210937500006, 13.60625],\n                            [80.34238281250006, 13.361328125000071],\n                            [80.22910156250018, 12.690332031249966],\n                            [79.85849609375018, 11.988769531250043],\n                            [79.69316406250007, 11.312548828124946],\n                            [79.79902343750004, 11.338671874999932],\n                            [79.84863281250009, 11.196875],\n                            [79.83818359375002, 10.322558593750045],\n                            [79.31455078125018, 10.256689453124949],\n                            [78.93994140625009, 9.565771484375063],\n                            [79.01992187500005, 9.333349609374963],\n                            [79.41142578125002, 9.192382812500014],\n                            [78.97958984375018, 9.268554687500085],\n                            [78.42148437500006, 9.105029296874989],\n                            [78.19248046874995, 8.890869140625057],\n                            [78.06015625000006, 8.384570312499932],\n                            [77.51757812500003, 8.078320312500068],\n                            [77.06591796875003, 8.315917968749986],\n                            [76.5534179687501, 8.902783203124997],\n                            [76.32460937500016, 9.452099609374997],\n                            [76.24238281250004, 9.927099609374949],\n                            [76.37558593750006, 9.539892578124935],\n                            [76.45878906250013, 9.536230468750077],\n                            [76.34648437500002, 9.922119140625],\n                            [76.19560546875002, 10.086132812500026],\n                            [75.72382812500015, 11.361767578125026],\n                            [74.94550781250004, 12.56455078124992],\n                            [74.38222656250005, 14.494726562500048],\n                            [73.94921875000014, 15.074755859375088],\n                            [73.80078125000009, 15.39697265625],\n                            [73.93193359375013, 15.39697265625],\n                            [73.77177734375013, 15.573046874999989],\n                            [73.83281250000013, 15.659375],\n                            [73.67988281250015, 15.708886718750136],\n                            [73.47607421875003, 16.05424804687496],\n                            [72.87548828124997, 18.642822265625114],\n                            [72.97207031250011, 19.15332031250003],\n                            [72.8346679687501, 18.975585937500057],\n                            [72.80302734375013, 19.07929687500004],\n                            [72.81162109375, 19.298925781250006],\n                            [72.98720703125, 19.27744140625009],\n                            [72.78789062500013, 19.362988281250097],\n                            [72.66777343750019, 19.83095703125005],\n                            [72.89375, 20.672753906250136],\n                            [72.81386718750011, 21.117187500000085],\n                            [72.62382812500002, 21.371972656250108],\n                            [72.73476562500016, 21.470800781250006],\n                            [72.61328125000009, 21.461816406250108],\n                            [73.1125, 21.750439453125125],\n                            [72.54306640625, 21.69658203124999],\n                            [72.70019531250003, 21.971923828124943],\n                            [72.52226562500013, 21.976220703125108],\n                            [72.55302734375007, 22.159960937500074],\n                            [72.80917968749995, 22.23330078125008],\n                            [72.18281250000015, 22.26972656250004],\n                            [72.30644531250002, 22.18920898437497],\n                            [72.27441406250009, 22.089746093749966],\n                            [72.03720703125006, 21.82304687499999],\n                            [72.2103515625, 21.72822265625004],\n                            [72.25400390625006, 21.531005859375],\n                            [72.01523437500012, 21.155712890625097],\n                            [71.0246093750001, 20.73886718750009],\n                            [70.71933593750006, 20.740429687500068],\n                            [70.12734375, 21.094677734375097],\n                            [68.96992187500021, 22.29028320312497],\n                            [69.05166015625016, 22.437304687500074],\n                            [69.27656250000004, 22.285498046875063],\n                            [70.17724609375014, 22.57275390624997],\n                            [70.48925781250009, 23.08950195312508],\n                            [70.33945312500012, 22.939746093749932],\n                            [69.66464843750006, 22.759082031250074],\n                            [69.23593749999995, 22.848535156250023],\n                            [68.64072265625006, 23.189941406250114],\n                            [68.41748046875009, 23.57148437500004],\n                            [68.7767578125, 23.852099609375017],\n                            [68.23496093749995, 23.596972656250074],\n                            [68.16503906250009, 23.857324218749994],\n                            [68.28251953125013, 23.927978515625],\n                            [68.38125000000016, 23.950878906250068],\n                            [68.48867187500011, 23.96723632812501],\n                            [68.5866210937501, 23.966601562500074],\n                            [68.72412109375003, 23.964697265625034],\n                            [68.72812500000012, 24.265625],\n                            [68.73964843750016, 24.291992187500085],\n                            [68.75898437499993, 24.307226562500006],\n                            [68.78115234375011, 24.313720703125085],\n                            [68.8, 24.30908203125003],\n                            [68.82832031250004, 24.26401367187509],\n                            [68.86347656250015, 24.26650390625005],\n                            [68.90078125000011, 24.29243164062501],\n                            [68.98457031250015, 24.273095703124966],\n                            [69.05156250000013, 24.28632812500001],\n                            [69.11953125000011, 24.26865234374995],\n                            [69.23505859374993, 24.268261718750068],\n                            [69.44345703124995, 24.275390625000085],\n                            [69.55917968750006, 24.273095703124966],\n                            [69.80517578125009, 24.16523437500004],\n                            [70.0982421875, 24.2875],\n                            [70.28906250000009, 24.356298828125063],\n                            [70.54677734375, 24.418310546875063],\n                            [70.56503906250006, 24.385791015625017],\n                            [70.55585937500015, 24.331103515625074],\n                            [70.57929687500015, 24.279052734374943],\n                            [70.65947265625013, 24.24609374999997],\n                            [70.71630859375009, 24.237988281250097],\n                            [70.7672851562501, 24.245410156250017],\n                            [70.80507812500011, 24.26196289062503],\n                            [70.88623046875014, 24.34375],\n                            [70.92812500000016, 24.362353515625045],\n                            [70.98281250000011, 24.361035156250125],\n                            [71.04404296875006, 24.400097656250097],\n                            [71.04531250000005, 24.42998046874996],\n                            [70.96982421875012, 24.571875],\n                            [70.97636718750013, 24.61875],\n                            [71.00234375000016, 24.6539062499999],\n                            [71.04785156250003, 24.687744140625085],\n                            [71.02070312500021, 24.75766601562492],\n                            [70.95087890625015, 24.89160156250003],\n                            [70.87773437500019, 25.06298828124997],\n                            [70.65205078125004, 25.422900390625102],\n                            [70.64843750000003, 25.666943359375068],\n                            [70.5695312500001, 25.705957031250023],\n                            [70.50585937500009, 25.685302734375085],\n                            [70.44853515625013, 25.681347656249983],\n                            [70.26464843750009, 25.70654296874997],\n                            [70.10019531250006, 25.91005859375005],\n                            [70.14921875000002, 26.347558593749994],\n                            [70.11464843750016, 26.548046874999983],\n                            [69.47001953125002, 26.804443359375],\n                            [69.56796875, 27.174609375000102],\n                            [69.89628906250007, 27.473632812500085],\n                            [70.04980468750009, 27.694726562500023],\n                            [70.14453125000003, 27.849023437499994],\n                            [70.19394531250006, 27.89487304687492],\n                            [70.24433593750004, 27.934130859375102],\n                            [70.4037109375, 28.025048828124994],\n                            [70.48857421875013, 28.023144531250125],\n                            [70.62910156250015, 27.937451171875068],\n                            [70.6916015625001, 27.76899414062504],\n                            [70.79794921875012, 27.709619140625023],\n                            [70.87490234375016, 27.71445312499995],\n                            [71.18476562500004, 27.831640625],\n                            [71.54296875000003, 27.869873046875],\n                            [71.8703125000001, 27.9625],\n                            [71.88886718750004, 28.04746093749992],\n                            [71.94804687500002, 28.177294921875102],\n                            [72.12851562500012, 28.34633789062508],\n                            [72.29199218750003, 28.69726562499997],\n                            [72.34189453125006, 28.751904296875097],\n                            [72.90332031250003, 29.02875976562501],\n                            [73.38164062500013, 29.934375],\n                            [73.8091796875, 30.093359375],\n                            [73.88652343750013, 30.162011718750136],\n                            [73.93339843750002, 30.222070312500108],\n                            [73.92460937500007, 30.28164062499999],\n                            [73.88271484375, 30.352148437499977],\n                            [73.89931640625, 30.435351562500045],\n                            [74.00898437500004, 30.519677734374994],\n                            [74.33935546875003, 30.893554687499943],\n                            [74.38037109375003, 30.89340820312509],\n                            [74.50976562500009, 30.959667968750097],\n                            [74.63281250000014, 31.034667968750114],\n                            [74.62578125000002, 31.068750000000108],\n                            [74.61035156250009, 31.112841796875045],\n                            [74.51767578125012, 31.185595703124932],\n                            [74.53496093750007, 31.261376953125108],\n                            [74.59394531249993, 31.465380859375102],\n                            [74.58183593750013, 31.523925781250114],\n                            [74.50996093750015, 31.712939453125074],\n                            [74.52597656249995, 31.765136718750057],\n                            [74.55556640625011, 31.818554687500097],\n                            [74.63574218750003, 31.889746093750034],\n                            [74.73945312500015, 31.948828125],\n                            [75.07148437500015, 32.08935546875003],\n                            [75.13876953125, 32.10478515624999],\n                            [75.25410156250004, 32.140332031250125],\n                            [75.33349609374997, 32.279199218749994],\n                            [75.32221348233018, 32.28516356678968]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"India\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [72.49199218750002, -7.37744140625],\n                        [72.42910156250002, -7.435351562500003],\n                        [72.34970703125, -7.263378906250011],\n                        [72.447265625, -7.395703125000011],\n                        [72.44560546875002, -7.220410156250011],\n                        [72.49199218750002, -7.37744140625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Br. Indian Ocean Ter.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-9.948193359374926, 53.91313476562499],\n                            [-10.265722656249949, 53.977685546874994],\n                            [-9.99638671874996, 54.00361328125004],\n                            [-9.948193359374926, 53.91313476562499]\n                        ]\n                    ],\n                    [\n                        [\n                            [-6.218017578125, 54.08872070312506],\n                            [-6.347607421874926, 53.94130859375005],\n                            [-6.027392578124989, 52.927099609375006],\n                            [-6.463183593749932, 52.345361328124994],\n                            [-6.325, 52.246679687500034],\n                            [-6.890234375, 52.15922851562499],\n                            [-6.965771484374926, 52.24951171875],\n                            [-8.057812499999926, 51.82558593750005],\n                            [-8.4091796875, 51.888769531250034],\n                            [-8.349121093749943, 51.73930664062496],\n                            [-8.813427734374926, 51.584912109374955],\n                            [-9.737304687499943, 51.473730468750034],\n                            [-9.524902343750028, 51.68110351562501],\n                            [-10.120751953124994, 51.60068359375006],\n                            [-9.598828124999983, 51.87441406250005],\n                            [-10.341064453124943, 51.798925781250034],\n                            [-9.909667968749972, 52.122949218749966],\n                            [-10.39023437499992, 52.134912109374994],\n                            [-10.356689453125, 52.20693359375002],\n                            [-9.772119140624937, 52.250097656250034],\n                            [-9.90605468749996, 52.403710937499966],\n                            [-9.632226562499937, 52.54692382812502],\n                            [-8.783447265624943, 52.679638671874955],\n                            [-8.990283203124989, 52.755419921875045],\n                            [-9.175390624999949, 52.634912109374994],\n                            [-9.916601562499977, 52.56972656250005],\n                            [-9.46489257812496, 52.82319335937498],\n                            [-9.299218749999966, 53.09755859375002],\n                            [-8.930126953124983, 53.207080078125045],\n                            [-9.51420898437496, 53.23823242187498],\n                            [-10.091259765624926, 53.41284179687503],\n                            [-10.116992187499932, 53.548535156249955],\n                            [-9.720654296874926, 53.6044921875],\n                            [-9.901611328124943, 53.72719726562502],\n                            [-9.578222656249949, 53.80541992187497],\n                            [-9.578857421875, 53.879833984374955],\n                            [-9.9140625, 53.863720703124955],\n                            [-9.856445312499972, 54.095361328124994],\n                            [-10.092675781249966, 54.15576171875003],\n                            [-10.056396484374943, 54.25781250000006],\n                            [-8.545556640624994, 54.24121093750003],\n                            [-8.623144531249977, 54.346875],\n                            [-8.133447265624966, 54.64082031250001],\n                            [-8.763916015624972, 54.68120117187496],\n                            [-8.377294921874977, 54.88945312500002],\n                            [-8.274609374999955, 55.146289062500045],\n                            [-7.667089843749977, 55.25649414062502],\n                            [-7.65874023437496, 54.97094726562503],\n                            [-7.308789062500011, 55.365820312500006],\n                            [-6.961669921874972, 55.23789062500006],\n                            [-7.218652343749937, 55.09199218749998],\n                            [-7.55039062499992, 54.767968749999966],\n                            [-7.910595703124955, 54.698339843750006],\n                            [-7.75439453125, 54.59492187499998],\n                            [-8.118261718749977, 54.41425781250004],\n                            [-7.606542968750006, 54.14384765625002],\n                            [-7.324511718750017, 54.13344726562502],\n                            [-7.007714843749937, 54.40668945312501],\n                            [-6.649804687499937, 54.05864257812496],\n                            [-6.218017578125, 54.08872070312506]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Ireland\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [56.18798828125003, 26.92114257812497],\n                            [55.95429687500004, 26.70112304687501],\n                            [55.31152343749997, 26.592626953125006],\n                            [55.76259765625005, 26.81196289062504],\n                            [55.75761718750002, 26.94765625000005],\n                            [56.279394531250006, 26.952099609374983],\n                            [56.18798828125003, 26.92114257812497]\n                        ]\n                    ],\n                    [\n                        [\n                            [46.1144531250001, 38.877783203125034],\n                            [46.490625, 38.90668945312498],\n                            [47.995898437500074, 39.683935546875034],\n                            [48.322167968749994, 39.39907226562502],\n                            [48.10439453125005, 39.241113281249994],\n                            [48.292089843750006, 39.01884765624999],\n                            [47.99648437499999, 38.85375976562503],\n                            [48.59267578125005, 38.41108398437498],\n                            [48.86875, 38.43549804687498],\n                            [48.95996093750003, 37.89013671875],\n                            [49.171191406250074, 37.60058593749997],\n                            [50.13046875, 37.407128906249994],\n                            [50.53320312499997, 37.01367187500006],\n                            [51.11855468750005, 36.742578124999966],\n                            [52.19013671875004, 36.62172851562505],\n                            [53.76767578125006, 36.93032226562502],\n                            [53.91542968750005, 36.93032226562502],\n                            [53.67949218750002, 36.853125],\n                            [53.970117187499994, 36.818310546874955],\n                            [53.91416015625006, 37.34355468750002],\n                            [54.6994140625001, 37.47016601562498],\n                            [54.90009765625004, 37.77792968750006],\n                            [55.38085937500003, 38.051123046875034],\n                            [56.272070312500006, 38.080419921875034],\n                            [56.440625, 38.249414062499994],\n                            [57.1935546875001, 38.216406250000034],\n                            [57.35371093750004, 37.97333984374998],\n                            [58.261621093749994, 37.665820312500045],\n                            [58.81542968750003, 37.683496093749994],\n                            [59.30175781249997, 37.51064453125005],\n                            [59.454980468749994, 37.25283203125002],\n                            [60.06279296875002, 36.962890625],\n                            [60.34130859375003, 36.63764648437501],\n                            [61.11962890625003, 36.64257812500003],\n                            [61.212011718750006, 36.190527343750034],\n                            [61.15292968750006, 35.97675781250001],\n                            [61.25214843750004, 35.86762695312498],\n                            [61.26201171875002, 35.61958007812498],\n                            [61.28183593750006, 35.55341796875001],\n                            [61.2785156250001, 35.513769531250006],\n                            [61.245507812499994, 35.47407226562501],\n                            [61.18925781250002, 35.31201171875003],\n                            [61.1, 35.272314453125034],\n                            [61.08007812499997, 34.85561523437505],\n                            [60.95117187499997, 34.65385742187499],\n                            [60.91474609375004, 34.63398437500001],\n                            [60.80234375000006, 34.55463867187501],\n                            [60.73945312500004, 34.544726562500045],\n                            [60.7262695312501, 34.51826171874998],\n                            [60.736132812500074, 34.491796875],\n                            [60.76259765625005, 34.475244140624994],\n                            [60.88945312500002, 34.31943359375006],\n                            [60.642675781250006, 34.30717773437496],\n                            [60.48574218750005, 34.09477539062502],\n                            [60.4859375, 33.7119140625],\n                            [60.57382812500006, 33.58833007812498],\n                            [60.91699218749997, 33.505224609375006],\n                            [60.56054687499997, 33.13784179687502],\n                            [60.5765625, 32.99487304687503],\n                            [60.71044921874997, 32.6],\n                            [60.82929687500004, 32.24941406250005],\n                            [60.82724609375006, 32.16796874999997],\n                            [60.789941406249994, 31.98710937499999],\n                            [60.7875, 31.87719726562497],\n                            [60.791601562500006, 31.660595703124983],\n                            [60.82070312499999, 31.495166015625045],\n                            [60.854101562500006, 31.483251953125006],\n                            [61.110742187499994, 31.45112304687504],\n                            [61.346484375000074, 31.42163085937497],\n                            [61.66015625000003, 31.382421874999977],\n                            [61.7550781250001, 31.285302734374994],\n                            [61.814257812500074, 31.072558593750017],\n                            [61.810839843750074, 30.913281249999983],\n                            [61.78417968749997, 30.831933593750023],\n                            [61.55947265625005, 30.59936523437497],\n                            [61.33164062500006, 30.36372070312501],\n                            [60.84335937500006, 29.85869140624999],\n                            [61.03417968750003, 29.663427734374977],\n                            [61.15214843750002, 29.542724609375],\n                            [61.8898437500001, 28.546533203124994],\n                            [62.7625, 28.202050781249994],\n                            [62.782324218750006, 27.800537109375],\n                            [62.75273437500002, 27.265625],\n                            [63.16679687500002, 27.25249023437499],\n                            [63.19609375000002, 27.243945312500017],\n                            [63.25625, 27.20791015625005],\n                            [63.30156250000002, 27.151464843750006],\n                            [63.30517578124997, 27.124560546875017],\n                            [63.242089843749994, 27.07768554687499],\n                            [63.25039062499999, 26.879248046875063],\n                            [63.24160156250005, 26.86474609375003],\n                            [63.18613281250006, 26.83759765625001],\n                            [63.168066406250006, 26.66557617187496],\n                            [62.31230468750002, 26.490869140624994],\n                            [62.23935546875006, 26.357031249999977],\n                            [62.12597656249997, 26.368994140625034],\n                            [61.842382812500006, 26.225927734375006],\n                            [61.809960937499994, 26.165283203125],\n                            [61.78076171874997, 25.99584960937503],\n                            [61.75439453125003, 25.843359375000063],\n                            [61.737695312499994, 25.821093750000045],\n                            [61.66865234375004, 25.76899414062501],\n                            [61.6618164062501, 25.751269531250017],\n                            [61.67138671874997, 25.69238281250003],\n                            [61.64013671875003, 25.584619140624994],\n                            [61.61542968750004, 25.28613281250003],\n                            [61.58789062499997, 25.20234375000001],\n                            [61.533105468749994, 25.195507812499955],\n                            [61.41220703125006, 25.102099609375017],\n                            [60.66386718750002, 25.28222656250003],\n                            [60.51054687500002, 25.437060546875045],\n                            [60.40019531250002, 25.311572265625074],\n                            [59.45605468749997, 25.481494140625045],\n                            [59.0460937500001, 25.417285156250017],\n                            [58.79785156249997, 25.554589843750023],\n                            [57.334570312500006, 25.791552734375074],\n                            [57.03603515625005, 26.80068359375005],\n                            [56.728125, 27.127685546875057],\n                            [56.118066406249994, 27.14311523437499],\n                            [54.75927734375003, 26.50507812500004],\n                            [54.24707031250003, 26.696630859374977],\n                            [53.70576171875004, 26.72558593750003],\n                            [52.69160156250004, 27.323388671875023],\n                            [52.475878906250074, 27.61650390624999],\n                            [52.03076171874997, 27.824414062499955],\n                            [51.58906250000004, 27.864208984374983],\n                            [51.27890625, 28.13134765624997],\n                            [51.06201171874997, 28.72612304687499],\n                            [50.86699218750002, 28.870166015625017],\n                            [50.87578125000002, 29.062695312499983],\n                            [50.67519531250005, 29.146582031250034],\n                            [50.64960937500004, 29.420068359374966],\n                            [50.16894531250003, 29.921240234375034],\n                            [50.071582031250074, 30.198535156250017],\n                            [49.55488281250004, 30.028955078125023],\n                            [49.028125, 30.333447265624983],\n                            [49.224511718749994, 30.472314453125023],\n                            [49.00195312500003, 30.506542968749983],\n                            [48.91914062500004, 30.120898437500017],\n                            [48.54648437500006, 29.962353515624955],\n                            [48.47851562499997, 30.003808593749966],\n                            [48.43457031249997, 30.03759765625],\n                            [48.33105468749997, 30.28544921874996],\n                            [48.01494140625002, 30.465625],\n                            [48.01064453125005, 30.989794921875017],\n                            [47.679492187500074, 31.00239257812501],\n                            [47.679492187500074, 31.400585937499955],\n                            [47.75390624999997, 31.601367187500017],\n                            [47.829980468749994, 31.79443359375],\n                            [47.71455078125004, 31.936425781249966],\n                            [47.5915039062501, 32.087988281250034],\n                            [47.51191406250004, 32.15083007812504],\n                            [47.3297851562501, 32.45551757812501],\n                            [47.28515625000003, 32.474023437499966],\n                            [47.121386718750074, 32.46660156249996],\n                            [46.569921875, 32.83393554687501],\n                            [46.37705078125006, 32.92924804687499],\n                            [46.29824218750005, 32.95024414062502],\n                            [46.11279296875003, 32.957666015624994],\n                            [46.09306640625002, 32.97587890624999],\n                            [46.08046875, 33.028222656249994],\n                            [46.0807617187501, 33.08652343750006],\n                            [46.14111328125003, 33.174414062500034],\n                            [46.145898437499994, 33.229638671874994],\n                            [46.01992187500005, 33.41572265624998],\n                            [45.39707031250006, 33.970849609374994],\n                            [45.542773437500074, 34.21552734375004],\n                            [45.459375, 34.470361328124994],\n                            [45.50078125000002, 34.58159179687499],\n                            [45.6375, 34.573828125],\n                            [45.678125, 34.798437500000034],\n                            [45.92089843750003, 35.02851562500001],\n                            [46.04179687500002, 35.08017578125006],\n                            [46.13378906249997, 35.127636718749955],\n                            [46.15468750000005, 35.19672851562498],\n                            [46.112109375000074, 35.32167968750005],\n                            [45.97109375000005, 35.524169921875],\n                            [46.03740234375002, 35.67314453124999],\n                            [46.180957031250074, 35.71137695312504],\n                            [46.2625, 35.74414062500006],\n                            [46.27343749999997, 35.77324218750002],\n                            [46.16748046874997, 35.820556640625],\n                            [45.77636718749997, 35.82182617187499],\n                            [45.36162109375002, 36.015332031249955],\n                            [45.241113281249994, 36.35595703125],\n                            [45.20654296874997, 36.397167968749955],\n                            [45.15527343749997, 36.407373046874994],\n                            [45.11240234375006, 36.409277343750034],\n                            [45.053125, 36.47163085937501],\n                            [44.76513671875003, 37.142431640625006],\n                            [44.79414062500004, 37.290380859375034],\n                            [44.574023437500074, 37.435400390625006],\n                            [44.589941406250006, 37.710351562499966],\n                            [44.21132812499999, 37.908056640625006],\n                            [44.4499023437501, 38.33422851562506],\n                            [44.2985351562501, 38.38627929687499],\n                            [44.27167968750004, 38.83603515625006],\n                            [44.02324218750002, 39.37744140625006],\n                            [44.3893554687501, 39.422119140625],\n                            [44.58710937500004, 39.76855468750006],\n                            [44.81718750000002, 39.65043945312496],\n                            [45.4796875000001, 39.00625],\n                            [46.1144531250001, 38.877783203125034]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Iran\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [45.6375, 34.573828125],\n                        [45.50078125000002, 34.581591796874996],\n                        [45.459375, 34.470361328124994],\n                        [45.54277343750002, 34.21552734375],\n                        [45.397070312500006, 33.970849609374994],\n                        [46.01992187500002, 33.41572265625],\n                        [46.14589843750002, 33.229638671874994],\n                        [46.14111328125, 33.1744140625],\n                        [46.08076171875001, 33.0865234375],\n                        [46.08046875000002, 33.028222656249994],\n                        [46.09306640625002, 32.975878906249996],\n                        [46.11279296875, 32.957666015624994],\n                        [46.377050781250006, 32.929248046874996],\n                        [46.569921875, 32.833935546875],\n                        [47.12138671875002, 32.466601562499996],\n                        [47.28515625, 32.474023437499994],\n                        [47.32978515625001, 32.455517578125],\n                        [47.51191406250001, 32.150830078125],\n                        [47.59150390625001, 32.08798828125],\n                        [47.71455078125001, 31.936425781249994],\n                        [47.82998046875002, 31.79443359375],\n                        [47.75390625, 31.601367187499996],\n                        [47.67949218750002, 31.400585937499997],\n                        [47.67949218750002, 31.002392578124997],\n                        [48.01064453125002, 30.989794921874996],\n                        [48.01494140625002, 30.465625],\n                        [48.3310546875, 30.285449218749996],\n                        [48.546484375, 29.962353515624997],\n                        [48.454199218750006, 29.9384765625],\n                        [48.354589843750006, 29.956738281249997],\n                        [48.141699218750006, 30.040917968749994],\n                        [47.982519531250006, 30.011328125],\n                        [47.97871093750001, 29.9828125],\n                        [47.64375, 30.097314453124994],\n                        [47.14824218750002, 30.0009765625],\n                        [46.905859375, 29.5375],\n                        [46.76933593750002, 29.347460937499996],\n                        [46.69375, 29.259667968749994],\n                        [46.53144531250001, 29.096240234374996],\n                        [46.3564453125, 29.063671875],\n                        [44.71650390625001, 29.193603515625],\n                        [43.77373046875002, 29.84921875],\n                        [42.07441406250001, 31.080371093749996],\n                        [40.47890625000002, 31.893359375],\n                        [40.36933593750001, 31.93896484375],\n                        [40.02783203125, 31.995019531249994],\n                        [39.7041015625, 32.042529296874996],\n                        [39.14541015625002, 32.12451171875],\n                        [39.29277343750002, 32.24384765625],\n                        [39.24746093750002, 32.350976562499994],\n                        [39.04140625000002, 32.3056640625],\n                        [38.773535156250006, 33.372216796874994],\n                        [40.98701171875001, 34.429052734375],\n                        [41.19472656250002, 34.768994140625],\n                        [41.354101562500006, 35.640429687499996],\n                        [41.295996093750006, 36.383349609374996],\n                        [41.41679687500002, 36.5146484375],\n                        [41.78857421875, 36.59716796875],\n                        [42.358984375, 37.10859375],\n                        [42.45585937500002, 37.128710937499996],\n                        [42.63544921875001, 37.249267578125],\n                        [42.74111328125002, 37.3619140625],\n                        [42.77460937500001, 37.371875],\n                        [42.869140625, 37.334912109375],\n                        [42.936621093750006, 37.324755859374996],\n                        [43.09248046875001, 37.3673828125],\n                        [43.67578125, 37.22724609375],\n                        [43.83642578125, 37.223535156249994],\n                        [44.01318359375, 37.313525390624996],\n                        [44.11445312500001, 37.30185546875],\n                        [44.15625, 37.282958984375],\n                        [44.19179687500002, 37.249853515625],\n                        [44.20839843750002, 37.20263671875],\n                        [44.20166015625, 37.051806640624996],\n                        [44.281835937500006, 36.97802734375],\n                        [44.32558593750002, 37.0107421875],\n                        [44.401953125, 37.058496093749994],\n                        [44.60595703125, 37.176025390625],\n                        [44.66933593750002, 37.173583984375],\n                        [44.73095703125, 37.165283203125],\n                        [44.76513671875, 37.142431640625],\n                        [45.053125, 36.471630859375],\n                        [45.112402343750006, 36.40927734375],\n                        [45.1552734375, 36.407373046874994],\n                        [45.20654296875, 36.39716796875],\n                        [45.24111328125002, 36.35595703125],\n                        [45.36162109375002, 36.01533203125],\n                        [45.7763671875, 35.821826171874996],\n                        [46.16748046875, 35.820556640625],\n                        [46.2734375, 35.773242187499996],\n                        [46.2625, 35.744140625],\n                        [46.18095703125002, 35.711376953125],\n                        [46.03740234375002, 35.673144531249996],\n                        [45.97109375000002, 35.524169921875],\n                        [46.11210937500002, 35.321679687499994],\n                        [46.15468750000002, 35.196728515625],\n                        [46.1337890625, 35.12763671875],\n                        [46.04179687500002, 35.08017578125],\n                        [45.9208984375, 35.028515625],\n                        [45.678125, 34.7984375],\n                        [45.6375, 34.573828125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Iraq\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-15.543115234374994, 66.228515625],\n                        [-14.595849609374994, 66.38154296875],\n                        [-15.117382812499983, 66.125634765625],\n                        [-14.698193359374983, 66.02021484375],\n                        [-14.827099609374983, 65.7642578125],\n                        [-14.391845703125, 65.78740234375],\n                        [-14.473388671875, 65.575341796875],\n                        [-14.166943359374983, 65.64228515625],\n                        [-13.617871093749983, 65.5193359375],\n                        [-13.804785156249977, 65.35478515625],\n                        [-13.599316406249983, 65.0359375],\n                        [-14.04443359375, 64.74189453125],\n                        [-14.385107421874977, 64.74521484375],\n                        [-14.475390624999989, 64.493994140625],\n                        [-14.927392578124994, 64.319677734375],\n                        [-15.832910156249994, 64.17666015625],\n                        [-16.640332031249983, 63.865478515625],\n                        [-17.81572265624999, 63.71298828125],\n                        [-17.946923828124994, 63.5357421875],\n                        [-18.65361328124999, 63.406689453125],\n                        [-20.198144531249994, 63.555810546874994],\n                        [-20.494042968749994, 63.687353515625],\n                        [-20.413964843749994, 63.80517578125],\n                        [-20.65092773437499, 63.73740234375],\n                        [-21.15239257812499, 63.94453125],\n                        [-22.652197265624977, 63.827734375],\n                        [-22.701171875, 64.083203125],\n                        [-22.51005859374999, 63.991455078125],\n                        [-22.187597656249977, 64.039208984375],\n                        [-21.463330078124983, 64.379150390625],\n                        [-22.053369140624994, 64.313916015625],\n                        [-21.950341796874994, 64.514990234375],\n                        [-21.590625, 64.6263671875],\n                        [-22.10600585937499, 64.533056640625],\n                        [-22.467041015625, 64.794970703125],\n                        [-23.818994140624994, 64.73916015625],\n                        [-24.02617187499999, 64.863427734375],\n                        [-22.7880859375, 65.046484375],\n                        [-21.89213867187499, 65.048779296875],\n                        [-21.779980468749983, 65.1876953125],\n                        [-22.50908203124999, 65.19677734375],\n                        [-21.844384765624994, 65.44736328125],\n                        [-22.902490234374994, 65.58046875],\n                        [-23.89990234375, 65.407568359375],\n                        [-24.475683593749977, 65.5251953125],\n                        [-24.248925781249994, 65.614990234375],\n                        [-23.85673828124999, 65.53837890625],\n                        [-24.092626953124977, 65.77646484375],\n                        [-23.615917968749983, 65.67958984375],\n                        [-23.285351562499983, 65.75],\n                        [-23.832617187499977, 65.84921875],\n                        [-23.52495117187499, 65.880029296875],\n                        [-23.77734375, 66.017578125],\n                        [-23.434472656249994, 66.02421875],\n                        [-23.452539062499994, 66.181005859375],\n                        [-23.018994140624983, 65.98212890625],\n                        [-22.659863281249983, 66.025927734375],\n                        [-22.61601562499999, 65.86748046875],\n                        [-22.44169921874999, 65.90830078125],\n                        [-22.4453125, 66.07001953125],\n                        [-22.947900390624994, 66.212744140625],\n                        [-22.48442382812499, 66.26630859375],\n                        [-23.116943359375, 66.338720703125],\n                        [-22.9443359375, 66.429443359375],\n                        [-22.426123046874977, 66.430126953125],\n                        [-21.406884765624994, 66.0255859375],\n                        [-21.374902343749994, 65.74189453125],\n                        [-21.658447265625, 65.723583984375],\n                        [-21.12968749999999, 65.2666015625],\n                        [-20.804345703124994, 65.63642578125],\n                        [-20.454833984375, 65.571044921875],\n                        [-20.20751953125, 66.10009765625],\n                        [-19.489697265624983, 65.76806640625],\n                        [-19.382958984374994, 66.07568359375],\n                        [-18.845898437499983, 66.183935546875],\n                        [-18.141943359374977, 65.73408203125],\n                        [-18.29716796874999, 66.157421875],\n                        [-17.906982421875, 66.143310546875],\n                        [-17.550439453124994, 65.964404296875],\n                        [-17.153027343749983, 66.20283203125],\n                        [-16.838037109374994, 66.125244140625],\n                        [-16.485009765624994, 66.195947265625],\n                        [-16.540673828124994, 66.446728515625],\n                        [-16.24931640624999, 66.522900390625],\n                        [-15.985400390624989, 66.5146484375],\n                        [-15.543115234374994, 66.228515625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Iceland\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [35.78730468750001, 32.734912109374996],\n                        [35.572851562500006, 32.640869140625],\n                        [35.56904296875001, 32.619873046875],\n                        [35.55146484375001, 32.3955078125],\n                        [35.484375, 32.401660156249996],\n                        [35.40263671875002, 32.450634765625],\n                        [35.38671875, 32.493017578125],\n                        [35.303808593750006, 32.512939453125],\n                        [35.19326171875002, 32.534423828125],\n                        [35.065039062500006, 32.46044921875],\n                        [35.01054687500002, 32.338183593749996],\n                        [34.95595703125002, 32.1609375],\n                        [34.98974609375, 31.91328125],\n                        [34.97832031250002, 31.86640625],\n                        [34.95380859375001, 31.841259765624997],\n                        [34.96113281250001, 31.82333984375],\n                        [34.983007812500006, 31.81679687499999],\n                        [35.05322265625, 31.837939453124996],\n                        [35.12714843750001, 31.816748046875],\n                        [35.203710937500006, 31.75],\n                        [34.95097656250002, 31.602294921875],\n                        [34.88046875, 31.3681640625],\n                        [35.45058593750002, 31.479296875],\n                        [34.97343750000002, 29.555029296875],\n                        [34.904296875, 29.47734375],\n                        [34.24531250000001, 31.208300781249996],\n                        [34.34833984375001, 31.292919921874997],\n                        [34.350195312500006, 31.362744140624997],\n                        [34.52558593750001, 31.525634765625],\n                        [34.47734375000002, 31.584863281249994],\n                        [34.483984375, 31.59228515625],\n                        [34.67841796875001, 31.895703125],\n                        [35.10859375000001, 33.08369140625],\n                        [35.411230468750006, 33.07568359375],\n                        [35.869140625, 33.43173828125],\n                        [35.91347656250002, 32.94960937499999],\n                        [35.78730468750001, 32.734912109374996]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Israel\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [15.576562500000051, 38.220312500000034],\n                            [15.099511718750023, 37.45859375],\n                            [15.295703125000017, 37.05517578124997],\n                            [15.112597656250017, 36.687841796875006],\n                            [14.501855468750023, 36.798681640625034],\n                            [14.142968750000023, 37.103662109374994],\n                            [13.90546875000004, 37.10063476562502],\n                            [13.169921875000028, 37.47929687499996],\n                            [12.640234375000034, 37.594335937500034],\n                            [12.435546874999972, 37.819775390624955],\n                            [12.734375, 38.18305664062498],\n                            [12.902734375000023, 38.03486328124998],\n                            [13.291113281250034, 38.19145507812502],\n                            [13.788867187499989, 37.981201171875],\n                            [15.11875, 38.15273437500002],\n                            [15.498730468750011, 38.290869140625006],\n                            [15.576562500000051, 38.220312500000034]\n                        ]\n                    ],\n                    [\n                        [\n                            [8.478906250000023, 39.067529296874966],\n                            [8.421484375000034, 38.968652343749994],\n                            [8.366796875, 39.115917968749955],\n                            [8.478906250000023, 39.067529296874966]\n                        ]\n                    ],\n                    [\n                        [\n                            [8.28603515625008, 41.03984375],\n                            [8.205664062500034, 40.99746093750005],\n                            [8.320214843750023, 41.121875],\n                            [8.28603515625008, 41.03984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [9.632031250000011, 40.88203124999998],\n                            [9.805273437500063, 40.499560546875045],\n                            [9.642968750000023, 40.268408203125006],\n                            [9.5625, 39.16601562500006],\n                            [9.056347656250068, 39.23916015625002],\n                            [8.966601562500074, 38.963720703125034],\n                            [8.648535156250034, 38.92656250000002],\n                            [8.418164062500068, 39.205712890624966],\n                            [8.547753906250023, 39.83920898437506],\n                            [8.4078125, 39.91723632812497],\n                            [8.471289062500063, 40.29267578124998],\n                            [8.189941406250028, 40.651611328125],\n                            [8.22421875, 40.91333007812503],\n                            [8.571875, 40.85019531250006],\n                            [9.228417968750023, 41.257080078125],\n                            [9.615332031249977, 41.01728515624998],\n                            [9.632031250000011, 40.88203124999998]\n                        ]\n                    ],\n                    [\n                        [\n                            [10.395117187500034, 42.85815429687503],\n                            [10.419335937499994, 42.71318359374999],\n                            [10.13125, 42.742041015625006],\n                            [10.395117187500034, 42.85815429687503]\n                        ]\n                    ],\n                    [\n                        [\n                            [13.420996093750006, 46.212304687499994],\n                            [13.63251953125004, 46.17705078125002],\n                            [13.634960937499983, 46.15776367187499],\n                            [13.61660156250008, 46.133105468750045],\n                            [13.54804687500004, 46.08911132812503],\n                            [13.486425781250034, 46.03955078124997],\n                            [13.480273437500017, 46.00922851562501],\n                            [13.487695312500023, 45.987109375000045],\n                            [13.509179687500051, 45.973779296874994],\n                            [13.6005859375, 45.97978515624996],\n                            [13.663476562500023, 45.7919921875],\n                            [13.831152343750006, 45.680419921875],\n                            [13.719824218750063, 45.58759765625001],\n                            [13.628320312500051, 45.77094726562498],\n                            [13.206347656250074, 45.771386718749966],\n                            [12.27431640625008, 45.44604492187503],\n                            [12.225683593750034, 45.24150390625002],\n                            [12.523437500000028, 44.96796874999998],\n                            [12.248339843750045, 44.72250976562498],\n                            [12.396289062500074, 44.223876953125],\n                            [13.56416015625004, 43.57128906250003],\n                            [14.010449218750011, 42.68955078125006],\n                            [14.54072265625004, 42.24428710937502],\n                            [15.16875, 41.93403320312498],\n                            [16.164648437500034, 41.89619140624998],\n                            [15.900488281250034, 41.51206054687498],\n                            [17.954980468749994, 40.65517578125002],\n                            [18.460644531249983, 40.221044921875034],\n                            [18.34375, 39.82138671874998],\n                            [18.077929687500017, 39.93696289062498],\n                            [17.865039062500074, 40.28017578125002],\n                            [17.395800781250045, 40.34023437499999],\n                            [17.179980468750045, 40.50278320312498],\n                            [16.92822265625, 40.45805664062502],\n                            [16.521875, 39.74755859375003],\n                            [17.114550781250017, 39.38061523437497],\n                            [17.174609375000017, 38.998095703125045],\n                            [16.61669921875003, 38.800146484375034],\n                            [16.54560546875001, 38.40908203125002],\n                            [16.05683593750001, 37.941845703124955],\n                            [15.72451171875008, 37.93911132812502],\n                            [15.645800781250017, 38.034228515625045],\n                            [15.87890625, 38.61391601562502],\n                            [16.19677734375, 38.759228515624955],\n                            [16.20996093750003, 38.94111328124998],\n                            [15.692773437499994, 39.99018554687501],\n                            [14.95087890625004, 40.23901367187497],\n                            [14.94765625000008, 40.469335937500006],\n                            [14.765722656250063, 40.66840820312498],\n                            [14.339941406250006, 40.59882812500001],\n                            [14.460546875000063, 40.72871093750001],\n                            [14.04433593750008, 40.81225585937506],\n                            [13.733398437500057, 41.23564453124999],\n                            [13.088671875000074, 41.243847656249955],\n                            [12.630859374999972, 41.469677734374955],\n                            [11.637304687500063, 42.287548828124955],\n                            [11.141210937499977, 42.38989257812503],\n                            [11.167773437500074, 42.53515625000006],\n                            [10.708398437500023, 42.93632812499999],\n                            [10.514843750000011, 42.96752929687503],\n                            [10.188085937500063, 43.947509765625],\n                            [8.76582031250004, 44.42231445312501],\n                            [8.004980468750006, 43.87675781249999],\n                            [7.4931640625, 43.767138671875045],\n                            [7.637207031250057, 44.16484375],\n                            [7.318554687500068, 44.13798828125002],\n                            [6.900195312499989, 44.33574218749996],\n                            [6.99267578125, 44.82729492187502],\n                            [6.634765625000028, 45.06816406249996],\n                            [7.07832031250004, 45.23994140624998],\n                            [7.146386718750051, 45.381738281249994],\n                            [6.790917968750023, 45.740869140624966],\n                            [7.021093750000034, 45.92578124999997],\n                            [7.055761718749977, 45.90380859375003],\n                            [7.129003906249977, 45.88041992187499],\n                            [7.327929687500017, 45.912353515625],\n                            [7.9931640625, 46.01591796874996],\n                            [8.081542968750057, 46.25600585937502],\n                            [8.231933593750057, 46.341210937499966],\n                            [8.29853515625004, 46.403417968750034],\n                            [8.370703125, 46.44511718750002],\n                            [8.458398437500023, 46.24589843750002],\n                            [8.818554687500011, 46.0771484375],\n                            [8.826757812500006, 46.06103515625],\n                            [8.77802734375004, 45.996191406250034],\n                            [8.953710937500034, 45.83002929687501],\n                            [9.023730468750074, 45.845703125],\n                            [9.203417968750017, 46.21923828125],\n                            [9.304394531250068, 46.49555664062498],\n                            [9.399316406250023, 46.480664062499955],\n                            [9.427636718750023, 46.48232421875002],\n                            [9.528710937500023, 46.306201171875045],\n                            [9.57958984375, 46.29609375000001],\n                            [9.639453125000017, 46.29589843749997],\n                            [9.78779296875004, 46.34604492187498],\n                            [9.884472656250011, 46.36777343750006],\n                            [9.939257812500074, 46.36181640625],\n                            [10.041015625000028, 46.23808593750002],\n                            [10.08056640625, 46.22797851562501],\n                            [10.128320312500051, 46.238232421874955],\n                            [10.109667968750074, 46.36284179687502],\n                            [10.081933593750023, 46.420751953125006],\n                            [10.045605468750068, 46.44790039062505],\n                            [10.038281250000011, 46.483203125000045],\n                            [10.061230468750068, 46.54677734375002],\n                            [10.087011718750063, 46.59990234375002],\n                            [10.1375, 46.614355468750034],\n                            [10.195507812500068, 46.62109374999997],\n                            [10.4306640625, 46.55004882812497],\n                            [10.409352678571473, 46.6092047991071],\n                            [10.39794921875, 46.66503906250006],\n                            [10.406054687500045, 46.73486328124997],\n                            [10.452832031249983, 46.86494140625001],\n                            [10.47939453125008, 46.85512695312505],\n                            [10.579785156250011, 46.85371093750001],\n                            [10.689257812500017, 46.846386718749955],\n                            [10.759765625, 46.79331054687498],\n                            [10.828906250000045, 46.775244140625034],\n                            [10.927343750000034, 46.76948242187501],\n                            [10.993261718750034, 46.77700195312502],\n                            [11.02509765625004, 46.796972656250006],\n                            [11.063476562500057, 46.85913085937497],\n                            [11.133886718750006, 46.93618164062505],\n                            [11.244433593750045, 46.975683593750006],\n                            [11.433203125000063, 46.983056640624994],\n                            [11.527539062500011, 46.99741210937498],\n                            [11.775683593750017, 46.986083984375],\n                            [12.169433593750028, 47.082128906250006],\n                            [12.19716796875008, 47.075],\n                            [12.201269531250034, 47.060888671875034],\n                            [12.165527343750028, 47.028173828125034],\n                            [12.130761718750051, 46.98476562499999],\n                            [12.154101562500017, 46.93525390625004],\n                            [12.267968750000023, 46.83588867187504],\n                            [12.330078125, 46.75981445312499],\n                            [12.388281250000034, 46.70263671874997],\n                            [12.479199218749983, 46.672509765624966],\n                            [13.16875, 46.572656249999966],\n                            [13.3515625, 46.55791015624999],\n                            [13.490039062500045, 46.55556640625002],\n                            [13.7, 46.52026367187503],\n                            [13.679687500000057, 46.46289062499997],\n                            [13.63710937500008, 46.44853515624999],\n                            [13.563281250000045, 46.41508789062502],\n                            [13.399511718749977, 46.31752929687502],\n                            [13.420996093750006, 46.212304687499994]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Italy\", \"childNum\": 6 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-77.261474609375, 18.45742187499999],\n                        [-76.349853515625, 18.15185546875],\n                        [-76.21079101562499, 17.913525390624997],\n                        [-76.524609375, 17.8662109375],\n                        [-76.85322265625, 17.97373046874999],\n                        [-76.94414062499999, 17.848779296874994],\n                        [-77.11948242187499, 17.880078125],\n                        [-77.20498046875, 17.71494140624999],\n                        [-77.36142578124999, 17.833691406249997],\n                        [-77.76816406249999, 17.877392578124997],\n                        [-78.04448242187499, 18.173828125],\n                        [-78.339501953125, 18.28720703124999],\n                        [-78.21669921875, 18.44809570312499],\n                        [-77.8734375, 18.522216796875],\n                        [-77.261474609375, 18.45742187499999]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Jamaica\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-2.018652343749977, 49.23125],\n                        [-2.23583984375, 49.1763671875],\n                        [-2.220507812499989, 49.266357421875],\n                        [-2.018652343749977, 49.23125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Jersey\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [39.14541015625002, 32.12451171875],\n                        [38.9970703125, 32.007470703124994],\n                        [38.96230468750002, 31.994921875],\n                        [38.37548828125, 31.847460937499996],\n                        [38.111425781250006, 31.781152343749994],\n                        [37.49335937500001, 31.625878906249994],\n                        [37.215625, 31.556103515624997],\n                        [36.95859375, 31.491503906249996],\n                        [37.980078125, 30.5],\n                        [37.862890625, 30.442626953125],\n                        [37.66972656250002, 30.34814453125],\n                        [37.64990234375, 30.330957031249994],\n                        [37.63359375000002, 30.31328125],\n                        [37.55361328125002, 30.144580078124996],\n                        [37.49072265625, 30.01171875],\n                        [37.46923828125, 29.995068359374997],\n                        [36.75527343750002, 29.866015625],\n                        [36.70390625000002, 29.831640625],\n                        [36.591796875, 29.66611328125],\n                        [36.47607421875, 29.4951171875],\n                        [36.2828125, 29.355371093749994],\n                        [36.068457031250006, 29.200537109375],\n                        [34.95078125, 29.353515625],\n                        [34.97343750000002, 29.555029296875],\n                        [35.45058593750002, 31.479296875],\n                        [35.57207031250002, 32.237890625],\n                        [35.55146484375001, 32.3955078125],\n                        [35.56904296875001, 32.619873046875],\n                        [35.572851562500006, 32.640869140625],\n                        [35.78730468750001, 32.734912109374996],\n                        [36.3720703125, 32.3869140625],\n                        [36.818359375, 32.317285156249994],\n                        [38.773535156250006, 33.372216796874994],\n                        [39.04140625000002, 32.3056640625],\n                        [39.24746093750002, 32.350976562499994],\n                        [39.29277343750002, 32.24384765625],\n                        [39.14541015625002, 32.12451171875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Jordan\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [123.88867187499997, 24.280126953124977],\n                            [123.67978515625012, 24.317773437500023],\n                            [123.77148437499997, 24.41445312499999],\n                            [123.93486328125002, 24.362011718749983],\n                            [123.88867187499997, 24.280126953124977]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.29316406250004, 24.515917968750074],\n                            [124.13574218750003, 24.347607421874983],\n                            [124.08476562500002, 24.435839843750017],\n                            [124.30195312500004, 24.58710937500001],\n                            [124.29316406250004, 24.515917968750074]\n                        ]\n                    ],\n                    [\n                        [\n                            [125.44414062500002, 24.7431640625],\n                            [125.26894531250005, 24.732519531250063],\n                            [125.28359375, 24.871923828125034],\n                            [125.44414062500002, 24.7431640625]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.25878906249997, 26.65278320312501],\n                            [127.86708984375, 26.442480468749977],\n                            [127.80361328125005, 26.152539062499983],\n                            [127.653125, 26.0947265625],\n                            [127.90722656250003, 26.69360351562497],\n                            [128.09765624999997, 26.66777343749996],\n                            [128.25488281249997, 26.88188476562496],\n                            [128.25878906249997, 26.65278320312501]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.99814453125012, 27.720800781250006],\n                            [128.90000000000012, 27.727783203125],\n                            [128.9076171875, 27.897998046875045],\n                            [128.99814453125012, 27.720800781250006]\n                        ]\n                    ],\n                    [\n                        [\n                            [129.45253906250005, 28.20898437499997],\n                            [129.3664062500001, 28.127734375000045],\n                            [129.16464843750012, 28.24975585937503],\n                            [129.68955078125012, 28.517480468750023],\n                            [129.45253906250005, 28.20898437499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.6227539062501, 30.262988281250017],\n                            [130.44560546875002, 30.264697265625017],\n                            [130.38808593750005, 30.38818359375003],\n                            [130.49716796875006, 30.465527343749983],\n                            [130.64355468749997, 30.388964843750017],\n                            [130.6227539062501, 30.262988281250017]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.95976562500007, 30.39692382812504],\n                            [130.87031250000004, 30.444238281249994],\n                            [131.06035156250007, 30.828466796875006],\n                            [130.95976562500007, 30.39692382812504]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.38105468750004, 32.42373046875002],\n                            [130.24169921874997, 32.462792968749994],\n                            [130.46142578124997, 32.515722656250034],\n                            [130.38105468750004, 32.42373046875002]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.08251953124997, 32.22968750000001],\n                            [129.9601562500001, 32.24375],\n                            [130.00976562499997, 32.521630859374994],\n                            [130.16777343750002, 32.54121093749998],\n                            [130.19951171875002, 32.34057617187506],\n                            [130.08251953124997, 32.22968750000001]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.66533203125002, 32.783886718749955],\n                            [128.89453124999997, 32.65214843750002],\n                            [128.69296875000012, 32.60473632812506],\n                            [128.66533203125002, 32.783886718749955]\n                        ]\n                    ],\n                    [\n                        [\n                            [129.07695312500002, 32.84028320312498],\n                            [128.99726562500004, 32.95185546874998],\n                            [129.10976562500005, 33.13256835937503],\n                            [129.18193359375002, 32.99311523437504],\n                            [129.07695312500002, 32.84028320312498]\n                        ]\n                    ],\n                    [\n                        [\n                            [129.49179687500006, 33.22304687499999],\n                            [129.37041015625002, 33.176025390625],\n                            [129.56992187500006, 33.36103515625004],\n                            [129.49179687500006, 33.22304687499999]\n                        ]\n                    ],\n                    [\n                        [\n                            [129.79570312500007, 33.74882812499999],\n                            [129.67480468749997, 33.73969726562498],\n                            [129.71728515624997, 33.8583984375],\n                            [129.79570312500007, 33.74882812499999]\n                        ]\n                    ],\n                    [\n                        [\n                            [131.17460937500007, 33.602587890625045],\n                            [131.69628906250003, 33.60283203124999],\n                            [131.53740234375007, 33.274072265624994],\n                            [131.89658203125006, 33.25458984375001],\n                            [131.8478515625001, 33.118066406249994],\n                            [132.0021484375001, 32.882373046875045],\n                            [131.6603515625001, 32.465625],\n                            [131.33720703125007, 31.4046875],\n                            [131.07080078124997, 31.436865234374977],\n                            [131.09843750000002, 31.256152343750017],\n                            [130.68574218750004, 31.01513671875003],\n                            [130.77626953125, 31.70629882812497],\n                            [130.65507812500002, 31.71840820312505],\n                            [130.5560546875, 31.563085937500034],\n                            [130.58876953125, 31.178515625000017],\n                            [130.20068359374997, 31.291894531250023],\n                            [130.14726562500002, 31.40849609374996],\n                            [130.2941406250001, 31.45068359375003],\n                            [130.3219726562501, 31.601464843750023],\n                            [130.18789062500005, 31.768847656250017],\n                            [130.19443359375012, 32.090771484374955],\n                            [130.64052734375005, 32.61923828124998],\n                            [130.49785156250002, 32.65693359375001],\n                            [130.547265625, 32.83159179687499],\n                            [130.2375, 33.177636718749966],\n                            [130.12685546875005, 33.10483398437506],\n                            [130.175, 32.851318359375],\n                            [130.32646484375002, 32.852636718750006],\n                            [130.34042968750012, 32.70185546875004],\n                            [130.05410156250005, 32.770800781250045],\n                            [129.76855468749997, 32.57099609375001],\n                            [129.82675781250006, 32.72534179687503],\n                            [129.67910156250005, 33.059960937499966],\n                            [129.99169921875003, 32.85156249999997],\n                            [129.58007812500003, 33.23627929687501],\n                            [129.61015625000002, 33.34365234375005],\n                            [129.844140625, 33.32177734375003],\n                            [129.82568359374997, 33.43701171875006],\n                            [130.36503906250007, 33.634472656249955],\n                            [130.4837890625, 33.834619140624966],\n                            [130.715625, 33.92778320312502],\n                            [130.953125, 33.87202148437504],\n                            [131.17460937500007, 33.602587890625045]\n                        ]\n                    ],\n                    [\n                        [\n                            [132.266015625, 33.945166015625006],\n                            [132.44492187500006, 33.91318359374998],\n                            [132.20878906250007, 33.87285156250002],\n                            [132.266015625, 33.945166015625006]\n                        ]\n                    ],\n                    [\n                        [\n                            [129.27949218750004, 34.123388671875006],\n                            [129.18642578125, 34.14501953125006],\n                            [129.21484374999997, 34.320654296875034],\n                            [129.3371093750001, 34.284765625],\n                            [129.27949218750004, 34.123388671875006]\n                        ]\n                    ],\n                    [\n                        [\n                            [134.35742187500003, 34.25634765625],\n                            [134.6375, 34.22661132812499],\n                            [134.73886718750012, 33.82050781250001],\n                            [134.37705078125012, 33.60839843749997],\n                            [134.18164062500003, 33.24721679687502],\n                            [133.95869140625004, 33.44833984375006],\n                            [133.63203125000004, 33.51098632812503],\n                            [133.28593750000007, 33.35996093749998],\n                            [132.97724609375004, 32.84199218749998],\n                            [132.80429687500006, 32.75200195312502],\n                            [132.6417968750001, 32.76245117187503],\n                            [132.70898437500003, 32.90249023437505],\n                            [132.49511718749997, 32.91660156249998],\n                            [132.41279296875004, 33.43046875],\n                            [132.0326171875, 33.339990234374994],\n                            [132.64306640624997, 33.68994140624997],\n                            [132.93515625000006, 34.09531250000006],\n                            [133.19306640625004, 33.93320312499998],\n                            [133.58203124999997, 34.01713867187502],\n                            [133.60263671875006, 34.24384765625001],\n                            [133.94833984375006, 34.34804687500002],\n                            [134.35742187500003, 34.25634765625]\n                        ]\n                    ],\n                    [\n                        [\n                            [134.35185546875002, 34.48364257812503],\n                            [134.25185546875, 34.42304687500004],\n                            [134.18212890625003, 34.51923828124998],\n                            [134.35185546875002, 34.48364257812503]\n                        ]\n                    ],\n                    [\n                        [\n                            [134.9328125000001, 34.28813476562499],\n                            [134.82441406250004, 34.202929687500045],\n                            [134.66787109375005, 34.294140624999955],\n                            [135.00468750000002, 34.54404296874998],\n                            [134.9328125000001, 34.28813476562499]\n                        ]\n                    ],\n                    [\n                        [\n                            [129.38564453125, 34.35366210937502],\n                            [129.26669921875012, 34.37045898437506],\n                            [129.45107421875005, 34.68657226562499],\n                            [129.38564453125, 34.35366210937502]\n                        ]\n                    ],\n                    [\n                        [\n                            [133.37050781250005, 36.203857421875],\n                            [133.23925781249997, 36.178759765625045],\n                            [133.20615234375006, 36.293408203124955],\n                            [133.29570312500002, 36.34013671874996],\n                            [133.37050781250005, 36.203857421875]\n                        ]\n                    ],\n                    [\n                        [\n                            [138.34404296875007, 37.822119140625006],\n                            [138.22519531250006, 37.82939453124996],\n                            [138.25, 38.078466796875006],\n                            [138.50361328125004, 38.31591796875006],\n                            [138.45361328124997, 38.07568359375006],\n                            [138.57519531249997, 38.065527343750034],\n                            [138.34404296875007, 37.822119140625006]\n                        ]\n                    ],\n                    [\n                        [\n                            [141.22929687500007, 41.37265625],\n                            [141.45546875000005, 41.404736328124955],\n                            [141.43046875000002, 40.72333984374998],\n                            [141.7970703125001, 40.29116210937502],\n                            [141.97695312500005, 39.428808593750034],\n                            [141.90078125, 39.111328125],\n                            [141.5462890625, 38.762841796874966],\n                            [141.4674804687501, 38.404150390625006],\n                            [141.10839843750003, 38.33793945312502],\n                            [140.9621093750001, 38.148876953124955],\n                            [141.00166015625004, 37.11464843750002],\n                            [140.57353515625007, 36.23134765625002],\n                            [140.87402343749997, 35.72495117187506],\n                            [140.457421875, 35.51025390625],\n                            [140.35468750000004, 35.18144531249999],\n                            [139.8439453125001, 34.914892578125034],\n                            [139.82646484375002, 35.29667968750002],\n                            [140.096875, 35.58515624999998],\n                            [139.83476562500002, 35.658056640625006],\n                            [139.65000000000012, 35.40913085937501],\n                            [139.675, 35.149267578125006],\n                            [139.47441406250002, 35.298535156249955],\n                            [139.24941406250005, 35.27802734375004],\n                            [139.08603515625006, 34.83916015624999],\n                            [138.8375, 34.619238281250034],\n                            [138.80273437499997, 34.97480468749998],\n                            [138.90361328125002, 35.02524414062506],\n                            [138.71962890625, 35.12407226562502],\n                            [138.18906250000012, 34.596337890624994],\n                            [137.543359375, 34.66420898437505],\n                            [137.06171875000004, 34.58281249999999],\n                            [137.27519531250002, 34.77250976562499],\n                            [136.96328125000005, 34.83491210937501],\n                            [136.87128906250004, 34.733105468749955],\n                            [136.89707031250006, 35.03554687500002],\n                            [136.80419921874997, 35.05029296875],\n                            [136.53300781250007, 34.678369140624994],\n                            [136.8802734375, 34.43359375000006],\n                            [136.8537109375001, 34.324072265625034],\n                            [136.32988281250007, 34.17685546875006],\n                            [135.91621093750004, 33.561718749999955],\n                            [135.69531250000003, 33.48696289062502],\n                            [135.4528320312501, 33.55336914062505],\n                            [135.12792968749997, 34.006982421874994],\n                            [135.10009765624997, 34.288378906250045],\n                            [135.41591796875, 34.61748046875002],\n                            [134.74003906250007, 34.765234375],\n                            [134.246875, 34.71386718750003],\n                            [133.96826171874997, 34.52729492187504],\n                            [133.14238281250002, 34.30244140624998],\n                            [132.65654296875007, 34.24609375000003],\n                            [132.31259765625006, 34.32495117187503],\n                            [132.14648437499997, 33.83876953125002],\n                            [131.74052734375007, 34.05205078125002],\n                            [130.91884765625, 33.97573242187502],\n                            [130.88925781250012, 34.261816406250034],\n                            [131.00419921875007, 34.39257812500003],\n                            [131.35439453125, 34.41318359375006],\n                            [132.92294921875006, 35.511279296875045],\n                            [133.98125, 35.50722656250002],\n                            [135.17431640625003, 35.74707031250003],\n                            [135.32695312500002, 35.52553710937502],\n                            [135.68027343750006, 35.503125],\n                            [135.903125, 35.60688476562498],\n                            [136.09531250000006, 35.767626953125045],\n                            [136.06748046875006, 36.11684570312505],\n                            [136.69814453125005, 36.742041015625034],\n                            [136.84345703125004, 37.38212890624999],\n                            [137.32265625, 37.52207031249998],\n                            [136.89990234375003, 37.11767578125],\n                            [137.01669921875006, 36.83720703124999],\n                            [137.24628906250004, 36.753173828125],\n                            [137.5140625, 36.95156250000002],\n                            [138.31992187500012, 37.21840820312502],\n                            [138.88505859375007, 37.84394531250001],\n                            [139.36386718750006, 38.09902343750002],\n                            [139.80195312500004, 38.881591796875],\n                            [140.06474609375002, 39.624414062499994],\n                            [139.99472656250006, 39.855078125],\n                            [139.74150390625002, 39.92084960937498],\n                            [140.01113281250005, 40.26035156250006],\n                            [139.92285156250003, 40.59843750000002],\n                            [140.28125, 40.84609375000002],\n                            [140.3444335937501, 41.203320312499955],\n                            [140.62763671875004, 41.195410156250034],\n                            [140.74863281250012, 40.830322265625],\n                            [140.93603515625003, 40.940771484375034],\n                            [141.1185546875, 40.88227539062501],\n                            [141.24423828125006, 41.20561523437499],\n                            [140.80058593750002, 41.138818359374966],\n                            [140.80185546875012, 41.253662109375],\n                            [140.9369140625, 41.50556640624998],\n                            [141.22929687500007, 41.37265625]\n                        ]\n                    ],\n                    [\n                        [\n                            [139.48125, 42.08100585937498],\n                            [139.43134765625004, 42.19956054687498],\n                            [139.55839843750002, 42.235205078125034],\n                            [139.48125, 42.08100585937498]\n                        ]\n                    ],\n                    [\n                        [\n                            [141.29541015625003, 45.11933593750001],\n                            [141.14531250000002, 45.153906250000034],\n                            [141.19375, 45.24785156249999],\n                            [141.29541015625003, 45.11933593750001]\n                        ]\n                    ],\n                    [\n                        [\n                            [141.07275390624997, 45.33286132812498],\n                            [141.03398437500007, 45.26933593750002],\n                            [140.97167968749997, 45.465478515624994],\n                            [141.07275390624997, 45.33286132812498]\n                        ]\n                    ],\n                    [\n                        [\n                            [143.82431640625012, 44.11699218749999],\n                            [144.71523437500005, 43.92797851562503],\n                            [145.36953125000005, 44.32739257812506],\n                            [145.13964843750003, 43.6625],\n                            [145.34082031249997, 43.30253906249999],\n                            [145.83300781249997, 43.38593750000001],\n                            [144.92138671874997, 43.00092773437498],\n                            [143.96933593750006, 42.88139648437499],\n                            [143.42949218750002, 42.41889648437498],\n                            [143.2365234375001, 42.000195312499955],\n                            [141.85136718750007, 42.57905273437501],\n                            [141.40664062500005, 42.54692382812496],\n                            [140.98613281250002, 42.34213867187498],\n                            [140.70976562500002, 42.555615234374955],\n                            [140.48046875000003, 42.559375],\n                            [140.32666015625003, 42.29335937499999],\n                            [141.15097656250012, 41.80507812499999],\n                            [140.99951171874997, 41.73740234375006],\n                            [140.65986328125004, 41.815576171874994],\n                            [140.3849609375001, 41.51928710937503],\n                            [140.08515625000004, 41.43408203125],\n                            [139.99531250000004, 41.57641601562503],\n                            [140.10839843749997, 41.912939453125034],\n                            [139.83544921874997, 42.278076171875],\n                            [139.86015625000002, 42.58173828125004],\n                            [140.43222656250012, 42.95410156250006],\n                            [140.39238281250002, 43.303125],\n                            [141.13818359374997, 43.17993164062506],\n                            [141.37412109375006, 43.279638671875006],\n                            [141.7609375000001, 44.482519531250034],\n                            [141.58300781250003, 45.15595703125001],\n                            [141.66796874999997, 45.401269531249966],\n                            [141.93769531250004, 45.509521484375],\n                            [142.88476562499997, 44.670117187499955],\n                            [143.82431640625012, 44.11699218749999]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Japan\", \"childNum\": 28 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [77.04863281249999, 35.109912109374996],\n                        [76.927734375, 35.346630859375],\n                        [76.88222656250002, 35.4357421875],\n                        [76.81279296874999, 35.571826171874996],\n                        [76.76689453124999, 35.66171875],\n                        [76.87890625, 35.61328125],\n                        [77.09003906250001, 35.552050781249996],\n                        [77.29482421875002, 35.508154296875],\n                        [77.44648437500001, 35.4755859375],\n                        [77.57255859374999, 35.471826171874994],\n                        [77.72402343750002, 35.48056640625],\n                        [77.79941406250003, 35.495898437499996],\n                        [77.42343750000003, 35.302587890625],\n                        [77.16855468750003, 35.171533203124994],\n                        [77.04863281249999, 35.109912109374996]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Siachen Glacier\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [50.184472656249994, 44.854638671874994],\n                            [49.99511718750003, 44.93696289062498],\n                            [50.10986328124997, 45.08193359375002],\n                            [50.038867187500074, 44.949121093749966],\n                            [50.184472656249994, 44.854638671874994]\n                        ]\n                    ],\n                    [\n                        [\n                            [87.32285156250012, 49.085791015625006],\n                            [86.8083007812501, 49.04970703125002],\n                            [86.54941406250012, 48.52861328125002],\n                            [85.7494140625, 48.38505859374999],\n                            [85.52597656250006, 47.915625],\n                            [85.65664062500005, 47.254638671875],\n                            [85.484765625, 47.06352539062496],\n                            [84.78613281249997, 46.83071289062505],\n                            [84.66660156250006, 46.97236328125004],\n                            [84.016015625, 46.97050781250002],\n                            [83.02949218750004, 47.18593750000002],\n                            [82.31523437500002, 45.59492187499998],\n                            [82.61162109375007, 45.424267578124955],\n                            [82.52148437500003, 45.12548828125],\n                            [82.26660156249997, 45.21909179687498],\n                            [81.94492187500006, 45.16083984375001],\n                            [81.69199218750012, 45.34936523437497],\n                            [80.05917968750012, 45.006445312500006],\n                            [79.871875, 44.88378906249997],\n                            [80.48154296875006, 44.71464843749999],\n                            [80.35527343750002, 44.09726562500006],\n                            [80.78574218750006, 43.16157226562504],\n                            [80.39023437500006, 43.043115234374966],\n                            [80.53896484375005, 42.873486328124955],\n                            [80.20224609375012, 42.73447265624998],\n                            [80.209375, 42.190039062500006],\n                            [80.07128906249997, 42.302978515625],\n                            [79.92109375000004, 42.41313476562496],\n                            [79.49013671875, 42.45756835937496],\n                            [79.42822265624997, 42.483496093750006],\n                            [79.20302734375005, 42.66601562499997],\n                            [79.16484375000007, 42.759033203125],\n                            [79.1266601562501, 42.775732421875034],\n                            [76.98808593750007, 42.97358398437501],\n                            [76.64648437500003, 42.928808593750034],\n                            [76.50917968750005, 42.91889648437498],\n                            [75.9322265625, 42.92851562499999],\n                            [75.84033203125003, 42.9375],\n                            [75.78955078124997, 42.93291015624999],\n                            [75.68173828125, 42.83046875],\n                            [75.04765625000007, 42.904394531250034],\n                            [74.20908203125006, 43.24038085937502],\n                            [73.88603515625002, 43.132568359375],\n                            [73.55625, 43.002783203125006],\n                            [73.45019531249997, 42.703027343749966],\n                            [73.421875, 42.59350585937503],\n                            [73.49296875000007, 42.409033203125034],\n                            [73.41162109375003, 42.41977539062498],\n                            [73.316015625, 42.46699218750001],\n                            [73.2829101562501, 42.50410156250004],\n                            [72.85507812500006, 42.561132812500006],\n                            [72.75292968750003, 42.63789062500001],\n                            [72.54316406250004, 42.67773437500006],\n                            [72.27578125, 42.757666015625006],\n                            [71.76054687500002, 42.82148437500004],\n                            [71.5142578125, 42.766943359375006],\n                            [71.42207031250004, 42.78315429687504],\n                            [71.25664062500002, 42.733544921874966],\n                            [70.89287109375007, 42.339990234374994],\n                            [70.94677734374997, 42.24868164062505],\n                            [69.15361328125002, 41.42524414062498],\n                            [68.58408203125, 40.876269531250045],\n                            [68.57265625, 40.62265624999998],\n                            [68.29189453125, 40.656103515625034],\n                            [68.04765625000007, 40.80927734374998],\n                            [68.11308593750007, 41.02861328124999],\n                            [67.9357421875001, 41.19658203125002],\n                            [66.70966796875004, 41.17915039062501],\n                            [66.49863281250006, 41.99487304687503],\n                            [66.00957031250007, 42.00488281250003],\n                            [66.1002929687501, 42.99082031249998],\n                            [65.80302734375002, 42.87695312500006],\n                            [65.49619140625, 43.310546875],\n                            [64.9054687500001, 43.714697265625006],\n                            [64.44316406250007, 43.55117187499999],\n                            [63.20703125000003, 43.62797851562502],\n                            [61.99023437500003, 43.492138671874955],\n                            [61.007910156250006, 44.39379882812497],\n                            [58.555273437500006, 45.55537109375001],\n                            [55.97568359375006, 44.99492187499996],\n                            [55.97744140625005, 41.32221679687504],\n                            [55.434375, 41.296289062499994],\n                            [54.85380859375002, 41.965185546875006],\n                            [54.120996093749994, 42.335205078125],\n                            [53.0558593750001, 42.14775390624999],\n                            [52.4938476562501, 41.780371093750034],\n                            [52.59658203125005, 42.760156249999966],\n                            [51.898242187500074, 42.86962890624997],\n                            [51.61601562500002, 43.15844726562503],\n                            [51.29541015624997, 43.17412109375002],\n                            [51.30175781249997, 43.48237304687501],\n                            [50.8307617187501, 44.192773437499966],\n                            [50.331152343750006, 44.32548828125002],\n                            [50.25292968749997, 44.461523437500006],\n                            [50.409472656250074, 44.6240234375],\n                            [51.543554687500006, 44.53100585937506],\n                            [51.009375, 44.92182617187501],\n                            [51.4157226562501, 45.35786132812501],\n                            [53.20039062500004, 45.33198242187498],\n                            [52.77382812499999, 45.57275390625],\n                            [53.13525390625003, 46.19165039062497],\n                            [53.069433593750006, 46.85605468750006],\n                            [52.48320312500002, 46.99067382812504],\n                            [52.13828125, 46.82861328124997],\n                            [51.178027343750074, 47.110156250000045],\n                            [49.886328125, 46.59565429687504],\n                            [49.347460937500074, 46.51914062499998],\n                            [49.232226562500074, 46.33715820312503],\n                            [48.54121093750004, 46.60561523437502],\n                            [48.558398437500074, 46.75712890624999],\n                            [48.959375, 46.77460937499998],\n                            [48.16699218750003, 47.70878906249996],\n                            [47.48193359374997, 47.80390624999998],\n                            [47.292382812499994, 47.74091796875004],\n                            [47.06464843750004, 48.23247070312499],\n                            [46.660937500000074, 48.41225585937502],\n                            [46.70263671875003, 48.80556640625002],\n                            [47.031347656250006, 49.150292968749994],\n                            [46.80205078125002, 49.36708984375002],\n                            [46.889550781249994, 49.69697265625001],\n                            [47.42919921874997, 50.35795898437502],\n                            [47.7057617187501, 50.37797851562502],\n                            [48.33496093750003, 49.858251953125006],\n                            [48.7589843750001, 49.92832031250006],\n                            [48.625097656250006, 50.61269531250005],\n                            [49.32343750000004, 50.851708984374966],\n                            [49.49804687500003, 51.08359375000006],\n                            [50.246875, 51.28950195312498],\n                            [50.79394531249997, 51.729199218749955],\n                            [51.16347656250005, 51.6474609375],\n                            [51.344531250000074, 51.47534179687503],\n                            [52.21914062499999, 51.709375],\n                            [52.57119140625005, 51.481640624999955],\n                            [53.33808593750004, 51.48237304687504],\n                            [54.139746093750006, 51.04077148437503],\n                            [54.555273437500006, 50.535791015624994],\n                            [54.64160156250003, 51.011572265625034],\n                            [55.68623046875004, 50.582861328125006],\n                            [56.49140625000004, 51.01953124999997],\n                            [57.01171874999997, 51.06518554687503],\n                            [57.44218750000002, 50.88886718749998],\n                            [57.83886718750003, 51.091650390625006],\n                            [58.359179687500074, 51.063818359375034],\n                            [58.88369140625005, 50.694433593750006],\n                            [59.4523437500001, 50.62041015625002],\n                            [59.523046875, 50.492871093749955],\n                            [59.812402343749994, 50.58203125],\n                            [60.05859374999997, 50.850292968749955],\n                            [60.42480468749997, 50.67915039062498],\n                            [60.94228515625005, 50.69550781250004],\n                            [61.38945312500002, 50.86103515625001],\n                            [61.55468750000003, 51.32460937500005],\n                            [60.464746093749994, 51.651171875000045],\n                            [60.03027343749997, 51.93325195312505],\n                            [60.99453125000005, 52.33686523437504],\n                            [60.77441406249997, 52.67578124999997],\n                            [61.047460937500006, 52.97246093750002],\n                            [62.08271484375004, 53.00541992187499],\n                            [61.65986328125004, 53.22846679687504],\n                            [61.19921874999997, 53.28715820312502],\n                            [61.22890625, 53.445898437500006],\n                            [61.53496093750002, 53.52329101562506],\n                            [60.97949218749997, 53.62172851562505],\n                            [61.231054687500006, 54.01948242187498],\n                            [61.92871093750003, 53.94648437500004],\n                            [64.46123046875002, 54.38417968750002],\n                            [65.08837890624997, 54.340185546875034],\n                            [65.476953125, 54.62329101562497],\n                            [68.15585937500006, 54.97670898437505],\n                            [68.20625, 55.16093750000002],\n                            [68.9772460937501, 55.389599609374955],\n                            [70.18242187500002, 55.162451171875034],\n                            [70.73808593750007, 55.30517578125],\n                            [71.18554687500003, 54.59931640624998],\n                            [71.09316406250005, 54.21220703124999],\n                            [72.00449218750006, 54.20566406249998],\n                            [72.18603515625003, 54.32563476562501],\n                            [72.44677734375003, 53.94184570312498],\n                            [72.62226562500004, 54.13432617187502],\n                            [73.22988281250005, 53.957812500000045],\n                            [73.71240234375003, 54.04238281250002],\n                            [73.30566406250003, 53.707226562499955],\n                            [73.40693359375004, 53.44755859374999],\n                            [73.85898437500006, 53.61972656249998],\n                            [74.35156250000003, 53.487646484375006],\n                            [74.45195312500007, 53.64726562500002],\n                            [75.22021484374997, 53.89379882812506],\n                            [75.43720703125004, 54.08964843749999],\n                            [76.8373046875, 54.4423828125],\n                            [76.65458984375007, 54.14526367187503],\n                            [76.42167968750007, 54.151513671874966],\n                            [76.48476562500005, 54.02255859374998],\n                            [77.85996093750006, 53.269189453124994],\n                            [79.98623046875, 50.774560546874966],\n                            [80.42363281250002, 50.94628906249997],\n                            [80.44804687500002, 51.18334960937503],\n                            [80.73525390625, 51.29340820312498],\n                            [81.12724609375002, 51.19106445312502],\n                            [81.0714843750001, 50.96875],\n                            [81.38828125000006, 50.95649414062501],\n                            [81.46591796875006, 50.73984375],\n                            [82.49394531250007, 50.72758789062499],\n                            [82.76083984375012, 50.89335937500002],\n                            [83.35732421875005, 50.99458007812504],\n                            [83.94511718750007, 50.774658203125],\n                            [84.32324218749997, 50.239160156249966],\n                            [84.9894531250001, 50.061425781249994],\n                            [85.2326171875001, 49.61582031249998],\n                            [86.1808593750001, 49.49931640624996],\n                            [86.67548828125004, 49.77729492187501],\n                            [86.62646484374997, 49.56269531250001],\n                            [87.32285156250012, 49.085791015625006]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Kazakhstan\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [35.325292968750006, 5.364892578124994],\n                        [35.745019531249994, 5.343994140625],\n                        [35.80029296874997, 5.156933593749983],\n                        [35.77929687499997, 5.105566406250006],\n                        [35.756152343750074, 4.950488281250031],\n                        [35.76308593750005, 4.808007812500051],\n                        [36.02197265625003, 4.468115234374991],\n                        [36.90556640625002, 4.411474609374991],\n                        [37.15458984375002, 4.254541015624994],\n                        [37.944921875, 3.746728515625023],\n                        [38.0861328125001, 3.648828124999966],\n                        [38.22529296875004, 3.61899414062502],\n                        [38.45156250000005, 3.604833984374977],\n                        [38.608007812500006, 3.600097656249986],\n                        [39.49443359375002, 3.45610351562496],\n                        [39.65751953125002, 3.577832031249983],\n                        [39.79033203125002, 3.754248046875034],\n                        [39.8421875, 3.851464843750037],\n                        [40.765234375, 4.273046875000034],\n                        [41.02080078125002, 4.057470703124991],\n                        [41.22089843750004, 3.943554687499969],\n                        [41.372460937499994, 3.94619140624998],\n                        [41.48193359375003, 3.96328125],\n                        [41.737695312499994, 3.979052734375003],\n                        [41.88398437500004, 3.977734375000011],\n                        [41.6134765625001, 3.59047851562498],\n                        [41.34179687499997, 3.20166015625],\n                        [40.964453125, 2.814648437500026],\n                        [40.9787109375001, -0.870312500000011],\n                        [41.249804687500074, -1.220507812499946],\n                        [41.4269531250001, -1.449511718749974],\n                        [41.521875, -1.572265625000028],\n                        [41.53271484374997, -1.695312499999957],\n                        [41.26748046875005, -1.945019531250026],\n                        [40.889746093750006, -2.023535156250034],\n                        [40.89824218750002, -2.269921874999966],\n                        [40.64414062500006, -2.53945312499998],\n                        [40.22246093750002, -2.688378906250037],\n                        [40.1154296875001, -3.250585937499991],\n                        [39.8609375, -3.576757812500006],\n                        [39.49091796875004, -4.478417968750023],\n                        [39.221777343750006, -4.692382812500014],\n                        [37.608203125000074, -3.497070312500028],\n                        [37.643847656250074, -3.045410156250028],\n                        [33.90322265625005, -1.002050781250034],\n                        [33.94316406250002, 0.173779296874969],\n                        [34.160937500000074, 0.605175781250026],\n                        [34.4108398437501, 0.867285156250034],\n                        [34.48173828125002, 1.042138671875051],\n                        [34.79863281250002, 1.24453125],\n                        [34.976464843749994, 1.719628906250051],\n                        [34.97753906249997, 1.861914062499991],\n                        [34.9640625000001, 2.06240234374998],\n                        [34.8830078125001, 2.417919921875026],\n                        [34.90576171875003, 2.4796875],\n                        [34.44785156250006, 3.163476562500037],\n                        [34.40722656249997, 3.357519531250034],\n                        [34.39941406249997, 3.412695312500006],\n                        [34.44179687499999, 3.60625],\n                        [34.43769531250004, 3.650585937499969],\n                        [34.392871093750074, 3.691503906250048],\n                        [34.26708984375003, 3.733154296875],\n                        [34.16503906250003, 3.812988281250014],\n                        [34.18574218750004, 3.869775390625037],\n                        [34.13203125000004, 3.889160156249986],\n                        [33.97607421874997, 4.220214843750028],\n                        [34.176855468750006, 4.419091796875037],\n                        [34.38017578125002, 4.620654296874974],\n                        [34.6398437500001, 4.875488281250028],\n                        [34.878320312499994, 5.109570312500026],\n                        [35.08447265624997, 5.31186523437502],\n                        [35.268359375000074, 5.492285156250006],\n                        [35.325292968750006, 5.364892578124994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Kenya\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [72.63994140625002, 39.385986328125],\n                        [72.22998046875, 39.20751953125],\n                        [72.14736328125002, 39.2607421875],\n                        [72.08417968750001, 39.31064453125],\n                        [72.04277343750002, 39.3521484375],\n                        [71.77861328125002, 39.277978515624994],\n                        [71.73222656250002, 39.422998046874994],\n                        [71.50332031250002, 39.478808593749996],\n                        [71.51738281250002, 39.553857421874994],\n                        [71.50302734375, 39.582177734374994],\n                        [71.4703125, 39.603662109374994],\n                        [70.79931640625, 39.3947265625],\n                        [70.50117187500001, 39.587353515625],\n                        [69.29765625000002, 39.524804687499994],\n                        [69.2447265625, 39.827099609375],\n                        [69.27880859375, 39.917773437499996],\n                        [69.3072265625, 39.968554687499996],\n                        [69.36542968750001, 39.947070312499996],\n                        [69.43193359375002, 39.909765625],\n                        [69.47626953125001, 39.919726562499996],\n                        [69.47099609375002, 39.990625],\n                        [69.46875, 40.020751953125],\n                        [69.966796875, 40.20224609375],\n                        [70.59921875, 39.974511718749994],\n                        [70.990625, 40.2548828125],\n                        [71.3046875, 40.286914062499996],\n                        [71.69248046875, 40.15234375],\n                        [72.13125, 40.438623046874994],\n                        [72.3892578125, 40.427392578124994],\n                        [72.40205078125001, 40.578076171875],\n                        [72.6041015625, 40.525439453124996],\n                        [73.13212890625002, 40.82851562499999],\n                        [72.65830078125, 40.869921875],\n                        [72.36406250000002, 41.04345703125],\n                        [72.294921875, 41.039941406249994],\n                        [72.21308593750001, 41.0142578125],\n                        [72.18730468750002, 41.025927734374996],\n                        [72.18095703125002, 41.118457031249996],\n                        [72.16425781250001, 41.173730468749994],\n                        [72.11542968750001, 41.186572265624996],\n                        [72.05244140625001, 41.16474609375],\n                        [71.95849609375, 41.187060546874996],\n                        [71.87861328125001, 41.19501953125],\n                        [71.8580078125, 41.311376953125],\n                        [71.79248046875, 41.413134765624996],\n                        [71.75771484375002, 41.428027343749996],\n                        [71.70068359375, 41.454003906249994],\n                        [71.66494140625002, 41.5412109375],\n                        [71.6375, 41.5341796875],\n                        [71.60224609375001, 41.503271484375],\n                        [71.60625, 41.367431640625],\n                        [71.54560546875001, 41.308056640625],\n                        [71.5, 41.307470703125],\n                        [71.4208984375, 41.34189453125],\n                        [71.40839843750001, 41.136035156249996],\n                        [71.39306640625, 41.123388671875],\n                        [71.11074218750002, 41.152636718749996],\n                        [70.86044921875, 41.224902343749996],\n                        [70.734375, 41.400537109374994],\n                        [70.18095703125002, 41.571435546874994],\n                        [70.85664062500001, 42.030810546874996],\n                        [71.0322265625, 42.077783203124994],\n                        [71.228515625, 42.162890625],\n                        [71.23232421875002, 42.186279296875],\n                        [71.21269531250002, 42.206445312499994],\n                        [71.12998046875, 42.25],\n                        [71.03603515625002, 42.28466796875],\n                        [70.97900390625, 42.266552734375],\n                        [70.94677734375, 42.248681640624994],\n                        [70.89287109375002, 42.339990234374994],\n                        [71.25664062500002, 42.733544921874994],\n                        [71.42207031250001, 42.783154296875],\n                        [71.5142578125, 42.766943359375],\n                        [71.76054687500002, 42.821484375],\n                        [72.16181640625001, 42.760693359375],\n                        [72.27578125000002, 42.757666015625],\n                        [72.54316406250001, 42.677734375],\n                        [72.7529296875, 42.637890625],\n                        [72.855078125, 42.5611328125],\n                        [73.28291015625001, 42.5041015625],\n                        [73.316015625, 42.4669921875],\n                        [73.41162109375, 42.419775390625],\n                        [73.49296875000002, 42.409033203125],\n                        [73.421875, 42.593505859375],\n                        [73.4501953125, 42.703027343749994],\n                        [73.55625, 43.002783203125],\n                        [73.88603515625002, 43.132568359375],\n                        [74.20908203125, 43.240380859374994],\n                        [75.04765625000002, 42.90439453125],\n                        [75.68173828125, 42.83046875],\n                        [75.78955078125, 42.932910156249996],\n                        [75.84033203125, 42.9375],\n                        [75.9322265625, 42.928515625],\n                        [76.50917968750002, 42.918896484375],\n                        [76.646484375, 42.92880859375],\n                        [76.98808593749999, 42.973583984375],\n                        [79.12666015625001, 42.775732421875],\n                        [79.20302734375002, 42.666015625],\n                        [79.29550781250003, 42.604833984375],\n                        [79.36777343750003, 42.547216796875],\n                        [79.42822265625, 42.48349609375],\n                        [79.92109375000001, 42.413134765624996],\n                        [80.0712890625, 42.302978515625],\n                        [80.209375, 42.1900390625],\n                        [80.24619140625003, 42.059814453125],\n                        [80.23515624999999, 42.04345703125],\n                        [80.21621093750002, 42.032421875],\n                        [79.90966796875, 42.014990234375],\n                        [79.84042968750003, 41.995751953124994],\n                        [79.76611328125, 41.898876953125],\n                        [78.74257812500002, 41.56005859375],\n                        [78.54316406250001, 41.4595703125],\n                        [78.44287109375, 41.417529296874996],\n                        [78.36240234375003, 41.371630859374996],\n                        [78.34628906250003, 41.2814453125],\n                        [78.12343750000002, 41.075634765625],\n                        [77.95644531250002, 41.050683593749994],\n                        [77.81523437499999, 41.055615234375],\n                        [77.71933593750003, 41.024316406249994],\n                        [77.58173828125001, 40.9927734375],\n                        [76.98662109374999, 41.03916015625],\n                        [76.90771484375, 41.024169921875],\n                        [76.82402343749999, 40.982324218749994],\n                        [76.70839843750002, 40.818115234375],\n                        [76.6611328125, 40.779638671875],\n                        [76.63984375000001, 40.742236328124996],\n                        [76.62216796875003, 40.662353515625],\n                        [76.57792968749999, 40.577880859375],\n                        [76.48017578125001, 40.449511718749996],\n                        [76.39638671875002, 40.389794921874994],\n                        [76.31855468750001, 40.35224609375],\n                        [76.25830078125, 40.43076171875],\n                        [75.87197265625002, 40.30322265625],\n                        [75.67714843750002, 40.305810546874994],\n                        [75.55556640625002, 40.6251953125],\n                        [75.52080078125002, 40.6275390625],\n                        [75.24101562500002, 40.480273437499996],\n                        [75.111328125, 40.4541015625],\n                        [75.0044921875, 40.449511718749996],\n                        [74.865625, 40.493505859375],\n                        [74.80126953125, 40.428515625],\n                        [74.83046875000002, 40.32851562499999],\n                        [74.41191406250002, 40.13720703125],\n                        [74.24267578125, 40.092041015625],\n                        [74.08515625000001, 40.07431640625],\n                        [73.99160156250002, 40.043115234374994],\n                        [73.93876953125002, 39.978808593749996],\n                        [73.88457031250002, 39.8779296875],\n                        [73.85625, 39.828662109374996],\n                        [73.83535156250002, 39.800146484375],\n                        [73.83974609375002, 39.762841796874994],\n                        [73.88251953125001, 39.71455078125],\n                        [73.9146484375, 39.606494140624996],\n                        [73.90712890625002, 39.57851562499999],\n                        [73.87275390625001, 39.53330078125],\n                        [73.82294921875001, 39.48896484375],\n                        [73.71572265625002, 39.462255859375],\n                        [73.63164062500002, 39.448876953124994],\n                        [73.47041015625001, 39.460595703124994],\n                        [73.38740234375001, 39.442724609375],\n                        [73.33613281250001, 39.412353515625],\n                        [73.2349609375, 39.374560546874996],\n                        [73.10927734375002, 39.3619140625],\n                        [72.63994140625002, 39.385986328125]\n                    ],\n                    [\n                        [70.66416015625, 39.85546875],\n                        [70.56708984375001, 39.866601562499994],\n                        [70.49775390625001, 39.882421875],\n                        [70.48281250000002, 39.882714843749994],\n                        [70.4892578125, 39.863037109375],\n                        [70.5595703125, 39.790917968749994],\n                        [70.61210937500002, 39.786767578124994],\n                        [70.70166015625, 39.82529296875],\n                        [70.66416015625, 39.85546875]\n                    ],\n                    [\n                        [71.20615234375, 39.892578125],\n                        [71.22871093750001, 40.048144531249996],\n                        [71.08037109375002, 40.079882812499996],\n                        [71.02412109375001, 40.149169921875],\n                        [71.00546875, 40.152294921875],\n                        [70.96064453125001, 40.08798828125],\n                        [71.04482421875002, 39.992529296875],\n                        [71.04365234375001, 39.976318359375],\n                        [71.01171875, 39.8951171875],\n                        [71.06425781250002, 39.884912109374994],\n                        [71.15625, 39.883447265624994],\n                        [71.20615234375, 39.892578125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Kyrgyzstan\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [104.42636718750006, 10.411230468749991],\n                        [103.87050781250005, 10.655126953125034],\n                        [103.58710937500004, 10.552197265625026],\n                        [103.54042968750005, 10.668701171875043],\n                        [103.721875, 10.890136718750043],\n                        [103.5324218750001, 11.146679687499997],\n                        [103.35361328125006, 10.921582031250054],\n                        [103.15283203124997, 10.913720703125051],\n                        [103.12548828124997, 11.460644531250011],\n                        [102.9486328125, 11.773486328124974],\n                        [102.93388671875002, 11.706689453125037],\n                        [102.73662109375007, 12.089794921875011],\n                        [102.75566406250002, 12.42626953125],\n                        [102.49960937500012, 12.669970703125003],\n                        [102.33632812500005, 13.560302734375014],\n                        [102.546875, 13.585693359375043],\n                        [102.90927734375006, 14.136718750000028],\n                        [103.19941406250004, 14.332617187499977],\n                        [104.77900390625004, 14.427832031250006],\n                        [105.07412109375005, 14.227441406250037],\n                        [105.12597656250003, 14.280957031250011],\n                        [105.16914062500004, 14.336083984374966],\n                        [105.1833007812501, 14.346240234374989],\n                        [105.18554687500003, 14.319091796874972],\n                        [105.20703125000003, 14.259375],\n                        [105.24570312500006, 14.200537109374977],\n                        [105.35019531250006, 14.109570312500011],\n                        [105.53154296875007, 14.156152343749994],\n                        [105.73974609375003, 14.084960937500057],\n                        [105.83144531250005, 13.976611328125003],\n                        [105.9044921875001, 13.924511718750054],\n                        [106.06679687500005, 13.921191406250003],\n                        [106.12470703125004, 14.049121093750031],\n                        [106.09667968749997, 14.127099609375023],\n                        [106.00410156250004, 14.262890624999983],\n                        [105.97890625, 14.343017578125043],\n                        [106.00839843750012, 14.357177734375],\n                        [106.1652343750001, 14.372363281249989],\n                        [106.19072265625007, 14.388134765624997],\n                        [106.22539062500002, 14.476220703125009],\n                        [106.26796875, 14.466210937500009],\n                        [106.35498046875003, 14.454785156249997],\n                        [106.44697265625004, 14.515039062500009],\n                        [106.50146484375003, 14.578222656250006],\n                        [106.53115234375005, 14.549414062499991],\n                        [106.5636718750001, 14.505078125000026],\n                        [106.59921875000006, 14.479394531250037],\n                        [106.66542968750005, 14.441308593749994],\n                        [106.73818359375005, 14.387744140625017],\n                        [106.78349609375002, 14.335107421875037],\n                        [106.81992187500006, 14.314697265625057],\n                        [106.91318359375006, 14.329394531250031],\n                        [106.93808593750006, 14.327343750000054],\n                        [106.99218750000003, 14.391015624999966],\n                        [107.03017578125, 14.425683593750009],\n                        [107.06240234375, 14.415771484375043],\n                        [107.109375, 14.416699218750054],\n                        [107.29267578125004, 14.592382812500048],\n                        [107.37988281250003, 14.555322265625051],\n                        [107.41474609375004, 14.56289062499999],\n                        [107.51943359375005, 14.705078125],\n                        [107.3314453125, 14.126611328125009],\n                        [107.60546874999997, 13.437792968750017],\n                        [107.47539062500002, 13.030371093749963],\n                        [107.50644531250006, 12.364550781250031],\n                        [107.39335937500002, 12.260498046874972],\n                        [107.21210937500004, 12.30400390624996],\n                        [106.70009765625, 11.979296874999974],\n                        [106.41386718750002, 11.9484375],\n                        [106.39921875000007, 11.687011718750028],\n                        [106.0060546875001, 11.758007812500011],\n                        [105.85146484375005, 11.635009765625],\n                        [105.85605468750006, 11.294287109375048],\n                        [106.16093750000002, 11.037109375000057],\n                        [106.16396484375005, 10.794921875],\n                        [105.85332031250007, 10.86357421874996],\n                        [105.75507812500004, 10.989990234375043],\n                        [105.40576171875003, 10.95161132812504],\n                        [105.3146484375001, 10.845166015625026],\n                        [105.04570312500002, 10.911376953125014],\n                        [105.04638671874997, 10.701660156250014],\n                        [104.85058593749997, 10.534472656249974],\n                        [104.42636718750006, 10.411230468749991]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Cambodia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-157.34213867187503, 1.855566406250034],\n                            [-157.17578125, 1.73984375],\n                            [-157.57895507812498, 1.902050781249997],\n                            [-157.43583984374993, 1.84726562500002],\n                            [-157.365185546875, 1.94609375],\n                            [-157.44189453125003, 2.025048828125009],\n                            [-157.321875, 1.968554687500045],\n                            [-157.34213867187503, 1.855566406250034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-159.3390625, 3.923535156249983],\n                            [-159.27475585937503, 3.796582031250054],\n                            [-159.40903320312503, 3.87324218750004],\n                            [-159.3390625, 3.923535156249983]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Kiribati\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [126.32695312500002, 33.2236328125],\n                            [126.16562500000012, 33.31201171875],\n                            [126.33769531250002, 33.46040039062501],\n                            [126.90117187500002, 33.51513671874997],\n                            [126.87285156250002, 33.34116210937498],\n                            [126.32695312500002, 33.2236328125]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.23369140625002, 34.370507812499994],\n                            [126.12285156250002, 34.443945312500034],\n                            [126.34384765625012, 34.544921875],\n                            [126.23369140625002, 34.370507812499994]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.17197265625006, 34.73115234375001],\n                            [126.00751953125004, 34.86748046874999],\n                            [126.07841796875002, 34.914843750000045],\n                            [126.17197265625006, 34.73115234375001]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.0658203125, 34.80585937500004],\n                            [128.05468750000003, 34.70805664062502],\n                            [127.87343750000005, 34.73496093749998],\n                            [127.8322265625001, 34.87451171875],\n                            [128.0658203125, 34.80585937500004]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.74101562500007, 34.798535156249955],\n                            [128.64667968750004, 34.73686523437502],\n                            [128.48925781250003, 34.86528320312496],\n                            [128.66796875000003, 35.0087890625],\n                            [128.74101562500007, 34.798535156249955]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.52070312500004, 37.73681640625003],\n                            [126.516015625, 37.60468750000001],\n                            [126.42333984375003, 37.62363281250006],\n                            [126.41162109374997, 37.82265625000002],\n                            [126.52070312500004, 37.73681640625003]\n                        ]\n                    ],\n                    [\n                        [\n                            [128.37460937500012, 38.6234375],\n                            [129.41826171875002, 37.059033203124955],\n                            [129.40351562500004, 36.052148437499994],\n                            [129.57285156250006, 36.05053710937503],\n                            [129.4191406250001, 35.49785156249996],\n                            [129.07675781250006, 35.12270507812502],\n                            [128.5109375000001, 35.10097656250002],\n                            [128.44394531250012, 34.87036132812503],\n                            [128.03623046875006, 35.02197265625],\n                            [127.71484374999997, 34.95468749999998],\n                            [127.71542968750012, 34.72104492187498],\n                            [127.40429687499997, 34.823095703125006],\n                            [127.47910156250012, 34.625244140625],\n                            [127.324609375, 34.463281249999966],\n                            [127.17343750000006, 34.54614257812497],\n                            [127.24707031249997, 34.755126953125],\n                            [126.89746093749997, 34.438867187499966],\n                            [126.75478515625005, 34.511865234374994],\n                            [126.53144531250004, 34.31425781249999],\n                            [126.26445312500002, 34.67324218750002],\n                            [126.52451171875006, 34.697900390624966],\n                            [126.59335937500012, 34.824365234374994],\n                            [126.42070312500002, 34.823388671874966],\n                            [126.29111328125012, 35.154150390625034],\n                            [126.61406250000007, 35.57099609375004],\n                            [126.4884765625001, 35.647070312500006],\n                            [126.75302734375006, 35.871972656249994],\n                            [126.5404296875, 36.166162109374966],\n                            [126.4876953125, 36.69379882812498],\n                            [126.18085937500004, 36.69160156249998],\n                            [126.16054687500005, 36.77192382812501],\n                            [126.48701171875004, 37.00747070312502],\n                            [126.78447265625007, 36.94843749999998],\n                            [126.87207031249997, 36.82446289062506],\n                            [126.97685546875002, 36.93940429687501],\n                            [126.74638671875002, 37.19355468750001],\n                            [126.63388671875012, 37.78183593750006],\n                            [127.09033203125003, 38.28388671875001],\n                            [128.03896484375, 38.30854492187498],\n                            [128.37460937500012, 38.6234375]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Korea\", \"childNum\": 7 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [48.27539062499997, 29.624316406250017],\n                            [48.17968750000003, 29.611425781250063],\n                            [48.081445312499994, 29.798925781250063],\n                            [48.1847656250001, 29.978857421875034],\n                            [48.348242187500006, 29.78266601562504],\n                            [48.27539062499997, 29.624316406250017]\n                        ]\n                    ],\n                    [\n                        [\n                            [48.442480468750006, 28.542919921874983],\n                            [47.671289062499994, 28.53315429687504],\n                            [47.433203125, 28.989550781250017],\n                            [46.53144531250004, 29.09624023437499],\n                            [46.69375, 29.259667968749966],\n                            [46.76933593750002, 29.347460937500017],\n                            [46.90585937500006, 29.5375],\n                            [47.14824218750002, 30.0009765625],\n                            [47.64375, 30.097314453125023],\n                            [47.75390624999997, 30.076611328124955],\n                            [47.97871093750004, 29.98281250000005],\n                            [48.00566406250002, 29.835791015625034],\n                            [48.143457031249994, 29.57246093750001],\n                            [47.96962890625005, 29.61669921874997],\n                            [47.72265624999997, 29.393017578124955],\n                            [48.0514648437501, 29.355371093750023],\n                            [48.442480468750006, 28.542919921874983]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Kuwait\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [102.12744140625011, 22.37919921874999],\n                        [102.58251953125006, 21.904296875000057],\n                        [102.66201171875008, 21.676025390625057],\n                        [102.73857421875005, 21.677929687500125],\n                        [102.77109375000015, 21.70966796875001],\n                        [102.79824218750014, 21.797949218750034],\n                        [102.81591796875, 21.807373046875],\n                        [102.94960937500008, 21.681347656250068],\n                        [102.85117187500009, 21.26591796874999],\n                        [102.8837890625, 21.202587890625068],\n                        [103.1044921875, 20.89165039062499],\n                        [103.21074218749999, 20.840625],\n                        [103.46357421874995, 20.779833984375102],\n                        [103.6350585937501, 20.697070312500102],\n                        [104.10136718750005, 20.945507812500125],\n                        [104.1953125, 20.91396484375008],\n                        [104.349609375, 20.82109374999999],\n                        [104.58320312500001, 20.646679687499955],\n                        [104.53271484375, 20.554882812500125],\n                        [104.47861328124998, 20.529589843750102],\n                        [104.40781250000015, 20.485742187500023],\n                        [104.36777343750015, 20.441406250000057],\n                        [104.39218750000015, 20.424755859375068],\n                        [104.49619140625003, 20.41367187499992],\n                        [104.61884765624995, 20.374511718750114],\n                        [104.65644531250001, 20.328515624999966],\n                        [104.66191406250005, 20.289013671875125],\n                        [104.67695312500007, 20.224707031249977],\n                        [104.69873046875006, 20.205322265625114],\n                        [104.84785156250007, 20.202441406250045],\n                        [104.88867187500006, 20.169091796875023],\n                        [104.92919921874994, 20.082812500000045],\n                        [104.92792968750007, 20.01811523437499],\n                        [104.81513671875001, 19.90400390625001],\n                        [104.80175781250011, 19.836132812500068],\n                        [104.74316406250006, 19.754736328124977],\n                        [104.58789062500006, 19.61875],\n                        [104.54628906250014, 19.610546875000068],\n                        [104.25986328125003, 19.685498046875068],\n                        [104.06279296875005, 19.678417968750068],\n                        [104.03203124999999, 19.67514648437492],\n                        [104.0134765625001, 19.646484374999943],\n                        [104.05156250000005, 19.564160156250068],\n                        [104.06289062500002, 19.482568359375136],\n                        [104.02753906250013, 19.420458984375102],\n                        [103.93203125000002, 19.366064453125034],\n                        [103.89638671875002, 19.339990234375023],\n                        [103.89160156249994, 19.30498046874999],\n                        [105.146484375, 18.650976562499977],\n                        [105.14541015625014, 18.616796874999977],\n                        [105.08701171875015, 18.49624023437508],\n                        [105.11455078125005, 18.405273437500057],\n                        [105.45820312500007, 18.154296875000057],\n                        [105.51855468750011, 18.077441406250045],\n                        [105.58847656250015, 17.983691406249932],\n                        [105.69140625, 17.737841796874932],\n                        [106.00625, 17.415283203124943],\n                        [106.26953125, 17.216796875000057],\n                        [106.33339843750002, 17.14370117187508],\n                        [106.42597656250007, 17.00253906250009],\n                        [106.50224609374999, 16.9541015625],\n                        [106.52597656250003, 16.876611328125023],\n                        [106.53369140625, 16.821044921875057],\n                        [106.54619140625005, 16.650732421874977],\n                        [106.65644531250013, 16.492626953125125],\n                        [106.73955078124999, 16.452539062500136],\n                        [106.79160156250015, 16.490332031249977],\n                        [106.83242187500008, 16.526269531250023],\n                        [106.85107421875, 16.515625],\n                        [106.89277343750013, 16.396533203125102],\n                        [106.93066406250006, 16.353125],\n                        [107.39638671875008, 16.04301757812499],\n                        [107.39199218750008, 15.951660156250057],\n                        [107.36064453125005, 15.921728515624977],\n                        [107.18886718750008, 15.838623046875114],\n                        [107.16591796875002, 15.802490234375],\n                        [107.27939453125003, 15.618701171875045],\n                        [107.33876953125002, 15.560498046875125],\n                        [107.56425781249999, 15.3916015625],\n                        [107.62167968750015, 15.309863281250045],\n                        [107.653125, 15.255224609375091],\n                        [107.63369140625008, 15.18984375000008],\n                        [107.58964843749999, 15.118457031250102],\n                        [107.55527343750009, 15.057031250000023],\n                        [107.48037109375014, 14.979882812500136],\n                        [107.5046875000001, 14.91591796875008],\n                        [107.52451171875003, 14.871826171874943],\n                        [107.51376953124998, 14.817382812500057],\n                        [107.51943359375008, 14.705078125000114],\n                        [107.46513671875005, 14.664990234375125],\n                        [107.41474609375007, 14.56289062500008],\n                        [107.37988281250006, 14.555322265625136],\n                        [107.29267578125007, 14.592382812500034],\n                        [107.109375, 14.416699218749955],\n                        [107.06240234375008, 14.415771484374943],\n                        [107.03017578125008, 14.425683593750023],\n                        [106.99218749999994, 14.39101562500008],\n                        [106.93808593750015, 14.327343750000068],\n                        [106.91318359375003, 14.329394531249932],\n                        [106.81992187500003, 14.314697265624943],\n                        [106.7834960937501, 14.335107421875023],\n                        [106.73818359375008, 14.387744140625102],\n                        [106.66542968750002, 14.441308593750023],\n                        [106.59921875000003, 14.479394531250136],\n                        [106.56367187500007, 14.505078125000011],\n                        [106.53115234375002, 14.549414062499977],\n                        [106.50146484375, 14.578222656250034],\n                        [106.22539062500005, 14.476220703125023],\n                        [106.1907226562501, 14.388134765625011],\n                        [106.16523437500007, 14.372363281249989],\n                        [106.00839843750009, 14.357177734375114],\n                        [105.97890625000014, 14.343017578125057],\n                        [106.00410156250013, 14.262890625000068],\n                        [106.09667968750011, 14.127099609375136],\n                        [106.12470703124995, 14.049121093750045],\n                        [106.06679687500008, 13.921191406250102],\n                        [105.90449218750007, 13.924511718750068],\n                        [105.83144531250008, 13.976611328124989],\n                        [105.73974609375006, 14.084960937500057],\n                        [105.5315429687501, 14.156152343750023],\n                        [105.35019531250009, 14.109570312500125],\n                        [105.24570312500015, 14.200537109374977],\n                        [105.20703125000006, 14.259375],\n                        [105.18554687499994, 14.319091796875],\n                        [105.18330078125001, 14.346240234374989],\n                        [105.24365234375006, 14.367871093749955],\n                        [105.34218750000008, 14.416699218749955],\n                        [105.42265624999993, 14.471630859374955],\n                        [105.47558593750006, 14.530126953124977],\n                        [105.49736328125005, 14.590673828125034],\n                        [105.52304687500015, 14.843310546874989],\n                        [105.54667968749999, 14.932470703125034],\n                        [105.53339843750013, 15.041601562500091],\n                        [105.49042968750007, 15.127587890625023],\n                        [105.49042968750007, 15.256591796875],\n                        [105.615625, 15.488281249999943],\n                        [105.63886718750013, 15.585937499999943],\n                        [105.64101562500002, 15.656542968749932],\n                        [105.62207031250006, 15.699951171875114],\n                        [105.39892578125011, 15.829882812500102],\n                        [105.40625, 15.987451171875023],\n                        [105.33066406250003, 16.037890625000045],\n                        [105.1487304687501, 16.09355468749999],\n                        [105.04716796874999, 16.16025390625009],\n                        [104.81933593749994, 16.466064453125057],\n                        [104.75058593750015, 16.647558593750034],\n                        [104.74355468750014, 16.884375],\n                        [104.75898437500013, 17.0771484375],\n                        [104.81601562499998, 17.30029296875],\n                        [104.73964843750008, 17.461669921875],\n                        [104.428125, 17.698974609375057],\n                        [104.32265625000002, 17.815820312500023],\n                        [104.19619140625002, 17.988378906250034],\n                        [104.04873046875002, 18.216699218749966],\n                        [103.94960937500008, 18.318994140625023],\n                        [103.89882812500002, 18.295312500000023],\n                        [103.79228515624999, 18.31650390625009],\n                        [103.62968750000005, 18.382568359375057],\n                        [103.48798828124995, 18.41816406250001],\n                        [103.36699218750005, 18.42333984375],\n                        [103.28828124999995, 18.408398437499955],\n                        [103.25175781249999, 18.373486328125125],\n                        [103.24892578125014, 18.338964843750034],\n                        [103.27958984374999, 18.304980468750045],\n                        [103.26318359375, 18.278466796875136],\n                        [103.19970703125006, 18.25947265625001],\n                        [103.14853515625009, 18.221728515624932],\n                        [103.09121093750014, 18.13823242187499],\n                        [103.05136718750003, 18.02851562500001],\n                        [102.80742187500005, 17.945556640625],\n                        [102.71757812500005, 17.892236328125136],\n                        [102.67519531250014, 17.851757812500068],\n                        [102.68007812500008, 17.824121093750136],\n                        [102.66064453125, 17.8179687499999],\n                        [102.61679687500015, 17.833349609375034],\n                        [102.59824218750009, 17.926757812500057],\n                        [102.55253906249999, 17.965087890625057],\n                        [102.4587890625001, 17.984619140624943],\n                        [102.35185546874999, 18.045947265625045],\n                        [102.14824218750005, 18.203857421875057],\n                        [102.10146484375014, 18.21064453125001],\n                        [102.03457031250002, 18.169824218750023],\n                        [101.94746093750001, 18.081494140624955],\n                        [101.87548828125011, 18.046435546874932],\n                        [101.81865234375005, 18.064648437500125],\n                        [101.77480468750002, 18.033398437500125],\n                        [101.6875, 17.889404296875114],\n                        [101.56367187500001, 17.820507812500125],\n                        [101.55507812500002, 17.812353515625034],\n                        [101.41367187500015, 17.71875],\n                        [101.16748046875011, 17.4990234375],\n                        [101.10517578125001, 17.479541015625102],\n                        [100.9084960937501, 17.583886718750023],\n                        [101.14394531250008, 18.14262695312499],\n                        [101.1375, 18.286865234375057],\n                        [101.0505859375001, 18.407031250000045],\n                        [101.04697265625003, 18.441992187500034],\n                        [101.28632812499995, 18.977148437500034],\n                        [101.19755859374999, 19.327929687500045],\n                        [101.22080078125015, 19.486621093750045],\n                        [101.21191406250011, 19.548339843750057],\n                        [100.51357421875008, 19.553466796875],\n                        [100.39765625000013, 19.756103515625057],\n                        [100.51953125000006, 20.177929687500068],\n                        [100.31796875000003, 20.385888671875136],\n                        [100.2180664062501, 20.339599609375114],\n                        [100.13974609375015, 20.245410156250102],\n                        [100.11494140625007, 20.25766601562492],\n                        [100.12246093750002, 20.316650390625057],\n                        [100.12968750000005, 20.372216796875023],\n                        [100.1838867187501, 20.589111328124943],\n                        [100.2493164062501, 20.730273437499932],\n                        [100.32607421875008, 20.795703124999932],\n                        [100.40742187499995, 20.823242187500057],\n                        [100.56513671875013, 20.82509765625008],\n                        [100.62294921875002, 20.85957031250001],\n                        [100.61767578125, 20.87924804687509],\n                        [100.54931640625011, 20.884228515625068],\n                        [100.5222656250001, 20.921923828125102],\n                        [100.53613281250006, 20.992382812500068],\n                        [100.703125, 21.25136718750008],\n                        [101.0803710937501, 21.46865234375008],\n                        [101.13886718750013, 21.567480468749977],\n                        [101.19667968750002, 21.522070312499977],\n                        [101.17539062500009, 21.407519531250102],\n                        [101.21992187500013, 21.342431640625136],\n                        [101.21181640625008, 21.278222656250023],\n                        [101.22441406249999, 21.22373046874992],\n                        [101.24785156249993, 21.197314453125045],\n                        [101.28144531250007, 21.184130859375045],\n                        [101.44355468750001, 21.230810546874977],\n                        [101.54238281250008, 21.234277343750136],\n                        [101.70478515625013, 21.150146484375057],\n                        [101.728125, 21.15639648437508],\n                        [101.78349609374999, 21.204150390625045],\n                        [101.8005859375001, 21.212597656249955],\n                        [101.7229492187501, 21.314941406250057],\n                        [101.74726562500007, 21.60576171874999],\n                        [101.7439453125001, 21.77797851562508],\n                        [101.73652343750001, 21.826513671874977],\n                        [101.52451171874998, 22.253662109375],\n                        [101.56787109375011, 22.2763671875],\n                        [101.6199218750001, 22.327441406250102],\n                        [101.67148437500009, 22.462304687500023],\n                        [101.70751953125, 22.486572265625],\n                        [101.73876953125011, 22.495263671874966],\n                        [101.75996093750001, 22.490332031250034],\n                        [101.841796875, 22.388476562500102],\n                        [102.02441406250006, 22.439208984375114],\n                        [102.09150390625007, 22.412255859375136],\n                        [102.12744140625011, 22.37919921874999]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Lao PDR\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [35.869140625, 33.43173828125],\n                        [35.411230468750006, 33.07568359375],\n                        [35.10859375000001, 33.08369140625],\n                        [35.64785156250002, 34.2482421875],\n                        [35.97626953125001, 34.629199218749996],\n                        [36.383886718750006, 34.65791015625],\n                        [36.32988281250002, 34.499609375],\n                        [36.50439453125, 34.432373046875],\n                        [36.5849609375, 34.221240234374996],\n                        [36.27783203125, 33.92529296875],\n                        [36.36503906250002, 33.83935546875],\n                        [35.98613281250002, 33.75263671875],\n                        [36.03447265625002, 33.58505859375],\n                        [35.869140625, 33.43173828125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Lebanon\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-8.486425781249977, 7.558496093749994],\n                        [-8.408740234374989, 7.411816406249997],\n                        [-8.324511718749989, 6.920019531249991],\n                        [-8.587890625, 6.490527343749989],\n                        [-8.287109375, 6.319042968749997],\n                        [-7.981591796874994, 6.2861328125],\n                        [-7.888623046874983, 6.23486328125],\n                        [-7.800927734374994, 6.038916015624991],\n                        [-7.730371093749994, 5.919042968749991],\n                        [-7.636132812499994, 5.90771484375],\n                        [-7.454394531249989, 5.84130859375],\n                        [-7.39990234375, 5.550585937499989],\n                        [-7.585058593749977, 4.916748046875],\n                        [-7.574658203124983, 4.572314453124989],\n                        [-7.544970703124989, 4.351318359375],\n                        [-8.259033203125, 4.589990234374994],\n                        [-9.132177734374977, 5.054638671874997],\n                        [-10.2763671875, 6.07763671875],\n                        [-11.291601562499977, 6.688232421875],\n                        [-11.507519531249983, 6.906542968749989],\n                        [-11.267675781249977, 7.232617187499997],\n                        [-10.878076171874994, 7.538232421874994],\n                        [-10.6474609375, 7.759375],\n                        [-10.570849609374989, 8.071142578124991],\n                        [-10.516748046874994, 8.125292968749989],\n                        [-10.359814453124983, 8.187939453124997],\n                        [-10.283203125, 8.485156249999989],\n                        [-10.233056640624994, 8.488818359374989],\n                        [-10.147412109374983, 8.519726562499997],\n                        [-10.064355468749994, 8.429882812499997],\n                        [-9.781982421875, 8.537695312499991],\n                        [-9.518261718749983, 8.34609375],\n                        [-9.369140625, 7.703808593749997],\n                        [-9.463818359374983, 7.415869140624991],\n                        [-9.11757812499999, 7.215917968749991],\n                        [-8.8896484375, 7.2626953125],\n                        [-8.659765624999977, 7.688378906249994],\n                        [-8.486425781249977, 7.558496093749994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Liberia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [25.150488281250006, 31.654980468749997],\n                        [24.85273437500001, 31.334814453125],\n                        [24.96142578125, 30.678515625],\n                        [24.703222656250006, 30.201074218749994],\n                        [24.980273437500017, 29.181884765625],\n                        [24.980273437500017, 25.5888671875],\n                        [24.980273437500017, 21.995849609375],\n                        [24.9794921875, 20.002587890624994],\n                        [23.980273437500017, 19.99594726562499],\n                        [23.980273437500017, 19.496630859375003],\n                        [20.14765625000001, 21.38925781249999],\n                        [15.984082031250011, 23.445214843749994],\n                        [14.97900390625, 22.99619140624999],\n                        [14.215527343750011, 22.619677734375003],\n                        [13.48125, 23.18017578125],\n                        [11.967871093750006, 23.517871093750003],\n                        [11.507617187500017, 24.314355468749994],\n                        [10.686132812500006, 24.55136718749999],\n                        [10.395898437500023, 24.485595703125],\n                        [10.255859375, 24.591015625],\n                        [10.000683593750011, 25.332080078125003],\n                        [9.4482421875, 26.067138671875],\n                        [9.491406250000011, 26.333740234375],\n                        [9.883203125000023, 26.630810546874997],\n                        [9.74755859375, 27.330859375],\n                        [9.916015625, 27.785693359374996],\n                        [9.805273437500006, 29.176953125],\n                        [9.310253906250011, 30.115234375],\n                        [9.51875, 30.229394531249994],\n                        [9.89501953125, 30.3873046875],\n                        [9.932519531250023, 30.425341796874996],\n                        [10.059765625000011, 30.580078125],\n                        [10.21640625, 30.783203125],\n                        [10.114941406250011, 31.463769531249994],\n                        [10.274609375000011, 31.684960937499994],\n                        [10.475781250000011, 31.736035156249997],\n                        [10.60888671875, 31.929541015625],\n                        [10.826367187500011, 32.0806640625],\n                        [11.005175781250017, 32.172705078125],\n                        [11.168261718750017, 32.256738281249994],\n                        [11.358007812500006, 32.34521484375],\n                        [11.504980468750006, 32.413671875],\n                        [11.535937500000017, 32.47333984375],\n                        [11.533789062500006, 32.524951171874996],\n                        [11.453906250000017, 32.642578125],\n                        [11.453906250000017, 32.781689453125],\n                        [11.459179687500011, 32.897363281249994],\n                        [11.467187500000023, 32.965722656249994],\n                        [11.504589843750011, 33.181933593749996],\n                        [11.657128906250023, 33.118896484375],\n                        [11.8134765625, 33.093701171875],\n                        [12.279882812500006, 32.858544921874994],\n                        [12.753515625, 32.801074218749996],\n                        [13.283496093750017, 32.9146484375],\n                        [15.176562500000017, 32.391162109374996],\n                        [15.705957031250023, 31.426416015624994],\n                        [17.830468750000023, 30.927587890625],\n                        [18.669824218750023, 30.415673828124994],\n                        [19.12373046875001, 30.26611328125],\n                        [19.713281250000023, 30.48837890625],\n                        [20.11152343750001, 30.963720703125],\n                        [19.926367187500006, 31.817529296874994],\n                        [20.121484375000023, 32.21875],\n                        [20.62109375, 32.58017578125],\n                        [21.63593750000001, 32.937304687499996],\n                        [22.187402343750023, 32.918261718749996],\n                        [23.090625, 32.61875],\n                        [23.10625, 32.331445312499994],\n                        [23.28632812500001, 32.213818359375],\n                        [24.129687500000017, 32.009228515625],\n                        [24.878515625, 31.984277343749994],\n                        [25.150488281250006, 31.654980468749997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Libya\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-60.89521484375, 13.821972656249997],\n                        [-60.951416015625, 13.717578125],\n                        [-61.073144531249994, 13.865576171874991],\n                        [-60.908105468749994, 14.09335937499999],\n                        [-60.89521484375, 13.821972656249997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Saint Lucia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [79.87480468750002, 9.050732421875026],\n                            [79.90371093750005, 8.975],\n                            [79.74765625000006, 9.104589843749991],\n                            [79.87480468750002, 9.050732421875026]\n                        ]\n                    ],\n                    [\n                        [\n                            [79.98232421875, 9.812695312500011],\n                            [80.25283203125005, 9.796337890625054],\n                            [80.71113281250004, 9.366357421875023],\n                            [81.226953125, 8.50551757812498],\n                            [81.37285156250002, 8.431445312499989],\n                            [81.42216796875007, 8.147851562500023],\n                            [81.87412109375012, 7.288330078124986],\n                            [81.86142578125012, 6.901269531249994],\n                            [81.63740234375004, 6.425146484374991],\n                            [80.72412109375003, 5.97905273437496],\n                            [80.26738281250007, 6.009765625],\n                            [80.09531250000012, 6.153173828125006],\n                            [79.859375, 6.829296874999983],\n                            [79.71298828125012, 8.18232421875004],\n                            [79.74980468750007, 8.294238281250003],\n                            [79.78349609375007, 8.018457031250051],\n                            [79.92890625000004, 8.899218749999974],\n                            [80.09960937499997, 9.209960937500043],\n                            [80.08632812500005, 9.577832031250026],\n                            [80.42832031250006, 9.480957031250014],\n                            [80.04580078125005, 9.649902343749972],\n                            [79.98232421875, 9.812695312500011]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Sri Lanka\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [28.646875, -30.1265625],\n                        [28.39208984375, -30.147558593750006],\n                        [28.128710937500017, -30.52509765625001],\n                        [28.05683593750001, -30.63105468750001],\n                        [27.753125, -30.6],\n                        [27.364062500000017, -30.27919921875001],\n                        [27.19355468750001, -29.94130859375001],\n                        [27.056933593750017, -29.625585937500006],\n                        [27.29453125, -29.519335937500003],\n                        [27.73554687500001, -28.940039062500006],\n                        [27.959863281250023, -28.873339843750003],\n                        [28.084375, -28.77998046875001],\n                        [28.23261718750001, -28.701269531250006],\n                        [28.471875, -28.615820312500006],\n                        [28.583398437500023, -28.594140625],\n                        [28.625781250000017, -28.58173828125001],\n                        [29.301367187500006, -29.08984375],\n                        [29.38671875, -29.31972656250001],\n                        [29.34882812500001, -29.441992187500006],\n                        [29.293554687500006, -29.56689453125],\n                        [29.1421875, -29.700976562500003],\n                        [29.098046875000023, -29.919042968750006],\n                        [28.646875, -30.1265625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Lesotho\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [20.957812500000074, 55.27890625000006],\n                            [20.89980468750008, 55.286669921875045],\n                            [21.11484375, 55.61650390624999],\n                            [20.957812500000074, 55.27890625000006]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.573046875000017, 54.139892578125],\n                            [25.497363281250045, 54.17524414062501],\n                            [25.52734375000003, 54.21513671874996],\n                            [25.505664062500045, 54.26494140624999],\n                            [25.46113281250004, 54.29277343749996],\n                            [25.179492187500017, 54.214257812499966],\n                            [25.111425781250006, 54.15493164062505],\n                            [25.04609375000004, 54.13305664062503],\n                            [24.869531250000023, 54.14516601562502],\n                            [24.82568359374997, 54.118994140625006],\n                            [24.78925781250001, 53.99824218750001],\n                            [24.768164062499977, 53.97465820312499],\n                            [24.31796875, 53.892968749999966],\n                            [24.236621093750045, 53.91997070312496],\n                            [24.19130859375005, 53.95043945312503],\n                            [23.559082031250057, 53.91982421875002],\n                            [23.484667968750074, 53.939794921875006],\n                            [23.453613281250057, 54.14345703125002],\n                            [23.3701171875, 54.20048828124999],\n                            [23.282324218750063, 54.240332031250034],\n                            [23.17031250000008, 54.28144531249998],\n                            [23.0875, 54.299462890624994],\n                            [23.042187500000068, 54.30419921875],\n                            [23.01552734375005, 54.34833984375001],\n                            [22.976757812500068, 54.36635742187505],\n                            [22.89394531250008, 54.390527343749994],\n                            [22.82373046874997, 54.39580078124999],\n                            [22.766210937499977, 54.356787109375034],\n                            [22.679882812500068, 54.493017578125006],\n                            [22.684472656250023, 54.56293945312504],\n                            [22.82470703125, 54.87128906249998],\n                            [22.56728515625005, 55.05913085937496],\n                            [22.072363281250034, 55.06367187499998],\n                            [21.235742187500023, 55.26411132812498],\n                            [21.237890625000034, 55.455029296874955],\n                            [21.06191406250005, 55.81342773437498],\n                            [21.053808593750006, 56.02294921875003],\n                            [21.04609375000004, 56.07006835937503],\n                            [21.31464843750004, 56.18813476562502],\n                            [21.65351562500004, 56.314550781250006],\n                            [22.084570312500034, 56.40673828125006],\n                            [22.875585937500063, 56.39643554687501],\n                            [22.96826171875003, 56.38041992187502],\n                            [23.042968750000057, 56.324072265625006],\n                            [23.119824218749983, 56.330664062500006],\n                            [23.195898437500034, 56.36713867187498],\n                            [24.120703125000063, 56.26425781249998],\n                            [24.90302734375001, 56.398193359375],\n                            [25.069921875, 56.20039062500004],\n                            [25.663183593750063, 56.104833984375006],\n                            [26.593554687500074, 55.66752929687502],\n                            [26.590820312500057, 55.62265625],\n                            [26.56660156250001, 55.546484375000034],\n                            [26.51923828125004, 55.448144531249994],\n                            [26.469531250000045, 55.371923828125006],\n                            [26.457617187500006, 55.342480468749955],\n                            [26.49531250000004, 55.31801757812502],\n                            [26.68125, 55.30644531249999],\n                            [26.76015625000008, 55.29335937499999],\n                            [26.775683593750045, 55.27309570312502],\n                            [26.601171875000034, 55.130175781250045],\n                            [26.291796875000074, 55.13959960937501],\n                            [26.250781250000045, 55.12451171875006],\n                            [26.175195312500023, 55.003271484375034],\n                            [26.092968750000068, 54.96230468750005],\n                            [25.964453124999977, 54.947167968749966],\n                            [25.85927734375005, 54.91928710937498],\n                            [25.722460937500074, 54.71787109374998],\n                            [25.731640625000068, 54.59038085937502],\n                            [25.72480468750001, 54.564257812500045],\n                            [25.68515625, 54.53579101562502],\n                            [25.62031250000004, 54.46040039062501],\n                            [25.56757812500004, 54.377050781250006],\n                            [25.54736328125, 54.33183593750002],\n                            [25.55751953125005, 54.310693359374994],\n                            [25.702539062499994, 54.29296875],\n                            [25.765234374999977, 54.179785156250034],\n                            [25.573046875000017, 54.139892578125]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Lithuania\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [6.4873046875, 49.798486328124994],\n                        [6.344335937500006, 49.452734375],\n                        [6.181054687500023, 49.498925781249994],\n                        [6.119921875000017, 49.485205078125],\n                        [6.074121093750023, 49.454638671874996],\n                        [6.011425781250011, 49.445458984374994],\n                        [5.95947265625, 49.454638671874996],\n                        [5.928906250000011, 49.4775390625],\n                        [5.9013671875, 49.48974609375],\n                        [5.823437500000011, 49.505078125],\n                        [5.789746093750011, 49.53828125],\n                        [5.776710379464286, 49.639953962053575],\n                        [5.744042968750023, 49.91962890625],\n                        [5.7880859375, 49.961230468749996],\n                        [5.8173828125, 50.0126953125],\n                        [5.866894531250011, 50.0828125],\n                        [5.976269531250011, 50.1671875],\n                        [6.089062500000011, 50.154589843749996],\n                        [6.110058593750011, 50.123779296875],\n                        [6.116503906250017, 50.120996093749994],\n                        [6.109765625000023, 50.034375],\n                        [6.13818359375, 49.97431640625],\n                        [6.204882812500017, 49.91513671875],\n                        [6.272327008928583, 49.887234933035714],\n                        [6.4873046875, 49.798486328124994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Luxembourg\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [28.14794921875, 56.142919921875],\n                        [27.576757812500006, 55.798779296875],\n                        [27.052539062500017, 55.83056640625],\n                        [26.593554687500017, 55.667529296874996],\n                        [25.663183593750006, 56.104833984375],\n                        [25.069921875, 56.200390625],\n                        [24.90302734375001, 56.398193359375],\n                        [24.120703125, 56.2642578125],\n                        [23.81269531250001, 56.329248046875],\n                        [23.195898437500006, 56.367138671875],\n                        [23.11982421875001, 56.3306640625],\n                        [23.04296875, 56.324072265625],\n                        [22.875585937500006, 56.396435546875],\n                        [22.084570312500006, 56.40673828125],\n                        [21.730566406250006, 56.325976562499996],\n                        [21.65351562500001, 56.31455078125],\n                        [21.31464843750001, 56.188134765625],\n                        [21.04609375000001, 56.070068359375],\n                        [21.0712890625, 56.82373046875],\n                        [21.72871093750001, 57.57099609375],\n                        [22.554589843750023, 57.724267578125],\n                        [23.28730468750001, 57.08974609375],\n                        [23.647753906250017, 56.971044921875],\n                        [24.382617187500017, 57.250048828124996],\n                        [24.322558593750017, 57.87060546875],\n                        [24.3625, 57.866162109375],\n                        [24.458886718750023, 57.907861328125],\n                        [25.11103515625001, 58.063427734375],\n                        [25.27265625000001, 58.009375],\n                        [25.66015625, 57.920166015625],\n                        [26.29804687500001, 57.60107421875],\n                        [26.532617187500023, 57.531005859375],\n                        [26.96601562500001, 57.609130859375],\n                        [27.187109375, 57.538330078125],\n                        [27.326562500000023, 57.52548828125],\n                        [27.4697265625, 57.5240234375],\n                        [27.538671875, 57.42978515625],\n                        [27.796875, 57.316943359374996],\n                        [27.82861328125, 57.293310546875],\n                        [27.838281250000023, 57.247705078125],\n                        [27.83027343750001, 57.194482421875],\n                        [27.639453125000017, 56.845654296875],\n                        [27.806054687500023, 56.86708984375],\n                        [27.8486328125, 56.85341796875],\n                        [27.89208984375, 56.741064453125],\n                        [28.00751953125001, 56.599853515625],\n                        [28.103125, 56.545703125],\n                        [28.11083984375, 56.510693359375],\n                        [28.169238281250017, 56.386865234375],\n                        [28.191699218750017, 56.315576171875],\n                        [28.202050781250023, 56.260400390625],\n                        [28.14794921875, 56.142919921875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Latvia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [28.2125, 45.450439453125],\n                        [28.07470703125, 45.598974609375],\n                        [28.23945312500001, 46.6408203125],\n                        [28.07177734375, 46.978417968749994],\n                        [27.614062500000017, 47.34052734375],\n                        [26.980761718750017, 48.155029296875],\n                        [26.618945312500017, 48.25986328125],\n                        [26.640429687500017, 48.294140625],\n                        [26.847070312500023, 48.387158203125],\n                        [26.90058593750001, 48.371923828125],\n                        [27.228515625, 48.371435546875],\n                        [27.549218750000023, 48.477734375],\n                        [28.34052734375001, 48.144433593749994],\n                        [28.42304687500001, 48.146875],\n                        [29.125390625000023, 47.96455078125],\n                        [29.134863281250006, 47.489697265625],\n                        [29.455664062500006, 47.292626953124994],\n                        [29.57197265625001, 46.964013671874994],\n                        [29.7197265625, 46.88291015625],\n                        [29.877832031250023, 46.82890625],\n                        [29.942480468750006, 46.723779296874994],\n                        [29.93476562500001, 46.625],\n                        [29.92431640625, 46.538867187499996],\n                        [30.13105468750001, 46.423095703125],\n                        [30.07568359375, 46.377832031249994],\n                        [29.878027343750006, 46.360205078125],\n                        [29.837890625, 46.350537109375],\n                        [29.458789062500017, 46.453759765624994],\n                        [29.30488281250001, 46.466601562499996],\n                        [29.22382812500001, 46.376953125],\n                        [29.20458984375, 46.379345703125],\n                        [29.20078125, 46.50498046875],\n                        [29.18623046875001, 46.523974609374996],\n                        [29.146289062500017, 46.526904296874996],\n                        [28.958398437500023, 46.45849609375],\n                        [28.92744140625001, 46.424121093749996],\n                        [28.930566406250023, 46.362255859375],\n                        [28.94375, 46.288427734375],\n                        [29.00625, 46.17646484375],\n                        [28.971875, 46.12763671875],\n                        [28.94775390625, 46.049951171874994],\n                        [28.849511718750023, 45.978662109374994],\n                        [28.73876953125, 45.937158203124994],\n                        [28.729296875000017, 45.852001953125],\n                        [28.667578125, 45.793847656249994],\n                        [28.562304687500017, 45.735791015625],\n                        [28.491601562500023, 45.665771484375],\n                        [28.4990234375, 45.517724609374994],\n                        [28.310351562500017, 45.498583984374996],\n                        [28.26484375000001, 45.48388671875],\n                        [28.2125, 45.450439453125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Moldova\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [49.936425781249994, -16.90292968750002],\n                            [49.82402343750002, -17.08652343750002],\n                            [50.02304687500006, -16.6953125],\n                            [49.936425781249994, -16.90292968750002]\n                        ]\n                    ],\n                    [\n                        [\n                            [48.3421875, -13.363867187500034],\n                            [48.21191406250003, -13.385253906249957],\n                            [48.191210937500074, -13.259960937500011],\n                            [48.308886718750074, -13.198242187499957],\n                            [48.3421875, -13.363867187500034]\n                        ]\n                    ],\n                    [\n                        [\n                            [49.53828125000004, -12.432128906250014],\n                            [49.9375, -13.072265624999957],\n                            [50.23535156249997, -14.732031249999963],\n                            [50.482714843750074, -15.385644531249994],\n                            [50.20898437499997, -15.960449218750028],\n                            [50.02041015625005, -15.801757812500028],\n                            [49.89257812500003, -15.457714843750011],\n                            [49.664355468750074, -15.521582031249977],\n                            [49.83906250000004, -16.486523437499997],\n                            [49.76718750000006, -16.815136718749983],\n                            [49.44931640625006, -17.240625],\n                            [49.477832031250074, -17.89853515624999],\n                            [49.362890625, -18.336328125],\n                            [47.934472656249994, -22.393945312500023],\n                            [47.55800781250005, -23.874609374999963],\n                            [47.17734375, -24.787207031249977],\n                            [46.72851562499997, -25.14990234374997],\n                            [46.15869140624997, -25.230371093750023],\n                            [45.5080078125001, -25.56318359374997],\n                            [45.2057617187501, -25.57050781250004],\n                            [44.0353515625001, -24.995703125],\n                            [43.670019531250006, -24.30029296875],\n                            [43.722265625, -23.529687500000037],\n                            [43.2648437500001, -22.38359375],\n                            [43.29052734374997, -21.93251953124998],\n                            [43.50185546875005, -21.356445312499957],\n                            [43.800195312499994, -21.179199218749986],\n                            [44.40468750000005, -19.922070312500026],\n                            [44.44882812500006, -19.42871093749997],\n                            [44.23876953124997, -19.075195312499986],\n                            [44.23310546875004, -18.740625],\n                            [44.04003906249997, -18.288476562500023],\n                            [43.979394531249994, -17.3916015625],\n                            [44.42138671874997, -16.70263671874997],\n                            [44.476171875, -16.217285156249957],\n                            [44.90917968749997, -16.174511718750026],\n                            [45.2228515625001, -15.95048828124996],\n                            [45.3421875, -16.03671875000002],\n                            [45.598242187500006, -15.992578125],\n                            [45.70019531249997, -15.813769531249989],\n                            [46.157519531250074, -15.738281249999972],\n                            [46.3996093750001, -15.924609375000017],\n                            [46.331445312499994, -15.713671875000031],\n                            [46.47509765625003, -15.513476562500003],\n                            [46.942285156249994, -15.219042968749974],\n                            [47.09921875, -15.43417968750002],\n                            [47.092578125000074, -15.150097656249969],\n                            [47.35195312500005, -14.766113281249986],\n                            [47.46474609375005, -14.713281249999966],\n                            [47.47832031250002, -15.009375],\n                            [47.77402343750006, -14.63671875],\n                            [47.964160156250074, -14.672558593750026],\n                            [47.773339843749994, -14.369921875],\n                            [47.995507812499994, -13.960449218749986],\n                            [47.88359375000002, -13.807519531250009],\n                            [47.94101562500006, -13.662402343750017],\n                            [48.03984375000002, -13.596289062499963],\n                            [48.25527343750005, -13.719335937499977],\n                            [48.796484375, -13.267480468750023],\n                            [48.91943359375003, -12.839062499999969],\n                            [48.78632812500004, -12.470898437500011],\n                            [48.931738281250006, -12.4390625],\n                            [49.20703124999997, -12.079589843749957],\n                            [49.53828125000004, -12.432128906250014]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Madagascar\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-91.68369140624998, 18.677343750000034],\n                            [-91.81611328124995, 18.675878906250006],\n                            [-91.53671874999998, 18.760009765625],\n                            [-91.68369140624998, 18.677343750000034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-86.93964843750001, 20.303320312500006],\n                            [-86.97797851562498, 20.489794921875074],\n                            [-86.76328124999995, 20.579052734374955],\n                            [-86.93964843750001, 20.303320312500006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-106.50224609374999, 21.61083984375003],\n                            [-106.60703124999993, 21.561474609374983],\n                            [-106.63935546874995, 21.697851562499977],\n                            [-106.50224609374999, 21.61083984375003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-110.56738281249994, 25.003466796875017],\n                            [-110.5388671875, 24.89155273437504],\n                            [-110.69926757812499, 25.081445312499994],\n                            [-110.56738281249994, 25.003466796875017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-112.05727539062498, 24.545703125000017],\n                            [-112.29677734375002, 24.789648437500063],\n                            [-112.15942382812501, 25.28564453125003],\n                            [-112.19501953124998, 24.841064453125057],\n                            [-112.05727539062498, 24.545703125000017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-111.10029296874998, 26.020605468750006],\n                            [-111.224658203125, 25.83588867187504],\n                            [-111.18291015625002, 26.040625],\n                            [-111.10029296874998, 26.020605468750006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-115.17060546875001, 28.06938476562496],\n                            [-115.35292968750002, 28.103955078124983],\n                            [-115.23354492187495, 28.36835937500004],\n                            [-115.17060546875001, 28.06938476562496]\n                        ]\n                    ],\n                    [\n                        [\n                            [-112.20307617187503, 29.00532226562504],\n                            [-112.27841796875, 28.769335937500017],\n                            [-112.51406249999997, 28.847607421874955],\n                            [-112.42353515625, 29.203662109375017],\n                            [-112.28505859374994, 29.240429687499955],\n                            [-112.20307617187503, 29.00532226562504]\n                        ]\n                    ],\n                    [\n                        [\n                            [-113.15561523437502, 29.05224609375],\n                            [-113.49633789062497, 29.30761718749997],\n                            [-113.58720703125002, 29.57304687499996],\n                            [-113.20214843749999, 29.301855468750034],\n                            [-113.15561523437502, 29.05224609375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-97.14624023437494, 25.961474609375045],\n                            [-97.66767578124995, 24.389990234374977],\n                            [-97.84248046874995, 22.510302734375017],\n                            [-97.76328124999998, 22.105859374999966],\n                            [-97.31450195312496, 21.56420898437503],\n                            [-97.40917968749997, 21.272558593750034],\n                            [-97.38344726562497, 21.56669921874999],\n                            [-97.75380859375002, 22.02666015624999],\n                            [-97.18632812499996, 20.717041015625],\n                            [-96.45605468749994, 19.869775390624966],\n                            [-96.28955078124994, 19.34375],\n                            [-95.778125, 18.805517578125034],\n                            [-95.92036132812495, 18.81958007812497],\n                            [-95.62680664062503, 18.690576171874994],\n                            [-95.71982421874998, 18.768359375000017],\n                            [-95.18183593749995, 18.700732421875017],\n                            [-94.79814453124996, 18.51459960937501],\n                            [-94.45976562499993, 18.166650390624994],\n                            [-93.55234375, 18.430468750000017],\n                            [-92.88476562499997, 18.468652343749966],\n                            [-92.44101562499998, 18.67529296874997],\n                            [-91.97377929687502, 18.715869140625074],\n                            [-91.91357421875, 18.52851562500001],\n                            [-91.53398437499993, 18.45654296875],\n                            [-91.27524414062498, 18.62446289062501],\n                            [-91.34306640624996, 18.900585937499955],\n                            [-91.43666992187502, 18.889794921874966],\n                            [-90.73925781249994, 19.352246093749955],\n                            [-90.69316406249996, 19.729882812499966],\n                            [-90.49169921874997, 19.94677734375003],\n                            [-90.353125, 21.009423828124966],\n                            [-89.81977539062495, 21.274609374999983],\n                            [-88.46669921874997, 21.569384765625017],\n                            [-88.0068359375, 21.604052734375045],\n                            [-87.25087890625, 21.44697265625004],\n                            [-87.18828124999993, 21.546435546875045],\n                            [-87.36850585937498, 21.57373046875],\n                            [-87.034765625, 21.592236328124955],\n                            [-86.824072265625, 21.421679687500017],\n                            [-86.77177734374999, 21.150537109375023],\n                            [-86.92622070312493, 20.786474609375034],\n                            [-87.42138671875, 20.23139648437501],\n                            [-87.44174804687498, 19.861523437499983],\n                            [-87.68769531249998, 19.63710937499999],\n                            [-87.6453125, 19.55390625000001],\n                            [-87.42475585937498, 19.583349609375063],\n                            [-87.65869140625003, 19.352343750000074],\n                            [-87.65576171874997, 19.25786132812499],\n                            [-87.50107421874998, 19.287792968749983],\n                            [-87.76181640624998, 18.446142578125006],\n                            [-87.88198242187497, 18.27387695312501],\n                            [-88.05644531249996, 18.524462890625074],\n                            [-88.03173828125, 18.838916015625017],\n                            [-88.29565429687494, 18.47241210937503],\n                            [-88.52299804687499, 18.445898437500063],\n                            [-88.80634765624998, 17.965527343749983],\n                            [-89.13354492187503, 17.970800781249977],\n                            [-89.16147460937503, 17.81484375],\n                            [-90.98916015624997, 17.81640624999997],\n                            [-90.99296874999993, 17.25244140625],\n                            [-91.19550781249998, 17.254101562499983],\n                            [-91.40961914062501, 17.255859375],\n                            [-90.975830078125, 16.867822265624994],\n                            [-90.710693359375, 16.708105468750034],\n                            [-90.65996093749996, 16.630908203125045],\n                            [-90.634375, 16.565136718749955],\n                            [-90.63408203125002, 16.51074218749997],\n                            [-90.57578124999995, 16.467822265625017],\n                            [-90.47109374999994, 16.439550781250034],\n                            [-90.41699218750003, 16.391015625000023],\n                            [-90.41699218750003, 16.351318359375],\n                            [-90.45014648437493, 16.261376953124994],\n                            [-90.45986328124997, 16.16235351562497],\n                            [-90.44716796874994, 16.07270507812501],\n                            [-90.52197265625, 16.07119140625005],\n                            [-90.70322265624998, 16.07104492187503],\n                            [-90.97958984374998, 16.07080078124997],\n                            [-91.433984375, 16.070458984374994],\n                            [-91.736572265625, 16.070166015625006],\n                            [-91.95722656250001, 15.703222656250034],\n                            [-92.08212890624998, 15.495556640625011],\n                            [-92.18715820312497, 15.320898437499963],\n                            [-92.07480468749998, 15.074218749999972],\n                            [-92.09873046874998, 15.026757812499994],\n                            [-92.14423828125001, 15.001953125],\n                            [-92.158544921875, 14.963574218749997],\n                            [-92.23515625, 14.545410156249986],\n                            [-93.91606445312493, 16.053564453125006],\n                            [-94.374169921875, 16.284765625000034],\n                            [-94.426416015625, 16.22626953125001],\n                            [-94.00126953124996, 16.018945312499966],\n                            [-94.66152343750002, 16.20190429687503],\n                            [-94.58710937499995, 16.315820312499966],\n                            [-94.79082031249999, 16.28715820312499],\n                            [-94.85869140624996, 16.41972656249999],\n                            [-95.02084960937503, 16.277636718750017],\n                            [-94.79941406249995, 16.20966796875001],\n                            [-95.134375, 16.17695312500001],\n                            [-96.21357421874993, 15.693066406250011],\n                            [-96.80795898437495, 15.726416015624977],\n                            [-97.18466796874998, 15.909277343750006],\n                            [-97.75478515624994, 15.966845703125017],\n                            [-98.52031249999993, 16.30483398437505],\n                            [-98.76220703125, 16.534765624999977],\n                            [-99.69067382812499, 16.719628906249994],\n                            [-100.847802734375, 17.20048828124999],\n                            [-101.91870117187494, 17.959765625000045],\n                            [-102.69956054687495, 18.062841796875006],\n                            [-103.44160156249995, 18.32539062500001],\n                            [-103.91245117187496, 18.828466796875006],\n                            [-104.9384765625, 19.309375],\n                            [-105.482080078125, 19.97607421875003],\n                            [-105.66943359374997, 20.385595703124977],\n                            [-105.26015625, 20.579052734374955],\n                            [-105.32705078124994, 20.752978515625045],\n                            [-105.51083984374999, 20.808740234375023],\n                            [-105.23706054687499, 21.119189453125045],\n                            [-105.20869140624998, 21.490820312499977],\n                            [-105.43144531249997, 21.618261718750006],\n                            [-105.64912109375001, 21.988085937500045],\n                            [-105.64550781249999, 22.32690429687497],\n                            [-105.79179687500003, 22.627490234375017],\n                            [-106.93549804687497, 23.88125],\n                            [-107.76494140625002, 24.47192382812497],\n                            [-107.52724609375001, 24.36005859375001],\n                            [-107.51191406249998, 24.489160156250023],\n                            [-107.95117187499994, 24.614892578124966],\n                            [-108.28076171874994, 25.08154296875],\n                            [-108.05146484374995, 25.067041015624994],\n                            [-108.69638671874998, 25.382910156250034],\n                            [-108.78725585937502, 25.53803710937501],\n                            [-109.02880859375003, 25.48046875000003],\n                            [-108.886572265625, 25.733447265625045],\n                            [-109.19648437499998, 25.59252929687503],\n                            [-109.38496093750001, 25.727148437500006],\n                            [-109.42563476562495, 26.032568359375063],\n                            [-109.19970703125003, 26.30522460937499],\n                            [-109.11669921874999, 26.25273437499996],\n                            [-109.27626953125, 26.533886718749955],\n                            [-109.48286132812498, 26.710351562500023],\n                            [-109.75478515624995, 26.702929687500017],\n                            [-109.94399414062495, 27.079345703125057],\n                            [-110.37729492187495, 27.233300781249966],\n                            [-110.59267578124995, 27.544335937500023],\n                            [-110.52988281249995, 27.864208984374983],\n                            [-111.12138671875002, 27.966992187499983],\n                            [-112.16176757812495, 29.018896484375034],\n                            [-113.05766601562496, 30.651025390625023],\n                            [-113.04672851562495, 31.17924804687499],\n                            [-113.62348632812494, 31.34589843750001],\n                            [-113.75942382812501, 31.557763671874994],\n                            [-113.94775390625001, 31.62934570312501],\n                            [-114.14931640624995, 31.507373046875045],\n                            [-114.93359374999994, 31.900732421874977],\n                            [-114.78989257812498, 31.647119140624994],\n                            [-114.88188476562499, 31.156396484375023],\n                            [-114.55048828124997, 30.02226562499999],\n                            [-113.75546875, 29.367480468750017],\n                            [-113.49970703124995, 28.92670898437501],\n                            [-113.20556640624997, 28.798779296874955],\n                            [-113.09365234375001, 28.511767578125017],\n                            [-112.870849609375, 28.42421875000005],\n                            [-112.73403320312501, 27.825976562500017],\n                            [-112.32919921874996, 27.52343750000003],\n                            [-111.86264648437495, 26.678515625000017],\n                            [-111.6994140625, 26.58095703125005],\n                            [-111.79526367187499, 26.8796875],\n                            [-111.56967773437495, 26.707617187500006],\n                            [-111.29160156249996, 25.78979492187497],\n                            [-110.68676757812501, 24.867675781250057],\n                            [-110.65932617187502, 24.34145507812505],\n                            [-110.36743164062497, 24.100488281249994],\n                            [-110.30375976562497, 24.339453125],\n                            [-110.02280273437502, 24.17460937499999],\n                            [-109.6765625, 23.66157226562501],\n                            [-109.42084960937495, 23.480126953124994],\n                            [-109.49570312500002, 23.159814453125023],\n                            [-110.00625, 22.894042968750057],\n                            [-110.3626953125, 23.60493164062501],\n                            [-111.68291015625002, 24.555810546875023],\n                            [-111.80249023437494, 24.542529296875074],\n                            [-112.07255859374999, 24.84003906250001],\n                            [-112.06987304687497, 25.572851562500006],\n                            [-112.37724609374997, 26.21391601562496],\n                            [-113.02075195312499, 26.58325195312497],\n                            [-113.15581054687496, 26.94624023437504],\n                            [-113.27226562499997, 26.79096679687501],\n                            [-113.59853515625001, 26.721289062500034],\n                            [-113.84096679687502, 26.966503906249983],\n                            [-114.44526367187503, 27.218164062499994],\n                            [-114.53989257812495, 27.431103515624955],\n                            [-114.99350585937499, 27.736035156249983],\n                            [-115.03647460937495, 27.84184570312496],\n                            [-114.57001953124995, 27.78393554687497],\n                            [-114.30058593749995, 27.87299804687501],\n                            [-114.30224609375003, 27.775732421875006],\n                            [-114.0693359375, 27.67568359375005],\n                            [-114.15839843750003, 27.919677734375],\n                            [-114.26586914062499, 27.934472656249994],\n                            [-114.04848632812502, 28.42617187499999],\n                            [-114.93730468749999, 29.35161132812496],\n                            [-115.67382812500003, 29.756396484375017],\n                            [-116.06215820312501, 30.80415039062504],\n                            [-116.29628906250001, 30.97050781249999],\n                            [-116.33344726562494, 31.202783203124994],\n                            [-116.66215820312495, 31.56489257812504],\n                            [-116.72207031249998, 31.734570312499955],\n                            [-116.62080078124995, 31.85107421874997],\n                            [-116.84799804687496, 31.997363281250045],\n                            [-117.12827148437495, 32.533349609374994],\n                            [-114.72475585937495, 32.71533203125003],\n                            [-114.83593749999994, 32.50830078125003],\n                            [-111.0419921875, 31.32421875000003],\n                            [-108.21445312499993, 31.329443359375034],\n                            [-108.21181640625002, 31.779345703125017],\n                            [-106.44541015624996, 31.768408203125006],\n                            [-106.14804687499995, 31.450927734375],\n                            [-104.97880859374996, 30.645947265624955],\n                            [-104.50400390624995, 29.677685546874955],\n                            [-104.110595703125, 29.386132812499994],\n                            [-103.16831054687498, 28.998193359374994],\n                            [-102.8919921875, 29.216406250000034],\n                            [-102.61494140624994, 29.75234375],\n                            [-102.26894531249998, 29.871191406250034],\n                            [-101.44038085937503, 29.77685546875],\n                            [-100.75458984375001, 29.182519531249994],\n                            [-100.29604492187495, 28.32768554687499],\n                            [-99.50532226562497, 27.54833984375003],\n                            [-99.45654296874999, 27.05668945312496],\n                            [-99.10776367187498, 26.446923828124994],\n                            [-97.37563476562497, 25.871826171875],\n                            [-97.14624023437494, 25.961474609375045]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Mexico\", \"childNum\": 10 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [22.344042968750017, 42.31396484375],\n                        [22.836816406250023, 41.993603515625],\n                        [23.00361328125001, 41.73984375],\n                        [22.916015625, 41.336279296875],\n                        [22.78388671875001, 41.331982421875],\n                        [22.72480468750001, 41.178515625],\n                        [22.603613281250006, 41.140185546874996],\n                        [22.493554687500023, 41.118505859375],\n                        [22.184472656250023, 41.158642578125],\n                        [21.99335937500001, 41.13095703125],\n                        [21.77949218750001, 40.950439453125],\n                        [21.627539062500006, 40.896337890625],\n                        [21.57578125, 40.868945312499996],\n                        [20.964257812500023, 40.849902343749996],\n                        [20.709277343750017, 40.928369140624994],\n                        [20.48896484375001, 41.272607421874994],\n                        [20.566210937500017, 41.873681640624994],\n                        [20.725, 41.87353515625],\n                        [20.778125, 42.071044921875],\n                        [21.05976562500001, 42.171289062499994],\n                        [21.28662109375, 42.100390625],\n                        [21.389550781250023, 42.21982421875],\n                        [21.560839843750017, 42.24765625],\n                        [21.5625, 42.247509765625],\n                        [21.81464843750001, 42.303125],\n                        [22.344042968750017, 42.31396484375]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Macedonia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [4.227636718750006, 19.142773437499997],\n                        [4.234667968750017, 16.996386718750003],\n                        [4.121289062500011, 16.357714843750003],\n                        [3.842968750000011, 15.701708984375003],\n                        [3.5205078125, 15.483105468749997],\n                        [3.504296875000023, 15.356347656249994],\n                        [3.06015625, 15.427197265624997],\n                        [3.001074218750006, 15.340966796874994],\n                        [1.300195312500023, 15.272265625],\n                        [0.947460937500011, 14.982128906249997],\n                        [0.217480468750011, 14.911474609374991],\n                        [-0.235888671874989, 15.059423828124991],\n                        [-0.760449218749983, 15.047753906249994],\n                        [-1.049560546875, 14.81953125],\n                        [-1.97304687499999, 14.45654296875],\n                        [-2.113232421874983, 14.16845703125],\n                        [-2.586718749999989, 14.227587890625003],\n                        [-2.873925781249994, 13.950732421875003],\n                        [-2.950830078124994, 13.6484375],\n                        [-3.248632812499977, 13.658349609374994],\n                        [-3.3017578125, 13.28076171875],\n                        [-3.527636718749989, 13.182714843749991],\n                        [-3.947314453124989, 13.402197265624991],\n                        [-4.151025390624994, 13.306201171875003],\n                        [-4.328710937499977, 13.119042968749994],\n                        [-4.227099609374989, 12.793701171875],\n                        [-4.480615234374994, 12.672216796874991],\n                        [-4.4287109375, 12.337597656249997],\n                        [-4.699316406249977, 12.076171875],\n                        [-5.288134765624989, 11.827929687499989],\n                        [-5.250244140625, 11.375781249999989],\n                        [-5.490478515625, 11.042382812499994],\n                        [-5.523535156249977, 10.426025390625],\n                        [-5.556591796874983, 10.43994140625],\n                        [-5.694287109374983, 10.43320312499999],\n                        [-5.843847656249977, 10.389550781249994],\n                        [-5.896191406249983, 10.354736328125],\n                        [-5.907568359374977, 10.307226562499991],\n                        [-6.034570312499994, 10.19482421875],\n                        [-6.1171875, 10.201904296875],\n                        [-6.238378906249977, 10.261621093749994],\n                        [-6.241308593749977, 10.279199218749994],\n                        [-6.192626953125, 10.369433593749989],\n                        [-6.190673828125, 10.400292968749994],\n                        [-6.250244140625, 10.717919921874994],\n                        [-6.482617187499983, 10.561230468749997],\n                        [-6.564599609374994, 10.58642578125],\n                        [-6.654150390624977, 10.656445312499997],\n                        [-6.676367187499977, 10.6337890625],\n                        [-6.686132812499977, 10.578027343749994],\n                        [-6.691992187499977, 10.512011718749989],\n                        [-6.669335937499994, 10.3921875],\n                        [-6.693261718749994, 10.349462890624991],\n                        [-6.950341796874994, 10.342333984374989],\n                        [-7.01708984375, 10.143261718749997],\n                        [-7.385058593749989, 10.340136718749989],\n                        [-7.6611328125, 10.427441406249997],\n                        [-7.990625, 10.1625],\n                        [-8.007275390624983, 10.321875],\n                        [-8.266650390624989, 10.485986328124994],\n                        [-8.33740234375, 10.990625],\n                        [-8.666699218749983, 11.009472656249997],\n                        [-8.398535156249977, 11.366552734374991],\n                        [-8.822021484375, 11.673242187499994],\n                        [-8.818310546874983, 11.922509765624994],\n                        [-9.043066406249977, 12.40234375],\n                        [-9.395361328124977, 12.464648437499989],\n                        [-9.358105468749983, 12.255419921874989],\n                        [-9.754003906249977, 12.029931640624994],\n                        [-10.274853515624983, 12.212646484375],\n                        [-10.709228515625, 11.898730468749989],\n                        [-10.933203124999977, 12.205175781249991],\n                        [-11.30517578125, 12.015429687499989],\n                        [-11.502197265625, 12.198632812499994],\n                        [-11.389404296875, 12.404394531249991],\n                        [-11.390380859375, 12.941992187499991],\n                        [-11.634960937499983, 13.369873046875],\n                        [-11.831689453124994, 13.315820312499994],\n                        [-12.05419921875, 13.633056640625],\n                        [-11.960888671874983, 13.875292968750003],\n                        [-12.019189453124994, 14.206494140624997],\n                        [-12.228417968749994, 14.45859375],\n                        [-12.280615234374977, 14.809033203124997],\n                        [-12.104687499999983, 14.745361328125],\n                        [-12.08154296875, 14.766357421875],\n                        [-12.021582031249977, 14.804931640625],\n                        [-11.76015625, 15.425537109375],\n                        [-11.675878906249977, 15.512060546874991],\n                        [-11.502685546875, 15.636816406249991],\n                        [-11.455224609374994, 15.62539062499999],\n                        [-10.9482421875, 15.151123046875],\n                        [-10.696582031249989, 15.42265625],\n                        [-9.94140625, 15.373779296875],\n                        [-9.446923828124994, 15.458203125],\n                        [-9.447705078124983, 15.574853515624994],\n                        [-9.426562499999989, 15.623046875],\n                        [-9.3505859375, 15.677392578124994],\n                        [-9.33544921875, 15.525683593750003],\n                        [-9.293701171875, 15.502832031249994],\n                        [-5.5125, 15.496289062499997],\n                        [-5.359912109374989, 16.282861328124994],\n                        [-5.509619140624977, 16.442041015624994],\n                        [-5.628662109375, 16.568652343750003],\n                        [-5.65625, 16.8095703125],\n                        [-5.684765624999983, 17.058251953124994],\n                        [-5.713183593749989, 17.306884765625],\n                        [-5.74169921875, 17.555566406249994],\n                        [-5.827099609374983, 18.3015625],\n                        [-6.026416015624989, 20.0421875],\n                        [-6.396582031249977, 23.274804687499994],\n                        [-6.482031249999977, 24.020800781250003],\n                        [-6.538964843749994, 24.51816406249999],\n                        [-6.5673828125, 24.766796875],\n                        [-6.594091796874977, 24.99462890625],\n                        [-6.287207031249977, 24.994824218749997],\n                        [-5.959814453124977, 24.99497070312499],\n                        [-5.640771484374994, 24.995166015625003],\n                        [-4.822607421874977, 24.99560546875],\n                        [-1.947900390624994, 23.124804687500003],\n                        [1.1455078125, 21.102246093749997],\n                        [1.165722656250011, 20.817431640625003],\n                        [1.610644531250017, 20.555566406249994],\n                        [1.685449218750023, 20.378369140624997],\n                        [3.130273437500023, 19.85019531249999],\n                        [3.255859375, 19.4109375],\n                        [3.119726562500006, 19.103173828124994],\n                        [3.3564453125, 18.986621093750003],\n                        [4.227636718750006, 19.142773437499997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Mali\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [14.566210937499989, 35.85273437499998],\n                        [14.436425781250023, 35.82167968750005],\n                        [14.351269531250011, 35.978417968749994],\n                        [14.566210937499989, 35.85273437499998]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Malta\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [98.18261718749997, 9.933447265625006],\n                            [98.11806640625, 9.877880859375054],\n                            [98.2916992187501, 10.051318359375031],\n                            [98.18261718749997, 9.933447265625006]\n                        ]\n                    ],\n                    [\n                        [\n                            [98.20976562500002, 10.952734375],\n                            [98.27148437499997, 10.73989257812498],\n                            [98.08046875000005, 10.886621093750037],\n                            [98.20976562500002, 10.952734375]\n                        ]\n                    ],\n                    [\n                        [\n                            [98.55380859375012, 11.744873046875],\n                            [98.52841796875012, 11.538671875],\n                            [98.43476562500004, 11.567089843750026],\n                            [98.37646484374997, 11.79150390625],\n                            [98.55380859375012, 11.744873046875]\n                        ]\n                    ],\n                    [\n                        [\n                            [98.516015625, 11.905029296875028],\n                            [98.46621093750005, 12.08427734374996],\n                            [98.60957031250004, 11.956640624999977],\n                            [98.516015625, 11.905029296875028]\n                        ]\n                    ],\n                    [\n                        [\n                            [98.06611328125004, 12.389794921875023],\n                            [98.00234375000005, 12.279003906250011],\n                            [97.93867187500004, 12.34609375],\n                            [98.06611328125004, 12.389794921875023]\n                        ]\n                    ],\n                    [\n                        [\n                            [98.41396484375005, 12.597949218749974],\n                            [98.45947265625003, 12.473730468749991],\n                            [98.3138671875, 12.335986328124989],\n                            [98.31210937500006, 12.678173828124983],\n                            [98.41396484375005, 12.597949218749974]\n                        ]\n                    ],\n                    [\n                        [\n                            [98.31542968749997, 13.099072265625026],\n                            [98.30917968750012, 12.934716796875023],\n                            [98.26533203125004, 13.202246093749991],\n                            [98.31542968749997, 13.099072265625026]\n                        ]\n                    ],\n                    [\n                        [\n                            [94.80488281250004, 15.8193359375],\n                            [94.73349609375006, 15.823046875000045],\n                            [94.82802734375005, 15.933007812499966],\n                            [94.80488281250004, 15.8193359375]\n                        ]\n                    ],\n                    [\n                        [\n                            [94.47675781250004, 15.945947265625023],\n                            [94.41191406250007, 15.848388671875057],\n                            [94.3878906250001, 15.994140624999972],\n                            [94.60126953125004, 16.205517578124983],\n                            [94.47675781250004, 15.945947265625023]\n                        ]\n                    ],\n                    [\n                        [\n                            [97.575, 16.253222656250017],\n                            [97.48037109375, 16.305712890625045],\n                            [97.54199218749997, 16.505078124999983],\n                            [97.575, 16.253222656250017]\n                        ]\n                    ],\n                    [\n                        [\n                            [93.6908203125, 18.68427734375004],\n                            [93.4875, 18.867529296875063],\n                            [93.74472656250006, 18.865527343750017],\n                            [93.6908203125, 18.68427734375004]\n                        ]\n                    ],\n                    [\n                        [\n                            [93.71484374999997, 19.558251953124994],\n                            [93.94570312500005, 19.428613281249966],\n                            [93.90195312500012, 19.33203125],\n                            [93.75585937500003, 19.325683593750057],\n                            [93.64404296874997, 19.49506835937501],\n                            [93.71484374999997, 19.558251953124994]\n                        ]\n                    ],\n                    [\n                        [\n                            [93.49179687500012, 19.892578125],\n                            [93.51328125000006, 19.754785156249994],\n                            [93.41289062500002, 19.950341796875023],\n                            [93.49179687500012, 19.892578125]\n                        ]\n                    ],\n                    [\n                        [\n                            [93.01015625000005, 19.923925781249977],\n                            [93.02324218750007, 19.82885742187497],\n                            [92.91464843750006, 20.086474609375045],\n                            [93.01015625000005, 19.923925781249977]\n                        ]\n                    ],\n                    [\n                        [\n                            [101.1388671875001, 21.567480468749977],\n                            [101.08037109375007, 21.468652343749994],\n                            [100.703125, 21.251367187499966],\n                            [100.613671875, 21.059326171875],\n                            [100.56660156250004, 21.038183593750063],\n                            [100.53613281250003, 20.992382812499955],\n                            [100.52226562500007, 20.92192382812499],\n                            [100.54931640624997, 20.884228515624955],\n                            [100.61767578125003, 20.879248046875006],\n                            [100.62294921875005, 20.859570312499983],\n                            [100.5651367187501, 20.825097656249994],\n                            [100.4074218750001, 20.823242187500057],\n                            [100.32607421875005, 20.795703125000045],\n                            [100.24931640625002, 20.730273437500045],\n                            [100.18388671875002, 20.589111328125057],\n                            [100.12968750000002, 20.372216796874994],\n                            [100.12246093750005, 20.316650390625057],\n                            [100.0036132812501, 20.37958984375001],\n                            [99.9542968750001, 20.415429687500023],\n                            [99.8903320312501, 20.424414062499977],\n                            [99.72011718750005, 20.32543945312497],\n                            [99.45888671875005, 20.363037109375],\n                            [99.48593750000006, 20.14985351562501],\n                            [99.07421875000003, 20.09936523437503],\n                            [98.9166992187501, 19.77290039062504],\n                            [98.37128906250004, 19.68916015625004],\n                            [98.01503906250005, 19.74951171874997],\n                            [97.816796875, 19.459960937500057],\n                            [97.74589843750002, 18.58818359374999],\n                            [97.37392578125, 18.51796875],\n                            [97.63222656250005, 18.290332031250074],\n                            [97.7064453125, 17.79711914062503],\n                            [98.4388671875, 16.975683593750034],\n                            [98.66074218750006, 16.330419921875006],\n                            [98.83544921875003, 16.417578125],\n                            [98.88828125000006, 16.351904296875034],\n                            [98.81796875000012, 16.180810546874994],\n                            [98.59238281250006, 16.05068359375005],\n                            [98.55693359375007, 15.367675781249986],\n                            [98.19101562500012, 15.204101562499972],\n                            [98.20214843749997, 14.97592773437502],\n                            [98.57001953125004, 14.359912109375031],\n                            [99.13681640625006, 13.716699218749994],\n                            [99.12392578125, 13.030761718750043],\n                            [99.40507812500002, 12.547900390625003],\n                            [99.61474609374997, 11.781201171875026],\n                            [99.1901367187501, 11.105273437499989],\n                            [98.7572265625, 10.660937499999974],\n                            [98.70253906250005, 10.19038085937504],\n                            [98.56259765625006, 10.034960937499989],\n                            [98.46494140625006, 10.675830078124989],\n                            [98.67558593750007, 10.986914062500034],\n                            [98.74140625000004, 11.591699218749966],\n                            [98.87597656250003, 11.719726562500028],\n                            [98.63632812500006, 11.738378906250006],\n                            [98.69628906250003, 12.225244140624994],\n                            [98.6002929687501, 12.2453125],\n                            [98.67871093749997, 12.348486328124963],\n                            [98.57597656250002, 13.161914062500031],\n                            [98.20039062500004, 13.980175781250026],\n                            [98.14951171875012, 13.647607421875037],\n                            [98.11064453125007, 13.712890625000014],\n                            [98.10019531250006, 14.161523437500023],\n                            [97.90976562500012, 14.652685546874991],\n                            [98.01875, 14.652587890625057],\n                            [97.81230468750007, 14.858935546874989],\n                            [97.7103515625, 15.875537109375074],\n                            [97.58427734375007, 16.019580078125017],\n                            [97.72597656250005, 16.56855468750004],\n                            [97.37587890625005, 16.52294921874997],\n                            [97.20019531249997, 17.095410156249983],\n                            [96.85146484375005, 17.401025390624994],\n                            [96.90859375000005, 17.03095703125001],\n                            [96.76542968750002, 16.710351562499966],\n                            [96.43115234374997, 16.504931640625045],\n                            [96.18906250000012, 16.768310546875057],\n                            [96.32431640625006, 16.444433593750063],\n                            [95.76328125000006, 16.169042968750006],\n                            [95.38955078125005, 15.722753906250034],\n                            [95.30146484375004, 15.756152343749989],\n                            [95.34677734375012, 16.09760742187501],\n                            [95.17695312500004, 15.825683593750028],\n                            [94.9425781250001, 15.818261718750023],\n                            [94.89316406250006, 16.182812499999955],\n                            [94.66152343750005, 15.904394531250006],\n                            [94.70332031250004, 16.511914062499955],\n                            [94.4416015625001, 16.094384765624966],\n                            [94.22382812500004, 16.016455078125006],\n                            [94.58896484375006, 17.5693359375],\n                            [94.17070312500007, 18.73242187499997],\n                            [94.24570312500006, 18.741162109374983],\n                            [94.07001953125004, 18.893408203125006],\n                            [94.04492187500003, 19.287402343750074],\n                            [93.92919921874997, 18.89965820312503],\n                            [93.70546875000005, 19.026904296875017],\n                            [93.49306640625005, 19.369482421875006],\n                            [93.82490234375004, 19.238476562499955],\n                            [93.99814453125006, 19.440869140624983],\n                            [93.61171875000005, 19.776074218749983],\n                            [93.70703125000003, 19.912158203125074],\n                            [93.25, 20.070117187500017],\n                            [93.12949218750012, 19.858007812500063],\n                            [93.00195312499997, 20.074853515624994],\n                            [93.06679687500005, 20.377636718749955],\n                            [92.82832031250004, 20.177587890625063],\n                            [92.89111328124997, 20.34033203125],\n                            [92.73564453125007, 20.56269531250001],\n                            [92.72285156250004, 20.29560546875004],\n                            [92.32412109375, 20.791845703125063],\n                            [92.17958984375005, 21.293115234375023],\n                            [92.33056640624997, 21.439794921874977],\n                            [92.63164062500002, 21.306201171875045],\n                            [92.5934570312501, 21.46733398437499],\n                            [92.58281250000002, 21.940332031249994],\n                            [92.57490234375004, 21.978076171875045],\n                            [92.68896484374997, 22.130957031250006],\n                            [92.72099609375002, 22.132421875000063],\n                            [92.77138671875, 22.104785156250017],\n                            [92.9645507812501, 22.003759765625034],\n                            [93.07060546875002, 22.20942382812501],\n                            [93.16201171875, 22.360205078125006],\n                            [93.07871093750006, 22.71821289062501],\n                            [93.20390625000002, 23.03701171875005],\n                            [93.34941406250007, 23.08496093750003],\n                            [93.36601562500007, 23.132519531249955],\n                            [93.32626953125006, 24.064208984375057],\n                            [93.45214843750003, 23.987402343750034],\n                            [93.68339843750007, 24.00654296875004],\n                            [94.07480468750006, 23.8720703125],\n                            [94.29306640625012, 24.321875],\n                            [94.37724609375002, 24.473730468750006],\n                            [94.49316406250003, 24.637646484374983],\n                            [94.70371093750012, 25.097851562499955],\n                            [94.55302734375007, 25.215722656249994],\n                            [94.66777343750007, 25.458886718749966],\n                            [94.99199218750002, 25.77045898437504],\n                            [95.01523437500006, 25.912939453125006],\n                            [95.0929687500001, 25.98730468749997],\n                            [95.13242187500006, 26.041259765625057],\n                            [95.12929687500005, 26.070410156250034],\n                            [95.10839843749997, 26.091406250000034],\n                            [95.06894531250006, 26.19111328125001],\n                            [95.0597656250001, 26.473974609375006],\n                            [95.20146484375007, 26.641406250000017],\n                            [96.19082031250005, 27.26127929687499],\n                            [96.79785156249997, 27.29619140624999],\n                            [96.95341796875002, 27.13330078125003],\n                            [97.10205078125003, 27.11542968750004],\n                            [97.10371093750004, 27.16333007812503],\n                            [96.90195312500012, 27.439599609374994],\n                            [96.88359375000002, 27.514843749999955],\n                            [96.96279296875, 27.698291015625017],\n                            [97.04970703125005, 27.760009765625],\n                            [97.34355468750002, 27.982324218749994],\n                            [97.30273437499997, 28.08598632812496],\n                            [97.3224609375001, 28.21796875000004],\n                            [97.35644531249997, 28.254492187500006],\n                            [97.43144531250002, 28.353906250000023],\n                            [97.53789062500002, 28.510205078124983],\n                            [97.59921875000006, 28.51704101562504],\n                            [98.06162109375012, 28.185888671874977],\n                            [98.29882812499997, 27.550097656250045],\n                            [98.4525390625, 27.6572265625],\n                            [98.65117187500007, 27.572460937499983],\n                            [98.7384765625001, 26.785742187500006],\n                            [98.68554687499997, 26.189355468750023],\n                            [98.56406250000006, 26.072412109374994],\n                            [98.65625, 25.86357421874999],\n                            [98.33378906250007, 25.586767578125006],\n                            [98.14287109375007, 25.571093750000017],\n                            [98.01074218749997, 25.292529296875017],\n                            [97.8195312500001, 25.251855468749994],\n                            [97.73789062500006, 24.869873046875057],\n                            [97.58330078125002, 24.77480468750005],\n                            [97.53144531250004, 24.49169921875003],\n                            [97.7082031250001, 24.228759765625],\n                            [97.56455078125012, 23.911035156250023],\n                            [98.2125, 24.110644531250017],\n                            [98.83505859375006, 24.121191406250034],\n                            [98.67675781250003, 23.905078125000045],\n                            [98.8322265625001, 23.624365234374977],\n                            [98.86376953125003, 23.191259765625034],\n                            [99.41806640625006, 23.069238281250023],\n                            [99.50712890625002, 22.959130859374994],\n                            [99.19296875000006, 22.12597656249997],\n                            [99.9176757812501, 22.02802734375001],\n                            [99.94072265625007, 21.75874023437504],\n                            [100.14765625000004, 21.480517578125017],\n                            [100.60458984375012, 21.471777343750006],\n                            [101.07978515625004, 21.75585937499997],\n                            [101.1388671875001, 21.567480468749977]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Myanmar\", \"childNum\": 15 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [19.21875, 43.449951171875],\n                        [19.670996093750006, 43.163964843749994],\n                        [20.344335937500006, 42.827929687499996],\n                        [20.054296875, 42.760058593749996],\n                        [20.06396484375, 42.54726562499999],\n                        [19.78828125000001, 42.476171875],\n                        [19.65449218750001, 42.628564453124994],\n                        [19.280664062500023, 42.17255859375],\n                        [19.342382812500006, 41.869091796875],\n                        [18.436328125000017, 42.559716796874994],\n                        [18.5458984375, 42.6416015625],\n                        [18.46601562500001, 42.777246093749994],\n                        [18.44384765625, 42.96845703125],\n                        [18.46015625000001, 42.997900390625],\n                        [18.48847656250001, 43.012158203125],\n                        [18.623632812500006, 43.027685546875],\n                        [18.621875, 43.124609375],\n                        [18.674218750000023, 43.230810546875],\n                        [18.74921875000001, 43.283544921875],\n                        [18.85107421875, 43.346337890624994],\n                        [18.934667968750006, 43.339453125],\n                        [18.97871093750001, 43.285400390625],\n                        [19.026660156250017, 43.292431640625],\n                        [19.03671875, 43.357324218749994],\n                        [18.940234375000017, 43.496728515624994],\n                        [18.95068359375, 43.526660156249996],\n                        [18.97421875, 43.542333984375],\n                        [19.0283203125, 43.532519531249996],\n                        [19.080078125, 43.517724609374994],\n                        [19.11279296875, 43.52773437499999],\n                        [19.164355468750017, 43.535449218749996],\n                        [19.1943359375, 43.53330078125],\n                        [19.21875, 43.449951171875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Montenegro\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [111.878125, 43.68017578125],\n                        [111.00722656250002, 43.34140625],\n                        [110.400390625, 42.773681640625],\n                        [109.44316406249999, 42.455957031249994],\n                        [109.33984375, 42.438378906249994],\n                        [108.68730468749999, 42.41611328125],\n                        [108.17119140624999, 42.447314453124996],\n                        [106.77001953125, 42.288720703124994],\n                        [105.86757812500002, 41.993994140625],\n                        [105.31435546875002, 41.770898437499994],\n                        [105.19707031249999, 41.738037109375],\n                        [105.11542968750001, 41.66328125],\n                        [105.05058593749999, 41.61591796875],\n                        [104.98203125000003, 41.595507812499996],\n                        [104.49824218750001, 41.65869140625],\n                        [104.49824218750001, 41.877001953124996],\n                        [104.30517578125, 41.846142578125],\n                        [103.99726562500001, 41.79697265625],\n                        [103.71113281250001, 41.751318359375],\n                        [103.07285156250003, 42.00595703125],\n                        [102.5751953125, 42.092089843749996],\n                        [102.15664062500002, 42.158105468749994],\n                        [101.97294921874999, 42.215869140624996],\n                        [101.65996093749999, 42.500048828124996],\n                        [101.5791015625, 42.52353515625],\n                        [101.49531250000001, 42.53876953125],\n                        [101.09199218750001, 42.551318359374996],\n                        [100.51904296875, 42.616796875],\n                        [100.08632812500002, 42.670751953125],\n                        [99.98378906250002, 42.67734375],\n                        [99.46787109375003, 42.568212890625],\n                        [97.20566406250003, 42.789794921875],\n                        [96.38544921875001, 42.720361328124994],\n                        [95.85957031250001, 43.2759765625],\n                        [95.52558593750001, 43.953955078125],\n                        [95.32558593750002, 44.039355468749996],\n                        [95.35029296875001, 44.278076171875],\n                        [94.71201171875003, 44.350830078125],\n                        [93.51621093750003, 44.944482421874994],\n                        [92.78789062499999, 45.0357421875],\n                        [92.57890624999999, 45.010986328125],\n                        [92.423828125, 45.008935546874994],\n                        [92.17265624999999, 45.03525390625],\n                        [92.02978515625, 45.068505859374994],\n                        [91.584375, 45.076513671875],\n                        [91.05, 45.217431640624994],\n                        [90.87724609374999, 45.19609375],\n                        [90.66181640625001, 45.525244140625],\n                        [91.00175781249999, 46.035791015624994],\n                        [90.99677734375001, 46.10498046875],\n                        [90.94755859374999, 46.177294921874996],\n                        [90.91152343750002, 46.270654296874994],\n                        [90.98574218750002, 46.7490234375],\n                        [90.91054687500002, 46.883251953125],\n                        [90.86992187499999, 46.954492187499994],\n                        [90.79902343750001, 46.98515625],\n                        [90.71552734375001, 47.003857421875],\n                        [90.49619140625003, 47.28515625],\n                        [90.42519531250002, 47.5041015625],\n                        [90.34746093749999, 47.596972656249996],\n                        [90.33066406250003, 47.655175781249994],\n                        [90.31328124999999, 47.67617187499999],\n                        [90.19101562500003, 47.702099609375],\n                        [90.10322265625001, 47.745410156249996],\n                        [90.02792968750003, 47.877685546875],\n                        [89.95869140625001, 47.886328125],\n                        [89.91044921874999, 47.8443359375],\n                        [89.83134765624999, 47.823291015624996],\n                        [89.778125, 47.827001953125],\n                        [89.56093750000002, 48.003955078124996],\n                        [89.47919921875001, 48.029052734375],\n                        [89.04765624999999, 48.0025390625],\n                        [88.97109375000002, 48.049951171874994],\n                        [88.91777343749999, 48.089013671874994],\n                        [88.83828125000002, 48.101708984374994],\n                        [88.68183593750001, 48.170556640624994],\n                        [88.57597656249999, 48.220166015625],\n                        [88.56679687500002, 48.317431640624996],\n                        [88.51708984375, 48.38447265625],\n                        [88.41396484375002, 48.40341796875],\n                        [88.30996093750002, 48.472070312499994],\n                        [87.97968750000001, 48.555126953125],\n                        [88.06005859375, 48.707177734374994],\n                        [87.83183593749999, 48.791650390624994],\n                        [87.7431640625, 48.881640625],\n                        [87.87216796875003, 49.000146484374994],\n                        [87.81630859375002, 49.0802734375],\n                        [87.8251953125, 49.11630859375],\n                        [87.81425781249999, 49.1623046875],\n                        [87.93476562500001, 49.16455078125],\n                        [87.98808593749999, 49.186914062499994],\n                        [88.02851562500001, 49.219775390624996],\n                        [88.11572265625, 49.256298828125],\n                        [88.19257812500001, 49.451708984374996],\n                        [88.63320312500002, 49.486132812499996],\n                        [88.83164062500003, 49.4484375],\n                        [88.86386718750003, 49.527636718749996],\n                        [88.90019531249999, 49.539697265624994],\n                        [88.94541015625003, 49.507666015625],\n                        [88.97060546875002, 49.483740234375],\n                        [89.00839843750003, 49.472802734374994],\n                        [89.10947265625003, 49.501367187499994],\n                        [89.17998046874999, 49.5322265625],\n                        [89.20292968749999, 49.595703125],\n                        [89.24394531249999, 49.62705078125],\n                        [89.39560546875003, 49.6115234375],\n                        [89.475, 49.66054687499999],\n                        [89.57919921875003, 49.69970703125],\n                        [89.65410156249999, 49.71748046875],\n                        [89.64384765624999, 49.90302734375],\n                        [90.0537109375, 50.09375],\n                        [90.65507812499999, 50.22236328125],\n                        [90.71435546875, 50.259423828124994],\n                        [90.7607421875, 50.305957031249996],\n                        [91.02158203125003, 50.415478515625],\n                        [91.23378906250002, 50.452392578125],\n                        [91.30058593749999, 50.46337890625],\n                        [91.3408203125, 50.470068359375],\n                        [91.4150390625, 50.468017578125],\n                        [91.44648437500001, 50.52216796875],\n                        [91.80429687500003, 50.693603515625],\n                        [92.10400390625, 50.6919921875],\n                        [92.1923828125, 50.700585937499994],\n                        [92.35478515624999, 50.864160156249994],\n                        [92.42636718750003, 50.803076171875],\n                        [92.62666015625001, 50.68828125],\n                        [92.68134765625001, 50.683203125],\n                        [92.73867187500002, 50.7109375],\n                        [92.779296875, 50.778662109375],\n                        [92.8564453125, 50.789111328124996],\n                        [92.94130859375002, 50.778222656249994],\n                        [93.103125, 50.60390625],\n                        [94.25107421875003, 50.556396484375],\n                        [94.35468750000001, 50.221826171874994],\n                        [94.61474609375, 50.023730468749996],\n                        [94.67548828125001, 50.028076171875],\n                        [94.71806640624999, 50.043261718749996],\n                        [94.93027343750003, 50.04375],\n                        [95.11142578125003, 49.935449218749994],\n                        [95.52265625000001, 49.91123046875],\n                        [96.06552734375003, 49.99873046875],\n                        [96.31503906250003, 49.901123046875],\n                        [96.98574218750002, 49.8828125],\n                        [97.20859375000003, 49.730810546875],\n                        [97.35976562500002, 49.741455078125],\n                        [97.58935546875, 49.911474609375],\n                        [98.00390625, 50.0142578125],\n                        [98.25029296874999, 50.30244140625],\n                        [98.27949218750001, 50.533251953124996],\n                        [98.14501953125, 50.5685546875],\n                        [98.07890624999999, 50.603808593749996],\n                        [98.02978515625, 50.64462890625],\n                        [97.82529296875003, 50.985253906249994],\n                        [98.103125, 51.483544921874994],\n                        [98.64052734375002, 51.801171875],\n                        [98.89316406250003, 52.11728515625],\n                        [99.92167968749999, 51.755517578125],\n                        [100.03457031250002, 51.737109375],\n                        [100.23037109375002, 51.729833984375],\n                        [100.46894531250001, 51.72607421875],\n                        [100.53623046875003, 51.7134765625],\n                        [101.38125, 51.45263671875],\n                        [101.57089843750003, 51.4671875],\n                        [101.82119140625002, 51.421044921874994],\n                        [102.11152343750001, 51.353466796875],\n                        [102.15566406250002, 51.313769531249996],\n                        [102.16005859375002, 51.26083984375],\n                        [102.14238281249999, 51.216064453125],\n                        [102.15195312500003, 51.10751953125],\n                        [102.19453125000001, 51.050683593749994],\n                        [102.21503906250001, 50.829443359375],\n                        [102.31660156250001, 50.71845703125],\n                        [102.28837890624999, 50.585107421874994],\n                        [103.30439453125001, 50.20029296875],\n                        [103.63291015625003, 50.138574218749994],\n                        [103.72324218750003, 50.153857421874996],\n                        [103.80263671875002, 50.176074218749996],\n                        [104.07871093750003, 50.154248046875],\n                        [105.38359374999999, 50.47373046875],\n                        [106.21787109375003, 50.304589843749994],\n                        [106.36845703124999, 50.317578125],\n                        [106.57441406250001, 50.32880859375],\n                        [106.71113281250001, 50.31259765625],\n                        [106.94130859375002, 50.196679687499994],\n                        [107.04023437500001, 50.086474609374996],\n                        [107.14306640625, 50.033007812499996],\n                        [107.23330078125002, 49.989404296874994],\n                        [107.34707031250002, 49.986669921875],\n                        [107.63095703125003, 49.98310546875],\n                        [107.91660156250003, 49.947802734374996],\n                        [107.96542968750003, 49.653515625],\n                        [108.40693359375001, 49.396386718749994],\n                        [108.5224609375, 49.34150390625],\n                        [108.61367187500002, 49.322802734374996],\n                        [109.23671875000002, 49.334912109375],\n                        [109.45371093750003, 49.296337890625],\n                        [109.52871093750002, 49.269873046875],\n                        [110.19990234375001, 49.17041015625],\n                        [110.42783203125003, 49.219970703125],\n                        [110.70976562499999, 49.14296875],\n                        [110.82792968749999, 49.166162109374994],\n                        [111.20419921875003, 49.304296875],\n                        [111.33662109375001, 49.35585937499999],\n                        [111.42929687500003, 49.342626953125],\n                        [112.07968750000003, 49.42421875],\n                        [112.49492187499999, 49.53232421875],\n                        [112.69736328125003, 49.507275390625],\n                        [112.80644531249999, 49.523583984374994],\n                        [112.91484374999999, 49.569238281249994],\n                        [113.05556640625002, 49.616259765624996],\n                        [113.09208984374999, 49.692529296874994],\n                        [113.16416015625003, 49.797167968749996],\n                        [113.31904296875001, 49.874316406249996],\n                        [113.44550781250001, 49.9416015625],\n                        [113.57421875, 50.00703125],\n                        [114.29707031250001, 50.2744140625],\n                        [114.7431640625, 50.233691406249996],\n                        [115.00332031250002, 50.138574218749994],\n                        [115.27451171875003, 49.948876953124994],\n                        [115.36503906249999, 49.911767578124994],\n                        [115.42919921875, 49.896484375],\n                        [115.58798828125003, 49.886035156249996],\n                        [115.7177734375, 49.880615234375],\n                        [115.79521484374999, 49.905908203124994],\n                        [115.92597656250001, 49.9521484375],\n                        [116.13457031249999, 50.010791015624996],\n                        [116.216796875, 50.00927734375],\n                        [116.35117187500003, 49.978076171874996],\n                        [116.55117187500002, 49.9203125],\n                        [116.68330078125001, 49.823779296874996],\n                        [115.82050781250001, 48.57724609375],\n                        [115.79169921875001, 48.455712890624994],\n                        [115.79658203125001, 48.346337890624994],\n                        [115.78554687500002, 48.2482421875],\n                        [115.63945312499999, 48.18623046875],\n                        [115.52509765625001, 48.130859375],\n                        [115.61640625000001, 47.874804687499996],\n                        [115.89824218749999, 47.686914062499994],\n                        [115.99384765625001, 47.71132812499999],\n                        [116.07480468750003, 47.78955078125],\n                        [116.23115234375001, 47.858203125],\n                        [116.31718749999999, 47.85986328125],\n                        [116.37822265624999, 47.844042968749996],\n                        [116.51347656249999, 47.83955078125],\n                        [116.65195312500003, 47.864501953125],\n                        [116.76054687499999, 47.869775390624994],\n                        [116.90117187499999, 47.853076171874996],\n                        [116.95166015625, 47.836572265624994],\n                        [117.06972656250002, 47.806396484375],\n                        [117.28593749999999, 47.666357421875],\n                        [117.35078125000001, 47.652197265625],\n                        [117.76835937499999, 47.987890625],\n                        [118.49843750000002, 47.983984375],\n                        [118.56777343750002, 47.943261718749994],\n                        [118.69052734375003, 47.822265625],\n                        [118.75996093750001, 47.757617187499996],\n                        [118.88027343750002, 47.72509765625],\n                        [119.017578125, 47.685351562499996],\n                        [119.08193359375002, 47.654150390625],\n                        [119.71113281250001, 47.15],\n                        [119.89785156250002, 46.8578125],\n                        [119.8671875, 46.672167968749996],\n                        [119.74746093750002, 46.627197265625],\n                        [119.70664062500003, 46.606005859374996],\n                        [119.62021484375003, 46.603955078125],\n                        [119.47402343750002, 46.62666015625],\n                        [119.33183593749999, 46.613818359374996],\n                        [119.162109375, 46.638671875],\n                        [118.95712890625003, 46.73486328125],\n                        [118.84394531250001, 46.760205078125],\n                        [118.79033203124999, 46.7470703125],\n                        [118.72294921874999, 46.69189453125],\n                        [118.64873046874999, 46.70166015625],\n                        [118.58046875000002, 46.69189453125],\n                        [118.40439453125003, 46.703173828124996],\n                        [118.30869140625003, 46.717041015625],\n                        [118.15683593750003, 46.678564453125],\n                        [118.0712890625, 46.6666015625],\n                        [117.7412109375, 46.5181640625],\n                        [117.546875, 46.58828125],\n                        [117.43808593750003, 46.586230468749996],\n                        [117.40556640624999, 46.5708984375],\n                        [117.39218750000003, 46.53759765625],\n                        [117.35634765625002, 46.436669921874994],\n                        [117.35693359375, 46.39130859375],\n                        [117.33339843750002, 46.36201171875],\n                        [116.85908203125001, 46.387939453125],\n                        [116.56259765625003, 46.289794921875],\n                        [116.21298828125003, 45.8869140625],\n                        [116.22910156250003, 45.845751953124996],\n                        [116.240625, 45.79599609375],\n                        [116.19765625000002, 45.73935546875],\n                        [115.68105468750002, 45.458251953125],\n                        [115.16259765625, 45.390234375],\n                        [114.91923828124999, 45.378271484375],\n                        [114.73876953125, 45.41962890625],\n                        [114.56015625000003, 45.389990234375],\n                        [114.41914062500001, 45.202587890625],\n                        [114.16738281250002, 45.049853515624996],\n                        [114.08027343750001, 44.971142578125],\n                        [113.87705078125003, 44.89619140625],\n                        [113.65263671874999, 44.763476562499996],\n                        [113.58701171875003, 44.745703125],\n                        [113.04941406250003, 44.810351562499996],\n                        [112.70673828125001, 44.883447265624994],\n                        [112.59677734375003, 44.91767578125],\n                        [112.49931640624999, 45.0109375],\n                        [112.41132812500001, 45.058203125],\n                        [112.11289062500003, 45.062939453125],\n                        [112.03261718750002, 45.081640625],\n                        [111.89804687500003, 45.0640625],\n                        [111.40224609375002, 44.36728515625],\n                        [111.93173828125003, 43.81494140625],\n                        [111.878125, 43.68017578125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Mongolia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [145.75195312499997, 15.133154296874991],\n                        [145.71318359375007, 15.215283203125026],\n                        [145.821875, 15.265380859375014],\n                        [145.75195312499997, 15.133154296874991]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"N. Mariana Is.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [32.112890625, -26.839453125],\n                        [32.10595703125, -26.52001953125],\n                        [32.04140625000002, -26.28125],\n                        [32.060546875, -26.018359375],\n                        [31.9482421875, -25.957617187500006],\n                        [31.98583984375, -24.46064453125001],\n                        [31.799609375000017, -23.8921875],\n                        [31.54560546875001, -23.48232421875001],\n                        [31.287890625000017, -22.40205078125001],\n                        [31.429492187500017, -22.298828125],\n                        [32.429785156250006, -21.29707031250001],\n                        [32.353613281250006, -21.136523437500003],\n                        [32.49238281250001, -20.659765625],\n                        [32.992773437500006, -19.98486328125],\n                        [32.77763671875002, -19.388769531250006],\n                        [32.84980468750001, -19.10439453125001],\n                        [32.69970703125, -18.94091796875],\n                        [32.99306640625002, -18.35957031250001],\n                        [32.87626953125002, -16.88359375],\n                        [32.94804687500002, -16.71230468750001],\n                        [31.939843750000023, -16.428808593750006],\n                        [31.236230468750023, -16.02363281250001],\n                        [30.437792968750017, -15.995312500000011],\n                        [30.39609375, -15.64306640625],\n                        [30.231835937500023, -14.990332031250006],\n                        [33.201757812500006, -14.013378906250011],\n                        [33.63642578125001, -14.568164062500003],\n                        [34.375, -14.4248046875],\n                        [34.50527343750002, -14.59814453125],\n                        [34.54082031250002, -15.297265625],\n                        [34.24609375, -15.829394531250003],\n                        [34.528125, -16.319140625],\n                        [34.93339843750002, -16.760351562500006],\n                        [35.11210937500002, -16.898535156250006],\n                        [35.06464843750001, -17.07861328125],\n                        [35.124609375, -17.127246093750003],\n                        [35.20136718750001, -17.13105468750001],\n                        [35.272558593750006, -17.118457031250003],\n                        [35.29042968750002, -17.096972656250003],\n                        [35.28115234375002, -16.80781250000001],\n                        [35.22978515625002, -16.639257812500006],\n                        [35.178320312500006, -16.573339843750006],\n                        [35.16718750000001, -16.56025390625001],\n                        [35.242773437500006, -16.375390625],\n                        [35.358496093750006, -16.160546875],\n                        [35.59931640625001, -16.12587890625001],\n                        [35.70888671875002, -16.095800781250006],\n                        [35.75527343750002, -16.05830078125001],\n                        [35.79121093750001, -15.958691406250011],\n                        [35.89277343750001, -14.891796875000011],\n                        [35.86669921875, -14.86376953125],\n                        [35.84716796875, -14.6708984375],\n                        [35.6904296875, -14.465527343750011],\n                        [35.48847656250001, -14.201074218750009],\n                        [35.37578125000002, -14.058691406250006],\n                        [35.24746093750002, -13.896875],\n                        [35.01386718750001, -13.643457031250009],\n                        [34.61152343750001, -13.437890625],\n                        [34.54570312500002, -13.21630859375],\n                        [34.542578125, -13.108691406250003],\n                        [34.35781250000002, -12.164746093750011],\n                        [34.60625, -11.690039062500006],\n                        [34.65957031250002, -11.588671875],\n                        [34.82656250000002, -11.57568359375],\n                        [34.95947265625, -11.578125],\n                        [35.1826171875, -11.574804687500006],\n                        [35.41826171875002, -11.583203125000011],\n                        [35.50439453125, -11.604785156250003],\n                        [35.56435546875002, -11.60234375],\n                        [35.630957031250006, -11.58203125],\n                        [35.78544921875002, -11.452929687500003],\n                        [35.91132812500001, -11.4546875],\n                        [36.08222656250001, -11.537304687500011],\n                        [36.17548828125001, -11.609277343750009],\n                        [36.19130859375002, -11.670703125],\n                        [36.3056640625, -11.706347656250003],\n                        [36.97890625000002, -11.566992187500006],\n                        [37.37285156250002, -11.71044921875],\n                        [37.54169921875001, -11.675097656250003],\n                        [37.72480468750001, -11.580664062500006],\n                        [37.92021484375002, -11.294726562500003],\n                        [38.491796875, -11.413281250000011],\n                        [38.9875, -11.167285156250003],\n                        [39.81708984375001, -10.912402343750003],\n                        [39.98867187500002, -10.82080078125],\n                        [40.46357421875001, -10.46435546875],\n                        [40.61171875000002, -10.661523437500009],\n                        [40.48662109375002, -10.76513671875],\n                        [40.59716796875, -10.830664062500006],\n                        [40.40283203125, -11.33203125],\n                        [40.53154296875002, -12.004589843750011],\n                        [40.48710937500002, -12.4921875],\n                        [40.58085937500002, -12.635546875],\n                        [40.43681640625002, -12.983105468750011],\n                        [40.56875, -12.984667968750003],\n                        [40.595703125, -14.122851562500003],\n                        [40.715625, -14.214453125],\n                        [40.64609375, -14.538671875],\n                        [40.775, -14.421289062500009],\n                        [40.84453125000002, -14.718652343750009],\n                        [40.617773437500006, -15.115527343750003],\n                        [40.650976562500006, -15.260937500000011],\n                        [39.98359375000001, -16.22548828125001],\n                        [39.79091796875002, -16.29453125],\n                        [39.84462890625002, -16.435644531250006],\n                        [39.084375, -16.97285156250001],\n                        [38.14492187500002, -17.242773437500006],\n                        [37.24453125000002, -17.73994140625001],\n                        [36.93935546875002, -17.993457031250003],\n                        [36.40371093750002, -18.76972656250001],\n                        [36.26289062500001, -18.71962890625001],\n                        [36.23564453125002, -18.861328125],\n                        [35.85371093750001, -18.99335937500001],\n                        [34.947851562500006, -19.81269531250001],\n                        [34.6494140625, -19.70136718750001],\n                        [34.75576171875002, -19.82197265625001],\n                        [34.705078125, -20.473046875],\n                        [34.98232421875002, -20.80625],\n                        [35.267675781250006, -21.650976562500006],\n                        [35.31572265625002, -22.396875],\n                        [35.38300781250001, -22.45458984375],\n                        [35.45634765625002, -22.11591796875001],\n                        [35.53007812500002, -22.248144531250006],\n                        [35.57539062500001, -22.96308593750001],\n                        [35.37041015625002, -23.79824218750001],\n                        [35.5419921875, -23.82441406250001],\n                        [35.48964843750002, -24.065527343750006],\n                        [34.99208984375002, -24.65058593750001],\n                        [32.96113281250001, -25.49042968750001],\n                        [32.590429687500006, -26.00410156250001],\n                        [32.84882812500001, -26.26806640625],\n                        [32.95488281250002, -26.08359375],\n                        [32.93359375, -26.25234375],\n                        [32.88916015625, -26.83046875],\n                        [32.88613281250002, -26.84931640625001],\n                        [32.353515625, -26.861621093750003],\n                        [32.19960937500002, -26.83349609375],\n                        [32.112890625, -26.839453125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Mozambique\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-16.37333984374999, 19.706445312499994],\n                            [-16.437548828124932, 19.609277343749994],\n                            [-16.477001953124983, 19.710351562499994],\n                            [-16.343652343749994, 19.86621093750003],\n                            [-16.37333984374999, 19.706445312499994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-5.359912109374989, 16.282861328124994],\n                            [-5.5125, 15.496289062499983],\n                            [-9.293701171875, 15.502832031249994],\n                            [-9.350585937499943, 15.677392578125023],\n                            [-9.38535156249992, 15.667626953124994],\n                            [-9.4265625, 15.623046875000057],\n                            [-9.447705078124926, 15.574853515624994],\n                            [-9.446923828124937, 15.458203124999955],\n                            [-9.941406249999972, 15.373779296874986],\n                            [-10.696582031249989, 15.42265625],\n                            [-10.9482421875, 15.151123046875014],\n                            [-11.455224609374994, 15.62539062499999],\n                            [-11.760156249999937, 15.425537109375057],\n                            [-11.828759765624966, 15.244873046875014],\n                            [-11.872851562499989, 14.995166015625031],\n                            [-12.02158203124992, 14.804931640625],\n                            [-12.081542968749972, 14.766357421875057],\n                            [-12.104687499999955, 14.745361328125043],\n                            [-12.40869140625, 14.889013671874991],\n                            [-12.735253906249994, 15.13125],\n                            [-13.105273437499989, 15.57177734375],\n                            [-13.40966796875, 16.059179687500006],\n                            [-13.756640624999989, 16.172509765624994],\n                            [-13.868457031249932, 16.14814453125001],\n                            [-14.300097656249932, 16.58027343750001],\n                            [-14.990625, 16.676904296874994],\n                            [-15.768212890624994, 16.485107421875],\n                            [-16.23901367187497, 16.53129882812499],\n                            [-16.44101562499992, 16.20454101562504],\n                            [-16.480078124999977, 16.097216796875017],\n                            [-16.50205078124992, 15.917333984375063],\n                            [-16.53525390624995, 15.838378906250057],\n                            [-16.53574218749995, 16.28681640625001],\n                            [-16.463623046875, 16.60151367187501],\n                            [-16.030322265625017, 17.88793945312497],\n                            [-16.213085937499926, 19.003320312500023],\n                            [-16.51445312499996, 19.361962890624994],\n                            [-16.305273437499977, 19.51264648437504],\n                            [-16.44487304687499, 19.47314453124997],\n                            [-16.21044921875003, 20.227929687500023],\n                            [-16.42978515624995, 20.652343750000057],\n                            [-16.622509765624955, 20.634179687499994],\n                            [-16.87607421874992, 21.086132812499955],\n                            [-16.998242187499926, 21.039697265625023],\n                            [-17.048046874999955, 20.80615234375003],\n                            [-17.06396484375, 20.89882812499999],\n                            [-16.96455078125001, 21.329248046875023],\n                            [-15.231201171875, 21.331298828125],\n                            [-14.084667968749926, 21.33271484375001],\n                            [-13.626025390624932, 21.33325195312503],\n                            [-13.396728515624943, 21.333544921875017],\n                            [-13.167431640624926, 21.333789062500074],\n                            [-13.016210937499949, 21.33393554687501],\n                            [-13.025097656249983, 21.46679687499997],\n                            [-13.032226562500028, 21.572070312500017],\n                            [-13.041748046875, 21.71381835937504],\n                            [-13.051220703124983, 21.854785156250074],\n                            [-13.094335937499977, 22.49599609375005],\n                            [-13.153271484374983, 22.820507812499983],\n                            [-13.031494140624943, 23.000244140625],\n                            [-12.895996093749972, 23.08955078125001],\n                            [-12.739599609375006, 23.192724609375063],\n                            [-12.62041015624996, 23.271337890625006],\n                            [-12.559375, 23.290820312500045],\n                            [-12.372900390624977, 23.318017578124994],\n                            [-12.023437499999943, 23.467578125000017],\n                            [-12.016308593749983, 23.97021484375],\n                            [-12.016308593749983, 24.378662109375],\n                            [-12.016308593749983, 24.923242187499994],\n                            [-12.016308593749983, 25.059375],\n                            [-12.016308593749983, 25.331689453124994],\n                            [-12.016308593749983, 25.740136718749994],\n                            [-12.016308593749983, 25.995410156250017],\n                            [-10.376123046874966, 25.995458984375034],\n                            [-9.444531249999983, 25.99550781250005],\n                            [-9.071923828124937, 25.99550781250005],\n                            [-8.885644531249994, 25.99550781250005],\n                            [-8.682226562499949, 25.99550781250005],\n                            [-8.68212890625, 26.109472656250006],\n                            [-8.68212890625, 26.273193359375057],\n                            [-8.682324218749955, 26.49770507812505],\n                            [-8.682617187500028, 26.723144531250057],\n                            [-8.682861328124972, 26.92133789062501],\n                            [-8.683349609375, 27.285937500000045],\n                            [-4.822607421874949, 24.99560546875],\n                            [-5.640771484374994, 24.99516601562499],\n                            [-5.959814453124977, 24.994970703125063],\n                            [-6.287207031249977, 24.99482421875001],\n                            [-6.594091796874977, 24.99462890624997],\n                            [-6.396582031249977, 23.274804687499994],\n                            [-6.02641601562496, 20.04218750000001],\n                            [-5.827099609374955, 18.301562500000045],\n                            [-5.741699218749943, 17.555566406250023],\n                            [-5.713183593750017, 17.306884765625],\n                            [-5.684765624999983, 17.058251953124966],\n                            [-5.628662109375028, 16.568652343750045],\n                            [-5.50961914062492, 16.442041015625023],\n                            [-5.359912109374989, 16.282861328124994]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Mauritania\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-62.1484375, 16.74033203124999],\n                        [-62.221630859375, 16.699511718750003],\n                        [-62.191357421875, 16.804394531249997],\n                        [-62.1484375, 16.74033203124999]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Montserrat\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [57.65126953125002, -20.48486328125],\n                        [57.31767578125002, -20.42763671875001],\n                        [57.416015625, -20.18378906250001],\n                        [57.65654296875002, -19.98994140625001],\n                        [57.7919921875, -20.21259765625001],\n                        [57.65126953125002, -20.48486328125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Mauritius\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [34.95947265625003, -11.578125],\n                        [34.82656250000005, -11.575683593749972],\n                        [34.65957031250005, -11.58867187499996],\n                        [34.61855468750005, -11.620214843749991],\n                        [34.60625, -11.690039062500006],\n                        [34.3578125, -12.164746093749997],\n                        [34.542578125, -13.108691406250003],\n                        [34.54570312500002, -13.21630859375],\n                        [34.6115234375001, -13.437890625000023],\n                        [35.0138671875001, -13.64345703124998],\n                        [35.247460937499994, -13.896875],\n                        [35.37578125000002, -14.05869140625002],\n                        [35.48847656250004, -14.20107421874998],\n                        [35.69042968749997, -14.465527343750026],\n                        [35.84716796875003, -14.670898437500043],\n                        [35.8927734375001, -14.891796875000011],\n                        [35.7912109375001, -15.958691406250026],\n                        [35.75527343750005, -16.058300781249983],\n                        [35.708886718749994, -16.095800781249977],\n                        [35.5993164062501, -16.12587890624998],\n                        [35.35849609375006, -16.160546875000023],\n                        [35.242773437500006, -16.375390625],\n                        [35.16718750000004, -16.56025390625001],\n                        [35.178320312500006, -16.57333984375002],\n                        [35.22978515625002, -16.639257812500034],\n                        [35.281152343749994, -16.8078125],\n                        [35.29042968750005, -17.096972656250017],\n                        [35.27255859375006, -17.11845703124996],\n                        [35.2013671875001, -17.13105468750004],\n                        [35.124609375, -17.127246093749974],\n                        [35.06464843750004, -17.078613281250014],\n                        [35.11210937500002, -16.898535156250006],\n                        [34.93339843750002, -16.760351562500006],\n                        [34.528125, -16.319140625],\n                        [34.24609374999997, -15.829394531249974],\n                        [34.54082031250002, -15.297265625],\n                        [34.50527343750005, -14.598144531249957],\n                        [34.375, -14.4248046875],\n                        [33.63642578125004, -14.568164062499974],\n                        [33.148046875, -13.94091796875],\n                        [32.98125, -14.009375],\n                        [32.797460937500006, -13.6884765625],\n                        [32.67041015624997, -13.590429687500006],\n                        [32.96757812500002, -13.225],\n                        [32.97519531250006, -12.701367187499983],\n                        [33.51230468750006, -12.347753906249977],\n                        [33.340136718750074, -12.308300781250011],\n                        [33.25234375000005, -12.112597656250031],\n                        [33.3039062500001, -11.69082031249998],\n                        [33.23271484375002, -11.417675781250026],\n                        [33.26835937500002, -11.403906249999977],\n                        [33.379785156249994, -11.15791015625004],\n                        [33.29277343750002, -10.85234375],\n                        [33.661523437499994, -10.553125],\n                        [33.55371093749997, -10.391308593750011],\n                        [33.53759765624997, -10.351562499999986],\n                        [33.52890625, -10.234667968749974],\n                        [33.31152343750003, -10.037988281249966],\n                        [33.3371093750001, -9.954003906249994],\n                        [33.350976562499994, -9.862207031250037],\n                        [33.25, -9.759570312500003],\n                        [33.148046875, -9.603515625],\n                        [32.99599609375005, -9.622851562499946],\n                        [32.91992187500003, -9.407421875000026],\n                        [33.88886718750004, -9.670117187499983],\n                        [33.99560546875003, -9.495410156250003],\n                        [34.32089843750006, -9.731542968749977],\n                        [34.56992187500006, -10.241113281249966],\n                        [34.66708984375006, -10.792480468750028],\n                        [34.60791015624997, -11.08046875],\n                        [34.77382812500005, -11.341699218750009],\n                        [34.890625, -11.3935546875],\n                        [34.93701171874997, -11.463476562500034],\n                        [34.95947265625003, -11.578125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Malawi\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [111.38925781250006, 2.415332031250031],\n                            [111.31152343749997, 2.437597656250034],\n                            [111.33349609374997, 2.768310546875],\n                            [111.38925781250006, 2.415332031250031]\n                        ]\n                    ],\n                    [\n                        [\n                            [104.22158203125, 2.731738281250003],\n                            [104.1291015625001, 2.767236328125037],\n                            [104.18476562500004, 2.871728515625009],\n                            [104.22158203125, 2.731738281250003]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.88476562499997, 4.186132812500006],\n                            [117.64902343750012, 4.168994140624974],\n                            [117.70800781249997, 4.262402343749997],\n                            [117.88476562499997, 4.186132812500006]\n                        ]\n                    ],\n                    [\n                        [\n                            [100.28896484375005, 5.294726562499989],\n                            [100.19101562500006, 5.28286132812498],\n                            [100.2455078125, 5.467773437499986],\n                            [100.33886718749997, 5.410058593750037],\n                            [100.28896484375005, 5.294726562499989]\n                        ]\n                    ],\n                    [\n                        [\n                            [99.848046875, 6.465722656249994],\n                            [99.9186523437501, 6.358593750000011],\n                            [99.74375, 6.263281249999963],\n                            [99.64628906250002, 6.418359375000023],\n                            [99.848046875, 6.465722656249994]\n                        ]\n                    ],\n                    [\n                        [\n                            [102.10107421874997, 6.242236328125031],\n                            [102.34013671875002, 6.172021484375023],\n                            [102.534375, 5.862548828125028],\n                            [103.09707031250005, 5.408447265624986],\n                            [103.41582031250007, 4.85029296875004],\n                            [103.43945312499997, 2.93310546875],\n                            [103.8122070312501, 2.58046875],\n                            [104.21855468750002, 1.722851562499997],\n                            [104.25009765625012, 1.388574218750009],\n                            [104.11494140625004, 1.412255859375037],\n                            [103.98144531250003, 1.623632812500034],\n                            [103.99150390625002, 1.454785156249997],\n                            [103.6945312500001, 1.449658203125026],\n                            [103.48027343750007, 1.329492187499966],\n                            [103.35683593750005, 1.546142578125057],\n                            [102.72714843750012, 1.855566406250034],\n                            [101.29550781250012, 2.885205078125011],\n                            [101.29990234375012, 3.253271484375034],\n                            [100.71542968750006, 3.966210937499966],\n                            [100.79550781250012, 4.023388671874983],\n                            [100.61455078125002, 4.3734375],\n                            [100.34326171874997, 5.984179687500031],\n                            [100.11914062499997, 6.441992187500048],\n                            [100.26142578125004, 6.682714843749963],\n                            [100.3454101562501, 6.549902343750006],\n                            [100.75449218750012, 6.460058593749991],\n                            [100.87392578125, 6.24541015624996],\n                            [101.05351562500002, 6.242578125],\n                            [100.98164062500004, 5.771044921875045],\n                            [101.1139648437501, 5.636767578125045],\n                            [101.5560546875, 5.907763671875003],\n                            [101.67841796875004, 5.778808593750028],\n                            [101.87363281250012, 5.825292968749991],\n                            [102.10107421874997, 6.242236328125031]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.5744140625001, 4.17060546875004],\n                            [117.10058593750003, 4.337060546875023],\n                            [116.51474609375006, 4.370800781249969],\n                            [115.86074218750005, 4.348046875000037],\n                            [115.67880859375006, 4.193017578124994],\n                            [115.45439453125002, 3.034326171875009],\n                            [115.24697265625005, 3.025927734374989],\n                            [115.117578125, 2.89487304687502],\n                            [115.08076171875004, 2.63422851562504],\n                            [115.1791015625, 2.523193359374972],\n                            [114.78642578125002, 2.250488281250014],\n                            [114.83056640625003, 1.980029296874989],\n                            [114.5125, 1.452001953124963],\n                            [113.90234375000003, 1.434277343749997],\n                            [113.6222656250001, 1.2359375],\n                            [113.00654296875004, 1.433886718750003],\n                            [112.94296875000006, 1.566992187500034],\n                            [112.47617187500006, 1.559082031250028],\n                            [112.1857421875001, 1.4390625],\n                            [112.078515625, 1.143359374999974],\n                            [111.80898437500005, 1.011669921874969],\n                            [111.10136718750002, 1.050537109374986],\n                            [110.50576171875005, 0.861962890625023],\n                            [109.65400390625004, 1.614892578125023],\n                            [109.53896484375, 1.89619140625004],\n                            [109.62890625000003, 2.027539062499983],\n                            [109.86484375000012, 1.764453125000031],\n                            [110.34921875000012, 1.719726562499972],\n                            [111.22324218750012, 1.395849609374991],\n                            [111.0287109375, 1.557812500000026],\n                            [111.26816406250012, 2.13974609375002],\n                            [111.20859375000012, 2.379638671875043],\n                            [111.44384765625003, 2.381542968749983],\n                            [111.5125, 2.743017578124991],\n                            [112.98789062500006, 3.161914062499974],\n                            [113.92392578125006, 4.243212890625003],\n                            [114.0638671875, 4.592675781249966],\n                            [114.65410156250007, 4.037646484375045],\n                            [114.84023437500005, 4.393212890625009],\n                            [114.74667968750006, 4.718066406250017],\n                            [115.02675781250005, 4.899707031249989],\n                            [115.10703125000006, 4.390429687499974],\n                            [115.290625, 4.352587890624989],\n                            [115.1400390625, 4.899755859374991],\n                            [115.37490234375, 4.932763671874966],\n                            [115.55449218750007, 5.093554687500045],\n                            [115.41904296875012, 5.413183593749963],\n                            [115.60390625, 5.603417968749994],\n                            [115.74082031250012, 5.533007812500045],\n                            [115.8771484375001, 5.613525390625014],\n                            [116.74980468750007, 6.977099609374989],\n                            [116.8498046875001, 6.826708984374989],\n                            [116.78808593749997, 6.606103515624994],\n                            [117.12851562500012, 6.968896484375009],\n                            [117.2298828125, 6.939990234374974],\n                            [117.29404296875006, 6.676904296875023],\n                            [117.60966796875002, 6.512646484375054],\n                            [117.69375, 6.35],\n                            [117.64453124999997, 6.001855468749994],\n                            [117.5011718750001, 5.884667968750009],\n                            [118.00380859375, 6.053320312499991],\n                            [118.11582031250006, 5.8625],\n                            [117.93476562500004, 5.7875],\n                            [117.97363281249997, 5.70625],\n                            [118.35312500000012, 5.80605468749998],\n                            [118.59482421875006, 5.592089843750003],\n                            [119.22343750000007, 5.412646484375031],\n                            [119.2663085937501, 5.308105468750057],\n                            [119.21962890625, 5.159814453125037],\n                            [118.9125, 5.02290039062504],\n                            [118.26054687500007, 4.988867187500034],\n                            [118.18535156250002, 4.828515625000051],\n                            [118.5625, 4.502148437499997],\n                            [118.54833984375003, 4.379248046875006],\n                            [118.008203125, 4.250244140625014],\n                            [117.6964843750001, 4.342822265625045],\n                            [117.5744140625001, 4.17060546875004]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.14160156250003, 7.168212890625028],\n                            [117.08066406250006, 7.115283203124989],\n                            [117.06425781250007, 7.26069335937504],\n                            [117.2640625, 7.351660156250006],\n                            [117.26679687500004, 7.220800781249991],\n                            [117.14160156250003, 7.168212890625028]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Malaysia\", \"childNum\": 8 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [23.380664062500017, -17.640625],\n                        [24.27490234375, -17.481054687500006],\n                        [24.73291015625, -17.51777343750001],\n                        [25.001757812500017, -17.56855468750001],\n                        [25.2587890625, -17.793554687500006],\n                        [24.909082031250023, -17.821386718750006],\n                        [24.530566406250017, -18.052734375],\n                        [24.243945312500017, -18.0234375],\n                        [23.599707031250006, -18.4599609375],\n                        [23.219335937500006, -17.99970703125001],\n                        [20.97412109375, -18.31884765625],\n                        [20.9794921875, -21.9619140625],\n                        [19.977343750000017, -22.00019531250001],\n                        [19.98046875, -24.77675781250001],\n                        [19.98046875, -28.310351562500003],\n                        [19.98046875, -28.451269531250006],\n                        [19.539843750000017, -28.574609375],\n                        [19.31269531250001, -28.73330078125001],\n                        [19.24580078125001, -28.901660156250003],\n                        [19.16171875, -28.938769531250003],\n                        [18.310839843750017, -28.88623046875],\n                        [17.44794921875001, -28.69814453125001],\n                        [17.34785156250001, -28.50117187500001],\n                        [17.358691406250017, -28.26943359375001],\n                        [17.1884765625, -28.13251953125001],\n                        [17.05625, -28.031054687500003],\n                        [16.93330078125001, -28.069628906250003],\n                        [16.875292968750017, -28.1279296875],\n                        [16.841210937500023, -28.21894531250001],\n                        [16.81015625, -28.26455078125001],\n                        [16.7875, -28.39472656250001],\n                        [16.755761718750023, -28.4521484375],\n                        [16.62617187500001, -28.487890625],\n                        [16.487109375000017, -28.572851562500006],\n                        [16.447558593750017, -28.617578125],\n                        [15.719042968750017, -27.9658203125],\n                        [15.341503906250011, -27.386523437500003],\n                        [15.139062500000023, -26.50800781250001],\n                        [14.9677734375, -26.31806640625001],\n                        [14.837109375000011, -25.033203125],\n                        [14.5015625, -24.201953125],\n                        [14.462792968750023, -22.44912109375001],\n                        [13.450585937500023, -20.91669921875001],\n                        [13.168359375000023, -20.184667968750006],\n                        [12.458203125000011, -18.9267578125],\n                        [11.77587890625, -18.001757812500003],\n                        [11.733496093750006, -17.7509765625],\n                        [11.743066406250023, -17.24921875000001],\n                        [11.902539062500011, -17.2265625],\n                        [12.013964843750017, -17.168554687500006],\n                        [12.21337890625, -17.2099609375],\n                        [12.318457031250006, -17.21337890625],\n                        [12.359277343750023, -17.205859375],\n                        [12.548144531250017, -17.212695312500003],\n                        [13.179492187500017, -16.9716796875],\n                        [13.475976562500023, -17.0400390625],\n                        [14.017480468750023, -17.40888671875001],\n                        [16.1484375, -17.390234375],\n                        [18.396386718750023, -17.3994140625],\n                        [18.95527343750001, -17.803515625],\n                        [20.1943359375, -17.863671875],\n                        [20.745507812500023, -18.01972656250001],\n                        [22.32421875, -17.8375],\n                        [23.380664062500017, -17.640625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Namibia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [167.54443359375003, -22.62324218750001],\n                            [167.44375, -22.63916015624997],\n                            [167.44345703125006, -22.541406250000037],\n                            [167.54443359375003, -22.62324218750001]\n                        ]\n                    ],\n                    [\n                        [\n                            [168.01093750000004, -21.429980468750017],\n                            [168.1390625, -21.44521484375001],\n                            [168.12070312500012, -21.615820312500034],\n                            [167.96679687500003, -21.641601562499957],\n                            [167.81542968749997, -21.392675781249963],\n                            [167.9884765625001, -21.337890624999986],\n                            [168.01093750000004, -21.429980468750017]\n                        ]\n                    ],\n                    [\n                        [\n                            [167.40087890625003, -21.16064453125003],\n                            [167.07265625, -20.99726562499997],\n                            [167.03271484374997, -20.922558593750026],\n                            [167.18945312500003, -20.803515625000017],\n                            [167.05576171875012, -20.720214843750014],\n                            [167.29794921875006, -20.732519531250034],\n                            [167.40087890625003, -21.16064453125003]\n                        ]\n                    ],\n                    [\n                        [\n                            [164.20234375000004, -20.246093749999957],\n                            [164.4359375, -20.282226562499957],\n                            [165.191796875, -20.768847656249974],\n                            [165.66279296875004, -21.267187499999977],\n                            [166.94238281250003, -22.09013671875003],\n                            [166.97031250000012, -22.32285156250002],\n                            [166.77412109375004, -22.37617187500004],\n                            [166.4679687500001, -22.256054687499997],\n                            [164.92744140625004, -21.289843749999974],\n                            [164.16972656250007, -20.48017578125004],\n                            [164.05966796875012, -20.141503906249966],\n                            [164.20234375000004, -20.246093749999957]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"New Caledonia\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [14.97900390625, 22.99619140624999],\n                        [15.181835937500011, 21.523388671874997],\n                        [15.607324218750023, 20.954394531250003],\n                        [15.587109375000011, 20.733300781249994],\n                        [15.963183593750017, 20.34619140625],\n                        [15.735058593750011, 19.904052734375],\n                        [15.474316406250011, 16.908398437499997],\n                        [14.367968750000017, 15.750146484374994],\n                        [13.4482421875, 14.380664062500003],\n                        [13.505761718750023, 14.134423828124994],\n                        [13.606347656250023, 13.70458984375],\n                        [13.426953125000011, 13.701757812499991],\n                        [13.323828125, 13.670849609374997],\n                        [12.871679687500006, 13.449023437500003],\n                        [12.65478515625, 13.3265625],\n                        [12.463183593750017, 13.09375],\n                        [10.958886718750023, 13.371533203124997],\n                        [10.475878906250017, 13.330224609374994],\n                        [10.229589843750006, 13.281005859375],\n                        [10.184667968750006, 13.270117187499991],\n                        [9.615917968750011, 12.810644531249991],\n                        [9.201562500000023, 12.821484375],\n                        [8.750585937500006, 12.908154296874997],\n                        [8.4560546875, 13.059667968749991],\n                        [8.095019531250017, 13.291162109374994],\n                        [7.955761718750011, 13.32275390625],\n                        [7.788671875, 13.337890625],\n                        [7.056738281250006, 13.000195312499997],\n                        [6.804296875, 13.107666015625],\n                        [6.2998046875, 13.658789062499991],\n                        [6.184277343750011, 13.66367187499999],\n                        [5.838183593750017, 13.765380859375],\n                        [5.491992187500017, 13.872851562500003],\n                        [5.415820312500017, 13.859179687500003],\n                        [5.361621093750017, 13.836865234374997],\n                        [5.241894531250011, 13.757226562499994],\n                        [4.664843750000017, 13.733203125],\n                        [4.147558593750006, 13.457714843749997],\n                        [3.947851562500006, 12.775048828124994],\n                        [3.646679687500011, 12.529980468749997],\n                        [3.595410156250011, 11.6962890625],\n                        [2.805273437500006, 12.383837890624989],\n                        [2.366015625000017, 12.221923828125],\n                        [2.38916015625, 11.897070312499991],\n                        [2.072949218750011, 12.309375],\n                        [2.226269531250011, 12.466064453125],\n                        [2.104589843750006, 12.701269531249991],\n                        [1.56494140625, 12.635400390624994],\n                        [0.9873046875, 13.041894531249994],\n                        [0.988476562500011, 13.36484375],\n                        [1.201171875, 13.357519531249991],\n                        [0.6181640625, 13.703417968750003],\n                        [0.42919921875, 13.972119140624997],\n                        [0.382519531250011, 14.245800781249997],\n                        [0.163867187500017, 14.497216796874994],\n                        [0.217480468750011, 14.911474609374991],\n                        [0.947460937500011, 14.982128906249997],\n                        [1.300195312500023, 15.272265625],\n                        [3.001074218750006, 15.340966796874994],\n                        [3.06015625, 15.427197265624997],\n                        [3.504296875000023, 15.356347656249994],\n                        [3.5205078125, 15.483105468749997],\n                        [3.842968750000011, 15.701708984375003],\n                        [4.121289062500011, 16.357714843750003],\n                        [4.234667968750017, 16.996386718750003],\n                        [4.227636718750006, 19.142773437499997],\n                        [5.836621093750011, 19.479150390624994],\n                        [7.481738281250017, 20.873095703125003],\n                        [11.967871093750006, 23.517871093750003],\n                        [13.48125, 23.18017578125],\n                        [14.215527343750011, 22.619677734375003],\n                        [14.97900390625, 22.99619140624999]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Niger\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [7.30078125, 4.418164062500026],\n                            [7.140429687500017, 4.395117187500034],\n                            [7.227343750000045, 4.527343749999972],\n                            [7.30078125, 4.418164062500026]\n                        ]\n                    ],\n                    [\n                        [\n                            [6.804296875, 13.107666015625],\n                            [7.056738281250006, 13.00019531250004],\n                            [7.788671875, 13.337890625],\n                            [7.955761718750011, 13.322753906250028],\n                            [8.095019531250045, 13.29116210937498],\n                            [8.750585937500034, 12.908154296875026],\n                            [9.20156250000008, 12.82148437500004],\n                            [9.615917968750011, 12.810644531249963],\n                            [10.184667968750063, 13.270117187499963],\n                            [10.229589843749977, 13.281005859375043],\n                            [10.475878906250074, 13.330224609375037],\n                            [10.958886718750051, 13.371533203125011],\n                            [12.463183593750017, 13.09375],\n                            [12.654785156250057, 13.3265625],\n                            [13.426953125000068, 13.701757812499963],\n                            [13.606347656250023, 13.704589843750014],\n                            [13.932324218750011, 13.258496093749997],\n                            [14.06396484375, 13.078515625],\n                            [14.160058593750023, 12.612792968749986],\n                            [14.184863281250017, 12.447216796874997],\n                            [14.272851562500023, 12.356494140624989],\n                            [14.518945312500051, 12.298242187500023],\n                            [14.619726562500063, 12.150976562500048],\n                            [14.559765625000011, 11.492285156249963],\n                            [14.20234375000004, 11.268164062499963],\n                            [14.143261718750068, 11.248535156250043],\n                            [14.056738281250034, 11.245019531250037],\n                            [13.981445312500057, 11.21186523437504],\n                            [13.892089843750057, 11.140087890624983],\n                            [13.699902343749983, 10.873144531250048],\n                            [13.53535156250004, 10.605078124999963],\n                            [13.414550781250028, 10.171435546874989],\n                            [13.269921875000051, 10.036181640624974],\n                            [13.198730468750028, 9.563769531250003],\n                            [12.929492187500074, 9.426269531249972],\n                            [12.87568359375004, 9.303515625000017],\n                            [12.80654296875008, 8.886621093749994],\n                            [12.7822265625, 8.817871093750014],\n                            [12.651562500000011, 8.667773437499989],\n                            [12.40351562500004, 8.59555664062502],\n                            [12.311328125000074, 8.419726562499989],\n                            [12.2333984375, 8.282324218749977],\n                            [12.016015625000051, 7.589746093750009],\n                            [11.809179687500006, 7.345068359374991],\n                            [11.767382812500017, 7.272265624999989],\n                            [11.861425781249977, 7.11640625000004],\n                            [11.657519531250017, 6.951562500000023],\n                            [11.580078125000057, 6.88886718750004],\n                            [11.551660156250023, 6.697265625],\n                            [11.153320312500057, 6.437939453125011],\n                            [11.1064453125, 6.457714843750054],\n                            [11.032519531250045, 6.697900390625037],\n                            [10.954199218750006, 6.7765625],\n                            [10.60625, 7.063085937500006],\n                            [10.413183593750006, 6.877734375],\n                            [10.293066406250034, 6.876757812499974],\n                            [10.205468750000051, 6.891601562499986],\n                            [10.185546874999972, 6.91279296875004],\n                            [10.167773437500017, 6.959179687499983],\n                            [10.143554687500057, 6.99643554687502],\n                            [10.038867187500045, 6.921386718750014],\n                            [9.874218750000068, 6.803271484375017],\n                            [9.82070312500008, 6.783935546874986],\n                            [9.779882812500034, 6.760156250000023],\n                            [9.725585937499972, 6.65],\n                            [9.659960937500017, 6.531982421874986],\n                            [9.490234375, 6.418652343749997],\n                            [8.997167968750006, 5.917724609375],\n                            [8.715625, 5.046875],\n                            [8.514843750000068, 4.724707031250034],\n                            [8.23378906250008, 4.907470703124972],\n                            [8.293066406250006, 4.557617187500014],\n                            [7.644238281250068, 4.525341796875011],\n                            [7.530761718750028, 4.655175781249994],\n                            [7.284375, 4.547656250000031],\n                            [7.076562500000051, 4.716162109374991],\n                            [7.15468750000008, 4.514404296875],\n                            [6.92324218750008, 4.390673828125017],\n                            [6.767675781250006, 4.724707031250034],\n                            [6.860351562500057, 4.373339843750045],\n                            [6.633007812500011, 4.340234375000051],\n                            [6.579980468750051, 4.475976562499994],\n                            [6.554589843750023, 4.34140625000002],\n                            [6.263671875, 4.309423828124991],\n                            [6.270996093749972, 4.432128906250028],\n                            [6.173339843749972, 4.277392578125031],\n                            [5.970703125, 4.338574218749983],\n                            [5.587792968750051, 4.647216796874972],\n                            [5.448144531250023, 4.945849609374974],\n                            [5.383300781250057, 5.129003906249977],\n                            [5.475976562500023, 5.153857421874989],\n                            [5.370019531250023, 5.195019531250026],\n                            [5.367968750000045, 5.337744140624963],\n                            [5.549707031250023, 5.474218749999963],\n                            [5.385839843750034, 5.401757812500037],\n                            [5.199218750000028, 5.533544921874977],\n                            [5.456640624999977, 5.61171875],\n                            [5.327343750000011, 5.707519531249986],\n                            [5.112402343750034, 5.64155273437504],\n                            [4.861035156250068, 6.026318359374997],\n                            [4.431347656250011, 6.348583984375026],\n                            [3.450781249999977, 6.427050781250017],\n                            [3.71699218750004, 6.597949218750017],\n                            [3.430175781250057, 6.525],\n                            [3.335546875000063, 6.396923828125011],\n                            [2.706445312500051, 6.369238281249963],\n                            [2.735644531250045, 6.595703125],\n                            [2.753710937499989, 6.661767578124966],\n                            [2.774609374999983, 6.711718750000017],\n                            [2.752929687500028, 6.771630859374966],\n                            [2.731738281250045, 6.852832031249989],\n                            [2.721386718750068, 6.980273437500017],\n                            [2.75673828125008, 7.067919921875017],\n                            [2.750488281250057, 7.39506835937496],\n                            [2.765820312500068, 7.422509765625051],\n                            [2.783984375000045, 7.443408203125045],\n                            [2.78515625, 7.476855468750017],\n                            [2.703125, 8.371826171875],\n                            [2.774804687500023, 9.048535156250026],\n                            [3.044921875, 9.08383789062502],\n                            [3.325195312499972, 9.778466796875051],\n                            [3.60205078125, 10.004541015625009],\n                            [3.646582031250006, 10.408984374999989],\n                            [3.771777343750017, 10.417626953124966],\n                            [3.83447265625, 10.607421875],\n                            [3.7568359375, 10.76875],\n                            [3.71640625, 11.07958984375],\n                            [3.695312499999972, 11.12031250000004],\n                            [3.63886718750004, 11.176855468750006],\n                            [3.487792968749972, 11.395410156250037],\n                            [3.490527343750017, 11.499218750000054],\n                            [3.55390625000004, 11.631884765624989],\n                            [3.595410156250068, 11.696289062500057],\n                            [3.664746093750068, 11.762451171875028],\n                            [3.646679687500011, 12.529980468749983],\n                            [3.947851562500006, 12.775048828124994],\n                            [4.147558593750006, 13.457714843749983],\n                            [4.664843750000045, 13.733203124999974],\n                            [5.241894531250011, 13.757226562499994],\n                            [5.361621093750074, 13.836865234375054],\n                            [5.415820312500017, 13.859179687499974],\n                            [5.491992187500074, 13.872851562500003],\n                            [6.2998046875, 13.658789062500006],\n                            [6.804296875, 13.107666015625]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Nigeria\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-83.6419921875, 10.917236328125],\n                        [-83.71293945312499, 10.785888671875],\n                        [-83.91928710937499, 10.7353515625],\n                        [-84.6341796875, 11.045605468749997],\n                        [-84.9091796875, 10.9453125],\n                        [-85.5841796875, 11.189453125],\n                        [-85.7443359375, 11.06210937499999],\n                        [-87.670166015625, 12.965673828124991],\n                        [-87.58505859374999, 13.043310546874991],\n                        [-87.42436523437499, 12.921142578125],\n                        [-87.33725585937499, 12.979248046875],\n                        [-87.05917968749999, 12.991455078125],\n                        [-87.00932617187499, 13.0078125],\n                        [-86.918212890625, 13.223583984374997],\n                        [-86.87353515625, 13.266503906249994],\n                        [-86.792138671875, 13.27978515625],\n                        [-86.72929687499999, 13.284375],\n                        [-86.710693359375, 13.313378906249994],\n                        [-86.76352539062499, 13.63525390625],\n                        [-86.77060546874999, 13.69873046875],\n                        [-86.758984375, 13.746142578125003],\n                        [-86.733642578125, 13.763476562500003],\n                        [-86.61025390625, 13.774853515624997],\n                        [-86.376953125, 13.755664062500003],\n                        [-86.33173828125, 13.770068359375003],\n                        [-86.238232421875, 13.899462890625003],\n                        [-86.15122070312499, 13.994580078124997],\n                        [-86.0892578125, 14.037207031249991],\n                        [-86.04038085937499, 14.050146484374991],\n                        [-85.9837890625, 13.965673828124991],\n                        [-85.78671875, 13.844433593749997],\n                        [-85.75341796875, 13.85205078125],\n                        [-85.73393554687499, 13.858691406250003],\n                        [-85.727734375, 13.876074218749991],\n                        [-85.731201171875, 13.931835937499997],\n                        [-85.68193359374999, 13.982568359374994],\n                        [-85.20834960937499, 14.311816406250003],\n                        [-85.059375, 14.582958984374997],\n                        [-84.86044921874999, 14.809765625],\n                        [-84.645947265625, 14.661083984374997],\n                        [-84.53764648437499, 14.633398437499991],\n                        [-83.635498046875, 14.876416015624997],\n                        [-83.5365234375, 14.977001953124997],\n                        [-83.4150390625, 15.008056640625],\n                        [-83.15751953124999, 14.993066406249994],\n                        [-83.18535156249999, 14.956396484374991],\n                        [-83.21591796874999, 14.932373046875],\n                        [-83.27988281249999, 14.812792968750003],\n                        [-83.344384765625, 14.902099609375],\n                        [-83.413720703125, 14.825341796874994],\n                        [-83.29921875, 14.7490234375],\n                        [-83.187744140625, 14.340087890625],\n                        [-83.4123046875, 13.99648437499999],\n                        [-83.567333984375, 13.3203125],\n                        [-83.5109375, 12.411816406249997],\n                        [-83.627197265625, 12.459326171874991],\n                        [-83.59335937499999, 12.713085937499997],\n                        [-83.75424804687499, 12.501953125],\n                        [-83.680419921875, 12.024316406249994],\n                        [-83.7671875, 12.059277343749997],\n                        [-83.82890624999999, 11.861035156249997],\n                        [-83.70458984375, 11.824560546874991],\n                        [-83.6517578125, 11.642041015624997],\n                        [-83.86787109375, 11.300048828125],\n                        [-83.6419921875, 10.917236328125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Nicaragua\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-169.80341796875, -19.0830078125],\n                        [-169.94833984375, -19.072851562500006],\n                        [-169.834033203125, -18.96601562500001],\n                        [-169.80341796875, -19.0830078125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Niue\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-68.205810546875, 12.144580078124989],\n                            [-68.25434570312495, 12.032080078124977],\n                            [-68.36923828125, 12.301953124999983],\n                            [-68.205810546875, 12.144580078124989]\n                        ]\n                    ],\n                    [\n                        [\n                            [4.226171875000034, 51.38647460937503],\n                            [3.902050781250011, 51.20766601562502],\n                            [3.43251953125008, 51.24575195312505],\n                            [3.35009765625, 51.37768554687503],\n                            [4.226171875000034, 51.38647460937503]\n                        ]\n                    ],\n                    [\n                        [\n                            [3.94912109375008, 51.73945312500001],\n                            [4.07509765625008, 51.648779296875006],\n                            [3.699023437500017, 51.70991210937501],\n                            [3.94912109375008, 51.73945312500001]\n                        ]\n                    ],\n                    [\n                        [\n                            [4.886132812500023, 53.07070312500005],\n                            [4.70917968750004, 53.036035156249994],\n                            [4.886425781249983, 53.18330078124998],\n                            [4.886132812500023, 53.07070312500005]\n                        ]\n                    ],\n                    [\n                        [\n                            [4.226171875000034, 51.38647460937503],\n                            [3.448925781250068, 51.54077148437503],\n                            [3.743945312500017, 51.596044921875006],\n                            [4.27412109375004, 51.47163085937498],\n                            [4.004785156250051, 51.595849609374966],\n                            [4.182617187500057, 51.61030273437498],\n                            [3.946875, 51.810546875],\n                            [4.482812500000023, 52.30917968749998],\n                            [4.76875, 52.941308593749966],\n                            [5.061230468750068, 52.96064453125001],\n                            [5.532031250000074, 53.268701171874966],\n                            [6.062207031250068, 53.407080078125006],\n                            [6.816210937500045, 53.44116210937503],\n                            [7.197265625000028, 53.28227539062499],\n                            [7.033007812500045, 52.65136718749997],\n                            [6.710742187500045, 52.61787109374998],\n                            [6.748828125000074, 52.464013671874994],\n                            [7.035156250000057, 52.38022460937498],\n                            [6.724511718749994, 52.080224609374966],\n                            [6.800390625, 51.96738281249998],\n                            [5.948730468750057, 51.80268554687501],\n                            [6.198828125000034, 51.45],\n                            [6.129980468750034, 51.14741210937501],\n                            [5.857519531250034, 51.030126953125006],\n                            [6.048437500000034, 50.90488281250006],\n                            [5.993945312500017, 50.75043945312504],\n                            [5.693554687500011, 50.774755859375006],\n                            [5.796484375000034, 51.153076171875],\n                            [5.214160156250045, 51.278955078124966],\n                            [5.03095703125004, 51.46909179687498],\n                            [4.226171875000034, 51.38647460937503]\n                        ]\n                    ],\n                    [\n                        [\n                            [5.325781250000063, 53.38574218750003],\n                            [5.190234375000074, 53.39179687500001],\n                            [5.582617187500063, 53.438085937500034],\n                            [5.325781250000063, 53.38574218750003]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Netherlands\", \"childNum\": 6, \"cp\": [5.0752777, 52.358465] }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [5.085839843750023, 60.30756835937501],\n                            [5.089062500000068, 60.188769531250045],\n                            [4.95722656250004, 60.44726562500006],\n                            [5.085839843750023, 60.30756835937501]\n                        ]\n                    ],\n                    [\n                        [\n                            [4.958691406250068, 61.084570312500034],\n                            [4.79902343750004, 61.08271484375001],\n                            [4.861621093749989, 61.19384765625],\n                            [4.958691406250068, 61.084570312500034]\n                        ]\n                    ],\n                    [\n                        [\n                            [8.10273437500004, 63.33759765625004],\n                            [7.804003906250017, 63.413916015625034],\n                            [8.073535156250045, 63.47080078124998],\n                            [8.10273437500004, 63.33759765625004]\n                        ]\n                    ],\n                    [\n                        [\n                            [8.470800781250063, 63.66713867187502],\n                            [8.287109375000028, 63.68715820312502],\n                            [8.764648437500057, 63.804638671874955],\n                            [8.78652343750008, 63.703466796875034],\n                            [8.470800781250063, 63.66713867187502]\n                        ]\n                    ],\n                    [\n                        [\n                            [11.2314453125, 64.865869140625],\n                            [10.739843750000034, 64.87031250000001],\n                            [11.02099609375, 64.97871093749995],\n                            [11.2314453125, 64.865869140625]\n                        ]\n                    ],\n                    [\n                        [\n                            [12.971777343750063, 67.87412109375],\n                            [12.824023437500074, 67.82124023437498],\n                            [13.068066406250068, 68.07133789062505],\n                            [12.971777343750063, 67.87412109375]\n                        ]\n                    ],\n                    [\n                        [\n                            [13.872851562500045, 68.26533203125004],\n                            [14.096777343750034, 68.218603515625],\n                            [13.229394531250051, 67.995361328125],\n                            [13.300195312499994, 68.16044921875007],\n                            [13.872851562500045, 68.26533203125004]\n                        ]\n                    ],\n                    [\n                        [\n                            [15.207128906250006, 68.943115234375],\n                            [15.222070312500023, 68.61630859375003],\n                            [14.404687500000051, 68.663232421875],\n                            [15.037792968750068, 69.00053710937507],\n                            [15.207128906250006, 68.943115234375]\n                        ]\n                    ],\n                    [\n                        [\n                            [15.760351562500006, 68.56123046875001],\n                            [16.328906250000017, 68.87631835937498],\n                            [16.519238281250068, 68.63300781249998],\n                            [15.975292968750011, 68.402490234375],\n                            [14.257519531249983, 68.19077148437503],\n                            [15.412597656250028, 68.61582031250003],\n                            [15.483007812500006, 69.04345703125003],\n                            [16.04804687500001, 69.30205078125002],\n                            [15.760351562500006, 68.56123046875001]\n                        ]\n                    ],\n                    [\n                        [\n                            [17.503027343750034, 69.59624023437502],\n                            [18.004101562500068, 69.50498046874998],\n                            [17.95068359375003, 69.19814453125],\n                            [17.487890625000063, 69.19682617187499],\n                            [17.08251953124997, 69.013671875],\n                            [16.81044921875008, 69.07070312499997],\n                            [17.001757812500045, 69.36191406250006],\n                            [17.36083984375003, 69.38149414062497],\n                            [17.503027343750034, 69.59624023437502]\n                        ]\n                    ],\n                    [\n                        [\n                            [29.956152343750006, 69.79677734375002],\n                            [29.766210937500006, 69.76752929687501],\n                            [29.835839843749994, 69.90556640625005],\n                            [29.956152343750006, 69.79677734375002]\n                        ]\n                    ],\n                    [\n                        [\n                            [20.779199218750023, 70.08974609375002],\n                            [20.46425781250005, 70.0765625],\n                            [20.492773437500006, 70.20332031249995],\n                            [20.78603515625008, 70.21953124999999],\n                            [20.779199218750023, 70.08974609375002]\n                        ]\n                    ],\n                    [\n                        [\n                            [19.25507812500001, 70.06640625000006],\n                            [19.607812500000023, 70.019140625],\n                            [19.334765625000074, 69.82026367187501],\n                            [18.784765625000034, 69.57900390624997],\n                            [18.12988281250003, 69.557861328125],\n                            [18.34931640625004, 69.76787109374999],\n                            [18.67402343750004, 69.78164062500002],\n                            [19.13271484375005, 70.24414062500003],\n                            [19.25507812500001, 70.06640625000006]\n                        ]\n                    ],\n                    [\n                        [\n                            [19.76748046875005, 70.21669921875002],\n                            [20.005957031250034, 70.07622070312502],\n                            [19.599023437499994, 70.26616210937507],\n                            [19.76748046875005, 70.21669921875002]\n                        ]\n                    ],\n                    [\n                        [\n                            [23.615332031250034, 70.54931640625003],\n                            [23.15917968750003, 70.28261718750005],\n                            [22.941015625000063, 70.444580078125],\n                            [23.546679687500017, 70.61708984374997],\n                            [23.615332031250034, 70.54931640625003]\n                        ]\n                    ],\n                    [\n                        [\n                            [24.01757812500003, 70.56738281249997],\n                            [23.716601562500074, 70.561865234375],\n                            [23.778417968750063, 70.74736328125005],\n                            [24.01757812500003, 70.56738281249997]\n                        ]\n                    ],\n                    [\n                        [\n                            [23.440527343750063, 70.81577148437503],\n                            [22.8291015625, 70.54155273437505],\n                            [22.358691406250017, 70.514794921875],\n                            [21.99453125000008, 70.65712890624997],\n                            [23.440527343750063, 70.81577148437503]\n                        ]\n                    ],\n                    [\n                        [\n                            [30.869726562500006, 69.78344726562506],\n                            [30.860742187499994, 69.53842773437503],\n                            [30.18017578124997, 69.63583984375],\n                            [30.08730468750005, 69.43286132812503],\n                            [29.38828125, 69.29814453125005],\n                            [28.96582031250003, 69.02197265625],\n                            [28.846289062500006, 69.17690429687502],\n                            [29.33339843750005, 69.47299804687503],\n                            [29.14160156250003, 69.67143554687505],\n                            [27.747851562500045, 70.06484375],\n                            [27.127539062500063, 69.90649414062497],\n                            [26.525390625000057, 69.91503906250003],\n                            [26.07246093750004, 69.69155273437497],\n                            [25.748339843750017, 68.99013671875],\n                            [24.94140625000003, 68.59326171875006],\n                            [23.85400390625, 68.80590820312503],\n                            [23.324023437500017, 68.64897460937502],\n                            [22.410937500000074, 68.719873046875],\n                            [21.59375, 69.273583984375],\n                            [21.06611328125001, 69.21411132812497],\n                            [21.065722656250017, 69.04174804687503],\n                            [20.622167968750006, 69.036865234375],\n                            [20.116699218750057, 69.02089843750005],\n                            [20.348046875000023, 68.84873046875003],\n                            [19.969824218750063, 68.35639648437501],\n                            [18.303027343750045, 68.55541992187497],\n                            [17.91669921875001, 67.96489257812502],\n                            [17.324609375000023, 68.10380859374999],\n                            [16.783593750000023, 67.89501953125],\n                            [16.12744140625, 67.42583007812507],\n                            [16.40351562500004, 67.05498046875002],\n                            [15.422949218750006, 66.48984374999998],\n                            [15.483789062500051, 66.30595703124999],\n                            [14.543261718750045, 66.12934570312498],\n                            [14.47968750000004, 65.30146484374998],\n                            [13.650292968750023, 64.58154296874997],\n                            [14.077636718750028, 64.464013671875],\n                            [14.141210937500006, 64.17353515624998],\n                            [13.960546875000063, 64.01401367187498],\n                            [13.203515625000023, 64.07509765625],\n                            [12.792773437500017, 64],\n                            [12.175195312500051, 63.595947265625],\n                            [11.999902343750051, 63.29169921875001],\n                            [12.303515625000074, 62.28559570312501],\n                            [12.155371093750006, 61.720751953125045],\n                            [12.88076171875008, 61.35229492187506],\n                            [12.706054687500028, 61.059863281250074],\n                            [12.29414062500004, 61.00268554687506],\n                            [12.588671874999989, 60.450732421875045],\n                            [12.486132812500074, 60.10678710937506],\n                            [11.680761718750034, 59.59228515625003],\n                            [11.798144531250074, 59.28989257812498],\n                            [11.64277343750004, 58.92607421875002],\n                            [11.470703125000057, 58.909521484375034],\n                            [11.388281250000063, 59.036523437499966],\n                            [10.834472656250028, 59.18393554687498],\n                            [10.595312500000063, 59.764550781249966],\n                            [10.179394531250068, 59.00927734375003],\n                            [9.842578125000017, 58.95849609374997],\n                            [9.557226562500063, 59.11269531250002],\n                            [9.65693359375004, 58.97119140624997],\n                            [8.166113281250063, 58.145312500000045],\n                            [7.0048828125, 58.024218750000074],\n                            [6.877050781250006, 58.15073242187498],\n                            [6.590527343750068, 58.09731445312502],\n                            [6.659863281250068, 58.26274414062499],\n                            [5.706835937500074, 58.52363281250001],\n                            [5.55556640625008, 58.975195312500006],\n                            [6.099023437500023, 58.87026367187502],\n                            [6.363281250000028, 59.00092773437501],\n                            [6.099414062500017, 58.951953125000074],\n                            [5.88916015625, 59.097949218750045],\n                            [5.951855468750068, 59.299072265625],\n                            [6.415332031250074, 59.547119140625],\n                            [5.17324218750008, 59.16254882812498],\n                            [5.2421875, 59.564306640625034],\n                            [5.472460937500017, 59.713085937499955],\n                            [5.77216796875004, 59.66093749999999],\n                            [6.216601562499989, 59.818359375],\n                            [5.73046875, 59.863085937500045],\n                            [6.348730468750006, 60.35297851562504],\n                            [6.57363281250008, 60.36059570312497],\n                            [6.526855468750057, 60.152929687500034],\n                            [6.995703125, 60.511962890625],\n                            [6.1533203125, 60.34624023437499],\n                            [5.145800781250074, 59.63881835937502],\n                            [5.205664062500006, 60.087939453125045],\n                            [5.688574218749977, 60.12319335937502],\n                            [5.285839843750011, 60.20571289062505],\n                            [5.13710937500008, 60.445605468750074],\n                            [5.648339843750051, 60.68798828124997],\n                            [5.244042968750023, 60.569580078125],\n                            [5.115820312500006, 60.63598632812503],\n                            [5.008593750000017, 61.038183593750006],\n                            [6.777832031250028, 61.142431640625006],\n                            [7.038671875000063, 60.952929687500045],\n                            [7.040136718750006, 61.091162109375034],\n                            [7.604492187500057, 61.210546875000034],\n                            [7.34658203125008, 61.30058593749999],\n                            [7.442578125000011, 61.43461914062502],\n                            [7.173535156250011, 61.16596679687501],\n                            [6.599902343750017, 61.28964843749998],\n                            [6.383496093750068, 61.133886718750034],\n                            [5.451269531250034, 61.10234375000002],\n                            [5.106738281250017, 61.187548828125045],\n                            [5.002734375000074, 61.43359375],\n                            [5.338671875000017, 61.485498046874994],\n                            [4.927832031249977, 61.71069335937506],\n                            [4.93007812499999, 61.878320312499994],\n                            [6.01582031250004, 61.7875],\n                            [6.730761718750045, 61.86977539062505],\n                            [5.266894531250045, 61.935595703125045],\n                            [5.143164062500063, 62.159912109375],\n                            [5.908300781249977, 62.41601562500003],\n                            [6.083496093750057, 62.349609375],\n                            [6.580078125000057, 62.407275390625045],\n                            [6.692382812500028, 62.46806640624999],\n                            [6.136132812500051, 62.40747070312497],\n                            [6.352929687500051, 62.61113281249999],\n                            [7.653125, 62.56401367187499],\n                            [7.538378906250074, 62.67207031249998],\n                            [8.045507812500006, 62.77124023437503],\n                            [6.734960937500006, 62.72070312500003],\n                            [6.940429687500028, 62.930468750000045],\n                            [7.571875, 63.09951171875002],\n                            [8.100585937500028, 63.090966796874966],\n                            [8.623144531250006, 62.84624023437502],\n                            [8.158007812500017, 63.16152343750005],\n                            [8.635546875000045, 63.34233398437502],\n                            [8.360742187500023, 63.498876953125034],\n                            [8.576171875000028, 63.60117187499998],\n                            [9.135839843750006, 63.593652343749966],\n                            [9.156054687500045, 63.459326171875034],\n                            [9.696875, 63.624560546875045],\n                            [10.020996093750028, 63.39082031250004],\n                            [10.76015625000008, 63.461279296875006],\n                            [10.725292968750068, 63.625],\n                            [11.370703125000034, 63.804833984374994],\n                            [11.175585937500074, 63.89887695312498],\n                            [11.457617187500063, 64.00297851562505],\n                            [11.306640625000028, 64.04887695312499],\n                            [10.91425781250004, 63.92109374999998],\n                            [10.934863281250045, 63.770214843749955],\n                            [10.055078125000051, 63.5126953125],\n                            [9.567285156250051, 63.70615234374998],\n                            [10.565625, 64.418310546875],\n                            [11.523828125000051, 64.744384765625],\n                            [11.632910156250063, 64.81391601562495],\n                            [11.296777343750051, 64.75478515625],\n                            [11.489355468750034, 64.975830078125],\n                            [12.15966796875, 65.178955078125],\n                            [12.508398437499977, 65.09941406250005],\n                            [12.915527343750057, 65.33925781249997],\n                            [12.417578125000063, 65.18408203124997],\n                            [12.133886718749977, 65.27915039062498],\n                            [12.68886718750008, 65.90219726562498],\n                            [13.033105468750051, 65.95625],\n                            [12.783789062500063, 66.10043945312506],\n                            [14.034179687500057, 66.29755859374998],\n                            [13.118847656250011, 66.23066406250004],\n                            [13.211425781250028, 66.64082031250001],\n                            [13.959472656250028, 66.79433593750002],\n                            [13.651562500000011, 66.90708007812498],\n                            [14.10878906250008, 67.11923828125003],\n                            [15.41572265625004, 67.20244140625002],\n                            [14.441699218750045, 67.27138671875005],\n                            [14.961914062500057, 67.57426757812502],\n                            [15.59443359375004, 67.34853515625005],\n                            [15.691503906250006, 67.52138671875],\n                            [15.24873046875004, 67.6021484375],\n                            [15.303906250000011, 67.76528320312502],\n                            [14.854687500000068, 67.66333007812506],\n                            [14.798925781250063, 67.80932617187503],\n                            [15.13427734375, 67.97270507812502],\n                            [15.621386718750017, 67.94829101562502],\n                            [15.316015624999977, 68.06875],\n                            [16.007910156250006, 68.22871093750004],\n                            [16.312304687500017, 67.88144531249998],\n                            [16.20380859375001, 68.31674804687503],\n                            [17.552832031250063, 68.42626953125006],\n                            [16.51435546875004, 68.53256835937503],\n                            [18.101464843749994, 69.15629882812499],\n                            [18.259765625, 69.47060546875],\n                            [18.915917968750023, 69.33559570312502],\n                            [18.614453125000068, 69.49057617187498],\n                            [19.197265625000057, 69.74785156249999],\n                            [19.722460937500017, 69.78164062500002],\n                            [19.64150390625005, 69.42402343750001],\n                            [20.324218750000057, 69.94531249999997],\n                            [20.054492187500074, 69.33266601562497],\n                            [20.486718750000023, 69.54208984375],\n                            [20.739453124999983, 69.52050781250003],\n                            [20.622070312500057, 69.91391601562498],\n                            [21.163085937500057, 69.88950195312498],\n                            [21.432910156250045, 70.01318359375006],\n                            [21.974707031250034, 69.83457031249998],\n                            [21.355761718750045, 70.23339843749997],\n                            [22.321972656250068, 70.264501953125],\n                            [22.684570312500057, 70.374755859375],\n                            [23.35390625000008, 69.98339843750003],\n                            [23.3291015625, 70.20722656249995],\n                            [24.420019531250034, 70.70200195312503],\n                            [24.263476562500017, 70.82631835937497],\n                            [24.658007812500017, 71.00102539062505],\n                            [25.264648437500057, 70.843505859375],\n                            [25.768164062500063, 70.85317382812502],\n                            [25.043847656250023, 70.10903320312502],\n                            [26.66132812500004, 70.93974609374999],\n                            [26.585058593750034, 70.41000976562498],\n                            [26.989355468750063, 70.51137695312502],\n                            [27.183691406250034, 70.74404296875],\n                            [27.546484375000063, 70.80400390625005],\n                            [27.23525390625008, 70.94721679687498],\n                            [27.59707031250005, 71.09130859375003],\n                            [28.392285156250068, 70.97529296875004],\n                            [27.898046875, 70.67792968750001],\n                            [28.271777343750017, 70.66796875000003],\n                            [28.192968750000034, 70.24858398437505],\n                            [28.83154296875003, 70.86396484375001],\n                            [29.7375, 70.646826171875],\n                            [30.065136718750097, 70.70297851562498],\n                            [30.944140625000017, 70.27441406249997],\n                            [30.262988281250074, 70.12470703125004],\n                            [28.804296875000063, 70.09252929687506],\n                            [29.601367187500017, 69.97675781249998],\n                            [29.792089843750063, 69.727880859375],\n                            [30.08828125, 69.71757812500005],\n                            [30.237597656250017, 69.86220703125002],\n                            [30.428320312500006, 69.722265625],\n                            [30.869726562500006, 69.78344726562506]\n                        ]\n                    ],\n                    [\n                        [\n                            [25.58632812500005, 71.14208984375],\n                            [26.13378906250003, 70.99580078125004],\n                            [25.582031250000057, 70.960791015625],\n                            [25.31494140625, 71.03413085937504],\n                            [25.58632812500005, 71.14208984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-8.953564453124983, 70.83916015625002],\n                            [-8.001367187499966, 71.17768554687495],\n                            [-8.002099609374937, 71.04125976562497],\n                            [-8.953564453124983, 70.83916015625002]\n                        ]\n                    ],\n                    [\n                        [\n                            [19.219335937500006, 74.39101562500002],\n                            [18.86123046875008, 74.51416015624997],\n                            [19.182910156250045, 74.51791992187503],\n                            [19.219335937500006, 74.39101562500002]\n                        ]\n                    ],\n                    [\n                        [\n                            [21.60810546875004, 78.59570312499997],\n                            [22.04316406250004, 78.57695312500007],\n                            [22.29951171875004, 78.22817382812497],\n                            [23.451953125000074, 78.14946289062502],\n                            [23.11669921874997, 77.99150390624999],\n                            [24.901855468750057, 77.756591796875],\n                            [22.55371093750003, 77.26665039062502],\n                            [22.685351562500045, 77.55351562500002],\n                            [20.928125, 77.45966796874998],\n                            [21.653125, 77.92353515624998],\n                            [20.22792968750005, 78.47783203125005],\n                            [21.60810546875004, 78.59570312499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [11.250292968750017, 78.610693359375],\n                            [12.116406250000068, 78.232568359375],\n                            [11.121289062500011, 78.46328125],\n                            [10.558203125000063, 78.90292968750003],\n                            [11.250292968750017, 78.610693359375]\n                        ]\n                    ],\n                    [\n                        [\n                            [29.047070312500068, 78.91206054687504],\n                            [29.69667968750005, 78.90473632812495],\n                            [27.88906250000005, 78.8521484375],\n                            [28.511132812500023, 78.96733398437502],\n                            [29.047070312500068, 78.91206054687504]\n                        ]\n                    ],\n                    [\n                        [\n                            [16.786718750000034, 79.90673828125],\n                            [17.834570312499977, 79.80004882812503],\n                            [17.66875, 79.38593750000004],\n                            [18.39736328125008, 79.60517578125001],\n                            [18.677832031250006, 79.26171875000003],\n                            [19.893554687500057, 79.05620117187499],\n                            [20.61103515625004, 79.10664062499998],\n                            [21.388769531250034, 78.74042968749998],\n                            [19.67675781250003, 78.60957031249995],\n                            [16.700488281250045, 76.57929687499995],\n                            [14.365820312500034, 77.23447265625003],\n                            [13.995703125000034, 77.50820312500002],\n                            [14.69501953125004, 77.525048828125],\n                            [14.920800781250023, 77.68881835937506],\n                            [17.033300781250006, 77.79770507812503],\n                            [16.91406250000003, 77.89799804687505],\n                            [14.089941406250063, 77.77138671875],\n                            [13.680566406250051, 78.028125],\n                            [14.307226562500006, 78.00507812500001],\n                            [15.783886718750011, 78.32705078125005],\n                            [17.00292968750003, 78.36938476562497],\n                            [16.44863281250008, 78.50356445312502],\n                            [16.78261718750008, 78.66362304687505],\n                            [15.417382812500023, 78.47324218749998],\n                            [15.384179687500023, 78.77119140625001],\n                            [15.01630859375004, 78.63012695312497],\n                            [14.689257812500017, 78.720947265625],\n                            [14.638281250000034, 78.41459960937502],\n                            [14.110449218750063, 78.27089843749997],\n                            [13.150195312499989, 78.2375],\n                            [11.365429687500011, 78.95039062500004],\n                            [12.323437500000068, 78.91425781249995],\n                            [12.083984375000028, 79.26752929687498],\n                            [11.579785156250068, 79.28349609375005],\n                            [11.208105468750034, 79.12963867187503],\n                            [10.737597656250017, 79.52016601562502],\n                            [10.804003906250045, 79.79877929687504],\n                            [11.150390625, 79.71699218749998],\n                            [11.702343750000011, 79.82060546875005],\n                            [12.287792968750068, 79.713134765625],\n                            [12.279980468749983, 79.81596679687507],\n                            [13.692871093749972, 79.860986328125],\n                            [13.777539062500011, 79.71528320312498],\n                            [12.555371093750068, 79.56948242187502],\n                            [13.333789062500017, 79.57480468750006],\n                            [14.029589843750017, 79.34414062500005],\n                            [14.59365234375008, 79.79873046875002],\n                            [16.34375, 78.97612304687502],\n                            [15.816113281250011, 79.68183593750001],\n                            [16.245703125000034, 80.04946289062502],\n                            [16.786718750000034, 79.90673828125]\n                        ]\n                    ],\n                    [\n                        [\n                            [32.52597656250006, 80.119140625],\n                            [31.48193359374997, 80.10791015625003],\n                            [33.62929687499999, 80.21743164062497],\n                            [32.52597656250006, 80.119140625]\n                        ]\n                    ],\n                    [\n                        [\n                            [20.897851562500023, 80.24995117187501],\n                            [22.289746093749983, 80.04921874999997],\n                            [22.450781250000034, 80.40224609375005],\n                            [23.00800781250004, 80.473974609375],\n                            [23.114550781250074, 80.18696289062498],\n                            [24.29755859375004, 80.36040039062505],\n                            [26.86083984375, 80.16000976562498],\n                            [27.19863281250008, 79.90659179687506],\n                            [25.641210937500034, 79.40302734374995],\n                            [23.94775390625, 79.19428710937498],\n                            [22.903710937500023, 79.23066406250001],\n                            [22.865527343750045, 79.41186523437497],\n                            [20.861132812500017, 79.39785156249997],\n                            [20.128222656250074, 79.489599609375],\n                            [19.674609375000045, 79.591162109375],\n                            [20.784082031250023, 79.74858398437502],\n                            [18.725, 79.7607421875],\n                            [18.25537109375, 79.92919921875003],\n                            [18.855957031250057, 80.03662109375],\n                            [17.91689453125005, 80.14311523437502],\n                            [19.343359375000063, 80.11640624999998],\n                            [19.733300781249994, 80.47783203124999],\n                            [20.897851562500023, 80.24995117187501]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Norway\", \"childNum\": 27 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [87.984375, 27.133935546874994],\n                        [87.9931640625, 27.086083984374994],\n                        [88.11103515625001, 26.928466796875],\n                        [88.1572265625, 26.807324218749997],\n                        [88.16152343750002, 26.724804687499997],\n                        [88.11152343750001, 26.58642578125],\n                        [88.05488281250001, 26.430029296875],\n                        [88.02695312500003, 26.39501953125],\n                        [87.9951171875, 26.382373046874996],\n                        [87.28740234374999, 26.360302734374997],\n                        [87.01640624999999, 26.555419921875],\n                        [86.70136718750001, 26.43505859375],\n                        [86.00732421875, 26.649365234374997],\n                        [85.79453125000003, 26.604150390624994],\n                        [85.7373046875, 26.63974609375],\n                        [85.6484375, 26.829003906249994],\n                        [85.56845703125003, 26.83984375],\n                        [85.29296875, 26.741015625],\n                        [85.19179687500002, 26.766552734374997],\n                        [84.68535156249999, 27.041015625],\n                        [84.65380859375, 27.091699218749994],\n                        [84.65478515625, 27.203662109374996],\n                        [84.64072265625003, 27.249853515625],\n                        [84.61015624999999, 27.298681640625],\n                        [84.48085937500002, 27.348193359374996],\n                        [84.22978515624999, 27.42783203125],\n                        [84.09101562500001, 27.491357421874994],\n                        [83.82880859375001, 27.377832031249994],\n                        [83.74697265625002, 27.395947265624997],\n                        [83.55166015625002, 27.456347656249996],\n                        [83.44716796875002, 27.46533203125],\n                        [83.38398437500001, 27.44482421875],\n                        [83.36943359374999, 27.41025390625],\n                        [83.28974609375001, 27.370996093749994],\n                        [82.7333984375, 27.518994140624997],\n                        [82.71083984375002, 27.5966796875],\n                        [82.67734375000003, 27.6734375],\n                        [82.6298828125, 27.687060546874996],\n                        [82.45136718750001, 27.671826171874997],\n                        [82.28769531250003, 27.756542968749997],\n                        [82.11191406250003, 27.864941406249997],\n                        [82.03701171875002, 27.900585937499997],\n                        [81.98769531250002, 27.913769531249997],\n                        [81.94521484375002, 27.899267578125],\n                        [81.896875, 27.874462890624997],\n                        [81.85263671875003, 27.867089843749994],\n                        [81.1689453125, 28.335009765624996],\n                        [80.58701171875003, 28.649609375],\n                        [80.51787109374999, 28.665185546874994],\n                        [80.49580078125001, 28.635791015624996],\n                        [80.47910156250003, 28.604882812499994],\n                        [80.41855468750003, 28.612011718749997],\n                        [80.32480468750003, 28.66640625],\n                        [80.2265625, 28.723339843749997],\n                        [80.07070312500002, 28.83017578125],\n                        [80.05166015625002, 28.8703125],\n                        [80.08457031250003, 28.994189453124996],\n                        [80.13046875000003, 29.100390625],\n                        [80.16953125000003, 29.124316406249996],\n                        [80.23300781250003, 29.194628906249996],\n                        [80.25595703125003, 29.318017578124994],\n                        [80.2548828125, 29.42333984375],\n                        [80.31689453125, 29.572070312499996],\n                        [80.40185546875, 29.730273437499996],\n                        [80.54902343750001, 29.899804687499994],\n                        [80.81992187500003, 30.119335937499997],\n                        [80.84814453125, 30.13974609375],\n                        [80.90761718750002, 30.171923828124996],\n                        [80.96611328124999, 30.180029296875],\n                        [81.17714843750002, 30.039892578125],\n                        [81.25507812500001, 30.093310546874996],\n                        [81.41718750000001, 30.337597656249997],\n                        [81.64189453124999, 30.3875],\n                        [81.85488281250002, 30.36240234375],\n                        [82.04335937500002, 30.3267578125],\n                        [82.220703125, 30.063867187499994],\n                        [83.15546875000001, 29.612646484375],\n                        [83.58349609375, 29.18359375],\n                        [83.93593750000002, 29.279492187499997],\n                        [84.02197265625, 29.253857421874997],\n                        [84.10136718749999, 29.219970703125],\n                        [84.12783203125002, 29.156298828124996],\n                        [84.17558593749999, 29.036376953125],\n                        [84.22871093750001, 28.911767578124994],\n                        [84.796875, 28.560205078124994],\n                        [84.85507812500003, 28.553613281249994],\n                        [85.06914062499999, 28.609667968749996],\n                        [85.12636718750002, 28.60263671875],\n                        [85.15908203125002, 28.592236328124997],\n                        [85.16015625, 28.571875],\n                        [85.12148437500002, 28.484277343749994],\n                        [85.08857421875001, 28.372265625],\n                        [85.12246093750002, 28.315966796874996],\n                        [85.21210937500001, 28.292626953124994],\n                        [85.41064453125, 28.276025390624994],\n                        [85.67832031250003, 28.27744140625],\n                        [85.75947265625001, 28.220654296874997],\n                        [85.84023437500002, 28.1353515625],\n                        [85.92167968749999, 27.989697265624997],\n                        [85.9541015625, 27.92822265625],\n                        [85.99453125000002, 27.910400390625],\n                        [86.06416015625001, 27.934716796874994],\n                        [86.07871093750003, 28.08359375],\n                        [86.13701171874999, 28.11435546875],\n                        [86.21796875000001, 28.0220703125],\n                        [86.32861328125, 27.959521484374996],\n                        [86.40869140625, 27.928662109374997],\n                        [86.51689453124999, 27.963525390624994],\n                        [86.55449218749999, 28.085205078125],\n                        [86.61445312500001, 28.10302734375],\n                        [86.69052734375003, 28.094921875],\n                        [86.71962890625002, 28.070654296875],\n                        [86.75039062500002, 28.0220703125],\n                        [86.93378906250001, 27.968457031249997],\n                        [87.02011718750003, 27.928662109374997],\n                        [87.14140624999999, 27.838330078124997],\n                        [87.29072265625001, 27.821923828124994],\n                        [87.62255859375, 27.815185546875],\n                        [87.86074218750002, 27.886083984375],\n                        [88.10976562500002, 27.87060546875],\n                        [87.984375, 27.133935546874994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Nepal\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [169.17822265624997, -52.497265625],\n                            [169.12753906250006, -52.570312499999964],\n                            [169.02177734375002, -52.49541015624998],\n                            [169.17822265624997, -52.497265625]\n                        ]\n                    ],\n                    [\n                        [\n                            [166.22109375, -50.76152343749997],\n                            [166.2428710937501, -50.84570312499998],\n                            [165.88916015624997, -50.80771484374996],\n                            [166.10136718750002, -50.538964843750016],\n                            [166.26748046875005, -50.558593750000014],\n                            [166.22109375, -50.76152343749997]\n                        ]\n                    ],\n                    [\n                        [\n                            [168.14492187500005, -46.862207031249966],\n                            [168.04316406250004, -46.9326171875],\n                            [168.2409179687501, -47.070019531250026],\n                            [167.52197265624997, -47.258691406249994],\n                            [167.80078125000003, -46.90654296875002],\n                            [167.78398437500007, -46.699804687500006],\n                            [167.9557617187501, -46.69443359374998],\n                            [168.14492187500005, -46.862207031249966]\n                        ]\n                    ],\n                    [\n                        [\n                            [166.97949218749997, -45.17968750000003],\n                            [167.02265625000004, -45.299804687499986],\n                            [166.89267578125012, -45.24052734374999],\n                            [166.97949218749997, -45.17968750000003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-176.17763671874997, -43.74033203124998],\n                            [-176.38173828124997, -43.86679687499998],\n                            [-176.40737304687497, -43.7609375],\n                            [-176.516552734375, -43.78476562499996],\n                            [-176.33359375000003, -44.02529296875004],\n                            [-176.51552734374997, -44.11660156249998],\n                            [-176.62934570312495, -44.036132812500014],\n                            [-176.55512695312504, -43.85195312499998],\n                            [-176.84765625000003, -43.82392578125004],\n                            [-176.56611328124995, -43.717578125000045],\n                            [-176.17763671874997, -43.74033203124998]\n                        ]\n                    ],\n                    [\n                        [\n                            [173.91464843750018, -40.86367187500004],\n                            [173.78085937500012, -40.921777343749966],\n                            [173.964453125, -40.71298828124998],\n                            [173.91464843750018, -40.86367187500004]\n                        ]\n                    ],\n                    [\n                        [\n                            [173.11533203125006, -41.27929687499997],\n                            [173.94716796875005, -40.92412109375],\n                            [173.79785156250003, -41.271972656249986],\n                            [173.99941406250005, -40.99326171874996],\n                            [174.30253906249996, -41.019531249999986],\n                            [174.03857421875003, -41.24189453125],\n                            [174.37011718750009, -41.1037109375],\n                            [174.06933593750009, -41.42949218750002],\n                            [174.08369140625015, -41.67080078124998],\n                            [174.2831054687501, -41.740625],\n                            [173.22119140624997, -42.976562499999986],\n                            [172.62402343749997, -43.27246093749996],\n                            [172.73476562500005, -43.35478515625003],\n                            [172.52666015625002, -43.464746093749966],\n                            [172.69345703125006, -43.444335937499986],\n                            [172.80703125000005, -43.620996093749994],\n                            [173.07324218750003, -43.676171874999966],\n                            [173.065625, -43.87460937499998],\n                            [172.50273437500002, -43.84365234374998],\n                            [172.48037109375, -43.726660156250034],\n                            [172.29658203125004, -43.867871093750026],\n                            [172.035546875, -43.70175781250002],\n                            [172.17978515625006, -43.895996093749986],\n                            [171.24072265624997, -44.26416015625003],\n                            [171.14628906250002, -44.9123046875],\n                            [170.99902343750003, -44.91142578124999],\n                            [171.11328125000003, -45.03925781250001],\n                            [170.7005859375, -45.68427734374997],\n                            [170.77626953125005, -45.870898437499974],\n                            [170.4191406250001, -45.94101562499996],\n                            [169.68662109375006, -46.55166015625002],\n                            [169.34228515625003, -46.62050781250001],\n                            [168.38212890625007, -46.60537109374995],\n                            [168.1891601562501, -46.362207031249966],\n                            [167.8419921875001, -46.366210937499986],\n                            [167.539453125, -46.14853515624996],\n                            [167.36894531250007, -46.24150390624999],\n                            [166.73154296875006, -46.19785156249998],\n                            [166.91669921875004, -45.95722656249998],\n                            [166.64990234374997, -46.04169921875004],\n                            [166.71796875000004, -45.88935546875001],\n                            [166.49316406249997, -45.9638671875],\n                            [166.48828124999997, -45.83183593750002],\n                            [167.0033203125, -45.71210937500004],\n                            [166.79765625000002, -45.64560546874999],\n                            [166.99082031250012, -45.531738281249986],\n                            [166.73398437500012, -45.54355468749999],\n                            [166.74306640625, -45.46845703124997],\n                            [166.91992187499997, -45.40791015624998],\n                            [166.86923828125006, -45.31123046875],\n                            [167.15566406250005, -45.410937499999974],\n                            [167.23007812500012, -45.29033203125],\n                            [167.02587890624997, -45.12363281249998],\n                            [167.25947265625004, -45.08222656249997],\n                            [167.19453125000004, -44.963476562500034],\n                            [167.41074218750006, -44.82792968750003],\n                            [167.4662109375, -44.958300781250045],\n                            [167.48496093750006, -44.77138671874998],\n                            [167.78701171875, -44.59501953125002],\n                            [167.90898437500002, -44.66474609375001],\n                            [167.85654296875012, -44.50068359374998],\n                            [168.45742187500005, -44.030566406250045],\n                            [169.17890625000004, -43.9130859375],\n                            [169.16953125000006, -43.77705078125],\n                            [169.83388671875, -43.53701171875004],\n                            [170.24023437499997, -43.163867187500045],\n                            [170.39609375000012, -43.18222656249996],\n                            [170.30283203125012, -43.10761718750004],\n                            [170.61181640625003, -43.091796875000014],\n                            [170.5236328125001, -43.00898437500001],\n                            [170.6654296875, -42.961230468749974],\n                            [170.73525390625005, -43.029785156249986],\n                            [170.96992187500004, -42.71835937499996],\n                            [171.01171875000003, -42.88505859374999],\n                            [171.027734375, -42.696093750000045],\n                            [171.31337890625005, -42.460156250000026],\n                            [171.48623046875, -41.7947265625],\n                            [171.94804687500002, -41.53867187499996],\n                            [172.13945312500002, -40.947265625000014],\n                            [172.640625, -40.51826171875001],\n                            [172.94365234375007, -40.51875],\n                            [172.73261718750004, -40.54375],\n                            [172.70439453125002, -40.6677734375],\n                            [172.988671875, -40.84824218749999],\n                            [173.11533203125006, -41.27929687499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [175.54316406250015, -36.279296874999986],\n                            [175.34619140624997, -36.217773437499986],\n                            [175.3895507812501, -36.07773437499996],\n                            [175.54316406250015, -36.279296874999986]\n                        ]\n                    ],\n                    [\n                        [\n                            [173.26943359375, -34.93476562499998],\n                            [173.44785156250012, -34.844335937500034],\n                            [173.47265625000003, -34.94697265624998],\n                            [174.10400390625003, -35.14287109375002],\n                            [174.1431640625, -35.3],\n                            [174.32031250000003, -35.246679687500034],\n                            [174.58066406250018, -35.78554687500004],\n                            [174.39580078124996, -35.79736328124996],\n                            [174.8021484375, -36.30947265625001],\n                            [174.72246093750007, -36.84121093749998],\n                            [175.29951171875004, -36.99326171874996],\n                            [175.38535156250012, -37.206933593749966],\n                            [175.54248046874997, -37.2013671875],\n                            [175.46083984375005, -36.475683593750034],\n                            [175.77216796875004, -36.73515625],\n                            [176.10839843749997, -37.64511718749998],\n                            [177.27402343750012, -37.993457031249974],\n                            [178.0091796875, -37.55488281249998],\n                            [178.53623046875006, -37.69208984375004],\n                            [178.26767578125006, -38.551171875],\n                            [177.976171875, -38.72226562500005],\n                            [177.90878906250012, -39.23955078125],\n                            [177.52294921875003, -39.07382812499999],\n                            [177.07675781250012, -39.22177734375002],\n                            [176.93925781249996, -39.55527343750002],\n                            [177.10986328125009, -39.673144531250045],\n                            [176.8421875000001, -40.15781250000002],\n                            [175.98291015625003, -41.21328125000002],\n                            [175.30976562499998, -41.610644531249974],\n                            [175.16562500000012, -41.41738281249995],\n                            [174.88134765624997, -41.42402343749997],\n                            [174.8656250000001, -41.223046874999966],\n                            [174.63535156250012, -41.28945312499999],\n                            [175.1625, -40.62158203125],\n                            [175.25410156250004, -40.28935546875],\n                            [175.1559570312501, -40.11494140625],\n                            [175.00927734375009, -39.95214843749996],\n                            [173.93437500000013, -39.50908203125002],\n                            [173.76367187499997, -39.31875],\n                            [173.84433593750006, -39.13935546875001],\n                            [174.39843749999997, -38.96259765624998],\n                            [174.59736328124998, -38.78505859374995],\n                            [174.80166015625005, -37.895507812500014],\n                            [174.92802734375002, -37.80449218750003],\n                            [174.58583984374994, -37.09775390625002],\n                            [174.73427734375, -37.21523437499998],\n                            [174.92890625000004, -37.084765625000045],\n                            [174.78203125000013, -36.94375],\n                            [174.47558593750009, -36.94189453124997],\n                            [174.1888671875001, -36.492285156250034],\n                            [174.4015625000001, -36.60195312499999],\n                            [174.39277343750004, -36.24003906249999],\n                            [174.26787109375002, -36.16308593750003],\n                            [174.25371093749996, -36.24912109374998],\n                            [174.03642578125013, -36.12246093750001],\n                            [173.91445312499994, -35.908691406249986],\n                            [173.91728515625002, -36.01816406249999],\n                            [174.16640624999994, -36.327636718749986],\n                            [174.05468749999991, -36.35976562500004],\n                            [173.41220703125012, -35.542578125],\n                            [173.62617187500004, -35.31914062499996],\n                            [173.3763671875001, -35.50009765624996],\n                            [173.31396484375003, -35.44335937499996],\n                            [173.11669921874997, -35.205273437500026],\n                            [173.190625, -35.01621093749998],\n                            [172.70595703125005, -34.45517578124998],\n                            [173.04394531249997, -34.429101562499994],\n                            [172.96376953125, -34.53515625000003],\n                            [173.26943359375, -34.93476562499998]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"New Zealand\", \"childNum\": 9 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [58.722070312499994, 20.21875],\n                            [58.640917968750074, 20.210693359375057],\n                            [58.64121093750006, 20.33735351562501],\n                            [58.884375, 20.680566406250023],\n                            [58.95078125000006, 20.516162109375017],\n                            [58.722070312499994, 20.21875]\n                        ]\n                    ],\n                    [\n                        [\n                            [56.38798828125002, 24.97919921875004],\n                            [56.640625, 24.4703125],\n                            [57.12304687500003, 23.980712890625],\n                            [58.773046875, 23.517187499999977],\n                            [59.42939453125004, 22.660839843749955],\n                            [59.82324218749997, 22.50898437500004],\n                            [59.8, 22.21992187500001],\n                            [59.37148437500005, 21.498828125000017],\n                            [58.89570312500004, 21.11279296874997],\n                            [58.47421875000006, 20.406884765624966],\n                            [58.20898437500003, 20.423974609374994],\n                            [58.245019531249994, 20.599218749999977],\n                            [58.16943359375003, 20.58950195312505],\n                            [57.86181640624997, 20.24414062500003],\n                            [57.71416015625002, 19.678417968749983],\n                            [57.81162109375006, 19.01708984374997],\n                            [56.825976562500074, 18.753515625],\n                            [56.3834960937501, 17.98798828125001],\n                            [55.479101562500006, 17.84326171875003],\n                            [55.25537109375003, 17.58564453125004],\n                            [55.275195312500074, 17.320898437500006],\n                            [55.06416015625004, 17.038916015625034],\n                            [54.06816406250002, 17.005517578124966],\n                            [53.60986328124997, 16.75996093750004],\n                            [53.08564453125004, 16.648388671874955],\n                            [51.977636718750006, 18.996142578125074],\n                            [54.97734375000002, 19.995947265625006],\n                            [55.64101562499999, 22.001855468749994],\n                            [55.185839843750074, 22.7041015625],\n                            [55.1999023437501, 23.034765625000034],\n                            [55.53164062499999, 23.81904296875001],\n                            [55.4684570312501, 23.94111328125001],\n                            [55.98515625000002, 24.063378906249966],\n                            [55.92861328125005, 24.215136718750074],\n                            [55.76083984375006, 24.24267578125],\n                            [55.795703125000074, 24.868115234374955],\n                            [56.00058593750006, 24.953222656249977],\n                            [56.06386718750005, 24.73876953125],\n                            [56.38798828125002, 24.97919921875004]\n                        ]\n                    ],\n                    [\n                        [\n                            [56.29785156250003, 25.650683593750045],\n                            [56.144628906250006, 25.690527343750006],\n                            [56.16748046875003, 26.047460937499977],\n                            [56.08046875, 26.06264648437505],\n                            [56.41308593749997, 26.351171875000034],\n                            [56.29785156250003, 25.650683593750045]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Oman\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [73.08961802927895, 36.86435907947333],\n                        [73.08203125000107, 36.43949943991182],\n                        [72.31128647748268, 35.77290936638241],\n                        [73.13410859949555, 34.82510160558277],\n                        [73.19895048106557, 33.88770931468204],\n                        [74.00809389139292, 33.25375789331485],\n                        [73.98984375, 33.22119140625],\n                        [74.30361328125002, 32.991796875],\n                        [74.30546875000002, 32.810449218749994],\n                        [74.35458984375, 32.768701171874994],\n                        [74.58828125000002, 32.753222656249996],\n                        [74.632421875, 32.770898437499994],\n                        [74.66328125000001, 32.757666015625],\n                        [74.64335937500002, 32.607714843749996],\n                        [74.68574218750001, 32.493798828124994],\n                        [74.78886718750002, 32.4578125],\n                        [74.9873046875, 32.462207031249996],\n                        [75.33349609375, 32.279199218749994],\n                        [75.25410156250001, 32.14033203125],\n                        [75.13876953125, 32.104785156249996],\n                        [75.07148437500001, 32.08935546875],\n                        [74.73945312500001, 31.948828125],\n                        [74.6357421875, 31.88974609375],\n                        [74.55556640625002, 31.818554687499997],\n                        [74.5259765625, 31.76513671875],\n                        [74.50996093750001, 31.712939453124996],\n                        [74.58183593750002, 31.52392578125],\n                        [74.59394531250001, 31.465380859374996],\n                        [74.53496093750002, 31.261376953124994],\n                        [74.51767578125, 31.185595703124996],\n                        [74.6103515625, 31.112841796874996],\n                        [74.62578125000002, 31.06875],\n                        [74.6328125, 31.03466796875],\n                        [74.509765625, 30.959667968749997],\n                        [74.38037109375, 30.893408203125],\n                        [74.33935546875, 30.8935546875],\n                        [74.00898437500001, 30.519677734374994],\n                        [73.89931640625002, 30.435351562499996],\n                        [73.88271484375002, 30.3521484375],\n                        [73.92460937500002, 30.28164062499999],\n                        [73.93339843750002, 30.222070312499994],\n                        [73.88652343750002, 30.162011718749994],\n                        [73.8091796875, 30.093359375],\n                        [73.38164062500002, 29.934375],\n                        [72.9033203125, 29.028759765624997],\n                        [72.34189453125, 28.751904296874997],\n                        [72.2919921875, 28.697265625],\n                        [72.128515625, 28.346337890624994],\n                        [71.94804687500002, 28.177294921874996],\n                        [71.88886718750001, 28.0474609375],\n                        [71.87031250000001, 27.9625],\n                        [71.54296875, 27.869873046875],\n                        [71.18476562500001, 27.831640625],\n                        [70.87490234375002, 27.714453125],\n                        [70.79794921875, 27.709619140624994],\n                        [70.69160156250001, 27.768994140624997],\n                        [70.62910156250001, 27.937451171874997],\n                        [70.40371093750002, 28.025048828124994],\n                        [70.24433593750001, 27.934130859374996],\n                        [70.1939453125, 27.894873046875],\n                        [70.14453125, 27.849023437499994],\n                        [70.0498046875, 27.694726562499994],\n                        [69.89628906250002, 27.4736328125],\n                        [69.56796875, 27.174609375],\n                        [69.47001953125002, 26.804443359375],\n                        [70.11464843750002, 26.548046875],\n                        [70.14921875000002, 26.347558593749994],\n                        [70.1001953125, 25.910058593749994],\n                        [70.2646484375, 25.70654296875],\n                        [70.3251953125, 25.685742187499997],\n                        [70.44853515625002, 25.681347656249997],\n                        [70.505859375, 25.685302734375],\n                        [70.56953125000001, 25.705957031249994],\n                        [70.6484375, 25.666943359374997],\n                        [70.65205078125001, 25.422900390625003],\n                        [70.87773437500002, 25.06298828125],\n                        [70.95087890625001, 24.8916015625],\n                        [71.02070312500001, 24.75766601562499],\n                        [71.0478515625, 24.687744140625],\n                        [71.00234375000002, 24.65390625],\n                        [70.97636718750002, 24.61875],\n                        [70.96982421875, 24.571875],\n                        [71.04531250000002, 24.429980468750003],\n                        [71.04404296875, 24.400097656249997],\n                        [70.98281250000002, 24.361035156249997],\n                        [70.928125, 24.362353515625003],\n                        [70.88623046875, 24.34375],\n                        [70.80507812500002, 24.261962890625],\n                        [70.76728515625001, 24.245410156250003],\n                        [70.71630859375, 24.237988281249997],\n                        [70.65947265625002, 24.24609375],\n                        [70.57929687500001, 24.279052734375],\n                        [70.55585937500001, 24.331103515625003],\n                        [70.5650390625, 24.385791015625003],\n                        [70.54677734375002, 24.41831054687499],\n                        [70.2890625, 24.35629882812499],\n                        [70.0982421875, 24.2875],\n                        [69.80517578125, 24.165234375],\n                        [69.71621093750002, 24.172607421875],\n                        [69.63417968750002, 24.22519531249999],\n                        [69.5591796875, 24.273095703124994],\n                        [69.44345703125, 24.275390625],\n                        [69.23505859375001, 24.268261718749997],\n                        [69.11953125000002, 24.26865234374999],\n                        [69.05156250000002, 24.286328125],\n                        [68.98457031250001, 24.273095703124994],\n                        [68.90078125000002, 24.292431640624997],\n                        [68.86347656250001, 24.266503906249994],\n                        [68.82832031250001, 24.26401367187499],\n                        [68.78115234375002, 24.313720703125],\n                        [68.75898437500001, 24.30722656249999],\n                        [68.73964843750002, 24.2919921875],\n                        [68.728125, 24.265625],\n                        [68.72412109375, 23.96469726562499],\n                        [68.48867187500002, 23.967236328124997],\n                        [68.38125, 23.950878906249997],\n                        [68.28251953125002, 23.927978515625],\n                        [68.1650390625, 23.857324218749994],\n                        [68.11552734375002, 23.753369140624997],\n                        [67.8599609375, 23.90268554687499],\n                        [67.66845703125, 23.810986328124997],\n                        [67.309375, 24.1748046875],\n                        [67.171484375, 24.756103515625],\n                        [66.70302734375002, 24.8609375],\n                        [66.69863281250002, 25.226318359375],\n                        [66.32421875, 25.601806640625],\n                        [66.13115234375002, 25.49326171874999],\n                        [66.46767578125002, 25.4453125],\n                        [64.77666015625002, 25.307324218749997],\n                        [64.65898437500002, 25.18408203125],\n                        [64.059375, 25.40292968749999],\n                        [63.556640625, 25.353173828124994],\n                        [63.49140625000001, 25.210839843749994],\n                        [61.56689453125, 25.186328125],\n                        [61.587890625, 25.20234375],\n                        [61.61542968750001, 25.2861328125],\n                        [61.64013671875, 25.584619140624994],\n                        [61.67138671875, 25.6923828125],\n                        [61.66181640625001, 25.751269531250003],\n                        [61.66865234375001, 25.768994140624997],\n                        [61.73769531250002, 25.82109375],\n                        [61.75439453125, 25.84335937499999],\n                        [61.78076171875, 25.995849609375],\n                        [61.80996093750002, 26.165283203125],\n                        [61.842382812500006, 26.225927734375],\n                        [62.1259765625, 26.368994140625],\n                        [62.239355468750006, 26.35703125],\n                        [62.31230468750002, 26.490869140624994],\n                        [63.168066406250006, 26.665576171874996],\n                        [63.186132812500006, 26.837597656249997],\n                        [63.24160156250002, 26.86474609375],\n                        [63.25039062500002, 26.879248046875],\n                        [63.24208984375002, 27.077685546874996],\n                        [63.30517578125, 27.124560546874996],\n                        [63.30156250000002, 27.15146484375],\n                        [63.25625, 27.207910156249994],\n                        [63.19609375000002, 27.243945312499996],\n                        [63.16679687500002, 27.252490234374996],\n                        [62.75273437500002, 27.265625],\n                        [62.782324218750006, 27.800537109375],\n                        [62.7625, 28.202050781249994],\n                        [61.88984375000001, 28.546533203124994],\n                        [61.15214843750002, 29.542724609375],\n                        [61.0341796875, 29.663427734375],\n                        [60.843359375, 29.858691406249996],\n                        [61.22441406250002, 29.749414062499994],\n                        [62.0009765625, 29.530419921874994],\n                        [62.4765625, 29.408349609374994],\n                        [63.56757812500001, 29.497998046874997],\n                        [64.09873046875, 29.391943359375],\n                        [64.39375, 29.544335937499994],\n                        [65.09550781250002, 29.559472656249994],\n                        [66.23125, 29.86572265625],\n                        [66.346875, 30.802783203124996],\n                        [66.82929687500001, 31.263671875],\n                        [67.45283203125001, 31.234619140625],\n                        [67.737890625, 31.343945312499997],\n                        [67.57822265625, 31.506494140624994],\n                        [68.16103515625002, 31.802978515625],\n                        [68.59765625, 31.802978515625],\n                        [68.86894531250002, 31.634228515624997],\n                        [69.279296875, 31.936816406249996],\n                        [69.24140625000001, 32.433544921875],\n                        [69.5015625, 33.020068359374996],\n                        [70.26113281250002, 33.289013671875],\n                        [69.8896484375, 34.007275390625],\n                        [70.65400390625001, 33.952294921874994],\n                        [71.05156250000002, 34.049707031249994],\n                        [71.095703125, 34.369433593749996],\n                        [70.965625, 34.53037109375],\n                        [71.62050781250002, 35.183007812499994],\n                        [71.57197265625001, 35.546826171875],\n                        [71.18505859375, 36.04208984375],\n                        [71.23291015625, 36.12177734375],\n                        [72.24980468750002, 36.734716796875],\n                        [73.08961802927895, 36.86435907947333]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Pakistan\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-81.60327148437497, 7.332812499999989],\n                            [-81.85205078125003, 7.453320312500026],\n                            [-81.812158203125, 7.59238281250002],\n                            [-81.72875976562494, 7.62119140625002],\n                            [-81.60327148437497, 7.332812499999989]\n                        ]\n                    ],\n                    [\n                        [\n                            [-78.89833984375002, 8.27426757812502],\n                            [-78.960595703125, 8.435839843749989],\n                            [-78.88325195312495, 8.460253906249989],\n                            [-78.89833984375002, 8.27426757812502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-77.37421874999993, 8.65830078125002],\n                            [-77.47851562499994, 8.498437500000037],\n                            [-77.19599609374995, 7.972460937500003],\n                            [-77.53828124999995, 7.56625976562502],\n                            [-77.76191406249995, 7.698828125000034],\n                            [-77.90117187499999, 7.229345703125048],\n                            [-78.42158203124995, 8.060986328125011],\n                            [-78.28735351562497, 8.091796874999972],\n                            [-78.14189453125002, 8.386083984374977],\n                            [-77.76054687499993, 8.133251953124983],\n                            [-78.09946289062498, 8.496972656250009],\n                            [-78.22304687500002, 8.396630859374994],\n                            [-78.39921874999993, 8.505664062500003],\n                            [-78.40986328124998, 8.35532226562502],\n                            [-78.51406249999997, 8.628173828125],\n                            [-79.08637695312495, 8.997167968750034],\n                            [-79.50708007812494, 8.97006835937502],\n                            [-79.68745117187493, 8.850976562500009],\n                            [-79.81591796875, 8.639208984375031],\n                            [-79.75043945312498, 8.595507812500017],\n                            [-80.458984375, 8.213867187499972],\n                            [-80.45810546875, 8.077050781249994],\n                            [-80.01123046875, 7.500048828125031],\n                            [-80.66669921874995, 7.225683593750006],\n                            [-80.90122070312503, 7.277148437500017],\n                            [-81.06386718749994, 7.89975585937502],\n                            [-81.26840820312495, 7.625488281250014],\n                            [-81.50415039062503, 7.721191406249972],\n                            [-81.72763671875, 8.137548828124977],\n                            [-82.15986328124995, 8.19482421875],\n                            [-82.23544921874998, 8.311035156250057],\n                            [-82.67954101562503, 8.321972656249969],\n                            [-82.86611328124994, 8.246337890625014],\n                            [-82.87934570312498, 8.07065429687502],\n                            [-83.02734375, 8.337744140624991],\n                            [-82.86162109374999, 8.453515625000037],\n                            [-82.84477539062493, 8.489355468749963],\n                            [-82.85571289062494, 8.635302734375031],\n                            [-82.91704101562502, 8.740332031250034],\n                            [-82.88198242187497, 8.805322265625037],\n                            [-82.72783203125002, 8.916064453125031],\n                            [-82.78305664062498, 8.990283203124974],\n                            [-82.88134765625003, 9.055859375000011],\n                            [-82.94033203124997, 9.060107421874989],\n                            [-82.93984374999994, 9.449169921875026],\n                            [-82.92504882812494, 9.469042968749989],\n                            [-82.88896484374999, 9.481005859375017],\n                            [-82.86015625, 9.511474609375014],\n                            [-82.84399414062497, 9.570800781250014],\n                            [-82.801025390625, 9.591796875000028],\n                            [-82.64409179687502, 9.505859375000028],\n                            [-82.56357421875003, 9.576660156249972],\n                            [-82.50034179687503, 9.523242187500017],\n                            [-82.37080078124993, 9.428564453124991],\n                            [-82.33974609375, 9.209179687499983],\n                            [-82.18813476562502, 9.191748046874977],\n                            [-82.24418945312499, 9.031494140625014],\n                            [-82.07788085937503, 8.93486328124996],\n                            [-81.78022460937495, 8.957226562499983],\n                            [-81.89448242187495, 9.140429687500003],\n                            [-81.35478515624996, 8.78056640624996],\n                            [-80.83867187499999, 8.887207031250014],\n                            [-80.12709960937497, 9.20991210937504],\n                            [-79.57729492187497, 9.597851562500026],\n                            [-78.08276367187494, 9.236279296874997],\n                            [-77.37421874999993, 8.65830078125002]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Panama\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-73.137353515625, -6.4658203125],\n                        [-73.75810546874999, -6.90576171875],\n                        [-73.79301757812499, -7.135058593750003],\n                        [-73.758203125, -7.172753906250009],\n                        [-73.72041015625, -7.309277343750011],\n                        [-73.964306640625, -7.37890625],\n                        [-73.95849609375, -7.506640625],\n                        [-73.98173828124999, -7.535742187500006],\n                        [-74.00205078124999, -7.556054687500009],\n                        [-73.98173828124999, -7.585058593750006],\n                        [-73.946875, -7.611230468750009],\n                        [-73.89462890624999, -7.65478515625],\n                        [-73.82207031249999, -7.738964843750011],\n                        [-73.76689453124999, -7.753515625],\n                        [-73.72041015625, -7.782519531250003],\n                        [-73.73203125, -7.875390625],\n                        [-73.54912109374999, -8.345800781250006],\n                        [-73.39814453125, -8.458984375],\n                        [-73.36040039062499, -8.479296875],\n                        [-73.351708984375, -8.51416015625],\n                        [-73.35673828124999, -8.566992187500006],\n                        [-73.30244140625, -8.654003906250011],\n                        [-73.203125, -8.719335937500006],\n                        [-73.0705078125, -8.8828125],\n                        [-72.9740234375, -8.9931640625],\n                        [-72.970361328125, -9.1201171875],\n                        [-73.08984375, -9.265722656250006],\n                        [-73.209423828125, -9.411425781250003],\n                        [-72.379052734375, -9.51015625],\n                        [-72.181591796875, -10.003710937500003],\n                        [-71.33940429687499, -9.988574218750003],\n                        [-71.11528320312499, -9.852441406250009],\n                        [-71.041748046875, -9.81875],\n                        [-70.6369140625, -9.478222656250011],\n                        [-70.60791015625, -9.463671875],\n                        [-70.54111328124999, -9.4375],\n                        [-70.57016601562499, -9.48984375],\n                        [-70.592236328125, -9.54345703125],\n                        [-70.59916992187499, -9.620507812500009],\n                        [-70.642333984375, -11.01025390625],\n                        [-70.59653320312499, -10.976855468750003],\n                        [-70.53325195312499, -10.946875],\n                        [-70.45087890625, -11.024804687500009],\n                        [-70.39228515625, -11.05859375],\n                        [-70.3419921875, -11.066699218750003],\n                        [-70.29038085937499, -11.064257812500003],\n                        [-70.22006835937499, -11.04765625],\n                        [-70.06630859375, -10.982421875],\n                        [-69.9603515625, -10.929882812500011],\n                        [-69.839794921875, -10.933398437500003],\n                        [-69.6740234375, -10.9541015625],\n                        [-69.57861328125, -10.951757812500006],\n                        [-68.68525390625, -12.501953125],\n                        [-68.97861328124999, -12.880078125000011],\n                        [-69.07412109375, -13.682812500000011],\n                        [-68.87089843749999, -14.169726562500003],\n                        [-69.35947265624999, -14.7953125],\n                        [-69.37470703125, -14.962988281250006],\n                        [-69.17246093749999, -15.236621093750003],\n                        [-69.4208984375, -15.640625],\n                        [-69.21757812499999, -16.14912109375001],\n                        [-68.8427734375, -16.337890625],\n                        [-69.03291015625, -16.47597656250001],\n                        [-69.020703125, -16.6421875],\n                        [-69.62485351562499, -17.2001953125],\n                        [-69.645703125, -17.24853515625],\n                        [-69.521923828125, -17.388964843750003],\n                        [-69.510986328125, -17.46035156250001],\n                        [-69.51108398437499, -17.5048828125],\n                        [-69.5109375, -17.50605468750001],\n                        [-69.58642578125, -17.5732421875],\n                        [-69.684765625, -17.64980468750001],\n                        [-69.85209960937499, -17.70380859375001],\n                        [-69.80258789062499, -17.990234375],\n                        [-69.92636718749999, -18.2060546875],\n                        [-70.41826171874999, -18.34560546875001],\n                        [-71.33696289062499, -17.68251953125001],\n                        [-71.5322265625, -17.29433593750001],\n                        [-72.46767578125, -16.708105468750006],\n                        [-73.727685546875, -16.20166015625],\n                        [-75.104248046875, -15.411914062500003],\n                        [-75.533642578125, -14.89921875],\n                        [-75.93388671874999, -14.63359375],\n                        [-76.37646484375, -13.863085937500003],\n                        [-76.259228515625, -13.802832031250006],\n                        [-76.2236328125, -13.371191406250006],\n                        [-76.83212890624999, -12.348730468750006],\n                        [-77.152734375, -12.060351562500003],\n                        [-77.2203125, -11.663378906250003],\n                        [-77.633203125, -11.287792968750011],\n                        [-77.736083984375, -10.83671875],\n                        [-78.18559570312499, -10.089062500000011],\n                        [-78.76225585937499, -8.616992187500003],\n                        [-79.37724609374999, -7.835546875],\n                        [-79.99497070312499, -6.768945312500009],\n                        [-81.142041015625, -6.056738281250006],\n                        [-81.164306640625, -5.875292968750003],\n                        [-80.9306640625, -5.8408203125],\n                        [-80.88193359374999, -5.635058593750003],\n                        [-81.33662109375, -4.66953125],\n                        [-81.283203125, -4.322265625],\n                        [-80.503662109375, -3.49609375],\n                        [-80.324658203125, -3.387890625000011],\n                        [-80.24375, -3.576757812500006],\n                        [-80.19414062499999, -3.905859375],\n                        [-80.23051757812499, -3.924023437500011],\n                        [-80.26689453124999, -3.948828125],\n                        [-80.30327148437499, -4.005078125000011],\n                        [-80.43720703125, -3.978613281250006],\n                        [-80.49013671875, -4.010058593750003],\n                        [-80.510009765625, -4.069531250000011],\n                        [-80.49345703124999, -4.119140625],\n                        [-80.4884765625, -4.16552734375],\n                        [-80.453759765625, -4.205175781250006],\n                        [-80.35288085937499, -4.20849609375],\n                        [-80.44384765625, -4.335839843750009],\n                        [-80.4884765625, -4.393652343750006],\n                        [-80.47856445312499, -4.430078125],\n                        [-80.42416992187499, -4.46142578125],\n                        [-80.38349609375, -4.463671875],\n                        [-80.293359375, -4.416796875],\n                        [-80.1974609375, -4.31103515625],\n                        [-80.13955078125, -4.296093750000011],\n                        [-80.06352539062499, -4.327539062500009],\n                        [-79.962890625, -4.390332031250011],\n                        [-79.8451171875, -4.445898437500006],\n                        [-79.797265625, -4.476367187500003],\n                        [-79.71098632812499, -4.467578125],\n                        [-79.63852539062499, -4.454882812500003],\n                        [-79.57768554687499, -4.500585937500006],\n                        [-79.51616210937499, -4.539160156250006],\n                        [-79.501904296875, -4.670605468750011],\n                        [-79.45576171875, -4.766210937500006],\n                        [-79.3994140625, -4.840039062500011],\n                        [-79.33095703125, -4.927832031250006],\n                        [-79.26811523437499, -4.957617187500006],\n                        [-79.186669921875, -4.958203125000011],\n                        [-79.07626953124999, -4.990625],\n                        [-79.03330078124999, -4.969140625],\n                        [-78.995263671875, -4.908007812500003],\n                        [-78.97539062499999, -4.873242187500011],\n                        [-78.919189453125, -4.8583984375],\n                        [-78.92578125, -4.770703125000011],\n                        [-78.9076171875, -4.714453125],\n                        [-78.8615234375, -4.6650390625],\n                        [-78.68603515625, -4.562402343750009],\n                        [-78.64799804687499, -4.248144531250006],\n                        [-78.345361328125, -3.397363281250009],\n                        [-78.240380859375, -3.472558593750009],\n                        [-77.860595703125, -2.981640625000011],\n                        [-76.6791015625, -2.562597656250006],\n                        [-76.089794921875, -2.133105468750003],\n                        [-75.570556640625, -1.53125],\n                        [-75.42041015625, -0.962207031250003],\n                        [-75.40805664062499, -0.92431640625],\n                        [-75.24960937499999, -0.951855468750011],\n                        [-75.259375, -0.590136718750003],\n                        [-75.42470703125, -0.408886718750011],\n                        [-75.49106445312499, -0.248339843750003],\n                        [-75.56059570312499, -0.200097656250009],\n                        [-75.63203125, -0.157617187500009],\n                        [-75.62626953124999, -0.122851562500003],\n                        [-75.340478515625, -0.1421875],\n                        [-75.13837890625, -0.050488281250011],\n                        [-74.8017578125, -0.200097656250009],\n                        [-74.78046875, -0.24453125],\n                        [-74.75537109375, -0.298632812500003],\n                        [-74.691650390625, -0.335253906250003],\n                        [-74.616357421875, -0.370019531250009],\n                        [-74.555078125, -0.429882812500011],\n                        [-74.5138671875, -0.470117187500009],\n                        [-74.46518554687499, -0.517675781250006],\n                        [-74.41787109375, -0.580664062500006],\n                        [-74.334423828125, -0.850878906250003],\n                        [-74.28388671875, -0.927832031250006],\n                        [-74.24638671874999, -0.970605468750009],\n                        [-74.05439453125, -1.028613281250003],\n                        [-73.98681640625, -1.09814453125],\n                        [-73.926953125, -1.125195312500011],\n                        [-73.86318359375, -1.196679687500009],\n                        [-73.664306640625, -1.248828125],\n                        [-73.4962890625, -1.693066406250011],\n                        [-73.19697265625, -1.830273437500011],\n                        [-73.1544921875, -2.278222656250009],\n                        [-72.9896484375, -2.339746093750009],\n                        [-72.94111328125, -2.39404296875],\n                        [-72.21845703125, -2.400488281250006],\n                        [-71.98427734375, -2.3265625],\n                        [-71.93247070312499, -2.288671875],\n                        [-71.86728515624999, -2.227734375000011],\n                        [-71.802734375, -2.166308593750003],\n                        [-71.75253906249999, -2.152734375],\n                        [-71.55947265625, -2.22421875],\n                        [-71.39697265625, -2.334082031250006],\n                        [-71.19638671874999, -2.313085937500006],\n                        [-71.11337890624999, -2.245410156250003],\n                        [-71.027294921875, -2.225781250000011],\n                        [-70.96855468749999, -2.206835937500003],\n                        [-70.70537109374999, -2.341992187500011],\n                        [-70.64799804687499, -2.40576171875],\n                        [-70.57587890625, -2.418261718750003],\n                        [-70.29462890625, -2.552539062500003],\n                        [-70.24443359374999, -2.606542968750006],\n                        [-70.16474609375, -2.639843750000011],\n                        [-70.095849609375, -2.658203125],\n                        [-70.735107421875, -3.781542968750003],\n                        [-70.5296875, -3.866406250000011],\n                        [-70.48583984375, -3.869335937500011],\n                        [-70.42109375, -3.849609375],\n                        [-70.37919921874999, -3.81875],\n                        [-70.339501953125, -3.814355468750009],\n                        [-70.2984375, -3.84423828125],\n                        [-70.24028320312499, -3.882714843750009],\n                        [-70.16752929687499, -4.050195312500009],\n                        [-70.0171875, -4.162011718750009],\n                        [-69.96591796874999, -4.2359375],\n                        [-69.97202148437499, -4.301171875],\n                        [-70.00395507812499, -4.327246093750006],\n                        [-70.05332031249999, -4.333105468750006],\n                        [-70.12880859375, -4.28662109375],\n                        [-70.23916015625, -4.301171875],\n                        [-70.31689453125, -4.246972656250009],\n                        [-70.34365234375, -4.193652343750003],\n                        [-70.40463867187499, -4.150097656250011],\n                        [-70.5306640625, -4.167578125],\n                        [-70.72158203125, -4.158886718750011],\n                        [-70.79951171875, -4.17333984375],\n                        [-70.97368164062499, -4.350488281250009],\n                        [-71.8447265625, -4.50439453125],\n                        [-72.256787109375, -4.748925781250009],\n                        [-72.35283203124999, -4.786035156250009],\n                        [-72.468994140625, -4.901269531250009],\n                        [-72.608349609375, -5.009570312500003],\n                        [-72.69873046875, -5.0671875],\n                        [-72.83193359375, -5.09375],\n                        [-72.88706054687499, -5.122753906250011],\n                        [-72.9798828125, -5.634863281250006],\n                        [-73.16289062499999, -5.933398437500003],\n                        [-73.209375, -6.028710937500009],\n                        [-73.235546875, -6.0984375],\n                        [-73.137353515625, -6.4658203125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Peru\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [120.250390625, 5.256591796875043],\n                            [119.82148437500004, 5.06953125000004],\n                            [120.1652343750001, 5.332421875000037],\n                            [120.250390625, 5.256591796875043]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.159375, 6.075634765625011],\n                            [121.41103515625005, 5.939843749999966],\n                            [121.29443359374997, 5.869970703125034],\n                            [120.8763671875, 5.95263671875],\n                            [121.159375, 6.075634765625011]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.09287109375012, 6.428320312500006],\n                            [121.95917968750004, 6.415820312500045],\n                            [121.83203125000003, 6.664062499999986],\n                            [122.0583007812501, 6.740722656249972],\n                            [122.32353515625002, 6.602246093750011],\n                            [122.09287109375012, 6.428320312500006]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.93710937500006, 7.409130859374983],\n                            [122.80468750000003, 7.315966796875017],\n                            [122.82216796875, 7.428466796875014],\n                            [122.93710937500006, 7.409130859374983]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.07988281250007, 7.883398437499977],\n                            [117.02832031249997, 7.807519531249966],\n                            [116.96953125000007, 7.894921875],\n                            [116.9935546875, 8.050537109375014],\n                            [117.07705078125, 8.069140624999974],\n                            [117.07988281250007, 7.883398437499977]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.35527343750002, 8.21464843749996],\n                            [117.28701171875, 8.191015625000034],\n                            [117.28085937500006, 8.314990234374974],\n                            [117.35527343750002, 8.21464843749996]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.80664062500003, 9.142626953125003],\n                            [124.66582031250002, 9.132324218750043],\n                            [124.65332031250003, 9.225830078125],\n                            [124.80664062500003, 9.142626953125003]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.69765625000005, 9.237304687500028],\n                            [123.61445312500004, 9.103320312499989],\n                            [123.49345703125002, 9.192089843750054],\n                            [123.69765625000005, 9.237304687500028]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.00595703125006, 9.320947265625009],\n                            [126.19335937499997, 9.276708984374963],\n                            [126.30458984375, 8.952050781249994],\n                            [126.13955078125005, 8.59565429687504],\n                            [126.36533203125012, 8.483886718750014],\n                            [126.45869140625004, 8.20283203125004],\n                            [126.43535156250002, 7.832812499999974],\n                            [126.57011718750002, 7.677246093749986],\n                            [126.58154296875003, 7.247753906249969],\n                            [126.1920898437501, 6.852539062500014],\n                            [126.18935546875, 6.309667968749991],\n                            [125.82441406250004, 7.333300781249989],\n                            [125.68925781250007, 7.263037109374977],\n                            [125.38066406250007, 6.689941406250014],\n                            [125.58847656250012, 6.465771484374997],\n                            [125.66796874999997, 5.97866210937498],\n                            [125.34648437500002, 5.598974609374977],\n                            [125.23154296875006, 6.069531250000011],\n                            [124.92734375000006, 5.875341796874977],\n                            [124.21279296875, 6.233251953124977],\n                            [124.078125, 6.404443359375037],\n                            [123.98525390625, 6.993701171875003],\n                            [124.20664062500006, 7.396435546874983],\n                            [123.66582031250002, 7.817773437500023],\n                            [123.49306640625, 7.80791015624996],\n                            [123.39091796875007, 7.407519531250017],\n                            [123.09667968749997, 7.700439453125],\n                            [122.8429687500001, 7.529296875000043],\n                            [122.79179687500002, 7.72246093749996],\n                            [122.61621093749997, 7.763134765624983],\n                            [122.14248046875, 6.949658203124997],\n                            [121.96425781250005, 6.96821289062504],\n                            [121.92460937500002, 7.199511718750003],\n                            [122.24335937500004, 7.945117187500031],\n                            [122.91113281250003, 8.156445312499997],\n                            [123.05058593750002, 8.433935546875048],\n                            [123.43457031249997, 8.70332031250004],\n                            [123.84921875000006, 8.432714843749977],\n                            [123.79941406250006, 8.049121093749989],\n                            [124.19765625, 8.229541015624974],\n                            [124.40488281250006, 8.599853515625014],\n                            [124.7311523437501, 8.562988281250043],\n                            [124.86894531250002, 8.972265625000034],\n                            [125.141015625, 8.86875],\n                            [125.20966796875004, 9.027148437500017],\n                            [125.49873046875004, 9.014746093749977],\n                            [125.47128906250006, 9.756787109374983],\n                            [126.00595703125006, 9.320947265625009]\n                        ]\n                    ],\n                    [\n                        [\n                            [126.059375, 9.766210937500034],\n                            [125.99121093750003, 9.838525390625023],\n                            [126.07382812500006, 10.059228515625051],\n                            [126.1725585937501, 9.79995117187498],\n                            [126.059375, 9.766210937500034]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.59384765625006, 9.787207031249963],\n                            [124.1224609375, 9.599316406249969],\n                            [123.93564453125012, 9.623974609375011],\n                            [123.81718750000002, 9.817382812499986],\n                            [124.17285156250003, 10.135205078124983],\n                            [124.33574218750002, 10.159912109375043],\n                            [124.57714843749997, 10.026708984374991],\n                            [124.59384765625006, 9.787207031249963]\n                        ]\n                    ],\n                    [\n                        [\n                            [125.69023437500007, 9.914453125000037],\n                            [125.49482421875004, 10.118701171875003],\n                            [125.66679687500002, 10.440136718750026],\n                            [125.69023437500007, 9.914453125000037]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.91621093750004, 10.485986328125037],\n                            [119.79316406250004, 10.455273437499997],\n                            [119.85205078124997, 10.64013671875],\n                            [120.00839843750012, 10.570117187500031],\n                            [119.91621093750004, 10.485986328125037]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.64951171875012, 10.472705078125003],\n                            [122.53837890625002, 10.424951171875037],\n                            [122.5375, 10.607568359375023],\n                            [122.70126953125006, 10.740625],\n                            [122.64951171875012, 10.472705078125003]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.13085937500003, 9.064111328124994],\n                            [122.99472656250006, 9.058837890624986],\n                            [122.8666015625, 9.319824218750043],\n                            [122.5625, 9.482812500000037],\n                            [122.39951171875006, 9.823046874999989],\n                            [122.47148437500007, 9.961523437500034],\n                            [122.85556640625006, 10.0869140625],\n                            [122.81699218750012, 10.503808593750023],\n                            [122.98330078125, 10.886621093750037],\n                            [123.25664062500007, 10.99394531249996],\n                            [123.51064453125005, 10.923046875],\n                            [123.5675781250001, 10.780761718750057],\n                            [123.16201171875, 9.864257812500028],\n                            [123.1498046875, 9.606152343750026],\n                            [123.32050781250004, 9.27294921875],\n                            [123.13085937500003, 9.064111328124994]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.37031250000004, 9.449609375000023],\n                            [123.38623046874997, 9.967089843750017],\n                            [124.03886718750002, 11.273535156249991],\n                            [124.00498046875012, 10.40009765625004],\n                            [123.70048828125007, 10.128320312500009],\n                            [123.37031250000004, 9.449609375000023]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.75703125000004, 11.28330078125002],\n                            [123.815625, 11.15073242187502],\n                            [123.73671875, 11.151464843749991],\n                            [123.75703125000004, 11.28330078125002]\n                        ]\n                    ],\n                    [\n                        [\n                            [117.31113281250012, 8.439599609375051],\n                            [117.21855468750007, 8.367285156249963],\n                            [117.34990234375002, 8.713574218749997],\n                            [119.22382812500004, 10.477294921875043],\n                            [119.30566406250003, 10.9736328125],\n                            [119.55332031250012, 11.31352539062496],\n                            [119.52666015625002, 10.953173828125003],\n                            [119.68691406250005, 10.500341796875034],\n                            [119.36933593750004, 10.327294921875037],\n                            [119.19150390625012, 10.061083984374989],\n                            [118.78212890625005, 9.91611328125002],\n                            [118.4349609375, 9.256005859375009],\n                            [117.31113281250012, 8.439599609375051]\n                        ]\n                    ],\n                    [\n                        [\n                            [119.86142578125006, 11.52534179687504],\n                            [119.83066406250012, 11.375683593750011],\n                            [119.72998046874997, 11.431933593750017],\n                            [119.86142578125006, 11.52534179687504]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.574609375, 11.343066406250031],\n                            [124.92998046875002, 11.372851562499974],\n                            [125.02656250000004, 11.21171875],\n                            [125.01318359374997, 10.785693359374989],\n                            [125.26845703125005, 10.307714843750048],\n                            [125.14257812499997, 10.189453125000028],\n                            [124.9875, 10.36757812499998],\n                            [125.02656250000004, 10.033105468749966],\n                            [124.78076171874997, 10.16806640625002],\n                            [124.78671875000012, 10.781396484375009],\n                            [124.66269531250006, 10.961962890625017],\n                            [124.44550781250004, 10.923583984375014],\n                            [124.33066406250012, 11.535205078125003],\n                            [124.574609375, 11.343066406250031]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.60839843750003, 11.492187500000043],\n                            [124.48349609375006, 11.485839843749986],\n                            [124.36035156250003, 11.665917968749994],\n                            [124.5109375000001, 11.687109375000048],\n                            [124.60839843750003, 11.492187500000043]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.49619140625006, 11.615087890625034],\n                            [122.83808593750004, 11.595654296874983],\n                            [122.89453125000003, 11.44130859374998],\n                            [123.15830078125012, 11.53554687499999],\n                            [123.11953125, 11.286816406250026],\n                            [122.8029296875001, 10.99003906249996],\n                            [122.76992187500005, 10.823828125000034],\n                            [121.95400390625, 10.444384765625003],\n                            [122.10351562499997, 11.64291992187502],\n                            [121.91601562499997, 11.854345703125006],\n                            [122.02919921875005, 11.895410156250023],\n                            [122.49619140625006, 11.615087890625034]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.03876953125004, 11.703320312499969],\n                            [119.94492187500006, 11.690722656249989],\n                            [119.86093750000006, 11.953955078124963],\n                            [120.03593750000002, 11.917236328125028],\n                            [120.03876953125004, 11.703320312499969]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.1, 12.167675781249983],\n                            [120.22822265625004, 12.219824218750034],\n                            [120.31455078125012, 12.012402343749969],\n                            [120.01054687500002, 12.008251953125011],\n                            [119.88574218749997, 12.299853515625003],\n                            [120.1, 12.167675781249983]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.65449218750004, 12.309033203125011],\n                            [122.42294921875006, 12.455078125],\n                            [122.60361328125006, 12.49160156249998],\n                            [122.65449218750004, 12.309033203125011]\n                        ]\n                    ],\n                    [\n                        [\n                            [125.23955078125002, 12.527880859375003],\n                            [125.32021484375, 12.321826171875031],\n                            [125.53564453125003, 12.191406250000028],\n                            [125.49179687500006, 11.594335937499977],\n                            [125.57353515625002, 11.238232421874997],\n                            [125.73564453125002, 11.049609375000017],\n                            [125.23339843749997, 11.145068359375017],\n                            [125.03427734375012, 11.341259765625026],\n                            [124.91699218750003, 11.558398437500031],\n                            [124.99501953125, 11.764941406250003],\n                            [124.445703125, 12.152783203124969],\n                            [124.29472656250007, 12.569335937500014],\n                            [125.23955078125002, 12.527880859375003]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.71660156250007, 12.287353515625028],\n                            [124.04033203125002, 11.966796875],\n                            [124.04550781250012, 11.752441406250028],\n                            [123.47373046875006, 12.21665039062502],\n                            [123.15781250000012, 11.925634765624963],\n                            [123.23642578125012, 12.583496093750057],\n                            [123.71660156250007, 12.287353515625028]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.09404296875002, 12.354882812500023],\n                            [122.01396484375002, 12.105615234375037],\n                            [121.9232421875, 12.331298828125014],\n                            [122.00156250000006, 12.598535156250009],\n                            [122.14501953124997, 12.652636718750017],\n                            [122.09404296875002, 12.354882812500023]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.77539062499997, 12.453906250000031],\n                            [123.77910156250002, 12.366259765625031],\n                            [123.62148437500005, 12.67490234375002],\n                            [123.77539062499997, 12.453906250000031]\n                        ]\n                    ],\n                    [\n                        [\n                            [123.28183593750006, 12.85341796874998],\n                            [123.36718750000003, 12.70083007812498],\n                            [122.95751953124997, 13.107177734374986],\n                            [123.28183593750006, 12.85341796874998]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.70439453125002, 13.479492187499986],\n                            [121.20273437500006, 13.432324218749969],\n                            [121.52275390625007, 13.131201171874991],\n                            [121.540625, 12.63818359375],\n                            [121.39433593750002, 12.300585937499974],\n                            [121.23671875000005, 12.218798828125003],\n                            [120.92216796875002, 12.51162109374998],\n                            [120.65136718749997, 13.169140625],\n                            [120.33847656250012, 13.412353515624986],\n                            [120.40126953125, 13.517041015624997],\n                            [120.70439453125002, 13.479492187499986]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.91484375000002, 13.540332031250031],\n                            [122.11455078125002, 13.463183593750031],\n                            [122.00488281249997, 13.204980468750009],\n                            [121.82919921875006, 13.328613281249972],\n                            [121.91484375000002, 13.540332031250031]\n                        ]\n                    ],\n                    [\n                        [\n                            [124.35361328125006, 13.632226562500009],\n                            [124.17539062500012, 13.531542968750017],\n                            [124.03886718750002, 13.663134765625003],\n                            [124.22490234375007, 14.077587890624969],\n                            [124.41718750000004, 13.871044921874997],\n                            [124.35361328125006, 13.632226562500009]\n                        ]\n                    ],\n                    [\n                        [\n                            [122.03349609375002, 15.005029296875009],\n                            [121.93300781250005, 14.656054687500045],\n                            [121.83984374999997, 15.038134765625003],\n                            [122.03349609375002, 15.005029296875009]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.10156249999997, 18.615283203125017],\n                            [121.84560546875, 18.29541015625003],\n                            [122.03847656250005, 18.32792968749999],\n                            [122.14667968750004, 18.486572265625],\n                            [122.26552734375005, 18.458837890625034],\n                            [122.15234374999997, 17.664404296875006],\n                            [122.51914062500012, 17.124853515625034],\n                            [122.13515625000005, 16.18481445312503],\n                            [121.59531250000012, 15.933251953125023],\n                            [121.60703125000006, 15.669824218749994],\n                            [121.39228515625004, 15.324414062499969],\n                            [121.69541015625006, 14.7373046875],\n                            [121.62792968749997, 14.581152343749977],\n                            [121.76660156249997, 14.16806640625002],\n                            [122.21171875000002, 13.930175781250057],\n                            [122.2875, 13.996191406250006],\n                            [122.19970703125003, 14.148046875000034],\n                            [122.6271484375001, 14.317529296875009],\n                            [122.93417968750012, 14.18808593750002],\n                            [123.101953125, 13.750244140624986],\n                            [123.29697265625012, 13.836425781250043],\n                            [123.32031249999997, 14.061669921875023],\n                            [123.81572265625002, 13.837109375000011],\n                            [123.80625000000012, 13.721728515625045],\n                            [123.54960937500007, 13.645751953125014],\n                            [123.81923828125, 13.269482421875011],\n                            [123.78515625000003, 13.110546875000054],\n                            [124.14277343750004, 13.035791015625009],\n                            [124.0597656250001, 12.567089843749997],\n                            [123.87783203125005, 12.689697265625014],\n                            [123.94853515625007, 12.916406250000023],\n                            [123.31093750000005, 13.044091796875009],\n                            [123.16328125000004, 13.44174804687502],\n                            [122.59521484374997, 13.90761718749998],\n                            [122.46796875000004, 13.886718749999986],\n                            [122.66787109375, 13.395361328124991],\n                            [122.59990234375002, 13.194140625000031],\n                            [122.37656250000012, 13.520605468750006],\n                            [121.77792968750006, 13.93764648437498],\n                            [121.50107421875006, 13.8421875],\n                            [121.344140625, 13.649121093749997],\n                            [121.09550781250007, 13.679492187500045],\n                            [120.84072265625, 13.884716796875026],\n                            [120.637109375, 13.804492187500031],\n                            [120.61679687500006, 14.188037109375003],\n                            [120.9220703125001, 14.493115234374983],\n                            [120.94130859375, 14.645068359375031],\n                            [120.58369140625004, 14.88125],\n                            [120.58867187500002, 14.483105468749983],\n                            [120.43876953125002, 14.453369140624972],\n                            [120.25078125000002, 14.793310546875034],\n                            [120.08212890625012, 14.851074218749986],\n                            [119.77255859375012, 16.25512695312503],\n                            [119.83076171875004, 16.326562500000023],\n                            [120.15976562500012, 16.047656250000045],\n                            [120.36875, 16.109570312499955],\n                            [120.35839843749997, 17.63818359375],\n                            [120.59970703125012, 18.507861328125074],\n                            [121.10156249999997, 18.615283203125017]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.92167968750007, 18.89472656250001],\n                            [121.82519531250003, 18.842724609374983],\n                            [121.94335937500003, 19.010449218749955],\n                            [121.92167968750007, 18.89472656250001]\n                        ]\n                    ],\n                    [\n                        [\n                            [121.52089843750005, 19.361962890624994],\n                            [121.53125, 19.271337890625006],\n                            [121.37460937500006, 19.356298828124977],\n                            [121.52089843750005, 19.361962890624994]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Philippines\", \"childNum\": 37 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [134.5954101562501, 7.382031249999969],\n                        [134.51572265625012, 7.525781250000037],\n                        [134.65117187500002, 7.712109374999983],\n                        [134.5954101562501, 7.382031249999969]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Palau\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [153.53613281249997, -11.476171874999949],\n                            [153.75986328125006, -11.586328125],\n                            [153.55371093749997, -11.630566406249969],\n                            [153.28681640625004, -11.516992187500009],\n                            [153.20361328124997, -11.32412109374998],\n                            [153.53613281249997, -11.476171874999949]\n                        ]\n                    ],\n                    [\n                        [\n                            [154.28076171874997, -11.36142578125002],\n                            [154.12119140625006, -11.425683593749966],\n                            [154.02343750000003, -11.347949218750031],\n                            [154.28076171874997, -11.36142578125002]\n                        ]\n                    ],\n                    [\n                        [\n                            [150.89873046875002, -10.565332031250023],\n                            [150.88466796875, -10.643457031250037],\n                            [150.78574218750006, -10.603417968749966],\n                            [150.89873046875002, -10.565332031250023]\n                        ]\n                    ],\n                    [\n                        [\n                            [151.08095703125, -10.020117187499963],\n                            [151.29648437500012, -9.956738281250026],\n                            [151.230859375, -10.194726562500009],\n                            [150.95917968750004, -10.092578124999989],\n                            [150.77607421875004, -9.70908203125002],\n                            [151.08095703125, -10.020117187499963]\n                        ]\n                    ],\n                    [\n                        [\n                            [150.52841796875006, -9.34658203124998],\n                            [150.78867187500006, -9.417968749999957],\n                            [150.89404296875003, -9.667480468749986],\n                            [150.43623046875004, -9.624609374999949],\n                            [150.5084960937501, -9.536132812499957],\n                            [150.43730468750007, -9.359960937500034],\n                            [150.52841796875006, -9.34658203124998]\n                        ]\n                    ],\n                    [\n                        [\n                            [150.3454101562501, -9.493847656249955],\n                            [150.10976562500005, -9.361914062499991],\n                            [150.20830078125002, -9.206347656250003],\n                            [150.32011718750007, -9.264160156249972],\n                            [150.3454101562501, -9.493847656249955]\n                        ]\n                    ],\n                    [\n                        [\n                            [152.63095703125012, -8.959375],\n                            [152.95292968750007, -9.07011718749996],\n                            [152.96689453125006, -9.208984375000014],\n                            [152.51513671874997, -9.009863281250034],\n                            [152.63095703125012, -8.959375]\n                        ]\n                    ],\n                    [\n                        [\n                            [151.10683593750005, -8.733496093749949],\n                            [151.12412109375012, -8.804882812500011],\n                            [151.00498046875006, -8.523828124999952],\n                            [151.117578125, -8.41884765624998],\n                            [151.10683593750005, -8.733496093749949]\n                        ]\n                    ],\n                    [\n                        [\n                            [143.58681640625005, -8.481738281250003],\n                            [143.321875, -8.367578125],\n                            [143.5814453125, -8.390917968749974],\n                            [143.58681640625005, -8.481738281250003]\n                        ]\n                    ],\n                    [\n                        [\n                            [148.02578125, -5.826367187500011],\n                            [147.78105468750007, -5.627246093749946],\n                            [147.7946289062501, -5.492382812500011],\n                            [148.05478515625006, -5.61152343750004],\n                            [148.02578125, -5.826367187500011]\n                        ]\n                    ],\n                    [\n                        [\n                            [155.95761718750006, -6.686816406249989],\n                            [155.71933593750012, -6.862792968749957],\n                            [155.34404296875007, -6.721679687499986],\n                            [155.20214843750003, -6.3076171875],\n                            [154.75927734375003, -5.931347656249997],\n                            [154.72929687500002, -5.444433593750006],\n                            [155.09384765625006, -5.620214843750034],\n                            [155.46699218750004, -6.145117187500034],\n                            [155.82255859375002, -6.38046875000002],\n                            [155.95761718750006, -6.686816406249989]\n                        ]\n                    ],\n                    [\n                        [\n                            [147.17626953124997, -5.431933593749946],\n                            [147.00585937499997, -5.30703125],\n                            [147.1310546875001, -5.190820312500037],\n                            [147.17626953124997, -5.431933593749946]\n                        ]\n                    ],\n                    [\n                        [\n                            [154.64726562500002, -5.43271484375002],\n                            [154.54003906250003, -5.11083984375],\n                            [154.63261718750007, -5.013867187499955],\n                            [154.72714843750006, -5.218066406249989],\n                            [154.64726562500002, -5.43271484375002]\n                        ]\n                    ],\n                    [\n                        [\n                            [146.01933593750007, -4.726171874999963],\n                            [145.88359375000007, -4.66748046875],\n                            [145.9958007812501, -4.539257812499983],\n                            [146.01933593750007, -4.726171874999963]\n                        ]\n                    ],\n                    [\n                        [\n                            [151.915625, -4.296777343749966],\n                            [152.11718749999997, -4.212207031249974],\n                            [152.40566406250005, -4.340722656249952],\n                            [152.35117187500006, -4.82216796874998],\n                            [151.98369140625007, -5.07441406250004],\n                            [152.14296875, -5.357031249999963],\n                            [152.07705078125, -5.458300781249989],\n                            [151.86542968750004, -5.564843750000023],\n                            [151.51513671874997, -5.552343749999963],\n                            [151.22929687500002, -5.919921874999986],\n                            [150.47353515625, -6.263378906249969],\n                            [149.65253906250004, -6.290429687499966],\n                            [149.38232421874997, -6.078125],\n                            [149.0990234375, -6.116992187499989],\n                            [148.33720703125007, -5.669433593750014],\n                            [148.43203125, -5.471777343749991],\n                            [149.35888671875003, -5.583984375000014],\n                            [149.8314453125, -5.524121093749997],\n                            [149.96279296875, -5.447753906249972],\n                            [150.0900390625001, -5.011816406249977],\n                            [150.1703125, -5.070605468749974],\n                            [150.0724609375001, -5.309570312499986],\n                            [150.18310546874997, -5.523632812499983],\n                            [150.90029296875005, -5.447167968750037],\n                            [151.32656250000005, -4.96035156249998],\n                            [151.67119140625007, -4.88330078125],\n                            [151.59306640625007, -4.200781249999949],\n                            [151.915625, -4.296777343749966]\n                        ]\n                    ],\n                    [\n                        [\n                            [152.67060546875004, -3.13339843750002],\n                            [152.64619140625004, -3.221191406249957],\n                            [152.54326171875002, -3.095605468749952],\n                            [152.63876953125012, -3.042773437500031],\n                            [152.67060546875004, -3.13339843750002]\n                        ]\n                    ],\n                    [\n                        [\n                            [140.97617187500012, -9.11875],\n                            [140.97519531250006, -6.90537109375002],\n                            [140.86230468749997, -6.740039062499989],\n                            [140.975, -6.346093750000023],\n                            [140.97353515625, -2.803417968750026],\n                            [140.97343750000007, -2.609765625],\n                            [142.90517578125, -3.32070312499998],\n                            [143.50898437500004, -3.431152343750014],\n                            [144.06640625000003, -3.80517578125],\n                            [144.4777343750001, -3.82529296875002],\n                            [145.08779296875, -4.349121093749972],\n                            [145.33457031250012, -4.385253906249972],\n                            [145.7669921875, -4.823046874999989],\n                            [145.74521484375012, -5.402441406249977],\n                            [147.56669921875002, -6.056933593750003],\n                            [147.80205078125002, -6.31523437499996],\n                            [147.84550781250007, -6.662402343749989],\n                            [147.11914062499997, -6.721679687499986],\n                            [146.95361328124997, -6.834082031249963],\n                            [147.19003906250012, -7.378125],\n                            [148.12675781250007, -8.103613281249963],\n                            [148.246875, -8.554296875000034],\n                            [148.45117187499997, -8.694531250000011],\n                            [148.58310546875006, -9.051757812499957],\n                            [149.19833984375006, -9.03125],\n                            [149.26318359374997, -9.497851562499974],\n                            [150.01103515625007, -9.688183593750026],\n                            [149.76123046874997, -9.805859375000011],\n                            [149.87441406250005, -10.012988281250031],\n                            [150.84951171875, -10.236035156249997],\n                            [150.44609375000007, -10.30732421875004],\n                            [150.6471679687501, -10.517968749999966],\n                            [150.31992187500012, -10.654882812499963],\n                            [150.0167968750001, -10.577148437500028],\n                            [149.75410156250004, -10.353027343750028],\n                            [147.76865234375012, -10.070117187500031],\n                            [147.01718750000006, -9.38789062500004],\n                            [146.96376953125, -9.059570312499943],\n                            [146.63085937499997, -8.951171874999972],\n                            [146.03320312499997, -8.076367187500011],\n                            [144.97382812500004, -7.802148437500009],\n                            [144.86425781249997, -7.631542968749983],\n                            [144.50986328125006, -7.567382812499972],\n                            [144.14287109375007, -7.757226562500009],\n                            [143.65488281250012, -7.460351562500009],\n                            [143.94228515625005, -7.944238281250009],\n                            [143.8333984375, -8.029101562499974],\n                            [143.51816406250006, -8.000683593749955],\n                            [143.61376953125003, -8.200390624999969],\n                            [142.52412109375004, -8.32167968749998],\n                            [142.34746093750002, -8.167480468750014],\n                            [142.20683593750002, -8.195800781250014],\n                            [142.47480468750004, -8.369433593750031],\n                            [142.79794921875006, -8.345019531250031],\n                            [143.11181640624997, -8.474511718750037],\n                            [143.37724609375007, -8.762207031250028],\n                            [143.36621093750003, -8.961035156250034],\n                            [142.6471679687501, -9.327832031249969],\n                            [142.22958984375012, -9.169921874999957],\n                            [141.13320312500005, -9.221289062500034],\n                            [140.97617187500012, -9.11875]\n                        ]\n                    ],\n                    [\n                        [\n                            [152.96582031249997, -4.756347656249986],\n                            [152.89169921875006, -4.832421875000023],\n                            [152.73994140625004, -4.635839843750034],\n                            [152.66816406250004, -4.131835937500028],\n                            [152.27939453125006, -3.582421875],\n                            [151.06679687500005, -2.829003906249994],\n                            [150.74609374999997, -2.73886718750002],\n                            [150.8253906250001, -2.572949218749969],\n                            [152.03291015625004, -3.25136718749998],\n                            [153.01679687500004, -4.105664062500026],\n                            [153.1325195312501, -4.352441406250037],\n                            [152.96582031249997, -4.756347656249986]\n                        ]\n                    ],\n                    [\n                        [\n                            [150.43662109375012, -2.66181640625004],\n                            [150.16572265625004, -2.660253906249991],\n                            [149.96162109375004, -2.473828125000026],\n                            [150.22714843750006, -2.384179687499966],\n                            [150.42949218750007, -2.47041015625004],\n                            [150.43662109375012, -2.66181640625004]\n                        ]\n                    ],\n                    [\n                        [\n                            [147.06757812500004, -1.96015625],\n                            [147.43808593750012, -2.05898437499998],\n                            [147.20634765625007, -2.181933593749974],\n                            [146.54648437500012, -2.20859375],\n                            [146.65625, -1.97402343749998],\n                            [147.06757812500004, -1.96015625]\n                        ]\n                    ],\n                    [\n                        [\n                            [149.76542968750007, -1.553027343750017],\n                            [149.54589843749997, -1.471679687499957],\n                            [149.58095703125005, -1.353222656249983],\n                            [149.76542968750007, -1.553027343750017]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Papua New Guinea\", \"childNum\": 21 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [23.484667968750017, 53.939794921875],\n                        [23.915429687500023, 52.770263671875],\n                        [23.175097656250017, 52.28662109375],\n                        [23.652441406250006, 52.040380859375],\n                        [23.605273437500017, 51.517919921875],\n                        [23.664453125000023, 51.31005859375],\n                        [24.095800781250006, 50.87275390625],\n                        [23.9970703125, 50.809375],\n                        [24.089941406250006, 50.53046875],\n                        [23.97265625, 50.410058593749994],\n                        [23.711718750000017, 50.37734375],\n                        [23.03632812500001, 49.899072265624994],\n                        [22.706152343750006, 49.606201171875],\n                        [22.6494140625, 49.539013671875],\n                        [22.66064453125, 49.483691406249996],\n                        [22.71992187500001, 49.353808593749996],\n                        [22.732421875, 49.295166015625],\n                        [22.705664062500006, 49.171191406249996],\n                        [22.847070312500023, 49.08125],\n                        [22.538671875, 49.072705078125],\n                        [22.473046875000023, 49.081298828125],\n                        [22.020117187500006, 49.209521484374996],\n                        [21.6396484375, 49.411962890625],\n                        [21.079394531250017, 49.418261718749996],\n                        [20.868457031250017, 49.314697265625],\n                        [20.36298828125001, 49.38525390625],\n                        [20.0576171875, 49.181298828124994],\n                        [19.756640625000017, 49.204394531249996],\n                        [19.77392578125, 49.37216796875],\n                        [19.44160156250001, 49.597705078124996],\n                        [19.1494140625, 49.4],\n                        [18.83222656250001, 49.510791015624996],\n                        [18.562402343750023, 49.879345703125],\n                        [18.0283203125, 50.03525390625],\n                        [17.874804687500017, 49.972265625],\n                        [17.627050781250006, 50.11640625],\n                        [17.702246093750006, 50.307177734374996],\n                        [17.41523437500001, 50.254785156249994],\n                        [16.88007812500001, 50.427050781249996],\n                        [16.989648437500023, 50.2369140625],\n                        [16.63916015625, 50.1021484375],\n                        [16.210351562500023, 50.423730468749994],\n                        [16.419726562500017, 50.573632812499994],\n                        [16.2822265625, 50.655615234375],\n                        [16.007226562500023, 50.611621093749996],\n                        [14.99375, 51.01435546875],\n                        [14.98291015625, 50.886572265625],\n                        [14.895800781250017, 50.861376953124996],\n                        [14.809375, 50.858984375],\n                        [14.814257812500017, 50.871630859374996],\n                        [14.91748046875, 51.008740234375],\n                        [14.9638671875, 51.095117187499994],\n                        [14.935546875, 51.435351562499996],\n                        [14.905957031250011, 51.463330078125],\n                        [14.724707031250006, 51.523876953125],\n                        [14.7109375, 51.544921875],\n                        [14.738671875000023, 51.6271484375],\n                        [14.601660156250006, 51.832373046875],\n                        [14.752539062500006, 52.081835937499996],\n                        [14.679882812500011, 52.25],\n                        [14.615625, 52.277636718749996],\n                        [14.573925781250011, 52.31416015625],\n                        [14.554589843750023, 52.359667968749996],\n                        [14.569726562500023, 52.431103515625],\n                        [14.619433593750017, 52.528515625],\n                        [14.514062500000023, 52.64560546875],\n                        [14.253710937500017, 52.782519531249996],\n                        [14.128613281250011, 52.878222656249996],\n                        [14.138867187500011, 52.932861328125],\n                        [14.293164062500011, 53.0267578125],\n                        [14.368554687500023, 53.10556640625],\n                        [14.410937500000017, 53.199023437499996],\n                        [14.412304687500011, 53.216748046875],\n                        [14.41455078125, 53.283496093749996],\n                        [14.258886718750006, 53.729638671875],\n                        [14.58349609375, 53.63935546875],\n                        [14.558398437500017, 53.823193359375],\n                        [14.21142578125, 53.950341796875],\n                        [16.186328125000017, 54.290380859375],\n                        [16.55976562500001, 54.55380859375],\n                        [18.32343750000001, 54.838183593749996],\n                        [18.75927734375, 54.6845703125],\n                        [18.43623046875001, 54.7447265625],\n                        [18.83642578125, 54.369580078125],\n                        [19.604394531250023, 54.4591796875],\n                        [20.20820312500001, 54.420751953125],\n                        [22.16845703125, 54.35986328125],\n                        [22.731835937500023, 54.35009765625],\n                        [22.766210937500006, 54.356787109375],\n                        [22.82373046875, 54.395800781249996],\n                        [22.893945312500023, 54.39052734375],\n                        [22.97675781250001, 54.366357421875],\n                        [23.015527343750023, 54.34833984375],\n                        [23.04218750000001, 54.30419921875],\n                        [23.0875, 54.299462890625],\n                        [23.170312500000023, 54.2814453125],\n                        [23.282324218750006, 54.24033203125],\n                        [23.3701171875, 54.200488281249996],\n                        [23.45361328125, 54.14345703125],\n                        [23.484667968750017, 53.939794921875]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Poland\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-65.42558593749999, 18.105615234374994],\n                            [-65.57221679687493, 18.137304687499977],\n                            [-65.29487304687501, 18.133349609375045],\n                            [-65.42558593749999, 18.105615234374994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-66.12939453125003, 18.444921875000034],\n                            [-65.62880859375, 18.381396484375045],\n                            [-65.62084960937497, 18.242333984374966],\n                            [-65.97080078124995, 17.974365234375],\n                            [-67.196875, 17.994189453125045],\n                            [-67.2640625, 18.364599609375006],\n                            [-67.15864257812501, 18.499218749999983],\n                            [-66.12939453125003, 18.444921875000034]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Puerto Rico\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [130.52695312500012, 42.535400390625],\n                        [130.68730468750007, 42.30253906249999],\n                        [130.2357421875, 42.183203125000034],\n                        [129.75634765624997, 41.712255859375006],\n                        [129.70869140625004, 40.857324218749994],\n                        [129.34111328125002, 40.72631835937506],\n                        [128.51123046874997, 40.130224609375006],\n                        [127.56816406250002, 39.78198242187503],\n                        [127.39453125000003, 39.207910156249966],\n                        [127.78613281250003, 39.084130859374966],\n                        [128.37460937500012, 38.6234375],\n                        [128.03896484375, 38.30854492187498],\n                        [127.09033203125003, 38.28388671875001],\n                        [126.63388671875012, 37.78183593750006],\n                        [126.36992187500007, 37.87836914062501],\n                        [126.11669921875003, 37.74291992187503],\n                        [125.76914062500006, 37.98535156250003],\n                        [125.35781250000005, 37.72480468749998],\n                        [125.31074218750004, 37.843505859375],\n                        [124.98876953124997, 37.93144531249999],\n                        [125.2067382812501, 38.08154296875],\n                        [124.69091796874997, 38.12919921875002],\n                        [125.06738281250003, 38.556738281250006],\n                        [125.55449218750002, 38.68623046875001],\n                        [125.16884765625, 38.80551757812506],\n                        [125.40966796875003, 39.28837890625002],\n                        [125.36083984375003, 39.52661132812497],\n                        [124.77529296875, 39.75805664062506],\n                        [124.63828125000006, 39.61508789062506],\n                        [124.36210937500002, 40.004052734374994],\n                        [124.8893554687501, 40.459814453125006],\n                        [125.98906250000002, 40.904638671875034],\n                        [126.74306640625, 41.724853515625],\n                        [126.95478515625004, 41.76948242187501],\n                        [127.17968750000003, 41.531347656250006],\n                        [128.14941406249997, 41.38774414062496],\n                        [128.28925781250004, 41.60742187500006],\n                        [128.04521484375007, 41.9875],\n                        [128.92343750000006, 42.038232421874966],\n                        [129.3136718750001, 42.41357421874997],\n                        [129.69785156250012, 42.448144531249994],\n                        [129.89824218750002, 42.998144531250034],\n                        [130.24033203125006, 42.891796874999955],\n                        [130.24667968750012, 42.744824218749955],\n                        [130.52695312500012, 42.535400390625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Dem. Rep. Korea\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-17.190869140624926, 32.86860351562498],\n                            [-16.693261718749966, 32.75800781250001],\n                            [-17.018261718749926, 32.66279296874998],\n                            [-17.226025390624983, 32.76684570312503],\n                            [-17.190869140624926, 32.86860351562498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-25.64897460937499, 37.840917968750006],\n                            [-25.18193359374996, 37.837890625],\n                            [-25.19072265624999, 37.764355468749955],\n                            [-25.73447265624992, 37.76289062500001],\n                            [-25.845898437499983, 37.89404296875],\n                            [-25.64897460937499, 37.840917968750006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-28.14726562499996, 38.45268554687502],\n                            [-28.064794921875034, 38.412744140624966],\n                            [-28.454492187500023, 38.40864257812504],\n                            [-28.54882812499997, 38.51855468750003],\n                            [-28.14726562499996, 38.45268554687502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-28.641308593749983, 38.525],\n                            [-28.842041015625, 38.5984375],\n                            [-28.69775390625, 38.638476562500045],\n                            [-28.641308593749983, 38.525]\n                        ]\n                    ],\n                    [\n                        [\n                            [-27.07524414062496, 38.643457031249994],\n                            [-27.38593750000001, 38.765820312499955],\n                            [-27.127001953125017, 38.78984375],\n                            [-27.07524414062496, 38.643457031249994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-31.137109374999937, 39.40693359375001],\n                            [-31.282958984375, 39.39409179687496],\n                            [-31.260839843750034, 39.49677734375001],\n                            [-31.137109374999937, 39.40693359375001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-7.406152343749937, 37.17944335937497],\n                            [-7.834130859374994, 37.005712890625034],\n                            [-8.597656249999943, 37.12133789062506],\n                            [-8.997802734375028, 37.03227539062502],\n                            [-8.814160156249983, 37.43081054687502],\n                            [-8.881103515624943, 38.44667968750005],\n                            [-8.668310546874949, 38.42431640625003],\n                            [-8.798876953124989, 38.518164062500034],\n                            [-9.213281249999937, 38.44809570312498],\n                            [-9.250390624999966, 38.65673828125003],\n                            [-9.021484374999943, 38.746875],\n                            [-8.79160156249992, 39.07817382812502],\n                            [-9.13579101562496, 38.74277343749998],\n                            [-9.35673828124996, 38.697900390624994],\n                            [-9.479736328124972, 38.79877929687501],\n                            [-9.374755859374972, 39.338281249999966],\n                            [-8.837841796874926, 40.11567382812498],\n                            [-8.684619140624989, 40.75253906250006],\n                            [-8.755419921874932, 41.69838867187502],\n                            [-8.887597656249937, 41.76459960937501],\n                            [-8.777148437500017, 41.941064453124994],\n                            [-8.266064453124983, 42.13740234375001],\n                            [-8.152490234374937, 41.81196289062498],\n                            [-7.40361328124996, 41.833691406249955],\n                            [-7.147119140625023, 41.98115234374998],\n                            [-6.61826171874992, 41.9423828125],\n                            [-6.542187499999955, 41.672509765624994],\n                            [-6.2125, 41.53203125],\n                            [-6.928466796874972, 41.009130859375006],\n                            [-6.8101562499999, 40.343115234375034],\n                            [-7.032617187499966, 40.16791992187498],\n                            [-6.896093749999949, 40.02182617187506],\n                            [-6.975390624999932, 39.79838867187502],\n                            [-7.117675781249972, 39.681689453125045],\n                            [-7.53569335937496, 39.66157226562501],\n                            [-6.997949218749994, 39.05644531250002],\n                            [-7.343017578124943, 38.45742187500002],\n                            [-7.106396484374983, 38.181005859375006],\n                            [-6.957568359374932, 38.18789062499999],\n                            [-7.44394531249992, 37.72827148437497],\n                            [-7.406152343749937, 37.17944335937497]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Portugal\", \"childNum\": 7, \"cp\": [-8.7440694, 39.9251454] }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-58.15976562499999, -20.164648437500006],\n                        [-58.13779296874999, -20.2373046875],\n                        [-58.12460937499999, -20.29345703125],\n                        [-58.09150390625, -20.33320312500001],\n                        [-58.05844726562499, -20.38613281250001],\n                        [-58.025390625, -20.415820312500003],\n                        [-58.00224609374999, -20.465429687500006],\n                        [-57.97905273437499, -20.657324218750006],\n                        [-57.91513671874999, -20.69033203125001],\n                        [-57.830224609374994, -20.99794921875001],\n                        [-57.94267578124999, -21.79833984375],\n                        [-57.95590820312499, -22.109179687500003],\n                        [-56.77519531249999, -22.261328125],\n                        [-56.44780273437499, -22.076171875],\n                        [-56.39487304687499, -22.09267578125001],\n                        [-56.35185546874999, -22.17861328125001],\n                        [-56.246044921875, -22.2646484375],\n                        [-56.18984375, -22.28115234375001],\n                        [-55.99140625, -22.28115234375001],\n                        [-55.84916992187499, -22.3076171875],\n                        [-55.75327148437499, -22.41015625],\n                        [-55.74663085937499, -22.5126953125],\n                        [-55.61767578125, -22.671484375],\n                        [-55.53828125, -23.58095703125001],\n                        [-55.518457031249994, -23.627246093750003],\n                        [-55.458886718749994, -23.68671875000001],\n                        [-55.4423828125, -23.792578125],\n                        [-55.4423828125, -23.865332031250006],\n                        [-55.415917968749994, -23.95136718750001],\n                        [-55.36630859374999, -23.991015625],\n                        [-55.28691406249999, -24.004296875],\n                        [-55.1943359375, -24.01748046875001],\n                        [-55.08188476562499, -23.99765625],\n                        [-54.982666015625, -23.97451171875001],\n                        [-54.62548828125, -23.8125],\n                        [-54.44023437499999, -23.90175781250001],\n                        [-54.37080078125, -23.97119140625],\n                        [-54.24179687499999, -24.047265625],\n                        [-54.281005859375, -24.30605468750001],\n                        [-54.43623046875, -25.12128906250001],\n                        [-54.47314453125, -25.22021484375],\n                        [-54.610546875, -25.432714843750006],\n                        [-54.615869140624994, -25.57607421875001],\n                        [-54.63193359374999, -26.00576171875001],\n                        [-54.677734375, -26.30878906250001],\n                        [-54.934472656249994, -26.70253906250001],\n                        [-55.1359375, -26.93115234375],\n                        [-55.426660156249994, -27.00927734375],\n                        [-55.450634765625, -27.068359375],\n                        [-55.496728515624994, -27.115332031250006],\n                        [-55.564892578125, -27.15],\n                        [-55.59726562499999, -27.207617187500006],\n                        [-55.59379882812499, -27.2880859375],\n                        [-55.63291015624999, -27.35712890625001],\n                        [-55.71464843749999, -27.41484375],\n                        [-55.789990234375, -27.41640625],\n                        [-55.95146484374999, -27.32568359375],\n                        [-56.1640625, -27.32148437500001],\n                        [-56.437158203124994, -27.553808593750006],\n                        [-58.16826171874999, -27.2734375],\n                        [-58.60483398437499, -27.31435546875001],\n                        [-58.641748046874994, -27.19609375],\n                        [-58.618603515625, -27.132128906250003],\n                        [-58.222070312499994, -26.65],\n                        [-58.18149414062499, -26.307421875],\n                        [-57.943115234375, -26.05292968750001],\n                        [-57.563134765624994, -25.473730468750006],\n                        [-57.821679687499994, -25.13642578125001],\n                        [-59.187255859375, -24.562304687500003],\n                        [-59.892480468749994, -24.093554687500003],\n                        [-60.83984375, -23.85810546875001],\n                        [-61.084716796875, -23.65644531250001],\n                        [-61.79853515625, -23.18203125],\n                        [-62.21416015624999, -22.612402343750006],\n                        [-62.372509765625, -22.43916015625001],\n                        [-62.54155273437499, -22.349609375],\n                        [-62.6259765625, -22.29042968750001],\n                        [-62.62568359375, -22.261523437500003],\n                        [-62.65097656249999, -22.233691406250003],\n                        [-62.27666015624999, -21.066015625],\n                        [-62.276318359375, -20.5625],\n                        [-61.7568359375, -19.6453125],\n                        [-60.00737304687499, -19.29755859375001],\n                        [-59.09052734375, -19.286230468750006],\n                        [-58.18017578125, -19.81787109375],\n                        [-58.15976562499999, -20.164648437500006]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Paraguay\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [34.34833984375004, 31.292919921874955],\n                            [34.2453125000001, 31.208300781250045],\n                            [34.2125, 31.292285156250017],\n                            [34.198144531249994, 31.322607421875063],\n                            [34.47734375000002, 31.584863281250023],\n                            [34.52412109375004, 31.541650390624994],\n                            [34.5255859375001, 31.52563476562503],\n                            [34.34833984375004, 31.292919921874955]\n                        ]\n                    ],\n                    [\n                        [\n                            [34.88046875, 31.3681640625],\n                            [34.950976562500074, 31.60229492187503],\n                            [35.20371093750006, 31.75],\n                            [35.1271484375001, 31.816748046875006],\n                            [35.05322265625003, 31.83793945312496],\n                            [34.983007812500006, 31.816796875000023],\n                            [34.9611328125001, 31.823339843750006],\n                            [34.95380859375004, 31.84125976562504],\n                            [34.98974609374997, 31.913281249999955],\n                            [34.955957031249994, 32.1609375],\n                            [35.01054687500002, 32.33818359375002],\n                            [35.06503906250006, 32.46044921875006],\n                            [35.19326171875005, 32.53442382812503],\n                            [35.303808593750006, 32.512939453125],\n                            [35.38671875000003, 32.493017578125034],\n                            [35.402636718750074, 32.45063476562501],\n                            [35.484375, 32.40166015624999],\n                            [35.5514648437501, 32.39550781250006],\n                            [35.57207031250002, 32.237890625],\n                            [35.450585937499994, 31.479296875000017],\n                            [34.88046875, 31.3681640625]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Palestine\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-149.321533203125, -17.690039062499963],\n                            [-149.177685546875, -17.736621093750045],\n                            [-149.18178710937497, -17.86230468749997],\n                            [-149.34111328125, -17.732421874999986],\n                            [-149.57890624999993, -17.734960937499963],\n                            [-149.635009765625, -17.564257812500003],\n                            [-149.37919921874993, -17.522363281249994],\n                            [-149.321533203125, -17.690039062499963]\n                        ]\n                    ],\n                    [\n                        [\n                            [-143.44057617187497, -16.619726562499963],\n                            [-143.38618164062498, -16.668847656250023],\n                            [-143.55068359375002, -16.62109374999997],\n                            [-143.44057617187497, -16.619726562499963]\n                        ]\n                    ],\n                    [\n                        [\n                            [-139.02431640624997, -9.695214843750037],\n                            [-138.82734375, -9.74160156249998],\n                            [-139.13408203124996, -9.829492187500037],\n                            [-139.02431640624997, -9.695214843750037]\n                        ]\n                    ],\n                    [\n                        [\n                            [-140.075634765625, -9.425976562499983],\n                            [-140.14438476562498, -9.359375],\n                            [-140.07094726562497, -9.328125],\n                            [-140.075634765625, -9.425976562499983]\n                        ]\n                    ],\n                    [\n                        [\n                            [-140.07260742187503, -8.910449218750031],\n                            [-140.21743164062497, -8.929687499999957],\n                            [-140.24003906249993, -8.79755859375004],\n                            [-140.057666015625, -8.801464843750026],\n                            [-140.07260742187503, -8.910449218750031]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Fr. Polynesia\", \"childNum\": 5 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [51.26796875000002, 24.607226562500003],\n                        [51.17802734375002, 24.58671875],\n                        [51.093359375, 24.564648437499997],\n                        [51.02275390625002, 24.565234375],\n                        [50.96601562500001, 24.573925781249997],\n                        [50.928320312500006, 24.595117187499994],\n                        [50.85566406250001, 24.679638671874997],\n                        [50.80439453125001, 24.789257812499997],\n                        [50.8359375, 24.850390625],\n                        [50.846777343750006, 24.888574218749994],\n                        [50.75458984375001, 25.39926757812499],\n                        [51.003125, 25.9814453125],\n                        [51.262304687500006, 26.153271484374997],\n                        [51.543066406250006, 25.902392578125003],\n                        [51.4853515625, 25.524707031250003],\n                        [51.60888671875, 25.052880859374994],\n                        [51.42792968750001, 24.668261718750003],\n                        [51.26796875000002, 24.607226562500003]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Qatar\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [28.2125, 45.450439453125],\n                        [28.317675781250017, 45.347119140625],\n                        [28.451269531250006, 45.2921875],\n                        [28.78828125000001, 45.240966796875],\n                        [28.78173828125, 45.309863281249996],\n                        [28.894335937500017, 45.289941406249994],\n                        [29.223535156250023, 45.4029296875],\n                        [29.403710937500023, 45.419677734375],\n                        [29.567675781250017, 45.37080078125],\n                        [29.705859375000017, 45.259912109374994],\n                        [29.557519531250023, 44.843408203124994],\n                        [29.048242187500023, 44.757568359375],\n                        [29.0953125, 44.975048828125],\n                        [28.891503906250023, 44.91865234375],\n                        [28.585351562500023, 43.742236328124996],\n                        [28.221972656250017, 43.772851562499994],\n                        [27.88427734375, 43.987353515624996],\n                        [27.425390625, 44.0205078125],\n                        [27.0869140625, 44.167382812499994],\n                        [26.2158203125, 44.007275390625],\n                        [25.4970703125, 43.670800781249994],\n                        [22.919042968750006, 43.83447265625],\n                        [22.868261718750006, 43.947900390624994],\n                        [23.02851562500001, 44.077978515625],\n                        [22.705078125, 44.23779296875],\n                        [22.687890625000023, 44.248291015625],\n                        [22.494531250000023, 44.435449218749994],\n                        [22.554003906250017, 44.540332031249996],\n                        [22.6201171875, 44.562353515625],\n                        [22.70078125, 44.555517578125],\n                        [22.734375, 44.569921875],\n                        [22.72089843750001, 44.605517578124996],\n                        [22.64208984375, 44.6509765625],\n                        [22.49765625, 44.70625],\n                        [22.350683593750006, 44.676123046875],\n                        [22.200976562500017, 44.560693359374994],\n                        [22.093066406250017, 44.541943359375],\n                        [21.909277343750006, 44.66611328125],\n                        [21.636132812500023, 44.71044921875],\n                        [21.52314453125001, 44.790087890624996],\n                        [21.36005859375001, 44.82666015625],\n                        [21.35791015625, 44.86181640625],\n                        [21.384375, 44.870068359375],\n                        [21.442187500000017, 44.873388671875],\n                        [21.519921875000023, 44.880810546875],\n                        [21.532324218750006, 44.900683593749996],\n                        [21.35703125, 44.990771484374996],\n                        [21.465429687500006, 45.171875],\n                        [21.431445312500017, 45.192529296874994],\n                        [20.794042968750006, 45.46787109375],\n                        [20.775, 45.749804687499996],\n                        [20.760156250000023, 45.758105468749996],\n                        [20.746875, 45.748974609375],\n                        [20.727832031250017, 45.73740234375],\n                        [20.709277343750017, 45.735253906249994],\n                        [20.652734375000023, 45.77939453125],\n                        [20.581152343750006, 45.869482421875],\n                        [20.35859375000001, 45.975488281249994],\n                        [20.241796875, 46.10859375],\n                        [20.28095703125001, 46.1330078125],\n                        [20.508105468750017, 46.166943359375],\n                        [20.613671875000023, 46.13349609375],\n                        [20.76025390625, 46.246240234374994],\n                        [21.121679687500006, 46.282421875],\n                        [21.99970703125001, 47.505029296874994],\n                        [22.87666015625001, 47.947265625],\n                        [23.054785156250006, 48.00654296875],\n                        [23.139453125000017, 48.08740234375],\n                        [23.20263671875, 48.084521484374996],\n                        [23.408203125, 47.989990234375],\n                        [23.628710937500017, 47.995849609375],\n                        [24.578906250000017, 47.931054687499994],\n                        [24.979101562500006, 47.72412109375],\n                        [25.464257812500023, 47.910791015624994],\n                        [25.689257812500017, 47.932470703125],\n                        [25.90869140625, 47.967578125],\n                        [26.162695312500006, 47.992529296875],\n                        [26.236230468750023, 48.064355468749994],\n                        [26.276953125, 48.113232421875],\n                        [26.3056640625, 48.203759765624994],\n                        [26.4423828125, 48.22998046875],\n                        [26.618945312500017, 48.25986328125],\n                        [26.980761718750017, 48.155029296875],\n                        [27.614062500000017, 47.34052734375],\n                        [28.07177734375, 46.978417968749994],\n                        [28.23945312500001, 46.6408203125],\n                        [28.07470703125, 45.598974609375],\n                        [28.2125, 45.450439453125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Romania\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [146.71396484375012, 43.743798828124994],\n                            [146.62197265625, 43.81298828125006],\n                            [146.88408203125002, 43.82915039062496],\n                            [146.71396484375012, 43.743798828124994]\n                        ]\n                    ],\n                    [\n                        [\n                            [146.20761718750006, 44.49765625],\n                            [146.5677734375, 44.44042968749997],\n                            [145.91406249999997, 44.10371093750004],\n                            [145.58681640625, 43.84511718750002],\n                            [145.5558593750001, 43.66459960937502],\n                            [145.46171875000007, 43.870898437500045],\n                            [146.20761718750006, 44.49765625]\n                        ]\n                    ],\n                    [\n                        [\n                            [148.59951171875, 45.317626953125],\n                            [147.91376953125004, 44.99038085937502],\n                            [147.65781250000012, 44.97714843749998],\n                            [146.89746093750003, 44.404296875],\n                            [147.24658203124997, 44.856054687500006],\n                            [147.88554687500007, 45.22563476562499],\n                            [147.9240234375001, 45.38330078125006],\n                            [148.05605468750005, 45.26210937500005],\n                            [148.32421874999997, 45.28242187500001],\n                            [148.8122070312501, 45.510009765625],\n                            [148.83710937500004, 45.36269531250002],\n                            [148.59951171875, 45.317626953125]\n                        ]\n                    ],\n                    [\n                        [\n                            [149.68769531250004, 45.64204101562501],\n                            [149.44707031250002, 45.593359375000034],\n                            [149.9623046875, 46.02192382812504],\n                            [150.553125, 46.208544921875045],\n                            [149.68769531250004, 45.64204101562501]\n                        ]\n                    ],\n                    [\n                        [\n                            [152.00205078125006, 46.89716796874998],\n                            [151.72343750000007, 46.82880859375001],\n                            [152.28886718750007, 47.1421875],\n                            [152.00205078125006, 46.89716796874998]\n                        ]\n                    ],\n                    [\n                        [\n                            [154.81044921875005, 49.31201171875],\n                            [154.61093750000006, 49.29404296874998],\n                            [154.82490234375004, 49.64692382812501],\n                            [154.81044921875005, 49.31201171875]\n                        ]\n                    ],\n                    [\n                        [\n                            [155.9210937500001, 50.30219726562501],\n                            [155.39716796875004, 50.04125976562497],\n                            [155.24306640625, 50.09462890625002],\n                            [155.21835937500012, 50.29785156250003],\n                            [155.68017578124997, 50.400732421875034],\n                            [156.096875, 50.771875],\n                            [155.9210937500001, 50.30219726562501]\n                        ]\n                    ],\n                    [\n                        [\n                            [156.40507812500002, 50.65761718750005],\n                            [156.16796874999997, 50.73188476562498],\n                            [156.37646484374997, 50.86210937499996],\n                            [156.4875, 50.84296874999998],\n                            [156.40507812500002, 50.65761718750005]\n                        ]\n                    ],\n                    [\n                        [\n                            [142.76103515625002, 54.393945312499966],\n                            [143.32470703125003, 52.96308593749998],\n                            [143.15556640625002, 52.08374023437497],\n                            [143.29951171875004, 51.632373046875045],\n                            [143.81601562500006, 50.28261718750002],\n                            [144.71376953125, 48.64028320312502],\n                            [144.04873046875, 49.249169921874994],\n                            [143.73232421875, 49.31201171875],\n                            [143.10498046875003, 49.198828125000034],\n                            [142.57421874999997, 48.07216796875002],\n                            [142.55693359375002, 47.737890625000034],\n                            [143.21767578125005, 46.79487304687504],\n                            [143.48564453125002, 46.752050781250006],\n                            [143.58066406250012, 46.360693359375034],\n                            [143.43164062500003, 46.02866210937498],\n                            [143.28232421875006, 46.55898437500002],\n                            [142.57802734375005, 46.700781250000034],\n                            [142.07714843749997, 45.91704101562499],\n                            [141.83037109375002, 46.451074218749966],\n                            [142.03867187500012, 47.140283203124966],\n                            [141.9640625000001, 47.58745117187502],\n                            [142.18173828125012, 48.01337890625001],\n                            [141.86630859375006, 48.750097656250006],\n                            [142.1422851562501, 49.56914062499999],\n                            [142.06601562500006, 50.630468750000034],\n                            [142.20673828125004, 51.22255859375002],\n                            [141.72236328125004, 51.73632812499997],\n                            [141.66083984375004, 52.27294921874997],\n                            [141.85556640625012, 52.79350585937499],\n                            [141.82353515625007, 53.33950195312502],\n                            [142.1419921875, 53.49560546875003],\n                            [142.52617187500002, 53.44746093749998],\n                            [142.70595703125, 53.89570312499998],\n                            [142.33496093749997, 54.28071289062501],\n                            [142.76103515625002, 54.393945312499966]\n                        ]\n                    ],\n                    [\n                        [\n                            [137.17861328125005, 55.100439453125034],\n                            [137.05527343750006, 54.9267578125],\n                            [136.71464843750002, 54.956152343750034],\n                            [137.17861328125005, 55.100439453125034]\n                        ]\n                    ],\n                    [\n                        [\n                            [137.94052734375012, 55.092626953125034],\n                            [138.20615234375012, 55.03354492187498],\n                            [137.72148437500007, 54.66323242187505],\n                            [137.46269531250002, 54.873388671875034],\n                            [137.23291015624997, 54.79057617187496],\n                            [137.5773437500001, 55.19702148437497],\n                            [137.94052734375012, 55.092626953125034]\n                        ]\n                    ],\n                    [\n                        [\n                            [21.235742187500023, 55.26411132812498],\n                            [22.072363281250034, 55.06367187499998],\n                            [22.56728515625005, 55.05913085937496],\n                            [22.82470703125, 54.87128906249998],\n                            [22.684472656250023, 54.56293945312504],\n                            [22.679882812500068, 54.493017578125006],\n                            [22.766210937499977, 54.356787109375034],\n                            [22.168457031250057, 54.35986328125006],\n                            [21.14052734375008, 54.39179687499998],\n                            [19.604394531250023, 54.45917968750004],\n                            [19.974511718750023, 54.92119140625002],\n                            [20.520312500000017, 54.994873046875],\n                            [20.89980468750008, 55.286669921875045],\n                            [20.957812500000074, 55.27890625000006],\n                            [20.594824218750006, 54.982373046874955],\n                            [20.995898437500017, 54.90268554687506],\n                            [21.18886718750008, 54.93520507812502],\n                            [21.235742187500023, 55.26411132812498]\n                        ]\n                    ],\n                    [\n                        [\n                            [166.65029296875005, 54.83906249999998],\n                            [166.64511718750006, 54.69409179687503],\n                            [165.75107421875006, 55.294531250000034],\n                            [166.27578125000005, 55.311962890624955],\n                            [166.24804687499997, 55.16542968750002],\n                            [166.65029296875005, 54.83906249999998]\n                        ]\n                    ],\n                    [\n                        [\n                            [150.58994140625006, 59.01875],\n                            [150.47021484375003, 59.05405273437498],\n                            [150.66621093750004, 59.16015625000003],\n                            [150.58994140625006, 59.01875]\n                        ]\n                    ],\n                    [\n                        [\n                            [163.63515625000005, 58.603369140625006],\n                            [163.47138671875004, 58.509375],\n                            [163.7609375000001, 59.01503906250002],\n                            [164.57265625, 59.22114257812501],\n                            [164.61572265624997, 58.885595703125034],\n                            [163.63515625000005, 58.603369140625006]\n                        ]\n                    ],\n                    [\n                        [\n                            [35.8161132812501, 65.18208007812501],\n                            [35.77871093750005, 64.97666015625],\n                            [35.52890625000006, 65.15107421875001],\n                            [35.8161132812501, 65.18208007812501]\n                        ]\n                    ],\n                    [\n                        [\n                            [70.02070312500004, 66.502197265625],\n                            [69.65136718750003, 66.56533203125],\n                            [69.50273437500002, 66.75107421875],\n                            [70.07666015624997, 66.69589843750003],\n                            [70.02070312500004, 66.502197265625]\n                        ]\n                    ],\n                    [\n                        [\n                            [-179.79853515625, 68.9404296875],\n                            [-178.873876953125, 68.75410156249995],\n                            [-178.69262695312503, 68.54599609375],\n                            [-178.09746093750002, 68.4248046875],\n                            [-178.05581054687497, 68.26489257812503],\n                            [-177.79677734374997, 68.33798828125],\n                            [-178.37304687500003, 68.56567382812503],\n                            [-177.52724609375002, 68.29438476562501],\n                            [-177.58920898437503, 68.22421875],\n                            [-175.34521484375, 67.67807617187503],\n                            [-175.37470703124998, 67.35737304687498],\n                            [-175.00268554687494, 67.4375],\n                            [-174.849853515625, 67.34887695312503],\n                            [-174.92490234375, 66.62314453125006],\n                            [-174.503759765625, 66.537939453125],\n                            [-174.39409179687496, 66.34423828124997],\n                            [-174.084765625, 66.47309570312504],\n                            [-174.06503906249998, 66.22958984374998],\n                            [-173.77397460937502, 66.43466796875003],\n                            [-174.23159179687497, 66.63188476562505],\n                            [-174.08642578125, 66.94287109375],\n                            [-174.55009765624993, 67.090625],\n                            [-173.6796875, 67.144775390625],\n                            [-173.15781249999998, 67.06909179687503],\n                            [-173.32353515625, 66.95483398437503],\n                            [-173.25893554687497, 66.84008789062503],\n                            [-173.19301757812497, 66.99360351562504],\n                            [-172.5201171875, 66.952490234375],\n                            [-173.00751953125, 67.06489257812498],\n                            [-171.79555664062502, 66.93173828125003],\n                            [-170.50952148437503, 66.34365234375005],\n                            [-170.604443359375, 66.24892578125002],\n                            [-170.30122070312504, 66.29404296874998],\n                            [-170.24394531250002, 66.16928710937503],\n                            [-169.777880859375, 66.14311523437505],\n                            [-169.83168945312497, 65.99892578124997],\n                            [-170.54067382812497, 65.86542968749995],\n                            [-170.66630859375, 65.62153320312501],\n                            [-171.42153320312502, 65.81035156250002],\n                            [-171.10585937500002, 65.51103515625005],\n                            [-171.90712890625, 65.495947265625],\n                            [-172.78330078124998, 65.68105468749997],\n                            [-172.23281250000002, 65.45571289062497],\n                            [-172.30927734375004, 65.27563476562497],\n                            [-172.66191406249993, 65.24853515625006],\n                            [-172.28603515625002, 65.20571289062502],\n                            [-172.21318359375, 65.04814453124999],\n                            [-173.08579101562498, 64.81733398437495],\n                            [-172.80107421874996, 64.79052734375],\n                            [-172.90087890624994, 64.62885742187501],\n                            [-172.40146484374998, 64.413916015625],\n                            [-172.73916015624997, 64.41225585937502],\n                            [-172.90317382812498, 64.52607421875004],\n                            [-172.96005859375003, 64.32768554687502],\n                            [-173.27548828124998, 64.2896484375],\n                            [-173.327490234375, 64.53955078125003],\n                            [-173.72973632812497, 64.36450195312497],\n                            [-174.57055664062503, 64.7177734375],\n                            [-175.39511718749998, 64.80239257812502],\n                            [-175.85385742187498, 65.01083984375003],\n                            [-176.09326171875, 65.471044921875],\n                            [-177.05625, 65.613623046875],\n                            [-177.48876953125, 65.50371093749999],\n                            [-178.4125, 65.49555664062501],\n                            [-178.93906249999998, 66.03276367187505],\n                            [-178.74672851562497, 66.01367187500006],\n                            [-178.52656250000004, 66.40156250000004],\n                            [-178.86811523437498, 66.18706054687502],\n                            [-179.14340820312503, 66.37504882812505],\n                            [-179.327197265625, 66.16259765625003],\n                            [-179.68330078124998, 66.18413085937505],\n                            [-179.78969726562497, 65.90087890625],\n                            [-179.352099609375, 65.51674804687497],\n                            [-180, 65.06723632812498],\n                            [-180, 65.31196289062501],\n                            [-180, 65.55678710937497],\n                            [-180, 65.80156250000002],\n                            [-180, 66.04628906250002],\n                            [-180, 66.29106445312499],\n                            [-180, 66.53583984375004],\n                            [-180, 66.78056640625005],\n                            [-180, 67.02534179687501],\n                            [-180, 67.27011718750006],\n                            [-180, 67.51484374999998],\n                            [-180, 67.75961914062503],\n                            [-180, 68.00439453124997],\n                            [-180, 68.24912109375],\n                            [-180, 68.49389648437497],\n                            [-180, 68.738671875],\n                            [-179.999951171875, 68.98344726562505],\n                            [-179.79853515625, 68.9404296875]\n                        ]\n                    ],\n                    [\n                        [\n                            [50.265234375, 69.18559570312502],\n                            [49.62626953125002, 68.85971679687498],\n                            [48.91035156250004, 68.74306640625002],\n                            [48.4390625, 68.80488281249998],\n                            [48.319921875, 69.26923828125001],\n                            [48.8449218750001, 69.49472656250003],\n                            [49.22519531250006, 69.51123046875],\n                            [50.265234375, 69.18559570312502]\n                        ]\n                    ],\n                    [\n                        [\n                            [161.46708984375002, 68.90097656250003],\n                            [161.08281250000007, 69.4056640625],\n                            [161.50517578125007, 69.63945312500002],\n                            [161.46708984375002, 68.90097656250003]\n                        ]\n                    ],\n                    [\n                        [\n                            [169.20078125000006, 69.58046875],\n                            [168.34804687500005, 69.66435546875005],\n                            [167.86474609375003, 69.90107421875004],\n                            [168.35791015625003, 70.01567382812502],\n                            [169.37480468750007, 69.88261718749999],\n                            [169.20078125000006, 69.58046875]\n                        ]\n                    ],\n                    [\n                        [\n                            [60.450488281250074, 69.93486328124999],\n                            [60.44023437500002, 69.72592773437506],\n                            [59.637011718750074, 69.72104492187503],\n                            [59.50263671875004, 69.86621093750003],\n                            [58.952734375, 69.89277343750004],\n                            [58.51992187500005, 70.31831054687504],\n                            [59.04804687500004, 70.46049804687505],\n                            [60.450488281250074, 69.93486328124999]\n                        ]\n                    ],\n                    [\n                        [\n                            [52.90332031250003, 71.36499023437503],\n                            [53.19257812500004, 71.21528320312498],\n                            [53.0226562500001, 70.96870117187501],\n                            [52.24960937500006, 71.28491210937506],\n                            [52.90332031250003, 71.36499023437503]\n                        ]\n                    ],\n                    [\n                        [\n                            [178.8615234375001, 70.826416015625],\n                            [178.68388671875013, 71.10566406250004],\n                            [180, 71.53774414062505],\n                            [180, 70.993017578125],\n                            [178.8615234375001, 70.826416015625]\n                        ]\n                    ],\n                    [\n                        [\n                            [137.95986328125005, 71.50766601562503],\n                            [137.71181640625005, 71.4232421875],\n                            [137.06406250000006, 71.52988281250003],\n                            [137.816796875, 71.58789062500006],\n                            [137.95986328125005, 71.50766601562503]\n                        ]\n                    ],\n                    [\n                        [\n                            [-178.87646484375, 71.57705078124997],\n                            [-178.13388671874998, 71.46547851562497],\n                            [-177.523583984375, 71.16689453125],\n                            [-179.415673828125, 70.91899414062502],\n                            [-179.999951171875, 70.993017578125],\n                            [-179.999951171875, 71.53774414062505],\n                            [-178.87646484375, 71.57705078124997]\n                        ]\n                    ],\n                    [\n                        [\n                            [77.6325195312501, 72.291259765625],\n                            [76.87109374999997, 72.317041015625],\n                            [77.74853515625003, 72.63120117187506],\n                            [78.36513671875005, 72.48242187500003],\n                            [77.6325195312501, 72.291259765625]\n                        ]\n                    ],\n                    [\n                        [\n                            [79.50146484374997, 72.72192382812497],\n                            [78.63320312500005, 72.85073242187502],\n                            [79.16425781250004, 73.0943359375],\n                            [79.50146484374997, 72.72192382812497]\n                        ]\n                    ],\n                    [\n                        [\n                            [74.660546875, 72.87343750000002],\n                            [74.18066406250003, 72.975341796875],\n                            [74.19853515625002, 73.10908203124998],\n                            [74.9615234375, 73.0625],\n                            [74.660546875, 72.87343750000002]\n                        ]\n                    ],\n                    [\n                        [\n                            [120.26132812500012, 73.08984374999997],\n                            [119.79208984375006, 73.04541015624997],\n                            [119.64042968750002, 73.12431640625007],\n                            [120.26132812500012, 73.08984374999997]\n                        ]\n                    ],\n                    [\n                        [\n                            [55.31982421875003, 73.30830078124998],\n                            [56.42958984375005, 73.201171875],\n                            [56.121679687500006, 72.80659179687498],\n                            [55.40332031249997, 72.54907226562503],\n                            [55.29785156249997, 71.93535156250005],\n                            [56.45439453125002, 71.10737304687504],\n                            [57.62539062500005, 70.72880859374999],\n                            [57.14589843750005, 70.58911132812506],\n                            [56.38574218749997, 70.73413085937503],\n                            [56.49970703125004, 70.56640625000003],\n                            [55.687304687500074, 70.69218749999999],\n                            [54.60117187500006, 70.68007812500002],\n                            [53.383593750000074, 70.87353515625],\n                            [53.670507812500006, 71.08691406250003],\n                            [54.155664062499994, 71.12548828125],\n                            [53.40996093750002, 71.34013671875002],\n                            [53.41162109375003, 71.530126953125],\n                            [51.93789062500005, 71.47470703124998],\n                            [51.511328125, 71.64809570312497],\n                            [51.58251953124997, 72.07119140625],\n                            [52.252050781250006, 72.12973632812503],\n                            [52.66191406250002, 72.33686523437495],\n                            [52.91660156250006, 72.66889648437501],\n                            [52.5792968750001, 72.791357421875],\n                            [53.3698242187501, 72.91674804687506],\n                            [53.2511718750001, 73.182958984375],\n                            [54.80390625000004, 73.38764648437498],\n                            [55.31982421875003, 73.30830078124998]\n                        ]\n                    ],\n                    [\n                        [\n                            [70.67392578125006, 73.09501953125005],\n                            [70.04072265625004, 73.03715820312507],\n                            [69.99589843750002, 73.359375],\n                            [70.94023437500002, 73.51440429687503],\n                            [71.6261718750001, 73.17397460937497],\n                            [70.67392578125006, 73.09501953125005]\n                        ]\n                    ],\n                    [\n                        [\n                            [142.18486328125007, 73.89589843750005],\n                            [143.34375, 73.56875],\n                            [143.45146484375007, 73.231298828125],\n                            [141.59667968750003, 73.31083984375005],\n                            [140.66279296875004, 73.45200195312503],\n                            [139.785546875, 73.35522460937503],\n                            [141.08476562500002, 73.86586914062497],\n                            [142.18486328125007, 73.89589843750005]\n                        ]\n                    ],\n                    [\n                        [\n                            [83.5490234375001, 74.07177734375],\n                            [82.8177734375, 74.09160156250005],\n                            [83.14980468750005, 74.151611328125],\n                            [83.5490234375001, 74.07177734375]\n                        ]\n                    ],\n                    [\n                        [\n                            [141.01025390625003, 73.99946289062501],\n                            [140.40947265625002, 73.92167968750005],\n                            [140.1935546875001, 74.23671875000002],\n                            [141.03857421875003, 74.24272460937502],\n                            [141.01025390625003, 73.99946289062501]\n                        ]\n                    ],\n                    [\n                        [\n                            [113.38720703124997, 74.40043945312499],\n                            [112.78242187500004, 74.09506835937503],\n                            [111.50341796874997, 74.35307617187502],\n                            [111.87978515625, 74.36381835937499],\n                            [112.08447265624997, 74.54897460937505],\n                            [113.38720703124997, 74.40043945312499]\n                        ]\n                    ],\n                    [\n                        [\n                            [86.653125, 74.981298828125],\n                            [87.05214843750005, 74.982568359375],\n                            [86.92714843750005, 74.83076171874998],\n                            [86.25859375000002, 74.89350585937498],\n                            [86.653125, 74.981298828125]\n                        ]\n                    ],\n                    [\n                        [\n                            [82.17236328125003, 75.41938476562501],\n                            [81.97851562499997, 75.24711914062499],\n                            [81.65478515625003, 75.28891601562498],\n                            [81.71210937500004, 75.45141601562506],\n                            [82.165625, 75.515625],\n                            [82.17236328125003, 75.41938476562501]\n                        ]\n                    ],\n                    [\n                        [\n                            [146.79521484375007, 75.37075195312505],\n                            [148.43242187500002, 75.41352539062495],\n                            [148.59013671875007, 75.23637695312502],\n                            [150.82236328125006, 75.15654296875002],\n                            [150.64628906250002, 74.944580078125],\n                            [149.596875, 74.77260742187505],\n                            [148.296875, 74.80043945312502],\n                            [146.14853515625012, 75.19829101562499],\n                            [146.5375, 75.58178710937506],\n                            [146.79521484375007, 75.37075195312505]\n                        ]\n                    ],\n                    [\n                        [\n                            [135.9486328125, 75.40957031250005],\n                            [135.45195312500007, 75.38955078124997],\n                            [135.6986328125, 75.84526367187499],\n                            [136.16894531249997, 75.60556640625],\n                            [135.9486328125, 75.40957031250005]\n                        ]\n                    ],\n                    [\n                        [\n                            [140.04873046875, 75.82895507812503],\n                            [140.81591796874997, 75.63071289062498],\n                            [141.48544921875012, 76.13715820312495],\n                            [142.66953125000012, 75.86342773437497],\n                            [143.68583984375002, 75.86367187500002],\n                            [145.35996093750006, 75.53046874999998],\n                            [144.01972656250004, 75.04467773437506],\n                            [143.1703125, 75.11689453125001],\n                            [142.72949218749997, 75.33764648437506],\n                            [142.941796875, 75.71328125000002],\n                            [142.30791015625007, 75.69169921875005],\n                            [142.19882812500006, 75.39267578124998],\n                            [143.12792968749997, 74.9703125],\n                            [142.47275390625006, 74.82041015625],\n                            [141.98730468750003, 74.99125976562499],\n                            [140.26787109375002, 74.846923828125],\n                            [139.68125, 74.96406249999995],\n                            [139.09912109374997, 74.65654296875002],\n                            [138.09228515625003, 74.79746093750003],\n                            [136.94765625000005, 75.32553710937498],\n                            [137.28974609375004, 75.34863281249997],\n                            [137.26884765625002, 75.7494140625],\n                            [137.70654296875003, 75.75957031250002],\n                            [137.56054687499997, 75.95522460937502],\n                            [138.20761718750006, 76.11494140624995],\n                            [138.91953125000006, 76.19672851562501],\n                            [140.04873046875, 75.82895507812503]\n                        ]\n                    ],\n                    [\n                        [\n                            [96.5324218750001, 76.278125],\n                            [96.30058593750002, 76.121728515625],\n                            [95.31113281250006, 76.21474609375002],\n                            [95.37988281250003, 76.2890625],\n                            [96.5324218750001, 76.278125]\n                        ]\n                    ],\n                    [\n                        [\n                            [112.47802734375003, 76.62089843749999],\n                            [112.531640625, 76.450048828125],\n                            [111.96894531250004, 76.62617187500001],\n                            [112.47802734375003, 76.62089843749999]\n                        ]\n                    ],\n                    [\n                        [\n                            [149.15019531250002, 76.65991210937506],\n                            [148.39863281250004, 76.64824218750007],\n                            [149.4064453125001, 76.78208007812498],\n                            [149.15019531250002, 76.65991210937506]\n                        ]\n                    ],\n                    [\n                        [\n                            [67.7653320312501, 76.23759765624999],\n                            [61.35595703124997, 75.31484375000002],\n                            [60.27685546875003, 75.00756835937503],\n                            [60.501367187499994, 74.90463867187503],\n                            [59.67402343750004, 74.61015624999999],\n                            [59.24013671875005, 74.69296874999998],\n                            [59.040429687499994, 74.48554687500001],\n                            [58.53466796875003, 74.49892578124997],\n                            [58.6178710937501, 74.22739257812498],\n                            [57.76738281250002, 74.013818359375],\n                            [57.755957031250006, 73.769189453125],\n                            [57.313085937500006, 73.838037109375],\n                            [57.54257812500006, 73.65820312500003],\n                            [56.96386718750003, 73.36655273437503],\n                            [56.43037109375004, 73.29721679687503],\n                            [55.00683593750003, 73.45385742187506],\n                            [54.29990234375006, 73.35097656249997],\n                            [53.7628906250001, 73.76616210937499],\n                            [54.64267578125006, 73.95957031250006],\n                            [55.34091796875006, 74.41962890624998],\n                            [56.13710937500005, 74.49609375000003],\n                            [55.5822265625001, 74.627685546875],\n                            [56.4987304687501, 74.95708007812505],\n                            [55.81005859374997, 75.12490234374997],\n                            [56.03554687499999, 75.19423828124997],\n                            [56.57031250000003, 75.09775390625003],\n                            [56.8444335937501, 75.351416015625],\n                            [57.606835937499994, 75.34125976562498],\n                            [58.05830078125004, 75.6630859375],\n                            [58.88125, 75.85478515625007],\n                            [60.27929687499997, 76.09624023437505],\n                            [60.94218750000002, 76.07128906250003],\n                            [61.20166015624997, 76.28203125000007],\n                            [62.97148437500002, 76.23666992187498],\n                            [64.4634765625, 76.37817382812503],\n                            [67.65185546874997, 77.011572265625],\n                            [68.48574218750005, 76.93369140625003],\n                            [68.94169921875002, 76.707666015625],\n                            [67.7653320312501, 76.23759765624999]\n                        ]\n                    ],\n                    [\n                        [\n                            [96.28544921875002, 77.02666015625007],\n                            [95.27031250000007, 77.01884765624999],\n                            [96.52841796875006, 77.20551757812501],\n                            [96.28544921875002, 77.02666015625007]\n                        ]\n                    ],\n                    [\n                        [\n                            [89.51425781250006, 77.18881835937498],\n                            [89.14169921875012, 77.22680664062497],\n                            [89.61621093749997, 77.31103515625],\n                            [89.51425781250006, 77.18881835937498]\n                        ]\n                    ],\n                    [\n                        [\n                            [130.68730468750007, 42.30253906249999],\n                            [130.52695312500012, 42.535400390625],\n                            [130.42480468749997, 42.72705078124997],\n                            [131.06855468750004, 42.90224609375005],\n                            [131.25732421875003, 43.378076171874994],\n                            [131.2552734375, 44.07158203124999],\n                            [130.9816406250001, 44.844335937500034],\n                            [131.44687500000012, 44.984033203124966],\n                            [131.85185546875002, 45.32685546875001],\n                            [132.93603515624997, 45.029931640624994],\n                            [133.1134765625001, 45.130712890625006],\n                            [133.18603515625003, 45.49482421875004],\n                            [133.43642578125, 45.60468750000004],\n                            [133.86132812500003, 46.24775390625004],\n                            [134.1676757812501, 47.30219726562501],\n                            [134.75234375, 47.71542968749998],\n                            [134.56601562500006, 48.02250976562502],\n                            [134.66523437500004, 48.25390625],\n                            [134.29335937500005, 48.37343750000002],\n                            [133.46835937500006, 48.09716796875003],\n                            [133.14404296875003, 48.10566406249998],\n                            [132.7072265625001, 47.94726562500006],\n                            [132.47626953125004, 47.714990234374994],\n                            [130.96191406249997, 47.70932617187498],\n                            [130.7326171875001, 48.01923828124998],\n                            [130.80429687500012, 48.34150390624998],\n                            [130.5521484375, 48.602490234374955],\n                            [130.553125, 48.861181640625006],\n                            [130.1959960937501, 48.89165039062499],\n                            [129.49814453125012, 49.38881835937502],\n                            [129.0651367187501, 49.374658203124966],\n                            [128.70400390625, 49.60014648437499],\n                            [127.99960937500006, 49.56860351562506],\n                            [127.55078124999997, 49.801806640625045],\n                            [127.590234375, 50.20898437500003],\n                            [127.33720703125007, 50.35014648437502],\n                            [127.30703125000005, 50.70795898437501],\n                            [126.92480468749997, 51.10014648437496],\n                            [126.34169921875, 52.36201171875001],\n                            [125.64902343750012, 53.042285156250045],\n                            [125.075, 53.20366210937496],\n                            [124.81230468750002, 53.133837890625045],\n                            [123.6078125, 53.546533203124994],\n                            [120.98544921875012, 53.28457031250002],\n                            [120.09453125000007, 52.787207031250034],\n                            [120.0675781250001, 52.632910156250034],\n                            [120.65615234375, 52.56665039062503],\n                            [120.74980468750007, 52.096533203125006],\n                            [120.06689453125003, 51.60068359375006],\n                            [119.16367187500006, 50.40600585937503],\n                            [119.34628906250012, 50.278955078124994],\n                            [119.25986328125012, 50.06640625000003],\n                            [118.4515625, 49.84448242187503],\n                            [117.8734375, 49.51347656250002],\n                            [116.6833007812501, 49.82377929687499],\n                            [116.551171875, 49.92031250000002],\n                            [116.35117187500012, 49.97807617187499],\n                            [116.21679687500003, 50.00927734375003],\n                            [116.13457031250002, 50.01079101562499],\n                            [115.9259765625001, 49.95214843750003],\n                            [115.79521484375002, 49.90590820312502],\n                            [115.71777343750003, 49.88061523437503],\n                            [115.58798828125006, 49.88603515624996],\n                            [115.42919921874997, 49.89648437499997],\n                            [115.36503906250002, 49.911767578124966],\n                            [115.27451171875006, 49.948876953124994],\n                            [115.00332031250005, 50.138574218749994],\n                            [114.74316406249997, 50.23369140625002],\n                            [114.29707031250004, 50.27441406250006],\n                            [113.57421874999997, 50.00703125000001],\n                            [113.44550781250004, 49.94160156250001],\n                            [113.31904296875004, 49.87431640624999],\n                            [113.16416015625012, 49.79716796874999],\n                            [113.09208984375007, 49.692529296874994],\n                            [113.05556640625, 49.61625976562499],\n                            [112.91484375000002, 49.569238281249994],\n                            [112.80644531250007, 49.52358398437502],\n                            [112.69736328125012, 49.50727539062498],\n                            [112.49492187500002, 49.532324218750034],\n                            [112.07968750000006, 49.42421875000002],\n                            [111.42929687500006, 49.342626953125034],\n                            [111.3366210937501, 49.355859374999966],\n                            [111.20419921875012, 49.304296875000034],\n                            [110.82792968750002, 49.16616210937505],\n                            [110.70976562500002, 49.14296875000002],\n                            [110.42783203125006, 49.219970703125],\n                            [110.32138671875012, 49.215869140625045],\n                            [110.19990234375004, 49.17041015625003],\n                            [109.5287109375, 49.269873046875034],\n                            [109.45371093750012, 49.29633789062501],\n                            [109.23671875000005, 49.334912109374955],\n                            [108.61367187500005, 49.32280273437499],\n                            [108.52246093750003, 49.34150390624998],\n                            [108.4069335937501, 49.39638671875005],\n                            [107.96542968750012, 49.65351562500004],\n                            [107.91660156250012, 49.947802734375045],\n                            [107.63095703125012, 49.98310546875004],\n                            [107.3470703125, 49.986669921875034],\n                            [107.23330078125, 49.989404296874994],\n                            [107.14306640625003, 50.03300781249999],\n                            [107.04023437500004, 50.086474609375045],\n                            [106.94130859375005, 50.19667968750002],\n                            [106.71113281250004, 50.312597656250006],\n                            [106.57441406250004, 50.32880859375004],\n                            [106.36845703125002, 50.317578124999955],\n                            [106.21787109375006, 50.304589843749966],\n                            [105.38359375000002, 50.47373046874998],\n                            [104.07871093750012, 50.15424804687498],\n                            [103.63291015625006, 50.138574218749994],\n                            [103.49628906250004, 50.16494140625005],\n                            [103.42119140625002, 50.18706054687502],\n                            [103.3043945312501, 50.200292968750034],\n                            [102.28837890625007, 50.58510742187502],\n                            [102.31660156250004, 50.71845703125001],\n                            [102.21503906250004, 50.82944335937506],\n                            [102.19453125000004, 51.05068359375002],\n                            [102.15195312500006, 51.107519531250034],\n                            [102.14238281250007, 51.21606445312503],\n                            [102.16005859375005, 51.260839843750006],\n                            [102.1556640625, 51.31376953124996],\n                            [102.1115234375001, 51.353466796874955],\n                            [101.97919921875004, 51.382226562499966],\n                            [101.82119140625, 51.421044921874966],\n                            [101.57089843750006, 51.46718750000005],\n                            [101.38125, 51.45263671875],\n                            [100.53623046875006, 51.713476562500034],\n                            [100.46894531250004, 51.72607421875003],\n                            [100.23037109375, 51.729833984375006],\n                            [100.0345703125, 51.73710937499996],\n                            [99.92167968750002, 51.755517578124994],\n                            [99.71923828124997, 51.87163085937502],\n                            [98.89316406250006, 52.11728515625006],\n                            [98.64052734375005, 51.80117187500005],\n                            [98.103125, 51.483544921874994],\n                            [97.82529296875012, 50.985253906249994],\n                            [97.953125, 50.85517578124998],\n                            [98.02978515625003, 50.64462890624998],\n                            [98.07890625000002, 50.60380859375002],\n                            [98.14501953124997, 50.56855468750001],\n                            [98.22050781250007, 50.55717773437502],\n                            [98.2794921875001, 50.53325195312502],\n                            [98.25029296875002, 50.30244140624998],\n                            [98.00390625000003, 50.01425781249998],\n                            [97.35976562500005, 49.741455078125],\n                            [97.20859375000006, 49.73081054687506],\n                            [96.98574218750005, 49.88281250000003],\n                            [96.31503906250012, 49.90112304687503],\n                            [96.06552734375006, 49.99873046875001],\n                            [95.52265625000004, 49.911230468750034],\n                            [95.11142578125012, 49.935449218749994],\n                            [94.93027343750006, 50.04375],\n                            [94.8112304687501, 50.04819335937506],\n                            [94.71806640625002, 50.04326171875002],\n                            [94.67548828125004, 50.02807617187506],\n                            [94.61474609375003, 50.02373046874996],\n                            [94.56464843750004, 50.08793945312499],\n                            [94.35468750000004, 50.221826171874994],\n                            [94.25107421875006, 50.55639648437503],\n                            [93.103125, 50.60390625000002],\n                            [92.94130859375005, 50.77822265625002],\n                            [92.85644531250003, 50.78911132812502],\n                            [92.77929687500003, 50.778662109375006],\n                            [92.738671875, 50.71093749999997],\n                            [92.68134765625004, 50.683203125],\n                            [92.6266601562501, 50.68828124999999],\n                            [92.57890625000002, 50.725439453125006],\n                            [92.42636718750006, 50.803076171875006],\n                            [92.35478515625002, 50.86416015625002],\n                            [92.29580078125, 50.84980468750004],\n                            [92.19238281249997, 50.700585937499994],\n                            [91.80429687500006, 50.693603515625],\n                            [91.4464843750001, 50.52216796874998],\n                            [91.41503906249997, 50.46801757812506],\n                            [91.34082031249997, 50.470068359375034],\n                            [91.30058593750002, 50.46337890625],\n                            [91.2337890625, 50.45239257812497],\n                            [91.02158203125012, 50.41547851562501],\n                            [90.83808593750004, 50.32373046874997],\n                            [90.76074218749997, 50.30595703124999],\n                            [90.71435546874997, 50.25942382812502],\n                            [90.65507812500007, 50.22236328125001],\n                            [90.05371093750003, 50.09375],\n                            [89.64384765625002, 49.90302734374998],\n                            [89.65410156250007, 49.71748046875001],\n                            [89.57919921875006, 49.69970703125003],\n                            [89.475, 49.66054687500005],\n                            [89.39560546875006, 49.61152343750001],\n                            [89.24394531250007, 49.62705078125006],\n                            [89.20292968750007, 49.59570312499997],\n                            [89.17998046875002, 49.5322265625],\n                            [89.10947265625012, 49.50136718750002],\n                            [89.00839843750006, 49.472802734374994],\n                            [88.97060546875, 49.483740234375006],\n                            [88.94541015625012, 49.50766601562498],\n                            [88.90019531250002, 49.53969726562502],\n                            [88.86386718750006, 49.52763671874996],\n                            [88.83164062500012, 49.44843749999998],\n                            [88.633203125, 49.486132812500045],\n                            [88.19257812500004, 49.451708984375045],\n                            [88.13554687500002, 49.38149414062502],\n                            [88.11572265624997, 49.25629882812501],\n                            [88.0285156250001, 49.219775390625045],\n                            [87.98808593750002, 49.186914062499994],\n                            [87.9347656250001, 49.16455078124997],\n                            [87.81425781250002, 49.162304687499955],\n                            [87.7625, 49.16582031249996],\n                            [87.5158203125001, 49.122412109375006],\n                            [87.41669921875004, 49.07661132812501],\n                            [87.32285156250012, 49.085791015625006],\n                            [86.62646484374997, 49.56269531250001],\n                            [86.67548828125004, 49.77729492187501],\n                            [86.1808593750001, 49.49931640624996],\n                            [85.2326171875001, 49.61582031249998],\n                            [84.9894531250001, 50.061425781249994],\n                            [84.32324218749997, 50.239160156249966],\n                            [83.94511718750007, 50.774658203125],\n                            [83.35732421875005, 50.99458007812504],\n                            [82.76083984375012, 50.89335937500002],\n                            [82.49394531250007, 50.72758789062499],\n                            [81.46591796875006, 50.73984375],\n                            [81.38828125000006, 50.95649414062501],\n                            [81.0714843750001, 50.96875],\n                            [81.12724609375002, 51.19106445312502],\n                            [80.73525390625, 51.29340820312498],\n                            [80.44804687500002, 51.18334960937503],\n                            [80.42363281250002, 50.94628906249997],\n                            [79.98623046875, 50.774560546874966],\n                            [77.85996093750006, 53.269189453124994],\n                            [76.48476562500005, 54.02255859374998],\n                            [76.42167968750007, 54.151513671874966],\n                            [76.65458984375007, 54.14526367187503],\n                            [76.8373046875, 54.4423828125],\n                            [75.43720703125004, 54.08964843749999],\n                            [75.22021484374997, 53.89379882812506],\n                            [74.45195312500007, 53.64726562500002],\n                            [74.35156250000003, 53.487646484375006],\n                            [73.85898437500006, 53.61972656249998],\n                            [73.40693359375004, 53.44755859374999],\n                            [73.30566406250003, 53.707226562499955],\n                            [73.71240234375003, 54.04238281250002],\n                            [73.22988281250005, 53.957812500000045],\n                            [72.62226562500004, 54.13432617187502],\n                            [72.44677734375003, 53.94184570312498],\n                            [72.18603515625003, 54.32563476562501],\n                            [72.00449218750006, 54.20566406249998],\n                            [71.09316406250005, 54.21220703124999],\n                            [71.18554687500003, 54.59931640624998],\n                            [70.73808593750007, 55.30517578125],\n                            [70.18242187500002, 55.162451171875034],\n                            [68.9772460937501, 55.389599609374955],\n                            [68.20625, 55.16093750000002],\n                            [68.15585937500006, 54.97670898437505],\n                            [65.476953125, 54.62329101562497],\n                            [65.08837890624997, 54.340185546875034],\n                            [64.46123046875002, 54.38417968750002],\n                            [61.92871093750003, 53.94648437500004],\n                            [61.231054687500006, 54.01948242187498],\n                            [60.97949218749997, 53.62172851562505],\n                            [61.53496093750002, 53.52329101562506],\n                            [61.22890625, 53.445898437500006],\n                            [61.19921874999997, 53.28715820312502],\n                            [61.65986328125004, 53.22846679687504],\n                            [62.08271484375004, 53.00541992187499],\n                            [61.047460937500006, 52.97246093750002],\n                            [60.77441406249997, 52.67578124999997],\n                            [60.99453125000005, 52.33686523437504],\n                            [60.03027343749997, 51.93325195312505],\n                            [60.464746093749994, 51.651171875000045],\n                            [61.55468750000003, 51.32460937500005],\n                            [61.38945312500002, 50.86103515625001],\n                            [60.94228515625005, 50.69550781250004],\n                            [60.42480468749997, 50.67915039062498],\n                            [60.05859374999997, 50.850292968749955],\n                            [59.812402343749994, 50.58203125],\n                            [59.523046875, 50.492871093749955],\n                            [59.4523437500001, 50.62041015625002],\n                            [58.88369140625005, 50.694433593750006],\n                            [58.359179687500074, 51.063818359375034],\n                            [57.83886718750003, 51.091650390625006],\n                            [57.44218750000002, 50.88886718749998],\n                            [57.01171874999997, 51.06518554687503],\n                            [56.49140625000004, 51.01953124999997],\n                            [55.68623046875004, 50.582861328125006],\n                            [54.64160156250003, 51.011572265625034],\n                            [54.555273437500006, 50.535791015624994],\n                            [54.139746093750006, 51.04077148437503],\n                            [53.33808593750004, 51.48237304687504],\n                            [52.57119140625005, 51.481640624999955],\n                            [52.21914062499999, 51.709375],\n                            [51.344531250000074, 51.47534179687503],\n                            [51.16347656250005, 51.6474609375],\n                            [50.79394531249997, 51.729199218749955],\n                            [50.246875, 51.28950195312498],\n                            [49.49804687500003, 51.08359375000006],\n                            [49.32343750000004, 50.851708984374966],\n                            [48.625097656250006, 50.61269531250005],\n                            [48.7589843750001, 49.92832031250006],\n                            [48.33496093750003, 49.858251953125006],\n                            [47.7057617187501, 50.37797851562502],\n                            [47.42919921874997, 50.35795898437502],\n                            [46.889550781249994, 49.69697265625001],\n                            [46.80205078125002, 49.36708984375002],\n                            [47.031347656250006, 49.150292968749994],\n                            [46.70263671875003, 48.80556640625002],\n                            [46.660937500000074, 48.41225585937502],\n                            [47.06464843750004, 48.23247070312499],\n                            [47.292382812499994, 47.74091796875004],\n                            [47.48193359374997, 47.80390624999998],\n                            [48.16699218750003, 47.70878906249996],\n                            [48.959375, 46.77460937499998],\n                            [48.558398437500074, 46.75712890624999],\n                            [48.54121093750004, 46.60561523437502],\n                            [49.232226562500074, 46.33715820312503],\n                            [48.683691406250006, 46.08618164062497],\n                            [48.72958984375006, 45.896826171875034],\n                            [48.4870117187501, 45.93486328124996],\n                            [47.63330078124997, 45.58403320312499],\n                            [47.46328125, 45.67968750000003],\n                            [47.5294921875001, 45.530224609374955],\n                            [47.3512695312501, 45.21772460937498],\n                            [46.7072265625001, 44.503320312499994],\n                            [47.30703125000005, 44.103125],\n                            [47.462792968749994, 43.55502929687498],\n                            [47.64648437500003, 43.88461914062498],\n                            [47.463183593750074, 43.03505859375002],\n                            [48.572851562500006, 41.84448242187503],\n                            [47.79101562499997, 41.19926757812502],\n                            [47.31767578125002, 41.28242187500001],\n                            [46.74931640625002, 41.812597656250006],\n                            [46.42988281250004, 41.890966796875006],\n                            [46.21269531250002, 41.989892578124994],\n                            [45.63857421875005, 42.20507812500003],\n                            [45.63427734374997, 42.234716796875034],\n                            [45.72753906249997, 42.47504882812498],\n                            [45.70527343750004, 42.49809570312496],\n                            [45.56289062499999, 42.53574218749998],\n                            [45.34375, 42.52978515625003],\n                            [45.16025390625006, 42.675],\n                            [45.07158203125002, 42.69414062500002],\n                            [44.94335937499997, 42.73027343750002],\n                            [44.870996093749994, 42.75639648437499],\n                            [44.850488281249994, 42.746826171875],\n                            [44.77109375000006, 42.61679687499998],\n                            [44.69179687499999, 42.709619140624966],\n                            [44.64433593750002, 42.734716796875034],\n                            [44.50585937500003, 42.748632812500006],\n                            [44.329492187499994, 42.703515624999966],\n                            [44.10273437500004, 42.616357421874994],\n                            [44.004687500000074, 42.59560546875002],\n                            [43.95742187500005, 42.56655273437505],\n                            [43.825976562500074, 42.571533203125],\n                            [43.759863281250006, 42.593847656250006],\n                            [43.738378906250006, 42.61699218750002],\n                            [43.74990234375005, 42.65751953125002],\n                            [43.79541015624997, 42.702978515625034],\n                            [43.78261718750005, 42.747021484374955],\n                            [43.62304687500003, 42.80771484374998],\n                            [43.5578125000001, 42.844482421875],\n                            [43.089160156250074, 42.9890625],\n                            [43.00019531250004, 43.04965820312506],\n                            [42.991601562499994, 43.09150390624998],\n                            [42.76064453125005, 43.169580078124966],\n                            [41.58056640624997, 43.21923828124997],\n                            [41.460742187500074, 43.276318359374955],\n                            [41.35820312500002, 43.33339843750005],\n                            [41.08310546875006, 43.37446289062498],\n                            [40.94199218750006, 43.41806640624998],\n                            [40.801660156249994, 43.479931640624955],\n                            [40.64804687500006, 43.53388671875004],\n                            [40.084570312500006, 43.553125],\n                            [40.02373046875002, 43.48486328125],\n                            [39.873632812500006, 43.47280273437502],\n                            [38.71728515624997, 44.28808593750003],\n                            [38.18125, 44.41967773437503],\n                            [37.851464843749994, 44.698828125000034],\n                            [37.49511718750003, 44.69526367187504],\n                            [37.20478515625004, 44.97197265624999],\n                            [36.62763671875004, 45.15131835937504],\n                            [36.941210937500074, 45.289697265624994],\n                            [36.72041015625004, 45.371875],\n                            [36.8659179687501, 45.42705078124999],\n                            [37.21357421875004, 45.272314453125006],\n                            [37.6471679687501, 45.37719726562506],\n                            [37.61240234375006, 45.56469726562506],\n                            [37.93310546875003, 46.001708984375],\n                            [38.014257812500006, 46.047753906249966],\n                            [38.07958984375003, 45.93481445312506],\n                            [38.18359374999997, 46.09482421875006],\n                            [38.49228515625006, 46.09052734374998],\n                            [37.913867187500074, 46.40649414062503],\n                            [37.766503906249994, 46.63613281250002],\n                            [38.50097656249997, 46.663671875000034],\n                            [38.43867187500004, 46.813085937500006],\n                            [39.29345703125003, 47.105761718750045],\n                            [39.19570312499999, 47.268847656250045],\n                            [39.023730468750074, 47.27221679687503],\n                            [38.928320312500006, 47.175683593749994],\n                            [38.55244140625004, 47.15034179687498],\n                            [38.7619140625001, 47.261621093749994],\n                            [38.21435546875003, 47.091455078124966],\n                            [38.36884765625004, 47.609960937500006],\n                            [38.90029296875005, 47.85512695312502],\n                            [39.77871093750005, 47.88754882812506],\n                            [39.95791015625005, 48.268896484375034],\n                            [39.8356445312501, 48.54277343749996],\n                            [39.6447265625001, 48.591210937499966],\n                            [39.792871093749994, 48.807714843750034],\n                            [40.00361328125004, 48.82207031250002],\n                            [39.68652343749997, 49.007910156250034],\n                            [40.10878906250005, 49.251562500000034],\n                            [40.080664062500006, 49.576855468749955],\n                            [39.780566406250074, 49.57202148437503],\n                            [39.17480468750003, 49.85595703124997],\n                            [38.91835937499999, 49.82470703125],\n                            [38.258593750000074, 50.05234375],\n                            [38.046875, 49.92001953125006],\n                            [37.42285156249997, 50.411474609375006],\n                            [36.619433593750074, 50.209228515625],\n                            [36.1164062500001, 50.408544921875006],\n                            [35.59111328125002, 50.36875],\n                            [35.31191406250005, 51.043896484374955],\n                            [35.0640625, 51.203417968750045],\n                            [34.21386718750003, 51.25537109375006],\n                            [34.12109375000003, 51.67915039062498],\n                            [34.397851562499994, 51.780419921874994],\n                            [33.735253906249994, 52.344775390625045],\n                            [32.435449218749994, 52.307226562500034],\n                            [32.12226562500004, 52.05058593749996],\n                            [31.763378906250097, 52.10107421875003],\n                            [31.758593750000017, 52.125830078125034],\n                            [31.690625, 52.22065429687498],\n                            [31.64990234374997, 52.26220703125],\n                            [31.60156250000003, 52.284814453124994],\n                            [31.57734375000004, 52.31230468749999],\n                            [31.585546875, 52.532470703125],\n                            [31.56484375, 52.75922851562501],\n                            [31.53515624999997, 52.798242187499966],\n                            [31.442773437499994, 52.86181640625003],\n                            [31.35302734374997, 52.93344726562498],\n                            [31.295117187500097, 52.98979492187499],\n                            [31.25878906249997, 53.01669921875006],\n                            [31.364550781250017, 53.13896484375002],\n                            [31.388378906250097, 53.18481445312503],\n                            [31.41787109375005, 53.196044921875],\n                            [31.849707031250006, 53.106201171875],\n                            [32.14199218750005, 53.091162109375034],\n                            [32.46933593750006, 53.270312500000045],\n                            [32.578027343749994, 53.312402343749994],\n                            [32.644433593749994, 53.32890624999999],\n                            [32.70429687500004, 53.33632812499999],\n                            [32.45097656250002, 53.6533203125],\n                            [32.20039062500004, 53.78125],\n                            [31.99218750000003, 53.796875],\n                            [31.82080078124997, 53.79194335937498],\n                            [31.754199218750017, 53.81044921875002],\n                            [31.825292968750006, 53.93500976562501],\n                            [31.837792968749994, 54.00078124999999],\n                            [31.825976562500074, 54.030712890624955],\n                            [31.79199218749997, 54.05590820312503],\n                            [31.62841796874997, 54.111181640625006],\n                            [31.403613281250017, 54.195947265624966],\n                            [31.299121093750017, 54.29169921875001],\n                            [31.184765625000097, 54.452978515625006],\n                            [31.074804687500063, 54.491796875],\n                            [31.154882812500063, 54.610937500000034],\n                            [31.152148437500017, 54.625341796875034],\n                            [31.12128906250004, 54.64848632812496],\n                            [30.984179687500074, 54.695898437500034],\n                            [30.79882812499997, 54.78325195312499],\n                            [30.79101562499997, 54.806005859375006],\n                            [30.804492187500074, 54.8609375],\n                            [30.829882812500017, 54.91499023437498],\n                            [30.977734375000097, 55.05048828124998],\n                            [30.977734375000097, 55.08779296875002],\n                            [30.958886718749994, 55.13759765625005],\n                            [30.87744140625003, 55.223437500000045],\n                            [30.81445312499997, 55.27871093750002],\n                            [30.81054687499997, 55.306982421875006],\n                            [30.82099609375004, 55.330273437499955],\n                            [30.900585937500097, 55.397412109374955],\n                            [30.906835937500063, 55.57001953125004],\n                            [30.625585937500006, 55.666259765625],\n                            [30.23359375000004, 55.84521484375006],\n                            [30.04267578125004, 55.83642578125003],\n                            [29.93701171874997, 55.84526367187499],\n                            [29.881640625000074, 55.83232421875002],\n                            [29.82392578125004, 55.79511718749998],\n                            [29.74414062499997, 55.770410156249994],\n                            [29.630078125000097, 55.75117187499998],\n                            [29.482226562500074, 55.6845703125],\n                            [29.412988281249994, 55.72485351562506],\n                            [29.35341796875005, 55.784375],\n                            [29.375, 55.938720703125],\n                            [28.284277343750006, 56.055908203125],\n                            [28.14794921875003, 56.142919921875034],\n                            [28.202050781250023, 56.260400390624994],\n                            [28.191699218750045, 56.31557617187505],\n                            [28.169238281250017, 56.386865234374994],\n                            [28.11083984375, 56.51069335937501],\n                            [28.103125, 56.545703125000045],\n                            [27.89208984375003, 56.741064453125034],\n                            [27.88154296875001, 56.82416992187501],\n                            [27.848632812500057, 56.85341796875002],\n                            [27.806054687499994, 56.86708984375005],\n                            [27.639453125000074, 56.84565429687504],\n                            [27.83027343750004, 57.19448242187505],\n                            [27.83828125000008, 57.247705078124966],\n                            [27.82861328124997, 57.293310546875006],\n                            [27.796875, 57.316943359375045],\n                            [27.538671875000063, 57.429785156250034],\n                            [27.51113281250005, 57.508154296875006],\n                            [27.469726562500057, 57.524023437500034],\n                            [27.35195312500005, 57.528125],\n                            [27.4, 57.66679687499999],\n                            [27.542089843750063, 57.799414062500006],\n                            [27.778515625000068, 57.87070312500006],\n                            [27.502441406250057, 58.221337890624994],\n                            [27.434179687500006, 58.787255859374994],\n                            [28.15107421875004, 59.374414062499966],\n                            [28.0125, 59.484277343749966],\n                            [28.05800781250008, 59.781542968750045],\n                            [28.334570312500034, 59.69252929687502],\n                            [28.518164062500034, 59.849560546874955],\n                            [28.947265625000057, 59.828759765624994],\n                            [29.147265625000045, 59.999755859375],\n                            [30.12255859374997, 59.873583984375074],\n                            [30.172656250000017, 59.957128906250034],\n                            [29.72119140624997, 60.19531249999997],\n                            [29.069140625000017, 60.19145507812499],\n                            [28.643164062500006, 60.375292968750045],\n                            [28.512792968750006, 60.67729492187502],\n                            [27.797656250000074, 60.53613281250003],\n                            [29.69013671875004, 61.54609375000001],\n                            [31.18671875000004, 62.48139648437504],\n                            [31.533984375000017, 62.885400390624994],\n                            [31.180859375000097, 63.208300781250074],\n                            [29.991503906250074, 63.73515625000002],\n                            [30.50390625000003, 64.02060546875],\n                            [30.513769531250006, 64.2],\n                            [30.04189453125005, 64.44335937499997],\n                            [30.072851562500063, 64.76503906250005],\n                            [29.60419921875004, 64.968408203125],\n                            [29.826953125000017, 65.14506835937502],\n                            [29.608007812500006, 65.248681640625],\n                            [29.715917968750063, 65.62456054687502],\n                            [30.102734375000097, 65.72626953125004],\n                            [29.066210937500045, 66.89174804687497],\n                            [29.988085937500017, 67.66826171874999],\n                            [29.343847656250006, 68.06186523437506],\n                            [28.685156250000034, 68.189794921875],\n                            [28.470703125000057, 68.48837890625],\n                            [28.77285156250005, 68.84003906249995],\n                            [28.414062500000057, 68.90415039062506],\n                            [28.96582031250003, 69.02197265625],\n                            [29.38828125, 69.29814453125005],\n                            [30.08730468750005, 69.43286132812503],\n                            [30.18017578124997, 69.63583984375],\n                            [30.860742187499994, 69.53842773437503],\n                            [30.869726562500006, 69.78344726562506],\n                            [31.546972656250063, 69.696923828125],\n                            [31.997949218749994, 69.80991210937503],\n                            [31.98457031250004, 69.95366210937499],\n                            [33.00781249999997, 69.72211914062498],\n                            [32.91503906249997, 69.60170898437497],\n                            [32.17675781250003, 69.67402343749995],\n                            [32.37773437500002, 69.47910156250003],\n                            [32.99980468750002, 69.4701171875],\n                            [32.97890625000005, 69.367333984375],\n                            [33.45429687500004, 69.42817382812495],\n                            [33.14121093750006, 69.068701171875],\n                            [33.684375, 69.31025390625001],\n                            [35.85791015625003, 69.19174804687503],\n                            [37.73056640625006, 68.69213867187503],\n                            [38.43017578125003, 68.35561523437505],\n                            [39.568945312500006, 68.07172851562501],\n                            [39.82333984375006, 68.05859375],\n                            [39.80927734375004, 68.15083007812498],\n                            [40.38066406250002, 67.831884765625],\n                            [40.96640625000006, 67.71347656250003],\n                            [41.358789062499994, 67.20966796874998],\n                            [41.18896484375003, 66.82617187500003],\n                            [40.10332031250002, 66.29995117187502],\n                            [38.65390625000006, 66.06904296874995],\n                            [35.51347656250002, 66.39580078125002],\n                            [34.82460937499999, 66.61113281249999],\n                            [34.48261718750004, 66.55034179687505],\n                            [34.4515625, 66.651220703125],\n                            [33.15019531250002, 66.84394531250001],\n                            [32.93046875000002, 67.08681640625002],\n                            [31.895312500000074, 67.16142578125002],\n                            [33.65595703125004, 66.44262695312506],\n                            [33.36054687500004, 66.32954101562501],\n                            [34.112695312499994, 66.225244140625],\n                            [34.69179687500005, 65.95185546874998],\n                            [34.77695312500006, 65.76826171874998],\n                            [34.40644531250004, 65.39575195312503],\n                            [35.03535156250004, 64.44023437500005],\n                            [35.802050781250074, 64.3353515625],\n                            [36.3649414062501, 64.00283203125002],\n                            [37.44218750000002, 63.813378906249966],\n                            [37.9679687500001, 63.949121093749994],\n                            [38.0622070312501, 64.09101562499995],\n                            [37.953710937500006, 64.32011718749999],\n                            [37.183691406250006, 64.40849609375007],\n                            [36.6242187500001, 64.75053710937502],\n                            [36.534570312499994, 64.93862304687497],\n                            [36.88281249999997, 65.17236328124997],\n                            [39.7580078125001, 64.57705078125002],\n                            [40.05781250000004, 64.77075195312497],\n                            [40.44492187500006, 64.7787109375],\n                            [39.7980468750001, 65.349853515625],\n                            [39.816503906250006, 65.59794921874999],\n                            [41.4757812500001, 66.12343750000002],\n                            [42.21054687500006, 66.51967773437502],\n                            [43.23320312500002, 66.41552734375003],\n                            [43.653125, 66.2509765625],\n                            [43.54189453125005, 66.12338867187503],\n                            [43.84375, 66.14238281249999],\n                            [44.10439453125005, 66.00859374999999],\n                            [44.42929687500006, 66.93774414062503],\n                            [43.7824218750001, 67.25449218749998],\n                            [44.20468750000006, 68.25375976562498],\n                            [43.33320312500004, 68.67338867187502],\n                            [44.04804687500004, 68.54882812499997],\n                            [45.891992187499994, 68.47968750000001],\n                            [46.69042968750003, 67.84882812500001],\n                            [45.52871093750005, 67.75756835937497],\n                            [44.90214843750002, 67.41313476562505],\n                            [45.56220703125004, 67.18559570312507],\n                            [45.88535156250006, 66.89106445312501],\n                            [46.4923828125001, 66.80019531249997],\n                            [47.65585937500006, 66.97592773437498],\n                            [47.87470703125004, 67.58417968749998],\n                            [48.83320312500004, 67.681494140625],\n                            [48.75429687500005, 67.89594726562501],\n                            [49.15527343750003, 67.87041015625005],\n                            [51.994726562500006, 68.53876953124995],\n                            [52.3966796875001, 68.35170898437505],\n                            [52.72265624999997, 68.484033203125],\n                            [52.34404296875002, 68.60815429687497],\n                            [53.80195312500004, 68.99589843750002],\n                            [54.49121093750003, 68.992333984375],\n                            [53.797656250000074, 68.90747070312503],\n                            [53.9308593750001, 68.43554687499997],\n                            [53.260546875000074, 68.26748046875002],\n                            [54.476171875, 68.29414062499995],\n                            [54.86132812500003, 68.20185546874998],\n                            [55.418066406250006, 68.56782226562501],\n                            [56.04365234375004, 68.64887695312501],\n                            [57.126855468749994, 68.55400390625005],\n                            [58.17304687500004, 68.88974609375006],\n                            [59.0573242187501, 69.00605468750004],\n                            [59.37050781250005, 68.73837890625003],\n                            [59.09902343750005, 68.4443359375],\n                            [59.725683593750006, 68.35161132812502],\n                            [59.89599609374997, 68.70634765624999],\n                            [60.489160156249994, 68.72895507812498],\n                            [60.93359374999997, 68.98676757812501],\n                            [60.17060546875004, 69.59091796875],\n                            [60.90908203125005, 69.84711914062495],\n                            [64.19042968750003, 69.53466796875],\n                            [64.89628906250002, 69.247802734375],\n                            [67.00244140625003, 68.87358398437505],\n                            [68.37119140625006, 68.31425781250005],\n                            [69.14052734375005, 68.95063476562501],\n                            [68.54277343750002, 68.96708984374999],\n                            [68.00585937499997, 69.48002929687505],\n                            [67.62412109375, 69.58442382812501],\n                            [67.06445312500003, 69.69370117187498],\n                            [66.89667968750004, 69.55380859374998],\n                            [67.28476562500006, 70.73872070312498],\n                            [67.14335937500002, 70.83754882812502],\n                            [66.70224609375006, 70.81850585937497],\n                            [66.63964843749997, 71.08139648437498],\n                            [68.2692382812501, 71.68281250000001],\n                            [69.61181640625003, 72.98193359375],\n                            [69.73828124999997, 72.88496093749998],\n                            [71.5001953125001, 72.91367187500003],\n                            [72.812109375, 72.69140624999997],\n                            [72.57412109375, 72.01254882812506],\n                            [71.86728515625, 71.457373046875],\n                            [72.70449218750005, 70.96323242187498],\n                            [72.5767578125, 68.96870117187498],\n                            [73.59169921875005, 68.48188476562501],\n                            [73.13945312500002, 68.18134765624998],\n                            [73.06679687500005, 67.766943359375],\n                            [71.84746093750002, 67.00761718750005],\n                            [71.36523437500003, 66.96152343749998],\n                            [71.53955078125003, 66.68310546875],\n                            [70.72490234375007, 66.51943359374997],\n                            [70.38281249999997, 66.60249023437501],\n                            [70.69072265625002, 66.74531249999998],\n                            [70.2833984375001, 66.68579101562503],\n                            [69.8771484375001, 66.84545898437506],\n                            [69.21777343749997, 66.82861328125],\n                            [69.01347656250002, 66.78833007812503],\n                            [69.19433593749997, 66.57866210937505],\n                            [70.33945312500006, 66.34238281250006],\n                            [71.35800781250006, 66.35942382812505],\n                            [71.91699218749997, 66.24672851562502],\n                            [72.32158203125002, 66.33212890625],\n                            [72.4173828125, 66.56079101562506],\n                            [73.79208984375, 66.99531250000001],\n                            [74.07451171875007, 67.41411132812499],\n                            [74.76953124999997, 67.76635742187497],\n                            [74.39140625000007, 68.42060546874995],\n                            [74.57958984375003, 68.751220703125],\n                            [76.10751953125006, 68.975732421875],\n                            [76.45917968750004, 68.97827148437497],\n                            [77.2384765625001, 68.46958007812498],\n                            [77.17441406250012, 67.77851562499998],\n                            [77.77158203125006, 67.57026367187501],\n                            [78.92246093750006, 67.58911132812503],\n                            [77.58828125000005, 67.75190429687498],\n                            [77.66484375000002, 68.19038085937495],\n                            [77.99511718749997, 68.25947265624998],\n                            [77.65068359375007, 68.90302734375001],\n                            [76.00097656249997, 69.23505859374998],\n                            [75.42001953125, 69.23862304687498],\n                            [74.81484375, 69.09057617187503],\n                            [73.83603515625006, 69.143212890625],\n                            [73.578125, 69.80297851562503],\n                            [74.34335937500006, 70.57871093749998],\n                            [73.08623046875007, 71.44492187500006],\n                            [73.67177734375, 71.84506835937503],\n                            [74.99218749999997, 72.14482421874999],\n                            [74.78681640625004, 72.811865234375],\n                            [75.15244140625, 72.85273437499998],\n                            [75.74140625000004, 72.29624023437503],\n                            [75.273828125, 71.95893554687495],\n                            [75.33203125000003, 71.34174804687498],\n                            [76.92900390625002, 71.12788085937504],\n                            [77.58964843750007, 71.16791992187501],\n                            [78.32060546875002, 70.93041992187503],\n                            [78.94218750000002, 70.93378906250001],\n                            [79.08388671875, 71.00200195312505],\n                            [78.58769531250007, 70.993896484375],\n                            [78.21259765625004, 71.26630859374998],\n                            [76.43339843750002, 71.55249023437503],\n                            [76.03242187500004, 71.91040039062503],\n                            [76.87138671875002, 72.03300781250005],\n                            [77.77753906250004, 71.83642578125006],\n                            [78.23242187500003, 71.95229492187502],\n                            [78.01640625000007, 72.092041015625],\n                            [77.49287109375004, 72.07172851562504],\n                            [77.47158203125, 72.19213867187506],\n                            [78.22539062500007, 72.37744140625006],\n                            [79.4220703125001, 72.38076171875002],\n                            [80.7625, 72.08916015625002],\n                            [81.66162109374997, 71.71596679687502],\n                            [82.75781250000003, 71.76411132812498],\n                            [83.23359375000004, 71.66816406249995],\n                            [82.32285156250006, 71.26000976562503],\n                            [82.16318359375012, 70.59814453125003],\n                            [82.22119140625003, 70.39570312499998],\n                            [82.86914062499997, 70.95483398437503],\n                            [83.03017578125, 70.58051757812498],\n                            [82.6823242187501, 70.21772460937498],\n                            [83.0807617187501, 70.09301757812497],\n                            [83.07382812500012, 70.276708984375],\n                            [83.73593750000006, 70.54648437499998],\n                            [83.15126953125005, 71.10361328124998],\n                            [83.534375, 71.68393554687498],\n                            [83.20029296875012, 71.87470703125004],\n                            [82.64541015625005, 71.92524414062504],\n                            [82.09365234375, 72.26542968750005],\n                            [80.82705078125005, 72.48828124999997],\n                            [80.84160156250007, 72.94916992187498],\n                            [80.4245117187501, 73.23115234374998],\n                            [80.5832031250001, 73.56845703125003],\n                            [85.20058593750005, 73.72153320312506],\n                            [86.89296875, 73.88710937500002],\n                            [85.79257812500012, 73.438330078125],\n                            [86.67705078125002, 73.10678710937503],\n                            [85.93896484374997, 73.45649414062495],\n                            [87.12011718750003, 73.61503906250002],\n                            [87.57119140625, 73.81074218750001],\n                            [86.57109375000007, 74.24375],\n                            [86.0013671875, 74.316015625],\n                            [86.39580078125007, 74.45009765624997],\n                            [86.89794921874997, 74.32534179687497],\n                            [87.22968750000004, 74.3638671875],\n                            [85.79101562499997, 74.6451171875],\n                            [86.20126953125006, 74.81621093750005],\n                            [86.65146484375012, 74.68242187500005],\n                            [87.04179687500007, 74.77885742187499],\n                            [87.46757812500002, 75.01323242187505],\n                            [86.93906250000006, 75.06811523437503],\n                            [87.00595703125012, 75.16982421874997],\n                            [87.67138671874997, 75.12958984375004],\n                            [90.18496093750005, 75.59106445312497],\n                            [94.07519531249997, 75.91289062499999],\n                            [92.89042968750002, 75.90996093750002],\n                            [93.25927734375003, 76.09877929687502],\n                            [95.57871093750012, 76.13730468749998],\n                            [96.07548828125007, 76.08198242187498],\n                            [95.65332031250003, 75.89218750000003],\n                            [96.50859375000002, 76.00556640624995],\n                            [96.49707031249997, 75.89121093750003],\n                            [98.66201171875005, 76.24267578125003],\n                            [99.77041015625, 76.02875976562498],\n                            [99.5407226562501, 75.79858398437497],\n                            [99.85136718750007, 75.93027343749998],\n                            [99.8253906250001, 76.13593749999995],\n                            [98.80566406250003, 76.48066406250004],\n                            [100.84375, 76.52519531250005],\n                            [101.59775390625006, 76.43920898437503],\n                            [100.92802734375002, 76.55673828124998],\n                            [100.98994140625004, 76.99047851562497],\n                            [102.61015625000007, 77.508544921875],\n                            [104.01455078125, 77.73041992187501],\n                            [106.05957031249997, 77.39052734375002],\n                            [104.20244140625002, 77.101806640625],\n                            [106.9416015625001, 77.034375],\n                            [107.42978515625006, 76.92656250000002],\n                            [106.41357421874997, 76.51225585937499],\n                            [107.72216796875003, 76.52231445312498],\n                            [108.18164062500003, 76.73784179687502],\n                            [111.39248046875, 76.686669921875],\n                            [112.09394531250004, 76.48032226562506],\n                            [111.94267578125002, 76.38046875000003],\n                            [112.61953125, 76.38354492187506],\n                            [112.65625, 76.05356445312498],\n                            [113.2726562500001, 76.25166015625001],\n                            [113.5638671875, 75.89165039062502],\n                            [113.85722656250007, 75.92128906250002],\n                            [113.56757812500004, 75.56840820312499],\n                            [112.45302734375, 75.83017578125003],\n                            [112.95566406250006, 75.571923828125],\n                            [113.24296875000007, 75.61142578125003],\n                            [113.72617187500012, 75.45063476562498],\n                            [112.92490234375012, 75.01503906249997],\n                            [109.84033203124997, 74.32197265624998],\n                            [109.8102539062501, 74.16918945312503],\n                            [108.19951171875002, 73.69409179687497],\n                            [107.27109375000006, 73.62104492187501],\n                            [106.67939453125004, 73.3306640625],\n                            [106.1886718750001, 73.3080078125],\n                            [105.14394531250005, 72.77705078125001],\n                            [105.7082031250001, 72.836669921875],\n                            [106.47792968750005, 73.13940429687503],\n                            [107.750390625, 73.17314453125007],\n                            [109.33105468749997, 73.48745117187497],\n                            [109.85527343750002, 73.47246093750002],\n                            [110.86816406249997, 73.73071289062497],\n                            [109.70673828125004, 73.74375],\n                            [110.2614257812501, 74.01743164062503],\n                            [111.05625, 73.93935546875002],\n                            [111.13085937500003, 74.05283203125003],\n                            [111.55058593750007, 74.02851562499998],\n                            [111.22812500000012, 73.96855468750002],\n                            [111.40039062500003, 73.827734375],\n                            [112.14726562500007, 73.70893554687498],\n                            [112.79541015625003, 73.74609375],\n                            [112.83593750000003, 73.96206054687502],\n                            [113.03281250000006, 73.91386718750007],\n                            [113.4162109375001, 73.647607421875],\n                            [113.15693359375004, 73.45957031249998],\n                            [113.49091796875004, 73.34609375000002],\n                            [113.12783203125, 72.8306640625],\n                            [113.66455078124997, 72.63452148437503],\n                            [113.2155273437501, 72.80585937500001],\n                            [113.88623046875003, 73.34580078124998],\n                            [113.51035156250012, 73.50498046874998],\n                            [115.33769531250007, 73.70258789062501],\n                            [118.87089843750007, 73.53789062500002],\n                            [118.45703124999997, 73.46440429687507],\n                            [118.43027343750012, 73.24653320312501],\n                            [119.750390625, 72.97910156250006],\n                            [122.26015625, 72.88056640624995],\n                            [122.75195312500003, 72.906494140625],\n                            [122.61523437499997, 73.02792968750006],\n                            [123.1603515625001, 72.95488281250002],\n                            [123.62226562500004, 73.19326171875],\n                            [123.49111328125005, 73.666357421875],\n                            [124.54121093750004, 73.75126953125007],\n                            [125.59853515625005, 73.447412109375],\n                            [126.25449218750012, 73.548193359375],\n                            [126.55253906250007, 73.33491210937498],\n                            [127.03134765625006, 73.54746093750003],\n                            [127.74033203125012, 73.48154296875],\n                            [129.10058593750003, 73.11235351562502],\n                            [128.5990234375, 72.895166015625],\n                            [129.01728515625004, 72.8724609375],\n                            [129.250390625, 72.70517578125003],\n                            [128.41826171875002, 72.53515625000003],\n                            [129.28134765625006, 72.43769531249998],\n                            [129.41064453124997, 72.16630859375002],\n                            [128.93496093750005, 72.07949218750002],\n                            [127.8034179687501, 72.43403320312504],\n                            [127.84140625000012, 72.308251953125],\n                            [128.91142578125002, 71.75532226562495],\n                            [129.21025390625007, 71.91694335937501],\n                            [129.46083984375, 71.73930664062499],\n                            [128.84326171875003, 71.6634765625],\n                            [129.76191406250004, 71.11953125000002],\n                            [130.53710937500003, 70.89252929687495],\n                            [130.75712890625002, 70.96235351562498],\n                            [131.02158203125006, 70.74609374999997],\n                            [132.0353515625001, 71.24404296875],\n                            [132.65390625000006, 71.92597656250001],\n                            [133.6888671875, 71.434228515625],\n                            [134.70273437500012, 71.38681640625003],\n                            [135.55917968750006, 71.6103515625],\n                            [136.09033203125003, 71.61958007812501],\n                            [137.9396484375001, 71.1333984375],\n                            [137.84404296875007, 71.22680664062503],\n                            [138.31406250000006, 71.32553710937498],\n                            [137.918359375, 71.38408203124999],\n                            [138.23417968750007, 71.596337890625],\n                            [138.78017578125, 71.62900390624998],\n                            [139.209375, 71.44477539062501],\n                            [139.98417968750007, 71.49150390625005],\n                            [139.72294921875002, 71.88496093749998],\n                            [139.35927734375005, 71.95136718750001],\n                            [140.18769531250004, 72.19130859374997],\n                            [139.17636718750006, 72.16347656249997],\n                            [139.14082031250004, 72.32973632812502],\n                            [139.60117187500012, 72.49609374999997],\n                            [141.07929687500004, 72.5869140625],\n                            [140.80820312500006, 72.89096679687503],\n                            [142.06142578125005, 72.72080078125],\n                            [146.25292968749997, 72.442236328125],\n                            [146.234765625, 72.34970703125],\n                            [144.77636718749997, 72.38227539062495],\n                            [144.16923828125002, 72.25878906250003],\n                            [144.29492187499997, 72.19262695312497],\n                            [146.83183593750007, 72.29541015625003],\n                            [146.11328125000003, 71.94497070312497],\n                            [146.23027343750007, 72.1375],\n                            [145.75859375000007, 72.22587890624999],\n                            [145.75673828125005, 71.94130859375002],\n                            [145.06396484374997, 71.92607421875002],\n                            [145.18857421875012, 71.69580078125],\n                            [146.07324218749997, 71.80834960937503],\n                            [147.26181640625006, 72.327880859375],\n                            [149.50156250000012, 72.16430664062497],\n                            [150.01689453125002, 71.89565429687505],\n                            [149.04873046875005, 71.79575195312503],\n                            [148.9681640625, 71.69047851562499],\n                            [150.59980468750004, 71.5201171875],\n                            [150.09765624999997, 71.22656249999997],\n                            [150.96777343749997, 71.38046874999998],\n                            [151.58242187500005, 71.28696289062503],\n                            [152.09277343749997, 71.02329101562503],\n                            [151.76201171875002, 70.98247070312499],\n                            [152.50878906250003, 70.83447265625003],\n                            [156.68457031250003, 71.09375],\n                            [158.03701171875005, 71.03925781250001],\n                            [159.35068359375006, 70.79072265625001],\n                            [160.00644531250006, 70.30966796875006],\n                            [159.72939453125005, 69.87021484375006],\n                            [160.91074218750012, 69.60634765625002],\n                            [161.03554687500005, 69.09819335937507],\n                            [161.30986328125007, 68.98227539062498],\n                            [160.85605468750006, 68.53833007812506],\n                            [161.565625, 68.90517578125],\n                            [161.53691406250002, 69.379541015625],\n                            [162.16601562499997, 69.61157226562503],\n                            [163.20136718750004, 69.71474609375],\n                            [166.82031250000003, 69.49956054687505],\n                            [167.8568359375, 69.72822265624998],\n                            [168.30302734375002, 69.27148437500003],\n                            [169.31064453125006, 69.07954101562498],\n                            [169.60986328124997, 68.78603515624997],\n                            [170.53759765624997, 68.82539062500001],\n                            [170.99541015625002, 69.04531250000005],\n                            [170.58222656250004, 69.58334960937506],\n                            [170.16093750000007, 69.62656249999998],\n                            [170.48681640625003, 70.107568359375],\n                            [173.27744140625006, 69.823828125],\n                            [173.43867187500004, 69.94682617187502],\n                            [175.92148437500012, 69.89531250000002],\n                            [179.27265624999998, 69.25966796875002],\n                            [180, 68.98344726562505],\n                            [180, 65.06723632812498],\n                            [178.51953125000003, 64.60297851562498],\n                            [177.7486328125, 64.71704101562503],\n                            [176.88085937499997, 65.08193359375002],\n                            [176.34101562500015, 65.04731445312501],\n                            [177.03730468750004, 64.99965820312497],\n                            [177.22285156250004, 64.861669921875],\n                            [177.06875, 64.78666992187502],\n                            [176.06113281250012, 64.96088867187498],\n                            [174.54882812500009, 64.68388671875005],\n                            [176.0565429687501, 64.90473632812498],\n                            [176.35097656250005, 64.70512695312502],\n                            [176.14091796875007, 64.58583984375005],\n                            [177.42744140625015, 64.76337890624998],\n                            [177.43291015625002, 64.44448242187502],\n                            [177.6875, 64.30473632812507],\n                            [178.04472656250013, 64.21958007812503],\n                            [178.22949218749991, 64.36440429687497],\n                            [178.38144531250018, 64.26088867187502],\n                            [178.73144531250003, 63.667089843750006],\n                            [178.44042968750009, 63.605566406250006],\n                            [178.74404296874994, 63.39477539062503],\n                            [178.79296874999997, 63.54033203125002],\n                            [179.38857421875, 63.14721679687497],\n                            [179.25957031250002, 63.00830078125],\n                            [179.5705078125001, 62.6875],\n                            [179.12070312500012, 62.32036132812499],\n                            [177.292578125, 62.59902343750002],\n                            [177.33896484375006, 62.781347656250034],\n                            [177.02353515625012, 62.777246093749994],\n                            [177.15947265625007, 62.56098632812498],\n                            [174.51435546875015, 61.823632812499966],\n                            [173.6234375, 61.716064453125],\n                            [173.13183593749997, 61.40664062500002],\n                            [172.85654296875006, 61.469189453124955],\n                            [172.90800781250002, 61.311621093750006],\n                            [172.39609375000006, 61.16738281250002],\n                            [172.39277343750004, 61.061767578125],\n                            [170.60820312500007, 60.434912109375034],\n                            [170.3509765625, 59.965527343749955],\n                            [169.9826171875001, 60.067089843749955],\n                            [169.2267578125001, 60.59594726562497],\n                            [168.1375, 60.57392578125001],\n                            [167.22675781250004, 60.406298828125045],\n                            [166.27304687500012, 59.85625],\n                            [166.13603515625007, 59.979345703125034],\n                            [166.35214843750006, 60.48481445312498],\n                            [165.08457031250006, 60.09858398437498],\n                            [164.95371093750006, 59.843603515625006],\n                            [164.52529296875, 60.06127929687503],\n                            [164.11328125000003, 59.89755859374998],\n                            [164.13505859375002, 59.984375],\n                            [163.74384765625004, 60.02802734374998],\n                            [163.36484375000012, 59.78144531250004],\n                            [163.27285156250005, 59.302587890625006],\n                            [162.14160156249997, 58.44741210937502],\n                            [161.96005859375012, 58.07690429687506],\n                            [162.39140625000002, 57.717236328124955],\n                            [162.65429687499997, 57.94824218750003],\n                            [163.22578125000004, 57.790380859375034],\n                            [162.77929687500003, 57.35761718749998],\n                            [162.79111328125012, 56.875390624999966],\n                            [162.92207031250004, 56.72265625000003],\n                            [163.2565429687501, 56.68803710937499],\n                            [163.33554687500012, 56.232519531250006],\n                            [163.04736328125003, 56.044677734375],\n                            [162.84033203125003, 56.065625],\n                            [162.628125, 56.232275390625034],\n                            [163.03837890625002, 56.521875],\n                            [162.67148437500006, 56.49008789062498],\n                            [162.52822265625005, 56.260693359374955],\n                            [162.08496093749997, 56.08964843750002],\n                            [161.72392578125002, 55.49614257812499],\n                            [162.10556640625006, 54.75214843750004],\n                            [161.62480468750002, 54.51625976562502],\n                            [160.77265625000004, 54.54135742187498],\n                            [160.0744140625001, 54.18916015625001],\n                            [159.84375, 53.78364257812498],\n                            [160.02509765625004, 53.129589843749955],\n                            [159.58593750000003, 53.237695312499966],\n                            [158.74541015625002, 52.90893554687506],\n                            [158.47207031250005, 53.032373046874966],\n                            [158.6087890625, 52.873632812500034],\n                            [158.49316406249997, 52.383154296875034],\n                            [158.10351562500003, 51.80961914062499],\n                            [156.84746093750002, 51.006591796875],\n                            [156.74775390625004, 50.969287109375045],\n                            [156.52119140625004, 51.38027343750002],\n                            [156.36474609374997, 52.509375],\n                            [156.11035156250003, 52.86616210937504],\n                            [155.62031250000004, 54.86455078125002],\n                            [155.5548828125001, 55.348486328125034],\n                            [155.98251953125012, 56.69521484375002],\n                            [156.8488281250001, 57.290185546874994],\n                            [156.97675781250004, 57.46630859375],\n                            [156.82988281250007, 57.77963867187498],\n                            [157.4503906250001, 57.79926757812498],\n                            [157.66640625000005, 58.01977539062506],\n                            [158.27519531250007, 58.00898437499998],\n                            [159.21064453125004, 58.519433593749966],\n                            [159.8473632812501, 59.127148437499955],\n                            [161.75351562500012, 60.15229492187501],\n                            [162.06816406250002, 60.466406250000034],\n                            [163.70996093749997, 60.916796875000045],\n                            [163.55351562500002, 61.02563476562503],\n                            [164.00546875000006, 61.34379882812499],\n                            [163.80439453125004, 61.46137695312498],\n                            [164.20722656250004, 62.29223632812506],\n                            [164.59833984375004, 62.470556640625034],\n                            [165.20810546875012, 62.37397460937501],\n                            [165.41738281250005, 62.447070312500045],\n                            [164.418359375, 62.704638671875045],\n                            [163.33173828125004, 62.550927734374994],\n                            [163.01767578125006, 61.89106445312504],\n                            [163.25781249999997, 61.69946289062497],\n                            [163.08525390625002, 61.570556640625],\n                            [162.85595703125003, 61.705029296874955],\n                            [162.39257812500003, 61.662109375],\n                            [160.76660156249997, 60.753320312499966],\n                            [160.17363281250002, 60.638427734375],\n                            [160.37890625000003, 61.02548828124998],\n                            [159.79042968750005, 60.956640625],\n                            [160.309375, 61.894384765625006],\n                            [159.55234375000012, 61.71948242187497],\n                            [159.18925781250007, 61.92939453125001],\n                            [158.07011718750002, 61.75361328125001],\n                            [157.46933593750012, 61.798925781250006],\n                            [157.0841796875001, 61.67568359375002],\n                            [155.71611328125002, 60.682373046875],\n                            [154.97080078125012, 60.376660156249955],\n                            [154.29306640625006, 59.833349609375034],\n                            [154.1498046875, 59.52851562500001],\n                            [154.97128906250006, 59.44960937500002],\n                            [155.16044921875002, 59.19013671875001],\n                            [154.45800781250003, 59.21655273437497],\n                            [154.01093750000004, 59.075537109375006],\n                            [153.69521484375005, 59.22475585937505],\n                            [153.36113281250002, 59.214794921874955],\n                            [152.81787109375003, 58.92626953124997],\n                            [152.31962890625002, 59.03076171875003],\n                            [152.08789062499997, 58.910449218750045],\n                            [151.32675781250006, 58.875097656250034],\n                            [151.12109375000003, 59.08251953125003],\n                            [152.26064453125, 59.22358398437498],\n                            [151.34824218750012, 59.561132812500006],\n                            [150.4835937500001, 59.494384765625],\n                            [150.66728515625002, 59.55634765625001],\n                            [149.64257812499997, 59.770410156249994],\n                            [149.06523437500002, 59.63051757812502],\n                            [149.20498046875, 59.488183593749966],\n                            [148.79707031250004, 59.532324218750006],\n                            [148.74414062499997, 59.37353515624997],\n                            [148.96464843750007, 59.36914062499997],\n                            [148.72666015625006, 59.257910156250034],\n                            [148.25742187500006, 59.414208984374994],\n                            [147.51445312500002, 59.2685546875],\n                            [146.53720703125006, 59.45698242187501],\n                            [146.0495117187501, 59.17055664062502],\n                            [145.55458984375, 59.413525390624955],\n                            [143.19218750000002, 59.3701171875],\n                            [142.58027343750004, 59.240136718749966],\n                            [140.79023437500004, 58.30346679687503],\n                            [140.446875, 57.81367187499998],\n                            [138.66210937500003, 56.96552734375004],\n                            [137.69150390625006, 56.13935546875004],\n                            [135.2625, 54.94331054687498],\n                            [135.25771484375005, 54.73149414062499],\n                            [135.85156249999997, 54.583935546874955],\n                            [136.797265625, 54.62099609375005],\n                            [136.71884765625006, 53.804101562499994],\n                            [137.15537109375012, 53.82167968750002],\n                            [137.14160156249997, 54.182226562500006],\n                            [137.66601562500003, 54.283300781250006],\n                            [137.3392578125, 54.10053710937498],\n                            [137.83476562500002, 53.94672851562498],\n                            [137.25371093750007, 53.546142578125],\n                            [137.95048828125007, 53.60356445312499],\n                            [138.52792968750012, 53.959863281249994],\n                            [138.56914062500002, 53.818798828124955],\n                            [138.24970703125004, 53.524023437500034],\n                            [138.45068359375003, 53.53701171875002],\n                            [138.69941406250004, 53.869726562500034],\n                            [138.65722656249997, 54.29833984375003],\n                            [139.31972656250005, 54.19296874999998],\n                            [139.707421875, 54.27714843749999],\n                            [140.68759765625012, 53.59643554687503],\n                            [141.3737304687501, 53.29277343749999],\n                            [141.18125, 53.01528320312505],\n                            [140.83964843750002, 53.087890625],\n                            [141.25585937499997, 52.84013671874996],\n                            [141.13242187500006, 52.435693359374994],\n                            [141.48525390625, 52.17851562500002],\n                            [141.36689453125004, 51.92065429687506],\n                            [140.93261718750003, 51.61992187499999],\n                            [140.5208984375, 50.80019531250005],\n                            [140.62451171874997, 50.08242187500002],\n                            [140.46269531250002, 49.911474609375006],\n                            [140.51718750000012, 49.59614257812498],\n                            [140.17060546875004, 48.52368164062497],\n                            [138.58681640625005, 47.057226562500006],\n                            [138.33691406250003, 46.543408203124955],\n                            [137.68544921875, 45.81835937500003],\n                            [136.14228515625004, 44.489111328125034],\n                            [135.87460937500012, 44.37353515625003],\n                            [135.1310546875001, 43.52573242187506],\n                            [134.01044921875004, 42.94746093750001],\n                            [133.15996093750007, 42.69697265624998],\n                            [132.70898437500003, 42.875830078125006],\n                            [132.30380859375006, 42.88330078125],\n                            [132.30957031249997, 43.31352539062499],\n                            [131.8666015625, 43.09516601562501],\n                            [131.93896484374997, 43.30195312500004],\n                            [131.15830078125012, 42.62602539062499],\n                            [130.709375, 42.656396484374966],\n                            [130.8341796875001, 42.52294921875006],\n                            [130.68730468750007, 42.30253906249999]\n                        ]\n                    ],\n                    [\n                        [\n                            [107.69550781250004, 78.13090820312505],\n                            [107.48164062500004, 78.057763671875],\n                            [106.41552734375003, 78.13984375000001],\n                            [107.69550781250004, 78.13090820312505]\n                        ]\n                    ],\n                    [\n                        [\n                            [102.88476562499997, 79.25395507812505],\n                            [102.4123046875001, 78.83544921874997],\n                            [103.80078124999997, 79.14926757812503],\n                            [104.45205078125005, 78.880029296875],\n                            [105.14599609375003, 78.81884765625006],\n                            [105.31259765625012, 78.49990234375],\n                            [104.74179687500012, 78.33974609374997],\n                            [102.79667968750007, 78.18789062500002],\n                            [101.20410156249997, 78.19194335937505],\n                            [99.50029296875002, 77.97607421875003],\n                            [101.590625, 79.350439453125],\n                            [102.25126953125002, 79.25605468749995],\n                            [102.40488281250006, 79.43320312499998],\n                            [102.88476562499997, 79.25395507812505]\n                        ]\n                    ],\n                    [\n                        [\n                            [76.24892578125005, 79.65107421874995],\n                            [77.58896484375012, 79.50190429687504],\n                            [76.64951171875012, 79.493408203125],\n                            [76.24892578125005, 79.65107421874995]\n                        ]\n                    ],\n                    [\n                        [\n                            [92.68349609375005, 79.685205078125],\n                            [91.37626953125007, 79.83549804687505],\n                            [91.22929687500007, 80.03071289062504],\n                            [93.803125, 79.904541015625],\n                            [92.68349609375005, 79.685205078125]\n                        ]\n                    ],\n                    [\n                        [\n                            [51.409277343750006, 79.94423828125],\n                            [50.09140625, 79.98056640625003],\n                            [50.93632812500002, 80.09423828125],\n                            [51.409277343750006, 79.94423828125]\n                        ]\n                    ],\n                    [\n                        [\n                            [59.68886718750005, 79.95581054687506],\n                            [58.91923828125002, 79.98461914062506],\n                            [59.54453125000006, 80.11884765624995],\n                            [59.68886718750005, 79.95581054687506]\n                        ]\n                    ],\n                    [\n                        [\n                            [97.67451171875004, 80.15825195312499],\n                            [97.65166015625002, 79.76064453125],\n                            [98.59648437500002, 80.05219726562495],\n                            [100.0612304687501, 79.77709960937506],\n                            [99.68066406250003, 79.32333984374998],\n                            [99.04179687500007, 79.29301757812502],\n                            [99.92929687500012, 78.96142578124997],\n                            [98.41113281250003, 78.78779296875004],\n                            [95.53105468750007, 79.09809570312501],\n                            [95.02041015625005, 79.05268554687498],\n                            [94.21875, 79.40234375],\n                            [93.07080078124997, 79.49531250000001],\n                            [94.98730468749997, 80.096826171875],\n                            [95.28134765625012, 80.030517578125],\n                            [97.67451171875004, 80.15825195312499]\n                        ]\n                    ],\n                    [\n                        [\n                            [50.05175781250003, 80.07431640625003],\n                            [49.55605468750005, 80.15893554687503],\n                            [49.883691406249994, 80.230224609375],\n                            [50.05175781250003, 80.07431640625003]\n                        ]\n                    ],\n                    [\n                        [\n                            [57.07871093750006, 80.35092773437498],\n                            [56.986914062500006, 80.07148437499998],\n                            [55.811621093750006, 80.08715820312497],\n                            [56.02441406250003, 80.34130859374997],\n                            [57.07871093750006, 80.35092773437498]\n                        ]\n                    ],\n                    [\n                        [\n                            [53.521386718749994, 80.18520507812497],\n                            [52.34355468750002, 80.213232421875],\n                            [52.85390625, 80.40239257812499],\n                            [53.85166015625006, 80.26835937500005],\n                            [53.521386718749994, 80.18520507812497]\n                        ]\n                    ],\n                    [\n                        [\n                            [57.95625, 80.12324218749995],\n                            [57.33232421875002, 80.15810546875005],\n                            [57.075, 80.49394531249999],\n                            [59.25546875000006, 80.34321289062501],\n                            [58.39794921874997, 80.31875],\n                            [57.95625, 80.12324218749995]\n                        ]\n                    ],\n                    [\n                        [\n                            [54.41533203125002, 80.47280273437502],\n                            [53.811914062499994, 80.47622070312502],\n                            [53.87724609375002, 80.60527343750002],\n                            [54.41533203125002, 80.47280273437502]\n                        ]\n                    ],\n                    [\n                        [\n                            [47.441992187500006, 80.853662109375],\n                            [48.44570312500005, 80.80600585937506],\n                            [48.68359375000003, 80.63325195312504],\n                            [47.7052734375001, 80.76518554687499],\n                            [46.141406250000074, 80.44672851562495],\n                            [45.969042968750074, 80.56948242187502],\n                            [44.9049804687501, 80.61127929687501],\n                            [47.441992187500006, 80.853662109375]\n                        ]\n                    ],\n                    [\n                        [\n                            [62.167773437500074, 80.83476562500005],\n                            [62.07578125000006, 80.616943359375],\n                            [61.05126953124997, 80.418603515625],\n                            [60.27832031249997, 80.49443359374999],\n                            [59.649804687499994, 80.43125],\n                            [59.59228515625003, 80.81650390624998],\n                            [62.167773437500074, 80.83476562500005]\n                        ]\n                    ],\n                    [\n                        [\n                            [50.278125, 80.92724609374997],\n                            [51.70361328125003, 80.68764648437502],\n                            [48.81103515625003, 80.35371093750001],\n                            [48.97753906250003, 80.16259765624997],\n                            [47.73730468749997, 80.08168945312502],\n                            [47.89296875000005, 80.23925781249997],\n                            [46.991015625000074, 80.182763671875],\n                            [46.644433593749994, 80.30034179687507],\n                            [47.89580078125002, 80.52905273437503],\n                            [49.087792968749994, 80.515771484375],\n                            [49.24433593750004, 80.82138671875],\n                            [50.278125, 80.92724609374997]\n                        ]\n                    ],\n                    [\n                        [\n                            [80.02666015625007, 80.84814453125003],\n                            [79.09853515625005, 80.81206054687505],\n                            [79.21738281250012, 80.96035156249997],\n                            [80.27958984375007, 80.94980468750003],\n                            [80.02666015625007, 80.84814453125003]\n                        ]\n                    ],\n                    [\n                        [\n                            [61.1408203125001, 80.95034179687497],\n                            [60.0783203125001, 80.99916992187497],\n                            [61.45742187499999, 81.10395507812501],\n                            [61.1408203125001, 80.95034179687497]\n                        ]\n                    ],\n                    [\n                        [\n                            [54.71894531250004, 81.11596679687497],\n                            [56.47226562500006, 80.99824218749995],\n                            [57.58037109375002, 80.75546874999998],\n                            [55.88339843750006, 80.62841796875003],\n                            [54.66816406250004, 80.73867187500002],\n                            [54.04541015624997, 80.87197265625],\n                            [54.71894531250004, 81.11596679687497]\n                        ]\n                    ],\n                    [\n                        [\n                            [58.62236328125002, 81.04165039062502],\n                            [58.930566406249994, 80.83168945312497],\n                            [58.28564453124997, 80.76489257812503],\n                            [57.21093749999997, 81.01708984374997],\n                            [58.04951171875004, 81.11845703125002],\n                            [58.62236328125002, 81.04165039062502]\n                        ]\n                    ],\n                    [\n                        [\n                            [63.37382812500002, 80.70009765624997],\n                            [62.59257812500002, 80.85302734375006],\n                            [64.80205078125002, 81.197265625],\n                            [65.43740234375005, 80.93071289062507],\n                            [63.37382812500002, 80.70009765624997]\n                        ]\n                    ],\n                    [\n                        [\n                            [91.56718750000007, 81.14121093750003],\n                            [91.2228515625001, 81.063818359375],\n                            [89.90117187500002, 81.17070312500002],\n                            [91.56718750000007, 81.14121093750003]\n                        ]\n                    ],\n                    [\n                        [\n                            [96.52656250000004, 81.0755859375],\n                            [97.86992187500007, 80.76328125000006],\n                            [97.02539062499997, 80.53554687500002],\n                            [97.29843750000006, 80.27275390625005],\n                            [93.6546875, 80.009619140625],\n                            [91.52382812500005, 80.35854492187502],\n                            [93.2625, 80.79125976562497],\n                            [92.59257812500007, 80.780859375],\n                            [92.7103515625, 80.87216796875003],\n                            [95.1595703125, 81.27099609375003],\n                            [95.80068359375005, 81.28046874999998],\n                            [96.52656250000004, 81.0755859375]\n                        ]\n                    ],\n                    [\n                        [\n                            [57.81025390625004, 81.54604492187502],\n                            [58.563867187499994, 81.41840820312504],\n                            [57.858691406250074, 81.36806640625],\n                            [57.76972656250004, 81.16972656249999],\n                            [55.71669921875005, 81.1884765625],\n                            [55.46601562500004, 81.31118164062502],\n                            [57.81025390625004, 81.54604492187502]\n                        ]\n                    ],\n                    [\n                        [\n                            [63.65097656250006, 81.60932617187501],\n                            [62.10644531249997, 81.679345703125],\n                            [63.709570312500006, 81.68730468750002],\n                            [63.65097656250006, 81.60932617187501]\n                        ]\n                    ],\n                    [\n                        [\n                            [58.29541015625003, 81.715185546875],\n                            [58.13457031250002, 81.82797851562498],\n                            [59.261816406250006, 81.85419921874998],\n                            [59.35644531250003, 81.75898437499995],\n                            [58.29541015625003, 81.715185546875]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Russia\", \"childNum\": 73 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [30.50996093750001, -1.067285156250009],\n                        [30.47705078125, -1.0830078125],\n                        [30.47021484375, -1.131152343750003],\n                        [30.508105468750017, -1.208203125000011],\n                        [30.631933593750006, -1.367480468750003],\n                        [30.710742187500017, -1.396777343750003],\n                        [30.76220703125, -1.458691406250011],\n                        [30.812597656250006, -1.563085937500006],\n                        [30.8765625, -2.143359375],\n                        [30.85498046875, -2.265429687500003],\n                        [30.828710937500006, -2.338476562500006],\n                        [30.7625, -2.371679687500006],\n                        [30.71484375, -2.363476562500011],\n                        [30.656640625000023, -2.373828125],\n                        [30.593359375, -2.396777343750003],\n                        [30.553613281250023, -2.400097656250011],\n                        [30.408496093750017, -2.31298828125],\n                        [30.117285156250006, -2.416601562500006],\n                        [29.93017578125, -2.339550781250011],\n                        [29.8681640625, -2.71640625],\n                        [29.698046875000017, -2.794726562500003],\n                        [29.390234375, -2.80859375],\n                        [29.10205078125, -2.595703125],\n                        [29.01435546875001, -2.72021484375],\n                        [28.893945312500023, -2.635058593750003],\n                        [28.876367187500023, -2.400292968750009],\n                        [29.13154296875001, -2.195117187500003],\n                        [29.196582031250017, -1.719921875000011],\n                        [29.576953125000017, -1.387890625000011],\n                        [29.82539062500001, -1.335546875],\n                        [29.930078125000023, -1.469921875000011],\n                        [30.360253906250023, -1.074609375],\n                        [30.41230468750001, -1.063085937500006],\n                        [30.46992187500001, -1.066015625],\n                        [30.50996093750001, -1.067285156250009]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Rwanda\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [41.98769531250005, 16.715625],\n                            [42.059960937499994, 16.803515625000017],\n                            [42.15781250000006, 16.570703125000023],\n                            [41.80156250000002, 16.778759765624955],\n                            [41.86044921875006, 17.002539062499977],\n                            [41.98769531250005, 16.715625]\n                        ]\n                    ],\n                    [\n                        [\n                            [46.53144531250004, 29.09624023437499],\n                            [47.433203125, 28.989550781250017],\n                            [47.671289062499994, 28.53315429687504],\n                            [48.442480468750006, 28.542919921874983],\n                            [48.80898437499999, 27.895898437499966],\n                            [48.797167968750074, 27.72431640625001],\n                            [49.2375, 27.49272460937499],\n                            [49.17509765625002, 27.43764648437505],\n                            [49.40527343749997, 27.18095703124996],\n                            [50.149804687499994, 26.66264648437499],\n                            [50.00810546875002, 26.678515625000017],\n                            [50.21386718750003, 26.30849609375005],\n                            [50.15546875000004, 26.100537109374955],\n                            [50.03164062499999, 26.11098632812505],\n                            [50.55791015625002, 25.086669921875],\n                            [50.66689453125005, 24.96381835937501],\n                            [50.72558593749997, 24.869384765625057],\n                            [50.80439453125004, 24.789257812499983],\n                            [50.928320312500006, 24.595117187500023],\n                            [50.96601562500004, 24.573925781249983],\n                            [51.022753906250074, 24.56523437499999],\n                            [51.09335937500006, 24.564648437499955],\n                            [51.178027343750074, 24.586718750000017],\n                            [51.26796875, 24.607226562500017],\n                            [51.33847656250006, 24.564355468749994],\n                            [51.41123046875006, 24.570800781250057],\n                            [51.30986328125002, 24.340380859375017],\n                            [51.56835937500003, 24.286181640625074],\n                            [51.592578125000074, 24.07885742187503],\n                            [52.55507812500005, 22.932812499999955],\n                            [55.104296875000074, 22.621484375000023],\n                            [55.185839843750074, 22.7041015625],\n                            [55.64101562499999, 22.001855468749994],\n                            [54.97734375000002, 19.995947265625006],\n                            [51.977636718750006, 18.996142578125074],\n                            [49.04199218750003, 18.58178710937503],\n                            [48.17216796875002, 18.156933593749983],\n                            [47.57958984374997, 17.448339843750034],\n                            [47.44179687499999, 17.111865234375045],\n                            [47.14355468749997, 16.946679687499966],\n                            [46.97568359375006, 16.953466796875034],\n                            [46.72763671875006, 17.26557617187501],\n                            [45.5353515625001, 17.30205078124999],\n                            [45.14804687500006, 17.427441406249955],\n                            [43.91699218749997, 17.32470703124997],\n                            [43.41796875000003, 17.516259765625023],\n                            [43.19091796875003, 17.359375],\n                            [43.16503906249997, 16.689404296874955],\n                            [42.79931640624997, 16.37177734375001],\n                            [42.29394531249997, 17.434960937499966],\n                            [41.75, 17.88574218749997],\n                            [41.22949218750003, 18.678417968749983],\n                            [40.75917968750005, 19.755468750000034],\n                            [40.080664062500006, 20.265917968750017],\n                            [39.728320312500074, 20.390332031249955],\n                            [39.27607421875004, 20.973974609375034],\n                            [39.093554687500074, 21.31035156249999],\n                            [39.14707031250006, 21.518994140624955],\n                            [38.98789062500006, 21.88173828125005],\n                            [39.06201171874997, 22.592187500000023],\n                            [38.46416015625002, 23.71186523437504],\n                            [37.91972656250002, 24.185400390625063],\n                            [37.54306640625006, 24.291650390625023],\n                            [37.18085937500004, 24.82001953125001],\n                            [37.26630859375004, 24.960058593750034],\n                            [37.14882812499999, 25.291113281249977],\n                            [35.18046875000002, 28.03486328125004],\n                            [34.722070312499994, 28.130664062500017],\n                            [34.625, 28.064501953125017],\n                            [34.95078125, 29.353515625],\n                            [36.068457031250006, 29.200537109375006],\n                            [36.28281250000006, 29.355371093750023],\n                            [36.47607421874997, 29.49511718749997],\n                            [36.59179687500003, 29.666113281250006],\n                            [36.703906250000074, 29.831640624999977],\n                            [36.75527343750005, 29.86601562499996],\n                            [37.46923828125003, 29.995068359374955],\n                            [37.49072265625003, 30.01171874999997],\n                            [37.55361328125005, 30.14458007812496],\n                            [37.63359375000002, 30.313281250000045],\n                            [37.64990234374997, 30.330957031249994],\n                            [37.669726562500074, 30.34814453125003],\n                            [37.862890625, 30.44262695312503],\n                            [37.98007812500006, 30.5],\n                            [37.47900390624997, 31.007763671874955],\n                            [37.10527343750002, 31.35517578125004],\n                            [36.95859375000006, 31.491503906250017],\n                            [37.215625, 31.55610351562501],\n                            [37.49335937500004, 31.625878906250023],\n                            [38.111425781250006, 31.78115234375005],\n                            [38.37548828124997, 31.84746093749996],\n                            [38.962304687499994, 31.99492187499999],\n                            [38.99707031249997, 32.00747070312505],\n                            [39.145410156249994, 32.12451171875],\n                            [39.36865234374997, 32.09174804687498],\n                            [39.70410156250003, 32.04252929687499],\n                            [40.02783203124997, 31.995019531249994],\n                            [40.3693359375001, 31.93896484375003],\n                            [40.47890625000005, 31.89335937499999],\n                            [42.07441406250004, 31.08037109374999],\n                            [43.77373046875002, 29.84921875],\n                            [44.71650390625004, 29.19360351562503],\n                            [46.35644531250003, 29.06367187500001],\n                            [46.53144531250004, 29.09624023437499]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Saudi Arabia\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [36.87138671875002, 21.996728515624994],\n                        [36.92695312500001, 21.58652343749999],\n                        [37.25859375000002, 21.108544921874994],\n                        [37.25722656250002, 21.03940429687499],\n                        [37.15058593750001, 21.103759765625],\n                        [37.14111328125, 20.98178710937499],\n                        [37.19316406250002, 20.12070312499999],\n                        [37.471289062500006, 18.820117187500003],\n                        [38.609472656250006, 18.005078125],\n                        [38.422460937500006, 17.823925781249997],\n                        [38.39716796875001, 17.778369140625003],\n                        [38.38554687500002, 17.751269531250003],\n                        [38.37373046875001, 17.717333984375003],\n                        [38.34736328125001, 17.68359375],\n                        [38.28984375000002, 17.637011718750003],\n                        [38.26728515625001, 17.61669921875],\n                        [38.253515625, 17.584765625],\n                        [37.78242187500001, 17.4580078125],\n                        [37.547460937500006, 17.324121093749994],\n                        [37.51015625000002, 17.288134765625003],\n                        [37.45292968750002, 17.108691406250003],\n                        [37.41103515625002, 17.06171875],\n                        [37.24882812500002, 17.056884765625],\n                        [37.16953125, 17.04140625],\n                        [37.0615234375, 17.061279296875],\n                        [37.00898437500001, 17.058886718750003],\n                        [36.995214843750006, 17.020556640625003],\n                        [36.97578125000001, 16.86655273437499],\n                        [36.97871093750001, 16.800585937500003],\n                        [36.887792968750006, 16.624658203124994],\n                        [36.91376953125001, 16.296191406250003],\n                        [36.566015625, 15.362109375],\n                        [36.4267578125, 15.132080078125],\n                        [36.44814453125002, 14.940087890624994],\n                        [36.470800781250006, 14.736474609374994],\n                        [36.52431640625002, 14.2568359375],\n                        [36.12519531250001, 12.75703125],\n                        [35.67021484375002, 12.623730468749997],\n                        [35.1123046875, 11.816552734374994],\n                        [34.93144531250002, 10.864794921874989],\n                        [34.77128906250002, 10.746191406249991],\n                        [34.571875, 10.880175781249989],\n                        [34.34394531250001, 10.658642578124997],\n                        [34.31123046875001, 10.190869140624997],\n                        [34.078125, 9.461523437499991],\n                        [33.87148437500002, 9.506152343749989],\n                        [33.96328125000002, 9.861767578124997],\n                        [33.90703125000002, 10.181445312499989],\n                        [33.13007812500001, 10.745947265624991],\n                        [33.073339843750006, 11.606103515624994],\n                        [33.199316406250006, 12.21728515625],\n                        [32.721875, 12.223095703124997],\n                        [32.73671875000002, 12.009667968749994],\n                        [32.072265625, 12.006738281249994],\n                        [32.338476562500006, 11.710107421874994],\n                        [32.42080078125002, 11.089111328125],\n                        [31.224902343750017, 9.799267578124997],\n                        [30.75537109375, 9.731201171875],\n                        [30.003027343750006, 10.277392578124989],\n                        [29.60546875, 10.065087890624994],\n                        [29.47314453125, 9.768603515624989],\n                        [28.979589843750006, 9.594189453124997],\n                        [28.844531250000017, 9.326074218749994],\n                        [28.048925781250006, 9.32861328125],\n                        [27.880859375, 9.601611328124989],\n                        [27.07421875, 9.613818359374989],\n                        [26.65869140625, 9.484130859375],\n                        [25.91914062500001, 10.169335937499994],\n                        [25.858203125000017, 10.406494140625],\n                        [25.211718750000017, 10.329931640624991],\n                        [25.066992187500006, 10.293798828124991],\n                        [24.785253906250006, 9.774658203125],\n                        [24.53193359375001, 8.886914062499997],\n                        [24.147363281250023, 8.665625],\n                        [23.53730468750001, 8.815820312499994],\n                        [23.46826171875, 9.11474609375],\n                        [23.62265625, 9.340625],\n                        [23.646289062500017, 9.822900390624994],\n                        [22.86005859375001, 10.919677734375],\n                        [22.922656250000017, 11.344873046874994],\n                        [22.591113281250017, 11.579882812499989],\n                        [22.580957031250023, 11.990136718749994],\n                        [22.472460937500017, 12.067773437499994],\n                        [22.352343750000017, 12.660449218749989],\n                        [21.928125, 12.678125],\n                        [21.825292968750006, 12.79052734375],\n                        [22.228125, 13.32958984375],\n                        [22.1064453125, 13.7998046875],\n                        [22.53857421875, 14.161865234375],\n                        [22.38154296875001, 14.550488281249997],\n                        [22.6708984375, 14.722460937500003],\n                        [22.93232421875001, 15.162109375],\n                        [22.933886718750017, 15.533105468749994],\n                        [23.10517578125001, 15.702539062499994],\n                        [23.970800781250006, 15.721533203124991],\n                        [23.980273437500017, 19.496630859375003],\n                        [23.980273437500017, 19.99594726562499],\n                        [24.9794921875, 20.002587890624994],\n                        [24.980273437500017, 21.995849609375],\n                        [28.036425781250017, 21.995361328125],\n                        [31.092675781250023, 21.994873046875],\n                        [31.260644531250023, 22.00229492187499],\n                        [31.400292968750023, 22.202441406250003],\n                        [31.486132812500017, 22.14780273437499],\n                        [31.434472656250023, 21.995849609375],\n                        [36.87138671875002, 21.996728515624994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Sudan\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [34.078125, 9.461523437499991],\n                        [34.07275390625, 8.545263671874991],\n                        [33.95332031250001, 8.443505859374994],\n                        [33.28105468750002, 8.437255859375],\n                        [32.99892578125002, 7.899511718749991],\n                        [33.902441406250006, 7.509521484375],\n                        [34.06425781250002, 7.225732421874994],\n                        [34.71064453125001, 6.660302734374994],\n                        [34.98359375000001, 5.858300781249994],\n                        [35.26835937500002, 5.492285156249991],\n                        [35.08447265625, 5.311865234374991],\n                        [34.87832031250002, 5.109570312499997],\n                        [34.63984375000001, 4.87548828125],\n                        [34.38017578125002, 4.620654296874989],\n                        [34.176855468750006, 4.419091796874994],\n                        [33.97607421875, 4.22021484375],\n                        [33.74160156250002, 3.985253906249994],\n                        [33.568457031250006, 3.81171875],\n                        [33.489355468750006, 3.755078125],\n                        [32.99726562500001, 3.880175781249989],\n                        [32.33574218750002, 3.706201171874994],\n                        [32.13593750000001, 3.519726562499997],\n                        [31.79804687500001, 3.802636718749994],\n                        [31.547167968750017, 3.677587890624991],\n                        [31.15234375, 3.785595703124997],\n                        [30.83857421875001, 3.49072265625],\n                        [30.757226562500023, 3.62421875],\n                        [30.586718750000017, 3.62421875],\n                        [30.50830078125, 3.835693359375],\n                        [30.194921875, 3.98193359375],\n                        [29.676855468750006, 4.5869140625],\n                        [29.469628906250023, 4.61181640625],\n                        [29.224902343750017, 4.391894531249989],\n                        [28.72705078125, 4.504980468749991],\n                        [28.427539062500017, 4.324169921874997],\n                        [28.19208984375001, 4.350244140624994],\n                        [27.7880859375, 4.644677734374994],\n                        [27.4033203125, 5.109179687499989],\n                        [27.143945312500023, 5.722949218749989],\n                        [26.514257812500006, 6.069238281249994],\n                        [26.30859375, 6.455322265625],\n                        [26.36181640625, 6.635302734374989],\n                        [25.27890625, 7.427490234375],\n                        [25.18134765625001, 7.557226562499991],\n                        [25.20039062500001, 7.807910156249989],\n                        [24.853320312500017, 8.137548828124991],\n                        [24.291406250000023, 8.29140625],\n                        [24.147363281250023, 8.665625],\n                        [24.53193359375001, 8.886914062499997],\n                        [24.785253906250006, 9.774658203125],\n                        [25.066992187500006, 10.293798828124991],\n                        [25.211718750000017, 10.329931640624991],\n                        [25.858203125000017, 10.406494140625],\n                        [25.91914062500001, 10.169335937499994],\n                        [26.65869140625, 9.484130859375],\n                        [27.07421875, 9.613818359374989],\n                        [27.880859375, 9.601611328124989],\n                        [28.048925781250006, 9.32861328125],\n                        [28.844531250000017, 9.326074218749994],\n                        [28.979589843750006, 9.594189453124997],\n                        [29.47314453125, 9.768603515624989],\n                        [29.60546875, 10.065087890624994],\n                        [30.003027343750006, 10.277392578124989],\n                        [30.75537109375, 9.731201171875],\n                        [31.224902343750017, 9.799267578124997],\n                        [32.42080078125002, 11.089111328125],\n                        [32.338476562500006, 11.710107421874994],\n                        [32.072265625, 12.006738281249994],\n                        [32.73671875000002, 12.009667968749994],\n                        [32.721875, 12.223095703124997],\n                        [33.199316406250006, 12.21728515625],\n                        [33.073339843750006, 11.606103515624994],\n                        [33.13007812500001, 10.745947265624991],\n                        [33.90703125000002, 10.181445312499989],\n                        [33.96328125000002, 9.861767578124997],\n                        [33.87148437500002, 9.506152343749989],\n                        [34.078125, 9.461523437499991]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"S. Sudan\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-12.280615234374977, 14.809033203124997],\n                        [-12.228417968749994, 14.45859375],\n                        [-12.019189453124994, 14.206494140624997],\n                        [-11.960888671874983, 13.875292968750003],\n                        [-12.05419921875, 13.633056640625],\n                        [-11.831689453124994, 13.315820312499994],\n                        [-11.634960937499983, 13.369873046875],\n                        [-11.390380859375, 12.941992187499991],\n                        [-11.389404296875, 12.404394531249991],\n                        [-12.399072265624994, 12.340087890625],\n                        [-12.930712890624989, 12.532275390624989],\n                        [-13.061279296875, 12.489990234375],\n                        [-13.082910156249994, 12.633544921875],\n                        [-13.729248046875, 12.673925781249991],\n                        [-14.06484375, 12.67529296875],\n                        [-14.349218749999977, 12.676416015624994],\n                        [-15.196093749999989, 12.679931640625],\n                        [-15.3779296875, 12.588964843749991],\n                        [-15.574804687499977, 12.490380859374994],\n                        [-15.839550781249983, 12.43789062499999],\n                        [-16.144189453124994, 12.45742187499999],\n                        [-16.24150390624999, 12.443310546874997],\n                        [-16.41630859374999, 12.36767578125],\n                        [-16.521337890624977, 12.3486328125],\n                        [-16.656933593749983, 12.364355468749991],\n                        [-16.711816406249994, 12.354833984374991],\n                        [-16.76030273437499, 12.52578125],\n                        [-16.44287109375, 12.609472656249991],\n                        [-16.59765625, 12.715283203124997],\n                        [-16.743896484375, 12.58544921875],\n                        [-16.763330078124994, 13.064160156249997],\n                        [-16.648779296874977, 13.154150390624991],\n                        [-15.834277343749989, 13.156445312499997],\n                        [-15.814404296874983, 13.325146484374997],\n                        [-15.286230468749977, 13.39599609375],\n                        [-15.151123046875, 13.556494140624991],\n                        [-14.246777343749983, 13.23583984375],\n                        [-13.826708984374989, 13.4078125],\n                        [-13.977392578124977, 13.54345703125],\n                        [-14.405468749999983, 13.503710937500003],\n                        [-15.108349609374983, 13.81210937499999],\n                        [-15.426855468749977, 13.727001953124997],\n                        [-15.509667968749994, 13.586230468750003],\n                        [-16.56230468749999, 13.587304687499994],\n                        [-16.766943359374977, 13.904931640624994],\n                        [-16.618115234374983, 14.04052734375],\n                        [-16.791748046875, 14.004150390625],\n                        [-17.168066406249977, 14.640625],\n                        [-17.345800781249977, 14.729296875],\n                        [-17.445019531249983, 14.651611328125],\n                        [-17.53564453125, 14.755126953125],\n                        [-17.147167968749983, 14.922021484374994],\n                        [-16.843408203124994, 15.293994140625003],\n                        [-16.570751953124983, 15.734423828125003],\n                        [-16.535253906249977, 15.83837890625],\n                        [-16.502050781249977, 15.917333984374991],\n                        [-16.480078124999977, 16.097216796875003],\n                        [-16.441015624999977, 16.204541015624997],\n                        [-16.239013671875, 16.531298828125003],\n                        [-15.768212890624994, 16.485107421875],\n                        [-14.990625, 16.676904296874994],\n                        [-14.300097656249989, 16.580273437499997],\n                        [-13.868457031249989, 16.148144531249997],\n                        [-13.756640624999989, 16.172509765624994],\n                        [-13.40966796875, 16.05917968749999],\n                        [-13.105273437499989, 15.57177734375],\n                        [-12.735253906249994, 15.13125],\n                        [-12.40869140625, 14.889013671874991],\n                        [-12.280615234374977, 14.809033203124997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Senegal\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [103.9697265625, 1.331445312499994],\n                        [103.65019531249999, 1.325537109374991],\n                        [103.81796875000003, 1.447070312499989],\n                        [103.9697265625, 1.331445312499994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Singapore\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-37.10332031249996, -54.065625],\n                        [-36.70380859375001, -54.10810546874999],\n                        [-36.64741210937498, -54.26230468749996],\n                        [-36.32646484374996, -54.251171875],\n                        [-35.79858398437497, -54.76347656250002],\n                        [-36.08549804687499, -54.86679687500001],\n                        [-36.885986328125, -54.33945312499996],\n                        [-37.63090820312496, -54.16748046875001],\n                        [-37.61884765625001, -54.04208984375004],\n                        [-38.017431640625034, -54.008007812500026],\n                        [-37.10332031249996, -54.065625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"S. Geo. and S. Sandw. Is.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-5.692138671874972, -15.997753906249997],\n                        [-5.782519531250017, -16.00400390625002],\n                        [-5.707861328124977, -15.90615234374998],\n                        [-5.692138671874972, -15.997753906249997]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Saint Helena\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [160.57626953125006, -11.797851562500028],\n                            [160.44306640625004, -11.814941406249957],\n                            [159.98632812499997, -11.494726562500006],\n                            [160.57626953125006, -11.797851562500028]\n                        ]\n                    ],\n                    [\n                        [\n                            [166.13320312500005, -10.757812499999972],\n                            [165.90400390625004, -10.851464843749966],\n                            [165.79101562500003, -10.784765624999963],\n                            [166.02382812500005, -10.6611328125],\n                            [166.13320312500005, -10.757812499999972]\n                        ]\n                    ],\n                    [\n                        [\n                            [161.71533203124997, -10.387304687499991],\n                            [162.10537109375005, -10.45380859375004],\n                            [162.37333984375002, -10.823242187499986],\n                            [161.78681640625004, -10.716894531249991],\n                            [161.53789062500007, -10.566406249999972],\n                            [161.4870117187501, -10.361425781249963],\n                            [161.29394531250003, -10.326464843750031],\n                            [161.30478515625012, -10.204394531250031],\n                            [161.71533203124997, -10.387304687499991]\n                        ]\n                    ],\n                    [\n                        [\n                            [161.54785156249997, -9.625683593749997],\n                            [161.55380859375012, -9.769726562500026],\n                            [161.40976562500006, -9.681640625000028],\n                            [161.36416015625, -9.353417968750037],\n                            [161.54785156249997, -9.625683593749997]\n                        ]\n                    ],\n                    [\n                        [\n                            [159.75039062500005, -9.272656250000011],\n                            [159.97060546875, -9.433300781249969],\n                            [160.35458984375006, -9.421582031249983],\n                            [160.81894531250006, -9.862792968749986],\n                            [160.64921875000002, -9.92861328124998],\n                            [159.80273437499997, -9.763476562500003],\n                            [159.61230468749997, -9.470703124999943],\n                            [159.62558593750012, -9.311230468749969],\n                            [159.75039062500005, -9.272656250000011]\n                        ]\n                    ],\n                    [\n                        [\n                            [160.1681640625001, -8.995507812500037],\n                            [160.40751953125007, -9.140332031249969],\n                            [160.10537109375, -9.080761718749997],\n                            [160.1681640625001, -8.995507812500037]\n                        ]\n                    ],\n                    [\n                        [\n                            [159.18857421875006, -9.123535156250014],\n                            [159.03632812500004, -9.075],\n                            [159.12978515625, -8.99306640624998],\n                            [159.22841796875005, -9.029980468749955],\n                            [159.18857421875006, -9.123535156250014]\n                        ]\n                    ],\n                    [\n                        [\n                            [158.10791015625003, -8.684179687500034],\n                            [157.93759765625006, -8.73642578125002],\n                            [157.90927734375006, -8.565625],\n                            [158.10546874999997, -8.536816406250026],\n                            [158.10791015625003, -8.684179687500034]\n                        ]\n                    ],\n                    [\n                        [\n                            [157.38896484375002, -8.713476562499963],\n                            [157.2123046875, -8.565039062500006],\n                            [157.37949218750012, -8.420898437499943],\n                            [157.38896484375002, -8.713476562499963]\n                        ]\n                    ],\n                    [\n                        [\n                            [160.7494140625, -8.313964843750014],\n                            [160.99765625000006, -8.612011718749983],\n                            [160.94433593750003, -8.799023437499983],\n                            [161.15869140624997, -8.961816406250009],\n                            [161.36738281250004, -9.61123046874998],\n                            [160.77207031250012, -8.963867187499986],\n                            [160.7140625000001, -8.539257812499997],\n                            [160.59042968750006, -8.372753906249997],\n                            [160.7494140625, -8.313964843750014]\n                        ]\n                    ],\n                    [\n                        [\n                            [157.76347656250002, -8.242187499999957],\n                            [157.89843749999997, -8.506347656249943],\n                            [157.81933593750003, -8.612011718749983],\n                            [157.58789062500003, -8.445410156249963],\n                            [157.5580078125, -8.269921875],\n                            [157.30244140625004, -8.33330078124996],\n                            [157.21757812500002, -8.262792968749977],\n                            [157.490625, -7.965722656250037],\n                            [157.76347656250002, -8.242187499999957]\n                        ]\n                    ],\n                    [\n                        [\n                            [157.171875, -8.108105468749997],\n                            [156.95830078125002, -8.014355468749997],\n                            [157.02412109375004, -7.867871093749997],\n                            [157.18613281250006, -7.941210937500017],\n                            [157.171875, -8.108105468749997]\n                        ]\n                    ],\n                    [\n                        [\n                            [156.687890625, -7.92304687500004],\n                            [156.5109375000001, -7.707812499999974],\n                            [156.5609375, -7.574023437499989],\n                            [156.80908203124997, -7.722851562500026],\n                            [156.687890625, -7.92304687500004]\n                        ]\n                    ],\n                    [\n                        [\n                            [159.8791015625001, -8.534277343749949],\n                            [158.9440429687501, -8.04072265625004],\n                            [158.457421875, -7.544726562499974],\n                            [158.734375, -7.604296875000031],\n                            [159.43144531250002, -8.029003906249955],\n                            [159.84306640625002, -8.326953124999989],\n                            [159.8791015625001, -8.534277343749949]\n                        ]\n                    ],\n                    [\n                        [\n                            [155.83984374999997, -7.097167968750014],\n                            [155.67753906250002, -7.08896484375002],\n                            [155.73896484375004, -6.972949218750017],\n                            [155.83984374999997, -7.097167968750014]\n                        ]\n                    ],\n                    [\n                        [\n                            [157.48671875000005, -7.330371093750003],\n                            [157.44130859375, -7.425683593749966],\n                            [157.10156249999997, -7.323632812499966],\n                            [156.4525390625, -6.638281249999963],\n                            [157.03027343750003, -6.891992187499952],\n                            [157.19335937499997, -7.160351562499997],\n                            [157.48671875000005, -7.330371093750003]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Solomon Is.\", \"childNum\": 16 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-12.526074218749926, 7.436328125000017],\n                            [-12.951611328124926, 7.570849609374989],\n                            [-12.615234374999972, 7.63720703125],\n                            [-12.5125, 7.582421875000037],\n                            [-12.526074218749926, 7.436328125000017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-10.758593749999989, 9.385351562499991],\n                            [-10.682714843750006, 9.289355468749974],\n                            [-10.687646484374937, 9.261132812499994],\n                            [-10.749951171874926, 9.12236328124996],\n                            [-10.747021484374955, 9.095263671875045],\n                            [-10.726855468749932, 9.081689453125023],\n                            [-10.615966796875, 9.059179687499977],\n                            [-10.500537109375017, 8.687548828125017],\n                            [-10.677343749999977, 8.400585937499997],\n                            [-10.712109374999955, 8.335253906250017],\n                            [-10.686962890624983, 8.321679687500009],\n                            [-10.652636718749989, 8.330273437499983],\n                            [-10.604003906249943, 8.319482421874994],\n                            [-10.55771484374992, 8.315673828125028],\n                            [-10.496435546874977, 8.362109374999974],\n                            [-10.394433593749966, 8.480957031250028],\n                            [-10.360058593749983, 8.49550781249998],\n                            [-10.283203124999972, 8.48515625],\n                            [-10.285742187499949, 8.454101562499986],\n                            [-10.314648437499983, 8.310839843750017],\n                            [-10.359814453124926, 8.187939453125026],\n                            [-10.570849609374932, 8.071142578125034],\n                            [-10.6474609375, 7.759375],\n                            [-10.878076171874994, 7.538232421874994],\n                            [-11.267675781249977, 7.232617187499997],\n                            [-11.507519531249983, 6.906542968750003],\n                            [-12.48564453124996, 7.386279296875045],\n                            [-12.480273437499932, 7.75327148437502],\n                            [-12.697607421874977, 7.715869140625045],\n                            [-12.850878906249932, 7.818701171875034],\n                            [-12.956933593749966, 8.145312500000045],\n                            [-13.148974609374989, 8.214599609375043],\n                            [-13.272753906249989, 8.429736328124989],\n                            [-13.085009765624932, 8.42475585937504],\n                            [-12.894091796874932, 8.62978515624998],\n                            [-13.181835937499955, 8.576904296875043],\n                            [-13.206933593749994, 8.843115234375006],\n                            [-13.059472656249966, 8.881152343750031],\n                            [-13.292675781249955, 9.04921875],\n                            [-13.077294921874966, 9.069628906249974],\n                            [-12.958789062499989, 9.263330078124994],\n                            [-12.755859374999943, 9.373583984374989],\n                            [-12.557861328125, 9.704980468749994],\n                            [-12.427978515625028, 9.898144531250011],\n                            [-12.142333984375, 9.87539062499999],\n                            [-11.911083984374955, 9.993017578124977],\n                            [-11.273632812499955, 9.996533203124983],\n                            [-11.205664062499949, 9.977734374999969],\n                            [-11.180859374999955, 9.925341796875045],\n                            [-11.047460937499977, 9.786328125000054],\n                            [-10.758593749999989, 9.385351562499991]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Sierra Leone\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-89.36259765624999, 14.416015625],\n                        [-89.1205078125, 14.370214843749991],\n                        [-88.51254882812499, 13.978955078124997],\n                        [-88.504345703125, 13.964208984374991],\n                        [-88.49765625, 13.904541015625],\n                        [-88.482666015625, 13.854248046875],\n                        [-88.44912109375, 13.850976562499994],\n                        [-88.40849609374999, 13.87539062499999],\n                        [-88.27622070312499, 13.942675781250003],\n                        [-88.151025390625, 13.987353515625003],\n                        [-87.99101562499999, 13.879638671875],\n                        [-87.8919921875, 13.894970703124997],\n                        [-87.80224609375, 13.889990234374991],\n                        [-87.7314453125, 13.841064453125],\n                        [-87.71533203125, 13.812695312499997],\n                        [-87.781884765625, 13.521386718749994],\n                        [-87.930859375, 13.1806640625],\n                        [-88.68564453124999, 13.281494140625],\n                        [-88.51201171874999, 13.183935546874991],\n                        [-89.80419921875, 13.560107421875003],\n                        [-90.09521484375, 13.736523437499997],\n                        [-90.04814453124999, 13.904052734375],\n                        [-89.54716796874999, 14.241259765625003],\n                        [-89.5736328125, 14.390087890624997],\n                        [-89.36259765624999, 14.416015625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"El Salvador\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-56.26708984374997, 46.838476562500034],\n                        [-56.38476562499994, 46.81943359375006],\n                        [-56.36464843749994, 47.09897460937498],\n                        [-56.26708984374997, 46.838476562500034]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"St. Pierre and Miquelon\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [6.659960937499989, 0.120654296874989],\n                            [6.51972656250004, 0.066308593750023],\n                            [6.468164062499994, 0.22734375],\n                            [6.68691406250008, 0.404394531249977],\n                            [6.75, 0.24345703124996],\n                            [6.659960937499989, 0.120654296874989]\n                        ]\n                    ],\n                    [\n                        [\n                            [7.423828125, 1.567724609375006],\n                            [7.330664062500034, 1.603369140624991],\n                            [7.414453125000051, 1.699121093750037],\n                            [7.423828125, 1.567724609375006]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"São Tomé and Principe\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-54.03422851562499, 3.62939453125],\n                        [-54.00957031249999, 3.448535156249989],\n                        [-54.06318359375, 3.353320312499989],\n                        [-54.18803710937499, 3.178759765624989],\n                        [-54.203125, 3.13818359375],\n                        [-54.17070312499999, 2.993603515624997],\n                        [-54.18808593749999, 2.874853515624991],\n                        [-54.1955078125, 2.81787109375],\n                        [-54.256738281249994, 2.713720703124991],\n                        [-54.402001953124994, 2.461523437499991],\n                        [-54.53593749999999, 2.343310546874989],\n                        [-54.56840820312499, 2.342578124999989],\n                        [-54.604736328125, 2.335791015624991],\n                        [-54.61625976562499, 2.326757812499991],\n                        [-54.661865234375, 2.327539062499994],\n                        [-54.697412109374994, 2.359814453124997],\n                        [-54.72221679687499, 2.441650390625],\n                        [-54.87607421874999, 2.450390625],\n                        [-54.92656249999999, 2.497363281249989],\n                        [-54.968408203124994, 2.54833984375],\n                        [-54.978662109374994, 2.59765625],\n                        [-55.005810546875, 2.59296875],\n                        [-55.0703125, 2.54833984375],\n                        [-55.11411132812499, 2.539208984374994],\n                        [-55.1876953125, 2.547509765624994],\n                        [-55.286035156249994, 2.499658203124994],\n                        [-55.343994140625, 2.48876953125],\n                        [-55.38535156249999, 2.440625],\n                        [-55.73056640624999, 2.406152343749994],\n                        [-55.957470703125, 2.520458984374997],\n                        [-55.99350585937499, 2.497509765624997],\n                        [-56.02036132812499, 2.392773437499997],\n                        [-56.0451171875, 2.364404296874994],\n                        [-56.087792968749994, 2.34130859375],\n                        [-56.12939453125, 2.299511718749997],\n                        [-56.1376953125, 2.259033203125],\n                        [-56.073632812499994, 2.236767578124997],\n                        [-56.02006835937499, 2.158154296874997],\n                        [-55.96196289062499, 2.095117187499994],\n                        [-55.91533203124999, 2.03955078125],\n                        [-55.921630859375, 1.976660156249991],\n                        [-55.929638671875, 1.8875],\n                        [-56.01992187499999, 1.842236328124997],\n                        [-56.4828125, 1.942138671875],\n                        [-56.704345703125, 2.036474609374991],\n                        [-57.19736328124999, 2.853271484375],\n                        [-57.303662109375, 3.377099609374994],\n                        [-57.646728515625, 3.39453125],\n                        [-58.05429687499999, 4.101660156249991],\n                        [-57.84599609374999, 4.668164062499997],\n                        [-57.91704101562499, 4.820410156249991],\n                        [-57.711083984374994, 4.991064453124991],\n                        [-57.331005859375, 5.020166015624994],\n                        [-57.20981445312499, 5.195410156249991],\n                        [-57.3185546875, 5.335351562499994],\n                        [-57.194775390625, 5.5484375],\n                        [-56.96982421874999, 5.992871093749997],\n                        [-56.235595703125, 5.885351562499991],\n                        [-55.897607421874994, 5.699316406249991],\n                        [-55.909912109375, 5.892626953124989],\n                        [-55.648339843749994, 5.985888671874989],\n                        [-54.83369140625, 5.988330078124989],\n                        [-54.05419921875, 5.807910156249989],\n                        [-54.08046875, 5.502246093749989],\n                        [-54.4796875, 4.836523437499991],\n                        [-54.350732421874994, 4.054101562499994],\n                        [-54.03422851562499, 3.62939453125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Suriname\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [22.538671875, 49.072705078125],\n                        [22.52412109375001, 49.031396484374994],\n                        [22.389453125000017, 48.873486328125],\n                        [22.295214843750017, 48.685839843749996],\n                        [22.142871093750017, 48.568505859374994],\n                        [22.1318359375, 48.405322265624996],\n                        [21.766992187500023, 48.3380859375],\n                        [21.45136718750001, 48.55224609375],\n                        [20.490039062500017, 48.526904296874996],\n                        [20.333789062500017, 48.295556640624994],\n                        [19.95039062500001, 48.146630859374994],\n                        [19.625390625000023, 48.223095703125],\n                        [18.791894531250023, 48.000292968749996],\n                        [18.72421875, 47.787158203124996],\n                        [17.76191406250001, 47.770166015624994],\n                        [17.147363281250023, 48.00595703125],\n                        [16.86542968750001, 48.3869140625],\n                        [16.953125, 48.598828125],\n                        [17.135644531250023, 48.841064453125],\n                        [17.75849609375001, 48.888134765625],\n                        [18.0859375, 49.06513671875],\n                        [18.160937500000017, 49.257373046874996],\n                        [18.83222656250001, 49.510791015624996],\n                        [19.1494140625, 49.4],\n                        [19.44160156250001, 49.597705078124996],\n                        [19.77392578125, 49.37216796875],\n                        [19.756640625000017, 49.204394531249996],\n                        [20.0576171875, 49.181298828124994],\n                        [20.36298828125001, 49.38525390625],\n                        [20.868457031250017, 49.314697265625],\n                        [21.079394531250017, 49.418261718749996],\n                        [21.6396484375, 49.411962890625],\n                        [22.020117187500006, 49.209521484374996],\n                        [22.473046875000023, 49.081298828125],\n                        [22.538671875, 49.072705078125]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Slovakia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [16.516210937500006, 46.499902343749994],\n                        [16.427636718750023, 46.5244140625],\n                        [16.321191406250023, 46.534619140625],\n                        [16.1064453125, 46.382226562499994],\n                        [15.608984375, 46.171923828124996],\n                        [15.592578125000017, 46.139990234375],\n                        [15.596875, 46.109228515625],\n                        [15.675585937500017, 45.983691406249996],\n                        [15.652148437500017, 45.862158203125],\n                        [15.277050781250011, 45.7326171875],\n                        [15.353710937500011, 45.659912109375],\n                        [15.283593750000023, 45.5796875],\n                        [15.291210937500011, 45.541552734374996],\n                        [15.32666015625, 45.502294921875],\n                        [15.339453125, 45.467041015625],\n                        [15.242089843750023, 45.44140625],\n                        [15.110449218750006, 45.45078125],\n                        [14.95458984375, 45.499902343749994],\n                        [14.793066406250006, 45.47822265625],\n                        [14.649511718750006, 45.571484375],\n                        [14.591796875, 45.651269531249994],\n                        [14.56884765625, 45.6572265625],\n                        [14.548448660714302, 45.628388671875],\n                        [14.507586495535731, 45.59039341517857],\n                        [14.42734375, 45.505761718749994],\n                        [14.369921875000017, 45.4814453125],\n                        [13.878710937500017, 45.428369140624994],\n                        [13.577929687500017, 45.516894531249996],\n                        [13.8447265625, 45.59287109375],\n                        [13.831152343750006, 45.680419921875],\n                        [13.663476562500023, 45.7919921875],\n                        [13.6005859375, 45.979785156249996],\n                        [13.509179687500023, 45.973779296874994],\n                        [13.487695312500023, 45.987109375],\n                        [13.480273437500017, 46.009228515625],\n                        [13.486425781250006, 46.03955078125],\n                        [13.548046875000011, 46.089111328125],\n                        [13.616601562500023, 46.133105468749996],\n                        [13.634960937500011, 46.157763671874996],\n                        [13.632519531250011, 46.177050781249996],\n                        [13.420996093750006, 46.212304687499994],\n                        [13.399511718750006, 46.317529296874994],\n                        [13.563281250000017, 46.415087890624996],\n                        [13.637109375000023, 46.448535156249996],\n                        [13.6796875, 46.462890625],\n                        [13.7, 46.520263671875],\n                        [14.5498046875, 46.399707031249996],\n                        [14.893261718750011, 46.605908203125],\n                        [15.957617187500006, 46.677636718749994],\n                        [16.093066406250017, 46.86328125],\n                        [16.283593750000023, 46.857275390625],\n                        [16.516210937500006, 46.499902343749994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Slovenia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [16.52851562500001, 56.29052734375],\n                            [16.431640625, 56.24375],\n                            [16.41230468750004, 56.568994140624994],\n                            [17.02539062499997, 57.345068359375006],\n                            [16.52851562500001, 56.29052734375]\n                        ]\n                    ],\n                    [\n                        [\n                            [19.076464843750045, 57.8359375],\n                            [18.813867187500023, 57.70620117187502],\n                            [18.907910156250068, 57.39833984375002],\n                            [18.146386718749994, 56.920507812500006],\n                            [18.285351562500068, 57.08320312500001],\n                            [18.136523437500045, 57.55664062500003],\n                            [18.53740234374999, 57.83056640625006],\n                            [18.90058593750001, 57.91547851562504],\n                            [19.076464843750045, 57.8359375]\n                        ]\n                    ],\n                    [\n                        [\n                            [19.156347656250063, 57.92260742187497],\n                            [19.086523437500034, 57.86499023437506],\n                            [19.134863281250034, 57.98134765625002],\n                            [19.331445312500023, 57.962890625],\n                            [19.156347656250063, 57.92260742187497]\n                        ]\n                    ],\n                    [\n                        [\n                            [24.15546875000004, 65.80527343750006],\n                            [23.102343750000074, 65.73535156250003],\n                            [22.400976562500006, 65.86210937499999],\n                            [22.254003906250006, 65.59755859375002],\n                            [21.565527343750063, 65.40810546874997],\n                            [21.609179687500074, 65.261376953125],\n                            [21.410351562500068, 65.31743164062505],\n                            [21.57392578125001, 65.12578124999999],\n                            [21.138183593750057, 64.80869140625006],\n                            [21.519628906250034, 64.46308593749998],\n                            [20.76269531250003, 63.86782226562505],\n                            [18.60644531250003, 63.17827148437499],\n                            [18.31289062500008, 62.996386718750045],\n                            [18.46308593750004, 62.895849609375006],\n                            [18.170019531250034, 62.789355468750074],\n                            [17.906640625000023, 62.88676757812502],\n                            [18.037304687500068, 62.60053710937498],\n                            [17.834472656250057, 62.50273437500002],\n                            [17.410253906250063, 62.508398437500034],\n                            [17.633691406249994, 62.23300781250006],\n                            [17.374511718750057, 61.866308593750034],\n                            [17.465429687500006, 61.68447265625005],\n                            [17.196386718750006, 61.72456054687504],\n                            [17.13076171875005, 61.57573242187499],\n                            [17.25097656250003, 60.70078125],\n                            [17.6611328125, 60.53515625000003],\n                            [17.955761718750068, 60.589794921874955],\n                            [18.85273437500001, 60.02587890625],\n                            [18.970507812500045, 59.757226562499994],\n                            [17.964257812500023, 59.359375],\n                            [18.56025390625004, 59.39448242187498],\n                            [18.285351562500068, 59.109375],\n                            [16.978125, 58.65415039062506],\n                            [16.214257812500023, 58.636669921874955],\n                            [16.92382812499997, 58.49257812499999],\n                            [16.651953125, 58.43432617187503],\n                            [16.65224609375008, 57.50068359374998],\n                            [16.348730468750063, 56.70927734374996],\n                            [15.826660156250028, 56.12495117187501],\n                            [14.782031250000017, 56.16191406250002],\n                            [14.754785156250051, 56.03315429687498],\n                            [14.401953125000034, 55.97675781250004],\n                            [14.21503906250004, 55.83261718749998],\n                            [14.341699218749994, 55.52773437500002],\n                            [14.17373046875008, 55.396630859374966],\n                            [12.885839843750063, 55.41137695312506],\n                            [12.973925781250074, 55.748144531250006],\n                            [12.471191406250057, 56.29052734375],\n                            [12.801660156250051, 56.263916015625],\n                            [12.65644531250004, 56.44057617187502],\n                            [12.857421875000028, 56.45239257812503],\n                            [12.883691406250051, 56.61772460937496],\n                            [12.421484375000034, 56.906396484374966],\n                            [11.449316406250063, 58.118359374999955],\n                            [11.43154296875008, 58.339990234374994],\n                            [11.24824218750004, 58.369140625],\n                            [11.14716796875004, 58.98862304687498],\n                            [11.19580078125, 59.07827148437505],\n                            [11.388281250000063, 59.036523437499966],\n                            [11.470703125000057, 58.909521484375034],\n                            [11.64277343750004, 58.92607421875002],\n                            [11.798144531250074, 59.28989257812498],\n                            [11.680761718750034, 59.59228515625003],\n                            [12.486132812500074, 60.10678710937506],\n                            [12.588671874999989, 60.450732421875045],\n                            [12.29414062500004, 61.00268554687506],\n                            [12.706054687500028, 61.059863281250074],\n                            [12.88076171875008, 61.35229492187506],\n                            [12.155371093750006, 61.720751953125045],\n                            [12.303515625000074, 62.28559570312501],\n                            [11.999902343750051, 63.29169921875001],\n                            [12.175195312500051, 63.595947265625],\n                            [12.792773437500017, 64],\n                            [13.203515625000023, 64.07509765625],\n                            [13.960546875000063, 64.01401367187498],\n                            [14.141210937500006, 64.17353515624998],\n                            [14.077636718750028, 64.464013671875],\n                            [13.650292968750023, 64.58154296874997],\n                            [14.47968750000004, 65.30146484374998],\n                            [14.543261718750045, 66.12934570312498],\n                            [15.483789062500051, 66.30595703124999],\n                            [15.422949218750006, 66.48984374999998],\n                            [16.40351562500004, 67.05498046875002],\n                            [16.12744140625, 67.42583007812507],\n                            [16.783593750000023, 67.89501953125],\n                            [17.324609375000023, 68.10380859374999],\n                            [17.91669921875001, 67.96489257812502],\n                            [18.303027343750045, 68.55541992187497],\n                            [19.969824218750063, 68.35639648437501],\n                            [20.348046875000023, 68.84873046875003],\n                            [20.116699218750057, 69.02089843750005],\n                            [20.622167968750006, 69.036865234375],\n                            [21.99746093750005, 68.52060546874998],\n                            [22.854101562500034, 68.36733398437502],\n                            [23.63886718750004, 67.95439453125002],\n                            [23.454882812500045, 67.46025390625007],\n                            [23.733593750000068, 67.42290039062499],\n                            [23.64150390625005, 67.12939453124997],\n                            [23.988574218750045, 66.81054687500003],\n                            [23.700292968750034, 66.25263671874998],\n                            [24.15546875000004, 65.80527343750006]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Sweden\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [31.9482421875, -25.957617187500006],\n                        [32.060546875, -26.018359375],\n                        [32.04140625000002, -26.28125],\n                        [32.10595703125, -26.52001953125],\n                        [32.112890625, -26.839453125],\n                        [32.02480468750002, -26.811132812500006],\n                        [31.994726562500006, -26.817480468750006],\n                        [31.967187500000023, -26.96064453125001],\n                        [31.946093750000017, -27.173632812500003],\n                        [31.958398437500023, -27.30585937500001],\n                        [31.742578125000023, -27.30996093750001],\n                        [31.469531250000017, -27.295507812500006],\n                        [31.274023437500006, -27.238378906250006],\n                        [31.063378906250023, -27.1123046875],\n                        [30.938085937500006, -26.915820312500003],\n                        [30.88330078125, -26.79238281250001],\n                        [30.806738281250006, -26.785253906250006],\n                        [30.794335937500023, -26.764257812500006],\n                        [30.803320312500006, -26.41347656250001],\n                        [31.08808593750001, -25.98066406250001],\n                        [31.207324218750017, -25.843359375],\n                        [31.33515625000001, -25.75556640625001],\n                        [31.382617187500017, -25.74296875],\n                        [31.415136718750006, -25.74658203125],\n                        [31.921679687500017, -25.96875],\n                        [31.9482421875, -25.957617187500006]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Swaziland\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [55.54033203125002, -4.693066406250011],\n                        [55.54296875, -4.785546875],\n                        [55.383398437500006, -4.609277343750009],\n                        [55.45576171875001, -4.558789062500011],\n                        [55.54033203125002, -4.693066406250011]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Seychelles\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [42.358984375, 37.10859375],\n                        [41.78857421875, 36.59716796875],\n                        [41.41679687500002, 36.5146484375],\n                        [41.295996093750006, 36.383349609374996],\n                        [41.354101562500006, 35.640429687499996],\n                        [41.19472656250002, 34.768994140625],\n                        [40.98701171875001, 34.429052734375],\n                        [38.773535156250006, 33.372216796874994],\n                        [36.818359375, 32.317285156249994],\n                        [36.3720703125, 32.3869140625],\n                        [35.78730468750001, 32.734912109374996],\n                        [35.91347656250002, 32.94960937499999],\n                        [35.869140625, 33.43173828125],\n                        [36.03447265625002, 33.58505859375],\n                        [35.98613281250002, 33.75263671875],\n                        [36.36503906250002, 33.83935546875],\n                        [36.27783203125, 33.92529296875],\n                        [36.5849609375, 34.221240234374996],\n                        [36.50439453125, 34.432373046875],\n                        [36.32988281250002, 34.499609375],\n                        [36.383886718750006, 34.65791015625],\n                        [35.97626953125001, 34.629199218749996],\n                        [35.902441406250006, 35.420703125],\n                        [35.76445312500002, 35.571582031249996],\n                        [35.83964843750002, 35.84921875],\n                        [35.892675781250006, 35.916552734374996],\n                        [35.96757812500002, 35.910058593749994],\n                        [36.12734375000002, 35.831445312499994],\n                        [36.15361328125002, 35.833886718749994],\n                        [36.34755859375002, 36.003515625],\n                        [36.37539062500002, 36.171240234375],\n                        [36.63671875, 36.233984375],\n                        [36.64140625000002, 36.263525390625],\n                        [36.5375, 36.45742187499999],\n                        [36.54667968750002, 36.50634765625],\n                        [36.596875, 36.7013671875],\n                        [36.62841796875, 36.777685546875],\n                        [36.65859375000002, 36.802539062499996],\n                        [36.77656250000001, 36.79267578125],\n                        [36.94179687500002, 36.7583984375],\n                        [36.9853515625, 36.702392578125],\n                        [37.06621093750002, 36.652636718749996],\n                        [37.43632812500002, 36.643310546875],\n                        [37.523535156250006, 36.6783203125],\n                        [37.7203125, 36.743701171874996],\n                        [37.90664062500002, 36.79462890625],\n                        [38.19169921875002, 36.9015625],\n                        [38.7666015625, 36.693115234375],\n                        [38.90644531250001, 36.694677734375],\n                        [39.1083984375, 36.680566406249994],\n                        [39.35664062500001, 36.681591796875],\n                        [39.50146484375, 36.70224609375],\n                        [39.6865234375, 36.738623046875],\n                        [40.01640625000002, 36.826074218749994],\n                        [40.705664062500006, 37.097705078124996],\n                        [41.886816406250006, 37.156396484374994],\n                        [42.05986328125002, 37.2060546875],\n                        [42.16787109375002, 37.288623046874996],\n                        [42.202734375, 37.29726562499999],\n                        [42.24755859375, 37.2822265625],\n                        [42.2685546875, 37.2765625],\n                        [42.31289062500002, 37.22958984375],\n                        [42.358984375, 37.10859375]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Syria\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-72.3328125, 21.85136718749999],\n                        [-72.14433593750002, 21.79272460937503],\n                        [-72.33544921874994, 21.758007812499983],\n                        [-72.3328125, 21.85136718749999]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Turks and Caicos Is.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [23.980273437500017, 19.496630859375003],\n                        [23.970800781250006, 15.721533203124991],\n                        [23.10517578125001, 15.702539062499994],\n                        [22.933886718750017, 15.533105468749994],\n                        [22.93232421875001, 15.162109375],\n                        [22.6708984375, 14.722460937500003],\n                        [22.38154296875001, 14.550488281249997],\n                        [22.53857421875, 14.161865234375],\n                        [22.1064453125, 13.7998046875],\n                        [22.228125, 13.32958984375],\n                        [21.825292968750006, 12.79052734375],\n                        [21.928125, 12.678125],\n                        [22.352343750000017, 12.660449218749989],\n                        [22.472460937500017, 12.067773437499994],\n                        [22.580957031250023, 11.990136718749994],\n                        [22.591113281250017, 11.579882812499989],\n                        [22.922656250000017, 11.344873046874994],\n                        [22.86005859375001, 10.919677734375],\n                        [22.49384765625001, 10.996240234374994],\n                        [21.771484375, 10.642822265625],\n                        [21.682714843750006, 10.289843749999989],\n                        [20.773242187500017, 9.405664062499994],\n                        [20.342089843750017, 9.127099609374994],\n                        [18.95625, 8.938867187499994],\n                        [18.886035156250017, 8.836035156249991],\n                        [19.108691406250017, 8.656152343749994],\n                        [18.56416015625001, 8.0458984375],\n                        [17.6494140625, 7.98359375],\n                        [16.784765625, 7.550976562499997],\n                        [16.545312500000023, 7.865478515625],\n                        [16.37890625, 7.683544921874997],\n                        [15.957617187500006, 7.507568359375],\n                        [15.480078125, 7.523779296874991],\n                        [15.5498046875, 7.787890624999989],\n                        [15.1162109375, 8.557324218749997],\n                        [14.332324218750017, 9.20351562499999],\n                        [13.977246093750011, 9.691552734374994],\n                        [14.243261718750006, 9.979736328125],\n                        [15.654882812500006, 10.0078125],\n                        [15.276074218750011, 10.357373046874997],\n                        [15.132226562500023, 10.648486328124989],\n                        [15.029882812500006, 11.11367187499999],\n                        [15.08125, 11.845507812499989],\n                        [14.847070312500023, 12.502099609374994],\n                        [14.461718750000017, 13.021777343749989],\n                        [14.244824218750011, 13.07734375],\n                        [14.06396484375, 13.07851562499999],\n                        [13.932324218750011, 13.258496093749997],\n                        [13.606347656250023, 13.70458984375],\n                        [13.505761718750023, 14.134423828124994],\n                        [13.4482421875, 14.380664062500003],\n                        [14.367968750000017, 15.750146484374994],\n                        [15.474316406250011, 16.908398437499997],\n                        [15.735058593750011, 19.904052734375],\n                        [15.963183593750017, 20.34619140625],\n                        [15.587109375000011, 20.733300781249994],\n                        [15.607324218750023, 20.954394531250003],\n                        [15.181835937500011, 21.523388671874997],\n                        [14.97900390625, 22.99619140624999],\n                        [15.984082031250011, 23.445214843749994],\n                        [20.14765625000001, 21.38925781249999],\n                        [23.980273437500017, 19.496630859375003]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Chad\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [0.900488281250006, 10.993261718749991],\n                        [0.763378906250011, 10.386669921874997],\n                        [1.330078125, 9.996972656249994],\n                        [1.3857421875, 9.361669921874991],\n                        [1.600195312500006, 9.050048828125],\n                        [1.624707031250011, 6.997314453125],\n                        [1.530957031250011, 6.992431640625],\n                        [1.777929687500006, 6.294628906249997],\n                        [1.62265625, 6.216796875],\n                        [1.187207031250011, 6.089404296874989],\n                        [0.736914062500006, 6.452587890624997],\n                        [0.525585937500011, 6.850927734374991],\n                        [0.634765625, 7.353662109374994],\n                        [0.5, 7.546875],\n                        [0.686328125000017, 8.354882812499994],\n                        [0.37255859375, 8.75927734375],\n                        [0.48876953125, 8.851464843749994],\n                        [0.525683593750017, 9.398486328124989],\n                        [0.2333984375, 9.463525390624994],\n                        [0.342578125000017, 9.604150390624994],\n                        [0.264550781250023, 9.644726562499997],\n                        [0.380859375, 10.291845703124991],\n                        [-0.08632812499999, 10.673046875],\n                        [0.009423828125023, 11.02099609375],\n                        [-0.068603515625, 11.115625],\n                        [0.49267578125, 10.954980468749994],\n                        [0.900488281250006, 10.993261718749991]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Togo\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [98.40908203125005, 7.90205078125004],\n                            [98.2962890625, 7.776074218750054],\n                            [98.32207031250007, 8.166308593749974],\n                            [98.4349609375, 8.085644531249969],\n                            [98.40908203125005, 7.90205078125004]\n                        ]\n                    ],\n                    [\n                        [\n                            [100.070703125, 9.58603515625002],\n                            [99.96240234375003, 9.421630859375],\n                            [99.93955078125006, 9.559960937500037],\n                            [100.070703125, 9.58603515625002]\n                        ]\n                    ],\n                    [\n                        [\n                            [102.42675781250003, 11.988720703125026],\n                            [102.30195312500004, 11.98081054687502],\n                            [102.27744140625006, 12.151855468750043],\n                            [102.42675781250003, 11.988720703125026]\n                        ]\n                    ],\n                    [\n                        [\n                            [100.12246093750005, 20.316650390625057],\n                            [100.11494140625004, 20.257666015625034],\n                            [100.13974609375012, 20.245410156250017],\n                            [100.31796875000006, 20.38588867187505],\n                            [100.51953125000003, 20.17792968750004],\n                            [100.39765625000004, 19.756103515625],\n                            [100.51357421875005, 19.553466796875],\n                            [101.21191406249997, 19.54833984375003],\n                            [101.22080078125006, 19.486621093750074],\n                            [101.19755859375007, 19.327929687500074],\n                            [101.2863281250001, 18.977148437500006],\n                            [101.04697265625012, 18.441992187500063],\n                            [101.05058593750002, 18.407031250000045],\n                            [101.1375, 18.28686523437497],\n                            [101.14394531250005, 18.14262695312499],\n                            [100.90849609375002, 17.583886718750023],\n                            [100.95585937500002, 17.541113281250006],\n                            [101.10517578125004, 17.47954101562499],\n                            [101.16748046874997, 17.49902343749997],\n                            [101.41367187500012, 17.71875],\n                            [101.55507812500005, 17.812353515625034],\n                            [101.56367187500004, 17.82050781250001],\n                            [101.6875, 17.889404296875],\n                            [101.77480468750005, 18.03339843750004],\n                            [101.81865234375002, 18.06464843750001],\n                            [101.87548828124997, 18.046435546875017],\n                            [101.94746093750004, 18.081494140624983],\n                            [102.03457031250005, 18.169824218750023],\n                            [102.10146484375, 18.210644531249983],\n                            [102.14824218750002, 18.20385742187503],\n                            [102.35185546875002, 18.045947265625017],\n                            [102.45878906250002, 17.984619140625057],\n                            [102.55253906250007, 17.96508789062497],\n                            [102.61679687500006, 17.833349609375034],\n                            [102.66064453124997, 17.817968750000034],\n                            [102.680078125, 17.824121093750023],\n                            [103.05136718750006, 18.02851562500001],\n                            [103.0912109375, 18.13823242187499],\n                            [103.14853515625006, 18.221728515625045],\n                            [103.19970703124997, 18.259472656249983],\n                            [103.26318359374997, 18.27846679687505],\n                            [103.27958984375002, 18.304980468750017],\n                            [103.24892578125, 18.338964843750034],\n                            [103.25175781250002, 18.373486328124955],\n                            [103.2882812500001, 18.408398437499955],\n                            [103.36699218750007, 18.42333984374997],\n                            [103.48798828125004, 18.418164062499983],\n                            [103.62968750000002, 18.38256835937503],\n                            [103.79228515625002, 18.316503906249977],\n                            [103.89882812500005, 18.295312500000023],\n                            [103.949609375, 18.31899414062505],\n                            [104.04873046875005, 18.216699218749994],\n                            [104.19619140625005, 17.988378906250006],\n                            [104.32265625, 17.815820312500023],\n                            [104.428125, 17.69897460937503],\n                            [104.7396484375, 17.461669921875],\n                            [104.81601562500012, 17.30029296874997],\n                            [104.75898437500004, 17.0771484375],\n                            [104.7435546875, 16.884375],\n                            [104.75058593750012, 16.647558593750063],\n                            [104.81933593750003, 16.46606445312503],\n                            [105.04716796875007, 16.160253906249977],\n                            [105.14873046875007, 16.09355468749999],\n                            [105.33066406250006, 16.037890625000017],\n                            [105.40625, 15.987451171875051],\n                            [105.39892578124997, 15.829882812500017],\n                            [105.62207031250003, 15.699951171875],\n                            [105.641015625, 15.656542968750045],\n                            [105.6388671875001, 15.585937500000057],\n                            [105.615625, 15.488281250000057],\n                            [105.49042968750004, 15.256591796875],\n                            [105.49042968750004, 15.127587890625009],\n                            [105.5333984375001, 15.041601562499991],\n                            [105.54667968750002, 14.932470703124963],\n                            [105.52304687500012, 14.843310546875003],\n                            [105.49736328125002, 14.590673828124963],\n                            [105.47558593750003, 14.530126953124977],\n                            [105.42265625000007, 14.471630859375054],\n                            [105.34218750000005, 14.416699218750054],\n                            [105.24365234375003, 14.367871093750054],\n                            [105.1833007812501, 14.346240234374989],\n                            [105.16914062500004, 14.336083984374966],\n                            [105.12597656250003, 14.280957031250011],\n                            [105.07412109375005, 14.227441406250037],\n                            [104.77900390625004, 14.427832031250006],\n                            [103.19941406250004, 14.332617187499977],\n                            [102.90927734375006, 14.136718750000028],\n                            [102.546875, 13.585693359375043],\n                            [102.33632812500005, 13.560302734375014],\n                            [102.49960937500012, 12.669970703125003],\n                            [102.75566406250002, 12.42626953125],\n                            [102.73662109375007, 12.089794921875011],\n                            [102.93388671875002, 11.706689453125037],\n                            [102.594140625, 12.203027343749994],\n                            [102.54023437500004, 12.109228515624977],\n                            [101.83574218750002, 12.640380859375014],\n                            [100.89775390625007, 12.653808593749986],\n                            [100.96269531250007, 13.431982421874991],\n                            [100.60292968750005, 13.568164062500017],\n                            [100.23564453125002, 13.48447265625002],\n                            [99.99052734375007, 13.243457031250031],\n                            [100.08994140625006, 13.045654296874972],\n                            [99.96396484375006, 12.690039062500006],\n                            [99.98906250000007, 12.170800781249994],\n                            [99.16503906250003, 10.319824218750028],\n                            [99.25390625000003, 9.265234375000034],\n                            [99.83554687500012, 9.288378906250031],\n                            [99.98955078125007, 8.589208984374977],\n                            [100.129296875, 8.428076171875006],\n                            [100.16347656250005, 8.508398437500034],\n                            [100.27939453125006, 8.268505859375011],\n                            [100.54521484375002, 7.226904296874991],\n                            [100.43935546875005, 7.280761718750043],\n                            [100.38037109375003, 7.541503906250043],\n                            [100.28378906250006, 7.551513671875043],\n                            [100.25664062500002, 7.774902343749986],\n                            [100.16074218750012, 7.599267578124994],\n                            [100.4235351562501, 7.18784179687502],\n                            [101.01787109375002, 6.860937500000034],\n                            [101.49794921875005, 6.865283203125031],\n                            [102.10107421874997, 6.242236328125031],\n                            [101.87363281250012, 5.825292968749991],\n                            [101.67841796875004, 5.778808593750028],\n                            [101.5560546875, 5.907763671875003],\n                            [101.1139648437501, 5.636767578125045],\n                            [100.98164062500004, 5.771044921875045],\n                            [101.05351562500002, 6.242578125],\n                            [100.87392578125, 6.24541015624996],\n                            [100.75449218750012, 6.460058593749991],\n                            [100.3454101562501, 6.549902343750006],\n                            [100.26142578125004, 6.682714843749963],\n                            [100.11914062499997, 6.441992187500048],\n                            [99.69599609375004, 6.87666015625004],\n                            [99.72031250000012, 7.106201171875],\n                            [99.55302734375002, 7.218798828125031],\n                            [99.59697265625002, 7.355615234375009],\n                            [99.35859375000004, 7.372216796875023],\n                            [99.26367187499997, 7.619042968750037],\n                            [99.07763671874997, 7.718066406250045],\n                            [99.05107421875002, 7.887841796874994],\n                            [98.78867187500012, 8.059814453125028],\n                            [98.703515625, 8.256738281250009],\n                            [98.57919921875006, 8.344287109374989],\n                            [98.42099609375006, 8.17822265625],\n                            [98.30546875000007, 8.226220703125009],\n                            [98.24179687500006, 8.767871093750045],\n                            [98.70253906250005, 10.19038085937504],\n                            [98.7572265625, 10.660937499999974],\n                            [99.1901367187501, 11.105273437499989],\n                            [99.61474609374997, 11.781201171875026],\n                            [99.40507812500002, 12.547900390625003],\n                            [99.12392578125, 13.030761718750043],\n                            [99.13681640625006, 13.716699218749994],\n                            [98.57001953125004, 14.359912109375031],\n                            [98.20214843749997, 14.97592773437502],\n                            [98.19101562500012, 15.204101562499972],\n                            [98.55693359375007, 15.367675781249986],\n                            [98.59238281250006, 16.05068359375005],\n                            [98.81796875000012, 16.180810546874994],\n                            [98.88828125000006, 16.351904296875034],\n                            [98.83544921875003, 16.417578125],\n                            [98.66074218750006, 16.330419921875006],\n                            [98.4388671875, 16.975683593750034],\n                            [97.7064453125, 17.79711914062503],\n                            [97.63222656250005, 18.290332031250074],\n                            [97.37392578125, 18.51796875],\n                            [97.74589843750002, 18.58818359374999],\n                            [97.816796875, 19.459960937500057],\n                            [98.01503906250005, 19.74951171874997],\n                            [98.37128906250004, 19.68916015625004],\n                            [98.9166992187501, 19.77290039062504],\n                            [99.07421875000003, 20.09936523437503],\n                            [99.48593750000006, 20.14985351562501],\n                            [99.45888671875005, 20.363037109375],\n                            [99.72011718750005, 20.32543945312497],\n                            [99.8903320312501, 20.424414062499977],\n                            [99.9542968750001, 20.415429687500023],\n                            [100.0036132812501, 20.37958984375001],\n                            [100.12246093750005, 20.316650390625057]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Thailand\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [70.66416015625, 39.85546875],\n                            [70.55957031250003, 39.790917968749994],\n                            [70.48925781250003, 39.86303710937503],\n                            [70.48281250000005, 39.88271484375005],\n                            [70.49775390625004, 39.88242187499998],\n                            [70.56708984375004, 39.86660156250005],\n                            [70.66416015625, 39.85546875]\n                        ]\n                    ],\n                    [\n                        [\n                            [70.95800781250003, 40.238867187500034],\n                            [70.59921875, 39.974511718749994],\n                            [69.96679687499997, 40.202246093750034],\n                            [69.46875, 40.020751953125],\n                            [69.47099609375002, 39.990625],\n                            [69.43193359375007, 39.909765625000034],\n                            [69.36542968750004, 39.94707031250002],\n                            [69.30722656250006, 39.968554687500045],\n                            [69.27880859374997, 39.91777343749999],\n                            [69.24472656250006, 39.82709960937498],\n                            [69.29765625000007, 39.52480468750005],\n                            [70.50117187500004, 39.58735351562501],\n                            [70.79931640625003, 39.39472656250001],\n                            [71.4703125, 39.60366210937502],\n                            [71.50302734375006, 39.58217773437502],\n                            [71.51738281250002, 39.55385742187502],\n                            [71.50585937499997, 39.51708984374997],\n                            [71.5033203125, 39.47880859374999],\n                            [71.73222656250002, 39.422998046874994],\n                            [71.77861328125007, 39.27797851562502],\n                            [72.04277343750002, 39.352148437500034],\n                            [72.08417968750004, 39.310644531250034],\n                            [72.14736328125005, 39.26074218749997],\n                            [72.22998046874997, 39.20751953124997],\n                            [72.63994140625002, 39.385986328125],\n                            [73.10927734375, 39.36191406249998],\n                            [73.2349609375, 39.37456054687499],\n                            [73.3361328125001, 39.41235351562506],\n                            [73.38740234375004, 39.442724609375034],\n                            [73.4704101562501, 39.46059570312502],\n                            [73.63164062500007, 39.44887695312502],\n                            [73.63632812500006, 39.396679687499955],\n                            [73.60732421875, 39.229199218749955],\n                            [73.8052734375, 38.968652343749994],\n                            [73.69609375000007, 38.85429687499996],\n                            [73.80166015625, 38.60688476562501],\n                            [74.02558593750004, 38.53984375000002],\n                            [74.27744140625, 38.659765625000034],\n                            [74.81230468750002, 38.46030273437498],\n                            [74.8942382812501, 37.60141601562498],\n                            [75.11875, 37.38569335937498],\n                            [74.89130859375004, 37.231640624999955],\n                            [74.875390625, 37.24199218750002],\n                            [74.83046875, 37.28593750000002],\n                            [74.73056640625006, 37.35703125],\n                            [74.659375, 37.39448242187501],\n                            [74.34902343750005, 37.41875],\n                            [74.25966796875005, 37.41542968750002],\n                            [74.20351562500005, 37.37246093750005],\n                            [74.16708984375, 37.32944335937498],\n                            [73.74960937500006, 37.23178710937498],\n                            [73.6535156250001, 37.239355468750034],\n                            [73.62753906250006, 37.261572265625006],\n                            [73.71728515625003, 37.32944335937498],\n                            [73.7337890625, 37.37578125000002],\n                            [73.72060546875, 37.41875],\n                            [73.65712890625005, 37.43046875],\n                            [73.6046875000001, 37.44604492187503],\n                            [73.48134765625, 37.4716796875],\n                            [73.38291015625006, 37.462255859375034],\n                            [73.21113281250004, 37.40849609375002],\n                            [72.89550781250003, 37.26752929687498],\n                            [72.65742187500004, 37.029052734375],\n                            [71.665625, 36.696923828124994],\n                            [71.530859375, 36.845117187499994],\n                            [71.43291015625007, 37.12753906249998],\n                            [71.5822265625001, 37.91010742187498],\n                            [71.55195312500004, 37.93315429687496],\n                            [71.48779296874997, 37.93188476562497],\n                            [71.38964843750003, 37.90629882812502],\n                            [71.31992187500006, 37.90185546875],\n                            [71.27851562500004, 37.91840820312498],\n                            [71.33271484375004, 38.170263671875034],\n                            [71.25585937499997, 38.306982421875006],\n                            [70.7359375, 38.42255859375001],\n                            [70.41777343750002, 38.075439453125],\n                            [70.21464843750002, 37.92441406250006],\n                            [70.19941406250004, 37.88603515624996],\n                            [70.25498046875006, 37.76538085937497],\n                            [70.25146484374997, 37.66416015625006],\n                            [70.18867187500004, 37.58247070312501],\n                            [70.11982421875004, 37.54350585937499],\n                            [69.9849609375, 37.566162109375],\n                            [69.8208984375, 37.60957031250004],\n                            [69.62578125000002, 37.59404296874999],\n                            [69.49208984375, 37.55307617187498],\n                            [69.42011718750004, 37.486718749999966],\n                            [69.39921875000007, 37.39931640625002],\n                            [69.42968749999997, 37.290869140625034],\n                            [69.414453125, 37.20776367187497],\n                            [69.35380859375007, 37.15004882812502],\n                            [69.3039062500001, 37.11694335937503],\n                            [69.26484375000004, 37.1083984375],\n                            [69.18017578125003, 37.158300781250034],\n                            [68.96044921875003, 37.32504882812498],\n                            [68.9118164062501, 37.33393554687501],\n                            [68.88525390624997, 37.32807617187498],\n                            [68.85537109375005, 37.31684570312501],\n                            [68.83847656250006, 37.30283203124998],\n                            [68.82373046874997, 37.27070312500001],\n                            [68.78203125000002, 37.25800781250001],\n                            [68.7232421875, 37.26801757812501],\n                            [68.6691406250001, 37.258398437500006],\n                            [68.3869140625001, 37.1375],\n                            [68.29951171875004, 37.08842773437502],\n                            [68.28476562500006, 37.036328124999955],\n                            [68.2609375000001, 37.01308593750002],\n                            [68.2121093750001, 37.02153320312496],\n                            [68.0677734375, 36.949804687500006],\n                            [67.95800781249997, 36.972021484375006],\n                            [67.83447265624997, 37.06420898437506],\n                            [67.75898437500004, 37.172216796875034],\n                            [67.7980468750001, 37.244970703125006],\n                            [67.81435546875005, 37.48701171875004],\n                            [68.3502929687501, 38.211035156250006],\n                            [68.08720703125002, 38.47353515625002],\n                            [68.13251953125004, 38.927636718749966],\n                            [67.69443359375006, 38.99462890625003],\n                            [67.64833984375005, 39.13105468750004],\n                            [67.3576171875001, 39.216699218749994],\n                            [67.426171875, 39.46557617187497],\n                            [67.71904296875007, 39.62138671875002],\n                            [68.46328125, 39.53671874999998],\n                            [68.63896484375007, 39.8388671875],\n                            [68.86875, 39.90747070312503],\n                            [68.80468750000003, 40.05034179687499],\n                            [68.9720703125, 40.08994140624998],\n                            [68.63066406250007, 40.16708984374998],\n                            [69.27490234374997, 40.19809570312498],\n                            [69.20625, 40.566552734374994],\n                            [69.35722656250002, 40.76738281249996],\n                            [69.71289062500003, 40.65698242187503],\n                            [70.40195312500006, 41.03510742187498],\n                            [70.75107421875006, 40.721777343750006],\n                            [70.37158203125003, 40.38413085937506],\n                            [70.653125, 40.201171875],\n                            [70.95800781250003, 40.238867187500034]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Tajikistan\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [66.52226562500007, 37.34848632812506],\n                        [66.471875, 37.3447265625],\n                        [65.7650390625, 37.56914062499996],\n                        [65.55498046875002, 37.25117187500004],\n                        [65.30361328125005, 37.24677734375001],\n                        [65.08964843750007, 37.237939453124994],\n                        [64.9515625, 37.19355468750001],\n                        [64.81630859375005, 37.13208007812503],\n                        [64.7824218750001, 37.05927734375001],\n                        [64.60253906250003, 36.554541015625034],\n                        [64.5658203125, 36.427587890625034],\n                        [64.51103515625002, 36.34067382812498],\n                        [64.184375, 36.14892578125],\n                        [63.8625, 36.012353515624994],\n                        [63.12998046875006, 35.84619140624997],\n                        [63.169726562500074, 35.678125],\n                        [63.05664062500003, 35.44580078125003],\n                        [62.98027343750002, 35.40917968750003],\n                        [62.85800781250006, 35.34965820312499],\n                        [62.688085937500006, 35.25532226562504],\n                        [62.3078125000001, 35.17080078125005],\n                        [62.08964843750002, 35.3796875],\n                        [61.62099609375005, 35.43232421875004],\n                        [61.34472656249997, 35.62949218750006],\n                        [61.26201171875002, 35.61958007812498],\n                        [61.25214843750004, 35.86762695312498],\n                        [61.15292968750006, 35.97675781250001],\n                        [61.212011718750006, 36.190527343750034],\n                        [61.11962890625003, 36.64257812500003],\n                        [60.34130859375003, 36.63764648437501],\n                        [60.06279296875002, 36.962890625],\n                        [59.454980468749994, 37.25283203125002],\n                        [59.30175781249997, 37.51064453125005],\n                        [58.81542968750003, 37.683496093749994],\n                        [58.261621093749994, 37.665820312500045],\n                        [57.35371093750004, 37.97333984374998],\n                        [57.1935546875001, 38.216406250000034],\n                        [56.440625, 38.249414062499994],\n                        [56.272070312500006, 38.080419921875034],\n                        [55.38085937500003, 38.051123046875034],\n                        [54.90009765625004, 37.77792968750006],\n                        [54.6994140625001, 37.47016601562498],\n                        [53.91416015625006, 37.34355468750002],\n                        [53.86865234375003, 38.949267578125045],\n                        [53.70458984375003, 39.209570312500034],\n                        [53.33632812500005, 39.34082031250006],\n                        [53.15664062499999, 39.26499023437506],\n                        [53.23564453125002, 39.608544921874966],\n                        [53.603125, 39.546972656250034],\n                        [53.472265625, 39.66879882812498],\n                        [53.48730468749997, 39.909375],\n                        [52.9875, 39.98759765625002],\n                        [53.03554687500005, 39.7744140625],\n                        [52.80468749999997, 40.054003906250045],\n                        [52.73369140625002, 40.39873046875002],\n                        [52.943457031250006, 41.03808593750006],\n                        [53.1452148437501, 40.82495117187497],\n                        [53.61523437500003, 40.818505859374994],\n                        [53.87001953125005, 40.64868164062503],\n                        [54.37734375, 40.693261718749966],\n                        [54.319433593750006, 40.83457031249998],\n                        [54.68505859375003, 40.873046875],\n                        [54.70371093750006, 41.071142578125034],\n                        [54.094824218750006, 41.51938476562506],\n                        [53.80468749999997, 42.11762695312498],\n                        [53.16416015625006, 42.09379882812502],\n                        [52.97001953125002, 41.97622070312505],\n                        [52.81484375, 41.711816406249994],\n                        [52.850390625000074, 41.20029296875006],\n                        [52.4938476562501, 41.780371093750034],\n                        [53.0558593750001, 42.14775390624999],\n                        [54.120996093749994, 42.335205078125],\n                        [54.85380859375002, 41.965185546875006],\n                        [55.434375, 41.296289062499994],\n                        [55.97744140625005, 41.32221679687504],\n                        [57.01796875, 41.26347656249996],\n                        [57.11884765625004, 41.35029296874998],\n                        [56.96406250000004, 41.856542968750006],\n                        [57.290625, 42.123779296875],\n                        [57.814257812500074, 42.18984375000005],\n                        [58.02890625, 42.48764648437506],\n                        [58.474414062500074, 42.29936523437496],\n                        [58.15156250000004, 42.628076171874966],\n                        [58.477148437500006, 42.66284179687503],\n                        [58.5890625000001, 42.778466796874966],\n                        [59.35429687500002, 42.32329101562496],\n                        [59.98515625000002, 42.21171875],\n                        [59.94179687499999, 41.97353515625002],\n                        [60.20078125000006, 41.803125],\n                        [60.07558593750005, 41.759667968749966],\n                        [60.089648437500074, 41.39941406250003],\n                        [60.454980468749994, 41.221630859374955],\n                        [61.2423828125001, 41.18920898437503],\n                        [61.496972656249994, 41.276074218749955],\n                        [61.90283203124997, 41.09370117187501],\n                        [62.48320312500002, 39.97563476562496],\n                        [63.76367187500003, 39.16054687499999],\n                        [64.3099609375, 38.97729492187497],\n                        [65.612890625, 38.23857421875002],\n                        [65.97119140624997, 38.244238281250006],\n                        [66.60625, 37.98671875000005],\n                        [66.52558593750004, 37.785742187500034],\n                        [66.51132812500006, 37.59916992187496],\n                        [66.51064453125, 37.45869140625004],\n                        [66.52226562500007, 37.34848632812506]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Turkmenistan\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [124.0363281250001, -9.341601562500031],\n                            [124.44443359375012, -9.190332031250023],\n                            [124.28232421875012, -9.427929687500026],\n                            [124.0363281250001, -9.341601562500031]\n                        ]\n                    ],\n                    [\n                        [\n                            [125.06816406250002, -9.511914062499997],\n                            [124.96015625000004, -9.213769531250009],\n                            [125.10048828125, -9.189843750000023],\n                            [125.14902343750012, -9.042578125000034],\n                            [124.93681640625007, -9.053417968750026],\n                            [124.92226562500005, -8.942480468749977],\n                            [125.17802734375002, -8.647851562499994],\n                            [125.38183593749997, -8.575390624999983],\n                            [126.61972656250006, -8.459472656249986],\n                            [126.96640625000012, -8.315722656250017],\n                            [127.29609375000004, -8.424511718749969],\n                            [126.91523437500004, -8.715234374999966],\n                            [125.40800781250002, -9.275781250000023],\n                            [125.06816406250002, -9.511914062499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [125.64609375000006, -8.139941406250003],\n                            [125.5794921875, -8.311816406250017],\n                            [125.50712890625007, -8.275097656249997],\n                            [125.64609375000006, -8.139941406250003]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Timor-Leste\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-175.1619140625, -21.169335937500023],\n                            [-175.07817382812496, -21.129003906249977],\n                            [-175.15659179687495, -21.26367187499997],\n                            [-175.36235351562496, -21.106835937499994],\n                            [-175.1619140625, -21.169335937500023]\n                        ]\n                    ],\n                    [\n                        [\n                            [-173.953515625, -18.63935546875001],\n                            [-174.06914062500002, -18.640234375],\n                            [-173.96806640624993, -18.565332031250023],\n                            [-173.953515625, -18.63935546875001]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Tonga\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-61.012109374999966, 10.134326171874989],\n                        [-61.906103515625006, 10.069140625000031],\n                        [-61.49931640624999, 10.268554687499972],\n                        [-61.47827148437497, 10.603369140624977],\n                        [-61.65117187499993, 10.718066406249974],\n                        [-60.917626953124966, 10.84023437499999],\n                        [-61.03374023437502, 10.669873046875026],\n                        [-61.012109374999966, 10.134326171874989]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Trinidad and Tobago\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [10.957617187500063, 33.72207031250005],\n                            [10.722070312500051, 33.738916015624994],\n                            [10.745214843750063, 33.88867187500006],\n                            [11.017871093749989, 33.82333984374998],\n                            [10.957617187500063, 33.72207031250005]\n                        ]\n                    ],\n                    [\n                        [\n                            [11.278027343750068, 34.753808593749994],\n                            [11.123632812500063, 34.68168945312496],\n                            [11.254882812500057, 34.82031250000006],\n                            [11.278027343750068, 34.753808593749994]\n                        ]\n                    ],\n                    [\n                        [\n                            [10.274609375000011, 31.684960937499994],\n                            [10.114941406250068, 31.46376953125005],\n                            [10.216406250000063, 30.78320312500003],\n                            [10.05976562500004, 30.58007812500003],\n                            [9.932519531250051, 30.42534179687496],\n                            [9.895019531250028, 30.387304687500034],\n                            [9.51875, 30.229394531249994],\n                            [9.224023437500023, 31.373681640624994],\n                            [9.160253906250006, 31.621337890625],\n                            [9.044042968750034, 32.072363281250034],\n                            [8.333398437500051, 32.54360351562502],\n                            [8.1125, 33.055322265624994],\n                            [7.877246093750017, 33.172119140625],\n                            [7.534375, 33.717919921874994],\n                            [7.513867187500068, 34.080517578124955],\n                            [8.24560546875, 34.73408203124998],\n                            [8.276855468750057, 34.97949218749997],\n                            [8.312109375000063, 35.084619140624994],\n                            [8.394238281250011, 35.20385742187503],\n                            [8.318066406250011, 35.654931640624994],\n                            [8.348730468750063, 36.367968750000045],\n                            [8.207617187500006, 36.518945312499994],\n                            [8.601269531250068, 36.83393554687504],\n                            [8.576562500000023, 36.93720703125001],\n                            [9.687988281250057, 37.34038085937499],\n                            [9.838476562500063, 37.30898437499999],\n                            [9.830273437499983, 37.13535156250006],\n                            [9.875585937499977, 37.25415039062503],\n                            [10.196386718750063, 37.205859375000045],\n                            [10.293261718750074, 36.781494140625],\n                            [10.412304687499983, 36.73183593750002],\n                            [11.053906250000068, 37.07250976562506],\n                            [11.12666015625004, 36.874072265625045],\n                            [10.476562500000028, 36.175146484375006],\n                            [10.590820312500028, 35.88725585937499],\n                            [11.00429687500008, 35.63383789062496],\n                            [11.120117187500057, 35.24028320312499],\n                            [10.69091796875, 34.67846679687503],\n                            [10.118359375000068, 34.280078125000045],\n                            [10.049023437500068, 34.056298828124994],\n                            [10.305273437500034, 33.72827148437497],\n                            [10.713183593750017, 33.68901367187496],\n                            [10.722753906250006, 33.514404296875],\n                            [10.958007812500057, 33.62631835937498],\n                            [11.257421875000034, 33.30883789062506],\n                            [11.202636718749972, 33.24921874999998],\n                            [11.50458984375004, 33.181933593750045],\n                            [11.502441406250028, 33.15556640624999],\n                            [11.467187500000051, 32.96572265625005],\n                            [11.459179687500011, 32.897363281249966],\n                            [11.453906250000017, 32.64257812500003],\n                            [11.533789062500034, 32.52495117187496],\n                            [11.535937500000017, 32.47333984375001],\n                            [11.504980468750034, 32.413671875000034],\n                            [11.358007812500006, 32.34521484375003],\n                            [11.168261718750074, 32.25673828125002],\n                            [11.005175781250074, 32.17270507812506],\n                            [10.826367187500068, 32.080664062500034],\n                            [10.771582031250006, 32.02119140625001],\n                            [10.60888671875, 31.929541015624977],\n                            [10.47578125000004, 31.736035156249983],\n                            [10.274609375000011, 31.684960937499994]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Tunisia\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [25.970019531250045, 40.136328125],\n                            [25.6689453125, 40.13588867187502],\n                            [25.918359375000023, 40.23798828125004],\n                            [25.970019531250045, 40.136328125]\n                        ]\n                    ],\n                    [\n                        [\n                            [43.43339843750002, 41.155517578125],\n                            [43.43945312500003, 41.10712890625001],\n                            [43.72265624999997, 40.71953124999999],\n                            [43.56933593750003, 40.48237304687498],\n                            [43.66621093750004, 40.12636718750002],\n                            [44.28925781250004, 40.040380859375006],\n                            [44.76826171875004, 39.70351562500005],\n                            [44.81718750000002, 39.65043945312496],\n                            [44.58710937500004, 39.76855468750006],\n                            [44.3893554687501, 39.422119140625],\n                            [44.02324218750002, 39.37744140625006],\n                            [44.27167968750004, 38.83603515625006],\n                            [44.2985351562501, 38.38627929687499],\n                            [44.4499023437501, 38.33422851562506],\n                            [44.21132812499999, 37.908056640625006],\n                            [44.589941406250006, 37.710351562499966],\n                            [44.574023437500074, 37.435400390625006],\n                            [44.79414062500004, 37.290380859375034],\n                            [44.76513671875003, 37.142431640625006],\n                            [44.73095703124997, 37.16528320312503],\n                            [44.66933593750005, 37.17358398437503],\n                            [44.60595703124997, 37.176025390625],\n                            [44.401953125, 37.05849609375002],\n                            [44.325585937499994, 37.0107421875],\n                            [44.28183593750006, 36.97802734374997],\n                            [44.24570312500006, 36.983300781249994],\n                            [44.20166015624997, 37.05180664062502],\n                            [44.208398437499994, 37.20263671875],\n                            [44.19179687499999, 37.249853515625034],\n                            [44.15625, 37.28295898437503],\n                            [44.11445312500004, 37.30185546875006],\n                            [44.01318359375003, 37.313525390625045],\n                            [43.83642578124997, 37.223535156249994],\n                            [43.67578125000003, 37.227246093749955],\n                            [43.09248046875004, 37.36738281249998],\n                            [42.936621093750006, 37.32475585937502],\n                            [42.77460937500004, 37.371875],\n                            [42.74111328125005, 37.361914062500034],\n                            [42.6354492187501, 37.249267578125],\n                            [42.45585937500002, 37.128710937500045],\n                            [42.358984375, 37.10859375000004],\n                            [42.31289062499999, 37.22958984374998],\n                            [42.26855468749997, 37.276562499999955],\n                            [42.24755859375003, 37.28222656250006],\n                            [42.20273437500006, 37.29726562499999],\n                            [42.16787109375005, 37.28862304687502],\n                            [42.059863281250074, 37.2060546875],\n                            [41.886816406250006, 37.156396484374994],\n                            [40.70566406250006, 37.09770507812502],\n                            [40.4503906250001, 37.00888671875006],\n                            [40.016406250000074, 36.82607421875002],\n                            [39.68652343749997, 36.73862304687506],\n                            [39.50146484374997, 36.702246093750034],\n                            [39.35664062500004, 36.68159179687498],\n                            [39.10839843749997, 36.68056640625005],\n                            [38.90644531250004, 36.69467773437498],\n                            [38.76660156249997, 36.69311523437503],\n                            [38.19169921875002, 36.90156250000004],\n                            [37.90664062500005, 36.79462890625001],\n                            [37.7203125, 36.74370117187502],\n                            [37.52353515625006, 36.678320312500034],\n                            [37.436328125000074, 36.643310546875],\n                            [37.327050781249994, 36.64658203125006],\n                            [37.18740234375005, 36.655908203124994],\n                            [37.066210937500074, 36.652636718750045],\n                            [36.98535156250003, 36.70239257812506],\n                            [36.94179687499999, 36.758398437500006],\n                            [36.77656250000004, 36.79267578124998],\n                            [36.65859375000005, 36.80253906250002],\n                            [36.62841796875003, 36.777685546875034],\n                            [36.596875, 36.70136718750001],\n                            [36.546679687500074, 36.50634765625],\n                            [36.5375, 36.457421874999966],\n                            [36.63671874999997, 36.233984375],\n                            [36.37539062499999, 36.171240234375034],\n                            [36.347558593749994, 36.003515625000034],\n                            [36.20195312500002, 35.93754882812502],\n                            [36.15361328125002, 35.83388671875005],\n                            [36.12734375, 35.831445312499994],\n                            [35.967578125000074, 35.91005859375002],\n                            [35.89267578125006, 35.91655273437502],\n                            [35.81093750000005, 36.30986328125002],\n                            [36.18847656250003, 36.65898437499999],\n                            [36.048925781250006, 36.91059570312501],\n                            [35.393164062500006, 36.57519531249997],\n                            [34.70361328125003, 36.81679687499999],\n                            [33.694726562499994, 36.18198242187498],\n                            [32.794824218749994, 36.03588867187497],\n                            [32.37773437500002, 36.18364257812496],\n                            [32.02197265625003, 36.53530273437502],\n                            [31.35253906249997, 36.80107421874999],\n                            [30.64404296874997, 36.86567382812501],\n                            [30.446093750000074, 36.269873046875034],\n                            [29.6890625, 36.15668945312498],\n                            [29.22363281249997, 36.32446289062497],\n                            [28.96962890625008, 36.71533203125003],\n                            [28.303710937500057, 36.81196289062498],\n                            [28.01943359375005, 36.63447265624998],\n                            [28.083984375000057, 36.75146484375],\n                            [27.453906250000017, 36.712158203125],\n                            [28.00537109375003, 36.83198242187498],\n                            [28.242382812500068, 37.029052734375],\n                            [27.262988281250045, 36.97656250000003],\n                            [27.30019531250005, 37.12685546875002],\n                            [27.53505859375005, 37.16386718750002],\n                            [27.06796875, 37.65791015625004],\n                            [27.224414062500074, 37.725439453125006],\n                            [27.23242187500003, 37.978662109374994],\n                            [26.29072265625001, 38.27719726562498],\n                            [26.44130859375005, 38.64121093749998],\n                            [26.67421875000008, 38.33574218750002],\n                            [27.14423828125001, 38.45195312499996],\n                            [26.906835937500034, 38.48173828124999],\n                            [26.763671875, 38.709619140624966],\n                            [27.013671875000057, 38.88686523437502],\n                            [26.814941406250057, 38.96098632812502],\n                            [26.853613281250034, 39.115625],\n                            [26.68183593750004, 39.292236328125],\n                            [26.89921874999999, 39.549658203125034],\n                            [26.113085937500074, 39.46738281249998],\n                            [26.101367187500074, 39.56894531249998],\n                            [26.18134765625004, 39.99008789062498],\n                            [26.738085937500045, 40.40024414062506],\n                            [27.28457031250008, 40.45561523437496],\n                            [27.4755859375, 40.319921875000034],\n                            [27.72802734375, 40.32880859374998],\n                            [27.84853515625005, 40.38173828125002],\n                            [27.73183593750008, 40.48149414062499],\n                            [27.87490234375008, 40.512939453125],\n                            [27.989550781250074, 40.48945312500001],\n                            [27.96259765625001, 40.369873046875],\n                            [29.00712890624999, 40.389746093750034],\n                            [28.787890625000017, 40.534033203125034],\n                            [28.95800781250003, 40.63056640624998],\n                            [29.849218750000063, 40.760107421875006],\n                            [29.113867187499977, 40.93784179687506],\n                            [29.14814453125004, 41.221044921875034],\n                            [31.25488281249997, 41.10761718750001],\n                            [31.45800781249997, 41.32001953125004],\n                            [32.306445312500074, 41.72958984374998],\n                            [33.38134765625003, 42.01757812500003],\n                            [34.75048828124997, 41.95683593749999],\n                            [35.006445312500006, 42.06328125000002],\n                            [35.15488281250006, 42.02753906250001],\n                            [35.12207031250003, 41.89111328125003],\n                            [35.297753906249994, 41.72851562500003],\n                            [35.558007812499994, 41.63403320312506],\n                            [36.05175781249997, 41.68256835937498],\n                            [36.40537109375006, 41.27460937500001],\n                            [36.77773437499999, 41.36347656250001],\n                            [37.066210937500074, 41.184423828125034],\n                            [38.38105468750004, 40.92451171875001],\n                            [39.426367187500006, 41.10644531250003],\n                            [40.26523437500006, 40.96132812500005],\n                            [41.08359375000006, 41.26118164062504],\n                            [41.41435546875002, 41.42363281249999],\n                            [41.510058593750074, 41.51748046875002],\n                            [41.70175781250006, 41.471582031249994],\n                            [41.77939453125006, 41.44052734374998],\n                            [41.823535156250074, 41.432373046875],\n                            [41.92578125000003, 41.49565429687502],\n                            [42.46640625, 41.43984375000002],\n                            [42.56738281249997, 41.55927734375001],\n                            [42.590429687500006, 41.57070312500002],\n                            [42.60683593750005, 41.57880859374998],\n                            [42.682421875000074, 41.58574218749999],\n                            [42.75410156250004, 41.57890625000002],\n                            [42.787890625000074, 41.56372070312503],\n                            [42.82167968750005, 41.49238281249998],\n                            [42.90673828125003, 41.46684570312502],\n                            [43.05712890625003, 41.35283203124996],\n                            [43.149023437500006, 41.30712890624997],\n                            [43.171289062499994, 41.28793945312498],\n                            [43.14101562499999, 41.26484374999998],\n                            [43.15283203124997, 41.23642578125006],\n                            [43.20546875000005, 41.19916992187501],\n                            [43.43339843750002, 41.155517578125]\n                        ]\n                    ],\n                    [\n                        [\n                            [27.47480468750001, 41.946875],\n                            [28.014453125000017, 41.96904296874999],\n                            [28.197851562500063, 41.55449218750002],\n                            [29.057226562500006, 41.22973632812503],\n                            [28.95625, 41.00820312499999],\n                            [28.172167968750074, 41.08071289062502],\n                            [27.49941406250005, 40.97314453124997],\n                            [27.258007812499983, 40.687353515625006],\n                            [26.772070312500034, 40.498046875],\n                            [26.202734375000034, 40.07539062500004],\n                            [26.25380859375005, 40.31469726562503],\n                            [26.792089843750034, 40.626611328124994],\n                            [26.10546875000003, 40.61132812499997],\n                            [26.03896484375008, 40.726757812499955],\n                            [26.331054687500057, 40.954492187499994],\n                            [26.330664062499977, 41.23876953125],\n                            [26.62490234375008, 41.401757812499994],\n                            [26.581347656250074, 41.60126953125004],\n                            [26.320898437500034, 41.716552734375],\n                            [26.3603515625, 41.80156249999999],\n                            [26.51142578125004, 41.82636718749998],\n                            [26.549707031250023, 41.896728515625],\n                            [26.5796875, 41.947949218749955],\n                            [26.615332031250063, 41.964892578125045],\n                            [26.884863281250006, 41.99184570312502],\n                            [26.96875, 42.02685546875006],\n                            [27.01171875, 42.05864257812496],\n                            [27.193359375000057, 42.07709960937498],\n                            [27.24433593750004, 42.09326171875],\n                            [27.294921875000057, 42.079541015624955],\n                            [27.47480468750001, 41.946875]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Turkey\", \"childNum\": 3 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [39.71132812499999, -7.977441406250023],\n                            [39.602929687499994, -7.936132812499949],\n                            [39.907128906249994, -7.649218750000031],\n                            [39.71132812499999, -7.977441406250023]\n                        ]\n                    ],\n                    [\n                        [\n                            [39.49648437499999, -6.174609375],\n                            [39.573046875000074, -6.387402343750011],\n                            [39.48095703124997, -6.45371093750002],\n                            [39.18232421875004, -6.172558593750026],\n                            [39.30898437499999, -5.721972656249974],\n                            [39.49648437499999, -6.174609375]\n                        ]\n                    ],\n                    [\n                        [\n                            [39.86503906250002, -4.906152343750037],\n                            [39.74931640625002, -5.443847656249986],\n                            [39.646777343750074, -5.368554687500009],\n                            [39.6734375, -4.927050781250031],\n                            [39.86503906250002, -4.906152343750037]\n                        ]\n                    ],\n                    [\n                        [\n                            [33.90322265625005, -1.002050781250034],\n                            [37.643847656250074, -3.045410156250028],\n                            [37.608203125000074, -3.497070312500028],\n                            [39.221777343750006, -4.692382812500014],\n                            [38.80468750000003, -6.070117187500031],\n                            [38.87402343750003, -6.33125],\n                            [39.5460937500001, -7.024023437500034],\n                            [39.288476562499994, -7.517871093750003],\n                            [39.28701171875005, -7.787695312500006],\n                            [39.4284179687501, -7.81279296874996],\n                            [39.441015625, -8.011523437499946],\n                            [39.304003906250074, -8.44384765625],\n                            [39.451269531250006, -8.94296875],\n                            [39.64130859375004, -9.19248046875002],\n                            [39.72519531250006, -10.000488281249972],\n                            [40.46357421875004, -10.464355468749972],\n                            [39.98867187499999, -10.820800781250014],\n                            [39.81708984375004, -10.912402343750031],\n                            [38.9875, -11.167285156250003],\n                            [38.49179687500006, -11.413281250000026],\n                            [37.92021484375002, -11.294726562500031],\n                            [37.72480468750004, -11.58066406250002],\n                            [37.54169921875004, -11.675097656249974],\n                            [37.37285156250002, -11.710449218749986],\n                            [36.97890625000005, -11.566992187499977],\n                            [36.30566406250003, -11.706347656249946],\n                            [36.191308593749994, -11.670703124999974],\n                            [36.17548828125004, -11.60927734374998],\n                            [36.08222656250004, -11.537304687499969],\n                            [35.91132812500004, -11.45468750000002],\n                            [35.785449218750074, -11.452929687500017],\n                            [35.63095703125006, -11.582031250000028],\n                            [35.564355468749994, -11.602343749999989],\n                            [35.418261718750074, -11.583203125],\n                            [35.18261718750003, -11.574804687499977],\n                            [34.95947265625003, -11.578125],\n                            [34.93701171874997, -11.463476562500034],\n                            [34.890625, -11.3935546875],\n                            [34.77382812500005, -11.341699218750009],\n                            [34.60791015624997, -11.08046875],\n                            [34.66708984375006, -10.792480468750028],\n                            [34.56992187500006, -10.241113281249966],\n                            [34.32089843750006, -9.731542968749977],\n                            [33.99560546875003, -9.495410156250003],\n                            [33.88886718750004, -9.670117187499983],\n                            [32.91992187500003, -9.407421875000026],\n                            [32.75664062500002, -9.322265625],\n                            [31.94257812500004, -9.05400390624996],\n                            [31.91865234375004, -8.942187500000017],\n                            [31.886132812499994, -8.921972656249977],\n                            [31.81806640625004, -8.902246093749952],\n                            [31.673632812500017, -8.908789062499963],\n                            [31.55625, -8.80546875],\n                            [31.44921874999997, -8.65390625],\n                            [31.35058593750003, -8.607031250000034],\n                            [31.07636718750004, -8.611914062499963],\n                            [30.968359375000063, -8.550976562499983],\n                            [30.89199218750005, -8.473730468749963],\n                            [30.830664062500063, -8.385546875000031],\n                            [30.720898437500097, -8.104394531250037],\n                            [30.40673828125003, -7.460644531249983],\n                            [30.313183593750097, -7.203710937499949],\n                            [30.212695312500017, -7.037890625000017],\n                            [30.10625, -6.915039062500028],\n                            [29.961816406249994, -6.803125],\n                            [29.798144531250017, -6.691894531249957],\n                            [29.70966796875004, -6.61689453125004],\n                            [29.590625, -6.394433593750023],\n                            [29.540820312500017, -6.313867187500037],\n                            [29.50625, -6.172070312500011],\n                            [29.480078125, -6.025],\n                            [29.490820312500063, -5.96542968750002],\n                            [29.59638671875004, -5.775976562499963],\n                            [29.60703125, -5.722656250000028],\n                            [29.59414062500005, -5.650781250000037],\n                            [29.542382812499994, -5.499804687500017],\n                            [29.34277343749997, -4.983105468749997],\n                            [29.32343750000004, -4.898828124999966],\n                            [29.32568359374997, -4.835644531249969],\n                            [29.404199218749994, -4.49667968750002],\n                            [29.40322265625005, -4.449316406249963],\n                            [29.71777343750003, -4.45585937499996],\n                            [29.94726562499997, -4.307324218749983],\n                            [30.4, -3.65390625],\n                            [30.790234375000097, -3.274609375000011],\n                            [30.811132812500006, -3.116406250000011],\n                            [30.78027343750003, -2.984863281249957],\n                            [30.70947265624997, -2.977246093749997],\n                            [30.604296875000074, -2.935253906249969],\n                            [30.515039062499994, -2.917578125],\n                            [30.45556640625003, -2.893164062500006],\n                            [30.433496093749994, -2.874511718750028],\n                            [30.424023437500097, -2.82402343749996],\n                            [30.473339843750097, -2.6943359375],\n                            [30.42421875000005, -2.641601562500014],\n                            [30.441992187500006, -2.613476562499969],\n                            [30.53369140624997, -2.426269531250014],\n                            [30.55361328125005, -2.400097656250011],\n                            [30.593359375000063, -2.39677734374996],\n                            [30.65664062500005, -2.373828124999989],\n                            [30.71484375000003, -2.363476562500011],\n                            [30.7625, -2.371679687499991],\n                            [30.828710937500006, -2.338476562499977],\n                            [30.85498046874997, -2.265429687500017],\n                            [30.8765625, -2.143359375000017],\n                            [30.864648437499994, -2.044042968749949],\n                            [30.819140625000017, -1.967480468749983],\n                            [30.812597656250006, -1.56308593750002],\n                            [30.76220703124997, -1.458691406249983],\n                            [30.710742187500074, -1.396777343749974],\n                            [30.631933593750006, -1.36748046874996],\n                            [30.508105468750074, -1.208203125000026],\n                            [30.47021484374997, -1.13115234374996],\n                            [30.47705078124997, -1.0830078125],\n                            [30.509960937500097, -1.067285156249994],\n                            [30.51992187499999, -1.0625],\n                            [30.67275390625005, -1.051367187499949],\n                            [30.741992187500017, -1.007519531249997],\n                            [30.809179687500063, -0.994921875],\n                            [30.82363281250005, -0.999023437499943],\n                            [30.84472656250003, -1.002050781250034],\n                            [32.371875, -1.002050781250034],\n                            [33.90322265625005, -1.002050781250034]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Tanzania\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [30.50996093750001, -1.067285156250009],\n                        [30.46992187500001, -1.066015625],\n                        [30.41230468750001, -1.063085937500006],\n                        [30.360253906250023, -1.074609375],\n                        [29.930078125000023, -1.469921875000011],\n                        [29.82539062500001, -1.335546875],\n                        [29.576953125000017, -1.387890625000011],\n                        [29.717675781250023, 0.098339843749997],\n                        [29.934472656250023, 0.4990234375],\n                        [29.94287109375, 0.819238281249994],\n                        [31.252734375000017, 2.044580078124994],\n                        [31.176367187500006, 2.270068359374989],\n                        [30.728613281250006, 2.455371093749989],\n                        [30.8466796875, 2.847021484374991],\n                        [30.754003906250006, 3.041796874999989],\n                        [30.90644531250001, 3.408935546875],\n                        [30.83857421875001, 3.49072265625],\n                        [31.15234375, 3.785595703124997],\n                        [31.547167968750017, 3.677587890624991],\n                        [31.79804687500001, 3.802636718749994],\n                        [32.13593750000001, 3.519726562499997],\n                        [32.33574218750002, 3.706201171874994],\n                        [32.99726562500001, 3.880175781249989],\n                        [33.489355468750006, 3.755078125],\n                        [33.568457031250006, 3.81171875],\n                        [33.74160156250002, 3.985253906249994],\n                        [33.97607421875, 4.22021484375],\n                        [34.13203125000001, 3.88916015625],\n                        [34.18574218750001, 3.869775390624994],\n                        [34.1650390625, 3.81298828125],\n                        [34.26708984375, 3.733154296875],\n                        [34.39287109375002, 3.691503906249991],\n                        [34.43769531250001, 3.650585937499997],\n                        [34.44179687500002, 3.60625],\n                        [34.3994140625, 3.412695312499991],\n                        [34.4072265625, 3.357519531249991],\n                        [34.447851562500006, 3.163476562499994],\n                        [34.90576171875, 2.4796875],\n                        [34.88300781250001, 2.417919921874997],\n                        [34.96406250000001, 2.062402343749994],\n                        [34.9775390625, 1.861914062499991],\n                        [34.97646484375002, 1.719628906249994],\n                        [34.79863281250002, 1.24453125],\n                        [34.48173828125002, 1.042138671874994],\n                        [34.41083984375001, 0.867285156249991],\n                        [34.16093750000002, 0.605175781249997],\n                        [33.94316406250002, 0.173779296874997],\n                        [33.90322265625002, -1.002050781250006],\n                        [32.371875, -1.002050781250006],\n                        [30.8447265625, -1.002050781250006],\n                        [30.823632812500023, -0.9990234375],\n                        [30.809179687500006, -0.994921875],\n                        [30.741992187500017, -1.007519531250011],\n                        [30.672753906250023, -1.051367187500006],\n                        [30.598730468750006, -1.069726562500009],\n                        [30.519921875000023, -1.0625],\n                        [30.50996093750001, -1.067285156250009]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Uganda\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [32.01220703124997, 46.20390624999999],\n                            [32.15009765625004, 46.1546875],\n                            [31.56386718750005, 46.25776367187504],\n                            [31.50878906250003, 46.373144531250006],\n                            [32.01220703124997, 46.20390624999999]\n                        ]\n                    ],\n                    [\n                        [\n                            [38.21435546875003, 47.091455078124966],\n                            [37.54335937499999, 47.07456054687498],\n                            [36.794824218749994, 46.71440429687499],\n                            [36.55878906250004, 46.76269531250006],\n                            [35.82714843749997, 46.62431640625002],\n                            [35.01455078125005, 46.10600585937502],\n                            [35.280175781249994, 46.27949218750001],\n                            [35.23037109375005, 46.440625],\n                            [34.84960937500003, 46.189892578124955],\n                            [35.02285156250005, 45.70097656250002],\n                            [35.45751953124997, 45.316308593749994],\n                            [36.170507812500006, 45.453076171874955],\n                            [36.575, 45.3935546875],\n                            [36.39335937500002, 45.06538085937501],\n                            [35.87011718750003, 45.005322265624955],\n                            [35.472558593749994, 45.098486328125006],\n                            [35.08769531250002, 44.802636718749966],\n                            [34.46992187500004, 44.7216796875],\n                            [33.909960937500074, 44.387597656249966],\n                            [33.45068359374997, 44.553662109374955],\n                            [33.55517578125003, 45.09765625000003],\n                            [32.5080078125001, 45.40380859375006],\n                            [33.664843750000074, 45.94707031249996],\n                            [33.59414062500005, 46.09624023437499],\n                            [33.42988281250004, 46.05761718750003],\n                            [33.20224609375006, 46.17573242187501],\n                            [32.47675781250004, 46.08369140625001],\n                            [31.83125, 46.28168945312501],\n                            [32.00849609375004, 46.42998046875002],\n                            [31.554882812500097, 46.554296875000034],\n                            [32.36132812499997, 46.474951171875034],\n                            [32.578027343749994, 46.615625],\n                            [32.04433593750005, 46.642480468749966],\n                            [31.75917968750005, 47.21284179687501],\n                            [31.872851562500017, 46.649755859375034],\n                            [31.532128906249994, 46.66474609374998],\n                            [31.56337890625005, 46.77729492187501],\n                            [31.402929687500063, 46.62880859375002],\n                            [30.796289062499994, 46.55200195312503],\n                            [30.219042968750074, 45.866748046875045],\n                            [29.62841796875003, 45.722460937500045],\n                            [29.705859375000074, 45.25991210937505],\n                            [29.567675781250074, 45.37080078124998],\n                            [29.40371093750005, 45.419677734375],\n                            [29.22353515625005, 45.402929687500034],\n                            [28.894335937500017, 45.28994140625002],\n                            [28.78173828125, 45.30986328125002],\n                            [28.76660156250003, 45.28623046874998],\n                            [28.78828125000001, 45.240966796875],\n                            [28.451269531250006, 45.292187499999955],\n                            [28.317675781250045, 45.347119140624955],\n                            [28.2125, 45.45043945312506],\n                            [28.26484375000004, 45.48388671875003],\n                            [28.310351562500074, 45.49858398437499],\n                            [28.499023437500057, 45.517724609374994],\n                            [28.513769531250034, 45.57241210937502],\n                            [28.49160156250005, 45.66577148437503],\n                            [28.562304687500074, 45.73579101562501],\n                            [28.667578125, 45.79384765625002],\n                            [28.729296875000074, 45.852001953124955],\n                            [28.73876953125003, 45.937158203124994],\n                            [28.84951171875005, 45.97866210937502],\n                            [28.94775390624997, 46.049951171874966],\n                            [28.971875, 46.12763671874998],\n                            [29.00625, 46.17646484374998],\n                            [28.94375, 46.28842773437506],\n                            [28.930566406250023, 46.36225585937501],\n                            [28.92744140625001, 46.42412109374999],\n                            [28.958398437500023, 46.45849609374997],\n                            [29.146289062500017, 46.52690429687496],\n                            [29.186230468750068, 46.52397460937499],\n                            [29.20078125, 46.504980468750034],\n                            [29.20458984374997, 46.37934570312501],\n                            [29.223828125000097, 46.37695312499997],\n                            [29.458789062500017, 46.453759765624994],\n                            [29.83789062499997, 46.35053710937501],\n                            [29.878027343750063, 46.360205078125034],\n                            [30.07568359375003, 46.377832031249966],\n                            [30.131054687500097, 46.42309570312506],\n                            [29.92431640624997, 46.53886718750002],\n                            [29.934765625000097, 46.625],\n                            [29.942480468750063, 46.72377929687502],\n                            [29.918066406250063, 46.78242187499998],\n                            [29.877832031249994, 46.828906250000045],\n                            [29.57197265625004, 46.96401367187502],\n                            [29.455664062500006, 47.292626953124994],\n                            [29.134863281250006, 47.48969726562501],\n                            [29.125390625000023, 47.96455078125001],\n                            [28.42304687500001, 48.146875],\n                            [28.34052734375001, 48.144433593749994],\n                            [27.54921875000008, 48.47773437500004],\n                            [27.22851562500003, 48.37143554687506],\n                            [26.90058593750001, 48.37192382812506],\n                            [26.847070312500023, 48.387158203124955],\n                            [26.640429687500045, 48.29414062500001],\n                            [26.618945312500017, 48.25986328125006],\n                            [26.4423828125, 48.22998046875],\n                            [26.162695312500063, 47.992529296875034],\n                            [25.90869140625, 47.96757812500002],\n                            [25.689257812500045, 47.93247070312506],\n                            [25.46425781250005, 47.910791015624994],\n                            [24.979101562500063, 47.72412109374997],\n                            [24.578906250000074, 47.93105468750005],\n                            [23.628710937500017, 47.995849609375],\n                            [23.40820312500003, 47.98999023437506],\n                            [23.20263671875, 48.084521484375045],\n                            [23.13945312499999, 48.08740234375],\n                            [22.87666015625001, 47.94726562500006],\n                            [22.769140625000063, 48.109619140625],\n                            [22.582421875000023, 48.134033203125],\n                            [22.253710937500017, 48.407373046874994],\n                            [22.131835937500057, 48.40532226562502],\n                            [22.142871093750017, 48.568505859374966],\n                            [22.295214843750045, 48.68583984374999],\n                            [22.389453125000045, 48.87348632812501],\n                            [22.52412109375004, 49.03139648437502],\n                            [22.538671875, 49.07270507812501],\n                            [22.847070312500023, 49.08125],\n                            [22.705664062500006, 49.17119140624999],\n                            [22.6494140625, 49.53901367187498],\n                            [22.706152343750006, 49.60620117187497],\n                            [23.03632812500004, 49.899072265624966],\n                            [23.711718750000045, 50.377343749999966],\n                            [23.97265625, 50.410058593749966],\n                            [24.089941406250006, 50.53046874999998],\n                            [24.0947265625, 50.617041015625034],\n                            [23.9970703125, 50.809375],\n                            [24.095800781250063, 50.87275390625001],\n                            [23.664453125000023, 51.31005859375],\n                            [23.61376953125, 51.525390625],\n                            [23.706835937500045, 51.64130859374998],\n                            [23.79169921875001, 51.63710937500002],\n                            [23.864257812500057, 51.62397460937501],\n                            [23.951171875, 51.58505859374998],\n                            [23.978320312500017, 51.59130859375003],\n                            [24.12685546875008, 51.664648437500034],\n                            [24.280078125000017, 51.77470703124999],\n                            [24.361914062500006, 51.86752929687498],\n                            [25.785742187500006, 51.923828125],\n                            [26.77343750000003, 51.77070312499998],\n                            [26.952832031249983, 51.754003906250034],\n                            [27.074121093750023, 51.760839843750006],\n                            [27.14199218750008, 51.75205078124998],\n                            [27.29628906250008, 51.59741210937503],\n                            [27.689746093750017, 51.572412109374994],\n                            [27.7, 51.47797851562501],\n                            [27.85859375000004, 51.59238281250006],\n                            [28.532031250000017, 51.56245117187501],\n                            [28.59902343750008, 51.54262695312505],\n                            [28.647753906250074, 51.45654296875],\n                            [28.690234375000017, 51.43886718750005],\n                            [28.73125, 51.43339843749999],\n                            [28.84951171875005, 51.540185546874994],\n                            [28.927539062500045, 51.56215820312502],\n                            [28.97773437500004, 51.57177734375003],\n                            [29.01308593750005, 51.59892578124996],\n                            [29.06074218750001, 51.625439453124955],\n                            [29.102050781250057, 51.627539062500034],\n                            [29.346484375000017, 51.38256835937503],\n                            [30.160742187500006, 51.477880859375006],\n                            [30.449511718750017, 51.274316406249994],\n                            [30.63251953125004, 51.35541992187501],\n                            [30.61171875000005, 51.406347656250006],\n                            [30.602343750000017, 51.47124023437499],\n                            [30.56074218750004, 51.531494140625],\n                            [30.533007812500017, 51.596337890624966],\n                            [30.583886718749994, 51.68896484375003],\n                            [30.667285156250017, 51.81411132812502],\n                            [30.755273437499994, 51.89516601562502],\n                            [30.84570312500003, 51.95307617187501],\n                            [30.980664062500097, 52.04619140624996],\n                            [31.217968750000097, 52.05024414062498],\n                            [31.345996093750074, 52.10537109375002],\n                            [31.57373046875003, 52.108105468749955],\n                            [31.763378906250097, 52.10107421875003],\n                            [32.12226562500004, 52.05058593749996],\n                            [32.435449218749994, 52.307226562500034],\n                            [33.735253906249994, 52.344775390625045],\n                            [34.397851562499994, 51.780419921874994],\n                            [34.12109375000003, 51.67915039062498],\n                            [34.21386718750003, 51.25537109375006],\n                            [35.0640625, 51.203417968750045],\n                            [35.31191406250005, 51.043896484374955],\n                            [35.59111328125002, 50.36875],\n                            [36.1164062500001, 50.408544921875006],\n                            [36.619433593750074, 50.209228515625],\n                            [37.42285156249997, 50.411474609375006],\n                            [38.046875, 49.92001953125006],\n                            [38.258593750000074, 50.05234375],\n                            [38.91835937499999, 49.82470703125],\n                            [39.17480468750003, 49.85595703124997],\n                            [39.780566406250074, 49.57202148437503],\n                            [40.080664062500006, 49.576855468749955],\n                            [40.10878906250005, 49.251562500000034],\n                            [39.68652343749997, 49.007910156250034],\n                            [40.00361328125004, 48.82207031250002],\n                            [39.792871093749994, 48.807714843750034],\n                            [39.6447265625001, 48.591210937499966],\n                            [39.8356445312501, 48.54277343749996],\n                            [39.95791015625005, 48.268896484375034],\n                            [39.77871093750005, 47.88754882812506],\n                            [38.90029296875005, 47.85512695312502],\n                            [38.36884765625004, 47.609960937500006],\n                            [38.21435546875003, 47.091455078124966]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Ukraine\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-57.81059570312499, -30.85859375000001],\n                        [-57.872509765625, -30.59101562500001],\n                        [-57.831201171874994, -30.495214843750006],\n                        [-57.71269531249999, -30.38447265625001],\n                        [-57.65087890625, -30.295019531250006],\n                        [-57.645751953125, -30.226953125],\n                        [-57.60888671875, -30.187792968750003],\n                        [-57.55229492187499, -30.26123046875],\n                        [-57.21445312499999, -30.28339843750001],\n                        [-57.186914062499994, -30.26484375000001],\n                        [-57.120507812499994, -30.14443359375001],\n                        [-56.83271484375, -30.107226562500003],\n                        [-56.4072265625, -30.44746093750001],\n                        [-55.998974609375, -30.837207031250003],\n                        [-56.018457031249994, -30.99189453125001],\n                        [-56.00468749999999, -31.079199218750006],\n                        [-55.873681640624994, -31.069628906250003],\n                        [-55.6271484375, -30.85810546875001],\n                        [-55.60302734375, -30.85078125000001],\n                        [-55.55732421875, -30.8759765625],\n                        [-55.17353515625, -31.279589843750003],\n                        [-55.09116210937499, -31.31396484375],\n                        [-55.036035156249994, -31.27900390625001],\n                        [-54.587646484375, -31.48515625],\n                        [-54.22055664062499, -31.85517578125001],\n                        [-53.76171875, -32.05683593750001],\n                        [-53.601708984374994, -32.40302734375001],\n                        [-53.12558593749999, -32.73671875],\n                        [-53.2140625, -32.82109375],\n                        [-53.31010742187499, -32.92705078125],\n                        [-53.39521484375, -33.010351562500006],\n                        [-53.482861328125, -33.06855468750001],\n                        [-53.511865234374994, -33.10869140625],\n                        [-53.53134765624999, -33.1708984375],\n                        [-53.53134765624999, -33.65546875000001],\n                        [-53.37060546875, -33.7421875],\n                        [-53.419580078124994, -33.77919921875001],\n                        [-53.47246093749999, -33.84931640625001],\n                        [-53.53452148437499, -34.01748046875001],\n                        [-53.742919921875, -34.24951171875],\n                        [-53.785302734374994, -34.38037109375],\n                        [-54.16855468749999, -34.670703125],\n                        [-54.902294921875, -34.93281250000001],\n                        [-55.67314453124999, -34.77568359375],\n                        [-56.249951171875, -34.90126953125001],\n                        [-57.17070312499999, -34.45234375000001],\n                        [-57.8291015625, -34.47734375],\n                        [-58.40019531249999, -33.91240234375],\n                        [-58.363525390625, -33.18232421875001],\n                        [-58.08232421874999, -32.893652343750006],\n                        [-58.12958984375, -32.75722656250001],\n                        [-58.16220703124999, -32.566503906250006],\n                        [-58.201171875, -32.4716796875],\n                        [-58.123046875, -32.321875],\n                        [-58.11972656249999, -32.24892578125001],\n                        [-58.164794921875, -32.18486328125],\n                        [-58.177001953125, -32.11904296875001],\n                        [-58.15634765624999, -32.0515625],\n                        [-58.160400390625, -31.98652343750001],\n                        [-58.18901367187499, -31.92421875],\n                        [-58.16748046875, -31.87265625],\n                        [-58.04233398437499, -31.76923828125001],\n                        [-58.006982421874994, -31.68496093750001],\n                        [-58.053857421874994, -31.494921875],\n                        [-58.0333984375, -31.416601562500006],\n                        [-57.89335937499999, -31.1953125],\n                        [-57.868408203125, -31.10439453125001],\n                        [-57.88632812499999, -30.93740234375001],\n                        [-57.81059570312499, -30.85859375000001]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Uruguay\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-155.58134765624996, 19.012011718750017],\n                            [-155.88129882812495, 19.07050781250001],\n                            [-156.04868164062498, 19.749951171874983],\n                            [-155.82031249999997, 20.01416015624997],\n                            [-155.83164062499998, 20.27583007812501],\n                            [-155.198779296875, 19.99438476562503],\n                            [-154.80419921875, 19.524462890625045],\n                            [-155.58134765624996, 19.012011718750017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-156.84960937499997, 20.772656249999955],\n                            [-156.97338867187497, 20.757519531249983],\n                            [-157.0505859375, 20.912451171875034],\n                            [-156.88056640624995, 20.904833984375074],\n                            [-156.84960937499997, 20.772656249999955]\n                        ]\n                    ],\n                    [\n                        [\n                            [-156.48681640624994, 20.93256835937504],\n                            [-156.27753906250004, 20.951269531250034],\n                            [-155.98984374999998, 20.75712890624999],\n                            [-156.40878906249998, 20.60517578125004],\n                            [-156.480078125, 20.80122070312501],\n                            [-156.69775390625003, 20.949072265625034],\n                            [-156.58540039062495, 21.034326171874994],\n                            [-156.48681640624994, 20.93256835937504]\n                        ]\n                    ],\n                    [\n                        [\n                            [-157.21362304687497, 21.215380859375017],\n                            [-156.71215820312506, 21.155078125000074],\n                            [-156.85986328125, 21.05634765625004],\n                            [-157.29033203124996, 21.112597656250017],\n                            [-157.21362304687497, 21.215380859375017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-157.79936523437502, 21.456640625000034],\n                            [-157.63540039062502, 21.30761718749997],\n                            [-158.11035156249994, 21.318603515625],\n                            [-158.27314453125, 21.585253906250045],\n                            [-157.9625, 21.701367187499983],\n                            [-157.79936523437502, 21.456640625000034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-159.37275390625, 21.93237304687497],\n                            [-159.60883789062495, 21.909521484375034],\n                            [-159.78916015625003, 22.041796875000074],\n                            [-159.57919921874998, 22.22314453124997],\n                            [-159.35205078124997, 22.219580078125034],\n                            [-159.37275390625, 21.93237304687497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-81.04418945312503, 24.716796875000057],\n                            [-81.137353515625, 24.710498046875017],\n                            [-80.93046875, 24.75947265625004],\n                            [-81.04418945312503, 24.716796875000057]\n                        ]\n                    ],\n                    [\n                        [\n                            [-80.3818359375, 25.142285156249955],\n                            [-80.58056640624997, 24.954248046875023],\n                            [-80.25708007812497, 25.34760742187504],\n                            [-80.3818359375, 25.142285156249955]\n                        ]\n                    ],\n                    [\n                        [\n                            [-97.17070312499996, 26.159375],\n                            [-97.40209960937494, 26.820507812499983],\n                            [-97.38598632812494, 27.19648437500004],\n                            [-97.17070312499996, 26.159375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-80.18676757812497, 27.278417968750034],\n                            [-80.17050781250003, 27.20478515625004],\n                            [-80.43691406249994, 27.850537109374955],\n                            [-80.18676757812497, 27.278417968750034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-91.793701171875, 29.50073242187497],\n                            [-92.00664062499996, 29.61030273437501],\n                            [-91.875244140625, 29.640966796875034],\n                            [-91.793701171875, 29.50073242187497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-84.90791015624998, 29.642626953125017],\n                            [-85.11674804687499, 29.63281249999997],\n                            [-84.737158203125, 29.732421875],\n                            [-84.90791015624998, 29.642626953125017]\n                        ]\n                    ],\n                    [\n                        [\n                            [-89.22397460937498, 30.084082031249977],\n                            [-89.34199218749995, 30.062841796875006],\n                            [-89.18466796874995, 30.168652343749983],\n                            [-89.22397460937498, 30.084082031249977]\n                        ]\n                    ],\n                    [\n                        [\n                            [-118.34794921875002, 33.3857421875],\n                            [-118.29746093750003, 33.312109375],\n                            [-118.44628906249997, 33.317089843749955],\n                            [-118.56943359375002, 33.46416015624999],\n                            [-118.34794921875002, 33.3857421875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-120.04355468749995, 33.918847656249994],\n                            [-120.25190429687494, 34.01386718749998],\n                            [-120.07182617187493, 34.026513671874966],\n                            [-120.04355468749995, 33.918847656249994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-119.88237304687497, 34.07968749999998],\n                            [-119.54926757812497, 34.02817382812506],\n                            [-119.80957031249997, 33.9677734375],\n                            [-119.88237304687497, 34.07968749999998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-75.54414062499995, 35.240087890625034],\n                            [-75.69008789062502, 35.221582031249994],\n                            [-75.53637695312497, 35.27861328124999],\n                            [-75.50351562500003, 35.769140625],\n                            [-75.46474609374994, 35.448632812499966],\n                            [-75.54414062499995, 35.240087890625034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-74.13320312500002, 39.680761718750034],\n                            [-74.25048828125, 39.529394531250006],\n                            [-74.10673828124996, 39.74643554687498],\n                            [-74.13320312500002, 39.680761718750034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-72.50976562500003, 40.98603515625001],\n                            [-72.58085937499996, 40.92133789062498],\n                            [-71.90322265625, 41.06069335937505],\n                            [-73.19428710937495, 40.654199218749994],\n                            [-74.01489257812497, 40.581201171874966],\n                            [-73.87924804687498, 40.79165039062502],\n                            [-73.573828125, 40.91962890624998],\n                            [-72.62509765624998, 40.99184570312505],\n                            [-72.27412109374998, 41.15302734375001],\n                            [-72.50976562500003, 40.98603515625001]\n                        ]\n                    ],\n                    [\n                        [\n                            [-69.9779296875, 41.26557617187504],\n                            [-70.23305664062502, 41.28632812500001],\n                            [-70.04121093750001, 41.3974609375],\n                            [-69.9779296875, 41.26557617187504]\n                        ]\n                    ],\n                    [\n                        [\n                            [-70.50991210937502, 41.376318359375034],\n                            [-70.82919921874995, 41.35898437500006],\n                            [-70.61601562499996, 41.45722656250001],\n                            [-70.50991210937502, 41.376318359375034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-71.24140625000001, 41.49194335937497],\n                            [-71.34624023437496, 41.469384765624994],\n                            [-71.23203124999995, 41.654296875],\n                            [-71.24140625000001, 41.49194335937497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-68.18725585937497, 44.33247070312501],\n                            [-68.41171875000003, 44.294335937499966],\n                            [-68.29941406249998, 44.456494140624955],\n                            [-68.18725585937497, 44.33247070312501]\n                        ]\n                    ],\n                    [\n                        [\n                            [-122.394140625, 47.39526367187503],\n                            [-122.50991210937497, 47.358007812500006],\n                            [-122.486474609375, 47.48876953125],\n                            [-122.394140625, 47.39526367187503]\n                        ]\n                    ],\n                    [\n                        [\n                            [-122.57275390624999, 48.15664062499999],\n                            [-122.38315429687499, 47.923193359375034],\n                            [-122.74150390624999, 48.22529296875004],\n                            [-122.62861328125, 48.38422851562498],\n                            [-122.54243164062503, 48.29399414062499],\n                            [-122.69702148437499, 48.228662109374994],\n                            [-122.57275390624999, 48.15664062499999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-94.80346679687497, 49.0029296875],\n                            [-94.71279296874997, 48.863427734374994],\n                            [-94.62089843749999, 48.74262695312501],\n                            [-93.85161132812496, 48.607275390625034],\n                            [-93.70771484374995, 48.52543945312499],\n                            [-93.37788085937498, 48.61655273437498],\n                            [-93.25795898437497, 48.62885742187501],\n                            [-92.83671875, 48.567773437499994],\n                            [-92.50058593749995, 48.43535156250002],\n                            [-92.41459960937493, 48.276611328125],\n                            [-92.3484375, 48.276611328125],\n                            [-92.00517578125002, 48.301855468750006],\n                            [-91.38720703124997, 48.05854492187498],\n                            [-91.04345703125003, 48.19370117187498],\n                            [-90.84033203125003, 48.20053710937506],\n                            [-90.79731445312495, 48.13105468750001],\n                            [-89.4556640625, 47.996240234374994],\n                            [-88.37817382812497, 48.30307617187498],\n                            [-87.74389648437497, 48.06054687500003],\n                            [-87.20800781249997, 47.848486328125006],\n                            [-86.67216796874996, 47.636425781249955],\n                            [-85.65224609375, 47.21997070312503],\n                            [-85.07006835937497, 46.97993164062498],\n                            [-84.87597656249994, 46.89990234375003],\n                            [-84.66577148437503, 46.54326171875002],\n                            [-84.44047851562496, 46.49814453125006],\n                            [-84.12319335937497, 46.50292968749997],\n                            [-83.97778320312503, 46.08491210937498],\n                            [-83.61596679687503, 46.116845703124994],\n                            [-83.46948242187503, 45.99467773437499],\n                            [-83.59267578125, 45.81713867187506],\n                            [-82.91933593749994, 45.51796875000002],\n                            [-82.55107421874996, 45.34736328125001],\n                            [-82.48505859374993, 45.08374023437503],\n                            [-82.137841796875, 43.570898437500034],\n                            [-82.19038085937495, 43.47407226562501],\n                            [-82.54531249999997, 42.62470703124998],\n                            [-83.10952148437497, 42.25068359375001],\n                            [-83.141943359375, 41.97587890624996],\n                            [-82.69003906249995, 41.675195312499994],\n                            [-82.43906249999998, 41.67485351562502],\n                            [-81.97416992187496, 41.88872070312499],\n                            [-81.50732421874997, 42.10346679687504],\n                            [-81.02822265624997, 42.247167968750006],\n                            [-80.24755859375, 42.36601562499996],\n                            [-79.17373046875, 42.74853515625],\n                            [-78.91508789062496, 42.90913085937504],\n                            [-78.98076171874993, 42.98061523437502],\n                            [-79.02617187499996, 43.01733398437506],\n                            [-79.066064453125, 43.10610351562502],\n                            [-79.171875, 43.466552734375],\n                            [-79.00249023437502, 43.52714843749999],\n                            [-78.845556640625, 43.58334960937498],\n                            [-78.72041015625001, 43.62495117187501],\n                            [-78.45825195312497, 43.63149414062502],\n                            [-77.596533203125, 43.62861328124998],\n                            [-76.819970703125, 43.62880859375002],\n                            [-76.18579101562503, 44.24223632812502],\n                            [-75.81933593749997, 44.468017578125],\n                            [-75.40126953124997, 44.77226562499999],\n                            [-74.99614257812496, 44.970117187499966],\n                            [-74.76245117187494, 44.99907226562502],\n                            [-74.663232421875, 45.00390625000003],\n                            [-71.51752929687495, 45.00756835937497],\n                            [-71.327294921875, 45.29008789062496],\n                            [-70.86503906249999, 45.27070312500001],\n                            [-70.296240234375, 45.90610351562506],\n                            [-70.00771484375002, 46.70893554687501],\n                            [-69.24287109374998, 47.46298828124998],\n                            [-69.0501953125, 47.426611328125034],\n                            [-68.93720703124998, 47.21123046875002],\n                            [-68.23549804687502, 47.34594726562503],\n                            [-67.806787109375, 47.08281249999999],\n                            [-67.80224609374994, 45.7275390625],\n                            [-67.43266601562496, 45.603125],\n                            [-67.366943359375, 45.17377929687498],\n                            [-67.12485351562498, 45.16943359375],\n                            [-66.98701171874995, 44.82768554687502],\n                            [-67.191259765625, 44.67558593750002],\n                            [-67.83906249999998, 44.576269531250034],\n                            [-68.056640625, 44.38432617187502],\n                            [-68.15205078124998, 44.50200195312499],\n                            [-68.45058593749997, 44.50761718749999],\n                            [-68.53251953124996, 44.25864257812498],\n                            [-68.81191406249994, 44.33935546875],\n                            [-68.76269531249994, 44.57075195312498],\n                            [-69.22607421875003, 43.98647460937505],\n                            [-69.52075195312503, 43.89736328125002],\n                            [-69.55668945312496, 43.982763671875006],\n                            [-69.62392578125, 43.88061523437497],\n                            [-69.65288085937493, 43.99389648437506],\n                            [-69.808349609375, 43.772314453125034],\n                            [-69.965234375, 43.855078125],\n                            [-70.17880859374998, 43.76635742187506],\n                            [-70.73310546875001, 43.07001953125004],\n                            [-70.82905273437493, 42.82534179687502],\n                            [-70.61293945312497, 42.623242187499955],\n                            [-71.04619140624993, 42.331103515625045],\n                            [-70.73828125, 42.228857421875006],\n                            [-70.42666015625002, 41.75727539062501],\n                            [-70.00141601562498, 41.82617187500003],\n                            [-70.24106445312495, 42.09121093750002],\n                            [-70.10893554687496, 42.07832031249998],\n                            [-69.97788085937498, 41.961279296875006],\n                            [-69.94863281249997, 41.67714843750005],\n                            [-70.65712890625, 41.53422851562496],\n                            [-70.70112304687498, 41.71484375],\n                            [-71.1685546875, 41.489404296874994],\n                            [-71.14873046874996, 41.74570312499998],\n                            [-71.27109375, 41.68125],\n                            [-71.39013671875003, 41.79531250000005],\n                            [-71.52285156249997, 41.378955078125045],\n                            [-72.92470703125002, 41.28515625000003],\n                            [-73.98710937499999, 40.751367187499994],\n                            [-73.87197265625, 41.05517578124997],\n                            [-73.96992187499995, 41.24970703125001],\n                            [-73.92719726562495, 40.914257812499955],\n                            [-74.26420898437496, 40.52861328124999],\n                            [-73.972265625, 40.40034179687498],\n                            [-74.079931640625, 39.78813476562496],\n                            [-74.06459960937497, 39.99311523437498],\n                            [-74.79448242187499, 39.00190429687501],\n                            [-74.95429687499995, 38.949951171875],\n                            [-74.89702148437502, 39.14545898437504],\n                            [-75.52421874999999, 39.49018554687501],\n                            [-75.421875, 39.78969726562502],\n                            [-75.07416992187495, 39.98349609375006],\n                            [-75.40063476562503, 39.83159179687502],\n                            [-75.58759765625001, 39.64077148437505],\n                            [-75.3921875, 39.09277343750006],\n                            [-75.08867187499999, 38.777539062499955],\n                            [-75.18710937499995, 38.59111328124999],\n                            [-75.03876953124993, 38.426367187500006],\n                            [-75.934375, 37.15190429687496],\n                            [-75.97504882812498, 37.3984375],\n                            [-75.65927734374995, 37.953955078125034],\n                            [-75.850830078125, 37.971582031249994],\n                            [-75.85869140624999, 38.36206054687503],\n                            [-76.05122070312495, 38.27954101562503],\n                            [-76.2646484375, 38.436425781249994],\n                            [-76.26416015625, 38.599951171875006],\n                            [-76.016943359375, 38.62509765624998],\n                            [-76.21298828124998, 38.75830078125003],\n                            [-76.34116210937498, 38.70966796874998],\n                            [-76.16816406249998, 38.85273437499998],\n                            [-76.32958984375, 38.95278320312505],\n                            [-76.13520507812493, 39.082128906250006],\n                            [-76.23569335937498, 39.19160156250001],\n                            [-76.153125, 39.315039062500034],\n                            [-75.87597656249997, 39.3759765625],\n                            [-76.003125, 39.41083984375001],\n                            [-75.87294921874997, 39.510888671874966],\n                            [-75.95893554687498, 39.58505859374998],\n                            [-76.2763671875, 39.32275390625],\n                            [-76.330810546875, 39.40390625],\n                            [-76.42089843749997, 39.225],\n                            [-76.57041015624995, 39.26933593749996],\n                            [-76.42758789062498, 39.12602539062499],\n                            [-76.55854492187493, 39.065234375000045],\n                            [-76.39409179687502, 38.368994140625034],\n                            [-76.67734374999998, 38.611962890624966],\n                            [-76.66855468749998, 38.5375],\n                            [-76.34116210937498, 38.08701171875006],\n                            [-76.86811523437495, 38.39028320312502],\n                            [-76.88974609375, 38.292089843750006],\n                            [-77.00117187499995, 38.44526367187504],\n                            [-77.23251953125, 38.40771484375003],\n                            [-77.03037109374995, 38.88925781249998],\n                            [-77.26040039062502, 38.6],\n                            [-77.27324218749996, 38.35175781249998],\n                            [-77.04677734375002, 38.356689453125],\n                            [-76.26425781250003, 37.89355468749997],\n                            [-76.34414062499997, 37.675683593749994],\n                            [-76.49248046874999, 37.682226562500006],\n                            [-77.11108398437497, 38.165673828124994],\n                            [-76.54946289062494, 37.66914062500001],\n                            [-76.30556640625, 37.57148437500001],\n                            [-76.26347656249996, 37.35703125],\n                            [-76.40097656249998, 37.386132812499994],\n                            [-76.45390624999993, 37.27353515625006],\n                            [-76.75771484375002, 37.50541992187496],\n                            [-76.28330078125, 37.05268554687501],\n                            [-76.40087890624997, 36.991308593750034],\n                            [-76.63090820312493, 37.22172851562499],\n                            [-77.25087890624994, 37.329199218750034],\n                            [-76.671875, 37.172949218750006],\n                            [-76.48784179687502, 36.89702148437499],\n                            [-75.99941406249997, 36.91264648437499],\n                            [-75.53417968749997, 35.81909179687506],\n                            [-75.94648437499995, 36.65908203125002],\n                            [-75.99277343749995, 36.47377929687502],\n                            [-75.82006835937494, 36.11284179687502],\n                            [-76.14785156250002, 36.279296875],\n                            [-76.15, 36.14575195312497],\n                            [-76.27060546874998, 36.18989257812501],\n                            [-76.22739257812498, 36.11601562499996],\n                            [-76.559375, 36.015332031249955],\n                            [-76.733642578125, 36.229150390624994],\n                            [-76.726220703125, 35.957617187500034],\n                            [-76.06977539062501, 35.970312500000034],\n                            [-76.08359374999998, 35.69052734375006],\n                            [-75.85390625, 35.96015625000001],\n                            [-75.75883789062499, 35.84326171875],\n                            [-75.77392578124997, 35.64697265624997],\n                            [-76.17382812499997, 35.354150390624994],\n                            [-76.489501953125, 35.397021484375045],\n                            [-76.57719726562502, 35.53232421874998],\n                            [-76.74140624999998, 35.431494140625034],\n                            [-77.03999023437495, 35.527392578125045],\n                            [-76.51293945312497, 35.270410156249994],\n                            [-76.77915039062503, 34.990332031250034],\n                            [-77.07026367187501, 35.154638671875034],\n                            [-76.97495117187503, 35.025195312500045],\n                            [-76.74497070312498, 34.94096679687502],\n                            [-76.45673828124998, 34.989355468750034],\n                            [-76.36220703125, 34.9365234375],\n                            [-76.43979492187498, 34.84291992187502],\n                            [-77.29624023437503, 34.602929687499994],\n                            [-77.41225585937497, 34.730810546875034],\n                            [-77.37978515625, 34.526611328125],\n                            [-77.750732421875, 34.28496093749996],\n                            [-77.92783203125, 33.93974609374999],\n                            [-77.95327148437494, 34.16899414062496],\n                            [-78.01333007812502, 33.91181640624998],\n                            [-78.40585937499995, 33.91757812499998],\n                            [-78.84145507812497, 33.72407226562501],\n                            [-79.19379882812498, 33.24414062500003],\n                            [-79.22646484375, 33.40488281249998],\n                            [-79.27602539062497, 33.135400390624966],\n                            [-79.80498046874999, 32.78740234374996],\n                            [-79.93310546874997, 32.81005859375006],\n                            [-79.94072265625002, 32.667138671874966],\n                            [-80.36284179687496, 32.500732421875],\n                            [-80.6341796875, 32.51171875000003],\n                            [-80.474267578125, 32.42275390625002],\n                            [-80.579345703125, 32.28730468750004],\n                            [-80.80253906249999, 32.44804687500002],\n                            [-80.69423828124997, 32.21572265625002],\n                            [-81.11328124999997, 31.87861328125001],\n                            [-81.06611328124995, 31.787988281250023],\n                            [-81.259375, 31.538916015624977],\n                            [-81.17543945312494, 31.531298828125017],\n                            [-81.38095703124998, 31.353271484375],\n                            [-81.28847656249997, 31.263916015625],\n                            [-81.441748046875, 31.19970703124997],\n                            [-81.5162109375, 30.801806640625017],\n                            [-81.24951171875003, 29.793798828125006],\n                            [-80.52412109374995, 28.48608398437503],\n                            [-80.5849609375, 28.271582031250034],\n                            [-80.456884765625, 27.90068359374996],\n                            [-80.61000976562494, 28.177587890624977],\n                            [-80.60693359375003, 28.522900390624983],\n                            [-80.693505859375, 28.34497070312497],\n                            [-80.68847656250003, 28.578515625000023],\n                            [-80.83818359374999, 28.757666015625034],\n                            [-80.74863281250003, 28.381005859375023],\n                            [-80.050048828125, 26.807714843750063],\n                            [-80.1263671875, 25.83349609375],\n                            [-80.48466796874999, 25.229833984375034],\n                            [-81.11049804687494, 25.138037109374977],\n                            [-81.13603515624999, 25.309667968750034],\n                            [-80.94042968750003, 25.264208984375017],\n                            [-81.11333007812499, 25.367236328125045],\n                            [-81.36494140625001, 25.83105468750003],\n                            [-81.715478515625, 25.98315429687503],\n                            [-81.95893554687495, 26.489941406249983],\n                            [-81.82866210937496, 26.68706054687499],\n                            [-82.03959960937496, 26.552050781250017],\n                            [-82.01328125, 26.96157226562505],\n                            [-82.24287109374998, 26.848876953125],\n                            [-82.44135742187501, 27.059667968750034],\n                            [-82.71459960937497, 27.499609375000063],\n                            [-82.40576171874994, 27.862890624999977],\n                            [-82.67519531249994, 27.963769531250023],\n                            [-82.61098632812502, 27.77724609375005],\n                            [-82.74287109374995, 27.709375],\n                            [-82.84350585937494, 27.845996093750017],\n                            [-82.65146484375, 28.8875],\n                            [-83.69438476562502, 29.92597656250001],\n                            [-84.04423828124996, 30.10380859374999],\n                            [-84.30966796874995, 30.064746093750045],\n                            [-84.38281250000003, 29.90737304687505],\n                            [-85.31894531249995, 29.680224609375045],\n                            [-85.413818359375, 29.76757812499997],\n                            [-85.413818359375, 29.842480468749955],\n                            [-85.31489257812493, 29.758105468750017],\n                            [-85.35361328125, 29.875732421875],\n                            [-85.67578125, 30.121923828125063],\n                            [-85.60351562500003, 30.286767578124966],\n                            [-85.75581054687495, 30.1669921875],\n                            [-86.454443359375, 30.39912109375004],\n                            [-86.12382812499999, 30.40581054687499],\n                            [-86.25737304687502, 30.493017578124977],\n                            [-87.201171875, 30.339257812499994],\n                            [-86.98579101562498, 30.43085937500001],\n                            [-86.99755859375, 30.5703125],\n                            [-87.17060546874998, 30.538769531249983],\n                            [-87.28105468750002, 30.339257812499994],\n                            [-87.47578124999998, 30.294287109375006],\n                            [-87.44829101562499, 30.394140625],\n                            [-87.62226562499998, 30.264746093750006],\n                            [-88.00595703124998, 30.230908203124955],\n                            [-87.79028320312503, 30.291796875000017],\n                            [-88.011328125, 30.694189453125006],\n                            [-88.13544921874998, 30.366601562499994],\n                            [-88.90522460937495, 30.415136718750006],\n                            [-89.32055664062503, 30.3453125],\n                            [-89.58847656249998, 30.165966796874955],\n                            [-90.12597656249997, 30.369091796874955],\n                            [-90.33198242187493, 30.277587890625057],\n                            [-90.41303710937501, 30.140332031249983],\n                            [-90.17534179687499, 30.02910156249996],\n                            [-89.73745117187497, 30.171972656250034],\n                            [-89.66503906249994, 30.117041015625034],\n                            [-89.81518554687497, 30.007275390624955],\n                            [-89.631689453125, 29.90380859375003],\n                            [-89.400732421875, 30.04604492187505],\n                            [-89.35444335937501, 29.82021484375005],\n                            [-89.72089843749995, 29.619287109374966],\n                            [-89.01572265625, 29.202880859375057],\n                            [-89.15551757812497, 29.01660156250003],\n                            [-89.23608398437494, 29.081103515625017],\n                            [-89.37612304687497, 28.981347656250023],\n                            [-89.44316406249996, 29.194140625000045],\n                            [-90.15908203124997, 29.537158203125017],\n                            [-90.05278320312499, 29.336816406249966],\n                            [-90.21279296875, 29.104931640624983],\n                            [-90.37919921874996, 29.29511718750001],\n                            [-90.75102539062496, 29.13085937500003],\n                            [-91.29013671875, 29.288964843749994],\n                            [-91.15078124999994, 29.317919921875045],\n                            [-91.24882812499993, 29.56420898437503],\n                            [-91.51420898437499, 29.55537109375001],\n                            [-91.8931640625, 29.836035156249977],\n                            [-92.135498046875, 29.699462890625057],\n                            [-92.08403320312499, 29.59282226562499],\n                            [-92.26083984374995, 29.55683593750004],\n                            [-93.17568359375, 29.778955078124994],\n                            [-93.82646484374999, 29.725146484375045],\n                            [-93.84145507812502, 29.97973632812503],\n                            [-93.89047851562495, 29.689355468750023],\n                            [-94.759619140625, 29.384277343750057],\n                            [-94.52626953125, 29.547949218750006],\n                            [-94.77827148437498, 29.54785156249997],\n                            [-94.74194335937497, 29.75],\n                            [-95.0228515625, 29.70234375000001],\n                            [-94.88828125000003, 29.37055664062501],\n                            [-95.27348632812499, 28.96386718750003],\n                            [-96.23452148437502, 28.488964843749983],\n                            [-96.01103515624996, 28.631933593749977],\n                            [-96.44873046874997, 28.594482421875],\n                            [-96.64003906249994, 28.708789062500017],\n                            [-96.42109374999993, 28.457324218750045],\n                            [-96.67636718749998, 28.34130859375003],\n                            [-96.77353515624998, 28.421630859375057],\n                            [-96.839501953125, 28.194384765625017],\n                            [-97.156494140625, 28.144335937500045],\n                            [-97.141259765625, 28.060742187499983],\n                            [-97.034326171875, 28.093847656250063],\n                            [-97.07309570312498, 27.98608398437503],\n                            [-97.43149414062498, 27.83720703124999],\n                            [-97.28872070312494, 27.670605468749983],\n                            [-97.43911132812502, 27.328271484374966],\n                            [-97.76845703124997, 27.45751953125],\n                            [-97.69238281250003, 27.287158203125017],\n                            [-97.48510742187497, 27.237402343750006],\n                            [-97.55468749999994, 26.96733398437496],\n                            [-97.43505859375, 26.48583984375003],\n                            [-97.14624023437494, 25.961474609375045],\n                            [-97.37563476562497, 25.871826171875],\n                            [-99.10776367187498, 26.446923828124994],\n                            [-99.45654296874999, 27.05668945312496],\n                            [-99.50532226562497, 27.54833984375003],\n                            [-100.29604492187495, 28.32768554687499],\n                            [-100.75458984375001, 29.182519531249994],\n                            [-101.44038085937503, 29.77685546875],\n                            [-102.26894531249998, 29.871191406250034],\n                            [-102.61494140624994, 29.75234375],\n                            [-102.8919921875, 29.216406250000034],\n                            [-103.16831054687498, 28.998193359374994],\n                            [-104.110595703125, 29.386132812499994],\n                            [-104.50400390624995, 29.677685546874955],\n                            [-104.97880859374996, 30.645947265624955],\n                            [-106.14804687499995, 31.450927734375],\n                            [-106.44541015624996, 31.768408203125006],\n                            [-108.21181640625002, 31.779345703125017],\n                            [-108.21445312499993, 31.329443359375034],\n                            [-111.0419921875, 31.32421875000003],\n                            [-114.83593749999994, 32.50830078125003],\n                            [-114.72475585937495, 32.71533203125003],\n                            [-117.12827148437495, 32.533349609374994],\n                            [-117.46743164062495, 33.295507812500006],\n                            [-118.08051757812497, 33.72216796874997],\n                            [-118.41044921874996, 33.74394531249996],\n                            [-118.506201171875, 34.01738281249999],\n                            [-119.14375, 34.11201171874998],\n                            [-119.60605468749999, 34.41801757812499],\n                            [-120.48120117187503, 34.47163085937498],\n                            [-120.64467773437502, 34.57998046875002],\n                            [-120.65908203124994, 35.122412109375034],\n                            [-120.85737304687501, 35.209667968749955],\n                            [-120.899609375, 35.42509765624999],\n                            [-121.28383789062494, 35.67631835937499],\n                            [-121.87739257812498, 36.33105468749997],\n                            [-121.80742187499995, 36.851220703124994],\n                            [-122.394921875, 37.20751953125003],\n                            [-122.49921875000001, 37.542626953124994],\n                            [-122.44560546875002, 37.797998046874966],\n                            [-122.07050781249998, 37.47827148437503],\n                            [-122.38544921875001, 37.960595703124966],\n                            [-122.31425781249999, 38.00732421874997],\n                            [-121.52534179687503, 38.05590820312503],\n                            [-122.39335937499995, 38.14482421875002],\n                            [-122.52133789062499, 37.82641601562497],\n                            [-122.93198242187498, 38.05546875000002],\n                            [-122.998779296875, 37.98862304687498],\n                            [-122.90815429687501, 38.19658203124999],\n                            [-123.701123046875, 38.90727539062502],\n                            [-123.83291015624994, 39.775488281250034],\n                            [-124.35654296875003, 40.37109374999997],\n                            [-124.07192382812497, 41.45952148437502],\n                            [-124.53964843750003, 42.812890624999966],\n                            [-124.14873046874997, 43.691748046875034],\n                            [-123.92934570312495, 45.57695312499996],\n                            [-123.989306640625, 46.21938476562502],\n                            [-123.22060546874998, 46.153613281250045],\n                            [-123.46484375, 46.27109374999998],\n                            [-124.07275390624996, 46.279443359374994],\n                            [-124.04433593750002, 46.605078125],\n                            [-123.946142578125, 46.43256835937501],\n                            [-123.88916015625003, 46.660009765625006],\n                            [-124.11254882812497, 46.862695312499994],\n                            [-123.84287109375002, 46.963183593750045],\n                            [-124.11171875, 47.03520507812496],\n                            [-124.1392578125, 46.95468749999998],\n                            [-124.376025390625, 47.658642578124955],\n                            [-124.66308593749996, 47.97412109375003],\n                            [-124.7099609375, 48.38037109375],\n                            [-123.97578125, 48.16845703125],\n                            [-122.97387695312499, 48.07329101562496],\n                            [-122.77861328125, 48.13759765625002],\n                            [-122.65664062500002, 47.88115234374999],\n                            [-122.77841796874996, 47.738427734374966],\n                            [-122.82138671875, 47.79316406250001],\n                            [-123.1390625, 47.386083984375034],\n                            [-122.92216796874993, 47.40766601562498],\n                            [-123.066796875, 47.39965820312506],\n                            [-123.04863281249995, 47.479345703125034],\n                            [-122.53281250000002, 47.919726562500045],\n                            [-122.67548828124995, 47.612353515625045],\n                            [-122.57788085937496, 47.29316406250001],\n                            [-122.76777343750001, 47.21835937500006],\n                            [-122.82846679687503, 47.336572265624994],\n                            [-123.02758789062501, 47.13891601562503],\n                            [-122.70195312500002, 47.11088867187502],\n                            [-122.35380859374996, 47.37158203125],\n                            [-122.40180664062497, 47.78427734374998],\n                            [-122.24199218750002, 48.01074218750003],\n                            [-122.5169921875, 48.15966796874997],\n                            [-122.40854492187502, 48.29389648437498],\n                            [-122.66899414062496, 48.465234374999966],\n                            [-122.49677734374995, 48.50556640625001],\n                            [-122.51274414062502, 48.66943359375],\n                            [-122.56201171875001, 48.777978515624994],\n                            [-122.68593749999995, 48.794287109375034],\n                            [-122.72246093750002, 48.85302734375003],\n                            [-122.78876953125003, 48.993017578125034],\n                            [-121.40722656249994, 48.993017578125034],\n                            [-119.70170898437495, 48.993017578125034],\n                            [-119.27534179687494, 48.99306640625005],\n                            [-118.84892578124993, 48.99306640625005],\n                            [-117.99619140625002, 48.99306640625005],\n                            [-116.71704101562501, 48.99306640625005],\n                            [-110.74765625, 48.99306640625005],\n                            [-104.77832031249997, 48.993115234374955],\n                            [-98.80898437499995, 48.99316406249997],\n                            [-97.52983398437493, 48.99316406249997],\n                            [-96.67705078124993, 48.99316406249997],\n                            [-96.25068359374993, 48.99316406249997],\n                            [-95.39790039062493, 48.99316406249997],\n                            [-95.16206054687493, 48.991748046875045],\n                            [-95.15527343749997, 49.36967773437502],\n                            [-94.85434570312495, 49.304589843749994],\n                            [-94.86040039062493, 49.258593750000045],\n                            [-94.80346679687497, 49.0029296875]\n                        ]\n                    ],\n                    [\n                        [\n                            [-176.28671874999998, 51.79199218750006],\n                            [-176.34965820312502, 51.733300781249994],\n                            [-176.41372070312502, 51.840576171875],\n                            [-176.28671874999998, 51.79199218750006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-177.87905273437502, 51.64970703125002],\n                            [-178.05888671875, 51.67260742187497],\n                            [-177.98637695312493, 51.76425781249998],\n                            [-178.16826171874996, 51.90302734375001],\n                            [-177.644482421875, 51.826269531250006],\n                            [-177.87905273437502, 51.64970703125002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-177.14819335937497, 51.71674804687498],\n                            [-177.67021484375002, 51.701074218749994],\n                            [-177.11005859375, 51.92875976562502],\n                            [-177.14819335937497, 51.71674804687498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-176.593310546875, 51.86669921875],\n                            [-176.45234374999995, 51.735693359375034],\n                            [-176.96162109374998, 51.60366210937505],\n                            [-176.69833984374998, 51.986035156249955],\n                            [-176.593310546875, 51.86669921875]\n                        ]\n                    ],\n                    [\n                        [\n                            [179.72773437500015, 51.905419921874966],\n                            [179.50390625000003, 51.97958984374998],\n                            [179.6271484375001, 52.03041992187502],\n                            [179.72773437500015, 51.905419921874966]\n                        ]\n                    ],\n                    [\n                        [\n                            [177.4154296875, 51.88281249999997],\n                            [177.25029296875013, 51.902929687500006],\n                            [177.6696289062501, 52.10302734375],\n                            [177.4154296875, 51.88281249999997]\n                        ]\n                    ],\n                    [\n                        [\n                            [-173.5533203125, 52.13627929687502],\n                            [-173.02290039062504, 52.07915039062502],\n                            [-173.83579101562498, 52.048193359375006],\n                            [-173.99248046874993, 52.12333984374996],\n                            [-173.5533203125, 52.13627929687502]\n                        ]\n                    ],\n                    [\n                        [\n                            [-172.464794921875, 52.27226562500002],\n                            [-172.61982421874998, 52.27285156250005],\n                            [-172.47041015625, 52.38803710937506],\n                            [-172.31362304687497, 52.32958984375006],\n                            [-172.464794921875, 52.27226562500002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-174.67739257812502, 52.035009765625006],\n                            [-175.29555664062502, 52.022167968749955],\n                            [-174.30615234375, 52.216162109375034],\n                            [-174.43554687499997, 52.317236328125034],\n                            [-174.168896484375, 52.42016601562503],\n                            [-174.04560546875, 52.36723632812499],\n                            [-174.12065429687493, 52.13520507812498],\n                            [-174.67739257812502, 52.035009765625006]\n                        ]\n                    ],\n                    [\n                        [\n                            [173.72275390625018, 52.35957031250004],\n                            [173.40234375000009, 52.40478515625],\n                            [173.77607421875004, 52.49511718750003],\n                            [173.72275390625018, 52.35957031250004]\n                        ]\n                    ],\n                    [\n                        [\n                            [172.81181640625002, 53.01298828125002],\n                            [173.43603515625003, 52.85205078125],\n                            [172.93515625000012, 52.752099609374966],\n                            [172.49482421875004, 52.93789062499999],\n                            [172.81181640625002, 53.01298828125002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-167.96435546875003, 53.345117187499994],\n                            [-169.088916015625, 52.83203125],\n                            [-168.68984375000002, 53.227246093749955],\n                            [-168.38041992187496, 53.28344726562506],\n                            [-168.28769531249998, 53.500146484374966],\n                            [-167.82807617187495, 53.50795898437505],\n                            [-167.96435546875003, 53.345117187499994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-166.61533203124998, 53.90092773437499],\n                            [-166.37231445312494, 53.99897460937498],\n                            [-166.230859375, 53.93261718750006],\n                            [-166.54560546875, 53.726464843749966],\n                            [-166.354541015625, 53.67353515625004],\n                            [-166.85097656249997, 53.45288085937503],\n                            [-167.78085937500003, 53.30024414062501],\n                            [-167.13608398437503, 53.526464843750006],\n                            [-167.01572265625003, 53.69838867187502],\n                            [-166.80898437500002, 53.64614257812505],\n                            [-166.741259765625, 53.71293945312496],\n                            [-167.10561523437497, 53.813378906249994],\n                            [-167.03808593749997, 53.9421875],\n                            [-166.67329101562498, 54.00595703124998],\n                            [-166.61533203124998, 53.90092773437499]\n                        ]\n                    ],\n                    [\n                        [\n                            [-165.841552734375, 54.070654296875006],\n                            [-166.05664062500003, 54.054345703124994],\n                            [-166.08774414062498, 54.16914062500001],\n                            [-165.89287109375, 54.20698242187498],\n                            [-165.69287109375, 54.09990234375002],\n                            [-165.841552734375, 54.070654296875006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-165.56113281249998, 54.13671874999997],\n                            [-165.55063476562498, 54.28452148437498],\n                            [-165.40786132812502, 54.19682617187496],\n                            [-165.56113281249998, 54.13671874999997]\n                        ]\n                    ],\n                    [\n                        [\n                            [-162.29814453124993, 54.847021484375006],\n                            [-162.43388671875, 54.931542968749994],\n                            [-162.26459960937504, 54.983496093750006],\n                            [-162.29814453124993, 54.847021484375006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-163.476025390625, 54.98071289062497],\n                            [-163.37895507812496, 54.81552734374998],\n                            [-163.083251953125, 54.66899414062496],\n                            [-163.35810546874995, 54.73569335937506],\n                            [-164.82343749999998, 54.41909179687505],\n                            [-164.887646484375, 54.60781250000002],\n                            [-164.47861328124998, 54.906835937500006],\n                            [-163.80712890624997, 55.04907226562503],\n                            [-163.476025390625, 54.98071289062497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-159.51513671875, 55.15185546875003],\n                            [-159.617724609375, 55.05732421875004],\n                            [-159.54506835937497, 55.22597656250002],\n                            [-159.51513671875, 55.15185546875003]\n                        ]\n                    ],\n                    [\n                        [\n                            [-131.33974609375002, 55.079833984375],\n                            [-131.32954101562498, 54.887744140625045],\n                            [-131.592236328125, 55.02568359374999],\n                            [-131.5654296875, 55.26411132812498],\n                            [-131.33974609375002, 55.079833984375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-159.87299804687495, 55.128759765625034],\n                            [-160.22705078124997, 54.92270507812506],\n                            [-160.17207031249995, 55.123046875],\n                            [-159.88735351562497, 55.27299804687502],\n                            [-159.87299804687495, 55.128759765625034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-132.86225585937504, 54.894433593749966],\n                            [-132.61723632812493, 54.892431640625006],\n                            [-132.70581054687497, 54.684179687500034],\n                            [-133.42905273437498, 55.30380859374998],\n                            [-133.097412109375, 55.213720703125006],\n                            [-132.86225585937504, 54.894433593749966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-160.329296875, 55.337695312500045],\n                            [-160.34331054687493, 55.25878906250006],\n                            [-160.51748046875, 55.33383789062506],\n                            [-160.329296875, 55.337695312500045]\n                        ]\n                    ],\n                    [\n                        [\n                            [-160.68491210937498, 55.314794921875006],\n                            [-160.552783203125, 55.38076171875002],\n                            [-160.48754882812503, 55.18486328124999],\n                            [-160.79506835937497, 55.14521484375001],\n                            [-160.72392578124993, 55.404638671875006],\n                            [-160.68491210937498, 55.314794921875006]\n                        ]\n                    ],\n                    [\n                        [\n                            [-133.30507812500002, 55.54375],\n                            [-133.6501953125, 55.26928710937506],\n                            [-133.73710937500002, 55.49692382812498],\n                            [-133.30507812500002, 55.54375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-155.56601562500003, 55.82119140625005],\n                            [-155.73735351562493, 55.82978515625001],\n                            [-155.59394531250004, 55.92431640625],\n                            [-155.56601562500003, 55.82119140625005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-130.97915039062502, 55.489160156249994],\n                            [-131.187890625, 55.206298828125],\n                            [-131.44755859374996, 55.40878906250006],\n                            [-131.7625, 55.16582031250002],\n                            [-131.84609374999997, 55.41625976562497],\n                            [-131.62495117187504, 55.831689453124966],\n                            [-131.26923828125004, 55.95537109375002],\n                            [-130.997802734375, 55.727636718750006],\n                            [-130.97915039062502, 55.489160156249994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-133.56611328125, 56.33920898437498],\n                            [-133.202978515625, 56.31982421875003],\n                            [-133.096630859375, 56.09003906250001],\n                            [-132.59760742187504, 55.89501953125],\n                            [-132.17270507812498, 55.48061523437502],\n                            [-132.51127929687493, 55.59394531250001],\n                            [-132.63129882812495, 55.47319335937502],\n                            [-132.41787109375002, 55.48291015625006],\n                            [-132.20668945312497, 55.22441406249996],\n                            [-131.97641601562498, 55.208593750000034],\n                            [-132.06474609375002, 54.713134765625],\n                            [-133.11855468750002, 55.32763671875003],\n                            [-132.95888671875002, 55.39555664062502],\n                            [-133.0333984375, 55.589697265625034],\n                            [-133.68017578124994, 55.78515625],\n                            [-133.24150390624993, 55.920800781249994],\n                            [-133.371240234375, 56.035888671875],\n                            [-133.74252929687498, 55.96484375],\n                            [-133.530859375, 56.145654296874966],\n                            [-133.56611328125, 56.33920898437498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-132.77988281249998, 56.24726562499998],\n                            [-133.03500976562498, 56.34091796875006],\n                            [-132.90205078124998, 56.45375976562505],\n                            [-132.62910156249995, 56.411914062500045],\n                            [-132.77988281249998, 56.24726562499998]\n                        ]\n                    ],\n                    [\n                        [\n                            [-132.11235351562493, 56.109375],\n                            [-132.13295898437497, 55.94326171875005],\n                            [-132.28730468749995, 55.92939453124998],\n                            [-132.65991210937503, 56.07817382812499],\n                            [-132.379833984375, 56.49877929687497],\n                            [-132.06689453125, 56.24423828124998],\n                            [-132.11235351562493, 56.109375]\n                        ]\n                    ],\n                    [\n                        [\n                            [-154.208642578125, 56.51489257812497],\n                            [-154.32221679687504, 56.570605468750045],\n                            [-154.11040039062496, 56.602929687499966],\n                            [-154.208642578125, 56.51489257812497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-169.755224609375, 56.63505859375002],\n                            [-169.47431640624998, 56.59404296875002],\n                            [-169.6326171875, 56.545703125000045],\n                            [-169.755224609375, 56.63505859375002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-132.746875, 56.525683593750045],\n                            [-132.94804687500002, 56.56723632812498],\n                            [-132.842529296875, 56.79477539062506],\n                            [-132.56796875000003, 56.57583007812505],\n                            [-132.746875, 56.525683593750045]\n                        ]\n                    ],\n                    [\n                        [\n                            [-133.98959960937503, 56.84497070312497],\n                            [-133.73837890625, 56.65043945312496],\n                            [-133.94970703125, 56.12773437499996],\n                            [-134.18959960937502, 56.07695312500002],\n                            [-134.084375, 56.456347656250045],\n                            [-134.37368164062502, 56.838671875000045],\n                            [-134.14326171874998, 56.93232421875001],\n                            [-133.98959960937503, 56.84497070312497]\n                        ]\n                    ],\n                    [\n                        [\n                            [-133.36621093750006, 57.003515625000034],\n                            [-132.99624023437497, 56.93041992187497],\n                            [-132.95917968749998, 56.67705078124996],\n                            [-133.03491210937494, 56.62075195312505],\n                            [-133.32895507812498, 56.83007812499997],\n                            [-133.158154296875, 56.495166015625045],\n                            [-133.4841796875, 56.45175781249998],\n                            [-133.979443359375, 57.009570312500045],\n                            [-133.36621093750006, 57.003515625000034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-153.007080078125, 57.12485351562498],\n                            [-153.37460937499998, 57.05190429687505],\n                            [-153.285205078125, 57.18505859375],\n                            [-152.90839843750004, 57.152441406250006],\n                            [-153.007080078125, 57.12485351562498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-134.96977539062496, 57.351416015625034],\n                            [-134.62070312499998, 56.71831054687502],\n                            [-134.68188476562503, 56.216162109375034],\n                            [-134.98056640625003, 56.518945312499994],\n                            [-134.88344726562497, 56.679052734375034],\n                            [-135.33061523437505, 56.821875],\n                            [-135.19960937499997, 57.02734375],\n                            [-135.45493164062503, 57.24941406250005],\n                            [-135.81230468750002, 57.00952148437503],\n                            [-135.82275390625, 57.280419921874966],\n                            [-135.448681640625, 57.534375],\n                            [-134.96977539062496, 57.351416015625034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-152.89804687499998, 57.82392578125004],\n                            [-152.42875976562493, 57.82568359375003],\n                            [-152.48261718749998, 57.70332031249998],\n                            [-152.21621093749997, 57.577001953125006],\n                            [-152.41220703125003, 57.454785156249955],\n                            [-152.94077148437498, 57.49809570312499],\n                            [-152.67905273437503, 57.345117187499994],\n                            [-153.274365234375, 57.22636718749996],\n                            [-153.732568359375, 57.052343750000034],\n                            [-153.643310546875, 56.960742187500045],\n                            [-154.02734375, 56.77797851562502],\n                            [-153.793212890625, 56.98950195312503],\n                            [-154.24375, 57.143017578124955],\n                            [-154.33896484374998, 56.9208984375],\n                            [-154.67319335937498, 57.44609375],\n                            [-154.11616210937498, 57.651220703125006],\n                            [-153.6876953125, 57.30512695312504],\n                            [-153.841552734375, 57.86284179687496],\n                            [-153.48793945312497, 57.73095703125],\n                            [-153.21748046875004, 57.79575195312506],\n                            [-153.16044921875, 57.97197265624999],\n                            [-152.85039062499993, 57.896777343750045],\n                            [-152.89804687499998, 57.82392578125004]\n                        ]\n                    ],\n                    [\n                        [\n                            [-135.73037109375002, 58.244238281250034],\n                            [-135.61323242187507, 57.99184570312505],\n                            [-135.346630859375, 58.12412109374998],\n                            [-134.9546875, 58.01533203125004],\n                            [-134.97065429687495, 57.817236328125006],\n                            [-135.33847656250003, 57.768652343750034],\n                            [-134.97885742187503, 57.724365234375],\n                            [-134.93149414062498, 57.48115234375001],\n                            [-135.564208984375, 57.66640625],\n                            [-135.691943359375, 57.41992187500006],\n                            [-135.91079101562502, 57.44658203124999],\n                            [-136.568603515625, 57.97216796875003],\n                            [-136.32197265625, 58.21889648437502],\n                            [-136.14375, 58.098486328125006],\n                            [-136.09438476562502, 58.198144531249966],\n                            [-135.73037109375002, 58.244238281250034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-134.68027343749998, 58.16166992187499],\n                            [-134.24008789062498, 58.143994140624955],\n                            [-133.82275390624997, 57.62866210937503],\n                            [-134.29233398437498, 58.044726562500074],\n                            [-133.91113281250003, 57.3525390625],\n                            [-134.51601562499997, 57.042578125],\n                            [-134.48676757812495, 57.48203125],\n                            [-134.92348632812497, 58.354638671874966],\n                            [-134.68027343749998, 58.16166992187499]\n                        ]\n                    ],\n                    [\n                        [\n                            [-152.416943359375, 58.360205078125034],\n                            [-151.974365234375, 58.30986328124999],\n                            [-152.068896484375, 58.17792968750001],\n                            [-152.26835937499993, 58.25170898437506],\n                            [-152.30922851562502, 58.133886718750034],\n                            [-152.5982421875, 58.16259765625],\n                            [-152.92841796875004, 57.99370117187499],\n                            [-153.38134765625003, 58.08720703125002],\n                            [-152.976123046875, 58.29702148437505],\n                            [-152.771875, 58.278564453125],\n                            [-152.84111328125002, 58.41640625000002],\n                            [-152.416943359375, 58.360205078125034]\n                        ]\n                    ],\n                    [\n                        [\n                            [-152.486083984375, 58.485009765624966],\n                            [-152.63662109375002, 58.54169921874998],\n                            [-152.3955078125, 58.619384765625],\n                            [-152.486083984375, 58.485009765624966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-160.918994140625, 58.57709960937498],\n                            [-161.13149414062502, 58.668212890625],\n                            [-160.71513671875005, 58.79521484375002],\n                            [-160.918994140625, 58.57709960937498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-148.02177734375, 60.06533203125005],\n                            [-148.271875, 60.05327148437499],\n                            [-148.07958984375003, 60.151660156250045],\n                            [-148.02177734375, 60.06533203125005]\n                        ]\n                    ],\n                    [\n                        [\n                            [-147.735888671875, 59.81323242187503],\n                            [-147.76806640625, 59.94375],\n                            [-147.180859375, 60.358251953125034],\n                            [-147.01987304687498, 60.33222656249998],\n                            [-147.735888671875, 59.81323242187503]\n                        ]\n                    ],\n                    [\n                        [\n                            [-166.13544921875, 60.38354492187503],\n                            [-165.72968750000004, 60.31420898437503],\n                            [-165.591796875, 59.913134765625045],\n                            [-166.14873046874996, 59.764111328124955],\n                            [-167.13886718749998, 60.00854492187503],\n                            [-167.43642578125002, 60.20664062500006],\n                            [-166.836328125, 60.21699218750004],\n                            [-166.47568359374998, 60.382763671874955],\n                            [-166.13544921875, 60.38354492187503]\n                        ]\n                    ],\n                    [\n                        [\n                            [-146.3939453125, 60.44965820312501],\n                            [-146.10224609374998, 60.41118164062499],\n                            [-146.61831054687497, 60.27368164062503],\n                            [-146.70253906249997, 60.40854492187498],\n                            [-146.3939453125, 60.44965820312501]\n                        ]\n                    ],\n                    [\n                        [\n                            [-147.658251953125, 60.45048828124999],\n                            [-147.787841796875, 60.17792968749998],\n                            [-147.89145507812498, 60.299414062500034],\n                            [-147.658251953125, 60.45048828124999]\n                        ]\n                    ],\n                    [\n                        [\n                            [-172.74223632812496, 60.45737304687498],\n                            [-172.23208007812494, 60.299121093750074],\n                            [-172.63574218750003, 60.328857421875],\n                            [-173.04765625000002, 60.56831054687501],\n                            [-172.74223632812496, 60.45737304687498]\n                        ]\n                    ],\n                    [\n                        [\n                            [-171.46303710937494, 63.640039062499994],\n                            [-171.03486328125, 63.58549804687499],\n                            [-170.29936523437502, 63.68061523437501],\n                            [-169.55454101562498, 63.373486328124955],\n                            [-168.71601562500004, 63.310595703125045],\n                            [-168.76132812500003, 63.21376953125002],\n                            [-169.364697265625, 63.17114257812506],\n                            [-169.67636718750003, 62.95610351562502],\n                            [-169.81860351562494, 63.122363281250045],\n                            [-170.84838867187494, 63.44438476562502],\n                            [-171.63183593749997, 63.351220703124966],\n                            [-171.74638671874993, 63.703076171874955],\n                            [-171.46303710937494, 63.640039062499994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-141.00214843750004, 68.77416992187506],\n                            [-141.00214843750004, 67.89755859374998],\n                            [-141.00214843750004, 66.43652343750006],\n                            [-141.00214843750004, 65.55991210937498],\n                            [-141.00214843750004, 64.09887695312506],\n                            [-141.00214843750004, 63.22226562499998],\n                            [-141.00214843750004, 61.761279296875045],\n                            [-141.00214843750004, 60.884667968749994],\n                            [-141.00214843750004, 60.30024414062504],\n                            [-140.76274414062505, 60.259130859375006],\n                            [-140.525439453125, 60.218359375000034],\n                            [-140.45283203125004, 60.29970703125002],\n                            [-139.97329101562497, 60.183154296875074],\n                            [-139.67631835937505, 60.32832031249998],\n                            [-139.23476562499997, 60.339746093749994],\n                            [-139.07924804687497, 60.34370117187501],\n                            [-139.07924804687497, 60.279443359374966],\n                            [-139.136962890625, 60.17270507812498],\n                            [-139.18515624999998, 60.083593750000034],\n                            [-138.86875, 59.94575195312501],\n                            [-138.317626953125, 59.611132812500074],\n                            [-137.59331054687493, 59.22626953124998],\n                            [-137.52089843750002, 58.91538085937498],\n                            [-137.43857421875003, 58.903125],\n                            [-137.2775390625, 58.988183593749994],\n                            [-137.126220703125, 59.04096679687498],\n                            [-136.81328125000002, 59.150048828124994],\n                            [-136.57875976562502, 59.15224609375002],\n                            [-136.46635742187493, 59.459082031250006],\n                            [-136.27797851562502, 59.48032226562506],\n                            [-136.321826171875, 59.604833984375034],\n                            [-135.70258789062504, 59.72875976562506],\n                            [-135.36787109374998, 59.743310546874994],\n                            [-135.051025390625, 59.57866210937502],\n                            [-134.94375, 59.28828125000001],\n                            [-134.67724609374997, 59.19926757812499],\n                            [-134.39306640625, 59.009179687499994],\n                            [-134.32963867187505, 58.93969726562506],\n                            [-134.21850585937503, 58.849902343750045],\n                            [-133.54638671874997, 58.50346679687499],\n                            [-133.27529296875, 58.22285156250004],\n                            [-133.00141601562495, 57.948974609375],\n                            [-132.55048828125, 57.499902343749994],\n                            [-132.44248046874998, 57.40673828125003],\n                            [-132.30166015624997, 57.27631835937501],\n                            [-132.232177734375, 57.19853515624999],\n                            [-132.27939453124998, 57.14536132812498],\n                            [-132.33798828124998, 57.07944335937506],\n                            [-132.15703125, 57.048193359375006],\n                            [-132.03154296875, 57.02656250000004],\n                            [-132.062890625, 56.95336914062503],\n                            [-132.104296875, 56.856787109375006],\n                            [-131.86616210937495, 56.792822265625006],\n                            [-131.82426757812496, 56.589990234374994],\n                            [-131.471875, 56.55673828125006],\n                            [-130.649072265625, 56.26367187500003],\n                            [-130.47709960937496, 56.230566406250034],\n                            [-130.413134765625, 56.12250976562498],\n                            [-130.09785156249995, 56.10927734375002],\n                            [-130.01406249999997, 55.950537109375006],\n                            [-130.2140625, 55.02587890625003],\n                            [-130.57534179687497, 54.769677734374966],\n                            [-130.849609375, 54.80761718750006],\n                            [-131.04785156249997, 55.157666015624955],\n                            [-130.74819335937502, 55.31801757812502],\n                            [-131.127685546875, 55.96015625000001],\n                            [-131.032763671875, 56.08808593749998],\n                            [-131.78417968749997, 55.876562500000034],\n                            [-131.98339843749994, 55.535009765625006],\n                            [-132.15541992187502, 55.59956054687501],\n                            [-132.20751953124997, 55.75341796875],\n                            [-131.84384765625003, 56.16010742187498],\n                            [-131.55136718749998, 56.206787109375],\n                            [-131.88789062500004, 56.24165039062498],\n                            [-132.18203125000002, 56.42065429687506],\n                            [-132.82460937500002, 57.05581054687505],\n                            [-133.465869140625, 57.17216796875002],\n                            [-133.64873046874993, 57.64228515624998],\n                            [-133.11704101562498, 57.56621093750002],\n                            [-133.535205078125, 57.83295898437501],\n                            [-133.1943359375, 57.87768554687506],\n                            [-133.559375, 57.924462890624994],\n                            [-133.72231445312502, 57.84423828125],\n                            [-134.03110351562498, 58.072167968749966],\n                            [-133.87675781249996, 58.51816406249998],\n                            [-134.20883789062503, 58.232958984375045],\n                            [-134.77612304687506, 58.45385742187503],\n                            [-135.36367187500002, 59.41943359375],\n                            [-135.50234375000002, 59.202294921874994],\n                            [-135.090234375, 58.245849609375],\n                            [-135.57177734374994, 58.41206054687504],\n                            [-135.89755859374998, 58.40019531250002],\n                            [-136.04311523437497, 58.82163085937498],\n                            [-135.82636718750004, 58.89794921874997],\n                            [-136.0166015625, 58.87397460937498],\n                            [-136.150048828125, 59.04809570312503],\n                            [-136.22583007812497, 58.765478515625006],\n                            [-136.98901367187503, 59.03447265624999],\n                            [-137.05903320312498, 58.87373046875001],\n                            [-136.613916015625, 58.809277343749955],\n                            [-136.48374023437503, 58.61767578125],\n                            [-136.224609375, 58.602246093749955],\n                            [-136.06147460937495, 58.45273437500006],\n                            [-136.607421875, 58.24399414062498],\n                            [-137.54399414062502, 58.58120117187502],\n                            [-138.51489257812503, 59.16591796875005],\n                            [-139.77329101562498, 59.52729492187504],\n                            [-139.51303710937498, 59.698095703125006],\n                            [-139.5123046875, 59.95356445312501],\n                            [-139.28671874999998, 59.610937500000034],\n                            [-139.22080078125003, 59.819873046875045],\n                            [-138.9880859375, 59.83500976562502],\n                            [-139.43144531249996, 60.012255859375074],\n                            [-140.41982421874997, 59.71074218750002],\n                            [-141.40830078125, 59.90278320312498],\n                            [-141.408740234375, 60.11767578125006],\n                            [-141.67016601562497, 59.969873046874966],\n                            [-142.94565429687503, 60.09697265625002],\n                            [-144.14721679687494, 60.01640625000002],\n                            [-144.185498046875, 60.150732421875034],\n                            [-144.901318359375, 60.335156249999955],\n                            [-144.69111328125, 60.66909179687502],\n                            [-145.248291015625, 60.38012695312506],\n                            [-145.898876953125, 60.47817382812505],\n                            [-145.67490234374998, 60.65112304687503],\n                            [-146.57045898437497, 60.72915039062502],\n                            [-146.39199218749997, 60.810839843750045],\n                            [-146.63842773437497, 60.89731445312498],\n                            [-146.59912109374994, 61.05351562500002],\n                            [-146.284912109375, 61.11264648437498],\n                            [-147.89111328125, 60.889892578125],\n                            [-148.00512695312494, 60.96855468750002],\n                            [-147.75185546874997, 61.218945312499955],\n                            [-148.34189453125, 61.060400390625006],\n                            [-148.34443359374998, 60.853564453125045],\n                            [-148.55615234374994, 60.82700195312506],\n                            [-148.25673828124997, 60.67529296874997],\n                            [-148.64013671875, 60.48945312500004],\n                            [-148.11918945312502, 60.57514648437498],\n                            [-147.96411132812494, 60.48486328124997],\n                            [-148.430712890625, 59.98911132812498],\n                            [-149.2666015625, 59.99829101562497],\n                            [-149.395263671875, 60.10576171875002],\n                            [-149.59804687500002, 59.77045898437501],\n                            [-149.7138671875, 59.91958007812502],\n                            [-149.80126953124994, 59.737939453124966],\n                            [-150.00532226562507, 59.78442382812503],\n                            [-150.19804687499996, 59.56655273437505],\n                            [-150.60737304687504, 59.56337890625002],\n                            [-150.934521484375, 59.249121093750034],\n                            [-151.18276367187502, 59.30078124999997],\n                            [-151.73818359375002, 59.18852539062502],\n                            [-151.94951171875, 59.26508789062498],\n                            [-151.88461914062503, 59.386328125],\n                            [-151.39960937499995, 59.51630859375001],\n                            [-151.04648437499998, 59.771826171875034],\n                            [-151.45009765624997, 59.65039062499997],\n                            [-151.85322265625, 59.78208007812498],\n                            [-151.39599609375006, 60.27446289062502],\n                            [-151.35644531249997, 60.72294921874999],\n                            [-150.44125976562503, 61.02358398437505],\n                            [-149.07509765624997, 60.87641601562498],\n                            [-150.05327148437496, 61.17109374999998],\n                            [-149.433544921875, 61.50078125000002],\n                            [-149.97568359374998, 61.27934570312502],\n                            [-150.61225585937495, 61.301123046875006],\n                            [-151.59350585937494, 60.979638671874966],\n                            [-152.54091796874997, 60.265429687500045],\n                            [-153.025, 60.29565429687497],\n                            [-152.660107421875, 59.99721679687502],\n                            [-153.21123046875002, 59.84272460937498],\n                            [-153.09360351562503, 59.70913085937505],\n                            [-153.65253906250004, 59.64702148437499],\n                            [-154.17832031250003, 59.155566406250074],\n                            [-153.41826171875, 58.9599609375],\n                            [-153.43759765625003, 58.754833984374955],\n                            [-154.289013671875, 58.30434570312502],\n                            [-154.247021484375, 58.15942382812497],\n                            [-155.006884765625, 58.01606445312501],\n                            [-155.77797851562497, 57.56821289062498],\n                            [-156.43588867187498, 57.359960937500006],\n                            [-156.62900390624998, 57.00996093750001],\n                            [-158.41440429687498, 56.435839843750045],\n                            [-158.5521484375, 56.31269531249998],\n                            [-158.27563476562497, 56.19624023437498],\n                            [-158.5046875, 56.062109375],\n                            [-158.59116210937503, 56.18452148437498],\n                            [-158.78984375000002, 55.98691406250006],\n                            [-159.52324218749993, 55.81000976562498],\n                            [-159.65966796875003, 55.625927734374955],\n                            [-159.77138671874997, 55.84111328125002],\n                            [-160.49931640625002, 55.53730468750004],\n                            [-161.38193359374998, 55.371289062499955],\n                            [-161.44379882812495, 55.513281250000034],\n                            [-161.202099609375, 55.54355468750006],\n                            [-161.51694335937503, 55.61840820312503],\n                            [-162.073974609375, 55.13930664062505],\n                            [-162.38637695312497, 55.05234375],\n                            [-162.63037109375003, 55.24667968749998],\n                            [-162.67436523437505, 54.99658203125],\n                            [-162.86503906249996, 54.954541015624955],\n                            [-163.11962890624997, 55.06469726562503],\n                            [-163.131103515625, 54.916552734375045],\n                            [-163.33530273437503, 54.83916015624999],\n                            [-163.27880859374997, 55.12182617187503],\n                            [-162.906591796875, 55.19555664062503],\n                            [-161.69731445312502, 55.9072265625],\n                            [-161.215625, 56.02143554687498],\n                            [-160.8986328125, 55.99365234375],\n                            [-161.00537109375, 55.88715820312498],\n                            [-160.80283203125003, 55.754443359375045],\n                            [-160.70634765625002, 55.870458984375034],\n                            [-160.29169921875, 55.80507812500005],\n                            [-160.53906250000006, 56.00629882812501],\n                            [-160.30205078125, 56.31411132812502],\n                            [-158.91801757812502, 56.882177734375006],\n                            [-158.675146484375, 56.79487304687498],\n                            [-158.66079101562502, 57.03940429687498],\n                            [-158.32094726562497, 57.29790039062499],\n                            [-157.84575195312496, 57.52807617187497],\n                            [-157.4619140625, 57.506201171875034],\n                            [-157.697216796875, 57.679443359375],\n                            [-157.610888671875, 58.050830078125074],\n                            [-157.19370117187498, 58.19418945312506],\n                            [-157.48837890624998, 58.25371093750002],\n                            [-157.52363281249998, 58.421337890624955],\n                            [-156.97465820312496, 58.736328125],\n                            [-156.80888671875005, 59.13427734375],\n                            [-157.14204101562504, 58.87763671875001],\n                            [-158.19091796875003, 58.6142578125],\n                            [-158.50317382812494, 58.85034179687497],\n                            [-158.42563476562498, 58.99931640625002],\n                            [-158.080517578125, 58.97744140625002],\n                            [-158.422802734375, 59.08984375],\n                            [-158.67827148437502, 58.92939453124998],\n                            [-158.80947265625002, 58.973876953125],\n                            [-158.78862304687493, 58.440966796875045],\n                            [-158.95068359375, 58.404541015625],\n                            [-159.67026367187498, 58.9111328125],\n                            [-159.92021484375, 58.819873046875074],\n                            [-160.36313476562498, 59.05117187500002],\n                            [-161.246826171875, 58.799462890624994],\n                            [-161.36132812499994, 58.66953124999998],\n                            [-162.144921875, 58.64423828124998],\n                            [-161.724365234375, 58.794287109375006],\n                            [-161.64438476562498, 59.109667968750045],\n                            [-161.9810546875, 59.14614257812502],\n                            [-161.82871093749998, 59.588623046875],\n                            [-162.421337890625, 60.28398437500002],\n                            [-161.96201171875003, 60.695361328125045],\n                            [-162.68496093749997, 60.268945312499966],\n                            [-162.57075195312495, 59.98974609375],\n                            [-163.68037109374998, 59.80151367187503],\n                            [-164.14282226562497, 59.89677734374999],\n                            [-165.02651367187497, 60.500634765624994],\n                            [-165.35380859375002, 60.54121093750001],\n                            [-164.80517578125, 60.89204101562498],\n                            [-164.31850585937497, 60.77128906249999],\n                            [-164.37236328125002, 60.59184570312502],\n                            [-163.999560546875, 60.76606445312498],\n                            [-163.72998046874997, 60.589990234374994],\n                            [-163.420947265625, 60.757421875],\n                            [-163.90654296874996, 60.85380859375002],\n                            [-163.58691406249994, 60.902978515624994],\n                            [-163.74902343750003, 60.9697265625],\n                            [-163.99462890624997, 60.86469726562501],\n                            [-165.11484375, 60.93281250000004],\n                            [-164.86899414062503, 61.11176757812498],\n                            [-165.27978515624994, 61.169628906249955],\n                            [-165.27363281250004, 61.27485351562498],\n                            [-165.56586914062498, 61.10234375000002],\n                            [-165.86396484375004, 61.33569335937503],\n                            [-165.84531249999998, 61.536230468750034],\n                            [-166.152734375, 61.545947265625074],\n                            [-166.16811523437502, 61.65083007812501],\n                            [-165.80893554687503, 61.69609375000002],\n                            [-166.07880859375, 61.803125],\n                            [-165.61279296875003, 61.86928710937502],\n                            [-165.707275390625, 62.10043945312506],\n                            [-165.19453125, 62.47353515625002],\n                            [-164.75786132812493, 62.496728515624966],\n                            [-164.589453125, 62.709375],\n                            [-164.79267578125, 62.623193359374966],\n                            [-164.79965820312503, 62.918066406250006],\n                            [-164.384228515625, 63.03046874999998],\n                            [-164.40903320312503, 63.21503906250001],\n                            [-163.94287109375, 63.247216796874994],\n                            [-163.61630859374998, 63.125146484374994],\n                            [-163.73784179687496, 63.016406250000045],\n                            [-163.504345703125, 63.105859374999966],\n                            [-163.28784179687494, 63.046435546875045],\n                            [-162.621484375, 63.26582031249998],\n                            [-162.28281250000003, 63.529199218749994],\n                            [-161.97397460937498, 63.45292968749999],\n                            [-161.09970703125003, 63.557910156250045],\n                            [-160.778564453125, 63.818945312500034],\n                            [-160.987548828125, 64.25126953125002],\n                            [-161.49072265625003, 64.43378906249998],\n                            [-160.93193359374996, 64.5791015625],\n                            [-160.855908203125, 64.755615234375],\n                            [-161.13017578125005, 64.92543945312505],\n                            [-161.759375, 64.816259765625],\n                            [-162.80703124999997, 64.37421875000001],\n                            [-163.20390625, 64.65200195312502],\n                            [-163.14433593750002, 64.423828125],\n                            [-163.71308593749998, 64.588232421875],\n                            [-164.978759765625, 64.45366210937502],\n                            [-166.1427734375, 64.58276367187503],\n                            [-166.48139648437498, 64.72807617187507],\n                            [-166.415234375, 64.926513671875],\n                            [-166.92841796875, 65.15708007812498],\n                            [-166.15703125, 65.28583984375001],\n                            [-167.40400390625, 65.42211914062497],\n                            [-168.08837890624997, 65.65776367187502],\n                            [-166.39873046875002, 66.14443359375005],\n                            [-165.62993164062496, 66.131201171875],\n                            [-165.77617187500002, 66.31904296875001],\n                            [-164.46049804687502, 66.58842773437499],\n                            [-163.63823242187502, 66.57465820312504],\n                            [-163.89394531249997, 66.57587890625001],\n                            [-164.03374023437493, 66.21552734374995],\n                            [-163.69536132812502, 66.08383789062503],\n                            [-161.93369140625003, 66.04287109374997],\n                            [-161.45541992187503, 66.28139648437497],\n                            [-161.03427734375003, 66.18881835937503],\n                            [-161.12031249999995, 66.334326171875],\n                            [-161.91689453124997, 66.41181640624998],\n                            [-162.54365234375004, 66.80512695312501],\n                            [-162.36162109375, 66.94731445312502],\n                            [-161.591015625, 66.45952148437502],\n                            [-160.23168945312503, 66.420263671875],\n                            [-160.360888671875, 66.6125],\n                            [-160.864013671875, 66.67084960937501],\n                            [-161.39804687499998, 66.55185546875],\n                            [-161.85668945312497, 66.70034179687497],\n                            [-161.719921875, 67.02055664062502],\n                            [-163.5318359375, 67.10258789062502],\n                            [-164.1251953125, 67.60673828125007],\n                            [-166.786279296875, 68.35961914062497],\n                            [-166.38051757812502, 68.425146484375],\n                            [-166.20908203125, 68.88535156250003],\n                            [-165.04394531249994, 68.882470703125],\n                            [-163.867919921875, 69.03666992187505],\n                            [-161.88095703125003, 70.33173828125001],\n                            [-162.073876953125, 70.16196289062498],\n                            [-160.9962890625, 70.30458984375],\n                            [-160.11713867187495, 70.59121093750002],\n                            [-159.86567382812498, 70.27885742187499],\n                            [-159.81499023437496, 70.49707031250003],\n                            [-159.38676757812493, 70.52451171875003],\n                            [-160.081591796875, 70.63486328125003],\n                            [-159.680908203125, 70.786767578125],\n                            [-159.31450195312496, 70.87851562500003],\n                            [-159.251171875, 70.7484375],\n                            [-157.909375, 70.860107421875],\n                            [-156.47021484374994, 71.40766601562501],\n                            [-156.469970703125, 71.29155273437507],\n                            [-155.57944335937503, 71.12109374999997],\n                            [-156.14658203125, 70.92783203125003],\n                            [-155.97353515625002, 70.84199218749995],\n                            [-155.16684570312498, 71.09921875000006],\n                            [-154.19521484375002, 70.80112304687498],\n                            [-153.23291015625, 70.93256835937504],\n                            [-152.49121093749994, 70.88095703125],\n                            [-152.23291015625, 70.81035156249999],\n                            [-152.39921875, 70.62045898437503],\n                            [-151.76904296875, 70.56015625],\n                            [-151.94467773437498, 70.45209960937501],\n                            [-149.26943359374997, 70.50078124999999],\n                            [-147.70537109375, 70.21723632812495],\n                            [-145.82314453124997, 70.16005859375002],\n                            [-145.19736328125003, 70.00869140625002],\n                            [-143.218310546875, 70.11625976562499],\n                            [-142.70786132812498, 70.03378906249998],\n                            [-141.40791015625, 69.65336914062502],\n                            [-141.00214843750004, 69.65078125000002],\n                            [-141.00214843750004, 68.77416992187506]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"United States\", \"childNum\": 76 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [71.20615234375006, 39.892578125],\n                            [71.15625, 39.88344726562502],\n                            [71.06425781250002, 39.88491210937505],\n                            [71.01171874999997, 39.895117187500006],\n                            [71.04365234375004, 39.97631835937503],\n                            [71.04482421875005, 39.992529296875034],\n                            [70.96064453125004, 40.087988281250034],\n                            [71.00546875, 40.15229492187498],\n                            [71.0241210937501, 40.14916992187497],\n                            [71.08037109375007, 40.07988281249999],\n                            [71.2287109375001, 40.04814453124999],\n                            [71.20615234375006, 39.892578125]\n                        ]\n                    ],\n                    [\n                        [\n                            [70.94677734374997, 42.24868164062505],\n                            [70.97900390625003, 42.26655273437504],\n                            [71.03603515625, 42.28466796875],\n                            [71.12998046875006, 42.25],\n                            [71.21269531250002, 42.20644531250005],\n                            [71.23232421875005, 42.18627929687503],\n                            [71.22851562499997, 42.16289062499996],\n                            [70.18095703125007, 41.571435546874994],\n                            [70.734375, 41.400537109374994],\n                            [70.86044921875006, 41.22490234375002],\n                            [71.11074218750005, 41.152636718750045],\n                            [71.29882812500003, 41.152490234374994],\n                            [71.39306640625003, 41.123388671875034],\n                            [71.40839843750004, 41.13603515625002],\n                            [71.42089843750003, 41.341894531250034],\n                            [71.60625, 41.367431640625],\n                            [71.66494140625, 41.54121093749998],\n                            [71.70068359374997, 41.454003906249966],\n                            [71.75771484375005, 41.42802734375002],\n                            [71.79248046875003, 41.41313476562499],\n                            [71.85800781250006, 41.311376953125034],\n                            [71.8786132812501, 41.195019531249955],\n                            [71.95849609375003, 41.18706054687502],\n                            [72.05244140625004, 41.16474609375001],\n                            [72.1154296875001, 41.18657226562502],\n                            [72.1642578125001, 41.173730468749966],\n                            [72.18095703125002, 41.11845703124999],\n                            [72.18730468750002, 41.02592773437499],\n                            [72.2130859375001, 41.014257812500006],\n                            [72.36406250000002, 41.04345703125],\n                            [72.65830078125, 40.86992187499999],\n                            [73.13212890625002, 40.82851562499999],\n                            [72.6041015625, 40.52543945312499],\n                            [72.40205078125004, 40.578076171874955],\n                            [72.3892578125, 40.427392578124994],\n                            [72.13125, 40.438623046874966],\n                            [71.69248046875, 40.15234375],\n                            [71.30468749999997, 40.28691406249996],\n                            [70.990625, 40.2548828125],\n                            [70.95800781250003, 40.238867187500034],\n                            [70.653125, 40.201171875],\n                            [70.37158203125003, 40.38413085937506],\n                            [70.75107421875006, 40.721777343750006],\n                            [70.40195312500006, 41.03510742187498],\n                            [69.71289062500003, 40.65698242187503],\n                            [69.35722656250002, 40.76738281249996],\n                            [69.20625, 40.566552734374994],\n                            [69.27490234374997, 40.19809570312498],\n                            [68.63066406250007, 40.16708984374998],\n                            [68.9720703125, 40.08994140624998],\n                            [68.80468750000003, 40.05034179687499],\n                            [68.86875, 39.90747070312503],\n                            [68.63896484375007, 39.8388671875],\n                            [68.46328125, 39.53671874999998],\n                            [67.71904296875007, 39.62138671875002],\n                            [67.426171875, 39.46557617187497],\n                            [67.3576171875001, 39.216699218749994],\n                            [67.64833984375005, 39.13105468750004],\n                            [67.69443359375006, 38.99462890625003],\n                            [68.13251953125004, 38.927636718749966],\n                            [68.08720703125002, 38.47353515625002],\n                            [68.3502929687501, 38.211035156250006],\n                            [67.81435546875005, 37.48701171875004],\n                            [67.7980468750001, 37.244970703125006],\n                            [67.75898437500004, 37.172216796875034],\n                            [67.75292968749997, 37.199804687500034],\n                            [67.7, 37.227246093749955],\n                            [67.60742187499997, 37.22250976562506],\n                            [67.5172851562501, 37.26665039062499],\n                            [67.44169921875007, 37.25800781250001],\n                            [67.3197265625, 37.209570312500006],\n                            [67.1955078125001, 37.23520507812498],\n                            [67.06884765624997, 37.334814453125006],\n                            [66.82773437500006, 37.37128906249998],\n                            [66.52226562500007, 37.34848632812506],\n                            [66.51064453125, 37.45869140625004],\n                            [66.51132812500006, 37.59916992187496],\n                            [66.52558593750004, 37.785742187500034],\n                            [66.60625, 37.98671875000005],\n                            [65.97119140624997, 38.244238281250006],\n                            [65.612890625, 38.23857421875002],\n                            [64.3099609375, 38.97729492187497],\n                            [63.76367187500003, 39.16054687499999],\n                            [62.48320312500002, 39.97563476562496],\n                            [61.90283203124997, 41.09370117187501],\n                            [61.496972656249994, 41.276074218749955],\n                            [61.2423828125001, 41.18920898437503],\n                            [60.454980468749994, 41.221630859374955],\n                            [60.089648437500074, 41.39941406250003],\n                            [60.07558593750005, 41.759667968749966],\n                            [60.20078125000006, 41.803125],\n                            [59.94179687499999, 41.97353515625002],\n                            [59.98515625000002, 42.21171875],\n                            [59.35429687500002, 42.32329101562496],\n                            [58.5890625000001, 42.778466796874966],\n                            [58.477148437500006, 42.66284179687503],\n                            [58.15156250000004, 42.628076171874966],\n                            [58.474414062500074, 42.29936523437496],\n                            [58.02890625, 42.48764648437506],\n                            [57.814257812500074, 42.18984375000005],\n                            [57.290625, 42.123779296875],\n                            [56.96406250000004, 41.856542968750006],\n                            [57.11884765625004, 41.35029296874998],\n                            [57.01796875, 41.26347656249996],\n                            [55.97744140625005, 41.32221679687504],\n                            [55.97568359375006, 44.99492187499996],\n                            [58.555273437500006, 45.55537109375001],\n                            [61.007910156250006, 44.39379882812497],\n                            [61.99023437500003, 43.492138671874955],\n                            [63.20703125000003, 43.62797851562502],\n                            [64.44316406250007, 43.55117187499999],\n                            [64.9054687500001, 43.714697265625006],\n                            [65.49619140625, 43.310546875],\n                            [65.80302734375002, 42.87695312500006],\n                            [66.1002929687501, 42.99082031249998],\n                            [66.00957031250007, 42.00488281250003],\n                            [66.49863281250006, 41.99487304687503],\n                            [66.70966796875004, 41.17915039062501],\n                            [67.9357421875001, 41.19658203125002],\n                            [68.11308593750007, 41.02861328124999],\n                            [68.04765625000007, 40.80927734374998],\n                            [68.29189453125, 40.656103515625034],\n                            [68.57265625, 40.62265624999998],\n                            [68.58408203125, 40.876269531250045],\n                            [69.15361328125002, 41.42524414062498],\n                            [70.94677734374997, 42.24868164062505]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Uzbekistan\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-61.17451171875001, 13.158105468749966],\n                        [-61.268457031249966, 13.287695312499991],\n                        [-61.13896484374996, 13.358740234374991],\n                        [-61.17451171875001, 13.158105468749966]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"St. Vin. and Gren.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-60.99790039062498, 8.867333984374966],\n                            [-61.069189453125034, 8.947314453125003],\n                            [-60.91582031249996, 9.070312500000014],\n                            [-60.86142578124998, 8.949609375000037],\n                            [-60.99790039062498, 8.867333984374966]\n                        ]\n                    ],\n                    [\n                        [\n                            [-60.821191406249966, 9.138378906250026],\n                            [-60.94140625000003, 9.105566406250006],\n                            [-60.73583984374997, 9.203320312500026],\n                            [-60.821191406249966, 9.138378906250026]\n                        ]\n                    ],\n                    [\n                        [\n                            [-63.84936523437494, 11.131005859374994],\n                            [-63.917626953124994, 10.887548828125048],\n                            [-64.40234375, 10.981591796875023],\n                            [-64.21367187500002, 11.086132812499997],\n                            [-64.0283203125, 11.00185546874998],\n                            [-63.84936523437494, 11.131005859374994]\n                        ]\n                    ],\n                    [\n                        [\n                            [-60.742138671874926, 5.202050781250037],\n                            [-60.71196289062499, 5.191552734375023],\n                            [-60.671972656250034, 5.164355468749989],\n                            [-60.603857421875006, 4.94936523437498],\n                            [-61.00283203125002, 4.535253906249991],\n                            [-61.28007812500002, 4.516894531249974],\n                            [-61.82084960937496, 4.197021484375],\n                            [-62.153125, 4.098388671874986],\n                            [-62.41064453124994, 4.156738281249972],\n                            [-62.71210937499998, 4.01791992187502],\n                            [-62.85698242187502, 3.593457031249969],\n                            [-63.33867187500002, 3.943896484375045],\n                            [-64.02148437500003, 3.929101562500051],\n                            [-64.19248046874995, 4.126855468750009],\n                            [-64.57636718750001, 4.139892578125],\n                            [-64.788671875, 4.276025390625023],\n                            [-64.66899414062496, 4.01181640625002],\n                            [-64.22109375000002, 3.587402343749972],\n                            [-64.04658203124998, 2.502392578124997],\n                            [-63.389257812500006, 2.411914062500045],\n                            [-63.43251953124994, 2.155566406250045],\n                            [-64.00849609374995, 1.931591796874969],\n                            [-64.20502929687493, 1.52949218750004],\n                            [-65.10375976562497, 1.108105468749983],\n                            [-65.47338867187497, 0.691259765624977],\n                            [-65.55605468750002, 0.687988281250014],\n                            [-65.52299804687493, 0.843408203124966],\n                            [-65.68144531249999, 0.983447265624989],\n                            [-66.06005859375003, 0.78535156250004],\n                            [-66.34711914062498, 0.7671875],\n                            [-66.87602539062499, 1.223046875000037],\n                            [-67.21083984375, 2.390136718750043],\n                            [-67.61870117187496, 2.793603515624994],\n                            [-67.85908203124998, 2.793603515624994],\n                            [-67.3111328125, 3.41586914062502],\n                            [-67.66162109375, 3.864257812499986],\n                            [-67.85527343750002, 4.506884765624989],\n                            [-67.82490234374995, 5.270458984375026],\n                            [-67.47387695312503, 5.929980468750003],\n                            [-67.48198242187499, 6.18027343750002],\n                            [-67.85917968749999, 6.289892578124963],\n                            [-68.47177734375, 6.156542968749974],\n                            [-69.42714843749997, 6.123974609374997],\n                            [-70.12919921874999, 6.95361328125],\n                            [-70.73715820312503, 7.090039062499997],\n                            [-71.12861328124993, 6.98671875],\n                            [-72.00664062499993, 7.032617187500023],\n                            [-72.20771484374995, 7.37026367187498],\n                            [-72.47197265624996, 7.524267578124991],\n                            [-72.39033203124995, 8.287060546874969],\n                            [-72.66542968749994, 8.62758789062498],\n                            [-72.79638671874997, 9.10898437499999],\n                            [-73.05839843749999, 9.259570312500031],\n                            [-73.36621093749997, 9.194140625000017],\n                            [-73.00654296874998, 9.789160156250006],\n                            [-72.86933593750001, 10.49125976562496],\n                            [-72.690087890625, 10.835839843749994],\n                            [-72.24848632812501, 11.196435546875009],\n                            [-71.95810546875, 11.66640625],\n                            [-71.31972656249997, 11.861914062500048],\n                            [-71.95693359375002, 11.569921874999977],\n                            [-71.835107421875, 11.190332031250009],\n                            [-71.6416015625, 11.013525390625048],\n                            [-71.73090820312498, 10.994677734375017],\n                            [-71.59433593749995, 10.657373046875051],\n                            [-72.11284179687499, 9.815576171874966],\n                            [-71.61953124999994, 9.047949218749991],\n                            [-71.24140625000001, 9.160449218750003],\n                            [-71.08583984375002, 9.348242187499977],\n                            [-71.05268554687501, 9.705810546874986],\n                            [-71.49423828125, 10.533203124999972],\n                            [-71.46953124999993, 10.964160156250017],\n                            [-70.23251953124998, 11.372998046874997],\n                            [-70.09711914062493, 11.519775390624972],\n                            [-69.80478515624998, 11.47421875000002],\n                            [-69.81733398437495, 11.672070312499997],\n                            [-70.19257812499993, 11.62460937500002],\n                            [-70.28652343749997, 11.886035156249989],\n                            [-70.20278320312497, 12.098388671874986],\n                            [-70.00395507812496, 12.177880859375023],\n                            [-69.63159179687494, 11.479931640625026],\n                            [-68.827978515625, 11.431738281249977],\n                            [-68.39863281249995, 11.160986328124977],\n                            [-68.29628906249997, 10.689355468749994],\n                            [-68.13994140624999, 10.492724609374989],\n                            [-66.24721679687497, 10.632226562499994],\n                            [-65.85175781249995, 10.257763671874997],\n                            [-65.12910156249998, 10.070068359375043],\n                            [-64.85048828125, 10.098095703124969],\n                            [-64.188330078125, 10.457812499999989],\n                            [-63.73188476562501, 10.503417968750043],\n                            [-64.24750976562498, 10.54257812500002],\n                            [-64.298193359375, 10.635156249999966],\n                            [-61.879492187500006, 10.741015625000031],\n                            [-62.379980468750006, 10.546875],\n                            [-62.91357421875, 10.531494140624986],\n                            [-62.68583984374996, 10.289794921875043],\n                            [-62.740576171875006, 10.056152343750043],\n                            [-62.55034179687499, 10.200439453125043],\n                            [-62.320410156250034, 9.783056640625006],\n                            [-62.22114257812498, 9.882568359375028],\n                            [-62.15336914062493, 9.821777343749986],\n                            [-62.15532226562499, 9.979248046875014],\n                            [-62.077099609374926, 9.97504882812504],\n                            [-61.73593749999998, 9.631201171874977],\n                            [-61.76591796874996, 9.813818359374963],\n                            [-61.58886718749994, 9.894531249999986],\n                            [-60.79248046874997, 9.360742187500037],\n                            [-61.02314453124998, 9.15458984374996],\n                            [-61.24726562499998, 8.600341796875014],\n                            [-61.61870117187499, 8.59746093749996],\n                            [-61.30400390624999, 8.410400390625043],\n                            [-60.800976562499926, 8.592138671875034],\n                            [-60.16748046875, 8.616992187500031],\n                            [-60.01752929687501, 8.549316406250014],\n                            [-59.83164062499998, 8.305957031250003],\n                            [-59.84907226562498, 8.248681640624966],\n                            [-59.96484375000003, 8.191601562499969],\n                            [-59.99072265624997, 8.16201171874998],\n                            [-60.032421874999926, 8.053564453125006],\n                            [-60.51362304687501, 7.813183593749969],\n                            [-60.71865234374994, 7.535937499999974],\n                            [-60.606542968750006, 7.320849609375031],\n                            [-60.63330078124997, 7.211083984374966],\n                            [-60.58320312499998, 7.156201171874969],\n                            [-60.523193359375, 7.143701171875009],\n                            [-60.464941406250034, 7.166552734375045],\n                            [-60.39238281249999, 7.164550781249986],\n                            [-60.34506835937495, 7.15],\n                            [-60.32548828124996, 7.133984374999983],\n                            [-60.32207031249996, 7.092041015625043],\n                            [-60.35209960937496, 7.002880859374997],\n                            [-60.39501953125, 6.945361328125003],\n                            [-60.717919921874966, 6.768310546875],\n                            [-61.14560546874998, 6.694531249999983],\n                            [-61.20361328124997, 6.588378906250028],\n                            [-61.181591796874926, 6.513378906250026],\n                            [-61.15102539062502, 6.446533203124986],\n                            [-61.15229492187501, 6.385107421875006],\n                            [-61.12871093749999, 6.214306640625026],\n                            [-61.15947265624996, 6.174414062499977],\n                            [-61.22495117187498, 6.129199218750003],\n                            [-61.303125, 6.049511718750026],\n                            [-61.39082031250001, 5.938769531250017],\n                            [-61.376806640625006, 5.906982421875028],\n                            [-61.167187499999926, 5.674218750000037],\n                            [-60.95400390625002, 5.437402343750023],\n                            [-60.742138671874926, 5.202050781250037]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Venezuela\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [-64.765625, 17.794335937499994],\n                        [-64.58046874999994, 17.750195312499983],\n                        [-64.88911132812495, 17.701708984375045],\n                        [-64.765625, 17.794335937499994]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"U.S. Virgin Is.\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [104.06396484375003, 10.390820312500011],\n                            [104.01845703125, 10.029199218749966],\n                            [103.84951171875005, 10.371093749999986],\n                            [104.06396484375003, 10.390820312500011]\n                        ]\n                    ],\n                    [\n                        [\n                            [107.52128906250007, 20.926611328124977],\n                            [107.39921875000007, 20.903466796874966],\n                            [107.55126953125003, 21.034033203125006],\n                            [107.52128906250007, 20.926611328124977]\n                        ]\n                    ],\n                    [\n                        [\n                            [107.60273437500004, 21.21679687500003],\n                            [107.40351562500004, 21.093652343749994],\n                            [107.47626953125004, 21.268945312499994],\n                            [107.60273437500004, 21.21679687500003]\n                        ]\n                    ],\n                    [\n                        [\n                            [107.97265624999997, 21.507958984375023],\n                            [107.40996093750002, 21.284814453125023],\n                            [107.35429687500007, 21.055175781250057],\n                            [107.1647460937501, 20.94873046875003],\n                            [106.68339843750007, 21.000292968750074],\n                            [106.75341796875003, 20.73505859375004],\n                            [106.55078124999997, 20.52656250000001],\n                            [106.57285156250012, 20.392187499999977],\n                            [105.98408203125004, 19.939062500000034],\n                            [105.62177734375004, 18.96630859375003],\n                            [105.88828125000006, 18.502490234375045],\n                            [106.49902343749997, 17.946435546874994],\n                            [106.47890625000005, 17.719580078125063],\n                            [106.3705078125, 17.746875],\n                            [107.83378906250002, 16.322460937499983],\n                            [108.02939453125012, 16.331103515625074],\n                            [108.82128906249997, 15.377929687500028],\n                            [109.30332031250012, 13.856445312500043],\n                            [109.271875, 13.279345703124974],\n                            [109.42392578125006, 12.955957031249994],\n                            [109.44492187500006, 12.599609375000057],\n                            [109.33554687500012, 12.751904296874997],\n                            [109.21894531250004, 12.64580078124996],\n                            [109.30468750000003, 12.391162109375045],\n                            [109.20683593750007, 12.415380859375006],\n                            [109.1986328125, 11.724853515625014],\n                            [109.03964843750012, 11.592675781249994],\n                            [108.98671875, 11.336376953124997],\n                            [108.09492187500004, 10.897265624999989],\n                            [108.0013671875, 10.720361328125009],\n                            [107.26152343750007, 10.39838867187504],\n                            [107.00664062500002, 10.66054687499998],\n                            [106.94746093750004, 10.400341796874997],\n                            [106.72734375000007, 10.535644531250028],\n                            [106.605859375, 10.46494140625002],\n                            [106.74121093750003, 10.444384765625003],\n                            [106.75742187500006, 10.295800781250023],\n                            [106.46406250000004, 10.298291015624997],\n                            [106.78525390625012, 10.116455078124986],\n                            [106.59560546875005, 9.859863281250028],\n                            [106.1364257812501, 10.221679687500014],\n                            [106.56435546875005, 9.715625],\n                            [106.48408203125004, 9.559423828125006],\n                            [105.83095703125005, 10.000732421875028],\n                            [106.15859375, 9.59414062499998],\n                            [106.16835937500005, 9.396728515625],\n                            [105.50097656249997, 9.093212890624983],\n                            [105.11435546875006, 8.629199218750031],\n                            [104.77041015625, 8.59765625],\n                            [104.89628906250007, 8.746630859374974],\n                            [104.81855468750004, 8.801855468750034],\n                            [104.84521484375003, 9.606152343750026],\n                            [105.08447265625003, 9.99570312499999],\n                            [104.8019531250001, 10.202734374999977],\n                            [104.66347656250005, 10.169921875000043],\n                            [104.42636718750006, 10.411230468749991],\n                            [104.85058593749997, 10.534472656249974],\n                            [105.04638671874997, 10.701660156250014],\n                            [105.04570312500002, 10.911376953125014],\n                            [105.3146484375001, 10.845166015625026],\n                            [105.40576171875003, 10.95161132812504],\n                            [105.75507812500004, 10.989990234375043],\n                            [105.85332031250007, 10.86357421874996],\n                            [106.16396484375005, 10.794921875],\n                            [106.16093750000002, 11.037109375000057],\n                            [105.85605468750006, 11.294287109375048],\n                            [105.85146484375005, 11.635009765625],\n                            [106.0060546875001, 11.758007812500011],\n                            [106.39921875000007, 11.687011718750028],\n                            [106.41386718750002, 11.9484375],\n                            [106.70009765625, 11.979296874999974],\n                            [107.21210937500004, 12.30400390624996],\n                            [107.39335937500002, 12.260498046874972],\n                            [107.50644531250006, 12.364550781250031],\n                            [107.47539062500002, 13.030371093749963],\n                            [107.60546874999997, 13.437792968750017],\n                            [107.3314453125, 14.126611328125009],\n                            [107.51943359375005, 14.705078125],\n                            [107.51376953125012, 14.817382812500057],\n                            [107.52451171875012, 14.871826171875043],\n                            [107.50468750000007, 14.915917968749966],\n                            [107.48037109375, 14.979882812500037],\n                            [107.55527343750006, 15.057031250000023],\n                            [107.58964843750002, 15.118457031250017],\n                            [107.63369140625005, 15.18984375],\n                            [107.653125, 15.255224609374991],\n                            [107.62167968750006, 15.309863281250017],\n                            [107.56425781250002, 15.391601562499972],\n                            [107.45957031250012, 15.4658203125],\n                            [107.33876953125, 15.560498046875011],\n                            [107.27939453125006, 15.618701171875045],\n                            [107.16591796875005, 15.802490234375028],\n                            [107.1888671875, 15.838623046875],\n                            [107.36064453125002, 15.921728515624977],\n                            [107.3919921875, 15.951660156250028],\n                            [107.39638671875, 16.04301757812499],\n                            [106.93066406249997, 16.353125],\n                            [106.8927734375001, 16.396533203125074],\n                            [106.85107421875003, 16.515625],\n                            [106.83242187500005, 16.526269531250023],\n                            [106.79160156250006, 16.490332031250006],\n                            [106.73955078125007, 16.452539062500023],\n                            [106.6564453125001, 16.49262695312501],\n                            [106.54619140625002, 16.650732421874977],\n                            [106.53369140625003, 16.821044921875057],\n                            [106.52597656250006, 16.876611328124994],\n                            [106.50224609375002, 16.95410156249997],\n                            [106.26953125000003, 17.21679687500003],\n                            [106.00625, 17.415283203125057],\n                            [105.69140625000003, 17.737841796875045],\n                            [105.58847656250012, 17.983691406250045],\n                            [105.51855468749997, 18.077441406250045],\n                            [105.45820312500004, 18.15429687499997],\n                            [105.11455078125002, 18.40527343750003],\n                            [105.08701171875006, 18.496240234374994],\n                            [105.14541015625, 18.616796875000063],\n                            [105.14648437500003, 18.650976562500006],\n                            [103.89160156250003, 19.304980468750017],\n                            [103.89638671875, 19.339990234375023],\n                            [103.93203125, 19.366064453125006],\n                            [104.0275390625001, 19.42045898437499],\n                            [104.062890625, 19.48256835937505],\n                            [104.05156250000007, 19.564160156249955],\n                            [104.01347656250007, 19.64648437500003],\n                            [104.03203125000002, 19.675146484375006],\n                            [104.06279296875007, 19.678417968749983],\n                            [104.25986328125006, 19.685498046874983],\n                            [104.5462890625, 19.61054687500001],\n                            [104.58789062500003, 19.61875],\n                            [104.74316406250003, 19.754736328124977],\n                            [104.80175781249997, 19.83613281250004],\n                            [104.81513671875004, 19.90400390625001],\n                            [104.9279296875001, 20.01811523437499],\n                            [104.92919921875003, 20.082812500000017],\n                            [104.88867187500003, 20.169091796875023],\n                            [104.84785156250004, 20.202441406250045],\n                            [104.69873046875003, 20.20532226562503],\n                            [104.67695312500004, 20.224707031249977],\n                            [104.66191406250007, 20.28901367187501],\n                            [104.65644531250004, 20.32851562499999],\n                            [104.6188476562501, 20.37451171875003],\n                            [104.49619140625006, 20.413671875],\n                            [104.39218750000012, 20.424755859374955],\n                            [104.36777343750012, 20.44140624999997],\n                            [104.40781250000012, 20.48574218750005],\n                            [104.47861328125006, 20.529589843750017],\n                            [104.53271484374997, 20.55488281250001],\n                            [104.58320312500004, 20.646679687499955],\n                            [104.34960937499997, 20.821093750000074],\n                            [104.19531249999997, 20.913964843749966],\n                            [104.10136718750002, 20.94550781250001],\n                            [103.63505859375007, 20.697070312500017],\n                            [103.46357421875004, 20.779833984375017],\n                            [103.21074218750002, 20.840625],\n                            [103.10449218749997, 20.891650390625045],\n                            [102.88378906250003, 21.202587890624983],\n                            [102.85117187500006, 21.26591796874999],\n                            [102.94960937500005, 21.681347656249983],\n                            [102.84521484374997, 21.73476562500005],\n                            [102.81591796874997, 21.807373046875],\n                            [102.7982421875, 21.797949218750034],\n                            [102.77109375000006, 21.709667968749983],\n                            [102.73857421875002, 21.67792968750001],\n                            [102.66201171875005, 21.67602539062497],\n                            [102.58251953125003, 21.90429687500003],\n                            [102.12744140624997, 22.379199218750045],\n                            [102.1759765625001, 22.414648437500006],\n                            [102.2370117187501, 22.466015624999983],\n                            [102.40644531250004, 22.70800781249997],\n                            [102.47089843750004, 22.75092773437501],\n                            [102.98193359374997, 22.4482421875],\n                            [103.32666015625003, 22.769775390625057],\n                            [103.49296875000007, 22.587988281250034],\n                            [103.62021484375006, 22.782031250000045],\n                            [103.94150390625006, 22.540087890625045],\n                            [104.14306640624997, 22.800146484375006],\n                            [104.37177734375004, 22.704052734374983],\n                            [104.68730468750002, 22.822216796874983],\n                            [104.86474609375003, 23.136376953125023],\n                            [105.27539062500003, 23.34521484375003],\n                            [105.8429687500001, 22.922802734374955],\n                            [106.14843749999997, 22.970068359375006],\n                            [106.2790039062501, 22.857470703125045],\n                            [106.54179687500007, 22.908349609375023],\n                            [106.78027343749997, 22.778906250000034],\n                            [106.55039062500006, 22.501367187499994],\n                            [106.66357421875003, 21.97890625000005],\n                            [106.97099609375002, 21.923925781250034],\n                            [107.35117187500012, 21.60888671874997],\n                            [107.75927734374997, 21.655029296875057],\n                            [107.97265624999997, 21.507958984375023]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Vietnam\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [169.49130859375006, -19.54013671875002],\n                            [169.34726562500006, -19.623535156249957],\n                            [169.2174804687501, -19.476367187500003],\n                            [169.24746093750005, -19.3447265625],\n                            [169.49130859375006, -19.54013671875002]\n                        ]\n                    ],\n                    [\n                        [\n                            [169.334375, -18.940234375000017],\n                            [168.98691406250006, -18.87128906250001],\n                            [169.01582031250004, -18.64375],\n                            [169.14384765625002, -18.63105468750001],\n                            [169.334375, -18.940234375000017]\n                        ]\n                    ],\n                    [\n                        [\n                            [168.44580078124997, -17.54218750000004],\n                            [168.58496093750003, -17.695898437500006],\n                            [168.52460937500004, -17.798046875000026],\n                            [168.15820312500003, -17.710546874999963],\n                            [168.2731445312501, -17.552246093749957],\n                            [168.44580078124997, -17.54218750000004]\n                        ]\n                    ],\n                    [\n                        [\n                            [168.44677734375003, -16.778808593749957],\n                            [168.18144531250002, -16.804003906250017],\n                            [168.13535156250006, -16.636914062499997],\n                            [168.44677734375003, -16.778808593749957]\n                        ]\n                    ],\n                    [\n                        [\n                            [168.29667968750007, -16.33652343749999],\n                            [167.92900390625002, -16.22871093749997],\n                            [168.16386718750002, -16.081640625000034],\n                            [168.29667968750007, -16.33652343749999]\n                        ]\n                    ],\n                    [\n                        [\n                            [167.4125, -16.095898437499997],\n                            [167.83662109375004, -16.449707031249957],\n                            [167.44931640625012, -16.554980468750003],\n                            [167.34921875000006, -16.15449218750004],\n                            [167.15146484375006, -16.080468749999966],\n                            [167.19951171875002, -15.885058593750031],\n                            [167.33574218750007, -15.916699218749997],\n                            [167.4125, -16.095898437499997]\n                        ]\n                    ],\n                    [\n                        [\n                            [167.9113281250001, -15.435937500000023],\n                            [167.67421875, -15.4515625],\n                            [168.00253906250012, -15.283203124999986],\n                            [167.9113281250001, -15.435937500000023]\n                        ]\n                    ],\n                    [\n                        [\n                            [166.74580078125004, -14.826855468750011],\n                            [166.81015625000012, -15.15742187500004],\n                            [167.0755859375, -14.935644531249977],\n                            [167.20078125000012, -15.443066406249969],\n                            [167.0939453125001, -15.580859374999974],\n                            [166.75830078125003, -15.631152343750003],\n                            [166.63105468750004, -15.406054687499974],\n                            [166.56738281250003, -14.641796874999969],\n                            [166.74580078125004, -14.826855468750011]\n                        ]\n                    ],\n                    [\n                        [\n                            [167.58486328125, -14.260937500000011],\n                            [167.43027343750012, -14.294921875],\n                            [167.41074218750006, -14.19746093750004],\n                            [167.50644531250012, -14.142187499999977],\n                            [167.58486328125, -14.260937500000011]\n                        ]\n                    ],\n                    [\n                        [\n                            [167.48886718750006, -13.907226562499972],\n                            [167.3917968750001, -13.788378906250017],\n                            [167.48105468750006, -13.709472656250014],\n                            [167.48886718750006, -13.907226562499972]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Vanuatu\", \"childNum\": 10 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [-171.4541015625, -14.04648437500002],\n                            [-171.9119140625, -14.001660156250026],\n                            [-172.04589843750003, -13.857128906249983],\n                            [-171.60390624999997, -13.879199218750045],\n                            [-171.4541015625, -14.04648437500002]\n                        ]\n                    ],\n                    [\n                        [\n                            [-172.33349609375, -13.46523437499999],\n                            [-172.17685546874998, -13.68466796875002],\n                            [-172.224951171875, -13.804296874999963],\n                            [-172.535693359375, -13.791699218749983],\n                            [-172.77851562499998, -13.516796875000011],\n                            [-172.33349609375, -13.46523437499999]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Samoa\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [53.76318359374997, 12.636816406249991],\n                            [54.18740234375005, 12.664013671875026],\n                            [54.511132812499994, 12.552783203125017],\n                            [54.12949218750006, 12.360644531250045],\n                            [53.71884765625006, 12.318994140624994],\n                            [53.31582031250005, 12.533154296875011],\n                            [53.53496093750002, 12.715771484374997],\n                            [53.76318359374997, 12.636816406249991]\n                        ]\n                    ],\n                    [\n                        [\n                            [42.75585937500003, 13.70429687500004],\n                            [42.689746093750074, 13.673632812500017],\n                            [42.7941406250001, 13.766113281250028],\n                            [42.75585937500003, 13.70429687500004]\n                        ]\n                    ],\n                    [\n                        [\n                            [42.787402343750074, 13.971484375000031],\n                            [42.69404296875004, 14.007910156249991],\n                            [42.76210937500005, 14.067480468750048],\n                            [42.787402343750074, 13.971484375000031]\n                        ]\n                    ],\n                    [\n                        [\n                            [53.08564453125004, 16.648388671874955],\n                            [52.327734375, 16.293554687500063],\n                            [52.17402343750004, 15.956835937500017],\n                            [52.2174804687501, 15.655517578125],\n                            [51.3224609375001, 15.22626953125004],\n                            [49.34990234375002, 14.637792968749977],\n                            [48.66835937499999, 14.050146484374977],\n                            [47.9899414062501, 14.048095703125],\n                            [47.40771484374997, 13.661621093750057],\n                            [46.78886718750002, 13.465576171874986],\n                            [45.65732421875006, 13.338720703124991],\n                            [45.03867187500006, 12.815869140624969],\n                            [44.617773437500006, 12.817236328124977],\n                            [44.00585937499997, 12.607666015625],\n                            [43.634375, 12.744482421874991],\n                            [43.487597656250074, 12.69882812500002],\n                            [43.23193359375003, 13.267089843750057],\n                            [43.2824218750001, 13.692529296875037],\n                            [43.08906250000004, 14.010986328125],\n                            [42.93642578125005, 14.938574218749963],\n                            [42.85566406250004, 15.132958984375037],\n                            [42.65781250000006, 15.232812500000051],\n                            [42.79902343750004, 15.326269531249991],\n                            [42.71718750000005, 15.654638671875006],\n                            [42.83964843750002, 16.032031250000074],\n                            [42.79931640624997, 16.37177734375001],\n                            [43.16503906249997, 16.689404296874955],\n                            [43.19091796875003, 17.359375],\n                            [43.41796875000003, 17.516259765625023],\n                            [43.91699218749997, 17.32470703124997],\n                            [45.14804687500006, 17.427441406249955],\n                            [45.5353515625001, 17.30205078124999],\n                            [46.72763671875006, 17.26557617187501],\n                            [46.97568359375006, 16.953466796875034],\n                            [47.14355468749997, 16.946679687499966],\n                            [47.44179687499999, 17.111865234375045],\n                            [47.57958984374997, 17.448339843750034],\n                            [48.17216796875002, 18.156933593749983],\n                            [49.04199218750003, 18.58178710937503],\n                            [51.977636718750006, 18.996142578125074],\n                            [53.08564453125004, 16.648388671874955]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Yemen\", \"childNum\": 4 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [37.85693359375003, -46.94423828124998],\n                            [37.5900390625001, -46.90800781250006],\n                            [37.78955078124997, -46.8375],\n                            [37.85693359375003, -46.94423828124998]\n                        ]\n                    ],\n                    [\n                        [\n                            [31.799609375000017, -23.8921875],\n                            [31.98583984374997, -24.460644531249983],\n                            [31.921679687500017, -25.96875],\n                            [31.335156250000097, -25.755566406249997],\n                            [31.207324218750074, -25.843359375000034],\n                            [31.08808593750004, -25.980664062500026],\n                            [30.803320312500006, -26.41347656250001],\n                            [30.806738281250006, -26.78525390624999],\n                            [30.88330078124997, -26.792382812500023],\n                            [30.938085937500006, -26.91582031250003],\n                            [31.06337890625005, -27.1123046875],\n                            [31.274023437500063, -27.23837890625002],\n                            [31.469531250000017, -27.29550781250002],\n                            [31.74257812500005, -27.309960937500037],\n                            [31.95839843750005, -27.305859375],\n                            [31.946093750000017, -27.173632812499974],\n                            [31.96718750000005, -26.96064453125001],\n                            [31.994726562500006, -26.817480468749977],\n                            [32.024804687499994, -26.81113281250002],\n                            [32.112890625, -26.83945312500002],\n                            [32.19960937499999, -26.833496093749957],\n                            [32.35351562499997, -26.861621093750003],\n                            [32.7765625000001, -26.850976562499966],\n                            [32.88613281250005, -26.849316406249983],\n                            [32.53476562500006, -28.19970703125003],\n                            [32.285742187500006, -28.62148437499998],\n                            [31.335156250000097, -29.378125],\n                            [29.97119140625003, -31.322070312500017],\n                            [28.449414062500068, -32.62460937499999],\n                            [27.077441406250074, -33.52119140625004],\n                            [26.429492187500045, -33.75957031250002],\n                            [25.80585937500001, -33.737109374999974],\n                            [25.574218750000057, -34.03535156249998],\n                            [25.00292968750003, -33.97363281250003],\n                            [24.8271484375, -34.16894531250003],\n                            [24.595507812500074, -34.17451171875],\n                            [23.697851562500063, -33.99277343750002],\n                            [23.268164062500006, -34.08115234374999],\n                            [22.553808593750063, -34.01005859374999],\n                            [22.24550781250005, -34.06914062500003],\n                            [21.788964843750023, -34.37265624999996],\n                            [20.529882812500034, -34.4630859375],\n                            [20.020605468750006, -34.785742187500006],\n                            [19.298242187500023, -34.61503906249996],\n                            [19.330761718750068, -34.49238281250001],\n                            [19.098339843750068, -34.350097656249986],\n                            [18.831347656250017, -34.36406249999999],\n                            [18.75214843750004, -34.08261718750002],\n                            [18.50039062499999, -34.10927734375004],\n                            [18.46162109375001, -34.346875],\n                            [18.35205078124997, -34.1884765625],\n                            [18.43300781250005, -33.71728515625003],\n                            [17.851074218750057, -32.82744140625002],\n                            [17.96523437500005, -32.70859374999996],\n                            [18.125, -32.74912109374996],\n                            [18.325292968750034, -32.50498046874996],\n                            [18.21083984375008, -31.74248046874996],\n                            [17.34707031250005, -30.44482421875],\n                            [16.95, -29.40341796875002],\n                            [16.739453124999983, -29.009375],\n                            [16.447558593750045, -28.61757812499998],\n                            [16.755761718750023, -28.45214843750003],\n                            [16.7875, -28.39472656249997],\n                            [16.81015625, -28.264550781249994],\n                            [16.841210937500023, -28.21894531250004],\n                            [16.875292968750045, -28.12792968749997],\n                            [16.93330078125004, -28.06962890624999],\n                            [17.05625, -28.03105468750003],\n                            [17.1884765625, -28.13251953125001],\n                            [17.358691406250017, -28.269433593750023],\n                            [17.44794921875001, -28.698144531249966],\n                            [18.310839843750017, -28.88623046875],\n                            [19.16171875, -28.93876953124996],\n                            [19.245800781250068, -28.90166015625003],\n                            [19.31269531250004, -28.733300781250023],\n                            [19.539843750000017, -28.574609375000023],\n                            [19.98046875, -28.45126953125002],\n                            [19.98046875, -28.310351562500003],\n                            [19.98046875, -24.77675781249998],\n                            [20.430664062500057, -25.14707031250002],\n                            [20.79316406250001, -25.915625],\n                            [20.641406250000017, -26.7421875],\n                            [20.739843749999977, -26.84882812499997],\n                            [21.694726562500023, -26.840917968749963],\n                            [21.738085937500045, -26.806835937500026],\n                            [21.788281250000068, -26.710058593750034],\n                            [22.01093750000004, -26.635839843750006],\n                            [22.090917968749977, -26.580175781250034],\n                            [22.217578125000045, -26.38886718749997],\n                            [22.47089843750004, -26.219042968750003],\n                            [22.548632812500074, -26.178417968749997],\n                            [22.59765625000003, -26.13271484375001],\n                            [22.878808593750023, -25.457910156250023],\n                            [23.148730468750017, -25.288671875],\n                            [23.389257812500006, -25.291406250000023],\n                            [23.89375, -25.600878906250017],\n                            [23.96953124999999, -25.62607421874999],\n                            [24.192968750000034, -25.632910156249963],\n                            [24.33056640625, -25.742871093749983],\n                            [25.21337890625, -25.75625],\n                            [25.518164062500006, -25.66279296875001],\n                            [25.91210937499997, -24.747460937499966],\n                            [26.031835937500034, -24.70244140625003],\n                            [26.130859375000057, -24.671484375000034],\n                            [26.39716796875004, -24.61357421874996],\n                            [26.451757812500063, -24.582714843749983],\n                            [26.835058593750063, -24.240820312499963],\n                            [27.085546875000034, -23.577929687500003],\n                            [27.7685546875, -23.14892578125],\n                            [27.812597656250006, -23.108007812500006],\n                            [28.210156249999983, -22.693652343749974],\n                            [28.83984375000003, -22.480859374999966],\n                            [28.94580078125003, -22.39511718749999],\n                            [29.013476562500045, -22.27841796875002],\n                            [29.129882812500057, -22.21328125],\n                            [29.364843750000063, -22.19394531250005],\n                            [29.37744140625003, -22.19277343749998],\n                            [29.66308593749997, -22.146289062500017],\n                            [29.90234375000003, -22.184179687500006],\n                            [30.19042968750003, -22.291113281250034],\n                            [30.460156250000097, -22.329003906250023],\n                            [30.71162109375004, -22.297851562499986],\n                            [31.07343750000004, -22.30781249999997],\n                            [31.19726562499997, -22.344921874999983],\n                            [31.287890625000074, -22.402050781249983],\n                            [31.54560546875004, -23.48232421874998],\n                            [31.799609375000017, -23.8921875]\n                        ],\n                        [\n                            [27.19355468750001, -29.94130859375001],\n                            [27.364062500000017, -30.27919921875001],\n                            [27.753125, -30.6],\n                            [28.05683593750001, -30.63105468750001],\n                            [28.128710937500017, -30.52509765625001],\n                            [28.39208984375003, -30.14755859375002],\n                            [28.646875, -30.1265625],\n                            [29.09804687500005, -29.919042968750006],\n                            [29.142187500000063, -29.70097656249999],\n                            [29.293554687500006, -29.56689453125003],\n                            [29.348828125000097, -29.441992187499977],\n                            [29.38671874999997, -29.319726562500023],\n                            [29.301367187500006, -29.08984375],\n                            [28.625781250000017, -28.581738281250054],\n                            [28.583398437499994, -28.59414062499999],\n                            [28.471875, -28.615820312499977],\n                            [28.23261718750004, -28.701269531249977],\n                            [28.084375, -28.779980468750026],\n                            [27.95986328125008, -28.87333984375003],\n                            [27.73554687500004, -28.940039062500034],\n                            [27.294531250000063, -29.519335937500017],\n                            [27.056933593750074, -29.62558593749999],\n                            [27.19355468750001, -29.94130859375001]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"South Africa\", \"childNum\": 2 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [33.148046875, -9.603515625],\n                        [33.25, -9.759570312500003],\n                        [33.35097656250002, -9.862207031250009],\n                        [33.33710937500001, -9.954003906250009],\n                        [33.3115234375, -10.037988281250009],\n                        [33.52890625, -10.234667968750003],\n                        [33.53759765625, -10.3515625],\n                        [33.5537109375, -10.391308593750011],\n                        [33.66152343750002, -10.553125],\n                        [33.29277343750002, -10.85234375],\n                        [33.37978515625002, -11.157910156250011],\n                        [33.26835937500002, -11.40390625],\n                        [33.23271484375002, -11.417675781250011],\n                        [33.22636718750002, -11.534863281250011],\n                        [33.30390625000001, -11.690820312500009],\n                        [33.25234375000002, -12.112597656250003],\n                        [33.34013671875002, -12.308300781250011],\n                        [33.512304687500006, -12.347753906250006],\n                        [32.975195312500006, -12.701367187500011],\n                        [32.96757812500002, -13.225],\n                        [32.67041015625, -13.590429687500006],\n                        [32.797460937500006, -13.6884765625],\n                        [32.98125, -14.009375],\n                        [33.148046875, -13.94091796875],\n                        [33.201757812500006, -14.013378906250011],\n                        [30.231835937500023, -14.990332031250006],\n                        [30.39609375, -15.64306640625],\n                        [29.4873046875, -15.69677734375],\n                        [28.9130859375, -15.98779296875],\n                        [28.760546875000017, -16.53212890625001],\n                        [27.932226562500006, -16.89619140625001],\n                        [27.020800781250017, -17.95839843750001],\n                        [26.779882812500006, -18.04150390625],\n                        [26.333398437500023, -17.929296875],\n                        [25.995898437500017, -17.969824218750006],\n                        [25.2587890625, -17.793554687500006],\n                        [25.001757812500017, -17.56855468750001],\n                        [24.73291015625, -17.51777343750001],\n                        [24.27490234375, -17.481054687500006],\n                        [23.380664062500017, -17.640625],\n                        [22.193945312500006, -16.628125],\n                        [21.979785156250017, -15.95556640625],\n                        [21.979394531250023, -14.440527343750006],\n                        [21.979296875000017, -14.11962890625],\n                        [21.979101562500006, -13.798730468750009],\n                        [21.978906250000023, -13.0009765625],\n                        [22.209570312500006, -13.0009765625],\n                        [23.843164062500023, -13.0009765625],\n                        [23.962988281250006, -12.988476562500011],\n                        [23.882421875, -12.799023437500011],\n                        [23.886523437500017, -12.743261718750006],\n                        [23.909375, -12.636132812500009],\n                        [23.98388671875, -11.725],\n                        [23.96650390625001, -10.871777343750011],\n                        [24.36572265625, -11.1298828125],\n                        [24.3779296875, -11.417089843750006],\n                        [25.28876953125001, -11.21240234375],\n                        [25.349414062500017, -11.623046875],\n                        [26.025976562500006, -11.89013671875],\n                        [26.824023437500017, -11.965234375],\n                        [27.1591796875, -11.579199218750006],\n                        [27.573828125, -12.22705078125],\n                        [28.412890625000017, -12.51806640625],\n                        [28.550878906250006, -12.836132812500011],\n                        [28.730078125, -12.925488281250011],\n                        [29.014257812500006, -13.368847656250011],\n                        [29.20185546875001, -13.398339843750009],\n                        [29.55419921875, -13.248925781250009],\n                        [29.775195312500017, -13.438085937500006],\n                        [29.79511718750001, -12.155468750000011],\n                        [29.508203125000023, -12.228222656250011],\n                        [29.48554687500001, -12.41845703125],\n                        [29.064355468750023, -12.348828125000011],\n                        [28.482519531250006, -11.812109375],\n                        [28.383398437500006, -11.566699218750003],\n                        [28.6455078125, -10.550195312500009],\n                        [28.60419921875001, -9.678808593750006],\n                        [28.400683593750017, -9.224804687500011],\n                        [28.869531250000023, -8.785839843750011],\n                        [28.89814453125001, -8.485449218750006],\n                        [30.75117187500001, -8.193652343750003],\n                        [30.830664062500006, -8.385546875],\n                        [30.891992187500023, -8.473730468750006],\n                        [30.968359375, -8.550976562500011],\n                        [31.07636718750001, -8.611914062500006],\n                        [31.3505859375, -8.60703125],\n                        [31.44921875, -8.65390625],\n                        [31.53486328125001, -8.71328125],\n                        [31.55625, -8.80546875],\n                        [31.673632812500017, -8.908789062500006],\n                        [31.91865234375001, -8.9421875],\n                        [31.921875, -9.019433593750009],\n                        [31.94257812500001, -9.054003906250003],\n                        [32.75664062500002, -9.322265625],\n                        [32.919921875, -9.407421875000011],\n                        [32.99599609375002, -9.622851562500003],\n                        [33.148046875, -9.603515625]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Zambia\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [31.287890625000017, -22.40205078125001],\n                        [31.07343750000001, -22.30781250000001],\n                        [30.71162109375001, -22.2978515625],\n                        [30.46015625000001, -22.32900390625001],\n                        [30.1904296875, -22.291113281250006],\n                        [29.90234375, -22.184179687500006],\n                        [29.6630859375, -22.146289062500003],\n                        [29.37744140625, -22.19277343750001],\n                        [29.36484375, -22.193945312500006],\n                        [29.315234375000017, -22.15771484375],\n                        [29.237207031250023, -22.07949218750001],\n                        [29.042382812500023, -22.018359375],\n                        [29.02558593750001, -21.796875],\n                        [28.014062500000023, -21.55419921875],\n                        [27.66943359375, -21.064257812500003],\n                        [27.679296875, -20.503027343750006],\n                        [27.28076171875, -20.47871093750001],\n                        [27.17822265625, -20.10097656250001],\n                        [26.168066406250006, -19.53828125000001],\n                        [25.939355468750023, -18.93867187500001],\n                        [25.242285156250006, -17.969042968750003],\n                        [25.2587890625, -17.793554687500006],\n                        [25.995898437500017, -17.969824218750006],\n                        [26.333398437500023, -17.929296875],\n                        [26.779882812500006, -18.04150390625],\n                        [27.020800781250017, -17.95839843750001],\n                        [27.932226562500006, -16.89619140625001],\n                        [28.760546875000017, -16.53212890625001],\n                        [28.9130859375, -15.98779296875],\n                        [29.4873046875, -15.69677734375],\n                        [30.39609375, -15.64306640625],\n                        [30.437792968750017, -15.995312500000011],\n                        [31.236230468750023, -16.02363281250001],\n                        [31.939843750000023, -16.428808593750006],\n                        [32.94804687500002, -16.71230468750001],\n                        [32.87626953125002, -16.88359375],\n                        [32.99306640625002, -18.35957031250001],\n                        [32.69970703125, -18.94091796875],\n                        [32.84980468750001, -19.10439453125001],\n                        [32.77763671875002, -19.388769531250006],\n                        [32.992773437500006, -19.98486328125],\n                        [32.49238281250001, -20.659765625],\n                        [32.353613281250006, -21.136523437500003],\n                        [32.429785156250006, -21.29707031250001],\n                        [31.429492187500017, -22.298828125],\n                        [31.287890625000017, -22.40205078125001]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"Zimbabwe\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"Polygon\",\n                \"coordinates\": [\n                    [\n                        [74.00809389139292, 33.25375789331485],\n                        [73.19660141888893, 33.898124784580936],\n                        [73.13410859949555, 34.82510160558277],\n                        [72.31128647748268, 35.77290936638241],\n                        [73.08203125000107, 36.43949943991182],\n                        [73.08961802927895, 36.86435907947333],\n                        [73.116796875, 36.868554687499994],\n                        [74.03886718750002, 36.825732421874996],\n                        [74.54140625000002, 37.02216796875],\n                        [74.69218750000002, 37.0357421875],\n                        [74.8892578125, 36.952441406249996],\n                        [74.94912109375002, 36.968359375],\n                        [75.05390625000001, 36.987158203125],\n                        [75.14521484375001, 36.9732421875],\n                        [75.3466796875, 36.913476562499994],\n                        [75.37685546875002, 36.883691406249994],\n                        [75.42421875000002, 36.738232421875],\n                        [75.46025390625002, 36.725048828125],\n                        [75.57373046875, 36.759326171874996],\n                        [75.66718750000001, 36.741992187499996],\n                        [75.77216796875001, 36.694921875],\n                        [75.84023437500002, 36.649707031249996],\n                        [75.88496093750001, 36.600732421874994],\n                        [75.93300781250002, 36.52158203125],\n                        [75.95185546875001, 36.45810546875],\n                        [75.97441406250002, 36.382421875],\n                        [75.91230468750001, 36.048974609374994],\n                        [76.07089843750003, 35.9830078125],\n                        [76.14785156250002, 35.829003906249994],\n                        [76.17783203125003, 35.810546875],\n                        [76.25166015625001, 35.8109375],\n                        [76.3857421875, 35.837158203125],\n                        [76.50205078125003, 35.878222656249996],\n                        [76.55126953125, 35.887060546875],\n                        [76.5634765625, 35.772998046874996],\n                        [76.6318359375, 35.729394531249994],\n                        [76.7275390625, 35.678662109375],\n                        [76.76689453124999, 35.66171875],\n                        [76.81279296874999, 35.571826171874996],\n                        [76.88222656250002, 35.4357421875],\n                        [76.927734375, 35.346630859375],\n                        [77.04863281249999, 35.109912109374996],\n                        [77.00087890625002, 34.991992187499996],\n                        [76.78291015625001, 34.900195312499996],\n                        [76.75751953125001, 34.877832031249994],\n                        [76.7490234375, 34.847558593749994],\n                        [76.6962890625, 34.786914062499996],\n                        [76.59443359375001, 34.73583984375],\n                        [76.45673828125001, 34.756103515625],\n                        [76.17246093750003, 34.667724609375],\n                        [76.041015625, 34.669921875],\n                        [75.93828125000002, 34.612548828125],\n                        [75.86210937500002, 34.56025390625],\n                        [75.70917968750001, 34.503076171874994],\n                        [74.300390625, 34.765380859375],\n                        [74.17197265625, 34.7208984375],\n                        [74.05585937500001, 34.6806640625],\n                        [73.96123046875002, 34.653466796874994],\n                        [73.79453125, 34.378222656249996],\n                        [73.80996093750002, 34.325341796874994],\n                        [73.92460937500002, 34.287841796875],\n                        [73.97236328125001, 34.236621093749996],\n                        [73.9794921875, 34.191308593749994],\n                        [73.90390625, 34.1080078125],\n                        [73.94990234375001, 34.018798828125],\n                        [74.24648437500002, 33.990185546875],\n                        [73.97646484375002, 33.7212890625],\n                        [74.15, 33.506982421874994],\n                        [74.00809389139292, 33.25375789331485]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"\", \"childNum\": 1 }\n        },\n        {\n            \"geometry\": {\n                \"type\": \"MultiPolygon\",\n                \"coordinates\": [\n                    [\n                        [\n                            [78.49194250885338, 32.53122786149202],\n                            [78.10154031239509, 32.87658365066666],\n                            [77.71342088235082, 32.6917648744551],\n                            [77.06655516561037, 33.301666835953235],\n                            [76.62299010270264, 33.32014871357439],\n                            [76.32728006076415, 32.87658365066666],\n                            [75.73585997688717, 32.78417426256088],\n                            [75.62496871116024, 32.28516356678968],\n                            [75.32221348233018, 32.28516356678968],\n                            [74.98730468749997, 32.46220703124996],\n                            [74.78886718750013, 32.4578125],\n                            [74.6857421875001, 32.493798828124994],\n                            [74.66328125000004, 32.75766601562495],\n                            [74.63242187499995, 32.770898437500136],\n                            [74.58828125000011, 32.7532226562501],\n                            [74.35458984375012, 32.76870117187505],\n                            [74.30546875000007, 32.81044921875002],\n                            [74.30361328125005, 32.991796875000034],\n                            [73.98984375000006, 33.22119140625006],\n                            [74.15, 33.506982421874994],\n                            [73.97646484375016, 33.72128906249998],\n                            [74.24648437500011, 33.99018554687504],\n                            [73.9499023437501, 34.018798828125],\n                            [73.90390625000012, 34.10800781250006],\n                            [73.97949218750009, 34.191308593749966],\n                            [73.97236328125004, 34.23662109374996],\n                            [73.92460937500007, 34.287841796875114],\n                            [73.80996093750016, 34.32534179687511],\n                            [73.79453125000006, 34.378222656250045],\n                            [73.96123046875007, 34.653466796874994],\n                            [74.05585937500015, 34.68066406250003],\n                            [74.17197265624995, 34.72089843750004],\n                            [74.30039062500006, 34.76538085937506],\n                            [75.70917968750004, 34.50307617187508],\n                            [75.86210937500002, 34.56025390625001],\n                            [75.93828125000019, 34.612548828125],\n                            [76.04101562500014, 34.66992187499997],\n                            [76.17246093750006, 34.66772460937506],\n                            [76.4567382812501, 34.756103515625114],\n                            [76.5944335937501, 34.73583984375006],\n                            [76.69628906249997, 34.78691406249999],\n                            [76.74902343750014, 34.84755859375008],\n                            [76.7575195312501, 34.87783203125005],\n                            [76.7829101562501, 34.90019531249999],\n                            [77.00087890625011, 34.99199218750002],\n                            [77.03066406250011, 35.06235351562498],\n                            [77.04863281250007, 35.109912109375074],\n                            [77.42343749999995, 35.30258789062506],\n                            [77.57158203125002, 35.37875976562495],\n                            [77.69697265625015, 35.443261718750136],\n                            [77.79941406250006, 35.49589843750002],\n                            [78.0426757812501, 35.4797851562501],\n                            [78.07578125000006, 35.13491210937502],\n                            [78.15849609375002, 34.94648437499998],\n                            [78.32695312500007, 34.60639648437498],\n                            [78.86484375000006, 34.39033203125001],\n                            [78.93642578125, 34.35195312500002],\n                            [78.97060546875011, 34.22822265625004],\n                            [78.72666015625006, 34.013378906249955],\n                            [78.78378906250006, 33.80878906250004],\n                            [78.86503906250002, 33.43110351562501],\n                            [78.94843750000004, 33.346533203125006],\n                            [79.1125, 33.22626953125001],\n                            [79.13515625000005, 33.17192382812496],\n                            [79.10283203125007, 33.05253906249996],\n                            [79.14550781250003, 33.00146484375006],\n                            [79.16992187500003, 32.497216796874994],\n                            [78.91894531249997, 32.3582031250001],\n                            [78.75351562500012, 32.49926757812506],\n                            [78.73671875, 32.55839843750002],\n                            [78.49194250885338, 32.53122786149202]\n                        ]\n                    ]\n                ]\n            },\n            \"properties\": { \"name\": \"\", \"childNum\": 1 }\n        }\n    ]\n}\n"
  },
  {
    "path": "core/cmd/server/web/web.go",
    "content": "package web\n\nimport \"embed\"\n\n//go:embed index.html\nvar IndexHtml embed.FS\n\n//go:embed assets/*\nvar Assets embed.FS\n\n//go:embed favicon.png\nvar Favicon embed.FS\n\n//go:embed static/*\nvar Static embed.FS\n"
  },
  {
    "path": "core/constant/alert.go",
    "content": "package constant\n\nconst (\n\tWeChat   = \"wechat\"\n\tSMS      = \"sms\"\n\tEmail    = \"mail\"\n\tWeCom    = \"weCom\"\n\tDingTalk = \"dingTalk\"\n\tFeiShu   = \"feiShu\"\n\tCustom   = \"custom\"\n)\n"
  },
  {
    "path": "core/constant/common.go",
    "content": "package constant\n\nimport \"sync/atomic\"\n\ntype DBContext string\n\nconst (\n\tTimeOut5s  = 5\n\tTimeOut20s = 20\n\tTimeOut5m  = 300\n\n\tDateLayout         = \"2006-01-02\" // or use time.DateOnly while go version >= 1.20\n\tDefaultDate        = \"1970-01-01\"\n\tDateTimeLayout     = \"2006-01-02 15:04:05\" // or use time.DateTime while go version >= 1.20\n\tDateTimeSlimLayout = \"20060102150405\"\n\n\tOrderDesc = \"descending\"\n\tOrderAsc  = \"ascending\"\n\n\t// backup\n\tS3          = \"S3\"\n\tOSS         = \"OSS\"\n\tSftp        = \"SFTP\"\n\tOneDrive    = \"OneDrive\"\n\tMinIo       = \"MINIO\"\n\tCos         = \"COS\"\n\tKodo        = \"KODO\"\n\tWebDAV      = \"WebDAV\"\n\tLocal       = \"LOCAL\"\n\tUPYUN       = \"UPYUN\"\n\tALIYUN      = \"ALIYUN\"\n\tGoogleDrive = \"GoogleDrive\"\n\n\tOneDriveRedirectURI = \"http://localhost/login/authorized\"\n)\n\nconst (\n\tDirPerm  = 0755\n\tFilePerm = 0644\n)\n\nconst (\n\tSyncSystemProxy                  = \"SyncSystemProxy\"\n\tSyncScripts                      = \"SyncScripts\"\n\tSyncBackupAccounts               = \"SyncBackupAccounts\"\n\tSyncAlertSetting                 = \"SyncAlertSetting\"\n\tSyncCustomApp                    = \"SyncCustomApp\"\n\tSyncLanguage                     = \"SyncLanguage\"\n\tSyncEdition                      = \"SyncEdition\"\n\tSyncSystemProxyWithRestartDocker = \"SyncSystemProxyWithRestartDocker\"\n)\n\nvar WebUrlMap = map[string]struct{}{\n\t\"/apps\":           {},\n\t\"/apps/all\":       {},\n\t\"/apps/installed\": {},\n\t\"/apps/upgrade\":   {},\n\t\"/apps/setting\":   {},\n\n\t\"/ai\":                {},\n\t\"/ai/model\":          {},\n\t\"/ai/gpu\":            {},\n\t\"/ai/gpu/current\":    {},\n\t\"/ai/gpu/history\":    {},\n\t\"/ai/mcp\":            {},\n\t\"/ai/model/tensorrt\": {},\n\t\"/ai/model/vllm\":     {},\n\t\"/ai/model/ollama\":   {},\n\t\"/ai/agents/agent\":   {},\n\t\"/ai/agents/model\":   {},\n\n\t\"/containers\":                   {},\n\t\"/containers/container/operate\": {},\n\t\"/containers/container\":         {},\n\t\"/containers/image\":             {},\n\t\"/containers/network\":           {},\n\t\"/containers/volume\":            {},\n\t\"/containers/repo\":              {},\n\t\"/containers/compose\":           {},\n\t\"/containers/template\":          {},\n\t\"/containers/setting\":           {},\n\t\"/containers/dashboard\":         {},\n\n\t\"/cronjobs\":                 {},\n\t\"/cronjobs/cronjob\":         {},\n\t\"/cronjobs/library\":         {},\n\t\"/cronjobs/cronjob/operate\": {},\n\n\t\"/databases\":                   {},\n\t\"/databases/mysql\":             {},\n\t\"/databases/mysql/remote\":      {},\n\t\"/databases/postgresql\":        {},\n\t\"/databases/postgresql/remote\": {},\n\t\"/databases/redis\":             {},\n\t\"/databases/redis/remote\":      {},\n\n\t\"/hosts\":                  {},\n\t\"/hosts/files\":            {},\n\t\"/hosts/monitor/monitor\":  {},\n\t\"/hosts/monitor/setting\":  {},\n\t\"/hosts/firewall/port\":    {},\n\t\"/hosts/firewall/forward\": {},\n\t\"/hosts/firewall/ip\":      {},\n\t\"/hosts/firewall/advance\": {},\n\t\"/hosts/process/process\":  {},\n\t\"/hosts/process/network\":  {},\n\t\"/hosts/ssh/ssh\":          {},\n\t\"/hosts/ssh/log\":          {},\n\t\"/hosts/ssh/session\":      {},\n\t\"/hosts/disk\":             {},\n\n\t\"/terminal\": {},\n\n\t\"/logs\":           {},\n\t\"/logs/operation\": {},\n\t\"/logs/login\":     {},\n\t\"/logs/website\":   {},\n\t\"/logs/system\":    {},\n\t\"/logs/ssh\":       {},\n\t\"/logs/task\":      {},\n\n\t\"/settings\":               {},\n\t\"/settings/panel\":         {},\n\t\"/settings/backupaccount\": {},\n\t\"/settings/license\":       {},\n\t\"/settings/about\":         {},\n\t\"/settings/safe\":          {},\n\t\"/settings/alert\":         {},\n\t\"/settings/snapshot\":      {},\n\t\"/settings/expired\":       {},\n\n\t\"/toolbox\":              {},\n\t\"/toolbox/device\":       {},\n\t\"/toolbox/supervisor\":   {},\n\t\"/toolbox/clam\":         {},\n\t\"/toolbox/clam/setting\": {},\n\t\"/toolbox/ftp\":          {},\n\t\"/toolbox/fail2ban\":     {},\n\t\"/toolbox/clean\":        {},\n\n\t\"/websites\":                 {},\n\t\"/websites/ssl\":             {},\n\t\"/websites/runtimes/php\":    {},\n\t\"/websites/runtimes/node\":   {},\n\t\"/websites/runtimes/java\":   {},\n\t\"/websites/runtimes/go\":     {},\n\t\"/websites/runtimes/python\": {},\n\t\"/websites/runtimes/dotnet\": {},\n\n\t\"/login\": {},\n\n\t\"/xpack\":                {},\n\t\"/xpack/waf/dashboard\":  {},\n\t\"/xpack/waf/global\":     {},\n\t\"/xpack/waf/websites\":   {},\n\t\"/xpack/waf/log\":        {},\n\t\"/xpack/waf/block\":      {},\n\t\"/xpack/waf/blackwhite\": {},\n\t\"/xpack/waf/stat\":       {},\n\n\t\"/xpack/monitor/dashboard\": {},\n\t\"/xpack/monitor/setting\":   {},\n\t\"/xpack/monitor/rank\":      {},\n\t\"/xpack/monitor/log\":       {},\n\t\"/xpack/monitor/trend\":     {},\n\t\"/xpack/monitor/websites\":  {},\n\n\t\"/xpack/tamper\":          {},\n\t\"/xpack/gpu\":             {},\n\t\"/xpack/alert/dashboard\": {},\n\t\"/xpack/alert/log\":       {},\n\t\"/xpack/alert/setting\":   {},\n\t\"/xpack/setting\":         {},\n\t\"/xpack/node/dashboard\":  {},\n\t\"/xpack/node\":            {},\n\t\"/xpack/simple-node\":     {},\n\t\"/xpack/exchange/file\":   {},\n\t\"/xpack/app\":             {},\n\t\"/xpack/app-upgrade\":     {},\n\n\t\"/xpack/cluster/mysql\":    {},\n\t\"/xpack/cluster/postgres\": {},\n\t\"/xpack/cluster/redis\":    {},\n}\n\nvar DynamicRoutes = []string{\n\t`^/containers/composeDetail/[^/]+$`,\n\t`^/databases/mysql/setting/[^/]+/[^/]+$`,\n\t`^/databases/postgresql/setting/[^/]+/[^/]+$`,\n\t`^/websites/[^/]+/config/[^/]+$`,\n}\n\nvar CertStore atomic.Value\n\nvar DaemonJsonPath = \"/etc/docker/daemon.json\"\n\nconst (\n\tRoleMaster = \"master\"\n\tRoleSlave  = \"slave\"\n)\n"
  },
  {
    "path": "core/constant/session.go",
    "content": "package constant\n\nconst (\n\tAuthMethodSession = \"session\"\n\tSessionName       = \"psession\"\n\n\tPasswordExpiredName = \"expired\"\n)\n"
  },
  {
    "path": "core/constant/status.go",
    "content": "package constant\n\nconst (\n\tStatusSuccess = \"Success\"\n\tStatusFailed  = \"Failed\"\n\n\t// node\n\tStatusWaiting        = \"Waiting\"\n\tStatusHealthy        = \"Healthy\"\n\tStatusStarting       = \"Starting\"\n\tStatusUnhealthy      = \"Unhealthy\"\n\tStatusWaitForUpgrade = \"WaitForUpgrade\"\n\tStatusUpgrading      = \"Upgrading\"\n\tStatusRunning        = \"Running\"\n\tStatusFree           = \"Free\"\n\tStatusBound          = \"Bound\"\n\tStatusExceptional    = \"Exceptional\"\n\tStatusRetrying       = \"Retrying\"\n\tStatusLost           = \"Lost\"\n\tStatusExecuting      = \"Executing\"\n\n\tStatusEnable  = \"Enable\"\n\tStatusDisable = \"Disable\"\n\tStatusMux     = \"Mux\"\n\n\tStatusInstalling = \"Installing\"\n\tStatusNormal     = \"Normal\"\n\tStatusDeleted    = \"Deleted\"\n\tStatusLoading    = \"Loading\"\n)\n"
  },
  {
    "path": "core/deps/keepdeps.go",
    "content": "//go:build !xpack\n\npackage deps\n\n// Keep xpack deps from being pruned by go mod tidy\nimport (\n\t_ \"github.com/patrickmn/go-cache\"\n\t_ \"github.com/pkg/sftp\"\n)\n"
  },
  {
    "path": "core/global/config.go",
    "content": "package global\n\ntype ServerConfig struct {\n\tBase      Base      `mapstructure:\"base\"`\n\tConn      Conn      `mapstructure:\"conn\"`\n\tLogConfig LogConfig `mapstructure:\"log\"`\n}\n\ntype Base struct {\n\tMode           string `mapstructure:\"mode\"`\n\tUsername       string `mapstructure:\"username\"`\n\tPassword       string `mapstructure:\"password\"`\n\tLanguage       string `mapstructure:\"language\"`\n\tIsDemo         bool   `mapstructure:\"is_demo\"`\n\tIsOffLine      bool   `mapstructure:\"is_offline\"`\n\tIsFxplay       bool   `mapstructure:\"is_fxplay\"`\n\tEdition        string `mapstructure:\"edition\"`\n\tVersion        string `mapstructure:\"version\"`\n\tInstallDir     string `mapstructure:\"install_dir\"`\n\tChangeUserInfo string `mapstructure:\"change_user_info\"`\n\tEncryptKey     string `mapstructure:\"encrypt_key\"`\n}\n\ntype Conn struct {\n\tPort        string `mapstructure:\"port\"`\n\tBindAddress string `mapstructure:\"bindAddress\"`\n\tIpv6        string `mapstructure:\"ipv6\"`\n\tSSL         string `mapstructure:\"ssl\"`\n\tEntrance    string `mapstructure:\"entrance\"`\n}\n\ntype ApiInterface struct {\n\tApiKey             string `mapstructure:\"api_key\"`\n\tApiInterfaceStatus string `mapstructure:\"api_interface_status\"`\n\tIpWhiteList        string `mapstructure:\"ip_white_list\"`\n\tApiKeyValidityTime string `mapstructure:\"api_key_validity_time\"`\n}\n\ntype LogConfig struct {\n\tLevel     string `mapstructure:\"level\"`\n\tTimeZone  string `mapstructure:\"timeZone\"`\n\tLogName   string `mapstructure:\"log_name\"`\n\tLogSuffix string `mapstructure:\"log_suffix\"`\n\tMaxBackup int    `mapstructure:\"max_backup\"`\n}\n"
  },
  {
    "path": "core/global/global.go",
    "content": "package global\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/init/auth\"\n\t\"github.com/1Panel-dev/1Panel/core/init/session/psession\"\n\t\"github.com/go-playground/validator/v10\"\n\t\"github.com/nicksnyder/go-i18n/v2/i18n\"\n\t\"github.com/robfig/cron/v3\"\n\t\"github.com/sirupsen/logrus\"\n\t\"github.com/spf13/viper\"\n\t\"gorm.io/gorm\"\n)\n\nvar (\n\tDB      *gorm.DB\n\tAlertDB *gorm.DB\n\tTaskDB  *gorm.DB\n\tAgentDB *gorm.DB\n\tLOG     *logrus.Logger\n\tCONF    ServerConfig\n\tApi     ApiInterface\n\tVALID   *validator.Validate\n\tSESSION *psession.PSession\n\tViper   *viper.Viper\n\n\tI18n       *i18n.Localizer\n\tI18nForCmd *i18n.Localizer\n\n\tCron *cron.Cron\n\n\tScriptSyncJobID cron.EntryID\n\n\tIPTracker *auth.IPTracker\n)\n\ntype DBOption func(*gorm.DB) *gorm.DB\n\nfunc RepoURL() string {\n\tif CONF.Base.Edition != \"intl\" {\n\t\treturn \"https://resource.fit2cloud.com/1panel/package/v2\"\n\t}\n\treturn \"https://resource.1panel.pro/v2\"\n}\nfunc ResourceURL() string {\n\tif CONF.Base.Edition != \"intl\" {\n\t\treturn \"https://resource.fit2cloud.com/1panel/resource/v2\"\n\t}\n\treturn \"https://resource.1panel.pro/v2/resource\"\n}\nfunc AppRepoURL() string {\n\tif CONF.Base.Edition != \"intl\" {\n\t\treturn \"https://apps-assets.fit2cloud.com\"\n\t}\n\treturn \"https://apps.1panel.pro\"\n}\n"
  },
  {
    "path": "core/go.mod",
    "content": "module github.com/1Panel-dev/1Panel/core\n\ngo 1.25.7\n\nrequire (\n\tgithub.com/1panel-dev/base64Captcha v1.3.8\n\tgithub.com/creack/pty v1.1.21\n\tgithub.com/fsnotify/fsnotify v1.7.0\n\tgithub.com/gin-contrib/gzip v1.0.1\n\tgithub.com/gin-gonic/gin v1.10.0\n\tgithub.com/glebarez/sqlite v1.11.0\n\tgithub.com/go-gormigrate/gormigrate/v2 v2.1.2\n\tgithub.com/go-playground/validator/v10 v10.22.0\n\tgithub.com/go-resty/resty/v2 v2.15.3\n\tgithub.com/go-webauthn/webauthn v0.15.0\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/gorilla/securecookie v1.1.2\n\tgithub.com/gorilla/sessions v1.4.0\n\tgithub.com/gorilla/websocket v1.5.3\n\tgithub.com/jinzhu/copier v0.4.0\n\tgithub.com/nicksnyder/go-i18n/v2 v2.4.0\n\tgithub.com/oschwald/maxminddb-golang v1.13.1\n\tgithub.com/patrickmn/go-cache v2.1.0+incompatible\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/pkg/sftp v1.13.6\n\tgithub.com/robfig/cron/v3 v3.0.1\n\tgithub.com/sirupsen/logrus v1.9.3\n\tgithub.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e\n\tgithub.com/soheilhy/cmux v0.1.5\n\tgithub.com/spf13/cobra v1.8.1\n\tgithub.com/spf13/viper v1.19.0\n\tgithub.com/swaggo/files/v2 v2.0.2\n\tgithub.com/swaggo/swag v1.16.3\n\tgithub.com/wader/gormstore/v2 v2.0.3\n\tgithub.com/xlzd/gotp v0.1.0\n\tgolang.org/x/crypto v0.45.0\n\tgolang.org/x/net v0.47.0\n\tgolang.org/x/sys v0.38.0\n\tgolang.org/x/term v0.37.0\n\tgolang.org/x/text v0.31.0\n\tgopkg.in/yaml.v2 v2.4.0\n\tgopkg.in/yaml.v3 v3.0.1\n\tgorm.io/gorm v1.30.0\n)\n\nrequire (\n\tgithub.com/KyleBanks/depth v1.2.1 // indirect\n\tgithub.com/PuerkitoBio/purell v1.1.1 // indirect\n\tgithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect\n\tgithub.com/bytedance/sonic v1.11.6 // indirect\n\tgithub.com/bytedance/sonic/loader v0.1.1 // indirect\n\tgithub.com/cloudwego/base64x v0.1.4 // indirect\n\tgithub.com/cloudwego/iasm v0.2.0 // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.9.0 // indirect\n\tgithub.com/gabriel-vasile/mimetype v1.4.3 // indirect\n\tgithub.com/gin-contrib/sse v0.1.0 // indirect\n\tgithub.com/glebarez/go-sqlite v1.22.0 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.19.5 // indirect\n\tgithub.com/go-openapi/jsonreference v0.19.6 // indirect\n\tgithub.com/go-openapi/spec v0.20.4 // indirect\n\tgithub.com/go-openapi/swag v0.19.15 // indirect\n\tgithub.com/go-playground/locales v0.14.1 // indirect\n\tgithub.com/go-playground/universal-translator v0.18.1 // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.4.0 // indirect\n\tgithub.com/go-webauthn/x v0.1.26 // indirect\n\tgithub.com/goccy/go-json v0.10.3 // indirect\n\tgithub.com/golang-jwt/jwt/v5 v5.3.0 // indirect\n\tgithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect\n\tgithub.com/google/go-tpm v0.9.6 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jinzhu/inflection v1.0.0 // indirect\n\tgithub.com/jinzhu/now v1.1.5 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.8 // indirect\n\tgithub.com/kr/fs v0.1.0 // indirect\n\tgithub.com/leodido/go-urn v1.4.0 // indirect\n\tgithub.com/magiconair/properties v1.8.7 // indirect\n\tgithub.com/mailru/easyjson v0.7.6 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mitchellh/mapstructure v1.5.0 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/ncruces/go-strftime v0.1.9 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.2.2 // indirect\n\tgithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect\n\tgithub.com/sagikazarmark/locafero v0.4.0 // indirect\n\tgithub.com/sagikazarmark/slog-shim v0.1.0 // indirect\n\tgithub.com/sourcegraph/conc v0.3.0 // indirect\n\tgithub.com/spf13/afero v1.11.0 // indirect\n\tgithub.com/spf13/cast v1.6.0 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/subosito/gotenv v1.6.0 // indirect\n\tgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirect\n\tgithub.com/ugorji/go/codec v1.2.12 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgo.uber.org/multierr v1.9.0 // indirect\n\tgolang.org/x/arch v0.8.0 // indirect\n\tgolang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect\n\tgolang.org/x/image v0.28.0 // indirect\n\tgolang.org/x/tools v0.38.0 // indirect\n\tgoogle.golang.org/protobuf v1.34.1 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/ini.v1 v1.67.0 // indirect\n\tgorm.io/driver/sqlite v1.4.4 // indirect\n\tmodernc.org/libc v1.66.1 // indirect\n\tmodernc.org/mathutil v1.7.1 // indirect\n\tmodernc.org/memory v1.11.0 // indirect\n\tmodernc.org/sqlite v1.38.0 // indirect\n)\n"
  },
  {
    "path": "core/go.sum",
    "content": "github.com/1panel-dev/base64Captcha v1.3.8 h1:GbQ2IuGMp4ai4erpwf4BMjm5eLC8Efb+dATVwgpPIII=\ngithub.com/1panel-dev/base64Captcha v1.3.8/go.mod h1:gVpwyGm9+g4rg3pXdYnsFAouP73qMOSBfnT3bxCFzco=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=\ngithub.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=\ngithub.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=\ngithub.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=\ngithub.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=\ngithub.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=\ngithub.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=\ngithub.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=\ngithub.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=\ngithub.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=\ngithub.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=\ngithub.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=\ngithub.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=\ngithub.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=\ngithub.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=\ngithub.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=\ngithub.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=\ngithub.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=\ngithub.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=\ngithub.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE=\ngithub.com/gin-contrib/gzip v1.0.1/go.mod h1:njt428fdUNRvjuJf16tZMYZ2Yl+WQB53X5wmhDwXvC4=\ngithub.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=\ngithub.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=\ngithub.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=\ngithub.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=\ngithub.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=\ngithub.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=\ngithub.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=\ngithub.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=\ngithub.com/go-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9zTgHSBoOOZ4CY=\ngithub.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=\ngithub.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=\ngithub.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=\ngithub.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=\ngithub.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=\ngithub.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=\ngithub.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=\ngithub.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=\ngithub.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=\ngithub.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=\ngithub.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=\ngithub.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=\ngithub.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=\ngithub.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=\ngithub.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=\ngithub.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=\ngithub.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/go-webauthn/webauthn v0.15.0 h1:LR1vPv62E0/6+sTenX35QrCmpMCzLeVAcnXeH4MrbJY=\ngithub.com/go-webauthn/webauthn v0.15.0/go.mod h1:hcAOhVChPRG7oqG7Xj6XKN1mb+8eXTGP/B7zBLzkX5A=\ngithub.com/go-webauthn/x v0.1.26 h1:eNzreFKnwNLDFoywGh9FA8YOMebBWTUNlNSdolQRebs=\ngithub.com/go-webauthn/x v0.1.26/go.mod h1:jmf/phPV6oIsF6hmdVre+ovHkxjDOmNH0t6fekWUxvg=\ngithub.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=\ngithub.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=\ngithub.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=\ngithub.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-tpm v0.9.6 h1:Ku42PT4LmjDu1H5C5ISWLlpI1mj+Zq7sPGKoRw2XROA=\ngithub.com/google/go-tpm v0.9.6/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=\ngithub.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=\ngithub.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=\ngithub.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=\ngithub.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=\ngithub.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=\ngithub.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=\ngithub.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=\ngithub.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=\ngithub.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=\ngithub.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=\ngithub.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=\ngithub.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=\ngithub.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=\ngithub.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=\ngithub.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=\ngithub.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=\ngithub.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=\ngithub.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=\ngithub.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=\ngithub.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=\ngithub.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=\ngithub.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=\ngithub.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=\ngithub.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=\ngithub.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=\ngithub.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=\ngithub.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=\ngithub.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=\ngithub.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=\ngithub.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=\ngithub.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=\ngithub.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=\ngithub.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=\ngithub.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=\ngithub.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=\ngithub.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=\ngithub.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=\ngithub.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=\ngithub.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=\ngithub.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=\ngithub.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=\ngithub.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=\ngithub.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=\ngithub.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=\ngithub.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=\ngithub.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=\ngithub.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=\ngithub.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=\ngithub.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=\ngithub.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=\ngithub.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=\ngithub.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=\ngithub.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=\ngithub.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=\ngithub.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=\ngithub.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=\ngithub.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=\ngithub.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=\ngithub.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=\ngithub.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=\ngithub.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=\ngithub.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=\ngithub.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=\ngithub.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=\ngithub.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=\ngithub.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=\ngithub.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=\ngithub.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=\ngithub.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=\ngithub.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=\ngithub.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=\ngithub.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=\ngithub.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=\ngithub.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=\ngithub.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=\ngithub.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=\ngithub.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=\ngithub.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=\ngithub.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=\ngithub.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=\ngithub.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=\ngithub.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=\ngithub.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=\ngithub.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=\ngithub.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=\ngithub.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngithub.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU=\ngithub.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=\ngithub.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=\ngithub.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=\ngithub.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=\ngithub.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=\ngithub.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=\ngithub.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=\ngithub.com/wader/gormstore/v2 v2.0.3 h1:/29GWPauY8xZkpLnB8hsp+dZfP3ivA9fiDw1YVNTp6U=\ngithub.com/wader/gormstore/v2 v2.0.3/go.mod h1:sr3N3a8F1+PBc3fHoKaphFqDXLRJ9Oe6Yow0HxKFbbg=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=\ngithub.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=\ngo.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=\ngo.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=\ngolang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=\ngolang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=\ngolang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=\ngolang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=\ngolang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=\ngolang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=\ngolang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=\ngolang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=\ngolang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=\ngolang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=\ngolang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=\ngolang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=\ngolang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=\ngolang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=\ngolang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=\ngoogle.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngorm.io/driver/mysql v1.4.0 h1:P+gpa0QGyNma39khn1vZMS/eXEJxTwHz4Q26NR4C8fw=\ngorm.io/driver/mysql v1.4.0/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=\ngorm.io/driver/postgres v1.4.1 h1:DutsKq2LK2Ag65q/+VygWth0/L4GAVOp+sCtg6WzZjs=\ngorm.io/driver/postgres v1.4.1/go.mod h1:whNfh5WhhHs96honoLjBAMwJGYEuA3m1hvgUbNXhPCw=\ngorm.io/driver/sqlite v1.4.1/go.mod h1:AKZZCAoFfOWHF7Nd685Iq8Uywc0i9sWJlzpoE/INzsw=\ngorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=\ngorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=\ngorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=\ngorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=\ngorm.io/gorm v1.23.10/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=\ngorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=\ngorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=\ngorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nmodernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=\nmodernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=\nmodernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=\nmodernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=\nmodernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=\nmodernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=\nmodernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=\nmodernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=\nmodernc.org/goabi0 v0.0.3 h1:y81b9r3asCh6Xtse6Nz85aYGB0cG3M3U6222yap1KWI=\nmodernc.org/goabi0 v0.0.3/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=\nmodernc.org/libc v1.66.1 h1:4uQsntXbVyAgrV+j6NhKvDiUypoJL48BWQx6sy9y8ok=\nmodernc.org/libc v1.66.1/go.mod h1:AiZxInURfEJx516LqEaFcrC+X38rt9G7+8ojIXQKHbo=\nmodernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=\nmodernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=\nmodernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=\nmodernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=\nmodernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=\nmodernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=\nmodernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=\nmodernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=\nmodernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=\nmodernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=\nmodernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=\nmodernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=\nmodernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=\nmodernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=\nnullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=\nrsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=\n"
  },
  {
    "path": "core/i18n/i18n.go",
    "content": "package i18n\n\nimport (\n\t\"embed\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"sync/atomic\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/nicksnyder/go-i18n/v2/i18n\"\n\t\"golang.org/x/text/language\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nconst defaultLang = \"en\"\n\nvar langFiles = map[string]string{\n\t\"zh\":      \"lang/zh.yaml\",\n\t\"en\":      \"lang/en.yaml\",\n\t\"zh-Hant\": \"lang/zh-Hant.yaml\",\n\t\"pt-BR\":   \"lang/pt-BR.yaml\",\n\t\"ja\":      \"lang/ja.yaml\",\n\t\"ru\":      \"lang/ru.yaml\",\n\t\"ms\":      \"lang/ms.yaml\",\n\t\"ko\":      \"lang/ko.yaml\",\n\t\"tr\":      \"lang/tr.yaml\",\n\t\"es-ES\":   \"lang/es-ES.yaml\",\n}\n\nfunc GetMsgWithMap(key string, maps map[string]interface{}) string {\n\tvar content string\n\tif maps == nil {\n\t\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID: key,\n\t\t})\n\t} else {\n\t\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID:    key,\n\t\t\tTemplateData: maps,\n\t\t})\n\t}\n\tcontent = strings.ReplaceAll(content, \": <no value>\", \"\")\n\tif content == \"\" {\n\t\treturn key\n\t} else {\n\t\treturn content\n\t}\n}\n\nfunc GetMsgWithDetail(key string, detail string) string {\n\tvar (\n\t\tcontent string\n\t\tdataMap = make(map[string]interface{})\n\t)\n\tdataMap[\"detail\"] = detail\n\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID:    key,\n\t\tTemplateData: dataMap,\n\t})\n\tif content != \"\" {\n\t\treturn content\n\t}\n\treturn key\n}\n\nfunc GetErrMsg(key string, maps map[string]interface{}) string {\n\tvar content string\n\tif maps == nil {\n\t\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID: key,\n\t\t})\n\t} else {\n\t\tcontent, _ = global.I18n.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID:    key,\n\t\t\tTemplateData: maps,\n\t\t})\n\t}\n\treturn content\n}\n\nfunc GetMsgByKey(key string) string {\n\tcontent, _ := global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID: key,\n\t})\n\treturn content\n}\n\nfunc Get(key string) string {\n\tcontent, _ := global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID: key,\n\t})\n\tif content != \"\" {\n\t\treturn content\n\t}\n\treturn key\n}\n\nfunc GetWithName(key string, name string) string {\n\tvar (\n\t\tdataMap = make(map[string]interface{})\n\t)\n\tdataMap[\"name\"] = name\n\tcontent, _ := global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID:    key,\n\t\tTemplateData: dataMap,\n\t})\n\treturn content\n}\n\nfunc GetWithNameAndErr(key string, name string, err error) string {\n\tvar (\n\t\tdataMap = make(map[string]interface{})\n\t)\n\tdataMap[\"name\"] = name\n\tdataMap[\"err\"] = err.Error()\n\tcontent, _ := global.I18n.Localize(&i18n.LocalizeConfig{\n\t\tMessageID:    key,\n\t\tTemplateData: dataMap,\n\t})\n\treturn content\n}\n\n//go:embed lang/*\nvar fs embed.FS\nvar bundle *i18n.Bundle\n\nfunc UseI18n() gin.HandlerFunc {\n\treturn func(context *gin.Context) {\n\t\tlang := context.GetHeader(\"Accept-Language\")\n\t\tif lang == \"\" {\n\t\t\tlang = GetLanguage()\n\t\t}\n\t\tglobal.I18n = i18n.NewLocalizer(bundle, lang)\n\t}\n}\n\nfunc Init() {\n\tif bundle == nil {\n\t\tinitBundle()\n\t}\n\tdbLang := getLanguageFromDBInternal()\n\tif dbLang == \"\" {\n\t\tdbLang = defaultLang\n\t}\n\tSetCachedDBLanguage(dbLang)\n\n\tglobal.I18n = i18n.NewLocalizer(bundle, dbLang)\n}\n\nfunc UseI18nForCmd(lang string) {\n\tif bundle == nil {\n\t\tinitBundle()\n\t}\n\tif lang == \"\" {\n\t\tlangFrom1pctl := getLanguageFrom1pctl()\n\t\tif langFrom1pctl == \"\" {\n\t\t\tlang = defaultLang\n\t\t} else {\n\t\t\tlang = langFrom1pctl\n\t\t}\n\t}\n\tglobal.I18nForCmd = i18n.NewLocalizer(bundle, lang)\n}\n\nfunc GetMsgByKeyForCmd(key string) string {\n\tif global.I18nForCmd == nil {\n\t\tUseI18nForCmd(\"\")\n\t}\n\tcontent, _ := global.I18nForCmd.Localize(&i18n.LocalizeConfig{\n\t\tMessageID: key,\n\t})\n\treturn content\n}\n\nfunc GetMsgWithMapForCmd(key string, maps map[string]interface{}) string {\n\tif global.I18nForCmd == nil {\n\t\tUseI18nForCmd(\"\")\n\t}\n\tvar content string\n\tif maps == nil {\n\t\tcontent, _ = global.I18nForCmd.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID: key,\n\t\t})\n\t} else {\n\t\tcontent, _ = global.I18nForCmd.Localize(&i18n.LocalizeConfig{\n\t\t\tMessageID:    key,\n\t\t\tTemplateData: maps,\n\t\t})\n\t}\n\tcontent = strings.ReplaceAll(content, \": <no value>\", \"\")\n\tif content == \"\" {\n\t\treturn key\n\t} else {\n\t\treturn content\n\t}\n}\n\nfunc getLanguageFromDBInternal() string {\n\tif global.DB == nil {\n\t\treturn defaultLang\n\t}\n\tlang, _ := repo.NewISettingRepo().GetValueByKey(\"Language\")\n\tif lang == \"\" {\n\t\treturn defaultLang\n\t}\n\treturn lang\n}\nfunc getLanguageFrom1pctl() string {\n\tcmd := exec.Command(\"bash\", \"-c\", \"grep '^LANGUAGE=' /usr/local/bin/1pctl | cut -d'=' -f2\")\n\tstdout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tinfo := strings.ReplaceAll(string(stdout), \"\\n\", \"\")\n\tif len(info) == 0 || info == `\"\"` {\n\t\tpanic(\"error `LANGUAGE` find in /usr/local/bin/1pctl\")\n\t}\n\treturn info\n}\n\nvar cachedDBLang atomic.Value\n\nfunc GetLanguage() string {\n\tif v := cachedDBLang.Load(); v != nil {\n\t\treturn v.(string)\n\t}\n\treturn defaultLang\n}\n\nfunc SetCachedDBLanguage(lang string) {\n\tif lang == \"\" {\n\t\tlang = defaultLang\n\t}\n\tcachedDBLang.Store(lang)\n}\n\nfunc initBundle() {\n\tbundle = i18n.NewBundle(language.Chinese)\n\tbundle.RegisterUnmarshalFunc(\"yaml\", yaml.Unmarshal)\n\n\tisSuccess := true\n\tfor _, file := range langFiles {\n\t\tif _, err := bundle.LoadMessageFileFS(fs, file); err != nil {\n\t\t\tfmt.Printf(\"[i18n] load language file %s failed: %v\\n\", file, err)\n\t\t\tisSuccess = false\n\t\t}\n\t}\n\n\tif !isSuccess {\n\t\tpanic(\"[i18n] failed to init language files, See log above for details\")\n\t}\n}\n"
  },
  {
    "path": "core/i18n/lang/en.yaml",
    "content": "ErrInvalidParams: \"Invalid request parameters: {{ .detail }}\"\nErrTokenParse: \"Token generation error: {{ .detail }}\"\nErrInitialPassword: \"Incorrect initial password\"\nErrInternalServer: \"Internal server error: {{ .detail }}\"\nErrRecordExist: \"Record already exists\"\nErrRecordNotFound: \"Record not found\"\nErrStructTransform: \"Type conversion failed: {{ .detail }}\"\nErrNotLogin: \"User not logged in: {{ .detail }}\"\nErrSessionDataNotFound: \"Session expired\"\nErrSessionDataFormat: \"Invalid session data format\"\nErrPasswordExpired: \"Password expired: {{ .detail }}\"\nErrNotSupportType: \"Unsupported type: {{ .detail }}\"\nErrProxy: \"Request failed; check this node status: {{ .detail }}\"\nErrApiConfigStatusInvalid: \"API access disabled: {{ .detail }}\"\nErrApiConfigKeyInvalid: \"Invalid API key: {{ .detail }}\"\nErrApiConfigIPInvalid: \"The API request IP is not on the whitelist: {{ .detail }}\"\nErrApiConfigDisable: \"This interface prohibits API calls: {{ .detail }}\"\nErrApiConfigKeyTimeInvalid: \"Invalid API timestamp: {{ .detail }}\"\nErrPasskeyDisabled: \"Passkey requires HTTPS to be enabled\"\nErrPasskeyNotConfigured: \"No passkey configured\"\nErrPasskeyLimit: \"Passkey limit reached (max 5)\"\nErrPasskeySession: \"Passkey session expired or invalid\"\nErrPasskeyDuplicate: \"Passkey already exists\"\nErrPasskeyVerify: \"Passkey verification failed\"\n\n# request\nErrNoSuchHost: \"Host resolution failed: {{ .err }}\"\nErrHttpReqNotFound: \"Requested resource not found: {{ .err }}\"\nErrHttpReqFailed: \"Request failed {{ .err }}\"\nErrHttpReqTimeOut: \"Request timed out {{ .err }}\"\nErrCreateHttpClient: \"Failed to create request {{ .err }}\"\nErrProxySetting: \"Proxy server information unavailable {{ .err }}, check and try again!\"\nErrEntranceFormat: \"Security entry {{ .name }} is not supported. Check and try again.\"\n\n# common\nErrDemoEnvironment: \"Demo server, this operation is prohibited!\"\nErrCmdTimeout: \"Command execution timeout!\"\nErrEntrance: \"Security entrance information error, check and try again!\"\nErrGroupIsDefault: \"Default group, unable to delete\"\nErrGroupIsInUse: \"The group is in use and cannot be deleted.\"\nErrLocalDelete: \"Cannot delete the local node!\"\nErrPortInUsed: \"The {{ .name }} port is already in use!\"\nErrInternalServerKey: \"Internal server error:\"\nMasterNode: \"Master Node\"\n\n# app\nCustomAppStoreFileValid: \"Application store package requires .tar.gz format\"\nErrFileNotFound: \"{{ .name }} file does not exist\"\nAppBackup: 'Application backup'\nAppBackupPush: 'Transfer application backup file {{.file}} to node {{ .name }}'\nErrSourceTargetSame: 'Source node and target node cannot be the same!'\nAppInstall: 'Install application {{ .name }} on node {{ .targetNode }}'\nAppInstallCheck: 'Check application installation environment'\n\n# backup\nErrBackupInUsed: \"This backup account is used in scheduled tasks and cannot be deleted\"\nErrBackupCheck: \"Backup account connection test failed {{ .err }}\"\nErrBackupLocal: \"Local server backup account does not support this operation!\"\nErrBackupPublic: \"Detected that this backup account is not public, check and try again!\"\nErrOSSConn: \"Unable to get the latest version. Check server internet connectivity.\"\n\n#license\nLicenseCheck: 'Check if the license is available'\nErrLicenseInUsed: 'The license is already bound. Check and try again!'\nErrLicenseExpired: 'The license has expired. Check and try again!'\nErrLicense: \"License format error, check and retry!\"\nErrLicenseCheck: \"License validation failed, check and retry!\"\nErrXpackVersion: \"License validation failed, this license is version-limited, cannot import, check and retry!\"\nErrLicenseSave: \"Failed to save license information, error {{ .err }}, retry!\"\nErrLicenseSync: \"License synchronization failed, no license info detected in database!\"\nErrLicenseExist: \"This license record already exists. You can directly go to the license page for node binding.\"\nErrXpackNotFound: \"This section requires Professional Edition. Import a license in Panel Settings - License\"\nErrXpackExceptional: \"This section requires Professional Edition. Sync license status in Panel Settings - License\"\nErrXpackLost: \"License reached max retry count. Go to [Panel Settings] [License] and run manual sync\"\nErrDeviceLost: \"Required files for license verification are missing, check and try again!\"\nErrDeviceErr: \"Current environment does not match the license import environment. Edit the license and re-import.\"\nErrXpackTimeout: \"Request timeout, network connection might be unstable, try again later!\"\nErrUnbindMaster: \"Nodes exist in node management. Remove nodes first, then unbind the current license.\"\nErrFreeNodeLimit: \"Community edition node limit reached. Go to www.lxware.cn/1panel to purchase and retry.\"\nErrNodeBound: \"This license is bound to another node, check and retry!\"\nErrNodeBoundDelete: \"This license is bound and does not support delete operations. Check and try again!\"\nErrNodeBoundLimit: \"The current free node has reached its limit, check and try again!\"\nErrLicenseFree: \"Free nodes are available only when the license is correctly bound to a node. Verify and retry.\"\nErrLicenseUnbind: \"Community Edition nodes detected for this license. Unbind in [Panel Settings - License] and retry.\"\nErrNoSuchNode: \"Node information not found, check and retry!\"\nErrNodeUnbind: \"This node is not within the license binding range, check and retry!\"\nErrNodeBind: \"This node is already bound to a license, check and retry!\"\nErrNodeLocalRollback: \"The primary node does not support direct rollback. Manually execute the '1pctl restore' command to rollback!\"\nErrIntlLicense: \"The current version does not support importing international licenses yet. Stay tuned!\"\n\nInvalidRequestBodyType: \"Invalid request body format. Ensure content matches the required format and retry.\"  \nInvalidLicenseCodeType: \"Invalid license code format provided, check and try again!\"  \nLicenseNotFoundType: \"License not found, no matching record exists in the system for the provided license. Check and try again!\"  \nLicenseRevokedType: \"The requested license has been revoked and is no longer usable. Check and try again!\"  \nLicenseExpiredType: \"License expired. Renew it or re-import it in Panel Settings - License before retrying.\"  \nLicenseProductMismatchType: \"The license does not match the requested product or service!\"  \nInvalidAssigneeType: \"Invalid target user or device information for license assignment. Check and try again!\"  \nLicenseUsageNotFoundType: \"No usage records found. This license has not been activated or used yet. Check and try again!\"  \nLicenseUsageLimitExceededType: \"This license is already bound to another node. Check and try again!\"  \n\n# alert\nErrAlertSync: \"Alert information sync error, check and retry!\"\n\n# task\nTaskStart: \"{{ .name }} task started [START]\"\nTaskEnd: \"{{ .name }} task ended [COMPLETED]\"\nTaskFailed: \"{{ .name }} task failed\"\nTaskTimeout: \"{{ .name }} timed out\"\nTaskSuccess: \"{{ .name }} task succeeded\"\nTaskRetry: \"Retry {{ .name }}\"\nSubTaskSuccess: \"{{ .name }} succeeded\"\nSubTaskFailed: \"{{ .name }} failed: {{ .err }}\"\nTaskInstall: \"Install\"\nTaskUpgrade: \"Upgrade\"\nTaskSync: 'Sync'\nTaskSyncForNode: \"Sync node data\"\nTaskBackup: \"Backup\"\nSuccessStatus: \"{{ .name }} succeeded\"\nFailedStatus: \"{{ .name }} failed {{ .err }}\"\nStart: \"Start\"\nSubTask: \"Subtask\"\nSkip: \"Ignore errors and continue\"\nPushAppInstallTaskToNode: \"Push app installation task to node [{{ .name }}]\"\nTaskPush: \"Push\"\nAppInstallTask: \"App installation task\"\nPushAppFailed: \"Failed to push app installation task\"\nSuccess: \"Success\"\n\n#script\nScriptLibrary: \"Script Library\"\nRemoteScriptLibrary: \"Remote Script Library\"\nScriptSyncSkip: \"Script library is already up to date\"\nDownloadData: \"Downloading script library file data.yaml\"\nDownloadPackage: \"Downloading script library package\"\nAnalyticCompletion: \"Analysis completed, now syncing to database...\"\n\nNode: \"Node\"\nSyncNode: \"Sync data to node {{ .name }}\"\nLocalName: \"'local' name is only used for system local identification\"\nSyncPackageData: \"Package sync data\"\nSyncPackageEncrypt: \"Data package encryption\"\nSyncRequest: \"Request node sync API\"\nSyncFailedRetry: \"Node data sync timeout (attempt {{ .index }}), retrying...\"\nSyncFailed: \"Sync failed, manually sync in the node list!\"\nSyncSystemProxy: \"System Proxy Settings\"\nSyncScripts: \"Script Library\"\nSyncBackupAccounts: \"Backup Accounts\"\nSyncAlertSetting: \"Alert Settings\"\nSyncCustomApp: \"Custom App\"\nSyncLanguage: \"System Language\"\nSyncEdition: \"Region\"\n\n#upgrade node\nNodeUpgrade: \"Upgrade node {{ .name }}\"\nUpgradeCheck: \"Check for node updates\"\nUpgradeCheckLocal: \"Local nodes do not support batch upgrades, skipping...\"\nUpgradeCheckLatest: \"The node is already the latest version, skipping...\"\nNewSSHClient: \"Initializing SSH connection\"\nBackupBeforeUpgrade: \"Backup data before upgrade\"\nUploadUpgradeFile: \"Distribute upgrade files\"\nRestartAfterUpgrade: \"Start service after upgrade\"\n\n#add node\nMasterData: \"Master Node Data\"\nLoadSftpClient: \"Load SFTP Client\"\nPackageMasterData: \"Generate master node backup package\"\nUploadBackup: \"Upload backup data\"\nMvBackup: \"Move data to backup directory\"\nTaskAddNode: \"Add node\"\nLoadNodeArch: \"Get node architecture info\"\nLoadNodeArchDetail: \"Detected master node architecture: {{ .local }}, child node architecture: {{ .node }}\"\nLoadNodeUpgradeDetail: \"Using the v1 version historical installation directory: {{ .baseDir }}, service listening port: {{ .port }}\"\nSyncAgentBaseInfo: \"Sync basic node info\"\nGenerateSSLInfo: \"Generate node SSL info\"\nConnInfoNotMatch: \"Connection info mismatch\"\nMakeAgentPackage: \"Generate node installation package\"\nSendAgent: \"Distribute node installation package\"\nStartService: \"Start service\"\nNoBackupNode: \"Backup node is empty. Select one and save first\"\n\n#cmd\nAppVersion: \"App version\"\nAppCommands: \"App related commands\"\nAppInit: \"Initialize app\"\nAppKeyVal: \"App key (only supports English)\"\nAppCreateFileErr: \"File {{ .name }} creation failed {{ .err }}\"\nAppCreateDirErr: \"Folder {{ .name }} creation failed {{ .err }}\"\nAppMissKey: \"App key missing, use -k to specify\"\nAppMissVersion: \"App version missing, use -v to specify\"\nAppVersionExist: \"Version already exists!\"\nAppCreateSuccessful: \"Creation successful!\"\nAppWriteErr: \"File {{ .name }} write failed {{ .err }}\"\nSudoHelper: \"Use {{ .cmd }} or switch to root user\"\nListenIPCommands: \"Switch listening ip\"\nListenIPv4: \"Listen on IPv4\"\nListenIPv6: \"Listen on IPv6\"\nListenChangeSuccessful: \"Switch successful! Now listening on {{ .value }}\"\nResetCommands: \"Reset system info\"\nResetMFA: \"Cancel 1Panel two-factor authentication\"\nResetHttps: \"Cancel 1Panel https login\"\nResetEntrance: \"Cancel 1Panel secure entrance\"\nResetIPs: \"Cancel 1Panel authorized ip restrictions\"\nResetDomain: \"Cancel 1Panel domain binding\"\nResetPasskey: \"Clear 1Panel passkeys\"\nRestoreCommands: \"Rollback 1Panel service and data\"\nRestoreNoSuchFile: \"No rollback files found\"\nRestoreStep1: \"(1/5) Starting rollback of 1Panel service and data from {{ .name }} directory...\"\nRestoreStep2: \"(2/5) 1Panel binary rollback successful\"\nRestoreStep3: \"(3/5) 1Panel script rollback successful\"\nRestoreStep4: \"(4/5) 1Panel service rollback successful\"\nRestoreStep5: \"(5/5) 1Panel data rollback successful\"\nRestoreSuccessful: \"Rollback succeeded. Reverted to {{ .version }}, restarting service...\"\nUpdateCommands: \"Update panel info\"\nUpdateUser: \"Update panel user\"\nUpdatePassword: \"Update panel password\"\nUpdatePort: \"Update panel port\"\nUpdateUserNull: \"Error: panel user is empty!\"\nUpdateUserBlank: \"Error: panel user contains spaces!\"\nUpdateUserFormat: \"Error: Invalid panel user format! Only supports English, Chinese, numbers, and _, length 3-30\"\nUpdateUserErr: \"Error: Failed to update panel user, {{ .err }}\"\nUpdateSuccessful: \"Update successful!\"\nUpdateUserResult: \"Panel user: {{ .name }}\"\nUpdatePasswordRead: \"Error: Failed to read panel password information, {{ .err }}\"\nUpdatePasswordNull: \"Error: Panel password is empty!\"\nUpdateUPasswordBlank: \"Error: Panel password contains spaces!\"\nUpdatePasswordFormat: \"Error: Panel password supports only letters, numbers, and special characters (!@#$%*_,.?) with a length of 8-30.\"\nUpdatePasswordLen: \"Error: Enter a password longer than 6 characters\"\nUpdatePasswordRe: \"Confirm password:\"\nUpdatePasswordErr: \"Error: Failed to update panel password, {{ .err }}\"\nUpdatePasswordSame: \"Error: The two passwords do not match, check and try again!\"\nUpdatePasswordResult: \"Panel password: {{ .name }}\"\nUpdatePortFormat: \"Error: The input port number must be between 1 and 65535!\"\nUpdatePortUsed: \"Error: The port number is already in use, check and try again!\"\nUpdatePortErr: \"Error: Failed to update panel port, {{ .err }}\"\nUpdatePortResult: \"Panel Port: {{ .name }}\"\nUpdatePortFirewallAdd: \"Failed to add firewall port rule, {{ .err }}, manually add the {{ .name }} port to the firewall rules.\"\nUpdatePortFirewallDel: \"Error: Failed to delete firewall port, {{ .err }}\"\nUpdatePortFirewallReload: \"Failed to reload the firewall, {{ .err }}, manually reload the firewall.\"\nUserInfo: \"Get panel information\"\nUserInfoAddr: \"Panel address: \"\nUserInfoPassHelp: \"Tip: To change the password, you can execute the command: \"\nDBConnErr: \"Error: Failed to initialize database connection, {{ .err }}\"\nSystemVersion: \"version: \"\nSystemMode: \"mode: \"\n\n#mobile app\nErrVerifyToken: 'Token verification failed. Reset and scan again.'\nErrInvalidToken: 'Invalid token. Reset and scan again.'\nErrExpiredToken: 'Token expired. Reset and scan again.'\n\n#cluster\nErrMasterDelete: \"Unable to delete the master node. Delete slave nodes first.\"\nClusterNameIsExist: \"Cluster name already exists.\"\nAppStatusUnHealthy: \"Failed to fetch application status. Check installation node status in the node list.\"\nMasterNodePortNotAvailable: \"Node {{ .name }} port {{ .port }} connectivity check failed. Check firewall/security group settings and master node status.\"\nClusterMasterNotExist: \"Cluster master node is disconnected. Delete child nodes.\"\n\n#ssl\nErrReqFailed: \"{{.name}} request failed: {{ .err }}\"\n\n#command\nName: \"Name\"\nCommand: \"Command\"\n"
  },
  {
    "path": "core/i18n/lang/es-ES.yaml",
    "content": "ErrInvalidParams: 'Solicitud inválida: {{ .detail }}'\nErrTokenParse: 'Token inválido: {{ .detail }}'\nErrInitialPassword: 'Contraseña original incorrecta'\nErrInternalServer: 'Error interno: {{ .detail }}'\nErrRecordExist: 'Registro ya existe'\nErrRecordNotFound: 'Registro no encontrado'\nErrStructTransform: 'Error de conversión: {{ .err }}'\nErrNotLogin: 'No has iniciado sesión: {{ .detail }}'\nErrSessionDataNotFound: \"La sesión actual ha caducado\"\nErrSessionDataFormat: \"El formato de los datos de la sesión actual es anómalo\"\nErrPasswordExpired: 'Contraseña expirada: {{ .detail }}'\nErrNotSupportType: 'Tipo no soportado: {{ .name }}'\nErrProxy: 'Solicitud fallida: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API desactivada: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Clave API inválida: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP no permitida: {{ .detail }}'\nErrApiConfigDisable: 'API no permitida: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Timestamp inválido: {{ .detail }}'\nPushAppInstallTaskToNode: \"Enviar tarea de instalación de aplicación al nodo [{{ .name }}]\"\nTaskPush: \"Enviar\"\nAppInstallTask: \"Tarea de instalación de aplicación\"\nPushAppFailed: \"Error al enviar tarea de instalación de aplicación\"\nSuccess: \"Éxito\"\n\n# request\nErrNoSuchHost: \"No se pudo encontrar el servidor solicitado {{ .err }}\"\nErrHttpReqNotFound: \"No se pudo encontrar el recurso solicitado {{ .err }}\"\nErrHttpReqFailed: \"Solicitud fallida {{ .err }}\"\nErrHttpReqTimeOut: \"La solicitud ha expirado {{ .err }}\"\nErrCreateHttpClient: \"Error al crear la solicitud {{ .err }}\"\nErrProxySetting: \"Información del servidor proxy no disponible {{ .err }}, compruebe e inténtelo de nuevo\"\nErrEntranceFormat: \"La entrada de seguridad {{ .name }} no está actualmente soportada. Por favor verifique e inténtelo de nuevo\"\n\n# common\nErrDemoEnvironment: 'No disponible en demo'\nErrCmdTimeout: 'Comando agotó tiempo'\nErrEntrance: \"Error en la información de entrada de seguridad, por favor revise e intente de nuevo\"\nErrGroupIsDefault: \"Grupo predeterminado, no se puede eliminar\"\nErrGroupIsInUse: \"El grupo está en uso y no se puede eliminar.\"\nErrLocalDelete: \"No se puede eliminar el nodo local\"\nErrPortInUsed: \"El puerto {{ .name }} ya está en uso\"\nErrInternalServerKey: \"Error interno del servidor:\"\nMasterNode: \"Nodo Maestro\"\n\n# app\nCustomAppStoreFileValid: \"El paquete de la tienda de aplicaciones debe tener formato .tar.gz\"\nErrFileNotFound: \"El archivo {{ .name }} no existe\"\nAppBackup: 'Copia de seguridad de aplicación'\nAppBackupPush: 'Transferir archivo de copia de seguridad de aplicación {{.file}} al nodo {{ .name }}'\nErrSourceTargetSame: 'El nodo de origen y el nodo de destino no pueden ser el mismo'\nAppInstall: 'Instalar aplicación {{ .name }} en nodo {{ .targetNode }}'\nAppInstallCheck: 'Verificar entorno de instalación de aplicación'\n\n# backup\nErrBackupInUsed: 'Cuenta de respaldo en uso por tarea programada'\nErrBackupCheck: 'Conexión de respaldo falló: {{ .err }}'\nErrBackupLocal: \"La cuenta de respaldo del servidor local no admite esta operación\"\nErrBackupPublic: \"Se detectó que esta cuenta de respaldo no es pública, verifique e intente de nuevo\"\nErrOSSConn: \"No se pudo obtener la última versión, por favor revise la conectividad de red externa del servidor.\"\n\n#license\nLicenseCheck: 'Comprobar si la licencia está disponible'\nErrLicenseInUsed: 'La licencia ya está vinculada. Verifique e intente de nuevo'\nErrLicenseExpired: 'La licencia ha expirado. Verifique e intente de nuevo'\nErrLicense: \"Error en el formato de la licencia, verifique y reintente\"\nErrLicenseCheck: \"Validación de licencia fallida, verifique y reintente\"\nErrXpackVersion: \"Validación de licencia fallida, esta licencia está limitada por versión, no se puede importar, verifique y reintente\"\nErrLicenseSave: \"Error al guardar la información de la licencia, error {{ .err }}, reintente\"\nErrLicenseSync: \"Sincronización de licencia fallida, no se detectó información de licencia en la base de datos\"\nErrLicenseExist: \"Este registro de licencia ya existe. Puede ir directamente a la página de licencia para la vinculación del nodo.\"\nErrXpackNotFound: \"Esta sección es para la edición profesional, importe la licencia en Panel > Ajustes > Licencia\"\nErrXpackExceptional: \"Esta sección es para la edición profesional, sincronice el estado de la licencia en Panel > Ajustes > Licencia\"\nErrXpackOutOfDate: \"La licencia actual ha expirado, importe nuevamente la licencia en Panel > Ajustes > Licencia\"\nErrXpackLost: \"La licencia ha alcanzado el número máximo de reintentos, vaya a [Ajustes del Panel] [Licencia] y haga clic en el botón de sincronización manualmente para asegurar el funcionamiento correcto de las funciones profesionales\"\nErrDeviceLost: \"Faltan archivos necesarios para la verificación de licencia, verifique e intente de nuevo\"\nErrDeviceErr: \"El entorno actual no coincide con el entorno de importación de la licencia. Edite la licencia e impórtela de nuevo\"\nErrXpackTimeout: \"Tiempo de espera de la solicitud, puede que la conexión de red sea inestable, intente más tarde\"\nErrUnbindMaster: \"Se detectaron nodos en la gestión de nodos, no se puede desvincular la licencia actual, elimine los nodos primero e intente de nuevo\"\nErrFreeNodeLimit: \"Se alcanzó el límite de nodos en la versión comunitaria, vaya a www.lxware.cn/1panel para comprar y reintentar\"\nErrNodeBound: \"Esta licencia está vinculada a otro nodo, verifique e intente de nuevo\"\nErrNodeBoundDelete: \"Esta licencia está vinculada y no admite operaciones de eliminación. Verifique e intente de nuevo\"\nErrNodeBoundLimit: \"El nodo gratuito actual ha alcanzado su límite, verifique e intente de nuevo\"\nErrLicenseFree: \"Los nodos gratuitos solo pueden usarse si la licencia está correctamente vinculada a un nodo. Verifique e intente de nuevo\"\nErrLicenseUnbind: \"Se detectaron nodos de la Edición Comunitaria para esta licencia. Desvincule en [Panel > Ajustes > Licencia] y vuelva a intentarlo\"\nErrNoSuchNode: \"Información del nodo no encontrada, verifique e intente de nuevo\"\nErrNodeUnbind: \"Este nodo no está dentro del rango de vinculación de la licencia, verifique e intente de nuevo\"\nErrNodeBind: \"Este nodo ya está vinculado a una licencia, verifique e intente de nuevo\"\nErrNodeLocalRollback: \"El nodo principal no admite la reversión directa. Ejecute manualmente el comando '1pctl restore' para revertir\"\nErrIntlLicense: \"La versión actual aún no admite importar licencias de la edición internacional. Próximamente\"\nInvalidRequestBodyType: \"Formato del cuerpo de la solicitud no válido, por favor revisa y asegúrate de que el contenido cumpla con el formato requerido antes de reintentar.\"\nInvalidLicenseCodeType: \"Formato de código de licencia no válido, por favor revisa e inténtalo de nuevo.\"\nLicenseNotFoundType: \"Licencia no encontrada, no existe ningún registro coincidente en el sistema para la licencia proporcionada. Por favor revisa e inténtalo de nuevo.\"\nLicenseRevokedType: \"La licencia solicitada ha sido revocada y ya no se puede utilizar. Por favor revisa e inténtalo de nuevo.\"\nLicenseExpiredType: \"La licencia ha expirado. Renuévala o vuelve a importarla en Configuración del Panel - Sección de Licencia antes de reintentar.\"\nLicenseProductMismatchType: \"La licencia no corresponde con el producto o servicio solicitado.\"\nInvalidAssigneeType: \"Información de usuario o dispositivo de destino no válida para la asignación de licencia. Por favor revisa e inténtalo de nuevo.\"\nLicenseUsageNotFoundType: \"No se encontraron registros de uso. Esta licencia no ha sido activada o utilizada aún. Por favor revisa e inténtalo de nuevo.\"\nLicenseUsageLimitExceededType: \"Esta licencia ya está vinculada a otro nodo. Por favor revisa e inténtalo de nuevo.\"\n\n# alert\nErrAlertSync: \"Error al sincronizar la información de alertas, verifique e intente de nuevo\"\n\n# task\nTaskStart: \"Tarea {{ .name }} iniciada [INICIO]\"\nTaskEnd: \"Tarea {{ .name }} finalizada [COMPLETADA]\"\nTaskFailed: \"Tarea {{ .name }} fallida\"\nTaskTimeout: \"{{ .name }} ha expirado\"\nTaskSuccess: \"Tarea {{ .name }} terminada con éxito\"\nTaskRetry: \"Iniciando reintento {{ .name }}\"\nSubTaskSuccess: \"{{ .name }} completada correctamente\"\nSubTaskFailed: \"{{ .name }} falló: {{ .err }}\"\nTaskInstall: \"Instalar\"\nTaskUpgrade: \"Actualizar\"\nTaskSync: 'Sincronizar'\nTaskSyncForNode: \"Sincronizar Datos del Nodo\"\nTaskBackup: \"Respaldar\"\nSuccessStatus: \"{{ .name }} correcta\"\nFailedStatus: \"{{ .name }} fallida {{ .err }}\"\nStart: \"Iniciar\"\nSubTask: \"Subtarea\"\nSkip: \"Omitir errores y continuar...\"\n\n#script\nScriptLibrary: \"Biblioteca de scripts\"\nRemoteScriptLibrary: \"Biblioteca de Scripts Remota\"\nScriptSyncSkip: \"La biblioteca de scripts ya está en la última versión\"\nDownloadData: \"Descargando archivo de datos de la biblioteca de scripts data.yaml\"\nDownloadPackage: \"Descargando paquete de la biblioteca de scripts\"\nAnalyticCompletion: \"Análisis completado, sincronizando con la base de datos...\"\n\nNode: \"Nodo\"\nSyncNode: \"Sincronizar datos del nodo\"\nLocalName: \"El nombre 'local' solo se utiliza para la identificación local del sistema\"\nSyncPackageData: \"Sincronizar datos del paquete [{{ .detail }}]\"\nSyncPackageEncrypt: \"Encriptación del paquete de datos\"\nSyncRequest: \"Solicitar API de sincronización de nodos\"\nSyncFailedRetry: \"Tiempo de espera en la sincronización de datos del nodo (intento {{ .index }}), reintentando...\"\nSyncFailed: \"La sincronización falló, sincronice manualmente en la lista de nodos\"\nSyncSystemProxy: \"Configuración del proxy del sistema\"\nSyncScripts: \"Biblioteca de scripts\"\nSyncBackupAccounts: \"Cuentas de copia de seguridad\"\nSyncAlertSetting: \"Configuración de alertas\"\nSyncCustomApp: \"Aplicación personalizada\"\nSyncLanguage: \"Idioma del sistema\"\nSyncEdition: \"Región\"\n\n#upgrade node\nNodeUpgrade: \"Actualizar nodo {{ .name }}\"\nUpgradeCheck: \"Comprobar actualizaciones de nodos\"\nUpgradeCheckLocal: \"Los nodos locales no admiten actualizaciones en lote, omitiendo...\"\nUpgradeCheckLatest: \"El nodo ya está en la última versión, omitiendo...\"\nNewSSHClient: \"Inicializando conexión SSH\"\nBackupBeforeUpgrade: \"Respaldar datos antes de actualizar\"\nUploadUpgradeFile: \"Distribuir archivos de actualización\"\nRestartAfterUpgrade: \"Iniciar servicio después de actualizar\"\n\n#add node\nMasterData: \"Datos del nodo principal\"\nLoadSftpClient: \"Cargar cliente SFTP\"\nPackageMasterData: \"Generar paquete de respaldo del nodo principal\"\nUploadBackup: \"Subir datos de respaldo\"\nMvBackup: \"Mover datos al directorio de respaldos\"\nTaskAddNode: \"Agregar nodo\"\nLoadNodeArch: \"Obtener información de arquitectura del nodo\"\nLoadNodeArchDetail: \"Arquitectura detectada - principal: {{ .local }}, nodo secundario: {{ .node }}\"\nLoadNodeUpgradeDetail: \"Usando el directorio de instalación histórico versión v1: {{ .baseDir }}, puerto de escucha del servicio: {{ .port }}\"\nSyncAgentBaseInfo: \"Sincronizar información básica del nodo\"\nGenerateSSLInfo: \"Generar información SSL del nodo\"\nConnInfoNotMatch: \"La información de conexión no coincide\"\nMakeAgentPackage: \"Generar paquete de instalación del nodo\"\nSendAgent: \"Distribuir paquete de instalación del nodo\"\nStartService: \"Iniciar servicio\"\nNoBackupNode: \"Actualmente no hay nodo de respaldo. Seleccione uno para guardar e intente de nuevo.\"\n\n#cmd\nAppVersion: \"Versión de la aplicación\"\nAppCommands: \"Comandos relacionados con la app\"\nAppInit: \"Inicializar aplicación\"\nAppKeyVal: \"Clave de la app (solo admite inglés)\"\nAppCreateFileErr: \"Error al crear el archivo {{ .name }} {{ .err }}\"\nAppCreateDirErr: \"Error al crear la carpeta {{ .name }} {{ .err }}\"\nAppMissKey: \"Falta la clave de la app, use -k para especificar\"\nAppMissVersion: \"Falta la versión de la app, use -v para especificar\"\nAppVersionExist: \"La versión ya existe\"\nAppCreateSuccessful: \"Creación finalizada con éxito\"\nAppWriteErr: \"Error al escribir el archivo {{ .name }} {{ .err }}\"\nSudoHelper: \"Por favor use {{ .cmd }} o cambie al usuario root\"\nListenIPCommands: \"Cambiar ip de escucha\"\nListenIPv4: \"Escuchar en IPv4\"\nListenIPv6: \"Escuchar en IPv6\"\nListenChangeSuccessful: \"Cambio exitoso Ahora escuchando en {{ .value }}\"\nResetCommands: \"Restablecer información del sistema\"\nResetMFA: \"Cancelar autenticación de dos factores de 1Panel\"\nResetHttps: \"Cancelar inicio de sesión https de 1Panel\"\nResetEntrance: \"Cancelar entrada segura de 1Panel\"\nResetIPs: \"Cancelar restricciones de IP autorizadas de 1Panel\"\nResetDomain: \"Cancelar vinculación de dominio de 1Panel\"\nResetPasskey: \"Eliminar passkeys de 1Panel\"\nRestoreCommands: \"Revertir servicio y datos de 1Panel\"\nRestoreNoSuchFile: \"No hay archivos disponibles para revertir\"\nRestoreStep1: \"(1/5) Iniciando reversión del servicio y datos de 1Panel desde el directorio {{ .name }}...\"\nRestoreStep2: \"(2/5) Reversión de binarios de 1Panel finalizada con éxito\"\nRestoreStep3: \"(3/5) Reversión de scripts de 1Panel finalizada con éxito\"\nRestoreStep4: \"(4/5) Reversión de servicio de 1Panel finalizada con éxito\"\nRestoreStep5: \"(5/5) Reversión de datos de 1Panel finalizada con éxito\"\nRestoreSuccessful: \"Reversión exitosa Revertido a {{ .version }}, reiniciando servicio, espere por favor...\"\nUpdateCommands: \"Actualizar información del panel\"\nUpdateUser: \"Actualizar usuario del panel\"\nUpdatePassword: \"Actualizar contraseña del panel\"\nUpdatePort: \"Actualizar puerto del panel\"\nUpdateUserNull: \"Error: el usuario del panel está vacío\"\nUpdateUserBlank: \"Error: el usuario del panel contiene espacios\"\nUpdateUserFormat: \"Error: formato de usuario del panel inválido Solo admite inglés, chino, números y _, longitud 3-30\"\nUpdateUserErr: \"Error: Fallo al actualizar usuario del panel, {{ .err }}\"\nUpdateSuccessful: \"Actualización finalizada con éxito\"\nUpdateUserResult: \"Usuario del panel: {{ .name }}\"\nUpdatePasswordRead: \"Error: Fallo al leer información de la contraseña del panel, {{ .err }}\"\nUpdatePasswordNull: \"Error: la contraseña del panel está vacía\"\nUpdateUPasswordBlank: \"Error: la contraseña del panel contiene espacios\"\nUpdatePasswordFormat: \"Error: La contraseña del panel solo admite letras, números y caracteres especiales (@#$%*_,.?), con una longitud de 8-30.\"\nUpdatePasswordLen: \"Error: Ingrese una contraseña mayor a 6 caracteres\"\nUpdatePasswordRe: \"Confirmar contraseña:\"\nUpdatePasswordErr: \"Error: Fallo al actualizar la contraseña del panel, {{ .err }}\"\nUpdatePasswordSame: \"Error: Las dos contraseñas no coinciden, verifique e intente de nuevo\"\nUpdatePasswordResult: \"Contraseña del panel: {{ .name }}\"\nUpdatePortFormat: \"Error: El puerto debe estar entre 1 y 65535\"\nUpdatePortUsed: \"Error: El puerto ya está en uso, verifique e intente de nuevo\"\nUpdatePortErr: \"Error: Fallo al actualizar el puerto del panel, {{ .err }}\"\nUpdatePortResult: \"Puerto del panel: {{ .name }}\"\nUpdatePortFirewallAdd: \"Fallo al agregar la regla de puerto al firewall, {{ .err }}, agregue manualmente el puerto {{ .name }} al firewall.\"\nUpdatePortFirewallDel: \"Error: Fallo al eliminar el puerto del firewall, {{ .err }}\"\nUpdatePortFirewallReload: \"Fallo al recargar el firewall, {{ .err }}, recargue el firewall manualmente.\"\nUserInfo: \"Obtener información del panel\"\nUserInfoAddr: \"Dirección del panel: \"\nUserInfoPassHelp: \"Consejo: para cambiar la contraseña, ejecute el comando: \"\nDBConnErr: \"Error: Fallo al inicializar la conexión a la base de datos, {{ .err }}\"\nSystemVersion: \"versión: \"\nSystemMode: \"modo: \"\n\n#mobile app\nErrVerifyToken: 'Error al verificar el token, por favor restablezca y escanee de nuevo.'\nErrInvalidToken: 'Token inválido, por favor restablezca y escanee de nuevo.'\nErrExpiredToken: 'El token ha expirado, por favor restablezca y escanee de nuevo.'\n\n#cluster\nErrMasterDelete: \"No se puede eliminar el nodo principal, elimine primero los nodos secundarios.\"\nClusterNameIsExist: \"El nombre del clúster ya existe.\"\nAppStatusUnHealthy: \"Adquisición anormal del estado de la aplicación, revise el estado del nodo en la lista de nodos.\"\nMasterNodePortNotAvailable: \"Verificación de conectividad del puerto {{ .port }} del nodo {{ .name }} fallida, verifique la configuración del firewall/grupo de seguridad y el estado del nodo principal.\"\nClusterMasterNotExist: \"El nodo principal del clúster está desconectado, elimine los nodos secundarios.\"\n\n#ssl\nErrReqFailed: \"{{.name}} petición fallida: {{ .err }}\"\n\n#command\nName: \"Nombre\"\nCommand: \"Comando\"\n"
  },
  {
    "path": "core/i18n/lang/ja.yaml",
    "content": "ErrInvalidParams: '不正なリクエスト: {{ .detail }}'\nErrTokenParse: 'トークンエラー: {{ .detail }}'\nErrInitialPassword: '元のパスワードが違います'\nErrInternalServer: '内部エラー: {{ .detail }}'\nErrRecordExist: 'レコードが既に存在します'\nErrRecordNotFound: 'レコードが見つかりません'\nErrStructTransform: '型変換エラー: {{ .err }}'\nErrNotLogin: 'ログインしていません: {{ .detail }}'\nErrSessionDataNotFound: \"現在のセッションは期限切れです！\"\nErrSessionDataFormat: \"現在のセッションデータ形式が異常です！\"\nErrPasswordExpired: 'パスワード期限切れ: {{ .detail }}'\nErrNotSupportType: '未対応のタイプ: {{ .name }}'\nErrProxy: 'リクエストに失敗しました: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API利用不可: {{ .detail }}'\nErrApiConfigKeyInvalid: 'APIキーが無効です: {{ .detail }}'\nErrApiConfigIPInvalid: 'IPが許可されていません: {{ .detail }}'\nErrApiConfigDisable: 'APIは無効です: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'タイムスタンプが無効です: {{ .detail }}'\n\n# request\nErrNoSuchHost: \"要求されたサーバーが見つかりません {{ .err }}\"\nErrHttpReqNotFound: \"要求されたリソースが見つかりません {{ .err }}\"\nErrHttpReqFailed: \"リクエスト失敗 {{ .err }}\"\nErrHttpReqTimeOut: \"リクエストタイムアウト {{ .err }}\"\nErrCreateHttpClient: \"リクエストの作成に失敗しました {{ .err }}\"\nErrProxySetting: \"プロキシサーバー情報が利用できません {{ .err }}、確認して再試行してください！\"\nErrEntranceFormat: \"セキュリティエントランス {{ .name }} は現在サポートされていません。確認して再試行してください！\"\n\n# common\nErrDemoEnvironment: 'デモ環境では利用できません'\nErrCmdTimeout: 'コマンドがタイムアウトしました'\nErrEntrance: \"セキュリティ情報エラー、再確認してください！\"\nErrGroupIsDefault: \"デフォルトグループの削除はできません\"\nErrGroupIsInUse: \"グループは使用中のため、削除できません。\"\nErrLocalDelete: \"ローカルノードは削除できません！\"\nErrPortInUsed: \"{{ .name }} ポートはすでに使用されています！\"\nErrInternalServerKey: \"サーバー内部エラー: \"\nMasterNode: \"マスターノード\"\n\n# app\nCustomAppStoreFileValid: \"アプリストアパッケージは .tar.gz 形式である必要があります\"\nErrFileNotFound: \"{{ .name }} ファイルが存在しません\"\nAppBackup: 'アプリケーションバックアップ'\nAppBackupPush: 'アプリケーションバックアップファイル {{.file}} をノード {{ .name }} に転送'\nErrSourceTargetSame: 'ソースノードとターゲットノードは同じにできません！'\nAppInstall: 'ノード {{ .targetNode }} にアプリケーション {{ .name }} をインストール'\nAppInstallCheck: 'アプリケーションインストール環境を確認'\n\n# backup\nErrBackupInUsed: 'バックアップアカウントがスケジュールで使用中'\nErrBackupCheck: '接続テストに失敗しました: {{ .err }}'\nErrBackupLocal: \"ローカルサーバーバックアップアカウントはこの操作をサポートしていません！\"\nErrBackupPublic: \"このバックアップアカウントが公開されていないと検出されました。再確認してください！\"\nErrOSSConn: \"最新バージョンを取得できません。外部ネットワーク接続を確認してください。\"\n\n\n#license\nLicenseCheck: 'ライセンスが利用可能か確認する'\nErrLicenseInUsed: 'このライセンスは既に紐付けられています。確認して再試行してください！'\nErrLicenseExpired: 'このライセンスは期限切れです。確認して再試行してください！'\nErrLicense: \"ライセンスフォーマットエラー、確認して再試行してください\"\nErrLicenseCheck: \"ライセンスの確認に失敗しました、確認して再試行してください\"\nErrXpackVersion: \"ライセンスの確認に失敗しました、このライセンスはバージョン制限があり、正常にインポートできません、確認して再試行してください\"\nErrLicenseSave: \"ライセンス情報の保存に失敗しました、エラー {{ .err }}、再試行してください\"\nErrLicenseSync: \"ライセンス情報の同期に失敗しました、データベースにライセンス情報が検出されませんでした\"\nErrLicenseExist: \"このライセンス記録は既に存在します。ライセンスページで直接ノードの紐付けが可能です。\"\nErrXpackNotFound: \"この部分はプロフェッショナル版機能です、まず「パネル設定-ライセンス」画面でライセンスをインポートしてください\"\nErrXpackExceptional: \"この部分はプロフェッショナル版機能です、まず「パネル設定-ライセンス」画面でライセンス状態を同期してください\"\nErrXpackLost: \"ライセンスの最大試行回数に達しました、「パネル設定」「ライセンス」ページに移動し、手動で同期ボタンをクリックして、プロフェッショナル版機能が正常に使用できることを確認してください\"\nErrDeviceLost: \"ライセンス検証に必要なファイルが失われました。確認して再試行してください！\"\nErrDeviceErr: \"現在の環境とライセンスのインポート環境が一致しません。ライセンスを編集して再度インポートしてください！\"\nErrXpackTimeout: \"リクエストタイムアウト、ネットワーク接続が不安定な可能性があります。後で再試行してください\"\nErrUnbindMaster: \"ノード管理にノードが存在するため、現在のライセンスを解除できません。ノードを削除してから再試行してください\"\nErrFreeNodeLimit: \"コミュニティ版のノード数が無料上限に達しました、www.lxware.cn/1panel で購入して再試行してください\"\nErrNodeBound: \"このライセンスは他のノードにバインドされています、確認して再試行してください\"\nErrNodeBoundDelete: \"このライセンスはバインドされており、削除操作はサポートされていません。確認して再試行してください！\"\nErrNodeBoundLimit: \"現在の無料ノードは上限に達しました。確認して再試行してください！\"\nErrLicenseFree: \"ライセンスがノードに正常にバインドされている場合のみ、その無料ノードを使用できます。確認して再試行してください！\"\nErrLicenseUnbind: \"このライセンスにコミュニティ版ノードが存在します。[パネル設定 - ライセンス]でバインド解除してから再試行してください！\"\nErrNoSuchNode: \"そのノード情報が見つかりませんでした、確認して再試行してください\"\nErrNodeUnbind: \"そのノードはライセンスのバインド範囲内ではありません、確認して再試行してください\"\nErrNodeBind: \"そのノードはライセンスにバインドされています、確認して再試行してください\"\nErrNodeLocalRollback: \"マスターノードは直接ロールバックをサポートしていません。手動で「1pctl restore」コマンドを実行してロールバックしてください！\"\nErrIntlLicense: \"現在のバージョンでは国際版ライセンスのインポートはまだサポートされていません。しばらくお待ちください！\"\n\nInvalidRequestBodyType: \"リクエストボディの形式が無効です。内容が要求された形式に準拠しているか確認してから再試行してください！\"\nInvalidLicenseCodeType: \"提供されたライセンスコードの形式が無効です。確認してから再試行してください！\"\nLicenseNotFoundType: \"ライセンスが見つかりません。提供されたライセンスに一致する記録がシステム内に存在しません。確認してから再試行してください！\"\nLicenseRevokedType: \"リクエストされたライセンスは廃止されており、使用できません。確認してから再試行してください！\"\nLicenseExpiredType: \"ライセンスの有効期限が切れています。更新するか、パネル設定 - ライセンス 画面で再インポートしてから再試行してください！\"\nLicenseProductMismatchType: \"ライセンスがリクエストされた製品またはサービスと一致しません！\"\nInvalidAssigneeType: \"ライセンス割り当ての対象ユーザーまたはデバイス情報が無効です。確認してから再試行してください！\"\nLicenseUsageNotFoundType: \"使用記録がありません。このライセンスはまだ有効化または使用されていません。確認してから再試行してください！\"\nLicenseUsageLimitExceededType: \"このライセンスは既に他のノードにバインドされています。確認してから再試行してください！\"\n\n# alert\nErrAlertSync: \"アラート情報の同期エラーです。後で再試行してください！\"\n\n#task\nTaskStart: \"{{ .name }} タスク開始 [START]\"\nTaskEnd: \"{{ .name }} タスク終了 [COMPLETED]\"\nTaskFailed: \"{{ .name }} タスク失敗\"\nTaskTimeout: \"{{ .name }} タイムアウト\"\nTaskSuccess: \"{{ .name }} タスク成功\"\nTaskRetry: \"第 {{ .name }} 回リトライ開始\"\nSubTaskSuccess: \"{{ .name }} 成功\"\nSubTaskFailed: \"{{ .name }} 失敗: {{ .err }}\"\nTaskInstall: \"インストール\"\nTaskUpgrade: \"アップグレード\"\nTaskSync: '同期'\nTaskSyncForNode: \"ノードデータを同期\"\nTaskBackup: \"バックアップ\"\nSuccessStatus: \"{{ .name }} 成功\"\nFailedStatus: \"{{ .name }} 失敗 {{ .err }}\"\nStart: \"開始\"\nSubTask: \"サブタスク\"\nSkip: \"エラーを無視して続行...\"\nPushAppInstallTaskToNode: \"アプリインストールタスクをノード [{{ .name }}] にプッシュ\"\nTaskPush: \"プッシュ\"\nAppInstallTask: \"アプリインストールタスク\"\nPushAppFailed: \"アプリインストールタスクのプッシュに失敗しました\"\nSuccess: \"成功\"\n\n#script\nScriptLibrary: \"スクリプトライブラリ\"\nRemoteScriptLibrary: \"リモートスクリプトライブラリ\"\nScriptSyncSkip: \"現在のスクリプトライブラリは最新バージョンです！\"\nDownloadData: \"スクリプトライブラリファイル data.yaml をダウンロード中\"\nDownloadPackage: \"スクリプトライブラリパッケージをダウンロード中\"\nAnalyticCompletion: \"解析が完了しました、データベースに同期しています...\"\n\nNode: \"ノード\"\nSyncNode: \"データをノード {{ .name }} に同期\"\nLocalName: \"'local'名称はシステムのローカル識別のみに使用されます\"\nSyncPackageData: \"同期データのパッケージ化\"\nSyncPackageEncrypt: \"データパッケージの暗号化\"\nSyncRequest: \"ノード同期APIをリクエスト\"\nSyncFailedRetry: \"ノードデータ同期タイムアウト ({{ .index }}回目)、再試行中...\"\nSyncFailed: \"同期に失敗しました、ノードリストで手動同期してください！\"\nSyncSystemProxy: \"システムプロキシ設定\"\nSyncScripts: \"スクリプトライブラリ\"\nSyncBackupAccounts: \"バックアップアカウント\"\nSyncAlertSetting: \"アラート設定\"\nSyncCustomApp: \"カスタムアプリ\"\nSyncLanguage: \"システム言語\"\nSyncEdition: \"実行リージョン\"\n\n#upgrade node\nNodeUpgrade: \"{{ .name }} ノードのアップグレード\"\nUpgradeCheck: \"ノードの更新を確認\"\nUpgradeCheckLocal: \"ローカルノードは一括アップグレードをサポートしていないため、スキップします...\"\nUpgradeCheckLatest: \"ノードは既に最新バージョンです、スキップします...\"\nNewSSHClient: \"SSH接続の初期化\"\nBackupBeforeUpgrade: \"アップグレード前にデータをバックアップ\"\nUploadUpgradeFile: \"アップグレードに必要なファイルを配布\"\nRestartAfterUpgrade: \"アップグレード後にサービスを起動\"\n\n#add node\nMasterData: \"マスターノードデータ\"\nLoadSftpClient: \"SFTPクライアントの取得\"\nPackageMasterData: \"マスターノードのバックアップパッケージを生成\"\nUploadBackup: \"バックアップデータをアップロード\"\nMvBackup: \"データをバックアップディレクトリに移動\"\nTaskAddNode: \"ノードを追加\"\nLoadNodeArch: \"ノードアーキテクチャ情報を取得\"\nLoadNodeArchDetail: \"検出されたマスターノードアーキテクチャ: {{ .local }}, 子ノードアーキテクチャ: {{ .node }}\"\nLoadNodeUpgradeDetail: \"v1 バージョンの履歴インストールディレクトリを使用しています: {{ .baseDir }}、サービスリスニングポート: {{ .port }}\"\nSyncAgentBaseInfo: \"ノードの基本情報を同期\"\nGenerateSSLInfo: \"ノードのSSL情報を生成\"\nConnInfoNotMatch: \"接続情報が一致しません\"\nMakeAgentPackage: \"ノードインストールパッケージを生成\"\nSendAgent: \"ノードインストールパッケージを送信\"\nStartService: \"サービスを開始\"\nNoBackupNode: \"現在バックアップノードが空です。保存するバックアップノードを選択して再試行してください！\"\n\n#cmd\nAppVersion: \"アプリバージョン\"\nAppCommands: \"アプリ関連のコマンド\"\nAppInit: \"アプリを初期化します\"\nAppKeyVal: \"アプリキー（英語のみをサポートする）\"\nAppCreateFileErr: \"file {{ .name }}作成に失敗した{{ .err }}\"\nAppCreateDirErr: \"Folder {{ .name }}作成に失敗した{{ .err }}\"\nAppMissKey: \"アプリキーがありません、-kを使用して指定します\"\nAppMissVersion: \"アプリバージョンがありません、-vを使用して指定します\"\nAppVersionExist: \"バージョンはすでに存在します！\"\nAppCreateSuccessful: \"創造は成功しました！\"\nAppWriteErr: \"file {{ .name }} write failed {{ .err }}\"\nSudoHelper: \"{{.cmd}}を使用するか、ルートユーザーに切り替えてください\"\nListenIPCommands: \"リスニングIPを切り替えます\"\nListenIPv4: \"IPv4で聞いてください\"\nListenIPv6: \"IPv6で聞く\"\nListenChangeSuccessful: \"切り替え成功！{{.Value}}で聞いています\"\nResetCommands: \"システム情報をリセットします\"\nResetMFA: \"1パネルの2要素認証をキャンセルします\"\nResetHttps: \"1Panel HTTPSログインをキャンセルします\"\nResetEntrance: \"1パネルの安全な入り口をキャンセルします\"\nResetIPs: \"1パネル認定IP制限をキャンセルします\"\nResetDomain: \"1パネルドメインバインディングをキャンセルします\"\nResetPasskey: \"1Panel のパスキーをクリアします\"\nRestoreCommands: \"ロールバック1パネルサービスとデータ\"\nRestoreNoSuchFile: \"ロールバックに使用できるファイルはありません\"\nRestoreStep1: \"（1/5）{{ .name }}ディレクトリからの1パネルサービスとデータの開始ロールバック...\"\nRestoreStep2: \"（2/5）1パネルバイナリロールバックが成功しました\"\nRestoreStep3: \"（3/5）1パネルスクリプトロールバック成功\"\nRestoreStep4: \"（4/5）1パネルサービスロールバックが成功しました\"\nRestoreStep5: \"（5/5）1パネルデータロールバックが成功しました\"\nRestoreSuccessful: \"ロールバック成功！{{ .version }} にロールバックしました。サービスを再起動中です。しばらくお待ちください...\"\nUpdateCommands: \"パネル情報を更新します\"\nUpdateUser: \"パネルユーザーを更新します\"\nUpdatePassword: \"パスワードを更新します\"\nUpdatePort: \"パネルポートを更新します\"\nUpdateUserNull: \"エラー: パネルユーザーは空です！\"\nUpdateUserBlank: \"エラー: パネルユーザーにはスペースが含まれています！\"\nUpdateUserFormat: \"エラー: 無効なパネルユーザー形式！英語、中国語、数字、_、長さ3〜30のみをサポートしています\"\nUpdateUserErr: \"エラー: パネルユーザーの更新に失敗しました{{ .err }}\"\nUpdateSuccessful: \"更新成功！\"\nUpdateUserResult: \"パネルユーザー: {{ .name }}\"\nUpdatePasswordRead: \"エラー: パネルパスワード情報の読み取りに失敗しました、{{ .err }}\"\nUpdatePasswordNull: \"エラー: パネルパスワードは空です！\"\nUpdateUPasswordBlank: \"エラー: パスワードにはスペースが含まれています！\"\nUpdatePasswordFormat: \"エラー: パネルパスワードは英字、数字、特殊文字（@#$%*_,.?）のみ使用でき、長さは8〜30文字です。\"\nUpdatePasswordLen: \"エラー: 6文字より長くパスワードを入力してください！\"\nUpdatePasswordRe: \"パスワードを認証する: \"\nUpdatePasswordErr: \"エラー: {{ .err }}のパスワードの更新に失敗しました\"\nUpdatePasswordSame: \"エラー: 2つのパスワードが一致しません。チェックして再試行してください！\"\nUpdatePasswordResult: \"パネルパスワード: {{ .name }}\"\nUpdatePortFormat: \"エラー: 入力ポート番号は1〜65535でなければなりません！\"\nUpdatePortUsed: \"エラー: ポート番号はすでに使用されています。チェックして再試行してください！\"\nUpdatePortErr: \"エラー: パネルポートの更新に失敗しました{{ .err }}\"\nUpdatePortResult: \"パネルポート: {{ .name }}\"\nUpdatePortFirewallAdd: \"ファイアウォールポートルールの追加に失敗した{{ .err }}、ファイアウォールルールに{{ .name }}ポートを手動で追加してください。\"\nUpdatePortFirewallDel: \"エラー: ファイアウォールポートの削除に失敗しました、{{ .err }}\"\nUpdatePortFirewallReload: \"ファイアウォールのリロードに失敗しました{{ .err }}、ファイアウォールを手動でリロードしてください。\"\nUserInfo: \"パネル情報を取得します\"\nUserInfoAddr: \"パネルアドレス: \"\nUserInfoPassHelp: \"ヒント: パスワードを変更するには、コマンドを実行できます。\"\nDBConnErr: \"エラー: データベース接続の初期化に失敗しました、{{ .err }}\"\nSystemVersion: \"バージョン: \"\nSystemMode: \"モード: \"\n\n#mobile app\nErrVerifyToken: 'トークンの検証エラーです。リセット後、再度QRコードをスキャンしてください。'\nErrInvalidToken: '無効なトークンです。リセット後、再度QRコードをスキャンしてください。'\nErrExpiredToken: 'トークンの有効期限が切れました。リセット後、再度QRコードをスキャンしてください。'\n\n#cluster\nErrMasterDelete: \"マスターノードを削除できません。スレーブノードを先に削除してください。\"\nClusterNameIsExist: \"クラスタ名は既に存在します。\"\nAppStatusUnHealthy: \"アプリケーションのステータス取得が異常です。ノードリストでインストールノードのステータスを確認してください。\"\nMasterNodePortNotAvailable: \"ノード {{ .name }} のポート {{ .port }} の接続性検証が失敗しました。ファイアウォール/セキュリティグループの設定とマスターノードのステータスを確認してください。\"\nClusterMasterNotExist: \"クラスタのマスターノードが切断されています。子ノードを削除してください。\"\n\n#ssl\nErrReqFailed: \"{{.name}} リクエスト失敗: {{ .err }}\"\n\n#command\nName: \"名前\"\nCommand: \"コマンド\"\n"
  },
  {
    "path": "core/i18n/lang/ko.yaml",
    "content": "ErrInvalidParams: '잘못된 요청: {{ .detail }}'\nErrTokenParse: '토큰 오류: {{ .detail }}'\nErrInitialPassword: '원래 비밀번호가 틀립니다'\nErrInternalServer: '서버 오류: {{ .detail }}'\nErrRecordExist: '레코드가 이미 있습니다'\nErrRecordNotFound: '레코드를 찾을 수 없습니다'\nErrStructTransform: '형 변환 실패: {{ .err }}'\nErrNotLogin: '로그인하지 않음: {{ .detail }}'\nErrSessionDataNotFound: \"현재 세션이 만료되었습니다\"\nErrSessionDataFormat: \"현재 세션 데이터 형식이 비정상입니다\"\nErrPasswordExpired: '비밀번호 만료: {{ .detail }}'\nErrNotSupportType: '지원하지 않는 타입: {{ .name }}'\nErrProxy: '요청 실패: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API 사용 중지: {{ .detail }}'\nErrApiConfigKeyInvalid: 'API 키가 잘못됨: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP 허용되지 않음: {{ .detail }}'\nErrApiConfigDisable: 'API 접근 금지: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: '타임스탬프 오류: {{ .detail }}'\n\n# request\nErrNoSuchHost: \"요청한 서버를 찾을 수 없습니다 {{ .err }}\"\nErrHttpReqNotFound: \"요청한 리소스를 찾을 수 없습니다 {{ .err }}\"\nErrHttpReqFailed: \"요청 실패 {{ .err }}\"\nErrHttpReqTimeOut: \"요청 시간이 초과되었습니다 {{ .err }}\"\nErrCreateHttpClient: \"요청 생성 실패 {{ .err }}\"\nErrProxySetting: \"프록시 서버 정보를 사용할 수 없음 {{ .err }}, 확인 후 다시 시도하세요\"\nErrEntranceFormat: \"보안 입구 {{ .name }}은(는) 현재 지원되지 않습니다. 확인 후 다시 시도해 주세요\"\n\n# common\nErrDemoEnvironment: '데모 환경에서는 불가'\nErrCmdTimeout: '명령이 시간 초과했습니다'\nErrEntrance: \"보안 정보 오류입니다. 확인 후 다시 시도하십시오\"\nErrGroupIsDefault: \"기본 그룹은 삭제할 수 없습니다\"\nErrGroupIsInUse: \"그룹이 사용 중이므로 삭제할 수 없습니다.\"\nErrLocalDelete: \"로컬 노드는 삭제할 수 없습니다\"\nErrPortInUsed: \"{{ .name }} 포트가 이미 사용 중입니다\"\nErrInternalServerKey: \"서버 내부 오류:\"\nMasterNode: \"마스터 노드\"\n\n# app\nCustomAppStoreFileValid: \"앱 스토어 패키지는 .tar.gz 형식이어야 합니다\"\nErrFileNotFound: \"{{ .name }} 파일이 존재하지 않습니다\"\nAppBackup: '애플리케이션 백업'\nAppBackupPush: '애플리케이션 백업 파일 {{.file}}을(를) 노드 {{ .name }}(으)로 전송'\nErrSourceTargetSame: '소스 노드와 대상 노드는 동일할 수 없습니다'\nAppInstall: '노드 {{ .targetNode }}에 애플리케이션 {{ .name }} 설치'\nAppInstallCheck: '애플리케이션 설치 환경 확인'\n\n# backup\nErrBackupInUsed: '백업 계정이 예약에 사용 중'\nErrBackupCheck: '연결 테스트 실패: {{ .err }}'\nErrBackupLocal: \"로컬 서버 백업 계정은 이 작업을 지원하지 않습니다\"\nErrBackupPublic: \"이 백업 계정이 공개된 것으로 감지되지 않았습니다. 다시 확인하십시오\"\nErrOSSConn: \"최신 버전을 가져올 수 없습니다. 외부 네트워크 연결을 확인하십시오.\"\n\n#license\nLicenseCheck: '라이선스 사용 가능 여부 확인'\nErrLicenseInUsed: '해당 라이선스가 이미 연결되었습니다. 확인 후 다시 시도하세요'\nErrLicenseExpired: '해당 라이선스가 만료되었습니다. 확인 후 다시 시도하세요'\nErrLicense: \"라이선스 형식이 잘못되었습니다. 다시 확인하고 시도해 주세요\"\nErrLicenseCheck: \"라이선스 검증 실패. 다시 확인하고 시도해 주세요\"\nErrXpackVersion: \"라이선스 검증 실패, 이 라이선스는 버전 제한이 있습니다. 성공적으로 가져올 수 없습니다. 다시 확인하고 시도해 주세요\"\nErrLicenseSave: \"라이선스 정보 저장 실패, 오류 {{ .err }}, 다시 시도해 주세요\"\nErrLicenseSync: \"라이선스 정보 동기화 실패, 데이터베이스에서 라이선스 정보를 찾을 수 없습니다\"\nErrLicenseExist: \"해당 라이선스 기록이 이미 존재합니다. 라이선스 페이지에서 직접 노드 바인딩이 가능합니다.\"\nErrXpackNotFound: \"이 섹션은 프로페셔널 에디션 기능입니다. 먼저 [패널 설정]-[라이선스] 인터페이스에서 라이선스를 가져오세요.\"\nErrXpackExceptional: \"이 섹션은 프로페셔널 에디션 기능입니다. 먼저 [패널 설정]-[라이선스] 인터페이스에서 라이선스 상태를 동기화하세요.\"\nErrXpackLost: \"라이선스가 최대 재시도 횟수에 도달했습니다. [패널 설정][라이선스] 페이지로 이동하여 수동으로 동기화 버튼을 클릭하여 프로페셔널 기능을 정상적으로 사용할 수 있도록 하세요.\"\nErrDeviceLost: \"라이센스 검증에 필요한 파일이 누락되었습니다. 확인 후 다시 시도해 주세요\"\nErrDeviceErr: \"현재 환경이 라이선스 가져오기 환경과 일치하지 않습니다. 라이선스를 편집하고 다시 가져오십시오\"\nErrXpackTimeout: \"요청 시간 초과, 네트워크 연결이 불안정할 수 있습니다. 나중에 다시 시도해 주세요\"\nErrUnbindMaster: \"노드 관리 내에 노드가 존재함을 감지하였습니다. 현재 라이선스를 해제할 수 없습니다. 먼저 제거 후 다시 시도해 주세요\"\nErrFreeNodeLimit: \"커뮤니티 에디션 노드 수가 무료 한도에 도달하였습니다. www.lxware.cn/1panel 에서 구매 후 다시 시도해 주세요\"\nErrNodeBound: \"이 라이선스는 다른 노드에 바인딩되어 있습니다. 다시 확인하고 시도해 주세요\"\nErrNodeBoundDelete: \"이 라이센스는 바인딩되어 있으며 삭제 작업을 지원하지 않습니다. 확인 후 다시 시도해 주세요\"\nErrNodeBoundLimit: \"현재 무료 노드가 한도에 도달했습니다. 확인 후 다시 시도해 주세요\"\nErrLicenseFree: \"라이선스가 노드에 정상적으로 바인딩된 경우에만 무료 노드를 사용할 수 있습니다. 확인 후 다시 시도하세요\"\nErrLicenseUnbind: \"이 라이선스에 커뮤니티 에디션 노드가 존재합니다. [패널 설정 - 라이선스]에서 바인딩 해제 후 다시 시도하세요\"\nErrNoSuchNode: \"노드 정보를 찾을 수 없습니다. 다시 확인하고 시도해 주세요\"\nErrNodeUnbind: \"이 노드가 라이선스 바인딩 범위에 있지 않음을 감지하였습니다. 다시 확인하고 시도해 주세요\"\nErrNodeBind: \"이 노드가 이미 라이선스에 바인딩되어 있음을 감지하였습니다. 다시 확인하고 시도해 주세요\"\nErrNodeLocalRollback: \"마스터 노드는 직접 롤백을 지원하지 않습니다. 수동으로 '1pctl restore' 명령어를 실행하여 롤백하세요\"\nErrIntlLicense: \"현재 버전에서는 국제판 라이선스 가져오기를 아직 지원하지 않습니다. 곧 제공될 예정입니다\"\n\nInvalidRequestBodyType: \"요청 본문 형식이 잘못되었습니다. 내용이 형식 요구 사항을 충족하는지 확인한 후 다시 시도하세요\"\nInvalidLicenseCodeType: \"제공된 라이선스 코드 형식이 잘못되었습니다. 확인 후 다시 시도하세요\"\nLicenseNotFoundType: \"라이선스가 존재하지 않습니다. 시스템에서 제공된 라이선스와 일치하는 기록을 찾을 수 없습니다. 확인 후 다시 시도하세요\"\nLicenseRevokedType: \"요청한 라이선스는 폐기되어 사용할 수 없습니다. 확인 후 다시 시도하세요\"\nLicenseExpiredType: \"라이선스가 만료되었습니다. 갱신하거나 패널 설정 - 라이선스에서 다시 가져온 후 시도하세요\"\nLicenseProductMismatchType: \"라이선스가 요청한 제품 또는 서비스와 일치하지 않습니다\"\nInvalidAssigneeType: \"라이선스 할당 대상 사용자 또는 장치 정보가 유효하지 않습니다. 확인 후 다시 시도하세요\"\nLicenseUsageNotFoundType: \"사용 기록이 없습니다. 이 라이선스는 아직 활성화되거나 사용되지 않았습니다. 확인 후 다시 시도하세요\"\nLicenseUsageLimitExceededType: \"이 라이선스는 이미 다른 노드에 연결되었습니다. 확인 후 다시 시도하세요\"\n\n# alert\nErrAlertSync: \"경고 정보 동기화 오류, 다시 확인하고 시도해 주세요\"\n\n#task\nTaskStart: \"{{ .name }} 작업 시작 [START]\"\nTaskEnd: \"{{ .name }} 작업 종료 [COMPLETED]\"\nTaskFailed: \"{{ .name }} 작업 실패\"\nTaskTimeout: \"{{ .name }} 시간 초과\"\nTaskSuccess: \"{{ .name }} 작업 성공\"\nTaskRetry: \"제 {{ .name }} 차 재시도 시작\"\nSubTaskSuccess: \"{{ .name }} 성공\"\nSubTaskFailed: \"{{ .name }} 실패: {{ .err }}\"\nTaskInstall: \"설치\"\nTaskUpgrade: \"업그레이드\"\nTaskSync: '동기화'\nTaskSyncForNode: \"노드 데이터 동기화\"\nTaskBackup: \"백업\"\nSuccessStatus: \"{{ .name }} 성공\"\nFailedStatus: \"{{ .name }} 실패 {{ .err }}\"\nStart: \"시작\"\nSubTask: \"서브 작업\"\nSkip: \"오류 무시하고 계속...\"\nPushAppInstallTaskToNode: \"노드 [{{ .name }}]에 앱 설치 작업 푸시\"\nTaskPush: \"푸시\"\nAppInstallTask: \"앱 설치 작업\"\nPushAppFailed: \"앱 설치 작업 푸시 실패\"\nSuccess: \"성공\"\n\n#script\nScriptLibrary: \"스크립트 라이브러리\"\nRemoteScriptLibrary: \"원격 스크립트 라이브러리\"\nScriptSyncSkip: \"현재 스크립트 라이브러리가 이미 최신 버전입니다\"\nDownloadData: \"스크립트 라이브러리 파일 data.yaml 다운로드 중\"\nDownloadPackage: \"스크립트 라이브러리 패키지 다운로드 중\"\nAnalyticCompletion: \"분석 완료, 데이터베이스에 동기화 중...\"\n\nNode: \"노드\"\nSyncNode: \"데이터를 노드 {{ .name }}(으)로 동기화\"\nLocalName: \"'local' 이름은 시스템 로컬 식별 전용으로 사용됩니다\"\nSyncPackageData: \"동기화 데이터 패키징\"\nSyncPackageEncrypt: \"데이터 패키지 암호화\"\nSyncRequest: \"노드 동기화 API 요청\"\nSyncFailedRetry: \"노드 데이터 동기화 시간 초과 ({{ .index }}번째 시도), 재시도 중...\"\nSyncFailed: \"동기화 실패, 노드 목록에서 수동 동기화를 실행하세요\"\nSyncSystemProxy: \"시스템 프록시 설정\"\nSyncScripts: \"스크립트 라이브러리\"\nSyncBackupAccounts: \"백업 계정\"\nSyncAlertSetting: \"알림 설정\"\nSyncCustomApp: \"사용자 정의 앱\"\nSyncLanguage: \"시스템 언어\"\nSyncEdition: \"운영 지역\"\n\n#upgrade node\nNodeUpgrade: \"{{ .name }} 노드 업그레이드\"\nUpgradeCheck: \"노드 업데이트 확인\"\nUpgradeCheckLocal: \"로컬 노드는 일괄 업그레이드를 지원하지 않으므로 건너뜁니다...\"\nUpgradeCheckLatest: \"이 노드는 이미 최신 버전입니다. 건너뜁니다...\"\nNewSSHClient: \"SSH 연결 초기화\"\nBackupBeforeUpgrade: \"업그레이드 전 데이터 백업\"\nUploadUpgradeFile: \"업그레이드에 필요한 파일 전송\"\nRestartAfterUpgrade: \"업그레이드 후 서비스 시작\"\n\n#add node\nMasterData: \"마스터 노드 데이터\"\nLoadSftpClient: \"SFTP 클라이언트 가져오기\"\nPackageMasterData: \"마스터 노드 백업 패키지 생성\"\nUploadBackup: \"백업 데이터 업로드\"\nMvBackup: \"데이터를 백업 디렉터리로 이동\"\nTaskAddNode: \"노드 추가\"\nLoadNodeArch: \"노드 아키텍처 정보 가져오기\"\nLoadNodeArchDetail: \"감지된 마스터 노드 아키텍처: {{ .local }}, 자식 노드 아키텍처: {{ .node }}\"\nLoadNodeUpgradeDetail: \"v1 버전 기록 설치 디렉토리 사용: {{ .baseDir }}, 서비스 수신 포트: {{ .port }}\"\nSyncAgentBaseInfo: \"노드 기본 데이터 동기화\"\nGenerateSSLInfo: \"노드 SSL 정보 생성\"\nConnInfoNotMatch: \"연결 정보가 일치하지 않음\"\nMakeAgentPackage: \"노드 설치 패키지 생성\"\nSendAgent: \"노드 설치 패키지 배포\"\nStartService: \"서비스 시작\"\nNoBackupNode: \"현재 백업 노드가 비어 있습니다. 저장할 백업 노드를 선택한 후 다시 시도하십시오\"\n\n# CMD\nAppVersion: \"앱 버전\"\nAppCommands: \"앱 관련 명령어\"\nAppInit: \"앱 초기화\"\nAppKeyVal: \"앱 키 (영어만 지원)\"\nAppCreateFileErr: \"파일 {{ .name }} 생성 실패 {{ .err }}\"\nAppCreateDirErr: \"폴더 {{ .name }} 생성 실패 {{ .err }}\"\nAppMissKey: \"앱 키가 없습니다. -k 옵션으로 지정하세요.\"\nAppMissVersion: \"앱 버전이 없습니다. -v 옵션으로 지정하세요.\"\nAppVersionExist: \"버전이 이미 존재합니다\"\nAppCreateSuccessful: \"생성 성공\"\nAppWriteErr: \"파일 {{ .name }} 쓰기 실패 {{ .err }}\"\nSudoHelper: \"{{ .cmd }} 명령을 사용하거나 root 사용자로 전환하세요.\"\nListenIPCommands: \"IP 변경\"\nListenIPv4: \"IPv4 에서 수신 대기\"\nListenIPv6: \"IPv6 에서 수신 대기\"\nListenChangeSuccessful: \"변경 성공 현재 {{ .value }}에서 수신 대기 중입니다.\"\nResetCommands: \"시스템 정보 초기화\"\nResetMFA: \"1Panel 이중 인증 취소\"\nResetHttps: \"1Panel HTTPS 로그인 취소\"\nResetEntrance: \"1Panel 보안 입구 취소\"\nResetIPs: \"1Panel 허용 IP 제한 취소\"\nResetDomain: \"1Panel 도메인 바인딩 취소\"\nResetPasskey: \"1Panel 패스키 초기화\"\nRestoreCommands: \"1Panel 서비스 및 데이터 복구\"\nRestoreNoSuchFile: \"복구 가능한 파일이 없습니다.\"\nRestoreStep1: \"(1/5) {{ .name }} 디렉토리에서 1Panel 서비스 및 데이터 복구 시작...\"\nRestoreStep2: \"(2/5) 1Panel 바이너리 복구 성공\"\nRestoreStep3: \"(3/5) 1Panel 스크립트 복구 성공\"\nRestoreStep4: \"(4/5) 1Panel 서비스 복구 성공\"\nRestoreStep5: \"(5/5) 1Panel 데이터 복구 성공\"\nRestoreSuccessful: \"롤백 성공 {{ .version }}(으)로 롤백되었습니다. 서비스를 재시작 중입니다. 잠시만 기다려 주세요...\"\nUpdateCommands: \"패널 정보 업데이트\"\nUpdateUser: \"패널 사용자 업데이트\"\nUpdatePassword: \"패널 비밀번호 업데이트\"\nUpdatePort: \"패널 포트 업데이트\"\nUpdateUserNull: \"오류: 패널 사용자가 비어 있습니다\"\nUpdateUserBlank: \"오류: 패널 사용자에 공백이 포함되어 있습니다\"\nUpdateUserFormat: \"오류: 잘못된 패널 사용자 형식 영어, 중국어, 숫자, _만 지원하며 길이는 3~30자입니다.\"\nUpdateUserErr: \"오류: 패널 사용자 업데이트 실패 {{ .err }}\"\nUpdateSuccessful: \"업데이트 성공\"\nUpdateUserResult: \"패널 사용자: {{ .name }}\"\nUpdatePasswordRead: \"오류: 패널 비밀번호 정보 읽기 실패 {{ .err }}\"\nUpdatePasswordNull: \"오류: 패널 비밀번호가 비어 있습니다\"\nUpdateUPasswordBlank: \"오류: 패널 비밀번호에 공백이 포함되어 있습니다\"\nUpdatePasswordFormat: \"오류: 패널 비밀번호는 문자, 숫자, 특수문자(@#$%*_,.?)만 지원하며 길이는 8~30자입니다.\"\nUpdatePasswordLen: \"오류: 6자 이상의 비밀번호를 입력하세요\"\nUpdatePasswordRe: \"비밀번호 확인:\"\nUpdatePasswordErr: \"오류: 패널 비밀번호 업데이트 실패 {{ .err }}\"\nUpdatePasswordSame: \"오류: 두 비밀번호가 일치하지 않습니다. 확인 후 다시 시도하세요\"\nUpdatePasswordResult: \"패널 비밀번호: {{ .name }}\"\nUpdatePortFormat: \"오류: 입력한 포트 번호는 1~65535 사이여야 합니다\"\nUpdatePortUsed: \"오류: 포트 번호가 이미 사용 중입니다. 확인 후 다시 시도하세요\"\nUpdatePortErr: \"오류: 패널 포트 업데이트 실패 {{ .err }}\"\nUpdatePortResult: \"패널 포트: {{ .name }}\"\nUpdatePortFirewallAdd: \"방화벽 포트 규칙 추가 실패 {{ .err }}, 방화벽 규칙에 {{ .name }} 포트를 수동으로 추가하세요.\"\nUpdatePortFirewallDel: \"오류: 방화벽 포트 삭제 실패 {{ .err }}\"\nUpdatePortFirewallReload: \"방화벽 다시 로드 실패 {{ .err }}, 방화벽을 수동으로 다시 로드하세요.\"\nUserInfo: \"패널 정보 가져오기\"\nUserInfoAddr: \"패널 주소: \"\nUserInfoPassHelp: \"팁: 비밀번호를 변경하려면 다음 명령어를 실행할 수 있습니다: \"\nDBConnErr: \"오류: 데이터베이스 연결 초기화 실패 {{ .err }}\"\nSystemVersion: \"버전: \"\nSystemMode: \"모드: \"\n\n#mobile app\nErrVerifyToken: '토큰 검증 오류가 발생했습니다. 재설정 후 다시 스캔해 주세요.'\nErrInvalidToken: '유효하지 않은 토큰입니다. 재설정 후 다시 스캔해 주세요.'\nErrExpiredToken: '토큰이 만료되었습니다. 재설정 후 다시 스캔해 주세요.'\n\n#cluster\nErrMasterDelete: \"마스터 노드를 삭제할 수 없습니다. 슬레이브 노드를 먼저 삭제하세요.\"\nClusterNameIsExist: \"클러스터 이름이 이미 존재합니다.\"\nAppStatusUnHealthy: \"애플리케이션 상태 획득이 비정상입니다. 노드 목록에서 설치 노드 상태를 확인하세요.\"\nMasterNodePortNotAvailable: \"노드 {{ .name }} 포트 {{ .port }} 연결성 검증에 실패했습니다. 방화벽/보안 그룹 설정 및 마스터 노드 상태를 확인하세요.\"\nClusterMasterNotExist: \"클러스터의 마스터 노드가 연결이 끊어졌습니다. 자식 노드를 삭제하세요.\"\n\n#ssl\nErrReqFailed: \"{{.name}} 요청 실패: {{ .err }}\"\n\n#command\nName: \"이름\"\nCommand: \"명령어\"\n"
  },
  {
    "path": "core/i18n/lang/ms.yaml",
    "content": "ErrInvalidParams: 'Permintaan tidak sah: {{ .detail }}'\nErrTokenParse: 'Token tidak sah: {{ .detail }}'\nErrInitialPassword: 'Kata laluan asal salah'\nErrInternalServer: 'Ralat pelayan: {{ .detail }}'\nErrRecordExist: 'Rekod sudah wujud'\nErrRecordNotFound: 'Rekod tidak ditemui'\nErrStructTransform: 'Ralat penukaran: {{ .err }}'\nErrNotLogin: 'Belum log masuk: {{ .detail }}'\nErrSessionDataNotFound: \"Sesi semasa telah tamat tempoh\"\nErrSessionDataFormat: \"Format data sesi semasa tidak normal\"\nErrPasswordExpired: 'Kata laluan tamat: {{ .detail }}'\nErrNotSupportType: 'Jenis tidak disokong: {{ .name }}'\nErrProxy: 'Permintaan gagal: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API dilumpuhkan: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Kunci API tidak sah: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP tidak dibenarkan: {{ .detail }}'\nErrApiConfigDisable: 'API tidak dibenarkan: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Cap masa tidak sah: {{ .detail }}'\n\n# request\nErrNoSuchHost: \"Pelayan yang diminta tidak dapat ditemui {{ .err }}\"\nErrHttpReqNotFound: \"Sumber yang diminta tidak dapat ditemui {{ .err }}\"\nErrHttpReqFailed: \"Permintaan gagal {{ .err }}\"\nErrHttpReqTimeOut: \"Permintaan telah tamat masa {{ .err }}\"\nErrCreateHttpClient: \"Gagal mencipta permintaan {{ .err }}\"\nErrProxySetting: \"Maklumat pelayan proksi tidak tersedia {{ .err }}, sila periksa dan cuba lagi\"\nErrEntranceFormat: \"Pintu masuk keselamatan {{ .name }} tidak disokong buat masa ini. Sila periksa dan cuba lagi\"\n\n# common\nErrDemoEnvironment: 'Tidak tersedia dalam demo'\nErrCmdTimeout: 'Arahan tamat masa'\nErrEntrance: \"Maklumat pintu masuk keselamatan salah, sila periksa dan cuba lagi\"\nErrGroupIsDefault: \"Kumpulan lalai tidak boleh dihapuskan\"\nErrGroupIsInUse: \"Kumpulan sedang digunakan dan tidak boleh dipadam.\"\nErrLocalDelete: \"Nod tempatan tidak boleh dihapuskan\"\nErrPortInUsed: \"Port {{ .name }} telah digunakan\"\nErrInternalServerKey: \"Ralat dalaman pelayan:\"\nMasterNode: \"Nod Induk\"\n\n# app\nCustomAppStoreFileValid: \"Pakej stor aplikasi perlu dalam format .tar.gz\"\nErrFileNotFound: \"Fail {{ .name }} tidak wujud\"\n\n# backup\nErrBackupInUsed: 'Akaun sandaran sedang digunakan oleh tugas'\nErrBackupCheck: 'Ujian sambungan gagal: {{ .err }}'\nErrBackupLocal: \"Akaun sandaran pelayan tempatan tidak menyokong operasi ini\"\nErrBackupPublic: \"Akaun sandaran ini dikesan tidak awam, sila semak semula dan cuba lagi\"\nErrOSSConn: \"Tidak dapat mendapatkan versi terkini, sila semak sambungan rangkaian luar pelayan.\"\n\n#license\nLicenseCheck: 'Periksa sama ada lesen tersedia'\nErrLicenseInUsed: 'Lesen ini telah terikat. Sila periksa dan cuba lagi'\nErrLicenseExpired: 'Lesen ini telah tamat tempoh. Sila periksa dan cuba lagi'\nErrLicense: \"Format lesen tidak sah, sila semak dan cuba lagi\"\nErrLicenseCheck: \"Pengesahan lesen gagal, sila semak dan cuba lagi\"\nErrXpackVersion: \"Pengesahan lesen gagal, lesen ini terhad kepada versi tertentu, sila semak dan cuba lagi\"\nErrLicenseSave: \"Gagal menyimpan maklumat lesen {{ .err }}, sila cuba lagi\"\nErrLicenseSync: \"Penyelarasan maklumat lesen gagal, maklumat lesen tidak ditemui dalam pangkalan data\"\nErrLicenseExist: \"Rekod lesen ini sudah wujud. Anda boleh terus ke halaman lesen untuk mengikat nod.\"\nErrXpackNotFound: \"Bahagian ini adalah fungsi profesional, sila muatkan lesen di Tetapan Papan Pemuka terlebih dahulu.\"\nErrXpackExceptional: \"Bahagian ini adalah fungsi profesional, sila selaraskan lesen di Tetapan Papan Pemuka terlebih dahulu.\"\nErrXpackLost: \"Lesen telah mencapai bilangan percubaan maksimum, sila ke [Tetapan Papan Pemuka][Lesen] dan klik butang penyelarasan secara manual.\"\nErrDeviceLost: \"Fail yang diperlukan untuk pengesahan lesen hilang, sila semak dan cuba lagi\"\nErrDeviceErr: \"Persekitaran semasa tidak sepadan dengan persekitaran import lesen. Sila edit lesen dan import semula\"\nErrXpackTimeout: \"Permintaan tamat masa, sambungan rangkaian mungkin tidak stabil, sila cuba lagi kemudian\"\nErrUnbindMaster: \"Terdapat nod dalam pengurusan nod, sila keluarkan dahulu dan cuba lagi\"\nErrFreeNodeLimit: \"Jumlah nod versi komuniti telah mencapai had percuma, sila lawati www.lxware.cn/1panel untuk pembelian\"\nErrNodeBound: \"Lesen ini telah diikat dengan nod lain, sila semak dan cuba lagi\"\nErrNodeBoundDelete: \"Lisensi ini telah diikat dan tidak menyokong operasi penghapusan. Sila semak dan cuba lagi\"\nErrNodeBoundLimit: \"Nod percuma semasa telah mencapai had, sila semak dan cuba lagi\"\nErrLicenseFree: \"Nod percuma hanya boleh digunakan apabila lesen terikat dengan betul pada nod. Sila semak dan cuba lagi\"\nErrLicenseUnbind: \"Nod Edisi Komuniti dikesan untuk lesen ini. Sila lepaskan ikatan dalam [Tetapan Panel - Lesen] dan cuba lagi\"\nErrNoSuchNode: \"Maklumat nod tidak ditemui, sila semak dan cuba lagi\"\nErrNodeUnbind: \"Nod di luar skop lesen dikesan, sila semak dan cuba lagi\"\nErrNodeBind: \"Nod ini telah diikat dengan lesen, sila semak dan cuba lagi\"\nErrNodeLocalRollback: \"Nod utama tidak menyokong rollback secara langsung. Sila laksanakan arahan '1pctl restore' secara manual untuk rollback\"\nErrIntlLicense: \"Versi semasa belum menyokong import lesen edisi antarabangsa. Nantikan\"\n\nInvalidRequestBodyType: \"Format badan permintaan tidak sah, sila periksa dan pastikan kandungan memenuhi format yang diperlukan sebelum mencuba semula\"\nInvalidLicenseCodeType: \"Format kod lesen yang diberikan tidak sah, sila periksa dan cuba lagi\"\nLicenseNotFoundType: \"Lesen tidak dijumpai, tiada rekod yang sepadan dengan lesen yang diberikan dalam sistem. Sila periksa dan cuba lagi\"\nLicenseRevokedType: \"Lesen yang diminta telah dibatalkan dan tidak boleh digunakan lagi. Sila periksa dan cuba lagi\"\nLicenseExpiredType: \"Lesen telah tamat tempoh. Sila perbaharui atau import semula lesen dalam Tetapan Panel - Bahagian Lesen sebelum mencuba semula\"\nLicenseProductMismatchType: \"Lesen tidak sepadan dengan produk atau perkhidmatan yang diminta\"\nInvalidAssigneeType: \"Maklumat pengguna atau peranti sasaran untuk penugasan lesen tidak sah. Sila periksa dan cuba lagi\"\nLicenseUsageNotFoundType: \"Tiada rekod penggunaan dijumpai. Lesen ini belum diaktifkan atau digunakan. Sila periksa dan cuba lagi\"\nLicenseUsageLimitExceededType: \"Lesen ini sudah terikat pada nod lain. Sila periksa dan cuba lagi\"\n\n# alert\nErrAlertSync: \"Ralat penyinkronan maklumat amaran, sila semak dan cuba lagi\"\n\n#task\nTaskStart: \"{{ .name }} tugas bermula [START]\"\nTaskEnd: \"{{ .name }} tugas tamat [COMPLETED]\"\nTaskFailed: \"{{ .name }} tugas gagal\"\nTaskTimeout: \"{{ .name }} tamat masa\"\nTaskSuccess: \"{{ .name }} tugas berjaya\"\nTaskRetry: \"Mula percubaan semula {{ .name }}\"\nSubTaskSuccess: \"{{ .name }} berjaya\"\nSubTaskFailed: \"{{ .name }} gagal: {{ .err }}\"\nTaskInstall: \"Pasang\"\nTaskUpgrade: \"Kemas kini\"\nTaskSync: 'Selaraskan'\nTaskSyncForNode: \"Segerakan Data Nod\"\nTaskBackup: \"Sandaran\"\nSuccessStatus: \"{{ .name }} berjaya\"\nFailedStatus: \"{{ .name }} gagal {{ .err }}\"\nStart: \"Mula\"\nSubTask: \"Tugas Sub\"\nSkip: \"Abaikan ralat dan teruskan...\"\nPushAppInstallTaskToNode: \"Tolak tugas pemasangan aplikasi ke nod [{{ .name }}]\"\nTaskPush: \"Tolak\"\nAppInstallTask: \"Tugas pemasangan aplikasi\"\nPushAppFailed: \"Gagal menolak tugas pemasangan aplikasi\"\nSuccess: \"Berjaya\"\n\n#script \nScriptLibrary: \"Pustaka Skrip\"\nRemoteScriptLibrary: \"Pustaka Skrip Jauh\"\nScriptSyncSkip: \"Pustaka skrip semasa sudah versi terkini\"\nDownloadData: \"Muat turun fail pustaka skrip data.yaml\"\nDownloadPackage: \"Muat turun pakej pustaka skrip\"\nAnalyticCompletion: \"Analisis selesai, sedang menyegerakkan ke pangkalan data...\"\n\nNode: \"Nod\"\nSyncNode: \"Selaraskan data ke nod {{ .name }}\"\nLocalName: \"Nama 'local' hanya digunakan untuk pengenalpastian tempatan sistem\"\nSyncPackageData: \"Pakej data segerak\"\nSyncPackageEncrypt: \"Enkripsi pakej data\"\nSyncRequest: \"Permintaan API segerak nod\"\nSyncFailedRetry: \"Segerakan data nod tamat masa (percubaan ke-{{ .index }}), mencuba semula...\"\nSyncFailed: \"Segerakan gagal, sila segerakkan secara manual dalam senarai nod\"\nSyncSystemProxy: \"Tetapan Proksi Sistem\"\nSyncScripts: \"Pustaka Skrip\"\nSyncBackupAccounts: \"Akaun Sandaran\"\nSyncAlertSetting: \"Tetapan Amaran\"\nSyncCustomApp: \"Aplikasi Tersuai\"\nSyncLanguage: \"Bahasa Sistem\"\nSyncEdition: \"Wilayah Operasi\"\n\n#upgrade node\nNodeUpgrade: \"Naik taraf node {{ .name }}\"\nUpgradeCheck: \"Periksa kemas kini nod\"\nUpgradeCheckLocal: \"Nod tempatan tidak menyokong peningkatan kelompok, dilangkau...\"\nUpgradeCheckLatest: \"Nod sudah versi terkini, dilangkau...\"\nNewSSHClient: \"Memulakan sambungan SSH\"\nBackupBeforeUpgrade: \"Menyandarkan data sebelum naik taraf\"\nUploadUpgradeFile: \"Sebarkan fail naik taraf\"\nRestartAfterUpgrade: \"Mulakan perkhidmatan selepas naik taraf\"\n\n#add node\nMasterData: \"Data Nod Master\"\nLoadSftpClient: \"Dapatkan Klien SFTP\"\nPackageMasterData: \"Hasilkan pakej sandaran nod master\"\nUploadBackup: \"Muat naik data sandaran\"\nMvBackup: \"Alihkan data ke direktori sandaran\"\nTaskAddNode: \"Tambah nod\"\nLoadNodeArch: \"Dapatkan maklumat seni bina nod\"\nLoadNodeArchDetail: \"Mengesan seni bina nod induk: {{ .local }}, seni bina nod anak: {{ .node }}\"\nLoadNodeUpgradeDetail: \"Menggunakan direktori pemasangan sejarah versi v1: {{ .baseDir }}, port mendengar perkhidmatan: {{ .port }}\"\nSyncAgentBaseInfo: \"Sinkronkan maklumat asas nod\"\nGenerateSSLInfo: \"Cipta maklumat SSL nod\"\nConnInfoNotMatch: \"Maklumat sambungan tidak sepadan\"\nMakeAgentPackage: \"Cipta pakej pemasangan nod\"\nSendAgent: \"Muat naik pakej pemasangan nod\"\nStartService: \"Mulakan perkhidmatan\"\nNoBackupNode: \"Nod sandaran kosong. Sila pilih nod sandaran untuk disimpan dan cuba lagi\"\n\n#cmd\nAppVersion: \"Versi aplikasi\"\nAppCommands: \"Arahan berkaitan aplikasi\"\nAppInit: \"Inisialisasi aplikasi\"\nAppKeyVal: \"Kunci aplikasi (hanya menyokong bahasa Inggeris)\"\nAppCreateFileErr: \"Gagal mencipta fail {{ .name }} {{ .err }}\"\nAppCreateDirErr: \"Gagal mencipta folder {{ .name }} {{ .err }}\"\nAppMissKey: \"Kunci aplikasi hilang, gunakan -k untuk menetapkan\"\nAppMissVersion: \"Versi aplikasi hilang, gunakan -v untuk menetapkan\"\nAppVersionExist: \"Versi sudah wujud\"\nAppCreateSuccessful: \"Ciptaan berjaya\"\nAppWriteErr: \"Penulisan fail {{ .name }} gagal {{ .err }}\"\nSudoHelper: \"Sila gunakan {{ .cmd }} atau tukar ke pengguna root\"\nListenIPCommands: \"Tukar IP mendengar\"\nListenIPv4: \"Mendengar pada IPv4\"\nListenIPv6: \"Mendengar pada IPv6\"\nListenChangeSuccessful: \"Tukar berjaya Kini mendengar pada {{ .value }}\"\nResetCommands: \"Tetapkan semula maklumat sistem\"\nResetMFA: \"Batal pengesahan dua faktor 1Panel\"\nResetHttps: \"Batal log masuk HTTPS 1Panel\"\nResetEntrance: \"Batal pintu masuk keselamatan 1Panel\"\nResetIPs: \"Batal sekatan IP yang dibenarkan 1Panel\"\nResetDomain: \"Batal pengikatan domain 1Panel\"\nResetPasskey: \"Kosongkan passkey 1Panel\"\nRestoreCommands: \"Pulihkan perkhidmatan dan data 1Panel\"\nRestoreNoSuchFile: \"Tiada fail tersedia untuk pemulihan\"\nRestoreStep1: \"(1/5) Memulakan pemulihan perkhidmatan dan data 1Panel daripada direktori {{ .name }}...\"\nRestoreStep2: \"(2/5) Pemulihan binari 1Panel berjaya\"\nRestoreStep3: \"(3/5) Pemulihan skrip 1Panel berjaya\"\nRestoreStep4: \"(4/5) Pemulihan perkhidmatan 1Panel berjaya\"\nRestoreStep5: \"(5/5) Pemulihan data 1Panel berjaya\"\nRestoreSuccessful: \"Pulangan semula berjaya Dikembalikan ke {{ .version }}, memulakan semula perkhidmatan, sila tunggu...\"\nUpdateCommands: \"Kemas kini maklumat panel\"\nUpdateUser: \"Kemas kini pengguna panel\"\nUpdatePassword: \"Kemas kini kata laluan panel\"\nUpdatePort: \"Kemas kini port panel\"\nUpdateUserNull: \"Ralat: Pengguna panel kosong\"\nUpdateUserBlank: \"Ralat: Pengguna panel mengandungi ruang kosong\"\nUpdateUserFormat: \"Ralat: Format pengguna panel tidak sah Hanya menyokong huruf Inggeris, Cina, nombor, dan _, dengan panjang 3-30 aksara\"\nUpdateUserErr: \"Ralat: Gagal mengemas kini pengguna panel, {{ .err }}\"\nUpdateSuccessful: \"Kemas kini berjaya\"\nUpdateUserResult: \"Pengguna panel: {{ .name }}\"\nUpdatePasswordRead: \"Ralat: Gagal membaca maklumat kata laluan panel, {{ .err }}\"\nUpdatePasswordNull: \"Ralat: Kata laluan panel kosong\"\nUpdateUPasswordBlank: \"Ralat: Kata laluan panel mengandungi ruang kosong\"\nUpdatePasswordFormat: \"Ralat: Kata laluan panel hanya menyokong huruf, nombor, dan aksara khas (@#$%*_,.?), dengan panjang 8-30 aksara.\"\nUpdatePasswordLen: \"Ralat: Sila masukkan kata laluan lebih panjang daripada 6 aksara\"\nUpdatePasswordRe: \"Sahkan kata laluan:\"\nUpdatePasswordErr: \"Ralat: Gagal mengemas kini kata laluan panel, {{ .err }}\"\nUpdatePasswordSame: \"Ralat: Kedua-dua kata laluan tidak sepadan, sila semak dan cuba lagi\"\nUpdatePasswordResult: \"Kata laluan panel: {{ .name }}\"\nUpdatePortFormat: \"Ralat: Nombor port yang dimasukkan mesti antara 1 hingga 65535\"\nUpdatePortUsed: \"Ralat: Nombor port sudah digunakan, sila semak dan cuba lagi\"\nUpdatePortErr: \"Ralat: Gagal mengemas kini port panel, {{ .err }}\"\nUpdatePortResult: \"Port Panel: {{ .name }}\"\nUpdatePortFirewallAdd: \"Gagal menambah peraturan port firewall, {{ .err }}, sila tambah port {{ .name }} secara manual ke dalam peraturan firewall.\"\nUpdatePortFirewallDel: \"Ralat: Gagal memadam port firewall, {{ .err }}\"\nUpdatePortFirewallReload: \"Gagal memuat semula firewall, {{ .err }}, sila muat semula firewall secara manual.\"\nUserInfo: \"Dapatkan maklumat panel\"\nUserInfoAddr: \"Alamat panel: \"\nUserInfoPassHelp: \"Petua: Untuk menukar kata laluan, anda boleh menjalankan arahan: \"\nDBConnErr: \"Ralat: Gagal memulakan sambungan pangkalan data, {{ .err }}\"\nSystemVersion: \"Versi: \"\nSystemMode: \"Mod: \"\n\n#cluster\nErrMasterDelete: \"Tidak dapat menghapus nod utama, sila hapuskan nod perantara dahulu.\"\nClusterNameIsExist: \"Nama kluster sudah wujud.\"\nAppStatusUnHealthy: \"Pengambilan status aplikasi tidak normal, sila periksa status nod pemasangan dalam senarai nod.\"\nMasterNodePortNotAvailable: \"Pengesahan kesambungan pelabuhan {{ .name }} nod {{ .port }} gagal, sila periksa tetapan firewall/kumpulan keselamatan dan status nod utama.\"\nClusterMasterNotExist: \"Node utama kluster terputus, sila padamkan nod anak.\"\n\n#ssl\nErrReqFailed: \"{{.name}} permintaan gagal: {{ .err }}\"\n\n#command\nName: \"Nama\"\nCommand: \"Arahan\"\n"
  },
  {
    "path": "core/i18n/lang/pt-BR.yaml",
    "content": "ErrInvalidParams: 'Requisição inválida: {{ .detail }}'\nErrTokenParse: 'Erro de token: {{ .detail }}'\nErrInitialPassword: 'Senha original incorreta'\nErrInternalServer: 'Erro interno: {{ .detail }}'\nErrRecordExist: 'Registro já existe'\nErrRecordNotFound: 'Registro não encontrado'\nErrStructTransform: 'Erro de conversão: {{ .err }}'\nErrNotLogin: 'Sem sessão: {{ .detail }}'\nErrSessionDataNotFound: \"A sessão atual expirou\"\nErrSessionDataFormat: \"O formato dos dados da sessão atual está anormal\"\nErrPasswordExpired: 'Senha expirada: {{ .detail }}'\nErrNotSupportType: 'Tipo não suportado: {{ .name }}'\nErrProxy: 'Requisição falhou: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API desativada: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Chave API inválida: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP sem permissão: {{ .detail }}'\nErrApiConfigDisable: 'API bloqueada: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Timestamp inválido: {{ .detail }}'\n\n# request\nErrNoSuchHost: \"Servidor solicitado não encontrado {{ .err }}\"\nErrHttpReqNotFound: \"Recurso solicitado não encontrado {{ .err }}\"\nErrHttpReqFailed: \"Falha na requisição {{ .err }}\"\nErrHttpReqTimeOut: \"Tempo de requisição esgotado {{ .err }}\"\nErrCreateHttpClient: \"Falha ao criar a requisição {{ .err }}\"\nErrProxySetting: \"Informação do servidor proxy indisponível {{ .err }}, verifique e tente novamente\"\nErrEntranceFormat: \"A entrada de segurança {{ .name }} não é suportada atualmente. Por favor verifique e tente novamente\"\n\n# common\nErrDemoEnvironment: 'Indisponível em demo'\nErrCmdTimeout: 'Comando expirou'\nErrEntrance: \"Erro nas informações de entrada de segurança, por favor, verifique e tente novamente\"\nErrGroupIsDefault: \"Grupo padrão não pode ser excluído\"\nErrGroupIsInUse: \"O grupo está em uso e não pode ser excluído.\"\nErrLocalDelete: \"O nó local não pode ser excluído\"\nErrPortInUsed: \"A porta {{ .name }} já está em uso\"\nErrInternalServerKey: \"Erro interno do servidor:\"\nMasterNode: \"Nó Mestre\"\n\n# app\nCustomAppStoreFileValid: \"O pacote da loja de aplicativos deve estar no formato .tar.gz\"\nErrFileNotFound: \"Arquivo {{ .name }} não encontrado\"\nAppBackup: 'Backup de aplicação'\nAppBackupPush: 'Transferir arquivo de backup de aplicação {{.file}} para o nó {{ .name }}'\nErrSourceTargetSame: 'O nó de origem e o nó de destino não podem ser os mesmos'\nAppInstall: 'Instalar aplicação {{ .name }} no nó {{ .targetNode }}'\nAppInstallCheck: 'Verificar ambiente de instalação da aplicação'\n\n# backup\nErrBackupInUsed: 'Conta de backup em uso por tarefa'\nErrBackupCheck: 'Teste de conexão falhou: {{ .err }}'\nErrBackupLocal: \"A conta de backup do servidor local não suporta essa operação\"\nErrBackupPublic: \"A conta de backup detectada não é pública, por favor verifique e tente novamente\"\nErrOSSConn: \"Não foi possível obter a versão mais recente, verifique a conectividade externa do servidor.\"\n\n#license\nLicenseCheck: 'Verificar se a licença está disponível'\nErrLicenseInUsed: 'Esta licença já está vinculada. Por favor, verifique e tente novamente'\nErrLicenseExpired: 'Esta licença expirou. Por favor, verifique e tente novamente'\nErrLicense: \"Formato de licença inválido, por favor verifique e tente novamente\"\nErrLicenseCheck: \"Falha na verificação da licença, por favor verifique e tente novamente\"\nErrXpackVersion: \"Falha na verificação da licença, esta licença é restrita a uma versão específica, por favor verifique e tente novamente\"\nErrLicenseSave: \"Falha ao salvar as informações da licença {{ .err }}, por favor tente novamente\"\nErrLicenseSync: \"Falha ao sincronizar as informações da licença, nenhuma informação foi encontrada no banco de dados\"\nErrLicenseExist: \"Este registro de licença já existe. Você pode ir diretamente para a página de licenças para vincular nós.\"\nErrXpackNotFound: \"Esta parte é uma funcionalidade profissional, por favor importe a licença em Configurações do Painel - Licença.\"\nErrXpackExceptional: \"Esta parte é uma funcionalidade profissional, por favor sincronize a licença em Configurações do Painel - Licença.\"\nErrXpackLost: \"A licença atingiu o número máximo de tentativas, acesse [Configurações do Painel][Licença] e clique no botão de sincronização manual.\"\nErrDeviceLost: \"Arquivos necessários para a verificação da licença estão faltando, por favor verifique e tente novamente\"\nErrDeviceErr: \"O ambiente atual não corresponde ao ambiente de importação da licença. Por favor, edite a licença e reimporte\"\nErrXpackTimeout: \"Requisição expirou, a conexão de rede pode estar instável, por favor tente novamente mais tarde\"\nErrUnbindMaster: \"Foram encontrados nós na gestão de nós, remova-os primeiro e tente novamente\"\nErrFreeNodeLimit: \"O número de nós da versão comunitária atingiu o limite gratuito, por favor acesse www.lxware.cn/1panel para comprar\"\nErrNodeBound: \"Esta licença já está vinculada a outro nó, por favor verifique e tente novamente\"\nErrNodeBoundDelete: \"Esta licença está vinculada e não suporta operações de exclusão. Por favor, verifique e tente novamente\"\nErrNodeBoundLimit: \"O nó gratuito atual atingiu seu limite, por favor verifique e tente novamente\"\nErrLicenseFree: \"Nós gratuitos só podem ser usados quando a licença está devidamente vinculada a um nó. Por favor verifique e tente novamente\"\nErrLicenseUnbind: \"Nós da Edição Comunitária detectados para esta licença. Por favor desvincule em [Configurações do Painel - Licença] e tente novamente\"\nErrNoSuchNode: \"As informações deste nó não foram encontradas, por favor verifique e tente novamente\"\nErrNodeUnbind: \"Nó fora do escopo da licença detectado, por favor verifique e tente novamente\"\nErrNodeBind: \"Este nó já está vinculado a uma licença, por favor verifique e tente novamente\"\nErrNodeLocalRollback: \"O nó principal não suporta rollback direto. Por favor, execute manualmente o comando '1pctl restore' para fazer o rollback\"\nErrIntlLicense: \"A versão atual ainda não oferece suporte à importação de licença da edição internacional. Em breve\"\n\nInvalidRequestBodyType: \"Formato do corpo da requisição inválido. Verifique se o conteúdo está no formato correto e tente novamente\"\nInvalidLicenseCodeType: \"Formato do código de licença inválido. Verifique e tente novamente\"\nLicenseNotFoundType: \"Licença não encontrada. Não há registros correspondentes no sistema para a licença fornecida. Verifique e tente novamente\"\nLicenseRevokedType: \"A licença solicitada foi revogada e não pode mais ser usada. Verifique e tente novamente\"\nLicenseExpiredType: \"A licença expirou. Renove-a ou reimporte-a na seção Configurações do Painel - Licença antes de tentar novamente\"\nLicenseProductMismatchType: \"A licença não corresponde ao produto ou serviço solicitado\"\nInvalidAssigneeType: \"Informações do usuário ou dispositivo alvo para atribuição de licença inválidas. Verifique e tente novamente\"\nLicenseUsageNotFoundType: \"Nenhum registro de uso encontrado. Esta licença ainda não foi ativada ou usada. Verifique e tente novamente\"\nLicenseUsageLimitExceededType: \"Esta licença já está vinculada a outro nó. Verifique e tente novamente\"\n\n# alert\nErrAlertSync: \"Erro na sincronização das informações de alerta, verifique e tente novamente\"\n\n#task\nTaskStart: \"{{ .name }} tarefa iniciada [START]\"\nTaskEnd: \"{{ .name }} tarefa finalizada [COMPLETED]\"\nTaskFailed: \"{{ .name }} tarefa falhou\"\nTaskTimeout: \"{{ .name }} tempo esgotado\"\nTaskSuccess: \"{{ .name }} tarefa bem-sucedida\"\nTaskRetry: \"Iniciando tentativa {{ .name }}\"\nSubTaskSuccess: \"{{ .name }} bem-sucedido\"\nSubTaskFailed: \"{{ .name }} falhou: {{ .err }}\"\nTaskInstall: \"Instalar\"\nTaskUpgrade: \"Atualizar\"\nTaskSync: 'Sincronizar'\nTaskSyncForNode: \"Sincronizar Dados do Nó\"\nTaskBackup: \"Backup\"\nSuccessStatus: \"{{ .name }} bem-sucedido\"\nFailedStatus: \"{{ .name }} falhou {{ .err }}\"\nStart: \"Iniciar\"\nSubTask: \"Subtarefa\"\nSkip: \"Ignorar erros e continuar...\"\nPushAppInstallTaskToNode: \"Enviar tarefa de instalação de aplicativo para o nó [{{ .name }}]\"\nTaskPush: \"Enviar\"\nAppInstallTask: \"Tarefa de instalação de aplicativo\"\nPushAppFailed: \"Falha ao enviar tarefa de instalação de aplicativo\"\nSuccess: \"Sucesso\"\n\n#script\nScriptLibrary: \"Biblioteca de Scripts\"\nRemoteScriptLibrary: \"Biblioteca de Scripts Remota\"\nScriptSyncSkip: \"A biblioteca de scripts atual já é a versão mais recente\"\nDownloadData: \"Baixando arquivo data.yaml da biblioteca de scripts\"\nDownloadPackage: \"Baixando pacote da biblioteca de scripts\"\nAnalyticCompletion: \"Análise concluída, agora sincronizando com o banco de dados...\"\n\nNode: \"Nó\"\nSyncNode: \"Sincronizar dados para o nó {{ .name }}\"\nLocalName: \"O nome 'local' é usado apenas para identificação local do sistema\"\nSyncPackageData: \"Empacotar dados de sincronização\"\nSyncPackageEncrypt: \"Criptografia de pacote de dados\"\nSyncRequest: \"Solicitar API de sincronização de nó\"\nSyncFailedRetry: \"Tempo esgotado na sincronização de dados do nó (tentativa {{ .index }}), tentando novamente...\"\nSyncFailed: \"Falha na sincronização, por favor sincronize manualmente na lista de nós\"\nSyncSystemProxy: \"Configurações de Proxy do Sistema\"\nSyncScripts: \"Biblioteca de Scripts\"\nSyncBackupAccounts: \"Contas de Backup\"\nSyncAlertSetting: \"Configurações de Alerta\"\nSyncCustomApp: \"Aplicativo Personalizado\"\nSyncLanguage: \"Idioma do Sistema\"\nSyncEdition: \"Região de Operação\"\n\n#upgrade node\nNodeUpgrade: \"Atualizar nó {{ .name }}\"\nUpgradeCheck: \"Verificar atualizações do nó\"\nUpgradeCheckLocal: \"Nós locais não suportam atualização em lote, pulando...\"\nUpgradeCheckLatest: \"O nó já está na versão mais recente, pulando...\"\nNewSSHClient: \"Inicializando a conexão SSH\"\nBackupBeforeUpgrade: \"Fazer backup dos dados antes da atualização\"\nUploadUpgradeFile: \"Distribuir arquivos necessários para a atualização\"\nRestartAfterUpgrade: \"Iniciar serviço após a atualização\"\n\n#add node\nMasterData: \"Dados do Nó Mestre\"\nLoadSftpClient: \"Carregar Cliente SFTP\"\nPackageMasterData: \"Gerar pacote de backup do nó mestre\"\nUploadBackup: \"Carregar dados de backup\"\nMvBackup: \"Mover dados para diretório de backup\"\nTaskAddNode: \"Adicionar nó\"\nLoadNodeArch: \"Obter informações de arquitetura do nó\"\nLoadNodeArchDetail: \"Arquitetura do nó mestre detectada: {{ .local }}, arquitetura do nó filho: {{ .node }}\"\nLoadNodeUpgradeDetail: \"Usando o diretório de instalação histórico da versão v1: {{ .baseDir }}, porta de escuta do serviço: {{ .port }}\"\nSyncAgentBaseInfo: \"Sincronizando dados básicos do nó\"\nGenerateSSLInfo: \"Gerando informações SSL do nó\"\nConnInfoNotMatch: \"As informações de conexão não coincidem\"\nMakeAgentPackage: \"Gerando pacote de instalação do nó\"\nSendAgent: \"Enviando pacote de instalação do nó\"\nStartService: \"Iniciando serviço\"\nNoBackupNode: \"O nó de backup está vazio atualmente. Selecione um nó de backup para salvar e tente novamente\"\n\n#cmd\nAppVersion: \"Versão do aplicativo\"\nAppCommands: \"Comandos relacionados ao aplicativo\"\nAppInit: \"Inicializar aplicativo\"\nAppKeyVal: \"Chave do aplicativo (somente suporta inglês)\"\nAppCreateFileErr: \"Falha ao criar o arquivo {{ .name }} {{ .err }}\"\nAppCreateDirErr: \"Falha ao criar a pasta {{ .name }} {{ .err }}\"\nAppMissKey: \"Chave do aplicativo ausente, use -k para especificar\"\nAppMissVersion: \"Versão do aplicativo ausente, use -v para especificar\"\nAppVersionExist: \"A versão já existe\"\nAppCreateSuccessful: \"Criação bem-sucedida\"\nAppWriteErr: \"Falha ao escrever no arquivo {{ .name }} {{ .err }}\"\nSudoHelper: \"Por favor, use {{ .cmd }} ou altere para o usuário root\"\nListenIPCommands: \"Trocar IP de escuta\"\nListenIPv4: \"Escutando em IPv4\"\nListenIPv6: \"Escutando em IPv6\"\nListenChangeSuccessful: \"Troca bem-sucedida Agora escutando em {{ .value }}\"\nResetCommands: \"Redefinir informações do sistema\"\nResetMFA: \"Cancelar autenticação de dois fatores do 1Panel\"\nResetHttps: \"Cancelar login https do 1Panel\"\nResetEntrance: \"Cancelar entrada segura do 1Panel\"\nResetIPs: \"Cancelar restrições de IP autorizado do 1Panel\"\nResetDomain: \"Cancelar vinculação de domínio do 1Panel\"\nResetPasskey: \"Redefinir passkeys do 1Panel\"\nRestoreCommands: \"Restaurar serviço e dados do 1Panel\"\nRestoreNoSuchFile: \"Nenhum arquivo disponível para restauração\"\nRestoreStep1: \"(1/5) Iniciando a restauração do serviço e dados do 1Panel a partir do diretório {{ .name }}...\"\nRestoreStep2: \"(2/5) Restauração do binário do 1Panel bem-sucedida\"\nRestoreStep3: \"(3/5) Restauração dos scripts do 1Panel bem-sucedida\"\nRestoreStep4: \"(4/5) Restauração do serviço do 1Panel bem-sucedida\"\nRestoreStep5: \"(5/5) Restauração dos dados do 1Panel bem-sucedida\"\nRestoreSuccessful: \"Reversão bem-sucedida Revertido para {{ .version }}, reiniciando serviço, por favor aguarde...\"\nUpdateCommands: \"Atualizar informações do painel\"\nUpdateUser: \"Atualizar usuário do painel\"\nUpdatePassword: \"Atualizar senha do painel\"\nUpdatePort: \"Atualizar porta do painel\"\nUpdateUserNull: \"Erro: usuário do painel está vazio\"\nUpdateUserBlank: \"Erro: o usuário do painel contém espaços\"\nUpdateUserFormat: \"Erro: Formato de usuário do painel inválido Apenas suporta inglês, chinês, números e _, comprimento de 3-30.\"\nUpdateUserErr: \"Erro: Falha ao atualizar o usuário do painel, {{ .err }}\"\nUpdateSuccessful: \"Atualização bem-sucedida\"\nUpdateUserResult: \"Usuário do painel: {{ .name }}\"\nUpdatePasswordRead: \"Erro: Falha ao ler as informações da senha do painel, {{ .err }}\"\nUpdatePasswordNull: \"Erro: A senha do painel está vazia\"\nUpdateUPasswordBlank: \"Erro: A senha do painel contém espaços\"\nUpdatePasswordFormat: \"Erro: A senha do painel suporta apenas letras, números e caracteres especiais (@#$%*_,.?), com comprimento de 8-30.\"\nUpdatePasswordLen: \"Erro: Por favor, insira uma senha com mais de 6 caracteres\"\nUpdatePasswordRe: \"Confirmar senha:\"\nUpdatePasswordErr: \"Erro: Falha ao atualizar a senha do painel, {{ .err }}\"\nUpdatePasswordSame: \"Erro: As duas senhas não correspondem, por favor, verifique e tente novamente\"\nUpdatePasswordResult: \"Senha do painel: {{ .name }}\"\nUpdatePortFormat: \"Erro: O número da porta deve estar entre 1 e 65535\"\nUpdatePortUsed: \"Erro: O número da porta já está em uso, por favor, verifique e tente novamente\"\nUpdatePortErr: \"Erro: Falha ao atualizar a porta do painel, {{ .err }}\"\nUpdatePortResult: \"Porta do painel: {{ .name }}\"\nUpdatePortFirewallAdd: \"Falha ao adicionar regra de porta ao firewall, {{ .err }}, por favor, adicione manualmente a porta {{ .name }} às regras do firewall.\"\nUpdatePortFirewallDel: \"Erro: Falha ao excluir a porta do firewall, {{ .err }}\"\nUpdatePortFirewallReload: \"Falha ao recarregar o firewall, {{ .err }}, por favor, recarregue o firewall manualmente.\"\nUserInfo: \"Obter informações do painel\"\nUserInfoAddr: \"Endereço do painel: \"\nUserInfoPassHelp: \"Dica: Para alterar a senha, você pode executar o comando: \"\nDBConnErr: \"Erro: Falha ao inicializar a conexão com o banco de dados, {{ .err }}\"\nSystemVersion: \"versão: \"\nSystemMode: \"modo: \"\n\n#mobile app\nErrVerifyToken: 'Erro de verificação do token, por favor, reinicie e escaneie novamente.'\nErrInvalidToken: 'Token inválido, por favor, reinicie e escaneie novamente.'\nErrExpiredToken: 'O token expirou, por favor, reinicie e escaneie novamente.'\n\n#cluster\nErrMasterDelete: \"Não é possível excluir o nó mestre, exclua os nós escravos primeiro.\"\nClusterNameIsExist: \"O nome do cluster já existe.\"\nAppStatusUnHealthy: \"A aquisição do status do aplicativo está anormal, verifique o status dos nós de instalação na lista de nós.\"\nMasterNodePortNotAvailable: \"A verificação de conectividade da porta {{ .port }} do nó {{ .name }} falhou, verifique as configurações de firewall/grupo de segurança e o status do nó mestre.\"\nClusterMasterNotExist: \"O nó mestre do cluster está desconectado, por favor, exclua os nós filhos.\"\n\n#ssl\nErrReqFailed: \"{{.name}} solicitação falhou: {{ .err }}\"\n\n#command\nName: \"Название\"\nCommand: \"Команда\"\n"
  },
  {
    "path": "core/i18n/lang/ru.yaml",
    "content": "ErrInvalidParams: 'Неверный запрос: {{ .detail }}'\nErrTokenParse: 'Ошибка токена: {{ .detail }}'\nErrInitialPassword: 'Исходный пароль неверен'\nErrInternalServer: 'Внутренняя ошибка: {{ .detail }}'\nErrRecordExist: 'Запись уже существует'\nErrRecordNotFound: 'Запись не найдена'\nErrStructTransform: 'Ошибка преобразования: {{ .err }}'\nErrNotLogin: 'Не авторизован: {{ .detail }}'\nErrSessionDataNotFound: \"Текущая сессия истекла\"\nErrSessionDataFormat: \"Ненормальный формат данных текущей сессии\"\nErrPasswordExpired: 'Пароль истёк: {{ .detail }}'\nErrNotSupportType: 'Тип не поддерживается: {{ .name }}'\nErrProxy: 'Запрос не удался: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API отключено: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Неверный API-ключ: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP не в списке: {{ .detail }}'\nErrApiConfigDisable: 'Доступ по API запрещён: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Неверная метка времени: {{ .detail }}'\n\n# request\nErrNoSuchHost: \"Не удалось найти запрашиваемый сервер {{ .err }}\"\nErrHttpReqNotFound: \"Не удалось найти запрашиваемый ресурс {{ .err }}\"\nErrHttpReqFailed: \"Ошибка запроса {{ .err }}\"\nErrHttpReqTimeOut: \"Время ожидания запроса истекло {{ .err }}\"\nErrCreateHttpClient: \"Ошибка создания запроса {{ .err }}\"\nErrProxySetting: \"Информация о прокси-сервере недоступна {{ .err }}, проверьте и повторите попытку\"\nErrEntranceFormat: \"Защищенный вход {{ .name }} в настоящее время не поддерживается. Пожалуйста, проверьте и повторите попытку\"\n\n# common\nErrDemoEnvironment: 'Недоступно в демо'\nErrCmdTimeout: 'Команда завершилась по тайм-ауту'\nErrEntrance: \"Ошибка информации о безопасном входе, проверьте и повторите попытку\"\nErrGroupIsDefault: \"Группу по умолчанию нельзя удалить\"\nErrGroupIsInUse: \"Группа используется и не может быть удалена.\"\nErrLocalDelete: \"Локальный узел нельзя удалить\"\nErrPortInUsed: \"Порт {{ .name }} уже используется\"\nErrInternalServerKey: \"Внутренняя ошибка сервера:\"\nMasterNode: \"Главный узел\"\n\n# app\nCustomAppStoreFileValid: \"Пакет магазина приложений должен быть в формате .tar.gz\"\nErrFileNotFound: \"Файл {{ .name }} не найден\"\nAppBackup: 'Резервная копия приложения'\nAppBackupPush: 'Передать файл резервной копии приложения {{.file}} на узел {{ .name }}'\nErrSourceTargetSame: 'Исходный узел и целевой узел не могут быть одинаковыми'\nAppInstall: 'Установить приложение {{ .name }} на узел {{ .targetNode }}'\nAppInstallCheck: 'Проверить среду установки приложения'\n\n# backup\nErrBackupInUsed: 'Аккаунт бэкапа занят задачей'\nErrBackupCheck: 'Проверка подключения не удалась: {{ .err }}'\nErrBackupLocal: \"Локальная учетная запись резервного копирования не поддерживает эту операцию\"\nErrBackupPublic: \"Обнаружено, что эта учетная запись резервного копирования не является публичной, проверьте и повторите попытку\"\nErrOSSConn: \"Не удалось получить последнюю версию, проверьте подключение сервера к внешней сети.\"\n\n#license\nLicenseCheck: 'Проверить доступность лицензии'\nErrLicenseInUsed: 'Лицензия уже привязана. Пожалуйста, проверьте и повторите попытку'\nErrLicenseExpired: 'Срок действия лицензии истек. Пожалуйста, проверьте и повторите попытку'\nErrLicense: \"Неверный формат лицензии, проверьте и повторите попытку\"\nErrLicenseCheck: \"Ошибка проверки лицензии, проверьте и повторите попытку\"\nErrXpackVersion: \"Ошибка проверки лицензии, лицензия ограничена по версии, проверьте и повторите попытку\"\nErrLicenseSave: \"Ошибка сохранения информации о лицензии {{ .err }}, повторите попытку\"\nErrLicenseSync: \"Ошибка синхронизации информации о лицензии, информация не найдена в базе данных\"\nErrLicenseExist: \"Данная лицензия уже существует. Вы можете перейти на страницу лицензий для привязки узлов.\"\nErrXpackNotFound: \"Эта часть доступна только в профессиональной версии, сначала импортируйте лицензию в разделе Настройки панели - Лицензия.\"\nErrXpackExceptional: \"Эта часть доступна только в профессиональной версии, сначала синхронизируйте лицензию в разделе Настройки панели - Лицензия.\"\nErrXpackLost: \"Лицензия достигла максимального количества попыток, перейдите в [Настройки панели][Лицензия] и нажмите кнопку синхронизации вручную.\"\nErrDeviceLost: \"Необходимые файлы для проверки лицензии отсутствуют, пожалуйста, проверьте и попробуйте снова\"\nErrDeviceErr: \"Текущая среда не соответствует среде импорта лицензии. Отредактируйте лицензию и повторите импорт\"\nErrXpackTimeout: \"Время ожидания запроса истекло, возможно нестабильное сетевое соединение, повторите попытку позже\"\nErrUnbindMaster: \"В управлении узлами обнаружены узлы, сначала удалите их и повторите попытку\"\nErrFreeNodeLimit: \"Количество узлов в версии сообщества достигло бесплатного лимита, посетите www.lxware.cn/1panel для покупки\"\nErrNodeBound: \"Эта лицензия уже связана с другим узлом, проверьте и повторите попытку\"\nErrNodeBoundDelete: \"Эта лицензия привязана и не поддерживает операции удаления. Пожалуйста, проверьте и попробуйте снова\"\nErrNodeBoundLimit: \"Текущий бесплатный узел достиг предела, пожалуйста, проверьте и попробуйте снова\"\nErrLicenseFree: \"Бесплатные узлы доступны только при правильной привязке лицензии к узлу. Проверьте и повторите попытку\"\nErrLicenseUnbind: \"Обнаружены узлы Community Edition для этой лицензии. Отвяжите в [Настройки панели - Лицензия] и повторите попытку\"\nErrNoSuchNode: \"Информация об узле не найдена, проверьте и повторите попытку\"\nErrNodeUnbind: \"Обнаружен узел вне области действия лицензии, проверьте и повторите попытку\"\nErrNodeBind: \"Этот узел уже связан с лицензией, проверьте и повторите попытку\"\nErrNodeLocalRollback: \"Основной узел не поддерживает прямой откат. Пожалуйста, вручную выполните команду '1pctl restore' для отката\"\nErrIntlLicense: \"Текущая версия пока не поддерживает импорт международной лицензии. Скоро будет доступно\"\n\nInvalidRequestBodyType: \"Неверный формат тела запроса. Проверьте, соответствует ли содержимое требуемому формату, и повторите попытку\"\nInvalidLicenseCodeType: \"Указан неверный формат лицензионного кода. Проверьте и повторите попытку\"\nLicenseNotFoundType: \"Лицензия не найдена. В системе нет записей, соответствующих указанной лицензии. Проверьте и повторите попытку\"\nLicenseRevokedType: \"Запрошенная лицензия аннулирована и больше не может быть использована. Проверьте и повторите попытку\"\nLicenseExpiredType: \"Срок действия лицензии истек. Продлите ее или повторно импортируйте в разделе Настройки панели - Лицензия перед повторной попыткой\"\nLicenseProductMismatchType: \"Лицензия не соответствует запрошенному продукту или услуге\"\nInvalidAssigneeType: \"Некорректная информация о целевом пользователе или устройстве для назначения лицензии. Проверьте и повторите попытку\"\nLicenseUsageNotFoundType: \"Записи об использовании отсутствуют. Эта лицензия еще не активирована или не использовалась. Проверьте и повторите попытку\"\nLicenseUsageLimitExceededType: \"Эта лицензия уже привязана к другому узлу. Проверьте и повторите попытку\"\n\n# alert\nErrAlertSync: \"Ошибка синхронизации информации об оповещениях, пожалуйста, проверьте и повторите попытку\"\n\n# task\nTaskStart: \"{{ .name }} задача начата [START]\"\nTaskEnd: \"{{ .name }} задача завершена [COMPLETED]\"\nTaskFailed: \"{{ .name }} задача не выполнена\"\nTaskTimeout: \"{{ .name }} истекло время\"\nTaskSuccess: \"{{ .name }} задача выполнена успешно\"\nTaskRetry: \"Начало повторной попытки {{ .name }}\"\nSubTaskSuccess: \"{{ .name }} успешно\"\nSubTaskFailed: \"{{ .name }} не удалось: {{ .err }}\"\nTaskInstall: \"Установить\"\nTaskUpgrade: \"Обновить\"\nTaskSync: 'Синхронизация'\nTaskSyncForNode: \"Синхронизировать Данные Узла\"\nTaskBackup: \"Резервная копия\"\nSuccessStatus: \"{{ .name }} успешно\"\nFailedStatus: \"{{ .name }} не удалось {{ .err }}\"\nStart: \"Начать\"\nSubTask: \"Подзадача\"\nSkip: \"Пропустить ошибки и продолжить...\"\nTaskPush: \"Отправить\"\nAppInstallTask: \"Задача установки приложения\"\nPushAppFailed: \"Не удалось отправить задачу установки приложения\"\nSuccess: \"Успешно\"\n\n#script\nScriptLibrary: \"Библиотека скриптов\"\nRemoteScriptLibrary: \"Удаленная Библиотека Скриптов\"\nScriptSyncSkip: \"Текущая библиотека скриптов уже является последней версией\"\nDownloadData: \"Загрузка файла библиотеки скриптов data.yaml\"\nDownloadPackage: \"Загрузка архива библиотеки скриптов\"\nAnalyticCompletion: \"Анализ завершён, начинается синхронизация с базой данных...\"\n\nNode: \"Узел\"\nSyncNode: \"Синхронизировать данные с узлом {{ .name }}\"\nLocalName: \"Имя 'local' используется только для локальной идентификации в системе\"\nSyncPackageData: \"Упаковка данных синхронизации\"\nSyncPackageEncrypt: \"Шифрование пакета данных\"\nSyncRequest: \"Запрос API синхронизации узла\"\nSyncFailedRetry: \"Таймаут синхронизации данных узла (попытка {{ .index }}), повторная попытка...\"\nSyncFailed: \"Ошибка синхронизации, выполните ручную синхронизацию в списке узлов\"\nSyncSystemProxy: \"Настройки системного прокси\"\nSyncScripts: \"Библиотека скриптов\"\nSyncBackupAccounts: \"Резервные аккаунты\"\nSyncAlertSetting: \"Настройки оповещений\"\nSyncCustomApp: \"Пользовательское приложение\"\nSyncLanguage: \"Язык системы\"\nSyncEdition: \"Регион работы\"\n\n#upgrade node\nNodeUpgrade: \"Обновление узла {{ .name }}\"\nUpgradeCheck: \"Проверить обновления узла\"\nUpgradeCheckLocal: \"Локальные узлы не поддерживают массовое обновление, пропуск...\"\nUpgradeCheckLatest: \"Узел уже имеет последнюю версию, пропуск...\"\nNewSSHClient: \"Инициализация SSH-соединения\"\nBackupBeforeUpgrade: \"Резервное копирование данных перед обновлением\"\nUploadUpgradeFile: \"Распределение файлов обновления\"\nRestartAfterUpgrade: \"Запуск службы после обновления\"\n\n#add node\nMasterData: \"Данные главного узла\"\nLoadSftpClient: \"Загрузить SFTP-клиент\"\nPackageMasterData: \"Создать архив резервной копии главного узла\"\nUploadBackup: \"Загрузить резервные данные\"\nMvBackup: \"Переместить данные в резервный каталог\"\nTaskAddNode: \"Добавить узел\"\nLoadNodeArch: \"Получить информацию об архитектуре узла\"\nLoadNodeArchDetail: \"Обнаружена архитектура главного узла: {{ .local }}, архитектура дочернего узла: {{ .node }}\"\nLoadNodeUpgradeDetail: \"Используется каталог установки исторической версии v1: {{ .baseDir }}, порт прослушивания службы: {{ .port }}\"\nSyncAgentBaseInfo: \"Синхронизировать базовую информацию узла\"\nGenerateSSLInfo: \"Сгенерировать SSL информацию узла\"\nConnInfoNotMatch: \"Информация о соединении не совпадает\"\nMakeAgentPackage: \"Сгенерировать установочный пакет узла\"\nSendAgent: \"Отправить установочный пакет узла\"\nStartService: \"Запустить сервис\"\nNoBackupNode: \"Резервный узел в настоящее время пуст. Выберите резервный узел для сохранения и повторите попытку\"\n\n#cmd\nAppVersion: \"Версия приложения\"\nAppCommands: \"Команды приложения\"\nAppInit: \"Инициализация приложения\"\nAppKeyVal: \"Ключ приложения (поддерживаются только английские символы)\"\nAppCreateFileErr: \"Не удалось создать файл {{ .name }} {{ .err }}\"\nAppCreateDirErr: \"Не удалось создать папку {{ .name }} {{ .err }}\"\nAppMissKey: \"Отсутствует ключ приложения, используйте -k для указания\"\nAppMissVersion: \"Отсутствует версия приложения, используйте -v для указания\"\nAppVersionExist: \"Версия уже существует\"\nAppCreateSuccessful: \"Успешно создано\"\nAppWriteErr: \"Не удалось записать файл {{ .name }} {{ .err }}\"\nSudoHelper: \"Пожалуйста, используйте {{ .cmd }} или переключитесь на пользователя root\"\nListenIPCommands: \"Переключить IP для прослушивания\"\nListenIPv4: \"Прослушивание IPv4\"\nListenIPv6: \"Прослушивание IPv6\"\nListenChangeSuccessful: \"Переключено успешно Теперь прослушиваем {{ .value }}\"\nResetCommands: \"Сбросить информацию о системе\"\nResetMFA: \"Отменить двухфакторную аутентификацию 1Panel\"\nResetHttps: \"Отменить вход в 1Panel через https\"\nResetEntrance: \"Отменить безопасный вход 1Panel\"\nResetIPs: \"Отменить ограничения авторизованных IP для 1Panel\"\nResetDomain: \"Отменить привязку домена доступа 1Panel\"\nResetPasskey: \"Сбросить passkey 1Panel\"\nRestoreCommands: \"Откатить сервисы и данные 1Panel\"\nRestoreNoSuchFile: \"Нет доступных для отката файлов\"\nRestoreStep1: \"（1/5）Начинаем откат сервисов и данных 1Panel из каталога {{ .name }}...\"\nRestoreStep2: \"（2/5）Откат бинарного файла 1Panel завершён\"\nRestoreStep3: \"（3/5）Откат скриптов 1Panel завершён\"\nRestoreStep4: \"（4/5）Откат сервисов 1Panel завершён\"\nRestoreStep5: \"（5/5）Откат данных 1Panel завершён\"\nRestoreSuccessful: \"Откат успешен Откатился к {{ .version }}, перезапускаю службу, пожалуйста, подождите...\"\nUpdateCommands: \"Изменить информацию панели\"\nUpdateUser: \"Изменить пользователя панели\"\nUpdatePassword: \"Изменить пароль панели\"\nUpdatePort: \"Изменить порт панели\"\nUpdateUserNull: \"Ошибка: введён пользователь панели пуст\"\nUpdateUserBlank: \"Ошибка: введён пользователь панели с пробелами\"\nUpdateUserFormat: \"Ошибка: неверный формат имени пользователя панели Поддерживаются только английские, китайские символы, цифры и _ , длина 3-30 символов\"\nUpdateUserErr: \"Ошибка: не удалось изменить пользователя панели, {{ .err }}\"\nUpdateSuccessful: \"Успешно изменено\"\nUpdateUserResult: \"Пользователь панели: {{ .name }}\"\nUpdatePasswordRead: \"Ошибка: не удалось прочитать информацию о пароле панели, {{ .err }}\"\nUpdatePasswordNull: \"Ошибка: введён пароль пуст\"\nUpdateUPasswordBlank: \"Ошибка: введён пароль с пробелами\"\nUpdatePasswordFormat: \"Ошибка: пароль панели должен содержать только буквы, цифры и специальные символы (@#$%*_,.?) и иметь длину 8-30 символов.\"\nUpdatePasswordLen: \"Ошибка: пароль должен быть длиной не менее 6 символов\"\nUpdatePasswordRe: \"Подтверждение пароля:\"\nUpdatePasswordErr: \"Ошибка: не удалось изменить пароль панели, {{ .err }}\"\nUpdatePasswordSame: \"Ошибка: пароли не совпадают, пожалуйста, проверьте и попробуйте снова\"\nUpdatePasswordResult: \"Пароль панели: {{ .name }}\"\nUpdatePortFormat: \"Ошибка: введённый порт должен быть в пределах от 1 до 65535\"\nUpdatePortUsed: \"Ошибка: этот порт уже занят, пожалуйста, проверьте и попробуйте снова\"\nUpdatePortErr: \"Ошибка: не удалось изменить порт панели, {{ .err }}\"\nUpdatePortResult: \"Порт панели: {{ .name }}\"\nUpdatePortFirewallAdd: \"Не удалось добавить правило фаервола для порта, {{ .err }}, пожалуйста, вручную добавьте порт {{ .name }} в правила фаервола.\"\nUpdatePortFirewallDel: \"Ошибка: не удалось удалить правило фаервола для порта, {{ .err }}\"\nUpdatePortFirewallReload: \"Не удалось перезагрузить фаервол, {{ .err }}, пожалуйста, вручную перезагрузите фаервол.\"\nUserInfo: \"Получить информацию о панели\"\nUserInfoAddr: \"Адрес панели:\"\nUserInfoPassHelp: \"Подсказка: для изменения пароля можно выполнить команду:\"\nDBConnErr: \"Ошибка: не удалось инициализировать соединение с базой данных, {{ .err }}\"\nSystemVersion: \"Версия:\"\nSystemMode: \"Режим:\"\n\n#mobile app\nErrVerifyToken: 'шибка проверки токена, пожалуйста, сбросьте и отсканируйте снова.'\nErrInvalidToken: 'Неверный токен, пожалуйста, сбросьте и отсканируйте снова.'\nErrExpiredToken: 'Токен истек, пожалуйста, сбросьте и отсканируйте снова.'\n\n#cluster\nErrMasterDelete: \"Невозможно удалить основной узел, сначала удалите подчиненные узлы.\"\nClusterNameIsExist: \"Имя кластера уже существует.\"\nAppStatusUnHealthy: \"Получение статуса приложения аномально, пожалуйста, проверьте статус узлов установки в списке узлов.\"\nMasterNodePortNotAvailable: \"Проверка подключения порта {{ .port }} узла {{ .name }} не удалась, пожалуйста, проверьте настройки брандмауэра/группы безопасности и статус главного узла.\"\nClusterMasterNotExist: \"Основной узел кластера отключен, пожалуйста, удалите дочерние узлы.\"\n\n#ssl\nErrReqFailed: \"{{.name}} запрос не удался: {{ .err }}\"\n\n#command\nName: \"Название\"\nCommand: \"Команда\"\n"
  },
  {
    "path": "core/i18n/lang/tr.yaml",
    "content": "ErrInvalidParams: 'İstek geçersiz: {{ .detail }}'\nErrTokenParse: 'Token hatası: {{ .detail }}'\nErrInitialPassword: 'Orijinal şifre yanlış'\nErrInternalServer: 'Sunucu hatası: {{ .detail }}'\nErrRecordExist: 'Kayıt zaten var'\nErrRecordNotFound: 'Kayıt bulunamadı'\nErrStructTransform: 'Dönüşüm hatası: {{ .err }}'\nErrNotLogin: 'Oturum açılmamış: {{ .detail }}'\nErrSessionDataNotFound: \"Mevcut oturumun süresi doldu\"\nErrSessionDataFormat: \"Mevcut oturum verisi biçimi anormal\"\nErrPasswordExpired: 'Şifre süresi doldu: {{ .detail }}'\nErrNotSupportType: 'Bu tür desteklenmiyor: {{ .name }}'\nErrProxy: 'İstek başarısız: {{ .detail }}'\nErrApiConfigStatusInvalid: 'API kapalı: {{ .detail }}'\nErrApiConfigKeyInvalid: 'Geçersiz API anahtarı: {{ .detail }}'\nErrApiConfigIPInvalid: 'IP izinli değil: {{ .detail }}'\nErrApiConfigDisable: 'API erişimi kapalı: {{ .detail }}'\nErrApiConfigKeyTimeInvalid: 'Zaman damgası hatalı: {{ .detail }}'\n\n# request\nErrNoSuchHost: \"İstenen sunucu bulunamıyor {{ .err }}\"\nErrHttpReqNotFound: \"İstenen kaynak bulunamıyor {{ .err }}\"\nErrHttpReqFailed: \"İstek başarısız {{ .err }}\"\nErrHttpReqTimeOut: \"İstek zaman aşımına uğradı {{ .err }}\"\nErrCreateHttpClient: \"İstek oluşturma başarısız {{ .err }}\"\nErrProxySetting: \"Proxy sunucu bilgisi kullanılamıyor {{ .err }}, lütfen kontrol edip tekrar deneyin\"\nErrEntranceFormat: \"{{ .name }} güvenlik girişi şu anda desteklenmiyor. Lütfen kontrol edip tekrar deneyin\"\n\n# common\nErrDemoEnvironment: 'Demo modunda yok'\nErrCmdTimeout: 'Komut zaman aşımına uğradı'\nErrEntrance: \"Güvenlik girişi bilgi hatası, lütfen kontrol edip tekrar deneyin\"\nErrGroupIsDefault: \"Varsayılan grup, silinemez\"\nErrGroupIsInUse: \"Grup kullanımda ve silinemez.\"\nErrLocalDelete: \"Yerel düğüm silinemez\"\nErrPortInUsed: \"{{ .name }} portu zaten kullanımda\"\nErrInternalServerKey: \"İç sunucu hatası:\"\nMasterNode: \"Ana Düğüm\"\n\n# app\nCustomAppStoreFileValid: \"Uygulama mağazası paketi .tar.gz formatında olmalıdır\"\nErrFileNotFound: \"{{ .name }} dosyası mevcut değil\"\nAppBackup: 'Uygulama yedekleme'\nAppBackupPush: 'Uygulama yedek dosyası {{.file}} düğüm {{ .name }} a aktar'\nErrSourceTargetSame: 'Kaynak düğüm ve hedef düğüm aynı olamaz'\nAppInstall: 'Düğüm {{ .targetNode }} üzerine {{ .name }} uygulamasını yükle'\nAppInstallCheck: 'Uygulama kurulum ortamını kontrol et'\n\n# backup\nErrBackupInUsed: 'Yedek hesabı görevde kullanılıyor'\nErrBackupCheck: 'Bağlantı testi başarısız: {{ .err }}'\nErrBackupLocal: \"Yerel sunucu yedekleme hesabı bu işlemi desteklemiyor\"\nErrBackupPublic: \"Bu yedekleme hesabının herkese açık olmadığı tespit edildi, lütfen kontrol edip tekrar deneyin\"\nErrOSSConn: \"En son sürüm alınamıyor, lütfen sunucunun dış ağ bağlantısını kontrol edin.\"\n\n#license\nLicenseCheck: 'Lisansın mevcut olup olmadığını kontrol et'\nErrLicenseInUsed: 'Lisans zaten bağlı. Lütfen kontrol edip tekrar deneyin'\nErrLicenseExpired: 'Lisansın süresi dolmuş. Lütfen kontrol edip tekrar deneyin'\nErrLicense: \"Lisans format hatası, lütfen kontrol edip tekrar deneyin\"\nErrLicenseCheck: \"Lisans doğrulama başarısız, lütfen kontrol edip tekrar deneyin\"\nErrXpackVersion: \"Lisans doğrulama başarısız, bu lisans sürüm sınırlıdır, içe aktarılamıyor, lütfen kontrol edip tekrar deneyin\"\nErrLicenseSave: \"Lisans bilgilerini kaydetme başarısız, hata {{ .err }}, lütfen tekrar deneyin\"\nErrLicenseSync: \"Lisans senkronizasyonu başarısız, veritabanında lisans bilgisi tespit edilmedi\"\nErrLicenseExist: \"Bu lisans kaydı zaten mevcut. Düğüm bağlama için doğrudan lisans sayfasına gidebilirsiniz.\"\nErrXpackNotFound: \"Bu bölüm profesyonel sürüm içindir, lütfen Panel Ayarları - Lisans'ta lisansı içe aktarın\"\nErrXpackExceptional: \"Bu bölüm profesyonel sürüm içindir, lütfen Panel Ayarları - Lisans'ta lisans durumunu senkronize edin\"\nErrXpackLost: \"Lisans maksimum yeniden deneme sayısına ulaştı, lütfen [Panel Ayarları] [Lisans] sayfasına gidin ve profesyonel özelliklerin doğru çalışması için manuel olarak senkronize butonuna tıklayın\"\nErrDeviceLost: \"Lisans doğrulama için gerekli dosyalar eksik, lütfen kontrol edip tekrar deneyin\"\nErrXpackTimeout: \"İstek zaman aşımı, ağ bağlantısı kararsız olabilir, lütfen daha sonra tekrar deneyin\"\nErrUnbindMaster: \"Düğüm yönetiminde düğümler tespit edildi, mevcut lisansın bağı çözülemiyor, lütfen önce düğümleri kaldırın ve tekrar deneyin\"\nErrFreeNodeLimit: \"Topluluk sürümü düğüm limiti ulaşıldı, lütfen www.lxware.cn/1panel adresinden satın alın ve tekrar deneyin\"\nErrNodeBound: \"Bu lisans başka bir düğüme bağlı, lütfen kontrol edip tekrar deneyin\"\nErrNodeBoundDelete: \"Bu lisans bağlı ve silme işlemlerini desteklemiyor. Lütfen kontrol edip tekrar deneyin\"\nErrNodeBoundLimit: \"Mevcut ücretsiz düğüm limitine ulaştı, lütfen kontrol edip tekrar deneyin\"\nErrLicenseFree: \"Ücretsiz düğümler yalnızca lisans bir düğüme düzgün şekilde bağlandığında kullanılabilir. Lütfen doğrulayın ve tekrar deneyin\"\nErrLicenseUnbind: \"Bu lisans için Topluluk Sürümü düğümleri tespit edildi. Lütfen [Panel Ayarları - Lisans]'ta bağı çözün ve tekrar deneyin\"\nErrNoSuchNode: \"Düğüm bilgisi bulunamadı, lütfen kontrol edip tekrar deneyin\"\nErrNodeUnbind: \"Bu düğüm lisans bağlama aralığında değil, lütfen kontrol edip tekrar deneyin\"\nErrNodeBind: \"Bu düğüm zaten bir lisansa bağlı, lütfen kontrol edip tekrar deneyin\"\nErrNodeLocalRollback: \"Ana düğüm doğrudan geri alma desteklemiyor. Lütfen geri almak için '1pctl restore' komutunu manuel olarak çalıştırın\"\nErrIntlLicense: \"Mevcut sürüm uluslararası sürüm lisansının içe aktarılmasını henüz desteklemiyor. Yakında\"\n\nInvalidRequestBodyType: \"Geçersiz istek gövdesi formatı, lütfen içeriğin format gereksinimlerine uygun olduğunu kontrol edip tekrar deneyin\"\nInvalidLicenseCodeType: \"Sağlanan lisans kodu formatı geçersiz, lütfen kontrol edip tekrar deneyin\"\nLicenseNotFoundType: \"Lisans bulunamadı, sistemde sağlanan lisansla eşleşen bir kayıt yok. Lütfen kontrol edip tekrar deneyin\"\nLicenseRevokedType: \"İstenen lisans iptal edilmiş ve artık kullanılamaz. Lütfen kontrol edip tekrar deneyin\"\nLicenseExpiredType: \"Lisansın süresi doldu. Lütfen yenileyin veya Panel Ayarları - Lisans bölümünden yeniden içe aktarın\"\nLicenseProductMismatchType: \"Lisans, istenen ürün veya hizmetle eşleşmiyor\"\nInvalidAssigneeType: \"Lisans ataması için hedef kullanıcı veya cihaz bilgisi geçersiz. Lütfen kontrol edip tekrar deneyin\"\nLicenseUsageNotFoundType: \"Kullanım kaydı bulunamadı. Bu lisans henüz etkinleştirilmemiş veya kullanılmamış. Lütfen kontrol edip tekrar deneyin\"\nLicenseUsageLimitExceededType: \"Bu lisans zaten başka bir düğüme bağlı. Lütfen kontrol edip tekrar deneyin\"\n\n# alert\nErrAlertSync: \"Uyarı bilgisi senkronizasyon hatası, lütfen kontrol edip tekrar deneyin\"\n\n# task\nTaskStart: \"{{ .name }} görevi başladı [BAŞLANGIÇ]\"\nTaskEnd: \"{{ .name }} görevi bitti [TAMAMLANDI]\"\nTaskFailed: \"{{ .name }} görevi başarısız\"\nTaskTimeout: \"{{ .name }} zaman aşımına uğradı\"\nTaskSuccess: \"{{ .name }} görevi başarılı\"\nTaskRetry: \"{{ .name }} yeniden denenmeye başlanıyor\"\nSubTaskSuccess: \"{{ .name }} başarılı\"\nSubTaskFailed: \"{{ .name }} başarısız: {{ .err }}\"\nTaskInstall: \"Kurulum\"\nTaskUpgrade: \"Yükseltme\"\nTaskSync: 'Senkronize'\nTaskSyncForNode: \"Düğüm Verilerini Senkronize Et\"\nTaskBackup: \"Yedekleme\"\nSuccessStatus: \"{{ .name }} başarılı\"\nFailedStatus: \"{{ .name }} başarısız {{ .err }}\"\nStart: \"Başla\"\nSubTask: \"Alt görev\"\nSkip: \"Hataları atla ve devam et...\"\nPushAppInstallTaskToNode: \"Uygulama kurulum görevini düğüme [{{ .name }}] gönder\"\nTaskPush: \"Gönder\"\nAppInstallTask: \"Uygulama kurulum görevi\"\nPushAppFailed: \"Uygulama kurulum görevi gönderilemedi\"\nSuccess: \"Başarılı\"\n\n#script\nScriptLibrary: \"Betik Kütüphanesi\"\nRemoteScriptLibrary: \"Uzak Komut Dosyası Kütüphanesi\"\nScriptSyncSkip: \"Mevcut betik kütüphanesi zaten en son sürüm\"\nDownloadData: \"Betik kütüphanesi dosyası data.yaml indiriliyor\"\nDownloadPackage: \"Betik kütüphanesi paketi indiriliyor\"\nAnalyticCompletion: \"Analiz tamamlandı, şimdi veritabanına senkronize ediliyor...\"\n\nNode: \"Düğüm\"\nSyncNode: \"Verileri {{ .name }} düğümüne senkronize et\"\nLocalName: \"'local' adı yalnızca sistem yerel tanımlaması için kullanılır\"\nSyncPackageData: \"Paket senkronizasyon verisi\"\nSyncPackageEncrypt: \"Veri paketi şifreleme\"\nSyncRequest: \"Düğüm senkronizasyon API'si isteği\"\nSyncFailedRetry: \"Düğüm veri senkronizasyonu zaman aşımı (deneme {{ .index }}), yeniden deneniyor...\"\nSyncFailed: \"Senkronizasyon başarısız, lütfen düğüm listesinde manuel senkronize edin\"\nSyncSystemProxy: \"Sistem Vekil Sunucu Ayarları\"\nSyncScripts: \"Komut Dosyası Kütüphanesi\"\nSyncBackupAccounts: \"Yedek Hesaplar\"\nSyncAlertSetting: \"Uyarı Ayarları\"\nSyncCustomApp: \"Özel Uygulama\"\nSyncLanguage: \"Sistem Dili\"\nSyncEdition: \"Çalışma Bölgesi\"\n\n#upgrade node\nNodeUpgrade: \"Düğümü yükselt {{ .name }}\"\nUpgradeCheck: \"Düğüm güncellemelerini kontrol et\"\nUpgradeCheckLocal: \"Yerel düğümler toplu yükseltmeleri desteklemiyor, atlanıyor...\"\nUpgradeCheckLatest: \"Düğüm zaten en son sürüm, atlanıyor...\"\nNewSSHClient: \"SSH bağlantısı başlatılıyor\"\nBackupBeforeUpgrade: \"Yükseltme öncesi veri yedekleme\"\nUploadUpgradeFile: \"Yükseltme dosyalarını dağıt\"\nRestartAfterUpgrade: \"Yükseltme sonrası servisi başlat\"\n\n#add node\nMasterData: \"Ana Düğüm Verisi\"\nLoadSftpClient: \"SFTP İstemcisini Yükle\"\nPackageMasterData: \"Ana düğüm yedek paketi oluştur\"\nUploadBackup: \"Yedek verileri yükle\"\nMvBackup: \"Verileri yedek dizinine taşı\"\nTaskAddNode: \"Düğüm ekle\"\nLoadNodeArch: \"Düğüm mimari bilgilerini al\"\nLoadNodeArchDetail: \"Ana düğüm mimarisi tespit edildi: {{ .local }}, alt düğüm mimarisi: {{ .node }}\"\nLoadNodeUpgradeDetail: \"v1 sürüm geçmiş kurulum dizini kullanılıyor: {{ .baseDir }}, servis dinleme portu: {{ .port }}\"\nSyncAgentBaseInfo: \"Temel düğüm bilgilerini senkronize et\"\nGenerateSSLInfo: \"Düğüm SSL bilgilerini oluştur\"\nConnInfoNotMatch: \"Bağlantı bilgisi uyuşmuyor\"\nMakeAgentPackage: \"Düğüm kurulum paketini oluştur\"\nSendAgent: \"Düğüm kurulum paketini dağıt\"\nStartService: \"Servisi başlat\"\nNoBackupNode: \"Yedek düğüm şu anda boş. Lütfen kaydetmek için bir yedek düğüm seçin ve tekrar deneyin\"\n\n#cmd\nAppVersion: \"Uygulama sürümü\"\nAppCommands: \"Uygulama ile ilgili komutlar\"\nAppInit: \"Uygulamayı başlat\"\nAppKeyVal: \"Uygulama anahtarı (sadece İngilizce destekler)\"\nAppCreateFileErr: \"Dosya {{ .name }} oluşturma başarısız {{ .err }}\"\nAppCreateDirErr: \"Klasör {{ .name }} oluşturma başarısız {{ .err }}\"\nAppMissKey: \"Uygulama anahtarı eksik, belirtmek için -k kullanın\"\nAppMissVersion: \"Uygulama sürümü eksik, belirtmek için -v kullanın\"\nAppVersionExist: \"Sürüm zaten mevcut\"\nAppCreateSuccessful: \"Oluşturma başarılı\"\nAppWriteErr: \"Dosya {{ .name }} yazma başarısız {{ .err }}\"\nSudoHelper: \"Lütfen {{ .cmd }} kullanın veya root kullanıcısına geçin\"\nListenIPCommands: \"Dinleme IP'sini değiştir\"\nListenIPv4: \"IPv4'te dinle\"\nListenIPv6: \"IPv6'da dinle\"\nListenChangeSuccessful: \"Değişiklik başarılı Şimdi {{ .value }} üzerinde dinleniyor\"\nResetCommands: \"Sistem bilgilerini sıfırla\"\nResetMFA: \"1Panel iki faktörlü kimlik doğrulamayı iptal et\"\nResetHttps: \"1Panel https girişini iptal et\"\nResetEntrance: \"1Panel güvenli girişi iptal et\"\nResetIPs: \"1Panel yetkili IP kısıtlamalarını iptal et\"\nResetDomain: \"1Panel alan adı bağlamasını iptal et\"\nResetPasskey: \"1Panel passkey'lerini sıfırla\"\nRestoreCommands: \"1Panel servisini ve verilerini geri al\"\nRestoreNoSuchFile: \"Geri alma için mevcut dosya yok\"\nRestoreStep1: \"(1/5) {{ .name }} dizininden 1Panel servisi ve verilerin geri alınması başlatılıyor...\"\nRestoreStep2: \"(2/5) 1Panel binary geri alımı başarılı\"\nRestoreStep3: \"(3/5) 1Panel betik geri alımı başarılı\"\nRestoreStep4: \"(4/5) 1Panel servis geri alımı başarılı\"\nRestoreStep5: \"(5/5) 1Panel veri geri alımı başarılı\"\nRestoreSuccessful: \"Geri alma başarılı {{ .version }} sürümüne geri alındı, hizmet yeniden başlatılıyor, lütfen bekleyin...\"\nUpdateCommands: \"Panel bilgilerini güncelle\"\nUpdateUser: \"Panel kullanıcısını güncelle\"\nUpdatePassword: \"Panel şifresini güncelle\"\nUpdatePort: \"Panel portunu güncelle\"\nUpdateUserNull: \"Hata: panel kullanıcısı boş\"\nUpdateUserBlank: \"Hata: panel kullanıcısı boşluk içeriyor\"\nUpdateUserFormat: \"Hata: Geçersiz panel kullanıcı formatı Sadece İngilizce, Çince, sayılar ve _ destekler, uzunluk 3-30\"\nUpdateUserErr: \"Hata: Panel kullanıcısı güncelleme başarısız, {{ .err }}\"\nUpdateSuccessful: \"Güncelleme başarılı\"\nUpdateUserResult: \"Panel kullanıcısı: {{ .name }}\"\nUpdatePasswordRead: \"Hata: Panel şifre bilgilerini okuma başarısız, {{ .err }}\"\nUpdatePasswordNull: \"Hata: Panel şifresi boş\"\nUpdateUPasswordBlank: \"Hata: Panel şifresi boşluk içeriyor\"\nUpdatePasswordFormat: \"Hata: Panel şifresi yalnızca harf, sayı ve özel karakter (@#$%*_,.?) içerebilir ve uzunluğu 8-30 olmalıdır.\"\nUpdatePasswordLen: \"Hata: Lütfen 6 karakterden uzun bir şifre girin\"\nUpdatePasswordRe: \"Şifreyi onayla:\"\nUpdatePasswordErr: \"Hata: Panel şifresi güncelleme başarısız, {{ .err }}\"\nUpdatePasswordSame: \"Hata: İki şifre eşleşmiyor, lütfen kontrol edip tekrar deneyin\"\nUpdatePasswordResult: \"Panel şifresi: {{ .name }}\"\nUpdatePortFormat: \"Hata: Girilen port numarası 1 ile 65535 arasında olmalıdır\"\nUpdatePortUsed: \"Hata: Port numarası zaten kullanımda, lütfen kontrol edip tekrar deneyin\"\nUpdatePortErr: \"Hata: Panel port güncelleme başarısız, {{ .err }}\"\nUpdatePortResult: \"Panel Portu: {{ .name }}\"\nUpdatePortFirewallAdd: \"Güvenlik duvarı port kuralı ekleme başarısız, {{ .err }}, lütfen {{ .name }} portunu güvenlik duvarı kurallarına manuel olarak ekleyin.\"\nUpdatePortFirewallDel: \"Hata: Güvenlik duvarı portu silme başarısız, {{ .err }}\"\nUpdatePortFirewallReload: \"Güvenlik duvarını yeniden yükleme başarısız, {{ .err }}, lütfen güvenlik duvarını manuel olarak yeniden yükleyin.\"\nUserInfo: \"Panel bilgilerini al\"\nUserInfoAddr: \"Panel adresi: \"\nUserInfoPassHelp: \"İpucu: Şifreyi değiştirmek için komutu çalıştırabilirsiniz: \"\nDBConnErr: \"Hata: Veritabanı bağlantısını başlatma başarısız, {{ .err }}\"\nSystemVersion: \"sürüm: \"\nSystemMode: \"mod: \"\n\n#mobile app\nErrVerifyToken: 'Token doğrulama hatası, lütfen sıfırlayın ve tekrar tarayın.'\nErrInvalidToken: 'Geçersiz token, lütfen sıfırlayın ve tekrar tarayın.'\nErrExpiredToken: 'Token süresi dolmuş, lütfen sıfırlayın ve tekrar tarayın.'\n\n#cluster\nErrMasterDelete: \"Ana düğümü silinemiyor, lütfen önce alt düğümleri silin.\"\nClusterNameIsExist: \"Küme adı zaten var.\"\nAppStatusUnHealthy: \"Uygulama durumu alımı anormal, lütfen düğüm listesindeki yükleme düğümü durumunu kontrol edin.\"\nMasterNodePortNotAvailable: \"Düğüm {{ .name }} portu {{ .port }} bağlantı doğrulaması başarısız oldu, lütfen güvenlik duvarı/güvenlik grubu ayarlarını ve ana düğüm durumunu kontrol edin.\"\nClusterMasterNotExist: \"Küme ana düğümü bağlantısı kesildi, lütfen alt düğümleri silin.\"\n\n#ssl\nErrReqFailed: \"{{.name}} istek başarısız: {{ .err }}\"\n\n#command\nName: \"Ad\"\nCommand: \"Komut\"\n"
  },
  {
    "path": "core/i18n/lang/zh-Hant.yaml",
    "content": "ErrInvalidParams: \"請求參數錯誤: {{ .detail }}\"\nErrTokenParse: \"Token 產生錯誤: {{ .detail }}\"\nErrInitialPassword: \"原密碼錯誤\"\nErrInternalServer: \"服務內部錯誤: {{ .detail }}\"\nErrRecordExist: \"記錄已存在\"\nErrRecordNotFound: \"記錄未找到\"\nErrStructTransform: \"類型轉換失敗: {{ .detail }}\"\nErrNotLogin: \"使用者未登入: {{ .detail }}\"\nErrSessionDataNotFound: \"目前工作階段已過期！\"\nErrSessionDataFormat: \"目前工作階段資料格式異常！\"\nErrPasswordExpired: \"目前密碼已過期: {{ .detail }}\"\nErrNotSupportType: \"系統暫不支援目前類型: {{ .detail }}\"\nErrProxy: \"請求錯誤，請檢查該節點狀態: {{ .detail }}\"\nErrApiConfigStatusInvalid: \"API 介面禁止存取: {{ .detail }}\"\nErrApiConfigKeyInvalid: \"API 金鑰錯誤: {{ .detail }}\"\nErrApiConfigIPInvalid: \"API 請求 IP 不在白名單: {{ .detail }}\"\nErrApiConfigDisable: \"此介面禁止使用 API 呼叫: {{ .detail }}\"\nErrApiConfigKeyTimeInvalid: \"API 時間戳錯誤: {{ .detail }}\"\n\n#request\nErrNoSuchHost: \"無法找到請求的伺服器 {{ .err }}\"\nErrHttpReqNotFound: \"無法找到請求的資源 {{ .err }}\"\nErrHttpReqFailed: \"請求失敗 {{ .err }}\"\nErrHttpReqTimeOut: \"請求逾時 {{ .err }}\"\nErrCreateHttpClient: \"建立請求失敗 {{ .err }}\"\nErrProxySetting: \"代理伺服器資訊不可用 {{ .err }}，請檢查後重試。\"\nErrEntranceFormat: \"暫不支援安全入口 {{ .name }} ，請檢查後重試。\"\n\n#common\nErrDemoEnvironment: \"示範伺服器，禁止此操作！\"\nErrCmdTimeout: \"指令執行逾時！\"\nErrEntrance: \"安全入口資訊錯誤，請檢查後再試。\"\nErrGroupIsDefault: \"預設分組無法刪除\"\nErrGroupIsInUse: \"分組正被使用，無法刪除。\"\nErrLocalDelete: \"無法刪除本機節點！\"\nErrPortInUsed: \"{{ .name }} 埠已被佔用！\"\nErrInternalServerKey: \"服務內部錯誤: \"\nMasterNode: \"主節點\"\n\n#app\nCustomAppStoreFileValid: \"應用商店包需要 .tar.gz 格式\"\nErrFileNotFound: \"{{ .name }} 檔案不存在\"\nAppBackup: '應用備份'\nAppBackupPush: '傳輸應用備份檔案 {{.file}} 到節點 {{ .name }}'\nErrSourceTargetSame: '源節點和目標節點不可相同！'\nAppInstall: '在 {{ .targetNode }} 節點安裝應用 {{ .name }}'\nAppInstallCheck: '檢查應用安裝環境'\n\n#backup\nErrBackupInUsed: \"該備份帳號已在排程任務中使用，無法刪除\"\nErrBackupCheck: \"備份帳號測試連線失敗 {{ .err }}\"\nErrBackupLocal: \"本機伺服器備份帳號暫不支援該操作！\"\nErrBackupPublic: \"偵測到該備份帳號為非公用，請檢查後再試。\"\nErrOSSConn: \"無法取得最新版本，請確認伺服器是否能夠連接外部網路。\"\n\n#license\nLicenseCheck: '檢查許可證是否可用'\nErrLicenseInUsed: '偵測到該許可證已被綁定，請檢查後重試。'\nErrLicenseExpired: '偵測到該許可證已過期，請檢查後重試。'\nErrLicense: \"許可證格式錯誤，請檢查後重試。\"\nErrLicenseCheck: \"許可證驗證失敗，請檢查後重試。\"\nErrXpackVersion: \"許可證驗證失敗，該許可證受版本限制，無法成功匯入，請檢查後重試。\"\nErrLicenseSave: \"許可證資訊儲存失敗，錯誤 {{ .err }}，請重試。\"\nErrLicenseSync: \"許可證資訊同步失敗，資料庫中未偵測到許可證資訊！\"\nErrLicenseExist: \"該許可證記錄已存在，您可直接前往許可證頁面進行節點綁定。\"\nErrXpackNotFound: \"該部分為專業版功能，請先在 面板設定-許可證 介面匯入許可證\"\nErrXpackExceptional: \"該部分為專業版功能，請先在 面板設定-許可證 介面同步許可證狀態\"\nErrXpackLost: \"許可證已達到最大重試次數，請進入【面板設定】【許可證】頁面手動點選同步按鈕，以確保專業版功能正常使用\"\nErrDeviceLost: \"許可證驗證必要檔案遺失，請檢查後重試。\"\nErrDeviceErr: \"目前環境與許可證匯入環境不一致，請編輯許可證重新匯入！\"\nErrXpackTimeout: \"請求逾時，網路連接可能不穩定，請稍後再試。\"\nErrUnbindMaster: \"偵測到節點管理內存在節點，無法解綁目前許可證，請先移除後重試！\"\nErrFreeNodeLimit: \"社區版節點數量已達免費上限，請前往 www.lxware.cn/1panel 購買後重試！\"\nErrNodeBound: \"該許可證已綁定到其他節點，請檢查後重試。\"\nErrNodeBoundDelete: \"該許可證已被綁定，不支援刪除操作，請檢查後重試。\"\nErrNodeBoundLimit: \"目前免費節點已達上限，請檢查後重試。\"\nErrLicenseFree: \"僅當許可證正常綁定到節點後，才能使用其免費節點，請檢查後重試。\"\nErrLicenseUnbind: \"偵測到該許可證存在社區版節點，請在 [面板設定 - 許可證] 中解除綁定後重試！\"\nErrNoSuchNode: \"未能找到該節點資訊，請檢查後重試。\"\nErrNodeUnbind: \"偵測到該節點未在許可證綁定範圍內，請檢查後重試。\"\nErrNodeBind: \"偵測到該節點已綁定許可證，請檢查後重試。\"\nErrNodeLocalRollback: \"主節點暫不支援直接回滾，請手動執行「1pctl restore」指令回滾！\"\nErrIntlLicense: \"當前版本暫不支援匯入國際版許可證，敬請期待！\"\n\nInvalidRequestBodyType: \"請求內容格式錯誤，請檢查請求內容是否符合格式要求後重試！\"\nInvalidLicenseCodeType: \"提供的許可證格式錯誤，請檢查後重試。\"\nLicenseNotFoundType: \"許可證不存在，系統中未找到與提供許可證符合的紀錄，請檢查後重試。\"\nLicenseRevokedType: \"請求的許可證已被廢棄，無法使用，請檢查後重試。\"\nLicenseExpiredType: \"許可證已超過有效期，請續費或在 面板設定-許可證 介面重新匯入許可證後重試！\"  \nLicenseProductMismatchType: \"許可證與目前請求的產品或服務不符！\"\nInvalidAssigneeType: \"許可證分配的目標使用者或裝置資訊無效，請檢查後重試。\"\nLicenseUsageNotFoundType: \"無使用記錄，目前許可證尚未被啟動或使用，請檢查後重試。\"\nLicenseUsageLimitExceededType: \"該許可證已綁定到其他節點，請檢查後重試。\"\n\n# alert\nErrAlertSync: \"告警資訊同步錯誤，請檢查後重試。\"\n\n#task\nTaskStart: \"{{ .name }} 任務開始 [START]\"\nTaskEnd: \"{{ .name }} 任務結束 [COMPLETED]\"\nTaskFailed: \"{{ .name }} 任務失敗\"\nTaskTimeout: \"{{ .name }} 逾時\"\nTaskSuccess: \"{{ .name }} 任務成功\"\nTaskRetry: \"開始第 {{ .name }} 次重試\"\nSubTaskSuccess: \"{{ .name }} 成功\"\nSubTaskFailed: \"{{ .name }} 失敗: {{ .err }}\"\nTaskInstall: \"安裝\"\nTaskUpgrade: \"升級\"\nTaskSync: \"同步\"\nTaskSyncForNode: \"同步節點資料\"\nTaskBackup: \"備份\"\nSuccessStatus: \"{{ .name }} 成功\"\nFailedStatus: \"{{ .name }} 失敗 {{ .err }}\"\nStart: \"開始\"\nSubTask: \"子任務\"\nSkip: \"忽略錯誤並繼續...\"\nPushAppInstallTaskToNode: \"推送應用安裝任務到節點 [{{ .name }}]\"\nTaskPush: \"推送\"\nAppInstallTask: \"應用安裝任務\"\nPushAppFailed: \"推送應用安裝任務失敗\"\nSuccess: \"成功\"\n\n#script\nScriptLibrary: \"腳本庫\"\nRemoteScriptLibrary: \"遠端腳本庫\"\nScriptSyncSkip: \"目前腳本庫已是最新版本！\"\nDownloadData: \"下載腳本庫檔案 data.yaml\"\nDownloadPackage: \"下載腳本庫壓縮檔\"\nAnalyticCompletion: \"解析成功，現在開始同步到資料庫...\"\n\n#sync node\nNode: \"節點\"\nSyncNode: \"同步資料到節點 {{ .name }}\"\nLocalName: \"'local' 名稱僅用於系統本機標識\"\nSyncPackageData: \"打包同步資料\"\nSyncPackageEncrypt: \"封包加密\"\nSyncRequest: \"請求節點同步介面\"\nSyncFailedRetry: \"第 {{ .index }} 次同步節點資料失敗逾時，正在重試...\"\nSyncFailed: \"同步失敗，請在節點列表中手動同步！\"\nSyncSystemProxy: \"系統代理設定\"\nSyncScripts: \"腳本庫\"\nSyncBackupAccounts: \"備份帳號\"\nSyncAlertSetting: \"告警設定\"\nSyncCustomApp: \"自訂應用\"\nSyncLanguage: \"系統語言\"\nSyncEdition: \"運行區域\"\n\n#upgrade node\nNodeUpgrade: \"升級節點 {{ .name }}\"\nUpgradeCheck: \"檢查節點更新\"\nUpgradeCheckLocal: \"本機節點不支援批次升級，跳過...\"\nUpgradeCheckLatest: \"偵測到該節點已是最新版本，跳過...\"\nNewSSHClient: \"初始化 SSH 連接\"\nBackupBeforeUpgrade: \"升級前備份資料\"\nUploadUpgradeFile: \"發送升級所需檔案\"\nRestartAfterUpgrade: \"升級後啟動服務\"\n\n#add node\nMasterData: \"主節點資料\"\nLoadSftpClient: \"取得上傳用戶端\"\nPackageMasterData: \"產生主節點備份壓縮檔\"\nUploadBackup: \"上傳備份資料\"\nMvBackup: \"行動資料到備份目錄\"\nTaskAddNode: \"新增節點\"\nLoadNodeArch: \"取得節點架構資訊\"\nLoadNodeArchDetail: \"偵測到主節點架構: {{ .local }}，子節點架構: {{ .node }}\"\nLoadNodeUpgradeDetail: \"使用 v1 版本歷史安裝目錄:  {{ .baseDir }}，服務監聽埠: {{ .port }}\"\nSyncAgentBaseInfo: \"同步節點基礎資料\"\nGenerateSSLInfo: \"產生節點 SSL 資訊\"\nConnInfoNotMatch: \"連接資訊不符\"\nMakeAgentPackage: \"產生節點安裝包\"\nSendAgent: \"下發節點安裝包\"\nStartService: \"啟動服務\"\nNoBackupNode: \"目前備份節點為空，請選擇備份節點儲存後重試！\"\n\n#cmd\nAppVersion: \"應用版本\"\nAppCommands: \"應用相關指令\"\nAppInit: \"初始化應用\"\nAppKeyVal: \"應用的 key（僅支援英文）\"\nAppCreateFileErr: \"檔案 {{ .name }} 建立失敗 {{ .err }}\"\nAppCreateDirErr: \"資料夾 {{ .name }} 建立失敗 {{ .err }}\"\nAppMissKey: \"應用的 key 缺失，使用 -k 指定\"\nAppMissVersion: \"應用版本缺失，使用 -v 指定\"\nAppVersionExist: \"版本已存在！\"\nAppCreateSuccessful: \"建立成功！\"\nAppWriteErr: \"檔案 {{ .name }} 寫入失敗 {{ .err }}\"\nSudoHelper: \"請使用 {{ .cmd }} 或切換到 root 使用者\"\nListenIPCommands: \"切換監聽 IP\"\nListenIPv4: \"監聽 IPv4\"\nListenIPv6: \"監聽 IPv6\"\nListenChangeSuccessful: \"切換成功！已切換至監聽 {{ .value }}\"\nResetCommands: \"重設系統資訊\"\nResetMFA: \"取消 1Panel 兩步驗證\"\nResetHttps: \"取消 1Panel https 方式登入\"\nResetEntrance: \"取消 1Panel 安全入口\"\nResetIPs: \"取消 1Panel 授權 IP 限制\"\nResetDomain: \"取消 1Panel 存取網域綁定\"\nResetPasskey: \"清空 1Panel 通行密鑰\"\nRestoreCommands: \"回滾 1Panel 服務及資料\"\nRestoreNoSuchFile: \"暫無可回滾檔案\"\nRestoreStep1: \"（1/5）開始從 {{ .name }} 目錄回滾 1Panel 服務及資料... \"\nRestoreStep2: \"（2/5）1Panel 二進位回滾成功\"\nRestoreStep3: \"（3/5）1Panel 腳本回滾成功\"\nRestoreStep4: \"（4/5）1Panel 服務回滾成功\"\nRestoreStep5: \"（5/5）1Panel 資料回滾成功\"\nRestoreSuccessful: \"回滾成功！已回滾到 {{ .version }}，正在重新啟動服務，請稍候...\"\nUpdateCommands: \"修改面板資訊\"\nUpdateUser: \"修改面板使用者\"\nUpdatePassword: \"修改面板密碼\"\nUpdatePort: \"修改面板埠\"\nUpdateUserNull: \"錯誤: 輸入面板使用者為空！\"\nUpdateUserBlank: \"錯誤: 輸入面板使用者中包含空格字元！\"\nUpdateUserFormat: \"錯誤: 輸入面板使用者錯誤！僅支援英文、中文、數字和_，長度3-30\"\nUpdateUserErr: \"錯誤: 面板使用者修改失敗，{{ .err }}\"\nUpdateSuccessful: \"修改成功！\"\nUpdateUserResult: \"面板使用者: {{ .name }}\"\nUpdatePasswordRead: \"錯誤: 面板密碼資訊讀取錯誤，{{ .err }}\"\nUpdatePasswordNull: \"錯誤: 輸入面板密碼為空！\"\nUpdateUPasswordBlank: \"錯誤: 輸入面板密碼中包含空格字元！\"\nUpdatePasswordFormat: \"錯誤: 面板密碼僅支援字母、數字、特殊字元（!@#$%*_,.?），長度 8-30 位！\"\nUpdatePasswordLen: \"錯誤: 請輸入 6 位以上密碼！\"\nUpdatePasswordRe: \"確認密碼: \"\nUpdatePasswordErr: \"錯誤: 面板密碼修改失敗，{{ .err }}\"\nUpdatePasswordSame: \"錯誤: 兩次密碼不符，請檢查後重試。\"\nUpdatePasswordResult: \"面板密碼: {{ .name }}\"\nUpdatePortFormat: \"錯誤: 輸入的埠號必須在 1 到 65535 之間！\"\nUpdatePortUsed: \"錯誤: 該埠號正被佔用，請檢查後重試。\"\nUpdatePortErr: \"錯誤: 面板埠修改失敗，{{ .err }}\"\nUpdatePortResult: \"面板埠: {{ .name }}\"\nUpdatePortFirewallAdd: \"新增防火牆埠規則失敗，{{ .err }}，請您手動將 {{ .name }} 埠新增至防火牆規則中。\"\nUpdatePortFirewallDel: \"錯誤: 防火牆埠刪除失敗，{{ .err }}\"\nUpdatePortFirewallReload: \"防火牆重載失敗，{{ .err }}，請您手動重載防火牆。\"\nUserInfo: \"取得面板資訊\"\nUserInfoAddr: \"面板位址: \"\nUserInfoPassHelp: \"提示: 修改密碼可執行指令: \"\nDBConnErr: \"錯誤: 初始化資料庫連線失敗，{{ .err }}\"\nSystemVersion: \"版本: \"\nSystemMode: \"模式: \"\n\n#exchange\nLocalNodeIpFailed: \"無法取得主節點 IP ，請編輯主節點增加 IP 位址和 SSH 認證資訊\"\nHandlePrivateKey: \"處理節點私鑰\"\nHandlePublicKey: \"處理節點公鑰\"\nExchangeFile: \"開始從 {{ .source }} 節點同步 {{ .sourcePath }} 到 {{ .dest  }} 節點 {{ .destPath }}\"\nTaskRsync: \"同步\"\nNodeFile: \"節點檔案\"\n\n#mobile app\nErrVerifyToken: '令牌驗證錯誤，請重設後再次掃碼'\nErrInvalidToken: '無效的令牌，請重設後再次掃碼'\nErrExpiredToken: '令牌過期，請重設後再次掃碼'\n\n#cluster\nErrMasterDelete: \"無法刪除主節點，請先刪除從節點\"\nClusterNameIsExist: \"叢集名稱已存在\"\nAppStatusUnHealthy: \"應用取得狀態異常，請在節點列表檢查安裝節點狀態\"\nMasterNodePortNotAvailable: \"節點 {{ .name }} 埠 {{ .port }} 連通性驗證失敗，請檢查防火牆/安全組設定和主節點狀態\"\nClusterMasterNotExist: \"叢集主節點失聯，請刪除子節點\"\n\n#ssl\nErrReqFailed: \"{{.name}} 請求失敗: {{ .err }}\"\n\n#command\nName: \"名稱\"\nCommand: \"指令\"\n"
  },
  {
    "path": "core/i18n/lang/zh.yaml",
    "content": "ErrInvalidParams: \"请求参数错误: {{ .detail }}\"\nErrTokenParse: \"Token 生成错误: {{ .detail }}\"\nErrInitialPassword: \"原密码错误\"\nErrInternalServer: \"服务内部错误: {{ .detail }}\"\nErrRecordExist: \"记录已存在\"\nErrRecordNotFound: \"记录未能找到\"\nErrStructTransform: \"类型转换失败: {{ .detail }}\"\nErrNotLogin: \"用户未登录: {{ .detail }}\"\nErrSessionDataNotFound: \"当前会话已过期！\"\nErrSessionDataFormat: \"当前会话数据格式异常！\"\nErrPasswordExpired: \"当前密码已过期: {{ .detail }}\"\nErrNotSupportType: \"系统暂不支持当前类型: {{ .detail }}\"\nErrProxy: \"请求错误，请检查该节点状态: {{ .detail }}\"\nErrApiConfigStatusInvalid: \"API 接口禁止访问: {{ .detail }}\"\nErrApiConfigKeyInvalid: \"API 接口密钥错误: {{ .detail }}\"\nErrApiConfigIPInvalid: \"调用 API 接口 IP 不在白名单: {{ .detail }}\"\nErrApiConfigDisable: \"此接口禁止使用 API 接口调用: {{ .detail }}\"\nErrApiConfigKeyTimeInvalid: \"API 接口时间戳错误: {{ .detail }}\"\nErrPasskeyDisabled: \"需开启 HTTPS 才能使用 Passkey\"\nErrPasskeyNotConfigured: \"尚未配置 Passkey\"\nErrPasskeyLimit: \"Passkey 数量已达上限（最多 5 个）\"\nErrPasskeySession: \"Passkey 会话已过期或无效\"\nErrPasskeyDuplicate: \"Passkey 已存在\"\nErrPasskeyVerify: \"Passkey 验证失败\"\n\n#request\nErrNoSuchHost: \"无法找到请求的服务器 {{ .err }}\"\nErrHttpReqNotFound: \"无法找到请求的资源 {{ .err }}\"\nErrHttpReqFailed: \"请求失败 {{ .err }}\"\nErrHttpReqTimeOut: \"请求超时 {{ .err }}\"\nErrCreateHttpClient: \"创建请求失败 {{ .err }}\"\nErrProxySetting: \"代理服务器信息不可用 {{ .err }}，请检查后重试！\"\nErrEntranceFormat: \"暂不支持安全入口 {{ .name }} ，请检查后重试！\"\n\n#common\nErrDemoEnvironment: \"演示服务器，禁止此操作!\"\nErrCmdTimeout: \"命令执行超时！\"\nErrEntrance: \"安全入口信息错误，请检查后重试！\"\nErrGroupIsDefault: \"默认分组，无法删除\"\nErrGroupIsInUse: \"分组正被使用，无法删除\"\nErrLocalDelete: \"无法删除本地节点！\"\nErrPortInUsed: \"{{ .name }} 端口已被占用！\"\nErrInternalServerKey: \"服务内部错误:\"\nMasterNode: \"主节点\"\n\n#app\nCustomAppStoreFileValid: \"应用商店包需要 .tar.gz 格式\"\nErrFileNotFound: '{{ .name }} 文件不存在'\nAppBackup: \"应用备份\"\nAppBackupPush: \"传输应用备份文件 {{.file}} 到节点 {{ .name }}\"\nErrSourceTargetSame: \"源节点和目标节点不能相同！\"\nAppInstall: \"在 {{ .targetNode }} 节点安装应用 {{ .name }}\"\nAppInstallCheck: \"检查应用安装环境\"\n\n#backup\nErrBackupInUsed: \"该备份账号已在计划任务中使用，无法删除\"\nErrBackupCheck: \"备份账号测试连接失败 {{ .err }}\"\nErrBackupLocal: \"本地服务器备份账号暂不支持该操作！\"\nErrBackupPublic: \"检测到该备份账号为非公用，请检查后重试！\"\nErrOSSConn: \"无法获取最新版本，请确认服务器是否能够连接外部网络。\"\n\n#license\nLicenseCheck: '检查许可证是否可用'\nErrLicenseInUsed: '检查到该许可证已被绑定，请检查后重试！'\nErrLicenseExpired: '检查到该许可证已过期，请检查后重试！'\nErrLicense: \"许可证格式错误，请检查后重试！\"\nErrLicenseCheck: \"许可证校验失败，请检查后重试！\"\nErrXpackVersion: \"许可证校验失败，该许可证受版本限制，无法成功导入，请检查后重试！\"\nErrLicenseSave: \"许可证信息保存失败，错误 {{ .err }}，请重试！\"\nErrLicenseSync: \"许可证信息同步失败，数据库中未检测到许可证信息！\"\nErrLicenseExist: \"该许可证记录已存在，您可直接前往许可证页面进行节点绑定。\"\nErrXpackNotFound: \"该部分为专业版功能，请先在 面板设置-许可证 界面导入许可证\"\nErrXpackExceptional: \"该部分为专业版功能，请先在 面板设置-许可证 界面同步许可证状态\"\nErrXpackLost: \"许可证已达到最大重试次数，请进入【面板设置】【许可证】页面手动点击同步按钮，以确保专业版功能正常使用\"\nErrDeviceLost: \"许可证校验必要文件丢失，请检查后重试！\"\nErrDeviceErr: \"当前环境与许可证导入环境不一致，请编辑许可证重新导入！\"\nErrXpackTimeout: \"请求超时，网络连接可能不稳定，请稍后再试！\"\nErrUnbindMaster: \"检测到节点管理内存在专业版节点，无法解绑当前许可证，请先移除或解绑后重试！\"\nErrFreeNodeLimit: \"社区版节点数量已达到免费上限，请前往 www.lxware.cn/1panel 购买后重试！\"\nErrNodeBound: \"该许可证已绑定到其他节点，请检查后重试！\"\nErrNodeBoundDelete: \"该许可证已被绑定，不支持删除操作，请检查后重试！\"\nErrNodeBoundLimit: \"当前免费节点已经达到上限，请检查后重试！\"\nErrLicenseFree: \"仅当许可证正常绑定到节点后，才能使用其免费节点，请检查后重试！\"\nErrLicenseUnbind: \"检测到该许可证存在社区版节点，请在 [ 面板设置 - 许可证 ] 中解绑后重试！\"\nErrNoSuchNode: \"未能找到该节点信息，请检查后重试！\"\nErrNodeUnbind: \"检测到该节点未在许可证绑定范围内，请检查后重试！\"\nErrNodeBind: \"检测到该节点已绑定许可证，请检查后重试！\"\nErrNodeLocalRollback: \"主节点暂不支持直接回滚，请手动执行 1pctl restore 命令回滚！\"\nErrIntlLicense: \"当前版本暂不支持导入国际版许可证，敬请期待！\"\n\nInvalidRequestBodyType: \"请求体格式错误，请检查请求内容是否符合格式要求后重试！\"\nInvalidLicenseCodeType: \"提供的许可证格式错误，请检查后重试！\"       \nLicenseNotFoundType: \"许可证不存在，系统中未找到与提供许可证匹配的记录，请检查后重试！\"          \nLicenseRevokedType: \"请求的许可证已被废弃，无法使用，请检查后重试！\"           \nLicenseExpiredType: \"许可证已超过有效期，请续费或在 面板设置-许可证 界面重新导入许可证后重试！\"         \nLicenseProductMismatchType: \"许可证与当前请求的产品或服务不匹配！\"   \nInvalidAssigneeType: \"许可证分配的目标用户或设备信息无效，请检查后重试！\"         \nLicenseUsageNotFoundType: \"无使用记录，当前许可证尚未被激活或使用，请检查后重试！\"    \nLicenseUsageLimitExceededType: \"该许可证已绑定到其他节点，请检查后重试！\"    \n\n# alert\nErrAlertSync: \"告警信息同步错误，请检查后重试！\"\n\n#task\nTaskStart: \"{{ .name }} 任务开始 [START]\"\nTaskEnd: \"{{ .name }} 任务结束 [COMPLETED]\"\nTaskFailed: \"{{ .name }} 任务失败\"\nTaskTimeout: \"{{ .name }} 超时\"\nTaskSuccess: \"{{ .name }} 任务成功\"\nTaskRetry: \"开始第 {{ .name }} 次重试\"\nSubTaskSuccess: \"{{ .name }} 成功\"\nSubTaskFailed: \"{{ .name }} 失败: {{ .err }}\"\nTaskInstall: \"安装\"\nTaskUpgrade: \"升级\"\nTaskSync: \"同步\"\nTaskSyncForNode: \"同步节点数据\"\nTaskBackup: \"备份\"\nSuccessStatus: \"{{ .name }} 成功\"\nFailedStatus: \"{{ .name }} 失败 {{ .err }}\"\nStart: \"开始\"\nSubTask: \"子任务\"\nSkip: \"忽略错误并继续...\"\nPushAppInstallTaskToNode: \"推送应用安装任务到节点 [{{ .name }}]\"\nTaskPush: \"推送\"\nAppInstallTask: \"应用安装任务\"\nPushAppFailed: \"推送应用安装任务失败\"\nSuccess: \"成功\"\n\n#script\nScriptLibrary: \"脚本库\"\nRemoteScriptLibrary: \"远程脚本库\"\nScriptSyncSkip: \"检查到当前脚本库已是最新版本！\"\nDownloadData: \"下载脚本库文件 data.yaml\"\nDownloadPackage: \"下载脚本库压缩包\"\nAnalyticCompletion: \"解析成功，现在开始同步到数据库...\"\n\n#sync node\nNode: \"节点\"\nSyncNode: \"同步数据到节点 {{ .name }}\"\nLocalName: \"local 名称仅用于系统本机标识\"\nSyncPackageData: \"打包同步数据\"\nSyncPackageEncrypt: \"数据包加密\"\nSyncRequest: \"请求节点同步接口\"\nSyncFailedRetry: \"第 {{ .index }} 次同步节点数据失败超时，正在重试...\"\nSyncFailed: \"同步失败，请在节点列表中手动同步！\"\nSyncSystemProxy: \"系统代理配置\"\nSyncScripts: \"脚本库\"\nSyncBackupAccounts: \"备份账号\"\nSyncAlertSetting: \"告警设置\"\nSyncCustomApp: \"自定义应用\"\nSyncLanguage: \"系统语言\"\nSyncEdition: \"运行区域\"\n\n\n#upgrade node\nNodeUpgrade: \"升级节点 {{ .name }}\"\nUpgradeCheck: \"检查节点更新\"\nUpgradeCheckLocal: \"本地节点不支持批量升级，跳过...\"\nUpgradeCheckLatest: \"检测到该节点已是最新版本，跳过...\"\nNewSSHClient: \"初始化 SSH 连接\"\nBackupBeforeUpgrade: \"升级前备份数据\"\nUploadUpgradeFile: \"下发升级所需文件\"\nRestartAfterUpgrade: \"升级后启动服务\"\n\n#add node\nMasterData: \"主节点数据\"\nLoadSftpClient: \"获取上传客户端\"\nPackageMasterData: \"生成主节点备份压缩包\"\nUploadBackup: \"上传备份数据\"\nTaskAddNode: \"添加节点\"\nLoadNodeArch: \"获取节点架构信息\"\nLoadNodeArchDetail: \"检测到主节点架构: {{ .local }}，子节点架构: {{ .node }}\"\nLoadNodeUpgradeDetail: \"使用 v1 版本历史安装目录:  {{ .baseDir }}，服务监听端口: {{ .port }}\"\nSyncAgentBaseInfo: \"同步节点基础数据\"\nGenerateSSLInfo: \"生成节点 SSL 信息\"\nConnInfoNotMatch: \"连接信息不匹配\"\nMakeAgentPackage: \"生成节点安装包\"\nSendAgent: \"下发节点安装包\"\nStartService: \"启动服务\"\nNoBackupNode: \"当前备份节点为空，请选择备份节点保存后重试！\"\n\n#cmd\nAppVersion: \"应用版本\"\nAppCommands: \"应用相关命令\"\nAppInit: \"初始化应用\"\nAppKeyVal: \"应用的 key（仅支持英文）\"\nAppCreateFileErr: \"文件 {{ .name }} 创建失败 {{ .err }}\"\nAppCreateDirErr: \"文件夹 {{ .name }} 创建失败 {{ .err }}\"\nAppMissKey: \"应用的 key 缺失，使用 -k 指定\"\nAppMissVersion: \"应用版本缺失，使用 -v 指定\"\nAppVersionExist: \"版本已存在！\"\nAppCreateSuccessful: \"创建成功！\"\nAppWriteErr: \"文件 {{ .name }} 写入失败 {{ .err }}\"\nSudoHelper: \"请使用 {{ .cmd }} 或切换到 root 用户\"\nListenIPCommands: \"切换监听 IP\"\nListenIPv4: \"监听 IPv4\"\nListenIPv6: \"监听 IPv6\"\nListenChangeSuccessful: \"切换成功！已切换至监听 {{ .value }}\"\nResetCommands: \"重置系统信息\"\nResetMFA: \"取消 1Panel 两步验证\"\nResetHttps: \"取消 1Panel https 方式登录\"\nResetEntrance: \"取消 1Panel 安全入口\"\nResetIPs: \"取消 1Panel 授权 IP 限制\"\nResetDomain: \"取消 1Panel 访问域名绑定\"\nResetPasskey: \"清空 1Panel 通行密钥\"\nRestoreCommands: \"回滚 1Panel 服务及数据\"\nRestoreNoSuchFile: \"暂无可回滚文件\"\nRestoreStep1: \"（1/5）开始从 {{ .name }} 目录回滚 1Panel 服务及数据... \"\nRestoreStep2: \"（2/5）1Panel 二进制回滚成功\"\nRestoreStep3: \"（3/5）1Panel 脚本回滚成功\"\nRestoreStep4: \"（4/5）1Panel 服务回滚成功\"\nRestoreStep5: \"（5/5）1Panel 数据回滚成功\"\nRestoreSuccessful: \"回滚成功！已回滚到 {{ .version }}，正在重启服务，请稍候...\"\nUpdateCommands: \"修改面板信息\"\nUpdateUser: \"修改面板用户\"\nUpdatePassword: \"修改面板密码\"\nUpdatePort: \"修改面板端口\"\nUpdateUserNull: \"错误: 输入面板用户为空！\"\nUpdateUserBlank: \"错误: 输入面板用户中包含空格字符！\"\nUpdateUserFormat: \"错误: 输入面板用户错误！仅支持英文、中文、数字和_，长度3-30\"\nUpdateUserErr: \"错误: 面板用户修改失败，{{ .err }}\"\nUpdateSuccessful: \"修改成功！\"\nUpdateUserResult: \"面板用户: {{ .name }}\"\nUpdatePasswordRead: \"错误: 面板密码信息读取错误，{{ .err }}\"\nUpdatePasswordNull: \"错误: 输入面板密码为空！\"\nUpdateUPasswordBlank: \"错误: 输入面板密码中包含空格字符！\"\nUpdatePasswordFormat: \"错误: 面板密码仅支持字母、数字、特殊字符（!@#$%*_,.?），长度 8-30 位！\"\nUpdatePasswordLen: \"错误: 请输入 6 位以上密码！\"\nUpdatePasswordRe: \"确认密码: \"\nUpdatePasswordErr: \"错误: 面板密码修改失败，{{ .err }}\"\nUpdatePasswordSame: \"错误: 两次密码不匹配，请检查后重试！\"\nUpdatePasswordResult: \"面板密码: {{ .name }}\"\nUpdatePortFormat: \"错误: 输入的端口号必须在 1 到 65535 之间！\"\nUpdatePortUsed: \"错误: 该端口号正被占用，请检查后重试！\"\nUpdatePortErr: \"错误: 面板端口修改失败，{{ .err }}\"\nUpdatePortResult: \"面板端口: {{ .name }}\"\nUpdatePortFirewallAdd: \"添加防火墙端口规则失败，{{ .err }}，请您手动将 {{ .name }} 端口添加至防火墙规则中。\"\nUpdatePortFirewallDel: \"错误: 防火墙端口删除失败，{{ .err }}\"\nUpdatePortFirewallReload: \"防火墙重载失败，{{ .err }}，请您手动重载防火墙。\"\nUserInfo: \"获取面板信息\"\nUserInfoAddr: \"面板地址: \"\nUserInfoPassHelp: \"提示: 修改密码可执行命令: \"\nDBConnErr: \"错误: 初始化数据库连接失败，{{ .err }}\"\nSystemVersion: \"版本: \"\nSystemMode: \"模式: \"\n\n#exchange\nLocalNodeIpFailed: \"无法获取主节点 IP ，请编辑主节点增加 IP 地址和 SSH 认证信息\"\nHandlePrivateKey: \"处理节点私钥\"\nHandlePublicKey: \"处理节点公钥\"\nExchangeFile: \"开始从 {{ .source }} 节点同步 {{ .sourcePath }} 到 {{ .dest  }} 节点 {{ .destPath }}\"\nTaskRsync: \"同步\"\nNodeFile: \"节点文件\"\n\n#mobile app\nErrVerifyToken: '令牌验证错误，请重置后再次扫码'\nErrInvalidToken: '无效的令牌，请重置后再次扫码'\nErrExpiredToken: '令牌过期，请重置后再次扫码'\n\n#cluster\nErrMasterDelete: \"无法删除主节点，请先删除从节点\"\nClusterNameIsExist: \"集群名称已存在\"\nAppStatusUnHealthy: \"应用获取状态异常，请在节点列表检查安装节点状态\"\nMasterNodePortNotAvailable: \"节点 {{ .name }} 端口 {{ .port }} 连通性校验失败，请检查防火墙/安全组设置和主节点状态\"\nClusterMasterNotExist: \"集群主节点失联，请删除子节点\"\n\n#ssl\nErrReqFailed: \"{{.name}} 请求失败: {{ .err }}\"\n\n#command\nName: \"名称\"\nCommand: \"命令\"\n"
  },
  {
    "path": "core/init/auth/ip_tracker.go",
    "content": "package auth\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\nconst (\n\tMaxIPCount     = 100\n\tExpireDuration = 30 * time.Minute\n)\n\ntype IPRecord struct {\n\tNeedCaptcha bool\n\tLastUpdate  time.Time\n}\n\ntype IPTracker struct {\n\trecords map[string]*IPRecord\n\tipOrder []string\n\tmu      sync.RWMutex\n}\n\nfunc NewIPTracker() *IPTracker {\n\treturn &IPTracker{\n\t\trecords: make(map[string]*IPRecord),\n\t\tipOrder: make([]string, 0),\n\t}\n}\n\nfunc (t *IPTracker) NeedCaptcha(ip string) bool {\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\n\trecord, exists := t.records[ip]\n\tif !exists {\n\t\treturn false\n\t}\n\n\tif time.Since(record.LastUpdate) > ExpireDuration {\n\t\tt.removeIPUnsafe(ip)\n\t\treturn false\n\t}\n\n\treturn record.NeedCaptcha\n}\n\nfunc (t *IPTracker) SetNeedCaptcha(ip string) {\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\n\tif record, exists := t.records[ip]; exists {\n\t\tif time.Since(record.LastUpdate) > ExpireDuration {\n\t\t\tt.removeIPUnsafe(ip)\n\t\t} else {\n\t\t\trecord.NeedCaptcha = true\n\t\t\trecord.LastUpdate = time.Now()\n\t\t\treturn\n\t\t}\n\t}\n\n\tif len(t.records) >= MaxIPCount {\n\t\tt.removeOldestUnsafe()\n\t}\n\n\tt.records[ip] = &IPRecord{\n\t\tNeedCaptcha: true,\n\t\tLastUpdate:  time.Now(),\n\t}\n\tt.ipOrder = append(t.ipOrder, ip)\n}\n\nfunc (t *IPTracker) Clear(ip string) {\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\n\tt.removeIPUnsafe(ip)\n}\n\nfunc (t *IPTracker) removeIPUnsafe(ip string) {\n\tdelete(t.records, ip)\n\n\tfor i, storedIP := range t.ipOrder {\n\t\tif storedIP == ip {\n\t\t\tt.ipOrder = append(t.ipOrder[:i], t.ipOrder[i+1:]...)\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc (t *IPTracker) removeOldestUnsafe() {\n\tif len(t.ipOrder) == 0 {\n\t\treturn\n\t}\n\n\toldestIP := t.ipOrder[0]\n\tdelete(t.records, oldestIP)\n\tt.ipOrder = t.ipOrder[1:]\n}\n"
  },
  {
    "path": "core/init/cron/cron.go",
    "content": "package cron\n\nimport (\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/service\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/init/cron/job\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/robfig/cron/v3\"\n)\n\nfunc Init() {\n\tnyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())\n\tglobal.Cron = cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))\n\n\tif _, err := global.Cron.AddJob(\"0 3 */31 * *\", job.NewBackupJob()); err != nil {\n\t\tglobal.LOG.Errorf(\"[core] can not add backup token refresh corn job: %s\", err.Error())\n\t}\n\n\tservice.StartSync()\n\tglobal.Cron.Start()\n}\n"
  },
  {
    "path": "core/init/cron/job/backup.go",
    "content": "package job\n\nimport (\n\t\"encoding/json\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cloud_storage\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n)\n\ntype backup struct{}\n\nfunc NewBackupJob() *backup {\n\treturn &backup{}\n}\n\nfunc (b *backup) Run() {\n\tvar backups []model.BackupAccount\n\t_ = global.DB.Where(\"`type` in (?) AND is_public = 0\", []string{constant.OneDrive, constant.ALIYUN, constant.GoogleDrive}).Find(&backups)\n\tif len(backups) == 0 {\n\t\treturn\n\t}\n\tfor _, backupItem := range backups {\n\t\tif backupItem.ID == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tglobal.LOG.Infof(\"Start to refresh %s-%s access_token ...\", backupItem.Type, backupItem.Name)\n\t\tvarMap := make(map[string]interface{})\n\t\tif err := json.Unmarshal([]byte(backupItem.Vars), &varMap); err != nil {\n\t\t\tglobal.LOG.Errorf(\"failed to refresh %s - %s token, please retry, err: %v\", backupItem.Type, backupItem.Name, err)\n\t\t\tcontinue\n\t\t}\n\t\tvar (\n\t\t\trefreshToken string\n\t\t\terr          error\n\t\t)\n\t\tswitch backupItem.Type {\n\t\tcase constant.OneDrive:\n\t\t\trefreshToken, err = cloud_storage.RefreshToken(\"refresh_token\", \"refreshToken\", varMap)\n\t\tcase constant.ALIYUN:\n\t\t\trefreshToken, err = cloud_storage.RefreshALIToken(varMap)\n\t\t}\n\t\tif err != nil {\n\t\t\tvarMap[\"refresh_status\"] = constant.StatusFailed\n\t\t\tvarMap[\"refresh_msg\"] = err.Error()\n\t\t\tglobal.LOG.Errorf(\"failed to refresh %s-%s token, please retry, err: %v\", backupItem.Type, backupItem.Name, err)\n\t\t\tcontinue\n\t\t}\n\t\tvarMap[\"refresh_status\"] = constant.StatusSuccess\n\t\tvarMap[\"refresh_time\"] = time.Now().Format(constant.DateTimeLayout)\n\t\tvarMap[\"refresh_token\"] = refreshToken\n\n\t\tvarsItem, _ := json.Marshal(varMap)\n\t\t_ = global.DB.Model(&model.BackupAccount{}).Where(\"id = ?\", backupItem.ID).Updates(map[string]interface{}{\"vars\": string(varsItem)}).Error\n\t\tglobal.LOG.Infof(\"Refresh %s-%s access_token successful!\", backupItem.Type, backupItem.Name)\n\t\tif err := xpack.Sync(constant.SyncBackupAccounts); err != nil {\n\t\t\tglobal.LOG.Errorf(\"sync backup account to node failed, err: %v\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/init/db/db.go",
    "content": "package db\n\nimport (\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n)\n\nfunc Init() {\n\tglobal.DB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, \"1panel/db/core.db\"), \"core\")\n\tglobal.TaskDB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, \"1panel/db/task.db\"), \"task\")\n\tglobal.AgentDB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, \"1panel/db/agent.db\"), \"agent\")\n\tglobal.AlertDB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, \"1panel/db/alert.db\"), \"alert\")\n}\n"
  },
  {
    "path": "core/init/geo/lang.go",
    "content": "package geo\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\tfileUtils \"github.com/1Panel-dev/1Panel/core/utils/files\"\n)\n\nfunc Init() {\n\tgo initLang()\n}\n\nfunc initLang() {\n\tgeoPath := path.Join(global.CONF.Base.InstallDir, \"1panel/geo/GeoIP.mmdb\")\n\tisLangExist := fileUtils.Stat(\"/usr/local/bin/lang/zh.sh\")\n\tisGeoExist := fileUtils.Stat(geoPath)\n\tif isLangExist && isGeoExist {\n\t\treturn\n\t}\n\tupgradePath := path.Join(global.CONF.Base.InstallDir, \"1panel/tmp/upgrade\")\n\ttmpPath, err := loadRestorePath(upgradePath)\n\tupgradeDir := path.Join(upgradePath, tmpPath, \"downloads\")\n\tif err != nil || len(tmpPath) == 0 || !fileUtils.Stat(upgradeDir) {\n\t\tif !isLangExist {\n\t\t\tdownloadLangFromRemote()\n\t\t}\n\t\tif !isGeoExist {\n\t\t\tdownloadGeoFromRemote(geoPath)\n\t\t}\n\t\treturn\n\t}\n\n\tfiles, _ := os.ReadDir(upgradeDir)\n\tif len(files) == 0 {\n\t\ttmpPath = \"no such file\"\n\t} else {\n\t\tfor _, item := range files {\n\t\t\tif item.IsDir() && strings.HasPrefix(item.Name(), \"1panel-\") {\n\t\t\t\ttmpPath = path.Join(upgradePath, tmpPath, \"downloads\", item.Name())\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif tmpPath == \"no such file\" || !fileUtils.Stat(tmpPath) {\n\t\tif !isLangExist {\n\t\t\tdownloadLangFromRemote()\n\t\t}\n\t\tif !isGeoExist {\n\t\t\tdownloadGeoFromRemote(geoPath)\n\t\t}\n\t\treturn\n\t}\n\tif !isLangExist {\n\t\tif !fileUtils.Stat(path.Join(tmpPath, \"lang\")) {\n\t\t\tdownloadLangFromRemote()\n\t\t\treturn\n\t\t}\n\t\tif err := cmd.RunDefaultBashCf(\"cp -r %s %s\", path.Join(tmpPath, \"lang\"), \"/usr/local/bin/\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"load lang from package failed, %v\", err)\n\t\t\treturn\n\t\t}\n\t\tglobal.LOG.Info(\"init lang successful\")\n\t}\n\tif !isGeoExist {\n\t\tif !fileUtils.Stat(path.Join(tmpPath, \"GeoIP.mmdb\")) {\n\t\t\tdownloadGeoFromRemote(geoPath)\n\t\t\treturn\n\t\t}\n\t\tif err := cmd.RunDefaultBashCf(\"mkdir %s && cp %s %s/\", path.Dir(geoPath), path.Join(tmpPath, \"GeoIP.mmdb\"), path.Dir(geoPath)); err != nil {\n\t\t\tglobal.LOG.Errorf(\"load geo ip from package failed, %v\", err)\n\t\t\treturn\n\t\t}\n\t\tglobal.LOG.Info(\"init geo ip successful\")\n\t}\n}\n\nfunc loadRestorePath(upgradeDir string) (string, error) {\n\tif _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) {\n\t\treturn \"no such file\", nil\n\t}\n\tfiles, err := os.ReadDir(upgradeDir)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar folders []string\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tfolders = append(folders, file.Name())\n\t\t}\n\t}\n\tif len(folders) == 0 {\n\t\treturn \"no such file\", nil\n\t}\n\tsort.Slice(folders, func(i, j int) bool {\n\t\treturn folders[i] > folders[j]\n\t})\n\treturn folders[0], nil\n}\n\nfunc downloadLangFromRemote() {\n\tpath := fmt.Sprintf(\"%s/language/lang.tar.gz\", global.RepoURL())\n\tif err := fileUtils.DownloadFile(path, \"/usr/local/bin/lang.tar.gz\"); err != nil {\n\t\tglobal.LOG.Errorf(\"download lang.tar.gz failed, err: %v\", err)\n\t\treturn\n\t}\n\tif !fileUtils.Stat(\"/usr/local/bin/lang.tar.gz\") {\n\t\tglobal.LOG.Error(\"download lang.tar.gz failed, no such file\")\n\t\treturn\n\t}\n\tif err := cmd.RunDefaultBashCf(\"tar zxvfC %s %s\", \"/usr/local/bin/lang.tar.gz\", \"/usr/local/bin/\"); err != nil {\n\t\tglobal.LOG.Errorf(\"decompress lang.tar.gz failed, %v\", err)\n\t\treturn\n\t}\n\t_ = os.Remove(\"/usr/local/bin/lang.tar.gz\")\n\tglobal.LOG.Info(\"download lang successful\")\n}\nfunc downloadGeoFromRemote(targetPath string) {\n\t_ = os.MkdirAll(path.Dir(targetPath), os.ModePerm)\n\tpathItem := fmt.Sprintf(\"%s/geo/GeoIP.mmdb\", global.RepoURL())\n\tif err := fileUtils.DownloadFile(pathItem, targetPath); err != nil {\n\t\tglobal.LOG.Errorf(\"download geo ip failed, err: %v\", err)\n\t\treturn\n\t}\n\tglobal.LOG.Info(\"download geo ip successful\")\n}\n"
  },
  {
    "path": "core/init/hook/hook.go",
    "content": "package hook\n\nimport (\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/app/service\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/encrypt\"\n)\n\nfunc Init() {\n\tsettingRepo := repo.NewISettingRepo()\n\tglobal.CONF.Conn.Port, _ = settingRepo.GetValueByKey(\"ServerPort\")\n\tglobal.CONF.Conn.Ipv6, _ = settingRepo.GetValueByKey(\"Ipv6\")\n\tglobal.CONF.Base.Edition, _ = settingRepo.GetValueByKey(\"Edition\")\n\tglobal.Api.ApiInterfaceStatus, _ = settingRepo.GetValueByKey(\"ApiInterfaceStatus\")\n\tif global.Api.ApiInterfaceStatus == constant.StatusEnable {\n\t\tglobal.Api.ApiKey, _ = settingRepo.GetValueByKey(\"ApiKey\")\n\t\tglobal.Api.IpWhiteList, _ = settingRepo.GetValueByKey(\"IpWhiteList\")\n\t\tglobal.Api.ApiKeyValidityTime, _ = settingRepo.GetValueByKey(\"ApiKeyValidityTime\")\n\t}\n\tglobal.CONF.Conn.BindAddress, _ = settingRepo.GetValueByKey(\"BindAddress\")\n\tglobal.CONF.Conn.SSL, _ = settingRepo.GetValueByKey(\"SSL\")\n\tglobal.CONF.Base.Version, _ = settingRepo.GetValueByKey(\"SystemVersion\")\n\tif err := settingRepo.Update(\"SystemStatus\", \"Free\"); err != nil {\n\t\tglobal.LOG.Fatalf(\"init service before start failed, err: %v\", err)\n\t}\n\n\thandleUserInfo(global.CONF.Base.ChangeUserInfo, settingRepo)\n\n\tgenerateKey()\n\tinitDockerConf()\n}\n\nfunc handleUserInfo(tags string, settingRepo repo.ISettingRepo) {\n\tif len(tags) == 0 {\n\t\treturn\n\t}\n\tsettingMap := make(map[string]string)\n\tif tags == \"use_existing\" {\n\t\tsettingMap[\"ServerPort\"] = common.LoadParams(\"ORIGINAL_PORT\")\n\t\tglobal.CONF.Conn.Port = settingMap[\"ServerPort\"]\n\t\tsettingMap[\"UserName\"] = global.CONF.Base.Username\n\t\tsettingMap[\"Password\"] = global.CONF.Base.Password\n\t\tsettingMap[\"SecurityEntrance\"] = global.CONF.Conn.Entrance\n\t\tsettingMap[\"SystemVersion\"] = common.LoadParams(\"ORIGINAL_VERSION\")\n\t\tglobal.CONF.Base.Version = settingMap[\"SystemVersion\"]\n\t\tsettingMap[\"Language\"] = global.CONF.Base.Language\n\t}\n\tif tags == \"all\" {\n\t\tsettingMap[\"UserName\"] = common.RandStrAndNum(10)\n\t\tsettingMap[\"Password\"] = common.RandStrAndNum(10)\n\t\tsettingMap[\"SecurityEntrance\"] = common.RandStrAndNum(10)\n\t}\n\tif strings.Contains(global.CONF.Base.ChangeUserInfo, \"username\") {\n\t\tsettingMap[\"UserName\"] = common.RandStrAndNum(10)\n\t}\n\tif strings.Contains(global.CONF.Base.ChangeUserInfo, \"password\") {\n\t\tsettingMap[\"Password\"] = common.RandStrAndNum(10)\n\t}\n\tif strings.Contains(global.CONF.Base.ChangeUserInfo, \"entrance\") {\n\t\tsettingMap[\"SecurityEntrance\"] = common.RandStrAndNum(10)\n\t}\n\tfor key, val := range settingMap {\n\t\tif len(val) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif key == \"Password\" {\n\t\t\tval, _ = encrypt.StringEncrypt(val)\n\t\t}\n\t\tif err := settingRepo.Update(key, val); err != nil {\n\t\t\tglobal.LOG.Errorf(\"update %s before start failed, err: %v\", key, err)\n\t\t}\n\t}\n\n\t_, _ = cmd.RunDefaultWithStdoutBashCf(\"%s sed -i '/CHANGE_USER_INFO=%v/d' /usr/local/bin/1pctl\", cmd.SudoHandleCmd(), global.CONF.Base.ChangeUserInfo)\n\t_, _ = cmd.RunDefaultWithStdoutBashCf(\"%s sed -i -e 's#ORIGINAL_PASSWORD=.*#ORIGINAL_PASSWORD=**********#g' /usr/local/bin/1pctl\", cmd.SudoHandleCmd())\n}\n\nfunc generateKey() {\n\tif err := service.NewISettingService().GenerateRSAKey(); err != nil {\n\t\tglobal.LOG.Errorf(\"generate rsa key error : %s\", err.Error())\n\t}\n}\n\nfunc initDockerConf() {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"which docker\")\n\tif err != nil {\n\t\treturn\n\t}\n\tdockerPath := stdout\n\tif strings.Contains(dockerPath, \"snap\") {\n\t\tconstant.DaemonJsonPath = \"/var/snap/docker/current/config/daemon.json\"\n\t}\n}\n"
  },
  {
    "path": "core/init/log/log.go",
    "content": "package log\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/log\"\n\n\t\"github.com/sirupsen/logrus\"\n)\n\nconst (\n\tTimeFormat         = \"2006-01-02 15:04:05\"\n\tFileTImeFormat     = \"2006-01-02\"\n\tRollingTimePattern = \"0 0  * * *\"\n)\n\nfunc Init() {\n\tl := logrus.New()\n\tsetOutput(l, global.CONF.LogConfig)\n\tglobal.LOG = l\n\tglobal.LOG.Info(\"init logger successfully\")\n}\n\nfunc setOutput(logger *logrus.Logger, config global.LogConfig) {\n\twriter, err := log.NewWriterFromConfig(&log.Config{\n\t\tLogPath:            path.Join(global.CONF.Base.InstallDir, \"1panel/log\"),\n\t\tFileName:           config.LogName,\n\t\tTimeTagFormat:      FileTImeFormat,\n\t\tMaxRemain:          config.MaxBackup,\n\t\tRollingTimePattern: RollingTimePattern,\n\t\tLogSuffix:          config.LogSuffix,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tlevel, err := logrus.ParseLevel(config.Level)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfileAndStdoutWriter := io.MultiWriter(writer, os.Stdout)\n\n\tlogger.SetOutput(fileAndStdoutWriter)\n\tlogger.SetLevel(level)\n\tlogger.SetFormatter(new(MineFormatter))\n}\n\ntype MineFormatter struct{}\n\nfunc (s *MineFormatter) Format(entry *logrus.Entry) ([]byte, error) {\n\tdetailInfo := \"\"\n\tif entry.Caller != nil {\n\t\tfunction := strings.ReplaceAll(entry.Caller.Function, \"github.com/1Panel-dev/1Panel/core/\", \"\")\n\t\tdetailInfo = fmt.Sprintf(\"(%s: %d)\", function, entry.Caller.Line)\n\t}\n\tif len(entry.Data) == 0 {\n\t\tmsg := fmt.Sprintf(\"[%s] [%s] %s %s \\n\", time.Now().Format(TimeFormat), strings.ToUpper(entry.Level.String()), entry.Message, detailInfo)\n\t\treturn []byte(msg), nil\n\t}\n\tmsg := fmt.Sprintf(\"[%s] [%s] %s %s {%v} \\n\", time.Now().Format(TimeFormat), strings.ToUpper(entry.Level.String()), entry.Message, detailInfo, entry.Data)\n\treturn []byte(msg), nil\n}\n"
  },
  {
    "path": "core/init/migration/helper/menu.go",
    "content": "package helper\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"gorm.io/gorm\"\n)\n\nfunc LoadMenus() string {\n\titem := []dto.ShowMenu{\n\t\t{ID: \"1\", Disabled: true, Title: \"menu.home\", IsShow: true, Label: \"Home-Menu\", Path: \"/\", Sort: 100},\n\t\t{ID: \"2\", Disabled: true, Title: \"menu.apps\", IsShow: true, Label: \"App-Menu\", Path: \"/apps/all\", Sort: 200},\n\t\t{ID: \"3\", Disabled: false, Title: \"menu.aiTools\", IsShow: true, Label: \"AI-Menu\", Path: \"/ai/model\", Sort: 300,\n\t\t\tChildren: []dto.ShowMenu{\n\t\t\t\t{ID: \"44\", Disabled: false, Title: \"aiTools.agents.agents\", IsShow: true, Label: \"Agents\", Path: \"/ai/agents/agent\", Sort: 50},\n\t\t\t\t{ID: \"41\", Disabled: false, Title: \"aiTools.model.localModel\", IsShow: true, Label: \"OllamaModel\", Path: \"/ai/model\", Sort: 100},\n\t\t\t\t{ID: \"42\", Disabled: false, Title: \"menu.mcp\", IsShow: true, Label: \"MCPServer\", Path: \"/ai/mcp\", Sort: 200},\n\t\t\t\t{ID: \"43\", Disabled: false, Title: \"aiTools.gpu.gpu\", IsShow: true, Label: \"GPU\", Path: \"/ai/gpu\", Sort: 300},\n\t\t\t}},\n\t\t{ID: \"4\", Disabled: false, Title: \"menu.website\", IsShow: true, Label: \"Website-Menu\", Path: \"/websites\", Sort: 400,\n\t\t\tChildren: []dto.ShowMenu{\n\t\t\t\t{ID: \"31\", Disabled: false, Title: \"menu.website\", IsShow: true, Label: \"Website\", Path: \"/websites\", Sort: 100},\n\t\t\t\t{ID: \"32\", Disabled: false, Title: \"menu.ssl\", IsShow: true, Label: \"SSL\", Path: \"/websites/ssl\", Sort: 200},\n\t\t\t\t{ID: \"33\", Disabled: false, Title: \"menu.runtime\", IsShow: true, Label: \"PHP\", Path: \"/websites/runtimes/php\", Sort: 300},\n\t\t\t}},\n\t\t{ID: \"5\", Disabled: false, Title: \"menu.database\", IsShow: true, Label: \"Database-Menu\", Path: \"/databases\", Sort: 500},\n\t\t{ID: \"6\", Disabled: false, Title: \"menu.container\", IsShow: true, Label: \"Container-Menu\", Path: \"/containers\", Sort: 600},\n\t\t{ID: \"7\", Disabled: false, Title: \"menu.system\", IsShow: true, Label: \"System-Menu\", Path: \"/hosts/files\", Sort: 700,\n\t\t\tChildren: []dto.ShowMenu{\n\t\t\t\t{ID: \"71\", Disabled: false, Title: \"menu.files\", IsShow: true, Label: \"File\", Path: \"/hosts/files\", Sort: 100},\n\t\t\t\t{ID: \"72\", Disabled: false, Title: \"menu.monitor\", IsShow: true, Label: \"Monitorx\", Path: \"/hosts/monitor/monitor\", Sort: 200},\n\t\t\t\t{ID: \"74\", Disabled: false, Title: \"menu.firewall\", IsShow: true, Label: \"FirewallPort\", Path: \"/hosts/firewall/port\", Sort: 300},\n\t\t\t\t{ID: \"75\", Disabled: false, Title: \"menu.processManage\", IsShow: true, Label: \"Process\", Path: \"/hosts/process/process\", Sort: 400},\n\t\t\t\t{ID: \"76\", Disabled: false, Title: \"menu.ssh\", IsShow: true, Label: \"SSH\", Path: \"/hosts/ssh/ssh\", Sort: 500},\n\t\t\t\t{ID: \"77\", Disabled: false, Title: \"menu.disk\", IsShow: true, Label: \"Disk\", Path: \"/hosts/disk\", Sort: 600},\n\t\t\t}},\n\t\t{ID: \"8\", Disabled: false, Title: \"menu.terminal\", IsShow: true, Label: \"Terminal-Menu\", Path: \"/hosts/terminal\", Sort: 800},\n\t\t{ID: \"10\", Disabled: false, Title: \"menu.cronjob\", IsShow: true, Label: \"Cronjob-Menu\", Path: \"/cronjobs\", Sort: 900},\n\t\t{ID: \"9\", Disabled: false, Title: \"menu.toolbox\", IsShow: true, Label: \"Toolbox-Menu\", Path: \"/toolbox\", Sort: 1000},\n\t\t{ID: \"11\", Disabled: false, Title: \"xpack.menu\", IsShow: true, Label: \"Xpack-Menu\", Sort: 1100,\n\t\t\tChildren: []dto.ShowMenu{\n\t\t\t\t{ID: \"118\", Disabled: false, Title: \"xpack.app.app\", IsShow: true, Label: \"XApp\", Path: \"/xpack/app\", Sort: 100},\n\t\t\t\t{ID: \"112\", Disabled: false, Title: \"xpack.waf.name\", IsShow: true, Label: \"Dashboard\", Path: \"/xpack/waf/dashboard\", Sort: 200},\n\t\t\t\t{ID: \"111\", Disabled: false, Title: \"xpack.node.nodeManagement\", IsShow: true, Label: \"NodeDashboard\", Path: \"/xpack/node/dashboard\", Sort: 300},\n\t\t\t\t{ID: \"119\", Disabled: false, Title: \"xpack.upage\", IsShow: true, Label: \"Upage\", Path: \"/xpack/upage\", Sort: 400},\n\t\t\t\t{ID: \"113\", Disabled: false, Title: \"xpack.monitor.name\", IsShow: true, Label: \"MonitorDashboard\", Path: \"/xpack/monitor/dashboard\", Sort: 500},\n\t\t\t\t{ID: \"114\", Disabled: false, Title: \"xpack.tamper.tamper\", IsShow: true, Label: \"Tamper\", Path: \"/xpack/tamper\", Sort: 600},\n\t\t\t\t{ID: \"120\", Disabled: false, Title: \"xpack.cluster.cluster\", IsShow: true, Label: \"Cluster\", Path: \"/xpack/cluster\", Sort: 700},\n\t\t\t\t{ID: \"115\", Disabled: false, Title: \"xpack.exchange.exchange\", IsShow: true, Label: \"FileExange\", Path: \"/xpack/exchange/file\", Sort: 800},\n\t\t\t\t{ID: \"117\", Disabled: false, Title: \"xpack.setting.setting\", IsShow: true, Label: \"XSetting\", Path: \"/xpack/setting\", Sort: 900},\n\t\t\t}},\n\t\t{ID: \"12\", Disabled: false, Title: \"menu.logs\", IsShow: true, Label: \"Log-Menu\", Path: \"/logs\", Sort: 1200},\n\t\t{ID: \"13\", Disabled: true, Title: \"menu.settings\", IsShow: true, Label: \"Setting-Menu\", Path: \"/settings\", Sort: 1300},\n\t}\n\tmenu, _ := json.Marshal(item)\n\treturn string(menu)\n}\n\nfunc MenuSort() []dto.MenuLabelSort {\n\tvar MenuLabelsWithSort = []dto.MenuLabelSort{\n\t\t{Label: \"Home-Menu\", Sort: 100},\n\t\t{Label: \"App-Menu\", Sort: 200},\n\t\t{Label: \"AI-Menu\", Sort: 300},\n\t\t{Label: \"Agents\", Sort: 50},\n\t\t{Label: \"OllamaModel\", Sort: 100},\n\t\t{Label: \"MCPServer\", Sort: 200},\n\t\t{Label: \"GPU\", Sort: 300},\n\t\t{Label: \"Website-Menu\", Sort: 400},\n\t\t{Label: \"Website\", Sort: 100},\n\t\t{Label: \"SSL\", Sort: 200},\n\t\t{Label: \"PHP\", Sort: 300},\n\t\t{Label: \"Database-Menu\", Sort: 500},\n\t\t{Label: \"Container-Menu\", Sort: 600},\n\t\t{Label: \"System-Menu\", Sort: 700},\n\t\t{Label: \"File\", Sort: 100},\n\t\t{Label: \"Monitorx\", Sort: 200},\n\t\t{Label: \"FirewallPort\", Sort: 300},\n\t\t{Label: \"Process\", Sort: 400},\n\t\t{Label: \"SSH\", Sort: 500},\n\t\t{Label: \"Disk\", Sort: 600},\n\t\t{Label: \"Terminal-Menu\", Sort: 800},\n\t\t{Label: \"Cronjob-Menu\", Sort: 900},\n\t\t{Label: \"Toolbox-Menu\", Sort: 1000},\n\t\t{Label: \"Xpack-Menu\", Sort: 1100},\n\t\t{Label: \"XApp\", Sort: 100},\n\t\t{Label: \"Dashboard\", Sort: 200},\n\t\t{Label: \"Node\", Sort: 300},\n\t\t{Label: \"Upage\", Sort: 400},\n\t\t{Label: \"MonitorDashboard\", Sort: 500},\n\t\t{Label: \"Tamper\", Sort: 600},\n\t\t{Label: \"Cluster\", Sort: 700},\n\t\t{Label: \"FileExange\", Sort: 800},\n\t\t{Label: \"XSetting\", Sort: 900},\n\t\t{Label: \"Log-Menu\", Sort: 1200},\n\t\t{Label: \"Setting-Menu\", Sort: 1300},\n\t}\n\treturn MenuLabelsWithSort\n}\n\nfunc AddMenu(newMenu dto.ShowMenu, parentMenuID string, tx *gorm.DB) error {\n\tvar menuJSON string\n\tif err := tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Pluck(\"value\", &menuJSON).Error; err != nil {\n\t\treturn err\n\t}\n\tif strings.Contains(menuJSON, fmt.Sprintf(`\"%s\"`, newMenu.Label)) && strings.Contains(menuJSON, fmt.Sprintf(`\"%s\"`, newMenu.Path)) {\n\t\treturn nil\n\t}\n\tvar menus []dto.ShowMenu\n\tif err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {\n\t\treturn tx.Model(&model.Setting{}).\n\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\tUpdate(\"value\", LoadMenus()).Error\n\t}\n\tfor i, menu := range menus {\n\t\tif menu.ID == parentMenuID {\n\t\t\texists := false\n\t\t\tfor _, child := range menu.Children {\n\t\t\t\tif child.ID == newMenu.ID {\n\t\t\t\t\texists = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !exists {\n\t\t\t\tmenus[i].Children = append([]dto.ShowMenu{newMenu}, menus[i].Children...)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\tupdatedJSON, err := json.Marshal(menus)\n\tif err != nil {\n\t\treturn tx.Model(&model.Setting{}).\n\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\tUpdate(\"value\", LoadMenus()).Error\n\t}\n\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Update(\"value\", string(updatedJSON)).Error\n}\n\nfunc RemoveMenuByID(menus []dto.ShowMenu, id string) []dto.ShowMenu {\n\tvar result []dto.ShowMenu\n\tfor _, menu := range menus {\n\t\tif menu.ID == id {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(menu.Children) > 0 {\n\t\t\tmenu.Children = RemoveMenuByID(menu.Children, id)\n\t\t}\n\n\t\tresult = append(result, menu)\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "core/init/migration/migrate.go",
    "content": "package migration\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/init/migration/migrations\"\n\n\t\"github.com/go-gormigrate/gormigrate/v2\"\n)\n\nfunc Init() {\n\tm := gormigrate.New(global.DB, gormigrate.DefaultOptions, []*gormigrate.Migration{\n\t\tmigrations.AddTable,\n\t\tmigrations.InitSetting,\n\t\tmigrations.InitOneDrive,\n\t\tmigrations.InitHost,\n\t\tmigrations.InitTerminalSetting,\n\t\tmigrations.AddTaskDB,\n\t\tmigrations.AddPasskeySetting,\n\t\tmigrations.AddPasskeyTrustedProxySetting,\n\t\tmigrations.AddXpackHideMenu,\n\t\tmigrations.UpdateXpackHideMenu,\n\t\tmigrations.UpdateOnedrive,\n\t\tmigrations.AddClusterMenu,\n\t\tmigrations.DeleteXpackHideMenu,\n\t\tmigrations.AddCronjobGroup,\n\t\tmigrations.AddDiskMenu,\n\t\tmigrations.AddAgentsMenu,\n\t\tmigrations.AddSimpleNodeGroup,\n\t\tmigrations.AddUpgradeBackupCopies,\n\t\tmigrations.AddScriptSync,\n\t\tmigrations.UpdateXpackHideMenuSort,\n\t\tmigrations.AdjustXpackNode,\n\t\tmigrations.UpdateAiAgentsMenu,\n\t\tmigrations.AddDashboardCarouselSetting,\n\t\tmigrations.AddEditionSetting,\n\t\tmigrations.UpdateAiLocalModelMenuTitle,\n\t\tmigrations.AddDocSourceSetting,\n\t})\n\tif err := m.Migrate(); err != nil {\n\t\tglobal.LOG.Error(err)\n\t\tpanic(err)\n\t}\n\tglobal.LOG.Info(\"Migration run successfully\")\n}\n"
  },
  {
    "path": "core/init/migration/migrations/init.go",
    "content": "package migrations\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/init/migration/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/encrypt\"\n\t\"github.com/go-gormigrate/gormigrate/v2\"\n\t\"gorm.io/gorm\"\n)\n\nvar AddTable = &gormigrate.Migration{\n\tID: \"20240506-add-table\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.AutoMigrate(\n\t\t\t&model.OperationLog{},\n\t\t\t&model.LoginLog{},\n\t\t\t&model.Setting{},\n\t\t\t&model.BackupAccount{},\n\t\t\t&model.Group{},\n\t\t\t&model.Command{},\n\t\t\t&model.UpgradeLog{},\n\t\t\t&model.ScriptLibrary{},\n\t\t)\n\t},\n}\n\nvar InitSetting = &gormigrate.Migration{\n\tID: \"20200908-add-table-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tencryptKey := common.RandStr(16)\n\t\tif err := tx.Create(&model.Setting{Key: \"UserName\", Value: global.CONF.Base.Username}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tglobal.CONF.Base.EncryptKey = encryptKey\n\t\tpass, _ := encrypt.StringEncrypt(global.CONF.Base.Password)\n\t\tlanguage := \"en\"\n\t\tif global.CONF.Base.Language == \"zh\" {\n\t\t\tlanguage = \"zh\"\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"Password\", Value: pass}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, _ = cmd.RunDefaultWithStdoutBashCf(\"%s sed -i -e 's#ORIGINAL_PASSWORD=.*#ORIGINAL_PASSWORD=**********#g' /usr/local/bin/1pctl\", cmd.SudoHandleCmd())\n\t\tif err := tx.Create(&model.Setting{Key: \"Theme\", Value: \"light\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"MenuTabs\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"PanelName\", Value: \"1Panel\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"Edition\", Value: global.CONF.Base.Edition}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"Language\", Value: language}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"DocSource\", Value: \"withByRegion\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SessionTimeout\", Value: \"86400\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"SSLType\", Value: \"self\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SSLID\", Value: \"0\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SSL\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"DeveloperMode\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyType\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyUrl\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyPort\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyUser\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyPasswd\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ProxyPasswdKeep\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"HideMenu\", Value: helper.LoadMenus()}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"ServerPort\", Value: global.CONF.Conn.Port}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SecurityEntrance\", Value: global.CONF.Conn.Entrance}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"EncryptKey\", Value: encryptKey}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ExpirationTime\", Value: time.Now().AddDate(0, 0, 10).Format(constant.DateTimeLayout)}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ExpirationDays\", Value: \"0\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ComplexityVerification\", Value: constant.StatusEnable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"MFAStatus\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"MFASecret\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"MFAInterval\", Value: \"30\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"PasskeyUserID\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"PasskeyCredentials\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"PasskeyTrustedProxies\", Value: \"127.0.0.1\\n::1\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SystemVersion\", Value: global.CONF.Base.Version}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"SystemStatus\", Value: \"Free\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"BindAddress\", Value: \"0.0.0.0\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"Ipv6\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"BindDomain\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"AllowIPs\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"NoAuthSetting\", Value: \"200\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ApiInterfaceStatus\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ApiKey\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"IpWhiteList\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ApiKeyValidityTime\", Value: \"120\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ScriptVersion\", Value: \"\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := tx.Create(&model.Setting{Key: \"UninstallDeleteImage\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"UpgradeBackup\", Value: \"Enable\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"UninstallDeleteBackup\", Value: constant.StatusDisable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddPasskeySetting = &gormigrate.Migration{\n\tID: \"20250910-add-passkey-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar addSettingsIfMissing = func(tx *gorm.DB, key, value string) error {\n\t\t\tvar setting model.Setting\n\t\t\tif err := tx.Where(\"key = ?\", key).First(&setting).Error; err != nil {\n\t\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\t\treturn tx.Create(&model.Setting{Key: key, Value: value}).Error\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tif err := addSettingsIfMissing(tx, \"PasskeyUserID\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := addSettingsIfMissing(tx, \"PasskeyCredentials\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddPasskeyTrustedProxySetting = &gormigrate.Migration{\n\tID: \"20260210-add-passkey-trusted-proxy-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar addSettingsIfMissing = func(tx *gorm.DB, key, value string) error {\n\t\t\tvar setting model.Setting\n\t\t\tif err := tx.Where(\"key = ?\", key).First(&setting).Error; err != nil {\n\t\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\t\treturn tx.Create(&model.Setting{Key: key, Value: value}).Error\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\treturn addSettingsIfMissing(tx, \"PasskeyTrustedProxies\", \"127.0.0.1\\n::1\")\n\t},\n}\n\nvar InitTerminalSetting = &gormigrate.Migration{\n\tID: \"20240814-init-terminal-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Setting{Key: \"LineHeight\", Value: \"1.2\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"LetterSpacing\", Value: \"0\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"FontSize\", Value: \"12\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"FontFamily\", Value: \"Monaco, Menlo, Consolas, 'Courier New', monospace\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"BackgroundColor\", Value: \"#000000\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ForegroundColor\", Value: \"#f5f5f5\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"CursorBlink\", Value: constant.StatusEnable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"CursorStyle\", Value: \"block\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"Scrollback\", Value: \"1000\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"ScrollSensitivity\", Value: \"6\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitHost = &gormigrate.Migration{\n\tID: \"20240816-init-host\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"host\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"node\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"command\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"website\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"redis\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"script\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar InitOneDrive = &gormigrate.Migration{\n\tID: \"20240808-init-one-drive\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Setting{Key: \"OneDriveID\", Value: \"MDEwOTM1YTktMWFhOS00ODU0LWExZGMtNmU0NWZlNjI4YzZi\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.Setting{Key: \"OneDriveSc\", Value: \"akpuOFF+YkNXOU1OLWRzS1ZSRDdOcG1LT2ZRM0RLNmdvS1RkVWNGRA==\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Create(&model.BackupAccount{\n\t\t\tName: \"localhost\",\n\t\t\tType: \"LOCAL\",\n\t\t\tVars: fmt.Sprintf(\"{\\\"dir\\\":\\\"%s\\\"}\", path.Join(global.CONF.Base.InstallDir, \"1panel/backup\")),\n\t\t}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddTaskDB = &gormigrate.Migration{\n\tID: \"20241125-add-task-table\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn global.TaskDB.AutoMigrate(\n\t\t\t&model.Task{},\n\t\t)\n\t},\n}\n\nvar AddXpackHideMenu = &gormigrate.Migration{\n\tID: \"20250529-add-xpack-hide-menu\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar menuJSON string\n\t\tif err := tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Pluck(\"value\", &menuJSON).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif strings.Contains(menuJSON, `\"XApp\"`) && strings.Contains(menuJSON, `\"/xpack/app\"`) {\n\t\t\treturn nil\n\t\t}\n\n\t\tvar menus []dto.ShowMenu\n\t\tif err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\tnewItem := dto.ShowMenu{\n\t\t\tID:       \"118\",\n\t\t\tDisabled: false,\n\t\t\tTitle:    \"xpack.app.app\",\n\t\t\tIsShow:   true,\n\t\t\tLabel:    \"XApp\",\n\t\t\tPath:     \"/xpack/app\",\n\t\t}\n\n\t\tfor i, menu := range menus {\n\t\t\tif menu.ID == \"11\" {\n\t\t\t\texists := false\n\t\t\t\tfor _, child := range menu.Children {\n\t\t\t\t\tif child.ID == newItem.ID {\n\t\t\t\t\t\texists = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !exists {\n\t\t\t\t\tmenus[i].Children = append([]dto.ShowMenu{newItem}, menus[i].Children...)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tupdatedJSON, err := json.Marshal(menus)\n\t\tif err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Update(\"value\", string(updatedJSON)).Error\n\t},\n}\n\nvar UpdateXpackHideMenu = &gormigrate.Migration{\n\tID: \"20250617-update-xpack-hide-menu\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar menuJSON string\n\t\tif err := tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Pluck(\"value\", &menuJSON).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar menus []dto.ShowMenu\n\t\tif err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\t\tnewItem := dto.ShowMenu{\n\t\t\tID:       \"119\",\n\t\t\tDisabled: false,\n\t\t\tTitle:    \"xpack.upage\",\n\t\t\tIsShow:   true,\n\t\t\tLabel:    \"Upage\",\n\t\t\tPath:     \"/xpack/upage\",\n\t\t}\n\n\t\tfor i, menu := range menus {\n\t\t\tif menu.ID == \"11\" {\n\t\t\t\texists := false\n\t\t\t\tfor _, child := range menu.Children {\n\t\t\t\t\tif child.ID == newItem.ID {\n\t\t\t\t\t\texists = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif exists {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tinsertIndex := -1\n\t\t\t\tfor j, child := range menu.Children {\n\t\t\t\t\tif child.ID == \"111\" {\n\t\t\t\t\t\tinsertIndex = j\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif insertIndex != -1 {\n\t\t\t\t\tchildren := menu.Children\n\t\t\t\t\tmenus[i].Children = append(children[:insertIndex+1], append([]dto.ShowMenu{newItem}, children[insertIndex+1:]...)...)\n\t\t\t\t} else {\n\t\t\t\t\tmenus[i].Children = append([]dto.ShowMenu{newItem}, menus[i].Children...)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tfor i, menu := range menus {\n\t\t\tif menu.ID == \"11\" {\n\t\t\t\texistsIndex := -1\n\t\t\t\tfor j, child := range menu.Children {\n\t\t\t\t\tif child.ID == \"118\" {\n\t\t\t\t\t\texistsIndex = j\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif existsIndex == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tvar item118 dto.ShowMenu\n\t\t\t\tif existsIndex != -1 {\n\t\t\t\t\titem118 = menu.Children[existsIndex]\n\t\t\t\t\tmenus[i].Children = append(menu.Children[:existsIndex], menu.Children[existsIndex+1:]...)\n\t\t\t\t} else {\n\t\t\t\t\titem118 = dto.ShowMenu{\n\t\t\t\t\t\tID:       \"118\",\n\t\t\t\t\t\tDisabled: false,\n\t\t\t\t\t\tTitle:    \"xpack.app.app\",\n\t\t\t\t\t\tIsShow:   true,\n\t\t\t\t\t\tLabel:    \"XApp\",\n\t\t\t\t\t\tPath:     \"/xpack/app\",\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tmenus[i].Children = append([]dto.ShowMenu{item118}, menus[i].Children...)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tvar idx9, idx10 = -1, -1\n\t\tfor i, menu := range menus {\n\t\t\tif menu.ID == \"9\" && menu.Path == \"/toolbox\" {\n\t\t\t\tidx9 = i\n\t\t\t}\n\t\t\tif menu.ID == \"10\" && menu.Path == \"/cronjobs\" {\n\t\t\t\tidx10 = i\n\t\t\t}\n\t\t}\n\t\tif idx9 != -1 && idx10 != -1 && idx10 > idx9 {\n\t\t\tmenus[idx9], menus[idx10] = menus[idx10], menus[idx9]\n\t\t}\n\n\t\tfor i, menu := range menus {\n\t\t\tif menu.ID == \"7\" {\n\t\t\t\tfor j, child := range menu.Children {\n\t\t\t\t\tif child.ID == \"75\" {\n\t\t\t\t\t\tif child.Title != \"menu.processManage\" {\n\t\t\t\t\t\t\tmenus[i].Children[j].Title = \"menu.processManage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tupdatedJSON, err := json.Marshal(menus)\n\t\tif err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Update(\"value\", string(updatedJSON)).Error\n\t},\n}\n\nvar UpdateOnedrive = &gormigrate.Migration{\n\tID: \"20250704-update-onedrive\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Model(&model.Setting{}).\n\t\t\tWhere(\"key = ?\", \"OneDriveID\").\n\t\t\tUpdate(\"value\", \"NTQ0NmNmZTMtNGM3OS00N2EwLWFlMjUtZmM2NDU0NzhlMmQ5\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := tx.Model(&model.Setting{}).\n\t\t\tWhere(\"key = ?\", \"OneDriveSc\").\n\t\t\tUpdate(\"value\", \"bGRlOFF+WEVrR1M0b25Vb1VsRWpMYzE2MW9rTXZEM25KdnZ1MGN6MA==\").Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddClusterMenu = &gormigrate.Migration{\n\tID: \"20250707-add-cluster-menu\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn helper.AddMenu(dto.ShowMenu{\n\t\t\tID:       \"120\",\n\t\t\tDisabled: false,\n\t\t\tTitle:    \"xpack.cluster.cluster\",\n\t\t\tIsShow:   true,\n\t\t\tLabel:    \"Cluster\",\n\t\t\tPath:     \"/xpack/cluster\",\n\t\t}, \"11\", tx)\n\t},\n}\n\nvar DeleteXpackHideMenu = &gormigrate.Migration{\n\tID: \"20250718-delete-xpack-hide-menu\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar menuJSON string\n\t\tif err := tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Pluck(\"value\", &menuJSON).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif menuJSON == \"\" {\n\t\t\tmenuJSON = helper.LoadMenus()\n\t\t}\n\t\tvar menus []dto.ShowMenu\n\t\tif err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\t\tmenus = helper.RemoveMenuByID(menus, \"116\")\n\t\tupdatedJSON, err := json.Marshal(menus)\n\t\tif err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Update(\"value\", string(updatedJSON)).Error\n\t},\n}\n\nvar AddCronjobGroup = &gormigrate.Migration{\n\tID: \"20250729-add-cronjob-group\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"cronjob\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddDiskMenu = &gormigrate.Migration{\n\tID: \"20250811-add-disk-menu\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn helper.AddMenu(dto.ShowMenu{\n\t\t\tID:       \"77\",\n\t\t\tDisabled: false,\n\t\t\tTitle:    \"menu.disk\",\n\t\t\tIsShow:   true,\n\t\t\tLabel:    \"Disk\",\n\t\t\tPath:     \"/hosts/disk\",\n\t\t}, \"7\", tx)\n\t},\n}\n\nvar AddAgentsMenu = &gormigrate.Migration{\n\tID: \"20260204-add-agents-menu\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn helper.AddMenu(dto.ShowMenu{\n\t\t\tID:       \"44\",\n\t\t\tDisabled: false,\n\t\t\tTitle:    \"aiTools.agents.agents\",\n\t\t\tIsShow:   true,\n\t\t\tLabel:    \"Agents\",\n\t\t\tPath:     \"/ai/agents/agent\",\n\t\t}, \"4\", tx)\n\t},\n}\n\nvar AddSimpleNodeGroup = &gormigrate.Migration{\n\tID: \"20250916-add-simple-node-group\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Group{Name: \"Default\", Type: \"SimpleNode\", IsDefault: true}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddUpgradeBackupCopies = &gormigrate.Migration{\n\tID: \"20250925-add-upgrade-backup-copies\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Setting{Key: \"UpgradeBackupCopies\", Value: \"0\"}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddScriptSync = &gormigrate.Migration{\n\tID: \"20250916-add-script-sync\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tif err := tx.Create(&model.Setting{Key: \"ScriptSync\", Value: constant.StatusEnable}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddDashboardCarouselSetting = &gormigrate.Migration{\n\tID: \"20260210-add-dashboard-carousel-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar addSettingsIfMissing = func(tx *gorm.DB, key, value string) error {\n\t\t\tvar setting model.Setting\n\t\t\tif err := tx.Where(\"key = ?\", key).First(&setting).Error; err != nil {\n\t\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\t\treturn tx.Create(&model.Setting{Key: key, Value: value}).Error\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tif err := addSettingsIfMissing(tx, \"DashboardMemoVisible\", constant.StatusEnable); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := addSettingsIfMissing(tx, \"DashboardSimpleNodeVisible\", constant.StatusEnable); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateXpackHideMenuSort = &gormigrate.Migration{\n\tID: \"20251009-update-xpack-hide-menu-sort\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar menuJSON string\n\t\tif err := tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Pluck(\"value\", &menuJSON).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar menus []dto.ShowMenu\n\t\tif err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\tlabelSortMap := make(map[string]int)\n\t\tfor _, item := range helper.MenuSort() {\n\t\t\tlabelSortMap[item.Label] = item.Sort\n\t\t}\n\n\t\tfor i := range menus {\n\t\t\tif sortVal, exists := labelSortMap[menus[i].Label]; exists {\n\t\t\t\tmenus[i].Sort = sortVal\n\t\t\t} else if menus[i].Sort <= 0 {\n\t\t\t\tmenus[i].Sort = (i + 1) * 100\n\t\t\t}\n\n\t\t\tfor j := range menus[i].Children {\n\t\t\t\tif childSort, exists := labelSortMap[menus[i].Children[j].Label]; exists {\n\t\t\t\t\tmenus[i].Children[j].Sort = childSort\n\t\t\t\t} else if menus[i].Children[j].Sort <= 0 {\n\t\t\t\t\tmenus[i].Children[j].Sort = menus[i].Sort + (j+1)*10\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tupdatedJSON, err := json.Marshal(menus)\n\t\tif err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Update(\"value\", string(updatedJSON)).Error\n\t},\n}\n\nvar AdjustXpackNode = &gormigrate.Migration{\n\tID: \"20260108-adjust-xpack-node\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar menuJSON string\n\t\tif err := tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Pluck(\"value\", &menuJSON).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif menuJSON == \"\" {\n\t\t\tmenuJSON = helper.LoadMenus()\n\t\t}\n\t\tvar menus []dto.ShowMenu\n\t\tif err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\tfor i := 0; i < len(menus); i++ {\n\t\t\tif menus[i].Label != \"Xpack-Menu\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor j := 0; j < len(menus[i].Children); j++ {\n\t\t\t\tif menus[i].Children[j].Label == \"Node\" {\n\t\t\t\t\tmenus[i].Children[j].Label = \"NodeDashboard\"\n\t\t\t\t\tmenus[i].Children[j].Path = \"/xpack/node/dashboard\"\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tupdatedJSON, err := json.Marshal(menus)\n\t\tif err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Update(\"value\", string(updatedJSON)).Error\n\t},\n}\n\nvar UpdateAiAgentsMenu = &gormigrate.Migration{\n\tID: \"20260209-update-ai-agents-menu\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar menuJSON string\n\t\tif err := tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Pluck(\"value\", &menuJSON).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif menuJSON == \"\" {\n\t\t\tmenuJSON = helper.LoadMenus()\n\t\t}\n\n\t\tvar menus []dto.ShowMenu\n\t\tif err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\tfoundAI := false\n\t\tnewItem := dto.ShowMenu{\n\t\t\tID:       \"44\",\n\t\t\tDisabled: false,\n\t\t\tTitle:    \"aiTools.agents.agents\",\n\t\t\tIsShow:   true,\n\t\t\tLabel:    \"Agents\",\n\t\t\tPath:     \"/ai/agents/agent\",\n\t\t\tSort:     50,\n\t\t}\n\n\t\tfor i := range menus {\n\t\t\tif menus[i].Label != \"AI-Menu\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfoundAI = true\n\t\t\tmenus[i].IsShow = true\n\n\t\t\texists := false\n\t\t\tfor j := range menus[i].Children {\n\t\t\t\tchild := &menus[i].Children[j]\n\t\t\t\tif child.Label == newItem.Label {\n\t\t\t\t\texists = true\n\t\t\t\t\tchild.IsShow = true\n\t\t\t\t\tif child.Title == \"\" {\n\t\t\t\t\t\tchild.Title = newItem.Title\n\t\t\t\t\t}\n\t\t\t\t\tif child.Sort == 0 {\n\t\t\t\t\t\tchild.Sort = newItem.Sort\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !exists {\n\t\t\t\tmenus[i].Children = append([]dto.ShowMenu{newItem}, menus[i].Children...)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\tif !foundAI {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\tupdatedJSON, err := json.Marshal(menus)\n\t\tif err != nil {\n\t\t\treturn tx.Model(&model.Setting{}).\n\t\t\t\tWhere(\"key = ?\", \"HideMenu\").\n\t\t\t\tUpdate(\"value\", helper.LoadMenus()).Error\n\t\t}\n\n\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"HideMenu\").Update(\"value\", string(updatedJSON)).Error\n\t},\n}\n\nvar AddEditionSetting = &gormigrate.Migration{\n\tID: \"20260224-add-edition-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar setting model.Setting\n\t\tif err := tx.Where(\"key = ?\", \"Edition\").First(&setting).Error; err != nil {\n\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\treturn tx.Create(&model.Setting{Key: \"Edition\", Value: global.CONF.Base.Edition}).Error\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif setting.Value == \"\" {\n\t\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"Edition\").Update(\"value\", global.CONF.Base.Edition).Error\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar AddDocSourceSetting = &gormigrate.Migration{\n\tID: \"20260316-add-doc-source-setting\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\tvar setting model.Setting\n\t\tif err := tx.Where(\"key = ?\", \"DocSource\").First(&setting).Error; err != nil {\n\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\treturn tx.Create(&model.Setting{Key: \"DocSource\", Value: \"withByRegion\"}).Error\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tif setting.Value == \"\" {\n\t\t\treturn tx.Model(&model.Setting{}).Where(\"key = ?\", \"DocSource\").Update(\"value\", \"withByRegion\").Error\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar UpdateAiLocalModelMenuTitle = &gormigrate.Migration{\n\tID: \"20260307-update-ai-local-model-menu-title\",\n\tMigrate: func(tx *gorm.DB) error {\n\t\treturn tx.Exec(\n\t\t\t`UPDATE settings\n\t\t\t SET value = REPLACE(value, 'aiTools.model.model', 'aiTools.model.localModel')\n\t\t\t WHERE key = 'HideMenu'\n\t\t\t   AND value LIKE '%aiTools.model.model%'`,\n\t\t).Error\n\t},\n}\n"
  },
  {
    "path": "core/init/proxy/proxy.go",
    "content": "package proxy\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"time\"\n)\n\nconst SockPath = \"/etc/1panel/agent.sock\"\n\nvar (\n\tLocalAgentProxy *httputil.ReverseProxy\n)\n\nfunc Init() {\n\tdialer := &net.Dialer{\n\t\tTimeout: 5 * time.Second,\n\t}\n\tdialUnix := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\treturn dialer.DialContext(ctx, \"unix\", SockPath)\n\t}\n\ttransport := &http.Transport{\n\t\tDialContext:         dialUnix,\n\t\tForceAttemptHTTP2:   false,\n\t\tMaxIdleConns:        50,\n\t\tMaxIdleConnsPerHost: 50,\n\t\tIdleConnTimeout:     30 * time.Second,\n\t}\n\tLocalAgentProxy = &httputil.ReverseProxy{\n\t\tDirector: func(req *http.Request) {\n\t\t\treq.URL.Scheme = \"http\"\n\t\t\treq.URL.Host = \"unix\"\n\t\t},\n\t\tTransport: transport,\n\t\tErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) {\n\t\t\trw.WriteHeader(http.StatusBadGateway)\n\t\t\t_, _ = rw.Write([]byte(\"Bad Gateway: \" + err.Error()))\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "core/init/router/proxy.go",
    "content": "package router\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/init/proxy\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/res\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc Proxy() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\treqPath := c.Request.URL.Path\n\t\tif strings.HasPrefix(reqPath, \"/1panel/swagger\") || !strings.HasPrefix(c.Request.URL.Path, \"/api/v2\") {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tif strings.HasPrefix(reqPath, \"/api/v2/core\") && !strings.HasPrefix(c.Request.URL.Path, \"/api/v2/core/xpack\") {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tvar nodeItem string\n\t\tqueryNode := c.Query(\"operateNode\")\n\t\tif queryNode != \"\" && queryNode != \"undefined\" {\n\t\t\tnodeItem = queryNode\n\t\t} else {\n\t\t\tnodeItem = c.Request.Header.Get(\"CurrentNode\")\n\t\t}\n\t\tcurrentNode, err := url.QueryUnescape(nodeItem)\n\t\tif err != nil {\n\t\t\thelper.ErrorWithDetail(c, http.StatusBadRequest, \"ErrProxy\", err)\n\t\t\treturn\n\t\t}\n\n\t\tapiReq := c.GetBool(\"API_AUTH\")\n\n\t\tif !apiReq && strings.HasPrefix(c.Request.URL.Path, \"/api/v2/\") && !isLocalAPI(c.Request.URL.Path) && !checkSession(c) {\n\t\t\tdata, _ := res.ErrorMsg.ReadFile(\"html/401.html\")\n\t\t\tc.Data(401, \"text/html; charset=utf-8\", data)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\tif !strings.HasPrefix(c.Request.URL.Path, \"/api/v2/core\") && (currentNode == \"local\" || len(currentNode) == 0) {\n\t\t\tdefer func() {\n\t\t\t\tif err := recover(); err != nil && err != http.ErrAbortHandler {\n\t\t\t\t\tglobal.LOG.Debug(err)\n\t\t\t\t}\n\t\t\t}()\n\t\t\tproxy.LocalAgentProxy.ServeHTTP(c.Writer, c.Request)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\txpack.Proxy(c, currentNode)\n\t\tc.Abort()\n\t}\n}\n\nfunc checkSession(c *gin.Context) bool {\n\tpsession, err := global.SESSION.Get(c)\n\tif err != nil {\n\t\treturn false\n\t}\n\tsettingRepo := repo.NewISettingRepo()\n\tsessionTimeout, err := settingRepo.GetValueByKey(\"SessionTimeout\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tlifeTime, _ := strconv.Atoi(sessionTimeout)\n\tssl, err := settingRepo.GetValueByKey(\"SSL\")\n\tif err != nil {\n\t\treturn false\n\t}\n\t_ = global.SESSION.Set(c, psession, ssl == constant.StatusEnable, lifeTime)\n\treturn true\n}\n\nfunc isLocalAPI(urlPath string) bool {\n\treturn urlPath == \"/api/v2/core/xpack/sync/ssl\"\n}\n"
  },
  {
    "path": "core/init/router/router.go",
    "content": "package router\n\nimport (\n\t\"encoding/base64\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/service\"\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/docs\"\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/web\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/1Panel-dev/1Panel/core/init/swagger\"\n\t\"github.com/1Panel-dev/1Panel/core/middleware\"\n\trou \"github.com/1Panel-dev/1Panel/core/router\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/security\"\n\t\"github.com/gin-contrib/gzip\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nvar (\n\tRouter *gin.Engine\n)\n\nfunc setWebStatic(rootRouter *gin.RouterGroup) {\n\trootRouter.StaticFS(\"/public\", http.FS(web.Favicon))\n\trootRouter.StaticFS(\"/favicon.ico\", http.FS(web.Favicon))\n\tRegisterImages(rootRouter)\n\tsetStaticResource(rootRouter)\n\trootRouter.GET(\"/assets/*filepath\", func(c *gin.Context) {\n\t\tc.Writer.Header().Set(\"Cache-Control\", \"private, max-age=2628000, immutable\")\n\t\tif c.Request.URL.Path[len(c.Request.URL.Path)-1] == '/' {\n\t\t\tc.AbortWithStatus(http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\t\tstaticServer := http.FileServer(http.FS(web.Assets))\n\t\tstaticServer.ServeHTTP(c.Writer, c.Request)\n\t})\n\tauthService := service.NewIAuthService()\n\tentrance := authService.GetSecurityEntrance()\n\tif entrance != \"\" {\n\t\trootRouter.GET(\"/\"+entrance, func(c *gin.Context) {\n\t\t\tcurrentEntrance := authService.GetSecurityEntrance()\n\t\t\tif currentEntrance != entrance {\n\t\t\t\tsecurity.HandleNotSecurity(c, \"\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsecurity.ToIndexHtml(c)\n\t\t})\n\t}\n\trootRouter.GET(\"/\", func(c *gin.Context) {\n\t\tif !security.CheckSecurity(c) {\n\t\t\treturn\n\t\t}\n\t\tentrance = authService.GetSecurityEntrance()\n\t\tif entrance != \"\" {\n\t\t\tentranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))\n\t\t\tc.SetCookie(\"SecurityEntrance\", entranceValue, 0, \"\", \"\", false, true)\n\t\t}\n\t\tstaticServer := http.FileServer(http.FS(web.IndexHtml))\n\t\tstaticServer.ServeHTTP(c.Writer, c.Request)\n\t})\n}\n\nfunc Routers() *gin.Engine {\n\tRouter = gin.New()\n\tRouter.Use(i18n.UseI18n())\n\tRouter.Use(middleware.WhiteAllow())\n\tRouter.Use(middleware.BindDomain())\n\n\tswaggerRouter := Router.Group(\"1panel\")\n\tdocs.SwaggerInfo.BasePath = \"/api/v2\"\n\tswaggerRouter.Use(middleware.SessionAuth()).GET(\"/swagger/*any\", swagger.SwaggerHandler())\n\n\tPublicGroup := Router.Group(\"\")\n\t{\n\t\tPublicGroup.Use(gzip.Gzip(gzip.DefaultCompression))\n\t\tsetWebStatic(PublicGroup)\n\t}\n\tif global.CONF.Base.IsDemo {\n\t\tRouter.Use(middleware.DemoHandle())\n\t}\n\n\tRouter.Use(middleware.OperationLog())\n\tRouter.Use(middleware.GlobalLoading())\n\tRouter.Use(middleware.PasswordExpired())\n\tRouter.Use(middleware.ApiAuth())\n\n\tPrivateGroup := Router.Group(\"/api/v2/core\")\n\tPrivateGroup.Use(middleware.SetPasswordPublicKey())\n\tfor _, router := range rou.RouterGroupApp {\n\t\trouter.InitRouter(PrivateGroup)\n\t}\n\n\tRouter.Use(Proxy())\n\tRouter.NoRoute(func(c *gin.Context) {\n\t\tif !security.HandleNotRoute(c) {\n\t\t\treturn\n\t\t}\n\t\tsecurity.HandleNotSecurity(c, \"\")\n\t})\n\n\treturn Router\n}\n\nfunc RegisterImages(rootRouter *gin.RouterGroup) {\n\tstaticDir := filepath.Join(global.CONF.Base.InstallDir, \"1panel/uploads/theme\")\n\trootRouter.GET(\"/api/v2/images/*filename\", func(c *gin.Context) {\n\t\tfileName := filepath.Base(c.Param(\"filename\"))\n\t\tfilePath := filepath.Join(staticDir, fileName)\n\t\tif !strings.HasPrefix(filePath, staticDir) {\n\t\t\tc.AbortWithStatus(http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\t\tf, err := os.Open(filePath)\n\t\tif err != nil {\n\t\t\tc.Status(http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\tdefer f.Close()\n\t\tbuf := make([]byte, 512)\n\t\tn, _ := f.Read(buf)\n\t\tcontent := buf[:n]\n\t\tmimeType := http.DetectContentType(buf[:n])\n\t\tif strings.Contains(string(content), \"<svg\") {\n\t\t\tmimeType = \"image/svg+xml\"\n\t\t}\n\t\t_, _ = f.Seek(0, io.SeekStart)\n\t\tc.Header(\"Content-Type\", mimeType)\n\t\t_, _ = io.Copy(c.Writer, f)\n\t})\n}\n\nfunc setStaticResource(rootRouter *gin.RouterGroup) {\n\trootRouter.GET(\"/api/v2/static/*filename\", func(c *gin.Context) {\n\t\tc.Writer.Header().Set(\"Cache-Control\", \"private, max-age=2628000\")\n\t\tfilename := c.Param(\"filename\")\n\t\tfilePath := \"static\" + filename\n\t\tdata, err := web.Static.ReadFile(filePath)\n\t\tif err != nil {\n\t\t\tc.AbortWithStatus(http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\tc.Writer.Header().Set(\"Content-Type\", \"application/json; charset=utf-8\")\n\t\tc.Writer.Write(data)\n\t})\n}\n"
  },
  {
    "path": "core/init/run/run.go",
    "content": "package run\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/app/service\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n)\n\nfunc Init() {\n\tscriptSync, _ := repo.NewISettingRepo().GetValueByKey(\"ScriptSync\")\n\tif !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {\n\t\tif err := service.NewIScriptService().Sync(dto.OperateByTaskID{}); err != nil {\n\t\t\tglobal.LOG.Errorf(\"sync scripts from remote failed, err: %v\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/init/session/psession/psession.go",
    "content": "package psession\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/glebarez/sqlite\"\n\t\"github.com/gorilla/securecookie\"\n\t\"github.com/gorilla/sessions\"\n\t\"github.com/wader/gormstore/v2\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n)\n\ntype SessionUser struct {\n\tID   uint   `json:\"id\"`\n\tName string `json:\"name\"`\n}\n\ntype PSession struct {\n\tStore *gormstore.Store\n\tdb    *gorm.DB\n}\n\nfunc NewPSession(dbPath string) *PSession {\n\tnewLogger := logger.New(\n\t\tlog.New(os.Stdout, \"\\r\\n\", log.LstdFlags),\n\t\tlogger.Config{\n\t\t\tSlowThreshold:             time.Second,\n\t\t\tLogLevel:                  logger.Silent,\n\t\t\tIgnoreRecordNotFoundError: true,\n\t\t\tColorful:                  false,\n\t\t},\n\t)\n\tdb, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{\n\t\tDisableForeignKeyConstraintWhenMigrating: true,\n\t\tLogger:                                   newLogger,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tsqlDB, dbError := db.DB()\n\tif dbError != nil {\n\t\tpanic(dbError)\n\t}\n\tsqlDB.SetMaxOpenConns(4)\n\tsqlDB.SetMaxIdleConns(1)\n\tsqlDB.SetConnMaxIdleTime(15 * time.Minute)\n\tsqlDB.SetConnMaxLifetime(time.Hour)\n\n\tstore := gormstore.New(db, securecookie.GenerateRandomKey(32))\n\treturn &PSession{\n\t\tStore: store,\n\t\tdb:    db,\n\t}\n}\n\nfunc (p *PSession) Get(c *gin.Context) (SessionUser, error) {\n\tvar result SessionUser\n\tsession, err := p.Store.Get(c.Request, constant.SessionName)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tdata, ok := session.Values[\"user\"]\n\tif !ok {\n\t\treturn result, errors.New(\"ErrSessionDataNotFound\")\n\t}\n\tbytes, ok := data.([]byte)\n\tif !ok {\n\t\treturn result, errors.New(\"ErrSessionDataFormat\")\n\t}\n\terr = json.Unmarshal(bytes, &result)\n\treturn result, err\n}\n\nfunc (p *PSession) Set(c *gin.Context, user SessionUser, secure bool, ttlSeconds int) error {\n\tsession, err := p.Store.Get(c.Request, constant.SessionName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdata, err := json.Marshal(user)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsession.Values[\"user\"] = data\n\tsession.Options = &sessions.Options{\n\t\tPath:     \"/\",\n\t\tMaxAge:   ttlSeconds,\n\t\tHttpOnly: true,\n\t\tSecure:   secure,\n\t}\n\treturn p.Store.Save(c.Request, c.Writer, session)\n}\n\nfunc (p *PSession) Delete(c *gin.Context) error {\n\tsession, err := p.Store.Get(c.Request, constant.SessionName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsession.Values = make(map[interface{}]interface{})\n\tsession.Options.MaxAge = -1\n\treturn p.Store.Save(c.Request, c.Writer, session)\n}\n\nfunc (p *PSession) Clean() error {\n\tp.db.Table(\"sessions\").Where(\"1=1\").Delete(nil)\n\treturn nil\n}\n"
  },
  {
    "path": "core/init/session/session.go",
    "content": "package session\n\nimport (\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/init/session/psession\"\n)\n\nfunc Init() {\n\tglobal.SESSION = psession.NewPSession(path.Join(global.CONF.Base.InstallDir, \"1panel/db/session.db\"))\n\tglobal.LOG.Info(\"init session successfully\")\n}\n"
  },
  {
    "path": "core/init/swagger/swag.go",
    "content": "package swagger\n\nimport (\n\t\"net/http\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/docs\"\n\t\"github.com/gin-gonic/gin\"\n\tswaggerfiles \"github.com/swaggo/files/v2\"\n)\n\nvar matcher = regexp.MustCompile(fileMatchPattern)\n\nconst swaggerDocFile = \"doc.json\"\n\nconst fileMatchPattern = `(.*)(index\\.html|index\\.css|swagger-initializer\\.js|doc\\.json|favicon-16x16\\.png|favicon-32x32\\.png|/oauth2-redirect\\.html|swagger-ui\\.css|swagger-ui\\.css\\.map|swagger-ui\\.js|swagger-ui\\.js\\.map|swagger-ui-bundle\\.js|swagger-ui-bundle\\.js\\.map|swagger-ui-standalone-preset\\.js|swagger-ui-standalone-preset\\.js\\.map)[?|.]*`\nconst CustomSwaggerInitializerJS = `window.onload = function() {\n  //<editor-fold desc=\"Changeable Configuration Block\">\n  window.ui = SwaggerUIBundle({\n    url: \"{{.URL}}\",\n    dom_id: '#swagger-ui',\n    deepLinking: true,\n    presets: [\n      SwaggerUIBundle.presets.apis,\n      SwaggerUIStandalonePreset\n    ],\n    plugins: [\n      SwaggerUIBundle.plugins.DownloadUrl\n    ],\n    layout: \"StandaloneLayout\"\n  });\n\n  //</editor-fold>\n};\n`\n\nfunc SwaggerHandler() gin.HandlerFunc {\n\tfileServer := http.StripPrefix(\"/1panel/swagger/\", http.FileServer(http.FS(swaggerfiles.FS)))\n\n\treturn func(c *gin.Context) {\n\t\tpath := c.Request.URL.Path\n\t\tpath = strings.TrimPrefix(path, \"/1panel/swagger\")\n\t\tif !matcher.MatchString(path) && path != \"/\" {\n\t\t\t// 404\n\t\t\tc.AbortWithStatus(http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\t\tswitch path {\n\t\tcase \"/doc.json\":\n\t\t\tc.Header(\"Content-Type\", \"application/json; charset=utf-8\")\n\t\t\tc.Header(\"Cache-Control\", \"private, max-age=600\")\n\t\t\tc.String(http.StatusOK, docs.SwaggerInfo.ReadDoc())\n\t\tcase \"/swagger-initializer.js\":\n\t\t\tc.Header(\"Content-Type\", \"application/javascript; charset=utf-8\")\n\t\t\tc.Header(\"Cache-Control\", \"private, max-age=600\")\n\t\t\tc.String(http.StatusOK, strings.ReplaceAll(CustomSwaggerInitializerJS, \"{{.URL}}\", swaggerDocFile))\n\t\tdefault:\n\n\t\t\tc.Header(\"Cache-Control\", \"private, max-age=600\")\n\t\t\tfileServer.ServeHTTP(c.Writer, c.Request)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/init/validator/validator.go",
    "content": "package validator\n\nimport (\n\t\"regexp\"\n\t\"unicode\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\n\t\"github.com/go-playground/validator/v10\"\n)\n\nfunc Init() {\n\tvalidator := validator.New()\n\tif err := validator.RegisterValidation(\"name\", checkNamePattern); err != nil {\n\t\tpanic(err)\n\t}\n\tif err := validator.RegisterValidation(\"ip\", checkIpPattern); err != nil {\n\t\tpanic(err)\n\t}\n\tif err := validator.RegisterValidation(\"password\", checkPasswordPattern); err != nil {\n\t\tpanic(err)\n\t}\n\tglobal.VALID = validator\n}\n\nfunc checkNamePattern(fl validator.FieldLevel) bool {\n\tvalue := fl.Field().String()\n\tresult, err := regexp.MatchString(\"^[a-zA-Z\\u4e00-\\u9fa5]{1}[a-zA-Z0-9_\\u4e00-\\u9fa5]{0,30}$\", value)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"regexp matchString failed, %v\", err)\n\t}\n\treturn result\n}\n\nfunc checkIpPattern(fl validator.FieldLevel) bool {\n\tvalue := fl.Field().String()\n\tresult, err := regexp.MatchString(`^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$`, value)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"regexp check ip matchString failed, %v\", err)\n\t}\n\treturn result\n}\n\nfunc checkPasswordPattern(fl validator.FieldLevel) bool {\n\tvalue := fl.Field().String()\n\tif len(value) < 8 || len(value) > 30 {\n\t\treturn false\n\t}\n\n\thasNum := false\n\thasLetter := false\n\tfor _, r := range value {\n\t\tif unicode.IsLetter(r) && !hasLetter {\n\t\t\thasLetter = true\n\t\t}\n\t\tif unicode.IsNumber(r) && !hasNum {\n\t\t\thasNum = true\n\t\t}\n\t\tif hasLetter && hasNum {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "core/init/viper/viper.go",
    "content": "package viper\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/conf\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/fsnotify/fsnotify\"\n\t\"github.com/spf13/viper\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc Init() {\n\tbaseDir := \"/opt\"\n\tport := \"9999\"\n\tmode := \"\"\n\tversion := \"v2.0.0\"\n\tusername, password, entrance, language, edition := \"\", \"\", \"\", \"zh\", \"\"\n\tv := viper.NewWithOptions()\n\tv.SetConfigType(\"yaml\")\n\n\tconfig := global.ServerConfig{}\n\tif err := yaml.Unmarshal(conf.AppYaml, &config); err != nil {\n\t\tpanic(err)\n\t}\n\tif config.Base.Mode != \"\" {\n\t\tmode = config.Base.Mode\n\t}\n\t_, err := os.Stat(\"/opt/1panel/conf/app.yaml\")\n\tif mode == \"dev\" && err == nil {\n\t\tv.SetConfigName(\"app\")\n\t\tv.AddConfigPath(path.Join(\"/opt/1panel/conf\"))\n\t\tif err := v.ReadInConfig(); err != nil {\n\t\t\tpanic(fmt.Errorf(\"fatal error config file: %s\", err))\n\t\t}\n\t} else {\n\t\tbaseDir = common.LoadParams(\"BASE_DIR\")\n\t\tport = common.LoadParams(\"ORIGINAL_PORT\")\n\t\tversion = common.LoadParams(\"ORIGINAL_VERSION\")\n\t\tusername = common.LoadParams(\"ORIGINAL_USERNAME\")\n\t\tpassword = common.LoadParams(\"ORIGINAL_PASSWORD\")\n\t\tentrance = common.LoadParams(\"ORIGINAL_ENTRANCE\")\n\t\tlanguage = common.LoadParams(\"LANGUAGE\")\n\t\tedition = common.LoadParamsWithoutPanic(\"PANEL_EDITION\")\n\n\t\treader := bytes.NewReader(conf.AppYaml)\n\t\tif err := v.ReadConfig(reader); err != nil {\n\t\t\tpanic(fmt.Errorf(\"fatal error config file: %s\", err))\n\t\t}\n\t}\n\tv.OnConfigChange(func(e fsnotify.Event) {\n\t\tif err := v.Unmarshal(&global.CONF); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t})\n\tserverConfig := global.ServerConfig{}\n\tif err := v.Unmarshal(&serverConfig); err != nil {\n\t\tpanic(err)\n\t}\n\t_, err = os.Stat(\"/opt/1panel/conf/app.yaml\")\n\tif mode == \"dev\" && err == nil {\n\t\tif serverConfig.Base.InstallDir != \"\" {\n\t\t\tbaseDir = serverConfig.Base.InstallDir\n\t\t}\n\t\tif serverConfig.Conn.Port != \"\" {\n\t\t\tport = serverConfig.Conn.Port\n\t\t}\n\t\tif serverConfig.Base.Version != \"\" {\n\t\t\tversion = serverConfig.Base.Version\n\t\t}\n\t\tif serverConfig.Base.Username != \"\" {\n\t\t\tusername = serverConfig.Base.Username\n\t\t}\n\t\tif serverConfig.Base.Password != \"\" {\n\t\t\tpassword = serverConfig.Base.Password\n\t\t}\n\t\tif serverConfig.Conn.Entrance != \"\" {\n\t\t\tentrance = serverConfig.Conn.Entrance\n\t\t}\n\t}\n\n\tglobal.CONF = serverConfig\n\tglobal.CONF.Base.InstallDir = baseDir\n\tglobal.CONF.Base.IsDemo = v.GetBool(\"base.is_demo\")\n\tglobal.CONF.Base.IsFxplay = v.GetBool(\"base.is_fxplay\")\n\tglobal.CONF.Base.IsOffLine = v.GetBool(\"base.is_offline\")\n\tif edition == \"intl\" {\n\t\tglobal.CONF.Base.Edition = \"intl\"\n\t} else {\n\t\tglobal.CONF.Base.Edition = \"cn\"\n\t}\n\tglobal.CONF.Base.Version = version\n\tglobal.CONF.Base.Username = username\n\tglobal.CONF.Base.Password = password\n\tglobal.CONF.Base.Language = language\n\tglobal.CONF.Base.ChangeUserInfo = loadChangeInfo()\n\tglobal.CONF.Conn.Entrance = entrance\n\tglobal.CONF.Conn.Port = port\n\tglobal.Viper = v\n}\n\nfunc loadChangeInfo() string {\n\tstdout, err := cmd.RunDefaultWithStdoutBashC(\"grep '^CHANGE_USER_INFO=' /usr/local/bin/1pctl | cut -d'=' -f2\")\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strings.ReplaceAll(stdout, \"\\n\", \"\")\n}\n"
  },
  {
    "path": "core/log/config.go",
    "content": "package log\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n)\n\nvar (\n\tBufferSize         = 0x100000\n\tDefaultFileMode    = os.FileMode(constant.DirPerm)\n\tDefaultFileFlag    = os.O_RDWR | os.O_CREATE | os.O_APPEND\n\tErrInvalidArgument = errors.New(\"error argument invalid\")\n\tQueueSize          = 1024\n\tLogQueueSize       = 1024\n\tErrClosed          = errors.New(\"error write on close\")\n)\n\ntype Config struct {\n\tTimeTagFormat      string\n\tLogPath            string\n\tFileName           string\n\tLogSuffix          string\n\tMaxRemain          int\n\tRollingTimePattern string\n}\n\ntype Manager interface {\n\tFire() chan string\n\tClose()\n}\n\ntype RollingWriter interface {\n\tio.Writer\n\tClose() error\n}\n\nfunc FilePath(c *Config) (filepath string) {\n\tfilepath = path.Join(c.LogPath, c.FileName) + c.LogSuffix\n\treturn\n}\n"
  },
  {
    "path": "core/log/dup_write_darwin.go",
    "content": "package log\n\nimport (\n\t\"golang.org/x/sys/unix\"\n\t\"os\"\n\t\"runtime\"\n)\n\nvar stdErrFileHandler *os.File\n\nfunc dupWrite(file *os.File) error {\n\tstdErrFileHandler = file\n\tif err := unix.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {\n\t\treturn err\n\t}\n\truntime.SetFinalizer(stdErrFileHandler, func(fd *os.File) {\n\t\tfd.Close()\n\t})\n\treturn nil\n}\n"
  },
  {
    "path": "core/log/dup_write_linux.go",
    "content": "package log\n\nimport (\n\t\"golang.org/x/sys/unix\"\n\t\"os\"\n\t\"runtime\"\n)\n\nvar stdErrFileHandler *os.File\n\nfunc dupWrite(file *os.File) error {\n\tstdErrFileHandler = file\n\tif err := unix.Dup2(int(file.Fd()), int(os.Stderr.Fd())); err != nil {\n\t\treturn err\n\t}\n\truntime.SetFinalizer(stdErrFileHandler, func(fd *os.File) {\n\t\tfd.Close()\n\t})\n\treturn nil\n}\n"
  },
  {
    "path": "core/log/dup_write_windows.go",
    "content": "package log\n\nimport (\n\t\"os\"\n)\n\nfunc dupWrite(file *os.File) error {\n\treturn nil\n}\n"
  },
  {
    "path": "core/log/manager.go",
    "content": "package log\n\nimport (\n\t\"github.com/robfig/cron/v3\"\n\t\"path\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype manager struct {\n\tstartAt time.Time\n\tfire    chan string\n\tcr      *cron.Cron\n\tcontext chan int\n\twg      sync.WaitGroup\n\tlock    sync.Mutex\n}\n\nfunc (m *manager) Fire() chan string {\n\treturn m.fire\n}\n\nfunc (m *manager) Close() {\n\tclose(m.context)\n\tm.cr.Stop()\n}\n\nfunc NewManager(c *Config) (Manager, error) {\n\tm := &manager{\n\t\tstartAt: time.Now(),\n\t\tcr:      cron.New(),\n\t\tfire:    make(chan string),\n\t\tcontext: make(chan int),\n\t\twg:      sync.WaitGroup{},\n\t}\n\n\tif _, err := m.cr.AddFunc(c.RollingTimePattern, func() {\n\t\tm.fire <- m.GenLogFileName(c)\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\tm.cr.Start()\n\n\treturn m, nil\n}\n\nfunc (m *manager) GenLogFileName(c *Config) (filename string) {\n\tm.lock.Lock()\n\tfilename = path.Join(c.LogPath, c.FileName+\"-\"+m.startAt.Format(c.TimeTagFormat)) + c.LogSuffix\n\tm.startAt = time.Now()\n\tm.lock.Unlock()\n\treturn\n}\n"
  },
  {
    "path": "core/log/writer.go",
    "content": "package log\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n)\n\ntype Writer struct {\n\tm             Manager\n\tfile          *os.File\n\tabsPath       string\n\tfire          chan string\n\tcf            *Config\n\trollingfilech chan string\n\tqueue         chan []byte\n}\n\ntype AsynchronousWriter struct {\n\tWriter\n\tctx     chan int\n\terrChan chan error\n\tclosed  int32\n\twg      sync.WaitGroup\n}\n\nfunc (w *AsynchronousWriter) Close() error {\n\tif atomic.CompareAndSwapInt32(&w.closed, 0, 1) {\n\t\tclose(w.ctx)\n\t\tw.onClose()\n\n\t\tfunc() {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tglobal.LOG.Error(r)\n\t\t\t\t}\n\t\t\t}()\n\t\t\tw.m.Close()\n\t\t}()\n\t\treturn w.file.Close()\n\t}\n\treturn ErrClosed\n}\n\nfunc (w *AsynchronousWriter) onClose() {\n\tvar err error\n\tfor {\n\t\tselect {\n\t\tcase b := <-w.queue:\n\t\t\tif _, err = w.file.Write(b); err != nil {\n\t\t\t\tselect {\n\t\t\t\tcase w.errChan <- err:\n\t\t\t\tdefault:\n\t\t\t\t\t_asyncBufferPool.Put(&b)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\t_asyncBufferPool.Put(&b)\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nvar _asyncBufferPool = sync.Pool{\n\tNew: func() interface{} {\n\t\treturn make([]byte, BufferSize)\n\t},\n}\n\nfunc NewWriterFromConfig(c *Config) (RollingWriter, error) {\n\tif c.LogPath == \"\" || c.FileName == \"\" {\n\t\treturn nil, ErrInvalidArgument\n\t}\n\tif err := os.MkdirAll(c.LogPath, 0700); err != nil {\n\t\treturn nil, err\n\t}\n\tfilepath := FilePath(c)\n\tfile, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_APPEND, constant.FilePerm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := dupWrite(file); err != nil {\n\t\treturn nil, err\n\t}\n\tmng, err := NewManager(c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar rollingWriter RollingWriter\n\twriter := Writer{\n\t\tqueue:   make(chan []byte, LogQueueSize),\n\t\tm:       mng,\n\t\tfile:    file,\n\t\tabsPath: filepath,\n\t\tfire:    mng.Fire(),\n\t\tcf:      c,\n\t}\n\tif c.MaxRemain > 0 {\n\t\twriter.rollingfilech = make(chan string, c.MaxRemain)\n\t\tdir, err := os.ReadDir(c.LogPath)\n\t\tif err != nil {\n\t\t\tmng.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfiles := make([]string, 0, 10)\n\t\tfor _, fi := range dir {\n\t\t\tif fi.IsDir() {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfileName := c.FileName\n\t\t\tif strings.Contains(fi.Name(), fileName) && strings.Contains(fi.Name(), c.LogSuffix) {\n\t\t\t\tstart := strings.Index(fi.Name(), \"-\")\n\t\t\t\tend := strings.Index(fi.Name(), c.LogSuffix)\n\t\t\t\tname := fi.Name()\n\t\t\t\tif start > 0 && end > 0 {\n\t\t\t\t\t_, err := time.Parse(c.TimeTagFormat, name[start+1:end])\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tfiles = append(files, fi.Name())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsort.Slice(files, func(i, j int) bool {\n\t\t\tt1Start := strings.Index(files[i], \"-\")\n\t\t\tt1End := strings.Index(files[i], c.LogSuffix)\n\t\t\tt2Start := strings.Index(files[i], \"-\")\n\t\t\tt2End := strings.Index(files[i], c.LogSuffix)\n\t\t\tt1, _ := time.Parse(c.TimeTagFormat, files[i][t1Start+1:t1End])\n\t\t\tt2, _ := time.Parse(c.TimeTagFormat, files[j][t2Start+1:t2End])\n\t\t\treturn t1.Before(t2)\n\t\t})\n\n\t\tfor _, file := range files {\n\t\tretry:\n\t\t\tselect {\n\t\t\tcase writer.rollingfilech <- path.Join(c.LogPath, file):\n\t\t\tdefault:\n\t\t\t\twriter.DoRemove()\n\t\t\t\tgoto retry\n\t\t\t}\n\t\t}\n\t}\n\n\twr := &AsynchronousWriter{\n\t\tctx:     make(chan int),\n\t\terrChan: make(chan error, QueueSize),\n\t\twg:      sync.WaitGroup{},\n\t\tclosed:  0,\n\t\tWriter:  writer,\n\t}\n\trollingWriter = wr\n\twr.wg.Add(1)\n\tgo wr.writer()\n\twr.wg.Wait()\n\n\treturn rollingWriter, nil\n}\n\nfunc (w *AsynchronousWriter) writer() {\n\tvar err error\n\tw.wg.Done()\n\tfor {\n\t\tselect {\n\t\tcase filename := <-w.fire:\n\t\t\tif err = w.Reopen(filename); err != nil && len(w.errChan) < cap(w.errChan) {\n\t\t\t\tw.errChan <- err\n\t\t\t}\n\t\tcase b := <-w.queue:\n\t\t\tif _, err = w.file.Write(b); err != nil && len(w.errChan) < cap(w.errChan) {\n\t\t\t\tw.errChan <- err\n\t\t\t}\n\t\t\t_asyncBufferPool.Put(&b)\n\t\tcase <-w.ctx:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (w *Writer) DoRemove() {\n\tfile := <-w.rollingfilech\n\tif err := os.Remove(file); err != nil {\n\t\tlog.Println(\"error in remove log file\", file, err)\n\t}\n}\n\nfunc (w *Writer) Write(b []byte) (int, error) {\n\tvar ok = false\n\tfor !ok {\n\t\tselect {\n\t\tcase filename := <-w.fire:\n\t\t\tif err := w.Reopen(filename); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\tdefault:\n\t\t\tok = true\n\t\t}\n\t}\n\n\tselect {\n\tcase w.queue <- b:\n\t\treturn len(b), nil\n\t}\n}\n\nfunc (w *Writer) Reopen(file string) error {\n\tfileInfo, err := w.file.Stat()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif fileInfo.Size() == 0 {\n\t\treturn nil\n\t}\n\n\tw.file.Close()\n\tif err := os.Rename(w.absPath, file); err != nil {\n\t\treturn err\n\t}\n\tnewFile, err := os.OpenFile(w.absPath, DefaultFileFlag, DefaultFileMode)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tw.file = newFile\n\n\tgo func() {\n\t\tif w.cf.MaxRemain > 0 {\n\t\tretry:\n\t\t\tselect {\n\t\t\tcase w.rollingfilech <- file:\n\t\t\tdefault:\n\t\t\t\tw.DoRemove()\n\t\t\t\tgoto retry\n\t\t\t}\n\t\t}\n\t}()\n\treturn nil\n}\n"
  },
  {
    "path": "core/middleware/api_auth.go",
    "content": "package middleware\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc ApiAuth() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tif strings.HasPrefix(c.Request.URL.Path, \"/api/v2/core/auth\") {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tpanelToken := c.GetHeader(\"1Panel-Token\")\n\t\tpanelTimestamp := c.GetHeader(\"1Panel-Timestamp\")\n\t\tif panelToken != \"\" || panelTimestamp != \"\" {\n\t\t\tif global.Api.ApiInterfaceStatus == constant.StatusEnable {\n\t\t\t\tclientIP := c.ClientIP()\n\t\t\t\tif !isValid1PanelTimestamp(panelTimestamp) {\n\t\t\t\t\thelper.BadAuth(c, \"ErrApiConfigKeyTimeInvalid\", nil)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !isValid1PanelToken(panelToken, panelTimestamp) {\n\t\t\t\t\thelper.BadAuth(c, \"ErrApiConfigKeyInvalid\", nil)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif !isIPInWhiteList(clientIP) {\n\t\t\t\t\thelper.BadAuth(c, \"ErrApiConfigIPInvalid\", nil)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tc.Set(\"API_AUTH\", true)\n\t\t\t\tc.Next()\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\thelper.BadAuth(c, \"ErrApiConfigStatusInvalid\", nil)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc isValid1PanelTimestamp(panelTimestamp string) bool {\n\tapiKeyValidityTime := global.Api.ApiKeyValidityTime\n\tapiTime, err := strconv.Atoi(apiKeyValidityTime)\n\tif err != nil || apiTime < 0 {\n\t\tglobal.LOG.Errorf(\"apiTime %d, err: %v\", apiTime, err)\n\t\treturn false\n\t}\n\tif apiTime == 0 {\n\t\treturn true\n\t}\n\tpanelTime, err := strconv.ParseInt(panelTimestamp, 10, 64)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"panelTimestamp %s, panelTime %d, apiTime %d, err: %v\", panelTimestamp, apiTime, panelTime, err)\n\t\treturn false\n\t}\n\tnowTime := time.Now().Unix()\n\ttolerance := int64(60)\n\tif panelTime > nowTime+tolerance {\n\t\tglobal.LOG.Errorf(\"Valid Panel Timestamp, apiTime %d, panelTime %d, nowTime %d, err: %v\", apiTime, panelTime, nowTime, err)\n\t\treturn false\n\t}\n\treturn nowTime-panelTime <= int64(apiTime)*60+tolerance\n}\n\nfunc isValid1PanelToken(panelToken string, panelTimestamp string) bool {\n\tsystem1PanelToken := global.Api.ApiKey\n\treturn panelToken == GenerateMD5(\"1panel\"+system1PanelToken+panelTimestamp)\n}\n\nfunc isIPInWhiteList(clientIP string) bool {\n\tipWhiteString := global.Api.IpWhiteList\n\tif len(ipWhiteString) == 0 {\n\t\tglobal.LOG.Error(\"IP whitelist is empty\")\n\t\treturn false\n\t}\n\tipWhiteList, ipErr := common.HandleIPList(ipWhiteString)\n\tif ipErr != nil {\n\t\tglobal.LOG.Errorf(\"Failed to handle IP list: %v\", ipErr)\n\t\treturn false\n\t}\n\tclientParsedIP := net.ParseIP(clientIP)\n\tif clientParsedIP == nil {\n\t\treturn false\n\t}\n\tiPv4 := clientParsedIP.To4()\n\tiPv6 := clientParsedIP.To16()\n\tfor _, cidr := range ipWhiteList {\n\t\tif (iPv4 != nil && (cidr == \"0.0.0.0\" || cidr == \"0.0.0.0/0\" || iPv4.String() == cidr)) || (iPv6 != nil && (cidr == \"::/0\" || iPv6.String() == cidr)) {\n\t\t\treturn true\n\t\t}\n\t\t_, ipNet, err := net.ParseCIDR(cidr)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif (iPv4 != nil && ipNet.Contains(iPv4)) || (iPv6 != nil && ipNet.Contains(iPv6)) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc GenerateMD5(param string) string {\n\thash := md5.New()\n\thash.Write([]byte(param))\n\treturn hex.EncodeToString(hash.Sum(nil))\n}\n"
  },
  {
    "path": "core/middleware/bind_domain.go",
    "content": "package middleware\n\nimport (\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc BindDomain() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tlocalRequest := c.GetBool(\"LOCAL_REQUEST\")\n\t\tif localRequest {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tsettingRepo := repo.NewISettingRepo()\n\t\tbindDomain, err := settingRepo.GetValueByKey(\"BindDomain\")\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\tif len(bindDomain) == 0 {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tdomains := c.Request.Host\n\t\tparts := strings.Split(c.Request.Host, \":\")\n\t\tif len(parts) > 0 {\n\t\t\tdomains = parts[0]\n\t\t}\n\n\t\tif domains != bindDomain {\n\t\t\tcode := LoadErrCode()\n\t\t\thelper.ErrWithHtml(c, code, \"err_domain\")\n\t\t\treturn\n\t\t}\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "core/middleware/demo_handle.go",
    "content": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nvar whiteUrlList = map[string]struct{}{\n\t\"/api/v2/dashboard/app/launcher/option\": {},\n\t\"/api/v2/websites/config\":               {},\n\t\"/api/v2/websites/waf/config\":           {},\n\t\"/api/v2/files/loadfile\":                {},\n\t\"/api/v2/files/size\":                    {},\n\t\"/api/v2/runtimes/sync\":                 {},\n\t\"/api/v2/toolbox/device/base\":           {},\n\t\"/api/v2/files/user/group\":              {},\n\t\"/api/v2/files/mount\":                   {},\n\t\"/api/v2/hosts/ssh/log\":                 {},\n\t\"/api/v2/toolbox/clam/base\":             {},\n\t\"/api/v2/hosts/too\":                     {},\n\t\"/api/v2/backups/record/size\":           {},\n\n\t\"/api/v2/core/auth/login\":     {},\n\t\"/api/v2/core/logs/login\":     {},\n\t\"/api/v2/core/logs/operation\": {},\n\t\"/api/v2/core/auth/logout\":    {},\n\n\t\"/api/v2/apps/installed/loadport\": {},\n\t\"/api/v2/apps/installed/check\":    {},\n\t\"/api/v2/apps/installed/conninfo\": {},\n\t\"/api/v2/databases/load/file\":     {},\n\t\"/api/v2/databases/variables\":     {},\n\t\"/api/v2/databases/status\":        {},\n\t\"/api/v2/databases/baseinfo\":      {},\n\n\t\"/api/v2/xpack/waf/attack/stat\":    {},\n\t\"/api/v2/xpack/waf/config/website\": {},\n\t\"/api/v2/xpack/waf/relation/stat\":  {},\n\n\t\"/api/v2/xpack/monitor/stat\":         {},\n\t\"/api/v2/xpack/monitor/visitors\":     {},\n\t\"/api/v2/xpack/monitor/visitors/loc\": {},\n\t\"/api/v2/xpack/monitor/qps\":          {},\n\t\"/api/v2/xpack/monitor/logs/stat\":    {},\n\t\"/api/v2/xpack/monitor/websites\":     {},\n\t\"/api/v2/xpack/monitor/trend\":        {},\n\t\"/api/v2/xpack/monitor/rank\":         {},\n\t\"/api/v2/xpack/waf/cdn\":              {},\n\n\t\"/api/v2/core/nodes/list\": {},\n}\n\nfunc DemoHandle() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tif strings.Contains(c.Request.URL.Path, \"search\") || (c.Request.Method == http.MethodGet && c.Request.URL.Path != \"/api/v2/containers/exec\") {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tif _, ok := whiteUrlList[c.Request.URL.Path]; ok {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tc.JSON(http.StatusInternalServerError, dto.Response{\n\t\t\tCode:    http.StatusInternalServerError,\n\t\t\tMessage: buserr.New(\"ErrDemoEnvironment\").Error(),\n\t\t})\n\t\tc.Abort()\n\t}\n}\n"
  },
  {
    "path": "core/middleware/helper.go",
    "content": "package middleware\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n)\n\nfunc LoadErrCode() int {\n\tsettingRepo := repo.NewISettingRepo()\n\tcodeVal, err := settingRepo.GetValueByKey(\"NoAuthSetting\")\n\tif err != nil {\n\t\treturn 500\n\t}\n\n\tswitch codeVal {\n\tcase \"400\":\n\t\treturn http.StatusBadRequest\n\tcase \"401\":\n\t\treturn http.StatusUnauthorized\n\tcase \"403\":\n\t\treturn http.StatusForbidden\n\tcase \"404\":\n\t\treturn http.StatusNotFound\n\tcase \"408\":\n\t\treturn http.StatusRequestTimeout\n\tcase \"416\":\n\t\treturn http.StatusRequestedRangeNotSatisfiable\n\tcase \"500\":\n\t\treturn http.StatusInternalServerError\n\tcase \"444\":\n\t\treturn 444\n\tdefault:\n\t\treturn http.StatusOK\n\t}\n}\n"
  },
  {
    "path": "core/middleware/ip_limit.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc WhiteAllow() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\ttokenString := c.GetHeader(\"X-Panel-Local-Token\")\n\t\tclientIP := common.GetRealClientIP(c)\n\t\tif clientIP == \"127.0.0.1\" && tokenString != \"\" && c.Request.URL.Path == \"/api/v2/core/xpack/sync/ssl\" {\n\t\t\tc.Set(\"LOCAL_REQUEST\", true)\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tif common.IsPrivateIP(clientIP) {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tsettingRepo := repo.NewISettingRepo()\n\t\tallowIPs, err := settingRepo.GetValueByKey(\"AllowIPs\")\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\n\t\tif len(allowIPs) == 0 {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tfor _, ip := range strings.Split(allowIPs, \",\") {\n\t\t\tif len(ip) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif ip == clientIP || (strings.Contains(ip, \"/\") && common.CheckIpInCidr(ip, clientIP)) {\n\t\t\t\tc.Next()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tcode := LoadErrCode()\n\t\thelper.ErrWithHtml(c, code, \"err_ip_limit\")\n\t}\n}\n"
  },
  {
    "path": "core/middleware/loading.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc GlobalLoading() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tsettingRepo := repo.NewISettingRepo()\n\t\tstatus, err := settingRepo.GetValueByKey(\"SystemStatus\")\n\t\tif err != nil {\n\t\t\thelper.InternalServer(c, err)\n\t\t\treturn\n\t\t}\n\t\tif status != \"Free\" {\n\t\t\thelper.ErrorWithDetail(c, 407, status, err)\n\t\t\treturn\n\t\t}\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "core/middleware/operation.go",
    "content": "package middleware\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/docs\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/glebarez/sqlite\"\n\t\"gorm.io/gorm\"\n)\n\nconst (\n\theaderNeedOperationResolve = \"X-Need-Op-Resolve\"\n\theaderOperationResolved    = \"X-Op-Resolved\"\n)\n\nfunc OperationLog() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tc.Request.Header.Del(headerNeedOperationResolve)\n\n\t\tif strings.Contains(c.Request.URL.Path, \"search\") || c.Request.Method == http.MethodGet {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tsource := loadLogInfo(c.Request.URL.Path)\n\t\tpathItem := strings.TrimPrefix(c.Request.URL.Path, \"/api/v2\")\n\t\tpathItem = strings.TrimPrefix(pathItem, \"/api/v2/core\")\n\t\tcurrentNodeItem := c.Request.Header.Get(\"CurrentNode\")\n\t\tcurrentNode, _ := url.QueryUnescape(currentNodeItem)\n\t\trecord := &model.OperationLog{\n\t\t\tSource:    source,\n\t\t\tNode:      currentNode,\n\t\t\tIP:        c.ClientIP(),\n\t\t\tMethod:    strings.ToLower(c.Request.Method),\n\t\t\tPath:      pathItem,\n\t\t\tUserAgent: c.Request.UserAgent(),\n\t\t}\n\t\tswagger := make(map[string]operationJson)\n\t\tif err := json.Unmarshal(docs.XLogJson, &swagger); err != nil {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\toperationDic, hasPath := swagger[record.Path]\n\t\tif !hasPath {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tif len(operationDic.FormatZH) == 0 {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tformatMap := make(map[string]interface{})\n\t\tif len(operationDic.BodyKeys) != 0 {\n\t\t\tbody, err := io.ReadAll(c.Request.Body)\n\t\t\tif err == nil {\n\t\t\t\tc.Request.Body = io.NopCloser(bytes.NewBuffer(body))\n\t\t\t}\n\t\t\tbodyMap := make(map[string]interface{})\n\t\t\tif strings.Contains(c.Request.Header.Get(\"Content-Type\"), \"multipart/form-data\") {\n\t\t\t\tbodyMap, _ = parseMultipart(body, c.Request.Header.Get(\"Content-Type\"))\n\t\t\t} else {\n\t\t\t\tdecoder := json.NewDecoder(bytes.NewReader(body))\n\t\t\t\tdecoder.UseNumber()\n\t\t\t\t_ = decoder.Decode(&bodyMap)\n\t\t\t}\n\t\t\tfor _, key := range operationDic.BodyKeys {\n\t\t\t\tif _, ok := bodyMap[key]; ok {\n\t\t\t\t\tformatMap[key] = bodyMap[key]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tneedAgentResolve := len(operationDic.BeforeFunctions) != 0 && len(currentNode) != 0 && currentNode != \"local\" && !strings.HasPrefix(record.Path, \"/core\")\n\t\tallowCoreFallback := strings.HasPrefix(record.Path, \"/core/xpack\") || !willProxy(c.Request.URL.Path, currentNode) || len(currentNode) == 0 || currentNode == \"local\"\n\t\tif needAgentResolve {\n\t\t\tc.Request.Header.Set(headerNeedOperationResolve, \"1\")\n\t\t\tdefer func() {\n\t\t\t\tc.Request.Header.Del(headerNeedOperationResolve)\n\t\t\t}()\n\t\t}\n\n\t\twriter := responseBodyWriter{\n\t\t\tResponseWriter: c.Writer,\n\t\t\tbody:           &bytes.Buffer{},\n\t\t\tcaptureBody:    shouldCaptureResponseBody(c.Request.URL.Path),\n\t\t}\n\t\tc.Writer = &writer\n\t\tnow := time.Now()\n\n\t\tc.Next()\n\n\t\tif len(operationDic.BeforeFunctions) != 0 {\n\t\t\tif needAgentResolve {\n\t\t\t\tmergeResolvedData(writer.resolvedHeader, formatMap)\n\t\t\t}\n\n\t\t\tif allowCoreFallback && !hasAllResolvedData(formatMap, operationDic.BeforeFunctions) {\n\t\t\t\tdbItem, err := newDB(record.Path)\n\t\t\t\tif err == nil {\n\t\t\t\t\tresolveByDB(dbItem, formatMap, operationDic.BeforeFunctions)\n\t\t\t\t\tcloseDB(dbItem)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfillOperationDetail(&operationDic, formatMap)\n\t\trecord.DetailEN = strings.ReplaceAll(operationDic.FormatEN, \"[]\", \"\")\n\t\trecord.DetailZH = strings.ReplaceAll(operationDic.FormatZH, \"[]\", \"\")\n\n\t\tdatas := writer.body.Bytes()\n\t\tlogRepo := repo.NewILogRepo()\n\t\tif c.Request.Header.Get(\"Content-Encoding\") == \"gzip\" {\n\t\t\tbuf := bytes.NewReader(writer.body.Bytes())\n\t\t\treader, err := gzip.NewReader(buf)\n\t\t\tif err != nil {\n\t\t\t\trecord.Status = constant.StatusFailed\n\t\t\t\trecord.Message = fmt.Sprintf(\"gzip new reader failed, err: %v\", err)\n\t\t\t\tlatency := time.Since(now)\n\t\t\t\trecord.Latency = latency\n\n\t\t\t\tif err := logRepo.CreateOperationLog(record); err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"create operation record failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer reader.Close()\n\t\t\tdatas, _ = io.ReadAll(reader)\n\t\t}\n\t\tvar res response\n\t\tcontentType := strings.ToLower(c.Writer.Header().Get(\"Content-Type\"))\n\t\tisJSONResponse := strings.Contains(contentType, \"application/json\")\n\t\tif isJSONResponse {\n\t\t\t_ = json.Unmarshal(datas, &res)\n\t\t\tif res.Code == 200 {\n\t\t\t\trecord.Status = constant.StatusSuccess\n\t\t\t} else {\n\t\t\t\trecord.Status = constant.StatusFailed\n\t\t\t\trecord.Message = res.Message\n\t\t\t}\n\t\t} else {\n\t\t\tstatusCode := c.Writer.Status()\n\t\t\tif statusCode >= 200 && statusCode < 400 {\n\t\t\t\trecord.Status = constant.StatusSuccess\n\t\t\t} else {\n\t\t\t\trecord.Status = constant.StatusFailed\n\t\t\t\trecord.Message = http.StatusText(statusCode)\n\t\t\t}\n\t\t}\n\n\t\tlatency := time.Since(now)\n\t\trecord.Latency = latency\n\n\t\tif err := logRepo.CreateOperationLog(record); err != nil {\n\t\t\tglobal.LOG.Errorf(\"create operation record failed, err: %v\", err)\n\t\t}\n\t}\n}\n\nfunc fillOperationDetail(operationDic *operationJson, formatMap map[string]interface{}) {\n\tfor key, value := range formatMap {\n\t\tif !strings.Contains(operationDic.FormatEN, \"[\"+key+\"]\") {\n\t\t\tcontinue\n\t\t}\n\t\tt := reflect.TypeOf(value)\n\t\tif t == nil || (t.Kind() != reflect.Array && t.Kind() != reflect.Slice) {\n\t\t\toperationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, \"[\"+key+\"]\", fmt.Sprintf(\"[%v]\", value))\n\t\t\toperationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, \"[\"+key+\"]\", fmt.Sprintf(\"[%v]\", value))\n\t\t\tcontinue\n\t\t}\n\n\t\tval := reflect.ValueOf(value)\n\t\tlength := val.Len()\n\t\telements := make([]string, 0, length)\n\t\tfor i := 0; i < length; i++ {\n\t\t\telements = append(elements, fmt.Sprintf(\"%v\", val.Index(i).Interface()))\n\t\t}\n\t\treplaced := fmt.Sprintf(\"[%v]\", strings.Join(elements, \",\"))\n\t\toperationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, \"[\"+key+\"]\", replaced)\n\t\toperationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, \"[\"+key+\"]\", replaced)\n\t}\n}\n\ntype operationJson struct {\n\tAPI             string         `json:\"api\"`\n\tMethod          string         `json:\"method\"`\n\tBodyKeys        []string       `json:\"bodyKeys\"`\n\tParamKeys       []string       `json:\"paramKeys\"`\n\tBeforeFunctions []functionInfo `json:\"beforeFunctions\"`\n\tFormatZH        string         `json:\"formatZH\"`\n\tFormatEN        string         `json:\"formatEN\"`\n}\ntype functionInfo struct {\n\tInputColumn  string `json:\"input_column\"`\n\tInputValue   string `json:\"input_value\"`\n\tIsList       bool   `json:\"isList\"`\n\tDB           string `json:\"db\"`\n\tOutputColumn string `json:\"output_column\"`\n\tOutputValue  string `json:\"output_value\"`\n}\n\ntype response struct {\n\tCode    int    `json:\"code\"`\n\tMessage string `json:\"message\"`\n}\n\ntype responseBodyWriter struct {\n\tgin.ResponseWriter\n\tbody           *bytes.Buffer\n\tresolvedHeader string\n\tcaptureBody    bool\n}\n\nfunc (r *responseBodyWriter) sanitizeResolvedHeader() {\n\tif len(r.resolvedHeader) == 0 {\n\t\tr.resolvedHeader = r.ResponseWriter.Header().Get(headerOperationResolved)\n\t}\n\tr.ResponseWriter.Header().Del(headerOperationResolved)\n}\n\nfunc (r *responseBodyWriter) WriteHeader(code int) {\n\tr.sanitizeResolvedHeader()\n\tr.ResponseWriter.WriteHeader(code)\n}\n\nfunc (r *responseBodyWriter) WriteHeaderNow() {\n\tr.sanitizeResolvedHeader()\n\tr.ResponseWriter.WriteHeaderNow()\n}\n\nfunc (r *responseBodyWriter) Write(b []byte) (int, error) {\n\tr.sanitizeResolvedHeader()\n\tif r.captureBody {\n\t\tr.body.Write(b)\n\t}\n\treturn r.ResponseWriter.Write(b)\n}\n\nfunc shouldCaptureResponseBody(reqPath string) bool {\n\treqPath = strings.ToLower(reqPath)\n\tif strings.Contains(reqPath, \"download\") {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc loadLogInfo(path string) string {\n\tpath = replaceStr(path, \"/api/v2\", \"/core\", \"/xpack\")\n\tif !strings.Contains(path, \"/\") {\n\t\treturn \"\"\n\t}\n\tpathArrays := strings.Split(path, \"/\")\n\tif len(pathArrays) < 2 {\n\t\treturn \"\"\n\t}\n\treturn pathArrays[1]\n}\n\nfunc newDB(pathItem string) (*gorm.DB, error) {\n\tdbFile := \"\"\n\tswitch {\n\tcase strings.HasPrefix(pathItem, \"/core/xpack\") || strings.HasPrefix(pathItem, \"/xpack\"):\n\t\tdbFile = path.Join(global.CONF.Base.InstallDir, \"1panel/db/xpack.db\")\n\tcase strings.HasPrefix(pathItem, \"/core\"):\n\t\tdbFile = path.Join(global.CONF.Base.InstallDir, \"1panel/db/core.db\")\n\tdefault:\n\t\tdbFile = path.Join(global.CONF.Base.InstallDir, \"1panel/db/agent.db\")\n\t}\n\n\tdb, _ := gorm.Open(sqlite.Open(dbFile), &gorm.Config{\n\t\tDisableForeignKeyConstraintWhenMigrating: true,\n\t})\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsqlDB.SetMaxOpenConns(4)\n\tsqlDB.SetMaxIdleConns(1)\n\tsqlDB.SetConnMaxIdleTime(15 * time.Minute)\n\tsqlDB.SetConnMaxLifetime(time.Hour)\n\treturn db, nil\n}\n\nfunc closeDB(db *gorm.DB) {\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn\n\t}\n\t_ = sqlDB.Close()\n}\n\nfunc resolveByDB(dbItem *gorm.DB, values map[string]interface{}, beforeFunctions []functionInfo) {\n\tfor _, funcs := range beforeFunctions {\n\t\tfor key, value := range values {\n\t\t\tif funcs.InputValue != key {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar names []string\n\t\t\tif funcs.IsList {\n\t\t\t\tsql := fmt.Sprintf(\"SELECT %s FROM %s where %s in (?);\", funcs.OutputColumn, funcs.DB, funcs.InputColumn)\n\t\t\t\t_ = dbItem.Raw(sql, value).Scan(&names)\n\t\t\t} else {\n\t\t\t\tsql := fmt.Sprintf(\"select %s from %s where %s = ?;\", funcs.OutputColumn, funcs.DB, funcs.InputColumn)\n\t\t\t\t_ = dbItem.Raw(sql, value).Scan(&names)\n\t\t\t}\n\t\t\tvalues[funcs.OutputValue] = strings.Join(names, \",\")\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc replaceStr(val string, rep ...string) string {\n\tfor _, item := range rep {\n\t\tval = strings.ReplaceAll(val, item, \"\")\n\t}\n\treturn val\n}\n\nfunc parseMultipart(formData []byte, contentType string) (map[string]interface{}, error) {\n\td, params, err := mime.ParseMediaType(contentType)\n\tif err != nil || d != \"multipart/form-data\" {\n\t\treturn nil, http.ErrNotMultipart\n\t}\n\tboundary, ok := params[\"boundary\"]\n\tif !ok {\n\t\treturn nil, http.ErrMissingBoundary\n\t}\n\treader := multipart.NewReader(bytes.NewReader(formData), boundary)\n\tret := make(map[string]interface{})\n\n\tf, err := reader.ReadForm(32 << 20)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor k, v := range f.Value {\n\t\tif len(v) > 0 {\n\t\t\tret[k] = v[0]\n\t\t}\n\t}\n\tfor k, v := range f.File {\n\t\tif len(v) > 0 {\n\t\t\tret[k] = v[0].Filename\n\t\t}\n\t}\n\treturn ret, nil\n}\n\nfunc mergeResolvedData(headerVal string, values map[string]interface{}) {\n\tif len(headerVal) == 0 {\n\t\treturn\n\t}\n\tdata, err := base64.RawURLEncoding.DecodeString(headerVal)\n\tif err != nil {\n\t\treturn\n\t}\n\tresolved := make(map[string]string)\n\tif err := json.Unmarshal(data, &resolved); err != nil {\n\t\treturn\n\t}\n\tfor key, value := range resolved {\n\t\tvalues[key] = value\n\t}\n}\n\nfunc hasAllResolvedData(values map[string]interface{}, beforeFunctions []functionInfo) bool {\n\tfor _, item := range beforeFunctions {\n\t\tif _, ok := values[item.OutputValue]; ok {\n\t\t\tcontinue\n\t\t}\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc willProxy(reqPath, currentNode string) bool {\n\tif strings.HasPrefix(reqPath, \"/1panel/swagger\") || !strings.HasPrefix(reqPath, \"/api/v2\") {\n\t\treturn false\n\t}\n\tif strings.HasPrefix(reqPath, \"/api/v2/core\") && !strings.HasPrefix(reqPath, \"/api/v2/core/xpack\") {\n\t\treturn false\n\t}\n\tif !strings.HasPrefix(reqPath, \"/api/v2/core\") && (currentNode == \"local\" || len(currentNode) == 0) {\n\t\treturn true\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "core/middleware/password_expired.go",
    "content": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nvar (\n\texpiredLoc     *time.Location\n\texpiredLocOnce sync.Once\n)\n\nfunc PasswordExpired() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tif strings.HasPrefix(c.Request.URL.Path, \"/api/v2/core/auth\") ||\n\t\t\tc.Request.URL.Path == \"/api/v2/core/settings/expired/handle\" ||\n\t\t\tc.Request.URL.Path == \"/api/v2/core/settings/search\" {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tsettingRepo := repo.NewISettingRepo()\n\t\texpirationDays, err := settingRepo.GetValueByKey(\"ExpirationDays\")\n\t\tif err != nil {\n\t\t\thelper.ErrorWithDetail(c, http.StatusInternalServerError, \"ErrPasswordExpired\", err)\n\t\t\treturn\n\t\t}\n\t\texpiredDays, _ := strconv.Atoi(expirationDays)\n\t\tif expiredDays == 0 {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\texpirationTime, err := settingRepo.GetValueByKey(\"ExpirationTime\")\n\t\tif err != nil {\n\t\t\thelper.ErrorWithDetail(c, http.StatusInternalServerError, \"ErrPasswordExpired\", err)\n\t\t\treturn\n\t\t}\n\t\texpiredTime, err := time.ParseInLocation(constant.DateTimeLayout, expirationTime, loadExpiredLocation())\n\t\tif err != nil {\n\t\t\thelper.ErrorWithDetail(c, 313, \"ErrPasswordExpired\", err)\n\t\t\treturn\n\t\t}\n\t\tif time.Now().After(expiredTime) {\n\t\t\thelper.ErrorWithDetail(c, 313, \"ErrPasswordExpired\", err)\n\t\t\treturn\n\t\t}\n\t\tc.Next()\n\t}\n}\n\nfunc loadExpiredLocation() *time.Location {\n\texpiredLocOnce.Do(func() {\n\t\tloc, err := time.LoadLocation(common.LoadTimeZoneByCmd())\n\t\tif err != nil {\n\t\t\texpiredLoc = time.Local\n\t\t\treturn\n\t\t}\n\t\texpiredLoc = loc\n\t})\n\tif expiredLoc == nil {\n\t\treturn time.Local\n\t}\n\treturn expiredLoc\n}\n"
  },
  {
    "path": "core/middleware/password_rsa.go",
    "content": "package middleware\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SetPasswordPublicKey() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tcookieKey, _ := c.Cookie(\"panel_public_key\")\n\t\tsettingRepo := repo.NewISettingRepo()\n\t\tkey, _ := settingRepo.GetValueByKey(\"PASSWORD_PUBLIC_KEY\")\n\t\tbase64Key := base64.StdEncoding.EncodeToString([]byte(key))\n\t\tif base64Key == cookieKey {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\t\tc.SetCookie(\"panel_public_key\", base64Key, 7*24*60*60, \"/\", \"\", false, false)\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "core/middleware/session.go",
    "content": "package middleware\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/api/v2/helper\"\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc SessionAuth() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tapiReq := c.GetBool(\"API_AUTH\")\n\t\tif strings.HasPrefix(c.Request.URL.Path, \"/api/v2/core/auth\") || apiReq {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tpsession, err := global.SESSION.Get(c)\n\t\tif err != nil {\n\t\t\terrItem := err.Error()\n\t\t\tif errItem == \"ErrSessionDataFormat\" || errItem == \"ErrSessionDataNotFound\" {\n\t\t\t\thelper.BadAuth(c, \"ErrNotLogin\", buserr.New(errItem))\n\t\t\t\treturn\n\t\t\t}\n\t\t\thelper.BadAuth(c, \"ErrNotLogin\", err)\n\t\t\treturn\n\t\t}\n\t\tsettingRepo := repo.NewISettingRepo()\n\t\tsessionTimeout, err := settingRepo.GetValueByKey(\"SessionTimeout\")\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"create operation record failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tlifeTime, _ := strconv.Atoi(sessionTimeout)\n\t\tssl, err := settingRepo.GetValueByKey(\"SSL\")\n\t\tif err != nil {\n\t\t\tglobal.LOG.Errorf(\"create operation record failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t\t_ = global.SESSION.Set(c, psession, ssl == constant.StatusEnable, lifeTime)\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "core/router/command.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/core/app/api/v2\"\n\t\"github.com/1Panel-dev/1Panel/core/middleware\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype CommandRouter struct{}\n\nfunc (s *CommandRouter) InitRouter(Router *gin.RouterGroup) {\n\tcommandRouter := Router.Group(\"commands\").\n\t\tUse(middleware.SessionAuth()).\n\t\tUse(middleware.PasswordExpired())\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tcommandRouter.POST(\"/list\", baseApi.ListCommand)\n\t\tcommandRouter.POST(\"\", baseApi.CreateCommand)\n\t\tcommandRouter.POST(\"/del\", baseApi.DeleteCommand)\n\t\tcommandRouter.POST(\"/search\", baseApi.SearchCommand)\n\t\tcommandRouter.POST(\"/tree\", baseApi.SearchCommandTree)\n\t\tcommandRouter.POST(\"/update\", baseApi.UpdateCommand)\n\t\tcommandRouter.POST(\"/export\", baseApi.ExportCommands)\n\t\tcommandRouter.POST(\"/upload\", baseApi.UploadCommandCsv)\n\t\tcommandRouter.POST(\"/import\", baseApi.ImportCommands)\n\t}\n}\n"
  },
  {
    "path": "core/router/common.go",
    "content": "package router\n\nfunc commonGroups() []CommonRouter {\n\treturn []CommonRouter{\n\t\t&BaseRouter{},\n\t\t&BackupRouter{},\n\t\t&LogRouter{},\n\t\t&SettingRouter{},\n\t\t&CommandRouter{},\n\t\t&GroupRouter{},\n\t\t&ScriptRouter{},\n\t}\n}\n"
  },
  {
    "path": "core/router/entry.go",
    "content": "//go:build !xpack\n\npackage router\n\nfunc RouterGroups() []CommonRouter {\n\treturn commonGroups()\n}\n\nvar RouterGroupApp = RouterGroups()\n"
  },
  {
    "path": "core/router/ro_backup.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/core/app/api/v2\"\n\t\"github.com/1Panel-dev/1Panel/core/middleware\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype BackupRouter struct{}\n\nfunc (s *BackupRouter) InitRouter(Router *gin.RouterGroup) {\n\tbackupRouter := Router.Group(\"backups\").\n\t\tUse(middleware.SessionAuth()).\n\t\tUse(middleware.PasswordExpired())\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tbackupRouter.GET(\"/client/:clientType\", baseApi.LoadBackupClientInfo)\n\t\tbackupRouter.POST(\"/refresh/token\", baseApi.RefreshToken)\n\t\tbackupRouter.POST(\"\", baseApi.CreateBackup)\n\t\tbackupRouter.POST(\"/del\", baseApi.DeleteBackup)\n\t\tbackupRouter.POST(\"/update\", baseApi.UpdateBackup)\n\t}\n}\n"
  },
  {
    "path": "core/router/ro_base.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/core/app/api/v2\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype BaseRouter struct{}\n\nfunc (s *BaseRouter) InitRouter(Router *gin.RouterGroup) {\n\tbaseRouter := Router.Group(\"auth\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tbaseRouter.GET(\"/captcha\", baseApi.Captcha)\n\t\tbaseRouter.POST(\"/passkey/begin\", baseApi.PasskeyBeginLogin)\n\t\tbaseRouter.POST(\"/passkey/finish\", baseApi.PasskeyFinishLogin)\n\t\tbaseRouter.POST(\"/mfalogin\", baseApi.MFALogin)\n\t\tbaseRouter.POST(\"/login\", baseApi.Login)\n\t\tbaseRouter.POST(\"/logout\", baseApi.LogOut)\n\t\tbaseRouter.GET(\"/setting\", baseApi.GetLoginSetting)\n\t\tbaseRouter.GET(\"/welcome\", baseApi.GetWelcomePage)\n\t}\n}\n"
  },
  {
    "path": "core/router/ro_group.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/core/app/api/v2\"\n\t\"github.com/1Panel-dev/1Panel/core/middleware\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype GroupRouter struct {\n}\n\nfunc (a *GroupRouter) InitRouter(Router *gin.RouterGroup) {\n\tgroupRouter := Router.Group(\"groups\").\n\t\tUse(middleware.SessionAuth()).\n\t\tUse(middleware.PasswordExpired())\n\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tgroupRouter.POST(\"\", baseApi.CreateGroup)\n\t\tgroupRouter.POST(\"/del\", baseApi.DeleteGroup)\n\t\tgroupRouter.POST(\"/update\", baseApi.UpdateGroup)\n\t\tgroupRouter.POST(\"/search\", baseApi.ListGroup)\n\t}\n}\n"
  },
  {
    "path": "core/router/ro_log.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/core/app/api/v2\"\n\t\"github.com/1Panel-dev/1Panel/core/middleware\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype LogRouter struct{}\n\nfunc (s *LogRouter) InitRouter(Router *gin.RouterGroup) {\n\toperationRouter := Router.Group(\"logs\").\n\t\tUse(middleware.SessionAuth()).\n\t\tUse(middleware.PasswordExpired())\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\toperationRouter.POST(\"/login\", baseApi.GetLoginLogs)\n\t\toperationRouter.POST(\"/operation\", baseApi.GetOperationLogs)\n\t\toperationRouter.POST(\"/clean\", baseApi.CleanLogs)\n\t}\n}\n"
  },
  {
    "path": "core/router/ro_router.go",
    "content": "package router\n\nimport \"github.com/gin-gonic/gin\"\n\ntype CommonRouter interface {\n\tInitRouter(Router *gin.RouterGroup)\n}\n"
  },
  {
    "path": "core/router/ro_script_library.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/core/app/api/v2\"\n\t\"github.com/1Panel-dev/1Panel/core/middleware\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype ScriptRouter struct{}\n\nfunc (s *ScriptRouter) InitRouter(Router *gin.RouterGroup) {\n\tscriptRouter := Router.Group(\"script\").\n\t\tUse(middleware.SessionAuth()).\n\t\tUse(middleware.PasswordExpired())\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\tscriptRouter.POST(\"\", baseApi.CreateScript)\n\t\tscriptRouter.POST(\"/search\", baseApi.SearchScript)\n\t\tscriptRouter.POST(\"/del\", baseApi.DeleteScript)\n\t\tscriptRouter.POST(\"/update\", baseApi.UpdateScript)\n\t\tscriptRouter.POST(\"/sync\", baseApi.SyncScript)\n\t\tscriptRouter.GET(\"/run\", baseApi.RunScript)\n\t}\n}\n"
  },
  {
    "path": "core/router/ro_setting.go",
    "content": "package router\n\nimport (\n\tv2 \"github.com/1Panel-dev/1Panel/core/app/api/v2\"\n\t\"github.com/1Panel-dev/1Panel/core/middleware\"\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype SettingRouter struct{}\n\nfunc (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {\n\trouter := Router.Group(\"settings\").\n\t\tUse(middleware.SessionAuth())\n\tsettingRouter := Router.Group(\"settings\").\n\t\tUse(middleware.SessionAuth()).\n\t\tUse(middleware.PasswordExpired())\n\n\tnoAuthRouter := Router.Group(\"settings\")\n\tbaseApi := v2.ApiGroupApp.BaseApi\n\t{\n\t\trouter.POST(\"/search\", baseApi.GetSettingInfo)\n\t\trouter.POST(\"/expired/handle\", baseApi.HandlePasswordExpired)\n\t\tsettingRouter.POST(\"/by\", baseApi.GetSettingByKey)\n\t\tsettingRouter.POST(\"/terminal/search\", baseApi.GetTerminalSettingInfo)\n\t\tsettingRouter.GET(\"/search/available\", baseApi.GetSystemAvailable)\n\t\tsettingRouter.POST(\"/update\", baseApi.UpdateSetting)\n\t\tsettingRouter.POST(\"/terminal/update\", baseApi.UpdateTerminalSetting)\n\t\tsettingRouter.GET(\"/interface\", baseApi.LoadInterfaceAddr)\n\t\tsettingRouter.POST(\"/menu/update\", baseApi.UpdateMenu)\n\t\tsettingRouter.POST(\"/menu/default\", baseApi.DefaultMenu)\n\t\tsettingRouter.POST(\"/proxy/update\", baseApi.UpdateProxy)\n\t\tsettingRouter.POST(\"/bind/update\", baseApi.UpdateBindInfo)\n\t\tsettingRouter.POST(\"/port/update\", baseApi.UpdatePort)\n\t\tsettingRouter.POST(\"/ssl/update\", baseApi.UpdateSSL)\n\t\tsettingRouter.GET(\"/ssl/info\", baseApi.LoadFromCert)\n\t\tsettingRouter.POST(\"/ssl/download\", baseApi.DownloadSSL)\n\t\tsettingRouter.POST(\"/password/update\", baseApi.UpdatePassword)\n\t\tsettingRouter.POST(\"/mfa\", baseApi.LoadMFA)\n\t\tsettingRouter.POST(\"/mfa/bind\", baseApi.MFABind)\n\t\tsettingRouter.POST(\"/passkey/register/begin\", baseApi.PasskeyRegisterBegin)\n\t\tsettingRouter.POST(\"/passkey/register/finish\", baseApi.PasskeyRegisterFinish)\n\t\tsettingRouter.GET(\"/passkey/list\", baseApi.PasskeyList)\n\t\tsettingRouter.DELETE(\"/passkey/:id\", baseApi.PasskeyDelete)\n\n\t\tsettingRouter.POST(\"/upgrade\", baseApi.Upgrade)\n\t\tsettingRouter.POST(\"/upgrade/notes\", baseApi.GetNotesByVersion)\n\t\tsettingRouter.GET(\"/upgrade/releases\", baseApi.LoadRelease)\n\t\tsettingRouter.GET(\"/upgrade\", baseApi.GetUpgradeInfo)\n\t\tsettingRouter.POST(\"/api/config/generate/key\", baseApi.GenerateApiKey)\n\t\tsettingRouter.POST(\"/api/config/update\", baseApi.UpdateApiConfig)\n\n\t\tnoAuthRouter.POST(\"/ssl/reload\", baseApi.ReloadSSL)\n\n\t\tsettingRouter.POST(\"/apps/store/update\", baseApi.UpdateAppstoreConfig)\n\t\tsettingRouter.GET(\"/apps/store/config\", baseApi.GetAppstoreConfig)\n\n\t\tsettingRouter.GET(\"/memo\", baseApi.GetMemo)\n\t\tsettingRouter.POST(\"/memo\", baseApi.UpdateMemo)\n\t}\n}\n"
  },
  {
    "path": "core/server/init.go",
    "content": "//go:build !xpack\n\npackage server\n\nfunc InitOthers() {\n}\n"
  },
  {
    "path": "core/server/server.go",
    "content": "package server\n\nimport (\n\t\"bufio\"\n\t\"crypto/tls\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/init/auth\"\n\t\"github.com/1Panel-dev/1Panel/core/init/db\"\n\t\"github.com/1Panel-dev/1Panel/core/init/geo\"\n\t\"github.com/1Panel-dev/1Panel/core/init/log\"\n\t\"github.com/1Panel-dev/1Panel/core/init/migration\"\n\t\"github.com/1Panel-dev/1Panel/core/init/proxy\"\n\t\"github.com/1Panel-dev/1Panel/core/init/run\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/soheilhy/cmux\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n\t\"github.com/1Panel-dev/1Panel/core/init/cron\"\n\t\"github.com/1Panel-dev/1Panel/core/init/hook\"\n\t\"github.com/1Panel-dev/1Panel/core/init/router\"\n\t\"github.com/1Panel-dev/1Panel/core/init/session\"\n\t\"github.com/1Panel-dev/1Panel/core/init/session/psession\"\n\t\"github.com/1Panel-dev/1Panel/core/init/validator\"\n\t\"github.com/1Panel-dev/1Panel/core/init/viper\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/re\"\n)\n\nfunc Start() {\n\tre.Init()\n\tviper.Init()\n\tlog.Init()\n\tdb.Init()\n\tmigration.Init()\n\ti18n.Init()\n\tvalidator.Init()\n\tgeo.Init()\n\tgob.Register(psession.SessionUser{})\n\tcron.Init()\n\tsession.Init()\n\thook.Init()\n\tInitOthers()\n\n\trun.Init()\n\tproxy.Init()\n\n\trootRouter := router.Routers()\n\n\tif global.CONF.Base.Mode != \"stable\" {\n\t\tgin.SetMode(gin.DebugMode)\n\t} else {\n\t\tgin.SetMode(gin.ReleaseMode)\n\t}\n\n\tglobal.IPTracker = auth.NewIPTracker()\n\n\ttcpItem := \"tcp4\"\n\tif global.CONF.Conn.Ipv6 == constant.StatusEnable {\n\t\ttcpItem = \"tcp\"\n\t\tglobal.CONF.Conn.BindAddress = fmt.Sprintf(\"[%s]\", global.CONF.Conn.BindAddress)\n\t}\n\tserver := &http.Server{\n\t\tAddr:              global.CONF.Conn.BindAddress + \":\" + global.CONF.Conn.Port,\n\t\tHandler:           rootRouter,\n\t\tReadHeaderTimeout: 5 * time.Second,\n\t\tReadTimeout:       600 * time.Second,\n\t\tWriteTimeout:      600 * time.Second,\n\t\tIdleTimeout:       240 * time.Second,\n\t}\n\tln, err := net.Listen(tcpItem, server.Addr)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttype tcpKeepAliveListener struct {\n\t\t*net.TCPListener\n\t}\n\tif global.CONF.Conn.SSL == constant.StatusEnable {\n\t\tconstant.CertStore.Store(loadCert())\n\n\t\tserver.TLSConfig = &tls.Config{\n\t\t\tGetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\t\treturn constant.CertStore.Load().(*tls.Certificate), nil\n\t\t\t},\n\t\t}\n\t\tglobal.LOG.Infof(\"listen at https://%s:%s [%s]\", global.CONF.Conn.BindAddress, global.CONF.Conn.Port, tcpItem)\n\n\t\tif err := server.ServeTLS(tcpKeepAliveListener{ln.(*net.TCPListener)}, \"\", \"\"); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\treturn\n\t} else if global.CONF.Conn.SSL == constant.StatusMux {\n\t\tconstant.CertStore.Store(loadCert())\n\n\t\tserver.TLSConfig = &tls.Config{\n\t\t\tNextProtos: []string{\"h2\", \"http/1.1\"},\n\t\t\tGetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\t\treturn constant.CertStore.Load().(*tls.Certificate), nil\n\t\t\t},\n\t\t}\n\n\t\tm := cmux.New(ln)\n\n\t\thttpsL := m.Match(cmux.TLS())\n\t\thttpL := m.Match(cmux.HTTP1Fast())\n\t\tanyL := m.Match(cmux.Any())\n\n\t\tgo func() {\n\t\t\tif err := server.Serve(tls.NewListener(httpsL, server.TLSConfig)); err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"HTTPS Serve Error: %v\", err)\n\t\t\t}\n\t\t}()\n\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tconn, err := httpL.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tgo handleMuxHttpConn(conn)\n\t\t\t}\n\t\t}()\n\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tconn, err := anyL.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconn.Close()\n\t\t\t}\n\t\t}()\n\n\t\tglobal.LOG.Infof(\"listen at mux (http/https)://%s:%s [%s]\", global.CONF.Conn.BindAddress, global.CONF.Conn.Port, tcpItem)\n\n\t\tif err := m.Serve(); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\treturn\n\t} else {\n\t\tglobal.LOG.Infof(\"listen at http://%s:%s [%s]\", global.CONF.Conn.BindAddress, global.CONF.Conn.Port, tcpItem)\n\t\tif err := server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc loadCert() *tls.Certificate {\n\tcertPath := path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.crt\")\n\tkeyPath := path.Join(global.CONF.Base.InstallDir, \"1panel/secret/server.key\")\n\tcertificate, err := os.ReadFile(certPath)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tkey, err := os.ReadFile(keyPath)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tcert, err := tls.X509KeyPair(certificate, key)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &cert\n}\n\nfunc handleMuxHttpConn(conn net.Conn) {\n\tdefer conn.Close()\n\n\treq, err := http.ReadRequest(bufio.NewReader(conn))\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif req.Host == \"\" {\n\t\treturn\n\t}\n\n\tua := req.Header.Get(\"User-Agent\")\n\tif ua == \"\" {\n\t\treturn\n\t}\n\n\tswitch req.Method {\n\tcase http.MethodGet, http.MethodHead, http.MethodPost:\n\tdefault:\n\t\treturn\n\t}\n\n\tif len(req.RequestURI) > 4096 {\n\t\treturn\n\t}\n\n\tif !strings.HasPrefix(req.URL.Path, \"/\") {\n\t\treturn\n\t}\n\n\ttarget := \"https://\" + req.Host + req.URL.RequestURI()\n\n\tresp := &http.Response{\n\t\tStatusCode: http.StatusTemporaryRedirect,\n\t\tProto:      \"HTTP/1.1\",\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tHeader:     make(http.Header),\n\t}\n\tresp.Header.Set(\"Location\", target)\n\tresp.Header.Set(\"Connection\", \"close\")\n\n\t_ = resp.Write(conn)\n\treturn\n}\n"
  },
  {
    "path": "core/utils/captcha/captcha.go",
    "content": "package captcha\n\nimport (\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1panel-dev/base64Captcha\"\n)\n\nvar store = base64Captcha.DefaultMemStore\n\nfunc VerifyCode(codeID string, code string) string {\n\tvv := store.Get(codeID, true)\n\tvv = strings.TrimSpace(vv)\n\tcode = strings.TrimSpace(code)\n\tif codeID == \"\" || code == \"\" {\n\t\treturn \"ErrCaptchaCode\"\n\t}\n\tif strings.EqualFold(vv, code) {\n\t\treturn \"\"\n\t}\n\treturn \"ErrCaptchaCode\"\n}\n\nfunc CreateCaptcha() (*dto.CaptchaResponse, error) {\n\tvar driverString base64Captcha.DriverString\n\tdriverString.Source = \"1234567890QWERTYUPLKJHGFDSAZXCVBNMqwertyupkjhgfdsazxcvbnm\"\n\tdriverString.Width = 120\n\tdriverString.Height = 50\n\tdriverString.NoiseCount = 0\n\tdriverString.Length = 4\n\tdriverString.Fonts = []string{\"RitaSmith.ttf\", \"actionj.ttf\", \"chromohv.ttf\"}\n\tdriver := driverString.ConvertFonts()\n\tc := base64Captcha.NewCaptcha(driver, store)\n\tid, b64s, _, err := c.Generate()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &dto.CaptchaResponse{\n\t\tCaptchaID: id,\n\t\tImagePath: b64s,\n\t}, nil\n}\n"
  },
  {
    "path": "core/utils/cloud_storage/refresh_token.go",
    "content": "package cloud_storage\n\nimport (\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/go-resty/resty/v2\"\n)\n\nfunc loadParamFromVars(key string, vars map[string]interface{}) string {\n\tif _, ok := vars[key]; !ok {\n\t\tif key != \"bucket\" && key != \"port\" && key != \"authMode\" && key != \"passPhrase\" {\n\t\t\tglobal.LOG.Errorf(\"load param %s from vars failed, err: not exist!\", key)\n\t\t}\n\t\treturn \"\"\n\t}\n\n\treturn fmt.Sprintf(\"%v\", vars[key])\n}\n\ntype aliTokenResp struct {\n\tRefreshToken string `json:\"refresh_token\"`\n\tAccessToken  string `json:\"access_token\"`\n}\n\nfunc RefreshALIToken(varMap map[string]interface{}) (string, error) {\n\trefresh_token := loadParamFromVars(\"refresh_token\", varMap)\n\tif len(refresh_token) == 0 {\n\t\treturn \"\", errors.New(\"no such refresh token find in db\")\n\t}\n\tclient := resty.New()\n\tclient.SetTLSClientConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\tdata := map[string]interface{}{\n\t\t\"grant_type\":    \"refresh_token\",\n\t\t\"refresh_token\": refresh_token,\n\t}\n\n\turl := \"https://api.aliyundrive.com/token/refresh\"\n\tresp, err := client.R().\n\t\tSetBody(data).\n\t\tPost(url)\n\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"load account token failed, err: %v\", err)\n\t}\n\tif resp.StatusCode() != 200 {\n\t\treturn \"\", fmt.Errorf(\"load account token failed, code: %v\", resp.StatusCode())\n\t}\n\tvar respItem aliTokenResp\n\tif err := json.Unmarshal(resp.Body(), &respItem); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn respItem.RefreshToken, nil\n}\n\nfunc RefreshToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) {\n\tdata := url.Values{}\n\tisCN := loadParamFromVars(\"isCN\", varMap)\n\tdata.Set(\"client_id\", loadParamFromVars(\"client_id\", varMap))\n\tdata.Set(\"client_secret\", loadParamFromVars(\"client_secret\", varMap))\n\tif grantType == \"refresh_token\" {\n\t\tdata.Set(\"grant_type\", \"refresh_token\")\n\t\tdata.Set(\"refresh_token\", loadParamFromVars(\"refresh_token\", varMap))\n\t} else {\n\t\tdata.Set(\"grant_type\", \"authorization_code\")\n\t\tdata.Set(\"code\", loadParamFromVars(\"code\", varMap))\n\t}\n\tdata.Set(\"redirect_uri\", loadParamFromVars(\"redirect_uri\", varMap))\n\tclient := &http.Client{}\n\tdefer client.CloseIdleConnections()\n\turl := \"https://login.microsoftonline.com/common/oauth2/v2.0/token\"\n\tif isCN == \"true\" {\n\t\turl = \"https://login.chinacloudapi.cn/common/oauth2/v2.0/token\"\n\t}\n\treq, err := http.NewRequest(\"POST\", url, strings.NewReader(data.Encode()))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"new http post client for access token failed, err: %v\", err)\n\t}\n\treq.Header.Add(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"request for access token failed, err: %v\", err)\n\t}\n\tdefer resp.Body.Close()\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"read data from response body failed, err: %v\", err)\n\t}\n\n\ttokenMap := map[string]interface{}{}\n\tif err := json.Unmarshal(respBody, &tokenMap); err != nil {\n\t\treturn \"\", fmt.Errorf(\"unmarshal data from response body failed, err: %v\", err)\n\t}\n\tif tokenType == \"accessToken\" {\n\t\taccessToken, ok := tokenMap[\"access_token\"].(string)\n\t\tif !ok {\n\t\t\treturn \"\", errors.New(\"no such access token in response\")\n\t\t}\n\t\ttokenMap = nil\n\t\treturn accessToken, nil\n\t}\n\trefreshToken, ok := tokenMap[\"refresh_token\"].(string)\n\tif !ok {\n\t\treturn \"\", errors.New(\"no such access token in response\")\n\t}\n\ttokenMap = nil\n\treturn refreshToken, nil\n}\n"
  },
  {
    "path": "core/utils/cmd/cmd.go",
    "content": "package cmd\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\nfunc SudoHandleCmd() string {\n\tcmd := exec.Command(\"sudo\", \"-n\", \"ls\")\n\tif err := cmd.Run(); err == nil {\n\t\treturn \"sudo \"\n\t}\n\treturn \"\"\n}\n\nfunc Which(name string) bool {\n\tstdout, err := RunDefaultWithStdoutBashCf(\"which %s\", name)\n\tif err != nil || (len(strings.ReplaceAll(stdout, \"\\n\", \"\")) == 0) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc ExecWithStreamOutput(command string, outputCallback func(string)) error {\n\tcmd := exec.Command(\"bash\", \"-c\", command)\n\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get stdout: %w\", err)\n\t}\n\n\tstderr, err := cmd.StderrPipe()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get stderr: %w\", err)\n\t}\n\n\tif err := cmd.Start(); err != nil {\n\t\treturn fmt.Errorf(\"failed to start command: %w\", err)\n\t}\n\n\tgo streamReader(stdout, outputCallback)\n\tgo streamReader(stderr, outputCallback)\n\n\tif err := cmd.Wait(); err != nil {\n\t\treturn fmt.Errorf(\"command finished with error: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc streamReader(reader io.ReadCloser, callback func(string)) {\n\tscanner := bufio.NewScanner(reader)\n\tfor scanner.Scan() {\n\t\tcallback(scanner.Text())\n\t}\n}\n"
  },
  {
    "path": "core/utils/cmd/cmdx.go",
    "content": "package cmd\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/task\"\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n)\n\ntype CommandHelper struct {\n\tworkDir      string\n\toutputFile   string\n\tscriptPath   string\n\ttimeout      time.Duration\n\ttaskItem     *task.Task\n\tlogger       *log.Logger\n\tIgnoreExist1 bool\n}\n\ntype Option func(*CommandHelper)\n\nfunc NewCommandMgr(opts ...Option) *CommandHelper {\n\ts := &CommandHelper{}\n\tfor _, opt := range opts {\n\t\topt(s)\n\t}\n\treturn s\n}\n\nfunc RunDefaultBashC(command string) error {\n\tmgr := NewCommandMgr()\n\treturn mgr.RunBashC(command)\n}\nfunc RunDefaultBashCf(command string, arg ...interface{}) error {\n\tmgr := NewCommandMgr()\n\treturn mgr.RunBashCf(command, arg...)\n}\nfunc RunDefaultWithStdoutBashC(command string) (string, error) {\n\tmgr := NewCommandMgr(WithTimeout(20 * time.Second))\n\treturn mgr.RunWithStdoutBashC(command)\n}\nfunc RunDefaultWithStdoutBashCf(command string, arg ...interface{}) (string, error) {\n\tmgr := NewCommandMgr(WithTimeout(20 * time.Second))\n\treturn mgr.RunWithStdoutBashCf(command, arg...)\n}\n\nfunc (c *CommandHelper) Run(name string, arg ...string) error {\n\t_, err := c.run(name, arg...)\n\treturn err\n}\nfunc (c *CommandHelper) RunBashCWithArgs(arg ...string) error {\n\targ = append([]string{\"-c\"}, arg...)\n\t_, err := c.run(\"bash\", arg...)\n\treturn err\n}\nfunc (c *CommandHelper) RunBashC(command string) error {\n\t_, err := c.run(\"bash\", \"-c\", command)\n\treturn err\n}\nfunc (c *CommandHelper) RunBashCf(command string, arg ...interface{}) error {\n\t_, err := c.run(\"bash\", \"-c\", fmt.Sprintf(command, arg...))\n\treturn err\n}\n\nfunc (c *CommandHelper) RunWithStdout(name string, arg ...string) (string, error) {\n\treturn c.run(name, arg...)\n}\nfunc (c *CommandHelper) RunWithStdoutBashC(command string) (string, error) {\n\treturn c.run(\"bash\", \"-c\", command)\n}\nfunc (c *CommandHelper) RunWithStdoutBashCf(command string, arg ...interface{}) (string, error) {\n\treturn c.run(\"bash\", \"-c\", fmt.Sprintf(command, arg...))\n}\n\nfunc (c *CommandHelper) run(name string, arg ...string) (string, error) {\n\tvar cmd *exec.Cmd\n\tvar ctx context.Context\n\tvar cancel context.CancelFunc\n\n\tif c.timeout != 0 {\n\t\tctx, cancel = context.WithTimeout(context.Background(), c.timeout)\n\t\tdefer cancel()\n\t\tcmd = exec.CommandContext(ctx, name, arg...)\n\t} else {\n\t\tcmd = exec.Command(name, arg...)\n\t}\n\n\tcustomWriter := &CustomWriter{taskItem: c.taskItem}\n\tvar stdout, stderr bytes.Buffer\n\tif c.taskItem != nil {\n\t\tcmd.Stdout = customWriter\n\t\tcmd.Stderr = customWriter\n\t} else if c.logger != nil {\n\t\tcmd.Stdout = c.logger.Writer()\n\t\tcmd.Stderr = c.logger.Writer()\n\t} else if len(c.outputFile) != 0 {\n\t\tfile, err := os.OpenFile(c.outputFile, os.O_WRONLY|os.O_CREATE, constant.FilePerm)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tdefer file.Close()\n\t\tcmd.Stdout = file\n\t\tcmd.Stderr = file\n\t} else if len(c.scriptPath) != 0 {\n\t\tcmd.Stdout = &stdout\n\t\tcmd.Stderr = &stderr\n\t\tcmd = exec.Command(\"bash\", c.scriptPath)\n\t} else {\n\t\tcmd.Stdout = &stdout\n\t\tcmd.Stderr = &stderr\n\t}\n\tenv := os.Environ()\n\tcmd.Env = env\n\tif len(c.workDir) != 0 {\n\t\tcmd.Dir = c.workDir\n\t}\n\n\tif c.timeout != 0 {\n\t\terr := cmd.Run()\n\t\tif c.taskItem != nil {\n\t\t\tcustomWriter.Flush()\n\t\t}\n\t\tif ctx != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\t\treturn \"\", buserr.New(\"ErrCmdTimeout\")\n\t\t}\n\t\tif err != nil {\n\t\t\treturn handleErr(stdout, stderr, c.IgnoreExist1, err)\n\t\t}\n\t\treturn stdout.String(), nil\n\t}\n\n\terr := cmd.Run()\n\tif err != nil {\n\t\treturn handleErr(stdout, stderr, c.IgnoreExist1, err)\n\t}\n\treturn stdout.String(), nil\n}\n\nfunc WithOutputFile(outputFile string) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.outputFile = outputFile\n\t}\n}\nfunc WithTimeout(timeout time.Duration) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.timeout = timeout\n\t}\n}\nfunc WithLogger(logger *log.Logger) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.logger = logger\n\t}\n}\nfunc WithTask(taskItem task.Task) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.taskItem = &taskItem\n\t}\n}\nfunc WithWorkDir(workDir string) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.workDir = workDir\n\t}\n}\nfunc WithScriptPath(scriptPath string) Option {\n\treturn func(s *CommandHelper) {\n\t\ts.scriptPath = scriptPath\n\t}\n}\nfunc WithIgnoreExist1() Option {\n\treturn func(s *CommandHelper) {\n\t\ts.IgnoreExist1 = true\n\t}\n}\n\ntype CustomWriter struct {\n\ttaskItem *task.Task\n\tbuffer   bytes.Buffer\n}\n\nfunc (cw *CustomWriter) Write(p []byte) (n int, err error) {\n\tcw.buffer.Write(p)\n\tlines := strings.Split(cw.buffer.String(), \"\\n\")\n\n\tfor i := 0; i < len(lines)-1; i++ {\n\t\tcw.taskItem.Log(lines[i])\n\t}\n\tcw.buffer.Reset()\n\tcw.buffer.WriteString(lines[len(lines)-1])\n\n\treturn len(p), nil\n}\nfunc (cw *CustomWriter) Flush() {\n\tif cw.buffer.Len() > 0 {\n\t\tcw.taskItem.Log(cw.buffer.String())\n\t\tcw.buffer.Reset()\n\t}\n}\n\nfunc handleErr(stdout, stderr bytes.Buffer, ignoreExist1 bool, err error) (string, error) {\n\tvar exitError *exec.ExitError\n\tif ignoreExist1 && errors.As(err, &exitError) {\n\t\tif status, ok := exitError.Sys().(syscall.WaitStatus); ok {\n\t\t\tif status.ExitStatus() == 1 {\n\t\t\t\treturn \"\", nil\n\t\t\t}\n\t\t}\n\t}\n\terrMsg := \"\"\n\tif len(stderr.String()) != 0 {\n\t\terrMsg = fmt.Sprintf(\"stderr: %s\", stderr.String())\n\t}\n\tif len(stdout.String()) != 0 {\n\t\tif len(errMsg) != 0 {\n\t\t\terrMsg = fmt.Sprintf(\"%s; stdout: %s\", errMsg, stdout.String())\n\t\t} else {\n\t\t\terrMsg = fmt.Sprintf(\"stdout: %s\", stdout.String())\n\t\t}\n\t}\n\treturn errMsg, err\n}\n"
  },
  {
    "path": "core/utils/common/common.go",
    "content": "package common\n\nimport (\n\t\"crypto/md5\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"net\"\n\t\"os\"\n\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n)\n\nvar letters = []rune(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\")\n\nfunc RandStr(n int) string {\n\tb := make([]rune, n)\n\tmax := big.NewInt(int64(len(letters)))\n\tfor i := range b {\n\t\tnum, err := rand.Int(rand.Reader, max)\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\tb[i] = letters[num.Int64()]\n\t}\n\treturn string(b)\n}\n\nfunc RandStrAndNum(n int) string {\n\tconst charset = \"abcdefghijklmnopqrstuvwxyz0123456789\"\n\tb := make([]byte, n)\n\tmax := big.NewInt(int64(len(charset)))\n\tfor i := range b {\n\t\tnum, err := rand.Int(rand.Reader, max)\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\t\tb[i] = charset[num.Int64()]\n\t}\n\treturn string(b)\n}\n\nfunc Md5(val string) string {\n\thash := md5.New()\n\thash.Write([]byte(val))\n\treturn hex.EncodeToString(hash.Sum(nil))\n}\n\nfunc LoadTimeZoneByCmd() string {\n\tloc := time.Now().Location().String()\n\tif _, err := time.LoadLocation(loc); err != nil {\n\t\tloc = \"Asia/Shanghai\"\n\t}\n\tstd, err := cmd.RunDefaultWithStdoutBashC(\"timedatectl | grep 'Time zone'\")\n\tif err != nil {\n\t\treturn loc\n\t}\n\tfields := strings.Fields(string(std))\n\tif len(fields) != 5 {\n\t\treturn loc\n\t}\n\tif _, err := time.LoadLocation(fields[2]); err != nil {\n\t\treturn loc\n\t}\n\treturn fields[2]\n}\n\nfunc ScanPort(port int) bool {\n\tln, err := net.Listen(\"tcp\", \":\"+strconv.Itoa(port))\n\tif err != nil {\n\t\treturn true\n\t}\n\tdefer func() { _ = ln.Close() }()\n\treturn false\n}\n\nfunc CheckPort(host string, port string, timeout time.Duration) bool {\n\ttarget := net.JoinHostPort(host, port)\n\tconn, err := net.DialTimeout(\"tcp\", target, timeout)\n\tif err != nil {\n\t\tif netErr, ok := err.(net.Error); ok && netErr.Timeout() {\n\t\t\treturn false\n\t\t}\n\t\treturn strings.Contains(err.Error(), \"connection refused\")\n\t}\n\tdefer func() { _ = conn.Close() }()\n\treturn true\n}\n\nfunc ComparePanelVersion(version1, version2 string) bool {\n\tif version1 == version2 {\n\t\treturn false\n\t}\n\tversion1s := SplitStr(version1, \".\", \"-\")\n\tversion2s := SplitStr(version2, \".\", \"-\")\n\n\tif len(version2s) > len(version1s) {\n\t\tfor i := 0; i < len(version2s)-len(version1s); i++ {\n\t\t\tversion1s = append(version1s, \"0\")\n\t\t}\n\t}\n\tif len(version1s) > len(version2s) {\n\t\tfor i := 0; i < len(version1s)-len(version2s); i++ {\n\t\t\tversion2s = append(version2s, \"0\")\n\t\t}\n\t}\n\n\tn := min(len(version1s), len(version2s))\n\tfor i := 0; i < n; i++ {\n\t\tif version1s[i] == version2s[i] {\n\t\t\tcontinue\n\t\t} else {\n\t\t\tv1, err1 := strconv.Atoi(version1s[i])\n\t\t\tif err1 != nil {\n\t\t\t\treturn version1s[i] > version2s[i]\n\t\t\t}\n\t\t\tv2, err2 := strconv.Atoi(version2s[i])\n\t\t\tif err2 != nil {\n\t\t\t\treturn version1s[i] > version2s[i]\n\t\t\t}\n\t\t\treturn v1 > v2\n\t\t}\n\t}\n\treturn true\n}\n\nfunc SplitStr(str string, spi ...string) []string {\n\tlists := []string{str}\n\tvar results []string\n\tfor _, s := range spi {\n\t\tresults = []string{}\n\t\tfor _, list := range lists {\n\t\t\tresults = append(results, strings.Split(list, s)...)\n\t\t}\n\t\tlists = results\n\t}\n\treturn results\n}\n\nfunc LoadArch() (string, error) {\n\tstd, err := cmd.RunDefaultWithStdoutBashC(\"uname -a\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"std: %s, err: %s\", std, err.Error())\n\t}\n\treturn LoadArchWithStdout(std)\n}\nfunc LoadArchWithStdout(std string) (string, error) {\n\tif strings.Contains(std, \"x86_64\") {\n\t\treturn \"amd64\", nil\n\t}\n\tif strings.Contains(std, \"arm64\") || strings.Contains(std, \"aarch64\") {\n\t\treturn \"arm64\", nil\n\t}\n\tif strings.Contains(std, \"armv7l\") {\n\t\treturn \"armv7\", nil\n\t}\n\tif strings.Contains(std, \"ppc64le\") {\n\t\treturn \"ppc64le\", nil\n\t}\n\tif strings.Contains(std, \"s390x\") {\n\t\treturn \"s390x\", nil\n\t}\n\tif strings.Contains(std, \"riscv64\") {\n\t\treturn \"riscv64\", nil\n\t}\n\treturn \"\", fmt.Errorf(\"unsupported such arch: %s\", std)\n}\n\nfunc Clean(str []byte) {\n\tfor i := 0; i < len(str); i++ {\n\t\tstr[i] = 0\n\t}\n}\n\nfunc CreateDirWhenNotExist(isDir bool, pathItem string) (string, error) {\n\tcheckPath := pathItem\n\tif !isDir {\n\t\tcheckPath = path.Dir(pathItem)\n\t}\n\tif _, err := os.Stat(checkPath); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(checkPath, os.ModePerm); err != nil {\n\t\t\tglobal.LOG.Errorf(\"mkdir %s failed, err: %v\", checkPath, err)\n\t\t\treturn pathItem, err\n\t\t}\n\t}\n\treturn pathItem, nil\n}\n\nfunc GetLang(c *gin.Context) string {\n\tlang := c.GetHeader(\"Accept-Language\")\n\tif lang == \"\" {\n\t\tlang = \"en\"\n\t}\n\treturn lang\n}\n\nfunc CheckIpInCidr(cidr, checkIP string) bool {\n\tip, ipNet, err := net.ParseCIDR(cidr)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"parse CIDR %s failed, err: %v\", cidr, err)\n\t\treturn false\n\t}\n\tfor ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); incIP(ip) {\n\t\tif ip.String() == checkIP {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc incIP(ip net.IP) {\n\tfor j := len(ip) - 1; j >= 0; j-- {\n\t\tip[j]++\n\t\tif ip[j] > 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc HandleIPList(content string) ([]string, error) {\n\tipList := strings.Split(content, \"\\n\")\n\tvar res []string\n\tfor _, ip := range ipList {\n\t\tif ip == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif net.ParseIP(ip) != nil {\n\t\t\tres = append(res, ip)\n\t\t\tcontinue\n\t\t}\n\t\tif _, _, err := net.ParseCIDR(ip); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres = append(res, ip)\n\t}\n\treturn res, nil\n}\n\nfunc LoadParams(param string) string {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2\", param)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tinfo := strings.ReplaceAll(stdout, \"\\n\", \"\")\n\tif len(info) == 0 || info == `\"\"` {\n\t\tpanic(fmt.Sprintf(\"error `%s` find in /usr/local/bin/1pctl\", param))\n\t}\n\treturn info\n}\nfunc LoadParamsWithoutPanic(param string) string {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2\", param)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strings.ReplaceAll(stdout, \"\\n\", \"\")\n}\n\nfunc GetRealClientIP(c *gin.Context) string {\n\taddr := c.Request.RemoteAddr\n\tif ip, _, err := net.SplitHostPort(addr); err == nil {\n\t\treturn ip\n\t}\n\treturn addr\n}\n\nfunc IsPrivateIP(ipStr string) bool {\n\tip := net.ParseIP(ipStr)\n\tif ip == nil {\n\t\treturn false\n\t}\n\treturn ip.IsPrivate() || ip.IsLoopback()\n}\n"
  },
  {
    "path": "core/utils/common/sqlite.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/glebarez/sqlite\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n)\n\nfunc LoadDBConnByPath(fullPath, dbName string) *gorm.DB {\n\tif _, err := CreateDirWhenNotExist(true, path.Dir(fullPath)); err != nil {\n\t\tpanic(fmt.Errorf(\"init db dir failed, err: %v\", err))\n\t}\n\tif _, err := os.Stat(fullPath); err != nil {\n\t\tf, err := os.Create(fullPath)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(\"init %s db file failed, err: %v\", dbName, err))\n\t\t}\n\t\t_ = f.Close()\n\t}\n\n\tdb, err := GetDBWithPath(fullPath)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn db\n}\n\nfunc CloseDB(db *gorm.DB) {\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn\n\t}\n\t_ = sqlDB.Close()\n}\n\nfunc GetDBWithPath(dbPath string) (*gorm.DB, error) {\n\tdb, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{\n\t\tDisableForeignKeyConstraintWhenMigrating: true,\n\t\tLogger:                                   newLogger(),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsqlDB.SetMaxOpenConns(4)\n\tsqlDB.SetMaxIdleConns(1)\n\tsqlDB.SetConnMaxLifetime(0)\n\tsqlDB.SetConnMaxIdleTime(0)\n\n\tif err := db.Exec(\"PRAGMA journal_mode = WAL;\").Error; err != nil {\n\t\treturn nil, err\n\t}\n\tif err := db.Exec(\"PRAGMA synchronous = NORMAL;\").Error; err != nil {\n\t\treturn nil, err\n\t}\n\tif err := db.Exec(\"PRAGMA busy_timeout = 5000;\").Error; err != nil {\n\t\treturn nil, err\n\t}\n\tif err := db.Exec(\"PRAGMA temp_store = MEMORY;\").Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn db, nil\n}\n\nfunc newLogger() logger.Interface {\n\treturn logger.New(\n\t\tlog.New(os.Stdout, \"\\r\\n\", log.LstdFlags),\n\t\tlogger.Config{\n\t\t\tSlowThreshold:             time.Second,\n\t\t\tLogLevel:                  logger.Silent,\n\t\t\tIgnoreRecordNotFoundError: true,\n\t\t\tColorful:                  false,\n\t\t},\n\t)\n}\n"
  },
  {
    "path": "core/utils/controller/controller.go",
    "content": "package controller\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/controller/manager\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/ssh\"\n)\n\ntype Controller interface {\n\tName() string\n\tIsActive(serviceName string) (bool, error)\n\tIsEnable(serviceName string) (bool, error)\n\tIsExist(serviceName string) (bool, error)\n\tStatus(serviceName string) (string, error)\n\n\tOperate(operate, serviceName string) error\n\n\tReload() error\n}\n\nfunc New() (Controller, error) {\n\tmanagerOptions := []string{\"systemctl\", \"rc-service\", \"service\"}\n\tfor _, item := range managerOptions {\n\t\tif _, err := exec.LookPath(item); err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tswitch item {\n\t\tcase \"systemctl\":\n\t\t\treturn manager.NewSystemd(), nil\n\t\tcase \"rc-service\":\n\t\t\treturn manager.NewOpenrc(), nil\n\t\tcase \"service\":\n\t\t\treturn manager.NewSysvinit(), nil\n\t\t}\n\t}\n\treturn nil, errors.New(\"not support such manager initializatio\")\n}\n\nfunc NewWithClient(client *ssh.SSHClient) (Controller, error) {\n\tmanagerOptions := []string{\"systemctl\", \"rc-service\", \"service\"}\n\tfor _, item := range managerOptions {\n\t\tstdout, err := client.Runf(\"which %s\", item)\n\t\tif err != nil || (len(strings.ReplaceAll(stdout, \"\\n\", \"\")) == 0) {\n\t\t\tcontinue\n\t\t}\n\t\tswitch item {\n\t\tcase \"systemctl\":\n\t\t\tmgr := manager.NewSystemd()\n\t\t\tmgr.Client = client\n\t\t\treturn mgr, nil\n\t\tcase \"rc-service\":\n\t\t\tmgr := manager.NewOpenrc()\n\t\t\tmgr.Client = client\n\t\t\treturn mgr, nil\n\t\tcase \"service\":\n\t\t\tmgr := manager.NewSysvinit()\n\t\t\tmgr.Client = client\n\t\t\treturn mgr, nil\n\t\t}\n\t}\n\treturn nil, errors.New(\"not support such manager initializatio\")\n}\n\nfunc Handle(operate, serviceName string) error {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn client.Operate(operate, service)\n}\nfunc HandleStart(serviceName string) error {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn Handle(\"start\", service)\n}\nfunc HandleStop(serviceName string) error {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn Handle(\"stop\", service)\n}\nfunc HandleRestart(serviceName string) error {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn Handle(\"restart\", service)\n}\n\nfunc CheckExist(serviceName string) (bool, error) {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tb, er := client.IsExist(service)\n\treturn b, er\n}\nfunc CheckActive(serviceName string) (bool, error) {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn client.IsActive(service)\n}\nfunc CheckEnable(serviceName string) (bool, error) {\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn client.IsEnable(service)\n}\n\nfunc Reload() error {\n\tclient, err := New()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn client.Reload()\n}\n\nfunc RestartPanel(core, agent, reload bool) {\n\tclient, err := New()\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"load client for controller failed, err: %v\", err)\n\t\treturn\n\t}\n\tif reload {\n\t\tif err := client.Reload(); err != nil {\n\t\t\tglobal.LOG.Errorf(\"restart 1panel service failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n\tif agent {\n\t\tif err := client.Operate(\"restart\", \"1panel-agent\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"restart 1panel agent service failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n\tif core {\n\t\tif err := client.Operate(\"restart\", \"1panel-core\"); err != nil {\n\t\t\tglobal.LOG.Errorf(\"restart 1panel core service failed, err: %v\", err)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc LoadServiceName(keyword string) (string, error) {\n\tclient, err := New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tprocessedName := loadProcessedName(client.Name(), keyword)\n\texist, _ := client.IsExist(processedName)\n\tif exist {\n\t\treturn processedName, nil\n\t}\n\talistName := loadFromPredefined(client, keyword)\n\tif len(alistName) != 0 {\n\t\treturn alistName, nil\n\t}\n\treturn \"\", fmt.Errorf(\"find such service for %s failed\", keyword)\n}\n\nfunc loadProcessedName(mgr, keyword string) string {\n\tkeyword = strings.ToLower(keyword)\n\tif strings.HasSuffix(keyword, \".service.socket\") {\n\t\tkeyword = strings.TrimSuffix(keyword, \".service.socket\") + \".socket\"\n\t}\n\tif mgr != \"systemd\" {\n\t\tkeyword = strings.TrimSuffix(keyword, \".service\")\n\t\treturn keyword\n\t}\n\tif !strings.HasSuffix(keyword, \".service\") && !strings.HasSuffix(keyword, \".socket\") {\n\t\tkeyword += \".service\"\n\t}\n\treturn keyword\n}\n\nfunc loadFromPredefined(mgr Controller, keyword string) string {\n\tpredefinedMap := map[string][]string{\n\t\t\"clam\":         {\"clamav-daemon.service\", \"clamd@scan.service\", \"clamd\"},\n\t\t\"freshclam\":    {\"clamav-freshclam.service\", \"freshclam.service\"},\n\t\t\"fail2ban\":     {\"fail2ban.service\", \"fail2ban\"},\n\t\t\"supervisor\":   {\"supervisord.service\", \"supervisor.service\", \"supervisord\", \"supervisor\"},\n\t\t\"ssh\":          {\"sshd.service\", \"ssh.service\", \"sshd\", \"ssh\"},\n\t\t\"1panel-core\":  {\"1panel-core.service\"},\n\t\t\"1panel-agent\": {\"1panel-agent.service\"},\n\t\t\"docker\":       {\"docker.service\", \"dockerd\"},\n\t}\n\tif val, ok := predefinedMap[keyword]; ok {\n\t\tfor _, item := range val {\n\t\t\tif exist, _ := mgr.IsExist(item); exist {\n\t\t\t\treturn item\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// GetServicePath returns the configuration file path for the specified service.\n// If serviceName is empty, it returns the default directory path based on the system's service manager.\n// For non-empty serviceName, it retrieves the exact service file path.\n// Parameters:\n//   - serviceName: Name of the service. If empty, returns the default directory.\n//\n// Returns:\n//   - string: The service configuration file path.\n//   - error: Error if the service manager is unsupported or command execution fails.\nfunc GetServicePath(serviceName string) (string, error) {\n\tif serviceName == \"\" {\n\t\tclient, err := New()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tswitch client.Name() {\n\t\tcase \"systemd\":\n\t\t\treturn \"/etc/systemd/system/\", nil\n\t\tcase \"openrc\", \"sysvinit\":\n\t\t\treturn \"/etc/init.d/\", nil\n\t\tdefault:\n\t\t\treturn \"\", fmt.Errorf(\"unsupported manager: %s\", client.Name())\n\t\t}\n\t}\n\tservice, err := LoadServiceName(serviceName)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tclient, err := New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tswitch client.Name() {\n\tcase \"systemd\":\n\t\tstdout, err := exec.Command(\"systemctl\", \"show\", \"-p\", \"FragmentPath\", service).Output()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tparts := strings.SplitN(string(stdout), \"=\", 2)\n\t\tif len(parts) != 2 {\n\t\t\treturn \"\", fmt.Errorf(\"unexpected output: %s\", string(stdout))\n\t\t}\n\t\treturn strings.TrimSpace(parts[1]), nil\n\tcase \"openrc\", \"sysvinit\":\n\t\treturn fmt.Sprintf(\"/etc/init.d/%s\", service), nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported manager: %s\", client.Name())\n\t}\n}\n\nfunc SelectInitScript(keyword string) (string, error) {\n\tclient, err := New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tswitch client.Name() {\n\tcase \"systemd\":\n\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".service\"\n\tcase \"openrc\":\n\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".openrc\"\n\tcase \"sysvinit\":\n\t\tif _, err := os.Stat(\"/etc/rc.common\"); err == nil {\n\t\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".prod\"\n\t\t} else {\n\t\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".init\"\n\t\t}\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported manager: %s\", client.Name())\n\t}\n\treturn keyword, nil\n}\n\nfunc GetScriptName(keyword string) (string, error) {\n\tclient, err := New()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tswitch client.Name() {\n\tcase \"systemd\":\n\t\tkeyword = strings.TrimSuffix(keyword, \".service\") + \".service\"\n\tcase \"openrc\", \"sysvinit\":\n\t\tlastDotIdx := strings.LastIndex(keyword, \".\")\n\t\tif lastDotIdx != -1 {\n\t\t\tkeyword = keyword[:lastDotIdx]\n\t\t}\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unsupported manager: %s\", client.Name())\n\t}\n\treturn keyword, nil\n}\n"
  },
  {
    "path": "core/utils/controller/manager/common.go",
    "content": "package manager\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/ssh\"\n)\n\nfunc handlerErr(out string, err error) error {\n\tif err != nil {\n\t\tif out != \"\" {\n\t\t\treturn errors.New(out)\n\t\t}\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc run(client *ssh.SSHClient, name string, args ...string) (string, error) {\n\tif client == nil {\n\t\treturn cmd.NewCommandMgr(cmd.WithTimeout(10*time.Second)).RunWithStdoutBashCf(\"LANGUAGE=en_US:en %s %s\", name, strings.Join(args, \" \"))\n\t}\n\treturn client.Runf(\"LANGUAGE=en_US:en %s %s\", name, strings.Join(args, \" \"))\n}\n"
  },
  {
    "path": "core/utils/controller/manager/openrc.go",
    "content": "package manager\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/ssh\"\n)\n\ntype Openrc struct {\n\ttoolCmd string\n\tClient  *ssh.SSHClient\n}\n\nfunc NewOpenrc() *Openrc {\n\treturn &Openrc{toolCmd: \"rc-service\"}\n}\n\nfunc (s *Openrc) Name() string {\n\treturn \"openrc\"\n}\nfunc (s *Openrc) IsActive(serviceName string) (bool, error) {\n\tout, err := cmd.RunDefaultWithStdoutBashCf(\"if service %s status >/dev/null 2>&1; then echo 'active'; else echo 'inactive'; fi\", serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn out == \"active\\n\", nil\n}\nfunc (s *Openrc) IsEnable(serviceName string) (bool, error) {\n\tout, err := cmd.RunDefaultWithStdoutBashCf(\"if ls /etc/rc*.d/S*%s >/dev/null 2>&1; then echo 'enabled'; else echo 'disabled'; fi\", serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn out == \"enabled\\n\", nil\n}\nfunc (s *Openrc) IsExist(serviceName string) (bool, error) {\n\tif _, err := os.Stat(filepath.Join(\"/etc/init.d\", serviceName)); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn false, nil\n\t\t}\n\t\treturn false, fmt.Errorf(\"stat /etc/init.d/%s failed: %w\", serviceName, err)\n\t}\n\treturn true, nil\n}\nfunc (s *Openrc) Status(serviceName string) (string, error) {\n\treturn run(s.Client, s.toolCmd, serviceName, \"status\")\n}\n\nfunc (s *Openrc) Operate(operate, serviceName string) error {\n\tswitch operate {\n\tcase \"enable\":\n\t\treturn handlerErr(run(s.Client, \"rc-update\", \"add\", serviceName, \"default\"))\n\tcase \"disable\":\n\t\treturn handlerErr(run(s.Client, \"rc-update\", \"del\", serviceName, \"default\"))\n\tdefault:\n\t\treturn handlerErr(run(s.Client, s.toolCmd, serviceName, operate))\n\t}\n}\n\nfunc (s *Openrc) Reload() error {\n\treturn nil\n}\n"
  },
  {
    "path": "core/utils/controller/manager/snap.go",
    "content": "package manager\n\nimport (\n\t\"strings\"\n)\n\ntype Snap struct{ toolCmd string }\n\nfunc NewSnap() *Snap {\n\treturn &Snap{toolCmd: \"snap\"}\n}\n\nfunc (s *Snap) IsExist(serviceName string) bool {\n\tout, err := run(nil, s.toolCmd, \"services\")\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn strings.Contains(out, serviceName)\n}\n\nfunc (s *Snap) IsActive(serviceName string) bool {\n\tout, err := run(nil, s.toolCmd, \"services\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tlines := strings.Split(out, \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.Contains(line, serviceName) && strings.Contains(line, \"active\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (s *Snap) IsEnable(serviceName string) bool {\n\tout, err := run(nil, s.toolCmd, \"services\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tlines := strings.Split(out, \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.Contains(line, serviceName) && strings.Contains(line, \"enabled\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (s *Snap) Operate(operate, serviceName string) error {\n\tif s.IsExist(serviceName) {\n\t\treturn handlerErr(run(nil, s.toolCmd, operate, serviceName))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/utils/controller/manager/systemd.go",
    "content": "package manager\n\nimport (\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/utils/ssh\"\n)\n\ntype Systemd struct {\n\ttoolCmd string\n\tClient  *ssh.SSHClient\n}\n\nfunc NewSystemd() *Systemd {\n\treturn &Systemd{toolCmd: \"systemctl\"}\n}\n\nfunc (s *Systemd) Name() string {\n\treturn \"systemd\"\n}\nfunc (s *Systemd) IsActive(serviceName string) (bool, error) {\n\tout, err := run(s.Client, s.toolCmd, \"is-active\", serviceName)\n\tif err != nil && out != \"inactive\\n\" {\n\t\tif NewSnap().IsActive(serviceName) {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, err\n\t}\n\treturn out == \"active\\n\", nil\n}\n\nfunc (s *Systemd) IsEnable(serviceName string) (bool, error) {\n\tout, err := run(s.Client, s.toolCmd, \"is-enabled\", serviceName)\n\tif err != nil && out != \"disabled\\n\" {\n\t\tif serviceName == \"sshd\" && out == \"alias\\n\" {\n\t\t\treturn s.IsEnable(\"ssh\")\n\t\t}\n\t\tif NewSnap().IsEnable(serviceName) {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, err\n\t}\n\treturn out == \"enabled\\n\", nil\n}\n\nfunc (s *Systemd) IsExist(serviceName string) (bool, error) {\n\tout, err := run(s.Client, s.toolCmd, \"is-enabled\", serviceName)\n\tif err != nil && out != \"enabled\\n\" {\n\t\tif strings.Contains(out, \"disabled\") {\n\t\t\treturn true, err\n\t\t}\n\t\tif NewSnap().IsExist(serviceName) {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, err\n\t}\n\treturn true, err\n}\n\nfunc (s *Systemd) Status(serviceName string) (string, error) {\n\treturn run(s.Client, s.toolCmd, \"status\", serviceName)\n}\nfunc (s *Systemd) Operate(operate, serviceName string) error {\n\tout, err := run(s.Client, s.toolCmd, operate, serviceName)\n\tif err != nil {\n\t\tif serviceName == \"sshd\" && strings.Contains(out, \"alias name or linked unit file\") {\n\t\t\treturn s.Operate(operate, \"ssh\")\n\t\t}\n\t\tif err := NewSnap().Operate(operate, serviceName); err == nil {\n\t\t\treturn nil\n\t\t}\n\t\treturn handlerErr(run(s.Client, s.toolCmd, operate, serviceName))\n\t}\n\treturn nil\n}\n\nfunc (s *Systemd) Reload() error {\n\tout, err := run(s.Client, s.toolCmd, \"daemon-reload\")\n\treturn handlerErr(out, err)\n}\n"
  },
  {
    "path": "core/utils/controller/manager/sysvinit.go",
    "content": "package manager\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/ssh\"\n)\n\ntype Sysvinit struct {\n\ttoolCmd string\n\tClient  *ssh.SSHClient\n}\n\nfunc NewSysvinit() *Sysvinit {\n\treturn &Sysvinit{toolCmd: \"service\"}\n}\n\nfunc (s *Sysvinit) Name() string {\n\treturn \"sysvinit\"\n}\nfunc (s *Sysvinit) IsActive(serviceName string) (bool, error) {\n\tout, err := cmd.RunDefaultWithStdoutBashCf(\"if service %s status >/dev/null 2>&1; then echo 'active'; else echo 'inactive'; fi\", serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn out == \"active\\n\", nil\n}\nfunc (s *Sysvinit) IsEnable(serviceName string) (bool, error) {\n\tout, err := cmd.RunDefaultWithStdoutBashCf(\"if ls /etc/rc*.d/S*%s >/dev/null 2>&1; then echo 'enabled'; else echo 'disabled'; fi\", serviceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn out == \"enabled\\n\", nil\n}\nfunc (s *Sysvinit) IsExist(serviceName string) (bool, error) {\n\tif _, err := os.Stat(filepath.Join(\"/etc/init.d\", serviceName)); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn false, nil\n\t\t}\n\t\treturn false, fmt.Errorf(\"stat /etc/init.d/%s failed: %w\", serviceName, err)\n\t}\n\treturn true, nil\n}\nfunc (s *Sysvinit) Status(serviceName string) (string, error) {\n\treturn run(s.Client, s.toolCmd, serviceName, \"status\")\n}\n\nfunc (s *Sysvinit) Operate(operate, serviceName string) error {\n\treturn handlerErr(run(s.Client, s.toolCmd, serviceName, operate))\n}\n\nfunc (s *Sysvinit) Reload() error {\n\treturn nil\n}\n"
  },
  {
    "path": "core/utils/copier/copier.go",
    "content": "package copier\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/pkg/errors\"\n)\n\nfunc Copy(to, from interface{}) error {\n\tb, err := json.Marshal(from)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"marshal from data err\")\n\t}\n\tif err = json.Unmarshal(b, to); err != nil {\n\t\treturn errors.Wrap(err, \"unmarshal to data err\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/utils/csv/command.go",
    "content": "package csv\n\nimport (\n\t\"encoding/csv\"\n\t\"os\"\n\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n)\n\ntype CommandTemplate struct {\n\tName    string `json:\"name\"`\n\tCommand string `json:\"command\"`\n}\n\nfunc ExportCommands(filename string, commands []CommandTemplate) error {\n\tfile, err := os.Create(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\twriter := csv.NewWriter(file)\n\tdefer writer.Flush()\n\n\tif err := writer.Write([]string{\n\t\ti18n.GetMsgByKey(\"Name\"),\n\t\ti18n.GetMsgByKey(\"Command\"),\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\tfor _, log := range commands {\n\t\trecord := []string{\n\t\t\tlog.Name,\n\t\t\tlog.Command,\n\t\t}\n\t\tif err := writer.Write(record); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/utils/encrypt/encrypt.go",
    "content": "package encrypt\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/model\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n)\n\nfunc StringDecryptWithBase64(text string) (string, error) {\n\tdecryptItem, err := StringDecrypt(text)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.StdEncoding.EncodeToString([]byte(decryptItem)), nil\n}\n\nfunc StringEncrypt(text string) (string, error) {\n\tif len(text) == 0 {\n\t\treturn \"\", nil\n\t}\n\tif len(global.CONF.Base.EncryptKey) == 0 {\n\t\tvar encryptSetting model.Setting\n\t\tif err := global.DB.Where(\"key = ?\", \"EncryptKey\").First(&encryptSetting).Error; err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tglobal.CONF.Base.EncryptKey = encryptSetting.Value\n\t}\n\tkey := global.CONF.Base.EncryptKey\n\treturn StringEncryptWithKey(text, key)\n}\n\nfunc StringEncryptWithKey(text, key string) (string, error) {\n\tif len(text) == 0 || len(key) == 0 {\n\t\treturn \"\", nil\n\t}\n\tpass := []byte(text)\n\txpass, err := aesEncryptWithSalt([]byte(key), pass)\n\tif err == nil {\n\t\tpass64 := base64.StdEncoding.EncodeToString(xpass)\n\t\treturn pass64, err\n\t}\n\treturn \"\", err\n}\n\nfunc StringDecrypt(text string) (string, error) {\n\tif len(text) == 0 {\n\t\treturn \"\", nil\n\t}\n\tif len(global.CONF.Base.EncryptKey) == 0 {\n\t\tvar encryptSetting model.Setting\n\t\tif err := global.DB.Where(\"key = ?\", \"EncryptKey\").First(&encryptSetting).Error; err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tglobal.CONF.Base.EncryptKey = encryptSetting.Value\n\t}\n\tkey := global.CONF.Base.EncryptKey\n\treturn StringDecryptWithKey(text, key)\n}\n\nfunc StringDecryptWithKey(text, key string) (string, error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tif global.LOG != nil {\n\t\t\t\tglobal.LOG.Errorf(\"A panic occurred during string decrypt with key, error message: %v\", r)\n\t\t\t}\n\t\t}\n\t}()\n\tif len(text) == 0 {\n\t\treturn \"\", nil\n\t}\n\tbytesPass, err := base64.StdEncoding.DecodeString(text)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tvar tpass []byte\n\ttpass, err = aesDecryptWithSalt([]byte(key), bytesPass)\n\tif err == nil {\n\t\tresult := string(tpass[:])\n\t\treturn result, err\n\t}\n\treturn \"\", err\n}\n\nfunc padding(plaintext []byte, blockSize int) []byte {\n\tpadding := blockSize - len(plaintext)%blockSize\n\tpadtext := bytes.Repeat([]byte{byte(padding)}, padding)\n\treturn append(plaintext, padtext...)\n}\n\nfunc unPadding(origData []byte) []byte {\n\tlength := len(origData)\n\tunpadding := int(origData[length-1])\n\treturn origData[:(length - unpadding)]\n}\n\nfunc aesEncryptWithSalt(key, plaintext []byte) ([]byte, error) {\n\tplaintext = padding(plaintext, aes.BlockSize)\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tciphertext := make([]byte, aes.BlockSize+len(plaintext))\n\tiv := ciphertext[0:aes.BlockSize]\n\tif _, err := io.ReadFull(rand.Reader, iv); err != nil {\n\t\treturn nil, err\n\t}\n\tcbc := cipher.NewCBCEncrypter(block, iv)\n\tcbc.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)\n\treturn ciphertext, nil\n}\nfunc aesDecryptWithSalt(key, ciphertext []byte) ([]byte, error) {\n\tvar block cipher.Block\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(ciphertext) < aes.BlockSize {\n\t\treturn nil, fmt.Errorf(\"iciphertext too short\")\n\t}\n\tiv := ciphertext[:aes.BlockSize]\n\tciphertext = ciphertext[aes.BlockSize:]\n\tcbc := cipher.NewCBCDecrypter(block, iv)\n\tcbc.CryptBlocks(ciphertext, ciphertext)\n\tciphertext = unPadding(ciphertext)\n\treturn ciphertext, nil\n}\n\nfunc ParseRSAPrivateKey(privateKeyPEM string) (*rsa.PrivateKey, error) {\n\tblock, _ := pem.Decode([]byte(privateKeyPEM))\n\tif block == nil || block.Type != \"RSA PRIVATE KEY\" {\n\t\treturn nil, errors.New(\"failed to decode PEM block containing the private key\")\n\t}\n\tprivateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn privateKey, nil\n}\n\nfunc aesDecrypt(ciphertext, key, iv []byte) ([]byte, error) {\n\tif len(key) != 16 && len(key) != 24 && len(key) != 32 {\n\t\treturn nil, errors.New(\"invalid AES key length: must be 16, 24, or 32 bytes\")\n\t}\n\tif len(iv) != aes.BlockSize {\n\t\treturn nil, errors.New(\"invalid IV length: must be 16 bytes\")\n\t}\n\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmode := cipher.NewCBCDecrypter(block, iv)\n\tmode.CryptBlocks(ciphertext, ciphertext)\n\tunpadded, err := pkcs7Unpad(ciphertext)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn unpadded, nil\n}\n\nfunc pkcs7Unpad(data []byte) ([]byte, error) {\n\tlength := len(data)\n\tif length == 0 {\n\t\treturn nil, errors.New(\"invalid padding size\")\n\t}\n\n\tpadLength := int(data[length-1])\n\tif padLength == 0 || padLength > length {\n\t\treturn nil, errors.New(\"invalid padding\")\n\t}\n\n\tfor i := 0; i < padLength; i++ {\n\t\tif data[length-1-i] != byte(padLength) {\n\t\t\treturn nil, errors.New(\"invalid padding\")\n\t\t}\n\t}\n\n\treturn data[:length-padLength], nil\n}\n\nfunc DecryptPassword(encryptedData string, privateKey *rsa.PrivateKey) (string, error) {\n\tparts := strings.Split(encryptedData, \":\")\n\tif len(parts) != 3 {\n\t\treturn \"\", errors.New(\"encrypted data format error\")\n\t}\n\tkeyCipher := parts[0]\n\tivBase64 := parts[1]\n\tciphertextBase64 := parts[2]\n\n\tencryptedAESKey, err := base64.StdEncoding.DecodeString(keyCipher)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"failed to decode keyCipher\")\n\t}\n\n\taesKey, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, encryptedAESKey)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"failed to decode AES Key\")\n\t}\n\n\tciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"failed to decrypt the encrypted data\")\n\t}\n\tiv, err := base64.StdEncoding.DecodeString(ivBase64)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"failed to decode the IV\")\n\t}\n\n\tpassword, err := aesDecrypt(ciphertext, aesKey, iv)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(password), nil\n}\n\nfunc ExportPrivateKeyToPEM(privateKey *rsa.PrivateKey) string {\n\tprivateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)\n\tprivateKeyPEM := pem.EncodeToMemory(&pem.Block{\n\t\tType:  \"RSA PRIVATE KEY\",\n\t\tBytes: privateKeyBytes,\n\t})\n\treturn string(privateKeyPEM)\n}\n\nfunc ExportPublicKeyToPEM(publicKey *rsa.PublicKey) (string, error) {\n\tpublicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tpublicKeyPEM := pem.EncodeToMemory(&pem.Block{\n\t\tType:  \"PUBLIC KEY\",\n\t\tBytes: publicKeyBytes,\n\t})\n\treturn string(publicKeyPEM), nil\n}\n"
  },
  {
    "path": "core/utils/files/files.go",
    "content": "package files\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/req_helper\"\n)\n\nfunc CopyFile(src, dst string, withName bool) error {\n\tsource, err := os.Open(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer source.Close()\n\n\tif path.Base(src) != path.Base(dst) && !withName {\n\t\tdst = path.Join(dst, path.Base(src))\n\t}\n\tif _, err := os.Stat(path.Dir(dst)); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\t_ = os.MkdirAll(path.Dir(dst), os.ModePerm)\n\t\t}\n\t}\n\ttarget, err := os.OpenFile(dst+\"_temp\", os.O_RDWR|os.O_CREATE|os.O_TRUNC, constant.FilePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer target.Close()\n\n\tif _, err = io.Copy(target, source); err != nil {\n\t\treturn err\n\t}\n\tif err = os.Rename(dst+\"_temp\", dst); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc CopyItem(isDir, withName bool, src, dst string) error {\n\tif path.Base(src) != path.Base(dst) && !withName {\n\t\tdst = path.Join(dst, path.Base(src))\n\t}\n\tsrcInfo, err := os.Stat(path.Dir(src))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err := os.Stat(dst); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\t_ = os.MkdirAll(dst, srcInfo.Mode())\n\t\t}\n\t}\n\tcmdStr := fmt.Sprintf(`cp -rf %s %s`, src, dst+\"/\")\n\tif !isDir {\n\t\tcmdStr = fmt.Sprintf(`cp -f %s %s`, src, dst+\"/\")\n\t}\n\tstdout, err := cmd.NewCommandMgr(cmd.WithTimeout(60 * time.Second)).RunWithStdoutBashC(cmdStr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"handle %s failed, stdout: %s, err: %v\", cmdStr, stdout, err)\n\t}\n\treturn nil\n}\n\nfunc CopyFileWithRename(src, dst string) error {\n\tsrcInfo, err := os.Stat(path.Dir(src))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err := os.Stat(path.Dir(dst)); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\t_ = os.MkdirAll(path.Dir(dst), srcInfo.Mode())\n\t\t}\n\t}\n\tif err := cmd.RunDefaultBashCf(\"cp -f %s %s.tmp\", src, dst); err != nil {\n\t\treturn fmt.Errorf(\"handle cp file failed, err: %v\", err)\n\t}\n\tif err = cmd.RunDefaultBashCf(\"mv %s.tmp %s\", dst, dst); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc HandleTar(sourceDir, targetDir, name, exclusionRules string, secret string) error {\n\tif _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(targetDir, os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\texMap := make(map[string]struct{})\n\texcludes := strings.Split(exclusionRules, \",\")\n\texcludeRules := \"\"\n\tfor _, exclude := range excludes {\n\t\tif len(exclude) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := exMap[exclude]; ok {\n\t\t\tcontinue\n\t\t}\n\t\texcludeRules += fmt.Sprintf(\" --exclude '%s'\", exclude)\n\t\texMap[exclude] = struct{}{}\n\t}\n\tpath := \"\"\n\tif strings.Contains(sourceDir, \"/\") {\n\t\titemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, \"/\"):], \"/\", \"\")\n\t\taheadDir := sourceDir[:strings.LastIndex(sourceDir, \"/\")]\n\t\tif len(aheadDir) == 0 {\n\t\t\taheadDir = \"/\"\n\t\t}\n\t\tpath += fmt.Sprintf(\"-C %s %s\", aheadDir, itemDir)\n\t} else {\n\t\tpath = sourceDir\n\t}\n\n\tcommands := \"\"\n\n\tif len(secret) != 0 {\n\t\textraCmd := \"| openssl enc -aes-256-cbc -salt -k '\" + secret + \"' -out\"\n\t\tcommands = fmt.Sprintf(\"tar -zcf %s %s %s %s\", \" -\"+excludeRules, path, extraCmd, targetDir+\"/\"+name)\n\t\tglobal.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(\" '%s' \", secret), \" ****** \"))\n\t} else {\n\t\tcommands = fmt.Sprintf(\"tar -zcf %s %s %s\", targetDir+\"/\"+name, excludeRules, path)\n\t\tglobal.LOG.Debug(commands)\n\t}\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(24*time.Hour), cmd.WithIgnoreExist1())\n\tstdout, err := cmdMgr.RunWithStdoutBashC(commands)\n\tif err != nil {\n\t\tif len(stdout) != 0 {\n\t\t\tglobal.LOG.Errorf(\"do handle tar failed, stdout: %s, err: %v\", stdout, err)\n\t\t\treturn fmt.Errorf(\"do handle tar failed, stdout: %s, err: %v\", stdout, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc HandleUnTar(sourceFile, targetDir string, secret string) error {\n\tif _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(targetDir, os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tcommands := \"\"\n\tif len(secret) != 0 {\n\t\textraCmd := \"openssl enc -d -aes-256-cbc -k '\" + secret + \"' -in \" + sourceFile + \" | \"\n\t\tcommands = fmt.Sprintf(\"%s tar -zxvf - -C %s\", extraCmd, targetDir+\" > /dev/null 2>&1\")\n\t\tglobal.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(\" '%s' \", secret), \" ****** \"))\n\t} else {\n\t\tcommands = fmt.Sprintf(\"tar zxvf '%s' -C '%s'\", sourceFile, targetDir)\n\t\tglobal.LOG.Debug(commands)\n\t}\n\n\tcmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(24 * time.Hour))\n\tstdout, err := cmdMgr.RunWithStdoutBashC(commands)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"do handle untar failed, stdout: %s, err: %v\", stdout, err)\n\t\treturn errors.New(stdout)\n\t}\n\treturn nil\n}\n\nfunc DownloadFile(url, dst string) error {\n\tresp, err := req_helper.HandleGet(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tout, err := os.Create(dst)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create download file [%s] error, err %s\", dst, err.Error())\n\t}\n\tdefer out.Close()\n\n\tif _, err = io.Copy(out, resp.Body); err != nil {\n\t\treturn fmt.Errorf(\"save download file [%s] error, err %s\", dst, err.Error())\n\t}\n\treturn nil\n}\n\nfunc DownloadFileWithProxyStream(url, dst string) error {\n\tresp, err := req_helper.HandleGetWithProxy(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode >= http.StatusBadRequest {\n\t\treturn fmt.Errorf(\"download file [%s] failed, status code: %d\", url, resp.StatusCode)\n\t}\n\n\ttmpDst := dst + \".part\"\n\t_ = os.Remove(tmpDst)\n\tout, err := os.Create(tmpDst)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create download file [%s] error, err %s\", dst, err.Error())\n\t}\n\tsuccess := false\n\tdefer func() {\n\t\t_ = out.Close()\n\t\tif !success {\n\t\t\t_ = os.Remove(tmpDst)\n\t\t}\n\t}()\n\n\tn, err := io.Copy(out, resp.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"save download file [%s] error, err %s\", dst, err.Error())\n\t}\n\tif resp.ContentLength > 0 && n != resp.ContentLength {\n\t\treturn fmt.Errorf(\"save download file [%s] error, content-length mismatch: expected %d, actual %d\", dst, resp.ContentLength, n)\n\t}\n\tif err = out.Sync(); err != nil {\n\t\treturn fmt.Errorf(\"sync download file [%s] error, err %s\", dst, err.Error())\n\t}\n\tif err = os.Rename(tmpDst, dst); err != nil {\n\t\treturn fmt.Errorf(\"rename download file [%s] error, err %s\", dst, err.Error())\n\t}\n\tsuccess = true\n\treturn nil\n}\n\nfunc Stat(path string) bool {\n\t_, err := os.Stat(path)\n\tif err != nil && os.IsNotExist(err) {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc GetFileMD5(filePath string) (string, error) {\n\tfile, err := os.Open(filePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer file.Close()\n\thash := md5.New()\n\n\tif _, err = io.Copy(hash, file); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn hex.EncodeToString(hash.Sum(nil)), nil\n}\n"
  },
  {
    "path": "core/utils/firewall/firewall.go",
    "content": "package firewall\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/1Panel-dev/1Panel/core/utils/cmd\"\n)\n\nfunc UpdatePort(oldPort, newPort string) error {\n\tfirewalld := cmd.Which(\"firewalld\")\n\tif firewalld {\n\t\tstatus, _ := cmd.RunDefaultWithStdoutBashC(\"LANGUAGE=en_US:en firewall-cmd --state\")\n\t\tisRunning := status == \"running\\n\"\n\t\tif isRunning {\n\t\t\treturn firewallUpdatePort(oldPort, newPort)\n\t\t}\n\t}\n\n\tufw := cmd.Which(\"ufw\")\n\tif !ufw {\n\t\treturn nil\n\t}\n\tstatus, _ := cmd.RunDefaultWithStdoutBashC(\"LANGUAGE=en_US:en ufw status | grep Status\")\n\tisRuning := status == \"Status: active\\n\"\n\tif isRuning {\n\t\treturn ufwUpdatePort(oldPort, newPort)\n\t}\n\treturn nil\n}\n\nfunc firewallUpdatePort(oldPort, newPort string) error {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"firewall-cmd --zone=public --add-port=%s/tcp --permanent\", newPort)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"add (port: %s/tcp) failed, err: %s\", newPort, stdout)\n\t}\n\n\t_, _ = cmd.RunDefaultWithStdoutBashCf(\"firewall-cmd --zone=public --remove-port=%s/tcp --permanent\", oldPort)\n\t_, _ = cmd.RunDefaultWithStdoutBashC(\"firewall-cmd --reload\")\n\treturn nil\n}\n\nfunc ufwUpdatePort(oldPort, newPort string) error {\n\tstdout, err := cmd.RunDefaultWithStdoutBashCf(\"ufw allow %s\", newPort)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"add (port: %s/tcp) failed, err: %s\", newPort, stdout)\n\t}\n\n\t_, _ = cmd.RunDefaultWithStdoutBashCf(\"ufw delete allow %s\", oldPort)\n\treturn nil\n}\n"
  },
  {
    "path": "core/utils/geo/geo.go",
    "content": "package geo\n\nimport (\n\t\"net\"\n\t\"path\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/oschwald/maxminddb-golang\"\n)\n\ntype Location struct {\n\tEn string `maxminddb:\"en\"`\n\tZh string `maxminddb:\"zh\"`\n}\n\ntype LocationRes struct {\n\tIso       string   `maxminddb:\"iso\"`\n\tCountry   Location `maxminddb:\"country\"`\n\tLatitude  float64  `maxminddb:\"latitude\"`\n\tLongitude float64  `maxminddb:\"longitude\"`\n\tProvince  Location `maxminddb:\"province\"`\n}\n\nfunc NewGeo() (*maxminddb.Reader, error) {\n\tgeoPath := path.Join(global.CONF.Base.InstallDir, \"1panel\", \"geo\", \"GeoIP.mmdb\")\n\treturn maxminddb.Open(geoPath)\n}\n\nfunc GetIPLocation(reader *maxminddb.Reader, ip, lang string) (string, error) {\n\tvar err error\n\tvar geoLocation LocationRes\n\tif reader == nil {\n\t\tgeoPath := path.Join(global.CONF.Base.InstallDir, \"1panel\", \"geo\", \"GeoIP.mmdb\")\n\t\treader, err = maxminddb.Open(geoPath)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tipNet := net.ParseIP(ip)\n\terr = reader.Lookup(ipNet, &geoLocation)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif lang == \"zh\" {\n\t\treturn geoLocation.Country.Zh + \" \" + geoLocation.Province.Zh, nil\n\t}\n\treturn geoLocation.Country.En + \" \" + geoLocation.Province.En, nil\n}\n"
  },
  {
    "path": "core/utils/mfa/mfa.go",
    "content": "package mfa\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/skip2/go-qrcode\"\n\t\"github.com/xlzd/gotp\"\n)\n\nconst secretLength = 16\n\ntype Otp struct {\n\tSecret  string `json:\"secret\"`\n\tQrImage string `json:\"qrImage\"`\n}\n\nfunc GetOtp(username, title string, interval int) (otp Otp, err error) {\n\tsecret := gotp.RandomSecret(secretLength)\n\totp.Secret = secret\n\ttotp := gotp.NewTOTP(secret, 6, interval, nil)\n\turi := totp.ProvisioningUri(username, title)\n\tsubImg, err := qrcode.Encode(uri, qrcode.Medium, 256)\n\tdist := make([]byte, 3000)\n\tbase64.StdEncoding.Encode(dist, subImg)\n\tindex := bytes.IndexByte(dist, 0)\n\tbaseImage := dist[0:index]\n\totp.QrImage = \"data:image/png;base64,\" + string(baseImage)\n\treturn\n}\n\nfunc ValidCode(code, intervalStr, secret string) bool {\n\tinterval, err := strconv.Atoi(intervalStr)\n\tif err != nil {\n\t\tglobal.LOG.Errorf(\"type conversion failed, err: %v\", err)\n\t\treturn false\n\t}\n\ttotp := gotp.NewTOTP(secret, 6, interval, nil)\n\tnow := time.Now().Unix()\n\tprevTime := now - int64(interval)\n\treturn totp.Verify(code, now) || totp.Verify(code, prevTime)\n}\n"
  },
  {
    "path": "core/utils/passkey/passkey_record.go",
    "content": "package passkey\n\nimport \"github.com/go-webauthn/webauthn/webauthn\"\n\ntype PasskeyCredentialRecord struct {\n\tID         string              `json:\"id\"`\n\tName       string              `json:\"name\"`\n\tCreatedAt  string              `json:\"createdAt\"`\n\tLastUsedAt string              `json:\"lastUsedAt\"`\n\tFlagsValue uint8               `json:\"flagsValue\"`\n\tCredential webauthn.Credential `json:\"credential\"`\n}\n"
  },
  {
    "path": "core/utils/passkey/passkey_store.go",
    "content": "package passkey\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/go-webauthn/webauthn/webauthn\"\n)\n\nconst (\n\tPasskeyUserIDSettingKey        = \"PasskeyUserID\"\n\tPasskeyCredentialSettingKey    = \"PasskeyCredentials\"\n\tPasskeyMaxCredentials          = 5\n\tPasskeySessionTTL              = 5 * time.Minute\n\tPasskeySessionKindLogin        = \"login\"\n\tPasskeySessionKindRegister     = \"register\"\n\tPasskeyCredentialNameDefault   = \"Passkey\"\n\tPasskeySessionStoreMaxEntries  = 1024\n)\n\nvar passkeySessions = newPasskeySessionStore()\n\nfunc GetPasskeySessionStore() *passkeySessionStore {\n\treturn passkeySessions\n}\n\ntype passkeySession struct {\n\tKind      string\n\tName      string\n\tSession   webauthn.SessionData\n\tExpiresAt time.Time\n}\n\ntype passkeySessionStore struct {\n\tmu    sync.Mutex\n\titems map[string]passkeySession\n}\n\nfunc newPasskeySessionStore() *passkeySessionStore {\n\treturn &passkeySessionStore{items: make(map[string]passkeySession)}\n}\n\nfunc (s *passkeySessionStore) Set(kind, name string, session webauthn.SessionData) string {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\ts.cleanupExpiredLocked()\n\tif len(s.items) >= PasskeySessionStoreMaxEntries {\n\t\ts.removeOldestLocked()\n\t}\n\n\tsessionID := generatePasskeySessionID()\n\ts.items[sessionID] = passkeySession{\n\t\tKind:      kind,\n\t\tName:      name,\n\t\tSession:   session,\n\t\tExpiresAt: time.Now().Add(PasskeySessionTTL),\n\t}\n\treturn sessionID\n}\n\nfunc (s *passkeySessionStore) Get(sessionID string) (passkeySession, bool) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\titem, ok := s.items[sessionID]\n\tif !ok {\n\t\treturn passkeySession{}, false\n\t}\n\tif time.Now().After(item.ExpiresAt) {\n\t\tdelete(s.items, sessionID)\n\t\treturn passkeySession{}, false\n\t}\n\treturn item, true\n}\n\nfunc (s *passkeySessionStore) Delete(sessionID string) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tdelete(s.items, sessionID)\n}\n\nfunc (s *passkeySessionStore) cleanupExpiredLocked() {\n\tnow := time.Now()\n\tfor id, item := range s.items {\n\t\tif now.After(item.ExpiresAt) {\n\t\t\tdelete(s.items, id)\n\t\t}\n\t}\n}\n\nfunc (s *passkeySessionStore) removeOldestLocked() {\n\tvar oldestID string\n\tvar oldestTime time.Time\n\tfor id, item := range s.items {\n\t\tif oldestID == \"\" || item.ExpiresAt.Before(oldestTime) {\n\t\t\toldestID = id\n\t\t\toldestTime = item.ExpiresAt\n\t\t}\n\t}\n\tif oldestID != \"\" {\n\t\tdelete(s.items, oldestID)\n\t}\n}\n\nfunc generatePasskeySessionID() string {\n\traw := make([]byte, 32)\n\tif _, err := rand.Read(raw); err != nil {\n\t\treturn common.RandStr(32)\n\t}\n\treturn base64.RawURLEncoding.EncodeToString(raw)\n}\n"
  },
  {
    "path": "core/utils/passkey/passkey_user.go",
    "content": "package passkey\n\nimport \"github.com/go-webauthn/webauthn/webauthn\"\n\ntype PasskeyUser struct {\n\tID          []byte\n\tName        string\n\tDisplayName string\n\tCredentials []webauthn.Credential\n}\n\nfunc (u PasskeyUser) WebAuthnID() []byte {\n\treturn u.ID\n}\n\nfunc (u PasskeyUser) WebAuthnName() string {\n\treturn u.Name\n}\n\nfunc (u PasskeyUser) WebAuthnDisplayName() string {\n\treturn u.DisplayName\n}\n\nfunc (u PasskeyUser) WebAuthnCredentials() []webauthn.Credential {\n\treturn u.Credentials\n}\n"
  },
  {
    "path": "core/utils/re/re.go",
    "content": "package re\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n)\n\nconst (\n\tOrderByValidationPattern = `^[a-zA-Z_][a-zA-Z0-9_]*$`\n)\n\nvar regexMap = make(map[string]*regexp.Regexp)\n\nfunc Init() {\n\tpatterns := []string{\n\t\tOrderByValidationPattern,\n\t}\n\n\tfor _, pattern := range patterns {\n\t\tregexMap[pattern] = regexp.MustCompile(pattern)\n\t}\n}\n\nfunc GetRegex(pattern string) *regexp.Regexp {\n\tregex, exists := regexMap[pattern]\n\tif !exists {\n\t\tpanic(fmt.Sprintf(\"regex pattern not found: %s\", pattern))\n\t}\n\treturn regex\n}\n\nfunc RegisterRegex(pattern string) {\n\tregexMap[pattern] = regexp.MustCompile(pattern)\n}\n"
  },
  {
    "path": "core/utils/req_helper/proxy_local/req_to_local.go",
    "content": "package proxy_local\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/gin-gonic/gin\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/dto\"\n\t\"github.com/1Panel-dev/1Panel/core/i18n\"\n)\n\nfunc NewLocalClient(reqUrl, reqMethod string, body io.Reader, ctx *gin.Context) (interface{}, error) {\n\tsockPath := \"/etc/1panel/agent.sock\"\n\tif _, err := os.Stat(sockPath); err != nil {\n\t\treturn nil, fmt.Errorf(\"no such agent.sock find in localhost, err: %v\", err)\n\t}\n\tdialUnix := func() (conn net.Conn, err error) {\n\t\treturn net.Dial(\"unix\", sockPath)\n\t}\n\ttransport := &http.Transport{\n\t\tDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\treturn dialUnix()\n\t\t},\n\t}\n\tclient := &http.Client{\n\t\tTransport: transport,\n\t}\n\tdefer client.CloseIdleConnections()\n\tparsedURL, err := url.Parse(\"http://unix\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"handle url Parse failed, err: %v \\n\", err)\n\t}\n\trURL := &url.URL{\n\t\tScheme: \"http\",\n\t\tPath:   reqUrl,\n\t\tHost:   parsedURL.Host,\n\t}\n\n\treq, err := http.NewRequest(reqMethod, rURL.String(), body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating request failed, err: %v\", err)\n\t}\n\tif ctx != nil {\n\t\tfor key, values := range ctx.Request.Header {\n\t\t\tfor _, value := range values {\n\t\t\t\treq.Header.Add(key, value)\n\t\t\t}\n\t\t}\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"client do request failed, err: %v\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"do request failed, err: %v\", resp.Status)\n\t}\n\tbodyByte, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read resp body from request failed, err: %v\", err)\n\t}\n\tvar respJson dto.Response\n\tif err := json.Unmarshal(bodyByte, &respJson); err != nil {\n\t\treturn nil, fmt.Errorf(\"json umarshal resp data failed, err: %v\", err)\n\t}\n\tif respJson.Code != http.StatusOK {\n\t\treturn nil, errors.New(strings.ReplaceAll(respJson.Message, i18n.Get(\"ErrInternalServerKey\"), \"\"))\n\t}\n\n\treturn respJson.Data, nil\n}\n"
  },
  {
    "path": "core/utils/req_helper/requset.go",
    "content": "package req_helper\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/buserr\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/xpack\"\n)\n\nfunc HandleRequest(url, method string, timeout int) (int, []byte, error) {\n\ttransport := &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\tDialContext: (&net.Dialer{\n\t\t\tTimeout:   60 * time.Second,\n\t\t\tKeepAlive: 60 * time.Second,\n\t\t}).DialContext,\n\t\tTLSHandshakeTimeout:   5 * time.Second,\n\t\tResponseHeaderTimeout: 10 * time.Second,\n\t\tIdleConnTimeout:       15 * time.Second,\n\t}\n\treturn handleRequestWithTransport(url, method, transport, timeout)\n}\n\nfunc HandleRequestWithProxy(url, method string, timeout int) (int, []byte, error) {\n\ttransport := xpack.LoadRequestTransport()\n\treturn handleRequestWithTransport(url, method, transport, timeout)\n}\n\nfunc handleRequestWithTransport(url, method string, transport *http.Transport, timeout int) (int, []byte, error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"handle request failed, error message: %v\", r)\n\t\t\treturn\n\t\t}\n\t}()\n\n\tclient := http.Client{Timeout: time.Duration(timeout) * time.Second, Transport: transport}\n\tctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)\n\tdefer cancel()\n\trequest, err := http.NewRequestWithContext(ctx, method, url, nil)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\tresp, err := client.Do(request)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\treturn resp.StatusCode, body, nil\n}\n\nfunc HandleGet(url string) (*http.Response, error) {\n\ttransport := &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\tDialContext: (&net.Dialer{\n\t\t\tTimeout:   60 * time.Second,\n\t\t\tKeepAlive: 60 * time.Second,\n\t\t}).DialContext,\n\t\tTLSHandshakeTimeout:   5 * time.Second,\n\t\tResponseHeaderTimeout: 10 * time.Second,\n\t\tIdleConnTimeout:       15 * time.Second,\n\t}\n\treturn handleGetWithTransport(url, transport)\n}\n\nfunc HandleGetWithProxy(url string) (*http.Response, error) {\n\ttransport := xpack.LoadRequestTransport()\n\treturn handleGetWithTransport(url, transport)\n}\n\nfunc handleGetWithTransport(url string, transport *http.Transport) (*http.Response, error) {\n\tclient := &http.Client{\n\t\tTimeout: time.Second * 300,\n\t}\n\tclient.Transport = transport\n\tdefer client.CloseIdleConnections()\n\n\treq, err := http.NewRequestWithContext(context.Background(), \"GET\", url, nil)\n\tif err != nil {\n\t\treturn nil, buserr.WithMap(\"ErrCreateHttpClient\", map[string]interface{}{\"err\": err.Error()}, err)\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tif errors.Is(err, context.DeadlineExceeded) {\n\t\t\treturn nil, buserr.WithMap(\"ErrHttpReqTimeOut\", map[string]interface{}{\"err\": err.Error()}, err)\n\t\t} else {\n\t\t\tif strings.Contains(err.Error(), \"no such host\") {\n\t\t\t\treturn nil, buserr.WithErr(\"ErrNoSuchHost\", errors.New(\"no such host\"))\n\t\t\t}\n\t\t\treturn nil, buserr.WithMap(\"ErrHttpReqFailed\", map[string]interface{}{\"err\": err.Error()}, err)\n\t\t}\n\t}\n\tif resp.StatusCode == 404 {\n\t\treturn nil, buserr.WithErr(\"ErrHttpReqNotFound\", errors.New(\"no such resource\"))\n\t}\n\n\treturn resp, nil\n}\n"
  },
  {
    "path": "core/utils/security/security.go",
    "content": "package security\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/app/service\"\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/res\"\n\t\"github.com/1Panel-dev/1Panel/core/cmd/server/web\"\n\t\"github.com/1Panel-dev/1Panel/core/constant\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/common\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc HandleNotRoute(c *gin.Context) bool {\n\tif !checkBindDomain(c) {\n\t\tHandleNotSecurity(c, \"err_domain\")\n\t\treturn false\n\t}\n\tif !checkIPLimit(c) {\n\t\tHandleNotSecurity(c, \"err_ip_limit\")\n\t\treturn false\n\t}\n\tif checkFrontendPath(c) {\n\t\tToIndexHtml(c)\n\t\treturn false\n\t}\n\tif isEntrancePath(c) {\n\t\tToIndexHtml(c)\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc CheckSecurity(c *gin.Context) bool {\n\tif !checkEntrance(c) && !checkSession(c) {\n\t\tHandleNotSecurity(c, \"\")\n\t\treturn false\n\t}\n\tif !checkBindDomain(c) {\n\t\tHandleNotSecurity(c, \"err_domain\")\n\t\treturn false\n\t}\n\tif !checkIPLimit(c) {\n\t\tHandleNotSecurity(c, \"err_ip_limit\")\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc ToIndexHtml(c *gin.Context) {\n\tc.Writer.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tc.Writer.WriteHeader(http.StatusOK)\n\tdata, err := web.IndexHtml.ReadFile(\"index.html\")\n\tif err != nil {\n\t\tc.String(http.StatusInternalServerError, \"index.html not found\")\n\t\treturn\n\t}\n\t_, _ = c.Writer.Write(data)\n\tc.Writer.Flush()\n}\n\nfunc isEntrancePath(c *gin.Context) bool {\n\tentrance := service.NewIAuthService().GetSecurityEntrance()\n\tif entrance != \"\" && strings.TrimSuffix(c.Request.URL.Path, \"/\") == \"/\"+entrance {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc checkEntrance(c *gin.Context) bool {\n\tauthService := service.NewIAuthService()\n\tentrance := authService.GetSecurityEntrance()\n\tif entrance == \"\" {\n\t\treturn true\n\t}\n\n\tcookieValue, err := c.Cookie(\"SecurityEntrance\")\n\tif err != nil {\n\t\treturn false\n\t}\n\tentranceValue, err := base64.StdEncoding.DecodeString(cookieValue)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn string(entranceValue) == entrance\n}\n\nfunc HandleNotSecurity(c *gin.Context, resType string) {\n\tresPage, err := service.NewIAuthService().GetResponsePage()\n\tif err != nil {\n\t\tc.String(http.StatusInternalServerError, \"Internal Server Error\")\n\t\treturn\n\t}\n\tif resPage == \"444\" {\n\t\tCloseDirectly(c)\n\t\treturn\n\t}\n\n\tfile := fmt.Sprintf(\"html/%s.html\", resPage)\n\tif resPage == \"200\" && resType != \"\" {\n\t\tfile = fmt.Sprintf(\"html/200_%s.html\", resType)\n\t}\n\tdata, err := res.ErrorMsg.ReadFile(file)\n\tif err != nil {\n\t\tc.String(http.StatusInternalServerError, \"Internal Server Error\")\n\t\treturn\n\t}\n\tstatusCode, err := strconv.Atoi(resPage)\n\tif err != nil {\n\t\tc.String(http.StatusInternalServerError, \"Internal Server Error\")\n\t\treturn\n\t}\n\tc.Data(statusCode, \"text/html; charset=utf-8\", data)\n}\n\nfunc isFrontendPath(c *gin.Context) bool {\n\treqUri := strings.TrimSuffix(c.Request.URL.Path, \"/\")\n\tif _, ok := constant.WebUrlMap[reqUri]; ok {\n\t\treturn true\n\t}\n\tfor _, route := range constant.DynamicRoutes {\n\t\tif match, _ := regexp.MatchString(route, reqUri); match {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc checkFrontendPath(c *gin.Context) bool {\n\tif !isFrontendPath(c) {\n\t\treturn false\n\t}\n\tauthService := service.NewIAuthService()\n\tif authService.GetSecurityEntrance() != \"\" {\n\t\treturn authService.IsLogin(c)\n\t}\n\treturn true\n}\n\nfunc checkBindDomain(c *gin.Context) bool {\n\tsettingRepo := repo.NewISettingRepo()\n\tstatus, _ := settingRepo.Get(repo.WithByKey(\"BindDomain\"))\n\tif len(status.Value) == 0 {\n\t\treturn true\n\t}\n\tdomains := c.Request.Host\n\tparts := strings.Split(c.Request.Host, \":\")\n\tif len(parts) > 0 {\n\t\tdomains = parts[0]\n\t}\n\treturn domains == status.Value\n}\n\nfunc checkIPLimit(c *gin.Context) bool {\n\tsettingRepo := repo.NewISettingRepo()\n\tstatus, _ := settingRepo.Get(repo.WithByKey(\"AllowIPs\"))\n\tif len(status.Value) == 0 {\n\t\treturn true\n\t}\n\tclientIP := common.GetRealClientIP(c)\n\tif common.IsPrivateIP(clientIP) {\n\t\treturn true\n\t}\n\n\tfor _, ip := range strings.Split(status.Value, \",\") {\n\t\tif len(ip) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif ip == clientIP || (strings.Contains(ip, \"/\") && common.CheckIpInCidr(ip, clientIP)) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc checkSession(c *gin.Context) bool {\n\t_, err := global.SESSION.Get(c)\n\treturn err == nil\n}\n\nfunc CloseDirectly(c *gin.Context) {\n\thijacker, ok := c.Writer.(http.Hijacker)\n\tif !ok {\n\t\tc.AbortWithStatus(http.StatusForbidden)\n\t\treturn\n\t}\n\tconn, _, err := hijacker.Hijack()\n\tif err != nil {\n\t\tc.AbortWithStatus(http.StatusForbidden)\n\t\treturn\n\t}\n\n\tconn.Close()\n\tc.Abort()\n}\n"
  },
  {
    "path": "core/utils/ssh/http.go",
    "content": "package ssh\n\nimport (\n\t\"bufio\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n)\n\ntype HTTPProxyDialer struct {\n\tType     string\n\tURL      string\n\tUser     string\n\tPassword string\n}\n\nfunc HTTPDial(dialer HTTPProxyDialer, network, addr string) (net.Conn, error) {\n\tvar conn net.Conn\n\tvar err error\n\n\tglobal.LOG.Debugf(\"Dialing HTTP proxy %s for %s\", dialer.URL, addr)\n\tdialer.URL = strings.TrimPrefix(dialer.URL, dialer.Type+\"://\")\n\tif dialer.Type == \"https\" {\n\t\tconn, err = tls.DialWithDialer(\n\t\t\t&net.Dialer{Timeout: 30 * time.Second},\n\t\t\tnetwork,\n\t\t\tdialer.URL,\n\t\t\t&tls.Config{InsecureSkipVerify: true},\n\t\t)\n\t} else {\n\t\tconn, err = net.DialTimeout(network, dialer.URL, 30*time.Second)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconn.SetDeadline(time.Now().Add(30 * time.Second))\n\tconnectReq := fmt.Sprintf(\"CONNECT %s HTTP/1.1\\r\\n\", addr)\n\tconnectReq += fmt.Sprintf(\"Host: %s\\r\\n\", addr)\n\tconnectReq += \"User-Agent: Go-ssh-client/1.0\\r\\n\"\n\n\tif dialer.User != \"\" {\n\t\tauth := base64.StdEncoding.EncodeToString(\n\t\t\t[]byte(dialer.User + \":\" + dialer.Password),\n\t\t)\n\t\tconnectReq += fmt.Sprintf(\"Proxy-Authorization: Basic %s\\r\\n\", auth)\n\t}\n\tconnectReq += \"Connection: keep-alive\\r\\n\\r\\n\"\n\tif _, err := conn.Write([]byte(connectReq)); err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\treader := bufio.NewReader(conn)\n\tresponse, err := reader.ReadString('\\n')\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\tif !strings.HasPrefix(response, \"HTTP/1.1 200\") &&\n\t\t!strings.HasPrefix(response, \"HTTP/1.0 200\") {\n\t\tconn.Close()\n\t\treturn nil, fmt.Errorf(\"proxy connection failed: %s\", strings.TrimSpace(response))\n\t}\n\tfor {\n\t\tline, err := reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tconn.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tif line == \"\\r\\n\" || line == \"\\n\" {\n\t\t\tbreak\n\t\t}\n\t}\n\tconn.SetDeadline(time.Time{})\n\n\treturn conn, nil\n}\n"
  },
  {
    "path": "core/utils/ssh/ssh.go",
    "content": "package ssh\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/app/repo\"\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/1Panel-dev/1Panel/core/utils/encrypt\"\n\tgossh \"golang.org/x/crypto/ssh\"\n\t\"golang.org/x/net/proxy\"\n)\n\ntype ConnInfo struct {\n\tUser       string `json:\"user\"`\n\tAddr       string `json:\"addr\"`\n\tPort       int    `json:\"port\"`\n\tAuthMode   string `json:\"authMode\"`\n\tPassword   string `json:\"password\"`\n\tPrivateKey []byte `json:\"privateKey\"`\n\tPassPhrase []byte `json:\"passPhrase\"`\n\n\tUseProxy    bool          `json:\"useProxy\"`\n\tDialTimeOut time.Duration `json:\"dialTimeOut\"`\n}\n\ntype SSHClient struct {\n\tClient   *gossh.Client `json:\"client\"`\n\tSudoItem string        `json:\"sudoItem\"`\n}\n\nfunc NewClient(c ConnInfo) (*SSHClient, error) {\n\tconfig := &gossh.ClientConfig{}\n\tconfig.SetDefaults()\n\taddr := net.JoinHostPort(c.Addr, fmt.Sprintf(\"%d\", c.Port))\n\tconfig.User = c.User\n\tif c.AuthMode == \"password\" {\n\t\tconfig.Auth = []gossh.AuthMethod{gossh.Password(c.Password)}\n\t} else {\n\t\tsigner, err := makePrivateKeySigner(c.PrivateKey, c.PassPhrase)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tconfig.Auth = []gossh.AuthMethod{gossh.PublicKeys(signer)}\n\t}\n\tif c.DialTimeOut == 0 {\n\t\tc.DialTimeOut = 5 * time.Second\n\t}\n\tconfig.Timeout = c.DialTimeOut\n\n\tconfig.HostKeyCallback = gossh.InsecureIgnoreHostKey()\n\tproto := \"tcp\"\n\tif strings.Contains(c.Addr, \":\") {\n\t\tproto = \"tcp6\"\n\t}\n\tclient, err := DialWithTimeout(proto, addr, c.UseProxy, config)\n\tif nil != err {\n\t\treturn nil, err\n\t}\n\tsshClient := &SSHClient{Client: client}\n\tif c.User == \"root\" {\n\t\treturn sshClient, nil\n\t}\n\tif _, err := sshClient.Run(\"sudo -n ls\"); err == nil {\n\t\tsshClient.SudoItem = \"sudo\"\n\t}\n\treturn sshClient, nil\n}\n\nfunc (c *SSHClient) Run(shell string) (string, error) {\n\tshell = c.SudoItem + \" \" + shell\n\tshell = strings.ReplaceAll(shell, \" && \", fmt.Sprintf(\" && %s \", c.SudoItem))\n\tsession, err := c.Client.NewSession()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer session.Close()\n\tbuf, err := session.CombinedOutput(shell)\n\n\treturn string(buf), err\n}\n\nfunc (c *SSHClient) CpFileWithCheck(src, dst string) error {\n\tlocalMd5, err := c.Runf(\"md5sum %s | awk '{print $1}'\", src)\n\tif err != nil {\n\t\tglobal.LOG.Debugf(\"load md5sum with src for %s failed, std: %s, err: %v\", path.Base(src), localMd5, err)\n\t\tlocalMd5 = \"\"\n\t}\n\tfor i := 0; i < 3; i++ {\n\t\tstd, cpErr := c.Runf(\"cp %s %s\", src, dst)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"cp file %s failed, std: %s, err: %v\", src, std, cpErr)\n\t\t\tcontinue\n\t\t}\n\t\tif len(strings.TrimSpace(localMd5)) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tremoteMd5, errDst := c.Runf(\"md5sum %s | awk '{print $1}'\", dst)\n\t\tif errDst != nil {\n\t\t\tglobal.LOG.Debugf(\"load md5sum with dst for %s failed, std: %s, err: %v\", path.Base(src), remoteMd5, errDst)\n\t\t\treturn nil\n\t\t}\n\t\tif strings.TrimSpace(localMd5) == strings.TrimSpace(remoteMd5) {\n\t\t\treturn nil\n\t\t}\n\t\terr = errors.New(\"cp file failed, file is not match!\")\n\t}\n\n\treturn err\n}\n\nfunc (c *SSHClient) SudoHandleCmd() string {\n\tif _, err := c.Run(\"sudo -n ls\"); err == nil {\n\t\treturn \"sudo \"\n\t}\n\treturn \"\"\n}\n\nfunc (c *SSHClient) IsRoot(user string) bool {\n\tif user == \"root\" {\n\t\treturn true\n\t}\n\t_, err := c.Run(\"sudo -n true\")\n\treturn err == nil\n}\n\nfunc (c *SSHClient) Runf(shell string, args ...interface{}) (string, error) {\n\tshell = c.SudoItem + \" \" + shell\n\tshell = strings.ReplaceAll(shell, \" && \", fmt.Sprintf(\" && %s \", c.SudoItem))\n\tsession, err := c.Client.NewSession()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer session.Close()\n\tbuf, err := session.CombinedOutput(fmt.Sprintf(shell, args...))\n\n\treturn string(buf), err\n}\n\nfunc (c *SSHClient) Close() {\n\tif c.Client != nil {\n\t\t_ = c.Client.Close()\n\t}\n}\n\nfunc makePrivateKeySigner(privateKey []byte, passPhrase []byte) (gossh.Signer, error) {\n\tif len(passPhrase) != 0 {\n\t\treturn gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)\n\t}\n\treturn gossh.ParsePrivateKey(privateKey)\n}\n\nfunc (c *SSHClient) RunWithStreamOutput(command string, outputCallback func(string)) error {\n\tsession, err := c.Client.NewSession()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create SSH session: %w\", err)\n\t}\n\tdefer session.Close()\n\n\tstdout, err := session.StdoutPipe()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set up stdout pipe: %w\", err)\n\t}\n\n\tstderr, err := session.StderrPipe()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set up stderr pipe: %w\", err)\n\t}\n\n\tif err := session.Start(command); err != nil {\n\t\treturn fmt.Errorf(\"failed to start command: %w\", err)\n\t}\n\n\tstdoutCh := make(chan string, 100)\n\tstderrCh := make(chan string, 100)\n\tdoneCh := make(chan struct{})\n\n\tgo func() {\n\t\tbuffer := make([]byte, 1024)\n\t\tfor {\n\t\t\tn, err := stdout.Read(buffer)\n\t\t\tif err != nil {\n\t\t\t\tclose(stdoutCh)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif n > 0 {\n\t\t\t\tstdoutCh <- string(buffer[:n])\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tbuffer := make([]byte, 1024)\n\t\tfor {\n\t\t\tn, err := stderr.Read(buffer)\n\t\t\tif err != nil {\n\t\t\t\tclose(stderrCh)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif n > 0 {\n\t\t\t\tstderrCh <- string(buffer[:n])\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase stdoutOutput, ok := <-stdoutCh:\n\t\t\t\tif !ok {\n\t\t\t\t\tstdoutCh = nil\n\t\t\t\t\tif stderrCh == nil {\n\t\t\t\t\t\tclose(doneCh)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif outputCallback != nil {\n\t\t\t\t\toutputCallback(stdoutOutput)\n\t\t\t\t}\n\n\t\t\tcase stderrOutput, ok := <-stderrCh:\n\t\t\t\tif !ok {\n\t\t\t\t\tstderrCh = nil\n\t\t\t\t\tif stdoutCh == nil {\n\t\t\t\t\t\tclose(doneCh)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif outputCallback != nil {\n\t\t\t\t\toutputCallback(stderrOutput)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\terr = session.Wait()\n\t<-doneCh\n\n\treturn err\n}\n\nfunc DialWithTimeout(network, addr string, useProxy bool, config *gossh.ClientConfig) (*gossh.Client, error) {\n\tvar conn net.Conn\n\tvar err error\n\tif useProxy {\n\t\tconn, err = loadSSHConnByProxy(network, addr, config.Timeout)\n\t} else {\n\t\tconn, err = net.DialTimeout(network, addr, config.Timeout)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_ = conn.SetDeadline(time.Now().Add(config.Timeout))\n\tc, chans, reqs, err := gossh.NewClientConn(conn, addr, config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := conn.SetDeadline(time.Time{}); err != nil {\n\t\tconn.Close()\n\t\treturn nil, fmt.Errorf(\"clear deadline failed: %v\", err)\n\t}\n\treturn gossh.NewClient(c, chans, reqs), nil\n}\n\nfunc loadSSHConnByProxy(network, addr string, timeout time.Duration) (net.Conn, error) {\n\tsettingRepo := repo.NewISettingRepo()\n\tproxyType, err := settingRepo.Get(repo.WithByKey(\"ProxyType\"))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get proxy type from db failed, err: %v\", err)\n\t}\n\tif len(proxyType.Value) == 0 {\n\t\treturn nil, fmt.Errorf(\"get proxy type from db failed, err: %v\", err)\n\t}\n\tproxyUrl, _ := settingRepo.Get(repo.WithByKey(\"ProxyUrl\"))\n\tport, _ := settingRepo.Get(repo.WithByKey(\"ProxyPort\"))\n\tuser, _ := settingRepo.Get(repo.WithByKey(\"ProxyUser\"))\n\tpasswd, _ := settingRepo.Get(repo.WithByKey(\"ProxyPasswd\"))\n\n\tpass, _ := encrypt.StringDecrypt(passwd.Value)\n\tproxyItem := fmt.Sprintf(\"%s:%s\", proxyUrl.Value, port.Value)\n\tswitch proxyType.Value {\n\tcase \"http\", \"https\":\n\t\titem := HTTPProxyDialer{\n\t\t\tType:     proxyType.Value,\n\t\t\tURL:      proxyItem,\n\t\t\tUser:     user.Value,\n\t\t\tPassword: pass,\n\t\t}\n\t\treturn HTTPDial(item, network, addr)\n\tcase \"socks5\":\n\t\tvar auth *proxy.Auth\n\t\tif len(user.Value) == 0 {\n\t\t\tauth = nil\n\t\t} else {\n\t\t\tauth = &proxy.Auth{\n\t\t\t\tUser:     user.Value,\n\t\t\t\tPassword: pass,\n\t\t\t}\n\t\t}\n\t\tdialer, err := proxy.SOCKS5(\"tcp\", proxyItem, auth, &net.Dialer{\n\t\t\tTimeout:   30 * time.Second,\n\t\t\tKeepAlive: 30 * time.Second,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"new socks5 proxy failed, err: %v\", err)\n\t\t}\n\t\treturn dialer.Dial(network, addr)\n\tdefault:\n\t\tconn, err := net.DialTimeout(network, addr, timeout)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn conn, nil\n\t}\n}\n"
  },
  {
    "path": "core/utils/terminal/local_cmd.go",
    "content": "package terminal\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"syscall\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/creack/pty\"\n\t\"github.com/pkg/errors\"\n)\n\nconst (\n\tDefaultCloseSignal  = syscall.SIGINT\n\tDefaultCloseTimeout = 10 * time.Second\n)\n\ntype LocalCommand struct {\n\tcloseSignal  syscall.Signal\n\tcloseTimeout time.Duration\n\n\tcmd *exec.Cmd\n\tpty *os.File\n}\n\nfunc NewCommand(script string) (*LocalCommand, error) {\n\tcmd := exec.Command(\"bash\")\n\tif term := os.Getenv(\"TERM\"); term != \"\" {\n\t\tcmd.Env = append(os.Environ(), \"TERM=\"+term)\n\t} else {\n\t\tcmd.Env = append(os.Environ(), \"TERM=xterm\")\n\t}\n\tcmd.Env = append(cmd.Env, \"INIT_SCRIPT=\"+script)\n\tpty, err := pty.Start(cmd)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to start command\")\n\t}\n\tif len(script) != 0 {\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\t_, _ = pty.Write([]byte(\"bash -c \\\"$INIT_SCRIPT\\\"\\n\"))\n\t}\n\n\tlcmd := &LocalCommand{\n\t\tcloseSignal:  DefaultCloseSignal,\n\t\tcloseTimeout: DefaultCloseTimeout,\n\n\t\tcmd: cmd,\n\t\tpty: pty,\n\t}\n\n\treturn lcmd, nil\n}\n\nfunc (lcmd *LocalCommand) Read(p []byte) (n int, err error) {\n\treturn lcmd.pty.Read(p)\n}\n\nfunc (lcmd *LocalCommand) Write(p []byte) (n int, err error) {\n\treturn lcmd.pty.Write(p)\n}\n\nfunc (lcmd *LocalCommand) Close() error {\n\tif lcmd.cmd != nil && lcmd.cmd.Process != nil {\n\t\t_ = lcmd.cmd.Process.Kill()\n\t}\n\t_ = lcmd.pty.Close()\n\treturn nil\n}\n\nfunc (lcmd *LocalCommand) ResizeTerminal(width int, height int) error {\n\twindow := struct {\n\t\trow uint16\n\t\tcol uint16\n\t\tx   uint16\n\t\ty   uint16\n\t}{\n\t\tuint16(height),\n\t\tuint16(width),\n\t\t0,\n\t\t0,\n\t}\n\t_, _, errno := syscall.Syscall(\n\t\tsyscall.SYS_IOCTL,\n\t\tlcmd.pty.Fd(),\n\t\tsyscall.TIOCSWINSZ,\n\t\tuintptr(unsafe.Pointer(&window)),\n\t)\n\tif errno != 0 {\n\t\treturn errno\n\t} else {\n\t\treturn nil\n\t}\n}\n\nfunc (lcmd *LocalCommand) Wait(quitChan chan bool) {\n\tif err := lcmd.cmd.Wait(); err != nil {\n\t\tglobal.LOG.Errorf(\"ssh session wait failed, err: %v\", err)\n\t\tsetQuit(quitChan)\n\t}\n\tsetQuit(quitChan)\n}\n"
  },
  {
    "path": "core/utils/terminal/ws_local_session.go",
    "content": "package terminal\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"sync\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/gorilla/websocket\"\n\t\"github.com/pkg/errors\"\n)\n\ntype LocalWsSession struct {\n\tslave  *LocalCommand\n\twsConn *websocket.Conn\n\n\tallowCtrlC bool\n\twriteMutex sync.Mutex\n}\n\nfunc NewLocalWsSession(cols, rows int, wsConn *websocket.Conn, slave *LocalCommand, allowCtrlC bool) (*LocalWsSession, error) {\n\tif err := slave.ResizeTerminal(cols, rows); err != nil {\n\t\tglobal.LOG.Errorf(\"ssh pty change windows size failed, err: %v\", err)\n\t}\n\n\treturn &LocalWsSession{\n\t\tslave:  slave,\n\t\twsConn: wsConn,\n\n\t\tallowCtrlC: allowCtrlC,\n\t}, nil\n}\n\nfunc (sws *LocalWsSession) Start(quitChan chan bool) {\n\tgo sws.handleSlaveEvent(quitChan)\n\tgo sws.receiveWsMsg(quitChan)\n}\n\nfunc (sws *LocalWsSession) handleSlaveEvent(exitCh chan bool) {\n\tdefer setQuit(exitCh)\n\tdefer global.LOG.Debug(\"thread of handle slave event has exited now\")\n\n\tbuffer := make([]byte, 1024)\n\tfor {\n\t\tselect {\n\t\tcase <-exitCh:\n\t\t\treturn\n\t\tdefault:\n\t\t\tn, _ := sws.slave.Read(buffer)\n\t\t\t_ = sws.masterWrite(buffer[:n])\n\t\t}\n\t}\n}\n\nfunc (sws *LocalWsSession) masterWrite(data []byte) error {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"A panic occurred during write ws message to master, error message: %v\", r)\n\t\t}\n\t}()\n\tsws.writeMutex.Lock()\n\tdefer sws.writeMutex.Unlock()\n\twsData, err := json.Marshal(WsMsg{\n\t\tType: WsMsgCmd,\n\t\tData: base64.StdEncoding.EncodeToString(data),\n\t})\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"failed to encoding to json\")\n\t}\n\terr = sws.wsConn.WriteMessage(websocket.TextMessage, wsData)\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"failed to write to master\")\n\t}\n\treturn nil\n}\n\nfunc (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tsetQuit(exitCh)\n\t\t\tglobal.LOG.Errorf(\"A panic occurred during receive ws message, error message: %v\", r)\n\t\t}\n\t}()\n\twsConn := sws.wsConn\n\tdefer setQuit(exitCh)\n\tdefer global.LOG.Debug(\"thread of receive ws msg has exited now\")\n\tfor {\n\t\tselect {\n\t\tcase <-exitCh:\n\t\t\treturn\n\t\tdefault:\n\t\t\t_, wsData, err := wsConn.ReadMessage()\n\t\t\tif err != nil {\n\t\t\t\tglobal.LOG.Errorf(\"reading webSocket message failed, err: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tmsgObj := WsMsg{}\n\t\t\t_ = json.Unmarshal(wsData, &msgObj)\n\t\t\tswitch msgObj.Type {\n\t\t\tcase WsMsgResize:\n\t\t\t\tif msgObj.Cols > 0 && msgObj.Rows > 0 {\n\t\t\t\t\tif err := sws.slave.ResizeTerminal(msgObj.Cols, msgObj.Rows); err != nil {\n\t\t\t\t\t\tglobal.LOG.Errorf(\"ssh pty change windows size failed, err: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase WsMsgCmd:\n\t\t\t\tdecodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Data)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"websock cmd string base64 decoding failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\tif string(decodeBytes) != \"\\x03\" || sws.allowCtrlC {\n\t\t\t\t\tsws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes)\n\t\t\t\t}\n\t\t\tcase WsMsgHeartbeat:\n\t\t\t\terr = wsConn.WriteMessage(websocket.TextMessage, wsData)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"ssh sending heartbeat to webSocket failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (sws *LocalWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {\n\tif _, err := sws.slave.Write(cmdBytes); err != nil {\n\t\tglobal.LOG.Errorf(\"ws cmd bytes write to ssh.stdin pipe failed, err: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "core/utils/terminal/ws_session.go",
    "content": "package terminal\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/global\"\n\t\"github.com/gorilla/websocket\"\n\t\"golang.org/x/crypto/ssh\"\n)\n\ntype safeBuffer struct {\n\tbuffer bytes.Buffer\n\tmu     sync.Mutex\n}\n\nfunc (w *safeBuffer) Write(p []byte) (int, error) {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\treturn w.buffer.Write(p)\n}\nfunc (w *safeBuffer) Bytes() []byte {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\treturn w.buffer.Bytes()\n}\nfunc (w *safeBuffer) Reset() {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\tw.buffer.Reset()\n}\n\nconst (\n\tWsMsgCmd       = \"cmd\"\n\tWsMsgResize    = \"resize\"\n\tWsMsgHeartbeat = \"heartbeat\"\n)\n\ntype WsMsg struct {\n\tType      string `json:\"type\"`\n\tData      string `json:\"data,omitempty\"`      // WsMsgCmd\n\tCols      int    `json:\"cols,omitempty\"`      // WsMsgResize\n\tRows      int    `json:\"rows,omitempty\"`      // WsMsgResize\n\tTimestamp int    `json:\"timestamp,omitempty\"` // WsMsgHeartbeat\n}\n\ntype LogicSshWsSession struct {\n\tstdinPipe       io.WriteCloser\n\tcomboOutput     *safeBuffer\n\tlogBuff         *safeBuffer\n\tinputFilterBuff *safeBuffer\n\tsession         *ssh.Session\n\twsConn          *websocket.Conn\n\tisAdmin         bool\n\tIsFlagged       bool\n}\n\nfunc NewLogicSshWsSession(cols, rows int, sshClient *ssh.Client, wsConn *websocket.Conn, initCmd string) (*LogicSshWsSession, error) {\n\tsshSession, err := sshClient.NewSession()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstdinP, err := sshSession.StdinPipe()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcomboWriter := new(safeBuffer)\n\tlogBuf := new(safeBuffer)\n\tinputBuf := new(safeBuffer)\n\tsshSession.Stdout = comboWriter\n\tsshSession.Stderr = comboWriter\n\n\tmodes := ssh.TerminalModes{\n\t\tssh.ECHO:          1,\n\t\tssh.TTY_OP_ISPEED: 14400,\n\t\tssh.TTY_OP_OSPEED: 14400,\n\t}\n\tif err := sshSession.RequestPty(\"xterm\", rows, cols, modes); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := sshSession.Shell(); err != nil {\n\t\treturn nil, err\n\t}\n\tif len(initCmd) != 0 {\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\t_, _ = stdinP.Write([]byte(\" clear &&\" + initCmd + \"\\n\"))\n\t}\n\treturn &LogicSshWsSession{\n\t\tstdinPipe:       stdinP,\n\t\tcomboOutput:     comboWriter,\n\t\tlogBuff:         logBuf,\n\t\tinputFilterBuff: inputBuf,\n\t\tsession:         sshSession,\n\t\twsConn:          wsConn,\n\t\tisAdmin:         true,\n\t\tIsFlagged:       false,\n\t}, nil\n}\n\nfunc (sws *LogicSshWsSession) Close() {\n\tif sws.session != nil {\n\t\tsws.session.Close()\n\t}\n\tif sws.logBuff != nil {\n\t\tsws.logBuff = nil\n\t}\n\tif sws.comboOutput != nil {\n\t\tsws.comboOutput = nil\n\t}\n}\n\nfunc (sws *LogicSshWsSession) Start(quitChan chan bool) {\n\tgo sws.receiveWsMsg(quitChan)\n\tgo sws.sendComboOutput(quitChan)\n}\n\nfunc (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tglobal.LOG.Errorf(\"[A panic occurred during receive ws message, error message: %v\", r)\n\t\t}\n\t}()\n\twsConn := sws.wsConn\n\tdefer setQuit(exitCh)\n\tfor {\n\t\tselect {\n\t\tcase <-exitCh:\n\t\t\treturn\n\t\tdefault:\n\t\t\t_, wsData, err := wsConn.ReadMessage()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tmsgObj := WsMsg{}\n\t\t\t_ = json.Unmarshal(wsData, &msgObj)\n\t\t\tswitch msgObj.Type {\n\t\t\tcase WsMsgResize:\n\t\t\t\tif msgObj.Cols > 0 && msgObj.Rows > 0 {\n\t\t\t\t\tif err := sws.session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {\n\t\t\t\t\t\tglobal.LOG.Errorf(\"ssh pty change windows size failed, err: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase WsMsgCmd:\n\t\t\t\tdecodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Data)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"websock cmd string base64 decoding failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\tsws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes)\n\t\t\tcase WsMsgHeartbeat:\n\t\t\t\t// 接收到心跳包后将心跳包原样返回，可以用于网络延迟检测等情况\n\t\t\t\terr = wsConn.WriteMessage(websocket.TextMessage, wsData)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"ssh sending heartbeat to webSocket failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (sws *LogicSshWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {\n\tif _, err := sws.stdinPipe.Write(cmdBytes); err != nil {\n\t\tglobal.LOG.Errorf(\"ws cmd bytes write to ssh.stdin pipe failed, err: %v\", err)\n\t}\n}\n\nfunc (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) {\n\twsConn := sws.wsConn\n\tdefer setQuit(exitCh)\n\n\ttick := time.NewTicker(time.Millisecond * time.Duration(60))\n\tdefer tick.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-tick.C:\n\t\t\tif sws.comboOutput == nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tbs := sws.comboOutput.Bytes()\n\t\t\tif len(bs) > 0 {\n\t\t\t\twsData, err := json.Marshal(WsMsg{\n\t\t\t\t\tType: WsMsgCmd,\n\t\t\t\t\tData: base64.StdEncoding.EncodeToString(bs),\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"encoding combo output to json failed, err: %v\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\terr = wsConn.WriteMessage(websocket.TextMessage, wsData)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"ssh sending combo output to webSocket failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\t_, err = sws.logBuff.Write(bs)\n\t\t\t\tif err != nil {\n\t\t\t\t\tglobal.LOG.Errorf(\"combo output to log buffer failed, err: %v\", err)\n\t\t\t\t}\n\t\t\t\tsws.comboOutput.buffer.Reset()\n\t\t\t}\n\t\t\tif string(bs) == string([]byte{13, 10, 108, 111, 103, 111, 117, 116, 13, 10}) {\n\t\t\t\tsws.Close()\n\t\t\t\treturn\n\t\t\t}\n\n\t\tcase <-exitCh:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (sws *LogicSshWsSession) Wait(quitChan chan bool) {\n\tif err := sws.session.Wait(); err != nil {\n\t\tsetQuit(quitChan)\n\t}\n}\n\nfunc setQuit(ch chan bool) {\n\tch <- true\n}\n"
  },
  {
    "path": "core/utils/xpack/community.go",
    "content": "//go:build !xpack\n\npackage xpack\n\nimport (\n\t\"crypto/tls\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/1Panel-dev/1Panel/core/utils/ssh\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc Proxy(c *gin.Context, currentNode string) {}\n\nfunc ProxyDocker(proxyURL string) error { return nil }\n\nfunc UpdateGroup(name string, group, newGroup uint) error { return nil }\n\nfunc CheckBackupUsed(name string) error { return nil }\n\nfunc LoadRequestTransport() *http.Transport {\n\treturn &http.Transport{\n\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\tDialContext: (&net.Dialer{\n\t\t\tTimeout:   60 * time.Second,\n\t\t\tKeepAlive: 60 * time.Second,\n\t\t}).DialContext,\n\t\tTLSHandshakeTimeout:   5 * time.Second,\n\t\tResponseHeaderTimeout: 10 * time.Second,\n\t\tIdleConnTimeout:       15 * time.Second,\n\t}\n}\n\nfunc LoadNodeInfo(currentNode string) (*ssh.ConnInfo, string, error) {\n\treturn nil, \"\", nil\n}\n\nfunc Sync(dataType string) error { return nil }\n\nfunc AutoUpgradeWithMaster() {}\n"
  },
  {
    "path": "docs/README.ar.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>أفضل أداة إدارة خوادم لينكس عبر الويب بتقييم عالٍ</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>  \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n<div dir=\"rtl\">\n1Panel هي لوحة تحكم وتشغيل وتصيان خادمات Linux من الجيل الجديد.\n\n- **الإدارة الفعالة**: من خلال واجهة رسومية ويب مستخدمة بسهولة، يتيح 1Panel للمستخدمين إدارة خادمات Linux الخاصة بهم بلا عناء. وتشمل الميزات الرئيسية مراقبة المضيفات، وإدارة الملفات، وإدارة قواعد البيانات، وإدارة الحاويات. \n- **استعراض المواقع على الإنترنت بسرعة**: بفضل التكامل العميق مع برنامج WordPress الشهير لبناء المواقع عبر الإنترنت المفتوح المصدر، يعمل 1Panel على تبسيط عملية ربطドومين (المجال) وتكوين شهادات SSL، وكل ذلك يمكن تحقيقه بضغطة زر واحدة. \n- **مخزن التطبيقات**: يقوم 1Panel بترتيب مجموعة واسعة من الأدوات والتطبيقات المفتوحة المصدر عالية الجودة، مما يسهل عملية التثبيت والتحديثات لاستخدميه.\n- **الأمن والاستقرار**: من خلال الاستفادة من الحاويات وممارسات استقرارية لتوزيع التطبيقات الآمنة، يعمل 1Panel على تقليل تغيرات الحساسية. كما يعزز الأمن بشكل أكبر من خلال إمكانيات إدارة الحماية الحائط والتدقيق في السجلات. \n- **نسخ واحتياطي و استعادة بضغطة زر واحدة**: حماية البيانات أصبحت بسيطة بفضل وظيفة النسخ واحتياطي واستعادة بضغطة زر واحدة في 1Panel، والتي تدعم حلول تخزين سحابية مختلفة لضمان سلامة البيانات وسهولة الوصول إليها. \n\n## بداية سريعة\n\nأعد تشغيل البرنامج النصي أدناه واتبع التعليمات التي تظهر لتثبيت 1Panel:\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nيرجى الرجوع إلى مستنداتنا [هنا](https://docs.1panel.pro/quick_start/) لمزيد من التفاصيل.\n\n## مقاطع شاشة\n\n![UI Display](https://resource.1panel.pro/img/1panel.png)\n\n## تاريخ النجوم\n\n[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## معلومات أمنية\n\nإذا اكتشفت أي مشاكل أمنية، يرجى الاتصال بنا عبر: wanghe@fit2cloud.com.\n\n## رخصة\n\nمخول بموجب الرخصة العامة العامة لغنو GPLv3 (الرخصة العامة العامة لغنو GPLv3) (هذه \"الرخصة\")؛ لن تتمكن من استخدام هذا الملف إلا إذا اتبعت الرخصة. يمكنك الحصول على نسخة من الرخصة من\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nما لم يكن ذلك مطلوبًا بموجب القانون المعمول به أو لم يتم الاتفاق عليه كتابيًا، يتم توزيع البرنامج الموزع بموجب الرخصة على أساس \"كما هو\"، بلا أي نوع من الضمانات أو الشروط، سواء كانت صريحة أو ضمنية. راجع الرخصة للحصول على اللغة المحددة التي تحكم الصلاحيات والقيود بموجب الرخصة. \n</div>\n"
  },
  {
    "path": "docs/README.de.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>Hochbewertetes Web-basiertes Linux-Server-Management-Tool</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>  \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel ist ein modernes und quelloffenes Web-basiertes Steuerungsfeld zur Verwaltung von Linux-Servern.\n\n- **Effiziente Verwaltung**: Über eine benutzerfreundliche grafische Web-Schnittstelle ermöglicht 1Panel es Benutzern, ihre Linux-Server mühelos zu verwalten. Zu seinen Hauptfunktionen gehören die Überwachung des Hosts, die Dateiverwaltung, die Datenbankadministration und die Containerverwaltung.\n- **Schnelle Website-Bereitstellung**: Durch die tiefe Integration des beliebten quelloffenen Website-Erstellungstools WordPress vereinfacht 1Panel den Prozess der Domainbindung und der SSL-Zertifikatskonfiguration, und alles kann mit nur einem Klick erreicht werden.\n- **App-Store**: 1Panel bietet eine Vielzahl hochwertiger quelloffener Tools und Anwendungen an und erleichtert die Installation und Aktualisierung für seine Benutzer.\n- **Sicherheit und Zuverlässigkeit**: Durch die Nutzung von Containerisierungstechnologie und sicheren Anwendungsbereitstellungspraktiken minimiert 1Panel die Angriffsfläche für Schwachstellen. Die Sicherheit wird durch integriertes Firewall-Management und Audit-Log-Funktionen weiter erhöht.\n- **Ein-Klick-Sicherung und -Wiederherstellung**: Der Datenschutz wird durch die Ein-Klick-Sicherungs- und -Wiederherstellungsfunktionen von 1Panel vereinfacht und unterstützt verschiedene Cloud-Speicherlösungen, um die Integrität und Verfügbarkeit der Daten zu gewährleisten.\n\n## Schneller Startleitfaden\n\nFühren Sie das folgende Skript aus und befolgen Sie die Anweisungen, um 1Panel zu installieren:\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nBitte verweisen Sie auf unsere [Dokumentation](https://docs.1panel.pro/quick_start/) für weitere Informationen.\n\n## Bildschirmfoto\n\n![Benutzeroberflächenanzeige](https://resource.1panel.pro/img/1panel.png)\n\n## Sternverlauf\n\n[![Sternverlaufsdiagramm](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Sicherheitsinformationen\n\nWenn Sie irgendwelche Sicherheitsprobleme entdecken, kontaktieren Sie uns bitte über: wanghe@fit2cloud.com.\n\n## Lizenz\n\nLizenziert unter der GNU General Public License Version 3 (GPLv3) (die „Lizenz“); Sie dürfen diese Datei nur in Übereinstimmung mit der Lizenz verwenden. Sie können eine Kopie der Lizenz unter \n\nhttps://www.gnu.org/licenses/gpl-3.0.html erhalten.\n\nSofern nicht durch anwendbares Recht vorgeschrieben oder schriftlich vereinbart, wird die unter der Lizenz verbreitete Software „AS IS“ ohne jegliche Arten von Garantien oder Bedingungen verteilt, sei es ausdrücklich oder stillschweigend. Siehe die Lizenz für die spezifische Sprache, die die Rechte und Einschränkungen unter der Lizenz regelt.\n"
  },
  {
    "path": "docs/README.es-es.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>La herramienta de gestión más valorada para administrar servidores Linux.</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>  \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel es un panel de control moderno y de código abierto basado en web para la gestión de servidores Linux.\n\n- **Gestión eficiente**: los usuarios pueden gestionar fácilmente servidores Linux a través de una interfaz web, como monitorización de hosts, gestión de archivos, gestión de bases de datos, gestión de contenedores, entre otros;  \n- **Creación rápida de sitios web**: integración profunda con el software de creación de sitios web de código abierto WordPress y Halo, operaciones como vinculación de dominios y configuración de certificados SSL se realizan con un solo clic;  \n- **Tienda de aplicaciones**: selección de diversas herramientas y software de código abierto de alta calidad, ayudando a los usuarios a instalar y actualizar fácilmente;  \n- **Seguro y confiable**: basado en la gestión de contenedores y el despliegue de aplicaciones, se logra la mínima exposición a vulnerabilidades, al tiempo que se ofrecen funciones como protección contra virus, cortafuegos y auditoría de registros;  \n- **Copia de seguridad con un clic**: soporta copias de seguridad y restauraciones con un clic, los usuarios pueden respaldar datos en diversos medios de almacenamiento en la nube, asegurando que nunca se pierdan.\n\n## Inicio Rápido\n\nEjecute el siguiente script y siga las indicaciones para instalar 1Panel:\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nPor favor, consulte nuestra [documentación](https://docs.1panel.pro/quick_start/) para más detalles.\n\n## Captura de Pantalla\n\n![UI Display](https://resource.1panel.pro/img/1panel.png)\n\n## Historial de Estrellas\n\n[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Información de Seguridad\n\nSi descubre algún problema de seguridad, contáctenos a través de: wanghe@fit2cloud.com.\n\n## Licencia\n\nLicenciado bajo la Licencia Pública General GNU versión 3 (GPLv3) (la \"Licencia\"); no puede usar este archivo excepto en cumplimiento con la Licencia. Puede obtener una copia de la Licencia en\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nA menos que lo exija la ley aplicable o se acuerde por escrito, el software distribuido bajo la Licencia se distribuye en una BASE \"TAL CUAL\", SIN GARANTÍAS O CONDICIONES DE NINGÚN TIPO, ya sean expresas o implícitas. Consulte la Licencia para el lenguaje específico que rige los permisos y limitaciones bajo la Licencia.\n"
  },
  {
    "path": "docs/README.fr.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>Outil de gestion de serveurs Linux basé sur le web le mieux noté</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>  \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel est un panneau de contrôle basé sur le web, moderne et open source, destiné à la gestion de serveurs Linux.\n\n- **Gestion efficace**: Grâce à une interface graphique web conviviale, 1Panel permet aux utilisateurs de gérer facilement leurs serveurs Linux. Ses principales fonctionnalités comprennent la surveillance des hôtes, la gestion des fichiers, l'administration des bases de données et la gestion des conteneurs.\n- **Déploiement rapide de sites web**: Avec une intégration approfondie du logiciel de création de sites Web open-source populaire WordPress, 1Panel simplifie le processus de liaison de domaine et de configuration du certificat SSL, tout cela pouvant être réalisé en un seul clic.\n- **Boutique d'applications**: 1Panel propose une variété d'outils et d'applications open-source de haute qualité, facilitant l'installation et la mise à jour pour les utilisateurs.\n- **Sécurité et fiabilité**: En tirant parti de la technologie de conteneurisation et des pratiques de déploiement d'applications sécurisées, 1Panel minimise l'exposition aux vulnérabilités. La sécurité est davantage renforcée via la gestion du pare-feu intégré et la capacité d'audit des journaux.\n- **Sauvegarde et restauration en un clic**: La protection des données est simplifiée grâce à la fonction de sauvegarde et de restauration en un clic de 1Panel, qui prend en charge diverses solutions de stockage cloud pour garantir l'intégrité et la disponibilité des données.\n\n## Guide de démarrage rapide\n\nExécutez le script ci-dessous et suivez les instructions pour installer 1Panel :\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nVeuillez vous référer à notre [documentation](https://docs.1panel.pro/quick_start/) pour plus d'informations.\n\n## Capture d'écran\n\n![UI Display](https://resource.1panel.pro/img/1panel.png)\n\n## Histoire des étoiles\n\n[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Informations de sécurité\n\nSi vous découvrez un quelconque problème de sécurité, veuillez nous contacter via : wanghe@fit2cloud.com.\n\n## Licence\n\nCe logiciel est sous licence GNU General Public License version 3 (GPLv3) (la « Licence ») ; vous ne pouvez utiliser ce fichier que conformément à la Licence. Vous pouvez obtenir une copie de la Licence à l'adresse suivante :\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nSauf si exigé par la loi en vigueur ou convenu par écrit, le logiciel distribué sous la Licence est distribué « tel quel », SANS GARANTIE D'AUCUNE SORTE, expresse ou implicite, et sans aucune condition. Consultez la Licence pour les termes spécifiques régissant les autorisations et les limitations prévues par la Licence.\n"
  },
  {
    "path": "docs/README.id.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>Alat Manajemen Server Linux Berbasis Web dengan Peringkat Teratas</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>  \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel adalah panel kontrol berbasis web yang modern dan open-source untuk manajemen server Linux.\n\n- **Manajemen Efisien**: Melalui antarmuka grafis web yang ramah pengguna, 1Panel memungkinkan pengguna mengelola server Linux mereka dengan mudah. Fitur utamanya mencakup pemantauan host, manajemen file, administrasi database, dan manajemen kontainer.\n- **Penyebaran Situs Web Cepat**: Dengan integrasi mendalam perangkat lunak pembuatan situs web open-source populer WordPress, 1Panel menyederhanakan proses pengikatan domain dan konfigurasi sertifikat SSL, semuanya dapat dicapai hanya dengan satu klik.\n- **Toko Aplikasi**: 1Panel menyediakan berbagai macam alat dan aplikasi open-source berkualitas tinggi, memudahkan instalasi dan pembaruan bagi penggunanya.\n- **Keamanan dan Keandalan**: Dengan memanfaatkan teknologi kontainerisasi dan praktik penyebaran aplikasi aman, 1Panel meminimalkan paparan kerentanan. Keamanan lebih ditingkatkan melalui manajemen firewall terintegrasi dan kemampuan audit log.\n- **Cadangan & Pulih Satu Klik**: Perlindungan data menjadi sederhana dengan fungsi cadangan dan pulih satu klik dari 1Panel, mendukung berbagai solusi penyimpanan cloud untuk memastikan integritas dan ketersediaan data.\n\n## Panduan Cepat Memulai\n\nJalankan skrip di bawah ini dan ikuti petunjuk untuk menginstal 1Panel:\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nSilakan merujuk ke [dokumentasi](https://docs.1panel.pro/quick_start/) kami untuk informasi lebih lanjut.\n\n## Tangkapan Layar\n\n![UI Display](https://resource.1panel.pro/img/1panel.png)\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Informasi Keamanan\n\nJika Anda menemukan masalah keamanan apa pun, silakan hubungi kami melalui: wanghe@fit2cloud.com.\n\n## License\n\nDilindungi di bawah Lisensi GNU General Public License versi 3 (GPLv3) (selanjutnya disebut “Lisensi”); Anda tidak diperbolehkan menggunakan file ini kecuali sesuai dengan ketentuan Lisensi. Anda dapat memperoleh salinan Lisensi di\n\nhttps://www.gnu.org/licenses/gpl-3.0.html\n\nKecuali diwajibkan oleh hukum yang berlaku atau disetujui secara tertulis, perangkat lunak yang didistribusikan berdasarkan Lisensi ini disediakan “APA ADANYA” TANPA JAMINAN APAPUN, baik tersurat maupun tersirat. Lihat Lisensi untuk informasi lebih lanjut mengenai hak dan batasan dalam Lisensi ini.\n"
  },
  {
    "path": "docs/README.ja.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>最高評価のWebベースのLinuxサーバー管理ツール</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>  \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel はモダンでオープンソースの Linux サーバー操作と管理パネルです:\n\n- **効率的管理**：ユーザーは、Web グラフィカルインターフェースを使用して、Linux サーバーを簡単に管理し、ホストの監視、ファイル管理、データベース管理、コンテナ管理などの機能を実現できます。\n- **迅速なウェブサイト構築**：オープンソースのウェブサイト構築ソフトウェアであるWordPressと[Halo](https://github.com/halo-dev/halo/)を深く統合し、ドメインのバインディング、SSL 証明書の設定などをワンクリックで完了します。\n- **アプリストア**：さまざまな高品質のオープンソースツールやアプリケーションソフトウェアを厳選して提供し、ユーザーが簡単にインストールおよびアップグレードできるよう支援します。\n- **安全性と信頼性**：コンテナの管理とアプリケーションのデプロイに基づいて、最小限の脆弱性露出面を実現し、同時にファイアウォールやログ監査などの機能を提供します。\n- **ワンクリックバックアップ**：ワンクリックでバックアップと復元をサポートし、ユーザーはデータをさまざまなクラウドストレージメディアにバックアップし、永久に失うことはありません。\n\n## クイックスタート\n\nワンクリックで 1Panel をインストールするには、次のコマンドを実行します:\n\n```sh\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\n## UI ディスプレイ\n\n![UI Display](https://resource.fit2cloud.com/1panel/img/overview_en.png)\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## セキュリティ情報\n\nセキュリティの問題を発見した場合は、以下の方法でご連絡ください：wanghe@fit2cloud.com。\n\n## License\n\nこのファイルは、GNU General Public License バージョン 3 (GPLv3)（以下「ライセンス」）の下でライセンスされています。ライセンスに従わない限り、このファイルを使用することはできません。ライセンスのコピーは以下で取得できます。\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\n適用法により要求される場合、または書面で同意される場合を除き、ライセンスの下で配布されるソフトウェアは「現状のまま（AS IS）」で提供され、いかなる種類の保証もありません。ライセンスにおける特定の権利および制限については、ライセンスを参照してください。\n"
  },
  {
    "path": "docs/README.ko.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>최고 평점의 웹 기반 리눅스 서버 관리 도구</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>    \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel은 리눅스 서버 관리를 위한 오픈 소스 현대 웹 기반 제어판입니다.\n\n- **효율적인 관리**: 사용자 친화적인 웹 그래픽 인터페이스를 통해 1Panel은 사용자가 리눅스 서버를 손쉽게 관리할 수 있도록 합니다. 주요 기능으로는 호스트 모니터링, 파일 관리, 데이터베이스 관리 및 컨테이너 관리가 포함됩니다.\n- **신속한 웹사이트 배포**: 인기 있는 오픈 소스 웹사이트 빌딩 소프트웨어인 WordPress와의 깊은 통합을 통해 1Panel은 도메인 바인딩 및 SSL 인증서 구성을 단 한 번의 클릭으로 간소화합니다.\n- **애플리케이션 스토어**: 1Panel은 다양한 고품질 오픈 소스 도구 및 애플리케이션을 선별하여 사용자가 쉽게 설치하고 업데이트할 수 있도록 합니다.\n- **보안 및 신뢰성**: 컨테이너화 및 안전한 애플리케이션 배포 관행을 활용하여 1Panel은 취약점 노출을 최소화합니다. 또한 통합 방화벽 관리 및 로그 감사 기능을 통해 보안을 강화합니다.\n- **원클릭 백업 및 복원**: 1Panel의 원클릭 백업 및 복원 기능을 통해 데이터 보호가 간편해지며, 다양한 클라우드 스토리지 솔루션을 지원하여 데이터 무결성과 가용성을 보장합니다.\n\n## 빠른 시작\n\n아래 스크립트를 실행하고 프롬프트에 따라 1Panel을 설치하세요:\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\n자세한 내용은 [문서](https://docs.1panel.pro/quick_start/)를 참조하세요.\n\n## 스크린샷\n\n![UI 디스플레이](https://resource.1panel.pro/img/1panel.png)\n\n## 스타 히스토리\n\n[![스타 히스토리 차트](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## 보안 정보\n\n보안 문제를 발견하신 경우, 다음을 통해 저희에게 연락해 주시기 바랍니다: wanghe@fit2cloud.com.\n\n## 라이센스\n\nGNU 일반 공공 라이센스 버전 3(GPLv3) (이하 \"라이센스\")에 따라 이 파일을 사용할 수 있으며, 라이센스를 준수하지 않는 한 이 파일을 사용할 수 없습니다. 라이센스의 사본은 다음에서 얻을 수 있습니다.\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\n적용 가능한 법률에 따라 요구되거나 서면으로 동의하지 않는 한, 라이센스에 따라 배포된 소프트웨어는 \"있는 그대로\" 제공되며, 명시적이거나 묵시적인 보증이나 조건이 없습니다. 라이센스에 따라 권한 및 제한 사항을 규정하는 특정 언어를 참조하십시오."
  },
  {
    "path": "docs/README.ms.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>Alat Pengurusan Pelayan Linux Berasaskan Web Terbaik</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel ialah panel kawalan moden sumber terbuka berasaskan web untuk pengurusan pelayan Linux.\n\n- **Pengurusan yang Cekap**: Melalui antara muka grafik web yang mesra pengguna, 1Panel membolehkan pengguna menguruskan pelayan Linux mereka dengan mudah. Ciri utama termasuk pemantauan hos, pengurusan fail, pentadbiran pangkalan data, dan pengurusan kontena.\n- **Penggunaan Laman Web yang Pantas**: Dengan integrasi mendalam perisian pembinaan laman web sumber terbuka popular seperti WordPress, 1Panel mempermudah proses pengikatan domain dan konfigurasi sijil SSL, semuanya boleh dicapai dengan hanya satu klik.\n- **Gedung Aplikasi**: 1Panel menyediakan pelbagai alat dan aplikasi sumber terbuka berkualiti tinggi, memudahkan pemasangan dan kemas kini untuk penggunanya.\n- **Keselamatan dan Kebolehpercayaan**: Dengan memanfaatkan penggunaan kontena dan amalan penyebaran aplikasi yang selamat, 1Panel meminimumkan pendedahan terhadap kerentanan. Ia turut meningkatkan keselamatan melalui pengurusan firewall yang terintegrasi dan keupayaan pengauditan log.\n- **Sandaran & Pemulihan Satu Klik**: Perlindungan data menjadi mudah dengan fungsi sandaran dan pemulihan satu klik 1Panel, yang menyokong pelbagai penyelesaian storan awan untuk memastikan integriti dan ketersediaan data.\n\n## Permulaan Pantas\n\nJalankan skrip di bawah dan ikuti arahan untuk memasang 1Panel:\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nSila rujuk [dokumentasi](https://docs.1panel.pro/quick_start/) untuk maklumat lanjut.\n\n## Tangkap Layar\n\n![UI Display](https://resource.1panel.pro/img/1panel.png)\n\n## Sejarah Bintang\n\n[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Maklumat Keselamatan\n\nJika anda menemui sebarang isu keselamatan, sila hubungi kami melalui: wanghe@fit2cloud.com.\n\n## Lesen\n\nDilesenkan di bawah Lesen Awam Am GNU versi 3 (GPLv3) (selepas ini disebut sebagai \"Lesen\"); anda tidak boleh menggunakan fail ini kecuali mematuhi terma-terma Lesen. Anda boleh mendapatkan salinan Lesen di:\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nKecuali dikehendaki oleh undang-undang yang berkuat kuasa atau dipersetujui secara bertulis, perisian yang diedarkan di bawah Lesen ini diedarkan \"SEBAGAIMANA ADANYA\", TANPA SEBARANG JAMINAN ATAU SYARAT, sama ada tersurat atau tersirat. Sila rujuk Lesen untuk bahasa khusus yang mengawal hak dan batasan di bawah Lesen ini.\n"
  },
  {
    "path": "docs/README.pt-br.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>Ferramenta de Gerenciamento de Servidores Linux Baseada na Web Mais Avaliada</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>  \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel é um painel de controle open-source, moderno e baseado na web para gerenciamento de servidores Linux.\n\n- **Gerenciamento Eficiente**: Por meio de uma interface gráfica web amigável, o 1Panel permite que os usuários gerenciem seus servidores Linux com facilidade. As principais funcionalidades incluem monitoramento de hosts, gerenciamento de arquivos, administração de bancos de dados e gerenciamento de containers.\n- **Implantação Rápida de Sites**: Com integração profunda com o popular software open-source WordPress, o 1Panel simplifica o processo de vinculação de domínios e configuração de certificados SSL, tudo realizável com um único clique.\n- **Loja de Aplicativos**: O 1Panel seleciona uma ampla gama de ferramentas e aplicativos open-source de alta qualidade, facilitando a instalação e atualização para seus usuários.\n- **Segurança e Confiabilidade**: Ao utilizar práticas de containerização e implantação segura de aplicativos, o 1Panel minimiza a exposição a vulnerabilidades. A segurança é ainda aprimorada por meio da gestão integrada de firewall e auditoria de logs.\n- **Backup e Restauração com um Clique**: A proteção de dados é simplificada com a funcionalidade de backup e restauração com um clique do 1Panel, que suporta várias soluções de armazenamento em nuvem para garantir a integridade e disponibilidade dos dados.\n\n## Início Rápido\n\nExecute o script abaixo e siga as instruções para instalar o 1Panel:\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nPor favor, consulte nossa [documentação](https://docs.1panel.pro/quick_start/) para mais detalhes.\n\n## Captura de Tela\n\n![Exibição da UI](https://resource.1panel.pro/img/1panel.png)\n\n## Histórico de Estrelas\n\n[![Gráfico de Histórico de Estrelas](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Informações de Segurança\n\nSe você descobrir algum problema de segurança, por favor, entre em contato conosco através do e-mail: wanghe@fit2cloud.com.\n\n## Licença\n\nLicenciado sob a Licença Pública Geral GNU versão 3 (GPLv3) (a \"Licença\"); você não pode usar este arquivo exceto em conformidade com a Licença. Você pode obter uma cópia da Licença em\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nExceto quando exigido por lei aplicável ou acordado por escrito, o software distribuído sob a Licença é distribuído \"COMO ESTÁ\", SEM GARANTIAS OU CONDIÇÕES DE QUALQUER TIPO, expressas ou implícitas. Consulte a Licença para ver as permissões e limitações específicas sob a Licença.\n"
  },
  {
    "path": "docs/README.ru.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>Высококлассная веб-панель управления Linux-серверами</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/CVD7jHkrWn\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel - это современная веб-панель управления Linux-серверами с открытым исходным кодом.\n\n- **Эффективное управление**: Благодаря удобному веб-интерфейсу, 1Panel позволяет пользователям легко управлять своими Linux-серверами. Основные функции включают мониторинг хоста, управление файлами, администрирование баз данных и управление контейнерами.\n- **Быстрое развертывание сайтов**: Благодаря глубокой интеграции с популярной системой создания сайтов WordPress, 1Panel упрощает процесс привязки домена и настройки SSL-сертификатов, все это достигается одним кликом.\n- **Магазин приложений**: 1Panel предлагает широкий выбор качественных инструментов и приложений с открытым исходным кодом, обеспечивая простую установку и обновление для пользователей.\n- **Безопасность и надежность**: Используя контейнеризацию и безопасные практики развертывания приложений, 1Panel минимизирует уязвимости. Безопасность дополнительно усиливается благодаря встроенному управлению межсетевым экраном и возможностям аудита логов.\n- **Резервное копирование и восстановление в один клик**: Защита данных упрощается благодаря функции резервного копирования и восстановления в один клик, с поддержкой различных облачных хранилищ для обеспечения целостности и доступности данных.\n\n## Быстрый старт\n\nВыполните скрипт ниже и следуйте инструкциям для установки 1Panel:\n\n```bash\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nДля получения дополнительной информации обратитесь к нашей [документации](https://docs.1panel.pro/quick_start/).\n\n## Скриншот\n\n![Интерфейс](https://resource.1panel.pro/img/1panel.png)\n\n## История звезд\n\n[![График истории звезд](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Информация о безопасности\n\nЕсли вы обнаружите проблемы безопасности, пожалуйста, свяжитесь с нами по адресу: wanghe@fit2cloud.com.\n\n## Лицензия\n\nЛицензировано под GNU General Public License версии 3 (GPLv3) (\"Лицензия\"); вы не можете использовать этот файл, кроме как в соответствии с Лицензией. Вы можете получить копию Лицензии по адресу\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nЕсли иное не предусмотрено применимым законодательством или не согласовано в письменной форме, программное обеспечение, распространяемое по Лицензии, распространяется на условиях \"КАК ЕСТЬ\", БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ ИЛИ УСЛОВИЙ, явных или подразумеваемых. См. текст Лицензии для получения информации о разрешениях и ограничениях по Лицензии.\n"
  },
  {
    "path": "docs/README.tr.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.pro\"><img src=\"https://resource.1panel.pro/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>En Yüksek Puanlı Web Tabanlı Linux Sunucu Yönetim Aracı</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel\" target=\"_blank\"><img src=\"https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light\" alt=\"1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://discord.gg/bUpUqWqdRr\" target=\"_blank\">\n        <img src=\"https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb\"\n            alt=\"chat on Discord\"></a>  \n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n------------------------------\n\n1Panel, Linux sunucu yönetimi için modern, açık kaynaklı bir web tabanlı kontrol panelidir.\n\n- **Verimli Yönetim**: Kullanıcı dostu bir web grafik arayüzü aracılığıyla, 1Panel kullanıcıların Linux sunucularını zahmetsizce yönetmelerini sağlar. Ana özellikler arasında ana bilgisayar izleme, dosya yönetimi, veritabanı yönetimi ve konteyner yönetimi yer alır.\n- **Hızlı Web Sitesi Dağıtımı**: Popüler açık kaynaklı web sitesi oluşturma yazılımı WordPress ile derin entegrasyon sayesinde, 1Panel alan adı bağlama ve SSL sertifikası yapılandırma işlemlerini basitleştirir, hepsi sadece bir tıkla yapılabilir.\n- **Uygulama Mağazası**: 1Panel, yüksek kaliteli açık kaynaklı araçlar ve uygulamalar sunar, kullanıcıların kolayca kurulum yapmalarını ve güncellemelerini sağlar.\n- **Güvenlik ve Güvenilirlik**: Konteynerleştirme ve güvenli uygulama dağıtımı yöntemlerini kullanarak 1Panel, güvenlik açıklarını en aza indirir. Ayrıca, entegre güvenlik duvarı yönetimi ve günlük denetleme özellikleri ile güvenliği artırır.\n- **Tek Tıkla Yedekleme ve Geri Yükleme**: 1Panel’in tek tıkla yedekleme ve geri yükleme işlevi, veri korumasını basitleştirir ve çeşitli bulut depolama çözümleriyle veri bütünlüğü ve erişilebilirliğini sağlar.\n\n## Hızlı Başlangıç\n\n**Tek Tuşlu Yükleme**\n\nAşağıdaki komutu çalıştırın ve 1Panel’i kurmak için yönergeleri takip edin:\n\n```sh\ncurl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh\n```\n\nDaha fazla detay için [dokümantasyonumuza](https://docs.1panel.pro/quick_start/) göz atın.\n\n## Ekran Görüntüsü\n\n![UI Display](https://resource.1panel.pro/img/1panel.png)\n\n## Yıldız Geçmişi\n\n[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)\n\n## Güvenlik Bilgileri\n\nHerhangi bir güvenlik sorunu keşfettiğinizde, lütfen bizimle şu adres üzerinden iletişime geçin: wanghe@fit2cloud.com.\n\n## Lisans\n\nBu yazılım, GNU Genel Kamu Lisansı sürüm 3 (GPLv3) ile lisanslanmıştır (bu \"Lisans\" olarak anılacaktır); bu dosyayı yalnızca Lisans ile uyumlu olarak kullanabilirsiniz. Lisansı şu adresten edinebilirsiniz:\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nUygulanan yasalar gereği veya yazılı olarak kabul edilmedikçe, Lisans altında dağıtılan yazılım, HERHANGİ BİR GARANTİ VEYA ŞART olmaksızın \"OLDUĞU GİBİ\" sunulmaktadır. Lisans, izinler ve sınırlamalar ile ilgili özel dil için Lisans’a bakın."
  },
  {
    "path": "docs/README.zh-Hans.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.cn\"><img src=\"http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>Top-Rated Web-based Linux Server Management Tool</b></p>\n<p align=\"center\"><b>现代化、开源的 Linux 服务器运维管理面板</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://hellogithub.com/repository/71791baf930149ac9b84e1acf186573f\" target=\"_blank\"><img src=\"https://api.hellogithub.com/v1/widgets/recommend.svg?rid=71791baf930149ac9b84e1acf186573f&claim_uid=p8vB3kP5CMrthiL&theme=dark&theme=neutral\" alt=\"Featured｜HelloGitHub\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"GitHub Stars\"></a>\n  <a href=\"https://gitee.com/fit2cloud-feizhiyun/1Panel\"><img src=\"https://gitee.com/fit2cloud-feizhiyun/1Panel/badge/star.svg?theme=gvp\" alt=\"Gitee Stars\"></a>\n  <a href=\"https://gitcode.com/feizhiyun/1Panel\"><img src=\"https://gitcode.com/feizhiyun/1Panel/star/badge.svg\" alt=\"GitCode Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(简体)\" src=\"https://img.shields.io/badge/中文(简体)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n[![Watch the video](https://resource.fit2cloud.com/1panel/img/overview_video.png)](https://www.bilibili.com/video/BV1Mt421n7LZ/)\n\n------------------------------\n## 什么是 1Panel？\n\n1Panel 是新一代的 Linux 服务器运维管理面板。\n\n- **高效管理**：用户可以通过 Web 界面轻松管理 Linux 服务器，如主机监控、文件管理、数据库管理、容器管理等；\n- **快速建站**：深度集成开源建站软件 WordPress 和 [Halo](https://github.com/halo-dev/halo/)，域名绑定、SSL 证书配置等操作一键搞定；\n- **应用商店**：精选上架各类高质量的开源工具和应用软件，协助用户轻松安装并升级；\n- **安全可靠**：基于容器管理并部署应用，实现最小的漏洞暴露面，同时提供病毒防护、防火墙和日志审计等功能；\n- **一键备份**：支持一键备份和恢复，用户可以将数据备份到各类云端存储介质，永不丢失。\n\n## 快速开始\n\n**一键安装**\n\n执行如下命令一键安装 1Panel:\n\n```sh\ncurl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh\n```\n\n如果是用于离线环境，推荐使用 [安装包方式](https://1panel.cn/docs/installation/package_installation/) 进行安装部署。\n\n**学习资料**\n\n- [在线文档](https://1panel.cn/docs/)\n- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)\n- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)\n\n## 专业版\n\n相比于社区版，1Panel 专业版为用户提供了大量增强功能及技术支持服务，增强功能包括 WAF 增强、网站防篡改、网站监控、GPU 监控、黑金主题及自定义界面等。 [点击查看专业版详细介绍](https://www.lxware.cn/1panel)。\n\n## 飞致云的其他明星项目\n\n- [MaxKB](https://github.com/1Panel-dev/MaxKB/) - 基于 LLM 大语言模型的开源知识库问答系统\n- [JumpServer](https://github.com/jumpserver/jumpserver/) - 广受欢迎的开源堡垒机\n- [Halo](https://github.com/halo-dev/halo/) - 强大易用的开源建站工具\n- [DataEase](https://github.com/dataease/dataease/) - 人人可用的开源数据可视化分析工具\n- [MeterSphere](https://github.com/metersphere/metersphere/) - 开源持续测试工具\n- [KubePi](https://github.com/1Panel-dev/KubePi/) - 现代化、开源的 K8s 面板\n\n## License\n\nCopyright (c) 2014-2026 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.\n\nLicensed under The GNU General Public License version 3 (GPLv3)  (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n"
  },
  {
    "path": "docs/README.zh-Hant.md",
    "content": "<p align=\"center\"><a href=\"https://1panel.cn\"><img src=\"http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png\" alt=\"1Panel\" width=\"300\" /></a></p>\n<p align=\"center\"><b>Top-Rated Web-based Linux Server Management Tool</b></p>\n<p align=\"center\"><b>現代化、開源的 Linux 伺服器運維管理面板</b></p>\n<p align=\"center\">\n  <a href=\"https://trendshift.io/repositories/2462\" target=\"_blank\"><img src=\"https://trendshift.io/api/badge/repositories/2462\" alt=\"1Panel-dev%2F1Panel | Trendshift\" style=\"width: 180px; height: auto;\" /></a>\n  <a href=\"https://hellogithub.com/repository/71791baf930149ac9b84e1acf186573f\" target=\"_blank\"><img src=\"https://api.hellogithub.com/v1/widgets/recommend.svg?rid=71791baf930149ac9b84e1acf186573f&claim_uid=p8vB3kP5CMrthiL&theme=dark&theme=neutral\" alt=\"Featured｜HelloGitHub\" style=\"width: 180px; height: auto;\" /></a>\n</p>\n<p align=\"center\">\n  <a href=\"https://www.gnu.org/licenses/gpl-3.0.html\"><img src=\"https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF\" alt=\"License: GPL v3\"></a>\n  <a href=\"https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard\"><img src=\"https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef\" alt=\"Codacy\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel/releases\"><img src=\"https://img.shields.io/github/v/release/1Panel-dev/1Panel\" alt=\"GitHub release\"></a>\n  <a href=\"https://github.com/1Panel-dev/1Panel\"><img src=\"https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square\" alt=\"GitHub Stars\"></a>\n  <a href=\"https://gitee.com/fit2cloud-feizhiyun/1Panel\"><img src=\"https://gitee.com/fit2cloud-feizhiyun/1Panel/badge/star.svg?theme=gvp\" alt=\"Gitee Stars\"></a>\n  <a href=\"https://gitcode.com/feizhiyun/1Panel\"><img src=\"https://gitcode.com/feizhiyun/1Panel/star/badge.svg\" alt=\"GitCode Stars\"></a><br>\n</p>\n<p align=\"center\">\n  <a href=\"/README.md\"><img alt=\"English\" src=\"https://img.shields.io/badge/English-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hans.md\"><img alt=\"中文(簡體)\" src=\"https://img.shields.io/badge/中文(簡體)-d9d9d9\"></a>\n  <a href=\"/docs/README.ja.md\"><img alt=\"日本語\" src=\"https://img.shields.io/badge/日本語-d9d9d9\"></a>\n  <a href=\"/docs/README.pt-br.md\"><img alt=\"Português (Brasil)\" src=\"https://img.shields.io/badge/Português (Brasil)-d9d9d9\"></a>\n  <a href=\"/docs/README.ar.md\"><img alt=\"العربية\" src=\"https://img.shields.io/badge/العربية-d9d9d9\"></a><br>\n  <a href=\"/docs/README.de.md\"><img alt=\"Deutsch\" src=\"https://img.shields.io/badge/Deutsch-d9d9d9\"></a>\n  <a href=\"/docs/README.es.md\"><img alt=\"Español\" src=\"https://img.shields.io/badge/Español-d9d9d9\"></a>\n  <a href=\"/docs/README.fr.md\"><img alt=\"français\" src=\"https://img.shields.io/badge/français-d9d9d9\"></a>\n  <a href=\"/docs/README.ko.md\"><img alt=\"한국어\" src=\"https://img.shields.io/badge/한국어-d9d9d9\"></a>\n  <a href=\"/docs/README.id.md\"><img alt=\"Bahasa Indonesia\" src=\"https://img.shields.io/badge/Bahasa Indonesia-d9d9d9\"></a>\n  <a href=\"/docs/README.zh-Hant.md\"><img alt=\"中文(繁體)\" src=\"https://img.shields.io/badge/中文(繁體)-d9d9d9\"></a>\n  <a href=\"/docs/README.tr.md\"><img alt=\"Türkçe\" src=\"https://img.shields.io/badge/Türkçe-d9d9d9\"></a>\n  <a href=\"/docs/README.ru.md\"><img alt=\"Русский\" src=\"https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9\"></a>\n  <a href=\"/docs/README.ms.md\"><img alt=\"Bahasa Melayu\" src=\"https://img.shields.io/badge/Bahasa Melayu-d9d9d9\"></a>\n</p>\n\n[![Watch the video](https://resource.fit2cloud.com/1panel/img/overview_video.png)](https://www.bilibili.com/video/BV1Mt421n7LZ/)\n\n------------------------------\n## 什麼是 1Panel？\n\n1Panel 是新一代的 Linux 伺服器運維管理面板。\n\n- **高效管理**：使用者可以透過 Web 介面輕鬆管理 Linux 伺服器，如主機監控、文件管理、資料庫管理、容器管理等；\n- **快速建站**：深度整合開源建站軟體 WordPress 和 [Halo](https://github.com/halo-dev/halo/)，域名綁定、SSL 證書配置等操作一鍵搞定；\n- **應用商店**：精選上架各類高品質的開源工具和應用軟體，協助使用者輕鬆安裝並升級；\n- **安全可靠**：基於容器管理並部署應用，實現最小的漏洞暴露面，同時提供病毒防護、防火牆和日誌審計等功能；\n- **一鍵備份**：支援一鍵備份和復原，使用者可以將資料備份到各類雲端儲存介質，永不遺失。\n\n## 快速開始\n\n**一鍵安裝**\n\n執行如下指令一鍵安裝 1Panel:\n\n```sh\ncurl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh\n```\n\n如果是用於離線環境，推薦使用 [安裝包方式](https://1panel.cn/docs/installation/package_installation/) 進行安裝部署。\n\n**學習資料**\n\n- [線上文件](https://1panel.cn/docs/)\n- [社區論壇](https://bbs.fit2cloud.com/c/1p/7)\n- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)\n\n## 專業版\n\n相比於社區版，1Panel 專業版為使用者提供了大量增強功能及技術支援服務，增強功能包括 WAF 增強、網站防篡改、網站監控、GPU 監控、黑金主題及自訂介面等。 [點擊查看專業版詳細介紹](https://www.lxware.cn/1panel)。\n\n## 飛致雲的其他明星項目\n\n- [MaxKB](https://github.com/1Panel-dev/MaxKB/) - 基於 LLM 大語言模型的開源知識庫問答系統\n- [JumpServer](https://github.com/jumpserver/jumpserver/) - 廣受歡迎的開源堡壘機\n- [Halo](https://github.com/halo-dev/halo/) - 強大易用的開源建站工具\n- [DataEase](https://github.com/dataease/dataease/) - 人人可用的開源資料可視化分析工具\n- [MeterSphere](https://github.com/metersphere/metersphere/) - 開源持續測試工具\n- [KubePi](https://github.com/1Panel-dev/KubePi/) - 現代化、開源的 K8s 面板\n\n## License\n\nCopyright (c) 2014-2026 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.\n\nLicensed under The GNU General Public License version 3 (GPLv3)  (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n\n<https://www.gnu.org/licenses/gpl-3.0.html>\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n"
  },
  {
    "path": "frontend/.editorconfig",
    "content": "# @see: http://editorconfig.org\n\nroot = true\n\n# 表示所有文件适用\n[*]\n# 设置文件字符集为 utf-8\ncharset = utf-8\n# 控制换行类型(lf | cr | crlf)\nend_of_line = lf\n# 始终在文件末尾插入一个新行\ninsert_final_newline = true\n# 缩进风格（tab | space）\nindent_style = space\n# 缩进大小\nindent_size = 4\n# 最大行长度\nmax_line_length = 120\n\n# 表示仅 md 文件适用以下规则\n[*.md]\n# 关闭最大行长度限制\nmax_line_length = off\n# 关闭末尾空格修剪\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": "frontend/.eslintignore",
    "content": "*.sh\nnode_modules\n*.md\n*.woff\n*.ttf\n.vscode\n.idea\ndist\n/public\n/docs\n.husky\n.local\n/bin\n.eslintrc.js\n.prettierrc.js\n/src/mock/*\n/src/assets/*\n\n"
  },
  {
    "path": "frontend/.eslintrc.js",
    "content": "// @see: http://eslint.cn\n\nmodule.exports = {\n    root: true,\n    env: {\n        browser: true,\n        node: true,\n        es6: true,\n    },\n    /* 指定如何解析语法 */\n    parser: 'vue-eslint-parser',\n    /* 优先级低于 parse 的语法解析配置 */\n    parserOptions: {\n        parser: '@typescript-eslint/parser',\n        ecmaVersion: 2020,\n        sourceType: 'module',\n        jsxPragma: 'React',\n        ecmaFeatures: {\n            jsx: true,\n        },\n    },\n    /* 继承某些已有的规则 */\n    extends: [\n        'plugin:vue/vue3-recommended',\n        'plugin:@typescript-eslint/recommended',\n        'prettier',\n        'plugin:prettier/recommended',\n    ],\n    /*\n     * \"off\" 或 0    ==>  关闭规则\n     * \"warn\" 或 1   ==>  打开的规则作为警告（不影响代码执行）\n     * \"error\" 或 2  ==>  规则作为一个错误（代码不能执行，界面报错）\n     */\n    rules: {\n        // eslint (http://eslint.cn/docs/rules)\n        'no-var': 'error', // 要求使用 let 或 const 而不是 var\n        'no-multiple-empty-lines': ['error', { max: 1 }], // 不允许多个空行\n        'no-use-before-define': 'off', // 禁止在 函数/类/变量 定义之前使用它们\n        'prefer-const': 'off', // 此规则旨在标记使用 let 关键字声明但在初始分配后从未重新分配的变量，要求使用 const\n        'no-irregular-whitespace': 'off', // 禁止不规则的空白\n\n        // typeScript (https://typescript-eslint.io/rules)\n        '@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量\n        '@typescript-eslint/no-inferrable-types': 'off', // 可以轻松推断的显式类型可能会增加不必要的冗长\n        '@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。\n        '@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型\n        '@typescript-eslint/ban-ts-ignore': 'off', // 禁止使用 @ts-ignore\n        '@typescript-eslint/ban-types': 'off', // 禁止使用特定类型\n        '@typescript-eslint/explicit-function-return-type': 'off', // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明\n        '@typescript-eslint/no-var-requires': 'off', // 不允许在 import 语句中使用 require 语句\n        '@typescript-eslint/no-empty-function': 'off', // 禁止空函数\n        '@typescript-eslint/no-use-before-define': 'off', // 禁止在变量定义之前使用它们\n        '@typescript-eslint/ban-ts-comment': 'off', // 禁止 @ts-<directive> 使用注释或要求在指令后进行描述\n        '@typescript-eslint/no-non-null-assertion': 'off', // 不允许使用后缀运算符的非空断言(!)\n        '@typescript-eslint/explicit-module-boundary-types': 'off', // 要求导出函数和类的公共类方法的显式返回和参数类型\n\n        // vue (https://eslint.vuejs.org/rules)\n        'vue/no-v-html': 'off', // 禁止使用 v-html\n        'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用，此规则仅在启用该no-unused-vars规则时有效。\n        'vue/v-slot-style': 'error', // 强制执行 v-slot 指令样式\n        'vue/no-mutating-props': 'off', // 不允许组件 prop的改变（明天找原因）\n        'vue/custom-event-name-casing': 'off', // 为自定义事件名称强制使用特定大小写\n        'vue/attributes-order': 'off', // vue api使用顺序，强制执行属性顺序\n        'vue/one-component-per-file': 'off', // 强制每个组件都应该在自己的文件中\n        'vue/html-closing-bracket-newline': 'off', // 在标签的右括号之前要求或禁止换行\n        'vue/max-attributes-per-line': 'off', // 强制每行的最大属性数\n        'vue/multiline-html-element-content-newline': 'off', // 在多行元素的内容之前和之后需要换行符\n        'vue/singleline-html-element-content-newline': 'off', // 在单行元素的内容之前和之后需要换行符\n        'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式\n        'vue/require-default-prop': 'off', // 此规则要求为每个 prop 为必填时，必须提供默认值\n        'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词\n    },\n};\n"
  },
  {
    "path": "frontend/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\npnpm-lock.yaml\npackage-lock.json\n\nnode_modules\ndist\ndist-ssr\n*.local\nstats.html\n\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n!.vscode/settings.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n.vscode\nfolder-alias.json\n"
  },
  {
    "path": "frontend/.prettierignore",
    "content": "/dist/*\n.local\n/node_modules/**\n\n**/*.svg\n**/*.sh\n\n/public/*\n/src/assets/iconfont/**"
  },
  {
    "path": "frontend/.prettierrc.js",
    "content": "module.exports = {\n    // 一行最多 120 字符\n    printWidth: 120,\n    // 使用 4 个空格缩进\n    tabWidth: 4,\n    // 不使用 tab 缩进，而使用空格\n    useTabs: false,\n    // 行尾需要有分号\n    semi: true,\n    // 使用单引号代替双引号\n    singleQuote: true,\n    // 对象的 key 仅在必要时用引号\n    quoteProps: 'as-needed',\n    // jsx 不使用单引号，而使用双引号\n    jsxSingleQuote: false,\n    // 末尾使用逗号\n    trailingComma: 'all',\n    // 大括号内的首尾需要空格 { foo: bar }\n    bracketSpacing: true,\n    // 箭头函数，只有一个参数的时候，也需要括号\n    arrowParens: 'always',\n    // 每个文件格式化的范围是文件的全部内容\n    rangeStart: 0,\n    rangeEnd: Infinity,\n    // 不需要写文件开头的 @prettier\n    requirePragma: false,\n    // 不需要自动在文件开头插入 @prettier\n    insertPragma: false,\n    // 使用默认的折行标准\n    proseWrap: 'never',\n    // 根据显示样式决定 html 要不要折行\n    htmlWhitespaceSensitivity: 'ignore',\n    // 换行符使用 lf\n    endOfLine: 'auto',\n};\n"
  },
  {
    "path": "frontend/index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\" />\n        <meta name=\"robots\" content=\"noindex,nofollow\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1\" />\n        <title>loading...</title>\n    </head>\n    <body>\n        <div id=\"app\">\n            <style>\n                html,\n                body,\n                #app {\n                    width: 100%;\n                    height: 100%;\n                    padding: 0;\n                    margin: 0;\n                }\n                .first-loading-wrap {\n                    display: flex;\n                    flex-direction: column;\n                    align-items: center;\n                    justify-content: center;\n                    width: 100%;\n                    height: 100%;\n                }\n                .first-loading-wrap > h1 {\n                    font-size: 128px;\n                }\n                .first-loading-wrap .loading-wrap {\n                    display: flex;\n                    align-items: center;\n                    justify-content: center;\n                    padding: 98px;\n                }\n                .dot {\n                    position: relative;\n                    box-sizing: border-box;\n                    display: inline-block;\n                    width: 32px;\n                    height: 32px;\n                    font-size: 32px;\n                    transform: rotate(45deg);\n                    animation: ant-rotate 1.2s infinite linear;\n                }\n                .dot i {\n                    position: absolute;\n                    display: block;\n                    width: 14px;\n                    height: 14px;\n                    background-color: #1890ff;\n                    border-radius: 100%;\n                    opacity: 0.3;\n                    transform: scale(0.75);\n                    transform-origin: 50% 50%;\n                    animation: ant-spin-move 1s infinite linear alternate;\n                }\n                .dot i:nth-child(1) {\n                    top: 0;\n                    left: 0;\n                }\n                .dot i:nth-child(2) {\n                    top: 0;\n                    right: 0;\n                    animation-delay: 0.4s;\n                }\n                .dot i:nth-child(3) {\n                    right: 0;\n                    bottom: 0;\n                    animation-delay: 0.8s;\n                }\n                .dot i:nth-child(4) {\n                    bottom: 0;\n                    left: 0;\n                    animation-delay: 1.2s;\n                }\n                @keyframes ant-rotate {\n                    to {\n                        transform: rotate(405deg);\n                    }\n                }\n                @keyframes ant-spin-move {\n                    to {\n                        opacity: 1;\n                    }\n                }\n            </style>\n            <div class=\"first-loading-wrap\">\n                <div class=\"loading-wrap\">\n                    <span class=\"dot dot-spin\">\n                        <i></i>\n                        <i></i>\n                        <i></i>\n                        <i></i>\n                    </span>\n                </div>\n            </div>\n        </div>\n        <script type=\"module\" src=\"/src/main.ts\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "frontend/lint-staged.config.js",
    "content": "module.exports = {\n    '*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],\n    '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'],\n    'package.json': ['prettier --write'],\n    '*.vue': ['eslint --fix', 'prettier --write'],\n    '*.{scss,less,styl,html}': ['prettier --write'],\n    '*.md': ['prettier --write'],\n};\n"
  },
  {
    "path": "frontend/package.json",
    "content": "{\n    \"name\": \"1panel-frontend\",\n    \"private\": true,\n    \"version\": \"2.0\",\n    \"description\": \"1Panel 前端\",\n    \"scripts\": {\n        \"dev\": \"node --max-old-space-size=8192 ./node_modules/.bin/vite --host\",\n        \"serve\": \"vite\",\n        \"build:dev\": \"vite build --mode development\",\n        \"build:test\": \"vue-tsc --noEmit && vite build --mode test\",\n        \"build:pro\": \"vite build --mode production\",\n        \"preview\": \"vite preview\",\n        \"lint:eslint\": \"eslint --fix --ext .js,.ts,.vue ./src\",\n        \"lint:prettier\": \"prettier --write --loglevel warn \\\"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\\\"\",\n        \"lint:lint-staged\": \"lint-staged\",\n        \"release\": \"standard-version\",\n        \"commit\": \"git pull && git add -A && git-cz && git push\",\n        \"prettier:comment\": \"自动格式化当前目录下的所有文件\",\n        \"prettier\": \"prettier --write .\"\n    },\n    \"dependencies\": {\n        \"@codemirror/lang-html\": \"^6.4.10\",\n        \"@codemirror/lang-json\": \"^6.0.2\",\n        \"@codemirror/lang-php\": \"^6.0.2\",\n        \"@codemirror/language\": \"^6.11.3\",\n        \"@codemirror/legacy-modes\": \"^6.5.1\",\n        \"@codemirror/theme-one-dark\": \"^6.1.3\",\n        \"@element-plus/icons-vue\": \"^1.1.4\",\n        \"@vue-office/docx\": \"^1.6.2\",\n        \"@vue-office/excel\": \"^1.7.8\",\n        \"@vueuse/core\": \"^8.9.4\",\n        \"@xterm/addon-fit\": \"^0.10.0\",\n        \"@xterm/xterm\": \"^5.5.0\",\n        \"anser\": \"^2.3.2\",\n        \"axios\": \"^1.7.2\",\n        \"codemirror\": \"^6.0.2\",\n        \"crypto-js\": \"^4.2.0\",\n        \"dompurify\": \"^3.3.1\",\n        \"echarts\": \"^5.5.0\",\n        \"element-plus\": \"2.11.9\",\n        \"fit2cloud-ui-plus\": \"^1.2.4\",\n        \"highlight.js\": \"^11.9.0\",\n        \"js-base64\": \"^3.7.7\",\n        \"jsencrypt\": \"^3.3.2\",\n        \"md-editor-v3\": \"^2.11.3\",\n        \"monaco-editor\": \"^0.53.0\",\n        \"nprogress\": \"^0.2.0\",\n        \"pinia\": \"^2.1.7\",\n        \"pinia-plugin-persistedstate\": \"^1.6.1\",\n        \"punycode\": \"^2.3.1\",\n        \"qs\": \"^6.12.1\",\n        \"screenfull\": \"^6.0.2\",\n        \"sortablejs\": \"^1.15.7\",\n        \"uuid\": \"^10.0.0\",\n        \"vue\": \"^3.4.27\",\n        \"vue-clipboard3\": \"^2.0.0\",\n        \"vue-codemirror\": \"^6.1.1\",\n        \"vue-demi\": \"^0.14.6\",\n        \"vue-i18n\": \"^10.0.5\",\n        \"vue-router\": \"^4.5.1\"\n    },\n    \"devDependencies\": {\n        \"@types/node\": \"^20.14.8\",\n        \"@types/uuid\": \"^10.0.0\",\n        \"@typescript-eslint/eslint-plugin\": \"^5.62.0\",\n        \"@typescript-eslint/parser\": \"^5.22.0\",\n        \"@vitejs/plugin-vue\": \"^6.0.1\",\n        \"@vitejs/plugin-vue-jsx\": \"^5.1.1\",\n        \"autoprefixer\": \"^10.4.7\",\n        \"commitizen\": \"^4.2.4\",\n        \"eslint\": \"^8.57.0\",\n        \"eslint-config-prettier\": \"^8.10.0\",\n        \"eslint-plugin-prettier\": \"^4.2.1\",\n        \"eslint-plugin-vue\": \"^8.7.1\",\n        \"lint-staged\": \"^12.4.2\",\n        \"postcss\": \"^8.4.31\",\n        \"postcss-html\": \"^1.4.1\",\n        \"prettier\": \"^2.6.2\",\n        \"rollup-plugin-visualizer\": \"^5.5.4\",\n        \"sass\": \"^1.83.0\",\n        \"standard-version\": \"^9.5.0\",\n        \"tailwindcss\": \"^3.4.1\",\n        \"typescript\": \"^4.5.4\",\n        \"unplugin-auto-import\": \"^0.16.4\",\n        \"unplugin-vue-components\": \"^0.25.0\",\n        \"unplugin-vue-define-options\": \"^3.0.0-beta.23\",\n        \"vite\": \"^7.1.5\",\n        \"vite-plugin-compression\": \"^0.5.1\",\n        \"vite-plugin-eslint\": \"^1.8.1\",\n        \"vite-plugin-html\": \"^3.2.2\",\n        \"vite-plugin-monaco-editor\": \"^1.1.0\",\n        \"vite-plugin-vue-setup-extend\": \"^0.4.0\",\n        \"vite-svg-loader\": \"^5.1.0\",\n        \"vue-tsc\": \"^0.29.8\"\n    },\n    \"trustedDependencies\": [\n        \"@parcel/watcher\",\n        \"esbuild\",\n        \"@vue-office/docx\",\n        \"@vue-office/excel\"\n    ],\n    \"config\": {\n        \"commitizen\": {\n            \"path\": \"node_modules/cz-git\"\n        }\n    }\n}\n"
  },
  {
    "path": "frontend/postcss.config.cjs",
    "content": "module.exports = {\n    plugins: {\n        tailwindcss: {},\n        autoprefixer: {},\n    },\n};\n"
  },
  {
    "path": "frontend/src/App.vue",
    "content": "<template>\n    <el-config-provider :locale=\"i18nLocale\" :button=\"config\" size=\"default\">\n        <router-view v-if=\"isRouterAlive\" />\n    </el-config-provider>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, computed, ref, nextTick, provide } from 'vue';\nimport { GlobalStore } from '@/store';\nimport zhCn from 'element-plus/es/locale/lang/zh-cn';\nimport zhTw from 'element-plus/es/locale/lang/zh-tw';\nimport en from 'element-plus/es/locale/lang/en';\nimport ja from 'element-plus/es/locale/lang/ja';\nimport ms from 'element-plus/es/locale/lang/ms';\nimport ptBR from 'element-plus/es/locale/lang/pt-br';\nimport ru from 'element-plus/es/locale/lang/ru';\nimport ko from 'element-plus/es/locale/lang/ko';\nimport tr from 'element-plus/es/locale/lang/tr';\nimport esES from 'element-plus/es/locale/lang/es';\nimport { useTheme } from '@/global/use-theme';\nuseTheme();\n\nconst globalStore = GlobalStore();\nconst config = reactive({\n    autoInsertSpace: false,\n});\n\nconst i18nLocale = computed(() => {\n    if (globalStore.language === 'zh') return zhCn;\n    if (globalStore.language === 'zh-Hant') return zhTw;\n    if (globalStore.language === 'en') return en;\n    if (globalStore.language === 'ja') return ja;\n    if (globalStore.language === 'ms') return ms;\n    if (globalStore.language === 'ru') return ru;\n    if (globalStore.language === 'pt-br') return ptBR;\n    if (globalStore.language === 'ko') return ko;\n    if (globalStore.language === 'tr') return tr;\n    if (globalStore.language === 'es-ES') return esES;\n    return zhCn;\n});\n\nconst isRouterAlive = ref(true);\n\nconst reload = () => {\n    isRouterAlive.value = false;\n    nextTick(() => {\n        isRouterAlive.value = true;\n    });\n};\nprovide('reload', reload);\n</script>\n\n<style scoped lang=\"scss\"></style>\n"
  },
  {
    "path": "frontend/src/api/config/service-port.ts",
    "content": "// * 后端微服务端口名\nexport const PORT1 = '9999';\nexport const PORT2 = '/hooks';\n"
  },
  {
    "path": "frontend/src/api/helper/axios-cancel.ts",
    "content": "import axios, { AxiosRequestConfig, Canceler } from 'axios';\nimport { isFunction } from '@vueuse/core';\nimport qs from 'qs';\n\n// * 声明一个 Map 用于存储每个请求的标识 和 取消函数\nlet pendingMap = new Map<string, Canceler>();\n\n// * 序列化参数\nexport const getPendingUrl = (config: AxiosRequestConfig) =>\n    [config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join('&');\n\nexport class AxiosCanceler {\n    /**\n     * @description: 添加请求\n     * @param {Object} config\n     * @return void\n     */\n    addPending(config: AxiosRequestConfig) {\n        // * 在请求开始前，对之前的请求做检查取消操作\n        this.removePending(config);\n        const url = getPendingUrl(config);\n        config.cancelToken =\n            config.cancelToken ||\n            new axios.CancelToken((cancel) => {\n                if (!pendingMap.has(url)) {\n                    // 如果 pending 中不存在当前请求，则添加进去\n                    pendingMap.set(url, cancel);\n                }\n            });\n    }\n\n    /**\n     * @description: 移除请求\n     * @param {Object} config\n     */\n    removePending(config: AxiosRequestConfig) {\n        const url = getPendingUrl(config);\n\n        if (pendingMap.has(url)) {\n            // 如果在 pending 中存在当前请求标识，需要取消当前请求，并且移除\n            const cancel = pendingMap.get(url);\n            isFunction(cancel) && cancel();\n            pendingMap.delete(url);\n        }\n    }\n\n    /**\n     * @description: 清空所有pending\n     */\n    removeAllPending() {\n        pendingMap.forEach((cancel) => {\n            isFunction(cancel) && cancel();\n        });\n        pendingMap.clear();\n    }\n\n    /**\n     * @description: 重置\n     */\n    reset(): void {\n        pendingMap = new Map<string, Canceler>();\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/helper/check-status.ts",
    "content": "import i18n from '@/lang';\nimport router from '@/routers';\nimport { MsgError } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nexport const checkStatus = (status: number, msg: string): void => {\n    switch (status) {\n        case 400:\n            MsgError(msg ? msg : i18n.global.t('commons.res.paramError'));\n            break;\n        case 404:\n            MsgError(msg ? msg : i18n.global.t('commons.res.notFound'));\n            break;\n        case 403:\n            globalStore.setLogStatus(false);\n            router.replace({ name: 'entrance', params: { code: globalStore.entrance } });\n            MsgError(msg ? msg : i18n.global.t('commons.res.forbidden'));\n            break;\n        case 500:\n            MsgError(msg ? msg : i18n.global.t('commons.res.serverError'));\n            break;\n        default:\n            MsgError(msg ? msg : i18n.global.t('commons.res.commonError'));\n    }\n};\n"
  },
  {
    "path": "frontend/src/api/index.ts",
    "content": "import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';\nimport { ResultData } from '@/api/interface';\nimport { ResultEnum } from '@/enums/http-enum';\nimport { checkStatus } from './helper/check-status';\nimport router from '@/routers';\nimport { GlobalStore } from '@/store';\nimport { MsgError } from '@/utils/message';\nimport { Base64 } from 'js-base64';\nimport i18n from '@/lang';\nimport { changeToLocal } from '@/utils/node';\n\nconst globalStore = GlobalStore();\n\nconst config = {\n    baseURL: import.meta.env.VITE_API_URL as string,\n    timeout: ResultEnum.TIMEOUT as number,\n    withCredentials: true,\n};\n\nclass RequestHttp {\n    service: AxiosInstance;\n    public constructor(config: AxiosRequestConfig) {\n        this.service = axios.create(config);\n        this.service.interceptors.request.use(\n            (config: AxiosRequestConfig) => {\n                let language = globalStore.language;\n                config.headers = {\n                    'Accept-Language': language,\n                    ...config.headers,\n                };\n                if (config.headers.CurrentNode == undefined) {\n                    config.headers.CurrentNode = encodeURIComponent(globalStore.currentNode);\n                } else {\n                    config.headers.CurrentNode = encodeURIComponent(String(config.headers.CurrentNode));\n                }\n                if (\n                    config.url === '/core/auth/login' ||\n                    config.url === '/core/auth/mfalogin' ||\n                    config.url === '/core/auth/passkey/begin' ||\n                    config.url === '/core/auth/passkey/finish'\n                ) {\n                    let entrance = Base64.encode(globalStore.entrance);\n                    config.headers.EntranceCode = entrance;\n                }\n                return {\n                    ...config,\n                } as InternalAxiosRequestConfig<any>;\n            },\n            (error: AxiosError) => {\n                return Promise.reject(error);\n            },\n        );\n\n        this.service.interceptors.response.use(\n            (response: AxiosResponse) => {\n                const { data } = response;\n                if (data.code == ResultEnum.OVERDUE || data.code == ResultEnum.FORBIDDEN) {\n                    globalStore.setLogStatus(false);\n                    router.push({\n                        name: 'entrance',\n                        params: { code: globalStore.entrance },\n                    });\n                    return Promise.reject(data);\n                }\n                if (data.code == ResultEnum.EXPIRED) {\n                    router.push({ name: 'Expired' });\n                    return;\n                }\n                if (data.code == ResultEnum.ERRXPACK) {\n                    globalStore.isProductPro = false;\n                    window.location.reload();\n                    return Promise.reject(data);\n                }\n                if (data.code == ResultEnum.NodeUnBind) {\n                    changeToLocal();\n                    window.location.reload();\n                    return;\n                }\n                if (data.code == ResultEnum.ERRGLOBALLOADDING) {\n                    globalStore.setGlobalLoading(true);\n                    globalStore.setLoadingText(data.message);\n                    return;\n                } else {\n                    if (globalStore.isLoading) {\n                        globalStore.setGlobalLoading(false);\n                    }\n                }\n                if (data.code == ResultEnum.ERRAUTH) {\n                    return data;\n                }\n                if (data.code && data.code !== ResultEnum.SUCCESS) {\n                    if (data.message.toLowerCase().indexOf('operation not permitted') !== -1) {\n                        MsgError(i18n.global.t('license.tamperHelper'));\n                        return Promise.reject(data);\n                    }\n                    MsgError(data.message);\n                    return Promise.reject(data);\n                }\n                return data;\n            },\n            async (error: AxiosError) => {\n                const { response } = error;\n\n                if (error.message.indexOf('timeout') !== -1) MsgError(i18n.global.t('commons.msg.requestTimeout'));\n                if (response) {\n                    switch (response.status) {\n                        case 313:\n                            router.push({ name: 'Expired' });\n                            return;\n                        case 500:\n                        case 502:\n                        case 524:\n                        case 407:\n                            checkStatus(\n                                response.status,\n                                response.data && response.data['message'] ? response.data['message'] : '',\n                            );\n                            return Promise.reject(error);\n                        default:\n                            return;\n                    }\n                }\n                if (!window.navigator.onLine) router.replace({ path: '/500' });\n                return Promise.reject(error);\n            },\n        );\n    }\n\n    get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {\n        return this.service.get(url, { params, ..._object });\n    }\n    post<T>(url: string, params?: object, timeout?: number, headers?: object): Promise<ResultData<T>> {\n        let config = {\n            baseURL: import.meta.env.VITE_API_URL as string,\n            timeout: timeout ? timeout : (ResultEnum.TIMEOUT as number),\n            withCredentials: true,\n            headers: headers,\n        };\n        if (headers) {\n            config.headers = headers;\n        }\n        return this.service.post(url, params, config);\n    }\n    postLocalNode<T>(url: string, params?: object, timeout?: number): Promise<ResultData<T>> {\n        return this.service.post(url, params, {\n            baseURL: import.meta.env.VITE_API_URL as string,\n            timeout: timeout ? timeout : (ResultEnum.TIMEOUT as number),\n            withCredentials: true,\n            headers: {\n                CurrentNode: 'local',\n            },\n        });\n    }\n    put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {\n        return this.service.put(url, params, _object);\n    }\n    delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {\n        return this.service.delete(url, { params, ..._object });\n    }\n    download<BlobPart>(url: string, params?: object, _object = {}): Promise<BlobPart> {\n        return this.service.post(url, params, _object);\n    }\n    upload<T>(url: string, params: object = {}, config?: AxiosRequestConfig): Promise<ResultData<T>> {\n        return this.service.post(url, params, config);\n    }\n}\n\nexport default new RequestHttp(config);\n"
  },
  {
    "path": "frontend/src/api/interface/ai.ts",
    "content": "import { ReqPage } from '.';\n\nexport namespace AI {\n    export interface OllamaModelInfo {\n        id: number;\n        name: string;\n        size: string;\n        from: string;\n        logFileExist: boolean;\n        status: string;\n        message: string;\n        createdAt: Date;\n    }\n    export interface OllamaModelDropInfo {\n        id: number;\n        name: string;\n    }\n    export interface OllamaModelSearch extends ReqPage {\n        info: string;\n    }\n\n    export interface Info {\n        cudaVersion: string;\n        driverVersion: string;\n        type: string;\n        gpu: GPU[];\n    }\n    export interface GPU {\n        index: number;\n        productName: string;\n        persistenceMode: string;\n        busID: string;\n        displayActive: string;\n        ecc: string;\n        fanSpeed: string;\n\n        temperature: string;\n        performanceState: string;\n        powerDraw: string;\n        maxPowerLimit: string;\n        memUsed: string;\n        memTotal: string;\n        gpuUtil: string;\n        computeMode: string;\n        migMode: string;\n        processes: Process[];\n    }\n    export interface Process {\n        pid: string;\n        type: string;\n        processName: string;\n        usedMemory: string;\n    }\n\n    export interface XpuInfo {\n        type: string;\n        driverVersion: string;\n        xpu: Xpu[];\n    }\n\n    interface Xpu {\n        basic: Basic;\n        stats: Stats;\n        processes: XpuProcess[];\n    }\n\n    interface Basic {\n        deviceID: number;\n        deviceName: string;\n        vendorName: string;\n        driverVersion: string;\n        memory: string;\n        freeMemory: string;\n        pciBdfAddress: string;\n    }\n\n    interface Stats {\n        power: string;\n        frequency: string;\n        temperature: string;\n        memoryUsed: string;\n        memoryUtil: string;\n    }\n\n    interface XpuProcess {\n        pid: number;\n        command: string;\n        shr: string;\n        memory: string;\n    }\n\n    export interface BindDomain {\n        domain: string;\n        sslID: number;\n        ipList: string;\n        appInstallID: number;\n        websiteID?: number;\n    }\n\n    export interface BindDomainReq {\n        appInstallID: number;\n    }\n\n    export interface BindDomainRes {\n        domain: string;\n        sslID: number;\n        allowIPs: string[];\n        websiteID?: number;\n        connUrl: string;\n        acmeAccountID: number;\n    }\n\n    export interface Environment {\n        key: string;\n        value: string;\n    }\n\n    export interface Volume {\n        source: string;\n        target: string;\n    }\n\n    export interface McpServer {\n        id: number;\n        name: string;\n        status: string;\n        baseUrl: string;\n        ssePath: string;\n        command: string;\n        port: number;\n        message: string;\n        createdAt?: string;\n        containerName: string;\n        environments: Environment[];\n        volumes: Volume[];\n        dir?: string;\n        hostIP: string;\n        protocol: string;\n        url: string;\n        outputTransport: string;\n        streamableHttpPath: string;\n        type: string;\n    }\n\n    export interface McpServerSearch extends ReqPage {\n        name: string;\n    }\n\n    export interface McpServerDelete {\n        id: number;\n    }\n\n    export interface McpServerOperate {\n        id: number;\n        operate: string;\n    }\n\n    export interface McpBindDomain {\n        domain: string;\n        sslID: number;\n        ipList: string;\n    }\n\n    export interface McpDomainRes {\n        domain: string;\n        sslID: number;\n        acmeAccountID: number;\n        allowIPs: string[];\n        websiteID?: number;\n        connUrl: string;\n    }\n\n    export interface McpBindDomainUpdate {\n        websiteID: number;\n        sslID: number;\n        ipList: string;\n    }\n\n    export interface ImportMcpServer {\n        name: string;\n        command: string;\n        ssePath: string;\n        containerName: string;\n        environments: Environment[];\n    }\n\n    export interface ExposedPort {\n        hostPort: number;\n        containerPort: number;\n        hostIP: string;\n    }\n\n    export interface Environment {\n        key: string;\n        value: string;\n    }\n    export interface Volume {\n        source: string;\n        target: string;\n    }\n\n    export interface ExtraHosts {\n        hostname: string;\n        ip: string;\n    }\n\n    export interface TensorRTLLM {\n        id?: number;\n        name: string;\n        containerName: string;\n        version: string;\n        modelDir: string;\n        status?: string;\n        message?: string;\n        createdAt?: string;\n        exposedPorts?: ExposedPort[];\n        environments?: Environment[];\n        volumes?: Volume[];\n        extraHosts?: ExtraHosts[];\n    }\n\n    export interface TensorRTLLMDTO extends TensorRTLLM {\n        dir?: string;\n    }\n\n    export interface TensorRTLLMSearch extends ReqPage {\n        name: string;\n    }\n\n    export interface TensorRTLLMDelete {\n        id: number;\n    }\n\n    export interface TensorRTLLMOperate {\n        id: number;\n        operate: string;\n    }\n\n    export interface AgentCreateReq {\n        name: string;\n        appVersion: string;\n        webUIPort: number;\n        bridgePort?: number;\n        allowedOrigins?: string[];\n        agentType: 'openclaw' | 'copaw';\n        model?: string;\n        accountId?: number;\n        token?: string;\n        taskID: string;\n        advanced: boolean;\n        containerName: string;\n        allowPort: boolean;\n        specifyIP: string;\n        restartPolicy: string;\n        cpuQuota: number;\n        memoryLimit: number;\n        memoryUnit: string;\n        pullImage: boolean;\n        editCompose: boolean;\n        dockerCompose: string;\n    }\n\n    export interface AgentItem {\n        id: number;\n        name: string;\n        agentType: 'openclaw' | 'copaw';\n        provider: string;\n        providerName: string;\n        model: string;\n        apiType: string;\n        maxTokens: number;\n        contextWindow: number;\n        baseUrl: string;\n        apiKey: string;\n        token: string;\n        status: string;\n        message: string;\n        appInstallId: number;\n        accountId: number;\n        appVersion: string;\n        containerName: string;\n        webUIPort: number;\n        bridgePort: number;\n        path: string;\n        configPath: string;\n        upgradable: boolean;\n        createdAt: string;\n    }\n\n    export interface AgentDeleteReq {\n        id: number;\n        taskID: string;\n        forceDelete: boolean;\n    }\n\n    export interface AgentTokenResetReq {\n        id: number;\n    }\n\n    export interface AgentModelConfigUpdateReq {\n        agentId: number;\n        accountId: number;\n        model: string;\n    }\n\n    export interface AgentAccountModel {\n        recordId: number;\n        id: string;\n        name: string;\n        contextWindow: number;\n        maxTokens: number;\n        reasoning: boolean;\n        input: string[];\n    }\n\n    export interface AgentAccountModelReq {\n        accountId: number;\n    }\n\n    export interface AgentAccountModelCreateReq {\n        accountId: number;\n        model: AgentAccountModel;\n    }\n\n    export interface AgentAccountModelUpdateReq {\n        accountId: number;\n        model: AgentAccountModel;\n    }\n\n    export interface AgentAccountModelDeleteReq {\n        accountId: number;\n        recordId: number;\n    }\n\n    export interface ProviderModelInfo {\n        id: string;\n        name: string;\n        contextWindow: number;\n        maxTokens: number;\n        reasoning: boolean;\n        input: string[];\n    }\n\n    export interface ProviderInfo {\n        provider: string;\n        displayName: string;\n        baseUrl: string;\n        models: ProviderModelInfo[];\n    }\n\n    export interface AgentAccountCreateReq {\n        provider: string;\n        name: string;\n        apiKey: string;\n        rememberApiKey: boolean;\n        baseURL: string;\n        apiType: string;\n        models?: AgentAccountModel[];\n        remark: string;\n    }\n\n    export interface AgentAccountUpdateReq {\n        id: number;\n        name: string;\n        apiKey: string;\n        rememberApiKey: boolean;\n        baseURL: string;\n        apiType: string;\n        remark: string;\n        syncAgents: boolean;\n    }\n\n    export interface AgentAccountSearch {\n        page: number;\n        pageSize: number;\n        provider: string;\n        name: string;\n    }\n\n    export interface AgentAccountItem {\n        id: number;\n        provider: string;\n        providerName: string;\n        name: string;\n        apiKey: string;\n        rememberApiKey: boolean;\n        baseUrl: string;\n        models: AgentAccountModel[];\n        apiType: string;\n        verified: boolean;\n        remark: string;\n        createdAt: string;\n    }\n\n    export interface AgentAccountVerifyReq {\n        provider: string;\n        apiKey: string;\n        baseURL: string;\n    }\n\n    export interface AgentAccountDeleteReq {\n        id: number;\n    }\n\n    export interface AgentFeishuConfigReq {\n        agentId: number;\n    }\n\n    export interface AgentFeishuConfig {\n        enabled: boolean;\n        dmPolicy: string;\n        botName: string;\n        appId: string;\n        appSecret: string;\n    }\n\n    export interface AgentFeishuConfigUpdateReq {\n        agentId: number;\n        enabled: boolean;\n        dmPolicy: string;\n        botName: string;\n        appId: string;\n        appSecret: string;\n    }\n\n    export interface AgentFeishuPairingApproveReq {\n        agentId: number;\n        pairingCode: string;\n    }\n\n    export interface AgentTelegramConfigReq {\n        agentId: number;\n    }\n\n    export interface AgentTelegramConfig {\n        enabled: boolean;\n        dmPolicy: string;\n        botToken: string;\n        proxy: string;\n    }\n\n    export interface AgentTelegramConfigUpdateReq {\n        agentId: number;\n        enabled: boolean;\n        dmPolicy: string;\n        botToken: string;\n        proxy: string;\n    }\n\n    export interface AgentChannelPairingApproveReq {\n        agentId: number;\n        type: 'feishu' | 'telegram' | 'discord' | 'wecom' | 'dingtalk-connector';\n        pairingCode: string;\n    }\n\n    export interface AgentWecomConfigReq {\n        agentId: number;\n    }\n\n    export interface AgentWecomConfig {\n        enabled: boolean;\n        dmPolicy: 'pairing' | 'open';\n        botId: string;\n        secret: string;\n        installed: boolean;\n    }\n\n    export interface AgentWecomConfigUpdateReq {\n        agentId: number;\n        enabled: boolean;\n        dmPolicy: 'pairing' | 'open';\n        botId: string;\n        secret: string;\n    }\n\n    export interface AgentDingTalkConfigReq {\n        agentId: number;\n    }\n\n    export interface AgentDingTalkConfig {\n        enabled: boolean;\n        clientId: string;\n        clientSecret: string;\n        dmPolicy: 'pairing' | 'allowlist' | 'open' | 'disabled';\n        allowFrom: string[];\n        groupPolicy: 'open' | 'allowlist' | 'disabled';\n        groupAllowFrom: string[];\n        installed: boolean;\n    }\n\n    export interface AgentDingTalkConfigUpdateReq {\n        agentId: number;\n        enabled: boolean;\n        clientId: string;\n        clientSecret: string;\n        dmPolicy: 'pairing' | 'allowlist' | 'open' | 'disabled';\n        allowFrom: string[];\n        groupPolicy: 'open' | 'allowlist' | 'disabled';\n        groupAllowFrom: string[];\n    }\n\n    export interface AgentQQBotConfigReq {\n        agentId: number;\n    }\n\n    export interface AgentQQBotConfig {\n        enabled: boolean;\n        appId: string;\n        clientSecret: string;\n        installed: boolean;\n    }\n\n    export interface AgentQQBotConfigUpdateReq {\n        agentId: number;\n        enabled: boolean;\n        appId: string;\n        clientSecret: string;\n    }\n\n    export interface AgentPluginInstallReq {\n        agentId: number;\n        type: 'qqbot' | 'wecom' | 'dingtalk';\n        taskID: string;\n    }\n\n    export interface AgentPluginCheckReq {\n        agentId: number;\n        type: 'qqbot' | 'wecom' | 'dingtalk';\n    }\n\n    export interface AgentPluginStatus {\n        installed: boolean;\n    }\n\n    export interface AgentDiscordConfigReq {\n        agentId: number;\n    }\n\n    export interface AgentDiscordConfig {\n        enabled: boolean;\n        dmPolicy: string;\n        groupPolicy: string;\n        token: string;\n        proxy: string;\n    }\n\n    export interface AgentDiscordConfigUpdateReq {\n        agentId: number;\n        enabled: boolean;\n        dmPolicy: string;\n        groupPolicy: string;\n        token: string;\n        proxy: string;\n    }\n\n    export interface AgentSecurityConfigReq {\n        agentId: number;\n    }\n\n    export interface AgentSecurityConfig {\n        allowedOrigins: string[];\n    }\n\n    export interface AgentSecurityConfigUpdateReq {\n        agentId: number;\n        allowedOrigins: string[];\n    }\n\n    export interface AgentOtherConfigReq {\n        agentId: number;\n    }\n\n    export interface AgentOtherConfig {\n        userTimezone: string;\n        browserEnabled: boolean;\n    }\n\n    export interface AgentOtherConfigUpdateReq {\n        agentId: number;\n        userTimezone: string;\n        browserEnabled: boolean;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/alert.ts",
    "content": "import { CommonModel, ReqPage } from '@/api/interface';\n\nexport namespace Alert {\n    export interface AlertInfo extends CommonModel {\n        type: string;\n        subType: string;\n        cycle: number;\n        count: number;\n        interval: number;\n        method: string;\n        title: string;\n        project: string;\n        status: string;\n        sendCount: number;\n        sendMethod: string[];\n        advancedParams: string;\n    }\n\n    export interface AlertDetail {\n        type: string;\n        licenseId: string;\n        title: string;\n        project: string;\n        method: string;\n        params: string;\n    }\n\n    export interface AlertUpdateStatusReq {\n        id: number;\n        status: string;\n    }\n\n    export interface AlertLog extends CommonModel {\n        alertId: number;\n        alertDetail: AlertDetail;\n        alertRule: AlertInfo;\n        count: number;\n        message: string;\n        status: string;\n        method: string;\n    }\n\n    export interface DisksDTO {\n        path: string;\n        type: string;\n        device: string;\n        total: number;\n        free: number;\n        used: number;\n        usedPercent: number;\n        inodesTotal: number;\n        inodesUsed: number;\n        inodesFree: number;\n        inodesUsedPercent: number;\n    }\n\n    export interface AlertSearch extends ReqPage {\n        type: string;\n        status: string;\n        method: string;\n        orderBy: string;\n        order: string;\n    }\n\n    export interface AlertCreateReq {\n        type: string;\n        cycle: number;\n        count: number;\n        interval: number;\n        method: string;\n        title: string;\n        project: string;\n        status: string;\n        sendCount: number;\n    }\n\n    export interface AlertUpdateReq extends CommonModel {\n        type: string;\n        cycle: number;\n        count: number;\n        interval: number;\n        method: string;\n        title: string;\n        project: string;\n        status: string;\n        sendCount: number;\n    }\n\n    export interface DelReq {\n        id: number;\n    }\n\n    export interface AlertLogSearch extends ReqPage {\n        status: string;\n        count: number;\n    }\n\n    export interface AlertLogId {\n        id: number;\n    }\n\n    export interface ClamsDTO {\n        id: number;\n        name: string;\n        status: string;\n        path: string;\n        createdAt: string;\n        updatedAt: string;\n    }\n\n    export interface CronJobDTO {\n        id: number;\n        name: string;\n        type: string;\n        status: string;\n        createdAt: string;\n        updatedAt: string;\n    }\n\n    export interface CronJobReq {\n        name: string;\n        type: string;\n        status: string;\n    }\n\n    export interface AlertSmsReq {\n        licenseId: string;\n    }\n\n    export interface AlertSmsDTO {\n        name: string;\n        total: number;\n        used: number;\n    }\n\n    export interface AlertConfigInfo extends CommonModel {\n        type: string;\n        title: string;\n        config: string;\n        status: string;\n    }\n\n    export interface AlertConfigUpdateReq {\n        id: number;\n        type: string;\n        title: string;\n        config: string;\n        status: string;\n    }\n\n    export interface AlertConfigTest {\n        port: number;\n        host: string;\n        sender: string;\n        userName: string;\n        password: string;\n        displayName: string;\n        encryption: string;\n        recipient: string;\n    }\n\n    export interface CommonAlertConfig {\n        id?: number;\n        type: string;\n        title: string;\n        status: string;\n        config: CommonConfig;\n    }\n\n    export interface CommonConfig {\n        isOffline?: string;\n        alertDailyNum?: number;\n        alertSendTimeRange?: string;\n    }\n\n    export interface EmailConfig {\n        id?: number;\n        type: string;\n        title: string;\n        status: string;\n        config: {\n            status?: string;\n            sender?: string;\n            userName?: string;\n            password?: string;\n            displayName?: string;\n            host?: string;\n            port?: number;\n            encryption?: string;\n            recipient?: string;\n        };\n    }\n\n    export interface SmsConfig {\n        id?: number;\n        type: string;\n        title: string;\n        status: string;\n        config: {\n            phone?: string;\n            alertDailyNum?: number;\n        };\n    }\n\n    export interface WebhookConfig {\n        id?: number;\n        type: string;\n        title: string;\n        status: string;\n        config: {\n            displayName?: string;\n            url?: string;\n        };\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/app.ts",
    "content": "import { ReqPage, CommonModel } from '.';\n\nexport namespace App {\n    export interface App extends CommonModel {\n        name: string;\n        icon?: string;\n        key: string;\n        tags: Tag[];\n        shortDescZh: string;\n        shortDescEn: string;\n        description: string;\n        author: string;\n        source: string;\n        type: string;\n        status: string;\n        limit: number;\n        website?: string;\n        github?: string;\n        readme: string;\n        batchInstallSupport: boolean;\n    }\n\n    interface Locale {\n        zh: string;\n        en: string;\n        'zh-Hant': string;\n        ja: string;\n        ms: string;\n        'pt-br': string;\n        ru: string;\n        ko: string;\n        tr: string;\n        'es-es': string;\n    }\n\n    export interface AppDTO extends App {\n        versions: string[];\n        installed: boolean;\n        architectures: string;\n    }\n\n    export interface Tag {\n        key: string;\n        name: string;\n        sort: number;\n    }\n\n    export interface AppItem {\n        name: string;\n        key: string;\n        id: number;\n        description: string;\n        status: string;\n        installed: boolean;\n        limit: number;\n        tags: string[];\n        gpuSupport: boolean;\n        recommend: number;\n        batchInstallSupport: boolean;\n    }\n\n    export interface AppResPage {\n        total: number;\n        items: AppItem[];\n    }\n\n    export interface AppUpdateRes {\n        version: string;\n        canUpdate: boolean;\n    }\n\n    export interface AppDetail extends CommonModel {\n        appId: number;\n        icon: string;\n        version: string;\n        readme: string;\n        params: AppParams;\n        dockerCompose: string;\n        image: string;\n        hostMode?: boolean;\n        memoryRequired: number;\n        architectures: string;\n        gpuSupport: boolean;\n    }\n\n    export interface AppReq extends ReqPage {\n        name?: string;\n        tags?: string[];\n        type?: string;\n        recommend?: boolean;\n        resource?: string;\n        showCurrentArch?: boolean;\n    }\n\n    export interface AppParams {\n        formFields: FromField[];\n    }\n\n    export interface FromField {\n        type: string;\n        labelZh: string;\n        labelEn: string;\n        required: boolean;\n        default: any;\n        envKey: string;\n        key?: string;\n        values?: ServiceParam[];\n        child?: FromFieldChild;\n        params?: FromParam[];\n        multiple?: boolean;\n        allowCreate?: boolean;\n        label: Locale;\n        description: Locale;\n    }\n\n    export interface FromFieldChild extends FromField {\n        services: App.AppService[];\n    }\n\n    export interface FromParam {\n        type: string;\n        key: string;\n        value: string;\n        envKey: string;\n    }\n\n    export interface ServiceParam {\n        label: '';\n        value: '';\n        from?: '';\n    }\n\n    export interface AppInstall {\n        appDetailId: number;\n        params: any;\n        taskID: string;\n        name: string;\n    }\n\n    export interface AppInstallSearch extends ReqPage {\n        name?: string;\n        tags?: string[];\n        update?: boolean;\n        unused?: boolean;\n        sync?: boolean;\n    }\n    export interface ChangePort {\n        key: string;\n        name: string;\n        port: number;\n    }\n\n    export interface AppInstalled extends CommonModel {\n        name: string;\n        appID: number;\n        appDetailId: string;\n        env: string;\n        status: string;\n        description: string;\n        message: string;\n        icon: string;\n        canUpdate: boolean;\n        path: string;\n        httpPort?: number;\n        httpsPort?: number;\n        favorite: boolean;\n        app: App;\n        webUI: string;\n        appKey?: string;\n        serviceName: string;\n        appStatus?: string;\n        appType?: string;\n        version?: string;\n    }\n\n    export interface AppInstalledInfo {\n        id: number;\n        name: string;\n        version: string;\n        status: string;\n        message: string;\n        httpPort: number;\n        container: string;\n        env: { [key: string]: string };\n        appKey: string;\n    }\n\n    export interface AppInstallDto {\n        id: number;\n        name: string;\n        appID: number;\n        appDetailID: number;\n        version: string;\n        status: string;\n        message: string;\n        httpPort: number;\n        httpsPort: number;\n        path: string;\n        canUpdate: boolean;\n        icon: string;\n        appName: string;\n        ready: number;\n        total: number;\n        appKey: string;\n        appType: string;\n        appStatus: string;\n        isEdit: boolean;\n        dockerCompose: string;\n        app: App.AppDetail;\n        linkDB: boolean;\n    }\n\n    export interface AppInstalledInfo {\n        id: number;\n        key: string;\n        name: string;\n    }\n\n    export interface CheckInstalled {\n        name: string;\n        version: string;\n        isExist: boolean;\n        app: string;\n        status: string;\n        createdAt: string;\n        lastBackupAt: string;\n        appInstallId: number;\n        containerName: string;\n        installPath: string;\n        httpPort: number;\n        httpsPort: number;\n        websiteDir: string;\n    }\n\n    export interface DatabaseConnInfo {\n        status: string;\n        username: string;\n        password: string;\n        privilege: boolean;\n        containerName: string;\n        serviceName: string;\n        systemIP: string;\n        port: number;\n    }\n    export interface AppInstallResource {\n        type: string;\n        name: string;\n    }\n\n    export interface AppInstalledOp {\n        installId: number;\n        operate: string;\n        backupId?: number;\n        detailId?: number;\n        forceDelete?: boolean;\n        deleteBackup?: boolean;\n        deleteImage?: boolean;\n        taskID?: string;\n    }\n\n    export interface AppInstalledSearch extends ReqPage {\n        type: string;\n        unused?: boolean;\n        all?: boolean;\n    }\n\n    export interface AppService {\n        label: string;\n        value: string;\n        config?: Object;\n        from?: string;\n        status: string;\n    }\n\n    export interface VersionDetail {\n        version: string;\n        detailId: number;\n    }\n\n    export interface InstallParams {\n        labelZh: string;\n        labelEn: string;\n        value: any;\n        edit: boolean;\n        key: string;\n        rule: string;\n        type: string;\n        values?: any;\n        showValue?: string;\n        required?: boolean;\n        multiple?: boolean;\n        label: Locale;\n        showPassword: boolean;\n    }\n\n    export interface AppConfig {\n        params: InstallParams[];\n        rawCompose?: string;\n        cpuQuota: number;\n        memoryLimit: number;\n        memoryUnit: string;\n        containerName: string;\n        allowPort: boolean;\n        dockerCompose: string;\n        hostMode?: boolean;\n        type: string;\n        webUI: string;\n        specifyIP: string;\n        restartPolicy: string;\n    }\n\n    export interface IgnoredApp {\n        name: string;\n        detailID: number;\n        version: string;\n        scope: string;\n    }\n\n    export interface AppUpdateVersionReq {\n        appInstallID: number;\n        updateVersion?: string;\n    }\n\n    export interface AppIgnoreReq {\n        appID: number;\n        appDetailID: number;\n        scope: string;\n    }\n\n    export interface CancelAppIgnore {\n        id: number;\n    }\n\n    export interface AppStoreSync {\n        taskID: string;\n    }\n\n    export interface AppConfigUpdate {\n        installID: number;\n        webUI: string;\n    }\n\n    export interface AppStoreConfig {\n        uninstallDeleteImage: string;\n        uninstallDeleteBackup: string;\n        upgradeBackup: string;\n    }\n\n    export interface AppStoreConfigUpdate {\n        scope: string;\n        status: string;\n    }\n\n    export interface CustomAppStoreConfig {\n        status: string;\n        imagePrefix: string;\n    }\n\n    export interface InstallAppToNodes extends AppInstall {\n        nodes: string[];\n        appKey: string;\n        version: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/auth.ts",
    "content": "export namespace Login {\n    export interface ReqLoginForm {\n        name: string;\n        password: string;\n        captcha: string;\n        captchaID: string;\n        authMethod: string;\n    }\n    export interface MFALoginForm {\n        name: string;\n        password: string;\n        code: string;\n        authMethod: string;\n    }\n    export interface ResLogin {\n        name: string;\n        token: string;\n        mfaStatus: string;\n    }\n    export interface PasskeyBeginResponse {\n        sessionId: string;\n        publicKey: Record<string, any>;\n    }\n    export interface ResCaptcha {\n        imagePath: string;\n        captchaID: string;\n        captchaLength: number;\n    }\n    export interface ResAuthButtons {\n        [propName: string]: any;\n    }\n\n    export interface LoginSetting {\n        isDemo: boolean;\n        isIntl: boolean;\n        isFxplay: boolean;\n        language: string;\n        menuTabs: string;\n        panelName: string;\n        theme: string;\n        isOffLine: boolean;\n        needCaptcha: boolean;\n        passkeySetting: boolean;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/backup.ts",
    "content": "import { ReqPage } from '.';\n\nexport namespace Backup {\n    export interface SearchWithType extends ReqPage {\n        type: string;\n        name: string;\n    }\n    export interface BackupOption {\n        id: number;\n        name: string;\n        type: string;\n    }\n    export interface BackupInfo {\n        id: number;\n        name: string;\n        type: string;\n        isPublic: boolean;\n        accessKey: string;\n        bucket: string;\n        credential: string;\n        rememberAuth: boolean;\n        backupPath: string;\n        bucketInput: boolean;\n        vars: string;\n        varsJson: object;\n        createdAt: Date;\n    }\n    export interface CheckResult {\n        isOk: boolean;\n        msg: string;\n        token: string;\n    }\n    export interface ClientInfo {\n        client_id: string;\n        client_secret: string;\n        redirect_uri: string;\n    }\n    export interface BackupOperate {\n        id: number;\n        type: string;\n        name: string;\n        isPublic: boolean;\n        accessKey: string;\n        bucket: string;\n        credential: string;\n        backupPath: string;\n        vars: string;\n    }\n    export interface RecordDownload {\n        downloadAccountID: number;\n        fileDir: string;\n        fileName: string;\n    }\n    export interface RecordInfo {\n        id: number;\n        createdAt: Date;\n        accountType: string;\n        accountName: string;\n        downloadAccountID: number;\n        fileDir: string;\n        fileName: string;\n        size: number;\n    }\n    export interface ForBucket {\n        type: string;\n        isPublic: boolean;\n        accessKey: string;\n        credential: string;\n        vars: string;\n    }\n    export interface SearchBackupRecord extends ReqPage {\n        type: string;\n        name: string;\n        detailName: string;\n    }\n    export interface SearchForSize extends ReqPage {\n        type: string;\n        name: string;\n        detailName: string;\n        info: string;\n        cronjobID: number;\n    }\n    export interface RecordFileSize extends ReqPage {\n        id: number;\n        size: number;\n    }\n    export interface SearchBackupRecordByCronjob extends ReqPage {\n        cronjobID: number;\n    }\n    export interface Backup {\n        type: string;\n        name: string;\n        detailName: string;\n        secret: string;\n        taskID: string;\n        stopBefore?: boolean;\n    }\n    export interface Recover {\n        downloadAccountID: number;\n        type: string;\n        name: string;\n        detailName: string;\n        file: string;\n        secret: string;\n        taskID: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/command.ts",
    "content": "export namespace Command {\n    export interface CommandInfo {\n        id: number;\n        type: string;\n        name: string;\n        groupID: number;\n        command: string;\n    }\n    export interface CommandOperate {\n        id: number;\n        type: string;\n        name: string;\n        groupID: number;\n        command: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/container.ts",
    "content": "import { ReqPage } from '.';\n\nexport namespace Container {\n    export interface ContainerOperate {\n        taskID: string;\n        names: Array<string>;\n        operation: string;\n    }\n    export interface ContainerRename {\n        name: string;\n        newName: string;\n    }\n    export interface ContainerCommit {\n        containerID: string;\n        containerName: string;\n        newImageName: string;\n        comment: string;\n        author: string;\n        pause: boolean;\n        taskID: string;\n    }\n    export interface ContainerSearch extends ReqPage {\n        name: string;\n        state: string;\n        filters: string;\n        orderBy: string;\n        order: string;\n    }\n    export interface ContainerStatus {\n        created: number;\n        running: number;\n        paused: number;\n        restarting: number;\n        removing: number;\n        exited: number;\n        dead: number;\n\n        containerCount: number;\n        composeCount: number;\n        composeTemplateCount: number;\n        imageCount: number;\n        networkCount: number;\n        volumeCount: number;\n        repoCount: number;\n\n        containerUsage: number;\n        containerReclaimable: number;\n        imageUsage: number;\n        imageReclaimable: number;\n        volumeUsage: number;\n        volumeReclaimable: number;\n        buildCacheUsage: number;\n        buildCacheReclaimable: number;\n    }\n    export interface ContainerOption {\n        name: string;\n        state: string;\n    }\n    export interface ContainerFileReq {\n        containerID: string;\n        path: string;\n    }\n    export interface ContainerFileInfo {\n        name: string;\n        path: string;\n        isDir: boolean;\n        isLink: boolean;\n        linkTo: string;\n        size: number;\n        mode: string;\n        modTime: string;\n    }\n    export interface ContainerFileContent {\n        content: string;\n        size: number;\n        truncated: boolean;\n        isBinary: boolean;\n    }\n    export interface ResourceLimit {\n        cpu: number;\n        memory: number;\n    }\n    export interface ContainerHelper {\n        taskID: string;\n        name: string;\n        image: string;\n        imageInput: boolean;\n        forcePull: boolean;\n        networks: Array<ContainerNetwork>;\n        hostname: string;\n        domainName: string;\n        dns: Array<string>;\n        cmdStr: string;\n        entrypointStr: string;\n        memoryItem: number;\n        cmd: Array<string>;\n        workingDir: string;\n        user: string;\n        openStdin: boolean;\n        tty: boolean;\n        entrypoint: Array<string>;\n        publishAllPorts: boolean;\n        exposedPorts: Array<Port>;\n        nanoCPUs: number;\n        cpuShares: number;\n        memory: number;\n        volumes: Array<Volume>;\n        extraHosts: Array<ExtraHost>;\n        privileged: boolean;\n        autoRemove: boolean;\n        labels: Array<string>;\n        env: Array<string>;\n        restartPolicy: string;\n    }\n    export interface ExtraHost {\n        hostname: string;\n        ip: string;\n    }\n    export interface ContainerUpgrade {\n        taskID: string;\n        names: Array<string>;\n        image: string;\n        forcePull: boolean;\n    }\n    export interface Port {\n        host: string;\n        hostIP: string;\n        containerPort: string;\n        hostPort: string;\n        protocol: string;\n    }\n    export interface Volume {\n        type: string;\n        sourceDir: string;\n        containerDir: string;\n        mode: string;\n    }\n    export interface ContainerInfo {\n        containerID: string;\n        name: string;\n        imageName: string;\n        createTime: string;\n        state: string;\n        runTime: string;\n        network: Array<ContainerNetwork>;\n        ports: Array<string>;\n        isFromApp: boolean;\n        isFromCompose: boolean;\n\n        hasLoad: boolean;\n        cpuPercent: number;\n        memoryPercent: number;\n    }\n    export interface ContainerNetwork {\n        network: string;\n        ipv4: string;\n        ipv6: string;\n        macAddr: string;\n    }\n    export interface ContainerInfo {\n        name: string;\n        state: string;\n    }\n    export interface ContainerItemStats {\n        sizeRw: number;\n        sizeRootFs: number;\n\n        containerUsage: number;\n        containerReclaimable: number;\n        imageUsage: number;\n        imageReclaimable: number;\n        volumeUsage: number;\n        volumeReclaimable: number;\n        buildCacheUsage: number;\n        buildCacheReclaimable: number;\n    }\n    export interface ContainerListStats {\n        containerID: string;\n        cpuTotalUsage: number;\n        systemUsage: number;\n        cpuPercent: number;\n        percpuUsage: number;\n        memoryCache: number;\n        memoryUsage: number;\n        memoryLimit: number;\n        memoryPercent: number;\n    }\n    export interface ContainerStats {\n        cpuPercent: number;\n        memory: number;\n        cache: number;\n        ioRead: number;\n        ioWrite: number;\n        networkRX: number;\n        networkTX: number;\n        shotTime: Date;\n    }\n    export interface ContainerInspect {\n        id: string;\n        type: string;\n        detail: string;\n    }\n    export interface ContainerPrune {\n        pruneType: string;\n        withTagAll: boolean;\n    }\n    export interface ContainerPruneReport {\n        deletedNumber: number;\n        spaceReclaimed: number;\n    }\n    export interface Options {\n        option: string;\n    }\n\n    export interface ImageSearch extends ReqPage {\n        name: string;\n        orderBy: string;\n        order: string;\n    }\n    export interface ImageInfo {\n        id: string;\n        createdAt: Date;\n        name: string;\n        tags: Array<string>;\n        size: string;\n        isUsed: boolean;\n    }\n    export interface ImageBuild {\n        taskID: string;\n        from: string;\n        name: string;\n        dockerfile: string;\n        tags: Array<string>;\n        args: Array<string>;\n    }\n    export interface ImagePull {\n        taskID: string;\n        repoID: number;\n        imageName: Array<string>;\n    }\n    export interface ImageTag {\n        sourceID: string;\n        tags: Array<string>;\n    }\n    export interface ImagePush {\n        taskID: string;\n        repoID: number;\n        tagName: string;\n    }\n    export interface ImageLoad {\n        taskID: string;\n        paths: Array<string>;\n    }\n    export interface ImageSave {\n        taskID: string;\n        tagName: string;\n        path: string;\n        name: string;\n    }\n\n    export interface NetworkInfo {\n        id: string;\n        name: string;\n        isSystem: boolean;\n        labels: Array<string>;\n        driver: string;\n        ipamDriver: string;\n        subnet: string;\n        gateway: string;\n        createdAt: string;\n        attachable: string;\n        expand: boolean;\n    }\n    export interface NetworkCreate {\n        name: string;\n        labels: Array<string>;\n        options: Array<string>;\n        driver: string;\n        subnet: string;\n        gateway: string;\n        ipRange: string;\n    }\n\n    export interface VolumeInfo {\n        name: string;\n        labels: Array<string>;\n        driver: string;\n        mountpoint: string;\n        createdAt: string;\n    }\n    export interface VolumeCreate {\n        name: string;\n        driver: string;\n        options: Array<string>;\n        labels: Array<string>;\n    }\n\n    export interface RepoCreate {\n        name: string;\n        downloadUrl: string;\n        protocol: string;\n        username: string;\n        password: string;\n        auth: boolean;\n    }\n    export interface RepoUpdate {\n        id: number;\n        downloadUrl: string;\n        protocol: string;\n        username: string;\n        password: string;\n        auth: boolean;\n    }\n    export interface RepoInfo {\n        id: number;\n        createdAt: Date;\n        name: string;\n        downloadUrl: string;\n        protocol: string;\n        username: string;\n        password: string;\n        auth: boolean;\n    }\n    export interface RepoOptions {\n        id: number;\n        name: string;\n        downloadUrl: string;\n    }\n\n    export interface ComposeInfo {\n        name: string;\n        createdAt: string;\n        createdBy: string;\n        containerCount: number;\n        runningCount: number;\n        configFile: string;\n        workdir: string;\n        path: string;\n        containers: Array<ComposeContainer>;\n        expand: boolean;\n        env: string;\n    }\n    export interface ComposeContainer {\n        name: string;\n        createTime: string;\n        containerID: string;\n        state: string;\n    }\n    export interface ComposeCreate {\n        taskID: string;\n        name: string;\n        from: string;\n        file: string;\n        path: string;\n        template: number;\n        env: string;\n        forcePull: boolean;\n    }\n    export interface ComposeOperation {\n        name: string;\n        operation: string;\n        path: string;\n        withFile: boolean;\n        force: boolean;\n    }\n    export interface ComposeUpdate {\n        taskID: string;\n        name: string;\n        path: string;\n        content: string;\n        env: string;\n        forcePull: boolean;\n        createdBy: string;\n    }\n\n    export interface TemplateCreate {\n        name: string;\n        description: string;\n        content: string;\n    }\n    export interface TemplateUpdate {\n        id: number;\n        description: string;\n        content: string;\n    }\n    export interface TemplateInfo {\n        id: number;\n        createdAt: Date;\n        name: string;\n        description: string;\n        content: string;\n    }\n\n    export interface BatchDelete {\n        names: Array<string>;\n    }\n\n    export interface DaemonJsonUpdateByFile {\n        file: string;\n    }\n    export interface DockerStatus {\n        isActive: boolean;\n        isExist: boolean;\n    }\n    export interface DaemonJsonConf {\n        isSwarm: boolean;\n        isExist: boolean;\n        isActive: boolean;\n        version: string;\n        registryMirrors: Array<string>;\n        insecureRegistries: Array<string>;\n        liveRestore: boolean;\n        iptables: boolean;\n        cgroupDriver: string;\n\n        ipv6: boolean;\n        fixedCidrV6: string;\n        ip6Tables: boolean;\n        experimental: boolean;\n\n        logMaxSize: string;\n        logMaxFile: string;\n    }\n\n    export interface ContainerLogInfo {\n        container: string;\n        since: string;\n        tail: number;\n        containerType: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/cronjob.ts",
    "content": "import { ReqPage } from '.';\n\nexport namespace Cronjob {\n    export interface Search extends ReqPage {\n        info: string;\n        groupIDs: Array<number>;\n        orderBy?: string;\n        order?: string;\n    }\n    export interface CronjobInfo {\n        id: number;\n        name: string;\n        type: string;\n        groupID: number;\n        specCustom: boolean;\n        spec: string;\n        specs: Array<string>;\n        specObjs: Array<SpecObj>;\n\n        executor: string;\n        isExecutorCustom: boolean;\n        script: string;\n        scriptMode: string;\n        isCustom: boolean;\n        command: string;\n        inContainer: boolean;\n        containerName: string;\n        user: string;\n        scriptID: number;\n        appID: string;\n        website: string;\n        exclusionRules: string;\n        ignoreFiles: Array<string>;\n        dbType: string;\n        dbName: string;\n        url: string;\n        urlItems: Array<string>;\n        isDir: boolean;\n        files: Array<Item>;\n        sourceDir: string;\n        snapshotRule: snapshotRule;\n        ignoreAppIDs: Array<Number>;\n        withImage: boolean;\n\n        websiteList: Array<string>;\n        appIdList: Array<string>;\n        dbNameList: Array<string>;\n\n        sourceAccounts: Array<string>;\n        downloadAccount: string;\n        sourceAccountIDs: string;\n        downloadAccountID: number;\n        sourceAccountItems: Array<number>;\n\n        retainCopies: number;\n        ignoreErr: boolean;\n        retryTimes: number;\n        timeout: number;\n        timeoutItem: number;\n        timeoutUnit: string;\n        status: string;\n        secret: string;\n        hasAlert: boolean;\n        alertCount: number;\n        alertTitle: string;\n        alertMethod: string;\n        alertMethodItems: Array<string>;\n\n        scopes: string[];\n        args: string;\n        argItems: Array<string>;\n    }\n    export interface Item {\n        val: string;\n    }\n    export interface CronjobOperate {\n        id: number;\n        name: string;\n        groupID: number;\n        type: string;\n        specCustom: boolean;\n        spec: string;\n        specs: Array<string>;\n        specObjs: Array<SpecObj>;\n\n        scriptID: number;\n        appID: string;\n        website: string;\n        exclusionRules: string;\n        dbType: string;\n        dbName: string;\n        url: string;\n        isDir: boolean;\n        sourceDir: string;\n        snapshotRule: snapshotRule;\n\n        //shell\n        executor: string;\n        scriptMode: string;\n        script: string;\n        command: string;\n        containerName: string;\n        user: string;\n\n        sourceAccountIDs: string;\n        downloadAccountID: number;\n        retainCopies: number;\n        retryTimes: number;\n        timeout: number;\n        ignoreErr: boolean;\n        secret: string;\n\n        alertCount: number;\n        alertTitle: string;\n        alertMethod: string;\n\n        scopes?: string[];\n        args: string;\n    }\n    export interface CronjobTrans {\n        name: string;\n        type: string;\n        specCustom: boolean;\n        spec: string;\n        group: string;\n\n        executor: string;\n        scriptMode: string;\n        script: string;\n        command: string;\n        containerName: string;\n        user: string;\n        url: string;\n\n        scriptName: string;\n        apps: Array<TransHelper>;\n        websites: Array<string>;\n        dbType: string;\n        dbNames: Array<TransHelper>;\n\n        exclusionRules: string;\n\n        isDir: boolean;\n        sourceDir: string;\n\n        retainCopies: number;\n        retryTimes: number;\n        timeout: number;\n        ignoreErr: boolean;\n        snapshotRule: string;\n        secret: string;\n\n        sourceAccounts: Array<string>;\n        downloadAccount: string;\n\n        alertCount: number;\n    }\n    export interface TransHelper {\n        name: string;\n        detailName: string;\n    }\n    export interface snapshotTransHelper {\n        withImage: boolean;\n        ignoreApps: Array<TransHelper>;\n    }\n    export interface snapshotRule {\n        withImage: boolean;\n        ignoreAppIDs: Array<Number>;\n    }\n    export interface SpecObj {\n        specType: string;\n        week: number;\n        day: number;\n        hour: number;\n        minute: number;\n        second: number;\n    }\n    export interface CronjobDelete {\n        ids: Array<number>;\n        cleanData: boolean;\n        cleanRemoteData: boolean;\n    }\n    export interface UpdateStatus {\n        id: number;\n        status: string;\n    }\n    export interface Download {\n        recordID: number;\n        backupAccountID: number;\n    }\n    export interface ScriptOptions {\n        id: number;\n        name: string;\n        script: string;\n    }\n    export interface SearchRecord extends ReqPage {\n        cronjobID: number;\n        startTime: Date;\n        endTime: Date;\n        status: string;\n    }\n    export interface Record {\n        id: number;\n        taskID: string;\n        file: string;\n        startTime: string;\n        records: string;\n        status: string;\n        message: string;\n        targetPath: string;\n        interval: number;\n    }\n\n    export interface ScriptInfo {\n        id: number;\n        name: string;\n        script: string;\n        groups: string;\n        isInteractive: boolean;\n        groupList: Array<number>;\n        groupBelong: Array<string>;\n        description: string;\n        createdAt: Date;\n    }\n    export interface ScriptOperate {\n        id: number;\n        name: string;\n        script: string;\n        groups: string;\n        description: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/dashboard.ts",
    "content": "export namespace Dashboard {\n    export interface OsInfo {\n        os: string;\n        platform: string;\n        platformFamily: string;\n        kernelArch: string;\n        kernelVersion: string;\n\n        diskSize: number;\n    }\n    export interface QuickJump {\n        id: number;\n        name: string;\n        alias: string;\n        title: string;\n        detail: string;\n        recommend: number;\n        isShow: boolean;\n        router: string;\n    }\n    export interface AppLauncher {\n        key: string;\n        icon: string;\n        limit: number;\n        shortDescEn: string;\n        shortDescZh: string;\n        currentRow: InstallDetail;\n\n        isInstall: boolean;\n        isRecommend: boolean;\n        detail: Array<InstallDetail>;\n    }\n    export interface AppLauncherOption {\n        key: string;\n        isShow: boolean;\n    }\n    export interface InstallDetail {\n        installID: number;\n        detailID: string;\n        name: string;\n        version: string;\n        path: string;\n        status: string;\n        appType: string;\n        webUI: string;\n        httpPort: string;\n        httpsPort: string;\n    }\n    export interface BaseInfo {\n        hostname: string;\n        os: string;\n        platform: string;\n        platformFamily: string;\n        platformVersion: string;\n        prettyDistro: string;\n        kernelArch: string;\n        kernelVersion: string;\n        virtualizationSystem: string;\n        ipV4Addr: string;\n        httpProxy: string;\n\n        cpuCores: number;\n        cpuLogicalCores: number;\n        cpuModelName: string;\n        cpuMhz: number;\n\n        currentInfo: CurrentInfo;\n        quickJump: Array<QuickJump>;\n    }\n    export interface CurrentInfo {\n        uptime: number;\n        timeSinceUptime: string;\n        procs: number;\n\n        load1: number;\n        load5: number;\n        load15: number;\n        loadUsagePercent: number;\n\n        cpuPercent: Array<number>;\n        cpuUsedPercent: number;\n        cpuUsed: number;\n        cpuTotal: number;\n        cpuDetailedPercent: Array<number>; // [user, system, nice, idle, iowait, irq, softirq, steal]\n\n        memoryTotal: number;\n        memoryAvailable: number;\n        memoryUsed: number;\n        memoryFree: number;\n        memoryShard: number;\n        memoryCache: number;\n        memoryUsedPercent: number;\n        swapMemoryTotal: number;\n        swapMemoryAvailable: number;\n        swapMemoryUsed: number;\n        swapMemoryUsedPercent: number;\n\n        ioReadBytes: number;\n        ioWriteBytes: number;\n        ioCount: number;\n        ioReadTime: number;\n        ioWriteTime: number;\n\n        diskData: Array<DiskInfo>;\n\n        gpuData: Array<GPUInfo>;\n        xpuData: Array<XPUInfo>;\n\n        topCPUItems?: Array<Process>;\n        topMemItems?: Array<Process>;\n\n        netBytesSent: number;\n        netBytesRecv: number;\n\n        shotTime: Date;\n    }\n    export interface Process {\n        name: string;\n        pid: number;\n        percent: number;\n        memory: number;\n        cmd: string;\n        user: string;\n    }\n    export interface DiskInfo {\n        path: string;\n        type: string;\n        device: string;\n        total: number;\n        free: number;\n        used: number;\n        usedPercent: number;\n\n        inodesTotal: number;\n        inodesUsed: number;\n        inodesFree: number;\n        inodesUsedPercent: number;\n    }\n    export interface GPUInfo {\n        index: number;\n        productName: string;\n        gpuUtil: string;\n        temperature: string;\n        performanceState: string;\n        powerUsage: string;\n        memoryUsage: string;\n        fanSpeed: string;\n    }\n\n    export interface XPUInfo {\n        deviceID: number;\n        deviceName: string;\n        memory: string;\n        temperature: string;\n        memoryUsed: string;\n        power: string;\n        memoryUtil: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/database.ts",
    "content": "import { ReqPage } from '.';\n\nexport namespace Database {\n    export interface SearchDBWithPage {\n        info: string;\n        database: string;\n        page: number;\n        pageSize: number;\n        orderBy?: string;\n        order?: string;\n    }\n    export interface SearchBackupRecord extends ReqPage {\n        mysqlName: string;\n        dbName: string;\n    }\n    export interface MysqlDBInfo {\n        id: number;\n        createdAt: Date;\n        name: string;\n        mysqlName: string;\n        from: string;\n        format: string;\n        username: string;\n        password: string;\n        permission: string;\n        isDelete: string;\n        description: string;\n    }\n    export interface BaseInfo {\n        name: string;\n        port: number;\n        password: string;\n        remoteConn: boolean;\n        mysqlKey: string;\n        containerName: string;\n    }\n    export interface DBConfUpdate {\n        type: string;\n        database: string;\n        file: string;\n    }\n    export interface MysqlDBCreate {\n        name: string;\n        from: string;\n        database: string;\n        format: string;\n        username: string;\n        password: string;\n        permission: string;\n        description: string;\n    }\n\n    export interface BindUser {\n        database: string;\n        db: string;\n        username: string;\n        password: string;\n        permission: string;\n    }\n\n    export interface MysqlLoadDB {\n        from: string;\n        type: string;\n        database: string;\n    }\n    export interface MysqlDBDeleteCheck {\n        id: number;\n        type: string;\n        database: string;\n    }\n\n    export interface MysqlDBDelete {\n        id: number;\n        type: string;\n        database: string;\n        forceDelete: boolean;\n        deleteBackup: boolean;\n    }\n    export interface MysqlVariables {\n        mysqlName: string;\n        binlog_cache_size: number;\n        innodb_buffer_pool_size: number;\n        innodb_log_buffer_size: number;\n        join_buffer_size: number;\n        key_buffer_size: number;\n        max_connections: number;\n        query_cache_size: number;\n        read_buffer_size: number;\n        read_rnd_buffer_size: number;\n        sort_buffer_size: number;\n        table_open_cache: number;\n        thread_cache_size: number;\n        thread_stack: number;\n        tmp_table_size: number;\n\n        slow_query_log: string;\n        long_query_time: number;\n    }\n    export interface VariablesUpdate {\n        type: string;\n        database: string;\n        variables: Array<VariablesUpdateHelper>;\n    }\n    export interface VariablesUpdateHelper {\n        param: string;\n        value: any;\n    }\n    export interface MysqlStatus {\n        Aborted_clients: number;\n        Aborted_connects: number;\n        Bytes_received: number;\n        Bytes_sent: number;\n        Com_commit: number;\n        Com_rollback: number;\n        Connections: number;\n        Created_tmp_disk_tables: number;\n        Created_tmp_tables: number;\n        Innodb_buffer_pool_pages_dirty: number;\n        Innodb_buffer_pool_read_requests: number;\n        Innodb_buffer_pool_reads: number;\n        Key_read_requests: number;\n        Key_reads: number;\n        Key_write_requests: number;\n        Key_writes: number;\n        Max_used_connections: number;\n        Open_tables: number;\n        Opened_files: number;\n        Opened_tables: number;\n        Qcache_hits: number;\n        Qcache_inserts: number;\n        Questions: number;\n        Select_full_join: number;\n        Select_range_check: number;\n        Sort_merge_passes: number;\n        Table_locks_waited: number;\n        Threads_cached: number;\n        Threads_connected: number;\n        Threads_created: number;\n        Threads_running: number;\n        Uptime: number;\n        Run: number;\n        File: string;\n        Position: number;\n    }\n    export interface FormatCollationOption {\n        format: string;\n        collations: Array<string>;\n    }\n    export interface PgLoadDB {\n        from: string;\n        type: string;\n        database: string;\n    }\n    export interface PgBind {\n        name: string;\n        database: string;\n        username: string;\n        password: string;\n        superUser: boolean;\n    }\n    export interface PgChangePrivileges {\n        name: string;\n        database: string;\n        username: string;\n        superUser: boolean;\n    }\n    export interface PostgresqlDBDelete {\n        id: number;\n        type: string;\n        database: string;\n        forceDelete: boolean;\n        deleteBackup: boolean;\n    }\n    export interface PostgresqlDBDeleteCheck {\n        id: number;\n        type: string;\n        database: string;\n    }\n    export interface PostgresqlDBInfo {\n        id: number;\n        createdAt: Date;\n        name: string;\n        postgresqlName: string;\n        from: string;\n        format: string;\n        username: string;\n        password: string;\n        description: string;\n    }\n    export interface PostgresqlConfUpdateByFile {\n        type: string;\n        database: string;\n        file: string;\n    }\n    export interface PostgresqlDBCreate {\n        name: string;\n        from: string;\n        database: string;\n        format: string;\n        username: string;\n        password: string;\n        superUser: boolean;\n        description: string;\n    }\n    export interface PostgresqlDBInfo {\n        id: number;\n        createdAt: Date;\n        name: string;\n        mysqlName: string;\n        from: string;\n        format: string;\n        username: string;\n        password: string;\n        superUser: boolean;\n        isDelete: string;\n        description: string;\n    }\n    export interface ChangeInfo {\n        id: number;\n        from: string;\n        type: string;\n        database: string;\n        value: string;\n    }\n\n    // redis\n    export interface RedisConfUpdate {\n        dbType: string;\n        database: string;\n        timeout: string;\n        maxclients: string;\n        maxmemory: string;\n    }\n    export interface RedisConfPersistenceUpdate {\n        database: string;\n        type: string;\n        appendonly: string;\n        appendfsync: string;\n        save: string;\n        dbType: string;\n    }\n    export interface RedisStatus {\n        tcp_port: string;\n        uptime_in_days: string;\n        connected_clients: string;\n        used_memory: string;\n        used_memory_rss: string;\n        used_memory_peak: string;\n        mem_fragmentation_ratio: string;\n        total_connections_received: string;\n        total_commands_processed: string;\n        instantaneous_ops_per_sec: string;\n        keyspace_hits: string;\n        keyspace_misses: string;\n        latest_fork_usec: string;\n    }\n    export interface RedisConf {\n        name: string;\n        port: number;\n        timeout: number;\n        maxclients: number;\n        requirepass: string;\n        maxmemory: string;\n    }\n    export interface RedisPersistenceConf {\n        appendonly: string;\n        appendfsync: string;\n        save: string;\n    }\n\n    // remote\n    export interface DatabaseInfo {\n        id: number;\n        createdAt: Date;\n        name: string;\n        type: string;\n        version: string;\n        from: string;\n        address: string;\n        port: number;\n        initialDB: string;\n        username: string;\n        password: string;\n\n        ssl: boolean;\n        hasCA: boolean;\n        rootCert: string;\n        clientKey: string;\n        clientCert: string;\n        skipVerify: boolean;\n\n        timeout: number;\n        description: string;\n    }\n    export interface SearchDatabasePage {\n        info: string;\n        type: string;\n        page: number;\n        pageSize: number;\n        orderBy?: string;\n        order?: string;\n    }\n    export interface DatabaseOption {\n        id: number;\n        from: string;\n        type: string;\n        database: string;\n        version: string;\n        address: string;\n    }\n    export interface DbItem {\n        id: number;\n        from: string;\n        database: string;\n        name: string;\n    }\n    export interface DatabaseCreate {\n        name: string;\n        version: string;\n        from: string;\n        address: string;\n        port: number;\n        username: string;\n        password: string;\n\n        ssl: boolean;\n        rootCert: string;\n        clientKey: string;\n        clientCert: string;\n        skipVerify: boolean;\n\n        timeout: number;\n        description: string;\n    }\n    export interface DatabaseUpdate {\n        id: number;\n        version: string;\n        address: string;\n        port: number;\n        username: string;\n        password: string;\n\n        ssl: boolean;\n        rootCert: string;\n        clientKey: string;\n        clientCert: string;\n        skipVerify: boolean;\n\n        timeout: number;\n        description: string;\n    }\n    export interface DatabaseDelete {\n        id: number;\n        forceDelete: boolean;\n        deleteBackup: boolean;\n    }\n\n    export interface DBResource {\n        type: string;\n        name: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/file.ts",
    "content": "import { CommonModel, ReqPage } from '.';\nexport namespace File {\n    export interface File extends CommonModel {\n        path: string;\n        name: string;\n        user: string;\n        group: string;\n        uid: number;\n        gid: number;\n        content: string;\n        size: number;\n        isDir: boolean;\n        isSymlink: boolean;\n        isHidden: boolean;\n        linkPath: string;\n        type: string;\n        updateTime: string;\n        modTime: string;\n        mode: number;\n        mimeType: string;\n        dirSize: number;\n        items: File[];\n        extension: string;\n        itemTotal: number;\n        favoriteID: number;\n        remark?: string;\n    }\n\n    export interface ReqFile extends ReqPage {\n        path: string;\n        search?: string;\n        expand: boolean;\n        dir?: boolean;\n        showHidden?: boolean;\n        containSub?: boolean;\n        sortBy?: string;\n        sortOrder?: string;\n        isDetail?: boolean;\n    }\n\n    export interface ReqNodeFile extends ReqFile {\n        node: string;\n    }\n\n    export interface PreviewContentReq {\n        path: string;\n        isDetail?: boolean;\n    }\n\n    export interface SearchUploadInfo extends ReqPage {\n        path: string;\n    }\n    export interface UploadInfo {\n        name: string;\n        size: number;\n        createdAt: string;\n    }\n\n    export interface FileTree {\n        id: string;\n        name: string;\n        isDir: boolean;\n        path: string;\n        children?: FileTree[];\n    }\n\n    export interface FileCreate {\n        path: string;\n        isDir: boolean;\n        mode?: number;\n        isLink?: boolean;\n        isSymlink?: boolean;\n        linkPath?: boolean;\n        sub?: boolean;\n        name?: string;\n    }\n\n    export interface FileDelete {\n        path: string;\n        isDir: boolean;\n        forceDelete: boolean;\n    }\n\n    export interface FileBatchDelete {\n        isDir: boolean;\n        paths: Array<string>;\n    }\n\n    export interface FileCompress {\n        files: string[];\n        type: string;\n        dst: string;\n        name: string;\n        replace: boolean;\n        secret: string;\n    }\n\n    export interface FileDeCompress {\n        path: string;\n        dst: string;\n        type: string;\n        secret: string;\n    }\n\n    export interface FileEdit {\n        path: string;\n        content: string;\n    }\n\n    export interface FileRename {\n        oldName: string;\n        newName: string;\n    }\n\n    export interface FileOwner {\n        path: string;\n        user: string;\n        group: string;\n        sub: boolean;\n    }\n\n    export interface FileWget {\n        path: string;\n        name: string;\n        url: string;\n        ignoreCertificate?: boolean;\n    }\n\n    export interface FileWgetRes {\n        key: string;\n    }\n\n    export interface FileKeys {\n        keys: string[];\n    }\n\n    export interface FileMove {\n        oldPaths: string[];\n        newPath: string;\n        type: string;\n    }\n\n    export interface FileDownload {\n        paths: string[];\n        name: string;\n        url: string;\n    }\n\n    export interface FileChunkDownload {\n        name: string;\n        path: string;\n    }\n\n    export interface DirSizeReq {\n        path: string;\n    }\n\n    export interface DirSizeRes {\n        size: number;\n    }\n\n    export interface DepthDirSizeRes {\n        size: number;\n        path: string;\n    }\n\n    export interface FilePath {\n        path: string;\n    }\n\n    export interface ExistFileInfo {\n        name: string;\n        path: string;\n        size: number;\n        uploadSize: number;\n        modTime: string;\n        isDir: boolean;\n    }\n\n    export interface RecycleBin {\n        sourcePath: string;\n        name: string;\n        isDir: boolean;\n        size: number;\n        deleteTime: string;\n        rName: string;\n        from: string;\n    }\n\n    export interface RecycleBinReduce {\n        rName: string;\n        from: string;\n        name: string;\n    }\n\n    export interface FileReadByLine {\n        id?: number;\n        type: string;\n        name?: string;\n        page: number;\n        pageSize: number;\n        taskID?: string;\n        taskType?: string;\n        taskOperate?: string;\n        resourceID?: number;\n    }\n\n    export interface Favorite extends CommonModel {\n        path: string;\n        isDir: boolean;\n        isTxt: boolean;\n        name: string;\n    }\n\n    export interface FileRole {\n        paths: string[];\n        mode: number;\n        user: string;\n        group: string;\n        sub: boolean;\n    }\n\n    export interface FileRemarkUpdate {\n        path: string;\n        remark: string;\n    }\n\n    export interface FileRemarksRes {\n        remarks: Record<string, string>;\n    }\n\n    export interface UserGroupResponse {\n        users: UserInfo[];\n        groups: string[];\n    }\n    export interface UserInfo {\n        username: string;\n        group: string;\n    }\n\n    export interface ConvertFile {\n        type: string;\n        path: string;\n        extension: string;\n        inputFile: string;\n        outputFormat: string;\n    }\n\n    export interface ConvertFileRequest {\n        files: ConvertFile[];\n        outputPath: string;\n        deleteSource: boolean;\n        taskID: string;\n    }\n\n    export interface ConvertLogResponse {\n        date: string;\n        type: string;\n        log: string;\n        status: string;\n        message: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/group.ts",
    "content": "export namespace Group {\n    export interface GroupInfo {\n        id: number;\n        name: string;\n        type: string;\n        isDefault: boolean;\n        isDelete: boolean;\n    }\n    export interface GroupCreate {\n        id: number;\n        name: string;\n        type: string;\n    }\n    export interface GroupUpdate {\n        id: number;\n        name: string;\n        isDefault: boolean;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/host-tool.ts",
    "content": "export namespace HostTool {\n    export interface HostTool {\n        type: string;\n        config: {};\n    }\n\n    export interface Supersivor extends HostTool {\n        configPath: string;\n        includeDir: string;\n        logPath: string;\n        isExist: boolean;\n        init: boolean;\n        msg: string;\n        version: string;\n        status: string;\n        ctlExist: boolean;\n        serviceName: string;\n    }\n\n    export interface SupersivorConfig {\n        type: string;\n        operate: string;\n        content?: string;\n    }\n\n    export interface SupersivorConfigRes {\n        type: string;\n        content: string;\n    }\n\n    export interface SupersivorInit {\n        type: string;\n        configPath: string;\n        serviceName: string;\n    }\n\n    export interface SupersivorProcess {\n        operate: string;\n        name: string;\n        command: string;\n        user: string;\n        dir: string;\n        numprocs: string;\n        status?: ProcessStatus[];\n        autoRestart: string;\n        autoStart: string;\n        environment: string;\n    }\n\n    export interface ProcessStatus {\n        PID: string;\n        status: string;\n        uptime: string;\n        name: string;\n    }\n\n    export interface ProcessReq {\n        operate: string;\n        name: string;\n    }\n\n    export interface ProcessFileReq {\n        operate: string;\n        name: string;\n        content?: string;\n        file: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/host.ts",
    "content": "import { CommonModel, ReqPage } from '.';\n\nexport namespace Host {\n    export interface HostTree {\n        id: number;\n        label: string;\n        children: Array<TreeNode>;\n    }\n    export interface TreeNode {\n        id: number;\n        label: string;\n    }\n    export interface Host extends CommonModel {\n        name: string;\n        groupID: number;\n        groupBelong: string;\n        addr: string;\n        port: number;\n        user: string;\n        authMode: string;\n        password: string;\n        privateKey: string;\n        passPhrase: string;\n        rememberPassword: boolean;\n        description: string;\n    }\n    export interface HostOperate {\n        isLocal: boolean;\n        id: number;\n        name: string;\n        groupID: number;\n        addr: string;\n        port: number;\n        user: string;\n        authMode: string;\n        password: string;\n        privateKey: string;\n        passPhrase: string;\n        rememberPassword: boolean;\n\n        description: string;\n    }\n    export interface HostConnTest {\n        isLocal: boolean;\n        addr: string;\n        port: number;\n        user: string;\n        authMode: string;\n        privateKey: string;\n        passPhrase: string;\n        password: string;\n\n        localSSHConnShow: string;\n    }\n    export interface GroupChange {\n        id: number;\n        groupID: number;\n    }\n    export interface ReqSearch {\n        info?: string;\n    }\n    export interface SearchWithPage extends ReqPage {\n        groupID: number;\n        info?: string;\n    }\n\n    export interface FirewallBase {\n        name: string;\n        isExist: boolean;\n        isActive: boolean;\n        isInit: boolean;\n        isBind: boolean;\n        version: string;\n        pingStatus: string;\n    }\n    export interface RuleSearch extends ReqPage {\n        strategy: string;\n        info: string;\n        type: string;\n    }\n    export interface RuleInfo extends ReqPage {\n        family: string;\n        address: string;\n        destination: string;\n        port: string;\n        srcPort: string;\n        destPort: string;\n        protocol: string;\n        strategy: string;\n\n        usedStatus: string;\n        description: string;\n\n        [key: string]: any;\n    }\n    export interface UpdateDescription {\n        type: string;\n        chain: string;\n        srcIP: string;\n        dstIP: string;\n        srcPort: string;\n        dstPort: string;\n        protocol: string;\n        strategy: string;\n        description: string;\n    }\n    export interface RulePort {\n        operation: string;\n        address: string;\n        port: string;\n        source: string;\n        protocol: string;\n        strategy: string;\n        description: string;\n    }\n    export interface RuleForward {\n        operation: string;\n        protocol: string;\n        port: string;\n        targetIP: string;\n        targetPort: string;\n        interface: string;\n    }\n    export interface RuleIP {\n        operation: string;\n        address: string;\n        strategy: string;\n        description: string;\n    }\n    export interface UpdatePortRule {\n        oldRule: RulePort;\n        newRule: RulePort;\n    }\n    export interface UpdateAddrRule {\n        oldRule: RuleIP;\n        newRule: RuleIP;\n    }\n    export interface BatchRule {\n        type: string;\n        rules: Array<RulePort>;\n    }\n\n    export interface MonitorSetting {\n        defaultNetwork: string;\n        defaultIO: string;\n        monitorStatus: string;\n        monitorStoreDays: string;\n        monitorInterval: string;\n    }\n    export interface MonitorData {\n        param: string;\n        date: Array<Date>;\n        value: Array<any>;\n    }\n    export interface MonitorSearch {\n        param: string;\n        io: string;\n        network: string;\n        startTime: Date;\n        endTime: Date;\n    }\n\n    export interface MonitorGPUSearch {\n        productName: string;\n        startTime: Date;\n        endTime: Date;\n    }\n    export interface MonitorGPUOptions {\n        gpuType: string;\n        options: Array<string>;\n        chartHide: Array<ChartHide>;\n    }\n    export interface ChartHide {\n        productName: string;\n        process: boolean;\n        gpu: boolean;\n        memory: boolean;\n        power: boolean;\n        temperature: boolean;\n        speed: boolean;\n    }\n    export interface MonitorGPUData {\n        date: Array<Date>;\n        gpuValue: Array<number>;\n        temperatureValue: Array<number>;\n        powerTotal: Array<number>;\n        powerUsed: Array<number>;\n        powerPercent: Array<number>;\n        memoryTotal: Array<number>;\n        memoryUsed: Array<number>;\n        memoryPercent: Array<number>;\n        speedValue: Array<number>;\n        gpuProcesses: Array<Array<GPUProcess>>;\n    }\n    export interface GPUProcess {\n        pid: string;\n        type: string;\n        processName: string;\n        usedMemory: string;\n    }\n\n    export interface SSHInfo {\n        autoStart: boolean;\n        isActive: boolean;\n        message: string;\n        port: string;\n        listenAddress: string;\n        passwordAuthentication: string;\n        pubkeyAuthentication: string;\n        encryptionMode: string;\n        primaryKey: string;\n        permitRootLogin: string;\n        useDNS: string;\n        currentUser: string;\n    }\n    export interface SSHUpdate {\n        key: string;\n        oldValue: string;\n        newValue: string;\n    }\n    export interface RootCert {\n        name: string;\n        mode: string;\n        encryptionMode: string;\n        passPhrase: string;\n        privateKey: string;\n        publicKey: string;\n        description: string;\n    }\n    export interface RootCertInfo {\n        id: number;\n        createAt: Date;\n        name: string;\n        mode: string;\n        encryptionMode: string;\n        passPhrase: string;\n        description: string;\n        publicKey: string;\n        privateKey: string;\n    }\n    export interface searchSSHLog extends ReqPage {\n        info: string;\n        status: string;\n    }\n    export interface analysisSSHLog extends ReqPage {\n        orderBy: string;\n    }\n    export interface sshHistory {\n        date: Date;\n        area: string;\n        user: string;\n        authMode: string;\n        address: string;\n        port: string;\n        status: string;\n        message: string;\n    }\n\n    export interface DiskBasicInfo {\n        device: string;\n        size: string;\n        model: string;\n        diskType: string;\n        isRemovable: boolean;\n        isSystem: boolean;\n        filesystem: string;\n        used: string;\n        avail: string;\n        usePercent: number;\n        mountPoint: string;\n        isMounted: boolean;\n        serial: string;\n    }\n\n    export interface DiskInfo extends DiskBasicInfo {\n        partitions?: DiskBasicInfo[];\n    }\n\n    export interface CompleteDiskInfo {\n        disks: DiskInfo[];\n        unpartitionedDisks: DiskBasicInfo[];\n        systemDisks?: DiskInfo[];\n        totalDisks: number;\n        totalCapacity: number;\n    }\n\n    export interface DiskPartition {\n        device: string;\n        filesystem: string;\n        label: string;\n        autoMount: boolean;\n        mountPoint: string;\n        noFail: boolean;\n    }\n\n    export interface DiskMount {\n        device: string;\n        mountPoint: string;\n        filesystem?: string;\n    }\n\n    export interface DiskUmount {\n        mountPoint: string;\n    }\n\n    export interface ComponentInfo {\n        exists: boolean;\n        version: string;\n        path: string;\n        error: string;\n    }\n\n    // Iptables Filter\n    export interface IptablesFilterRuleSearch extends ReqPage {\n        info: string;\n        type: string;\n    }\n    export interface IptablesData {\n        items: IptablesRules[];\n        total: number;\n        defaultStrategy: string;\n    }\n    export interface IptablesRules {\n        id: number;\n        protocol: string;\n        srcPort: string;\n        dstPort: string;\n        srcIP: string;\n        dstIP: string;\n        strategy: string;\n        description: string;\n    }\n    export interface ChainStatus {\n        isBind: boolean;\n        defaultStrategy: string;\n    }\n    export interface IptablesFilterRuleOp {\n        operation: string;\n        id?: number;\n        chain: string;\n        protocol: string;\n        srcIP?: string;\n        srcPort?: number;\n        dstIP?: string;\n        dstPort?: number;\n        strategy: string;\n        description?: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/index.ts",
    "content": "export interface Result {\n    code: number;\n    message: string;\n}\n\nexport interface ResultData<T> {\n    code: number;\n    message: string;\n    data: T;\n}\n\nexport interface ResPage<T> {\n    items: T[];\n    total: number;\n}\n\nexport interface ReqPage {\n    page: number;\n    pageSize: number;\n}\nexport interface SearchWithPage {\n    info: string;\n    page: number;\n    pageSize: number;\n    orderBy?: string;\n    order?: string;\n    name?: string;\n}\nexport interface CommonModel {\n    id: number;\n    createdAt?: string;\n    updatedAt?: string;\n}\nexport interface DescriptionUpdate {\n    id: number;\n    description: string;\n}\nexport interface UpdateByFile {\n    file: string;\n}\n"
  },
  {
    "path": "frontend/src/api/interface/log.ts",
    "content": "import { DateTimeFormats } from '@intlify/core-base';\nimport { ReqPage } from '.';\n\nexport namespace Log {\n    export interface OperationLog {\n        id: number;\n        source: string;\n        action: string;\n        ip: string;\n        path: string;\n        method: string;\n        userAgent: string;\n        body: string;\n        resp: string;\n\n        status: number;\n        latency: number;\n        errorMessage: string;\n\n        detail: string;\n        createdAt: DateTimeFormats;\n    }\n    export interface SearchOpLog extends ReqPage {\n        source: string;\n        status: string;\n        operation: string;\n    }\n    export interface SearchLgLog extends ReqPage {\n        ip: string;\n        status: string;\n    }\n    export interface LoginLogs {\n        ip: string;\n        address: string;\n        agent: string;\n        status: string;\n        message: string;\n        createdAt: DateTimeFormats;\n    }\n    export interface CleanLog {\n        logType: string;\n    }\n\n    export interface SearchTaskReq extends ReqPage {\n        type: string;\n        status: string;\n    }\n\n    export interface Task {\n        id: string;\n        name: string;\n        type: string;\n        logFile: string;\n        status: string;\n        errorMsg: string;\n        operationLogID: number;\n        resourceID: number;\n        currentStep: string;\n        endAt: Date;\n        createdAt: Date;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/nginx.ts",
    "content": "export namespace Nginx {\n    export interface NginxScopeReq {\n        scope: string;\n    }\n    export interface NginxParam {\n        name: string;\n        params: string[];\n    }\n\n    export interface NginxConfigReq {\n        operate: string;\n        websiteId?: number;\n        scope: string;\n        params?: any;\n    }\n\n    export interface NginxStatus {\n        accepts: number;\n        handled: number;\n        active: number;\n        requests: number;\n        reading: number;\n        writing: number;\n        waiting: number;\n    }\n\n    export interface NginxFileUpdate {\n        content: string;\n        backup: boolean;\n    }\n\n    export interface NginxBuildReq {\n        taskID: string;\n        mirror: string;\n    }\n\n    export interface NginxModule {\n        name: string;\n        script?: string;\n        packages?: string;\n        enable: boolean;\n        params: string;\n    }\n\n    export interface NginxBuildConfig {\n        mirror: string;\n        modules: NginxModule[];\n    }\n\n    export interface NginxModuleUpdate extends NginxModule {\n        operate: string;\n    }\n\n    export interface NginxHttpsStatus {\n        https: boolean;\n        sslRejectHandshake: boolean;\n    }\n\n    export interface NginxOperateReq {\n        operate: string;\n    }\n\n    export interface NginxHttpsOperateReq extends NginxOperateReq {\n        sslRejectHandshake: boolean;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/process.ts",
    "content": "export namespace Process {\n    export interface StopReq {\n        PID: number;\n    }\n\n    export interface PsProcessData {\n        PID: number;\n        name: string;\n        PPID: number;\n        username: string;\n        status: string;\n        startTime: string;\n        numThreads: number;\n        numConnections: number;\n        cpuPercent: string;\n\n        diskRead: string;\n        diskWrite: string;\n        cmdLine: string;\n\n        rss: string;\n        vms: string;\n        hwm: string;\n        data: string;\n        stack: string;\n        locked: string;\n        swap: string;\n        dirty: string;\n        pss: string;\n        uss: string;\n        shared: string;\n        text: string;\n\n        cpuValue: number;\n        rssValue: number;\n\n        envs: string[];\n\n        openFiles: OpenFilesStat[];\n        connects: ProcessConnect[];\n    }\n\n    export interface ProcessConnect {\n        type: string;\n        status: string;\n        localaddr: string;\n        remoteaddr: string;\n        PID: number;\n        name: string;\n    }\n\n    export type ProcessConnects = ProcessConnect[];\n\n    export interface OpenFilesStat {\n        path: string;\n        fd: number;\n    }\n\n    export interface ListeningProcess {\n        PID: number;\n        Port: { [key: string]: {} };\n        Protocol: number;\n        Name: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/runtime.ts",
    "content": "import { CommonModel, ReqPage } from '.';\nimport { App } from './app';\nexport namespace Runtime {\n    export interface Runtime extends CommonModel {\n        name: string;\n        appDetailID: number;\n        image: string;\n        workDir: string;\n        dockerCompose: string;\n        env: string;\n        params: string;\n        type: string;\n        resource: string;\n        version: string;\n        status: string;\n        codeDir: string;\n        port: string;\n        appID: number;\n        remark: string;\n    }\n\n    export interface RuntimeReq extends ReqPage {\n        name?: string;\n        status?: string;\n        type?: string;\n    }\n\n    export interface NodeReq {\n        codeDir: string;\n    }\n\n    export interface NodeScripts {\n        name: string;\n        script: string;\n    }\n\n    export interface RuntimeDTO extends Runtime {\n        appParams: App.InstallParams[];\n        appID: number;\n        source?: string;\n        path?: string;\n        exposedPorts?: ExposedPort[];\n        environments?: Environment[];\n        volumes?: Volume[];\n        extraHosts?: ExtraHost[];\n        container: string;\n    }\n\n    export interface RuntimeCreate {\n        id?: number;\n        name: string;\n        appDetailID: number;\n        image: string;\n        params: Object;\n        type: string;\n        resource: string;\n        appID?: number;\n        version?: string;\n        rebuild?: boolean;\n        source?: string;\n        codeDir?: string;\n        port?: number;\n        exposedPorts?: ExposedPort[];\n        environments?: Environment[];\n        volumes?: Volume[];\n        extraHosts?: ExtraHost[];\n        remark?: string;\n    }\n\n    export interface ExposedPort {\n        hostPort: number;\n        containerPort: number;\n        hostIP: string;\n    }\n    export interface Environment {\n        key: string;\n        value: string;\n    }\n    export interface Volume {\n        source: string;\n        target: string;\n    }\n\n    export interface ExtraHost {\n        hostname: string;\n        ip: string;\n    }\n\n    export interface RuntimeUpdate {\n        name: string;\n        appDetailID: number;\n        image: string;\n        params: object;\n        type: string;\n        resource: string;\n        appID?: number;\n        version?: string;\n        rebuild?: boolean;\n    }\n\n    export interface RuntimeDelete {\n        id: number;\n        forceDelete: boolean;\n    }\n\n    export interface RuntimeOperate {\n        ID: number;\n        operate: string;\n    }\n\n    export interface NodeModule {\n        name: string;\n        version: string;\n        description: string;\n    }\n\n    export interface NodeModuleReq {\n        ID: number;\n        Operate?: string;\n        Module?: string;\n        PkgManager?: string;\n    }\n\n    export interface PHPExtensions extends CommonModel {\n        id: number;\n        name: string;\n        extensions: string;\n    }\n\n    export interface PHPExtensionsList extends ReqPage {\n        all: boolean;\n    }\n\n    export interface PHPExtensionsCreate {\n        name: string;\n        extensions: string;\n    }\n\n    export interface PHPExtensionsUpdate {\n        id: number;\n        name: string;\n        extensions: string;\n    }\n\n    export interface PHPExtensionsDelete {\n        id: number;\n    }\n\n    export interface PHPExtensionsRes {\n        extensions: string[];\n        supportExtensions: SupportExtension[];\n    }\n\n    export interface SupportExtension {\n        name: string;\n        description: string;\n        installed: boolean;\n        check: string;\n        versions: string[];\n    }\n\n    export interface PHPExtensionInstall {\n        name: string;\n        id: number;\n        taskID?: string;\n    }\n\n    export interface PHPConfig {\n        params: any;\n        disableFunctions: string[];\n        uploadMaxSize: string;\n        maxExecutionTime: string;\n    }\n\n    export interface PHPConfigUpdate {\n        id: number;\n        params?: any;\n        disableFunctions?: string[];\n        scope: string;\n        uploadMaxSize?: string;\n        maxExecutionTime?: string;\n    }\n\n    export interface PHPUpdate {\n        id: number;\n        content: string;\n        type: string;\n    }\n\n    export interface PHPFileReq {\n        id: number;\n        type: string;\n    }\n\n    export interface FPMConfig {\n        id: number;\n        params: any;\n    }\n\n    export interface ProcessReq {\n        operate: string;\n        name: string;\n        id: number;\n    }\n\n    export interface ProcessFileReq {\n        operate: string;\n        name: string;\n        content?: string;\n        file: string;\n        id: number;\n    }\n\n    export interface SupersivorProcess {\n        operate: string;\n        name: string;\n        command: string;\n        user: string;\n        dir: string;\n        numprocs: string;\n        id: number;\n        environment: string;\n    }\n\n    export interface PHPContainerConfig {\n        id: number;\n        containerName: string;\n        exposedPorts: ExposedPort[];\n        environments: Environment[];\n        volumes: Volume[];\n        extraHosts: ExtraHost[];\n    }\n\n    export interface RemarkUpdate {\n        id: number;\n        remark: string;\n    }\n\n    export interface FpmStatus {\n        key: string;\n        value: any;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/setting.ts",
    "content": "import { DateTimeFormats } from '@intlify/core-base';\n\nexport namespace Setting {\n    export interface SettingInfo {\n        userName: string;\n        password: string;\n        email: string;\n        systemIP: string;\n        systemVersion: string;\n        upgradeBackupCopies: string;\n        dockerSockPath: string;\n        developerMode: string;\n\n        sessionTimeout: number;\n        localTime: string;\n        timeZone: string;\n        ntpSite: string;\n\n        panelName: string;\n        edition: string;\n        theme: string;\n        menuTabs: string;\n        language: string;\n        docSource: string;\n        defaultIO: string;\n        defaultNetwork: string;\n        lastCleanTime: string;\n        lastCleanSize: string;\n        lastCleanData: string;\n\n        serverPort: number;\n        ipv6: string;\n        bindAddress: string;\n        ssl: string;\n        sslType: string;\n        allowIPs: string;\n        bindDomain: string;\n        passkeyTrustedProxies: string;\n        securityEntrance: string;\n        dashboardMemoVisible: string;\n        dashboardSimpleNodeVisible: string;\n        expirationDays: number;\n        expirationTime: string;\n        complexityVerification: string;\n        mfaStatus: string;\n        mfaSecret: string;\n        mfaInterval: string;\n\n        monitorStatus: string;\n        monitorInterval: number;\n        monitorStoreDays: number;\n\n        messageType: string;\n        emailVars: string;\n        weChatVars: string;\n        dingVars: string;\n        snapshotIgnore: string;\n        hideMenu: string;\n        noAuthSetting: string;\n\n        proxyUrl: string;\n        proxyType: string;\n        proxyPort: string;\n        proxyUser: string;\n        proxyPasswd: string;\n        proxyPasswdKeep: string;\n\n        apiInterfaceStatus: string;\n        apiKey: string;\n        ipWhiteList: string;\n        apiKeyValidityTime: number;\n    }\n    export interface TerminalInfo {\n        lineHeight: string;\n        letterSpacing: string;\n        fontSize: string;\n        fontFamily: string;\n        backgroundColor: string;\n        foregroundColor: string;\n        cursorBlink: string;\n        cursorStyle: string;\n        scrollback: string;\n        scrollSensitivity: string;\n    }\n    export interface TerminalAIInfo {\n        aiStatus: string;\n        aiAccountId: string;\n        aiPrefix: string;\n        aiRiskCommands: string;\n    }\n    export interface SettingUpdate {\n        key: string;\n        value: string;\n    }\n    export interface ProxyUpdate {\n        proxyUrl: string;\n        proxyType: string;\n        proxyPort: string;\n        proxyUser: string;\n        proxyPasswd: string;\n        proxyPasswdKeep: string;\n        withDockerRestart: boolean;\n    }\n    export interface ApiConfig {\n        apiInterfaceStatus: string;\n        apiKey: string;\n        ipWhiteList: string;\n        apiKeyValidityTime: number;\n    }\n    export interface SSLUpdate {\n        ssl: string;\n        domain: string;\n        sslType: string;\n        cert: string;\n        key: string;\n        sslID: number;\n    }\n    export interface SSLInfo {\n        domain: string;\n        timeout: string;\n        rootPath: string;\n        cert: string;\n        key: string;\n        sslID: number;\n    }\n    export interface PasswordUpdate {\n        oldPassword: string;\n        newPassword: string;\n    }\n    export interface PortUpdate {\n        serverPort: number;\n    }\n    export interface MFARequest {\n        title: string;\n        interval: number;\n    }\n    export interface MFAInfo {\n        secret: string;\n        qrImage: string;\n    }\n    export interface MFABind {\n        secret: string;\n        code: string;\n        interval: string;\n    }\n    export interface PasskeyRegisterRequest {\n        name: string;\n    }\n    export interface PasskeyBeginResponse {\n        sessionId: string;\n        publicKey: Record<string, any>;\n    }\n    export interface PasskeyInfo {\n        id: string;\n        name: string;\n        createdAt: string;\n        lastUsedAt: string;\n    }\n    export interface CommonDescription {\n        id: string;\n        type: string;\n        detailType: string;\n        isPinned: boolean;\n        description: string;\n    }\n\n    export interface SnapshotCreate {\n        id: number;\n        sourceAccountIDs: string;\n        downloadAccountID: string;\n        description: string;\n        secret: string;\n        timeout: number;\n\n        appData: Array<DataTree>;\n        panelData: Array<DataTree>;\n        backupData: Array<DataTree>;\n\n        withMonitorData: boolean;\n        withLoginLog: boolean;\n        withOperationLog: boolean;\n    }\n    export interface SnapshotImport {\n        backupAccountID: number;\n        names: Array<string>;\n        description: string;\n    }\n    export interface SnapshotRecover {\n        id: number;\n        taskID: string;\n        isNew: boolean;\n        reDownload: boolean;\n        secret: string;\n    }\n    export interface SnapshotInfo {\n        id: number;\n        name: string;\n        sourceAccounts: Array<string>;\n        downloadAccount: string;\n        description: string;\n        status: string;\n        message: string;\n        createdAt: DateTimeFormats;\n        version: string;\n        secret: string;\n        timeout: number;\n\n        taskID: string;\n        taskRecoverID: string;\n        taskRollbackID: string;\n\n        interruptStep: string;\n        recoverStatus: string;\n        recoverMessage: string;\n        rollbackStatus: string;\n        rollbackMessage: string;\n    }\n    export interface SnapshotData {\n        appData: Array<DataTree>;\n        panelData: Array<DataTree>;\n        backupData: Array<DataTree>;\n\n        withMonitorData: boolean;\n        withLoginLog: boolean;\n        withOperationLog: boolean;\n    }\n    export interface DataTree {\n        id: string;\n        label: string;\n        key: string;\n        name: string;\n        size: number;\n        isShow: boolean;\n        isDisable: boolean;\n\n        path: string;\n\n        Children: Array<DataTree>;\n    }\n    export interface UpgradeInfo {\n        testVersion: string;\n        newVersion: string;\n        latestVersion: string;\n        releaseNote: string;\n    }\n\n    export interface License {\n        licenseName: string;\n        assigneeName: string;\n        productPro: string;\n        versionConstraint: string;\n        trial: boolean;\n        status: string;\n        message: string;\n        smsUsed: number;\n        smsTotal: number;\n    }\n    export interface LicenseOptions {\n        id: number;\n        licenseName: string;\n        totalFreeCount: number;\n        availableXpackCount: number;\n        availableFreeCount: number;\n    }\n    export interface LicenseStatus {\n        productPro: string;\n        status: string;\n        smsTotal: number;\n        smsUsed: number;\n    }\n    export interface NodeItem {\n        id: number;\n        addr: string;\n        status: string;\n        version: string;\n        isXpack: boolean;\n        isBound: boolean;\n        name: string;\n    }\n    export interface SimpleNodeItem {\n        id: number;\n        name: string;\n        addr: string;\n        description: string;\n        systemVersion: string;\n        securityEntrance: string;\n        cpuUsedPercent: number;\n        cpuTotal: number;\n        memoryTotal: number;\n        memoryUsedPercent: number;\n    }\n    export interface ReleasesNotes {\n        Version: string;\n        CreatedAt: string;\n        Content: string;\n        NewCount: number;\n        OptimizationCount: number;\n        FixCount: number;\n    }\n\n    export interface LicenseBind {\n        nodeID: number;\n        licenseID: number;\n        syncList: string;\n        withDockerRestart: boolean;\n    }\n    export interface LicenseUnbind {\n        id: number;\n        force: boolean;\n        withDockerRestart: boolean;\n    }\n\n    export interface SmsInfo {\n        licenseName: string;\n        smsUsed: number;\n        smsTotal: number;\n    }\n\n    export interface NodeAppItem {\n        name: string;\n        updateCount: number;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/terminal.ts",
    "content": "export interface ReqTerminal {\n    name: string;\n    ip: string;\n    port: number;\n    user: string;\n    authType: string;\n    password: string;\n    key: string;\n}\n"
  },
  {
    "path": "frontend/src/api/interface/toolbox.ts",
    "content": "import { ReqPage } from '.';\nimport { Cronjob } from './cronjob';\n\nexport namespace Toolbox {\n    export interface DeviceBaseInfo {\n        dns: Array<string>;\n        hosts: Array<HostHelper>;\n        hostname: string;\n        ntp: string;\n        user: string;\n        timeZone: string;\n        localTime: string;\n\n        swapMemoryTotal: number;\n        swapMemoryAvailable: number;\n        swapMemoryUsed: number;\n        maxSize: number;\n\n        swapDetails: Array<SwapHelper>;\n    }\n    export interface SwapHelper {\n        path: string;\n        size: number;\n        used: string;\n\n        isNew: boolean;\n    }\n    export interface HostHelper {\n        ip: string;\n        host: string;\n    }\n    export interface TimeZoneOptions {\n        from: string;\n        zones: Array<string>;\n    }\n\n    export interface CleanData {\n        systemClean: Array<CleanTree>;\n        backupClean: Array<CleanTree>;\n        uploadClean: Array<CleanTree>;\n        downloadClean: Array<CleanTree>;\n        systemLogClean: Array<CleanTree>;\n        containerClean: Array<CleanTree>;\n    }\n    export interface CleanTree {\n        id: string;\n        label: string;\n        children: Array<CleanTree>;\n        type: string;\n        name: string;\n        size: number;\n        isCheck: boolean;\n        isRecommend: boolean;\n    }\n\n    export interface Fail2banBaseInfo {\n        isEnable: boolean;\n        isActive: boolean;\n        isExist: boolean;\n        version: string;\n\n        port: number;\n        maxRetry: number;\n        banTime: string;\n        findTime: string;\n        banAction: string;\n        logPath: string;\n    }\n\n    export interface Fail2banSearch {\n        status: string;\n    }\n\n    export interface Fail2banUpdate {\n        key: string;\n        value: string;\n    }\n\n    export interface Fail2banSet {\n        ips: Array<string>;\n        operate: string;\n    }\n\n    export interface FtpBaseInfo {\n        isActive: boolean;\n        isExist: boolean;\n    }\n    export interface FtpInfo {\n        id: number;\n        user: string;\n        password: string;\n        status: string;\n        path: string;\n        description: string;\n    }\n    export interface FtpCreate {\n        user: string;\n        password: string;\n        path: string;\n        description: string;\n    }\n    export interface FtpUpdate {\n        id: number;\n        password: string;\n        status: string;\n        path: string;\n        description: string;\n    }\n    export interface FtpSearchLog extends ReqPage {\n        user: string;\n        operation: string;\n    }\n    export interface FtpLog {\n        ip: string;\n        user: string;\n        time: string;\n        operation: string;\n        status: string;\n        size: string;\n    }\n\n    export interface ClamBaseInfo {\n        version: string;\n        isActive: boolean;\n        isExist: boolean;\n\n        freshVersion: string;\n        freshIsExist: boolean;\n        freshIsActive: boolean;\n    }\n    export interface ClamInfo {\n        id: number;\n        name: string;\n        status: string;\n        path: string;\n        infectedStrategy: string;\n        infectedDir: string;\n        lastHandleDate: string;\n        hasSpec: boolean;\n        spec: string;\n        specObj: Cronjob.SpecObj;\n        timeout: number;\n        timeoutItem: number;\n        timeoutUnit: string;\n        description: string;\n        hasAlert: boolean;\n        alertCount: number;\n        alertTitle: string;\n        alertMethod: string;\n        alertMethodItems: Array<string>;\n    }\n    export interface ClamCreate {\n        name: string;\n        path: string;\n        infectedStrategy: string;\n        infectedDir: string;\n        spec: string;\n        timeout: number;\n        specObj: Cronjob.SpecObj;\n        description: string;\n    }\n    export interface ClamUpdate {\n        id: number;\n        name: string;\n        path: string;\n        infectedStrategy: string;\n        infectedDir: string;\n        spec: string;\n        timeout: number;\n        specObj: Cronjob.SpecObj;\n        description: string;\n    }\n    export interface ClamSearchLog extends ReqPage {\n        clamID: number;\n        startTime: Date;\n        endTime: Date;\n    }\n    export interface ClamRecordReq {\n        tail: string;\n        clamName: string;\n        recordName: string;\n    }\n    export interface ClamRecord {\n        id: number;\n        taskID: string;\n        scanDate: string;\n        scanTime: string;\n        infectedFiles: string;\n        totalError: string;\n        startTime: Date;\n        status: string;\n        message: string;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/interface/website.ts",
    "content": "import { CommonModel, ReqPage } from '.';\n\nexport namespace Website {\n    export interface Website extends CommonModel {\n        primaryDomain: string;\n        type: string;\n        alias: string;\n        remark: string;\n        domains: string[];\n        appType: string;\n        appInstallId?: number;\n        webSiteGroupId: number;\n        otherDomains: string;\n        defaultServer: boolean;\n        protocol: string;\n        autoRenew: boolean;\n        appinstall?: NewAppInstall;\n        webSiteSSL: SSL;\n        runtimeID: number;\n        rewrite: string;\n        user: string;\n        group: string;\n        IPV6: boolean;\n        accessLog?: boolean;\n        errorLog?: boolean;\n        childSites?: string[];\n        dbID: number;\n        dbType: string;\n        favorite: boolean;\n        streamPorts: string;\n        udp: boolean;\n    }\n\n    export interface WebsiteDTO extends Website {\n        errorLogPath: string;\n        accessLogPath: string;\n        sitePath: string;\n        appName: string;\n        runtimeName: string;\n        runtimeType: string;\n        openBaseDir: boolean;\n        algorithm: string;\n        servers: NginxUpstreamServer[];\n    }\n\n    export interface WebsiteStreamUpdate {\n        websiteID: number;\n        algorithm: string;\n        streamPorts?: string;\n        servers: NginxUpstreamServer[];\n    }\n\n    export interface WebsiteRes extends CommonModel {\n        protocol: string;\n        primaryDomain: string;\n        type: string;\n        alias: string;\n        remark: string;\n        status: string;\n        expireDate: string;\n        sitePath: string;\n        appName: string;\n        runtimeName: string;\n        sslExpireDate: Date;\n    }\n\n    export interface NewAppInstall {\n        name: string;\n        appDetailId: number;\n        params: any;\n    }\n\n    export interface WebSiteSearch extends ReqPage {\n        name: string;\n        orderBy: string;\n        order: string;\n        websiteGroupId: number;\n    }\n\n    export interface WebSiteDel {\n        id: number;\n        deleteApp: boolean;\n        deleteBackup: boolean;\n        forceDelete: boolean;\n    }\n\n    export interface WebSiteCreateReq {\n        type: string;\n        alias: string;\n        remark: string;\n        appType: string;\n        appInstallId: number;\n        webSiteGroupId: number;\n        proxy: string;\n        proxyType: string;\n        ftpUser: string;\n        ftpPassword: string;\n        taskID: string;\n        SSLID?: number;\n        enableSSL: boolean;\n        createDB?: boolean;\n        dbName?: string;\n        dbPassword?: string;\n        dbFormat?: string;\n        dbUser?: string;\n        dbHost?: string;\n        domains: SubDomain[];\n        streamPorts?: string;\n        name: string;\n        algorithm: string;\n        servers: NginxUpstreamServer[];\n    }\n\n    export interface WebSiteUpdateReq {\n        id: number;\n        primaryDomain: string;\n        remark: string;\n        webSiteGroupId: number;\n        expireDate?: string;\n        IPV6: boolean;\n        favorite: boolean;\n    }\n\n    export interface WebSiteOp {\n        id: number;\n        operate: string;\n    }\n\n    export interface WebSiteOpLog {\n        id: number;\n        operate: string;\n        logType: string;\n        page?: number;\n        pageSize?: number;\n    }\n\n    export interface OptionReq {\n        types?: string[];\n    }\n\n    export interface WebSiteLog {\n        enable: boolean;\n        content: string;\n        end: boolean;\n        path: string;\n    }\n\n    export interface Domain {\n        websiteId: number;\n        port: number;\n        id: number;\n        domain: string;\n        ssl: boolean;\n    }\n\n    export interface DomainCreate {\n        websiteID: number;\n        domains: SubDomain[];\n    }\n\n    export interface DomainUpdate {\n        id: number;\n        ssl: boolean;\n    }\n\n    interface SubDomain {\n        domain: string;\n        port: number;\n        ssl: boolean;\n    }\n\n    export interface DomainDelete {\n        id: number;\n    }\n\n    export interface NginxConfigReq {\n        operate: string;\n        websiteId: number;\n        scope: string;\n        params?: any;\n    }\n\n    export interface NginxScopeReq {\n        websiteId: number;\n        scope: string;\n    }\n\n    export interface NginxParam {\n        name: string;\n        params: string[];\n    }\n\n    export interface NginxScopeConfig {\n        enable: boolean;\n        params: NginxParam[];\n    }\n\n    export interface DnsAccount extends CommonModel {\n        name: string;\n        type: string;\n        authorization: Object;\n    }\n\n    export interface DnsAccountCreate {\n        name: string;\n        type: string;\n        authorization: Object;\n    }\n\n    export interface DnsAccountUpdate {\n        id: number;\n        name: string;\n        type: string;\n        authorization: Object;\n    }\n\n    export interface SSL extends CommonModel {\n        primaryDomain: string;\n        privateKey: string;\n        pem: string;\n        otherDomains: string;\n        certURL: string;\n        type: string;\n        issuerName: string;\n        expireDate: string;\n        startDate: string;\n        provider: string;\n        websites?: Website.Website[];\n        autoRenew: boolean;\n        acmeAccountId: number;\n        status: string;\n        domains: string;\n        description: string;\n        dnsAccountId?: number;\n        pushDir: boolean;\n        dir: string;\n        keyType: string;\n        nameserver1: string;\n        nameserver2: string;\n        disableCNAME: boolean;\n        skipDNS: boolean;\n        execShell: boolean;\n        shell: string;\n        pushNode: boolean;\n        nodes: string;\n        privateKeyPath: string;\n        certPath: string;\n        isIP: boolean;\n    }\n\n    export interface SSLDTO extends SSL {\n        logPath: string;\n    }\n\n    export interface SSLCreate {\n        primaryDomain: string;\n        otherDomains: string;\n        provider: string;\n        acmeAccountId: number;\n        dnsAccountId: number;\n        id?: number;\n        description: string;\n        isIP: boolean;\n    }\n\n    export interface SSLApply {\n        websiteId: number;\n        SSLId: number;\n    }\n\n    export interface SSLRenew {\n        SSLId: number;\n    }\n\n    export interface SSLUpdate {\n        id: number;\n        autoRenew: boolean;\n        description: string;\n        primaryDomain: string;\n        otherDomains: string;\n        acmeAccountId: number;\n        provider: string;\n        dnsAccountId?: number;\n        keyType: string;\n        pushDir: boolean;\n        dir: string;\n    }\n\n    export interface AcmeAccount extends CommonModel {\n        email: string;\n        url: string;\n        type: string;\n        useProxy: boolean;\n    }\n\n    export interface AcmeAccountCreate {\n        email: string;\n        useProxy: boolean;\n    }\n\n    export interface AcmeAccountUpdate {\n        id: number;\n        useProxy: boolean;\n    }\n\n    export interface DNSResolveReq {\n        acmeAccountId: number;\n        websiteSSLId: number;\n    }\n\n    export interface DNSResolve {\n        resolve: string;\n        value: string;\n        domain: string;\n        err: string;\n    }\n\n    export interface SSLReq {\n        name?: string;\n        acmeAccountID?: string;\n    }\n\n    export interface HTTPSReq {\n        websiteId: number;\n        enable: boolean;\n        websiteSSLId?: number;\n        type: string;\n        certificate?: string;\n        privateKey?: string;\n        httpConfig: string;\n        SSLProtocol: string[];\n        algorithm: string;\n        http3: boolean;\n    }\n\n    export interface HTTPSConfig {\n        enable: boolean;\n        SSL: SSL;\n        httpConfig: string;\n        SSLProtocol: string[];\n        algorithm: string;\n        hsts: boolean;\n        hstsIncludeSubDomains: boolean;\n        httpsPort?: string;\n        http3: boolean;\n    }\n\n    export interface BatchSetHttps {\n        ids: number[];\n        taskID: string;\n        enable: boolean;\n        websiteSSLId?: number;\n        type: string;\n        certificate?: string;\n        privateKey?: string;\n        httpConfig: string;\n        SSLProtocol: string[];\n        algorithm: string;\n        http3: boolean;\n    }\n\n    export interface CheckReq {\n        installIds?: number[];\n    }\n\n    export interface CheckRes {\n        name: string;\n        status: string;\n        version: string;\n        appName: string;\n    }\n\n    export interface DelReq {\n        id: number;\n    }\n\n    export interface NginxUpdate {\n        id: number;\n        content: string;\n    }\n\n    export interface DefaultServerUpdate {\n        id: number;\n    }\n    export interface RewriteReq {\n        websiteID: number;\n        name: string;\n    }\n\n    export interface RewriteRes {\n        content: string;\n    }\n\n    export interface RewriteUpdate {\n        websiteID: number;\n        name: string;\n        content: string;\n    }\n\n    export interface CustomRewirte {\n        operate: string;\n        name: string;\n        content: string;\n    }\n\n    export interface DirUpdate {\n        id: number;\n        siteDir: string;\n    }\n\n    export interface DirPermissionUpdate {\n        id: number;\n        user: string;\n        group: string;\n    }\n\n    export interface ProxyReq {\n        id: number;\n    }\n\n    export interface ProxyConfig {\n        id: number;\n        operate: string;\n        enable: boolean;\n        cache: boolean;\n        cacheTime: number;\n        cacheUnit: string;\n        serverCacheTime: number;\n        serverCacheUnit: string;\n        name: string;\n        modifier: string;\n        match: string;\n        proxyPass: string;\n        proxyHost: string;\n        filePath?: string;\n        replaces?: ProxReplace;\n        content?: string;\n        proxyAddress?: string;\n        proxyProtocol?: string;\n        sni?: boolean;\n        proxySSLName: string;\n        cors: boolean;\n        allowOrigins: string;\n        allowMethods: string;\n        allowHeaders: string;\n        allowCredentials: boolean;\n        preflight: boolean;\n        browserCache?: 'enable' | 'disable' | 'noModify';\n    }\n\n    export interface ProxyDel {\n        id: number;\n        name: string;\n    }\n\n    export interface ProxyStatusUpdate {\n        id: number;\n        name: string;\n        status: string;\n    }\n\n    export interface ProxReplace {\n        [key: string]: string;\n    }\n\n    export interface ProxyFileUpdate {\n        websiteID: number;\n        name: string;\n        content: string;\n    }\n\n    export interface AuthReq {\n        websiteID: number;\n    }\n\n    export interface NginxAuth {\n        username: string;\n        remark: string;\n    }\n\n    export interface AuthConfig {\n        enable: boolean;\n        items: NginxAuth[];\n    }\n\n    export interface NginxAuthConfig {\n        websiteID: number;\n        operate: string;\n        username: string;\n        password: string;\n        remark: string;\n        scope: string;\n        path?: '';\n        name?: '';\n    }\n\n    export interface NginxPathAuthConfig {\n        websiteID: number;\n        operate: string;\n        path: string;\n        username: string;\n        password: string;\n        name: string;\n    }\n\n    export interface LeechConfig {\n        enable: boolean;\n        cache: boolean;\n        cacheTime: number;\n        cacheUint: string;\n        extends: string;\n        return: string;\n        serverNames: string[];\n        noneRef: boolean;\n        logEnable: boolean;\n        blocked: boolean;\n        websiteID?: number;\n    }\n\n    export interface LeechReq {\n        websiteID: number;\n    }\n\n    export interface WebsiteReq {\n        websiteID: number;\n    }\n\n    export interface RedirectConfig {\n        operate: string;\n        websiteID: number;\n        domains?: string[];\n        enable: boolean;\n        name: string;\n        keepPath: boolean;\n        type: string;\n        redirect: string;\n        path?: string;\n        target: string;\n        redirectRoot?: boolean;\n        filePath?: string;\n        content?: string;\n    }\n\n    export interface RedirectFileUpdate {\n        websiteID: number;\n        name: string;\n        content: string;\n    }\n\n    export interface PHPVersionChange {\n        websiteID: number;\n        runtimeID: number;\n    }\n\n    export interface DirConfig {\n        dirs: string[];\n        user: string;\n        userGroup: string;\n        msg: string;\n    }\n\n    export interface SSLUpload {\n        privateKey: string;\n        certificate: string;\n        privateKeyPath: string;\n        certificatePath: string;\n        type: string;\n        sslID: number;\n    }\n\n    export interface SSLObtain {\n        ID: number;\n    }\n\n    export interface CA extends CommonModel {\n        name: string;\n        csr: string;\n        privateKey: string;\n        keyType: string;\n    }\n\n    export interface CACreate {\n        name: string;\n        commonName: string;\n        country: string;\n        organization: string;\n        organizationUint: string;\n        keyType: string;\n        province: string;\n        city: string;\n    }\n\n    export interface CADTO extends CA {\n        commonName: string;\n        country: string;\n        organization: string;\n        organizationUint: string;\n        province: string;\n        city: string;\n    }\n\n    export interface SSLObtainByCA {\n        id: number;\n        domains: string;\n        keyType: string;\n        time: number;\n        unit: string;\n        pushDir: boolean;\n        dir: string;\n        description: string;\n    }\n\n    export interface RenewSSLByCA {\n        SSLID: number;\n    }\n\n    export interface SSLDownload {\n        id: number;\n    }\n\n    export interface WebsiteHtml {\n        content: string;\n    }\n    export interface WebsiteHtmlUpdate {\n        type: string;\n        content: string;\n        sync: boolean;\n    }\n\n    export interface NginxUpstream {\n        name: string;\n        algorithm: string;\n        servers: NginxUpstreamServer[];\n        content?: string;\n        websiteID?: number;\n    }\n\n    export interface NginxUpstreamFile {\n        name: string;\n        content: string;\n        websiteID: number;\n    }\n\n    export interface LoadBalanceReq {\n        websiteID: number;\n        name: string;\n        algorithm: string;\n        servers: NginxUpstreamServer[];\n    }\n\n    interface NginxUpstreamServer {\n        server: string;\n        weight: number;\n        failTimeout: number;\n        failTimeoutUnit: string;\n        maxFails: number;\n        maxConns: number;\n        flag: string;\n    }\n\n    export interface LoadBalanceDel {\n        websiteID: number;\n        name: string;\n    }\n\n    export interface WebsiteLBUpdateFile {\n        websiteID: number;\n        name: string;\n        content: string;\n    }\n\n    export interface WebsiteCacheConfig {\n        open: boolean;\n        cacheLimit: number;\n        cacheLimitUnit: string;\n        shareCache: number;\n        shareCacheUnit: string;\n        cacheExpire: number;\n        cacheExpireUnit: string;\n    }\n\n    export interface WebsiteRealIPConfig {\n        open: boolean;\n        ipFrom: string;\n        ipHeader: string;\n        ipOther: string;\n    }\n\n    export interface WebsiteResource {\n        name: string;\n        type: string;\n        resourceID: number;\n        detail: any;\n    }\n\n    export interface WebsiteDatabase {\n        type: string;\n        databaseID: number;\n        websiteID: number;\n        from: string;\n        databaseName: number;\n    }\n\n    export interface ChangeDatabase {\n        websiteID: number;\n        databaseID: number;\n        databaseType: string;\n    }\n\n    export interface CrossSiteAccessOp {\n        websiteID: number;\n        operation: string;\n    }\n\n    export interface ExecComposer {\n        websiteID: number;\n        command: string;\n        extCommand?: string;\n        mirror: string;\n        dir: string;\n        user: string;\n        taskID: string;\n    }\n\n    export interface BatchOperate {\n        ids: number[];\n        operate: string;\n        taskID: string;\n    }\n\n    export interface CorsConfig {\n        cors: boolean;\n        allowOrigins: string;\n        allowMethods: string;\n        allowHeaders: string;\n        allowCredentials: boolean;\n        preflight: boolean;\n    }\n\n    export interface CorsConfigReq extends CorsConfig {\n        websiteID: number;\n    }\n\n    export interface BatchSetGroup {\n        ids: number[];\n        groupID: number;\n    }\n}\n"
  },
  {
    "path": "frontend/src/api/modules/ai.ts",
    "content": "import { AI } from '@/api/interface/ai';\nimport http from '@/api';\nimport { ResPage, SearchWithPage } from '../interface';\nimport { TimeoutEnum } from '@/enums/http-enum';\n\nexport const createOllamaModel = (name: string, taskID: string) => {\n    return http.post(`/ai/ollama/model`, { name: name, taskID: taskID });\n};\nexport const recreateOllamaModel = (name: string, taskID: string) => {\n    return http.post(`/ai/ollama/model/recreate`, { name: name, taskID: taskID });\n};\nexport const deleteOllamaModel = (ids: Array<number>, force: boolean) => {\n    return http.post(`/ai/ollama/model/del`, { ids: ids, forceDelete: force });\n};\nexport const searchOllamaModel = (params: AI.OllamaModelSearch) => {\n    return http.post<ResPage<AI.OllamaModelInfo>>(`/ai/ollama/model/search`, params);\n};\nexport const loadOllamaModel = (name: string) => {\n    return http.post<string>(`/ai/ollama/model/load`, { name: name });\n};\nexport const syncOllamaModel = () => {\n    return http.post<Array<AI.OllamaModelDropInfo>>(`/ai/ollama/model/sync`);\n};\nexport const closeOllamaModel = (name: string) => {\n    return http.post(`/ai/ollama/close`, { name: name });\n};\n\nexport const loadGPUInfo = () => {\n    return http.get<any>(`/ai/gpu/load`);\n};\n\nexport const bindDomain = (req: AI.BindDomain) => {\n    return http.post(`/ai/domain/bind`, req);\n};\n\nexport const getBindDomain = (req: AI.BindDomainReq) => {\n    return http.post<AI.BindDomainRes>(`/ai/domain/get`, req);\n};\n\nexport const updateBindDomain = (req: AI.BindDomain) => {\n    return http.post(`/ai/domain/update`, req);\n};\n\nexport const pageMcpServer = (req: AI.McpServerSearch) => {\n    return http.post<ResPage<AI.McpServer>>(`/ai/mcp/search`, req);\n};\n\nexport const createMcpServer = (req: AI.McpServer) => {\n    return http.post(`/ai/mcp/server`, req);\n};\n\nexport const updateMcpServer = (req: AI.McpServer) => {\n    return http.post(`/ai/mcp/server/update`, req);\n};\n\nexport const deleteMcpServer = (req: AI.McpServerDelete) => {\n    return http.post(`/ai/mcp/server/del`, req);\n};\n\nexport const operateMcpServer = (req: AI.McpServerOperate) => {\n    return http.post(`/ai/mcp/server/op`, req);\n};\n\nexport const bindMcpDomain = (req: AI.McpBindDomain) => {\n    return http.post(`/ai/mcp/domain/bind`, req);\n};\n\nexport const getMcpDomain = () => {\n    return http.get<AI.McpDomainRes>(`/ai/mcp/domain/get`);\n};\n\nexport const updateMcpDomain = (req: AI.McpBindDomainUpdate) => {\n    return http.post(`/ai/mcp/domain/update`, req);\n};\n\nexport const pageTensorRTLLM = (req: AI.TensorRTLLMSearch) => {\n    return http.post<ResPage<AI.TensorRTLLMDTO>>(`/ai/tensorrt/search`, req);\n};\n\nexport const createTensorRTLLM = (req: AI.TensorRTLLM) => {\n    return http.post(`/ai/tensorrt/create`, req);\n};\n\nexport const updateTensorRTLLM = (req: AI.TensorRTLLM) => {\n    return http.post(`/ai/tensorrt/update`, req);\n};\n\nexport const deleteTensorRTLLM = (req: AI.TensorRTLLMDelete) => {\n    return http.post(`/ai/tensorrt/delete`, req);\n};\n\nexport const operateTensorRTLLM = (req: AI.TensorRTLLMOperate) => {\n    return http.post(`/ai/tensorrt/operate`, req);\n};\n\nexport const createAgent = (req: AI.AgentCreateReq) => {\n    return http.post<AI.AgentItem>(`/ai/agents`, req, TimeoutEnum.T_60S);\n};\n\nexport const pageAgents = (req: SearchWithPage) => {\n    return http.post<ResPage<AI.AgentItem>>(`/ai/agents/search`, req);\n};\n\nexport const deleteAgent = (req: AI.AgentDeleteReq) => {\n    return http.post(`/ai/agents/delete`, req);\n};\n\nexport const resetAgentToken = (req: AI.AgentTokenResetReq) => {\n    return http.post(`/ai/agents/token/reset`, req);\n};\n\nexport const updateAgentModelConfig = (req: AI.AgentModelConfigUpdateReq) => {\n    return http.post(`/ai/agents/model/update`, req);\n};\n\nexport const getAgentProviders = () => {\n    return http.get<AI.ProviderInfo[]>(`/ai/agents/providers`);\n};\n\nexport const createAgentAccount = (req: AI.AgentAccountCreateReq) => {\n    return http.post(`/ai/agents/accounts`, req);\n};\n\nexport const updateAgentAccount = (req: AI.AgentAccountUpdateReq) => {\n    return http.post(`/ai/agents/accounts/update`, req);\n};\n\nexport const pageAgentAccounts = (req: AI.AgentAccountSearch) => {\n    return http.post<ResPage<AI.AgentAccountItem>>(`/ai/agents/accounts/search`, req);\n};\n\nexport const getAgentAccountModels = (req: AI.AgentAccountModelReq) => {\n    return http.post<AI.AgentAccountModel[]>(`/ai/agents/accounts/models`, req);\n};\n\nexport const createAgentAccountModel = (req: AI.AgentAccountModelCreateReq) => {\n    return http.post(`/ai/agents/accounts/models/create`, req);\n};\n\nexport const updateAgentAccountModel = (req: AI.AgentAccountModelUpdateReq) => {\n    return http.post(`/ai/agents/accounts/models/update`, req);\n};\n\nexport const deleteAgentAccountModel = (req: AI.AgentAccountModelDeleteReq) => {\n    return http.post(`/ai/agents/accounts/models/delete`, req);\n};\n\nexport const verifyAgentAccount = (req: AI.AgentAccountVerifyReq) => {\n    return http.post(`/ai/agents/accounts/verify`, req);\n};\n\nexport const deleteAgentAccount = (req: AI.AgentAccountDeleteReq) => {\n    return http.post(`/ai/agents/accounts/delete`, req);\n};\n\nexport const getAgentFeishuConfig = (req: AI.AgentFeishuConfigReq) => {\n    return http.post<AI.AgentFeishuConfig>(`/ai/agents/channel/feishu/get`, req);\n};\n\nexport const updateAgentFeishuConfig = (req: AI.AgentFeishuConfigUpdateReq) => {\n    return http.post(`/ai/agents/channel/feishu/update`, req);\n};\n\nexport const getAgentTelegramConfig = (req: AI.AgentTelegramConfigReq) => {\n    return http.post<AI.AgentTelegramConfig>(`/ai/agents/channel/telegram/get`, req);\n};\n\nexport const updateAgentTelegramConfig = (req: AI.AgentTelegramConfigUpdateReq) => {\n    return http.post(`/ai/agents/channel/telegram/update`, req);\n};\n\nexport const getAgentDiscordConfig = (req: AI.AgentDiscordConfigReq) => {\n    return http.post<AI.AgentDiscordConfig>(`/ai/agents/channel/discord/get`, req);\n};\n\nexport const updateAgentDiscordConfig = (req: AI.AgentDiscordConfigUpdateReq) => {\n    return http.post(`/ai/agents/channel/discord/update`, req);\n};\n\nexport const getAgentWecomConfig = (req: AI.AgentWecomConfigReq) => {\n    return http.post<AI.AgentWecomConfig>(`/ai/agents/channel/wecom/get`, req);\n};\n\nexport const updateAgentWecomConfig = (req: AI.AgentWecomConfigUpdateReq) => {\n    return http.post(`/ai/agents/channel/wecom/update`, req);\n};\n\nexport const getAgentDingTalkConfig = (req: AI.AgentDingTalkConfigReq) => {\n    return http.post<AI.AgentDingTalkConfig>(`/ai/agents/channel/dingtalk/get`, req);\n};\n\nexport const updateAgentDingTalkConfig = (req: AI.AgentDingTalkConfigUpdateReq) => {\n    return http.post(`/ai/agents/channel/dingtalk/update`, req);\n};\n\nexport const getAgentQQBotConfig = (req: AI.AgentQQBotConfigReq) => {\n    return http.post<AI.AgentQQBotConfig>(`/ai/agents/channel/qqbot/get`, req);\n};\n\nexport const updateAgentQQBotConfig = (req: AI.AgentQQBotConfigUpdateReq) => {\n    return http.post(`/ai/agents/channel/qqbot/update`, req);\n};\n\nexport const installAgentPlugin = (req: AI.AgentPluginInstallReq) => {\n    return http.post(`/ai/agents/plugin/install`, req);\n};\n\nexport const checkAgentPlugin = (req: AI.AgentPluginCheckReq) => {\n    return http.post<AI.AgentPluginStatus>(`/ai/agents/plugin/check`, req);\n};\n\nexport const getAgentSecurityConfig = (req: AI.AgentSecurityConfigReq) => {\n    return http.post<AI.AgentSecurityConfig>(`/ai/agents/security/get`, req);\n};\n\nexport const updateAgentSecurityConfig = (req: AI.AgentSecurityConfigUpdateReq) => {\n    return http.post(`/ai/agents/security/update`, req);\n};\n\nexport const getAgentOtherConfig = (req: AI.AgentOtherConfigReq) => {\n    return http.post<AI.AgentOtherConfig>(`/ai/agents/other/get`, req);\n};\n\nexport const updateAgentOtherConfig = (req: AI.AgentOtherConfigUpdateReq) => {\n    return http.post(`/ai/agents/other/update`, req);\n};\n\nexport const approveAgentChannelPairing = (req: AI.AgentChannelPairingApproveReq) => {\n    return http.post(`/ai/agents/channel/pairing/approve`, req);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/alert.ts",
    "content": "import http from '@/api';\nimport { ResPage } from '@/api/interface';\nimport { Alert } from '../interface/alert';\nimport { deepCopy } from '@/utils/util';\n\nexport const SearchAlerts = (req: Alert.AlertSearch) => {\n    return http.post<ResPage<Alert.AlertInfo>>(`/alert/search`, req);\n};\n\nexport const CreateAlert = (req: Alert.AlertCreateReq) => {\n    let request = deepCopy(req) as Alert.AlertCreateReq;\n    return http.post<any>(`/alert`, request);\n};\n\nexport const UpdateAlert = (req: Alert.AlertUpdateReq) => {\n    return http.post<any>(`/alert/update`, req);\n};\n\nexport const DeleteAlert = (req: Alert.DelReq) => {\n    return http.post<any>(`/alert/del`, req);\n};\n\nexport const UpdateAlertStatus = (req: Alert.AlertUpdateStatusReq) => {\n    return http.post<any>(`/alert/status`, req);\n};\n\nexport const ListDisks = () => {\n    return http.get<Alert.DisksDTO[]>(`/alert/disks/list`);\n};\n\nexport const SearchAlertLogs = (req: Alert.AlertLogSearch) => {\n    return http.post<ResPage<Alert.AlertLog>>(`/alert/logs/search`, req);\n};\n\nexport const CleanAlertLogs = () => {\n    return http.post<any>(`/alert/logs/clean`);\n};\n\nexport const ListClams = () => {\n    return http.get<Alert.ClamsDTO[]>(`/alert/clams/list`);\n};\n\nexport const ListCronJob = (req: Alert.CronJobReq) => {\n    return http.post<Alert.CronJobDTO[]>(`/alert/cronjob/list`, req);\n};\n\nexport const ListAlertConfigs = () => {\n    return http.post<Alert.AlertConfigInfo[]>(`/alert/config/info`);\n};\n\nexport const DeleteAlertConfig = (req: Alert.DelReq) => {\n    return http.post<any>(`/alert/config/del`, req);\n};\n\nexport const UpdateAlertConfig = (req: Alert.AlertConfigUpdateReq) => {\n    return http.post<any>(`/alert/config/update`, req);\n};\n\nexport const TestAlertConfig = (req: Alert.AlertConfigTest) => {\n    return http.post<any>(`/alert/config/test`, req);\n};\n\nexport const SyncAlertInfo = (req: Alert.AlertLogId) => {\n    return http.post<any>(`/xpack/alert/logs/sync`, req);\n};\n\nexport const SyncAlertAll = () => {\n    return http.post<any>(`/xpack/alert/logs/sync/all`);\n};\n\nexport const SyncOfflineAlert = () => {\n    return http.post<any>(`/core/xpack/alert/offline/sync`);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/app.ts",
    "content": "import http from '@/api';\nimport { ResPage } from '../interface';\nimport { App } from '../interface/app';\nimport { TimeoutEnum } from '@/enums/http-enum';\n\nexport const syncApp = (req: App.AppStoreSync) => {\n    return http.post('apps/sync/remote', req);\n};\n\nexport const syncLocalApp = (req: App.AppStoreSync) => {\n    return http.post('apps/sync/local', req);\n};\n\nexport const searchApp = (req: App.AppReq) => {\n    return http.post<App.AppResPage>('apps/search', req);\n};\n\nexport const getAppByKey = (key: string, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.get<App.AppDTO>('apps/' + key + `${params}`);\n};\n\nexport const getAppTags = () => {\n    return http.get<App.Tag[]>('apps/tags');\n};\n\nexport const getAppDetail = (appID: number, version: string, type: string, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.get<App.AppDetail>(`apps/detail/${appID}/${version}/${type}${params}`);\n};\n\nexport const getAppDetailByID = (id: number) => {\n    return http.get<App.AppDetail>(`apps/details/${id}`);\n};\n\nexport const installApp = (install: App.AppInstall) => {\n    return http.post<any>('apps/install', install);\n};\n\nexport const changePort = (params: App.ChangePort) => {\n    return http.post<any>('apps/installed/port/change', params);\n};\n\nexport const searchAppInstalled = (search: App.AppInstallSearch, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.post<ResPage<App.AppInstallDto>>(`apps/installed/search${params}`, search);\n};\n\nexport const listAppInstalled = () => {\n    return http.get<Array<App.AppInstalledInfo>>('apps/installed/list');\n};\n\nexport const getAppPort = (type: string, name: string) => {\n    return http.post<number>(`apps/installed/loadport`, { type: type, name: name });\n};\n\nexport const getAppConnInfo = (type: string, name: string) => {\n    return http.post<App.DatabaseConnInfo>(`apps/installed/conninfo`, { type: type, name: name });\n};\n\nexport const checkAppInstalled = (key: string, name: string) => {\n    return http.post<App.CheckInstalled>(`apps/installed/check`, { key: key, name: name });\n};\n\nexport const appInstalledDeleteCheck = (appInstallId: number, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.get<App.AppInstallResource[]>(`apps/installed/delete/check/${appInstallId}${params}`);\n};\n\nexport const getAppInstalled = (search: App.AppInstalledSearch) => {\n    return http.post<ResPage<App.AppInstalled>>('apps/installed/search', search);\n};\n\nexport const getAppInstalledByID = (installID: number, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.get<App.AppInstalledInfo>(`apps/installed/info/${installID}${params}`);\n};\n\nexport const installedOp = (op: App.AppInstalledOp, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.post<any>(`apps/installed/op${params}`, op, TimeoutEnum.T_40S);\n};\n\nexport const syncInstalledApp = () => {\n    return http.post<any>('apps/installed/sync', {});\n};\n\nexport const getAppService = (key: string | undefined, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.get<App.AppService[]>(`apps/services/${key}${params}`);\n};\n\nexport const getAppUpdateVersions = (req: App.AppUpdateVersionReq, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.post<any>(`apps/installed/update/versions${params}`, req);\n};\n\nexport const getAppDefaultConfig = (key: string, name: string) => {\n    return http.post<string>(`apps/installed/conf`, { type: key, name: name });\n};\n\nexport const getAppInstallParams = (id: number) => {\n    return http.get<App.AppConfig>(`apps/installed/params/${id}`);\n};\n\nexport const updateAppInstallParams = (req: any) => {\n    return http.post<any>(`apps/installed/params/update`, req);\n};\n\nexport const ignoreUpgrade = (req: App.AppIgnoreReq) => {\n    return http.post<any>(`apps/installed/ignore`, req);\n};\n\nexport const getIgnoredApp = () => {\n    return http.get<App.IgnoredApp>(`apps/ignored/detail`);\n};\n\nexport const cancelAppIgnore = (req: App.CancelAppIgnore) => {\n    return http.post(`apps/ignored/cancel`, req);\n};\n\nexport const updateInstallConfig = (req: App.AppConfigUpdate) => {\n    return http.post(`apps/installed/config/update`, req);\n};\n\nexport const updateAppInstallSort = (items: Array<{ installID: number; sortOrder: number }>) => {\n    return http.post(`apps/installed/sort/update`, { items });\n};\n\nexport const syncCutomAppStore = (req: App.AppStoreSync) => {\n    return http.post(`/custom/app/sync`, req);\n};\n\nexport const getCurrentNodeCustomAppConfig = () => {\n    return http.get<App.CustomAppStoreConfig>(`/custom/app/config`);\n};\n\nexport function getAppIconUrl(appKey: string, node?: string): string {\n    const baseURL = import.meta.env.VITE_API_URL as string;\n    const params = node ? `?operateNode=${node}` : '';\n    return `${baseURL}/apps/icon/${appKey}${params}`;\n}\n\nexport const installAppToNodes = (param: App.InstallAppToNodes) => {\n    return http.post(`/core/xpack/sync/app/install`, param);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/auth.ts",
    "content": "import { Login } from '@/api/interface/auth';\nimport http from '@/api';\n\nexport const loginApi = (params: Login.ReqLoginForm) => {\n    return http.post<Login.ResLogin>(`/core/auth/login`, params);\n};\n\nexport const mfaLoginApi = (params: Login.MFALoginForm) => {\n    return http.post<Login.ResLogin>(`/core/auth/mfalogin`, params);\n};\n\nexport const passkeyBeginApi = () => {\n    return http.post<Login.PasskeyBeginResponse>(`/core/auth/passkey/begin`);\n};\n\nexport const passkeyFinishApi = (params: Record<string, any>, sessionId: string) => {\n    return http.post<Login.ResLogin>(`/core/auth/passkey/finish`, params, undefined, { 'Passkey-Session': sessionId });\n};\n\nexport const getCaptcha = () => {\n    return http.get<Login.ResCaptcha>(`/core/auth/captcha`);\n};\n\nexport const logOutApi = () => {\n    return http.post<any>(`/core/auth/logout`);\n};\n\nexport const getLoginSetting = () => {\n    return http.get<Login.LoginSetting>('/core/auth/setting');\n};\n\nexport const getWelcomePage = () => {\n    return http.get<string>('/core/auth/welcome');\n};\n"
  },
  {
    "path": "frontend/src/api/modules/backup.ts",
    "content": "import http from '@/api';\nimport { deepCopy } from '@/utils/util';\nimport { Base64 } from 'js-base64';\nimport { ResPage } from '../interface';\nimport { Backup } from '../interface/backup';\nimport { TimeoutEnum } from '@/enums/http-enum';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\n// backup-agent\nexport const getLocalBackupDir = (node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.get<string>(`/backups/local${params}`);\n};\nexport const searchBackup = (params: Backup.SearchWithType) => {\n    return http.post<ResPage<Backup.BackupInfo>>(`/backups/search`, params);\n};\nexport const checkBackup = (params: Backup.BackupOperate) => {\n    let request = deepCopy(params) as Backup.BackupOperate;\n    if (request.accessKey) {\n        request.accessKey = Base64.encode(request.accessKey);\n    }\n    if (request.credential) {\n        request.credential = Base64.encode(request.credential);\n    }\n    if (!params.isPublic || !globalStore.isProductPro) {\n        return http.postLocalNode<Backup.CheckResult>(`/backups/conn/check`, request);\n    }\n    return http.post<Backup.CheckResult>(`/backups/conn/check`, request);\n};\nexport const listBucket = (params: Backup.ForBucket) => {\n    let request = deepCopy(params) as Backup.BackupOperate;\n    if (request.accessKey) {\n        request.accessKey = Base64.encode(request.accessKey);\n    }\n    if (request.credential) {\n        request.credential = Base64.encode(request.credential);\n    }\n    if (!params.isPublic || !globalStore.isProductPro) {\n        return http.postLocalNode('/backups/buckets', request, TimeoutEnum.T_40S);\n    }\n    return http.post('/backups/buckets', request, TimeoutEnum.T_40S);\n};\nexport const handleBackup = (params: Backup.Backup, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post(`/backups/backup${query}`, params, TimeoutEnum.T_10M);\n};\nexport const listBackupOptions = () => {\n    return http.get<Array<Backup.BackupOption>>(`/backups/options`);\n};\nexport const handleRecover = (params: Backup.Recover, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post(`/backups/recover${query}`, params, TimeoutEnum.T_10M);\n};\nexport const handleRecoverByUpload = (params: Backup.Recover, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post(`/backups/recover/byupload${query}`, params, TimeoutEnum.T_10M);\n};\nexport const downloadBackupRecord = (params: Backup.RecordDownload, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post<string>(`/backups/record/download${query}`, params, TimeoutEnum.T_10M);\n};\nexport const deleteBackupRecord = (params: { ids: number[] }, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post(`/backups/record/del${query}`, params);\n};\nexport const updateRecordDescription = (id: Number, description: String, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post(`/backups/record/description/update${query}`, { id: id, description: description });\n};\nexport const uploadByRecover = (filePath: string, targetDir: String, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post(`/backups/upload${query}`, { filePath: filePath, targetDir: targetDir });\n};\nexport const searchBackupRecords = (params: Backup.SearchBackupRecord, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post<ResPage<Backup.RecordInfo>>(`/backups/record/search${query}`, params, TimeoutEnum.T_5M);\n};\nexport const loadRecordSize = (param: Backup.SearchForSize, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post<Array<Backup.RecordFileSize>>(`/backups/record/size${query}`, param);\n};\nexport const searchBackupRecordsByCronjob = (params: Backup.SearchBackupRecordByCronjob) => {\n    return http.post<ResPage<Backup.RecordInfo>>(`/backups/record/search/bycronjob`, params, TimeoutEnum.T_5M);\n};\nexport const getFilesFromBackup = (id: number) => {\n    return http.post<Array<any>>(`/backups/search/files`, { id: id });\n};\n\n// backup-core\nexport const refreshToken = (params: { id: number; name: string; isPublic: boolean }) => {\n    if (!params.isPublic) {\n        return http.post('/backups/refresh/token', { id: params.id });\n    }\n    return http.post('/core/backups/refresh/token', { name: params.name });\n};\nexport const getClientInfo = (clientType: string) => {\n    return http.get<Backup.ClientInfo>(`/core/backups/client/${clientType}`);\n};\nexport const addBackup = (params: Backup.BackupOperate) => {\n    let request = deepCopy(params) as Backup.BackupOperate;\n    if (request.accessKey) {\n        request.accessKey = Base64.encode(request.accessKey);\n    }\n    if (request.credential) {\n        request.credential = Base64.encode(request.credential);\n    }\n    let urlItem = '/core/backups';\n    if (!params.isPublic) {\n        urlItem = '/backups';\n    }\n    return http.post(urlItem, request, TimeoutEnum.T_60S);\n};\nexport const editBackup = (params: Backup.BackupOperate) => {\n    let request = deepCopy(params) as Backup.BackupOperate;\n    if (request.accessKey) {\n        request.accessKey = Base64.encode(request.accessKey);\n    }\n    if (request.credential) {\n        request.credential = Base64.encode(request.credential);\n    }\n    let urlItem = '/core/backups/update';\n    if (!params.isPublic) {\n        urlItem = '/backups/update';\n    }\n    return http.post(urlItem, request, TimeoutEnum.T_60S);\n};\nexport const deleteBackup = (params: { id: number; name: string; isPublic: boolean }) => {\n    if (!params.isPublic) {\n        return http.post('/backups/del', { id: params.id });\n    }\n    return http.post('/core/backups/del', { name: params.name });\n};\n"
  },
  {
    "path": "frontend/src/api/modules/command.ts",
    "content": "import http from '@/api';\nimport { ResPage, SearchWithPage } from '../interface';\nimport { Command } from '../interface/command';\n\nexport const getCommandList = (type: string) => {\n    return http.post<Array<Command.CommandInfo>>(`/core/commands/list`, { type: type });\n};\nexport const exportCommands = () => {\n    return http.post<string>(`/core/commands/export`);\n};\nexport const uploadCommands = (params: FormData) => {\n    return http.upload<Array<Command.CommandInfo>>(`/core/commands/upload`, params);\n};\nexport const importCommands = (list: Array<Command.CommandOperate>) => {\n    return http.post(`/core/commands/import`, { items: list });\n};\nexport const getCommandPage = (params: SearchWithPage) => {\n    return http.post<ResPage<Command.CommandInfo>>(`/core/commands/search`, params);\n};\nexport const getCommandTree = (type: string) => {\n    return http.post<any>(`/core/commands/tree`, { type: type });\n};\nexport const addCommand = (params: Command.CommandOperate) => {\n    return http.post<Command.CommandOperate>(`/core/commands`, params);\n};\nexport const editCommand = (params: Command.CommandOperate) => {\n    return http.post(`/core/commands/update`, params);\n};\nexport const deleteCommand = (params: { ids: number[] }) => {\n    return http.post(`/core/commands/del`, params);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/container.ts",
    "content": "import http from '@/api';\nimport { ResPage, SearchWithPage } from '../interface';\nimport { Container } from '../interface/container';\nimport { TimeoutEnum } from '@/enums/http-enum';\n\nexport const searchContainer = (params: Container.ContainerSearch) => {\n    return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params, TimeoutEnum.T_40S);\n};\nexport const listContainer = () => {\n    return http.post<Array<Container.ContainerOption>>(`/containers/list`, {});\n};\nexport const listContainerByImage = (image: string) => {\n    return http.post<Array<Container.ContainerOption>>(`/containers/list/byimage`, { name: image });\n};\nexport const loadContainerUsers = (name: string) => {\n    return http.post<Array<string>>(`/containers/users`, { name: name });\n};\nexport const listContainerFiles = (params: Container.ContainerFileReq) => {\n    return http.post<Array<Container.ContainerFileInfo>>(`/containers/files/search`, params, TimeoutEnum.T_40S);\n};\nexport const uploadContainerFile = (params: FormData) => {\n    return http.upload(`/containers/files/upload`, params, {\n        headers: { 'Content-Type': 'multipart/form-data' },\n        timeout: TimeoutEnum.T_5M,\n    });\n};\nexport const getContainerFileContent = (params: Container.ContainerFileReq) => {\n    return http.post<Container.ContainerFileContent>(`/containers/files/content`, params, TimeoutEnum.T_40S);\n};\nexport const getContainerFileSize = (params: Container.ContainerFileReq) => {\n    return http.post<number>(`/containers/files/size`, params, TimeoutEnum.T_40S);\n};\nexport const deleteContainerFile = (params: { containerID: string; paths: string[] }) => {\n    return http.post(`/containers/files/del`, params, TimeoutEnum.T_40S);\n};\nexport const downloadContainerFile = (params: { containerID: string; path: string }) => {\n    return http.download<BlobPart>(`/containers/files/download`, params, {\n        responseType: 'blob',\n        timeout: TimeoutEnum.T_40S,\n    });\n};\nexport const loadContainerStatus = () => {\n    return http.get<Container.ContainerStatus>(`/containers/status`);\n};\nexport const loadResourceLimit = () => {\n    return http.get<Container.ResourceLimit>(`/containers/limit`);\n};\nexport const createContainer = (params: Container.ContainerHelper) => {\n    return http.post(`/containers`, params, TimeoutEnum.T_10M);\n};\nexport const updateContainer = (params: Container.ContainerHelper) => {\n    return http.post(`/containers/update`, params, TimeoutEnum.T_10M);\n};\nexport const upgradeContainer = (params: Container.ContainerUpgrade) => {\n    return http.post(`/containers/upgrade`, params, TimeoutEnum.T_10M);\n};\nexport const commitContainer = (params: Container.ContainerCommit) => {\n    return http.post(`/containers/commit`, params);\n};\nexport const loadContainerInfo = (name: string) => {\n    return http.post<Container.ContainerHelper>(`/containers/info`, { name: name });\n};\nexport const cleanComposeLog = (composeName: string, composePath: string, operateNode?: string) => {\n    const params = operateNode ? `?operateNode=${operateNode}` : '';\n    return http.post(\n        `/containers/compose/clean/log${params}`,\n        { name: composeName, path: composePath },\n        TimeoutEnum.T_60S,\n    );\n};\nexport const cleanContainerLog = (containerName: string, operateNode?: string) => {\n    const params = operateNode ? `?operateNode=${operateNode}` : '';\n    return http.post(`/containers/clean/log${params}`, { name: containerName }, TimeoutEnum.T_60S);\n};\nexport const containerItemStats = (containerID: string) => {\n    return http.post<Container.ContainerItemStats>(`/containers/item/stats`, { name: containerID }, TimeoutEnum.T_60S);\n};\nexport const containerListStats = () => {\n    return http.get<Array<Container.ContainerListStats>>(`/containers/list/stats`);\n};\nexport const containerStats = (id: string) => {\n    return http.get<Container.ContainerStats>(`/containers/stats/${id}`);\n};\nexport const containerRename = (params: Container.ContainerRename) => {\n    return http.post(`/containers/rename`, params);\n};\nexport const containerOperator = (params: Container.ContainerOperate) => {\n    return http.post(`/containers/operate`, params);\n};\nexport const containerPrune = (params: Container.ContainerPrune) => {\n    return http.post(`/containers/prune`, params);\n};\nexport const inspect = (params: Container.ContainerInspect) => {\n    return http.post<string>(`/containers/inspect`, params);\n};\n\nexport const DownloadFile = (params: Container.ContainerLogInfo) => {\n    return http.download<BlobPart>('/containers/download/log', params, {\n        responseType: 'blob',\n        timeout: TimeoutEnum.T_40S,\n    });\n};\n\n// image\nexport const searchImage = (params: Container.ImageSearch) => {\n    return http.post<ResPage<Container.ImageInfo>>(`/containers/image/search`, params);\n};\nexport const listAllImage = () => {\n    return http.get<Array<Container.ImageInfo>>(`/containers/image/all`);\n};\nexport const listImage = () => {\n    return http.get<Array<Container.Options>>(`/containers/image`);\n};\nexport const imageBuild = (params: Container.ImageBuild) => {\n    return http.post<string>(`/containers/image/build`, params);\n};\nexport const imagePull = (params: Container.ImagePull) => {\n    return http.post<string>(`/containers/image/pull`, params);\n};\nexport const imagePush = (params: Container.ImagePush) => {\n    return http.post<string>(`/containers/image/push`, params);\n};\nexport const imageLoad = (params: Container.ImageLoad) => {\n    return http.post(`/containers/image/load`, params, TimeoutEnum.T_10M);\n};\nexport const imageSave = (params: Container.ImageSave) => {\n    return http.post(`/containers/image/save`, params, TimeoutEnum.T_10M);\n};\nexport const imageTag = (params: Container.ImageTag) => {\n    return http.post(`/containers/image/tag`, params);\n};\nexport const imageRemove = (params: Container.BatchDelete) => {\n    return http.post(`/containers/image/remove`, params);\n};\n\n// network\nexport const searchNetwork = (params: SearchWithPage) => {\n    return http.post<ResPage<Container.NetworkInfo>>(`/containers/network/search`, params);\n};\nexport const listNetwork = () => {\n    return http.get<Array<Container.Options>>(`/containers/network`);\n};\nexport const deleteNetwork = (params: Container.BatchDelete) => {\n    return http.post(`/containers/network/del`, params);\n};\nexport const createNetwork = (params: Container.NetworkCreate) => {\n    return http.post(`/containers/network`, params);\n};\n\n// volume\nexport const searchVolume = (params: SearchWithPage) => {\n    return http.post<ResPage<Container.VolumeInfo>>(`/containers/volume/search`, params);\n};\nexport const listVolume = () => {\n    return http.get<Array<Container.Options>>(`/containers/volume`);\n};\nexport const deleteVolume = (params: Container.BatchDelete) => {\n    return http.post(`/containers/volume/del`, params);\n};\nexport const createVolume = (params: Container.VolumeCreate) => {\n    return http.post(`/containers/volume`, params);\n};\n\n// repo\nexport const checkRepoStatus = (id: number) => {\n    return http.post(`/containers/repo/status`, { id: id }, TimeoutEnum.T_40S);\n};\nexport const searchImageRepo = (params: SearchWithPage) => {\n    return http.post<ResPage<Container.RepoInfo>>(`/containers/repo/search`, params);\n};\nexport const listImageRepo = () => {\n    return http.get<Container.RepoOptions>(`/containers/repo`);\n};\nexport const createImageRepo = (params: Container.RepoCreate) => {\n    return http.post(`/containers/repo`, params, TimeoutEnum.T_40S);\n};\nexport const updateImageRepo = (params: Container.RepoUpdate) => {\n    return http.post(`/containers/repo/update`, params, TimeoutEnum.T_40S);\n};\nexport const deleteImageRepo = (id: Number) => {\n    return http.post(`/containers/repo/del`, { id: id }, TimeoutEnum.T_40S);\n};\n\n// composeTemplate\nexport const searchComposeTemplate = (params: SearchWithPage) => {\n    return http.post<ResPage<Container.TemplateInfo>>(`/containers/template/search`, params);\n};\nexport const listComposeTemplate = () => {\n    return http.get<Container.TemplateInfo>(`/containers/template`);\n};\nexport const deleteComposeTemplate = (params: { ids: number[] }) => {\n    return http.post(`/containers/template/del`, params);\n};\nexport const createComposeTemplate = (params: Container.TemplateCreate) => {\n    return http.post(`/containers/template`, params);\n};\nexport const batchComposeTemplate = (templates: Array<Container.TemplateCreate>) => {\n    return http.post(`/containers/template/batch`, { templates: templates });\n};\nexport const updateComposeTemplate = (params: Container.TemplateUpdate) => {\n    return http.post(`/containers/template/update`, params);\n};\n\n// compose\nexport const searchCompose = (params: SearchWithPage) => {\n    return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);\n};\nexport const upCompose = (params: Container.ComposeCreate) => {\n    return http.post<string>(`/containers/compose`, params);\n};\nexport const testCompose = (params: Container.ComposeCreate) => {\n    return http.post<boolean>(`/containers/compose/test`, params);\n};\nexport const composeOperate = (params: Container.ComposeOperation) => {\n    return http.post(`/containers/compose/operate`, params);\n};\nexport const composeUpdate = (params: Container.ComposeUpdate) => {\n    return http.post(`/containers/compose/update`, params, TimeoutEnum.T_10M);\n};\n\n// docker\nexport const dockerOperate = (operation: string) => {\n    return http.post(`/containers/docker/operate`, { operation: operation }, TimeoutEnum.T_3M);\n};\nexport const loadComposeEnv = (path: string) => {\n    return http.post<string>(`/containers/compose/env`, { path: path });\n};\nexport const loadDaemonJson = () => {\n    return http.get<Container.DaemonJsonConf>(`/containers/daemonjson`);\n};\nexport const loadDaemonJsonFile = () => {\n    return http.get<string>(`/containers/daemonjson/file`);\n};\nexport const loadDockerStatus = () => {\n    return http.get<Container.DockerStatus>(`/containers/docker/status`);\n};\nexport const updateDaemonJson = (key: string, value: string) => {\n    return http.post(`/containers/daemonjson/update`, { key: key, value: value }, TimeoutEnum.T_3M);\n};\nexport const updateLogOption = (maxSize: string, maxFile: string) => {\n    return http.post(`/containers/logoption/update`, { logMaxSize: maxSize, logMaxFile: maxFile }, TimeoutEnum.T_3M);\n};\nexport const updateIpv6Option = (fixedCidrV6: string, ip6Tables: boolean, experimental: boolean) => {\n    return http.post(\n        `/containers/ipv6option/update`,\n        { fixedCidrV6: fixedCidrV6, ip6Tables: ip6Tables, experimental: experimental },\n        TimeoutEnum.T_3M,\n    );\n};\nexport const updateDaemonJsonByfile = (params: Container.DaemonJsonUpdateByFile) => {\n    return http.post(`/containers/daemonjson/update/byfile`, params, TimeoutEnum.T_3M);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/cronjob.ts",
    "content": "import http from '@/api';\nimport { ResPage, SearchWithPage } from '../interface';\nimport { Cronjob } from '../interface/cronjob';\nimport { TimeoutEnum } from '@/enums/http-enum';\n\nexport const searchCronjobPage = (params: Cronjob.Search, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post<ResPage<Cronjob.CronjobInfo>>(`/cronjobs/search${query}`, params);\n};\n\nexport const loadNextHandle = (spec: string) => {\n    return http.post<Array<String>>(`/cronjobs/next`, { spec: spec });\n};\n\nexport const editCronjobGroup = (id: number, groupID: number) => {\n    return http.post(`/cronjobs/group/update`, { id: id, groupID: groupID });\n};\n\nexport const importCronjob = (trans: Array<Cronjob.CronjobTrans>) => {\n    return http.post('cronjobs/import', { cronjobs: trans }, TimeoutEnum.T_60S);\n};\nexport const exportCronjob = (params: { ids: Array<number> }) => {\n    return http.download<BlobPart>('cronjobs/export', params, { responseType: 'blob', timeout: TimeoutEnum.T_40S });\n};\n\nexport const loadCronjobInfo = (id: number) => {\n    return http.post<Cronjob.CronjobOperate>(`/cronjobs/load/info`, { id: id });\n};\n\nexport const loadScriptOptions = () => {\n    return http.get<Array<Cronjob.ScriptOptions>>(`/cronjobs/script/options`);\n};\n\nexport const getRecordLog = (id: number) => {\n    return http.post<string>(`/cronjobs/records/log`, { id: id });\n};\n\nexport const addCronjob = (params: Cronjob.CronjobOperate) => {\n    return http.post<Cronjob.CronjobOperate>(`/cronjobs`, params);\n};\n\nexport const editCronjob = (params: Cronjob.CronjobOperate) => {\n    return http.post(`/cronjobs/update`, params);\n};\n\nexport const deleteCronjob = (params: Cronjob.CronjobDelete) => {\n    return http.post(`/cronjobs/del`, params);\n};\n\nexport const searchRecords = (params: Cronjob.SearchRecord) => {\n    return http.post<ResPage<Cronjob.Record>>(`cronjobs/search/records`, params);\n};\n\nexport const stopCronjob = (id: number) => {\n    return http.post(`cronjobs/stop`, { id: id });\n};\n\nexport const cleanRecords = (id: number, cleanData: boolean, cleanRemoteData: boolean) => {\n    return http.post(`cronjobs/records/clean`, { cronjobID: id, cleanData: cleanData, cleanRemoteData });\n};\n\nexport const updateStatus = (params: Cronjob.UpdateStatus, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post(`cronjobs/status${query}`, params);\n};\n\nexport const handleOnce = (id: number, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post(`cronjobs/handle${query}`, { id: id });\n};\n\nexport const searchScript = (params: SearchWithPage) => {\n    return http.post<ResPage<Cronjob.ScriptInfo>>(`/core/script/search`, params);\n};\nexport const addScript = (params: Cronjob.ScriptOperate) => {\n    return http.post(`/core/script`, params);\n};\nexport const syncScript = (taskID: string) => {\n    return http.post(`/core/script/sync`, { taskID: taskID }, TimeoutEnum.T_60S);\n};\nexport const editScript = (params: Cronjob.ScriptOperate) => {\n    return http.post(`/core/script/update`, params);\n};\nexport const deleteScript = (ids: Array<number>) => {\n    return http.post(`/core/script/del`, { ids: ids });\n};\n"
  },
  {
    "path": "frontend/src/api/modules/dashboard.ts",
    "content": "import http from '@/api';\nimport { Dashboard } from '../interface/dashboard';\n\nexport const loadOsInfo = () => {\n    return http.get<Dashboard.OsInfo>(`/dashboard/base/os`);\n};\n\nexport const loadQuickOption = () => {\n    return http.get<Array<Dashboard.QuickJump>>(`/dashboard/quick/option`);\n};\nexport const changeQuick = (quicks: Array<Dashboard.QuickJump>) => {\n    return http.post(`/dashboard/quick/change`, { quicks: quicks });\n};\nexport const loadAppLauncher = () => {\n    return http.get<Array<Dashboard.AppLauncher>>(`/dashboard/app/launcher`);\n};\nexport const loadAppLauncherOption = (filter: string) => {\n    return http.post<Array<Dashboard.AppLauncherOption>>(`/dashboard/app/launcher/option`, { filter: filter });\n};\nexport const changeLauncherStatus = (key: string, val: string) => {\n    return http.post(`/dashboard/app/launcher/show`, { key: key, value: val });\n};\n\nexport const loadBaseInfo = (ioOption: string, netOption: string) => {\n    return http.get<Dashboard.BaseInfo>(`/dashboard/base/${ioOption}/${netOption}`);\n};\n\nexport const loadCurrentInfo = (ioOption: string, netOption: string) => {\n    return http.get<Dashboard.CurrentInfo>(`/dashboard/current/${ioOption}/${netOption}`);\n};\n\nexport const loadTopCPU = () => {\n    return http.get<Array<Dashboard.Process>>(`/dashboard/current/top/cpu`);\n};\n\nexport const loadTopMem = () => {\n    return http.get<Array<Dashboard.Process>>(`/dashboard/current/top/mem`);\n};\n\nexport const systemRestart = (operation: string) => {\n    return http.post(`/dashboard/system/restart/${operation}`);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/database.ts",
    "content": "import http from '@/api';\nimport { deepCopy } from '@/utils/util';\nimport { Base64 } from 'js-base64';\nimport { ResPage, DescriptionUpdate } from '../interface';\nimport { Database } from '../interface/database';\nimport { TimeoutEnum } from '@/enums/http-enum';\n\n// common\nexport const loadDBBaseInfo = (type: string, database: string) => {\n    return http.post<Database.BaseInfo>(`/databases/common/info`, { type: type, name: database });\n};\nexport const loadDBFile = (type: string, database: string) => {\n    return http.post<string>(`/databases/common/load/file`, { type: type, name: database });\n};\nexport const updateDBFile = (params: Database.DBConfUpdate) => {\n    return http.post(`/databases/common/update/conf`, params);\n};\n\n// pg\nexport const addPostgresqlDB = (params: Database.PostgresqlDBCreate) => {\n    let request = deepCopy(params) as Database.PostgresqlDBCreate;\n    if (request.password) {\n        request.password = Base64.encode(request.password);\n    }\n    return http.post(`/databases/pg`, request, TimeoutEnum.T_40S);\n};\nexport const bindPostgresqlUser = (params: Database.PgBind) => {\n    return http.post(`/databases/pg/bind`, params, TimeoutEnum.T_40S);\n};\nexport const changePrivileges = (params: Database.PgChangePrivileges) => {\n    return http.post(`/databases/pg/privileges`, params, TimeoutEnum.T_40S);\n};\nexport const searchPostgresqlDBs = (params: Database.SearchDBWithPage, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post<ResPage<Database.PostgresqlDBInfo>>(`/databases/pg/search${query}`, params);\n};\nexport const updatePostgresqlDescription = (params: DescriptionUpdate) => {\n    return http.post(`/databases/pg/description`, params);\n};\nexport const loadPgFromRemote = (database: string) => {\n    return http.post(`/databases/pg/${database}/load`);\n};\nexport const deleteCheckPostgresqlDB = (params: Database.PostgresqlDBDeleteCheck) => {\n    return http.post<Database.DBResource[]>(`/databases/pg/del/check`, params, TimeoutEnum.T_40S);\n};\nexport const updatePostgresqlPassword = (params: Database.ChangeInfo) => {\n    let request = deepCopy(params) as Database.ChangeInfo;\n    if (request.value) {\n        request.value = Base64.encode(request.value);\n    }\n    return http.post(`/databases/pg/password`, request, TimeoutEnum.T_40S);\n};\nexport const deletePostgresqlDB = (params: Database.PostgresqlDBDelete) => {\n    return http.post(`/databases/pg/del`, params, TimeoutEnum.T_40S);\n};\n\n// mysql\nexport const searchMysqlDBs = (params: Database.SearchDBWithPage, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post<ResPage<Database.MysqlDBInfo>>(`/databases/search${query}`, params);\n};\nexport const addMysqlDB = (params: Database.MysqlDBCreate) => {\n    let request = deepCopy(params) as Database.MysqlDBCreate;\n    if (request.password) {\n        request.password = Base64.encode(request.password);\n    }\n    return http.post(`/databases`, request);\n};\nexport const bindUser = (params: Database.BindUser) => {\n    let request = deepCopy(params) as Database.BindUser;\n    if (request.password) {\n        request.password = Base64.encode(request.password);\n    }\n    return http.post(`/databases/bind`, request);\n};\nexport const loadDBFromRemote = (params: Database.MysqlLoadDB) => {\n    return http.post(`/databases/load`, params);\n};\nexport const updateMysqlAccess = (params: Database.ChangeInfo) => {\n    return http.post(`/databases/change/access`, params);\n};\nexport const updateMysqlPassword = (params: Database.ChangeInfo) => {\n    let request = deepCopy(params) as Database.ChangeInfo;\n    if (request.value) {\n        request.value = Base64.encode(request.value);\n    }\n    return http.post(`/databases/change/password`, request);\n};\nexport const updateMysqlDescription = (params: DescriptionUpdate) => {\n    return http.post(`/databases/description/update`, params);\n};\nexport const updateMysqlVariables = (params: Database.VariablesUpdate) => {\n    return http.post(`/databases/variables/update`, params);\n};\nexport const deleteCheckMysqlDB = (params: Database.MysqlDBDeleteCheck) => {\n    return http.post<Array<string>>(`/databases/del/check`, params);\n};\nexport const deleteMysqlDB = (params: Database.MysqlDBDelete) => {\n    return http.post(`/databases/del`, params);\n};\n\nexport const loadMysqlVariables = (type: string, database: string) => {\n    return http.post<Database.MysqlVariables>(`/databases/variables`, { type: type, name: database });\n};\nexport const loadMysqlStatus = (type: string, database: string) => {\n    return http.post<Database.MysqlStatus>(`/databases/status`, { type: type, name: database });\n};\nexport const loadRemoteAccess = (type: string, database: string) => {\n    return http.post<boolean>(`/databases/remote`, { type: type, name: database });\n};\nexport const loadFormatCollations = (database: string) => {\n    return http.post<Array<Database.FormatCollationOption>>(`/databases/format/options`, { name: database });\n};\n\n// redis\nexport const loadRedisStatus = (type: string, database: string) => {\n    return http.post<Database.RedisStatus>(`/databases/redis/status`, { type: type, name: database });\n};\nexport const loadRedisConf = (type: string, database: string) => {\n    return http.post<Database.RedisConf>(`/databases/redis/conf`, { type: type, name: database });\n};\nexport const redisPersistenceConf = (type: string, database: string) => {\n    return http.post<Database.RedisPersistenceConf>(`/databases/redis/persistence/conf`, {\n        type: type,\n        name: database,\n    });\n};\nexport const checkRedisCli = () => {\n    return http.get<boolean>(`/databases/redis/check`);\n};\nexport const installRedisCli = () => {\n    return http.post(`/databases/redis/install/cli`, {}, TimeoutEnum.T_5M);\n};\nexport const changeRedisPassword = (database: string, password: string) => {\n    if (password) {\n        password = Base64.encode(password);\n    }\n    return http.post(`/databases/redis/password`, { database: database, value: password });\n};\nexport const updateRedisPersistenceConf = (params: Database.RedisConfPersistenceUpdate) => {\n    return http.post(`/databases/redis/persistence/update`, params);\n};\nexport const updateRedisConf = (params: Database.RedisConfUpdate) => {\n    return http.post(`/databases/redis/conf/update`, params);\n};\nexport const updateRedisConfByFile = (params: Database.DBConfUpdate) => {\n    return http.post(`/databases/redis/conffile/update`, params);\n};\n\n// database\nexport const getDatabase = (name: string) => {\n    return http.get<Database.DatabaseInfo>(`/databases/db/${name}`);\n};\nexport const searchDatabases = (params: Database.SearchDatabasePage) => {\n    return http.post<ResPage<Database.DatabaseInfo>>(`/databases/db/search`, params);\n};\nexport const listDatabases = (type: string, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.get<Array<Database.DatabaseOption>>(`/databases/db/list/${type}${query}`);\n};\nexport const listDbItems = (type: string) => {\n    return http.get<Array<Database.DbItem>>(`/databases/db/item/${type}`);\n};\nexport const checkDatabase = (params: Database.DatabaseCreate) => {\n    let request = deepCopy(params) as Database.DatabaseCreate;\n    if (request.ssl) {\n        request.clientKey = Base64.encode(request.clientKey);\n        request.clientCert = Base64.encode(request.clientCert);\n        request.rootCert = Base64.encode(request.rootCert);\n    }\n\n    return http.post<boolean>(`/databases/db/check`, request, TimeoutEnum.T_60S);\n};\nexport const addDatabase = (params: Database.DatabaseCreate) => {\n    let request = deepCopy(params) as Database.DatabaseCreate;\n    if (request.ssl) {\n        request.clientKey = Base64.encode(request.clientKey);\n        request.clientCert = Base64.encode(request.clientCert);\n        request.rootCert = Base64.encode(request.rootCert);\n    }\n\n    return http.post(`/databases/db`, request, TimeoutEnum.T_60S);\n};\nexport const editDatabase = (params: Database.DatabaseUpdate) => {\n    let request = deepCopy(params) as Database.DatabaseCreate;\n    if (request.ssl) {\n        request.clientKey = Base64.encode(request.clientKey);\n        request.clientCert = Base64.encode(request.clientCert);\n        request.rootCert = Base64.encode(request.rootCert);\n    }\n\n    return http.post(`/databases/db/update`, request, TimeoutEnum.T_60S);\n};\nexport const deleteCheckDatabase = (id: number) => {\n    return http.post<Database.DBResource[]>(`/databases/db/del/check`, { id: id });\n};\nexport const deleteDatabase = (params: Database.DatabaseDelete) => {\n    return http.post(`/databases/db/del`, params);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/files.ts",
    "content": "import { File } from '@/api/interface/file';\nimport http from '@/api';\nimport { AxiosRequestConfig } from 'axios';\nimport { ResPage } from '../interface';\nimport { TimeoutEnum } from '@/enums/http-enum';\nimport { ReqPage } from '@/api/interface';\nimport { Dashboard } from '@/api/interface/dashboard';\n\nexport const getFilesList = (params: File.ReqFile) => {\n    return http.post<File.File>('files/search', params, TimeoutEnum.T_5M);\n};\n\nexport const getFilesListByNode = (params: File.ReqNodeFile) => {\n    return http.post<File.File>('files/search?operateNode=' + params.node, params, TimeoutEnum.T_5M);\n};\n\nexport const getUploadList = (params: File.SearchUploadInfo) => {\n    return http.post<ResPage<File.UploadInfo>>('files/upload/search', params);\n};\n\nexport const getFilesTree = (params: File.ReqFile) => {\n    return http.post<File.FileTree[]>('files/tree', params);\n};\n\nexport const createFile = (form: File.FileCreate) => {\n    return http.post<File.File>('files', form);\n};\n\nexport const deleteFile = (form: File.FileDelete) => {\n    return http.post<File.File>('files/del', form);\n};\n\nexport const deleteFileByNode = (form: File.FileDelete, node: string) => {\n    return http.post<File.File>('files/del?operateNode=' + node, form);\n};\n\nexport const batchDeleteFile = (form: File.FileBatchDelete) => {\n    return http.post('files/batch/del', form);\n};\n\nexport const changeFileMode = (form: File.FileCreate) => {\n    return http.post<File.File>('files/mode', form, TimeoutEnum.T_5M);\n};\n\nexport const compressFile = (form: File.FileCompress) => {\n    return http.post<File.File>('files/compress', form, TimeoutEnum.T_10M);\n};\n\nexport const deCompressFile = (form: File.FileDeCompress) => {\n    return http.post<File.File>('files/decompress', form, TimeoutEnum.T_10M);\n};\n\nexport const getFileContent = (params: File.ReqFile) => {\n    return http.post<File.File>('files/content', params);\n};\n\nexport const getPreviewContent = (params: File.PreviewContentReq) => {\n    return http.post<File.File>('files/preview', params, TimeoutEnum.T_5M);\n};\n\nexport const saveFileContent = (params: File.FileEdit) => {\n    return http.post<File.File>('files/save', params);\n};\n\nexport const checkFile = (path: string, withInit: boolean) => {\n    return http.post<boolean>('files/check', { path: path, withInit: withInit });\n};\n\nexport const uploadFileData = (params: FormData, config: AxiosRequestConfig) => {\n    return http.upload<File.File>('files/upload', params, config);\n};\n\nexport const batchCheckFiles = (paths: string[]) => {\n    return http.post<File.ExistFileInfo[]>('files/batch/check', { paths: paths }, TimeoutEnum.T_5M);\n};\n\nexport const batchGetFileRemarks = (paths: string[]) => {\n    return http.post<File.FileRemarksRes>('files/remarks', { paths: paths }, TimeoutEnum.T_5M);\n};\n\nexport const setFileRemark = (params: File.FileRemarkUpdate) => {\n    return http.post('files/remark', params);\n};\n\nexport const chunkUploadFileData = (params: FormData, config: AxiosRequestConfig) => {\n    return http.upload<File.File>('files/chunkupload', params, config);\n};\n\nexport const renameRile = (params: File.FileRename) => {\n    return http.post<File.File>('files/rename', params);\n};\n\nexport const changeOwner = (params: File.FileOwner) => {\n    return http.post<File.File>('files/owner', params);\n};\n\nexport const wgetFile = (params: File.FileWget) => {\n    return http.post<File.FileWgetRes>('files/wget', params);\n};\n\nexport const moveFile = (params: File.FileMove) => {\n    return http.post<File.File>('files/move', params, TimeoutEnum.T_5M);\n};\n\nexport const downloadFile = (params: File.FileDownload) => {\n    return http.download<BlobPart>('files/download', params, { responseType: 'blob', timeout: TimeoutEnum.T_40S });\n};\n\nexport const computeDirSize = (params: File.DirSizeReq) => {\n    return http.post<File.DirSizeRes>('files/size', params, TimeoutEnum.T_5M);\n};\n\nexport const computeDepthDirSize = (params: File.DirSizeReq) => {\n    return http.post<File.DepthDirSizeRes[]>('files/depth/size', params, TimeoutEnum.T_5M);\n};\n\nexport const fileWgetKeys = () => {\n    return http.get<File.FileKeys>('files/wget/process/keys');\n};\n\nexport const getRecycleList = (params: ReqPage) => {\n    return http.post<ResPage<File.RecycleBin>>('files/recycle/search', params);\n};\n\nexport const reduceFile = (params: File.RecycleBinReduce) => {\n    return http.post<any>('files/recycle/reduce', params);\n};\n\nexport const clearRecycle = () => {\n    return http.post<any>('files/recycle/clear');\n};\n\nexport const searchFavorite = (params: ReqPage) => {\n    return http.post<ResPage<File.Favorite>>('files/favorite/search', params);\n};\n\nexport const addFavorite = (path: string) => {\n    return http.post<any>('files/favorite', { path: path });\n};\n\nexport const readByLine = (req: File.FileReadByLine, operateNode?: string) => {\n    const params = operateNode ? `?operateNode=${operateNode}` : '';\n    return http.post<any>(`files/read${params}`, req, TimeoutEnum.T_40S);\n};\n\nexport const removeFavorite = (id: number) => {\n    return http.post<any>('files/favorite/del', { id: id });\n};\n\nexport const batchChangeRole = (params: File.FileRole) => {\n    return http.post<any>('files/batch/role', params, TimeoutEnum.T_5M);\n};\n\nexport const getRecycleStatus = () => {\n    return http.get<string>('files/recycle/status');\n};\n\nexport const getRecycleStatusByNode = (node: string) => {\n    return http.get<string>('files/recycle/status?operateNode=' + node);\n};\n\nexport const getPathByType = (pathType: string) => {\n    return http.get<string>(`files/path/${pathType}`);\n};\n\nexport const searchHostMount = () => {\n    return http.post<Dashboard.DiskInfo[]>(`/files/mount`);\n};\n\nexport const searchUserGroup = () => {\n    return http.post<File.UserGroupResponse>(`/files/user/group`);\n};\n\nexport const convertFiles = (params: File.ConvertFileRequest) => {\n    return http.post<File.ConvertFile>('files/convert', params, TimeoutEnum.T_5M);\n};\n\nexport const convertLogs = (params: ReqPage) => {\n    return http.post<ResPage<File.ConvertLogResponse>>('files/convert/log', params, TimeoutEnum.T_5M);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/group.ts",
    "content": "import { Group } from '../interface/group';\nimport http from '@/api';\n\nexport const getGroupList = (type: string) => {\n    return http.post<Array<Group.GroupInfo>>(`/core/groups/search`, { type: type });\n};\nexport const createGroup = (params: Group.GroupCreate) => {\n    return http.post<Group.GroupCreate>(`/core/groups`, params);\n};\nexport const updateGroup = (params: Group.GroupUpdate) => {\n    return http.post(`/core/groups/update`, params);\n};\nexport const deleteGroup = (id: number) => {\n    return http.post(`/core/groups/del`, { id: id });\n};\n\nexport const getAgentGroupList = (type: string) => {\n    return http.post<Array<Group.GroupInfo>>(`/groups/search`, { type: type });\n};\nexport const createAgentGroup = (params: Group.GroupCreate) => {\n    return http.post<Group.GroupCreate>(`/groups`, params);\n};\nexport const updateAgentGroup = (params: Group.GroupUpdate) => {\n    return http.post(`/groups/update`, params);\n};\nexport const deleteAgentGroup = (id: number) => {\n    return http.post(`/groups/del`, { id: id });\n};\n"
  },
  {
    "path": "frontend/src/api/modules/host-tool.ts",
    "content": "import http from '@/api';\nimport { HostTool } from '../interface/host-tool';\nimport { TimeoutEnum } from '@/enums/http-enum';\n\nexport const getSupervisorStatus = () => {\n    return http.post<HostTool.HostTool>(`/hosts/tool`, { type: 'supervisord', operate: 'status' });\n};\n\nexport const operateSupervisor = (operate: string) => {\n    return http.post<any>(`/hosts/tool/operate`, { type: 'supervisord', operate: operate });\n};\n\nexport const operateSupervisorConfig = (req: HostTool.SupersivorConfig) => {\n    return http.post<HostTool.SupersivorConfigRes>(`/hosts/tool/config`, req);\n};\n\nexport const getSupervisorLog = () => {\n    return http.post<any>(`/hosts/tool/log`, { type: 'supervisord' });\n};\n\nexport const initSupervisor = (req: HostTool.SupersivorInit) => {\n    return http.post<any>(`/hosts/tool/init`, req);\n};\n\nexport const createSupervisorProcess = (req: HostTool.SupersivorProcess) => {\n    return http.post<any>(`/hosts/tool/supervisor/process`, req);\n};\n\nexport const operateSupervisorProcess = (req: HostTool.ProcessReq) => {\n    return http.post<any>(`/hosts/tool/supervisor/process`, req, TimeoutEnum.T_60S);\n};\n\nexport const getSupervisorProcess = () => {\n    return http.get<HostTool.SupersivorProcess[]>(`/hosts/tool/supervisor/process`, {}, { timeout: TimeoutEnum.T_3M });\n};\n\nexport const operateSupervisorProcessFile = (req: HostTool.ProcessFileReq) => {\n    return http.post<any>(`/hosts/tool/supervisor/process/file`, req, TimeoutEnum.T_60S);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/host.ts",
    "content": "import http from '@/api';\nimport { ResPage, ReqPage } from '../interface';\nimport { Host } from '../interface/host';\nimport { TimeoutEnum } from '@/enums/http-enum';\nimport { deepCopy } from '@/utils/util';\nimport { Base64 } from 'js-base64';\n\n// firewall\nexport const loadFireBaseInfo = (tab: string) => {\n    return http.post<Host.FirewallBase>(`/hosts/firewall/base`, { name: tab }, TimeoutEnum.T_40S);\n};\nexport const searchFireRule = (params: Host.RuleSearch) => {\n    return http.post<ResPage<Host.RuleInfo>>(`/hosts/firewall/search`, params, TimeoutEnum.T_40S);\n};\nexport const operateFire = (operation: string, withDockerRestart: boolean) => {\n    return http.post(\n        `/hosts/firewall/operate`,\n        {\n            operation: operation,\n            withDockerRestart: withDockerRestart,\n        },\n        TimeoutEnum.T_60S,\n    );\n};\nexport const operatePortRule = (params: Host.RulePort) => {\n    return http.post<Host.RulePort>(`/hosts/firewall/port`, params, TimeoutEnum.T_40S);\n};\nexport const operateForwardRule = (params: { rules: Host.RuleForward[]; forceDelete?: boolean }) => {\n    return http.post<Host.RulePort>(`/hosts/firewall/forward`, params, TimeoutEnum.T_40S);\n};\nexport const operateIPRule = (params: Host.RuleIP) => {\n    return http.post<Host.RuleIP>(`/hosts/firewall/ip`, params, TimeoutEnum.T_40S);\n};\nexport const updatePortRule = (params: Host.UpdatePortRule) => {\n    return http.post(`/hosts/firewall/update/port`, params, TimeoutEnum.T_40S);\n};\nexport const updateAddrRule = (params: Host.UpdateAddrRule) => {\n    return http.post(`/hosts/firewall/update/addr`, params, TimeoutEnum.T_40S);\n};\nexport const updateFirewallDescription = (params: Host.UpdateDescription) => {\n    return http.post(`/hosts/firewall/update/description`, params);\n};\nexport const batchOperateRule = (params: Host.BatchRule) => {\n    return http.post(`/hosts/firewall/batch`, params, TimeoutEnum.T_60S);\n};\n\n// Iptables Filter\nexport const searchFilterRules = (params: Host.IptablesFilterRuleSearch) => {\n    return http.post<Host.IptablesData>(`/hosts/firewall/filter/rule/search`, params);\n};\nexport const loadChainStatus = (name: string) => {\n    return http.post<Host.ChainStatus>(`/hosts/firewall/filter/chain/status`, { name: name }, TimeoutEnum.T_60S);\n};\nexport const operateFilterRule = (params: Host.IptablesFilterRuleOp) => {\n    return http.post(`/hosts/firewall/filter/rule/operate`, params, TimeoutEnum.T_40S);\n};\nexport const batchOperateFilterRule = (params: { rules: Host.IptablesFilterRuleOp[] }) => {\n    return http.post(`/hosts/firewall/filter/rule/batch`, params, TimeoutEnum.T_40S);\n};\nexport const operateFilterChain = (name: string, op: string) => {\n    return http.post(`/hosts/firewall/filter/operate`, { name: name, operate: op }, TimeoutEnum.T_60S);\n};\n\n// monitors\nexport const loadMonitor = (param: Host.MonitorSearch) => {\n    return http.post<Array<Host.MonitorData>>(`/hosts/monitor/search`, param);\n};\nexport const getGPUOptions = () => {\n    return http.get<Host.MonitorGPUOptions>(`/hosts/monitor/gpuoptions`);\n};\nexport const loadGPUMonitor = (param: Host.MonitorGPUSearch) => {\n    return http.post<Host.MonitorGPUData>(`/hosts/monitor/gpu/search`, param);\n};\nexport const getNetworkOptions = () => {\n    return http.get<Array<string>>(`/hosts/monitor/netoptions`);\n};\nexport const getIOOptions = () => {\n    return http.get<Array<string>>(`/hosts/monitor/iooptions`);\n};\nexport const cleanMonitors = () => {\n    return http.post(`/hosts/monitor/clean`, {});\n};\nexport const loadMonitorSetting = () => {\n    return http.get<Host.MonitorSetting>(`/hosts/monitor/setting`, {});\n};\nexport const updateMonitorSetting = (key: string, value: string) => {\n    return http.post(`/hosts/monitor/setting/update`, { key: key, value: value });\n};\n\n// ssh\nexport const getSSHInfo = () => {\n    return http.post<Host.SSHInfo>(`/hosts/ssh/search`);\n};\nexport const operateSSH = (operation: string) => {\n    return http.post(`/hosts/ssh/operate`, { operation: operation }, TimeoutEnum.T_40S);\n};\nexport const updateSSH = (params: Host.SSHUpdate) => {\n    return http.post(`/hosts/ssh/update`, params, TimeoutEnum.T_40S);\n};\nexport const loadSSHFile = (name: string) => {\n    return http.post<string>(`/hosts/ssh/file`, { name: name });\n};\nexport const updateSSHByFile = (key: string, file: string) => {\n    return http.post(`/hosts/ssh/file/update`, { key: key, value: file }, TimeoutEnum.T_60S);\n};\nexport const createCert = (params: Host.RootCert) => {\n    let request = deepCopy(params) as Host.RootCert;\n    if (request.passPhrase) {\n        request.passPhrase = Base64.encode(request.passPhrase);\n    }\n    if (request.privateKey) {\n        request.privateKey = Base64.encode(request.privateKey);\n    }\n    if (request.publicKey) {\n        request.publicKey = Base64.encode(request.publicKey);\n    }\n    return http.post(`/hosts/ssh/cert`, request);\n};\nexport const editCert = (params: Host.RootCert) => {\n    let request = deepCopy(params) as Host.RootCert;\n    if (request.passPhrase) {\n        request.passPhrase = Base64.encode(request.passPhrase);\n    }\n    if (request.privateKey) {\n        request.privateKey = Base64.encode(request.privateKey);\n    }\n    if (request.publicKey) {\n        request.publicKey = Base64.encode(request.publicKey);\n    }\n    return http.post(`/hosts/ssh/cert/update`, request);\n};\nexport const searchCert = (params: ReqPage) => {\n    return http.post<ResPage<Host.RootCertInfo>>(`/hosts/ssh/cert/search`, params);\n};\nexport const deleteCert = (ids: Array<number>, forceDelete: boolean) => {\n    return http.post(`/hosts/ssh/cert/delete`, { ids: ids, forceDelete: forceDelete });\n};\nexport const syncCert = () => {\n    return http.post(`/hosts/ssh/cert/sync`);\n};\nexport const loadSSHLogs = (params: Host.searchSSHLog) => {\n    return http.post<ResPage<Host.sshHistory>>(`/hosts/ssh/log`, params);\n};\nexport const exportSSHLogs = (params: Host.searchSSHLog) => {\n    return http.post<string>(`/hosts/ssh/log/export`, params, TimeoutEnum.T_40S);\n};\n\nexport const listDisks = () => {\n    return http.get<Host.CompleteDiskInfo>(`/hosts/disks`);\n};\n\nexport const partitionDisk = (params: Host.DiskPartition) => {\n    return http.post(`/hosts/disks/partition`, params, TimeoutEnum.T_60S);\n};\n\nexport const mountDisk = (params: Host.DiskMount) => {\n    return http.post(`/hosts/disks/mount`, params, TimeoutEnum.T_60S);\n};\n\nexport const unmountDisk = (params: Host.DiskUmount) => {\n    return http.post(`/hosts/disks/unmount`, params, TimeoutEnum.T_60S);\n};\n\nexport const getComponentInfo = (name: string, operateNode?: string) => {\n    const params = operateNode ? `?operateNode=${operateNode}` : '';\n    return http.get<Host.ComponentInfo>(`/hosts/components/${name}${params}`);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/log.ts",
    "content": "import http from '@/api';\nimport { ResPage } from '../interface';\nimport { Log } from '../interface/log';\n\nexport const getOperationLogs = (info: Log.SearchOpLog) => {\n    return http.post<ResPage<Log.OperationLog>>(`/core/logs/operation`, info);\n};\n\nexport const getLoginLogs = (info: Log.SearchLgLog) => {\n    return http.post<ResPage<Log.OperationLog>>(`/core/logs/login`, info);\n};\n\nexport const getSystemFiles = (node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.get<Array<string>>(`/logs/system/files${params}`);\n};\n\nexport const cleanLogs = (param: Log.CleanLog) => {\n    return http.post(`/core/logs/clean`, param);\n};\n\nexport const searchTasks = (req: Log.SearchTaskReq, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.post<ResPage<Log.Task>>(`/logs/tasks/search${params}`, req);\n};\n\nexport const countExecutingTask = () => {\n    return http.get<number>(`/logs/tasks/executing/count`);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/nginx.ts",
    "content": "import http from '@/api';\nimport { File } from '../interface/file';\nimport { Nginx } from '../interface/nginx';\n\nexport const getNginx = () => {\n    return http.get<File.File>(`/openresty`);\n};\n\nexport const getNginxConfigByScope = (req: Nginx.NginxScopeReq) => {\n    return http.post<Nginx.NginxParam[]>(`/openresty/scope`, req);\n};\n\nexport const updateNginxConfigByScope = (req: Nginx.NginxConfigReq) => {\n    return http.post(`/openresty/update`, req);\n};\n\nexport const getNginxStatus = () => {\n    return http.get<Nginx.NginxStatus>(`/openresty/status`);\n};\n\nexport const updateNginxConfigFile = (req: Nginx.NginxFileUpdate) => {\n    return http.post(`/openresty/file`, req);\n};\n\nexport const buildNginx = (req: Nginx.NginxBuildReq) => {\n    return http.post(`/openresty/build`, req);\n};\n\nexport const getNginxModules = () => {\n    return http.get<Nginx.NginxBuildConfig>(`/openresty/modules`);\n};\n\nexport const updateNginxModule = (req: Nginx.NginxModuleUpdate) => {\n    return http.post(`/openresty/modules/update`, req);\n};\n\nexport const getHttpsStatus = () => {\n    return http.get<Nginx.NginxHttpsStatus>(`/openresty/https`);\n};\n\nexport const operateHttps = (req: Nginx.NginxOperateReq) => {\n    return http.post(`/openresty/https`, req);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/process.ts",
    "content": "import http from '@/api';\nimport { Process } from '../interface/process';\n\nexport const stopProcess = (req: Process.StopReq) => {\n    return http.post<any>(`/process/stop`, req);\n};\n\nexport const getProcessByID = (pid: number) => {\n    return http.get<Process.PsProcessData>(`/process/${pid}`);\n};\n\nexport const getListeningProcess = () => {\n    return http.post<Process.ListeningProcess[]>(`/process/listening`);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/runtime.ts",
    "content": "import http from '@/api';\nimport { ResPage, ReqPage } from '../interface';\nimport { Runtime } from '../interface/runtime';\nimport { TimeoutEnum } from '@/enums/http-enum';\nimport { App } from '@/api/interface/app';\nimport { File } from '../interface/file';\nimport { HostTool } from '../interface/host-tool';\n\nexport const SearchRuntimes = (req: Runtime.RuntimeReq) => {\n    return http.post<ResPage<Runtime.RuntimeDTO>>(`/runtimes/search`, req);\n};\n\nexport const CreateRuntime = (req: Runtime.RuntimeCreate) => {\n    return http.post<Runtime.Runtime>(`/runtimes`, req);\n};\n\nexport const DeleteRuntime = (req: Runtime.RuntimeDelete) => {\n    return http.post<any>(`/runtimes/del`, req);\n};\n\nexport const RuntimeDeleteCheck = (runTimeId: number) => {\n    return http.get<App.AppInstallResource[]>(`runtimes/installed/delete/check/${runTimeId}`);\n};\n\nexport const GetRuntime = (id: number) => {\n    return http.get<Runtime.RuntimeDTO>(`/runtimes/${id}`);\n};\n\nexport const UpdateRuntime = (req: Runtime.RuntimeUpdate) => {\n    return http.post<any>(`/runtimes/update`, req);\n};\n\nexport const GetNodeScripts = (req: Runtime.NodeReq) => {\n    return http.post<Runtime.NodeScripts[]>(`/runtimes/node/package`, req);\n};\n\nexport const OperateRuntime = (req: Runtime.RuntimeOperate) => {\n    return http.post<any>(`/runtimes/operate`, req);\n};\n\nexport const GetNodeModules = (req: Runtime.NodeModuleReq) => {\n    return http.post<Runtime.NodeModule[]>(`/runtimes/node/modules`, req);\n};\n\nexport const OperateNodeModule = (req: Runtime.NodeModuleReq) => {\n    return http.post<any>(`/runtimes/node/modules/operate`, req, TimeoutEnum.T_10M);\n};\n\nexport const SearchPHPExtensions = (req: ReqPage) => {\n    return http.post<ResPage<Runtime.PHPExtensions>>(`/runtimes/php/extensions/search`, req);\n};\n\nexport const ListPHPExtensions = (req: Runtime.PHPExtensionsList) => {\n    return http.post<ResPage<Runtime.PHPExtensions>>(`/runtimes/php/extensions/search`, req);\n};\n\nexport const CreatePHPExtensions = (req: Runtime.PHPExtensionsCreate) => {\n    return http.post<any>(`/runtimes/php/extensions`, req);\n};\n\nexport const UpdatePHPExtensions = (req: Runtime.PHPExtensionsUpdate) => {\n    return http.post<any>(`/runtimes/php/extensions/update`, req);\n};\n\nexport const DeletePHPExtensions = (req: Runtime.PHPExtensionsDelete) => {\n    return http.post<any>(`/runtimes/php/extensions/del`, req);\n};\n\nexport const SyncRuntime = () => {\n    return http.post(`/runtimes/sync`, {});\n};\n\nexport const GetPHPExtensions = (id: number) => {\n    return http.get<Runtime.PHPExtensionsRes>(`/runtimes/php/${id}/extensions`);\n};\n\nexport const InstallPHPExtension = (req: Runtime.PHPExtensionInstall) => {\n    return http.post(`/runtimes/php/extensions/install`, req);\n};\n\nexport const UnInstallPHPExtension = (req: Runtime.PHPExtensionInstall) => {\n    return http.post(`/runtimes/php/extensions/uninstall`, req);\n};\n\nexport const GetPHPConfig = (id: number) => {\n    return http.get<Runtime.PHPConfig>(`/runtimes/php/config/${id}`);\n};\n\nexport const UpdatePHPConfig = (req: Runtime.PHPConfigUpdate) => {\n    return http.post<any>(`/runtimes/php/config`, req);\n};\n\nexport const UpdatePHPFile = (req: Runtime.PHPUpdate) => {\n    return http.post<any>(`/runtimes/php/update`, req);\n};\n\nexport const GetPHPConfigFile = (req: Runtime.PHPFileReq) => {\n    return http.post<File.File>(`/runtimes/php/file`, req);\n};\n\nexport const UpdateFPMConfig = (req: Runtime.FPMConfig) => {\n    return http.post(`/runtimes/php/fpm/config`, req);\n};\n\nexport const GetFPMConfig = (id: number) => {\n    return http.get<Runtime.FPMConfig>(`/runtimes/php/fpm/config/${id}`);\n};\n\nexport const GetSupervisorProcess = (id: number) => {\n    return http.get<HostTool.ProcessStatus[]>(`/runtimes/supervisor/process/${id}`);\n};\n\nexport const operateSupervisorProcess = (req: Runtime.ProcessReq) => {\n    return http.post(`/runtimes/supervisor/process`, req, TimeoutEnum.T_60S);\n};\n\nexport const operateSupervisorProcessFile = (req: Runtime.ProcessFileReq) => {\n    return http.post<string>(`/runtimes/supervisor/process/file`, req, TimeoutEnum.T_60S);\n};\n\nexport const createSupervisorProcess = (req: Runtime.SupersivorProcess) => {\n    return http.post(`/runtimes/supervisor/process`, req);\n};\n\nexport const getPHPContainerConfig = (id: number) => {\n    return http.get<Runtime.PHPContainerConfig>(`/runtimes/php/container/${id}`);\n};\n\nexport const updatePHPContainerConfig = (req: Runtime.PHPContainerConfig) => {\n    return http.post<any>(`/runtimes/php/container/update`, req);\n};\n\nexport const updateRemark = (req: Runtime.RemarkUpdate) => {\n    return http.post(`/runtimes/remark`, req);\n};\n\nexport const getFPMStatus = (id: number) => {\n    return http.get<Runtime.FpmStatus[]>(`/runtimes/php/fpm/status/${id}`);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/setting.ts",
    "content": "import http from '@/api';\nimport { deepCopy } from '@/utils/util';\nimport { Base64 } from 'js-base64';\nimport { ResPage, SearchWithPage, DescriptionUpdate, ReqPage } from '../interface';\nimport { Setting } from '../interface/setting';\nimport { TimeoutEnum } from '@/enums/http-enum';\nimport { App } from '../interface/app';\n\n// license\nexport const uploadLicense = (oldLicense: string, params: FormData) => {\n    if (oldLicense === '') {\n        return http.upload('/core/licenses/upload', params);\n    }\n    return http.upload('/core/licenses/update', params);\n};\nexport const searchLicense = (params: ReqPage) => {\n    return http.post<ResPage<Setting.License>>('/core/licenses/search', params);\n};\nexport const deleteLicense = (params: { ids: number }) => {\n    return http.post('/core/licenses/del', params);\n};\nexport const getLicenseStatus = () => {\n    return http.get<Setting.LicenseStatus>(`/core/licenses/status`);\n};\nexport const getMasterLicenseStatus = () => {\n    return http.get<Setting.LicenseStatus>(`/core/licenses/master/status`);\n};\nexport const syncLicense = (id: number) => {\n    return http.post(`/core/licenses/sync`, { id: id });\n};\nexport const bindLicense = (params: Setting.LicenseBind) => {\n    return http.post(`/core/licenses/bind`, params, TimeoutEnum.T_60S);\n};\nexport const unbindLicense = (params: Setting.LicenseUnbind) => {\n    return http.post(`/core/licenses/unbind`, params, TimeoutEnum.T_60S);\n};\nexport const changeBind = (id: number, nodeIDs: Array<number>) => {\n    return http.post(`/core/licenses/bind/free`, { licenseID: id, nodeIDs: nodeIDs }, TimeoutEnum.T_60S);\n};\nexport const loadLicenseOptions = () => {\n    return http.get<Array<Setting.LicenseOptions>>(`/core/licenses/options`);\n};\nexport const listNodeOptions = (type: string) => {\n    return http.post<Array<Setting.NodeItem>>(`/core/nodes/list`, { type: type });\n};\n\nexport const listAllNodes = () => {\n    return http.get<Array<Setting.NodeItem>>(`/core/nodes/all`);\n};\nexport const listAllSimpleNodes = () => {\n    return http.get<Array<Setting.SimpleNodeItem>>(`/core/nodes/simple/all`);\n};\n\nexport const getLicenseSmsInfo = () => {\n    return http.get<Setting.SmsInfo>(`/core/licenses/sms/info`);\n};\n\nexport const listAppNodes = () => {\n    return http.get<Array<Setting.NodeAppItem>>(`/core/xpack/nodes/apps/update`, {}, { timeout: TimeoutEnum.T_60S });\n};\n\n// agent\nexport const loadBaseDir = (node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.get<string>(`/settings/basedir${query}`);\n};\nexport const loadDaemonJsonPath = () => {\n    return http.get<string>(`/settings/daemonjson`, {});\n};\nexport const updateAgentSetting = (param: Setting.SettingUpdate) => {\n    return http.post(`/settings/update`, param);\n};\nexport const getAgentSettingInfo = () => {\n    return http.post<Setting.SettingInfo>(`/settings/search`);\n};\nexport const getAgentTerminalAIInfo = () => {\n    return http.post<Setting.TerminalAIInfo>(`/settings/terminal/ai/search`);\n};\nexport const updateAgentTerminalAIInfo = (param: Setting.TerminalAIInfo) => {\n    return http.post(`/settings/terminal/ai/update`, param);\n};\nexport const getAgentSettingByKey = (key: string) => {\n    return http.get<string>(`/settings/get/${key}`);\n};\nexport const updateCommonDescription = (param: Setting.CommonDescription) => {\n    return http.post(`/settings/description/save`, param);\n};\n\n// core\nexport const getSettingInfo = () => {\n    return http.post<Setting.SettingInfo>(`/core/settings/search`);\n};\nexport const getSettingBy = (key: string) => {\n    return http.post<string>(`/core/settings/by`, { key: key });\n};\nexport const getTerminalInfo = () => {\n    return http.post<Setting.TerminalInfo>(`/core/settings/terminal/search`);\n};\nexport const UpdateTerminalInfo = (param: Setting.TerminalInfo) => {\n    return http.post(`/core/settings/terminal/update`, param);\n};\nexport const getSystemAvailable = () => {\n    return http.get(`/core/settings/search/available`);\n};\nexport const updateSetting = (param: Setting.SettingUpdate) => {\n    return http.post(`/core/settings/update`, param);\n};\nexport const updateMenu = (param: Setting.SettingUpdate) => {\n    return http.post(`/core/settings/menu/update`, param);\n};\nexport const defaultMenu = () => {\n    return http.post(`/core/settings/menu/default`);\n};\nexport const updateProxy = (params: Setting.ProxyUpdate) => {\n    let request = deepCopy(params) as Setting.ProxyUpdate;\n    if (request.proxyPasswd) {\n        request.proxyPasswd = Base64.encode(request.proxyPasswd);\n    }\n    request.proxyType = request.proxyType === 'close' ? '' : request.proxyType;\n    return http.post(`/core/settings/proxy/update`, request);\n};\nexport const updatePassword = (param: Setting.PasswordUpdate) => {\n    return http.post(`/core/settings/password/update`, param);\n};\nexport const loadInterfaceAddr = () => {\n    return http.get(`/core/settings/interface`);\n};\nexport const updateBindInfo = (ipv6: string, bindAddress: string) => {\n    return http.post(`/core/settings/bind/update`, { ipv6: ipv6, bindAddress: bindAddress });\n};\nexport const updatePort = (param: Setting.PortUpdate) => {\n    return http.post(`/core/settings/port/update`, param);\n};\nexport const updateSSL = (param: Setting.SSLUpdate) => {\n    return http.post(`/core/settings/ssl/update`, param);\n};\nexport const loadSSLInfo = () => {\n    return http.get<Setting.SSLInfo>(`/core/settings/ssl/info`);\n};\nexport const downloadSSL = () => {\n    return http.download<any>(`/core/settings/ssl/download`);\n};\nexport const handleExpired = (param: Setting.PasswordUpdate) => {\n    return http.post(`/core/settings/expired/handle`, param);\n};\nexport const loadMFA = (param: Setting.MFARequest) => {\n    return http.post<Setting.MFAInfo>(`/core/settings/mfa`, param);\n};\nexport const bindMFA = (param: Setting.MFABind) => {\n    return http.post(`/core/settings/mfa/bind`, param);\n};\nexport const passkeyRegisterBegin = (param: Setting.PasskeyRegisterRequest) => {\n    return http.post<Setting.PasskeyBeginResponse>(`/core/settings/passkey/register/begin`, param);\n};\nexport const passkeyRegisterFinish = (param: Record<string, any>, sessionId: string) => {\n    return http.post(`/core/settings/passkey/register/finish`, param, undefined, { 'Passkey-Session': sessionId });\n};\nexport const passkeyList = () => {\n    return http.get<Array<Setting.PasskeyInfo>>(`/core/settings/passkey/list`);\n};\nexport const passkeyDelete = (id: string) => {\n    return http.delete(`/core/settings/passkey/${id}`);\n};\nexport const getAppStoreConfig = (node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.get<App.AppStoreConfig>(`/core/settings/apps/store/config${params}`);\n};\nexport const updateAppStoreConfig = (req: App.AppStoreConfigUpdate) => {\n    return http.post(`/core/settings/apps/store/update`, req);\n};\n\n// snapshot\nexport const loadSnapshotInfo = () => {\n    return http.get<Setting.SnapshotData>(`/settings/snapshot/load`, {}, { timeout: TimeoutEnum.T_60S });\n};\nexport const snapshotCreate = (param: Setting.SnapshotCreate) => {\n    return http.post(`/settings/snapshot`, param);\n};\nexport const snapshotRecreate = (id: number) => {\n    return http.post(`/settings/snapshot/recreate`, { id: id });\n};\nexport const snapshotImport = (param: Setting.SnapshotImport) => {\n    return http.post(`/settings/snapshot/import`, param);\n};\nexport const updateSnapshotDescription = (param: DescriptionUpdate) => {\n    return http.post(`/settings/snapshot/description/update`, param);\n};\nexport const snapshotDelete = (param: { ids: number[]; deleteWithFile: boolean }) => {\n    return http.post(`/settings/snapshot/del`, param);\n};\nexport const snapshotRecover = (param: Setting.SnapshotRecover) => {\n    return http.post(`/settings/snapshot/recover`, param);\n};\nexport const snapshotRollback = (param: Setting.SnapshotRecover) => {\n    return http.post(`/settings/snapshot/rollback`, param);\n};\nexport const searchSnapshotPage = (param: SearchWithPage) => {\n    return http.post<ResPage<Setting.SnapshotInfo>>(`/settings/snapshot/search`, param);\n};\n\n// upgrade\nexport const loadUpgradeInfo = () => {\n    return http.get<Setting.UpgradeInfo>(`/core/settings/upgrade`);\n};\nexport const loadReleaseNotes = (version: string) => {\n    return http.post<string>(`/core/settings/upgrade/notes`, { version: version });\n};\nexport const listReleases = () => {\n    return http.get<Array<Setting.ReleasesNotes>>(`/core/settings/upgrade/releases`);\n};\nexport const upgrade = (version: string) => {\n    return http.post(`/core/settings/upgrade`, { version: version });\n};\n\n// api config\nexport const generateApiKey = () => {\n    return http.post<string>(`/core/settings/api/config/generate/key`);\n};\nexport const updateApiConfig = (param: Setting.ApiConfig) => {\n    return http.post(`/core/settings/api/config/update`, param);\n};\n\n// memo\nexport const getMemo = () => {\n    return http.get<string>(`/core/settings/memo`);\n};\nexport const updateMemo = (content: string) => {\n    return http.post(`/core/settings/memo`, { content });\n};\n"
  },
  {
    "path": "frontend/src/api/modules/terminal.ts",
    "content": "import http from '@/api';\nimport { ResPage } from '../interface';\nimport { Host } from '../interface/host';\nimport { Base64 } from 'js-base64';\nimport { deepCopy } from '@/utils/util';\n\nexport const searchHosts = (params: Host.SearchWithPage) => {\n    return http.postLocalNode<ResPage<Host.Host>>(`/hosts/search`, params);\n};\nexport const getHostByID = (id: number) => {\n    return http.postLocalNode<Host.Host>(`/hosts/info`, { id: id });\n};\nexport const getHostTree = (params: Host.ReqSearch) => {\n    return http.postLocalNode<Array<Host.HostTree>>(`/hosts/tree`, params);\n};\nexport const updateLocalConn = (param: { withReset: boolean; defaultConn: string }) => {\n    return http.post(`/settings/ssh/default`, param);\n};\nexport const addHost = (params: Host.HostOperate) => {\n    let request = deepCopy(params) as Host.HostOperate;\n    if (request.password) {\n        request.password = Base64.encode(request.password);\n    }\n    if (request.privateKey) {\n        request.privateKey = Base64.encode(request.privateKey);\n    }\n    if (params.isLocal) {\n        return http.post(`/settings/ssh`, request);\n    }\n    return http.postLocalNode<Host.HostOperate>(`/hosts`, request);\n};\nexport const testByInfo = (params: Host.HostConnTest) => {\n    let request = deepCopy(params) as Host.HostOperate;\n    if (request.password) {\n        request.password = Base64.encode(request.password);\n    }\n    if (request.privateKey) {\n        request.privateKey = Base64.encode(request.privateKey);\n    }\n    if (params.isLocal) {\n        return http.post<boolean>(`/settings/ssh/check/info`, request);\n    }\n    return http.postLocalNode<boolean>(`/hosts/test/byinfo`, request);\n};\nexport const testByID = (id: number) => {\n    return http.postLocalNode<boolean>(`/hosts/test/byid`, { id: id });\n};\nexport const editHost = (params: Host.HostOperate) => {\n    let request = deepCopy(params) as Host.HostOperate;\n    if (request.password) {\n        request.password = Base64.encode(request.password);\n    }\n    if (request.privateKey) {\n        request.privateKey = Base64.encode(request.privateKey);\n    }\n    return http.postLocalNode(`/hosts/update`, request);\n};\nexport const editHostGroup = (params: Host.GroupChange) => {\n    return http.postLocalNode(`/hosts/update/group`, params);\n};\nexport const deleteHost = (params: { ids: number[] }) => {\n    return http.postLocalNode(`/hosts/del`, params);\n};\n\n// agent\nexport const loadLocalConn = () => {\n    return http.get<Host.HostConnTest>(`/settings/ssh/conn`);\n};\nexport const testLocalConn = () => {\n    return http.post<boolean>(`/settings/ssh/check`);\n};\n"
  },
  {
    "path": "frontend/src/api/modules/toolbox.ts",
    "content": "import http from '@/api';\nimport { ReqPage, ResPage, UpdateByFile } from '../interface';\nimport { Toolbox } from '../interface/toolbox';\nimport { Base64 } from 'js-base64';\nimport { TimeoutEnum } from '@/enums/http-enum';\nimport { deepCopy } from '@/utils/util';\n\n// device\nexport const getDeviceBase = () => {\n    return http.post<Toolbox.DeviceBaseInfo>(`/toolbox/device/base`, {}, TimeoutEnum.T_60S);\n};\nexport const loadTimeZoneOptions = () => {\n    return http.get<Array<string>>(`/toolbox/device/zone/options`);\n};\nexport const loadUsers = () => {\n    return http.get<Array<string>>(`/toolbox/device/users`);\n};\nexport const updateDevice = (key: string, value: string) => {\n    return http.post(`/toolbox/device/update/conf`, { key: key, value: value }, TimeoutEnum.T_60S);\n};\nexport const updateDeviceHost = (param: Array<Toolbox.TimeZoneOptions>) => {\n    return http.post(`/toolbox/device/update/host`, param, TimeoutEnum.T_60S);\n};\nexport const updateDevicePasswd = (user: string, passwd: string) => {\n    return http.post(`/toolbox/device/update/passwd`, { user: user, passwd: Base64.encode(passwd) }, TimeoutEnum.T_60S);\n};\nexport const updateDeviceSwap = (params: Toolbox.SwapHelper) => {\n    return http.post(`/toolbox/device/update/swap`, params, TimeoutEnum.T_10M);\n};\nexport const updateDeviceByConf = (name: string, file: string) => {\n    return http.post(`/toolbox/device/update/byconf`, { name: name, file: file }, TimeoutEnum.T_5M);\n};\nexport const checkDNS = (key: string, value: string) => {\n    return http.post(`/toolbox/device/check/dns`, { key: key, value: value });\n};\nexport const loadDeviceConf = (name: string) => {\n    return http.post(`/toolbox/device/conf`, { name: name });\n};\n\n// clean\nexport const scan = () => {\n    return http.post<Toolbox.CleanData>(`/toolbox/scan`, {});\n};\nexport const clean = (param: any) => {\n    return http.post(`/toolbox/clean`, param, TimeoutEnum.T_5M);\n};\n\n// fail2ban\nexport const getFail2banBase = () => {\n    return http.get<Toolbox.Fail2banBaseInfo>(`/toolbox/fail2ban/base`);\n};\nexport const getFail2banConf = () => {\n    return http.get<string>(`/toolbox/fail2ban/load/conf`);\n};\n\nexport const searchFail2ban = (param: Toolbox.Fail2banSearch) => {\n    return http.post<Array<string>>(`/toolbox/fail2ban/search`, param);\n};\n\nexport const operateFail2ban = (operate: string) => {\n    return http.post(`/toolbox/fail2ban/operate`, { operation: operate }, TimeoutEnum.T_5M);\n};\n\nexport const operatorFail2banSSHD = (param: Toolbox.Fail2banSet) => {\n    return http.post(`/toolbox/fail2ban/operate/sshd`, param, TimeoutEnum.T_5M);\n};\n\nexport const updateFail2ban = (param: Toolbox.Fail2banUpdate) => {\n    return http.post(`/toolbox/fail2ban/update`, param, TimeoutEnum.T_5M);\n};\n\nexport const updateFail2banByFile = (param: UpdateByFile) => {\n    return http.post(`/toolbox/fail2ban/update/byconf`, param, TimeoutEnum.T_5M);\n};\n\n// ftp\nexport const getFtpBase = () => {\n    return http.get<Toolbox.FtpBaseInfo>(`/toolbox/ftp/base`);\n};\nexport const searchFtpLog = (param: Toolbox.FtpSearchLog) => {\n    return http.post<ResPage<Toolbox.FtpLog>>(`/toolbox/ftp/log/search`, param);\n};\nexport const searchFtp = (param: ReqPage) => {\n    return http.post<ResPage<Toolbox.FtpInfo>>(`/toolbox/ftp/search`, param);\n};\nexport const operateFtp = (operate: string) => {\n    return http.post(`/toolbox/ftp/operate`, { operation: operate }, TimeoutEnum.T_5M);\n};\nexport const syncFtp = () => {\n    return http.post(`/toolbox/ftp/sync`);\n};\n\nexport const createFtp = (params: Toolbox.FtpCreate) => {\n    let request = deepCopy(params) as Toolbox.FtpCreate;\n    if (request.password) {\n        request.password = Base64.encode(request.password);\n    }\n    return http.post(`/toolbox/ftp`, request);\n};\n\nexport const updateFtp = (params: Toolbox.FtpUpdate) => {\n    let request = deepCopy(params) as Toolbox.FtpUpdate;\n    if (request.password) {\n        request.password = Base64.encode(request.password);\n    }\n    return http.post(`/toolbox/ftp/update`, request);\n};\n\nexport const deleteFtp = (params: { ids: number[] }) => {\n    return http.post(`/toolbox/ftp/del`, params);\n};\n\n// clam\nexport const cleanClamRecord = (id: number) => {\n    return http.post(`/toolbox/clam/record/clean`, { id: id });\n};\nexport const searchClamRecord = (param: Toolbox.ClamSearchLog) => {\n    return http.post<ResPage<Toolbox.ClamRecord>>(`/toolbox/clam/record/search`, param);\n};\nexport const searchClamFile = (name: string, tail: string) => {\n    return http.post<string>(`/toolbox/clam/file/search`, { name: name, tail: tail });\n};\nexport const updateClamFile = (name: string, file: string) => {\n    return http.post(`/toolbox/clam/file/update`, { name: name, file: file }, TimeoutEnum.T_60S);\n};\nexport const searchClamBaseInfo = () => {\n    return http.post<Toolbox.ClamBaseInfo>(`/toolbox/clam/base`);\n};\nexport const updateClamBaseInfo = (operate: string) => {\n    return http.post(`/toolbox/clam/operate`, { Operation: operate }, TimeoutEnum.T_60S);\n};\nexport const searchClam = (param: ReqPage) => {\n    return http.post<ResPage<Toolbox.ClamInfo>>(`/toolbox/clam/search`, param);\n};\nexport const createClam = (params: Toolbox.ClamCreate) => {\n    return http.post(`/toolbox/clam`, params);\n};\nexport const updateClam = (params: Toolbox.ClamUpdate) => {\n    return http.post(`/toolbox/clam/update`, params);\n};\nexport const updateClamStatus = (id: number, status: string) => {\n    return http.post(`/toolbox/clam/status/update`, { id: id, status: status });\n};\nexport const deleteClam = (params: { ids: number[]; removeInfected: boolean }) => {\n    return http.post(`/toolbox/clam/del`, params);\n};\nexport const handleClamScan = (id: number) => {\n    return http.post(`/toolbox/clam/handle`, { id: id });\n};\n"
  },
  {
    "path": "frontend/src/api/modules/website.ts",
    "content": "import http from '@/api';\nimport { ReqPage, ResPage } from '../interface';\nimport { Website } from '../interface/website';\nimport { File } from '../interface/file';\nimport { TimeoutEnum } from '@/enums/http-enum';\nimport { deepCopy } from '@/utils/util';\nimport { Base64 } from 'js-base64';\n\nexport const searchWebsites = (req: Website.WebSiteSearch, node?: string) => {\n    const params = node ? `?operateNode=${node}` : '';\n    return http.post<ResPage<Website.WebsiteRes>>(`/websites/search${params}`, req);\n};\n\nexport const listWebsites = () => {\n    return http.get<Website.WebsiteDTO[]>(`/websites/list`);\n};\n\nexport const createWebsite = (req: Website.WebSiteCreateReq) => {\n    let request = deepCopy(req) as Website.WebSiteCreateReq;\n    if (request.ftpPassword) {\n        request.ftpPassword = Base64.encode(request.ftpPassword);\n    }\n    return http.post<any>(`/websites`, request, TimeoutEnum.T_10M);\n};\n\nexport const opWebsite = (req: Website.WebSiteOp, node?: string) => {\n    const query = node ? `?operateNode=${node}` : '';\n    return http.post<any>(`/websites/operate${query}`, req);\n};\n\nexport const opWebsiteLog = (req: Website.WebSiteOpLog) => {\n    return http.post<Website.WebSiteLog>(`/websites/log`, req);\n};\n\nexport const updateWebsite = (req: Website.WebSiteUpdateReq) => {\n    return http.post<any>(`/websites/update`, req);\n};\n\nexport const getWebsite = (id: number) => {\n    return http.get<Website.WebsiteDTO>(`/websites/${id}`);\n};\n\nexport const getWebsiteOptions = (req: Website.OptionReq) => {\n    return http.post<any>(`/websites/options`, req);\n};\n\nexport const getWebsiteConfig = (id: number, type: string) => {\n    return http.get<File.File>(`/websites/${id}/config/${type}`);\n};\n\nexport const deleteWebsite = (req: Website.WebSiteDel) => {\n    return http.post<any>(`/websites/del`, req);\n};\n\nexport const listDomains = (id: number) => {\n    return http.get<Website.Domain[]>(`/websites/domains/${id}`);\n};\n\nexport const deleteDomain = (req: Website.DomainDelete) => {\n    return http.post<any>(`/websites/domains/del/`, req);\n};\n\nexport const createDomain = (req: Website.DomainCreate) => {\n    return http.post<any>(`/websites/domains`, req);\n};\n\nexport const updateDomain = (req: Website.DomainUpdate) => {\n    return http.post<any>(`/websites/domains/update`, req);\n};\n\nexport const getNginxConfig = (req: Website.NginxScopeReq) => {\n    return http.post<Website.NginxScopeConfig>(`/websites/config`, req);\n};\n\nexport const updateNginxConfig = (req: Website.NginxConfigReq) => {\n    return http.post<any>(`/websites/config/update`, req);\n};\n\nexport const searchDnsAccount = (req: ReqPage) => {\n    return http.post<ResPage<Website.DnsAccount>>(`/websites/dns/search`, req);\n};\n\nexport const createDnsAccount = (req: Website.DnsAccountCreate) => {\n    return http.post<any>(`/websites/dns`, req);\n};\n\nexport const updateDnsAccount = (req: Website.DnsAccountUpdate) => {\n    return http.post<any>(`/websites/dns/update`, req);\n};\n\nexport const deleteDnsAccount = (req: Website.DelReq) => {\n    return http.post<any>(`/websites/dns/del`, req);\n};\n\nexport const searchAcmeAccount = (req: ReqPage) => {\n    return http.post<ResPage<Website.AcmeAccount>>(`/websites/acme/search`, req);\n};\n\nexport const createAcmeAccount = (req: Website.AcmeAccountCreate) => {\n    return http.post<Website.AcmeAccount>(`/websites/acme`, req, TimeoutEnum.T_10M);\n};\n\nexport const deleteAcmeAccount = (req: Website.DelReq) => {\n    return http.post<any>(`/websites/acme/del`, req);\n};\n\nexport const updateAcmeAccount = (req: Website.AcmeAccountUpdate) => {\n    return http.post<Website.AcmeAccount>(`/websites/acme/update`, req, TimeoutEnum.T_10M);\n};\n\nexport const searchSSL = (req: ReqPage) => {\n    return http.post<ResPage<Website.SSLDTO>>(`/websites/ssl/search`, req);\n};\n\nexport const listSSL = (req: Website.SSLReq) => {\n    return http.post<Website.SSLDTO[]>(`/websites/ssl/list`, req);\n};\n\nexport const listLocalNodeSSL = (req: Website.SSLReq) => {\n    return http.postLocalNode<Website.SSLDTO[]>(`/websites/ssl/list`, req);\n};\n\nexport const createSSL = (req: Website.SSLCreate) => {\n    return http.post<Website.SSLCreate>(`/websites/ssl`, req, TimeoutEnum.T_10M);\n};\n\nexport const deleteSSL = (req: Website.DelReq) => {\n    return http.post<any>(`/websites/ssl/del`, req);\n};\n\nexport const getSSL = (id: number) => {\n    return http.get<Website.SSL>(`/websites/ssl/${id}`);\n};\n\nexport const obtainSSL = (req: Website.SSLObtain) => {\n    return http.post<any>(`/websites/ssl/obtain`, req);\n};\n\nexport const updateSSL = (req: Website.SSLUpdate) => {\n    return http.post<any>(`/websites/ssl/update`, req);\n};\n\nexport const getDnsResolve = (req: Website.DNSResolveReq) => {\n    return http.post<Website.DNSResolve[]>(`/websites/ssl/resolve`, req, TimeoutEnum.T_5M);\n};\n\nexport const getHTTPSConfig = (id: number) => {\n    return http.get<Website.HTTPSConfig>(`/websites/${id}/https`);\n};\n\nexport const updateHTTPSConfig = (req: Website.HTTPSReq) => {\n    return http.post<Website.HTTPSConfig>(`/websites/${req.websiteId}/https`, req);\n};\n\nexport const preCheck = (req: Website.CheckReq) => {\n    return http.post<Website.CheckRes[]>(`/websites/check`, req);\n};\n\nexport const updateNginxFile = (req: Website.NginxUpdate) => {\n    return http.post<any>(`/websites/nginx/update`, req, TimeoutEnum.T_3M);\n};\n\nexport const changeDefaultServer = (req: Website.DefaultServerUpdate) => {\n    return http.post<any>(`/websites/default/server`, req);\n};\n\nexport const getRewriteConfig = (req: Website.RewriteReq) => {\n    return http.post<Website.RewriteRes>(`/websites/rewrite`, req);\n};\n\nexport const updateRewriteConfig = (req: Website.RewriteUpdate) => {\n    return http.post<any>(`/websites/rewrite/update`, req);\n};\n\nexport const updateWebsiteDir = (req: Website.DirUpdate) => {\n    return http.post<any>(`/websites/dir/update`, req);\n};\n\nexport const updateWebsiteDirPermission = (req: Website.DirPermissionUpdate) => {\n    return http.post<any>(`/websites/dir/permission`, req);\n};\n\nexport const getProxyConfig = (req: Website.ProxyReq) => {\n    return http.post<Website.ProxyConfig[]>(`/websites/proxies`, req);\n};\n\nexport const operateProxyConfig = (req: Website.ProxyReq) => {\n    return http.post<any>(`/websites/proxies/update`, req);\n};\n\nexport const deleteProxyConfig = (req: Website.ProxyDel) => {\n    return http.post<any>(`/websites/proxies/delete`, req);\n};\n\nexport const updateProxyConfigStatus = (req: Website.ProxyStatusUpdate) => {\n    return http.post<any>(`/websites/proxies/status`, req);\n};\n\nexport const updateProxyConfigFile = (req: Website.ProxyFileUpdate) => {\n    return http.post<any>(`/websites/proxies/file`, req);\n};\n\nexport const clearProxyCache = (req: Website.WebsiteReq) => {\n    return http.post(`/websites/proxy/clear`, req);\n};\n\nexport const getAuthConfig = (req: Website.AuthReq) => {\n    return http.post<Website.AuthConfig>(`/websites/auths`, req);\n};\n\nexport const operateAuthConfig = (req: Website.NginxAuthConfig) => {\n    return http.post<any>(`/websites/auths/update`, req);\n};\n\nexport const getPathAuthConfig = (req: Website.AuthReq) => {\n    return http.post<Website.NginxPathAuthConfig[]>(`/websites/auths/path`, req);\n};\n\nexport const operatePathAuthConfig = (req: Website.NginxPathAuthConfig) => {\n    return http.post(`/websites/auths/path/update`, req);\n};\n\nexport const getAntiLeech = (req: Website.LeechReq) => {\n    return http.post<Website.LeechConfig>(`/websites/leech`, req);\n};\n\nexport const updateAntiLeech = (req: Website.LeechConfig) => {\n    return http.post<any>(`/websites/leech/update`, req);\n};\n\nexport const getRedirectConfig = (req: Website.WebsiteReq) => {\n    return http.post<Website.RedirectConfig[]>(`/websites/redirect`, req);\n};\n\nexport const operateRedirectConfig = (req: Website.WebsiteReq) => {\n    return http.post<any>(`/websites/redirect/update`, req);\n};\n\nexport const updateRedirectConfigFile = (req: Website.RedirectFileUpdate) => {\n    return http.post<any>(`/websites/redirect/file`, req);\n};\n\nexport const changePHPVersion = (req: Website.PHPVersionChange) => {\n    return http.post<any>(`/websites/php/version`, req);\n};\n\nexport const getDirConfig = (req: Website.ProxyReq) => {\n    return http.post<Website.DirConfig>(`/websites/dir`, req);\n};\n\nexport const uploadSSL = (req: Website.SSLUpload) => {\n    return http.post<any>(`/websites/ssl/upload`, req);\n};\n\nexport const uploadSSLFile = (params: FormData) => {\n    return http.upload<File.File>(`/websites/ssl/upload/file`, params, {});\n};\n\nexport const searchCAs = (req: ReqPage) => {\n    return http.post<ResPage<Website.CA>>(`/websites/ca/search`, req);\n};\n\nexport const createCA = (req: Website.CACreate) => {\n    return http.post<Website.CA>(`/websites/ca`, req);\n};\n\nexport const obtainSSLByCA = (req: Website.SSLObtainByCA) => {\n    return http.post<any>(`/websites/ca/obtain`, req);\n};\n\nexport const deleteCA = (req: Website.DelReq) => {\n    return http.post<any>(`/websites/ca/del`, req);\n};\n\nexport const renewSSLByCA = (req: Website.RenewSSLByCA) => {\n    return http.post<any>(`/websites/ca/renew`, req);\n};\n\nexport const downloadFile = (params: Website.SSLDownload) => {\n    return http.download<BlobPart>(`/websites/ssl/download`, params, {\n        responseType: 'blob',\n        timeout: TimeoutEnum.T_40S,\n    });\n};\n\nexport const getCA = (id: number) => {\n    return http.get<Website.CADTO>(`/websites/ca/${id}`);\n};\n\nexport const getDefaultHtml = (type: string) => {\n    return http.get<Website.WebsiteHtml>(`/websites/default/html/${type}`);\n};\n\nexport const updateDefaultHtml = (req: Website.WebsiteHtmlUpdate) => {\n    return http.post(`/websites/default/html/update`, req);\n};\n\nexport const downloadCAFile = (params: Website.SSLDownload) => {\n    return http.download<BlobPart>(`/websites/ca/download`, params, {\n        responseType: 'blob',\n        timeout: TimeoutEnum.T_40S,\n    });\n};\n\nexport const getLoadBalances = (id: number) => {\n    return http.get<Website.NginxUpstream[]>(`/websites/${id}/lbs`);\n};\n\nexport const createLoadBalance = (req: Website.LoadBalanceReq) => {\n    return http.post(`/websites/lbs/create`, req);\n};\n\nexport const deleteLoadBalance = (req: Website.LoadBalanceDel) => {\n    return http.post(`/websites/lbs/del`, req);\n};\n\nexport const updateLoadBalance = (req: Website.LoadBalanceReq) => {\n    return http.post(`/websites/lbs/update`, req);\n};\n\nexport const updateLoadBalanceFile = (req: Website.WebsiteLBUpdateFile) => {\n    return http.post(`/websites/lbs/file`, req);\n};\n\nexport const updateCacheConfig = (req: Website.WebsiteCacheConfig) => {\n    return http.post(`/websites/proxy/config`, req);\n};\n\nexport const getCacheConfig = (id: number) => {\n    return http.get<Website.WebsiteCacheConfig>(`/websites/proxy/config/${id}`);\n};\n\nexport const updateRealIPConfig = (req: Website.WebsiteRealIPConfig) => {\n    return http.post(`/websites/realip/config`, req);\n};\n\nexport const getRealIPConfig = (id: number) => {\n    return http.get<Website.WebsiteRealIPConfig>(`/websites/realip/config/${id}`);\n};\n\nexport const getWebsiteResource = (id: number) => {\n    return http.get<Website.WebsiteResource[]>(`/websites/resource/${id}`);\n};\n\nexport const getWebsiteDatabase = () => {\n    return http.get<Website.WebsiteDatabase[]>(`/websites/databases`);\n};\n\nexport const changeDatabase = (req: Website.ChangeDatabase) => {\n    return http.post(`/websites/databases`, req);\n};\n\nexport const operateCustomRewrite = (req: Website.CustomRewirte) => {\n    return http.post(`/websites/rewrite/custom`, req);\n};\n\nexport const listCustomRewrite = () => {\n    return http.get<string[]>(`/websites/rewrite/custom`);\n};\n\nexport const operateCrossSiteAccess = (req: Website.CrossSiteAccessOp) => {\n    return http.post(`/websites/crosssite`, req);\n};\n\nexport const execComposer = (req: Website.ExecComposer) => {\n    return http.post(`/websites/exec/composer`, req);\n};\n\nexport const batchOperate = (req: Website.BatchOperate) => {\n    return http.post(`/websites/batch/operate`, req);\n};\n\nexport const getCorsConfig = (id: number) => {\n    return http.get<Website.CorsConfig>(`/websites/cors/${id}`);\n};\n\nexport const updateCorsConfig = (req: Website.CorsConfigReq) => {\n    return http.post(`/websites/cors/update`, req);\n};\n\nexport const batchSetGroup = (req: Website.BatchSetGroup) => {\n    return http.post(`/websites/batch/group`, req);\n};\n\nexport const updateWebsiteStream = (req: Website.WebsiteStreamUpdate) => {\n    return http.post(`/websites/stream/update`, req);\n};\n\nexport const batchSetHttps = (req: Website.BatchSetHttps) => {\n    return http.post(`/websites/batch/ssl`, req);\n};\n"
  },
  {
    "path": "frontend/src/assets/iconfont/iconfont.css",
    "content": "@font-face {\n  font-family: \"iconfont\"; /* Project id 4776196 */\n  src: url('iconfont.woff2?t=1773971674579') format('woff2'),\n       url('iconfont.woff?t=1773971674579') format('woff'),\n       url('iconfont.ttf?t=1773971674579') format('truetype'),\n       url('iconfont.svg?t=1773971674579#iconfont') format('svg');\n}\n\n.iconfont {\n  font-family: \"iconfont\" !important;\n  font-size: 16px;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.p-tuichudenglu3:before {\n  content: \"\\e62d\";\n}\n\n.p-renwuzhongxin1:before {\n  content: \"\\e807\";\n}\n\n.p-zhuji:before {\n  content: \"\\e629\";\n}\n\n.p-pcm:before {\n  content: \"\\e7b2\";\n}\n\n.p-run-menu:before {\n  content: \"\\e636\";\n}\n\n.p-ssh-menu:before {\n  content: \"\\e668\";\n}\n\n.p-mcp-menu:before {\n  content: \"\\e62b\";\n}\n\n.p-ai-menu:before {\n  content: \"\\e7bf\";\n}\n\n.p-moxing-menu:before {\n  content: \"\\e62c\";\n}\n\n.p-ah-menu:before {\n  content: \"\\e6ba\";\n}\n\n.p-tamper-menu:before {\n  content: \"\\e61c\";\n}\n\n.p-ssl-menu:before {\n  content: \"\\e7b0\";\n}\n\n.p-file-menu:before {\n  content: \"\\e609\";\n}\n\n.p-system-monitor-menu:before {\n  content: \"\\e621\";\n}\n\n.p-gpu-menu:before {\n  content: \"\\e631\";\n}\n\n.p-waf-menu:before {\n  content: \"\\e623\";\n}\n\n.p-xsetting-menu:before {\n  content: \"\\e624\";\n}\n\n.p-disk-menu:before {\n  content: \"\\e6e5\";\n}\n\n.p-exchange-menu:before {\n  content: \"\\e625\";\n}\n\n.p-mobile-menu:before {\n  content: \"\\e628\";\n}\n\n.p-process-menu:before {\n  content: \"\\ec35\";\n}\n\n.p-firewalld-menu:before {\n  content: \"\\e798\";\n}\n\n.p-monitor-menu:before {\n  content: \"\\e64a\";\n}\n\n.p-node-menu:before {\n  content: \"\\e813\";\n}\n\n.p-featureshitu:before {\n  content: \"\\e63e\";\n}\n\n.p-youhuawendang:before {\n  content: \"\\e7c4\";\n}\n\n.p-cluster-3:before {\n  content: \"\\e706\";\n}\n\n.p-cluster-2:before {\n  content: \"\\e61f\";\n}\n\n.p-cluster-1:before {\n  content: \"\\e7d8\";\n}\n\n.p-start:before {\n  content: \"\\e688\";\n}\n\n.p-stop:before {\n  content: \"\\e750\";\n}\n\n.p-node-4:before {\n  content: \"\\e73c\";\n}\n\n.p-node-3:before {\n  content: \"\\e7d7\";\n}\n\n.p-file-2:before {\n  content: \"\\e6bf\";\n}\n\n.p-file-1:before {\n  content: \"\\e742\";\n}\n\n.p-file-3:before {\n  content: \"\\e607\";\n}\n\n.p-huobao1:before {\n  content: \"\\e604\";\n}\n\n.p-gerenzhongxin1:before {\n  content: \"\\e61e\";\n}\n\n.p-jiqiren2:before {\n  content: \"\\e61b\";\n}\n\n.p-terminal2:before {\n  content: \"\\e82f\";\n}\n\n.p-tuijian:before {\n  content: \"\\e627\";\n}\n\n.p-node-1:before {\n  content: \"\\e61a\";\n}\n\n.p-node-2:before {\n  content: \"\\e647\";\n}\n\n.p-tongyijiancha:before {\n  content: \"\\e619\";\n}\n\n.p-alert-3:before {\n  content: \"\\e728\";\n}\n\n.p-file-zip:before {\n  content: \"\\e606\";\n}\n\n.p-file-normal:before {\n  content: \"\\e7ac\";\n}\n\n.p-txt:before {\n  content: \"\\e6e3\";\n}\n\n.p-file-folder:before {\n  content: \"\\e600\";\n}\n\n.p-file-unknown:before {\n  content: \"\\e601\";\n}\n\n.p-file-txt:before {\n  content: \"\\e602\";\n}\n\n.p-language:before {\n  content: \"\\e605\";\n}\n\n.p-theme:before {\n  content: \"\\e638\";\n}\n\n.p-arrow-right:before {\n  content: \"\\e665\";\n}\n\n.p-docker:before {\n  content: \"\\e659\";\n}\n\n.p-appstore:before {\n  content: \"\\eb65\";\n}\n\n.p-website:before {\n  content: \"\\e781\";\n}\n\n.p-config:before {\n  content: \"\\e78e\";\n}\n\n.p-appstore1:before {\n  content: \"\\e792\";\n}\n\n.p-log:before {\n  content: \"\\e793\";\n}\n\n.p-host:before {\n  content: \"\\e7b1\";\n}\n\n.p-home:before {\n  content: \"\\e7c6\";\n}\n\n.p-plan:before {\n  content: \"\\e746\";\n}\n\n.p-database:before {\n  content: \"\\e754\";\n}\n\n.p-rejected-order:before {\n  content: \"\\e75e\";\n}\n\n.p-toolbox:before {\n  content: \"\\e769\";\n}\n\n.p-yingwen:before {\n  content: \"\\e6c3\";\n}\n\n.p-zhongwen:before {\n  content: \"\\e6c8\";\n}\n\n.p-logout:before {\n  content: \"\\e8fe\";\n}\n\n.p-taolun:before {\n  content: \"\\e603\";\n}\n\n.p-bug:before {\n  content: \"\\e616\";\n}\n\n.p-huaban88:before {\n  content: \"\\e67c\";\n}\n\n.p-star:before {\n  content: \"\\e60f\";\n}\n\n.p-file-ppt:before {\n  content: \"\\e6e2\";\n}\n\n.p-file-html:before {\n  content: \"\\e608\";\n}\n\n.p-file-word:before {\n  content: \"\\e6e4\";\n}\n\n.p-file-excel:before {\n  content: \"\\e6e6\";\n}\n\n.p-file-pdf:before {\n  content: \"\\e6e7\";\n}\n\n.p-file-mp3:before {\n  content: \"\\e6e8\";\n}\n\n.p-file-svg:before {\n  content: \"\\e6e9\";\n}\n\n.p-file-jpg:before {\n  content: \"\\e6ea\";\n}\n\n.p-file-video:before {\n  content: \"\\e6eb\";\n}\n\n.p-file-png:before {\n  content: \"\\e7ae\";\n}\n\n.p-yanzhengma1:before {\n  content: \"\\e744\";\n}\n\n.p-caidan:before {\n  content: \"\\e61d\";\n}\n\n.p-xiangqing:before {\n  content: \"\\e677\";\n}\n\n.p-webdav:before {\n  content: \"\\e622\";\n}\n\n.p-tongji:before {\n  content: \"\\e856\";\n}\n\n.p-tamper-4:before {\n  content: \"\\e7c3\";\n}\n\n.p-tamper-2:before {\n  content: \"\\e610\";\n}\n\n.p-tamper-3:before {\n  content: \"\\ec4d\";\n}\n\n.p-tamper-1:before {\n  content: \"\\e687\";\n}\n\n.p-setting-1:before {\n  content: \"\\e626\";\n}\n\n.p-setting-2:before {\n  content: \"\\e630\";\n}\n\n.p-setting-3:before {\n  content: \"\\e617\";\n}\n\n.p-waf-4:before {\n  content: \"\\e60a\";\n}\n\n.p-waf-1:before {\n  content: \"\\e62a\";\n}\n\n.p-waf-2:before {\n  content: \"\\e682\";\n}\n\n.p-waf-3:before {\n  content: \"\\e666\";\n}\n\n.p-xpack:before {\n  content: \"\\e60b\";\n}\n\n.p-monitor-4:before {\n  content: \"\\ec4e\";\n}\n\n.p-monitor-2:before {\n  content: \"\\ec4f\";\n}\n\n.p-monitor-1:before {\n  content: \"\\e60d\";\n}\n\n.p-monitor-3:before {\n  content: \"\\ec50\";\n}\n\n.p-m-ios:before {\n  content: \"\\e60e\";\n}\n\n.p-m-pc:before {\n  content: \"\\e771\";\n}\n\n.p-m-theworld:before {\n  content: \"\\e947\";\n}\n\n.p-m-android:before {\n  content: \"\\e9e0\";\n}\n\n.p-m-tencent:before {\n  content: \"\\e60c\";\n}\n\n.p-m-windows:before {\n  content: \"\\e6a0\";\n}\n\n.p-m-machine:before {\n  content: \"\\e862\";\n}\n\n.p-m-mobile:before {\n  content: \"\\e620\";\n}\n\n.p-m-ucweb:before {\n  content: \"\\e611\";\n}\n\n.p-m-edge:before {\n  content: \"\\e8e2\";\n}\n\n.p-m-2345explorer:before {\n  content: \"\\e612\";\n}\n\n.p-m-chrome:before {\n  content: \"\\ea09\";\n}\n\n.p-m-opera:before {\n  content: \"\\ea0e\";\n}\n\n.p-m-linux:before {\n  content: \"\\e80b\";\n}\n\n.p-m-maxthon:before {\n  content: \"\\e676\";\n}\n\n.p-m-mac:before {\n  content: \"\\ef2d\";\n}\n\n.p-m-ie:before {\n  content: \"\\eaab\";\n}\n\n.p-Chrome-OS:before {\n  content: \"\\e613\";\n}\n\n.p-m-safari:before {\n  content: \"\\e614\";\n}\n\n.p-m-360se:before {\n  content: \"\\e678\";\n}\n\n.p-Firefox:before {\n  content: \"\\e87c\";\n}\n\n.p-docker1:before {\n  content: \"\\e76a\";\n}\n\n.p-alert-1:before {\n  content: \"\\e615\";\n}\n\n.p-alert-2:before {\n  content: \"\\e701\";\n}\n\n.p-17:before {\n  content: \"\\e618\";\n}\n\n"
  },
  {
    "path": "frontend/src/assets/iconfont/iconfont.js",
    "content": "window._iconfont_svg_string_4776196='<svg><symbol id=\"p-tuichudenglu3\" viewBox=\"0 0 1024 1024\"><path d=\"M0 0h1024v1024H0z\" fill=\"#FFFFFF\" opacity=\".01\" ></path><path d=\"M603.204267 630.715733h-241.834667a19.012267 19.012267 0 0 1-21.026133-14.779733 31.095467 31.095467 0 0 1-0.375467-5.870933v-208.213334a18.261333 18.261333 0 0 1 20.48-20.206933h242.653867V190.020267a18.056533 18.056533 0 0 1 10.922666-17.92 17.7152 17.7152 0 0 1 20.855467 4.676266c3.413333 3.413333 7.168 6.826667 10.6496 10.478934l132.676267 132.4032q85.333333 84.957867 170.3936 170.052266a18.056533 18.056533 0 0 1 0.170666 28.637867q-121.890133 121.7536-243.848533 243.4048-34.7136 34.577067-69.495467 69.051733a18.2272 18.2272 0 0 1-20.650666 5.495467 17.954133 17.954133 0 0 1-11.5712-19.012267zM457.216 174.216533v70.144H272.384A63.658667 63.658667 0 0 0 206.916267 309.930667c0 130.6624 0.273067 261.290667-0.170667 391.953066a65.365333 65.365333 0 0 0 50.4832 64 89.736533 89.736533 0 0 0 16.418133 1.467734h183.466667v69.768533a13.346133 13.346133 0 0 1-2.7648 0.546133c-62.600533 0-125.3376 0.648533-187.938133-0.273066a134.2464 134.2464 0 0 1-127.146667-107.52 135.953067 135.953067 0 0 1-2.491733-25.9072c-0.170667-132.027733-0.443733-264.157867 0-396.1856a130.8672 130.8672 0 0 1 75.4688-120.456534 114.449067 114.449067 0 0 1 54.613333-13.2096c62.1568-0.170667 124.2112 0 186.368 0 1.2288 0.034133 2.321067 0.1024 3.9936 0.1024z m0 0\"  ></path></symbol><symbol id=\"p-renwuzhongxin1\" viewBox=\"0 0 1024 1024\"><path d=\"M293.63 603.53l368.709-0.553 0.072 48-368.71 0.553zM293.903 416.53l246.768-0.37 0.072 48-246.768 0.37z\"  ></path><path d=\"M833.477 365.696l-0.006-0.007-222.66-222.66-7.03-7.029H271.5c-48.523 0-88 39.477-88 88v576c0 48.523 39.477 88 88 88h481c48.523 0 88-39.477 88-88V372.719l-7.023-7.023z m-74.918-7.036H617.84V217.941l70.359 70.359 70.36 70.36zM752.5 840h-481c-22.056 0-40-17.944-40-40V224c0-22.056 17.944-40 40-40h298.34v198.66c0 13.255 10.745 24 24 24H792.5V800c0 22.056-17.944 40-40 40z\"  ></path></symbol><symbol id=\"p-zhuji\" viewBox=\"0 0 1024 1024\"><path d=\"M747.52 51.2H327.68c-40.96 0-71.68 30.72-71.68 71.68v783.36c0 35.84 30.72 66.56 71.68 66.56h424.96c40.96 0 71.68-30.72 71.68-71.68V122.88c-5.12-40.96-35.84-71.68-76.8-71.68zM768 901.12c0 10.24-10.24 20.48-20.48 20.48H327.68c-10.24 0-20.48-10.24-20.48-20.48V122.88c0-10.24 10.24-20.48 20.48-20.48h424.96c5.12 0 15.36 10.24 15.36 20.48v778.24z\"  ></path><path d=\"M691.2 204.8H384c-15.36 0-25.6 10.24-25.6 25.6S368.64 256 384 256h307.2c15.36 0 25.6-10.24 25.6-25.6s-10.24-25.6-25.6-25.6z m0 102.4H384c-15.36 0-25.6 10.24-25.6 25.6s10.24 25.6 25.6 25.6h307.2c15.36 0 25.6-10.24 25.6-25.6s-10.24-25.6-25.6-25.6zM537.6 665.6c-40.96 0-76.8 35.84-76.8 76.8s35.84 76.8 76.8 76.8 76.8-35.84 76.8-76.8-35.84-76.8-76.8-76.8z m0 102.4c-15.36 0-25.6-10.24-25.6-25.6s10.24-25.6 25.6-25.6 25.6 10.24 25.6 25.6-10.24 25.6-25.6 25.6z\"  ></path></symbol><symbol id=\"p-pcm\" viewBox=\"0 0 1024 1024\"><path d=\"M165.327238 666.672762V229.327238H773.36381V341.333333h58.660571V202.654476c0-17.65181-14.336-31.98781-32.012191-31.987809H138.703238c-17.67619 0-32.01219 14.336-32.01219 32.01219v490.642286c0 17.67619 14.336 32.01219 32.01219 32.01219h394.654476v-58.660571H165.327238z\"  ></path><path d=\"M298.666667 405.333333h234.666666v58.660572H298.666667v-58.660572zM533.333333 821.345524v-37.351619H298.666667v58.684952h242.834285a31.890286 31.890286 0 0 1-8.167619-21.357714zM576 416.01219c0-17.67619 14.336-32.01219 32.01219-32.01219h277.333334c17.65181 0 31.98781 14.336 31.987809 32.01219v405.333334c0 17.65181-14.336 31.98781-32.01219 31.987809H608.01219a31.98781 31.98781 0 0 1-32.01219-32.01219V416.036571z m58.660571 378.636191h224.012191V442.684952H634.63619v352.012191z\"  ></path></symbol><symbol id=\"p-run-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M888.875 155.75000029h-749.99999971c-20.62500029 0-37.50000029 16.875-37.50000029 37.49999942v521.25000029c0 20.62500029 16.875 37.50000029 37.50000029 37.50000029h749.99999971c20.62500029 0 37.50000029-16.875 37.50000029-37.50000029V193.24999971c0-20.62500029-16.875-37.50000029-37.50000029-37.49999942z m-37.50000029 521.24999942h-675V230.75h675v446.24999971zM740.75000029 830.75000029h-450c-20.62500029 0-37.50000029 16.875-37.50000029 37.49999942s16.875 37.50000029 37.50000029 37.50000029h450c20.62500029 0 37.50000029-16.875 37.49999942-37.50000029s-16.875-37.50000029-37.49999942-37.49999942z\"  ></path><path d=\"M384.49999971 622.62500029c7.49999971 3.75000029 15.00000029 5.625 22.5 5.625 7.49999971 0 16.875-1.87499971 24.37500058-5.625l238.12499971-138.75000029c15.00000029-9.37500029 22.5-24.37499971 22.5-41.24999971 0-16.875-9.37500029-31.87500029-22.5-41.25000058l-238.12499971-138.74999942c-15.00000029-9.37500029-31.87500029-9.37500029-46.87500058 0S361.99999971 287 361.99999971 303.875v275.625c-1.87499971 18.74999971 7.49999971 33.75 22.5 43.12500029z m41.25000058-76.87500029v-206.25000029l178.12499942 103.12500058-178.12499942 103.12499971z\"  ></path></symbol><symbol id=\"p-ssh-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M830.06805333 155.10186667c-172.30506667 0-315.66506667-117.80096-317.41269333-118.94784L494.08682667 20.48l-18.56853334 15.67402667s-144.45226667 118.94784-316.75733333 118.94784H129.70666667v489.71776c1.14688 7.59125333 24.95829333 182.79082667 355.09589333 300.59178666l9.88501333 3.49525334 9.8304-3.49525334c329.64608-117.20021333 354.00362667-293.00053333 355.15050667-303.48629333V155.10186667h-29.60042667zM494.08682667 887.41205333c-280.22101333-103.27381333-305.78005333-246.03306667-306.3808-246.03306666V211.95434667c144.50688-9.28426667 262.30784-84.70528 306.3808-117.20021334 44.12757333 32.49493333 162.52928 107.91594667 306.98154666 117.20021334v425.92938666s-23.7568 145.65376-306.92693333 249.52832z\"  ></path><path d=\"M624.66730667 362.25024a130.74432 130.74432 0 0 0-130.52586667-130.52586667 130.74432 130.74432 0 0 0-130.58048 130.52586667c0 62.09536 43.52682667 113.75957333 101.5808 127.08522667v244.88618666h57.99936V625.0496h91.64117333V567.15946667h-91.64117333V489.33546667a130.63509333 130.63509333 0 0 0 101.5808-127.08522667z m-203.10698667 0a72.58112 72.58112 0 1 1 145.16224 0.05461333 72.58112 72.58112 0 0 1-145.16224 0z\"  ></path></symbol><symbol id=\"p-mcp-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M856.874667 662.4l51.285333 30.762667a21.333333 21.333333 0 0 1 0 36.608l-374.186667 224.512a42.666667 42.666667 0 0 1-43.946666 0l-374.186667-224.512a21.333333 21.333333 0 0 1 0-36.608l51.285333-30.762667L512 869.333333l344.874667-206.933333z m0-200.533333l51.285333 30.762666a21.333333 21.333333 0 0 1 0 36.608L512 766.890667l-396.16-237.653334a21.333333 21.333333 0 0 1 0-36.608l51.285333-30.762666L512 668.8l344.874667-206.933333z m-322.944-392.149334l374.229333 224.512a21.333333 21.333333 0 0 1 0 36.608L512 568.490667l-396.16-237.653334a21.333333 21.333333 0 0 1 0-36.608l374.186667-224.512a42.666667 42.666667 0 0 1 43.946666 0zM512 156.032L251.178667 312.533333 512 469.034667l260.821333-156.501334L512 156.032z\"  ></path></symbol><symbol id=\"p-ai-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M905.75 129.5H118.25c-20.62500029 0-37.50000029 16.875-37.50000029 37.50000029v590.625c0 20.62500029 16.875 37.50000029 37.50000029 37.49999942h176.24999971l-14.99999942 50.625c-3.75000029 11.25-1.87499971 24.37499971 5.625 33.75s18.74999971 15.00000029 29.99999971 15.00000029h393.75c11.25 0 22.5-5.625 29.99999971-15.00000029 7.49999971-9.37500029 9.37500029-22.5 5.625-33.75l-14.99999942-50.625H905.75c20.62500029 0 37.50000029-16.875 37.50000029-37.49999942V167.00000029c0-20.62500029-16.875-37.50000029-37.50000029-37.50000029zM365.75 819.50000029l7.49999971-22.5h277.50000058l7.49999971 22.5H365.75zM868.24999971 204.49999971v515.62500029H155.75000029V204.49999971h712.49999942z\"  ></path><path d=\"M675.125 298.25h65.62500029v328.12499971h-65.62500029zM399.5 547.62499971h91.87499971l24.37500058 78.75h95.625l-116.25000029-328.12499971H399.5l-116.25000029 328.12499971h93.75000029l22.5-78.75z m26.25000029-88.12499942c5.625-20.62500029 13.12499971-41.24999971 18.74999971-63.75000058v1.87500058c5.625 20.62500029 11.25 41.24999971 18.74999971 61.875l5.625 20.62499942h-48.74999942l5.625-20.62499942z\"  ></path></symbol><symbol id=\"p-moxing-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M386.95424 122.34752a187.20768 187.20768 0 0 1 187.00288 0l197.40672 113.80736a187.20768 187.20768 0 0 1 93.70112 162.18624v41.81504a38.52288 38.52288 0 1 1-77.04064 0v-41.81504c0-9.02656-1.12128-17.8944-3.23584-26.43968l-267.7504 154.58816v316.7488a110.19776 110.19776 0 0 0 18.44224-8.4736l61.72672-35.64032a38.53312 38.53312 0 0 1 52.62848 14.10048 38.53824 38.53824 0 0 1-14.1056 52.62848l-61.72672 35.63008a187.19232 187.19232 0 0 1-187.10016 0.05632l-197.35552-113.77152a187.21792 187.21792 0 0 1-93.70624-162.18624V398.34112a187.2128 187.2128 0 0 1 93.70624-162.18624l197.40672-113.80736z m-211.456 252.2112a110.1824 110.1824 0 0 0-2.6112 23.7824v227.24096a110.1824 110.1824 0 0 0 55.1424 95.44192l197.3504 113.77152c4.73088 2.72896 9.6256 5.06368 14.61248 7.02976v-314.55744l-264.4992-152.704z m359.97696-185.45664a110.16192 110.16192 0 0 0-110.03904 0L228.02944 302.90432c-3.67616 2.11456-7.18848 4.43904-10.5472 6.92736l260.352 150.3232 263.27552-151.99232a109.568 109.568 0 0 0-8.23296-5.26336l-197.4016-113.79712z\"  ></path><path d=\"M744.09472 525.83424a35.11296 35.11296 0 0 0-32.60416-22.15424 35.14368 35.14368 0 0 0-32.66048 22.07232l-83.66592 208.9216a29.07136 29.07136 0 0 0 53.98016 21.62176l18.10944-45.19936h87.9616l17.95584 45.12768a29.06112 29.06112 0 0 0 37.76 16.27648 29.0816 29.0816 0 0 0 16.27136-37.76512l-83.10784-208.90112z m-53.52448 127.10912l20.84352-52.0448 20.69504 52.0448h-41.53856z m198.98368-159.54944a29.0816 29.0816 0 0 0-29.07648 29.07648v223.01696a29.06624 29.06624 0 1 0 58.14784 0v-223.01696a29.07136 29.07136 0 0 0-29.07136-29.07648z\"  ></path></symbol><symbol id=\"p-ah-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M243.2 364.544l-2.048 301.056 260.096 153.6 264.704-147.456 2.048-300.544-262.144-153.6-262.656 146.944z m241.152 401.408l-206.848-119.296 2.048-243.2L486.4 512l-2.048 253.952z m243.2-115.2l-206.848 115.2 2.048-256 206.848-104.448-2.048 245.248zM505.856 480.256L307.2 375.296l200.704-115.2 198.144 117.248c0 0.512-200.704 102.912-200.192 102.912z\"  ></path><path d=\"M665.6 151.552L631.296 204.8l208.896 125.952-4.096 381.952-64 38.4 31.744 55.296 93.696-55.296 4.096-456.704v-2.048L665.6 151.552zM170.496 322.048l336.896-189.952 66.048 40.448 31.744-53.248-95.232-57.344h-2.048l-399.36 221.696-2.048 277.504h61.952l2.048-239.104z m330.752 582.656l-334.848-196.096 2.048-76.8-61.952-2.048-2.048 115.2 394.752 232.448h2.048l241.152-136.704 2.048-2.048-31.744-55.296-211.456 121.344z\"  ></path></symbol><symbol id=\"p-tamper-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M850.74317938 513.27563469c0-192.37253062-155.98871156-348.33866438-348.33866344-348.33866344h-0.44026406l-0.40639688 0.02257688c-192.01128844 0.44026312-347.49200344 156.21448687-347.49200344 348.31608656 0 192.05644406 155.480715 347.85324562 347.49200344 348.31608656l0.40639687 0.02257781h0.44026407c192.34995281 0 348.33866438-155.98871156 348.33866344-348.33866437zM431.57855094 216.33490156c-25.8174975 26.90122313-47.66133562 67.76669812-63.23988656 117.80997188a464.83913438 464.83913438 0 0 1-83.24365032-34.49858906 307.28127937 307.28127937 0 0 1 146.48353688-83.31138282z m-164.96331094 103.80056438a490.04703656 490.04703656 0 0 0 94.442145 39.84948187c-10.80338625 42.64910625-17.27187187 90.72812812-18.25399875 141.75352782H197.05362219a306.03951094 306.03951094 0 0 1 69.56161781-181.60300969zM197.05362219 528.48165594h145.74976406c0.98212594 51.00282188 7.45061156 99.08184375 18.25399875 141.71966062a492.5870175 492.5870175 0 0 0-94.442145 39.88334907 305.99435531 305.99435531 0 0 1-69.56161781-181.60300969z m87.95108156 202.00188094a464.26340531 464.26340531 0 0 1 83.31138281-34.52116594c15.57855094 50.08842938 37.42238906 91.01034844 63.26246438 117.93414843a307.50705562 307.50705562 0 0 1-146.57384719-83.41298249z m205.86265312 90.36688687c-36.31608656-9.48259688-73.19661375-57.76481625-96.65475093-131.98871156a463.67638781 463.67638781 0 0 1 96.65475093-12.92568188v144.91439344z m0-171.63499594a490.48729969 490.48729969 0 0 0-103.94731968 13.91909719c-10.01317031-39.40921875-16.23330188-84.84666-17.20413844-134.65286906h121.16274656v120.73377187z m0-147.46566281H369.71589875c0.98212594-49.85136375 7.21354687-95.32267125 17.22671625-134.75446875a491.90968969 491.90968969 0 0 0 103.93603031 13.86265313v120.89181562z m0-147.60112875a463.24741313 463.24741313 0 0 1-96.60959625-12.92568281c23.46942656-74.14487344 60.327375-122.37064875 96.60959625-131.83066781v144.75635062z m320.5682025 147.60112875H664.85042375c-0.98212594-50.980245-7.43932313-98.980245-18.23142094-141.595485a493.77234281 493.77234281 0 0 0 95.18720625-40.0865475 306.00564469 306.00564469 0 0 1 69.62935031 181.6820325z m-88.05268031-202.11477a466.78080938 466.78080938 0 0 1-84.01128937 34.73565375c-15.634995-50.30291625-37.58043281-91.34901188-63.52210688-118.30667906a307.7215425 307.7215425 0 0 1 147.53339625 83.57102531z m-205.77234281-89.93791125c36.04515562 9.97930406 72.56444062 58.02445875 95.84195625 131.72906906a466.13734687 466.13734687 0 0 1-95.84195625 12.72248344V209.69708375z m0 171.18344344c35.43555938-0.9708375 69.92286-5.6444025 103.134525-13.73847656 10.01317031 39.40921875 16.22201344 84.82408313 17.19285 134.596425H517.61053625V380.88052719z m0 147.60112875h120.327375c-0.98212594 49.72718719-7.17967969 95.08560656-17.15898375 134.47224844a490.1825025 490.1825025 0 0 0-103.16839125-13.73847657V528.48165594z m0 292.05268125V675.93603031c32.92944469 0.93697125 64.96707469 5.30573813 95.86453406 12.74506125-23.26622812 73.73847563-59.79680156 121.87394156-95.86453406 131.85324563z m58.22765719-6.40075313c25.98682969-26.94637781 47.9322675-68.02634063 63.57855187-118.40827875a466.08090281 466.08090281 0 0 1 84.05644406 34.78080938 307.38287906 307.38287906 0 0 1-147.63499593 83.62746937z m70.81467562-144.18062062c10.76952-42.59266219 17.21542781-90.57008438 18.19755469-141.46001906h146.58513562a305.93791125 305.93791125 0 0 1-69.55032843 181.58043281 493.58043281 493.58043281 0 0 0-95.23236188-40.12041375zM928.06020688 669.73847563c-7.76669812 0-12.93697125 5.17027312-12.93697032 12.93697124v155.26622719c0 49.16274656-41.40733781 90.57008438-90.57008531 90.57008532H695.16086562c-7.76669812 0-12.93697125 5.17027312-12.93697125 12.93697031s5.17027312 12.93697125 12.93697125 12.93697125h129.38099719a115.95860812 115.95860812 0 0 0 116.44402594-116.44402688V682.67544688c0.01128844-7.76669812-5.17027312-12.93697125-12.92568188-12.93697126zM89.12135469 347.30762c7.76669812 0 12.93697125-5.17027312 12.93697125-12.93697125V181.68955812c0-49.16274656 41.40733781-90.57008438 90.57008437-90.57008531h160.43650032c7.76669812 0 12.93697125-5.17027312 12.93697125-12.93697031s-5.17027312-12.93697125-12.93697125-12.93697125H192.62841031A115.95860812 115.95860812 0 0 0 76.18438344 181.68955812v152.66980219c0 7.76669812 5.17027312 12.94825969 12.93697125 12.94825969zM339.08936937 928.50047H181.24929406c-49.16274656 0-90.57008438-41.40733781-90.57008437-90.57008438V680.07902187c0-7.76669812-5.17027312-12.93697125-12.93697032-12.93697124s-12.93697125 5.17027312-12.93697125 12.93697125v157.85136374a115.95860812 115.95860812 0 0 0 116.44402594 116.44402688h157.85136469c7.76669812 0 12.93697125-5.17027312 12.93697031-12.93697125 0-7.75540969-5.18156156-12.93697125-12.94825969-12.93697125zM824.54186281 65.24553125H669.28692406c-7.76669812 0-12.93697125 5.17027312-12.93697125 12.93697125s5.17027312 12.93697125 12.93697125 12.93697031h155.26622719c49.16274656 0 90.57008438 41.40733781 90.57008531 90.57008532v152.66980218c0 7.76669812 5.17027312 12.93697125 12.93697032 12.93697125s12.93697125-5.17027312 12.93697125-12.93697125V181.68955812c0-64.68485438-51.7591725-116.44402594-116.45531532-116.44402687z\"  ></path></symbol><symbol id=\"p-ssl-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M167.1 99.1v825h684.6v-825H167.1z m653.5 790.8H198.2V133.3h622.4v756.6zM704 496.7c-2.2-3.3-2.1-2.9-7.9-9.2-2.8-3.1-5.8-5.7-9.2-8-83.6-54.1-171.5 4.6-171.5 83.8 0 17.4 4.2 33.8 11.7 48.2l-76 151.6 69.8-7.6 35.7 60.4 62.1-124 71 123.7 32.2-62.3L792 757l-81.1-141.5c18.8-32.6 20.6-76.2-6.9-118.8zM554.9 766.4l-21.3-36.1-41.7 4.5 50.7-101.2c15.8 17.4 37.4 29.5 61.8 33.2l0.3 0.5-49.8 99.1z m7.9-145.9c-15.3-15.3-23.7-35.7-23.7-57.3 0-21.6 8.4-42 23.7-57.3 15.3-15.3 35.7-23.7 57.3-23.7s42 8.4 57.3 23.7 23.7 35.7 23.7 57.3c0 21.6-8.4 42-23.7 57.3-15.3 15.3-35.7 23.7-57.3 23.7s-42-8.3-57.3-23.7z m187.1 110.6l-41.9-2.2-19.3 37.3-56.7-98.9c24.8-2.8 47-14.2 63.4-31.2l54.5 95z m30.7-573.2H238.2V174h542.4v-16.1z m-73.9 129.8H279.2v32.5h427.6v-32.5zM279.2 430h244.9v-32.5H279.2V430z m0 109.8h183.7v-32.5H279.2v32.5z m340.9-45.4v16.3c29 0 52.6 23.6 52.6 52.6s-23.6 52.6-52.6 52.6v16.3c38 0 68.8-30.9 68.8-68.9 0.1-38-30.8-68.9-68.8-68.9z\"  ></path></symbol><symbol id=\"p-file-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M791.375 321.40625c-10.96875-58.6875-62.53125-103.3125-124.40625-103.3125H521.5625c-26.34375-33-67.21875-54.1875-113.0625-54.1875H236.1875c-79.40625 0-144 63.5625-144 141.65625v415.875c0 78.09375 64.59375 141.65625 144 141.65625h552.1875c79.40625 0 144-63.5625 144-141.65625V462.875c0-77.0625-63-139.875-141-141.46875z m-124.40625-52.6875c33.75 0 62.34375 22.125 72.1875 52.5H552.5v-15.75c0-12.75-1.875-25.03125-5.0625-36.75h119.53125z m-524.15625 36.75c0-52.03125 41.90625-94.40625 93.375-94.40625h172.40625c51.46875 0 93.375 42.375 93.375 94.40625v15.75h-359.0625v-15.75z m738.84375 415.875c0 52.03125-41.90625 94.40625-93.375 94.40625h-552.1875c-51.46875 0-93.375-42.375-93.375-94.40625V368.46875h645.46875c51.46875 0 93.375 42.375 93.375 94.40625v258.46875z\"  ></path></symbol><symbol id=\"p-system-monitor-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M152 152c-22.08 0-39.84 17.76-39.84 39.84v480c0 22.08 17.76 39.84 39.84 39.84h720c22.08 0 39.84-17.76 39.84-39.84v-480c0-22.08-17.76-39.84-39.84-39.84H152z m0-39.84h720c44.16 0 80.16 36 80.16 80.16v480c0 44.16-36 80.16-80.16 80.16H152c-44.16 0-80.16-36-80.16-80.16v-480c0-44.64 36-80.16 80.16-80.16z\"  ></path><path d=\"M825.44 488.96l-141.12-201.6c-2.4-3.84-4.32-6.24-6.72-9.12-4.32-4.32-8.64-6.24-13.44-6.24-4.8 0-9.6 1.92-13.44 6.24-3.36 3.84-3.36 4.8-6.72 9.12l-166.08 237.12-120-153.6c-7.68-9.6-18.72-10.08-26.4-5.28-4.32 2.88-6.24 5.28-9.12 9.12L189.44 569.6c-7.68 11.04-5.76 20.16 5.28 27.84 11.04 7.68 20.16 5.76 27.84-5.28L341.6 416l120.48 154.56c8.16 10.56 20.16 10.56 27.84 3.36 2.88-2.4 6.72-8.16 10.08-12.96l163.2-233.28 128.64 183.84c7.68 11.04 16.8 12.48 27.84 4.8 11.52-7.2 13.44-16.32 5.76-27.36zM172.16 872h680.16c13.44 0 20.16 6.72 20.16 20.16 0 13.44-6.72 20.16-20.16 20.16H172.16c-13.44 0-20.16-6.72-20.16-20.16 0-13.44 6.72-20.16 20.16-20.16z\"  ></path><path d=\"M371.84 712.16c13.44 0 20.16 6.72 20.16 20.16v159.84c0 13.44-6.72 20.16-20.16 20.16-13.44 0-20.16-6.72-20.16-20.16v-159.84c0.48-13.44 7.2-20.16 20.16-20.16zM652.16 712.16c13.44 0 20.16 6.72 20.16 20.16v159.84c0 13.44-6.72 20.16-20.16 20.16-13.44 0-20.16-6.72-20.16-20.16v-159.84c0-13.44 6.72-20.16 20.16-20.16z\"  ></path></symbol><symbol id=\"p-gpu-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M725.33333333 800H298.66666667c-41.1712 0-74.66666667-33.49546667-74.66666667-74.66666667V298.66666667c0-41.1712 33.49546667-74.66666667 74.66666667-74.66666667h426.66666666c41.1712 0 74.66666667 33.49546667 74.66666667 74.66666667v426.66666666c0 41.1712-33.49546667 74.66666667-74.66666667 74.66666667zM298.66666667 266.66666667c-17.6448 0-32 14.3552-32 32v426.66666666c0 17.6448 14.3552 32 32 32h426.66666666c17.6448 0 32-14.3552 32-32V298.66666667c0-17.6448-14.3552-32-32-32H298.66666667z\"  ></path><path d=\"M627.2 677.33333333H396.8c-27.64373333 0-50.13333333-22.4896-50.13333333-50.13333333V396.8c0-27.64373333 22.4896-50.13333333 50.13333333-50.13333333h230.4c27.64373333 0 50.13333333 22.4896 50.13333333 50.13333333v230.4c0 27.64373333-22.4896 50.13333333-50.13333333 50.13333333zM396.8 389.33333333c-4.11733333 0-7.46666667 3.34933333-7.46666667 7.46666667v230.4c0 4.11626667 3.34933333 7.46666667 7.46666667 7.46666667h230.4c4.11626667 0 7.46666667-3.3504 7.46666667-7.46666667V396.8c0-4.11733333-3.3504-7.46666667-7.46666667-7.46666667H396.8zM346.66666667 263.4432c-11.7824 0-21.33333333-9.55093333-21.33333334-21.33333333V114.1152c0-11.7824 9.55093333-21.33333333 21.33333334-21.33333333s21.33333333 9.55093333 21.33333333 21.33333333v127.9936c0 11.7824-9.55093333 21.3344-21.33333333 21.3344zM456.88853333 263.4432c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333333V114.1152c0-11.7824 9.55093333-21.33333333 21.33333333-21.33333333s21.33333333 9.55093333 21.33333334 21.33333333v127.9936c0 11.7824-9.55093333 21.3344-21.33333334 21.3344zM567.1104 263.4432c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333333V114.1152c0-11.7824 9.55093333-21.33333333 21.33333333-21.33333333s21.33333333 9.55093333 21.33333333 21.33333333v127.9936c0 11.7824-9.55093333 21.3344-21.33333333 21.3344zM677.33333333 263.4432c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333333V114.1152c0-11.7824 9.55093333-21.33333333 21.33333333-21.33333333s21.33333333 9.55093333 21.33333334 21.33333333v127.9936c0 11.7824-9.55093333 21.3344-21.33333334 21.3344z\"  ></path><path d=\"M346.66666667 930.10986667c-11.7824 0-21.33333333-9.55093333-21.33333334-21.33333334V780.78186667c0-11.7824 9.55093333-21.33333333 21.33333334-21.33333334s21.33333333 9.55093333 21.33333333 21.33333334v127.9936c0 11.7824-9.55093333 21.3344-21.33333333 21.3344zM456.88853333 930.10986667c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333334V780.78186667c0-11.7824 9.55093333-21.33333333 21.33333333-21.33333334s21.33333333 9.55093333 21.33333334 21.33333334v127.9936c0 11.7824-9.55093333 21.3344-21.33333334 21.3344zM567.1104 930.10986667c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333334V780.78186667c0-11.7824 9.55093333-21.33333333 21.33333333-21.33333334s21.33333333 9.55093333 21.33333333 21.33333334v127.9936c0 11.7824-9.55093333 21.3344-21.33333333 21.3344zM677.33333333 930.10986667c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333334V780.78186667c0-11.7824 9.55093333-21.33333333 21.33333333-21.33333334s21.33333333 9.55093333 21.33333334 21.33333334v127.9936c0 11.7824-9.55093333 21.3344-21.33333334 21.3344z\"  ></path><path d=\"M242.66346667 698.112H114.66986667c-11.7824 0-21.33333333-9.55093333-21.33333334-21.33333333s9.55093333-21.33333333 21.33333334-21.33333334h127.9936c11.7824 0 21.33333333 9.55093333 21.33333333 21.33333334s-9.55093333 21.33333333-21.33333333 21.33333333zM242.66346667 587.89013333H114.66986667c-11.7824 0-21.33333333-9.55093333-21.33333334-21.33333333s9.55093333-21.33333333 21.33333334-21.33333333h127.9936c11.7824 0 21.33333333 9.55093333 21.33333333 21.33333333s-9.55093333 21.33333333-21.33333333 21.33333333zM242.66346667 477.66826667H114.66986667c-11.7824 0-21.33333333-9.55093333-21.33333334-21.33333334s9.55093333-21.33333333 21.33333334-21.33333333h127.9936c11.7824 0 21.33333333 9.55093333 21.33333333 21.33333333s-9.55093333 21.33333333-21.33333333 21.33333334zM242.66346667 367.44533333H114.66986667c-11.7824 0-21.33333333-9.55093333-21.33333334-21.33333333s9.55093333-21.33333333 21.33333334-21.33333333h127.9936c11.7824 0 21.33333333 9.55093333 21.33333333 21.33333333s-9.55093333 21.33333333-21.33333333 21.33333333z\"  ></path><path d=\"M909.33013333 698.112H781.33653333c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333333s9.55093333-21.33333333 21.33333333-21.33333334h127.9936c11.7824 0 21.33333333 9.55093333 21.33333334 21.33333334s-9.55093333 21.33333333-21.33333334 21.33333333zM909.33013333 587.89013333H781.33653333c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333333s9.55093333-21.33333333 21.33333333-21.33333333h127.9936c11.7824 0 21.33333333 9.55093333 21.33333334 21.33333333s-9.55093333 21.33333333-21.33333334 21.33333333zM909.33013333 477.66826667H781.33653333c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333334s9.55093333-21.33333333 21.33333333-21.33333333h127.9936c11.7824 0 21.33333333 9.55093333 21.33333334 21.33333333s-9.55093333 21.33333333-21.33333334 21.33333334zM909.33013333 367.44533333H781.33653333c-11.7824 0-21.33333333-9.55093333-21.33333333-21.33333333s9.55093333-21.33333333 21.33333333-21.33333333h127.9936c11.7824 0 21.33333333 9.55093333 21.33333334 21.33333333s-9.55093333 21.33333333-21.33333334 21.33333333z\"  ></path></symbol><symbol id=\"p-waf-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M961.209095 160.830977C796.898228 135.22394 575.9016 49.806099 523.142175 4.956581c-7.875566-6.696521-24.691274-6.456133-32.898804-0.194599-59.993957 46.154481-348.322097 138.074243-427.970628 159.113909-21.199925 5.563263-22.4591 22.894088-21.14269 41.884733 6.673627 151.44439 31.422135 355.808464 94.518241 493.688107 66.690477 145.594951 223.812601 254.52502 363.901523 320.917874a33.631415 33.631415 0 0 0 14.228676 3.434113 33.024721 33.024721 0 0 0 18.681575-3.434113c140.352205-69.048568 284.37891-177.715356 355.430712-320.917874 67.68637-136.402974 88.82906-345.219948 94.895993-496.42395 1.385092-15.224568 0.538011-38.736796-21.577678-42.193804zM831.708688 666.22941c-58.895041 128.584643-168.500485 215.089954-315.938409 291.693571-155.576773-74.714855-253.162822-155.095997-319.612912-278.311977C131.058029 559.039291 111.15162 351.470046 104.420758 213.773555c70.468002-18.807493 342.701598-95.628604 402.935943-142.172284 61.447731 46.394869 324.500799 126.35247 409.151688 143.431459-7.303214 133.564108-29.35022 330.178533-84.708124 451.19668z m0 0\"  ></path><path d=\"M327.729689 739.536279c-33.745885 0-46.62381-17.857388-46.692491-39.824266V319.269511a39.824266 39.824266 0 0 1 39.824265-39.812818h380.453949a39.755583 39.755583 0 0 1 39.686901 39.755583v380.499737c0 21.978324-16.68979 39.824266-50.057923 39.824266z m373.448359-34.341132s5.403005 0 5.494581-5.425899v-76.351782h-176.284476v81.846363h170.789895v-0.11447z m-385.879848-5.425899a5.483134 5.483134 0 0 0 5.494581 5.425899H496.024128v-81.766234H315.2982z m391.488899-110.612784v-62.569542h-76.328888v62.512307z m-110.578443 0v-62.569542H431.405565v62.512307z m-199.018305 0v-62.569542h-81.846363v62.512307z m309.55096-96.784756v-62.500859H530.353812v62.500859z m-210.72863 0v-62.500859H315.2982v62.500859z m210.72863-96.773308s0.274729-57.715995 0-76.283101a5.185511 5.185511 0 0 0-5.563263-5.494581h-70.719837v81.846364h76.317441z m-110.578444 0v-81.800576H431.405565v81.846364h164.711514z m-199.018304 0v-81.800576h-76.2831a5.494581 5.494581 0 0 0-5.494581 5.494581v76.2831z m0 0\"  ></path></symbol><symbol id=\"p-xsetting-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M507.05615234 858.06933594c192.81005859 0 341.12548828-153.25927734 341.12548829-341.12548828s-153.25927734-341.12548828-341.12548829-341.12548828S165.93066406 329.07763672 165.93066406 516.94384766c0 98.87695313 34.60693359 143.37158203 123.59619141 168.09082031 9.88769531 4.94384766 14.83154297 4.94384766 24.71923828 9.88769531 4.94384766 0 24.71923828 4.94384766 24.71923828 9.88769531 54.38232422 19.77539063 84.04541016 29.66308594 103.82080078 54.38232422 19.77539063 19.77539063 29.66308594 39.55078125 44.49462891 64.27001953 4.94384766 9.88769531 9.88769531 19.77539063 9.88769531 24.71923829 4.94384766-4.94384766 4.94384766 4.94384766 9.88769531 9.88769531z m0 44.4946289c-64.27001953 0-24.71923828-44.49462891-93.93310546-118.65234375C343.90917969 719.64160156 121.43603516 759.19238281 121.43603516 516.94384766 121.43603516 304.35839844 294.47070312 131.32373047 507.05615234 131.32373047s385.62011719 173.03466797 385.62011719 385.62011719-168.09082031 385.62011719-385.62011719 385.62011718z m79.1015625-113.70849609c-44.49462891 0-84.04541016-34.60693359-84.04541015-84.04541016s34.60693359-79.1015625 84.04541015-79.1015625c44.49462891 0 84.04541016 34.60693359 84.04541016 79.1015625 0 44.49462891-39.55078125 84.04541016-84.04541016 84.04541016z m0-44.49462891c19.77539063 0 34.60693359-14.83154297 34.6069336-34.60693359 0-24.71923828-14.83154297-29.66308594-34.6069336-29.66308594s-34.60693359 9.88769531-34.60693359 29.66308594c0 14.83154297 14.83154297 34.60693359 34.60693359 34.60693359z m34.6069336-385.62011718c-29.66308594 0-44.49462891-19.77539063-44.49462891-44.49462891s19.77539063-49.43847656 44.49462891-49.43847656c29.66308594 0 44.49462891 19.77539063 44.4946289 44.4946289s-14.83154297 49.43847656-44.4946289 49.43847657z m-123.59619141-39.55078125c-29.66308594 0-44.49462891-19.77539063-44.4946289-44.49462891s19.77539063-44.49462891 44.4946289-44.49462891c29.66308594 0 44.49462891 19.77539063 44.49462891 44.49462891s-19.77539063 44.49462891-44.49462891 44.49462891z m217.52929688 128.54003906c-29.66308594 0-44.49462891-19.77539063-44.49462891-44.49462891s19.77539063-44.49462891 44.49462891-44.4946289c29.66308594 0 44.49462891 19.77539063 44.4946289 44.4946289s-19.77539063 44.49462891-44.4946289 44.49462891zM373.57226562 358.74072266c-29.66308594 0-44.49462891-19.77539063-44.4946289-44.49462891s14.83154297-49.43847656 44.49462891-49.43847656 44.49462891 19.77539063 44.4946289 44.4946289-19.77539063 49.43847656-44.4946289 49.43847657z\"  ></path></symbol><symbol id=\"p-disk-menu\" viewBox=\"0 0 1088 1024\"><path d=\"M291.28185547 163.53125a25.3125 25.3125 0 0 0-25.04882813 21.25195313L147.26427734 794.18164063c-1.95117187 11.6015625 19.98632813 30.42773437 23.25585938 34.27734374 3.26953125 3.90234375 10.546875 10.3359375 22.25390625 10.3359375h691.18945312a28.6875 28.6875 0 0 0 22.46484375-10.546875c3.32226563-3.95507813 33.64453125-11.70703125 31.4296875-23.4140625L797.32091797 184.30859375a25.47070313 25.47070313 0 0 0-24.99609375-20.77734375H291.28185547zM192.77404297 912.25390625a102.515625 102.515625 0 0 1-101.25-119.390625L193.77599609 172.91796875C201.63341797 124.9296875 242.60802734 90.125 291.28185547 90.125h481.04296875c47.56640625 0 88.43554688 33.9609375 97.18945312 80.68359375l115.3828125 619.9453125a102.515625 102.515625 0 0 1-100.93359375 121.5H192.77404297z\"  ></path><path d=\"M832.07287109 598.484375a126.93164063 126.93164063 0 0 1 126.93164063 126.87890625v47.14453125a126.93164063 126.93164063 0 0 1-126.93164063 126.984375H253.78771484A126.93164063 126.93164063 0 0 1 126.85607422 772.5078125v-47.14453125A126.93164063 126.93164063 0 0 1 253.78771484 598.484375h578.28515625z m14.02734375 88.59375H230.63732422a65.75976563 65.75976563 0 0 0 0 131.46679688h615.46289062a65.70703125 65.70703125 0 1 0 0-131.46679688z\"  ></path><path d=\"M866.77208984 752.83789063a35.859375 35.859375 0 1 1-71.71875-1e-8 35.859375 35.859375 0 0 1 71.71875 0\"  ></path><path d=\"M660.31701172 359.86132812h-12.39257813a14.50195313 14.50195313 0 0 1-13.18359375-9.70312499l-6.85546875-17.0859375a14.34375 14.34375 0 0 1 2.42578125-16.08398438l8.70117188-8.70117187a10.17773438 10.17773438 0 0 0 0-14.34375l-14.39648438-14.39648438a10.17773438 10.17773438 0 0 0-14.34375 0l-8.70117187 8.70117188a14.765625 14.765625 0 0 1-16.08398438 2.47851562l-17.13867187-6.9609375a14.66015625 14.66015625 0 0 1-9.65039063-13.18359375v-12.12890625A10.23046875 10.23046875 0 0 0 548.57287109 248.328125h-20.35546875a10.23046875 10.23046875 0 0 0-10.125 10.17773438v12.12890624c0 5.6953125-4.48242188 11.6015625-9.703125 13.13085938l-17.0859375 6.9609375a14.34375 14.34375 0 0 1-16.08398437-2.47851563l-8.70117188-8.70117187a10.17773438 10.17773438 0 0 0-14.34375 0l-14.39648437 14.39648438a10.17773438 10.17773438 0 0 0 0 14.34374999l8.70117187 8.70117188a14.765625 14.765625 0 0 1 2.47851563 16.08398438l-6.9609375 17.08593749a14.66015625 14.66015625 0 0 1-13.18359375 9.70312501h-12.12890625A10.01953125 10.01953125 0 0 0 406.55919922 369.98632812v20.35546876c0 5.6953125 4.74609375 10.125 10.17773437 10.125h12.12890625c5.6953125 0 11.6015625 4.48242188 13.13085938 9.703125l6.9609375 17.0859375a14.29101563 14.29101563 0 0 1-2.47851563 16.08398437l-8.70117187 8.70117188a10.17773438 10.17773438 0 0 0 0 14.34375l14.39648437 14.39648437a10.17773438 10.17773438 0 0 0 14.34375 0l8.70117188-8.70117187a14.765625 14.765625 0 0 1 16.08398437-2.47851563l17.0859375 6.9609375a14.66015625 14.66015625 0 0 1 9.703125 13.18359375v12.12890625c0 5.64257813 4.69335938 10.125 10.125 10.125h20.35546875c5.6953125 0 10.125-4.74609375 10.125-10.17773437v-11.390625c0-5.6953125 4.48242188-11.6015625 9.65039063-13.13085938l17.13867187-6.90820312a14.34375 14.34375 0 0 1 16.08398438 2.47851562l8.70117187 8.6484375a10.17773438 10.17773438 0 0 0 14.34375 0l14.39648438-14.39648437a10.17773438 10.17773438 0 0 0 0-14.34375l-8.70117188-8.70117188a14.765625 14.765625 0 0 1-2.47851562-16.08398437l6.9609375-17.0859375a14.66015625 14.66015625 0 0 1 13.13085937-9.703125h12.12890625A10.01953125 10.01953125 0 0 0 670.23107422 391.08007813V370.671875c0.26367188-6.43359375-4.21875-10.86328125-9.9140625-10.86328125zM588.96740234 380.1640625a50.99414063 50.99414063 0 0 1-51.046875 51.046875 50.99414063 50.99414063 0 0 1-51.09960937-51.046875c0-28.265625 22.83398438-51.046875 51.09960937-51.046875 28.21289063 0 51.046875 22.78125 51.046875 51.046875z\"  ></path></symbol><symbol id=\"p-exchange-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M861.888 500.16V383.744q0-14.912-14.976-14.912h-320.64q-28.032 0-45.952-21.568L390.272 238.912H185.6q-14.912 0-14.912 14.976v490.624q0 14.976 14.912 14.976h266.688a38.4 38.4 0 0 1 0 76.8H185.6q-38.016 0-64.896-26.88-26.88-26.88-26.88-64.896V253.888q0-38.016 26.88-64.896 26.88-26.88 64.896-26.88h212.672q28.032 0 45.952 21.568l90.048 108.352h312.64q38.016 0 64.896 26.88 26.88 26.88 26.88 64.832v116.416a38.4 38.4 0 1 1-76.8 0z m-42.752 55.68l96 85.312a38.4 38.4 0 0 1-25.536 67.136H580.288a38.4 38.4 0 0 1 0-76.8h208.32l-20.48-18.24a38.4 38.4 0 1 1 51.008-57.408z m-168.32 356.032l-96-85.312a38.4 38.4 0 0 1 25.472-67.072H889.6a38.4 38.4 0 0 1 0 76.8h-208.32l20.48 18.24a38.4 38.4 0 0 1-51.008 57.344z\"  ></path></symbol><symbol id=\"p-mobile-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M826.090023 861.946161c0 53.442166-43.324738 96.764857-96.764857 96.764857L294.670741 958.711018c-53.439096 0-96.761788-43.322691-96.761788-96.764857L197.908954 162.052816c0-53.439096 43.322691-96.762811 96.761788-96.762811l434.655448 0c53.44012 0 96.764857 43.323715 96.764857 96.762811L826.091046 861.946161 826.090023 861.946161zM770.25025 162.052816c0-22.602787-18.321274-40.925085-40.924061-40.925085L294.670741 121.127731c-22.601764 0-40.924061 18.322297-40.924061 40.925085l0 699.893344c0 22.602787 18.322297 40.924061 40.924061 40.924061l434.655448 0c22.602787 0 40.924061-18.321274 40.924061-40.924061L770.25025 162.052816 770.25025 162.052816z\"  ></path><path d=\"M797.529547 777.23278 225.18825 777.23278c-7.716752 0-13.959943-6.244214-13.959943-13.95892 0-7.716752 6.244214-13.960966 13.959943-13.960966l572.341297 0c7.701402 0 13.960966 6.244214 13.960966 13.960966C811.48949 770.989589 805.231973 777.23278 797.529547 777.23278z\"  ></path><path d=\"M797.529547 218.845287 225.18825 218.845287c-7.716752 0-13.959943-6.243191-13.959943-13.95892 0-7.716752 6.244214-13.960966 13.959943-13.960966l572.341297 0c7.701402 0 13.960966 6.244214 13.960966 13.960966C811.48949 212.602096 805.231973 218.845287 797.529547 218.845287z\"  ></path><path d=\"M546.898323 836.56305c0 13.495362-10.946306 24.429389-24.429389 24.429389l-6.978948 0c-13.483082 0-24.430412-10.93505-24.430412-24.429389l0-6.980995c0-13.495362 10.94733-24.429389 24.430412-24.429389l6.978948 0c13.483082 0 24.429389 10.937097 24.429389 24.429389L546.898323 836.56305z\"  ></path><path d=\"M575.090409 466.872197c-1.526773 0-3.094478-0.24457-4.623297-0.789992-7.277754-2.549056-11.096732-10.523681-8.547677-17.803481l3.790326-10.782577c2.562359-7.266497 10.497075-11.137665 17.801435-8.534374 7.267521 2.549056 11.098779 10.523681 8.534374 17.803481l-3.78828 10.782577C586.240353 463.286532 580.855716 466.872197 575.090409 466.872197z\"  ></path><path d=\"M521.869277 618.443287c-1.526773 0-3.094478-0.2415-4.623297-0.787946-7.2798-2.537799-11.096732-10.524704-8.547677-17.775852l39.588647-112.7489c2.549056-7.278777 10.510378-11.150968 17.804505-8.547677 7.265474 2.549056 11.096732 10.524704 8.53335 17.790178l-39.588647 112.747877C533.020244 614.875019 527.635607 618.443287 521.869277 618.443287z\"  ></path><path d=\"M465.29477 513.174756c-1.526773 0-3.095501-0.24457-4.623297-0.789992-7.278777-2.535753-11.096732-10.523681-8.547677-17.781992l57.882292-164.883281c2.536776-7.293103 10.510378-11.137665 17.791202-8.547677 7.277754 2.549056 11.097756 10.524704 8.5487 17.790178l-57.882292 164.890444C476.459041 509.603417 471.061101 513.174756 465.29477 513.174756z\"  ></path><path d=\"M438.111664 590.523401c-1.526773 0-3.093455-0.2415-4.621251-0.787946-7.2798-2.537799-11.096732-10.522658-8.5487-17.803481l8.520047-24.211425c2.549056-7.278777 10.497075-11.122315 17.790178-8.534374 7.278777 2.536776 11.097756 10.524704 8.5487 17.803481l-8.520047 24.211425C449.262632 586.956156 443.880041 590.523401 438.111664 590.523401z\"  ></path></symbol><symbol id=\"p-process-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M452 872H184.85C166.91 872 152 856.7 152 837.38V186.62C152 167.3 166.91 152 184.85 152h534.3C737.09 152 752 167.3 752 186.62v171.06a30 30 0 0 0 60 0V186.62C812 134.54 770.6 92 719.15 92H184.85C133.4 92 92 134.54 92 186.62v650.76C92 889.46 133.4 932 184.85 932H452a30 30 0 0 0 0-60z\"  ></path><path d=\"M722 452a240 240 0 1 0 0 480 240 240 0 0 0 0-480z m0 420a180 180 0 1 1 0-360 180 180 0 0 1 0 360z\"  ></path><path d=\"M811.49 677H737v-60a30 30 0 0 0-60 0v90a30 30 0 0 0 30 30h104.49a30 30 0 0 0 0-60zM632 332a30 30 0 0 0-30-30H302a30 30 0 0 0 0 60h300a30 30 0 0 0 30-30zM302 542a30 30 0 0 0 0 60h75.27a30 30 0 0 0 0-60H302z\"  ></path></symbol><symbol id=\"p-firewalld-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M160 224a64 64 0 0 1 64-64h576a64 64 0 0 1 64 64v576a64 64 0 0 1-64 64H224a64 64 0 0 1-64-64V224z m256 480H224v96h192v-96z m384 0H480v96h320v-96z m0-160h-192v96h192v-96z m-256 0H224v96h320v-96z m256-160H480v96h320v-96zM224 480h192v-96H224v96z m320-256H224v96h320V224z m256 0h-192v96h192V224z\"  ></path></symbol><symbol id=\"p-monitor-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M907.17714248 163.57142832h-789.4285708A51.95571416 51.95571416 0 0 0 65.85714248 215.52714248v595.51714336A51.95571416 51.95571416 0 0 0 117.78714248 863h789.42857168a51.95571416 51.95571416 0 0 0 51.95571416-51.95571416V215.52714248A51.95571416 51.95571416 0 0 0 907.17714248 163.57142832z m20.27571504 51.95571416v84.85714336H607.51571416v-105.13285752h299.66142832a20.28857168 20.28857168 0 0 1 20.27571504 20.27571416zM117.78714248 195.25142832h458.04857168v105.15857168h-478.28571416v-84.85714248a20.30142832 20.30142832 0 0 1 20.23714248-20.3014292z m789.42857168 636.06857168h-789.42857168a20.30142832 20.30142832 0 0 1-20.27571416-20.27571416V332.09h829.9414292v478.95428584a20.28857168 20.28857168 0 0 1-20.27571504 20.27571416z\"  ></path><path d=\"M789.97142833 407.48428584h-44.34428585v318.67714248h-63.77142832V574.1h-44.35714248v152.06142832h-63.7714292V408.11428584h-44.34428496v318.04714248h-63.78428584V573.47h-44.34428584v152.69142833h-56.39142832v44.34428583h481.5v-44.34428584H789.97142833V407.48428584zM833.17142833 228.56428584h41.06571415v41.06571416H833.17142833zM746.63 228.56428584h41.06571416v41.06571416h-41.06571416zM660.10142833 228.56428584h41.06571415v41.06571416h-41.06571416zM171.00285752 230.09428584h324.37285664v38.01857168H171.00285752z\"  ></path></symbol><symbol id=\"p-node-menu\" viewBox=\"0 0 1024 1024\"><path d=\"M362 182H122a45 45 0 0 0-45 45v570a45 45 0 0 0 45 45h240a45 45 0 0 0 45-45V227a45 45 0 0 0-45-45z m15 615a15 15 0 0 1-15 15H122a15 15 0 0 1-15-15V227a15 15 0 0 1 15-15h240a15 15 0 0 1 15 15zM902 407H572a45 45 0 0 0-45 45v120a45 45 0 0 0 45 45h330a45 45 0 0 0 45-45v-120a45 45 0 0 0-45-45z m15 165a15 15 0 0 1-15 15H572a15 15 0 0 1-15-15v-120a15 15 0 0 1 15-15h330a15 15 0 0 1 15 15z\"  ></path><path d=\"M902 437H572a15 15 0 0 0-15 15v120a15 15 0 0 0 15 15h330a15 15 0 0 0 15-15v-120a15 15 0 0 0-15-15z m-285 105a15 15 0 0 1-30 0v-60a15 15 0 0 1 30 0z m60 0a15 15 0 0 1-30 0v-60a15 15 0 0 1 30 0z m60 0a15 15 0 0 1-30 0v-60a15 15 0 0 1 30 0z m105 15a45 45 0 1 1 45-45 45 45 0 0 1-45 45z\"  ></path><path d=\"M902 77H572a45 45 0 0 0-45 45v120a45 45 0 0 0 45 45h330a45 45 0 0 0 45-45V122a45 45 0 0 0-45-45z m15 165a15 15 0 0 1-15 15H572a15 15 0 0 1-15-15V122a15 15 0 0 1 15-15h330a15 15 0 0 1 15 15z\"  ></path><path d=\"M902 107H572a15 15 0 0 0-15 15v120a15 15 0 0 0 15 15h330a15 15 0 0 0 15-15V122a15 15 0 0 0-15-15z m-285 105a15 15 0 0 1-30 0V152a15 15 0 0 1 30 0z m60 0a15 15 0 0 1-30 0V152a15 15 0 0 1 30 0z m60 0a15 15 0 0 1-30 0V152a15 15 0 0 1 30 0z m105 15a45 45 0 1 1 45-45 45 45 0 0 1-45 45z\"  ></path><path d=\"M902 737H572a45 45 0 0 0-45 45v120a45 45 0 0 0 45 45h330a45 45 0 0 0 45-45v-120a45 45 0 0 0-45-45z m15 165a15 15 0 0 1-15 15H572a15 15 0 0 1-15-15v-120a15 15 0 0 1 15-15h330a15 15 0 0 1 15 15z\"  ></path><path d=\"M902 767H572a15 15 0 0 0-15 15v120a15 15 0 0 0 15 15h330a15 15 0 0 0 15-15v-120a15 15 0 0 0-15-15z m-285 105a15 15 0 0 1-30 0v-60a15 15 0 0 1 30 0z m60 0a15 15 0 0 1-30 0v-60a15 15 0 0 1 30 0z m60 0a15 15 0 0 1-30 0v-60a15 15 0 0 1 30 0z m105 15a45 45 0 1 1 45-45 45 45 0 0 1-45 45z\"  ></path><path d=\"M842 182m-15 0a15 15 0 1 0 30 0 15 15 0 1 0-30 0Z\"  ></path><path d=\"M842 512m-15 0a15 15 0 1 0 30 0 15 15 0 1 0-30 0Z\"  ></path><path d=\"M842 842m-15 0a15 15 0 1 0 30 0 15 15 0 1 0-30 0Z\"  ></path><path d=\"M542 527H392a15 15 0 0 1 0-30h150a15 15 0 0 1 0 30z\"  ></path><path d=\"M542 857h-45a45 45 0 0 1-45-45V212a45 45 0 0 1 45-45h45a15 15 0 0 1 0 30h-45a15 15 0 0 0-15 15v600a15 15 0 0 0 15 15h45a15 15 0 0 1 0 30z\"  ></path><path d=\"M242 692m-60 0a60 60 0 1 0 120 0 60 60 0 1 0-120 0Z\"  ></path><path d=\"M152 272h180v60H152zM152 392h180v60H152zM152 512h180v60H152z\"  ></path></symbol><symbol id=\"p-featureshitu\" viewBox=\"0 0 1024 1024\"><path d=\"M540.16 96.768l284.16 164.053333a85.333333 85.333333 0 0 1 42.666667 73.898667v328.106667a85.333333 85.333333 0 0 1-42.666667 73.941333l-284.16 164.053333a85.333333 85.333333 0 0 1-85.333333 0L170.666667 736.768a85.333333 85.333333 0 0 1-42.666667-73.898667v-328.106666a85.333333 85.333333 0 0 1 42.666667-73.941334l284.16-164.053333a85.333333 85.333333 0 0 1 85.333333 0zM497.493333 170.666667L213.333333 334.72v328.106667l284.16 164.096 284.16-164.053334v-328.106666L497.493333 170.666667z m137.898667 205.866666a42.666667 42.666667 0 1 1 42.666667 73.898667l-137.898667 79.573333v139.477334a42.666667 42.666667 0 1 1-85.333333 0v-139.477334l-137.898667-79.573333a42.666667 42.666667 0 1 1 42.666667-73.898667l137.898666 79.573334z\" fill=\"#898A8C\" ></path></symbol><symbol id=\"p-youhuawendang\" viewBox=\"0 0 1024 1024\"><path d=\"M834.7 170.7c9.2 2.1 16.4 9.3 18.6 18.6 31.2 135.8-5.9 283.9-111.5 389.4-14.9 14.9-30.9 28.6-47.9 41-4.8 3.5-8.2 8.6-9.6 14.3-13.6 55.1-42.4 108.2-86.5 152.3-19.7 19.7-41.8 36.9-65.8 51.1-20.4 12.1-44.4-8.6-36.2-30.8 12.9-35 19.3-72.9 19.3-111.9-3.3 0.5-6.6 1.3-9.9 1.8-31.9-27.4-62.7-55.9-92.4-85.6-29.7-29.7-58.2-60.5-85.6-92.4 0.5-3.3 1.3-6.6 1.8-9.9-39 0-76.8 6.4-111.9 19.3-22.2 8.2-42.8-15.8-30.8-36.2 14.2-24 31.4-46.1 51.1-65.8 44.1-44.1 97.2-72.9 152.3-86.5 5.8-1.4 10.8-4.8 14.3-9.6 12.5-17 26.2-33 41-47.9 105.9-105.3 254-142.4 389.7-111.2zM563.8 340.8c-33.2 32.7-33.7 86.2-1 119.4s86.2 33.7 119.4 1l1-1c32.7-33.2 32.3-86.7-1-119.4-32.8-32.4-85.5-32.4-118.4 0z\"  ></path><path d=\"M380.6 755.3C355 780.9 196.4 889.4 165.5 858.5c-30.9-30.9 77.6-189.5 103.2-215.1 25.6-25.6 71.4-21.3 102.2 9.6 31 30.9 35.3 76.7 9.7 102.3z\"  ></path></symbol><symbol id=\"p-cluster-3\" viewBox=\"0 0 1024 1024\"><path d=\"M672 832 224 832c-52.928 0-96-43.072-96-96L128 160c0-52.928 43.072-96 96-96l448 0c52.928 0 96 43.072 96 96l0 576C768 788.928 724.928 832 672 832zM224 128C206.368 128 192 142.368 192 160l0 576c0 17.664 14.368 32 32 32l448 0c17.664 0 32-14.336 32-32L704 160c0-17.632-14.336-32-32-32L224 128z\" fill=\"#1296db\" ></path><path d=\"M800 960 320 960c-17.664 0-32-14.304-32-32s14.336-32 32-32l480 0c17.664 0 32-14.336 32-32L832 256c0-17.664 14.304-32 32-32s32 14.336 32 32l0 608C896 916.928 852.928 960 800 960z\" fill=\"#1296db\" ></path><path d=\"M544 320 288 320c-17.664 0-32-14.336-32-32s14.336-32 32-32l256 0c17.696 0 32 14.336 32 32S561.696 320 544 320z\" fill=\"#1296db\" ></path><path d=\"M608 480 288.032 480c-17.664 0-32-14.336-32-32s14.336-32 32-32L608 416c17.696 0 32 14.336 32 32S625.696 480 608 480z\" fill=\"#1296db\" ></path><path d=\"M608 640 288 640c-17.664 0-32-14.304-32-32s14.336-32 32-32l320 0c17.696 0 32 14.304 32 32S625.696 640 608 640z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-cluster-2\" viewBox=\"0 0 1024 1024\"><path d=\"M326.962 655.431l-88.209-36.51 19.521-84.453c-8.009-6.828-15.524-14.345-22.448-22.457l-84.494 19.565-36.554-88.173 73.58-45.95c-0.85-10.532-0.849-21.172-0.010-31.747l-73.575-45.893 36.505-88.199 84.497 19.519c6.846-8.027 14.356-15.532 22.441-22.442l-19.587-84.511 88.179-36.545 45.938 73.562c10.556-0.84 21.187-0.835 31.749 0.004l45.888-73.58 88.208 36.511-19.52 84.454c8.010 6.826 15.526 14.344 22.449 22.457l84.494-19.564 36.552 88.172-73.58 45.947c0.851 10.534 0.856 21.163 0.012 31.748l73.575 45.894-36.51 88.208-84.482-19.571c-6.83 8.017-14.344 15.534-22.444 22.438l19.597 84.515-88.176 36.546-45.955-73.531c-10.564 0.851-21.189 0.847-31.753 0.010l-45.887 73.575zM265.903 604.942l51.73 21.412 43.19-69.244 7.335 0.871c13.647 1.598 27.488 1.6 41.135-0.006l7.328-0.855 43.242 69.182 51.713-21.445-18.43-79.489 5.779-4.579c10.869-8.614 20.66-18.411 29.096-29.107l4.577-5.812 79.486 18.418 21.411-51.731-69.226-43.183 0.854-7.33c1.612-13.69 1.602-27.522-0.011-41.132l-0.875-7.33 69.24-43.239-21.432-51.719-79.49 18.408-4.577-5.773c-8.628-10.898-18.421-20.688-29.096-29.107l-5.804-4.571 18.362-79.467-51.731-21.411-43.179 69.233-7.331-0.868c-13.657-1.601-27.504-1.597-41.15 0.010l-7.329 0.858-43.228-69.226-51.711 21.445 18.429 79.5-5.786 4.586c-10.837 8.576-20.633 18.379-29.091 29.097l-4.576 5.79-79.498-18.374-21.416 51.741 69.239 43.178-0.86 7.328c-1.608 13.69-1.606 27.532 0.009 41.137l0.871 7.333-69.239 43.237 21.435 51.723 79.486-18.408 4.578 5.774c8.635 10.899 18.424 20.688 29.1 29.1l5.8 4.579-18.36 79.466zM330.035 665.008l-100.223-41.482 19.948-86.303c-5.837-5.213-11.426-10.805-16.688-16.697l-86.342 19.993-41.532-100.183 75.186-46.953c-0.449-7.823-0.451-15.727-0.006-23.603l-75.185-46.897 41.478-100.212 86.347 19.947c5.213-5.836 10.799-11.421 16.681-16.678l-20.016-86.362 100.192-41.522 46.943 75.171c7.843-0.444 15.728-0.442 23.603 0.004l46.89-75.188 100.222 41.483-19.947 86.303c5.839 5.213 11.428 10.805 16.688 16.698l86.342-19.992 41.53 100.182-75.186 46.95c0.452 7.838 0.455 15.745 0.008 23.604l75.183 46.897-41.483 100.224-86.33-20c-5.222 5.851-10.809 11.436-16.682 16.676l20.024 86.36-100.186 41.523-46.959-75.136c-7.837 0.45-15.724 0.452-23.61 0.006l-46.893 75.186zM247.695 614.316l76.196 31.537 44.916-72.017 4.65 0.368c10.156 0.804 20.468 0.801 30.532-0.010l4.651-0.374 44.986 71.978 76.166-31.567-19.182-82.728 3.552-3.028c7.75-6.606 15.011-13.865 21.579-21.574l3.027-3.553 82.693 19.157 31.536-76.192-72.020-44.924 0.371-4.652c0.809-10.148 0.805-20.416-0.011-30.52l-0.377-4.655 72.026-44.977-31.573-76.162-82.703 19.149-3.027-3.546c-6.645-7.785-13.909-15.052-21.592-21.598l-3.549-3.024 19.106-82.664-76.195-31.538-44.916 72.022-4.651-0.369c-10.138-0.806-20.457-0.807-30.533-0.004l-4.649 0.37-44.964-72.005-76.168 31.567 19.171 82.717-3.543 3.028c-7.772 6.64-15.035 13.904-21.587 21.587l-3.024 3.547-82.704-19.105-31.534 76.187 72.017 44.922-0.368 4.649c-0.807 10.179-0.803 20.449 0.009 30.523l0.376 4.654-72.026 44.98 31.575 76.163 82.703-19.149 3.027 3.546c6.646 7.785 13.911 15.052 21.592 21.6l3.548 3.024-19.106 82.663zM320.704 635.933l-63.742-26.385 18.729-81.061-1.984-1.566c-11.137-8.773-21.352-18.988-30.36-30.358l-1.56-1.968-81.090 18.78-26.412-63.733 70.622-44.1-0.297-2.498c-1.686-14.201-1.689-28.648-0.010-42.938l0.293-2.495-70.628-44.045 26.389-63.755 81.097 18.743 1.562-1.976c8.835-11.197 19.047-21.412 30.349-30.356l1.975-1.566-18.801-81.102 63.721-26.426 44.097 70.618 2.502-0.293c14.171-1.668 28.759-1.673 42.935-0.009l2.503 0.296 44.043-70.616 63.743 26.384-18.728 81.055 1.979 1.559c11.163 8.803 21.377 19.021 30.365 30.372l1.558 1.965 81.094-18.779 26.408 63.729-70.62 44.101 0.297 2.493c1.688 14.234 1.692 28.679 0.013 42.938l-0.291 2.495 70.618 44.051-26.385 63.748-81.075-18.787-1.562 1.983c-8.817 11.18-19.030 21.397-30.36 30.374l-1.973 1.563 18.803 81.089-63.723 26.424-44.113-70.575-2.506 0.292c-14.144 1.663-28.726 1.668-42.917 0.005l-2.509-0.298-44.050 70.626zM274.844 600.336l39.717 16.441 42.328-67.862 12.173 1.446c12.982 1.522 26.356 1.521 39.334-0.007l12.159-1.417 42.371 67.789 39.706-16.465-18.059-77.888 9.588-7.596c10.395-8.237 19.76-17.605 27.836-27.844l7.587-9.635 77.896 18.051 16.437-39.715-67.834-42.315 1.416-12.157c1.538-13.074 1.536-26.307-0.009-39.34l-1.452-12.162 67.86-42.378-16.455-39.71-77.887 18.037-7.593-9.577c-8.25-10.419-17.613-19.788-27.835-27.849l-9.625-7.582 17.993-77.878-39.717-16.439-42.317 67.849-12.168-1.441c-12.975-1.521-26.352-1.519-39.35 0.011l-12.161 1.423-42.359-67.834-39.7 16.465 18.058 77.899-9.591 7.604c-10.372 8.208-19.734 17.573-27.833 27.837l-7.593 9.607-77.902-18.006-16.443 39.727 67.851 42.313-1.428 12.159c-1.538 13.097-1.536 26.332 0.008 39.338l1.446 12.168-67.855 42.373 16.458 39.712 77.883-18.037 7.594 9.578c8.267 10.433 17.632 19.801 27.835 27.839l9.623 7.598-17.993 77.868zM328.648 660.691l-94.805-39.239 19.753-85.456c-6.801-5.948-13.274-12.426-19.297-19.305l-85.494 19.797-39.288-94.767 74.448-46.492c-0.611-9.023-0.613-18.18-0.008-27.29l-74.447-46.436 39.236-94.795 85.498 19.749c5.955-6.802 12.427-13.274 19.29-19.287l-19.821-85.513 94.775-39.276 46.482 74.431c9.047-0.603 18.204-0.602 27.291 0.002l46.431-74.449 94.805 39.24-19.749 85.454c6.801 5.951 13.276 12.428 19.295 19.306l85.494-19.794 39.288 94.765-74.45 46.491c0.612 9.058 0.617 18.216 0.008 27.293l74.445 46.436-39.239 94.806-85.479-19.802c-5.974 6.826-12.447 13.298-19.292 19.283l19.826 85.515-94.768 39.277-46.5-74.401c-9.045 0.611-18.204 0.615-27.298 0.007l-46.431 74.45zM243.663 616.393l81.611 33.78 45.355-72.722 2.555 0.202c10.347 0.819 20.804 0.817 31.082-0.007l2.555-0.204 45.422 72.678 81.58-33.812-19.369-83.535 1.95-1.661c7.885-6.717 15.278-14.108 21.972-21.963l1.66-1.951 83.495 19.344 33.779-81.61-72.72-45.36 0.203-2.555c0.822-10.322 0.819-20.779-0.010-31.075l-0.205-2.557 72.727-45.415-33.82-81.576-83.513 19.335-1.658-1.946c-6.767-7.926-14.16-15.321-21.978-21.984l-1.951-1.661 19.294-83.471-81.614-33.778-45.353 72.722-2.553-0.202c-10.34-0.819-20.798-0.821-31.081-0.003l-2.553 0.203-45.402-72.707-81.584 33.81 19.358 83.525-1.946 1.664c-7.907 6.753-15.299 14.147-21.972 21.971l-1.659 1.948-83.513-19.292-33.776 81.601 72.719 45.361-0.201 2.553c-0.82 10.37-0.818 20.826 0.008 31.076l0.206 2.555-72.726 45.418 33.82 81.578 83.511-19.337 1.66 1.948c6.767 7.928 14.161 15.322 21.979 21.983l1.949 1.662-19.295 83.468zM319.321 631.616l-58.328-24.144 18.564-80.344-3.703-2.923c-10.933-8.613-20.959-18.637-29.792-29.791l-2.923-3.686-80.366 18.613-24.168-58.319 69.999-43.711-0.555-4.677c-1.654-13.933-1.657-28.107-0.009-42.127l0.547-4.671-70-43.657 24.145-58.337 80.378 18.578 2.92-3.697c8.672-10.99 18.692-21.013 29.779-29.789l3.695-2.928-18.633-80.382 58.308-24.179 43.706 69.989 4.679-0.546c13.985-1.645 28.159-1.65 42.131-0.011l4.679 0.553 43.652-69.991 58.329 24.143-18.565 80.34 3.7 2.916c10.953 8.639 20.977 18.665 29.797 29.803l2.918 3.682 80.369-18.61 24.166 58.315-70.002 43.711 0.56 4.675c1.656 13.952 1.659 28.124 0.012 42.123l-0.544 4.676 69.991 43.659-24.139 58.33-80.362-18.621-2.92 3.709c-8.644 10.959-18.667 20.986-29.793 29.802l-3.688 2.922 18.632 80.368-58.305 24.176-43.719-69.944-4.682 0.546c-13.951 1.641-28.12 1.641-42.111 0.007l-4.687-0.559-43.662 70.006zM270.813 602.412l45.134 18.683 42.716-68.483 9.991 1.184c13.333 1.563 26.843 1.558 40.146-0.006l9.984-1.164 42.763 68.414 45.118-18.71-18.227-78.61 7.869-6.234c10.615-8.409 20.168-17.97 28.404-28.414l6.231-7.912 78.614 18.218 18.682-45.131-68.467-42.709 1.163-9.98c1.571-13.346 1.567-26.852-0.009-40.148l-1.191-9.985 68.483-42.765-18.699-45.124-78.608 18.203-6.234-7.862c-8.414-10.628-17.969-20.187-28.404-28.418l-7.901-6.223 18.159-78.592-45.132-18.682-42.706 68.471-9.987-1.182c-13.313-1.562-26.828-1.559-40.161 0.010l-9.983 1.167-42.752-68.461-45.116 18.71 18.225 78.622-7.874 6.244c-10.577 8.371-20.132 17.929-28.401 28.405l-6.233 7.888-78.621-18.172-18.686 45.145 68.478 42.701-1.172 9.981c-1.57 13.366-1.567 26.873 0.007 40.149l1.19 9.989-68.482 42.764 18.701 45.128 78.607-18.207 6.234 7.866c8.429 10.637 17.985 20.196 28.405 28.407l7.899 6.233-18.159 78.592zM331.72 670.27l-106.818-44.212 20.195-87.367c-4.67-4.271-9.185-8.789-13.489-13.497l-87.402 20.24-44.267-106.778 76.108-47.531c-0.286-6.323-0.288-12.71-0.004-19.077l-76.11-47.474 44.209-106.808 87.411 20.192c4.273-4.67 8.785-9.182 13.482-13.477l-20.263-87.425 106.787-44.253 47.521 76.096c6.34-0.281 12.714-0.282 19.074 0.002l47.468-76.112 106.819 44.212-20.192 87.367c4.673 4.276 9.188 8.794 13.489 13.497l87.402-20.237 44.267 106.775-76.11 47.528c0.288 6.349 0.29 12.736 0.006 19.082l76.106 47.472-44.213 106.822-87.391-20.245c-4.282 4.682-8.796 9.192-13.48 13.475l20.268 87.424-106.778 44.254-47.538-76.061c-6.347 0.29-12.724 0.291-19.087 0.005l-47.468 76.115zM279.753 597.808l33.123 13.711 41.854-67.099 14.827 1.758c12.647 1.482 25.685 1.481 38.347-0.007l14.814-1.728 41.892 67.021 33.11-13.731-17.856-77.011 11.677-9.251c10.138-8.032 19.27-17.167 27.143-27.151l9.244-11.736 77.025 17.848 13.708-33.116-67.076-41.84 1.727-14.809c1.501-12.754 1.499-25.656-0.008-38.355l-1.767-14.818 67.102-41.902-13.723-33.113-77.004 17.831-9.251-11.664c-8.049-10.165-17.179-19.302-27.144-27.162l-11.722-9.233 17.792-77.003-33.12-13.709-41.843 67.088-14.824-1.754c-12.642-1.483-25.683-1.481-38.362 0.011l-14.815 1.733-41.881-67.069-33.106 13.73 17.854 77.019-11.68 9.261c-10.115 8.004-19.246 17.136-27.143 27.145l-9.25 11.706-77.023-17.804-13.713 33.131 67.090 41.836-1.739 14.813c-1.5 12.768-1.498 25.671 0.007 38.35l1.765 14.823-67.1 41.902 13.724 33.117 77.003-17.835 9.251 11.672c8.061 10.173 17.192 19.306 27.142 27.146l11.722 9.25-17.792 76.997zM357.482 456.881c-41.548-17.198-61.358-64.998-44.164-106.54 17.197-41.544 64.992-61.362 106.541-44.165 41.545 17.196 61.354 64.995 44.159 106.539-17.196 41.542-64.991 61.361-106.537 44.166zM415.404 316.941c-35.615-14.74-76.576 2.238-91.318 37.854-14.741 35.615 2.237 76.581 37.852 91.322 35.611 14.739 76.577-2.244 91.318-37.857 14.74-35.617-2.241-76.579-37.852-91.318zM388.557 470.753c-11.694 0-23.138-2.281-34.010-6.782-45.458-18.816-67.134-71.108-48.32-116.567 13.856-33.474 46.26-55.103 82.553-55.103 11.696 0 23.141 2.282 34.015 6.784 45.456 18.814 67.131 71.105 48.316 116.566-13.857 33.473-46.261 55.103-82.555 55.103zM388.76 319.302c-25.318 0-47.921 15.084-57.584 38.43-6.345 15.33-6.331 32.241 0.041 47.615 6.373 15.373 18.324 27.336 33.655 33.681 7.582 3.138 15.557 4.73 23.704 4.73 25.32 0.002 47.925-15.082 57.589-38.431 13.12-31.703-1.995-68.171-33.697-81.292-7.584-3.139-15.562-4.732-23.708-4.732zM353.515 466.467c-46.836-19.386-69.166-73.26-49.782-120.096 19.387-46.836 73.261-69.166 120.096-49.78 46.833 19.383 69.162 73.256 49.777 120.092-19.385 46.835-73.257 69.167-120.089 49.783zM411.436 326.527c-30.327-12.552-65.213 1.909-77.767 32.236-12.552 30.329 1.907 65.215 32.235 77.767 30.325 12.552 65.211-1.909 77.765-32.239 12.552-30.328-1.907-65.213-32.233-77.765zM388.547 481.129c-13.055 0-25.829-2.547-37.968-7.571-50.744-21.004-74.941-79.375-53.939-130.121 15.468-37.367 51.637-61.51 92.15-61.51 13.057 0 25.833 2.548 37.973 7.573 24.544 10.158 43.677 29.302 53.875 53.904s10.218 51.668 0.058 76.215c-15.467 37.367-51.637 61.511-92.149 61.511zM388.79 297.276c-34.28 0-64.882 20.423-77.967 52.030-17.766 42.925 2.703 92.302 45.626 110.068 10.268 4.25 21.067 6.405 32.098 6.405 34.28 0 64.882-20.423 77.965-52.032 8.592-20.757 8.572-43.652-0.054-64.467-8.628-20.814-24.81-37.008-45.566-45.598-10.268-4.25-21.070-6.406-32.102-6.406zM388.596 448.731c-0.003 0-0.002 0-0.006 0-8.812 0-17.431-1.719-25.619-5.108-34.238-14.17-50.562-53.555-36.392-87.795 10.436-25.21 34.839-41.501 62.171-41.501 8.811 0 17.431 1.719 25.622 5.108 34.237 14.17 50.56 53.553 36.39 87.792-10.437 25.211-34.839 41.504-62.167 41.504zM388.75 329.676c-21.1 0-39.936 12.569-47.989 32.022-10.934 26.42 1.661 56.808 28.079 67.742 6.317 2.616 12.963 3.941 19.75 3.942 0.001 0 0.003 0 0.004 0 21.097 0 39.931-12.57 47.984-32.024 10.934-26.419-1.661-56.806-28.077-67.739-6.32-2.617-12.965-3.943-19.751-3.943zM665.383 908.292l-55.363-30.675 19.201-54.162c-4.762-5.099-9.149-10.615-13.1-16.479l-57.127 6.54-17.455-60.825 51.916-24.743c0.232-6.999 1.032-14.007 2.377-20.913l-45.018-35.756 30.67-55.355 54.19 19.204c5.113-4.774 10.623-9.154 16.469-13.098l-6.556-57.14 60.828-17.448 24.738 51.903c7.015 0.241 14.018 1.043 20.913 2.385l35.752-45.018 55.364 30.672-19.204 54.169c4.765 5.097 9.152 10.613 13.103 16.477l57.127-6.543 17.455 60.826-51.917 24.74c-0.233 7.004-1.027 14.005-2.377 20.916l45.019 35.755-30.674 55.362-54.176-19.235c-5.103 4.766-10.616 9.156-16.471 13.093l6.561 57.143-60.826 17.449-24.75-51.887c-7.024-0.233-14.022-1.033-20.917-2.379l-35.752 45.022zM628.954 870.448l32.468 17.992 33.648-42.369 4.764 1.126c8.873 2.073 17.989 3.116 27.1 3.083l4.89-0.012 23.288 48.818 35.676-10.241-6.172-53.745 4.15-2.583c7.809-4.857 14.993-10.574 21.354-16.988l3.453-3.487 50.975 18.104 17.988-32.468-42.359-33.646 1.114-4.763c2.091-8.896 3.125-18.007 3.082-27.096l-0.025-4.894 48.857-23.283-10.234-35.677-53.743 6.158-2.581-4.149c-4.867-7.827-10.582-15.010-16.983-21.357l-3.479-3.448 18.064-50.968-32.469-17.988-33.642 42.361-4.763-1.121c-8.878-2.083-17.998-3.118-27.107-3.087l-4.892 0.017-23.276-48.847-35.675 10.243 6.17 53.752-4.159 2.587c-7.78 4.837-14.969 10.559-21.346 16.981l-3.448 3.471-50.987-18.073-17.992 32.474 42.367 33.64-1.117 4.764c-2.086 8.898-3.127 18.013-3.083 27.097l0.022 4.898-48.856 23.283 10.236 35.674 53.741-6.153 2.582 4.149c4.867 7.826 10.582 15.008 16.984 21.353l3.476 3.452-18.062 50.967zM742.672 926.706l-25.577-53.622c-4.274-0.289-8.552-0.776-12.796-1.457l-36.95 46.528-66.736-36.976 19.846-55.98c-2.812-3.223-5.49-6.594-8.011-10.080l-59.040 6.759-21.041-73.321 53.652-25.569c0.288-4.265 0.775-8.539 1.456-12.789l-46.525-36.955 36.97-66.727 56.009 19.847c3.22-2.81 6.588-5.486 10.069-8.007l-6.776-59.056 73.326-21.032 25.567 53.642c4.268 0.292 8.544 0.78 12.787 1.459l36.95-46.526 66.737 36.973-19.847 55.984c2.812 3.22 5.491 6.592 8.013 10.079l59.038-6.762 21.042 73.322-53.653 25.567c-0.288 4.276-0.774 8.554-1.454 12.793l46.526 36.953-36.977 66.737-55.992-19.881c-3.229 2.817-6.595 5.493-10.069 8.003l6.78 59.056-73.322 21.038zM698.027 854.844l4.579 0.895c6.499 1.268 13.128 2.022 19.701 2.241l4.663 0.156 23.962 50.232 48.33-13.865-6.352-55.322 3.874-2.606c5.475-3.682 10.695-7.831 15.517-12.334l3.41-3.184 52.444 18.619 24.37-43.988-43.581-34.613 0.894-4.579c1.267-6.488 2.018-13.116 2.237-19.7l0.156-4.665 50.263-23.951-13.869-48.33-55.303 6.335-2.605-3.867c-3.711-5.509-7.864-10.732-12.344-15.523l-3.186-3.409 18.59-52.439-43.99-24.371-34.608 43.579-4.577-0.892c-6.508-1.267-13.139-2.022-19.71-2.249l-4.658-0.16-23.947-50.243-48.33 13.863 6.346 55.311-3.863 2.606c-5.501 3.711-10.725 7.865-15.525 12.346l-3.408 3.18-52.458-18.589-24.369 43.984 43.579 34.613-0.892 4.576c-1.269 6.521-2.022 13.149-2.24 19.701l-0.154 4.665-50.263 23.955 13.87 48.329 55.303-6.332 2.605 3.867c3.709 5.505 7.862 10.731 12.344 15.53l3.182 3.408-18.587 52.43 43.99 24.373 34.612-43.584zM750.984 908.255l-24.031-50.301-0.436 0.001c-9.536 0-19.102-1.105-28.431-3.286l-0.039-0.009-34.66 43.643-43.839-24.293 18.608-52.509-0.025-0.025c-6.827-6.767-12.916-14.421-18.094-22.747l-0.016-0.026-55.374 6.34-13.821-48.169 50.329-24.013c-0.046-9.667 1.059-19.386 3.286-28.883l0.007-0.028-43.647-34.658 24.292-43.846 52.552 18.599c6.808-6.856 14.458-12.942 22.738-18.089l0.032-0.020-6.356-55.383 48.172-13.831 24.009 50.326 0.422-0.001c9.579 0 19.155 1.107 28.463 3.292l0.029 0.007 34.657-43.638 43.841 24.288-18.608 52.507 0.022 0.022c6.834 6.777 12.923 14.433 18.097 22.755l0.015 0.024 55.375-6.345 13.818 48.175-50.326 24.004c0.046 9.698-1.059 19.417-3.285 28.891l-0.006 0.022 43.642 34.666-24.29 43.843-52.534-18.627c-6.787 6.844-14.442 12.933-22.754 18.104l-0.025 0.016 6.358 55.378-48.169 13.826zM736.661 842.58l22.582 47.334 23.18-6.654-5.983-52.112 8.273-5.149c7.301-4.541 14.017-9.883 19.961-15.877l6.878-6.945 49.437 17.559 11.686-21.092-41.076-32.628 2.22-9.496c1.952-8.308 2.92-16.822 2.88-25.313l-0.051-9.762 47.388-22.583-6.65-23.18-52.109 5.971-5.146-8.273c-4.545-7.311-9.885-14.026-15.87-19.961l-6.935-6.873 17.519-49.43-21.097-11.688-32.63 41.084-9.502-2.237c-8.154-1.913-16.551-2.884-24.952-2.884l-10.124 0.035-22.571-47.367-23.176 6.655 5.981 52.119-8.284 5.153c-7.271 4.521-13.984 9.86-19.954 15.872l-6.875 6.922-49.443-17.527-11.691 21.101 41.086 32.624-2.229 9.502c-1.95 8.326-2.92 16.841-2.88 25.308l0.045 9.769-47.383 22.581 6.651 23.177 52.106-5.966 5.147 8.272c4.548 7.313 9.889 14.027 15.871 19.958l6.932 6.881-17.515 49.426 21.097 11.691 32.637-41.095 9.508 2.248c8.166 1.908 16.556 2.878 24.918 2.878l10.142-0.027zM666.098 911.881l-59.502-32.964 19.429-54.811c-4.033-4.428-7.809-9.18-11.261-14.163l-57.802 6.618-18.762-65.376 52.53-25.034c0.276-5.988 0.961-12.021 2.044-17.977l-45.551-36.182 32.964-59.494 54.836 19.431c4.433-4.034 9.182-7.813 14.154-11.257l-6.632-57.816 65.377-18.757 25.029 52.521c6.006 0.282 12.036 0.972 17.977 2.053l36.174-45.554 59.503 32.967-19.428 54.806c4.034 4.433 7.814 9.185 11.26 14.167l57.803-6.618 18.765 65.374-52.532 25.033c-0.278 6.015-0.964 12.047-2.046 17.978l45.549 36.182-32.964 59.504-54.822-19.464c-4.447 4.046-9.195 7.824-14.155 11.253l6.638 57.819-65.376 18.755-25.042-52.5c-6.004-0.278-12.036-0.963-17.981-2.046l-36.176 45.552zM613.443 876.322l51.221 28.379 35.337-44.498 1.668 0.323c6.754 1.319 13.642 2.102 20.473 2.332l1.7 0.057 24.462 51.285 56.276-16.145-6.486-56.48 1.41-0.948c5.696-3.836 11.121-8.148 16.123-12.82l1.241-1.158 53.545 19.012 28.379-51.22-44.496-35.339 0.326-1.669c1.316-6.739 2.099-13.625 2.328-20.471l0.056-1.699 51.315-24.452-16.149-56.276-56.463 6.462-0.948-1.404c-3.861-5.733-8.175-11.157-12.825-16.133l-1.162-1.242 18.979-53.533-51.223-28.384-35.336 44.499-1.664-0.325c-6.75-1.316-13.64-2.102-20.475-2.338l-1.696-0.056-24.447-51.302-56.278 16.144 6.479 56.473-1.408 0.95c-5.716 3.856-11.138 8.169-16.124 12.826l-1.24 1.157-53.563-18.981-28.374 51.216 44.493 35.342-0.323 1.665c-1.32 6.769-2.101 13.655-2.329 20.469l-0.054 1.699-51.319 24.455 16.153 56.273 56.462-6.462 0.947 1.407c3.861 5.731 8.178 11.158 12.827 16.132l1.16 1.241-18.978 53.538zM662.138 892.028l-36.608-20.285 18.262-51.53-2.22-2.201c-6.555-6.497-12.404-13.851-17.386-21.862l-1.648-2.647-54.338 6.224-11.54-40.226 49.392-23.537-0.015-3.124c-0.043-9.304 1.020-18.638 3.157-27.751l0.712-3.038-42.833-34.011 20.287-36.617 51.548 18.277 2.201-2.217c6.538-6.589 13.89-12.437 21.854-17.385l2.652-1.653-6.233-54.345 40.22-11.548 23.535 49.383 3.122-0.007c9.336-0.033 18.675 1.027 27.754 3.157l3.042 0.717 34.009-42.826 36.609 20.282-18.261 51.529 2.219 2.197c6.564 6.513 12.414 13.87 17.388 21.869l1.647 2.648 54.339-6.225 11.537 40.227-49.393 23.535 0.017 3.121c0.041 9.318-1.020 18.652-3.154 27.752l-0.712 3.036 42.829 34.015-20.286 36.608-51.536-18.299-2.201 2.224c-6.517 6.57-13.871 12.422-21.862 17.391l-2.648 1.651 6.238 54.34-40.224 11.544-23.548-49.356-3.124 0.008c-9.312 0.034-18.647-1.029-27.739-3.159l-3.045-0.717-34.018 42.829zM632.377 869.152l28.328 15.693 33.279-41.902 6.494 1.53c8.663 2.033 17.564 3.043 26.444 3.012l6.662-0.017 23.032 48.277 31.125-8.936-6.102-53.151 5.654-3.514c7.62-4.742 14.632-10.324 20.842-16.584l4.7-4.744 50.416 17.904 15.695-28.325-41.895-33.277 1.518-6.488c2.036-8.673 3.050-17.568 3.008-26.444l-0.033-6.667 48.32-23.027-8.928-31.128-53.148 6.088-3.517-5.648c-4.742-7.631-10.319-14.648-16.574-20.853l-4.739-4.691 17.864-50.408-28.326-15.694-33.274 41.896-6.489-1.527c-8.651-2.028-17.557-3.044-26.455-3.011l-6.662 0.020-23.020-48.304-31.124 8.934 6.103 53.155-5.659 3.522c-7.593 4.721-14.607 10.298-20.839 16.577l-4.7 4.731-50.423-17.876-15.699 28.334 41.902 33.271-1.522 6.485c-2.038 8.689-3.051 17.587-3.010 26.45l0.035 6.668-48.322 23.026 8.932 31.132 53.146-6.094 3.513 5.649c4.756 7.646 10.332 14.657 16.577 20.847l4.739 4.699-17.866 50.408zM741.169 930.044l-25.897-54.293c-3.268-0.259-6.541-0.63-9.793-1.114l-37.413 47.108-70.876-39.265 20.093-56.686c-2.118-2.486-4.166-5.063-6.129-7.712l-59.778 6.845-22.348-77.872 54.323-25.888c0.259-3.262 0.63-6.534 1.114-9.787l-47.108-37.418 39.266-70.866 56.713 20.095c2.489-2.122 5.063-4.169 7.702-6.126l-6.859-59.793 77.874-22.343 25.885 54.318c3.265 0.26 6.536 0.634 9.787 1.118l37.41-47.11 70.877 39.269-20.093 56.684c2.126 2.496 4.175 5.073 6.131 7.714l59.776-6.844 22.352 77.87-54.327 25.888c-0.259 3.271-0.63 6.542-1.113 9.788l47.105 37.418-39.266 70.88-56.699-20.131c-2.498 2.128-5.069 4.174-7.7 6.123l6.865 59.793-77.873 22.338zM738.421 839.782l22.324 46.794 18.631-5.349-5.916-51.521 9.782-6.081c7.108-4.422 13.651-9.629 19.445-15.471l8.126-8.202 48.877 17.359 9.392-16.95-40.612-32.257 2.625-11.221c1.901-8.095 2.845-16.39 2.807-24.661l-0.056-11.535 46.849-22.326-5.344-18.632-51.51 5.9-6.084-9.767c-4.432-7.131-9.633-13.675-15.465-19.462l-8.196-8.111 17.32-48.873-16.955-9.393-32.258 40.618-11.229-2.643c-7.943-1.862-16.114-2.808-24.279-2.808l-11.917 0.035-22.315-46.822-18.627 5.347 5.917 51.523-9.784 6.088c-7.094 4.411-13.636 9.614-19.447 15.468l-8.131 8.182-48.877-17.329-9.398 16.961 40.623 32.255-2.635 11.223c-1.899 8.101-2.844 16.399-2.807 24.66l0.060 11.535-46.852 22.325 5.346 18.636 51.513-5.905 6.080 9.773c4.437 7.133 9.639 13.678 15.462 19.45l8.197 8.126-17.321 48.867 16.957 9.393 32.267-40.626 11.234 2.647c7.938 1.863 16.112 2.809 24.289 2.809l11.894-0.028zM700.397 779.795c-26.079-14.448-35.536-47.422-21.091-73.495 14.449-26.076 47.419-35.537 73.495-21.092 26.076 14.451 35.535 47.422 21.087 73.5-14.446 26.070-47.419 35.536-73.492 21.088zM749.060 691.966c-22.353-12.385-50.611-4.28-62.996 18.076-12.384 22.354-4.276 50.614 18.077 62.997 22.352 12.383 50.611 4.271 62.996-18.078 12.383-22.356 4.272-50.614-18.077-62.995zM726.505 794.233c-10.391 0-20.706-2.672-29.828-7.727-29.778-16.496-40.583-54.146-24.084-83.928 10.872-19.622 31.602-31.81 54.101-31.81 10.391 0 20.706 2.671 29.827 7.724 29.777 16.501 40.581 54.154 24.083 83.932-10.874 19.62-31.603 31.808-54.1 31.808zM726.675 693.841c-14.102 0-27.091 7.633-33.899 19.921-10.333 18.651-3.568 42.232 15.082 52.565 5.709 3.163 12.161 4.835 18.659 4.836 0.001 0 0.002 0 0.003 0 14.102 0 27.092-7.633 33.903-19.921 10.331-18.65 3.565-42.231-15.083-52.562-5.713-3.166-12.167-4.839-18.665-4.839zM697.063 785.811c-29.395-16.286-40.058-53.449-23.773-82.847 16.288-29.394 53.453-40.057 82.849-23.772 29.392 16.287 40.057 53.448 23.768 82.845-16.285 29.394-53.451 40.060-82.845 23.774zM745.726 697.98c-19.033-10.545-43.1-3.638-53.647 15.397-10.545 19.036-3.637 43.101 15.395 53.647 19.033 10.545 43.099 3.637 53.646-15.396 10.545-19.034 3.64-43.1-15.394-53.647zM726.495 801.111c-11.55 0-23.014-2.969-33.152-8.587-33.095-18.335-45.103-60.18-26.767-93.278 12.083-21.807 35.122-35.354 60.127-35.354 11.551 0 23.015 2.97 33.155 8.587 33.094 18.338 45.1 60.182 26.763 93.278-12.083 21.808-35.122 35.355-60.127 35.355zM726.703 679.242c-19.427 0-37.321 10.515-46.701 27.442-14.234 25.695-4.913 58.18 20.779 72.413 7.868 4.359 16.76 6.664 25.714 6.664 19.426 0 37.321-10.515 46.699-27.443 14.236-25.692 4.915-58.177-20.776-72.413-7.868-4.357-16.761-6.663-25.716-6.663zM726.532 779.636c-0.001 0-0.002 0-0.003 0-7.936 0-15.811-2.040-22.775-5.897-22.734-12.598-30.983-41.344-18.389-64.080 8.302-14.982 24.127-24.289 41.302-24.289 7.936 0 15.812 2.040 22.777 5.898 22.734 12.598 30.983 41.344 18.388 64.081-8.303 14.98-24.128 24.287-41.301 24.287zM726.669 700.719c-11.597 0-22.279 6.276-27.876 16.378-8.494 15.332-2.931 34.718 12.401 43.215 4.693 2.6 9.997 3.975 15.337 3.975 11.596 0.001 22.278-6.274 27.877-16.377 8.494-15.332 2.932-34.717-12.401-43.215-4.694-2.6-9.997-3.976-15.338-3.976z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-cluster-1\" viewBox=\"0 0 1024 1024\"><path d=\"M888 680h-54V540H546v-92h238c8.8 0 16-7.2 16-16V168c0-8.8-7.2-16-16-16H240c-8.8 0-16 7.2-16 16v264c0 8.8 7.2 16 16 16h238v92H190v140h-54c-4.4 0-8 3.6-8 8v176c0 4.4 3.6 8 8 8h176c4.4 0 8-3.6 8-8V688c0-4.4-3.6-8-8-8h-54v-72h220v72h-54c-4.4 0-8 3.6-8 8v176c0 4.4 3.6 8 8 8h176c4.4 0 8-3.6 8-8V688c0-4.4-3.6-8-8-8h-54v-72h220v72h-54c-4.4 0-8 3.6-8 8v176c0 4.4 3.6 8 8 8h176c4.4 0 8-3.6 8-8V688c0-4.4-3.6-8-8-8zM256 805.3c0 1.5-1.2 2.7-2.7 2.7h-58.7c-1.5 0-2.7-1.2-2.7-2.7v-58.7c0-1.5 1.2-2.7 2.7-2.7h58.7c1.5 0 2.7 1.2 2.7 2.7v58.7z m288 0c0 1.5-1.2 2.7-2.7 2.7h-58.7c-1.5 0-2.7-1.2-2.7-2.7v-58.7c0-1.5 1.2-2.7 2.7-2.7h58.7c1.5 0 2.7 1.2 2.7 2.7v58.7zM288 384V216h448v168H288z m544 421.3c0 1.5-1.2 2.7-2.7 2.7h-58.7c-1.5 0-2.7-1.2-2.7-2.7v-58.7c0-1.5 1.2-2.7 2.7-2.7h58.7c1.5 0 2.7 1.2 2.7 2.7v58.7z\" fill=\"#1296db\" ></path><path d=\"M400 300m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-start\" viewBox=\"0 0 1024 1024\"><path d=\"M755.2 544L390.4 874.666667c-17.066667 14.933333-44.8 14.933333-59.733333-2.133334-6.4-8.533333-10.666667-19.2-10.666667-29.866666v-661.333334c0-23.466667 19.2-42.666667 42.666667-42.666666 10.666667 0 21.333333 4.266667 27.733333 10.666666l362.666667 330.666667c17.066667 14.933333 19.2 42.666667 2.133333 59.733333 2.133333 2.133333 0 2.133333 0 4.266667z\" fill=\"#67C23A\" ></path></symbol><symbol id=\"p-stop\" viewBox=\"0 0 1024 1024\"><path d=\"M291.0654683 865.4952507c-24.3911723 0-44.18690634-19.75154713-44.18690633-44.18690635L246.87856197 202.69165565c0-24.3911723 19.79573404-44.18690634 44.18690633-44.18690635s44.18690634 19.79573404 44.18690636 44.18690635l0 618.6166887C335.25237466 845.74370357 315.45664061 865.4952507 291.0654683 865.4952507z\" fill=\"#F56C6C\" ></path><path d=\"M732.9345317 865.4952507c-24.4353592 0-44.18690634-19.75154713-44.18690636-44.18690635L688.74762534 202.69165565c0-24.3911723 19.75154713-44.18690634 44.18690636-44.18690635s44.18690634 19.79573404 44.18690633 44.18690635l0 618.6166887C777.12143803 845.74370357 757.36989089 865.4952507 732.9345317 865.4952507z\" fill=\"#F56C6C\" ></path></symbol><symbol id=\"p-node-4\" viewBox=\"0 0 1024 1024\"><path d=\"M607.934 417.857c-6.18-6.178-12.766-11.747-19.554-16.91l-0.012 0.011c-6.986-6.72-16.472-10.857-26.93-10.857-21.465 0-38.865 17.4-38.865 38.864a38.697 38.697 0 0 0 9.072 24.947h-0.001a39.02 39.02 0 0 0 9.59 8.256c3.665 3.022 7.262 5.998 10.625 9.361l3.204 3.205c40.28 40.23 28.255 109.54-12.025 149.82l-171.88 171.83c-40.279 40.23-105.762 40.23-146.042 0l-3.23-3.232c-40.281-40.278-40.281-105.81 0-145.99l75.936-75.91c9.742-7.734 15.997-19.67 15.997-33.073 0-23.313-18.899-42.211-42.212-42.211a42.01 42.01 0 0 0-23.725 7.297c-0.021-0.045-0.044-0.088-0.066-0.134l-0.81 0.757a42.455 42.455 0 0 0-8.026 7.51l-78.913 73.842c-74.178 74.23-74.178 195.633 0 269.759l3.204 3.203c74.179 74.127 195.53 74.127 269.708 0l171.83-171.88c74.075-74.175 80.356-191.185 6.281-265.312l-3.156-3.153z\" fill=\"#1296db\" ></path><path d=\"M855.62 165.804l-3.204-3.204c-74.178-74.178-195.529-74.178-269.707 0l-171.83 171.88c-74.178 74.178-78.263 181.296-4.085 255.523l3.153 3.104c3.369 3.368 6.866 6.543 10.435 9.589a36.872 36.872 0 0 0 8.993 7.31l0.077 0.062 0.012-0.01a36.508 36.508 0 0 0 18.258 4.87c20.263 0 36.69-16.428 36.69-36.69 0-5.719-1.31-11.132-3.646-15.958-4.85-10.89-13.93-17.52-20.21-23.802l-3.154-3.103c-40.278-40.278-24.983-98.796 15.295-139.074l171.931-171.83c40.18-40.281 105.685-40.281 145.966 0l3.206 3.152c40.279 40.282 40.279 105.839 0 146.068l-75.687 75.738c-10.297 7.629-16.974 19.865-16.974 33.663 0 23.123 18.746 41.87 41.87 41.87a41.668 41.668 0 0 0 21.946-6.211c0.048 0.082 0.093 0.157 0.14 0.24l1.175-1.083a42.09 42.09 0 0 0 9.529-8.793l79.766-73.603c74.233-74.177 74.233-195.53 0.055-269.708z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-node-3\" viewBox=\"0 0 1024 1024\"><path d=\"M900.144 96.24H123.84a66.896 66.896 0 0 0-66.816 66.832v541.152a66.896 66.896 0 0 0 66.816 66.832h467.744a24 24 0 0 0 0-48H123.84a18.848 18.848 0 0 1-18.816-18.832V163.04A18.864 18.864 0 0 1 123.84 144.208h776.304a18.848 18.848 0 0 1 18.816 18.832v541.152a18.832 18.832 0 0 1-18.816 18.832H753.76a24 24 0 0 0 0 48h146.384a66.896 66.896 0 0 0 66.816-66.832V163.04a66.896 66.896 0 0 0-66.816-66.8z\" fill=\"#1296db\" ></path><path d=\"M795.808 879.76H213.168a24 24 0 0 0 0 48h582.64a24 24 0 0 0 0-48z\" fill=\"#1296db\" ></path><path d=\"M204.192 393.84v192.608a24 24 0 0 0 48 0V393.84a24 24 0 0 0-48 0z\" fill=\"#1296db\" ></path><path d=\"M478.096 452.64v133.856a24 24 0 0 0 48 0V452.64a24 24 0 0 0-48 0z\" fill=\"#1296db\" ></path><path d=\"M819.808 586.448V452.64a24 24 0 0 0-48 0v133.856a24 24 0 0 0 48 0z\" fill=\"#1296db\" ></path><path d=\"M342.336 273.04v313.408a24 24 0 0 0 48 0V273.04a24 24 0 0 0-48 0z\" fill=\"#1296db\" ></path><path d=\"M648.64 249.04a24 24 0 0 0-24 24v313.408a24 24 0 0 0 48 0V273.04a24 24 0 0 0-24-24z\" fill=\"#1296db\" ></path><path d=\"M655.152 733.808a32.24 32.24 0 0 0-2.24 4c-0.48 1.44-0.96 3.04-1.28 4.48a24.416 24.416 0 0 0-0.48 4.8 25.008 25.008 0 0 0 1.76 9.12 26.048 26.048 0 0 0 5.28 7.84 22.288 22.288 0 0 0 7.68 5.12 23.504 23.504 0 0 0 9.28 1.92 22.192 22.192 0 0 0 9.12-1.92 22.912 22.912 0 0 0 7.84-5.12 26.048 26.048 0 0 0 5.28-7.84 25.008 25.008 0 0 0 1.76-9.12 24 24 0 0 0-7.04-16.96 24.752 24.752 0 0 0-33.92 0 23.264 23.264 0 0 0-3.04 3.68z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-file-2\" viewBox=\"0 0 1024 1024\"><path d=\"M244.792889 211.399111a9.443556 9.443556 0 0 0-9.386667 9.386667v56.32c0 5.176889 4.209778 9.443556 9.386667 9.443555h450.901333c5.176889 0 9.386667-4.266667 9.386667-9.386666V220.728889a9.443556 9.443556 0 0 0-9.386667-9.386667H244.792889z m225.450667 234.837333v-56.32a9.443556 9.443556 0 0 0-9.386667-9.443555H244.792889a9.443556 9.443556 0 0 0-9.386667 9.386667v56.376888c0 5.12 4.209778 9.386667 9.386667 9.386667H460.8c5.12 0 9.386667-4.209778 9.386667-9.386667z m-56.32 465.009778h-272.497778V84.536889h657.635555v403.968c0 5.176889 4.209778 9.386667 9.386667 9.386667h65.763556c5.12 0 9.386667-4.209778 9.386666-9.386667V37.603556a37.546667 37.546667 0 0 0-37.546666-37.603556H94.435556A37.546667 37.546667 0 0 0 56.888889 37.546667v920.689777c0 20.764444 16.782222 37.546667 37.546667 37.546667h319.431111c5.176889 0 9.386667-4.209778 9.386666-9.386667v-65.763555a9.443556 9.443556 0 0 0-9.386666-9.386667z m122.197333-135.736889a163.271111 163.271111 0 0 1 47.900444-107.349333 164.920889 164.920889 0 0 1 243.541334 11.889778l-27.079111 21.048889a9.386667 9.386667 0 0 0 3.527111 16.497777l109.511111 26.453334a9.500444 9.500444 0 0 0 11.662222-9.045334l0.682667-112.071111a9.386667 9.386667 0 0 0-15.132445-7.452444l-23.893333 18.545778A239.786667 239.786667 0 0 0 460.8 774.712889a9.443556 9.443556 0 0 0 9.443556 9.728h56.490666a9.329778 9.329778 0 0 0 9.386667-8.931556z m394.467555 8.931556h-56.490666a9.329778 9.329778 0 0 0-9.386667 8.931555 163.271111 163.271111 0 0 1-47.900444 107.349334 164.920889 164.920889 0 0 1-243.598223-11.889778l27.136-21.048889a9.386667 9.386667 0 0 0-3.527111-16.497778l-109.568-26.453333a9.500444 9.500444 0 0 0-11.605333 9.045333l-0.682667 112.071111c0 7.850667 8.931556 12.288 15.132445 7.452445l23.836444-18.545778a239.786667 239.786667 0 0 0 426.040889-140.686222 9.443556 9.443556 0 0 0-9.386667-9.728z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-file-1\" viewBox=\"0 0 1024 1024\"><path d=\"M483.918 79.57a64 64 0 0 1 55.579-0.282l0.585 0.282 336 164.067A64.012 64.012 0 0 1 912 300.414v280.423l-1.43 4.62c-71.508 231.136-203.927 357.79-394.746 373.427l-5.8 0.44-3.887 0.27-3.838-0.668c-197.229-34.393-327.422-158.589-387.343-368.01l-1.794-6.372-1.162-4.193V301.147a64 64 0 0 1 35.261-57.185l0.657-0.325 336-164.067zM512 137.079L176 301.146v270.44l0.437 1.55c52.265 182.906 160.114 288.418 326.59 320.847l5.064 0.96 1.347 0.242 1.07-0.08c159.21-13.037 270.09-116.389 335.005-316.208l1.953-6.085 0.534-1.709V301.147L512 137.079zM512 210c80.634 0 146 65.366 146 146 0 69.64-48.757 127.892-113.998 142.482L544 578h96c0 35.346-28.654 64-64 64h-32v62h96c0 35.346-28.654 64-64 64h-32v64c-35.346 0-64-28.654-64-64l-0.001-269.518C414.758 483.892 366 425.64 366 356c0-80.634 65.366-146 146-146z m0 64c-45.287 0-82 36.713-82 82s36.713 82 82 82 82-36.713 82-82-36.713-82-82-82z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-file-3\" viewBox=\"0 0 1024 1024\"><path d=\"M280.832 508.032L167.68 394.944q-26.88-26.88-26.88-64.896 0-38.016 26.88-64.832l113.152-113.152a38.4 38.4 0 0 1 54.272 54.272l-86.4 86.4h595.904a38.4 38.4 0 1 1 0 76.8H250.88L335.104 453.76a38.4 38.4 0 1 1-54.272 54.272z m462.208 6.656l113.152 113.152q26.88 26.88 26.88 64.896 0 37.952-26.88 64.832l-113.152 113.152a38.4 38.4 0 0 1-54.336-54.272l86.4-86.4H179.2a38.4 38.4 0 0 1 0-76.8h593.792L688.64 569.024a38.4 38.4 0 0 1 54.336-54.336z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-huobao1\" viewBox=\"0 0 1024 1024\"><path d=\"M522.449 932.929c73.143-83.702 36.626-174.663 36.626-174.663s213.82 61.924 110.1 263.094c550.716-311.05 136.936-776.304 65.113-828.989 23.978 52.685 28.378 141.336-19.908 184.342C632.328 66.103 429.398 2.31 429.398 2.31c23.978 160.364-67.533 334.917-194.021 466.574 44.326-46.085-8.03-108.12-41.686-169.273-7.59 116.148-119.448 208.54-120.548 326.998-2.64 286.412 240.986 394.751 240.986 394.751s123.188 8.91 208.32-88.431z\" fill=\"#e98f36\" ></path></symbol><symbol id=\"p-gerenzhongxin1\" viewBox=\"0 0 1024 1024\"><path d=\"M512 619.4c-152.3 0-276.2-123.9-276.2-276.2C235.8 190.9 359.7 67 512 67c152.3 0 276.2 123.9 276.2 276.2 0 152.3-123.9 276.2-276.2 276.2z m0-491c-118.4 0-214.8 96.4-214.8 214.8S393.6 558 512 558s214.8-96.4 214.8-214.8S630.4 128.4 512 128.4z\"  ></path><path d=\"M910.9 957h-61.4c0-186.1-151.4-337.6-337.6-337.6S174.4 770.8 174.4 957H113c0.1-220 179-399 399-399s398.9 179 398.9 399z\"  ></path></symbol><symbol id=\"p-jiqiren2\" viewBox=\"0 0 1024 1024\"><path d=\"M554.667 362.667h-320V832h554.666V362.667H554.667z m-85.334-85.334V192H448a21.333 21.333 0 0 1-21.333-21.333V85.333A21.333 21.333 0 0 1 448 64h128a21.333 21.333 0 0 1 21.333 21.333v85.334A21.333 21.333 0 0 1 576 192h-21.333v85.333H832A42.667 42.667 0 0 1 874.667 320v554.667A42.667 42.667 0 0 1 832 917.333H192a42.667 42.667 0 0 1-42.667-42.666V320A42.667 42.667 0 0 1 192 277.333h277.333zM21.333 512H64a21.333 21.333 0 0 1 21.333 21.333v128A21.333 21.333 0 0 1 64 682.667H21.333A21.333 21.333 0 0 1 0 661.333v-128A21.333 21.333 0 0 1 21.333 512zM320 533.333h128v128H320v-128z m256 0h128v128H576v-128zM960 512h42.667A21.333 21.333 0 0 1 1024 533.333v128a21.333 21.333 0 0 1-21.333 21.334H960a21.333 21.333 0 0 1-21.333-21.334v-128A21.333 21.333 0 0 1 960 512z\"  ></path></symbol><symbol id=\"p-terminal2\" viewBox=\"0 0 1024 1024\"><path d=\"M512 597.333333h256v85.333334h-256z m-183.168 72.832l128-128a42.624 42.624 0 0 0 0-60.330666l-128-128-60.330667 60.330666L366.336 512l-97.834667 97.834667 60.330667 60.330666z\" fill=\"\" ></path><path d=\"M896 170.666667H128a42.666667 42.666667 0 0 0-42.666667 42.666666v597.333334a42.666667 42.666667 0 0 0 42.666667 42.666666h768a42.666667 42.666667 0 0 0 42.666667-42.666666V213.333333a42.666667 42.666667 0 0 0-42.666667-42.666666z m-42.666667 597.333333H170.666667V256h682.666666v512z\" fill=\"\" ></path></symbol><symbol id=\"p-tuijian\" viewBox=\"0 0 1024 1024\"><path d=\"M64 64m128 0l640 0q128 0 128 128l0 640q0 128-128 128l-640 0q-128 0-128-128l0-640q0-128 128-128Z\" fill=\"#FC7032\" ></path><path d=\"M585.6 448h88.96a64 64 0 0 1 19.2 0 50.56 50.56 0 0 1 29.44 64c-10.88 55.68-22.4 110.72-33.28 165.76a64 64 0 0 1-19.2 39.68 37.12 37.12 0 0 1-23.04 9.6H410.24V472.32c0-5.12 0-7.04 6.4-8.32A107.52 107.52 0 0 0 499.2 366.08a112.64 112.64 0 0 1 6.4-38.4 41.6 41.6 0 0 1 52.48-24.96 39.68 39.68 0 0 1 17.92 10.88 54.4 54.4 0 0 1 16 33.28A238.72 238.72 0 0 1 586.24 448z m-198.4 38.4c0-13.44-7.04-21.76-19.2-21.76H320a17.92 17.92 0 0 0-18.56 17.92V704a17.92 17.92 0 0 0 16 20.48h49.28c14.72 0 21.76-7.04 21.76-24.32V488.32z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-node-1\" viewBox=\"0 0 1024 1024\"><path d=\"M 897 97.08 H 129 a 64.19 64.19 0 0 0 -64 64 v 128.56 a 64.19 64.19 0 0 0 64 64 h 768 a 64.19 64.19 0 0 0 64 -64 V 161.08 a 64.19 64.19 0 0 0 -64 -64 Z m 0 192.44 l -0.11 0.11 H 129.11 l -0.11 -0.11 V 161.19 l 0.11 -0.11 h 767.78 l 0.11 0.11 Z\" fill=\"#1296db\" ></path><path d=\"M 225.71 225.36 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path><path d=\"M 353.74 225.36 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path><path d=\"M 897 383.72 H 129 a 64.19 64.19 0 0 0 -64 64 v 128.56 a 64.19 64.19 0 0 0 64 64 h 768 a 64.19 64.19 0 0 0 64 -64 V 447.72 a 64.19 64.19 0 0 0 -64 -64 Z m 0 192.44 l -0.11 0.11 H 129.11 l -0.11 -0.11 V 447.84 l 0.11 -0.11 h 767.78 l 0.11 0.11 Z\" fill=\"#1296db\" ></path><path d=\"M 225.71 512 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path><path d=\"M 353.74 512 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path><path d=\"M 481.71 512.72 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path><path d=\"M 897 670.36 H 129 a 64.19 64.19 0 0 0 -64 64 v 128.56 a 64.19 64.19 0 0 0 64 64 h 768 a 64.19 64.19 0 0 0 64 -64 V 734.36 a 64.19 64.19 0 0 0 -64 -64 Z m 0 192.44 l -0.11 0.11 H 129.11 l -0.11 -0.11 V 734.48 l 0.11 -0.11 h 767.78 l 0.11 0.11 Z\" fill=\"#1296db\" ></path><path d=\"M 225.71 798.64 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path><path d=\"M 353.74 798.64 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path><path d=\"M 481.51 798.64 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path><path d=\"M 609.73 798.81 m -32 0 a 32 32 0 1 0 64 0 a 32 32 0 1 0 -64 0 Z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-node-2\" viewBox=\"0 0 1024 1024\"><path d=\"M749.2096 642.3552c15.36 0 30.72-15.36 30.72-30.72v-424.96c0-15.36-15.36-30.72-30.72-30.72s-30.72 15.36-30.72 30.72v424.96c0 15.36 15.36 30.72 30.72 30.72z\" fill=\"#1296db\" ></path><path d=\"M964.2496 391.4752c10.24-10.24 10.24-30.72 0-40.96l-174.08-174.08c-15.36-15.36-30.72-15.36-46.08 0-10.24 10.24-10.24 30.72 0 40.96l174.08 174.08c15.36 15.36 30.72 15.36 46.08 0z\" fill=\"#1296db\" ></path><path d=\"M539.2896 391.4752c-10.24-10.24-10.24-30.72 0-40.96l174.08-174.08c10.24-10.24 30.72-10.24 40.96 0 10.24 10.24 10.24 30.72 0 40.96l-168.96 174.08c-15.36 15.36-30.72 15.36-46.08 0z\" fill=\"#1296db\" ></path><path d=\"M81.92 868.7104h757.76c15.36 0 30.72-15.36 30.72-30.72s-15.36-30.72-30.72-30.72H81.92c-15.36 0-30.72 15.36-30.72 30.72s15.36 30.72 30.72 30.72zM81.92 612.7104h348.16c15.36 0 30.72-15.36 30.72-30.72s-15.36-30.72-30.72-30.72H81.92c-15.36 0-30.72 15.36-30.72 30.72s15.36 30.72 30.72 30.72zM81.92 356.7104h245.76c15.36 0 30.72-15.36 30.72-30.72s-15.36-30.72-30.72-30.72H81.92c-15.36 0-30.72 15.36-30.72 30.72s15.36 30.72 30.72 30.72z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-tongyijiancha\" viewBox=\"0 0 1024 1024\"><path d=\"M464 192a270.95 270.95 0 1 1-105.85 21.35A270.24 270.24 0 0 1 464 192m0-64c-185.57 0-336 150.43-336 336s150.43 336 336 336 336-150.43 336-336-150.43-336-336-336zM710.63 665.37l-45.26 45.26L850.75 896 896 850.75 710.63 665.37z\" fill=\"#1296db\" ></path><path d=\"M604.97 326.06L419.6 511.43l-96.57-96.57-45.26 45.26 96.57 96.57 45.26 45.25 45.25-45.25 185.38-185.38-45.26-45.25z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-alert-3\" viewBox=\"0 0 1024 1024\"><path d=\"M334.50666667 531.5047616a97.5628192 97.5628192 0 1 1 0-39.0095232H863.08571413a19.5047616 19.5047616 0 1 1 0 39.0095232H334.50666667z m354.98666666 273.06666667H160.91428587a19.5047616 19.5047616 0 1 1 0-39.0095232h528.57904746a97.52380907 97.52380907 0 1 1 0 39.0095232z m0-546.13333334H160.91428587a19.5047616 19.5047616 0 0 1 0-39.0095232h528.57904746a97.52380907 97.52380907 0 1 1 0 39.0095232zM843.58095253 238.93333333a58.51428587 58.51428587 0 1 0-117.02857173 0 58.51428587 58.51428587 0 0 0 117.02857173 0z m0 546.13333334a58.51428587 58.51428587 0 1 0-117.02857173 0 58.51428587 58.51428587 0 0 0 117.02857173 0z m-546.13333333-273.06666667a58.51428587 58.51428587 0 1 0-117.02857173 0 58.51428587 58.51428587 0 0 0 117.02857173 0z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-file-zip\" viewBox=\"0 0 1024 1024\"><path d=\"M659.72 64.81H270.4c-78.06 0-141.33 63.28-141.33 141.33v612.95c0 78.06 63.28 141.33 141.33 141.33h485.49c78.06 0 141.33-63.28 141.33-141.33V302.3c0-31.83-12.64-62.35-35.15-84.85l-117.5-117.5a120.008 120.008 0 0 0-84.85-35.14z\" fill=\"#FFC757\" ></path><path d=\"M304.85 196.81h71.52c7.86 0 14.24-6.38 14.24-14.24v-71.52c0-7.87-6.38-14.24-14.24-14.24h-71.52c-7.86 0-14.24 6.38-14.24 14.24v71.52c0 7.86 6.38 14.24 14.24 14.24zM476.37 196.81h-71.52c-7.86 0-14.24 6.38-14.24 14.24v71.52c0 7.87 6.38 14.24 14.24 14.24h71.52c7.86 0 14.24-6.38 14.24-14.24v-71.52c0-7.86-6.38-14.24-14.24-14.24zM304.85 396.81h71.52c7.86 0 14.24-6.38 14.24-14.24v-71.52c0-7.87-6.38-14.24-14.24-14.24h-71.52c-7.86 0-14.24 6.38-14.24 14.24v71.52c0 7.86 6.38 14.24 14.24 14.24zM476.37 396.81h-71.52c-7.86 0-14.24 6.38-14.24 14.24v71.52c0 7.87 6.38 14.24 14.24 14.24h71.52c7.86 0 14.24-6.38 14.24-14.24v-71.52c0-7.86-6.38-14.24-14.24-14.24zM455.65 552.02H321.52c-17.07 0-30.91 13.84-30.91 30.91v155.48c0 17.07 13.84 30.91 30.91 30.91h134.13c17.07 0 30.91-13.84 30.91-30.91V582.93c0-17.07-13.84-30.91-30.91-30.91z m-4.93 164.59c0 13.72-11.12 24.84-24.84 24.84h-74.6c-13.72 0-24.84-11.12-24.84-24.84v-27.36c0-13.72 11.12-24.84 24.84-24.84h74.6c13.72 0 24.84 11.12 24.84 24.84v27.36zM862.07 217.45l-117.5-117.5a120.001 120.001 0 0 0-61.75-32.9v131.88c0 41.87 33.94 75.81 75.81 75.81H894a120.021 120.021 0 0 0-31.93-57.29z\" fill=\"#F79F2B\" ></path><path d=\"M304.85 184.81h71.52c7.86 0 14.24-6.38 14.24-14.24V99.05c0-7.87-6.38-14.24-14.24-14.24h-71.52c-7.86 0-14.24 6.38-14.24 14.24v71.52c0 7.86 6.38 14.24 14.24 14.24zM476.37 184.81h-71.52c-7.86 0-14.24 6.38-14.24 14.24v71.52c0 7.87 6.38 14.24 14.24 14.24h71.52c7.86 0 14.24-6.38 14.24-14.24v-71.52c0-7.86-6.38-14.24-14.24-14.24zM304.85 384.81h71.52c7.86 0 14.24-6.38 14.24-14.24v-71.52c0-7.87-6.38-14.24-14.24-14.24h-71.52c-7.86 0-14.24 6.38-14.24 14.24v71.52c0 7.86 6.38 14.24 14.24 14.24zM476.37 384.81h-71.52c-7.86 0-14.24 6.38-14.24 14.24v71.52c0 7.87 6.38 14.24 14.24 14.24h71.52c7.86 0 14.24-6.38 14.24-14.24v-71.52c0-7.86-6.38-14.24-14.24-14.24zM455.65 540.02H321.52c-17.07 0-30.91 13.84-30.91 30.91v155.48c0 17.07 13.84 30.91 30.91 30.91h134.13c17.07 0 30.91-13.84 30.91-30.91V570.93c0-17.07-13.84-30.91-30.91-30.91z m-4.93 164.59c0 13.72-11.12 24.84-24.84 24.84h-74.6c-13.72 0-24.84-11.12-24.84-24.84v-27.36c0-13.72 11.12-24.84 24.84-24.84h74.6c13.72 0 24.84 11.12 24.84 24.84v27.36z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-normal\" viewBox=\"0 0 1024 1024\"><path d=\"M137.90246 0.00041a48.573421 48.573421 0 0 0-35.589106 15.293433A53.964778 53.964778 0 0 0 87.0404 50.934149V968.345622a48.706541 48.706541 0 0 0 15.272954 35.640306 49.97118 49.97118 0 0 0 35.589106 15.293434h746.336982a48.639981 48.639981 0 0 0 35.589105-15.293434 50.37054 50.37054 0 0 0 15.272954-35.640306V288.717094L646.727857 0.00041H137.90246z\" fill=\"#E5E5E5\" ></path><path d=\"M935.101501 288.717094h-237.445025c-27.822069-0.6656-50.22718-23.075831-50.928619-50.93374V0.00041l288.373644 288.716684z\" fill=\"#CCCCCC\" ></path><path d=\"M248.125776 365.184264h220.518312a25.51807 25.51807 0 0 0 24.19199-25.49759 25.51807 25.51807 0 0 0-24.19711-25.50271H248.125776a25.51807 25.51807 0 0 0-24.19711 25.49759 25.51807 25.51807 0 0 0 24.19711 25.49759z m0 169.825212h525.82379a25.44639 25.44639 0 0 0 25.431029-25.46687 25.44639 25.44639 0 0 0-25.431029-25.46687h-525.82379a25.44639 25.44639 0 0 0-25.43103 25.46687 25.44639 25.44639 0 0 0 25.43103 25.46687z m525.82379 118.886352h-525.82379a25.51807 25.51807 0 0 0-24.19711 25.49759 25.51807 25.51807 0 0 0 24.19711 25.50271h525.82379a25.51807 25.51807 0 0 0 24.19711-25.49759 25.51807 25.51807 0 0 0-24.19711-25.49759z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-txt\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 73.144889H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333v804.579556a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 73.144889z\" fill=\"#FCCC5A\" ></path><path d=\"M661.944889 288.910222a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 73.144889v215.765333z\" fill=\"#FFD980\" ></path><path d=\"M347.434667 420.565333V486.4h118.855111v288.910222h91.420444V486.4h118.855111v-65.834667z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-folder\" viewBox=\"0 0 1024 1024\"><path d=\"M739.82 290.16H64.21V234.1c0-58.59 47.5-106.09 106.09-106.09h393.16c43.85 0 84.74 22.11 108.75 58.81l67.61 103.34z\" fill=\"#FFBA53\" ></path><path d=\"M960.19 396.82v357.49c0 78.06-63.28 141.33-141.33 141.33H205.54c-78.06 0-141.33-63.28-141.33-141.33V255.49h754.64c78.06 0 141.34 63.28 141.34 141.33z\" fill=\"#FFDC53\" ></path></symbol><symbol id=\"p-file-unknown\" viewBox=\"0 0 1024 1024\"><path d=\"M659.72 64.19H270.4c-78.06 0-141.33 63.28-141.33 141.33v612.95c0 78.06 63.28 141.33 141.33 141.33h485.49c78.06 0 141.33-63.28 141.33-141.33V301.69c0-31.83-12.64-62.35-35.15-84.85l-117.5-117.5a120.017 120.017 0 0 0-84.85-35.15z\" fill=\"#C7DADD\" ></path><path d=\"M862.07 216.84l-117.5-117.5a120.001 120.001 0 0 0-61.75-32.9v131.88c0 41.87 33.94 75.81 75.81 75.81H894a119.975 119.975 0 0 0-31.93-57.29zM512.53 295.92c-82.1 0-148.9 66.8-148.9 148.9 0 12.53 10.15 22.68 22.68 22.68s22.68-10.15 22.68-22.68c0-57.09 46.45-103.54 103.54-103.54 27.63 0 53.62 10.78 73.19 30.35s30.35 45.57 30.35 73.19c0 21.11-6.31 41.41-18.24 58.71-11.68 16.92-27.89 29.89-46.9 37.48-37.11 14.83-61.08 49.9-61.08 89.33V632c0 12.53 10.15 22.68 22.68 22.68s22.68-10.15 22.68-22.68v-1.66c0-20.78 12.78-39.31 32.56-47.21 27.33-10.92 50.63-29.54 67.4-53.84 17.18-24.9 26.26-54.11 26.26-84.47 0-39.74-15.5-77.13-43.64-105.27-28.13-28.13-65.52-43.63-105.26-43.63z\" fill=\"#9DC0C9\" ></path><path d=\"M500.973351 750.25961a30.24 30.24 0 1 0 23.144694-55.876234 30.24 30.24 0 1 0-23.144694 55.876234Z\" fill=\"#9DC0C9\" ></path><path d=\"M618.4 327.31c-28.14-28.14-65.52-43.64-105.26-43.64-82.1 0-148.9 66.8-148.9 148.9 0 12.53 10.15 22.68 22.68 22.68s22.68-10.15 22.68-22.68c0-57.09 46.45-103.54 103.54-103.54 27.63 0 53.62 10.78 73.19 30.35s30.35 45.57 30.35 73.19c0 21.11-6.31 41.41-18.24 58.71-11.68 16.92-27.89 29.89-46.9 37.48-37.11 14.83-61.08 49.9-61.08 89.33v1.66c0 12.53 10.15 22.68 22.68 22.68s22.68-10.15 22.68-22.68v-1.66c0-20.78 12.78-39.31 32.56-47.21 27.33-10.92 50.63-29.54 67.4-53.84 17.18-24.9 26.26-54.11 26.26-84.47 0-39.74-15.5-77.12-43.64-105.26z\" fill=\"#FFFFFF\" ></path><path d=\"M513.14 710.09m-30.24 0a30.24 30.24 0 1 0 60.48 0 30.24 30.24 0 1 0-60.48 0Z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-txt\" viewBox=\"0 0 1024 1024\"><path d=\"M659.72 64.19H270.4c-78.06 0-141.33 63.28-141.33 141.33v612.95c0 78.06 63.28 141.33 141.33 141.33h485.49c78.06 0 141.33-63.28 141.33-141.33V301.69c0-31.83-12.64-62.35-35.15-84.85l-117.5-117.5a120.017 120.017 0 0 0-84.85-35.15z\" fill=\"#4FD397\" ></path><path d=\"M862.07 216.84l-117.5-117.5a120.001 120.001 0 0 0-61.75-32.9v131.88c0 41.87 33.94 75.81 75.81 75.81H894a119.975 119.975 0 0 0-31.93-57.29zM670.25 381.88H517.97a24.052 24.052 0 0 0-9.66 0H356.03c-13.25 0-24 10.75-24 24s10.75 24 24 24h133.11v258.73c0 13.25 10.75 24 24 24s24-10.75 24-24V429.88h133.11c13.25 0 24-10.75 24-24s-10.75-24-24-24z\" fill=\"#19BC6E\" ></path><path d=\"M670.25 369.88H517.97a24.052 24.052 0 0 0-9.66 0H356.03c-13.25 0-24 10.75-24 24s10.75 24 24 24h133.11v258.73c0 13.25 10.75 24 24 24s24-10.75 24-24V417.88h133.11c13.25 0 24-10.75 24-24s-10.75-24-24-24z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-language\" viewBox=\"0 0 1024 1024\"><path d=\"M651.3 390.041h325.224a47.76 47.76 0 0 1 46.623 48.897v536.165A47.76 47.76 0 0 1 976.524 1024H419.322a47.76 47.76 0 0 1-46.623-48.897V633.959H46.907A47.76 47.76 0 0 1 0.284 585.062V48.897A47.76 47.76 0 0 1 46.907 0h557.77A47.76 47.76 0 0 1 651.3 48.897zM46.907 585.061h557.77V48.898H46.907v536.165z m635.665-16.488l-102.343 278.6h45.485l25.018-71.64h113.714l25.018 71.64h43.78l-102.912-278.6z m-22.175 170.572l44.35-126.223 47.19 126.223z m-354.22-625.43h35.251v71.071h135.32v188.198h-35.251V350.81H341.428V481.58h-33.546V350.81h-97.226v22.174h-35.251V184.786h132.477z m-95.52 200.706h96.657v-93.246h-96.658z m130.771 0h100.069v-93.246H341.428z\"  ></path></symbol><symbol id=\"p-theme\" viewBox=\"0 0 1024 1024\"><path d=\"M1012.872533 322.1504l-0.1024-0.580267c-0.1024-0.682667-0.1024-2.491733-0.1024-3.4816v-1.365333l-1.877333-8.4992-0.238933-0.682667c-6.417067-18.670933-17.681067-30.242133-25.9072-38.6048l-5.9392-6.144a288.494933 288.494933 0 0 0-20.036267-20.514133c-5.768533-5.461333-11.1616-10.5472-16.725333-16.554667l-0.2048-0.238933-146.056534-146.0224-1.536-1.570133c-7.645867-7.953067-16.384-16.896-29.218133-23.688534-13.9264-7.714133-31.197867-9.284267-52.872533-9.284266-1.194667 0-4.437333 0-8.874667-0.136534-9.6256-0.1024-24.098133-0.341333-35.84-0.341333-13.7216 0-17.476267 0.238933-19.3536 0.580267a116.667733 116.667733 0 0 0-17.408 5.12l-1.262933 0.4096-18.7392 12.8-0.2048 0.136533c-8.533333 6.485333-14.1312 14.267733-18.261334 20.036267l-0.4096 0.580266-0.136533 0.238934a49.5616 49.5616 0 0 1-3.9936 5.2224c-14.916267 12.219733-25.224533 15.633067-54.203733 21.845333l-0.341334 0.136533-0.341333 0.1024c-4.744533 1.365333-8.533333 1.467733-10.1376 1.570134l-17.749333-0.887467c-15.223467-2.9696-26.624-5.666133-36.2496-9.966933l-0.341334-0.136534c-8.3968-3.413333-16.827733-9.6256-19.592533-13.346133l-1.4336-1.9456a117.248 117.248 0 0 0-22.357333-24.4736c-7.2704-6.144-22.186667-14.404267-31.505067-16.315733-6.997333-1.501867-18.056533-2.2528-33.518933-2.2528-25.122133 0-53.009067 1.911467-54.237867 2.048l-0.989867 0.068266-24.9856 6.382934-0.785066 0.341333c-16.725333 6.792533-28.228267 18.568533-35.84 26.385067L88.3712 234.564267c-4.539733 4.096-8.533333 8.192-12.356267 12.151466-3.310933 3.413333-6.519467 6.656-11.0592 10.8544C51.438933 269.482667 31.061333 287.470933 24.849067 320.853333c-3.515733 18.773333 3.584 35.4304 7.441066 44.407467l0.238934 0.443733c6.9632 15.9744 17.681067 26.146133 25.429333 33.621334l1.467733 1.365333 0.546134 0.477867c1.194667 0.887467 2.8672 2.594133 4.096 3.857066l5.632 5.768534c5.5296 6.212267 11.502933 11.776 17.271466 17.237333l19.114667 19.592533 35.293867 34.679467 10.069333 10.308267c0.682667 0.682667 1.3312 1.467733 2.116267 2.1504 3.959467 4.096 8.840533 9.216 15.36 13.9264l0.238933 0.238933c14.472533 9.966933 32.529067 15.735467 49.4592 15.735467 7.509333 0 14.609067-1.024 21.0944-3.140267v348.706133c0 13.0048-0.068267 27.648 5.2224 41.5744 7.202133 19.0464 22.1184 33.450667 32.768 40.004267 17.442133 11.6736 38.8096 13.277867 59.392 13.277867h327.714133l29.559467 0.443733c24.439467 0 52.292267-1.365333 74.205867-18.2272 14.062933-11.025067 24.917333-29.013333 29.013333-48.196267l0.1024-0.546133 0.682667-5.666133v-0.580267c0.443733-30.685867 0.443733-60.381867 0.443733-91.648v-279.8592c12.629333 4.061867 26.043733 4.778667 39.0144 2.048 30.208-5.802667 49.493333-28.433067 60.962133-42.154667l92.842667-92.091733 2.56-3.310933 0.955733-1.467734c6.7584-9.045333 22.254933-30.378667 17.851734-61.064533l-0.136534-0.580267z m-165.717333 111.035733l-0.887467 1.024c-6.417067 7.2704-15.0528 17.237333-20.923733 18.363734a13.2096 13.2096 0 0 1-2.8672 0.341333c-4.642133 0-8.635733-3.2768-18.602667-12.9024l-32.085333-31.6416-42.5984 30.037333v362.120534c0 14.848-0.1024 29.866667-0.238933 44.509866-0.1024 13.7216-0.170667 27.7504-0.170667 41.5744a19.968 19.968 0 0 1-2.798933 4.1984c-4.778667 3.413333-23.7568 3.857067-31.744 3.857067l-338.1248-0.443733-18.773334 0.443733c-16.2816 0-20.821333-1.501867-21.9136-2.048a21.230933 21.230933 0 0 1-4.983466-5.5296c-0.682667-3.515733-0.682667-13.380267-0.682667-18.944v-430.421333l-47.104-28.330667-6.417067 7.509333c-5.8368 6.826667-11.776 13.5168-17.8176 20.138667l-0.238933 0.238933c-6.826667 7.338667-12.4928 13.243733-18.773333 15.735467h-0.443734a21.060267 21.060267 0 0 1-9.3184-3.072l-0.443733-0.3072-0.443733-0.238933a25.9072 25.9072 0 0 1-4.983467-4.744534 41.335467 41.335467 0 0 1-2.082133-2.286933l-46.250667-45.738667a377.173333 377.173333 0 0 1-18.705067-19.182933l-0.750933-0.785067-2.56-1.911466c-3.413333-2.833067-6.519467-6.144-9.762133-9.5232a61.678933 61.678933 0 0 0-3.515734-3.652267l-6.314666-6.894933-0.1024-0.1024c-2.56-2.628267-4.778667-4.881067-7.509334-7.168-5.973333-5.188267-8.0896-7.68-9.4208-10.615467a29.422933 29.422933 0 0 0-1.3312-3.652267 7.9872 7.9872 0 0 1-0.546133-1.467733c1.536-6.212267 5.5296-10.410667 17.92-21.742933l0.238933-0.238934 12.8-13.141333c1.4336-1.501867 3.003733-2.935467 4.642134-4.539733 1.911467-1.809067 3.754667-3.618133 5.870933-5.768534L264.874667 157.627733l27.989333-28.194133 0.341333-0.341333c8.772267-9.5232 10.410667-10.308267 14.7456-10.5472l13.277867-1.024h55.842133l0.887467 0.341333c1.024 0.341333 1.774933 0.580267 2.218667 0.9216l0.785066 0.341333 0.750934 0.238934a9.216 9.216 0 0 0 1.911466 0.546133c2.730667 2.730667 5.5296 6.007467 8.8064 10.5472l1.467734 1.911467 0.238933 0.238933c15.5648 20.1728 40.379733 32.085333 47.547733 35.2256l0.238934 0.1024c14.9504 6.2464 30.72 10.581333 53.111466 14.506667l0.443734 0.136533 16.042666 1.467733c3.1744 0.580267 6.621867 0.785067 10.376534 0.785067 7.0656 0 15.291733-0.9216 25.258666-2.935467 35.498667-7.031467 57.275733-14.404267 84.718934-36.488533a81.237333 81.237333 0 0 0 16.384-18.1248c1.4336-1.604267 2.6624-3.1744 3.754666-4.539733 0.1024-0.238933 0.341333-0.443733 0.546134-0.682667l6.621866-3.857067c1.1264-0.2048 2.56-0.443733 4.334934-0.887466h16.5888c2.56 0 5.2224-0.136533 7.748266-0.238934 2.6624-0.1024 5.461333-0.238933 8.192-0.238933h0.887467c5.3248-0.443733 10.717867-0.443733 16.384-0.443733 13.380267 0 18.807467 0.9216 20.48 1.2288 3.754667 2.286933 9.045333 7.748267 13.687467 12.458666l145.271466 144.827734c5.870933 6.485333 12.151467 12.458667 19.3536 19.285333 4.881067 4.5056 11.537067 10.752 16.725334 16.520533l0.238933 0.238934 6.621867 6.826666 0.238933 0.068267c4.1984 3.9936 6.587733 6.690133 8.260267 9.216l0.682666 4.608v0.238933c0.1024 0.443733 0.341333 1.570133-3.6864 7.133867l-90.9312 90.4192-3.072 3.754667z\"  ></path></symbol><symbol id=\"p-arrow-right\" viewBox=\"0 0 1024 1024\"><path d=\"M320 885.333333c-8.533333 0-17.066667-4.266667-23.466667-10.666666-12.8-12.8-10.666667-34.133333 2.133334-44.8L654.933333 512 298.666667 194.133333c-12.8-10.666667-14.933333-32-2.133334-44.8 10.666667-12.8 32-14.933333 44.8-2.133333l384 341.333333c6.4 6.4 10.666667 14.933333 10.666667 23.466667 0 8.533333-4.266667 17.066667-10.666667 23.466667l-384 341.333333c-6.4 6.4-12.8 8.533333-21.333333 8.533333z\"  ></path></symbol><symbol id=\"p-docker\" viewBox=\"0 0 1024 1024\"><path d=\"M205.653333 737.066667c-29.184 0-55.637333-23.893333-55.637333-52.906667s23.893333-53.034667 55.68-53.034667c31.914667 0 55.893333 23.893333 55.893333 52.992s-26.538667 52.906667-55.68 52.906667z m683.178667-288.554667c-5.76-42.325333-32-76.8-66.56-103.253333l-13.44-10.666667-10.837333 13.226667c-21.077333 23.893333-29.44 66.261333-26.88 97.92 2.56 23.978667 10.24 47.786667 23.637333 66.304-10.837333 5.546667-24.234667 10.666667-34.56 16.085333a225.706667 225.706667 0 0 1-71.68 10.666667H4.138667l-2.56 15.786666a297.813333 297.813333 0 0 0 23.978666 151.04l10.410667 18.56v2.56c64 105.941333 177.92 153.6 301.994667 153.6 238.677333 0 434.432-103.253333 527.232-325.674666 60.8 2.645333 122.197333-13.226667 151.04-71.509334l7.68-13.226666-12.8-7.978667c-34.56-21.077333-81.92-23.893333-121.6-13.226667l-0.768 0.085334z m-341.674667-42.325333h-103.594666v103.253333h103.68V406.101333l-0.085334 0.128z m0-129.834667h-103.594666v103.253333h103.68V276.48l-0.085334-0.128z m0-132.437333h-103.594666v103.253333h103.68v-103.253333h-0.085334z m126.72 262.272H570.88v103.253333h103.253333V406.101333l-0.298666 0.128z m-383.914666 0H187.008v103.253333h103.338667V406.101333l-0.426667 0.128z m129.28 0h-102.4v103.253333H419.84V406.101333l-0.64 0.128z m-257.28 0H59.733333v103.253333h103.594667V406.101333l-1.28 0.128z m257.28-129.834667h-102.4v103.253333H419.84V276.48l-0.64-0.128z m-129.92 0H187.178667v103.253333H290.133333V276.48l-0.682666-0.128z\" fill=\"\" ></path></symbol><symbol id=\"p-appstore\" viewBox=\"0 0 1024 1024\"><path d=\"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16z m-52 268H212V212h200v200z m452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16z m-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16z m-52 268H212V612h200v200z m452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16z m-52 268H612V612h200v200z\"  ></path></symbol><symbol id=\"p-website\" viewBox=\"0 0 1024 1024\"><path d=\"M854.4 800.9c0.2-0.3 0.5-0.6 0.7-0.9C920.6 722.1 960 621.7 960 512s-39.4-210.1-104.8-288c-0.2-0.3-0.5-0.5-0.7-0.8-1.1-1.3-2.1-2.5-3.2-3.7-0.4-0.5-0.8-0.9-1.2-1.4-1.4-1.6-2.7-3.1-4.1-4.7l-0.1-0.1c-1.5-1.7-3.1-3.4-4.6-5.1l-0.1-0.1c-3.2-3.4-6.4-6.8-9.7-10.1l-0.1-0.1-4.8-4.8-0.3-0.3c-1.5-1.5-3-2.9-4.5-4.3-0.5-0.5-1-1-1.6-1.5-1-1-2-1.9-3-2.8-0.3-0.3-0.7-0.6-1-1C736.4 109.2 629.5 64 512 64s-224.4 45.2-304.3 119.2c-0.3 0.3-0.7 0.6-1 1-1 0.9-2 1.9-3 2.9-0.5 0.5-1 1-1.6 1.5-1.5 1.4-3 2.9-4.5 4.3l-0.3 0.3-4.8 4.8-0.1 0.1c-3.3 3.3-6.5 6.7-9.7 10.1l-0.1 0.1c-1.6 1.7-3.1 3.4-4.6 5.1l-0.1 0.1c-1.4 1.5-2.8 3.1-4.1 4.7-0.4 0.5-0.8 0.9-1.2 1.4-1.1 1.2-2.1 2.5-3.2 3.7-0.2 0.3-0.5 0.5-0.7 0.8C103.4 301.9 64 402.3 64 512s39.4 210.1 104.8 288c0.2 0.3 0.5 0.6 0.7 0.9 1 1.2 2.1 2.5 3.1 3.7 0.4 0.5 0.8 0.9 1.2 1.4 1.4 1.6 2.7 3.1 4.1 4.7 0 0.1 0.1 0.1 0.1 0.2 1.5 1.7 3 3.4 4.6 5l0.1 0.1c3.2 3.4 6.4 6.8 9.6 10.1l0.1 0.1c1.6 1.6 3.1 3.2 4.7 4.7l0.3 0.3c3.3 3.3 6.7 6.5 10.1 9.6 80.1 74 187 119.2 304.5 119.2s224.4-45.2 304.3-119.2c3.4-3.1 6.7-6.3 10-9.6l0.3-0.3c1.6-1.6 3.2-3.1 4.7-4.7l0.1-0.1c3.3-3.3 6.5-6.7 9.6-10.1l0.1-0.1c1.5-1.7 3.1-3.3 4.6-5 0-0.1 0.1-0.1 0.1-0.2 1.4-1.5 2.8-3.1 4.1-4.7 0.4-0.5 0.8-0.9 1.2-1.4 1.2-1.3 2.3-2.5 3.3-3.7z m4.1-142.6c-13.8 32.6-32 62.8-54.2 90.2-24.9-21.5-52.2-40.3-81.5-55.9 11.6-46.9 18.8-98.4 20.7-152.6H887c-3 40.9-12.6 80.6-28.5 118.3zM887 484H743.5c-1.9-54.2-9.1-105.7-20.7-152.6 29.3-15.6 56.6-34.4 81.5-55.9 22.2 27.4 40.4 57.6 54.2 90.2C874.4 403.4 884 443.1 887 484zM658.3 165.5c39.7 16.8 75.8 40 107.6 69.2-18.5 15.8-38.4 29.7-59.4 41.8-15.7-45-35.8-84.1-59.2-115.4 3.7 1.4 7.4 2.9 11 4.4z m-90.6 700.6c-9.2 7.2-18.4 12.7-27.7 16.4V697c39.9 2.8 78.6 11.6 115.7 26.2-8.3 24.6-17.9 47.3-29 67.8-17.4 32.4-37.8 58.3-59 75.1z m59-633.1c11 20.6 20.7 43.3 29 67.8-37.1 14.6-75.8 23.4-115.7 26.2V141.6c9.2 3.7 18.5 9.1 27.7 16.4 21.2 16.7 41.6 42.6 59 75zM540 640.9V540h147.5c-1.6 44.2-7.1 87.1-16.3 127.8l-0.3 1.2c-41.1-15.6-85.1-25.3-130.9-28.1z m0-156.9V383.1c45.8-2.8 89.8-12.5 130.9-28.1l0.3 1.2c9.2 40.7 14.7 83.5 16.3 127.8H540z m-56 56v100.9c-45.8 2.8-89.8 12.5-130.9 28.1l-0.3-1.2c-9.2-40.7-14.7-83.5-16.3-127.8H484z m-147.5-56c1.6-44.2 7.1-87.1 16.3-127.8l0.3-1.2c41.1 15.6 85 25.3 130.9 28.1V484H336.5zM484 697v185.4c-9.2-3.7-18.5-9.1-27.7-16.4-21.2-16.7-41.7-42.7-59.1-75.1-11-20.6-20.7-43.3-29-67.8 37.2-14.6 75.9-23.3 115.8-26.1z m0-370c-39.9-2.8-78.6-11.6-115.7-26.2 8.3-24.6 17.9-47.3 29-67.8 17.4-32.4 37.8-58.4 59.1-75.1 9.2-7.2 18.4-12.7 27.7-16.4V327zM365.7 165.5c3.7-1.5 7.3-3 11-4.4-23.4 31.3-43.5 70.4-59.2 115.4-21-12-40.9-26-59.4-41.8 31.8-29.2 67.9-52.4 107.6-69.2zM165.5 365.7c13.8-32.6 32-62.8 54.2-90.2 24.9 21.5 52.2 40.3 81.5 55.9-11.6 46.9-18.8 98.4-20.7 152.6H137c3-40.9 12.6-80.6 28.5-118.3zM137 540h143.5c1.9 54.2 9.1 105.7 20.7 152.6-29.3 15.6-56.6 34.4-81.5 55.9-22.2-27.4-40.4-57.6-54.2-90.2C149.6 620.6 140 580.9 137 540z m228.7 318.5c-39.7-16.8-75.8-40-107.6-69.2 18.5-15.8 38.4-29.7 59.4-41.8 15.7 45 35.8 84.1 59.2 115.4-3.7-1.4-7.4-2.9-11-4.4z m292.6 0c-3.7 1.5-7.3 3-11 4.4 23.4-31.3 43.5-70.4 59.2-115.4 21 12 40.9 26 59.4 41.8-31.8 29.2-67.9 52.4-107.6 69.2z\"  ></path></symbol><symbol id=\"p-config\" viewBox=\"0 0 1024 1024\"><path d=\"M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56c10.1-8.6 13.8-22.6 9.3-35.2l-0.9-2.6c-18.1-50.5-44.9-96.9-79.7-137.9l-1.8-2.1c-8.6-10.1-22.5-13.9-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85c-2.4-13.1-12.7-23.3-25.8-25.7l-2.7-0.5c-52.1-9.4-106.9-9.4-159 0l-2.7 0.5c-13.1 2.4-23.4 12.6-25.8 25.7l-15.8 85.4c-35.9 13.6-69.2 32.9-99 57.4l-81.9-29.1c-12.5-4.4-26.5-0.7-35.1 9.5l-1.8 2.1c-34.8 41.1-61.6 87.5-79.7 137.9l-0.9 2.6c-4.5 12.5-0.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5c-10.1 8.6-13.8 22.6-9.3 35.2l0.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1c8.6 10.1 22.5 13.9 35.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4c2.4 13.1 12.7 23.3 25.8 25.7l2.7 0.5c26.1 4.7 52.8 7.1 79.5 7.1 26.7 0 53.5-2.4 79.5-7.1l2.7-0.5c13.1-2.4 23.4-12.6 25.8-25.7l15.7-85c36.2-13.6 69.7-32.9 99.7-57.6l81.3 28.9c12.5 4.4 26.5 0.7 35.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l0.9-2.6c4.5-12.3 0.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9c-11.3 26.1-25.6 50.7-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97c-28.1 3.2-56.8 3.2-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9z\"  ></path><path d=\"M512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176z m79.2 255.2C570 602.3 541.9 614 512 614c-29.9 0-58-11.7-79.2-32.8C411.7 560 400 531.9 400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8C612.3 444 624 472.1 624 502c0 29.9-11.7 58-32.8 79.2z\"  ></path></symbol><symbol id=\"p-appstore1\" viewBox=\"0 0 1024 1024\"><path d=\"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16z m-52 268H212V212h200v200zM864 144H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16z m-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16z m-52 268H212V612h200v200zM864 544H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16z m-52 268H612V612h200v200z\"  ></path></symbol><symbol id=\"p-log\" viewBox=\"0 0 1024 1024\"><path d=\"M857 137H167c-16.59375 0-30 13.40625-30 30v690c0 16.59375 13.40625 30 30 30h690c16.59375 0 30-13.40625 30-30V167c0-16.59375-13.40625-30-30-30z m-37.5 682.5H204.5V204.5h615v615z\"  ></path><path d=\"M493.25 407h172.5c4.125 0 7.5-3.375 7.5-7.5v-45c0-4.125-3.375-7.5-7.5-7.5H493.25c-4.125 0-7.5 3.375-7.5 7.5v45c0 4.125 3.375 7.5 7.5 7.5zM493.25 542h172.5c4.125 0 7.5-3.375 7.5-7.5v-45c0-4.125-3.375-7.5-7.5-7.5H493.25c-4.125 0-7.5 3.375-7.5 7.5v45c0 4.125 3.375 7.5 7.5 7.5zM493.25 677h172.5c4.125 0 7.5-3.375 7.5-7.5v-45c0-4.125-3.375-7.5-7.5-7.5H493.25c-4.125 0-7.5 3.375-7.5 7.5v45c0 4.125 3.375 7.5 7.5 7.5z\"  ></path><path d=\"M388.25 377m-37.5 0a37.5 37.5 0 1 0 75 0 37.5 37.5 0 1 0-75 0Z\"  ></path><path d=\"M388.25 512m-37.5 0a37.5 37.5 0 1 0 75 0 37.5 37.5 0 1 0-75 0Z\"  ></path><path d=\"M388.25 647m-37.5 0a37.5 37.5 0 1 0 75 0 37.5 37.5 0 1 0-75 0Z\"  ></path></symbol><symbol id=\"p-host\" viewBox=\"0 0 1024 1024\"><path d=\"M832 64H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32z m-600 72h560v208H232V136z m560 480H232V408h560v208z m0 272H232V680h560v208z\"  ></path><path d=\"M496 208H312c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 544h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H312c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z\"  ></path><path d=\"M680 788m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z\"  ></path></symbol><symbol id=\"p-home\" viewBox=\"0 0 1024 1024\"><path d=\"M946.5 505L560.1 118.8l-25.9-25.9c-12.3-12.2-32.1-12.2-44.4 0L77.5 505c-12.3 12.3-18.9 28.6-18.8 46 0.4 35.2 29.7 63.3 64.9 63.3h42.5V940h691.8V614.3h43.4c17.1 0 33.2-6.7 45.3-18.8 12.1-12.1 18.7-28.2 18.7-45.3 0-17-6.7-33.1-18.8-45.2zM568 868H456V664h112v204z m217.9-325.7V868H632V640c0-22.1-17.9-40-40-40H432c-22.1 0-40 17.9-40 40v228H238.1V542.3h-96l370-369.7 23.1 23.1L882 542.3h-96.1z\"  ></path></symbol><symbol id=\"p-plan\" viewBox=\"0 0 1024 1024\"><path d=\"M234.666667 213.333333h57.706666v63.978667L234.666667 277.333333v106.666667h554.666666v-106.666667h-55.232v-64H789.333333a64 64 0 0 1 64 64v512a64 64 0 0 1-64 64H234.666667a64 64 0 0 1-64-64V277.333333a64 64 0 0 1 64-64z m554.666666 234.666667H234.666667v341.333333h554.666666V448z m-384 192v64h-106.666666v-64h106.666666z m160 0v64h-106.666666v-64h106.666666z m160 0v64h-106.666666v-64h106.666666z m-320-128v64h-106.666666v-64h106.666666z m160 0v64h-106.666666v-64h106.666666z m160 0v64h-106.666666v-64h106.666666zM697.728 170.666667v106.666666h-64.021333V170.666667h64z m-104.170667 42.666666v64l-160.618666-0.021333V213.333333h160.618666z m-197.013333-42.666666v106.666666h-64V170.666667h64z\"  ></path></symbol><symbol id=\"p-database\" viewBox=\"0 0 1024 1024\"><path d=\"M94.64086164 644.81962667l146.54173153 70.34521008 270.97275142 131.24028917 261.3154951-126.60584297 156.27666015-74.85020205a77.6722963 77.6722963 0 0 1-36.35063467 103.12291791l-347.37639939 168.23819378a77.6722963 77.6722963 0 0 1-67.73024237 0L130.91382401 748.07199881A77.6722963 77.6722963 0 0 1 94.64086164 644.81962667z m834.77005866-162.54222578a77.6722963 77.6722963 0 0 1-36.01405513 103.82196978L545.99457541 754.33756445a77.6722963 77.6722963 0 0 1-67.73024236 0L130.91382401 586.09937067A77.6722963 77.6722963 0 0 1 93.08741571 486.36814222l384.73677392 181.46837451 34.33115496 16.62187141 347.3763994-168.23819378-0.18123496-0.0776723 70.06041127-33.83922961zM546.02046578 116.10430578l347.35050904 168.21230341a77.6722963 77.6722963 0 0 1 0 139.81013334l-347.35050904 168.23819378a77.6722963 77.6722963 0 0 1-67.73024237 0L130.91382401 424.12674253a77.6722963 77.6722963 0 0 1 0-139.81013334L478.29022341 116.13019614a77.6722963 77.6722963 0 0 1 67.73024237 0z m-33.86512119 69.90506667l-347.3763994 168.21230341 347.3763994 168.23819378 347.3763994-168.23819378-347.3763994-168.21230341z\"  ></path></symbol><symbol id=\"p-rejected-order\" viewBox=\"0 0 1024 1024\"><path d=\"M810.666667 640a64 64 0 0 1 64 64v128a64 64 0 0 1-64 64h-149.333334a64 64 0 0 1-64-64v-128a64 64 0 0 1 64-64h149.333334zM298.666667 192h42.666666v63.978667L298.666667 256v533.333333h256v64H298.666667a64 64 0 0 1-64-64V256a64 64 0 0 1 64-64z m512 512h-149.333334v128h149.333334v-128z m-21.333334 32v64h-105.898666v-64H789.333333zM725.333333 192a64 64 0 0 1 64 64v341.333333h-64V256h-42.666666V192h42.666666z m-170.666666 341.333333v64h-192v-64h192z m106.666666-128v64H362.666667v-64h298.666666zM576 128a64 64 0 0 1 64 64v42.666667a64 64 0 0 1-64 64h-128a64 64 0 0 1-64-64V192a64 64 0 0 1 64-64h128z m0 64h-128v42.666667h128V192z\"  ></path></symbol><symbol id=\"p-toolbox\" viewBox=\"0 0 1024 1024\"><path d=\"M261.68888853 284.4444448v68.26666667h-68.26666666v113.77777706h637.15555626v-113.77777706h-68.26666666v-68.26666667h68.26666666a68.26666667 68.26666667 0 0 1 68.26666667 68.26666667v455.1111104a68.26666667 68.26666667 0 0 1-68.26666667 68.26666666H193.42222187a68.26666667 68.26666667 0 0 1-68.26666667-68.26666666V352.71111147a68.26666667 68.26666667 0 0 1 68.26666667-68.26666667h68.26666666z m568.8888896 250.3111104h-204.8v136.53333333H398.22222187v-136.53333333h-204.8v273.06666667h637.15555626V534.7555552z m-273.06666666 0h-91.02222294v68.26666667h91.02222294v-68.26666667z m91.02222186-409.6a68.26666667 68.26666667 0 0 1 68.26666667 68.26666667v159.2888896H307.2v-159.2888896a68.26666667 68.26666667 0 0 1 68.26666667-68.26666667h273.06666666z m0 68.26666667H375.46666667v91.02222293h273.06666666v-91.02222293z\"  ></path></symbol><symbol id=\"p-yingwen\" viewBox=\"0 0 1024 1024\"><path d=\"M827.446642 727.549335h-80.351338v-155.002256c0-32.799081-1.717712-54.027541-5.13146-63.654674a44.548518 44.548518 0 0 0-16.758075-22.435012 47.68411 47.68411 0 0 0-27.895204-8.019601c-13.911478 0-26.358114 3.802085-37.448282 11.45683-11.057656 7.611395-18.672664 17.713563-22.742069 30.306503-4.096499 12.594746-6.142942 35.880486-6.142942 69.78136v137.56685H550.625935V423.817812h74.640081v44.620767c26.50803-34.334365 59.854395-51.468133 100.078832-51.468133 17.758718 0 33.938804 3.186166 48.614311 9.55669 14.686345 6.404843 25.812637 14.529204 33.319272 24.45075 7.542759 9.91974 12.811492 21.159824 15.73395 33.75457 2.935101 12.594746 4.436067 30.602722 4.436067 54.07089l-0.001806 188.745989zM197.680437 727.549335V308.279575h310.875109v70.910245H282.319732v92.980402h210.491026v70.659181H282.319732v114.062557h234.255414v70.657375h-318.85136z\" fill=\"#231815\" ></path></symbol><symbol id=\"p-zhongwen\" viewBox=\"0 0 1024 1024\"><path d=\"M555.231787 330.203429v-107.997284h-68.202727v108.038827H263.433935v273.457531H487.02906v210.976899h68.202727V603.70431h224.21827V330.203429H555.231787z m-68.202727 209.074952h-157.337694v-144.605675h157.335888v144.605675z m226.131053 0H555.195662v-144.605675h157.962645v144.605675z\" fill=\"#231815\" ></path></symbol><symbol id=\"p-logout\" viewBox=\"0 0 1024 1024\"><path d=\"M610.87695313 907.5078125L149.45117213 907.5078125c-19.77539063 0-32.95898463-13.18359399-32.95898463-32.95898463L116.4921875 149.45117213c0-19.77539063 13.18359399-32.95898463 32.95898463-32.95898463l461.425781 0c19.77539063 0 32.95898463 13.18359399 32.95898462 32.95898463l0 230.71289088c0 19.77539063-13.18359399 32.95898463-32.95898462 32.95898464s-32.95898463-13.18359399-32.95898463-32.95898464L577.9179685 182.410156 182.410156 182.410156l0 659.17968724 395.5078125 0 0-197.75390625c0-19.77539063 13.18359399-32.95898463 32.95898463-32.95898464s32.95898463 13.18359399 32.95898462 32.95898464l0 230.71289088C643.83593775 894.3242185 630.65234375 907.5078125 610.87695313 907.5078125z\"  ></path><path d=\"M874.54882787 544.95898463L380.16406225 544.95898463c-19.77539063 0-32.95898463-13.18359399-32.95898464-32.95898463s13.18359399-32.95898463 32.95898464-32.95898463l494.38476562 0c19.77539063 0 32.95898463 13.18359399 32.95898463 32.95898463S894.3242185 544.95898463 874.54882787 544.95898463z\"  ></path><path d=\"M874.54882787 544.95898463c-9.88769531 0-16.47949193-3.29589869-23.07128931-9.88769531l-131.83593778-131.83593776c-13.18359399-13.18359399-13.18359399-32.95898463 0-46.14257787s32.95898463-13.18359399 46.14257788 0l131.83593775 131.83593774c13.18359399 13.18359399 13.18359399 32.95898463 0 46.14257789C891.02832057 541.66308594 884.43652318 544.95898463 874.54882787 544.95898463z\"  ></path><path d=\"M742.71289088 676.79492162c-9.88769531 0-16.47949193-3.29589869-23.07128932-9.88769531-13.18359399-13.18359399-13.18359399-32.95898463 0-46.14257787l131.83593776-131.83593776c13.18359399-13.18359399 32.95898463-13.18359399 46.14257787 0s13.18359399 32.95898463 0 46.14257787l-131.83593776 131.83593776C759.19238281 673.49902369 752.60058619 676.79492162 742.71289088 676.79492162z\"  ></path></symbol><symbol id=\"p-taolun\" viewBox=\"0 0 1024 1024\"><path d=\"M479.232 18.944c-264.704 0-479.232 186.368-479.232 416.256 0 140.8 81.92 266.24 205.312 343.04v129.024c0 17.408 12.288 27.648 26.112 27.648 5.12 0 10.24-1.536 13.824-3.584l130.56-87.04c33.28 7.168 66.048 8.704 100.864 8.704 264.704 0 478.72-186.368 478.72-416.256 2.56-231.424-211.456-417.792-476.16-417.792z m0 764.416c-29.696 0-59.392-3.584-88.576-8.704l-13.824-3.584c-8.704-1.536-17.408 0-26.112 5.12l-12.288 8.704-62.464 41.984v-67.584c0-12.288-7.168-24.576-17.408-29.696l-17.408-10.24c-108.032-64.512-172.544-170.496-172.544-282.112 0-191.488 182.784-346.624 409.088-346.624s409.088 155.136 409.088 346.624c1.536 189.44-183.296 346.112-407.552 346.112z m-271.872-335.872c0 31.744 25.6 57.344 57.344 57.344 31.744 0 57.344-25.6 57.344-57.344 0-31.744-25.6-57.344-57.344-57.344-31.744 0-57.344 25.6-57.344 57.344z m217.6 0c0 31.744 25.6 57.344 57.344 57.344 31.744 0 57.344-25.6 57.344-57.344 0-31.744-25.6-57.344-57.344-57.344-31.744 0-57.344 25.6-57.344 57.344z m210.944 0c0 31.744 25.6 57.344 57.344 57.344 31.744 0 57.344-25.6 57.344-57.344 0-31.744-25.6-57.344-57.344-57.344-31.744 0-57.344 25.6-57.344 57.344z\" fill=\"\" ></path><path d=\"M965.632 537.6c-1.024-1.536-2.048-2.56-3.072-3.584-7.68 28.672-18.432 55.808-31.744 81.92 0.512 1.024 1.024 2.56 2.048 3.584 9.216 20.992 13.824 43.52 13.824 67.072 0 62.464-36.352 122.368-96.768 158.72l-9.728 5.632c-5.632 3.072-9.728 9.728-9.728 16.384v37.888l-35.328-23.552-6.656-5.12c-5.12-3.072-9.728-4.096-14.848-3.072l-7.68 2.048c-16.896 3.072-33.28 5.12-49.664 5.12-39.936 0-77.312-8.704-110.08-24.064-32.768 7.68-66.56 12.288-100.864 13.824 54.272 47.104 129.536 75.776 212.48 75.776 22.016 0 43.008-1.024 63.488-5.632l82.432 54.784c2.048 1.024 5.632 2.048 8.704 2.048 8.704 0 16.384-6.656 16.384-17.408v-80.896c77.824-48.128 129.536-126.976 129.536-216.064-1.024-53.248-19.968-103.936-52.736-145.408z\" fill=\"\" ></path></symbol><symbol id=\"p-bug\" viewBox=\"0 0 1024 1024\"><path d=\"M969.143 548.571q0 14.848-10.862 25.71t-25.71 10.862h-128q0 97.719-38.29 165.705L885.138 870.29Q896 881.152 896 896t-10.862 25.71q-10.276 10.861-25.71 10.861t-25.71-10.861L720.568 809.143q-2.853 2.852-8.558 7.424t-23.99 16.274-37.157 20.846-46.848 16.567-55.443 7.424v-512H475.43v512q-29.148 0-58.003-7.717t-49.7-18.87-37.705-22.273-24.869-18.578l-8.558-8.01-104.557 118.273q-11.447 11.995-27.429 11.995-13.714 0-24.576-9.143-10.862-10.276-11.703-25.417t8.85-26.587L252.6 741.632q-33.134-65.134-33.134-156.562h-128q-14.848 0-25.71-10.862t-10.861-25.71 10.861-25.71 25.71-10.861h128v-168.01l-98.852-98.852q-10.862-10.862-10.862-25.71t10.862-25.71 25.71-10.861 25.709 10.862l98.853 98.852h482.304l98.852-98.852q10.862-10.862 25.71-10.862t25.71 10.862 10.861 25.71-10.861 25.71l-98.853 98.852v168.009h128q14.848 0 25.71 10.862t10.861 25.71zM694.857 219.43H329.143q0-75.996 53.43-129.427T512 36.572t129.426 53.43 53.431 129.427z\"  ></path></symbol><symbol id=\"p-huaban88\" viewBox=\"0 0 1024 1024\"><path d=\"M960 512a435.2 435.2 0 0 1-85.76 263.36 440 440 0 0 1-220.48 161.92 26.88 26.88 0 0 1-23.04-4.16 22.72 22.72 0 0 1-7.04-17.6v-122.88a104.64 104.64 0 0 0-30.4-82.88 416 416 0 0 0 59.52-10.24 218.24 218.24 0 0 0 54.72-23.04 164.8 164.8 0 0 0 47.04-38.4 184 184 0 0 0 32-61.44 293.76 293.76 0 0 0 12.16-88 168.64 168.64 0 0 0-46.4-120 154.88 154.88 0 0 0-4.48-119.04 76.48 76.48 0 0 0-47.04 6.4 300.48 300.48 0 0 0-53.76 25.6l-22.08 13.76a416 416 0 0 0-224 0c-6.4-4.48-14.4-9.28-24.64-15.68A326.08 326.08 0 0 0 326.4 256a82.88 82.88 0 0 0-50.24-8 156.8 156.8 0 0 0-4.16 120 173.76 173.76 0 0 0-46.08 120.64A288 288 0 0 0 238.08 576a196.48 196.48 0 0 0 32 61.44 152 152 0 0 0 47.04 39.04 267.84 267.84 0 0 0 54.72 23.04 414.08 414.08 0 0 0 59.84 10.24 92.48 92.48 0 0 0-28.8 60.16 99.2 99.2 0 0 1-26.56 8.32 167.36 167.36 0 0 1-32 2.88A69.12 69.12 0 0 1 303.36 768a109.44 109.44 0 0 1-32-36.48 96 96 0 0 0-28.16-30.4 80.64 80.64 0 0 0-28.8-14.08h-11.52a41.6 41.6 0 0 0-16.96 2.56q-4.8 2.88-2.88 6.72a44.16 44.16 0 0 0 5.44 8 55.36 55.36 0 0 0 7.68 7.36l4.16 2.56a78.08 78.08 0 0 1 25.6 22.08 157.76 157.76 0 0 1 18.24 29.44l5.76 13.44a72.32 72.32 0 0 0 25.6 36.16 96 96 0 0 0 38.72 16.64 196.16 196.16 0 0 0 40.32 4.16 182.08 182.08 0 0 0 32-2.24l13.44-2.24v83.84a23.04 23.04 0 0 1-7.68 17.6 27.84 27.84 0 0 1-23.36 4.16 438.08 438.08 0 0 1-219.2-162.88A427.84 427.84 0 0 1 64 512a437.44 437.44 0 0 1 60.16-224A443.52 443.52 0 0 1 288 124.16 437.44 437.44 0 0 1 512 64a437.44 437.44 0 0 1 224 60.16A443.52 443.52 0 0 1 899.84 288 436.8 436.8 0 0 1 960 512z\"  ></path></symbol><symbol id=\"p-star\" viewBox=\"0 0 1024 1024\"><path d=\"M511.51086 191.938474c3.901867 0 7.855922 2.629897 9.619078 6.404873l76.456323 163.568333 14.470573 30.958082 33.778314 5.177929 176.005596 26.978445c4.025687 0.613984 7.151887 3.372817 8.588609 7.573489 1.49812 4.597715 0.525979 9.113565-2.705622 12.43317L698.311313 578.179258l-22.428825 23.075555 5.163603 31.762401 30.016641 184.659697c0.708128 4.342912-1.251503 9.267061-4.669346 11.773138-1.888 1.294482-3.837398 1.923816-5.951549 1.923816-1.657756 0-3.391237-0.452301-4.885264-1.27504L542.540573 745.200226l-31.029714-17.217127-31.029714 17.217127-152.8492 84.810594c-1.804089 0.979304-3.708462 1.184989-4.993734 1.184989-2.229784 0-4.393054-0.618077-5.947456-1.697665-3.517103-2.547009-5.465478-7.361665-4.743024-11.745509l0.011256-0.070608 0.011256-0.070608 29.979802-184.605461 5.15644-31.749098-22.413476-23.070438L195.321524 445.029726l-0.034792-0.035816-0.034792-0.035816c-3.152806-3.234671-4.151553-7.829316-2.676969-12.299117 1.429559-4.243651 4.564969-7.03216 8.625448-7.652284l175.983083-26.960025 33.787524-5.175883 14.470573-30.967292 76.41232-163.519214C503.630379 194.588837 507.60183 191.938474 511.51086 191.938474M511.51086 127.981836c-28.733415 0-55.262628 16.983813-67.577095 43.278689L367.498932 334.828858 191.515848 361.78786c-27.747971 4.242627-50.581002 23.639396-59.603493 50.610678-9.069563 27.215852-2.341325 56.800657 17.537422 77.197196l129.370463 133.159766-29.979802 184.604438c-4.693906 28.500101 7.41897 57.661258 30.884404 74.331939 12.605086 8.908904 27.721365 13.461593 42.912345 13.461593 12.334933 0 24.720008-3.003404 35.839253-9.114588l153.034419-84.913948 153.084561 84.937484c11.003612 6.083555 23.384593 9.268084 35.847439 9.268084 15.358803 0 30.170137-4.721535 42.881646-13.662161 23.39585-16.623609 35.509748-45.783743 30.861892-74.284867l-30.011524-184.628998 129.413442-133.147486c19.854187-20.396539 26.571169-49.970088 17.513886-77.246315-9.03477-26.960025-31.886221-46.332235-59.615773-50.561559L655.525857 334.828858l-76.456323-163.568333C566.788837 144.976906 540.26781 127.981836 511.51086 127.981836L511.51086 127.981836z\"  ></path></symbol><symbol id=\"p-file-ppt\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 73.144889H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333v804.579556a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 73.144889z\" fill=\"#F2733D\" ></path><path d=\"M661.944889 288.910222a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 73.144889v215.765333z\" fill=\"#EF8F6F\" ></path><path d=\"M512 420.565333h-140.8v354.744889h91.434667v-124.344889h53.020444c104.234667 0 166.4-43.875556 166.4-118.855111-1.820444-73.144889-58.510222-111.544889-170.055111-111.544889z m-49.365333 62.179556h43.875555c60.344889 0 84.124444 14.620444 84.124445 51.2 0 38.4-27.434667 56.689778-82.289778 56.689778H460.8v-107.889778h1.834667z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-html\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 73.144889H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333v804.579556a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 73.144889z\" fill=\"#8DDE83\" ></path><path d=\"M661.944889 288.910222a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 73.144889v215.765333z\" fill=\"#BCFFB3\" ></path><path d=\"M332.8 605.255111l-31.089778-10.965333 104.234667-32.924445v-71.310222L204.8 568.689778v53.020444l201.144889 78.634667v-71.310222L332.8 605.255111z m106.055111 193.834667h58.510222l80.469334-362.055111h-58.524445l-80.455111 362.055111zM819.2 566.855111l-201.144889-76.8v71.310222l73.144889 21.944889 31.089778 10.979556-104.234667 34.744889v71.310222L819.2 621.710222v-54.855111z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-word\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 73.144889H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333v804.579556a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 73.144889z\" fill=\"#108EE9\" ></path><path d=\"M661.944889 288.910222a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 73.144889v215.765333z\" fill=\"#57A1F2\" ></path><path d=\"M647.310222 420.565333l-27.420444 171.889778c-3.655111 31.089778-9.144889 64-12.8 96.910222a594.858667 594.858667 0 0 0-5.489778-25.6c-5.489778-23.765333-9.144889-47.530667-14.634667-69.475555L546.744889 420.551111h-64l-40.234667 171.889778c-7.310222 32.910222-12.8 64-18.275555 95.089778l-5.489778-43.889778c-1.834667-16.455111-5.489778-34.744889-7.310222-51.2l-31.089778-171.889778h-78.634667l67.655111 354.744889H460.8l38.4-175.544889c3.655111-21.930667 9.144889-43.875556 12.8-65.820444 3.655111 20.110222 7.310222 40.220444 12.8 65.820444l40.234667 173.724445h91.420444l64-351.089778v-3.655111h-73.144889v1.820444z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-excel\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 73.144889H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333v804.579556a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 73.144889z\" fill=\"#1DB87B\" ></path><path d=\"M661.944889 288.910222a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 73.144889v215.765333z\" fill=\"#63CE9E\" ></path><path d=\"M568.689778 596.110222l113.365333-175.544889h-96.910222l-40.234667 73.144889c-9.144889 14.634667-16.455111 31.089778-25.6 51.2-5.475556-9.144889-9.144889-18.275556-14.620444-25.6-5.489778-9.144889-10.979556-18.275556-14.634667-25.6l-43.889778-73.144889h-102.4l113.379556 171.889778-120.689778 182.855111H435.2l45.710222-78.620444c9.144889-14.634667 16.455111-31.089778 29.255111-53.034667 5.489778 9.144889 9.144889 16.455111 14.634667 23.779556 5.489778 9.130667 10.965333 20.110222 16.455111 29.255111l47.544889 78.620444h102.4l-122.510222-179.2z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-pdf\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 82.289778H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333V923.448889a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 82.289778z\" fill=\"#EC5C59\" ></path><path d=\"M661.944889 298.055111a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 82.289778v215.765333z\" fill=\"#EA8B8B\" ></path><path d=\"M298.055111 424.234667h-84.110222V778.951111h56.689778V654.648889h29.255111c62.165333 0 98.744889-43.889778 98.744889-118.869333-1.834667-73.130667-34.744889-111.530667-100.579556-111.530667zM268.8 486.4h23.765333c34.744889 0 47.544889 14.634667 47.544889 51.2 0 38.4-14.620444 56.689778-45.710222 56.689778h-25.6V486.4z m235.889778-62.165333H433.351111V778.951111h73.144889c76.8 0 122.524444-65.820444 122.524444-179.2 0-111.530667-43.889778-175.530667-124.344888-175.530667z m-14.634667 64h10.979556c47.530667 0 71.310222 36.565333 71.310222 113.365333s-23.779556 115.2-71.310222 115.2h-10.979556V488.234667z m338.289778 1.820444V424.248889H669.255111V778.951111h56.689778v-140.8h87.765333v-65.820444h-87.765333v-82.289778h102.4z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-mp3\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 73.144889H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333v804.579556a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 73.144889z\" fill=\"#8450EA\" ></path><path d=\"M661.944889 288.910222a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 73.144889v215.765333z\" fill=\"#9C72ED\" ></path><path d=\"M696.689778 694.855111V418.744889c0-5.489778 0-9.144889-1.834667-14.634667-5.489778-20.110222-25.6-31.075556-45.710222-25.6L394.951111 449.848889c-16.455111 5.475556-27.420444 20.110222-27.420444 38.4v243.2c-12.8-1.834667-29.255111-1.834667-43.889778 5.475555-31.089778 14.634667-47.544889 47.544889-34.744889 73.144889 10.979556 25.6 47.544889 34.744889 78.634667 20.110223 16.455111-7.310222 29.255111-20.110222 34.744889-34.730667V793.6c3.655111-9.144889 3.655111-20.110222 1.820444-29.255111V625.351111l254.179556-69.475555V665.6c-12.8-3.655111-29.255111-1.834667-43.889778 5.489778-31.089778 14.620444-47.544889 47.544889-34.744889 73.144889 10.979556 25.6 47.544889 34.730667 78.634667 20.110222 16.455111-7.310222 29.255111-21.944889 34.744888-36.579556v-1.820444c5.475556-10.979556 7.310222-21.944889 3.655112-31.089778z m-36.579556-179.2l-252.344889 69.489778V522.951111c3.669333 0 7.324444 0 9.144889-1.820444l243.2-67.655111v62.165333z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-svg\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 73.144889H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333v804.579556a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 73.144889z\" fill=\"#5BC0E5\" ></path><path d=\"M661.944889 288.910222a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 73.144889v215.765333z\" fill=\"#7EDCFE\" ></path><path d=\"M336.455111 570.510222L303.544889 550.4l-1.834667-1.834667c-20.110222-10.965333-34.744889-20.110222-34.744889-40.220444s10.979556-32.910222 31.089778-32.910222c18.289778 0 32.910222 9.130667 49.379556 27.420444l3.655111 3.655111 29.255111-49.365333-1.834667-1.834667C358.4 426.055111 329.144889 409.6 298.055111 409.6c-51.2 0-87.765333 43.889778-87.765333 104.234667 0 54.855111 27.420444 82.275556 51.2 96.910222l34.744889 21.944889c20.110222 12.8 32.910222 20.110222 32.910222 42.055111 0 23.765333-12.8 36.565333-34.744889 36.565333-20.110222 0-42.055111-14.620444-58.510222-36.565333l-3.655111-3.655111-32.924445 54.855111 1.834667 1.820444c23.765333 32.924444 58.510222 53.034667 91.420444 53.034667 54.869333 0 93.269333-43.889778 93.269334-107.889778 1.820444-51.2-14.634667-82.275556-49.379556-102.4z m181.034667 16.455111c-3.655111 20.124444-7.324444 38.4-10.979556 58.524445-1.820444 12.8-5.475556 25.6-7.310222 40.220444-3.655111-14.620444-5.489778-31.075556-7.310222-45.710222-3.655111-16.455111-5.489778-34.744889-9.144889-53.034667l-32.910222-171.875555h-58.524445l76.8 354.744889h62.179556l76.8-354.744889H550.4L517.489778 586.951111z m202.965333-16.455111v64h43.889778v64c-5.489778 7.324444-16.455111 10.979556-27.434667 10.979556-43.875556 0-69.475556-42.055111-69.475555-117.034667 0-71.310222 25.6-115.2 65.820444-115.2 21.944889 0 34.744889 14.634667 45.710222 27.434667l3.669334 3.655111 29.255111-51.2-1.834667-1.834667C789.944889 424.248889 762.510222 409.6 731.448889 409.6c-71.324444 0-120.689778 76.8-120.689778 184.689778 0 111.544889 47.544889 182.855111 120.689778 182.855111 32.910222 0 62.165333-16.455111 82.275555-43.889778V570.510222h-93.255111z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-file-jpg\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 82.289778H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333V923.448889a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 82.289778z\" fill=\"#FCD165\" ></path><path d=\"M259.697778 724.295111c21.191111 0 38.272-4.437333 51.228444-13.312a68.152889 68.152889 0 0 0 26.808889-35.328c3.669333-11.235556 5.504-27.264 5.504-48.128V468.024889h-50.702222v162.773333c0 20.394667-2.360889 33.834667-7.054222 40.305778-4.693333 6.471111-12.714667 9.713778-24.064 9.713778-11.235556 0-19.584-4.536889-25.088-13.582222-3.669333-5.959111-5.788444-16.156444-6.357334-30.592l-47.957333 5.504c0.113778 26.922667 6.869333 47.345778 20.280889 61.269333 13.411556 13.923556 32.540444 20.878222 57.400889 20.878222z m172.558222-4.295111v-95.047111h33.166222c23.04 0 40.618667-1.208889 52.764445-3.612445 8.931556-1.948444 17.735111-5.930667 26.382222-11.946666 8.647111-6.016 15.786667-14.293333 21.404444-24.832 5.603556-10.538667 8.419556-23.552 8.419556-39.011556 0-20.053333-4.878222-36.408889-14.606222-49.066666-9.742222-12.657778-21.831111-20.892444-36.266667-24.675556-9.400889-2.517333-29.568-3.783111-60.501333-3.783111h-81.635556v251.975111h50.872889z m27.832889-137.841778h-27.832889v-71.495111h28.700444c16.042667 0.085333 26.88 0.654222 32.483556 1.706667 8.248889 1.493333 15.075556 5.219556 20.451556 11.178666 5.390222 5.959111 8.078222 13.511111 8.078222 22.684445 0 7.452444-1.92 13.980444-5.76 19.598222-3.84 5.617778-9.130667 9.742222-15.900445 12.373333-6.755556 2.631111-20.167111 3.953778-40.220444 3.953778z m259.171555 142.136889c20.750222 0 41.230222-3.925333 61.44-11.776 20.238222-7.850667 35.669333-16.924444 46.336-27.235555v-100.380445h-109.653333v42.453333h58.254222v31.971556c-7.68 5.959111-16.867556 11.022222-27.591111 15.217778a87.608889 87.608889 0 0 1-32.042666 6.257778c-21.546667 0-38.968889-7.466667-52.252445-22.414223-13.297778-14.961778-19.939556-37.390222-19.939555-67.299555 0-27.733333 6.556444-48.64 19.683555-62.72 13.112889-14.108444 30.791111-21.148444 53.020445-21.148445 14.663111 0 26.894222 3.584 36.693333 10.737778 9.799111 7.168 16.469333 16.924444 20.024889 29.297778l50.531555-9.443556c-5.034667-23.139556-16.355556-41.272889-33.948444-54.4-17.578667-13.112889-42.026667-19.683556-73.301333-19.683555-24.064 0-44.174222 4.181333-60.330667 12.558222-21.191111 10.88-37.290667 26.524444-48.298667 46.919111-10.993778 20.394667-16.497778 43.704889-16.497778 69.944889 0 24.177778 4.935111 46.549333 14.791112 67.128889 9.841778 20.565333 24.576 36.366222 44.16 47.431111 19.598222 11.050667 42.567111 16.583111 68.920888 16.583111z\" fill=\"#FFFFFF\" ></path><path d=\"M661.944889 298.055111a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 82.289778v215.765333z\" fill=\"#FFE3A6\" ></path></symbol><symbol id=\"p-file-video\" viewBox=\"0 0 1024 1024\"><path d=\"M661.944889 82.289778H146.289778a36.679111 36.679111 0 0 0-36.579556 36.565333V923.448889a36.679111 36.679111 0 0 0 36.579556 36.565333h731.420444a36.679111 36.679111 0 0 0 36.579556-36.565333v-588.8L661.944889 82.289778z\" fill=\"#8095FF\" ></path><path d=\"M615.210667 398.222222c20.437333 0 37.148444 20.437333 39.011555 42.737778v284.302222C654.222222 749.411556 637.496889 768 617.059556 768H278.940444C258.503111 768 241.777778 749.411556 241.777778 725.262222v-284.302222C241.777778 416.810667 258.503111 398.222222 278.940444 398.222222z m-40.874667 89.187556h-105.898667v42.737778h105.898667v-42.737778z\" fill=\"#FFFFFF\" ></path><path d=\"M768.796444 478.805333c-9.585778-7.210667-21.091556-10.808889-32.597333-9.016889l-93.966222 19.825778C624.981333 493.226667 611.555556 511.246222 611.555556 531.072v104.533333c0 19.825778 13.425778 37.859556 30.677333 41.457778L736.199111 696.888889h7.68c9.585778 0 17.251556-3.598222 24.917333-9.016889 9.585778-7.210667 13.425778-19.825778 13.425778-32.426667V511.232c0-12.615111-3.84-23.424-13.425778-32.440889z\" fill=\"#FFFFFF\" ></path><path d=\"M661.944889 298.055111a36.679111 36.679111 0 0 0 36.565333 36.579556h215.779556L661.944889 82.289778v215.765333z\" fill=\"#B6C6FF\" ></path></symbol><symbol id=\"p-file-png\" viewBox=\"0 0 1024 1024\"><path d=\"M137.90231 0C110.10583 0.70144 87.74167 23.10144 87.04023 50.93376V968.3456a48.70656 48.70656 0 0 0 15.27296 35.6352 49.9712 49.9712 0 0 0 35.58912 15.29856h746.33728a48.64 48.64 0 0 0 35.58912-15.29344 50.37056 50.37056 0 0 0 15.27296-35.64032V288.7168L646.72791 0H137.90231z\" fill=\"#6CCBFF\" ></path><path d=\"M935.10167 288.7168h-237.44512c-27.82208-0.6656-50.2272-23.07584-50.92864-50.93376V0l288.37376 288.7168z\" fill=\"#C4EAFF\" ></path><path d=\"M299.05943 390.65088h84.7872v84.91008H299.05943V390.656z m169.57952 0h84.79744v84.91008H468.63383V390.656z m169.65632 0h84.7872v84.91008h-84.7872V390.656zM383.84663 475.56096h84.79232v84.9152h-84.7872V475.56096z m169.58976 0h84.85888v84.9152H553.43639V475.56096zM383.84663 645.38624h84.79232v84.97664h-84.7872v-84.97664z m169.58976 0h84.85888v84.97664H553.43639v-84.97664z m-254.38208-84.91008H383.84663v84.91008H299.05943v-84.91008z m169.58464 0h84.79744v84.91008H468.63383v-84.91008z m169.65632 0h84.7872v84.91008h-84.7872v-84.91008z m-339.24096 169.88672H383.84663v85.0432H299.05943v-85.0432z m169.58464 0h84.79744v85.0432H468.63383v-85.0432z m169.65632 0h84.7872v85.0432h-84.7872v-85.0432z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-yanzhengma1\" viewBox=\"0 0 1024 1024\"><path d=\"M544 269h124c17.673 0 32 14.327 32 32 0 17.673-14.327 32-32 32H544v116a32.5 32.5 0 0 1-0.056 1.916C670.218 466.412 768 574.037 768 704.5 768 845.609 653.609 960 512.5 960 371.391 960 257 845.609 257 704.5c0-130.12 97.27-237.523 223.064-253.46A32.488 32.488 0 0 1 480 449V96c0-17.673 14.327-32 32-32 17.673 0 32 14.327 32 32v45h192c17.673 0 32 14.327 32 32 0 17.673-14.327 32-32 32H544v64z m-31.5 627C618.263 896 704 810.263 704 704.5S618.263 513 512.5 513 321 598.737 321 704.5 406.737 896 512.5 896z\" fill=\"#bfbfbf\" ></path></symbol><symbol id=\"p-caidan\" viewBox=\"0 0 1024 1024\"><path d=\"M896 232.727273h-744.727273a34.909091 34.909091 0 0 1 0-69.818182h744.727273a34.909091 34.909091 0 0 1 0 69.818182zM896 884.363636h-744.727273a34.909091 34.909091 0 0 1 0-69.818181h744.727273a34.909091 34.909091 0 0 1 0 69.818181zM709.818182 558.545455h-558.545455a34.909091 34.909091 0 0 1 0-69.818182h558.545455a34.909091 34.909091 0 0 1 0 69.818182z\"  ></path></symbol><symbol id=\"p-xiangqing\" viewBox=\"0 0 1024 1024\"><path d=\"M512 241.72989082m-77.34417915 0a77.34417915 77.34417915 0 1 0 154.6883583 0 77.34417915 77.34417915 0 1 0-154.6883583 0Z\" fill=\"#1296db\" ></path><path d=\"M512 512m-77.34417915 0a77.34417915 77.34417915 0 1 0 154.6883583 0 77.34417915 77.34417915 0 1 0-154.6883583 0Z\" fill=\"#1296db\" ></path><path d=\"M512 782.27010918m-77.34417915 0a77.34417915 77.34417915 0 1 0 154.6883583 0 77.34417915 77.34417915 0 1 0-154.6883583 0Z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-webdav\" viewBox=\"0 0 1024 1024\"><path d=\"M512 49.548387c255.405419 0 462.451613 207.046194 462.451613 462.451613S767.405419 974.451613 512 974.451613 49.548387 767.405419 49.548387 512 256.594581 49.548387 512 49.548387z m0 66.064516C293.07871 115.612903 115.612903 293.07871 115.612903 512s177.465806 396.387097 396.387097 396.387097 396.387097-177.465806 396.387097-396.387097S730.92129 115.612903 512 115.612903zM332.122839 330.322581a33.032258 33.032258 0 0 1 32.090838 25.236645l54.618839 224.916645 57.575226-225.296516A33.032258 33.032258 0 0 1 508.416 330.322581h17.061161a33.032258 33.032258 0 0 1 32.008258 24.856774l57.575226 225.296516 54.618839-224.916645A33.032258 33.032258 0 0 1 701.786839 330.322581h12.684387a29.217032 29.217032 0 0 1 28.110451 37.194322l-85.80129 302.146065A33.032258 33.032258 0 0 1 625.003355 693.677419h-19.059613a33.032258 33.032258 0 0 1-32.008258-24.87329L517.169548 445.935484 459.974194 668.853677A33.032258 33.032258 0 0 1 427.982452 693.677419h-19.555097a33.032258 33.032258 0 0 1-31.777032-24.047484l-85.371871-302.113032A29.233548 29.233548 0 0 1 319.438452 330.322581h12.684387z\" fill=\"#13227a\" ></path></symbol><symbol id=\"p-tongji\" viewBox=\"0 0 1024 1024\"><path d=\"M157.538462 866.461538h787.692307v78.769231H78.769231V78.769231h78.769231v787.692307z m78.76923-374.153846h78.769231v315.076923h-78.769231V492.307692z m177.23077-118.153846h78.76923v433.230769h-78.76923V374.153846z m177.230769 118.153846h78.769231v315.076923h-78.769231V492.307692z m177.230769-157.538461h78.769231v472.615384h-78.769231V334.769231z m-470.468923 13.075692l-43.697231-65.536L452.923077 149.582769l173.784615 115.849846 154.505846-137.334153 52.322462 58.88-199.936 177.723076L452.923077 244.263385l-155.392 103.581538z\" fill=\"#333333\" ></path></symbol><symbol id=\"p-tamper-4\" viewBox=\"0 0 1024 1024\"><path d=\"M781.824 343.466667 242.133333 343.466667c-11.797333 0-21.354667 9.557333-21.354667 21.354667 0 11.797333 9.557333 21.354667 21.354667 21.354667l539.690667 0c11.84 0 21.376-9.578667 21.376-21.354667C803.2 353.045333 793.664 343.466667 781.824 343.466667z\" fill=\"#1296db\" ></path><path d=\"M242.133333 533.376l441.578667 0c11.84 0 21.376-9.6 21.376-21.376s-9.536-21.354667-21.376-21.354667L242.133333 490.645333c-11.797333 0-21.354667 9.557333-21.354667 21.354667S230.336 533.376 242.133333 533.376z\" fill=\"#1296db\" ></path><path d=\"M781.824 637.781333 242.133333 637.781333c-11.797333 0-21.354667 9.557333-21.354667 21.376 0 11.84 9.557333 21.376 21.354667 21.376l539.690667 0c11.84 0 21.376-9.536 21.376-21.376C803.2 647.36 793.664 637.781333 781.824 637.781333z\" fill=\"#1296db\" ></path><path d=\"M683.712 785.002667 242.133333 785.002667c-11.797333 0-21.354667 9.557333-21.354667 21.376 0 11.84 9.557333 21.354667 21.354667 21.354667l441.578667 0c11.84 0 21.376-9.514667 21.376-21.354667C705.088 794.56 695.552 785.002667 683.712 785.002667z\" fill=\"#1296db\" ></path><path d=\"M781.824 122.666667l-35.008 0c-12.906667-28.864-41.770667-49.088-75.370667-49.088l-66.922667 0C594.794667 31.509333 556.949333 0 512 0c-45.034667 0-82.816 31.509333-92.544 73.578667l-66.922667 0c-33.621333 0-62.485333 20.224-75.413333 49.088l-23.765333 0c-95.765333 0-179.776 78.762667-179.776 168.554667L73.578667 855.466667c0 89.749333 84.010667 168.533333 179.776 168.533333l528.469333 0c92.906667 0 168.576-75.626667 168.554667-168.533333L950.378667 291.221333C950.4 198.272 874.773333 122.666667 781.824 122.666667zM352.533333 116.309333l85.888 0c11.776 0 21.354667-9.557333 21.354667-21.354667 0-28.778667 23.445333-52.224 52.224-52.224 28.8 0 52.202667 23.424 52.202667 52.224 0 11.797333 9.536 21.354667 21.333333 21.354667l85.909333 0c21.994667 0 39.957333 17.92 39.957333 39.978667 0 22.037333-17.962667 39.978667-39.957333 39.978667l-318.933333 0c-22.058667 0-39.978667-17.941333-39.978667-39.978667C312.554667 134.229333 330.517333 116.309333 352.533333 116.309333zM907.648 855.466667c0 69.376-56.448 125.802667-125.802667 125.802667L253.354667 981.269333c-73.002667 0-137.045333-58.794667-137.045333-125.802667L116.309333 291.221333c0-67.029333 64.042667-125.824 137.045333-125.824l17.386667 0c4.608 41.28 39.317333 73.6 81.770667 73.6l318.933333 0c42.453333 0 77.141333-32.32 81.749333-73.6l28.629333 0c69.376 0 125.802667 56.448 125.802667 125.824L907.626667 855.466667z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-tamper-2\" viewBox=\"0 0 1024 1024\"><path d=\"M512 629.333333a117.333333 117.333333 0 1 1 117.333333-117.333333 117.546667 117.546667 0 0 1-117.333333 117.333333z m0-213.333333a96 96 0 1 0 96 96 96 96 0 0 0-96-96z\" fill=\"#1296db\" ></path><path d=\"M512 789.333333a277.333333 277.333333 0 1 1 277.333333-277.333333 277.333333 277.333333 0 0 1-277.333333 277.333333z m0-533.333333a256 256 0 1 0 256 256 256 256 0 0 0-256-256z\" fill=\"#1296db\" ></path><path d=\"M512 949.333333A437.333333 437.333333 0 1 1 949.333333 512 437.76 437.76 0 0 1 512 949.333333z m0-853.333333A416 416 0 1 0 928 512 416.426667 416.426667 0 0 0 512 96z\" fill=\"#1296db\" ></path><path d=\"M85.333333 501.333333h853.333334v21.333334H85.333333z\" fill=\"#1296db\" ></path><path d=\"M501.333333 85.333333h21.333334v853.333334h-21.333334z\" fill=\"#1296db\" ></path><path d=\"M293.76 347.52m-34.56 0a34.56 34.56 0 1 0 69.12 0 34.56 34.56 0 1 0-69.12 0Z\" fill=\"#1296db\" ></path><path d=\"M778.666667 512m-34.56 0a34.56 34.56 0 1 0 69.12 0 34.56 34.56 0 1 0-69.12 0Z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-tamper-3\" viewBox=\"0 0 1024 1024\"><path d=\"M867.584 160.192c-149.632-16.928-262.208-57.408-334.592-120.352l-19.04-16.544-20.544 14.656C379.968 118.944 267.776 160 160 160H128v448c0 137.344 121.088 261.92 370.208 380.864l13.088 6.24 13.344-5.728C771.072 883.52 896 755.232 896 608V163.424l-28.416-3.232zM832 608c0 116.8-107.392 223.36-319.328 316.8C299.872 821.024 192 714.464 192 608V222.976c104.672-6.784 211.584-46.688 318.496-118.944C587.232 162.528 695.168 201.536 832 220.256V608z\" fill=\"#1296db\" ></path><path d=\"M359.776 468.672a32 32 0 1 0-47.968 42.4l121.792 137.824c12.608 14.24 30.176 21.568 47.904 21.568a64.384 64.384 0 0 0 49.696-23.52l197.6-242.72a32 32 0 0 0-49.632-40.416l-197.6 242.688-121.792-137.824z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-tamper-1\" viewBox=\"0 0 1027 1024\"><path d=\"M499.2 768c-147.2 0-268.8-121.6-268.8-268.8 0-147.2 121.6-268.8 268.8-268.8 147.2 0 268.8 121.6 268.8 268.8 0 150.4-121.6 268.8-268.8 268.8z m0-486.4c-121.6 0-220.8 99.2-220.8 220.8s99.2 220.8 220.8 220.8 220.8-99.2 220.8-220.8-99.2-220.8-220.8-220.8z\" fill=\"#1296db\" ></path><path d=\"M800 825.6c-6.4 0-12.8-3.2-16-6.4l-105.6-105.6c-9.6-9.6-9.6-25.6 0-35.2 9.6-9.6 25.6-9.6 35.2 0l105.6 105.6c9.6 9.6 9.6 25.6 0 35.2-6.4 3.2-12.8 6.4-19.2 6.4z m-288-233.6h-102.4c-12.8 0-22.4-9.6-22.4-22.4 0-12.8 9.6-22.4 22.4-22.4H512c12.8 0 22.4 9.6 22.4 22.4 3.2 12.8-9.6 22.4-22.4 22.4z m73.6-134.4h-176c-12.8 0-22.4-9.6-22.4-22.4 0-12.8 9.6-22.4 22.4-22.4h176c12.8 0 22.4 9.6 22.4 22.4 3.2 9.6-6.4 22.4-22.4 22.4z\" fill=\"#1296db\" ></path><path d=\"M512 0C230.4 0 0 230.4 0 512s230.4 512 512 512c67.2 0 134.4-12.8 195.2-38.4 6.4-3.2 16-6.4 22.4-9.6 6.4-3.2 12.8-9.6 16-16 3.2-6.4 3.2-16 0-22.4-6.4-16-25.6-19.2-38.4-12.8l-19.2 9.6c-54.4 22.4-112 35.2-172.8 35.2C262.4 966.4 57.6 761.6 57.6 512S262.4 57.6 512 57.6 966.4 262.4 966.4 512c0 48-6.4 99.2-22.4 144-6.4 16-9.6 28.8-16 44.8-3.2 6.4-3.2 16 0 22.4 3.2 6.4 9.6 12.8 16 16 6.4 3.2 16 3.2 22.4 0 6.4-3.2 12.8-9.6 16-16 6.4-16 12.8-32 19.2-51.2 19.2-51.2 25.6-108.8 25.6-163.2C1024 230.4 793.6 0 512 0z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-setting-1\" viewBox=\"0 0 1024 1024\"><path d=\"M586.587654 972.348501l0-299.434215c0-3.973192 0-7.585185 0-10.835979 0.361199-11.197178 0.361199-21.310758-2.528395-24.200353-1.444797-1.444797-5.05679-3.250794-14.447972-3.250794l-114.861376 0c-9.391182 0-13.003175 1.805996-14.447972 3.250794-3.250794 3.250794-2.889594 13.003175-2.528395 24.200353 0 3.250794 0 6.862787 0 10.835979l0 299.434215L282.096649 972.348501c-65.015873 0-118.112169-58.514286-118.112169-130.392945l0-263.314286L123.891358 578.64127c-31.424339 0-55.985891-15.17037-67.905467-41.17672-9.391182-21.310758-7.223986-57.069489 26.728748-92.828219l341.694533-355.420106c23.116755-24.200353 54.179894-37.203527 87.410229-37.203527 33.230335 0 64.293474 13.364374 87.410229 37.203527l341.694533 355.420106c34.313933 35.397531 36.119929 71.156261 26.728748 92.467019-5.417989 12.280776-23.116755 41.17672-67.905467 41.17672l-40.093122 0L859.654321 841.594356c0 71.87866-53.096296 130.392945-118.112169 130.392945L586.587654 971.987302zM741.903351 895.77425c24.922751 0 41.537919-28.173545 41.537919-54.179894L783.44127 502.067019l104.747795 0c-1.083598-1.805996-2.167196-3.611993-3.611993-4.695591l-341.694533-355.058907c-8.668783-9.029982-19.504762-14.086772-29.979541-14.086772 0 0-0.361199 0-0.361199 0-11.197178 0-22.755556 5.05679-31.424339 14.086772l-341.694533 355.058907c-1.444797 1.444797-2.528395 2.889594-3.611993 4.695591l104.747795 0L240.55873 841.594356c0 26.367549 16.615168 54.179894 41.537919 54.179894L361.199295 895.77425l0-223.221164c0-77.296649 30.70194-114.500176 93.189418-114.500176l114.861376 0c62.848677 0 93.550617 37.564727 93.550617 114.500176L662.800705 895.77425 741.903351 895.77425z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-setting-2\" viewBox=\"0 0 1024 1024\"><path d=\"M437.996 697.519 437.996 697.519 437.996 586.722 327.195 586.722c-61.187 0-110.797 49.61-110.797 110.797 0 61.195 49.61 110.797 110.797 110.797C388.387 808.316 437.996 758.714 437.996 697.519L437.996 697.519 437.996 697.519 437.996 697.519zM493.395 697.519c0 91.789-74.411 166.195-166.2 166.195C235.41 863.714 161 789.308 161 697.519c0-91.789 74.41-166.195 166.195-166.195l110.801 0 55.399 0 0 55.398L493.395 697.519 493.395 697.519 493.395 697.519zM585.723 697.519 585.723 586.722 696.52 586.722c61.195 0 110.796 49.61 110.796 110.797 0 61.195-49.601 110.797-110.796 110.797C635.332 808.316 585.723 758.714 585.723 697.519L585.723 697.519 585.723 697.519zM530.324 697.519c0 91.789 74.41 166.195 166.196 166.195 91.789 0 166.199-74.406 166.199-166.195 0-91.789-74.41-166.195-166.199-166.195L585.723 531.324l-55.399 0 0 55.398L530.324 697.519 530.324 697.519 530.324 697.519zM437.996 328.195l0 110.801L327.195 438.996c-61.187 0-110.797-49.61-110.797-110.801 0-61.188 49.61-110.797 110.797-110.797C388.387 217.398 437.996 267.007 437.996 328.195L437.996 328.195 437.996 328.195zM493.395 328.195c0-91.785-74.411-166.195-166.2-166.195C235.41 162 161 236.41 161 328.195c0 91.789 74.41 166.199 166.195 166.199l110.801 0 55.399 0 0-55.398L493.395 328.195 493.395 328.195 493.395 328.195zM585.723 328.195l0 110.801L696.52 438.996c61.195 0 110.796-49.61 110.796-110.801 0-61.188-49.601-110.797-110.796-110.797C635.332 217.398 585.723 267.007 585.723 328.195L585.723 328.195 585.723 328.195zM530.324 328.195 530.324 328.195C530.324 236.41 604.734 162 696.52 162c91.789 0 166.199 74.41 166.199 166.195 0 91.789-74.41 166.199-166.199 166.199L585.723 494.394l-55.399 0 0-55.398L530.324 328.195 530.324 328.195 530.324 328.195zM530.324 328.195\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-setting-3\" viewBox=\"0 0 1024 1024\"><path d=\"M827.5 777.4c60.5-71.8 97-164.4 97-265.4 0-227.4-185.1-412.5-412.5-412.5S99.5 284.6 99.5 512 284.6 924.5 512 924.5c121 0 230-52.4 305.5-135.6l2.7 1.5 7.3-13zM512 361.2c28.7 0 57.2-1.8 85.4-5.3 10.1 47.5 16 97.1 17.2 147.9H415.5c1.2-50.5 7-99.9 17-147.2 26.2 3.1 52.8 4.6 79.5 4.6z m-70.1-43.8C458.8 255 483.4 197 515 146c31.5 50.8 56 108.5 72.9 170.7-25.1 2.9-50.4 4.4-75.9 4.4-23.6 0-46.9-1.2-70.1-3.7z m212.8 186.4c-1.2-52.7-7.2-104.4-17.6-154 61.3-11.3 120.3-30.9 174.9-58.3 44 59.7 70.7 133 72.4 212.3H654.7z m-279.3 0H139.7c1.7-79.3 28.4-152.6 72.4-212.3 56.4 28.3 117.3 48.3 180.7 59.4-10.4 49.2-16.2 100.5-17.4 152.9z m0 40.1c1.1 46.8 6 93 14.5 137.8-60.7 10.9-119.2 30-173.4 56.7-42.1-54.8-69.3-121.7-75.5-194.4h234.4z m40.1 0h199.1c-1.1 45.1-5.9 89.6-14.2 132.5-29.1-3.7-58.6-5.7-88.4-5.7-27.7 0-55.2 1.7-82.4 4.9-8.3-42.6-13-86.8-14.1-131.7z m239.2 0H883c-6.2 72.7-33.4 139.6-75.5 194.4-52.5-25.8-108.9-44.5-167.5-55.6 8.6-45 13.6-91.6 14.7-138.8z m131-284.2c-49.5 23.9-102.7 41-157.9 51-16-60.5-38.9-117.3-68.1-168.1 88.9 11.5 168.1 54.3 226 117.1zM470.8 141.9c-29.6 51.4-52.8 108.7-68.9 169.9-57.3-9.8-112.4-27.3-163.7-52.1 59.4-64.3 141.1-107.7 232.6-117.8zM243.5 769.8c48.7-22.9 100.8-39.3 154.8-48.9 13.8 56.8 33.5 110.6 58.6 159.4-83.2-12.4-157.6-52.4-213.4-110.5z m261.2 114.5c-29-50.7-51.3-108.1-66.5-169.2 24.4-2.8 49-4.2 73.8-4.2 26.8 0 53.5 1.6 79.7 4.9-15.1 60.8-37.5 117.9-66.3 168.4-4.5 0.2-8.9 0.2-13.4 0.2-2.4 0-4.9 0-7.3-0.1z m68.9-5c24.7-48.3 44.2-101.4 57.9-157.4 52 9.7 102.1 25.8 149 47.9-54.3 56.5-126.3 96-206.9 109.5z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-waf-4\" viewBox=\"0 0 1024 1024\"><path d=\"M136.533333 136.533333h546.133334v68.266667H136.533333zM136.533333 273.066667h546.133334v68.266666H136.533333zM136.533333 409.6h546.133334v68.266667H136.533333zM136.533333 546.133333h68.266667v68.266667H136.533333zM136.533333 682.666667h68.266667v68.266666H136.533333zM466.261333 840.362667h68.266667L546.133333 887.466667h43.690667l-68.266667-204.8H477.866667l-68.266667 204.8h43.008z m30.037334-105.130667a81.92 81.92 0 0 0 0-17.066667 84.650667 84.650667 0 0 0 0 17.749334l21.162666 68.266666H477.866667zM691.541333 887.466667a115.370667 115.370667 0 0 0 53.248-10.922667v-38.912a84.650667 84.650667 0 0 1-45.738666 12.970667 53.248 53.248 0 0 1-42.325334-17.749334 68.266667 68.266667 0 0 1-15.701333-47.104 68.266667 68.266667 0 0 1 16.384-48.469333 55.296 55.296 0 0 1 43.690667-18.432 79.872 79.872 0 0 1 43.008 11.605333v-40.96a121.514667 121.514667 0 0 0-45.738667-7.509333 92.842667 92.842667 0 0 0-68.266667 30.037333 108.544 108.544 0 0 0-27.989333 76.458667 101.034667 101.034667 0 0 0 24.576 68.266667 86.698667 86.698667 0 0 0 64.853333 30.72zM887.466667 851.968h-69.632V682.666667h-40.277334v204.8H887.466667v-35.498667z\" fill=\"#1296db\" ></path><path d=\"M819.2 546.133333V0H0v1024h1024V546.133333zM68.266667 955.733333V68.266667h682.666666v477.866666H273.066667v409.6z m887.466666 0H341.333333V614.4h614.4z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-waf-1\" viewBox=\"0 0 1024 1024\"><path d=\"M1017.6 889.6c-6.4-12.8-6.4-19.2-6.4-19.2l-121.6-236.8-6.4-6.4-6.4-6.4c-12.8-19.2-25.6-44.8-57.6-44.8h-128c-19.2 0-25.6 12.8-25.6 25.6s12.8 25.6 25.6 25.6h128s6.4 6.4 12.8 19.2l6.4 6.4 121.6 217.6s-6.4 6.4-12.8 6.4h-870.4s-6.4 0-6.4-6.4l128-217.6s6.4-6.4 6.4-12.8 6.4-6.4 0-6.4h115.2c19.2 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6h-115.2c-32 0-44.8 19.2-51.2 32l-6.4 6.4s-6.4 6.4-6.4 12.8l-134.4 230.4v12.8c0 38.4 32 64 70.4 64h870.4c32 6.4 70.4-12.8 70.4-51.2zM512 403.2c70.4 0 128-57.6 128-128s-57.6-128-128-128-128 57.6-128 128 57.6 128 128 128z m0-198.4c38.4 0 70.4 32 70.4 70.4-6.4 38.4-38.4 70.4-70.4 70.4-38.4 0-70.4-32-70.4-70.4 0-38.4 32-70.4 70.4-70.4z\" fill=\"#1296db\" ></path><path d=\"M441.6 684.8c25.6 32 44.8 44.8 70.4 44.8s44.8-12.8 70.4-44.8c70.4-76.8 160-185.6 192-307.2 25.6-83.2 6.4-172.8-44.8-249.6-51.2-64-134.4-102.4-217.6-102.4-96 0-172.8 38.4-230.4 102.4-51.2 64-64 160-32 249.6 25.6 83.2 89.6 192 192 307.2z m-115.2-524.8c32-51.2 96-76.8 179.2-76.8 64 0 128 32 166.4 83.2 44.8 57.6 64 134.4 44.8 198.4-32 115.2-115.2 217.6-185.6 281.6l-25.6 25.6s-6.4-6.4-6.4-12.8c-6.4-6.4-6.4-12.8-12.8-12.8l-12.8-12.8c-44.8-51.2-140.8-166.4-172.8-275.2-19.2-70.4-12.8-140.8 25.6-198.4z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-waf-2\" viewBox=\"0 0 1024 1024\"><path d=\"M458.752 38.4C360.96 38.4 281.6 117.76 281.6 215.552c0 23.04 12.8 79.872 98.304 201.216 29.184 41.472 58.88 78.848 78.848 102.912 19.968-24.064 49.664-61.44 78.848-102.912 85.504-121.344 98.304-178.176 98.304-201.216 0-97.792-79.36-177.152-177.152-177.152z m0 257.024c-50.176 0-91.136-40.96-91.136-91.136s40.96-91.136 91.136-91.136 91.136 40.96 91.136 91.136-40.96 91.136-91.136 91.136zM417.28 734.72H141.312v-286.72h132.096v-61.44H129.024c-27.136 0-49.152 22.016-49.152 49.152V747.52c0 27.136 22.016 49.152 49.152 49.152H419.84c-2.048-14.336-3.072-28.672-3.072-43.52 0-6.144 0-12.288 0.512-18.432zM837.632 435.712c0-27.136-22.016-49.152-49.152-49.152h-138.752v61.44h126.464V465.92c21.504 4.608 41.984 11.776 61.44 20.992v-51.2z\" fill=\"#1296db\" ></path><path d=\"M381.952 524.8H227.328v133.12h205.312c6.144-17.408 13.824-34.304 22.528-50.176l-73.216-82.944zM711.168 581.12c94.72 0 171.52 76.8 171.52 171.52s-76.8 171.52-171.52 171.52-171.52-76.8-171.52-171.52 76.8-171.52 171.52-171.52m0-61.44c-128.512 0-232.96 104.448-232.96 232.96s104.448 232.96 232.96 232.96 232.96-104.448 232.96-232.96-104.448-232.96-232.96-232.96z\" fill=\"#1296db\" ></path><path d=\"M526.40768 853.05856l334.90944-261.6064 37.82144 48.41984-334.90944 261.6064z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-waf-3\" viewBox=\"0 0 1024 1024\"><path d=\"M818.869708 702.656507h-185.924226a38.390594 38.390594 0 1 0 0 75.07494h185.924226a38.390594 38.390594 0 1 0 0-75.07494z\" fill=\"#1296db\" ></path><path d=\"M198.136454 272.113101h441.634022a42.656216 42.656216 0 0 0 45.499964-38.049344 49.139961 49.139961 0 0 0-45.499964-45.499964H198.07958a42.656216 42.656216 0 0 0-38.049345 45.499964 35.944971 35.944971 0 0 0 38.049345 38.049344z m152.311129 319.807869H198.136454a42.656216 42.656216 0 0 0-38.049344 45.499964 35.944971 35.944971 0 0 0 38.049344 38.049345h144.689885a35.944971 35.944971 0 0 0 38.049344-38.049345c0-22.749982-7.621244-45.499964-30.484975-45.499964zM548.429299 432.045473a35.944971 35.944971 0 0 0-38.049344-38.049344H205.473324a38.10622 38.10622 0 0 0 0 76.155564h304.565381c23.091232 7.564369 38.333719-15.299363 38.333719-38.163095z\" fill=\"#1296db\" ></path><path d=\"M806.072843 464.919197V116.446351a97.313047 97.313047 0 0 0-58.069328-94.412425A65.918072 65.918072 0 0 0 697.157305 0.250819H116.35027A105.73054 105.73054 0 0 0 29.21784 36.53704 109.199913 109.199913 0 0 0 0.154738 116.446351v675.21946a126.717399 126.717399 0 0 0 29.063102 87.13243 109.199913 109.199913 0 0 0 79.624936 29.063102h341.249727a33.214973 33.214973 0 0 0 36.286221-29.063102c21.783108 21.783108 36.286221 43.566215 58.069329 65.349323A280.507276 280.507276 0 0 0 740.723521 1024 283.521648 283.521648 0 0 0 1023.903919 740.819601a287.616645 287.616645 0 0 0-217.831076-275.900404z m138.20614 275.900404a202.929838 202.929838 0 0 1-203.555462 203.555463 230.571066 230.571066 0 0 1-145.201759-58.069329 205.375461 205.375461 0 0 1 145.201759-348.529721 207.764209 207.764209 0 0 1 203.555462 203.043587z m-486.678986 87.132431h-341.249727c-7.279994 0-21.783108-7.279994-29.063102-7.279994-7.507494-7.109369-7.507494-14.503113-7.507494-29.006227V116.446351a43.679965 43.679965 0 0 1 7.279994-29.063102 43.679965 43.679965 0 0 1 29.290602-7.507494h573.583916c7.279994 0 14.503113 7.279994 21.783108 7.279994a37.708095 37.708095 0 0 1 14.503113 29.063102V464.919197a284.374773 284.374773 0 0 0-268.62041 275.900404 513.410214 513.410214 0 0 0 14.616863 87.132431h-14.616863z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-xpack\" viewBox=\"0 0 1024 1024\"><path d=\"M971.527 392.078L839.149 169.594c-5.729-11.652-17.836-20.114-32.248-20.114H217.227v0.442c-12.134 0-24.007 5.962-30.647 17.394L54.201 389.097c-10.077 14.177-8.931 33.836 3.645 46.178l428.043 428.499 1.133 0.456c13.943 13.722 36.362 13.722 50.306 0l428.707-428.955h-0.222c11.43-11.431 14.412-29.267 5.714-43.197zM760.032 221.032l-85.743 140.632a47.989 47.989 0 0 0-3.789-0.164c-2.771 0-5.48 0.25-8.12 0.706l-98.988-141.174h196.64z m-132.05 166.793c-0.136 0.271-0.273 0.541-0.404 0.815H401.421a47.213 47.213 0 0 0-2.563-4.683l113.537-161.311 115.587 165.179zM460.736 221.032l-98.403 140.636a48.105 48.105 0 0 0-3.833-0.167c-2.775 0-5.49 0.251-8.135 0.708l-86.504-141.176h196.875z m-234.605 19.45l89.842 147.361c-0.132 0.265-0.267 0.528-0.394 0.796h-178.16l88.712-148.157zM154.799 431.63h161.928c6.511 11.994 18.04 20.861 31.767 23.805l98.051 267.719L154.799 431.63z m357.597 345.475L390.021 444.522a47.675 47.675 0 0 0 10.252-12.892h228.454a47.625 47.625 0 0 0 6.843 9.55L512.396 777.105z m64.939-53.951l97.977-266.896c15.992-1.609 29.644-11.149 36.962-24.628h156.364L577.335 723.154zM713.422 388.64a47.323 47.323 0 0 0-2.727-4.938l87.756-143.22 88.022 148.158H713.422z\" fill=\"\" ></path></symbol><symbol id=\"p-monitor-4\" viewBox=\"0 0 1024 1024\"><path d=\"M838.22 756.41H187.78a89.1 89.1 0 0 1-89.1-89.1V198.54a89.1 89.1 0 0 1 89.1-89.1h650.44a89.1 89.1 0 0 1 89.1 89.1v468.77a89.1 89.1 0 0 1-89.1 89.1zM852 671.35V194.5a9 9 0 0 0-9-8.95H183a9 9 0 0 0-9 8.95v476.85a9 9 0 0 0 9 8.94h660a9 9 0 0 0 9-8.94z\" fill=\"#1296db\" ></path><path d=\"M287.01 832.52m37.86 0l376.26 0q37.86 0 37.86 37.86l0 0.39q0 37.86-37.86 37.86l-376.26 0q-37.86 0-37.86-37.86l0-0.39q0-37.86 37.86-37.86Z\" fill=\"#1296db\" ></path><path d=\"M249.34 337.78m31.68 0l11.97 0q31.68 0 31.68 31.68l0 203.04q0 31.68-31.68 31.68l-11.97 0q-31.68 0-31.68-31.68l0-203.04q0-31.68 31.68-31.68Z\" fill=\"#1296db\" ></path><path d=\"M400 451.95m37.67 0l-0.01 0q37.67 0 37.67 37.67l0 76.89q0 37.67-37.67 37.67l0.01 0q-37.67 0-37.67-37.67l0-76.89q0-37.67 37.67-37.67Z\" fill=\"#1296db\" ></path><path d=\"M550.67 261.67m37.67 0l-0.01 0q37.67 0 37.67 37.67l0 267.17q0 37.67-37.67 37.67l0.01 0q-37.67 0-37.67-37.67l0-267.17q0-37.67 37.67-37.67Z\" fill=\"#1296db\" ></path><path d=\"M701.33 413.89m37.67 0l-0.01 0q37.67 0 37.67 37.67l0 114.94q0 37.67-37.67 37.67l0.01 0q-37.67 0-37.67-37.67l0-114.94q0-37.67 37.67-37.67Z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-monitor-2\" viewBox=\"0 0 1024 1024\"><path d=\"M1017.6 889.6c-6.4-12.8-6.4-19.2-6.4-19.2l-121.6-236.8-6.4-6.4-6.4-6.4c-12.8-19.2-25.6-44.8-57.6-44.8h-128c-19.2 0-25.6 12.8-25.6 25.6s12.8 25.6 25.6 25.6h128s6.4 6.4 12.8 19.2l6.4 6.4 121.6 217.6s-6.4 6.4-12.8 6.4h-870.4s-6.4 0-6.4-6.4l128-217.6s6.4-6.4 6.4-12.8 6.4-6.4 0-6.4h115.2c19.2 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6h-115.2c-32 0-44.8 19.2-51.2 32l-6.4 6.4s-6.4 6.4-6.4 12.8l-134.4 230.4v12.8c0 38.4 32 64 70.4 64h870.4c32 6.4 70.4-12.8 70.4-51.2zM512 403.2c70.4 0 128-57.6 128-128s-57.6-128-128-128-128 57.6-128 128 57.6 128 128 128z m0-198.4c38.4 0 70.4 32 70.4 70.4-6.4 38.4-38.4 70.4-70.4 70.4-38.4 0-70.4-32-70.4-70.4 0-38.4 32-70.4 70.4-70.4z\" fill=\"#1296db\" ></path><path d=\"M441.6 684.8c25.6 32 44.8 44.8 70.4 44.8s44.8-12.8 70.4-44.8c70.4-76.8 160-185.6 192-307.2 25.6-83.2 6.4-172.8-44.8-249.6-51.2-64-134.4-102.4-217.6-102.4-96 0-172.8 38.4-230.4 102.4-51.2 64-64 160-32 249.6 25.6 83.2 89.6 192 192 307.2z m-115.2-524.8c32-51.2 96-76.8 179.2-76.8 64 0 128 32 166.4 83.2 44.8 57.6 64 134.4 44.8 198.4-32 115.2-115.2 217.6-185.6 281.6l-25.6 25.6s-6.4-6.4-6.4-12.8c-6.4-6.4-6.4-12.8-12.8-12.8l-12.8-12.8c-44.8-51.2-140.8-166.4-172.8-275.2-19.2-70.4-12.8-140.8 25.6-198.4z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-monitor-1\" viewBox=\"0 0 1024 1024\"><path d=\"M896 192v512H128V192h768m64-64H64v640h896V128z\" fill=\"#1296db\" ></path><path d=\"M632.9 924.7L532 866.5V750h-40v116.5l-100.9 58.2 20 34.6L512 901.1l100.9 58.2zM206.1 654.1l-28.2-28.2L384 419.7l128.5 128 241.3-241.8 28.4 28.2-269.7 270.2-128.5-128z\" fill=\"#1296db\" ></path><path d=\"M788 448h-40V340H640v-40h148z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-monitor-3\" viewBox=\"0 0 1024 1024\"><path d=\"M781.824 343.466667 242.133333 343.466667c-11.797333 0-21.354667 9.557333-21.354667 21.354667 0 11.797333 9.557333 21.354667 21.354667 21.354667l539.690667 0c11.84 0 21.376-9.578667 21.376-21.354667C803.2 353.045333 793.664 343.466667 781.824 343.466667z\" fill=\"#1296db\" ></path><path d=\"M242.133333 533.376l441.578667 0c11.84 0 21.376-9.6 21.376-21.376s-9.536-21.354667-21.376-21.354667L242.133333 490.645333c-11.797333 0-21.354667 9.557333-21.354667 21.354667S230.336 533.376 242.133333 533.376z\" fill=\"#1296db\" ></path><path d=\"M781.824 637.781333 242.133333 637.781333c-11.797333 0-21.354667 9.557333-21.354667 21.376 0 11.84 9.557333 21.376 21.354667 21.376l539.690667 0c11.84 0 21.376-9.536 21.376-21.376C803.2 647.36 793.664 637.781333 781.824 637.781333z\" fill=\"#1296db\" ></path><path d=\"M683.712 785.002667 242.133333 785.002667c-11.797333 0-21.354667 9.557333-21.354667 21.376 0 11.84 9.557333 21.354667 21.354667 21.354667l441.578667 0c11.84 0 21.376-9.514667 21.376-21.354667C705.088 794.56 695.552 785.002667 683.712 785.002667z\" fill=\"#1296db\" ></path><path d=\"M781.824 122.666667l-35.008 0c-12.906667-28.864-41.770667-49.088-75.370667-49.088l-66.922667 0C594.794667 31.509333 556.949333 0 512 0c-45.034667 0-82.816 31.509333-92.544 73.578667l-66.922667 0c-33.621333 0-62.485333 20.224-75.413333 49.088l-23.765333 0c-95.765333 0-179.776 78.762667-179.776 168.554667L73.578667 855.466667c0 89.749333 84.010667 168.533333 179.776 168.533333l528.469333 0c92.906667 0 168.576-75.626667 168.554667-168.533333L950.378667 291.221333C950.4 198.272 874.773333 122.666667 781.824 122.666667zM352.533333 116.309333l85.888 0c11.776 0 21.354667-9.557333 21.354667-21.354667 0-28.778667 23.445333-52.224 52.224-52.224 28.8 0 52.202667 23.424 52.202667 52.224 0 11.797333 9.536 21.354667 21.333333 21.354667l85.909333 0c21.994667 0 39.957333 17.92 39.957333 39.978667 0 22.037333-17.962667 39.978667-39.957333 39.978667l-318.933333 0c-22.058667 0-39.978667-17.941333-39.978667-39.978667C312.554667 134.229333 330.517333 116.309333 352.533333 116.309333zM907.648 855.466667c0 69.376-56.448 125.802667-125.802667 125.802667L253.354667 981.269333c-73.002667 0-137.045333-58.794667-137.045333-125.802667L116.309333 291.221333c0-67.029333 64.042667-125.824 137.045333-125.824l17.386667 0c4.608 41.28 39.317333 73.6 81.770667 73.6l318.933333 0c42.453333 0 77.141333-32.32 81.749333-73.6l28.629333 0c69.376 0 125.802667 56.448 125.802667 125.824L907.626667 855.466667z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-m-ios\" viewBox=\"0 0 1024 1024\"><path d=\"M849.124134 704.896288c-1.040702 3.157923-17.300015 59.872622-57.250912 118.190843-34.577516 50.305733-70.331835 101.018741-126.801964 101.909018-55.532781 0.976234-73.303516-33.134655-136.707568-33.134655-63.323211 0-83.23061 32.244378-135.712915 34.110889-54.254671 2.220574-96.003518-54.951543-130.712017-105.011682-70.934562-102.549607-125.552507-290.600541-52.30118-416.625816 36.040844-63.055105 100.821243-103.135962 171.364903-104.230899 53.160757-1.004887 103.739712 36.012192 136.028093 36.012192 33.171494 0 94.357018-44.791136 158.90615-38.089503 27.02654 1.151219 102.622262 11.298324 151.328567 81.891102-3.832282 2.607384-90.452081 53.724599-89.487104 157.76107C739.079832 663.275355 847.952448 704.467523 849.124134 704.896288M633.69669 230.749408c29.107945-35.506678 48.235584-84.314291 43.202964-132.785236-41.560558 1.630127-92.196819 27.600615-122.291231 62.896492-26.609031 30.794353-50.062186 80.362282-43.521213 128.270409C557.264926 291.935955 604.745311 264.949324 633.69669 230.749408\" fill=\"#272636\" ></path></symbol><symbol id=\"p-m-pc\" viewBox=\"0 0 1024 1024\"><path d=\"M597.3 170.3c0-47.1 38.2-85.3 85.3-85.3h256c47.1 0 85.3 38.2 85.3 85.3V853c0 47.1-38.2 85.3-85.3 85.3h-256c-47.1 0-85.3-38.2-85.3-85.3V170.3z\" fill=\"#666666\" ></path><path d=\"M640 170.3c0-23.6 19.1-42.7 42.7-42.7h256c23.6 0 42.7 19.1 42.7 42.7V853c0 23.6-19.1 42.7-42.7 42.7h-256c-23.6 0-42.7-19.1-42.7-42.7V170.3z\" fill=\"#F5F5F5\" ></path><path d=\"M682.7 170.3h256v85.3h-256V170.3z m0 170.7h256v85.3h-256V341z\" fill=\"#CCCCCC\" ></path><path d=\"M384 853v-85.3H85.3c-47.1 0-85.3-38.2-85.3-85.3V255.7c0-47.1 38.2-85.3 85.3-85.3H768c47.1 0 85.3 38.2 85.3 85.3v426.7c0 47.1-38.2 85.3-85.3 85.3H469.3V853H576c11.8 0 21.4 9.6 21.4 21.3 0 11.8-9.6 21.3-21.4 21.3H277.4c-11.8 0-21.4-9.6-21.4-21.3 0-11.8 9.6-21.3 21.4-21.3H384z\" fill=\"#666666\" ></path><path d=\"M42.7 255.7c0-23.6 19.1-42.7 42.7-42.7H768c23.6 0 42.7 19.1 42.7 42.7v426.7c0 23.6-19.1 42.7-42.7 42.7H85.3c-23.6 0-42.7-19.1-42.7-42.7V255.7h0.1z\" fill=\"#E6FFFA\" ></path><path d=\"M85.3 298.3H768v341.4H85.3z\" fill=\"#E1EEFF\" ></path></symbol><symbol id=\"p-m-theworld\" viewBox=\"0 0 1024 1024\"><path d=\"M499.2 42.666667h21.333333l64 6.4c89.6 12.8 166.4 49.066667 232.533334 106.666666 44.8 36.266667 78.933333 81.066667 106.666666 130.133334 27.733333 51.2 46.933333 108.8 51.2 168.533333 2.133333 12.8 2.133333 27.733333 4.266667 40.533333v32c-2.133333 17.066667-4.266667 32-4.266667 49.066667-4.266667 44.8-17.066667 89.6-36.266666 128-14.933333 29.866667-29.866667 57.6-49.066667 83.2-21.333333 32-49.066667 61.866667-81.066667 87.466667-42.666667 34.133333-87.466667 59.733333-136.533333 76.8-42.666667 14.933333-87.466667 25.6-130.133333 27.733333-6.4 0-14.933333 2.133333-19.2 2.133333h-14.933334c-6.4 0-14.933333 0-19.2-2.133333-29.866667-4.266667-61.866667-6.4-91.733333-14.933333-59.733333-14.933333-110.933333-40.533333-160-76.8-32-25.6-59.733333-51.2-83.2-81.066667-25.6-29.866667-44.8-64-61.866667-100.266667-19.2-44.8-32-89.6-40.533333-138.666666-2.133333-17.066667-2.133333-32-4.266667-49.066667v-14.933333c0-4.266667 2.133333-6.4 2.133334-12.8 2.133333-27.733333 4.266667-57.6 10.666666-83.2 12.8-61.866667 36.266667-115.2 74.666667-166.4 29.866667-42.666667 66.133333-78.933333 110.933333-110.933334 42.666667-29.866667 87.466667-51.2 136.533334-66.133333 34.133333-14.933333 74.666667-21.333333 113.066666-21.333333-2.133333 2.133333 0 0 4.266667 0z m-145.066667 840.533333c36.266667 17.066667 74.666667 25.6 110.933334 29.866667 34.133333 4.266667 68.266667 2.133333 100.266666-2.133334 36.266667-4.266667 72.533333-14.933333 104.533334-29.866666 44.8-19.2 83.2-46.933333 119.466666-78.933334 4.266667-4.266667 6.4-10.666667 2.133334-17.066666-2.133333-4.266667-6.4-12.8-10.666667-17.066667-4.266667-6.4-2.133333-12.8 0-19.2 4.266667-14.933333 12.8-27.733333 14.933333-42.666667 4.266667-17.066667 0-34.133333-12.8-49.066666-6.4-6.4-14.933333-17.066667-19.2-29.866667-4.266667-10.666667-10.666667-21.333333-12.8-34.133333-2.133333 2.133333-4.266667 4.266667-6.4 4.266666-14.933333 10.666667-27.733333 19.2-42.666666 27.733334-19.2 10.666667-40.533333 10.666667-57.6-6.4-4.266667-4.266667-12.8-10.666667-17.066667-10.666667-14.933333-4.266667-21.333333-14.933333-29.866667-27.733333-2.133333-4.266667-6.4-12.8-10.666666-17.066667-21.333333-42.666667-12.8-68.266667 21.333333-96 6.4-4.266667 10.666667-10.666667 10.666667-19.2 2.133333-12.8 4.266667-27.733333 10.666666-40.533333 2.133333-4.266667 10.666667-12.8 17.066667-14.933334 17.066667-2.133333 27.733333-10.666667 42.666667-17.066666 10.666667-4.266667 21.333333-10.666667 34.133333-12.8 21.333333-4.266667 40.533333 10.666667 57.6 25.6 4.266667 4.266667 6.4 4.266667 12.8 2.133333 12.8-6.4 27.733333-12.8 40.533333-17.066667 14.933333-4.266667 29.866667-6.4 46.933334-12.8v-2.133333c-14.933333-36.266667-36.266667-72.533333-64-100.266667-2.133333-2.133333-4.266667-2.133333-10.666667-2.133333s-10.666667 2.133333-14.933333 4.266667c-14.933333 2.133333-17.066667 4.266667-10.666667 19.2 4.266667 10.666667 10.666667 19.2 12.8 29.866666 2.133333 4.266667 2.133333 12.8 2.133333 17.066667-4.266667 0-12.8 2.133333-17.066666 0-10.666667-4.266667-19.2-12.8-29.866667-19.2-17.066667-14.933333-42.666667-14.933333-57.6 4.266667-6.4 10.666667-12.8 19.2-19.2 29.866666-10.666667 12.8-19.2 25.6-29.866667 36.266667 0 0-2.133333 0-2.133333-2.133333 2.133333-21.333333-12.8-40.533333-27.733333-57.6 6.4-2.133333 12.8-2.133333 17.066666-2.133334h19.2c4.266667 0 10.666667-2.133333 12.8-4.266666-2.133333-4.266667-2.133333-6.4-4.266666-12.8-17.066667-17.066667-10.666667-21.333333 4.266666-34.133334 2.133333 0 2.133333-2.133333 4.266667-2.133333 10.666667-6.4 21.333333-14.933333 21.333333-27.733333 2.133333-19.2 12.8-27.733333 29.866667-29.866667l27.733333-4.266667v-4.266666l-53.333333-29.866667c6.4 19.2 6.4 29.866667-10.666667 42.666667-12.8 10.666667-25.6 14.933333-40.533333 19.2-32 10.666667-40.533333 2.133333-34.133333-29.866667 6.4-17.066667 10.666667-32 12.8-49.066667 2.133333-6.4 0-12.8-6.4-14.933333-44.8-10.666667-91.733333-14.933333-138.666667-10.666667-21.333333 2.133333-21.333333 0-27.733333 29.866667 0 4.266667-2.133333 6.4-2.133334 12.8-4.266667-2.133333-10.666667-2.133333-14.933333-4.266667-19.2-19.2-32-14.933333-42.666667-2.133333-10.666667 14.933333-17.066667 32-27.733333 49.066667 12.8 6.4 17.066667 21.333333 14.933333 40.533333s-17.066667 12.8-32 14.933333c4.266667 2.133333 10.666667 4.266667 14.933334 6.4 10.666667 6.4 12.8 10.666667 2.133333 19.2l-14.933333 14.933334c-4.266667 4.266667-6.4 4.266667-12.8 2.133333-12.8-4.266667-25.6-6.4-36.266667-10.666667-17.066667-2.133333-21.333333 0-25.6 17.066667-2.133333 14.933333-10.666667 21.333333-21.333333 25.6-10.666667 2.133333-21.333333 2.133333-32 2.133333-21.333333 0-27.733333 4.266667-27.733334 27.733334 0 6.4 0 17.066667-2.133333 25.6 0 6.4-2.133333 14.933333-12.8 14.933333s-14.933333-4.266667-17.066667-14.933333c-2.133333-10.666667-4.266667-21.333333-10.666666-32-2.133333-2.133333-4.266667-6.4-10.666667-12.8-4.266667 14.933333-10.666667 27.733333-12.8 36.266666-4.266667 14.933333-6.4 25.6 0 40.533334 12.8 21.333333 17.066667 49.066667 25.6 74.666666 4.266667 14.933333 12.8 27.733333 19.2 40.533334 2.133333 0 4.266667 0 4.266667-2.133334 2.133333 0 2.133333-2.133333 4.266666-2.133333 10.666667-10.666667 19.2-12.8 34.133334-4.266667 10.666667 4.266667 21.333333 10.666667 32 10.666667 19.2 0 32 6.4 40.533333 25.6 2.133333 2.133333 4.266667 6.4 6.4 6.4 25.6 2.133333 36.266667 19.2 51.2 36.266667 25.6 29.866667 51.2 51.2 89.6 64 12.8 4.266667 25.6 12.8 34.133333 19.2 10.666667 10.666667 6.4 25.6-4.266666 32-10.666667 4.266667-17.066667 10.666667-19.2 21.333333-2.133333 12.8-2.133333 27.733333-4.266667 40.533333 0 17.066667-2.133333 19.2-21.333333 21.333334-10.666667 0-21.333333 2.133333-27.733334 6.4-10.666667 21.333333-21.333333 42.666667-32 66.133333zM110.933333 467.2c-10.666667 36.266667 2.133333 136.533333 21.333334 185.6 29.866667 76.8 81.066667 138.666667 106.666666 153.6-6.4-17.066667-17.066667-32-25.6-49.066667-2.133333-6.4-6.4-12.8-14.933333-14.933333-6.4-2.133333-12.8-10.666667-17.066667-14.933333-14.933333-21.333333-21.333333-46.933333-25.6-74.666667-2.133333-25.6-6.4-49.066667-2.133333-74.666667 2.133333-17.066667-2.133333-27.733333-12.8-36.266666 0 2.133333-2.133333 2.133333-2.133333 0-4.266667-4.266667-4.266667-6.4-2.133334-14.933334 2.133333-4.266667 4.266667-12.8 4.266667-19.2-2.133333-17.066667-14.933333-29.866667-29.866667-40.533333z\" fill=\"#040000\" ></path></symbol><symbol id=\"p-m-android\" viewBox=\"0 0 1024 1024\"><path d=\"M157.947546 735.493119c-45.735653 0-82.696442-37.226694-82.696442-82.696443v-253.141521c0-45.469748 36.960789-82.696442 82.696442-82.696443 22.070112 0 42.810699 8.508959 58.499091 24.197351 15.688393 15.688393 24.197351 36.428979 24.197352 58.499092v253.141521c0 22.070112-8.508959 42.810699-24.197352 58.499091-15.688393 15.422488-36.428979 24.197351-58.499091 24.197352\" fill=\"#FFFFFF\" ></path><path d=\"M157.947546 339.028824c-33.504025 0-60.360426 27.122306-60.360426 60.360426v253.141521c0 33.504025 27.122306 60.360426 60.360426 60.626331 33.504025 0 60.360426-27.122306 60.360426-60.626331v-253.141521c0-33.23812-27.122306-60.360426-60.360426-60.360426M806.489743 351.526357H217.244352v-22.336017c0-91.205401 49.724227-176.029083 131.091145-226.285121L316.692807 45.469748c-3.988574-6.647624-4.786289-15.422488-2.65905-23.133731 2.393145-7.711244 7.711244-14.624773 14.890678-18.347443C333.44482 1.329525 338.497014 0 343.549208 0c11.168008 0 21.538302 6.115814 26.856401 15.954298l33.23812 60.360425c34.30174-12.497533 70.464814-18.879252 107.957414-18.879252 38.024409 0 74.453389 6.381719 108.755128 19.145157L653.328486 15.954298c5.318099-9.838484 15.688393-15.954298 26.856401-15.954298 5.052194 0 10.104388 1.329525 14.624773 3.722669 7.445339 3.988574 12.763438 10.636198 14.890678 18.613348 2.393145 7.977149 1.329525 16.220203-2.65905 23.399636l-31.64269 57.701376C756.765515 153.693067 806.489743 238.516749 806.489743 329.72215v21.804207z\" fill=\"#FFFFFF\" ></path><path d=\"M645.351337 111.680083l42.278889-77.112438c2.12724-3.988574 0.797715-9.306674-3.19086-11.433914-4.254479-2.12724-9.306674-0.797715-11.433913 3.456765l-42.544793 77.910153c-35.89717-15.954298-76.048818-24.995066-118.327708-24.729161-42.278889 0-82.430538 8.774864-118.061802 24.729161l-42.544793-77.644248c-2.12724-3.988574-7.445339-5.584004-11.433913-3.456765s-5.584004 7.445339-3.456765 11.433914l42.278889 77.112438C295.95222 154.756687 239.846274 236.123604 239.846274 329.72215h544.573357c-0.265905-93.864451-56.105947-175.231368-139.068294-218.042067m-257.396001 119.125422c-12.497533 0-22.867827-10.104388-22.867826-22.867826 0-12.497533 10.104388-22.867827 22.867826-22.867827 12.497533 0 22.867827 10.370293 22.867827 22.867827 0 12.497533-10.104388 22.867827-22.867827 22.867826m248.089328 0c-12.497533 0-22.867827-10.104388-22.867827-22.867826 0-12.497533 10.104388-22.867827 22.867827-22.867827 12.497533 0 22.867827 10.370293 22.867826 22.867827 0 12.497533-10.370293 22.867827-22.867826 22.867826\" fill=\"#FFFFFF\" ></path><path d=\"M410.823163 1024c-45.469748 0-82.696442-36.960789-82.696443-82.696442v-111.680084h-21.804206c-23.133731 0-44.937938-9.040769-61.424046-25.260971-16.486108-16.486108-25.526876-38.024409-25.260971-61.424045V328.392625H804.894313v414.545833c0 47.862893-38.822124 86.685017-86.685017 86.685016h-21.804206v111.680084c0 45.469748-36.960789 82.696442-82.696443 82.696442-22.070112 0-42.810699-8.508959-58.499091-24.197351-15.688393-15.688393-24.197351-36.428979-24.197351-58.499091v-111.680084h-37.226695v111.680084c-0.265905 45.469748-37.492599 82.696442-82.962347 82.696442\" fill=\"#FFFFFF\" ></path><path d=\"M241.973513 742.672553c0 35.631265 28.717736 64.614905 64.614906 64.614905h43.874318V941.303558c0 33.23812 27.122306 60.360426 60.360426 60.360425 33.504025 0 60.360426-27.122306 60.360426-60.62633v-134.0161h81.632822v134.0161c0 33.23812 27.122306 60.360426 60.360426 60.360425 33.504025 0 60.360426-27.122306 60.360426-60.360425v-134.0161h44.140223c35.631265 0 64.614905-28.983641 64.614906-64.614905V349.930927H241.707608l0.265905 392.741626zM866.052454 735.227214c-45.469748 0-82.696442-36.960789-82.696443-82.696443v-253.141521c0-45.735653 36.960789-82.696442 82.696443-82.696443 45.735653 0 82.696442 36.960789 82.696442 82.696443v253.141521c0 45.735653-36.960789 82.696442-82.696442 82.696443\" fill=\"#FFFFFF\" ></path><path d=\"M866.052454 339.028824c-33.504025 0-60.360426 27.122306-60.360426 60.360426v253.141521c0 33.504025 27.122306 60.360426 60.360426 60.360426 33.504025 0 60.360426-27.122306 60.360426-60.360426v-253.141521c0-33.23812-26.856401-60.360426-60.360426-60.360426\" fill=\"#FFFFFF\" ></path><path d=\"M157.947546 339.028824c-33.504025 0-60.360426 27.122306-60.360426 60.360426v253.141521c0 33.504025 27.122306 60.360426 60.360426 60.360426 33.504025 0 60.360426-27.122306 60.360426-60.360426v-253.141521c0-33.23812-27.122306-60.360426-60.360426-60.360426M645.351337 111.680083l42.278889-77.112438c2.12724-3.988574 0.797715-9.040769-3.19086-11.433914-3.988574-2.12724-9.306674-0.797715-11.433913 3.456765l-42.544793 77.910153c-35.89717-15.954298-76.048818-24.995066-118.593612-24.995066-42.278889 0-82.430538 8.774864-118.061803 24.729161l-42.544793-77.644248c-2.12724-3.988574-7.445339-5.584004-11.433913-3.456765s-5.584004 7.445339-3.456765 11.433914l42.278889 77.112438c-82.962347 42.810699-139.068294 124.177616-139.068294 217.776162h544.573357c0-93.598546-55.840042-174.965464-138.802389-217.776162m-257.396001 119.125422c-12.497533 0-22.867827-10.104388-22.867826-22.867826 0-12.497533 10.104388-22.867827 22.867826-22.867827 12.497533 0 22.867827 10.370293 22.867827 22.867827 0 12.497533-10.104388 22.867827-22.867827 22.867826m248.089328 0c-12.497533 0-22.867827-10.104388-22.867827-22.867826 0-12.497533 10.104388-22.867827 22.867827-22.867827 12.497533 0 22.867827 10.370293 22.867826 22.867827 0 12.497533-10.370293 22.867827-22.867826 22.867826m-394.337056 119.657232v392.475721c0 35.631265 28.717736 64.614905 64.614906 64.614905h43.874318v134.016099c0 33.23812 27.122306 60.360426 60.360426 60.360426 33.504025 0 60.360426-27.122306 60.360426-60.62633v-134.0161h81.632822V941.303558c0 33.23812 27.122306 60.360426 60.360426 60.360425 33.504025 0 60.360426-27.122306 60.360426-60.360425v-134.0161h44.140223c35.631265 0 64.614905-28.717736 64.614906-64.614905V350.196832l-540.318879 0.265905z m684.971177 48.926513c0-33.504025-27.122306-60.360426-60.360426-60.360426-33.504025 0-60.360426 27.122306-60.360426 60.360426v253.141521c0 33.504025 27.122306 60.360426 60.360426 60.360426 33.504025 0 60.360426-27.122306 60.360426-60.360426v-253.141521z\" fill=\"#A4C439\" ></path></symbol><symbol id=\"p-m-tencent\" viewBox=\"0 0 1026 1024\"><path d=\"M471.381 3.009c5.015 0 8.024-3.009 13.038-3.009h57.168c10.03 0 18.053 3.009 29.085 3.009 5.015 0 8.023 3.009 10.03 3.009v5.014h-8.024c-3.009 0-3.009 0-5.015 3.01h-10.03c-3.008 3.008-8.023 3.008-13.038 3.008-3.008 0-3.008 0-5.014 3.009h-5.015c-3.009 0-8.023 3.009-10.03 3.009-3.008 3.008-8.023 3.008-10.029 3.008-3.008 3.01-5.014 3.01-8.023 3.01-3.009 0-5.015 3.008-8.024 3.008-5.014 3.009-10.029 5.015-16.047 5.015-31.09 13.038-63.185 26.076-89.261 44.129-10.03 5.015-18.053 13.038-26.077 18.053-10.029 8.023-18.052 13.038-24.07 21.062l-26.076 26.076c-10.03 13.038-21.062 24.07-31.092 39.115-16.047 24.07-29.085 47.138-39.114 71.208-3.009 5.015-5.015 13.038-8.024 18.053-8.023 24.07-16.047 50.147-21.061 76.223 0 5.015-3.01 13.039-3.01 18.053s-3.008 8.024-3.008 13.038-3.009 10.03-3.009 16.048c0 5.014-3.009 13.038-3.009 18.052v66.194c3.01 8.024 0 18.053 3.01 26.077v24.07c0 3.01 0 8.024 3.008 10.03 3.009 8.023 0 16.047 3.009 24.07 0 5.015 0 10.03 3.009 16.047v34.1c-3.01 21.062-10.03 39.115-24.07 55.162-16.048 18.053-39.116 29.085-63.186 31.09h-24.07c-13.039-3.008-24.071-5.014-34.1-13.037-10.03-5.015-18.053-13.039-26.077-21.062-8.023-10.03-16.047-21.062-18.053-34.1-10.03-37.109-18.053-73.215-18.053-110.323-3.008-8.024 0-16.047 0-21.062v-18.053c3.01-8.023 0-18.053 0-26.076 3.01-10.03 3.01-21.062 5.015-34.1 0-5.015 3.009-10.03 3.009-16.047C34.1 327.96 68.2 251.738 120.353 188.552c0-5.014 5.014-10.029 10.029-18.052 10.03-13.039 21.062-24.071 34.1-34.1 10.03-8.024 18.053-18.053 29.085-26.077 13.038-10.03 29.085-24.07 44.13-31.09 16.046-10.03 31.09-18.054 47.137-26.077 26.077-14.041 55.162-24.07 81.238-32.094 8.024-3.01 18.053-5.015 26.077-8.024 5.015 0 13.038-3.009 18.053-3.009 3.009 0 8.023-3.008 10.03-3.008 5.014 0 10.029-3.01 16.046-3.01 5.015 0 10.03 0 16.047-3.008 0 2.006 19.056 2.006 19.056 2.006z\" fill=\"#FFCD17\" ></path><path d=\"M594.742 21.062c5.015 0 10.03-3.01 16.047-3.01h18.053c55.162 8.024 107.315 29.086 154.453 58.171l23.067 15.044c39.115 26.077 71.209 58.17 102.3 94.277 21.062 26.076 39.115 52.152 55.162 84.246s29.085 63.186 39.114 97.285c5.015 18.053 10.03 39.115 13.038 60.177 3.01 5.014 0 13.038 3.01 21.061 3.008 5.015 0 13.039 3.008 21.062 3.009 18.053 3.009 37.109 3.009 55.162 0 10.03 0 21.061-3.009 31.09 0 5.016 0 13.039-3.009 18.054-3.009 8.023 0 16.047-3.009 21.061-5.014 31.091-13.038 63.186-24.07 92.27-8.024 18.054-13.038 34.1-21.062 52.154-3.009 5.014-5.014 13.038-10.03 18.052v3.01l-24.07 39.114c-4.011 6.017-7.02 14.041-14.04 16.047-3.01-3.009-5.015-3.009-3.01-5.015 3.01-5.015 3.01-8.023 5.015-13.038 0-3.009 0-3.009 3.009-5.015 0-3.009 0-3.009 3.009-3.009v-3.008c0-3.01 3.009-8.024 3.009-10.03 5.014-13.038 8.023-26.076 10.029-39.114 0-5.015 3.009-13.039 5.015-18.053 0-8.024 3.008-13.038 3.008-18.053s3.01-10.03 3.01-16.047v-24.07c3.008-5.015 0-10.03 0-18.054v-37.108c0-5.015-3.01-13.038-3.01-18.053-3.008-26.077-10.029-50.147-18.052-73.215 0 0 0-3.008-3.01-3.008l-8.023-24.071c0-3.009-3.009-3.009-3.009-5.015-3.008-3.008-3.008-8.023-5.014-10.03 0-3.008-3.01-3.008-3.01-5.014-10.028-21.061-26.075-42.123-39.114-63.185-5.014-8.023-13.038-16.047-18.053-24.07-5.014-8.024-13.038-13.039-18.052-21.062-3.01-5.015-8.024-8.024-10.03-10.03-5.015-5.014-10.03-10.029-18.053-16.046-5.014-5.015-8.023-8.024-13.038-10.03l-63.185-47.138c-8.024-5.015-16.047-8.023-21.062-13.038-3.009 0-3.009-3.009-5.014-3.009-3.01 0-3.01-3.009-5.015-3.009-8.024-5.014-18.053-8.023-26.077-13.038-3.008 0-3.008-3.009-5.014-3.009-3.009 0-3.009-3.009-5.015-3.009-3.009 0-3.009-3.008-5.015-3.008-10.029-5.015-18.052-8.024-29.085-13.039-10.03-5.014-18.053-8.023-29.085-13.038-3.009 0-3.009-3.009-5.015-3.009-3.009 0-3.009-3.008-5.014-3.008-3.01-3.01-8.024-3.01-10.03-5.015h-3.009c-8.023-5.015-16.047-10.03-21.061-18.053-8.024-8.024-13.039-16.047-18.053-26.076s-8.024-24.071-8.024-37.11c0-5.014 3.01-10.029 3.01-16.046 5.014-21.062 13.037-39.115 29.084-52.153-1.003-14.041 15.044-22.065 31.091-27.08z\" fill=\"#40C33C\" ></path><path d=\"M481.41 293.86c5.015 0 13.039-3.008 18.053-3.008h34.1c8.024 0 13.038 3.009 21.062 3.009 5.015 0 10.03 3.009 16.047 3.009 10.03 3.009 24.07 8.023 34.1 13.038 8.023 5.015 13.038 8.023 21.061 13.038 16.048 13.038 26.077 29.085 37.11 47.138 5.014 13.038 10.029 26.077 13.037 39.115 0 5.015 3.01 13.038 3.01 18.053s3.008 13.038 3.008 18.053v24.07c0 3.009 3.009 5.015 3.009 8.024 5.015 8.023 10.03 16.047 16.047 26.076 10.03 24.07 21.062 44.13 26.076 68.2 3.01 5.015 0 13.038 0 21.062-3.009 3.008-5.014 5.014-8.023 5.014-13.038 0-26.077-5.014-34.1-16.047l-8.024-8.023c-5.014 5.014-8.023 10.03-10.029 18.053-5.015 10.029-13.038 21.061-18.053 34.1 5.015 5.014 13.038 5.014 18.053 8.023 8.023 5.015 13.038 10.03 18.053 18.053 0 5.015 0 13.038-5.015 16.047-8.023 8.023-18.053 13.038-31.09 16.047-10.03 3.009-21.063 5.015-34.1 5.015-13.039 3.008-26.077 0-42.124 0-10.03 0-18.053-3.01-29.085-8.024-10.03-3.009-21.062-8.023-34.1-8.023-10.03 3.008-21.062 8.023-31.091 10.029-8.024 3.009-16.047 3.009-24.071 5.015h-24.07c-8.024 0-16.047-3.01-21.062-3.01-13.038-3.008-26.077-5.014-37.109-10.029-5.015-5.014-13.038-8.023-16.047-16.047-3.009-3.008 0-8.023 3.009-10.029 5.015-10.03 16.047-18.053 26.076-21.062 3.01 0 5.015-3.008 5.015-5.014s-3.009-5.015-3.009-10.03c-5.015-10.03-13.038-21.061-18.053-34.1-3.009-3.008-3.009-5.014-5.014-8.023-5.015 0-5.015 3.009-8.024 5.015-8.023 8.023-18.053 16.047-31.091 16.047-3.009 0-8.023 0-10.03-5.015 0-5.015 3.01-13.038 3.01-18.053 5.014-29.085 16.046-58.17 31.09-84.247 3.01-5.014 8.024-8.023 10.03-13.038 3.009-3.009 0-8.024 0-10.03 0-8.023 3.009-13.038 3.009-21.061 3.009-8.024 0-16.047 3.009-21.062 3.008-13.038 5.014-29.085 10.029-42.123 5.015-13.038 13.038-24.07 18.053-37.109 5.015-5.015 8.023-10.03 13.038-16.047 5.015-5.015 10.03-10.03 16.047-13.038 8.024-5.015 16.047-8.024 24.07-13.038 5.015-3.01 10.03-5.015 18.053-5.015 7.021 1.003 12.036 3.009 17.05 1.003z\" fill=\"#3E97DA\" ></path><path d=\"M799.342 703.06c8.023-3.01 16.047 0 21.062 0 16.047 3.009 29.085 8.023 42.123 18.053 5.015 3.008 10.03 8.023 13.038 13.038 5.015 5.015 10.03 13.038 13.038 18.053 5.015 10.03 8.024 18.053 10.03 29.085v21.062c0 13.038-5.015 26.076-8.024 39.114-3.009 5.015-5.014 10.03-8.023 13.038-8.024 10.03-16.047 16.047-24.07 26.077-5.016 8.023-16.048 13.038-24.071 21.062s-16.047 13.038-26.077 21.061c-10.03 10.03-24.07 16.047-37.108 26.077-16.047 8.023-31.092 18.053-50.147 26.076-24.07 10.03-50.147 21.062-73.215 26.076-18.053 5.015-39.114 10.03-58.17 13.039-5.015 3.008-13.038 0-21.062 3.008s-13.038 0-21.062 3.01c-8.023 0-16.047 0-21.061 3.008-18.053 0-37.11 0-55.162-3.009-8.024-3.009-13.038 0-21.062-3.009-5.014-3.008-13.038 0-21.061-3.008-34.1-5.015-65.191-13.039-97.285-26.077-24.07-8.023-47.139-18.053-68.2-31.09-5.015-5.016-13.038-8.024-18.053-10.03-26.077-16.047-50.147-34.1-73.215-52.153-5.014-5.015-10.029-10.03-18.053-16.047-5.014-5.015-13.038-10.03-16.047-16.047-13.038-13.038-26.076-29.085-39.114-44.13-18.053-24.07-31.091-47.137-44.13-73.214-3.008-5.015-8.023-13.038-8.023-18.053l-3.009-3.009c0-3.008-3.009-5.014-3.009-8.023l5.015-3.009c5.015 3.009 8.024 8.024 13.038 13.038 5.015 8.024 10.03 10.03 16.047 18.053 8.024 3.009 13.039 8.024 18.053 13.038 8.024 5.015 13.038 13.039 24.07 21.062 5.015 3.009 10.03 8.024 16.048 13.038 21.061 16.047 42.123 31.091 65.19 42.124 8.024 5.014 16.048 8.023 26.077 13.038 8.024 3.009 18.053 5.015 26.077 10.03 10.029 3.008 21.061 8.023 31.09 8.023 5.015 3.009 13.039 3.009 18.054 5.015 5.014 0 10.029 3.008 16.047 3.008 5.014 0 10.029 3.01 16.047 3.01h29.085c8.023 3.008 16.047 0 24.07 0 5.015-3.01 13.039 0 18.053 0h13.038c8.024 0 16.047-3.01 21.062-3.01s10.03-3.008 16.047-3.008c10.03-3.01 24.07-5.015 34.1-8.024 16.047-3.009 31.091-8.024 44.13-13.038 10.029-3.009 18.052-8.024 29.085-10.03 21.061-8.023 42.123-18.052 60.176-29.085 8.023-5.014 16.047-8.023 24.07-13.038l24.07-16.047c8.024-5.015 16.048-10.03 21.063-16.047 8.023-3.009 13.038-8.024 18.052-13.038 13.039-8.024 24.071-18.053 37.11-26.077 8.023-3.008 13.037-8.023 18.052-10.029 12.035-7.02 25.074-12.035 36.106-12.035z\" fill=\"#F02325\" ></path></symbol><symbol id=\"p-m-windows\" viewBox=\"0 0 1024 1024\"><path d=\"M456 484V160.1l-335.9 72V484H456zM512 484h391.8V64.2l-391.8 84V484zM456 540H120.2v251.9l335.9 72V540zM512 540v335.9l391.8 84V540H512z\" fill=\"#00adef\" ></path></symbol><symbol id=\"p-m-machine\" viewBox=\"0 0 1024 1024\"><path d=\"M320 934.976V1024H64v-311.68h256.96L320 712.704v88.64H153.6v133.632H320z m640-356.224H448v89.088H64v-311.68h896v222.592zM153.6 445.248v133.504h716.8V445.248H153.6zM64 0h896v311.68H64V0z m89.6 89.024v133.568h716.8V89.024H153.6z m44.8 44.544h89.6v44.544H198.4v-44.544z m0 356.16h89.6v44.544H198.4v-44.544z m0 356.16h89.6v44.544H198.4v-44.544z m825.6 28.864C1024 958.08 958.912 1024 876.288 1024h-320C461.44 1024 384 945.6 384 849.92c0-84.928 59.136-155.2 141.504-171.84A204.608 204.608 0 0 1 704 576c95.232 0 176.32 63.232 202.112 152.32A151.488 151.488 0 0 1 1024 874.752z m-96 0a55.552 55.552 0 0 0-41.984-52.544l-56.128-12.032-16-55.168A114.24 114.24 0 0 0 704 672c-40.32 0-76.096 20.608-95.232 53.952l-21.696 37.632-42.56 8.576c-38.4 7.744-64.512 39.296-64.512 77.696 0 43.008 34.752 78.144 76.288 78.144h320c29.312 0 51.712-22.656 51.712-53.248z\" fill=\"#262626\" ></path></symbol><symbol id=\"p-m-mobile\" viewBox=\"0 0 1024 1024\"><path d=\"M736 64H288c-70.7 0-128 57.3-128 128v640c0 70.7 57.3 128 128 128h448c70.7 0 128-57.3 128-128V192c0-70.7-57.3-128-128-128z m64 768c0 35.3-28.7 64-64 64H288c-35.3 0-64-28.7-64-64V192c0-35.3 28.7-64 64-64h448c35.3 0 64 28.7 64 64v640z\" fill=\"#1875F0\" ></path><path d=\"M672 192H352c-35.3 0-64 28.7-64 64v384c0 35.3 28.7 64 64 64h320c35.3 0 64-28.7 64-64V256c0-35.3-28.7-64-64-64z m0 448H352V256h320v384z\" fill=\"#1875F0\" ></path><path d=\"M512 800m-32 0a32 32 0 1 0 64 0 32 32 0 1 0-64 0Z\" fill=\"#1875F0\" ></path></symbol><symbol id=\"p-m-ucweb\" viewBox=\"0 0 1024 1024\"><path d=\"M48 48m139 0l650 0q139 0 139 139l0 650q0 139-139 139l-650 0q-139 0-139-139l0-650q0-139 139-139Z\" fill=\"#FF9500\" ></path><path d=\"M437.742 815c-55.505 0-100.5-44.995-100.5-100.5S382.237 614 437.742 614s100.5 44.995 100.5 100.5S493.247 815 437.742 815z m0-55c25.129 0 45.5-20.371 45.5-45.5s-20.371-45.5-45.5-45.5-45.5 20.371-45.5 45.5 20.371 45.5 45.5 45.5z m-136.656-12.72c14.731 61.636 70.08 107.499 136.177 107.72h-0.02C228.242 863 172 703 172 675s0-110 76.818-204c76.817-94 76.424-107 48.424-165-18.667-38.667-60.414-42-125.242-10 60.161-85.333 130.575-128 211.242-128 65 2 155 58 155 128s-29 127-55 156-124 71-173 134c-32.667 42-37 100-13 174 1.314-4.336 2.595-8.576 3.844-12.72z m35.963-130.766c0.129-1.028 0.193-1.866 0.193-2.514 0-14 29-49.223 69-69s87-23 172 29 57 218 57 219c48 0 78.333 16.333 91 49-51.797 3.237-144.154 7.418-277.069 12.542 72.25-5.818 129.07-66.294 129.07-140.042 0-77.596-62.905-140.5-140.5-140.5-39.488 0-75.17 16.29-100.694 42.514zM443.242 503c43.333-30.667 108.333-26.333 195 13 130 59 112 48 165.314 58 53.314 10 47.686 48 46.686 48-15.333-12-52.333-6-111 18h-64c-8-17.333-26.333-42.667-55-76s-87.667-53.667-177-61z m95-44c70.667-31.333 115.333-56.333 134-75 12-190 64-29 66-27 81.333 38 118.885 95.667 112.655 173-57.103 4-91.655 4-103.655 0-50.748-29.333-120.415-53-209-71z\" fill=\"#FFFFFF\" ></path></symbol><symbol id=\"p-m-edge\" viewBox=\"0 0 1024 1024\"><path d=\"M39.428571 454.285714h0.571429q9.142857-72 33.428571-138t65.714286-124 95.714286-100.571428T362.571429 24.571429 520.571429 0q132 0 236.571428 60.285714T925.142857 233.714286q59.428571 106.857143 59.428572 252.571428v107.428572H341.714286q0.571429 63.428571 30.571428 110T450.285714 773.714286t108.285715 32.571428 121.714285 1.714286 118.857143-26.571429T898.285714 733.142857v215.428572q-52.571429 31.428571-131.142857 52.571428t-178.571428 21.714286-180.571429-30.285714q-108-41.714286-178-142.285715T158.857143 637.714286q-1.714286-138.285714 63.428571-235.428572t185.714286-153.142857q-27.428571 34.285714-44.571429 71.714286T337.142857 412h362.857143q4.571429-44-4.571429-80t-26.857142-58-40.285715-38-46-23.428571-42.857143-11.714286-32-4.857143l-12.571428-0.571429q-77.142857 2.857143-148.285714 25.428572T218.857143 280.571429 118.285714 360.857143 39.428571 454.285714z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-m-2345explorer\" viewBox=\"0 0 1024 1024\"><path d=\"M474.035408 75.740722C337.448197 87.361245 208.918564 167.430227 138.714227 284.626012c-51.419532 85.84085-72.060037 180.118097-60.861846 277.971086C108.047822 826.395755 362.839294 1001.909157 620.955669 936.71393c137.219429-34.65936 250.793398-135.261345 300.405862-266.102284 2.554467-6.734272 2.702923-9.803216 0.517036-10.640201-3.432405-1.318187-239.354606-0.660373-241.98842 0.673171-1.371938 0.696208-5.533826 5.487753-9.250346 10.652999-82.390528 114.505659-236.830854 112.191793-319.126677-4.781307-20.338474-28.913088-43.026648-83.063699-39.942346-95.347155 1.313068-5.234354-14.517974-4.988634 320.058367-4.988634 250.092071 0 314.350489-0.261078 315.156758-1.279793 5.579898-7.05422 4.466478-83.908363-1.807068-124.651859C918.418007 267.763456 779.998132 121.790242 607.64582 84.509865c-38.982501-8.431278-94.645828-12.086367-133.610412-8.769143m62.208188 192.015055c86.421876 13.509497 153.547031 79.265273 177.809351 174.177298 2.052788 8.029423 2.029752 11.507901-0.087026 13.849922-2.439286 2.695245-399.44138 2.956322-402.134065 0.263637-6.882728-6.882728 11.472066-57.798021 33.23111-92.178386 42.683663-67.442543 121.408863-107.018868 191.18063-96.112471\" fill=\"#333333\" ></path><path d=\"M0.168933 251.095429c0.094705 138.102486 0.504239 247.639988 0.913772 243.41667 0.406974-4.223318 1.059669-12.516378 1.446166-18.429022 0.386498-5.912645 1.126218-11.902077 1.645814-13.30985 0.517036-1.407773 1.392415-6.706116 1.942727-11.774097 10.202512-94.021289 52.120859-192.375957 112.504062-263.972709C144.434903 156.421446 180.942284 121.55476 206.118376 103.468722c3.481038-2.500716 10.599247-7.655723 15.818245-11.456709C248.095594 72.96613 305.384258 42.686223 330.18665 34.797578c2.252436-0.716684 8.641164-3.066385 14.200586-5.224116C372.524769 18.659385 440.29238 3.519431 471.987739 1.067348c5.349536-0.414653-98.659259-0.824187-231.130655-0.908654L0 0l0.168933 251.095429M552.358753 1.005917c8.830573 0.665492 13.588844 1.36426 31.738871 4.658448 4.504872 0.816508 13.489021 2.388094 19.964775 3.491276 111.58773 19.022846 238.008264 92.439464 299.804359 174.110748 1.492239 1.970882 5.794904 7.317858 9.565174 11.88416 54.260673 65.719941 93.990573 160.214753 107.356734 255.336664 0.511917 3.660209 1.343783 9.649641 1.848021 13.309849 0.552871 4.041587 0.970083-85.748705 1.05455-228.571069L1023.834574 0l-240.345166 0.153575c-132.189841 0.084466-236.198636 0.468404-231.130655 0.852342M494.512099 5.144769c-87.617203 3.40681-185.877167 34.052738-252.144859 78.640733a614.456879 614.456879 0 0 1-16.051167 10.330491c-4.760831 2.943524-10.878242 7.215474-13.599083 9.490947-2.718281 2.278032-9.250345 7.42536-14.515414 11.441351C174.369266 133.226474 137.687833 169.982135 117.349359 196.064321 53.671968 277.725367 20.73521 358.516153 7.991029 464.308979c-1.860819 15.457342-2.178208 76.590505-0.455607 88.049774 0.844664 5.63109 2.063027 15.075964 2.705483 20.988608 12.931031 119.097557 83.340134 249.209014 175.073153 323.526607 124.680015 101.011519 266.171393 138.842207 419.00174 112.035657 219.346319-38.475703 389.569055-223.395585 412.24955-447.848278 3.493835-34.569774 3.683245-47.90522 1.228602-86.514022-6.050862-95.165424-42.379073-190.207987-103.903852-271.828079-11.569331-15.34984-12.700668-16.657789-34.928116-40.382596C782.020205 58.865369 637.075944-0.399295 494.512099 5.144769m65.013496 64.478542c156.513591 20.707054 274.618029 101.694929 349.37075 239.57473 18.989572 35.0305 36.829889 90.084645 43.315881 133.692319 7.24107 48.660298 6.9288 119.852635-0.55543 127.336866l-2.608219 2.608218-315.062053 0.511917-315.062054 0.511918-0.299472 2.098861c-1.179969 8.23163 17.121074 53.282911 30.295265 74.581229 77.048671 124.557155 232.95564 132.3255 317.096925 15.795208 3.934084-5.44936 8.377526-10.632522 9.872325-11.518139 4.783867-2.833462 250.90602-2.408571 253.75228 0.437689 3.048467 3.048467 2.595421 5.738593-3.639732 21.746247-57.112052 146.582396-201.549515 259.329619-353.678534 276.082112-5.349536 0.588705-14.333684 1.60998-19.964774 2.270353-31.209037 3.657649-86.877483 0.826746-123.883984-6.299142C239.211269 912.607745 96.097113 759.861865 71.665861 568.228189 43.973695 351.014005 178.551631 146.623349 390.086092 84.627606c48.77036-14.292731 124.280719-20.98093 169.439503-15.004295m-74.739924 205.826583c-63.029816 11.397838-112.76514 49.671334-145.338437 111.851368-14.208264 27.118818-25.327108 61.711629-20.438298 63.587806 3.696043 1.418011 385.939562 0.68085 387.895086-0.7474 5.052624-3.693483-13.814088-53.523512-30.057224-79.385573-44.396027-70.685539-120.195619-108.298662-192.061127-95.306201M0.181731 771.71531L0 1023.834574l240.857084-0.102384c153.843943-0.066549 239.377643-0.453047 236.761745-1.072466-2.252436-0.532394-8.932957-1.397534-14.845602-1.924809-5.912645-0.524715-13.284254-1.405213-16.381353-1.955524-3.0971-0.547751-9.199154-1.492239-13.560689-2.098861-117.413349-16.286648-242.280214-88.108644-315.57653-181.513072-58.448156-74.486525-93.250853-151.1359-107.947999-237.759984-2.260115-13.309849-3.399131-19.644826-4.33338-24.060113-0.534954-2.533991-1.443607-11.287776-2.019513-19.452857-0.575907-8.165081-1.394975-19.222494-1.817307-24.572029-0.424891-5.349536-0.854902 103.72724-0.954725 242.392835m1022.268107-216.541012a106.212599 106.212599 0 0 0-0.655254 10.297216c-0.00256 2.705483-0.43257 6.852013-0.954726 9.214511-0.519596 2.359939-1.727721 8.899682-2.682447 14.530772-16.685944 98.400741-56.63085 184.822617-118.780168 256.982478-76.306391 88.597525-195.618953 155.497436-302.993604 169.89511-2.815545 0.378819-8.561817 1.377058-12.767217 2.221721-4.20796 0.844664-13.389197 2.206364-20.405023 3.025431-7.013267 0.821627-12.979663 1.717482-13.253538 1.993918-0.273876 0.273876 106.235635 0.499119 236.687517 0.499119H1023.834574v-237.017704c0-130.359737-0.166373-237.017704-0.368581-237.017704-0.204767 0-0.660373 2.418809-1.016155 5.375132\" fill=\"#ffffff\" ></path><path d=\"M471.987739 1.067348c-31.695359 2.452084-99.462969 17.592038-127.600503 28.506114-5.559422 2.157731-11.948149 4.507432-14.200586 5.224116-24.802393 7.888645-82.091056 38.168553-108.250029 57.214435-5.218997 3.800986-12.337207 8.955993-15.818245 11.456709-107.231314 77.035873-185.326856 212.870565-200.057275 347.975776-0.581026 5.311142-1.374498 10.14876-1.766115 10.750263-0.391617 0.598943-0.99056 5.236914-1.330985 10.304895s-1.182529 15.664669-1.871058 23.548195c-0.967524 11.095807-0.980322 17.917105-0.05887 30.20312 0.657814 8.72819 1.586944 21.858868 2.065586 29.179285 0.481202 7.320417 1.318187 15.383114 1.86082 17.917105 0.941928 4.397369 2.050229 10.535258 4.346177 24.060113C46.502566 816.615575 218.911189 984.765047 440.248867 1017.706924c4.223318 0.629658 9.34505 1.574146 11.379921 2.098861 2.037431 0.527275 6.217235 0.957285 9.291299 0.957285s9.71875 0.709005 14.766254 1.576705c20.246329 3.478478 82.016828 1.17485 107.922402-4.023669 4.21052-0.844664 9.959351-1.842902 12.774896-2.221721 126.579228-16.972618 267.4947-106.82434 337.908923-215.455748 48.358267-74.609385 70.55756-131.818701 85.613047-220.636351 3.28139-19.350473 5.257391-94.144149 2.992156-113.13372-12.447269-104.290349-51.724123-201.782437-109.465833-271.718017-3.770271-4.566302-8.072936-9.913278-9.565174-11.88416C842.070662 101.595105 715.650129 28.178487 604.062399 9.155641c-6.475754-1.103182-15.459902-2.669649-19.964775-3.483597C565.320498 2.28827 562.15429 1.837783 550.866514 0.964964c-13.721943-1.064788-64.662832-0.998239-78.878775 0.102384m75.044514 4.599576c124.784958 8.97135 248.069998 67.163548 331.929729 156.669726 22.227449 23.724807 23.358786 25.032755 34.928116 40.382596 61.524779 81.620092 97.852989 176.662656 103.903852 271.828079 2.454643 38.608802 2.265234 51.944247-1.228602 86.514022-26.228082 259.549743-243.055768 455.834189-503.624226 455.908416-120.853433 0.035834-227.680333-39.123279-327.627064-120.095795C93.58104 822.556375 23.171936 692.444918 10.240905 573.347361c-0.642456-5.912645-1.860819-15.357519-2.705483-20.988608-1.722602-11.459268-1.405213-72.592431 0.455607-88.049774 12.744181-105.792827 45.680939-186.583613 109.35833-268.244658 20.338474-26.082186 57.019907-62.837847 80.852217-81.01603 5.265069-4.015991 11.797134-9.163319 14.515414-11.441351 2.72084-2.275472 8.838252-6.547422 13.599083-9.490947a614.456879 614.456879 0 0 0 16.051167-10.330491C323.06844 29.486436 444.676951-1.694446 547.032253 5.666924M480.178415 68.57388C312.807058 79.610817 162.131884 190.461386 99.526959 348.615672c-95.242211 240.603684 33.615049 511.070064 280.387336 588.512911 51.166133 16.058845 124.651859 23.824631 172.444458 18.224256 5.63109-0.660373 14.615239-1.681648 19.964774-2.270353 152.12902-16.752493 296.566482-129.499716 353.678534-276.082112 6.235153-16.007654 6.688199-18.697779 3.639732-21.746247-2.84626-2.84626-248.968413-3.271151-253.75228-0.437689-1.494798 0.885617-5.938241 6.068779-9.872325 11.518139-84.141285 116.530292-240.048254 108.761947-317.096925-15.795208-13.174191-21.298319-31.475234-66.3496-30.295265-74.581229l0.299472-2.098861 315.062054-0.511918 315.062053-0.511917 2.608219-2.608218c7.484231-7.484231 7.7965-78.676568 0.55543-127.336866-6.485992-43.607674-24.326309-98.661819-43.315881-133.692319C834.143623 171.318239 716.039186 90.330365 559.525595 69.623311c-10.796336-1.428249-63.111723-2.121897-79.34718-1.049431m92.657029 9.588211c182.64185 27.095782 327.698732 160.306898 367.525897 337.514746 9.910719 44.101674 14.023974 139.617761 6.424562 149.223889-0.80627 1.018715-65.064687 1.279793-315.156758 1.279793-334.576341 0-318.745299-0.24572-320.058367 4.988634-3.084302 12.283455 19.603873 66.434066 39.942346 95.347155 82.295823 116.9731 236.736149 119.286966 319.126677 4.781307 3.71652-5.165245 7.878407-9.956791 9.250346-10.652999 2.633814-1.333545 238.556015-1.991358 241.98842-0.673171 2.185887 0.836985 2.037431 3.905929-0.517036 10.640201-49.612464 130.840939-163.186433 231.442925-300.405862 266.102284-192.570485 48.639821-395.17455-37.950988-490.066097-209.445838C0.813948 492.180316 108.810579 194.415947 357.484639 102.508877c67.931424-25.106983 148.681257-34.237028 215.350805-24.346786m-83.442518 189.652557c-83.836694 12.920792-149.167578 74.609385-175.574831 165.792092-4.732675 16.33784-5.003991 19.419582-1.986239 22.439894 2.692685 2.692685 399.694779 2.431607 402.134065-0.263637 2.887213-3.191804 2.073265-8.482469-4.678924-30.379732-31.767027-103.031033-127.718244-171.794322-219.894071-157.588617m57.818498 8.579734c53.454403 11.63844 98.080793 44.122151 129.635374 94.361713 16.243136 25.862061 35.109847 75.69209 30.057224 79.385573-1.955524 1.428249-384.199043 2.16541-387.895086 0.7474-6.839215-2.623576 13.174191-54.593419 32.767826-85.088332 43.776607-68.133631 124.180895-104.917448 195.434662-89.406354\" fill=\"#333333\" ></path></symbol><symbol id=\"p-m-chrome\" viewBox=\"0 0 1024 1024\"><path d=\"M123.648 178.346667C361.642667-98.602667 802.986667-43.946667 967.936 279.68h-396.501333c-71.424 0-117.546667-1.621333-167.509334 24.661333-58.709333 30.933333-102.997333 88.234667-118.485333 155.52L123.648 178.389333z\" fill=\"#EA4335\" ></path><path d=\"M341.674667 512c0 93.866667 76.330667 170.24 170.154666 170.24 93.866667 0 170.154667-76.373333 170.154667-170.24s-76.330667-170.24-170.154667-170.24c-93.866667 0-170.154667 76.373333-170.154666 170.24z\" fill=\"#4285F4\" ></path><path d=\"M577.877333 734.848c-95.530667 28.373333-207.274667-3.114667-268.501333-108.8-46.762667-80.64-170.24-295.765333-226.346667-393.557333-196.565333 301.226667-27.136 711.808 329.685334 781.866666l165.12-279.509333z\" fill=\"#34A853\" ></path><path d=\"M669.866667 341.76a233.130667 233.130667 0 0 1 43.008 286.634667c-40.576 69.973333-170.154667 288.682667-232.96 394.581333 367.658667 22.656 635.733333-337.664 514.645333-681.258667H669.866667z\" fill=\"#FBBC05\" ></path></symbol><symbol id=\"p-m-opera\" viewBox=\"0 0 1024 1024\"><path d=\"M533.504 0C254.506667 0 42.666667 194.005333 42.666667 507.136 42.666667 777.642667 238.933333 1024 533.333333 1024 827.818667 1024 1024 777.642667 1024 507.136 1024 194.389333 812.458667 0 533.504 0z m0 85.333333C692.906667 85.333333 725.333333 294.528 725.333333 484.48 725.333333 660.437333 707.285333 896 535.424 896S341.333333 658.133333 341.333333 482.176C341.333333 292.266667 374.016 85.333333 533.461333 85.333333z\" fill=\"#D00003\" ></path></symbol><symbol id=\"p-m-linux\" viewBox=\"0 0 1024 1024\"><path d=\"M525.2 198.3c-8.6 5.6-15.2 13.8-18.9 23.4-3.8 12.4-3.2 25.6 1.5 37.7 3.9 12.7 11.7 23.8 22.2 31.8 5.4 3.8 11.6 6.2 18.2 7 6.6 0.8 13.2-0.3 19.1-3.3 7-3.9 12.6-10 15.9-17.3 3.2-7.4 5-15.3 5.2-23.3 0.7-10.2-0.6-20.4-3.8-30.1-3.5-10.6-10.3-19.7-19.5-25.9-4.7-3-9.9-5-15.4-5.8-5.5-0.8-11.1-0.2-16.3 1.8-2.9 1.2-5.7 2.7-8.3 4.5\" fill=\"#FFFFFF\" ></path><path d=\"M810.2 606.5c-5.1-28.3-13.1-56-23.8-82.6-7.3-19.8-17.2-38.6-29.5-55.8-12.4-16.5-28.1-30.4-40.2-47.1-6.4-8.7-11.8-18.4-18.5-26.9-2.7-5.6-5.3-11.2-7.9-16.8-8-17.5-15.3-35.4-24.8-52-1.5-2.6-3.1-5.2-4.6-7.7-1.2-16-2.9-32-3.8-48 0.7-32.1-2-64.3-8.1-95.9-4.2-15.1-10.6-29.6-19-42.8-9.8-15.6-22.4-29.2-37.2-40.1-24.1-17.1-52.9-26.3-82.4-26.4-21.7-0.5-43.2 4.4-62.5 14.4-20.3 11.1-36.7 28.2-47 48.9-9.6 20.9-14.7 43.5-15 66.5-0.8 22.6 1.3 45 2.2 67.6 0.9 23.4 0.4 46.9 2.3 70.3 0.6 7.5 1.5 15 1.5 22.6 0 3.8-0.2 7.6-0.3 11.3l-0.3 0.8c-10.2 17.3-21.5 34-33.8 49.9-8.6 10.9-17.2 21.7-25.9 32.4-11.3 12.7-20.9 26.8-28.5 42-5.1 13.2-9.2 26.8-12.4 40.6l-0.3 1.1c-4.8 15.9-10.8 31.3-18 46.2-0.7 1.4-1.4 2.9-2 4.2-4.3 8.9-8.8 17.8-13.5 26.5l-5.4 10.1c-3.4 6.1-6.4 12.4-9 18.8-1.5 3.9-2.7 7.9-3.4 12-1.3 8.7-0.7 17.5 1.6 25.9 0.5 2.1 1.2 4.2 1.9 6.3 2.2 6.2 4.8 12.3 7.9 18.1 1.4 2.7 2.9 5.3 4.3 8l1.3 1.9c1.4 2.5 2.9 5 4.4 7.4l0.2 0.3c1.7 2.8 3.6 5.5 5.4 8.2l0.3 0.4c1.9 2.6 3.8 5.3 5.8 7.9 7.4 28.9 21 55.8 39.7 79-2.9 5.1-5.5 10.1-8.4 15.1-10.2 14.8-18.6 30.7-25.1 47.4-2.7 8.6-3.4 17.7-1.9 26.6 1.4 9 6 17.1 13 23 4.7 3.6 10.1 6.1 15.8 7.3 5.7 1.2 11.6 1.8 17.5 1.5 22.2-1.7 44.2-6.1 65.4-12.9 12.8-3.4 25.6-6.4 38.6-9 13.5-3.1 27.2-5 41-5.6 3.4 0.1 6.8-0.1 10.1-0.3 9.4 1 18.8 1.4 28.3 1l3.5-0.2c2.4 0.3 4.9 0.4 7.4 0.6 16.6 0.9 33.1 2.6 49.5 5.1 14.4 2.2 28.8 5 43 8.5 21.9 6.6 44.4 11 67.3 12.9 6 0.3 12-0.2 18-1.4 5.9-1.2 11.5-3.8 16.3-7.4 7-5.8 11.6-13.9 13.1-22.9 1.5-8.9 0.8-18-1.9-26.6-6.6-16.7-15.1-32.6-25.5-47.3-3.6-6.1-7-12.4-10.6-18.5 15.5-17.3 29.2-36.3 40.7-56.5 7 0.4 13.9-0.4 20.6-2.6 17.5-5.9 32.7-17.3 43.3-32.5 3.2-4.5 5.7-9.5 7.2-14.8 6.9-10.7 11.6-22.7 13.8-35.3 3.2-20.8 2.7-42.1-1.5-62.7h-0.2z m0 0\" fill=\"#020204\" ></path><path d=\"M425.6 323.2c-3.1 4-5.3 8.7-6.4 13.6-1.1 4.9-1.8 10-1.9 15 0.3 10.1-0.5 20.2-2.5 30.1-3.5 10.3-8.8 19.8-15.6 28.3-11.7 14.7-20.9 31.2-27.2 48.8-3.2 10.9-4.3 22.3-3.1 33.7-12.1 17.9-22.6 36.9-31.3 56.7-13.4 29.9-22 61.8-25.5 94.4-4.3 40.1 1.6 80.6 17 117.8 11.3 26.8 28.5 50.8 50.3 70.1 11.2 9.7 23.5 17.9 36.7 24.4 46.7 22.8 101.4 22.3 147.6-1.4 23.1-13.5 44.2-30.2 62.6-49.5 11.9-10.8 22.5-22.9 31.8-36.1 15.5-26.9 24.6-57.1 26.5-88.1 9.6-53.6 3.7-108.8-16.9-159.2-8.1-16.8-18.8-32.2-31.8-45.6a252.5 252.5 0 0 0-20.2-68c-7.2-15.5-15.9-30.3-22.6-46.2-2.7-6.5-5.1-13.1-8.1-19.4-2.9-6.4-6.9-12.3-11.8-17.3-5.3-4.9-11.6-8.6-18.5-10.7-6.9-2.2-14-3.4-21.2-3.6-14.4-0.7-28.9 1.1-43.1 0.6-11.5-0.5-22.8-2.5-34.3-1.8-5.7 0.3-11.4 1.4-16.7 3.5-5.4 2.1-10.1 5.5-13.8 10m4.6-125.1c-5.4 0.4-10.5 2.7-14.4 6.4-3.9 3.7-6.8 8.4-8.4 13.5-2.7 10.4-3.4 21.3-1.9 32 0.2 9.7 1.9 19.4 5.1 28.6 1.8 4.5 4.4 8.7 7.8 12.2 3.4 3.5 7.7 6.1 12.4 7.3 4.5 1.1 9.2 0.9 13.5-0.5 4.3-1.4 8.3-3.8 11.5-7 4.7-4.8 8.1-10.7 9.8-17.1 1.7-6.4 2.5-13.1 2.3-19.8 0-8.3-1.3-16.6-3.8-24.6s-6.8-15.3-12.6-21.4c-2.8-2.9-6-5.4-9.6-7.2-3.7-1.7-7.7-2.6-11.7-2.4m95 0c-8.6 5.6-15.2 13.8-18.9 23.4-3.8 12.4-3.2 25.6 1.5 37.7 3.9 12.7 11.7 23.8 22.2 31.8 5.4 3.8 11.6 6.2 18.2 7 6.6 0.8 13.2-0.3 19.1-3.3 7-3.9 12.6-10 15.9-17.3 3.2-7.4 5-15.3 5.2-23.3 0.7-10.2-0.6-20.4-3.8-30.1-3.5-10.6-10.3-19.7-19.5-25.9-4.7-3-9.9-5-15.4-5.8-5.5-0.8-11.1-0.2-16.3 1.8-2.9 1.2-5.7 2.7-8.3 4.5\" fill=\"#FFFFFF\" ></path><path d=\"M544.5 223.6c-3.2 0.2-6.2 1.2-8.9 2.9s-5 4-6.8 6.6c-3.4 5.3-5.3 11.5-5.4 17.9-0.3 4.7 0.4 9.5 1.9 14s4.3 8.5 7.9 11.5c3.8 3.1 8.4 4.9 13.3 5.2 4.9 0.2 9.7-1.1 13.7-3.9 3.2-2.3 5.8-5.2 7.6-8.7 1.8-3.4 2.9-7.2 3.4-11 1-6.8-0.2-13.8-3.2-19.9-3.1-6.2-8.4-10.9-14.8-13.4-2.8-1.1-5.7-1.5-8.7-1.4\" fill=\"#020204\" ></path><path d=\"M430.2 198.3c-5.4 0.4-10.5 2.7-14.4 6.4-3.9 3.7-6.8 8.4-8.4 13.5-2.7 10.4-3.4 21.3-1.9 32 0.2 9.7 1.9 19.4 5.1 28.6 1.8 4.6 4.4 8.7 7.8 12.2 3.4 3.5 7.7 6.1 12.4 7.3 4.5 1.1 9.2 0.9 13.5-0.5 4.3-1.4 8.3-3.8 11.5-7 4.7-4.8 8.1-10.7 9.8-17.1 1.7-6.4 2.5-13.1 2.3-19.8 0-8.3-1.3-16.6-3.8-24.6s-6.8-15.3-12.6-21.4c-2.8-2.9-6-5.4-9.6-7.2-3.7-1.7-7.7-2.6-11.7-2.4\" fill=\"#FFFFFF\" ></path><path d=\"M417.3 242.8c-1.3 6.7-1 13.7 1.1 20.2 1.6 4.3 4 8.2 7.2 11.5 2 2.2 4.3 4.1 7 5.4 2.7 1.4 5.7 1.8 8.7 1.1 2.7-0.7 5-2.3 6.7-4.5 1.7-2.2 2.9-4.7 3.7-7.3 2.3-7.8 2.1-16.1-0.4-23.9-1.6-5.7-4.7-10.9-9.1-14.8-2.1-1.8-4.7-3.2-7.4-3.9-2.8-0.7-5.7-0.5-8.4 0.7-2.8 1.4-5.1 3.7-6.5 6.5-1.4 2.8-2.3 5.8-2.7 8.9\" fill=\"#020204\" ></path><path d=\"M404.6 326.9c0.2 0.9 0.5 1.8 1 2.5 0.9 1.4 2 2.5 3.4 3.4 1.3 0.9 2.6 1.7 3.9 2.5 6.9 4.7 13 10.5 17.9 17.3 6 9.4 13.5 17.8 22 25 6.5 4.5 14.1 7.2 22 7.9 9.2 0.7 18.5-0.4 27.4-3.2 8.2-2.4 16.1-5.8 23.5-10.3 12.7-10.2 26.3-19.2 40.7-26.7 3.4-1.2 6.8-2.1 10-3.6 3.3-1.4 6.1-3.8 7.8-7 1.1-3.2 1.8-6.6 1.9-10 0.5-3.6 1.7-7.1 2.3-10.7 0.8-3.6 0.5-7.3-0.8-10.8-1.4-2.7-3.6-4.9-6.3-6.3-2.7-1.3-5.7-2.1-8.7-2.2-6.1 0.2-12.1 0.8-18 1.8-8 0.7-16-0.3-24 0-9.9 0.3-19.8 2.5-29.8 2.9-11.4 0.6-22.7-1.2-34.1-1.7-4.9-0.3-9.9-0.1-14.8 0.7-4.9 0.7-9.6 2.5-13.7 5.3-3.8 3-7.3 6.2-10.7 9.6-1.8 1.6-3.8 3-5.9 4.1-2.2 1.1-4.5 1.7-7 1.6-1.2-0.2-2.5-0.2-3.7 0-0.7 0.3-1.4 0.7-1.9 1.2l-1.5 1.8c-1 1.5-1.9 3.1-2.6 4.7\" fill=\"#D99A03\" ></path><path d=\"M429.7 301.7c-4 2.4-7.9 5-11.8 7.7-2.1 1.3-3.8 3-5.1 5.1-0.7 1.6-1 3.3-0.9 5 0.1 1.7 0.1 3.4 0 5.1-0.1 1.1-0.5 2.3-0.5 3.5 0 0.6 0 1.2 0.2 1.7 0.2 0.6 0.4 1.1 0.8 1.5 0.5 0.5 1.2 0.9 2 1.1 0.7 0.2 1.5 0.3 2.3 0.5 3.5 1 6.7 2.9 9.3 5.4 2.7 2.4 5.1 5.2 8 7.5 8 6 17.7 9.1 27.6 9 9.9-0.2 19.7-1.6 29.2-4.1 7.5-1.6 14.9-3.6 22.1-6.1 11.2-4.2 21.5-10.3 30.4-18.2 3.9-3.8 8-7.2 12.4-10.3 4-2.5 8.7-4.2 12.7-6.6 0.4-0.2 0.7-0.5 1.1-0.7 0.3-0.3 0.6-0.6 0.8-1 0.3-0.7 0.3-1.5 0-2.2-0.2-0.7-0.5-1.3-0.9-1.8-0.5-0.6-1.1-1.2-1.7-1.7-4.6-3.4-10.1-5.3-15.8-5.5-5.8-0.4-11.3 0-16.9-1.1-5.2-1.1-10.3-2.6-15.3-4.4-5.3-1.7-10.7-3-16.3-3.9-13-2.1-26.2-1.8-39.1 1-12.1 2.7-23.8 7.3-34.6 13.5\" fill=\"#604405\" ></path><path d=\"M428.4 288.1c-5.8 3.9-11 8.7-15.5 14.1-2.6 3-4.7 6.5-6.1 10.3-0.9 3-1.5 6.1-2 9.2-0.3 1.1-0.5 2.3-0.5 3.5 0 0.6 0.1 1.2 0.3 1.7 0.2 0.6 0.5 1.1 0.9 1.5 0.7 0.7 1.6 1.1 2.6 1.3 0.9 0.2 1.9 0.2 2.9 0.3 4.4 0.7 8.5 2.5 12.1 5.1 3.6 2.5 7 5.4 10.7 7.8 8.4 5 18 7.7 27.8 7.9 9.8 0.2 19.5-0.8 29-2.9 7.6-1.4 15.1-3.5 22.4-6.3 10.9-4.7 21.1-10.8 30.4-18.2 4.3-3.2 8.5-6.6 12.4-10.3 1.3-1.3 2.6-2.6 4-3.8 1.4-1.2 3-2.1 4.7-2.7 2.7-0.7 5.5-0.8 8.3-0.1 2 0.5 4.1 0.7 6.2 0.7 1.1 0 2.1-0.2 3.1-0.5 1-0.4 1.9-1 2.5-1.8 0.9-1.1 1.3-2.4 1.3-3.8s-0.4-2.7-1.1-3.9c-1.5-2.3-3.8-4.1-6.3-5.1-3.5-1.4-7.1-2.5-10.8-3.2-11.3-2.7-22.3-6.7-32.7-11.9-5.2-2.6-10.1-5.4-15.3-8.1-5.2-2.9-10.6-5.4-16.2-7.2-12.9-3.5-26.6-2.9-39.1 1.8-14 4.9-26.5 13.4-36.1 24.7\" fill=\"#F5BD0C\" ></path><path d=\"M493.5 272.2c0.7 2.3 4.3 1.9 6.4 2.9 2.1 1 3.3 2.9 5.3 3.1 2.1 0.2 5-0.7 5.3-2.6 0.4-2.6-3.4-4.2-5.8-5.1-3.2-1.5-6.8-1.6-10-0.2-0.7 0.3-1.4 1.2-1.2 1.9z m-34.4-1.2c-2.7-0.9-7.1 3.8-5.8 6.3 0.4 0.7 1.6 1.5 2.4 1.1 0.8-0.4 2.3-3.1 3.6-4 1-0.8 0.8-3.1-0.2-3.4z m0 0\" fill=\"#CD8907\" ></path><path d=\"M887.7 829.8c-2 5.2-4.9 10-8.5 14.3-8.4 9-18.6 16.2-29.8 21.2-19 8.8-37.5 18.6-55.5 29.3-11.7 7.8-22.6 16.6-32.7 26.4-8.3 8.7-17.2 16.7-26.6 24.2-9.8 7.2-21.1 12.1-33.1 14-14.7 1.9-29.6-0.4-43.1-6.5-9.7-3.7-18.1-10.2-24-18.8-5-9.2-7.3-19.5-6.8-29.9 0.6-18.3 2.8-36.5 6.6-54.5 2.6-15 5.2-30 6.8-45.1 2.8-27.6 3.1-55.3 1-82.9-0.5-4.6-0.5-9.3 0-13.9 0.6-9.4 8.5-16.6 18-16.5 4.3-0.1 8.6 0.3 12.8 1.1 10 1.2 20 2.9 29.8 5.2 6.1 1.6 12.2 3.8 18.3 5.5 10.2 3 21 3.9 31.6 2.9 11.1-2.6 22.4-4.3 33.8-5.3 4.7 0.2 9.4 1 13.8 2.4 4.6 1.3 8.9 3.6 12.4 6.9 2.5 2.7 4.5 5.8 5.8 9.2 1.9 5.1 3.1 10.4 3.5 15.8 0.2 4.8 0.6 9.6 1.2 14.4 1.7 7.7 5.4 14.9 10.6 20.9 5.3 5.8 11 11.2 17.2 16 5.9 5.2 12.1 10 18.6 14.4 3.1 2.1 6.2 4 9.1 6.3 3 2.2 5.5 5 7.4 8.2 2.4 4.4 3.2 9.5 2 14.4\" fill=\"#F5BD0C\" ></path><path d=\"M887.7 829.8c-2 5.2-4.9 10-8.5 14.3-8.4 9-18.6 16.2-29.8 21.2-19 8.8-37.5 18.6-55.5 29.3-11.7 7.8-22.6 16.6-32.7 26.4-8.3 8.7-17.2 16.7-26.6 24.2-9.8 7.2-21.1 12.1-33.1 14-14.7 1.9-29.6-0.4-43.1-6.5-9.7-3.7-18.1-10.2-24-18.8-5-9.2-7.3-19.5-6.8-29.9 0.6-18.3 2.8-36.5 6.6-54.5 2.6-15 5.2-30 6.8-45.1 2.8-27.6 3.1-55.3 1-82.9-0.5-4.6-0.5-9.3 0-13.9 0.6-9.4 8.5-16.6 18-16.5 4.3-0.1 8.6 0.3 12.8 1.1 10 1.2 20 2.9 29.8 5.2 6.1 1.6 12.2 3.8 18.3 5.5 10.2 3 21 3.9 31.6 2.9 11.1-2.6 22.4-4.3 33.8-5.3 4.7 0.2 9.4 1 13.8 2.4 4.6 1.3 8.9 3.6 12.4 6.9 2.5 2.7 4.5 5.8 5.8 9.2 1.9 5.1 3.1 10.4 3.5 15.8 0.2 4.8 0.6 9.6 1.2 14.4 1.7 7.7 5.4 14.9 10.6 20.9 5.3 5.8 11 11.2 17.2 16 5.9 5.2 12.1 10 18.6 14.4 3.1 2.1 6.2 4 9.1 6.3 3 2.2 5.5 5 7.4 8.2 2.4 4.4 3.2 9.5 2 14.4M259.4 676.3c4.9-1.9 10.2-2.4 15.4-1.4 5.2 1 10.1 3.1 14.4 6.1 8.3 6.3 15.5 14.1 21.2 22.8 14.1 19.4 27.6 39.2 39.9 59.8 10 16.7 19.1 33.9 30.6 49.6 7.5 10.2 16 19.7 23.6 29.9 7.9 10 13.9 21.4 17.6 33.5 4.4 16.1 2.6 33.2-4.9 48.1-5.4 10.4-13.5 19.1-23.4 25.1-10 6-21.5 9-33.2 8.7-18.4-2.5-36.2-8.1-52.6-16.6-34.9-13.9-72.8-18.3-108.8-29.1-11.1-3.3-21.9-7.3-33.1-10.3-5-1.2-9.9-2.7-14.7-4.7-4.7-2-8.8-5.4-11.5-9.7-2-3.5-3-7.5-2.9-11.5 0.1-4 0.9-7.9 2.3-11.5 2.7-7.5 7.1-14.2 10-21.6 4.4-12.2 6.1-25.3 5-38.2-0.6-12.9-2.9-25.8-3.6-38.7-0.6-5.8-0.4-11.6 0.6-17.3 1.5-11.4 10.4-20.5 21.9-22.2 5.3-0.9 10.6-1.3 15.9-1 5.3 0.3 10.7 0.3 16 0 5.3-0.3 10.6-1.8 15.3-4.3 4.3-2.6 8.1-6.2 11-10.4 2.9-4.2 5.5-8.5 7.9-13 2.4-4.5 5.1-8.7 8.3-12.7 3-4.1 7.1-7.2 11.8-9.4\" fill=\"#F5BD0C\" ></path><path d=\"M259.4 676.4c4.9-1.9 10.2-2.4 15.4-1.4 5.2 1 10.1 3.1 14.4 6.1 8.3 6.3 15.5 14.1 21.2 22.8 14.1 19.4 27.6 39.2 39.9 59.8 10 16.7 19.1 33.9 30.6 49.6 7.5 10.2 16 19.7 23.6 29.9 7.9 10 13.9 21.4 17.6 33.5 4.4 16.1 2.6 33.2-4.9 48.1-5.4 10.4-13.5 19.1-23.4 25.1-10 6-21.5 9-33.2 8.7-18.4-2.5-36.2-8.1-52.6-16.6-34.9-13.9-72.8-18.3-108.8-29.1-11.1-3.3-21.9-7.3-33.1-10.3-5-1.2-9.9-2.7-14.7-4.7-4.7-2-8.8-5.4-11.5-9.7-2-3.5-3-7.5-2.9-11.5 0.1-4 0.9-7.9 2.3-11.5 2.7-7.5 7.1-14.2 10-21.6 4.4-12.2 6.1-25.3 5-38.2-0.6-12.9-2.9-25.7-3.6-38.7-0.6-5.8-0.4-11.6 0.6-17.3 1.5-11.4 10.4-20.5 21.9-22.2 5.3-0.9 10.6-1.3 15.9-1 5.3 0.3 10.7 0.3 16 0 5.3-0.3 10.6-1.8 15.3-4.3 4.3-2.6 8.1-6.2 11-10.4 2.9-4.2 5.5-8.5 7.9-13 2.4-4.5 5.1-8.7 8.3-12.7 3-4.1 7.1-7.3 11.8-9.4\" fill=\"#F5BD0C\" ></path><path d=\"M267.1 684.8c4.4-1.7 9.3-2 13.9-0.9s8.9 3.2 12.6 6.2c7.1 6.2 13.1 13.6 17.6 21.9 12 19.4 23.7 39 34.6 59 7.9 15.3 16.8 30.1 26.6 44.2 6.8 9.2 14.6 17.6 21.6 26.6 7.3 8.9 12.8 19 16.2 29.9 4 14.3 2.3 29.6-4.5 42.9-5 9.4-12.5 17.3-21.7 22.6-9.2 5.4-19.8 8-30.4 7.5-16.7-2.6-32.9-7.6-48.2-14.9-30.4-11.1-63.5-12.5-94.7-21.2-11.2-3-22.1-7.1-33.4-9.9-5-1.1-10-2.5-14.8-4.3-4.8-1.8-9-5.2-11.8-9.5-1.8-3.4-2.7-7.2-2.5-11 0.2-3.8 1-7.6 2.4-11.2 2.7-7.1 7-13.6 9.7-20.7 3.8-11 5.1-22.6 3.9-34.2-0.8-11.5-2.9-22.9-3.5-34.5-0.4-5.1-0.2-10.3 0.7-15.4 0.9-5.1 3.3-9.8 6.9-13.6 4.2-3.8 9.4-6.3 15-7 5.6-0.7 11.2-0.7 16.7 0 5.6 0.7 11.2 0.9 16.8 0.8 11 0 21-6.4 25.7-16.4 2.3-4.5 4.3-9.2 5.9-13.9 1.7-4.8 4-9.3 6.7-13.6 2.8-4.3 6.8-7.7 11.5-9.7\" fill=\"#F5BD0C\" ></path></symbol><symbol id=\"p-m-maxthon\" viewBox=\"0 0 1024 1024\"><path d=\"M552.96 2.048C800.768 20.48 1003.52 223.232 1021.952 471.04 1046.528 786.432 786.432 1046.528 471.04 1021.952 223.232 1003.52 20.48 800.768 2.048 552.96-22.528 237.568 237.568-22.528 552.96 2.048z m112.64 282.624H208.896v438.272h141.312V425.984h327.68v296.96H819.2V438.272c0-83.968-69.632-153.6-153.6-153.6z m-81.92 235.52h-141.312v202.752h141.312v-202.752z\" fill=\"#037FFC\" ></path></symbol><symbol id=\"p-m-mac\" viewBox=\"0 0 1024 1024\"><path d=\"M202.666667 192C161.813333 192 128 225.813333 128 266.666667v405.333333C128 712.853333 161.813333 746.666667 202.666667 746.666667h618.666666c40.853333 0 74.666667-33.813333 74.666667-74.666667v-405.333333c0-40.853333-33.813333-74.666667-74.666667-74.666667h-618.666666z m0 64h618.666666c6.293333 0 10.666667 4.373333 10.666667 10.666667v405.333333c0 6.293333-4.373333 10.666667-10.666667 10.666667h-618.666666a10.197333 10.197333 0 0 1-10.666667-10.666667v-405.333333c0-6.293333 4.373333-10.666667 10.666667-10.666667z m383.509333 42.965333a15.658667 15.658667 0 0 0-7.018667 0.213334c-2.197333 0.554667-22.101333 6.186667-42.538666 24.96-21.717333 19.925333-24.426667 44.864-24.533334 45.909333a12.096 12.096 0 0 0 4.8 10.837333 15.168 15.168 0 0 0 12.586667 2.773334c1.109333-0.213333 27.882667-6.058667 46.570667-28.586667 17.493333-20.992 20.821333-39.722667 21.162666-41.770667a12.16 12.16 0 0 0-4.757333-11.52 14.869333 14.869333 0 0 0-6.272-2.816zM457.706667 405.333333C418.069333 405.333333 384 437.354667 384 483.584c0 74.666667 54.314667 135.082667 77.589333 135.082667 23.253333 0 31.018667-14.250667 58.154667-14.250667 27.157333 0 34.944 14.250667 58.218667 14.250667 15.509333 0 44.224-27.562667 62.037333-67.584-27.2-8.533333-46.549333-34.368-46.549333-65.749334 0-27.029333 13.44-50.794667 34.88-62.250666a68.224 68.224 0 0 0-46.506667-17.749334c-27.157333 0-31.061333 14.250667-62.08 14.250667-31.018667 0-34.901333-14.250667-62.037333-14.250667zM74.666667 768a32 32 0 1 0 0 64h874.666666a32 32 0 1 0 0-64h-874.666666z\"  ></path></symbol><symbol id=\"p-m-ie\" viewBox=\"0 0 1024 1024\"><path d=\"M852.6 367.6c16.3-36.9 32.1-90.7 32.1-131.8 0-109.1-119.5-147.6-314.5-57.9-161.4-10.8-316.8 110.5-355.6 279.7 46.3-52.3 117.4-123.4 183-151.7C316.1 378.3 246.7 470 194 565.6c-31.1 56.9-66 148.8-66 217.5 0 147.9 139.3 129.8 270.4 63 47.1 23.1 99.8 23.4 152.5 23.4 145.7 0 276.4-81.4 325.2-219H694.9c-78.8 132.9-295.2 79.5-295.2-71.2h493.2c9.6-65.4-2.5-143.6-40.3-211.7zM224.8 648.3c26.6 76.7 80.6 143.8 150.4 185-133.1 73.4-259.9 43.6-150.4-185z m174-163.3c3-82.7 75.4-142.3 156-142.3 80.1 0 153 59.6 156 142.3h-312z m276.8-281.4c32.1-15.4 72.8-33 108.8-33 47.1 0 81.4 32.6 81.4 80.6 0 30-11.1 73.5-21.9 101.8-39.3-63.5-98.9-122.4-168.3-149.4z\" fill=\"#1296DB\" ></path></symbol><symbol id=\"p-Chrome-OS\" viewBox=\"0 0 1024 1024\"><path d=\"M258.007937 446.994031L111.9965 192.985969q71.005781-90.013187 175.9945-141.499578T511.984 0q138.011687 0 255.480017 67.997875t185.498203 183.002281H534.959283q-11.007656-0.991969-23.007282-0.991969-90.013187 0-160.986969 55.486266t-92.989094 141.499578z m436.978345-122.012187h294.006812q35.006906 90.013187 35.006906 187.002156 0 103.004781-39.99875 197.497829T876.516609 872.484735 715.017656 981.985313t-196.985844 42.014687L727.017281 661.003344q47.006531-67.005906 47.006531-148.987344 0-110.012562-79.005531-187.002157zM326.005812 511.984q0-76.989594 54.494297-131.483891T511.984 326.005812t131.483892 54.494297T697.962189 511.984t-54.494297 131.483892T511.984 697.962189t-131.483891-54.494297T326.005812 511.984z m255.992001 252.984095L435.986375 1017.952189q-122.012187-18.015437-222.009062-88.989219T56.990219 747.976626 0 511.984q0-135.003781 66.013937-251.000156l208.985469 362.004688q31.999 67.997875 95.997 109.500578T511.984 773.991813q35.998875 0 70.013813-8.991719z\" fill=\"#F4EA2A\" ></path></symbol><symbol id=\"p-m-safari\" viewBox=\"0 0 1024 1024\"><path d=\"M0 0m184.32 0l655.36 0q184.32 0 184.32 184.32l0 655.36q0 184.32-184.32 184.32l-655.36 0q-184.32 0-184.32-184.32l0-655.36q0-184.32 184.32-184.32Z\" fill=\"#FFFFFF\" ></path><path d=\"M512 512m-440.32 0a440.32 440.32 0 1 0 880.64 0 440.32 440.32 0 1 0-880.64 0Z\" fill=\"#468EE7\" ></path><path d=\"M512 179.2a5.12 5.12 0 0 1-5.12-5.12v-61.44a5.12 5.12 0 0 1 5.12-5.12 5.12 5.12 0 0 1 5.12 5.12v61.44a5.12 5.12 0 0 1-5.12 5.12zM543.6928 149.85216a5.12 5.12 0 0 1-4.6592-5.53984l2.67264-30.60736a5.12 5.12 0 0 1 5.53984-4.64896 5.12 5.12 0 0 1 4.6592 5.53984l-2.67264 30.59712a5.12 5.12 0 0 1-5.53984 4.6592zM569.76384 184.2688a5.12 5.12 0 0 1-4.1472-5.9392l10.69056-60.5184a5.12 5.12 0 0 1 5.92896-4.1472 5.12 5.12 0 0 1 4.15744 5.92896l-10.69056 60.5184a5.12 5.12 0 0 1-5.9392 4.15744zM606.08512 160.8704a5.12 5.12 0 0 1-3.62496-6.27712l7.95648-29.67552a5.12 5.12 0 0 1 6.27712-3.61472 5.12 5.12 0 0 1 3.61472 6.27712l-7.95648 29.67552a5.12 5.12 0 0 1-6.27712 3.61472zM625.82784 199.2704a5.12 5.12 0 0 1-3.06176-6.5536l21.01248-57.74336a5.12 5.12 0 0 1 6.56384-3.06176 5.12 5.12 0 0 1 3.06176 6.56384l-21.01248 57.73312a5.12 5.12 0 0 1-6.56384 3.06176zM665.63072 182.53824a5.12 5.12 0 0 1-2.47808-6.79936l12.98432-27.8528a5.12 5.12 0 0 1 6.79936-2.46784 5.12 5.12 0 0 1 2.47808 6.79936l-12.98432 27.8528a5.12 5.12 0 0 1-6.79936 2.46784zM678.4 223.78496a5.12 5.12 0 0 1-1.87392-6.99392l30.72-53.20704a5.12 5.12 0 0 1 6.99392-1.87392 5.12 5.12 0 0 1 1.87392 6.99392l-30.72 53.20704a5.12 5.12 0 0 1-6.99392 1.87392zM720.49664 214.2208a5.12 5.12 0 0 1-1.24928-7.13728l17.63328-25.15968a5.12 5.12 0 0 1 7.12704-1.24928 5.12 5.12 0 0 1 1.24928 7.12704l-17.63328 25.15968a5.12 5.12 0 0 1-7.12704 1.24928zM725.9136 257.05472a5.12 5.12 0 0 1-0.6144-7.20896l39.48544-47.06304a5.12 5.12 0 0 1 7.20896-0.63488 5.12 5.12 0 0 1 0.63488 7.20896l-39.49568 47.07328a5.12 5.12 0 0 1-7.20896 0.62464zM769.04448 254.95552a5.12 5.12 0 0 1 0-7.23968l21.71904-21.72928a5.12 5.12 0 0 1 7.24992 0 5.12 5.12 0 0 1 0 7.24992l-21.72928 21.7088a5.12 5.12 0 0 1-7.23968 0zM809.7792 303.5136a5.12 5.12 0 0 1 1.25952-7.12704l25.15968-17.63328a5.12 5.12 0 0 1 7.12704 1.24928 5.12 5.12 0 0 1-1.24928 7.12704l-25.15968 17.63328a5.12 5.12 0 0 1-7.12704-1.24928zM800.21504 345.6a5.12 5.12 0 0 1 1.87392-6.99392l53.20704-30.72a5.12 5.12 0 0 1 6.99392 1.87392 5.12 5.12 0 0 1-1.87392 6.99392l-53.20704 30.72a5.12 5.12 0 0 1-6.99392-1.87392zM841.46176 358.36928a5.12 5.12 0 0 1 2.47808-6.79936l27.84256-12.98432a5.12 5.12 0 0 1 6.79936 2.47808 5.12 5.12 0 0 1-2.47808 6.79936l-27.84256 12.98432a5.12 5.12 0 0 1-6.79936-2.47808zM824.7296 398.17216a5.12 5.12 0 0 1 3.06176-6.5536l57.73312-21.02272a5.12 5.12 0 0 1 6.5536 3.06176 5.12 5.12 0 0 1-3.05152 6.5536l-57.73312 21.02272a5.12 5.12 0 0 1-6.56384-3.06176zM863.1296 417.92512a5.12 5.12 0 0 1 3.62496-6.27712l29.67552-7.95648a5.12 5.12 0 0 1 6.26688 3.61472 5.12 5.12 0 0 1-3.61472 6.27712l-29.67552 7.95648a5.12 5.12 0 0 1-6.27712-3.61472zM839.7312 454.23616a5.12 5.12 0 0 1 4.1472-5.9392l60.5184-10.68032a5.12 5.12 0 0 1 5.9392 4.1472 5.12 5.12 0 0 1-4.15744 5.9392l-60.5184 10.68032a5.12 5.12 0 0 1-5.9392-4.1472z\" fill=\"#FFFFFF\" ></path><path d=\"M874.14784 480.3072a5.12 5.12 0 0 1 4.6592-5.53984l30.59712-2.67264a5.12 5.12 0 0 1 5.53984 4.64896 5.12 5.12 0 0 1-4.64896 5.55008l-30.59712 2.67264a5.12 5.12 0 0 1-5.55008-4.6592z\" fill=\"#FFFFFF\" ></path><path d=\"M844.8 512a5.12 5.12 0 0 1 5.12-5.12h61.44a5.12 5.12 0 0 1 5.12 5.12 5.12 5.12 0 0 1-5.12 5.12h-61.44a5.12 5.12 0 0 1-5.12-5.12zM874.1376 543.68256a5.12 5.12 0 0 1 5.55008-4.6592l30.60736 2.68288a5.12 5.12 0 0 1 4.64896 5.55008 5.12 5.12 0 0 1-5.55008 4.64896l-30.59712-2.67264a5.12 5.12 0 0 1-4.6592-5.55008zM839.74144 569.78432a5.12 5.12 0 0 1 5.92896-4.1472l60.50816 10.67008a5.12 5.12 0 0 1 4.1472 5.92896 5.12 5.12 0 0 1-5.92896 4.1472l-60.50816-10.65984a5.12 5.12 0 0 1-4.1472-5.9392zM863.1296 606.08512a5.12 5.12 0 0 1 6.27712-3.61472l29.66528 7.94624a5.12 5.12 0 0 1 3.62496 6.26688 5.12 5.12 0 0 1-6.26688 3.62496l-29.67552-7.94624a5.12 5.12 0 0 1-3.62496-6.27712zM824.7296 625.82784a5.12 5.12 0 0 1 6.5536-3.06176l57.74336 21.01248a5.12 5.12 0 0 1 3.06176 6.56384 5.12 5.12 0 0 1-6.56384 3.06176l-57.73312-21.01248a5.12 5.12 0 0 1-3.06176-6.56384zM841.46176 665.63072a5.12 5.12 0 0 1 6.79936-2.47808l27.8528 12.98432a5.12 5.12 0 0 1 2.46784 6.79936 5.12 5.12 0 0 1-6.79936 2.47808l-27.8528-12.98432a5.12 5.12 0 0 1-2.46784-6.79936zM800.21504 678.4a5.12 5.12 0 0 1 6.99392-1.87392l53.20704 30.72a5.12 5.12 0 0 1 1.87392 6.99392 5.12 5.12 0 0 1-6.99392 1.87392l-53.20704-30.72a5.12 5.12 0 0 1-1.87392-6.99392zM809.7792 720.4864a5.12 5.12 0 0 1 7.13728-1.24928l25.15968 17.63328a5.12 5.12 0 0 1 1.24928 7.12704 5.12 5.12 0 0 1-7.12704 1.24928l-25.15968-17.62304a5.12 5.12 0 0 1-1.24928-7.13728z\" fill=\"#FFFFFF\" ></path><path d=\"M766.94528 725.9136a5.12 5.12 0 0 1 7.20896-0.6144l47.06304 39.48544a5.12 5.12 0 0 1 0.63488 7.20896 5.12 5.12 0 0 1-7.20896 0.63488l-47.07328-39.49568a5.12 5.12 0 0 1-0.62464-7.20896z\" fill=\"#FFFFFF\" ></path><path d=\"M769.04448 769.04448a5.12 5.12 0 0 1 7.23968 0l21.72928 21.71904a5.12 5.12 0 0 1 0 7.24992 5.12 5.12 0 0 1-7.24992 0l-21.7088-21.72928a5.12 5.12 0 0 1 0-7.23968zM725.9136 766.94528a5.12 5.12 0 0 1 7.20896 0.62464l39.50592 47.06304a5.12 5.12 0 0 1-0.62464 7.2192 5.12 5.12 0 0 1-7.2192-0.62464l-39.50592-47.07328a5.12 5.12 0 0 1 0.62464-7.20896zM720.4864 809.7792a5.12 5.12 0 0 1 7.12704 1.25952l17.63328 25.15968a5.12 5.12 0 0 1-1.24928 7.12704 5.12 5.12 0 0 1-7.12704-1.24928l-17.63328-25.15968a5.12 5.12 0 0 1 1.24928-7.12704zM678.4 800.21504a5.12 5.12 0 0 1 6.99392 1.87392l30.72 53.20704a5.12 5.12 0 0 1-1.87392 6.99392 5.12 5.12 0 0 1-6.99392-1.87392l-30.72-53.20704a5.12 5.12 0 0 1 1.87392-6.99392zM665.63072 841.46176a5.12 5.12 0 0 1 6.79936 2.47808l12.98432 27.84256a5.12 5.12 0 0 1-2.47808 6.79936 5.12 5.12 0 0 1-6.79936-2.47808l-12.98432-27.84256a5.12 5.12 0 0 1 2.47808-6.79936zM625.82784 824.70912a5.12 5.12 0 0 1 6.56384 3.06176l21.01248 57.7536a5.12 5.12 0 0 1-3.06176 6.56384 5.12 5.12 0 0 1-6.56384-3.06176l-21.01248-57.7536a5.12 5.12 0 0 1 3.06176-6.56384zM606.08512 863.1296a5.12 5.12 0 0 1 6.26688 3.61472l7.95648 29.67552a5.12 5.12 0 0 1-3.61472 6.27712 5.12 5.12 0 0 1-6.27712-3.61472l-7.95648-29.67552a5.12 5.12 0 0 1 3.61472-6.27712zM569.78432 839.75168a5.12 5.12 0 0 1 5.9392 4.1472l10.65984 60.5184a5.12 5.12 0 0 1-4.1472 5.91872 5.12 5.12 0 0 1-5.9392-4.1472l-10.65984-60.50816a5.12 5.12 0 0 1 4.1472-5.9392zM543.6928 874.14784a5.12 5.12 0 0 1 5.53984 4.6592l2.67264 30.59712a5.12 5.12 0 0 1-4.6592 5.53984 5.12 5.12 0 0 1-5.53984-4.64896l-2.67264-30.59712a5.12 5.12 0 0 1 4.6592-5.55008zM511.97952 844.82048a5.12 5.12 0 0 1 5.12 5.12v61.44a5.12 5.12 0 0 1-5.12 5.12 5.12 5.12 0 0 1-5.12-5.12v-61.44a5.12 5.12 0 0 1 5.12-5.12zM480.29696 874.15808a5.12 5.12 0 0 1 4.64896 5.55008l-2.67264 30.59712a5.12 5.12 0 0 1-5.53984 4.64896 5.12 5.12 0 0 1-4.6592-5.53984l2.67264-30.59712a5.12 5.12 0 0 1 5.53984-4.6592zM454.21568 839.74144a5.12 5.12 0 0 1 4.1472 5.9392l-10.69056 60.5184a5.12 5.12 0 0 1-5.92896 4.1472 5.12 5.12 0 0 1-4.15744-5.92896l10.69056-60.5184a5.12 5.12 0 0 1 5.9392-4.15744zM417.8944 863.15008a5.12 5.12 0 0 1 3.62496 6.27712l-7.95648 29.67552a5.12 5.12 0 0 1-6.26688 3.61472 5.12 5.12 0 0 1-3.62496-6.27712l7.95648-29.67552a5.12 5.12 0 0 1 6.27712-3.61472zM398.16192 824.75008a5.12 5.12 0 0 1 3.06176 6.5536l-21.01248 57.74336a5.12 5.12 0 0 1-6.56384 3.06176 5.12 5.12 0 0 1-3.06176-6.56384l21.01248-57.73312a5.12 5.12 0 0 1 6.56384-3.06176zM358.3488 841.48224a5.12 5.12 0 0 1 2.47808 6.79936l-12.98432 27.8528a5.12 5.12 0 0 1-6.79936 2.46784 5.12 5.12 0 0 1-2.47808-6.79936l12.98432-27.8528a5.12 5.12 0 0 1 6.79936-2.46784zM345.57952 800.23552a5.12 5.12 0 0 1 1.87392 6.99392l-30.72 53.20704a5.12 5.12 0 0 1-6.99392 1.87392 5.12 5.12 0 0 1-1.87392-6.99392l30.72-53.20704a5.12 5.12 0 0 1 6.99392-1.87392zM303.49312 809.80992a5.12 5.12 0 0 1 1.24928 7.12704l-17.63328 25.15968a5.12 5.12 0 0 1-7.12704 1.24928 5.12 5.12 0 0 1-1.24928-7.12704l17.63328-25.15968a5.12 5.12 0 0 1 7.12704-1.24928zM298.06592 766.95552a5.12 5.12 0 0 1 0.63488 7.20896l-39.49568 47.06304a5.12 5.12 0 0 1-7.20896 0.63488 5.12 5.12 0 0 1-0.63488-7.20896l39.49568-47.07328a5.12 5.12 0 0 1 7.20896-0.62464zM254.93504 769.06496a5.12 5.12 0 0 1 0 7.23968l-21.71904 21.72928a5.12 5.12 0 0 1-7.24992 0 5.12 5.12 0 0 1 0-7.24992l21.72928-21.7088a5.12 5.12 0 0 1 7.23968 0zM214.20032 720.50688a5.12 5.12 0 0 1-1.25952 7.13728l-25.15968 17.63328a5.12 5.12 0 0 1-7.12704-1.25952 5.12 5.12 0 0 1 1.24928-7.12704l25.15968-17.63328a5.12 5.12 0 0 1 7.12704 1.24928zM223.76448 678.42048a5.12 5.12 0 0 1-1.87392 6.99392l-53.20704 30.72a5.12 5.12 0 0 1-6.99392-1.87392 5.12 5.12 0 0 1 1.87392-6.99392l53.20704-30.72a5.12 5.12 0 0 1 6.99392 1.87392zM182.51776 665.6512a5.12 5.12 0 0 1-2.47808 6.79936l-27.84256 12.98432a5.12 5.12 0 0 1-6.79936-2.47808 5.12 5.12 0 0 1 2.47808-6.79936l27.84256-12.98432a5.12 5.12 0 0 1 6.79936 2.47808zM199.24992 625.83808a5.12 5.12 0 0 1-3.06176 6.5536l-57.73312 21.02272a5.12 5.12 0 0 1-6.5536-3.06176 5.12 5.12 0 0 1 3.05152-6.5536l57.73312-21.02272a5.12 5.12 0 0 1 6.56384 3.06176zM160.84992 606.1056a5.12 5.12 0 0 1-3.61472 6.26688l-29.68576 7.95648a5.12 5.12 0 0 1-6.26688-3.61472 5.12 5.12 0 0 1 3.61472-6.27712l29.67552-7.95648a5.12 5.12 0 0 1 6.27712 3.61472zM184.25856 569.78432a5.12 5.12 0 0 1-4.15744 5.9392l-60.5184 10.68032a5.12 5.12 0 0 1-5.9392-4.1472 5.12 5.12 0 0 1 4.15744-5.9392l60.5184-10.68032a5.12 5.12 0 0 1 5.9392 4.1472zM149.84192 543.71328a5.12 5.12 0 0 1-4.6592 5.53984l-30.59712 2.67264a5.12 5.12 0 0 1-5.53984-4.64896 5.12 5.12 0 0 1 4.64896-5.55008l30.59712-2.67264a5.12 5.12 0 0 1 5.55008 4.6592zM179.17952 512.02048a5.12 5.12 0 0 1-5.12 5.12h-61.44a5.12 5.12 0 0 1-5.12-5.12 5.12 5.12 0 0 1 5.12-5.12h61.44a5.12 5.12 0 0 1 5.12 5.12zM149.85216 480.33792a5.12 5.12 0 0 1-5.55008 4.6592l-30.60736-2.68288a5.12 5.12 0 0 1-4.64896-5.55008 5.12 5.12 0 0 1 5.55008-4.64896l30.59712 2.67264a5.12 5.12 0 0 1 4.6592 5.55008zM184.23808 454.22592a5.12 5.12 0 0 1-5.92896 4.1472l-60.50816-10.67008a5.12 5.12 0 0 1-4.1472-5.92896 5.12 5.12 0 0 1 5.92896-4.1472l60.50816 10.65984a5.12 5.12 0 0 1 4.1472 5.9392zM160.84992 417.93536a5.12 5.12 0 0 1-6.27712 3.61472l-29.66528-7.94624a5.12 5.12 0 0 1-3.62496-6.26688 5.12 5.12 0 0 1 6.26688-3.62496l29.67552 7.94624a5.12 5.12 0 0 1 3.62496 6.27712zM199.24992 398.19264a5.12 5.12 0 0 1-6.5536 3.06176l-57.74336-21.01248a5.12 5.12 0 0 1-3.06176-6.56384 5.12 5.12 0 0 1 6.56384-3.06176l57.73312 21.01248a5.12 5.12 0 0 1 3.06176 6.56384zM182.51776 358.37952a5.12 5.12 0 0 1-6.79936 2.47808l-27.8528-12.98432a5.12 5.12 0 0 1-2.46784-6.79936 5.12 5.12 0 0 1 6.79936-2.47808l27.8528 12.98432a5.12 5.12 0 0 1 2.46784 6.79936zM223.76448 345.62048a5.12 5.12 0 0 1-6.99392 1.87392l-53.20704-30.72a5.12 5.12 0 0 1-1.87392-6.99392 5.12 5.12 0 0 1 6.99392-1.87392l53.20704 30.72a5.12 5.12 0 0 1 1.87392 6.99392zM214.20032 303.52384a5.12 5.12 0 0 1-7.13728 1.24928l-25.15968-17.63328a5.12 5.12 0 0 1-1.24928-7.12704 5.12 5.12 0 0 1 7.12704-1.24928l25.15968 17.62304a5.12 5.12 0 0 1 1.25952 7.13728zM257.04448 298.09664a5.12 5.12 0 0 1-7.20896 0.63488l-47.06304-39.49568a5.12 5.12 0 0 1-0.63488-7.20896 5.12 5.12 0 0 1 7.20896-0.63488l47.07328 39.49568a5.12 5.12 0 0 1 0.62464 7.20896zM254.93504 254.96576a5.12 5.12 0 0 1-7.23968 0l-21.72928-21.71904a5.12 5.12 0 0 1 0-7.24992 5.12 5.12 0 0 1 7.24992 0l21.7088 21.72928a5.12 5.12 0 0 1 0 7.23968zM298.0864 257.0752a5.12 5.12 0 0 1-7.2192-0.62464l-39.50592-47.06304a5.12 5.12 0 0 1 0.62464-7.2192 5.12 5.12 0 0 1 7.2192 0.63488l39.50592 47.06304a5.12 5.12 0 0 1-0.63488 7.20896zM303.49312 214.24128a5.12 5.12 0 0 1-7.12704-1.25952l-17.63328-25.15968a5.12 5.12 0 0 1 1.24928-7.12704 5.12 5.12 0 0 1 7.12704 1.24928l17.63328 25.15968a5.12 5.12 0 0 1-1.24928 7.13728zM345.58976 223.80544a5.12 5.12 0 0 1-6.99392-1.87392l-30.72-53.20704a5.12 5.12 0 0 1 1.87392-6.99392 5.12 5.12 0 0 1 6.99392 1.87392l30.72 53.20704a5.12 5.12 0 0 1-1.87392 6.99392zM358.3488 182.55872a5.12 5.12 0 0 1-6.79936-2.47808l-12.98432-27.84256a5.12 5.12 0 0 1 2.47808-6.79936 5.12 5.12 0 0 1 6.79936 2.47808l12.98432 27.84256a5.12 5.12 0 0 1-2.47808 6.79936zM398.16192 199.31136a5.12 5.12 0 0 1-6.56384-3.06176l-21.01248-57.7536a5.12 5.12 0 0 1 3.06176-6.56384 5.12 5.12 0 0 1 6.56384 3.06176l21.01248 57.7536a5.12 5.12 0 0 1-3.06176 6.56384zM417.91488 160.88064a5.12 5.12 0 0 1-6.27712-3.61472l-7.95648-29.67552a5.12 5.12 0 0 1 3.61472-6.27712 5.12 5.12 0 0 1 6.27712 3.61472l7.95648 29.68576a5.12 5.12 0 0 1-3.61472 6.26688zM454.1952 184.2688a5.12 5.12 0 0 1-5.9392-4.1472l-10.65984-60.5184a5.12 5.12 0 0 1 4.1472-5.91872 5.12 5.12 0 0 1 5.9392 4.1472l10.65984 60.50816a5.12 5.12 0 0 1-4.1472 5.9392zM480.29696 149.87264a5.12 5.12 0 0 1-5.55008-4.6592l-2.67264-30.59712a5.12 5.12 0 0 1 4.6592-5.53984 5.12 5.12 0 0 1 5.53984 4.6592l2.67264 30.58688a5.12 5.12 0 0 1-4.64896 5.55008z\" fill=\"#FFFFFF\" ></path><path d=\"M477.6448 472.18688l344.41216-222.208-311.99232 262.44096z\" fill=\"#ED6C60\" ></path><path d=\"M510.0544 512.4096l312.0128-262.3488-279.08096 302.97088z\" fill=\"#B73C2F\" ></path><path d=\"M203.06944 770.70336l274.5344-298.53696 32.4608 40.2432z\" fill=\"#FFFFFF\" ></path><path d=\"M203.0592 770.6624l306.9952-258.2528 32.9728 40.6528z\" fill=\"#C7C7C7\" ></path></symbol><symbol id=\"p-m-360se\" viewBox=\"0 0 1024 1024\"><path d=\"M384.768 582.08v1.856c0 30.4 6.72 59.584 20.288 87.424 16 29.184 37.824 52.288 65.472 69.44 27.648 17.024 58.368 25.6 92.16 25.6a172.16 172.16 0 0 0 92.224-25.6 184.384 184.384 0 0 0 65.408-69.44h219.456a417.28 417.28 0 0 1-147.52 202.496 383.552 383.552 0 0 1-236.032 78.912 403.328 403.328 0 0 1-184.32-43.776c-129.088 64.64-221.888 67.84-278.4 9.536C73.792 899.52 64 867.84 64 823.488c0-44.352 8.32-93.824 24.96-148.288 16.512-54.528 44.16-114.432 82.944-179.712A909.76 909.76 0 0 1 309.12 317.76c30.72-32.96 50.432-53.248 59.008-60.8-76.224 37.952-145.088 96.896-206.464 176.704a409.28 409.28 0 0 1 142.848-228.16 387.2 387.2 0 0 1 251.648-89.344c9.856 0 19.648 0.64 29.44 1.92 68.928-31.744 131.584-49.408 188.16-53.248 56.512-3.84 96.448 5.696 119.808 28.48 46.72 49.472 51.008 126.72 12.864 231.936A427.136 427.136 0 0 1 960 534.528c0 17.728-0.64 33.6-1.792 47.552h-573.44z m-38.656 311.808c-82.368-51.968-140.16-124.8-173.376-218.688-55.296 111.552-62.656 188.224-22.144 230.08 35.712 36.736 100.8 32.96 195.52-11.392z m379.776-422.08a170.944 170.944 0 0 0-53.44-116.928c-33.28-32.32-72.256-48.512-117.12-48.512-44.864 0-83.84 16.192-117.056 48.512a170.816 170.816 0 0 0-53.504 116.928h341.12z m-20.288-325.12c78.592 32.96 141.312 86.144 188.032 159.68 28.288-78.592 27.008-133.696-3.712-165.44-33.152-35.456-94.656-33.536-184.32 5.76z\" fill=\"#00CD00\" ></path></symbol><symbol id=\"p-Firefox\" viewBox=\"0 0 1024 1024\"><path d=\"M208.979592 1024h606.040816c115.461224 0 208.979592-93.518367 208.979592-208.979592V208.979592C1024 93.518367 930.481633 0 815.020408 0H208.979592C93.518367 0 0 93.518367 0 208.979592v606.040816c0 115.461224 93.518367 208.979592 208.979592 208.979592z\" fill=\"#20123A\" ></path><path d=\"M815.856327 255.164082c23.614694 46.497959 37.198367 93.204898 42.422857 128v0.835918c39.497143 107.937959 17.972245 217.547755-13.061225 284.630204-47.960816 103.758367-164.04898 210.02449-345.756735 204.8C303.124898 867.892245 130.089796 720.979592 97.802449 528.718367c-5.851429-30.302041 0-45.766531 3.030204-70.321632-3.657143 19.017143-5.01551 24.450612-6.791837 58.200816v2.089796C94.040816 751.595102 281.39102 940.408163 512.626939 940.408163c206.994286 0 378.88-151.405714 412.525714-350.354285 0.731429-5.32898 1.253878-10.762449 1.880816-16.195919 8.254694-72.411429-0.940408-148.37551-27.271836-212.009796v0.10449c-18.076735-44.199184-54.961633-91.742041-83.905306-106.788571z\" fill=\"#D51257\" ></path><path d=\"M699.768163 416.600816c-10.44898-18.808163-23.614694-36.04898-38.870204-51.09551-130.507755-131.134694-34.272653-284.316735-17.972245-292.048979l0.10449-0.20898c-105.325714 62.066939-141.061224 176.796735-144.404898 234.266122 4.91102-0.313469 9.822041-0.731429 14.733061-0.731428 76.173061 0.10449 146.390204 41.377959 183.797551 107.937959v-0.10449l2.612245 1.985306z\" fill=\"#FFD137\" ></path><path d=\"M463.203265 487.549388c-79.516735 0-125.805714 24.137143-138.657959 72.515918 5.22449 61.126531 47.647347 111.386122 98.847347 138.031021 2.298776 1.149388 4.702041 2.298776 7.000816 3.343673 4.075102 1.776327 8.150204 3.552653 12.225307 5.12 17.658776 6.164898 36.04898 9.717551 54.648163 10.553469 209.084082 9.822041 249.730612-251.402449 98.742857-327.26204 38.661224-6.687347 78.785306 8.986122 101.146122 24.659591a211.853061 211.853061 0 0 0-183.797551-107.833469c-5.01551 0-9.822041 0.417959-14.837551 0.835918a209.293061 209.293061 0 0 0-115.356734 44.721633c6.373878 5.433469 13.583673 12.747755 28.839183 27.794286 28.421224 28.212245 101.564082 57.364898 101.668572 60.813061-0.731429 10.44898-37.511837 46.706939-50.468572 46.706939z\" fill=\"#6C66EC\" ></path><path d=\"M372.297143 343.980408c-9.613061-33.645714-9.926531-69.172245-1.149388-103.026939-43.154286 19.748571-76.69551 50.886531-101.041633 78.471837 1.985306-0.10449 62.798367-1.149388 93.413878 18.808163l8.777143 5.746939z\" fill=\"#FF9E3E\" ></path><path d=\"M97.17551 531.957551c-7.418776-34.063673 7.523265-99.474286 7.836735-100.310204 10.24-45.97551 29.361633-89.338776 56.32-127.79102 11.702857-16.822857 24.868571-32.600816 39.183673-47.229388 12.643265-12.956735 26.331429-24.764082 41.06449-35.317551 1.567347-1.149388 13.688163-8.359184 13.897143-7.418776-0.940408 14.524082-2.089796 84.323265 14.524082 105.534694h0.208979c1.985306 0 62.902857-1.149388 93.413878 18.912653 3.552653 2.298776 6.269388 3.970612 8.777143 5.746939 3.761633 2.507755 7.418776 5.32898 10.866938 8.254694 6.478367 5.433469 13.688163 12.747755 28.839184 27.794286 28.525714 28.212245 101.459592 57.364898 101.668572 60.813061 0 0-76.277551 32.182857-157.570613-6.164898-58.409796-27.689796-109.923265-25.6-154.226938 6.164898 16.822857 41.900408 49.946122 62.798367 99.369795 62.798367-7.732245 68.963265 28.525714 149.420408 116.297143 191.529796 1.985306 1.044898 4.075102 2.089796 6.060408 3.030204 29.257143 19.957551 173.348571 68.858776 323.918368-54.334694 0-0.20898 0.10449-0.417959 0.208979-0.626939 77.00898-63.738776 126.432653-158.093061 111.908572-255.58204 39.288163 107.72898 17.763265 217.129796-13.165714 284.003265-48.169796 103.758367-164.571429 210.02449-346.801633 204.90449C302.915918 871.026939 129.567347 724.114286 97.17551 531.957551z m762.566531-144.195918c0-0.20898-0.10449-0.417959-0.20898-0.522449v-0.835919l0.10449 0.626939c0.10449 0.313469 0.10449 0.522449 0.10449 0.731429z\" fill=\"#FF4F38\" ></path><path d=\"M699.768163 416.600816c2.298776 1.671837 4.388571 3.448163 6.373878 5.22449 95.085714 88.084898 45.348571 212.636735 41.586939 221.622857 77.217959-64.052245 126.641633-158.72 111.804081-256.313469-47.438367-119.013878-127.89551-166.870204-193.619592-271.25551-3.448163-5.32898-6.687347-10.657959-9.92653-16.195919-1.567347-2.821224-3.134694-5.746939-4.597551-8.672653-2.716735-5.32898-4.806531-10.866939-6.269388-16.718367 0-0.522449-0.313469-0.940408-0.940408-1.044898-0.20898-0.10449-0.522449-0.10449-0.835919 0 0 0-0.10449 0-0.104489 0.10449l-0.31347 0.10449c-16.195918 7.836735-112.53551 160.914286 17.972245 292.048979 15.25551 15.15102 28.421224 32.287347 38.870204 51.09551z\" fill=\"#FFE544\" ></path><path d=\"M699.872653 416.496327l-2.716735-1.985307c-22.360816-15.777959-62.484898-31.346939-101.146122-24.659591 150.883265 75.859592 110.445714 336.979592-98.742857 327.157551-18.599184-0.731429-36.989388-4.284082-54.543674-10.55347-6.582857-2.507755-13.061224-5.32898-19.330612-8.359183l0.313469 0.104489c29.152653 19.957551 173.244082 68.858776 323.813878-54.334694l0.20898-0.626938c3.761633-8.777143 53.498776-133.433469-41.691429-221.413878h0.10449c-1.985306-1.880816-4.075102-3.657143-6.269388-5.328979z\" fill=\"#FF9800\" ></path><path d=\"M463.098776 487.549388c12.852245 0 49.737143-36.153469 50.364081-46.706939 0.731429-10.553469-76.173061 32.182857-157.466122-6.164898-58.514286-27.689796-109.923265-25.6-154.226939 6.164898 16.822857 41.900408 49.946122 62.798367 99.369796 62.798367-7.836735 68.963265 28.421224 149.420408 116.297143 191.634286 1.985306 0.835918 3.761633 1.880816 5.746938 2.821225-51.2-26.540408-93.518367-76.90449-98.847346-138.031021h0.104489c12.852245-48.378776 59.141224-72.515918 138.65796-72.515918z\" fill=\"#FFD439\" ></path></symbol><symbol id=\"p-docker1\" viewBox=\"0 0 1024 1024\"><path d=\"M1006.90944 410.9056c-39.16288-26.5984-85.02272-27.17184-115.35872-23.38304-15.02208-63.06304-72.18176-98.7648-75.00288-100.49536l-21.60128-13.21472-15.43168 20.08064c-48.77824 63.49312-35.87584 132.59776-15.25248 175.16032-25.69728 13.5936-51.33312 14.09024-51.52256 14.09024l-17.32096 0L695.41888 378.39872l-118.08256 0L577.33632 316.82048 577.33632 276.2752 577.33632 174.14656 434.6624 174.14656 434.6624 276.2752 343.51616 276.2752l-25.76384 0L200.83712 276.2752l0 102.12352L83.92192 378.39872l0 104.73984L7.66464 483.13856l-3.63008 23.94112c-0.8192 5.42208-19.12832 133.79584 66.35008 233.20576 62.51008 72.704 162.41152 109.568 296.91904 109.568 207.80032 0 335.47264-91.53024 405.97504-168.31488 54.99904-59.89376 84.37248-119.66976 96.93184-149.7856 130.16064-2.18624 147.84-88.52992 148.56192-92.38528l3.38432-18.10944L1006.90944 410.9056zM475.6224 377.99424 475.6224 317.2352l60.75904 0 0 60.75904L475.6224 377.99424zM241.79712 480.11264 241.79712 419.35872l60.75904 0 0 60.75392L241.79712 480.11264zM358.71232 480.11264 358.71232 419.35872l60.75392 0 0 60.75392L358.71232 480.11264zM475.6224 480.11264 475.6224 419.35872l60.75904 0 0 60.75392L475.6224 480.11264zM593.71008 419.35872l60.7488 0 0 60.75392-60.7488 0L593.71008 419.35872zM475.6224 215.10656l60.75904 0 0 60.75392L475.6224 275.86048 475.6224 215.10656zM358.71232 317.2352l60.75392 0 0 60.75904L358.71232 377.99424 358.71232 317.2352zM241.79712 317.2352l60.75904 0 0 60.75904L241.79712 377.99424 241.79712 317.2352zM124.88192 419.35872l60.75904 0 0 60.75392L124.88192 480.11264 124.88192 419.35872zM367.30368 793.53344c-63.86176 0-118.2976-8.96512-162.93376-26.75712 33.11616-6.95808 70.84544-19.92704 104.80128-43.81184 12.71808-8.94464 15.77984-26.51136 6.83008-39.22944s-26.51136-15.79008-39.23456-6.83008c-55.19872 38.83008-126.52032 41.54368-149.82144 41.2672-4.72576-4.52096-9.216-9.24672-13.47072-14.16704-49.07008-56.74496-55.54176-128.04608-55.17824-164.55168l654.44864 0c2.47296 0 61.14816-0.46592 107.61216-40.97536l3.5072-3.05664C818.48832 511.17568 716.83584 793.53344 367.30368 793.53344zM852.25472 475.16672l-17.40288-0.82944-10.47552-13.97248c-0.44032-0.58368-37.49376-51.12832-15.0272-106.56256 14.38208 14.91456 30.2592 37.97504 29.43488 66.57024l-1.152 39.9104 37.98016-12.30336c0.48128-0.1536 40.36608-12.31872 77.09184-1.12128C939.67872 461.00992 911.49824 478.0032 852.25472 475.16672z\"  ></path><path d=\"M379.25888 589.57312c-25.34912 0-45.96736 20.61824-45.96736 45.97248 0 25.344 20.61824 45.96736 45.96736 45.96736 25.344 0 45.96736-20.61824 45.96736-45.96736C425.22112 610.19648 404.60288 589.57312 379.25888 589.57312zM379.25888 645.89312c-5.7088 0-10.35264-4.64384-10.35264-10.34752 0-5.7088 4.64384-10.35264 10.35264-10.35264 5.7088 0 10.35264 4.64384 10.35264 10.35264C389.61152 641.24928 384.96256 645.89312 379.25888 645.89312z\"  ></path></symbol><symbol id=\"p-alert-1\" viewBox=\"0 0 1024 1024\"><path d=\"M918.50586112 294.78086656c-9.30092829-9.26143374-21.72191289-9.26143374-34.13302386-6.17099833v-49.36798549c0-52.45842091-40.33364423-92.56497152-93.08827307-92.56497152H232.71543581c-52.75462883 0-93.08827193 40.11642425-93.08827307 92.56497152v52.45842091c-12.41111097-6.18087197-24.83209671-3.09043541-34.13302386 6.16112469C86.87253504 313.30373405 77.56173312 337.98772622 77.56173312 362.67171954v431.96986823C77.56173312 847.09013504 117.90524985 887.19668565 170.65987869 887.19668565H853.33024768c52.75462883 0 93.09814557-40.11642425 93.0981467-92.56497152v-431.96986823c0-24.68399218-9.31080192-49.36798549-27.93240689-67.88097934zM443.73395001 677.3827493c37.23333405 37.02598883 96.19845575 37.02598883 130.33148074 0l55.85493789-55.53898269 211.00864171 200.56237512H183.07098965l204.80802248-203.65281167 55.85493788 58.62941924z m232.7305546-98.73596985l207.90833265-206.73337344v407.28587605L676.46450461 578.64677945z m-443.7490688-370.25988722h558.56912838c18.61173021 0 31.03271481 12.34199666 31.03271595 30.85499051v107.99740473L533.72191289 634.17588963c-12.41111097 12.34199666-31.03271481 12.34199666-43.44382578 0l-102.39907498-98.73596984-186.19629227-185.12994418v-111.07796651c0-18.51299385 12.42098461-30.85499051 31.03271595-30.85499051z m111.7197494 370.25988722L139.62716274 782.28971747v-407.28587605L344.43518521 578.64677945z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-alert-2\" viewBox=\"0 0 1024 1024\"><path d=\"M721.06666666 298.46186667v-51.2a8.53333333 8.53333333 0 0 0-8.53333333-8.53333334h-409.6a8.53333333 8.53333333 0 0 0-8.53333333 8.53333334v51.2c0 4.77866667 3.82293333 8.53333333 8.53333333 8.53333333h409.6a8.53333333 8.53333333 0 0 0 8.53333333-8.53333333zM302.93333333 392.32853333a8.53333333 8.53333333 0 0 0-8.53333333 8.53333334v51.2c0 4.77866667 3.82293333 8.53333333 8.53333333 8.53333333H499.2a8.53333333 8.53333333 0 0 0 8.53333333-8.53333333v-51.2a8.53333333 8.53333333 0 0 0-8.53333333-8.53333334H302.93333333zM704 516.096a230.4 230.4 0 1 0 0 460.8 230.4 230.4 0 0 0 0-460.8z m114.688 345.088a161.10933333 161.10933333 0 0 1-114.688 47.44533333 161.10933333 161.10933333 0 0 1-114.688-47.44533333 161.10933333 161.10933333 0 0 1-47.44533334-114.688c0-43.28106667 16.86186667-84.03626667 47.44533334-114.688a161.10933333 161.10933333 0 0 1 114.688-47.44533333 161.10933333 161.10933333 0 0 1 114.688 47.44533333 161.10933333 161.10933333 0 0 1 47.44533333 114.688 161.10933333 161.10933333 0 0 1-47.44533333 114.688z m-148.82133334-29.35466667a34.13333333 34.13333333 0 1 0 68.26666667 0 34.13333333 34.13333333 0 0 0-68.26666667 0z m12.83413334-68.26666666h42.5984a8.53333333 8.53333333 0 0 0 8.6016-8.53333334V635.56266667a8.53333333 8.53333333 0 0 0-8.6016-8.53333334h-42.5984a8.53333333 8.53333333 0 0 0-8.6016 8.53333334v119.46666666c0 4.7104 3.8912 8.53333333 8.6016 8.53333334zM456.46506666 874.496H209.06666666v-750.93333333H806.4v366.93333333c0 4.7104 3.82293333 8.53333333 8.53333333 8.53333333H874.66666666a8.53333333 8.53333333 0 0 0 8.53333334-8.53333333v-409.6a34.13333333 34.13333333 0 0 0-34.13333334-34.13333333h-682.66666666a34.13333333 34.13333333 0 0 0-34.13333334 34.13333333v836.26666667a34.13333333 34.13333333 0 0 0 34.13333334 34.13333333h290.13333333A8.53333333 8.53333333 0 0 0 465.06666666 942.76266667v-59.73333334a8.53333333 8.53333333 0 0 0-8.53333333-8.53333333z\" fill=\"#1296db\" ></path></symbol><symbol id=\"p-17\" viewBox=\"0 0 1024 1024\"><path d=\"M682.666667 128.170667h128c49.322667 0 86.954667 42.752 86.954666 93.696v665.770666c0 50.901333-37.674667 93.738667-86.954666 93.738667H215.893333c-49.322667 0-86.997333-42.794667-86.997333-93.738667V221.866667c0-50.944 37.546667-93.696 86.997333-93.696H341.333333V64a21.333333 21.333333 0 0 1 42.666667 0v170.666667a21.333333 21.333333 0 0 1-42.666667 0V170.837333H215.893333c-24.533333 0-44.330667 22.485333-44.330666 51.029334v665.770666c0 28.501333 19.84 51.072 44.330666 51.072H810.666667c24.448 0 44.288-22.613333 44.288-51.072V221.866667c0-28.501333-19.797333-51.029333-44.288-51.029334h-128V234.666667a21.333333 21.333333 0 1 1-42.666667 0v-170.666667a21.333333 21.333333 0 1 1 42.666667 0v64.170667z\"  ></path><path d=\"M447.872 170.666667h127.786667a21.333333 21.333333 0 1 0 0-42.666667h-127.786667a21.333333 21.333333 0 1 0 0 42.666667zM341.632 384h341.333333a21.333333 21.333333 0 0 0 0-42.666667h-341.333333a21.333333 21.333333 0 0 0 0 42.666667zM341.632 554.666667h170.666667a21.333333 21.333333 0 1 0 0-42.666667h-170.666667a21.333333 21.333333 0 1 0 0 42.666667zM341.632 725.333333h341.077333a21.333333 21.333333 0 1 0 0-42.666666H341.632a21.333333 21.333333 0 1 0 0 42.666666z\"  ></path></symbol></svg>',(c=>{var l=(a=(a=document.getElementsByTagName(\"script\"))[a.length-1]).getAttribute(\"data-injectcss\"),a=a.getAttribute(\"data-disable-injectsvg\");if(!a){var h,t,z,p,v,m=function(l,a){a.parentNode.insertBefore(l,a)};if(l&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write(\"<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>\")}catch(l){console&&console.log(l)}}h=function(){var l,a=document.createElement(\"div\");a.innerHTML=c._iconfont_svg_string_4776196,(a=a.getElementsByTagName(\"svg\")[0])&&(a.setAttribute(\"aria-hidden\",\"true\"),a.style.position=\"absolute\",a.style.width=0,a.style.height=0,a.style.overflow=\"hidden\",a=a,(l=document.body).firstChild?m(a,l.firstChild):l.appendChild(a))},document.addEventListener?~[\"complete\",\"loaded\",\"interactive\"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener(\"DOMContentLoaded\",t,!1),h()},document.addEventListener(\"DOMContentLoaded\",t,!1)):document.attachEvent&&(z=h,p=c.document,v=!1,d(),p.onreadystatechange=function(){\"complete\"==p.readyState&&(p.onreadystatechange=null,M())})}function M(){v||(v=!0,z())}function d(){try{p.documentElement.doScroll(\"left\")}catch(l){return void setTimeout(d,50)}M()}})(window);"
  },
  {
    "path": "frontend/src/assets/iconfont/iconfont.json",
    "content": "{\n  \"id\": \"4776196\",\n  \"name\": \"1Panel-V2\",\n  \"font_family\": \"iconfont\",\n  \"css_prefix_text\": \"p-\",\n  \"description\": \"\",\n  \"glyphs\": [\n    {\n      \"icon_id\": \"26213819\",\n      \"name\": \"退出登陆\",\n      \"font_class\": \"tuichudenglu3\",\n      \"unicode\": \"e62d\",\n      \"unicode_decimal\": 58925\n    },\n    {\n      \"icon_id\": \"32303204\",\n      \"name\": \"任务中心\",\n      \"font_class\": \"renwuzhongxin1\",\n      \"unicode\": \"e807\",\n      \"unicode_decimal\": 59399\n    },\n    {\n      \"icon_id\": \"3453887\",\n      \"name\": \"主机\",\n      \"font_class\": \"zhuji\",\n      \"unicode\": \"e629\",\n      \"unicode_decimal\": 58921\n    },\n    {\n      \"icon_id\": \"39180612\",\n      \"name\": \"pcm\",\n      \"font_class\": \"pcm\",\n      \"unicode\": \"e7b2\",\n      \"unicode_decimal\": 59314\n    },\n    {\n      \"icon_id\": \"5041257\",\n      \"name\": \"运行环境\",\n      \"font_class\": \"run-menu\",\n      \"unicode\": \"e636\",\n      \"unicode_decimal\": 58934\n    },\n    {\n      \"icon_id\": \"36768791\",\n      \"name\": \"SSH密钥\",\n      \"font_class\": \"ssh-menu\",\n      \"unicode\": \"e668\",\n      \"unicode_decimal\": 58984\n    },\n    {\n      \"icon_id\": \"44603268\",\n      \"name\": \"MCP服务\",\n      \"font_class\": \"mcp-menu\",\n      \"unicode\": \"e62b\",\n      \"unicode_decimal\": 58923\n    },\n    {\n      \"icon_id\": \"44724034\",\n      \"name\": \"线性- AI建站\",\n      \"font_class\": \"ai-menu\",\n      \"unicode\": \"e7bf\",\n      \"unicode_decimal\": 59327\n    },\n    {\n      \"icon_id\": \"45110983\",\n      \"name\": \"大模型-大\",\n      \"font_class\": \"moxing-menu\",\n      \"unicode\": \"e62c\",\n      \"unicode_decimal\": 58924\n    },\n    {\n      \"icon_id\": \"15170483\",\n      \"name\": \"ahas 应用高可用\",\n      \"font_class\": \"ah-menu\",\n      \"unicode\": \"e6ba\",\n      \"unicode_decimal\": 59066\n    },\n    {\n      \"icon_id\": \"1598478\",\n      \"name\": \"网站防篡改\",\n      \"font_class\": \"tamper-menu\",\n      \"unicode\": \"e61c\",\n      \"unicode_decimal\": 58908\n    },\n    {\n      \"icon_id\": \"3141923\",\n      \"name\": \"SSL数字证书\",\n      \"font_class\": \"ssl-menu\",\n      \"unicode\": \"e7b0\",\n      \"unicode_decimal\": 59312\n    },\n    {\n      \"icon_id\": \"4608954\",\n      \"name\": \"文件夹，文件管理\",\n      \"font_class\": \"file-menu\",\n      \"unicode\": \"e609\",\n      \"unicode_decimal\": 58889\n    },\n    {\n      \"icon_id\": \"5653301\",\n      \"name\": \"监控\",\n      \"font_class\": \"system-monitor-menu\",\n      \"unicode\": \"e621\",\n      \"unicode_decimal\": 58913\n    },\n    {\n      \"icon_id\": \"7082471\",\n      \"name\": \"gpu\",\n      \"font_class\": \"gpu-menu\",\n      \"unicode\": \"e631\",\n      \"unicode_decimal\": 58929\n    },\n    {\n      \"icon_id\": \"7948556\",\n      \"name\": \"WAF\",\n      \"font_class\": \"waf-menu\",\n      \"unicode\": \"e623\",\n      \"unicode_decimal\": 58915\n    },\n    {\n      \"icon_id\": \"18270117\",\n      \"name\": \"界面设置\",\n      \"font_class\": \"xsetting-menu\",\n      \"unicode\": \"e624\",\n      \"unicode_decimal\": 58916\n    },\n    {\n      \"icon_id\": \"35113302\",\n      \"name\": \"磁盘管理\",\n      \"font_class\": \"disk-menu\",\n      \"unicode\": \"e6e5\",\n      \"unicode_decimal\": 59109\n    },\n    {\n      \"icon_id\": \"43864633\",\n      \"name\": \"文件互传\",\n      \"font_class\": \"exchange-menu\",\n      \"unicode\": \"e625\",\n      \"unicode_decimal\": 58917\n    },\n    {\n      \"icon_id\": \"397898\",\n      \"name\": \"移动端，手机端\",\n      \"font_class\": \"mobile-menu\",\n      \"unicode\": \"e628\",\n      \"unicode_decimal\": 58920\n    },\n    {\n      \"icon_id\": \"5767874\",\n      \"name\": \"任务进程\",\n      \"font_class\": \"process-menu\",\n      \"unicode\": \"ec35\",\n      \"unicode_decimal\": 60469\n    },\n    {\n      \"icon_id\": \"14095351\",\n      \"name\": \"防火墙\",\n      \"font_class\": \"firewalld-menu\",\n      \"unicode\": \"e798\",\n      \"unicode_decimal\": 59288\n    },\n    {\n      \"icon_id\": \"15009371\",\n      \"name\": \"监测数据\",\n      \"font_class\": \"monitor-menu\",\n      \"unicode\": \"e64a\",\n      \"unicode_decimal\": 58954\n    },\n    {\n      \"icon_id\": \"22231530\",\n      \"name\": \"Data server\",\n      \"font_class\": \"node-menu\",\n      \"unicode\": \"e813\",\n      \"unicode_decimal\": 59411\n    },\n    {\n      \"icon_id\": \"18536446\",\n      \"name\": \"feature视图\",\n      \"font_class\": \"featureshitu\",\n      \"unicode\": \"e63e\",\n      \"unicode_decimal\": 58942\n    },\n    {\n      \"icon_id\": \"18133027\",\n      \"name\": \"优化文档\",\n      \"font_class\": \"youhuawendang\",\n      \"unicode\": \"e7c4\",\n      \"unicode_decimal\": 59332\n    },\n    {\n      \"icon_id\": \"88609\",\n      \"name\": \"cluster-3\",\n      \"font_class\": \"cluster-3\",\n      \"unicode\": \"e706\",\n      \"unicode_decimal\": 59142\n    },\n    {\n      \"icon_id\": \"663330\",\n      \"name\": \"cluster-2\",\n      \"font_class\": \"cluster-2\",\n      \"unicode\": \"e61f\",\n      \"unicode_decimal\": 58911\n    },\n    {\n      \"icon_id\": \"4766884\",\n      \"name\": \"cluster-1\",\n      \"font_class\": \"cluster-1\",\n      \"unicode\": \"e7d8\",\n      \"unicode_decimal\": 59352\n    },\n    {\n      \"icon_id\": \"15838584\",\n      \"name\": \"arrow-right-filling\",\n      \"font_class\": \"start\",\n      \"unicode\": \"e688\",\n      \"unicode_decimal\": 59016\n    },\n    {\n      \"icon_id\": \"212329\",\n      \"name\": \"stop\",\n      \"font_class\": \"stop\",\n      \"unicode\": \"e750\",\n      \"unicode_decimal\": 59216\n    },\n    {\n      \"icon_id\": \"7712749\",\n      \"name\": \"node-4\",\n      \"font_class\": \"node-4\",\n      \"unicode\": \"e73c\",\n      \"unicode_decimal\": 59196\n    },\n    {\n      \"icon_id\": \"10055631\",\n      \"name\": \"node-3\",\n      \"font_class\": \"node-3\",\n      \"unicode\": \"e7d7\",\n      \"unicode_decimal\": 59351\n    },\n    {\n      \"icon_id\": \"22553639\",\n      \"name\": \"file-2\",\n      \"font_class\": \"file-2\",\n      \"unicode\": \"e6bf\",\n      \"unicode_decimal\": 59071\n    },\n    {\n      \"icon_id\": \"25604138\",\n      \"name\": \"file-1\",\n      \"font_class\": \"file-1\",\n      \"unicode\": \"e742\",\n      \"unicode_decimal\": 59202\n    },\n    {\n      \"icon_id\": \"43808299\",\n      \"name\": \"file-3\",\n      \"font_class\": \"file-3\",\n      \"unicode\": \"e607\",\n      \"unicode_decimal\": 58887\n    },\n    {\n      \"icon_id\": \"29971586\",\n      \"name\": \"火爆\",\n      \"font_class\": \"huobao1\",\n      \"unicode\": \"e604\",\n      \"unicode_decimal\": 58884\n    },\n    {\n      \"icon_id\": \"10199157\",\n      \"name\": \"个人中心\",\n      \"font_class\": \"gerenzhongxin1\",\n      \"unicode\": \"e61e\",\n      \"unicode_decimal\": 58910\n    },\n    {\n      \"icon_id\": \"10505865\",\n      \"name\": \"机器人\",\n      \"font_class\": \"jiqiren2\",\n      \"unicode\": \"e61b\",\n      \"unicode_decimal\": 58907\n    },\n    {\n      \"icon_id\": \"5127551\",\n      \"name\": \"terminal\",\n      \"font_class\": \"terminal2\",\n      \"unicode\": \"e82f\",\n      \"unicode_decimal\": 59439\n    },\n    {\n      \"icon_id\": \"34213658\",\n      \"name\": \"推荐\",\n      \"font_class\": \"tuijian\",\n      \"unicode\": \"e627\",\n      \"unicode_decimal\": 58919\n    },\n    {\n      \"icon_id\": \"17895547\",\n      \"name\": \"node-1\",\n      \"font_class\": \"node-1\",\n      \"unicode\": \"e61a\",\n      \"unicode_decimal\": 58906\n    },\n    {\n      \"icon_id\": \"40498212\",\n      \"name\": \"node-2\",\n      \"font_class\": \"node-2\",\n      \"unicode\": \"e647\",\n      \"unicode_decimal\": 58951\n    },\n    {\n      \"icon_id\": \"3977832\",\n      \"name\": \"统一检查\",\n      \"font_class\": \"tongyijiancha\",\n      \"unicode\": \"e619\",\n      \"unicode_decimal\": 58905\n    },\n    {\n      \"icon_id\": \"4472516\",\n      \"name\": \"setting-2\",\n      \"font_class\": \"alert-3\",\n      \"unicode\": \"e728\",\n      \"unicode_decimal\": 59176\n    },\n    {\n      \"icon_id\": \"22761832\",\n      \"name\": \"文件类型-压缩包\",\n      \"font_class\": \"file-zip\",\n      \"unicode\": \"e606\",\n      \"unicode_decimal\": 58886\n    },\n    {\n      \"icon_id\": \"19671156\",\n      \"name\": \"txt-1\",\n      \"font_class\": \"file-normal\",\n      \"unicode\": \"e7ac\",\n      \"unicode_decimal\": 59308\n    },\n    {\n      \"icon_id\": \"26815646\",\n      \"name\": \"txt\",\n      \"font_class\": \"txt\",\n      \"unicode\": \"e6e3\",\n      \"unicode_decimal\": 59107\n    },\n    {\n      \"icon_id\": \"22735864\",\n      \"name\": \"文件类型-文件夹\",\n      \"font_class\": \"file-folder\",\n      \"unicode\": \"e600\",\n      \"unicode_decimal\": 58880\n    },\n    {\n      \"icon_id\": \"22761833\",\n      \"name\": \"文件类型-未知文件\",\n      \"font_class\": \"file-unknown\",\n      \"unicode\": \"e601\",\n      \"unicode_decimal\": 58881\n    },\n    {\n      \"icon_id\": \"22761837\",\n      \"name\": \"文件类型-Txt\",\n      \"font_class\": \"file-txt\",\n      \"unicode\": \"e602\",\n      \"unicode_decimal\": 58882\n    },\n    {\n      \"icon_id\": \"7533292\",\n      \"name\": \"中英文\",\n      \"font_class\": \"language\",\n      \"unicode\": \"e605\",\n      \"unicode_decimal\": 58885\n    },\n    {\n      \"icon_id\": \"22551111\",\n      \"name\": \"主题\",\n      \"font_class\": \"theme\",\n      \"unicode\": \"e638\",\n      \"unicode_decimal\": 58936\n    },\n    {\n      \"icon_id\": \"15838431\",\n      \"name\": \"arrow-right\",\n      \"font_class\": \"arrow-right\",\n      \"unicode\": \"e665\",\n      \"unicode_decimal\": 58981\n    },\n    {\n      \"icon_id\": \"3876424\",\n      \"name\": \"docker\",\n      \"font_class\": \"docker\",\n      \"unicode\": \"e659\",\n      \"unicode_decimal\": 58969\n    },\n    {\n      \"icon_id\": \"19688849\",\n      \"name\": \"应用商店\",\n      \"font_class\": \"appstore\",\n      \"unicode\": \"eb65\",\n      \"unicode_decimal\": 60261\n    },\n    {\n      \"icon_id\": \"4765743\",\n      \"name\": \"earth\",\n      \"font_class\": \"website\",\n      \"unicode\": \"e781\",\n      \"unicode_decimal\": 59265\n    },\n    {\n      \"icon_id\": \"4765891\",\n      \"name\": \"setting\",\n      \"font_class\": \"config\",\n      \"unicode\": \"e78e\",\n      \"unicode_decimal\": 59278\n    },\n    {\n      \"icon_id\": \"4765962\",\n      \"name\": \"app store\",\n      \"font_class\": \"appstore1\",\n      \"unicode\": \"e792\",\n      \"unicode_decimal\": 59282\n    },\n    {\n      \"icon_id\": \"4765971\",\n      \"name\": \"detail\",\n      \"font_class\": \"log\",\n      \"unicode\": \"e793\",\n      \"unicode_decimal\": 59283\n    },\n    {\n      \"icon_id\": \"4766440\",\n      \"name\": \"sever\",\n      \"font_class\": \"host\",\n      \"unicode\": \"e7b1\",\n      \"unicode_decimal\": 59313\n    },\n    {\n      \"icon_id\": \"4766685\",\n      \"name\": \"home\",\n      \"font_class\": \"home\",\n      \"unicode\": \"e7c6\",\n      \"unicode_decimal\": 59334\n    },\n    {\n      \"icon_id\": \"11487994\",\n      \"name\": \"calendar\",\n      \"font_class\": \"plan\",\n      \"unicode\": \"e746\",\n      \"unicode_decimal\": 59206\n    },\n    {\n      \"icon_id\": \"11488064\",\n      \"name\": \"integral\",\n      \"font_class\": \"database\",\n      \"unicode\": \"e754\",\n      \"unicode_decimal\": 59220\n    },\n    {\n      \"icon_id\": \"11488108\",\n      \"name\": \"rejected-order\",\n      \"font_class\": \"rejected-order\",\n      \"unicode\": \"e75e\",\n      \"unicode_decimal\": 59230\n    },\n    {\n      \"icon_id\": \"11488148\",\n      \"name\": \"tool\",\n      \"font_class\": \"toolbox\",\n      \"unicode\": \"e769\",\n      \"unicode_decimal\": 59241\n    },\n    {\n      \"icon_id\": \"8358944\",\n      \"name\": \"英文5\",\n      \"font_class\": \"yingwen\",\n      \"unicode\": \"e6c3\",\n      \"unicode_decimal\": 59075\n    },\n    {\n      \"icon_id\": \"8358949\",\n      \"name\": \"中文5\",\n      \"font_class\": \"zhongwen\",\n      \"unicode\": \"e6c8\",\n      \"unicode_decimal\": 59080\n    },\n    {\n      \"icon_id\": \"924436\",\n      \"name\": \"logout\",\n      \"font_class\": \"logout\",\n      \"unicode\": \"e8fe\",\n      \"unicode_decimal\": 59646\n    },\n    {\n      \"icon_id\": \"1760690\",\n      \"name\": \"讨论\",\n      \"font_class\": \"taolun\",\n      \"unicode\": \"e603\",\n      \"unicode_decimal\": 58883\n    },\n    {\n      \"icon_id\": \"6642940\",\n      \"name\": \"bug\",\n      \"font_class\": \"bug\",\n      \"unicode\": \"e616\",\n      \"unicode_decimal\": 58902\n    },\n    {\n      \"icon_id\": \"15337722\",\n      \"name\": \"Logo GitHub\",\n      \"font_class\": \"huaban88\",\n      \"unicode\": \"e67c\",\n      \"unicode_decimal\": 59004\n    },\n    {\n      \"icon_id\": \"974125\",\n      \"name\": \"star\",\n      \"font_class\": \"star\",\n      \"unicode\": \"e60f\",\n      \"unicode_decimal\": 58895\n    },\n    {\n      \"icon_id\": \"26815641\",\n      \"name\": \"PPT\",\n      \"font_class\": \"file-ppt\",\n      \"unicode\": \"e6e2\",\n      \"unicode_decimal\": 59106\n    },\n    {\n      \"icon_id\": \"26815643\",\n      \"name\": \"HTML\",\n      \"font_class\": \"file-html\",\n      \"unicode\": \"e608\",\n      \"unicode_decimal\": 58888\n    },\n    {\n      \"icon_id\": \"26815644\",\n      \"name\": \"word\",\n      \"font_class\": \"file-word\",\n      \"unicode\": \"e6e4\",\n      \"unicode_decimal\": 59108\n    },\n    {\n      \"icon_id\": \"26815647\",\n      \"name\": \"excel\",\n      \"font_class\": \"file-excel\",\n      \"unicode\": \"e6e6\",\n      \"unicode_decimal\": 59110\n    },\n    {\n      \"icon_id\": \"26815651\",\n      \"name\": \"PDF\",\n      \"font_class\": \"file-pdf\",\n      \"unicode\": \"e6e7\",\n      \"unicode_decimal\": 59111\n    },\n    {\n      \"icon_id\": \"26815652\",\n      \"name\": \"音频\",\n      \"font_class\": \"file-mp3\",\n      \"unicode\": \"e6e8\",\n      \"unicode_decimal\": 59112\n    },\n    {\n      \"icon_id\": \"26815654\",\n      \"name\": \"SVG\",\n      \"font_class\": \"file-svg\",\n      \"unicode\": \"e6e9\",\n      \"unicode_decimal\": 59113\n    },\n    {\n      \"icon_id\": \"26815655\",\n      \"name\": \"JPG\",\n      \"font_class\": \"file-jpg\",\n      \"unicode\": \"e6ea\",\n      \"unicode_decimal\": 59114\n    },\n    {\n      \"icon_id\": \"26815656\",\n      \"name\": \"视频\",\n      \"font_class\": \"file-video\",\n      \"unicode\": \"e6eb\",\n      \"unicode_decimal\": 59115\n    },\n    {\n      \"icon_id\": \"19671162\",\n      \"name\": \"png-1\",\n      \"font_class\": \"file-png\",\n      \"unicode\": \"e7ae\",\n      \"unicode_decimal\": 59310\n    },\n    {\n      \"icon_id\": \"7131916\",\n      \"name\": \"验证码\",\n      \"font_class\": \"yanzhengma1\",\n      \"unicode\": \"e744\",\n      \"unicode_decimal\": 59204\n    },\n    {\n      \"icon_id\": \"7708032\",\n      \"name\": \"菜单\",\n      \"font_class\": \"caidan\",\n      \"unicode\": \"e61d\",\n      \"unicode_decimal\": 58909\n    },\n    {\n      \"icon_id\": \"10293150\",\n      \"name\": \"详情\",\n      \"font_class\": \"xiangqing\",\n      \"unicode\": \"e677\",\n      \"unicode_decimal\": 58999\n    },\n    {\n      \"icon_id\": \"23044673\",\n      \"name\": \"功率\",\n      \"font_class\": \"webdav\",\n      \"unicode\": \"e622\",\n      \"unicode_decimal\": 58914\n    },\n    {\n      \"icon_id\": \"5838475\",\n      \"name\": \"统计\",\n      \"font_class\": \"tongji\",\n      \"unicode\": \"e856\",\n      \"unicode_decimal\": 59478\n    },\n    {\n      \"icon_id\": \"435944\",\n      \"name\": \"日志\",\n      \"font_class\": \"tamper-4\",\n      \"unicode\": \"e7c3\",\n      \"unicode_decimal\": 59331\n    },\n    {\n      \"icon_id\": \"5799978\",\n      \"name\": \"扫描\",\n      \"font_class\": \"tamper-2\",\n      \"unicode\": \"e610\",\n      \"unicode_decimal\": 58896\n    },\n    {\n      \"icon_id\": \"5961299\",\n      \"name\": \"安全\",\n      \"font_class\": \"tamper-3\",\n      \"unicode\": \"ec4d\",\n      \"unicode_decimal\": 60493\n    },\n    {\n      \"icon_id\": \"28131805\",\n      \"name\": \"完整性检查\",\n      \"font_class\": \"tamper-1\",\n      \"unicode\": \"e687\",\n      \"unicode_decimal\": 59015\n    },\n    {\n      \"icon_id\": \"899647\",\n      \"name\": \"欢迎页\",\n      \"font_class\": \"setting-1\",\n      \"unicode\": \"e626\",\n      \"unicode_decimal\": 58918\n    },\n    {\n      \"icon_id\": \"1114667\",\n      \"name\": \"图标\",\n      \"font_class\": \"setting-2\",\n      \"unicode\": \"e630\",\n      \"unicode_decimal\": 58928\n    },\n    {\n      \"icon_id\": \"12010559\",\n      \"name\": \"注册网站\",\n      \"font_class\": \"setting-3\",\n      \"unicode\": \"e617\",\n      \"unicode_decimal\": 58903\n    },\n    {\n      \"icon_id\": \"8229531\",\n      \"name\": \"ACL策略\",\n      \"font_class\": \"waf-4\",\n      \"unicode\": \"e60a\",\n      \"unicode_decimal\": 58890\n    },\n    {\n      \"icon_id\": \"8765146\",\n      \"name\": \"地图\",\n      \"font_class\": \"waf-1\",\n      \"unicode\": \"e62a\",\n      \"unicode_decimal\": 58922\n    },\n    {\n      \"icon_id\": \"16563651\",\n      \"name\": \"8-4限制区域\",\n      \"font_class\": \"waf-2\",\n      \"unicode\": \"e682\",\n      \"unicode_decimal\": 59010\n    },\n    {\n      \"icon_id\": \"17064153\",\n      \"name\": \"拦截\",\n      \"font_class\": \"waf-3\",\n      \"unicode\": \"e666\",\n      \"unicode_decimal\": 58982\n    },\n    {\n      \"icon_id\": \"3722144\",\n      \"name\": \"钻石\",\n      \"font_class\": \"xpack\",\n      \"unicode\": \"e60b\",\n      \"unicode_decimal\": 58891\n    },\n    {\n      \"icon_id\": \"40398413\",\n      \"name\": \"监控-copy\",\n      \"font_class\": \"monitor-4\",\n      \"unicode\": \"ec4e\",\n      \"unicode_decimal\": 60494\n    },\n    {\n      \"icon_id\": \"40398423\",\n      \"name\": \"地图-copy\",\n      \"font_class\": \"monitor-2\",\n      \"unicode\": \"ec4f\",\n      \"unicode_decimal\": 60495\n    },\n    {\n      \"icon_id\": \"8156501\",\n      \"name\": \"趋势\",\n      \"font_class\": \"monitor-1\",\n      \"unicode\": \"e60d\",\n      \"unicode_decimal\": 58893\n    },\n    {\n      \"icon_id\": \"40398652\",\n      \"name\": \"日志-copy\",\n      \"font_class\": \"monitor-3\",\n      \"unicode\": \"ec50\",\n      \"unicode_decimal\": 60496\n    },\n    {\n      \"icon_id\": \"1120947\",\n      \"name\": \"mac\",\n      \"font_class\": \"m-ios\",\n      \"unicode\": \"e60e\",\n      \"unicode_decimal\": 58894\n    },\n    {\n      \"icon_id\": \"1354850\",\n      \"name\": \"PC\",\n      \"font_class\": \"m-pc\",\n      \"unicode\": \"e771\",\n      \"unicode_decimal\": 59249\n    },\n    {\n      \"icon_id\": \"1948990\",\n      \"name\": \"浏览器-世界之窗\",\n      \"font_class\": \"m-theworld\",\n      \"unicode\": \"e947\",\n      \"unicode_decimal\": 59719\n    },\n    {\n      \"icon_id\": \"3176588\",\n      \"name\": \"Android\",\n      \"font_class\": \"m-android\",\n      \"unicode\": \"e9e0\",\n      \"unicode_decimal\": 59872\n    },\n    {\n      \"icon_id\": \"3547761\",\n      \"name\": \"tencent\",\n      \"font_class\": \"m-tencent\",\n      \"unicode\": \"e60c\",\n      \"unicode_decimal\": 58892\n    },\n    {\n      \"icon_id\": \"3846904\",\n      \"name\": \"Windows\",\n      \"font_class\": \"m-windows\",\n      \"unicode\": \"e6a0\",\n      \"unicode_decimal\": 59040\n    },\n    {\n      \"icon_id\": \"6172784\",\n      \"name\": \"cloud-machine\",\n      \"font_class\": \"m-machine\",\n      \"unicode\": \"e862\",\n      \"unicode_decimal\": 59490\n    },\n    {\n      \"icon_id\": \"6189402\",\n      \"name\": \"mobile\",\n      \"font_class\": \"m-mobile\",\n      \"unicode\": \"e620\",\n      \"unicode_decimal\": 58912\n    },\n    {\n      \"icon_id\": \"7172218\",\n      \"name\": \"UC浏览器\",\n      \"font_class\": \"m-ucweb\",\n      \"unicode\": \"e611\",\n      \"unicode_decimal\": 58897\n    },\n    {\n      \"icon_id\": \"7736005\",\n      \"name\": \"edge\",\n      \"font_class\": \"m-edge\",\n      \"unicode\": \"e8e2\",\n      \"unicode_decimal\": 59618\n    },\n    {\n      \"icon_id\": \"8714496\",\n      \"name\": \"2345浏览器\",\n      \"font_class\": \"m-2345explorer\",\n      \"unicode\": \"e612\",\n      \"unicode_decimal\": 58898\n    },\n    {\n      \"icon_id\": \"11983530\",\n      \"name\": \"chrome\",\n      \"font_class\": \"m-chrome\",\n      \"unicode\": \"ea09\",\n      \"unicode_decimal\": 59913\n    },\n    {\n      \"icon_id\": \"11983549\",\n      \"name\": \"opera\",\n      \"font_class\": \"m-opera\",\n      \"unicode\": \"ea0e\",\n      \"unicode_decimal\": 59918\n    },\n    {\n      \"icon_id\": \"12463174\",\n      \"name\": \"linux\",\n      \"font_class\": \"m-linux\",\n      \"unicode\": \"e80b\",\n      \"unicode_decimal\": 59403\n    },\n    {\n      \"icon_id\": \"13354966\",\n      \"name\": \"maxthonlogo\",\n      \"font_class\": \"m-maxthon\",\n      \"unicode\": \"e676\",\n      \"unicode_decimal\": 58998\n    },\n    {\n      \"icon_id\": \"18583105\",\n      \"name\": \"macbook\",\n      \"font_class\": \"m-mac\",\n      \"unicode\": \"ef2d\",\n      \"unicode_decimal\": 61229\n    },\n    {\n      \"icon_id\": \"19448086\",\n      \"name\": \"IE\",\n      \"font_class\": \"m-ie\",\n      \"unicode\": \"eaab\",\n      \"unicode_decimal\": 60075\n    },\n    {\n      \"icon_id\": \"32828855\",\n      \"name\": \"Chrome-OS\",\n      \"font_class\": \"Chrome-OS\",\n      \"unicode\": \"e613\",\n      \"unicode_decimal\": 58899\n    },\n    {\n      \"icon_id\": \"34065043\",\n      \"name\": \"Safari浏览器\",\n      \"font_class\": \"m-safari\",\n      \"unicode\": \"e614\",\n      \"unicode_decimal\": 58900\n    },\n    {\n      \"icon_id\": \"37095081\",\n      \"name\": \"bro360se\",\n      \"font_class\": \"m-360se\",\n      \"unicode\": \"e678\",\n      \"unicode_decimal\": 59000\n    },\n    {\n      \"icon_id\": \"37759945\",\n      \"name\": \"Firefox\",\n      \"font_class\": \"Firefox\",\n      \"unicode\": \"e87c\",\n      \"unicode_decimal\": 59516\n    },\n    {\n      \"icon_id\": \"1064806\",\n      \"name\": \"docker\",\n      \"font_class\": \"docker1\",\n      \"unicode\": \"e76a\",\n      \"unicode_decimal\": 59242\n    },\n    {\n      \"icon_id\": \"29851742\",\n      \"name\": \"短信告警\",\n      \"font_class\": \"alert-1\",\n      \"unicode\": \"e615\",\n      \"unicode_decimal\": 58901\n    },\n    {\n      \"icon_id\": \"33427786\",\n      \"name\": \"告警日志\",\n      \"font_class\": \"alert-2\",\n      \"unicode\": \"e701\",\n      \"unicode_decimal\": 59137\n    },\n    {\n      \"icon_id\": \"11124973\",\n      \"name\": \"表单\",\n      \"font_class\": \"17\",\n      \"unicode\": \"e618\",\n      \"unicode_decimal\": 58904\n    }\n  ]\n}\n"
  },
  {
    "path": "frontend/src/assets/json/iso.json",
    "content": "[\n    {\n        \"iso\": \"CN\",\n        \"zh\": \"中国\",\n        \"tw\": \"中國\",\n        \"en\": \"China\"\n    },\n    {\n        \"iso\": \"HK\",\n        \"zh\": \"中国香港\",\n        \"tw\": \"中國香港\",\n        \"en\": \"Hong Kong\"\n    },\n    {\n        \"iso\": \"MO\",\n        \"zh\": \"中国澳门\",\n        \"tw\": \"中國澳門\",\n        \"en\": \"Macau\"\n    },\n    {\n        \"iso\": \"TW\",\n        \"zh\": \"中国台湾\",\n        \"tw\": \"中國台灣\",\n        \"en\": \"Taiwan\"\n    },\n    {\n        \"iso\": \"AF\",\n        \"zh\": \"阿富汗\",\n        \"tw\": \"阿富汗\",\n        \"en\": \"Afghanistan\"\n    },\n    {\n        \"iso\": \"AL\",\n        \"zh\": \"阿尔巴尼亚\",\n        \"tw\": \"阿爾巴尼亞\",\n        \"en\": \"Albania\"\n    },\n    {\n        \"iso\": \"DZ\",\n        \"zh\": \"阿尔及利亚\",\n        \"tw\": \"阿爾及利亞\",\n        \"en\": \"Algeria\"\n    },\n    {\n        \"iso\": \"AO\",\n        \"zh\": \"安哥拉\",\n        \"tw\": \"安哥拉\",\n        \"en\": \"Angola\"\n    },\n    {\n        \"iso\": \"AR\",\n        \"zh\": \"阿根廷\",\n        \"tw\": \"阿根廷\",\n        \"en\": \"Argentina\"\n    },\n    {\n        \"iso\": \"AM\",\n        \"zh\": \"亚美尼亚\",\n        \"tw\": \"亞美尼亞\",\n        \"en\": \"Armenia\"\n    },\n    {\n        \"iso\": \"AU\",\n        \"zh\": \"澳大利亚\",\n        \"tw\": \"澳大利亞\",\n        \"en\": \"Australia\"\n    },\n    {\n        \"iso\": \"AT\",\n        \"zh\": \"奥地利\",\n        \"tw\": \"奧地利\",\n        \"en\": \"Austria\"\n    },\n    {\n        \"iso\": \"AZ\",\n        \"zh\": \"阿塞拜疆\",\n        \"tw\": \"阿塞拜疆\",\n        \"en\": \"Azerbaijan\"\n    },\n    {\n        \"iso\": \"BS\",\n        \"zh\": \"巴哈马\",\n        \"tw\": \"巴哈馬\",\n        \"en\": \"Bahamas\"\n    },\n    {\n        \"iso\": \"BH\",\n        \"zh\": \"巴林\",\n        \"tw\": \"巴林\",\n        \"en\": \"Bahrain\"\n    },\n    {\n        \"iso\": \"BD\",\n        \"zh\": \"孟加拉国\",\n        \"tw\": \"孟加拉國\",\n        \"en\": \"Bangladesh\"\n    },\n    {\n        \"iso\": \"BY\",\n        \"zh\": \"白俄罗斯\",\n        \"tw\": \"白俄羅斯\",\n        \"en\": \"Belarus\"\n    },\n    {\n        \"iso\": \"BE\",\n        \"zh\": \"比利时\",\n        \"tw\": \"比利時\",\n        \"en\": \"Belgium\"\n    },\n    {\n        \"iso\": \"BZ\",\n        \"zh\": \"伯利兹\",\n        \"tw\": \"貝里斯\",\n        \"en\": \"Belize\"\n    },\n    {\n        \"iso\": \"BJ\",\n        \"zh\": \"贝宁\",\n        \"tw\": \"貝南\",\n        \"en\": \"Benin\"\n    },\n    {\n        \"iso\": \"BT\",\n        \"zh\": \"不丹\",\n        \"tw\": \"不丹\",\n        \"en\": \"Bhutan\"\n    },\n    {\n        \"iso\": \"BO\",\n        \"zh\": \"玻利维亚\",\n        \"tw\": \"玻利維亞\",\n        \"en\": \"Bolivia\"\n    },\n    {\n        \"iso\": \"BA\",\n        \"zh\": \"波斯尼亚和黑塞哥维那\",\n        \"tw\": \"波斯尼亞和黑塞哥維那\",\n        \"en\": \"Bosnia and Herz.\"\n    },\n    {\n        \"iso\": \"BW\",\n        \"zh\": \"博茨瓦纳\",\n        \"tw\": \"博茨瓦納\",\n        \"en\": \"Botswana\"\n    },\n    {\n        \"iso\": \"BR\",\n        \"zh\": \"巴西\",\n        \"tw\": \"巴西\",\n        \"en\": \"Brazil\"\n    },\n    {\n        \"iso\": \"VG\",\n        \"zh\": \"英属维京群岛\",\n        \"tw\": \"英屬維京群島\",\n        \"en\": \"British Virgin Islands\"\n    },\n    {\n        \"iso\": \"BN\",\n        \"zh\": \"文莱\",\n        \"tw\": \"文萊\",\n        \"en\": \"Brunei\"\n    },\n    {\n        \"iso\": \"BG\",\n        \"zh\": \"保加利亚\",\n        \"tw\": \"保加利亞\",\n        \"en\": \"Bulgaria\"\n    },\n    {\n        \"iso\": \"BF\",\n        \"zh\": \"布基纳法索\",\n        \"tw\": \"布基納法索\",\n        \"en\": \"Burkina Faso\"\n    },\n    {\n        \"iso\": \"BI\",\n        \"zh\": \"布隆迪\",\n        \"tw\": \"布隆迪\",\n        \"en\": \"Burundi\"\n    },\n    {\n        \"iso\": \"KH\",\n        \"zh\": \"柬埔寨\",\n        \"tw\": \"柬埔寨\",\n        \"en\": \"Cambodia\"\n    },\n    {\n        \"iso\": \"KN\",\n        \"zh\": \"圣基茨和尼维斯\",\n        \"tw\": \"聖基茨和尼維斯\",\n        \"en\": \"St Kitts and Nevis\"\n    },\n    {\n        \"iso\": \"CM\",\n        \"zh\": \"喀麦隆\",\n        \"tw\": \"喀麥隆\",\n        \"en\": \"Cameroon\"\n    },\n    {\n        \"iso\": \"CA\",\n        \"zh\": \"加拿大\",\n        \"tw\": \"加拿大\",\n        \"en\": \"Canada\"\n    },\n    {\n        \"iso\": \"CV\",\n        \"zh\": \"佛得角\",\n        \"tw\": \"佛得角\",\n        \"en\": \"Cape Verde\"\n    },\n    {\n        \"iso\": \"KY\",\n        \"zh\": \"开曼群岛\",\n        \"tw\": \"開曼群島\",\n        \"en\": \"Cayman Islands\"\n    },\n    {\n        \"iso\": \"CF\",\n        \"zh\": \"中非共和国\",\n        \"tw\": \"中非共和國\",\n        \"en\": \"Central African Rep.\"\n    },\n    {\n        \"iso\": \"TD\",\n        \"zh\": \"乍得\",\n        \"tw\": \"乍得\",\n        \"en\": \"Chad\"\n    },\n    {\n        \"iso\": \"CL\",\n        \"zh\": \"智利\",\n        \"tw\": \"智利\",\n        \"en\": \"Chile\"\n    },\n\n    {\n        \"iso\": \"CO\",\n        \"zh\": \"哥伦比亚\",\n        \"tw\": \"哥倫比亞\",\n        \"en\": \"Colombia\"\n    },\n    {\n        \"iso\": \"KM\",\n        \"zh\": \"科摩罗\",\n        \"tw\": \"科摩羅\",\n        \"en\": \"Comoros\"\n    },\n    {\n        \"iso\": \"CG\",\n        \"zh\": \"刚果\",\n        \"tw\": \"剛果\",\n        \"en\": \"Congo\"\n    },\n    {\n        \"iso\": \"CR\",\n        \"zh\": \"哥斯达黎加\",\n        \"tw\": \"哥斯達黎加\",\n        \"en\": \"Costa Rica\"\n    },\n    {\n        \"iso\": \"HR\",\n        \"zh\": \"克罗地亚\",\n        \"tw\": \"克羅地亞\",\n        \"en\": \"Croatia\"\n    },\n    {\n        \"iso\": \"CU\",\n        \"zh\": \"古巴\",\n        \"tw\": \"古巴\",\n        \"en\": \"Cuba\"\n    },\n    {\n        \"iso\": \"CY\",\n        \"zh\": \"塞浦路斯\",\n        \"tw\": \"塞普勒斯\",\n        \"en\": \"Cyprus\"\n    },\n    {\n        \"iso\": \"CZ\",\n        \"zh\": \"捷克共和国\",\n        \"tw\": \"捷克共和國\",\n        \"en\": \"Czech Rep.\"\n    },\n    {\n        \"iso\": \"CI\",\n        \"zh\": \"科特迪瓦\",\n        \"tw\": \"象牙海岸\",\n        \"en\": \"Côte d'Ivoire\"\n    },\n    {\n        \"iso\": \"CD\",\n        \"zh\": \"刚果民主共和国\",\n        \"tw\": \"剛果民主共和國\",\n        \"en\": \"Dem. Rep. Congo\"\n    },\n    {\n        \"iso\": \"KP\",\n        \"zh\": \"朝鲜\",\n        \"tw\": \"朝鮮\",\n        \"en\": \"Dem. Rep. Korea\"\n    },\n    {\n        \"iso\": \"DK\",\n        \"zh\": \"丹麦\",\n        \"tw\": \"丹麥\",\n        \"en\": \"Denmark\"\n    },\n    {\n        \"iso\": \"DJ\",\n        \"zh\": \"吉布提\",\n        \"tw\": \"吉布提\",\n        \"en\": \"Djibouti\"\n    },\n    {\n        \"iso\": \"DO\",\n        \"zh\": \"多米尼加共和国\",\n        \"tw\": \"多明尼加共和國\",\n        \"en\": \"Dominican Rep.\"\n    },\n    {\n        \"iso\": \"EC\",\n        \"zh\": \"厄瓜多尔\",\n        \"tw\": \"厄瓜多爾\",\n        \"en\": \"Ecuador\"\n    },\n    {\n        \"iso\": \"EG\",\n        \"zh\": \"埃及\",\n        \"tw\": \"埃及\",\n        \"en\": \"Egypt\"\n    },\n    {\n        \"iso\": \"SV\",\n        \"zh\": \"萨尔瓦多\",\n        \"tw\": \"薩爾瓦多\",\n        \"en\": \"El Salvador\"\n    },\n    {\n        \"iso\": \"GQ\",\n        \"zh\": \"赤道几内亚\",\n        \"tw\": \"赤道幾內亞\",\n        \"en\": \"Equatorial Guinea\"\n    },\n    {\n        \"iso\": \"ER\",\n        \"zh\": \"厄立特里亚\",\n        \"tw\": \"厄立特里亞\",\n        \"en\": \"Eritrea\"\n    },\n    {\n        \"iso\": \"EE\",\n        \"zh\": \"爱沙尼亚\",\n        \"tw\": \"愛沙尼亞\",\n        \"en\": \"Estonia\"\n    },\n    {\n        \"iso\": \"ET\",\n        \"zh\": \"埃塞俄比亚\",\n        \"tw\": \"埃塞俄比亞\",\n        \"en\": \"Ethiopia\"\n    },\n    {\n        \"iso\": \"FK\",\n        \"zh\": \"福克兰群岛\",\n        \"tw\": \"福克蘭群島\",\n        \"en\": \"Falkland Is.\"\n    },\n    {\n        \"iso\": \"FJ\",\n        \"zh\": \"斐济\",\n        \"tw\": \"斐濟\",\n        \"en\": \"Fiji\"\n    },\n    {\n        \"iso\": \"FI\",\n        \"zh\": \"芬兰\",\n        \"tw\": \"芬蘭\",\n        \"en\": \"Finland\"\n    },\n    {\n        \"iso\": \"FR\",\n        \"zh\": \"法国\",\n        \"tw\": \"法國\",\n        \"en\": \"France\"\n    },\n    {\n        \"iso\": \"GA\",\n        \"zh\": \"加蓬\",\n        \"tw\": \"加蓬\",\n        \"en\": \"Gabon\"\n    },\n    {\n        \"iso\": \"GM\",\n        \"zh\": \"冈比亚\",\n        \"tw\": \"岡比亞\",\n        \"en\": \"Gambia\"\n    },\n    {\n        \"iso\": \"GE\",\n        \"zh\": \"格鲁吉亚\",\n        \"tw\": \"喬治亞\",\n        \"en\": \"Georgia\"\n    },\n    {\n        \"iso\": \"DE\",\n        \"zh\": \"德国\",\n        \"tw\": \"德國\",\n        \"en\": \"Germany\"\n    },\n    {\n        \"iso\": \"GH\",\n        \"zh\": \"加纳\",\n        \"tw\": \"加納\",\n        \"en\": \"Ghana\"\n    },\n    {\n        \"iso\": \"GR\",\n        \"zh\": \"希腊\",\n        \"tw\": \"希臘\",\n        \"en\": \"Greece\"\n    },\n    {\n        \"iso\": \"GL\",\n        \"zh\": \"格陵兰\",\n        \"tw\": \"格陵蘭\",\n        \"en\": \"Greenland\"\n    },\n    {\n        \"iso\": \"GT\",\n        \"zh\": \"危地马拉\",\n        \"tw\": \"危地馬拉\",\n        \"en\": \"Guatemala\"\n    },\n    {\n        \"iso\": \"GN\",\n        \"zh\": \"几内亚\",\n        \"tw\": \"幾內亞\",\n        \"en\": \"Guinea\"\n    },\n    {\n        \"iso\": \"GW\",\n        \"zh\": \"几内亚比绍\",\n        \"tw\": \"幾內亞比索\",\n        \"en\": \"Guinea-Bissau\"\n    },\n    {\n        \"iso\": \"GY\",\n        \"zh\": \"圭亚那\",\n        \"tw\": \"圭亞那\",\n        \"en\": \"Guyana\"\n    },\n    {\n        \"iso\": \"HT\",\n        \"zh\": \"海地\",\n        \"tw\": \"海地\",\n        \"en\": \"Haiti\"\n    },\n    {\n        \"iso\": \"HN\",\n        \"zh\": \"洪都拉斯\",\n        \"tw\": \"洪都拉斯\",\n        \"en\": \"Honduras\"\n    },\n    {\n        \"iso\": \"HU\",\n        \"zh\": \"匈牙利\",\n        \"tw\": \"匈牙利\",\n        \"en\": \"Hungary\"\n    },\n    {\n        \"iso\": \"IS\",\n        \"zh\": \"冰岛\",\n        \"tw\": \"冰島\",\n        \"en\": \"Iceland\"\n    },\n    {\n        \"iso\": \"IN\",\n        \"zh\": \"印度\",\n        \"tw\": \"印度\",\n        \"en\": \"India\"\n    },\n    {\n        \"iso\": \"ID\",\n        \"zh\": \"印度尼西亚\",\n        \"tw\": \"印尼\",\n        \"en\": \"Indonesia\"\n    },\n    {\n        \"iso\": \"IR\",\n        \"zh\": \"伊朗\",\n        \"tw\": \"伊朗\",\n        \"en\": \"Iran\"\n    },\n    {\n        \"iso\": \"IQ\",\n        \"zh\": \"伊拉克\",\n        \"tw\": \"伊拉克\",\n        \"en\": \"Iraq\"\n    },\n    {\n        \"iso\": \"IE\",\n        \"zh\": \"爱尔兰\",\n        \"tw\": \"愛爾蘭\",\n        \"en\": \"Ireland\"\n    },\n    {\n        \"iso\": \"IM\",\n        \"zh\": \"马恩岛\",\n        \"tw\": \"曼岛\",\n        \"en\": \"Isle of Man\"\n    },\n    {\n        \"iso\": \"IL\",\n        \"zh\": \"以色列\",\n        \"tw\": \"以色列\",\n        \"en\": \"Israel\"\n    },\n    {\n        \"iso\": \"IT\",\n        \"zh\": \"意大利\",\n        \"tw\": \"義大利\",\n        \"en\": \"Italy\"\n    },\n    {\n        \"iso\": \"JM\",\n        \"zh\": \"牙买加\",\n        \"tw\": \"牙買加\",\n        \"en\": \"Jamaica\"\n    },\n    {\n        \"iso\": \"JP\",\n        \"zh\": \"日本\",\n        \"tw\": \"日本\",\n        \"en\": \"Japan\"\n    },\n    {\n        \"iso\": \"JO\",\n        \"zh\": \"约旦\",\n        \"tw\": \"約旦\",\n        \"en\": \"Jordan\"\n    },\n    {\n        \"iso\": \"KZ\",\n        \"zh\": \"哈萨克斯坦\",\n        \"tw\": \"哈薩克斯坦\",\n        \"en\": \"Kazakhstan\"\n    },\n    {\n        \"iso\": \"KE\",\n        \"zh\": \"肯尼亚\",\n        \"tw\": \"肯亞\",\n        \"en\": \"Kenya\"\n    },\n    {\n        \"iso\": \"KR\",\n        \"zh\": \"韩国\",\n        \"tw\": \"韓國\",\n        \"en\": \"Korea\"\n    },\n    {\n        \"iso\": \"KW\",\n        \"zh\": \"科威特\",\n        \"tw\": \"科威特\",\n        \"en\": \"Kuwait\"\n    },\n    {\n        \"iso\": \"KG\",\n        \"zh\": \"吉尔吉斯斯坦\",\n        \"tw\": \"吉爾吉斯斯坦\",\n        \"en\": \"Kyrgyzstan\"\n    },\n    {\n        \"iso\": \"LA\",\n        \"zh\": \"老挝\",\n        \"tw\": \"寮國\",\n        \"en\": \"Lao PDR\"\n    },\n    {\n        \"iso\": \"LV\",\n        \"zh\": \"拉脱维亚\",\n        \"tw\": \"拉脫維亞\",\n        \"en\": \"Latvia\"\n    },\n    {\n        \"iso\": \"LB\",\n        \"zh\": \"黎巴嫩\",\n        \"tw\": \"黎巴嫩\",\n        \"en\": \"Lebanon\"\n    },\n    {\n        \"iso\": \"LS\",\n        \"zh\": \"莱索托\",\n        \"tw\": \"賴索托\",\n        \"en\": \"Lesotho\"\n    },\n    {\n        \"iso\": \"LR\",\n        \"zh\": \"利比里亚\",\n        \"tw\": \"賴比瑞亞\",\n        \"en\": \"Liberia\"\n    },\n    {\n        \"iso\": \"LY\",\n        \"zh\": \"利比亚\",\n        \"tw\": \"利比亞\",\n        \"en\": \"Libya\"\n    },\n    {\n        \"iso\": \"LT\",\n        \"zh\": \"立陶宛\",\n        \"tw\": \"立陶宛\",\n        \"en\": \"Lithuania\"\n    },\n    {\n        \"iso\": \"LU\",\n        \"zh\": \"卢森堡\",\n        \"tw\": \"盧森堡\",\n        \"en\": \"Luxembourg\"\n    },\n    {\n        \"iso\": \"MK\",\n        \"zh\": \"马其顿\",\n        \"tw\": \"馬其頓\",\n        \"en\": \"Macedonia\"\n    },\n    {\n        \"iso\": \"MG\",\n        \"zh\": \"马达加斯加\",\n        \"tw\": \"馬達加斯加\",\n        \"en\": \"Madagascar\"\n    },\n    {\n        \"iso\": \"MW\",\n        \"zh\": \"马拉维\",\n        \"tw\": \"馬拉威\",\n        \"en\": \"Malawi\"\n    },\n    {\n        \"iso\": \"MY\",\n        \"zh\": \"马来西亚\",\n        \"tw\": \"馬來西亞\",\n        \"en\": \"Malaysia\"\n    },\n    {\n        \"iso\": \"MV\",\n        \"zh\": \"马尔代夫\",\n        \"tw\": \"馬爾代夫\",\n        \"en\": \"Maldives\"\n    },\n    {\n        \"iso\": \"ML\",\n        \"zh\": \"马里\",\n        \"tw\": \"馬里\",\n        \"en\": \"Mali\"\n    },\n    {\n        \"iso\": \"MT\",\n        \"zh\": \"马耳他\",\n        \"tw\": \"馬爾他\",\n        \"en\": \"Malta\"\n    },\n    {\n        \"iso\": \"MR\",\n        \"zh\": \"毛里塔尼亚\",\n        \"tw\": \"毛里塔尼亞\",\n        \"en\": \"Mauritania\"\n    },\n    {\n        \"iso\": \"MU\",\n        \"zh\": \"毛里求斯\",\n        \"tw\": \"毛里求斯\",\n        \"en\": \"Mauritius\"\n    },\n    {\n        \"iso\": \"MX\",\n        \"zh\": \"墨西哥\",\n        \"tw\": \"墨西哥\",\n        \"en\": \"Mexico\"\n    },\n    {\n        \"iso\": \"MD\",\n        \"zh\": \"摩尔多瓦\",\n        \"tw\": \"摩爾多瓦\",\n        \"en\": \"Moldova\"\n    },\n    {\n        \"iso\": \"MC\",\n        \"zh\": \"摩纳哥\",\n        \"tw\": \"摩納哥\",\n        \"en\": \"Monaco\"\n    },\n    {\n        \"iso\": \"MN\",\n        \"zh\": \"蒙古\",\n        \"tw\": \"蒙古\",\n        \"en\": \"Mongolia\"\n    },\n    {\n        \"iso\": \"ME\",\n        \"zh\": \"黑山共和国\",\n        \"tw\": \"黑山共和國\",\n        \"en\": \"Montenegro\"\n    },\n    {\n        \"iso\": \"MA\",\n        \"zh\": \"摩洛哥\",\n        \"tw\": \"摩洛哥\",\n        \"en\": \"Morocco\"\n    },\n    {\n        \"iso\": \"MZ\",\n        \"zh\": \"莫桑比克\",\n        \"tw\": \"莫桑比克\",\n        \"en\": \"Mozambique\"\n    },\n    {\n        \"iso\": \"MM\",\n        \"zh\": \"缅甸\",\n        \"tw\": \"緬甸\",\n        \"en\": \"Myanmar\"\n    },\n    {\n        \"iso\": \"NA\",\n        \"zh\": \"纳米比亚\",\n        \"tw\": \"納米比亞\",\n        \"en\": \"Namibia\"\n    },\n    {\n        \"iso\": \"NP\",\n        \"zh\": \"尼泊尔\",\n        \"tw\": \"尼泊爾\",\n        \"en\": \"Nepal\"\n    },\n    {\n        \"iso\": \"NL\",\n        \"zh\": \"荷兰\",\n        \"tw\": \"荷蘭\",\n        \"en\": \"Netherlands\"\n    },\n    {\n        \"iso\": \"NC\",\n        \"zh\": \"新喀里多尼亚\",\n        \"tw\": \"新喀里多尼亞\",\n        \"en\": \"New Caledonia\"\n    },\n    {\n        \"iso\": \"NZ\",\n        \"zh\": \"新西兰\",\n        \"tw\": \"新西蘭\",\n        \"en\": \"New Zealand\"\n    },\n    {\n        \"iso\": \"NI\",\n        \"zh\": \"尼加拉瓜\",\n        \"tw\": \"尼加拉瓜\",\n        \"en\": \"Nicaragua\"\n    },\n    {\n        \"iso\": \"NE\",\n        \"zh\": \"尼日尔\",\n        \"tw\": \"尼日爾\",\n        \"en\": \"Niger\"\n    },\n    {\n        \"iso\": \"NG\",\n        \"zh\": \"尼日利亚\",\n        \"tw\": \"尼日利亞\",\n        \"en\": \"Nigeria\"\n    },\n    {\n        \"iso\": \"NO\",\n        \"zh\": \"挪威\",\n        \"tw\": \"挪威\",\n        \"en\": \"Norway\"\n    },\n    {\n        \"iso\": \"OM\",\n        \"zh\": \"阿曼\",\n        \"tw\": \"阿曼\",\n        \"en\": \"Oman\"\n    },\n    {\n        \"iso\": \"PK\",\n        \"zh\": \"巴基斯坦\",\n        \"tw\": \"巴基斯坦\",\n        \"en\": \"Pakistan\"\n    },\n    {\n        \"iso\": \"PA\",\n        \"zh\": \"巴拿马\",\n        \"tw\": \"巴拿馬\",\n        \"en\": \"Panama\"\n    },\n    {\n        \"iso\": \"PG\",\n        \"zh\": \"巴布亚新几内亚\",\n        \"tw\": \"巴布亞新幾內亞\",\n        \"en\": \"Papua New Guinea\"\n    },\n    {\n        \"iso\": \"PY\",\n        \"zh\": \"巴拉圭\",\n        \"tw\": \"巴拉圭\",\n        \"en\": \"Paraguay\"\n    },\n    {\n        \"iso\": \"PE\",\n        \"zh\": \"秘鲁\",\n        \"tw\": \"秘魯\",\n        \"en\": \"Peru\"\n    },\n    {\n        \"iso\": \"PH\",\n        \"zh\": \"菲律宾\",\n        \"tw\": \"菲律賓\",\n        \"en\": \"Philippines\"\n    },\n    {\n        \"iso\": \"PL\",\n        \"zh\": \"波兰\",\n        \"tw\": \"波蘭\",\n        \"en\": \"Poland\"\n    },\n    {\n        \"iso\": \"PT\",\n        \"zh\": \"葡萄牙\",\n        \"tw\": \"葡萄牙\",\n        \"en\": \"Portugal\"\n    },\n    {\n        \"iso\": \"PR\",\n        \"zh\": \"波多黎各\",\n        \"tw\": \"波多黎各\",\n        \"en\": \"Puerto Rico\"\n    },\n    {\n        \"iso\": \"QA\",\n        \"zh\": \"卡塔尔\",\n        \"tw\": \"卡塔爾\",\n        \"en\": \"Qatar\"\n    },\n    {\n        \"iso\": \"RE\",\n        \"zh\": \"留尼旺\",\n        \"tw\": \"留尼旺\",\n        \"en\": \"Reunion\"\n    },\n    {\n        \"iso\": \"RO\",\n        \"zh\": \"罗马尼亚\",\n        \"tw\": \"羅馬尼亞\",\n        \"en\": \"Romania\"\n    },\n    {\n        \"iso\": \"RU\",\n        \"zh\": \"俄罗斯\",\n        \"tw\": \"俄羅斯\",\n        \"en\": \"Russia\"\n    },\n    {\n        \"iso\": \"RW\",\n        \"zh\": \"卢旺达\",\n        \"tw\": \"盧旺達\",\n        \"en\": \"Rwanda\"\n    },\n    {\n        \"iso\": \"GS\",\n        \"zh\": \"南乔治亚和南桑威奇群岛\",\n        \"tw\": \"南喬治亞和南三明治群島\",\n        \"en\": \"S. Geo. and S. Sandw. Is.\"\n    },\n    {\n        \"iso\": \"SS\",\n        \"zh\": \"南苏丹\",\n        \"tw\": \"南蘇丹\",\n        \"en\": \"S. Sudan\"\n    },\n    {\n        \"iso\": \"SM\",\n        \"zh\": \"圣马力诺\",\n        \"tw\": \"聖馬力諾\",\n        \"en\": \"San Marino\"\n    },\n    {\n        \"iso\": \"SA\",\n        \"zh\": \"沙特阿拉伯\",\n        \"tw\": \"沙地阿拉伯\",\n        \"en\": \"Saudi Arabia\"\n    },\n    {\n        \"iso\": \"SN\",\n        \"zh\": \"塞内加尔\",\n        \"tw\": \"塞內加爾\",\n        \"en\": \"Senegal\"\n    },\n    {\n        \"iso\": \"RS\",\n        \"zh\": \"塞尔维亚\",\n        \"tw\": \"塞爾維亞\",\n        \"en\": \"Serbia\"\n    },\n    {\n        \"iso\": \"SL\",\n        \"zh\": \"塞拉利昂\",\n        \"tw\": \"塞拉利昂\",\n        \"en\": \"Sierra Leone\"\n    },\n    {\n        \"iso\": \"SG\",\n        \"zh\": \"新加坡\",\n        \"tw\": \"新加坡\",\n        \"en\": \"Singapore\"\n    },\n    {\n        \"iso\": \"SK\",\n        \"zh\": \"斯洛伐克\",\n        \"tw\": \"斯洛伐克\",\n        \"en\": \"Slovakia\"\n    },\n    {\n        \"iso\": \"SI\",\n        \"zh\": \"斯洛文尼亚\",\n        \"tw\": \"斯洛文尼亞\",\n        \"en\": \"Slovenia\"\n    },\n    {\n        \"iso\": \"SB\",\n        \"zh\": \"所罗门群岛\",\n        \"tw\": \"所羅門群島\",\n        \"en\": \"Solomon Is.\"\n    },\n    {\n        \"iso\": \"SO\",\n        \"zh\": \"索马里\",\n        \"tw\": \"索馬里\",\n        \"en\": \"Somalia\"\n    },\n    {\n        \"iso\": \"ZA\",\n        \"zh\": \"南非\",\n        \"tw\": \"南非\",\n        \"en\": \"South Africa\"\n    },\n    {\n        \"iso\": \"ES\",\n        \"zh\": \"西班牙\",\n        \"tw\": \"西班牙\",\n        \"en\": \"Spain\"\n    },\n    {\n        \"iso\": \"LK\",\n        \"zh\": \"斯里兰卡\",\n        \"tw\": \"斯里蘭卡\",\n        \"en\": \"Sri Lanka\"\n    },\n    {\n        \"iso\": \"SD\",\n        \"zh\": \"苏丹\",\n        \"tw\": \"蘇丹\",\n        \"en\": \"Sudan\"\n    },\n    {\n        \"iso\": \"SR\",\n        \"zh\": \"苏里南\",\n        \"tw\": \"蘇利南\",\n        \"en\": \"Suriname\"\n    },\n    {\n        \"iso\": \"SZ\",\n        \"zh\": \"斯威士兰\",\n        \"tw\": \"斯威士蘭\",\n        \"en\": \"Swaziland\"\n    },\n    {\n        \"iso\": \"SE\",\n        \"zh\": \"瑞典\",\n        \"tw\": \"瑞典\",\n        \"en\": \"Sweden\"\n    },\n    {\n        \"iso\": \"CH\",\n        \"zh\": \"瑞士\",\n        \"tw\": \"瑞士\",\n        \"en\": \"Switzerland\"\n    },\n    {\n        \"iso\": \"SY\",\n        \"zh\": \"叙利亚\",\n        \"tw\": \"敘利亞\",\n        \"en\": \"Syria\"\n    },\n\n    {\n        \"iso\": \"TJ\",\n        \"zh\": \"塔吉克斯坦\",\n        \"tw\": \"塔吉克斯坦\",\n        \"en\": \"Tajikistan\"\n    },\n    {\n        \"iso\": \"TZ\",\n        \"zh\": \"坦桑尼亚\",\n        \"tw\": \"坦桑尼亞\",\n        \"en\": \"Tanzania\"\n    },\n    {\n        \"iso\": \"TH\",\n        \"zh\": \"泰国\",\n        \"tw\": \"泰國\",\n        \"en\": \"Thailand\"\n    },\n    {\n        \"iso\": \"TG\",\n        \"zh\": \"多哥\",\n        \"tw\": \"多哥\",\n        \"en\": \"Togo\"\n    },\n    {\n        \"iso\": \"TO\",\n        \"zh\": \"汤加\",\n        \"tw\": \"湯加\",\n        \"en\": \"Tonga\"\n    },\n    {\n        \"iso\": \"TT\",\n        \"zh\": \"特立尼达和多巴哥\",\n        \"tw\": \"特立尼達和多巴哥\",\n        \"en\": \"Trinidad and Tobago\"\n    },\n    {\n        \"iso\": \"TN\",\n        \"zh\": \"突尼斯\",\n        \"tw\": \"突尼斯\",\n        \"en\": \"Tunisia\"\n    },\n    {\n        \"iso\": \"TR\",\n        \"zh\": \"土耳其\",\n        \"tw\": \"土耳其\",\n        \"en\": \"Turkey\"\n    },\n    {\n        \"iso\": \"TM\",\n        \"zh\": \"土库曼斯坦\",\n        \"tw\": \"土庫曼斯坦\",\n        \"en\": \"Turkmenistan\"\n    },\n    {\n        \"iso\": \"VI\",\n        \"zh\": \"美属维尔京群岛\",\n        \"tw\": \"美屬維爾京群島\",\n        \"en\": \"U.S. Virgin Islands\"\n    },\n    {\n        \"iso\": \"UG\",\n        \"zh\": \"乌干达\",\n        \"tw\": \"烏干達\",\n        \"en\": \"Uganda\"\n    },\n    {\n        \"iso\": \"UA\",\n        \"zh\": \"乌克兰\",\n        \"tw\": \"烏克蘭\",\n        \"en\": \"Ukraine\"\n    },\n    {\n        \"iso\": \"AE\",\n        \"zh\": \"阿拉伯联合酋长国\",\n        \"tw\": \"阿拉伯聯合大公國\",\n        \"en\": \"United Arab Emirates\"\n    },\n    {\n        \"iso\": \"GB\",\n        \"zh\": \"英国\",\n        \"tw\": \"英國\",\n        \"en\": \"United Kingdom\"\n    },\n    {\n        \"iso\": \"US\",\n        \"zh\": \"美国\",\n        \"tw\": \"美國\",\n        \"en\": \"United States\"\n    },\n    {\n        \"iso\": \"UY\",\n        \"zh\": \"乌拉圭\",\n        \"tw\": \"烏拉圭\",\n        \"en\": \"Uruguay\"\n    },\n    {\n        \"iso\": \"UZ\",\n        \"zh\": \"乌兹别克斯坦\",\n        \"tw\": \"烏茲別克斯坦\",\n        \"en\": \"Uzbekistan\"\n    },\n    {\n        \"iso\": \"VU\",\n        \"zh\": \"瓦努阿图\",\n        \"tw\": \"瓦努阿圖\",\n        \"en\": \"Vanuatu\"\n    },\n    {\n        \"iso\": \"VA\",\n        \"zh\": \"梵蒂冈城\",\n        \"tw\": \"梵蒂岡城\",\n        \"en\": \"Vatican City\"\n    },\n    {\n        \"iso\": \"VE\",\n        \"zh\": \"委内瑞拉\",\n        \"tw\": \"委內瑞拉\",\n        \"en\": \"Venezuela\"\n    },\n    {\n        \"iso\": \"VN\",\n        \"zh\": \"越南\",\n        \"tw\": \"越南\",\n        \"en\": \"Vietnam\"\n    },\n    {\n        \"iso\": \"EH\",\n        \"zh\": \"西撒哈拉\",\n        \"tw\": \"西撒哈拉\",\n        \"en\": \"W. Sahara\"\n    },\n    {\n        \"iso\": \"YE\",\n        \"zh\": \"也门\",\n        \"tw\": \"也門\",\n        \"en\": \"Yemen\"\n    },\n    {\n        \"iso\": \"YU\",\n        \"zh\": \"南斯拉夫\",\n        \"tw\": \"南斯拉夫\",\n        \"en\": \"Yugoslavia\"\n    },\n    {\n        \"iso\": \"ZR\",\n        \"zh\": \"扎伊尔\",\n        \"tw\": \"扎伊爾\",\n        \"en\": \"Zaire\"\n    },\n    {\n        \"iso\": \"ZM\",\n        \"zh\": \"赞比亚\",\n        \"tw\": \"贊比亞\",\n        \"en\": \"Zambia\"\n    },\n    {\n        \"iso\": \"ZW\",\n        \"zh\": \"津巴布韦\",\n        \"tw\": \"津巴布韋\",\n        \"en\": \"Zimbabwe\"\n    }\n]\n"
  },
  {
    "path": "frontend/src/components/advanced-setting/index.vue",
    "content": "<template>\n    <el-form-item prop=\"advanced\">\n        <el-checkbox v-model=\"form.advanced\" :label=\"$t('app.advanced')\" size=\"large\" />\n    </el-form-item>\n    <div v-if=\"form.advanced\">\n        <el-form-item :label=\"$t('app.containerName')\" prop=\"containerName\">\n            <el-input v-model.trim=\"form.containerName\" :placeholder=\"$t('app.containerNameHelper')\" />\n        </el-form-item>\n        <el-form-item v-if=\"showAllowPort\" prop=\"allowPort\">\n            <el-checkbox v-model=\"form.allowPort\" :label=\"$t('app.allowPort')\" size=\"large\" />\n            <span class=\"input-help\">{{ $t('app.allowPortHelper') }}</span>\n        </el-form-item>\n        <el-form-item v-if=\"showSpecifyIP && form.allowPort\" :label=\"$t('app.specifyIP')\" prop=\"specifyIP\">\n            <el-input v-model=\"form.specifyIP\" />\n            <span class=\"input-help\">{{ $t('app.specifyIPHelper') }}</span>\n        </el-form-item>\n        <el-form-item v-if=\"showRestartPolicy\" :label=\"$t('container.restartPolicy')\" prop=\"restartPolicy\">\n            <el-select v-model=\"form.restartPolicy\" class=\"p-w-300\">\n                <el-option :label=\"$t('container.no')\" value=\"no\" />\n                <el-option :label=\"$t('container.always')\" value=\"always\" />\n                <el-option :label=\"$t('container.onFailure')\" value=\"on-failure\" />\n                <el-option :label=\"$t('container.unlessStopped')\" value=\"unless-stopped\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item :label=\"$t('container.cpuQuota')\" prop=\"cpuQuota\" :rules=\"checkNumberRange(0, limits.cpu)\">\n            <el-input type=\"number\" class=\"!w-2/5\" v-model.number=\"form.cpuQuota\" maxlength=\"5\">\n                <template #append>{{ $t('app.cpuCore') }}</template>\n            </el-input>\n            <span class=\"input-help\">\n                {{ $t('container.limitHelper', [limits.cpu]) }}{{ $t('commons.units.core') }}\n            </span>\n        </el-form-item>\n        <el-form-item\n            :label=\"$t('container.memoryLimit')\"\n            prop=\"memoryLimit\"\n            :rules=\"checkNumberRange(0, limits.memory)\"\n        >\n            <el-input class=\"!w-2/5\" v-model.number=\"form.memoryLimit\" maxlength=\"10\">\n                <template #append>\n                    <el-select v-model=\"form.memoryUnit\" class=\"p-w-100\" @change=\"changeUnit\">\n                        <el-option label=\"MB\" value=\"M\" />\n                        <el-option label=\"GB\" value=\"G\" />\n                    </el-select>\n                </template>\n            </el-input>\n            <span class=\"input-help\">{{ $t('container.limitHelper', [limits.memory]) }}{{ form.memoryUnit }}B</span>\n        </el-form-item>\n        <el-form-item v-if=\"showPullImage\" prop=\"pullImage\">\n            <el-checkbox v-model=\"form.pullImage\" :label=\"$t('app.pullImage')\" />\n            <span class=\"input-help\">{{ $t('app.pullImageHelper') }}</span>\n        </el-form-item>\n        <el-form-item v-if=\"showCompose\" prop=\"editCompose\">\n            <el-checkbox v-model=\"form.editCompose\" :label=\"$t('app.editCompose')\" />\n            <span class=\"input-help\">{{ $t('app.editComposeHelper') }}</span>\n        </el-form-item>\n        <div v-if=\"showCompose && form.editCompose\">\n            <CodemirrorPro v-model=\"form.dockerCompose\" mode=\"yaml\" />\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, ref, toRef } from 'vue';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport { checkNumberRange } from '@/global/form-rules';\nimport { loadResourceLimit } from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\n\nconst props = withDefaults(\n    defineProps<{\n        form: {\n            advanced: boolean;\n            containerName: string;\n            allowPort: boolean;\n            specifyIP: string;\n            restartPolicy: string;\n            cpuQuota: number;\n            memoryLimit: number;\n            memoryUnit: string;\n            pullImage: boolean;\n            editCompose: boolean;\n            dockerCompose: string;\n        };\n        showAllowPort?: boolean;\n        showSpecifyIP?: boolean;\n        showRestartPolicy?: boolean;\n        showPullImage?: boolean;\n        showCompose?: boolean;\n        autoLoadLimit?: boolean;\n    }>(),\n    {\n        showAllowPort: true,\n        showSpecifyIP: true,\n        showRestartPolicy: true,\n        showPullImage: true,\n        showCompose: true,\n        autoLoadLimit: true,\n    },\n);\n\nconst form = toRef(props, 'form');\nconst limits = ref<Container.ResourceLimit>({\n    cpu: null as number,\n    memory: null as number,\n});\nconst oldMemory = ref(0);\n\nconst changeUnit = () => {\n    if (form.value.memoryUnit === 'M') {\n        limits.value.memory = oldMemory.value;\n    } else {\n        limits.value.memory = Number((oldMemory.value / 1024).toFixed(2));\n    }\n};\n\nconst loadLimit = async () => {\n    const res = await loadResourceLimit();\n    limits.value = res.data;\n    limits.value.memory = Number((limits.value.memory / 1024 / 1024).toFixed(2));\n    oldMemory.value = limits.value.memory;\n};\n\nonMounted(() => {\n    if (props.autoLoadLimit) {\n        loadLimit();\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/agent-group/change.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <DrawerPro v-model=\"drawerVisible\" :header=\"$t('terminal.groupChange')\" :back=\"handleClose\" size=\"small\">\n            <el-form @submit.prevent ref=\"hostInfoRef\" label-position=\"top\" :model=\"dialogData\" :rules=\"rules\">\n                <el-form-item :label=\"$t('commons.table.group')\" prop=\"group\">\n                    <el-select filterable v-model=\"dialogData.groupID\" clearable style=\"width: 100%\">\n                        <div v-for=\"item in groupList\" :key=\"item.id\">\n                            <el-option :label=\"item.name\" :value=\"item.id\" />\n                        </div>\n                    </el-select>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(hostInfoRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </template>\n        </DrawerPro>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, reactive } from 'vue';\nimport type { ElForm } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { getGroupList } from '@/api/modules/group';\n\nconst loading = ref();\ninterface DialogProps {\n    group: string;\n    groupType: string;\n}\nconst drawerVisible = ref(false);\nconst dialogData = ref({\n    groupID: 0,\n    groupType: '',\n});\n\nconst groupList = ref();\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value.groupType = params.groupType;\n    loadGroups(params.group);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits(['search', 'change']);\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst hostInfoRef = ref<FormInstance>();\nconst rules = reactive({\n    groupID: [Rules.requiredSelect],\n});\n\nconst loadGroups = async (groupName: string) => {\n    const res = await getGroupList(dialogData.value.groupType);\n    groupList.value = res.data;\n    for (const group of groupList.value) {\n        if (group.name === groupName) {\n            dialogData.value.groupID = group.id;\n            break;\n        }\n    }\n};\n\nconst onSubmit = (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        emit('change', Number(dialogData.value.groupID));\n        loading.value = false;\n        drawerVisible.value = false;\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/agent-group/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.table.group')\" @close=\"handleClose\" size=\"large\" :back=\"handleClose\">\n        <template #content>\n            <ComplexTable :data=\"data\" @search=\"search()\">\n                <template #toolbar>\n                    <el-button type=\"primary\" @click=\"openCreate\">{{ $t('website.createGroup') }}</el-button>\n                </template>\n                <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\">\n                    <template #default=\"{ row }\">\n                        <div v-if=\"!row.edit\">\n                            <span v-if=\"row.name === 'Default'\">\n                                {{ $t('commons.table.default') }}\n                            </span>\n                            <span v-if=\"row.name !== 'Default'\">{{ row.name }}</span>\n                            <el-tag v-if=\"row.isDefault\" type=\"success\" class=\"ml-2\" size=\"small\">\n                                ({{ $t('commons.table.default') }})\n                            </el-tag>\n\n                            <el-tag type=\"warning\" size=\"small\" class=\"ml-4\" v-if=\"row.isDelete\">\n                                {{ $t('app.takeDown') }}\n                            </el-tag>\n                        </div>\n\n                        <el-form @submit.prevent ref=\"groupForm\" v-if=\"row.edit\" :model=\"row\">\n                            <el-form-item prop=\"name\" v-if=\"row.edit\" :rules=\"Rules.name\">\n                                <div style=\"margin-top: 20px; width: 100%\"><el-input v-model=\"row.name\" /></div>\n                            </el-form-item>\n                        </el-form>\n                    </template>\n                </el-table-column>\n\n                <el-table-column :label=\"$t('commons.table.operate')\">\n                    <template #default=\"{ row, $index }\">\n                        <div>\n                            <el-button link v-if=\"row.edit\" type=\"primary\" @click=\"saveGroup(groupForm, row)\">\n                                {{ $t('commons.button.save') }}\n                            </el-button>\n                            <el-button link v-if=\"!row.edit\" type=\"primary\" @click=\"editGroup($index)\">\n                                {{ $t('commons.button.edit') }}\n                            </el-button>\n                            <el-button\n                                link\n                                v-if=\"!row.edit\"\n                                :disabled=\"row.isDefault\"\n                                type=\"primary\"\n                                @click=\"removeGroup($index)\"\n                            >\n                                {{ $t('commons.button.delete') }}\n                            </el-button>\n                            <el-button link v-if=\"row.edit\" type=\"primary\" @click=\"search()\">\n                                {{ $t('commons.button.cancel') }}\n                            </el-button>\n                            <el-button\n                                link\n                                v-if=\"!row.edit && !row.isDefault && !row.isDelete\"\n                                type=\"primary\"\n                                @click=\"setDefault(row)\"\n                            >\n                                {{ $t('website.setDefault') }}\n                            </el-button>\n                        </div>\n                    </template>\n                </el-table-column>\n            </ComplexTable>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { createAgentGroup, deleteAgentGroup, getAgentGroupList, updateAgentGroup } from '@/api/modules/group';\nimport { MsgSuccess } from '@/utils/message';\nimport { Group } from '@/api/interface/group';\nimport { Rules } from '@/global/form-rules';\nimport { FormInstance } from 'element-plus';\n\nconst open = ref(false);\nconst type = ref();\nconst data = ref();\nconst handleClose = () => {\n    open.value = false;\n    data.value = [];\n    emit('search');\n};\ninterface DialogProps {\n    type: string;\n}\n\nconst groupForm = ref<FormInstance>();\nconst acceptParams = (params: DialogProps): void => {\n    type.value = params.type;\n    open.value = true;\n    search();\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst search = () => {\n    getAgentGroupList(type.value).then((res) => {\n        data.value = res.data || [];\n    });\n};\n\nconst saveGroup = async (formEl: FormInstance, group: Group.GroupInfo) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        group.type = type.value;\n        if (group.id == 0) {\n            createAgentGroup(group).then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                search();\n            });\n        } else {\n            updateAgentGroup(group).then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                search();\n            });\n        }\n    });\n};\n\nconst setDefault = (group: Group.GroupInfo) => {\n    group.isDefault = true;\n    group.type = type.value;\n    updateAgentGroup(group).then(() => {\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        search();\n    });\n};\n\nconst openCreate = () => {\n    for (const d of data.value) {\n        if (d.name == '') {\n            return;\n        }\n        if (d.edit) {\n            d.edit = false;\n        }\n    }\n    const g = {\n        id: 0,\n        name: '',\n        isDefault: false,\n        edit: true,\n        status: 'Enable',\n    };\n    data.value.unshift(g);\n};\n\nconst removeGroup = (index: number) => {\n    const group = data.value[index];\n\n    if (group.id > 0) {\n        deleteAgentGroup(group.id).then(() => {\n            data.value.splice(index, 1);\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n        });\n    } else {\n        data.value.splice(index, 1);\n    }\n};\n\nconst editGroup = (index: number) => {\n    for (const i in data.value) {\n        const d = data.value[i];\n        if (d.name == '') {\n            data.value.splice(Number(i), 1);\n        }\n        if (d.edit) {\n            d.edit = false;\n        }\n    }\n    data.value[index].edit = true;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/components/app-status/index.vue",
    "content": "<template>\n    <div class=\"app-status\" v-if=\"data.isExist\">\n        <el-card>\n            <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                <div class=\"flex flex-wrap gap-4 ml-3\">\n                    <el-tag effect=\"dark\" type=\"success\">{{ data.app }}</el-tag>\n                    <Status class=\"mt-0.5\" :key=\"refresh\" :status=\"data.status\"></Status>\n                    <el-tag>{{ $t('app.version') }}: {{ data.version }}</el-tag>\n                </div>\n\n                <div class=\"mt-0.5\">\n                    <el-button\n                        type=\"primary\"\n                        v-if=\"data.status != 'Running'\"\n                        link\n                        @click=\"onOperate('start')\"\n                        :disabled=\"data.status === 'Installing'\"\n                    >\n                        {{ $t('commons.operate.start') }}\n                    </el-button>\n                    <el-button type=\"primary\" v-if=\"data.status === 'Running'\" link @click=\"onOperate('stop')\">\n                        {{ $t('commons.operate.stop') }}\n                    </el-button>\n                    <el-divider direction=\"vertical\" />\n                    <el-button\n                        type=\"primary\"\n                        link\n                        :disabled=\"data.status === 'Installing'\"\n                        @click=\"onOperate('restart')\"\n                    >\n                        {{ $t('commons.operate.restart') }}\n                    </el-button>\n                    <el-divider v-if=\"!hideSetting\" direction=\"vertical\" />\n                    <el-button\n                        type=\"primary\"\n                        link\n                        v-if=\"data.app === 'OpenResty'\"\n                        @click=\"onOperate('reload')\"\n                        :disabled=\"data.status !== 'Running'\"\n                    >\n                        {{ $t('commons.operate.reload') }}\n                    </el-button>\n                    <el-divider v-if=\"data.app === 'OpenResty'\" direction=\"vertical\" />\n                    <el-button\n                        v-if=\"!hideSetting\"\n                        type=\"primary\"\n                        @click=\"setting\"\n                        link\n                        :disabled=\"data.status === 'Installing'\"\n                    >\n                        {{ $t('commons.button.set') }}\n                    </el-button>\n                </div>\n                <div class=\"ml-5\" v-if=\"key === 'openresty' && (httpPort != 80 || httpsPort != 443)\">\n                    <el-tooltip\n                        effect=\"dark\"\n                        :content=\"$t('website.openrestyHelper', [httpPort, httpsPort])\"\n                        placement=\"top-start\"\n                    >\n                        <el-alert\n                            :title=\"$t('app.checkTitle')\"\n                            :closable=\"false\"\n                            center\n                            type=\"warning\"\n                            show-icon\n                            class=\"h-6 check-title\"\n                        />\n                    </el-tooltip>\n                </div>\n            </div>\n        </el-card>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { checkAppInstalled, installedOp } from '@/api/modules/app';\nimport { onMounted, reactive, ref } from 'vue';\nimport Status from '@/components/status/index.vue';\nimport { ElMessageBox } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    appKey: {\n        type: String,\n        default: 'openresty',\n    },\n    appName: {\n        type: String,\n        default: '',\n    },\n    hideSetting: {\n        type: Boolean,\n        default: false,\n    },\n});\n\nlet key = ref('');\nlet name = ref('');\n\nlet data = ref({\n    app: '',\n    version: '',\n    status: '',\n    lastBackupAt: '',\n    appInstallId: 0,\n    isExist: false,\n    containerName: '',\n});\nlet operateReq = reactive({\n    installId: 0,\n    operate: '',\n});\nlet refresh = ref(1);\nconst httpPort = ref(0);\nconst httpsPort = ref(0);\n\nconst em = defineEmits([\n    'setting',\n    'isExist',\n    'before',\n    'after',\n    'update:loading',\n    'update:maskShow',\n    'update:appInstallID',\n]);\nconst setting = () => {\n    em('setting', false);\n};\n\nconst onCheck = async (key: any, name: any) => {\n    await checkAppInstalled(key, name)\n        .then((res) => {\n            data.value = res.data;\n            em('isExist', res.data);\n            em('update:maskShow', res.data.status !== 'Running');\n            operateReq.installId = res.data.appInstallId;\n            em('update:appInstallID', res.data.appInstallId);\n            httpPort.value = res.data.httpPort;\n            httpsPort.value = res.data.httpsPort;\n            refresh.value++;\n        })\n        .catch(() => {\n            em('isExist', false);\n            refresh.value++;\n        });\n};\n\nconst onOperate = async (operation: string) => {\n    operateReq.operate = operation;\n    ElMessageBox.confirm(\n        i18n.global.t('app.operatorHelper', [i18n.global.t('commons.operate.' + operation)]),\n        i18n.global.t('commons.operate.' + operation),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(() => {\n        em('update:maskShow', true);\n        em('update:loading', true);\n        em('before');\n        installedOp(operateReq)\n            .then(() => {\n                em('update:loading', false);\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                onCheck(key.value, name.value);\n                em('after');\n            })\n            .catch(() => {\n                em('update:loading', false);\n            });\n    });\n};\n\nonMounted(() => {\n    key.value = props.appKey;\n    name.value = props.appName;\n    onCheck(key.value, name.value);\n});\n\ndefineExpose({\n    onCheck,\n    getHttpPort: () => httpPort.value,\n    getHttpsPort: () => httpsPort.value,\n});\n</script>\n<style scoped lang=\"scss\">\n.check-title {\n    color: var(--el-color-warning);\n    border: 1px solid var(--el-color-warning);\n    background-color: transparent;\n    padding: 8px 8px;\n    width: 70px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/back-button/index.vue",
    "content": "<template>\n    <el-page-header :content=\"header\" @back=\"jump\">\n        <template v-if=\"slots.buttons\" #content>\n            <slot name=\"buttons\"></slot>\n        </template>\n    </el-page-header>\n</template>\n\n<script setup lang=\"ts\">\nimport { routerToName, routerToPath } from '@/utils/router';\nimport { inject, useSlots } from 'vue';\nimport { useRouter } from 'vue-router';\n\nconst slots = useSlots();\nconst router = useRouter();\nconst props = defineProps({\n    path: String,\n    name: String,\n    to: Object,\n    header: String,\n    reload: Boolean,\n});\nfunction jump() {\n    const { path, name, to, reload } = props;\n    if (reload) {\n        reloadPage();\n    }\n    if (path) {\n        routerToPath(path);\n    }\n    if (name) {\n        routerToName(name);\n    }\n    if (to) {\n        router.push(to);\n    }\n}\n\nlet reloadPage: Function = inject('reload');\n</script>\n"
  },
  {
    "path": "frontend/src/components/backup/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"backupVisible\"\n        :header=\"$t('commons.button.backup')\"\n        :resource=\"detailName ? name + ' [' + detailName + ']' : name\"\n        @close=\"handleClose\"\n        size=\"60%\"\n    >\n        <template #content>\n            <el-alert v-if=\"type === 'app'\" :closable=\"false\" type=\"warning\">\n                <div class=\"mt-2 text-xs\">\n                    <span>{{ $t('setting.backupJump') }}</span>\n                    <span class=\"jump\" @click=\"goFile()\">\n                        <el-icon class=\"ml-2\"><Position /></el-icon>\n                        {{ $t('firewall.quickJump') }}\n                    </span>\n                </div>\n            </el-alert>\n\n            <ComplexTable\n                class=\"mt-5\"\n                v-loading=\"loading\"\n                :pagination-config=\"paginationConfig\"\n                v-model:selects=\"selects\"\n                @search=\"search\"\n                :data=\"data\"\n                style=\"width: 100%\"\n            >\n                <template #toolbar>\n                    <el-button\n                        type=\"primary\"\n                        :disabled=\"status && status.toLowerCase() != 'running'\"\n                        @click=\"onBackup()\"\n                    >\n                        {{ $t('commons.button.backup') }}\n                    </el-button>\n                    <el-button type=\"primary\" plain :disabled=\"selects.length === 0\" @click=\"onBatchDelete(null)\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n\n                    <TableRefresh class=\"float-right\" @search=\"search()\" />\n                </template>\n                <el-table-column type=\"selection\" :selectable=\"selectable\" fix />\n                <el-table-column :label=\"$t('commons.table.name')\" prop=\"fileName\" show-overflow-tooltip />\n                <el-table-column min-width=\"80px\" :label=\"$t('commons.table.status')\" prop=\"status\">\n                    <template #default=\"{ row }\">\n                        <Status\n                            v-if=\"row.status === 'Waiting'\"\n                            :status=\"row.status\"\n                            @click=\"openTaskLog(row.taskID)\"\n                            :msg=\"row.message\"\n                            :operate=\"true\"\n                        />\n                        <Status v-else :status=\"row.status\" :msg=\"row.message\" />\n                    </template>\n                </el-table-column>\n                <el-table-column min-width=\"80px\" :label=\"$t('file.size')\" prop=\"size\" show-overflow-tooltip>\n                    <template #default=\"{ row }\">\n                        <div v-if=\"row.hasLoad && (row.status === 'Success' || row.status === 'Failed')\">\n                            <span v-if=\"row.size\">\n                                {{ computeSize(row.size) }}\n                            </span>\n                            <span v-else>-</span>\n                        </div>\n                        <div v-else>\n                            <el-button link loading></el-button>\n                        </div>\n                    </template>\n                </el-table-column>\n                <el-table-column min-width=\"100px\" :label=\"$t('app.source')\" prop=\"backupType\">\n                    <template #default=\"{ row }\">\n                        <span v-if=\"row.accountType === 'LOCAL'\">\n                            {{ $t('setting.LOCAL') }}\n                        </span>\n                        <span v-if=\"row.accountType && row.accountType !== 'LOCAL'\">\n                            {{ $t('setting.' + row.accountType) + ' - ' + row.accountName }}\n                        </span>\n                        <span v-if=\"!row.accountType\">-</span>\n                    </template>\n                </el-table-column>\n                <el-table-column\n                    min-width=\"120px\"\n                    :label=\"$t('commons.table.description')\"\n                    prop=\"description\"\n                    show-overflow-tooltip\n                >\n                    <template #default=\"{ row }\">\n                        <fu-input-rw-switch v-model=\"row.description\" @enter=\"onChange(row)\" @blur=\"onChange(row)\" />\n                    </template>\n                </el-table-column>\n                <el-table-column\n                    min-width=\"80px\"\n                    prop=\"createdAt\"\n                    :label=\"$t('commons.table.date')\"\n                    :formatter=\"dateFormat\"\n                    show-overflow-tooltip\n                />\n\n                <fu-table-operations width=\"200px\" :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n            </ComplexTable>\n        </template>\n    </DrawerPro>\n\n    <DialogPro\n        v-model=\"open\"\n        :title=\"isBackup ? $t('commons.button.backup') : $t('commons.button.recover') + ' - ' + name\"\n        @close=\"handleBackupClose\"\n    >\n        <el-alert :closable=\"false\">\n            {{ $t('commons.msg.' + (isBackup ? 'backupHelper' : 'recoverHelper'), [name + '( ' + detailName + ' )']) }}\n        </el-alert>\n        <el-form class=\"mt-5\" ref=\"backupForm\" @submit.prevent label-position=\"top\" v-loading=\"loading\">\n            <el-form-item v-if=\"isBackup && (type === 'container' || type === 'compose')\">\n                <el-checkbox v-model=\"stopBefore\">\n                    {{\n                        type === 'container'\n                            ? $t('container.stopContainerBeforeBackup')\n                            : $t('container.stopComposeBeforeBackup')\n                    }}\n                </el-checkbox>\n                <span class=\"input-help\">{{ $t('container.stopBeforeBackupHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.compressPassword')\">\n                <el-input v-model=\"secret\" :placeholder=\"$t('setting.backupRecoverMessage')\" />\n            </el-form-item>\n            <el-form-item v-if=\"type === 'mysql' || type === 'mysql-cluster'\" :label=\"$t('cronjob.backupArgs')\">\n                <el-select v-model=\"args\" filterable allow-create multiple>\n                    <el-option v-for=\"item in mysqlArgs\" :key=\"item.arg\" :value=\"item.arg\" :label=\"item.arg\">\n                        {{ item.arg }}\n                        <span class=\"ml-2\">{{ item.description }}</span>\n                    </el-option>\n                </el-select>\n                <span class=\"input-help\">\n                    {{ $t('cronjob.backupArgsHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item\n                v-if=\"!isBackup && type !== 'app' && type !== 'website'\"\n                :label=\"$t('cronjob.timeout')\"\n                prop=\"timeoutItem\"\n            >\n                <el-input type=\"number\" class=\"selectClass\" v-model.number=\"timeoutItem\">\n                    <template #append>\n                        <el-select v-model=\"timeoutUnit\" style=\"width: 80px\">\n                            <el-option :label=\"$t('commons.units.second')\" value=\"s\" />\n                            <el-option :label=\"$t('commons.units.minute')\" value=\"m\" />\n                            <el-option :label=\"$t('commons.units.hour')\" value=\"h\" />\n                        </el-select>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('database.recoverTimeoutHelper') }}</span>\n            </el-form-item>\n            <el-form-item v-if=\"isBackup\" :label=\"$t('commons.table.description')\">\n                <el-input type=\"textarea\" :autosize=\"{ minRows: 2, maxRows: 5 }\" v-model=\"description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleBackupClose\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onSubmit\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n\n    <OpDialog ref=\"opRef\" @search=\"search\" />\n    <TaskLog ref=\"taskLogRef\" @close=\"search\" />\n    <PushApp ref=\"pushAppRef\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { computeSize, dateFormat, downloadFile, newUUID, transferTimeToSecond } from '@/utils/util';\nimport {\n    getLocalBackupDir,\n    handleBackup,\n    handleRecover,\n    deleteBackupRecord,\n    downloadBackupRecord,\n    searchBackupRecords,\n    loadRecordSize,\n    updateRecordDescription,\n} from '@/api/modules/backup';\nimport i18n from '@/lang';\nimport { Backup } from '@/api/interface/backup';\nimport { MsgSuccess } from '@/utils/message';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nimport { mysqlArgs } from '@/views/cronjob/cronjob/helper';\nconst { currentNode } = useGlobalStore();\n\nconst emit = defineEmits(['close']);\n\nconst PushApp = defineAsyncComponent(async () => {\n    const modules = import.meta.glob('@/xpack/views/appstore/push-app/index.vue');\n    const loader = modules['/src/xpack/views/appstore/push-app/index.vue'];\n    if (loader) {\n        return ((await loader()) as any).default;\n    }\n    return { template: '<div></div>' };\n});\n\nconst selects = ref<any>([]);\nconst args = ref([]);\nconst loading = ref();\nconst opRef = ref();\nconst taskLogRef = ref();\nconst pushAppRef = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst backupVisible = ref(false);\nconst type = ref();\nconst name = ref();\nconst detailName = ref();\nconst backupPath = ref();\nconst status = ref();\nconst secret = ref();\nconst description = ref();\nconst timeoutItem = ref(30);\nconst timeoutUnit = ref('m');\nconst node = ref();\nconst stopBefore = ref(false);\n\nconst open = ref();\nconst isBackup = ref();\nconst recordInfo = ref();\nconst appInstallID = ref();\n\ninterface DialogProps {\n    type: string;\n    name: string;\n    detailName: string;\n    status: string;\n    appInstallID?: number;\n    node?: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    type.value = params.type;\n    node.value = params.node || currentNode.value;\n    if (type.value === 'app') {\n        appInstallID.value = params.appInstallID || 0;\n        loadBackupDir();\n    }\n    name.value = params.name;\n    detailName.value = params.detailName;\n    backupVisible.value = true;\n    status.value = params.status;\n    secret.value = '';\n    search();\n};\nconst handleClose = () => {\n    backupVisible.value = false;\n    emit('close');\n};\nconst handleBackupClose = () => {\n    open.value = false;\n    search();\n};\n\nconst loadBackupDir = async () => {\n    const res = await getLocalBackupDir(node.value);\n    backupPath.value = res.data;\n};\n\nconst goFile = async () => {\n    routerToFileWithPath(`${backupPath.value}/app/${name.value}/${detailName.value}`);\n};\n\nconst onChange = async (info: any) => {\n    await updateRecordDescription(info.id, info.description, node.value);\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst search = async () => {\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        type: type.value,\n        name: name.value,\n        detailName: detailName.value,\n    };\n    loading.value = true;\n    await searchBackupRecords(params, node.value)\n        .then((res) => {\n            loading.value = false;\n            loadSize(params);\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadSize = async (params: any) => {\n    await loadRecordSize(params, node.value)\n        .then((res) => {\n            let stats = res.data || [];\n            if (stats.length === 0) {\n                return;\n            }\n            for (const backup of data.value) {\n                for (const item of stats) {\n                    if (backup.id === item.id) {\n                        backup.hasLoad = true;\n                        backup.size = item.size;\n                        break;\n                    }\n                }\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID, true, node.value);\n};\n\nfunction selectable(row) {\n    return row.status !== 'Waiting';\n}\n\nconst backup = async () => {\n    const taskID = newUUID();\n    let params = {\n        type: type.value,\n        name: name.value,\n        detailName: detailName.value,\n        secret: secret.value,\n        taskID: taskID,\n        description: description.value,\n        args: args.value,\n        stopBefore: stopBefore.value,\n    };\n    loading.value = true;\n    await handleBackup(params, node.value)\n        .then(() => {\n            loading.value = false;\n            openTaskLog(taskID);\n            handleBackupClose();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst recover = async (row?: any) => {\n    const taskID = newUUID();\n    let params = {\n        downloadAccountID: row.downloadAccountID,\n        type: type.value,\n        name: name.value,\n        detailName: detailName.value,\n        file: row.fileDir + '/' + row.fileName,\n        secret: secret.value,\n        taskID: taskID,\n        backupRecordID: row.id,\n        timeout: timeoutItem.value === -1 ? -1 : transferTimeToSecond(timeoutItem.value + timeoutUnit.value),\n    };\n    loading.value = true;\n    await handleRecover(params, node.value)\n        .then(() => {\n            loading.value = false;\n            openTaskLog(taskID);\n            handleBackupClose();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onBackup = async () => {\n    description.value = '';\n    secret.value = '';\n    args.value = [];\n    stopBefore.value = false;\n    isBackup.value = true;\n    open.value = true;\n};\n\nconst onRecover = async (row: Backup.RecordInfo) => {\n    secret.value = '';\n    isBackup.value = false;\n    recordInfo.value = row;\n    open.value = true;\n};\n\nconst onSubmit = () => {\n    if (isBackup.value) {\n        backup();\n    } else {\n        recover(recordInfo.value);\n    }\n};\n\nconst onDownload = async (row: Backup.RecordInfo) => {\n    let params = {\n        downloadAccountID: row.downloadAccountID,\n        fileDir: row.fileDir,\n        fileName: row.fileName,\n    };\n    await downloadBackupRecord(params, node.value).then(async (res) => {\n        downloadFile(res.data, node.value);\n    });\n};\n\nconst onBatchDelete = async (row: Backup.RecordInfo | null) => {\n    let ids: Array<number> = [];\n    let names = [];\n    if (row) {\n        ids.push(row.id);\n        names.push(row.fileName);\n    } else {\n        selects.value.forEach((item: Backup.RecordInfo) => {\n            ids.push(item.id);\n            names.push(item.fileName);\n        });\n    }\n    opRef.value.acceptParams({\n        names: names,\n        title: i18n.global.t('commons.button.delete'),\n        api: deleteBackupRecord,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('commons.button.backup'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        params: { ids: ids, node: node.value },\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.recover'),\n        disabled: (row: any) => {\n            return row.size === 0 || row.status === 'Failed';\n        },\n        click: (row: Backup.RecordInfo) => {\n            onRecover(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.migrate'),\n        disabled: (row: any) => {\n            return row.size === 0 || row.status === 'Failed' || row.accountType !== 'LOCAL';\n        },\n        show: () => {\n            return type.value === 'app';\n        },\n        click: (row: Backup.RecordInfo) => {\n            pushAppRef.value.acceptParams({\n                appInstallID: appInstallID.value,\n                appBackupID: row.id,\n            });\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.download'),\n        disabled: (row: any) => {\n            return row.size === 0 || row.status === 'Failed';\n        },\n        click: (row: Backup.RecordInfo) => {\n            onDownload(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        disabled: (row: any) => {\n            return row.status === 'Waiting';\n        },\n        click: (row: Backup.RecordInfo) => {\n            onBatchDelete(row);\n        },\n    },\n];\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.jump {\n    color: $primary-color;\n    cursor: pointer;\n    &:hover {\n        color: #74a4f3;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/card-with-header/index.vue",
    "content": "<template>\n    <div>\n        <el-card :style=\"{ height: height }\" class=\"home-card\">\n            <div class=\"header\">\n                <div class=\"header-left flex flex-wrap gap-3\">\n                    <span class=\"header-span\">{{ header }}</span>\n                    <slot name=\"header-l\" />\n                </div>\n                <div class=\"header-right flex flex-wrap gap-3\">\n                    <slot name=\"header-r\" />\n                </div>\n            </div>\n            <div class=\"body-content\">\n                <slot name=\"body\" />\n            </div>\n        </el-card>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\ndefineOptions({ name: 'CardWithHeader' });\ndefineProps({\n    header: String,\n    height: String,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.home-card {\n    .header {\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n\n        .header-left {\n            display: flex;\n            align-items: center;\n            gap: 12px;\n\n            .header-span {\n                position: relative;\n                font-size: 16px;\n                font-weight: 500;\n                margin-left: 18px;\n                display: flex;\n                align-items: center;\n\n                &::before {\n                    position: absolute;\n                    top: 50%;\n                    transform: translateY(-50%);\n                    left: -13px;\n                    width: 4px;\n                    height: 14px;\n                    content: '';\n                    background: $primary-color;\n                    border-radius: 10px;\n                }\n            }\n        }\n\n        .header-right {\n            display: flex;\n            align-items: center;\n        }\n    }\n\n    .body-content {\n        margin-top: 20px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/codemirror-pro/drawer.vue",
    "content": "<template>\n    <DrawerPro v-model=\"codeVisible\" :header=\"header\" size=\"large\" @close=\"handleClose\">\n        <CodemirrorPro v-model=\"detailInfo\" :height-diff=\"160\" :readonly=\"true\" :mode=\"mode\"></CodemirrorPro>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"codeVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nconst header = ref();\nconst detailInfo = ref();\nconst mode = ref();\nconst codeVisible = ref(false);\n\ninterface DialogProps {\n    header: string;\n    mode: string;\n    detailInfo: string;\n}\n\nconst acceptParams = (props: DialogProps): void => {\n    header.value = props.header;\n    detailInfo.value = props.detailInfo;\n    mode.value = props.mode;\n    codeVisible.value = true;\n};\n\nconst handleClose = () => {\n    codeVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/codemirror-pro/index.vue",
    "content": "<template>\n    <div :style=\"customStyle\">\n        <div ref=\"editorRef\" class=\"editor-container\"></div>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { CSSProperties } from 'vue';\nimport { basicSetup, EditorView } from 'codemirror';\nimport { EditorState, Extension } from '@codemirror/state';\nimport { oneDark } from '@codemirror/theme-one-dark';\nimport { StreamLanguage } from '@codemirror/language';\nimport { nginx } from './nginx';\nimport { yaml } from '@codemirror/legacy-modes/mode/yaml';\nimport { shell } from '@codemirror/legacy-modes/mode/shell';\nimport { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile';\nimport { javascript } from '@codemirror/legacy-modes/mode/javascript';\nimport { KeyBinding, placeholder } from '@codemirror/view';\nimport { json } from '@codemirror/lang-json';\nimport { keymap } from '@codemirror/view';\nimport { defaultKeymap, indentWithTab } from '@codemirror/commands';\n\ndefineOptions({ name: 'CodemirrorPro' });\n\nconst props = defineProps({\n    disabled: {\n        type: Boolean,\n        default: false,\n    },\n    readonly: {\n        type: Boolean,\n        default: false,\n    },\n    modelValue: {\n        type: String,\n        default: '',\n    },\n    mode: {\n        type: String,\n        default: 'javascript',\n    },\n    placeholder: {\n        type: String,\n        default: '',\n    },\n    height: {\n        type: Number,\n        default: 0,\n    },\n    heightDiff: {\n        type: Number,\n        default: 200,\n    },\n    minHeight: {\n        type: Number,\n        default: 400,\n    },\n    lineWrapping: {\n        type: Boolean,\n        default: false,\n    },\n});\n\nconst emit = defineEmits(['update:modelValue']);\nconst editorRef = ref();\nconst editorView = ref();\nconst content = computed(() => {\n    return props.modelValue;\n});\n\nconst customStyle = computed<CSSProperties>(() => ({\n    width: '100%',\n}));\n\nconst toggleLineComment = (mode: string) => {\n    return (view: EditorView) => {\n        const commentChar =\n            mode === 'yaml' || mode === 'shell' || mode === 'nginx' || mode === 'dockerfile' ? '#' : '//';\n\n        const { state } = view;\n\n        const transaction = state.changeByRange((range) => {\n            let line = state.doc.lineAt(range.from);\n            let text = line.text;\n            let changes;\n\n            if (text.trim().startsWith(commentChar)) {\n                const pos = line.from + text.indexOf(commentChar);\n                changes = { from: pos, to: pos + commentChar.length, insert: '' };\n            } else {\n                changes = { from: line.from, insert: commentChar };\n            }\n\n            return {\n                changes,\n                range,\n            };\n        });\n\n        view.dispatch(transaction);\n        return true;\n    };\n};\n\nconst customKeymap: KeyBinding[] = [\n    {\n        key: 'Alt-/',\n        run: toggleLineComment(props.mode),\n        preventDefault: true,\n    },\n    {\n        key: 'Mod-/',\n        run: toggleLineComment(props.mode),\n        preventDefault: true,\n    },\n];\n\nconst initCodeMirror = () => {\n    const defaultTheme = EditorView.theme({\n        '&.cm-editor': {\n            minHeight: props.minHeight + 'px',\n            height: loadHeight(),\n        },\n    });\n\n    const extensions: Extension[] = [\n        defaultTheme,\n        oneDark,\n        basicSetup,\n        keymap.of([...defaultKeymap, indentWithTab, ...customKeymap]),\n        EditorView.updateListener.of((v: any) => {\n            if (v.docChanged) {\n                emit('update:modelValue', v.state.doc.toString());\n            }\n        }),\n        placeholder(props.placeholder),\n        EditorView.editable.of(!props.disabled),\n        EditorState.readOnly.of(props.readonly),\n    ];\n\n    if (props.lineWrapping) {\n        extensions.push(EditorView.lineWrapping);\n    }\n\n    switch (props.mode) {\n        case 'dockerfile':\n            extensions.push(StreamLanguage.define(dockerFile));\n            break;\n        case 'javascript':\n            extensions.push(StreamLanguage.define(javascript));\n            break;\n        case 'nginx':\n            extensions.push(StreamLanguage.define(nginx));\n            break;\n        case 'yaml':\n            extensions.push(StreamLanguage.define(yaml));\n            break;\n        case 'json':\n            extensions.push(json());\n            break;\n        case 'shell':\n            extensions.push(StreamLanguage.define(shell));\n            break;\n    }\n\n    let startState = EditorState.create({\n        doc: content.value,\n        extensions: extensions,\n    });\n\n    editorView.value = new EditorView({\n        state: startState,\n        parent: editorRef.value,\n    });\n};\n\nconst loadHeight = () => {\n    if (props.height || props.heightDiff) {\n        return props.height ? props.height + 'px' : 'calc(100vh - ' + props.heightDiff + 'px)';\n    }\n};\n\nwatch(\n    () => content.value,\n    (newValue) => {\n        if (editorView.value) {\n            if (newValue === editorView.value.state.doc.toString()) {\n                return;\n            }\n            editorView.value.dispatch({\n                changes: {\n                    from: 0,\n                    to: editorView.value.state.doc.length,\n                    insert: newValue,\n                },\n                scrollIntoView: false,\n            });\n        } else {\n            initCodeMirror();\n        }\n    },\n    { immediate: true },\n);\n\nonMounted(() => {\n    initCodeMirror();\n});\n\nonUnmounted(() => {\n    editorView.value?.destroy();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/codemirror-pro/nginx.ts",
    "content": "import { StreamParser } from '@codemirror/language';\n\nfunction words(str) {\n    const obj = {},\n        words = str.split(' ');\n    for (let i = 0; i < words.length; ++i) obj[words[i]] = true;\n    return obj;\n}\n\nconst keywords = words(\n    /* ngxDirectiveControl */ 'break return rewrite set' +\n        /* ngxDirective */ ' accept_mutex accept_mutex_delay access_log add_after_body add_before_body add_header addition_types aio alias allow ancient_browser ancient_browser_value auth_basic auth_basic_user_file auth_http auth_http_header auth_http_timeout autoindex autoindex_exact_size autoindex_localtime charset charset_types client_body_buffer_size client_body_in_file_only client_body_in_single_buffer client_body_temp_path client_body_timeout client_header_buffer_size client_header_timeout client_max_body_size connection_pool_size create_full_put_path daemon dav_access dav_methods debug_connection debug_points default_type degradation degrade deny devpoll_changes devpoll_events directio directio_alignment empty_gif env epoll_events error_log eventport_events expires fastcgi_bind fastcgi_buffer_size fastcgi_buffers fastcgi_busy_buffers_size fastcgi_cache fastcgi_cache_key fastcgi_cache_methods fastcgi_cache_min_uses fastcgi_cache_path fastcgi_cache_use_stale fastcgi_cache_valid fastcgi_catch_stderr fastcgi_connect_timeout fastcgi_hide_header fastcgi_ignore_client_abort fastcgi_ignore_headers fastcgi_index fastcgi_intercept_errors fastcgi_max_temp_file_size fastcgi_next_upstream fastcgi_param fastcgi_pass_header fastcgi_pass_request_body fastcgi_pass_request_headers fastcgi_read_timeout fastcgi_send_lowat fastcgi_send_timeout fastcgi_split_path_info fastcgi_store fastcgi_store_access fastcgi_temp_file_write_size fastcgi_temp_path fastcgi_upstream_fail_timeout fastcgi_upstream_max_fails flv geoip_city geoip_country google_perftools_profiles gzip gzip_buffers gzip_comp_level gzip_disable gzip_hash gzip_http_version gzip_min_length gzip_no_buffer gzip_proxied gzip_static gzip_types gzip_vary gzip_window if_modified_since ignore_invalid_headers image_filter image_filter_buffer image_filter_jpeg_quality image_filter_transparency imap_auth imap_capabilities imap_client_buffer index ip_hash keepalive_requests keepalive_timeout kqueue_changes kqueue_events large_client_header_buffers limit_conn limit_conn_log_level limit_rate limit_rate_after limit_req limit_req_log_level limit_req_zone limit_zone lingering_time lingering_timeout lock_file log_format log_not_found log_subrequest map_hash_bucket_size map_hash_max_size master_process memcached_bind memcached_buffer_size memcached_connect_timeout memcached_next_upstream memcached_read_timeout memcached_send_timeout memcached_upstream_fail_timeout memcached_upstream_max_fails merge_slashes min_delete_depth modern_browser modern_browser_value msie_padding msie_refresh multi_accept open_file_cache open_file_cache_errors open_file_cache_events open_file_cache_min_uses open_file_cache_valid open_log_file_cache output_buffers override_charset perl perl_modules perl_require perl_set pid pop3_auth pop3_capabilities port_in_redirect postpone_gzipping postpone_output protocol proxy proxy_bind proxy_buffer proxy_buffer_size proxy_buffering proxy_buffers proxy_busy_buffers_size proxy_cache proxy_cache_key proxy_cache_methods proxy_cache_min_uses proxy_cache_path proxy_cache_use_stale proxy_cache_valid proxy_connect_timeout proxy_headers_hash_bucket_size proxy_headers_hash_max_size proxy_hide_header proxy_ignore_client_abort proxy_ignore_headers proxy_intercept_errors proxy_max_temp_file_size proxy_method proxy_next_upstream proxy_pass_error_message proxy_pass_header proxy_pass_request_body proxy_pass_request_headers proxy_read_timeout proxy_redirect proxy_send_lowat proxy_send_timeout proxy_set_body proxy_set_header proxy_ssl_session_reuse proxy_store proxy_store_access proxy_temp_file_write_size proxy_temp_path proxy_timeout proxy_upstream_fail_timeout proxy_upstream_max_fails random_index read_ahead real_ip_header recursive_error_pages request_pool_size reset_timedout_connection resolver resolver_timeout rewrite_log rtsig_overflow_events rtsig_overflow_test rtsig_overflow_threshold rtsig_signo satisfy secure_link_secret send_lowat send_timeout sendfile sendfile_max_chunk server_name_in_redirect server_names_hash_bucket_size server_names_hash_max_size server_tokens set_real_ip_from smtp_auth smtp_capabilities smtp_client_buffer smtp_greeting_delay so_keepalive source_charset ssi ssi_ignore_recycled_buffers ssi_min_file_chunk ssi_silent_errors ssi_types ssi_value_length ssl ssl_certificate ssl_certificate_key ssl_ciphers ssl_client_certificate ssl_crl ssl_dhparam ssl_engine ssl_prefer_server_ciphers ssl_protocols ssl_session_cache ssl_session_timeout ssl_verify_client ssl_verify_depth starttls stub_status sub_filter sub_filter_once sub_filter_types tcp_nodelay tcp_nopush thread_stack_size timeout timer_resolution types_hash_bucket_size types_hash_max_size underscores_in_headers uninitialized_variable_warn use user userid userid_domain userid_expires userid_mark userid_name userid_p3p userid_path userid_service valid_referers variables_hash_bucket_size variables_hash_max_size worker_connections worker_cpu_affinity worker_priority worker_processes worker_rlimit_core worker_rlimit_nofile worker_rlimit_sigpending worker_threads working_directory xclient xml_entities xslt_stylesheet xslt_typesdrew@li229-23',\n);\n\nconst keywords_block = words(\n    /* ngxDirectiveBlock */ 'http mail events server types location upstream charset_map limit_except if geo map',\n);\n\nconst keywords_important = words(\n    /* ngxDirectiveImportant */ 'include root server server_name listen internal proxy_pass memcached_pass fastcgi_pass try_files',\n);\n\nlet type;\nfunction ret(style, tp) {\n    type = tp;\n    return style;\n}\n\nfunction tokenBase(stream, state) {\n    stream.eatWhile(/[\\w\\$_]/);\n\n    const cur = stream.current();\n\n    if (keywords.propertyIsEnumerable(cur)) {\n        return 'keyword';\n    } else if (keywords_block.propertyIsEnumerable(cur)) {\n        return 'controlKeyword';\n    } else if (keywords_important.propertyIsEnumerable(cur)) {\n        return 'controlKeyword';\n    }\n\n    const ch = stream.next();\n    if (ch == '@') {\n        stream.eatWhile(/[\\w\\\\\\-]/);\n        return ret('meta', stream.current());\n    } else if (ch == '/' && stream.eat('*')) {\n        return 'compare';\n    } else if (ch == '<' && stream.eat('!')) {\n        state.tokenize = tokenSGMLComment;\n        return tokenSGMLComment(stream, state);\n    } else if (ch == '=') ret(null, 'compare');\n    else if ((ch == '~' || ch == '|') && stream.eat('=')) return ret(null, 'compare');\n    else if (ch == '\"' || ch == \"'\") {\n        state.tokenize = tokenString(ch);\n        return state.tokenize(stream, state);\n    } else if (ch == '#') {\n        stream.skipToEnd();\n        return ret('comment', 'comment');\n    } else if (ch == '!') {\n        stream.match(/^\\s*\\w*/);\n        return ret('keyword', 'important');\n    } else if (/\\d/.test(ch)) {\n        stream.eatWhile(/[\\w.%]/);\n        return ret('number', 'unit');\n    } else if (/[,.+>*\\/]/.test(ch)) {\n        return ret(null, 'select-op');\n    } else if (/[;{}:\\[\\]]/.test(ch)) {\n        return ret(null, ch);\n    } else {\n        stream.eatWhile(/[\\w\\\\\\-]/);\n        return ret('variable', 'variable');\n    }\n}\n\nfunction tokenSGMLComment(stream, state) {\n    let dashes = 0,\n        ch;\n    while ((ch = stream.next()) != null) {\n        if (dashes >= 2 && ch == '>') {\n            state.tokenize = tokenBase;\n            break;\n        }\n        dashes = ch == '-' ? dashes + 1 : 0;\n    }\n    return ret('comment', 'comment');\n}\n\nfunction tokenString(quote) {\n    return function (stream, state) {\n        let escaped = false,\n            ch;\n        while ((ch = stream.next()) != null) {\n            if (ch == quote && !escaped) break;\n            escaped = !escaped && ch == '\\\\';\n        }\n        if (!escaped) state.tokenize = tokenBase;\n        return ret('string', 'string');\n    };\n}\n\ninterface NgxState {\n    tokenize: (stream: any, state: NgxState) => string | null;\n    baseIndent: number;\n    stack: string[];\n}\n\nexport const nginx: StreamParser<NgxState> = {\n    name: 'nginx',\n    startState: function (): NgxState {\n        return {\n            tokenize: tokenBase,\n            baseIndent: 0,\n            stack: [],\n        };\n    },\n    token: function (stream, state) {\n        if (stream.eatSpace()) return null;\n        type = null;\n        const style = state.tokenize(stream, state);\n        const context = state.stack[state.stack.length - 1];\n        if (type === 'hash' && context === 'rule') return 'atom';\n        else if (style === 'variable') {\n            if (context === 'rule') return 'number';\n            else if (!context || context === '@media{') return 'tag';\n        }\n        if (context === 'rule' && /^[\\{\\};]$/.test(type!)) state.stack.pop();\n        if (type === '{') {\n            if (context === '@media') state.stack[state.stack.length - 1] = '@media{';\n            else state.stack.push('{');\n        } else if (type === '}') state.stack.pop();\n        else if (type === '@media') state.stack.push('@media');\n        else if (context === '{' && type !== 'comment') state.stack.push('rule');\n        return style;\n    },\n    indent: function (state, textAfter, cx) {\n        let n = state.stack.length;\n        if (/^\\}/.test(textAfter)) n -= state.stack[state.stack.length - 1] === 'rule' ? 2 : 1;\n        return state.baseIndent + n * cx.unit;\n    },\n    languageData: {\n        indentOnInput: /^\\s*\\}$/,\n    },\n};\n"
  },
  {
    "path": "frontend/src/components/complex-table/index.vue",
    "content": "<template>\n    <div class=\"complex-table\">\n        <div class=\"complex-table__header\" v-if=\"slots.header || header\">\n            <slot name=\"header\">{{ header }}</slot>\n        </div>\n        <div v-if=\"slots.toolbar\">\n            <slot name=\"toolbar\"></slot>\n        </div>\n\n        <div class=\"complex-table__body\">\n            <fu-table\n                v-bind=\"$attrs\"\n                ref=\"tableRef\"\n                @selection-change=\"handleSelectionChange\"\n                :max-height=\"tableHeight\"\n                @row-contextmenu=\"handleRightClick\"\n                @row-click=\"handleRowClick\"\n                :tooltip-options=\"{\n                    placement: 'bottom-start',\n                }\"\n            >\n                <slot></slot>\n                <template #empty>\n                    <slot name=\"empty\"></slot>\n                </template>\n            </fu-table>\n        </div>\n        <div class=\"table-footer-container\">\n            <div class=\"footer-left\" v-if=\"slots.footerLeft\">\n                <el-checkbox v-model=\"leftSelect\" @change=\"toggleSelection\"></el-checkbox>\n                <div class=\"ml-4\">\n                    <slot name=\"footerLeft\"></slot>\n                </div>\n            </div>\n\n            <div\n                class=\"complex-table__pagination flex items-center w-full sm:flex-row flex-col text-xs sm:text-sm\"\n                v-if=\"props.paginationConfig\"\n                :class=\"{ '!justify-between': slots.paginationLeft, '!justify-end': !slots.paginationLeft }\"\n            >\n                <slot name=\"paginationLeft\"></slot>\n                <slot name=\"pagination\">\n                    <el-pagination\n                        v-model:current-page=\"paginationConfig.currentPage\"\n                        v-model:page-size=\"paginationConfig.pageSize\"\n                        :total=\"paginationConfig.total\"\n                        :page-sizes=\"[5, 10, 20, 50, 100, 200, 500]\"\n                        @size-change=\"sizeChange\"\n                        @current-change=\"currentChange\"\n                        :size=\"mobile || paginationConfig.small ? 'small' : 'default'\"\n                        :layout=\"\n                            mobile || paginationConfig.small\n                                ? 'total, prev, pager, next'\n                                : 'total, sizes, prev, pager, next, jumper'\n                        \"\n                    />\n                </slot>\n            </div>\n        </div>\n        <ul\n            v-if=\"rightClick.visible\"\n            class=\"context-menu\"\n            ref=\"menuRef\"\n            :style=\"{ top: `${adjustedY}px`, left: `${adjustedX}px` }\"\n            @click.stop\n        >\n            <li\n                v-for=\"(btn, index) in rightButtons\"\n                :key=\"index\"\n                :class=\"[{ disabled: disabled(btn) }, { divided: btn.divided }]\"\n                @click=\"!disabled(btn) && rightButtonClick(btn)\"\n            >\n                {{ btn.label }}\n            </li>\n        </ul>\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { ref, computed, onMounted } from 'vue';\nimport { GlobalStore } from '@/store';\nconst slots = useSlots();\n\ndefineOptions({ name: 'ComplexTable' });\nexport interface DropdownProps {\n    disabled?: any;\n    command?: string | number | object;\n    label?: string | number;\n    [k: string]: any;\n}\n\nconst props = defineProps({\n    header: String,\n    paginationConfig: {\n        type: Object,\n        required: false,\n    },\n    heightDiff: {\n        type: Number,\n        default: 320,\n    },\n    height: {\n        type: Number,\n        default: 0,\n    },\n    rightButtons: {\n        type: Array as PropType<DropdownProps[]>,\n    },\n});\nconst emit = defineEmits(['search', 'update:selects', 'update:paginationConfig']);\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst tableRef = ref();\nconst tableHeight = ref<number | string>('');\nconst menuRef = ref<HTMLElement | null>(null);\nconst leftSelect = ref(false);\n\nconst rightClick = ref({\n    visible: false,\n    left: 0,\n    top: 0,\n    currentRow: null,\n});\nconst handleRightClick = (row, column, event) => {\n    clearSelects();\n    tableRef.value.refElTable.toggleRowSelection(row);\n    if (!props.rightButtons) {\n        return;\n    }\n    event.preventDefault();\n    rightClick.value = {\n        visible: true,\n        left: event.clientX + 5,\n        top: event.clientY,\n        currentRow: row,\n    };\n    document.addEventListener('click', closeRightClick);\n};\nconst closeRightClick = () => {\n    rightClick.value.visible = false;\n    clearSelects();\n    document.removeEventListener('click', closeRightClick);\n};\nconst disabled = computed(() => {\n    return function (btn: any) {\n        return typeof btn.disabled === 'function' ? btn.disabled(rightClick.value.currentRow) : btn.disabled;\n    };\n});\nfunction rightButtonClick(btn: any) {\n    closeRightClick();\n    btn.click(rightClick.value.currentRow);\n}\n\nfunction currentChange() {\n    emit('search');\n}\n\nfunction sizeChange() {\n    props.paginationConfig.currentPage = 1;\n    localStorage.setItem(props.paginationConfig.cacheSizeKey, props.paginationConfig.pageSize);\n    emit('search');\n}\n\nfunction handleSelectionChange(row: any) {\n    emit('update:selects', row);\n    if (row.length > 0) {\n        leftSelect.value = true;\n    } else {\n        leftSelect.value = false;\n    }\n}\n\nfunction sort(prop: string, order: string) {\n    tableRef.value.refElTable.sort(prop, order);\n}\n\nfunction clearSelects() {\n    tableRef.value.refElTable.clearSelection();\n}\n\nfunction clearSort() {\n    tableRef.value.refElTable.clearSort();\n}\n\nconst adjustedX = ref(rightClick.value.left);\nconst adjustedY = ref(rightClick.value.top);\n\nwatch(\n    () => [rightClick.value.left, rightClick.value.top],\n    async () => {\n        await nextTick();\n        if (!menuRef.value) return;\n\n        const menuRect = menuRef.value.getBoundingClientRect();\n        const windowWidth = window.innerWidth;\n        const windowHeight = window.innerHeight;\n\n        if (rightClick.value.left + menuRect.width > windowWidth) {\n            adjustedX.value = windowWidth - menuRect.width - 4;\n        } else {\n            adjustedX.value = rightClick.value.left;\n        }\n\n        if (rightClick.value.top + menuRect.height > windowHeight) {\n            adjustedY.value = windowHeight - menuRect.height - 4;\n        } else {\n            adjustedY.value = rightClick.value.top;\n        }\n    },\n    { immediate: true },\n);\n\nfunction handleRowClick(row: any, column: any, event: any) {\n    if (!tableRef.value) return;\n    try {\n        const selectionColumn = tableRef.value.refElTable.columns.find((col) => col.type === 'selection');\n        const isSelectable = selectionColumn.selectable(row);\n        if (!isSelectable) return;\n    } catch {}\n\n    const target = event.target as HTMLElement;\n\n    if (target.closest('.el-checkbox')) return;\n    if (\n        target.closest('button') ||\n        target.closest('a') ||\n        target.closest('.el-switch') ||\n        target.closest('.el-select') ||\n        target.closest('.table-link') ||\n        target.closest('.cursor-pointer')\n    ) {\n        return;\n    }\n    tableRef.value.refElTable.toggleRowSelection(row);\n}\n\ndefineExpose({\n    clearSelects,\n    sort,\n    clearSort,\n    closeRightClick,\n});\n\nfunction calcHeight() {\n    let heightDiff = props.heightDiff ?? 320;\n    let tabHeight = globalStore.openMenuTabs ? 48 : 0;\n\n    if (props.height) {\n        tableHeight.value = props.height - tabHeight;\n    } else {\n        tableHeight.value = window.innerHeight - heightDiff - tabHeight;\n    }\n}\n\nconst toggleSelection = () => {\n    tableRef.value.refElTable.toggleAllSelection();\n};\n\nonMounted(() => {\n    calcHeight();\n    window.addEventListener('resize', calcHeight);\n    watch(\n        () => props.height,\n        () => {\n            calcHeight();\n        },\n    );\n});\n\nonBeforeUnmount(() => {\n    window.removeEventListener('resize', calcHeight);\n});\n</script>\n\n<style scoped lang=\"scss\">\n@use '@/styles/mixins.scss' as *;\n\n.complex-table {\n    .complex-table__header {\n        @include flex-row(flex-start, center);\n        line-height: 60px;\n        font-size: 18px;\n    }\n\n    .complex-table__body {\n        margin-top: 10px;\n    }\n\n    .complex-table__toolbar {\n        @include flex-row(space-between, center);\n\n        .fu-search-bar {\n            width: auto;\n        }\n    }\n    .complex-table__pagination {\n        margin-top: 20px;\n        @include flex-row(flex-end);\n    }\n}\n.context-menu {\n    position: fixed;\n    background: var(--panel-main-bg-color-9);\n    border: 1px solid var(--el-border-color);\n    border-radius: 4px;\n    color: var(--el-color-primary);\n    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);\n    list-style: none;\n    font-size: 14px;\n    padding: 4px 0;\n    margin: 0;\n    z-index: 9999;\n    min-width: 120px;\n}\n.context-menu li {\n    padding: 6px 12px;\n    cursor: pointer;\n}\n.context-menu li:hover {\n    background-color: var(--panel-menu-bg-color);\n}\n.context-menu li.disabled {\n    color: var(--el-border-color);\n    cursor: not-allowed;\n}\n.context-menu li.divided {\n    border-top: 1px solid var(--el-border-color);\n}\n.table-footer-container {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n\n    .footer-left {\n        flex-shrink: 0;\n        margin-right: 16px;\n        margin-left: 12px;\n        display: flex;\n\n        .footer-left-button {\n            margin-left: 17px;\n            display: flex;\n        }\n    }\n}\n\n.complex-table__pagination {\n    flex: 1;\n    @include flex-row(flex-end);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/config-card/index.vue",
    "content": "<template>\n    <el-card class=\"config-card\">\n        <div class=\"config-header\">\n            <span>\n                {{ header }}\n            </span>\n\n            <div class=\"header-r\">\n                <slot name=\"header-r\" />\n            </div>\n        </div>\n\n        <el-text type=\"info\">\n            {{ description }}\n        </el-text>\n        <span class=\"ml-5\" v-if=\"website\">\n            <el-tooltip :content=\"$t('xpack.waf.websiteHelper')\" placement=\"bottom\">\n                <el-tag type=\"primary\" size=\"small\">{{ $t('menu.website') }}</el-tag>\n            </el-tooltip>\n        </span>\n\n        <div class=\"config-content\">\n            <slot name=\"content-r\" />\n        </div>\n        <div class=\"config-form\">\n            <slot name=\"content-form\" />\n        </div>\n    </el-card>\n</template>\n\n<script setup lang=\"ts\">\ndefineOptions({ name: 'ConfigCard' });\ndefineProps({\n    header: String,\n    description: String,\n    website: Boolean,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.config-card {\n    cursor: pointer;\n\n    .web-tag {\n        position: absolute;\n        left: 0px;\n        top: 0px;\n    }\n\n    .config-header {\n        margin-bottom: 18px;\n        display: flex;\n        justify-content: space-start;\n        align-items: center;\n        span {\n            font-weight: normal;\n            font-size: 18px;\n        }\n        .header-r {\n            margin-left: 20px;\n        }\n    }\n\n    .config-content {\n        display: flex;\n        justify-content: flex-end;\n    }\n\n    .config-form {\n        margin-top: 20px;\n    }\n}\n.config-card {\n    border: var(--panel-border) !important;\n    &:hover {\n        cursor: pointer;\n        border: 1px solid var(--el-color-primary) !important;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/confirm-dialog/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"submitVisible\" :title=\"header\" size=\"mini\">\n        <div>\n            <span v-if=\"operationInfo\" style=\"font-size: 12px\">{{ operationInfo }}</span>\n            <div :style=\"{ 'margin-top': operationInfo ? '10px' : '0px' }\">\n                <span style=\"font-size: 12px\">{{ $t('commons.msg.operateConfirm') }}</span>\n                <span style=\"font-size: 12px; color: red; font-weight: 500\">'{{ submitInputInfo }}'</span>\n            </div>\n            <el-input style=\"margin-top: 10px\" v-model=\"submitInput\"></el-input>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"onCancel\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"submitInput !== submitInputInfo\" @click=\"onConfirm\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\n\nconst header = ref();\nconst operationInfo = ref();\nconst submitInputInfo = ref();\nconst submitVisible = ref(false);\n\nconst submitInput = ref();\n\ninterface DialogProps {\n    header: string;\n    operationInfo: string;\n    submitInputInfo: string;\n}\n\nconst acceptParams = (props: DialogProps): void => {\n    submitVisible.value = true;\n    header.value = props.header;\n    operationInfo.value = props.operationInfo;\n    submitInputInfo.value = props.submitInputInfo;\n    submitInput.value = '';\n};\nconst emit = defineEmits(['confirm', 'cancel']);\n\nconst onConfirm = async () => {\n    emit('confirm');\n    submitVisible.value = false;\n};\n\nconst onCancel = async () => {\n    emit('cancel');\n    submitVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/copy-button/index.vue",
    "content": "<template>\n    <el-button v-if=\"isIcon\" link @click=\"copyText(content)\" icon=\"DocumentCopy\" class=\"ml-1.5\"></el-button>\n    <el-button @click=\"copyText(content)\" v-else>{{ $t('commons.button.copy') }}</el-button>\n</template>\n\n<script lang=\"ts\" setup>\nimport { copyText } from '@/utils/util';\ndefineOptions({ name: 'CopyButton' });\n\ndefineProps({\n    content: String,\n    isIcon: {\n        type: Boolean,\n        default: true,\n    },\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/del-dialog/index.vue",
    "content": "<template>\n    <div>\n        <DialogPro v-model=\"open\" :title=\"form.title\" size=\"small\" @close=\"handleClose\">\n            <div v-loading=\"loading\">\n                <el-row type=\"flex\" justify=\"center\">\n                    <el-col :span=\"22\">\n                        <el-alert class=\"mt-2\" :show-icon=\"true\" type=\"warning\" :closable=\"false\">\n                            <div v-for=\"(item, index) in form.msgs\" :key=\"index\">\n                                <div style=\"line-height: 20px; word-wrap: break-word\">\n                                    <span>{{ item }}</span>\n                                </div>\n                            </div>\n                        </el-alert>\n                        <slot name=\"content\"></slot>\n                        <ul v-for=\"(item, index) in form.names\" :key=\"index\">\n                            <div style=\"word-wrap: break-word\">\n                                <li>{{ item }}</li>\n                            </div>\n                        </ul>\n                    </el-col>\n                </el-row>\n            </div>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"handleClose\" :disabled=\"loading\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button type=\"primary\" @click=\"onConfirm\" :disabled=\"loading\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DialogPro>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { reactive, ref } from 'vue';\n\ndefineOptions({ name: 'OpDialog' });\n\nconst form = reactive({\n    msgs: [],\n    title: '',\n    names: [],\n    api: null as Function,\n    params: {},\n});\nconst loading = ref();\nconst open = ref();\nconst successMsg = ref('');\nconst noMsg = ref(false);\n\ninterface DialogProps {\n    title: string;\n    msg: string;\n    names: Array<string>;\n\n    api: Function;\n    params: Object;\n    successMsg: string;\n    noMsg: boolean;\n}\nconst acceptParams = (props: DialogProps): void => {\n    form.title = props.title;\n    form.names = props.names;\n    form.msgs = props.msg.split('\\n');\n    form.api = props.api;\n    form.params = props.params;\n    successMsg.value = props.successMsg;\n    if (props.noMsg) {\n        noMsg.value = props.noMsg;\n    }\n    open.value = true;\n};\n\nconst emit = defineEmits(['search', 'cancel', 'submit']);\n\nconst onConfirm = async () => {\n    if (form.api === null) {\n        emit('submit');\n        open.value = false;\n        return;\n    }\n    loading.value = true;\n    await form\n        .api(form.params)\n        .then(() => {\n            emit('search');\n            if (!noMsg.value) {\n                MsgSuccess(successMsg.value ?? i18n.global.t('commons.msg.deleteSuccess'));\n            }\n            open.value = false;\n            loading.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    emit('cancel');\n    open.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/detail-show/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"visible\" :title=\"$t('app.detail')\">\n        <div class=\"mt-5\">\n            <el-descriptions border :column=\"1\">\n                <el-descriptions-item v-for=\"(item, key) in list\" :label=\"item.label\" :key=\"key\">\n                    {{ item.value }}\n                    <CopyButton v-if=\"!item.hideCopy\" :content=\"item.value\" />\n                </el-descriptions-item>\n            </el-descriptions>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\n\nconst list = ref();\nconst visible = ref(false);\n\ninterface DialogProps {\n    list: Array<string>;\n}\n\nconst acceptParams = (props: DialogProps): void => {\n    visible.value = true;\n    list.value = props.list;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/dialog-pro/index.vue",
    "content": "<template>\n    <el-dialog\n        :title=\"title\"\n        v-model=\"dialogVisible\"\n        :destroy-on-close=\"true\"\n        :close-on-click-modal=\"false\"\n        :show-close=\"showClose\"\n        :width=\"size\"\n        :open=\"open\"\n        @opened=\"opened\"\n        :before-close=\"handleBeforeClose\"\n    >\n        <template #header v-if=\"slots.header\">\n            <slot name=\"header\"></slot>\n        </template>\n        <div v-if=\"slots.content\">\n            <slot name=\"content\"></slot>\n        </div>\n        <el-row v-else>\n            <el-col :span=\"22\" :offset=\"1\">\n                <slot></slot>\n            </el-col>\n        </el-row>\n\n        <template #footer v-if=\"slots.footer\">\n            <slot name=\"footer\"></slot>\n        </template>\n    </el-dialog>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, useSlots } from 'vue';\ndefineOptions({ name: 'DialogPro' });\n\nconst props = defineProps({\n    title: String,\n    showClose: {\n        type: Boolean,\n        default: true,\n    },\n    size: {\n        type: String,\n        default: 'normal',\n    },\n    modelValue: {\n        type: Boolean,\n        default: false,\n    },\n});\n\nconst slots = useSlots();\n\nconst emit = defineEmits(['update:modelValue', 'close', 'open', 'opened']);\n\nconst size = computed(() => {\n    switch (props.size) {\n        case 'mini':\n            return '20%';\n        case 'small':\n            return '30%';\n        case 'normal':\n            return '40%';\n        case 'large':\n            return '50%';\n        case 'full':\n            return '100%';\n        case 'w-60':\n            return '60%';\n        case 'w-70':\n            return '70%';\n        case 'w-90':\n            return '90%';\n        default:\n            return '50%';\n    }\n});\n\nconst dialogVisible = computed({\n    get() {\n        return props.modelValue;\n    },\n    set(value: boolean) {\n        emit('update:modelValue', value);\n    },\n});\n\nconst handleBeforeClose = () => {\n    emit('close');\n    dialogVisible.value = false;\n};\nconst open = () => {\n    emit('open');\n};\nconst opened = () => {\n    emit('opened');\n};\n</script>\n"
  },
  {
    "path": "frontend/src/components/docker-proxy/dialog.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.msg.infoTitle')\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" @submit.prevent>\n            <el-form-item :label=\"$t('xpack.node.syncProxyHelper')\">\n                <el-radio-group v-model=\"restart\">\n                    <el-radio :value=\"true\">{{ $t('setting.restartNow') }}</el-radio>\n                    <el-radio :value=\"false\">{{ $t('setting.restartLater') }}</el-radio>\n                </el-radio-group>\n                <span class=\"input-help\" v-if=\"restart\">{{ $t('xpack.node.syncProxyHelper1') }}</span>\n                <span class=\"input-help\" v-else>{{ $t('xpack.node.syncProxyHelper2') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getSettingInfo } from '@/api/modules/setting';\n\nconst open = ref(false);\nconst restart = ref(true);\n\nconst em = defineEmits(['update:withDockerRestart', 'submit']);\ninterface DialogProps {\n    syncList: string;\n    open: boolean;\n}\nconst emit = () => {\n    em('update:withDockerRestart', false);\n    em('submit');\n};\nconst acceptParams = async (props: DialogProps): Promise<void> => {\n    if (props.syncList.indexOf('SyncSystemProxy') === -1) {\n        emit();\n        return;\n    }\n    if (props.open) {\n        open.value = true;\n        return;\n    }\n    try {\n        const res = await getSettingInfo();\n        if (res.data.proxyType === '' || res.data.proxyType === 'close') {\n            emit();\n            return;\n        }\n    } catch (error) {\n        emit();\n        return;\n    }\n\n    let searchXSetting;\n    const xpackModules = import.meta.glob('../../xpack/api/modules/setting.ts', { eager: true });\n    if (xpackModules['../../xpack/api/modules/setting.ts']) {\n        searchXSetting = xpackModules['../../xpack/api/modules/setting.ts']['searchXSetting'] || {};\n        const res = await searchXSetting();\n        if (!res) {\n            emit();\n            return;\n        }\n        if (res.data.proxyDocker === '') {\n            emit();\n            return;\n        }\n        open.value = true;\n    }\n};\n\nconst onConfirm = async () => {\n    em('update:withDockerRestart', restart.value);\n    em('submit');\n    open.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/docker-proxy/docker-restart.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" @submit.prevent>\n            <el-form-item :label=\"title\">\n                <el-radio-group v-model=\"restart\">\n                    <el-radio :value=\"true\">{{ $t('setting.restartNow') }}</el-radio>\n                    <el-radio :value=\"false\">{{ $t('setting.restartLater') }}</el-radio>\n                </el-radio-group>\n                <span class=\"input-help\" v-if=\"restart\">{{ $t('xpack.node.syncProxyHelper1') }}</span>\n                <span class=\"input-help\" v-else>{{ $t('xpack.node.syncProxyHelper2') }}</span>\n            </el-form-item>\n        </el-form>\n        <slot name=\"helper\" />\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nconst open = ref(false);\nconst title = ref();\nconst restart = ref(true);\n\nconst em = defineEmits(['update:withDockerRestart', 'submit']);\ninterface DialogProps {\n    title: string;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    title.value = params.title;\n    open.value = true;\n};\n\nconst onConfirm = async () => {\n    em('update:withDockerRestart', restart.value);\n    em('submit');\n    open.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/docker-proxy/index.vue",
    "content": "<template>\n    <div v-if=\"showOption\" class=\"w-full\">\n        <el-radio-group v-model=\"restart\" @change=\"changeRestart\">\n            <el-radio :value=\"true\">{{ $t('setting.restartNow') }}</el-radio>\n            <el-radio :value=\"false\">{{ $t('setting.restartLater') }}</el-radio>\n        </el-radio-group>\n        <span class=\"input-help\" v-if=\"restart\">{{ $t('xpack.node.syncProxyHelper3') }}</span>\n        <span class=\"input-help\" v-else>{{ $t('xpack.node.syncProxyHelper4') }}</span>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getSettingInfo } from '@/api/modules/setting';\n\nconst showOption = ref(false);\nconst restart = ref(false);\n\nconst em = defineEmits(['update:withDockerRestart']);\nconst props = defineProps({\n    syncList: String,\n});\n\nconst loadStatus = async () => {\n    if (props.syncList.indexOf('SyncSystemProxy') === -1) {\n        em('update:withDockerRestart', false);\n        return;\n    }\n    await getSettingInfo()\n        .then((res) => {\n            if (res.data.proxyType === '' || res.data.proxyType === 'close') {\n                em('update:withDockerRestart', false);\n                return;\n            }\n        })\n        .catch(() => {\n            em('update:withDockerRestart', false);\n            return;\n        });\n    let searchXSetting;\n    const xpackModules = import.meta.glob('../../xpack/api/modules/setting.ts', { eager: true });\n    if (xpackModules['../../xpack/api/modules/setting.ts']) {\n        searchXSetting = xpackModules['../../xpack/api/modules/setting.ts']['searchXSetting'] || {};\n        const res = await searchXSetting();\n        if (!res) {\n            em('update:withDockerRestart', false);\n            return;\n        }\n        if (res.data.proxyDocker === '') {\n            em('update:withDockerRestart', false);\n            return;\n        }\n        showOption.value = true;\n    }\n};\n\nconst changeRestart = () => {\n    em('update:withDockerRestart', restart.value);\n};\n\nonMounted(() => {\n    showOption.value = false;\n    loadStatus();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/drawer-pro/index.vue",
    "content": "<template>\n    <el-drawer\n        v-model=\"localOpenPage\"\n        @close=\"handleClose\"\n        :destroy-on-close=\"true\"\n        :before-close=\"beforeClose\"\n        :size=\"isFull ? '100%' : size\"\n        :close-on-press-escape=\"autoClose\"\n        :close-on-click-modal=\"autoClose\"\n    >\n        <template #header>\n            <el-page-header @back=\"handleBack\">\n                <template #content>\n                    <span>{{ header }}</span>\n                    <span v-if=\"resource != ''\">\n                        -\n                        <el-tooltip v-if=\"resource.length > 25\" :content=\"resource\" placement=\"bottom\">\n                            <el-text type=\"primary\">{{ resource.substring(0, 23) + '...' }}</el-text>\n                        </el-tooltip>\n                        <el-text type=\"primary\" v-else>{{ resource }}</el-text>\n                    </span>\n                    <el-divider v-if=\"slots.buttons\" direction=\"vertical\" />\n                    <slot v-if=\"slots.buttons\" name=\"buttons\"></slot>\n                </template>\n                <template #extra>\n                    <el-tooltip :content=\"loadTooltip()\" placement=\"top\" v-if=\"fullScreen\">\n                        <el-button\n                            @click=\"toggleFullscreen\"\n                            link\n                            icon=\"FullScreen\"\n                            plain\n                            class=\"-mt-1 mr-2\"\n                        ></el-button>\n                    </el-tooltip>\n                    <slot v-if=\"slots.extra\" name=\"extra\"></slot>\n                </template>\n            </el-page-header>\n        </template>\n\n        <div ref=\"drawerContent\">\n            <div v-if=\"slots.content\">\n                <slot name=\"content\"></slot>\n            </div>\n            <el-row v-else>\n                <el-col :span=\"22\" :offset=\"1\">\n                    <slot></slot>\n                </el-col>\n            </el-row>\n        </div>\n\n        <template #footer v-if=\"slots.footer\">\n            <slot name=\"footer\"></slot>\n        </template>\n    </el-drawer>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, useSlots, ref } from 'vue';\ndefineOptions({ name: 'DrawerPro' });\nimport i18n from '@/lang';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\nconst drawerContent = ref();\n\nconst isFull = ref();\n\nconst props = defineProps({\n    header: String,\n    back: Function,\n    resource: {\n        type: String,\n        default: '',\n    },\n    size: {\n        type: String,\n        default: 'normal',\n    },\n    modelValue: {\n        type: Boolean,\n        default: false,\n    },\n    fullScreen: {\n        type: Boolean,\n        default: false,\n    },\n    autoClose: {\n        type: Boolean,\n        default: true,\n    },\n    confirmBeforeClose: {\n        type: Boolean,\n        default: false,\n    },\n});\n\nconst slots = useSlots();\nconst emit = defineEmits(['update:modelValue', 'close', 'beforeClose']);\n\nconst size = computed(() => {\n    switch (props.size) {\n        case 'small':\n            return '30%';\n        case 'normal':\n            return '40%';\n        case 'large':\n            return '50%';\n        case 'full':\n            return '100%';\n        case '60%':\n            return '60%';\n        case props.size:\n            return props.size;\n        default:\n            return '50%';\n    }\n});\n\nconst localOpenPage = computed({\n    get() {\n        return props.modelValue;\n    },\n    set(value: boolean) {\n        emit('update:modelValue', value);\n    },\n});\n\nconst handleBack = () => {\n    if (props.confirmBeforeClose) {\n        const done = () => {\n            if (props.back) {\n                props.back();\n            } else {\n                localOpenPage.value = false;\n                globalStore.isFullScreen = false;\n            }\n        };\n        emit('beforeClose', done);\n    } else {\n        if (props.back) {\n            props.back();\n        } else {\n            localOpenPage.value = false;\n            globalStore.isFullScreen = false;\n        }\n    }\n};\n\nconst handleClose = () => {\n    localOpenPage.value = false;\n    globalStore.isFullScreen = false;\n    emit('close');\n};\n\nconst beforeClose = (done: () => void) => {\n    if (!props.confirmBeforeClose) {\n        done();\n    } else {\n        emit('beforeClose', done);\n    }\n};\n\nfunction toggleFullscreen() {\n    globalStore.isFullScreen = !globalStore.isFullScreen;\n    isFull.value = globalStore.isFullScreen;\n}\nconst loadTooltip = () => {\n    return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen'));\n};\n</script>\n"
  },
  {
    "path": "frontend/src/components/error-message/404.vue",
    "content": "<template>\n    <div class=\"not-container\">\n        <img src=\"@/assets/images/404.svg\" class=\"not-img\" alt=\"404\" />\n        <div class=\"not-detail\">\n            <h2>404</h2>\n            <h4>{{ $t('commons.msg.notFound') }}</h4>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\" name=\"404\"></script>\n\n<style scoped lang=\"scss\">\n.not-container {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 100%;\n    height: 100%;\n    .not-img {\n        margin-top: 300px;\n    }\n    .not-detail {\n        margin-top: 300px;\n        display: flex;\n        flex-direction: column;\n        h2,\n        h4 {\n            padding: 0;\n            margin: 0;\n        }\n        h2 {\n            font-size: 60px;\n            color: #434e59;\n        }\n        h4 {\n            margin: 30px 0 20px;\n            font-size: 19px;\n            font-weight: normal;\n            color: #848587;\n        }\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/error-message/err_domain.vue",
    "content": "<template>\n    <div class=\"not-container\">\n        <img src=\"@/assets/images/unsafe.svg\" class=\"not-img\" alt=\"404\" />\n        <div class=\"not-detail\">\n            <h2>{{ $t('commons.login.notSafe') }}</h2>\n            <h4>{{ $t('commons.login.errDomain1') }}</h4>\n            <div>\n                <h4>{{ $t('commons.login.errHelper') }} 1pctl reset domain</h4>\n                <div class=\"float-left cursor-pointer\">\n                    <el-icon color=\"#409EFC\" class=\"ml-1.5 mt-8\" :size=\"18\" @click=\"copyText('1pctl reset domain')\">\n                        <DocumentCopy />\n                    </el-icon>\n                </div>\n            </div>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\" name=\"404\">\nimport { copyText } from '@/utils/util';\n</script>\n\n<style scoped lang=\"scss\">\n.not-container {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 100%;\n    height: 100%;\n    .not-img {\n        margin-top: 300px;\n    }\n    .not-detail {\n        margin-top: 300px;\n        display: flex;\n        flex-direction: column;\n        h2,\n        h4 {\n            padding: 0;\n            margin: 0;\n        }\n        h2 {\n            font-size: 60px;\n            color: #434e59;\n        }\n        h4 {\n            margin: 30px 0 20px;\n            float: left;\n            font-size: 19px;\n            font-weight: normal;\n            color: #848587;\n        }\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/error-message/err_ip.vue",
    "content": "<template>\n    <div class=\"not-container\">\n        <img src=\"@/assets/images/unsafe.svg\" class=\"not-img\" alt=\"404\" />\n        <div class=\"not-detail\">\n            <h2>{{ $t('commons.login.notSafe') }}</h2>\n            <h4>{{ $t('commons.login.errIP1') }}</h4>\n            <div>\n                <h4>{{ $t('commons.login.errHelper') }} 1pctl reset ips</h4>\n                <div class=\"float-left cursor-pointer\">\n                    <el-icon color=\"#409EFC\" class=\"ml-1.5 mt-8\" :size=\"18\" @click=\"copyText('1pctl reset ips')\">\n                        <DocumentCopy />\n                    </el-icon>\n                </div>\n            </div>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\" name=\"404\">\nimport { copyText } from '@/utils/util';\n</script>\n\n<style scoped lang=\"scss\">\n.not-container {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 100%;\n    .not-img {\n        margin-top: 300px;\n    }\n    .not-detail {\n        margin-top: 300px;\n        display: flex;\n        flex-direction: column;\n        h2,\n        h4 {\n            padding: 0;\n            margin: 0;\n        }\n        h2 {\n            font-size: 60px;\n            color: #434e59;\n        }\n        h4 {\n            margin: 30px 0 20px;\n            float: left;\n            font-size: 19px;\n            font-weight: normal;\n            color: #848587;\n        }\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/error-message/error_code.vue",
    "content": "<template>\n    <div>\n        <div>\n            <el-row type=\"flex\" justify=\"center\">\n                <h1>{{ loadErrInfo() }}</h1>\n            </el-row>\n        </div>\n        <hr />\n        <div>\n            <el-row type=\"flex\" justify=\"center\"><span>nginx</span></el-row>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nconst props = defineProps({\n    code: String,\n});\nconst loadErrInfo = () => {\n    switch (props.code) {\n        case '400':\n            return '400 Bad Request';\n        case '401':\n            return '401 Unauthorized';\n        case '403':\n            return '403 Forbidden';\n        case '404':\n            return '404 Not Found';\n        case '408':\n            return '408 Request Timeout';\n        case '416':\n            return '416 Requested Not Satisfiable';\n    }\n};\n</script>\n\n<style scoped lang=\"scss\">\n.container {\n    margin-left: 40%;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/error-message/unsafe.vue",
    "content": "<template>\n    <div class=\"not-container\">\n        <img src=\"@/assets/images/unsafe.svg\" class=\"not-img\" alt=\"404\" />\n        <div class=\"not-detail\">\n            <h2>{{ $t('commons.login.notSafe') }}</h2>\n            <h4>{{ $t('commons.login.safeEntrance1') }}</h4>\n            <div>\n                <h4>{{ $t('commons.login.safeEntrance2') }}</h4>\n                <div class=\"float-left cursor-pointer\">\n                    <el-icon color=\"#409EFC\" class=\"ml-1.5 mt-8\" :size=\"18\" @click=\"copyText('1pctl user-info')\">\n                        <DocumentCopy />\n                    </el-icon>\n                </div>\n            </div>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\" name=\"404\">\nimport { copyText } from '@/utils/util';\n</script>\n\n<style scoped lang=\"scss\">\n.not-container {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 100%;\n    height: 100%;\n    .not-img {\n        margin-top: 300px;\n    }\n    .not-detail {\n        margin-top: 300px;\n        display: flex;\n        flex-direction: column;\n        h2,\n        h4 {\n            padding: 0;\n            margin: 0;\n        }\n        h2 {\n            font-size: 60px;\n            color: #434e59;\n        }\n        h4 {\n            margin: 30px 0 20px;\n            float: left;\n            font-size: 19px;\n            font-weight: normal;\n            color: #848587;\n        }\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/error-prompt/index.vue",
    "content": "<template>\n    <div class=\"msg\">\n        <span>{{ props.title }}</span>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\ndefineOptions({ name: 'ErrPrompt' });\nconst props = defineProps({\n    title: {\n        type: String,\n        default: '',\n    },\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.msg {\n    color: #ff0000;\n    line-height: 30px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/exist-file/index.vue",
    "content": "<template>\n    <div>\n        <el-dialog\n            v-model=\"dialogVisible\"\n            :title=\"$t('file.existFileTitle')\"\n            width=\"35%\"\n            :close-on-click-modal=\"false\"\n            :destroy-on-close=\"true\"\n        >\n            <el-alert :show-icon=\"true\" type=\"warning\" :closable=\"false\">\n                <div class=\"whitespace-break-spaces\">\n                    <span>{{ $t('file.existFileHelper') }}</span>\n                </div>\n            </el-alert>\n            <div>\n                <el-table :data=\"existFiles\" max-height=\"350\">\n                    <el-table-column type=\"index\" :label=\"$t('commons.table.serialNumber')\" width=\"55\" />\n                    <el-table-column prop=\"path\" :label=\"$t('commons.table.name')\" :min-width=\"200\" />\n                    <el-table-column :label=\"$t('file.existFileSize')\" width=\"230\">\n                        <template #default=\"{ row }\">\n                            {{ getFileSize(row.uploadSize) }} -> {{ getFileSize(row.size) }}\n                        </template>\n                    </el-table-column>\n                </el-table>\n            </div>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"handleSkip\">{{ $t('commons.button.skip') }}</el-button>\n                    <el-button type=\"primary\" @click=\"handleOverwrite()\">\n                        {{ $t('commons.button.cover') }}\n                    </el-button>\n                </span>\n            </template>\n        </el-dialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { computeSize } from '@/utils/util';\n\nconst dialogVisible = ref();\nconst existFiles = ref<DialogProps[]>([]);\n\ninterface DialogProps {\n    name: string;\n    path: string;\n    size: number;\n    uploadSize: number;\n    modTime: string;\n}\nlet onConfirmCallback = null;\nconst getFileSize = (size: number) => {\n    return computeSize(size);\n};\n\nconst handleSkip = () => {\n    dialogVisible.value = false;\n    if (onConfirmCallback) {\n        onConfirmCallback(\n            'skip',\n            existFiles.value.map((file) => file.path),\n        );\n    }\n};\nconst handleOverwrite = () => {\n    dialogVisible.value = false;\n    if (onConfirmCallback) {\n        onConfirmCallback('overwrite');\n    }\n};\nconst acceptParams = async ({ paths, onConfirm }): Promise<void> => {\n    existFiles.value = paths;\n    onConfirmCallback = onConfirm;\n    dialogVisible.value = true;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/components/file-list/index.vue",
    "content": "<template>\n    <DialogPro :title=\"$t('file.list')\" size=\"w-60\" v-model=\"open\" @close=\"handleClose\">\n        <div>\n            <div\n                v-show=\"!searchableStatus\"\n                @click=\"searchableStatus = true\"\n                class=\"address-bar shadow-md rounded-md px-4 py-2 flex items-center flex-grow\"\n            >\n                <span class=\"root mr-1\">\n                    <el-link @click.stop=\"jump(-1)\">\n                        <el-icon :size=\"20\"><HomeFilled /></el-icon>\n                    </el-link>\n                </span>\n                <span v-if=\"paths.length > 0\">\n                    <span v-for=\"(_, index) in paths\" class=\"inline-flex items-center\" :key=\"index\">\n                        <span class=\"ml-1 mr-1 arrow\">></span>\n                        <el-tooltip effect=\"dark\" :content=\"paths[index]\" placement=\"top\">\n                            <el-link class=\"path-segment cursor-pointer mr-1 pathname\" @click.stop=\"jump(index)\">\n                                {{ paths[index].length > 25 ? paths[index].substring(0, 22) + '...' : paths[index] }}\n                            </el-link>\n                        </el-tooltip>\n                    </span>\n                </span>\n            </div>\n            <el-input\n                ref=\"searchableInputRef\"\n                v-show=\"searchableStatus\"\n                v-model=\"searchablePath\"\n                @blur=\"searchableInputBlur\"\n                class=\"px-4 py-2 border rounded-md shadow-md\"\n                @keyup.enter=\"\n                    jumpPath();\n                    searchableStatus = false;\n                \"\n            />\n        </div>\n        <el-button class=\"mt-4 float-left\" link @click=\"jump(paths.length - 2)\" type=\"primary\" size=\"small\">\n            {{ $t('file.top') }}\n        </el-button>\n        <div class=\"mt-4 float-right\">\n            <el-popover placement=\"bottom-end\" :width=\"300\" trigger=\"click\" @show=\"loadFavorites\">\n                <template #reference>\n                    <el-button link type=\"primary\" size=\"small\">\n                        {{ $t('file.favorite') }}\n                    </el-button>\n                </template>\n                <div v-if=\"favorites.length === 0\" class=\"text-center py-3 text-gray-400 text-sm\">\n                    {{ $t('commons.msg.noneData') }}\n                </div>\n                <div v-else class=\"favorite-list max-h-52 overflow-y-auto\">\n                    <div\n                        v-for=\"item in favorites\"\n                        :key=\"item.id\"\n                        class=\"favorite-item flex items-center cursor-pointer px-2 py-1.5 rounded\"\n                        @click=\"jumpToFavorite(item)\"\n                    >\n                        <svg-icon\n                            :iconName=\"item.isDir ? 'p-file-folder' : 'p-file-normal'\"\n                            class=\"mr-2 flex-shrink-0\"\n                        ></svg-icon>\n                        <el-tooltip :content=\"item.path\" placement=\"top\">\n                            <span class=\"truncate text-sm\">{{ item.name }}</span>\n                        </el-tooltip>\n                    </div>\n                </div>\n            </el-popover>\n            <el-button link @click=\"onAddItem(true)\" type=\"primary\" size=\"small\">\n                {{ $t('commons.button.createNewFolder') }}\n            </el-button>\n            <el-button link @click=\"onAddItem(false)\" type=\"primary\" size=\"small\">\n                {{ $t('commons.button.createNewFile') }}\n            </el-button>\n        </div>\n        <div v-loading=\"loading\">\n            <el-table\n                :data=\"data\"\n                highlight-current-row\n                height=\"40vh\"\n                @row-click=\"handleRowClick\"\n                class=\"cursor-pointer dialog-file-list\"\n            >\n                <el-table-column prop=\"name\" show-overflow-tooltip fix>\n                    <template #default=\"{ row }\">\n                        <svg-icon\n                            :class=\"'table-icon'\"\n                            :iconName=\"row.isDir ? 'p-file-folder' : 'p-file-normal'\"\n                        ></svg-icon>\n                        <template v-if=\"!row.isCreate\">\n                            {{ row.name }}\n                        </template>\n\n                        <template v-else>\n                            <el-input\n                                ref=\"rowRefs\"\n                                v-model=\"newFolder\"\n                                class=\"p-w-200\"\n                                placeholder=\"new folder\"\n                                @input=\"handleChange(newFolder, row)\"\n                            ></el-input>\n                            <el-button link @click=\"createFolder(row)\" type=\"primary\" size=\"small\" class=\"ml-2\">\n                                {{ $t('commons.button.save') }}\n                            </el-button>\n                            <el-button link @click=\"cancelFolder(row)\" type=\"primary\" size=\"small\" class=\"!ml-2\">\n                                {{ $t('commons.button.cancel') }}\n                            </el-button>\n                        </template>\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"size\" width=\"160px\" fix>\n                    <template #default=\"{ row }\">\n                        <el-button\n                            type=\"primary\"\n                            link\n                            small\n                            v-if=\"!row.isCreate\"\n                            :loading=\"row.btnLoading\"\n                            @click=\"row.isDir ? getDirSize(row.path) : getFileSize(row.path)\"\n                        >\n                            <span v-if=\"row.isDir\">\n                                <span v-if=\"row.dirSize === undefined\">\n                                    {{ $t('file.calculate') }}\n                                </span>\n                                <span v-else>{{ computeSize(row.dirSize) }}</span>\n                            </span>\n                            <span v-else>\n                                {{ computeSize(row.size) }}\n                            </span>\n                        </el-button>\n                    </template>\n                </el-table-column>\n            </el-table>\n        </div>\n        <div class=\"file-list-bottom\">\n            <div v-if=\"!isMultiple && selectRow?.path\">\n                {{ $t('file.currentSelect') }}\n                <el-tooltip :content=\"selectRow.path\" placement=\"top\">\n                    <el-tag type=\"success\">\n                        <div class=\"path\">\n                            <span>{{ selectRow.path }}</span>\n                        </div>\n                    </el-tag>\n                </el-tooltip>\n            </div>\n            <div v-if=\"isMultiple && selectRows.length > 0\">\n                {{ $t('file.currentSelect') }}\n                <el-tag\n                    v-for=\"(item, index) in selectRows\"\n                    :key=\"item.path\"\n                    type=\"success\"\n                    closable\n                    class=\"mr-1 mb-1\"\n                    @close=\"removeSelected(index)\"\n                >\n                    <el-tooltip :content=\"item.path\" placement=\"top\">\n                        <div class=\"path\">\n                            <span>{{ item.path }}</span>\n                        </div>\n                    </el-tooltip>\n                </el-tag>\n            </div>\n        </div>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"selectFile\" :disabled=\"disBtn\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { File } from '@/api/interface/file';\nimport { computeDirSize, createFile, getFileContent, getFilesList, searchFavorite } from '@/api/modules/files';\nimport { nextTick, onUpdated, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { useSearchableForSelect } from '@/views/host/file-management/hooks/searchable';\nimport { computeSize, debounce } from '@/utils/util';\n\nconst data = ref([]);\nconst loading = ref(false);\nconst paths = ref<string[]>([]);\nconst req = reactive({ path: '/', expand: true, page: 1, pageSize: 300, showHidden: true });\nconst selectRow = ref({ path: '', name: '' });\nconst selectRows = ref<Array<{ path: string; name: string }>>([]);\nconst isMultiple = ref(false);\nconst rowRefs = ref();\nconst open = ref(false);\nconst newFolder = ref();\nconst disBtn = ref(false);\nconst favorites = ref<File.Favorite[]>([]);\n\nconst { searchableStatus, searchablePath, searchableInputRef, searchableInputBlur } = useSearchableForSelect(paths);\nconst oldUrl = ref<string>('');\n\nconst em = defineEmits(['choose']);\n\nconst form = reactive({\n    path: '/',\n    dir: false,\n    isAll: false,\n    disabled: false,\n});\n\ninterface DialogProps {\n    path?: string;\n    dir: boolean;\n    isAll?: boolean;\n    disabled?: boolean;\n    multiple?: boolean;\n}\nconst acceptParams = (props: DialogProps): void => {\n    form.path = props.path || '/';\n    form.dir = props.dir;\n    form.isAll = props.isAll || false;\n    form.disabled = props.disabled || false;\n    isMultiple.value = props.multiple || false;\n    selectRows.value = [];\n    selectRow.value = { path: '', name: '' };\n    openPage();\n    req.path = form.path;\n    oldUrl.value = form.path;\n    search(req);\n    open.value = true;\n};\n\nconst selectFile = () => {\n    if (isMultiple.value) {\n        em(\n            'choose',\n            selectRows.value.map((r) => r.path),\n        );\n    } else {\n        if (selectRow.value) {\n            em('choose', selectRow.value.path);\n        }\n    }\n    handleClose();\n};\n\nconst handleClose = () => {\n    open.value = false;\n    selectRow.value = { path: '', name: '' };\n    selectRows.value = [];\n};\n\nconst openPage = () => {\n    open.value = true;\n    selectRow.value.path = form.dir ? form.path || '/' : '';\n};\n\nconst openDir = async (row: File.File, column: any, event: any) => {\n    if (event?.target?.tagName === 'BUTTON' || event?.target?.tagName === 'SPAN') {\n        return;\n    }\n    if (row.isDir) {\n        const name = row.name;\n        paths.value.push(name);\n        if (req.path === '/') {\n            req.path = req.path + name;\n        } else {\n            req.path = req.path + '/' + name;\n        }\n        loading.value = true;\n        await search(req)\n            .then(() => {\n                loading.value = false;\n                if (form.isAll || form.dir) {\n                    selectRow.value.path = req.path;\n                } else {\n                    selectRow.value.path = '';\n                }\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n        return;\n    }\n    // Handle file click\n    const fullPath = (req.path === '/' ? req.path : req.path + '/') + row.name;\n    if (isMultiple.value) {\n        // Multiple mode: toggle selection\n        const index = selectRows.value.findIndex((r) => r.path === fullPath);\n        if (index > -1) {\n            selectRows.value.splice(index, 1);\n        } else {\n            selectRows.value.push({ path: fullPath, name: row.name });\n        }\n        return;\n    }\n    // Single mode: original behavior\n    if (form.isAll || !form.dir) {\n        selectRow.value.path = fullPath;\n        return;\n    }\n    selectRow.value.path = '';\n};\nconst handleRowClick = (row: any, column: any, event: any) => {\n    if (row.isCreate) {\n        return;\n    }\n    debouncedOpenDir(row, column, event);\n};\nconst debouncedOpenDir = debounce(openDir, 300);\n\nconst jump = async (index: number) => {\n    oldUrl.value = req.path;\n    let path = '';\n    if (index !== -1) {\n        const jPaths = paths.value.slice(0, index + 1);\n        path = '/' + jPaths.join('/');\n    }\n    path = path || '/';\n    req.path = path;\n    selectRow.value.path = form.dir ? req.path : '';\n    await search(req);\n    open.value = true;\n};\n\nconst jumpPath = async () => {\n    loading.value = true;\n    try {\n        oldUrl.value = req.path;\n        getPaths(searchablePath.value);\n        req.path = searchablePath.value || '/';\n        search(req);\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst getFileSize = async (path: string) => {\n    let params = {\n        path: path,\n        expand: true,\n        isDetail: true,\n        page: 1,\n        pageSize: 100,\n    };\n    updateByPath(path, { btnLoading: true });\n    try {\n        const res = await getFileContent(params);\n        updateByPath(path, { dirSize: res.data.size });\n    } finally {\n        updateByPath(path, { btnLoading: false });\n    }\n};\n\nconst getDirSize = async (path: string) => {\n    const req = {\n        path: path,\n    };\n    updateByPath(path, { btnLoading: true });\n    try {\n        const res = await computeDirSize(req);\n        updateByPath(path, { dirSize: res.data.size });\n    } finally {\n        updateByPath(path, { btnLoading: false });\n    }\n};\n\nconst updateByPath = (path: string, patch: Partial<(typeof data.value)[0]>) => {\n    data.value = data.value.map((item) => (item.path === path ? { ...item, ...patch } : item));\n};\n\nconst getPaths = (reqPath: string) => {\n    const pathArray = reqPath.split('/');\n    paths.value = [];\n    let base = '/';\n    for (const p of pathArray) {\n        if (p != '') {\n            if (base.endsWith('/')) {\n                base = base + p;\n            } else {\n                base = base + '/' + p;\n            }\n            paths.value.push(p);\n        }\n    }\n};\n\nconst search = async (req: File.ReqFile) => {\n    req.dir = form.dir;\n    loading.value = true;\n    await getFilesList(req)\n        .then((res) => {\n            if (!res.data.path) {\n                req.path = oldUrl.value;\n                getPaths(oldUrl.value);\n                MsgWarning(i18n.global.t('commons.res.notFound'));\n                return;\n            }\n            data.value = res.data.items || [];\n            req.path = res.data.path;\n            const pathArray = req.path.split('/');\n            paths.value = [];\n            for (const p of pathArray) {\n                if (p != '') {\n                    paths.value.push(p);\n                }\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nlet addForm = reactive({ path: '', name: '', isDir: true, mode: 0o755, isLink: false, isSymlink: true, linkPath: '' });\n\nconst onAddItem = async (isDir: boolean) => {\n    const createRow = data.value?.find((row: { isCreate: any }) => row.isCreate);\n    if (createRow) {\n        MsgWarning(i18n.global.t('commons.msg.creatingInfo'));\n        return;\n    }\n    newFolder.value = isDir ? i18n.global.t('file.noNameFolder') : i18n.global.t('file.noNameFile');\n    if (form.dir === isDir) {\n        selectRow.value.name = newFolder.value;\n        const basePath = req.path === '/' ? req.path : `${req.path}/`;\n        selectRow.value.path = `${basePath}${newFolder.value}`;\n    }\n    data.value?.unshift({\n        path: selectRow.value.path,\n        isCreate: true,\n        isDir: isDir,\n        name: newFolder.value,\n    });\n    disBtn.value = true;\n    await nextTick();\n    rowRefs.value.focus();\n};\n\nconst cancelFolder = (row: any) => {\n    data.value.shift();\n    row.isCreate = false;\n    disBtn.value = false;\n    selectRow.value.path = form.dir ? req.path : '';\n    newFolder.value = '';\n};\n\nconst handleChange = (value: string, row: any) => {\n    row.name = value;\n    const basePath = req.path === '/' ? req.path : `${req.path}/`;\n    selectRow.value.path = `${basePath}${value}`;\n    if (row.isDir) {\n        if (form.isAll || form.dir) {\n            selectRow.value.path = `${basePath}${value}`;\n        } else {\n            selectRow.value.path = '';\n        }\n        return;\n    }\n    if (form.isAll || !form.dir) {\n        selectRow.value.path = `${basePath}${value}`;\n        return;\n    }\n    selectRow.value.path = '';\n};\n\nconst createFolder = async (row: any) => {\n    const basePath = req.path === '/' ? req.path : `${req.path}/`;\n    addForm.path = `${basePath}${newFolder.value}`;\n    if (addForm.path.indexOf('.1panel_clash') > -1) {\n        MsgWarning(i18n.global.t('file.clashDidNotSupport'));\n        return;\n    }\n    addForm.isDir = row.isDir;\n    addForm.name = newFolder.value;\n    let addItem = {};\n    Object.assign(addItem, addForm);\n    loading.value = true;\n    createFile(addItem as File.FileCreate)\n        .then(() => {\n            row.isCreate = false;\n            disBtn.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonUpdated(() => {\n    if (form.path != '') {\n        req.path = form.path;\n    }\n    search(req);\n});\n\nconst removeSelected = (index: number) => {\n    selectRows.value.splice(index, 1);\n};\n\nconst loadFavorites = async () => {\n    try {\n        const res = await searchFavorite({ page: 1, pageSize: 100 });\n        favorites.value = res.data.items || [];\n    } catch {}\n};\n\nconst jumpToFavorite = async (item: File.Favorite) => {\n    const targetPath = item.isDir ? item.path : item.path.substring(0, item.path.lastIndexOf('/')) || '/';\n    oldUrl.value = req.path;\n    req.path = targetPath;\n    getPaths(targetPath);\n    selectRow.value.path = form.dir ? targetPath : '';\n    await search(req);\n    if (!item.isDir && (form.isAll || !form.dir)) {\n        selectRow.value.path = item.path;\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.file-row {\n    display: flex;\n    align-items: center;\n    width: 100%;\n}\n\n.address-bar {\n    border: var(--el-border);\n    .arrow {\n        color: #726e6e;\n    }\n}\n.file-list-bottom {\n    margin-top: 10px;\n    .path {\n        white-space: nowrap;\n        overflow: hidden;\n        text-overflow: ellipsis;\n    }\n}\n:deep(.dialog-file-list .el-table__body tr.current-row > td.el-table__cell) {\n    background-color: var(--panel-main-bg-color-4);\n}\n\n.favorite-item {\n    transition: background-color 0.15s;\n    &:hover {\n        background-color: var(--panel-main-bg-color-4);\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/file-role/index.vue",
    "content": "<template>\n    <div>\n        <el-form ref=\"ruleForm\" label-position=\"left\" :model=\"form\" label-width=\"100px\" :rules=\"rules\">\n            <el-form-item :label=\"$t('file.owner')\">\n                <el-checkbox v-model=\"form.owner.r\" :label=\"$t('file.rRole')\" />\n                <el-checkbox v-model=\"form.owner.w\" :label=\"$t('file.wRole')\" />\n                <el-checkbox v-model=\"form.owner.x\" :label=\"$t('file.xRole')\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('file.group')\">\n                <el-checkbox v-model=\"form.group.r\" :label=\"$t('file.rRole')\" />\n                <el-checkbox v-model=\"form.group.w\" :label=\"$t('file.wRole')\" />\n                <el-checkbox v-model=\"form.group.x\" :label=\"$t('file.xRole')\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('file.public')\">\n                <el-checkbox v-model=\"form.public.r\" :label=\"$t('file.rRole')\" />\n                <el-checkbox v-model=\"form.public.w\" :label=\"$t('file.wRole')\" />\n                <el-checkbox v-model=\"form.public.x\" :label=\"$t('file.xRole')\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('file.role')\" required prop=\"mode\">\n                <el-input v-model=\"form.mode\" maxlength=\"4\" @input=\"changeMode\"></el-input>\n            </el-form-item>\n        </el-form>\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { FormInstance, FormRules } from 'element-plus';\nimport { computed, ref, toRefs, watch, onUpdated, onMounted, reactive } from 'vue';\nimport { Rules } from '@/global/form-rules';\n\ninterface Role {\n    r: boolean;\n    w: boolean;\n    x: boolean;\n}\ninterface RoleForm {\n    owner: Role;\n    group: Role;\n    public: Role;\n    mode: string;\n}\ninterface Props {\n    mode: string;\n}\n\nconst roles = ref<string[]>(['0', '1', '2', '3', '4', '5', '6', '7']);\n\nconst props = withDefaults(defineProps<Props>(), {\n    mode: '0755',\n});\nconst rules = reactive<FormRules>({\n    mode: [Rules.requiredInput, Rules.filePermission],\n});\n\nconst { mode } = toRefs(props);\nconst ruleForm = ref<FormInstance>();\nlet form = ref<RoleForm>({\n    owner: { r: true, w: true, x: true },\n    group: { r: true, w: true, x: true },\n    public: { r: true, w: false, x: true },\n    mode: '0755',\n});\nconst em = defineEmits(['getMode']);\n\nconst calculate = (role: Role) => {\n    let num = 0;\n    if (role.r) {\n        num = num + 4;\n    }\n    if (role.w) {\n        num = num + 2;\n    }\n    if (role.x) {\n        num = num + 1;\n    }\n\n    return num;\n};\n\nconst getRole = computed(() => {\n    const value =\n        '0' +\n        String(calculate(form.value.owner)) +\n        String(calculate(form.value.group)) +\n        String(calculate(form.value.public));\n    return value;\n});\n\nwatch(\n    () => getRole.value,\n    (newVal) => {\n        form.value.mode = newVal;\n    },\n);\n\nwatch(\n    () => form.value.mode,\n    (newVal) => {\n        em('getMode', Number.parseInt(newVal, 8));\n    },\n);\n\nconst getRoleNum = (roleStr: string, role: Role) => {\n    if (roles.value.indexOf(roleStr) < 0) {\n        return;\n    }\n    switch (roleStr) {\n        case '0':\n            role.x = false;\n            role.w = false;\n            role.r = false;\n            break;\n        case '1':\n            role.x = true;\n            role.w = false;\n            role.r = false;\n            break;\n        case '2':\n            role.x = false;\n            role.w = true;\n            role.r = false;\n            break;\n        case '3':\n            role.x = true;\n            role.w = true;\n            role.r = false;\n            break;\n        case '4':\n            role.x = false;\n            role.w = false;\n            role.r = true;\n            break;\n        case '5':\n            role.x = true;\n            role.w = false;\n            role.r = true;\n            break;\n        case '6':\n            role.x = false;\n            role.w = true;\n            role.r = true;\n            break;\n        case '7':\n            role.x = true;\n            role.w = true;\n            role.r = true;\n            break;\n    }\n};\n\nconst changeMode = (val: String) => {\n    if (val === '' || val.length !== 4) {\n        return;\n    }\n    getRoleNum(val[1], form.value.owner);\n    getRoleNum(val[2], form.value.group);\n    getRoleNum(val[3], form.value.public);\n};\n\nconst updateMode = () => {\n    form.value.mode = mode.value;\n    changeMode(form.value.mode);\n};\n\nonUpdated(() => {\n    updateMode();\n});\n\nonMounted(() => {\n    updateMode();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/group/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.table.manageGroup')\" @close=\"handleClose\" size=\"large\">\n        <template #content>\n            <ComplexTable :data=\"data\" @search=\"search()\">\n                <template #toolbar>\n                    <el-button type=\"primary\" @click=\"openCreate\">{{ $t('website.createGroup') }}</el-button>\n                </template>\n                <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\">\n                    <template #default=\"{ row }\">\n                        <div v-if=\"!row.edit\">\n                            <span v-if=\"row.name === 'Default'\">\n                                {{ $t('commons.table.default') }}\n                            </span>\n                            <span v-if=\"row.name !== 'Default'\">{{ row.name }}</span>\n                            <el-tag v-if=\"row.isDefault\" type=\"success\" class=\"ml-2\" size=\"small\">\n                                ({{ $t('commons.table.default') }})\n                            </el-tag>\n\n                            <el-tag type=\"warning\" size=\"small\" class=\"ml-4\" v-if=\"row.isDelete\">\n                                {{ $t('app.takeDown') }}\n                            </el-tag>\n                        </div>\n\n                        <el-form @submit.prevent ref=\"groupForm\" v-if=\"row.edit\" :model=\"row\">\n                            <el-form-item prop=\"name\" v-if=\"row.edit\" :rules=\"Rules.name\">\n                                <div style=\"margin-top: 20px; width: 100%\"><el-input v-model=\"row.name\" /></div>\n                            </el-form-item>\n                        </el-form>\n                    </template>\n                </el-table-column>\n\n                <el-table-column :label=\"$t('commons.table.operate')\">\n                    <template #default=\"{ row, $index }\">\n                        <div>\n                            <el-button link v-if=\"row.edit\" type=\"primary\" @click=\"saveGroup(groupForm, row)\">\n                                {{ $t('commons.button.save') }}\n                            </el-button>\n                            <el-button link v-if=\"!row.edit\" type=\"primary\" @click=\"editGroup($index)\">\n                                {{ $t('commons.button.edit') }}\n                            </el-button>\n                            <el-button\n                                link\n                                v-if=\"!row.edit\"\n                                :disabled=\"row.isDefault\"\n                                type=\"primary\"\n                                @click=\"removeGroup($index)\"\n                            >\n                                {{ $t('commons.button.delete') }}\n                            </el-button>\n                            <el-button link v-if=\"row.edit\" type=\"primary\" @click=\"search()\">\n                                {{ $t('commons.button.cancel') }}\n                            </el-button>\n                            <el-button\n                                link\n                                v-if=\"!hideDefaultButton && !row.edit && !row.isDefault && !row.isDelete\"\n                                type=\"primary\"\n                                @click=\"setDefault(row)\"\n                            >\n                                {{ $t('website.setDefault') }}\n                            </el-button>\n                        </div>\n                    </template>\n                </el-table-column>\n            </ComplexTable>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { createGroup, deleteGroup, getGroupList, updateGroup } from '@/api/modules/group';\nimport { MsgSuccess } from '@/utils/message';\nimport { Group } from '@/api/interface/group';\nimport { Rules } from '@/global/form-rules';\nimport { FormInstance } from 'element-plus';\n\nconst open = ref(false);\nconst hideDefaultButton = ref(false);\nconst type = ref();\nconst data = ref();\nconst handleClose = () => {\n    open.value = false;\n    data.value = [];\n    emit('search');\n};\ninterface DialogProps {\n    type: string;\n    hideDefaultButton: boolean;\n}\n\nconst groupForm = ref<FormInstance>();\nconst acceptParams = (params: DialogProps): void => {\n    hideDefaultButton.value = params.hideDefaultButton;\n    type.value = params.type;\n    open.value = true;\n    search();\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst search = () => {\n    getGroupList(type.value).then((res) => {\n        data.value = res.data || [];\n    });\n};\n\nconst saveGroup = async (formEl: FormInstance, group: Group.GroupInfo) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        group.type = type.value;\n        if (group.id == 0) {\n            createGroup(group).then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                search();\n            });\n        } else {\n            updateGroup(group).then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                search();\n            });\n        }\n    });\n};\n\nconst setDefault = (group: Group.GroupInfo) => {\n    group.isDefault = true;\n    group.type = type.value;\n    updateGroup(group).then(() => {\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        search();\n    });\n};\n\nconst openCreate = () => {\n    for (const d of data.value) {\n        if (d.name == '') {\n            return;\n        }\n        if (d.edit) {\n            d.edit = false;\n        }\n    }\n    const g = {\n        id: 0,\n        name: '',\n        isDefault: false,\n        edit: true,\n        status: 'Enable',\n    };\n    data.value.unshift(g);\n};\n\nconst removeGroup = (index: number) => {\n    const group = data.value[index];\n\n    if (group.id > 0) {\n        deleteGroup(group.id).then(() => {\n            data.value.splice(index, 1);\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n        });\n    } else {\n        data.value.splice(index, 1);\n    }\n};\n\nconst editGroup = (index: number) => {\n    for (const i in data.value) {\n        const d = data.value[i];\n        if (d.name == '') {\n            data.value.splice(Number(i), 1);\n        }\n        if (d.edit) {\n            d.edit = false;\n        }\n    }\n    data.value[index].edit = true;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/components/index.ts",
    "content": "import { type App } from 'vue';\nimport LayoutContent from './layout-content/index.vue';\nimport RouterButton from './router-button/index.vue';\nimport ComplexTable from './complex-table/index.vue';\nimport OpDialog from './del-dialog/index.vue';\nimport TableSearch from './table-search/index.vue';\nimport TableSetting from './table-setting/index.vue';\nimport TableRefresh from './table-refresh/index.vue';\nimport CopyButton from '@/components/copy-button/index.vue';\nimport MsgInfo from '@/components/msg-info/index.vue';\nimport DrawerPro from '@/components/drawer-pro/index.vue';\nimport DialogPro from '@/components/dialog-pro/index.vue';\nexport default {\n    install(app: App) {\n        app.component(LayoutContent.name, LayoutContent);\n        app.component(RouterButton.name, RouterButton);\n        app.component(ComplexTable.name, ComplexTable);\n        app.component(OpDialog.name, OpDialog);\n        app.component(CopyButton.name, CopyButton);\n        app.component(TableSearch.name, TableSearch);\n        app.component(TableSetting.name, TableSetting);\n        app.component(TableRefresh.name, TableRefresh);\n        app.component(MsgInfo.name, MsgInfo);\n        app.component(DrawerPro.name, DrawerPro);\n        app.component(DialogPro.name, DialogPro);\n    },\n};\n"
  },
  {
    "path": "frontend/src/components/input-tag/index.vue",
    "content": "<template>\n    <div>\n        <el-input-tag\n            v-model=\"tmpTags\"\n            trigger=\"Enter\"\n            @paste=\"handlePaste\"\n            @change=\"handleUpdate\"\n            @add-tag=\"handleAdd\"\n        >\n            <template #suffix>\n                <el-tooltip v-if=\"withFile\" :content=\"$t('website.select')\">\n                    <el-button\n                        class=\"-mr-3\"\n                        link\n                        icon=\"Folder\"\n                        @click=\"fileRef.acceptParams({ path: baseDir, isAll: true })\"\n                    />\n                </el-tooltip>\n                <el-tooltip :content=\"$t('commons.button.copy')\">\n                    <el-button class=\"-mr-3\" link icon=\"CopyDocument\" @click=\"copyText(tmpTags.join('\\n'))\" />\n                </el-tooltip>\n                <el-tooltip :content=\"$t('commons.button.clean')\">\n                    <el-button link icon=\"Close\" @click=\"handleClean\" />\n                </el-tooltip>\n            </template>\n        </el-input-tag>\n        <span v-if=\"props.egHelp\" class=\"input-help\">{{ props.egHelp }}</span>\n\n        <FileList ref=\"fileRef\" @choose=\"loadFile\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, onMounted } from 'vue';\nimport { copyText } from '@/utils/util';\nimport FileList from '@/components/file-list/index.vue';\nconst em = defineEmits(['update:tags']);\n\nconst tmpTags = ref([]);\nconst fileRef = ref();\n\nconst props = defineProps({\n    egHelp: { type: String, default: 'key=val' },\n    tags: { type: Array<string>, default: [] },\n\n    withFile: { type: Boolean, default: false },\n    baseDir: { type: String, default: '/' },\n});\nwatch(\n    () => props.tags,\n    (newVal) => {\n        tmpTags.value = newVal || [];\n    },\n);\n\nconst loadFile = async (path: string) => {\n    handleAdd(path);\n};\n\nconst handlePaste = (event: any) => {\n    event.preventDefault();\n    const pasteData = event.clipboardData.getData('text');\n    const tags = pasteData.split('\\n');\n    for (const item of tags) {\n        if (item) {\n            handleAdd(item);\n        }\n    }\n};\nconst handleAdd = (val: string) => {\n    tmpTags.value = tmpTags.value?.filter((item) => item !== val);\n    tmpTags.value.push(val);\n    handleUpdate();\n};\nconst handleUpdate = () => {\n    em('update:tags', tmpTags.value);\n};\nconst handleClean = () => {\n    tmpTags.value = [];\n    handleUpdate();\n};\nonMounted(() => {\n    tmpTags.value = props.tags || [];\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/layout-col/form.vue",
    "content": "<template>\n    <el-col :xs=\"24\" :sm=\"span\" :md=\"span\" :lg=\"span\" :xl=\"span\">\n        <slot />\n    </el-col>\n</template>\n\n<script>\nexport default {\n    props: {\n        span: {\n            type: Number,\n            default: 10,\n        },\n    },\n};\n</script>\n"
  },
  {
    "path": "frontend/src/components/layout-content/form-button.vue",
    "content": "<template>\n    <div class=\"form-button\">\n        <slot></slot>\n    </div>\n</template>\n"
  },
  {
    "path": "frontend/src/components/layout-content/index.vue",
    "content": "<template>\n    <div class=\"main-box\">\n        <div class=\"content-container__app\" v-if=\"slots.app\">\n            <slot name=\"app\"></slot>\n        </div>\n\n        <div class=\"content-container__search\" v-if=\"slots.search\">\n            <el-card>\n                <slot name=\"search\"></slot>\n            </el-card>\n        </div>\n\n        <div class=\"content-container_form\">\n            <slot name=\"form\">\n                <form-button>\n                    <slot name=\"button\"></slot>\n                </form-button>\n            </slot>\n        </div>\n        <div class=\"content-container__main\" v-if=\"slots.main\">\n            <el-card>\n                <div class=\"content-container__title\">\n                    <slot name=\"title\">\n                        <div v-if=\"showBack\" class=\"flex flex-wrap gap-4 sm:justify-between\">\n                            <back-button\n                                :path=\"backPath\"\n                                :name=\"backName\"\n                                :to=\"backTo\"\n                                :header=\"title\"\n                                :reload=\"reload\"\n                            >\n                                <template v-if=\"slots.leftToolBar\" #buttons>\n                                    <div class=\"flex flex-wrap gap-2 items-center justify-start\">\n                                        <slot name=\"leftToolBar\" v-if=\"slots.leftToolBar\"></slot>\n                                    </div>\n                                </template>\n                            </back-button>\n                            <div class=\"flex flex-wrap gap-3\">\n                                <slot name=\"rightToolBar\" v-if=\"slots.rightToolBar\"></slot>\n                            </div>\n                        </div>\n                        <div v-else class=\"flex flex-wrap gap-4 sm:justify-between\">\n                            <div class=\"flex gap-2 flex-wrap items-center justify-start\">\n                                <slot name=\"leftToolBar\" v-if=\"slots.leftToolBar\"></slot>\n                            </div>\n                            <div class=\"flex flex-wrap gap-3\" v-if=\"slots.rightToolBar\">\n                                <slot name=\"rightToolBar\"></slot>\n                            </div>\n                        </div>\n\n                        <span v-if=\"slots.toolbar\">\n                            <slot name=\"toolbar\"></slot>\n                        </span>\n\n                        <div v-if=\"prop.divider\">\n                            <div class=\"divider\"></div>\n                        </div>\n                    </slot>\n                </div>\n                <div v-if=\"slots.prompt\" class=\"prompt\">\n                    <slot name=\"prompt\"></slot>\n                </div>\n                <div class=\"main-content\">\n                    <slot name=\"main\"></slot>\n                </div>\n            </el-card>\n        </div>\n        <slot></slot>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, useSlots } from 'vue';\nimport FormButton from './form-button.vue';\ndefineOptions({ name: 'LayoutContent' });\nconst slots = useSlots();\nconst prop = defineProps({\n    title: String,\n    backPath: String,\n    backName: String,\n    backTo: Object,\n    reload: Boolean,\n    divider: Boolean,\n});\n\nconst showBack = computed(() => {\n    const { backPath, backName, backTo, reload } = prop;\n    return backPath || backName || backTo || reload;\n});\n</script>\n\n<style lang=\"scss\">\n@use '@/styles/mixins.scss' as *;\n\n.content-container__app {\n    margin-top: 7px;\n}\n\n.content-container__search {\n    margin-top: 7px;\n    .el-card {\n        --el-card-padding: 12px;\n    }\n}\n\n.content-container__title {\n    font-weight: 400;\n    font-size: 18px;\n    .el-button + .el-button {\n        margin: 0 !important;\n    }\n    .el-button-group > .el-button + .el-button {\n        margin-left: 0 !important;\n    }\n    .el-button-group > .el-button:not(:last-child) {\n        margin-right: -1px !important;\n    }\n}\n\n.content-container_form {\n    text-align: -webkit-center;\n    width: 60%;\n    margin-left: 15%;\n    .form-button {\n        float: right;\n    }\n}\n\n.content-container__main {\n    margin-top: 10px;\n}\n\n.prompt {\n    margin-top: 10px;\n}\n\n.divider {\n    margin-top: 20px;\n    border: 0;\n    border-top: var(--panel-border);\n}\n\n.main-box {\n    position: relative;\n}\n.main-content {\n    margin-top: 15px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/layout-content/no-such-service.vue",
    "content": "<template>\n    <div>\n        <LayoutContent :divider=\"true\">\n            <template #main>\n                <div class=\"app-warn\">\n                    <div class=\"flex flex-col gap-2 items-center justify-center w-full sm:flex-row\">\n                        <span>{{ $t('cronjob.library.noSuchApp', [prop.name]) }}</span>\n                        <span @click=\"toDoc\" class=\"flex items-center justify-center gap-0.5\">\n                            <el-icon><Position /></el-icon>\n                            {{ $t('firewall.quickJump') }}\n                        </span>\n                    </div>\n                    <div>\n                        <img src=\"@/assets/images/no_app.svg\" />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { routerToNameWithQuery } from '@/utils/router';\n\nconst prop = defineProps({\n    name: String,\n});\n\nconst toDoc = () => {\n    routerToNameWithQuery('Library', { uncached: 'true' });\n};\n</script>\n"
  },
  {
    "path": "frontend/src/components/license-import/index.vue",
    "content": "<template>\n    <div>\n        <DialogPro v-model=\"open\" class=\"level-up-pro\" @close=\"handleClose\">\n            <div style=\"text-align: center\" v-loading=\"loading\">\n                <span class=\"text-3xl font-medium title\">\n                    {{ isImport ? $t('license.importLicense') : $t('license.levelUpPro') }}\n                </span>\n                <el-row type=\"flex\" justify=\"center\" class=\"mt-6\">\n                    <el-col :span=\"22\">\n                        <el-upload\n                            action=\"#\"\n                            :auto-upload=\"false\"\n                            ref=\"uploadRef\"\n                            class=\"upload-demo\"\n                            drag\n                            :limit=\"1\"\n                            :on-change=\"fileOnChange\"\n                            :on-exceed=\"handleExceed\"\n                            v-model:file-list=\"uploaderFiles\"\n                        >\n                            <el-icon class=\"el-icon--upload\"><upload-filled /></el-icon>\n                            <div class=\"el-upload__text\">\n                                {{ $t('license.importHelper') }}\n                            </div>\n                        </el-upload>\n\n                        <span v-if=\"!isImport\" class=\"input-help\">{{ $t('xpack.node.syncWithMaster') }}</span>\n                        <DockerProxy\n                            v-if=\"!isImport\"\n                            class=\"w-full mt-2\"\n                            v-model:with-docker-restart=\"withDockerRestart\"\n                            syncList=\"SyncSystemProxy\"\n                        />\n                    </el-col>\n                </el-row>\n\n                <div v-if=\"oldLicense\">\n                    <el-checkbox v-model=\"isForce\">{{ $t('license.updateForce') }}</el-checkbox>\n                </div>\n                <el-button\n                    type=\"primary\"\n                    class=\"mt-3\"\n                    size=\"large\"\n                    :disabled=\"loading || uploaderFiles.length == 0\"\n                    plain\n                    @click=\"submit\"\n                >\n                    {{ isImport ? $t('commons.button.confirm') : $t('commons.button.power') }}\n                </el-button>\n                <div class=\"mt-3 mb-5\">\n                    <el-button text type=\"primary\" @click=\"toLxware\">{{ $t('license.knowMorePro') }}</el-button>\n                </div>\n            </div>\n        </DialogPro>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport i18n from '@/lang';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { uploadLicense } from '@/api/modules/setting';\nimport DockerProxy from '@/components/docker-proxy/index.vue';\nimport { GlobalStore } from '@/store';\nimport { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile, genFileId } from 'element-plus';\nimport { getXpackSettingForTheme, loadMasterProductProFromDB, loadProductProFromDB } from '@/utils/xpack';\nconst globalStore = GlobalStore();\n\nconst em = defineEmits(['search']);\n\nconst loading = ref(false);\nconst open = ref(false);\nconst uploadRef = ref<UploadInstance>();\nconst uploaderFiles = ref<UploadFiles>([]);\nconst isImport = ref();\nconst isForce = ref();\nconst withoutReload = ref();\n\nconst withDockerRestart = ref();\n\nconst oldLicense = ref();\ninterface DialogProps {\n    oldLicense: string;\n    isImport: boolean;\n    withoutReload: boolean;\n}\n\nconst acceptParams = (params: DialogProps) => {\n    oldLicense.value = params?.oldLicense || '';\n    uploaderFiles.value = [];\n    uploadRef.value?.clearFiles();\n    isImport.value = params?.isImport;\n    withoutReload.value = params?.withoutReload || false;\n\n    open.value = true;\n};\n\nconst handleClose = () => {\n    open.value = false;\n    uploadRef.value!.clearFiles();\n};\n\nconst fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    uploaderFiles.value = uploadFiles;\n};\n\nconst handleExceed: UploadProps['onExceed'] = (files) => {\n    uploadRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadRef.value!.handleStart(file);\n};\n\nconst toLxware = () => {\n    if (!globalStore.isIntl) {\n        window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer');\n    } else {\n        window.open('https://1panel.pro/pricing' + '', '_blank', 'noopener,noreferrer');\n    }\n};\n\nconst submit = async () => {\n    if (uploaderFiles.value.length !== 1) {\n        return;\n    }\n    const file = uploaderFiles.value[0];\n    const formData = new FormData();\n    formData.append('file', file.raw);\n    if (oldLicense.value) {\n        formData.append('oldLicenseName', oldLicense.value);\n    }\n    if (!isImport.value) {\n        formData.append('currentNode', globalStore.currentNode);\n        formData.append('withDockerRestart', withDockerRestart.value);\n    }\n    formData.append('isForce', isForce.value);\n    loading.value = true;\n    await uploadLicense(oldLicense.value, formData)\n        .then(async () => {\n            loading.value = false;\n            uploadRef.value!.clearFiles();\n            uploaderFiles.value = [];\n            open.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            if (!isImport.value) {\n                globalStore.isProductPro = true;\n                globalStore.isMasterProductPro = true;\n            }\n            if (!withoutReload.value) {\n                loadMasterProductProFromDB();\n                loadProductProFromDB();\n                getXpackSettingForTheme();\n                window.location.reload();\n            } else {\n                em('search');\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n            uploadRef.value!.clearFiles();\n            uploaderFiles.value = [];\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/log/compose/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"resource\"\n        @close=\"handleClose\"\n        :size=\"globalStore.isFullScreen ? 'full' : 'large'\"\n        :resource=\"container\"\n    >\n        <template #extra v-if=\"!mobile\">\n            <el-tooltip :content=\"loadTooltip()\" placement=\"top\">\n                <el-button @click=\"toggleFullscreen\" class=\"fullScreen\" icon=\"FullScreen\" plain></el-button>\n            </el-tooltip>\n        </template>\n        <template #content>\n            <ContainerLog\n                :compose=\"compose\"\n                :resource=\"resource\"\n                :container=\"container\"\n                :node=\"node\"\n                :highlightDiff=\"highlightDiff\"\n            />\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { computed, onBeforeUnmount, ref, watch } from 'vue';\nimport { GlobalStore } from '@/store';\nimport screenfull from 'screenfull';\nimport ContainerLog from '@/components/log/container/index.vue';\n\nconst open = ref(false);\nconst resource = ref('');\nconst container = ref('');\nconst globalStore = GlobalStore();\nconst logVisible = ref(false);\nconst compose = ref('');\nconst highlightDiff = ref(150);\nconst node = ref('');\n\ninterface DialogProps {\n    compose: string;\n    resource: string;\n    container: string;\n    node: string;\n}\n\nconst defaultProps = defineProps({\n    highlightDiff: {\n        type: Number,\n        default: 150,\n    },\n});\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst handleClose = () => {\n    open.value = false;\n    globalStore.isFullScreen = false;\n};\n\nfunction toggleFullscreen() {\n    globalStore.isFullScreen = !globalStore.isFullScreen;\n}\nconst loadTooltip = () => {\n    return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen'));\n};\n\nwatch(logVisible, (val) => {\n    if (screenfull.isEnabled && !val && !mobile.value) screenfull.exit();\n});\n\nconst acceptParams = (props: DialogProps): void => {\n    highlightDiff.value = defaultProps.highlightDiff;\n    compose.value = props.compose;\n    resource.value = props.resource;\n    container.value = props.container;\n    node.value = props.node;\n    open.value = true;\n};\n\nonBeforeUnmount(() => {\n    handleClose();\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.fullScreen {\n    border: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/log/container/index.vue",
    "content": "<template>\n    <div v-if=\"showControl\" class=\"log-toolbar\">\n        <el-select @change=\"searchLogs\" class=\"fetchClass\" v-model=\"logSearch.mode\">\n            <template #prefix>{{ $t('container.fetch') }}</template>\n            <el-option v-for=\"item in timeOptions\" :key=\"item.label\" :value=\"item.value\" :label=\"item.label\" />\n        </el-select>\n        <el-select @change=\"searchLogs\" class=\"tailClass\" v-model.number=\"logSearch.tail\">\n            <template #prefix>{{ $t('container.lines') }}</template>\n            <el-option :value=\"0\" :label=\"$t('commons.table.all')\" />\n            <el-option :value=\"100\" :label=\"100\" />\n            <el-option :value=\"200\" :label=\"200\" />\n            <el-option :value=\"500\" :label=\"500\" />\n            <el-option :value=\"1000\" :label=\"1000\" />\n        </el-select>\n        <div class=\"margin-button float-left\">\n            <el-checkbox border @change=\"searchLogs\" v-model=\"logSearch.isWatch\">\n                {{ $t('commons.button.watch') }}\n            </el-checkbox>\n        </div>\n        <div class=\"margin-button float-left\">\n            <el-checkbox border @change=\"searchLogs\" v-model=\"logSearch.isShowTimestamp\">\n                {{ $t('commons.table.date') }}\n            </el-checkbox>\n        </div>\n        <el-button class=\"margin-button\" @click=\"openDownloadDialog\" icon=\"Download\">\n            {{ $t('commons.button.download') }}\n        </el-button>\n        <el-button class=\"margin-button\" @click=\"onClean\" icon=\"Delete\">\n            {{ $t('commons.button.clean') }}\n        </el-button>\n    </div>\n    <div class=\"log-container\" :style=\"styleVars\">\n        <div class=\"xterm-log-viewer\" ref=\"terminalElement\"></div>\n    </div>\n    <el-dialog v-model=\"downloadDialogVisible\" :title=\"$t('commons.button.download')\" width=\"420px\">\n        <el-form label-position=\"top\">\n            <el-form-item :label=\"$t('container.fetch')\">\n                <el-select v-model=\"downloadForm.mode\" class=\"w-full\">\n                    <el-option v-for=\"item in timeOptions\" :key=\"item.label\" :value=\"item.value\" :label=\"item.label\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.lines')\">\n                <el-select\n                    v-model=\"downloadForm.tail\"\n                    class=\"w-full\"\n                    filterable\n                    allow-create\n                    default-first-option\n                    :reserve-keyword=\"false\"\n                >\n                    <el-option :value=\"0\" :label=\"$t('commons.table.all')\" />\n                    <el-option :value=\"100\" :label=\"100\" />\n                    <el-option :value=\"200\" :label=\"200\" />\n                    <el-option :value=\"500\" :label=\"500\" />\n                    <el-option :value=\"1000\" :label=\"1000\" />\n                </el-select>\n                <div class=\"download-tail-helper\">{{ $t('container.downloadLinesHelper') }}</div>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"downloadDialogVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"onDownload\">{{ $t('commons.button.confirm') }}</el-button>\n        </template>\n    </el-dialog>\n</template>\n\n<script lang=\"ts\" setup>\nimport { cleanComposeLog, cleanContainerLog, DownloadFile } from '@/api/modules/container';\nimport { FitAddon } from '@xterm/addon-fit';\nimport { Terminal } from '@xterm/xterm';\nimport '@xterm/xterm/css/xterm.css';\nimport i18n from '@/lang';\nimport { dateFormatForName } from '@/utils/util';\nimport { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';\nimport { ElMessageBox } from 'element-plus';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst em = defineEmits(['update:loading']);\n\nconst props = defineProps({\n    container: {\n        type: String,\n        default: '',\n    },\n    compose: {\n        type: String,\n        default: '',\n    },\n    resource: {\n        type: String,\n        default: '',\n    },\n    highlightDiff: {\n        type: Number,\n        default: 320,\n    },\n    node: {\n        type: String,\n        default: '',\n    },\n    showControl: {\n        type: Boolean,\n        default: true,\n    },\n    defaultFollow: {\n        type: Boolean,\n        default: false,\n    },\n    defaultIsShowTimestamp: {\n        type: Boolean,\n        default: false,\n    },\n});\n\nconst styleVars = computed(() => ({\n    '--custom-height': `${props.highlightDiff || 320}px`,\n}));\n\nconst terminalElement = ref<HTMLDivElement | null>(null);\nlet eventSource: EventSource | null = null;\nlet term: Terminal | null = null;\nconst fitAddon = new FitAddon();\nlet onScrollDisposable: { dispose: () => void } | null = null;\nconst MAX_VIEW_LINES = 20000;\nconst followBottom = ref(true);\n\nconst logSearch = reactive({\n    isWatch: props.defaultFollow ? true : true,\n    isShowTimestamp: props.defaultIsShowTimestamp,\n    container: '',\n    mode: 'all',\n    tail: props.defaultFollow ? 0 : 100,\n    compose: '',\n    resource: '',\n});\nconst downloadDialogVisible = ref(false);\nconst downloadForm = reactive<{ mode: string; tail: number | string }>({\n    mode: 'all',\n    tail: 0,\n});\n\nconst timeOptions = ref([\n    { label: i18n.global.t('commons.table.all'), value: 'all' },\n    {\n        label: i18n.global.t('container.lastDay'),\n        value: '24h',\n    },\n    {\n        label: i18n.global.t('container.last4Hour'),\n        value: '4h',\n    },\n    {\n        label: i18n.global.t('container.lastHour'),\n        value: '1h',\n    },\n    {\n        label: i18n.global.t('container.last10Min'),\n        value: '10m',\n    },\n]);\n\nconst stopListening = () => {\n    if (eventSource) {\n        eventSource.close();\n        eventSource = null;\n    }\n};\n\nconst clearTerminal = () => {\n    term?.reset();\n    followBottom.value = true;\n};\n\nconst writeLogLine = (data: string) => {\n    if (!term) return;\n    term.writeln(data);\n    if (followBottom.value) {\n        term.scrollToBottom();\n    }\n};\n\nconst bindXTermEvents = () => {\n    if (!term) return;\n    onScrollDisposable?.dispose();\n    onScrollDisposable = term.onScroll(() => {\n        if (!term) return;\n        const active = term.buffer.active;\n        followBottom.value = active.baseY + active.cursorY >= active.length - 2;\n    });\n};\n\nconst initTerminal = () => {\n    if (!terminalElement.value || term) return;\n    term = new Terminal({\n        cursorBlink: false,\n        cursorStyle: 'block',\n        disableStdin: true,\n        convertEol: true,\n        scrollback: MAX_VIEW_LINES,\n        fontSize: 14,\n        fontFamily: \"'JetBrains Mono', Monaco, Menlo, Consolas, 'Courier New', monospace\",\n        fontWeight: '500',\n        lineHeight: 1.2,\n        theme: {\n            background: '#1e1e1e',\n            foreground: '#666666',\n            selectionBackground: 'rgba(102, 178, 255, 0.30)',\n            selectionInactiveBackground: 'rgba(102, 178, 255, 0.20)',\n        },\n    });\n    term.open(terminalElement.value);\n    term.loadAddon(fitAddon);\n    fitAddon.fit();\n    bindXTermEvents();\n};\n\nconst handleClose = async () => {\n    stopListening();\n};\n\nconst searchLogs = async () => {\n    if (Number(logSearch.tail) < 0) {\n        MsgError(i18n.global.t('container.linesHelper'));\n        return;\n    }\n    stopListening();\n    clearTerminal();\n\n    let currentNode = globalStore.currentNode;\n    if (props.node && props.node !== '') {\n        currentNode = props.node;\n    }\n\n    let url = `/api/v2/containers/search/log?container=${logSearch.container}&since=${logSearch.mode}&tail=${logSearch.tail}&follow=${logSearch.isWatch}&timestamp=${logSearch.isShowTimestamp}&operateNode=${currentNode}`;\n    if (logSearch.compose !== '') {\n        url = `/api/v2/containers/search/log?compose=${logSearch.compose}&since=${logSearch.mode}&tail=${logSearch.tail}&follow=${logSearch.isWatch}&timestamp=${logSearch.isShowTimestamp}&operateNode=${currentNode}`;\n    }\n\n    eventSource = new EventSource(url);\n    eventSource.onmessage = (event: MessageEvent) => {\n        writeLogLine(event.data);\n    };\n    eventSource.onerror = (event: MessageEvent) => {\n        stopListening();\n        if (event.data && event.data != '') {\n            MsgError(event.data);\n        }\n    };\n};\n\nconst openDownloadDialog = () => {\n    downloadForm.mode = logSearch.mode;\n    downloadForm.tail = logSearch.tail;\n    downloadDialogVisible.value = true;\n};\n\nconst onDownload = async () => {\n    const customTail = Number(downloadForm.tail);\n    if (Number.isNaN(customTail) || customTail < 0) {\n        MsgError(i18n.global.t('container.linesHelper'));\n        return;\n    }\n    const container = logSearch.compose === '' ? logSearch.container : logSearch.compose;\n    let resource = container;\n    if (props.resource) {\n        resource = props.resource;\n    }\n    const containerType = logSearch.compose === '' ? 'container' : 'compose';\n    const params = {\n        container: container,\n        since: downloadForm.mode,\n        tail: customTail,\n        timestamp: logSearch.isShowTimestamp,\n        containerType: containerType,\n    };\n    const addItem = {};\n    addItem['name'] = resource + '-' + dateFormatForName(new Date()) + '.log';\n    DownloadFile(params).then((res) => {\n        const downloadUrl = window.URL.createObjectURL(new Blob([res]));\n        const a = document.createElement('a');\n        a.style.display = 'none';\n        a.href = downloadUrl;\n        a.download = addItem['name'];\n        const event = new MouseEvent('click');\n        a.dispatchEvent(event);\n    });\n    downloadDialogVisible.value = false;\n};\n\nconst onClean = async () => {\n    ElMessageBox.confirm(i18n.global.t('container.cleanLogHelper'), i18n.global.t('container.cleanLog'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        let currentNode = globalStore.currentNode;\n        if (props.node && props.node !== '') {\n            currentNode = props.node;\n        }\n        if (logSearch.compose !== '') {\n            em('update:loading', true);\n            await cleanComposeLog(logSearch.resource, logSearch.compose, currentNode)\n                .then(() => {\n                    em('update:loading', false);\n                    searchLogs();\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .finally(() => {\n                    em('update:loading', false);\n                });\n            return;\n        }\n        await cleanContainerLog(logSearch.container, currentNode);\n        searchLogs();\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\n\nconst resizeObserver = ref<ResizeObserver | null>(null);\n\nonMounted(() => {\n    logSearch.container = props.container;\n    logSearch.compose = props.compose;\n    logSearch.resource = props.resource;\n\n    logSearch.tail = 100;\n    logSearch.mode = 'all';\n    logSearch.isWatch = true;\n\n    nextTick(() => {\n        initTerminal();\n        if (terminalElement.value) {\n            resizeObserver.value = new ResizeObserver(() => {\n                fitAddon.fit();\n            });\n            resizeObserver.value.observe(terminalElement.value);\n        }\n        searchLogs();\n    });\n});\n\nonUnmounted(() => {\n    handleClose();\n    onScrollDisposable?.dispose();\n    if (term) {\n        term.dispose();\n        term = null;\n    }\n    resizeObserver.value?.disconnect();\n});\n</script>\n\n<style scoped lang=\"scss\">\n.margin-button {\n    margin-left: 0;\n}\n.fullScreen {\n    border: none;\n}\n.tailClass {\n    width: 160px;\n}\n.fetchClass {\n    width: 220px;\n}\n\n.log-toolbar {\n    display: flex;\n    align-items: center;\n    flex-wrap: wrap;\n    gap: 10px;\n}\n\n.log-toolbar :deep(.el-button),\n.log-toolbar :deep(.el-checkbox) {\n    white-space: nowrap;\n    flex-shrink: 0;\n}\n\n.download-tail-helper {\n    margin-top: 6px;\n    font-size: 12px;\n    color: var(--el-text-color-secondary);\n}\n\n.log-container {\n    height: calc(100vh - var(--custom-height, 320px));\n    overflow: hidden;\n    position: relative;\n    background-color: #1e1e1e;\n    margin-top: 10px;\n}\n\n.xterm-log-viewer {\n    width: 100%;\n    height: 100%;\n}\n\n:deep(.xterm) {\n    padding: 2px !important;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/log/container-drawer/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"logVisible\"\n        :header=\"$t('commons.button.log')\"\n        @close=\"handleClose\"\n        :resource=\"logSearch.container\"\n        :size=\"globalStore.isFullScreen ? 'full' : 'large'\"\n    >\n        <template #extra v-if=\"!mobile\">\n            <el-tooltip :content=\"loadTooltip()\" placement=\"top\">\n                <el-button @click=\"toggleFullscreen\" class=\"fullScreen\" icon=\"FullScreen\" plain></el-button>\n            </el-tooltip>\n        </template>\n        <template #content>\n            <ContainerLog :container=\"config.container\" :highlightDiff=\"highlightDiff\" />\n        </template>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { computed, onBeforeUnmount, reactive, ref, watch } from 'vue';\nimport screenfull from 'screenfull';\nimport { GlobalStore } from '@/store';\nimport ContainerLog from '@/components/log/container/index.vue';\n\nconst logVisible = ref(false);\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst globalStore = GlobalStore();\nconst logSearch = reactive({\n    isWatch: true,\n    container: '',\n    containerID: '',\n    mode: 'all',\n    tail: 100,\n});\n\ndefineProps({\n    highlightDiff: {\n        type: Number,\n        default: 320,\n    },\n});\n\nfunction toggleFullscreen() {\n    globalStore.isFullScreen = !globalStore.isFullScreen;\n}\n\nconst loadTooltip = () => {\n    return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen'));\n};\n\nconst handleClose = async () => {\n    logVisible.value = false;\n    globalStore.isFullScreen = false;\n};\n\nwatch(logVisible, (val) => {\n    if (screenfull.isEnabled && !val && !mobile.value) screenfull.exit();\n});\n\ninterface DialogProps {\n    container: string;\n    containerID: string;\n}\n\nconst config = ref<DialogProps>({\n    container: '',\n    containerID: '',\n});\n\nconst acceptParams = (props: DialogProps): void => {\n    config.value.containerID = props.containerID;\n    config.value.container = props.container;\n    logSearch.container = props.container;\n    logVisible.value = true;\n\n    if (!mobile.value) {\n        screenfull.on('change', () => {\n            globalStore.isFullScreen = screenfull.isFullscreen;\n        });\n    }\n};\n\nonBeforeUnmount(() => {\n    handleClose();\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.fullScreen {\n    border: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/log/custom-hightlight/index.vue",
    "content": "<template>\n    <span v-for=\"(token, index) in tokens\" :key=\"index\" :class=\"['token', token.type]\" :style=\"{ color: token.color }\">\n        <span class=\"whitespace-pre\">{{ token.text }}</span>\n    </span>\n</template>\n<script setup lang=\"ts\">\nimport { ansiToJson } from 'anser';\ninterface TokenRule {\n    type: string;\n    pattern: RegExp;\n    color: string;\n}\n\ninterface Token {\n    text: string;\n    type: string;\n    color: string;\n    html?: string;\n}\n\nconst props = defineProps<{\n    log: string;\n    type: string;\n    container?: string;\n}>();\n\nconst rules = ref<TokenRule[]>([]);\nconst nginxRules: TokenRule[] = [\n    {\n        type: 'log-level-warn',\n        pattern: /\\b(warn|warning|NOTICE)\\b/g,\n        color: '#F39C12',\n    },\n    {\n        type: 'log-level-info',\n        pattern: /\\b(info|debug)\\b/g,\n        color: '#3498DB',\n    },\n    {\n        type: 'log-level-error',\n        pattern: /\\[(crit|error)\\]/g,\n        color: '#E74C3C',\n    },\n    {\n        type: 'path',\n        pattern: /(?<=[\\s\"])\\/[^\\s\"]*/g,\n        color: '#B87A2B',\n    },\n    {\n        type: 'http-method',\n        pattern: /\\b(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\\b/g,\n        color: '#27AE60',\n    },\n    {\n        type: 'status-success',\n        pattern: /(?<=\")\\s(2\\d{2})\\b/g,\n        color: '#2ECC71',\n    },\n    {\n        type: 'status-error',\n        pattern: /(?<=\")\\s([45]\\d{2})\\b/g,\n        color: '#E74C3C',\n    },\n    {\n        type: 'status-redirect',\n        pattern: /(?<=\")\\s(3\\d{2})\\b/g,\n        color: '#F39C12',\n    },\n    {\n        type: 'status-info',\n        pattern: /(?<=\")\\s(1\\d{2})\\b/g,\n        color: '#268785',\n    },\n    {\n        type: 'process-info',\n        pattern: /\\d+#\\d+/g,\n        color: '#7F8C8D',\n    },\n];\n\nconst systemRules: TokenRule[] = [\n    {\n        type: 'log-error',\n        pattern: /\\[(ERROR|WARN|FATAL)\\]/g,\n        color: '#E74C3C',\n    },\n    {\n        type: 'log-normal',\n        pattern: /\\[(INFO|DEBUG)\\]/g,\n        color: '#8B8B8B',\n    },\n    {\n        type: 'timestamp',\n        pattern: /\\[\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}\\]/g,\n        color: '#8B8B8B',\n    },\n    {\n        type: 'bracket-text',\n        pattern: /\\[([^\\]]+)\\]/g,\n        color: '#B87A2B',\n    },\n    {\n        type: 'referrer-ua',\n        pattern: /https?:\\/\\/(?:[\\w-]+\\.)+[\\w-]+(?::\\d+)?(?:\\/[^\\s\\]\\)\"]*)?/g,\n        color: '#786C88',\n    },\n];\n\nconst taskRules: TokenRule[] = [\n    {\n        type: 'bracket-text',\n        pattern: /\\[(?:[^\\[\\]]|\\[[^\\[\\]]*\\]){0,100}\\]/g,\n        color: '#B87A2B',\n    },\n];\n\nconst defaultRules: TokenRule[] = [\n    {\n        type: 'timestamp',\n        pattern:\n            /(?:\\[\\d{2}\\/\\w{3}\\/\\d{4}:\\d{2}:\\d{2}:\\d{2}\\s[+-]\\d{4}\\]|\\d{4}[-\\/]\\d{2}[-\\/]\\d{2}\\s\\d{2}:\\d{2}:\\d{2})/g,\n        color: '#8B8B8B',\n    },\n    {\n        type: 'referrer-ua',\n        pattern: /\"(?:https?:\\/\\/[^\"]+|Mozilla[^\"]+|curl[^\"]+)\"/g,\n        color: '#786C88',\n    },\n    {\n        type: 'ip',\n        pattern: /(?<=^|\\s|\")\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(?=\\s|\"|$)/g,\n        color: '#4A90E2',\n    },\n    {\n        type: 'ipv6',\n        pattern:\n            /(?<!\\d)(?:(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}|(?:[A-Fa-f0-9]{1,4}:){1,7}:|(?:[A-Fa-f0-9]{1,4}:){1,6}:[A-Fa-f0-9]{1,4}|(?:[A-Fa-f0-9]{1,4}:){1,5}(?::[A-Fa-f0-9]{1,4}){1,2}|(?:[A-Fa-f0-9]{1,4}:){1,4}(?::[A-Fa-f0-9]{1,4}){1,3}|(?:[A-Fa-f0-9]{1,4}:){1,3}(?::[A-Fa-f0-9]{1,4}){1,4}|(?:[A-Fa-f0-9]{1,4}:){1,2}(?::[A-Fa-f0-9]{1,4}){1,5}|[A-Fa-f0-9]{1,4}:(?:(?::[A-Fa-f0-9]{1,4}){1,6})|:(?:(?::[A-Fa-f0-9]{1,4}){1,7}|:)|fe80:(?::[A-Fa-f0-9]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[A-Fa-f0-9]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?![\\da-fA-F])(?!\\s*:\\s*(?:tid|\\d{2}))/g,\n        color: '#4A90E2',\n    },\n    {\n        type: 'server-host',\n        pattern: /(?:server|host):\\s*[^,\\s]+/g,\n        color: '#5D6D7E',\n    },\n];\n\nconst commonContainerRules: TokenRule[] = [\n    {\n        type: 'timestamp',\n        pattern: /\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}\\+\\d{2}:\\d{2}/g,\n        color: '#8B8B8B',\n    },\n    {\n        type: 'path',\n        pattern: /(?<=[\\s\"]|^)\\/[^\\s\"]+/g,\n        color: '#9B59B6',\n    },\n    {\n        type: 'ip',\n        pattern: /\\b(?<!\\[)\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b(?!\\])/g,\n        color: '#4A90E2',\n    },\n    {\n        type: 'ipv6',\n        pattern: /\\b(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}\\b/g,\n        color: '#4A90E2',\n    },\n    {\n        type: 'log-level-warn',\n        pattern: /\\b(WARN|WARNING|NOTICE)\\b/g,\n        color: '#F39C12',\n    },\n    {\n        type: 'log-level-info',\n        pattern: /\\b(INFO|DEBUG)\\b/g,\n        color: '#3498DB',\n    },\n    {\n        type: 'log-level-error',\n        pattern: /\\[(CRIT|ERROR)\\]/g,\n        color: '#E74C3C',\n    },\n    {\n        type: 'url',\n        pattern: /https?:\\/\\/(?:[\\w-]+\\.)+[\\w-]+(?::\\d+)?(?:\\/[^\\s\\]\\)\"]*)?/g,\n        color: '#786C88',\n    },\n];\n\nconst mysqlContainerRules: TokenRule[] = [\n    {\n        type: 'mysql-timestamp',\n        pattern: /\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{6}Z/g,\n        color: '#8B8B8D',\n    },\n    {\n        type: 'mysql-thread-id',\n        pattern: /(?<=T\\d{2}:\\d{2}:\\d{2}\\.\\d{6}Z\\s)\\d+(?=\\s)/g,\n        color: '#7F8C8D',\n    },\n    {\n        type: 'mysql-log-error',\n        pattern: /\\[(Error)\\]/g,\n        color: '#E74C3C',\n    },\n    {\n        type: 'mysql-log-warn',\n        pattern: /\\[(Warning)\\]/g,\n        color: '#F39C12',\n    },\n    {\n        type: 'mysql-log-info',\n        pattern: /\\[(System|Note|Entrypoint)\\]/g,\n        color: '#3498DB',\n    },\n    {\n        type: 'mysql-error-code',\n        pattern: /\\[MY-\\d{6}\\]/g,\n        color: '#9B59B6',\n    },\n    {\n        type: 'mysql-component',\n        pattern: /\\[(Server|Repl|InnoDB|Audit|Query)\\]/g,\n        color: '#3498DB',\n    },\n];\n\nconst redisContainerRules: TokenRule[] = [\n    {\n        type: 'redis-role',\n        pattern: /\\b(Master|Slave|Replica)\\b/g,\n        color: '#E67E22',\n    },\n    {\n        type: 'redis-signal',\n        pattern: /\\b(SIGTERM|SIGINT|SIGHUP)\\b/g,\n        color: '#9B59B6',\n    },\n    {\n        type: 'redis-memory',\n        pattern: /\\d+\\s*[KMG]B?\\b/g,\n        color: '#3498DB',\n    },\n];\n\nconst postgresContainerRules: TokenRule[] = [\n    {\n        type: 'postgres-severity',\n        pattern: /\\b(LOG|INFO|NOTICE|WARNING|ERROR|FATAL|PANIC)\\b/g,\n        color: '#E74C3C',\n    },\n    {\n        type: 'postgres-process',\n        pattern: /\\[\\d+\\]/g,\n        color: '#7F8C8D',\n    },\n    {\n        type: 'postgres-statement',\n        pattern: /\\b(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)\\b/gi,\n        color: '#27AE60',\n    },\n];\n\nconst phpContainerRules: TokenRule[] = [];\n\nconst getContainerRules = (containerName?: string): TokenRule[] => {\n    if (!containerName) {\n        return commonContainerRules;\n    }\n\n    const name = containerName.toLowerCase();\n    let specificRules: TokenRule[] = [];\n\n    if (name.includes('openresty') || name.includes('nginx')) {\n        specificRules = nginxRules;\n    } else if (name.includes('mysql') || name.includes('mariadb')) {\n        specificRules = mysqlContainerRules;\n    } else if (name.includes('redis')) {\n        specificRules = redisContainerRules;\n    } else if (name.includes('postgres') || name.includes('postgresql')) {\n        specificRules = postgresContainerRules;\n    } else if (name.includes('php')) {\n        specificRules = phpContainerRules;\n    }\n\n    return [...specificRules, ...commonContainerRules];\n};\n\nfunction tokenizeLog(log: string): Token[] {\n    if (\n        log.indexOf('<div') !== -1 ||\n        log.indexOf('<span') !== -1 ||\n        log.indexOf('<p>') !== -1 ||\n        log.indexOf('<img') !== -1 ||\n        log.indexOf('</div>') !== -1 ||\n        log.indexOf('</span>') !== -1 ||\n        log.indexOf('<script>') !== -1\n    ) {\n        return [\n            {\n                text: log,\n                type: 'plain',\n                color: '#666666',\n            },\n        ];\n    }\n\n    if (log.indexOf('\\x1b[') !== -1) {\n        try {\n            const parsed = ansiToJson(log, { json: true, remove_empty: true });\n            return parsed.map((item: any) => ({\n                text: item.content,\n                type: 'ansi',\n                color: item.fg ? `rgb(${item.fg})` : '#666666',\n            }));\n        } catch (error) {\n            return [\n                {\n                    text: log,\n                    type: 'plain',\n                    color: '#666666',\n                },\n            ];\n        }\n    }\n\n    const textLength = log.length;\n    if (textLength > 5000) {\n        return [\n            {\n                text: log,\n                type: 'plain',\n                color: '#666666',\n            },\n        ];\n    }\n\n    const tokens: Token[] = [];\n    let lastIndex = 0;\n    const matches: { index: number; text: string; type: string; color: string }[] = [];\n\n    for (const rule of rules.value) {\n        const regex = new RegExp(rule.pattern.source, rule.pattern.flags);\n        let match;\n        while ((match = regex.exec(log)) !== null) {\n            matches.push({\n                index: match.index,\n                text: match[0],\n                type: rule.type,\n                color: rule.color,\n            });\n        }\n    }\n\n    matches.sort((a, b) => a.index - b.index);\n\n    const filteredMatches = matches.filter((match, index) => {\n        if (index === 0) return true;\n        const prev = matches[index - 1];\n        return match.index >= prev.index + prev.text.length;\n    });\n\n    for (const match of filteredMatches) {\n        if (match.index > lastIndex) {\n            tokens.push({\n                text: log.substring(lastIndex, match.index),\n                type: 'plain',\n                color: '#666666',\n            });\n        }\n        tokens.push({\n            text: match.text,\n            type: match.type,\n            color: match.color,\n        });\n        lastIndex = match.index + match.text.length;\n    }\n\n    if (lastIndex < log.length) {\n        const rest = log.substring(lastIndex).replace(/\\n?$/, '\\n');\n        tokens.push({\n            text: rest,\n            type: 'plain',\n            color: '#666666',\n        });\n    }\n\n    return tokens;\n}\n\nconst tokens = computed(() => tokenizeLog(props.log));\n\nconst initRules = () => {\n    switch (props.type) {\n        case 'nginx':\n            return [...nginxRules, ...defaultRules];\n        case 'system':\n            return [...systemRules, ...defaultRules];\n        case 'container':\n            return [...getContainerRules(props.container), ...defaultRules];\n        case 'task':\n            return [...taskRules, ...defaultRules];\n        default:\n            return defaultRules;\n    }\n};\n\nwatchEffect(() => {\n    rules.value = initRules();\n});\n</script>\n\n<style scoped>\n.token {\n    font-family: 'JetBrains Mono', Monaco, Menlo, Consolas, 'Courier New', monospace;\n    font-size: 14px;\n    font-weight: 500;\n}\n\n.ip {\n    text-decoration: underline;\n    text-decoration-style: dotted;\n    text-decoration-thickness: 1px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/log/file/index.vue",
    "content": "<template>\n    <div v-loading=\"firstLoading\">\n        <div v-if=\"defaultButton\">\n            <el-button icon=\"Refresh\" v-if=\"!showTail\" @click=\"getContent(false)\">\n                {{ $t('commons.button.refresh') }}\n            </el-button>\n            <el-checkbox\n                border\n                :disabled=\"isTailDisabled\"\n                v-model=\"tailLog\"\n                class=\"float-left\"\n                @change=\"changeTail(false)\"\n                v-if=\"showTail\"\n            >\n                {{ $t('commons.button.watch') }}\n            </el-checkbox>\n            <el-button\n                class=\"ml-2.5\"\n                v-if=\"showDownload\"\n                @click=\"onDownload\"\n                icon=\"Download\"\n                :disabled=\"logs.length === 0\"\n            >\n                {{ $t('commons.button.download') }}\n            </el-button>\n            <span v-if=\"$slots.button\" class=\"ml-2.5\">\n                <slot name=\"button\"></slot>\n            </span>\n        </div>\n        <div class=\"log-container\" ref=\"logContainer\" @scroll=\"onScroll\" :style=\"containerStyle\">\n            <div class=\"log-spacer\" :style=\"{ height: `${totalHeight}px` }\"></div>\n            <div class=\"log-viewport\" :style=\"{ transform: `translateY(${offsetY}px)` }\">\n                <div\n                    v-for=\"(log, index) in visibleLogs\"\n                    :key=\"`${startIndex + index}-${log}`\"\n                    class=\"log-item\"\n                    :style=\"{ height: `${logHeight}px` }\"\n                >\n                    <hightlight :log=\"log\" :type=\"config.colorMode ?? 'nginx'\"></hightlight>\n                </div>\n            </div>\n            <hightlight v-if=\"logs.length === 0\" :log=\"$t('commons.log.noLog')\" type=\"system\"></hightlight>\n        </div>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { nextTick, onMounted, onUnmounted, reactive, ref, computed } from 'vue';\nimport { downloadFile } from '@/utils/util';\nimport { readByLine } from '@/api/modules/files';\nimport { GlobalStore } from '@/store';\nimport bus from '@/global/bus';\nimport hightlight from '@/components/log/custom-hightlight/index.vue';\nconst globalStore = GlobalStore();\n\ninterface LogProps {\n    id?: number;\n    type?: string;\n    name?: string;\n    tail?: boolean;\n    taskID?: string;\n    colorMode?: string;\n    taskType?: string;\n    taskOperate?: string;\n    resourceID?: number;\n\n    operateNode?: string;\n}\n\nconst props = defineProps({\n    config: {\n        type: Object as () => LogProps | null,\n        default: () => ({\n            id: 0,\n            type: '',\n            name: '',\n            tail: false,\n            colorMode: 'nginx',\n            taskType: '',\n            taskOperate: '',\n            resourceID: 0,\n            taskID: '',\n\n            operateNode: '',\n        }),\n    },\n    defaultButton: {\n        type: Boolean,\n        default: true,\n    },\n    loading: {\n        type: Boolean,\n        default: true,\n    },\n    hasContent: {\n        type: Boolean,\n        default: false,\n    },\n    heightDiff: {\n        type: Number,\n        default: 420,\n    },\n    showTail: {\n        type: Boolean,\n        default: true,\n    },\n    showDownload: {\n        type: Boolean,\n        default: true,\n    },\n});\nconst stopSignals = [\n    'docker-compose up failed!',\n    'docker-compose up successful!',\n    'image build failed!',\n    'image build successful!',\n    'image pull failed!',\n    'image pull successful!',\n    'image push failed!',\n    'image push successful!',\n    '[TASK-END]',\n];\nconst emit = defineEmits(['update:loading', 'update:hasContent', 'update:isReading', 'stop-reading']);\nconst tailLog = ref(true);\nconst loading = ref(props.loading);\nconst readReq = reactive({\n    id: 0,\n    type: '',\n    name: '',\n    page: 1,\n    pageSize: 500,\n    latest: false,\n    taskID: '',\n    taskType: '',\n    taskOperate: '',\n    resourceID: 0,\n});\nconst isLoading = ref(false);\nconst end = ref(false);\nconst lastLogs = ref([]);\nconst maxPage = ref(0);\nconst minPage = ref(0);\nlet timer: ReturnType<typeof setInterval> | null = null;\nconst logPath = ref('');\nconst showTail = ref(false);\nconst isTailDisabled = ref();\nconst firstLoading = ref(false);\nconst logs = ref<string[]>([]);\nconst logContainer = ref<HTMLElement | null>(null);\nconst logHeight = 23;\nconst containerHeight = ref(500);\nconst scrollTop = ref(0);\nconst lastScrollTop = ref(0);\nconst totalLines = ref(0);\nconst totalPages = ref(0);\nlet resizeObserver: ResizeObserver | null = null;\nconst isEndOfFile = ref(false);\n\nconst totalHeight = computed(() => logs.value.length * logHeight);\n\nconst visibleCount = computed(() => {\n    const buffer = 5;\n    return Math.ceil(containerHeight.value / logHeight) + buffer * 2;\n});\n\nconst startIndex = computed(() => {\n    const buffer = 5;\n    const index = Math.floor(scrollTop.value / logHeight) - buffer;\n    return Math.max(0, index);\n});\n\nconst endIndex = computed(() => {\n    return Math.min(logs.value.length, startIndex.value + visibleCount.value);\n});\n\nconst visibleLogs = computed(() => {\n    return logs.value.slice(startIndex.value, endIndex.value);\n});\n\nconst offsetY = computed(() => {\n    return startIndex.value * logHeight;\n});\n\nconst updateContainerHeight = () => {\n    if (logContainer.value) {\n        const rect = logContainer.value.getBoundingClientRect();\n        containerHeight.value = rect.height;\n    }\n};\n\nconst onScroll = async () => {\n    if (!logContainer.value) return;\n\n    scrollTop.value = logContainer.value.scrollTop;\n    const scrollHeight = logContainer.value.scrollHeight;\n    const clientHeight = logContainer.value.clientHeight;\n\n    lastScrollTop.value = scrollTop.value;\n\n    if (isLoading.value) return;\n\n    if (scrollTop.value <= 50 && readReq.page > 1) {\n        if (minPage.value <= 1) {\n            return;\n        }\n        readReq.page = minPage.value > 1 ? minPage.value - 1 : 1;\n        minPage.value = readReq.page;\n        await getContent(true);\n        return;\n    }\n    if (scrollHeight - scrollTop.value - clientHeight <= 50 && !end.value && !isEndOfFile.value) {\n        if (readReq.page < maxPage.value) {\n            readReq.page = maxPage.value;\n            await getContent(false);\n        } else if (readReq.page < totalPages.value) {\n            maxPage.value++;\n            readReq.page = maxPage.value;\n            await getContent(false);\n        }\n    }\n};\n\nconst changeLoading = () => {\n    loading.value = !loading.value;\n    emit('update:loading', loading.value);\n};\n\nconst onDownload = async () => {\n    changeLoading();\n    downloadFile(logPath.value, props.config.operateNode || globalStore.currentNode);\n    changeLoading();\n};\n\nconst changeTail = (fromOutSide: boolean) => {\n    if (fromOutSide) {\n        tailLog.value = !tailLog.value;\n    }\n    if (tailLog.value) {\n        timer = setInterval(() => {\n            getContent(false);\n        }, 1000 * 3);\n    } else {\n        onCloseLog();\n    }\n};\n\nconst clearLog = (): void => {\n    logs.value = [];\n    scrollTop.value = 0;\n    readReq.page = 1;\n    lastLogs.value = [];\n};\n\nconst getContent = async (pre: boolean) => {\n    if (isLoading.value) {\n        return;\n    }\n    if (readReq.page < 1) {\n        readReq.page = 1;\n    }\n    isLoading.value = true;\n    emit('update:isReading', true);\n\n    let res;\n    try {\n        res = await readByLine(readReq, props.config.operateNode || globalStore.currentNode);\n    } catch (error) {\n        isLoading.value = false;\n        firstLoading.value = false;\n        return;\n    }\n\n    totalLines.value = res.data.totalLines;\n\n    if (res.data.scope == 'tail') {\n        showTail.value = false;\n    }\n\n    if (res.data.taskStatus && res.data.taskStatus !== 'Executing') {\n        isTailDisabled.value = true;\n        tailLog.value = false;\n    }\n\n    logPath.value = res.data.path;\n    firstLoading.value = false;\n\n    if (!end.value && res.data.end) {\n        lastLogs.value = [...logs.value];\n    }\n    if (res.data.lines && res.data.lines.length > 0) {\n        res.data.lines = res.data.lines.map((line) =>\n            line.replace(/\\\\u(\\w{4})/g, function (match, grp) {\n                return String.fromCharCode(parseInt(grp, 16));\n            }),\n        );\n        const newLogs = res.data.lines;\n        if (tailLog.value && newLogs.length === readReq.pageSize && readReq.page < res.data.total) {\n            readReq.page++;\n        }\n        if (\n            readReq.type == 'php' &&\n            logs.value.length > 0 &&\n            newLogs.length > 0 &&\n            newLogs[newLogs.length - 1] === logs.value[logs.value.length - 1]\n        ) {\n            isLoading.value = false;\n            return;\n        }\n\n        if (stopSignals.some((signal) => newLogs[newLogs.length - 1].endsWith(signal))) {\n            onCloseLog();\n        }\n\n        if (logs.value.length == 0) {\n            logs.value = newLogs;\n        } else {\n            if (pre) {\n                logs.value = [...newLogs, ...logs.value];\n            } else {\n                if (end.value) {\n                    logs.value = [...lastLogs.value, ...newLogs];\n                } else {\n                    if (newLogs.length > logs.value.length) {\n                        logs.value = newLogs;\n                    } else {\n                        logs.value = [...logs.value, ...newLogs];\n                    }\n                }\n            }\n        }\n\n        nextTick(() => {\n            if (logContainer.value) {\n                if (pre) {\n                    if (readReq.page > 1) {\n                        const addedLines = newLogs.length;\n                        const newScrollPosition = lastScrollTop.value + addedLines * logHeight;\n                        logContainer.value.scrollTop = newScrollPosition;\n                    }\n                } else {\n                    logContainer.value.scrollTop = logContainer.value.scrollHeight;\n                }\n            }\n        });\n    }\n\n    end.value = res.data.end;\n    totalPages.value = res.data.total;\n    emit('update:hasContent', logs.value.length > 0);\n    if (readReq.latest) {\n        readReq.page = res.data.total;\n        readReq.latest = false;\n        maxPage.value = res.data.total;\n        isEndOfFile.value = true;\n        if (res.data.lines && res.data.lines.length > 500) {\n            minPage.value = res.data.total - 1;\n        } else {\n            minPage.value = res.data.total;\n        }\n    } else {\n        maxPage.value = Math.max(maxPage.value, readReq.page);\n    }\n    if (logs.value && logs.value.length > 3000) {\n        const removedCount = readReq.pageSize;\n        const currentScrollRatio = scrollTop.value / (logs.value.length * logHeight);\n\n        if (pre) {\n            logs.value.splice(logs.value.length - removedCount, removedCount);\n            if (maxPage.value > 1) {\n                maxPage.value--;\n            }\n        } else {\n            isEndOfFile.value = false;\n            logs.value.splice(0, removedCount);\n            nextTick(() => {\n                if (logContainer.value) {\n                    const newScrollTop = currentScrollRatio * (logs.value.length * logHeight);\n                    logContainer.value.scrollTop = Math.max(0, newScrollTop - removedCount * logHeight);\n                }\n            });\n            if (minPage.value > 1) {\n                minPage.value++;\n            }\n        }\n    }\n    isLoading.value = false;\n};\n\nconst onCloseLog = async () => {\n    tailLog.value = false;\n    if (timer) {\n        clearInterval(Number(timer));\n        timer = null;\n    }\n    timer = null;\n    emit('stop-reading');\n    isLoading.value = false;\n    emit('update:isReading', false);\n    bus.emit('refreshTask', true);\n};\n\nwatch(\n    () => props.loading,\n    (newLoading) => {\n        loading.value = newLoading;\n    },\n);\n\nconst init = async () => {\n    tailLog.value = false;\n    if (props.config.tail) {\n        tailLog.value = props.config.tail;\n    }\n    if (tailLog.value) {\n        changeTail(false);\n    }\n    readReq.latest = true;\n    await getContent(false);\n    if (readReq.page > 1 && totalPages.value == maxPage.value) {\n        readReq.page--;\n        await getContent(true);\n    }\n};\n\nconst containerStyle = computed(() => ({\n    height: `calc(100vh - ${props.heightDiff}px)`,\n}));\n\nonMounted(async () => {\n    showTail.value = props.showTail;\n    logs.value = [];\n    isTailDisabled.value = false;\n    firstLoading.value = true;\n    readReq.id = props.config.id;\n    readReq.type = props.config.type;\n    readReq.name = props.config.name;\n    readReq.taskID = props.config.taskID;\n    readReq.taskType = props.config.taskType;\n    readReq.taskOperate = props.config.taskOperate;\n    readReq.resourceID = props.config.resourceID;\n    await init();\n\n    updateContainerHeight();\n\n    if (logContainer.value) {\n        resizeObserver = new ResizeObserver(() => {\n            updateContainerHeight();\n        });\n        resizeObserver.observe(logContainer.value);\n    }\n\n    nextTick(() => {\n        if (logContainer.value) {\n            logContainer.value.scrollTop = logContainer.value.scrollHeight;\n        }\n    });\n});\n\nonUnmounted(() => {\n    onCloseLog();\n    if (resizeObserver && logContainer.value) {\n        resizeObserver.unobserve(logContainer.value);\n        resizeObserver.disconnect();\n    }\n});\n\ndefineExpose({ changeTail, onDownload, clearLog });\n</script>\n<style lang=\"scss\" scoped>\n.log-container {\n    overflow-y: auto;\n    overflow-x: auto;\n    position: relative;\n    background-color: var(--panel-logs-bg-color);\n    margin-top: 10px;\n}\n\n.log-spacer {\n    position: relative;\n    width: 100%;\n}\n\n.log-viewport {\n    position: absolute;\n    top: 0;\n    left: 0;\n    right: 0;\n    will-change: transform;\n    width: max-content;\n    min-width: 100%;\n}\n\n.log-item {\n    min-width: 100%;\n    padding: 5px;\n    color: #f5f5f5;\n    box-sizing: border-box;\n    white-space: nowrap;\n}\n\n.log-content {\n    font-size: 14px;\n    line-height: 20px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/log/file-drawer/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.log')\"\n        @close=\"handleClose\"\n        :size=\"globalStore.isFullScreen ? 'full' : 'large'\"\n    >\n        <template #extra v-if=\"!mobile\">\n            <el-tooltip :content=\"loadTooltip()\" placement=\"top\">\n                <el-button @click=\"toggleFullscreen\" class=\"fullScreen\" icon=\"FullScreen\" plain></el-button>\n            </el-tooltip>\n        </template>\n        <template #content>\n            <LogFile :config=\"config\" :height-diff=\"props.heightDiff\"></LogFile>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport LogFile from '@/components/log/file/index.vue';\nimport { GlobalStore } from '@/store';\nimport i18n from '@/lang';\nimport screenfull from 'screenfull';\n\nconst globalStore = GlobalStore();\ninterface LogProps {\n    id: number;\n    type: string;\n    name: string;\n    tail: boolean;\n}\n\nconst props = defineProps({\n    heightDiff: {\n        type: Number,\n        default: 0,\n    },\n    style: {\n        type: Object,\n        default: () => ({}),\n    },\n});\n\nconst open = ref(false);\nconst config = ref();\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    open.value = false;\n    globalStore.isFullScreen = false;\n    em('close', false);\n};\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nfunction toggleFullscreen() {\n    globalStore.isFullScreen = !globalStore.isFullScreen;\n}\nconst loadTooltip = () => {\n    return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen'));\n};\n\nwatch(open, (val) => {\n    if (screenfull.isEnabled && !val && !mobile.value) screenfull.exit();\n});\n\nconst acceptParams = (logProps: LogProps) => {\n    config.value = logProps;\n    open.value = true;\n};\n\nonBeforeUnmount(() => {\n    handleClose();\n});\n\ndefineExpose({ acceptParams });\n</script>\n\n<style lang=\"scss\">\n.fullScreen {\n    border: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/log/task/index.vue",
    "content": "<template>\n    <el-dialog v-model=\"open\" :show-close=\"showClose\" @close=\"handleClose\" :width=\"width\">\n        <div v-if=\"open\">\n            <LogFile :config=\"config\" :showTail=\"showTail\"></LogFile>\n        </div>\n        <template #footer>\n            <slot name=\"task-footer\"></slot>\n        </template>\n    </el-dialog>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport bus from '@/global/bus';\nimport LogFile from '@/components/log/file/index.vue';\n\ndefineProps({\n    showClose: {\n        type: Boolean,\n        default: true,\n    },\n    width: {\n        type: String,\n        default: '50%',\n    },\n    tail: {\n        type: Boolean,\n        default: true,\n    },\n});\n\nconst config = reactive({\n    id: 0,\n    name: '',\n    taskID: '',\n    type: 'task',\n    taskOperate: '',\n    resourceID: 0,\n    taskType: '',\n    tail: true,\n    colorMode: 'task',\n\n    operateNode: '',\n});\nconst open = ref(false);\nconst showTail = ref(true);\n\nconst openWithTaskID = (id: string, tail: boolean, operateNode?: string) => {\n    config.taskID = id;\n    if (tail === undefined) {\n        config.tail = true;\n    } else {\n        config.tail = tail;\n    }\n    config.operateNode = operateNode || '';\n    open.value = true;\n    bus.emit('refreshTask', true);\n};\n\nconst openWithResourceID = (taskType: string, taskOperate: string, resourceID: number, operateNode?: string) => {\n    config.taskType = taskType;\n    config.resourceID = resourceID;\n    config.taskOperate = taskOperate;\n    config.operateNode = operateNode || '';\n    open.value = true;\n};\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    em('close', true);\n    open.value = false;\n    bus.emit('refreshTask', true);\n    bus.emit('refreshApp', true);\n};\n\ndefineExpose({ openWithResourceID, openWithTaskID, handleClose });\n</script>\n"
  },
  {
    "path": "frontend/src/components/log/task/log-without-dialog.vue",
    "content": "<template>\n    <div>\n        <LogFile :config=\"config\" :showTail=\"false\" :showDownload=\"false\" :heightDiff=\"heightDiff\"></LogFile>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive } from 'vue';\n\nconst props = defineProps({\n    taskID: String,\n    heightDiff: Number,\n});\n\nconst config = reactive({\n    taskID: props.taskID,\n    type: 'task',\n    taskOperate: '',\n    resourceID: 0,\n    taskType: '',\n    tail: false,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/main-div/index.vue",
    "content": "<template>\n    <div class=\"main-div\" :style=\"{ '--main-height': mainHeight + 'px' }\">\n        <slot></slot>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { ref, onMounted, onUnmounted, computed } from 'vue';\nconst props = defineProps({\n    heightDiff: {\n        type: Number,\n        default: 0,\n    },\n});\nconst windowHeight = ref(window.innerHeight);\nconst mainHeight = computed(() => windowHeight.value - props.heightDiff);\n\nconst updateHeight = () => {\n    windowHeight.value = window.innerHeight;\n};\n\nonMounted(() => {\n    window.addEventListener('resize', updateHeight);\n});\n\nonUnmounted(() => {\n    window.removeEventListener('resize', updateHeight);\n});\ndefineOptions({ name: 'MainDiv' });\n</script>\n<style scoped>\n.main-div {\n    height: var(--main-height);\n    overflow-y: auto;\n    overflow-x: hidden;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/mkdown-editor/index.vue",
    "content": "<template>\n    <MdEditor\n        previewOnly\n        :model-value=\"props.content\"\n        :sanitize=\"sanitizeHtml\"\n        :theme=\"isDarkTheme ? 'dark' : 'light'\"\n    />\n</template>\n\n<script lang=\"ts\" setup>\nimport MdEditor from 'md-editor-v3';\nimport 'md-editor-v3/lib/style.css';\nimport DOMPurify from 'dompurify';\n\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { isDarkTheme } = useGlobalStore();\n\nconst props = defineProps({\n    content: {\n        type: String,\n        default: '',\n    },\n});\n\nconst sanitizeHtml = (html: string): string => {\n    return DOMPurify.sanitize(html);\n};\n</script>\n"
  },
  {
    "path": "frontend/src/components/msg-info/index.vue",
    "content": "<template>\n    <el-tooltip placement=\"top-start\">\n        <template #content>\n            <div class=\"info-break\" :style=\"{ width: width + 'px' }\">{{ info }}</div>\n        </template>\n        <div class=\"info-hidden\" :style=\"{ width: width + 'px' }\">{{ info }}</div>\n    </el-tooltip>\n</template>\n<script lang=\"ts\" setup>\ndefineProps({\n    width: {\n        type: String,\n        default: '100',\n    },\n    info: {\n        type: String,\n        default: '',\n    },\n});\ndefineOptions({ name: 'MsgInfo' });\n</script>\n\n<style scoped>\n.info-hidden {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n\n.info-break {\n    width: 100px;\n    word-break: break-all;\n    word-wrap: break-word;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/node-select/index.vue",
    "content": "<template>\n    <el-select\n        :model-value=\"modelValue\"\n        @update:model-value=\"handleChange\"\n        class=\"p-w-200\"\n        :placeholder=\"$t('setting.selectNode')\"\n    >\n        <template #prefix>{{ $t('xpack.node.node') }}</template>\n        <el-option\n            v-for=\"item in nodes\"\n            :key=\"item.id\"\n            :label=\"item.name === 'local' ? globalStore.getMasterAlias() : item.name\"\n            :value=\"item.name\"\n        ></el-option>\n    </el-select>\n</template>\n\n<script setup>\nimport { defineProps, defineEmits } from 'vue';\nimport { listAllNodes } from '@/api/modules/setting';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { globalStore } = useGlobalStore();\n\ndefineProps({\n    modelValue: {\n        type: String,\n        default: '',\n    },\n});\n\nconst nodes = ref([]);\nconst emit = defineEmits(['update:modelValue', 'change']);\n\nconst handleChange = (value) => {\n    emit('update:modelValue', value);\n    emit('change', value);\n};\n\nconst listNodes = async () => {\n    try {\n        const res = await listAllNodes();\n        nodes.value = res.data || [];\n    } catch (error) {}\n};\n\nonMounted(() => {\n    listNodes();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/port-jump/index.vue",
    "content": "<template>\n    <div>\n        <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"small\">\n            <el-alert :closable=\"false\" :title=\"$t('setting.systemIPWarning')\" type=\"info\">\n                <el-link icon=\"Position\" @click=\"jumpToPath(router, '/settings/panel')\" type=\"primary\">\n                    {{ $t('firewall.quickJump') }}\n                </el-link>\n            </el-alert>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                </span>\n            </template>\n        </DialogPro>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { getAgentSettingInfo } from '@/api/modules/setting';\nimport i18n from '@/lang';\nimport { MsgError, MsgWarning } from '@/utils/message';\nimport { jumpToPath } from '@/utils/util';\nimport { useRouter } from 'vue-router';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\nconst router = useRouter();\n\nconst open = ref();\n\ninterface DialogProps {\n    port: any;\n    ip: string;\n    protocol: string;\n    path?: string;\n    query?: string;\n    hash?: string;\n}\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    if (Number(params.port) === 0) {\n        MsgError(i18n.global.t('commons.msg.errPort'));\n        return;\n    }\n    let protocol = params.protocol === 'https' ? 'https' : 'http';\n    const res = await getAgentSettingInfo();\n    if (!res.data.systemIP) {\n        if (!globalStore.isMaster || globalStore.currentNodeAddr != '127.0.0.1') {\n            res.data.systemIP = globalStore.currentNodeAddr;\n        } else {\n            open.value = true;\n            return;\n        }\n    }\n    const buildUrl = (host: string) => {\n        let url = `${protocol}://${host}:${params.port}`;\n        if (params.path) {\n            url += params.path.startsWith('/') ? params.path : `/${params.path}`;\n        }\n        if (params.query) {\n            url += params.query.startsWith('?') ? params.query : `?${params.query}`;\n        }\n        if (params.hash) {\n            url += params.hash.startsWith('#') ? params.hash : `#${params.hash}`;\n        }\n        return url;\n    };\n    if (res.data.systemIP.indexOf(':') === -1) {\n        if (params.ip && params.ip === 'ipv6') {\n            MsgWarning(i18n.global.t('setting.systemIPWarning1', ['IPv4']));\n            return;\n        }\n        window.open(buildUrl(res.data.systemIP), '_blank');\n    } else {\n        if (params.ip && params.ip === 'ipv4') {\n            MsgWarning(i18n.global.t('setting.systemIPWarning1', ['IPv6']));\n            return;\n        }\n        window.open(buildUrl(`[${res.data.systemIP}]`), '_blank');\n    }\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/components/router-button/index.vue",
    "content": "<template>\n    <el-card class=\"router_card p-1 sm:p-0\">\n        <div class=\"flex w-full flex-col justify-start sm:items-center items-start sm:justify-between sm:flex-row\">\n            <el-radio-group v-model=\"activeName\" @change=\"handleChange\">\n                <el-radio-button\n                    class=\"router_card_button\"\n                    :label=\"button.label\"\n                    :value=\"button.label\"\n                    v-for=\"(button, index) in buttonArray\"\n                    size=\"large\"\n                    :key=\"index\"\n                >\n                    <el-badge :value=\"button.count\" v-if=\"button.count\" is-dot>\n                        <span>{{ button.label }}</span>\n                    </el-badge>\n                </el-radio-button>\n            </el-radio-group>\n            <div class=\"flex flex-col gap-2 sm:flex-row\">\n                <slot name=\"route-button\"></slot>\n            </div>\n        </div>\n    </el-card>\n</template>\n\n<script lang=\"ts\" setup>\nimport { routerToName, routerToPath } from '@/utils/router';\nimport { computed, onMounted, ref } from 'vue';\nimport { useRouter } from 'vue-router';\n\ndefineOptions({ name: 'RouterButton' });\n\nconst props = defineProps({\n    buttons: {\n        type: Array<RouterButton>,\n        required: true,\n    },\n});\n\nconst buttonArray = computed(() => {\n    return props.buttons;\n});\n\nconst router = useRouter();\nconst activeName = ref('');\n\nconst handleChange = (label: string) => {\n    const btn = buttonArray.value.find((btn) => btn.label === label);\n    if (!btn) return;\n    if (btn.path) routerToPath(btn.path);\n    else if (btn.name) routerToName(btn.name);\n    activeName.value = btn.label;\n};\n\nonMounted(() => {\n    if (buttonArray.value.length) {\n        let isPathExist = false;\n        const btn = buttonArray.value.find((btn) => {\n            return router.currentRoute.value.path.startsWith(btn.path);\n        });\n        if (btn) {\n            isPathExist = true;\n            activeName.value = btn.label;\n        }\n        if (!isPathExist) {\n            activeName.value = buttonArray.value[0].label;\n        }\n    }\n});\n</script>\n\n<style lang=\"scss\">\n.router_card {\n    --el-card-padding: 0;\n    .el-card__body {\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n    }\n}\n\n.router_card_button {\n    .el-radio-button__inner {\n        min-width: 100px;\n        height: 100%;\n        background-color: var(--panel-button-active) !important;\n        box-shadow: none !important;\n        border: 2px solid transparent !important;\n        color: var(--el-text-color-regular) !important;\n    }\n\n    .el-radio-button__original-radio:checked + .el-radio-button__inner {\n        color: var(--panel-button-text-color) !important;\n        background-color: var(--panel-button-bg-color) !important;\n        border-color: var(--panel-color-primary) !important;\n        border-radius: 4px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/status/index.vue",
    "content": "<template>\n    <el-tooltip v-if=\"msg && msg != ''\" effect=\"dark\" placement=\"bottom\">\n        <template #content>\n            <div class=\"content\">{{ msg }}</div>\n        </template>\n        <el-tag size=\"small\" :type=\"getType(statusItem)\" round effect=\"light\">\n            <span class=\"flx-align-center\">\n                <span v-if=\"statusItem != ''\">{{ $t('commons.status.' + statusItem) }}</span>\n                <el-icon v-if=\"loadingIcon(statusItem)\" class=\"is-loading\">\n                    <Loading />\n                </el-icon>\n            </span>\n        </el-tag>\n    </el-tooltip>\n    <span v-else>\n        <el-tag size=\"small\" :type=\"getType(statusItem)\" round effect=\"light\" v-if=\"!operate\">\n            <span class=\"flx-align-center\">\n                <span v-if=\"statusItem != ''\">{{ $t('commons.status.' + statusItem) }}</span>\n                <el-icon v-if=\"loadingIcon(statusItem)\" class=\"is-loading\">\n                    <Loading />\n                </el-icon>\n            </span>\n        </el-tag>\n        <el-button size=\"small\" v-else :type=\"getType(statusItem)\" plain round>\n            <span v-if=\"statusItem != ''\">{{ $t('commons.status.' + statusItem) }}</span>\n            <el-icon v-if=\"loadingIcon(statusItem)\" class=\"is-loading\">\n                <Loading />\n            </el-icon>\n            <svg-icon iconName=\"p-stop\" className=\"status-icon\" v-if=\"stopIcon(statusItem)\"></svg-icon>\n            <svg-icon iconName=\"p-start\" className=\"status-icon\" v-if=\"runningIcon(statusItem)\"></svg-icon>\n        </el-button>\n    </span>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed } from 'vue';\n\nconst props = defineProps({\n    status: String,\n    msg: String,\n    hasIcon: Boolean,\n    operate: {\n        type: Boolean,\n        default: false,\n        required: false,\n    },\n});\n\nconst statusItem = computed(() => {\n    return props.status?.toLowerCase() || '';\n});\n\nconst getType = (status: string) => {\n    if (status.includes('error') || status.includes('err')) {\n        return 'danger';\n    }\n    switch (status) {\n        case 'running':\n        case 'free':\n        case 'success':\n        case 'enable':\n        case 'done':\n        case 'healthy':\n        case 'unused':\n        case 'executing':\n        case 'new':\n            return 'success';\n        case 'stopped':\n        case 'exceptional':\n        case 'disable':\n        case 'unhealthy':\n        case 'failed':\n        case 'lost':\n        case 'exited':\n            return 'danger';\n        case 'paused':\n        case 'dead':\n        case 'removing':\n        case 'deleted':\n        case 'conflict':\n            return 'warning';\n        case 'duplicate':\n        case 'unexecuted':\n            return 'info';\n        default:\n            return 'primary';\n    }\n};\n\nconst loadingStatus = [\n    'installing',\n    'building',\n    'restarting',\n    'upgrading',\n    'rebuilding',\n    'recreating',\n    'creating',\n    'starting',\n    'removing',\n    'applying',\n    'uninstalling',\n    'downloading',\n    'packing',\n    'sending',\n    'waiting',\n    'executing',\n    'loading',\n];\n\nconst stopStatus = ['stopped', 'exited', 'disable'];\nconst runningStatus = ['running', 'enable'];\n\nconst loadingIcon = (status: string): boolean => {\n    return loadingStatus.indexOf(status) > -1;\n};\nconst stopIcon = (status: string): boolean => {\n    return stopStatus.indexOf(status.toLocaleLowerCase()) > -1;\n};\nconst runningIcon = (status: string): boolean => {\n    return runningStatus.indexOf(status.toLocaleLowerCase()) > -1;\n};\n</script>\n\n<style lang=\"scss\" scoped>\n.content {\n    width: 300px;\n    word-break: break-all;\n}\n\n.status-icon {\n    width: 1em;\n    height: 1em;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/svg-icon/svg-icon.vue",
    "content": "<template>\n    <svg :class=\"svgClass\" aria-hidden=\"true\">\n        <use :xlink:href=\"iconClassName\" />\n    </svg>\n</template>\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nconst props = defineProps({\n    iconName: {\n        type: String,\n        required: true,\n    },\n    className: {\n        type: String,\n        default: '',\n    },\n    color: {\n        type: String,\n        default: '#005eeb',\n    },\n});\nconst iconClassName = computed(() => {\n    return `#${props.iconName}`;\n});\nconst svgClass = computed(() => {\n    if (props.className) {\n        return `svg-icon ${props.className}`;\n    }\n    return 'svg-icon';\n});\n</script>\n<style scoped>\n.svg-icon {\n    width: 2.5em;\n    height: 2.5em;\n    position: relative;\n    fill: currentColor;\n    vertical-align: -2px;\n    padding-top: 0.1em;\n    padding-bottom: 0.1em;\n    padding-left: 0.1em;\n    padding-right: 0.1em;\n}\n.table-icon {\n    width: 1.5em;\n    height: 1.5em;\n    position: relative;\n    fill: currentColor;\n    vertical-align: middle;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/system-upgrade/index.vue",
    "content": "<template>\n    <div>\n        <div class=\"flex w-full flex-col gap-2 md:flex-row items-center\">\n            <div class=\"flex flex-wrap gap-y-2 items-center\">\n                <span v-if=\"props.footer\">\n                    <el-link type=\"primary\" underline=\"never\" @click=\"toForum\" v-if=\"!isFxplay\">\n                        <span class=\"font-normal\">{{ $t('setting.forum') }}</span>\n                    </el-link>\n                    <el-divider direction=\"vertical\" v-if=\"!isFxplay\" />\n                    <el-link type=\"primary\" underline=\"never\" @click=\"toDoc\">\n                        <span class=\"font-normal\">{{ $t('setting.doc2') }}</span>\n                    </el-link>\n                    <el-divider direction=\"vertical\" v-if=\"!isFxplay\" />\n                    <el-link type=\"primary\" underline=\"never\" @click=\"toGithub\" v-if=\"!isFxplay\">\n                        <span class=\"font-normal\">{{ $t('setting.project') }}</span>\n                    </el-link>\n                    <el-divider direction=\"vertical\" />\n                </span>\n                <div class=\"flex flex-wrap items-center\">\n                    <el-link underline=\"never\" type=\"primary\" @click=\"toLxware\">\n                        <span v-if=\"isMasterPro\">\n                            {{ $t('license.pro') }}\n                        </span>\n                        <span v-else-if=\"isOffLine\">\n                            {{ $t('license.offLine') }}\n                        </span>\n                        <span v-else>\n                            {{ $t('license.community') }}\n                        </span>\n                    </el-link>\n                    <el-link underline=\"never\" class=\"version\" type=\"primary\" @click=\"getVersionLog()\">\n                        {{ version }}\n                    </el-link>\n                    <el-badge is-dot class=\"-mt-0.5\" :hidden=\"version === 'Waiting' || !globalStore.hasNewVersion\">\n                        <el-link\n                            class=\"ml-2\"\n                            underline=\"never\"\n                            type=\"primary\"\n                            @click=\"onLoadUpgradeInfo\"\n                            v-if=\"!globalStore.isOffLine\"\n                        >\n                            {{ $t('commons.button.update') }}\n                        </el-link>\n                    </el-badge>\n                    <el-tag v-if=\"version === 'Waiting'\" round class=\"ml-2.5\">{{ $t('setting.upgrading') }}</el-tag>\n                </div>\n            </div>\n        </div>\n\n        <Upgrade ref=\"upgradeRef\" @search=\"search\" />\n        <Releases ref=\"releasesRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { getSettingInfo, loadUpgradeInfo } from '@/api/modules/setting';\nimport Upgrade from '@/components/system-upgrade/upgrade/index.vue';\nimport Releases from '@/components/system-upgrade/releases/index.vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { onMounted, ref } from 'vue';\nimport { GlobalStore } from '@/store';\nimport { storeToRefs } from 'pinia';\n\nconst globalStore = GlobalStore();\nconst { docsUrl, isOffLine, isFxplay } = storeToRefs(globalStore);\nconst upgradeRef = ref();\nconst releasesRef = ref();\nconst isMasterPro = computed(() => {\n    return globalStore.isMasterPro();\n});\n\nconst version = ref<string>('');\nconst loading = ref(false);\nconst upgradeInfo = ref();\nconst upgradeVersion = ref();\nconst props = defineProps({\n    footer: {\n        type: Boolean,\n        default: false,\n    },\n});\n\nconst search = async () => {\n    const res = await getSettingInfo();\n    version.value = res.data.systemVersion;\n};\n\nconst getVersionLog = () => {\n    if (isOffLine.value) {\n        return;\n    }\n    releasesRef.value.acceptParams();\n};\n\nconst toLxware = () => {\n    if (isOffLine.value) {\n        to1Panel();\n        return;\n    }\n    if (!globalStore.isIntl) {\n        window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer');\n    } else {\n        window.open('https://1panel.pro/pricing' + '', '_blank', 'noopener,noreferrer');\n    }\n};\n\nconst to1Panel = () => {\n    let url = globalStore.isIntl ? 'https://1panel.pro' : 'https://1panel.cn';\n    window.open(url, '_blank', 'noopener,noreferrer');\n};\n\nconst toDoc = () => {\n    window.open(docsUrl.value, '_blank', 'noopener,noreferrer');\n};\n\nconst toForum = () => {\n    let url = globalStore.isIntl\n        ? 'https://github.com/1Panel-dev/1Panel/discussions'\n        : 'https://bbs.fit2cloud.com/c/1p/7';\n    window.open(url, '_blank', 'noopener,noreferrer');\n};\n\nconst toGithub = () => {\n    window.open('https://github.com/1Panel-dev/1Panel', '_blank', 'noopener,noreferrer');\n};\n\nconst onLoadUpgradeInfo = async () => {\n    loading.value = true;\n    await loadUpgradeInfo()\n        .then((res) => {\n            loading.value = false;\n            if (res.data.testVersion || res.data.newVersion || res.data.latestVersion) {\n                upgradeInfo.value = res.data;\n                if (upgradeInfo.value.latestVersion) {\n                    upgradeVersion.value = upgradeInfo.value.latestVersion;\n                } else if (upgradeInfo.value.testVersion) {\n                    upgradeVersion.value = upgradeInfo.value.testVersion;\n                } else if (upgradeInfo.value.newVersion) {\n                    upgradeVersion.value = upgradeInfo.value.newVersion;\n                }\n                upgradeRef.value.acceptParams({ upgradeInfo: upgradeInfo.value, upgradeVersion: upgradeVersion.value });\n            } else {\n                MsgSuccess(i18n.global.t('setting.noUpgrade'));\n                return;\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.line-height {\n    line-height: 25px;\n}\n:deep(.el-link__inner) {\n    font-weight: 400;\n}\n.version {\n    margin-left: 8px;\n    font-size: 14px;\n    color: var(--panel-color-primary-light-4);\n    text-decoration: none;\n    letter-spacing: 0.5px;\n    cursor: pointer;\n    font-family: auto;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/system-upgrade/releases/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.release')\" @close=\"handleClose\" size=\"large\">\n        <div class=\"note\" v-loading=\"loading\">\n            <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\">\n                <div class=\"release-settings\">\n                    <el-form-item :label=\"$t('setting.versionItem')\" prop=\"version\" class=\"compact-form-item\">\n                        <div class=\"setting-control\">\n                            <el-input disabled v-model=\"form.version\" />\n                            <CopyButton\n                                class=\"setting-action-btn\"\n                                :isIcon=\"false\"\n                                :content=\"form.version\"\n                                type=\"primary\"\n                            />\n                        </div>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('setting.backupCopies')\" prop=\"backupCopies\" class=\"compact-form-item\">\n                        <div class=\"setting-control\">\n                            <el-input-number v-model=\"form.backupCopies\" :min=\"0\" controls-position=\"right\" />\n                            <el-button @click=\"onSave(formRef)\" class=\"setting-action-btn\" type=\"primary\">\n                                {{ $t('commons.button.save') }}\n                            </el-button>\n                        </div>\n                        <span class=\"input-help\">{{ $t('setting.backupCopiesHelper') }}</span>\n                    </el-form-item>\n                </div>\n            </el-form>\n            <el-collapse v-if=\"notes && notes.length !== 0\" v-model=\"currentVersion\" :accordion=\"true\">\n                <div v-for=\"(item, index) in notes\" :key=\"index\">\n                    <el-collapse-item :name=\"index\">\n                        <template #title>\n                            <span class=\"version\">{{ item.version }}</span>\n                            <span v-if=\"!mobile\" class=\"date\">{{ item.createdAt }}</span>\n                            <svg-icon class=\"icon\" iconName=\"p-featureshitu\"></svg-icon>\n                            <span class=\"icon-span\">{{ item.newCount }}</span>\n                            <svg-icon class=\"icon\" iconName=\"p-youhuawendang\"></svg-icon>\n                            <span class=\"icon-span\">{{ item.optimizationCount }}</span>\n                            <svg-icon class=\"icon\" iconName=\"p-bug\"></svg-icon>\n                            <span class=\"icon-span\">{{ item.fixCount }}</span>\n                        </template>\n                        <div class=\"panel-MdEditor\">\n                            <MarkDownEditor :content=\"item.content\" />\n                        </div>\n                    </el-collapse-item>\n                </div>\n            </el-collapse>\n            <el-empty v-else>\n                <template #description>\n                    <span class=\"input-help\">\n                        {{ $t('setting.releaseHelper') }}\n                        <el-link class=\"pageRoute\" icon=\"Position\" type=\"primary\">\n                            {{ $t('firewall.quickJump') }}\n                        </el-link>\n                    </span>\n                </template>\n            </el-empty>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport MarkDownEditor from '@/components/mkdown-editor/index.vue';\n\nimport { getSettingInfo, listReleases, updateSetting } from '@/api/modules/setting';\nimport { ref } from 'vue';\nimport { GlobalStore } from '@/store';\nimport { FormInstance } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { Rules } from '@/global/form-rules';\n\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst drawerVisible = ref(false);\nconst currentVersion = ref(0);\nconst notes = ref([]);\nconst loading = ref();\nconst formRef = ref();\n\nconst form = reactive({\n    version: '',\n    backupCopies: 0,\n});\nconst rules = reactive({\n    version: [Rules.requiredInput],\n    backupCopies: [{ validator: checkBackupCopies, trigger: 'blur', required: true }],\n});\n\nconst acceptParams = (): void => {\n    search();\n    loadInfo();\n    drawerVisible.value = true;\n};\n\nconst loadInfo = async () => {\n    const res = await getSettingInfo();\n    form.version = res.data.systemVersion;\n    form.backupCopies = Number(res.data.upgradeBackupCopies) || 0;\n};\n\nfunction checkBackupCopies(rule: any, value: any, callback: any) {\n    if (value === 0) {\n        return callback();\n    }\n    if (value < 3) {\n        return callback(new Error(i18n.global.t('setting.backupCopiesRule')));\n    }\n    callback();\n}\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await updateSetting({ key: 'UpgradeBackupCopies', value: form.backupCopies + '' })\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst search = async () => {\n    loading.value = true;\n    await listReleases()\n        .then((res) => {\n            notes.value = res.data || [];\n            loading.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.version {\n    margin-left: 10px;\n    display: inline-block;\n    width: 50px;\n}\n.date {\n    margin-left: 20px;\n    margin-right: 40px;\n    display: inline-block;\n    width: 100px;\n}\n.icon-span {\n    display: inline-block;\n    width: 10px;\n}\n.panel-MdEditor {\n    :deep(.md-editor-preview) {\n        font-size: 12px;\n    }\n}\n:deep(.md-editor-dark) {\n    background-color: var(--panel-main-bg-color-9);\n}\n:deep(.el-collapse-item__content) {\n    padding: 0px;\n}\n.icon {\n    display: inline-block;\n    font-size: 7px;\n    margin-left: 50px;\n}\n.pageRoute {\n    font-size: 12px;\n    margin-left: 5px;\n    margin-top: -4px;\n}\n.release-settings {\n    margin-bottom: 16px;\n    padding: 14px 14px 10px;\n    border: 1px solid var(--el-border-color-light);\n    border-radius: 10px;\n}\n.compact-form-item {\n    margin-bottom: 12px;\n}\n.compact-form-item :deep(.el-form-item__label) {\n    padding-bottom: 6px;\n}\n.setting-control {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    max-width: 340px;\n    width: 100%;\n}\n.setting-control :deep(.el-input),\n.setting-control :deep(.el-input-number) {\n    flex: 1;\n    width: 100%;\n}\n.setting-action-btn {\n    min-width: 64px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/system-upgrade/upgrade/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('commons.button.upgrade')\" @close=\"handleClose\" size=\"large\">\n        <div class=\"panel-MdEditor\">\n            <div class=\"default-theme\" style=\"margin-left: 20px\">\n                <h2 class=\"inline-block\">{{ $t('app.version') }}</h2>\n            </div>\n            <el-radio-group class=\"inline-block tag\" v-model=\"upgradeVersion\" @change=\"changeOption\">\n                <el-radio v-if=\"upgradeInfo.newVersion\" :value=\"upgradeInfo.newVersion\">\n                    {{ upgradeInfo.newVersion }}\n                </el-radio>\n                <el-radio v-if=\"upgradeInfo.latestVersion\" :value=\"upgradeInfo.latestVersion\">\n                    {{ upgradeInfo.latestVersion }}\n                </el-radio>\n                <el-radio v-if=\"upgradeInfo.testVersion\" :value=\"upgradeInfo.testVersion\">\n                    {{ upgradeInfo.testVersion }}\n                </el-radio>\n            </el-radio-group>\n            <MarkDownEditor v-loading=\"loading\" :content=\"upgradeInfo.releaseNote\" />\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onUpgrade\">{{ $t('setting.upgradeNow') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport MarkDownEditor from '@/components/mkdown-editor/index.vue';\n\nimport { loadReleaseNotes, upgrade } from '@/api/modules/setting';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ref } from 'vue';\nimport { GlobalStore } from '@/store';\nimport { ElMessageBox } from 'element-plus';\n\nconst globalStore = GlobalStore();\n\nconst drawerVisible = ref(false);\nconst upgradeInfo = ref();\nconst loading = ref();\nconst upgradeVersion = ref();\n\ninterface DialogProps {\n    upgradeInfo: number;\n    upgradeVersion: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    upgradeInfo.value = params.upgradeInfo;\n    upgradeVersion.value = params.upgradeVersion;\n    drawerVisible.value = true;\n};\n\nconst emit = defineEmits(['search']);\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst changeOption = async () => {\n    loading.value = true;\n    await loadReleaseNotes(upgradeVersion.value)\n        .then((res) => {\n            loading.value = false;\n            upgradeInfo.value.releaseNote = res.data;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onUpgrade = async () => {\n    ElMessageBox.confirm(i18n.global.t('setting.upgradeHelper', i18n.global.t('commons.button.upgrade')), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        await upgrade(upgradeVersion.value);\n        globalStore.isLoading = true;\n        globalStore.isOnRestart = true;\n        drawerVisible.value = false;\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        emit('search');\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.line-height {\n    line-height: 25px;\n}\n.panel-MdEditor {\n    height: calc(100vh - 330px);\n    .tag {\n        margin-top: -6px;\n        margin-left: 20px;\n        vertical-align: middle;\n    }\n    :deep(.md-editor-preview) {\n        font-size: 14px;\n    }\n    :deep(.default-theme h2) {\n        color: var(--el-color-primary);\n        margin: 13px 0;\n        padding: 0;\n        font-size: 16px;\n    }\n}\n:deep(.md-editor-dark) {\n    background-color: var(--panel-main-bg-color-9);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/table-refresh/index.vue",
    "content": "<template>\n    <el-button class=\"fresh-button\" icon=\"Refresh\" @click=\"onRefresh\"></el-button>\n</template>\n\n<script setup lang=\"ts\">\ndefineOptions({ name: 'TableRefresh' });\n\nconst emit = defineEmits(['search']);\n\nconst onRefresh = () => {\n    emit('search');\n};\n</script>\n\n<style lang=\"scss\" scoped>\n.fresh-button {\n    float: right;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/table-search/index.vue",
    "content": "<template>\n    <div>\n        <div class=\"search-button\">\n            <el-input\n                clearable\n                v-model=\"searchInfo\"\n                @clear=\"search()\"\n                suffix-icon=\"Search\"\n                @change=\"search()\"\n                :disabled=\"props.disabled\"\n                :placeholder=\"props.placeholder || $t('commons.button.search')\"\n            ></el-input>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch } from 'vue';\ndefineOptions({ name: 'TableSearch' });\n\nconst emit = defineEmits(['search', 'update:searchName']);\nconst searchInfo = ref();\nconst props = defineProps({\n    placeholder: String,\n    disabled: {\n        type: Boolean,\n        default: false,\n    },\n    searchName: {\n        type: [String, Number],\n        default: undefined,\n    },\n});\n\nwatch(\n    () => props.searchName,\n    (newVal) => {\n        if (searchInfo.value !== newVal) {\n            searchInfo.value = newVal;\n        }\n    },\n    { immediate: true },\n);\n\nconst search = () => {\n    emit('update:searchName', searchInfo.value);\n    emit('search');\n};\n</script>\n"
  },
  {
    "path": "frontend/src/components/table-setting/index.vue",
    "content": "<template>\n    <div>\n        <el-dropdown @command=\"changeRefresh\">\n            <el-button class=\"timer-button\">\n                {{ refreshRateUnit === 0 ? $t('commons.table.noRefresh') : refreshRateUnit + 's' }}\n            </el-button>\n            <template #dropdown>\n                <el-dropdown-menu>\n                    <el-dropdown-item :command=\"0\">{{ $t('commons.table.noRefresh') }}</el-dropdown-item>\n                    <el-dropdown-item :command=\"5\">5s</el-dropdown-item>\n                    <el-dropdown-item :command=\"10\">10s</el-dropdown-item>\n                    <el-dropdown-item :command=\"30\">30s</el-dropdown-item>\n                    <el-dropdown-item :command=\"60\">60s</el-dropdown-item>\n                    <el-dropdown-item :command=\"120\">120s</el-dropdown-item>\n                    <el-dropdown-item :command=\"300\">300s</el-dropdown-item>\n                </el-dropdown-menu>\n            </template>\n        </el-dropdown>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue';\ndefineOptions({ name: 'TableSetting' });\n\nconst refreshRateUnit = ref<number>(0);\nconst emit = defineEmits(['search']);\nconst props = defineProps({\n    title: String,\n    rate: Number,\n});\n\nlet timer: NodeJS.Timer | null = null;\n\nconst changeRefresh = (command: number) => {\n    refreshRateUnit.value = command || 0;\n    if (refreshRateUnit.value !== 0) {\n        if (timer) {\n            clearInterval(Number(timer));\n            timer = null;\n        }\n        timer = setInterval(() => {\n            emit('search');\n        }, 1000 * refreshRateUnit.value);\n    } else {\n        if (timer) {\n            clearInterval(Number(timer));\n            timer = null;\n        }\n    }\n    localStorage.setItem(props.title, refreshRateUnit.value + '');\n};\n\nonUnmounted(() => {\n    clearInterval(Number(timer));\n    timer = null;\n    if (props.title) {\n        localStorage.setItem(props.title, refreshRateUnit.value + '');\n    }\n});\n\nonMounted(() => {\n    if (props.title && localStorage.getItem(props.title) != null) {\n        let rate = Number(localStorage.getItem(props.title));\n        refreshRateUnit.value = rate ? Number(rate) : 0;\n        changeRefresh(refreshRateUnit.value);\n        return;\n    }\n    if (props.rate) {\n        refreshRateUnit.value = props.rate;\n        changeRefresh(refreshRateUnit.value);\n    }\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.timer-button {\n    float: right;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/task-list/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" size=\"large\" :header=\"$t('menu.msgCenter')\" @close=\"handleClose\">\n        <template #content>\n            <LayoutContent v-loading=\"loading\" :title=\"$t('logs.task')\">\n                <template #leftToolBar>\n                    <NodeSelect v-model=\"targeNode\" @change=\"search()\" />\n                </template>\n                <template #rightToolBar>\n                    <el-select v-model=\"req.status\" @change=\"search()\" clearable class=\"p-w-200\">\n                        <template #prefix>{{ $t('commons.table.status') }}</template>\n                        <el-option :label=\"$t('commons.table.all')\" value=\"\"></el-option>\n                        <el-option :label=\"$t('commons.status.success')\" value=\"Success\"></el-option>\n                        <el-option :label=\"$t('commons.status.failed')\" value=\"Failed\"></el-option>\n                        <el-option :label=\"$t('logs.taskRunning')\" value=\"Executing\"></el-option>\n                    </el-select>\n                    <TableRefresh @search=\"search()\" />\n                    <TableSetting title=\"task-refresh\" @search=\"search()\" :rate=\"5\" />\n                </template>\n                <template #main>\n                    <ComplexTable :pagination-config=\"paginationConfig\" :data=\"data\" @search=\"search\" :heightDiff=\"320\">\n                        <el-table-column :label=\"$t('logs.taskName')\" prop=\"name\" min-width=\"180px\"></el-table-column>\n                        <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\" max-width=\"100px\">\n                            <template #default=\"{ row }\">\n                                <Status :status=\"row.status\" :msg=\"row.errorMsg\" />\n                            </template>\n                        </el-table-column>\n                        <el-table-column :label=\"$t('commons.button.log')\" prop=\"log\" max-width=\"100px\">\n                            <template #default=\"{ row }\">\n                                <el-button @click=\"openTaskLog(row)\" link type=\"primary\">\n                                    {{ $t('website.check') }}\n                                </el-button>\n                            </template>\n                        </el-table-column>\n                        <el-table-column\n                            prop=\"createdAt\"\n                            :label=\"$t('commons.table.date')\"\n                            :formatter=\"dateFormat\"\n                            show-overflow-tooltip\n                        />\n                    </ComplexTable>\n                </template>\n            </LayoutContent>\n\n            <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport TaskLog from '@/components/log/task/index.vue';\nimport NodeSelect from '@/components/node-select/index.vue';\n\nimport { dateFormat } from '@/utils/util';\nimport { searchTasks } from '@/api/modules/log';\nimport { reactive, ref } from 'vue';\nimport { Log } from '@/api/interface/log';\nimport bus from '@/global/bus';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst open = ref(false);\nconst handleClose = () => {\n    open.value = false;\n};\nconst loading = ref();\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'task-list-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('task-list-page-size')) || 20,\n    total: 0,\n    small: true,\n});\nconst taskLogRef = ref();\nconst req = reactive({\n    type: '',\n    status: 'Executing',\n    page: 1,\n    pageSize: 10,\n});\nconst targeNode = ref('local');\n\nconst search = async () => {\n    bus.emit('refreshTask', true);\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    try {\n        const res = await searchTasks(req, targeNode.value);\n        loading.value = false;\n        data.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst openTaskLog = (row: Log.Task) => {\n    taskLogRef.value.openWithTaskID(row.id, row.status == 'Executing', targeNode.value);\n};\n\nconst acceptParams = () => {\n    targeNode.value = globalStore.currentNode;\n    search();\n    open.value = true;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/terminal/database.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('menu.terminal')\"\n        @close=\"handleClose\"\n        :resource=\"database\"\n        :autoClose=\"!open\"\n        size=\"large\"\n        :fullScreen=\"true\"\n    >\n        <template #content>\n            <Terminal style=\"height: calc(100vh - 100px)\" ref=\"terminalRef\"></Terminal>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, nextTick } from 'vue';\nimport Terminal from '@/components/terminal/index.vue';\n\nconst open = ref(false);\nconst terminalRef = ref<InstanceType<typeof Terminal> | null>(null);\nconst database = ref();\nconst databaseType = ref();\n\ninterface DialogProps {\n    databaseType: string;\n    database: string;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    database.value = params.database;\n    databaseType.value = params.databaseType;\n    open.value = false;\n    await initTerm();\n};\n\nconst initTerm = async () => {\n    open.value = true;\n    await nextTick();\n    terminalRef.value!.acceptParams({\n        endpoint: '/api/v2/containers/exec',\n        args: `source=database&databaseType=${databaseType.value}&database=${database.value}`,\n        error: '',\n        initCmd: '',\n    });\n};\n\nfunction handleClose() {\n    terminalRef.value?.onClose();\n    open.value = false;\n}\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/terminal/index.vue",
    "content": "<template>\n    <div ref=\"terminalElement\" class=\"terminal-container\"></div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, watch, onBeforeUnmount, nextTick, computed, onMounted } from 'vue';\nimport { Terminal } from '@xterm/xterm';\nimport '@xterm/xterm/css/xterm.css';\nimport { FitAddon } from '@xterm/addon-fit';\nimport { Base64 } from 'js-base64';\nimport { GlobalStore, TerminalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst terminalElement = ref<HTMLDivElement | null>(null);\nconst fitAddon = new FitAddon();\nconst termReady = ref(false);\nconst webSocketReady = ref(false);\nconst term = ref();\nconst terminalSocket = ref<WebSocket>();\nconst heartbeatTimer = ref<NodeJS.Timer>();\nconst latency = ref(0);\nconst initCmd = ref('');\n\nconst readyWatcher = watch(\n    () => webSocketReady.value && termReady.value,\n    (ready) => {\n        if (ready) {\n            changeTerminalSize();\n            readyWatcher(); // unwatch self\n        }\n    },\n);\n\nconst terminalStore = TerminalStore();\nconst lineHeight = computed(() => terminalStore.lineHeight);\nconst fontSize = computed(() => terminalStore.fontSize);\nconst fontFamily = computed(() => terminalStore.fontFamily);\nconst backgroundColor = computed(() => terminalStore.backgroundColor);\nconst foregroundColor = computed(() => terminalStore.foregroundColor);\nconst letterSpacing = computed(() => terminalStore.letterSpacing);\nwatch(\n    [lineHeight, fontSize, letterSpacing, fontFamily],\n    ([newLineHeight, newFontSize, newLetterSpacing, newFontFamily]) => {\n        if (!term.value) return;\n        term.value.options.lineHeight = newLineHeight;\n        term.value.options.letterSpacing = newLetterSpacing;\n        term.value.options.fontSize = newFontSize;\n        term.value.options.fontFamily = newFontFamily;\n        changeTerminalSize();\n    },\n);\nwatch([backgroundColor, foregroundColor], ([newBackgroundColor, newForegroundColor]) => {\n    if (!term.value) return;\n    term.value.options.theme = {\n        ...(term.value.options.theme || {}),\n        background: newBackgroundColor,\n        foreground: newForegroundColor,\n    };\n    applyTerminalBackground(newBackgroundColor);\n});\nconst cursorStyle = computed(() => terminalStore.cursorStyle);\nwatch(cursorStyle, (newCursorStyle) => {\n    if (!term.value) return;\n    term.value.options.cursorStyle = newCursorStyle;\n});\nconst cursorBlink = computed(() => terminalStore.cursorBlink);\nwatch(cursorBlink, (newCursorBlink) => {\n    if (!term.value) return;\n    term.value.options.cursorBlink = String(newCursorBlink).toLowerCase() === 'enable';\n});\nconst scrollback = computed(() => terminalStore.scrollback);\nwatch(scrollback, (newScrollback) => {\n    if (!term.value) return;\n    term.value.options.scrollback = newScrollback;\n});\nconst scrollSensitivity = computed(() => terminalStore.scrollSensitivity);\nwatch(scrollSensitivity, (newScrollSensitivity) => {\n    if (!term.value) return;\n    term.value.options.scrollSensitivity = newScrollSensitivity;\n});\n\ninterface WsProps {\n    endpoint: string;\n    args: string;\n    error: string;\n    initCmd: string;\n}\nconst acceptParams = (props: WsProps) => {\n    nextTick(() => {\n        if (props.error.length !== 0) {\n            initError(props.error);\n        } else {\n            initCmd.value = props.initCmd || '';\n            init(props.endpoint, props.args);\n        }\n    });\n};\n\nconst newTerm = () => {\n    const bg = terminalStore.backgroundColor || '#000000';\n    const fg = terminalStore.foregroundColor || '#f5f5f5';\n    term.value = new Terminal({\n        lineHeight: terminalStore.lineHeight || 1.2,\n        fontSize: terminalStore.fontSize || 12,\n        fontFamily: terminalStore.fontFamily || \"Monaco, Menlo, Consolas, 'Courier New', monospace\",\n        theme: {\n            background: bg,\n            foreground: fg,\n        },\n        cursorBlink: terminalStore.cursorBlink ? String(terminalStore.cursorBlink).toLowerCase() === 'enable' : true,\n        cursorStyle: terminalStore.cursorStyle ? getStyle() : 'underline',\n        scrollback: terminalStore.scrollback || 1000,\n        scrollSensitivity: terminalStore.scrollSensitivity || 15,\n    });\n};\n\nconst applyTerminalBackground = (color: string) => {\n    if (!terminalElement.value) return;\n    terminalElement.value.style.backgroundColor = color || '#000000';\n    terminalElement.value.style.backgroundImage = '';\n    terminalElement.value.style.backgroundSize = '';\n    terminalElement.value.style.backgroundPosition = '';\n    terminalElement.value.style.backgroundRepeat = '';\n    terminalElement.value.style.imageRendering = '';\n};\n\nconst getStyle = (): 'underline' | 'block' | 'bar' => {\n    switch (terminalStore.cursorStyle) {\n        case 'bar':\n            return 'bar';\n        case 'block':\n            return 'block';\n        default:\n            return 'underline';\n    }\n};\n\nconst init = (endpoint: string, args: string) => {\n    if (initTerminal(true)) {\n        initWebSocket(endpoint, args);\n    }\n};\n\nconst initError = (errorInfo: string) => {\n    if (initTerminal(false)) {\n        term.value.write(errorInfo);\n    }\n};\n\nfunction onClose(isKeepShow: boolean = false) {\n    window.removeEventListener('resize', changeTerminalSize);\n    try {\n        terminalSocket.value?.close();\n    } catch {}\n    if (!isKeepShow) {\n        try {\n            term.value.dispose();\n        } catch {}\n    }\n    if (terminalElement.value) {\n        terminalElement.value.innerHTML = '';\n    }\n}\n\n// terminal 相关代码 start\n\nconst initTerminal = (online: boolean = false): boolean => {\n    newTerm();\n    if (terminalElement.value) {\n        term.value.open(terminalElement.value);\n        applyTerminalBackground(terminalStore.backgroundColor);\n        term.value.loadAddon(fitAddon);\n        window.addEventListener('resize', changeTerminalSize);\n        if (online) {\n            term.value.onData((data) => onTermData(data));\n        }\n        termReady.value = true;\n    }\n    return termReady.value;\n};\n\nfunction changeTerminalSize() {\n    if (!terminalElement.value || !term.value) return;\n    if (terminalElement.value.clientWidth <= 0 || terminalElement.value.clientHeight <= 0) {\n        return;\n    }\n\n    fitAddon.fit();\n    if (isWsOpen()) {\n        const { cols, rows } = term.value;\n        terminalSocket.value!.send(\n            JSON.stringify({\n                type: 'resize',\n                cols: cols,\n                rows: rows,\n            }),\n        );\n    }\n}\n\n// terminal 相关代码 end\n\n// websocket 相关代码 start\n\nconst initWebSocket = (endpoint_: string, args: string = '') => {\n    const href = window.location.href;\n    const protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss';\n    const host = href.split('//')[1].split('/')[0];\n    const endpoint = endpoint_.replace(/^\\/+/, '');\n    let conn = `${protocol}://${host}/${endpoint}?cols=${term.value.cols}&rows=${term.value.rows}&${args}&operateNode=${globalStore.currentNode}`;\n    if (args.indexOf('&operateNode=') !== -1) {\n        conn = `${protocol}://${host}/${endpoint}?cols=${term.value.cols}&rows=${term.value.rows}&${args}`;\n    }\n    terminalSocket.value = new WebSocket(conn);\n    terminalSocket.value.onopen = runRealTerminal;\n    terminalSocket.value.onmessage = onWSReceive;\n    terminalSocket.value.onclose = closeRealTerminal;\n    terminalSocket.value.onerror = errorRealTerminal;\n    heartbeatTimer.value = setInterval(() => {\n        if (isWsOpen()) {\n            terminalSocket.value!.send(\n                JSON.stringify({\n                    type: 'heartbeat',\n                    timestamp: `${new Date().getTime()}`,\n                }),\n            );\n        }\n    }, 1000 * 10);\n};\n\nconst runRealTerminal = () => {\n    webSocketReady.value = true;\n    if (initCmd.value !== '') {\n        sendMsg(initCmd.value);\n    }\n};\n\nconst onWSReceive = (message: MessageEvent) => {\n    const wsMsg = JSON.parse(message.data);\n    switch (wsMsg.type) {\n        case 'cmd': {\n            term.value.element && term.value.focus();\n            if (wsMsg.data) {\n                let receiveMsg = Base64.decode(wsMsg.data);\n                if (initCmd.value != '') {\n                    receiveMsg = receiveMsg?.replace(initCmd.value.trim(), '').trim();\n                    initCmd.value = '';\n                }\n                term.value.write(receiveMsg);\n            }\n            break;\n        }\n        case 'heartbeat': {\n            latency.value = new Date().getTime() - wsMsg.timestamp;\n            break;\n        }\n    }\n};\n\nconst errorRealTerminal = (ex: any) => {\n    let message = ex.message;\n    if (!message) message = 'disconnected';\n    term.value.write(`\\x1b[31m${message}\\x1b[m\\r\\n`);\n};\n\nconst closeRealTerminal = (ev: CloseEvent) => {\n    if (heartbeatTimer.value) {\n        clearInterval(Number(heartbeatTimer.value));\n    }\n    term.value?.write('The connection has been disconnected.');\n    term.value?.write(ev.reason);\n};\n\nconst isWsOpen = () => {\n    const readyState = terminalSocket.value && terminalSocket.value.readyState;\n    return readyState === 1;\n};\n\nfunction sendMsg(data: string) {\n    if (isWsOpen()) {\n        terminalSocket.value!.send(\n            JSON.stringify({\n                type: 'cmd',\n                data: Base64.encode(data),\n            }),\n        );\n    }\n}\n\nfunction onTermData(data: string) {\n    if (!data) return;\n    sendMsg(data);\n}\n\n// websocket 相关代码 end\n\nconst resizeObserver = ref<ResizeObserver>();\n\nonMounted(() => {\n    // 使用 ResizeObserver 监听容器大小变化\n    resizeObserver.value = new ResizeObserver(() => {\n        if (termReady.value && webSocketReady.value) {\n            changeTerminalSize();\n        }\n    });\n\n    if (terminalElement.value) {\n        resizeObserver.value.observe(terminalElement.value);\n    }\n});\n\ndefineExpose({\n    acceptParams,\n    onClose,\n    isWsOpen,\n    sendMsg,\n    getLatency: () => latency.value,\n});\n\nonBeforeUnmount(() => {\n    onClose();\n    resizeObserver.value?.disconnect();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.terminal-container {\n    width: 100%;\n    height: 100%;\n}\n:deep(.xterm) {\n    padding: 5px !important;\n    background-color: transparent !important;\n}\n\n:deep(.xterm .xterm-viewport) {\n    background-color: transparent !important;\n    scrollbar-width: thin;\n    scrollbar-color: rgba(255, 255, 255, 0.3) rgba(255, 255, 255, 0.1);\n}\n\n:deep(.xterm .xterm-viewport::-webkit-scrollbar) {\n    width: 10px;\n    height: 10px;\n    background: rgba(255, 255, 255, 0.1);\n}\n\n:deep(.xterm .xterm-viewport::-webkit-scrollbar-thumb) {\n    border-radius: 6px;\n    border: 2px solid transparent;\n    background-clip: content-box;\n    background-color: rgba(255, 255, 255, 0.3);\n}\n\n:deep(.xterm .xterm-viewport::-webkit-scrollbar-thumb:hover) {\n    background-color: rgba(255, 255, 255, 0.45);\n}\n\n:deep(.xterm .xterm-viewport::-webkit-scrollbar-corner) {\n    background: transparent;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/tooltip/index.vue",
    "content": "<template>\n    <div class=\"tooltip-container\">\n        <el-tooltip :disabled=\"showTooltip\">\n            <template #content>\n                <div :style=\"{ width: tootipWidth, 'word-break': 'break-all' }\">{{ text }}</div>\n            </template>\n            <p ref=\"tooltipBox\" class=\"text-box\">\n                <span v-if=\"islink\" ref=\"tooltipItem\" class=\"table-link\">{{ text }}</span>\n                <span v-else ref=\"tooltipItem\" class=\"\">{{ text }}</span>\n            </p>\n        </el-tooltip>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, ref } from 'vue';\n\ndefineOptions({ name: 'Tooltip' });\n\nconst showTooltip = ref();\nconst tooltipBox = ref();\nconst tooltipItem = ref();\n\nconst tootipWidth = ref();\n\ndefineProps({\n    text: {\n        type: String,\n        default: '',\n    },\n    islink: {\n        type: Boolean,\n        default: true,\n    },\n});\n\nonMounted(() => {\n    const boxWidth = tooltipBox.value.offsetWidth;\n    const itemWidth = tooltipItem.value.offsetWidth;\n    tootipWidth.value = itemWidth > 250 ? '250px' : itemWidth + 'px';\n    showTooltip.value = boxWidth > itemWidth;\n});\n</script>\n<style scoped lang=\"scss\">\n.tooltip-container {\n    width: 100%;\n    .text-box {\n        margin: 0;\n        overflow: hidden;\n        white-space: nowrap;\n        text-overflow: ellipsis;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/components/upload/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro\n            v-model=\"uploadOpen\"\n            :header=\"$t('commons.button.import')\"\n            :resource=\"title\"\n            @close=\"handleUploadClose\"\n            size=\"large\"\n        >\n            <template #content>\n                <div v-loading=\"loading\">\n                    <div>\n                        <el-alert :closable=\"false\" type=\"warning\">\n                            <template #default>\n                                <ul>\n                                    <li v-if=\"type === 'mysql' || type === 'mariadb' || type === 'mysql-cluster'\">\n                                        {{ $t('database.formatHelper', [remark]) }}\n                                    </li>\n                                    <li v-if=\"isDb()\">{{ $t('database.supportUpType') }}</li>\n                                    <li v-if=\"type === 'website' || type === 'app'\">\n                                        {{ $t('website.websiteBackupWarn') }}\n                                    </li>\n                                    <li v-if=\"type === 'container'\">{{ $t('container.importContainerBackupTip') }}</li>\n                                    <li v-else-if=\"type === 'compose'\">{{ $t('container.importComposeBackupTip') }}</li>\n                                    <li v-else-if=\"!isDb()\">{{ $t('website.supportUpType', [type]) }}</li>\n                                </ul>\n                            </template>\n                        </el-alert>\n                    </div>\n\n                    <ComplexTable\n                        :pagination-config=\"paginationConfig\"\n                        class=\"mt-5\"\n                        @search=\"search\"\n                        v-model:selects=\"selects\"\n                        :data=\"data\"\n                    >\n                        <template #toolbar>\n                            <el-upload\n                                :limit=\"1\"\n                                class=\"float-left\"\n                                ref=\"uploadRef\"\n                                accept=\".tar.gz,.sql,.gz,.zip\"\n                                :show-file-list=\"false\"\n                                :on-exceed=\"handleExceed\"\n                                :on-change=\"fileOnChange\"\n                                :auto-upload=\"false\"\n                            >\n                                <el-button class=\"float-left\">\n                                    {{ $t('database.localUpload') }}\n                                </el-button>\n                            </el-upload>\n                            <el-button class=\"float-left ml-3\" @click=\"fileRef.acceptParams({ dir: false })\">\n                                {{ $t('database.hostSelect') }}\n                            </el-button>\n                            <el-button :disabled=\"selects.length === 0\" @click=\"onBatchDelete(null)\">\n                                {{ $t('commons.button.delete') }}\n                            </el-button>\n\n                            <el-progress v-if=\"isUpload\" text-inside :stroke-width=\"12\" :percentage=\"uploadPercent\" />\n                        </template>\n                        <el-table-column type=\"selection\" fix />\n                        <el-table-column :label=\"$t('commons.table.name')\" show-overflow-tooltip prop=\"name\" />\n                        <el-table-column :label=\"$t('file.size')\" prop=\"size\">\n                            <template #default=\"{ row }\">\n                                {{ computeSize(row.size) }}\n                            </template>\n                        </el-table-column>\n                        <el-table-column\n                            show-overflow-tooltip\n                            :label=\"$t('commons.table.createdAt')\"\n                            min-width=\"90\"\n                            fix\n                        >\n                            <template #default=\"{ row }\">\n                                {{ row.createdAt }}\n                            </template>\n                        </el-table-column>\n                        <fu-table-operations\n                            width=\"150px\"\n                            :buttons=\"buttons\"\n                            :ellipsis=\"10\"\n                            :label=\"$t('commons.table.operate')\"\n                            fix\n                        />\n                    </ComplexTable>\n                </div>\n            </template>\n        </DrawerPro>\n\n        <DialogPro\n            v-model=\"recoverDialog\"\n            :title=\"name ? $t('commons.button.recover') + ' - ' + name : $t('commons.button.recover')\"\n            @close=\"handleRecoverClose\"\n            size=\"small\"\n        >\n            <el-form ref=\"backupForm\" @submit.prevent label-position=\"top\" v-loading=\"loading\">\n                <el-form-item :label=\"$t('setting.compressPassword')\">\n                    <el-input v-model=\"secret\" :placeholder=\"$t('setting.backupRecoverMessage')\" />\n                </el-form-item>\n                <el-form-item\n                    v-if=\"type !== 'app' && type !== 'website'\"\n                    :label=\"$t('cronjob.timeout')\"\n                    prop=\"timeoutItem\"\n                >\n                    <el-input type=\"number\" class=\"selectClass\" v-model.number=\"timeoutItem\">\n                        <template #append>\n                            <el-select v-model=\"timeoutUnit\" style=\"width: 80px\">\n                                <el-option :label=\"$t('commons.units.second')\" value=\"s\" />\n                                <el-option :label=\"$t('commons.units.minute')\" value=\"m\" />\n                                <el-option :label=\"$t('commons.units.hour')\" value=\"h\" />\n                            </el-select>\n                        </template>\n                    </el-input>\n                    <span class=\"input-help\">{{ $t('database.recoverTimeoutHelper') }}</span>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"handleRecoverClose\" :disabled=\"loading\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button type=\"primary\" @click=\"onHandleRecover\" :disabled=\"loading\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <FileList ref=\"fileRef\" @choose=\"loadFile\" />\n        <TaskLog ref=\"taskLogRef\" @close=\"handleTaskLogClose\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { computeSize, newUUID, transferTimeToSecond } from '@/utils/util';\nimport i18n from '@/lang';\nimport { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile, genFileId } from 'element-plus';\nimport { File } from '@/api/interface/file';\nimport { batchDeleteFile, checkFile, chunkUploadFileData, getUploadList } from '@/api/modules/files';\nimport { loadBaseDir } from '@/api/modules/setting';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { handleRecoverByUpload, uploadByRecover } from '@/api/modules/backup';\nimport TaskLog from '@/components/log/task/index.vue';\n\nconst emit = defineEmits(['close']);\n\ninterface DialogProps {\n    type: string;\n    name: string;\n    detailName: string;\n    remark: string;\n    node?: string;\n}\nconst loading = ref();\nconst fileRef = ref();\nconst isUpload = ref();\nconst uploadPercent = ref<number>(0);\nconst selects = ref<any>([]);\nconst baseDir = ref();\nconst opRef = ref();\nconst currentRow = ref();\nconst data = ref();\nconst title = ref();\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\nconst uploadOpen = ref(false);\nconst type = ref('mysql');\nconst name = ref();\nconst detailName = ref();\nconst remark = ref();\nconst secret = ref();\nconst timeoutItem = ref(30);\nconst timeoutUnit = ref('m');\nconst taskLogRef = ref();\nconst node = ref();\n\nconst recoverDialog = ref();\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    type.value = params.type;\n    name.value = params.name;\n    detailName.value = params.detailName;\n    remark.value = params.remark;\n    node.value = params.node;\n\n    const pathRes = await loadBaseDir(node.value);\n    switch (type.value) {\n        case 'mysql':\n        case 'mariadb':\n        case 'postgresql':\n        case 'mysql-cluster':\n        case 'postgresql-cluster':\n            title.value = name.value + ' [ ' + detailName.value + ' ]';\n            if (detailName.value) {\n                baseDir.value = `${pathRes.data}/uploads/database/${type.value}/${name.value}/${detailName.value}/`;\n            } else {\n                baseDir.value = `${pathRes.data}/uploads/database/${type.value}/${name.value}/`;\n            }\n            break;\n        case 'website':\n            title.value = name.value;\n            baseDir.value = `${pathRes.data}/uploads/website/${type.value}/${detailName.value}/`;\n            break;\n        case 'app':\n            title.value = name.value;\n            baseDir.value = `${pathRes.data}/uploads/app/${type.value}/${name.value}/`;\n            break;\n        case 'container':\n            title.value = name.value || i18n.global.t('menu.container');\n            if (name.value) {\n                baseDir.value = `${pathRes.data}/uploads/container/${name.value}/`;\n            } else {\n                baseDir.value = `${pathRes.data}/uploads/container/import/`;\n            }\n            break;\n        case 'compose':\n            title.value = name.value || i18n.global.t('container.compose');\n            if (name.value) {\n                baseDir.value = `${pathRes.data}/uploads/compose/${name.value}/`;\n            } else {\n                baseDir.value = `${pathRes.data}/uploads/compose/import/`;\n            }\n            break;\n    }\n    uploadOpen.value = true;\n    search();\n};\n\nconst search = async () => {\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        path: baseDir.value,\n    };\n    const res = await getUploadList(params);\n    data.value = res.data.items || [];\n    paginationConfig.total = res.data.total;\n};\n\nconst beforeUpload = (fileName: string) => {\n    const itemName = fileName.toLowerCase();\n    let reg = /^[a-zA-Z0-9\\u4e00-\\u9fa5]{1}[a-z:A-Z0-9_.\\u4e00-\\u9fa5-]{0,256}$/;\n    if (!reg.test(itemName)) {\n        MsgError(i18n.global.t('commons.msg.fileNameErr'));\n        return false;\n    }\n    if (isDb()) {\n        const allowedExtensions = ['.sql', '.sql.gz', '.tar.gz', '.zip'];\n        const isValidFile = allowedExtensions.some((ext) => itemName.endsWith(ext));\n        if (!isValidFile) {\n            MsgError(i18n.global.t('database.supportUpType'));\n            return false;\n        }\n        return true;\n    }\n    const allowedExtensions = ['.tar.gz'];\n    const isValidFile = allowedExtensions.some((ext) => itemName.endsWith(ext));\n    if (!isValidFile) {\n        if (type.value === 'compose') {\n            MsgError(i18n.global.t('container.importComposeBackupTip'));\n            return false;\n        }\n        if (type.value === 'container') {\n            MsgError(i18n.global.t('container.importContainerBackupTip'));\n            return false;\n        }\n        MsgError(i18n.global.t('website.supportUpType'));\n        return false;\n    }\n    return true;\n};\n\nconst loadFile = async (path: string) => {\n    let filaName = path.split('/').pop();\n    if (!filaName) {\n        MsgError(i18n.global.t('commons.msg.fileNameErr'));\n        return;\n    }\n    if (!beforeUpload(filaName)) {\n        return;\n    }\n    ElMessageBox.confirm(i18n.global.t('database.selectHelper', [path]), i18n.global.t('database.loadBackup'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        uploadByRecover(path, baseDir.value, node.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID, true, node.value);\n};\n\nconst onHandleRecover = async () => {\n    let params = {\n        downloadAccountID: 1,\n        type: type.value,\n        name: name.value,\n        detailName: detailName.value,\n        file: baseDir.value + currentRow.value.name,\n        secret: secret.value,\n        taskID: newUUID(),\n        timeout: timeoutItem.value === -1 ? -1 : transferTimeToSecond(timeoutItem.value + timeoutUnit.value),\n    };\n    loading.value = true;\n    await handleRecoverByUpload(params, node.value)\n        .then(() => {\n            loading.value = false;\n            handleUploadClose();\n            handleRecoverClose();\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search();\n            openTaskLog(params.taskID);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onRecover = async (row: File.File) => {\n    currentRow.value = row;\n    secret.value = '';\n    recoverDialog.value = true;\n};\n\nconst isDb = () => {\n    return (\n        type.value === 'mysql' ||\n        type.value === 'mariadb' ||\n        type.value === 'postgresql' ||\n        type.value === 'mysql-cluster' ||\n        type.value === 'postgresql-cluster'\n    );\n};\nconst uploaderFiles = ref<UploadFiles>([]);\nconst uploadRef = ref<UploadInstance>();\n\nconst fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    uploaderFiles.value = uploadFiles;\n    if (uploaderFiles.value.length !== 1) {\n        return;\n    }\n    const file = uploaderFiles.value[0];\n    if (!file.raw.name) {\n        MsgError(i18n.global.t('commons.msg.fileNameErr'));\n        return;\n    }\n    if (!beforeUpload(file.raw.name)) {\n        return;\n    }\n    ElMessageBox.confirm(\n        i18n.global.t('database.selectHelper', [file.raw.name]),\n        i18n.global.t('database.loadBackup'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    ).then(async () => {\n        onSubmit();\n    });\n};\n\nconst handleUploadClose = () => {\n    uploaderFiles.value = [];\n    uploadRef.value!.clearFiles();\n    uploadOpen.value = false;\n    emit('close');\n};\nconst handleTaskLogClose = () => {\n    emit('close');\n};\n\nconst handleRecoverClose = () => {\n    recoverDialog.value = false;\n};\n\nconst handleExceed: UploadProps['onExceed'] = (files) => {\n    uploadRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadRef.value!.handleStart(file);\n};\n\nconst onSubmit = async () => {\n    const file = uploaderFiles.value[0];\n    let reg = /^[a-zA-Z0-9\\u4e00-\\u9fa5]{1}[a-z:A-Z0-9_.\\u4e00-\\u9fa5-]{0,256}$/;\n    if (!reg.test(file.raw.name)) {\n        MsgError(i18n.global.t('commons.msg.fileNameErr'));\n        return;\n    }\n    const res = await checkFile(baseDir.value + file.raw.name, false);\n    if (res.data) {\n        MsgError(i18n.global.t('commons.msg.fileExist'));\n        return;\n    }\n    submitUpload(file);\n};\n\nconst submitUpload = async (file: any) => {\n    isUpload.value = true;\n    const CHUNK_SIZE = 1024 * 1024;\n    const fileSize = file.size;\n    const chunkCount = Math.ceil(fileSize / CHUNK_SIZE);\n    let uploadedChunkCount = 0;\n\n    for (let i = 0; i < chunkCount; i++) {\n        const start = i * CHUNK_SIZE;\n        const end = Math.min(start + CHUNK_SIZE, fileSize);\n        const chunk = file.raw.slice(start, end);\n\n        const formData = new FormData();\n\n        formData.append('filename', file.name);\n        formData.append('path', baseDir.value);\n        formData.append('chunk', chunk);\n        formData.append('chunkIndex', i.toString());\n        formData.append('chunkCount', chunkCount.toString());\n\n        try {\n            await chunkUploadFileData(formData, {\n                onUploadProgress: (progressEvent) => {\n                    const progress = Math.round(\n                        ((uploadedChunkCount + progressEvent.loaded / progressEvent.total) * 100) / chunkCount,\n                    );\n                    uploadPercent.value = progress;\n                },\n            });\n            uploadedChunkCount++;\n        } catch (error) {\n            isUpload.value = false;\n            break;\n        }\n        if (uploadedChunkCount == chunkCount) {\n            isUpload.value = false;\n            uploadRef.value?.clearFiles();\n            uploaderFiles.value = [];\n            MsgSuccess(i18n.global.t('file.uploadSuccess'));\n            search();\n        }\n    }\n};\n\nconst onBatchDelete = async (row: File.File | null) => {\n    let files: Array<string> = [];\n    let names: Array<string> = [];\n    if (row) {\n        files.push(baseDir.value + row.name);\n        names.push(row.name);\n    } else {\n        selects.value.forEach((item: File.File) => {\n            files.push(baseDir.value + item.name);\n            names.push(item.name);\n        });\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('commons.button.import'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: batchDeleteFile,\n        params: { paths: files, isDir: false },\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.recover'),\n        click: (row: File.File) => {\n            onRecover(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: File.File) => {\n            onBatchDelete(row);\n        },\n    },\n];\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/components/v-charts/components/Line.vue",
    "content": "<template>\n    <div :id=\"id\" ref=\"LineChartRef\" :style=\"{ height: height, width: width }\" />\n</template>\n<script lang=\"ts\" setup>\nimport { onMounted, nextTick, watch, onBeforeUnmount, ref } from 'vue';\nimport echarts from '@/utils/echarts';\nimport { GlobalStore } from '@/store';\nimport { computeSizeFromKBs, computeSizeFromKB, computeSizeFromMB } from '@/utils/util';\nimport i18n from '@/lang';\nconst globalStore = GlobalStore();\nconst isDarkTheme = ref(false);\nlet mediaQuery: MediaQueryList;\nconst props = defineProps({\n    id: {\n        type: String,\n        default: 'lineChartId',\n    },\n    width: {\n        type: String,\n        default: '100%',\n    },\n    height: {\n        type: String,\n        default: '200px',\n    },\n    dataZoom: {\n        type: Boolean,\n        default: false,\n    },\n    option: {\n        type: Object,\n        required: true,\n    },\n});\n\nconst seriesStyle = [\n    {\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n            {\n                offset: 0,\n                color: getComputedStyle(document.documentElement)\n                    .getPropertyValue('--panel-color-primary-light-9')\n                    .trim(),\n            },\n            {\n                offset: 1,\n                color: getComputedStyle(document.documentElement).getPropertyValue('--panel-color-primary').trim(),\n            },\n        ]),\n    },\n    {\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n            {\n                offset: 0,\n                color: 'rgba(0, 94, 235, .3)',\n            },\n            {\n                offset: 1,\n                color: 'rgba(0, 94, 235, .4)',\n            },\n        ]),\n    },\n    {\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n            {\n                offset: 0,\n                color: 'rgba(27, 143, 60, .3)',\n            },\n            {\n                offset: 1,\n                color: 'rgba(27, 143, 60, .4)',\n            },\n        ]),\n    },\n    {\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n            {\n                offset: 0,\n                color: 'rgba(249, 199, 79, .3)',\n            },\n            {\n                offset: 1,\n                color: 'rgba(249, 199, 79, .4)',\n            },\n        ]),\n    },\n    {\n        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n            {\n                offset: 0,\n                color: 'rgba(255, 173, 177, 0.3)',\n            },\n            {\n                offset: 1,\n                color: 'rgba(255, 173, 177, .4)',\n            },\n        ]),\n    },\n];\n\nfunction initChart() {\n    if (globalStore.themeConfig.theme === 'auto') {\n        isDarkTheme.value = window.matchMedia('(prefers-color-scheme: dark)').matches;\n    } else {\n        isDarkTheme.value = globalStore.themeConfig.theme === 'dark';\n    }\n    let itemChart = echarts?.getInstanceByDom(document.getElementById(props.id) as HTMLElement);\n    const optionItem = itemChart?.getOption();\n    const itemSelect = optionItem?.legend;\n    if (itemChart == null) {\n        itemChart = echarts.init(document.getElementById(props.id) as HTMLElement);\n    }\n\n    const series = [];\n    if (props.option?.yData?.length) {\n        props.option?.yData.forEach((item: any, index: number) => {\n            series.push({\n                name: item?.name,\n                type: 'line',\n                itemStyle: seriesStyle[index + 2],\n                areaStyle: seriesStyle[index],\n                data: item?.data,\n                showSymbol: false,\n                yAxisIndex: item.yAxisIndex ? 1 : null,\n            });\n        });\n    }\n    const yAxis = [];\n    if (props.option.yAxis && props.option.yAxis.length > 0) {\n        props.option.yAxis.forEach((item: any) => {\n            yAxis.push({\n                splitLine: {\n                    show: true,\n                    lineStyle: {\n                        type: 'dashed',\n                        opacity: isDarkTheme.value ? 0.1 : 1,\n                    },\n                },\n                ...item,\n            });\n        });\n    }\n\n    const option = {\n        title: [\n            {\n                left: 'center',\n                text: props.option.title,\n                show: props.option.title,\n            },\n        ],\n        zlevel: 1,\n        z: 1,\n        tooltip: props.option.tooltip || {\n            trigger: 'axis',\n            formatter: function (datas: any) {\n                let res = datas[0].name + '<br/>';\n                switch (props.option.formatStr) {\n                    case 'KB/s':\n                        for (const item of datas) {\n                            res +=\n                                item.marker +\n                                ' ' +\n                                item.seriesName +\n                                i18n.global.t('commons.colon') +\n                                computeSizeFromKBs(item.data) +\n                                '<br/>';\n                        }\n                        break;\n                    case 'KB':\n                        for (const item of datas) {\n                            res +=\n                                item.marker +\n                                ' ' +\n                                item.seriesName +\n                                i18n.global.t('commons.colon') +\n                                computeSizeFromKB(item.data) +\n                                '<br/>';\n                        }\n                        break;\n                    case 'MB':\n                        for (const item of datas) {\n                            res +=\n                                item.marker +\n                                ' ' +\n                                item.seriesName +\n                                i18n.global.t('commons.colon') +\n                                computeSizeFromMB(item.data) +\n                                '<br/>';\n                        }\n                        break;\n                    default:\n                        for (const item of datas) {\n                            res +=\n                                item.marker +\n                                ' ' +\n                                item.seriesName +\n                                i18n.global.t('commons.colon') +\n                                item.data +\n                                props.option.formatStr +\n                                '<br/>';\n                        }\n                        break;\n                }\n                return res;\n            },\n        },\n        grid: props.option.grid || { left: '7%', right: '7%', bottom: '20%' },\n        legend: itemSelect || {\n            right: 10,\n            itemWidth: 8,\n            textStyle: {\n                color: '#646A73',\n            },\n            icon: 'circle',\n        },\n        xAxis: { data: props.option.xData, boundaryGap: false },\n        yAxis: props.option.yAxis\n            ? yAxis\n            : {\n                  name: '( ' + props.option.formatStr + ' )',\n                  splitLine: {\n                      //分隔辅助线\n                      lineStyle: {\n                          type: 'dashed', //线的类型 虚线0\n                          opacity: isDarkTheme.value ? 0.1 : 1, //透明度\n                      },\n                  },\n              },\n        series: series,\n        dataZoom: [{ startValue: props?.option.xData[0], show: props.dataZoom }],\n    };\n    // 渲染数据\n    itemChart.setOption(option, true);\n}\n\nfunction changeChartSize() {\n    echarts.getInstanceByDom(document.getElementById(props.id) as HTMLElement)?.resize();\n}\n\nwatch(\n    () => props.option,\n    (val) => {\n        if (val) {\n            nextTick(() => {\n                initChart();\n            });\n        }\n    },\n);\n\nfunction handleThemeChange() {\n    nextTick(() => initChart());\n}\n\nonMounted(() => {\n    nextTick(() => {\n        mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n        mediaQuery.addEventListener('change', handleThemeChange);\n        initChart();\n        window.addEventListener('resize', changeChartSize);\n    });\n});\n\nonBeforeUnmount(() => {\n    echarts.getInstanceByDom(document.getElementById(props.id) as HTMLElement).dispose();\n    window.removeEventListener('resize', changeChartSize);\n    mediaQuery.removeEventListener('change', handleThemeChange);\n});\n</script>\n<style lang=\"scss\" scoped></style>\n"
  },
  {
    "path": "frontend/src/components/v-charts/components/Pie.vue",
    "content": "<template>\n    <div :id=\"id\" ref=\"PieChartRef\" :style=\"{ height: height, width: width }\" />\n</template>\n<script lang=\"ts\" setup>\nimport { onMounted, nextTick, watch, onBeforeUnmount, ref } from 'vue';\nimport echarts from '@/utils/echarts';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\nconst isDarkTheme = ref(false);\nlet mediaQuery: MediaQueryList;\n\nconst props = defineProps({\n    id: {\n        type: String,\n        default: 'pieChartId',\n    },\n    width: {\n        type: String,\n        default: '100%',\n    },\n    height: {\n        type: String,\n        default: '200px',\n    },\n    option: {\n        type: Object,\n        required: true,\n    },\n});\nfunction changeChartSize() {\n    echarts.getInstanceByDom(document.getElementById(props.id) as HTMLElement)?.resize();\n}\nfunction getThemeColors() {\n    return {\n        primaryLight2: getComputedStyle(document.documentElement)\n            .getPropertyValue('--panel-color-primary-light-3')\n            .trim(),\n        primaryLight1: getComputedStyle(document.documentElement).getPropertyValue('--panel-color-primary').trim(),\n        pieBgColor: isDarkTheme.value ? '#434552' : '#ffffff',\n        textColor: isDarkTheme.value ? '#ffffff' : '#0f0f0f',\n        subtextColor: isDarkTheme.value ? '#BBBFC4' : '#646A73',\n        shadowColor: isDarkTheme.value ? '#16191D' : 'rgba(0, 94, 235, 0.1)',\n        backgroundStyleColor: isDarkTheme.value ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 94, 235, 0.05)',\n    };\n}\n\nfunction initChart() {\n    if (globalStore.themeConfig.theme === 'auto') {\n        isDarkTheme.value = window.matchMedia('(prefers-color-scheme: dark)').matches;\n    } else {\n        isDarkTheme.value = globalStore.themeConfig.theme === 'dark';\n    }\n    let myChart = echarts?.getInstanceByDom(document.getElementById(props.id) as HTMLElement);\n    if (myChart === null || myChart === undefined) {\n        myChart = echarts.init(document.getElementById(props.id) as HTMLElement);\n    }\n    let percentText = String(props.option.data).split('.');\n    const { primaryLight2, primaryLight1, pieBgColor, textColor, subtextColor, shadowColor, backgroundStyleColor } =\n        getThemeColors();\n    const option = {\n        title: [\n            {\n                text: `{a|${percentText[0]}.}{b|${percentText[1] || 0} %}`,\n                textStyle: {\n                    rich: {\n                        a: {\n                            fontSize: '22',\n                        },\n                        b: {\n                            fontSize: '14',\n                            padding: [5, 0, 0, 0],\n                        },\n                    },\n\n                    color: textColor,\n                    lineHeight: 25,\n                    // fontSize: 20,\n                    fontWeight: 500,\n                },\n                left: '49%',\n                top: '32%',\n                subtext: props.option.title,\n                subtextStyle: {\n                    color: subtextColor,\n                    fontSize: 13,\n                },\n                textAlign: 'center',\n            },\n        ],\n        polar: {\n            radius: ['71%', '80%'],\n            center: ['50%', '50%'],\n        },\n        angleAxis: {\n            max: 100,\n            show: false,\n        },\n        radiusAxis: {\n            type: 'category',\n            show: true,\n            axisLabel: {\n                show: false,\n            },\n            axisLine: {\n                show: false,\n            },\n            axisTick: {\n                show: false,\n            },\n        },\n        series: [\n            {\n                type: 'bar',\n                roundCap: true,\n                barWidth: 30,\n                showBackground: true,\n                coordinateSystem: 'polar',\n                backgroundStyle: {\n                    color: backgroundStyleColor,\n                },\n                color: [\n                    new echarts.graphic.LinearGradient(0, 1, 0, 0, [\n                        {\n                            offset: 0,\n                            color: primaryLight2,\n                        },\n                        {\n                            offset: 1,\n                            color: primaryLight1,\n                        },\n                    ]),\n                ],\n                label: {\n                    show: false,\n                },\n                data: [props.option.data],\n            },\n            {\n                type: 'pie',\n                radius: ['0%', '60%'],\n                center: ['50%', '50%'],\n                label: {\n                    show: false,\n                },\n                color: pieBgColor,\n                data: [\n                    {\n                        value: 0,\n                        itemStyle: {\n                            shadowColor: shadowColor,\n                            shadowBlur: 5,\n                        },\n                    },\n                ],\n            },\n        ],\n    };\n    // 渲染数据\n    myChart.setOption(option, true);\n}\n\nwatch(\n    () => props.option,\n    (val) => {\n        if (val) {\n            nextTick(() => {\n                initChart();\n            });\n        }\n    },\n);\n\nfunction handleThemeChange() {\n    nextTick(() => initChart());\n}\n\nonMounted(() => {\n    nextTick(() => {\n        mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n        mediaQuery.addEventListener('change', handleThemeChange);\n        initChart();\n        window.addEventListener('resize', changeChartSize);\n    });\n});\n\nonBeforeUnmount(() => {\n    echarts.getInstanceByDom(document.getElementById(props.id) as HTMLElement).dispose();\n    window.removeEventListener('resize', changeChartSize);\n    mediaQuery.removeEventListener('change', handleThemeChange);\n});\n</script>\n<style lang=\"scss\" scoped></style>\n"
  },
  {
    "path": "frontend/src/components/v-charts/index.vue",
    "content": "<template>\n    <component :is=\"typeComponentMap[type]\" :height=\"height\" :option=\"option\" :dataZoom=\"dataZoom\" class=\"v-charts\" />\n</template>\n<script lang=\"ts\" setup>\nimport line from './components/Line.vue';\nimport pie from './components/Pie.vue';\ndefineOptions({ name: 'VCharts' });\ndefineProps({\n    type: {\n        type: String,\n        default: 'line',\n    },\n    height: {\n        type: String,\n        default: '200px',\n    },\n    dataZoom: Boolean,\n    option: {\n        type: Object,\n        required: true,\n    },\n});\n\nconst typeComponentMap = { line, pie };\n</script>\n"
  },
  {
    "path": "frontend/src/components/vscode-open/index.vue",
    "content": "<template>\n    <div>\n        <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"small\">\n            <el-row>\n                <el-col :span=\"22\" :offset=\"1\">\n                    <el-alert :closable=\"false\" :title=\"$t('file.vscodeHelper')\" type=\"info\"></el-alert>\n                    <el-form\n                        ref=\"vscodeConnectInfoForm\"\n                        label-position=\"top\"\n                        :model=\"addForm\"\n                        label-width=\"100px\"\n                        class=\"mt-4\"\n                    >\n                        <el-form-item :label=\"$t('setting.systemIP')\" prop=\"host\">\n                            <el-input v-model=\"addForm.host\" />\n                        </el-form-item>\n                        <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\">\n                            <el-input v-model=\"addForm.port\" />\n                        </el-form-item>\n                        <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                            <el-input v-model=\"addForm.username\" />\n                        </el-form-item>\n                    </el-form>\n                </el-col>\n            </el-row>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                    <el-button type=\"primary\" @click=\"submit(vscodeConnectInfoForm)\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DialogPro>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { FormInstance } from 'element-plus';\n\nconst open = ref();\n\ninterface DialogProps {\n    path: string;\n}\nconst vscodeConnectInfoForm = ref<FormInstance>();\n\nconst addForm = reactive({\n    host: '',\n    port: 22,\n    username: 'root',\n    path: '',\n});\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    if (vscodeConnectInfoForm.value) {\n        vscodeConnectInfoForm.value.resetFields();\n    }\n    em('close', false);\n};\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        localStorage.setItem('VscodeConnectInfo', JSON.stringify(addForm));\n        open.value = false;\n        const vscodeUrl = `vscode://vscode-remote/ssh-remote+${addForm.username}@${addForm.host}:${addForm.port}${addForm.path}?windowId=_blank`;\n        window.open(vscodeUrl);\n    });\n};\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    const vscodeConnectInfo = localStorage.getItem('VscodeConnectInfo');\n\n    if (vscodeConnectInfo) {\n        try {\n            const storedInfo = JSON.parse(vscodeConnectInfo);\n            addForm.host = storedInfo.host;\n            addForm.port = storedInfo.port;\n            addForm.username = storedInfo.username;\n        } catch (error) {}\n    }\n\n    addForm.path = params.path;\n    open.value = true;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/composables/useGlobalStore.ts",
    "content": "import { GlobalStore } from '@/store';\nimport { storeToRefs } from 'pinia';\n\nexport const useGlobalStore = () => {\n    const globalStore = GlobalStore();\n    const storeRefs = storeToRefs(globalStore);\n\n    return {\n        globalStore,\n        ...storeRefs,\n    };\n};\n"
  },
  {
    "path": "frontend/src/config/nprogress.ts",
    "content": "import NProgress from 'nprogress';\nimport 'nprogress/nprogress.css';\n\nNProgress.configure({\n    easing: 'ease',\n    speed: 300,\n    showSpinner: true,\n    trickleSpeed: 200,\n    minimum: 0.3,\n});\nexport default NProgress;\n"
  },
  {
    "path": "frontend/src/config/pinia-persist.ts",
    "content": "import { PersistedStateOptions } from 'pinia-plugin-persistedstate';\n\nconst piniaPersistConfig = (key: string) => {\n    const persist: PersistedStateOptions = {\n        key,\n        storage: window.localStorage,\n    };\n    return persist;\n};\n\nexport default piniaPersistConfig;\n"
  },
  {
    "path": "frontend/src/directives/index.ts",
    "content": "import { App, Directive } from 'vue';\nimport integerInput from './modules/integer';\n\nconst directivesList: { [key: string]: Directive } = {\n    'integer-input': integerInput,\n};\n\nconst directives = {\n    install: function (app: App<Element>) {\n        Object.keys(directivesList).forEach((key) => {\n            app.directive(key, directivesList[key]);\n        });\n    },\n};\n\nexport default directives;\n"
  },
  {
    "path": "frontend/src/directives/modules/integer.ts",
    "content": "import type { Directive, DirectiveBinding } from 'vue';\n\nconst integerInput: Directive = {\n    mounted(el: HTMLElement, binding: DirectiveBinding) {\n        const { value } = binding;\n        el.addEventListener('input', (event: Event) => {\n            const inputElement = event.target as HTMLInputElement;\n            let inputValue = inputElement.value;\n            inputValue = inputValue.replace(/\\..*/, '');\n            if (value?.min !== undefined && Number(inputValue) < value.min) {\n                inputValue = value.min.toString();\n            }\n            if (value?.max !== undefined && Number(inputValue) > value.max) {\n                inputValue = value.max.toString();\n            }\n            inputElement.value = inputValue;\n            const inputEvent = new Event('input', { bubbles: true });\n            inputElement.dispatchEvent(inputEvent);\n        });\n    },\n};\n\nexport default integerInput;\n"
  },
  {
    "path": "frontend/src/enums/app.ts",
    "content": "export enum DeviceType {\n    Mobile,\n    Desktop,\n}\n"
  },
  {
    "path": "frontend/src/enums/files.ts",
    "content": "export enum CompressType {\n    Zip = 'zip',\n    Gz = 'gz',\n    Bz2 = 'bz2',\n    TarBz2 = 'tar.bz2',\n    Tar = 'tar',\n    TGz = 'tgz',\n    TarGz = 'tar.gz',\n    Xz = 'xz',\n    TarXz = 'tar.xz',\n    Rar = 'rar',\n    '7z' = '7z',\n}\n\nexport enum CompressExtension {\n    zip = '.zip',\n    gz = '.gz',\n    xz = '.xz',\n    'tar.bz2' = '.tar.bz2',\n    bz2 = '.bz2',\n    tar = '.tar',\n    tgz = '.tgz',\n    'tar.gz' = '.tar.gz',\n    'tar.xz' = '.tar.xz',\n    rar = '.rar',\n    '7z' = '.7z',\n}\n\nexport const MimetypeByExtensionObject: Record<string, string> = {\n    '.zip': 'application/zip',\n    '.tar': 'application/x-tar',\n    '.tar.bz2': 'application/x-bzip2',\n    '.bz2': 'application/x-bzip2',\n    '.tar.gz': 'application/gzip',\n    '.tgz': 'application/x-gzip',\n    '.gz': 'application/gzip',\n    '.tar.xz': 'application/x-xz',\n    '.xz': 'application/x-xz',\n    '.rar': 'application/x-rar-compressed',\n    '.7z': 'application/x-7z-compressed',\n};\n"
  },
  {
    "path": "frontend/src/enums/http-enum.ts",
    "content": "export enum ResultEnum {\n    SUCCESS = 200,\n    ERRIP = 310,\n    ERRDOMAIN = 311,\n    UNSAFETY = 312,\n    EXPIRED = 313,\n\n    ERROR = 500,\n    OVERDUE = 401,\n    FORBIDDEN = 403,\n    NOTFOUND = 404,\n    ERRAUTH = 406,\n    ERRGLOBALLOADDING = 407,\n    ERRXPACK = 410,\n    NodeUnBind = 411,\n    TIMEOUT = 20000,\n    TYPE = 'success',\n}\n\nexport enum TimeoutEnum {\n    T_40S = 40000,\n    T_60S = 60000,\n    T_3M = 180000,\n    T_5M = 300000,\n    T_10M = 600000,\n}\n"
  },
  {
    "path": "frontend/src/env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n\ndeclare module '*.vue' {\n    import type { DefineComponent } from 'vue';\n    const component: DefineComponent<{}, {}, any>;\n    export default component;\n}\n"
  },
  {
    "path": "frontend/src/global/bus.ts",
    "content": "import Bus from '@/utils/bus';\n\nexport default new Bus();\n"
  },
  {
    "path": "frontend/src/global/form-rules.ts",
    "content": "import i18n from '@/lang';\nimport { FormItemRule } from 'element-plus';\n\nconst checkNoSpace = (rule: any, value: any, callback: any) => {\n    if (value.indexOf(' ') !== -1) {\n        return callback(new Error(i18n.global.t('setting.noSpace')));\n    }\n    callback();\n};\n\nconst checkIp = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n    } else {\n        const reg =\n            /^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.ip')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkIpv4 = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback();\n    } else {\n        const reg =\n            /^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.ip')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkIpV6 = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n    } else {\n        const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';\n        const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`;\n        const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';\n        const IPv6AddressRegExp = new RegExp(\n            '^(' +\n                `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` +\n                `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` +\n                `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` +\n                `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` +\n                `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` +\n                `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` +\n                `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` +\n                `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` +\n                ')(%[0-9a-zA-Z-.:]{1,})?$',\n        );\n        if (!IPv6AddressRegExp.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.ip')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkIpV4V6OrDomain = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n    } else {\n        const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';\n        const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`;\n        const IPv4AddressRegExp = new RegExp(`^${IPv4AddressFormat}$`);\n        const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';\n        const IPv6AddressRegExp = new RegExp(\n            '^(' +\n                `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` +\n                `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` +\n                `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` +\n                `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` +\n                `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` +\n                `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` +\n                `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` +\n                `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` +\n                ')(%[0-9a-zA-Z-.:]{1,})?$',\n        );\n        const regHost = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;\n        if (!regHost.test(value) && !IPv4AddressRegExp.test(value) && !IPv6AddressRegExp.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.ip')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkDomainOrIP = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback();\n    } else {\n        const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;\n        const ipv6Regex =\n            /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;\n        const domainRegex = /^(?:\\*\\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i;\n\n        if (ipv4Regex.test(value) || ipv6Regex.test(value) || domainRegex.test(value)) {\n            callback();\n        } else {\n            callback(new Error(i18n.global.t('commons.rule.domain')));\n        }\n    }\n};\n\nconst checkHost = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n    } else {\n        const regIP =\n            /^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$/;\n        const regHost = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;\n        if (!regIP.test(value) && !regHost.test(value)) {\n            callback(new Error(i18n.global.t('commons.rule.host')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkIllegal = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n        return;\n    }\n    if (\n        value.indexOf('&') !== -1 ||\n        value.indexOf('|') !== -1 ||\n        value.indexOf(';') !== -1 ||\n        value.indexOf('$') !== -1 ||\n        value.indexOf(\"'\") !== -1 ||\n        value.indexOf('`') !== -1 ||\n        value.indexOf('(') !== -1 ||\n        value.indexOf(')') !== -1 ||\n        value.indexOf('>') !== -1 ||\n        value.indexOf('<') !== -1\n    ) {\n        callback(new Error(i18n.global.t('commons.rule.illegalInput')));\n    } else {\n        callback();\n    }\n};\n\nconst complexityPassword = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.complexityPassword')));\n    } else {\n        const reg = /^(?![\\d]+$)(?![a-zA-Z]+$)(?![^\\da-zA-Z]+$).{8,30}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.complexityPassword')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.commonName')));\n    } else {\n        const reg = /^[a-zA-Z0-9\\u4e00-\\u9fa5]{1}[a-zA-Z0-9_.\\u4e00-\\u9fa5-]{0,127}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.commonName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkUserName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.userName')));\n    } else {\n        const reg = /^[a-zA-Z0-9_\\u4e00-\\u9fa5]{3,30}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.userName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkSimpleName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.simpleName')));\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9_]{2,29}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.simpleName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkSimplePassword = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.simplePassword')));\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9_]{0,29}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.simplePassword')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkAuthBasicPassword = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.authBasicPassword')));\n    } else {\n        const reg = /^[a-zA-Z0-9_\\-\\.@$!%*?&]{1,72}$/;\n        if (!reg.test(value)) {\n            callback(new Error(i18n.global.t('commons.rule.authBasicPassword')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkDBName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.dbName')));\n    } else {\n        const reg = /^[a-zA-Z0-9\\u4e00-\\u9fa5]{1}[a-zA-Z0-9_.\\u4e00-\\u9fa5-]{0,63}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.dbName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkComposeName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.composeName')));\n    } else {\n        const reg = /^[a-z0-9]{1}[a-z0-9_-]{0,256}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.composeName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkImageName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.imageName')));\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-z:@A-Z0-9_/.-]{0,255}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.imageName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkVolumeName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.volumeName')));\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9_.-]{1,30}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.volumeName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkLinuxName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.linuxName', ['/\\\\:*?\\'\"()<>|'])));\n    } else {\n        const reg = /^[^/\\\\\\\"'|<>()?*]{1,128}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.linuxName', ['/\\\\:*?\\'\"()<>|'])));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkSupervisorName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.supervisorName')));\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,127}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.supervisorName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkDatabaseName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.databaseName')));\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9_]{0,30}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.databaseName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkAppName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.appName')));\n    } else {\n        const reg = /^(?![_-])[a-zA-Z0-9_-]{1,29}[a-zA-Z0-9]$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.appName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkAlias = (rule: any, value: any, callback: any) => {\n    if (!value) {\n        callback(new Error(i18n.global.t('commons.rule.alias')));\n        return;\n    }\n    const reg = /^(?![-_])[A-Za-z0-9._-]{1,128}(?<![-_])$/;\n    if (!reg.test(value)) {\n        callback(new Error(i18n.global.t('commons.rule.alias')));\n    } else {\n        callback();\n    }\n};\n\nconst checkDomain = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.domain')));\n    } else {\n        const reg =\n            /^([\\w\\u4e00-\\u9fa5\\-\\*]{1,100}\\.){1,10}([\\w\\u4e00-\\u9fa5\\-]{1,24}|[\\w\\u4e00-\\u9fa5\\-]{1,24}\\.[\\w\\u4e00-\\u9fa5\\-]{1,24})$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.domain')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkDomainWithPort = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.domain')));\n    } else {\n        const reg =\n            /^([\\w\\u4e00-\\u9fa5\\-\\*]{1,100}\\.){1,10}([\\w\\u4e00-\\u9fa5\\-]{1,24}|[\\w\\u4e00-\\u9fa5\\-]{1,24}\\.[\\w\\u4e00-\\u9fa5\\-]{1,24})(:\\d{1,5})?$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.domain')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkIntegerNumber = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.integer')));\n    } else {\n        const reg = /^[1-9]\\d*$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.integer')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkIntegerNumberWith0 = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.integer')));\n    } else {\n        const reg = /^[0-9]*$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.integer')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkFloatNumber = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.integer')));\n    } else {\n        const reg = /^\\d+(\\.\\d+)?$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.number')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkParamCommon = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.paramName')));\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9._-]{1,63}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.paramName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkParamComplexity = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.paramComplexity', ['.%@!~_-'])));\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9.%@!~_-]{4,126}[a-zA-Z0-9]{1}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.paramComplexity', ['.%@!~_-'])));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkParamUrlAndPort = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.paramUrlAndPort')));\n    } else {\n        const reg =\n            /^(https?:\\/\\/)?((localhost)|([a-zA-Z0-9_-]+\\.)*[a-zA-Z0-9_-]+)(:[1-9]\\d{0,3}|:[1-5]\\d{4}|:6[0-4]\\d{3}|:65[0-4]\\d{2}|:655[0-2]\\d|:6553[0-5])?(\\/\\S*)?$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.paramUrlAndPort')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkPort = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.port')));\n    } else {\n        const reg = /^([1-9](\\d{0,3}))$|^([1-5]\\d{4})$|^(6[0-4]\\d{3})$|^(65[0-4]\\d{2})$|^(655[0-2]\\d)$|^(6553[0-5])$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.port')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkDoc = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.nginxDoc')));\n    } else {\n        const reg = /^[A-Za-z0-9\\n./]+$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.nginxDoc')));\n        } else {\n            callback();\n        }\n    }\n};\n\nexport function checkNumberRange(min: number, max: number): FormItemRule {\n    return {\n        required: false,\n        trigger: 'blur',\n        min: min,\n        max: max,\n        type: 'number',\n        message: i18n.global.t('commons.rule.numberRange', [min, max]),\n    };\n}\n\nexport function checkFloatNumberRange(min: number, max: number): FormItemRule {\n    let validatorFunc = function (rule: any, value: any, callback: any) {\n        if (value === '' || typeof value === 'undefined' || value == null) {\n            callback(new Error(i18n.global.t('commons.rule.disableFunction')));\n        } else {\n            if ((Number(value) < min || Number(value) > max) && value !== '') {\n                callback(new Error(i18n.global.t('commons.rule.disableFunction')));\n            } else {\n                callback();\n            }\n        }\n    };\n    return {\n        required: false,\n        trigger: 'blur',\n        validator: validatorFunc,\n        message: i18n.global.t('commons.rule.numberRange', [min, max]),\n    };\n}\n\nconst checkContainerName = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback();\n    } else {\n        const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9_.-]{1,127}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.containerName')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkDisableFunctions = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.disableFunction')));\n    } else {\n        const reg = /^[a-zA-Z_,]+$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.disableFunction')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkLeechExts = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.leechExts')));\n    } else {\n        const reg = /^[a-zA-Z0-9,]+$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.leechExts')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkParamSimple = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback();\n    } else {\n        const reg = /^[a-z0-9][a-z0-9]{1,128}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.paramSimple')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkFilePermission = (rule, value, callback) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.filePermission')));\n    } else {\n        const regFilePermission = /^[0-7]{3,4}$/;\n        if (!regFilePermission.test(value)) {\n            callback(new Error(i18n.global.t('commons.rule.filePermission')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkPHPExtensions = (rule, value, callback) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.phpExtension')));\n    } else {\n        const reg = /^[a-z0-9,_]+$/;\n        if (!reg.test(value)) {\n            callback(new Error(i18n.global.t('commons.rule.phpExtension')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkHttpOrHttps = (rule, value, callback) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback(new Error(i18n.global.t('commons.rule.paramHttp')));\n    } else {\n        const regHttpHttps = /^(http|https):\\/\\//;\n        if (!regHttpHttps.test(value)) {\n            callback(new Error(i18n.global.t('commons.rule.paramHttp')));\n        } else {\n            callback();\n        }\n    }\n};\n\nconst checkPhone = (rule: any, value: any, callback: any) => {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        callback();\n    } else {\n        const reg = /^(?:(?:\\+|00)86)?1[3-9]\\d{9}$/;\n        if (!reg.test(value) && value !== '') {\n            callback(new Error(i18n.global.t('commons.rule.phone')));\n        } else {\n            callback();\n        }\n    }\n};\n\nexport function checkMaxLength(maxLength: number): FormItemRule {\n    return {\n        required: false,\n        trigger: 'blur',\n        type: 'string',\n        validator: (rule: any, value: any, callback: any) => {\n            if (value === '' || typeof value === 'undefined' || value == null) {\n                callback();\n            } else {\n                if (value.length > maxLength) {\n                    callback(new Error(i18n.global.t('commons.rule.maxLength', [maxLength])));\n                } else {\n                    callback();\n                }\n            }\n        },\n    };\n}\n\nconst checkIpv4orV6 = (rule: any, value: any, callback: any) => {\n    if (value === '' || value == null) {\n        return callback();\n    }\n\n    const ipv4 = /^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}$/;\n    const ipv6 = /^((?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}|(?:[A-Fa-f0-9]{1,4}:){1,7}:|:(?::[A-Fa-f0-9]{1,4}){1,7})$/;\n\n    if (!ipv4.test(value) && !ipv6.test(value)) {\n        return callback(new Error(i18n.global.t('commons.rule.ip')));\n    }\n    callback();\n};\n\ninterface CommonRule {\n    requiredInput: FormItemRule;\n    requiredSelect: FormItemRule;\n    requiredSelectBusiness: FormItemRule;\n    noSpace: FormItemRule;\n    name: FormItemRule;\n    userName: FormItemRule;\n    simpleName: FormItemRule;\n    simplePassword: FormItemRule;\n    dbName: FormItemRule;\n    imageName: FormItemRule;\n    composeName: FormItemRule;\n    volumeName: FormItemRule;\n    linuxName: FormItemRule;\n    password: FormItemRule;\n    email: FormItemRule;\n    number: FormItemRule;\n    integerNumber: FormItemRule;\n    integerNumberWith0: FormItemRule;\n    floatNumber: FormItemRule;\n    ip: FormItemRule;\n    ipV6: FormItemRule;\n    ipv4: FormItemRule;\n    ipV4V6OrDomain: FormItemRule;\n    host: FormItemRule;\n    illegal: FormItemRule;\n    port: FormItemRule;\n    domain: FormItemRule;\n    databaseName: FormItemRule;\n    nginxDoc: FormItemRule;\n    appName: FormItemRule;\n    containerName: FormItemRule;\n    disabledFunctions: FormItemRule;\n    leechExts: FormItemRule;\n    domainWithPort: FormItemRule;\n    filePermission: FormItemRule;\n    phpExtensions: FormItemRule;\n    supervisorName: FormItemRule;\n    domainOrIP: FormItemRule;\n    authBasicPassword: FormItemRule;\n    ipv4orV6: FormItemRule;\n    alias: FormItemRule;\n\n    paramCommon: FormItemRule;\n    paramComplexity: FormItemRule;\n    paramPort: FormItemRule;\n    paramExtUrl: FormItemRule;\n    paramSimple: FormItemRule;\n    paramHttp: FormItemRule;\n    phone: FormItemRule;\n}\n\nexport const Rules: CommonRule = {\n    requiredInput: {\n        required: true,\n        message: i18n.global.t('commons.rule.requiredInput'),\n        trigger: 'blur',\n    },\n    requiredSelect: {\n        required: true,\n        message: i18n.global.t('commons.rule.requiredSelect'),\n        trigger: 'change',\n    },\n    requiredSelectBusiness: {\n        required: true,\n        min: 1,\n        max: 65535,\n        type: 'number',\n        message: i18n.global.t('commons.rule.requiredSelect'),\n        trigger: 'change',\n    },\n    noSpace: {\n        required: true,\n        validator: checkNoSpace,\n        trigger: 'blur',\n    },\n    simpleName: {\n        required: true,\n        validator: checkSimpleName,\n        trigger: 'blur',\n    },\n    simplePassword: {\n        required: true,\n        validator: checkSimplePassword,\n        trigger: 'blur',\n    },\n    dbName: {\n        required: true,\n        validator: checkDBName,\n        trigger: 'blur',\n    },\n    composeName: {\n        required: true,\n        validator: checkComposeName,\n        trigger: 'blur',\n    },\n    imageName: {\n        required: true,\n        validator: checkImageName,\n        trigger: 'blur',\n    },\n    volumeName: {\n        required: true,\n        validator: checkVolumeName,\n        trigger: 'blur',\n    },\n    name: {\n        required: true,\n        validator: checkName,\n        trigger: 'blur',\n    },\n    userName: {\n        required: true,\n        validator: checkUserName,\n        trigger: 'blur',\n    },\n    linuxName: {\n        required: true,\n        validator: checkLinuxName,\n        trigger: 'blur',\n    },\n    databaseName: {\n        required: true,\n        validator: checkDatabaseName,\n        trigger: 'blur',\n    },\n    password: {\n        validator: complexityPassword,\n        trigger: 'blur',\n    },\n    email: {\n        required: true,\n        type: 'email',\n        message: i18n.global.t('commons.rule.email'),\n        trigger: 'blur',\n    },\n    number: {\n        required: true,\n        trigger: 'blur',\n        min: 0,\n        type: 'number',\n        message: i18n.global.t('commons.rule.number'),\n    },\n    integerNumber: {\n        required: true,\n        validator: checkIntegerNumber,\n        trigger: 'blur',\n    },\n    integerNumberWith0: {\n        required: true,\n        validator: checkIntegerNumberWith0,\n        trigger: 'blur',\n    },\n    floatNumber: {\n        required: true,\n        validator: checkFloatNumber,\n        trigger: 'blur',\n        min: 0,\n        message: i18n.global.t('commons.rule.number'),\n    },\n    ip: {\n        validator: checkIp,\n        required: true,\n        trigger: 'blur',\n    },\n    ipV6: {\n        validator: checkIpV6,\n        required: true,\n        trigger: 'blur',\n    },\n    ipV4V6OrDomain: {\n        validator: checkIpV4V6OrDomain,\n        required: true,\n        trigger: 'blur',\n    },\n    host: {\n        validator: checkHost,\n        required: true,\n        trigger: 'blur',\n    },\n    illegal: {\n        validator: checkIllegal,\n        required: true,\n        trigger: 'blur',\n    },\n    port: {\n        required: true,\n        trigger: 'blur',\n        min: 1,\n        max: 65535,\n        type: 'number',\n        message: i18n.global.t('commons.rule.port'),\n    },\n    domain: {\n        required: true,\n        validator: checkDomain,\n        trigger: 'blur',\n    },\n    paramCommon: {\n        required: true,\n        validator: checkParamCommon,\n        trigger: 'blur',\n    },\n    paramComplexity: {\n        required: true,\n        validator: checkParamComplexity,\n        trigger: 'blur',\n    },\n    paramPort: {\n        required: true,\n        trigger: 'blur',\n        validator: checkPort,\n    },\n    paramExtUrl: {\n        required: true,\n        validator: checkParamUrlAndPort,\n        trigger: 'blur',\n    },\n    nginxDoc: {\n        required: true,\n        validator: checkDoc,\n        trigger: 'blur',\n    },\n    appName: {\n        required: true,\n        trigger: 'blur',\n        validator: checkAppName,\n    },\n    containerName: {\n        required: false,\n        trigger: 'blur',\n        validator: checkContainerName,\n    },\n    disabledFunctions: {\n        required: true,\n        trigger: 'blur',\n        validator: checkDisableFunctions,\n    },\n    leechExts: {\n        required: true,\n        trigger: 'blur',\n        validator: checkLeechExts,\n    },\n    supervisorName: {\n        required: true,\n        trigger: 'blur',\n        validator: checkSupervisorName,\n    },\n    paramSimple: {\n        required: true,\n        trigger: 'blur',\n        validator: checkParamSimple,\n    },\n    domainWithPort: {\n        required: true,\n        validator: checkDomainWithPort,\n        trigger: 'blur',\n    },\n    filePermission: {\n        required: true,\n        validator: checkFilePermission,\n        trigger: 'blur',\n    },\n    phpExtensions: {\n        required: true,\n        validator: checkPHPExtensions,\n        trigger: 'blur',\n    },\n    paramHttp: {\n        required: true,\n        validator: checkHttpOrHttps,\n        trigger: 'blur',\n    },\n    ipv4: {\n        validator: checkIpv4,\n        trigger: 'blur',\n    },\n    domainOrIP: {\n        validator: checkDomainOrIP,\n        trigger: 'blur',\n    },\n    phone: {\n        validator: checkPhone,\n        trigger: 'blur',\n    },\n    authBasicPassword: {\n        validator: checkAuthBasicPassword,\n        trigger: 'blur',\n    },\n    ipv4orV6: {\n        validator: checkIpv4orV6,\n        trigger: 'blur',\n    },\n    alias: {\n        required: true,\n        validator: checkAlias,\n        trigger: 'blur',\n    },\n};\n"
  },
  {
    "path": "frontend/src/global/mimetype.ts",
    "content": "import { CompressType } from '@/enums/files';\nimport i18n from '@/lang';\n\nexport const Mimetypes = new Map([\n    ['application/zip', CompressType.Zip],\n    ['application/x-zip', CompressType.Zip],\n    ['application/x-zip-compressed', CompressType.Zip],\n    ['application/x-tar', CompressType.Tar],\n    ['application/x-bzip2', CompressType.Bz2],\n    ['application/gzip', CompressType.TarGz],\n    ['application/x-gzip', CompressType.TarGz],\n    ['application/x-gunzip', CompressType.TarGz],\n    ['application/gzipped', CompressType.TarGz],\n    ['application/gzip-compressed', CompressType.TarGz],\n    ['application/x-gzip-compressed', CompressType.TarGz],\n    ['gzip/document', CompressType.TarGz],\n    ['application/x-xz', CompressType.Xz],\n    ['application/octet-stream', CompressType.Tar],\n    ['application/x-rar-compressed', CompressType.Rar],\n    ['application/vnd.rar', CompressType.Rar],\n    ['application/rar', CompressType.Rar],\n    ['application/x-7z-compressed', CompressType['7z']],\n]);\n\nexport const Languages = [\n    {\n        label: 'plaintext',\n        value: ['txt'],\n    },\n    {\n        label: 'json',\n        value: ['json'],\n    },\n    {\n        label: 'vue',\n        value: ['vue'],\n    },\n    {\n        label: 'typescript',\n        value: ['ts'],\n    },\n    {\n        label: 'lua',\n        value: ['lua'],\n    },\n    {\n        label: 'markdown',\n        value: ['md'],\n    },\n    {\n        label: 'yaml',\n        value: ['yml', 'yaml'],\n    },\n    {\n        label: 'xml',\n        value: ['xml'],\n    },\n    {\n        label: 'php',\n        value: ['php'],\n    },\n    {\n        label: 'sql',\n        value: ['sql'],\n    },\n    {\n        label: 'go',\n        value: ['go'],\n    },\n    {\n        label: 'html',\n        value: ['html'],\n    },\n    {\n        label: 'javascript',\n        value: ['js'],\n    },\n    {\n        label: 'java',\n        value: ['java'],\n    },\n    {\n        label: 'kotlin',\n        value: ['kt'],\n    },\n    {\n        label: 'python',\n        value: ['py'],\n    },\n    {\n        label: 'redis',\n        value: ['redis'],\n    },\n    {\n        label: 'shell',\n        value: ['sh'],\n    },\n    {\n        label: 'css',\n        value: ['css'],\n    },\n    {\n        label: 'ini',\n        value: ['ini'],\n    },\n];\n\nexport const Rewrites = [\n    'default',\n    'wordpress',\n    'wp2',\n    'typecho',\n    'typecho2',\n    'thinkphp',\n    'yii2',\n    'laravel5',\n    'discuz',\n    'discuzx',\n    'discuzx2',\n    'discuzx3',\n    'EduSoho',\n    'EmpireCMS',\n    'ShopWind',\n    'crmeb',\n    'dabr',\n    'dbshop',\n    'dedecms',\n    'drupal',\n    'ecshop',\n    'emlog',\n    'maccms',\n    'mvc',\n    'niushop',\n    'phpcms',\n    'sablog',\n    'seacms',\n    'shopex',\n    'zblog',\n];\n\nexport const Units = [\n    { label: i18n.global.t('commons.units.second'), value: 's' },\n    { label: i18n.global.t('commons.units.minute'), value: 'm' },\n    { label: i18n.global.t('commons.units.hour'), value: 'h' },\n    { label: i18n.global.t('commons.units.day'), value: 'd' },\n    { label: i18n.global.t('commons.units.week'), value: 'w' },\n    { label: i18n.global.t('commons.units.month'), value: 'M' },\n    { label: i18n.global.t('commons.units.year'), value: 'y' },\n];\n\nexport const sizeUnits = [\n    { label: 'B', value: 'b' },\n    { label: 'KB', value: 'k' },\n    { label: 'MB', value: 'm' },\n    { label: 'GB', value: 'g' },\n];\n\nexport const AcmeAccountTypes = [\n    { label: \"Let's Encrypt\", value: 'letsencrypt' },\n    { label: 'ZeroSSL', value: 'zerossl' },\n    { label: 'Buypass', value: 'buypass' },\n    { label: 'Google Cloud', value: 'google' },\n    { label: i18n.global.t('ssl.customAcme'), value: 'custom' },\n];\n\nexport const KeyTypes = [\n    { label: 'EC 256', value: 'P256' },\n    { label: 'EC 384', value: 'P384' },\n    { label: 'RSA 2048', value: '2048' },\n    { label: 'RSA 3072', value: '3072' },\n    { label: 'RSA 4096', value: '4096' },\n];\n\nexport const DNSTypes = [\n    {\n        label: i18n.global.t('website.aliyun'),\n        value: 'AliYun',\n    },\n    {\n        label: i18n.global.t('website.aliEsa'),\n        value: 'AliESA',\n    },\n    {\n        label: i18n.global.t('website.awsRoute53'),\n        value: 'AWSRoute53',\n    },\n    {\n        label: i18n.global.t('website.tencentCloud'),\n        value: 'TencentCloud',\n    },\n    {\n        label: i18n.global.t('website.huaweicloud'),\n        value: 'HuaweiCloud',\n    },\n    {\n        label: 'GoDaddy',\n        value: 'Godaddy',\n    },\n    {\n        label: 'Cloudflare',\n        value: 'CloudFlare',\n    },\n    {\n        label: 'Vercel',\n        value: 'Vercel',\n    },\n    {\n        label: 'CloudDNS',\n        value: 'CloudDns',\n    },\n    {\n        label: 'NameSilo',\n        value: 'NameSilo',\n    },\n    {\n        label: 'NameCheap',\n        value: 'NameCheap',\n    },\n    {\n        label: 'Name.com',\n        value: 'NameCom',\n    },\n    {\n        label: 'Dynu',\n        value: 'Dynu',\n    },\n    {\n        label: 'reg.ru',\n        value: 'RegRu',\n    },\n    {\n        label: 'FreeMyIP',\n        value: 'FreeMyIP',\n    },\n    {\n        label: i18n.global.t('ssl.baiduCloud'),\n        value: 'BaiduCloud',\n    },\n    {\n        label: i18n.global.t('website.rainyun'),\n        value: 'RainYun',\n    },\n    {\n        label: i18n.global.t('website.westCN'),\n        value: 'WestCN',\n    },\n    {\n        label: 'ClouDNS',\n        value: 'ClouDNS',\n    },\n    {\n        label: 'Spaceship',\n        value: 'Spaceship',\n    },\n    {\n        label: 'OVH',\n        value: 'Ovh',\n    },\n    {\n        label: 'Acme DNS',\n        value: 'AcmeDNS',\n    },\n    {\n        label: i18n.global.t('website.volcengine'),\n        value: 'Volcengine',\n    },\n    {\n        label: 'PorkBun',\n        value: 'PorkBun',\n    },\n    {\n        label: 'DNSPod (' + i18n.global.t('ssl.deprecated') + ')',\n        value: 'DnsPod',\n    },\n    {\n        label: 'Technitium',\n        value: 'Technitium',\n    },\n];\n\nexport const Fields = [\n    {\n        label: 'URL',\n        value: 'URL',\n    },\n    {\n        label: 'IP',\n        value: 'IP',\n    },\n    {\n        label: 'Header',\n        value: 'Header',\n    },\n    {\n        label: 'Host',\n        value: 'Host',\n    },\n    {\n        label: i18n.global.t('xpack.waf.method'),\n        value: 'Method',\n    },\n];\n\nexport const Patterns = [\n    {\n        label: i18n.global.t('xpack.waf.contain'),\n        value: 'contain',\n        hidden: ['Method'],\n    },\n    {\n        label: i18n.global.t('xpack.waf.notContain'),\n        value: 'notContain',\n        hidden: ['Method'],\n    },\n    {\n        label: i18n.global.t('xpack.waf.equal'),\n        value: 'eq',\n    },\n    {\n        label: i18n.global.t('xpack.waf.notEqual'),\n        value: 'notEq',\n    },\n    {\n        label: i18n.global.t('xpack.waf.regex'),\n        value: 'regex',\n        hidden: ['Method'],\n    },\n    {\n        label: i18n.global.t('xpack.waf.belongToIpGroup'),\n        value: 'belongToIpGroup',\n    },\n    {\n        label: i18n.global.t('xpack.waf.notBelongToIpGroup'),\n        value: 'notBelongToIpGroup',\n    },\n];\n\nexport const HttpCodes = [\n    {\n        label: i18n.global.t('xpack.waf.badReq'),\n        value: 400,\n    },\n    {\n        label: i18n.global.t('xpack.waf.forbidden'),\n        value: 403,\n    },\n    {\n        label: i18n.global.t('xpack.waf.notFound'),\n        value: 404,\n    },\n    {\n        label: i18n.global.t('xpack.waf.noRes'),\n        value: 444,\n    },\n    {\n        label: i18n.global.t('xpack.waf.serverErr'),\n        value: 500,\n    },\n    {\n        label: i18n.global.t('xpack.waf.serviceUnavailable'),\n        value: 503,\n    },\n    {\n        label: i18n.global.t('xpack.waf.gatewayTimeout'),\n        value: 504,\n    },\n];\n\nexport const HttpMethods = [\n    {\n        label: 'GET',\n        value: 'GET',\n    },\n    {\n        label: 'POST',\n        value: 'POST',\n    },\n    {\n        label: 'PUT',\n        value: 'PUT',\n    },\n    {\n        label: 'DELETE',\n        value: 'DELETE',\n    },\n    {\n        label: 'HEAD',\n        value: 'HEAD',\n    },\n    {\n        label: 'OPTIONS',\n        value: 'OPTIONS',\n    },\n    {\n        label: 'PATCH',\n        value: 'PATCH',\n    },\n    {\n        label: 'TRACE',\n        value: 'TRACE',\n    },\n];\n\nexport const Actions = [\n    {\n        label: i18n.global.t('xpack.waf.actionAllow'),\n        value: 'allow',\n    },\n    {\n        label: i18n.global.t('xpack.waf.deny'),\n        value: 'deny',\n    },\n    {\n        label: i18n.global.t('xpack.waf.captcha'),\n        value: 'captcha',\n    },\n    {\n        label: i18n.global.t('xpack.waf.fiveSeconds'),\n        value: 'five_seconds',\n    },\n];\n\nexport const getAlgorithms = (type: string) => {\n    const baseAlgorithms = [\n        {\n            label: i18n.global.t('commons.table.default'),\n            value: 'default',\n            placeHolder: i18n.global.t('website.defaultHelper'),\n        },\n        {\n            label: i18n.global.t('website.ipHash'),\n            value: 'ip_hash',\n            placeHolder: i18n.global.t('website.ipHashHelper'),\n        },\n        {\n            label: i18n.global.t('website.leastConn'),\n            value: 'least_conn',\n            placeHolder: i18n.global.t('website.leastConnHelper'),\n        },\n    ];\n\n    if (type === 'stream') {\n        return baseAlgorithms.filter((algo) => algo.value !== 'ip_hash');\n    }\n\n    return baseAlgorithms;\n};\n\nexport const getStatusStrategy = () => [\n    {\n        label: i18n.global.t('website.strategyDown'),\n        value: 'down',\n    },\n    {\n        label: i18n.global.t('website.strategyBackup'),\n        value: 'backup',\n    },\n];\n\nexport const getWebsiteTypes = () => [\n    {\n        label: i18n.global.t('website.deployment'),\n        value: 'deployment',\n    },\n    {\n        label: i18n.global.t('runtime.runtime'),\n        value: 'runtime',\n    },\n    {\n        label: i18n.global.t('website.static'),\n        value: 'static',\n    },\n    {\n        label: i18n.global.t('website.proxy'),\n        value: 'proxy',\n    },\n    {\n        label: i18n.global.t('website.subsite'),\n        value: 'subsite',\n    },\n    {\n        label: i18n.global.t('website.stream'),\n        value: 'stream',\n    },\n];\n"
  },
  {
    "path": "frontend/src/global/use-logo.ts",
    "content": "import { GlobalStore } from '@/store';\nimport { getXpackSetting } from '@/utils/xpack';\n\nexport const useLogo = async () => {\n    const globalStore = GlobalStore();\n    const res = await getXpackSetting();\n    if (res) {\n        localStorage.setItem('1p-favicon', res.data.logo);\n        globalStore.themeConfig.title = res.data.title;\n        globalStore.themeConfig.logo = res.data.logo;\n        globalStore.themeConfig.logoWithText = res.data.logoWithText;\n        globalStore.themeConfig.loginImage = res.data?.loginImage;\n        globalStore.themeConfig.loginBgType = res.data?.loginBgType;\n        globalStore.themeConfig.loginBackground = res.data?.loginBackground;\n        globalStore.themeConfig.loginBtnLinkColor = res.data?.loginBtnLinkColor;\n        globalStore.themeConfig.favicon = res.data.favicon;\n        globalStore.watermarkShow = res.data.watermarkShow === 'Enable';\n        try {\n            globalStore.watermark = JSON.parse(res.data.watermark);\n        } catch {\n            globalStore.watermark = null;\n        }\n    }\n\n    const link = (document.querySelector(\"link[rel*='icon']\") || document.createElement('link')) as HTMLLinkElement;\n    link.type = 'image/x-icon';\n    link.rel = 'shortcut icon';\n    link.href = globalStore.themeConfig.favicon ? `/api/v2/images/favicon?t=${Date.now()}` : '/public/favicon.png';\n    document.getElementsByTagName('head')[0].appendChild(link);\n};\n"
  },
  {
    "path": "frontend/src/global/use-theme.ts",
    "content": "import { GlobalStore } from '@/store';\nimport { setPrimaryColor } from '@/utils/theme';\n\nexport const useTheme = () => {\n    const switchTheme = () => {\n        const globalStore = GlobalStore();\n        const themeConfig = globalStore.themeConfig;\n        let itemTheme = themeConfig.theme;\n        if (itemTheme === 'auto') {\n            const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n            itemTheme = prefersDark ? 'dark' : 'light';\n        }\n        document.documentElement.className = itemTheme === 'dark' ? 'dark' : 'light';\n        if (globalStore.isMasterProductPro && themeConfig.themeColor) {\n            try {\n                const themeColor = JSON.parse(themeConfig.themeColor);\n                const color = itemTheme === 'dark' ? themeColor.dark : themeColor.light;\n\n                if (color) {\n                    themeConfig.primary = color;\n                    setPrimaryColor(color);\n                }\n            } catch (e) {\n                console.error('Failed to parse themeColor', e);\n            }\n        }\n    };\n\n    return {\n        switchTheme,\n    };\n};\n"
  },
  {
    "path": "frontend/src/lang/index.ts",
    "content": "import { createI18n } from 'vue-i18n';\n\ntype LocaleMessage = Record<string, unknown>;\ntype LocaleLoader = () => Promise<{ default: LocaleMessage }>;\n\nconst DEFAULT_LOCALE = 'en';\nconst STORAGE_KEY = 'lang';\n\nconst LOCALE_LOADERS: Record<string, LocaleLoader> = {\n    zh: () => import('./modules/zh'),\n    'zh-Hant': () => import('./modules/zh-Hant'),\n    en: () => import('./modules/en'),\n    'pt-BR': () => import('./modules/pt-br'),\n    ja: () => import('./modules/ja'),\n    ru: () => import('./modules/ru'),\n    ms: () => import('./modules/ms'),\n    ko: () => import('./modules/ko'),\n    tr: () => import('./modules/tr'),\n    'es-ES': () => import('./modules/es-es'),\n};\n\nconst getStoredLocale = () => {\n    if (typeof window === 'undefined') return DEFAULT_LOCALE;\n    return localStorage.getItem(STORAGE_KEY) || DEFAULT_LOCALE;\n};\n\nconst initialLocale = getStoredLocale();\n\nconst loadedLocales = new Set<string>();\n\nexport const loadLocaleMessages = async (locale: string) => {\n    const targetLocale = LOCALE_LOADERS[locale] ? locale : DEFAULT_LOCALE;\n    if (loadedLocales.has(targetLocale)) {\n        return targetLocale;\n    }\n    const loader = LOCALE_LOADERS[targetLocale];\n    if (!loader) {\n        return targetLocale;\n    }\n    const messagesModule = await loader();\n    const messages = messagesModule.default || {};\n    if (!i18n) {\n        return targetLocale;\n    }\n    i18n.global.setLocaleMessage(targetLocale, messages);\n    loadedLocales.add(targetLocale);\n    return targetLocale;\n};\n\nconst getInitialMessages = async (): Promise<Record<string, LocaleMessage>> => {\n    const loader = LOCALE_LOADERS[initialLocale];\n    if (!loader) {\n        return { [initialLocale]: {} };\n    }\n    try {\n        const messagesModule = await loader();\n        const messages = messagesModule.default || {};\n        loadedLocales.add(initialLocale);\n        return { [initialLocale]: messages };\n    } catch {\n        return { [initialLocale]: {} };\n    }\n};\n\nconst initialMessages = await getInitialMessages();\n\nconst i18n = createI18n({\n    legacy: false,\n    missingWarn: false,\n    fallbackWarn: false,\n    locale: initialLocale,\n    fallbackLocale: DEFAULT_LOCALE,\n    globalInjection: true,\n    messages: initialMessages,\n    warnHtmlMessage: false,\n});\n\nexport const ensureFallbackLocale = async () => {\n    const fallback = i18n.global.fallbackLocale.value || DEFAULT_LOCALE;\n    if (typeof fallback === 'string') {\n        await loadLocaleMessages(fallback);\n    }\n};\n\nexport const setActiveLocale = async (locale: string) => {\n    const loaded = await loadLocaleMessages(locale);\n    i18n.global.locale.value = loaded;\n    if (typeof window !== 'undefined') {\n        localStorage.setItem(STORAGE_KEY, loaded);\n    }\n    return loaded;\n};\n\nexport default i18n;\n"
  },
  {
    "path": "frontend/src/lang/modules/en.ts",
    "content": "import fit2cloudEnLocale from 'fit2cloud-ui-plus/src/locale/lang/en';\n\nconst message = {\n    commons: {\n        true: 'True',\n        false: 'False',\n        colon: ': ',\n        example: 'e.g., ',\n        fit2cloud: 'FIT2CLOUD',\n        lingxia: 'Lingxia',\n        button: {\n            run: 'Run',\n            prev: 'Previous',\n            next: 'Next',\n            create: 'Create',\n            add: 'Add',\n            save: 'Save',\n            set: 'Set',\n            sync: 'Sync',\n            delete: 'Delete',\n            edit: 'Edit',\n            enable: 'Enable',\n            disable: 'Disable',\n            confirm: 'Confirm',\n            cancel: 'Cancel',\n            reset: 'Reset',\n            setDefault: 'Restore Default',\n            restart: 'Restart',\n            conn: 'Connect',\n            disConn: 'Disconnect',\n            clean: 'Clean',\n            login: 'Login',\n            close: 'Close',\n            stop: 'Stop',\n            start: 'Start',\n            view: 'View',\n            watch: 'Watch',\n            handle: 'Trigger',\n            clone: 'Clone',\n            expand: 'Expand',\n            collapse: 'Collapse',\n            log: 'Logs',\n            back: 'Back',\n            backup: 'Backup',\n            recover: 'Recover',\n            retry: 'Retry',\n            upload: 'Upload',\n            download: 'Download',\n            init: 'Initialize',\n            verify: 'Verify',\n            saveAndEnable: 'Save and enable',\n            import: 'Import',\n            export: 'Export',\n            power: 'Permissions',\n            search: 'Search',\n            refresh: 'Refresh',\n            get: 'Get',\n            upgrade: 'Upgrade',\n            update: 'Update',\n            updateNow: 'Update Now',\n            ignore: 'Ignore upgrade',\n            copy: 'Copy',\n            random: 'Random',\n            install: 'Install',\n            uninstall: 'Uninstall',\n            fullscreen: 'Enter fullscreen',\n            quitFullscreen: 'Exit fullscreen',\n            showAll: 'Show All',\n            hideSome: 'Hide Some',\n            agree: 'Agree',\n            notAgree: 'Disagree',\n            preview: 'Preview',\n            open: 'Open',\n            notSave: \"Don't Save\",\n            createNewFolder: 'Create new folder',\n            createNewFile: 'Create new file',\n            helpDoc: 'Docs',\n            bind: 'Bind',\n            unbind: 'Unbind',\n            cover: 'Cover',\n            skip: 'Skip',\n            fix: 'Fix',\n            down: 'Stop',\n            up: 'Start',\n            sure: 'Confirm',\n            show: 'Show',\n            hide: 'Hide',\n            visit: 'Visit',\n            migrate: 'Migrate',\n        },\n        operate: {\n            start: 'Start',\n            stop: 'Stop',\n            restart: 'Restart',\n            reload: 'Reload',\n            rebuild: 'Rebuild',\n            sync: 'Sync',\n            up: 'Up',\n            down: 'Down',\n            delete: 'Delete',\n        },\n        search: {\n            timeStart: 'Start time',\n            timeEnd: 'End time',\n            timeRange: 'To',\n            dateStart: 'Start date',\n            dateEnd: 'End date',\n            date: 'Date',\n        },\n        table: {\n            all: 'All',\n            total: 'Total {0}',\n            name: 'Name',\n            type: 'Type',\n            status: 'Status',\n            statusSuccess: 'Success',\n            statusFailed: 'Failed',\n            statusWaiting: 'Waiting...',\n            records: 'Records',\n            group: 'Group',\n            default: 'Default',\n            createdAt: 'Created',\n            publishedAt: 'Published',\n            date: 'Date',\n            updatedAt: 'Updated',\n            operate: 'Actions',\n            message: 'Message',\n            description: 'Description',\n            interval: 'Interval',\n            user: 'Owner',\n            title: 'Title',\n            port: 'Port',\n            forward: 'Forward',\n            protocol: 'Protocol',\n            tableSetting: 'Table settings',\n            refreshRate: 'Refresh interval',\n            noRefresh: 'No refresh',\n            selectColumn: 'Select column',\n            local: 'Local',\n            serialNumber: 'No.',\n            manageGroup: 'Manage Groups',\n            backToList: 'Back to List',\n            keepEdit: 'Continue Editing',\n        },\n        loadingText: {\n            Upgrading: 'Upgrading system, please wait...',\n            Restarting: 'Restarting system, please wait...',\n            Recovering: 'Recovering from snapshot, please wait...',\n            Rollbacking: 'Rolling back snapshot, please wait...',\n        },\n        msg: {\n            noneData: 'No data available',\n            delete: 'This action cannot be undone. Continue?',\n            clean: 'This cleanup cannot be undone. Continue?',\n            closeDrawerHelper: 'Unsaved changes may be lost. Continue?',\n            deleteSuccess: 'Deleted successfully',\n            loginSuccess: 'Logged in successfully',\n            operationSuccess: 'Completed successfully',\n            copySuccess: 'Copied successfully',\n            notSupportOperation: 'This operation is not supported',\n            requestTimeout: 'Request timed out, try again later',\n            infoTitle: 'Notice',\n            notRecords: 'No run records for this task',\n            sureLogOut: 'Are you sure you want to log out?',\n            createSuccess: 'Created successfully',\n            updateSuccess: 'Updated successfully',\n            uploadSuccess: 'Uploaded successfully',\n            operateConfirm: 'To confirm, type: ',\n            inputOrSelect: 'Select or enter',\n            copyFailed: 'Failed to copy',\n            operatorHelper: 'Run \"{1}\" on \"{0}\". This cannot be undone. Continue?',\n            notFound: 'Sorry, the page you requested does not exist.',\n            unSupportType: 'Current file type is not supported.',\n            unSupportSize: 'Uploaded file exceeds {0}M. Confirm.',\n            fileExist: 'The file already exists in the current folder. Repeat uploading is not supported.',\n            fileNameErr: 'Filename must be 1-256 chars and only include letters, numbers, dots, dashes, or underscores',\n            confirmNoNull: 'Make sure the value {0} is not empty.',\n            errPort: 'Invalid port, please check',\n            remove: 'Remove',\n            backupHelper: 'The current operation will back up {0}. Proceed?',\n            recoverHelper: 'Restore from {0}. This is irreversible. Continue?',\n            backupSuccess: 'Backup successful',\n            restoreSuccess: 'Restore successful',\n            refreshSuccess: 'Refreshed successfully',\n            rootInfoErr: \"It's already the root directory\",\n            resetSuccess: 'Reset successful',\n            creatingInfo: 'Creating, no need for this operation',\n            installSuccess: 'Installed successfully',\n            uninstallSuccess: 'Uninstalled successfully',\n            offlineTips: 'Offline edition does not support this action',\n            errImportFormat: 'Import data or format is abnormal, check and try again!',\n            importHelper: 'If conflicts exist, imported data overwrites existing records.',\n            errImport: 'File content is abnormal:',\n        },\n        login: {\n            username: 'Username',\n            password: 'Password',\n            passkey: 'Passkey Login',\n            welcome: 'Welcome back. Sign in with your username and password.',\n            errorAuthInfo: 'Incorrect username or password, try again.',\n            errorMfaInfo: 'Invalid authentication info, try again.',\n            captchaHelper: 'Captcha',\n            errorCaptcha: 'Invalid captcha code',\n            notSafe: 'Access Denied',\n            safeEntrance1: 'Secure entry is enabled in this environment',\n            safeEntrance2: 'Run this command over SSH to view the panel entry: 1pctl user-info',\n            errIP1: 'IP allowlist is enabled in this environment',\n            errDomain1: 'Domain binding is enabled in this environment',\n            errHelper: 'To reset binding info, run this command over SSH: ',\n            codeInput: 'Enter the 6-digit MFA code',\n            mfaTitle: 'MFA Verification',\n            mfaCode: 'MFA verification code',\n            title: 'Linux Server Management Panel',\n            licenseHelper: '<Community License Agreement>',\n            errorAgree: 'Please accept the Community License Agreement',\n            logout: 'Logout',\n            agreeTitle: 'Agreement',\n            agreeContent:\n                'In order to better protect your legitimate rights and interests, please read and agree to the following agreement &laquo; <a href = \"https://www.fit2cloud.com/legal/licenses.html\" target = \"_blank\" > Community License Agreement </a> &raquo;',\n            passkeyFailed: 'Passkey sign-in failed, please try again',\n            passkeyNotSupported:\n                'Current browser or environment does not support passkeys, confirm you have bound a domain name and are accessing through HTTPS',\n            passkeyToPassword: 'Passkey not working? Use password instead',\n        },\n        rule: {\n            username: 'Enter a username',\n            password: 'Enter a password',\n            rePassword: 'Confirm password is inconsistent with the password.',\n            requiredInput: 'This field is required.',\n            requiredSelect: 'Select an item in the list',\n            illegalChar: 'Injection of characters & ; $ \\' ` ( ) \" > < | is currently not supported',\n            illegalInput: \"This field mustn't contains illegal characters.\",\n            commonName:\n                'This field must start with non-special characters and must consist of English, Chinese, numbers, \".\", \"-\", and \"_\" characters with a length of 1-128.',\n            userName: 'This field must consist of English, Chinese, numbers and \"_\" characters with a length of 3-30.',\n            simpleName:\n                'This field mustn\\'t start with underscore character and must consist of English, numbers, and \"_\" characters with a length of 3-30.',\n            simplePassword:\n                'This field mustn\\'t start with underscore character and must consist of English, numbers, and \"_\" characters with a length of 1-30.',\n            dbName: 'This field mustn\\'t start with underscore character and must consist of English, numbers, and \"_\" characters with a length of 1-64.',\n            imageName:\n                'This field must consist of English, numbers, \":\", \"@\", \"/\", \".\", \"-\", and \"_\" characters with a length of 1-256.',\n            volumeName:\n                'This field must consist of English, numbers, \".\", \"-\", and \"_\" characters with a length of 2-30.',\n            supervisorName:\n                'This field must start with non-special characters and must consist of English, numbers, \"-\", and \"_\" characters with a length of 1-128.',\n            composeName:\n                'Supports non-special characters at the beginning, lowercase letters, numbers, - and _, length 1-256',\n            complexityPassword:\n                'This field must consist of English, numbers with a length of 8-30 and contain at least two special characters.',\n            commonPassword: 'This field length must be more than 6.',\n            linuxName:\n                'This field length must be between 1 and 128. The field mustn\\'t contain these special characters: \"{0}\".',\n            email: 'This field must be a valid email address.',\n            number: 'This field must be a number.',\n            integer: 'This field must be a positive integer.',\n            ip: 'This field must be a valid IP address.',\n            host: 'This field must be a valid IP address or domain name.',\n            hostHelper: 'Support input of IP address or domain name',\n            port: 'This field must be a valid port number.',\n            selectHelper: 'Select the correct {0} file',\n            domain: 'This field must be like: example.com or example.com:8080.',\n            databaseName: 'This field must consist of English, numbers, and \"_\" characters with a length of 1-30.',\n            ipErr: 'This field must be a valid IP address.',\n            numberRange: 'This field must be a number between {0} and {1}.',\n            paramName:\n                'This field must consist of English, numbers, \".\", \"-\", and \"_\" characters with a length of 2-30.',\n            paramComplexity:\n                'This field mustn\\'t start and end with special characters and must consist of English, numbers, \"{0}\" characters with a length of 6-128.',\n            paramUrlAndPort: 'This field must be in the format of \"http(s)://(domain name/ip):(port)\".',\n            nginxDoc: 'This field must consist of English, numbers and \".\" characters.',\n            appName:\n                'This field musnt\\'t start and end with \"-\" and \"_\" characters and must consist of English, numbers, \"-\", and \"_\" characters with a length of 2-30.',\n            containerName: 'Supports letters, numbers, -, _ and .; cannot start with - _ or .; length: 2-128',\n            mirror: 'The mirror acceleration address should start with http(s)://, support English letters (both uppercase and lowercase), numbers, . / and -, and should not contain blank lines.',\n            disableFunction: 'Only support letters ,underscores,and,',\n            leechExts: 'Only support letters, numbers and,',\n            paramSimple: 'Support lowercase letters and numbers, length 1-128',\n            filePermission: 'File Permission Error',\n            formatErr: 'Format error, please check and retry',\n            phpExtension: 'Only supports , _ lowercase English and numbers',\n            paramHttp: 'Must start with http:// or https://',\n            phone: 'The format of the phone number is incorrect',\n            authBasicPassword: 'Supports letters, numbers, and common special characters, length 1-72',\n            length128Err: 'Length cannot exceed 128 characters',\n            maxLength: 'Length cannot exceed {0} characters',\n            alias: 'Supports English, numbers, - and _, length 1-128, and cannot start or end with -_.',\n        },\n        res: {\n            paramError: 'Request failed. Try again later.',\n            forbidden: 'Current user has no permission',\n            serverError: 'Service error',\n            notFound: 'Resource does not exist',\n            commonError: 'Request failed',\n        },\n        service: {\n            serviceNotStarted: 'The {0} service is not started.',\n        },\n        status: {\n            running: 'Running',\n            done: 'Done',\n            scanFailed: 'Incomplete',\n            success: 'Success',\n            waiting: 'Waiting',\n            waitForUpgrade: 'Wait for Upgrade',\n            failed: 'Failed',\n            stopped: 'Stopped',\n            error: 'Error',\n            created: 'Created',\n            restarting: 'Restarting',\n            uploading: 'Uploading',\n            unhealthy: 'Unhealthy',\n            removing: 'Removing',\n            paused: 'Paused',\n            exited: 'Exited',\n            dead: 'Dead',\n            installing: 'Installing',\n            enabled: 'Enabled',\n            disabled: 'Disabled',\n            normal: 'Normal',\n            building: 'Building',\n            upgrading: 'Upgrading',\n            pending: 'Pending Edit',\n            rebuilding: 'Rebuilding',\n            deny: 'Denied',\n            accept: 'Accepted',\n            used: 'Used',\n            unused: 'Unused',\n            starting: 'Starting',\n            recreating: 'Recreating',\n            creating: 'Creating',\n            init: 'Waiting for application',\n            ready: 'Ready',\n            applying: 'Applying',\n            uninstalling: 'Uninstalling',\n            lost: 'Lost Contact',\n            bound: 'Bound',\n            unbind: 'Unbound',\n            exceptional: 'Exceptional',\n            free: 'Free',\n            enable: 'Enabled',\n            disable: 'Disabled',\n            deleted: 'Deleted',\n            downloading: 'Downloading',\n            packing: 'Packing',\n            sending: 'Sending',\n            healthy: 'Normal',\n            executing: 'Executing',\n            installerr: 'Installation failed',\n            applyerror: 'Apply failed',\n            systemrestart: 'Interrupted',\n            starterr: 'Startup failed',\n            uperr: 'Startup failed',\n            new: 'New',\n            conflict: 'Conflict',\n            duplicate: 'Duplicate',\n            unexecuted: 'Unexecuted',\n        },\n        units: {\n            second: ' second | second | seconds',\n            minute: 'minute | minute | minutes',\n            hour: 'hour | hour | hours',\n            day: 'day | day | days',\n            week: 'week | week | weeks',\n            month: 'month | month | months',\n            year: 'year | year | years',\n            time: 'rqm',\n            core: 'core | core | cores',\n            millisecond: 'millisecond | milliseconds',\n            secondUnit: 's',\n            minuteUnit: 'min',\n            hourUnit: 'h',\n            dayUnit: 'd',\n            semicolon: ';',\n        },\n        log: {\n            noLog: 'No logs available',\n        },\n    },\n    menu: {\n        home: 'Dashboard',\n        apps: 'App Store',\n        website: 'Website | WebSites',\n        project: 'Project | Projects',\n        config: 'Configuration | Configurations',\n        ssh: 'SSH Settings',\n        firewall: 'Firewall',\n        filter: 'Filter',\n        ssl: 'Certificate | Certificates',\n        database: 'Database | Databases',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: 'Container | Containers',\n        cronjob: 'Cron Job | Cron Jobs',\n        system: 'System',\n        security: 'Security',\n        files: 'File Browser',\n        monitor: 'Monitoring',\n        terminal: 'Terminal | Terminals',\n        settings: 'Settings',\n        toolbox: 'Toolbox',\n        logs: 'Log | Logs',\n        runtime: 'Runtime | Runtimes',\n        processManage: 'Process | Processes',\n        process: 'Process | Processes',\n        network: 'Network | Networks',\n        supervisor: 'Supervisor',\n        tamper: 'Tamper-proof',\n        app: 'Application',\n        msgCenter: 'Tasks',\n        disk: 'Disk',\n    },\n    home: {\n        recommend: 'Recommended',\n        dir: 'Directory',\n        alias: 'Alias',\n        quickDir: 'Quick Access',\n        minQuickJump: 'Add at least one quick access entry',\n        maxQuickJump: 'You can add up to four quick access entries',\n        database: 'Database - All',\n        restart_1panel: 'Restart panel',\n        restart_system: 'Restart server',\n        operationSuccess: 'Completed successfully. Rebooting now, refresh the browser later.',\n        entranceHelper:\n            'Security entrance is not enabled. You can enable it in \"Settings -> Security\" to improve system security.',\n        appInstalled: 'Applications',\n        systemInfo: 'System information',\n        hostname: 'Hostname',\n        platformVersion: 'Operating system',\n        kernelVersion: 'Kernel',\n        kernelArch: 'Architecture',\n        network: 'Network',\n        io: 'Disk I/O',\n        ip: 'Local IP',\n        proxy: 'System proxy',\n        baseInfo: 'Basic info',\n        totalSend: 'Total sent',\n        totalRecv: 'Total received',\n        rwPerSecond: 'I/O operations',\n        ioDelay: 'I/O latency',\n        uptime: 'Uptime',\n        runningTime: 'Up since',\n        mem: 'System memory',\n        swapMem: 'Swap',\n        runSmoothly: 'Low load',\n        runNormal: 'Moderate load',\n        runSlowly: 'High load',\n        runJam: 'Heavy load',\n        core: 'Physical cores',\n        logicCore: 'Logical cores',\n        corePercent: 'Core Usage',\n        cpuFrequency: 'CPU Frequency',\n        cpuDetailedPercent: 'CPU usage details',\n        cpuUser: 'User',\n        cpuSystem: 'System',\n        cpuIdle: 'Idle',\n        cpuIrq: 'IRQ',\n        cpuSoftirq: 'Soft IRQ',\n        cpuSteal: 'Steal',\n        cpuTop: 'Top 5 Processes by CPU Usage',\n        memTop: 'Top 5 Processes by Memory Usage',\n        loadAverage: 'Load average in the last 1 minute | Load average in the last {n} minutes',\n        load: 'Load',\n        mount: 'Mount point',\n        fileSystem: 'File system',\n        total: 'Total',\n        used: 'Used',\n        cache: 'Cache',\n        free: 'Free',\n        shard: 'Sharded',\n        available: 'Available',\n        percent: 'Utilization',\n        goInstall: 'Go install',\n        networkCard: 'NIC',\n        disk: 'Disk',\n        memo: 'Memo',\n        memoPlaceholder: 'Click the edit button to enable editing.',\n        carouselSetting: 'Carousel settings',\n        tooltipSensitiveInfo: 'Show/Hide sensitive info',\n    },\n    tabs: {\n        more: 'More',\n        hide: 'Hide',\n        closeLeft: 'Close left',\n        closeRight: 'Close right',\n        closeCurrent: 'Close current',\n        closeOther: 'Close other',\n        closeAll: 'Close All',\n    },\n    header: {\n        logout: 'Logout',\n    },\n    database: {\n        manage: 'Manage Database',\n        deleteBackupHelper: 'Delete database backups simultaneously',\n        delete: 'Delete action cannot be undone, please input \"',\n        deleteHelper: '\" to delete this database',\n        noMysql: 'Database service (MySQL or MariaDB)',\n        noPostgresql: 'Database service PostgreSQL',\n        goUpgrade: 'Go to upgrade',\n        goInstall: 'Go to install',\n        isDelete: 'Deleted',\n        permission: 'Change permissions',\n        format: 'Charset',\n        collation: 'Collation',\n        collationHelper: 'If empty, use the default collation of the {0} character set',\n        permissionForIP: 'IP',\n        permissionAll: 'All (%)',\n        localhostHelper:\n            'Configuring database permissions as \"localhost\" for container deployment will prevent external access to the container. Please choose carefully!',\n        databaseConnInfo: 'Connection',\n        rootPassword: 'Root password',\n        serviceName: 'Service name',\n        serviceNameHelper: 'Access between containers in the same network.',\n        backupList: 'Backup',\n        loadBackup: 'Import',\n        localUpload: 'Local upload',\n        hostSelect: 'Server selection',\n        selectHelper: 'Are you sure you want to import backup file {0}?',\n        remoteAccess: 'Remote access',\n        remoteHelper: 'Use commas to separate multiple IPs, e.g. 172.16.10.111,172.16.10.112',\n        remoteConnHelper: 'Remote MySQL root login is risky. Proceed with caution.',\n        changePassword: 'Change password',\n        changeConnHelper: 'This will modify database {0}. Continue?',\n        changePasswordHelper:\n            'The database has been associated with an application. Changing the password will change the database password of the application at the same time. The change takes effect after the application restarts.',\n        recoverTimeoutHelper: '-1 means no timeout',\n        confChange: 'Configuration',\n        confNotFound:\n            'The configuration file could not be found. Please upgrade the application to the latest version in the app store and try again!',\n        portHelper:\n            'This port is the exposed port of the container. You need to save the modification separately and restart the container!',\n        loadFromRemote: 'Sync from server',\n        userBind: 'Bind user',\n        pgBindHelper:\n            'This operation is used to create a new user and bind it to the target database. Currently, selecting users already existing in the database is not supported.',\n        pgSuperUser: 'Superuser',\n        loadFromRemoteHelper: 'Sync database info from the server to 1Panel. Continue?',\n        passwordHelper: 'Not available, click to update',\n        remote: 'Remote',\n        remoteDB: 'Remote server | Remote servers',\n        createRemoteDB: 'Bind @.lower:database.remoteDB',\n        unBindRemoteDB: 'Unbind @.lower:database.remoteDB',\n        unBindForce: 'Force unbind',\n        unBindForceHelper: 'Ignore all errors during the unbinding process to ensure the final operation is successful',\n        unBindRemoteHelper:\n            'Unbinding the remote database will only remove the binding relationship and will not directly delete the remote database',\n        editRemoteDB: 'Edit remote server',\n        localDB: 'Local database',\n        address: 'Database address',\n        version: 'Database version',\n        userHelper: 'Use root or a user with root privileges to access the remote database.',\n        pgUserHelper: 'Use a database superuser account.',\n        ssl: 'Use SSL',\n        clientKey: 'Client private key',\n        clientCert: 'Client certificate',\n        caCert: 'CA certificate',\n        hasCA: 'Has CA certificate',\n        skipVerify: 'Skip certificate validation',\n        initialDB: 'Initial Database',\n        formatHelper:\n            'The current database character set is {0}, the character set inconsistency may cause recovery failure',\n        dropHelper: 'You can drag and drop the uploaded file here or',\n        clickHelper: 'click to upload',\n        supportUpType:\n            'Only supports sql, sql.gz, tar.gz, .zip file formats. The imported compressed file must contain only one .sql file or include test.sql',\n        currentStatus: 'Current status',\n        baseParam: 'Basic parameter',\n        performanceParam: 'Performance parameter',\n        runTime: 'Start time',\n        connections: 'Total connections',\n        bytesSent: 'Send bytes',\n        bytesReceived: 'Received bytes',\n        queryPerSecond: 'Query per second',\n        txPerSecond: 'Tx per second',\n        connInfo: 'Active/peak connections',\n        connInfoHelper: 'If the value is too large, increase \"max_connections\".',\n        threadCacheHit: 'Thread cache hit',\n        threadCacheHitHelper: 'If it is too low, increase \"thread_cache_size\".',\n        indexHit: 'Index hit',\n        indexHitHelper: 'If it is too low, increase \"key_buffer_size\".',\n        innodbIndexHit: 'Innodb index hit rate',\n        innodbIndexHitHelper: 'If it is too low, increase \"innodb_buffer_pool_size\".',\n        cacheHit: 'Query cache hit rate',\n        cacheHitHelper: 'If it is too low, increase \"query_cache_size\".',\n        tmpTableToDB: 'Temporary table to disk',\n        tmpTableToDBHelper: 'If it is too large, try increasing \"tmp_table_size\".',\n        openTables: 'Open tables',\n        openTablesHelper: 'The configuration value of \"table_open_cache\" must be greater than or equal to this value.',\n        selectFullJoin: 'Select full join',\n        selectFullJoinHelper: 'If this value is not 0, check table indexes.',\n        selectRangeCheck: 'The number of joins with no index',\n        selectRangeCheckHelper: 'If this value is not 0, check table indexes.',\n        sortMergePasses: 'Number of sorted merges',\n        sortMergePassesHelper: 'If the value is too large, increase \"sort_buffer_size\".',\n        tableLocksWaited: 'Lock table number',\n        tableLocksWaitedHelper: 'If the value is too large, consider increasing your database performance.',\n        performanceTuning: 'Performance tuning',\n        optimizationScheme: 'Optimization scheme',\n        keyBufferSizeHelper: 'Buffer size for index',\n        queryCacheSizeHelper: 'Query cache. If this function is disabled, set this parameter to 0.',\n        tmpTableSizeHelper: 'Temporary table cache size',\n        innodbBufferPoolSizeHelper: 'Innodb buffer size',\n        innodbLogBufferSizeHelper: 'Innodb log buffer size',\n        sortBufferSizeHelper: '* connections, buffer size per thread sort',\n        readBufferSizeHelper: '* connections, read buffer size',\n        readRndBufferSizeHelper: '* connections, random read buffer size',\n        joinBufferSizeHelper: '* connections, association table cache size',\n        threadStackelper: '* connections, stack size per thread',\n        binlogCacheSizeHelper: '* onnections, binary log cache size (multiples of 4096)',\n        threadCacheSizeHelper: 'Thread pool size',\n        tableOpenCacheHelper: 'Table cache',\n        maxConnectionsHelper: 'Max connections',\n        restart: 'Restart',\n        slowLog: 'Slow logs',\n        noData: 'No slow logs yet.',\n        isOn: 'On',\n        longQueryTime: 'threshold(s)',\n        thresholdRangeHelper: 'Enter the correct threshold (1 - 600).',\n        timeout: 'Timeout(s)',\n        timeoutHelper: 'Idle connection timeout period. 0 indicates that the connection is on continuously.',\n        maxclients: 'Max clients',\n        requirepassHelper:\n            'Leave this blank to indicate that no password has been set. Changes need to be saved separately and the container restarted!',\n        databases: 'Number of databases',\n        maxmemory: 'Maximum memory usage',\n        maxmemoryHelper: '0 indicates no restriction.',\n        tcpPort: 'Current listening port.',\n        uptimeInDays: 'Days in operation.',\n        connectedClients: 'Number of connected clients.',\n        usedMemory: 'Current memory usage of Redis.',\n        usedMemoryRss: 'Memory size requested from the operating system.',\n        usedMemoryPeak: 'Peak memory consumption of Redis.',\n        memFragmentationRatio: 'Memory fragmentation ratio.',\n        totalConnectionsReceived: 'Total number of clients connected since run.',\n        totalCommandsProcessed: 'The total number of commands executed since the run.',\n        instantaneousOpsPerSec: 'Number of commands executed by the server per second.',\n        keyspaceHits: 'The number of times a database key was successfully found.',\n        keyspaceMisses: 'Number of failed attempts to find the database key.',\n        hit: 'Find the database key hit ratio.',\n        latestForkUsec: 'The number of microseconds spent on the last fork() operation.',\n        redisCliHelper: '\"redis-cli\" not detected. Enable the service first.',\n        redisQuickCmd: 'Redis quick commands',\n        recoverHelper: 'This will overwrite data with [{0}]. Continue?',\n        submitIt: 'Overwrite the data',\n        baseConf: 'Basic',\n        allConf: 'All',\n        restartNow: 'Restart now',\n        restartNowHelper1:\n            'You need to restart the system after the configuration changes take effect. If your data needs to be persisted, perform the save operation first.',\n        restartNowHelper: 'This will take effect only after the system restarts.',\n        persistence: 'Persistence',\n        rdbHelper1: 'second(s), insert',\n        rdbHelper2: 'pieces of data',\n        rdbHelper3: 'Meeting any of the conditions will trigger RDB persistence.',\n        rdbInfo: 'Ensure that the value in the rule list ranges from 1 to 100000',\n        containerConn: 'Container connection',\n        copyConnURL: 'Copy connection URL',\n        connAddress: 'Address',\n        containerConnHelper:\n            'This connection address is used by applications running on the PHP execution environment/container installation.',\n        remoteConn: 'External connection',\n        remoteConnHelper2:\n            'This connnection address can be used by applications running on non-container or external applications.',\n        remoteConnHelper3:\n            'The default access address is the host IP. To modify it, go to the \"Default Access Address\" configuration item in the panel settings page.',\n        localIP: 'Local IP',\n    },\n    aiTools: {\n        agents: {\n            agents: 'Agents',\n            agent: 'Agent',\n            account: 'Model Account',\n            noAccountHint: 'Choose an existing model account or add a new one.',\n            accountCount: '{0} model accounts',\n            syncAgents: 'Sync related agents',\n            syncAgentsHelper: 'Update openclaw.json for agents using this model account',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: 'App Version',\n            webuiPort: 'WebUI Port',\n            allowedOrigins: 'Access Addresses',\n            allowedOriginsHelper:\n                'Enter one full access address per line. HTTPS is recommended, for example https://192.168.1.2:18789. Fill it manually if the default access address is not configured.',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: 'Enter at least one access address',\n            allowedOriginsInvalid: 'Use the format http(s)://host-or-ip[:port]',\n            provider: 'Provider',\n            apiKey: 'API Key',\n            baseUrl: 'Base URL',\n            accountModels: 'Model Catalog',\n            accountModelsHelper: 'Configure the models this account exposes to OpenClaw for switching and settings',\n            accountModelsRequired: 'Configure at least one model',\n            accountModelsDuplicate: 'Duplicate models exist in the catalog',\n            modelPool: 'Model Pool',\n            modelPoolHelper:\n                'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',\n            modelInputTypes: 'Input Types',\n            reasoning: 'Reasoning Model',\n            token: 'Token',\n            manualModel: 'Manual input',\n            verified: 'Verified',\n            verifySkipped: 'No verification',\n            configTitle: 'Configuration',\n            settingsTab: 'Settings',\n            securityTab: 'Security',\n            otherTab: 'Other',\n            timeZone: 'Time Zone',\n            browserEnabled: 'Browser Enabled',\n            switchModelSuccess: 'Model switched successfully',\n            channelsTab: 'Channels',\n            wecom: 'WeCom',\n            dingtalk: 'DingTalk',\n            feishu: 'Feishu',\n            pluginNotInstalled: 'Plugin is not installed. Please install it first.',\n            dmPolicy: 'DM Policy',\n            groupPolicy: 'Group Policy',\n            policyPairing: 'Pairing',\n            policyAllowlist: 'Allowlist',\n            policyOpen: 'Open',\n            policyDisabled: 'Disabled',\n            botName: 'Bot Name',\n            botId: 'Bot ID',\n            appId: 'App ID',\n            appSecret: 'App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: 'DM Allowlist',\n            allowFromHelper: 'One sender ID per line. Used only when DM Policy is Allowlist.',\n            allowFromPlaceholder: 'One sender ID per line',\n            groupAllowFrom: 'Group Allowlist',\n            groupAllowFromHelper: 'One group ID per line. Used only when Group Policy is Allowlist.',\n            groupAllowFromPlaceholder: 'One group ID per line',\n            allowFromRequired: 'Enter at least one allowlist entry',\n            saveAndRestartGateway: 'Save and restart gateway',\n            pairingCode: 'Pairing Code',\n            pairingCodePlaceholder: 'Enter pairing code',\n            approvePairing: 'Approve Pairing',\n            feishuRequired: 'Fill botName / appId / appSecret',\n            saveSuccess: 'Saved successfully',\n            pairingCodeRequired: 'Enter pairing code',\n            pairingApproveSuccess: 'Pairing approved successfully',\n            customProviderHelper: 'Custom model providers do not validate whether the account is available.',\n            feishuSaveSuccess: 'Saved to Feishu',\n        },\n        model: {\n            model: 'Models',\n            localModel: 'Local Models',\n            create: 'Add Model',\n            create_helper: 'Pull \"{0}\"',\n            ollama_doc: 'You can visit the Ollama official website to search and find more models.',\n            container_conn_helper: 'Use this address for inter-container access or connection',\n            ollama_sync: 'Syncing Ollama model found the following models do not exist, do you want to delete them?',\n            from_remote: 'This model was not downloaded via 1Panel, no related pull logs.',\n            no_logs: 'The pull logs for this model have been deleted and cannot be viewed.',\n            vllmVersionHelper: 'For FusionXpark GB 10 servers, please select the -cu130 version.',\n        },\n        proxy: {\n            proxy: 'AI Proxy Enhancement',\n            proxyHelper1: 'Bind domain and enable HTTPS for enhanced transmission security',\n            proxyHelper2: 'Limit IP access to prevent exposure on the public internet',\n            proxyHelper3: 'Enable streaming',\n            proxyHelper4: 'Once created, you can view and manage it in the website list',\n            proxyHelper5:\n                'After enabling, you can disable external access to the port in the App Store - Installed - Ollama - Parameters to improve security.',\n            proxyHelper6: 'To disable proxy configuration, you can delete it from the website list.',\n            whiteListHelper: 'Restrict access to only IPs in the whitelist',\n        },\n        gpu: {\n            gpu: 'GPU Monitoring',\n            gpuHelper: 'The system did not detect NVIDIA-SMI or XPU-SMI commands. Please check and try again!',\n            process: 'Process Information',\n            processCount: 'Process Count',\n            type: 'Type',\n            typeG: 'Graphics',\n            typeC: 'Compute',\n            typeCG: 'Compute+Graphics',\n            processName: 'Process Name',\n            shr: 'Shared Memory',\n            temperatureHelper: 'High GPU temperature may cause GPU frequency reduction',\n            gpuUtil: 'GPU Utilization',\n            temperature: 'Temperature',\n            performanceState: 'Performance State',\n            powerUsage: 'Power Consumption',\n            memoryUsage: 'Memory Utilization',\n            fanSpeed: 'Fan Speed',\n            power: 'Power',\n            powerCurrent: 'Current Power',\n            powerLimit: 'Power Limit',\n            memory: 'Memory',\n            memoryUsed: 'Memory Used',\n            memoryTotal: 'Total Memory',\n            percent: 'Utilization',\n            base: 'Basic Information',\n            driverVersion: 'Driver Version',\n            cudaVersion: 'CUDA Version',\n            processMemoryUsage: 'Memory Usage',\n            performanceStateHelper: 'From P0 (maximum performance) to P12 (minimum performance)',\n            busID: 'Bus Address',\n            persistenceMode: 'Persistence Mode',\n            enabled: 'Enabled',\n            disabled: 'Disabled',\n            persistenceModeHelper:\n                'Persistence mode responds to tasks more quickly, but standby power consumption will increase accordingly',\n            displayActive: 'GPU Initialization',\n            displayActiveT: 'Yes',\n            displayActiveF: 'No',\n            ecc: 'Error Checking and Correcting Technology',\n            computeMode: 'Compute Mode',\n            default: 'Default',\n            exclusiveProcess: 'Exclusive Process',\n            exclusiveThread: 'Exclusive Thread',\n            prohibited: 'Prohibited',\n            defaultHelper: 'Default: Processes can execute concurrently',\n            exclusiveProcessHelper:\n                'Exclusive Process: Only one CUDA context can use the GPU, but it can be shared by multiple threads',\n            exclusiveThreadHelper: 'Exclusive Thread: Only one thread in a CUDA context can use the GPU',\n            prohibitedHelper: 'Prohibited: Concurrent process execution is not allowed',\n            migModeHelper: 'Used to create MIG instances, implementing physical GPU isolation at the user layer.',\n            migModeNA: 'Not Supported',\n            current: 'Real-time Monitoring',\n            history: 'Historical Records',\n            notSupport: 'The current version or driver does not support displaying this parameter.',\n        },\n        mcp: {\n            server: 'MCP Server',\n            baseUrl: 'External Access Path',\n            baseUrlHelper: 'For example: http://192.168.1.2:8000',\n            ssePath: 'SSE Path',\n            ssePathHelper: 'For example: /sse, note not to duplicate with other servers',\n            environment: 'Environment Variables',\n            envKey: 'Variable Name',\n            envValue: 'Variable Value',\n            externalUrl: 'External Connection Address',\n            operatorHelper: 'Will perform {1} operation on {0}, continue?',\n            domain: 'Default Access Address',\n            domainHelper: 'For example: 192.168.1.1 or example.com',\n            bindDomain: 'Bind Website',\n            commandPlaceHolder: 'Currently only supports npx and binary startup commands',\n            importMcpJson: 'Import MCP Server Configuration',\n            importMcpJsonError: 'mcpServers structure is incorrect',\n            bindDomainHelper:\n                'After binding the website, it will modify the access address of all installed MCP Servers and close external access to the ports',\n            outputTransport: 'Output Type',\n            streamableHttpPath: 'Streaming Path',\n            streamableHttpPathHelper: 'For example: /mcp, note that it should not overlap with other Servers',\n            npxHelper: 'Suitable for mcp started with npx or binary',\n            uvxHelper: 'Suitable for mcp started with uvx',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: 'Model Directory',\n            commandHelper:\n                'If external access is needed, set the port in the command to be the same as the application port',\n            imageAlert:\n                'Due to the large image size, it is recommended to manually download the image to the server before installation',\n            modelSpeedup: 'Enable model acceleration',\n            modelType: 'Model type',\n        },\n    },\n    container: {\n        createByCommand: 'Create by command',\n        importContainerBackupTip: 'Import container backup file, only .tar.gz is supported',\n        importComposeBackupTip: 'Import compose backup file, only .tar.gz is supported',\n        stopContainerBeforeBackup: 'Stop container before backup',\n        stopComposeBeforeBackup: 'Stop compose before backup',\n        stopBeforeBackupHelper:\n            'When enabled, the container or compose service is stopped before backup and automatically restored after completion to ensure data consistency.',\n        commandInput: 'Command input',\n        commandRule: 'Enter the correct docker run container creation command!',\n        commandHelper: 'This command will run on the server to create the container. Continue?',\n        updateHelper1: 'Detected that this container comes from the app store. Please note the following two points:',\n        updateHelper2:\n            '1. The current modifications will not be synchronized to the installed applications in the app store.',\n        updateHelper3:\n            '2. If you modify the application on the installed page, the currently edited content will become invalid.',\n        updateHelper4: 'Editing the container requires rebuilding, and any non-persistent data will be lost. Continue?',\n        containerList: 'Container list',\n        operatorHelper: 'Run {0} on the following container. Continue?',\n        operatorAppHelper: 'Run \"{0}\" on the following container(s). Running services may be affected. Continue?',\n        containerDeleteHelper:\n            \"Detected that the container is from the App Store. Deleting the container will not completely remove it from 1Panel. To delete it completely, go to the App Store -> 'Installed' or 'Runtime Environment' menus. Continue?\",\n        start: 'Start',\n        stop: 'Stop',\n        restart: 'Restart',\n        kill: 'Kill',\n        pause: 'Pause',\n        unpause: 'Resume',\n        rename: 'Rename',\n        remove: 'Remove',\n        removeAll: 'Remove all',\n        containerPrune: 'Prune',\n        containerPruneHelper1: 'This will delete all containers that are in stopped state.',\n        containerPruneHelper2:\n            'If the containers are from the app store, you need to go to \"App Store -> Installed\" and click the \"Rebuild\" button to reinstall them after performing the cleanup.',\n        containerPruneHelper3: 'This operation cannot be undone. Continue?',\n        imagePrune: 'Prune',\n        imagePruneSome: 'Clean unlabeled',\n        imagePruneSomeEmpty: 'No images with the \"none\" tag can be cleaned.',\n        imagePruneSomeHelper: 'Clean the images with the tag \"none\" that are not used by any containers.',\n        imagePruneAll: 'Clean unused',\n        imagePruneAllEmpty: 'No unused images can be cleaned.',\n        imagePruneAllHelper: 'Clean the images that are not used by any containers.',\n        networkPrune: 'Prune',\n        networkPruneHelper: 'This will remove all unused networks. Continue?',\n        volumePrune: 'Prune',\n        volumePruneHelper: 'This will remove all unused local volumes. Continue?',\n        cleanSuccess: 'Completed successfully, the number of this cleanup: {0}!',\n        cleanSuccessWithSpace:\n            'Completed successfully. The number of disks cleaned this time is {0}. The disk space freed is {1}!',\n        unExposedPort: 'The current port mapping address is 127.0.0.1, which cannot enable external access.',\n        upTime: 'Uptime',\n        fetch: 'Fetch',\n        lines: 'Lines',\n        linesHelper: 'Enter the correct number of logs to retrieve!',\n        downloadLinesHelper: 'Select or enter the number of log lines to download.',\n        lastDay: 'Last day',\n        last4Hour: 'Last 4 hours',\n        lastHour: 'Last hour',\n        last10Min: 'Last 10 minutes',\n        cleanLog: 'Clean log',\n        cleanLogscope: 'Cleanup Type',\n        downLogHelper1: 'This will download all logs from container {0}. Continue?',\n        downLogHelper2: 'This will download the latest {1} logs from container {0}. Continue?',\n        cleanLogHelper: 'This will require restarting the container and cannot be undone. Continue?',\n        newName: 'New name',\n        workingDir: 'Working Dir',\n        source: 'Resource usage',\n        cpuUsage: 'CPU usage',\n        cpuTotal: 'CPU total',\n        core: 'Core',\n        memUsage: 'Memory usage',\n        memTotal: 'Memory limit',\n        memCache: 'Memory cache',\n        loadSize: 'Get Container Size',\n        ip: 'IP address',\n        cpuShare: 'CPU shares',\n        cpuShareHelper:\n            'Container engine uses a base value of 1024 for CPU shares. You can increase it to give the container more CPU time.',\n        inputIpv4: 'Example: 192.168.1.1',\n        inputIpv6: 'Example: 2001:0db8:85a3:0000:0000:8a2e:0370:7334',\n        diskUsage: 'Disk Usage',\n        localVolume: 'Local Storage Volume',\n        buildCache: 'Build Cache',\n        usage: 'Used: {0}, Releasable: {1}',\n        clean: 'Release',\n        imageClean: 'Clean up images will delete all unused images. This operation cannot be rolled back. Continue?',\n        containerClean:\n            'Clean up containers will delete all stopped containers (including stopped apps from App Store). This operation cannot be rolled back. Continue?',\n        sizeRw: 'Layer Size',\n        sizeRwHelper: 'Size of the writable layer unique to the container',\n        sizeRootFs: 'Virtual Size',\n        sizeRootFsHelper: 'Total size of all image layers the container depends on + container layer',\n        containerFromAppHelper:\n            'Detected that this container originates from the app store. App operations may cause current edits to be invalidated.',\n        containerFromAppHelper1:\n            'Click the [Param] button in the installed applications list to enter the editing page and modify the container name.',\n        command: 'Command',\n        console: 'Container interaction',\n        tty: 'Allocate a pseudo-TTY (-t)',\n        openStdin: 'Keep STDIN open even if not attached (-i)',\n        custom: 'Custom',\n        emptyUser: 'When empty, you will log in as  default',\n        privileged: 'Privileged',\n        privilegedHelper:\n            'Allow the container to perform certain privileged operations on the host, which may increase container risks. Use with caution!',\n        upgradeHelper: 'Repository Name/Image Name: Image Version',\n        upgradeWarning2:\n            'The upgrade operation requires rebuilding the container, any unpersisted data will be lost. Continue?',\n        oldImage: 'Current image',\n        sameImageContainer: 'Same-image containers',\n        sameImageHelper: 'Containers using the same image can be batch upgraded after selection',\n        targetImage: 'Target image',\n        imageLoadErr: 'No image name detected for the container',\n        imageUpdateTagEmpty: 'No updatable image tags detected',\n        appHelper: 'The container comes from the app store, and upgrading may make the service unavailable.',\n        resource: 'Resource',\n        input: 'Manually input',\n        forcePull: 'Always pull image ',\n        forcePullHelper: 'This will ignore existing images on the server and pull the latest image from the registry.',\n        imageUpdateHelper: 'Check the same tag in registry, and pull to update local image only if changed.',\n        server: 'Host',\n        serverExample: '80, 80-88, ip:80 or ip:80-88',\n        containerExample: '80 or 80-88',\n        exposePort: 'Expose port',\n        exposeAll: 'Expose all',\n        cmdHelper: 'Example: nginx -g \"daemon off;\"',\n        entrypointHelper: 'Example: docker-entrypoint.sh',\n        autoRemove: 'Auto remove',\n        cpuQuota: 'Number of CPU cores',\n        memoryLimit: 'Memory',\n        limitHelper: 'If set to 0, it means that there is no limitation. The maximum value is {0}',\n        macAddr: 'MAC Address',\n        mount: 'Mount',\n        volumeOption: 'Volume',\n        hostOption: 'Host',\n        serverPath: 'Server path',\n        containerDir: 'Container path',\n        volumeHelper: 'Ensure that the content of the storage volume is correct',\n        networkEmptyHelper: 'Please confirm the container network selection is correct',\n        modeRW: 'RW',\n        modeR: 'R',\n        sharedLabel: 'Propagation Mode',\n        private: 'Private',\n        privateHelper: 'Mount changes in the container and host do not affect each other',\n        rprivate: 'Recursive Private',\n        rprivateHelper: 'All mounts in the container are completely isolated from the host',\n        shared: 'Shared',\n        sharedHelper: 'Mount changes in the host and container are visible to each other',\n        rshared: 'Recursive Shared',\n        rsharedHelper: 'All mount changes in the host and container are visible to each other',\n        slave: 'Slave',\n        slaveHelper: 'The container can see host mount changes, but its own changes do not affect the host',\n        rslave: 'Recursive Slave',\n        rslaveHelper: 'All mounts in the container can see host changes, but do not affect the host',\n        mode: 'Mode',\n        env: 'Environments',\n        restartPolicy: 'Restart policy',\n        always: 'always',\n        unlessStopped: 'unless-stopped',\n        onFailure: 'on-failure (five times by default)',\n        no: 'never',\n        refreshTime: 'Refresh interval',\n        cache: 'Cache',\n        image: 'Image | Images',\n        imagePull: 'Pull',\n        imagePullHelper:\n            'Supports selecting multiple images to pull, press Enter after entering each image to continue',\n        imagePush: 'Push',\n        imagePushHelper:\n            'Detected that this image has multiple tags. Please confirm that the image name used for pushing is: {0}',\n        imageDelete: 'Image delete',\n        repoName: 'Container registry',\n        imageName: 'Image name',\n        pull: 'Pull',\n        path: 'Path',\n        importImage: 'Import',\n        buildArgs: 'Build Arguments',\n        imageBuild: 'Build',\n        pathSelect: 'Path',\n        label: 'Label',\n        imageTag: 'Image tag',\n        imageTagHelper: 'Supports setting multiple image tags, press Enter after entering each tag to continue',\n        push: 'Push',\n        fileName: 'Filename',\n        export: 'Export',\n        exportImage: 'Image export',\n        size: 'Size',\n        tag: 'Tags',\n        tagHelper: 'One per line. For example,\\nkey1=value1\\nkey2=value2',\n        imageNameHelper: 'Image name and Tag, for example: nginx:latest',\n        cleanBuildCache: 'Clean build cache',\n        delBuildCacheHelper:\n            'This will delete all cached artifacts that are generated during builds and cannot be undone. Continue?',\n        urlWarning: 'The URL prefix does not need to include http:// or https://. Please modify.',\n        network: 'Network | Networks',\n        networkHelper: 'This may cause some applications and runtime environments to not work properly. Continue?',\n        networkName: 'Name',\n        driver: 'Driver',\n        option: 'Option',\n        attachable: 'Attachable',\n        parentNetworkCard: 'Parent Network Card',\n        subnet: 'Subnet',\n        scope: 'IP scope',\n        gateway: 'Gateway',\n        auxAddress: 'Exclude IP',\n        volume: 'Volume | Volumes',\n        volumeDir: 'Volume directory',\n        nfsEnable: 'Enable NFS storage',\n        nfsAddress: 'Address',\n        mountpoint: 'Mountpoint',\n        mountpointNFSHelper: 'e.g. /nfs, /nfs-share',\n        options: 'Options',\n        repo: 'Repository | Repositories',\n        httpRepoHelper: 'Operating an HTTP-type repository requires restarting the Docker service.',\n        httpRepo: 'Choosing HTTP protocol requires restarting the Docker service to add it into insecure registries.',\n        delInsecure: 'Deletion of credit',\n        delInsecureHelper: 'This will restart Docker service to remove it from insecure registries. Continue?',\n        downloadUrl: 'Server',\n        imageRepo: 'Image repo',\n        repoHelper: 'Does it include a mirror repository/organization/project?',\n        auth: 'Require authentication',\n        mirrorHelper:\n            'If there are multiple mirrors, newlines must be displayed, for example:\\nhttp://xxxxxx.m.daocloud.io \\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper:\n            'If multiple private repositories exist, newlines must be displayed, for example:\\n172.16.10.111:8081 \\n172.16.10.112:8081',\n        compose: 'Compose | Composes',\n        composeFile: 'Compose File',\n        fromChangeHelper: 'Switching the source will clean the current edited content. Continue?',\n        composePathHelper: 'Configuration file save path: {0}',\n        composeHelper:\n            'The composition created through 1Panel editor or template will be saved in the {0}/docker/compose directory.',\n        deleteFile: 'Delete file',\n        deleteComposeHelper:\n            'Delete all files related to container compose, including configuration files and persistent files. Please proceed with caution!',\n        deleteCompose: '\" Delete this composition.',\n        composeDirectory: 'Compose directory',\n        template: 'Template',\n        composeTemplate: 'Compose template | Compose templates',\n        content: 'Content',\n        contentEmpty: 'Compose content cannot be empty, enter and try again!',\n        containerNumber: 'Container number',\n        containerStatus: 'Container status',\n        exited: 'Exited',\n        running: 'Running ( {0} / {1} )',\n        composeDetailHelper:\n            'The compose is created external to 1Panel. The start and stop operations are not supported.',\n        composeOperatorHelper: '{1} operation will be performed on {0}. Continue?',\n        composeDownHelper: 'This will stop and remove all containers and networks under the {0} compose. Continue?',\n        composeEnvHelper2:\n            'This orchestration was created by the 1Panel App Store. Please modify environment variables in the installed applications.',\n        composeContentPlaceholder: 'Write or paste the content of your docker-compose file here',\n\n        setting: 'Setting | Settings',\n        goSetting: 'Go to edit',\n        restartHelper: 'Configuration changes require restarting the Docker service to take effect',\n        operatorStatusHelper: 'This will \"{0}\" the Docker service. Continue?',\n        dockerStatus: 'Docker Service',\n        daemonJsonPathHelper: 'Ensure that the configuration path is the same as that specified in docker.service.',\n        mirrors: 'Registry mirrors',\n        mirrorsHelper: '',\n        mirrorsHelper2: 'For details, see the official documents. ',\n        registries: 'Insecure registries',\n        ipv6Helper:\n            'When enabling IPv6, you need to add an IPv6 container network. Refer to the official documentation for specific configuration steps.',\n        ipv6CidrHelper: 'IPv6 address pool range for containers',\n        ipv6TablesHelper: 'Automatic configuration of Docker IPv6 for iptables rules.',\n        experimentalHelper:\n            'Enabling ip6tables requires this configuration to be turned on; otherwise, ip6tables will be ignored',\n        cutLog: 'Log option',\n        cutLogHelper1: 'The current configuration will only affect newly created containers.',\n        cutLogHelper2: 'Existing containers need to be recreated for the configuration to take effect.',\n        cutLogHelper3:\n            'Please note that recreating containers may result in data loss. If your containers contain important data, make sure to backup before performing the rebuilding operation.',\n        maxSize: 'Max size',\n        maxFile: 'Max file',\n        liveHelper:\n            'By default, when the Docker daemon terminates, it shuts down running containers. You can configure the daemon so that containers remain running if the daemon becomes unavailable. This functionality is called live restore. The live restore option helps reduce container downtime due to daemon crashes, planned outages, or upgrades.',\n        liveWithSwarmHelper: 'live-restore daemon configuration is incompatible with swarm mode.',\n        iptablesDisable: 'Close iptables',\n        iptablesHelper1: 'Automatic configuration of iptables rules for Docker.',\n        iptablesHelper2:\n            'Disabling iptables will result in the containers being unable to communicate with external networks.',\n        daemonJsonPath: 'Conf Path',\n        serviceUnavailable: 'Docker service is not started at present.',\n        startIn: ' to start',\n        sockPath: 'Unix domain socket',\n        sockPathHelper: 'Communication channel between Docker daemon and the client.',\n        sockPathHelper1: 'Default path: /var/run/docker-x.sock',\n        sockPathMsg: 'Saving Socket Path may make Docker unavailable. Continue?',\n        sockPathErr: 'Select or enter the correct Docker sock file path',\n        related: 'Related',\n        includeAppstore: 'Show containers from the app store',\n        excludeAppstore: 'Hide App Store Container',\n        cleanDockerDiskZone: 'Clean up disk space used by Docker',\n        cleanImagesHelper: '( Clean up all images that are not used by any containers )',\n        cleanContainersHelper: '( Clean up all stopped containers )',\n        cleanVolumesHelper: '( Clean up all unused local volumes )',\n        makeImage: 'Create image',\n        newImageName: 'New image name',\n        commitMessage: 'Commit message',\n        author: 'Author',\n        ifPause: 'Pause Container During Creation',\n        ifMakeImageWithContainer: 'Create New Image from This Container?',\n        finishTime: 'Last stop time',\n        exportHelper: 'Export selected images to one tar file.',\n    },\n    cronjob: {\n        importHelper:\n            'Duplicate scheduled tasks will be automatically skipped during import. Tasks will be set to [Disabled] status by default, and set to [Pending Edit] status when data association is abnormal.',\n        changeStatus: 'Change status',\n        disableMsg: 'This will stop the scheduled task from automatically executing. Continue?',\n        enableMsg: 'This will allow the scheduled task to automatically execute. Continue?',\n        taskType: 'Type',\n        nextTime: 'Next 5 executions',\n        record: 'Records',\n        viewRecords: 'View records',\n        shell: 'Shell',\n        stop: 'Manual Stop',\n        stopHelper: 'This operation will force stop the current task execution. Continue?',\n        log: 'Backup logs',\n        logHelper: 'Backup system log',\n        logHelper1: '1. 1Panel system log',\n        logHelper2: '2. SSH login log of the server ',\n        logHelper3: '3. All site logs ',\n        containerCheckBox: 'In container (no need to enter the container command)',\n        containerName: 'Container name',\n        ntp: 'Time synchronization',\n        ntp_helper: 'You can configure the NTP server on the Quick Setup page of the Toolbox.',\n        app: 'Backup app',\n        website: 'Backup website',\n        rulesHelper:\n            'When there are multiple compression exclusion rules, they need to be displayed with line breaks. For example,\\n*.log \\n*.sql',\n        lastRecordTime: 'Last execution time',\n        all: 'All',\n        failedRecord: 'Failure records',\n        successRecord: 'Successful records',\n        database: 'Backup database',\n        backupArgs: 'Backup Arguments',\n        backupArgsHelper:\n            'Unlisted backup arguments can be manually entered and selected. For example: Enter --no-data and select the first option from the dropdown list.',\n        singleTransaction: 'Backup InnoDB tables using a single transaction, suitable for large-volume data backups',\n        quick: 'Read data row by row instead of loading the entire table into memory, suitable for large-volume data and low-memory machine backups',\n        skipLockTables: 'Backup without locking all tables, suitable for highly concurrent databases',\n        missBackupAccount: 'The backup account could not be found',\n        syncDate: 'Synchronization time ',\n        clean: 'Cache clean',\n        curl: 'Access URL',\n        taskName: 'Name',\n        cronSpec: 'Trigger cycle',\n        cronSpecDoc:\n            'Custom execution cycles only support the [minute hour day month week] format, e.g., 0 0 * * *. For details, see the official documentation.',\n        cronSpecHelper: 'Enter the correct execution period',\n        cleanHelper: 'This action records all job runs, backup files, and log files. Continue?',\n        backupContent: 'Backup content',\n        directory: 'Backup directory',\n        sourceDir: 'Backup directory',\n        snapshot: 'System snapshot',\n        allOptionHelper:\n            'The current task plan is to back up all [{0}]. Direct download is not supported at the moment. You can check the backup list of [{0}] menu.',\n        exclusionRules: 'Exclusive rule',\n        exclusionRulesHelper:\n            'Select or enter exclusion rules, press Enter after each set to continue. Exclusion rules will apply to all compression operations in this backup',\n        default_download_path: 'Default download link',\n        saveLocal: 'Retain local backups (the same as the number of cloud storage copies)',\n        url: 'URL Address',\n        urlHelper: 'Enter a valid URL address',\n        targetHelper: 'Backup accounts are maintained in panel settings.',\n        withImageHelper: 'Backup app store images, but this will increase the snapshot file size.',\n        ignoreApp: 'Exclude apps',\n        withImage: 'Backup Application Image',\n        retainCopies: 'Retain records',\n        retryTimes: 'Retry Attempts',\n        timeout: 'Timeout',\n        ignoreErr: 'Ignore errors',\n        ignoreErrHelper: 'Ignore errors during backup to ensure all backup tasks complete',\n        retryTimesHelper: '0 means no retry after failure',\n        retainCopiesHelper: 'Number of copies to retain for execution records and logs',\n        retainCopiesHelper1: 'Number of copies to retain for backup files',\n        retainCopiesUnit: ' copies (View)',\n        cronSpecRule: 'The execution period format in line {0} is incorrect. Please check and try again!',\n        cronSpecRule2: 'Execution period format is incorrect, check and try again!',\n        perMonthHelper: 'Execute on the {0} day of every month at {1}:{2}',\n        perWeekHelper: 'Execute every week on {0} at {1}:{2}',\n        perDayHelper: 'Execute every day at {0}:{1}',\n        perHourHelper: 'Execute every hour at {0} minutes',\n        perNDayHelper: 'Execute every {0} days at {1}:{2}',\n        perNHourHelper: 'Execute every {0} hours at {1}',\n        perNMinuteHelper: 'Execute every {0} minutes',\n        perNSecondHelper: 'Execute every {0} seconds',\n        perMonth: 'Every month',\n        perWeek: 'Every week',\n        perHour: 'Every hour',\n        perNDay: 'Every N day(s)',\n        perDay: 'Every day',\n        perNHour: 'Every N hour(s)',\n        perNMinute: 'Every N minute(s)',\n        perNSecond: 'Every N second(s)',\n        day: 'day(s)',\n        monday: 'Monday',\n        tuesday: 'Tuesday',\n        wednesday: 'Wednesday',\n        thursday: 'Thursday',\n        friday: 'Friday',\n        saturday: 'Saturday',\n        sunday: 'Sunday',\n        shellContent: 'Script',\n        executor: 'Executor',\n        errRecord: 'Incorrect logging',\n        errHandle: 'Cronjob execution failure',\n        noRecord: 'Trigger the Cron Job, and you will see the records here.',\n        cleanData: 'Clean data',\n        cleanRemoteData: 'Delete remote data',\n        cleanDataHelper: 'Delete the backup file generated during this task.',\n        noLogs: 'No task output yet...',\n        errPath: 'Backup path [{0}] error, cannot download!',\n        cutWebsiteLog: 'Website log rotation',\n        cutWebsiteLogHelper: 'The rotated log files will be backed up to the backup directory of 1Panel.',\n        syncIpGroup: 'Sync WAF IP groups',\n        requestExpirationTime: 'Upload request expiration time(Hours)',\n        unitHours: 'Unit: Hours',\n        alertTitle: 'Planned Task - {0} 「{1}」 Task Failure Alert',\n        library: {\n            script: 'Script',\n            syncNow: 'Sync Now',\n            turnOnSync: 'Enable Auto Sync',\n            turnOnSyncHelper:\n                'Enabling auto sync will perform automatic synchronization during early morning hours daily',\n            turnOffSync: 'Disable Auto Sync',\n            turnOffSyncHelper: 'Disabling auto sync may cause script synchronization delays, confirm?',\n            isInteractive: 'Interactive',\n            interactive: 'Interactive script',\n            interactiveHelper: 'Requires user input during execution and cannot be used in scheduled tasks.',\n            library: 'Script Library',\n            remoteLibrary: 'Remote Script Library',\n            create: 'Add Script',\n            edit: 'Edit Script',\n            groupHelper:\n                'Set different groups based on script characteristics, which allows for faster script filtering operations.',\n            handleHelper: 'Execute script {1} on {0}, continue?',\n            noSuchApp: 'The {0} service was not detected. Please install it quickly using the script library first!',\n            syncHelper: 'About to sync system script library. This operation only affects system scripts. Continue?',\n        },\n        cleanLog: 'Auto clean logs',\n        cleanLogscope: 'Log retention',\n    },\n    monitor: {\n        globalFilter: 'Global Filter',\n        enableMonitor: 'Monitoring Status',\n        storeDays: 'Retention Days',\n        defaultNetwork: 'Default Network Card',\n        defaultNetworkHelper: 'Default network card option displayed in monitoring and overview interfaces',\n        defaultIO: 'Default Disk',\n        defaultIOHelper: 'Default disk option displayed in monitoring and overview interfaces',\n        cleanMonitor: 'Clear Monitoring Records',\n        cleanHelper: 'This operation will clear all monitoring records including GPU. Continue?',\n        avgLoad: 'Load average',\n        loadDetail: 'Load detail',\n        resourceUsage: 'Utilization',\n        networkCard: 'Network interface',\n        read: 'Read',\n        write: 'Write',\n        readWriteCount: 'I/O operations',\n        readWriteTime: 'I/O latency',\n        today: 'Today',\n        yesterday: 'Yesterday',\n        lastNDay: 'Last {0} days',\n        lastNMonth: 'Last {0} months',\n        lastHalfYear: 'Last half year',\n        memory: 'Memory',\n        percent: 'Percentage',\n        cache: 'Cache',\n        disk: 'Disk',\n        network: 'Network',\n        up: 'Up',\n        down: 'Down',\n        interval: 'Collection Interval',\n        intervalHelper: 'Enter an appropriate monitoring collection interval (10 seconds - 12 hours)',\n    },\n    terminal: {\n        local: 'Local',\n        defaultConn: 'Default Connection',\n        defaultConnHelper:\n            'This operation will automatically connect to the node terminal after opening the terminal for 【{0}】. Continue?',\n        withReset: 'Reset Connection Information',\n        localConnJump:\n            'Default connection information is maintained in [Terminal - Settings]. If connection fails, please edit there!',\n        localHelper: 'The `local` name is used only for system local identification',\n        connLocalErr: 'Unable to automatically authenticate, fill in the local server login information.',\n        testConn: 'Test connection',\n        saveAndConn: 'Save and connect',\n        connTestOk: 'Connection information available',\n        connTestFailed: 'Connection unavailable, please check connection information.',\n        host: 'Host | Hosts',\n        createConn: 'New connection',\n        noHost: 'No host',\n        groupChange: 'Change group',\n        expand: 'Expand all',\n        fold: 'All contract',\n        batchInput: 'Batch processing',\n        quickCommand: 'Quick command | Quick commands',\n        noSuchCommand: 'No quick command data found in the imported CSV file, check and try again!',\n        quickCommandHelper: 'You can use the quick commands at the bottom of the \"Terminals -> Terminals\".',\n        groupDeleteHelper:\n            'After the group is removed, all connections in the group will be migrated to the default group. Continue?',\n        command: 'Command',\n        quickCmd: 'Quick command',\n        addHost: 'Add',\n        localhost: 'Localhost',\n        ip: 'Address',\n        authMode: 'Authentication',\n        passwordMode: 'Password',\n        rememberPassword: 'Remember authentication information',\n        keyMode: 'PrivateKey',\n        key: 'Private key',\n        keyPassword: 'Private key password',\n        emptyTerminal: 'No terminal is currently connected.',\n        lineHeight: 'Line Height',\n        letterSpacing: 'Letter Spacing',\n        fontSize: 'Font Size',\n        fontFamily: 'Font Set',\n        fontFamilySupportHelper:\n            'Select or input fonts. Actual rendering depends on fonts installed on the client OS/browser. If empty, the default font is used.',\n        backgroundColor: 'Background Color',\n        foregroundColor: 'Foreground Color',\n        cursorBlink: 'Cursor Blink',\n        cursorStyle: 'Cursor Style',\n        cursorUnderline: 'Underline',\n        cursorBlock: 'Block',\n        cursorBar: 'Bar',\n        scrollback: 'Scrollback',\n        scrollSensitivity: 'Scroll Sensitivity',\n        aiStatus: 'AI Terminal',\n        aiSettings: 'AI Terminal Settings',\n        aiAccountHelper:\n            'Use the selected model account to generate and fill commands. For local models like Ollama and vLLM, use a custom model account.',\n        aiAccountRequired: 'Please select an available AI account first.',\n        aiPrefix: 'Trigger Prefix',\n        aiPrefixHelper:\n            'When a line starts with this prefix and you press Enter, AI command generation will be triggered, for example # or //ai.',\n        aiRiskCommands: 'Risk Command Interception',\n        aiRiskCommandsHelper:\n            'Generated commands matching any of these fragments will be blocked and filled back as comments. Supports add, edit, and delete.',\n        aiAddRiskCommand: 'Add Risk Command',\n        aiRemoveRiskCommand: 'Delete',\n        aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',\n        aiPrefixAsciiVisible:\n            'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',\n        saveHelper: 'Are you sure you want to save the current terminal configuration?',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: 'For some installation and usage issues, see',\n        },\n        swap: {\n            swap: 'Swap Partition',\n            swapHelper1:\n                'The size of the swap should be 1 to 2 times the physical memory, adjustable based on specific requirements;',\n            swapHelper2:\n                'Before creating a swap file, ensure that the system disk has sufficient available space, as the swap file size will occupy the corresponding disk space;',\n            swapHelper3:\n                'Swap can help alleviate memory pressure, but it is only an alternative. Excessive reliance on swap may lead to a decrease in system performance. It is recommended to prioritize increasing memory or optimizing application memory usage;',\n            swapHelper4: 'It is advisable to regularly monitor the usage of swap to ensure normal system operation.',\n            swapDeleteHelper:\n                'This operation will remove the Swap partition {0}. For system security reasons, the corresponding file will not be automatically deleted. If deletion is required, please proceed manually!',\n            saveHelper: 'Please save the current settings first!',\n            saveSwap: 'Saving the current configuration will adjust the Swap partition {0} size to {1}. Continue?',\n            swapMin: 'The minimum partition size is 40 KB. Please modify and try again!',\n            swapMax: 'The maximum value for partition size is {0}. Please modify and try again!',\n            swapOff: 'The minimum partition size is 40 KB. Setting it to 0 will disable the Swap partition.',\n        },\n        device: {\n            dnsHelper: 'DNS server',\n            dnsAlert:\n                'Attention! Modifying the configuration of /etc/resolv.conf file will restore the file to its default values after system restart.',\n            dnsHelper1:\n                'When there are multiple DNS entries, they should be displayed on new lines. e.g.\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: 'Hostname resolution',\n            hosts: 'Domain',\n            hostAlert: 'Hidden commented records, click the All configuration button to view or set',\n            toolbox: 'Quick settings',\n            hostname: 'Hostname',\n            passwd: 'System password',\n            passwdHelper: 'Input characters cannot include $ and &',\n            timeZone: 'System time zone',\n            localTime: 'Server time',\n            timeZoneChangeHelper: 'Modifying the system time zone requires restarting the service. Continue?',\n            timeZoneHelper:\n                'If you don\\'t install \"timedatectl\" command, you may not change the time zone. Because system uses that command to change time zone.',\n            timeZoneCN: 'Beijing',\n            timeZoneAM: 'Los Angeles',\n            timeZoneNY: 'New York',\n            ntpALi: 'Alibaba',\n            ntpGoogle: 'Google',\n            syncSite: 'NTP server',\n            hostnameHelper:\n                'Hostname modification depends on the \"hostnamectl\" command. If the command is not installed, the modification may fail.',\n            userHelper:\n                'The username depends on the \"whoami\" command for retrieval. If the command is not installed, retrieval may fail.',\n            passwordHelper:\n                'Password modification depends on the \"chpasswd\" command. If the command is not installed, the modification may fail.',\n            hostHelper:\n                'There is an empty value in the provided content. Please check and try again after modification!',\n            dnsCheck: 'Test Availability',\n            dnsOK: 'DNS configuration information is available!',\n            dnsTestFailed: 'DNS configuration information is not available.',\n            syncSiteHelper: 'Sync system time with {0}. Continue?',\n        },\n        fail2ban: {\n            sshPort: 'Listen to SSH port',\n            sshPortHelper: 'Current Fail2ban listens to the SSH connection port of the host',\n            unActive: 'The Fail2ban service is not enabled at present.',\n            operation: 'You will perform operation \"{0}\" on Fail2ban service. Continue?',\n            fail2banChange: 'Fail2ban Configuration Modification',\n            ignoreHelper: 'The IP list in the allowlist will be ignored for blocking. Continue?',\n            bannedHelper: 'The IP list in the blocklist will be blocked by the server. Continue?',\n            maxRetry: 'Maximum retry attempts',\n            banTime: 'Ban time',\n            banTimeHelper: 'Default ban time is 10 minutes, -1 indicates permanent ban',\n            banTimeRule: 'Enter a valid ban time or -1',\n            banAllTime: 'Permanent ban',\n            findTime: 'Discovery period',\n            banAction: 'Ban action',\n            banActionOption: 'Ban specified IP addresses using {0}',\n            allPorts: ' (All Ports)',\n            ignoreIP: 'IP allowlist',\n            bannedIP: 'IP blocklist',\n            logPath: 'Log path',\n            logPathHelper: 'Default is /var/log/secure or /var/log/auth.log',\n        },\n        ftp: {\n            ftp: 'FTP account | FTP accounts',\n            notStart: 'FTP service is currently not running, start it first!',\n            operation: 'This will perform \"{0}\" operation on FTP service. Continue?',\n            noPasswdMsg: 'Cannot get the current FTP account password. Set the password and try again.',\n            enableHelper: 'Enabling the selected FTP account will restore its access permissions. Continue?',\n            disableHelper: 'Disabling the selected FTP account will revoke its access permissions. Continue?',\n            syncHelper: 'Sync FTP account data between server and database. Continue?',\n            dirSystem:\n                'This directory is system-reserved. Modification may cause system crash, please modify and try again!',\n            dirHelper: 'Enabling FTP requires directory permission changes - please choose carefully',\n            dirMsg: 'Enabling FTP will modify permissions for the entire {0} directory. Continue?',\n        },\n        clam: {\n            clam: 'Virus scan',\n            cron: 'Scheduled scan',\n            cronHelper: 'Professional version supports scheduled scan feature',\n            specErr: 'Execution schedule format error, please check and retry!',\n            disableMsg:\n                'Stopping scheduled execution will prevent this scan task from running automatically. Continue?',\n            enableMsg:\n                'Enabling scheduled execution will allow this scan task to run automatically at regular intervals. Continue?',\n            showFresh: 'Show signature updater service',\n            hideFresh: 'Hide signature updater service',\n            clamHelper:\n                'The minimum recommended configuration for ClamAV is: 3 GiB of RAM or more, single-core CPU with 2.0 GHz or higher, and at least 5 GiB of available hard disk space.',\n            notStart: 'ClamAV service is currently not running, start it first!',\n            removeRecord: 'Delete peport files',\n            noRecords: 'Click the \"Trigger\" button to start the scan and you will see records here.',\n            removeInfected: 'Delete virus files',\n            removeInfectedHelper:\n                'Delete virus files detected during the task to ensure server security and normal operation.',\n            clamCreate: 'Create scan rule',\n            infectedStrategy: 'Infected strategy',\n            removeHelper: 'Delete virus files, choose carefully!',\n            move: 'Move',\n            moveHelper: 'Move virus files to specified directory',\n            copyHelper: 'Copy virus files to specified directory',\n            none: 'Do nothing',\n            noneHelper: 'Take no action on virus files',\n            scanDir: 'Scan directory',\n            infectedDir: 'Infected directory',\n            scanDate: 'Scan Date',\n            scanResult: 'Scan logs tail',\n            tail: 'Lines',\n            infectedFiles: 'Infected files',\n            log: 'Details',\n            clamConf: 'Clam AV daemon',\n            clamLog: '@:toolbox.clam.clamConf logs',\n            freshClam: 'FreshClam',\n            freshClamLog: '@:toolbox.clam.freshClam logs',\n            alertHelper: 'Professional version supports scheduled scan and SMS alert',\n            alertTitle: 'Virus scan task 「{0}」 detected infected file alert',\n        },\n    },\n    logs: {\n        core: 'Panel Service',\n        agent: 'Node Monitoring',\n        panelLog: 'Panel logs',\n        operation: 'Operation logs',\n        login: 'Login logs',\n        loginIP: 'Login IP',\n        loginAddress: 'Login address',\n        loginAgent: 'Login agent',\n        loginStatus: 'Status',\n        system: 'System logs',\n        deleteLogs: 'Clean logs',\n        resource: 'Resource',\n        detail: {\n            dashboard: 'Overview',\n            ai: 'AI',\n            groups: 'Group',\n            hosts: 'Host',\n            apps: 'App',\n            openresty: 'OpenResty',\n            websites: 'Website',\n            containers: 'Container',\n            files: 'File',\n            runtimes: 'Runtime',\n            process: 'Process',\n            toolbox: 'Toolbox',\n            backups: 'Backup / Restore',\n            tampers: 'Tamper',\n            xsetting: 'Interface Settings',\n            logs: 'Log',\n            settings: 'Setting',\n            cronjobs: 'Cronjob',\n            databases: 'Database',\n            waf: 'WAF',\n            licenses: 'License',\n            nodes: 'Node',\n            commands: 'Quick Commands',\n        },\n        websiteLog: 'Website logs',\n        runLog: 'Run logs',\n        errLog: 'Error logs',\n        task: 'Task Log',\n        taskName: 'Task Name',\n        taskRunning: 'Running',\n    },\n    file: {\n        fileDirNum: '{0} directories, {1} files,',\n        currentDir: 'Directory',\n        dir: 'Folder',\n        fileName: 'File name',\n        search: 'Search',\n        mode: 'Permissions',\n        editPermissions: 'Edit @.lower:file.mode',\n        owner: 'Owner',\n        file: 'File',\n        remoteFile: 'Download from remote',\n        share: 'Share',\n        sync: 'Data Synchronization',\n        size: 'Size',\n        updateTime: 'Modified',\n        rename: 'Rename',\n        role: 'Permissions',\n        info: 'View attributes',\n        linkFile: 'Soft link',\n        shareList: 'Share list',\n        zip: 'Compressed',\n        group: 'Group',\n        path: 'Path',\n        public: 'Others',\n        setRole: 'Set permissions',\n        link: 'File link',\n        rRole: 'Read',\n        wRole: 'Write',\n        xRole: 'Executable',\n        name: 'Name',\n        compress: 'Compress',\n        deCompress: 'Decompress',\n        compressType: 'Compress format',\n        compressDst: 'Compress path',\n        replace: 'Overwrite existing files',\n        compressSuccess: 'Compressed successfully',\n        deCompressSuccess: 'Decompress succeeded',\n        deCompressDst: 'Decompress path',\n        linkType: 'Link type',\n        softLink: 'Soft link',\n        hardLink: 'Hard link',\n        linkPath: 'Link path',\n        selectFile: 'Select file',\n        downloadUrl: 'Remote URL',\n        downloadStart: 'Download started',\n        moveSuccess: 'Successfully moved',\n        copySuccess: 'Successfully copied',\n        pasteMsg: 'Click the [Paste] button at the top right of the target directory',\n        move: 'Move',\n        calculate: 'Calculate',\n        remark: 'Remark',\n        setRemark: 'Set remark',\n        remarkPrompt: 'Enter a remark',\n        remarkPlaceholder: 'Remark',\n        remarkToggle: 'Remarks',\n        remarkToggleTip: 'Load file remarks',\n        canNotDeCompress: 'Cannot decompress this file',\n        uploadSuccess: 'Successfully upload',\n        downloadProcess: 'Download progress',\n        downloading: 'Downloading...',\n        infoDetail: 'File properties',\n        root: 'Root directory',\n        list: 'File list',\n        sub: 'Include subdirectories',\n        downloadSuccess: 'Successfully downloaded',\n        theme: 'Theme',\n        language: 'Language',\n        eol: 'End of line',\n        copyDir: 'Copy',\n        paste: 'Paste',\n        changeOwner: 'Modify user and user group',\n        containSub: 'Apply the permission change recursively',\n        ownerHelper:\n            'The default user of the PHP operating environment: the user group is 1000:1000, it is normal that the users inside and outside the container show inconsistencies',\n        searchHelper: 'Support wildcards such as *',\n        uploadFailed: '[{0}] File upload failed',\n        fileUploadStart: 'Uploading [{0}]...',\n        currentSelect: 'Current selection: ',\n        unsupportedType: 'Unsupported file type',\n        deleteHelper:\n            'Are you sure you want to delete the following files? By default, it will enter the recycle bin after deletion',\n        fileHelper: \"Note:\\n1. Search results can't be sorted.\\n2. Folders can't be sorted by size.\",\n        forceDeleteHelper: 'Permanently delete the file (without entering the recycle bin, delete it directly)',\n        recycleBin: 'Recycle bin',\n        sourcePath: 'Original path',\n        deleteTime: 'Delete time',\n        confirmReduce: 'Are you sure you want to restore the following files?',\n        reduceSuccess: 'Restore successful',\n        reduce: 'Reduction',\n        reduceHelper:\n            'If a file or directory with the same name exists in the original path, it will be overwritten. Continue?',\n        clearRecycleBin: 'Clean',\n        clearRecycleBinHelper: 'clean the recycle bin?',\n        favorite: 'Favorites',\n        removeFavorite: 'Remove from favorites?',\n        addFavorite: 'Add/Remove to Favorites',\n        clearList: 'Clean list',\n        deleteRecycleHelper: 'Are you sure you want to permanently delete the following files?',\n        typeErrOrEmpty: '[{0}] file type is wrong or empty folder',\n        dropHelper: 'Drag the files you want to upload here',\n        fileRecycleBin: 'Enable recycle bin',\n        fileRecycleBinMsg: '{0} recycle bin',\n        wordWrap: 'Automatically wrap',\n        deleteHelper2:\n            'Are you sure you want to delete the selected file? The deletion operation cannot be rolled back',\n        ignoreCertificate: 'Allow insecure server connections',\n        ignoreCertificateHelper:\n            'Allowing inscure server connections may lead to data leak or tampering. Use this option only when trusting the download source.',\n        uploadOverLimit: 'The number of files exceeds 1000! Please compress and upload',\n        clashDidNotSupport: 'File names are prohibited from containing .1panel_clash',\n        clashDeleteAlert: 'The \"Recycle Bin\" folder can\\'t be deleted',\n        clashOpenAlert: 'Click the \"Recycle Bin\" button to open the recycle bin directory',\n        right: 'Forward',\n        back: 'Back',\n        top: 'Go Back',\n        up: 'Go back',\n        openWithVscode: 'Open with VS Code',\n        vscodeHelper: 'Please make sure that VS Code is installed locally and the SSH Remote plugin is configured',\n        saveContentAndClose: 'The file has been modified, do you want to save and close it?',\n        saveAndOpenNewFile: 'The file has been modified, do you want to save and open the new file?',\n        noEdit: 'The file has not been modified, no need to do this!',\n        noNameFolder: 'Untitled folder',\n        noNameFile: 'Untitled file',\n        minimap: 'Code mini map',\n        fileCanNotRead: 'File cannot read',\n        previewTruncated: 'File is too large, only showing the last part',\n        previewEmpty: 'File is empty or not a text file',\n        previewLargeFile: 'Preview',\n        panelInstallDir: \"1Panel installation directory can't be deleted\",\n        wgetTask: 'Download Task',\n        existFileTitle: 'Same name file prompt',\n        existFileHelper: 'The uploaded file contains a file with the same name, do you want to overwrite it?',\n        existFileSize: 'File size (new -> old)',\n        existFileDirHelper: 'The selected file/folder has a duplicate name. Please proceed with caution! \\n',\n        coverDirHelper: 'The selected folders to be replaced will be copied to the destination path!',\n        noSuchFile: 'The file or directory was not found. Please check and try again.',\n        setting: 'Setting',\n        showHide: 'Show hidden files',\n        noShowHide: 'Don’t show hidden files',\n        cancelUpload: 'Cancel Upload',\n        cancelUploadHelper: 'Whether to cancel the upload, after cancellation the upload list will be cleared.',\n        keepOneTab: 'Keep at least one tab',\n        notCanTab: 'Cannot add more tabs',\n        convert: 'Convert Format',\n        converting: 'Convert To',\n        fileCanNotConvert: 'This file does not support format conversion',\n        formatType: 'Format Type',\n        sourceFormat: 'Source Format',\n        sourceFile: 'Source File',\n        saveDir: 'Save Directory',\n        deleteSourceFile: 'Delete Source File',\n        convertHelper: 'Convert the selected files to another format',\n        convertHelper1: 'Select the files to be converted',\n        execConvert: 'Start conversion. You can view the conversion logs in the Tasks',\n        convertLogs: 'Conversion Logs',\n        formatConvert: 'Format Conversion',\n    },\n    ssh: {\n        autoStart: 'Auto start',\n        enable: 'Enable Autostart',\n        disable: 'Disable Autostart',\n        sshAlert:\n            'The list data is sorted based on login date. Changing time zone or performing other operations may cause deviations in the date of login logs.',\n        sshAlert2:\n            'You can use \"Fail2ban\" in the \"Toolbox\" to block IP addresses that attempt brute force attacks, and this will enhance the security of the host.',\n        sshOperate: 'Operation \"{0}\" on the SSH service will be performed. Continue?',\n        sshChange: 'SSH Setting',\n        sshChangeHelper: 'This action changed \"{0}\" to \"{1}\". Continue?',\n        sshFileChangeHelper:\n            'Modifying the configuration file may cause service availability. Exercise caution when performing this operation. Continue?',\n        port: 'Port',\n        portHelper: 'Specific the port that SSH service listens on.',\n        listenAddress: 'Listen address',\n        allV4V6: '0.0.0.0:{0}(IPv4) and :::{0}(IPv6)',\n        listenHelper:\n            'Leaving both of IPv4 and IPv6 settings blank will listen on \"0.0.0.0:{0}(IPv4)\" and \":::{0}(IPv6)\".',\n        addressHelper: 'Specify the address that SSH service listens on.',\n        permitRootLogin: 'Permit root',\n        rootSettingHelper: 'The default login method for root user is \"Allow SSH login\".',\n        rootHelper1: 'Allow SSH login',\n        rootHelper2: 'Disable SSH login',\n        rootHelper3: 'Only key login is allowed',\n        rootHelper4: 'Only predefined commands can be executed. No other operations can be performed.',\n        passwordAuthentication: 'Password auth',\n        pwdAuthHelper: 'Whether to enable password authentication. This parameter is enabled by default.',\n        pubkeyAuthentication: 'Key auth',\n        privateKey: 'Private Key',\n        publicKey: 'Public Key',\n        password: 'Password',\n        createMode: 'Creation Method',\n        generate: 'Auto-generate',\n        unSyncPass: 'Key password cannot be synchronized',\n        syncHelper: 'The sync operation will clean invalid keys and sync new complete key pairs. Continue?',\n        input: 'Manual Input',\n        import: 'File Upload',\n        authKeys: 'Authorization Keys',\n        authKeysHelper: 'Save current public key information?',\n        pubkey: 'Key info',\n        pubKeyHelper: 'The current key information only takes effect for user {0}',\n        encryptionMode: 'Encryption mode',\n        passwordHelper: 'Can contain 6 to 10 digits and English cases',\n        reGenerate: 'Regenerate key',\n        keyAuthHelper: 'Whether to enable key authentication.',\n        useDNS: 'useDNS',\n        dnsHelper:\n            'Control whether the DNS resolution function is enabled on the SSH server to verify the identity of the connection.',\n        analysis: 'Statistical information',\n        denyHelper:\n            \"Performing a 'deny' operation on the following addresses. After setting, the IP will be prohibited from accessing the server. Continue?\",\n        acceptHelper:\n            \"Performing an 'accept' operation on the following addresses. After setting, the IP will regain normal access. Continue?\",\n        noAddrWarning: 'No [{0}] addresses are currently selected. Please check and try again!',\n        loginLogs: 'SSH logs',\n        loginMode: 'Mode',\n        authenticating: 'Key',\n        publickey: 'Key',\n        belong: 'Belong',\n        local: 'Local',\n        session: 'Session | Sessions',\n        loginTime: 'Login time',\n        loginIP: 'Login IP',\n        stopSSHWarn: 'Whether to disconnect this SSH connection',\n        remote: 'Remote SSH',\n    },\n    setting: {\n        panel: 'Panel',\n        user: 'Panel user',\n        userChange: 'Change panel user',\n        userChangeHelper: 'Changing the panel user will log you out. Continue?',\n        passwd: 'Panel password',\n        emailHelper: 'For password retrieval',\n        watermark: 'Watermark Settings',\n        watermarkContent: 'Watermark Content',\n        contentHelper:\n            '{0} represents node name, {1} represents node address. You can use variables or fill in custom names.',\n        watermarkColor: 'Watermark Color',\n        watermarkFont: 'Watermark Font Size',\n        watermarkHeight: 'Watermark Height',\n        watermarkWidth: 'Watermark Width',\n        watermarkRotate: 'Rotation Angle',\n        watermarkGap: 'Spacing',\n        watermarkCloseHelper: 'Are you sure you want to turn off the system watermark settings?',\n        watermarkOpenHelper: 'Are you sure you want to save the current system watermark settings?',\n        title: 'Panel alias',\n        panelPort: 'Panel port',\n        titleHelper:\n            'Supports a length of 3 to 30 characters, including English letters, Chinese characters, numbers, spaces, and common special characters',\n        portHelper:\n            'The recommended port range is 8888 to 65535. Note: If the server has a security group, permit the new port from the security group in advance',\n        portChange: 'Port change',\n        portChangeHelper: 'Modify the service port and restart the service. Continue?',\n        theme: 'Theme',\n        menuTabs: 'Menu tabs',\n        componentSize: 'Component Size',\n        dark: 'Dark',\n        darkGold: 'Dark Gold',\n        light: 'Light',\n        auto: 'Follow System',\n        language: 'Language',\n        runtimeEnv: 'Runtime environment',\n        docSource: 'Document source',\n        withByRegion: 'Follow region (Default)',\n        withByLang: 'Follow system language',\n        region: 'Region',\n        cn: 'Mainland China',\n        intl: 'Global',\n        regionHelper: 'Changing the region will switch the following resource sources:',\n        regionHelper1: 'System installation packages',\n        regionHelper2: 'App Store and script library',\n        regionHelper3: 'User manual and related documents',\n        regionHelper4: 'This may affect future downloads and access. Proceed with caution.',\n        regionTip: 'Region affects the system update source and package download address.',\n        docSourceTip: 'Document source determines the language used for docs and release note links.',\n        languageHelper:\n            'By default, it follows the browser language. This parameter takes effect only on the current browser',\n        sessionTimeout: 'Session timeout',\n        sessionTimeoutError: 'The minimum session timeout is 300 seconds',\n        sessionTimeoutHelper:\n            'The panel will automatically be logged out if there is no operation for more than {0} second(s).',\n        systemIP: 'System address',\n        systemIPHelper:\n            'The address will be used for application redirection, container access, and other functions. Each node can be configured with a different address.',\n        proxy: 'Server proxy',\n        proxyHelper: 'It will be effective in the following scenarios after you set up the proxy server:',\n        proxyHelper1:\n            'Installation package download and synchronization from the app store (Professional edition only)',\n        proxyHelper2: 'System update and update information retrieval (Professional edition only)',\n        proxyHelper3: 'System license verification and synchronization',\n        proxyHelper4:\n            'Network requests such as Docker image pulls will go through the proxy server (Professional edition only)',\n        proxyHelper5: 'Unified download and sync for system-type script libraries (Professional)',\n        proxyHelper6: 'Apply for certificate (Professional)',\n        proxyType: 'Proxy type',\n        proxyUrl: 'Proxy Address',\n        proxyPort: 'Proxy Port',\n        proxyPasswdKeep: 'Remember Password',\n        proxyDocker: 'Docker Proxy',\n        proxyDockerHelper:\n            'Synchronize proxy server configuration to Docker, support offline server image pulling and other operations',\n        syncToNode: 'Sync to Node',\n        syncToNodeHelper: 'Sync settings to other nodes',\n        nodes: 'Node',\n        selectNode: 'Select Node',\n        selectNodeError: 'Select a node',\n        apiInterface: 'Enable API',\n        apiInterfaceClose: 'Once closed, API interfaces cannot be accessed. Continue?',\n        apiInterfaceHelper: 'Allow third-party applications to access the API.',\n        apiInterfaceAlert1: \"Don't enable it in production environments because it may increase server security risks.\",\n        apiInterfaceAlert2: \"Don't use third-party applications to call the API to prevent potential security threats.\",\n        apiInterfaceAlert3: 'API document:',\n        apiInterfaceAlert4: 'Usage document:',\n        apiKey: 'API key',\n        apiKeyHelper: 'API key is used for third-party applications to access the API.',\n        ipWhiteList: 'IP allowlist',\n        ipWhiteListEgs: 'One per line. For example,\\n172.161.10.111\\n172.161.10.0/24',\n        ipWhiteListHelper: 'IPs within the allowlist can access the API,0.0.0.0/0 (all IPv4), ::/0 (all IPv6)',\n        apiKeyValidityTime: 'Validity period of interface key',\n        apiKeyValidityTimeEgs: 'Validity period of interface key (in minutes)',\n        apiKeyValidityTimeHelper:\n            'The interface timestamp is valid if its difference from the current timestamp (in minutes) is within the allowed range. A value of 0 disables verification.',\n        apiKeyReset: 'Interface key reset',\n        apiKeyResetHelper: 'the associated key service will become invalid. Please add a new key to the service',\n        confDockerProxy: 'Configure docker proxy',\n        restartNowHelper: 'Configuring Docker proxy requires restarting the Docker service.',\n        restartNow: 'Restart immediately',\n        restartLater: 'Restart manually later',\n        systemIPWarning: 'The server address is not currently set. Set it in the control panel first.',\n        systemIPWarning1: 'The current server address is set to {0}, and quick redirection is not possible!',\n        syncTime: 'Server Time',\n        timeZone: 'Time Zone',\n        timeZoneChangeHelper: 'Changing the time zone requires restarting the service. Continue?',\n        timeZoneHelper:\n            'Timezone modification depends on the system timedatectl service. take effect after restart the 1Panel service.',\n        timeZoneCN: 'Bei Jing',\n        timeZoneAM: 'Los Angeles',\n        timeZoneNY: 'New York',\n        ntpALi: 'Alibaba',\n        ntpGoogle: 'Google',\n        syncSite: 'Ntp Server',\n        syncSiteHelper: 'This operation will use {0} as the source for system time synchronization. Continue?',\n        changePassword: 'Change Password',\n        oldPassword: 'Original password',\n        newPassword: 'New password',\n        retryPassword: 'Confirm password',\n        noSpace: 'Input information cannot include space characters',\n        duplicatePassword: 'The new password cannot be the same as the original password, enter again!',\n        diskClean: 'Cache clean',\n        developerMode: 'Preview Program',\n        developerModeHelper:\n            \"You will get to expirence new features and fixes before they're released broadly and give early feedback.\",\n        thirdParty: 'Third-party accounts',\n        scope: 'Scope',\n        public: 'Public',\n        publicHelper:\n            'Public type backup accounts will be synchronized to each sub-node, and sub-nodes can use them together',\n        private: 'Private',\n        privateHelper:\n            'Private type backup accounts are only created on the current node and are for the use of the current node only',\n        noTypeForCreate: 'No backup type is currently created',\n        LOCAL: 'Server disk',\n        OSS: 'Ali OSS',\n        S3: 'Amazon S3',\n        mode: 'Mode',\n        MINIO: 'MinIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'WebDAV connect Alist can refer to the official documentation',\n        UPYUN: 'UPYUN',\n        ALIYUN: 'Aliyun Drive',\n        ALIYUNHelper:\n            'The current maximum limit for non-client downloads on Aliyun Drive is 100 MB. Exceeding this limit requires downloading through the client.',\n        ALIYUNRecover:\n            'The current maximum limit for non-client downloads on Aliyun Drive is 100 MB. Exceeding this limit requires downloading through the client to the local device, then synchronizing the snapshot for recovery.',\n        GoogleDrive: 'Google Drive',\n        analysis: 'Analysis',\n        analysisHelper:\n            'Paste the entire token content to automatically parse the required parts. For specific operations, see the official documentation.',\n        serviceName: 'Service name',\n        operator: 'Operator',\n        OneDrive: 'Microsoft OneDrive',\n        isCN: 'Century Internet',\n        isNotCN: 'International Version',\n        client_id: 'Client ID',\n        client_secret: 'Client secret',\n        redirect_uri: 'Redirect URL',\n        onedrive_helper: 'Custom configuration can be referred to in the official documentation',\n        clickToRefresh: 'Click to refresh',\n        refreshTime: 'Token Refresh Time',\n        refreshStatus: 'Token Refresh Status',\n        backupDir: 'Backup directory',\n        codeWarning: 'The current authorization code format is incorrect, confirm again!',\n        code: 'Auth code',\n        codeHelper:\n            'Click on the \"Acquire\" button, then login to {0} and copy the content after \"code\" in the redirected link. Paste it into this input box. For specific instructions, see the official documentation.',\n        googleHelper:\n            'Please first create a Google application and obtain client information, fill in the form and click the get button. For specific operations, see the official documentation.',\n        loadCode: 'Acquire',\n        COS: 'Tencent COS',\n        ap_beijing_1: 'Beijing Zone 1',\n        ap_beijing: 'Beijing',\n        ap_nanjing: 'Nanjing',\n        ap_shanghai: 'Shanghai',\n        ap_guangzhou: 'Guangzhou',\n        ap_chengdu: 'Chengdu',\n        ap_chongqing: 'Chongqing',\n        ap_shenzhen_fsi: 'Shenzhen Financial',\n        ap_shanghai_fsi: 'Shanghai Financial',\n        ap_beijing_fsi: 'Beijing Financial',\n        ap_hongkong: 'Hong Kong, China',\n        ap_singapore: 'Singapore',\n        ap_mumbai: 'Mumbai',\n        ap_jakarta: 'Jakarta',\n        ap_seoul: 'Seoul',\n        ap_bangkok: 'Bangkok',\n        ap_tokyo: 'Tokyo',\n        na_siliconvalley: 'Silicon Valley (US West)',\n        na_ashburn: 'Ashburn (US East)',\n        na_toronto: 'Toronto',\n        sa_saopaulo: 'Sao Paulo',\n        eu_frankfurt: 'Frankfurt',\n        KODO: 'Qiniu Kodo',\n        scType: ' Storage type',\n        typeStandard: 'Standard',\n        typeStandard_IA: 'Standard_IA',\n        typeArchive: 'Archive',\n        typeDeep_Archive: 'Deep_Archive',\n        scLighthouse: 'Default, Lightweight object storage only supports this storage type',\n        scStandard:\n            'Standard storage, suitable for business scenarios with large amounts of frequently accessed hot files and frequent data interactions.',\n        scStandard_IA:\n            'Infrequent access storage, suitable for business scenarios with lower access frequency (e.g., average monthly access frequency of 1-2 times), minimum storage duration of 30 days.',\n        scArchive: 'Archival storage is suitable for business scenarios with extremely low access frequency.',\n        scDeep_Archive: 'Durable cold storage is suitable for business scenarios with extremely low access frequency.',\n        archiveHelper:\n            'Archival storage files cannot be downloaded directly and must first be restored through the corresponding cloud service provider`s website. Please use with caution!',\n        backupAlert:\n            'If a cloud provider is compatible with the S3 protocol, you can directly use Amazon S3 for backup. ',\n        domain: 'Accelerate domain',\n        backupAccount: 'Backup account | Backup accounts',\n        loadBucket: 'Get bucket',\n        accountName: 'Account name',\n        accountKey: 'Account key',\n        address: 'Address',\n        path: 'Path',\n        safe: 'Security',\n        passkey: 'Passkey',\n        passkeyManage: 'Manage',\n        passkeyKeyManagement: 'Key Management',\n        passkeyHelper: 'For quick login, up to 5 passkeys can be bound',\n        passkeyRequireSSL: 'Passkeys require a bound domain name and HTTPS access',\n        passkeyTrustedProxies: 'Trusted proxies',\n        passkeyTrustedProxiesHelper:\n            'Only requests from these IP/CIDR sources will trust Forwarded and X-Forwarded-Proto when determining HTTPS',\n        passkeyNotSupported: 'Current browser or environment does not support passkeys',\n        passkeyPrereqTitle: 'The following prerequisites are required for Passkey',\n        passkeyPrereqBindDomain: '1Panel domain binding configured',\n        passkeyPrereqHttps: 'Accessed securely via HTTPS',\n        passkeyPrereqBrowser: 'Environment supports Passkey (WebAuthn + platform authenticator)',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable:\n            'Use a system browser and make sure the panel is opened over HTTPS with a bound domain.',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            'This environment cannot use device authentication. Try a system browser on a newer OS version.',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            'Enable screen lock / fingerprint / face unlock / Windows Hello, then try again.',\n        passkeyPrereqBrowserDetailDetectFailed: 'Detection failed. Refresh and retry, or switch to a system browser.',\n        passkeyPrereqGoSetup: 'Configure',\n        passkeyCount: 'Bound {0}/{1}',\n        passkeyName: 'Name',\n        passkeyNameHelper: 'Enter a name to distinguish devices',\n        passkeyAdd: 'Add Passkey',\n        passkeyCreatedAt: 'Created At',\n        passkeyLastUsedAt: 'Last Used',\n        passkeyDeleteConfirm: 'After deletion, this passkey cannot be used to log in. Continue?',\n        passkeyLimit: 'Up to 5 passkeys can be bound',\n        passkeyFailed: 'Passkey registration failed, confirm that the panel SSL certificate is a trusted certificate',\n        bindInfo: 'Bind info',\n        bindAll: 'Listen All',\n        bindInfoHelper:\n            'Changing the service listening address or protocol may result in service unavailability. Continue?',\n        ipv6: 'Listen IPv6',\n        bindAddress: 'Listen address',\n        entrance: 'Entrance',\n        showEntrance: 'Show disabled alert in \"Overview\" page',\n        entranceHelper:\n            'Enabling security entrance will only allow logging in to the panel through specified security entrance.',\n        entranceError: 'Enter a secure login entry point of 5-116 characters, only numbers or letters are supported.',\n        entranceInputHelper: 'Leave it blank to disable the security entrance.',\n        randomGenerate: 'Random',\n        expirationTime: 'Expiration Date',\n        unSetting: 'Not set',\n        noneSetting:\n            'Set the expiration time for the panel password. After the expiration, you need to reset the password',\n        expirationHelper: 'If the password expiration time is [0] days, the password expiration function is disabled',\n        days: 'Expiration Days',\n        expiredHelper: 'The current password has expired. Please change the password again.',\n        timeoutHelper:\n            '[ {0} days ] The panel password is about to expire. After the expiration, you need to reset the password',\n        complexity: 'Complexity validation',\n        complexityHelper:\n            'After you enable it, the password validation rule will be: 8-30 characters, including English, numbers, and at least two special characters.',\n        bindDomain: 'Bind domain',\n        unBindDomain: 'Unbind domain',\n        panelSSL: 'Panel SSL',\n        unBindDomainHelper: 'The action of unbinding a domain name may cause system insecurity. Continue?',\n        bindDomainHelper: 'After you bind the domain, only that domain can access 1Panel service.',\n        bindDomainHelper1: 'Leave it blank to disable the domain name binding.',\n        bindDomainWarning:\n            'After domain binding, you will be logged out and can only access 1Panel service through the domain name specified in the settings. Continue?',\n        allowIPs: 'Authorized IP',\n        unAllowIPs: 'Unauthorized IP',\n        unAllowIPsWarning:\n            'Authorizing an empty IP will allow all IPs to access the system, which may cause system insecurity. Continue?',\n        allowIPsHelper:\n            'After you set the authorized IP address list, only the IP address in the list can access the panel service.',\n        allowIPsWarning:\n            'After you set the authorized IP address list, only the IP address in the list can access the panel service. Continue?',\n        allowIPsHelper1: 'Leave it blank to disable the IP address restriction.',\n        allowIPEgs: 'One per line. For example,\\n172.16.10.111\\n172.16.10.0/24',\n        mfa: 'Two-Factor Auth',\n        mfaClose: 'Disabling MFA will reduce the security of the service. Continue?',\n        secret: 'Secret',\n        mfaInterval: 'Refresh interval(s)',\n        mfaTitleHelper:\n            'The title is used to distinguish different 1Panel hosts. Scan again or manually add the secret key after you modify the title.',\n        mfaIntervalHelper:\n            'Scan again or manually add the secret key after changing the refresh interval.\\nSome authenticators only support a 30-second interval (for example, Google/Microsoft Authenticator).',\n        mfaAlert:\n            'One-time token is dynamically generated 6-digit number and based on the current time. Make sure that the server time is synchronized.',\n        mfaHelper: 'After you enabled it, the one-time token needs to be verified.',\n        mfaHelper1: 'Download an authenticator app, for example,',\n        mfaHelper2:\n            'To obtain the one-time token, scan the following QR code using your authenticator app or copy the secret key into your authentication app.',\n        mfaHelper3: 'Enter six digits from the app',\n        mfaCode: 'One-time token',\n        sslChangeHelper: 'Modify the https setting and restart the service. Continue?',\n        sslDisable: 'Disable',\n        sslDisableHelper:\n            'If the https service is disabled, you need to restart the panel for it to take effect. Continue?',\n        noAuthSetting: 'Unauthorized setting',\n        noAuthSettingHelper:\n            \"When users don't log in with specified security entrance, or don't access the panel from specified IP or domain name, this response can hid panel characteristic.\",\n        responseSetting: 'Response setting',\n        help200: 'Help Page',\n        error400: 'Bad Request',\n        error401: 'Unauthorized',\n        error403: 'Forbidden',\n        error404: 'Not Found',\n        error408: 'Request Timeout',\n        error416: 'Range Not Satisfiable',\n        error444: 'Connection Closed',\n        error500: 'Internal Server Error',\n        https: 'Setting HTTPS for the panel improves access security',\n        strictHelper: 'Non-HTTPS traffic cannot connect to the panel',\n        muxHelper:\n            \"The panel will listen on both HTTP and HTTPS protocols and redirect HTTP traffic to HTTPS, but this may reduce the panel's security\",\n        certType: 'Certificate type',\n        selfSigned: 'Self signed',\n        selfSignedHelper: 'Browsers may not trust self-signed certificates and may display security warnings.',\n        select: 'Select',\n        domainOrIP: 'Domain or IP:',\n        timeOut: 'Timeout',\n        rootCrtDownload: 'Root certificate download',\n        primaryKey: 'Primary key',\n        certificate: 'Certificate',\n        backupJump:\n            'Backup files not in the current backup list, please try downloading from the file directory and importing for backup.',\n        snapshot: 'Snapshot | Snaphshots',\n        noAppData: 'No system applications available for selection',\n        noBackupData: 'No backup data available for selection',\n        stepBaseData: 'Base Data',\n        stepAppData: 'System Application',\n        stepPanelData: 'System Data',\n        stepBackupData: 'Backup Data',\n        stepOtherData: 'Other Data',\n        operationLog: 'Retain Operation Log',\n        loginLog: 'Retain Access Log',\n        systemLog: 'Retain System Log',\n        taskLog: 'Retain Task Log',\n        monitorData: 'Retain Monitoring Data',\n        dockerConf: 'Retain Docker Configuration',\n        selectAllImage: 'Backup All Application Images',\n        logLabel: 'Log',\n        agentLabel: 'Node Configuration',\n        appDataLabel: 'Application Data',\n        appImage: 'Application Image',\n        appBackup: 'Application Backup',\n        backupLabel: 'Backup Directory',\n        confLabel: 'Configuration File',\n        dockerLabel: 'Container',\n        taskLabel: 'Scheduled Task',\n        resourceLabel: 'Application Resource Directory',\n        runtimeLabel: 'Runtime Environment',\n        appLabel: 'Application',\n        databaseLabel: 'Database',\n        snapshotLabel: 'Snapshot File',\n        websiteLabel: 'Website',\n        directoryLabel: 'Directory',\n        appStoreLabel: 'Application Store',\n        shellLabel: 'Script',\n        tmpLabel: 'Temporary Directory',\n        sslLabel: 'Certificate Directory',\n        reCreate: 'Failed to create snapshot',\n        reRollback: 'Rollback snapshot failed',\n        deleteHelper: 'All snapshot files including those in the third-party backup account will be deleted. Continue?',\n        status: 'Snapshot status',\n        ignoreRule: 'Ignore rule',\n        editIgnoreRule: '@:commons.button.edit @.lower:setting.ignoreRule',\n        ignoreHelper:\n            'This rule will be used to compress and backup the 1Panel data directory during creating snapshot. By default, socket files are ignored.',\n        ignoreHelper1: 'One per line. For example,\\n*.log\\n/opt/1panel/cache',\n        panelInfo: 'Write 1Panel basic information',\n        panelBin: 'Backup 1Panel system files',\n        daemonJson: 'Backup Docker configuration file',\n        appData: 'Backup installed apps from 1Panel',\n        panelData: 'Backup 1Panel data directory',\n        backupData: 'Backup local backup directory for 1Panel',\n        compress: 'Create Snapshot file',\n        upload: 'Upload snapshot file',\n        recoverDetail: 'Recover detail',\n        recoverFailed: 'Snapshot recovery failed',\n        importHelper: 'Snapshot directory: ',\n        lastRecoverAt: 'Last recovery time',\n        lastRollbackAt: 'Last rollback time',\n        reDownload: 'Download the backup file again',\n        recoverErrArch: 'Snapshot recovery between different server architectures is not supported!',\n        recoverErrSize: 'Detected insufficient disk space, please check or clean up and try again!',\n        recoverHelper: 'Starting recovery from snapshot {0}, confirm the following information before proceeding:',\n        recoverHelper1: 'Recovery requires restarting Docker and 1Panel services',\n        recoverHelper2:\n            'Please ensure there is sufficient disk space on the server (Snapshot file size: {0}, Available space: {1})',\n        recoverHelper3:\n            'Please ensure the server architecture matches the architecture of the server where the snapshot was created (Current server architecture: {0})',\n        rollback: 'Rollback',\n        rollbackHelper:\n            'Rolling back this recovery will replace all files from this recovery, and may require restarting Docker and 1Panel services. Continue?',\n        upgradeRecord: 'Upgrade record',\n        upgrading: 'Upgrading, please wait...',\n        upgradeHelper: 'The upgrade requires restarting the 1Panel service. Continue?',\n        noUpgrade: 'It is currently the latest version',\n        versionHelper:\n            'Name rules: [major version].[functional version].[Bug fix version], as shown in the following example:',\n        rollbackLocalHelper:\n            'The primary node does not support direct rollback. Please manually execute the [1pctl restore] command to rollback!',\n        upgradeCheck: 'Check for updates',\n        upgradeNotes: 'Release note',\n        upgradeNow: 'Upgrade now',\n        source: 'Download source',\n        versionNotSame: 'Node version mismatch with the main node. Please upgrade in Node Management before retrying.',\n        versionCompare:\n            'Detected that node {0} is already at the latest upgradable version. Please check the primary node version and try again!',\n        about: 'About',\n        versionItem: 'Current Version',\n        backupCopies: 'Backup Copies',\n        backupCopiesHelper: 'Set the number of upgrade backup copies to keep for version rollback. 0 means keep all.',\n        backupCopiesRule: 'Please keep at least 3 upgrade backup records',\n        release: 'Release Notes',\n        releaseHelper:\n            'Failed to fetch release notes for the current environment. You can manually check the official documentation.',\n        project: 'GitHub',\n        issue: 'Feedback',\n        doc: 'Official document',\n        star: 'Star',\n        description: 'Linux Server Panel',\n        forum: 'Discussions',\n        doc2: 'Docs',\n        currentVersion: 'Version',\n        license: 'License',\n        bindNode: 'Bind Node',\n        menuSetting: 'Menu Settings',\n        menuSettingHelper: 'When only 1 submenu exists, the menu bar will display only that submenu',\n        showAll: 'Show All',\n        hideALL: 'Hide All',\n        ifShow: 'Whether to Show',\n        menu: 'Menu',\n        confirmMessage: 'The page will be refreshed to update the advanced menu list. Continue?',\n        recoverMessage: 'The page will be refreshed and restore the menu list to its initial state. Continue?',\n        compressPassword: 'Compression password',\n        backupRecoverMessage: 'Enter the compression or decompression password (leave blank to not set)',\n    },\n    license: {\n        offLine: 'Offline',\n        community: 'OSS',\n        oss: 'Open Source Software',\n        pro: 'Pro',\n        trial: 'Trial',\n        add: 'Add Community Edition',\n        licenseBindHelper: 'Free node quotas can only be used when the license is bound to a node',\n        licenseAlert:\n            'Community Edition nodes can only be added when the license is properly bound to a node. Only nodes properly bound to the license support switching.',\n        licenseUnbindHelper: 'Community Edition nodes detected for this license. Please unbind and try again!',\n        subscription: 'Subscription',\n        perpetual: 'Perpetual',\n        versionConstraint: '{0} Version Buyout',\n        forceUnbind: 'Force Unbind',\n        forceUnbindHelper:\n            'Forcing unbind will ignore any errors that occur during the unbinding process and ultimately release the license binding.',\n        updateForce: 'Force update (ignore all errors during unbinding to ensure final operation succeeds)',\n        trialInfo: 'Version',\n        authorizationId: 'Subscription ID',\n        authorizedUser: 'Authorized user',\n        lostHelper:\n            'The license has reached the maximum number of retry attempts. Please manually click the sync button to ensure the professional version features are functioning properly.',\n        exceptionalHelper:\n            'License synchronization verification is abnormal. Please manually click the sync button to ensure the professional version functions properly. detail: ',\n        quickUpdate: 'Quick update',\n        import: 'Import',\n        power: 'Authorize',\n        unbindHelper: 'All Pro related Settings will be cleaned after unbinding. Continue? ',\n        importLicense: 'Import license',\n        importHelper: 'Click or drag the license file here',\n        levelUpPro: 'Upgrade to Professional Edition',\n        licenseSync: 'License Sync',\n        knowMorePro: 'Learn More',\n        closeAlert: 'The current page can be closed in the panel settings',\n        introduce: 'Feature Introduction',\n        waf: 'Upgrading to the professional version can provide features such as interception map, logs, block records, geographical location blocking, custom rules, custom interception pages, etc.',\n        tamper: 'Upgrading to the professional version can protect websites from unauthorized modifications or tampering.',\n        tamperHelper: 'Operation failed, the file or folder has tamper protection enabled. Please check and try again!',\n        setting:\n            'Upgrading to the professional version allows customization of panel logo, welcome message, and other information.',\n        monitor:\n            'Upgrade to the professional version to view the real-time status of the website, visitor trends, visitor sources, request logs and other information. ',\n        alert: 'Upgrade to the professional version to receive alarm information via SMS and view alarm logs, fully control various key events, and ensure worry-free system operation',\n        node: 'Upgrading to the Professional Edition allows you to manage multiple Linux servers with 1Panel.',\n        nodeApp:\n            'Upgrading to the Professional Edition allows unified upgrading of multi-node application versions without manually switching nodes.',\n        nodeDashboard:\n            'Upgrading to the Professional Edition enables centralized management of multi-node applications, websites, databases, and scheduled tasks.',\n        fileExchange: 'Upgrade to the Professional Edition to quickly transfer files between multiple servers.',\n        app: 'Upgrade to the professional version to view service information, abnormal monitoring, etc. through the mobile APP. ',\n        cluster:\n            'Upgrading to the Professional Edition allows you to manage MySQL/Postgres/Redis master-slave clusters.',\n        vllm: 'Upgrade to the Professional Edition to bring Vllm services under 1Panel management, with centralized deployment, configuration, lifecycle operations, and task tracking for local model workloads.',\n    },\n    clean: {\n        scan: 'Start scanning',\n        scanHelper: 'Easily clean up junk files produced during 1Panel runtime',\n        clean: 'Clean now',\n        reScan: 'Rescan',\n        cleanHelper: 'Selected files and directories cannot be rolled back after cleanup. Continue?',\n        statusSuggest: '(Recommended Cleaning)',\n        statusClean: '(Very clean)',\n        statusEmpty: 'Very clean, no cleaning needed!',\n        statusWarning: '(Proceed with Caution)',\n        lastCleanTime: 'Last Cleaned: {0}',\n        lastCleanHelper: 'Files and directories cleaned: {0}, total cleaned: {1}',\n        cleanSuccessful: 'Successfully cleaned',\n        currentCleanHelper: 'Files and directories cleaned in this session: {0}, Total cleaned: {1}',\n        suggest: '(Recommended)',\n        totalScan: 'Total junk files to be cleaned: ',\n        selectScan: 'Total selected junk files: ',\n        system: 'System Junk Files',\n        systemHelper: 'Temporary files generated during snapshot, upgrade, and other processes',\n        panelOriginal: 'Backup files before system snapshot restore',\n        upgrade: 'System upgrade backup files',\n        agentPackages: 'Historical version child node upgrade/installation packages',\n        upgradeHelper: '(Recommend keeping the latest upgrade backup for system rollback)',\n        snapshot: 'System snapshot temporary files',\n        rollback: 'Backup files before recover',\n        backup: 'System Backup',\n        backupHelper: 'Backup files not associated with local backup accounts',\n        unknownBackup: 'System Backup',\n        tmpBackup: 'Temporary Backup',\n        unknownApp: 'Unassociated App Backup',\n        unknownDatabase: 'Unassociated Database Backup',\n        unknownWebsite: 'Unassociated Website Backup',\n        unknownSnapshot: 'Unassociated Snapshot Backup',\n        upload: 'Temporary Upload Files',\n        uploadHelper: 'Temporary files uploaded from the system backup list',\n        download: 'Temporary Download Files',\n        downloadHelper: 'Temporary files downloaded from third-party backup accounts by the system',\n        directory: 'Directory',\n        systemLog: 'Log file',\n        systemLogHelper: 'System logs, task logs, website log files',\n        dockerLog: 'Container operation log files',\n        taskLog: 'Scheduled task execution log files',\n        shell: 'Shell script scheduled tasks',\n        containerShell: 'Shell script scheduled tasks executed inside containers',\n        curl: 'CURL scheduled tasks',\n        docker: 'Container garbage',\n        dockerHelper: 'Files such as containers, images, volumes, build cache, etc.',\n        volumes: 'Volumes',\n        buildCache: 'Container Build Cache',\n        appTmpDownload: 'App temporary download file',\n        unknownWebsiteLog: 'Unlinked website log backup file',\n    },\n    app: {\n        app: 'Application | Applications',\n        installName: 'Name',\n        installed: 'Installed',\n        all: 'All',\n        version: 'Version',\n        detail: 'Details',\n        params: 'Parameters',\n        author: 'Author',\n        source: 'Source',\n        appName: 'Application Name',\n        deleteWarn:\n            'The delete operation will delete all data and backups together. This operation cannot be rolled back. Continue? ',\n        syncSuccess: 'Synchronized successfully',\n        canUpgrade: 'Updates',\n        backupName: 'File Name',\n        backupPath: 'File Path',\n        backupdate: 'Backup time',\n        versionSelect: 'Select a version',\n        operatorHelper: 'Operation {0} will be performed on the selected application. Continue?',\n        startOperatorHelper: 'The application will be started. Continue?',\n        stopOperatorHelper: 'The application will be stopped. Continue?',\n        restartOperatorHelper: 'The application will be restarted. Continue?',\n        reloadOperatorHelper: 'The application will be reloaded. Continue?',\n        checkInstalledWarn: '\"{0}\" is not detected. Go to \"App Store\" to install.',\n        limitHelper: 'The application has already been installed.',\n        deleteHelper: '\"{0}\" has been associated with the following resource(s) and can\\'t be deleted',\n        checkTitle: 'Hint',\n        defaultConfig: 'Default configuration',\n        defaultConfigHelper: 'It has been restored to the default configuration, it will take effect after saving',\n        forceDelete: 'Force delete',\n        forceDeleteHelper:\n            'Force deletion will ignore errors during the deletion process and eventually delete metadata.',\n        deleteBackup: 'Delete backup',\n        deleteBackupHelper: 'Also delete the application backup',\n        deleteDB: 'Delete database',\n        deleteDBHelper: 'Also delete the database',\n        noService: 'No {0}',\n        toInstall: 'Go to install',\n        param: 'Parameters',\n        alreadyRun: 'Age',\n        syncAppList: 'Sync',\n        less1Minute: 'Less than 1 minute',\n        appOfficeWebsite: 'Website',\n        github: 'Github',\n        document: 'Document',\n        updatePrompt: 'No updates available',\n        installPrompt: 'No apps installed yet',\n        updateHelper: 'Editing parameters may cause the application to fail to start. Please proceed with caution.',\n        updateWarn: 'Update parameters need to rebuild the application, Continue? ',\n        busPort: 'Port',\n        syncStart: 'Start syncing! Please refresh the app store later',\n        advanced: 'Advanced settings',\n        cpuCore: 'core(s)',\n        containerName: 'Container name',\n        containerNameHelper: 'The container name will be automatically generated when not set',\n        allowPort: 'External access',\n        allowPortHelper: 'Allowing external port access will release the firewall port',\n        appInstallWarn:\n            'The application dosn\\'t expose the external access port by default. Click \"Advanced settings\" to expose it.',\n        upgradeStart: 'Start upgrading! Please refresh the page later',\n        toFolder: 'Open the installation directory',\n        editCompose: 'Edit compose file',\n        editComposeHelper: 'Editing the compose file may cause the software installation to fail',\n        composeNullErr: 'compose cannot be empty',\n        takeDown: 'TakeDown',\n        allReadyInstalled: 'Installed',\n        installHelper: 'If you have image pull issues, configure image acceleration.',\n        sortMode: 'Sort',\n        installWarn:\n            'The external access is not checked, and it will make the application unable to access through external network. Continue?',\n        showIgnore: 'View ignored applications',\n        cancelIgnore: 'Cancel ignore',\n        ignoreList: 'Ignored applications',\n        appHelper: 'Go to application details page to learn installation instruction for some special applications.',\n        backupApp: 'Backup application before upgrade',\n        backupAppHelper:\n            'If the upgrade fails, the backup will be automatically rolled back. Please check the failure reason in the log audit-system log',\n        openrestyDeleteHelper: 'Forced delete of OpenResty will delete all websites. Continue?',\n        downloadLogHelper1: 'All logs of {0} application are about to be downloaded. Continue? ',\n        downloadLogHelper2: 'The latest {1} logs of {0} application are about to be downloaded. Continue? ',\n        syncAllAppHelper: 'All applications will be synchronized. Continue? ',\n        hostModeHelper:\n            'The current application network mode is host mode. If you need to open the port, please open it manually on the firewall page.',\n        showLocal: 'Show local applications',\n        reload: 'Reload',\n        upgradeWarn:\n            'Upgrading the application will replace the docker-compose.yml file. If there are any changes, you can click to view the file comparison',\n        openclawHttpsUpgradeNoticeTitle:\n            'Note: The following instructions only apply to users upgrading OpenClaw from versions earlier than 2026.3.13:',\n        openclawHttpsUpgradeNoticeItem1:\n            'After the deployed agent is upgraded, go to Configuration -> Settings -> Security and manually add the access address.',\n        openclawHttpsUpgradeNoticeItem2:\n            'The new version now requires HTTPS to access the agent. If you previously used a reverse proxy website, change the proxy target to https://IP:Port.',\n        newVersion: 'New version',\n        oldVersion: 'Current version',\n        composeDiff: 'File comparison',\n        showDiff: 'View comparison',\n        useNew: 'Use custom version',\n        useDefault: 'Use default version',\n        useCustom: 'Customize docker-compose.yml',\n        useCustomHelper:\n            \"Using a custom docker-compose.yml file may cause the application upgrade to fail. If it is not necessary, don't check it.\",\n        diffHelper:\n            'The left side is the old version, the right side is the new version. After editing, click to save the custom version',\n        pullImage: 'Pull Image',\n        pullImageHelper: 'Execute docker pull to pull the image before the application starts',\n        deleteImage: 'Delete Image',\n        deleteImageHelper: 'Delete the image related to the application. The task will not stop if deletion fails',\n        requireMemory: 'Memory',\n        supportedArchitectures: 'Architectures',\n        link: 'Link',\n        showCurrentArch: 'Architecture',\n        syncLocalApp: 'Sync Local App',\n        memoryRequiredHelper: 'Current application memory requirement {0}',\n        gpuConfig: 'Enable GPU Support',\n        gpuConfigHelper:\n            'Please ensure the machine has an NVIDIA GPU and that NVIDIA drivers and the NVIDIA Docker Container Toolkit are installed',\n        webUI: 'Web Access Address',\n        webUIPlaceholder: 'For example: example.com:8080/login',\n        defaultWebDomain: 'Default Access Address',\n        defaultWebDomainHepler:\n            'If the application port is 8080, the redirect address will be http(s)://default access address:8080',\n        webUIConfig:\n            'The current node has no default access address configured. Please set it in application parameters or go to panel settings to configure!',\n        toLink: 'Open',\n        customAppHelper:\n            'Before installing a custom app store package, please ensure that there are no installed apps.',\n        forceUninstall: 'Force Uninstall',\n        syncCustomApp: 'Sync Custom App',\n        ignoreAll: 'Ignore all subsequent versions',\n        ignoreVersion: 'Ignore specified version',\n        specifyIP: 'Bind Host IP',\n        specifyIPHelper:\n            'Set the host address/network interface to bind the port (if you are not sure about this, please do not fill it in)',\n        uninstallDeleteBackup: 'Uninstall App - Delete Backup',\n        uninstallDeleteImage: 'Uninstall App - Delete Image',\n        upgradeBackup: 'Backup App Before Upgrade',\n        noAppHelper: 'No application detected, go to the task center to view the app store sync log',\n        isEdirWarn: 'Detected modification to docker-compose.yml file, please check the comparison',\n    },\n    website: {\n        primaryDomain: 'Primary domain',\n        otherDomains: 'Other domains',\n        static: 'Static',\n        deployment: 'Deployment',\n        supportUpType: 'Only .tar.gz file format is supported, and the compressed package must contain {0}.json file',\n        proxy: 'Reverse proxy',\n        alias: 'Alias',\n        ftpUser: 'FTP account',\n        ftpPassword: 'FTP password',\n        ftpHelper:\n            'After creating a website, a corresponding FTP account will be created and the FTP directory will link to the website directory.',\n        remark: 'Remark',\n        groupSetting: 'Group Management',\n        createGroup: 'Create group',\n        appNew: 'New Application',\n        appInstalled: 'Installed application',\n        delete: 'Delete Website',\n        deleteApp: 'Delete Application',\n        deleteBackup: 'Delete Backup',\n        domain: 'Domain',\n        domainHelper: 'One domain per line.\\nSupport wildcard \"*\" and IP address.\\nSupport adding port.',\n        addDomain: 'Add',\n        domainConfig: 'Domains',\n        defaultDoc: 'Document',\n        perserver: 'Concurrency',\n        perserverHelper: 'Limit the maximum concurrency of the current site',\n        perip: 'Single IP',\n        peripHelper: 'Limit the maximum number of concurrent access to a single IP',\n        rate: 'Traffic limits',\n        rateLimit: 'Per-request rate limit',\n        rateHelper: 'Limit the flow of each request (unit: KB)',\n        rateLimitHelper: 'Limit per-request transfer rate (unit: KB/s)',\n        limitHelper: 'Enable flow control',\n        other: 'Other',\n        currentSSL: 'Current Certificate',\n        dnsAccount: 'DNS account',\n        applySSL: 'Certificate Application',\n        SSLList: 'Certificate List',\n        createDnsAccount: 'DNS account',\n        aliyun: 'Aliyun DNS',\n        aliEsa: 'Aliyun ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: 'Manual parsing',\n        key: 'Key',\n        check: 'View',\n        acmeAccountManage: 'Manage ACME accounts',\n        email: 'Email',\n        acmeAccount: 'ACME account',\n        provider: 'Verification method',\n        dnsManual: 'Manual Resolution',\n        expireDate: 'Expiration date',\n        brand: 'Organization',\n        deploySSL: 'Deployment',\n        deploySSLHelper: 'Are you sure to deploy the certificate? ',\n        ssl: 'Certificate | Certificates',\n        dnsAccountManage: 'Manage DNS providers',\n        renewSSL: 'Renew',\n        renewHelper: 'Are you sure to renew the certificate? ',\n        renewSuccess: 'Renew certificate',\n        enableHTTPS: 'Enable',\n        aliasHelper: 'Alias is the directory name of the website',\n        lastBackupAt: 'last backup time',\n        null: 'none',\n        nginxConfig: 'Nginx configuration',\n        websiteConfig: 'Website settings',\n        proxySettings: 'Proxy Settings',\n        advancedSettings: 'Advanced Settings',\n        cacheSettings: 'Cache Settings',\n        sniSettings: 'SNI Settings',\n        basic: 'Basic',\n        source: 'Configuration',\n        security: 'Security',\n        nginxPer: 'Performance tuning',\n        neverExpire: 'Never',\n        setDefault: 'Set as default',\n        deleteHelper: 'Related application status is abnormal, please check',\n        toApp: 'Go to the installed list',\n        cycle: 'Cycle',\n        frequency: 'Frequency',\n        ccHelper:\n            'Accumulatively request the same URL more than {1} times within {0} seconds, trigger CC defense, block this IP',\n        mustSave: 'The modification needs to be saved to take effect',\n        fileExt: 'file extension',\n        fileExtBlock: 'file extension blocklist',\n        value: 'value',\n        enable: 'Enable',\n        proxyAddress: 'Proxy Address',\n        proxyHelper: 'Example: 127.0.0.1:8080',\n        forceDelete: 'Force Delete',\n        forceDeleteHelper:\n            'Force deletion will ignore errors during the deletion process and eventually delete metadata.',\n        deleteAppHelper: 'Delete associated applications and application backups at the same time',\n        deleteBackupHelper: 'Also delete website backups.',\n        deleteDatabaseHelper: 'Also delete the database associated with the website',\n        deleteConfirmHelper:\n            'The delete operation cannot be undone. Enter <span style=\"color:red\"> \"{0}\" </span> to confirm deletion.',\n        staticPath: 'The corresponding main directory is ',\n        limit: 'Scheme',\n        blog: 'Forum/Blog',\n        imageSite: 'Picture Site',\n        downloadSite: 'Download Site',\n        shopSite: 'Mall',\n        doorSite: 'Portal',\n        qiteSite: 'Enterprise',\n        videoSite: 'Video',\n        errLog: 'Error log',\n        stopHelper:\n            'After stopping the site, it will not be able to access normally, and the user will display the stop page of the current site when visiting. Continue? ',\n        startHelper: 'After enabling the site, users can access the content of the site normally, continue? ',\n        sitePath: 'Directory',\n        siteAlias: 'Site alias',\n        primaryPath: 'Root directory',\n        folderTitle: 'The website mainly contains the following folders',\n        wafFolder: 'Firewall rules',\n        indexFolder: 'Website root directory',\n        sslFolder: 'Website Certificate',\n        enableOrNot: 'Enable',\n        oldSSL: 'Existing certificate',\n        manualSSL: 'Import certificate',\n        select: 'Select',\n        selectSSL: 'Select Certificate',\n        privateKey: 'Key (KEY)',\n        certificate: 'Certificate (PEM format)',\n        HTTPConfig: 'HTTP Options',\n        HTTPSOnly: 'Block HTTP requests',\n        HTTPToHTTPS: 'Redirect to HTTPS',\n        HTTPAlso: 'Allow direct HTTP requests',\n        sslConfig: 'SSL options',\n        disableHTTPS: 'Disable HTTPS',\n        disableHTTPSHelper: 'Disabling HTTPS will delete the certificate related configuration, Continue?',\n        SSLHelper:\n            \"Note: Do not use SSL certificates for illegal websites.\\nIf HTTPS access can't be used after opening, check whether the security group has correctly released port 443.\",\n        SSLConfig: 'Certificate settings',\n        SSLProConfig: 'Protocol settings',\n        supportProtocol: 'Protocol version',\n        encryptionAlgorithm: 'Encryption algorithm',\n        notSecurity: '(not safe)',\n        encryptHelper:\n            \"Let's Encrypt has a frequency limit for issuing certificates, but it is sufficient to meet normal needs. Too frequent operations will cause issuance failure. For specific restrictions, please see <a target='_blank' href='https://letsencrypt.org/zh-cn/docs /rate-limits/'>official document</a> \",\n        ipValue: 'Value',\n        wafValueHelper: 'Value',\n        ext: 'file extension',\n        wafInputHelper: 'Input data by line, one line',\n        data: 'data',\n        ever: 'permanent',\n        nextYear: 'One year later',\n        noLog: 'No logs found',\n        defaultServer: 'Set default site',\n        noDefaultServer: 'Not set',\n        defaultServerHelper:\n            'After setting the default site, all unbinded domain names and IPs will be redirected to the default site\\nThis can effectively prevent malicious resolution\\nHowever, it will also cause the WAF unauthorized domain name interception to fail',\n        restoreHelper: 'Are you sure to restore using this backup?',\n        websiteDeploymentHelper: 'Use an installed application or create a new application to create a website.',\n        websiteStatictHelper: 'Create a website directory on the host.',\n        websiteProxyHelper:\n            'Use reverse proxy to proxy existing service. For example, if a service is installed and running on port 8080, the proxy address will be \"http://127.0.0.1:8080\".',\n        runtimeProxyHelper: 'Use a website runtime to create a website.',\n        runtime: 'Runtime',\n        deleteRuntimeHelper:\n            'The Runtime application needs to be deleted together with the website, please handle it with caution',\n        proxyType: 'Network Type',\n        unix: 'Unix Network',\n        tcp: 'TCP/IP Network',\n        phpFPM: 'FPM Config',\n        phpConfig: 'PHP Config',\n        updateConfig: 'Update Config',\n        isOn: 'On',\n        isOff: 'Off',\n        rewrite: 'Pseudo-static',\n        rewriteMode: 'Scheme',\n        current: 'Current',\n        rewriteHelper:\n            'If setting pseudo-static causes the website to become inaccessible, try to revert to the default settings.',\n        runDir: 'Run Directory',\n        runUserHelper:\n            'For websites deployed through the PHP container runtime environment, you need to set the owner and user group of all files and folders under index and subdirectories to 1000. For the local PHP environment, refer to the local PHP-FPM user and user group settings',\n        userGroup: 'User/Group',\n        uGroup: 'Group',\n        proxyPath: 'Proxy path',\n        proxyPass: 'Target URL',\n        cache: 'Cache',\n        cacheTime: 'Cache duration',\n        enableCache: 'Cache',\n        proxyHost: 'Proxy host',\n        disabled: 'Stopped',\n        startProxy: 'This will start reverse proxy. Continue?',\n        stopProxy: 'This will stop the reverse proxy. Continue?',\n        sourceFile: 'View source',\n        proxyHelper1: 'When accessing this directory, the content of the target URL will be returned and displayed.',\n        proxyPassHelper: 'The target URL must be valid and accessible.',\n        proxyHostHelper: 'Pass the domain name in the request header to the proxy server.',\n        modifier: 'Matching rules',\n        modifierHelper:\n            'Example: \"=\" is exact match, \"~\" is regular match, \"^~\" matches the beginning of the path, etc.',\n        replace: 'Text replacements',\n        replaceHelper:\n            'The nginx text replacement feature allows for string substitution in the response content during reverse proxying. It is commonly used to modify links, API addresses, etc., in HTML, CSS, JavaScript, and other files returned by the backend. It supports regular expression matching for complex content replacement needs.',\n        addReplace: 'Add',\n        replaced: 'Search String (cannot be empty)',\n        replaceText: 'Replace with string',\n        replacedErr: 'The Search String cannot be empty',\n        replacedErr2: 'The Search String cannot be repeated',\n        replacedListEmpty: 'No text replacement rules',\n        proxySslName: 'Proxy SNI Name',\n        basicAuth: 'Basic authentication',\n        editBasicAuthHelper:\n            'The password is asymmetrically encrypted and cannot be echoed. Editing needs to reset the password',\n        antiLeech: 'Anti-leech',\n        extends: 'Extension',\n        browserCache: 'Browser Cache',\n        noModify: 'No Modify',\n        serverCache: 'Server Cache',\n        leechLog: 'Record anti-leech log',\n        accessDomain: 'Allowed domains',\n        leechReturn: 'Response resource',\n        noneRef: 'Allow empty referrer',\n        disable: 'not enabled',\n        disableLeechHelper: 'Whether to disable the anti-leech',\n        disableLeech: 'Disable anti-leech',\n        ipv6: 'Listen IPv6',\n        leechReturnError: 'Fill in the HTTP status code',\n        blockedRef: 'Allow non-standard Referer',\n        accessControl: 'Anti-leech control',\n        leechcacheControl: 'Cache control',\n        logEnableControl: 'Log static asset requests',\n        leechSpecialValidHelper:\n            \"When 'Allow empty Referer' is enabled, requests without a Referer (direct access, etc.) are not blocked; enabling 'Allow non-standard Referer' allows any Referer that does not start with http/https (client requests, etc.).\",\n        leechInvalidReturnHelper: 'HTTP status code returned after blocking hotlinking requests',\n        leechlogControlHelper:\n            'Logs static asset requests; usually disabled in production to avoid excessive, noisy logs',\n        selectAcme: 'Select Acme account',\n        imported: 'Created manually',\n        importType: 'Import type',\n        pasteSSL: 'Paste code',\n        localSSL: 'Select server file',\n        privateKeyPath: 'Private key file',\n        certificatePath: 'Certificate file',\n        ipWhiteListHelper: 'The role of IP allowlist: all rules are invalid for IP allowlist',\n        redirect: 'Redirect',\n        sourceDomain: 'Source domain',\n        targetURL: 'Target URL address',\n        keepPath: 'URI params',\n        path: 'path',\n        redirectType: 'redirection type',\n        redirectWay: 'Way',\n        keep: 'keep',\n        notKeep: 'Do not keep',\n        redirectRoot: 'Redirect to the homepage',\n        redirectHelper: '301 permanent redirection, 302 temporary redirection',\n        changePHPVersionWarn: 'This operation cannot be rolled back, continue?',\n        changeVersion: 'Switch version',\n        retainConfig: 'Whether to keep php-fpm.conf and php.ini files',\n        runDirHelper2: 'Please ensure that the secondary running directory is under the index directory',\n        openrestyHelper:\n            'OpenResty default HTTP port: {0} HTTPS port: {1}, which may affect website domain name access and HTTPS forced redirect',\n        primaryDomainHelper: 'Exmaple: example.com or example.com:8080',\n        acmeAccountType: 'Account type',\n        keyType: 'Key algorithm',\n        tencentCloud: 'Tencent Cloud',\n        containWarn: 'The domain name contains the main domain, enter again',\n        rewriteHelper2:\n            'Applications like WordPress installed from the app store typically come with pseudo-static configuration preset. Reconfiguring them may lead to errors.',\n        websiteBackupWarn:\n            'Only supports importing local backups, importing backups from other machines may cause recovery failure',\n        ipWebsiteWarn: 'Websites with IP as domain names need to be set as default site to be accessed normally.',\n        hstsHelper: 'Enabling HSTS can increase website security',\n        includeSubDomains: 'SubDomains',\n        hstsIncludeSubDomainsHelper:\n            'Once enabled, the HSTS policy will apply to all subdomains of the current domain.',\n        defaultHtml: 'Set default page',\n        website404: 'Website 404 error page',\n        domain404: 'Website page does not exist',\n        indexHtml: 'Static website default page',\n        stopHtml: 'Website stop page',\n        indexPHP: 'PHP website default page',\n        sslExpireDate: 'SSL Expiration Date',\n        website404Helper: 'Website 404 error page only supports PHP runtime environment websites and static websites',\n        sni: 'Origin SNI',\n        sniHelper:\n            \"When the reverse proxy backend is HTTPS, you might need to set the origin SNI. See the CDN service provider's documentation for details.\",\n        huaweicloud: 'Huawei Cloud',\n        createDb: 'Create Database',\n        enableSSLHelper: 'Failure to enable will not affect the creation of the website',\n        batchAdd: 'Batch Add Domains',\n        batchInput: 'Batch Input',\n        domainNotFQDN: 'This domain may not be accessible on the public network',\n        domainInvalid: 'Invalid domain format',\n        domainBatchHelper: 'One domain per line, format: domain:port@ssl\\nExample: example.com:443@ssl or example.com',\n        generateDomain: 'Generate',\n        global: 'Global',\n        subsite: 'Subsite',\n        subsiteHelper: 'A subsite can select an existing PHP or static website directory as the main directory.',\n        parentWbeiste: 'Parent Website',\n        deleteSubsite: 'To delete the current website, you must first delete the subsite(s) {0}',\n        loadBalance: 'Load Balancing',\n        server: 'Server',\n        algorithm: 'Algorithm',\n        ipHash: 'IP Hash',\n        ipHashHelper:\n            'Distributes requests to a specific server based on the client IP address, ensuring that a particular client is always routed to the same server.',\n        leastConn: 'Least Connections',\n        leastConnHelper: 'Sends requests to the server with the fewest active connections.',\n        leastTime: 'Least Time',\n        leastTimeHelper: 'Sends requests to the server with the shortest active connection time.',\n        defaultHelper:\n            'Default method, requests are evenly distributed to each server. If servers have weights configured, requests are distributed based on the specified weights, with higher-weighted servers receiving more requests.',\n        weight: 'Weight',\n        maxFails: 'Max Fails',\n        maxConns: 'Max Connections',\n        strategy: 'Strategy',\n        strategyDown: 'Down',\n        strategyBackup: 'Backup',\n        ipHashBackupErr: 'IP hash does not support backup nodes',\n        failTimeout: 'Failure timeout',\n        failTimeoutHelper:\n            'The time window length for server health checks. When the cumulative number of failures reaches the threshold within this period, the server will be temporarily removed and retried after the same duration. Default 10 seconds',\n        staticChangePHPHelper: 'Currently a static website, you can switch to a PHP website',\n        proxyCache: 'Reverse Proxy Cache',\n        cacheLimit: 'Cache Space Limit',\n        shareCahe: 'Cache Count Memory Size',\n        cacheExpire: 'Cache Expiration Time',\n        shareCaheHelper: 'Approximately 8000 cache objects can be stored per 1M of memory',\n        cacheLimitHelper: 'Old cache will be automatically deleted when the limit is exceeded',\n        cacheExpireJHelper: 'Cache will be deleted if it misses after the expiration time',\n        realIP: 'Real IP',\n        ipFrom: 'IP Source',\n        ipFromHelper:\n            \"By configuring trusted IP sources, OpenResty will analyze IP information in HTTP headers, accurately identify and record visitors' real IP addresses, including in access logs\",\n        ipFromExample1: \"If the frontend is a tool like Frp, you can enter Frp's IP address, such as 127.0.0.1\",\n        ipFromExample2: \"If the frontend is a CDN, you can enter the CDN's IP address range\",\n        ipFromExample3:\n            'If unsure, you can enter 0.0.0.0/0 (ipv4) ::/0 (ipv6) [Note: Allowing any source IP is not secure]',\n        http3Helper:\n            'HTTP/3 is an upgrade to HTTP/2, offering faster connection speeds and better performance, but not all browsers support HTTP/3. Enabling it may cause some browsers to be unable to access the site.',\n        cors: 'CORS',\n        enableCors: 'Enable CORS',\n        allowOrigins: 'Allowed domains',\n        allowMethods: 'Allowed request methods',\n        allowHeaders: 'Allowed request headers',\n        allowCredentials: 'Allow cookies to be sent',\n        preflight: 'Preflight request fast response',\n        preflightHleper:\n            'When enabled, when the browser sends a cross-origin preflight request (OPTIONS request), the system will automatically return a 204 status code and set the necessary cross-origin response headers',\n        changeDatabase: 'Change Database',\n        changeDatabaseHelper1: 'Database association is used for backing up and restoring the website.',\n        changeDatabaseHelper2: 'Switching to another database will cause previous backups to be unrecoverable.',\n        saveCustom: 'Save as Template',\n        rainyun: 'Rain Yun',\n        volcengine: 'Volcengine',\n        runtimePortHelper: 'The current runtime environment has multiple ports. Select a proxy port.',\n        runtimePortWarn: 'The current runtime environment has no ports, unable to proxy',\n        cacheWarn: 'Please turn off the cache switch in the reverse proxy first',\n        loadBalanceHelper:\n            'After creating the load balancing, go to \"Reverse Proxy\", add a proxy and set the backend address to: http://<load balancing name>',\n        favorite: 'Favorite',\n        cancelFavorite: 'Cancel Favorite',\n        useProxy: 'Use Proxy',\n        useProxyHelper: 'Use the proxy server address in the panel settings',\n        westCN: 'West Digital',\n        openBaseDir: 'Prevent Cross-Site Attacks',\n        openBaseDirHelper:\n            'open_basedir is used to restrict the PHP file access path, which helps prevent cross-site access and enhance security',\n        serverCacheTime: 'Server Cache Time',\n        serverCacheTimeHelper:\n            'The time a request is cached on the server. During this period, identical requests will return the cached result directly without requesting the origin server.',\n        browserCacheTime: 'Browser Cache Time',\n        browserCacheTimeHelper:\n            'The time static resources are cached locally in the browser, reducing redundant requests. Users will use the local cache directly before it expires when refreshing the page.',\n        donotLinkeDB: 'Do Not Link Database',\n        toWebsiteDir: 'Enter Website Directory',\n        execParameters: 'Execution Parameters',\n        extCommand: 'Supplementary Command',\n        mirror: 'Mirror Source',\n        execUser: 'Executing User',\n        execDir: 'Execution Directory',\n        packagist: 'China Full Mirror',\n        batchOperate: 'Batch Operation',\n        batchOperateHelper: 'Batch {0} websites, continue operation?',\n        stream: 'TCP/UDP Proxy',\n        streamPorts: 'Listening Ports',\n        streamPortsHelper:\n            'Set the external listening port number, clients will access the service through this port, separated by commas, e.g., 5222,5223',\n        streamHelper: 'TCP/UDP Port Forwarding and Load Balancing',\n        udp: 'Enable UDP',\n        syncHtmlHelper: 'Sync to PHP and static websites',\n    },\n    php: {\n        short_open_tag: 'Short tag support',\n        max_execution_time: 'Maximum script execution time',\n        max_input_time: 'Maximum input time',\n        memory_limit: 'Script memory limit',\n        post_max_size: 'POST data maximum size',\n        file_uploads: 'Whether to allow uploading files',\n        upload_max_filesize: 'The maximum size allowed to upload files',\n        max_file_uploads: 'The maximum number of files allowed to be uploaded at the same time',\n        default_socket_timeout: 'Socket timeout',\n        error_reporting: 'Error level',\n        display_errors: 'Whether to output detailed error information',\n        cgi_fix_pathinfo: 'Whether to open pathinfo',\n        date_timezone: 'Time zone',\n        disableFunction: 'Disable function',\n        disableFunctionHelper: 'Enter the function to be disabled, such as exec, please use multiple, split',\n        uploadMaxSize: 'Upload limit',\n        indexHelper:\n            'In order to ensure the normal operation of the PHP website, please place the code in the index directory and avoid renaming',\n        extensions: 'Extension Templates',\n        extension: 'Extension',\n        extensionsHelper: 'For multiple extensions, separate with commas',\n        extensionHelper: 'Please use multiple extensions, split',\n        toExtensionsList: 'View extension list',\n        containerConfig: 'Container Configuration',\n        containerConfigHelper:\n            'Environment variables and other information can be modified in Configuration - Container Configuration after creation',\n        dateTimezoneHelper: 'Example: TZ=Asia/Shanghai (Please add as needed)',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: 'The hash table size of the server name',\n        clientHeaderBufferSizeHelper: 'The header buffer size requested by the client',\n        clientMaxBodySizeHelper: 'Maximum Upload File',\n        keepaliveTimeoutHelper: 'Connection Timeout',\n        gzipMinLengthHelper: 'Minimum Compressed File',\n        gzipCompLevelHelper: 'Compression Rate',\n        gzipHelper: 'Enable compression for transmission',\n        connections: 'Active connections',\n        accepts: 'Accepts',\n        handled: 'Handled',\n        requests: 'Requests',\n        reading: 'Reading',\n        writing: 'Writing',\n        waiting: 'Waiting',\n        status: 'Current Status',\n        configResource: 'Configuration',\n        saveAndReload: 'Save and reload',\n        clearProxyCache: 'Clean reverse proxy cache',\n        clearProxyCacheWarn: 'This action will delete all files in the cache directory. Continue?',\n        create: 'Add a new module',\n        update: 'Edit a module',\n        params: 'Parameters',\n        packages: 'Packages',\n        script: 'Scripts',\n        module: 'Modules',\n        build: 'Build',\n        buildWarn:\n            'Building OpenResty requires reserving a certain amount of CPU and memory, which may take a long time, please be patient',\n        mirrorUrl: 'Software Source',\n        paramsHelper: 'For example: --add-module=/tmp/ngx_brotli',\n        packagesHelper: 'For example: git, curl (separated by commas)',\n        scriptHelper:\n            'Scripts to execute before compilation, usually for downloading module source code, installing dependencies, etc.',\n        buildHelper:\n            'Click build after adding/modifying a module. OpenResty will automatically restart upon successful build.',\n        defaultHttps: 'HTTPS Anti-tampering',\n        defaultHttpsHelper1: 'Enabling this can resolve HTTPS tampering issues.',\n        sslRejectHandshake: 'Reject default SSL handshake',\n        sslRejectHandshakeHelper:\n            'Enabling this can avoid certificate leakage, setting a default website will invalidate this setting',\n    },\n    ssl: {\n        create: 'Request',\n        provider: 'Type',\n        manualCreate: 'Created manually',\n        acmeAccount: 'ACME account',\n        resolveDomain: 'Resolve domain name',\n        err: 'Error',\n        value: 'record value',\n        dnsResolveHelper: 'Go to the DNS resolution service provider to add the following resolution records:',\n        detail: 'View details',\n        msg: 'Information',\n        ssl: 'Certificate',\n        key: 'Private key',\n        startDate: 'Effective time',\n        organization: 'issuing organization',\n        renewConfirm: 'This will renew a new certificate for domain name {0}. Continue?',\n        autoRenew: 'Automatic renewal',\n        autoRenewHelper: 'Automatically renew 30 days before expiration',\n        renewSuccess: 'Renewal successful',\n        renewWebsite:\n            'This certificate has been associated with the following websites, and the application will be applied to these websites simultaneously',\n        createAcme: 'Create Account',\n        acmeHelper: 'Acme Account is used to apply for free certificates',\n        upload: 'Import',\n        applyType: 'Application method',\n        apply: 'Renew',\n        applyStart: 'Certificate application starts',\n        getDnsResolve: 'Getting DNS resolution value, please wait...',\n        selfSigned: 'Manage Self-signed CA',\n        ca: 'Certificate authority',\n        commonName: 'Common name',\n        caName: 'Certificate authority name',\n        company: 'Organization name',\n        department: 'Organizational unit name',\n        city: 'Locality name',\n        province: 'State or province name',\n        country: 'Country name (2 letter code)',\n        commonNameHelper: 'For example, ',\n        selfSign: 'Issue certificate',\n        days: 'validity period',\n        domainHelper: 'One domain name per line, supports * and IP address',\n        pushDir: 'Push the certificate to the local directory',\n        dir: 'Directory',\n        pushDirHelper:\n            'Certificate file \"fullchain.pem\" and key file \"privkey.pem\" will be generated in this directory.',\n        organizationDetail: 'Organization details',\n        fromWebsite: 'From website',\n        dnsMauanlHelper:\n            'In manual resolution mode, you need to click the apply button after creation to obtain the DNS resolution value',\n        httpHelper:\n            'Using HTTP mode requires installing OpenResty and does not support applying for wildcard domain certificates.',\n        buypassHelper: 'Buypass is not accessible in mainland China',\n        googleHelper: 'How to get EAB HmacKey and EAB kid',\n        googleCloudHelper: 'Google Cloud API is not accessible in most parts of mainland China',\n        skipDNSCheck: 'Skip DNS check',\n        skipDNSCheckHelper: 'Check here only if you encounter a timeout issue during certification request.',\n        cfHelper: 'Do not use Global API Key',\n        deprecated: 'will be deprecated',\n        deprecatedHelper:\n            'Maintenance has been stopped and may be abandoned in a future version. Please use Tencent Cloud method for analysis',\n        disableCNAME: 'Disable CNAME',\n        disableCNAMEHelper: 'Check here if the domain name has a CNAME record and the request fails.',\n        nameserver: 'DNS server',\n        nameserverHelper: 'Use a custom DNS server to verify domain names.',\n        edit: 'Edit certificate',\n        execShell: 'Execute the script after certification request.',\n        shell: 'Script content',\n        shellHelper:\n            'The default execution directory of the script is the 1Panel installation directory. If a certificate is pushed into local directory, the execution directory will be the certificate push directory. The default execution timeout is 30 minutes.',\n        customAcme: 'Custom ACME Service',\n        customAcmeURL: 'ACME Service URL',\n        baiduCloud: 'Baidu Cloud',\n        pushNode: 'Sync to Other Nodes',\n        pushNodeHelper: 'Push to selected nodes after application/renewal',\n        fromMaster: 'Master Node Push',\n        hostedZoneID: 'Hosted Zone ID',\n        isIP: 'IP Certificate',\n        useEAB: 'Use EAB authentication',\n    },\n    firewall: {\n        create: 'Create rule',\n        edit: 'Edit rule',\n        advancedControl: 'Advanced Control',\n        advancedControlNotAvailable: 'Currently using {0} firewall, advanced rules only support iptables',\n        ccDeny: 'CC Protection',\n        ipWhiteList: 'IP allowlist',\n        ipBlockList: 'IP blocklist',\n        fileExtBlockList: 'File extension blocklist',\n        urlWhiteList: 'URL allowlist',\n        urlBlockList: 'URL blocklist',\n        argsCheck: 'GET parameter check',\n        postCheck: 'POST parameter verification',\n        cookieBlockList: 'Cookie blocklist',\n        dockerHelper:\n            'The current firewall cannot disable container port mapping. Installed applications can go to the [Installed] page to edit application parameters and configure port release rules.',\n        iptablesHelper:\n            'Detected that the system is using {0} firewall. To switch to iptables, please uninstall it manually first!',\n        quickJump: 'Quick access',\n        used: 'Used',\n        unUsed: 'Unused',\n        dockerRestart: 'Firewall operations require restarting the Docker service',\n        firewallHelper: '{0} system firewall',\n        firewallNotStart: 'The system firewall is not enabled at present. Enable it first.',\n        restartFirewallHelper: 'This operation will restart the current firewall. Continue?',\n        stopFirewallHelper: 'This will make the server lose security protection. Continue?',\n        startFirewallHelper: 'After the firewall is enabled, the server security can be better protected. Continue?',\n        noPing: 'Disable ping',\n        enableBanPing: 'Block Ping',\n        disableBanPing: 'Unblock Ping',\n        noPingTitle: 'Disable ping',\n        noPingHelper: \"This will disable ping, and the server won't echo ICMP response. Continue?\",\n        onPingHelper: 'This will enable ping, and hackers may discover your server. Continue?',\n        changeStrategy: 'Change the {0} strategy',\n        changeStrategyIPHelper1:\n            'Change the IP address strategy to [deny]. After the IP address is set, access to the server is prohibited. Continue?',\n        changeStrategyIPHelper2:\n            'Change the IP address strategy to [allow]. After the IP address is set, normal access is restored. Continue?',\n        changeStrategyPortHelper1:\n            'Change the port policy to [drop]. After the port policy is set, external access is denied. Continue?',\n        changeStrategyPortHelper2:\n            'Change the port policy to [accept]. After the port policy is set, normal port access will be restored. Continue?',\n        stop: 'Stop',\n        portFormatError: 'This field must be a valid port.',\n        portHelper1: 'Multiple ports, e.g. 8080 and 8081',\n        portHelper2: 'Range port, e.g. 8080-8089',\n        changeStrategyHelper:\n            'Change [{1}] {0} strategy to [{2}]. After setting, {0} will access {2} externally. Continue?',\n        strategy: 'Strategy',\n        accept: 'Accept',\n        drop: 'Drop',\n        anyWhere: 'Any',\n        address: 'Specified IPs',\n        addressHelper: 'Support IP address or IP segment',\n        allow: 'Allow',\n        deny: 'Deny',\n        addressFormatError: 'This field must be a valid IP address.',\n        addressHelper1: 'Support IP address or IP range. For example, \"172.16.10.11\" or \"172.16.10.0/24\".',\n        addressHelper2: 'For multiple IP addresses, separate with comma. For example, \"172.16.10.11, 172.16.0.0/24\".',\n        allIP: 'All IP',\n        portRule: 'Port Rule | Port Rules',\n        forwardRule: 'Port-Forward rule | Port-Forward rules',\n        ipRule: 'IP rule | IP rules',\n        userAgent: 'User-Agent filter',\n        destination: 'Destination',\n        sourcePort: 'Source port',\n        targetIP: 'Destination IP',\n        targetPort: 'Destination port',\n        forwardHelper1: 'If you want to forward to the local port, the destination IP should be set to \"127.0.0.1\".',\n        forwardHelper2: 'Leave the destination IP blank to forward to the local port.',\n        forwardPortHelper: 'Supports port ranges, e.g. 8080-8089',\n        forwardInboundInterface: 'Forward Inbound Network Interface',\n        exportHelper: 'About to export {0} firewall rules. Continue?',\n        importSuccess: 'Successfully imported {0} rules',\n        importPartialSuccess: 'Import completed: {0} succeeded, {1} failed',\n        ipv4Limit: 'The current operation only supports IPv4 addresses',\n        basicStatus: 'Current chain {0} is unbound, please bind first!',\n        baseIptables: 'iptables Service',\n        forwardIptables: 'iptables Port Forwarding Service',\n        advanceIptables: 'iptables Advanced Configuration Service',\n        initMsg: 'About to initialize {0}, continue?',\n        initHelper:\n            'Detected that {0} is not initialized. Click the initialization button in the top status bar to configure!',\n        bindHelper: 'Bind - Firewall rules will only take effect when the status is bound. Confirm?',\n        unbindHelper:\n            'Unbind - When unbound, all added firewall rules will become invalid. Proceed with caution. Confirm?',\n        defaultStrategy: 'Default policy for current chain {0} is {1}',\n        defaultStrategy2:\n            'Default policy for current chain {0} is {1}, current status is unbound. Added firewall rules will take effect after binding!',\n        filterRule: 'Filter Rule',\n        filterHelper:\n            'Filter rules allow you to control network traffic at the INPUT/OUTPUT level. Configure carefully to avoid locking the system.',\n        chain: 'Chain',\n        targetChain: 'Target Chain',\n        sourceIP: 'Source IP',\n        destIP: 'Destination IP',\n        inboundDirection: 'Inbound Direction',\n        outboundDirection: 'Outbound Direction',\n        destPort: 'Destination Port',\n        action: 'Action',\n        reject: 'Reject',\n        sourceIPHelper: 'CIDR format, e.g., 192.168.1.0/24. Leave empty for all addresses',\n        destIPHelper: 'CIDR format, e.g., 10.0.0.0/8. Leave empty for all addresses',\n        portHelper: '0 means any port',\n        allPorts: 'All Ports',\n        deleteRuleConfirm: 'Will delete {0} rules. Continue?',\n    },\n    runtime: {\n        runtime: 'Runtime',\n        workDir: 'Working directory',\n        localHelper: 'For local environment installation and offline environment usage issues, see ',\n        versionHelper: 'PHP version, e.g. v8.0',\n        buildHelper:\n            'If more extensions are selected, the CPU usage will be higher during the image creation process. Avoid selecting all extensions.',\n        openrestyWarn: 'PHP needs to be upgraded to OpenResty to version 1.21.4.1 or later to use',\n        toupgrade: 'To Upgrade',\n        edit: 'Edit runtime',\n        extendHelper:\n            'If the extensions you need are not in the list, you can manually input the extension name. For example, input \"sockets\", then select the first one.',\n        rebuildHelper: 'After editing the extension, you need to rebuild the PHP application to take effect',\n        rebuild: 'Rebuild PHP App',\n        source: 'PHP extension source',\n        ustc: 'University of Science and Technology of China',\n        netease: 'Netease',\n        aliyun: 'Alibaba Cloud',\n        tsinghua: 'Tsinghua University',\n        xtomhk: 'XTOM Mirror Station (Hong Kong)',\n        xtom: 'XTOM Mirror Station (Global)',\n        phpsourceHelper: 'Choose a proper source according to your network environment.',\n        appPort: 'App port',\n        externalPort: 'External port',\n        packageManager: 'Package manager',\n        codeDir: 'Code directory',\n        appPortHelper: 'The port used by the application.',\n        externalPortHelper: 'The port exposed to the outside world.',\n        runScript: 'Run script',\n        runScriptHelper: 'The startup command list is parsed from the package.json file in the source directory.',\n        open: 'Open',\n        operatorHelper: 'The {0} operation will be performed on the selected operating environment. Continue? ',\n        taobao: 'Taobao',\n        tencent: 'Tencent',\n        imageSource: 'Image source',\n        moduleManager: 'Module Management',\n        module: 'Module',\n        nodeOperatorHelper:\n            'Is {0} {1} module? The operation may cause abnormality in the operating environment, confirm before proceeding',\n        customScript: 'Custom startup command',\n        customScriptHelper:\n            'Enter the complete startup command, for example: npm run start. For PM2 startup commands, please replace with pm2-runtime, otherwise it will fail to start.',\n        portError: \"Don't repeat the same port.\",\n        systemRestartHelper: 'Status description: Interruption - status acquisition failed due to system restart',\n        javaScriptHelper: 'Provide a full startup command. For example, \"java -jar halo.jar -Xmx1024M -Xms256M\".',\n        javaDirHelper: 'The directory must contain jar files, subdirectories are also acceptable',\n        goHelper: 'Provide a full startup command. For example, \"go run main.go\" or \"./main\".',\n        goDirHelper: 'The directory or subdirectory must contain Go or binary files.',\n        extension: 'Extension',\n        installExtension: 'Do you confirm to install the extension {0}',\n        loadedExtension: 'Loaded Extension',\n        popularExtension: 'Popular Extension',\n        uninstallExtension: 'Are you sure you want to uninstall the extension {0}',\n        phpConfigHelper:\n            'Modifying the configuration requires restarting the operating environment, do you want to continue',\n        operateMode: 'operation mode',\n        dynamic: 'dynamic',\n        static: 'static',\n        ondemand: 'on-demand',\n        dynamicHelper:\n            'dynamically adjust the number of processes, high flexibility, suitable for websites with large traffic fluctuations or low memory',\n        staticHelper:\n            'fixed number of processes, suitable for websites with high concurrency and stable traffic, high resource consumption',\n        ondemandHelper:\n            'processes are started and destroyed on demand, resource utilization is optimal, but the initial response may be slow',\n        max_children: 'maximum number of processes allowed to be created',\n        start_servers: 'number of processes created at startup',\n        min_spare_servers: 'minimum number of idle processes',\n        max_spare_servers: 'maximum number of idle processes',\n        envKey: 'Name',\n        envValue: 'Value',\n        environment: 'Environment Variable',\n        pythonHelper:\n            'Provide a full startup command. For example, \"pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000\".',\n        dotnetHelper: 'Provide a full startup command. For example, \"dotnet MyWebApp.dll\".',\n        dirHelper: 'Note: Fill in the directory path inside the container',\n        concurrency: 'Concurrency Scheme',\n        loadStatus: 'Load Status',\n        extraHosts: 'Host mapping',\n    },\n    process: {\n        pid: 'Process ID',\n        ppid: 'Parent process ID',\n        numThreads: 'Threads',\n        memory: 'Memory',\n        diskRead: 'Disk read',\n        diskWrite: 'Disk write',\n        netSent: 'uplink',\n        netRecv: 'downstream',\n        numConnections: 'Connections',\n        startTime: 'Start time',\n        running: 'Running',\n        sleep: 'sleep',\n        stop: 'stop',\n        idle: 'idle',\n        zombie: 'zombie process',\n        wait: 'waiting',\n        lock: 'lock',\n        blocked: 'blocked',\n        cmdLine: 'Start command',\n        basic: 'Basic',\n        mem: 'Memory',\n        openFiles: 'Open files',\n        env: 'Environments',\n        noenv: 'None',\n        net: 'Network connections',\n        laddr: 'Local address/port',\n        raddr: 'Remote address/port',\n        stopProcess: 'End',\n        viewDetails: 'View details',\n        stopProcessWarn: 'Are you sure you want to end this process (PID:{0})?',\n        kill: 'Kill Process',\n        killNow: 'Kill Now',\n        killHelper: 'Killing process {0} may cause some programs to malfunction. Continue?',\n        processName: 'Process name',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: 'Failed to retrieve process status, please check the status of the supervisor service.',\n            notSupport: 'Supervisor service not detected, go to the script library page to install it manually',\n            list: 'Daemon process',\n            config: 'Supervisor configuration',\n            primaryConfig: 'Main configuration file location',\n            notSupportCtl: 'The supervisorctl is not detected, go to the script library page to install it manually',\n            user: 'User',\n            command: 'Command',\n            dir: 'Directory',\n            numprocs: 'Number of process',\n            initWarn:\n                'This will modify \"files\" value in \"[include\"] section in the main configuration file. The directory of other configuration file will be: \"{1Panel installation directory}/1panel/tools/supervisord/supervisor.d/\".',\n            operatorHelper: 'Operation {1} will be performed on {0}, continue? ',\n            uptime: 'Running time',\n            notStartWarn: 'Supervisor is not started. Start it first.',\n            serviceName: 'Service name',\n            initHelper:\n                'Supervisor service is detected but not initialized. Click the initialization button in the top status bar to configure it.',\n            serviceNameHelper: 'Supervisor service name managed by systemctl, usually supervisor or supervisord',\n            restartHelper:\n                'This will restart the service after initialization, which causes all the existing daemon processes to stop.',\n            RUNNING: 'Running',\n            STOPPED: 'Stopped',\n            STOPPING: 'Stopping',\n            STARTING: 'Starting',\n            FATAL: 'Failed to start',\n            BACKOFF: 'Start exception',\n            ERROR: 'Error',\n            statusCode: 'Status code',\n            manage: 'Management',\n            autoRestart: 'Auto Restart',\n            EXITED: 'Exited',\n            autoRestartHelper: 'Whether to automatically restart the program after it crashes',\n            autoStart: 'Auto Start',\n            autoStartHelper: 'Whether to automatically start the service after Supervisor starts',\n        },\n    },\n    disk: {\n        management: 'Disk Management',\n        partition: 'Partition',\n        unmount: 'Unmount',\n        unmountHelper: 'unmount the partition {0}?',\n        mount: 'Mount',\n        partitionAlert:\n            'Disk partitioning requires formatting the disk, and existing data will be deleted. Please save or take snapshots of your data in advance.',\n        mountPoint: 'Mount Directory',\n        systemDisk: 'System Disk',\n        unpartitionedDisk: 'Unpartitioned Disk',\n        handlePartition: 'Partition Now',\n        filesystem: 'Filesystem',\n        unmounted: 'Unmounted',\n        cannotOperate: 'Cannot Operate',\n        systemDiskHelper: 'Hint: The current disk is the system disk. It cannot be operated on.',\n        autoMount: 'Auto Mount',\n        model: 'Device Model',\n        diskType: 'Disk Type',\n        serial: 'Serial Number',\n        noFail: 'Mount failure does not affect system startup',\n    },\n    xpack: {\n        expiresTrialAlert:\n            'Friendly reminder: Your Pro trial will expire in {0} days, and all Pro features will no longer be accessible. Please renew or upgrade to the full version in a timely manner.',\n        expiresAlert:\n            'Friendly reminder: Your Pro license will expire in {0} days, and all Pro features will no longer be accessible. Please renew promptly to ensure continued usage.',\n        menu: 'Pro',\n        upage: 'AI Website Builder',\n        proAlert: 'Upgrade to Pro to use this feature',\n        app: {\n            app: 'APP',\n            title: 'Panel Alias',\n            titleHelper: 'The panel alias is used for display in the APP (default panel alias)',\n            qrCode: 'QR Code',\n            apiStatusHelper: 'The Panel APP needs to enable the API interface feature',\n            apiInterfaceHelper:\n                'Supports panel API interface access (this feature needs to be enabled for the panel app)',\n            apiInterfaceHelper1:\n                \"Panel app access requires adding the visitor to the whitelist; for non-fixed IPs, it's recommended to add 0.0.0.0/0 (all IPv4), ::/0 (all IPv6)\",\n            qrCodeExpired: 'Refresh time',\n            apiLeakageHelper: 'Do not disclose the QR code. Ensure it is used only in trusted environments.',\n        },\n        waf: {\n            name: 'WAF',\n            blackWhite: 'Black and White List',\n            globalSetting: 'Global Settings',\n            websiteSetting: 'Website Settings',\n            blockRecords: 'Blocked Records',\n            world: 'World',\n            china: 'China',\n            intercept: 'Interception',\n            request: 'Requests',\n            count4xx: '4xx Quantity',\n            count5xx: '5xx Quantity',\n            todayStatus: \"Today's Status\",\n            reqMap: 'Attack Map (Last 30 days)',\n            resource: 'Source',\n            count: 'Quantity',\n            hight: 'High',\n            low: 'Low',\n            reqCount: 'Requests',\n            interceptCount: 'Interception Number',\n            requestTrends: 'Request Trends (Last 7 Days)',\n            interceptTrends: 'Interception Trends (Last 7 Days)',\n            whiteList: 'Whitelist',\n            blackList: 'Blacklist',\n            ipBlackListHelper: 'IP addresses in the blacklist are blocked from accessing the website',\n            ipWhiteListHelper: 'IP addresses in the whitelist bypass all restrictions',\n            uaBlackListHelper: 'Requests with User-Agent values in the blacklist will be blocked',\n            uaWhiteListHelper: 'Requests with User-Agent values in the whitelist bypass all restrictions',\n            urlBlackListHelper: 'Requests to URLs in the blacklist will be blocked',\n            urlWhiteListHelper: 'Requests to URLs in the whitelist bypass all restrictions',\n            ccHelper:\n                'If a website receives more than {1} requests from the same IP within {0} seconds, the IP will be blocked for {2}',\n            blockTime: 'Blocking Duration',\n            attackHelper: 'If cumulative interceptions exceed {1} within {0} seconds, the IP will be blocked for {2}',\n            notFoundHelper:\n                'If cumulative requests return 404 errors more than {1} times within {0} seconds, the IP will be blocked for {2}',\n            frequencyLimit: 'Frequency Limit',\n            regionLimit: 'Region Limit',\n            defaultRule: 'Default Rules',\n            accessFrequencyLimit: 'Access Frequency Limit',\n            attackLimit: 'Attack Frequency Limit',\n            notFoundLimit: '404 Frequency Limit',\n            urlLimit: 'URL Frequency Limit',\n            urlLimitHelper: 'Set access frequency for a single URL',\n            sqliDefense: 'SQL Injection Protection',\n            sqliHelper: 'Detect SQL injection in requests and block them',\n            xssHelper: 'Detect XSS in requests and block them',\n            xssDefense: 'XSS Protection',\n            uaDefense: 'Malicious User-Agent Rules',\n            uaHelper: 'Includes rules to identify common malicious bots',\n            argsDefense: 'Malicious Parameter Rules',\n            argsHelper: 'Blocks requests containing malicious parameters',\n            cookieDefense: 'Malicious Cookie Rules',\n            cookieHelper: 'Prohibit malicious cookies from being carried in requests',\n            headerDefense: 'Malicious Header Rules',\n            headerHelper: 'Prohibit requests from containing malicious headers',\n            httpRule: 'HTTP Request Method Rules',\n            httpHelper:\n                'Set the method types that are allowed to access. If you want to restrict certain types of access, please turn off this type of button. For example: only GET type access is allowed, then you need to turn off other types of buttons except GET',\n            geoRule: 'Regional Access Restrictions',\n            geoHelper:\n                'Restrict access to your website from certain regions, for example: if access is allowed from mainland China, then requests from outside mainland China will be blocked',\n            ipLocation: 'IP Location',\n            action: 'Action',\n            ruleType: 'Attack Type',\n            ipHelper: 'Enter the IP address',\n            attackLog: 'Attack Log',\n            rule: 'Rule',\n            ipArr: 'IPV4 Range',\n            ipStart: 'Start IP',\n            ipEnd: 'End IP',\n            ipv4: 'IPv4',\n            ipv6: 'IPv6',\n            urlDefense: 'URL Rules',\n            urlHelper: 'Forbidden URL',\n            dirFilter: 'Directory Filter',\n            sqlInject: 'SQL Injection',\n            xss: 'XSS',\n            phpExec: 'PHP Script Execution',\n            oneWordTrojan: 'One word Trojan',\n            appFilter: 'Dangerous Directory Filtering',\n            webShell: 'Webshell',\n            args: 'Malicious Parameters',\n            protocolFilter: 'Protocol Filter',\n            javaFilter: 'Java Dangerous File Filtering',\n            scannerFilter: 'Scanner Filter',\n            escapeFilter: 'Escape Filter',\n            customRule: 'Custom Rules',\n            httpMethod: 'HTTP Method Filter',\n            fileExt: 'File Upload Limit',\n            fileExtHelper: 'Prohibited file extensions for upload',\n            deny: 'Forbidden',\n            allow: 'Allow',\n            field: 'Object',\n            pattern: 'Condition',\n            ruleContent: 'Content',\n            contain: 'include',\n            equal: 'equal',\n            regex: 'regular expression',\n            notEqual: 'Not equal to',\n            customRuleHelper: 'Take actions based on specified conditions',\n            actionAllow: 'Allow',\n            blockIP: 'Block IP',\n            code: 'Return Status Code',\n            noRes: 'Disconnect (444)',\n            badReq: 'Invalid Parameters (400)',\n            forbidden: 'Access Forbidden (403)',\n            serverErr: 'Server Error (500)',\n            resHtml: 'Response Page',\n            allowHelper: 'Allowing access will skip subsequent WAF rules, please use with caution',\n            captcha: 'human-machine verification',\n            fiveSeconds: '5-Seconds verification',\n            location: 'Region',\n            redisConfig: 'Redis Configuration',\n            redisHelper: 'Enable Redis to persist temporarily blocked IPs',\n            wafHelper: 'All websites will lose protection after closing',\n            attackIP: 'Attacking IP',\n            attackParam: 'Attack Details',\n            execRule: 'Hit Rule',\n            acl: 'ACL',\n            sql: 'SQL Injection',\n            cc: 'Access Frequency Limit',\n            isBlocking: 'Blocked',\n            isFree: 'Unblocked',\n            unLock: 'Unlock',\n            unLockHelper: 'unblock IP: {0}?',\n            saveDefault: 'Save Default',\n            saveToWebsite: 'Apply to Website',\n            saveToWebsiteHelper: 'Apply current settings to all websites? ',\n            websiteHelper:\n                'Here are the default settings for creating a website. Modifications need to be applied to the website to take effect',\n            websiteHelper2:\n                'Here are the default settings for creating a website. Please modify the specific configuration at the website',\n            ipGroup: 'IP Group',\n            ipGroupHelper:\n                'One IP or IP segment per line, supports IPv4 and IPv6, for example: 192.168.1.1 or 192.168.1.0/24',\n            ipBlack: 'IP blacklist',\n            openRestyAlert: 'OpenResty version needs to be higher than {0}',\n            initAlert:\n                'Initialization is required for first-time use, the website configuration file will be modified, and the original WAF configuration will be lost. Please be sure to back up OpenResty in advance',\n            initHelper:\n                'The initialization operation will clear the existing WAF configuration. Are you sure you want to initialize? ',\n            mainSwitch: 'Main Switch',\n            websiteAlert: 'Please create a website first',\n            defaultUrlBlack: 'URL Rules',\n            htmlRes: 'Intercept Page',\n            urlSearchHelper: 'Enter the URL to support fuzzy search',\n            toCreate: 'Create',\n            closeWaf: 'Close WAF',\n            closeWafHelper: 'Closing WAF will cause the website to lose protection, do you want to continue',\n            addblack: 'Black',\n            addwhite: 'Add white',\n            addblackHelper: 'Add IP:{0} to the default blacklist?',\n            addwhiteHelper: 'Add IP:{0} to the default whitelist?',\n            defaultUaBlack: 'User-Agent rule',\n            defaultIpBlack: 'Malicious IP Group',\n            cookie: 'Cookie Rules',\n            urlBlack: 'URL Blacklist',\n            uaBlack: 'User-Agent blacklist',\n            attackCount: 'Attack frequency limit',\n            fileExtCheck: 'File upload limit',\n            geoRestrict: 'Regional access restriction',\n            attacklog: 'Interception Record',\n            unknownWebsite: 'Unauthorized domain name access',\n            geoRuleEmpty: 'Region cannot be empty',\n            unknown: 'Website Not Exist',\n            geo: 'Region Restriction',\n            revertHtml: 'restore {0} as the default page?',\n            five_seconds: '5-Second verification',\n            header: 'Header rules',\n            methodWhite: 'HTTP rules',\n            expiryDate: 'Expiration date',\n            expiryDateHelper:\n                'After passing the verification, it will no longer be verified within the validity period',\n            defaultIpBlackHelper: 'Some malicious IPs collected from the Internet to prevent access',\n            notFoundCount: '404 Frequency Limit',\n            matchValue: 'Match value',\n            headerName: 'Supports non-special characters starting with English, numbers, -, length 3-30',\n            cdnHelper: 'Websites using CDN can open here to obtain the correct source IP',\n            clearLogWarn: 'Clearing the log will not be possible, continue?',\n            commonRuleHelper: 'Rule is fuzzy matching',\n            blockIPHelper:\n                'Blocked IPs are temporarily stored in OpenResty and will be unblocked when you restart OpenResty. They can be permanently blocked through the blocking function',\n            addWhiteUrlHelper: 'Add URL {0} to the whitelist?',\n            dashHelper: 'The community version can also use the functions in global settings and website settings',\n            wafStatusHelper: 'WAF is not enabled, please enable it in global settings',\n            ccMode: 'Mode',\n            global: 'Global Mode',\n            uriMode: 'URL Mode',\n            globalHelper:\n                'Global Mode: Triggered when the total number of requests to any URL within a unit of time exceeds the threshold',\n            uriModeHelper:\n                'URL Mode: Triggered when the number of requests to a single URL within a unit of time exceeds the threshold',\n            ip: 'IP BlackList',\n            globalSettingHelper:\n                'Settings with the [Website] tag need to be enabled in [Website Settings], and global settings are only the default settings for newly created websites',\n            globalSettingHelper2:\n                'Settings need to be enabled in both [Global Settings] and [Website Settings] at the same time',\n            urlCCHelper: 'More than {1} requests to this URL within {0} seconds, blocking this IP {2}',\n            urlCCHelper2: 'URL cannot contain parameters',\n            notContain: 'Not contain',\n            urlcc: 'URL frequency limit',\n            method: 'Request type',\n            addIpsToBlock: 'Batch block IP',\n            addUrlsToWhite: 'Batch add URL to white list',\n            noBlackIp: 'IP is already blocked, no need to block again',\n            noWhiteUrl: 'URL is already in the white list, no need to add again',\n            spiderIpHelper:\n                'Includes Baidu, Bing, Google, 360, Shenma, Sogou, ByteDance, DuckDuckGo, Yandex. Closing this will block all spider access.',\n            spiderIp: 'Spider IP Pool',\n            geoIp: 'IP Address Library',\n            geoIpHelper: 'Used to confirm the geolocation of the IP',\n            stat: 'Attack Report',\n            statTitle: 'Report',\n            attackIp: 'IP',\n            attackCountNum: 'Counts',\n            percent: 'Percentage',\n            addblackUrlHelper: 'Whether to add URL: {0} to the default blacklist?',\n            rce: 'Remote Code Execution',\n            software: 'Software',\n            cveHelper: 'Contains vulnerabilities of common software and frameworks',\n            vulnCheck: 'Supplementary Rules',\n            ssrf: 'SSRF Vulnerability',\n            afr: 'Arbitrary File Read',\n            ua: 'Unauthorized Access',\n            id: 'Information Disclosure',\n            aa: 'Authentication Bypass',\n            dr: 'Directory Traversal',\n            xxe: 'XXE Vulnerability',\n            suid: 'Serialization Vulnerability',\n            dos: 'Denial of Service Vulnerability',\n            afd: 'Arbitrary File Download',\n            sqlInjection: 'SQL Injection',\n            afw: 'Arbitrary File Write',\n            il: 'Information Leak',\n            clearAllLog: 'Clear all logs',\n            exportLog: 'Export logs',\n            appRule: 'Application Rules',\n            appRuleHelper:\n                'Common application rules, enabling can reduce false positives, one website can only use one rule',\n            logExternal: 'Exclude Record Types',\n            ipWhite: 'IP White List',\n            urlWhite: 'URL White List',\n            uaWhite: 'User-Agent White List',\n            logExternalHelper:\n                'Excluded record types will not be recorded in logs, blacklist/whitelist, regional access restrictions, and custom rules will generate a lot of logs, it is recommended to exclude',\n            ssti: 'SSTI Attack',\n            crlf: 'CRLF Injection',\n            strict: 'Strict Mode',\n            strictHelper: 'Use stricter rules to validate requests',\n            saveLog: 'Save Log',\n            remoteURLHelper: 'The remote URL needs to ensure one IP per line and no other characters',\n            notFound: 'Not Found (404)',\n            serviceUnavailable: 'Service Unavailable (503)',\n            gatewayTimeout: 'Gateway Timeout (504)',\n            belongToIpGroup: 'Belongs to IP Group',\n            notBelongToIpGroup: 'Does not belong to IP Group',\n            unknownWebsiteKey: 'Unknown Domain',\n            special: 'Special',\n            fileToLarge: 'File exceeds 1MB and cannot be uploaded',\n            uploadOverLimit: 'Uploaded file exceeds the quantity limit, maximum 1 file',\n            importRuleHelper: 'One rule per line',\n        },\n        monitor: {\n            '360': '360 Search',\n            name: 'Website Monitoring',\n            pv: 'Page Views',\n            uv: 'Unique Visitors',\n            flow: 'Traffic Flow',\n            ip: 'IP',\n            spider: 'Spider',\n            visitors: 'Visitor Trends',\n            today: 'Today',\n            last7days: 'Last 7 Days',\n            last30days: 'Last 30 Days',\n            uvMap: 'Visitor Map (30th)',\n            qps: 'Real-time Requests (per minute)',\n            flowSec: 'Real-time Traffic (per minute)',\n            excludeCode: 'Exclude Status Codes',\n            excludeUrl: 'Exclude URLs',\n            excludeExt: 'Exclude Extensions',\n            cdnHelper: 'Obtain the real IP from the CDN-provided Header',\n            reqRank: 'Visit Ranking',\n            refererDomain: 'Referrer Domain',\n            os: 'System',\n            browser: 'Browser/Client',\n            device: 'Device',\n            showMore: 'More',\n            unknown: 'Other',\n            pc: 'Computer',\n            mobile: 'Mobile Device',\n            wechat: 'WeChat',\n            machine: 'machine',\n            tencent: 'Tencent Browser',\n            ucweb: 'UC Browser',\n            '2345explorer': '2345 browser',\n            huaweibrowser: 'Huawei Browser',\n            log: 'Request Logs',\n            statusCode: 'Status Code',\n            requestTime: 'Response Time',\n            flowRes: 'Response Traffic',\n            method: 'Request Method',\n            statusCodeHelper: 'Enter the status code above',\n            statusCodeError: 'Invalid status code type',\n            methodHelper: 'Enter the request method above',\n            all: 'All',\n            baidu: 'Baidu',\n            google: 'Google',\n            bing: 'Bing',\n            bytes: 'Today headlines',\n            sogou: 'Sogou',\n            failed: 'Error',\n            ipCount: 'IP Count',\n            spiderCount: 'Spider Requests',\n            averageReqTime: 'Average Response Time',\n            totalFlow: 'Total Flow',\n            logSize: 'Log File Size',\n            realIPType: 'Real IP acquisition method',\n            fromHeader: 'Get from HTTP Header',\n            fromHeaders: 'Get from Header list',\n            header: 'HTTP Header',\n            cdnConfig: 'CDN Configuration',\n            xff1: 'First-level Proxy from X-Forwarded-For',\n            xff2: 'Second-level Proxy from X-Forwarded-For',\n            xff3: 'Third-level Proxy from X-Forwarded-For',\n            xffHelper:\n                'For example: X-Forwarded-For: <client>,<proxy1>,<proxy2>,<proxy3> The upper level proxy will take the last IP <proxy3>',\n            headersHelper:\n                'Obtain the real IP from commonly used CDN HTTP headers, selecting the first available value',\n            monitorCDNHelper:\n                'Modifying the CDN configuration for website monitoring will also update the WAF CDN settings',\n            wafCDNHelper: 'Modifying the WAF CDN configuration will also update the website monitoring CDN settings',\n            statusErr: 'Invalid status code format',\n            shenma: 'Shenma Search',\n            duckduckgo: 'DuckDuckGo',\n            excludeUri: 'Exclude URIs',\n            top100Helper: 'Show the top 100 data',\n            logSaveDay: 'Log Retention Period (days)',\n            cros: 'Chrome OS',\n            theworld: 'TheWorld Browser',\n            edge: 'Microsoft Edge',\n            maxthon: 'Maxthon Browser',\n            monitorStatusHelper: 'Monitoring is not enabled, please enable it in settings',\n            excludeIp: 'Exclude IP Addresses',\n            excludeUa: 'Exclude User-Agent',\n            remotePort: 'Remote Port',\n            unknown_browser: 'Unknown',\n            unknown_os: 'Unknown',\n            unknown_device: 'Unknown',\n            logSaveSize: 'Maximum Log Save Size',\n            logSaveSizeHelper: 'This is the log save size for a single website',\n            '360se': '360 Security Browser',\n            websites: 'Website List',\n            trend: 'Trend Statistics',\n            reqCount: 'Request Count',\n            uriHelper: 'You can use /test/* or /*/index.php to exclude Uri',\n        },\n        tamper: {\n            tamper: 'Website Tamper Protection',\n            ignoreTemplate: 'Plantilla de Exclusión',\n            protectTemplate: 'Plantilla de Protección',\n            ignoreTemplateHelper:\n                'Ingrese contenido de exclusión, separado por Enter o espacio. (Directorio específico ./log o nombre de directorio tmp, para excluir archivos necesita ingresar archivo específico ./data/test.html)',\n            protectTemplateHelper:\n                'Ingrese contenido de protección, separado por Enter o espacio. (Archivo específico ./index.html, extensión de archivo .html, tipo de archivo js, para proteger directorios necesita ingresar directorio específico ./log)',\n            templateContent: 'Contenido de Plantilla',\n            template: 'Plantilla',\n            saveTemplate: 'Guardar como Plantilla',\n            tamperHelper1:\n                'Para sitios web de implementación con un clic, se recomienda habilitar la protección contra manipulaciones del directorio de aplicaciones; si el sitio web no se puede usar normalmente o falla la copia de seguridad/restauración, desactive primero la protección contra manipulaciones;',\n            tamperHelper2:\n                'Restringirá las operaciones de lectura/escritura, eliminación, permisos y modificación de propietario para archivos protegidos en directorios no excluidos',\n            tamperPath: 'Directorio de Protección',\n            tamperPathEdit: 'Modificar Ruta',\n            log: 'Registro de Bloqueo',\n            totalProtect: 'Protección Total',\n            todayProtect: 'Protección de Hoy',\n            templateRule: 'Longitud 1-512, el nombre no puede contener {0} y otros símbolos',\n            ignore: 'Excluir',\n            ignoreHelper:\n                'Seleccione o ingrese contenido de exclusión, separado por Enter o espacio. (Directorio específico ./log o nombre de directorio tmp, para excluir archivos necesita ingresar o seleccionar archivo específico ./data/test.html)',\n            protect: 'Proteger',\n            protectHelper:\n                'Seleccione o ingrese contenido de protección, separado por Enter o espacio. (Archivo específico ./index.html, extensión de archivo .html, tipo de archivo js, para proteger directorios necesita ingresar o seleccionar directorio específico ./log)',\n            tamperHelper00: 'La exclusión y protección solo admiten rutas relativas;',\n            tamperHelper01:\n                'Después de habilitar la protección contra manipulaciones, el sistema restringirá las operaciones de creación, edición y eliminación de archivos protegidos en directorios no excluidos;',\n            tamperHelper02:\n                'Prioridad: Protección de ruta específica > Exclusión de ruta específica > Protección > Exclusión',\n            tamperHelper03:\n                'Monitoring operations only target non-excluded directories, monitoring the creation of non-protected files in these directories.',\n            disableHelper: 'About to disable tamper protection for the following websites, continue?',\n            appendOnly: 'Append Only',\n            appendOnlyHelper:\n                'Restrict deletion operations for files in this directory, only allowing addition of excluded directories or non-protected files',\n            immutable: 'Immutable',\n            immutableHelper: 'Restrict editing, deletion, permission, and owner modification operations for this file',\n            onWatch: 'Watch',\n            onWatchHelper:\n                'Monitor and intercept creation of protected files or non-excluded directories in this directory',\n            forceStop: 'Force Close',\n            forceStopHelper: 'About to force disable the anti-tamper function for this website directory. Continue?',\n        },\n        setting: {\n            setting: 'Panel Settings',\n            title: 'Panel Description',\n            titleHelper:\n                'Will be displayed on the user login page (e.g. Linux server operation and maintenance management panel, recommended 8-15 characters)',\n            logo: 'Logo (Without Text)',\n            logoHelper:\n                'Will be displayed in the upper left corner of the management page when the menu is collapsed (recommended image size: 82px*82px)',\n            logoWithText: 'Logo (With Text)',\n            logoWithTextHelper:\n                'Will be displayed in the upper left corner of the management page when the menu is expanded (recommended image size: 185px*55px)',\n            favicon: 'Website Icon',\n            faviconHelper: 'Website icon (recommended image size: 16px*16px)',\n            setDefault: 'Restore Default',\n            setHelper: 'The current settings will be saved. Continue?',\n            setDefaultHelper: 'All panel settings will be restored to default. Continue?',\n            logoGroup: 'Logo',\n            imageGroup: 'Image',\n            loginImage: 'Login Page Image',\n            loginImageHelper: 'Displayed on the login page (recommended image size: 500*416px)',\n            loginBgType: 'Login Page Background Type',\n            loginBgImage: 'Login Page Background Image',\n            loginBgImageHelper: 'Displayed as background image on the login page (recommended image size: 1920*1080px)',\n            loginBgColor: 'Login Page Background Color',\n            loginBgColorHelper: 'Displayed as background color on the login page',\n            image: 'Image',\n            bgColor: 'Background Color',\n            loginGroup: 'Login Page',\n            loginBtnLinkColor: 'Button/Link Color',\n            loginBtnLinkColorHelper: 'Will be displayed as the button/link color on the login page',\n        },\n        helper: {\n            wafTitle1: 'Interception Map',\n            wafContent1: 'Displays the geographical distribution of interceptions over the past 30 days',\n            wafTitle2: 'Regional Access Restrictions',\n            wafContent2: 'Restrict website access sources according to geographical locations',\n            wafTitle3: 'Custom interception page',\n            wafContent3: 'Create a custom page to display after a request is intercepted',\n            wafTitle4: 'Custom Rules (ACL)',\n            wafContent4: 'Intercept requests according to custom rules',\n            tamperTitle1: 'File Integrity Monitoring',\n            tamperContent1:\n                'Monitor the integrity of website files, including core files, script files, and configuration files.',\n            tamperTitle2: 'Real-time Scanning and Detection',\n            tamperContent2: 'Detect abnormal or tampered files by real-time scanning the website file system.',\n            tamperTitle3: 'Security Permission Settings',\n            tamperContent3:\n                'Restrict access to website files through proper permission settings and access control policies, reducing potential attack surface.',\n            tamperTitle4: 'Logging and Analysis',\n            tamperContent4:\n                'Record file access and operation logs for subsequent auditing and analysis by administrators, as well as to identify potential security threats.',\n            settingTitle1: 'Custom Welcome Message',\n            settingContent1: 'Set a custom welcome message on the 1Panel login page.',\n            settingTitle2: 'Custom Logo',\n            settingContent2: 'Allow uploading logo images containing brand names or other text.',\n            settingTitle3: 'Custom Website Icon',\n            settingContent3:\n                'Allow uploading custom icons to replace the default browser icon, improving user experience.',\n            monitorTitle1: 'Visitor Trend',\n            monitorContent1: 'Statistics and displays website visitor trends',\n            monitorTitle2: 'Visitor Map',\n            monitorContent2: 'Statistics and displays the geographical distribution of visitors to the website',\n            monitorTitle3: 'Access Statistics',\n            monitorContent3:\n                'Statistics on website request information, including spiders, access devices, request status, etc.',\n            monitorTitle4: 'Real-time monitoring',\n            monitorContent4:\n                'Real-time monitoring of website request information, including number of requests, traffic, etc.',\n            alertTitle1: 'SMS Alerts',\n            alertContent1:\n                'When abnormal server resource usage, website and certificate expiration, new version update, password expiration, etc. occur, users will be notified via SMS alarm to ensure timely processing.',\n            alertTitle2: 'Alert log',\n            alertContent2:\n                'Provide users with the function of viewing alarm logs to facilitate tracking and analysis of historical alarm events.',\n            alertTitle3: 'Alert Settings',\n            alertContent3:\n                'Provide users with custom phone numbers, daily push frequency, and daily push time configurations, making it easier for users to set up more reasonable push alerts.',\n            nodeDashTitle1: 'Application Management',\n            nodeDashContent1:\n                'Unified management of multi-node applications, supports status monitoring, quick start/stop, terminal connection, and backup',\n            nodeDashTitle2: 'Website Management',\n            nodeDashContent2:\n                'Unified management of multi-node websites, real-time status monitoring, supports batch start/stop and quick backup',\n            nodeDashTitle3: 'Database Management',\n            nodeDashContent3:\n                'Unified management of multi-node databases, key status at a glance, supports one-click backup',\n            nodeDashTitle4: 'Scheduled Task Management',\n            nodeDashContent4:\n                'Unified management of multi-node scheduled tasks, supports status monitoring, quick start/stop, and manual trigger execution',\n            nodeTitle1: 'One-Click Node Addition',\n            nodeContent1: 'Quickly integrate multiple server nodes',\n            nodeTitle2: 'Batch Upgrade',\n            nodeContent2: 'Synchronize and upgrade all nodes with one operation',\n            nodeTitle3: 'Node Status Monitoring',\n            nodeContent3: \"Real-time monitoring of each node's operational status\",\n            nodeTitle4: 'Quick Remote Connection',\n            nodeContent4: 'One-click direct connection to node remote terminals',\n            fileExchangeTitle1: 'Key Authentication Transmission',\n            fileExchangeContent1: 'Authenticate via SSH keys to ensure transmission security.',\n            fileExchangeTitle2: 'Efficient File Synchronization',\n            fileExchangeContent2:\n                'Only synchronize changed content to significantly improve transmission speed and stability.',\n            fileExchangeTitle3: 'Support Multi-Node Intercommunication',\n            fileExchangeContent3:\n                'Easily transfer project files between different nodes, flexible management of multiple servers.',\n            nodeAppTitle1: 'Application Upgrade Management',\n            nodeAppContent1: 'Unified monitoring of multi-node application updates, supports one-click upgrade',\n            appTitle1: 'Flexible Panel Management',\n            appContent1: 'Easily manage your 1Panel server anytime, anywhere.',\n            appTitle2: 'Comprehensive Service Information',\n            appContent2:\n                'Manage basic applications, websites, Docker, databases, etc., and quickly create applications and websites via the mobile app.',\n            appTitle3: 'Real-Time Abnormal Monitoring',\n            appContent3:\n                'View real-time server status, WAF security monitoring, website traffic statistics, and process health status on the mobile app.',\n            clusterTitle1: 'Master-Slave Deployment',\n            clusterContent1:\n                'Supports creating MySQL/Postgres/Redis master-slave instances on different nodes, automatically completing master-slave association and initialization',\n            clusterTitle2: 'Master-Slave Management',\n            clusterContent2:\n                'Unified page to centrally manage multiple master-slave nodes, view their roles, running status, etc.',\n            clusterTitle3: 'Replication Status',\n            clusterContent3:\n                'Displays master-slave replication status and delay information, assisting in troubleshooting synchronization issues',\n            vllmTitle1: 'Centralized Management',\n            vllmContent1:\n                'Bring scattered Vllm services into 1Panel for unified visibility of instances, versions, status, and runtime paths.',\n            vllmTitle2: 'Fast Deployment',\n            vllmContent2:\n                'Use a visual form to configure version selection, model mounts, and startup parameters with less manual orchestration.',\n            vllmTitle3: 'Operational Efficiency',\n            vllmContent3:\n                'Handle create, edit, start, stop, restart, delete, and task tracking from one page to simplify local model operations.',\n            vllmTitle4: 'Controlled Configuration',\n            vllmContent4:\n                'Standardize ports, containers, startup commands, and Compose settings through advanced options for easier delivery and later adjustments.',\n        },\n        node: {\n            master: 'Main Node',\n            masterBackup: 'Master Node Backup',\n            backupNode: 'Backup Node',\n            backupFrequency: 'Backup Frequency (hours)',\n            backupCopies: 'Backup Copies',\n            noBackupNode: 'The backup node is currently empty. Select a backup node to save and try again!',\n            masterBackupAlert:\n                'Master node backup is not currently configured. To ensure data security, please set up a backup node as soon as possible to facilitate manual switching to a new master node in case of failure.',\n            node: 'Node',\n            addr: 'Address',\n            nodeUpgrade: 'Update Settings',\n            nodeUpgradeHelper:\n                'Selected nodes will automatically start upgrading after the master node upgrade is completed, no manual operation required.',\n            nodeUnhealthy: 'Node status abnormal',\n            deletedNode: 'Deleted node {0} does not currently support upgrade operations!',\n            nodeUnhealthyHelper: 'Abnormal node status detected. Please check in [Node Management] and try again!',\n            nodeUnbind: 'Node not bound to license',\n            nodeUnbindHelper:\n                'Detected that this node is not bound to a license. Please bind it in [Panel Settings - License] menu and try again!',\n            memTotal: 'Total Memory',\n            nodeManagement: 'Multi-Machine Management',\n            nodeItem: 'Node Management',\n            panelItem: 'Panel Management',\n            addPanel: 'Add Panel',\n            addPanelHelper:\n                'After successfully adding the panel, you can quickly access the target panel in [Overview - Panels].',\n            panel: '1Panel Panel',\n            others: 'Other Panels',\n            addNode: 'Add Node',\n            connInfo: 'Connection Information',\n            nodeInfo: 'Node Information',\n            withProxy: 'Enable Proxy Access',\n            withoutProxy: 'Disable Proxy Access',\n            withProxyHelper:\n                'Will use the system proxy {0} maintained in panel settings to access child nodes. Continue?',\n            withoutProxyHelper:\n                'Will stop using the system proxy maintained in panel settings to access child nodes. Continue?',\n            syncInfo: 'Sync',\n            syncHelper: 'When master node data changes, it synchronizes to this child node in real-time',\n            syncBackupAccount: 'Backup account settings',\n            syncWithMaster:\n                'After upgrading to Pro, all data will be synced by default. Sync policies can be manually adjusted in node management.',\n            syncProxy: 'System proxy settings',\n            syncProxyHelper: 'Syncing system proxy settings requires Docker restart',\n            syncProxyHelper1: 'Restarting Docker may affect currently running container services.',\n            syncProxyHelper2: 'You can manually restart in the Containers - Configuration page.',\n            syncProxyHelper3:\n                'Syncing system proxy settings requires Docker restart, which may affect currently running container services',\n            syncProxyHelper4:\n                'Syncing system proxy settings requires Docker restart. You can manually restart later in the Containers - Configuration page.',\n            syncCustomApp: 'Sync Custom App Repository',\n            syncAlertSetting: 'System alert settings',\n            syncNodeInfo: 'Node basic data,',\n            nodeSyncHelper: 'Node information synchronization will sync the following information:',\n            nodeSyncHelper1: '1. Public backup account information',\n            nodeSyncHelper2: '2. Connection information between the main node and sub-nodes',\n            nodeCheck: 'Availability check',\n            checkSSH: 'Check node SSH connection',\n            checkUserPermission: 'Check node user permissions',\n            isNotRoot: 'Detected that password-less sudo is not supported on this node and current user is non-root',\n            checkLicense: 'Check node license status',\n            checkService: 'Check existing service information on node',\n            checkPort: 'Check node port reachability',\n            panelExist:\n                'Detected that this node is running 1Panel V1 service. Please upgrade to V2 using the migration script before adding.',\n            coreExist:\n                'The current node is already enabled as a master node and cannot be directly added as a slave node. Please downgrade it to a slave node first before adding, refer to the documentation for details.',\n            agentExist:\n                'Detected that 1panel-agent is already installed on this node. Continuing will retain existing data and only replace the 1panel-agent service.',\n            agentNotExist:\n                'It is detected that 1panel-agent is not installed on this node, so the node information cannot be edited directly. Please delete it and add it again.',\n            oldDataExist:\n                'Detected historical 1Panel V2 data on this node. The following information will be used to overwrite current settings:',\n            errLicense: 'The license bound to this node is unavailable. Please check and try again!',\n            errNodePort:\n                'Node port [ {0} ] is detected as inaccessible. Please check if the firewall or security group has allowed this port.',\n            reinstallHelper: 'Reinstall node {0}, continue?',\n            unhealthyCheck: 'Abnormal Check',\n            fixOperation: 'Fix Operation',\n            checkName: 'Check Item',\n            checkSSHConn: 'Check SSH Connection Availability',\n            fixSSHConn: 'Manually edit the node to confirm connection information',\n            checkConnInfo: 'Check Agent Connection Information',\n            checkStatus: 'Check Node Service Availability',\n            fixStatus: 'Run \"systemctl status 1panel-agent.service\" to check if the service is running.',\n            checkAPI: 'Check Node API Availability',\n            fixAPI: 'Check the node logs and verify if the firewall ports are properly opened.',\n            forceDelete: 'Force Delete',\n            operateHelper: 'The following nodes will undergo {0} operation, continue?',\n            operatePanelHelper: 'The following panels will undergo {0} operation, continue?',\n            forceDeleteHelper: 'Force delete will ignore node deletion errors and delete database metadata',\n            uninstall: 'Delete node data',\n            uninstallHelper: 'This will delete all 1Panel related data of the node. Proceed with caution!',\n            baseDir: 'Installation Directory',\n            baseDirHelper:\n                'When the installation directory is empty, it will be installed in the /opt directory by default',\n            nodePort: 'Node Port',\n            offline: 'Offline mode',\n            freeCount: 'Free quota [{0}]',\n            offlineHelper: 'Used when the node is in an offline environment',\n            appUpgrade: 'App Upgrade',\n            appUpgradeHelper: 'There are {0} apps that need to be upgraded',\n        },\n        customApp: {\n            name: 'Custom App Repository',\n            appStoreType: 'App Store Package Source',\n            appStoreUrl: 'Repository URL',\n            local: 'Local Path',\n            remote: 'Remote Link',\n            imagePrefix: 'Image Prefix',\n            imagePrefixHelper:\n                'Function: Customize the image prefix and modify the image field in the compose file. For example, when the image prefix is set to 1panel/custom, the image field for MaxKB will change to 1panel/custom/maxkb:v1.10.0',\n            closeHelper: 'Cancel using custom app repository',\n            appStoreUrlHelper: 'Only .tar.gz format is supported',\n            postNode: 'Sync to sub-node',\n            postNodeHelper:\n                'Sync the custom store package to tmp/customApp/apps.tar.gz in the installation directory of the sub-node',\n            nodes: 'Nodes',\n            selectNode: 'Select Node',\n            selectNodeError: 'Select a node',\n            licenseHelper: 'The Pro version supports the custom application repository feature',\n            databaseHelper: 'Application associated database, select target node database',\n            nodeHelper: 'Cannot select current node',\n            migrateHelper:\n                'Currently only supports migrating monolithic applications and applications associated only with MySQL, MariaDB, PostgreSQL databases',\n            opensslHelper:\n                'If using encrypted backup, the OpenSSL versions between the two nodes must be consistent, otherwise migration may fail.',\n            installApp: 'Batch install',\n            installAppHelper: 'Batch install apps to selected nodes',\n        },\n        alert: {\n            isAlert: 'Alert',\n            alertCount: 'Alert Count',\n            clamHelper: 'Trigger alert when scanning infected files',\n            cronJobHelper: 'Trigger alert when task execution fails',\n            licenseHelper: 'Professional version supports more alert',\n            alertCountHelper: 'Maximum daily alarm frequency',\n            alert: 'SMS Alert',\n            logs: 'Alert Logs',\n            list: 'Alert List',\n            addTask: 'Create Alert',\n            editTask: 'Edit Alert',\n            alertMethod: 'Method',\n            alertMsg: 'Alert Message',\n            alertRule: 'Alert Rules',\n            titleSearchHelper: 'Enter alert title for fuzzy search',\n            taskType: 'Type',\n            ssl: 'Certificate Expiry',\n            siteEndTime: 'Website Expiry',\n            panelPwdEndTime: 'Panel Password Expiry',\n            panelUpdate: 'New Panel Version Available',\n            cpu: 'Server CPU Alert',\n            memory: 'Server Memory Alert',\n            load: 'Server Load Alert',\n            disk: 'Server Disk Alert',\n            website: 'Website',\n            certificate: 'SSL Certificate',\n            remainingDays: 'Remaining Days',\n            sendCount: 'Send Count',\n            sms: 'SMS',\n            wechat: 'WeChat',\n            dingTalk: 'DingTalk',\n            feiShu: 'FeiShu',\n            mail: 'Email',\n            weCom: 'WeCom',\n            sendCountRulesHelper: 'Total alerts sent before expiry (once daily)',\n            panelUpdateRulesHelper: 'Total alerts sent for new panel version (once daily)',\n            oneDaySendCountRulesHelper: 'Maximum alerts sent per day',\n            siteEndTimeRulesHelper: 'Websites that never expire will not trigger alerts',\n            autoRenewRulesHelper:\n                'Certificates with auto-renew enabled and remaining days less than 31 will not trigger alerts',\n            panelPwdEndTimeRulesHelper: 'Panel password expiry alerts are unavailable if no expiration is set',\n            sslRulesHelper: 'All SSL Certificates',\n            diskInfo: 'Disk',\n            monitoringType: 'Monitoring Type',\n            autoRenew: 'Auto-Renew',\n            useDisk: 'Disk Usage',\n            usePercentage: 'Usage Percentage',\n            changeStatus: 'Change Status',\n            disableMsg: 'Stopping the alert task will prevent this task from sending alert messages. Continue?',\n            enableMsg: 'Enabling the alert task will allow this task to send alert messages. Continue?',\n            useExceed: 'Usage Exceeds',\n            useExceedRulesHelper: 'Trigger alert when usage exceeds the set value',\n            cpuUseExceedAvg: 'The average cpu usage exceeds the specified value',\n            memoryUseExceedAvg: 'The average memory usage exceeds the specified value',\n            loadUseExceedAvg: 'The average load usage exceeds the specified value',\n            cpuUseExceedAvgHelper: 'The average cpu usage within the specified time exceeds the specified value',\n            memoryUseExceedAvgHelper: 'The average memory usage within the specified time exceeds the specified value',\n            loadUseExceedAvgHelper: 'The average load usage within the specified time exceeds the specified value',\n            resourceAlertRulesHelper: 'Note: Continuous alerts within 30 minutes will send only one',\n            specifiedTime: 'Specified Time',\n            deleteTitle: 'Delete Alert',\n            deleteMsg: 'Are you sure you want to delete the alert task?',\n            allSslTitle: 'All Website SSL Certificate Expiry Alerts',\n            sslTitle: 'SSL Certificate Expiry Alert for Website {0}',\n            allSiteEndTimeTitle: 'All Website Expiry Alerts',\n            siteEndTimeTitle: 'Website {0} Expiry Alert',\n            panelPwdEndTimeTitle: 'Panel Password Expiry Alert',\n            panelUpdateTitle: 'New Panel Version Notification',\n            cpuTitle: 'High CPU Usage Alert',\n            memoryTitle: 'High Memory Usage Alert',\n            loadTitle: 'High Load Alert',\n            diskTitle: 'High Disk Usage Alert for Mount Directory {0}',\n            allDiskTitle: 'High Disk Usage Alert',\n            timeRule: 'Remaining time less than {0} days (if not handled, will resend the next day)',\n            panelUpdateRule:\n                'Send an alert once when a new panel version is detected (if not handled, will resend the next day)',\n            avgRule: 'Average {1} usage exceeds {2}% within {0} minutes, triggers alert, sends {3} times per day',\n            diskRule: 'Disk usage for mount directory {0} exceeds {1}{2}, triggers alert, sends {3} times per day',\n            allDiskRule: 'Disk usage exceeds {0}{1}, triggers alert, sends {2} times per day',\n            cpuName: ' CPU ',\n            memoryName: 'Memory',\n            loadName: 'Load',\n            diskName: 'Disk',\n            syncAlertInfo: 'Manual Push',\n            syncAlertInfoMsg: 'manually push the alert task?',\n            pushError: 'Push Failed',\n            pushSuccess: 'Push Successful',\n            syncError: 'Sync Failed',\n            success: 'Alert Successful',\n            pushing: 'Pushing...',\n            error: 'Alert Failed',\n            cleanLog: 'Clean Logs',\n            cleanAlertLogs: 'Clean Alert Logs',\n            daily: 'Daily Alert Count: {0}',\n            cumulative: 'Cumulative Alert Count: {0}',\n            clams: 'Virus scan alert',\n            taskName: 'Task Name',\n            cronJobType: 'Task Type',\n            clamPath: 'Scan Directory',\n            cronjob: 'Cronjob execution {0} failed',\n            app: 'Backup App',\n            web: 'Backup Website',\n            database: 'Backup Database',\n            directory: 'Backup Directory',\n            log: 'Backup Logs',\n            snapshot: 'System Snapshot',\n            clamsRulesHelper: 'Virus scanning tasks that require alerts',\n            cronJobRulesHelper: 'This type of scheduled task needs to be configured',\n            clamsTitle: 'Virus scan task 「 {0} 」 detected infected file alert',\n            cronJobAppTitle: 'Cronjob - Backup App 「 {0} 」 Task Failure Alert',\n            cronJobWebsiteTitle: 'Cronjob - Backup Website「 {0} 」Task Failure Alert',\n            cronJobDatabaseTitle: 'Cronjob - Backup Database「 {0} 」Task Failure Alert',\n            cronJobDirectoryTitle: 'Cronjob - Backup Directory「 {0} 」Task Failure Alert',\n            cronJobLogTitle: 'Cronjob - Backup Logs「 {0} 」Task Failure Alert',\n            cronJobSnapshotTitle: 'Cronjob - Backup Snapshot「 {0} 」Task Failure Alert',\n            cronJobShellTitle: 'Cronjob - Shell script 「 {0} 」Task Failure Alert',\n            cronJobCurlTitle: 'Cronjob - URL access「 {0} 」Task Failure Alert',\n            cronJobCutWebsiteLogTitle: 'Cronjob - Cut website log「 {0} 」Task Failure Alert',\n            cronJobCleanTitle: 'Cronjob - Cache cleaning「 {0} 」Task Failure Alert',\n            cronJobNtpTitle: 'Cronjob - Synchronization server time「 {0} 」Task Failure Alert',\n            clamsRule: 'Virus scan detected infected file alert，sent {0} times per day',\n            cronJobAppRule: 'Backup app task failed alert，sent {0} times per day',\n            cronJobWebsiteRule: 'Backup website task failed alert，sent {0} times per day',\n            cronJobDatabaseRule: 'Backup database task failed alert，sent {0} times per day',\n            cronJobDirectoryRule: 'Backup directory task failed alert，sent {0} times per day',\n            cronJobLogRule: 'Backup logs task failed alert，sent {0} times per day',\n            cronJobSnapshotRule: 'Backup snapshot task failed alert，sent {0} times per day',\n            cronJobShellRule: 'Shell script task failed alert，sent {0} times per day',\n            cronJobCurlRule: 'URL access task failed alert，sent {0} times per day',\n            cronJobCutWebsiteLogRule: 'Cut website log task failed alert，sent {0} times per day',\n            cronJobCleanRule: 'Cache cleaning task failed alert，sent {0} times per day',\n            cronJobNtpRule: 'Synchronization server time task failed alert，sent {0} times per day',\n            alertSmsHelper: 'SMS limit: total of {0} messages, {1} already used',\n            goBuy: 'Purchase More',\n            phone: 'Phone',\n            phoneHelper: 'Provide real phone number for alert messages',\n            dailyAlertNum: 'Daily Alert Limit',\n            dailyAlertNumHelper: 'Maximum number of alerts per day (up to 100)',\n            timeRange: 'Time Range',\n            sendTimeRange: 'Send time range',\n            sendTimeRangeHelper: 'Can push {0} time range',\n            to: 'to',\n            startTime: 'Start Time',\n            endTime: 'End Time',\n            defaultPhone: 'Default to license-bound account phone number',\n            noticeAlert: 'Notice Alert',\n            resourceAlert: 'Resource Alert',\n            agentOfflineAlertHelper:\n                'When offline alert is enabled for the node, the main node will scan every 30 minutes to execute alert tasks.',\n            offline: 'Offline Alert',\n            offlineHelper:\n                'When set to offline alert, the main node will scan every 30 minutes to execute alert tasks.',\n            offlineOff: 'Enable Offline Alert',\n            offlineOffHelper:\n                'Enabling offline alert will make the main node scan every 30 minutes to execute alert tasks.',\n            offlineClose: 'Disable Offline Alert',\n            offlineCloseHelper:\n                'Disabling offline alert requires sub-nodes to handle alerts independently. Please ensure network connectivity to avoid alert failure.',\n            alertNotice: 'Alert Notification',\n            methodConfig: 'Notification Method Configuration',\n            commonConfig: 'Global Configuration',\n            smsConfig: 'SMS',\n            smsConfigHelper: 'Configure phone numbers for SMS notifications',\n            emailConfig: 'Email',\n            emailConfigHelper: 'Configure SMTP email sending service',\n            deleteConfigTitle: 'Delete Alert Configuration',\n            deleteConfigMsg: 'Are you sure you want to delete the alert configuration?',\n            test: 'Test',\n            alertTestOk: 'Test notification succeeded',\n            alertTestFailed: 'Test notification failed',\n            displayName: 'Display Name',\n            sender: 'Sender',\n            password: 'Password',\n            host: 'SMTP Server',\n            port: 'Port',\n            encryption: 'Encryption',\n            recipient: 'Recipient',\n            licenseTime: 'License Expiration Reminder',\n            licenseTimeTitle: 'License Expiration Reminder',\n            displayNameHelper: 'Sender display name for emails',\n            senderHelper: 'Email address used to send messages',\n            passwordHelper: 'Authorization code for the email service',\n            hostHelper: 'SMTP server address, e.g., smtp.qq.com',\n            portHelper: 'SSL usually uses 465, TLS usually uses 587',\n            sslHelper: 'If the SMTP port is 465, SSL is usually required',\n            tlsHelper: 'If the SMTP port is 587, TLS is usually required',\n            triggerCondition: 'Trigger Condition',\n            loginFail: ' login failures within',\n            nodeException: 'Node Exception Alert',\n            licenseException: 'License Exception Alert',\n            panelLogin: 'Panel Login Exception Alert',\n            sshLogin: 'SSH Login Exception Alert',\n            panelIpLogin: 'Panel Login IP Exception Alert',\n            sshIpLogin: 'SSH Login IP Exception Alert',\n            ipWhiteListHelper:\n                'IPs in the whitelist are not restricted by rules, and there will be no alert upon successful login',\n            nodeExceptionRule: 'Node exception alert, sent {0} times per day',\n            licenseExceptionRule: 'License exception alert, sent {0} times per day',\n            panelLoginRule: 'Panel login alert, sent {0} times per day',\n            sshLoginRule: 'SSH login alert, sent {0} times per day',\n            userNameHelper: 'Username is empty, the sender address will be used by default',\n            alertConfigHelper: 'Configure alert notification channels to receive panel message push',\n            weComConfigHelper: 'WeCom alert notification configuration',\n            wechatConfigHelper: 'WeChat Official Account alert notification configuration',\n            dingTalkConfigHelper: 'DingTalk alert notification configuration',\n            feiShuConfigHelper: 'Feishu alert notification configuration',\n            webhookName: 'Bot name',\n            webhookUrl: 'Webhook URL',\n            alertConfigProHelper:\n                'The Professional edition additionally supports WeCom, DingTalk, Feishu, and SMS alerts.',\n        },\n        theme: {\n            lingXiaGold: 'Ling Xia Gold',\n            classicBlue: 'Classic Blue',\n            freshGreen: 'Fresh Green',\n            customColor: 'Custom Color',\n            setDefault: 'Default',\n            setDefaultHelper: 'The theme color scheme is about to be restored to its initial state. Continue?',\n            setHelper: 'The currently selected theme color scheme is about to be saved. Continue?',\n        },\n        exchange: {\n            exchange: 'File Exchange',\n            exchangeConfirm: \"transfer the file/folder {1} from {0} node to {2} node's {3} directory?\",\n        },\n        cluster: {\n            cluster: 'Application High Availability',\n            name: 'Cluster Name',\n            addCluster: 'Add Cluster',\n            installNode: 'Install Node',\n            master: 'Master Node',\n            slave: 'Slave Node',\n            replicaStatus: 'Master-Slave Status',\n            unhealthyDeleteError: 'The installation node status is abnormal, please check the node list and try again!',\n            replicaStatusError: 'Status acquisition is abnormal, please check the master node.',\n            masterHostError: 'The master node IP cannot be 127.0.0.1',\n        },\n    },\n};\n\nexport default {\n    ...fit2cloudEnLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/es-es.ts",
    "content": "import fit2cloudEsEsLocale from 'fit2cloud-ui-plus/src/locale/lang/en';\n\nconst message = {\n    commons: {\n        true: 'Verdadero',\n        false: 'Falso',\n        colon: ': ',\n        example: 'p.ej., ',\n        fit2cloud: 'FIT2CLOUD',\n        lingxia: 'Lingxia',\n        button: {\n            run: 'Ejecutar',\n            prev: 'Anterior',\n            next: 'Siguiente',\n            create: 'Crear',\n            add: 'Agregar',\n            save: 'Guardar',\n            set: 'Establecer',\n            sync: 'Sincronizar',\n            delete: 'Eliminar',\n            edit: 'Editar',\n            enable: 'Habilitar',\n            disable: 'Deshabilitar',\n            confirm: 'Confirmar',\n            cancel: 'Cancelar',\n            reset: 'Restablecer',\n            setDefault: 'Restaurar valores predeterminados',\n            restart: 'Reiniciar',\n            conn: 'Conectar',\n            disconn: 'Desconectar',\n            clean: 'Limpiar',\n            login: 'Iniciar sesión',\n            close: 'Cerrar',\n            stop: 'Detener',\n            start: 'Iniciar',\n            view: 'Ver',\n            watch: 'Observar',\n            handle: 'Disparar',\n            clone: 'Clonar',\n            expand: 'Expandir',\n            collapse: 'Colapsar',\n            log: 'Registros',\n            back: 'Atrás',\n            backup: 'Respaldar',\n            recover: 'Recuperar',\n            retry: 'Reintentar',\n            upload: 'Subir',\n            download: 'Descargar',\n            init: 'Inicializar',\n            verify: 'Verificar',\n            saveAndEnable: 'Guardar y habilitar',\n            import: 'Importar',\n            export: 'Exportar',\n            power: 'Permisos',\n            search: 'Buscar',\n            refresh: 'Actualizar',\n            get: 'Obtener',\n            upgrade: 'Actualizar',\n            update: 'Actualizar',\n            updateNow: 'Actualizar Ahora',\n            ignore: 'Ignorar actualización',\n            copy: 'Copiar',\n            random: 'Aleatorio',\n            install: 'Instalar',\n            uninstall: 'Desinstalar',\n            fullscreen: 'Pantalla completa',\n            quitFullscreen: 'Salir de pantalla completa',\n            showAll: 'Mostrar todo',\n            hideSome: 'Ocultar algunos',\n            agree: 'Aceptar',\n            notAgree: 'No aceptar',\n            preview: 'Vista previa',\n            open: 'Abrir',\n            notSave: 'No guardar',\n            createNewFolder: 'Crear nueva carpeta',\n            createNewFile: 'Crear nuevo archivo',\n            helpDoc: 'Docs',\n            bind: 'Vincular',\n            unbind: 'Desvincular',\n            cover: 'cubrir',\n            skip: 'omitir',\n            fix: 'Arreglar',\n            down: 'Detener',\n            up: 'Iniciar',\n            sure: 'Confirmar',\n            show: 'Mostrar',\n            hide: 'Ocultar',\n            migrate: 'Migrar',\n            disConn: 'Desconectar',\n            visit: 'Visitar',\n        },\n        operate: {\n            start: 'Iniciar',\n            stop: 'Detener',\n            restart: 'Reiniciar',\n            reload: 'Recargar',\n            rebuild: 'Reconstruir',\n            sync: 'Sincronizar',\n            up: 'Subir',\n            down: 'Bajar',\n            delete: 'Eliminar',\n        },\n        search: {\n            timeStart: 'Hora de inicio',\n            timeEnd: 'Hora de fin',\n            timeRange: 'Hasta',\n            dateStart: 'Fecha de inicio',\n            dateEnd: 'Fecha de fin',\n            date: 'Fecha',\n        },\n        table: {\n            all: 'Todo',\n            total: 'Total {0}',\n            name: 'Nombre',\n            type: 'Tipo',\n            status: 'Estado',\n            statusSuccess: 'Éxito',\n            statusFailed: 'Fallido',\n            statusWaiting: 'Esperando...',\n            records: 'Registros',\n            group: 'Grupo',\n            default: 'Predeterminado',\n            createdAt: 'Fecha de creación',\n            publishedAt: 'Fecha de publicación',\n            date: 'Fecha',\n            updatedAt: 'Fecha de actualización',\n            operate: 'Operaciones',\n            message: 'Mensaje',\n            description: 'Descripción',\n            interval: 'Intervalo',\n            user: 'Propietario',\n            title: 'Título',\n            port: 'Puerto',\n            forward: 'Reenviar',\n            protocol: 'Protocolo',\n            tableSetting: 'Configuración de tabla',\n            refreshRate: 'Frecuencia de actualización',\n            noRefresh: 'Sin actualización',\n            selectColumn: 'Seleccionar columna',\n            local: 'local',\n            serialNumber: 'Número de serie',\n            manageGroup: 'Gestionar grupos',\n            backToList: 'Volver a la lista',\n            keepEdit: 'Continuar Editando',\n        },\n        loadingText: {\n            Upgrading: 'Actualizando el sistema, por favor espere...',\n            Restarting: 'Reiniciando el sistema, por favor espere...',\n            Recovering: 'Recuperando desde instantánea, por favor espere...',\n            Rollbacking: 'Revirtiendo desde instantánea, por favor espere...',\n        },\n        msg: {\n            noneData: 'No hay datos disponibles',\n            delete: 'Esta operación de eliminación no se puede deshacer. ¿Desea continuar?',\n            clean: 'Esta operación de limpieza no se puede deshacer. ¿Desea continuar?',\n            closeDrawerHelper: 'Es posible que el sistema no guarde los cambios que realizó. ¿Desea continuar?',\n            deleteSuccess: 'Eliminación completada correctamente',\n            loginSuccess: 'Inicio de sesión completado correctamente',\n            operationSuccess: 'Operación completada correctamente',\n            copySuccess: 'Copiado completado correctamente',\n            notSupportOperation: 'Esta operación no es compatible',\n            requestTimeout: 'La solicitud caducó, inténtalo más tarde',\n            infoTitle: 'Aviso',\n            notRecords: 'No hay registros de ejecución para esta tarea',\n            sureLogOut: '¿Está seguro de que desea cerrar sesión?',\n            createSuccess: 'Creación completada correctamente',\n            updateSuccess: 'Actualización completada correctamente',\n            uploadSuccess: 'Carga completada correctamente',\n            operateConfirm: 'Para confirmar, escribe: ',\n            inputOrSelect: 'Selecciona o ingresa',\n            copyFailed: 'Error al copiar',\n            operatorHelper: 'Ejecuta \"{1}\" en \"{0}\". Esto no se puede deshacer. ¿Continuar?',\n            notFound: 'Lo sentimos, la página que solicitó no existe.',\n            unSupportType: 'El tipo de archivo actual no es compatible.',\n            unSupportSize: 'El archivo subido supera los {0}M, ¡por favor verifique!',\n            fileExist: 'El archivo ya existe en la carpeta actual. No se permite subirlo de nuevo.',\n            fileNameErr:\n                'Solo puede subir archivos cuyo nombre contenga de 1 a 256 caracteres, incluyendo inglés, chino, dígitos o los caracteres .-_',\n            confirmNoNull: 'Asegúrese de que el valor {0} no esté vacío.',\n            errPort: 'Información de puerto incorrecta, ¡por favor verifique!',\n            remove: 'Eliminar',\n            backupHelper: 'La operación actual respaldará {0}. ¿Proceder?',\n            recoverHelper: 'Restaurar desde {0}. Esto es irreversible. ¿Continuar?',\n            refreshSuccess: 'Actualizado correctamente',\n            rootInfoErr: 'Ya se encuentra en el directorio raíz',\n            resetSuccess: 'Restablecimiento completado correctamente',\n            creatingInfo: 'Creando, no es necesario realizar esta operación',\n            installSuccess: 'Instalado correctamente',\n            uninstallSuccess: 'Desinstalado correctamente',\n            offlineTips: 'La edición sin conexión no admite esta acción',\n            errImportFormat: 'Los datos de importación o el formato son incorrectos, verifica y vuelve a intentarlo',\n            importHelper: 'Si hay conflictos, los datos importados sobrescribirán los existentes.',\n            errImport: 'El contenido del archivo es anormal:',\n            backupSuccess: 'Copia completada',\n            restoreSuccess: 'Restauración completada',\n        },\n        login: {\n            username: 'Usuario',\n            password: 'Contraseña',\n            passkey: 'Inicio de sesión con Passkey',\n            welcome: 'Bienvenido de nuevo. Inicia sesión con tu usuario y contraseña.',\n            errorAuthInfo: 'Usuario o contraseña incorrectos, inténtalo nuevamente.',\n            errorMfaInfo: 'Información de autenticación inválida, inténtalo nuevamente.',\n            captchaHelper: 'Captcha',\n            errorCaptcha: 'Código captcha inválido',\n            notSafe: 'Acceso denegado',\n            safeEntrance1: 'El inicio de sesión seguro está habilitado en el entorno actual',\n            safeEntrance2:\n                'Ingrese el siguiente comando en la terminal SSH para ver la entrada al panel: 1pctl user-info',\n            errIP1: 'El acceso mediante dirección IP autorizada está habilitado en el entorno actual',\n            errDomain1: 'La vinculación del nombre de dominio de acceso está habilitada en el entorno actual',\n            errHelper:\n                'Para restablecer la información de vinculación, ejecute el siguiente comando en la terminal SSH: ',\n            codeInput: 'Por favor, ingrese el código de verificación de 6 dígitos del validador MFA',\n            mfaTitle: 'Autenticación MFA',\n            mfaCode: 'Código de verificación MFA',\n            title: 'Panel de gestión de servidores Linux',\n            licenseHelper: '<Acuerdo de Licencia Comunitaria>',\n            errorAgree: 'Haga clic para aceptar la Licencia de Software Comunitaria',\n            logout: 'Cerrar sesión',\n            agreeTitle: 'Acuerdo',\n            agreeContent:\n                'Para proteger mejor sus derechos e intereses legítimos, por favor lea y acepte el siguiente acuerdo &laquo; <a href = \"https://www.fit2cloud.com/legal/licenses.html\" target = \"_blank\" > Acuerdo de Licencia Comunitaria </a> &raquo;',\n            passkeyFailed: 'El inicio de sesión con Passkey falló, inténtalo de nuevo',\n            passkeyNotSupported: 'El navegador o entorno actual no admite Passkey',\n            passkeyToPassword: '¿Tienes problemas para usar una passkey? Usa la contraseña en su lugar',\n        },\n        rule: {\n            username: 'Introduzca un nombre de usuario',\n            password: 'Introduzca una contraseña',\n            rePassword: 'La confirmación de la contraseña no coincide con la contraseña.',\n            requiredInput: 'Este campo es obligatorio.',\n            requiredSelect: 'Seleccione un elemento de la lista',\n            illegalChar: 'Actualmente no se admite la inyección de caracteres & ; $ \\' ` ( ) \" > < |',\n            illegalInput: 'Este campo no debe contener caracteres no permitidos.',\n            commonName:\n                'Este campo debe comenzar con un carácter no especial y debe estar compuesto por letras, caracteres chinos, números, \".\", \"-\", y \"_\" con una longitud de 1 a 128.',\n            userName:\n                'Este campo debe estar compuesto por letras, caracteres chinos, números y \"_\" con una longitud de 3 a 30.',\n            simpleName:\n                'Este campo no debe comenzar con el carácter \"_\" y debe estar compuesto por letras, números y \"_\" con una longitud de 3 a 30.',\n            simplePassword:\n                'Este campo no debe comenzar con el carácter \"_\" y debe estar compuesto por letras, números y \"_\" con una longitud de 1 a 30.',\n            dbName: 'Este campo no debe comenzar con el carácter \"_\" y debe estar compuesto por letras, números y \"_\" con una longitud de 1 a 64.',\n            imageName:\n                'Este campo debe estar compuesto por letras, números, \":\", \"@\", \"/\", \".\", \"-\", y \"_\" con una longitud de 1 a 256.',\n            volumeName:\n                'Este campo debe estar compuesto por letras, números, \".\", \"-\", y \"_\" con una longitud de 2 a 30.',\n            supervisorName:\n                'Este campo debe comenzar con un carácter no especial y debe estar compuesto por letras, números, \"-\", y \"_\" con una longitud de 1 a 128.',\n            composeName:\n                'Debe comenzar con un carácter no especial, permite minúsculas, números, - y _, longitud de 1 a 256',\n            complexityPassword:\n                'Este campo debe estar compuesto por letras, números, con una longitud de 8 a 30 y contener al menos dos caracteres especiales.',\n            commonPassword: 'La longitud de este campo debe ser mayor a 6.',\n            linuxName:\n                'La longitud de este campo debe estar entre 1 y 128. El campo no debe contener los siguientes caracteres especiales: \"{0}\".',\n            email: 'Este campo debe ser una dirección de correo electrónico válida.',\n            number: 'Este campo debe ser un número.',\n            integer: 'Este campo debe ser un número entero positivo.',\n            ip: 'Este campo debe ser una dirección IP válida.',\n            host: 'Este campo debe ser una dirección IP válida o un nombre de dominio.',\n            hostHelper: 'Admite introducir dirección IP o nombre de dominio',\n            port: 'Este campo debe ser un número de puerto válido.',\n            selectHelper: 'Por favor seleccione el archivo {0} correcto',\n            domain: 'Este campo debe tener el formato: ejemplo.com o ejemplo.com:8080.',\n            databaseName: 'Este campo debe estar compuesto por letras, números y \"_\" con una longitud de 1 a 30.',\n            ipErr: 'Este campo debe ser una dirección IP válida.',\n            numberRange: 'Este campo debe ser un número entre {0} y {1}.',\n            paramName:\n                'Este campo debe estar compuesto por letras, números, \".\", \"-\", y \"_\" con una longitud de 2 a 30.',\n            paramComplexity:\n                'Este campo no debe comenzar ni terminar con caracteres especiales y debe estar compuesto por letras, números y los caracteres \"{0}\", con una longitud de 6 a 128.',\n            paramUrlAndPort: 'Este campo debe tener el formato \"http(s)://(nombre de dominio/ip):(puerto)\".',\n            nginxDoc: 'Este campo debe estar compuesto por letras, números y \".\".',\n            appName:\n                'Este campo no debe comenzar ni terminar con \"-\" o \"_\" y debe estar compuesto por letras, números, \"-\", y \"_\" con una longitud de 2 a 30.',\n            containerName: 'Permite letras, números, -, _ y .; no puede comenzar con - _ o .; longitud: 2-128',\n            mirror: 'La dirección de aceleración del mirror debe comenzar con http(s)://, soporta letras (mayúsculas y minúsculas), números, . / y -, y no debe contener líneas en blanco.',\n            disableFunction: 'Solo admite letras, guiones bajos y comas',\n            leechExts: 'Solo admite letras, números y comas',\n            paramSimple: 'Admite letras minúsculas y números, longitud de 1 a 128',\n            filePermission: 'Error en el permiso del archivo',\n            formatErr: 'Error de formato, por favor verifique e intente nuevamente',\n            phpExtension: 'Solo admite _, letras minúsculas en inglés y números',\n            paramHttp: 'Debe comenzar con http:// o https://',\n            phone: 'El formato del número de teléfono es incorrecto',\n            authBasicPassword: 'Admite letras, números y caracteres especiales comunes, longitud de 1 a 72',\n            length128Err: 'La longitud no puede exceder los 128 caracteres',\n            maxLength: 'La longitud no puede exceder los {0} caracteres',\n            alias: 'Soporta A->Z, números, - and _, largo 1-128 carácteres, no puede empezar ni terminar con -_.',\n        },\n        res: {\n            paramError: 'La solicitud falló, por favor intente de nuevo más tarde.',\n            forbidden: 'El usuario actual no tiene permisos',\n            serverError: 'Excepción del servicio',\n            notFound: 'El recurso no existe',\n            commonError: 'La solicitud falló',\n        },\n        service: {\n            serviceNotStarted: 'El servicio {0} no está iniciado.',\n        },\n        status: {\n            running: 'En ejecución',\n            done: 'Completado',\n            scanFailed: 'Incompleto',\n            success: 'Completado con éxito',\n            waiting: 'En espera',\n            waitForUpgrade: 'Esperar Actualización',\n            failed: 'Fallido',\n            stopped: 'Detenido',\n            error: 'Error',\n            created: 'Creado',\n            restarting: 'Reiniciando',\n            uploading: 'Cargando',\n            unhealthy: 'No saludable',\n            removing: 'Eliminando',\n            paused: 'Pausado',\n            exited: 'Finalizado',\n            dead: 'Muerto',\n            installing: 'Instalando',\n            enabled: 'Habilitado',\n            disabled: 'Deshabilitado',\n            normal: 'Normal',\n            building: 'Construyendo',\n            upgrading: 'Actualizando',\n            pending: 'Pendiente de editarse',\n            rebuilding: 'Reconstruyendo',\n            deny: 'Denegado',\n            accept: 'Aceptado',\n            used: 'En uso',\n            unused: 'Sin usar',\n            starting: 'Iniciando',\n            recreating: 'Recreando',\n            creating: 'Creando',\n            init: 'Esperando aplicación',\n            ready: 'normal',\n            applying: 'Aplicando',\n            uninstalling: 'Desinstalando',\n            lost: 'Contacto perdido',\n            bound: 'Vinculado',\n            unbind: 'Desvinculado',\n            exceptional: 'Excepcional',\n            free: 'Libre',\n            enable: 'Habilitado',\n            disable: 'Deshabilitado',\n            deleted: 'Eliminado',\n            downloading: 'Descargando',\n            packing: 'Empaquetando',\n            sending: 'Enviando',\n            healthy: 'Normal',\n            executing: 'Ejecutando',\n            installerr: 'Instalación fallida',\n            applyerror: 'Aplicación fallida',\n            systemrestart: 'Interrumpido',\n            starterr: 'Error al iniciar',\n            uperr: 'Error al iniciar',\n            new: 'Nuevo',\n            conflict: 'Conflicto',\n            duplicate: 'Duplicado',\n            unexecuted: 'No Ejecutado',\n        },\n        units: {\n            second: ' segundo | segundo | segundos',\n            minute: 'minuto | minuto | minutos',\n            hour: 'hora | hora | horas',\n            day: 'día | día | días',\n            week: 'semana | semana | semanas',\n            month: 'mes | mes | meses',\n            year: 'año | año | años',\n            time: 'rqm',\n            core: 'núcleo | núcleo | núcleos',\n            millisecond: 'milisegundo | milisegundos',\n            secondUnit: 's',\n            minuteUnit: 'min',\n            hourUnit: 'h',\n            dayUnit: 'd',\n            semicolon: ';',\n        },\n        log: {\n            noLog: 'No logs available',\n        },\n    },\n    menu: {\n        home: 'Panel',\n        apps: 'Tienda de aplicaciones',\n        website: 'Sitio | Sitios',\n        project: 'Proyecto | Proyectos',\n        config: 'Configuración | Configuraciones',\n        ssh: 'Configuración SSH',\n        firewall: 'Cortafuegos',\n        ssl: 'Certificado | Certificados',\n        database: 'Base de datos | Bases de datos',\n        aiTools: 'IA',\n        mcp: 'MCP',\n        container: 'Contenedor | Contenedores',\n        cronjob: 'Tarea programada | Tareas programadas',\n        system: 'Sistema',\n        security: 'Seguridad',\n        files: 'Explorador de archivos',\n        monitor: 'Monitoreo',\n        terminal: 'Terminal | Terminales',\n        settings: 'Configuración',\n        toolbox: 'Caja de herramientas',\n        logs: 'Registro | Registros',\n        runtime: 'Ejecución | Entornos de ejecución',\n        processManage: 'Proceso | Procesos',\n        process: 'Proceso | Procesos',\n        network: 'Red | Redes',\n        supervisor: 'Supervisor',\n        tamper: 'Antimanipulación',\n        app: 'Aplicación',\n        msgCenter: 'Tareas',\n        disk: 'Disco',\n        filter: 'Filtro',\n    },\n    home: {\n        recommend: 'Recomendado',\n        dir: 'Directorio',\n        quickDir: 'Acceso rápido',\n        minQuickJump: 'Agrega al menos una entrada de acceso rápido',\n        maxQuickJump: 'Puedes agregar hasta cuatro entradas de acceso rápido',\n        database: 'Database - All',\n        restart_1panel: 'Reiniciar panel',\n        restart_system: 'Reiniciar servidor',\n        operationSuccess: 'Completado con éxito. Reiniciando ahora, actualiza el navegador luego.',\n        entranceHelper:\n            'La entrada segura no está habilitada. Actívala en \"Configuraciones -> Seguridad\" para mejorar la seguridad.',\n        appInstalled: 'Aplicaciones',\n        systemInfo: 'Información del sistema',\n        hostname: 'Nombre del host',\n        platformVersion: 'Sistema operativo',\n        kernelVersion: 'Kernel',\n        kernelArch: 'Arquitectura',\n        network: 'Red',\n        io: 'Disco I/O',\n        ip: 'IP local',\n        proxy: 'Proxy del sistema',\n        baseInfo: 'Información básica',\n        totalSend: 'Total enviado',\n        totalRecv: 'Total recibido',\n        rwPerSecond: 'Operaciones de E/S',\n        ioDelay: 'Latencia de E/S',\n        uptime: 'Tiempo en funcionamiento',\n        runningTime: 'Desde',\n        mem: 'Memoria del Sistema',\n        swapMem: 'Partición swap',\n        runSmoothly: 'Carga baja',\n        runNormal: 'Carga moderada',\n        runSlowly: 'Carga alta',\n        runJam: 'Carga pesada',\n        core: 'Núcleo físico',\n        logicCore: 'Núcleo lógico',\n        corePercent: 'Uso del Núcleo',\n        cpuFrequency: 'Frecuencia CPU',\n        cpuDetailedPercent: 'Distribución del Tiempo de CPU',\n        cpuUser: 'Usuario',\n        cpuSystem: 'Sistema',\n        cpuIdle: 'Inactivo',\n        cpuIrq: 'IRQ',\n        cpuSoftirq: 'Soft IRQ',\n        cpuSteal: 'Robado',\n        cpuTop: 'Top 5 de Procesos por Uso de CPU',\n        memTop: 'Top 5 de Procesos por Uso de Memoria',\n        loadAverage: 'Promedio de carga en el último minuto | Promedio de carga en los últimos {n} minutos',\n        load: 'Carga',\n        mount: 'Punto de montaje',\n        fileSystem: 'Sistema de archivos',\n        total: 'Total',\n        used: 'En uso',\n        cache: 'Cache',\n        free: 'Libre',\n        shard: 'Fragmentado',\n        available: 'Disponible',\n        percent: 'Utilización',\n        goInstall: 'Ir a instalar',\n        networkCard: 'Tarjeta de red',\n        disk: 'Disco',\n        memo: 'Memo',\n        memoPlaceholder: 'Haz clic en el botón de editar para habilitar la edición.',\n        carouselSetting: 'Configuración del carrusel',\n        tooltipSensitiveInfo: 'Mostrar/Ocultar información sensible',\n        alias: 'Alias',\n    },\n    tabs: {\n        more: 'Más',\n        hide: 'Ocultar',\n        closeLeft: 'Cerrar izquierda',\n        closeRight: 'Cerrar derecha',\n        closeCurrent: 'Cerrar actual',\n        closeOther: 'Cerrar otras',\n        closeAll: 'Cerrar todo',\n    },\n    header: {\n        logout: 'Cerrar sesión',\n    },\n    database: {\n        manage: 'Administrar base de datos',\n        deleteBackupHelper: 'Eliminar copias de seguridad de la base de datos simultáneamente',\n        delete: 'La operación de eliminación no se puede revertir, por favor introduzca \"',\n        deleteHelper: '\" para eliminar esta base de datos',\n        noMysql: 'Servicio de base de datos (MySQL o MariaDB)',\n        noPostgresql: 'Servicio de base de datos PostgreSQL',\n        goUpgrade: 'Ir a actualizar',\n        goInstall: 'Ir a instalar',\n        isDelete: 'Eliminada',\n        permission: 'Cambiar permisos',\n        format: 'Juego de Caracteres',\n        collation: 'Intercalación',\n        collationHelper: 'Si está vacío, use la intercalación predeterminada del juego de caracteres {0}',\n        permissionForIP: 'IP',\n        permissionAll: 'Todos (%)',\n        localhostHelper:\n            'Configurar los permisos de la base de datos como \"localhost\" para el despliegue en contenedores impedirá el acceso externo al contenedor. ¡Por favor elija cuidadosamente!',\n        databaseConnInfo: 'Conexión',\n        rootPassword: 'Contraseña root',\n        serviceName: 'Nombre del servicio',\n        serviceNameHelper: 'Acceso entre contenedores en la misma red.',\n        backupList: 'Respaldo',\n        loadBackup: 'Importar',\n        localUpload: 'Subida local',\n        hostSelect: 'Server Selection',\n        selectHelper: 'Estas seguro de importar el archivo de backup {0}?',\n        remoteAccess: 'Acceso remoto',\n        remoteHelper: 'Múltiples IP separadas por comas, ejemplo: 172.16.10.111, 172.16.10.112',\n        remoteConnHelper:\n            'Conectarse remotamente como usuario root en MySQL puede presentar riesgos de seguridad. Por lo tanto, realice esta operación con precaución.',\n        changePassword: 'Cambiar contraseña',\n        changeConnHelper: 'Esta operación modificará la base de datos actual {0}. ¿Desea continuar?',\n        changePasswordHelper:\n            'La base de datos ha sido asociada a una aplicación. Cambiar la contraseña también cambiará la contraseña de la base de datos en la aplicación. El cambio se aplica después de reiniciar la aplicación.',\n        recoverTimeoutHelper: '-1 significa sin límite de tiempo de espera',\n        confChange: 'Configuración',\n        confNotFound:\n            'No se pudo encontrar el archivo de configuración. Por favor actualice la aplicación a la última versión en la tienda y vuelva a intentarlo.',\n        portHelper:\n            'Este puerto es el puerto expuesto del contenedor. Debe guardar la modificación por separado y reiniciar el contenedor.',\n        loadFromRemote: 'Sincronizar desde el servidor',\n        userBind: 'Vincular usuario',\n        pgBindHelper:\n            'Esta operación se utiliza para crear un nuevo usuario y vincularlo a la base de datos destino. Actualmente no se admite seleccionar usuarios ya existentes en la base de datos.',\n        pgSuperUser: 'Superusuario',\n        loadFromRemoteHelper:\n            'Esto sincronizará la información de la base de datos del servidor a 1Panel. ¿Desea continuar?',\n        passwordHelper: 'No se puede obtener, haga clic para modificar',\n        remote: 'Remoto',\n        remoteDB: 'Servidor remoto | Servidores remotos',\n        manageRemoteDB: 'Administrar servidores remotos',\n        createRemoteDB: 'Vincular @.lower:database.remoteDB',\n        unBindRemoteDB: 'Desvincular @.lower:database.remoteDB',\n        unBindForce: 'Forzar desvinculación',\n        unBindForceHelper:\n            'Ignorar todos los errores durante la desvinculación para asegurar que la operación final sea exitosa',\n        unBindRemoteHelper:\n            'Desvincular la base de datos remota solo eliminará la relación de vinculación y no eliminará directamente la base de datos remota',\n        editRemoteDB: 'Editar servidor remoto',\n        localDB: 'Base de datos local',\n        address: 'Dirección de la base de datos',\n        version: 'Versión de la base de datos',\n        userHelper: 'El usuario root o un usuario con privilegios de root puede acceder a la base de datos remota.',\n        pgUserHelper: 'Usuario de base de datos con privilegios de superusuario.',\n        ssl: 'Usar SSL',\n        clientKey: 'Clave privada del cliente',\n        clientCert: 'Certificado del cliente',\n        caCert: 'Certificado CA',\n        hasCA: 'Posee certificado CA',\n        skipVerify: 'Omitir la verificación de validez del certificado',\n        initialDB: 'Base de Datos Inicial',\n        formatHelper:\n            'El conjunto de caracteres actual de la base de datos es {0}, la inconsistencia de conjuntos puede causar errores al recuperar',\n        dropHelper: 'Puede arrastrar y soltar el archivo aquí o',\n        clickHelper: 'hacer clic para subir',\n        supportUpType:\n            'Solo se admiten los formatos de archivo sql, sql.gz, tar.gz y .zip. El archivo comprimido importado debe contener solo un archivo .sql o incluir test.sql.',\n        currentStatus: 'Estado actual',\n        baseParam: 'Parámetro básico',\n        performanceParam: 'Parámetro de rendimiento',\n        runTime: 'Tiempo de inicio',\n        connections: 'Total de conexiones',\n        bytesSent: 'Bytes enviados',\n        bytesReceived: 'Bytes recibidos',\n        queryPerSecond: 'Consultas por segundo',\n        txPerSecond: 'Tx por segundo',\n        connInfo: 'Conexiones activas/pico',\n        connInfoHelper: 'Si el valor es demasiado grande, aumente \"max_connections\".',\n        threadCacheHit: 'Cache de hilos exitoso',\n        threadCacheHitHelper: 'Si es muy bajo, aumente \"thread_cache_size\".',\n        indexHit: 'Índice exitoso',\n        indexHitHelper: 'Si es muy bajo, aumente \"key_buffer_size\".',\n        innodbIndexHit: 'Tasa de aciertos del índice Innodb',\n        innodbIndexHitHelper: 'Si es muy bajo, aumente \"innodb_buffer_pool_size\".',\n        cacheHit: 'Aciertos en caché de consulta',\n        cacheHitHelper: 'Si es muy bajo, aumente \"query_cache_size\".',\n        tmpTableToDB: 'Tabla temporal en disco',\n        tmpTableToDBHelper: 'Si es muy alto, intente aumentar \"tmp_table_size\".',\n        openTables: 'Tablas abiertas',\n        openTablesHelper: 'El valor de configuración de \"table_open_cache\" debe ser mayor o igual a este valor.',\n        selectFullJoin: 'Seleccionar join completo',\n        selectFullJoinHelper: 'Si el valor no es 0, verifique si el índice de la tabla es correcto.',\n        selectRangeCheck: 'Uniones sin índice',\n        selectRangeCheckHelper: 'Si el valor no es 0, verifique si el índice de la tabla es correcto.',\n        sortMergePasses: 'Cantidad de merges ordenados',\n        sortMergePassesHelper: 'Si es muy alto, aumente \"sort_buffer_size\".',\n        tableLocksWaited: 'Bloqueos de tabla',\n        tableLocksWaitedHelper: 'Si el valor es muy alto, considere mejorar el rendimiento de su base de datos.',\n        performanceTuning: 'Ajuste de rendimiento',\n        optimizationScheme: 'Esquema de optimización',\n        keyBufferSizeHelper: 'Tamaño del buffer para índices',\n        queryCacheSizeHelper: 'Caché de consulta. Si la función está deshabilitada, establezca este parámetro en 0.',\n        tmpTableSizeHelper: 'Tamaño del caché de tabla temporal',\n        innodbBufferPoolSizeHelper: 'Tamaño del buffer Innodb',\n        innodbLogBufferSizeHelper: 'Tamaño del buffer de logs de Innodb',\n        sortBufferSizeHelper: '* conexiones, tamaño del buffer de ordenamiento por hilo',\n        readBufferSizeHelper: '* conexiones, tamaño del buffer de lectura',\n        readRndBufferSizeHelper: '* conexiones, tamaño del buffer de lectura aleatoria',\n        joinBufferSizeHelper: '* conexiones, tamaño del caché de asociación de tablas',\n        threadStackelper: '* conexiones, tamaño de pila por hilo',\n        binlogCacheSizeHelper: '* conexiones, caché del log binario (múltiplos de 4096)',\n        threadCacheSizeHelper: 'Tamaño del pool de hilos',\n        tableOpenCacheHelper: 'Caché de tablas',\n        maxConnectionsHelper: 'Máximo de conexiones',\n        restart: 'Reiniciar',\n        slowLog: 'Registros lentos',\n        noData: 'Aún no hay registros lentos.',\n        isOn: 'Activado',\n        longQueryTime: 'umbral(es)',\n        thresholdRangeHelper: 'Por favor, introduzca el umbral correcto (1 - 600).',\n        timeout: 'Tiempo de espera',\n        timeoutHelper: 'Tiempo de espera de conexión inactiva. 0 indica que la conexión está siempre activa.',\n        maxclients: 'Máximo de clientes',\n        requirepassHelper:\n            'Déjelo en blanco si no se ha establecido ninguna contraseña. Los cambios deben guardarse por separado y el contenedor debe reiniciarse.',\n        databases: 'Cantidad de bases de datos',\n        maxmemory: 'Uso máximo de memoria',\n        maxmemoryHelper: '0 indica sin restricción.',\n        tcpPort: 'Puerto de escucha actual.',\n        uptimeInDays: 'Días en operación.',\n        connectedClients: 'Cantidad de clientes conectados.',\n        usedMemory: 'Uso de memoria actual de Redis.',\n        usedMemoryRss: 'Tamaño de memoria solicitado al sistema operativo.',\n        usedMemoryPeak: 'Pico de uso de memoria de Redis.',\n        memFragmentationRatio: 'Ratio de fragmentación de memoria.',\n        totalConnectionsReceived: 'Total de clientes conectados desde el inicio.',\n        totalCommandsProcessed: 'Total de comandos ejecutados desde el inicio.',\n        instantaneousOpsPerSec: 'Comandos ejecutados por el servidor por segundo.',\n        keyspaceHits: 'Cantidad de veces que se encontró la clave de la base de datos.',\n        keyspaceMisses: 'Cantidad de intentos fallidos para encontrar la clave.',\n        hit: 'Proporción de aciertos de clave de la base de datos.',\n        latestForkUsec: 'Microsegundos consumidos en la última operación fork().',\n        redisCliHelper: 'No se detectó el servicio \"redis-cli\". Primero habilite el servicio.',\n        redisQuickCmd: 'Comandos rápidos de Redis',\n        recoverHelper: 'Esto sobrescribirá los datos con [{0}]. ¿Desea continuar?',\n        submitIt: 'Sobrescribir los datos',\n        baseConf: 'Básico',\n        allConf: 'Todos',\n        restartNow: 'Reiniciar ahora',\n        restartNowHelper1:\n            'Necesita reiniciar el sistema después de que los cambios de configuración tengan efecto. Si necesita persistir los datos, guarde antes de reiniciar.',\n        restartNowHelper: 'Esto solo surtirá efecto después de reiniciar el sistema.',\n        persistence: 'Persistencia',\n        rdbHelper1: 'segundo(s), insertar',\n        rdbHelper2: 'piezas de datos',\n        rdbHelper3: 'Cumplir cualquiera de las condiciones activará la persistencia RDB.',\n        rdbInfo: 'Asegúrese de que el valor de la regla esté entre 1 y 100000',\n        containerConn: 'Conexión del contenedor',\n        copyConnURL: 'Copiar URL de conexión',\n        connAddress: 'Dirección',\n        containerConnHelper:\n            'Esta dirección de conexión es usada por aplicaciones en entorno de ejecución PHP o instalaciones en contenedores.',\n        remoteConn: 'Conexión externa',\n        remoteConnHelper2:\n            'Esta dirección de conexión puede ser usada por aplicaciones fuera del contenedor o externas.',\n        remoteConnHelper3:\n            'La dirección de acceso predeterminada es la IP del host. Para modificarla, diríjase a la configuración de \"Dirección de acceso predeterminada\" en la página de ajustes del panel.',\n        localIP: 'IP local',\n    },\n    aiTools: {\n        agents: {\n            agents: 'Agentes',\n            agent: 'Agente',\n            account: 'Cuenta de modelo',\n            noAccountHint: 'Selecciona una cuenta de modelo existente o agrega una nueva.',\n            accountCount: '{0} cuentas de modelo',\n            syncAgents: 'Sincronizar agentes vinculados',\n            syncAgentsHelper: 'Actualiza openclaw.json para los agentes que usan esta cuenta de modelo',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: 'Versión de la app',\n            webuiPort: 'Puerto WebUI',\n            allowedOrigins: 'Direcciones de acceso',\n            allowedOriginsHelper:\n                'Introduce una dirección de acceso completa por línea. Se recomienda usar HTTPS, por ejemplo https://192.168.1.2:18789. Si no hay una dirección predeterminada configurada, rellénala manualmente.',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: 'Introduce al menos una dirección de acceso',\n            allowedOriginsInvalid: 'Usa el formato http(s)://host-o-ip[:puerto]',\n            provider: 'Proveedor de modelos',\n            apiKey: 'Clave API',\n            baseUrl: 'URL base',\n            accountModels: 'Model Catalog',\n            accountModelsHelper: 'Configure the models this account exposes to OpenClaw for switching and settings',\n            accountModelsRequired: 'Configure at least one model',\n            accountModelsDuplicate: 'Duplicate models exist in the catalog',\n            modelPool: 'Model Pool',\n            modelPoolHelper:\n                'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',\n            modelInputTypes: 'Input Types',\n            reasoning: 'Reasoning Model',\n            token: 'Token',\n            manualModel: 'Entrada manual de modelo',\n            verified: 'Verificado',\n            verifySkipped: 'Sin verificacion',\n            configTitle: 'Configuration',\n            settingsTab: 'Settings',\n            securityTab: 'Security',\n            otherTab: 'Other',\n            timeZone: 'Zona horaria',\n            browserEnabled: 'Browser Enabled',\n            switchModelSuccess: 'Model switched successfully',\n            channelsTab: 'Channels',\n            wecom: 'WeCom',\n            dingtalk: 'DingTalk',\n            feishu: 'Feishu',\n            pluginNotInstalled: 'El plugin no está instalado. Instálalo primero.',\n            dmPolicy: 'DM Policy',\n            groupPolicy: 'Group Policy',\n            policyPairing: 'Pairing',\n            policyAllowlist: 'Allowlist',\n            policyOpen: 'Open',\n            policyDisabled: 'Disabled',\n            botName: 'Bot Name',\n            botId: 'Bot ID',\n            appId: 'App ID',\n            appSecret: 'App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: 'DM Allowlist',\n            allowFromHelper: 'One sender ID per line. Used only when DM Policy is Allowlist.',\n            allowFromPlaceholder: 'One sender ID per line',\n            groupAllowFrom: 'Group Allowlist',\n            groupAllowFromHelper: 'One group ID per line. Used only when Group Policy is Allowlist.',\n            groupAllowFromPlaceholder: 'One group ID per line',\n            allowFromRequired: 'Enter at least one allowlist entry',\n            saveAndRestartGateway: 'Save and restart gateway',\n            pairingCode: 'Pairing Code',\n            pairingCodePlaceholder: 'Enter pairing code',\n            approvePairing: 'Approve Pairing',\n            feishuRequired: 'Please fill botName / appId / appSecret',\n            saveSuccess: 'Saved successfully',\n            pairingCodeRequired: 'Please enter pairing code',\n            pairingApproveSuccess: 'Pairing approved successfully',\n            customProviderHelper: 'En el proveedor de modelo personalizado no se valida si la cuenta está disponible',\n            feishuSaveSuccess: 'Guardado en Feishu',\n        },\n        model: {\n            model: 'Modelo',\n            localModel: 'Modelos locales',\n            create: 'Agregar modelo',\n            create_helper: 'Descargar \"{0}\"',\n            ollama_doc: 'Puede visitar el sitio oficial de Ollama para buscar y encontrar más modelos.',\n            container_conn_helper: 'Utilice esta dirección para el acceso o conexión entre contenedores',\n            ollama_sync:\n                'Al sincronizar modelos de Ollama, se detectaron los siguientes modelos que no existen. ¿Desea eliminarlos?',\n            from_remote: 'Este modelo no fue descargado vía 1Panel, no hay registros de descarga relacionados.',\n            no_logs: 'Los registros de descarga de este modelo han sido eliminados y no se pueden consultar.',\n            vllmVersionHelper: 'Para servidores FusionXpark GB 10, seleccione la versión -cu130.',\n        },\n        proxy: {\n            proxy: 'Mejoras de proxy de IA',\n            proxyHelper1: 'Vincule un dominio y habilite HTTPS para mejorar la seguridad de la transmisión',\n            proxyHelper2: 'Limite el acceso por IP para evitar exposición pública',\n            proxyHelper3: 'Habilitar transmisión en tiempo real',\n            proxyHelper4: 'Una vez creado, puede verlo y gestionarlo en la lista de sitios web',\n            proxyHelper5:\n                'Después de habilitarlo, puede deshabilitar el acceso externo al puerto en Tienda de Aplicaciones - Instaladas - Ollama - Parámetros para mejorar la seguridad.',\n            proxyHelper6:\n                'Para deshabilitar la configuración del proxy, puede eliminarla desde la lista de sitios web.',\n            whiteListHelper: 'Restringir el acceso solo a las IP incluidas en la lista blanca',\n        },\n        gpu: {\n            gpu: 'Monitoreo de GPU',\n            gpuHelper: 'El sistema no detectó comandos NVIDIA-SMI o XPU-SMI. ¡Compruebe e inténtelo de nuevo!',\n            process: 'Información del Proceso',\n            type: 'Tipo',\n            typeG: 'Gráficos',\n            typeC: 'Cómputo',\n            typeCG: 'Cómputo+Gráficos',\n            processName: 'Nombre del Proceso',\n            shr: 'Memoria Compartida',\n            temperatureHelper: 'La alta temperatura de la GPU puede causar una reducción de la frecuencia de la GPU',\n            gpuUtil: 'Utilización de GPU',\n            temperature: 'Temperatura',\n            performanceState: 'Estado de Rendimiento',\n            powerUsage: 'Consumo de Energía',\n            memoryUsage: 'Utilización de Memoria',\n            fanSpeed: 'Velocidad del Ventilador',\n            power: 'Energía',\n            powerCurrent: 'Energía Actual',\n            powerLimit: 'Límite de Energía',\n            memory: 'Memoria',\n            memoryUsed: 'Memoria Utilizada',\n            memoryTotal: 'Memoria Total',\n            percent: 'Utilización',\n            base: 'Información Básica',\n            driverVersion: 'Versión del Controlador',\n            cudaVersion: 'Versión de CUDA',\n            processMemoryUsage: 'Uso de Memoria',\n            performanceStateHelper: 'Desde P0 (rendimiento máximo) hasta P12 (rendimiento mínimo)',\n            busID: 'Dirección del Bus',\n            persistenceMode: 'Modo de Persistencia',\n            enabled: 'Habilitado',\n            disabled: 'Deshabilitado',\n            persistenceModeHelper:\n                'El modo de persistencia responde a las tareas más rápidamente, pero el consumo de energía en espera aumentará en consecuencia',\n            displayActive: 'Inicialización de GPU',\n            displayActiveT: 'Sí',\n            displayActiveF: 'No',\n            ecc: 'Tecnología de Corrección de Errores',\n            computeMode: 'Modo de Computación',\n            default: 'Predeterminado',\n            exclusiveProcess: 'Proceso Exclusivo',\n            exclusiveThread: 'Hilo Exclusivo',\n            prohibited: 'Prohibido',\n            defaultHelper: 'Predeterminado: Los procesos pueden ejecutarse concurrentemente',\n            exclusiveProcessHelper:\n                'Proceso Exclusivo: Solo un contexto CUDA puede usar la GPU, pero puede ser compartido por múltiples hilos',\n            exclusiveThreadHelper: 'Hilo Exclusivo: Solo un hilo en un contexto CUDA puede usar la GPU',\n            prohibitedHelper: 'Prohibido: No se permite la ejecución concurrente de procesos',\n            migModeHelper:\n                'Se utiliza para crear instancias MIG, implementando aislamiento físico de GPU en la capa de usuario.',\n            migModeNA: 'No Compatible',\n            current: 'Monitoreo en Tiempo Real',\n            history: 'Registros Históricos',\n            notSupport: 'La versión actual o el controlador no admiten mostrar este parámetro.',\n            processCount: 'Procesos',\n        },\n        mcp: {\n            server: 'Servidor MCP',\n            baseUrl: 'Ruta de acceso externa',\n            baseUrlHelper: 'Por ejemplo: http://192.168.1.2:8000',\n            ssePath: 'Ruta SSE',\n            ssePathHelper: 'Por ejemplo: /sse, asegúrese de que no se repita con otros servidores',\n            environment: 'Variables de entorno',\n            envKey: 'Nombre de la variable',\n            envValue: 'Valor de la variable',\n            externalUrl: 'Dirección de conexión externa',\n            operatorHelper: 'Se realizará la operación {1} en {0}, ¿desea continuar?',\n            domain: 'Dirección de acceso predeterminada',\n            domainHelper: 'Por ejemplo: 192.168.1.1 o ejemplo.com',\n            bindDomain: 'Vincular sitio web',\n            commandPlaceHolder: 'Actualmente solo se admiten comandos de inicio npx y binarios',\n            importMcpJson: 'Importar configuración del servidor MCP',\n            importMcpJsonError: 'La estructura de mcpServers es incorrecta',\n            bindDomainHelper:\n                'Al vincular el sitio web, se modificará la dirección de acceso de todos los servidores MCP instalados y se cerrará el acceso externo a los puertos',\n            outputTransport: 'Tipo de salida',\n            streamableHttpPath: 'Ruta de transmisión',\n            streamableHttpPathHelper:\n                'Por ejemplo: /mcp, tenga en cuenta que no debe superponerse con otros servidores',\n            npxHelper: 'Adecuado para mcp iniciado con npx o binario',\n            uvxHelper: 'Adecuado para mcp iniciado con uvx',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: 'Directorio del Modelo',\n            commandHelper:\n                'Si se necesita acceso externo, establezca el puerto en el comando para que sea el mismo que el puerto de la aplicación',\n            imageAlert:\n                'Debido al gran tamaño de la imagen, se recomienda descargar manualmente la imagen al servidor antes de la instalación',\n            modelSpeedup: 'Habilitar aceleración de modelo',\n            modelType: 'Tipo de modelo',\n        },\n    },\n    container: {\n        createByCommand: 'Crear por comando',\n        importContainerBackupTip: 'Importar archivo de copia de seguridad de contenedor, solo se admite .tar.gz',\n        importComposeBackupTip: 'Importar archivo de copia de seguridad de compose, solo se admite .tar.gz',\n        stopContainerBeforeBackup: 'Detener contenedor antes de la copia de seguridad',\n        stopComposeBeforeBackup: 'Detener compose antes de la copia de seguridad',\n        stopBeforeBackupHelper:\n            'Al habilitarlo, se detendrá el contenedor o servicio compose antes del respaldo y se restaurará automáticamente al finalizar para garantizar la consistencia de datos.',\n        commandInput: 'Introducir comando',\n        commandRule: 'Por favor introduzca el comando correcto para crear el contenedor con docker run.',\n        commandHelper: 'Este comando se ejecutará en el servidor para crear el contenedor. ¿Desea continuar?',\n        updateHelper1:\n            'Se detectó que este contenedor proviene de la tienda de aplicaciones. Tenga en cuenta lo siguiente:',\n        updateHelper2:\n            '1. Las modificaciones actuales no se sincronizarán con las aplicaciones instaladas en la tienda.',\n        updateHelper3:\n            '2. Si modifica la aplicación en la página de instalados, el contenido editado actualmente quedará inválido.',\n        updateHelper4:\n            'Editar el contenedor requiere reconstruirlo y se perderán los datos no persistentes. ¿Desea continuar?',\n        containerList: 'Lista de contenedores',\n        operatorHelper: 'Se realizará {0} en el siguiente contenedor, ¿desea continuar?',\n        operatorAppHelper:\n            'La operación \"{0}\" se realizará en los siguientes contenedores y puede afectar los servicios en ejecución. ¿Desea continuar?',\n        containerDeleteHelper:\n            \"Se detectó que el contenedor proviene de la Tienda de Aplicaciones. Eliminar el contenedor no lo eliminará completamente de 1Panel. Para eliminarlo por completo, vaya a la Tienda de Aplicaciones -> menús 'Instalado' o 'Entorno de Ejecución'. ¿Continuar?\",\n        start: 'Iniciar',\n        stop: 'Detener',\n        restart: 'Reiniciar',\n        kill: 'Finalizar',\n        pause: 'Pausar',\n        unpause: 'Reanudar',\n        rename: 'Renombrar',\n        remove: 'Eliminar',\n        removeAll: 'Eliminar todos',\n        containerPrune: 'Limpiar',\n        containerPruneHelper1: 'Esto eliminará todos los contenedores que estén detenidos.',\n        containerPruneHelper2:\n            'Si los contenedores son de la tienda de aplicaciones, vaya a \"Tienda de Aplicaciones -> Instaladas\" y haga clic en \"Reconstruir\" para reinstalarlos después de la limpieza.',\n        containerPruneHelper3: 'Esta operación no se puede deshacer. ¿Desea continuar?',\n        imagePrune: 'Limpiar',\n        imagePruneSome: 'Limpiar sin etiqueta',\n        imagePruneSomeEmpty: 'No hay imágenes con la etiqueta \"none\" para limpiar.',\n        imagePruneSomeHelper:\n            'Limpia las imágenes con la etiqueta \"none\" que no estén siendo usadas por ningún contenedor.',\n        imagePruneAll: 'Limpiar no usadas',\n        imagePruneAllEmpty: 'No hay imágenes sin uso para limpiar.',\n        imagePruneAllHelper: 'Limpia las imágenes que no estén siendo usadas por ningún contenedor.',\n        networkPrune: 'Limpiar',\n        networkPruneHelper: 'Esto eliminará todas las redes no utilizadas. ¿Desea continuar?',\n        volumePrune: 'Limpiar',\n        volumePruneHelper: 'Esto eliminará todos los volúmenes locales no utilizados. ¿Desea continuar?',\n        cleanSuccess: 'La operación se completó correctamente, cantidad limpiada: {0}!',\n        cleanSuccessWithSpace:\n            'La operación se completó correctamente. El número de discos limpiados esta vez es {0}. El espacio liberado es {1}!',\n        unExposedPort: 'La dirección de mapeo del puerto actual es 127.0.0.1, lo que impide el acceso externo.',\n        upTime: 'Tiempo activo',\n        fetch: 'Obtener',\n        lines: 'Líneas',\n        linesHelper: 'Por favor introduzca el número correcto de líneas de logs a recuperar.',\n        downloadLinesHelper: 'Seleccione o introduzca el número de líneas de log para descargar.',\n        lastDay: 'Último día',\n        last4Hour: 'Últimas 4 horas',\n        lastHour: 'Última hora',\n        last10Min: 'Últimos 10 minutos',\n        cleanLog: 'Limpiar logs',\n        downLogHelper1: 'Esto descargará todos los logs del contenedor {0}. ¿Desea continuar?',\n        downLogHelper2: 'Esto descargará los últimos {0} logs del contenedor {0}. ¿Desea continuar?',\n        cleanLogHelper: 'Esto requerirá reiniciar el contenedor y no se puede deshacer. ¿Desea continuar?',\n        newName: 'Nuevo nombre',\n        workingDir: 'Directorio de trabajo',\n        source: 'Uso de recursos',\n        cpuUsage: 'Uso de CPU',\n        cpuTotal: 'CPU total',\n        core: 'Núcleo',\n        memUsage: 'Uso de memoria',\n        memTotal: 'Límite de memoria',\n        memCache: 'Caché de memoria',\n        loadSize: 'Obtener Tamaño del Contenedor',\n        ip: 'Dirección IP',\n        cpuShare: 'Proporción de CPU',\n        cpuShareHelper:\n            'El motor de contenedores usa un valor base de 1024 para la proporción de CPU. Puede aumentarlo para darle al contenedor más tiempo de CPU.',\n        inputIpv4: 'Ejemplo: 192.168.1.1',\n        inputIpv6: 'Ejemplo: 2001:0db8:85a3:0000:0000:8a2e:0370:7334',\n        diskUsage: 'Uso del Disco',\n        localVolume: 'Volumen de Almacenamiento Local',\n        buildCache: 'Caché de Construcción',\n        usage: 'Usado: {0}, Liberable: {1}',\n        clean: 'Liberar',\n        imageClean:\n            'Limpiar imágenes eliminará todas las imágenes no utilizadas. Esta operación no se puede deshacer. ¿Continuar?',\n        containerClean:\n            'Limpiar contenedores eliminará todos los contenedores detenidos (incluidas las aplicaciones detenidas de la Tienda de Aplicaciones). Esta operación no se puede deshacer. ¿Continuar?',\n        sizeRw: 'Tamaño de Capa de Contenedor',\n        sizeRwHelper: 'Tamaño de la capa escribible exclusiva del contenedor',\n        sizeRootFs: 'Tamaño Virtual',\n        sizeRootFsHelper:\n            'Tamaño total de todas las capas de imagen de las que depende el contenedor + capa del contenedor',\n        containerFromAppHelper:\n            'Se detectó que este contenedor proviene de la tienda de aplicaciones. Las operaciones sobre la app pueden invalidar los cambios actuales.',\n        containerFromAppHelper1:\n            'Haga clic en el botón [Parámetros] en la lista de aplicaciones instaladas para acceder a la página de edición y modificar el nombre del contenedor.',\n        command: 'Comando',\n        console: 'Interacción con el contenedor',\n        tty: 'Asignar un pseudo-TTY (-t)',\n        openStdin: 'Mantener STDIN abierto (-i)',\n        custom: 'Personalizado',\n        emptyUser: 'Si está vacío, iniciará sesión como predeterminado',\n        privileged: 'Privilegiado',\n        privilegedHelper:\n            'Permite que el contenedor realice ciertas operaciones privilegiadas en el host, lo que puede aumentar los riesgos. ¡Úselo con precaución!',\n        upgradeHelper: 'Nombre de repositorio/imagen: versión de la imagen',\n        upgradeWarning2:\n            'La operación de actualización requiere reconstruir el contenedor, cualquier dato no persistente se perderá. ¿Desea continuar?',\n        oldImage: 'Imagen actual',\n        sameImageContainer: 'Contenedores de la misma imagen',\n        sameImageHelper:\n            'Los contenedores que usan la misma imagen pueden actualizarse en lote después de seleccionarlos',\n        targetImage: 'Imagen objetivo',\n        imageLoadErr: 'No se detectó un nombre de imagen para el contenedor',\n        imageUpdateTagEmpty: 'No se detectaron etiquetas de imagen actualizables',\n        appHelper:\n            'El contenedor proviene de la tienda de aplicaciones, y al actualizar podría hacer que el servicio no esté disponible.',\n        resource: 'Recurso',\n        input: 'Introducir manualmente',\n        forcePull: 'Siempre descargar imagen',\n        forcePullHelper:\n            'Esto ignorará las imágenes existentes en el servidor y descargará la más reciente desde el repositorio.',\n        imageUpdateHelper:\n            'Comprobar la misma etiqueta en el registro y, si hay cambios, descargar y actualizar la imagen local.',\n        server: 'Servidor',\n        serverExample: '80, 80-88, ip:80 o ip:80-88',\n        containerExample: '80 o 80-88',\n        exposePort: 'Exponer puerto',\n        exposeAll: 'Exponer todos',\n        cmdHelper: 'Ejemplo: nginx -g \"daemon off;\"',\n        entrypointHelper: 'Ejemplo: docker-entrypoint.sh',\n        autoRemove: 'Eliminar automáticamente',\n        cpuQuota: 'Cantidad de núcleos de CPU',\n        memoryLimit: 'Memoria',\n        limitHelper: 'Si se establece en 0, no hay limitación. El valor máximo es {0}',\n        macAddr: 'Dirección MAC',\n        mount: 'Montaje',\n        volumeOption: 'Volumen',\n        hostOption: 'Host',\n        serverPath: 'Ruta en el servidor',\n        containerDir: 'Ruta en el contenedor',\n        volumeHelper: 'Asegúrese de que el contenido del volumen de almacenamiento sea correcto',\n        networkEmptyHelper: 'Por favor confirme que la selección de red del contenedor es correcta',\n        modeRW: 'RW',\n        modeR: 'R',\n        sharedLabel: 'Modo de Propagación',\n        private: 'Privado',\n        privateHelper: 'Los cambios de montaje en el contenedor y el host no se afectan mutuamente',\n        rprivate: 'Privado Recursivo',\n        rprivateHelper: 'Todos los montajes en el contenedor están completamente aislados del host',\n        shared: 'Compartido',\n        sharedHelper: 'Los cambios de montaje en el host y el contenedor son visibles entre sí',\n        rshared: 'Compartido Recursivo',\n        rsharedHelper: 'Todos los cambios de montaje en el host y el contenedor son visibles entre sí',\n        slave: 'Esclavo',\n        slaveHelper:\n            'El contenedor puede ver los cambios de montaje del host, pero sus propios cambios no afectan al host',\n        rslave: 'Esclavo Recursivo',\n        rslaveHelper: 'Todos los montajes en el contenedor pueden ver los cambios del host, pero no afectan al host',\n        mode: 'Modo',\n        env: 'Entornos',\n        restartPolicy: 'Política de reinicio',\n        always: 'siempre',\n        unlessStopped: 'a menos que se detenga',\n        onFailure: 'al fallar (cinco veces por defecto)',\n        no: 'nunca',\n        refreshTime: 'Intervalo de actualización',\n        cache: 'Caché',\n        image: 'Imagen | Imágenes',\n        imagePull: 'Descargar',\n        imagePullHelper:\n            'Admite seleccionar múltiples imágenes para descargar, presione Enter después de ingresar cada imagen para continuar',\n        imagePush: 'Subir',\n        imagePushHelper:\n            'Detected that this image has multiple tags. Please confirm that the image name used for pushing is: {0}',\n        imageDelete: 'Eliminar imagen',\n        repoName: 'Repositorio de contenedores',\n        imageName: 'Nombre de la imagen',\n        pull: 'Descargar',\n        path: 'Ruta',\n        importImage: 'Importar',\n        buildArgs: 'Argumentos de Construcción',\n        imageBuild: 'Construir',\n        pathSelect: 'Ruta',\n        label: 'Etiqueta',\n        imageTag: 'Etiqueta de imagen',\n        imageTagHelper:\n            'Admite configurar múltiples etiquetas de imagen, presione Enter después de ingresar cada etiqueta para continuar',\n        push: 'Subir',\n        fileName: 'Nombre de archivo',\n        export: 'Exportar',\n        exportImage: 'Exportar imagen',\n        size: 'Tamaño',\n        tag: 'Etiquetas',\n        tagHelper: 'Una por línea. Por ejemplo,\\nkey1=value1\\nkey2=value2',\n        imageNameHelper: 'Nombre de imagen y etiqueta, por ejemplo: nginx:latest',\n        cleanBuildCache: 'Limpiar caché de compilación',\n        delBuildCacheHelper:\n            'Esto eliminará todos los artefactos almacenados en caché generados durante las compilaciones y no se puede deshacer. ¿Desea continuar?',\n        urlWarning: 'El prefijo de la URL no debe incluir http:// o https://. Por favor modifique.',\n        network: 'Red | Redes',\n        networkHelper:\n            'Esto puede causar que algunas aplicaciones o entornos no funcionen correctamente. ¿Desea continuar?',\n        networkName: 'Nombre',\n        driver: 'Driver',\n        option: 'Opción',\n        attachable: 'Adjuntable',\n        parentNetworkCard: 'Tarjeta de Red Principal',\n        subnet: 'Subred',\n        scope: 'Rango de IP',\n        gateway: 'Puerta de enlace',\n        auxAddress: 'Excluir IP',\n        volume: 'Volumen | Volúmenes',\n        volumeDir: 'Directorio de volumen',\n        nfsEnable: 'Habilitar almacenamiento NFS',\n        nfsAddress: 'Dirección',\n        mountpoint: 'Punto de montaje',\n        mountpointNFSHelper: 'ej: /nfs, /nfs-share',\n        options: 'Opciones',\n        repo: 'Repositorio | Repositorios',\n        httpRepoHelper: 'Operar un repositorio tipo HTTP requiere reiniciar el servicio Docker.',\n        httpRepo:\n            'Elegir el protocolo HTTP requiere reiniciar el servicio Docker para agregarlo a los registros inseguros.',\n        delInsecure: 'Eliminar registro inseguro',\n        delInsecureHelper:\n            'Esto reiniciará el servicio Docker para quitarlo de los registros inseguros. ¿Desea continuar?',\n        downloadUrl: 'Servidor',\n        imageRepo: 'Repositorio de imágenes',\n        repoHelper: '¿Incluye un repositorio/organización/proyecto mirror?',\n        auth: 'Requiere autenticación',\n        mirrorHelper:\n            'Si hay varios mirrors, deben estar en líneas separadas. Ejemplo:\\nhttp://xxxxxx.m.daocloud.io\\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper:\n            'Si existen varios repositorios privados, deben estar en líneas separadas. Ejemplo:\\n172.16.10.111:8081\\n172.16.10.112:8081',\n        compose: 'Compose | Composes',\n        composeFile: 'Archivo de Orquestación',\n        fromChangeHelper: 'Cambiar la fuente limpiará el contenido actualmente editado. ¿Desea continuar?',\n        composePathHelper: 'Ruta de guardado del archivo de configuración: {0}',\n        composeHelper:\n            'La composición creada mediante el editor o plantilla de 1Panel se guardará en el directorio {0}/docker/compose.',\n        deleteFile: 'Eliminar archivo',\n        deleteComposeHelper:\n            'Elimina todos los archivos relacionados con la composición de contenedores, incluidos los archivos de configuración y los persistentes. ¡Proceda con precaución!',\n        deleteCompose: '\" Eliminar esta composición.',\n        composeDirectory: 'Directorio de compose',\n        template: 'Plantilla',\n        composeTemplate: 'Plantilla de compose | Plantillas de compose',\n        content: 'Contenido',\n        contentEmpty: 'El contenido de Compose no puede estar vacío, por favor introduzca algo y vuelva a intentarlo.',\n        containerNumber: 'Número de contenedores',\n        containerStatus: 'Estado del contenedor',\n        exited: 'Finalizado',\n        running: 'En ejecución',\n        composeDetailHelper:\n            'La composición fue creada externamente a 1Panel. Las operaciones de inicio y detención no son compatibles.',\n        composeOperatorHelper: 'La operación {1} se realizará en {0}. ¿Desea continuar?',\n        composeDownHelper:\n            'Esto detendrá y eliminará todos los contenedores y redes bajo la composición {0}. ¿Desea continuar?',\n        composeEnvHelper2:\n            'Esta orquestación fue creada por la Tienda de Aplicaciones 1Panel. Modifique las variables de entorno en las aplicaciones instaladas.',\n        composeContentPlaceholder: 'Escriba o pegue el contenido de su archivo docker-compose aquí',\n        setting: 'Configuración | Configuraciones',\n        goSetting: 'Ir a editar',\n        restartHelper: 'Los cambios de configuración requieren reiniciar el servicio Docker para surtir efecto',\n        operatorStatusHelper: 'Esto \"{0}\" el servicio Docker. ¿Desea continuar?',\n        dockerStatus: 'Servicio Docker',\n        daemonJsonPathHelper:\n            'Asegúrese de que la ruta de configuración sea la misma que la especificada en docker.service.',\n        mirrors: 'Mirrors del registro',\n        mirrorsHelper: '',\n        mirrorsHelper2: 'Para más detalles, consulte la documentación oficial.',\n        registries: 'Registros inseguros',\n        ipv6Helper:\n            'Al habilitar IPv6, necesita agregar una red de contenedores IPv6. Consulte la documentación oficial para pasos específicos de configuración.',\n        ipv6CidrHelper: 'Rango de pool de direcciones IPv6 para contenedores',\n        ipv6TablesHelper: 'Configuración automática de reglas iptables para Docker IPv6.',\n        experimentalHelper:\n            'Para habilitar ip6tables, debe activar esta configuración; de lo contrario, se ignorará ip6tables.',\n        cutLog: 'Opción de logs',\n        cutLogHelper1: 'La configuración actual solo afectará a los contenedores nuevos.',\n        cutLogHelper2: 'Los contenedores existentes deben ser recreados para que la configuración surta efecto.',\n        cutLogHelper3:\n            'Tenga en cuenta que recrear los contenedores puede provocar la pérdida de datos. Si contienen información importante, realice una copia de seguridad antes de reconstruir.',\n        maxSize: 'Tamaño máximo',\n        maxFile: 'Archivo máximo',\n        liveHelper:\n            'Por defecto, cuando el demonio Docker termina, detiene los contenedores en ejecución. Puede configurar el demonio para que los contenedores sigan en ejecución si el demonio no está disponible. Esta función se llama \"live restore\" y ayuda a reducir el tiempo fuera de servicio por caídas, mantenimientos o actualizaciones.',\n        liveWithSwarmHelper: 'La configuración live-restore es incompatible con el modo swarm.',\n        iptablesDisable: 'Desactivar iptables',\n        iptablesHelper1: 'Configuración automática de reglas iptables para Docker.',\n        iptablesHelper2: 'Deshabilitar iptables impedirá que los contenedores se comuniquen con redes externas.',\n        daemonJsonPath: 'Ruta de configuración',\n        serviceUnavailable: 'El servicio Docker no está iniciado actualmente.',\n        startIn: ' para iniciar',\n        sockPath: 'Socket Unix',\n        sockPathHelper: 'Canal de comunicación entre el demonio Docker y el cliente.',\n        sockPathHelper1: 'Ruta predeterminada: /var/run/docker-x.sock',\n        sockPathMsg:\n            'Guardar la configuración del Socket puede dejar el servicio Docker fuera de servicio. ¿Desea continuar?',\n        sockPathErr: 'Por favor seleccione o introduzca la ruta correcta del archivo Docker sock',\n        related: 'Relacionado',\n        includeAppstore: 'Mostrar contenedores de la tienda',\n        excludeAppstore: 'Ocultar contenedores de la tienda',\n        cleanDockerDiskZone: 'Limpiar el espacio en disco usado por Docker',\n        cleanImagesHelper: '( Limpia todas las imágenes que no están siendo usadas por ningún contenedor )',\n        cleanContainersHelper: '( Limpia todos los contenedores detenidos )',\n        cleanVolumesHelper: '( Limpia todos los volúmenes locales no utilizados )',\n        makeImage: 'Crear imagen',\n        newImageName: 'Nuevo nombre de imagen',\n        commitMessage: 'Mensaje de commit',\n        author: 'Autor',\n        ifPause: '¿Pausar el contenedor durante la creación?',\n        ifMakeImageWithContainer: '¿Crear nueva imagen a partir de este contenedor?',\n        finishTime: 'Hora de la última detención',\n        exportHelper: 'Exporta las imágenes seleccionadas en un solo archivo tar.',\n    },\n    cronjob: {\n        importHelper:\n            'Las tareas programadas duplicadas se omitirán automáticamente durante la importación. Las tareas se establecerán en estado [Deshabilitado] por defecto, y en estado [Pendiente de edición] cuando la asociación de datos sea anormal.',\n        changeStatus: 'Cambiar estado',\n        disableMsg: 'Esto detendrá la ejecución automática de la tarea programada. ¿Desea continuar?',\n        enableMsg: 'Esto permitirá la ejecución automática de la tarea programada. ¿Desea continuar?',\n        taskType: 'Tipo',\n        nextTime: 'Próximas 5 ejecuciones',\n        record: 'Registros',\n        viewRecords: 'Ver registros',\n        shell: 'Shell',\n        stop: 'Detención Manual',\n        stopHelper: 'Esta operación forzará la detención de la ejecución de la tarea actual. ¿Continuar?',\n        log: 'Registros de respaldo',\n        logHelper: 'Registro del sistema de copias de seguridad',\n        ogHelper1: '1. Registro del sistema de 1Panel',\n        logHelper2: '2. Registro de inicio de sesión SSH del servidor',\n        logHelper3: '3. Todos los registros del sitio web',\n        containerCheckBox: 'En contenedor (no necesita introducir el comando del contenedor)',\n        containerName: 'Nombre del contenedor',\n        ntp: 'Sincronización horaria',\n        ntp_helper: 'Puede configurar el servidor NTP en la página de Configuración Rápida de la Caja de Herramientas.',\n        app: 'Respaldo de aplicación',\n        website: 'Respaldo de sitio web',\n        rulesHelper:\n            'Si hay varias reglas de exclusión de compresión, deben estar en líneas separadas. Ejemplo:\\n*.log\\n*.sql',\n        lastRecordTime: 'Hora de la última ejecución',\n        all: 'Todos',\n        failedRecord: 'Registros fallidos',\n        successRecord: 'Registros completados correctamente',\n        database: 'Respaldo de base de datos',\n        backupArgs: 'Argumentos de Copia de Seguridad',\n        backupArgsHelper:\n            'Los argumentos de copia de seguridad no listados pueden ser ingresados y seleccionados manualmente. Por ejemplo: Ingrese --no-data y seleccione la primera opción de la lista desplegable.',\n        singleTransaction:\n            'Copia de seguridad de tablas InnoDB utilizando una única transacción, adecuada para copias de seguridad de datos de gran volumen',\n        quick: 'Lee datos fila por fila en lugar de cargar la tabla completa en memoria, adecuado para copias de seguridad de datos de gran volumen y máquinas con poca memoria',\n        skipLockTables:\n            'Copia de seguridad sin bloquear todas las tablas, adecuada para bases de datos altamente concurrentes',\n        missBackupAccount: 'No se pudo encontrar la cuenta de respaldo',\n        syncDate: 'Hora de sincronización',\n        clean: 'Limpiar caché',\n        curl: 'Acceder a URL',\n        taskName: 'Nombre',\n        cronSpec: 'Ciclo de ejecución',\n        cronSpecDoc:\n            'Custom execution cycles only support the [minute hour day month week] format, e.g., 0 0 * * *. For details, please refer to the official documentation.',\n        cronSpecHelper: 'Introduzca el periodo de ejecución correcto',\n        cleanHelper:\n            'Esta operación registra todos los registros de ejecución de tareas, archivos de respaldo y archivos de registro. ¿Desea continuar?',\n        backupContent: 'Contenido de respaldo',\n        directory: 'Directorio de respaldo',\n        sourceDir: 'Directorio de respaldo',\n        snapshot: 'Instantánea del sistema',\n        allOptionHelper:\n            'El plan actual es respaldar todos los [{0}]. La descarga directa no está soportada por ahora. Puede consultar la lista de respaldos en el menú [{0}].',\n        exclusionRules: 'Reglas de exclusión',\n        exclusionRulesHelper:\n            'Seleccione o ingrese reglas de exclusión, presione Enter después de cada conjunto para continuar. Las reglas de exclusión se aplicarán a todas las operaciones de compresión en esta copia de seguridad',\n        default_download_path: 'Enlace de descarga predeterminado',\n        saveLocal: 'Retener respaldos locales (igual al número de copias en la nube)',\n        url: 'Dirección URL',\n        urlHelper: 'Por favor ingrese una dirección URL válida',\n        targetHelper: 'Las cuentas de respaldo se gestionan en los ajustes del panel.',\n        withImageHelper:\n            'Respalda imágenes de la tienda de aplicaciones, pero esto aumentará el tamaño del archivo de la instantánea.',\n        ignoreApp: 'Excluir aplicaciones',\n        withImage: 'Copia de Seguridad de la Imagen de la Aplicación',\n        retainCopies: 'Retener registros',\n        retryTimes: 'Intentos de reintento',\n        timeout: 'Tiempo de espera',\n        ignoreErr: 'Ignorar errores',\n        ignoreErrHelper: 'Ignora los errores durante el respaldo para asegurar que todas las tareas se completen',\n        retryTimesHelper: '0 significa que no habrá reintentos tras un fallo',\n        retainCopiesHelper: 'Número de copias a retener para registros y logs de ejecución',\n        retainCopiesHelper1: 'Número de copias a retener para los archivos de respaldo',\n        retainCopiesUnit: ' copias (Ver)',\n        cronSpecRule:\n            'El formato del periodo de ejecución en la línea {0} es incorrecto. ¡Por favor verifique e intente nuevamente!',\n        cronSpecRule2: 'El formato del periodo de ejecución es incorrecto, por favor verifique e intente nuevamente.',\n        perMonthHelper: 'Ejecutar el día {0} de cada mes a las {1}:{2}',\n        perWeekHelper: 'Ejecutar cada semana el {0} a las {1}:{2}',\n        perDayHelper: 'Ejecutar todos los días a las {0}:{1}',\n        perHourHelper: 'Ejecutar cada hora en el minuto {0}',\n        perNDayHelper: 'Ejecutar cada {0} días a las {1}:{2}',\n        perNHourHelper: 'Ejecutar cada {0} horas en el minuto {1}',\n        perNMinuteHelper: 'Ejecutar cada {0} minutos',\n        perNSecondHelper: 'Ejecutar cada {0} segundos',\n        perMonth: 'Cada mes',\n        perWeek: 'Cada semana',\n        perHour: 'Cada hora',\n        perNDay: 'Cada N días',\n        perDay: 'Cada día',\n        perNHour: 'Cada N horas',\n        perNMinute: 'Cada N minutos',\n        perNSecond: 'Cada N segundos',\n        day: 'día(s)',\n        monday: 'Lunes',\n        tuesday: 'Martes',\n        wednesday: 'Miércoles',\n        thursday: 'Jueves',\n        friday: 'Viernes',\n        saturday: 'Sábado',\n        sunday: 'Domingo',\n        shellContent: 'Script',\n        executor: 'Ejecutor',\n        errRecord: 'Registro incorrecto',\n        errHandle: 'Fallo en la ejecución de la tarea programada',\n        noRecord: 'Al activar la tarea programada, verá aquí los registros.',\n        cleanData: 'Limpiar datos',\n        cleanRemoteData: 'Eliminar datos remotos',\n        cleanDataHelper: 'Elimina el archivo de respaldo generado por esta tarea.',\n        noLogs: 'Aún no hay salida de tareas...',\n        errPath: '¡Error en la ruta de respaldo [{0}], no se puede descargar!',\n        cutWebsiteLog: 'Rotación de logs del sitio',\n        cutWebsiteLogHelper: 'Los archivos de log rotados se respaldarán en el directorio de respaldos de 1Panel.',\n        syncIpGroup: 'Sincronizar grupos de IP de WAF',\n        requestExpirationTime: 'Tiempo de expiración de solicitud de subida (Horas)',\n        unitHours: 'Unidad: Horas',\n        alertTitle: 'Tarea programada - {0} 「{1}」 Alerta de fallo',\n        library: {\n            script: 'Script',\n            syncNow: 'Sincronizar Ahora',\n            turnOnSync: 'Activar Sincronización Automática',\n            turnOnSyncHelper:\n                'Activar la sincronización automática realizará sincronizaciones automáticas durante las primeras horas de la madrugada diariamente',\n            turnOffSync: 'Desactivar Sincronización Automática',\n            turnOffSyncHelper:\n                'Desactivar la sincronización automática puede causar retrasos en la sincronización de scripts, ¿confirmar?',\n            isInteractive: 'Interactivo',\n            interactive: 'Script interactivo',\n            interactiveHelper:\n                'Requiere introducir datos durante la ejecución y no puede ser usada en tareas programadas.',\n            remoteLibrary: 'Biblioteca de Scripts Remota',\n            library: 'Librería de scripts',\n            create: 'Agregar script',\n            edit: 'Editar script',\n            groupHelper:\n                'Establezca diferentes grupos según las características de los scripts, esto facilita el filtrado.',\n            handleHelper: 'Ejecutar el script {1} en {0}, ¿desea continuar?',\n            noSuchApp: 'No se detectó el servicio {0}. ¡Por favor instálelo desde la librería de scripts!',\n            syncHelper:\n                'Se va a sincronizar la librería de scripts del sistema. Solo afecta a los scripts del sistema. ¿Desea continuar?',\n        },\n        logHelper1: '1. Registro del sistema de 1Panel',\n        cleanLog: 'Limpieza automática de logs',\n        cleanLogscope: 'Retención de logs',\n    },\n    monitor: {\n        globalFilter: 'Filtro Global',\n        enableMonitor: 'Estado de Monitoreo',\n        storeDays: 'Días de Retención',\n        defaultNetwork: 'Tarjeta de Red Predeterminada',\n        defaultNetworkHelper:\n            'Opción de tarjeta de red predeterminada mostrada en las interfaces de monitoreo y resumen',\n        defaultIO: 'Disco Predeterminado',\n        defaultIOHelper: 'Opción de disco predeterminada mostrada en las interfaces de monitoreo y resumen',\n        cleanMonitor: 'Limpiar Registros de Monitoreo',\n        cleanHelper: 'Esta operación borrará todos los registros de monitoreo, incluidos los de GPU. ¿Continuar?',\n        avgLoad: 'Carga promedio',\n        loadDetail: 'Detalle de carga',\n        resourceUsage: 'Utilización',\n        networkCard: 'Interfaz de red',\n        read: 'Lectura',\n        write: 'Escritura',\n        readWriteCount: 'Operaciones de E/S',\n        readWriteTime: 'Latencia de E/S',\n        today: 'Hoy',\n        yesterday: 'Ayer',\n        lastNDay: 'Últimos {0} días',\n        lastNMonth: 'Últimos {0} meses',\n        lastHalfYear: 'Último semestre',\n        memory: 'Memoria',\n        percent: 'Porcentaje',\n        cache: 'Caché',\n        disk: 'Disco',\n        network: 'Red',\n        up: 'Subida',\n        down: 'Bajada',\n        interval: 'Intervalo de Recolección',\n        intervalHelper: 'Ingrese un intervalo de recolección de monitoreo apropiado (10 segundos - 12 horas)',\n    },\n    terminal: {\n        local: 'Local',\n        defaultConn: 'Conexión predeterminada',\n        defaultConnHelper:\n            'Esta operación conectará automáticamente al terminal del nodo después de abrir el terminal para 【{0}】. ¿Continuar?',\n        withReset: 'Restablecer Información de Conexión',\n        localConnJump:\n            'La información de conexión predeterminada se mantiene en [Terminal - Configuración]. Si la conexión falla, ¡edite allí!',\n        localHelper: 'El nombre `local` se utiliza solo para identificación interna del sistema',\n        connLocalErr:\n            'No se puede autenticar automáticamente, por favor introduzca la información de inicio de sesión del servidor local.',\n        testConn: 'Probar conexión',\n        saveAndConn: 'Guardar y conectar',\n        connTestOk: 'La información de conexión es válida',\n        connTestFailed: 'Conexión no disponible, por favor revise la información de conexión.',\n        host: 'Host | Hosts',\n        createConn: 'Nueva conexión',\n        noHost: 'Sin host',\n        groupChange: 'Cambiar grupo',\n        expand: 'Expandir todo',\n        fold: 'Contraer todo',\n        batchInput: 'Procesamiento por lotes',\n        quickCommand: 'Comando rápido | Comandos rápidos',\n        noSuchCommand:\n            'No se encontraron datos de comandos rápidos en el archivo CSV importado, ¡compruebe e inténtelo de nuevo!',\n        quickCommandHelper: 'Puede usar comandos rápidos en la parte inferior de \"Terminales -> Terminales\".',\n        groupDeleteHelper:\n            'Después de eliminar el grupo, todas las conexiones pasarán al grupo predeterminado. ¿Desea continuar?',\n        command: 'Comando',\n        quickCmd: 'Comando rápido',\n        addHost: 'Agregar',\n        localhost: 'Localhost',\n        ip: 'Dirección',\n        authMode: 'Autenticación',\n        passwordMode: 'Contraseña',\n        rememberPassword: 'Recordar autenticación',\n        keyMode: 'Clave privada',\n        key: 'Clave privada',\n        keyPassword: 'Contraseña de la clave privada',\n        emptyTerminal: 'No hay ninguna terminal conectada actualmente.',\n        lineHeight: 'Altura de línea',\n        letterSpacing: 'Espaciado de letras',\n        fontSize: 'Tamaño de fuente',\n        fontFamily: 'Conjunto de fuentes',\n        fontFamilySupportHelper:\n            'Seleccione o introduzca fuentes. El efecto depende de si el sistema del navegador tiene instalada la fuente; si no, se usará una de respaldo. Si queda vacío, se usará la fuente predeterminada.',\n        backgroundColor: 'Color de fondo',\n        foregroundColor: 'Color de primer plano',\n        cursorBlink: 'Parpadeo del cursor',\n        cursorStyle: 'Estilo de cursor',\n        cursorUnderline: 'Subrayado',\n        cursorBlock: 'Bloque',\n        cursorBar: 'Barra',\n        scrollback: 'Scrollback',\n        scrollSensitivity: 'Sensibilidad de scroll',\n        aiStatus: 'AI Terminal',\n        aiSettings: 'AI Terminal Settings',\n        aiAccountHelper:\n            'Use la cuenta de modelo seleccionada para generar y rellenar comandos. Para modelos locales como Ollama y vLLM, use una cuenta de modelo personalizada.',\n        aiPrefix: 'Trigger Prefix',\n        aiPrefixHelper:\n            'When a line starts with this prefix and you press Enter, AI command generation will be triggered, for example # or //ai.',\n        aiRiskCommands: 'Risk Command Interception',\n        aiRiskCommandsHelper:\n            'Generated commands matching any of these fragments will be blocked and filled back as comments. Supports add, edit, and delete.',\n        aiAddRiskCommand: 'Add Risk Command',\n        aiRemoveRiskCommand: 'Delete',\n        aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',\n        aiPrefixAsciiVisible:\n            'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',\n        saveHelper: '¿Está seguro de que desea guardar la configuración actual de la terminal?',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: 'Para dudas de instalación y uso, consulte',\n        },\n        swap: {\n            swap: 'Partición Swap',\n            swapHelper1: 'El tamaño de swap debe ser de 1 a 2 veces la memoria física, ajustable según necesidades.',\n            swapHelper2:\n                'Antes de crear un archivo swap, asegúrese de que el disco tenga espacio suficiente, ya que el archivo ocupará ese espacio.',\n            swapHelper3:\n                'El swap ayuda a aliviar la presión de memoria, pero es solo un suplemento. Un uso excesivo puede afectar el rendimiento. Se recomienda aumentar memoria o optimizar el uso.',\n            swapHelper4: 'Se recomienda monitorear regularmente el uso de swap para asegurar el funcionamiento normal.',\n            swapDeleteHelper:\n                'Esta operación eliminará la partición Swap {0}. Por seguridad, el archivo no se borrará automáticamente. Si requiere borrarlo, hágalo manualmente.',\n            saveHelper: '¡Por favor guarde primero la configuración!',\n            saveSwap: 'Guardar la configuración ajustará la partición Swap {0} a {1}. ¿Desea continuar?',\n            swapMin: 'El tamaño mínimo es de 40 KB. ¡Por favor modifique y vuelva a intentar!',\n            swapMax: 'El tamaño máximo es {0}. ¡Por favor modifique y vuelva a intentar!',\n            swapOff: 'El tamaño mínimo es de 40 KB. Si lo pone en 0, desactivará la partición Swap.',\n        },\n        device: {\n            dnsHelper: 'Servidor DNS',\n            dnsAlert: '¡Atención! Modificar el archivo /etc/resolv.conf lo restaurará tras reiniciar el sistema.',\n            dnsHelper1: 'Si hay varios DNS, deben ir en líneas nuevas. Ejemplo:\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: 'Resolución de host',\n            hosts: 'Dominio',\n            hostAlert: 'Registros comentados ocultos, haga clic en Configuración completa para verlos o modificarlos.',\n            toolbox: 'Configuraciones rápidas',\n            hostname: 'Nombre del host',\n            passwd: 'Contraseña del sistema',\n            passwdHelper: 'Los caracteres no pueden incluir $ y &',\n            timeZone: 'Zona horaria del sistema',\n            localTime: 'Hora del servidor',\n            timeZoneChangeHelper: 'Cambiar la zona horaria requiere reiniciar el servicio. ¿Continuar?',\n            timeZoneHelper:\n                'Si no tiene instalado \"timedatectl\", no podrá cambiar la zona horaria porque el sistema lo usa.',\n            timeZoneCN: 'Beijing',\n            timeZoneAM: 'Los Ángeles',\n            timeZoneNY: 'Nueva York',\n            ntpALi: 'Alibaba',\n            ntpGoogle: 'Google',\n            syncSite: 'Servidor NTP',\n            hostnameHelper: 'El cambio de nombre depende de \"hostnamectl\". Si no está instalado, podría fallar.',\n            userHelper: 'El nombre de usuario depende de \"whoami\". Si no está instalado, podría fallar.',\n            passwordHelper: 'El cambio de contraseña depende de \"chpasswd\". Si no está instalado, podría fallar.',\n            hostHelper: 'Hay un valor vacío en el contenido. Por favor, revise y modifique.',\n            dnsCheck: 'Probar disponibilidad',\n            dnsOK: '¡La configuración DNS es válida!',\n            dnsTestFailed: 'La configuración DNS no es válida.',\n            syncSiteHelper: '¿Sincronizar la hora del sistema con {0}?',\n        },\n        fail2ban: {\n            sshPort: 'Puerto SSH',\n            sshPortHelper: 'Fail2ban escucha el puerto SSH del host',\n            unActive: 'El servicio Fail2ban no está habilitado actualmente.',\n            operation: 'Va a realizar la operación \"{0}\" en Fail2ban. ¿Desea continuar?',\n            fail2banChange: 'Modificación de configuración de Fail2ban',\n            ignoreHelper: 'La lista de IPs permitidas será ignorada para el bloqueo. ¿Desea continuar?',\n            bannedHelper: 'La lista de IPs bloqueadas será bloqueada por el servidor. ¿Desea continuar?',\n            maxRetry: 'Intentos máximos',\n            banTime: 'Tiempo de bloqueo',\n            banTimeHelper: 'El tiempo por defecto es 10 minutos, -1 es bloqueo permanente',\n            banTimeRule: 'Por favor, introduzca un tiempo válido o -1',\n            banAllTime: 'Bloqueo permanente',\n            findTime: 'Periodo de detección',\n            banAction: 'Acción de bloqueo',\n            banActionOption: 'Bloquear las IP especificadas usando {0}',\n            allPorts: ' (Todos los puertos)',\n            ignoreIP: 'Lista blanca de IP',\n            bannedIP: 'Lista negra de IP',\n            logPath: 'Ruta del log',\n            logPathHelper: 'Por defecto /var/log/secure o /var/log/auth.log',\n        },\n        ftp: {\n            ftp: 'Cuenta FTP | Cuentas FTP',\n            notStart: 'El servicio FTP no está iniciado, ¡inícielo primero!',\n            operation: 'Se realizará la operación \"{0}\" en el servicio FTP. ¿Desea continuar?',\n            noPasswdMsg: 'No se pudo obtener la contraseña actual de la cuenta FTP, ¡asigne una y vuelva a intentar!',\n            enableHelper: 'Habilitar la cuenta FTP restaurará los permisos de acceso. ¿Desea continuar?',\n            disableHelper: 'Deshabilitar la cuenta FTP revocará los permisos de acceso. ¿Desea continuar?',\n            syncHelper: 'Sincronizar datos de cuentas FTP entre servidor y base de datos. ¿Desea continuar?',\n            dirSystem:\n                'Este directorio es reservado por el sistema. Modificarlo podría causar errores. ¡Modifíquelo con cuidado!',\n            dirHelper: 'Habilitar FTP requiere cambios en los permisos de directorio - elija cuidadosamente',\n            dirMsg: 'Habilitar FTP modificará los permisos de todo el directorio {0}. ¿Desea continuar?',\n        },\n        clam: {\n            clam: 'Escaneo de virus',\n            cron: 'Escaneo programado',\n            cronHelper: 'La versión profesional soporta el escaneo programado',\n            specErr: 'Formato de horario incorrecto, ¡verifique e intente nuevamente!',\n            disableMsg:\n                'Detener la ejecución programada impedirá que esta tarea se ejecute automáticamente. ¿Desea continuar?',\n            enableMsg:\n                'Habilitar la ejecución programada permitirá que esta tarea se ejecute automáticamente. ¿Desea continuar?',\n            showFresh: 'Mostrar servicio de actualización de firmas',\n            hideFresh: 'Ocultar servicio de actualización de firmas',\n            clamHelper:\n                'La configuración recomendada para ClamAV es: al menos 3 GiB de RAM, CPU de un núcleo a 2.0 GHz o más, y al menos 5 GiB de disco disponible.',\n            notStart: 'El servicio ClamAV no está iniciado, ¡inícielo primero!',\n            removeRecord: 'Eliminar archivos de informe',\n            noRecords: 'Haga clic en \"Ejecutar\" para iniciar el escaneo y aquí verá los registros.',\n            removeInfected: 'Eliminar archivos infectados',\n            removeInfectedHelper:\n                'Elimina los archivos infectados detectados durante la tarea para asegurar el servidor.',\n            clamCreate: 'Crear regla de escaneo',\n            infectedStrategy: 'Estrategia para infectados',\n            removeHelper: 'Eliminar archivos infectados, ¡elija cuidadosamente!',\n            move: 'Mover',\n            moveHelper: 'Mover archivos infectados a un directorio específico',\n            copyHelper: 'Copiar archivos infectados a un directorio específico',\n            none: 'No hacer nada',\n            noneHelper: 'No realizar acciones sobre los archivos infectados',\n            scanDir: 'Directorio a escanear',\n            infectedDir: 'Directorio de infectados',\n            scanDate: 'Fecha de escaneo',\n            scanResult: 'Cola de logs de escaneo',\n            tail: 'Líneas',\n            infectedFiles: 'Archivos infectados',\n            log: 'Detalles',\n            clamConf: 'Clam AV daemon',\n            clamLog: '@:toolbox.clam.clamConf logs',\n            freshClam: 'FreshClam',\n            freshClamLog: '@:toolbox.clam.freshClam logs',\n            alertHelper: 'La versión profesional soporta escaneo programado y alertas por SMS',\n            alertTitle: 'La tarea de escaneo de virus 「{0}」 detectó archivos infectados',\n        },\n    },\n    logs: {\n        core: 'Servicio del panel',\n        agent: 'Monitoreo de nodo',\n        panelLog: 'Logs del panel',\n        operation: 'Logs de operación',\n        login: 'Logs de acceso',\n        loginIP: 'IP de acceso',\n        loginAddress: 'Dirección de acceso',\n        loginAgent: 'Agente de acceso',\n        loginStatus: 'Estado',\n        system: 'Logs del sistema',\n        deleteLogs: 'Limpiar logs',\n        resource: 'Recurso',\n        detail: {\n            dashboard: 'Resumen general',\n            ai: 'IA',\n            groups: 'Grupo',\n            hosts: 'Host',\n            apps: 'Aplicación',\n            openresty: 'OpenResty',\n            websites: 'Sitio web',\n            containers: 'Contenedor',\n            files: 'Archivo',\n            runtimes: 'Runtime',\n            process: 'Proceso',\n            toolbox: 'Caja de herramientas',\n            backups: 'Respaldo / Restaurar',\n            tampers: 'Antimanipulación',\n            xsetting: 'Configuración de interfaz',\n            logs: 'Log',\n            settings: 'Configuración',\n            cronjobs: 'Tarea programada',\n            databases: 'Base de datos',\n            waf: 'WAF',\n            licenses: 'Licencia',\n            nodes: 'Nodo',\n            commands: 'Comandos rápidos',\n        },\n        websiteLog: 'Logs de sitio web',\n        runLog: 'Logs de ejecución',\n        errLog: 'Logs de errores',\n        task: 'Log de tareas',\n        taskName: 'Nombre de la tarea',\n        taskRunning: 'En ejecución',\n    },\n    file: {\n        fileDirNum: '{0} directorios, {1} archivos,',\n        currentDir: 'Directorio',\n        dir: 'Carpeta',\n        fileName: 'Nombre del archivo',\n        search: 'Buscar',\n        mode: 'Permisos',\n        editPermissions: 'Editar @.lower:file.mode',\n        owner: 'Propietario',\n        file: 'Archivo',\n        remoteFile: 'Descargar de remoto',\n        share: 'Compartir',\n        sync: 'Sincronización de datos',\n        size: 'Tamaño',\n        updateTime: 'Modificado',\n        rename: 'Renombrar',\n        role: 'Permisos',\n        info: 'Ver atributos',\n        linkFile: 'Enlace simbólico',\n        shareList: 'Lista de compartidos',\n        zip: 'Comprimido',\n        group: 'Grupo',\n        path: 'Ruta',\n        public: 'Otros',\n        setRole: 'Asignar permisos',\n        link: 'Enlace de archivo',\n        rRole: 'Lectura',\n        wRole: 'Escritura',\n        xRole: 'Ejecutable',\n        name: 'Nombre',\n        compress: 'Comprimir',\n        deCompress: 'Descomprimir',\n        compressType: 'Formato de compresión',\n        compressDst: 'Ruta de compresión',\n        replace: 'Sobrescribir archivos existentes',\n        compressSuccess: 'Compresión completada correctamente',\n        deCompressSuccess: 'Descompresión completada correctamente',\n        deCompressDst: 'Ruta de descompresión',\n        linkType: 'Tipo de enlace',\n        softLink: 'Enlace simbólico',\n        hardLink: 'Enlace físico',\n        linkPath: 'Ruta del enlace',\n        selectFile: 'Seleccionar archivo',\n        downloadUrl: 'URL remota',\n        downloadStart: 'Descarga iniciada',\n        moveSuccess: 'Movido correctamente',\n        copySuccess: 'Copiado correctamente',\n        pasteMsg: 'Por favor haz clic en el botón [Pegar] en la parte superior derecha del directorio de destino',\n        move: 'Mover',\n        calculate: 'Calcular',\n        remark: 'Observación',\n        setRemark: 'Establecer observación',\n        remarkPrompt: 'Ingrese una observación',\n        remarkPlaceholder: 'Observación',\n        remarkToggle: 'Notas',\n        remarkToggleTip: 'Cargar notas del archivo',\n        canNotDeCompress: 'No se puede descomprimir este archivo',\n        uploadSuccess: 'Carga completada correctamente',\n        downloadProcess: 'Progreso de descarga',\n        downloading: 'Descargando...',\n        infoDetail: 'Propiedades del archivo',\n        root: 'Directorio raíz',\n        list: 'Lista de archivos',\n        sub: 'Incluir subdirectorios',\n        downloadSuccess: 'Descarga completada correctamente',\n        theme: 'Tema',\n        language: 'Idioma',\n        eol: 'Fin de línea',\n        copyDir: 'Copiar',\n        paste: 'Pegar',\n        changeOwner: 'Modificar usuario y grupo',\n        containSub: 'Aplicar cambio de permisos recursivamente',\n        ownerHelper:\n            'El usuario predeterminado del entorno PHP: el grupo es 1000:1000, es normal que haya diferencias entre usuario dentro y fuera del contenedor',\n        searchHelper: 'Soporta comodines como *',\n        uploadFailed: '[{0}] Fallo en la carga del archivo',\n        fileUploadStart: 'Cargando [{0}]...',\n        currentSelect: 'Selección actual: ',\n        unsupportedType: 'Tipo de archivo no soportado',\n        deleteHelper: '¿Está seguro de eliminar los siguientes archivos? Por defecto irán a la papelera de reciclaje.',\n        fileHelper:\n            'Nota:\\n1. Los resultados de búsqueda no pueden ordenarse.\\n2. Las carpetas no pueden ordenarse por tamaño.',\n        forceDeleteHelper: 'Eliminar permanentemente el archivo (sin pasar por la papelera, lo elimina directamente)',\n        recycleBin: 'Papelera',\n        sourcePath: 'Ruta original',\n        deleteTime: 'Hora de eliminación',\n        confirmReduce: '¿Desea restaurar los siguientes archivos?',\n        reduceSuccess: 'Restauración completada correctamente',\n        reduce: 'Restaurar',\n        reduceHelper:\n            'Si hay un archivo o directorio con el mismo nombre en la ruta original, será sobrescrito. ¿Desea continuar?',\n        clearRecycleBin: 'Vaciar',\n        clearRecycleBinHelper: '¿Desea vaciar la papelera?',\n        favorite: 'Favoritos',\n        removeFavorite: '¿Eliminar de favoritos?',\n        addFavorite: 'Agregar/Quitar de favoritos',\n        clearList: 'Limpiar lista',\n        deleteRecycleHelper: '¿Está seguro de eliminar permanentemente los siguientes archivos?',\n        typeErrOrEmpty: 'El archivo [{0}] es de tipo incorrecto o carpeta vacía',\n        dropHelper: 'Arrastre aquí los archivos que desea cargar',\n        fileRecycleBin: 'Habilitar papelera de reciclaje',\n        fileRecycleBinMsg: '{0} papelera',\n        wordWrap: 'Ajuste de línea',\n        deleteHelper2: '¿Está seguro de eliminar el archivo seleccionado? No se puede deshacer.',\n        ignoreCertificate: 'Permitir conexiones inseguras',\n        ignoreCertificateHelper:\n            'Permitir conexiones inseguras puede provocar fugas o alteraciones de datos. Úselo solo si confía en la fuente.',\n        uploadOverLimit: '¡El número de archivos supera los 1000! Por favor, comprima y cargue',\n        clashDidNotSupport: 'Los nombres de archivo no pueden contener .1panel_clash',\n        clashDeleteAlert: 'No se puede eliminar la carpeta \"Papelera\"',\n        clashOpenAlert: 'Haga clic en \"Papelera\" para abrir la carpeta de papelera',\n        right: 'Adelante',\n        back: 'Atrás',\n        top: 'Volver arriba',\n        up: 'Subir',\n        openWithVscode: 'Abrir con VS Code',\n        vscodeHelper: 'Asegúrese de tener instalado VS Code y configurado el plugin SSH Remote',\n        saveContentAndClose: 'El archivo se ha modificado, ¿desea guardar y cerrar?',\n        saveAndOpenNewFile: 'El archivo se ha modificado, ¿desea guardar y abrir el nuevo archivo?',\n        noEdit: 'El archivo no ha sido modificado, ¡no es necesario hacer esto!',\n        noNameFolder: 'Carpeta sin nombre',\n        noNameFile: 'Archivo sin nombre',\n        minimap: 'Mapa de código',\n        fileCanNotRead: 'No se puede leer el archivo',\n        previewTruncated: 'El archivo es demasiado grande, solo se muestra la última parte',\n        previewEmpty: 'El archivo está vacío o no es un archivo de texto',\n        previewLargeFile: 'Vista previa',\n        panelInstallDir: 'El directorio de instalación de 1Panel no puede eliminarse',\n        wgetTask: 'Tarea de descarga',\n        existFileTitle: 'Archivo con el mismo nombre',\n        existFileHelper: 'El archivo cargado contiene un archivo con el mismo nombre, ¿desea sobrescribirlo?',\n        existFileSize: 'Tamaño del archivo (nuevo -> viejo)',\n        existFileDirHelper: 'El archivo o carpeta seleccionado tiene nombre duplicado. ¡Proceda con precaución!\\n',\n        coverDirHelper: '¡Las carpetas seleccionadas para reemplazo se copiarán en la ruta de destino!',\n        noSuchFile: 'No se encontró el archivo o directorio. Por favor verifique e intente de nuevo.',\n        setting: 'Configuración',\n        showHide: 'Mostrar archivos ocultos',\n        noShowHide: 'No mostrar archivos ocultos',\n        cancelUpload: 'Cancelar carga',\n        cancelUploadHelper: 'Indica si se cancela la carga; después de la cancelación, la lista de cargas se borrará.',\n        keepOneTab: 'Mantener al menos una pestaña',\n        notCanTab: 'No se pueden añadir más pestañas',\n        convert: 'Convertir Formato',\n        converting: 'Convirtiendo',\n        fileCanNotConvert: 'Este archivo no admite conversión de formato',\n        formatType: 'Tipo de Formato',\n        sourceFormat: 'Formato de Origen',\n        sourceFile: 'Archivo de Origen',\n        saveDir: 'Directorio de Guardado',\n        deleteSourceFile: 'Eliminar Archivo de Origen',\n        convertHelper: 'Convertir los archivos seleccionados a otro formato',\n        convertHelper1: 'Por favor, seleccione los archivos a convertir',\n        execConvert: 'Iniciar conversión. Puede ver los registros de conversión en el Centro de Tareas',\n        convertLogs: 'Registros de Conversión',\n        formatConvert: 'Conversión de Formato',\n    },\n    ssh: {\n        autoStart: 'Inicio automático',\n        enable: 'Habilitar inicio automático',\n        disable: 'Deshabilitar inicio automático',\n        sshAlert:\n            'La lista se ordena por fecha de inicio de sesión. Cambiar la zona horaria u otras operaciones pueden afectar las fechas.',\n        sshAlert2:\n            'Puede usar \"Fail2ban\" en \"Caja de Herramientas\" para bloquear IPs que intentan ataques de fuerza bruta y así mejorar la seguridad.',\n        sshOperate: 'Se realizará la operación \"{0}\" en el servicio SSH. ¿Desea continuar?',\n        sshChange: 'Configuración SSH',\n        sshChangeHelper: 'Esta acción cambió \"{0}\" a \"{1}\". ¿Desea continuar?',\n        sshFileChangeHelper:\n            'Modificar el archivo de configuración puede afectar la disponibilidad del servicio. Ejecute esta acción con precaución. ¿Desea continuar?',\n        port: 'Puerto',\n        portHelper: 'Especifique el puerto en el que escucha el servicio SSH.',\n        listenAddress: 'Dirección de escucha',\n        allV4V6: '0.0.0.0:{0}(IPv4) y :::{0}(IPv6)',\n        listenHelper: 'Si deja ambos campos en blanco, escuchará en \"0.0.0.0:{0}(IPv4)\" y \":::{0}(IPv6)\".',\n        addressHelper: 'Especifique la dirección en la que escucha el servicio SSH.',\n        permitRootLogin: 'Permitir inicio de sesión root',\n        rootSettingHelper: 'El método predeterminado para root es \"Permitir inicio de sesión SSH\".',\n        rootHelper1: 'Permitir inicio SSH',\n        rootHelper2: 'Deshabilitar inicio SSH',\n        rootHelper3: 'Solo clave permitida',\n        rootHelper4: 'Solo se pueden ejecutar comandos predefinidos. No se permite ninguna otra operación.',\n        passwordAuthentication: 'Autenticación por contraseña',\n        pwdAuthHelper: 'Si habilitar o no la autenticación por contraseña. Por defecto está activada.',\n        pubkeyAuthentication: 'Autenticación por clave',\n        privateKey: 'Clave privada',\n        publicKey: 'Clave pública',\n        password: 'Contraseña',\n        createMode: 'Método de creación',\n        generate: 'Autogenerar',\n        unSyncPass: 'La contraseña de la clave no se puede sincronizar',\n        syncHelper:\n            'La operación de sincronización limpiará las claves inválidas y sincronizará nuevos pares de claves completos. ¿Desea continuar?',\n        input: 'Entrada manual',\n        import: 'Subir archivo',\n        authKeys: 'Claves de Autorización',\n        authKeysHelper: '¿Guardar información actual de clave pública?',\n        pubkey: 'Información de clave',\n        pubKeyHelper: 'La información de la clave actual solo tiene efecto para el usuario {0}',\n        encryptionMode: 'Modo de cifrado',\n        passwordHelper: 'Puede contener entre 6 y 10 dígitos y letras en inglés',\n        reGenerate: 'Regenerar clave',\n        keyAuthHelper: 'Indica si se habilita la autenticación por clave.',\n        useDNS: 'useDNS',\n        dnsHelper:\n            'Controla si la función de resolución DNS está habilitada en el servidor SSH para verificar la identidad de la conexión.',\n        analysis: 'Información estadística',\n        denyHelper:\n            \"Se realizará una operación de 'denegar' sobre las siguientes direcciones. Tras la configuración, la IP quedará prohibida de acceder al servidor. ¿Desea continuar?\",\n        acceptHelper:\n            \"Se realizará una operación de 'aceptar' sobre las siguientes direcciones. Tras la configuración, la IP recuperará el acceso normal. ¿Desea continuar?\",\n        noAddrWarning:\n            'Actualmente no se han seleccionado direcciones [{0}]. ¡Por favor verifique e inténtelo de nuevo!',\n        loginLogs: 'Registros de SSH',\n        loginMode: 'Modo',\n        authenticating: 'Clave',\n        publickey: 'Clave',\n        belong: 'Pertenece',\n        local: 'Local',\n        session: 'Sesión | Sesiones',\n        loginTime: 'Hora de inicio de sesión',\n        loginIP: 'IP de inicio de sesión',\n        stopSSHWarn: 'Indica si se debe desconectar esta conexión SSH',\n        remote: 'SSH remoto',\n    },\n    setting: {\n        panel: 'Panel',\n        user: 'Usuario del panel',\n        userChange: 'Cambiar usuario del panel',\n        userChangeHelper: 'Cambiar el usuario del panel cerrará tu sesión. ¿Deseas continuar?',\n        passwd: 'Contraseña del panel',\n        emailHelper: 'Para recuperación de contraseña',\n        watermark: 'Configuración de marca de agua',\n        watermarkContent: 'Contenido de la marca de agua',\n        contentHelper:\n            '{0} representa el nombre del nodo, {1} representa la dirección del nodo. Puede usar variables o ingresar nombres personalizados.',\n        watermarkColor: 'Color de la marca de agua',\n        watermarkFont: 'Tamaño de fuente de la marca de agua',\n        watermarkHeight: 'Altura de la marca de agua',\n        watermarkWidth: 'Anchura de la marca de agua',\n        watermarkRotate: 'Ángulo de rotación',\n        watermarkGap: 'Espaciado',\n        watermarkCloseHelper: '¿Seguro que deseas desactivar la configuración de marca de agua del sistema?',\n        watermarkOpenHelper: '¿Seguro que deseas guardar la configuración actual de marca de agua del sistema?',\n        title: 'Alias del panel',\n        panelPort: 'Puerto del panel',\n        titleHelper:\n            'Admite de 3 a 30 caracteres, incluyendo letras, números, espacios y caracteres especiales comunes',\n        portHelper:\n            'El rango recomendado de puertos es de 8888 a 65535. Nota: si el servidor tiene un grupo de seguridad, permite el nuevo puerto en el grupo de seguridad previamente',\n        portChange: 'Cambio de puerto',\n        portChangeHelper: 'Modificará el puerto del servicio y reiniciará el servicio. ¿Deseas continuar?',\n        theme: 'Tema',\n        menuTabs: 'Pestañas de menú',\n        dark: 'Oscuro',\n        darkGold: 'Dorado oscuro',\n        light: 'Claro',\n        auto: 'Seguir sistema',\n        language: 'Idioma',\n        runtimeEnv: 'Entorno de ejecución',\n        docSource: 'Fuente de documentación',\n        withByRegion: 'Seguir región de operación (Predeterminado)',\n        withByLang: 'Seguir idioma del sistema',\n        region: 'Región de operación',\n        cn: 'China continental',\n        intl: 'Global',\n        regionHelper: 'Al cambiar la región, se ajustarán las siguientes fuentes de recursos:',\n        regionHelper1: 'Paquetes de instalación del sistema',\n        regionHelper2: 'Tienda de aplicaciones y biblioteca de scripts',\n        regionHelper3: 'Manual de usuario y documentación relacionada',\n        regionHelper4: 'Esto puede afectar futuras descargas y accesos. Proceda con precaución.',\n        regionTip:\n            'La región de operación afecta la fuente de actualización del sistema y la dirección de descarga de paquetes de instalación.',\n        docSourceTip:\n            'La fuente de documentación determina el idioma de los enlaces de documentación y registro de actualizaciones.',\n        languageHelper:\n            'Por defecto sigue el idioma del navegador. Este parámetro solo tiene efecto en el navegador actual',\n        sessionTimeout: 'Tiempo de espera de sesión',\n        sessionTimeoutError: 'El tiempo mínimo de espera de sesión es de 300 segundos',\n        sessionTimeoutHelper:\n            'El panel cerrará la sesión automáticamente si no hay actividad durante más de {0} segundo(s).',\n        systemIP: 'Dirección del sistema',\n        systemIPHelper:\n            'La dirección se utilizará para la redirección de aplicaciones, el acceso a contenedores y otras funciones. Cada nodo puede configurarse con una dirección diferente.',\n        proxy: 'Proxy del servidor',\n        proxyHelper: 'Tendrá efecto en los siguientes escenarios después de configurar el servidor proxy:',\n        proxyHelper1:\n            'Descarga de paquetes de instalación y sincronización desde la tienda de aplicaciones (solo edición profesional)',\n        proxyHelper2:\n            'Actualización del sistema y recuperación de información de actualización (solo edición profesional)',\n        proxyHelper3: 'Verificación y sincronización de licencias del sistema',\n        proxyHelper4:\n            'Las solicitudes de red, como la descarga de imágenes de Docker, se realizarán a través del servidor proxy (solo edición profesional)',\n        proxyHelper5: 'Descarga y sincronización unificada para bibliotecas de scripts del sistema (Profesional)',\n        proxyHelper6: 'Solicitud de certificado (Profesional)',\n        proxyType: 'Tipo de proxy',\n        proxyUrl: 'Dirección del proxy',\n        proxyPort: 'Puerto del proxy',\n        proxyPasswdKeep: 'Recordar contraseña',\n        proxyDocker: 'Proxy de Docker',\n        proxyDockerHelper:\n            'Sincroniza la configuración del servidor proxy con Docker, admite la extracción de imágenes en servidores offline y otras operaciones',\n        syncToNode: 'Sincronizar con nodo',\n        syncToNodeHelper: 'Sincroniza la configuración con otros nodos',\n        nodes: 'Nodo',\n        selectNode: 'Seleccionar nodo',\n        selectNodeError: 'Por favor selecciona un nodo',\n        apiInterface: 'Habilitar API',\n        apiInterfaceClose: 'Una vez cerrado, no se podrá acceder a las interfaces de API. ¿Deseas continuar?',\n        apiInterfaceHelper: 'Permite que aplicaciones de terceros accedan a la API.',\n        apiInterfaceAlert1:\n            'No lo habilites en entornos de producción ya que puede aumentar riesgos de seguridad en el servidor.',\n        apiInterfaceAlert2:\n            'No uses aplicaciones de terceros para llamar a la API y evitar posibles amenazas de seguridad.',\n        apiInterfaceAlert3: 'Documento de API:',\n        apiInterfaceAlert4: 'Documento de uso:',\n        apiKey: 'Clave de API',\n        apiKeyHelper: 'La clave API se utiliza para que aplicaciones de terceros accedan a la API.',\n        ipWhiteList: 'Lista blanca de IP',\n        ipWhiteListEgs: 'Una por línea. Por ejemplo,\\n172.161.10.111\\n172.161.10.0/24',\n        ipWhiteListHelper:\n            'Las IP en la lista blanca pueden acceder a la API, 0.0.0.0/0 (todas IPv4), ::/0 (todas IPv6)',\n        apiKeyValidityTime: 'Periodo de validez de la clave de interfaz',\n        apiKeyValidityTimeEgs: 'Periodo de validez de la clave de interfaz (en minutos)',\n        apiKeyValidityTimeHelper:\n            'La marca de tiempo de la interfaz es válida si la diferencia con la actual (en minutos) está dentro del rango permitido. Un valor 0 desactiva la verificación.',\n        apiKeyReset: 'Restablecer clave de interfaz',\n        apiKeyResetHelper:\n            'El servicio asociado con la clave quedará inválido. Por favor añade una nueva clave al servicio',\n        confDockerProxy: 'Configurar proxy de Docker',\n        restartNowHelper: 'Configurar el proxy de Docker requiere reiniciar el servicio de Docker.',\n        restartNow: 'Reiniciar ahora',\n        restartLater: 'Reiniciar manualmente después',\n        systemIPWarning: 'La dirección del servidor no está configurada. Configúrala primero en el panel de control.',\n        systemIPWarning1:\n            'La dirección del servidor actual está configurada en {0}, ¡y la redirección rápida no es posible!',\n        syncTime: 'Hora del servidor',\n        timeZone: 'Zona horaria',\n        timeZoneChangeHelper: 'Cambiar la zona horaria requiere reiniciar el servicio. ¿Deseas continuar?',\n        timeZoneHelper:\n            'La modificación de zona horaria depende del servicio system timedatectl. Tomará efecto después de reiniciar el servicio 1Panel.',\n        timeZoneCN: 'Pekín',\n        timeZoneAM: 'Los Ángeles',\n        timeZoneNY: 'Nueva York',\n        ntpALi: 'Alibaba',\n        ntpGoogle: 'Google',\n        syncSite: 'Servidor NTP',\n        syncSiteHelper:\n            'Esta operación usará {0} como fuente para la sincronización horaria del sistema. ¿Deseas continuar?',\n        changePassword: 'Cambiar contraseña',\n        oldPassword: 'Contraseña original',\n        newPassword: 'Nueva contraseña',\n        retryPassword: 'Confirmar contraseña',\n        noSpace: 'La información ingresada no puede incluir espacios',\n        duplicatePassword: 'La nueva contraseña no puede ser igual a la original, por favor vuelve a introducirla',\n        diskClean: 'Limpieza de caché',\n        developerMode: 'Programa de vista previa',\n        developerModeHelper:\n            'Podrás experimentar nuevas funciones y correcciones antes de su lanzamiento general y dar retroalimentación anticipada.',\n        thirdParty: 'Cuentas de terceros',\n        scope: 'Ámbito',\n        public: 'Pública',\n        publicHelper:\n            'Las cuentas de respaldo de tipo público se sincronizarán en cada subnodo, y estos podrán usarlas conjuntamente',\n        private: 'Privada',\n        privateHelper:\n            'Las cuentas de respaldo de tipo privado solo se crean en el nodo actual y son para uso exclusivo de este nodo',\n        noTypeForCreate: 'Actualmente no se ha creado ningún tipo de respaldo',\n        LOCAL: 'Disco del servidor',\n        OSS: 'Ali OSS',\n        S3: 'Amazon S3',\n        mode: 'Modo',\n        MINIO: 'MinIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'La conexión WebDAV a Alist puede consultarse en la documentación oficial',\n        UPYUN: 'UPYUN',\n        ALIYUN: 'Aliyun Drive',\n        ALIYUNHelper:\n            'El límite máximo actual para descargas sin cliente en Aliyun Drive es de 100 MB. Superarlo requiere descargar mediante el cliente.',\n        ALIYUNRecover:\n            'El límite máximo actual para descargas sin cliente en Aliyun Drive es de 100 MB. Superarlo requiere descargar mediante el cliente al dispositivo local y luego sincronizar la instantánea para la recuperación.',\n        GoogleDrive: 'Google Drive',\n        analysis: 'Análisis',\n        analysisHelper:\n            'Pega el contenido completo del token para analizar automáticamente las partes requeridas. Para operaciones específicas, consulta la documentación oficial.',\n        serviceName: 'Nombre del servicio',\n        operator: 'Operador',\n        OneDrive: 'Microsoft OneDrive',\n        isCN: 'Versión Century Internet',\n        isNotCN: 'Versión internacional',\n        client_id: 'ID de cliente',\n        client_secret: 'Secreto de cliente',\n        redirect_uri: 'URL de redirección',\n        onedrive_helper: 'La configuración personalizada puede consultarse en la documentación oficial',\n        clickToRefresh: 'Haz clic para refrescar',\n        refreshTime: 'Tiempo de actualización del token',\n        refreshStatus: 'Estado de actualización del token',\n        backupDir: 'Directorio de respaldo',\n        codeWarning: 'El formato del código de autorización es incorrecto, ¡por favor confírmalo de nuevo!',\n        code: 'Código de autorización',\n        codeHelper:\n            'Haz clic en el botón \"Obtener\", luego inicia sesión en {0} y copia el contenido después de \"code\" en el enlace redirigido. Pégalo en este campo. Para instrucciones específicas, consulta la documentación oficial.',\n        googleHelper:\n            'Primero crea una aplicación de Google y obtén la información del cliente, completa el formulario y haz clic en obtener. Para operaciones específicas, consulta la documentación oficial.',\n        loadCode: 'Obtener',\n        COS: 'Tencent COS',\n        ap_beijing_1: 'Zona 1 Pekín',\n        ap_beijing: 'Pekín',\n        ap_nanjing: 'Nankín',\n        ap_shanghai: 'Shanghái',\n        ap_guangzhou: 'Cantón',\n        ap_chengdu: 'Chengdú',\n        ap_chongqing: 'Chongqing',\n        ap_shenzhen_fsi: 'Shenzhen Financiero',\n        ap_shanghai_fsi: 'Shanghái Financiero',\n        ap_beijing_fsi: 'Pekín Financiero',\n        ap_hongkong: 'Hong Kong, China',\n        ap_singapore: 'Singapur',\n        ap_mumbai: 'Bombay',\n        ap_jakarta: 'Yakarta',\n        ap_seoul: 'Seúl',\n        ap_bangkok: 'Bangkok',\n        ap_tokyo: 'Tokio',\n        na_siliconvalley: 'Silicon Valley (EE. UU. Oeste)',\n        na_ashburn: 'Ashburn (EE. UU. Este)',\n        na_toronto: 'Toronto',\n        sa_saopaulo: 'São Paulo',\n        eu_frankfurt: 'Fráncfort',\n        KODO: 'Qiniu Kodo',\n        scType: 'Tipo de almacenamiento',\n        typeStandard: 'Estándar',\n        typeStandard_IA: 'Estándar_IA',\n        typeArchive: 'Archivo',\n        typeDeep_Archive: 'Archivo profundo',\n        scLighthouse: 'Por defecto, el almacenamiento ligero de objetos solo admite este tipo de almacenamiento',\n        scStandard:\n            'Almacenamiento estándar, adecuado para escenarios con grandes volúmenes de archivos de acceso frecuente y alta interacción de datos.',\n        scStandard_IA:\n            'Almacenamiento de acceso infrecuente, adecuado para escenarios con baja frecuencia de acceso (ej., 1-2 veces por mes), con duración mínima de 30 días.',\n        scArchive: 'Almacenamiento de archivo, adecuado para escenarios con frecuencia de acceso extremadamente baja.',\n        scDeep_Archive:\n            'Almacenamiento en frío duradero, adecuado para escenarios con frecuencia de acceso extremadamente baja.',\n        archiveHelper:\n            'Los archivos de almacenamiento de archivo no pueden descargarse directamente, deben restaurarse primero desde la web del proveedor de la nube correspondiente. ¡Úsalo con precaución!',\n        backupAlert:\n            'Si un proveedor de nube es compatible con el protocolo S3, puedes usar Amazon S3 directamente para el respaldo. ',\n        domain: 'Dominio de aceleración',\n        backupAccount: 'Cuenta de respaldo | Cuentas de respaldo',\n        loadBucket: 'Obtener bucket',\n        accountName: 'Nombre de la cuenta',\n        accountKey: 'Clave de la cuenta',\n        address: 'Dirección',\n        path: 'Ruta',\n        safe: 'Seguridad',\n        passkey: 'Passkey',\n        passkeyManage: 'Administrar',\n        passkeyKeyManagement: 'Gestión de claves',\n        passkeyHelper: 'Para inicio rápido, se pueden vincular hasta 5 passkeys',\n        passkeyRequireSSL: 'Los passkeys requieren un dominio vinculado y acceso por HTTPS',\n        passkeyTrustedProxies: 'Proxies confiables',\n        passkeyTrustedProxiesHelper:\n            'Solo las solicitudes desde estas IP/CIDR confiarán en Forwarded y X-Forwarded-Proto para detectar HTTPS',\n        passkeyNotSupported: 'El navegador o entorno actual no admite passkeys',\n        passkeyPrereqTitle: 'Se requieren los siguientes requisitos previos para Passkey',\n        passkeyPrereqBindDomain: 'Vinculación de dominio de 1Panel configurada',\n        passkeyPrereqHttps: 'Acceso seguro mediante HTTPS',\n        passkeyPrereqBrowser: 'El entorno admite Passkey (WebAuthn + autenticador de plataforma)',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable:\n            'Usa un navegador del sistema y confirma que el panel se abre por HTTPS con un dominio vinculado.',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            'Este entorno no puede usar autenticación del dispositivo. Prueba un navegador del sistema en una versión más nueva del SO.',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            'Activa bloqueo de pantalla / huella / reconocimiento facial / Windows Hello y vuelve a intentarlo.',\n        passkeyPrereqBrowserDetailDetectFailed:\n            'La detección falló. Recarga la página y vuelve a intentarlo, o cambia a un navegador del sistema.',\n        passkeyPrereqGoSetup: 'Configurar',\n        passkeyCount: 'Vinculadas {0}/{1}',\n        passkeyName: 'Nombre',\n        passkeyNameHelper: 'Introduce un nombre para distinguir dispositivos',\n        passkeyAdd: 'Agregar Passkey',\n        passkeyCreatedAt: 'Creado',\n        passkeyLastUsedAt: 'Último uso',\n        passkeyDeleteConfirm: 'Después de eliminar, no podrás iniciar sesión con esta passkey. ¿Continuar?',\n        passkeyLimit: 'Se pueden vincular hasta 5 passkeys',\n        passkeyFailed:\n            'El registro de passkey falló, por favor, asegúrate de que el certificado SSL del panel es confiable',\n        bindInfo: 'Información de enlace',\n        bindAll: 'Escuchar en todas',\n        bindInfoHelper:\n            'Cambiar la dirección o el protocolo de escucha del servicio puede causar indisponibilidad. ¿Deseas continuar?',\n        ipv6: 'Escuchar IPv6',\n        bindAddress: 'Dirección de escucha',\n        entrance: 'Entrada',\n        showEntrance: 'Mostrar alerta de deshabilitado en la página \"Resumen\"',\n        entranceHelper:\n            'Habilitar entrada de seguridad solo permitirá iniciar sesión en el panel mediante la entrada especificada.',\n        entranceError: 'Introduce un punto de entrada seguro de 5 a 116 caracteres, solo se admiten números o letras.',\n        entranceInputHelper: 'Déjalo en blanco para desactivar la entrada de seguridad.',\n        randomGenerate: 'Aleatorio',\n        expirationTime: 'Fecha de expiración',\n        unSetting: 'No configurado',\n        noneSetting:\n            'Establece el tiempo de expiración para la contraseña del panel. Tras expirar, deberás restablecer la contraseña',\n        expirationHelper:\n            'Si el tiempo de expiración de la contraseña es [0] días, la función de expiración estará desactivada',\n        days: 'Días de expiración',\n        expiredHelper: 'La contraseña actual ha expirado. Por favor cámbiala de nuevo.',\n        timeoutHelper:\n            '[ {0} días ] La contraseña del panel está a punto de expirar. Tras expirar, deberás restablecerla',\n        complexity: 'Validación de complejidad',\n        complexityHelper:\n            'Tras habilitarlo, la regla de validación de contraseña será: 8-30 caracteres, incluyendo letras, números y al menos dos caracteres especiales.',\n        bindDomain: 'Vincular dominio',\n        unBindDomain: 'Desvincular dominio',\n        panelSSL: 'SSL del panel',\n        unBindDomainHelper:\n            'La acción de desvincular un dominio puede provocar inseguridad en el sistema. ¿Deseas continuar?',\n        bindDomainHelper: 'Tras vincular el dominio, solo ese dominio podrá acceder al servicio de 1Panel.',\n        bindDomainHelper1: 'Déjalo en blanco para desactivar la vinculación de dominio.',\n        bindDomainWarning:\n            'Tras la vinculación del dominio, se cerrará la sesión y solo se podrá acceder al servicio 1Panel mediante el dominio especificado. ¿Deseas continuar?',\n        allowIPs: 'IP autorizada',\n        unAllowIPs: 'IP no autorizada',\n        unAllowIPsWarning:\n            'Autorizar una IP vacía permitirá que todas las IP accedan al sistema, lo cual puede provocar inseguridad. ¿Deseas continuar?',\n        allowIPsHelper: 'Tras configurar la lista de IP autorizadas, solo las IP en la lista podrán acceder al panel.',\n        allowIPsWarning:\n            'Tras configurar la lista de IP autorizadas, solo las IP en la lista podrán acceder al panel. ¿Deseas continuar?',\n        allowIPsHelper1: 'Déjalo en blanco para desactivar la restricción por IP.',\n        allowIPEgs: 'Una por línea. Por ejemplo,\\n172.16.10.111\\n172.16.10.0/24',\n        mfa: 'Autenticación en Dos Pasos',\n        mfaClose: 'Desactivar MFA reducirá la seguridad del servicio. ¿Deseas continuar?',\n        secret: 'Secreto',\n        mfaInterval: 'Intervalo de refresco',\n        mfaTitleHelper:\n            'El título se usa para distinguir diferentes hosts de 1Panel. Tras modificarlo, vuelve a escanear o añade la clave secreta manualmente.',\n        mfaIntervalHelper:\n            'Tras modificar el intervalo de refresco, vuelve a escanear o añade la clave secreta manualmente.\\nAlgunas apps de autenticación solo admiten un intervalo de 30 segundos (por ejemplo, Google/Microsoft Authenticator).',\n        mfaAlert:\n            'El token de un solo uso es un número dinámico de 6 dígitos basado en la hora actual. Asegúrate de que la hora del servidor esté sincronizada.',\n        mfaHelper: 'Tras habilitarlo, será necesario verificar el token de un solo uso.',\n        mfaHelper1: 'Descarga una app de autenticación, por ejemplo,',\n        mfaHelper2:\n            'Para obtener el token, escanea el siguiente código QR con tu app de autenticación o copia la clave secreta en la aplicación.',\n        mfaHelper3: 'Introduce los seis dígitos de la app',\n        mfaCode: 'Token de un solo uso',\n        sslChangeHelper: 'Modificará la configuración https y reiniciará el servicio. ¿Deseas continuar?',\n        sslDisable: 'Deshabilitar',\n        sslDisableHelper:\n            'Si el servicio https está deshabilitado, deberás reiniciar el panel para que surta efecto. ¿Deseas continuar?',\n        noAuthSetting: 'Configuración no autorizada',\n        noAuthSettingHelper:\n            'Cuando los usuarios no inician sesión desde la entrada de seguridad especificada, o no acceden al panel desde la IP o dominio especificados, esta respuesta puede ocultar características del panel.',\n        responseSetting: 'Configuración de respuesta',\n        help200: 'Página de ayuda',\n        error400: 'Solicitud incorrecta',\n        error401: 'No autorizado',\n        error403: 'Prohibido',\n        error404: 'No encontrado',\n        error408: 'Tiempo de espera de la solicitud',\n        error416: 'Rango no satisfactorio',\n        error444: 'Conexión cerrada',\n        error500: 'Error interno del servidor',\n        https: 'Configurar HTTPS para el panel mejora la seguridad de acceso',\n        strictHelper: 'El tráfico no HTTPS no puede conectarse al panel',\n        muxHelper:\n            'El panel escuchará en los protocolos HTTP y HTTPS y redirigirá HTTP a HTTPS, pero esto puede reducir la seguridad del panel',\n        certType: 'Tipo de certificado',\n        selfSigned: 'Autofirmado',\n        selfSignedHelper:\n            'Los navegadores pueden no confiar en certificados autofirmados y mostrar advertencias de seguridad.',\n        select: 'Seleccionar',\n        domainOrIP: 'Dominio o IP:',\n        timeOut: 'Tiempo de espera',\n        rootCrtDownload: 'Descargar certificado raíz',\n        primaryKey: 'Clave primaria',\n        certificate: 'Certificado',\n        backupJump:\n            'Archivos de respaldo fuera de la lista actual, intenta descargarlos desde el directorio de archivos e importarlos para el respaldo.',\n        snapshot: 'Instantánea | Instantáneas',\n        noAppData: 'No hay aplicaciones del sistema disponibles para seleccionar',\n        noBackupData: 'No hay datos de respaldo disponibles para seleccionar',\n        stepBaseData: 'Datos base',\n        stepAppData: 'Aplicación del sistema',\n        stepPanelData: 'Datos del sistema',\n        stepBackupData: 'Datos de respaldo',\n        stepOtherData: 'Otros datos',\n        operationLog: 'Conservar registro de operaciones',\n        loginLog: 'Conservar registro de accesos',\n        systemLog: 'Conservar registro del sistema',\n        taskLog: 'Conservar registro de tareas',\n        monitorData: 'Conservar datos de monitoreo',\n        dockerConf: 'Conservar configuración de Docker',\n        selectAllImage: 'Respaldar todas las imágenes de aplicaciones',\n        logLabel: 'Registro',\n        agentLabel: 'Configuración de nodo',\n        appDataLabel: 'Datos de aplicación',\n        appImage: 'Imagen de aplicación',\n        appBackup: 'Respaldo de aplicación',\n        backupLabel: 'Directorio de respaldo',\n        confLabel: 'Archivo de configuración',\n        dockerLabel: 'Contenedor',\n        taskLabel: 'Tarea programada',\n        resourceLabel: 'Directorio de recursos de aplicación',\n        runtimeLabel: 'Entorno de ejecución',\n        appLabel: 'Aplicación',\n        databaseLabel: 'Base de datos',\n        snapshotLabel: 'Archivo de instantánea',\n        websiteLabel: 'Sitio web',\n        directoryLabel: 'Directorio',\n        appStoreLabel: 'Tienda de aplicaciones',\n        shellLabel: 'Script',\n        tmpLabel: 'Directorio temporal',\n        sslLabel: 'Directorio de certificados',\n        reCreate: 'No se pudo crear la instantánea',\n        reRollback: 'Error al revertir la instantánea',\n        deleteHelper:\n            'Todos los archivos de instantánea, incluidos los de la cuenta de respaldo de terceros, serán eliminados. ¿Deseas continuar?',\n        status: 'Estado de la instantánea',\n        ignoreRule: 'Regla de exclusión',\n        editIgnoreRule: '@:commons.button.edit @.lower:setting.ignoreRule',\n        ignoreHelper:\n            'Esta regla se usará para comprimir y respaldar el directorio de datos de 1Panel al crear la instantánea. Por defecto, se ignoran los archivos de socket.',\n        ignoreHelper1: 'Una por línea. Por ejemplo,\\n*.log\\n/opt/1panel/cache',\n        panelInfo: 'Escribir información básica de 1Panel',\n        panelBin: 'Respaldar archivos del sistema 1Panel',\n        daemonJson: 'Respaldar archivo de configuración de Docker',\n        appData: 'Respaldar aplicaciones instaladas de 1Panel',\n        panelData: 'Respaldar directorio de datos de 1Panel',\n        backupData: 'Respaldar directorio local de respaldo de 1Panel',\n        compress: 'Crear archivo de instantánea',\n        upload: 'Subir archivo de instantánea',\n        recoverDetail: 'Detalle de recuperación',\n        importHelper: 'Directorio de instantánea: ',\n        lastRecoverAt: 'Última hora de recuperación',\n        lastRollbackAt: 'Última hora de reversión',\n        reDownload: 'Descargar el archivo de respaldo de nuevo',\n        recoverErrArch: '¡La recuperación de instantánea entre arquitecturas de servidor diferentes no es compatible!',\n        recoverErrSize:\n            'Se detectó espacio insuficiente en disco, por favor verifica o libera espacio e inténtalo de nuevo.',\n        recoverHelper:\n            'Iniciando recuperación desde la instantánea {0}, confirma la siguiente información antes de continuar:',\n        recoverHelper1: 'La recuperación requiere reiniciar Docker y los servicios de 1Panel',\n        recoverHelper2:\n            'Asegúrate de que haya suficiente espacio en disco en el servidor (Tamaño del archivo de instantánea: {0}, Espacio disponible: {1})',\n        recoverHelper3:\n            'Asegúrate de que la arquitectura del servidor coincida con la del servidor donde se creó la instantánea (Arquitectura actual: {0})',\n        rollback: 'Reversión',\n        rollbackHelper:\n            'Revertir esta recuperación reemplazará todos los archivos de esta recuperación y puede requerir reiniciar Docker y los servicios de 1Panel. ¿Deseas continuar?',\n        upgradeRecord: 'Registro de actualización',\n        upgrading: ' Actualizando, por favor espera...',\n        upgradeHelper: 'La actualización requiere reiniciar el servicio 1Panel. ¿Deseas continuar?',\n        noUpgrade: 'Actualmente es la última versión',\n        versionHelper:\n            'Reglas de nombre: [versión mayor].[versión funcional].[versión de corrección de errores], como se muestra en el siguiente ejemplo:',\n        rollbackLocalHelper:\n            'El nodo principal no admite reversión directa. ¡Ejecuta manualmente el comando [1pctl restore] para revertir!',\n        upgradeCheck: 'Buscar actualizaciones',\n        upgradeNotes: 'Notas de la versión',\n        upgradeNow: 'Actualizar ahora',\n        source: 'Fuente de descarga',\n        versionNotSame:\n            'La versión del nodo no coincide con la del nodo principal. Actualiza en la gestión de nodos antes de reintentar.',\n        versionCompare:\n            'Se detectó que el nodo {0} ya está en la última versión actualizable. Verifica la versión del nodo principal e inténtalo de nuevo.',\n        about: 'Acerca de',\n        versionItem: 'Versión Actual',\n        backupCopies: 'Copias de Respaldo',\n        backupCopiesHelper:\n            'Establezca el número de copias de respaldo de actualización para conservar para la reversión de versión. 0 significa conservar todas.',\n        backupCopiesRule: 'Conserve al menos 3 registros de respaldo de actualización',\n        release: 'Notas de lanzamiento',\n        releaseHelper:\n            'No se pudieron obtener las notas de lanzamiento para el entorno actual. Puedes consultarlas manualmente en la documentación oficial.',\n        project: 'GitHub',\n        issue: 'Comentarios',\n        doc: 'Documento oficial',\n        star: 'Estrella',\n        description: 'Panel de servidor Linux',\n        forum: 'Discusiones',\n        doc2: 'Documentación',\n        currentVersion: 'Versión',\n        license: 'Licencia',\n        bindNode: 'Vincular nodo',\n        menuSetting: 'Configuración de menú',\n        menuSettingHelper: 'Cuando solo exista 1 submenú, la barra de menú mostrará únicamente ese submenú',\n        showAll: 'Mostrar todo',\n        hideALL: 'Ocultar todo',\n        ifShow: 'Mostrar o no',\n        menu: 'Menú',\n        confirmMessage: 'La página se actualizará para refrescar la lista de menús avanzados. ¿Deseas continuar?',\n        recoverMessage:\n            'La página se actualizará y la lista de menús se restaurará a su estado inicial. ¿Desea continuar?',\n        compressPassword: 'Contraseña de compresión',\n        backupRecoverMessage:\n            'Introduce la contraseña de compresión o descompresión (déjalo en blanco para no establecerla)',\n        componentSize: 'Tamaño del componente',\n        recoverFailed: 'Error al restaurar la instantánea',\n    },\n    license: {\n        community: 'OSS',\n        oss: 'Software Open Source',\n        pro: 'Pro',\n        trial: 'Prueba',\n        add: 'Agregar versión Community',\n        licenseBindHelper:\n            'Las cuotas de nodo gratuitas solo se pueden utilizar cuando la licencia está vinculada a un nodo',\n        licenseAlert:\n            'Solo se pueden agregar nodos Community cuando la licencia está correctamente vinculada. Solo los nodos vinculados admiten cambios.',\n        licenseUnbindHelper: 'Nodos Community detectados en esta licencia. Desvincule e intente nuevamente.',\n        subscription: 'Suscripción',\n        perpetual: 'Perpetua',\n        versionConstraint: '{0} Compra de versión',\n        forceUnbind: 'Desvinculación forzada',\n        forceUnbindHelper: 'La desvinculación forzada ignorará errores y liberará la licencia.',\n        updateForce: 'Forzar actualización (ignorar errores para liberar la licencia)',\n        trialInfo: 'Versión',\n        authorizationId: 'ID de autorización',\n        authorizedUser: 'Usuario autorizado',\n        lostHelper: 'La licencia alcanzó el máximo de reintentos. Sincronícela manualmente para activar funciones Pro.',\n        exceptionalHelper:\n            'Verificación de licencia anómala. Sincronícela manualmente para activar funciones Pro. detalle: ',\n        quickUpdate: 'Actualización rápida',\n        import: 'Importar',\n        power: 'Autorizar',\n        unbindHelper: 'Se eliminarán todos los ajustes Pro tras desvincular. ¿Desea continuar?',\n        importLicense: 'Importar licencia',\n        importHelper: 'Haga clic o arrastre el archivo de licencia aquí',\n        levelUpPro: 'Actualizar a Pro',\n        licenseSync: 'Sincronizar licencia',\n        knowMorePro: 'Ver más',\n        closeAlert: 'Puede cerrar la página en la configuración del panel',\n        introduce: 'Introducción de funciones',\n        waf: 'Actualizar a Pro permite interceptación, logs, geobloqueo, reglas, páginas personalizadas, etc.',\n        tamper: 'Actualizar a Pro protege sitios ante modificaciones no autorizadas.',\n        tamperHelper:\n            'La operación falló, el archivo/carpeta tiene protección antimanipulación. Revise e intente de nuevo.',\n        setting: 'Actualizar a Pro permite personalizar logo, mensaje de bienvenida y más.',\n        monitor: 'Pro permite ver estado web en tiempo real, tendencias, logs, etc.',\n        alert: 'Pro permite recibir alertas SMS y ver logs, control total de eventos clave.',\n        node: 'Actualizar a la Edición Profesional le permite administrar múltiples servidores Linux con 1Panel.',\n        nodeApp:\n            'Actualizar a la Edición Profesional permite actualizar de forma unificada las versiones de aplicaciones multi-nodo sin cambiar manualmente entre nodos.',\n        nodeDashboard:\n            'Actualizar a la Edición Profesional permite la gestión centralizada de aplicaciones, sitios web, bases de datos y tareas programadas multi-nodo.',\n        fileExchange: 'Pro permite transferir archivos entre varios servidores.',\n        app: 'Pro permite ver información de servicio, monitoreo, etc. vía app móvil.',\n        cluster:\n            'Actualizar a la Edición Profesional te permite gestionar clústeres maestro-esclavo de MySQL/Postgres/Redis.',\n        offLine: 'Sin conexión',\n    },\n    clean: {\n        scan: 'Iniciar escaneo',\n        scanHelper: 'Limpie fácilmente archivos basura generados por 1Panel',\n        clean: 'Limpiar ahora',\n        reScan: 'Volver a escanear',\n        cleanHelper:\n            'Los archivos y directorios seleccionados no se pueden revertir después de la limpieza. ¿Continuar?',\n        statusSuggest: '(Limpieza recomendada)',\n        statusClean: '(Muy limpio)',\n        statusEmpty: 'Muy limpio, ¡no necesita limpieza!',\n        statusWarning: '(Precaución)',\n        lastCleanTime: 'Última limpieza: {0}',\n        lastCleanHelper: 'Archivos/carpetas limpiados: {0}, total limpiado: {1}',\n        cleanSuccessful: 'Limpieza completada correctamente',\n        currentCleanHelper: 'En esta sesión: {0} archivos/carpetas, Total: {1}',\n        suggest: '(Recomendado)',\n        totalScan: 'Total a limpiar: ',\n        selectScan: 'Total seleccionado: ',\n        system: 'Archivos basura del sistema',\n        systemHelper: 'Archivos temporales generados durante instantáneas, actualizaciones y otros procesos',\n        panelOriginal: 'Archivos de copia de seguridad antes de restaurar instantánea del sistema',\n        upgrade: 'Archivos de copia de seguridad de actualización del sistema',\n        agentPackages: 'Paquetes de actualización/instalación de nodos secundarios de versiones históricas',\n        upgradeHelper: '(Mantenga el último respaldo para restaurar el sistema)',\n        snapshot: 'Archivos temporales de instantáneas',\n        rollback: 'Archivos de respaldo antes de recuperación',\n        backup: 'Copia de Seguridad del Sistema',\n        backupHelper: 'Archivos de copia de seguridad no asociados con cuentas de copia de seguridad locales',\n        unknownBackup: 'Copia de Seguridad del Sistema',\n        tmpBackup: 'Copia de Seguridad Temporal',\n        unknownApp: 'Copia de Seguridad de Aplicación no Asociada',\n        unknownDatabase: 'Copia de Seguridad de Base de Datos no Asociada',\n        unknownWebsite: 'Copia de Seguridad de Sitio Web no Asociada',\n        unknownSnapshot: 'Copia de Seguridad de Instantánea no Asociada',\n        upload: 'Archivos temporales de carga',\n        uploadHelper: 'Archivos temporales subidos en la lista de respaldos',\n        download: 'Archivos temporales de descarga',\n        downloadHelper: 'Archivos temporales descargados desde respaldos externos',\n        directory: 'Directorio',\n        systemLog: 'Archivo de registro',\n        systemLogHelper: 'Registros del sistema, registros de tareas, archivos de registro del sitio web',\n        dockerLog: 'Archivos de registro de operaciones de contenedores',\n        taskLog: 'Archivos de registro de ejecución de tareas programadas',\n        shell: 'Tareas programadas de script de shell',\n        containerShell: 'Tareas programadas de script de shell ejecutadas dentro de contenedores',\n        curl: 'Tareas programadas de CURL',\n        docker: 'Basura de contenedores',\n        dockerHelper: 'Archivos como contenedores, imágenes, volúmenes, caché de construcción, etc.',\n        volumes: 'Volúmenes',\n        buildCache: 'Caché de build de contenedores',\n        appTmpDownload: 'Archivo de descarga temporal de la aplicación',\n        unknownWebsiteLog: 'Archivo de respaldo de registro de sitio web no vinculado',\n    },\n    app: {\n        app: 'Aplicación | Aplicaciones',\n        installName: 'Nombre',\n        installed: 'Instaladas',\n        all: 'Todas',\n        version: 'Versión',\n        detail: 'Detalles',\n        params: 'Editar parámetros',\n        author: 'Autor',\n        source: 'Origen',\n        appName: 'Nombre de la aplicación',\n        deleteWarn:\n            'La eliminación eliminará todos los datos y respaldos. Esta acción no se puede deshacer. ¿Desea continuar?',\n        syncSuccess: 'Sincronizado correctamente',\n        canUpgrade: 'Actualizaciones',\n        backupName: 'Nombre de archivo',\n        backupPath: 'Ruta del archivo',\n        backupdate: 'Fecha de respaldo',\n        versionSelect: 'Seleccione una versión',\n        operatorHelper: 'Se realizará la operación {0} en la aplicación seleccionada. ¿Desea continuar?',\n        startOperatorHelper: 'La aplicación se iniciará. ¿Desea continuar?',\n        stopOperatorHelper: 'La aplicación se detendrá. ¿Desea continuar?',\n        restartOperatorHelper: 'La aplicación se reiniciará. ¿Desea continuar?',\n        reloadOperatorHelper: 'La aplicación se recargará. ¿Desea continuar?',\n        checkInstalledWarn: 'No se detecta \"{0}\". Vaya a \"Tienda de aplicaciones\" para instalar.',\n        limitHelper: 'La aplicación ya está instalada.',\n        deleteHelper: '\"{0}\" está asociada con los siguientes recursos y no puede eliminarse',\n        checkTitle: 'Aviso',\n        defaultConfig: 'Configuración predeterminada',\n        defaultConfigHelper: 'Se restauró a configuración predeterminada, surte efecto al guardar',\n        forceDelete: 'Forzar eliminación',\n        forceDeleteHelper: 'Forzar ignorará errores y eliminará la metadata.',\n        deleteBackup: 'Eliminar respaldo',\n        deleteBackupHelper: 'También eliminar respaldo de la aplicación',\n        deleteDB: 'Eliminar base de datos',\n        deleteDBHelper: 'También eliminar la base de datos',\n        noService: 'Sin {0}',\n        toInstall: 'Ir a instalar',\n        param: 'Parámetros',\n        alreadyRun: 'Edad',\n        syncAppList: 'Sincronizar',\n        less1Minute: 'Menos de 1 minuto',\n        appOfficeWebsite: 'Sitio web',\n        github: 'Github',\n        document: 'Documento',\n        updatePrompt: 'No hay actualizaciones disponibles',\n        installPrompt: 'Aún no hay apps instaladas',\n        updateHelper: 'Editar parámetros puede hacer que la app no inicie. Proceda con precaución.',\n        updateWarn: 'Actualizar parámetros requiere reconstruir la app. ¿Desea continuar?',\n        busPort: 'Puerto',\n        syncStart: '¡Sincronizando! Actualice la tienda después',\n        advanced: 'Configuración avanzada',\n        cpuCore: 'núcleo(s)',\n        containerName: 'Nombre del contenedor',\n        containerNameHelper: 'El nombre se generará automáticamente si no se establece',\n        allowPort: 'Acceso externo',\n        allowPortHelper: 'Permitir acceso externo abrirá el puerto en el firewall',\n        appInstallWarn:\n            'La aplicación no expone el puerto externo por defecto. Use \"Configuración avanzada\" para habilitarlo.',\n        upgradeStart: '¡Iniciando actualización! Refresque la página después',\n        toFolder: 'Abrir directorio de instalación',\n        editCompose: 'Editar archivo compose',\n        editComposeHelper: 'Editar el compose puede hacer que la instalación falle',\n        composeNullErr: 'El compose no puede estar vacío',\n        takeDown: 'Desinstalar',\n        allReadyInstalled: 'Instaladas',\n        installHelper: 'Si tiene problemas con el pull de imagen, configure aceleración.',\n        sortMode: 'Ordenar',\n        installWarn: 'Si no habilita el acceso externo, la app no será accesible externamente. ¿Desea continuar?',\n        showIgnore: 'Ver aplicaciones ignoradas',\n        cancelIgnore: 'Cancelar ignoradas',\n        ignoreList: 'Aplicaciones ignoradas',\n        appHelper: 'Consulte los detalles de la app para instrucciones de instalación.',\n        backupApp: 'Respaldar aplicación antes de actualizar',\n        backupAppHelper:\n            'Si la actualización falla, el respaldo se revertirá. Revise el motivo en los logs del sistema.',\n        openrestyDeleteHelper: 'Eliminar OpenResty forzadamente borrará todos los sitios. ¿Desea continuar?',\n        downloadLogHelper1: 'Se descargarán todos los logs de la app {0}. ¿Desea continuar?',\n        downloadLogHelper2: 'Se descargarán los últimos {1} logs de la app {0}. ¿Desea continuar?',\n        syncAllAppHelper: 'Se sincronizarán todas las aplicaciones. ¿Desea continuar?',\n        hostModeHelper: 'El modo de red es host. Si necesita abrir el puerto, hágalo manualmente en el firewall.',\n        showLocal: 'Mostrar apps locales',\n        reload: 'Recargar',\n        upgradeWarn: 'Actualizar reemplazará docker-compose.yml. Si hay cambios, puede ver la comparación.',\n        openclawHttpsUpgradeNoticeTitle:\n            'Nota: Las siguientes instrucciones solo se aplican a los usuarios que actualizan OpenClaw desde versiones anteriores a 2026.3.13:',\n        openclawHttpsUpgradeNoticeItem1:\n            'Después de actualizar el agente desplegado, vaya a Configuración -> Ajustes -> Seguridad y agregue manualmente la dirección de acceso.',\n        openclawHttpsUpgradeNoticeItem2:\n            'La nueva versión exige HTTPS para acceder al agente. Si antes usaba un sitio con proxy inverso, cambie el destino del proxy a https://IP:Port.',\n        newVersion: 'Nueva versión',\n        oldVersion: 'Versión actual',\n        composeDiff: 'Comparación de archivos',\n        showDiff: 'Ver comparación',\n        useNew: 'Usar versión personalizada',\n        useDefault: 'Usar versión predeterminada',\n        useCustom: 'Personalizar docker-compose.yml',\n        useCustomHelper:\n            'Personalizar docker-compose.yml puede causar errores de actualización. Si no es necesario, no marque.',\n        diffHelper: 'Izquierda: versión vieja, Derecha: nueva. Edite y guarde la versión personalizada.',\n        pullImage: 'Pull de imagen',\n        pullImageHelper: 'Ejecutar docker pull antes de iniciar la app',\n        deleteImage: 'Eliminar imagen',\n        deleteImageHelper: 'Eliminar imagen asociada a la app. Si falla, la tarea continuará.',\n        requireMemory: 'Memoria',\n        supportedArchitectures: 'Arquitecturas',\n        link: 'Enlace',\n        showCurrentArch: 'Arquitectura',\n        syncLocalApp: 'Sincronizar app local',\n        memoryRequiredHelper: 'La app requiere {0} de memoria',\n        gpuConfig: 'Habilitar soporte GPU',\n        gpuConfigHelper: 'Asegúrese de tener GPU NVIDIA, drivers y NVIDIA Docker Container Toolkit instalados',\n        webUI: 'Dirección web',\n        webUIPlaceholder: 'Ejemplo: ejemplo.com:8080/login',\n        defaultWebDomain: 'Dirección de acceso predeterminada',\n        defaultWebDomainHepler: 'Si el puerto es 8080, el acceso será http(s)://dirección:8080',\n        webUIConfig:\n            'No hay dirección de acceso predeterminada. Configúrela en los parámetros de la app o en la configuración del panel.',\n        toLink: 'Abrir',\n        customAppHelper: 'Antes de instalar un paquete personalizado, asegúrese de no tener apps instaladas.',\n        forceUninstall: 'Desinstalar forzadamente',\n        syncCustomApp: 'Sincronizar app personalizada',\n        ignoreAll: 'Ignorar todas las versiones siguientes',\n        ignoreVersion: 'Ignorar versión especificada',\n        specifyIP: 'Vincular IP de host',\n        specifyIPHelper: 'Establece la dirección/red del host para el puerto (si no sabe, no rellene)',\n        uninstallDeleteBackup: 'Desinstalar - Eliminar respaldo',\n        uninstallDeleteImage: 'Desinstalar - Eliminar imagen',\n        upgradeBackup: 'Respaldar app antes de actualizar',\n        noAppHelper:\n            'No se detectó ninguna aplicación, por favor vaya al centro de tareas para ver el registro de sincronización de la tienda de aplicaciones',\n        isEdirWarn: 'Se detectó modificación en el archivo docker-compose.yml, por favor revise la comparación',\n    },\n    website: {\n        primaryDomain: 'Dominio principal',\n        otherDomains: 'Otros dominios',\n        static: 'Estático',\n        deployment: 'Despliegue',\n        supportUpType: 'Solo se admite el formato .tar.gz, y el paquete comprimido debe contener el archivo {0}.json',\n        proxy: 'Proxy inverso',\n        alias: 'Alias',\n        ftpUser: 'Cuenta FTP',\n        ftpPassword: 'Contraseña FTP',\n        ftpHelper:\n            'Al crear un sitio web se generará una cuenta FTP correspondiente, cuyo directorio enlazará con el directorio del sitio.',\n        remark: 'Observación',\n        groupSetting: 'Gestión de grupos',\n        createGroup: 'Crear grupo',\n        appNew: 'Nueva aplicación',\n        appInstalled: 'Aplicación instalada',\n        delete: 'Eliminar sitio',\n        deleteApp: 'Eliminar aplicación',\n        deleteBackup: 'Eliminar copia de seguridad',\n        domain: 'Dominio',\n        domainHelper: 'Un dominio por línea.\\nSoporta comodín \"*\" y direcciones IP.\\nTambién permite añadir puerto.',\n        addDomain: 'Añadir dominio',\n        domainConfig: 'Dominios',\n        defaultDoc: 'Documento predeterminado',\n        perserver: 'Concurrencia por servidor',\n        perserverHelper: 'Límite de concurrencia máxima para este sitio',\n        perip: 'Concurrencia por IP',\n        peripHelper: 'Límite de accesos concurrentes desde una misma IP',\n        rate: 'Límite de tráfico',\n        rateLimit: 'Límite de velocidad por petición',\n        rateHelper: 'Limita el flujo de cada petición (unidad: KB)',\n        rateLimitHelper: 'Limita la velocidad de transferencia por petición (unidad: KB/s)',\n        limitHelper: 'Activar control de flujo',\n        other: 'Otros',\n        currentSSL: 'Certificado actual',\n        dnsAccount: 'Cuenta DNS',\n        applySSL: 'Solicitar certificado',\n        SSLList: 'Lista de certificados',\n        createDnsAccount: 'Cuenta DNS',\n        aliyun: 'Aliyun DNS',\n        aliEsa: 'Aliyun ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: 'Resolución manual',\n        key: 'Clave',\n        check: 'Ver',\n        acmeAccountManage: 'Gestión de cuentas ACME',\n        email: 'Correo electrónico',\n        acmeAccount: 'Cuenta ACME',\n        provider: 'Método de verificación',\n        dnsManual: 'Resolución manual',\n        expireDate: 'Fecha de expiración',\n        brand: 'Organización',\n        deploySSL: 'Desplegar certificado',\n        deploySSLHelper: '¿Seguro que deseas desplegar este certificado?',\n        ssl: 'Certificado | Certificados',\n        dnsAccountManage: 'Gestión de proveedores DNS',\n        renewSSL: 'Renovar',\n        renewHelper: '¿Seguro que deseas renovar el certificado?',\n        renewSuccess: 'Certificado renovado',\n        enableHTTPS: 'Habilitar',\n        aliasHelper: 'El alias es el nombre de directorio del sitio web',\n        lastBackupAt: 'Última copia de seguridad',\n        null: 'Ninguno',\n        nginxConfig: 'Configuración Nginx',\n        websiteConfig: 'Configuración del sitio',\n        proxySettings: 'Configuración de proxy',\n        advancedSettings: 'Configuración avanzada',\n        cacheSettings: 'Configuración de caché',\n        sniSettings: 'Configuración SNI',\n        basic: 'Básico',\n        source: 'Configuración',\n        security: 'Seguridad',\n        nginxPer: 'Optimización de rendimiento',\n        neverExpire: 'Nunca expira',\n        setDefault: 'Establecer como predeterminado',\n        deleteHelper: 'El estado de la aplicación asociada es anómalo, revisa antes de continuar',\n        toApp: 'Ir a aplicaciones instaladas',\n        cycle: 'Ciclo',\n        frequency: 'Frecuencia',\n        ccHelper:\n            'Si la misma URL recibe más de {1} peticiones en {0} segundos, se activa defensa CC y se bloquea la IP',\n        mustSave: 'Los cambios deben guardarse para que surtan efecto',\n        fileExt: 'Extensión de archivo',\n        fileExtBlock: 'Lista negra de extensiones',\n        value: 'Valor',\n        enable: 'Habilitar',\n        proxyAddress: 'Dirección de proxy',\n        proxyHelper: 'Ejemplo: 127.0.0.1:8080',\n        forceDelete: 'Forzar eliminación',\n        forceDeleteHelper: 'La eliminación forzada ignorará errores y eliminará metadatos igualmente.',\n        deleteAppHelper: 'Eliminar también aplicaciones y backups asociados',\n        deleteBackupHelper: 'Eliminar también copias de seguridad del sitio',\n        deleteDatabaseHelper: 'También eliminar la base de datos asociada al sitio web',\n        deleteConfirmHelper:\n            'Esta acción es irreversible. Escribe <span style=\"color:red\"> \"{0}\" </span> para confirmar.',\n        staticPath: 'El directorio principal correspondiente es ',\n        limit: 'Esquema',\n        blog: 'Foro/Blog',\n        imageSite: 'Sitio de imágenes',\n        downloadSite: 'Sitio de descargas',\n        shopSite: 'Tienda',\n        doorSite: 'Portal',\n        qiteSite: 'Empresa',\n        videoSite: 'Video',\n        errLog: 'Log de errores',\n        stopHelper: 'Al detener el sitio, no será accesible y mostrará una página de parada. ¿Deseas continuar?',\n        startHelper: 'Al habilitar el sitio, los usuarios podrán acceder normalmente. ¿Deseas continuar?',\n        sitePath: 'Directorio',\n        siteAlias: 'Alias del sitio',\n        primaryPath: 'Directorio raíz',\n        folderTitle: 'El sitio contiene principalmente las siguientes carpetas',\n        wafFolder: 'Reglas del firewall',\n        indexFolder: 'Directorio raíz del sitio',\n        sslFolder: 'Certificados del sitio',\n        enableOrNot: 'Habilitar',\n        oldSSL: 'Certificado existente',\n        manualSSL: 'Importar certificado',\n        select: 'Seleccionar',\n        selectSSL: 'Seleccionar certificado',\n        privateKey: 'Clave privada (KEY)',\n        certificate: 'Certificado (formato PEM)',\n        HTTPConfig: 'Opciones HTTP',\n        HTTPSOnly: 'Bloquear peticiones HTTP',\n        HTTPToHTTPS: 'Redirigir a HTTPS',\n        HTTPAlso: 'Permitir peticiones HTTP directas',\n        sslConfig: 'Opciones SSL',\n        disableHTTPS: 'Desactivar HTTPS',\n        disableHTTPSHelper: 'Desactivar HTTPS eliminará la configuración del certificado. ¿Quieres continuar?',\n        SSLHelper:\n            'Nota: No uses certificados SSL en sitios ilegales.\\nSi HTTPS no funciona, comprueba que el puerto 443 esté abierto en el firewall.',\n        SSLConfig: 'Configuración del certificado',\n        SSLProConfig: 'Configuración del protocolo',\n        supportProtocol: 'Versión de protocolo',\n        encryptionAlgorithm: 'Algoritmo de cifrado',\n        notSecurity: '(inseguro)',\n        encryptHelper:\n            \"Let's Encrypt tiene límites de frecuencia para emitir certificados. Consulta la <a target='_blank' href='https://letsencrypt.org/zh-cn/docs/rate-limits/'>documentación oficial</a>.\",\n        ipValue: 'Valor',\n        ext: 'Extensión de archivo',\n        wafInputHelper: 'Introduce datos línea por línea',\n        data: 'Datos',\n        ever: 'Permanente',\n        nextYear: 'Un año',\n        noLog: 'No se encontraron logs',\n        defaultServer: 'Configurar como sitio por defecto',\n        noDefaultServer: 'No configurado',\n        defaultServerHelper:\n            'El sitio por defecto recibirá peticiones de dominios/IP no asociados.\\nPreviene resoluciones maliciosas, pero anula la intercepción WAF de dominios no autorizados.',\n        restoreHelper: '¿Seguro que deseas restaurar este backup?',\n        websiteDeploymentHelper: 'Usa una aplicación instalada o crea una nueva aplicación para desplegar un sitio.',\n        websiteStatictHelper: 'Crea un directorio de sitio estático en el host.',\n        websiteProxyHelper:\n            'Usa un proxy inverso para servicios existentes. Ejemplo: un servicio en el puerto 8080 → \"http://127.0.0.1:8080\".',\n        runtimeProxyHelper: 'Usa un runtime de sitio web para crear un sitio.',\n        runtime: 'Runtime',\n        deleteRuntimeHelper: 'El runtime debe eliminarse junto con el sitio. Hazlo con precaución.',\n        proxyType: 'Tipo de red',\n        unix: 'Red Unix',\n        tcp: 'Red TCP/IP',\n        phpFPM: 'Config FPM',\n        phpConfig: 'Config PHP',\n        updateConfig: 'Actualizar configuración',\n        isOn: 'Activado',\n        isOff: 'Desactivado',\n        rewrite: 'Reescritura (pseudoestático)',\n        rewriteMode: 'Esquema',\n        current: 'Actual',\n        rewriteHelper: 'Si el pseudoestático causa inaccesibilidad, restaura la configuración predeterminada.',\n        runDir: 'Directorio de ejecución',\n        runUserHelper:\n            'En entornos PHP con contenedor, asigna propietario/grupo 1000 a index y subdirectorios. En PHP local, usa el usuario/grupo de PHP-FPM.',\n        userGroup: 'Usuario/Grupo',\n        uGroup: 'Grupo',\n        proxyPath: 'Ruta de proxy',\n        proxyPass: 'URL destino',\n        cache: 'Caché',\n        cacheTime: 'Duración de caché',\n        enableCache: 'Activar caché',\n        proxyHost: 'Host de proxy',\n        disabled: 'Detenido',\n        startProxy: 'Se iniciará el proxy inverso. ¿Deseas continuar?',\n        stopProxy: 'Se detendrá el proxy inverso. ¿Deseas continuar?',\n        sourceFile: 'Ver origen',\n        proxyHelper1: 'Al acceder a este directorio se devolverá el contenido del destino configurado.',\n        proxyPassHelper: 'La URL destino debe ser válida y accesible.',\n        proxyHostHelper: 'Pasar el dominio en la cabecera Host al servidor proxy.',\n        modifier: 'Reglas de coincidencia',\n        modifierHelper: 'Ejemplo: \"=\" coincidencia exacta, \"~\" regex, \"^~\" inicio de ruta.',\n        replace: 'Reemplazos de texto',\n        replaceHelper:\n            'Permite sustitución de cadenas en contenido proxy (HTML, CSS, JS, etc.). Soporta regex para casos complejos.',\n        addReplace: 'Añadir',\n        replaced: 'Cadena de búsqueda (obligatoria)',\n        replaceText: 'Reemplazar por',\n        replacedErr: 'La cadena de búsqueda no puede estar vacía',\n        replacedErr2: 'La cadena de búsqueda no puede repetirse',\n        replacedListEmpty: 'Sin reglas de reemplazo',\n        proxySslName: 'Nombre SNI de proxy',\n        basicAuth: 'Autenticación básica',\n        editBasicAuthHelper:\n            'La contraseña está cifrada de forma asimétrica y no puede mostrarse. Editar implica restablecerla.',\n        antiLeech: 'Anti-hotlink',\n        extends: 'Extensiones',\n        browserCache: 'Caché de navegador',\n        noModify: 'No Modificar',\n        serverCache: 'Caché del servidor',\n        leechLog: 'Registrar logs anti-hotlink',\n        accessDomain: 'Dominios permitidos',\n        leechReturn: 'Recurso de respuesta',\n        noneRef: 'Permitir Referer vacío',\n        disable: 'No habilitado',\n        disableLeechHelper: 'Deshabilitar protección anti-hotlink',\n        disableLeech: 'Desactivar anti-hotlink',\n        ipv6: 'Escuchar IPv6',\n        leechReturnError: 'Introduce un código HTTP válido',\n        blockedRef: 'Permitir Referer no estándar',\n        accessControl: 'Control anti-hotlink',\n        leechcacheControl: 'Control de caché',\n        logEnableControl: 'Registrar peticiones de estáticos',\n        leechSpecialValidHelper:\n            'Permitir Referer vacío: peticiones sin Referer no se bloquean. Permitir Referer no estándar: se aceptan Referer que no empiecen con http/https.',\n        leechInvalidReturnHelper: 'Código HTTP devuelto tras bloquear hotlinking',\n        leechlogControlHelper:\n            'Registrar peticiones de estáticos (deshabilitar en producción para evitar exceso de logs).',\n        selectAcme: 'Seleccionar cuenta Acme',\n        imported: 'Creado manualmente',\n        importType: 'Tipo de importación',\n        pasteSSL: 'Pegar código',\n        localSSL: 'Seleccionar archivo del servidor',\n        privateKeyPath: 'Archivo de clave privada',\n        certificatePath: 'Archivo de certificado',\n        ipWhiteListHelper: 'La lista blanca de IP ignora todas las reglas de bloqueo',\n        redirect: 'Redirección',\n        sourceDomain: 'Dominio origen',\n        targetURL: 'URL destino',\n        keepPath: 'Mantener parámetros URI',\n        path: 'Ruta',\n        redirectType: 'Tipo de redirección',\n        redirectWay: 'Modo',\n        keep: 'Mantener',\n        notKeep: 'No mantener',\n        redirectRoot: 'Redirigir a la home',\n        redirectHelper: '301 permanente o 302 temporal',\n        changePHPVersionWarn: 'Esta acción es irreversible, ¿continuar?',\n        changeVersion: 'Cambiar versión',\n        retainConfig: 'Conservar php-fpm.conf y php.ini',\n        runDirHelper2: 'El directorio secundario debe estar bajo index',\n        openrestyHelper:\n            'Puertos por defecto de OpenResty → HTTP: {0}, HTTPS: {1}. Esto puede afectar redirecciones y accesos.',\n        primaryDomainHelper: 'Ejemplo: ejemplo.com o ejemplo.com:8080',\n        acmeAccountType: 'Tipo de cuenta',\n        keyType: 'Algoritmo de clave',\n        tencentCloud: 'Tencent Cloud',\n        containWarn: 'El dominio incluye al principal, corrige la entrada',\n        rewriteHelper2:\n            'Aplicaciones como WordPress suelen tener configuración pseudoestática predefinida. Cambiarla puede causar errores.',\n        websiteBackupWarn: 'Solo se admiten backups locales. Los de otras máquinas pueden fallar en la restauración',\n        ipWebsiteWarn: 'Los sitios con IP como dominio deben configurarse como sitio por defecto para funcionar',\n        hstsHelper: 'Habilitar HSTS mejora la seguridad',\n        includeSubDomains: 'Incluir subdominios',\n        hstsIncludeSubDomainsHelper: 'Al habilitarlo, la política HSTS aplicará también a los subdominios.',\n        defaultHtml: 'Configurar página por defecto',\n        website404: 'Página de error 404',\n        domain404: 'Página no encontrada',\n        indexHtml: 'Página predeterminada estática',\n        stopHtml: 'Página de sitio detenido',\n        indexPHP: 'Página predeterminada PHP',\n        sslExpireDate: 'Fecha de expiración SSL',\n        website404Helper: 'La página 404 solo aplica a sitios PHP o estáticos',\n        sni: 'SNI de origen',\n        sniHelper:\n            'Cuando el backend proxy es HTTPS, puede ser necesario configurar el SNI. Consulta la doc del proveedor CDN.',\n        huaweicloud: 'Huawei Cloud',\n        createDb: 'Crear base de datos',\n        enableSSLHelper: 'Si falla, no afectará la creación del sitio',\n        batchAdd: 'Añadir múltiples dominios',\n        batchInput: 'Entrada por Lotes',\n        domainNotFQDN: 'Este dominio puede no ser accesible en la red pública',\n        domainInvalid: 'Formato de dominio inválido',\n        domainBatchHelper:\n            'Un dominio por línea, formato: dominio:puerto@ssl\\nEjemplo: example.com:443@ssl o example.com',\n        generateDomain: 'Generar',\n        global: 'Global',\n        subsite: 'Subsitio',\n        subsiteHelper: 'Un subsite puede usar un directorio PHP o estático existente como raíz.',\n        parentWbeiste: 'Sitio padre',\n        deleteSubsite: 'Antes de borrar este sitio elimina los subsitios {0}',\n        loadBalance: 'Balanceo de carga',\n        server: 'Servidor',\n        algorithm: 'Algoritmo',\n        ipHash: 'IP Hash',\n        ipHashHelper: 'Asigna al mismo servidor las peticiones de un cliente según su IP.',\n        leastConn: 'Menos conexiones',\n        leastConnHelper: 'Envía peticiones al servidor con menos conexiones activas.',\n        leastTime: 'Menor tiempo',\n        leastTimeHelper: 'Envía peticiones al servidor con menor tiempo de conexión activa.',\n        defaultHelper:\n            'Método por defecto: reparte las peticiones equitativamente. Si hay pesos configurados, reparte según ellos.',\n        weight: 'Peso',\n        maxFails: 'Fallos máximos',\n        maxConns: 'Máximo de conexiones',\n        strategy: 'Estrategia',\n        strategyDown: 'Baja',\n        strategyBackup: 'Backup',\n        ipHashBackupErr: 'IP hash does not support backup nodes',\n        failTimeout: 'Tiempo de espera de fallo',\n        failTimeoutHelper:\n            'La duración de la ventana de tiempo para las comprobaciones de estado del servidor. Cuando el número acumulado de fallos alcanza el umbral dentro de este período, el servidor se eliminará temporalmente y se reintentará después de la misma duración. Por defecto 10 segundos',\n        staticChangePHPHelper: 'Actualmente es un sitio estático, puedes cambiarlo a PHP',\n        proxyCache: 'Caché de proxy inverso',\n        cacheLimit: 'Límite de espacio de caché',\n        shareCahe: 'Tamaño de memoria para conteo de caché',\n        cacheExpire: 'Expiración de caché',\n        shareCaheHelper: '1M de memoria almacena aprox. 8000 objetos',\n        cacheLimitHelper: 'Se eliminará caché antigua al superar el límite',\n        cacheExpireJHelper: 'Caché eliminada al expirar si no se usa',\n        realIP: 'IP real',\n        ipFrom: 'Origen de IP',\n        ipFromHelper: 'Definiendo orígenes confiables, OpenResty analizará cabeceras HTTP para identificar la IP real.',\n        ipFromExample1: 'Si el frontend es Frp: 127.0.0.1',\n        ipFromExample2: 'Si el frontend es un CDN: rango IP del CDN',\n        ipFromExample3: 'En caso de duda: 0.0.0.0/0 (IPv4) ::/0 (IPv6) [Nota: poco seguro]',\n        http3Helper: 'HTTP/3 ofrece mayor velocidad y rendimiento, pero no todos los navegadores lo soportan.',\n        cors: 'Intercambio de Recursos de Origen Cruzado (CORS)',\n        enableCors: 'Habilitar CORS',\n        allowOrigins: 'Dominios permitidos',\n        allowMethods: 'Métodos de solicitud permitidos',\n        allowHeaders: 'Encabezados de solicitud permitidos',\n        allowCredentials: 'Permitir enviar cookies',\n        preflight: 'Respuesta rápida a solicitud de preflight',\n        preflightHleper:\n            'Cuando está habilitado, cuando el navegador envía una solicitud de preflight de origen cruzado (solicitud OPTIONS), el sistema devolverá automáticamente un código de estado 204 y establecerá los encabezados de respuesta de origen cruzado necesarios',\n        changeDatabase: 'Cambiar base de datos',\n        changeDatabaseHelper1: 'La asociación se usa en backups/restauraciones.',\n        changeDatabaseHelper2: 'Cambiar de DB invalida backups previos.',\n        saveCustom: 'Guardar como plantilla',\n        rainyun: 'Rain Yun',\n        volcengine: 'Volcengine',\n        runtimePortHelper: 'Este runtime tiene múltiples puertos, selecciona uno.',\n        runtimePortWarn: 'Este runtime no tiene puertos, no se puede usar proxy',\n        cacheWarn: 'Desactiva la caché en proxy inverso primero',\n        loadBalanceHelper: 'Tras crear balanceo, ve a \"Proxy inverso\" y añade un backend: http://<nombre balanceo>',\n        favorite: 'Favorito',\n        cancelFavorite: 'Quitar favorito',\n        useProxy: 'Usar proxy',\n        useProxyHelper: 'Usa la dirección del proxy configurado en el panel',\n        westCN: 'West Digital',\n        openBaseDir: 'Prevenir ataques cross-site',\n        openBaseDirHelper: 'open_basedir restringe rutas accesibles por PHP, protegiendo contra accesos cruzados',\n        serverCacheTime: 'Tiempo de caché en servidor',\n        serverCacheTimeHelper: 'Durante este tiempo, peticiones idénticas devuelven caché sin ir al backend.',\n        browserCacheTime: 'Tiempo de caché en navegador',\n        browserCacheTimeHelper:\n            'Recursos estáticos se guardan en caché en el navegador reduciendo solicitudes repetidas.',\n        donotLinkeDB: 'No vincular base de datos',\n        toWebsiteDir: 'Acceder a directorio del sitio',\n        execParameters: 'Parámetros de ejecución',\n        extCommand: 'Comando adicional',\n        mirror: 'Fuente mirror',\n        execUser: 'Usuario de ejecución',\n        execDir: 'Directorio de ejecución',\n        packagist: 'Mirror China completo',\n        batchOperate: 'Operación en Lote',\n        batchOperateHelper: 'Lote {0} sitios web, ¿continuar operación?',\n        stream: 'Proxy TCP/UDP',\n        streamPorts: 'Puertos de escucha',\n        streamPortsHelper:\n            'Configura el número de puerto de escucha externo, los clientes accederán al servicio a través de este puerto, separado por comas, p. ej., 5222,5223',\n        streamHelper: 'Reenvío de Puertos y Balanceo de Carga TCP/UDP',\n        udp: 'Habilitar UDP',\n        syncHtmlHelper: 'Sincronizar con PHP y sitios web estáticos',\n        wafValueHelper: 'Valor',\n    },\n    php: {\n        short_open_tag: 'Soporte de etiquetas cortas',\n        max_execution_time: 'Tiempo máximo de ejecución del script',\n        max_input_time: 'Tiempo máximo de entrada',\n        memory_limit: 'Límite de memoria del script',\n        post_max_size: 'Tamaño máximo de datos POST',\n        file_uploads: 'Permitir subida de archivos',\n        upload_max_filesize: 'Tamaño máximo permitido para subir archivos',\n        max_file_uploads: 'Cantidad máxima de archivos a subir al mismo tiempo',\n        default_socket_timeout: 'Tiempo de espera del socket',\n        error_reporting: 'Nivel de errores',\n        display_errors: 'Mostrar información detallada de errores',\n        cgi_fix_pathinfo: 'Habilitar pathinfo',\n        date_timezone: 'Zona horaria',\n        disableFunction: 'Deshabilitar función',\n        disableFunctionHelper: 'Introduce la función a deshabilitar, como exec. Para varias, separa con comas',\n        uploadMaxSize: 'Límite de subida',\n        indexHelper:\n            'Para garantizar el funcionamiento correcto del sitio PHP, coloca el código en el directorio index y evita renombrar',\n        extensions: 'Gestionar extensiones',\n        extension: 'Extensión',\n        extensionHelper: 'Para varias extensiones, sepáralas con comas',\n        toExtensionsList: 'Ver lista de extensiones',\n        containerConfig: 'Configuración del contenedor',\n        containerConfigHelper:\n            'Las variables de entorno y demás se pueden modificar en Configuración - Configuración del Contenedor después de la creación',\n        dateTimezoneHelper: 'Ejemplo: TZ=America/Mexico_City (ajusta según necesites)',\n        extensionsHelper: 'Para varias extensiones, sepáralas con comas',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: 'Tamaño de la tabla hash para nombres de servidor',\n        clientHeaderBufferSizeHelper: 'Tamaño del buffer de cabecera solicitado por el cliente',\n        clientMaxBodySizeHelper: 'Tamaño máximo de archivo subido',\n        keepaliveTimeoutHelper: 'Tiempo de espera de la conexión',\n        gzipMinLengthHelper: 'Tamaño mínimo para comprimir',\n        gzipCompLevelHelper: 'Nivel de compresión',\n        gzipHelper: 'Habilitar compresión para transmisión',\n        connections: 'Conexiones activas',\n        accepts: 'Aceptadas',\n        handled: 'Gestionadas',\n        requests: 'Solicitudes',\n        reading: 'Leyendo',\n        writing: 'Escribiendo',\n        waiting: 'Esperando',\n        status: 'Estado actual',\n        configResource: 'Configuración',\n        saveAndReload: 'Guardar y recargar',\n        clearProxyCache: 'Limpiar caché de proxy inverso',\n        clearProxyCacheWarn: 'Esta acción eliminará todos los archivos en el directorio de caché. ¿Deseas continuar?',\n        create: 'Agregar nuevo módulo',\n        update: 'Editar módulo',\n        params: 'Parámetros',\n        packages: 'Paquetes',\n        script: 'Scripts',\n        module: 'Módulos',\n        build: 'Compilar',\n        buildWarn: 'Compilar OpenResty requiere reservar CPU y memoria, puede tomar tiempo, ten paciencia',\n        mirrorUrl: 'Fuente de software',\n        paramsHelper: 'Por ejemplo: --add-module=/tmp/ngx_brotli',\n        packagesHelper: 'Por ejemplo: git, curl (separados por coma)',\n        scriptHelper:\n            'Scripts a ejecutar antes de compilar, usualmente para descargar código fuente de módulos, instalar dependencias, etc.',\n        buildHelper:\n            'Haz clic en compilar después de agregar/modificar un módulo. OpenResty se reiniciará automáticamente tras una compilación exitosa.',\n        defaultHttps: 'HTTPS Anti-manipulación',\n        defaultHttpsHelper1: 'Habilitar esto puede resolver problemas de manipulación de HTTPS.',\n        sslRejectHandshake: 'Rechazar handshake SSL predeterminado',\n        sslRejectHandshakeHelper:\n            'Habilitar esto puede evitar la fuga de certificados, establecer un sitio web predeterminado invalidará esta configuración',\n    },\n    ssl: {\n        create: 'Solicitar',\n        provider: 'Tipo',\n        manualCreate: 'Creado manualmente',\n        acmeAccount: 'Cuenta ACME',\n        resolveDomain: 'Resolver dominio',\n        err: 'Error',\n        value: 'valor del registro',\n        dnsResolveHelper: 'Por favor, agrega los siguientes registros en tu proveedor de DNS:',\n        detail: 'Ver detalles',\n        msg: 'Información',\n        ssl: 'Certificado',\n        key: 'Clave privada',\n        startDate: 'Fecha de inicio',\n        organization: 'Organización emisora',\n        renewConfirm: 'Esto renovará un nuevo certificado para el dominio {0}. ¿Deseas continuar?',\n        autoRenew: 'Renovación automática',\n        autoRenewHelper: 'Renueva automáticamente 30 días antes de la expiración',\n        renewSuccess: 'Renovación exitosa',\n        renewWebsite: 'Este certificado está asociado a los siguientes sitios y se aplicará en ellos simultáneamente',\n        createAcme: 'Crear cuenta',\n        acmeHelper: 'La cuenta Acme se usa para solicitar certificados gratuitos',\n        upload: 'Importar',\n        applyType: 'Método de solicitud',\n        apply: 'Renovar',\n        applyStart: 'Inicia solicitud de certificado',\n        getDnsResolve: 'Obteniendo valor DNS, espera...',\n        selfSigned: 'Gestionar CA autofirmada',\n        ca: 'Autoridad certificadora',\n        commonName: 'Nombre común',\n        caName: 'Nombre de CA',\n        company: 'Nombre de la organización',\n        department: 'Unidad organizativa',\n        city: 'Ciudad',\n        province: 'Estado o provincia',\n        country: 'País (código de 2 letras)',\n        commonNameHelper: 'Por ejemplo, ',\n        selfSign: 'Emitir certificado',\n        days: 'Período de validez',\n        domainHelper: 'Un dominio por línea, admite * y dirección IP',\n        pushDir: 'Guardar certificado en el directorio local',\n        dir: 'Directorio',\n        pushDirHelper: 'Se generarán los archivos \"fullchain.pem\" y \"privkey.pem\" en este directorio.',\n        organizationDetail: 'Detalles de la organización',\n        fromWebsite: 'Desde el sitio web',\n        dnsMauanlHelper: 'En modo manual, haz clic en solicitar tras la creación para obtener el valor DNS',\n        httpHelper: 'El modo HTTP requiere OpenResty instalado y no soporta certificados comodín.',\n        buypassHelper: 'Buypass no es accesible desde China continental',\n        googleHelper: 'Cómo obtener EAB HmacKey y EAB kid',\n        googleCloudHelper: 'La API de Google Cloud no es accesible en la mayoría de China',\n        skipDNSCheck: 'Omitir comprobación DNS',\n        skipDNSCheckHelper: 'Solo marca si tienes problemas de timeout al solicitar el certificado.',\n        cfHelper: 'No uses la clave API Global',\n        deprecated: 'será obsoleto',\n        deprecatedHelper: 'El soporte se ha detenido y puede eliminarse en el futuro. Usa el método Tencent Cloud.',\n        disableCNAME: 'Deshabilitar CNAME',\n        disableCNAMEHelper: 'Marca si el dominio tiene un CNAME y falla la solicitud.',\n        nameserver: 'Servidor DNS',\n        nameserverHelper: 'Usa un DNS personalizado para verificar dominios.',\n        edit: 'Editar certificado',\n        execShell: 'Ejecutar script tras solicitud de certificado.',\n        shell: 'Contenido del script',\n        shellHelper:\n            'Por defecto, el script se ejecuta en el directorio de instalación de 1Panel. Si se guarda el certificado, será en ese directorio. Timeout: 30 minutos.',\n        customAcme: 'Servicio ACME personalizado',\n        customAcmeURL: 'URL del servicio ACME',\n        baiduCloud: 'Baidu Cloud',\n        pushNode: 'Sincronizar con otros nodos',\n        pushNodeHelper: 'Enviar a los nodos seleccionados después de la aplicación/renovación',\n        fromMaster: 'Envío desde el nodo maestro',\n        hostedZoneID: 'Hosted Zone ID',\n        isIP: 'Certificado IP',\n        useEAB: 'Usar autenticación EAB',\n    },\n    firewall: {\n        create: 'Crear regla',\n        edit: 'Editar regla',\n        ccDeny: 'Protección CC',\n        ipWhiteList: 'Lista blanca de IP',\n        ipBlockList: 'Lista negra de IP',\n        fileExtBlockList: 'Lista negra de extensiones de archivo',\n        urlWhiteList: 'Lista blanca de URL',\n        urlBlockList: 'Lista negra de URL',\n        argsCheck: 'Verificación de parámetros GET',\n        postCheck: 'Verificación de parámetros POST',\n        cookieBlockList: 'Lista negra de cookies',\n        dockerHelper:\n            'El firewall actual no puede deshabilitar el mapeo de puertos de contenedores. Las aplicaciones instaladas pueden ir a la página [Instaladas] para editar los parámetros de la aplicación y configurar reglas de liberación de puertos.',\n        iptablesHelper:\n            'Se detectó que el sistema está usando el firewall {0}. Para cambiar a iptables, ¡desinstálelo manualmente primero!',\n        quickJump: 'Acceso rápido',\n        used: 'En uso',\n        unUsed: 'No usado',\n        dockerRestart: 'Las operaciones del firewall requieren reiniciar el servicio de Docker',\n        firewallHelper: 'Firewall del sistema {0}',\n        firewallNotStart: 'El firewall del sistema no está habilitado actualmente. Actívalo primero.',\n        restartFirewallHelper: 'Esta operación reiniciará el firewall actual. ¿Deseas continuar?',\n        stopFirewallHelper: 'Esto hará que el servidor pierda protección de seguridad. ¿Deseas continuar?',\n        startFirewallHelper:\n            'Tras habilitar el firewall, la seguridad del servidor podrá protegerse mejor. ¿Deseas continuar?',\n        noPing: 'Deshabilitar ping',\n        enableBanPing: 'Bloquear Ping',\n        disableBanPing: 'Desbloquear Ping',\n        noPingTitle: 'Deshabilitar ping',\n        noPingHelper: 'Esto deshabilitará el ping, y el servidor no responderá con eco ICMP. ¿Deseas continuar?',\n        onPingHelper: 'Esto habilitará el ping, y los atacantes podrían descubrir tu servidor. ¿Deseas continuar?',\n        changeStrategy: 'Cambiar la estrategia de {0}',\n        changeStrategyIPHelper1:\n            'Cambiar la estrategia de dirección IP a [denegar]. Después de establecerla, se prohibirá el acceso al servidor. ¿Deseas continuar?',\n        changeStrategyIPHelper2:\n            'Cambiar la estrategia de dirección IP a [permitir]. Después de establecerla, se restaurará el acceso normal. ¿Deseas continuar?',\n        changeStrategyPortHelper1:\n            'Cambiar la política de puerto a [rechazar]. Después de establecerla, se denegará el acceso externo. ¿Deseas continuar?',\n        changeStrategyPortHelper2:\n            'Cambiar la política de puerto a [aceptar]. Después de establecerla, se restaurará el acceso normal. ¿Deseas continuar?',\n        stop: 'Detener',\n        portFormatError: 'Este campo debe ser un puerto válido.',\n        portHelper1: 'Puertos múltiples, ej. 8080 y 8081',\n        portHelper2: 'Rango de puertos, ej. 8080-8089',\n        changeStrategyHelper:\n            'Cambiar estrategia de {0} [{1}] a [{2}]. Después de configurarla, {0} tendrá acceso externo como {2}. ¿Deseas continuar?',\n        strategy: 'Estrategia',\n        accept: 'Aceptar',\n        drop: 'Rechazar',\n        anyWhere: 'Cualquiera',\n        address: 'IPs especificadas',\n        addressHelper: 'Admite dirección IP o segmento de red',\n        allow: 'Permitir',\n        deny: 'Denegar',\n        addressFormatError: 'Este campo debe ser una dirección IP válida.',\n        addressHelper1: 'Admite dirección IP o rango de IP. Ejemplo: \"172.16.10.11\" o \"172.16.10.0/24\".',\n        addressHelper2: 'Para múltiples direcciones IP, sepáralas con comas. Ejemplo: \"172.16.10.11, 172.16.0.0/24\".',\n        allIP: 'Todas las IP',\n        portRule: 'Regla | Reglas',\n        forwardRule: 'Regla de redirección de puertos | Reglas de redirección de puertos',\n        ipRule: 'Regla IP | Reglas IP',\n        userAgent: 'Filtro User-Agent',\n        sourcePort: 'Puerto de origen',\n        targetIP: 'IP de destino',\n        targetPort: 'Puerto de destino',\n        forwardHelper1: 'Si quieres reenviar al puerto local, la IP de destino debe ser \"127.0.0.1\".',\n        forwardHelper2: 'Deja en blanco la IP de destino para reenviar al puerto local.',\n        forwardPortHelper: 'Admite rangos de puertos, ej.: 8080-8089',\n        forwardInboundInterface: 'Interfaz de Red de Entrada para Reenvío',\n        exportHelper: 'A punto de exportar {0} reglas de firewall. ¿Continuar?',\n        importSuccess: 'Se importaron correctamente {0} reglas',\n        importPartialSuccess: 'Importación completada: {0} correctas, {1} fallidas',\n        ipv4Limit: 'La operación actual solo admite direcciones IPv4',\n        basicStatus: 'La cadena actual {0} no está vinculada, ¡vincule primero!',\n        baseIptables: 'Servicio iptables',\n        forwardIptables: 'Servicio de Reenvío de Puertos iptables',\n        advanceIptables: 'Servicio de Configuración Avanzada de iptables',\n        initMsg: 'A punto de inicializar {0}, ¿continuar?',\n        initHelper:\n            'Se detectó que {0} no está inicializado. ¡Haga clic en el botón de inicialización en la barra de estado superior para configurar!',\n        bindHelper:\n            'Vincular: las reglas de firewall solo surtirán efecto cuando el estado esté vinculado. ¿Confirmar?',\n        unbindHelper:\n            'Desvincular: al desvincular, todas las reglas de firewall agregadas se volverán inválidas. Proceda con precaución. ¿Confirmar?',\n        defaultStrategy: 'La política predeterminada para la cadena actual {0} es {1}',\n        defaultStrategy2:\n            'La política predeterminada para la cadena actual {0} es {1}, el estado actual es no vinculado. ¡Las reglas de firewall agregadas surtirán efecto después de la vinculación!',\n        filterRule: 'Regla de Filtro',\n        filterHelper:\n            'Las reglas de filtro le permiten controlar el tráfico de red a nivel INPUT/OUTPUT. Configure con cuidado para evitar bloquear el sistema.',\n        chain: 'Cadena',\n        targetChain: 'Cadena de Destino',\n        sourceIP: 'IP de Origen',\n        destIP: 'IP de Destino',\n        inboundDirection: 'Dirección de Entrada',\n        outboundDirection: 'Dirección de Salida',\n        destPort: 'Puerto de Destino',\n        action: 'Acción',\n        reject: 'Rechazar',\n        sourceIPHelper: 'Formato CIDR, ej. 192.168.1.0/24. Dejar vacío para todas las direcciones',\n        destIPHelper: 'Formato CIDR, ej. 10.0.0.0/8. Dejar vacío para todas las direcciones',\n        portHelper: '0 significa cualquier puerto',\n        allPorts: 'Todos los Puertos',\n        deleteRuleConfirm: 'Se eliminarán {0} reglas. ¿Continuar?',\n        advancedControl: 'Control avanzado',\n        advancedControlNotAvailable: 'Actualmente se usa el firewall {0}; las reglas avanzadas solo admiten iptables',\n        destination: 'Destino',\n    },\n    runtime: {\n        runtime: 'Runtime',\n        workDir: 'Directorio de trabajo',\n        localHelper: 'Para problemas de instalación en entorno local y uso en entorno sin conexión, consulte ',\n        versionHelper: 'Versión de PHP, ej. v8.0',\n        buildHelper:\n            'Si se seleccionan más extensiones, el uso de CPU será mayor durante el proceso de creación de la imagen. Evita seleccionar todas las extensiones.',\n        openrestyWarn: 'PHP necesita actualizarse a OpenResty versión 1.21.4.1 o superior para poder usarse',\n        toupgrade: 'Actualizar',\n        edit: 'Editar runtime',\n        extendHelper:\n            'Si las extensiones que necesitas no están en la lista, puedes introducir el nombre manualmente. Ejemplo: \"sockets\", luego selecciona la primera.',\n        rebuildHelper: 'Tras editar la extensión, debes reconstruir la aplicación PHP para que surta efecto',\n        rebuild: 'Reconstruir aplicación PHP',\n        source: 'Origen de extensiones PHP',\n        ustc: 'Universidad de Ciencia y Tecnología de China',\n        netease: 'Netease',\n        aliyun: 'Alibaba Cloud',\n        tsinghua: 'Universidad de Tsinghua',\n        xtomhk: 'Espejo XTOM (Hong Kong)',\n        xtom: 'Espejo XTOM (Global)',\n        phpsourceHelper: 'Elige una fuente adecuada según tu entorno de red.',\n        appPort: 'Puerto de la aplicación',\n        externalPort: 'Puerto externo',\n        packageManager: 'Gestor de paquetes',\n        codeDir: 'Directorio del código',\n        appPortHelper: 'El puerto usado por la aplicación.',\n        externalPortHelper: 'El puerto expuesto al exterior.',\n        runScript: 'Script de ejecución',\n        runScriptHelper:\n            'La lista de comandos de inicio se obtiene desde el archivo package.json en el directorio fuente.',\n        open: 'Abrir',\n        operatorHelper: 'La operación {0} se ejecutará sobre el entorno de ejecución seleccionado. ¿Deseas continuar?',\n        taobao: 'Taobao',\n        tencent: 'Tencent',\n        imageSource: 'Fuente de imagen',\n        moduleManager: 'Gestión de módulos',\n        module: 'Módulo',\n        nodeOperatorHelper:\n            '¿Quieres {0} el módulo {1}? La operación puede causar fallos en el entorno de ejecución, confirma antes de continuar',\n        customScript: 'Comando de inicio personalizado',\n        customScriptHelper:\n            'Por favor, ingresa el comando de inicio completo, por ejemplo: npm run start. Para comandos de inicio de PM2, por favor reemplaza con pm2-runtime, de lo contrario fallará al iniciar.',\n        portError: 'No repitas el mismo puerto.',\n        systemRestartHelper:\n            'Descripción de estado: Interrupción - fallo al obtener estado debido a reinicio del sistema',\n        javaScriptHelper:\n            'Proporciona un comando de inicio completo. Ejemplo: \"java -jar halo.jar -Xmx1024M -Xms256M\".',\n        javaDirHelper: 'El directorio debe contener archivos .jar, se permiten subdirectorios',\n        goHelper: 'Proporciona un comando de inicio completo. Ejemplo: \"go run main.go\" o \"./main\".',\n        goDirHelper: 'El directorio o subdirectorio debe contener archivos Go o binarios.',\n        extension: 'Extensión',\n        installExtension: '¿Confirmas instalar la extensión {0}?',\n        loadedExtension: 'Extensión cargada',\n        popularExtension: 'Extensión popular',\n        uninstallExtension: '¿Seguro que quieres desinstalar la extensión {0}?',\n        phpConfigHelper: 'Modificar la configuración requiere reiniciar el entorno de ejecución, ¿quieres continuar?',\n        operateMode: 'Modo de operación',\n        dynamic: 'Dinámico',\n        static: 'Estático',\n        ondemand: 'Bajo demanda',\n        dynamicHelper:\n            'Ajusta dinámicamente el número de procesos, alta flexibilidad, adecuado para sitios con grandes fluctuaciones de tráfico o poca memoria',\n        staticHelper:\n            'Número fijo de procesos, adecuado para sitios con alta concurrencia y tráfico estable, alto consumo de recursos',\n        ondemandHelper:\n            'Los procesos se inician y destruyen bajo demanda, la utilización de recursos es óptima, pero la respuesta inicial puede ser lenta',\n        max_children: 'Número máximo de procesos permitidos',\n        start_servers: 'Número de procesos creados al inicio',\n        min_spare_servers: 'Número mínimo de procesos inactivos',\n        max_spare_servers: 'Número máximo de procesos inactivos',\n        envKey: 'Nombre',\n        envValue: 'Valor',\n        environment: 'Variable de entorno',\n        pythonHelper:\n            'Proporciona un comando de inicio completo. Ejemplo: \"pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000\".',\n        dotnetHelper: 'Proporciona un comando de inicio completo. Ejemplo: \"dotnet MyWebApp.dll\".',\n        dirHelper: 'Nota: rellena la ruta del directorio dentro del contenedor',\n        concurrency: 'Esquema de concurrencia',\n        loadStatus: 'Estado de carga',\n        extraHosts: 'Mapeo de host',\n    },\n    process: {\n        pid: 'ID de proceso',\n        ppid: 'ID de proceso padre',\n        numThreads: 'Hilos',\n        memory: 'Memoria',\n        diskRead: 'Lectura de disco',\n        diskWrite: 'Escritura en disco',\n        netSent: 'Tráfico de salida',\n        netRecv: 'Tráfico de entrada',\n        numConnections: 'Conexiones',\n        startTime: 'Hora de inicio',\n        running: 'En ejecución',\n        sleep: 'En espera',\n        stop: 'Detenido',\n        idle: 'Inactivo',\n        zombie: 'Proceso zombi',\n        wait: 'Esperando',\n        lock: 'Bloqueo',\n        blocked: 'Bloqueado',\n        cmdLine: 'Comando de inicio',\n        basic: 'Básico',\n        mem: 'Memoria',\n        openFiles: 'Archivos abiertos',\n        env: 'Entorno',\n        noenv: 'Ninguno',\n        net: 'Conexiones de red',\n        laddr: 'Dirección/puerto local',\n        raddr: 'Dirección/puerto remoto',\n        stopProcess: 'Finalizar',\n        viewDetails: 'Ver detalles',\n        stopProcessWarn: '¿Seguro que deseas finalizar este proceso (PID:{0})?',\n        kill: 'Terminar Proceso',\n        killNow: 'Terminar Ahora',\n        killHelper: 'Terminar el proceso {0} puede hacer que algunos programas funcionen incorrectamente. ¿Continuar?',\n        processName: 'Nombre del proceso',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: 'No se pudo obtener el estado del proceso, verifica el estado del servicio Supervisor.',\n            notSupport:\n                'Servicio Supervisor no detectado, ve a la página de librería de scripts para instalarlo manualmente',\n            list: 'Procesos en segundo plano',\n            config: 'Configuración de Supervisor',\n            primaryConfig: 'Ubicación del archivo de configuración principal',\n            notSupportCtl:\n                'No se detectó supervisorctl, ve a la página de librería de scripts para instalarlo manualmente',\n            user: 'Usuario',\n            command: 'Comando',\n            dir: 'Directorio',\n            numprocs: 'Número de procesos',\n            initWarn:\n                'Esto modificará el valor de \"files\" en la sección \"[include]\" del archivo de configuración principal. El directorio de otros archivos de configuración será: \"{directorio de instalación de 1Panel}/1panel/tools/supervisord/supervisor.d/\".',\n            operatorHelper: 'La operación {1} se ejecutará en {0}, ¿quieres continuar?',\n            uptime: 'Tiempo en ejecución',\n            notStartWarn: 'Supervisor no está iniciado. Inícialo primero.',\n            serviceName: 'Nombre del servicio',\n            initHelper:\n                'El servicio Supervisor está detectado pero no inicializado. Haz clic en el botón de inicialización en la barra de estado superior para configurarlo.',\n            serviceNameHelper:\n                'Nombre del servicio Supervisor gestionado por systemctl, normalmente supervisor o supervisord',\n            restartHelper:\n                'Esto reiniciará el servicio después de la inicialización, lo que detendrá todos los procesos en segundo plano existentes.',\n            RUNNING: 'En ejecución',\n            STOPPED: 'Detenido',\n            STOPPING: 'Deteniéndose',\n            STARTING: 'Iniciándose',\n            FATAL: 'Fallo al iniciar',\n            BACKOFF: 'Excepción al iniciar',\n            ERROR: 'Error',\n            statusCode: 'Código de estado',\n            manage: 'Gestión',\n            autoRestart: 'Reinicio automático',\n            EXITED: 'Finalizado',\n            autoRestartHelper: 'Indica si el programa se reinicia automáticamente tras un fallo',\n            autoStart: 'Inicio automático',\n            autoStartHelper: 'Indica si el servicio se inicia automáticamente cuando arranca Supervisor',\n        },\n    },\n    disk: {\n        management: 'Gestión de discos',\n        partition: 'Partición',\n        unmount: 'Desmontar',\n        unmountHelper: '¿Quieres desmontar la partición {0}?',\n        mount: 'Montar',\n        partitionAlert:\n            'El particionado de disco requiere formatearlo, y los datos existentes se eliminarán. Guarda o realiza snapshots de tus datos previamente.',\n        mountPoint: 'Directorio de montaje',\n        systemDisk: 'Disco del sistema',\n        unpartitionedDisk: 'Disco sin particionar',\n        handlePartition: 'Particionar ahora',\n        filesystem: 'Sistema de archivos',\n        unmounted: 'No montado',\n        cannotOperate: 'No se puede operar',\n        systemDiskHelper: 'Aviso: El disco actual es el disco del sistema. No se puede operar sobre él.',\n        autoMount: 'Montaje automático',\n        model: 'Modelo del dispositivo',\n        diskType: 'Tipo de disco',\n        serial: 'Núm. de serie',\n        noFail: 'El fallo de montaje no afecta al inicio del sistema',\n    },\n    xpack: {\n        expiresTrialAlert:\n            'Aviso: Tu prueba de Pro expirará en {0} días y todas las funciones Pro dejarán de estar disponibles. Renueva o actualiza a la versión completa a tiempo.',\n        expiresAlert:\n            'Aviso: Tu licencia Pro expirará en {0} días y todas las funciones Pro dejarán de estar disponibles. Renueva pronto para asegurar el uso continuo.',\n        menu: 'Pro',\n        upage: 'Constructor Web con IA',\n        proAlert: 'Actualiza a Pro para usar esta función',\n        app: {\n            app: 'APP',\n            title: 'Alias del Panel',\n            titleHelper: 'El alias del panel se usa para mostrar en la APP (alias por defecto)',\n            qrCode: 'Código QR',\n            apiStatusHelper: 'La APP del Panel necesita que la API esté habilitada',\n            apiInterfaceHelper: 'Permite acceso a la API del panel (debe estar habilitada para la APP)',\n            apiInterfaceHelper1:\n                'El acceso requiere añadir el visitante a la lista blanca; para IPs dinámicas se recomienda usar 0.0.0.0/0 (IPv4) o ::/0 (IPv6)',\n            qrCodeExpired: 'Tiempo de refresco',\n            apiLeakageHelper: 'No divulgues el código QR. Úsalo solo en entornos de confianza.',\n        },\n        waf: {\n            name: 'WAF',\n            blackWhite: 'Lista Blanca y Negra',\n            globalSetting: 'Configuración Global',\n            websiteSetting: 'Configuración del Sitio',\n            blockRecords: 'Registros Bloqueados',\n            world: 'Mundial',\n            china: 'China',\n            intercept: 'Intercepciones',\n            request: 'Peticiones',\n            count4xx: 'Cantidad 4xx',\n            count5xx: 'Cantidad 5xx',\n            todayStatus: 'Estado de hoy',\n            reqMap: 'Mapa de ataques (últimos 30 días)',\n            resource: 'Fuente',\n            count: 'Cantidad',\n            hight: 'Alto',\n            low: 'Bajo',\n            reqCount: 'Solicitudes',\n            interceptCount: 'Número de intercepciones',\n            requestTrends: 'Tendencia de solicitudes (últimos 7 días)',\n            interceptTrends: 'Tendencia de intercepciones (últimos 7 días)',\n            whiteList: 'Whitelist',\n            blackList: 'Blacklist',\n            ipBlackListHelper: 'Las IP en la blacklist no pueden acceder al sitio web',\n            ipWhiteListHelper: 'Las IP en la whitelist saltan todas las restricciones',\n            uaBlackListHelper: 'Las peticiones con User-Agent en blacklist serán bloqueadas',\n            uaWhiteListHelper: 'Las peticiones con User-Agent en whitelist saltan todas las restricciones',\n            urlBlackListHelper: 'Las peticiones a URLs en blacklist serán bloqueadas',\n            urlWhiteListHelper: 'Las peticiones a URLs en whitelist saltan todas las restricciones',\n            ccHelper:\n                'Si un sitio recibe más de {1} peticiones de la misma IP en {0} segundos, la IP será bloqueada por {2}',\n            blockTime: 'Duración de bloqueo',\n            attackHelper: 'Si las intercepciones acumuladas superan {1} en {0} segundos, la IP se bloquea por {2}',\n            notFoundHelper: 'Si el número de errores 404 supera {1} en {0} segundos, la IP será bloqueada por {2}',\n            frequencyLimit: 'Límite de frecuencia',\n            regionLimit: 'Límite regional',\n            defaultRule: 'Reglas por defecto',\n            accessFrequencyLimit: 'Límite de frecuencia de acceso',\n            attackLimit: 'Límite de frecuencia de ataques',\n            notFoundLimit: 'Límite de 404',\n            urlLimit: 'Límite de frecuencia por URL',\n            urlLimitHelper: 'Configura el límite de acceso para una URL específica',\n            sqliDefense: 'Protección contra SQL Injection',\n            sqliHelper: 'Detecta SQL Injection en peticiones y las bloquea',\n            xssHelper: 'Detecta XSS en peticiones y las bloquea',\n            xssDefense: 'Protección contra XSS',\n            uaDefense: 'Reglas contra User-Agent malicioso',\n            uaHelper: 'Incluye reglas para bots maliciosos comunes',\n            argsDefense: 'Reglas contra parámetros maliciosos',\n            argsHelper: 'Bloquea peticiones con parámetros maliciosos',\n            cookieDefense: 'Reglas contra cookies maliciosas',\n            cookieHelper: 'Prohíbe cookies maliciosas en las peticiones',\n            headerDefense: 'Reglas contra cabeceras maliciosas',\n            headerHelper: 'Prohíbe cabeceras maliciosas en las peticiones',\n            httpRule: 'Reglas de métodos HTTP',\n            httpHelper:\n                'Define qué métodos HTTP están permitidos. Ejemplo: solo GET. Para restringir, desactiva los métodos no deseados',\n            geoRule: 'Restricciones geográficas',\n            geoHelper: 'Restringe accesos desde ciertas regiones. Ejemplo: permitir solo China, bloqueando el resto',\n            ipLocation: 'Ubicación IP',\n            action: 'Acción',\n            ruleType: 'Tipo de ataque',\n            ipHelper: 'Introduce la dirección IP',\n            attackLog: 'Log de ataques',\n            rule: 'Regla',\n            ipArr: 'Rango IPv4',\n            ipStart: 'IP inicio',\n            ipEnd: 'IP fin',\n            ipv4: 'IPv4',\n            ipv6: 'IPv6',\n            urlDefense: 'Reglas de URL',\n            urlHelper: 'URL prohibida',\n            dirFilter: 'Filtro de directorios',\n            sqlInject: 'SQL Injection',\n            xss: 'XSS',\n            phpExec: 'Ejecución de scripts PHP',\n            oneWordTrojan: 'Troyano de una línea',\n            appFilter: 'Filtrado de directorios peligrosos',\n            webShell: 'Webshell',\n            args: 'Parámetros maliciosos',\n            protocolFilter: 'Filtro de protocolos',\n            javaFilter: 'Filtro de ficheros Java peligrosos',\n            scannerFilter: 'Filtro de escáneres',\n            escapeFilter: 'Filtro de escapes',\n            customRule: 'Reglas personalizadas',\n            httpMethod: 'Filtro de métodos HTTP',\n            fileExt: 'Restricción de extensiones',\n            fileExtHelper: 'Extensiones prohibidas para subir',\n            deny: 'Prohibir',\n            allow: 'Permitir',\n            field: 'Objeto',\n            pattern: 'Condición',\n            ruleContent: 'Contenido',\n            contain: 'contiene',\n            equal: 'igual',\n            regex: 'expresión regular',\n            notEqual: 'distinto',\n            customRuleHelper: 'Acciones basadas en condiciones definidas',\n            actionAllow: 'Permitir',\n            blockIP: 'Bloquear IP',\n            code: 'Código de respuesta',\n            noRes: 'Desconectar (444)',\n            badReq: 'Parámetros inválidos (400)',\n            forbidden: 'Acceso prohibido (403)',\n            serverErr: 'Error de servidor (500)',\n            resHtml: 'Página de respuesta',\n            allowHelper: 'Permitir acceso salta las siguientes reglas del WAF, úsalo con precaución',\n            captcha: 'Verificación humano-máquina',\n            fiveSeconds: 'Verificación de 5 segundos',\n            location: 'Región',\n            redisConfig: 'Configuración Redis',\n            redisHelper: 'Habilita Redis para persistir IPs bloqueadas temporalmente',\n            wafHelper: 'Todos los sitios perderán protección al deshabilitar',\n            attackIP: 'IP atacante',\n            attackParam: 'Detalles del ataque',\n            execRule: 'Regla aplicada',\n            acl: 'ACL',\n            sql: 'SQL Injection',\n            cc: 'Límite de frecuencia de acceso',\n            isBlocking: 'Bloqueado',\n            isFree: 'Libre',\n            unLock: 'Desbloquear',\n            unLockHelper: '¿Quieres desbloquear la IP: {0}?',\n            saveDefault: 'Guardar por defecto',\n            saveToWebsite: 'Aplicar al sitio web',\n            saveToWebsiteHelper: '¿Aplicar esta configuración a todos los sitios?',\n            websiteHelper:\n                'Estas son configuraciones por defecto al crear sitios. Para aplicar, edítalo en el sitio específico',\n            websiteHelper2:\n                'Configuraciones por defecto para sitios nuevos, modifica en cada sitio para que tengan efecto',\n            ipGroup: 'Grupo de IPs',\n            ipGroupHelper: 'Una IP o rango por línea. Soporta IPv4 e IPv6. Ejemplo: 192.168.1.1 o 192.168.1.0/24',\n            ipBlack: 'IP blacklist',\n            openRestyAlert: 'Se requiere versión de OpenResty mayor a {0}',\n            initAlert:\n                'La primera vez es necesario inicializar. El archivo de configuración será modificado y la configuración previa se perderá. Haz copia de seguridad',\n            initHelper: 'La inicialización borrará la configuración existente del WAF. ¿Seguro que quieres continuar?',\n            mainSwitch: 'Interruptor principal',\n            websiteAlert: 'Crea un sitio primero',\n            defaultUrlBlack: 'Reglas de URL',\n            htmlRes: 'Página de intercepción',\n            urlSearchHelper: 'Introduce la URL (búsqueda difusa)',\n            toCreate: 'Crear',\n            closeWaf: 'Cerrar WAF',\n            closeWafHelper: 'Cerrar WAF desprotege el sitio. ¿Continuar?',\n            addblack: 'Añadir a blacklist',\n            addwhite: 'Añadir a whitelist',\n            addblackHelper: '¿Añadir IP:{0} a la blacklist?',\n            addwhiteHelper: '¿Añadir IP:{0} a la whitelist?',\n            defaultUaBlack: 'Regla de User-Agent',\n            defaultIpBlack: 'Grupo de IPs maliciosas',\n            cookie: 'Reglas de cookies',\n            urlBlack: 'Blacklist de URL',\n            uaBlack: 'Blacklist de User-Agent',\n            attackCount: 'Límite de frecuencia de ataques',\n            fileExtCheck: 'Restricción de subida',\n            geoRestrict: 'Restricción geográfica',\n            attacklog: 'Registro de intercepciones',\n            unknownWebsite: 'Acceso a dominio no autorizado',\n            geoRuleEmpty: 'La región no puede estar vacía',\n            unknown: 'Sitio no existe',\n            geo: 'Restricción por región',\n            revertHtml: '¿Restaurar {0} como página por defecto?',\n            five_seconds: 'Verificación de 5 segundos',\n            header: 'Reglas de cabeceras',\n            methodWhite: 'Reglas HTTP',\n            expiryDate: 'Fecha de expiración',\n            expiryDateHelper: 'Tras validación exitosa, no se verificará de nuevo durante el periodo de validez',\n            defaultIpBlackHelper: 'IPs maliciosas recopiladas de internet para bloquear accesos',\n            notFoundCount: 'Límite de errores 404',\n            matchValue: 'Valor de coincidencia',\n            headerName: 'Debe iniciar con letra o número, admite \"-\", longitud 3-30',\n            cdnHelper: 'Para webs con CDN, obtener IP real aquí',\n            clearLogWarn: 'No podrás recuperar los logs borrados. ¿Continuar?',\n            commonRuleHelper: 'Las reglas usan coincidencia difusa',\n            blockIPHelper:\n                'Las IP bloqueadas se guardan temporalmente en OpenResty y se liberan tras reinicio. Para bloqueo permanente usa reglas',\n            addWhiteUrlHelper: '¿Añadir URL {0} a la whitelist?',\n            dashHelper: 'La versión community también soporta configuraciones globales y de sitio',\n            wafStatusHelper: 'WAF no está activo, habilítalo en Configuración Global',\n            ccMode: 'Modo',\n            global: 'Modo global',\n            uriMode: 'Modo URL',\n            globalHelper: 'Modo global: cuando el total de peticiones a cualquier URL supera el límite en el periodo',\n            uriModeHelper: 'Modo URL: cuando las peticiones a una URL superan el límite en el periodo',\n            ip: 'Blacklist de IPs',\n            globalSettingHelper:\n                'Las configuraciones con etiqueta [Website] requieren activarse también en la Configuración del Sitio',\n            globalSettingHelper2: 'Debe habilitarse en [Configuración Global] y [Configuración del Sitio] a la vez',\n            urlCCHelper: 'Más de {1} peticiones a esta URL en {0} segundos bloquean la IP por {2}',\n            urlCCHelper2: 'La URL no puede contener parámetros',\n            notContain: 'No contiene',\n            urlcc: 'Límite de frecuencia por URL',\n            method: 'Método de petición',\n            addIpsToBlock: 'Bloqueo masivo de IPs',\n            addUrlsToWhite: 'Añadir URLs en whitelist en lote',\n            noBlackIp: 'IP ya bloqueada',\n            noWhiteUrl: 'URL ya en whitelist',\n            spiderIpHelper:\n                'Incluye Baidu, Bing, Google, 360, Shenma, Sogou, ByteDance, DuckDuckGo, Yandex. Al cerrar se bloquea todo acceso de crawlers',\n            spiderIp: 'Pool de IPs de crawlers',\n            geoIp: 'Base de datos IP',\n            geoIpHelper: 'Usada para geolocalizar IPs',\n            stat: 'Reporte de ataques',\n            statTitle: 'Reporte',\n            attackIp: 'IP',\n            attackCountNum: 'Cantidad',\n            percent: 'Porcentaje',\n            addblackUrlHelper: '¿Añadir URL {0} a la blacklist?',\n            rce: 'Remote Code Execution',\n            software: 'Software',\n            cveHelper: 'Incluye vulnerabilidades de software y frameworks comunes',\n            vulnCheck: 'Reglas adicionales',\n            ssrf: 'Vulnerabilidad SSRF',\n            afr: 'Lectura arbitraria de ficheros',\n            ua: 'Acceso no autorizado',\n            id: 'Divulgación de información',\n            aa: 'Bypass de autenticación',\n            dr: 'Directory Traversal',\n            xxe: 'Vulnerabilidad XXE',\n            suid: 'Vulnerabilidad de serialización',\n            dos: 'Denegación de servicio',\n            afd: 'Descarga arbitraria de ficheros',\n            sqlInjection: 'SQL Injection',\n            afw: 'Escritura arbitraria de ficheros',\n            il: 'Fuga de información',\n            clearAllLog: 'Borrar todos los logs',\n            exportLog: 'Exportar logs',\n            appRule: 'Reglas de aplicación',\n            appRuleHelper: 'Reglas de apps comunes, reduce falsos positivos. Un sitio solo puede usar una regla',\n            logExternal: 'Excluir tipos de registros',\n            ipWhite: 'Whitelist de IPs',\n            urlWhite: 'Whitelist de URLs',\n            uaWhite: 'Whitelist de User-Agent',\n            logExternalHelper:\n                'Tipos de registro excluidos no se guardan. Whitelist/blacklist, restricciones regionales y reglas generan muchos logs, se recomienda excluir',\n            ssti: 'Ataque SSTI',\n            crlf: 'Inyección CRLF',\n            strict: 'Modo estricto',\n            strictHelper: 'Usa reglas más estrictas para validar peticiones',\n            saveLog: 'Guardar log',\n            remoteURLHelper: 'La URL remota debe tener solo una IP por línea y sin otros caracteres',\n            notFound: 'No encontrado (404)',\n            serviceUnavailable: 'Servicio no disponible (503)',\n            gatewayTimeout: 'Tiempo de espera de gateway (504)',\n            belongToIpGroup: 'Pertenece a grupo de IPs',\n            notBelongToIpGroup: 'No pertenece a grupo de IPs',\n            unknownWebsiteKey: 'Dominio desconocido',\n            special: 'Especial',\n            fileToLarge: 'Archivo supera 1MB y no puede subirse',\n            uploadOverLimit: 'El archivo supera el límite, máximo 1',\n            importRuleHelper: 'Una regla por línea',\n        },\n        monitor: {\n            '360': '360 Search',\n            name: 'Monitorización Web',\n            pv: 'Page Views (PV)',\n            uv: 'Unique Visitors (UV)',\n            flow: 'Tráfico',\n            ip: 'IP',\n            spider: 'Crawler',\n            visitors: 'Tendencia de visitantes',\n            today: 'Hoy',\n            last7days: 'Últimos 7 días',\n            last30days: 'Últimos 30 días',\n            uvMap: 'Mapa de visitantes (30 días)',\n            qps: 'Solicitudes en tiempo real (por minuto)',\n            flowSec: 'Tráfico en tiempo real (por minuto)',\n            excludeCode: 'Excluir códigos de estado',\n            excludeUrl: 'Excluir URLs',\n            excludeExt: 'Excluir extensiones',\n            cdnHelper: 'Obtiene la IP real desde la cabecera del CDN',\n            reqRank: 'Ranking de visitas',\n            refererDomain: 'Dominio referente',\n            os: 'Sistema operativo',\n            browser: 'Navegador/Cliente',\n            device: 'Dispositivo',\n            showMore: 'Más',\n            unknown: 'Otro',\n            pc: 'PC',\n            mobile: 'Móvil',\n            wechat: 'WeChat',\n            machine: 'Máquina',\n            tencent: 'Navegador Tencent',\n            ucweb: 'UC Browser',\n            '2345explorer': '2345 Browser',\n            huaweibrowser: 'Navegador Huawei',\n            log: 'Logs de solicitudes',\n            statusCode: 'Código de estado',\n            requestTime: 'Tiempo de respuesta',\n            flowRes: 'Tráfico de respuesta',\n            method: 'Método HTTP',\n            statusCodeHelper: 'Introduce el código de estado',\n            statusCodeError: 'Código de estado inválido',\n            methodHelper: 'Introduce el método HTTP',\n            all: 'Todos',\n            baidu: 'Baidu',\n            google: 'Google',\n            bing: 'Bing',\n            bytes: 'Bytedance',\n            sogou: 'Sogou',\n            failed: 'Error',\n            ipCount: 'Número de IPs',\n            spiderCount: 'Solicitudes de crawlers',\n            averageReqTime: 'Tiempo de respuesta medio',\n            totalFlow: 'Tráfico total',\n            logSize: 'Tamaño de logs',\n            realIPType: 'Método de obtención de IP real',\n            fromHeader: 'Desde cabecera HTTP',\n            fromHeaders: 'Desde lista de cabeceras',\n            header: 'Cabecera HTTP',\n            cdnConfig: 'Configuración CDN',\n            xff1: 'Proxy nivel 1 (X-Forwarded-For)',\n            xff2: 'Proxy nivel 2 (X-Forwarded-For)',\n            xff3: 'Proxy nivel 3 (X-Forwarded-For)',\n            xffHelper: 'Ejemplo: X-Forwarded-For: <cliente>,<proxy1>,<proxy2>,<proxy3>. El último proxy es <proxy3>',\n            headersHelper: 'Obtiene la IP real de cabeceras comunes de CDN, tomando la primera disponible',\n            monitorCDNHelper: 'Modificar la config de CDN en monitorización también actualiza la del WAF',\n            wafCDNHelper: 'Modificar la config de CDN en el WAF también actualiza la monitorización',\n            statusErr: 'Formato de código de estado inválido',\n            shenma: 'Shenma',\n            duckduckgo: 'DuckDuckGo',\n            excludeUri: 'Excluir URIs',\n            top100Helper: 'Mostrar top 100',\n            logSaveDay: 'Retención de logs (días)',\n            cros: 'Chrome OS',\n            theworld: 'TheWorld Browser',\n            edge: 'Microsoft Edge',\n            maxthon: 'Maxthon Browser',\n            monitorStatusHelper: 'Monitorización no habilitada, actívala en Configuración',\n            excludeIp: 'Excluir IPs',\n            excludeUa: 'Excluir User-Agent',\n            remotePort: 'Puerto remoto',\n            unknown_browser: 'Navegador desconocido',\n            unknown_os: 'SO desconocido',\n            unknown_device: 'Dispositivo desconocido',\n            logSaveSize: 'Tamaño máximo de logs',\n            logSaveSizeHelper: 'Tamaño máximo de log por sitio',\n            '360se': '360 Secure Browser',\n            websites: 'Lista de sitios',\n            trend: 'Estadísticas de tendencias',\n            reqCount: 'Número de solicitudes',\n            uriHelper: 'Puedes usar /test/* o /*/index.php para excluir URIs',\n        },\n        tamper: {\n            tamper: 'Protección contra manipulación web',\n            ignoreTemplate: 'Plantilla de Exclusión',\n            protectTemplate: 'Plantilla de Protección',\n            ignoreTemplateHelper:\n                'Ingrese contenido de exclusión, separado por Enter o espacio. (Directorio específico ./log o nombre de directorio tmp, para excluir archivos necesita ingresar archivo específico ./data/test.html)',\n            protectTemplateHelper:\n                'Ingrese contenido de protección, separado por Enter o espacio. (Archivo específico ./index.html, extensión de archivo .html, tipo de archivo js, para proteger directorios necesita ingresar directorio específico ./log)',\n            templateContent: 'Contenido de Plantilla',\n            template: 'Plantilla',\n            saveTemplate: 'Guardar como Plantilla',\n            tamperHelper1:\n                'Para sitios web de implementación con un clic, se recomienda habilitar la protección contra manipulaciones del directorio de aplicaciones; si el sitio web no se puede usar normalmente o falla la copia de seguridad/restauración, desactive primero la protección contra manipulaciones;',\n            tamperHelper2:\n                'Restringirá las operaciones de lectura/escritura, eliminación, permisos y modificación de propietario para archivos protegidos en directorios no excluidos',\n            tamperPath: 'Directorio de Protección',\n            tamperPathEdit: 'Modificar Ruta',\n            log: 'Registro de Bloqueo',\n            totalProtect: 'Protección Total',\n            todayProtect: 'Protección de Hoy',\n            templateRule: 'Longitud 1-512, el nombre no puede contener {0} y otros símbolos',\n            ignore: 'Excluir',\n            ignoreHelper:\n                'Seleccione o ingrese contenido de exclusión, separado por Enter o espacio. (Directorio específico ./log o nombre de directorio tmp, para excluir archivos necesita ingresar o seleccionar archivo específico ./data/test.html)',\n            protect: 'Proteger',\n            protectHelper:\n                'Seleccione o ingrese contenido de protección, separado por Enter o espacio. (Archivo específico ./index.html, extensión de archivo .html, tipo de archivo js, para proteger directorios necesita ingresar o seleccionar directorio específico ./log)',\n            tamperHelper00: 'La exclusión y protección solo admiten rutas relativas;',\n            tamperHelper01:\n                'Después de habilitar la protección contra manipulaciones, el sistema restringirá las operaciones de creación, edición y eliminación de archivos protegidos en directorios no excluidos;',\n            tamperHelper02:\n                'Prioridad: Protección de ruta específica > Exclusión de ruta específica > Protección > Exclusión',\n            tamperHelper03:\n                'Las operaciones de monitoreo solo se dirigen a directorios no excluidos, monitoreando la creación de archivos no protegidos en estos directorios.',\n            disableHelper:\n                'A punto de desactivar la protección contra manipulaciones para los siguientes sitios web, ¿continuar?',\n            appendOnly: 'Solo añadir',\n            appendOnlyHelper:\n                'Restringe el borrado en este directorio, solo se permite añadir directorios excluidos o archivos no protegidos',\n            immutable: 'Inmutable',\n            immutableHelper: 'Restringe edición, borrado, permisos y cambio de propietario del archivo',\n            onWatch: 'Monitorización',\n            onWatchHelper:\n                'Monitoriza y bloquea la creación de archivos protegidos o directorios no excluidos en este directorio',\n            forceStop: 'Forzar cierre',\n            forceStopHelper: 'Se forzará la desactivación de la función anti-manipulación en este sitio. ¿Continuar?',\n        },\n        setting: {\n            setting: 'Configuración del panel',\n            title: 'Descripción del panel',\n            titleHelper:\n                'Se mostrará en la página de login (ej.: Panel de administración de servidores Linux, recomendado 8-15 caracteres)',\n            logo: 'Logo (sin texto)',\n            logoHelper:\n                'Se mostrará arriba a la izquierda cuando el menú esté colapsado (tamaño recomendado: 82px*82px)',\n            logoWithText: 'Logo (con texto)',\n            logoWithTextHelper:\n                'Se mostrará arriba a la izquierda cuando el menú esté expandido (tamaño recomendado: 185px*55px)',\n            favicon: 'Icono del sitio',\n            faviconHelper: 'Icono del sitio (tamaño recomendado: 16px*16px)',\n            setDefault: 'Restaurar por defecto',\n            setHelper: 'Se guardará la configuración actual. ¿Continuar?',\n            setDefaultHelper: 'Todas las configuraciones se restaurarán a valores por defecto. ¿Continuar?',\n            logoGroup: 'Logo',\n            imageGroup: 'Imagen',\n            loginImage: 'Imagen de login',\n            loginImageHelper: 'Se muestra en la página de login (tamaño recomendado: 500*416px)',\n            loginBgType: 'Tipo de fondo de login',\n            loginBgImage: 'Imagen de fondo de login',\n            loginBgImageHelper: 'Se muestra como fondo de la página de login (tamaño recomendado: 1920*1080px)',\n            loginBgColor: 'Color de fondo de login',\n            loginBgColorHelper: 'Se muestra como color de fondo en la página de login',\n            image: 'Imagen',\n            bgColor: 'Color de fondo',\n            loginGroup: 'Página de login',\n            loginBtnLinkColor: 'Color de botones/enlaces',\n            loginBtnLinkColorHelper: 'Se aplica a botones y enlaces en la página de login',\n        },\n        helper: {\n            wafTitle1: 'Mapa de Intercepciones',\n            wafContent1: 'Muestra la distribución geográfica de intercepciones en los últimos 30 días',\n            wafTitle2: 'Restricciones de Acceso Regional',\n            wafContent2: 'Restringe las fuentes de acceso al sitio web según la ubicación geográfica',\n            wafTitle3: 'Página de Intercepción Personalizada',\n            wafContent3: 'Crea una página personalizada que se muestre tras interceptar una petición',\n            wafTitle4: 'Reglas Personalizadas (ACL)',\n            wafContent4: 'Intercepta peticiones según reglas definidas por el usuario',\n            tamperTitle1: 'Monitoreo de Integridad de Archivos',\n            tamperContent1:\n                'Supervisa la integridad de los archivos del sitio, incluyendo archivos core, scripts y de configuración.',\n            tamperTitle2: 'Escaneo y Detección en Tiempo Real',\n            tamperContent2:\n                'Detecta archivos anómalos o manipulados escaneando en tiempo real el sistema de archivos del sitio.',\n            tamperTitle3: 'Configuración de Permisos de Seguridad',\n            tamperContent3:\n                'Restringe el acceso a archivos del sitio mediante permisos adecuados y políticas de control de acceso, reduciendo la superficie de ataque.',\n            tamperTitle4: 'Registro y Análisis',\n            tamperContent4:\n                'Registra accesos y operaciones sobre archivos para auditoría y análisis posteriores, ayudando a detectar amenazas de seguridad.',\n            settingTitle1: 'Mensaje de Bienvenida Personalizado',\n            settingContent1: 'Configura un mensaje de bienvenida en la página de login de 1Panel.',\n            settingTitle2: 'Logo Personalizado',\n            settingContent2: 'Permite subir logos con nombres de marca u otro texto.',\n            settingTitle3: 'Icono del Sitio Web',\n            settingContent3:\n                'Permite subir un icono personalizado para reemplazar el favicon por defecto, mejorando la experiencia del usuario.',\n            monitorTitle1: 'Tendencia de Visitantes',\n            monitorContent1: 'Genera estadísticas y muestra la tendencia de visitantes del sitio web',\n            monitorTitle2: 'Mapa de Visitantes',\n            monitorContent2: 'Muestra la distribución geográfica de los visitantes del sitio',\n            monitorTitle3: 'Estadísticas de Acceso',\n            monitorContent3:\n                'Genera estadísticas de solicitudes al sitio web, incluyendo crawlers, dispositivos de acceso, estados de respuesta, etc.',\n            monitorTitle4: 'Monitoreo en Tiempo Real',\n            monitorContent4:\n                'Supervisa en tiempo real la información de peticiones al sitio web, incluyendo número de solicitudes y tráfico.',\n            alertTitle1: 'Alertas por SMS',\n            alertContent1:\n                'Cuando se detecta uso anómalo de recursos del servidor, expiración de sitios o certificados, nueva versión disponible o expiración de contraseñas, los usuarios son notificados por SMS para garantizar respuesta oportuna.',\n            alertTitle2: 'Registro de Alertas',\n            alertContent2:\n                'Permite a los usuarios consultar el historial de alertas para facilitar el seguimiento y análisis de eventos pasados.',\n            alertTitle3: 'Configuración de Alertas',\n            alertContent3:\n                'Permite configurar números de teléfono, frecuencia y horarios de notificación para alertas más personalizadas.',\n            nodeDashTitle1: 'Gestión de Aplicaciones',\n            nodeDashContent1:\n                'Gestión unificada de aplicaciones multi-nodo, admite monitoreo de estado, inicio/parada rápida, conexión de terminal y copia de seguridad',\n            nodeDashTitle2: 'Gestión de Sitios Web',\n            nodeDashContent2:\n                'Gestión unificada de sitios web multi-nodo, monitoreo de estado en tiempo real, admite inicio/parada por lotes y copia de seguridad rápida',\n            nodeDashTitle3: 'Gestión de Bases de Datos',\n            nodeDashContent3:\n                'Gestión unificada de bases de datos multi-nodo, estado clave de un vistazo, admite copia de seguridad con un clic',\n            nodeDashTitle4: 'Gestión de Tareas Programadas',\n            nodeDashContent4:\n                'Gestión unificada de tareas programadas multi-nodo, admite monitoreo de estado, inicio/parada rápida y ejecución manual por activación',\n            nodeTitle1: 'Adición de Nodo en un Clic',\n            nodeContent1: 'Integra rápidamente múltiples nodos de servidor',\n            nodeTitle2: 'Actualización en Lote',\n            nodeContent2: 'Sincroniza y actualiza todos los nodos con una sola operación',\n            nodeTitle3: 'Monitoreo del Estado de Nodos',\n            nodeContent3: 'Supervisa en tiempo real el estado operativo de cada nodo',\n            nodeTitle4: 'Conexión Remota Rápida',\n            nodeContent4: 'Conecta directamente a la terminal remota de un nodo con un clic',\n            fileExchangeTitle1: 'Transmisión con Autenticación por Claves',\n            fileExchangeContent1: 'Autenticación vía claves SSH para garantizar seguridad en la transmisión.',\n            fileExchangeTitle2: 'Sincronización de Archivos Eficiente',\n            fileExchangeContent2:\n                'Solo se sincroniza el contenido modificado, mejorando velocidad y estabilidad de transmisión.',\n            fileExchangeTitle3: 'Intercomunicación Multi-Nodo',\n            fileExchangeContent3:\n                'Permite transferir fácilmente archivos entre diferentes nodos y gestionar múltiples servidores de forma flexible.',\n            nodeAppTitle1: 'Gestión de Actualización de Aplicaciones',\n            nodeAppContent1:\n                'Monitoreo unificado de actualizaciones de aplicaciones multi-nodo, admite actualización con un clic',\n            appTitle1: 'Gestión Flexible del Panel',\n            appContent1: 'Gestiona tu servidor 1Panel en cualquier momento y lugar.',\n            appTitle2: 'Información Completa de Servicios',\n            appContent2:\n                'Administra aplicaciones básicas, sitios web, Docker, bases de datos, etc., y crea nuevos servicios desde la app móvil.',\n            appTitle3: 'Monitoreo de Anomalías en Tiempo Real',\n            appContent3:\n                'Consulta en la app móvil el estado del servidor, la seguridad WAF, estadísticas de tráfico web y la salud de procesos.',\n            clusterTitle1: 'Despliegue Maestro-Esclavo',\n            clusterContent1:\n                'Soporta creación de instancias maestro-esclavo de MySQL/Postgres/Redis en distintos nodos, completando la asociación e inicialización automáticamente',\n            clusterTitle2: 'Gestión Maestro-Esclavo',\n            clusterContent2:\n                'Página centralizada para gestionar múltiples nodos maestro-esclavo, ver sus roles y estado de ejecución.',\n            clusterTitle3: 'Estado de Replicación',\n            clusterContent3:\n                'Muestra estado de replicación maestro-esclavo y retrasos, ayudando a diagnosticar problemas de sincronización',\n        },\n        node: {\n            master: 'Nodo Principal',\n            masterBackup: 'Respaldo del Nodo Principal',\n            backupNode: 'Nodo de Respaldo',\n            backupFrequency: 'Frecuencia de Respaldo (horas)',\n            backupCopies: 'Copias de Respaldo',\n            noBackupNode: 'Actualmente no hay nodo de respaldo configurado. Selecciona uno y vuelve a intentarlo.',\n            masterBackupAlert:\n                'No se ha configurado un respaldo del nodo principal. Para garantizar la seguridad de los datos, configura un nodo de respaldo lo antes posible y así facilitar el cambio manual en caso de fallo.',\n            node: 'Nodo',\n            addr: 'Dirección',\n            nodeUpgrade: 'Configuración de Actualización',\n            nodeUpgradeHelper:\n                'Los nodos seleccionados comenzarán a actualizarse automáticamente después de que se complete la actualización del nodo maestro, sin necesidad de operación manual.',\n            nodeUnhealthy: 'Estado del nodo anómalo',\n            deletedNode: 'El nodo eliminado {0} no soporta operaciones de actualización.',\n            nodeUnhealthyHelper:\n                'Se ha detectado un estado anómalo en el nodo. Revisa [Gestión de Nodos] e inténtalo de nuevo.',\n            nodeUnbind: 'Nodo no vinculado a licencia',\n            nodeUnbindHelper:\n                'Se ha detectado que este nodo no está vinculado a una licencia. Vincúlalo desde [Ajustes del Panel - Licencia] e inténtalo de nuevo.',\n            memTotal: 'Memoria Total',\n            nodeManagement: 'Gestión Multi-Máquina',\n            nodeItem: 'Gestión de Nodos',\n            panelItem: 'Gestión de Paneles',\n            addNode: 'Añadir Nodo',\n            connInfo: 'Información de Conexión',\n            nodeInfo: 'Información del Nodo',\n            withProxy: 'Habilitar Acceso por Proxy',\n            withoutProxy: 'Deshabilitar Acceso por Proxy',\n            withProxyHelper:\n                'Utilizará el proxy del sistema {0} mantenido en la configuración del panel para acceder a los nodos secundarios. ¿Continuar?',\n            withoutProxyHelper:\n                'Dejará de usar el proxy del sistema mantenido en la configuración del panel para acceder a los nodos secundarios. ¿Continuar?',\n            syncInfo: 'Sincronización',\n            syncHelper:\n                'Cuando cambian los datos en el nodo principal, se sincronizan en tiempo real con este nodo secundario.',\n            syncBackupAccount: 'Configuración de Cuentas de Respaldo',\n            syncWithMaster:\n                'Tras actualizar a Pro, todos los datos se sincronizarán por defecto. Las políticas de sincronización se pueden ajustar manualmente en la gestión de nodos.',\n            syncProxy: 'Configuración del Proxy del Sistema',\n            syncProxyHelper: 'La sincronización del proxy del sistema requiere reiniciar Docker.',\n            syncProxyHelper1: 'Reiniciar Docker puede afectar a los contenedores actualmente en ejecución.',\n            syncProxyHelper2: 'Puedes reiniciar manualmente desde la página Contenedores → Configuración.',\n            syncProxyHelper3:\n                'La sincronización del proxy del sistema requiere reiniciar Docker, lo que puede afectar a servicios en ejecución.',\n            syncProxyHelper4:\n                'La sincronización del proxy del sistema requiere reinicio de Docker. Puedes reiniciar manualmente más tarde en Contenedores → Configuración.',\n            syncCustomApp: 'Sincronizar Repositorio de Apps Personalizadas',\n            syncAlertSetting: 'Configuración de Alertas del Sistema',\n            syncNodeInfo: 'Datos básicos del nodo,',\n            nodeSyncHelper: 'La sincronización de información de nodos incluye:',\n            nodeSyncHelper1: '1. Información de cuentas de respaldo públicas',\n            nodeSyncHelper2: '2. Información de conexión entre el nodo principal y los nodos secundarios',\n            nodeCheck: 'Comprobación de Disponibilidad',\n            checkSSH: 'Verificar conexión SSH del nodo',\n            checkUserPermission: 'Verificar permisos de usuario en el nodo',\n            isNotRoot: 'Se ha detectado que este nodo no soporta sudo sin contraseña y el usuario actual no es root',\n            checkLicense: 'Verificar estado de licencia del nodo',\n            checkService: 'Verificar servicios existentes en el nodo',\n            checkPort: 'Verificar accesibilidad del puerto del nodo',\n            panelExist:\n                'Se ha detectado que este nodo ejecuta 1Panel V1. Actualiza a V2 usando el script de migración antes de añadirlo.',\n            coreExist:\n                'Este nodo ya está configurado como nodo principal y no puede añadirse directamente como nodo secundario. Debe degradarse primero a nodo esclavo. Consulta la documentación.',\n            agentExist:\n                'Se ha detectado que 1panel-agent ya está instalado en este nodo. Continuar conservará los datos y solo reemplazará el servicio de 1panel-agent.',\n            agentNotExist:\n                'Se ha detectado que 1panel-agent no está instalado en este nodo, por lo que la información no puede editarse directamente. Elimínalo y vuelve a añadirlo.',\n            oldDataExist:\n                'Se han detectado datos históricos de 1Panel V2 en este nodo. La siguiente información sobrescribirá la configuración actual:',\n            errLicense: 'La licencia vinculada a este nodo no es válida. Revisa e inténtalo de nuevo.',\n            errNodePort:\n                'El puerto del nodo [ {0} ] no es accesible. Verifica si el firewall o el grupo de seguridad permiten este puerto.',\n            reinstallHelper: '¿Reinstalar el nodo {0}? ¿Quieres continuar?',\n            unhealthyCheck: 'Comprobación Anómala',\n            fixOperation: 'Operación de Corrección',\n            checkName: 'Elemento de Comprobación',\n            checkSSHConn: 'Comprobar disponibilidad de conexión SSH',\n            fixSSHConn: 'Edita manualmente el nodo para confirmar la información de conexión',\n            checkConnInfo: 'Comprobar información de conexión del agente',\n            checkStatus: 'Comprobar disponibilidad de servicios del nodo',\n            fixStatus:\n                'Ejecuta \"systemctl status 1panel-agent.service\" para verificar si el servicio está en ejecución.',\n            checkAPI: 'Comprobar disponibilidad de la API del nodo',\n            fixAPI: 'Revisa los logs del nodo y verifica si los puertos del firewall están abiertos correctamente.',\n            forceDelete: 'Eliminación Forzada',\n            operateHelper: 'Los siguientes nodos se someterán a la operación {0}, ¿quieres continuar?',\n            forceDeleteHelper: 'La eliminación forzada ignorará errores y borrará metadatos de la base de datos.',\n            uninstall: 'Eliminar datos del nodo',\n            uninstallHelper:\n                'Esto eliminará todos los datos relacionados con 1Panel de este nodo. Procede con precaución.',\n            baseDir: 'Directorio de Instalación',\n            baseDirHelper: 'Si el directorio de instalación está vacío, se instalará en /opt por defecto.',\n            nodePort: 'Puerto del Nodo',\n            offline: 'Modo Offline',\n            freeCount: 'Cuota gratuita [{0}]',\n            offlineHelper: 'Usado cuando el nodo está en un entorno sin conexión',\n            appUpgrade: 'Actualización de la aplicación',\n            appUpgradeHelper: 'Hay {0} aplicaciones que necesitan ser actualizadas',\n            addPanel: 'Añadir panel',\n            addPanelHelper: 'Tras añadir el panel, podrás acceder rápidamente en [Resumen - Paneles].',\n            panel: 'Panel 1Panel',\n            others: 'Otros paneles',\n            operatePanelHelper: 'Los siguientes paneles ejecutarán {0}. ¿Continuar?',\n        },\n        customApp: {\n            name: 'Repositorio de Apps Personalizadas',\n            appStoreType: 'Fuente de Paquetes del App Store',\n            appStoreUrl: 'URL del Repositorio',\n            local: 'Ruta Local',\n            remote: 'Enlace Remoto',\n            imagePrefix: 'Prefijo de Imagen',\n            imagePrefixHelper:\n                'Función: Personaliza el prefijo de la imagen y modifica el campo image en el archivo compose. Ejemplo: si el prefijo es 1panel/custom, la imagen de MaxKB pasará a ser 1panel/custom/maxkb:v1.10.0',\n            closeHelper: 'Cancelar el uso del repositorio de apps personalizadas',\n            appStoreUrlHelper: 'Solo se admite el formato .tar.gz',\n            postNode: 'Sincronizar al nodo secundario',\n            postNodeHelper:\n                'Sincroniza el paquete del repositorio personalizado en tmp/customApp/apps.tar.gz dentro del directorio de instalación del nodo secundario',\n            nodes: 'Nodos',\n            selectNode: 'Seleccionar Nodo',\n            selectNodeError: 'Por favor, selecciona un nodo',\n            licenseHelper: 'La versión Pro admite la función de repositorio de aplicaciones personalizadas',\n            databaseHelper: 'Base de datos asociada a la aplicación, seleccione la base de datos del nodo destino',\n            nodeHelper: 'No se puede seleccionar el nodo actual',\n            migrateHelper:\n                'Actualmente solo admite la migración de aplicaciones monolíticas y aplicaciones asociadas únicamente con bases de datos MySQL, MariaDB, PostgreSQL',\n            opensslHelper:\n                'Si se utiliza copia de seguridad cifrada, las versiones de OpenSSL entre los dos nodos deben ser consistentes, de lo contrario la migración puede fallar.',\n            installApp: 'Instalación por lotes',\n            installAppHelper: 'Instalar aplicaciones por lotes en los nodos seleccionados',\n        },\n        alert: {\n            isAlert: 'Alerta',\n            alertCount: 'Número de alertas',\n            clamHelper: 'Generar alerta al detectar archivos infectados en escaneo',\n            cronJobHelper: 'Generar alerta cuando falle la ejecución de una tarea programada',\n            licenseHelper: 'La versión Pro soporta alertas',\n            alertCountHelper: 'Frecuencia máxima diaria de alertas',\n            alert: 'Alerta SMS',\n            logs: 'Logs de alertas',\n            list: 'Lista de alertas',\n            addTask: 'Crear alerta',\n            editTask: 'Editar alerta',\n            alertMethod: 'Método',\n            alertMsg: 'Mensaje de alerta',\n            alertRule: 'Reglas de alerta',\n            titleSearchHelper: 'Introduce el título de alerta para búsqueda difusa',\n            taskType: 'Tipo',\n            ssl: 'Expiración de certificado',\n            siteEndTime: 'Expiración de sitio web',\n            panelPwdEndTime: 'Expiración de contraseña del panel',\n            panelUpdate: 'Nueva versión de panel disponible',\n            cpu: 'Alerta de CPU',\n            memory: 'Alerta de memoria',\n            load: 'Alerta de carga',\n            disk: 'Alerta de disco',\n            website: 'Sitio web',\n            certificate: 'Certificado SSL',\n            remainingDays: 'Días restantes',\n            sendCount: 'Envíos',\n            sms: 'SMS',\n            wechat: 'WeChat',\n            dingTalk: 'DingTalk',\n            feiShu: 'FeiShu',\n            mail: 'Correo',\n            weCom: 'WeCom',\n            sendCountRulesHelper: 'Número total de alertas enviadas antes de expirar (una vez al día)',\n            panelUpdateRulesHelper: 'Alertas totales enviadas por nueva versión del panel (una vez al día)',\n            oneDaySendCountRulesHelper: 'Número máximo de alertas diarias',\n            siteEndTimeRulesHelper: 'Los sitios sin fecha de expiración no generan alertas',\n            autoRenewRulesHelper:\n                'Los certificados con auto-renovación habilitada y menos de 31 días restantes no generan alerta',\n            panelPwdEndTimeRulesHelper:\n                'La alerta de expiración de contraseña no aplica si no se ha configurado expiración',\n            sslRulesHelper: 'Todos los certificados SSL',\n            diskInfo: 'Disco',\n            monitoringType: 'Tipo de monitorización',\n            autoRenew: 'Auto-renovación',\n            useDisk: 'Uso de disco',\n            usePercentage: 'Porcentaje de uso',\n            changeStatus: 'Cambiar estado',\n            disableMsg: 'Detener la tarea de alerta evitará que envíe mensajes. ¿Continuar?',\n            enableMsg: 'Al habilitar la tarea podrá enviar mensajes de alerta. ¿Continuar?',\n            useExceed: 'Uso excedido',\n            useExceedRulesHelper: 'Genera alerta cuando el uso supera el valor configurado',\n            cpuUseExceedAvg: 'El uso medio de CPU supera el valor especificado',\n            memoryUseExceedAvg: 'El uso medio de memoria supera el valor especificado',\n            loadUseExceedAvg: 'La carga media supera el valor especificado',\n            cpuUseExceedAvgHelper: 'El uso medio de CPU en el periodo supera el valor configurado',\n            memoryUseExceedAvgHelper: 'El uso medio de memoria en el periodo supera el valor configurado',\n            loadUseExceedAvgHelper: 'La carga media en el periodo supera el valor configurado',\n            resourceAlertRulesHelper: 'Nota: múltiples alertas en 30 minutos se unifican en una',\n            specifiedTime: 'Tiempo especificado',\n            deleteTitle: 'Eliminar alerta',\n            deleteMsg: '¿Seguro que quieres eliminar esta tarea de alerta?',\n            allSslTitle: 'Alertas de expiración de certificados SSL en todos los sitios',\n            sslTitle: 'Alerta de expiración de certificado SSL en sitio {0}',\n            allSiteEndTimeTitle: 'Alertas de expiración de todos los sitios web',\n            siteEndTimeTitle: 'Alerta de expiración para sitio {0}',\n            panelPwdEndTimeTitle: 'Alerta de expiración de contraseña del panel',\n            panelUpdateTitle: 'Notificación de nueva versión de panel',\n            cpuTitle: 'Alerta de alta CPU',\n            memoryTitle: 'Alerta de alta memoria',\n            loadTitle: 'Alerta de alta carga',\n            diskTitle: 'Alerta de alto uso de disco en {0}',\n            allDiskTitle: 'Alerta de alto uso de disco',\n            timeRule: 'Tiempo restante menor a {0} días (si no se gestiona, se reenviará al día siguiente)',\n            panelUpdateRule:\n                'Al detectar nueva versión del panel se envía una alerta (si no se gestiona, se reenviará al día siguiente)',\n            avgRule: 'Uso medio de {1} supera {2}% en {0} minutos, genera alerta, {3} envíos/día',\n            diskRule: 'Uso de disco en {0} supera {1}{2}, genera alerta, {3} envíos/día',\n            allDiskRule: 'Uso de disco supera {0}{1}, genera alerta, {2} envíos/día',\n            cpuName: 'CPU',\n            memoryName: 'Memoria',\n            loadName: 'Carga',\n            diskName: 'Disco',\n            syncAlertInfo: 'Push manual',\n            syncAlertInfoMsg: '¿Deseas enviar manualmente la tarea de alerta?',\n            pushError: 'Error en push',\n            pushSuccess: 'Push exitoso',\n            syncError: 'Error de sincronización',\n            success: 'Alerta enviada',\n            pushing: 'Enviando...',\n            error: 'Error de alerta',\n            cleanLog: 'Limpiar logs',\n            cleanAlertLogs: 'Limpiar logs de alertas',\n            daily: 'Alertas diarias: {0}',\n            cumulative: 'Alertas acumuladas: {0}',\n            clams: 'Alerta de escaneo antivirus',\n            taskName: 'Nombre de tarea',\n            cronJobType: 'Tipo de tarea',\n            clamPath: 'Directorio a escanear',\n            cronjob: 'Fallo en ejecución de cronjob {0}',\n            app: 'Backup de app',\n            web: 'Backup de sitio',\n            database: 'Backup de base de datos',\n            directory: 'Backup de directorio',\n            log: 'Backup de logs',\n            snapshot: 'Snapshot del sistema',\n            clamsRulesHelper: 'Tareas de escaneo antivirus que requieren alerta',\n            cronJobRulesHelper: 'Este tipo de tareas programadas necesita configuración',\n            clamsTitle: 'Tarea antivirus 「 {0} 」 detectó archivo infectado',\n            cronJobAppTitle: 'Cronjob - Backup de app 「 {0} 」 falló',\n            cronJobWebsiteTitle: 'Cronjob - Backup de sitio 「 {0} 」 falló',\n            cronJobDatabaseTitle: 'Cronjob - Backup de base de datos 「 {0} 」 falló',\n            cronJobDirectoryTitle: 'Cronjob - Backup de directorio 「 {0} 」 falló',\n            cronJobLogTitle: 'Cronjob - Backup de logs 「 {0} 」 falló',\n            cronJobSnapshotTitle: 'Cronjob - Backup de snapshot 「 {0} 」 falló',\n            cronJobShellTitle: 'Cronjob - Script shell 「 {0} 」 falló',\n            cronJobCurlTitle: 'Cronjob - Acceso URL 「 {0} 」 falló',\n            cronJobCutWebsiteLogTitle: 'Cronjob - Corte de logs web 「 {0} 」 falló',\n            cronJobCleanTitle: 'Cronjob - Limpieza de caché 「 {0} 」 falló',\n            cronJobNtpTitle: 'Cronjob - Sync hora de servidor 「 {0} 」 falló',\n            clamsRule: 'Escaneo antivirus detectó infección, {0} envíos/día',\n            cronJobAppRule: 'Fallo en backup de app, {0} envíos/día',\n            cronJobWebsiteRule: 'Fallo en backup de sitio, {0} envíos/día',\n            cronJobDatabaseRule: 'Fallo en backup de base de datos, {0} envíos/día',\n            cronJobDirectoryRule: 'Fallo en backup de directorio, {0} envíos/día',\n            cronJobLogRule: 'Fallo en backup de logs, {0} envíos/día',\n            cronJobSnapshotRule: 'Fallo en backup de snapshot, {0} envíos/día',\n            cronJobShellRule: 'Fallo en script shell, {0} envíos/día',\n            cronJobCurlRule: 'Fallo en acceso URL, {0} envíos/día',\n            cronJobCutWebsiteLogRule: 'Fallo en corte de logs web, {0} envíos/día',\n            cronJobCleanRule: 'Fallo en limpieza de caché, {0} envíos/día',\n            cronJobNtpRule: 'Fallo en sincronización de hora, {0} envíos/día',\n            alertSmsHelper: 'Límite SMS: {0} mensajes totales, {1} usados',\n            goBuy: 'Comprar más',\n            phone: 'Teléfono',\n            phoneHelper: 'Proporciona un número real para SMS de alertas',\n            dailyAlertNum: 'Límite diario de alertas',\n            dailyAlertNumHelper: 'Máximo de alertas por día (hasta 100)',\n            timeRange: 'Rango de tiempo',\n            sendTimeRange: 'Rango de envío',\n            sendTimeRangeHelper: 'Puede enviarse en {0}',\n            to: 'a',\n            startTime: 'Hora inicio',\n            endTime: 'Hora fin',\n            defaultPhone: 'Usar el número vinculado a la licencia',\n            noticeAlert: 'Alerta de notificación',\n            resourceAlert: 'Alerta de recursos',\n            agentOfflineAlertHelper: 'Con alerta offline habilitada, el nodo maestro escaneará cada 30 minutos.',\n            offline: 'Alerta offline',\n            offlineHelper: 'En modo offline, el nodo maestro escaneará cada 30 minutos para alertas.',\n            offlineOff: 'Habilitar alerta offline',\n            offlineOffHelper: 'Habilitar alerta offline hace que el nodo maestro ejecute alertas cada 30 minutos.',\n            offlineClose: 'Desactivar alerta offline',\n            offlineCloseHelper: 'Desactivarla obliga a que los sub-nodos gestionen alertas. Asegura conectividad.',\n            alertNotice: 'Notificación de alerta',\n            methodConfig: 'Config de métodos de notificación',\n            commonConfig: 'Config global',\n            smsConfig: 'SMS',\n            smsConfigHelper: 'Configura teléfonos para notificaciones SMS',\n            emailConfig: 'Correo',\n            emailConfigHelper: 'Configura servicio SMTP de envío de correos',\n            deleteConfigTitle: 'Eliminar configuración de alerta',\n            deleteConfigMsg: '¿Seguro que quieres eliminar esta configuración?',\n            test: 'Probar',\n            alertTestOk: 'Notificación de prueba exitosa',\n            alertTestFailed: 'Notificación de prueba fallida',\n            displayName: 'Nombre',\n            sender: 'Remitente',\n            password: 'Contraseña',\n            host: 'SMTP',\n            port: 'Puerto',\n            encryption: 'Cifrado',\n            recipient: 'Destinatario',\n            licenseTime: 'Recordatorio de expiración de licencia',\n            licenseTimeTitle: 'Recordatorio de expiración de licencia',\n            displayNameHelper: 'Nombre remitente para correos',\n            senderHelper: 'Correo usado como remitente',\n            passwordHelper: 'Código de autorización del servicio de correo',\n            hostHelper: 'Servidor SMTP, ej. smtp.qq.com',\n            portHelper: 'SSL normalmente 465, TLS normalmente 587',\n            sslHelper: 'Si el puerto es 465, requiere SSL',\n            tlsHelper: 'Si el puerto es 587, requiere TLS',\n            triggerCondition: 'Condición de disparo',\n            loginFail: ' fallos de login en',\n            nodeException: 'Alerta de excepción de nodo',\n            licenseException: 'Alerta de excepción de licencia',\n            panelLogin: 'Alerta de login en panel',\n            sshLogin: 'Alerta de login SSH',\n            panelIpLogin: 'Alerta de login en panel por IP',\n            sshIpLogin: 'Alerta de login SSH por IP',\n            ipWhiteListHelper: 'Las IPs en whitelist no tienen restricciones ni generan alerta en login exitoso',\n            nodeExceptionRule: 'Alerta de nodo anómalo, {0} envíos/día',\n            licenseExceptionRule: 'Alerta de licencia anómala, {0} envíos/día',\n            panelLoginRule: 'Alerta de login en panel, {0} envíos/día',\n            sshLoginRule: 'Alerta de login SSH, {0} envíos/día',\n            userNameHelper: 'El nombre de usuario está vacío, se usará la dirección del remitente por defecto',\n            alertConfigHelper: 'Configurar canales de notificación de alerta para recibir mensajes push del panel',\n            weComConfigHelper: 'Configuración de notificación de alerta WeCom',\n            wechatConfigHelper: 'Configuración de notificación de alerta de Cuenta Oficial WeChat',\n            dingTalkConfigHelper: 'Configuración de notificación de alerta DingTalk',\n            feiShuConfigHelper: 'Configuración de notificación de alerta Feishu',\n            webhookName: 'Nombre del bot',\n            webhookUrl: 'URL de Webhook',\n            alertConfigProHelper:\n                'La edición Profesional admite además alertas mediante WeCom, DingTalk, Feishu y SMS.',\n        },\n        theme: {\n            lingXiaGold: 'Ling Xia Gold',\n            classicBlue: 'Azul clásico',\n            freshGreen: 'Verde fresco',\n            customColor: 'Color personalizado',\n            setDefault: 'Por defecto',\n            setDefaultHelper: 'La paleta de colores volverá al estado inicial. ¿Continuar?',\n            setHelper: 'Se guardará la paleta actual como tema. ¿Continuar?',\n        },\n        exchange: {\n            exchange: 'Intercambio de archivos',\n            exchangeConfirm: '¿Quieres transferir {1} de {0} a {2}:{3}?',\n        },\n        cluster: {\n            cluster: 'Alta disponibilidad de aplicaciones',\n            name: 'Nombre del clúster',\n            addCluster: 'Añadir clúster',\n            installNode: 'Instalar nodo',\n            master: 'Nodo maestro',\n            slave: 'Nodo esclavo',\n            replicaStatus: 'Estado maestro-esclavo',\n            unhealthyDeleteError: 'El nodo tiene estado anómalo, revisa la lista e inténtalo de nuevo',\n            replicaStatusError: 'Error al obtener estado, revisa el nodo maestro',\n            masterHostError: 'La IP del nodo maestro no puede ser 127.0.0.1',\n        },\n    },\n};\n\nexport default {\n    ...fit2cloudEsEsLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/ja.ts",
    "content": "import fit2cloudEnLocale from 'fit2cloud-ui-plus/src/locale/lang/ja';\n\nconst message = {\n    commons: {\n        true: 'はい',\n        false: 'いいえ',\n        colon: ':',\n        example: '例：',\n        fit2cloud: 'FIT2CLOUD',\n        lingxia: 'Lingxia',\n        button: {\n            run: '実行',\n            create: '作成する',\n            add: '追加',\n            save: '保存',\n            set: '構成を編集します',\n            sync: '同期',\n            delete: '消去',\n            edit: '編集',\n            enable: '有効にする',\n            disable: '無効にします',\n            confirm: '確認する',\n            cancel: 'キャンセル',\n            reset: 'リセット',\n            restart: '再起動',\n            conn: '接続する',\n            disConn: '切断します',\n            clean: 'クリーン',\n            login: 'ログイン',\n            close: '近い',\n            off: '近い',\n            stop: '停止',\n            start: '始める',\n            view: 'ビュー',\n            watch: '時計',\n            handle: 'トリガー',\n            clone: 'クローン',\n            expand: '拡大する',\n            collapse: '崩壊',\n            log: 'ログ',\n            back: '戻る',\n            backup: 'バックアップ',\n            recover: '回復する',\n            retry: 'リトライ',\n            upload: 'アップロード',\n            download: 'ダウンロード',\n            init: '初期化',\n            verify: '確認する',\n            saveAndEnable: '保存して有効にします',\n            import: '輸入',\n            export: 'エクスポート',\n            power: '認可',\n            search: '検索',\n            refresh: 'リロード',\n            get: '得る',\n            upgrade: 'アップグレード',\n            update: '編集',\n            updateNow: '今すぐ更新',\n            ignore: '更新を無視する',\n            copy: 'コピー',\n            random: 'ランダム',\n            install: 'インストール',\n            uninstall: 'アンインストール',\n            fullscreen: 'フルスクリーン',\n            quitFullscreen: 'フルスクリーンを終了',\n            showAll: 'すべてを表示します',\n            hideSome: 'いくつかを隠します',\n            agree: '同意する',\n            notAgree: '同意しません',\n            preview: 'プレビュー',\n            open: '開ける',\n            notSave: '保存しないでください',\n            createNewFolder: '新しいフォルダーを作成します',\n            createNewFile: '新しいファイルを作成します',\n            helpDoc: '文書をヘルプします',\n            unbind: 'バインド',\n            cover: 'に覆いを',\n            skip: 'スキップ',\n            fix: '修正',\n            down: '停止',\n            up: '起動',\n            sure: '確認',\n            show: '表示する',\n            hide: '隠す',\n            visit: '訪問',\n            migrate: '移行',\n            prev: '前へ',\n            next: '次へ',\n            setDefault: 'デフォルトに戻す',\n            bind: 'バインド',\n        },\n        operate: {\n            start: '開始',\n            stop: '停止',\n            restart: '再起動',\n            reload: '再読み込み',\n            rebuild: '再構築',\n            sync: '同期',\n            up: '起動',\n            down: '停止',\n            delete: '削除',\n        },\n        search: {\n            timeStart: '時間開始',\n            timeEnd: 'タイムエンド',\n            timeRange: 'に',\n            dateStart: '日付開始',\n            dateEnd: '日付の終わり',\n            date: '日付',\n        },\n        table: {\n            all: '全て',\n            total: '合計{0}',\n            name: '名前',\n            type: 'タイプ',\n            status: '状態',\n            records: '記録',\n            group: 'グループ',\n            createdAt: '作成時間',\n            publishedAt: '公開時間',\n            date: '日付',\n            updatedAt: '時間を更新します',\n            operate: '操作',\n            message: 'メッセージ',\n            description: '説明',\n            interval: '間隔',\n            user: '所有者',\n            title: 'タイトル',\n            port: 'ポート',\n            forward: 'フォワード',\n            protocol: 'プロトコル',\n            tableSetting: 'テーブル設定',\n            refreshRate: 'リフレッシュレート',\n            selectColumn: '列を選択します',\n            local: 'ローカル',\n            serialNumber: 'シリアル番号',\n            manageGroup: 'グループ管理',\n            backToList: 'リストに戻る',\n            keepEdit: '編集を続ける',\n            default: 'デフォルト',\n            noRefresh: '自動更新なし',\n        },\n        loadingText: {\n            Upgrading: 'システムのアップグレード、待ってください...',\n            Restarting: 'システムの再起動、待ってください...',\n            Recovering: 'スナップショットから回復して、待ってください...',\n            Rollbacking: 'スナップショットからのロールバック、お待ちください...',\n        },\n        msg: {\n            noneData: '利用可能なデータはありません',\n            delete: 'この操作削除は元に戻すことはできません。続けたいですか？',\n            clean: 'この操作は取り消すことはできません。続けたいですか？',\n            closeDrawerHelper: 'システムは変更を保存しない可能性があります。続行しますか？',\n            deleteSuccess: '正常に削除されました',\n            loginSuccess: '正常にログインしました',\n            operationSuccess: '正常に完了',\n            copySuccess: '正常にコピーされました',\n            notSupportOperation: 'この操作はサポートされていません',\n            requestTimeout: 'リクエストがタイムアウトしました。後でもう一度やり直してください',\n            infoTitle: 'ヒント',\n            notRecords: '現在のタスクの実行レコードは生成されません',\n            sureLogOut: 'ログアウトしたいですか？',\n            createSuccess: '正常に作成されました',\n            updateSuccess: '正常に更新されました',\n            uploadSuccess: '正常にアップロードされました',\n            operateConfirm: '操作について確信している場合は、手動で入力してください。',\n            inputOrSelect: '選択または入力してください',\n            copyFailed: 'コピーに失敗しました',\n            operatorHelper: '操作「{1}」は「{0}」で実行され、元に戻すことはできません。続けたいですか？',\n            notFound: '申し訳ありませんが、要求したページは存在しません。',\n            unSupportType: '現在のファイルタイプはサポートされていません。',\n            unSupportSize: 'アップロードされたファイルは{0} mを超えています、確認してください！',\n            fileExist:\n                'ファイルはすでに現在のフォルダーに存在しています。リピートアップロードはサポートされていません。',\n            fileNameErr:\n                '名前には、英語、中国語、数字、または期間を含む1〜256文字が含まれるファイルのみをアップロードできます（。-_）',\n            confirmNoNull: '値{0}が空でないことを確認してください。',\n            errPort: 'ポート情報が正しくありません、確認してください！',\n            remove: '取り除く',\n            backupHelper: '現在の操作は{0}をバックアップします。先に進みたいですか？',\n            recoverHelper: '{0}ファイルから復元。この操作は不可逆的です。続けたいですか？',\n            refreshSuccess: 'リフレッシュして成功します',\n            rootInfoErr: 'すでにルートディレクトリです',\n            resetSuccess: 'リセット成功',\n            creatingInfo: '作成、この操作は必要ありません',\n            offlineTips: 'オフライン版はこの操作をサポートしていません',\n            errImportFormat: 'インポートデータまたはフォーマットが異常です。確認して再試行してください！',\n            importHelper:\n                '競合または重複するデータをインポートする場合、インポートされた内容を基準として元のデータベースデータを更新します。',\n            errImport: 'ファイル内容が異常です：',\n            backupSuccess: 'バックアップ成功',\n            restoreSuccess: '復元成功',\n            installSuccess: 'インストール成功',\n            uninstallSuccess: 'アンインストール成功',\n        },\n        login: {\n            username: 'ユーザー名',\n            password: 'パスワード',\n            passkey: 'パスキーでログイン',\n            welcome: 'ようこそ、ユーザー名とパスワードを入力してログインしてください！',\n            errorAuthInfo: '入力したユーザー名またはパスワードは間違っています。再入力してください！',\n            errorMfaInfo: '認証情報が誤っていない、もう一度やり直してください！',\n            captchaHelper: 'キャプチャ',\n            errorCaptcha: 'Captchaコードエラー！',\n            notSafe: 'アクセスが拒否されました',\n            safeEntrance1: '安全なログインは現在の環境で有効になっています',\n            safeEntrance2: 'SSH端末に次のコマンドを入力して、パネルエントリを表示します:1PCTLユーザー-INFO',\n            errIP1: '現在の環境で認定されたIPアドレスアクセスが有効になっています',\n            errDomain1: 'アクセスドメイン名のバインディングが現在の環境で有効になっています',\n            errHelper: 'バインディング情報をリセットするには、SSH端末で次のコマンドを実行します。',\n            codeInput: 'MFAバリデーターの6桁の検証コードを入力してください',\n            mfaTitle: 'MFA認定',\n            mfaCode: 'MFA検証コード',\n            title: 'Linuxサーバー管理パネル',\n            licenseHelper: '<コミュニティライセンス契約>',\n            errorAgree: 'クリックして、コミュニティソフトウェアライセンスに同意します',\n            logout: 'ログアウト',\n            agreeTitle: '合意',\n            agreeContent:\n                'あなたの正当な権利と利益をよりよく保護するために、次の契約とラコを読んで同意してください。<a href = \"https://www.fit2cloud.com/legal/licenses.html\" target=\"_blank\">コミュニティライセンス契約</a>＆raquo;',\n            passkeyFailed: 'パスキーでのログインに失敗しました。再試行してください',\n            passkeyNotSupported: '現在のブラウザまたは環境はパスキーに対応していません',\n            passkeyToPassword: 'パスキーが使えませんか？パスワードでログインしてください',\n        },\n        rule: {\n            username: 'ユーザー名を入力します',\n            password: 'パスワードを入力します',\n            rePassword: 'パスワードがパスワードと矛盾することを確認してください。',\n            requiredInput: 'この項目は必須です。',\n            requiredSelect: 'リスト内のアイテムを選択します',\n            illegalChar: '現在、文字 & ; $ \\' ` ( ) \" > < | の注入はサポートされていません',\n            illegalInput: 'このフィールドには違法なキャラクターが含まれてはなりません。',\n            commonName:\n                'このフィールドは、特別なキャラクターではなく、英語、中国語、数字で構成されている必要があります。「。」、「」、および「_」文字が1〜128の文字で構成されている必要があります。',\n            userName: '特殊文字で始まらない、英字、漢字、数字、および_をサポート、長さ3-30',\n            simpleName:\n                'このフィールドは、アンダースコアキャラクターから始めてはなりません。長さ3〜30の英語、数字、「_」文字で構成されている必要があります。',\n            simplePassword:\n                'このフィールドは、アンダースコアキャラクターから始めてはなりません。長さ1〜30の英語、数字、「_」文字で構成されている必要があります。',\n            dbName: 'このフィールドは、アンダースコアキャラクターから始めてはなりません。長さ1〜64の英語、数字、「_」文字で構成されている必要があります。',\n            imageName: '特殊文字で始まらない、英字、数字、:@/.-_をサポート、長さ1-256',\n            composeName: '最初の特別な文字、小文字、数字、_、長さ1-256をサポートします',\n            volumeName:\n                'このフィールドは、英語、数字、「。」、「 - 」、および「_」文字で構成されている必要があります。',\n            supervisorName:\n                'このフィールドは、特別な文字以外の文字から開始する必要があり、英語、数字、「 - 」、および「_」文字が1〜128の文字で構成されている必要があります。',\n            complexityPassword:\n                'このフィールドは、英語で構成され、長さは8〜30で、少なくとも2つの特殊文字が含まれている必要があります。',\n            commonPassword: 'このフィールドの長さは6を超える必要があります。',\n            linuxName:\n                'このフィールドの長さは1〜128でなければなりません。フィールドには、これらの特殊文字を含めてはなりません。「{0}」。',\n            email: 'このフィールドは有効な電子メールアドレスでなければなりません。',\n            number: 'このフィールドは数字でなければなりません。',\n            integer: 'このフィールドは正の整数でなければなりません。',\n            ip: 'このフィールドは有効なIPアドレスでなければなりません。',\n            host: 'このフィールドは、有効なIPアドレスまたはドメイン名でなければなりません。',\n            hostHelper: 'IPアドレスまたはドメイン名の入力をサポートします',\n            port: 'このフィールドは有効なポート番号でなければなりません。',\n            selectHelper: '正しい{0}ファイルを選択してください',\n            domain: 'このフィールドは、example.comまたはexample.com:8080のようなものでなければなりません。',\n            databaseName: 'このフィールドは、長さ1〜30の英語、数字、「_」文字で構成されている必要があります。',\n            ipErr: 'このフィールドは有効なIPアドレスでなければなりません。',\n            numberRange: 'このフィールドは、{0}と{1}の間の数字でなければなりません。',\n            paramName: 'このフィールドは、英語、数字、「。」、「 - 」、および「_」文字で構成されている必要があります。',\n            paramComplexity:\n                'このフィールドは、特殊文字で開始および終了する必要はなく、英語、数字「{0}」文字で構成されている必要があります。',\n            paramUrlAndPort:\n                'このフィールドは、「http（s）://（domain name/ip）:(ポート）」の形式でなければなりません。',\n            nginxDoc: 'このフィールドは、英語、数字、「」で構成されている必要があります。文字。',\n            appName:\n                'このフィールドは、「 - 」と「_」文字で開始および終了してはなりません。英語、数字、 \"、および「_」文字で2〜30の文字で構成されている必要があります。',\n            containerName: '文字、数字、 - 、_および。;-  _または。で始めることはできません。長さ:2-128',\n            mirror: 'ミラーアクセラレーションアドレスは、http（s）://、英語の文字（大文字と小文字の両方）、数字をサポートする必要があります。/および - 、そして空白の行を含めてはなりません。',\n            disableFunction: 'サポートレター、アンダースコア、および',\n            leechExts: 'サポートレター、数字、および',\n            paramSimple: '小文字と数字をサポート、長さ1〜128',\n            filePermission: 'ファイル許可エラー',\n            formatErr: 'フォーマットエラー、チェックして再試行してください',\n            phpExtension: '_小文字の英語と数字のみをサポートします',\n            paramHttp: 'http://またはhttps://で始める必要があります',\n            phone: '電話番号の形式は正しくありません',\n            authBasicPassword: '英字、数字、一般的な特殊文字をサポート、長さ1-72',\n            length128Err: '長さは128文字を超えることはできません',\n            maxLength: '長さは {0} 文字を超えることはできません',\n            alias: '英字、数字、-と_をサポート、長さ1-128、-_で始まるまたは終わることはできません。',\n        },\n        res: {\n            paramError: 'リクエストが失敗しました。後でもう一度やり直してください！',\n            forbidden: '現在のユーザーには許可がありません',\n            serverError: 'サービスの例外',\n            notFound: 'リソースは存在しません',\n            commonError: 'リクエストに失敗しました',\n        },\n        service: {\n            serviceNotStarted: '{0}サービスは開始されません。',\n        },\n        status: {\n            running: 'ランニング',\n            done: '終わり',\n            scanFailed: '不完全',\n            success: '成功',\n            waiting: '待っている',\n            waitForUpgrade: 'アップグレード待機中',\n            waiting1: '待っている',\n            failed: '失敗した',\n            stopped: '停止',\n            error: 'エラー',\n            created: '作成されました',\n            restarting: '再起動',\n            uploading: 'アップロード',\n            unhealthy: '不健康',\n            removing: '削除',\n            paused: '一時停止',\n            exited: '終了',\n            dead: '死んだ',\n            installing: 'インストール',\n            enabled: '有効になっています',\n            disabled: '無効',\n            normal: '普通',\n            building: '建物',\n            upgrading: 'アップグレード',\n            pending: '編集待ち',\n            rebuilding: '再構築',\n            deny: '拒否されました',\n            accept: '受け入れられました',\n            used: '使用済み',\n            unUsed: '未使用',\n            starting: '起動',\n            recreating: '再作成',\n            creating: '作成',\n            init: 'アプリケーションを待っています',\n            ready: '普通',\n            applying: '適用',\n            uninstalling: 'アンインストール中',\n            lost: '接続失効',\n            bound: 'バインド済み',\n            unbind: '未バインド',\n            exceptional: '異常',\n            free: '空き',\n            enable: '有効',\n            disable: '無効',\n            deleted: '削除済み',\n            downloading: 'ダウンロード中',\n            packing: 'パッキング中',\n            sending: '送信中',\n            healthy: '正常',\n            executing: '実行中',\n            installerr: 'インストールに失敗しました',\n            applyerror: '適用に失敗しました',\n            systemrestart: '中断',\n            starterr: '起動に失敗しました',\n            uperr: '起動に失敗しました',\n            new: '新規',\n            conflict: '競合',\n            duplicate: '重複',\n            unexecuted: '未実行',\n            unused: '未使用',\n        },\n        units: {\n            second: '2番目|2番目|秒',\n            minute: '分|分|分',\n            hour: '時間|時間|時間',\n            day: '日|日|日',\n            week: '週|週|週',\n            month: '月|月|数ヶ月',\n            year: '年|年|年',\n            time: 'RPM',\n            core: 'コア|コア|コア',\n            secondUnit: 's',\n            minuteUnit: 'min',\n            hourUnit: 'h',\n            dayUnit: 'd',\n            millisecond: 'ミリ秒',\n            semicolon: ';',\n        },\n        log: {\n            noLog: 'ログはありません',\n        },\n    },\n    menu: {\n        home: 'ダッシュボード',\n        apps: 'アプリストア',\n        website: 'サイト | サイト',\n        project: 'プロジェクト|プロジェクト',\n        config: '構成|構成',\n        ssh: 'SSH設定',\n        firewall: 'ファイアウォール',\n        ssl: '証明書|証明書',\n        database: 'データベース|データベース',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: 'コンテナ|コンテナ',\n        cronjob: 'クロンジョブ|クロンの仕事',\n        system: 'システム',\n        security: '安全',\n        files: 'ファイル',\n        monitor: '監視',\n        terminal: '端子',\n        settings: '設定',\n        toolbox: 'ツールボックス',\n        logs: 'ログ |ログ',\n        runtime: 'ランタイム|ランタイム',\n        processManage: 'プロセス|プロセス',\n        process: 'プロセス|プロセス',\n        network: 'ネットワーク|ネットワーク',\n        supervisor: '監督者',\n        tamper: '改ざん防止',\n        app: 'アプリケーション',\n        msgCenter: 'タスク',\n        disk: 'ディスク',\n        filter: 'フィルター',\n    },\n    home: {\n        recommend: 'おすすめ',\n        dir: 'ディレクトリ',\n        alias: 'エイリアス',\n        quickDir: 'クイックアクセス',\n        minQuickJump: 'クイックアクセス項目を1つ以上追加してください',\n        maxQuickJump: '最大4件のクイックアクセス項目を追加できます',\n        database: 'データベース - すべて',\n        restart_1panel: 'パネルを再起動します',\n        restart_system: 'サーバーを再起動します',\n        operationSuccess: '完了しました。再起動中です。後でブラウザを再読み込みしてください。',\n        entranceHelper:\n            '安全なログインが有効になっていません。「設定 -> セキュリティ」で有効化してセキュリティを強化してください。',\n        appInstalled: 'アプリケーション',\n        systemInfo: 'システム情報',\n        hostname: 'ホスト名',\n        platformVersion: 'オペレーティング·システム',\n        kernelVersion: 'カーネル',\n        kernelArch: '建築',\n        network: 'ネットワーク',\n        io: 'ディスクI/O',\n        ip: 'ローカルIP',\n        proxy: 'システムプロキシ',\n        baseInfo: '基本情報',\n        totalSend: '合計送信',\n        totalRecv: '総受領',\n        rwPerSecond: 'I/O操作',\n        ioDelay: 'I/Oレイテンシ',\n        uptime: 'それ以来',\n        runningTime: '稼働時間',\n        mem: 'システムメモリ',\n        swapMem: 'パーティションを交換します',\n        runSmoothly: '低負荷',\n        runNormal: '中程度の負荷',\n        runSlowly: '高負荷',\n        runJam: '重い負荷',\n        core: '物理コア',\n        logicCore: '論理コア',\n        corePercent: 'コア使用率',\n        cpuFrequency: 'CPU 周波数',\n        cpuDetailedPercent: 'CPU 占有',\n        cpuUser: 'ユーザー',\n        cpuSystem: 'システム',\n        cpuIdle: 'アイドル',\n        cpuIrq: 'IRQ',\n        cpuSoftirq: 'Soft IRQ',\n        cpuSteal: 'Steal',\n        cpuTop: 'CPU使用率トップ5のプロセス情報',\n        memTop: 'メモリ使用率トップ5のプロセス情報',\n        loadAverage: '最後の1分で平均を積み込みます|最後の{n}分で平均を読み込みます',\n        load: '負荷',\n        mount: 'マウントポイント',\n        fileSystem: 'ファイルシステム',\n        total: '合計',\n        used: '使用済み',\n        cache: 'キャッシュ',\n        free: '空き',\n        shard: 'シャーディング',\n        available: '利用可能',\n        percent: '利用',\n        goInstall: 'インストールします',\n        networkCard: 'ネットワークカード',\n        disk: 'ディスク',\n        memo: 'メモ',\n        memoPlaceholder: '編集ボタンをクリックして編集を有効にします。',\n        carouselSetting: 'カルーセル設定',\n        tooltipSensitiveInfo: '機密情報を表示/非表示',\n    },\n    tabs: {\n        more: 'もっと',\n        hide: '隠れる',\n        closeLeft: '左を閉じます',\n        closeRight: '右に閉じます',\n        closeCurrent: '電流を閉じます',\n        closeOther: '他を閉じます',\n        closeAll: 'すべてを閉じます',\n    },\n    header: {\n        logout: 'ログアウト',\n    },\n    database: {\n        manage: '管理',\n        deleteBackupHelper: 'データベースのバックアップを同時に削除します',\n        delete: '削除操作はロールバックできません、入力してください」',\n        deleteHelper: '「このデータベースを削除します',\n        noMysql: 'データベースサービス（mysqlまたはmariadb）',\n        noPostgresql: 'データベースサービスpostgreSql',\n        goUpgrade: 'アップグレードに移動します',\n        goInstall: 'インストールに移動します',\n        isDelete: '削除されました',\n        permission: '権限',\n        format: '文字セット',\n        collation: '照合順序',\n        collationHelper: '空の場合は {0} 文字セットのデフォルトの照合順序を使用します',\n        permissionForIP: 'ip',\n        permissionAll: 'それらすべて（％）',\n        localhostHelper:\n            'コンテナ展開でデータベース権限を\"localhost\"に設定すると、コンテナ外部からのアクセスができなくなります。慎重に選択してください！',\n        databaseConnInfo: '接続',\n        rootPassword: 'ルートパスワード',\n        serviceName: 'サービス名',\n        serviceNameHelper: '同じネットワーク内のコンテナ間のアクセス。',\n        backupList: 'バックアップ',\n        loadBackup: '輸入',\n        localUpload: 'ローカルアップロード',\n        hostSelect: 'サーバー選択',\n        selectHelper: 'バックアップファイル {0} をインポートしてもよろしいですか？',\n        remoteAccess: 'リモートアクセス',\n        remoteHelper: '複数のIP Comma delimited、例:172.16.10.111、172.16.10.112',\n        remoteConnHelper:\n            'ユーザールートとしてのMySQLへのリモート接続には、セキュリティリスクがある場合があります。したがって、この操作を慎重に実行します。',\n        changePassword: 'パスワード',\n        changeConnHelper: 'この操作は現在のデータベース {0} を変更します。続行しますか？',\n        changePasswordHelper:\n            'データベースはアプリケーションに関連付けられています。パスワードを変更すると、アプリケーションのデータベースパスワードが同時に変更されます。アプリケーションが再起動した後、変更は有効になります。',\n        recoverTimeoutHelper: '-1 はタイムアウト制限なしを意味します',\n        confChange: '構成',\n        confNotFound:\n            '設定ファイルが見つかりませんでした。アプリストアでアプリケーションを最新バージョンにアップグレードして、再度お試しください！',\n        portHelper:\n            'このポートは、コンテナの露出したポートです。変更を個別に保存して、コンテナを再起動する必要があります！',\n        loadFromRemote: '同期',\n        userBind: 'バインドユーザー',\n        pgBindHelper:\n            'この操作は、新しいユーザーを作成し、ターゲットデータベースにバインドするために使用されます。現在、データベースに既存のユーザーを選択することはサポートされていません。',\n        pgSuperUser: 'スーパーユーザー',\n        loadFromRemoteHelper: 'これにより、サーバー上のデータベース情報が1パネルに同期します。続けたいですか？',\n        passwordHelper: '取得できません、クリックして修正',\n        remote: 'リモート',\n        remoteDB: 'リモートサーバー|リモートサーバー',\n        createRemoteDB: 'リモートサーバーを追加',\n        unBindRemoteDB: 'リモートサーバーのバインドを解除',\n        unBindForce: '強制バインド',\n        unBindForceHelper: '結合プロセス中にすべてのエラーを無視して、最終操作が成功するようにします',\n        unBindRemoteHelper:\n            'リモートデータベースのバインディングを解除すると、バインディング関係が削除されるだけで、リモートデータベースは直接削除されません。',\n        editRemoteDB: 'リモートサーバーを編集します',\n        localDB: 'ローカルデータベース',\n        address: 'データベースアドレス',\n        version: 'データベースバージョン',\n        userHelper:\n            'ルートユーザーまたはルート特権を持つデータベースユーザーは、リモートデータベースにアクセスできます。',\n        pgUserHelper: 'スーパーユーザーの特権を持つデータベースユーザー。',\n        ssl: 'SSLを使用します',\n        clientKey: 'クライアントの秘密鍵',\n        clientCert: 'クライアント証明書',\n        caCert: '証明書として',\n        hasCA: 'CA証明書があります',\n        skipVerify: '証明書の有効性チェックを無視します',\n        initialDB: '初期データベース',\n        formatHelper: '現在のデータベース文字セットは{0}です。文字セットの矛盾は回復の故障を引き起こす可能性があります',\n        dropHelper: 'ここでアップロードされたファイルをドラッグアンドドロップするか、',\n        clickHelper: 'クリックしてアップロードします',\n        supportUpType:\n            'sql、sql.gz、tar.gz、.zip ファイル形式のみサポートしています。インポートする圧縮ファイルには、1つの.sqlファイルのみ、またはtest.sqlが含まれている必要があります',\n        currentStatus: '現在の状態',\n        baseParam: '基本パラメーター',\n        performanceParam: 'パフォーマンスパラメーター',\n        runTime: '起動時間',\n        connections: '合計接続',\n        bytesSent: 'バイトを送信します',\n        bytesReceived: '受信バイト',\n        queryPerSecond: 'クエリあたりのクエリ',\n        txPerSecond: '1秒あたりのTX',\n        connInfo: 'アクティブ/ピーク接続',\n        connInfoHelper: '値が大きすぎる場合は、「max_connections」を増やします。',\n        threadCacheHit: 'スレッドキャッシュがヒットします',\n        threadCacheHitHelper: '低すぎる場合は、「thread_cache_size」を増やします。',\n        indexHit: 'インデックスヒット',\n        indexHitHelper: '低すぎる場合は、「key_buffer_size」を増やします。',\n        innodbIndexHit: 'INNODBインデックスヒット率',\n        innodbIndexHitHelper: '低すぎる場合は、「innodb_buffer_pool_size」を増やします。',\n        cacheHit: 'キャッシュヒットのクエリ',\n        cacheHitHelper: '低すぎる場合は、「query_cache_size」を増やします。',\n        tmpTableToDB: 'ディスクへの一時テーブル',\n        tmpTableToDBHelper: '大きすぎる場合は、「tmp_table_size」を増やしてみてください。',\n        openTables: 'テーブルを開きます',\n        openTablesHelper: '「table_open_cache」の構成値は、この値以上に等しくなければなりません。',\n        selectFullJoin: '完全な結合を選択します',\n        selectFullJoinHelper: '値が0でない場合は、データテーブルのインデックスが正しいかどうかを確認します。',\n        selectRangeCheck: 'インデックスなしの結合の数',\n        selectRangeCheckHelper: '値が0でない場合は、データテーブルのインデックスが正しいかどうかを確認します。',\n        sortMergePasses: 'ソートされたマージの数',\n        sortMergePassesHelper: '値が大きすぎる場合は、「sort_buffer_size」を増やします。',\n        tableLocksWaited: 'テーブル番号をロックします',\n        tableLocksWaitedHelper:\n            '値が大きすぎる場合は、データベースのパフォーマンスを向上させることを検討してください。',\n        performanceTuning: 'パフォーマンスチューニング',\n        optimizationScheme: '最適化スキーム',\n        keyBufferSizeHelper: 'インデックスのバッファサイズ',\n        queryCacheSizeHelper: 'クエリキャッシュ。この関数が無効になっている場合は、このパラメーターを0に設定します。',\n        tmpTableSizeHelper: '一時的なテーブルキャッシュサイズ',\n        innodbBufferPoolSizeHelper: 'INNODBバッファサイズ',\n        innodbLogBufferSizeHelper: 'innodbログバッファサイズ',\n        sortBufferSizeHelper: '*接続、スレッドソートごとのバッファサイズ',\n        readBufferSizeHelper: '*接続、バッファサイズの読み取り',\n        readRndBufferSizeHelper: '*接続、ランダム読み取りバッファサイズ',\n        joinBufferSizeHelper: '*接続、アソシエーションテーブルキャッシュサイズ',\n        threadStackelper: '*接続、スレッドあたりのスタックサイズ',\n        binlogCacheSizeHelper: '* onnections、バイナリログキャッシュサイズ（4096の倍数）',\n        threadCacheSizeHelper: 'スレッドプールサイズ',\n        tableOpenCacheHelper: 'テーブルキャッシュ',\n        maxConnectionsHelper: 'マックス接続',\n        restart: '再起動',\n        slowLog: '遅いログ',\n        noData: 'まだ遅いログはありません。',\n        isOn: 'の上',\n        longQueryTime: 'しきい値',\n        thresholdRangeHelper: '正しいしきい値（1-600）を入力してください。',\n        timeout: 'タイムアウト(s)',\n        timeoutHelper: 'アイドル接続タイムアウト期間。0は、接続が継続的にオンになっていることを示します。',\n        maxclients: 'マックスクライアント',\n        requirepassHelper:\n            'この空白のままにして、パスワードが設定されていないことを示します。変更を個別に保存し、コンテナを再起動する必要があります！',\n        databases: 'データベースの数',\n        maxmemory: '最大メモリ使用量',\n        maxmemoryHelper: '0は制限がないことを示します。',\n        tcpPort: '現在のリスニングポート。',\n        uptimeInDays: '稼働している日。',\n        connectedClients: '接続されたクライアントの数。',\n        usedMemory: 'Redisの現在のメモリ使用。',\n        usedMemoryRss: 'オペレーティングシステムから要求されたメモリサイズ。',\n        usedMemoryPeak: 'Redisのピークメモリ消費。',\n        memFragmentationRatio: 'メモリフラグメンテーション比。',\n        totalConnectionsReceived: '実行以来、接続されているクライアントの総数。',\n        totalCommandsProcessed: '実行以降に実行されたコマンドの総数。',\n        instantaneousOpsPerSec: 'サーバーによって実行されるコマンドの数。',\n        keyspaceHits: 'データベースキーの回数が正常に見つかりました。',\n        keyspaceMisses: 'データベースキーを見つけようとする試みの失敗の数。',\n        hit: 'データベースキーヒット率を見つけます。',\n        latestForkUsec: '最後のfork（）操作に費やされたマイクロ秒数。',\n        redisCliHelper: '「Redis-Cli」サービスは検出されません。最初にサービスを有効にします。',\n        redisQuickCmd: 'Redis Quickコマンド',\n        recoverHelper: 'これにより、[{0}]でデータが上書きされます。続けたいですか？',\n        submitIt: 'データを上書きします',\n        baseConf: '基本',\n        allConf: '全て',\n        restartNow: '今すぐ再起動します',\n        restartNowHelper1:\n            '構成の変更が有効になった後、システムを再起動する必要があります。データを持続する必要がある場合は、最初に保存操作を実行します。',\n        restartNowHelper: 'これは、システムが再起動した後にのみ有効になります。',\n        persistence: '持続性',\n        rdbHelper1: '2番目、挿入',\n        rdbHelper2: 'データの部分',\n        rdbHelper3: '条件のいずれかを満たすと、RDBの持続性がトリガーされます。',\n        rdbInfo: 'ルールリストの値が1〜100000の範囲であることを確認してください',\n        containerConn: 'コンテナ接続',\n        copyConnURL: '接続 URL をコピー',\n        connAddress: '住所',\n        containerConnHelper:\n            'この接続アドレスは、Webサイトのランタイム（PHPなど）またはコンテナで実行されているアプリケーションで使用できます。',\n        remoteConn: '外部接続',\n        remoteConnHelper2: 'コンテナ環境以外または外部接続にはこのアドレスを使用してください。',\n        remoteConnHelper3:\n            'デフォルトアクセスアドレスはホストIPです。変更するには、パネル設定ページの「デフォルトアクセスアドレス」設定項目へ移動してください。',\n        localIP: 'ローカルIP',\n    },\n    aiTools: {\n        agents: {\n            agents: 'エージェント',\n            agent: 'エージェント',\n            account: 'モデルアカウント',\n            noAccountHint: '既存のモデルアカウントを選択するか、新規に追加してください。',\n            accountCount: 'モデルアカウント {0} 件',\n            syncAgents: '関連エージェントを同期',\n            syncAgentsHelper: 'このモデルアカウントを使用するエージェントの openclaw.json を更新',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: 'アプリバージョン',\n            webuiPort: 'WebUI ポート',\n            allowedOrigins: 'アクセスアドレス',\n            allowedOriginsHelper:\n                '1 行に 1 つずつ完全なアクセスアドレスを入力してください。HTTPS を推奨します。例: https://192.168.1.2:18789。デフォルトのアクセスアドレスが未設定の場合は手動で入力してください。',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: '少なくとも 1 つのアクセスアドレスを入力してください',\n            allowedOriginsInvalid: 'http(s)://host-or-ip[:port] の形式で入力してください',\n            provider: 'モデルプロバイダー',\n            apiKey: 'API キー',\n            baseUrl: 'ベースURL',\n            accountModels: 'Model Catalog',\n            accountModelsHelper: 'Configure the models this account exposes to OpenClaw for switching and settings',\n            accountModelsRequired: 'Configure at least one model',\n            accountModelsDuplicate: 'Duplicate models exist in the catalog',\n            modelPool: 'Model Pool',\n            modelPoolHelper:\n                'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',\n            modelInputTypes: 'Input Types',\n            reasoning: 'Reasoning Model',\n            token: 'トークン',\n            manualModel: '手動入力',\n            verified: '検証済み',\n            verifySkipped: '検証なし',\n            configTitle: 'Configuration',\n            settingsTab: 'Settings',\n            securityTab: 'Security',\n            otherTab: 'Other',\n            timeZone: 'タイムゾーン',\n            browserEnabled: 'Browser Enabled',\n            switchModelSuccess: 'Model switched successfully',\n            channelsTab: 'Channels',\n            wecom: 'WeCom',\n            dingtalk: 'DingTalk',\n            feishu: 'Feishu',\n            pluginNotInstalled: 'プラグインがインストールされていません。先にインストールしてください。',\n            dmPolicy: 'DM Policy',\n            groupPolicy: 'Group Policy',\n            policyPairing: 'Pairing',\n            policyAllowlist: 'Allowlist',\n            policyOpen: 'Open',\n            policyDisabled: 'Disabled',\n            botName: 'Bot Name',\n            botId: 'Bot ID',\n            appId: 'App ID',\n            appSecret: 'App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: 'DM Allowlist',\n            allowFromHelper: 'One sender ID per line. Used only when DM Policy is Allowlist.',\n            allowFromPlaceholder: 'One sender ID per line',\n            groupAllowFrom: 'Group Allowlist',\n            groupAllowFromHelper: 'One group ID per line. Used only when Group Policy is Allowlist.',\n            groupAllowFromPlaceholder: 'One group ID per line',\n            allowFromRequired: 'Enter at least one allowlist entry',\n            saveAndRestartGateway: 'Save and restart gateway',\n            pairingCode: 'Pairing Code',\n            pairingCodePlaceholder: 'Enter pairing code',\n            approvePairing: 'Approve Pairing',\n            feishuRequired: 'Please fill botName / appId / appSecret',\n            saveSuccess: 'Saved successfully',\n            pairingCodeRequired: 'Please enter pairing code',\n            pairingApproveSuccess: 'Pairing approved successfully',\n            customProviderHelper: 'カスタムモデルプロバイダーでは、アカウントの有効性を検証しません',\n            feishuSaveSuccess: 'Feishuに保存済み',\n        },\n        model: {\n            model: 'モデル',\n            localModel: 'ローカルモデル',\n            create: 'モデルを追加',\n            create_helper: 'を取得 \"{0}\"',\n            ollama_doc: 'Ollama の公式ウェブサイトを訪れて、さらに多くのモデルを検索して見つけることができます。',\n            container_conn_helper: 'コンテナ間のアクセスまたは接続にこのアドレスを使用',\n            ollama_sync: 'Ollamaモデルの同期中に、以下のモデルが存在しないことが判明しました。削除しますか？',\n            from_remote: 'このモデルは1Panelを介してダウンロードされておらず、関連するプルログはありません。',\n            no_logs: 'このモデルのプルログは削除されており、関連するログを表示できません。',\n            vllmVersionHelper: 'FusionXpark GB 10 サーバーでは -cu130 バージョンを選択してください。',\n        },\n        proxy: {\n            proxy: 'AI プロキシ強化',\n            proxyHelper1: 'ドメインをバインドし、HTTPS を有効にして通信のセキュリティを強化',\n            proxyHelper2: 'IP アクセスを制限し、パブリックインターネットでの露出を防止',\n            proxyHelper3: 'ストリーミングを有効にする',\n            proxyHelper4: '作成後、ウェブサイトリストで確認および管理できます',\n            proxyHelper5:\n                '有効にすると、アプリストア - インストール済み - Ollama - パラメータでポートの外部アクセスを無効にし、セキュリティを向上させることができます。',\n            proxyHelper6: 'プロキシ設定を無効にするには、ウェブサイトリストから削除できます。',\n            whiteListHelper: 'ホワイトリスト内のIPのみアクセスを許可する',\n        },\n        gpu: {\n            gpu: 'GPU 監視',\n            gpuHelper:\n                'システムが NVIDIA-SMI または XPU-SMI コマンドを検出しませんでした。確認して再試行してください！',\n            process: 'プロセス情報',\n            type: 'タイプ',\n            typeG: 'グラフィックス',\n            typeC: 'コンピュート',\n            typeCG: 'コンピュート+グラフィックス',\n            processName: 'プロセス名',\n            shr: '共有メモリ',\n            temperatureHelper: 'GPU 温度が高いと GPU 周波数が低下する可能性があります',\n            gpuUtil: 'GPU 使用率',\n            temperature: '温度',\n            performanceState: 'パフォーマンス状態',\n            powerUsage: '消費電力',\n            memoryUsage: 'メモリ使用率',\n            fanSpeed: 'ファン速度',\n            power: '電力',\n            powerCurrent: '現在の電力',\n            powerLimit: '電力上限',\n            memory: 'メモリ',\n            memoryUsed: '使用メモリ',\n            memoryTotal: '総メモリ',\n            percent: '使用率',\n            base: '基本情報',\n            driverVersion: 'ドライバーバージョン',\n            cudaVersion: 'CUDA バージョン',\n            processMemoryUsage: 'メモリ使用量',\n            performanceStateHelper: 'P0（最大パフォーマンス）から P12（最小パフォーマンス）まで',\n            busID: 'バスアドレス',\n            persistenceMode: '永続モード',\n            enabled: '有効',\n            disabled: '無効',\n            persistenceModeHelper: '永続モードはタスクへの応答がより迅速ですが、それに応じて待機電力消費も増加します',\n            displayActive: 'GPU 初期化',\n            displayActiveT: 'はい',\n            displayActiveF: 'いいえ',\n            ecc: 'エラー修正技術',\n            computeMode: '計算モード',\n            default: 'デフォルト',\n            exclusiveProcess: '排他プロセス',\n            exclusiveThread: '排他スレッド',\n            prohibited: '禁止',\n            defaultHelper: 'デフォルト: プロセスは同時実行可能',\n            exclusiveProcessHelper:\n                '排他プロセス: 1つのCUDAコンテキストのみがGPUを使用可能、ただし複数スレッドで共有可能',\n            exclusiveThreadHelper: '排他スレッド: CUDAコンテキスト内の1つのスレッドのみがGPUを使用可能',\n            prohibitedHelper: '禁止: プロセスの同時実行は許可されません',\n            migModeHelper: 'MIGインスタンスを作成するために使用され、ユーザーレイヤーでGPUの物理的隔離を実装します。',\n            migModeNA: 'サポートされていません',\n            current: 'リアルタイム監視',\n            history: '履歴記録',\n            notSupport: '現在のバージョンまたはドライバーはこのパラメータの表示をサポートしていません。',\n            processCount: 'プロセス数',\n        },\n        mcp: {\n            server: 'MCP サーバー',\n            baseUrl: '外部アクセスパス',\n            baseUrlHelper: '例: http://192.168.1.2:8000',\n            ssePath: 'SSE パス',\n            ssePathHelper: '例: /sse, 他のサーバーと重複しないように注意してください',\n            environment: '環境変数',\n            envKey: '変数名',\n            envValue: '変数値',\n            externalUrl: '外部接続アドレス',\n            operatorHelper: '{0} に {1} 操作を実行します、続行しますか？',\n            domain: 'デフォルトアクセスアドレス',\n            domainHelper: '例: 192.168.1.1 または example.com',\n            bindDomain: 'ウェブサイトをバインド',\n            commandPlaceHolder: '現在、npx およびバイナリスタートアップコマンドのみをサポートしています',\n            importMcpJson: 'MCP サーバー設定をインポート',\n            importMcpJsonError: 'mcpServers 構造が正しくありません',\n            bindDomainHelper:\n                'ウェブサイトをバインドした後、インストールされたすべての MCP サーバーのアクセスアドレスを変更し、ポートへの外部アクセスを閉じます',\n            outputTransport: '出力タイプ',\n            streamableHttpPath: 'ストリーミングパス',\n            streamableHttpPathHelper: '例：/mcp、他のサーバーと重複しないように注意してください',\n            npxHelper: 'npx またはバイナリで起動する mcp に適しています',\n            uvxHelper: 'uvx で起動する mcp に適しています',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: 'モデルディレクトリ',\n            commandHelper:\n                '外部アクセスが必要な場合は、コマンド内のポートをアプリケーションポートと同じに設定してください',\n            imageAlert:\n                'イメージサイズが大きいため、インストール前にサーバーにイメージを手動でダウンロードすることをお勧めします',\n            modelSpeedup: 'モデル加速を有効化',\n            modelType: 'モデルタイプ',\n        },\n    },\n    container: {\n        createByCommand: 'コマンドで作成',\n        importContainerBackupTip: 'コンテナバックアップファイルをインポートします。.tar.gz のみ対応です',\n        importComposeBackupTip: 'Compose バックアップファイルをインポートします。.tar.gz のみ対応です',\n        stopContainerBeforeBackup: 'バックアップ前にコンテナを停止',\n        stopComposeBeforeBackup: 'バックアップ前に Compose を停止',\n        stopBeforeBackupHelper:\n            '有効にすると、バックアップ前にコンテナまたは Compose サービスを停止し、完了後に自動で復元してデータ整合性を確保します。',\n        commandInput: 'コマンド入力',\n        commandRule: 'コンテナ作成用の正しい docker run コマンドを入力してください。',\n        commandHelper: 'このコマンドはサーバー上で実行され、コンテナを作成します。続行しますか？',\n        updateHelper1: 'このコンテナがアプリストアから取得されたことを検出しました。以下の2点にご注意ください:',\n        updateHelper2: '1.現在の変更内容は、アプリストアにインストールされているアプリケーションには同期されません。',\n        updateHelper3:\n            '2.インストールされているページでアプリケーションを変更すると、現在編集されているコンテンツが無効になります。',\n        updateHelper4: 'コンテナを編集するには再構築が必要であり、非存在データが失われます。続けたいですか？',\n        containerList: 'コンテナリスト',\n        operatorHelper: '{0}は次のコンテナで実行されます、続行しますか？',\n        operatorAppHelper:\n            '「{0}」操作は次のコンテナで実行され、実行中のサービスに影響を与える可能性があります。続けたいですか？',\n        containerDeleteHelper:\n            'コンテナがアプリストアから作成されたことを検出しました。コンテナを削除しても、1Panel から完全には削除されません。完全に削除するには、アプリストアの「インストール済み」または「実行環境」などのメニューに移動して操作してください。続行しますか？',\n        start: '始める',\n        stop: '停止',\n        restart: '再起動',\n        kill: '殺す',\n        pause: '一時停止',\n        unpause: '再開する',\n        rename: '名前を変更します',\n        remove: '取り除く',\n        removeAll: 'すべてを削除します',\n        containerPrune: 'プルーン',\n        containerPruneHelper1: 'これにより、停止状態にあるすべてのコンテナが削除されます。',\n        containerPruneHelper2:\n            'コンテナがアプリストアから取得された場合、クリーンアップを実行した後に、「アプリストア -> インストール済み」セクションに移動し、「再構築」ボタンをクリックして再インストールする必要があります。',\n        containerPruneHelper3: 'この操作は元に戻すことはできません。続けたいですか？',\n        imagePrune: 'プルーン',\n        imagePruneSome: 'クリーンラベル付けされています',\n        imagePruneSomeEmpty: '「none」タグのある画像はクリーニングできません。',\n        imagePruneSomeHelper: 'コンテナでは使用されていないタグ「None」で画像をクリーニングします。',\n        imagePruneAll: '未使用の清掃',\n        imagePruneAllEmpty: '未使用の画像をクリーニングすることはできません。',\n        imagePruneAllHelper: 'コンテナで使用されていない画像を清掃します。',\n        networkPrune: 'プルーン',\n        networkPruneHelper: 'これにより、すべての未使用ネットワークが削除されます。続けたいですか？',\n        volumePrune: 'プルーン',\n        volumePruneHelper: 'これにより、未使用のすべてのローカルボリュームが削除されます。続けたいですか？',\n        cleanSuccess: '操作は成功しました。このクリーンアップの数:{0}！',\n        cleanSuccessWithSpace:\n            '操作は成功しています。今回クリーニングされたディスクの数は{0}です。解放されたディスクスペースは{1}です！',\n        unExposedPort: '現在のポートマッピングアドレスは127.0.0.1であり、外部アクセスを有効にできません。',\n        upTime: '稼働時間',\n        fetch: 'フェッチ',\n        lines: '線',\n        linesHelper: '取得するには、正しい数のログを入力してください！',\n        downloadLinesHelper: 'ログダウンロード行数を選択または入力してください。',\n        lastDay: '最終日',\n        last4Hour: '最後の4時間',\n        lastHour: '最後の時間',\n        last10Min: '最後の10分',\n        cleanLog: 'クリーンログ',\n        downLogHelper1: 'これにより、Container {0}からすべてのログをダウンロードします。続けたいですか？',\n        downLogHelper2: 'これにより、コンテナ{0}から最近の{0}ログをダウンロードします。続けたいですか？',\n        cleanLogHelper: 'これには、コンテナを再起動する必要があり、元に戻すことはできません。続けたいですか？',\n        newName: '新しい名前',\n        source: 'リソースの使用',\n        cpuUsage: 'CPUの使用',\n        cpuTotal: 'CPU合計',\n        core: 'コア',\n        memUsage: 'メモリの使用',\n        memTotal: 'メモリ制限',\n        memCache: 'メモリキャッシュ',\n        loadSize: 'コンテナサイズを取得',\n        ip: 'IPアドレス',\n        cpuShare: 'CPU共有',\n        cpuShareHelper:\n            'コンテナエンジンは、CPU株に1024の基本値を使用します。それを増やして、コンテナにCPU時間を増やすことができます。',\n        inputIpv4: '例:192.168.1.1',\n        inputIpv6: '例:2001:0DB8:85A3:0000:0000:8A2E:0370:7334',\n        diskUsage: 'ディスク使用量',\n        localVolume: 'ローカルストレージボリューム',\n        buildCache: 'ビルドキャッシュ',\n        usage: '使用済み: {0}, 解放可能: {1}',\n        clean: '解放',\n        imageClean:\n            'イメージをクリーンアップすると、すべての未使用イメージが削除されます。この操作は元に戻せません。続行しますか？',\n        containerClean:\n            'コンテナをクリーンアップすると、停止中のすべてのコンテナ（アプリストアの停止アプリを含む）が削除されます。この操作は元に戻せません。続行しますか？',\n        sizeRw: 'コンテナレイヤーサイズ',\n        sizeRwHelper: 'コンテナ固有の書き込み可能レイヤーのサイズ',\n        sizeRootFs: '仮想サイズ',\n        sizeRootFsHelper: 'コンテナが依存するすべてのイメージレイヤー + コンテナレイヤーの合計サイズ',\n        containerFromAppHelper:\n            'このコンテナがアプリストアから取得されたことが検出されました。アプリの操作により、現在の編集が無効になる可能性があります。',\n        containerFromAppHelper1:\n            'インストールされているアプリケーションリストの[PARAM]ボタンをクリックして、編集ページを入力し、コンテナ名を変更します。',\n        command: '指示',\n        console: 'コンテナインタラクション',\n        tty: 'pseudo-tty（-t）を割り当てる',\n        openStdin: '添付されていなくてもstdinを開いたままにしてください（-i）',\n        custom: 'カスタム',\n        emptyUser: '空の場合、デフォルトとしてログインします',\n        privileged: '特権',\n        privilegedHelper:\n            'コンテナがホストに特定の特権操作を実行できるようにします。これにより、コンテナのリスクが増加する可能性があります。注意して使用してください！',\n        editComposeHelper:\n            '注意：環境変数は1panel.envファイルに保存され、composeでenv_fileを介して参照する必要があります。\\n変数はコンテナ内部でのみ有効であり、composeファイルの${VAR}置換には参加しません。',\n        upgradeHelper: 'リポジトリ名/画像名:画像バージョン',\n        upgradeWarning2: 'アップグレード操作では、コンテナを再構築する必要があります。続けたいですか？',\n        oldImage: '現在の画像',\n        targetImage: 'ターゲット画像',\n        sameImageContainer: '同一イメージコンテナ',\n        sameImageHelper: '同一イメージを使用するコンテナは選択後一括アップグレード可能',\n        imageLoadErr: 'コンテナの画像名は検出されません',\n        imageUpdateTagEmpty: '更新可能なイメージタグが検出されません',\n        appHelper:\n            'このコンテナはアプリストアから取得されたものであり、アップグレードによってサービスが利用不可になる可能性があります。',\n        input: '手動入力',\n        forcePull: '常に画像を引っ張ってください',\n        forcePullHelper: 'これにより、サーバー上の既存の画像が無視され、レジストリから最新の画像が引き出されます。',\n        imageUpdateHelper: 'レジストリの同名タグを確認し、更新があれば取得してローカルイメージを更新します。',\n        server: 'ホスト',\n        serverExample: '80、80-88、IP:80またはIP:80-88',\n        containerExample: '80または80-88',\n        exposePort: 'ポートを公開します',\n        exposeAll: 'すべてを公開します',\n        cmdHelper: '例:nginx -g \"daemon off;\"',\n        entrypointHelper: '例:docker-entrypoint.sh',\n        autoRemove: '自動削除',\n        cpuQuota: 'CPUコアの数',\n        memoryLimit: 'メモリ',\n        limitHelper: '0に設定すると、制限がないことを意味します。最大値は{0}です',\n        mount: 'マウント',\n        volumeOption: '音量',\n        hostOption: 'ホスト',\n        serverPath: 'サーバーパス',\n        containerDir: 'コンテナパス',\n        volumeHelper: 'ストレージボリュームのコンテンツが正しいことを確認してください',\n        networkEmptyHelper: 'コンテナネットワークの選択が正しいことを確認してください',\n        modeRW: 'rw',\n        modeR: 'r',\n        sharedLabel: '伝播モード',\n        private: 'プライベート',\n        privateHelper: 'コンテナ内とホストのマウント変更は互いに干渉しません',\n        rprivate: '再帰的プライベート',\n        rprivateHelper: 'コンテナ内のすべてのマウントはホストから完全に隔離されています',\n        shared: '共有',\n        sharedHelper: 'ホストとコンテナ内のマウント変更は互いに表示されます',\n        rshared: '再帰的共有',\n        rsharedHelper: 'ホストとコンテナ内のすべてのマウント変更が互いに表示されます',\n        slave: 'スレーブ',\n        slaveHelper: 'コンテナはホストのマウント変更を確認できますが、自身の変更はホストに影響しません',\n        rslave: '再帰的スレーブ',\n        rslaveHelper: 'コンテナ内のすべてのマウントはホストの変更を確認できますが、ホストに影響しません',\n        mode: 'モード',\n        env: '環境',\n        restartPolicy: 'ポリシーを再起動します',\n        always: 'いつも',\n        unlessStopped: '止まらない限り',\n        onFailure: 'オンフェイル（デフォルトで5回）',\n        no: '一度もない',\n        refreshTime: '間隔を更新します',\n        cache: 'キャッシュ',\n        image: '画像|画像',\n        imagePull: '引く',\n        imagePullHelper: '複数のイメージの選択をサポートし、各イメージ入力後にEnterキーを押して続行します',\n        imagePush: '押す',\n        imagePushHelper:\n            'このイメージに複数のタグが存在することが検出されました。プッシュ時に使用するイメージ名が以下であることを確認してください：{0}',\n        imageDelete: '画像削除',\n        repoName: 'コンテナレジストリ',\n        imageName: '画像名',\n        pull: '引く',\n        path: 'パス',\n        importImage: '輸入',\n        buildArgs: 'ビルド引数',\n        imageBuild: '建てる',\n        pathSelect: 'パス',\n        label: 'ラベル',\n        imageTag: '画像タグ',\n        imageTagHelper: '複数のイメージタグの設定をサポートし、各タグ入力後にEnterキーを押して続行します',\n        push: '押す',\n        fileName: 'ファイル名',\n        export: '輸出',\n        exportImage: '画像エクスポート',\n        size: 'サイズ',\n        tag: 'タグ',\n        tagHelper: '1行に1つ。たとえば、 nkey1 = value1  nkey2 = value2',\n        imageNameHelper: '画像名とタグ、例:nginx:最新',\n        cleanBuildCache: 'クリーンビルドキャッシュ',\n        delBuildCacheHelper:\n            'これにより、ビルド中に生成され、元に戻すことができないすべてのキャッシュされたアーティファクトが削除されます。続けたいですか？',\n        urlWarning: 'URLプレフィックスには、http://またはhttps://を含める必要はありません。変更してください。',\n        network: 'ネットワーク|ネットワーク',\n        networkHelper:\n            'これにより、一部のアプリケーションとランタイム環境が適切に機能しない場合があります。続けたいですか？',\n        networkName: '名前',\n        driver: 'ドライバ',\n        option: 'オプション',\n        attachable: '取り付け可能',\n        parentNetworkCard: '親ネットワークカード',\n        subnet: 'サブネット',\n        scope: 'IPスコープ',\n        gateway: 'ゲートウェイ',\n        auxAddress: 'IPを除外します',\n        volume: 'ボリューム|ボリューム',\n        volumeDir: 'ボリュームディレクトリ',\n        nfsEnable: 'NFSストレージを有効にします',\n        nfsAddress: '住所',\n        mountpoint: 'マウントポイント',\n        mountpointNFSHelper: '例えば/nfs、 /nfs-sh',\n        options: 'オプション',\n        repo: 'レジストリ',\n        httpRepoHelper: 'HTTPタイプのリポジトリを操作するにはDockerサービスの再起動が必要です。',\n        httpRepo:\n            'HTTPプロトコルを選択するには、Dockerサービスを再起動して不安定なレジストリに追加する必要があります。',\n        delInsecure: 'クレジットの削除',\n        delInsecureHelper: 'これにより、Dockerサービスを再起動して、不安定なレジストリから削除します。続けたいですか？',\n        downloadUrl: 'サーバ',\n        imageRepo: '画像リポジトリ',\n        repoHelper: 'ミラーリポジトリ/組織/プロジェクトが含まれていますか？',\n        auth: '認証が必要です',\n        mirrorHelper:\n            '複数のミラーがある場合、たとえばnewlinesを表示する必要があります:Nhttp://xxxxxxMDaocloudIo Nhttps://xxxxxxMirrorAliyuncsCom',\n        registrieHelper: '複数のプライベートリポジトリが存在する場合、たとえばnewlinesを表示する必要があります。',\n        compose: '構成|作曲',\n        composeFile: 'オーケストレーションファイル',\n        fromChangeHelper: 'ソースを切り替えると、現在の編集されたコンテンツがきれいになります。続けたいですか？',\n        composePathHelper: '構成ファイル保存パス:{0}',\n        composeHelper:\n            '1パネルの編集者またはテンプレートを介して作成された構成は、{0}/docker/composeディレクトリに保存されます。',\n        deleteFile: 'ファイルを削除します',\n        deleteComposeHelper:\n            '構成ファイルや永続的なファイルを含む、コンテナに関連するすべてのファイルを削除します。注意して進めてください！',\n        deleteCompose: 'この構成を削除します。',\n        composeDirectory: 'ディレクトリ',\n        template: 'テンプレート',\n        composeTemplate: 'テンプレートを作成|テンプレートを作成します',\n        content: 'コンテンツ',\n        contentEmpty: 'コンテンツを空にすることはできません。入力して再試行してください！',\n        containerNumber: 'コンテナ番号',\n        containerStatus: 'コンテナステータス',\n        exited: '終了',\n        running: 'ランニング ( {0} / {1} )',\n        composeDetailHelper: '構成は1パネルの外部に作成されます。開始および停止操作はサポートされていません。',\n        composeOperatorHelper: '{1}操作は{0}で実行されます。続けたいですか？',\n        composeDownHelper:\n            'これにより、{0}構成の下のすべてのコンテナとネットワークが停止して削除されます。続けたいですか？',\n        composeEnvHelper2:\n            'このオーケストレーションは1Panelアプリストアで作成されました。インストール済みアプリケーションで環境変数を変更してください。',\n        composeContentPlaceholder: 'ここに docker-compose ファイルの内容を記述または貼り付けてください',\n        setting: '設定|設定',\n        restartHelper: '設定変更を有効にするには Docker サービスの再起動が必要です',\n        operatorStatusHelper: 'これは「{0}」Dockerサービスになります。続けたいですか？',\n        dockerStatus: 'Dockerサービス',\n        daemonJsonPathHelper: '構成パスがdocker.serviceで指定されているものと同じであることを確認してください。',\n        mirrors: 'レジストリミラー',\n        mirrorsHelper2: '詳細については、公式文書を参照してください。',\n        registries: '不安定なレジストリ',\n        ipv6Helper:\n            'IPv6を有効にするときは、IPv6コンテナネットワークを追加する必要があります。特定の構成手順については、公式ドキュメントを参照してください。',\n        ipv6CidrHelper: 'IPv6はコンテナのプール範囲をアドレスします',\n        ipv6TablesHelper: 'iptablesルール用のDocker IPv6の自動構成。',\n        experimentalHelper:\n            'IP6Tablesを有効にするには、この構成をオンにする必要があります。それ以外の場合、IP6テーブルは無視されます',\n        cutLog: 'ログオプション',\n        cutLogHelper1: '現在の構成は、新しく作成されたコンテナのみに影響します。',\n        cutLogHelper2: '構成を有効にするために、既存のコンテナを再作成する必要があります。',\n        cutLogHelper3:\n            'コンテナを再現すると、データの損失が発生する可能性があることに注意してください。コンテナに重要なデータが含まれている場合は、再構築操作を実行する前に必ずバックアップしてください。',\n        maxSize: '最大サイズ',\n        maxFile: '最大ファイル',\n        liveHelper:\n            'デフォルトでは、Docker Daemonが終了すると、実行中のコンテナをシャットダウンします。デーモンを設定して、デーモンが利用できなくなった場合にコンテナが実行され続けるように設定できます。この機能は、Live Restoreと呼ばれます。Live Restoreオプションは、デーモンのクラッシュ、計画された停止、またはアップグレードにより、コンテナのダウンタイムを短縮するのに役立ちます。',\n        liveWithSwarmHelper: 'ライブレストアデーモン構成は、群れモードと互換性がありません。',\n        iptablesDisable: 'Iptablesを閉じます',\n        iptablesHelper1: 'DockerのiPtablesルールの自動構成。',\n        iptablesHelper2: 'IPTABLEを無効にすると、コンテナが外部ネットワークと通信できなくなります。',\n        daemonJsonPath: 'conf path',\n        serviceUnavailable: '現在、Dockerサービスは開始されていません。',\n        startIn: '開始する',\n        sockPath: 'UNIXドメインソケット',\n        sockPathHelper: 'Dockerデーモンとクライアントの間のコミュニケーションチャネル。',\n        sockPathHelper1: 'デフォルトパス:/var/run/docker-x.sock',\n        sockPathMsg:\n            'ソケットパスの設定を保存すると、Dockerサービスが利用できなくなる可能性があります。続けたいですか？',\n        sockPathErr: '正しいDockerソックファイルパスを選択または入力してください',\n        related: '関連している',\n        includeAppstore: 'アプリストアから取得したコンテナを表示',\n        excludeAppstore: 'アプリストアコンテナを隠す',\n        cleanDockerDiskZone: 'Dockerが使用するディスクスペースをクリーンアップします',\n        cleanImagesHelper: '（コンテナで使用されていないすべての画像をクリーンアップ）',\n        cleanContainersHelper: '（停止したすべての容器をクリーンアップ）',\n        cleanVolumesHelper: '（未使用のすべてのローカルボリュームをクリーンアップ）',\n        makeImage: '画像を作成します',\n        newImageName: '新しい画像名',\n        commitMessage: 'メッセージをコミットします',\n        author: '著者',\n        ifPause: '作成中にコンテナを一時停止します',\n        ifMakeImageWithContainer: 'このコンテナから新しい画像を作成しますか？',\n        finishTime: '前回の停止時間',\n        workingDir: '作業ディレクトリ',\n        resource: 'リソース',\n        macAddr: 'MACアドレス',\n        exportHelper: '選択したイメージを1つのtarにエクスポートします。',\n        goSetting: '編集へ',\n        mirrorsHelper: '',\n    },\n    cronjob: {\n        importHelper:\n            'インポート時に同名のスケジュールタスクは自動的にスキップされます。タスクはデフォルトで【無効】状態に設定され、データ関連付け異常時には【編集待ち】状態に設定されます。',\n        changeStatus: 'ステータスを変更します',\n        disableMsg: 'これにより、スケジュールされたタスクが自動的に実行されなくなります。続けたいですか？',\n        enableMsg: 'これにより、スケジュールされたタスクが自動的に実行されます。続けたいですか？',\n        taskType: 'タイプ',\n        record: '記録',\n        viewRecords: '記録',\n        shell: 'シェル',\n        stop: '手動終了',\n        stopHelper: 'この操作により現在のタスクの実行が強制停止されます。続行しますか？',\n        log: 'バックアップログ',\n        logHelper: 'バックアップシステムログ',\n        ogHelper1: '1.1パネルシステムログ',\n        logHelper2: '2。サーバーのSSHログインログ',\n        logHelper3: '3.すべてのサイトログ',\n        containerCheckBox: 'コンテナ内（コンテナコマンドを入力する必要はありません）',\n        containerName: 'コンテナ名',\n        ntp: '時間同期',\n        ntp_helper: 'ツールボックスのクイックセットアップページでNTPサーバーを構成できます。',\n        app: 'バックアップアプリ',\n        website: 'バックアップウェブサイト',\n        rulesHelper: '複数の除外ルールをサポート、英語のカンマ , で区切ります。例：*.log,*.sql',\n        lastRecordTime: '最後の実行時間',\n        all: '全て',\n        failedRecord: '失敗記録',\n        successRecord: '成功した記録',\n        database: 'バックアップデータベース',\n        backupArgs: 'バックアップ引数',\n        backupArgsHelper:\n            'リストにないバックアップ引数は手動で入力して選択できます。例：--no-data を入力し、ドロップダウンリストの最初のオプションを選択してください。',\n        singleTransaction:\n            '単一トランザクションを使用して InnoDB テーブルをバックアップします。大容量データのバックアップに適しています',\n        quick: 'テーブル全体をメモリにロードする代わりに、データを行単位で読み取ります。大容量データや低メモリマシンのバックアップに適しています',\n        skipLockTables: 'すべてのテーブルをロックせずにバックアップします。高並列データベースに適しています',\n        missBackupAccount: 'バックアップアカウントは見つかりませんでした',\n        syncDate: '同期時間',\n        clean: 'キャッシュクリーン',\n        curl: 'アクセスURL',\n        taskName: '名前',\n        cronSpec: 'トリガーサイクル',\n        cronSpecDoc:\n            'カスタム実行周期は【分 時 日 月 曜日】形式のみサポートしています（例: 0 0 * * *）。詳細は公式ドキュメントをご参照ください。',\n        cronSpecHelper: '正しい実行期間を入力します',\n        cleanHelper:\n            'この操作は、すべてのジョブ実行レコード、バックアップファイル、ログファイルを記録します。続けたいですか？',\n        directory: 'バックアップディレクトリ',\n        sourceDir: 'バックアップディレクトリ',\n        snapshot: 'システムスナップショット',\n        allOptionHelper:\n            '現在のタスク計画は、すべての[{0}]をバックアップすることです。直接ダウンロードは現時点ではサポートされていません。[{{0}]メニューのバックアップリストを確認できます。',\n        exclusionRules: '排他的ルール',\n        exclusionRulesHelper:\n            '除外ルールを選択または入力し、各セット入力後にEnterキーを押して続行します。除外ルールはこのバックアップのすべての圧縮操作に適用されます',\n        default_download_path: 'デフォルトのダウンロードリンク',\n        saveLocal: 'ローカルバックアップを保持します（クラウドストレージコピーの数と同じ）',\n        url: 'URLアドレス',\n        urlHelper: '正しいURLアドレスを入力してください',\n        targetHelper: 'バックアップアカウントは、パネル設定で維持されます。',\n        withImageHelper:\n            'アプリストアのイメージをバックアップしますが、スナップショットファイルのサイズが大きくなります。',\n        ignoreApp: 'アプリを除外',\n        withImage: 'アプリケーションイメージのバックアップ',\n        retainCopies: '記録を保持します',\n        retryTimes: 'リトライ回数',\n        timeout: 'タイムアウト',\n        ignoreErr: 'エラーを無視',\n        ignoreErrHelper: 'バックアップ中のエラーを無視し、全てのバックアップタスクを確実に実行します',\n        retryTimesHelper: '0は失敗後リトライしないことを意味します',\n        retainCopiesHelper: '実行記録とログのために保持するコピーの数',\n        retainCopiesHelper1: 'バックアップファイル用に保持するコピーの数',\n        retainCopiesUnit: 'コピー（表示）',\n        cronSpecRule: 'ライン{0}の実行期間形式は正しくありません。チェックしてもう一度やり直してください！',\n        perMonth: '毎月',\n        perWeek: '毎週',\n        perHour: '毎時間',\n        perNDay: '毎日毎日）',\n        perDay: '毎日',\n        perNHour: 'N時間ごと）',\n        perNMinute: '毎分（s）',\n        perNSecond: '毎秒毎回',\n        per: '毎',\n        day: '日',\n        dayUnit: 'd',\n        monday: '月曜日',\n        tuesday: '火曜日',\n        wednesday: '水曜日',\n        thursday: '木曜日',\n        friday: '金曜日',\n        saturday: '土曜日',\n        sunday: '日曜日',\n        shellContent: 'スクリプト',\n        errRecord: '誤ったロギング',\n        errHandle: 'cronjob実行障害',\n        noRecord: 'Cronジョブをトリガーすると、ここにレコードが表示されます。',\n        cleanData: 'クリーンデータ',\n        cleanRemoteData: 'リモートデータを削除',\n        cleanDataHelper: 'このタスク中に生成されたバックアップファイルを削除します。',\n        noLogs: 'タスク出力はまだありません...',\n        errPath: 'バックアップパス[{0}]エラー、ダウンロードできません！',\n        cutWebsiteLog: 'ウェブサイトのログローテーション',\n        cutWebsiteLogHelper: '回転したログファイルは、1パネルのバックアップディレクトリにバックアップされます。',\n        syncIpGroup: 'WAF IP グループを同期',\n        requestExpirationTime: 'リクエストの有効期限（時間）のアップロード',\n        unitHours: 'ユニット:時間',\n        alertTitle: '計画タスク -  {0}「{1}」タスク障害アラート',\n        library: {\n            script: 'スクリプト',\n            syncNow: '今すぐ同期',\n            turnOnSync: '自動同期を有効化',\n            turnOnSyncHelper: '自動同期を有効にすると、毎日未明の時間帯に自動同期が実行されます',\n            turnOffSync: '自動同期を無効化',\n            turnOffSyncHelper: '自動同期を無効にするとスクリプトの同期が遅れる可能性がありますが、よろしいですか？',\n            isInteractive: '対話型',\n            interactive: '対話型スクリプト',\n            interactiveHelper: '実行中にユーザー入力が必要で、スケジュールタスクでは使用できません。',\n            library: 'スクリプトライブラリ',\n            create: 'スクリプトを追加',\n            edit: 'スクリプトを編集',\n            groupHelper:\n                'スクリプトの特徴に基づいて異なるグループを設定することで、スクリプトのフィルタリング操作をより迅速に行うことができます。',\n            handleHelper: '{0} で {1} スクリプトを実行します。続行しますか？',\n            noSuchApp:\n                '{0} サービスが検出されませんでした。スクリプトライブラリを使って素早くインストールしてください！',\n            syncHelper:\n                'システムスクリプトライブラリを同期します。この操作はシステムスクリプトのみ影響します。続行しますか？',\n            remoteLibrary: 'リモートスクリプトライブラリ',\n        },\n        nextTime: '次の5回の実行',\n        logHelper1: '1. 1Panel システムログ',\n        backupContent: 'バックアップ内容',\n        cronSpecRule2: '実行周期の形式が不正です。確認して再試行してください。',\n        perMonthHelper: '毎月 {0} 日 {1}:{2} に実行',\n        perWeekHelper: '毎週 {0} {1}:{2} に実行',\n        perDayHelper: '毎日 {0}:{1} に実行',\n        perHourHelper: '毎時 {0} 分に実行',\n        perNDayHelper: '毎 {0} 日 {1}:{2} に実行',\n        perNHourHelper: '毎 {0} 時間 {1} に実行',\n        perNMinuteHelper: '毎 {0} 分に実行',\n        perNSecondHelper: '毎 {0} 秒に実行',\n        executor: '実行者',\n        cleanLog: 'ログ自動削除',\n        cleanLogscope: 'ログ保持',\n    },\n    monitor: {\n        globalFilter: 'グローバルフィルター',\n        enableMonitor: '監視ステータス',\n        storeDays: '保存日数',\n        defaultNetwork: 'デフォルトネットワークカード',\n        defaultNetworkHelper: '監視および概要インターフェースに表示されるデフォルトのネットワークカードオプション',\n        defaultIO: 'デフォルトディスク',\n        defaultIOHelper: '監視および概要インターフェースに表示されるデフォルトのディスクオプション',\n        cleanMonitor: '監視記録をクリア',\n        cleanHelper: 'この操作により、GPUを含むすべての監視記録がクリアされます。続行しますか？',\n        avgLoad: 'ロード平均',\n        loadDetail: '詳細を読み込みます',\n        resourceUsage: '利用',\n        networkCard: 'ネットワークインターフェイス',\n        read: '読む',\n        write: '書く',\n        readWriteCount: 'I/O操作',\n        readWriteTime: 'I/Oレイテンシ',\n        today: '今日',\n        yesterday: '昨日',\n        lastNDay: '過去 {0} 日間',\n        lastNMonth: '過去 {0} ヶ月間',\n        lastHalfYear: '過去半年間',\n        memory: 'メモリ',\n        percent: '割合',\n        cache: 'キャッシュ',\n        disk: 'ディスク',\n        network: 'ネットワーク',\n        up: '上',\n        down: '下',\n        interval: '収集間隔',\n        intervalHelper: '適切な監視収集間隔を入力してください（10秒 - 12時間）',\n    },\n    terminal: {\n        local: 'ローカル',\n        defaultConn: 'デフォルト接続',\n        defaultConnHelper:\n            'この操作は【{0}】のターミナルを開いた後、自動的にノードターミナルに接続します。続行しますか？',\n        withReset: '接続情報をリセット',\n        localConnJump:\n            'デフォルト接続情報は【ターミナル - 設定】で管理されています。接続に失敗した場合はこちらで編集してください！',\n        localHelper: 'ローカル名はシステムのローカル識別にのみ使用されます。',\n        connLocalErr: '自動的に認証できない場合は、ローカルサーバーのログイン情報を入力してください。',\n        testConn: 'テスト接続',\n        saveAndConn: '保存して接続します',\n        connTestOk: '利用可能な接続情報',\n        connTestFailed: '接続は利用できません。接続情報を確認してください。',\n        host: 'ホスト|ホスト',\n        createConn: '新しい接続',\n        manageGroup: 'グループを管理します',\n        noHost: 'ホストはありません',\n        groupChange: 'グループを変更します',\n        expand: 'すべて拡張します',\n        fold: 'すべての契約',\n        batchInput: 'バッチ処理',\n        quickCommand: 'クイックコマンド|クイックコマンド',\n        noSuchCommand:\n            'インポートしたCSVファイルにクイックコマンドデータが見つかりませんでした。確認して再試行してください！',\n        quickCommandHelper: '「端末 - >端子」の下部にあるクイックコマンドを使用できます。',\n        groupDeleteHelper:\n            'グループが削除された後、グループ内のすべての接続がデフォルトグループに移行されます。続けたいですか？',\n        command: '指示',\n        quickCmd: 'クイックコマンド',\n        addHost: '追加',\n        localhost: 'localhost',\n        ip: '住所',\n        authMode: '認証',\n        passwordMode: 'パスワード',\n        rememberPassword: '認証情報を忘れないでください',\n        keyMode: 'privatekey',\n        key: '秘密鍵',\n        keyPassword: '秘密キーパスワード',\n        emptyTerminal: '現在接続されている端子はありません。',\n        lineHeight: '行の高さ',\n        letterSpacing: '文字間隔',\n        fontSize: 'フォントサイズ',\n        fontFamily: 'フォントセット',\n        fontFamilySupportHelper:\n            'フォントを選択または入力できます。適用可否はブラウザ側OSにそのフォントがインストールされているかに依存し、未インストール時は自動でフォールバックします。空の場合はデフォルトフォントを使用します。',\n        backgroundColor: '背景色',\n        foregroundColor: '前景色',\n        cursorBlink: 'カーソル点滅',\n        cursorStyle: 'カーソルスタイル',\n        cursorUnderline: '下線',\n        cursorBlock: 'ブロック',\n        cursorBar: 'バー',\n        scrollback: 'スクロールバック',\n        scrollSensitivity: 'スクロール感度',\n        aiStatus: 'AI Terminal',\n        aiSettings: 'AI Terminal Settings',\n        aiAccountHelper:\n            '選択したモデルアカウントでコマンドを生成して補完します。Ollama や vLLM などのローカルモデルはカスタムモデルアカウントを使用してください。',\n        aiPrefix: 'Trigger Prefix',\n        aiPrefixHelper:\n            'When a line starts with this prefix and you press Enter, AI command generation will be triggered, for example # or //ai.',\n        aiRiskCommands: 'Risk Command Interception',\n        aiRiskCommandsHelper:\n            'Generated commands matching any of these fragments will be blocked and filled back as comments. Supports add, edit, and delete.',\n        aiAddRiskCommand: 'Add Risk Command',\n        aiRemoveRiskCommand: 'Delete',\n        aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',\n        aiPrefixAsciiVisible:\n            'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',\n        saveHelper: '現在のターミナル設定を保存してもよろしいですか？',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: '一部のインストールおよび使用に関する問題については、以下を参照してください',\n        },\n        swap: {\n            swap: 'パーティションを交換します',\n            swapHelper1: 'スワップのサイズは、特定の要件に基づいて調整可能な物理メモリの1〜2倍である必要があります。',\n            swapHelper2:\n                'スワップファイルを作成する前に、スワップファイルサイズが対応するディスクスペースを占有するため、システムディスクに十分な利用可能なスペースがあることを確認してください。',\n            swapHelper3:\n                'スワップはメモリの圧力を軽減するのに役立ちますが、それは代替案にすぎません。スワップに過度に依存すると、システムのパフォーマンスが低下する可能性があります。メモリの増加を優先したり、アプリケーションメモリの使用量を最適化することをお勧めします。',\n            swapHelper4: '通常のシステム操作を確保するために、スワップの使用を定期的に監視することをお勧めします。',\n            swapDeleteHelper:\n                'この操作は、スワップパーティション{0}を削除します。システムセキュリティ上の理由から、対応するファイルは自動的に削除されません。削除が必要な場合は、手動で続行してください！',\n            saveHelper: '最初に現在の設定を保存してください！',\n            saveSwap: '現在の構成を保存すると、スワップパーティション{0}サイズを{1}に調整します。続けたいですか？',\n            swapMin: '最小パーティションサイズは40 kbです。変更してもう一度やり直してください！',\n            swapMax: 'パーティションサイズの最大値は{0}です。変更してもう一度やり直してください！',\n            swapOff: '最小パーティションサイズは40 kbです。0に設定すると、スワップパーティションが無効になります。',\n        },\n        device: {\n            dnsHelper: 'DNSサーバー',\n            dnsAlert:\n                '注意！/etc/resolv.confファイルの構成を変更すると、システムが再起動した後、ファイルがデフォルト値に復元されます。',\n            dnsHelper1:\n                '複数のDNSエントリがある場合は、新しい行に表示する必要があります。例えば、 n114.114.114.114  n8.8.8.8',\n            hostsHelper: 'ホスト名解像度',\n            hosts: 'ドメイン',\n            hostAlert: '隠されたコメントレコード、すべての構成ボタンをクリックして表示または設定してください',\n            toolbox: 'クイック設定',\n            hostname: 'ホスト名',\n            passwd: 'システムパスワード',\n            passwdHelper: '入力文字は$ and＆＆を含めることはできません',\n            timeZone: 'タイムゾーン',\n            localTime: 'サーバー時間',\n            timeZoneChangeHelper: 'システムタイムゾーンを変更するには、サービスを再起動する必要があります。続く？',\n            timeZoneHelper:\n                '「TimeDatectl」コマンドをインストールしない場合、タイムゾーンを変更することはできません。システムはそのコマンドを使用してタイムゾーンを変更するためです。',\n            timeZoneCN: '北京',\n            timeZoneAM: 'ロサンゼルス',\n            timeZoneNY: 'ニューヨーク',\n            ntpALi: 'アリババ',\n            ntpGoogle: 'グーグル',\n            syncSite: 'NTPサーバー',\n            hostnameHelper:\n                'ホスト名の変更は、「hostnamectl」コマンドに依存します。コマンドがインストールされていない場合、変更が失敗する可能性があります。',\n            userHelper:\n                'ユーザー名は、取得の「whoami」コマンドに依存します。コマンドがインストールされていない場合、検索が失敗する可能性があります。',\n            passwordHelper:\n                'パスワードの変更は、「chpasswd」コマンドに依存します。コマンドがインストールされていない場合、変更が失敗する可能性があります。',\n            hostHelper: '提供されたコンテンツには空の値があります。変更後に確認して再試行してください！',\n            dnsCheck: 'テストの可用性',\n            dnsOK: 'DNS構成情報が利用可能です！',\n            dnsTestFailed: 'DNS構成情報は利用できません。',\n            syncSiteHelper: 'システム時刻を {0} と同期しますか？',\n        },\n        fail2ban: {\n            sshPort: 'SSHポートを聞いてください',\n            sshPortHelper: '現在のFAL2BANは、ホストのSSH接続ポートに耳を傾けます',\n            unActive: '現在、Fail2Banサービスは有効になっていません。',\n            operation: 'fail2banサービスで操作「{0}」を実行します。続けたいですか？',\n            fail2banChange: 'fail2ban構成の変更',\n            ignoreHelper: 'AllowListのIPリストは、ブロックについて無視されます。続けたいですか？',\n            bannedHelper: 'ブロックリストのIPリストは、サーバーによってブロックされます。続けたいですか？',\n            maxRetry: '最大再試行',\n            banTime: '禁止時間',\n            banTimeHelper: 'デフォルトの禁止時間は10分、-1は永続的な禁止を示します',\n            banTimeRule: '有効な禁止時間または-1を入力してください',\n            banAllTime: '恒久的な禁止',\n            findTime: '発見期間',\n            banAction: '禁止措置',\n            banActionOption: '{0}を使用して指定されたIPアドレスを禁止',\n            allPorts: '（すべてのポート）',\n            ignoreIP: 'IP AllowList',\n            bannedIP: 'IPブロックリスト',\n            logPath: 'ログパス',\n            logPathHelper: 'デフォルトは/var/log/secureまたは/var/log/auth.logです',\n        },\n        ftp: {\n            ftp: 'FTPアカウント|FTPアカウント',\n            notStart: 'FTP Serviceは現在実行されていません。最初に開始してください！',\n            operation: 'これにより、FTPサービスで「{0}」操作が実行されます。続けたいですか？',\n            noPasswdMsg: '現在のFTPアカウントパスワードを取得できません。パスワードを設定して再試行してください！',\n            enableHelper: '選択したFTPアカウントを有効にすると、アクセス許可が復元されます。続けたいですか？',\n            disableHelper: '選択したFTPアカウントを無効にすると、アクセス許可が取り消されます。続けたいですか？',\n            syncHelper: 'サーバーとデータベースの間でFTPアカウントデータを同期します。続けたいですか？',\n            dirSystem:\n                'このディレクトリはシステム予約領域です。変更するとシステムがクラッシュする可能性があります。修正して再試行してください！',\n            dirHelper: 'FTPを有効にするにはディレクトリ権限の変更が必要です。慎重に選択してください',\n            dirMsg: 'FTPを有効にすると{0}ディレクトリ全体の権限が変更されます。続行しますか？',\n        },\n        clam: {\n            clam: 'ウイルススキャン',\n            cron: 'スケジュールされたスキャン',\n            cronHelper: 'プロフェッショナルバージョンは、スケジュールされたスキャン機能をサポートしています',\n            specErr: '実行スケジュールフォーマットエラー、チェックして再試行してください！',\n            disableMsg:\n                'スケジュールされた実行を停止すると、このスキャンタスクが自動的に実行されなくなります。続けたいですか？',\n            enableMsg:\n                'スケジュールされた実行を有効にすることで、このスキャンタスクは定期的に自動的に実行できます。続けたいですか？',\n            showFresh: '署名のアップデーターサービスを表示します',\n            hideFresh: '署名のアップデーターサービスを非表示にします',\n            clamHelper:\n                'Clamavの最小推奨構成は、3ギブ以上のRAM、2.0 GHz以上のシングルコアCPU、および少なくとも5 GIBの利用可能なハードディスクスペースです。',\n            notStart: 'Clamav Serviceは現在実行されていません。最初に開始してください！',\n            removeRecord: 'ペポートファイルを削除します',\n            noRecords: '[トリガー]ボタンをクリックしてスキャンを開始すると、ここにレコードが表示されます。',\n            removeInfected: 'ウイルスファイルを削除します',\n            removeInfectedHelper:\n                'サーバーのセキュリティと通常の操作を確保するために、タスク中に検出されたウイルスファイルを削除します。',\n            clamCreate: 'スキャンルールを作成します',\n            infectedStrategy: '感染した戦略',\n            removeHelper: 'ウイルスファイルを削除して、慎重に選択してください！',\n            move: '動く',\n            moveHelper: 'ウイルスファイルを指定されたディレクトリに移動します',\n            copyHelper: 'ウイルスファイルを指定されたディレクトリにコピーします',\n            none: '何もしません',\n            noneHelper: 'ウイルスファイルにアクションを実行しません',\n            scanDir: 'スキャンディレクトリ',\n            infectedDir: '感染したディレクトリ',\n            scanDate: 'スキャン日',\n            scanResult: 'テールをスキャンします',\n            tail: '線',\n            infectedFiles: '感染したファイル',\n            log: '詳細',\n            clamConf: 'クラマブデーモン',\n            clamLog: '@:toolbox.clam.clamconfログ',\n            freshClam: 'フレッシュクラム',\n            freshClamLog: '@:toolbox.clam.freshclamログ',\n            alertHelper: 'プロフェッショナル版は、定期スキャンとSMSアラート機能をサポートしています',\n            alertTitle: 'ウイルススキャンタスク（{0}」感染したファイルアラートが検出されました',\n        },\n    },\n    logs: {\n        core: 'パネルサービス',\n        agent: 'ノード監視',\n        panelLog: 'パネルログ',\n        operation: '操作ログ',\n        login: 'ログインログ',\n        loginIP: 'ログインIP',\n        loginAddress: 'ログインアドレス',\n        loginAgent: 'ログインエージェント',\n        loginStatus: '状態',\n        system: 'システムログ',\n        deleteLogs: 'クリーンログ',\n        resource: 'リソース',\n        detail: {\n            dashboard: '概要',\n            ai: 'AI',\n            groups: 'グループ',\n            hosts: 'ホスト',\n            apps: 'アプリケーション',\n            openresty: 'OpenResty',\n            websites: 'ウェブサイト',\n            containers: 'コンテナ',\n            files: 'ファイル管理',\n            runtimes: 'ランタイム',\n            process: 'プロセス管理',\n            toolbox: 'ツールボックス',\n            backups: 'バックアップ / 復元',\n            tampers: '改ざん防止',\n            xsetting: 'インターフェース設定',\n            logs: 'ログ監査',\n            settings: 'パネル設定',\n            cronjobs: 'スケジュールされたタスク',\n            databases: 'データベース',\n            waf: 'WAF',\n            licenses: 'ライセンス',\n            nodes: 'ノード',\n            commands: 'クイックコマンド',\n        },\n        websiteLog: 'ウェブサイトログ',\n        runLog: 'ログを実行します',\n        errLog: 'エラーログ',\n        task: 'タスクログ',\n        taskName: 'タスク名',\n        taskRunning: '実行中',\n    },\n    file: {\n        fileDirNum: '{0} 個のディレクトリ、{1} 個のファイル、',\n        currentDir: '現在のディレクトリ',\n        dir: 'フォルダ',\n        upload: 'アップロード',\n        uploadFile: '@:file.upload@.lower:file.file',\n        uploadDirectory: '@:file.upload@.lower:file.dir',\n        download: 'ダウンロード',\n        fileName: 'ファイル名',\n        search: '検索',\n        mode: '権限',\n        editPermissions: '編集@:file.mode',\n        owner: '所有者',\n        file: 'ファイル',\n        remoteFile: 'リモートダウンロード',\n        share: '共有',\n        sync: 'データ同期',\n        size: 'サイズ',\n        updateTime: '修正',\n        rename: '名前を変更します',\n        role: '権限',\n        info: '属性',\n        linkFile: 'ソフトリンク',\n        batchoperation: 'バッチ操作',\n        shareList: '共有リスト',\n        zip: '圧縮',\n        group: 'グループ',\n        path: 'パス',\n        public: 'その他',\n        setRole: '設定権限',\n        link: 'ファイルリンク',\n        rRole: '読む',\n        wRole: '書く',\n        xRole: '実行可能',\n        name: '名前',\n        compress: '圧縮',\n        deCompress: '減圧',\n        compressType: '圧縮形式',\n        compressDst: 'パスを圧縮します',\n        replace: '既存のファイルを上書きします',\n        compressSuccess: '正常に圧縮されました',\n        deCompressSuccess: '減圧は成功しました',\n        deCompressDst: 'パスを減圧します',\n        linkType: 'リンクタイプ',\n        softLink: 'ソフトリンク',\n        hardLink: 'ハードリンク',\n        linkPath: 'リンクパス',\n        selectFile: '[ファイル]を選択します',\n        downloadUrl: 'リモートURL',\n        downloadStart: 'ダウンロードが始まりました',\n        moveSuccess: '正常に移動しました',\n        copySuccess: '正常にコピーされました',\n        pasteMsg: '対象ディレクトリの右上にある「貼り付け」ボタンをクリックしてください',\n        move: '動く',\n        calculate: '計算します',\n        remark: '備考',\n        setRemark: '備考を設定',\n        remarkPrompt: '備考を入力してください',\n        remarkPlaceholder: '備考',\n        remarkToggle: '備考',\n        remarkToggleTip: 'ファイルの備考を読み込む',\n        canNotDeCompress: 'このファイルを解凍できません',\n        uploadSuccess: '正常にアップロードします',\n        downloadProcess: '進捗状況をダウンロードします',\n        downloading: 'ダウンロード...',\n        infoDetail: 'ファイルプロパティ',\n        root: 'ルートディレクトリ',\n        list: 'ファイルリスト',\n        sub: 'サブフォルダー',\n        downloadSuccess: 'ダウンロードに成功しました',\n        theme: 'テーマ',\n        language: '言語',\n        eol: '行の終わり',\n        copyDir: 'コピー',\n        paste: 'ペースト',\n        changeOwner: 'ユーザーグループとユーザーグループを変更します',\n        containSub: '許可変更を再帰的に適用します',\n        ownerHelper:\n            'PHP運用環境のデフォルトユーザー:ユーザーグループは1000:1000です。コンテナの内側と外側のユーザーが矛盾を示すのは普通です',\n        searchHelper: '*などのワイルドカードをサポート',\n        uploadFailed: '[{0}] ファイルのアップロードに失敗しました',\n        fileUploadStart: 'アップロード[{0}]...',\n        currentSelect: '現在の選択:',\n        unsupportedType: 'サポートされていないファイルタイプ',\n        deleteHelper: '次のファイルを削除したいですか？デフォルトでは、削除後にリサイクルビンに入ります',\n        fileHelper: '注意:1. 検索結果は並べ替え機能をサポートしていません 2. フォルダはサイズで並べ替えできません。',\n        forceDeleteHelper: 'ファイルを永久に削除します（リサイクルビンを入力せずに、直接削除します）',\n        recycleBin: 'ビンをリサイクルします',\n        sourcePath: 'オリジナルパス',\n        deleteTime: '時間を削除します',\n        confirmReduce: '次のファイルを復元したいですか？',\n        reduceSuccess: '成功して復元します',\n        reduce: '削減',\n        reduceHelper: '同じ名前のファイルまたはディレクトリが元のパスに存在する場合、上書きされます。続けたいですか？',\n        clearRecycleBin: 'クリーン',\n        clearRecycleBinHelper: 'リサイクルビンを掃除しますか？',\n        favorite: 'お気に入り',\n        removeFavorite: 'お気に入りから取り外しますか？',\n        addFavorite: 'お気に入りの追加/削除',\n        clearList: 'クリーンリスト',\n        deleteRecycleHelper: '次のファイルを永続的に削除する必要がありますか？',\n        typeErrOrEmpty: '[{0}]ファイルタイプは間違っているか、空のフォルダーです',\n        dropHelper: 'ここにアップロードするファイルをドラッグします',\n        fileRecycleBin: 'リサイクルビンを有効にします',\n        fileRecycleBinMsg: '{0}リサイクルビン',\n        wordWrap: '自動的にラップします',\n        deleteHelper2: '選択したファイルを削除する必要がありますか？削除操作をロールバックすることはできません',\n        ignoreCertificate: '不安定なサーバー接続を許可します',\n        ignoreCertificateHelper:\n            '不安定なサーバー接続を可能にすると、データが漏れたり改ざんしたりする可能性があります。ダウンロードソースを信頼する場合にのみ、このオプションを使用します。',\n        uploadOverLimit: 'ファイルの数は1000を超えています！圧縮してアップロードしてください',\n        clashDidNotSupport: 'ファイル名は、.1panel_clashを含むことを禁止されています',\n        clashDeleteAlert: '「リサイクルビン」フォルダーを削除することはできません',\n        clashOpenAlert: '「リサイクルビン」ボタンをクリックして、リサイクルビンディレクトリを開きます',\n        right: 'フォワード',\n        back: '戻る',\n        top: '戻って行きます',\n        up: '戻って行きます',\n        openWithVscode: 'VSコードで開く',\n        vscodeHelper:\n            'VSコードがローカルにインストールされ、SSHリモートプラグインが構成されていることを確認してください',\n        saveContentAndClose: 'ファイルが変更されましたが、保存して閉じたいですか？',\n        saveAndOpenNewFile: 'ファイルが変更されましたが、新しいファイルを保存して開きますか？',\n        noEdit: 'ファイルは変更されておらず、これを行う必要はありません！',\n        noNameFolder: '無題のフォルダー',\n        noNameFile: '無題のファイル',\n        minimap: 'コードミニマップ',\n        fileCanNotRead: 'ファイルは読み取れません',\n        previewTruncated: 'ファイルが大きすぎるため、末尾の内容のみ表示しています',\n        previewEmpty: 'ファイルが空であるか、テキストファイルではありません',\n        previewLargeFile: 'プレビュー',\n        panelInstallDir: '1Panelインストールディレクトリは削除できません',\n        wgetTask: 'ダウンロードタスク',\n        existFileTitle: '同名ファイルの警告',\n        existFileHelper: 'アップロードしたファイルに同じ名前のファイルが含まれています。上書きしますか？',\n        existFileSize: 'ファイルサイズ（新しい -> 古い）',\n        existFileDirHelper: '選択したファイル/フォルダーには同じ名前のものが既に存在します。慎重に操作してください！',\n        coverDirHelper: '上書きするフォルダを選択すると、対象パスにコピーされます！',\n        noSuchFile: 'ファイルまたはディレクトリが見つかりませんでした。確認して再試行してください。',\n        setting: '設定',\n        showHide: '隠しファイルを表示',\n        noShowHide: '隠しファイルを表示しない',\n        cancelUpload: 'アップロードをキャンセル',\n        cancelUploadHelper: 'アップロードをキャンセルするかどうか、キャンセル後、アップロードリストはクリアされます。',\n        keepOneTab: '少なくとも1つのタブを保持してください',\n        notCanTab: 'これ以上タブを追加できません',\n        convert: 'フォーマットを変換',\n        converting: 'に変換中',\n        fileCanNotConvert: 'このファイルはフォーマット変換に対応していません',\n        formatType: 'フォーマットタイプ',\n        sourceFormat: '元のフォーマット',\n        sourceFile: '元ファイル',\n        saveDir: '保存ディレクトリ',\n        deleteSourceFile: '元ファイルを削除するかどうか',\n        convertHelper: '選択したファイルを別のフォーマットに変換します',\n        convertHelper1: '変換するファイルを選択してください',\n        execConvert: '変換を開始します。タスクセンターで変換ログを確認できます',\n        convertLogs: '変換ログ',\n        formatConvert: 'フォーマット変換',\n    },\n    ssh: {\n        autoStart: 'オートスタート',\n        enable: 'AutoStartを有効にします',\n        disable: 'AutoStartを無効にします',\n        sshAlert:\n            'リストデータは、ログイン日に基づいてソートされます。タイムゾーンを変更したり、他の操作を実行したりすると、ログインログの日付が逸脱を引き起こす可能性があります。',\n        sshAlert2:\n            '「ツールボックス」で「Fail2ban」を使用して、ブルートフォース攻撃を試みるIPアドレスをブロックすることができます。これにより、ホストのセキュリティが向上します。',\n        sshOperate: 'SSHサービスの操作「{0}」が実行されます。続けたいですか？',\n        sshChange: 'SSH設定',\n        sshChangeHelper: 'このアクションは「{0}」が「{1}」に変更されました。続けたいですか？',\n        sshFileChangeHelper:\n            '構成ファイルを変更すると、サービスの可用性が発生する場合があります。この操作を実行するときは注意してください。続けたいですか？',\n        port: 'ポート',\n        portHelper: 'SSHサービスが耳を傾けるポートを特定します。',\n        listenAddress: '住所を聞いてください',\n        allV4V6: '0.0.0.0: {0 }(IPv4）および::: {0}（IPv6）',\n        listenHelper:\n            'IPv4とIPv6の両方の設定を空白のままにしておくと、「0.0.0.0:0:0:0}」と「::: {0}（IPv6）」で聞きます。',\n        addressHelper: 'SSHサービスが耳を傾けるアドレスを指定します。',\n        permitRootLogin: 'ルートユーザーログインを許可します',\n        rootSettingHelper: 'ルートユーザーのデフォルトのログインメソッドは「SSHログインを許可」です。',\n        rootHelper1: 'SSHログインを許可します',\n        rootHelper2: 'SSHログインを無効にします',\n        rootHelper3: 'キーログインのみが許可されています',\n        rootHelper4: '事前定義されたコマンドのみを実行できます。他の操作を実行することはできません。',\n        passwordAuthentication: 'パスワード認証',\n        pwdAuthHelper: 'パスワード認証を有効にするかどうか。このパラメーターはデフォルトで有効になります。',\n        pubkeyAuthentication: '重要な認証',\n        privateKey: '秘密鍵',\n        publicKey: '公開鍵',\n        password: 'パスワード',\n        createMode: '作成方法',\n        generate: '自動生成',\n        unSyncPass: '鍵パスワードは同期できません',\n        syncHelper: '同期操作は無効なキーをクリーンアップし、新しい完全なキーペアを同期します。続行しますか？',\n        input: '手動入力',\n        import: 'ファイルアップロード',\n        authKeys: '認証キー',\n        authKeysHelper: '現在の公開鍵情報を保存しますか？',\n        pubkey: '重要な情報',\n        encryptionMode: '暗号化モード',\n        pubKeyHelper: '現在の鍵情報はユーザー {0} にのみ有効です',\n        passwordHelper: '6〜10桁と英語のケースを含めることができます',\n        reGenerate: 'キーを再生します',\n        keyAuthHelper: 'キー認証を有効にするかどうか。',\n        useDNS: '使用済み',\n        dnsHelper: 'DNS解像度関数がSSHサーバーで有効になっているかどうかを制御して、接続のIDを確認します。',\n        analysis: '統計情報',\n        denyHelper:\n            '次のアドレスで「拒否」操作を実行します。設定後、IPはサーバーへのアクセスが禁止されます。続けたいですか？',\n        acceptHelper:\n            '次のアドレスで「受け入れる」操作を実行します。設定後、IPは通常のアクセスを取り戻します。続けたいですか？',\n        noAddrWarning: '[{0}]アドレスは現在選択されていません。チェックしてもう一度やり直してください！',\n        loginLogs: 'SSHログ',\n        loginMode: 'モード',\n        authenticating: '鍵',\n        publickey: '鍵',\n        belong: '属する',\n        local: '地元',\n        session: 'セッション|セッション',\n        loginTime: 'ログイン時間',\n        loginIP: 'ログインIP',\n        stopSSHWarn: 'このSSH接続を切断するかどうか',\n        remote: 'リモートSSH',\n    },\n    setting: {\n        panel: 'パネル',\n        user: 'パネルユーザー',\n        userChange: 'パネルユーザーを変更します',\n        userChangeHelper: 'パネルユーザーを変更すると、ログアウトします。続く？',\n        passwd: 'パネルパスワード',\n        emailHelper: 'パスワード取得用',\n        watermark: '透かし設定',\n        watermarkContent: '透かし内容',\n        contentHelper: '{0} はノード名、{1} はノードアドレスを表します。変数を使用するか、カスタム名を入力できます。',\n        watermarkColor: '透かしの色',\n        watermarkFont: '透かしのフォントサイズ',\n        watermarkHeight: '透かしの高さ',\n        watermarkWidth: '透かしの幅',\n        watermarkRotate: '回転角度',\n        watermarkGap: '間隔',\n        watermarkCloseHelper: 'システムの透かし設定を無効にしてもよろしいですか？',\n        watermarkOpenHelper: '現在のシステム透かし設定を保存してもよろしいですか？',\n        title: 'パネルエイリアス',\n        panelPort: 'パネルポート',\n        titleHelper: '英語、漢字、数字、スペース、および一般的な特殊文字を含む3～30文字の長さをサポートします',\n        portHelper:\n            '推奨されるポート範囲は8888〜65535です。注:サーバーにセキュリティグループがある場合は、事前にセキュリティグループから新しいポートを許可します',\n        portChange: 'ポート変更',\n        portChangeHelper: 'サービスポートを変更し、サービスを再起動します。続けたいですか？',\n        theme: 'テーマ',\n        menuTabs: 'タブメニュー',\n        dark: '暗い',\n        darkGold: 'ダークゴールド',\n        light: 'ライト',\n        auto: 'システムをフォローします',\n        language: '言語',\n        runtimeEnv: '実行環境',\n        docSource: 'ドキュメントソース',\n        withByRegion: '運用リージョンに従う（デフォルト）',\n        withByLang: 'システム言語に従う',\n        region: '運用リージョン',\n        cn: '中国本土',\n        intl: 'グローバル',\n        regionHelper: '実行リージョンを変更すると、以下のリソース取得元が切り替わります：',\n        regionHelper1: 'システムインストールパッケージ',\n        regionHelper2: 'アプリストアおよびスクリプトライブラリ',\n        regionHelper3: 'ユーザーマニュアルおよび関連ドキュメント',\n        regionHelper4: '今後のダウンロードやアクセスに影響する可能性があります。ご注意ください。',\n        regionTip: '運用リージョンはシステム更新ソースとインストールパッケージのダウンロードアドレスに影響します。',\n        docSourceTip: 'ドキュメントソースはドキュメントと更新ログの遷移言語を決定します。',\n        languageHelper:\n            'デフォルトでは、ブラウザ言語に従います。このパラメーターは、現在のブラウザでのみ有効になります',\n        sessionTimeout: 'セッションタイムアウト',\n        sessionTimeoutError: '最小セッションタイムアウトは300秒です',\n        sessionTimeoutHelper: '{0}秒以上操作がない場合、パネルは自動的にログアウトされます。',\n        systemIP: 'デフォルトアクセスアドレス',\n        systemIPHelper:\n            'アプリケーションリダイレクト、コンテナアクセスなどの機能はこのアドレスを使用して転送されます。各ノードで異なるアドレスを設定できます。',\n        proxy: 'サーバープロキシ',\n        proxyHelper: 'プロキシサーバーを設定した後、次のシナリオで効果的になります。',\n        proxyHelper1:\n            'アプリストアからのインストールパッケージのダウンロードと同期（プロフェッショナルエディションのみ）',\n        proxyHelper2: 'システムの更新と更新情報検索（プロフェッショナルエディションのみ）',\n        proxyHelper4:\n            'Docker イメージのプルなどのネットワークリクエストはプロキシサーバー経由でアクセスされます（プロフェッショナルエディションのみ）',\n        proxyHelper5: 'システムタイプスクリプトライブラリの統一下載と同期（プロフェッショナル版機能）',\n        proxyHelper6: '証明書を申請する（プロ版機能）',\n        proxyHelper3: 'システムライセンスの確認と同期',\n        proxyType: 'プロキシタイプ',\n        proxyUrl: 'プロキシアドレス',\n        proxyPort: 'プロキシポート',\n        proxyPasswdKeep: 'パスワードを覚えておいてください',\n        proxyDocker: 'Dockerプロキシ',\n        proxyDockerHelper:\n            'プロキシサーバーの構成をDockerに同期し、オフラインサーバーイメージの引っ張りやその他の操作をサポートします',\n        syncToNode: '子ノードに同期',\n        syncToNodeHelper: '選択したノードへの同期設定',\n        nodes: 'ノード',\n        selectNode: 'ノードを選択',\n        selectNodeError: 'ノードを選択してください',\n        apiInterface: 'APIを有効にします',\n        apiInterfaceClose: '閉じたら、APIインターフェイスにアクセスできません。続けたいですか？',\n        apiInterfaceHelper: 'サードパーティのアプリケーションにAPIにアクセスできるようにします。',\n        apiInterfaceAlert1:\n            'サーバーのセキュリティリスクが増加する可能性があるため、生産環境で有効にしないでください。',\n        apiInterfaceAlert2:\n            'サードパーティのアプリケーションを使用してAPIを呼び出して、潜在的なセキュリティの脅威を防止しないでください。',\n        apiInterfaceAlert3: 'APIドキュメント',\n        apiInterfaceAlert4: '使用ドキュメント',\n        apiKey: 'APIキー',\n        apiKeyHelper: 'APIキーは、サードパーティアプリケーションに使用されてAPIにアクセスします。',\n        ipWhiteList: 'IP AllowList',\n        ipWhiteListEgs: '1行に1つ。たとえば、 n172.161.10.111  n172.161.10.0/24',\n        ipWhiteListHelper: 'AllowList内のIPSはAPIにアクセスできます、0.0.0.0/0（すべての IPv4）、::/0（すべての IPv6）',\n        apiKeyReset: 'インターフェイスキーリセット',\n        apiKeyResetHelper: '関連するキーサービスは無効になります。サービスに新しいキーを追加してください',\n        confDockerProxy: 'Dockerプロキシを構成します',\n        restartNowHelper: 'Dockerプロキシの構成には、Dockerサービスを再起動する必要があります。',\n        restartNow: 'すぐに再起動します',\n        restartLater: '後で手動で再起動',\n        systemIPWarning:\n            '現在のノードにはデフォルトアクセスアドレスが設定されていません。パネル設定から設定してください！',\n        systemIPWarning1: '現在のサーバーアドレスは{0}に設定されており、クイックリダイレクトは不可能です！',\n        defaultNetwork: 'ネットワークカード',\n        syncTime: 'サーバー時間',\n        timeZone: 'タイムゾーン',\n        timeZoneChangeHelper: 'タイムゾーンを変更するには、サービスを再起動する必要があります。続けたいですか？',\n        timeZoneHelper:\n            'TimeZoneの変更は、システムのTimeDatectlサービスに依存します。1パネルサービスを再起動した後に有効になります。',\n        timeZoneCN: '北京',\n        timeZoneAM: 'ロサンゼルス',\n        timeZoneNY: 'ニューヨーク',\n        ntpALi: 'アリババ',\n        ntpGoogle: 'グーグル',\n        syncSite: 'NTPサーバー',\n        syncSiteHelper: 'この操作は、システム時間同期のソースとして{0}を使用します。続けたいですか？',\n        changePassword: 'パスワードを変更する',\n        oldPassword: '元のパスワード',\n        newPassword: '新しいパスワード',\n        retryPassword: 'パスワードを認証する',\n        noSpace: '入力情報にはスペース文字を含めることはできません',\n        duplicatePassword: '新しいパスワードは元のパスワードと同じになることはできません。再入力してください！',\n        diskClean: 'キャッシュクリーン',\n        developerMode: 'プレビュープログラム',\n        developerModeHelper:\n            'それらが広くリリースされる前に、新しい機能と修正を体験し、早期のフィードバックを提供できます。',\n        thirdParty: 'サードパーティのアカウント',\n        noTypeForCreate: '現在、バックアップタイプは作成されていません',\n        LOCAL: 'サーバーディスク',\n        OSS: 'アリ私たち',\n        S3: 'amazonS3',\n        mode: 'モード',\n        MINIO: 'ミニオ',\n        SFTP: 'sftp',\n        WebDAV: 'webdav',\n        WebDAVAlist: 'WebDav Connect Alistは、公式ドキュメントを参照できます',\n        OneDrive: 'Microsoft Onedrive',\n        isCN: '世紀のインターネット',\n        isNotCN: '国際版',\n        client_id: 'クライアントID',\n        client_secret: 'クライアントの秘密',\n        redirect_uri: 'URLをリダイレクトします',\n        onedrive_helper: 'カスタム構成は公式ドキュメントで参照できます',\n        refreshTime: 'トークン更新時間',\n        refreshStatus: 'トークン更新ステータス',\n        backupDir: 'バックアップディレクトリ',\n        codeWarning: '現在の承認コード形式が正しくありません。もう一度確認してください！',\n        code: '認証コード',\n        codeHelper:\n            '[取得]ボタンをクリックしてから、リダイレクトリンクの「コード」の後にコンテンツをログインしてコピーします。この入力ボックスに貼り付けます。特定の手順については、公式のドキュメントを参照してください。',\n        googleHelper:\n            'まずGoogleアプリケーションを作成し、クライアント情報を取得してフォームに記入し、取得ボタンをクリックしてください。具体的な操作は公式ドキュメントを参照してください。',\n        loadCode: '取得する',\n        COS: 'tencent cos',\n        ap_beijing_1: '北京ゾーン1',\n        ap_beijing: '北京',\n        ap_nanjing: '南京',\n        ap_shanghai: '上海',\n        ap_guangzhou: '広州',\n        ap_chengdu: '成都',\n        ap_chongqing: '唐辛子',\n        ap_shenzhen_fsi: '深圳金融',\n        ap_shanghai_fsi: '上海金融',\n        ap_beijing_fsi: '北京金融',\n        ap_hongkong: '香港、中国',\n        ap_singapore: 'シンガポール',\n        ap_mumbai: 'ムンバイ',\n        ap_jakarta: 'ジャカルタ',\n        ap_seoul: 'ソウル',\n        ap_bangkok: 'バンコク',\n        ap_tokyo: '東京',\n        na_siliconvalley: 'シリコンバレー（米国西）',\n        na_ashburn: 'アッシュバーン（米国東）',\n        na_toronto: 'トロント',\n        sa_saopaulo: 'サンパウロ',\n        eu_frankfurt: 'フランクフルト',\n        KODO: 'Qiniコード',\n        scType: 'ストレージタイプ',\n        typeStandard: '標準ストレージ',\n        typeStandard_IA: '低頻度ストレージ',\n        typeArchive: 'アーカイブストレージ',\n        typeDeep_Archive: '深層アーカイブストレージ',\n        scLighthouse: 'デフォルト：軽量オブジェクトストレージはこのストレージタイプのみサポートしています',\n        scStandard:\n            '標準ストレージは、リアルタイムアクセスが多いホットファイルや頻繁なデータ交換などの業務シナリオに適しています。',\n        scStandard_IA:\n            '低頻度ストレージは、アクセス頻度が低い業務シナリオ（例:月に1〜2回程度のアクセス）に適しており、最低30日間の保存が必要です。',\n        scArchive: 'アーカイブストレージは、極めて低いアクセス頻度（例:半年に1回程度）の業務シナリオに適しています。',\n        scDeep_Archive:\n            '深層アーカイブストレージは、極めて低いアクセス頻度（例:年に1〜2回程度）の業務シナリオに適しています。',\n        archiveHelper:\n            'アーカイブストレージのファイルは直接ダウンロードできず、対応するクラウドサービスプロバイダーのサイトで復元操作を行う必要があります。慎重に使用してください。',\n        backupAlert:\n            '理論的には、クラウドプロバイダーがS3プロトコルに対応していれば、現行のAmazon S3クラウドストレージを使ってバックアップできます。具体的な設定については、',\n        domain: 'ドメインを加速します',\n        backupAccount: 'バックアップアカウント|バックアップアカウント',\n        loadBucket: 'バケツを入手してください',\n        accountName: 'アカウント名',\n        accountKey: 'アカウントキー',\n        address: '住所',\n        path: 'パス',\n        safe: '安全',\n        passkey: 'パスキー',\n        passkeyManage: '管理',\n        passkeyKeyManagement: 'キー管理',\n        passkeyHelper: '素早くログインするため、最大 5 個のパスキーを登録できます',\n        passkeyRequireSSL: 'Passkey はドメインをバインドして HTTPS でアクセスする必要があります',\n        passkeyTrustedProxies: '信頼済みプロキシ',\n        passkeyTrustedProxiesHelper:\n            '以下の IP/CIDR からのリクエストのみが Forwarded/X-Forwarded-Proto を HTTPS 判定に使います',\n        passkeyNotSupported: 'ブラウザまたは環境がパスキー非対応です。ドメインと HTTPS を確認してください',\n        passkeyPrereqTitle: 'Passkey を使用するには次の前提条件が必要です',\n        passkeyPrereqBindDomain: '1Panel のドメインバインド設定済み',\n        passkeyPrereqHttps: 'HTTPS で安全にアクセス済み',\n        passkeyPrereqBrowser: '現在の環境が Passkey（WebAuthn + プラットフォーム認証器）に対応',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable:\n            'システムブラウザでアクセスし、ドメイン設定済みかつ HTTPS で開いていることを確認してください',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            'この環境ではデバイス認証を利用できません。新しい OS のシステムブラウザでお試しください',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            '画面ロック/指紋/Face ID/Windows Hello を有効化してから再試行してください',\n        passkeyPrereqBrowserDetailDetectFailed:\n            '環境検出に失敗しました。ページを更新して再試行するか、システムブラウザをご利用ください',\n        passkeyPrereqGoSetup: '設定する',\n        passkeyCount: '登録済み {0}/{1}',\n        passkeyName: '名称',\n        passkeyNameHelper: 'デバイスを区別する名称を入力してください',\n        passkeyAdd: 'パスキーを追加',\n        passkeyCreatedAt: '作成時間',\n        passkeyLastUsedAt: '最終使用',\n        passkeyDeleteConfirm: '削除するとこのパスキーでログインできなくなります。続行しますか？',\n        passkeyLimit: '最大 5 個のパスキーを登録できます',\n        passkeyFailed: 'パスキーの登録に失敗しました。パネルSSL証明書が信頼できる証明書であることを確認してください',\n        bindInfo: 'バインド情報',\n        bindAll: 'すべてを聞いてください',\n        bindInfoHelper:\n            'サービスリスニングアドレスまたはプロトコルを変更すると、サービスが利用できない場合があります。続けたいですか？',\n        ipv6: 'IPv6をリッスン',\n        bindAddress: '住所を聞いてください',\n        entrance: '入り口',\n        showEntrance: '「概要」ページで無効なアラートを表示します',\n        entranceHelper:\n            'セキュリティの入り口を有効にすると、指定されたセキュリティの入り口を介してパネルにログインするだけです。',\n        entranceError:\n            '5〜116文字の安全なログインエントリポイントを入力してください。数字または文字のみがサポートされています。',\n        entranceInputHelper: 'セキュリティの入り口を無効にするために空白のままにしてください。',\n        randomGenerate: 'ランダム',\n        expirationTime: '有効期限',\n        unSetting: '解き放つ',\n        noneSetting:\n            'パネルパスワードの有効期限を設定します。有効期限が切れた後、パスワードをリセットする必要があります',\n        expirationHelper: 'パスワードの有効期限が[0]日の場合、パスワードの有効期限機能が無効になっています',\n        days: '有効期限',\n        expiredHelper: '現在のパスワードの有効期限が切れています。もう一度パスワードを変更してください。',\n        timeoutHelper:\n            '[{0}日]パネルパスワードの有効期限が切れようとしています。有効期限が切れた後、パスワードをリセットする必要があります',\n        complexity: '複雑さの検証',\n        complexityHelper:\n            '有効にした後、パスワード検証ルールは次のとおりです。英語、数字、少なくとも2つの特殊文字を含む8〜30文字です。',\n        bindDomain: 'バインドドメイン',\n        unBindDomain: 'バインドドメイン',\n        panelSSL: 'パネルSSL',\n        unBindDomainHelper:\n            'ドメイン名をバインドするアクションは、システムの不安を引き起こす可能性があります。続けたいですか？',\n        bindDomainHelper: 'ドメインにバインドした後、そのドメインのみが1パネルサービスにアクセスできます。',\n        bindDomainHelper1: 'ドメイン名のバインディングを無効にするために空白のままにします。',\n        bindDomainWarning:\n            'ドメインバインディングの後、ログアウトされ、設定で指定されたドメイン名を介して1パネルサービスのみにアクセスできます。続けたいですか？',\n        allowIPs: '承認されたIP',\n        unAllowIPs: '許可されていないIP',\n        unAllowIPsWarning:\n            '空のIPを許可すると、すべてのIPがシステムにアクセスできるようになり、システムの不安定性が発生する可能性があります。続けたいですか？',\n        allowIPsHelper:\n            '承認されたIPアドレスリストを設定した後、リスト内のIPアドレスのみがパネルサービスにアクセスできます。',\n        allowIPsWarning:\n            '承認されたIPアドレスリストを設定した後、リスト内のIPアドレスのみがパネルサービスにアクセスできます。続けたいですか？',\n        allowIPsHelper1: 'IPアドレスの制限を無効にするために空白のままにします。',\n        allowIPEgs: '1行に1つ。たとえば、 \\n172.16.10.111  \\n172.16.10.0/24',\n        mfa: '二段階認証',\n        mfaClose: 'MFAを無効にすると、サービスのセキュリティが減少します。続けたいですか？',\n        secret: '秘密',\n        mfaInterval: '間隔を更新する',\n        mfaTitleHelper:\n            'タイトルは、さまざまな1パネルホストを区別するために使用されます。タイトルを変更した後、再度スキャンするか、秘密のキーを手動で追加します。',\n        mfaIntervalHelper:\n            '更新間隔を変更した後、再度スキャンするか、秘密鍵を手動で追加してください。\\n一部の認証アプリは 30 秒間隔のみ対応しています（例: Google/Microsoft Authenticator）。',\n        mfaAlert:\n            '1回限りのトークンは、現在の時刻に基づいて動的に生成された6桁の数値です。サーバー時間が同期されていることを確認してください。',\n        mfaHelper: '有効にした後、1回限りのトークンを検証する必要があります。',\n        mfaHelper1: 'たとえば、Authenticatorアプリをダウンロードしてください。',\n        mfaHelper2:\n            '1回限りのトークンを取得するには、Authenticatorアプリを使用して次のQRコードをスキャンするか、Secretキーを認証アプリにコピーします。',\n        mfaHelper3: 'アプリから6桁を入力します',\n        mfaCode: '1回限りのトークン',\n        sslChangeHelper: 'HTTPS設定を変更し、サービスを再起動します。続けたいですか？',\n        sslDisable: '無効にします',\n        sslDisableHelper:\n            'HTTPSサービスが無効になっている場合は、有効にするためにパネルを再起動する必要があります。続けたいですか？',\n        noAuthSetting: '不正な設定',\n        noAuthSettingHelper:\n            'ユーザーが未ログインで、セキュリティ入口、認証IP、またはバインドされたドメイン名を正しく入力していない場合、このレスポンスでパネルの特徴を非表示にできます。',\n        responseSetting: '応答設定',\n        help200: 'ヘルプページ',\n        error400: '要求の形式が正しくありません',\n        error401: '不正',\n        error403: '禁断',\n        error404: '見つかりません',\n        error408: 'リクエストタイムアウト',\n        error416: '範囲は満足できません',\n        error444: '接続が閉じた',\n        error500: 'サーバーエラー',\n        https: 'パネルにHTTPSを設定するとアクセスセキュリティが向上します',\n        strictHelper: 'HTTPS以外のトラフィックはパネルに接続できません',\n        muxHelper:\n            'パネルはHTTPピュロコールとHTTPSピュロコールの両方を監視し、HTTPをHTTPSにリダイレクトしますが、このモードではパネルのセキュリティが低下する可能性があります',\n        certType: '証明書の種類',\n        selfSigned: '自己署名',\n        selfSignedHelper:\n            'ブラウザは、自己署名の証明書を信頼していない場合があり、セキュリティ警告を表示する場合があります。',\n        select: '選択します',\n        domainOrIP: 'ドメインまたはIP:',\n        timeOut: 'タイムアウト',\n        rootCrtDownload: 'ルート証明書のダウンロード',\n        primaryKey: '主キー',\n        certificate: '証明書',\n        backupJump:\n            'バックアップファイルは現在のバックアップリストにありません。ファイルディレクトリからダウンロードして、バックアップ用にインポートしてみてください。',\n        snapshot: 'スナップショット|スナップショット',\n        noAppData: '選択可能なシステムアプリはありません',\n        noBackupData: '選択可能なバックアップデータはありません',\n        stepBaseData: '基本データ',\n        stepAppData: 'システムアプリ',\n        stepPanelData: 'システムデータ',\n        stepBackupData: 'バックアップデータ',\n        stepOtherData: 'その他のデータ',\n        operationLog: '操作ログを保持',\n        loginLog: 'アクセスログを保持',\n        systemLog: 'システムログを保持',\n        taskLog: 'タスクログを保持',\n        monitorData: '監視データを保持',\n        dockerConf: 'Docker設定保持',\n        selectAllImage: 'すべてのアプリイメージをバックアップ',\n        logLabel: 'ログ',\n        agentLabel: 'ノード設定',\n        appDataLabel: 'アプリデータ',\n        appImage: 'アプリイメージ',\n        appBackup: 'アプリバックアップ',\n        backupLabel: 'バックアップディレクトリ',\n        confLabel: '設定ファイル',\n        dockerLabel: 'コンテナ',\n        taskLabel: 'スケジュールタスク',\n        resourceLabel: 'アプリリソースディレクトリ',\n        runtimeLabel: '実行環境',\n        appLabel: 'アプリ',\n        databaseLabel: 'データベース',\n        snapshotLabel: 'スナップショットファイル',\n        websiteLabel: 'ウェブサイト',\n        directoryLabel: 'ディレクトリ',\n        appStoreLabel: 'アプリストア',\n        shellLabel: 'スクリプト',\n        tmpLabel: '一時ディレクトリ',\n        sslLabel: '証明書ディレクトリ',\n        reCreate: 'スナップショットの作成に失敗しました',\n        reRollback: 'スナップショットのロールバックに失敗しました',\n        deleteHelper:\n            'サードパーティのバックアップアカウントにあるものを含むすべてのスナップショットファイルが削除されます。続けたいですか？',\n        status: 'スナップショットステータス',\n        ignoreRule: 'ルールを無視します',\n        editIgnoreRule: '@:commons.button.edit @.lower:setting.ignorerule',\n        ignoreHelper:\n            'このルールは、スナップショットの作成中に1パネルデータディレクトリを圧縮およびバックアップするために使用されます。デフォルトでは、ソケットファイルは無視されます。',\n        ignoreHelper1: '1行に1つ。たとえば、 n*.log  n/opt/1panel/cache',\n        panelInfo: '1パネルの基本情報を書いてください',\n        panelBin: 'バックアップ1パネルシステムファイル',\n        daemonJson: 'バックアップDocker構成ファイル',\n        appData: 'バックアップ1パネルからアプリをインストールしました',\n        panelData: 'バックアップ1パネルデータディレクトリ',\n        backupData: '1パネル用のバックアップローカルバックアップディレクトリ',\n        compress: 'スナップショットファイルを作成します',\n        upload: 'スナップショットファイルをアップロードします',\n        recoverDetail: '詳細を回復します',\n        lastRecoverAt: '最後の回復時間',\n        lastRollbackAt: '最後のロールバック時間',\n        reDownload: 'バックアップファイルをもう一度ダウンロードしてください',\n        recoverErrArch: 'さまざまなサーバーアーキテクチャ間のスナップショット回復はサポートされていません！',\n        recoverErrSize:\n            '不十分なディスクスペースが検出されました。チェックまたはクリーンアップして、再試行してください！',\n        recoverHelper: 'Snapshot {0}からの回復を開始して、先に進む前に次の情報を確認してください。',\n        recoverHelper1: '回復には、Dockerサービスと1パネルサービスを再起動する必要があります',\n        recoverHelper2:\n            'サーバーに十分なディスクスペースがあることを確認してください（スナップショットファイルサイズ:{0}、利用可能なスペース:{1}）',\n        recoverHelper3:\n            'サーバーアーキテクチャが、スナップショットが作成されたサーバーのアーキテクチャと一致していることを確認してください（現在のサーバーアーキテクチャ:{0}）',\n        rollback: 'ロールバック',\n        rollbackHelper:\n            'この回復をロールバックすると、この回復からすべてのファイルを置き換え、Dockerサービスと1パネルサービスを再起動する必要がある場合があります。続けたいですか？',\n        upgradeHelper: 'アップグレードには、1パネルサービスを再起動する必要があります。続けたいですか？',\n        rollbackLocalHelper:\n            'マスターノードは直接ロールバックをサポートしていません。手動で「1pctl restore」コマンドを実行してロールバックしてください！',\n        noUpgrade: '現在、最新バージョンです',\n        upgradeNotes: 'リリースノート',\n        upgradeNow: '今すぐアップグレードしてください',\n        source: 'ソースをダウンロードします',\n        versionNotSame:\n            'ノードのバージョンがメインノードと一致していません。ノード管理でアップグレードしてから再試行してください。',\n        versionCompare:\n            'ノード {0} は既にアップグレード可能な最新バージョンです。マスターノードのバージョンを確認後、再試行してください！',\n        about: 'について',\n        versionItem: '現在のバージョン',\n        backupCopies: 'バックアップ数',\n        backupCopiesHelper:\n            'バージョンロールバック用に保持するアップグレードバックアップの数を設定します。0はすべて保持を意味します。',\n        backupCopiesRule: '少なくとも3つのアップグレードバックアップ記録を保持してください',\n        release: 'バージョン更新履歴',\n        releaseHelper: '現在の環境の更新履歴の取得に異常が発生しました。手動で公式ドキュメントを確認してください。',\n        project: 'GitHub',\n        issue: '問題',\n        doc: '公式文書',\n        star: '星',\n        description: 'Linuxサーバーパネル',\n        forum: '議論',\n        doc2: 'ドキュメント',\n        currentVersion: 'バージョン',\n        license: 'ライセンス',\n        bindNode: 'ノードをバインド',\n        menuSetting: 'メニュー設定',\n        menuSettingHelper: 'サブメニューが1つしか存在しない場合、メニューバーにはそのサブメニューのみが表示されます',\n        showAll: 'すべてを表示します',\n        hideALL: 'すべてを隠します',\n        ifShow: '表示するかどうか',\n        menu: 'メニュー',\n        confirmMessage: 'ページは更新されて、高度なメニューリストを更新します。続く？',\n        recoverMessage: 'ページが更新され、メニューリストが初期状態に戻ります。続行しますか？',\n        compressPassword: '圧縮パスワード',\n        backupRecoverMessage: '圧縮または減圧パスワードを入力してください（設定しないように空白のままにしてください）',\n        componentSize: 'コンポーネントサイズ',\n        apiKeyValidityTime: 'インターフェースキー有効期間',\n        apiKeyValidityTimeEgs: 'インターフェースキー有効期間（分）',\n        apiKeyValidityTimeHelper: '現在時刻との差（分）が許容範囲内なら有効です。0 は検証無効。',\n        scope: '範囲',\n        public: 'パブリック',\n        publicHelper: 'パブリック型バックアップアカウントは各子ノードへ同期され共同利用できます',\n        private: 'プライベート',\n        privateHelper: 'プライベート型バックアップアカウントは現在ノードのみで使用します',\n        UPYUN: 'UPYUN',\n        ALIYUN: 'Aliyun Drive',\n        ALIYUNHelper:\n            'Aliyun Drive の非クライアントダウンロード上限は 100MB です。超える場合はクライアントでダウンロードしてください。',\n        ALIYUNRecover:\n            'Aliyun Drive の非クライアントダウンロード上限は 100MB です。超える場合はクライアントでローカルにダウンロード後、スナップショットを同期して復元してください。',\n        GoogleDrive: 'Google Drive',\n        analysis: '解析',\n        analysisHelper:\n            'トークン全体を貼り付けると必要項目を自動解析します。詳細は公式ドキュメントを参照してください。',\n        serviceName: 'サービス名',\n        operator: 'オペレーター',\n        clickToRefresh: 'クリックして更新',\n        recoverFailed: 'スナップショット復元失敗',\n        importHelper: 'スナップショットディレクトリ: ',\n        upgradeRecord: 'アップグレード履歴',\n        upgrading: 'アップグレード中、しばらくお待ちください...',\n        versionHelper: '命名規則: [メジャー].[機能].[バグ修正]。例:',\n        upgradeCheck: '更新を確認',\n        panelSSLHelper: 'パネルSSL自動更新後、1Panelサービスを手動再起動してください。',\n    },\n    license: {\n        offLine: 'オフライン版',\n        community: '無料',\n        oss: '無料',\n        pro: '専門',\n        trial: '体験',\n        add: 'コミュニティ版を追加',\n        licenseBindHelper: 'ライセンスがノードにバインドされている場合にのみ、無料ノードクォータを使用できます',\n        licenseAlert:\n            'ライセンスがノードに正常にバインドされている場合のみ、コミュニティ版ノードを追加できます。ライセンスに正常にバインドされているノードのみ切り替えがサポートされます。',\n        licenseUnbindHelper:\n            'このライセンスにコミュニティ版ノードが存在します。バインドを解除してから再試行してください！',\n        subscription: 'サブスクリプション',\n        perpetual: '永久ライセンス',\n        versionConstraint: '{0} バージョン買い取り',\n        forceUnbind: '強制バインド解除',\n        forceUnbindHelper:\n            '強制的にバインド解除を行うと、解除プロセス中に発生するエラーを無視し、最終的にライセンスのバインドを解除します。',\n        updateForce: '強制更新（アンバインド中のすべてのエラーを無視し、最終操作の成功を保証します）',\n        trialInfo: 'バージョン',\n        authorizationId: 'サブスクリプションID',\n        authorizedUser: '認定ユーザー',\n        lostHelper:\n            'ライセンスは、再試行の最大数に達しました。プロのバージョン機能が適切に機能していることを確認するには、手動で同期ボタンをクリックしてください。세부사항: ',\n        disableHelper:\n            'ライセンスの同期の検証は失敗しました。プロのバージョン機能が適切に機能していることを確認するには、手動で同期ボタンをクリックしてください。세부사항: ',\n        quickUpdate: 'クイックアップデート',\n        power: '許可',\n        unbindHelper: 'すべてのPro関連設定は、バインディングを解除した後にクリーニングされます。続けたいですか？',\n        importLicense: 'ライセンス',\n        importHelper: 'ここでライセンスファイルをクリックまたはドラッグしてください',\n        technicalAdvice: '技術的な相談',\n        advice: '相談',\n        levelUpPro: 'Proにアップグレードします',\n        licenseSync: 'ライセンス同期',\n        knowMorePro: 'もっと詳しく知る',\n        closeAlert: '現在のページはパネル設定で閉じることができます',\n        introduce: '機能の紹介',\n        waf: 'プロフェッショナルバージョンにアップグレードすると、インターセプトマップ、ログ、ブロックレコード、地理的位置ブロッキング、カスタムルール、カスタムインターセプトページなどの機能を提供できます。',\n        tamper: 'プロのバージョンにアップグレードすると、不正な変更や改ざんからWebサイトを保護できます。',\n        setting:\n            'プロのバージョンにアップグレードすることで、パネルロゴ、ウェルカムメッセージ、その他の情報のカスタマイズが可能になります。',\n        monitor:\n            'プロのバージョンにアップグレードして、Webサイトのリアルタイムステータス、訪問者の傾向、訪問者ソース、リクエストログ、その他の情報を表示します。',\n        alert: 'プロのバージョンにアップグレードして、SMSを介してアラーム情報を受信し、アラームログを表示し、さまざまなキーイベントを完全に制御し、心配のないシステム操作を確実にする',\n        app: 'モバイルアプリでサービス情報、異常監視などを表示するには、プロフェッショナル版にアップグレードしてください。',\n        node: 'プロフェッショナル版にアップグレードすると、1Panel で複数の Linux サーバーを管理できます。',\n        nodeApp:\n            'プロフェッショナル版にアップグレードすると、ノードを手動で切り替えることなく、マルチノードアプリケーションのバージョンを一括アップグレードできます。',\n        nodeDashboard:\n            'プロフェッショナル版にアップグレードすると、マルチノードのアプリケーション、ウェブサイト、データベース、スケジュールタスクを集中管理できます。',\n        fileExchange: 'プロフェッショナル版にアップグレードすると、複数のサーバー間でファイルを迅速に転送できます。',\n        cluster:\n            'プロフェッショナル版にアップグレードすると、MySQL/Postgres/Redisマスタースレーブクラスタを管理できます。',\n        exceptionalHelper: 'ライセンス同期検証が異常です。手動で同期して機能を確認してください。詳細: ',\n        tamperHelper: '操作失敗。ファイル/フォルダの改ざん保護を確認してください。',\n    },\n    clean: {\n        scan: 'スキャンを開始します',\n        scanHelper: '1パネルのランタイム中に作成されたジャンクファイルを簡単にクリーンアップできます',\n        clean: '今すぐきれいにします',\n        reScan: 'レスカン',\n        cleanHelper: '選択されたファイルおよびディレクトリは、クリーンアップ後にロールバックできません。続行しますか？',\n        statusSuggest: '（推奨クリーニング）',\n        statusClean: '（とてもきれい）',\n        statusEmpty: 'とてもきれいで、掃除は必要ありません！',\n        statusWarning: '（注意して進めます）',\n        lastCleanTime: '最後にクリーニング:{0}',\n        lastCleanHelper: 'ファイルとディレクトリのクリーニング:{0}、合計クリーニング:{1}',\n        cleanSuccessful: '正常に掃除',\n        currentCleanHelper: 'このセッションでクリーニングされたファイルとディレクトリ:{0}、合計クリーニング:{1}',\n        suggest: '（推奨）',\n        totalScan: 'クリーニングするジャンクファイルの合計:',\n        selectScan: '選択したジャンクファイルの合計:',\n        system: 'システムジャンクファイル',\n        systemHelper: 'スナップショット、アップグレードなどのプロセスで生成された一時ファイル',\n        panelOriginal: 'システムスナップショット復元前のバックアップファイル',\n        upgrade: 'システムアップグレードのバックアップファイル',\n        agentPackages: '履歴バージョンの子ノードアップグレード/インストールパッケージ',\n        upgradeHelper: '（システムロールバックのために最新のアップグレードバックアップを保持することをお勧めします）',\n        rollback: '回復する前にファイルをバックアップします',\n        backup: 'システムバックアップ',\n        backupHelper: 'ローカルバックアップアカウントに関連付けられていないバックアップファイル',\n        unknownBackup: 'システムバックアップ',\n        tmpBackup: '一時バックアップ',\n        unknownApp: '関連付けられていないアプリバックアップ',\n        unknownDatabase: '関連付けられていないデータベースバックアップ',\n        unknownWebsite: '関連付けられていないWebサイトバックアップ',\n        unknownSnapshot: '関連付けられていないスナップショットバックアップ',\n        upload: '一時的なアップロードファイル',\n        uploadHelper: 'システムバックアップリストからアップロードされた一時ファイル',\n        download: '一時的なダウンロードファイル',\n        downloadHelper: 'システムによってサードパーティのバックアップアカウントからダウンロードされた一時ファイル',\n        directory: 'ディレクトリ',\n        systemLog: 'ログファイル',\n        systemLogHelper: 'システムログ、タスクログ、ウェブサイトログファイル',\n        dockerLog: 'コンテナ操作ログファイル',\n        taskLog: 'スケジュールタスク実行ログファイル',\n        shell: 'シェルスクリプトスケジュールタスク',\n        containerShell: 'コンテナ内で実行するシェルスクリプトスケジュールタスク',\n        curl: 'CURLスケジュールタスク',\n        docker: 'コンテナゴミ',\n        dockerHelper: 'コンテナ、イメージ、ボリューム、ビルドキャッシュなどのファイル',\n        volumes: 'ボリューム',\n        buildCache: 'コンテナビルドキャッシュ',\n        appTmpDownload: 'Archivo de descarga temporal de la aplicación',\n        unknownWebsiteLog: '未関連ウェブサイトログバックアップファイル',\n        snapshot: 'システムスナップショット一時ファイル',\n    },\n    app: {\n        app: 'アプリケーション|アプリケーション',\n        installName: '名前',\n        installed: 'インストール',\n        all: '全て',\n        version: 'バージョン',\n        detail: '詳細',\n        params: '編集',\n        author: '著者',\n        source: 'ソース',\n        appName: 'アプリケーション名',\n        deleteWarn:\n            '削除操作は、すべてのデータとバックアップを一緒に削除します。この操作はロールバックすることはできません。続けたいですか？',\n        syncSuccess: '正常に同期しました',\n        canUpgrade: '更新',\n        backupName: 'ファイル名',\n        backupPath: 'ファイルパス',\n        backupdate: 'バックアップ時間',\n        versionSelect: 'バージョンを選択してください',\n        operatorHelper: '操作{0}は、選択したアプリケーションで実行されます。続けたいですか？',\n        startOperatorHelper: 'アプリケーションが開始されます。続けたいですか？',\n        stopOperatorHelper: 'アプリケーションは停止します。続けたいですか？',\n        restartOperatorHelper: 'アプリケーションが再起動されます。続けたいですか？',\n        reloadOperatorHelper: 'アプリケーションはリロードされます。続けたいですか？',\n        checkInstalledWarn: '「{0}」が検出されませんでした。「アプリストア」に移動してインストールしてください。',\n        limitHelper: 'アプリケーションはすでにインストールされています。',\n        deleteHelper: '「{0}」は、次のリソースに関連付けられています。チェックしてもう一度やり直してください！',\n        checkTitle: 'ヒント',\n        defaultConfig: 'デフォルトの構成',\n        defaultConfigHelper: 'デフォルトの構成に復元されており、保存後に有効になります',\n        forceDelete: 'フォース削除',\n        forceDeleteHelper: 'フォース削除は、削除プロセス中のエラーを無視し、最終的にメタデータを削除します。',\n        deleteBackup: 'バックアップを削除します',\n        deleteBackupHelper: 'また、アプリケーションのバックアップを削除します',\n        deleteDB: 'データベースを削除します',\n        deleteDBHelper: 'データベースも削除します',\n        noService: 'いいえ{0}',\n        toInstall: 'インストールに移動します',\n        param: 'パラメーター',\n        alreadyRun: '年',\n        syncAppList: '同期',\n        less1Minute: '1分未満',\n        appOfficeWebsite: 'オフィスのウェブサイト',\n        github: 'Github',\n        document: '書類',\n        updatePrompt: '更新はありません',\n        installPrompt: 'まだインストールされていません',\n        updateHelper:\n            'パラメーターの編集により、アプリケーションが開始されない場合があります。注意して進めてください。',\n        updateWarn: '更新パラメータアプリケーションを再構築する必要がありますが、続行しますか？',\n        busPort: 'ポート',\n        syncStart: '同期を開始します！後でアプリストアを更新してください',\n        advanced: '高度な設定',\n        cpuCore: 'コア',\n        containerName: 'コンテナ名',\n        containerNameHelper: 'コンテナ名は設定されていないときに自動的に生成されます',\n        allowPort: '外部アクセス',\n        allowPortHelper: '外部ポートアクセスを許可すると、ファイアウォールポートがリリースされます',\n        appInstallWarn:\n            'アプリケーションは、デフォルトで外部アクセスポートを公開しません。[Advanced Settings]をクリックして公開します。',\n        upgradeStart: 'アップグレードを起動します！後でページを更新してください',\n        toFolder: 'インストールディレクトリを開きます',\n        editCompose: '編集ファイルを作成します',\n        editComposeHelper: 'Composeファイルを編集すると、ソフトウェアのインストールの障害が発生する可能性があります',\n        composeNullErr: '作曲は空にすることはできません',\n        takeDown: '降ろす',\n        allReadyInstalled: 'インストール',\n        installHelper: '画像プルの問題がある場合は、画像アクセラレーションを構成します。',\n        sortMode: '並べ替え',\n        installWarn:\n            '外部アクセスは有効になっていないため、アプリケーションが外部ネットワークを介してアクセスできるようになります。続けたいですか？',\n        showIgnore: '無視されたアプリケーションを表示します',\n        cancelIgnore: 'キャンセルは無視します',\n        ignoreList: '無視されたアプリケーション',\n        appHelper:\n            'アプリケーションの詳細ページにアクセスして、いくつかの特別なアプリケーションのインストール命令を学びます。',\n        backupApp: 'アップグレード前のバックアップアプリケーション',\n        backupAppHelper:\n            'アップグレードが失敗した場合、バックアップは自動的にロールバックされます。ログ監査システムログの障害理由を確認してください。バックアップは、デフォルトで最新の3コピーを保持します',\n        openrestyDeleteHelper: 'OpenRestyの強制削除により、すべてのWebサイトが削除されます。続けたいですか？',\n        downloadLogHelper1: '{0}アプリケーションのすべてのログがダウンロードされようとしています。続けたいですか？',\n        downloadLogHelper2: '{0}アプリケーションの最新{1}ログはダウンロードされようとしています。続けたいですか？',\n        syncAllAppHelper: 'すべてのアプリケーションが同期されます。続けたいですか？',\n        hostModeHelper:\n            '現在のアプリケーションネットワークモードはホストモードです。ポートを開く必要がある場合は、ファイアウォールページで手動で開いてください。',\n        showLocal: 'ローカルアプリケーションを表示します',\n        reload: 'リロード',\n        upgradeWarn:\n            'アプリケーションのアップグレードは、docker-compose.ymlファイルを置き換えます。変更がある場合は、クリックしてファイルの比較を表示できます',\n        openclawHttpsUpgradeNoticeTitle:\n            '注意: 以下の説明は、2026.3.13 より前のバージョンから OpenClaw をアップグレードするユーザーにのみ適用されます:',\n        openclawHttpsUpgradeNoticeItem1:\n            'デプロイ済みエージェントのアップグレード完了後、設定 -> 設定 -> セキュリティ に入り、アクセスアドレスを手動で追加してください。',\n        openclawHttpsUpgradeNoticeItem2:\n            '新バージョンではエージェントへのアクセスに HTTPS が必須です。以前リバースプロキシサイト経由でアクセスしていた場合は、プロキシ先を https://IP:Port に変更してください。',\n        newVersion: '新しいバージョン',\n        oldVersion: '現在のバージョン',\n        composeDiff: 'ファイルの比較',\n        showDiff: '比較を表示します',\n        useNew: 'カスタムバージョンを使用します',\n        useDefault: 'デフォルトバージョンを使用します',\n        useCustom: 'docker-compose.ymlをカスタマイズします',\n        useCustomHelper:\n            'カスタムdocker-compose.ymlファイルを使用すると、アプリケーションのアップグレードが失敗する場合があります。必要でない場合は、確認しないでください。',\n        diffHelper:\n            '左側は古いバージョンで、右側は新しいバージョンです。編集後、クリックしてカスタムバージョンを保存します',\n        pullImage: '画像を引っ張ります',\n        pullImageHelper: 'アプリケーションが開始する前に、Docker Pullを実行して画像をプルします',\n        deleteImage: 'イメージを削除',\n        deleteImageHelper: 'アプリ関連のイメージを削除します。削除に失敗してもタスクは終了しません。',\n        requireMemory: 'メモリ要件',\n        supportedArchitectures: '対応アーキテクチャ',\n        link: 'リンク',\n        showCurrentArch: '現在のサーバーアーキテクチャのアプリケーション',\n        syncLocalApp: 'ローカルアプリの同期',\n        memoryRequiredHelper: '現在のアプリケーションは {0} メモリが必要です',\n        gpuConfig: 'GPUサポートを有効化',\n        gpuConfigHelper:\n            'マシンにNVIDIA GPUが搭載され、NVIDIAドライバーとNVIDIA Docker Container Toolkitがインストールされていることを確認してください',\n        webUI: 'Webアクセスアドレス',\n        webUIPlaceholder: '例：example.com:8080/login',\n        defaultWebDomain: 'デフォルトアクセスアドレス',\n        defaultWebDomainHepler:\n            'アプリケーションポートが8080の場合、アドレスはhttp(s)://デフォルトアクセスアドレス:8080にジャンプします',\n        webUIConfig:\n            '現在のノードにはデフォルトアクセスアドレスが設定されていません。アプリケーションパラメータで設定するか、パネル設定から設定してください！',\n        toLink: 'ジャンプ',\n        customAppHelper:\n            'カスタムアプリストアパッケージをインストールする前に、インストールされているアプリがないことを確認してください。',\n        forceUninstall: '強制アンインストール',\n        syncCustomApp: 'カスタムアプリを同期',\n        ignoreAll: '後続のすべてのバージョンを無視',\n        ignoreVersion: '指定されたバージョンを無視',\n        specifyIP: 'ホスト IP をバインド',\n        specifyIPHelper:\n            'ポートにバインドするホストアドレス/ネットワークインターフェースを設定します（この機能がわからない場合は、入力しないでください）',\n        uninstallDeleteBackup: 'アプリをアンインストール - バックアップを削除',\n        uninstallDeleteImage: 'アプリをアンインストール - イメージを削除',\n        upgradeBackup: 'アプリのアップグレード前にアプリをバックアップ',\n        noAppHelper: 'アプリケーションが検出されませんでした。タスクセンターでアプリストアの同期ログを確認してください',\n        isEdirWarn: 'docker-compose.yml ファイルが変更されたことを検出しました。比較を確認してください',\n    },\n    website: {\n        primaryDomain: 'プライマリドメイン',\n        otherDomains: '他のドメイン',\n        static: '静的',\n        deployment: '展開',\n        supportUpType:\n            '.tar.gz ファイル形式のみサポートされており、圧縮パッケージには {0}.json ファイルが含まれている必要があります',\n        proxy: '逆プロキシ',\n        alias: 'エイリアス',\n        ftpUser: 'FTPアカウント',\n        ftpPassword: 'FTPパスワード',\n        ftpHelper:\n            'Webサイトを作成すると、対応するFTPアカウントが作成され、FTPディレクトリがWebサイトディレクトリにリンクされます。',\n        remark: '述べる',\n        manageGroup: 'グループを管理します',\n        groupSetting: 'グループ管理',\n        createGroup: 'グループを作成します',\n        appNew: '新しいアプリケーション',\n        appInstalled: 'インストールされたアプリケーション',\n        delete: 'Webサイトを削除します',\n        deleteApp: 'アプリケーションを削除します',\n        deleteBackup: 'バックアップを削除します',\n        domain: 'ドメイン',\n        domainHelper:\n            '1行ごとに1つのドメイン.\\nワイルドカード「*」とIPアドレスをサポートします.\\nポートの追加をサポートします.',\n        addDomain: '追加',\n        domainConfig: 'ドメイン',\n        defaultDoc: '書類',\n        perserver: '並行性',\n        perserverHelper: '現在のサイトの最大並行性を制限します',\n        perip: '単一のIP',\n        peripHelper: '単一のIPへの同時アクセスの最大数を制限する',\n        rate: '交通制限',\n        rateLimit: 'リクエストごとの速度制限',\n        rateHelper: '各リクエストのフローを制限する（ユニット:KB）',\n        rateLimitHelper: '各リクエストの転送速度を制限します（単位: KB/s）',\n        limitHelper: 'フロー制御を有効にします',\n        other: '他の',\n        currentSSL: '現在の証明書',\n        dnsAccount: 'DNSアカウント',\n        applySSL: '証明書申請',\n        SSLList: '証明書リスト',\n        createDnsAccount: 'DNSアカウント',\n        aliyun: 'エイリアン',\n        aliEsa: 'エイリアン ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: '手動解析',\n        key: '鍵',\n        check: 'ビュー',\n        acmeAccountManage: 'ACMEアカウント',\n        email: 'メール',\n        acmeAccount: 'ACMEアカウント',\n        provider: '検証方法',\n        dnsManual: '手動解決',\n        expireDate: '有効期限',\n        brand: '組織',\n        deploySSL: '展開',\n        deploySSLHelper: '証明書を展開しますか？',\n        ssl: '証明書|証明書',\n        dnsAccountManage: 'DNSプロバイダー',\n        renewSSL: '更新します',\n        renewHelper: '必ず証明書を更新しますか？',\n        renewSuccess: '更新証明書',\n        enableHTTPS: '有効にする',\n        aliasHelper: 'エイリアスは、ウェブサイトのディレクトリ名です',\n        lastBackupAt: '最後のバックアップ時間',\n        null: 'なし',\n        nginxConfig: 'nginx構成',\n        websiteConfig: 'ウェブサイトの設定',\n        proxySettings: 'プロキシ設定',\n        advancedSettings: '詳細設定',\n        cacheSettings: 'キャッシュ設定',\n        sniSettings: 'SNI設定',\n        basic: '基本',\n        source: '構成',\n        security: '安全',\n        nginxPer: 'パフォーマンスチューニング',\n        neverExpire: '一度もない',\n        setDefault: 'デフォルトとして設定します',\n        default: 'デフォルト',\n        deleteHelper: '関連するアプリケーションステータスは異常です。確認してください',\n        toApp: 'インストールされているリストに移動します',\n        cycle: 'サイクル',\n        frequency: '頻度',\n        ccHelper: '同じURLを{0}秒以内に{1}を超えて同じURLを要求し、CC防御をトリガーし、このIPをブロックする',\n        mustSave: '有効にするには、変更を保存する必要があります',\n        fileExt: 'ファイル拡張子',\n        fileExtBlock: 'ファイル拡張ブロックリスト',\n        value: '価値',\n        enable: '有効にする',\n        proxyAddress: 'プロキシアドレス',\n        proxyHelper: '例:127.0.0.1:8080',\n        forceDelete: 'フォース削除',\n        forceDeleteHelper: 'フォース削除は、削除プロセス中のエラーを無視し、最終的にメタデータを削除します。',\n        deleteAppHelper: '関連するアプリケーションとアプリケーションのバックアップを同時に削除する',\n        deleteBackupHelper: 'また、Webサイトのバックアップを削除します。',\n        deleteDatabaseHelper: 'ウェブサイトに関連付けられたデータベースも削除します',\n        deleteConfirmHelper:\n            '削除操作を元に戻すことはできません。<span style = \"color:red\"> \"{0}\" </span>を入力して、削除を確認します。',\n        staticPath: '対応するメインディレクトリは ',\n        limit: 'スキーム',\n        blog: 'フォーラム/ブログ',\n        imageSite: '写真サイト',\n        downloadSite: 'サイトをダウンロードします',\n        shopSite: 'モール',\n        doorSite: 'ポータル',\n        qiteSite: '企業',\n        videoSite: 'ビデオ',\n        errLog: 'エラーログ',\n        stopHelper:\n            'サイトを停止した後、正常にアクセスできなくなり、ユーザーは訪問時に現在のサイトの停止ページを表示します。続けたいですか？',\n        startHelper: 'サイトを有効にした後、ユーザーは通常、サイトのコンテンツにアクセスできますが、続行しますか？',\n        sitePath: 'ディレクトリ',\n        siteAlias: 'サイトエイリアス',\n        primaryPath: 'ルートディレクトリ',\n        folderTitle: 'ウェブサイトには主に次のフォルダーが含まれています',\n        wafFolder: 'ファイアウォールルール',\n        indexFolder: 'ウェブサイトルートディレクトリ',\n        sslFolder: 'ウェブサイト証明書',\n        enableOrNot: '有効にする',\n        oldSSL: '既存の証明書',\n        manualSSL: '輸入証明書',\n        select: '選択します',\n        selectSSL: '選択証明書を選択します',\n        privateKey: 'キー（キー）',\n        certificate: '証明書（PEM形式）',\n        HTTPConfig: 'HTTPオプション',\n        HTTPSOnly: 'HTTP要求をブロックします',\n        HTTPToHTTPS: 'HTTPSにリダイレクトします',\n        HTTPAlso: '直接HTTPリクエストを許可します',\n        sslConfig: 'SSLオプション',\n        disableHTTPS: 'httpsを無効にします',\n        disableHTTPSHelper: 'HTTPSを無効にすると、証明書関連の構成が削除されますが、続行しますか？',\n        SSLHelper:\n            '注:違法なWebサイトにSSL証明書を使用しないでください。 nif httpsアクセスを開いた後に使用できません。.',\n        SSLConfig: '証明書設定',\n        SSLProConfig: 'プロトコル設定',\n        supportProtocol: 'プロトコルバージョン',\n        encryptionAlgorithm: '暗号化アルゴリズム',\n        notSecurity: '（安全ではない）',\n        encryptHelper:\n            \"暗号化しようと証明書を発行するための周波数制限がありますが、通常のニーズを満たすには十分です。頻繁に操作すると、発行の失敗が発生します。特定の制限については、参照してください<a target='_blank' href='https://letsencrypt.org/zh-cn/docs /rate-limits/'>公式文書</a>\",\n        ipValue: '価値',\n        ext: 'ファイル拡張子',\n        wafInputHelper: 'ラインごとに入力します。1つの行',\n        data: 'データ',\n        ever: '永続',\n        nextYear: '1年後',\n        noLog: 'ログは見つかりません',\n        defaultServer: 'デフォルトサイト',\n        noDefaultServer: '設定されていません',\n        defaultServerHelper:\n            'デフォルトサイトを設定した後、すべての未バインドのドメイン名とIPはデフォルトサイトにリダイレクトされます\\nこれにより、悪意のある解決を効果的に防ぐことができます\\nただし、WAFの未承認ドメイン名の遮断が失敗することもあります',\n        restoreHelper: 'このバックアップを使用して復元することは間違いありませんか？',\n        websiteDeploymentHelper:\n            'インストールされたアプリケーションを使用するか、新しいアプリケーションを作成してWebサイトを作成します。',\n        websiteStatictHelper: 'ホストにWebサイトディレクトリを作成します。',\n        websiteProxyHelper:\n            'リバースプロキシを使用して、既存のサービスをプロキシします。たとえば、サービスがインストールされ、ポート8080で実行されている場合、プロキシアドレスは「http://127.0.0.1:8080」になります。',\n        runtimeProxyHelper: 'Webサイトランタイムを使用してWebサイトを作成します。',\n        runtime: 'ランタイム',\n        deleteRuntimeHelper:\n            'ランタイムアプリケーションはWebサイトと一緒に削除する必要があります。注意して処理してください',\n        proxyType: 'ネットワークタイプ',\n        unix: 'UNIXネットワーク',\n        tcp: 'TCP/IPネットワーク',\n        phpFPM: 'fpm config',\n        phpConfig: 'php config',\n        updateConfig: 'configを更新します',\n        isOn: 'の上',\n        isOff: 'オフ',\n        rewrite: '擬似静的',\n        rewriteMode: 'スキーム',\n        current: '現在',\n        rewriteHelper:\n            '擬似静的に設定すると、ウェブサイトがアクセスできない場合、デフォルトの設定に戻るようにしてください。',\n        runDir: 'ディレクトリを実行します',\n        runUserHelper:\n            'PHPコンテナランタイム環境を介して展開されているWebサイトの場合、インデックスおよびサブディレクトリのすべてのファイルとフォルダーの所有者とユーザーグループを1000に設定する必要があります。ローカルPHP環境については、ローカルPHP-FPMユーザーとユーザーグループの設定を参照してください。',\n        userGroup: 'ユーザー/グループ',\n        uGroup: 'グループ',\n        proxyPath: 'プロキシパス',\n        proxyPass: 'ターゲットURL',\n        cache: 'キャッシュ',\n        cacheTime: 'キャッシュ期間',\n        enableCache: 'キャッシュ',\n        proxyHost: 'プロキシホスト',\n        disabled: '停止',\n        startProxy: 'これにより、リバースプロキシが開始されます。続けたいですか？',\n        stopProxy: 'これにより、逆プロキシが停止します。続けたいですか？',\n        sourceFile: 'ソース',\n        proxyHelper1: 'このディレクトリにアクセスすると、ターゲットURLのコンテンツが返されて表示されます。',\n        proxyPassHelper: 'ターゲットURLは有効でアクセス可能でなければなりません。',\n        proxyHostHelper: 'リクエストヘッダーのドメイン名をプロキシサーバーに渡します。',\n        replacementHelper: '最大5つの交換を追加できます。交換が不要な場合は、空白のままにしてください。',\n        modifier: '一致するルール',\n        modifierHelper: '例: \"=\"は正確な一致、 \"〜\"は通常の一致、^〜」はパスの始まりなどと一致します。',\n        replace: 'テキスト置換',\n        replaceHelper:\n            'nginxのテキスト置換機能は、リバースプロキシ時にレスポンス内容の文字列を置換することができます。バックエンドから返されるHTML、CSS、JavaScriptなどのファイル内のリンクアドレス、APIアドレスなどを変更するためによく使用されます。正規表現マッチングをサポートしており、複雑なコンテンツ置換のニーズに対応できます。',\n        addReplace: '追加',\n        replaced: '検索文字列（空にすることはできません）',\n        replaceText: '文字列に置き換えます',\n        replacedErr: '検索文字列を空にすることはできません',\n        replacedErr2: '検索文字列を繰り返すことはできません',\n        replacedListEmpty: 'テキスト置換ルールがありません',\n        proxySslName: 'プロキシSNI名',\n        basicAuth: '基本認証',\n        editBasicAuthHelper:\n            'パスワードは非対称的に暗号化されており、反響することはできません。編集はパスワードをリセットする必要があります',\n        antiLeech: '反リーチ',\n        extends: '拡大',\n        browserCache: 'キャッシュ',\n        noModify: '変更しない',\n        serverCache: 'サーバーキャッシュ',\n        leechLog: '反リーチログを記録します',\n        accessDomain: '許可されたドメイン',\n        leechReturn: '応答リソース',\n        noneRef: '空のリファラーを許可します',\n        disable: '有効になっていません',\n        disableLeechHelper: '反リーチを無効にするかどうか',\n        disableLeech: '反リーチを無効にします',\n        ipv6: '緑',\n        leechReturnError: 'HTTPステータスコードを入力してください',\n        blockedRef: '非標準のリファラーを許可',\n        accessControl: '反リーチ制御',\n        leechcacheControl: 'キャッシュ制御',\n        logEnableControl: '静的アセットのリクエストを記録',\n        leechSpecialValidHelper:\n            '「空のリファラーを許可」を有効にすると、リファラーのないリクエスト（直接アクセス等）はブロックされません。「非標準のリファラーを許可」を有効にすると、http/httpsで始まらないリファラー（クライアントからのリクエスト等）をすべて許可します。',\n        leechInvalidReturnHelper: 'ブロック後に返すHTTPステータスコード',\n        leechlogControlHelper:\n            '静的アセットのリクエストを記録します。運用環境では過剰で無意味なログを避けるため、通常は無効にします',\n        selectAcme: 'ACMEアカウントを選択します',\n        imported: '手動で作成されます',\n        importType: 'インポートタイプ',\n        pasteSSL: 'コードを貼り付けます',\n        localSSL: 'サーバーファイルを選択します',\n        privateKeyPath: '秘密キーファイル',\n        certificatePath: '証明書ファイル',\n        ipWhiteListHelper: 'IP AllowListの役割:すべてのルールはIP AllowListに対して無効です',\n        redirect: 'リダイレクト',\n        sourceDomain: 'ソースドメイン',\n        targetURL: 'ターゲットURLアドレス',\n        keepPath: 'uri params',\n        path: 'パス',\n        redirectType: 'リダイレクトタイプ',\n        redirectWay: '方法',\n        keep: '保つ',\n        notKeep: '保持しないでください',\n        redirectRoot: 'ホームページにリダイレクトします',\n        redirectHelper: '301永久リダイレクト、302一時的なリダイレクト',\n        changePHPVersionWarn:\n            'PHPバージョンを切り替えると、元のPHPコンテナが削除されます（マウントされたWebサイトコードは失われません）、続行しますか？',\n        changeVersion: 'スイッチバージョン',\n        retainConfig: 'php-fpm.confとphp.iniファイルを保持するかどうか',\n        runDirHelper2: 'セカンダリランニングディレクトリがインデックスディレクトリの下にあることを確認してください',\n        openrestyHelper: 'OpenRestyデフォルトのHTTPポート:{0} HTTPSポート:{1}。',\n        primaryDomainHelper: '例:example.comまたはexample.com:8080',\n        acmeAccountType: 'アカウントタイプ',\n        keyType: 'キーアルゴリズム',\n        tencentCloud: 'テンセントクラウド',\n        containWarn: 'ドメイン名にはメインドメインが含まれています。再入力してください',\n        rewriteHelper2:\n            'アプリストアからインストールされたWordPressなどのアプリケーションは、通常、擬似静的設定が事前に設定されています。それらを再設定すると、エラーが発生する可能性があります。',\n        websiteBackupWarn:\n            'ローカルバックアップのインポートをサポートするだけで、他のマシンからバックアップをインポートすることは回復の故障を引き起こす可能性があります',\n        ipWebsiteWarn:\n            'ドメイン名としてIPを持つWebサイトは、正常にアクセスするデフォルトサイトとして設定する必要があります。',\n        hstsHelper: 'HSTを有効にすると、Webサイトのセキュリティが向上する可能性があります',\n        includeSubDomains: 'サブドメイン',\n        hstsIncludeSubDomainsHelper: '有効化すると、HSTSポリシーが現在のドメインのすべてのサブドメインに適用されます。',\n        defaultHtml: 'デフォルトページ',\n        website404: 'ウェブサイト404エラーページ',\n        domain404: 'ウェブサイトドメインは存在しません',\n        indexHtml: '静的ウェブサイトのインデックス',\n        stopHtml: 'ウェブサイトを停止しました',\n        indexPHP: 'PHP Webサイトのインデックス',\n        sslExpireDate: '証明書の有効期限',\n        website404Helper:\n            'ウェブサイト404エラーページは、PHPランタイム環境Webサイトと静的Webサイトのみをサポートしています',\n        sni: '起源は悲しい',\n        sniHelper:\n            '逆プロキシバックエンドがHTTPSの場合、Origin SNIを設定する必要がある場合があります。詳細については、CDNサービスプロバイダーのドキュメントを参照してください。',\n        huaweicloud: 'huaweiCloud',\n        createDb: 'データベースを作成',\n        enableSSLHelper: 'SSLの有効化に失敗しても、ウェブサイトの作成には影響しません。',\n        batchAdd: 'ドメインを一括追加',\n        batchInput: 'バッチ入力',\n        domainNotFQDN: 'このドメインはパブリックネットワークからアクセスできない可能性があります',\n        domainInvalid: 'ドメイン形式が正しくありません',\n        domainBatchHelper: '1行に1ドメイン、形式: domain:port@ssl\\n例: example.com:443@ssl または example.com',\n        generateDomain: '生成',\n        global: 'グローバル',\n        subsite: 'サブサイト',\n        subsiteHelper:\n            'サブサイトは、既存のPHPまたは静的ウェブサイトのディレクトリをルートディレクトリとして選択できます。',\n        parentWebsite: '親ウェブサイト',\n        deleteSubsite: '現在のウェブサイトを削除するには、まずサブサイト {0} を削除する必要があります。',\n        loadBalance: 'ロードバランシング',\n        server: 'ノード',\n        algorithm: 'アルゴリズム',\n        ipHash: 'IPハッシュ',\n        ipHashHelper:\n            'クライアントのIPアドレスに基づいてリクエストを特定のサーバーに分散し、特定のクライアントが常に同じサーバーにルーティングされるようにします。',\n        leastConn: '最小接続',\n        leastConnHelper: 'アクティブな接続数が最も少ないサーバーにリクエストを送信します。',\n        leastTime: '最小時間',\n        leastTimeHelper: 'アクティブな接続時間が最も短いサーバーにリクエストを送信します。',\n        defaultHelper:\n            'デフォルトの方法では、リクエストは各サーバーに均等に分散されます。サーバーに重み設定がある場合、指定された重みに従ってリクエストが分散されます。重みが高いサーバーほど多くのリクエストを受け取ります。',\n        weight: '重み',\n        maxFails: '最大失敗回数',\n        maxConns: '最大接続数',\n        strategy: '戦略',\n        strategyDown: '無効',\n        strategyBackup: 'バックアップ',\n        ipHashBackupErr: 'IPハッシュはバックアップノードをサポートしていません',\n        failTimeout: '障害タイムアウト',\n        failTimeoutHelper:\n            'サーバーのヘルスチェックの時間ウィンドウの長さ。この期間内に累積障害回数がしきい値に達すると、サーバーは一時的に削除され、同じ時間経過後に再試行されます。デフォルト 10 秒',\n        staticChangePHPHelper: '現在は静的ウェブサイトですが、PHPウェブサイトに切り替えることができます。',\n        proxyCache: 'リバースプロキシキャッシュ',\n        cacheLimit: 'キャッシュスペース制限',\n        shareCache: 'キャッシュカウントメモリサイズ',\n        cacheExpire: 'キャッシュ有効期限',\n        shareCacheHelper: '1Mのメモリで約8000個のキャッシュオブジェクトを保存できます。',\n        cacheLimitHelper: '制限を超えると、古いキャッシュが自動的に削除されます。',\n        cacheExpireHelper: '有効期限内にヒットしないキャッシュは削除されます。',\n        realIP: 'リアルIP',\n        ipFrom: 'IPソース',\n        ipFromHelper:\n            '信頼できるIPソースを設定することで、OpenRestyはHTTPヘッダーのIP情報を分析し、訪問者のリアルIPアドレスを正確に識別して記録します（アクセスログを含む）。',\n        ipFromExample1: 'フロントエンドがFrpなどのツールの場合、FrpのIPアドレス（例：127.0.0.1）を入力できます。',\n        ipFromExample2: 'フロントエンドがCDNの場合、CDNのIP範囲を入力できます。',\n        ipFromExample3:\n            '不明な場合は、0.0.0.0/0（IPv4）または::/0（IPv6）を入力できます。[注意：任意のソースIPを許可することは安全ではありません。]',\n        http3Helper:\n            'HTTP/3はHTTP/2のアップグレード版で、より高速な接続速度とパフォーマンスを提供します。ただし、すべてのブラウザがHTTP/3をサポートしているわけではなく、有効にすると一部のブラウザがサイトにアクセスできなくなる可能性があります。',\n        cors: 'クロスオリジンリソース共有(CORS)',\n        enableCors: 'CORSを有効にする',\n        allowOrigins: '許可されたドメイン',\n        allowMethods: '許可されたリクエストメソッド',\n        allowHeaders: '許可されたリクエストヘッダー',\n        allowCredentials: 'Cookieの送信を許可する',\n        preflight: 'プリフライトリクエストの高速応答',\n        preflightHleper:\n            '有効にすると、ブラウザがクロスオリジンのプリフライトリクエスト（OPTIONSリクエスト）を送信した場合、システムは自動的に204ステータスコードを返し、必要なクロスオリジン応答ヘッダーを設定します',\n        changeDatabase: 'データベースを切り替え',\n        changeDatabaseHelper1: 'データベースの関連付けは、ウェブサイトのバックアップと復元に使用されます。',\n        changeDatabaseHelper2: '別のデータベースに切り替えると、以前のバックアップが復元できなくなる可能性があります。',\n        saveCustom: 'テンプレートとして保存',\n        rainyun: '雨雲',\n        volcengine: 'volcengine',\n        runtimePortHelper: '現在の実行環境には複数のポートがあります。プロキシポートを選択してください。',\n        runtimePortWarn: '現在の実行環境にはポートがありません。プロキシできません',\n        cacheWarn: 'まずリバースプロキシのキャッシュスイッチをオフにしてください',\n        loadBalanceHelper:\n            '負荷分散を作成した後、「リバースプロキシ」に移動し、プロキシを追加してバックエンドアドレスを次のように設定してください：http://<負荷分散名>。',\n        favorite: 'お気に入り',\n        cancelFavorite: 'お気に入りを解除',\n        useProxy: 'プロキシを使用',\n        useProxyHelper: 'パネル設定のプロキシサーバーアドレスを使用',\n        westCN: '西部デジタル',\n        openBaseDir: 'クロスサイト攻撃を防ぐ',\n        openBaseDirHelper:\n            'open_basedir は PHP ファイルのアクセスパスを制限し、クロスサイトアクセスを防ぎセキュリティを向上させるために使用されます',\n        serverCacheTime: 'サーバーキャッシュ時間',\n        serverCacheTimeHelper:\n            'リクエストがサーバー上でキャッシュされる時間。この期間中、同一のリクエストはオリジンサーバーにリクエストせず、キャッシュされた結果を直接返します。',\n        browserCacheTime: 'ブラウザキャッシュ時間',\n        browserCacheTimeHelper:\n            '静的リソースがブラウザのローカルにキャッシュされる時間、冗長なリクエストを減らします。有効期限前にユーザーがページをリフレッシュすると、ローカルキャッシュが直接使用されます。',\n        donotLinkeDB: 'データベースをリンクしない',\n        toWebsiteDir: 'ウェブサイトディレクトリに入る',\n        execParameters: '実行パラメータ',\n        extCommand: '補足コマンド',\n        mirror: 'ミラーソース',\n        execUser: '実行ユーザー',\n        execDir: '実行ディレクトリ',\n        packagist: '中国フルミラー',\n        batchOperate: 'バッチ操作',\n        batchOperateHelper: 'ウェブサイトをバッチ{0}しますか？',\n        stream: 'TCP/UDP プロキシ',\n        streamPorts: '待ち受けポート',\n        streamPortsHelper:\n            '外部リスニングポート番号を設定します。クライアントはこのポートを通じてサービスにアクセスします。カンマで区切ってください。例：5222,5223',\n        streamHelper: 'TCP/UDP ポート転送とロードバランシング',\n        udp: 'UDPを有効化',\n        syncHtmlHelper: 'PHP と静的ウェブサイトに同期',\n        wafValueHelper: '値',\n        parentWbeiste: '親サイト',\n        shareCahe: 'キャッシュ数メモリサイズ',\n        shareCaheHelper: 'メモリ1Mあたり約8000件のキャッシュを保存可能',\n        cacheExpireJHelper: '有効期限後に未ヒットならキャッシュを削除',\n    },\n    php: {\n        short_open_tag: '短いタグサポート',\n        max_execution_time: '最大スクリプト実行時間',\n        max_input_time: '最大入力時間',\n        memory_limit: 'スクリプトメモリ制限',\n        post_max_size: 'データの最大サイズを投稿します',\n        file_uploads: 'ファイルのアップロードを許可するかどうか',\n        upload_max_filesize: 'ファイルのアップロードに許可されている最大サイズ',\n        max_file_uploads: '同時にアップロードできるファイルの最大数',\n        default_socket_timeout: 'ソケットタイムアウト',\n        error_reporting: 'エラーレベル',\n        display_errors: '詳細なエラー情報を出力するかどうか',\n        cgi_fix_pathinfo: 'Pathinfoを開くかどうか',\n        date_timezone: 'タイムゾーン',\n        disableFunction: '機能を無効にします',\n        disableFunctionHelper: 'execなど、無効にする関数を入力してください。複数、分割を使用してください',\n        uploadMaxSize: 'アップロード制限',\n        indexHelper:\n            'PHP Webサイトの通常の操作を確保するために、コードをインデックスディレクトリに配置して、名前変更を避けてください',\n        extensions: '拡張テンプレート',\n        extension: '拡大',\n        extensionHelper: '複数の拡張機能を使用して、分割してください',\n        toExtensionsList: '拡張リストを表示します',\n        containerConfig: 'コンテナ設定',\n        containerConfigHelper: '環境変数などの情報は、作成完了後に設定 - コンテナ設定で変更できます',\n        dateTimezoneHelper: '例：TZ=Asia/Shanghai（必要に応じて追加してください）',\n        extensionsHelper: '複数拡張はカンマで区切ってください',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: 'サーバー名のハッシュテーブルサイズ',\n        clientHeaderBufferSizeHelper: 'クライアントが要求するヘッダーバッファサイズ',\n        clientMaxBodySizeHelper: '最大アップロードファイル',\n        keepaliveTimeoutHelper: '接続タイムアウト',\n        gzipMinLengthHelper: '最小圧縮ファイル',\n        gzipCompLevelHelper: '圧縮率',\n        gzipHelper: '伝送の圧縮を有効にします',\n        connections: 'アクティブな接続',\n        accepts: '受け入れます',\n        handled: '処理',\n        requests: 'リクエスト',\n        reading: '読む',\n        writing: '書き込み',\n        waiting: '待っている',\n        status: '現在のステータス',\n        configResource: '構成',\n        saveAndReload: '保存してリロードします',\n        clearProxyCache: '逆プロキシキャッシュをきれいにします',\n        clearProxyCacheWarn:\n            'キャッシュで構成されたすべてのWebサイトが影響を受け、「OpenResty」が再起動されます。続けたいですか？',\n        create: 'モジュールを追加',\n        update: 'モジュールを編集',\n        params: 'パラメータ',\n        packages: 'パッケージ',\n        script: 'スクリプト',\n        module: 'モジュール',\n        build: 'ビルド',\n        buildWarn:\n            'OpenRestyのビルドには一定量のCPUとメモリを確保する必要があり、時間がかかる場合がありますので、お待ちください。',\n        mirrorUrl: 'ソフトウェアソース',\n        paramsHelper: '例：--add-module=/tmp/ngx_brotli',\n        packagesHelper: '例：git,curl カンマ区切り',\n        scriptHelper:\n            'コンパイル前に実行するスクリプト、通常はモジュールソースコードのダウンロード、依存関係のインストールなど',\n        buildHelper:\n            'モジュールの追加/変更後にビルドをクリックします。ビルドが成功すると、OpenRestyは自動的に再起動します。',\n        defaultHttps: 'HTTPS 改ざん防止',\n        defaultHttpsHelper1: 'これを有効にすると、HTTPS 改ざん問題を解決できます。',\n        sslRejectHandshake: 'デフォルト SSL ハンドシェイクを拒否',\n        sslRejectHandshakeHelper:\n            '有効にすると証明書の漏洩を防げますが、デフォルト Web サイトを設定するとこの設定は無効になります',\n    },\n    ssl: {\n        create: 'リクエスト',\n        provider: 'タイプ',\n        manualCreate: '手動で作成されます',\n        acmeAccount: 'ACMEアカウント',\n        resolveDomain: 'ドメイン名を解決します',\n        err: 'エラー',\n        value: '記録値',\n        dnsResolveHelper: 'DNS Resolution Service Providerにアクセスして、次の解像度レコードを追加してください。',\n        detail: '詳細',\n        msg: '情報',\n        ssl: '証明書',\n        key: '秘密鍵',\n        startDate: '有効時間',\n        organization: '発行組織',\n        renewConfirm: 'これにより、ドメイン名{0}の新しい証明書が更新されます。続けたいですか？',\n        autoRenew: '自動更新',\n        autoRenewHelper: '有効期限の30日前に自動的に更新します',\n        renewSuccess: '更新成功',\n        renewWebsite:\n            'この証明書は次のWebサイトに関連付けられており、アプリケーションはこれらのWebサイトに同時に適用されます',\n        createAcme: 'アカウントを作成する',\n        acmeHelper: 'ACMEアカウントは、無料の証明書を申請するために使用されます',\n        upload: '輸入',\n        applyType: 'タイプ',\n        apply: '更新します',\n        applyStart: '証明書申請が開始されます',\n        getDnsResolve: 'DNS解像度の値を取得してください、待ってください...',\n        selfSigned: '自己署名CA',\n        ca: '証明書当局',\n        commonName: '一般名',\n        caName: '証明書当局名',\n        company: '組織名',\n        department: '組織ユニット名',\n        city: 'ローカリティ名',\n        province: '州または州の名前',\n        country: '国名（2文字コード）',\n        commonNameHelper: '例えば、',\n        selfSign: '発行証明書',\n        days: '有効期間',\n        domainHelper: '1行ごとに1つのドメイン名、 *およびIPアドレスをサポートします',\n        pushDir: '証明書をローカルディレクトリにプッシュします',\n        dir: 'ディレクトリ',\n        pushDirHelper: '証明書ファイル「FullChain.PEM」とキーファイル「Privkey.Pem」がこのディレクトリで生成されます。',\n        organizationDetail: '組織の詳細',\n        fromWebsite: 'ウェブサイトから',\n        dnsMauanlHelper: '手動解像度モードでは、作成後に適用ボタンをクリックしてDNS解像度値を取得する必要があります',\n        httpHelper:\n            'HTTPモードを使用するにはOpenRestyをインストールする必要があり、ワイルドカードドメイン証明書の申請はサポートされていません。',\n        buypassHelper: '中国本土では、ブライパスはアクセスできません',\n        googleHelper: 'EAB HMACキーとEABキッドを取得する方法',\n        googleCloudHelper: 'GoogleクラウドAPIは中国本土のほとんどの地域でアクセスできません',\n        skipDNSCheck: 'DNSチェックをスキップします',\n        skipDNSCheckHelper: '認定リクエスト中にタイムアウトの問題が発生した場合にのみ、こちらを確認してください。',\n        cfHelper: 'グローバルAPIキーを使用しないでください',\n        deprecated: '非推奨されます',\n        deprecatedHelper:\n            'メンテナンスは停止されており、将来のバージョンでは放棄される可能性があります。分析にはTencent Cloudメソッドを使用してください',\n        disableCNAME: 'cnameを無効にします',\n        disableCNAMEHelper: 'ドメイン名にCNAMEレコードがあり、リクエストが失敗するかどうかを確認してください。',\n        nameserver: 'DNSサーバー',\n        nameserverHelper: 'カスタムDNSサーバーを使用して、ドメイン名を確認します。',\n        edit: '編集証明書',\n        execShell: '認定リクエスト後にスクリプトを実行します。',\n        shell: 'スクリプトコンテンツ',\n        shellHelper:\n            'スクリプトのデフォルトの実行ディレクトリは、1Panelインストールディレクトリです。証明書がローカルディレクトリにプッシュされた場合、実行ディレクトリは証明書プッシュディレクトリになります。デフォルトの実行タイムアウトは30分です。',\n        customAcme: 'カスタム ACME サービス',\n        customAcmeURL: 'ACME サービス URL',\n        baiduCloud: '百度クラウド',\n        pushNode: '他のノードに同期',\n        pushNodeHelper: '申請/更新後に選択したノードにプッシュ',\n        fromMaster: 'マスターノードからのプッシュ',\n        hostedZoneID: 'Hosted Zone ID',\n        isIP: 'IP証明書',\n        useEAB: 'EAB認証を使用',\n    },\n    firewall: {\n        create: 'ルールを作成します',\n        edit: 'ルールを編集します',\n        ccDeny: 'CC保護',\n        ipWhiteList: 'IP AllowList',\n        ipBlockList: 'IPブロックリスト',\n        fileExtBlockList: 'ファイル拡張ブロックリスト',\n        urlWhiteList: 'URL AllowList',\n        urlBlockList: 'URLブロックリスト',\n        argsCheck: 'パラメーターチェックを取得します',\n        postCheck: 'パラメーターの検証を投稿します',\n        cookieBlockList: 'クッキーブロックリスト',\n        dockerHelper:\n            '現在のファイアウォールではコンテナのポートマッピングを無効にできません。インストール済みアプリケーションは【インストール済み】ページでアプリケーションパラメータを編集し、ポート開放ルールを設定できます。',\n        iptablesHelper:\n            'システムが {0} ファイアウォールを使用していることを検出しました。iptables に切り替えるには、まず手動でアンインストールしてください！',\n        quickJump: 'クイックアクセス',\n        used: '使用済み',\n        unUsed: '未使用',\n        dockerRestart: 'ファイアウォール操作にはDockerサービスの再起動が必要です',\n        firewallHelper: '{0}システムファイアウォール',\n        firewallNotStart: '現在、システムファイアウォールは有効になっていません。最初に有効にします。',\n        restartFirewallHelper: 'この操作は、現在のファイアウォールを再起動します。続けたいですか？',\n        stopFirewallHelper: 'これにより、サーバーはセキュリティ保護を失います。続けたいですか？',\n        startFirewallHelper:\n            'ファイアウォールが有効になった後、サーバーのセキュリティをよりよく保護できます。続けたいですか？',\n        noPing: 'pingを無効にします',\n        enableBanPing: 'Ping禁止',\n        disableBanPing: 'Ping禁止解除',\n        noPingTitle: 'pingを無効にします',\n        noPingHelper: 'これによりPingが無効になり、サーバーはICMP応答をエコーし​​ません。続けたいですか？',\n        onPingHelper: 'これによりPingが可能になり、ハッカーはサーバーを発見する場合があります。続けたいですか？',\n        changeStrategy: '{0}戦略を変更します',\n        changeStrategyIPHelper1:\n            'IPアドレス戦略を[拒否]に変更します。IPアドレスが設定された後、サーバーへのアクセスは禁止されています。続けたいですか？',\n        changeStrategyIPHelper2:\n            'IPアドレス戦略を[許可]に変更します。IPアドレスが設定された後、通常のアクセスが復元されます。続けたいですか？',\n        changeStrategyPortHelper1:\n            'ポートポリシーを[ドロップ]に変更します。ポートポリシーが設定された後、外部アクセスが拒否されます。続けたいですか？',\n        changeStrategyPortHelper2:\n            'ポートポリシーを[受け入れる]に変更します。ポートポリシーが設定されると、通常のポートアクセスが復元されます。続けたいですか？',\n        stop: '停止',\n        portFormatError: 'このフィールドは有効なポートでなければなりません。',\n        portHelper1: '複数のポート、例えば8080および8081',\n        portHelper2: '範囲ポート、例えば8080-8089',\n        changeStrategyHelper:\n            '[{1}] {0}戦略を[{2}]に変更します。設定後、{0}は外部から{2}にアクセスします。続けたいですか？',\n        strategy: '戦略',\n        accept: '受け入れる',\n        drop: '落とす',\n        anyWhere: 'どれでも',\n        address: '指定されたIPS',\n        addressHelper: 'IPアドレスまたはIPセグメントをサポートします',\n        allow: '許可する',\n        deny: '拒否',\n        addressFormatError: 'このフィールドは有効なIPアドレスでなければなりません。',\n        addressHelper1: 'IPアドレスまたはIP範囲をサポートします。たとえば、「172.16.10.11」または「172.16.10.0/24」。',\n        addressHelper2: '複数のIPアドレスの場合、コンマと分離します。たとえば、「172.16.10.11、172.16.0.0/24」。',\n        allIP: 'すべてのIP',\n        portRule: 'ルール|ルール',\n        forwardRule: 'ポートフォワードルール|ポートフォワードルール',\n        ipRule: 'IPルール|IPルール',\n        userAgent: 'ユーザーエージェントフィルター',\n        sourcePort: 'ソースポート',\n        targetIP: '宛先IP',\n        targetPort: '宛先ポート',\n        forwardHelper1: 'ローカルポートに転送する場合は、宛先IPを「127.0.0.1」に設定する必要があります。',\n        forwardHelper2: '宛先IPを空白のままにして、ローカルポートに転送します。',\n        forwardPortHelper: 'ポート範囲をサポートします。例: 8080-8089',\n        forwardInboundInterface: '転送入站ネットワークインターフェース',\n        exportHelper: '{0} 件のファイアウォールルールをエクスポートします。続行しますか？',\n        importSuccess: '{0} 件のルールを正常にインポートしました',\n        importPartialSuccess: 'インポート完了: {0} 件成功、{1} 件失敗',\n        ipv4Limit: '現在の操作は IPv4 アドレスのみをサポートしています',\n        basicStatus: '現在のチェーン {0} は未バインドです。まずバインドしてください！',\n        baseIptables: 'iptables サービス',\n        forwardIptables: 'iptables ポート転送サービス',\n        advanceIptables: 'iptables 高度な設定サービス',\n        initMsg: '{0} を初期化します。続行しますか？',\n        initHelper:\n            '{0} が初期化されていないことを検出しました。上部ステータスバーの初期化ボタンをクリックして設定してください！',\n        bindHelper: 'バインド - ファイアウォールルールは状態がバインドされている場合のみ有効になります。確認しますか？',\n        unbindHelper:\n            'アンバインド - アンバインドすると、追加されたすべてのファイアウォールルールが無効になります。注意して操作してください。確認しますか？',\n        defaultStrategy: '現在のチェーン {0} のデフォルトポリシーは {1} です',\n        defaultStrategy2:\n            '現在のチェーン {0} のデフォルトポリシーは {1} です。現在の状態は未バインドです。追加されたファイアウォールルールはバインド後に有効になります！',\n        filterRule: 'フィルタールール',\n        filterHelper:\n            'フィルタールールを使用すると、INPUT/OUTPUT レベルでネットワークトラフィックを制御できます。システムをロックしないように注意して設定してください。',\n        chain: 'チェーン',\n        targetChain: 'ターゲットチェーン',\n        sourceIP: '送信元 IP',\n        destIP: '宛先 IP',\n        inboundDirection: 'インバウンド方向',\n        outboundDirection: 'アウトバウンド方向',\n        destPort: '宛先ポート',\n        action: 'アクション',\n        reject: '拒否',\n        sourceIPHelper: 'CIDR 形式、例: 192.168.1.0/24。すべてのアドレスの場合は空のまま',\n        destIPHelper: 'CIDR 形式、例: 10.0.0.0/8。すべてのアドレスの場合は空のまま',\n        portHelper: '0 は任意のポートを意味します',\n        allPorts: 'すべてのポート',\n        deleteRuleConfirm: '{0} 個のルールを削除します。続行しますか？',\n        advancedControl: '高度制御',\n        advancedControlNotAvailable: '{0} ファイアウォールを使用中。高度ルールは iptables のみ対応',\n        destination: '宛先',\n    },\n    runtime: {\n        runtime: 'ランタイム',\n        workDir: '作業ディレクトリ',\n        localHelper:\n            'ローカル環境のインストールおよびオフライン環境の使用に関する問題については、以下を参照してください ',\n        versionHelper: 'PHPバージョン、例えばv8.0',\n        buildHelper:\n            '拡張機能が多いほど、イメージ作成時にCPUの負荷が高くなります。環境作成後に拡張機能をインストールすることもできます。',\n        openrestyWarn: 'PHPは、使用するためにバージョン1.21.4.1以降にOpenRestyにアップグレードする必要があります',\n        toupgrade: 'アップグレードします',\n        edit: 'ランタイムを編集します',\n        extendHelper:\n            'リストにない拡張機能は手動で入力して選択できます。例えば、「sockets」と入力し、ドロップダウンリストの最初の項目を選択して拡張機能リストを表示します。',\n        rebuildHelper: '拡張機能を編集した後、有効にするためにPHPアプリケーションを再構築する必要があります',\n        rebuild: 'PHPアプリを再構築します',\n        source: 'PHP拡張ソース',\n        ustc: '中国科学技術大学',\n        netease: 'netease',\n        aliyun: 'アリババクラウド',\n        default: 'デフォルト',\n        tsinghua: 'ツィンガ大学',\n        xtomhk: 'Xtom Mirror Station（香港）',\n        xtom: 'XTOMミラーステーション（グローバル）',\n        phpsourceHelper: 'ネットワーク環境に従って適切なソースを選択してください。',\n        appPort: 'アプリポート',\n        externalPort: '外部ポート',\n        packageManager: 'パッケージマネージャー',\n        codeDir: 'コードディレクトリ',\n        appPortHelper: 'アプリケーションで使用されるポート。',\n        externalPortHelper: '港は外の世界にさらされました。',\n        runScript: 'スクリプトを実行します',\n        runScriptHelper: '起動コマンドリストは、ソースディレクトリのpackage.jsonファイルから解析されます。',\n        open: '開ける',\n        operatorHelper: '{0}操作は、選択した動作環境で実行されます。続けたいですか？',\n        taobao: 'タオバオ',\n        tencent: 'テンセント',\n        imageSource: '画像ソース',\n        moduleManager: 'モジュール管理',\n        module: 'モジュール',\n        nodeOperatorHelper:\n            '{0} {1}モジュールですか？操作は、動作環境で異常を引き起こす可能性があります。進む前に確認してください',\n        customScript: 'カスタムスタートアップコマンド',\n        customScriptHelper:\n            '完全な起動コマンドを入力してください。例：npm run start。PM2の起動コマンドはpm2-runtimeに置き換えてください。そうしないと起動に失敗します。',\n        portError: '同じポートを繰り返さないでください。',\n        systemRestartHelper: 'ステータスの説明:中断 - システムの再起動によりステータスの取得が失敗しました',\n        javaScriptHelper: '完全な起動コマンドを提供します。たとえば、「Java -Jar Halo.Jar -XMX1024M -XMS256M」。',\n        javaDirHelper: 'ディレクトリにはjarファイルが含まれている必要があり、サブディレクトリも受け入れられます',\n        goHelper: '完全な起動コマンドを提供します。たとえば、「Go Run Main.go」または「./Main」。',\n        goDirHelper: 'ディレクトリまたはサブディレクトリには、goファイルまたはバイナリファイルを含める必要があります。',\n        pythonHelper:\n            '完全な起動コマンドを提供します。たとえば、「PIP Install -R Repormations.txt && python manage.py runserver 0.0.0.0:5000」。',\n        dotnetHelper: '完全な起動コマンドを入力してください。例えば、dotnet MyWebApp.dll',\n        dirHelper: 'ノート: コンテナ内のディレクトリパスを入力してください',\n        concurrency: '並行処理スキーム',\n        loadStatus: '負荷状態',\n        extraHosts: 'ホストマッピング',\n        extension: '拡張',\n        installExtension: '拡張 {0} をインストールしますか',\n        loadedExtension: 'ロード済み拡張',\n        popularExtension: '人気の拡張',\n        uninstallExtension: '拡張 {0} をアンインストールしますか',\n        phpConfigHelper: '設定変更には環境再起動が必要です。続行しますか',\n        operateMode: '動作モード',\n        dynamic: '動的',\n        static: '静的',\n        ondemand: 'オンデマンド',\n        dynamicHelper: '動的にプロセス数を調整。変動トラフィックや低メモリ向け',\n        staticHelper: '固定プロセス数。高並列で安定トラフィック向け、消費高め',\n        ondemandHelper: '必要時に起動/終了。効率は高いが初回応答は遅い',\n        max_children: '作成可能な最大プロセス数',\n        start_servers: '起動時のプロセス数',\n        min_spare_servers: '最小アイドルプロセス数',\n        max_spare_servers: '最大アイドルプロセス数',\n        envKey: '名前',\n        envValue: '値',\n        environment: '環境変数',\n    },\n    process: {\n        pid: 'プロセスID',\n        ppid: '親pid',\n        numThreads: 'スレッド',\n        memory: 'メモリ',\n        diskRead: 'ディスク読み取り',\n        diskWrite: 'ディスク書き込み',\n        netSent: 'アップリンク',\n        netRecv: '下流',\n        numConnections: '接続',\n        startTime: '開始時間',\n        state: '州',\n        running: 'ランニング',\n        sleep: '寝る',\n        stop: '停止',\n        idle: 'アイドル',\n        zombie: 'ゾンビプロセス',\n        wait: '待っている',\n        lock: 'ロック',\n        blocked: 'ブロックされています',\n        cmdLine: 'コマンドを開始します',\n        basic: '基本',\n        mem: 'メモリ',\n        openFiles: 'オープンファイル',\n        env: '環境',\n        noenv: 'なし',\n        net: 'ネットワーク接続',\n        laddr: 'ソースアドレス/ポート',\n        raddr: '宛先アドレス/ポート',\n        stopProcess: '終わり',\n        viewDetails: '詳細',\n        stopProcessWarn: 'このプロセスを終了したいですか（PID:{0}）？',\n        kill: 'プロセス終了',\n        killNow: '今すぐ終了',\n        killHelper: 'プロセス {0} を終了すると一部のプログラムが正常に動作しなくなる可能性があります。続行しますか？',\n        processName: 'プロセス名',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr:\n                'プロセスステータスの取得に失敗しました。スーパーバイザーサービスのステータスを確認してください。',\n            notSupport:\n                'Supervisor サービスが検出されませんでした。スクリプトライブラリページに移動して手動でインストールしてください',\n            list: 'デーモンプロセス|デーモンプロセス',\n            config: 'スーパーバイザー構成',\n            primaryConfig: 'メイン構成ファイルの場所',\n            notSupportCtl:\n                'supervisorctlは検出されません。スクリプトライブラリページに移動して手動でインストールしてください。',\n            user: 'ユーザー',\n            command: '指示',\n            dir: 'ディレクトリ',\n            numprocs: '番号。プロセスの',\n            initWarn:\n                'これにより、メイン構成ファイルの[[include] [include]セクションの「ファイル」値が変更されます。他の構成ファイルのディレクトリは、「{1Panel Installation Directory} /1Panel/tools/supervisord/supervisor.d/」になります。',\n            operatorHelper: '操作{1}は{0}で実行されます、続行しますか？',\n            uptime: '実行時間',\n            notStartWarn: 'スーパーバイザーは開始されません。最初に開始します。',\n            serviceName: 'サービス名',\n            initHelper:\n                'Supervisor サービスが検出されましたが、初期化されていません。上部のステータスバーにある初期化ボタンをクリックして設定してください。',\n            serviceNameHelper: 'Systemctlが管理するスーパーバイザーサービス名、通常は監督者または監督者',\n            restartHelper: 'これにより、初期化後にサービスが再起動され、既存のすべてのデーモンプロセスが停止します。',\n            RUNNING: 'ランニング',\n            STOPPED: '停止',\n            STOPPING: '停止',\n            STARTING: '起動',\n            FATAL: '開始に失敗しました',\n            BACKOFF: '例外を開始します',\n            ERROR: 'エラー',\n            statusCode: 'ステータスコード',\n            manage: '管理',\n            autoRestart: '自動再起動',\n            EXITED: '終了しました',\n            autoRestartHelper: 'プログラムが異常終了した後に自動的に再起動するかどうか',\n            autoStart: '自動起動',\n            autoStartHelper: 'Supervisor 起動後にサービスを自動的に起動するかどうか',\n        },\n    },\n    disk: {\n        management: 'ディスク管理',\n        partition: 'パーティション',\n        unmount: 'アンマウント',\n        unmountHelper: 'パーティション {0} をアンマウントしますか？',\n        mount: 'マウント',\n        partitionAlert:\n            'ディスクのパーティション分割にはディスクのフォーマットが必要で、既存のデータは削除されます。事前にデータを保存またはスナップショットを取ってください。',\n        mountPoint: 'マウントディレクトリ',\n        systemDisk: 'システムディスク',\n        unpartitionedDisk: '未パーティションディスク',\n        handlePartition: '今すぐパーティション',\n        filesystem: 'ファイルシステム',\n        unmounted: 'アンマウント',\n        cannotOperate: '操作不可',\n        systemDiskHelper: 'ヒント: 現在のディスクはシステムディスクです。操作できません。',\n        autoMount: '自動マウント',\n        model: 'デバイスモデル',\n        diskType: 'ディスクタイプ',\n        serial: 'シリアルナンバー',\n        noFail: 'マウント失敗はシステム起動に影響しません',\n    },\n    xpack: {\n        expiresTrialAlert:\n            'ご注意: あなたのProトライアルは{0}日後に終了し、すべてのPro機能が使用できなくなります。適時に更新またはフルバージョンにアップグレードしてください。',\n        expiresAlert:\n            'ご注意: あなたのProライセンスは{0}日後に終了し、すべてのPro機能が使用できなくなります。継続的な使用のために速やかに更新してください。',\n        menu: 'Рro',\n        upage: 'AIウェブサイトビルダー',\n        proAlert: 'この機能を使用するにはProにアップグレードしてください',\n        app: {\n            app: 'APP',\n            title: 'パネルの別名',\n            titleHelper: 'パネルのエイリアスは、APP端末での表示に使用されます（デフォルトのパネルエイリアス）',\n            qrCode: 'QRコード',\n            apiStatusHelper: 'パネルAPPはAPIインターフェース機能を有効にする必要があります',\n            apiInterfaceHelper:\n                'パネルAPIインターフェースアクセスをサポート（この機能はパネルアプリで有効にする必要があります）',\n            apiInterfaceHelper1:\n                'パネルアプリのアクセスには訪問者をホワイトリストに追加する必要があります。固定IPでない場合、0.0.0.0/0（すべての IPv4）、::/0（すべての IPv6）を追加することをお勧めします',\n            qrCodeExpired: 'リフレッシュ時間',\n            apiLeakageHelper: 'QRコードを漏洩しないでください。信頼できる環境でのみ使用してください。',\n        },\n        waf: {\n            name: 'WAF',\n            blackWhite: 'ブラックリストとホワイトリスト',\n            globalSetting: 'グローバル設定',\n            websiteSetting: 'ウェブサイト設定',\n            blockRecords: 'ブロック記録',\n            world: '世界',\n            china: '中国',\n            intercept: 'インターセプト',\n            request: 'リクエスト',\n            count4xx: '4xxの数',\n            count5xx: '5xxの数',\n            todayStatus: '今日のステータス',\n            reqMap: '攻撃マップ（過去30日間）',\n            resource: 'リソース',\n            count: '数',\n            hight: '高い',\n            low: '低い',\n            reqCount: 'リクエスト数',\n            interceptCount: 'インターセプト数',\n            requestTrends: 'リクエストトレンド（過去7日間）',\n            interceptTrends: 'インターセプトトレンド（過去7日間）',\n            whiteList: 'ホワイトリスト',\n            blackList: 'ブラックリスト',\n            ipBlackListHelper: 'ブラックリストに登録されたIPアドレスはウェブサイトへのアクセスがブロックされます',\n            ipWhiteListHelper: 'ホワイトリストに登録されたIPアドレスはすべての制限をバイパスします',\n            uaBlackListHelper: 'ブラックリストに登録されたUser-Agentのリクエストはブロックされます',\n            uaWhiteListHelper: 'ホワイトリストに登録されたUser-Agentのリクエストはすべての制限をバイパスします',\n            urlBlackListHelper: 'ブラックリストに登録されたURLへのリクエストはブロックされます',\n            urlWhiteListHelper: 'ホワイトリストに登録されたURLへのリクエストはすべての制限をバイパスします',\n            ccHelper:\n                'もしサイトが{1}回以上のリクエストを同一IPから{0}秒以内に受けた場合、そのIPは{2}間ブロックされます',\n            blockTime: 'ブロック時間',\n            attackHelper: 'もし累積的なインターセプトが{1}回以上{0}秒以内に発生した場合、そのIPは{2}間ブロックされます',\n            notFoundHelper: 'もし404エラーが{1}回以上{0}秒以内に返された場合、そのIPは{2}間ブロックされます',\n            frequencyLimit: '頻度制限',\n            regionLimit: '地域制限',\n            defaultRule: 'デフォルトルール',\n            accessFrequencyLimit: 'アクセス頻度制限',\n            attackLimit: '攻撃頻度制限',\n            notFoundLimit: '404頻度制限',\n            urlLimit: 'URL頻度制限',\n            urlLimitHelper: '単一URLのアクセス頻度を設定します',\n            sqliDefense: 'SQLインジェクション防止',\n            sqliHelper: 'リクエストでSQLインジェクションを検出してブロックします',\n            xssHelper: 'リクエストでXSSを検出してブロックします',\n            xssDefense: 'XSS防止',\n            uaDefense: '悪意のあるUser-Agentルール',\n            uaHelper: '一般的な悪意のあるボットを識別するルールが含まれています',\n            argsDefense: '悪意のあるパラメータルール',\n            argsHelper: '悪意のあるパラメータを含むリクエストをブロックします',\n            cookieDefense: '悪意のあるCookieルール',\n            cookieHelper: 'リクエストに悪意のあるCookieを持ち込むことを禁止します',\n            headerDefense: '悪意のあるヘッダールール',\n            headerHelper: '悪意のあるヘッダーを含むリクエストを禁止します',\n            httpRule: 'HTTPリクエストメソッドルール',\n            httpHelper:\n                'アクセスを許可するメソッドタイプを設定します。特定のタイプのアクセスを制限したい場合は、そのタイプのボタンをオフにしてください。例えば、GETタイプのみのアクセスを許可する場合は、GET以外のボタンをオフにする必要があります',\n            geoRule: '地域アクセス制限',\n            geoHelper:\n                '特定の地域からのウェブサイトへのアクセスを制限します。例えば、中国本土からのアクセスを許可し、それ以外の地域からのリクエストをブロックすることができます',\n            ipLocation: 'IP位置',\n            action: 'アクション',\n            ruleType: '攻撃タイプ',\n            ipHelper: 'IPアドレスを入力してください',\n            attackLog: '攻撃ログ',\n            rule: 'ルール',\n            ipArr: 'IPV4範囲',\n            ipStart: '開始IP',\n            ipEnd: '終了IP',\n            ipv4: 'IPv4',\n            ipv6: 'IPv6',\n            urlDefense: 'URLルール',\n            urlHelper: '禁止されたURL',\n            dirFilter: 'ディレクトリフィルター',\n            sqlInject: 'SQLインジェクション',\n            xss: 'XSS',\n            phpExec: 'PHPスクリプト実行',\n            oneWordTrojan: 'ワンワードトロイの木馬',\n            appFilter: '危険なディレクトリフィルタリング',\n            webShell: 'Webシェル',\n            args: '悪意のあるパラメータ',\n            protocolFilter: 'プロトコルフィルター',\n            javaFilter: 'Java危険ファイルフィルタリング',\n            scannerFilter: 'スキャナーフィルター',\n            escapeFilter: 'エスケープフィルター',\n            customRule: 'カスタムルール',\n            httpMethod: 'HTTPメソッドフィルター',\n            fileExt: 'ファイルアップロード制限',\n            fileExtHelper: 'アップロード禁止ファイル拡張子',\n            deny: '禁止',\n            allow: '許可',\n            field: 'オブジェクト',\n            pattern: '条件',\n            ruleContent: 'コンテンツ',\n            contain: '含む',\n            equal: '等しい',\n            regex: '正規表現',\n            notEqual: '等しくない',\n            customRuleHelper: '指定された条件に基づいてアクションを実行',\n            actionAllow: '許可',\n            blockIP: 'IPをブロック',\n            code: 'ステータスコードを返す',\n            noRes: '切断（444）',\n            badReq: '無効なパラメータ（400）',\n            forbidden: 'アクセス禁止（403）',\n            serverErr: 'サーバーエラー（500）',\n            resHtml: '応答ページ',\n            allowHelper: 'アクセスを許可すると、後続のWAFルールをスキップします。慎重に使用してください',\n            captcha: '人間と機械の検証',\n            fiveSeconds: '5秒検証',\n            location: '地域',\n            redisConfig: 'Redis設定',\n            redisHelper: 'Redisを有効にして、一時的にブロックされたIPを永続化します',\n            wafHelper: 'WAFを閉じると、すべてのウェブサイトが保護を失います',\n            attackIP: '攻撃IP',\n            attackParam: '攻撃詳細',\n            execRule: 'ヒットしたルール',\n            acl: 'ACL',\n            sql: 'SQLインジェクション',\n            cc: 'アクセス頻度制限',\n            isBlocking: 'ブロック中',\n            isFree: 'ブロック解除',\n            unLock: 'ロック解除',\n            unLockHelper: 'IP:{0}をブロック解除しますか？',\n            saveDefault: 'デフォルトを保存',\n            saveToWebsite: 'ウェブサイトに適用',\n            saveToWebsiteHelper: '現在の設定をすべてのウェブサイトに適用しますか？',\n            websiteHelper: 'ウェブサイトを作成するためのデフォルト設定です。変更をウェブサイトに適用する必要があります',\n            websiteHelper2:\n                'ウェブサイトを作成するためのデフォルト設定です。ウェブサイトで特定の設定を変更してください',\n            ipGroup: 'IPグループ',\n            ipGroupHelper:\n                '1行に1つのIPまたはIPセグメントを入力、IPv4およびIPv6をサポートします。例: 192.168.1.1または192.168.1.0/24',\n            ipBlack: 'IPブラックリスト',\n            openRestyAlert: 'OpenRestyのバージョンは{0}より高くする必要があります',\n            initAlert:\n                '初回使用時には初期化が必要です。ウェブサイトの設定ファイルが変更され、元のWAF設定が失われます。事前にOpenRestyのバックアップを取ってください',\n            initHelper: '初期化操作により、既存のWAF設定がクリアされます。初期化してもよろしいですか？',\n            mainSwitch: 'メインスイッチ',\n            websiteAlert: 'まずウェブサイトを作成してください',\n            defaultUrlBlack: 'URLルール',\n            htmlRes: 'インターセプトページ',\n            urlSearchHelper: 'URLを入力して、曖昧検索をサポートしてください',\n            toCreate: '作成',\n            closeWaf: 'WAFを閉じる',\n            closeWafHelper: 'WAFを閉じると、ウェブサイトは保護を失います。続行しますか？',\n            addblack: 'ブラック追加',\n            addwhite: 'ホワイト追加',\n            addblackHelper: 'IP:{0}をデフォルトのブラックリストに追加しますか？',\n            addwhiteHelper: 'IP:{0}をデフォルトのホワイトリストに追加しますか？',\n            defaultUaBlack: 'ユーザーエージェントルール',\n            defaultIpBlack: '悪意のあるIPグループ',\n            cookie: 'クッキールール',\n            urlBlack: 'URLブラックリスト',\n            uaBlack: 'ユーザーエージェントブラックリスト',\n            attackCount: '攻撃頻度制限',\n            fileExtCheck: 'ファイルアップロード制限',\n            geoRestrict: '地域アクセス制限',\n            attacklog: '遮断記録',\n            unknownWebsite: '認証されていないドメイン名アクセス',\n            geoRuleEmpty: '地域は空にできません',\n            unknown: 'ウェブサイトが存在しません',\n            geo: '地域制限',\n            revertHtml: '{0} をデフォルトページに戻しますか？',\n            five_seconds: '5秒認証',\n            header: 'ヘッダールール',\n            methodWhite: 'HTTPルール',\n            expiryDate: '有効期限',\n            expiryDateHelper: '認証後、有効期間内は再認証されません',\n            defaultIpBlackHelper: 'インターネットから収集された悪意のあるIPをアクセス防止のために使用',\n            notFoundCount: '404頻度制限',\n            matchValue: '一致する値',\n            headerName: 'このフィールドは、特別なキャラクターではなく、英語、数字、-をサポート、長さは3-30',\n            cdnHelper: 'CDNを使用しているウェブサイトは、ここで正しいソースIPを取得できます',\n            clearLogWarn: 'ログをクリアすると元に戻せません。続けますか？',\n            commonRuleHelper: 'ルールは部分一致です',\n            blockIPHelper:\n                'ブロックされたIPはOpenRestyに一時的に保存され、OpenRestyを再起動すると解除されます。ブロック機能で永久的にブロックできます',\n            addWhiteUrlHelper: 'URL {0} をホワイトリストに追加しますか？',\n            dashHelper: 'コミュニティバージョンでもグローバル設定とウェブサイト設定の機能を使用できます',\n            wafStatusHelper: 'WAFが有効ではありません。グローバル設定で有効にしてください',\n            ccMode: 'モード',\n            global: 'グローバルモード',\n            uriMode: 'URLモード',\n            globalHelper:\n                'グローバルモード: 任意のURLに対するリクエストの合計数が一定時間内にしきい値を超えるとトリガーされます',\n            uriModeHelper: 'URLモード: 単一のURLに対するリクエストの数が一定時間内にしきい値を超えるとトリガーされます',\n            ip: 'IPブラックリスト',\n            globalSettingHelper:\n                '[ウェブサイト] タグの設定は [ウェブサイト設定] で有効にする必要があり、グローバル設定は新しく作成されたウェブサイトのデフォルト設定です',\n            globalSettingHelper2: '設定は [グローバル設定] と [ウェブサイト設定] の両方で有効にする必要があります',\n            urlCCHelper:\n                '{0} 秒以内にこの URL に対して {1} 回を超えるリクエストがあったため、この IP をブロックします {2}',\n            urlCCHelper2: 'URL にパラメータを含めることはできません',\n            notContain: '含まない',\n            urlcc: 'URL 頻度制限',\n            method: 'リクエストタイプ',\n            addIpsToBlock: 'IP を一括ブロック',\n            addUrlsToWhite: 'URL を一括でホワイトリストに追加',\n            noBlackIp: 'IP は既にブロックされているため、再度ブロックする必要はありません',\n            noWhiteUrl: 'URL は既にホワイトリストに含まれているため、再度追加する必要はありません',\n            spiderIpHelper:\n                '百度、Bing、Google、360、神马、搜狗、字节、DuckDuckGo、Yandexを含みます。これを閉じると、すべてのクローラーのアクセスがブロックされます。',\n            spiderIp: 'スパイダー IP プール',\n            geoIp: 'IP アドレスライブラリ',\n            geoIpHelper: 'IP の地理的位置を確認するために使用されます',\n            stat: '攻撃レポート',\n            statTitle: 'レポート',\n            attackIp: '攻撃 IP',\n            attackCountNum: '攻撃回数',\n            percent: '割合',\n            addblackUrlHelper: 'URL: {0} をデフォルトのブラックリストに追加しますか？',\n            rce: 'リモートコード実行',\n            software: 'ソフトウェア',\n            cveHelper: '一般的なソフトウェアやフレームワークの脆弱性を含みます',\n            vulnCheck: '補足ルール',\n            ssrf: 'SSRF 脆弱性',\n            afr: '任意ファイル読み取り',\n            ua: '未承認アクセス',\n            id: '情報漏洩',\n            aa: '認証回避',\n            dr: 'ディレクトリトラバーサル',\n            xxe: 'XXE 脆弱性',\n            suid: 'シリアライズ脆弱性',\n            dos: 'サービス拒否脆弱性',\n            afd: '任意ファイルダウンロード',\n            sqlInjection: 'SQL インジェクション',\n            afw: '任意ファイル書き込み',\n            il: '情報漏洩',\n            clearAllLog: 'すべてのログをクリア',\n            exportLog: 'ログをエクスポート',\n            appRule: 'アプリケーションルール',\n            appRuleHelper:\n                '一般的なアプリケーションルール。有効にすると誤検出を減らすことができます。1つのウェブサイトにつき1つのルールのみ使用可能です',\n            logExternal: '記録タイプを除外',\n            ipWhite: 'IP ホワイトリスト',\n            urlWhite: 'URL ホワイトリスト',\n            uaWhite: 'ユーザーエージェントホワイトリスト',\n            logExternalHelper:\n                '除外された記録タイプはログに記録されません。ブラックリスト/ホワイトリスト、地域アクセス制限、カスタムルールは大量のログを生成します。除外をお勧めします',\n            ssti: 'SSTI 攻撃',\n            crlf: 'CRLF インジェクション',\n            strict: '厳格モード',\n            strictHelper: 'より厳格なルールを使用してリクエストを検証します',\n            saveLog: 'ログを保存',\n            remoteURLHelper: 'リモート URL は、1行に1つのIPで、他の文字がないことを保証する必要があります',\n            notFound: 'Not Found (404)',\n            serviceUnavailable: 'サービスを利用できません (503)',\n            gatewayTimeout: 'ゲートウェイタイムアウト (504)',\n            belongToIpGroup: 'IP グループに属しています',\n            notBelongToIpGroup: 'IP グループに属していません',\n            unknownWebsiteKey: '未知のドメイン',\n            special: '特別な文字は使用できません',\n            fileToLarge: 'ファイルが1MBを超えており、アップロードできません',\n            uploadOverLimit: 'アップロードファイル数が制限を超えています、最大1ファイル',\n            importRuleHelper: '1行に1つのルール',\n        },\n        monitor: {\n            '360': '360検索',\n            name: 'ウェブサイトモニタリング',\n            pv: 'ページビュー',\n            uv: 'ユニークビジター',\n            flow: 'トラフィックフロー',\n            ip: 'IP',\n            spider: 'スパイダー',\n            visitors: '訪問者のトレンド',\n            today: '今日',\n            last7days: '過去7日間',\n            last30days: '過去30日間',\n            uvMap: '訪問者マップ（30日間）',\n            qps: 'リアルタイムリクエスト（分単位）',\n            flowSec: 'リアルタイムトラフィック（分単位）',\n            excludeCode: 'ステータスコードを除外',\n            excludeUrl: 'URLを除外',\n            excludeExt: '拡張子を除外',\n            cdnHelper: 'CDN提供のヘッダーから実際のIPを取得',\n            reqRank: '訪問ランキング',\n            refererDomain: 'リファラードメイン',\n            os: 'システム',\n            browser: 'ブラウザ/クライアント',\n            device: 'デバイス',\n            showMore: 'もっと見る',\n            unknown: 'その他',\n            pc: 'コンピュータ',\n            mobile: 'モバイルデバイス',\n            wechat: 'WeChat',\n            machine: 'マシン',\n            tencent: 'Tencentブラウザ',\n            ucweb: 'UCブラウザ',\n            '2345explorer': '2345ブラウザ',\n            huaweibrowser: 'Huaweiブラウザ',\n            log: 'リクエストログ',\n            statusCode: 'ステータスコード',\n            requestTime: '応答時間',\n            flowRes: 'レスポンストラフィック',\n            method: 'リクエストメソッド',\n            statusCodeHelper: '上記のステータスコードを入力してください',\n            statusCodeError: '無効なステータスコードのタイプ',\n            methodHelper: '上記のリクエストメソッドを入力してください',\n            all: 'すべて',\n            baidu: 'Baidu',\n            google: 'Google',\n            bing: 'Bing',\n            bytes: '今日のヘッドライン',\n            sogou: 'Sogou',\n            failed: 'エラー',\n            ipCount: 'IP数',\n            spiderCount: 'スパイダリクエスト',\n            averageReqTime: '平均応答時間',\n            totalFlow: '総トラフィック',\n            logSize: 'ログファイルのサイズ',\n            realIPType: '実際のIP取得方法',\n            fromHeader: 'HTTPヘッダーから取得',\n            fromHeaders: 'ヘッダーリストから取得',\n            header: 'HTTPヘッダー',\n            cdnConfig: 'CDN設定',\n            xff1: 'X-Forwarded-Forの最初のプロキシ',\n            xff2: 'X-Forwarded-Forの第二のプロキシ',\n            xff3: 'X-Forwarded-Forの第三のプロキシ',\n            xffHelper:\n                '例: X-Forwarded-For: <client>,<proxy1>,<proxy2>,<proxy3> 上位のプロキシは最後のIP <proxy3> を取得します',\n            headersHelper: '一般的に使用されるCDNのHTTPヘッダーから実際のIPを取得し、最初の利用可能な値を選択',\n            monitorCDNHelper: 'ウェブサイトモニタリングのためにCDN設定を変更すると、WAF CDN設定も更新されます',\n            wafCDNHelper: 'WAF CDN設定を変更すると、ウェブサイトモニタリングのCDN設定も更新されます',\n            statusErr: '無効なステータスコード形式',\n            shenma: '神馬検索',\n            duckduckgo: 'DuckDuckGo',\n            excludeUri: 'URIを除外',\n            top100Helper: '上位100データを表示',\n            logSaveDay: 'ログ保持期間（日数）',\n            cros: 'Chrome OS',\n            theworld: 'TheWorldブラウザ',\n            edge: 'Microsoft Edge',\n            maxthon: 'Maxthonブラウザ',\n            monitorStatusHelper: 'モニタリングは有効ではありません。設定で有効にしてください',\n            excludeIp: 'IPアドレスを除外',\n            excludeUa: 'ユーザーエージェントを除外',\n            remotePort: 'リモートポート',\n            unknown_browser: '不明',\n            unknown_os: '不明',\n            unknown_device: '不明',\n            logSaveSize: '最大ログ保存サイズ',\n            logSaveSizeHelper: 'これは単一ウェブサイトのログ保存サイズです',\n            '360se': '360 セキュリティブラウザ',\n            websites: 'ウェブサイトリスト',\n            trend: 'トレンド統計',\n            reqCount: 'リクエスト数',\n            uriHelper: '/test/* や /*/index.php を使用して Uri を除外できます',\n        },\n        tamper: {\n            tamper: 'ウェブサイトの改ざん防止',\n            ignoreTemplate: '除外テンプレート',\n            protectTemplate: '保護テンプレート',\n            ignoreTemplateHelper:\n                '除外コンテンツを入力してください、Enterまたはスペースで区切ります。（特定のディレクトリ ./log またはディレクトリ名 tmp、ファイルを除外するには特定のファイル ./data/test.html を入力する必要があります）',\n            protectTemplateHelper:\n                '保護コンテンツを入力してください、Enterまたはスペースで区切ります。（特定のファイル ./index.html、ファイル拡張子 .html、ファイルタイプ js、ディレクトリを保護するには特定のディレクトリ ./log を入力する必要があります）',\n            templateContent: 'テンプレート内容',\n            template: 'テンプレート',\n            saveTemplate: 'テンプレートとして保存',\n            tamperHelper1:\n                'ワンクリックデプロイメントタイプのウェブサイトでは、アプリケーションディレクトリの改ざん防止機能を有効にすることを推奨します。ウェブサイトが正常に使用できない場合やバックアップ/復元が失敗した場合は、まず改ざん防止機能を無効にしてください。',\n            tamperHelper2:\n                '除外されていないディレクトリ内の保護ファイルに対する読み書き、削除、権限、所有者変更操作を制限します',\n            tamperPath: '保護ディレクトリ',\n            tamperPathEdit: 'パスの変更',\n            log: 'ブロックログ',\n            totalProtect: '総保護',\n            todayProtect: '本日の保護',\n            templateRule: '長さ1-512、名前に{0}などの記号を含めることはできません',\n            ignore: '除外',\n            ignoreHelper:\n                '除外コンテンツを選択または入力してください、Enterまたはスペースで区切ります。（特定のディレクトリ ./log またはディレクトリ名 tmp、ファイルを除外するには特定のファイル ./data/test.html を入力または選択する必要があります）',\n            protect: '保護',\n            protectHelper:\n                '保護コンテンツを選択または入力してください、Enterまたはスペースで区切ります。（特定のファイル ./index.html、ファイル拡張子 .html、ファイルタイプ js、ディレクトリを保護するには特定のディレクトリ ./log を入力または選択する必要があります）',\n            tamperHelper00: '除外と保護は相対パスのみ入力可能です。',\n            tamperHelper01:\n                '改ざん防止を有効にすると、システムは除外されていないディレクトリ内の保護ファイルの作成、編集、削除操作を制限します。',\n            tamperHelper02: '優先順位：特定パス保護 > 特定パス除外 > 保護 > 除外',\n            tamperHelper03:\n                '監視操作は除外されていないディレクトリのみを対象とし、これらのディレクトリ内の非保護ファイルの作成を監視します。',\n            disableHelper: '以下のウェブサイトの改ざん防止機能を無効にしますか？続行しますか？',\n            appendOnly: '追加のみ',\n            appendOnlyHelper:\n                'このディレクトリ内のファイルの削除操作を制限し、除外ディレクトリまたは非保護ファイルの追加のみを許可します',\n            immutable: 'イミュータブル',\n            immutableHelper: 'このファイルの編集、削除、権限、所有者変更操作を制限します',\n            onWatch: '監視',\n            onWatchHelper:\n                'このディレクトリ内での保護ファイルまたは非除外ディレクトリの作成を監視およびインターセプトします',\n            forceStop: '強制終了',\n            forceStopHelper: 'このウェブサイトディレクトリの改ざん防止機能を強制的に無効にします。続行しますか？',\n        },\n        setting: {\n            setting: 'パネル設定',\n            title: 'パネルの説明',\n            titleHelper:\n                'ユーザーログインページに表示されます（例：Linuxサーバー運用管理パネル、推奨文字数：8〜15文字）',\n            logo: 'ロゴ（テキストなし）',\n            logoHelper:\n                'メニューが折りたたまれている場合、管理ページの左上隅に表示されます（推奨画像サイズ：82px*82px）',\n            logoWithText: 'ロゴ（テキストあり）',\n            logoWithTextHelper:\n                'メニューが展開されている場合、管理ページの左上隅に表示されます（推奨画像サイズ：185px*55px）',\n            favicon: 'ウェブサイトアイコン',\n            faviconHelper: 'ウェブサイトアイコン（推奨画像サイズ：16px*16px）',\n            setDefault: 'デフォルトに戻す',\n            setHelper: '現在の設定が保存されます。続けますか？',\n            setDefaultHelper: 'すべてのパネル設定がデフォルトに戻されます。続けますか？',\n            logoGroup: 'ロゴ',\n            imageGroup: '画像',\n            loginImage: 'ログインページの画像',\n            loginImageHelper: 'ログインページに表示されます（推奨画像サイズ：500×416px）',\n            loginBgType: 'ログインページ背景タイプ',\n            loginBgImage: 'ログインページ背景画像',\n            loginBgImageHelper: 'ログインページの背景画像として表示されます（推奨画像サイズ：1920×1080px）',\n            loginBgColor: 'ログインページ背景色',\n            loginBgColorHelper: 'ログインページの背景色として表示されます',\n            image: '画像',\n            bgColor: '背景色',\n            loginGroup: 'ログインページ',\n            loginBtnLinkColor: 'ボタン／リンクの色',\n            loginBtnLinkColorHelper: 'ログインページに表示されるボタン／リンクの色になります',\n        },\n        helper: {\n            wafTitle1: 'インターセプションマップ',\n            wafContent1: '過去30日間のインターセプトの地理的分布を表示',\n            wafTitle2: '地域別アクセス制限',\n            wafContent2: '地理的な位置に基づいてウェブサイトのアクセス元を制限',\n            wafTitle3: 'カスタムインターセプションページ',\n            wafContent3: 'リクエストがインターセプトされた後に表示するカスタムページを作成',\n            wafTitle4: 'カスタムルール (ACL)',\n            wafContent4: 'カスタムルールに基づいてリクエストをインターセプト',\n            tamperTitle1: 'ファイル整合性監視',\n            tamperContent1:\n                'ウェブサイトのファイルの整合性を監視、コアファイル、スクリプトファイル、設定ファイルを含む。',\n            tamperTitle2: 'リアルタイムスキャンと検出',\n            tamperContent2:\n                'ウェブサイトのファイルシステムをリアルタイムでスキャンし、異常や改竄されたファイルを検出。',\n            tamperTitle3: 'セキュリティ権限設定',\n            tamperContent3:\n                '適切な権限設定とアクセス制御ポリシーを通じてウェブサイトファイルへのアクセスを制限し、潜在的な攻撃対象面を減少。',\n            tamperTitle4: 'ログ記録と分析',\n            tamperContent4:\n                'ファイルのアクセスおよび操作ログを記録し、後の監査および分析に使用、また潜在的なセキュリティ脅威を特定。',\n            settingTitle1: 'カスタムウェルカムメッセージ',\n            settingContent1: '1Panelのログインページにカスタムウェルカムメッセージを設定。',\n            settingTitle2: 'カスタムロゴ',\n            settingContent2: 'ブランド名やその他のテキストを含むロゴ画像をアップロードできる。',\n            settingTitle3: 'カスタムウェブサイトアイコン',\n            settingContent3:\n                'カスタムアイコンをアップロードして、デフォルトのブラウザアイコンを置き換え、ユーザー体験を向上。',\n            monitorTitle1: '訪問者トレンド',\n            monitorContent1: 'ウェブサイト訪問者のトレンドを統計および表示',\n            monitorTitle2: '訪問者マップ',\n            monitorContent2: 'ウェブサイト訪問者の地理的分布を統計および表示',\n            monitorTitle3: 'アクセス統計',\n            monitorContent3:\n                'ウェブサイトリクエスト情報を統計、スパイダー、アクセスデバイス、リクエストステータスなどを含む。',\n            monitorTitle4: 'リアルタイムモニタリング',\n            monitorContent4:\n                'ウェブサイトリクエスト情報をリアルタイムでモニタリング、リクエスト数、トラフィックなどを含む。',\n            alertTitle1: 'SMSアラート',\n            alertContent1:\n                'サーバーリソース使用量の異常、ウェブサイトおよび証明書の有効期限、新しいバージョンの更新、パスワードの期限切れなどが発生した場合、ユーザーにSMSアラートで通知し、タイムリーな処理を確保。',\n            alertTitle2: 'アラートログ',\n            alertContent2:\n                'ユーザーがアラートログを表示できる機能を提供し、過去のアラートイベントを追跡および分析しやすく。',\n            alertTitle3: 'アラート設定',\n            alertContent3:\n                'カスタム電話番号、日々のプッシュ頻度、日々のプッシュ時間設定を提供し、ユーザーがより合理的なプッシュアラートを設定しやすく。',\n            nodeDashTitle1: 'アプリケーション管理',\n            nodeDashContent1:\n                'マルチノードアプリケーションの統一管理、ステータス監視、迅速な起動/停止、端末接続、バックアップをサポート',\n            nodeDashTitle2: 'Webサイト管理',\n            nodeDashContent2:\n                'マルチノードWebサイトの統一管理、リアルタイムステータス監視、一括起動/停止と迅速なバックアップをサポート',\n            nodeDashTitle3: 'データベース管理',\n            nodeDashContent3:\n                'マルチノードデータベースの統一管理、主要ステータスが一目で分かり、ワンクリックバックアップをサポート',\n            nodeDashTitle4: 'スケジュールタスク管理',\n            nodeDashContent4:\n                'マルチノードスケジュールタスクの統一管理、ステータス監視、迅速な起動/停止、手動トリガー実行をサポート',\n            nodeTitle1: 'ワンクリックノード追加',\n            nodeContent1: '複数のサーバーノードを迅速に統合',\n            nodeTitle2: 'バッチアップグレード',\n            nodeContent2: '一度の操作ですべてのノードを同期アップグレード',\n            nodeTitle3: 'ノードステータス監視',\n            nodeContent3: '各ノードの運用状況をリアルタイムで把握',\n            nodeTitle4: '迅速なリモート接続',\n            nodeContent4: 'ワンクリックでノードリモート端末に直接接続',\n            fileExchangeTitle1: 'キー認証伝送',\n            fileExchangeContent1: 'SSHキーを介して認証し、伝送のセキュリティを確保します。',\n            fileExchangeTitle2: '効率的なファイル同期',\n            fileExchangeContent2: '変更されたコンテンツのみを同期し、伝送速度と安定性を大幅に向上させます。',\n            fileExchangeTitle3: 'マルチノード相互通信のサポート',\n            fileExchangeContent3:\n                '異なるノード間でプロジェクトファイルを簡単に転送し、複数のサーバーを柔軟に管理します。',\n            nodeAppTitle1: 'アプリケーションアップグレード管理',\n            nodeAppContent1: 'マルチノードアプリケーションの更新状況を統一監視、ワンクリックアップグレードをサポート',\n            appTitle1: '柔軟なパネル管理',\n            appContent1: 'いつでもどこでも1Panelサーバーを簡単に管理できます。',\n            appTitle2: '包括的なサービス情報',\n            appContent2:\n                'モバイル端末でアプリケーション、ウェブサイト、Docker、データベースなどの基本的な管理を行い、アプリやウェブサイトの迅速な作成をサポートします。',\n            appTitle3: 'リアルタイム異常監視',\n            appContent3:\n                'モバイル端末でサーバーステータス、WAFセキュリティ監視、ウェブサイトの訪問統計、プロセスの健康状態をリアルタイムで確認できます。',\n            clusterTitle1: 'マスタースレーブ展開',\n            clusterContent1:\n                '異なるノードで MySQL/Postgres/Redis マスタースレーブインスタンスを作成することをサポートし、自動的にマスタースレーブ関連付けと初期化を完了します',\n            clusterTitle2: 'マスタースレーブ管理',\n            clusterContent2:\n                '統一されたページで複数のマスタースレーブノードを一元的に管理し、それらの役割、実行状態などを表示します',\n            clusterTitle3: 'レプリケーション状態',\n            clusterContent3:\n                'マスタースレーブレプリケーション状態と遅延情報を表示し、同期の問題を解決するのに役立ちます',\n        },\n        node: {\n            master: '主ノード',\n            masterBackup: 'マスターノードバックアップ',\n            backupNode: 'バックアップノード',\n            backupFrequency: 'バックアップ頻度（時間）',\n            backupCopies: 'バックアップ数',\n            noBackupNode: '現在バックアップノードが空です。保存するバックアップノードを選択して再試行してください！',\n            masterBackupAlert:\n                '現在マスターノードのバックアップが設定されていません。データセキュリティを確保するため、障害時に新しいマスターノードに手動で切り替えられるよう、速やかにバックアップノードを設定してください。',\n            node: 'ノード',\n            addr: 'アドレス',\n            nodeUpgrade: '更新設定',\n            nodeUpgradeHelper:\n                '選択したノードは、マスターノードのアップグレード完了後に自動的にアップグレードを開始し、手動操作は不要です。',\n            nodeUnhealthy: 'ノード状態異常',\n            deletedNode: '削除済みノード {0} は現在アップグレード操作をサポートしていません！',\n            nodeUnhealthyHelper: 'ノード状態異常を検出しました。[ノード管理]で確認してから再試行してください！',\n            nodeUnbind: 'ノードがライセンスにバインドされていません',\n            nodeUnbindHelper:\n                'このノードがライセンスにバインドされていないことを検出しました。[パネル設定 - ライセンス]メニューでバインドしてから再試行してください！',\n            memTotal: '総メモリ',\n            nodeManagement: 'マルチマシン管理',\n            nodeItem: 'ノード管理',\n            panelItem: 'パネル管理',\n            addPanel: 'パネル追加',\n            addPanelHelper:\n                'Después de agregar el panel exitosamente, puede acceder rápidamente al panel objetivo en [Resumen - Paneles].',\n            panel: 'Panel 1Panel',\n            others: 'Otros Paneles',\n            addNode: 'ノードを追加',\n            connInfo: '接続情報',\n            nodeInfo: 'ノード情報',\n            withProxy: 'プロキシアクセスを有効化',\n            withoutProxy: 'プロキシアクセスを無効化',\n            withProxyHelper:\n                'パネル設定で管理されているシステムプロキシ {0} を使用して子ノードにアクセスします。続行しますか？',\n            withoutProxyHelper:\n                'パネル設定で管理されているシステムプロキシを使用して子ノードにアクセスすることを停止します。続行しますか？',\n            syncInfo: 'データ同期,',\n            syncHelper: 'マスターノードのデータが変更されると、この子ノードにリアルタイムで同期されます,',\n            syncBackupAccount: 'バックアップアカウント設定',\n            syncWithMaster:\n                'プロ版にアップグレード後、すべてのデータがデフォルトで同期されます。ノード管理で同期ポリシーを手動調整できます。',\n            syncProxy: 'システムプロキシ設定',\n            syncProxyHelper: 'システムプロキシ設定の同期にはDockerの再起動が必要です',\n            syncProxyHelper1: 'Dockerの再起動は現在実行中のコンテナサービスに影響する可能性があります。',\n            syncProxyHelper2: 'コンテナ - 設定 ページで手動で再起動できます。',\n            syncProxyHelper3:\n                'システムプロキシ設定の同期にはDockerの再起動が必要で、現在実行中のコンテナサービスに影響する可能性があります',\n            syncProxyHelper4:\n                'システムプロキシ設定の同期にはDockerの再起動が必要です。後でコンテナ - 設定 ページで手動で再起動できます。',\n            syncCustomApp: 'カスタムアプリリポジトリを同期',\n            syncAlertSetting: 'システムアラート設定',\n            syncNodeInfo: 'ノード基本データ,',\n            nodeSyncHelper: 'ノード情報の同期は、以下の情報を同期します：',\n            nodeSyncHelper1: '1. 公共のバックアップアカウント情報',\n            nodeSyncHelper2: '2. 主ノードとサブノードの接続情報',\n            nodeCheck: '可用性チェック',\n            checkSSH: 'ノードSSH接続を確認',\n            checkUserPermission: 'ノードユーザー権限を確認',\n            isNotRoot:\n                'このノードではパスワードなしsudoがサポートされておらず、現在のユーザーがrootではないことが検出されました',\n            checkLicense: 'ノードライセンス状態を確認',\n            checkService: 'ノードの既存サービス情報を確認',\n            checkPort: 'ノードポート到達性を確認',\n            panelExist:\n                'このノードで1Panel V1サービスが実行中です。追加前に移行スクリプトでV2へアップグレードしてください。',\n            coreExist:\n                '現在のノードはマスターノードとして有効化済みのため、直接スレーブノードとして追加できません。追加する前にまずスレーブノードにダウングレードしてください。詳細はドキュメントを参照してください。',\n            agentExist:\n                'このノードに1panel-agentが既にインストールされています。続行すると既存データを保持し、1panel-agentサービスのみを置換します。',\n            agentNotExist:\n                'このノードに1panel-agentがインストールされていないため、ノード情報を直接編集できません。一度削除してから再度追加してください。',\n            oldDataExist: 'このノードに1Panel V2の過去データが検出されました。以下の情報で現在の設定を上書きします:',\n            errLicense: 'このノードに紐づけられたライセンスが利用できません。確認して再試行してください！',\n            errNodePort:\n                'ノードポート[ {0} ]にアクセスできないことが検出されました。ファイアウォールまたはセキュリティグループでこのポートが開放されているか確認してください。',\n            reinstallHelper: 'ノード{0}を再インストールします。続行しますか？',\n            unhealthyCheck: '異常チェック',\n            fixOperation: '修正操作',\n            checkName: 'チェック項目',\n            checkSSHConn: 'SSH接続の可用性を確認',\n            fixSSHConn: 'ノードを手動で編集し、接続情報を確認',\n            checkConnInfo: 'エージェント接続情報を確認',\n            checkStatus: 'ノードサービスの可用性を確認',\n            fixStatus: '「systemctl status 1panel-agent.service」を実行して、サービスが起動しているか確認します。',\n            checkAPI: 'ノードAPIの可用性を確認',\n            fixAPI: 'ノードのログを確認し、ファイアウォールのポートが正常に開放されているか確認します。',\n            forceDelete: '強制削除',\n            operateHelper: '以下のノードに{0}操作を行います。続行しますか？',\n            operatePanelHelper: '以下のパネルに対して {0} 操作を実行します。続行しますか？',\n            forceDeleteHelper: '強制削除はノード削除エラーを無視し、データベースメタデータを削除します',\n            uninstall: 'ノードデータを削除',\n            uninstallHelper: 'ノードに関連するすべての1Panelデータが削除されます。慎重に選択してください！',\n            baseDir: 'インストールディレクトリ',\n            baseDirHelper: 'インストールディレクトリが空の場合、デフォルトで/optディレクトリにインストールされます',\n            nodePort: 'ノードポート',\n            offline: 'オフラインモード',\n            freeCount: '無料枠 [{0}]',\n            offlineHelper: 'ノードがオフライン環境にある場合に使用',\n            appUpgrade: 'アプリのアップグレード',\n            appUpgradeHelper: 'アップグレードが必要なアプリが {0} 個あります',\n        },\n        customApp: {\n            name: 'カスタムアプリリポジトリ',\n            appStoreType: 'アプリストアパッケージソース',\n            appStoreUrl: 'リポジトリURL',\n            local: 'ローカルパス',\n            remote: 'リモートリンク',\n            imagePrefix: 'イメージプレフィックス',\n            imagePrefixHelper:\n                '機能: イメージプレフィックスをカスタマイズし、composeファイル内のイメージフィールドを変更します。例えば、イメージプレフィックスを1panel/customに設定した場合、MaxKBのイメージフィールドは1panel/custom/maxkb:v1.10.0に変更されます',\n            closeHelper: 'カスタムアプリリポジトリの使用をキャンセルしますか',\n            appStoreUrlHelper: '.tar.gz形式のみサポートされます',\n            postNode: 'サブノードへ同期',\n            postNodeHelper:\n                'カスタムストアパッケージを子ノードのインストールディレクトリの tmp/customApp/apps.tar.gz に同期します',\n            nodes: 'ノードを選択',\n            selectNode: 'ノードを選択',\n            selectNodeError: 'ノードを選択してください',\n            licenseHelper: 'プロバージョンはカスタムアプリケーションリポジトリ機能をサポートしています',\n            databaseHelper: 'アプリケーション関連データベース、ターゲットノードのデータベースを選択してください',\n            nodeHelper: '現在のノードは選択できません',\n            migrateHelper:\n                '現在、単体アプリケーションと MySQL、MariaDB、PostgreSQL データベースのみに関連するアプリケーションの移行のみをサポートしています',\n            opensslHelper:\n                '暗号化バックアップを使用する場合、2つのノード間のOpenSSLバージョンは一致している必要があります。そうしないと、移行が失敗する可能性があります。',\n            installApp: '一括インストール',\n            installAppHelper: '選択したノードにアプリを一括インストール',\n        },\n        alert: {\n            isAlert: 'アラート',\n            alertCount: 'アラート数',\n            clamHelper: '感染したファイルをスキャンするときにアラートをトリガーします',\n            cronJobHelper: 'タスクの実行が失敗したときにアラートをトリガーします',\n            licenseHelper: 'プロのバージョンはアラートをサポートします',\n            alertCountHelper: '最大毎日のアラーム周波数',\n            alert: 'SMSアラート',\n            logs: 'アラートログ',\n            list: 'アラートリスト',\n            addTask: 'アラートを作成',\n            editTask: 'アラートを編集',\n            alertMethod: '方法',\n            alertMsg: 'アラートメッセージ',\n            alertRule: 'アラートルール',\n            titleSearchHelper: 'アラートタイトルを入力して検索します',\n            taskType: 'タイプ',\n            ssl: '証明書期限切れ',\n            siteEndTime: 'ウェブサイト期限切れ',\n            panelPwdEndTime: 'パネルパスワード期限切れ',\n            panelUpdate: '新しいパネルバージョンあり',\n            cpu: 'サーバーCPUアラート',\n            memory: 'サーバーメモリアラート',\n            load: 'サーバーロードアラート',\n            disk: 'サーバーディスクアラート',\n            website: 'ウェブサイト',\n            certificate: 'SSL証明書',\n            remainingDays: '残り日数',\n            sendCount: '送信回数',\n            sms: 'SMS',\n            wechat: 'WeChat',\n            dingTalk: 'DingTalk',\n            feiShu: 'FeiShu',\n            mail: 'メール',\n            weCom: 'WeCom',\n            sendCountRulesHelper: '期限前に送信されるアラートの合計（1日1回）',\n            panelUpdateRulesHelper: '新しいパネルバージョンに関するアラートの合計（1日1回）',\n            oneDaySendCountRulesHelper: '1日に送信できる最大アラート回数',\n            siteEndTimeRulesHelper: '期限が設定されていないウェブサイトはアラートをトリガーしません',\n            autoRenewRulesHelper: '自動更新が有効な証明書で残り日数が31日未満の場合、アラートはトリガーされません',\n            panelPwdEndTimeRulesHelper: 'パネルパスワード期限切れのアラートは設定されていない場合は使用できません',\n            sslRulesHelper: 'すべてのSSL証明書',\n            diskInfo: 'ディスク',\n            monitoringType: '監視タイプ',\n            autoRenew: '自動更新',\n            useDisk: 'ディスク使用率',\n            usePercentage: '使用割合',\n            changeStatus: 'ステータスを変更',\n            disableMsg:\n                'アラートタスクを停止すると、このタスクはアラートメッセージを送信できなくなります。続行しますか？',\n            enableMsg:\n                'アラートタスクを有効にすると、このタスクはアラートメッセージを送信できるようになります。続行しますか？',\n            useExceed: '使用率超過',\n            useExceedRulesHelper: '設定した値を超えた場合にアラートをトリガー',\n            cpuUseExceedAvg: '平均CPU使用率が指定した値を超過',\n            memoryUseExceedAvg: '平均メモリ使用率が指定した値を超過',\n            loadUseExceedAvg: '平均負荷使用率が指定した値を超過',\n            cpuUseExceedAvgHelper: '指定時間内の平均CPU使用率が指定した値を超過',\n            memoryUseExceedAvgHelper: '指定時間内の平均メモリ使用率が指定した値を超過',\n            loadUseExceedAvgHelper: '指定時間内の平均負荷使用率が指定した値を超過',\n            resourceAlertRulesHelper: '注意：30分以内に連続してアラートが発生した場合、は1回だけ送信されます',\n            specifiedTime: '指定時間',\n            deleteTitle: 'アラートを削除',\n            deleteMsg: 'アラートタスクを削除してもよろしいですか？',\n            allSslTitle: 'すべてのウェブサイトSSL証明書の期限切れアラート',\n            sslTitle: 'ウェブサイト{0}のSSL証明書期限切れアラート',\n            allSiteEndTimeTitle: 'すべてのウェブサイト期限切れアラート',\n            siteEndTimeTitle: 'ウェブサイト{0}の期限切れアラート',\n            panelPwdEndTimeTitle: 'パネルパスワード期限切れアラート',\n            panelUpdateTitle: '新しいパネルバージョン通知',\n            cpuTitle: '高CPU使用率アラート',\n            memoryTitle: '高メモリ使用率アラート',\n            loadTitle: '高負荷アラート',\n            diskTitle: 'マウントディレクトリ{0}の高ディスク使用率アラート',\n            allDiskTitle: '高ディスク使用率アラート',\n            timeRule: '残り時間が{0}日未満（処理されない場合、翌日再送信）',\n            panelUpdateRule:\n                '新しいパネルバージョンが検出されたときに1回アラートを送信（処理されない場合、翌日再送信）',\n            avgRule: '指定された時間内の平均{1}使用率が{2}%を超過するとアラートがトリガーされ、1日{3}回送信',\n            diskRule: 'マウントディレクトリ{0}のディスク使用率が{1}{2}を超過するとアラートがトリガーされ、1日{3}回送信',\n            allDiskRule: 'ディスク使用率が{0}{1}を超過するとアラートがトリガーされ、1日{2}回送信',\n            cpuName: 'CPU',\n            memoryName: 'メモリ',\n            loadName: '負荷',\n            diskName: 'ディスク',\n            syncAlertInfo: '手動プッシュ',\n            syncAlertInfoMsg: 'アラートタスクを手動でプッシュしてもよろしいですか？',\n            pushError: 'プッシュ失敗',\n            pushSuccess: 'プッシュ成功',\n            syncError: '同期失敗',\n            success: 'アラート成功',\n            pushing: 'プッシュ中...',\n            error: 'アラート失敗',\n            cleanLog: 'ログをクリア',\n            cleanAlertLogs: 'アラートログをクリア',\n            daily: '1日のアラート数：{0}',\n            cumulative: '累積アラート数：{0}',\n            clams: 'ウイルススキャン',\n            taskName: 'タスク名',\n            cronJobType: 'タスクタイプ',\n            clamPath: 'スキャンディレクトリ',\n            cronjob: 'スケジュールタスクの実行{0}で異常が発生しました',\n            app: 'アプリバックアップ',\n            web: 'ウェブサイトバックアップ',\n            database: 'データベースバックアップ',\n            directory: 'ディレクトリバックアップ',\n            log: 'ログバックアップ',\n            snapshot: 'システムスナップショット',\n            clamsRulesHelper: 'アラートが必要なウイルススキャンタスク',\n            cronJobRulesHelper: 'このタイプのスケジュールタスクには設定が必要です',\n            clamsTitle: 'ウイルススキャンタスク「{0}」が感染ファイルを検出したアラート',\n            cronJobAppTitle: 'Cronジョブ - アプリバックアップ「{0}」タスク失敗アラート',\n            cronJobWebsiteTitle: 'Cronジョブ - ウェブサイトバックアップ「{0}」タスク失敗アラート',\n            cronJobDatabaseTitle: 'Cronジョブ - データベースバックアップ「{0}」タスク失敗アラート',\n            cronJobDirectoryTitle: 'Cronジョブ - ディレクトリバックアップ「{0}」タスク失敗アラート',\n            cronJobLogTitle: 'Cronジョブ - ログバックアップ「{0}」タスク失敗アラート',\n            cronJobSnapshotTitle: 'Cronジョブ - システムスナップショット「{0}」タスク失敗アラート',\n            cronJobShellTitle: 'Cronジョブ - シェルスクリプト「{0}」タスク失敗アラート',\n            cronJobCurlTitle: 'Cronジョブ - URLアクセス「{0}」タスク失敗アラート',\n            cronJobCutWebsiteLogTitle: 'Cronジョブ - ウェブサイトログカット「{0}」タスク失敗アラート',\n            cronJobCleanTitle: 'Cronジョブ - キャッシュクリーニング「{0}」タスク失敗アラート',\n            cronJobNtpTitle: 'Cronジョブ - サーバー時間同期「{0}」タスク失敗アラート',\n            clamsRule: 'ウイルススキャンで感染ファイルが検出されたアラート、1日に{0}回送信',\n            cronJobAppRule: 'アプリバックアップタスク失敗アラート、1日に{0}回送信',\n            cronJobWebsiteRule: 'ウェブサイトバックアップタスク失敗アラート、1日に{0}回送信',\n            cronJobDatabaseRule: 'データベースバックアップタスク失敗アラート、1日に{0}回送信',\n            cronJobDirectoryRule: 'ディレクトリバックアップタスク失敗アラート、1日に{0}回送信',\n            cronJobLogRule: 'ログバックアップタスク失敗アラート、1日に{0}回送信',\n            cronJobSnapshotRule: 'スナップショットバックアップタスク失敗アラート、1日に{0}回送信',\n            cronJobShellRule: 'シェルスクリプトタスク失敗アラート、1日に{0}回送信',\n            cronJobCurlRule: 'URLアクセスタスク失敗アラート、1日に{0}回送信',\n            cronJobCutWebsiteLogRule: 'ウェブサイトログカットタスク失敗アラート、1日に{0}回送信',\n            cronJobCleanRule: 'キャッシュクリーニングタスク失敗アラート、1日に{0}回送信',\n            cronJobNtpRule: 'サーバー時間同期タスク失敗アラート、1日に{0}回送信',\n            alertSmsHelper: 'SMS制限：合計{0}メッセージ、{1}回使用済み',\n            goBuy: 'さらに購入',\n            phone: '電話',\n            phoneHelper: 'アラートメッセージのために実際の電話番号を提供してください',\n            dailyAlertNum: '毎日のアラート数',\n            dailyAlertNumHelper: '1日のアラート数の合計、最大100件まで',\n            timeRange: '時間範囲',\n            sendTimeRange: '送信の時間範囲',\n            sendTimeRangeHelper: 'アラートを送信できる時間範囲は{0}です',\n            to: '-',\n            startTime: '開始時間',\n            endTime: '終了時間',\n            defaultPhone: 'ライセンスに紐付けられたアカウントの電話番号をデフォルトにする',\n            noticeAlert: '通知アラート',\n            resourceAlert: 'リソースアラート',\n            agentOfflineAlertHelper:\n                'ノードでオフラインアラートを有効にすると、メインノードが30分ごとにスキャンしてアラートタスクを実行します。',\n            offline: 'オフラインアラート',\n            offlineHelper:\n                'オフラインアラートに設定すると、メインノードが30分ごとにスキャンしてアラートタスクを実行します。',\n            offlineOff: 'オフラインアラートを有効にする',\n            offlineOffHelper:\n                'オフラインアラートを有効にすると、メインノードが30分ごとにスキャンしてアラートタスクを実行します。',\n            offlineClose: 'オフラインアラートを無効にする',\n            offlineCloseHelper:\n                'オフラインアラートを無効にすると、サブノードが独自にアラートを処理する必要があります。アラートの失敗を防ぐため、ネットワーク接続が良好であることを確認してください。',\n            alertNotice: 'アラート通知',\n            methodConfig: '通知方法の設定',\n            commonConfig: 'グローバル設定',\n            smsConfig: 'SMS',\n            smsConfigHelper: 'SMS 通知の電話番号を設定する',\n            emailConfig: 'メール',\n            emailConfigHelper: 'SMTP メール送信サービスを設定する',\n            deleteConfigTitle: 'アラート設定を削除',\n            deleteConfigMsg: 'アラート設定を削除してもよろしいですか？',\n            test: 'テスト',\n            alertTestOk: 'テスト通知に成功しました',\n            alertTestFailed: 'テスト通知に失敗しました',\n            displayName: '表示名',\n            sender: '送信元アドレス',\n            password: 'パスワード',\n            host: 'SMTP サーバー',\n            port: 'ポート番号',\n            encryption: '暗号化方式',\n            recipient: '受信者',\n            licenseTime: 'ライセンスの有効期限切れ通知',\n            licenseTimeTitle: 'ライセンスの有効期限切れ通知',\n            displayNameHelper: 'メールの送信者表示名',\n            senderHelper: 'メール送信に使用するメールアドレス',\n            passwordHelper: 'メールサービスの認証コード',\n            hostHelper: 'SMTP サーバーアドレス（例：smtp.qq.com）',\n            portHelper: 'SSLは通常465、TLSは通常587',\n            sslHelper: 'SMTPポートが465の場合、通常はSSLが必要です',\n            tlsHelper: 'SMTPポートが587の場合、通常はTLSが必要です',\n            triggerCondition: 'トリガー条件',\n            loginFail: '以内にログイン失敗',\n            nodeException: 'ノード異常アラート',\n            licenseException: 'ライセンス異常アラート',\n            panelLogin: 'パネルログイン異常アラート',\n            sshLogin: 'SSHログイン異常アラート',\n            panelIpLogin: 'パネルログインIP異常アラート',\n            sshIpLogin: 'SSHログインIP異常アラート',\n            ipWhiteListHelper:\n                'ホワイトリストに登録されたIPはルールの制限を受けず、ログインが成功してもアラートは発生しません',\n            nodeExceptionRule: 'ノード異常アラートは、1日あたり{0}回送信',\n            licenseExceptionRule: 'ライセンス異常アラートは、1日あたり{0}回送信',\n            panelLoginRule: 'パネルログインアラートは、1日あたり{0}回送信',\n            sshLoginRule: 'SSHログインアラートは、1日あたり{0}回送信',\n            userNameHelper: 'ユーザー名が空の場合、送信者のアドレスがデフォルトで使用されます',\n            alertConfigHelper: 'パネルメッセージプッシュを受信するためのアラート通知チャネルを設定',\n            weComConfigHelper: 'WeComアラート通知設定',\n            wechatConfigHelper: 'WeChat公式アカウントアラート通知設定',\n            dingTalkConfigHelper: 'DingTalkアラート通知設定',\n            feiShuConfigHelper: 'Feishuアラート通知設定',\n            webhookName: 'ボット名',\n            webhookUrl: 'Webhook URL',\n            alertConfigProHelper:\n                'プロフェッショナル版では、企業微信（WeCom）、DingTalk、Feishu、およびSMS通知を追加でサポートします。',\n        },\n        theme: {\n            lingXiaGold: '凌霞金',\n            classicBlue: 'クラシックブルー',\n            freshGreen: 'フレッシュグリーン',\n            customColor: 'カスタムカラー',\n            setDefault: 'デフォルトに戻す',\n            setDefaultHelper: 'テーマカラーを初期状態に戻そうとしています。続行しますか？',\n            setHelper: '現在選択されているテーマカラーを保存しようとしています。続行しますか？',\n        },\n        exchange: {\n            exchange: 'ファイル交換',\n            exchangeConfirm: '{0} ノードのファイル/フォルダ {1} を {2} ノードの {3} ディレクトリに転送しますか？',\n        },\n        cluster: {\n            cluster: 'アプリケーションの高可用性',\n            name: 'クラスタ名',\n            addCluster: 'クラスタを追加',\n            installNode: 'ノードをインストール',\n            master: 'マスターノード',\n            slave: 'スレーブノード',\n            replicaStatus: 'マスタースレーブステータス',\n            unhealthyDeleteError:\n                'インストールノードのステータスが異常です。ノードリストを確認してから再試行してください！',\n            replicaStatusError: 'ステータスの取得が異常です。マスターノードを確認してください。',\n            masterHostError: 'マスターノードのIPは127.0.0.1にできません',\n        },\n    },\n};\nexport default {\n    ...fit2cloudEnLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/ko.ts",
    "content": "import fit2cloudKoLocale from 'fit2cloud-ui-plus/src/locale/lang/en';\n\nconst message = {\n    commons: {\n        true: '참',\n        false: '거짓',\n        colon: ': ',\n        example: '예: ',\n        fit2cloud: 'FIT2CLOUD',\n        lingxia: 'Lingxia',\n        button: {\n            run: '실행',\n            create: '생성',\n            add: '추가',\n            save: '저장',\n            set: '설정 수정',\n            sync: '동기화',\n            delete: '삭제',\n            edit: '편집',\n            enable: '활성화',\n            disable: '비활성화',\n            confirm: '확인',\n            cancel: '취소',\n            reset: '재설정',\n            restart: '재시작',\n            conn: '연결',\n            disConn: '연결 해제',\n            clean: '정리',\n            login: '로그인',\n            close: '닫기',\n            off: '꺼짐',\n            stop: '중지',\n            start: '시작',\n            view: '보기',\n            watch: '감시',\n            handle: '트리거',\n            clone: '복제',\n            expand: '확장',\n            collapse: '축소',\n            log: '로그',\n            back: '뒤로',\n            backup: '백업',\n            recover: '복구',\n            retry: '재시도',\n            upload: '업로드',\n            download: '다운로드',\n            init: '초기화',\n            verify: '검증',\n            saveAndEnable: '저장 및 활성화',\n            import: '가져오기',\n            export: '내보내기',\n            power: '권한 부여',\n            search: '검색',\n            refresh: '새로고침',\n            get: '가져오기',\n            upgrade: '업그레이드',\n            update: '업데이트',\n            updateNow: '지금 업데이트',\n            ignore: '업그레이드 무시',\n            install: '설치',\n            copy: '복사',\n            random: '무작위',\n            uninstall: '제거',\n            fullscreen: '전체 화면',\n            quitFullscreen: '전체 화면 종료',\n            showAll: '모두 보기',\n            hideSome: '일부 숨기기',\n            agree: '동의',\n            notAgree: '동의하지 않음',\n            preview: '미리 보기',\n            open: '열기',\n            notSave: '저장하지 않음',\n            createNewFolder: '새 폴더 생성',\n            createNewFile: '새 파일 생성',\n            helpDoc: '도움말 문서',\n            unbind: '연결 해제',\n            cover: '덮어쓰기',\n            skip: '건너뛰기',\n            fix: '수정',\n            down: '중지',\n            up: '시작',\n            sure: '확인',\n            show: '보기',\n            hide: '숨기기',\n            visit: '방문',\n            migrate: '마이그레이션',\n            prev: '이전',\n            next: '다음',\n            setDefault: '기본값 복원',\n            bind: '바인딩',\n        },\n        operate: {\n            start: '시작',\n            stop: '중지',\n            restart: '재시작',\n            reload: '다시 로드',\n            rebuild: '재구축',\n            sync: '동기화',\n            up: '실행',\n            down: '중지',\n            delete: '삭제',\n        },\n        search: {\n            timeStart: '시작 시간',\n            timeEnd: '종료 시간',\n            timeRange: '부터',\n            dateStart: '시작 날짜',\n            dateEnd: '종료 날짜',\n            date: '날짜',\n        },\n        table: {\n            all: '전체',\n            total: '총 {0}',\n            name: '이름',\n            type: '유형',\n            status: '상태',\n            records: '기록',\n            group: '그룹',\n            createdAt: '생성 시간',\n            publishedAt: '게시 시간',\n            date: '날짜',\n            updatedAt: '업데이트 시간',\n            operate: '작업',\n            message: '메시지',\n            description: '설명',\n            interval: '간격',\n            user: '소유자',\n            title: '제목',\n            port: '포트',\n            forward: '포워드',\n            protocol: '프로토콜',\n            tableSetting: '테이블 설정',\n            refreshRate: '새로 고침 속도',\n            selectColumn: '열 선택',\n            local: '로컬',\n            serialNumber: '일련 번호',\n            manageGroup: '그룹 관리',\n            backToList: '목록으로 돌아가기',\n            keepEdit: '계속 편집',\n            default: '기본값',\n            noRefresh: '자동 새로고침 안 함',\n        },\n        loadingText: {\n            Upgrading: '시스템 업그레이드 중입니다. 잠시만 기다려 주십시오...',\n            Restarting: '시스템 재시작 중입니다. 잠시만 기다려 주십시오...',\n            Recovering: '스냅샷에서 복구 중입니다. 잠시만 기다려 주십시오...',\n            Rollbacking: '스냅샷에서 롤백 중입니다. 잠시만 기다려 주십시오...',\n        },\n        msg: {\n            noneData: '데이터가 없습니다',\n            delete: '이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?',\n            clean: '이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?',\n            closeDrawerHelper: '시스템에서 변경 사항을 저장하지 않을 수 있습니다. 계속하시겠습니까?',\n            deleteSuccess: '삭제 완료',\n            loginSuccess: '로그인 성공',\n            operationSuccess: '작업 완료',\n            copySuccess: '복사 완료',\n            notSupportOperation: '이 작업은 지원되지 않습니다',\n            requestTimeout: '요청이 시간 초과되었습니다. 나중에 다시 시도해 주십시오',\n            infoTitle: '안내',\n            notRecords: '현재 작업에 대한 실행 기록이 생성되지 않았습니다',\n            sureLogOut: '로그아웃하시겠습니까?',\n            createSuccess: '생성 완료',\n            updateSuccess: '업데이트 완료',\n            uploadSuccess: '업로드 성공',\n            operateConfirm: '작업을 확인하려면 수동으로 입력하십시오: ',\n            inputOrSelect: '선택하거나 입력해 주십시오',\n            copyFailed: '복사 실패',\n            operatorHelper: '\"{0}\"에 대해 \"{1}\" 작업이 수행되며 이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?',\n            notFound: '죄송합니다. 요청하신 페이지를 찾을 수 없습니다.',\n            unSupportType: '현재 파일 형식은 지원되지 않습니다.',\n            unSupportSize: '업로드된 파일이 {0}M을 초과했습니다. 확인해 주십시오!',\n            fileExist: '현재 폴더에 이미 동일한 파일이 존재합니다. 중복 업로드는 지원되지 않습니다.',\n            fileNameErr: '파일 이름은 1~256 자 사이의 영어, 중국어, 숫자, 또는 점(.-_)만 포함해야 합니다.',\n            confirmNoNull: '{0} 값이 비어 있지 않은지 확인하십시오.',\n            errPort: '포트 정보가 올바르지 않습니다. 확인해 주십시오!',\n            remove: '제거',\n            backupHelper: '현재 작업은 {0}을(를) 백업합니다. 계속하시겠습니까?',\n            recoverHelper: '{0} 파일에서 복원 중입니다. 이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?',\n            refreshSuccess: '새로 고침 완료',\n            rootInfoErr: '이미 루트 디렉토리입니다',\n            resetSuccess: '초기화 완료',\n            creatingInfo: '생성 중입니다. 이 작업이 필요하지 않습니다',\n            offlineTips: '오프라인 버전은 이 작업을 지원하지 않습니다',\n            errImportFormat: '가져오기 데이터 또는 형식이 비정상입니다. 확인 후 다시 시도하세요!',\n            importHelper:\n                '충돌하거나 중복되는 데이터를 가져올 때 가져온 내용을 기준으로 원래 데이터베이스 데이터를 업데이트합니다.',\n            errImport: '파일 내용이 비정상입니다:',\n            backupSuccess: '백업 성공',\n            restoreSuccess: '복원 성공',\n            installSuccess: '설치 성공',\n            uninstallSuccess: '제거 성공',\n        },\n        login: {\n            username: '사용자 이름',\n            password: '비밀번호',\n            passkey: '패스키 로그인',\n            welcome: '다시 오신 것을 환영합니다. 사용자 이름과 비밀번호를 입력하여 로그인하세요!',\n            errorAuthInfo: '입력한 사용자 이름 또는 비밀번호가 잘못되었습니다. 다시 입력해주세요!',\n            errorMfaInfo: '인증 정보가 잘못되었습니다. 다시 시도해주세요!',\n            captchaHelper: '캡챠',\n            errorCaptcha: '캡챠 코드 오류!',\n            notSafe: '접근이 거부되었습니다',\n            safeEntrance1: '현재 환경에서 보안 로그인이 활성화되었습니다',\n            safeEntrance2: 'SSH 터미널에서 다음 명령어를 입력하여 패널 진입 경로를 확인하세요: 1pctl user-info',\n            errIP1: '현재 환경에서 승인된 IP 주소 접근이 활성화되었습니다',\n            errDomain1: '현재 환경에서 도메인 이름 바인딩이 활성화되었습니다',\n            errHelper: '바인딩 정보를 재설정하려면 SSH 터미널에서 다음 명령어를 실행하세요:',\n            codeInput: 'MFA 인증기의 6자리 인증 코드를 입력하세요',\n            mfaTitle: 'MFA 인증',\n            mfaCode: 'MFA 인증 코드',\n            title: 'Linux 서버 관리 패널',\n            licenseHelper: '<커뮤니티 라이선스 계약>',\n            errorAgree: '커뮤니티 소프트웨어 라이선스에 동의하려면 클릭하세요',\n            logout: '로그아웃',\n            agreeTitle: '동의',\n            agreeContent:\n                '귀하의 합법적인 권리와 이익을 보다 잘 보호하기 위해, 다음 &laquo; <a href = \"https://www.fit2cloud.com/legal/licenses.html\" target = \"_blank\" >커뮤니티 라이선스 계약</a> &raquo;을 읽고 동의해주세요.',\n            passkeyFailed: '패스키 로그인에 실패했습니다. 다시 시도하세요',\n            passkeyNotSupported: '현재 브라우저 또는 환경에서 패스키를 지원하지 않습니다',\n            passkeyToPassword: '패스키 사용에 문제가 있나요? 비밀번호로 로그인하세요',\n        },\n        rule: {\n            username: '사용자 이름을 입력하세요',\n            password: '비밀번호를 입력하세요',\n            rePassword: '확인 비밀번호가 비밀번호와 일치하지 않습니다.',\n            requiredInput: '이 필드는 필수 항목입니다.',\n            requiredSelect: '목록에서 항목을 선택하세요',\n            illegalChar: '현재 & ; $ \\' ` ( ) \" > < | 문자 주입은 지원되지 않습니다',\n            illegalInput: '이 필드에는 유효하지 않은 문자가 포함될 수 없습니다.',\n            commonName:\n                '이 필드는 특수 문자로 시작할 수 없으며, 영어, 한자, 숫자, \".\", \"-\", \"_\" 문자로 구성되어야 하며 길이는 1-128자여야 합니다.',\n            userName: '특수 문자로 시작하지 않고, 영어, 한국어, 숫자 및 _, 길이 3-30 지원',\n            simpleName:\n                '이 필드는 \"_\"로 시작할 수 없으며, 영어, 숫자 및 \"_\" 문자로 구성되어야 하며 길이는 3-30 자여야 합니다.',\n            simplePassword:\n                '이 필드는 \"_\"로 시작할 수 없으며, 영어, 숫자 및 \"_\" 문자로 구성되어야 하며 길이는 1-30 자여야 합니다.',\n            dbName: '이 필드는 \"_\"로 시작할 수 없으며, 영어, 숫자 및 \"_\" 문자로 구성되어야 하며 길이는 1-64 자여야 합니다.',\n            imageName: '특수 문자로 시작하지 않고, 영어, 숫자, :@/.-_ 지원, 길이 1-256',\n            composeName: '특수 문자로 시작할 수 없으며, 소문자, 숫자, \"-\", \"_\"를 지원하며 길이는 1-256 자여야 합니다.',\n            volumeName: '이 필드는 영어, 숫자, \".\", \"-\", \"_\" 문자로 구성되어야 하며 길이는 2-30 자여야 합니다.',\n            supervisorName:\n                '이 필드는 특수 문자로 시작할 수 없으며, 영어, 숫자, \"-\", \"_\" 문자로 구성되어야 하며 길이는 1-128 자여야 합니다.',\n            complexityPassword:\n                '이 필드는 영어와 숫자로 구성되어야 하며 길이는 8-30 자이고 최소 두 개의 특수 문자가 포함되어야 합니다.',\n            commonPassword: '이 필드 길이는 6 자 이상이어야 합니다.',\n            linuxName: '이 필드 길이는 1-128 자 사이여야 하며, 다음 특수 문자를 포함할 수 없습니다: \"{0}\".',\n            email: '이 필드는 유효한 이메일 주소여야 합니다.',\n            number: '이 필드는 숫자여야 합니다.',\n            integer: '이 필드는 양의 정수여야 합니다.',\n            ip: '이 필드는 유효한 IP 주소여야 합니다.',\n            host: '이 필드는 유효한 IP 주소 또는 도메인 이름이어야 합니다.',\n            hostHelper: 'IP 주소 또는 도메인 이름 입력을 지원합니다',\n            port: '이 필드는 유효한 포트 번호여야 합니다.',\n            selectHelper: '올바른 {0} 파일을 선택하세요',\n            domain: '이 필드는 다음 형식이어야 합니다: example.com 또는 example.com:8080.',\n            databaseName: '이 필드는 영어, 숫자 및 \"_\" 문자로 구성되어야 하며 길이는 1-30 자여야 합니다.',\n            ipErr: '이 필드는 유효한 IP 주소여야 합니다.',\n            numberRange: '이 필드는 {0}에서 {1} 사이의 숫자여야 합니다.',\n            paramName: '이 필드는 영어, 숫자, \".\", \"-\", \"_\" 문자로 구성되어야 하며 길이는 2-30 자여야 합니다.',\n            paramComplexity:\n                '이 필드는 특수 문자로 시작하거나 끝날 수 없으며, 영어, 숫자, \"{0}\" 문자로 구성되어야 하며 길이는 6-128 자여야 합니다.',\n            paramUrlAndPort: '이 필드는 \"http(s)://(도메인 이름/IP):(포트)\" 형식이어야 합니다.',\n            nginxDoc: '이 필드는 영어, 숫자 및 \".\" 문자로 구성되어야 합니다.',\n            appName:\n                '소문자, 숫자, \"-\", \"_\"를 지원하며 길이는 2-30 자이고, \"-\" 또는 \"_\"로 시작하거나 끝날 수 없습니다.',\n            containerName:\n                '영어, 숫자, \"-\", \"_\", \".\"를 지원하며, \"-\", \"_\", \".\"로 시작할 수 없고 길이는 2-128 자여야 합니다.',\n            mirror: '미러 가속 주소는 http(s)://로 시작해야 하며, 대소문자 영어, 숫자, \".\", \"/\", \"-\"를 지원하고 공백을 포함할 수 없습니다.',\n            disableFunction: '영어 문자, 밑줄 및 ,만 지원합니다',\n            leechExts: '영어 문자, 숫자 및 ,만 지원합니다',\n            paramSimple: '소문자와 숫자를 지원하며 길이는 1-128 자여야 합니다',\n            filePermission: '파일 권한 오류',\n            formatErr: '형식 오류입니다. 확인 후 다시 시도하세요',\n            phpExtension: '소문자 영어와 숫자, \"_\"만 지원합니다',\n            paramHttp: 'http:// 또는 https:// 로 시작해야 합니다',\n            phone: '전화번호 형식이 올바르지 않습니다',\n            authBasicPassword: '알파벳, 숫자 및 일반 특수 문자 지원, 길이 1-72',\n            length128Err: '길이는 128자를 초과할 수 없습니다',\n            maxLength: '길이는 {0}자를 초과할 수 없습니다',\n            alias: '영어, 숫자, - 및 _ 지원, 길이 1-128, -_로 시작하거나 끝날 수 없습니다.',\n        },\n        res: {\n            paramError: '요청이 실패했습니다. 나중에 다시 시도하세요!',\n            forbidden: '현재 사용자는 권한이 없습니다',\n            serverError: '서비스 예외',\n            notFound: '리소스가 존재하지 않습니다',\n            commonError: '요청이 실패했습니다',\n        },\n        service: {\n            serviceNotStarted: '{0} 서비스가 시작되지 않았습니다.',\n        },\n        status: {\n            running: '실행 중',\n            done: '완료',\n            scanFailed: '불완전',\n            success: '성공',\n            waiting: '대기 중',\n            waitForUpgrade: '업그레이드 대기 중',\n            waiting1: '대기 중',\n            failed: '실패',\n            stopped: '중지됨',\n            error: '오류',\n            created: '생성됨',\n            restarting: '재시작 중',\n            uploading: '업로드 중',\n            unhealthy: '비정상',\n            removing: '제거 중',\n            paused: '일시 중지',\n            exited: '종료됨',\n            dead: '중단됨',\n            installing: '설치 중',\n            enabled: '활성화됨',\n            disabled: '비활성화됨',\n            normal: '정상',\n            building: '빌드 중',\n            upgrading: '업그레이드 중',\n            pending: '편집 대기',\n            rebuilding: '재빌드 중',\n            deny: '거부됨',\n            accept: '수락됨',\n            used: '사용 중',\n            unUsed: '사용 안 함',\n            starting: '시작 중',\n            recreating: '재생성 중',\n            creating: '생성 중',\n            init: '애플리케이션 대기 중',\n            ready: '정상',\n            applying: '적용 중',\n            uninstalling: '제거 중',\n            lost: '연결 끊김',\n            bound: '바인딩됨',\n            unbind: '미바인드',\n            exceptional: '예외',\n            free: '여유',\n            enable: '활성화됨',\n            disable: '비활성화됨',\n            deleted: '삭제됨',\n            downloading: '다운로드 중',\n            packing: '패키징 중',\n            sending: '전송 중',\n            healthy: '정상',\n            executing: '실행 중',\n            installerr: '설치 실패',\n            applyerror: '적용 실패',\n            systemrestart: '중단됨',\n            starterr: '시작 실패',\n            uperr: '실행 실패',\n            new: '신규',\n            conflict: '충돌',\n            duplicate: '중복',\n            unexecuted: '실행되지 않음',\n            unused: '미사용',\n        },\n        units: {\n            second: '초 | 초 | 초',\n            minute: '분 | 분 | 분',\n            hour: '시간 | 시간 | 시간',\n            day: '일 | 일 | 일',\n            week: '주 | 주 | 주',\n            month: '월 | 월 | 월',\n            year: '년 | 년 | 년',\n            time: '시간',\n            core: '코어 | 코어 | 코어',\n            secondUnit: '초',\n            minuteUnit: '분',\n            hourUnit: '시간',\n            dayUnit: '일',\n            millisecond: '밀리초',\n            semicolon: ';',\n        },\n        log: {\n            noLog: '로그 없음',\n        },\n    },\n    menu: {\n        home: '대시보드',\n        apps: '앱 스토어',\n        website: '웹사이트 | 웹사이트들',\n        project: '프로젝트 | 프로젝트들',\n        config: '구성 | 구성들',\n        ssh: 'SSH 설정',\n        firewall: '방화벽',\n        ssl: '인증서 | 인증서들',\n        database: '데이터베이스 | 데이터베이스들',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: '컨테이너 | 컨테이너들',\n        cronjob: '크론 작업 | 크론 작업들',\n        system: '시스템',\n        security: '보안',\n        files: '파일',\n        monitor: '모니터링',\n        terminal: '터미널',\n        settings: '설정',\n        toolbox: '툴박스',\n        logs: '로그 | 로그들',\n        runtime: '런타임 | 런타임들',\n        processManage: '프로세스 | 프로세스들',\n        process: '프로세스 | 프로세스들',\n        network: '네트워크 | 네트워크들',\n        supervisor: '슈퍼바이저',\n        tamper: '변조 방지',\n        app: '애플리케이션',\n        msgCenter: '작업',\n        disk: '디스크',\n        filter: '필터',\n    },\n    home: {\n        recommend: '추천',\n        dir: '디렉토리',\n        alias: '별칭',\n        quickDir: '빠른 액세스',\n        minQuickJump: '최소 하나의 빠른 액세스 항목을 추가하세요',\n        maxQuickJump: '빠른 액세스를 최대 4개까지 추가할 수 있습니다',\n        database: '데이터베이스 - 전체',\n        restart_1panel: '패널 재시작',\n        restart_system: '서버 재시작',\n        operationSuccess: '완료되었습니다. 재시작 중입니다. 나중에 브라우저를 새로 고침하세요.',\n        entranceHelper: '보안 진입이 활성화되어 있지 않습니다. \"설정 -> 보안\"에서 활성화하여 보안을 강화하세요.',\n        appInstalled: '설치된 애플리케이션',\n        systemInfo: '시스템 정보',\n        hostname: '호스트 이름',\n        platformVersion: '운영 체제',\n        kernelVersion: '커널',\n        kernelArch: '아키텍처',\n        network: '네트워크',\n        io: '디스크 I/O',\n        ip: '로컬 IP',\n        proxy: '시스템 프록시',\n        baseInfo: '기본 정보',\n        totalSend: '총 송신',\n        totalRecv: '총 수신',\n        rwPerSecond: 'I/O 작업',\n        ioDelay: 'I/O 지연 시간',\n        uptime: '작동 시간',\n        runningTime: '가동 시간',\n        mem: '시스템 메모리',\n        swapMem: '스왑 파티션',\n        runSmoothly: '낮은 부하',\n        runNormal: '보통 부하',\n        runSlowly: '높은 부하',\n        runJam: '심한 부하',\n        core: '물리적 코어',\n        logicCore: '논리 코어',\n        corePercent: '코어 사용률',\n        cpuFrequency: 'CPU 주파수',\n        cpuDetailedPercent: 'CPU 사용률 상세',\n        cpuUser: '사용자',\n        cpuSystem: '시스템',\n        cpuIdle: '유휴',\n        cpuIrq: 'IRQ',\n        cpuSoftirq: 'Soft IRQ',\n        cpuSteal: 'Steal',\n        cpuTop: 'CPU 사용률 상위 5개 프로세스 정보',\n        memTop: '메모리 사용률 상위 5개 프로세스 정보',\n        loadAverage: '지난 1분의 평균 부하 | 지난 {n} 분의 평균 부하',\n        load: '부하',\n        mount: '마운트 지점',\n        fileSystem: '파일 시스템',\n        total: '전체',\n        used: '사용',\n        cache: '�시',\n        free: '여유',\n        shard: '샤딩',\n        available: '사용 가능',\n        percent: '사용률',\n        goInstall: 'Go 설치',\n        networkCard: '네트워크 카드',\n        disk: '디스크',\n        memo: '메모',\n        memoPlaceholder: '편집 버튼을 클릭하여 편집을 활성화하세요.',\n        carouselSetting: '캐러셀 설정',\n        tooltipSensitiveInfo: '민감한 정보 표시/숨기기',\n    },\n    tabs: {\n        more: '더 보기',\n        hide: '숨기기',\n        closeLeft: '왼쪽 닫기',\n        closeRight: '오른쪽 닫기',\n        closeCurrent: '현재 탭 닫기',\n        closeOther: '다른 탭 닫기',\n        closeAll: '모두 닫기',\n    },\n    header: {\n        logout: '로그아웃',\n    },\n    database: {\n        manage: '관리',\n        deleteBackupHelper: '데이터베이스 백업을 동시에 삭제',\n        delete: '삭제 작업은 되돌릴 수 없습니다. 삭제하려면 \"',\n        deleteHelper: '\"를 입력하세요.',\n        noMysql: '데이터베이스 서비스 (MySQL 또는 MariaDB)',\n        noPostgresql: '데이터베이스 서비스 PostgreSQL',\n        goUpgrade: '업그레이드로 이동',\n        goInstall: '설치로 이동',\n        isDelete: '삭제됨',\n        permission: '권한',\n        format: '문자 집합',\n        collation: '콜레이션',\n        collationHelper: '비어 있으면 {0} 문자 집합의 기본 콜레이션을 사용합니다',\n        permissionForIP: 'IP',\n        permissionAll: '모두(%)',\n        localhostHelper:\n            '컨테이너 배포 시 데이터베이스 권한을 \"localhost\"로 설정하면 컨테이너 외부에서 접근할 수 없게 됩니다. 신중하게 선택하세요!',\n        databaseConnInfo: '연결',\n        rootPassword: '루트 비밀번호',\n        serviceName: '서비스 이름',\n        serviceNameHelper: '같은 네트워크 내 컨테이너 간의 접근.',\n        backupList: '백업',\n        loadBackup: '불러오기',\n        localUpload: '로컬 업로드',\n        hostSelect: '서버 선택',\n        selectHelper: '백업 파일 {0}을(를) 가져오시겠습니까?',\n        remoteAccess: '원격 접근',\n        remoteHelper: '여러 IP 를 쉼표로 구분하여 입력, 예: 172.16.10.111, 172.16.10.112',\n        remoteConnHelper:\n            'MySQL 의 root 사용자로 원격 접속은 보안 위험을 초래할 수 있습니다. 따라서 이 작업은 신중히 수행해야 합니다.',\n        changePassword: '비밀번호',\n        changeConnHelper: '이 작업은 현재 데이터베이스 {0}을(를) 수정합니다. 계속하시겠습니까?',\n        changePasswordHelper:\n            '데이터베이스가 애플리케이션과 연결되어 있습니다. 비밀번호를 변경하면 애플리케이션의 데이터베이스 비밀번호도 변경됩니다. 변경 사항은 애플리케이션이 재시작된 후에 적용됩니다.',\n        recoverTimeoutHelper: '-1은 제한 시간 제한 없음을 의미합니다',\n        confChange: '설정',\n        confNotFound:\n            '설정 파일을 찾을 수 없습니다. 앱 스토어에서 애플리케이션을 최신 버전으로 업그레이드하고 다시 시도해주세요!',\n        portHelper: '이 포트는 컨테이너의 노출된 포트입니다. 수정을 별도로 저장하고 컨테이너를 재시작해야 합니다!',\n        loadFromRemote: '동기화',\n        userBind: '사용자 바인딩',\n        pgBindHelper:\n            '이 작업은 새 사용자를 생성하여 대상 데이터베이스에 바인딩하는 데 사용됩니다. 현재 데이터베이스에 이미 존재하는 사용자 선택은 지원되지 않습니다.',\n        pgSuperUser: '슈퍼 사용자',\n        loadFromRemoteHelper: '이 작업은 서버의 데이터베이스 정보를 1Panel로 동기화합니다. 계속 진행하시겠습니까?',\n        passwordHelper: '확인 불가, 수정하려면 클릭',\n        remote: '원격',\n        remoteDB: '원격 서버 | 원격 서버들',\n        createRemoteDB: '원격 DB 바인딩',\n        unBindRemoteDB: '원격 DB 바인딩 해제',\n        unBindForce: '강제 바인딩 해제',\n        unBindForceHelper: '바인딩 해제 중 발생하는 모든 오류를 무시하고 최종 작업을 성공적으로 완료합니다.',\n        unBindRemoteHelper:\n            '원격 데이터베이스 바인딩 해제는 바인딩 관계만 제거하며, 원격 데이터베이스 자체는 삭제되지 않습니다.',\n        editRemoteDB: '원격 서버 편집',\n        localDB: '로컬 데이터베이스',\n        address: '데이터베이스 주소',\n        version: '데이터베이스 버전',\n        userHelper: '루트 사용자 또는 루트 권한을 가진 데이터베이스 사용자가 원격 데이터베이스에 접근할 수 있습니다.',\n        pgUserHelper: '슈퍼 사용자 권한을 가진 데이터베이스 사용자.',\n        ssl: 'SSL 사용',\n        clientKey: '클라이언트 개인 키',\n        clientCert: '클라이언트 인증서',\n        caCert: 'CA 인증서',\n        hasCA: 'CA 인증서 있음',\n        skipVerify: '인증서 유효성 검사 무시',\n        initialDB: '초기 데이터베이스',\n        formatHelper: '현재 데이터베이스 문자셋은 {0} 입니다. 문자셋 불일치로 인해 복구에 실패할 수 있습니다.',\n        dropHelper: '여기에 업로드한 파일을 드래그 앤 드롭하거나',\n        clickHelper: '클릭하여 업로드',\n        supportUpType:\n            'sql, sql.gz, tar.gz, .zip 파일 형식만 지원합니다. 가져오는 압축 파일에는 하나의 .sql 파일만 있거나 test.sql이 포함되어 있어야 합니다',\n        currentStatus: '현재 상태',\n        baseParam: '기본 파라미터',\n        performanceParam: '성능 파라미터',\n        runTime: '시작 시간',\n        connections: '전체 연결',\n        bytesSent: '전송된 바이트',\n        bytesReceived: '수신된 바이트',\n        queryPerSecond: '초당 쿼리',\n        txPerSecond: '초당 전송',\n        connInfo: '활성/최대 연결',\n        connInfoHelper: '값이 너무 크면 \"max_connections\" 값을 증가시켜야 합니다.',\n        threadCacheHit: '스레드 캐시 적중',\n        threadCacheHitHelper: '값이 너무 낮으면 \"thread_cache_size\" 값을 증가시켜야 합니다.',\n        indexHit: '인덱스 적중',\n        indexHitHelper: '값이 너무 낮으면 \"key_buffer_size\" 값을 증가시켜야 합니다.',\n        innodbIndexHit: 'Innodb 인덱스 적중률',\n        innodbIndexHitHelper: '값이 너무 낮으면 \"innodb_buffer_pool_size\" 값을 증가시켜야 합니다.',\n        cacheHit: '쿼리 캐시 적중',\n        cacheHitHelper: '값이 너무 낮으면 \"query_cache_size\" 값을 증가시켜야 합니다.',\n        tmpTableToDB: '디스크로 임시 테이블',\n        tmpTableToDBHelper: '값이 너무 크면 \"tmp_table_size\" 값을 증가시켜야 합니다.',\n        openTables: '열린 테이블',\n        openTablesHelper: '\"table_open_cache\" 설정 값이 이 값 이상이어야 합니다.',\n        selectFullJoin: '전체 조인 선택',\n        selectFullJoinHelper: '값이 0이 아니면 데이터 테이블의 인덱스가 올바른지 확인하십시오.',\n        selectRangeCheck: '인덱스 없는 조인 수',\n        selectRangeCheckHelper: '값이 0이 아니면 데이터 테이블의 인덱스가 올바른지 확인하십시오.',\n        sortMergePasses: '정렬된 병합 횟수',\n        sortMergePassesHelper: '값이 너무 크면 \"sort_buffer_size\" 값을 증가시켜야 합니다.',\n        tableLocksWaited: '테이블 잠금 대기',\n        tableLocksWaitedHelper: '값이 너무 크면 데이터베이스 성능을 증가시키는 것을 고려해야 합니다.',\n        performanceTuning: '성능 튜닝',\n        optimizationScheme: '최적화 방안',\n        keyBufferSizeHelper: '인덱스용 버퍼 크기',\n        queryCacheSizeHelper: '쿼리 캐시. 이 기능이 비활성화된 경우 이 값을 0으로 설정하세요.',\n        tmpTableSizeHelper: '임시 테이블 캐시 크기',\n        innodbBufferPoolSizeHelper: 'Innodb 버퍼 크기',\n        innodbLogBufferSizeHelper: 'Innodb 로그 버퍼 크기',\n        sortBufferSizeHelper: '* 연결당, 스레드 정렬 버퍼 크기',\n        readBufferSizeHelper: '* 연결당, 읽기 버퍼 크기',\n        readRndBufferSizeHelper: '* 연결당, 임의 읽기 버퍼 크기',\n        joinBufferSizeHelper: '* 연결당, 조인 테이블 캐시 크기',\n        threadStackelper: '* 연결당, 스레드별 스택 크기',\n        binlogCacheSizeHelper: '* 연결당, 이진 로그 캐시 크기 (4096의 배수)',\n        threadCacheSizeHelper: '스레드 풀 크기',\n        tableOpenCacheHelper: '테이블 캐시',\n        maxConnectionsHelper: '최대 연결 수',\n        restart: '재시작',\n        slowLog: '느린 로그',\n        noData: '아직 느린 로그가 없습니다.',\n        isOn: '켜짐',\n        longQueryTime: '임계값(s)',\n        thresholdRangeHelper: '올바른 임계값을 입력하십시오 (1 - 600).',\n        timeout: '타임아웃(s)',\n        timeoutHelper: '유휴 연결의 타임아웃 기간. 0은 연결이 지속적으로 유지됨을 의미합니다.',\n        maxclients: '최대 클라이언트',\n        requirepassHelper:\n            '비밀번호가 설정되지 않은 경우 이 필드를 비워 두세요. 변경 사항은 별도로 저장하고 컨테이너를 재시작해야 합니다!',\n        databases: '데이터베이스 수',\n        maxmemory: '최대 메모리 사용량',\n        maxmemoryHelper: '0은 제한이 없음을 의미합니다.',\n        tcpPort: '현재 수신 포트.',\n        uptimeInDays: '운영 일수.',\n        connectedClients: '연결된 클라이언트 수.',\n        usedMemory: '현재 Redis 의 메모리 사용량.',\n        usedMemoryRss: '운영 체제에서 요청한 메모리 크기.',\n        usedMemoryPeak: 'Redis 의 최대 메모리 소비량.',\n        memFragmentationRatio: '메모리 단편화 비율.',\n        totalConnectionsReceived: '시작 이후 총 연결된 클라이언트 수.',\n        totalCommandsProcessed: '실행된 총 명령 수.',\n        instantaneousOpsPerSec: '초당 서버에서 실행된 명령 수.',\n        keyspaceHits: '데이터베이스 키가 성공적으로 발견된 횟수.',\n        keyspaceMisses: '데이터베이스 키를 찾지 못한 횟수.',\n        hit: '데이터베이스 키 발견 비율.',\n        latestForkUsec: '마지막 fork() 작업에 소요된 마이크로초 수.',\n        redisCliHelper: '\"redis-cli\" 서비스가 감지되지 않았습니다. 서비스를 먼저 활성화하십시오.',\n        redisQuickCmd: 'Redis 빠른 명령',\n        recoverHelper: '이 작업은 데이터를 [{0}]으로 덮어씁니다. 계속하시겠습니까?',\n        submitIt: '데이터 덮어쓰기',\n        baseConf: '기본 설정',\n        allConf: '모든 설정',\n        restartNow: '지금 재시작',\n        restartNowHelper1:\n            '구성 변경 사항이 적용되려면 시스템을 재시작해야 합니다. 데이터가 지속되어야 하는 경우 먼저 저장 작업을 수행하십시오.',\n        restartNowHelper: '이 작업은 시스템이 재시작된 후에만 적용됩니다.',\n        persistence: '지속성',\n        rdbHelper1: '초 단위, 삽입',\n        rdbHelper2: '데이터 항목 수',\n        rdbHelper3: '조건을 충족하면 RDB 지속성이 트리거됩니다.',\n        rdbInfo: '규칙 목록의 값이 1에서 100000 사이여야 합니다.',\n        containerConn: '컨테이너 연결',\n        copyConnURL: '연결 URL 복사',\n        connAddress: '주소',\n        containerConnHelper:\n            '이 연결 주소는 웹사이트 런타임(PHP 등) 또는 컨테이너에서 실행 중인 애플리케이션에서 사용할 수 있습니다.',\n        remoteConn: '외부 연결',\n        remoteConnHelper2: '컨테이너 환경이 아닌 경우 또는 외부 연결에는 이 주소를 사용하십시오.',\n        remoteConnHelper3:\n            '기본 접근 주소는 호스트 IP입니다. 수정하려면 패널 설정 페이지의 \"기본 접근 주소\" 구성 항목으로 이동하세요.',\n        localIP: '로컬 IP',\n    },\n    aiTools: {\n        agents: {\n            agents: '에이전트',\n            agent: '에이전트',\n            account: '모델 계정',\n            noAccountHint: '기존 모델 계정을 선택하거나 새로 추가하세요.',\n            accountCount: '모델 계정 {0}개',\n            syncAgents: '관련 에이전트 동기화',\n            syncAgentsHelper: '이 모델 계정을 사용하는 에이전트의 openclaw.json 업데이트',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: '앱 버전',\n            webuiPort: 'WebUI 포트',\n            allowedOrigins: '접속 주소',\n            allowedOriginsHelper:\n                '한 줄에 하나의 전체 접속 주소를 입력하세요. HTTPS 사용을 권장합니다. 예: https://192.168.1.2:18789. 기본 접속 주소가 설정되지 않은 경우 수동으로 입력하세요.',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: '접속 주소를 하나 이상 입력하세요',\n            allowedOriginsInvalid: 'http(s)://host-or-ip[:port] 형식으로 입력하세요',\n            provider: '모델 제공자',\n            apiKey: 'API 키',\n            baseUrl: '기본 URL',\n            accountModels: 'Model Catalog',\n            accountModelsHelper: 'Configure the models this account exposes to OpenClaw for switching and settings',\n            accountModelsRequired: 'Configure at least one model',\n            accountModelsDuplicate: 'Duplicate models exist in the catalog',\n            modelPool: 'Model Pool',\n            modelPoolHelper:\n                'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',\n            modelInputTypes: 'Input Types',\n            reasoning: 'Reasoning Model',\n            token: '토큰',\n            manualModel: '수동 입력',\n            verified: '검증됨',\n            verifySkipped: '검증 안 함',\n            configTitle: 'Configuration',\n            settingsTab: 'Settings',\n            securityTab: 'Security',\n            otherTab: 'Other',\n            timeZone: '시간대',\n            browserEnabled: 'Browser Enabled',\n            switchModelSuccess: 'Model switched successfully',\n            channelsTab: 'Channels',\n            wecom: 'WeCom',\n            dingtalk: 'DingTalk',\n            feishu: 'Feishu',\n            pluginNotInstalled: '플러그인이 설치되지 않았습니다. 먼저 설치해 주세요.',\n            dmPolicy: 'DM Policy',\n            groupPolicy: 'Group Policy',\n            policyPairing: 'Pairing',\n            policyAllowlist: 'Allowlist',\n            policyOpen: 'Open',\n            policyDisabled: 'Disabled',\n            botName: 'Bot Name',\n            botId: 'Bot ID',\n            appId: 'App ID',\n            appSecret: 'App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: 'DM Allowlist',\n            allowFromHelper: 'One sender ID per line. Used only when DM Policy is Allowlist.',\n            allowFromPlaceholder: 'One sender ID per line',\n            groupAllowFrom: 'Group Allowlist',\n            groupAllowFromHelper: 'One group ID per line. Used only when Group Policy is Allowlist.',\n            groupAllowFromPlaceholder: 'One group ID per line',\n            allowFromRequired: 'Enter at least one allowlist entry',\n            saveAndRestartGateway: 'Save and restart gateway',\n            pairingCode: 'Pairing Code',\n            pairingCodePlaceholder: 'Enter pairing code',\n            approvePairing: 'Approve Pairing',\n            feishuRequired: 'Please fill botName / appId / appSecret',\n            saveSuccess: 'Saved successfully',\n            pairingCodeRequired: 'Please enter pairing code',\n            pairingApproveSuccess: 'Pairing approved successfully',\n            customProviderHelper: '사용자 정의 모델 공급자는 계정 사용 가능 여부를 검증하지 않습니다',\n            feishuSaveSuccess: 'Feishu에 저장됨',\n        },\n        model: {\n            model: '모델',\n            localModel: '로컬 모델',\n            create: '모델 추가',\n            create_helper: '가져오기 \"{0}\"',\n            ollama_doc: 'Ollama 공식 웹사이트를 방문하여 더 많은 모델을 검색하고 찾을 수 있습니다.',\n            container_conn_helper: '컨테이너 간 접근 또는 연결에 이 주소를 사용',\n            ollama_sync: 'Ollama 모델 동기화 중 다음 모델이 존재하지 않음을 발견했습니다. 삭제하시겠습니까?',\n            from_remote: '이 모델은 1Panel을 통해 다운로드되지 않았으며 관련 풀 로그가 없습니다.',\n            no_logs: '이 모델의 풀 로그가 삭제되어 관련 로그를 볼 수 없습니다.',\n            vllmVersionHelper: 'FusionXpark GB 10 서버는 -cu130 버전을 선택하세요.',\n        },\n        proxy: {\n            proxy: 'AI 프록시 강화',\n            proxyHelper1: '도메인을 바인딩하고 HTTPS를 활성화하여 전송 보안을 강화',\n            proxyHelper2: 'IP 접근을 제한하여 공용 인터넷에서의 노출을 방지',\n            proxyHelper3: '스트리밍을 활성화',\n            proxyHelper4: '생성 후, 웹사이트 목록에서 이를 보고 관리할 수 있습니다',\n            proxyHelper5:\n                '활성화한 후, 앱 스토어 - 설치됨 - Ollama - 매개변수에서 포트 외부 접근을 비활성화하여 보안을 강화할 수 있습니다.',\n            proxyHelper6: '프록시 구성을 비활성화하려면 웹사이트 목록에서 삭제할 수 있습니다.',\n            whiteListHelper: '화이트리스트에 있는 IP만 접근 허용',\n        },\n        gpu: {\n            gpu: 'GPU 모니터링',\n            gpuHelper: '시스템에서 NVIDIA-SMI 또는 XPU-SMI 명령을 감지하지 못했습니다. 확인하고 다시 시도하세요!',\n            process: '프로세스 정보',\n            type: '유형',\n            typeG: '그래픽',\n            typeC: '컴퓨팅',\n            typeCG: '컴퓨팅+그래픽',\n            processName: '프로세스 이름',\n            shr: '공유 메모리',\n            temperatureHelper: 'GPU 온도가 높으면 GPU 주파수가 감소할 수 있습니다',\n            gpuUtil: 'GPU 사용률',\n            temperature: '온도',\n            performanceState: '성능 상태',\n            powerUsage: '전력 소비',\n            memoryUsage: '메모리 사용률',\n            fanSpeed: '팬 속도',\n            power: '전력',\n            powerCurrent: '현재 전력',\n            powerLimit: '전력 제한',\n            memory: '메모리',\n            memoryUsed: '사용된 메모리',\n            memoryTotal: '전체 메모리',\n            percent: '사용률',\n            base: '기본 정보',\n            driverVersion: '드라이버 버전',\n            cudaVersion: 'CUDA 버전',\n            processMemoryUsage: '메모리 사용량',\n            performanceStateHelper: 'P0(최대 성능)부터 P12(최소 성능)까지',\n            busID: '버스 주소',\n            persistenceMode: '지속성 모드',\n            enabled: '활성화',\n            disabled: '비활성화',\n            persistenceModeHelper: '지속성 모드는 작업에 더 빠르게 응답하지만 대기 전력 소비도 그에 따라 증가합니다',\n            displayActive: 'GPU 초기화',\n            displayActiveT: '예',\n            displayActiveF: '아니오',\n            ecc: '오류 검사 및 수정 기술',\n            computeMode: '계산 모드',\n            default: '기본값',\n            exclusiveProcess: '배타적 프로세스',\n            exclusiveThread: '배타적 스레드',\n            prohibited: '금지됨',\n            defaultHelper: '기본값: 프로세스가 동시에 실행될 수 있음',\n            exclusiveProcessHelper:\n                '배타적 프로세스: 하나의 CUDA 컨텍스트만 GPU를 사용할 수 있지만 여러 스레드에서 공유 가능',\n            exclusiveThreadHelper: '배타적 스레드: CUDA 컨텍스트의 하나의 스레드만 GPU를 사용할 수 있음',\n            prohibitedHelper: '금지됨: 프로세스 동시 실행이 허용되지 않음',\n            migModeHelper: 'MIG 인스턴스를 생성하는 데 사용되며 사용자 레이어에서 GPU의 물리적 격리를 구현합니다.',\n            migModeNA: '지원되지 않음',\n            current: '실시간 모니터링',\n            history: '기록',\n            notSupport: '현재 버전 또는 드라이버는 이 매개변수 표시를 지원하지 않습니다.',\n            processCount: '프로세스 수',\n        },\n        mcp: {\n            server: 'MCP サーバー',\n            baseUrl: '外部アクセスパス',\n            baseUrlHelper: '例: http://192.168.1.2:8000',\n            ssePath: 'SSE パス',\n            ssePathHelper: '例: /sse, 他のサーバーと重複しないように注意してください',\n            environment: '環境変数',\n            envKey: '変数名',\n            envValue: '変数値',\n            externalUrl: '外部接続アドレス',\n            operatorHelper: '{0} に {1} 操作を実行します、続行しますか？',\n            domain: 'デフォルトアクセスアドレス',\n            domainHelper: '例: 192.168.1.1 または example.com',\n            bindDomain: 'ウェブサイトをバインド',\n            commandPlaceHolder: '현재 npx 및 바이너리 시작 명령만 지원합니다',\n            importMcpJson: 'MCP サーバー設定をインポート',\n            importMcpJsonError: 'mcpServers 構造が正しくありません',\n            bindDomainHelper:\n                '웹사이트를 바인딩한 후, 설치된 모든 MCP 서버의 접근 주소를 수정하고 포트의 외부 접근을 닫습니다',\n            outputTransport: '출력 유형',\n            streamableHttpPath: '스트리밍 경로',\n            streamableHttpPathHelper: '예: /mcp, 다른 서버와 중복되지 않도록 주의하세요',\n            npxHelper: 'npx 또는 바이너리로 시작하는 mcp에 적합',\n            uvxHelper: 'uvx로 시작하는 mcp에 적합',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: '모델 디렉토리',\n            commandHelper: '외부 액세스가 필요한 경우 명령에서 포트를 애플리케이션 포트와 동일하게 설정하십시오',\n            imageAlert: '이미지 크기가 크므로 설치 전에 서버에 이미지를 수동으로 다운로드하는 것이 좋습니다',\n            modelSpeedup: '모델 가속 활성화',\n            modelType: '모델 유형',\n        },\n    },\n    container: {\n        createByCommand: '명령으로 생성',\n        importContainerBackupTip: '컨테이너 백업 파일 가져오기, .tar.gz 형식만 지원합니다',\n        importComposeBackupTip: 'Compose 백업 파일 가져오기, .tar.gz 형식만 지원합니다',\n        stopContainerBeforeBackup: '백업 전에 컨테이너 중지',\n        stopComposeBeforeBackup: '백업 전에 Compose 중지',\n        stopBeforeBackupHelper:\n            '활성화하면 백업 전에 컨테이너 또는 Compose 서비스를 중지하고, 완료 후 자동으로 복구하여 데이터 일관성을 보장합니다.',\n        commandInput: '명령 입력',\n        commandRule: '컨테이너 생성을 위한 올바른 docker run 명령을 입력하세요.',\n        commandHelper: '이 명령은 서버에서 실행되어 컨테이너를 생성합니다. 계속하시겠습니까?',\n        updateHelper1: '이 컨테이너가 앱 스토어에서 왔음을 감지했습니다. 다음 두 가지 사항을 유의하십시오:',\n        updateHelper2: '1. 현재 수정 사항은 앱 스토어에 설치된 애플리케이션에 동기화되지 않습니다.',\n        updateHelper3: '2. 설치된 페이지에서 애플리케이션을 수정하면 현재 편집된 내용이 무효화됩니다.',\n        updateHelper4: '컨테이너 편집에는 재빌드가 필요하며, 비지속적인 데이터는 손실됩니다. 계속하시겠습니까?',\n        containerList: '컨테이너 목록',\n        operatorHelper: '{0} 작업이 다음 컨테이너에서 수행됩니다. 계속하시겠습니까?',\n        operatorAppHelper:\n            '\"{0}\" 작업이 다음 컨테이너에서 수행되며, 실행 중인 서비스에 영향을 미칠 수 있습니다. 계속하시겠습니까?',\n        containerDeleteHelper:\n            \"컨테이너가 앱 스토어에서 생성된 것으로 감지되었습니다. 컨테이너를 삭제해도 1Panel에서 완전히 제거되지 않습니다. 완전히 삭제하려면 앱 스토어 -> '설치됨' 또는 '런타임 환경' 메뉴로 이동하여 작업하십시오. 계속하시겠습니까?\",\n        start: '시작',\n        stop: '중지',\n        restart: '재시작',\n        kill: '강제 종료',\n        pause: '일시 정지',\n        unpause: '재개',\n        rename: '이름 변경',\n        remove: '제거',\n        removeAll: '모두 제거',\n        containerPrune: '정리',\n        containerPruneHelper1: '이 작업은 중지된 모든 컨테이너를 삭제합니다.',\n        containerPruneHelper2:\n            \"앱 스토어에서 가져온 컨테이너는 정리 후 '앱 스토어 -> 설치됨' 페이지로 이동하여 '재빌드' 버튼을 클릭하여 재설치해야 합니다.\",\n        containerPruneHelper3: '이 작업은 취소할 수 없습니다. 계속하시겠습니까?',\n        imagePrune: '정리',\n        imagePruneSome: '라벨 없는 이미지 정리',\n        imagePruneSomeEmpty: \"라벨이 'none'인 이미지가 정리되지 않았습니다.\",\n        imagePruneSomeHelper: \"컨테이너에서 사용되지 않는 'none' 태그가 붙은 이미지를 정리합니다.\",\n        imagePruneAll: '사용되지 않는 이미지 정리',\n        imagePruneAllEmpty: '사용되지 않는 이미지가 정리되지 않았습니다.',\n        imagePruneAllHelper: '사용되지 않는 이미지를 정리합니다.',\n        networkPrune: '정리',\n        networkPruneHelper: '사용되지 않는 네트워크를 모두 제거합니다. 계속하시겠습니까?',\n        volumePrune: '정리',\n        volumePruneHelper: '사용되지 않는 로컬 볼륨을 모두 제거합니다. 계속하시겠습니까?',\n        cleanSuccess: '작업이 성공적으로 완료되었습니다. 이번 정리에서 {0}개의 항목이 정리되었습니다!',\n        cleanSuccessWithSpace:\n            '작업이 성공적으로 완료되었습니다. 이번 정리에서 {0}개의 디스크가 정리되었으며, 확보된 디스크 공간은 {1}입니다!',\n        unExposedPort: '현재 포트 매핑 주소는 127.0.0.1로 외부 액세스를 활성화할 수 없습니다.',\n        upTime: '업타임',\n        fetch: '가져오기',\n        lines: '라인',\n        linesHelper: '올바른 로그 수를 입력하세요!',\n        downloadLinesHelper: '다운로드할 로그 줄 수를 선택하거나 입력하세요.',\n        lastDay: '지난 하루',\n        last4Hour: '지난 4시간',\n        lastHour: '지난 1시간',\n        last10Min: '지난 10분',\n        cleanLog: '로그 정리',\n        downLogHelper1: '이 작업은 컨테이너 {0}의 모든 로그를 다운로드합니다. 계속하시겠습니까?',\n        downLogHelper2: '이 작업은 컨테이너 {0}의 최근 {0}개의 로그를 다운로드합니다. 계속하시겠습니까?',\n        cleanLogHelper: '이 작업은 컨테이너를 재시작해야 하며 취소할 수 없습니다. 계속하시겠습니까?',\n        newName: '새 이름',\n        source: '리소스 사용',\n        cpuUsage: 'CPU 사용',\n        cpuTotal: '전체 CPU',\n        core: '코어',\n        memUsage: '메모리 사용',\n        memTotal: '메모리 한도',\n        memCache: '메모리 캐시',\n        loadSize: '컨테이너 크기 가져오기',\n        ip: 'IP 주소',\n        cpuShare: 'CPU 공유',\n        cpuShareHelper:\n            '컨테이너 엔진은 기본값으로 1024를 사용합니다. 이를 늘리면 컨테이너에 더 많은 CPU 시간을 할당할 수 있습니다.',\n        inputIpv4: '예시: 192.168.1.1',\n        inputIpv6: '예시: 2001:0db8:85a3:0000:0000:8a2e:0370:7334',\n        diskUsage: '디스크 사용량',\n        localVolume: '로컬 스토리지 볼륨',\n        buildCache: '빌드 캐시',\n        usage: '사용됨: {0}, 해제 가능: {1}',\n        clean: '해제',\n        imageClean:\n            '이미지 정리를 수행하면 사용되지 않은 모든 이미지가 삭제됩니다. 이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?',\n        containerClean:\n            '컨테이너 정리를 수행하면 중지된 모든 컨테이너(앱 스토어의 중지된 앱 포함)가 삭제됩니다. 이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?',\n        sizeRw: '컨테이너 레이어 크기',\n        sizeRwHelper: '컨테이너에 고유한 쓰기 가능 레이어 크기',\n        sizeRootFs: '가상 크기',\n        sizeRootFsHelper: '컨테이너가 의존하는 모든 이미지 레이어 + 컨테이너 레이어의 총 크기',\n        containerFromAppHelper:\n            '이 컨테이너가 앱 스토어에서 왔음을 감지했습니다. 앱 작업으로 현재 편집이 무효화될 수 있습니다.',\n        containerFromAppHelper1:\n            '설치된 애플리케이션 목록에서 [매개변수] 버튼을 클릭하여 편집 페이지로 이동하고 컨테이너 이름을 수정하세요.',\n        command: '명령어',\n        console: '컨테이너 상호작용',\n        tty: '가상 TTY 할당 (-t)',\n        openStdin: 'STDIN 을 열어둡니다. 연결되지 않더라도 계속 열려있습니다 (-i)',\n        custom: '사용자 정의',\n        emptyUser: '비워두면 기본값으로 로그인합니다.',\n        privileged: '특권 모드',\n        privilegedHelper:\n            '컨테이너가 호스트에서 특정 특권 작업을 수행할 수 있도록 허용합니다. 이는 보안 위험을 초래할 수 있으므로 주의해서 사용하십시오.',\n        upgradeHelper: '레포지토리 이름/이미지 이름: 이미지 버전',\n        upgradeWarning2:\n            '업그레이드 작업은 컨테이너를 재빌드해야 하며, 비지속적인 데이터가 손실됩니다. 계속하시겠습니까?',\n        oldImage: '현재 이미지',\n        sameImageContainer: '동일 이미지 컨테이너',\n        sameImageHelper: '동일한 이미지를 사용하는 컨테이너는 선택 후 일괄 업그레이드 가능',\n        targetImage: '대상 이미지',\n        imageLoadErr: '컨테이너에 대한 이미지 이름이 감지되지 않았습니다.',\n        imageUpdateTagEmpty: '업데이트 가능한 이미지 태그를 찾지 못했습니다.',\n        appHelper: '이 컨테이너는 앱 스토어에서 왔으며 업그레이드 시 서비스가 중단될 수 있습니다.',\n        input: '수동 입력',\n        forcePull: '이미지 강제 풀',\n        forcePullHelper: '이 작업은 서버에 있는 기존 이미지를 무시하고 레지스트리에서 최신 이미지를 강제로 가져옵니다.',\n        imageUpdateHelper: '레지스트리의 동일 태그를 확인하고, 변경이 있으면 가져와 로컬 이미지를 업데이트합니다.',\n        server: '호스트',\n        serverExample: '80, 80-88, ip:80 또는 ip:80-88',\n        containerExample: '80 또는 80-88',\n        exposePort: '포트 노출',\n        exposeAll: '모든 포트 노출',\n        cmdHelper: '예시: nginx -g \"daemon off;\"',\n        entrypointHelper: '예시: docker-entrypoint.sh',\n        autoRemove: '자동 제거',\n        cpuQuota: 'CPU 코어 수',\n        memoryLimit: '메모리',\n        limitHelper: '0으로 설정하면 제한이 없으며, 최대값은 {0}입니다.',\n        mount: '마운트',\n        volumeOption: '볼륨',\n        hostOption: '호스트',\n        serverPath: '서버 경로',\n        containerDir: '컨테이너 경로',\n        volumeHelper: '저장소 볼륨의 내용이 올바른지 확인하십시오.',\n        networkEmptyHelper: '컨테이너 네트워크 선택이 올바른지 확인해 주세요',\n        modeRW: '읽기/쓰기',\n        modeR: '읽기 전용',\n        sharedLabel: '전파 모드',\n        private: '비공개',\n        privateHelper: '컨테이너와 호스트의 마운트 변경 사항이 서로 영향을 주지 않음',\n        rprivate: '재귀적 비공개',\n        rprivateHelper: '컨테이너 내 모든 마운트가 호스트와 완전히 격리됨',\n        shared: '공유',\n        sharedHelper: '호스트와 컨테이너의 마운트 변경 사항이 서로 보임',\n        rshared: '재귀적 공유',\n        rsharedHelper: '호스트와 컨테이너의 모든 마운트 변경 사항이 서로 보임',\n        slave: '슬레이브',\n        slaveHelper: '컨테이너는 호스트 마운트 변경 사항을 볼 수 있지만, 자신의 변경 사항은 호스트에 영향을 주지 않음',\n        rslave: '재귀적 슬레이브',\n        rslaveHelper: '컨테이너 내 모든 마운트가 호스트 변경 사항을 볼 수 있지만 호스트에 영향을 주지 않음',\n        mode: '모드',\n        env: '환경',\n        restartPolicy: '재시작 정책',\n        always: '항상',\n        unlessStopped: '중지되지 않는 한',\n        onFailure: '실패 시 (기본 5회)',\n        no: '절대',\n        refreshTime: '새로 고침 간격',\n        cache: '캐시',\n        image: '이미지 | 이미지들',\n        imagePull: '풀',\n        imagePullHelper: '여러 이미지 선택 풀링을 지원하며, 각 이미지 입력 후 Enter 키를 눌러 계속합니다',\n        imagePush: '푸시',\n        imagePushHelper:\n            '이 이미지에 여러 태그가 있는 것으로 감지되었습니다. 푸시 시 사용할 이미지 이름이 다음인지 확인하세요: {0}',\n        imageDelete: '이미지 삭제',\n        repoName: '컨테이너 저장소 이름',\n        imageName: '이미지 이름',\n        pull: '풀',\n        path: '경로',\n        importImage: '가져오기',\n        buildArgs: '빌드 인수',\n        imageBuild: '이미지 빌드',\n        pathSelect: '경로',\n        label: '레이블',\n        imageTag: '이미지 태그',\n        imageTagHelper: '여러 이미지 태그 설정을 지원하며, 각 태그 입력 후 Enter 키를 눌러 계속합니다',\n        push: '푸시',\n        fileName: '파일 이름',\n        export: '내보내기',\n        exportImage: '이미지 내보내기',\n        size: '크기',\n        tag: '태그',\n        tagHelper: '한 줄에 하나씩. 예시:\\nkey1=value1\\nkey2=value2',\n        imageNameHelper: '이미지 이름과 태그, 예시: nginx:latest',\n        cleanBuildCache: '빌드 캐시 정리',\n        delBuildCacheHelper:\n            '이 작업은 빌드 중 생성된 모든 캐시된 아티팩트를 삭제하며 되돌릴 수 없습니다. 계속 하시겠습니까?',\n        urlWarning: 'URL 접두어에 http:// 또는 https://를 포함할 필요는 없습니다. 수정해 주세요.',\n        network: '네트워크 | 네트워크들',\n        networkHelper: '이로 인해 일부 애플리케이션과 실행 환경이 제대로 작동하지 않을 수 있습니다. 계속 하시겠습니까?',\n        networkName: '이름',\n        driver: '드라이버',\n        option: '옵션',\n        attachable: '연결 가능',\n        parentNetworkCard: '부모 네트워크 카드',\n        subnet: '서브넷',\n        scope: 'IP 범위',\n        gateway: '게이트웨이',\n        auxAddress: '제외 IP',\n        volume: '볼륨 | 볼륨들',\n        volumeDir: '볼륨 디렉터리',\n        nfsEnable: 'NFS 스토리지 사용',\n        nfsAddress: '주소',\n        mountpoint: '마운트 지점',\n        mountpointNFSHelper: '예: /nfs, /nfs-share',\n        options: '옵션',\n        repo: '레지스트리',\n        httpRepoHelper: 'HTTP 타입 저장소 작업 시 Docker 서비스 재시작이 필요합니다.',\n        httpRepo: 'HTTP 프로토콜을 선택하면 Docker 서비스를 재시작하여 불안정한 레지스트리에 추가해야 합니다.',\n        delInsecure: '신뢰할 수 없는 항목 삭제',\n        delInsecureHelper:\n            '이 작업은 Docker 서비스를 재시작하여 불안정한 레지스트리에서 제거합니다. 계속 하시겠습니까?',\n        downloadUrl: '서버',\n        imageRepo: '이미지 레포지토리',\n        repoHelper: '거울 레포지토리/조직/프로젝트가 포함되어 있습니까?',\n        auth: '인증 필요',\n        mirrorHelper:\n            '거울이 여러 개 있을 경우 각 줄에 하나씩 표시해야 합니다. 예시:\\nhttp://xxxxxx.m.daocloud.io \\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper:\n            '개인 레지스트리가 여러 개 있을 경우 각 줄에 하나씩 표시해야 합니다. 예시:\\n172.16.10.111:8081 \\n172.16.10.112:8081',\n        compose: '컴포즈 | 컴포즈들',\n        composeFile: '컴포즈 파일',\n        fromChangeHelper: '소스를 변경하면 현재 편집한 내용이 삭제됩니다. 계속 하시겠습니까?',\n        composePathHelper: '구성 파일 저장 경로: {0}',\n        composeHelper: '1Panel 에디터나 템플릿을 통해 생성된 컴포지션은 {0}/docker/compose 디렉토리에 저장됩니다.',\n        deleteFile: '파일 삭제',\n        deleteComposeHelper:\n            '이 작업은 컴포즈와 관련된 모든 파일을 삭제합니다. 구성을 포함한 지속적인 파일도 포함됩니다. 신중히 진행해 주세요!',\n        deleteCompose: '\" 이 컴포즈를 삭제하시겠습니까?',\n        composeDirectory: '디렉토리',\n        template: '템플릿',\n        composeTemplate: '컴포즈 템플릿 | 컴포즈 템플릿들',\n        content: '내용',\n        contentEmpty: '컴포즈 내용이 비어 있습니다. 입력 후 다시 시도해 주세요!',\n        containerNumber: '컨테이너 수',\n        containerStatus: '컨테이너 상태',\n        exited: '종료됨',\n        running: '실행 중 ( {0} / {1} )',\n        composeDetailHelper: '이 컴포즈는 1Panel 외부에서 생성되었습니다. 시작 및 중지 작업은 지원되지 않습니다.',\n        composeOperatorHelper: '{1} 작업이 {0}에서 수행됩니다. 계속 하시겠습니까?',\n        composeDownHelper:\n            '이 작업은 {0} 컴포즈 아래의 모든 컨테이너와 네트워크를 중지하고 제거합니다. 계속 하시겠습니까?',\n        composeEnvHelper2:\n            '이 오케스트레이션은 1Panel 앱 스토어에서 생성되었습니다. 설치된 애플리케이션에서 환경 변수를 수정하세요.',\n        composeContentPlaceholder: '여기에 docker-compose 파일 내용을 작성하거나 붙여넣으세요',\n        setting: '설정 | 설정들',\n        restartHelper: '구성 변경 사항을 적용하려면 Docker 서비스를 재시작해야 합니다',\n        operatorStatusHelper: '이 작업은 Docker 서비스를 \"{0}\" 합니다. 계속 하시겠습니까?',\n        dockerStatus: 'Docker 서비스',\n        daemonJsonPathHelper: '구성 경로가 docker.service 에 지정된 경로와 동일한지 확인하십시오.',\n        mirrors: '레지스트리 미러들',\n        mirrorsHelper: '',\n        mirrorsHelper2: '자세한 내용은 공식 문서를 참조하십시오.',\n        registries: '불안정한 레지스트리들',\n        ipv6Helper:\n            'IPv6를 활성화하려면 IPv6 컨테이너 네트워크를 추가해야 합니다. 구체적인 구성 단계를 공식 문서에서 참조하십시오.',\n        ipv6CidrHelper: '컨테이너를 위한 IPv6 주소 풀 범위',\n        ipv6TablesHelper: 'ip6tables 규칙에 대해 Docker IPv6 을 자동 구성합니다.',\n        experimentalHelper: 'ip6tables 를 활성화하려면 이 구성을 켜야 합니다. 그렇지 않으면 ip6tables가 무시됩니다.',\n        cutLog: '로그 옵션',\n        cutLogHelper1: '현재 구성은 새로 생성된 컨테이너에만 영향을 미칩니다.',\n        cutLogHelper2: '기존 컨테이너는 재생성해야 구성이 적용됩니다.',\n        cutLogHelper3:\n            '컨테이너를 재생성하면 데이터 손실이 발생할 수 있습니다. 중요한 데이터가 포함된 컨테이너는 재구성 전 백업을 꼭 해주세요.',\n        maxSize: '최대 크기',\n        maxFile: '최대 파일',\n        liveHelper:\n            '기본적으로 Docker 데몬이 종료되면 실행 중인 컨테이너도 종료됩니다. 데몬이 비활성화된 상태에서 컨테이너를 계속 실행하려면 데몬을 설정할 수 있습니다. 이 기능은 라이브 복구라고 불리며, 데몬 충돌, 예정된 중단 또는 업그레이드로 인한 컨테이너 다운타임을 줄이는 데 도움을 줍니다.',\n        liveWithSwarmHelper: 'live-restore 데몬 구성은 스웜 모드와 호환되지 않습니다.',\n        iptablesDisable: 'iptables 비활성화',\n        iptablesHelper1: 'Docker 에 대한 iptables 규칙을 자동으로 구성합니다.',\n        iptablesHelper2: 'iptables 를 비활성화하면 컨테이너가 외부 네트워크와 통신할 수 없습니다.',\n        daemonJsonPath: '구성 경로',\n        serviceUnavailable: '현재 Docker 서비스가 시작되지 않았습니다.',\n        startIn: '시작하려면',\n        sockPath: '유닉스 도메인 소켓',\n        sockPathHelper: 'Docker 데몬과 클라이언트 간의 통신 채널입니다.',\n        sockPathHelper1: '기본 경로: /var/run/docker-x.sock',\n        sockPathMsg: '소켓 경로 설정을 저장하면 Docker 서비스가 사용 불가능할 수 있습니다. 계속 하시겠습니까?',\n        sockPathErr: '올바른 Docker 소켓 파일 경로를 선택하거나 입력해 주세요.',\n        related: '관련',\n        includeAppstore: '앱 스토어에서 컨테이너 표시',\n        excludeAppstore: '앱스토어 컨테이너 숨기기',\n        cleanDockerDiskZone: 'Docker 에서 사용하는 디스크 공간 정리',\n        cleanImagesHelper: '(사용되지 않는 모든 이미지를 정리합니다.)',\n        cleanContainersHelper: '(정지된 모든 컨테이너를 정리합니다.)',\n        cleanVolumesHelper: '(사용되지 않는 모든 로컬 볼륨을 정리합니다.)',\n        makeImage: '이미지 생성',\n        newImageName: '새 이미지 이름',\n        commitMessage: '커밋 메시지',\n        author: '작성자',\n        ifPause: '생성 중 컨테이너 일시 정지',\n        ifMakeImageWithContainer: '이 컨테이너에서 새 이미지를 생성하시겠습니까?',\n        finishTime: '마지막 중지 시간',\n        workingDir: '작업 디렉토리',\n        resource: '리소스',\n        macAddr: 'MAC 주소',\n        exportHelper: '선택한 이미지를 하나의 tar 파일로 내보냅니다.',\n        goSetting: '편집으로 이동',\n    },\n    cronjob: {\n        importHelper:\n            '가져오기 시 동일한 이름의 예약 작업은 자동으로 건너뜁니다. 작업은 기본적으로 【비활성화】 상태로 설정되며, 데이터 연동 이상 시 【편집 대기】 상태로 설정됩니다.',\n        changeStatus: '상태 변경',\n        disableMsg: '이 작업은 예약된 작업이 자동으로 실행되지 않도록 멈춥니다. 계속하시겠습니까?',\n        enableMsg: '이 작업은 예약된 작업이 자동으로 실행되도록 허용합니다. 계속하시겠습니까?',\n        taskType: '작업 유형',\n        record: '레코드',\n        viewRecords: '레코드 보기',\n        shell: '셸',\n        stop: '수동 중지',\n        stopHelper: '이 작업은 현재 작업 실행을 강제로 중지합니다. 계속하시겠습니까?',\n        log: '백업 로그',\n        logHelper: '시스템 백업 로그',\n        ogHelper1: '1. 1Panel 시스템 로그',\n        logHelper2: '2. 서버 SSH 로그인 로그',\n        logHelper3: '3. 모든 사이트 로그',\n        containerCheckBox: '컨테이너 내 (컨테이너 명령어 입력 불필요)',\n        containerName: '컨테이너 이름',\n        ntp: '시간 동기화',\n        ntp_helper: 'Toolbox 의 빠른 설정 페이지에서 NTP 서버를 구성할 수 있습니다.',\n        app: '백업 앱',\n        website: '백업 웹사이트',\n        rulesHelper: '여러 개의 제외 규칙 지원, 영어 쉼표 , 로 구분. 예: *.log,*.sql',\n        lastRecordTime: '마지막 실행 시간',\n        all: '전체',\n        failedRecord: '실패한 레코드',\n        successRecord: '성공한 레코드',\n        database: '백업 데이터베이스',\n        backupArgs: '백업 인수',\n        backupArgsHelper:\n            '목록에 없는 백업 인수는 수동으로 입력하여 선택할 수 있습니다. 예: --no-data를 입력하고 드롭다운 목록의 첫 번째 옵션을 선택하세요.',\n        singleTransaction: '단일 트랜잭션을 사용하여 InnoDB 테이블을 백업하며, 대용량 데이터 백업에 적합합니다',\n        quick: '전체 테이블을 메모리에 로드하는 대신 데이터를 행별로 읽습니다. 대용량 데이터 및 저메모리 시스템 백업에 적합합니다',\n        skipLockTables: '모든 테이블을 잠그지 않고 백업합니다. 높은 동시성 데이터베이스에 적합합니다',\n        missBackupAccount: '백업 계정을 찾을 수 없습니다',\n        syncDate: '동기화 시간',\n        clean: '캐시 정리',\n        curl: '접속 URL',\n        taskName: '작업 이름',\n        cronSpec: '트리거 주기',\n        cronSpecDoc:\n            '사용자 정의 실행 주기는 [분 시 일 월 요일] 형식만 지원합니다 (예: 0 0 * * *). 자세한 내용은 공식 문서를 참조하세요.',\n        cronSpecHelper: '올바른 실행 주기를 입력해 주세요',\n        cleanHelper: '이 작업은 모든 작업 실행 레코드, 백업 파일, 로그 파일을 기록합니다. 계속하시겠습니까?',\n        directory: '백업 디렉토리',\n        sourceDir: '백업 디렉토리',\n        snapshot: '시스템 스냅샷',\n        allOptionHelper:\n            '현재 작업 계획은 모든 [{0}]을 백업하는 것입니다. 현재 직접 다운로드는 지원되지 않습니다. [{0}] 메뉴에서 백업 목록을 확인하실 수 있습니다.',\n        exclusionRules: '배제 규칙',\n        exclusionRulesHelper:\n            '제외 규칙을 선택하거나 입력하고, 각 세트 입력 후 Enter 키를 눌러 계속합니다. 제외 규칙은 이 백업의 모든 압축 작업에 적용됩니다',\n        default_download_path: '기본 다운로드 링크',\n        saveLocal: '로컬 백업 보관 (클라우드 저장소 복사본 수와 동일)',\n        url: 'URL 주소',\n        urlHelper: '올바른 URL 주소를 입력해 주세요',\n        targetHelper: '백업 계정은 패널 설정에서 관리됩니다.',\n        withImageHelper: '앱 스토어 이미지를 백업하지만 스냅샷 파일 크기가 증가합니다.',\n        ignoreApp: '앱 제외',\n        withImage: '애플리케이션 이미지 백업',\n        retainCopies: '기록 보관',\n        retryTimes: '재시도 횟수',\n        timeout: '타임아웃',\n        ignoreErr: '오류 무시',\n        ignoreErrHelper: '백업 과정에서 발생하는 오류를 무시하여 모든 백업 작업이 실행되도록 합니다',\n        retryTimesHelper: '0은 실패 후 재시도 안 함을 의미합니다',\n        retainCopiesHelper: '실행 기록과 로그에 대해 보관할 복사본 수',\n        retainCopiesHelper1: '백업 파일에 대해 보관할 복사본 수',\n        retainCopiesUnit: '개 (보기)',\n        cronSpecRule: '라인 {0}의 실행 주기 형식이 잘못되었습니다. 확인 후 다시 시도해 주세요!',\n        perMonthHelper: '매월 {0}일 {1}:{2}에 실행',\n        perWeekHelper: '매주 {0}일 {1}:{2}에 실행',\n        perDayHelper: '매일 {0}:{1}에 실행',\n        perHourHelper: '매시간 {0}분에 실행',\n        perNDayHelper: '매 {0}일마다 {1}:{2}에 실행',\n        perNHourHelper: '매 {0}시간마다 {1}에 실행',\n        perNMinuteHelper: '매 {0}분마다 실행',\n        perNSecondHelper: '매 {0}초마다 실행',\n        perMonth: '매월',\n        perWeek: '매주',\n        perHour: '매시간',\n        perNDay: '매 N일',\n        perDay: '매일',\n        perNHour: '매 N시간',\n        perNMinute: '매 N분',\n        perNSecond: '매 N초',\n        day: '일',\n        dayUnit: 'd',\n        monday: '월요일',\n        tuesday: '화요일',\n        wednesday: '수요일',\n        thursday: '목요일',\n        friday: '금요일',\n        saturday: '토요일',\n        sunday: '일요일',\n        shellContent: '스크립트',\n        errRecord: '잘못된 로깅',\n        errHandle: '크론 작업 실행 실패',\n        noRecord: '크론 작업을 트리거하고 나면 여기에 레코드가 표시됩니다.',\n        cleanData: '데이터 정리',\n        cleanRemoteData: '원격 데이터 삭제',\n        cleanDataHelper: '이 작업에서 생성된 백업 파일을 삭제합니다.',\n        noLogs: '작업 출력이 아직 없습니다...',\n        errPath: '백업 경로 [{0}] 오류, 다운로드할 수 없습니다!',\n        cutWebsiteLog: '웹사이트 로그 회전',\n        cutWebsiteLogHelper: '회전된 로그 파일은 1Panel 의 백업 디렉토리로 백업됩니다.',\n        syncIpGroup: 'WAF IP 그룹 동기화',\n        requestExpirationTime: '업로드 요청 만료 시간(시간)',\n        unitHours: '단위: 시간',\n        alertTitle: '예정된 작업 - {0} 「{1}」 작업 실패 경고',\n        library: {\n            script: '스크립트',\n            syncNow: '지금 동기화',\n            turnOnSync: '자동 동기화 켜기',\n            turnOnSyncHelper: '자동 동기화를 켜면 매일 새벽 시간에 자동 동기화가 수행됩니다',\n            turnOffSync: '자동 동기화 끄기',\n            turnOffSyncHelper: '자동 동기화를 끄면 스크립트 동기화가 지연될 수 있습니다. 확인하시겠습니까?',\n            isInteractive: '대화형',\n            interactive: '대화형 스크립트',\n            interactiveHelper: '실행 중 사용자 입력이 필요하며 예약 작업에서는 사용할 수 없습니다.',\n            library: '스크립트 라이브러리',\n            remoteLibrary: 'リモートスクリプトライブラリ',\n            create: '스크립트 추가',\n            edit: '스크립트 수정',\n            groupHelper:\n                '스크립트 특성에 따라 다양한 그룹을 설정하여 스크립트 필터링 작업을 더 빠르게 수행할 수 있습니다.',\n            handleHelper: '{0} 에서 {1} 스크립트를 실행합니다. 계속하시겠습니까?',\n            noSuchApp: '{0} 서비스가 감지되지 않았습니다. 스크립트 라이브러리를 사용하여 먼저 빠르게 설치하세요!',\n            syncHelper:\n                '시스템 스크립트 라이브러리를 동기화합니다. 이 작업은 시스템 스크립트에만 적용됩니다. 계속하시겠습니까?',\n        },\n        nextTime: '다음 5회 실행',\n        logHelper1: '1. 1Panel 시스템 로그',\n        backupContent: '백업 내용',\n        cronSpecRule2: '실행 주기 형식이 올바르지 않습니다. 확인 후 다시 시도하세요.',\n        executor: '실행자',\n        cleanLog: '로그 자동 정리',\n        cleanLogscope: '로그 보관 기간',\n    },\n    monitor: {\n        globalFilter: '전역 필터',\n        enableMonitor: '모니터링 상태',\n        storeDays: '보관 일수',\n        defaultNetwork: '기본 네트워크 카드',\n        defaultNetworkHelper: '모니터링 및 개요 인터페이스에 표시되는 기본 네트워크 카드 옵션',\n        defaultIO: '기본 디스크',\n        defaultIOHelper: '모니터링 및 개요 인터페이스에 표시되는 기본 디스크 옵션',\n        cleanMonitor: '모니터링 기록 지우기',\n        cleanHelper: '이 작업은 GPU를 포함한 모든 모니터링 기록을 지웁니다. 계속하시겠습니까?',\n        avgLoad: '평균 부하',\n        loadDetail: '부하 세부사항',\n        resourceUsage: '자원 사용률',\n        networkCard: '네트워크 인터페이스',\n        read: '읽기',\n        write: '쓰기',\n        readWriteCount: 'I/O 작업',\n        readWriteTime: 'I/O 지연 시간',\n        today: '오늘',\n        yesterday: '어제',\n        lastNDay: '최근 {0}일',\n        lastNMonth: '최근 {0}개월',\n        lastHalfYear: '최근 반년',\n        memory: '메모리',\n        percent: '비율',\n        cache: '캐시',\n        disk: '디스크',\n        network: '네트워크',\n        up: '업',\n        down: '다운',\n        interval: '수집 간격',\n        intervalHelper: '적절한 모니터링 수집 간격을 입력하세요 (10초 - 12시간)',\n    },\n    terminal: {\n        local: '로컬',\n        defaultConn: '기본 연결',\n        defaultConnHelper: '이 작업은 【{0}】의 터미널을 연 후 자동으로 노드 터미널에 연결됩니다. 계속하시겠습니까?',\n        withReset: '연결 정보 재설정',\n        localConnJump: '기본 연결 정보는 [터미널 - 설정]에서 관리됩니다. 연결 실패 시 해당 위치에서 편집하세요!',\n        localHelper: '로컬 이름은 시스템 로컬 식별에만 사용됩니다.',\n        connLocalErr: '자동 인증에 실패했습니다. 로컬 서버 로그인 정보를 입력해주세요.',\n        testConn: '연결 테스트',\n        saveAndConn: '저장 후 연결',\n        connTestOk: '연결 정보가 유효합니다.',\n        connTestFailed: '연결할 수 없습니다. 연결 정보를 확인해주세요.',\n        host: '호스트 | 호스트들',\n        createConn: '새 연결',\n        manageGroup: '그룹 관리',\n        noHost: '호스트 없음',\n        groupChange: '그룹 변경',\n        expand: '모두 확장',\n        fold: '모두 축소',\n        batchInput: '배치 처리',\n        quickCommand: '빠른 명령 | 빠른 명령들',\n        noSuchCommand: '가져온 CSV 파일에서 빠른 명령어 데이터를 찾을 수 없습니다. 확인 후 다시 시도하세요!',\n        quickCommandHelper: '\"터미널 -> 터미널\" 하단에서 빠른 명령을 사용할 수 있습니다.',\n        groupDeleteHelper: '그룹을 제거하면 해당 그룹의 모든 연결이 기본 그룹으로 이동됩니다. 계속하시겠습니까?',\n        command: '명령',\n        quickCmd: '빠른 명령',\n        addHost: '추가',\n        localhost: '로컬호스트',\n        ip: '주소',\n        authMode: '인증 방식',\n        passwordMode: '비밀번호',\n        rememberPassword: '인증 정보 기억하기',\n        keyMode: '개인 키',\n        key: '개인 키',\n        keyPassword: '개인 키 비밀번호',\n        emptyTerminal: '현재 연결된 터미널이 없습니다.',\n        lineHeight: '줄 높이',\n        letterSpacing: '자간',\n        fontSize: '글꼴 크기',\n        fontFamily: '글꼴 세트',\n        fontFamilySupportHelper:\n            '글꼴을 선택하거나 직접 입력할 수 있습니다. 적용 여부는 브라우저가 실행 중인 시스템에 해당 글꼴이 설치되어 있는지에 따라 달라지며, 미설치 시 자동으로 폴백됩니다. 비어 있으면 기본 글꼴을 사용합니다.',\n        backgroundColor: '배경색',\n        foregroundColor: '전경색',\n        cursorBlink: '커서 깜박임',\n        cursorStyle: '커서 스타일',\n        cursorUnderline: '밑줄',\n        cursorBlock: '블록',\n        cursorBar: '막대',\n        scrollback: '스크롤백',\n        scrollSensitivity: '스크롤 감도',\n        aiStatus: 'AI Terminal',\n        aiSettings: 'AI Terminal Settings',\n        aiAccountHelper:\n            '선택한 모델 계정으로 명령을 생성하고 채웁니다. Ollama, vLLM 같은 로컬 모델은 사용자 지정 모델 계정을 사용하세요.',\n        aiPrefix: 'Trigger Prefix',\n        aiPrefixHelper:\n            'When a line starts with this prefix and you press Enter, AI command generation will be triggered, for example # or //ai.',\n        aiRiskCommands: 'Risk Command Interception',\n        aiRiskCommandsHelper:\n            'Generated commands matching any of these fragments will be blocked and filled back as comments. Supports add, edit, and delete.',\n        aiAddRiskCommand: 'Add Risk Command',\n        aiRemoveRiskCommand: 'Delete',\n        aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',\n        aiPrefixAsciiVisible:\n            'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',\n        saveHelper: '현재 터미널 설정을 저장하시겠습니까?',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: '일부 설치 및 사용 문제는 다음을 참고하세요',\n        },\n        swap: {\n            swap: '스왑 파티션',\n            swapHelper1: '스왑 크기는 물리적 메모리의 1~2배로 설정해야 하며, 특정 요구 사항에 따라 조정 가능합니다.',\n            swapHelper2:\n                '스왑 파일을 생성하기 전에 시스템 디스크에 충분한 가용 공간이 있는지 확인하세요. 스왑 파일 크기만큼 디스크 공간이 점유됩니다.',\n            swapHelper3:\n                '스왑은 메모리 압력을 완화하는 데 도움이 될 수 있지만, 단지 대안일 뿐입니다. 스왑에 과도하게 의존하면 시스템 성능이 저하될 수 있으므로 메모리를 추가하거나 애플리케이션 메모리 사용을 최적화하는 것을 우선적으로 고려해야 합니다.',\n            swapHelper4: '스왑 사용량을 정기적으로 모니터링하여 시스템이 정상적으로 작동하는지 확인하는 것이 좋습니다.',\n            swapDeleteHelper:\n                '이 작업은 스왑 파티션 {0}을 제거합니다. 시스템 보안상의 이유로 해당 파일은 자동으로 삭제되지 않습니다. 삭제가 필요한 경우 수동으로 진행하세요!',\n            saveHelper: '현재 설정을 먼저 저장해주세요!',\n            saveSwap: '현재 구성을 저장하면 스왑 파티션 {0}의 크기가 {1}(으)로 조정됩니다. 계속하시겠습니까?',\n            swapMin: '최소 파티션 크기는 40 KB입니다. 수정 후 다시 시도해주세요!',\n            swapMax: '파티션 크기의 최대값은 {0}입니다. 수정 후 다시 시도해주세요!',\n            swapOff: '최소 파티션 크기는 40 KB입니다. 0으로 설정하면 스왑 파티션이 비활성화됩니다.',\n        },\n        device: {\n            dnsHelper: 'DNS 서버',\n            dnsAlert: '/etc/resolv.conf 파일의 구성을 수정하면 시스템 재부팅 후 파일이 기본값으로 복원됩니다.',\n            dnsHelper1: 'DNS 항목이 여러 개인 경우 각 항목을 새 줄에 표시해야 합니다. 예:\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: '호스트 이름 해석',\n            hosts: '도메인',\n            hostAlert: '주석 처리된 기록은 숨겨져 있습니다. 모든 구성 버튼을 클릭하여 보거나 설정하세요.',\n            toolbox: '빠른 설정',\n            hostname: '호스트 이름',\n            passwd: '시스템 비밀번호',\n            passwdHelper: '입력 문자는 $ 및 &를 포함할 수 없습니다.',\n            timeZone: '시간대',\n            localTime: '서버 시간',\n            timeZoneChangeHelper: '시스템 시간대를 변경하려면 서비스를 재시작해야 합니다. 계속하시겠습니까?',\n            timeZoneHelper:\n                '\"timedatectl\" 명령이 설치되지 않은 경우 시간대를 변경할 수 없습니다. 시스템은 시간대 변경에 이 명령을 사용합니다.',\n            timeZoneCN: '베이징',\n            timeZoneAM: '로스앤젤레스',\n            timeZoneNY: '뉴욕',\n            ntpALi: '알리바바',\n            ntpGoogle: '구글',\n            syncSite: 'NTP 서버',\n            hostnameHelper:\n                '호스트 이름 수정은 \"hostnamectl\" 명령에 따라 달라집니다. 명령이 설치되지 않은 경우 수정이 실패할 수 있습니다.',\n            userHelper:\n                '사용자 이름은 \"whoami\" 명령을 사용하여 검색됩니다. 명령이 설치되지 않은 경우 검색이 실패할 수 있습니다.',\n            passwordHelper:\n                '비밀번호 수정은 \"chpasswd\" 명령에 따라 달라집니다. 명령이 설치되지 않은 경우 수정이 실패할 수 있습니다.',\n            hostHelper: '제공된 내용에 빈 값이 포함되어 있습니다. 확인 후 수정하여 다시 시도해주세요!',\n            dnsCheck: '가용성 테스트',\n            dnsOK: 'DNS 구성 정보가 유효합니다!',\n            dnsTestFailed: 'DNS 구성 정보가 유효하지 않습니다.',\n            syncSiteHelper: '{0}을(를) 기준으로 시스템 시간을 동기화하시겠습니까?',\n        },\n        fail2ban: {\n            sshPort: 'SSH 포트 청취',\n            sshPortHelper: '현재 Fail2ban 은 호스트의 SSH 연결 포트를 청취합니다.',\n            unActive: '현재 Fail2ban 서비스가 활성화되어 있지 않습니다.',\n            operation: 'Fail2ban 서비스에서 \"{0}\" 작업을 수행합니다. 계속하시겠습니까?',\n            fail2banChange: 'Fail2ban 구성 수정',\n            ignoreHelper: '허용 목록에 있는 IP는 차단되지 않습니다. 계속하시겠습니까?',\n            bannedHelper: '차단 목록에 있는 IP는 서버에 의해 차단됩니다. 계속하시겠습니까?',\n            maxRetry: '최대 재시도 횟수',\n            banTime: '차단 시간',\n            banTimeHelper: '기본 차단 시간은 10분이며, -1은 영구 차단을 나타냅니다.',\n            banTimeRule: '유효한 차단 시간 또는 -1을 입력하세요.',\n            banAllTime: '영구 차단',\n            findTime: '탐지 기간',\n            banAction: '차단 작업',\n            banActionOption: '{0}을(를) 사용하여 지정된 IP 주소 차단',\n            allPorts: ' (모든 포트)',\n            ignoreIP: 'IP 허용 목록',\n            bannedIP: 'IP 차단 목록',\n            logPath: '로그 경로',\n            logPathHelper: '기본값은 /var/log/secure 또는 /var/log/auth.log입니다.',\n        },\n        ftp: {\n            ftp: 'FTP 계정 | FTP 계정들',\n            notStart: 'FTP 서비스가 현재 실행 중이 아닙니다. 먼저 시작하세요!',\n            operation: 'FTP 서비스에서 \"{0}\" 작업을 수행합니다. 계속하시겠습니까?',\n            noPasswdMsg: '현재 FTP 계정의 비밀번호를 가져올 수 없습니다. 비밀번호를 설정한 후 다시 시도하세요!',\n            enableHelper: '선택한 FTP 계정을 활성화하면 접근 권한이 복원됩니다. 계속하시겠습니까?',\n            disableHelper: '선택한 FTP 계정을 비활성화하면 접근 권한이 취소됩니다. 계속하시겠습니까?',\n            syncHelper: '서버와 데이터베이스 간의 FTP 계정 데이터를 동기화합니다. 계속하시겠습니까?',\n            dirSystem:\n                '이 디렉터리는 시스템 예약 디렉터리입니다. 수정 시 시스템 충돌이 발생할 수 있으니 수정 후 다시 시도하세요!',\n            dirHelper: 'FTP 활성화를 위해 디렉터리 권한 변경이 필요합니다. 신중하게 선택하세요',\n            dirMsg: 'FTP 활성화 시 {0} 디렉터리 전체의 권한이 변경됩니다. 계속하시겠습니까?',\n        },\n        clam: {\n            clam: '바이러스 검사',\n            cron: '예약 스캔',\n            cronHelper: '전문 버전에서 예약 스캔 기능을 지원합니다.',\n            specErr: '실행 일정 형식 오류입니다. 확인 후 다시 시도하세요!',\n            disableMsg: '예약 실행을 중지하면 이 스캔 작업이 자동으로 실행되지 않습니다. 계속하시겠습니까?',\n            enableMsg: '예약 실행을 활성화하면 이 스캔 작업이 정기적으로 자동 실행됩니다. 계속하시겠습니까?',\n            showFresh: '서명 업데이트 서비스 표시',\n            hideFresh: '서명 업데이트 서비스 숨기기',\n            clamHelper:\n                'ClamAV의 최소 권장 구성은 다음과 같습니다: RAM 3 GiB 이상, 2.0 GHz 이상의 단일 코어 CPU, 최소 5 GiB의 사용 가능한 하드 디스크 공간.',\n            notStart: 'ClamAV 서비스가 현재 실행 중이 아닙니다. 먼저 시작하세요!',\n            removeRecord: '보고서 파일 삭제',\n            noRecords: '\"Trigger\" 버튼을 클릭하여 스캔을 시작하면 이곳에서 기록을 확인할 수 있습니다.',\n            removeInfected: '바이러스 파일 삭제',\n            removeInfectedHelper: '작업 중 감지된 바이러스 파일을 삭제하여 서버 보안 및 정상 작동을 보장합니다.',\n            clamCreate: '스캔 규칙 생성',\n            infectedStrategy: '감염 파일 처리 전략',\n            removeHelper: '바이러스 파일을 삭제합니다. 신중히 선택하세요!',\n            move: '이동',\n            moveHelper: '바이러스 파일을 지정된 디렉토리로 이동합니다.',\n            copyHelper: '바이러스 파일을 지정된 디렉토리로 복사합니다.',\n            none: '조치 안 함',\n            noneHelper: '바이러스 파일에 대해 아무 조치도 취하지 않습니다.',\n            scanDir: '스캔 디렉토리',\n            infectedDir: '감염 파일 디렉토리',\n            scanDate: '스캔 날짜',\n            scanResult: '스캔 로그 출력',\n            tail: '라인',\n            infectedFiles: '감염된 파일',\n            log: '상세 내용',\n            clamConf: 'Clam AV 데몬',\n            clamLog: '@:toolbox.clam.clamConf 로그',\n            freshClam: 'FreshClam',\n            freshClamLog: '@:toolbox.clam.freshClam 로그',\n            alertHelper: '전문 버전에서 예약 스캔 및 SMS 알림을 지원합니다.',\n            alertTitle: '바이러스 스캔 작업 「{0}」에서 감염된 파일을 발견했습니다.',\n        },\n    },\n    logs: {\n        core: '패널 서비스',\n        agent: '노드 모니터링',\n        panelLog: '패널 로그',\n        operation: '작업 로그',\n        login: '로그인 로그',\n        loginIP: '로그인 IP',\n        loginAddress: '로그인 주소',\n        loginAgent: '로그인 에이전트',\n        loginStatus: '상태',\n        system: '시스템 로그',\n        deleteLogs: '로그 정리',\n        resource: '자원',\n        detail: {\n            dashboard: '개요',\n            ai: 'AI',\n            groups: '그룹',\n            hosts: '호스트',\n            apps: '애플리케이션',\n            openresty: 'OpenResty',\n            websites: '웹사이트',\n            containers: '컨테이너',\n            files: '파일 관리',\n            runtimes: '실행 환경',\n            process: '프로세스 관리',\n            toolbox: '도구 상자',\n            backups: '백업 / 복원',\n            tampers: '변조 방지',\n            xsetting: '인터페이스 설정',\n            logs: '로그 감사',\n            settings: '패널 설정',\n            cronjobs: '예약 작업',\n            waf: 'WAF',\n            databases: '데이터베이스',\n            licenses: '라이선스',\n            nodes: '노드',\n            commands: '빠른 명령',\n        },\n        websiteLog: '웹사이트 로그',\n        runLog: '실행 로그',\n        errLog: '에러 로그',\n        task: '작업 로그',\n        taskName: '작업 이름',\n        taskRunning: '실행 중',\n    },\n    file: {\n        fileDirNum: '총 {0}개 디렉터리, {1}개 파일,',\n        currentDir: '현재 디렉터리',\n        dir: '폴더',\n        upload: '업로드',\n        uploadFile: '@:file.upload @.lower:file.file',\n        uploadDirectory: '@:file.upload @.lower:file.dir',\n        download: '다운로드',\n        fileName: '파일 이름',\n        search: '검색',\n        mode: '권한',\n        editPermissions: '@:file.mode',\n        owner: '소유자',\n        file: '파일',\n        remoteFile: '원격에서 다운로드',\n        share: '공유',\n        sync: '데이터 동기화',\n        size: '크기',\n        updateTime: '수정됨',\n        rename: '이름 바꾸기',\n        role: '권한',\n        info: '속성',\n        linkFile: '소프트 링크',\n        batchoperation: '일괄 작업',\n        shareList: '공유 목록',\n        zip: '압축됨',\n        group: '그룹',\n        path: '경로',\n        public: '기타',\n        setRole: '권한 설정',\n        link: '파일 링크',\n        rRole: '읽기',\n        wRole: '쓰기',\n        xRole: '실행 가능',\n        name: '이름',\n        compress: '압축',\n        deCompress: '압축 해제',\n        compressType: '압축 형식',\n        compressDst: '압축 경로',\n        replace: '기존 파일 덮어쓰기',\n        compressSuccess: '압축 성공',\n        deCompressSuccess: '압축 해제 성공',\n        deCompressDst: '압축 해제 경로',\n        linkType: '링크 유형',\n        softLink: '소프트 링크',\n        hardLink: '하드 링크',\n        linkPath: '링크 경로',\n        selectFile: '파일 선택',\n        downloadUrl: '원격 URL',\n        downloadStart: '다운로드 시작됨',\n        moveSuccess: '이동 성공',\n        copySuccess: '복사 성공',\n        pasteMsg: '대상 디렉토리의 오른쪽 상단에 있는 [붙여넣기] 버튼을 클릭하세요',\n        move: '이동',\n        calculate: '계산',\n        remark: '비고',\n        setRemark: '비고 설정',\n        remarkPrompt: '비고를 입력하세요',\n        remarkPlaceholder: '비고',\n        remarkToggle: '비고',\n        remarkToggleTip: '파일 비고 로드',\n        canNotDeCompress: '이 파일은 압축 해제할 수 없습니다',\n        uploadSuccess: '업로드 성공',\n        downloadProcess: '다운로드 진행률',\n        downloading: '다운로드 중...',\n        infoDetail: '파일 속성',\n        root: '루트 디렉터리',\n        list: '파일 목록',\n        sub: '하위 폴더',\n        downloadSuccess: '다운로드 성공',\n        theme: '테마',\n        language: '언어',\n        eol: '줄 끝',\n        copyDir: '복사',\n        paste: '붙여넣기',\n        changeOwner: '사용자 및 그룹 수정',\n        containSub: '권한 변경을 하위 폴더에 적용',\n        ownerHelper:\n            'PHP 운영 환경의 기본 사용자: 사용자 그룹은 1000:1000 입니다. 컨테이너 내부 및 외부에서 표시되는 불일치는 정상입니다.',\n        searchHelper: '* 등의 와일드카드를 지원합니다',\n        uploadFailed: '[{0}] 파일 업로드 실패',\n        fileUploadStart: '[{0}] 업로드 중...',\n        currentSelect: '현재 선택: ',\n        unsupportedType: '지원되지 않는 파일 유형',\n        deleteHelper: '다음 파일을 삭제하시겠습니까? 기본적으로 삭제 후 휴지통으로 이동합니다.',\n        fileHelper: '참고:\\n1. 검색 결과는 정렬할 수 없습니다.\\n2. 폴더는 크기로 정렬할 수 없습니다.',\n        forceDeleteHelper: '파일을 영구적으로 삭제합니다(휴지통으로 이동하지 않고 바로 삭제).',\n        recycleBin: '휴지통',\n        sourcePath: '원래 경로',\n        deleteTime: '삭제 시간',\n        confirmReduce: '다음 파일을 복원하시겠습니까?',\n        reduceSuccess: '복원 성공',\n        reduce: '복원',\n        reduceHelper: '원래 경로에 동일한 이름의 파일이나 디렉터리가 있으면 덮어씁니다. 계속하시겠습니까?',\n        clearRecycleBin: '정리',\n        clearRecycleBinHelper: '휴지통을 정리하시겠습니까?',\n        favorite: '즐겨찾기',\n        removeFavorite: '즐겨찾기에서 제거하시겠습니까?',\n        addFavorite: '즐겨찾기 추가 / 제거',\n        clearList: '목록 정리',\n        deleteRecycleHelper: '다음 파일을 영구적으로 삭제하시겠습니까?',\n        typeErrOrEmpty: '[{0}] 파일 유형이 잘못되었거나 빈 폴더입니다.',\n        dropHelper: '업로드하려는 파일을 여기에 드래그하세요',\n        fileRecycleBin: '휴지통 활성화',\n        fileRecycleBinMsg: '{0} 휴지통',\n        wordWrap: '자동 줄바꿈',\n        deleteHelper2: '선택한 파일을 삭제하시겠습니까? 삭제 작업은 되돌릴 수 없습니다.',\n        ignoreCertificate: '안전하지 않은 서버 연결 허용',\n        ignoreCertificateHelper:\n            '안전하지 않은 서버 연결을 허용하면 데이터 유출 또는 변조가 발생할 수 있습니다. 이 옵션은 다운로드 소스를 신뢰할 때만 사용하세요.',\n        uploadOverLimit: '파일 수가 1000 개를 초과했습니다! 압축하여 업로드하세요.',\n        clashDidNotSupport: '파일 이름에 .1panel_clash 를 포함할 수 없습니다.',\n        clashDeleteAlert: '\"휴지통\" 폴더는 삭제할 수 없습니다.',\n        clashOpenAlert: '휴지통 디렉터리를 열려면 \"휴지통\" 버튼을 클릭하세요.',\n        right: '앞으로',\n        back: '뒤로',\n        top: '처음으로 돌아가기',\n        up: '뒤로가기',\n        openWithVscode: 'VS Code 로 열기',\n        vscodeHelper: '로컬에 VS Code 가 설치되어 있고 SSH Remote 플러그인이 구성되어 있는지 확인하세요.',\n        saveContentAndClose: '파일이 수정되었습니다. 저장 후 닫으시겠습니까?',\n        saveAndOpenNewFile: '파일이 수정되었습니다. 저장 후 새 파일을 열겠습니까?',\n        noEdit: '파일이 수정되지 않았습니다. 이 작업은 필요하지 않습니다!',\n        noNameFolder: '제목 없는 폴더',\n        noNameFile: '제목 없는 파일',\n        minimap: '코드 미니맵',\n        fileCanNotRead: '파일을 읽을 수 없습니다.',\n        previewTruncated: '파일이 너무 커서 마지막 부분만 표시됩니다',\n        previewEmpty: '파일이 비어 있거나 텍스트 파일이 아닙니다',\n        previewLargeFile: '미리보기',\n        panelInstallDir: '1Panel 설치 디렉터리는 삭제할 수 없습니다.',\n        wgetTask: '다운로드 작업',\n        existFileTitle: '동일한 이름의 파일 경고',\n        existFileHelper: '업로드한 파일에 동일한 이름의 파일이 포함되어 있습니다. 덮어쓰시겠습니까?',\n        existFileSize: '파일 크기 (새로운 -> 오래된)',\n        existFileDirHelper: '선택한 파일/폴더에 동일한 이름이 이미 존재합니다. 신중하게 작업하세요!',\n        coverDirHelper: '덮어쓸 폴더를 선택하면 대상 경로로 복사됩니다!',\n        noSuchFile: '파일 또는 디렉터리를 찾을 수 없습니다. 확인 후 다시 시도하세요.',\n        setting: '설정',\n        showHide: '숨김 파일 표시',\n        noShowHide: '숨김 파일 숨기기',\n        cancelUpload: '업로드 취소',\n        cancelUploadHelper: '업로드를 취소할지 여부, 취소 후 업로드 목록이 비워집니다.',\n        keepOneTab: '최소한 하나의 탭을 유지하세요',\n        notCanTab: '더 이상 탭을 추가할 수 없습니다',\n        convert: '파일 형식 변환',\n        converting: '로 변환 중',\n        fileCanNotConvert: '이 파일은 형식 변환을 지원하지 않습니다',\n        formatType: '형식 종류',\n        sourceFormat: '원본 형식',\n        sourceFile: '원본 파일',\n        saveDir: '저장 디렉토리',\n        deleteSourceFile: '원본 파일 삭제 여부',\n        convertHelper: '선택한 파일을 다른 형식으로 변환합니다',\n        convertHelper1: '변환할 파일을 선택하세요',\n        execConvert: '변환을 시작합니다. 작업 센터에서 변환 로그를 확인할 수 있습니다',\n        convertLogs: '변환 로그',\n        formatConvert: '형식 변환',\n    },\n    ssh: {\n        autoStart: '자동 시작',\n        enable: '자동 시작 활성화',\n        disable: '자동 시작 비활성화',\n        sshAlert:\n            '목록 데이터는 로그인 날짜를 기준으로 정렬됩니다. 시간대 변경이나 다른 작업이 수행되면 로그인 로그의 날짜에 차이가 발생할 수 있습니다.',\n        sshAlert2:\n            '\"Fail2ban\"을 \"도구 상자\"에서 사용하여 무차별 대입 공격을 시도하는 IP 주소를 차단할 수 있으며, 이는 호스트의 보안을 강화하는 데 도움이 됩니다.',\n        sshOperate: 'SSH 서비스에서 \"{0}\" 작업을 수행합니다. 계속하시겠습니까?',\n        sshChange: 'SSH 설정',\n        sshChangeHelper: '\"{0}\"을(를) \"{1}\"로 변경하였습니다. 계속하시겠습니까?',\n        sshFileChangeHelper:\n            '구성 파일을 수정하면 서비스 가용성에 영향을 미칠 수 있습니다. 이 작업을 수행할 때는 주의하십시오. 계속하시겠습니까?',\n        port: '포트',\n        portHelper: 'SSH 서비스가 수신하는 포트를 지정하십시오.',\n        listenAddress: '수신 주소',\n        allV4V6: '0.0.0.0:{0}(IPv4) 및 :::{0}(IPv6)',\n        listenHelper: 'IPv4와 IPv6 설정을 모두 비워두면 \"0.0.0.0:{0}(IPv4)\"와 \":::{0}(IPv6)\"에서 수신합니다.',\n        addressHelper: 'SSH 서비스가 수신할 주소를 지정하십시오.',\n        permitRootLogin: '루트 사용자 로그인 허용',\n        rootSettingHelper: '루트 사용자의 기본 로그인 방법은 \"SSH 로그인 허용\"입니다.',\n        rootHelper1: 'SSH 로그인 허용',\n        rootHelper2: 'SSH 로그인 비허용',\n        rootHelper3: '키 로그인만 허용',\n        rootHelper4: '미리 정의된 명령만 실행할 수 있습니다. 다른 작업은 수행할 수 없습니다.',\n        passwordAuthentication: '비밀번호 인증',\n        pwdAuthHelper: '비밀번호 인증을 활성화할지 여부입니다. 기본적으로 이 매개변수는 활성화되어 있습니다.',\n        pubkeyAuthentication: '키 인증',\n        privateKey: '개인 키',\n        publicKey: '공개 키',\n        password: '비밀번호',\n        createMode: '생성 방식',\n        generate: '자동 생성',\n        unSyncPass: '키 비밀번호 동기화 불가',\n        syncHelper: '동기화 작업으로 유효하지 않은 키를 정리하고 새로운 완전한 키 쌍을 동기화합니다. 계속하시겠습니까?',\n        input: '수동 입력',\n        import: '파일 업로드',\n        authKeys: 'Kunci Pengesahan',\n        authKeysHelper: '현재 공개 키 정보를 저장하시겠습니까?',\n        pubkey: '키 정보',\n        encryptionMode: '암호화 모드',\n        pubKeyHelper: '현재 키 정보는 사용자 {0}에게만 적용됩니다',\n        passwordHelper: '6~10자리 숫자 및 영어 대소문자를 포함할 수 있습니다.',\n        reGenerate: '키 재생성',\n        keyAuthHelper: '키 인증을 활성화할지 여부입니다.',\n        useDNS: 'useDNS',\n        dnsHelper: 'SSH 서버에서 DNS 확인 기능을 활성화하여 연결의 신원을 확인할지 여부를 제어합니다.',\n        analysis: '통계 정보',\n        denyHelper:\n            \"'거부' 작업을 다음 주소에서 수행합니다. 설정 후 해당 IP는 서버에 접근할 수 없습니다. 계속하시겠습니까?\",\n        acceptHelper:\n            \"'수락' 작업을 다음 주소에서 수행합니다. 설정 후 해당 IP는 정상적으로 접근할 수 있습니다. 계속하시겠습니까?\",\n        noAddrWarning: '현재 [{0}] 주소가 선택되지 않았습니다. 확인 후 다시 시도하십시오!',\n        loginLogs: 'SSH 로그',\n        loginMode: '모드',\n        authenticating: '키',\n        publickey: '키',\n        belong: '소속',\n        local: '로컬',\n        session: '세션 | 세션들',\n        loginTime: '로그인 시간',\n        loginIP: '로그인 IP',\n        stopSSHWarn: '이 SSH 연결을 끊으시겠습니까?',\n        remote: '원격 SSH',\n    },\n    setting: {\n        panel: '패널',\n        user: '패널 사용자',\n        userChange: '패널 사용자 변경',\n        userChangeHelper: '패널 사용자를 변경하면 로그아웃됩니다. 계속하시겠습니까?',\n        passwd: '패널 비밀번호',\n        emailHelper: '비밀번호 복구용',\n        watermark: '워터마크 설정',\n        watermarkContent: '워터마크 내용',\n        contentHelper:\n            '{0} 은 노드 이름, {1} 은 노드 주소를 나타냅니다. 변수를 사용하거나 사용자 정의 이름을 입력할 수 있습니다.',\n        watermarkColor: '워터마크 색상',\n        watermarkFont: '워터마크 글꼴 크기',\n        watermarkHeight: '워터마크 높이',\n        watermarkWidth: '워터마크 너비',\n        watermarkRotate: '회전 각도',\n        watermarkGap: '간격',\n        watermarkCloseHelper: '시스템 워터마크 설정을 해제하시겠습니까?',\n        watermarkOpenHelper: '현재 시스템 워터마크 설정을 저장하시겠습니까?',\n        title: '패널 별칭',\n        panelPort: '패널 포트',\n        titleHelper: '영문자, 한자, 숫자, 공백, 일반 특수 문자를 포함하여 3~30자의 길이를 지원합니다.',\n        portHelper:\n            '권장 포트 범위는 8888에서 65535 사이입니다. 참고: 서버에 보안 그룹이 있는 경우, 보안 그룹에서 새로운 포트를 사전에 허용해야 합니다.',\n        portChange: '포트 변경',\n        portChangeHelper: '서비스 포트를 수정하고 서비스를 다시 시작합니다. 계속하시겠습니까?',\n        theme: '테마',\n        menuTabs: '메뉴 탭',\n        dark: '다크',\n        darkGold: '다크 골드',\n        light: '라이트',\n        auto: '시스템 따라가기',\n        language: '언어',\n        runtimeEnv: '실행 환경',\n        docSource: '문서 출처',\n        withByRegion: '운영 지역 따름(기본값)',\n        withByLang: '시스템 언어 따름',\n        region: '운영 지역',\n        cn: '중국 본토',\n        intl: '글로벌',\n        regionHelper: '실행 지역을 변경하면 다음 리소스 출처가 전환됩니다:',\n        regionHelper1: '시스템 설치 패키지',\n        regionHelper2: '앱 스토어 및 스크립트 라이브러리',\n        regionHelper3: '사용자 매뉴얼 및 관련 문서',\n        regionHelper4: '향후 다운로드 및 접근에 영향을 줄 수 있습니다. 주의하십시오.',\n        regionTip: '운영 지역은 시스템 업데이트 소스와 설치 패키지 다운로드 주소에 영향을 미칩니다.',\n        docSourceTip: '문서 출처는 문서와 업데이트 로그의 이동 언어를 결정합니다.',\n        languageHelper: '기본적으로 브라우저 언어를 따릅니다. 이 설정은 현재 브라우저에서만 적용됩니다.',\n        sessionTimeout: '세션 타임아웃',\n        sessionTimeoutError: '최소 세션 타임아웃은 300초입니다.',\n        sessionTimeoutHelper: '패널에서 {0}초 이상 조작이 없을 경우 자동으로 로그아웃됩니다.',\n        systemIP: '기본 접근 주소',\n        systemIPHelper:\n            '애플리케이션 리다이렉트, 컨테이너 접근 등의 기능은 이 주소를 사용하여 라우팅됩니다. 각 노드마다 다른 주소를 설정할 수 있습니다.',\n        proxy: '서버 프록시',\n        proxyHelper: '프록시 서버를 설정한 후 다음 시나리오에서 적용됩니다:',\n        proxyHelper1: '설치 패키지 다운로드 및 앱 스토어 동기화 (전문 버전에서만 제공)',\n        proxyHelper2: '시스템 업데이트 및 업데이트 정보 가져오기 (전문 버전에서만 제공)',\n        proxyHelper4: 'Docker 이미지 pull 등 네트워크 요청은 프록시 서버를 통해 이루어집니다 (전문 버전에서만 제공)',\n        proxyHelper3: '시스템 라이선스 인증 및 동기화',\n        proxyHelper5: '시스템 유형 스크립트 라이브러리의 통합 다운로드 및 동기화 (프로페셔널 에디션 기능)',\n        proxyHelper6: '인증서 신청 (프로 버전 기능)',\n        proxyType: '프록시 유형',\n        proxyUrl: '프록시 주소',\n        proxyPort: '프록시 포트',\n        proxyPasswdKeep: '비밀번호 기억',\n        proxyDocker: 'Docker 프록시',\n        proxyDockerHelper:\n            '프록시 서버 구성을 Docker 에 동기화하여 오프라인 서버 이미지 가져오기 등의 작업을 지원합니다.',\n        syncToNode: '자식 노드로 동기화',\n        syncToNodeHelper: '다른 노드로 설정 동기화',\n        nodes: '노드',\n        selectNode: '노드 선택',\n        selectNodeError: '노드를 선택해 주세요',\n        apiInterface: 'API 활성화',\n        apiInterfaceClose: '비활성화하면 API 인터페이스에 접근할 수 없습니다. 계속하시겠습니까?',\n        apiInterfaceHelper: '서드파티 애플리케이션이 API 에 접근할 수 있도록 허용합니다.',\n        apiInterfaceAlert1: '운영 환경에서는 활성화하지 마십시오. 서버 보안 위험이 증가할 수 있습니다.',\n        apiInterfaceAlert2: '잠재적인 보안 위협을 방지하기 위해 서드파티 애플리케이션으로 API를 호출하지 마십시오.',\n        apiInterfaceAlert3: 'API 문서',\n        apiInterfaceAlert4: '사용 설명서',\n        apiKey: 'API 키',\n        apiKeyHelper: 'API 키는 서드파티 애플리케이션이 API 에 접근하는 데 사용됩니다.',\n        ipWhiteList: 'IP 허용 목록',\n        ipWhiteListEgs: '한 줄에 하나씩 입력하십시오. 예:\\n172.161.10.111\\n172.161.10.0/24',\n        ipWhiteListHelper: '허용 목록에 있는 IP만 API 에 접근할 수 있습니다. 0.0.0.0/0(모든 IPv4), ::/0(모든 IPv6)',\n        apiKeyValidityTime: '인터페이스 키 유효 기간',\n        apiKeyValidityTimeEgs: '인터페이스 키 유효 기간 (분 단위)',\n        apiKeyValidityTimeHelper:\n            '인터페이스 타임스탬프가 현재 타임스탬프와의 차이가 허용 범위 내에 있을 경우 유효합니다. 값이 0이면 검증이 비활성화됩니다.',\n        apiKeyReset: '인터페이스 키 재설정',\n        apiKeyResetHelper: '연관된 키 서비스가 무효화됩니다. 서비스에 새 키를 추가하십시오.',\n        confDockerProxy: 'Docker 프록시 구성',\n        restartNowHelper: 'Docker 프록시 구성을 위해 Docker 서비스를 재시작해야 합니다.',\n        restartNow: '즉시 재시작',\n        restartLater: '나중에 수동으로 재시작',\n        systemIPWarning: '현재 노드에 기본 접근 주소가 설정되지 않았습니다. 패널 설정에서 설정해 주세요!',\n        systemIPWarning1: '현재 서버 주소는 {0}으로 설정되어 있어 빠른 리디렉션이 불가능합니다!',\n        defaultNetwork: '네트워크 카드',\n        syncTime: '서버 시간',\n        timeZone: '시간대',\n        timeZoneChangeHelper: '시간대를 변경하면 서비스를 재시작해야 합니다. 계속하시겠습니까?',\n        timeZoneHelper: '시간대 변경은 시스템 timedatectl 서비스에 따라 작동하며, 1Panel 서비스 재시작 후 적용됩니다.',\n        timeZoneCN: '베이징',\n        timeZoneAM: '로스앤젤레스',\n        timeZoneNY: '뉴욕',\n        ntpALi: '알리바바',\n        ntpGoogle: '구글',\n        syncSite: 'NTP 서버',\n        syncSiteHelper: '이 작업은 {0}을(를) 시스템 시간 동기화의 소스로 사용합니다. 계속하시겠습니까?',\n        changePassword: '비밀번호 변경',\n        oldPassword: '기존 비밀번호',\n        newPassword: '새 비밀번호',\n        retryPassword: '비밀번호 확인',\n        noSpace: '입력 정보에 공백 문자를 포함할 수 없습니다.',\n        duplicatePassword: '새 비밀번호는 기존 비밀번호와 동일할 수 없습니다. 다시 입력하십시오!',\n        diskClean: '캐시 정리',\n        developerMode: '미리보기 프로그램',\n        developerModeHelper: '새로운 기능과 수정 사항을 정식 출시 전에 체험하고 초기 피드백을 제공할 수 있습니다.',\n        thirdParty: '서드파티 계정',\n        noTypeForCreate: '현재 생성된 백업 유형이 없습니다.',\n        LOCAL: '서버 디스크',\n        OSS: 'Ali OSS',\n        S3: 'Amazon S3',\n        mode: '모드',\n        MINIO: 'MinIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'Alist 에 WebDAV 를 연결하는 방법은 공식 문서를 참조하십시오.',\n        OneDrive: 'Microsoft OneDrive',\n        isCN: 'Century Internet',\n        isNotCN: '국제 버전',\n        client_id: '클라이언트 ID',\n        client_secret: '클라이언트 시크릿',\n        redirect_uri: '리디렉션 URL',\n        onedrive_helper: '사용자 정의 구성은 공식 문서를 참조하십시오.',\n        refreshTime: '토큰 갱신 시간',\n        refreshStatus: '토큰 갱신 상태',\n        backupDir: '백업 디렉터리',\n        codeWarning: '현재 인증 코드 형식이 올바르지 않습니다. 다시 확인하십시오!',\n        code: '인증 코드',\n        codeHelper:\n            '\"획득\" 버튼을 클릭한 다음 OneDrive 에 로그인하여 리디렉션된 링크에서 \"code\" 이후의 내용을 복사하십시오. 이 입력 상자에 붙여넣으십시오. 자세한 지침은 공식 문서를 참조하십시오.',\n        googleHelper:\n            '먼저 Google 애플리케이션을 생성하고 클라이언트 정보를 획득한 후 양식을 작성하고 획득 버튼을 클릭하세요. 구체적인 작업은 공식 문서를 참조하십시오.',\n        loadCode: '획득',\n        COS: 'Tencent COS',\n        ap_beijing_1: '베이징 지역 1',\n        ap_beijing: '베이징',\n        ap_nanjing: '난징',\n        ap_shanghai: '상하이',\n        ap_guangzhou: '광저우',\n        ap_chengdu: '청두',\n        ap_chongqing: '충칭',\n        ap_shenzhen_fsi: '선전 금융',\n        ap_shanghai_fsi: '상하이 금융',\n        ap_beijing_fsi: '베이징 금융',\n        ap_hongkong: '홍콩, 중국',\n        ap_singapore: '싱가포르',\n        ap_mumbai: '뭄바이',\n        ap_jakarta: '자카르타',\n        ap_seoul: '서울',\n        ap_bangkok: '방콕',\n        ap_tokyo: '도쿄',\n        na_siliconvalley: '실리콘밸리 (미국 서부)',\n        na_ashburn: '애쉬번 (미국 동부)',\n        na_toronto: '토론토',\n        sa_saopaulo: '상파울루',\n        eu_frankfurt: '프랑크푸르트',\n        KODO: 'Qiniu Kodo',\n        scType: '스토리지 유형',\n        typeStandard: '표준',\n        typeStandard_IA: '저빈도 표준',\n        typeArchive: '아카이브',\n        typeDeep_Archive: '심층 아카이브',\n        scLighthouse: '기본값: 경량 오브젝트 스토리지는 이 스토리지 유형만 지원합니다',\n        scStandard:\n            '표준 스토리지는 실시간 접근, 빈번한 데이터 상호작용이 필요한 핫 파일이 많은 비즈니스 시나리오에 적합합니다.',\n        scStandard_IA:\n            '저빈도 스토리지는 접근 빈도가 비교적 낮고 최소 30일 동안 데이터를 저장하는 비즈니스 시나리오에 적합합니다.',\n        scArchive: '아카이브 스토리지는 접근 빈도가 극히 낮은 비즈니스 시나리오에 적합합니다.',\n        scDeep_Archive: '내구성이 뛰어난 콜드 스토리지는 접근 빈도가 극히 낮은 비즈니스 시나리오에 적합합니다.',\n        archiveHelper:\n            '아카이브 스토리지 파일은 직접 다운로드할 수 없으며, 해당 클라우드 서비스 제공자의 웹사이트를 통해 복원해야 합니다. 신중히 사용하십시오!',\n        backupAlert: 'S3 프로토콜을 지원하는 클라우드 공급자는 Amazon S3 를 사용하여 백업할 수 있습니다.',\n        domain: '가속 도메인',\n        backupAccount: '백업 계정 | 백업 계정',\n        loadBucket: '버킷 가져오기',\n        accountName: '계정 이름',\n        accountKey: '계정 키',\n        address: '주소',\n        path: '경로',\n        safe: '보안',\n        passkey: '패스키',\n        passkeyManage: '관리',\n        passkeyKeyManagement: '키 관리',\n        passkeyHelper: '빠른 로그인을 위해 최대 5개의 패스키를 등록할 수 있습니다',\n        passkeyRequireSSL: '패스키는 도메인 바인딩과 HTTPS 접속이 필요합니다',\n        passkeyTrustedProxies: '신뢰할 수 있는 프록시',\n        passkeyTrustedProxiesHelper:\n            '이 IP/CIDR 출처의 요청만 Forwarded와 X-Forwarded-Proto 헤더를 HTTPS 판단에 사용합니다',\n        passkeyNotSupported: '브라우저/환경이 패스키를 지원하지 않습니다. 도메인과 HTTPS를 확인하세요',\n        passkeyPrereqTitle: 'Passkey 사용을 위해 다음 전제 조건이 필요합니다',\n        passkeyPrereqBindDomain: '1Panel 도메인 바인딩 구성됨',\n        passkeyPrereqHttps: 'HTTPS를 통해 안전하게 접속됨',\n        passkeyPrereqBrowser: '현재 환경이 Passkey를 지원합니다 (WebAuthn + 플랫폼 인증기)',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable:\n            '시스템 브라우저로 접속하고, 바인딩된 도메인으로 HTTPS에서 패널을 열었는지 확인하세요.',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            '현재 환경에서는 기기 인증을 사용할 수 없습니다. 더 최신 OS의 시스템 브라우저에서 시도하세요.',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            '화면 잠금/지문/얼굴 인식/Windows Hello를 활성화한 뒤 다시 시도하세요.',\n        passkeyPrereqBrowserDetailDetectFailed:\n            '감지에 실패했습니다. 페이지를 새로고침 후 다시 시도하거나 시스템 브라우저로 전환하세요.',\n        passkeyPrereqGoSetup: '설정',\n        passkeyCount: '등록됨 {0}/{1}',\n        passkeyName: '이름',\n        passkeyNameHelper: '기기를 구분할 이름을 입력하세요',\n        passkeyAdd: '패스키 추가',\n        passkeyCreatedAt: '생성 시간',\n        passkeyLastUsedAt: '최근 사용',\n        passkeyDeleteConfirm: '삭제하면 이 패스키로 로그인할 수 없습니다. 계속하시겠습니까?',\n        passkeyLimit: '최대 5개의 패스키를 등록할 수 있습니다',\n        passkeyFailed: '패스키 등록에 실패했습니다. 패널 SSL 인증서가 신뢰할 수 있는지 확인하세요',\n        bindInfo: '바인딩 정보',\n        bindAll: '모두 수신',\n        bindInfoHelper: '서비스 수신 주소나 프로토콜 변경은 서비스 불가 상태를 초래할 수 있습니다. 계속하시겠습니까?',\n        ipv6: 'IPv6 수신',\n        bindAddress: '수신 주소',\n        entrance: '진입점',\n        showEntrance: '\"개요\" 페이지에서 비활성화 경고 표시',\n        entranceHelper: '보안 진입점을 활성화하면 지정된 보안 진입점을 통해서만 패널에 로그인할 수 있습니다.',\n        entranceError: '5-116자의 안전한 로그인 진입점을 입력하십시오. 숫자나 문자만 지원됩니다.',\n        entranceInputHelper: '공백으로 남기면 보안 진입점이 비활성화됩니다.',\n        randomGenerate: '랜덤',\n        expirationTime: '만료 날짜',\n        unSetting: '미설정',\n        noneSetting: '패널 비밀번호의 만료 시간을 설정합니다. 만료 후 비밀번호를 재설정해야 합니다.',\n        expirationHelper: '비밀번호 만료 시간이 [0]일인 경우 비밀번호 만료 기능이 비활성화됩니다.',\n        days: '만료 일수',\n        expiredHelper: '현재 비밀번호가 만료되었습니다. 비밀번호를 다시 변경하십시오.',\n        timeoutHelper: '[ {0}일 ] 패널 비밀번호가 곧 만료됩니다. 만료 후 비밀번호를 재설정해야 합니다.',\n        complexity: '복잡성 검증',\n        complexityHelper:\n            '활성화하면 비밀번호 검증 규칙이 8-30 자, 영어, 숫자 및 최소 두 개의 특수 문자 포함으로 설정됩니다.',\n        bindDomain: '도메인 바인딩',\n        unBindDomain: '도메인 바인딩 해제',\n        panelSSL: '패널 SSL',\n        unBindDomainHelper: '도메인 이름 바인딩 해제 작업은 시스템 보안에 영향을 미칠 수 있습니다. 계속하시겠습니까?',\n        bindDomainHelper: '도메인을 바인딩한 후에는 해당 도메인을 통해서만 1Panel 서비스에 접근할 수 있습니다.',\n        bindDomainHelper1: '공백으로 남기면 도메인 이름 바인딩이 비활성화됩니다.',\n        bindDomainWarning:\n            '도메인 바인딩 후 로그아웃되며 설정에 지정된 도메인 이름을 통해서만 1Panel 서비스에 접근할 수 있습니다. 계속하시겠습니까?',\n        allowIPs: '허가된 IP',\n        unAllowIPs: '허가되지 않은 IP',\n        unAllowIPsWarning:\n            '허가되지 않은 빈 IP를 설정하면 모든 IP가 시스템에 접근할 수 있어 시스템 보안에 영향을 미칠 수 있습니다. 계속하시겠습니까?',\n        allowIPsHelper: '허가된 IP 주소 목록을 설정하면 목록에 있는 IP 주소만 패널 서비스에 접근할 수 있습니다.',\n        allowIPsWarning:\n            '허가된 IP 주소 목록을 설정하면 목록에 있는 IP 주소만 패널 서비스에 접근할 수 있습니다. 계속하시겠습니까?',\n        allowIPsHelper1: '공백으로 남기면 IP 주소 제한이 비활성화됩니다.',\n        allowIPEgs: '한 줄에 하나씩 입력하십시오. 예:\\n172.16.10.111\\n172.16.10.0/24',\n        mfa: '2단계 인증',\n        mfaClose: 'MFA를 비활성화하면 서비스 보안이 낮아집니다. 계속하시겠습니까?',\n        secret: '비밀키',\n        mfaInterval: '갱신 간격(초)',\n        mfaTitleHelper:\n            '제목은 다른 1Panel 호스트를 구별하는 데 사용됩니다. 제목을 수정한 후 다시 스캔하거나 비밀키를 수동으로 추가하세요.',\n        mfaIntervalHelper:\n            '갱신 간격을 변경한 후 다시 스캔하거나 비밀키를 수동으로 추가하세요.\\n일부 인증 앱은 30초 간격만 지원합니다(예: Google/Microsoft Authenticator).',\n        mfaAlert:\n            '일회용 토큰은 현재 시간을 기반으로 생성된 동적 6자리 숫자입니다. 서버 시간이 동기화되어 있는지 확인하세요.',\n        mfaHelper: '활성화 후 일회용 토큰 검증이 필요합니다.',\n        mfaHelper1: '인증 앱을 다운로드하세요. 예를 들어,',\n        mfaHelper2:\n            '다음 QR 코드를 인증 앱으로 스캔하거나 비밀키를 복사하여 인증 앱에 입력하여 일회용 토큰을 획득하세요.',\n        mfaHelper3: '앱에서 생성된 6자리 숫자를 입력하세요.',\n        mfaCode: '일회용 토큰',\n        sslChangeHelper: 'HTTPS 설정을 수정하고 서비스를 재시작합니다. 계속하시겠습니까?',\n        sslDisable: '비활성화',\n        sslDisableHelper: 'HTTPS 서비스를 비활성화하면 패널을 재시작해야 적용됩니다. 계속하시겠습니까?',\n        noAuthSetting: '비인가 설정',\n        noAuthSettingHelper:\n            '지정된 보안 진입점을 사용하지 않거나, 지정된 IP 나 도메인에서 패널에 접근하지 않는 경우 이 응답은 패널 특성을 숨길 수 있습니다.',\n        responseSetting: '응답 설정',\n        help200: '도움말 페이지',\n        error400: '잘못된 요청',\n        error401: '권한 없음',\n        error403: '접근 금지',\n        error404: '페이지를 찾을 수 없음',\n        error408: '요청 시간 초과',\n        error416: '범위 불만족',\n        error444: '연결 닫힘',\n        error500: '서버 오류',\n        https: '패널에 HTTPS를 설정하면 액세스 보안이 향상됩니다',\n        strictHelper: '비 HTTPS 트래픽은 패널에 연결할 수 없습니다',\n        muxHelper:\n            '패널은 HTTP 및 HTTPS 프로토콜 모두를 수신하고 HTTP를 HTTPS로 리디렉션하지만 보안이 저하될 수 있습니다',\n        certType: '인증서 유형',\n        selfSigned: '자가 서명',\n        selfSignedHelper: '자가 서명 인증서는 브라우저에서 신뢰하지 않을 수 있으며 보안 경고가 표시될 수 있습니다.',\n        select: '선택',\n        domainOrIP: '도메인 또는 IP:',\n        timeOut: '시간 초과',\n        rootCrtDownload: '루트 인증서 다운로드',\n        primaryKey: '개인 키',\n        certificate: '인증서',\n        backupJump: '현재 백업 목록에 없는 파일입니다. 파일 디렉토리에서 다운로드하여 백업에 가져오기를 시도하세요.',\n        snapshot: '스냅샷 | 스냅샷들',\n        noAppData: '선택할 수 있는 시스템 앱이 없습니다',\n        noBackupData: '선택할 수 있는 백업 데이터가 없습니다',\n        stepBaseData: '기본 데이터',\n        stepAppData: '시스템 앱',\n        stepPanelData: '시스템 데이터',\n        stepBackupData: '백업 데이터',\n        stepOtherData: '기타 데이터',\n        operationLog: '작업 로그 유지',\n        loginLog: '접속 로그 유지',\n        systemLog: '시스템 로그 유지',\n        taskLog: '작업 로그 유지',\n        monitorData: '모니터링 데이터 유지',\n        dockerConf: 'Docker 설정',\n        selectAllImage: '모든 앱 이미지를 백업',\n        logLabel: '로그',\n        agentLabel: '노드 설정',\n        appDataLabel: '앱 데이터',\n        appImage: '앱 이미지',\n        appBackup: '앱 백업',\n        backupLabel: '백업 디렉토리',\n        confLabel: '설정 파일',\n        dockerLabel: '컨테이너',\n        taskLabel: '예약 작업',\n        resourceLabel: '앱 리소스 디렉토리',\n        runtimeLabel: '실행 환경',\n        appLabel: '앱',\n        databaseLabel: '데이터베이스',\n        snapshotLabel: '스냅샷 파일',\n        websiteLabel: '웹사이트',\n        directoryLabel: '디렉토리',\n        appStoreLabel: '앱 스토어',\n        shellLabel: '스크립트',\n        tmpLabel: '임시 디렉토리',\n        sslLabel: '인증서 디렉토리',\n        reCreate: '스냅샷 생성 실패',\n        reRollback: '스냅샷 롤백 실패',\n        deleteHelper: '타사 백업 계정에 포함된 스냅샷 파일을 포함하여 모든 스냅샷 파일이 삭제됩니다. 계속하시겠습니까?',\n        status: '스냅샷 상태',\n        ignoreRule: '무시 규칙',\n        editIgnoreRule: '@:commons.button.edit @.lower:setting.ignoreRule',\n        ignoreHelper:\n            '이 규칙은 스냅샷을 생성할 때 1Panel 데이터 디렉토리를 압축하고 백업하는 데 사용됩니다. 기본적으로 소켓 파일은 무시됩니다.',\n        ignoreHelper1: '한 줄에 하나씩 입력하세요. 예:\\n*.log\\n/opt/1panel/cache',\n        panelInfo: '1Panel 기본 정보 쓰기',\n        panelBin: '1Panel 시스템 파일 백업',\n        daemonJson: 'Docker 구성 파일 백업',\n        appData: '1Panel 에서 설치된 앱 백업',\n        panelData: '1Panel 데이터 디렉토리 백업',\n        backupData: '1Panel 의 로컬 백업 디렉토리 백업',\n        compress: '스냅샷 파일 생성',\n        upload: '스냅샷 파일 업로드',\n        recoverDetail: '복구 세부정보',\n        importHelper: '스냅샷 디렉토리: ',\n        lastRecoverAt: '마지막 복구 시간',\n        lastRollbackAt: '마지막 롤백 시간',\n        reDownload: '백업 파일 다시 다운로드',\n        recoverErrArch: '서버 아키텍처가 다른 스냅샷 복구는 지원되지 않습니다!',\n        recoverErrSize: '디스크 공간이 부족합니다. 확인하거나 정리 후 다시 시도하세요!',\n        recoverHelper: '스냅샷 {0} 에서 복구를 시작합니다. 계속하기 전에 다음 정보를 확인하세요:',\n        recoverHelper1: '복구에는 Docker 및 1Panel 서비스를 재시작해야 합니다.',\n        recoverHelper2: '서버에 충분한 디스크 공간이 있는지 확인하세요. (스냅샷 파일 크기: {0}, 사용 가능한 공간: {1})',\n        recoverHelper3:\n            '서버 아키텍처가 스냅샷이 생성된 서버의 아키텍처와 일치하는지 확인하세요. (현재 서버 아키텍처: {0})',\n        rollback: '롤백',\n        rollbackHelper:\n            '이 복구를 롤백하면 해당 복구의 모든 파일이 대체되며 Docker 및 1Panel 서비스를 재시작해야 할 수 있습니다. 계속하시겠습니까?',\n        upgradeHelper: '업그레이드에는 1Panel 서비스를 재시작해야 합니다. 계속하시겠습니까?',\n        rollbackLocalHelper:\n            '마스터 노드는 직접 롤백을 지원하지 않습니다. 수동으로 [1pctl restore] 명령어를 실행하여 롤백하세요!',\n        noUpgrade: '현재 최신 버전입니다',\n        upgradeNotes: '릴리스 노트',\n        upgradeNow: '지금 업그레이드',\n        source: '다운로드 소스',\n        versionNotSame: '노드 버전이 메인 노드와 일치하지 않습니다. 노드 관리에서 업그레이드한 후 다시 시도해 주세요.',\n        versionCompare:\n            '노드 {0}이(가) 이미 업그레이드 가능한 최신 버전입니다. 마스터 노드 버전을 확인 후 다시 시도하세요!',\n        about: '정보',\n        versionItem: '현재 버전',\n        backupCopies: '백업 수',\n        backupCopiesHelper:\n            '버전 롤백을 위해 보관할 업그레이드 백업 복사본 수를 설정합니다. 0은 모두 보관을 의미합니다.',\n        backupCopiesRule: '최소 3개의 업그레이드 백업 기록을 보관하세요',\n        release: '버전 업데이트 로그',\n        releaseHelper:\n            '현재 환경의 업데이트 로그를 가져오는 중 오류가 발생했습니다. 공식 문서에서 수동으로 확인하실 수 있습니다.',\n        project: 'GitHub',\n        issue: '이슈',\n        doc: '공식 문서',\n        star: '별',\n        description: '리눅스 서버 패널',\n        forum: '토론',\n        doc2: '문서',\n        currentVersion: '버전',\n        license: '라이선스',\n        bindNode: '노드 바인딩',\n        menuSetting: '메뉴 설정',\n        menuSettingHelper: '하위 메뉴가 1개만 존재할 경우, 메뉴 바에는 해당 하위 메뉴만 표시됩니다',\n        showAll: '모두 표시',\n        hideALL: '모두 숨기기',\n        ifShow: '표시 여부',\n        menu: '메뉴',\n        confirmMessage: '고급 메뉴 목록을 업데이트하려면 페이지가 새로 고쳐집니다. 계속하시겠습니까?',\n        recoverMessage: '페이지가 새로고침되어 메뉴 목록이 초기 상태로 복원됩니다. 계속하시겠습니까?',\n        compressPassword: '압축 비밀번호',\n        backupRecoverMessage: '압축 또는 압축 해제 비밀번호를 입력하세요 (설정하지 않으려면 비워 두세요)',\n        componentSize: '컴포넌트 크기',\n        scope: '범위',\n        public: '공개',\n        publicHelper: '공개 타입 백업 계정은 각 하위 노드로 동기화되어 함께 사용할 수 있습니다',\n        private: '비공개',\n        privateHelper: '비공개 타입 백업 계정은 현재 노드에서만 생성되며 현재 노드에서만 사용됩니다',\n        UPYUN: 'UPYUN',\n        ALIYUN: 'Aliyun Drive',\n        ALIYUNHelper:\n            'Aliyun Drive의 비클라이언트 다운로드 최대 용량은 100MB입니다. 초과 시 클라이언트로 다운로드하세요.',\n        ALIYUNRecover:\n            'Aliyun Drive의 비클라이언트 다운로드 최대 용량은 100MB입니다. 초과 시 클라이언트로 로컬 다운로드 후 스냅샷을 동기화해 복원하세요.',\n        GoogleDrive: 'Google Drive',\n        analysis: '분석',\n        analysisHelper:\n            '전체 토큰 내용을 붙여넣으면 필요한 항목을 자동으로 파싱합니다. 자세한 내용은 공식 문서를 참고하세요.',\n        serviceName: '서비스 이름',\n        operator: '운영자',\n        clickToRefresh: '클릭하여 새로고침',\n        recoverFailed: '스냅샷 복원 실패',\n        upgradeRecord: '업그레이드 기록',\n        upgrading: '업그레이드 중, 잠시만 기다려 주세요...',\n        versionHelper: '명명 규칙: [메이저].[기능].[버그 수정], 예시:',\n        upgradeCheck: '업데이트 확인',\n        panelSSLHelper: '패널 SSL 자동 갱신 후 1Panel 서비스를 수동 재시작해야 적용됩니다.',\n    },\n    license: {\n        offLine: '오프라인 버전',\n        community: 'OSS',\n        oss: '오픈 소스 소프트웨어',\n        pro: 'Pro',\n        trial: '체험판',\n        add: '커뮤니티 에디션 추가',\n        licenseBindHelper: '라이선스가 노드에 바인딩된 경우에만 무료 노드 할당량을 사용할 수 있습니다',\n        licenseAlert:\n            '라이선스가 노드에 정상적으로 바인딩된 경우에만 커뮤니티 에디션 노드를 추가할 수 있습니다. 라이선스에 정상적으로 바인딩된 노드만 전환이 지원됩니다.',\n        licenseUnbindHelper: '이 라이선스에 커뮤니티 에디션 노드가 존재합니다. 바인딩 해제 후 다시 시도하세요!',\n        subscription: '구독',\n        perpetual: '영구 라이선스',\n        versionConstraint: '{0} 버전 일시불 구매',\n        forceUnbind: '강제 바인딩 해제',\n        forceUnbindHelper:\n            '강제 바인딩 해제를 수행하면 해제 과정에서 발생하는 오류를 무시하고 궁극적으로 라이센스 바인딩을 해제합니다.',\n        updateForce: '강제 업데이트 (바인딩 해제 과정의 모든 오류를 무시하고 최종 작업 성공을 보장합니다)',\n        trialInfo: '버전',\n        authorizationId: '구독 ID',\n        authorizedUser: '인증된 사용자',\n        lostHelper:\n            '라이센스가 최대 재시도 횟수를 초과했습니다. 전문가 버전 기능이 제대로 작동하는지 확인하려면 동기화 버튼을 수동으로 클릭하세요.',\n        disableHelper:\n            '라이센스 동기화 검증에 실패했습니다. 전문가 버전 기능이 제대로 작동하는지 확인하려면 동기화 버튼을 수동으로 클릭하세요.',\n        quickUpdate: '빠른 업데이트',\n        power: '권한 부여',\n        unbindHelper: '연결 해제 후 모든 Pro 관련 설정이 초기화됩니다. 계속하시겠습니까?',\n        importLicense: '라이센스',\n        importHelper: '라이센스 파일을 여기에 클릭하거나 드래그하세요',\n        technicalAdvice: '기술 상담',\n        advice: '상담',\n        levelUpPro: 'Pro 로 업그레이드',\n        licenseSync: '라이센스 동기화',\n        knowMorePro: '더 알아보기',\n        closeAlert: '현재 페이지는 패널 설정에서 닫을 수 있습니다.',\n        introduce: '기능 소개',\n        waf: '전문 버전으로 업그레이드하면 차단 맵, 로그, 차단 기록, 지리적 위치 차단, 사용자 정의 규칙, 사용자 정의 차단 페이지 등의 기능을 제공받을 수 있습니다.',\n        tamper: '전문 버전으로 업그레이드하면 웹사이트를 무단 수정이나 변조로부터 보호할 수 있습니다.',\n        setting: '전문 버전으로 업그레이드하면 패널 로고, 환영 메시지 등 정보를 사용자 정의할 수 있습니다.',\n        monitor:\n            '전문 버전으로 업그레이드하면 웹사이트의 실시간 상태, 방문자 트렌드, 방문자 출처, 요청 로그 등 정보를 확인할 수 있습니다.',\n        alert: '전문 버전으로 업그레이드하면 SMS 를 통해 알림 정보를 받고 알림 로그를 볼 수 있으며, 다양한 주요 이벤트를 완벽하게 제어하여 시스템 운영을 걱정 없이 유지할 수 있습니다.',\n        node: '프로페셔널 에디션으로 업그레이드하면 1Panel로 여러 Linux 서버를 관리할 수 있습니다.',\n        nodeApp:\n            '프로페셔널 에디션으로 업그레이드하면 노드를 수동으로 전환하지 않고도 다중 노드 애플리케이션 버전을 통합 업그레이드할 수 있습니다.',\n        nodeDashboard:\n            '프로페셔널 에디션으로 업그레이드하면 다중 노드 애플리케이션, 웹사이트, 데이터베이스 및 예약 작업을 중앙 집중식으로 관리할 수 있습니다.',\n        fileExchange: '프로페셔널 에디션으로 업그레이드하여 여러 서버 간에 파일을 빠르게 전송할 수 있습니다.',\n        app: '프로페셔널 버전으로 업그레이드하면 모바일 APP을 통해 서비스 정보, 이상 모니터링 등을 확인할 수 있습니다.',\n        cluster:\n            '프로페셔널 에디션으로 업그레이드하면 MySQL/Postgres/Redis 마스터-슬레이브 클러스터를 관리할 수 있습니다.',\n        exceptionalHelper:\n            '라이선스 동기화 검증이 비정상입니다. 수동으로 동기화 버튼을 눌러 프로 버전 기능을 확인하세요. 상세: ',\n        tamperHelper: '작업 실패, 파일/폴더에 변조 방지가 활성화되어 있습니다. 확인 후 다시 시도하세요!',\n    },\n    clean: {\n        scan: '스캔 시작',\n        scanHelper: '1Panel 실행 중에 생성된 불필요한 파일을 쉽게 정리합니다.',\n        clean: '지금 정리',\n        reScan: '다시 스캔',\n        cleanHelper: '선택된 파일 및 디렉터리는 정리 후 롤백할 수 없습니다. 계속하시겠습니까?',\n        statusSuggest: '(권장 정리)',\n        statusClean: '(매우 깨끗함)',\n        statusEmpty: '매우 깨끗합니다. 정리가 필요하지 않습니다!',\n        statusWarning: '(주의하여 진행)',\n        lastCleanTime: '마지막 정리 시간: {0}',\n        lastCleanHelper: '정리된 파일 및 디렉터리: {0}, 총 정리된 크기: {1}',\n        cleanSuccessful: '정리 성공',\n        currentCleanHelper: '이번 세션에서 정리된 파일 및 디렉터리: {0}, 총 정리된 크기: {1}',\n        suggest: '(권장)',\n        totalScan: '정리해야 할 총 불필요 파일: ',\n        selectScan: '선택된 불필요 파일 총합: ',\n        system: '시스템 불필요 파일',\n        systemHelper: '스냅샷, 업그레이드 및 기타 프로세스 중에 생성된 임시 파일',\n        panelOriginal: '시스템 스냅샷 복원 전 백업 파일',\n        upgrade: '시스템 업그레이드 백업 파일',\n        agentPackages: '이전 버전 자식 노드 업그레이드/설치 패키지',\n        upgradeHelper: '(시스템 롤백을 위해 최신 업그레이드 백업을 유지하는 것이 좋습니다)',\n        rollback: '복구 전 백업 파일',\n        backup: '시스템 백업',\n        backupHelper: '로컬 백업 계정에 연결되지 않은 백업 파일',\n        unknownBackup: '시스템 백업',\n        tmpBackup: '임시 백업',\n        unknownApp: '연결되지 않은 앱 백업',\n        unknownDatabase: '연결되지 않은 데이터베이스 백업',\n        unknownWebsite: '연결되지 않은 웹사이트 백업',\n        unknownSnapshot: '연결되지 않은 스냅샷 백업',\n        upload: '임시 업로드 파일',\n        uploadHelper: '시스템 백업 리스트에서 업로드된 임시 파일',\n        download: '임시 다운로드 파일',\n        downloadHelper: '시스템에서 제3자 백업 계정으로 다운로드된 임시 파일',\n        directory: '디렉터리',\n        systemLog: '로그 파일',\n        systemLogHelper: '시스템 로그, 작업 로그, 웹사이트 로그 파일',\n        dockerLog: '컨테이너 작업 로그 파일',\n        taskLog: '예약 작업 실행 로그 파일',\n        shell: '셸 스크립트 예약 작업',\n        containerShell: '컨테이너 내에서 실행하는 셸 스크립트 예약 작업',\n        curl: 'CURL 예약 작업',\n        docker: '컨테이너 쓰레기',\n        dockerHelper: '컨테이너, 이미지, 볼륨, 빌드 캐시 등의 파일',\n        volumes: '볼륨',\n        buildCache: '컨테이너 빌드 캐시',\n        appTmpDownload: '앱 임시 다운로드 파일',\n        unknownWebsiteLog: '연결되지 않은 웹사이트 로그 백업 파일',\n        snapshot: '시스템 스냅샷 임시 파일',\n    },\n    app: {\n        app: '애플리케이션 | 애플리케이션들',\n        installName: '이름',\n        installed: '설치됨',\n        all: '모두',\n        version: '버전',\n        detail: '세부사항',\n        params: '편집',\n        author: '저자',\n        source: '출처',\n        appName: '애플리케이션 이름',\n        deleteWarn:\n            '삭제 작업은 모든 데이터와 백업을 함께 삭제합니다. 이 작업은 되돌릴 수 없습니다. 계속 하시겠습니까?',\n        syncSuccess: '동기화 성공',\n        canUpgrade: '업데이트',\n        backupName: '파일 이름',\n        backupPath: '파일 경로',\n        backupdate: '백업 시간',\n        versionSelect: '버전을 선택하세요',\n        operatorHelper: '선택한 애플리케이션에 대해 {0} 작업이 수행됩니다. 계속 하시겠습니까?',\n        startOperatorHelper: '애플리케이션이 시작됩니다. 계속 하시겠습니까?',\n        stopOperatorHelper: '애플리케이션이 중지됩니다. 계속 하시겠습니까?',\n        restartOperatorHelper: '애플리케이션이 재시작됩니다. 계속 하시겠습니까?',\n        reloadOperatorHelper: '애플리케이션이 다시 로드됩니다. 계속 하시겠습니까?',\n        checkInstalledWarn: '\"{0}\"이(가) 감지되지 않았습니다. \"앱 스토어\"로 가서 설치하세요.',\n        limitHelper: '애플리케이션은 이미 설치되었습니다.',\n        deleteHelper: '\"{0}\"은(는) 다음 리소스와 연결되어 있습니다. 확인 후 다시 시도하세요!',\n        checkTitle: '힌트',\n        defaultConfig: '기본 설정',\n        defaultConfigHelper: '기본 설정으로 복원되었습니다. 저장 후 적용됩니다.',\n        forceDelete: '강제 삭제',\n        forceDeleteHelper: '강제 삭제는 삭제 과정 중 오류를 무시하고 메타데이터를 삭제합니다.',\n        deleteBackup: '백업 삭제',\n        deleteBackupHelper: '애플리케이션 백업도 삭제됩니다.',\n        deleteDB: '데이터베이스 삭제',\n        deleteDBHelper: '데이터베이스도 삭제됩니다.',\n        noService: '{0} 없음',\n        toInstall: '설치하러 가기',\n        param: '파라미터',\n        alreadyRun: '나이',\n        syncAppList: '동기화',\n        less1Minute: '1분 미만',\n        appOfficeWebsite: '오피스 웹사이트',\n        github: 'GitHub',\n        document: '문서',\n        updatePrompt: '업데이트가 없습니다.',\n        installPrompt: '아직 설치된 앱이 없습니다.',\n        updateHelper: '파라미터를 수정하면 애플리케이션이 시작되지 않을 수 있습니다. 주의하세요.',\n        updateWarn: '업데이트 파라미터는 애플리케이션을 재빌드해야 합니다. 계속 하시겠습니까?',\n        busPort: '포트',\n        syncStart: '동기화 시작! 나중에 앱 스토어를 새로고침 해주세요.',\n        advanced: '고급 설정',\n        cpuCore: '코어',\n        containerName: '컨테이너 이름',\n        containerNameHelper: '설정되지 않으면 컨테이너 이름이 자동으로 생성됩니다.',\n        allowPort: '외부 접근 허용',\n        allowPortHelper: '외부 포트 접근을 허용하면 방화벽 포트가 열립니다.',\n        appInstallWarn:\n            '애플리케이션은 기본적으로 외부 접근 포트를 노출하지 않습니다. \"고급 설정\"을 클릭하여 노출할 수 있습니다.',\n        upgradeStart: '업그레이드 시작! 나중에 페이지를 새로고침 해주세요.',\n        toFolder: '설치 디렉터리 열기',\n        editCompose: 'Compose 파일 편집',\n        editComposeHelper: 'Compose 파일을 편집하면 소프트웨어 설치가 실패할 수 있습니다.',\n        composeNullErr: 'Compose는 비어 있을 수 없습니다.',\n        takeDown: '내리기',\n        allReadyInstalled: '설치됨',\n        installHelper: '이미지 풀 문제 시 이미지 가속을 구성하세요.',\n        sortMode: '정렬',\n        installWarn:\n            '외부 접근이 활성화되지 않아 애플리케이션이 외부 네트워크에서 접근할 수 없습니다. 계속 하시겠습니까?',\n        showIgnore: '무시된 애플리케이션 보기',\n        cancelIgnore: '무시 취소',\n        ignoreList: '무시된 애플리케이션',\n        appHelper: '특수 애플리케이션 설치 지침을 보려면 애플리케이션 상세 페이지로 이동하세요.',\n        backupApp: '업그레이드 전 애플리케이션 백업',\n        backupAppHelper:\n            '업그레이드 실패 시 백업이 자동으로 롤백됩니다. 로그 감사 시스템 로그에서 실패 원인을 확인하세요. 백업은 기본적으로 최신 3개 복사본을 유지합니다.',\n        openrestyDeleteHelper: 'OpenResty 강제 삭제는 모든 웹사이트를 삭제합니다. 계속 하시겠습니까?',\n        downloadLogHelper1: '{0} 애플리케이션의 모든 로그가 다운로드됩니다. 계속 하시겠습니까?',\n        downloadLogHelper2: '{0} 애플리케이션의 최신 {1} 로그가 다운로드됩니다. 계속 하시겠습니까?',\n        syncAllAppHelper: '모든 애플리케이션이 동기화됩니다. 계속 하시겠습니까?',\n        hostModeHelper:\n            '현재 애플리케이션 네트워크 모드는 호스트 모드입니다. 포트를 열어야 할 경우 방화벽 페이지에서 수동으로 열어주세요.',\n        showLocal: '로컬 애플리케이션 보기',\n        reload: '새로고침',\n        upgradeWarn:\n            '애플리케이션 업그레이드는 docker-compose.yml 파일을 교체합니다. 변경 사항이 있으면 파일 비교를 클릭하여 확인할 수 있습니다.',\n        openclawHttpsUpgradeNoticeTitle:\n            '주의: 다음 안내는 2026.3.13 이전 버전에서 OpenClaw 를 업그레이드하는 사용자에게만 적용됩니다:',\n        openclawHttpsUpgradeNoticeItem1:\n            '배포된 에이전트 업그레이드가 완료되면 구성 -> 설정 -> 보안 페이지로 이동해 접근 주소를 수동으로 추가해야 합니다.',\n        openclawHttpsUpgradeNoticeItem2:\n            '새 버전은 에이전트 접근에 HTTPS 를 강제합니다. 이전에 리버스 프록시 사이트로 접근했다면 프록시 대상 주소를 https://IP:Port 로 변경하세요.',\n        newVersion: '새 버전',\n        oldVersion: '현재 버전',\n        composeDiff: '파일 비교',\n        showDiff: '비교 보기',\n        useNew: '사용자 정의 버전 사용',\n        useDefault: '기본 버전 사용',\n        useCustom: '사용자 정의 docker-compose.yml 사용',\n        useCustomHelper:\n            '사용자 정의 docker-compose.yml 파일을 사용하면 애플리케이션 업그레이드가 실패할 수 있습니다. 필요하지 않으면 체크하지 마세요.',\n        diffHelper: '왼쪽은 이전 버전, 오른쪽은 새 버전입니다. 편집 후 사용자 정의 버전을 저장하려면 클릭하세요.',\n        pullImage: '이미지 풀',\n        pullImageHelper: '애플리케이션 시작 전에 docker pull 을 실행하여 이미지를 다운로드하세요.',\n        deleteImage: '이미지 삭제',\n        deleteImageHelper: '애플리케이션 관련 이미지를 삭제합니다. 삭제에 실패하더라도 작업은 종료되지 않습니다.',\n        requireMemory: '메모리 요구사항',\n        supportedArchitectures: '지원 아키텍처',\n        link: '링크',\n        showCurrentArch: '현재 서버 아키텍처 애플리케이션',\n        syncLocalApp: '로컬 애플리케이션 동기화',\n        memoryRequiredHelper: '현재 애플리케이션은 {0} 메모리가 필요합니다',\n        gpuConfig: 'GPU 지원 활성화',\n        gpuConfigHelper:\n            'NVIDIA GPU가 장착되어 있으며 NVIDIA 드라이버와 NVIDIA Docker Container Toolkit이 설치되어 있는지 확인하세요',\n        webUI: '웹 접속 주소',\n        webUIPlaceholder: '예: example.com:8080/login',\n        defaultWebDomain: '기본 접속 주소',\n        defaultWebDomainHepler: '애플리케이션 포트가 8080인 경우 접속 주소는 http(s)://기본 접속 주소:8080입니다',\n        webUIConfig:\n            '현재 노드에 기본 접근 주소가 설정되지 않았습니다. 애플리케이션 매개변수에서 설정하거나 패널 설정으로 이동하여 설정하세요!',\n        toLink: '이동',\n        customAppHelper: '사용자 정의 앱 스토어 패키지를 설치하기 전에 설치된 앱이 없는지 확인하십시오.',\n        forceUninstall: '강제 제거',\n        syncCustomApp: 'カスタムアプリを同期',\n        ignoreAll: '후속 모든 버전 무시',\n        ignoreVersion: '지정된 버전 무시',\n        specifyIP: '호스트 IP 바인딩',\n        specifyIPHelper:\n            '포트 바인딩을 위한 호스트 주소/네트워크 인터페이스를 설정합니다 (이 기능을 잘 모를 경우, 입력하지 마십시오)',\n        uninstallDeleteBackup: '앱 제거 - 백업 삭제',\n        uninstallDeleteImage: '앱 제거 - 이미지 삭제',\n        upgradeBackup: '앱 업그레이드 전 앱 백업',\n        noAppHelper: '애플리케이션이 감지되지 않았습니다. 작업 센터에서 앱 스토어 동기화 로그를 확인해 주세요',\n        isEdirWarn: 'docker-compose.yml 파일이 수정된 것을 감지했습니다. 비교를 확인해 주세요',\n    },\n    website: {\n        primaryDomain: '기본 도메인',\n        otherDomains: '기타 도메인',\n        static: '정적',\n        deployment: '배포',\n        supportUpType: '.tar.gz 파일 형식만 지원되며, 압축 패키지에는 {0}.json 파일이 포함되어야 합니다',\n        proxy: '리버스 프록시',\n        alias: '별칭',\n        ftpUser: 'FTP 계정',\n        ftpPassword: 'FTP 비밀번호',\n        ftpHelper: '웹사이트를 생성하면 해당 FTP 계정이 생성되고 FTP 디렉터리는 웹사이트 디렉터리와 연결됩니다.',\n        remark: '비고',\n        manageGroup: '그룹 관리',\n        groupSetting: '그룹 설정',\n        createGroup: '그룹 생성',\n        appNew: '새로운 애플리케이션',\n        appInstalled: '설치된 애플리케이션',\n        delete: '웹사이트 삭제',\n        deleteApp: '애플리케이션 삭제',\n        deleteBackup: '백업 삭제',\n        domain: '도메인',\n        domainHelper: \"한 줄에 하나의 도메인.\\n와일드카드 '*'와 IP 주소를 지원합니다.\\n포트 추가를 지원합니다.\",\n        addDomain: '추가',\n        domainConfig: '도메인 설정',\n        defaultDoc: '기본 문서',\n        perserver: '동시 연결',\n        perserverHelper: '현재 사이트의 최대 동시 연결 수를 제한합니다.',\n        perip: '단일 IP',\n        peripHelper: '단일 IP의 최대 동시 접속 수를 제한합니다.',\n        rate: '트래픽 제한',\n        rateLimit: '요청당 속도 제한',\n        rateHelper: '요청당 트래픽을 제한합니다 (단위: KB)',\n        rateLimitHelper: '각 요청의 전송 속도를 제한합니다 (단위: KB/s)',\n        limitHelper: '트래픽 제어 활성화',\n        other: '기타',\n        currentSSL: '현재 인증서',\n        dnsAccount: 'DNS 계정',\n        applySSL: '인증서 신청',\n        SSLList: '인증서 목록',\n        createDnsAccount: 'DNS 계정 생성',\n        aliyun: '알리윤',\n        aliEsa: '알리윤 ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: '수동 설정',\n        key: '키',\n        check: '보기',\n        acmeAccountManage: 'ACME 계정 관리',\n        email: '이메일',\n        acmeAccount: 'ACME 계정',\n        provider: '검증 방법',\n        dnsManual: '수동 설정',\n        expireDate: '만료일',\n        brand: '기관',\n        deploySSL: '배포',\n        deploySSLHelper: '인증서를 배포하시겠습니까?',\n        ssl: '인증서 | 인증서들',\n        dnsAccountManage: 'DNS 제공자 관리',\n        renewSSL: '갱신',\n        renewHelper: '인증서를 갱신하시겠습니까?',\n        renewSuccess: '인증서 갱신 성공',\n        enableHTTPS: 'HTTPS 활성화',\n        aliasHelper: '별칭은 웹사이트의 디렉터리 이름입니다.',\n        lastBackupAt: '마지막 백업 시간',\n        null: '없음',\n        nginxConfig: 'Nginx 설정',\n        websiteConfig: '웹사이트 설정',\n        proxySettings: '프록시 설정',\n        advancedSettings: '고급 설정',\n        cacheSettings: '캐시 설정',\n        sniSettings: 'SNI 설정',\n        basic: '기본',\n        source: '구성',\n        security: '보안',\n        nginxPer: '성능 튜닝',\n        neverExpire: '만료 없음',\n        setDefault: '기본값으로 설정',\n        default: '기본값',\n        deleteHelper: '관련 애플리케이션 상태가 비정상입니다. 확인해 주세요.',\n        toApp: '설치된 목록으로 이동',\n        cycle: '주기',\n        frequency: '빈도',\n        ccHelper: '{0}초 내에 동일한 URL을 {1}회 이상 누적 요청하면 CC 방어가 발동되며, 해당 IP가 차단됩니다.',\n        mustSave: '변경 사항은 저장해야 적용됩니다.',\n        fileExt: '파일 확장자',\n        fileExtBlock: '파일 확장자 차단 목록',\n        value: '값',\n        enable: '활성화',\n        proxyAddress: '프록시 주소',\n        proxyHelper: '예: 127.0.0.1:8080',\n        forceDelete: '강제 삭제',\n        forceDeleteHelper: '강제 삭제는 삭제 과정에서 발생하는 오류를 무시하고 최종적으로 메타데이터를 삭제합니다.',\n        deleteAppHelper: '관련 애플리케이션 및 애플리케이션 백업을 동시에 삭제합니다.',\n        deleteBackupHelper: '웹사이트 백업도 삭제합니다.',\n        deleteDatabaseHelper: '웹사이트와 연결된 데이터베이스도 삭제합니다',\n        deleteConfirmHelper:\n            '삭제 작업은 되돌릴 수 없습니다. 확인하려면 <span style=\\'color:red\\'>\"{0}\"</span>을(를) 입력하세요.',\n        staticPath: '해당 주요 디렉터리는 ',\n        limit: '제한',\n        blog: '포럼/블로그',\n        imageSite: '이미지 사이트',\n        downloadSite: '다운로드 사이트',\n        shopSite: '쇼핑몰',\n        doorSite: '포털',\n        qiteSite: '기업',\n        videoSite: '비디오',\n        errLog: '오류 로그',\n        stopHelper:\n            '사이트를 중지하면 정상적으로 액세스할 수 없으며, 사용자가 해당 사이트의 중지 페이지를 보게 됩니다. 계속하시겠습니까?',\n        startHelper: '사이트를 활성화하면 사용자가 정상적으로 사이트 내용을 액세스할 수 있습니다. 계속하시겠습니까?',\n        sitePath: '디렉터리',\n        siteAlias: '사이트 별칭',\n        primaryPath: '루트 디렉터리',\n        folderTitle: '웹사이트는 다음과 같은 폴더를 포함합니다.',\n        wafFolder: '방화벽 규칙',\n        indexFolder: '웹사이트 루트 디렉터리',\n        sslFolder: '웹사이트 인증서',\n        enableOrNot: '활성화 여부',\n        oldSSL: '기존 인증서',\n        manualSSL: '인증서 가져오기',\n        select: '선택',\n        selectSSL: '인증서 선택',\n        privateKey: '키(KEY)',\n        certificate: '인증서(PEM 형식)',\n        HTTPConfig: 'HTTP 옵션',\n        HTTPSOnly: 'HTTP 요청 차단',\n        HTTPToHTTPS: 'HTTPS로 리디렉션',\n        HTTPAlso: 'HTTP 요청 허용',\n        sslConfig: 'SSL 옵션',\n        disableHTTPS: 'HTTPS 비활성화',\n        disableHTTPSHelper: 'HTTPS 비활성화는 인증서 관련 구성을 삭제합니다. 계속하시겠습니까?',\n        SSLHelper:\n            '주의: 불법 웹사이트에는 SSL 인증서를 사용하지 마세요.\\nHTTPS 액세스가 안 되는 경우 보안 그룹에서 443 포트를 올바르게 해제했는지 확인하세요.',\n        SSLConfig: '인증서 설정',\n        SSLProConfig: '프로토콜 설정',\n        supportProtocol: '프로토콜 버전',\n        encryptionAlgorithm: '암호화 알고리즘',\n        notSecurity: '(안전하지 않음)',\n        encryptHelper:\n            \"Let's Encrypt 는 인증서 발급에 빈도 제한이 있지만 일반적인 요구 사항을 충족하기에 충분합니다. 너무 자주 작업하면 발급 실패가 발생할 수 있습니다. 자세한 제한 사항은 <a target='_blank' href='https://letsencrypt.org/zh-cn/docs /rate-limits/'>공식 문서</a>를 참조하세요.\",\n        ipValue: '값',\n        ext: '파일 확장자',\n        wafInputHelper: '줄 단위로 데이터 입력, 한 줄에 하나씩',\n        data: '데이터',\n        ever: '영구',\n        nextYear: '1년 후',\n        noLog: '로그를 찾을 수 없습니다.',\n        defaultServer: '기본 사이트',\n        noDefaultServer: '설정되지 않음',\n        defaultServerHelper:\n            '기본 사이트를 설정한 후, 바인딩되지 않은 모든 도메인 이름과 IP는 기본 사이트로 리디렉션됩니다\\n이는 악의적인 해석을 효과적으로 방지할 수 있습니다\\n하지만 WAF의 무단 도메인 차단이 실패할 수도 있습니다',\n        restoreHelper: '이 백업을 사용하여 복원하시겠습니까?',\n        websiteDeploymentHelper: '설치된 애플리케이션을 사용하거나 새 애플리케이션을 생성하여 웹사이트를 만드세요.',\n        websiteStatictHelper: '호스트에 웹사이트 디렉터리를 생성합니다.',\n        websiteProxyHelper:\n            '리버스 프록시를 사용하여 기존 서비스를 프록시합니다. 예를 들어, 포트 8080 에서 실행 중인 서비스를 프록시하려면 프록시 주소는 \"http://127.0.0.1:8080\"이 됩니다.',\n        runtimeProxyHelper: '웹사이트 런타임을 사용하여 웹사이트를 만드세요.',\n        runtime: '런타임',\n        deleteRuntimeHelper: '런타임 애플리케이션은 웹사이트와 함께 삭제해야 하므로 신중하게 처리하세요.',\n        proxyType: '네트워크 유형',\n        unix: '유닉스 네트워크',\n        tcp: 'TCP/IP 네트워크',\n        phpFPM: 'FPM 구성',\n        phpConfig: 'PHP 구성',\n        updateConfig: '구성 업데이트',\n        isOn: '켜짐',\n        isOff: '꺼짐',\n        rewrite: '의사 정적',\n        rewriteMode: '방식',\n        current: '현재',\n        rewriteHelper: '의사 정적 설정으로 인해 웹사이트에 접근할 수 없게 되면 기본 설정으로 되돌려보세요.',\n        runDir: '실행 디렉터리',\n        runUserHelper:\n            'PHP 컨테이너 런타임 환경에서 배포된 웹사이트의 경우, 인덱스 및 하위 디렉터리 아래의 모든 파일 및 폴더의 소유자와 사용자 그룹을 1000으로 설정해야 합니다. 로컬 PHP 환경의 경우, 로컬 PHP-FPM 사용자 및 사용자 그룹 설정을 참조하세요.',\n        userGroup: '사용자/그룹',\n        uGroup: '그룹',\n        proxyPath: '프록시 경로',\n        proxyPass: '대상 URL',\n        cache: '캐시',\n        cacheTime: '캐시 지속 시간',\n        enableCache: '캐시 활성화',\n        proxyHost: '프록시 호스트',\n        disabled: '중지됨',\n        startProxy: '리버스 프록시를 시작합니다. 계속하시겠습니까?',\n        stopProxy: '리버스 프록시를 중지합니다. 계속하시겠습니까?',\n        sourceFile: '소스',\n        proxyHelper1: '이 디렉터리에 접근할 때 대상 URL 의 내용이 반환되고 표시됩니다.',\n        proxyPassHelper: '대상 URL 은 유효하고 접근 가능해야 합니다.',\n        proxyHostHelper: '요청 헤더에 있는 도메인 이름을 프록시 서버로 전달합니다.',\n        replacementHelper: '최대 5개의 교체를 추가할 수 있으며, 교체가 필요하지 않은 경우 비워두세요.',\n        modifier: '매칭 규칙',\n        modifierHelper: '예: = 는 정확히 일치, ~ 는 정규식 일치, ^~ 는 경로 시작 부분 일치 등을 나타냅니다.',\n        replace: '텍스트 교체',\n        replaceHelper:\n            'nginx 텍스트 교체 기능은 리버스 프록시 중 응답 내용의 문자열을 대체할 수 있습니다. 백엔드에서 반환된 HTML, CSS, JavaScript 및 기타 파일의 링크, API 주소 등을 수정하는 데 일반적으로 사용됩니다. 복잡한 콘텐츠 교체 요구 사항에 대해 정규식 일치를 지원합니다.',\n        addReplace: '추가',\n        replaced: '검색 문자열 (비울 수 없음)',\n        replaceText: '교체할 문자열',\n        replacedErr: '검색 문자열은 비워둘 수 없습니다',\n        replacedErr2: '검색 문자열은 중복될 수 없습니다',\n        replacedListEmpty: '텍스트 교체 규칙 없음',\n        proxySslName: '프록시 SNI 이름',\n        basicAuth: '기본 인증',\n        editBasicAuthHelper:\n            '비밀번호는 비대칭으로 암호화되어 표시할 수 없습니다. 수정하려면 비밀번호를 재설정해야 합니다.',\n        antiLeech: '링크 차단',\n        extends: '확장',\n        browserCache: '캐시',\n        noModify: '수정 안 함',\n        serverCache: '서버 캐시',\n        leechLog: '링크 차단 로그 기록',\n        accessDomain: '허용된 도메인',\n        leechReturn: '응답 리소스',\n        noneRef: '빈 참조 허용',\n        disable: '비활성화',\n        disableLeechHelper: '링크 차단을 비활성화할지 여부',\n        disableLeech: '링크 차단 비활성화',\n        ipv6: 'IPv6 수신 대기',\n        leechReturnError: 'HTTP 상태 코드를 입력하세요',\n        blockedRef: '비표준 참조 허용',\n        accessControl: '링크 차단 제어',\n        leechcacheControl: '캐시 제어',\n        logEnableControl: '정적 리소스 요청 로그 기록',\n        leechSpecialValidHelper:\n            \"'빈 참조 허용'을 활성화하면 리퍼러가 없는 요청(직접 접근 등)은 차단되지 않습니다. '비표준 참조 허용'을 활성화하면 http/https로 시작하지 않는 모든 리퍼러 요청(클라이언트 요청 등)을 허용합니다.\",\n        leechInvalidReturnHelper: '차단된 요청에 대해 반환할 HTTP 상태 코드',\n        leechlogControlHelper:\n            '정적 리소스 요청을 기록합니다. 운영 환경에서는 과도하고 불필요한 로그를 피하기 위해 보통 비활성화합니다',\n        selectAcme: 'Acme 계정 선택',\n        imported: '수동으로 생성됨',\n        importType: '가져오기 유형',\n        pasteSSL: '코드 붙여넣기',\n        localSSL: '서버 파일 선택',\n        privateKeyPath: '개인 키 파일',\n        certificatePath: '인증서 파일',\n        ipWhiteListHelper: 'IP 허용 목록의 역할: 모든 규칙이 IP 허용 목록에 대해 무효화됩니다.',\n        redirect: '리디렉션',\n        sourceDomain: '소스 도메인',\n        targetURL: '대상 URL 주소',\n        keepPath: 'URI 매개변수',\n        path: '경로',\n        redirectType: '리디렉션 유형',\n        redirectWay: '방식',\n        keep: '유지',\n        notKeep: '유지하지 않음',\n        redirectRoot: '홈페이지로 리디렉션',\n        redirectHelper: '301 영구 리디렉션, 302 임시 리디렉션',\n        changePHPVersionWarn:\n            'PHP 버전을 변경하면 기존 PHP 컨테이너가 삭제됩니다 (마운트된 웹사이트 코드는 손실되지 않습니다). 계속하시겠습니까?',\n        changeVersion: '버전 전환',\n        retainConfig: 'php-fpm.conf 및 php.ini 파일을 유지할지 여부',\n        runDirHelper2: '보조 실행 디렉터리가 인덱스 디렉터리 아래에 있는지 확인하세요.',\n        openrestyHelper:\n            'OpenResty 기본 HTTP 포트: {0}, HTTPS 포트: {1}, 이는 웹사이트 도메인 접속 및 HTTPS 강제 리디렉션에 영향을 미칠 수 있습니다.',\n        primaryDomainHelper: '예: example.com 또는 example.com:8080',\n        acmeAccountType: '계정 유형',\n        keyType: '키 알고리즘',\n        tencentCloud: '텐센트 클라우드',\n        containWarn: '도메인 이름에 메인 도메인이 포함되어 있습니다. 다시 입력하세요.',\n        rewriteHelper2:\n            '앱 스토어에서 설치된 WordPress 와 같은 응용 프로그램은 일반적으로 사전 설정된 가상 정적 구성이 포함됩니다. 이를 재구성하면 오류가 발생할 수 있습니다.',\n        websiteBackupWarn:\n            '로컬 백업 가져오기만 지원합니다. 다른 기기에서 가져온 백업은 복구 실패를 초래할 수 있습니다.',\n        ipWebsiteWarn:\n            'IP를 도메인 이름으로 사용하는 웹사이트는 정상적으로 접속되기 위해 기본 사이트로 설정해야 합니다.',\n        hstsHelper: 'HSTS 를 활성화하면 웹사이트 보안을 강화할 수 있습니다.',\n        includeSubDomains: '서브도메인',\n        hstsIncludeSubDomainsHelper: '활성화하면 HSTS 정책이 현재 도메인의 모든 서브도메인에 적용됩니다.',\n        defaultHtml: '기본 페이지',\n        website404: '웹사이트 404 오류 페이지',\n        domain404: '웹사이트 도메인이 존재하지 않습니다.',\n        indexHtml: '정적 웹사이트 인덱스',\n        stopHtml: '중지된 웹사이트',\n        indexPHP: 'PHP 웹사이트 인덱스',\n        sslExpireDate: '인증서 만료 날짜',\n        website404Helper: '웹사이트 404 오류 페이지는 PHP 실행 환경 웹사이트 및 정적 웹사이트만 지원합니다.',\n        sni: '원본 SNI',\n        sniHelper:\n            '역방향 프록시 백엔드가 HTTPS 인 경우 원본 SNI 를 설정해야 할 수 있습니다. 자세한 내용은 CDN 서비스 제공자의 문서를 참조하세요.',\n        huaweicloud: '화웨이 클라우드',\n        createDb: '데이터베이스 생성',\n        enableSSLHelper: 'SSL 활성화 실패는 웹사이트 생성에 영향을 미치지 않습니다.',\n        batchAdd: '도메인 일괄 추가',\n        batchInput: '일괄 입력',\n        domainNotFQDN: '이 도메인은 공용 네트워크에서 액세스할 수 없을 수 있습니다',\n        domainInvalid: '도메인 형식이 올바르지 않습니다',\n        domainBatchHelper: '한 줄에 하나의 도메인, 형식: domain:port@ssl\\n예: example.com:443@ssl 또는 example.com',\n        generateDomain: '생성',\n        global: '글로벌',\n        subsite: '하위 사이트',\n        subsiteHelper: '하위 사이트는 기존 PHP 또는 정적 웹사이트의 디렉토리를 루트 디렉토리로 선택할 수 있습니다.',\n        parentWebsite: '상위 웹사이트',\n        deleteSubsite: '현재 웹사이트를 삭제하려면 먼저 하위 사이트 {0}를 삭제해야 합니다.',\n        loadBalance: '로드 밸런싱',\n        server: '노드',\n        algorithm: '알고리즘',\n        ipHash: 'IP 해시',\n        ipHashHelper:\n            '클라이언트 IP 주소를 기반으로 요청을 특정 서버에 분배하여 특정 클라이언트가 항상 동일한 서버로 라우팅되도록 합니다.',\n        leastConn: '최소 연결',\n        leastConnHelper: '활성 연결 수가 가장 적은 서버로 요청을 보냅니다.',\n        leastTime: '최소 시간',\n        leastTimeHelper: '활성 연결 시간이 가장 짧은 서버로 요청을 보냅니다.',\n        defaultHelper:\n            '기본 방법으로, 요청은 각 서버에 균등하게 분배됩니다. 서버에 가중치 설정이 있는 경우 지정된 가중치에 따라 요청이 분배됩니다. 가중치가 높은 서버는 더 많은 요청을 받습니다.',\n        weight: '가중치',\n        maxFails: '최대 실패 횟수',\n        maxConns: '최대 연결 수',\n        strategy: '전략',\n        strategyDown: '비활성화',\n        strategyBackup: '백업',\n        ipHashBackupErr: 'IP 해시는 백업 노드를 지원하지 않습니다',\n        failTimeout: '장애 시간 초과',\n        failTimeoutHelper:\n            '서버 상태 점검 시간 창 길이. 이 기간 동안 누적 실패 횟수가 임계값에 도달하면 서버가 일시적으로 제거되고 동일한 시간 후에 재시도됩니다. 기본값 10초',\n        staticChangePHPHelper: '현재 정적 웹사이트이며 PHP 웹사이트로 전환할 수 있습니다.',\n        proxyCache: '리버스 프록시 캐시',\n        cacheLimit: '캐시 공간 제한',\n        shareCache: '캐시 카운트 메모리 크기',\n        cacheExpire: '캐시 만료 시간',\n        shareCacheHelper: '1M 메모리로 약 8000개의 캐시 객체를 저장할 수 있습니다.',\n        cacheLimitHelper: '제한을 초과하면 이전 캐시가 자동으로 삭제됩니다.',\n        cacheExpireHelper: '만료 시간 내에 히트되지 않은 캐시는 삭제됩니다.',\n        realIP: '실제 IP',\n        ipFrom: 'IP 소스',\n        ipFromHelper:\n            '신뢰할 수 있는 IP 소스를 구성함으로써 OpenResty는 HTTP 헤더의 IP 정보를 분석하여 방문자의 실제 IP 주소를 정확하게 식별하고 기록합니다(액세스 로그 포함).',\n        ipFromExample1: '프론트엔드가 Frp와 같은 도구인 경우 Frp의 IP 주소(예: 127.0.0.1)를 입력할 수 있습니다.',\n        ipFromExample2: '프론트엔드가 CDN인 경우 CDN의 IP 범위를 입력할 수 있습니다.',\n        ipFromExample3:\n            '확실하지 않은 경우 0.0.0.0/0 (IPv4) 또는 ::/0 (IPv6)를 입력할 수 있습니다. [주의: 모든 소스 IP를 허용하는 것은 안전하지 않습니다.]',\n        http3Helper:\n            'HTTP/3는 HTTP/2의 업그레이드 버전으로, 더 빠른 연결 속도와 더 나은 성능을 제공합니다. 그러나 모든 브라우저가 HTTP/3를 지원하는 것은 아니며, 활성화하면 일부 브라우저가 사이트에 접근하지 못할 수 있습니다.',\n        cors: '교차 출처 리소스 공유(CORS)',\n        enableCors: 'CORS 활성화',\n        allowOrigins: '허용된 도메인',\n        allowMethods: '허용된 요청 메서드',\n        allowHeaders: '허용된 요청 헤더',\n        allowCredentials: '쿠키 전송 허용',\n        preflight: '프리플라이트 요청 빠른 응답',\n        preflightHleper:\n            '활성화하면 브라우저가 교차 출처 프리플라이트 요청(OPTIONS 요청)을 보낼 때 시스템이 자동으로 204 상태 코드를 반환하고 필요한 교차 출처 응답 헤더를 설정합니다',\n        changeDatabase: '데이터베이스 전환',\n        changeDatabaseHelper1: '데이터베이스 연관은 웹사이트 백업 및 복원에 사용됩니다.',\n        changeDatabaseHelper2: '다른 데이터베이스로 전환하면 이전 백업을 복원할 수 없게 될 수 있습니다.',\n        saveCustom: '템플릿으로 저장',\n        rainyun: 'Rainyun',\n        volcengine: 'volcengine',\n        runtimePortHelper: '현재 실행 환경에 여러 포트가 있습니다. 프록시 포트를 선택하세요.',\n        runtimePortWarn: '현재 실행 환경에 포트가 없습니다. 프록시할 수 없습니다',\n        cacheWarn: '먼저 리버스 프록시의 캐시 스위치를 끄십시오',\n        loadBalanceHelper:\n            '로드 밸런싱을 생성한 후, \"리버스 프록시\"로 이동하여 프록시를 추가하고 백엔드 주소를 다음으로 설정하세요: http://<로드 밸런싱 이름>.',\n        favorite: '즐겨찾기',\n        cancelFavorite: '즐겨찾기 취소',\n        useProxy: '프록시 사용',\n        useProxyHelper: '패널 설정의 프록시 서버 주소 사용',\n        westCN: '서부 디지털',\n        openBaseDir: '사이트 간 공격 방지',\n        openBaseDirHelper:\n            'open_basedir는 PHP 파일 액세스 경로를 제한하여 사이트 간 액세스를 방지하고 보안을 향상시키는 데 사용됩니다',\n        serverCacheTime: '서버 캐시 시간',\n        serverCacheTimeHelper:\n            '요청이 서버에서 캐시되는 시간. 이 기간 동안 동일한 요청은 원본 서버에 요청하지 않고 캐시된 결과를 직접 반환합니다.',\n        browserCacheTime: '브라우저 캐시 시간',\n        browserCacheTimeHelper:\n            '정적 리소스가 브라우저 로컬에 캐시되는 시간, 중복 요청을 줄입니다. 유효기간 전에 사용자가 페이지를 새로 고치면 로컬 캐시가 직접 사용됩니다.',\n        donotLinkeDB: '데이터베이스 연결하지 않기',\n        toWebsiteDir: '웹사이트 디렉토리로 이동',\n        execParameters: '실행 매개변수',\n        extCommand: '추가 명령',\n        mirror: '미러 소스',\n        execUser: '실행 사용자',\n        execDir: '실행 디렉토리',\n        packagist: '중국 전체 미러',\n        batchOperate: '일괄 작업',\n        batchOperateHelper: '웹사이트를 일괄 {0}, 계속 작업하시겠습니까?',\n        stream: 'TCP/UDP 프록시',\n        streamPorts: '수신 포트',\n        streamPortsHelper:\n            '외부 수신 포트 번호를 설정합니다. 클라이언트는 이 포트를 통해 서비스에 액세스합니다. 쉼표로 구분하세요. 예: 5222,5223',\n        streamHelper: 'TCP/UDP 포트 포워딩 및 로드 밸런싱',\n        udp: 'UDP 활성화',\n        syncHtmlHelper: 'PHP 및 정적 웹사이트에 동기화',\n        wafValueHelper: '값',\n        parentWbeiste: '상위 웹사이트',\n        shareCahe: '캐시 수 메모리 크기',\n        shareCaheHelper: '메모리 1M당 약 8000개의 캐시 객체를 저장할 수 있습니다',\n        cacheExpireJHelper: '만료 후 미적중 시 캐시가 삭제됩니다',\n    },\n    php: {\n        short_open_tag: '짧은 태그 지원',\n        max_execution_time: '최대 스크립트 실행 시간',\n        max_input_time: '최대 입력 시간',\n        memory_limit: '스크립트 메모리 제한',\n        post_max_size: 'POST 데이터 최대 크기',\n        file_uploads: '파일 업로드 허용 여부',\n        upload_max_filesize: '업로드 가능한 파일의 최대 크기',\n        max_file_uploads: '한 번에 업로드 가능한 파일의 최대 개수',\n        default_socket_timeout: '소켓 타임아웃',\n        error_reporting: '에러 수준',\n        display_errors: '상세한 에러 정보 출력 여부',\n        cgi_fix_pathinfo: 'pathinfo 활성화 여부',\n        date_timezone: '시간대',\n        disableFunction: '비활성화 함수',\n        disableFunctionHelper: '비활성화할 함수를 입력하세요. 예: exec, 여러 항목은 쉼표로 구분',\n        uploadMaxSize: '업로드 제한',\n        indexHelper: 'PHP 웹사이트의 정상 작동을 위해 코드를 인덱스 디렉터리에 배치하고 이름 변경을 피하세요.',\n        extensions: '확장 템플릿',\n        extension: '확장',\n        extensionHelper: '여러 확장은 쉼표로 구분하여 입력하세요.',\n        toExtensionsList: '확장 목록 보기',\n        containerConfig: '컨테이너 구성',\n        containerConfigHelper: '환경 변수 및 기타 정보는 생성 후 구성 - 컨테이너 구성에서 수정할 수 있습니다',\n        dateTimezoneHelper: '예: TZ=Asia/Shanghai（필요에 따라 추가하세요）',\n        extensionsHelper: '여러 확장은 쉼표로 구분하세요',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: '서버 이름의 해시 테이블 크기',\n        clientHeaderBufferSizeHelper: '클라이언트가 요청한 헤더 버퍼 크기',\n        clientMaxBodySizeHelper: '최대 업로드 파일 크기',\n        keepaliveTimeoutHelper: '연결 시간 초과',\n        gzipMinLengthHelper: '최소 압축 파일 크기',\n        gzipCompLevelHelper: '압축률',\n        gzipHelper: '전송을 위한 압축 활성화',\n        connections: '활성 연결',\n        accepts: '수락',\n        handled: '처리됨',\n        requests: '요청',\n        reading: '읽기 중',\n        writing: '쓰기 중',\n        waiting: '대기 중',\n        status: '현재 상태',\n        configResource: '구성',\n        saveAndReload: '저장 및 다시 로드',\n        clearProxyCache: '리버스 프록시 캐시 삭제',\n        clearProxyCacheWarn:\n            '캐시가 구성된 모든 웹사이트에 영향을 미치며 OpenResty 가 다시 시작됩니다. 계속하시겠습니까?',\n        create: '모듈 추가',\n        update: '모듈 편집',\n        params: '매개변수',\n        packages: '패키지',\n        script: '스크립트',\n        module: '모듈',\n        build: '빌드',\n        buildWarn: 'OpenResty 빌드는 CPU와 메모리의 일정량을 예약해야 하며, 시간이 오래 걸릴 수 있으니 기다려 주세요.',\n        mirrorUrl: '소프트웨어 소스',\n        paramsHelper: '예: --add-module=/tmp/ngx_brotli',\n        packagesHelper: '예: git,curl 쉼표로 구분',\n        scriptHelper: '컴파일 전에 실행할 스크립트, 일반적으로 모듈 소스 코드 다운로드, 종속성 설치 등',\n        buildHelper: '모듈 추가/수정 후 빌드를 클릭하세요. 빌드가 성공하면 OpenResty가 자동으로 재시작됩니다.',\n        defaultHttps: 'HTTPS 변조 방지',\n        defaultHttpsHelper1: '이를 활성화하면 HTTPS 변조 문제를 해결할 수 있습니다.',\n        sslRejectHandshake: '기본 SSL 핸드셰이크 거부',\n        sslRejectHandshakeHelper:\n            '활성화하면 인증서 누출을 방지할 수 있지만, 기본 웹사이트를 설정하면 이 설정이 무효화됩니다',\n    },\n    ssl: {\n        create: '요청',\n        provider: '유형',\n        manualCreate: '수동 생성됨',\n        acmeAccount: 'ACME 계정',\n        resolveDomain: '도메인 이름 확인',\n        err: '오류',\n        value: '레코드 값',\n        dnsResolveHelper: 'DNS 해석 서비스 제공업체에서 다음 해석 레코드를 추가하세요:',\n        detail: '세부 정보',\n        msg: '정보',\n        ssl: '인증서',\n        key: '개인 키',\n        startDate: '유효 시작 시간',\n        organization: '발급 기관',\n        renewConfirm: '도메인 이름 {0}에 대해 새 인증서를 갱신합니다. 계속하시겠습니까?',\n        autoRenew: '자동 갱신',\n        autoRenewHelper: '만료 30일 전에 자동으로 갱신',\n        renewSuccess: '갱신 성공',\n        renewWebsite: '이 인증서는 다음 웹사이트에 연결되었으며, 적용은 이러한 웹사이트에 동시에 적용됩니다.',\n        createAcme: '계정 생성',\n        acmeHelper: 'Acme 계정은 무료 인증서를 신청하는 데 사용됩니다.',\n        upload: '가져오기',\n        applyType: '유형',\n        apply: '갱신',\n        applyStart: '인증서 신청 시작',\n        getDnsResolve: 'DNS 해석 값을 가져오는 중입니다. 잠시 기다려주세요...',\n        selfSigned: '자체 서명된 CA',\n        ca: '인증 기관',\n        commonName: '공통 이름',\n        caName: '인증 기관 이름',\n        company: '기관 이름',\n        department: '조직 단위 이름',\n        city: '지역 이름',\n        province: '주 또는 지방 이름',\n        country: '국가 이름 (2자리 코드)',\n        commonNameHelper: '예를 들어, ',\n        selfSign: '인증서 발급',\n        days: '유효 기간',\n        domainHelper: '줄당 하나의 도메인 이름, * 및 IP 주소 지원',\n        pushDir: '로컬 디렉토리로 인증서 푸시',\n        dir: '디렉토리',\n        pushDirHelper: '인증서 파일 \"fullchain.pem\" 및 키 파일 \"privkey.pem\"이 이 디렉토리에 생성됩니다.',\n        organizationDetail: '조직 세부 정보',\n        fromWebsite: '웹사이트에서 가져오기',\n        dnsMauanlHelper: '수동 해석 모드에서는 생성 후 신청 버튼을 클릭하여 DNS 해석 값을 얻어야 합니다.',\n        httpHelper:\n            'HTTP 모드를 사용하려면 OpenResty를 설치해야 하며, 와일드카드 도메인 인증서 신청을 지원하지 않습니다.',\n        buypassHelper: 'Buypass 는 중국 본토에서 접근할 수 없습니다.',\n        googleHelper: 'EAB HmacKey 및 EAB kid 를 얻는 방법',\n        googleCloudHelper: 'Google Cloud API 는 중국 본토 대부분에서 접근할 수 없습니다.',\n        skipDNSCheck: 'DNS 확인 건너뛰기',\n        skipDNSCheckHelper: '인증 요청 중 타임아웃 문제가 발생할 경우에만 선택하세요.',\n        cfHelper: 'Global API Key 를 사용하지 마세요.',\n        deprecated: '더 이상 지원되지 않습니다.',\n        deprecatedHelper:\n            '유지 관리가 중단되었으며 향후 버전에서 제외될 수 있습니다. Tencent Cloud 방법을 사용하여 분석하세요.',\n        disableCNAME: 'CNAME 비활성화',\n        disableCNAMEHelper: '도메인 이름에 CNAME 레코드가 있고 요청이 실패할 경우 선택하세요.',\n        nameserver: 'DNS 서버',\n        nameserverHelper: '사용자 지정 DNS 서버를 사용하여 도메인 이름을 확인합니다.',\n        edit: '인증서 편집',\n        execShell: '인증 요청 후 스크립트 실행',\n        shell: '스크립트 내용',\n        shellHelper:\n            '스크립트의 기본 실행 디렉토리는 1Panel 설치 디렉토리입니다. 인증서가 로컬 디렉토리에 푸시되는 경우 실행 디렉토리는 인증서 푸시 디렉토리가 됩니다. 기본 실행 제한 시간은 30분입니다.',\n        customAcme: '사용자 정의 ACME 서비스',\n        customAcmeURL: 'ACME 서비스 URL',\n        baiduCloud: '바이두 클라우드',\n        pushNode: '다른 노드에 동기화',\n        pushNodeHelper: '신청/갱신 후 선택한 노드로 푸시',\n        fromMaster: '마스터 노드에서 푸시',\n        hostedZoneID: 'Hosted Zone ID',\n        isIP: 'IP 인증서',\n        useEAB: 'EAB 인증 사용',\n    },\n    firewall: {\n        create: '규칙 만들기',\n        edit: '규칙 수정',\n        ccDeny: 'CC 보호',\n        ipWhiteList: 'IP 허용 목록',\n        ipBlockList: 'IP 차단 목록',\n        fileExtBlockList: '파일 확장자 차단 목록',\n        urlWhiteList: 'URL 허용 목록',\n        urlBlockList: 'URL 차단 목록',\n        argsCheck: 'GET 파라미터 검사',\n        postCheck: 'POST 파라미터 검사',\n        cookieBlockList: '쿠키 차단 목록',\n        dockerHelper:\n            '현재 방화벽은 컨테이너 포트 매핑을 비활성화할 수 없습니다. 설치된 애플리케이션은 [설치됨] 페이지에서 애플리케이션 매개변수를 편집하고 포트 해제 규칙을 구성할 수 있습니다.',\n        iptablesHelper:\n            '시스템이 {0} 방화벽을 사용 중인 것으로 감지되었습니다. iptables로 전환하려면 먼저 수동으로 제거하세요!',\n        used: '사용됨',\n        unUsed: '사용 안 함',\n        dockerRestart: '방화벽 작업에는 Docker 서비스 재시작이 필요합니다',\n        firewallHelper: '{0} 시스템 방화벽',\n        firewallNotStart: '현재 시스템 방화벽이 활성화되지 않았습니다. 먼저 활성화하세요.',\n        restartFirewallHelper: '이 작업은 현재 방화벽을 재시작합니다. 계속하시겠습니까?',\n        stopFirewallHelper: '이 작업은 서버 보안을 잃게 만듭니다. 계속하시겠습니까?',\n        startFirewallHelper: '방화벽이 활성화되면 서버 보안이 강화됩니다. 계속하시겠습니까?',\n        noPing: 'Ping 비활성화',\n        enableBanPing: 'Ping 차단',\n        disableBanPing: 'Ping 차단 해제',\n        noPingTitle: 'Ping 비활성화',\n        noPingHelper: '이 작업은 Ping 을 비활성화하며 서버는 ICMP 응답을 보내지 않게 됩니다. 계속하시겠습니까?',\n        onPingHelper: '이 작업은 Ping 을 활성화하여 해커가 서버를 발견할 수 있습니다. 계속하시겠습니까?',\n        changeStrategy: '{0} 전략 변경',\n        changeStrategyIPHelper1:\n            'IP 주소 전략을 [거부]로 변경합니다. 설정 후 해당 IP 주소는 서버 접근이 차단됩니다. 계속하시겠습니까?',\n        changeStrategyIPHelper2:\n            'IP 주소 전략을 [허용]으로 변경합니다. 설정 후 해당 IP 주소는 정상적으로 접근할 수 있습니다. 계속하시겠습니까?',\n        changeStrategyPortHelper1:\n            '포트 정책을 [차단]으로 변경합니다. 설정 후 외부 접근이 차단됩니다. 계속하시겠습니까?',\n        changeStrategyPortHelper2:\n            '포트 정책을 [허용]으로 변경합니다. 설정 후 정상적으로 포트 접근이 복원됩니다. 계속하시겠습니까?',\n        stop: '정지',\n        portFormatError: '이 필드는 유효한 포트이어야 합니다.',\n        portHelper1: '여러 포트, 예: 8080, 8081',\n        portHelper2: '포트 범위, 예: 8080-8089',\n        changeStrategyHelper:\n            '[{1}] {0} 전략을 [{2}]로 변경합니다. 설정 후 {0}은(는) {2}로 외부 접근을 허용합니다. 계속하시겠습니까?',\n        strategy: '전략',\n        accept: '허용',\n        drop: '차단',\n        anyWhere: '어디든지',\n        address: '지정된 IP',\n        addressHelper: 'IP 주소 또는 IP 범위를 지원합니다.',\n        allow: '허용',\n        deny: '거부',\n        addressFormatError: '이 필드는 유효한 IP 주소여야 합니다.',\n        addressHelper1: \"IP 주소 또는 IP 범위가 필요합니다. 예: '172.16.10.11' 또는 '172.16.10.0/24'.\",\n        addressHelper2: \"여러 IP 주소는 쉼표로 구분합니다. 예: '172.16.10.11, 172.16.0.0/24'.\",\n        allIP: '모든 IP',\n        portRule: '규칙 | 규칙들',\n        forwardRule: '포트 전달 규칙 | 포트 전달 규칙들',\n        ipRule: 'IP 규칙 | IP 규칙들',\n        userAgent: 'User-Agent 필터',\n        sourcePort: '소스 포트',\n        targetIP: '대상 IP',\n        targetPort: '대상 포트',\n        forwardHelper1: \"로컬 포트로 전달하려면, 대상 IP 를 '127.0.0.1'로 설정해야 합니다.\",\n        forwardHelper2: '대상 IP 를 비워두면 로컬 포트로 전달됩니다.',\n        forwardPortHelper: '포트 범위를 지원합니다, 예: 8080-8089',\n        forwardInboundInterface: '포워딩 인바운드 네트워크 인터페이스',\n        exportHelper: '{0}개의 방화벽 규칙을 내보내려고 합니다. 계속하시겠습니까?',\n        importSuccess: '{0}개의 규칙을 성공적으로 가져왔습니다',\n        importPartialSuccess: '가져오기 완료: 성공 {0}건, 실패 {1}건',\n        ipv4Limit: '현재 작업은 IPv4 주소만 지원합니다',\n        basicStatus: '현재 체인 {0}이(가) 바인딩되지 않았습니다. 먼저 바인딩하세요!',\n        baseIptables: 'iptables 서비스',\n        forwardIptables: 'iptables 포트 포워딩 서비스',\n        advanceIptables: 'iptables 고급 구성 서비스',\n        initMsg: '{0}을(를) 초기화하려고 합니다. 계속하시겠습니까?',\n        initHelper: '{0}이(가) 초기화되지 않았습니다. 상단 상태 표시줄의 초기화 버튼을 클릭하여 구성하세요!',\n        bindHelper: '바인딩 - 방화벽 규칙은 상태가 바인딩된 경우에만 효과가 있습니다. 확인하시겠습니까?',\n        unbindHelper:\n            '바인딩 해제 - 바인딩 해제 시 추가된 모든 방화벽 규칙이 무효화됩니다. 주의하여 진행하세요. 확인하시겠습니까?',\n        defaultStrategy: '현재 체인 {0}의 기본 정책은 {1}입니다',\n        defaultStrategy2:\n            '현재 체인 {0}의 기본 정책은 {1}입니다. 현재 상태는 바인딩되지 않았습니다. 추가된 방화벽 규칙은 바인딩 후에 효과가 발생합니다!',\n        filterRule: '필터 규칙',\n        filterHelper:\n            '필터 규칙을 사용하면 INPUT/OUTPUT 수준에서 네트워크 트래픽을 제어할 수 있습니다. 시스템 잠금을 방지하기 위해 주의하여 구성하세요.',\n        chain: '체인',\n        targetChain: '대상 체인',\n        sourceIP: '소스 IP',\n        destIP: '대상 IP',\n        inboundDirection: '인바운드 방향',\n        outboundDirection: '아웃바운드 방향',\n        destPort: '대상 포트',\n        action: '동작',\n        reject: '거부',\n        sourceIPHelper: 'CIDR 형식, 예: 192.168.1.0/24. 모든 주소의 경우 비워 둠',\n        destIPHelper: 'CIDR 형식, 예: 10.0.0.0/8. 모든 주소의 경우 비워 둠',\n        portHelper: '0은 모든 포트를 의미합니다',\n        allPorts: '모든 포트',\n        deleteRuleConfirm: '{0}개의 규칙을 삭제합니다. 계속하시겠습니까?',\n        advancedControl: '고급 제어',\n        advancedControlNotAvailable: '현재 {0} 방화벽 사용 중이며, 고급 규칙은 iptables만 지원합니다',\n        quickJump: '빠른 이동',\n        destination: '대상',\n    },\n    runtime: {\n        runtime: '실행 환경',\n        workDir: '작업 디렉토리',\n        localHelper: '로컬 환경 설치 및 오프라인 환경 사용 관련 문제는 다음을 참조하세요 ',\n        versionHelper: 'PHP 버전, 예: v8.0',\n        buildHelper:\n            '확장 기능이 많을수록 이미지 생성 시 CPU 사용량이 증가합니다. 환경 생성 후 확장 기능을 설치하는 것도 가능합니다.',\n        openrestyWarn: 'PHP는 OpenResty 버전 1.21.4.1 이상으로 업그레이드해야 사용 가능합니다.',\n        toupgrade: '업그레이드하기',\n        edit: '실행 환경 수정',\n        extendHelper:\n            '목록에 없는 확장 프로그램은 수동으로 입력하고 선택할 수 있습니다. 예를 들어 \"sockets\"를 입력한 후 드롭다운 목록에서 첫 번째 옵션을 선택하여 확장 목록을 확인하세요.',\n        rebuildHelper: '확장 기능을 수정한 후에는 PHP 애플리케이션을 재빌드해야 적용됩니다.',\n        rebuild: 'PHP 애플리케이션 재빌드',\n        source: 'PHP 확장 소스',\n        ustc: '중국과학기술대학',\n        netease: '네티이즈',\n        aliyun: '알리바바 클라우드',\n        default: '기본',\n        tsinghua: '칭화대학교',\n        xtomhk: 'XTOM 미러 사이트 (홍콩)',\n        xtom: 'XTOM 미러 사이트 (전 세계)',\n        phpsourceHelper: '네트워크 환경에 맞는 적절한 소스를 선택하세요.',\n        appPort: '앱 포트',\n        externalPort: '외부 포트',\n        packageManager: '패키지 관리자',\n        codeDir: '코드 디렉터리',\n        appPortHelper: '애플리케이션이 사용하는 포트.',\n        externalPortHelper: '외부에 노출된 포트.',\n        runScript: '실행 스크립트',\n        runScriptHelper: '시작 명령 목록은 소스 디렉터리의 package.json 파일에서 분석됩니다.',\n        open: '열기',\n        operatorHelper: '{0} 작업이 선택된 운영 환경에서 수행됩니다. 계속하시겠습니까?',\n        taobao: '타오바오',\n        tencent: '텐센트',\n        imageSource: '이미지 소스',\n        moduleManager: '모듈 관리',\n        module: '모듈',\n        nodeOperatorHelper:\n            '{0} {1} 모듈인가요? 이 작업은 운영 환경에 비정상을 일으킬 수 있으므로 진행 전에 확인해 주세요.',\n        customScript: '사용자 정의 시작 명령',\n        customScriptHelper:\n            '전체 시작 명령어를 입력해 주세요. 예: npm run start. PM2 시작 명령어는 pm2-runtime로 변경해 주세요. 그렇지 않으면 시작에 실패합니다.',\n        portError: '포트를 중복 사용하지 마세요.',\n        systemRestartHelper: '상태 설명: 중단 - 시스템 재시작으로 인해 상태 가져오기가 실패했습니다.',\n        javaScriptHelper: '전체 시작 명령을 제공하세요. 예: \"java -jar halo.jar -Xmx1024M -Xms256M\".',\n        javaDirHelper: '디렉터리는 jar 파일을 포함해야 하며, 하위 디렉터리도 허용됩니다.',\n        goHelper: '전체 시작 명령을 제공하세요. 예: \"go run main.go\" 또는 \"./main\".',\n        goDirHelper: '디렉터리 또는 하위 디렉터리는 Go 또는 바이너리 파일을 포함해야 합니다.',\n        pythonHelper:\n            '전체 시작 명령을 제공하세요. 예: \"pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000\".',\n        dotnetHelper: '완전한 시작 명령을 입력하세요. 예: dotnet MyWebApp.dll',\n        dirHelper: '주의: 컨테이너 내의 디렉토리 경로를 입력하세요',\n        concurrency: '동시성 체계',\n        loadStatus: '부하 상태',\n        extraHosts: '호스트 매핑',\n        extension: '확장',\n        installExtension: '확장 {0} 설치를 확인하시겠습니까',\n        loadedExtension: '로드된 확장',\n        popularExtension: '인기 확장',\n        uninstallExtension: '확장 {0}을(를) 제거하시겠습니까',\n        phpConfigHelper: '설정 변경 후 운영 환경 재시작이 필요합니다. 계속하시겠습니까',\n        operateMode: '운영 모드',\n        dynamic: '동적',\n        static: '고정',\n        ondemand: '온디맨드',\n        dynamicHelper: '프로세스 수를 동적으로 조정합니다. 트래픽 변동이 크거나 메모리가 적은 사이트에 적합합니다',\n        staticHelper: '프로세스 수를 고정합니다. 고동시성·안정 트래픽 사이트에 적합하지만 자원 소모가 큽니다',\n        ondemandHelper: '필요 시 프로세스를 시작/종료합니다. 자원 효율은 좋지만 초기 응답이 느릴 수 있습니다',\n        max_children: '생성 가능한 최대 프로세스 수',\n        start_servers: '시작 시 생성되는 프로세스 수',\n        min_spare_servers: '최소 유휴 프로세스 수',\n        max_spare_servers: '최대 유휴 프로세스 수',\n        envKey: '이름',\n        envValue: '값',\n        environment: '환경 변수',\n    },\n    process: {\n        pid: '프로세스 ID',\n        ppid: '부모 PID',\n        numThreads: '스레드',\n        memory: '메모리',\n        diskRead: '디스크 읽기',\n        diskWrite: '디스크 쓰기',\n        netSent: '업링크',\n        netRecv: '다운스트림',\n        numConnections: '연결',\n        startTime: '시작 시간',\n        state: '상태',\n        running: '실행 중',\n        sleep: '대기 중',\n        stop: '중지',\n        idle: '유휴',\n        zombie: '좀비 프로세스',\n        wait: '대기',\n        lock: '잠금',\n        blocked: '차단됨',\n        cmdLine: '시작 명령',\n        basic: '기본',\n        mem: '메모리',\n        openFiles: '열린 파일',\n        env: '환경 변수',\n        noenv: '없음',\n        net: '네트워크 연결',\n        laddr: '출발지 주소/포트',\n        raddr: '목적지 주소/포트',\n        stopProcess: '종료',\n        viewDetails: '세부 사항',\n        stopProcessWarn: '이 프로세스(PID:{0})를 종료하시겠습니까?',\n        kill: '프로세스 종료',\n        killNow: '즉시 종료',\n        killHelper:\n            '프로세스 {0}을(를) 종료하면 일부 프로그램이 정상적으로 작동하지 않을 수 있습니다. 계속하시겠습니까?',\n        processName: '프로세스 이름',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: '프로세스 상태를 가져오지 못했습니다. supervisor 서비스의 상태를 확인하세요.',\n            notSupport: 'Supervisor 서비스를 감지하지 못했습니다. 스크립트 라이브러리 페이지에서 수동으로 설치하세요',\n            list: '데몬 프로세스 | 데몬 프로세스들',\n            config: 'Supervisor 설정',\n            primaryConfig: '주 설정 파일 위치',\n            notSupportCtl: 'supervisorctl 이 감지되지 않았습니다.  스크립트 라이브러리 페이지에서 수동으로 설치하세요.',\n            user: '사용자',\n            command: '명령어',\n            dir: '디렉토리',\n            numprocs: '프로세스 수',\n            initWarn:\n                '이 작업은 주 설정 파일의 \"[include]\" 섹션에 있는 \"files\" 값을 수정합니다. 다른 설정 파일의 디렉토리는 \"{1Panel 설치 디렉토리}/1panel/tools/supervisord/supervisor.d/\"입니다.',\n            operatorHelper: '{1} 작업을 {0}에서 수행합니다. 계속하시겠습니까?',\n            uptime: '운영 시간',\n            notStartWarn: 'Supervisor 가 시작되지 않았습니다. 먼저 시작하세요.',\n            serviceName: '서비스 이름',\n            initHelper:\n                'Supervisor 서비스가 감지되었지만 초기화되지 않았습니다. 상단 상태 표시줄의 초기화 버튼을 클릭하여 구성하세요.',\n            serviceNameHelper: 'systemctl 로 관리되는 Supervisor 서비스 이름, 보통 supervisor 또는 supervisord입니다.',\n            restartHelper:\n                '이 작업은 초기화 후 서비스를 재시작합니다. 이로 인해 기존의 모든 데몬 프로세스가 중지됩니다.',\n            RUNNING: '실행 중',\n            STOPPED: '중지됨',\n            STOPPING: '중지 중',\n            STARTING: '시작 중',\n            FATAL: '시작 실패',\n            BACKOFF: '시작 예외',\n            ERROR: '오류',\n            statusCode: '상태 코드',\n            manage: '관리',\n            autoRestart: '자동 재시작',\n            EXITED: '종료됨',\n            autoRestartHelper: '프로그램이 비정상적으로 종료된 후 자동으로 재시작할지 여부',\n            autoStart: '자동 시작',\n            autoStartHelper: 'Supervisor 시작 후 서비스를 자동으로 시작할지 여부',\n        },\n    },\n    disk: {\n        management: '디스크 관리',\n        partition: '파티션',\n        unmount: '마운트 해제',\n        unmountHelper: '파티션 {0} 을(를) 마운트 해제하시겠습니까?',\n        mount: '마운트',\n        partitionAlert:\n            '디스크 파티션 작업은 디스크 포맷이 필요하며, 기존 데이터는 삭제됩니다. 데이터를 미리 저장하거나 스냅샷을 찍어주세요.',\n        mountPoint: '마운트 디렉토리',\n        systemDisk: '시스템 디스크',\n        unpartitionedDisk: '미파티션 디스크',\n        handlePartition: '지금 파티션',\n        filesystem: '파일 시스템',\n        unmounted: '마운트 해제됨',\n        cannotOperate: '작업 불가',\n        systemDiskHelper: '힌트: 현재 디스크는 시스템 디스크입니다. 작업할 수 없습니다.',\n        autoMount: '자동 마운트',\n        model: '장치 모델',\n        diskType: '디스크 유형',\n        serial: '시리얼 번호',\n        noFail: '마운트 실패는 시스템 시작에 영향을 미치지 않습니다',\n    },\n    xpack: {\n        expiresTrialAlert:\n            '친절한 알림: 귀하의 Pro 체험판이 {0}일 후 만료되며, 모든 Pro 기능에 더 이상 접근할 수 없습니다. 제때 갱신하거나 전체 버전으로 업그레이드하시기 바랍니다.',\n        expiresAlert:\n            '친절한 알림: 귀하의 Pro 라이선스가 {0}일 후 만료되며, 모든 Pro 기능에 더 이상 접근할 수 없습니다. 지속적인 사용을 위해 신속하게 갱신하시기 바랍니다.',\n        menu: 'Pro',\n        upage: 'AI 웹사이트 빌더',\n        proAlert: '이 기능을 사용하려면 Pro로 업그레이드하세요',\n        app: {\n            app: 'APP',\n            title: '패널 별칭',\n            titleHelper: '패널 별칭은 APP 에서 표시되는 데 사용됩니다(기본 패널 별칭)',\n            qrCode: 'QR 코드',\n            apiStatusHelper: '패널 APP 는 API 인터페이스 기능을 활성화해야 합니다',\n            apiInterfaceHelper: '패널 API 인터페이스 액세스를 지원합니다(패널 앱에서 이 기능을 활성화해야 합니다)',\n            apiInterfaceHelper1:\n                '패널 앱의 액세스에는 방문자를 화이트리스트에 추가해야 하며, 고정 IP가 아닌 경우 0.0.0.0/0(모든 IPv4), ::/0(모든 IPv6) 을 추가하는 것이 좋습니다',\n            qrCodeExpired: '새로 고침 시간',\n            apiLeakageHelper: 'QR 코드를 누출하지 마십시오. 신뢰할 수 있는 환경에서만 사용하십시오.',\n        },\n        waf: {\n            WAF: 'WAF',\n            blackWhite: '블랙 및 화이트 목록',\n            globalSetting: '전역 설정',\n            websiteSetting: '웹사이트 설정',\n            blockRecords: '차단된 기록',\n            world: '전 세계',\n            china: '중국',\n            intercept: '차단',\n            request: '요청',\n            count4xx: '4xx 수량',\n            count5xx: '5xx 수량',\n            todayStatus: '오늘의 상태',\n            reqMap: '공격 맵 (최근 30일)',\n            resource: '출처',\n            count: '수량',\n            hight: '높음',\n            low: '낮음',\n            reqCount: '요청 수',\n            interceptCount: '차단 수',\n            requestTrends: '요청 트렌드 (최근 7일)',\n            interceptTrends: '차단 트렌드 (최근 7일)',\n            whiteList: '화이트 목록',\n            blackList: '블랙 목록',\n            ipBlackListHelper: '블랙 목록에 있는 IP 주소는 웹사이트에 접근할 수 없습니다.',\n            ipWhiteListHelper: '화이트 목록에 있는 IP 주소는 모든 제한을 우회합니다.',\n            uaBlackListHelper: '블랙 목록에 있는 User-Agent 값의 요청은 차단됩니다.',\n            uaWhiteListHelper: '화이트 목록에 있는 User-Agent 값의 요청은 모든 제한을 우회합니다.',\n            urlBlackListHelper: '블랙 목록에 있는 URL 로의 요청은 차단됩니다.',\n            urlWhiteListHelper: '화이트 목록에 있는 URL 로의 요청은 모든 제한을 우회합니다.',\n            ccHelper: '같은 IP 에서 {0}초 이내에 {1}회 이상 요청을 받으면 해당 IP 는 {2}동안 차단됩니다.',\n            blockTime: '차단 기간',\n            attackHelper: '누적 차단이 {0}초 이내에 {1}회를 초과하면 해당 IP 는 {2}동안 차단됩니다.',\n            notFoundHelper: '누적된 요청이 {0}초 이내에 404 오류를 {1}번 이상 반환하면 해당 IP 는 {2}동안 차단됩니다.',\n            frequencyLimit: '접속 빈도 제한',\n            regionLimit: '지역 제한',\n            defaultRule: '기본 규칙',\n            accessFrequencyLimit: '접속 빈도 제한',\n            attackLimit: '공격 빈도 제한',\n            notFoundLimit: '404 빈도 제한',\n            urlLimit: 'URL 빈도 제한',\n            urlLimitHelper: '단일 URL 의 접속 빈도를 설정합니다.',\n            sqliDefense: 'SQL 인젝션 보호',\n            sqliHelper: '요청에서 SQL 인젝션을 감지하여 차단합니다.',\n            xssHelper: '요청에서 XSS 를 감지하여 차단합니다.',\n            xssDefense: 'XSS 보호',\n            uaDefense: '악성 User-Agent 규칙',\n            uaHelper: '일반적인 악성 봇을 식별하는 규칙이 포함되어 있습니다.',\n            argsDefense: '악성 파라미터 규칙',\n            argsHelper: '악성 파라미터를 포함한 요청을 차단합니다.',\n            cookieDefense: '악성 쿠키 규칙',\n            cookieHelper: '악성 쿠키가 요청에 포함되지 않도록 차단합니다.',\n            headerDefense: '악성 헤더 규칙',\n            headerHelper: '악성 헤더가 포함된 요청을 차단합니다.',\n            httpRule: 'HTTP 요청 방법 규칙',\n            httpHelper:\n                '허용된 접근 방식 유형을 설정합니다. 특정 접근 방식을 제한하려면 해당 유형의 버튼을 끄십시오. 예를 들어: GET 방식만 허용하려면 GET 을 제외한 다른 방식의 버튼을 끄세요.',\n            geoRule: '지역별 접근 제한',\n            geoHelper:\n                '특정 지역에서만 웹사이트 접근을 허용하고 다른 지역에서의 접근을 차단할 수 있습니다. 예를 들어: 중국 본토에서만 접근을 허용하고, 그 외 지역에서의 접근을 차단할 수 있습니다.',\n            ipLocation: 'IP 위치',\n            action: '동작',\n            ruleType: '공격 유형',\n            ipHelper: 'IP 주소를 입력하세요',\n            attackLog: '공격 로그',\n            rule: '규칙',\n            ipArr: 'IPV4 범위',\n            ipStart: '시작 IP',\n            ipEnd: '끝 IP',\n            ipv4: 'IPv4',\n            ipv6: 'IPv6',\n            urlDefense: 'URL 규칙',\n            urlHelper: '금지된 URL',\n            dirFilter: '디렉터리 필터',\n            sqlInject: 'SQL 인젝션',\n            xss: 'XSS',\n            phpExec: 'PHP 스크립트 실행',\n            oneWordTrojan: '단어 트로이',\n            appFilter: '위험한 디렉터리 필터링',\n            webShell: '웹쉘',\n            args: '악성 파라미터',\n            protocolFilter: '프로토콜 필터',\n            javaFilter: '자바 위험 파일 필터링',\n            scannerFilter: '스캐너 필터',\n            escapeFilter: '이스케이프 필터',\n            customRule: '사용자 정의 규칙',\n            httpMethod: 'HTTP 메소드 필터',\n            fileExt: '파일 업로드 제한',\n            fileExtHelper: '업로드 제한 파일 확장자',\n            deny: '금지',\n            allow: '허용',\n            field: '객체',\n            pattern: '조건',\n            ruleContent: '내용',\n            contain: '포함',\n            equal: '같음',\n            regex: '정규 표현식',\n            notEqual: '같지 않음',\n            customRuleHelper: '지정된 조건에 따라 동작을 수행합니다.',\n            actionAllow: '허용',\n            blockIP: 'IP 차단',\n            code: '상태 코드 반환',\n            noRes: '연결 끊기 (444)',\n            badReq: '잘못된 매개변수 (400)',\n            forbidden: '접근 금지 (403)',\n            serverErr: '서버 오류 (500)',\n            resHtml: '응답 페이지',\n            allowHelper: '허용된 접근은 후속 WAF 규칙을 건너뛰게 하므로 주의해서 사용하세요.',\n            captcha: '사람-컴퓨터 검증',\n            fiveSeconds: '5초 검증',\n            location: '지역',\n            redisConfig: 'Redis 설정',\n            redisHelper: 'Redis 를 활성화하여 일시적으로 차단된 IP를 유지합니다.',\n            wafHelper: 'WAF 를 종료하면 모든 웹사이트 보호가 해제됩니다.',\n            attackIP: '공격 IP',\n            attackParam: '공격 세부사항',\n            execRule: '적용된 규칙',\n            acl: 'ACL',\n            sql: 'SQL 인젝션',\n            cc: '접속 빈도 제한',\n            isBlocking: '차단됨',\n            isFree: '차단 해제됨',\n            unLock: '차단 해제',\n            unLockHelper: 'IP: {0}의 차단을 해제하시겠습니까?',\n            saveDefault: '기본값 저장',\n            saveToWebsite: '웹사이트에 적용',\n            saveToWebsiteHelper: '현재 설정을 모든 웹사이트에 적용하시겠습니까?',\n            websiteHelper: '웹사이트를 생성할 때의 기본 설정입니다. 수정 사항은 웹사이트에 적용해야 효력이 있습니다.',\n            websiteHelper2: '웹사이트 생성 시 기본 설정이 제공됩니다. 웹사이트에서 해당 설정을 수정해 주세요.',\n            ipGroup: 'IP 그룹',\n            ipGroupHelper:\n                '한 줄에 하나의 IP 또는 IP 범위를 입력하세요, IPv4 및 IPv6 을 지원합니다. 예: 192.168.1.1 또는 192.168.1.0/24',\n            ipBlack: 'IP 블랙리스트',\n            openRestyAlert: 'OpenResty 버전은 {0} 이상이어야 합니다.',\n            initAlert:\n                '처음 사용 시 초기화가 필요하며, 웹사이트 설정 파일이 수정됩니다. 기존 WAF 설정은 손실될 수 있습니다. 미리 백업해 주세요.',\n            initHelper: '초기화 작업은 기존 WAF 설정을 삭제합니다. 초기화하시겠습니까?',\n            mainSwitch: '메인 스위치',\n            websiteAlert: '먼저 웹사이트를 생성해 주세요.',\n            defaultUrlBlack: 'URL 규칙',\n            htmlRes: '차단 페이지',\n            urlSearchHelper: 'URL 을 입력하여 유사 검색을 지원합니다.',\n            toCreate: '생성',\n            closeWaf: 'WAF 종료',\n            closeWafHelper: 'WAF 를 종료하면 웹사이트 보호가 해제됩니다. 계속하시겠습니까?',\n            addblack: '블랙 추가',\n            addwhite: '화이트 추가',\n            addblackHelper: 'IP: {0}를 기본 블랙리스트에 추가하시겠습니까?',\n            addwhiteHelper: 'IP: {0}를 기본 화이트리스트에 추가하시겠습니까?',\n            defaultUaBlack: 'User-Agent 규칙',\n            defaultIpBlack: '악성 IP 그룹',\n            cookie: '쿠키 규칙',\n            urlBlack: 'URL 블랙리스트',\n            uaBlack: 'User-Agent 블랙리스트',\n            attackCount: '공격 빈도 제한',\n            fileExtCheck: '파일 업로드 제한',\n            geoRestrict: '지역 접근 제한',\n            attacklog: '차단 기록',\n            unknownWebsite: '허가되지 않은 도메인 접근',\n            geoRuleEmpty: '지역은 비워 둘 수 없습니다.',\n            unknown: '웹사이트 없음',\n            geo: '지역 제한',\n            revertHtml: '{0}을 기본 페이지로 복원하시겠습니까?',\n            five_seconds: '5초 검증',\n            header: '헤더 규칙',\n            methodWhite: 'HTTP 규칙',\n            expiryDate: '만료 날짜',\n            expiryDateHelper: '검증을 통과한 후 유효 기간 내에는 더 이상 검증하지 않습니다.',\n            defaultIpBlackHelper: '인터넷에서 수집된 일부 악성 IP로 접근을 차단합니다.',\n            notFoundCount: '404 빈도 제한',\n            matchValue: '매칭 값',\n            headerName: '이 필드는 특수 문자로 시작할 수 없으며,영어, 숫자, -, 길이 3-30 지원',\n            cdnHelper: 'CDN 을 사용하는 웹사이트는 여기서 원본 IP를 확인할 수 있습니다.',\n            clearLogWarn: '로그를 삭제하면 복구할 수 없습니다. 계속하시겠습니까?',\n            commonRuleHelper: '규칙은 유사 매칭 방식입니다.',\n            blockIPHelper:\n                '차단된 IP는 OpenResty 에 일시적으로 저장되며, OpenResty 재시작 시 차단이 해제됩니다. 차단 기능을 통해 영구적으로 차단할 수 있습니다.',\n            addWhiteUrlHelper: 'URL {0}를 화이트리스트에 추가하시겠습니까?',\n            dashHelper: '커뮤니티 버전은 전역 설정 및 웹사이트 설정에서 기능을 사용할 수 있습니다.',\n            wafStatusHelper: 'WAF 가 활성화되지 않았습니다. 전역 설정에서 활성화해 주세요.',\n            ccMode: '모드',\n            global: '전역 모드',\n            uriMode: 'URL 모드',\n            globalHelper: '전역 모드: 특정 시간 내에 모든 URL에 대한 요청 수가 임계값을 초과하면 트리거됩니다.',\n            uriModeHelper: 'URL 모드: 특정 시간 내에 개별 URL에 대한 요청 수가 임계값을 초과하면 트리거됩니다.',\n            ip: 'IP 블랙리스트',\n            globalSettingHelper:\n                '[웹사이트] 태그가 있는 설정은 [웹사이트 설정]에서 활성화해야 하며, 전역 설정은 새로 생성된 웹사이트의 기본 설정입니다.',\n            globalSettingHelper2: '[전역 설정]과 [웹사이트 설정]에서 모두 활성화해야 설정이 적용됩니다.',\n            urlCCHelper: '{0} 초 이내에 이 URL에 대해 {1} 회를 초과하는 요청이 있어 이 IP를 차단합니다 {2}',\n            urlCCHelper2: 'URL에 매개변수를 포함할 수 없습니다',\n            notContain: '포함하지 않음',\n            urlcc: 'URL 빈도 제한',\n            method: '요청 유형',\n            addIpsToBlock: 'IP 일괄 차단',\n            addUrlsToWhite: 'URL을 일괄 허용 목록에 추가',\n            noBlackIp: 'IP가 이미 차단되어 있으므로 다시 차단할 필요가 없습니다',\n            noWhiteUrl: 'URL이 이미 허용 목록에 포함되어 있으므로 다시 추가할 필요가 없습니다',\n            spiderIpHelper:\n                '바이두, 빙, 구글, 360, 신마, 소구, 바이트댄스, DuckDuckGo, Yandex가 포함됩니다. 이를 끄면 모든 스파이더의 접근이 차단됩니다.',\n            spiderIp: '스파이더 IP 풀',\n            geoIp: 'IP 주소 라이브러리',\n            geoIpHelper: 'IP의 지리적 위치를 확인하는 데 사용됩니다',\n            stat: '공격 보고서',\n            statTitle: '보고서',\n            attackIp: '공격 IP',\n            attackCountNum: '공격 횟수',\n            percent: '비율',\n            addblackUrlHelper: 'URL: {0}을(를) 기본 블랙리스트에 추가할까요?',\n            rce: '원격 코드 실행',\n            software: '소프트웨어',\n            cveHelper: '일반적인 소프트웨어 및 프레임워크의 취약점을 포함',\n            vulnCheck: '보충 규칙',\n            ssrf: 'SSRF 취약점',\n            afr: '임의 파일 읽기',\n            ua: '무단 액세스',\n            id: '정보 누출',\n            aa: '인증 우회',\n            dr: '디렉토리 순회',\n            xxe: 'XXE 취약점',\n            suid: '직렬화 취약점',\n            dos: '서비스 거부 취약점',\n            afd: '임의 파일 다운로드',\n            sqlInjection: 'SQL 인젝션',\n            afw: '임의 파일 쓰기',\n            il: '정보 유출',\n            clearAllLog: '모든 로그 삭제',\n            exportLog: '로그 내보내기',\n            appRule: '애플리케이션 규칙',\n            appRuleHelper:\n                '일반적인 애플리케이션 규칙. 활성화하면 오탐지를 줄일 수 있습니다. 하나의 웹사이트는 하나의 규칙만 사용할 수 있습니다',\n            logExternal: '기록 유형 제외',\n            ipWhite: 'IP 허용 목록',\n            urlWhite: 'URL 허용 목록',\n            uaWhite: '사용자 에이전트 허용 목록',\n            logExternalHelper:\n                '제외된 기록 유형은 로그에 기록되지 않습니다. 블랙리스트/허용 목록, 지역 액세스 제한, 사용자 정의 규칙은 많은 로그를 생성합니다. 제외를 권장합니다',\n            ssti: 'SSTI 공격',\n            crlf: 'CRLF 인젝션',\n            strict: '엄격 모드',\n            strictHelper: '더 엄격한 규칙을 사용하여 요청을 검증합니다',\n            saveLog: '로그 저장',\n            remoteURLHelper: '원격 URL은 한 줄에 하나의 IP만 포함하고 다른 문자는 포함하지 않아야 합니다',\n            notFound: 'Not Found (404)',\n            serviceUnavailable: '서비스 불가 (503)',\n            gatewayTimeout: '게이트웨이 시간 초과 (504)',\n            belongToIpGroup: 'IP 그룹에 속함',\n            notBelongToIpGroup: 'IP 그룹에 속하지 않음',\n            unknownWebsiteKey: '알 수 없는 도메인',\n            special: '특수 문자',\n            fileToLarge: '파일이 1MB를 초과하여 업로드할 수 없습니다',\n            uploadOverLimit: '업로드된 파일 수가 제한을 초과했습니다, 최대 1개',\n            importRuleHelper: '한 줄에 하나의 규칙',\n            name: 'WAF 이름',\n        },\n        monitor: {\n            '360': '360 검색',\n            name: '웹사이트 모니터링',\n            pv: '페이지 조회수',\n            uv: '고유 방문자 수',\n            flow: '트래픽 흐름',\n            ip: 'IP',\n            spider: '스파이더',\n            visitors: '방문자 추세',\n            today: '오늘',\n            last7days: '지난 7일',\n            last30days: '지난 30일',\n            uvMap: '방문자 지도 (30일)',\n            qps: '실시간 요청 (분당)',\n            flowSec: '실시간 트래픽 (분당)',\n            excludeCode: '상태 코드 제외',\n            excludeUrl: 'URL 제외',\n            excludeExt: '확장자 제외',\n            cdnHelper: 'CDN 제공 헤더에서 실제 IP 가져오기',\n            reqRank: '방문 순위',\n            refererDomain: '참조 도메인',\n            os: '운영 체제',\n            browser: '브라우저/클라이언트',\n            device: '장치',\n            showMore: '더보기',\n            unknown: '기타',\n            pc: '컴퓨터',\n            mobile: '모바일 장치',\n            wechat: '위챗',\n            machine: '머신',\n            tencent: '텐센트 브라우저',\n            ucweb: 'UC 브라우저',\n            '2345explorer': '2345 브라우저',\n            huaweibrowser: '화웨이 브라우저',\n            log: '요청 로그',\n            statusCode: '상태 코드',\n            requestTime: '응답 시간',\n            flowRes: '응답 트래픽',\n            method: '요청 메서드',\n            statusCodeHelper: '상태 코드를 입력하세요',\n            statusCodeError: '유효하지 않은 상태 코드 유형',\n            methodHelper: '요청 메서드를 입력하세요',\n            all: '전체',\n            baidu: '바이두',\n            google: '구글',\n            bing: '빙',\n            bytes: '오늘의 헤드라인',\n            sogou: '소구',\n            failed: '오류',\n            ipCount: 'IP 수',\n            spiderCount: '스파이더 요청',\n            averageReqTime: '평균 응답 시간',\n            totalFlow: '총 트래픽',\n            logSize: '로그 파일 크기',\n            realIPType: '실제 IP 가져오기 방법',\n            fromHeader: 'HTTP 헤더에서 가져오기',\n            fromHeaders: '헤더 목록에서 가져오기',\n            header: 'HTTP 헤더',\n            cdnConfig: 'CDN 구성',\n            xff1: 'X-Forwarded-For 의 1단계 프록시',\n            xff2: 'X-Forwarded-For 의 2단계 프록시',\n            xff3: 'X-Forwarded-For 의 3단계 프록시',\n            xffHelper:\n                '예: X-Forwarded-For: <client>,<proxy1>,<proxy2>,<proxy3>. 최상위 프록시는 마지막 IP <proxy3>를 가져옵니다',\n            headersHelper:\n                '일반적으로 사용되는 CDN HTTP 헤더에서 실제 IP를 가져오며, 사용 가능한 첫 번째 값을 선택합니다',\n            monitorCDNHelper: '웹사이트 모니터링을 위한 CDN 구성을 수정하면 WAF CDN 설정도 업데이트됩니다',\n            wafCDNHelper: 'WAF CDN 구성을 수정하면 웹사이트 모니터링 CDN 설정도 업데이트됩니다',\n            statusErr: '잘못된 상태 코드 형식',\n            shenma: '션마 검색',\n            duckduckgo: '덕덕고',\n            excludeUri: 'URI 제외',\n            top100Helper: '상위 100 개의 데이터를 표시합니다',\n            logSaveDay: '로그 보관 기간 (일)',\n            cros: '크롬 OS',\n            theworld: '더월드 브라우저',\n            edge: '마이크로소프트 엣지',\n            maxthon: '맥스톤 브라우저',\n            monitorStatusHelper: '모니터링이 활성화되지 않았습니다. 설정에서 활성화하세요',\n            excludeIp: 'IP 주소 제외',\n            excludeUa: '사용자 에이전트 제외',\n            remotePort: '원격 포트',\n            unknown_browser: '알 수 없음',\n            unknown_os: '알 수 없음',\n            unknown_device: '알 수 없음',\n            logSaveSize: '최대 로그 저장 크기',\n            logSaveSizeHelper: '이것은 단일 웹사이트의 로그 저장 크기입니다',\n            '360se': '360 보안 브라우저',\n            websites: '웹사이트 목록',\n            trend: '추세 통계',\n            reqCount: '요청 수',\n            uriHelper: '/test/* 또는 /*/index.php를 사용하여 Uri를 제외할 수 있습니다',\n        },\n        tamper: {\n            tamper: '웹사이트 변조 방지',\n            ignoreTemplate: '제외 템플릿',\n            protectTemplate: '보호 템플릿',\n            ignoreTemplateHelper:\n                '제외 내용을 입력하세요, Enter 또는 공백으로 구분합니다. (특정 디렉토리 ./log 또는 디렉토리 이름 tmp, 파일을 제외하려면 특정 파일 ./data/test.html을 입력해야 합니다)',\n            protectTemplateHelper:\n                '보호 내용을 입력하세요, Enter 또는 공백으로 구분합니다. (특정 파일 ./index.html, 파일 확장자 .html, 파일 유형 js, 디렉토리를 보호하려면 특정 디렉토리 ./log을 입력해야 합니다)',\n            templateContent: '템플릿 내용',\n            template: '템플릿',\n            saveTemplate: '템플릿으로 저장',\n            tamperHelper1:\n                '원클릭 배포 유형의 웹사이트의 경우 애플리케이션 디렉토리 변조 방지 기능을 활성화하는 것이 좋습니다. 웹사이트가 정상적으로 사용되지 않거나 백업/복원이 실패하는 경우 먼저 변조 방지 기능을 비활성화하세요.',\n            tamperHelper2:\n                '제외되지 않은 디렉토리에서 보호된 파일에 대한 읽기/쓰기, 삭제, 권한 및 소유자 수정 작업을 제한합니다',\n            tamperPath: '보호 디렉토리',\n            tamperPathEdit: '경로 수정',\n            log: '차단 로그',\n            totalProtect: '총 보호',\n            todayProtect: '오늘 보호',\n            templateRule: '길이 1-512, 이름에 {0} 등의 기호를 포함할 수 없습니다',\n            ignore: '제외',\n            ignoreHelper:\n                '제외 내용을 선택하거나 입력하세요, Enter 또는 공백으로 구분합니다. (특정 디렉토리 ./log 또는 디렉토리 이름 tmp, 파일을 제외하려면 특정 파일 ./data/test.html을 입력하거나 선택해야 합니다)',\n            protect: '보호',\n            protectHelper:\n                '보호 내용을 선택하거나 입력하세요, Enter 또는 공백으로 구분합니다. (특정 파일 ./index.html, 파일 확장자 .html, 파일 유형 js, 디렉토리를 보호하려면 특정 디렉토리 ./log을 입력하거나 선택해야 합니다)',\n            tamperHelper00: '제외와 보호는 상대 경로만 입력 가능합니다.',\n            tamperHelper01:\n                '변조 방지를 활성화한 후 시스템은 제외되지 않은 디렉토리에서 보호된 파일의 생성, 편집 및 삭제 작업을 제한합니다.',\n            tamperHelper02: '우선순위: 특정 경로 보호 > 특정 경로 제외 > 보호 > 제외',\n            tamperHelper03:\n                '모니터링 작업은 제외되지 않은 디렉토리만 대상으로 하며, 이러한 디렉토리에서 비보호 파일의 생성을 모니터링합니다.',\n            disableHelper: '다음 웹사이트의 변조 방지 기능을 비활성화하려고 합니다. 계속하시겠습니까?',\n            appendOnly: '추가 전용',\n            appendOnlyHelper:\n                '이 디렉토리 내 파일의 삭제 작업을 제한하며, 제외 디렉토리 또는 비보호 파일 추가만 허용합니다',\n            immutable: '불변',\n            immutableHelper: '이 파일의 편집, 삭제, 권한 및 소유자 수정 작업을 제한합니다',\n            onWatch: '감시',\n            onWatchHelper: '이 디렉토리 내 보호 파일 또는 비제외 디렉토리 생성에 대한 감시 및 가로채기를 수행합니다',\n            forceStop: '강제 종료',\n            forceStopHelper: '이 웹사이트 디렉토리의 변조 방지 기능을 강제로 비활성화하려고 합니다. 계속하시겠습니까?',\n        },\n        setting: {\n            setting: '패널 설정',\n            title: '패널 설명',\n            titleHelper: '사용자 로그인 페이지에 표시됩니다 (예: Linux 서버 운영 및 유지 관리 패널, 권장 길이: 8-15자)',\n            logo: '로고 (텍스트 없음)',\n            logoHelper: '메뉴가 축소되었을 때 관리 페이지의 왼쪽 상단에 표시됩니다 (권장 이미지 크기: 82px*82px)',\n            logoWithText: '로고 (텍스트 포함)',\n            logoWithTextHelper:\n                '메뉴가 확장되었을 때 관리 페이지의 왼쪽 상단에 표시됩니다 (권장 이미지 크기: 185px*55px)',\n            favicon: '웹사이트 아이콘',\n            faviconHelper: '웹사이트 아이콘 (권장 이미지 크기: 16px*16px)',\n            setDefault: '기본값 복원',\n            setHelper: '현재 설정이 저장됩니다. 계속하시겠습니까?',\n            setDefaultHelper: '모든 패널 설정이 기본값으로 복원됩니다. 계속하시겠습니까?',\n            logoGroup: '로고',\n            imageGroup: '이미지',\n            loginImage: '로그인 페이지 이미지',\n            loginImageHelper: '로그인 페이지에 표시됩니다 (권장 크기: 500*416px)',\n            loginBgType: '로그인 배경 유형',\n            loginBgImage: '로그인 배경 이미지',\n            loginBgImageHelper: '로그인 페이지의 배경 이미지로 표시됩니다 (권장 크기: 1920*1080px)',\n            loginBgColor: '로그인 배경 색상',\n            loginBgColorHelper: '로그인 페이지의 배경 색상으로 표시됩니다',\n            image: '이미지',\n            bgColor: '배경 색상',\n            loginGroup: '로그인 페이지',\n            loginBtnLinkColor: '버튼/링크 색상',\n            loginBtnLinkColorHelper: '로그인 페이지의 버튼/링크 색상으로 표시됩니다',\n        },\n        helper: {\n            wafTitle1: '차단 지도',\n            wafContent1: '지난 30일 동안의 차단 지역 분포를 표시합니다.',\n            wafTitle2: '지역별 접근 제한',\n            wafContent2: '지리적 위치에 따라 웹사이트 접근 소스를 제한합니다.',\n            wafTitle3: '사용자 지정 차단 페이지',\n            wafContent3: '요청이 차단된 후 표시할 사용자 지정 페이지를 생성합니다.',\n            wafTitle4: '사용자 지정 규칙 (ACL)',\n            wafContent4: '사용자 지정 규칙에 따라 요청을 차단합니다.',\n            tamperTitle1: '파일 무결성 모니터링',\n            tamperContent1: '핵심 파일, 스크립트 파일, 구성 파일을 포함한 웹사이트 파일의 무결성을 모니터링합니다.',\n            tamperTitle2: '실시간 스캔 및 감지',\n            tamperContent2: '웹사이트 파일 시스템을 실시간으로 스캔하여 비정상적이거나 변조된 파일을 감지합니다.',\n            tamperTitle3: '보안 권한 설정',\n            tamperContent3:\n                '적절한 권한 설정과 접근 제어 정책을 통해 웹사이트 파일 접근을 제한하여 잠재적 공격 면적을 줄입니다.',\n            tamperTitle4: '로그 기록 및 분석',\n            tamperContent4:\n                '파일 접근 및 작업 로그를 기록하여 관리자가 감사 및 분석을 수행할 수 있도록 하고, 잠재적 보안 위협을 식별합니다.',\n            settingTitle1: '사용자 정의 환영 메시지',\n            settingContent1: '1Panel 로그인 페이지에 사용자 정의 환영 메시지를 설정합니다.',\n            settingTitle2: '사용자 정의 로고',\n            settingContent2: '브랜드명이나 텍스트가 포함된 로고 이미지를 업로드할 수 있습니다.',\n            settingTitle3: '사용자 정의 웹사이트 아이콘',\n            settingContent3: '브라우저 기본 아이콘을 대체할 사용자 정의 아이콘을 업로드하여 사용자 경험을 개선합니다.',\n            monitorTitle1: '방문자 추세',\n            monitorContent1: '웹사이트 방문자 추세를 통계적으로 표시합니다.',\n            monitorTitle2: '방문자 지도',\n            monitorContent2: '웹사이트 방문자의 지리적 분포를 통계적으로 표시합니다.',\n            monitorTitle3: '접속 통계',\n            monitorContent3: '웹사이트 요청 정보(스파이더, 접속 장치, 요청 상태 등)에 대한 통계를 제공합니다.',\n            monitorTitle4: '실시간 모니터링',\n            monitorContent4: '웹사이트 요청 정보(요청 수, 트래픽 등)를 실시간으로 모니터링합니다.',\n            alertTitle1: 'SMS 알림',\n            alertContent1:\n                '서버 리소스 사용량 이상, 웹사이트 및 인증서 만료, 새로운 버전 업데이트, 비밀번호 만료 등의 문제가 발생하면 SMS 알림을 통해 사용자가 신속히 처리할 수 있도록 합니다.',\n            alertTitle2: '알림 로그',\n            alertContent2:\n                '사용자에게 알림 로그를 조회할 수 있는 기능을 제공하여 과거 알림 이벤트를 추적하고 분석할 수 있도록 합니다.',\n            alertTitle3: '알림 설정',\n            alertContent3:\n                '사용자에게 전화번호, 일일 푸시 빈도, 일일 푸시 시간 등을 사용자 정의할 수 있는 설정을 제공하여 보다 합리적인 푸시 알림을 설정할 수 있도록 합니다.',\n            nodeDashTitle1: '애플리케이션 관리',\n            nodeDashContent1:\n                '다중 노드 애플리케이션의 통합 관리, 상태 모니터링, 빠른 시작/중지, 터미널 연결 및 백업 지원',\n            nodeDashTitle2: '웹사이트 관리',\n            nodeDashContent2: '다중 노드 웹사이트의 통합 관리, 실시간 상태 모니터링, 일괄 시작/중지 및 빠른 백업 지원',\n            nodeDashTitle3: '데이터베이스 관리',\n            nodeDashContent3: '다중 노드 데이터베이스의 통합 관리, 주요 상태 한눈에 확인, 원클릭 백업 지원',\n            nodeDashTitle4: '예약 작업 관리',\n            nodeDashContent4: '다중 노드 예약 작업의 통합 관리, 상태 모니터링, 빠른 시작/중지 및 수동 트리거 실행 지원',\n            nodeTitle1: '원클릭 노드 추가',\n            nodeContent1: '여러 서버 노드를 빠르게 통합',\n            nodeTitle2: '일괄 업그레이드',\n            nodeContent2: '한 번의 작업으로 모든 노드를 동기화 및 업그레이드',\n            nodeTitle3: '노드 상태 모니터링',\n            nodeContent3: '각 노드의 운영 상태를 실시간으로 파악',\n            nodeTitle4: '빠른 원격 연결',\n            nodeContent4: '원클릭으로 노드 원격 터미널에 직접 연결',\n            fileExchangeTitle1: '키 인증 전송',\n            fileExchangeContent1: 'SSH 키를 통해 인증하여 전송 보안을 보장합니다.',\n            fileExchangeTitle2: '효율적인 파일 동기화',\n            fileExchangeContent2: '변경된 내용만 동기화하여 전송 속도와 안정성을 크게 향상시킵니다.',\n            fileExchangeTitle3: '다중 노드 상호 통신 지원',\n            fileExchangeContent3: '다른 노드 간에 프로젝트 파일을 쉽게 전송하고, 여러 서버를 유연하게 관리합니다.',\n            nodeAppTitle1: '애플리케이션 업그레이드 관리',\n            nodeAppContent1: '다중 노드 애플리케이션 업데이트 통합 모니터링, 원클릭 업그레이드 지원',\n            appTitle1: '유연한 패널 관리',\n            appContent1: '언제 어디서나 1Panel 서버를 쉽게 관리하세요.',\n            appTitle2: '종합적인 서비스 정보',\n            appContent2:\n                '모바일 앱에서 애플리케이션, 웹사이트, Docker, 데이터베이스 등의 기본 관리를 하고, 애플리케이션과 웹사이트의 빠른 생성을 지원합니다.',\n            appTitle3: '실시간 이상 모니터링',\n            appContent3:\n                '모바일 앱에서 서버 상태, WAF 보안 모니터링, 웹사이트 방문 통계 및 프로세스 건강 상태를 실시간으로 확인하세요.',\n            clusterTitle1: '마스터-슬레이브 배포',\n            clusterContent1:\n                '다른 노드에서 MySQL/Postgres/Redis 마스터-슬레이브 인스턴스를 생성하는 것을 지원하며, 자동으로 마스터-슬레이브 연결 및 초기화를 완료합니다',\n            clusterTitle2: '마스터-슬레이브 관리',\n            clusterContent2:\n                '통합 페이지에서 여러 마스터-슬레이브 노드를 중앙에서 관리하고, 역할, 실행 상태 등을 확인합니다',\n            clusterTitle3: '복제 상태',\n            clusterContent3:\n                '마스터-슬레이브 복제 상태 및 지연 정보를 표시하여 동기화 문제를 해결하는 데 도움을 줍니다',\n        },\n        node: {\n            master: '주 노드',\n            masterBackup: '마스터 노드 백업',\n            backupNode: '백업 노드',\n            backupFrequency: '백업 주기(시간)',\n            backupCopies: '백업 수',\n            noBackupNode: '현재 백업 노드가 비어 있습니다. 저장할 백업 노드를 선택한 후 다시 시도하십시오!',\n            masterBackupAlert:\n                '현재 마스터 노드 백업이 구성되지 않았습니다. 데이터 보안을 위해 장애 시 새로운 마스터 노드로 수동 전환이 가능하도록 가능한 빨리 백업 노드를 설정하십시오.',\n            node: '노드',\n            addr: '주소',\n            nodeUpgrade: '업데이트 설정',\n            nodeUpgradeHelper:\n                '선택된 노드는 마스터 노드 업그레이드 완료 후 자동으로 업그레이드를 시작하며 수동 작업이 필요하지 않습니다.',\n            nodeUnhealthy: '노드 상태 이상',\n            deletedNode: '삭제된 노드 {0}은(는) 현재 업그레이드 작업을 지원하지 않습니다!',\n            nodeUnhealthyHelper: '노드 상태 이상이 감지되었습니다. [노드 관리]에서 확인 후 다시 시도하세요!',\n            nodeUnbind: '노드가 라이선스에 바인딩되지 않음',\n            nodeUnbindHelper:\n                '이 노드가 라이선스에 바인딩되지 않은 것으로 감지되었습니다. [패널 설정 - 라이선스] 메뉴에서 바인딩 후 다시 시도하세요!',\n            memTotal: '총 메모리',\n            nodeManagement: '다중 머신 관리',\n            nodeItem: '노드 관리',\n            panelItem: '패널 관리',\n            addPanel: '패널 추가',\n            addPanelHelper: 'パネルの追加に成功後、[概要 - パネル]で対象パネルに迅速にアクセスできます。',\n            panel: '1Panel パネル',\n            others: 'その他のパネル',\n            addNode: '노드 추가',\n            connInfo: '연결 정보',\n            nodeInfo: '노드 정보',\n            withProxy: '프록시 액세스 활성화',\n            withoutProxy: '프록시 액세스 비활성화',\n            withProxyHelper:\n                '패널 설정에서 유지 관리되는 시스템 프록시 {0}을(를) 사용하여 자식 노드에 액세스합니다. 계속하시겠습니까?',\n            withoutProxyHelper:\n                '패널 설정에서 유지 관리되는 시스템 프록시를 사용하여 자식 노드에 액세스하는 것을 중지합니다. 계속하시겠습니까?',\n            syncInfo: '데이터 동기화,',\n            syncHelper: '마스터 노드 데이터가 변경되면, 이 자식 노드에 실시간으로 동기화됩니다,',\n            syncBackupAccount: '백업 계정 설정',\n            syncWithMaster:\n                '프로 버전으로 업그레이드 후 모든 데이터가 기본적으로 동기화됩니다. 노드 관리에서 동기화 정책을 수동으로 조정할 수 있습니다.',\n            syncProxy: '시스템 프록시 설정',\n            syncProxyHelper: '시스템 프록시 설정 동기화에는 Docker 재시작이 필요합니다',\n            syncProxyHelper1: 'Docker 재시작은 현재 실행 중인 컨테이너 서비스에 영향을 줄 수 있습니다.',\n            syncProxyHelper2: '컨테이너 - 설정 페이지에서 수동으로 재시작할 수 있습니다.',\n            syncProxyHelper3:\n                '시스템 프록시 설정 동기화에는 Docker 재시작이 필요하며, 현재 실행 중인 컨테이너 서비스에 영향을 줄 수 있습니다',\n            syncProxyHelper4:\n                '시스템 프록시 설정 동기화에는 Docker 재시작이 필요합니다. 나중에 컨테이너 - 설정 페이지에서 수동으로 재시작할 수 있습니다.',\n            syncCustomApp: '사용자 정의 앱 저장소 동기화',\n            syncAlertSetting: '시스템 경고 설정',\n            syncNodeInfo: '노드 기본 데이터,',\n            nodeSyncHelper: '노드 정보 동기화는 다음 정보를 동기화합니다:',\n            nodeSyncHelper1: '1. 공용 백업 계정 정보',\n            nodeSyncHelper2: '2. 주 노드와 하위 노드 간의 연결 정보',\n            nodeCheck: '가용성 확인',\n            checkSSH: '노드 SSH 연결 확인',\n            checkUserPermission: '노드 사용자 권한 확인',\n            isNotRoot:\n                '이 노드에서 비밀번호 없이 sudo를 사용할 수 없으며 현재 사용자가 root가 아닌 것으로 감지되었습니다',\n            checkLicense: '노드 라이선스 상태 확인',\n            checkService: '노드의 기존 서비스 정보 확인',\n            checkPort: '노드 포트 접근 가능 여부 확인',\n            panelExist:\n                '이 노드에서 1Panel V1 서비스가 실행 중인 것으로 감지되었습니다. 추가 전에 마이그레이션 스크립트로 V2로 업그레이드하십시오.',\n            coreExist:\n                '현재 노드가 마스터 노드로 활성화되어 있어 슬레이브 노드로 직접 추가할 수 없습니다. 추가하기 전에 먼저 슬레이브 노드로 다운그레이드하십시오. 자세한 내용은 문서를 참조하십시오.',\n            agentExist:\n                '이 노드에 1panel-agent가 이미 설치되어 있는 것으로 감지되었습니다. 계속하면 기존 데이터를 유지하고 1panel-agent 서비스만 교체됩니다.',\n            agentNotExist:\n                '이 노드에 1panel-agent가 설치되지 않아 노드 정보를 직접 편집할 수 없습니다. 삭제 후 다시 추가해 주세요.',\n            oldDataExist:\n                '이 노드에서 1Panel V2 기록 데이터가 감지되었습니다. 다음 정보를 사용하여 현재 설정을 덮어씁니다:',\n            errLicense: '이 노드에 바인딩된 라이선스를 사용할 수 없습니다. 확인 후 다시 시도하십시오!',\n            errNodePort:\n                '노드 포트 [ {0} ]에 접근할 수 없는 것으로 감지되었습니다. 방화벽 또는 보안 그룹에서 해당 포트가 허용되었는지 확인하십시오.',\n            reinstallHelper: '노드 {0}를 재설치합니다. 계속하시겠습니까?',\n            unhealthyCheck: '비정상 체크',\n            fixOperation: '수정 작업',\n            checkName: '체크 항목',\n            checkSSHConn: 'SSH 연결 가능성 확인',\n            fixSSHConn: '노드를 수동으로 편집하여 연결 정보를 확인합니다',\n            checkConnInfo: '에이전트 연결 정보 확인',\n            checkStatus: '노드 서비스 가용성 확인',\n            fixStatus: '\"systemctl status 1panel-agent.service\"를 실행하여 서비스가 실행 중인지 확인합니다.',\n            checkAPI: '노드 API 가용성 확인',\n            fixAPI: '노드 로그를 확인하고 방화벽 포트가 정상적으로 열려 있는지 확인합니다.',\n            forceDelete: '강제 삭제',\n            operateHelper: '다음 노드에 대해 {0} 작업을 수행합니다. 계속하시겠습니까?',\n            operatePanelHelper: '다음 패널에 대해 {0} 작업을 수행합니다. 계속하시겠습니까?',\n            forceDeleteHelper: '강제 삭제는 노드 삭제 오류를 무시하고 데이터베이스 메타데이터를 삭제합니다',\n            uninstall: '노드 데이터 삭제',\n            uninstallHelper: '이 작업은 노드의 모든 1Panel 관련 데이터를 삭제합니다. 신중하게 선택하세요!',\n            baseDir: '설치 디렉토리',\n            baseDirHelper: '설치 디렉토리가 비어 있으면 기본적으로 /opt 디렉토리에 설치됩니다',\n            nodePort: '노드 포트',\n            offline: '오프라인 모드',\n            freeCount: '무료 할당량 [{0}]',\n            offlineHelper: '노드가 오프라인 환경일 때 사용',\n            appUpgrade: '앱 업그레이드',\n            appUpgradeHelper: '업그레이드가 필요한 앱이 {0}개 있습니다',\n        },\n        customApp: {\n            name: '사용자 정의 앱 저장소',\n            appStoreType: '앱 스토어 패키지 소스',\n            appStoreUrl: '저장소 URL',\n            local: '로컬 경로',\n            remote: '원격 링크',\n            imagePrefix: '이미지 접두사',\n            imagePrefixHelper:\n                '기능: 이미지 접두사를 사용자 정의하고 compose 파일의 이미지 필드를 수정합니다. 예를 들어 이미지 접두사를 1panel/custom으로 설정하면 MaxKB의 이미지 필드는 1panel/custom/maxkb:v1.10.0으로 변경됩니다',\n            closeHelper: '사용자 정의 앱 저장소 사용 취소',\n            appStoreUrlHelper: '.tar.gz 형식만 지원합니다',\n            postNode: '서브 노드로 동기화',\n            postNodeHelper:\n                '사용자 정의 스토어 패키지를 하위 노드의 설치 디렉토리에 있는 tmp/customApp/apps.tar.gz로 동기화합니다',\n            nodes: '노드 선택',\n            selectNode: '노드 선택',\n            selectNodeError: '노드를 선택하세요',\n            licenseHelper: '프로 버전은 사용자 정의 애플리케이션 저장소 기능을 지원합니다',\n            databaseHelper: '애플리케이션 관련 데이터베이스, 대상 노드 데이터베이스를 선택하세요',\n            nodeHelper: '현재 노드는 선택할 수 없습니다',\n            migrateHelper:\n                '현재 단일 애플리케이션과 MySQL, MariaDB, PostgreSQL 데이터베이스만 연결된 애플리케이션의 마이그레이션만 지원합니다',\n            opensslHelper:\n                '암호화된 백업을 사용하는 경우 두 노드 간의 OpenSSL 버전이 일치해야 합니다. 그렇지 않으면 마이그레이션이 실패할 수 있습니다.',\n            installApp: '일괄 설치',\n            installAppHelper: '선택한 노드에 앱 일괄 설치',\n        },\n        alert: {\n            isAlert: '알림',\n            alertCount: '알림 횟수',\n            clamHelper: '감염된 파일을 스캔할 때 알림 트리거',\n            cronJobHelper: '작업 실행 실패 시 알림 트리거',\n            licenseHelper: '전문 버전에서는 알림을 지원합니다.',\n            alertCountHelper: '최대 일일 알림 빈도',\n            alert: 'SMS 알림',\n            logs: '알림 로그',\n            list: '알림 목록',\n            addTask: '알림 생성',\n            editTask: '알림 수정',\n            alertMethod: '알림 방법',\n            alertMsg: '알림 메시지',\n            alertRule: '알림 규칙',\n            titleSearchHelper: '알림 제목을 입력하여 검색하세요',\n            taskType: '유형',\n            ssl: 'SSL 인증서 만료',\n            siteEndTime: '웹사이트 만료',\n            panelPwdEndTime: '패널 비밀번호 만료',\n            panelUpdate: '새 패널 버전 사용 가능',\n            cpu: '서버 CPU 알림',\n            memory: '서버 메모리 알림',\n            load: '서버 부하 알림',\n            disk: '서버 디스크 알림',\n            website: '웹사이트',\n            certificate: 'SSL 인증서',\n            remainingDays: '남은 일수',\n            sendCount: '발송 횟수',\n            sms: 'SMS',\n            wechat: '위챗',\n            dingTalk: '딩톡',\n            feiShu: '페이슈',\n            mail: '이메일',\n            weCom: 'WeCom',\n            sendCountRulesHelper: '만료 전 발송된 총 알림 수 (하루 1회)',\n            panelUpdateRulesHelper: '새 패널 버전에 대한 총 알림 수 (하루 1회)',\n            oneDaySendCountRulesHelper: '하루 최대 발송 가능한 알림 수',\n            siteEndTimeRulesHelper: '만료되지 않는 웹사이트는 알림이 발생하지 않습니다',\n            autoRenewRulesHelper: '자동 갱신이 설정된 인증서의 남은 일수가 31일 미만이면 알림이 발생하지 않습니다',\n            panelPwdEndTimeRulesHelper: '만료가 설정되지 않은 경우 패널 비밀번호 만료 알림이 비활성화됩니다',\n            sslRulesHelper: '모든 SSL 인증서',\n            diskInfo: '디스크',\n            monitoringType: '모니터링 유형',\n            autoRenew: '자동 갱신',\n            useDisk: '디스크 사용량',\n            usePercentage: '사용 비율',\n            changeStatus: '상태 변경',\n            disableMsg: '알림 작업을 중지하면 알림 메시지가 전송되지 않습니다. 계속하시겠습니까?',\n            enableMsg: '알림 작업을 활성화하면 알림 메시지가 전송됩니다. 계속하시겠습니까?',\n            useExceed: '사용량 초과',\n            useExceedRulesHelper: '사용량이 설정 값을 초과하면 알림을 트리거합니다',\n            cpuUseExceedAvg: '평균 CPU 사용량이 지정된 값을 초과함',\n            memoryUseExceedAvg: '평균 메모리 사용량이 지정된 값을 초과함',\n            loadUseExceedAvg: '평균 부하 사용량이 지정된 값을 초과함',\n            cpuUseExceedAvgHelper: '지정된 시간 내의 평균 CPU 사용량이 지정된 값을 초과함',\n            memoryUseExceedAvgHelper: '지정된 시간 내의 평균 메모리 사용량이 지정된 값을 초과함',\n            loadUseExceedAvgHelper: '지정된 시간 내의 평균 부하 사용량이 지정된 값을 초과함',\n            resourceAlertRulesHelper: '참고: 30분 내에 연속적인 알림은 한 번만 발송됩니다',\n            specifiedTime: '지정된 시간',\n            deleteTitle: '알림 삭제',\n            deleteMsg: '알림 작업을 삭제하시겠습니까?',\n            allSslTitle: '모든 웹사이트 SSL 인증서 만료 알림',\n            sslTitle: '웹사이트 {0}의 SSL 인증서 만료 알림',\n            allSiteEndTimeTitle: '모든 웹사이트 만료 알림',\n            siteEndTimeTitle: '웹사이트 {0} 만료 알림',\n            panelPwdEndTimeTitle: '패널 비밀번호 만료 알림',\n            panelUpdateTitle: '새 패널 버전 알림',\n            cpuTitle: '고 CPU 사용량 알림',\n            memoryTitle: '고 메모리 사용량 알림',\n            loadTitle: '고 부하 알림',\n            diskTitle: '디스크 사용량 초과 알림 {0}',\n            allDiskTitle: '고 디스크 사용량 알림',\n            timeRule: '{0}일 이하 남은 시간 (처리되지 않으면 다음 날 다시 전송)',\n            panelUpdateRule: '새 패널 버전이 감지되면 한 번 알림을 전송 (처리되지 않으면 다음 날 다시 전송)',\n            avgRule: '{0}분 내 평균 {1} 사용량이 {2}% 초과 시 알림을 트리거하며 하루에 {3}번 발송',\n            diskRule: '디스크 사용량이 마운트 디렉토리 {0}에서 {1}{2}를 초과하면 알림을 트리거하고 하루에 {3}번 발송',\n            allDiskRule: '디스크 사용량이 {0}{1}을 초과하면 알림을 트리거하고 하루에 {2}번 발송',\n            cpuName: ' CPU ',\n            memoryName: '메모리',\n            loadName: '부하',\n            diskName: '디스크',\n            syncAlertInfo: '수동 푸시',\n            syncAlertInfoMsg: '알림 작업을 수동으로 푸시하시겠습니까?',\n            pushError: '푸시 실패',\n            pushSuccess: '푸시 성공',\n            syncError: '동기화 실패',\n            success: '알림 성공',\n            pushing: '푸시 중...',\n            error: '알림 실패',\n            cleanLog: '로그 정리',\n            cleanAlertLogs: '알림 로그 정리',\n            daily: '일일 알림 수: {0}',\n            cumulative: '누적 알림 수: {0}',\n            clams: '바이러스 검사',\n            taskName: '작업 이름',\n            cronJobType: '작업 유형',\n            clamPath: '검사 디렉토리',\n            cronjob: '예약 작업 실행 {0} 중 오류가 발생했습니다',\n            app: '백업 애플리케이션',\n            web: '백업 웹사이트',\n            database: '백업 데이터베이스',\n            directory: '백업 디렉토리',\n            log: '백업 로그',\n            snapshot: '시스템 스냅샷',\n            clamsRulesHelper: '알림이 필요한 바이러스 검사 작업',\n            cronJobRulesHelper: '이 유형의 예약된 작업은 구성해야 합니다',\n            clamsTitle: '바이러스 검사 작업 「 {0} 」 감염된 파일 알림',\n            cronJobAppTitle: '크론 작업 - 백업 애플리케이션 「 {0} 」 작업 실패 알림',\n            cronJobWebsiteTitle: '크론 작업 - 백업 웹사이트「 {0} 」작업 실패 알림',\n            cronJobDatabaseTitle: '크론 작업 - 백업 데이터베이스「 {0} 」작업 실패 알림',\n            cronJobDirectoryTitle: '크론 작업 - 백업 디렉토리「 {0} 」작업 실패 알림',\n            cronJobLogTitle: '크론 작업 - 백업 로그「 {0} 」작업 실패 알림',\n            cronJobSnapshotTitle: '크론 작업 - 백업 스냅샷「 {0} 」작업 실패 알림',\n            cronJobShellTitle: '크론 작업 - 셸 스크립트 「 {0} 」작업 실패 알림',\n            cronJobCurlTitle: '크론 작업 - URL 접근「 {0} 」작업 실패 알림',\n            cronJobCutWebsiteLogTitle: '크론 작업 - 웹사이트 로그 자르기「 {0} 」작업 실패 알림',\n            cronJobCleanTitle: '크론 작업 - 캐시 정리「 {0} 」작업 실패 알림',\n            cronJobNtpTitle: '크론 작업 - 서버 시간 동기화「 {0} 」작업 실패 알림',\n            clamsRule: '바이러스 검사로 감염된 파일 알림, 하루에 {0}번 발송',\n            cronJobAppRule: '백업 애플리케이션 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobWebsiteRule: '백업 웹사이트 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobDatabaseRule: '백업 데이터베이스 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobDirectoryRule: '백업 디렉토리 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobLogRule: '백업 로그 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobSnapshotRule: '백업 스냅샷 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobShellRule: '셸 스크립트 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobCurlRule: 'URL 접근 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobCutWebsiteLogRule: '웹사이트 로그 자르기 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobCleanRule: '캐시 정리 작업 실패 알림, 하루에 {0}번 발송',\n            cronJobNtpRule: '서버 시간 동기화 작업 실패 알림, 하루에 {0}번 발송',\n            alertSmsHelper: 'SMS 한도: 총 {0}개의 메시지, {1}개 사용됨',\n            goBuy: '추가 구매',\n            phone: '전화',\n            phoneHelper: '알림 메시지를 위한 실제 전화번호를 제공하세요',\n            dailyAlertNum: '일일 알림 한도',\n            dailyAlertNumHelper: '하루에 보낼 수 있는 최대 알림 수 (최대 100개)',\n            timeRange: '시간 범위',\n            sendTimeRange: '발송 시간 범위',\n            sendTimeRangeHelper: '{0} 시간 범위를 푸시할 수 있습니다',\n            to: '부터',\n            startTime: '시작 시간',\n            endTime: '종료 시간',\n            defaultPhone: '기본적으로 라이선스에 묶인 계정의 전화번호 사용',\n            noticeAlert: '공지 알림',\n            resourceAlert: '리소스 알림',\n            agentOfflineAlertHelper:\n                '노드에서 오프라인 알림이 활성화되면, 메인 노드가 30분마다 스캔하여 알림 작업을 수행합니다.',\n            offline: '오프라인 알림',\n            offlineHelper: '오프라인 알림으로 설정하면, 메인 노드가 30분마다 스캔하여 알림 작업을 수행합니다.',\n            offlineOff: '오프라인 알림 활성화',\n            offlineOffHelper: '오프라인 알림을 활성화하면, 메인 노드가 30분마다 스캔하여 알림 작업을 수행합니다.',\n            offlineClose: '오프라인 알림 비활성화',\n            offlineCloseHelper:\n                '오프라인 알림을 비활성화하면, 하위 노드가 알림을 직접 처리해야 합니다. 알림 실패를 방지하려면 네트워크 연결이 원활한지 확인하세요.',\n            alertNotice: '알림 통지',\n            methodConfig: '알림 방법 설정',\n            commonConfig: '전역 설정',\n            smsConfig: 'SMS',\n            smsConfigHelper: 'SMS 알림을 받을 전화번호를 설정합니다',\n            emailConfig: '이메일',\n            emailConfigHelper: 'SMTP 이메일 발송 서비스를 설정합니다',\n            deleteConfigTitle: '알림 설정 삭제',\n            deleteConfigMsg: '알림 설정을 삭제하시겠습니까?',\n            test: '테스트',\n            alertTestOk: '테스트 알림 성공',\n            alertTestFailed: '테스트 알림 실패',\n            displayName: '표시 이름',\n            sender: '발신 주소',\n            password: '비밀번호',\n            host: 'SMTP 서버',\n            port: '포트 번호',\n            encryption: '암호화 방식',\n            recipient: '수신자',\n            licenseTime: '라이선스 만료 알림',\n            licenseTimeTitle: '라이선스 만료 알림',\n            displayNameHelper: '이메일 발신자 표시 이름',\n            senderHelper: '이메일 발송에 사용되는 주소',\n            passwordHelper: '메일 서비스의 인증 코드',\n            hostHelper: 'SMTP 서버 주소, 예: smtp.qq.com',\n            portHelper: 'SSL 은 일반적으로 465, TLS 는 587',\n            sslHelper: 'SMTP 포트가 465 이면 일반적으로 SSL 이 필요합니다',\n            tlsHelper: 'SMTP 포트가 587 이면 일반적으로 TLS 가 필요합니다',\n            triggerCondition: '트리거 조건',\n            loginFail: ' 이내 로그인 실패',\n            nodeException: '노드 이상 알림',\n            licenseException: '라이선스 이상 알림',\n            panelLogin: '패널 로그인 이상 알림',\n            sshLogin: 'SSH 로그인 이상 알림',\n            panelIpLogin: '패널 로그인 IP 이상 알림',\n            sshIpLogin: 'SSH 로그인 IP 이상 알림',\n            ipWhiteListHelper:\n                '화이트리스트에 있는 IP는 규칙의 제한을 받지 않으며, 로그인에 성공해도 알림이 발생하지 않습니다',\n            nodeExceptionRule: '노드 이상 알림은 하루 {0}회 전송',\n            licenseExceptionRule: '라이선스 이상 알림은 하루 {0}회 전송',\n            panelLoginRule: '패널 로그인 알림은 하루 {0}회 전송',\n            sshLoginRule: 'SSH 로그인 알림은 하루 {0}회 전송',\n            userNameHelper: '사용자 이름이 비어 있으면 기본적으로 발신자 주소가 사용됩니다',\n            alertConfigHelper: '패널 메시지 푸시를 수신하기 위한 알림 채널 구성',\n            weComConfigHelper: 'WeCom 알림 구성',\n            wechatConfigHelper: 'WeChat 공식 계정 알림 구성',\n            dingTalkConfigHelper: 'DingTalk 알림 구성',\n            feiShuConfigHelper: 'Feishu 알림 구성',\n            webhookName: '봇 이름',\n            webhookUrl: 'Webhook URL',\n            alertConfigProHelper:\n                '프로페셔널 버전에서는 기업 위챗(WeCom), DingTalk, Feishu 및 SMS 알림을 추가로 지원합니다.',\n        },\n        theme: {\n            lingXiaGold: '링샤 골드',\n            classicBlue: '클래식 블루',\n            freshGreen: '프레시 그린',\n            customColor: '사용자 정의 색상',\n            setDefault: '기본값',\n            setDefaultHelper: '테마 색상 스킴이 초기 상태로 복원됩니다. 계속하시겠습니까?',\n            setHelper: '현재 선택한 테마 색상 스킴이 저장됩니다. 계속하시겠습니까?',\n        },\n        exchange: {\n            exchange: '파일 교환',\n            exchangeConfirm: '{0} 노드의 파일/폴더 {1}을(를) {2} 노드의 {3} 디렉토리로 전송하시겠습니까?',\n        },\n        cluster: {\n            cluster: '애플리케이션 고가용성',\n            name: '클러스터 이름',\n            addCluster: '클러스터 추가',\n            installNode: '노드 설치',\n            master: '마스터 노드',\n            slave: '슬레이브 노드',\n            replicaStatus: '마스터-슬레이브 상태',\n            unhealthyDeleteError: '설치 노드 상태가 비정상입니다. 노드 목록을 확인한 후 다시 시도하세요!',\n            replicaStatusError: '상태 획득이 비정상입니다. 마스터 노드를 확인하세요.',\n            masterHostError: '마스터 노드의 IP는 127.0.0.1이 될 수 없습니다',\n        },\n    },\n};\n\nexport default {\n    ...fit2cloudKoLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/ms.ts",
    "content": "import fit2cloudEnLocale from 'fit2cloud-ui-plus/src/locale/lang/ms';\n\nconst message = {\n    commons: {\n        true: 'Benar',\n        false: 'Palsu',\n        colon: ': ',\n        example: 'cth., ',\n        fit2cloud: 'FIT2CLOUD',\n        lingxia: 'Lingxia',\n        button: {\n            run: 'Jalankan',\n            create: 'Cipta',\n            add: 'Tambah',\n            save: 'Simpan',\n            set: 'Sunting tetapan',\n            sync: 'Selaras',\n            delete: 'Padam',\n            edit: 'Sunting',\n            enable: 'Aktif',\n            disable: 'Nyahaktif',\n            confirm: 'Sah',\n            cancel: 'Batal',\n            reset: 'Tetapkan semula',\n            restart: 'Mulakan semula',\n            conn: 'Sambung',\n            disConn: 'Putus sambungan',\n            clean: 'Bersih',\n            login: 'Log masuk',\n            close: 'Tutup',\n            off: 'Tutup',\n            stop: 'Henti',\n            start: 'Mula',\n            view: 'Lihat',\n            watch: 'Pantau',\n            handle: 'Picu',\n            clone: 'Klon',\n            expand: 'Kembang',\n            collapse: 'Runtuh',\n            log: 'Log',\n            back: 'Kembali',\n            backup: 'Sandaran',\n            recover: 'Pulih',\n            retry: 'Cuba semula',\n            upload: 'Muat naik',\n            download: 'Muat turun',\n            init: 'Mulakan',\n            verify: 'Sahkan',\n            saveAndEnable: 'Simpan dan aktifkan',\n            import: 'Import',\n            export: 'Eksport',\n            power: 'Pemberian Kuasa',\n            search: 'Cari',\n            refresh: 'Segarkan',\n            get: 'Dapatkan',\n            upgrade: 'Tingkatkan',\n            update: 'kemas kini',\n            updateNow: 'Kemas Kini Sekarang',\n            ignore: 'Abaikan peningkatan',\n            install: 'pasang',\n            copy: 'Salin',\n            random: 'Rawak',\n            uninstall: 'Nyahpasang',\n            fullscreen: 'Masuk ke skrin penuh',\n            quitFullscreen: 'Keluar dari skrin penuh',\n            showAll: 'Tunjukkan Semua',\n            hideSome: 'Sembunyikan Sebahagian',\n            agree: 'Setuju',\n            notAgree: 'Tidak Setuju',\n            preview: 'Pratonton',\n            open: 'Buka',\n            notSave: 'Tidak Disimpan',\n            createNewFolder: 'Cipta folder baru',\n            createNewFile: 'Cipta fail baru',\n            helpDoc: 'Dokumen Bantuan',\n            unbind: 'Nyahkaitkan',\n            cover: 'Tindih',\n            skip: 'Langkau',\n            fix: 'Betulkan',\n            down: 'Hentikan',\n            up: 'Mulakan',\n            sure: 'Sahkan',\n            show: 'Tunjukkan',\n            hide: 'Sembunyikan',\n            visit: 'Lawati',\n            migrate: 'Migrasi',\n            prev: 'Sebelumnya',\n            next: 'Seterusnya',\n            setDefault: 'Pulihkan lalai',\n            bind: 'Ikat',\n        },\n        operate: {\n            start: 'Mula',\n            stop: 'Hentikan',\n            restart: 'Mulai Semula',\n            reload: 'Muat Semula',\n            rebuild: 'Bangun Semula',\n            sync: 'Segerakkan',\n            up: 'Naik',\n            down: 'Turun',\n            delete: 'Padam',\n        },\n        search: {\n            timeStart: 'Masa mula',\n            timeEnd: 'Masa tamat',\n            timeRange: 'Hingga',\n            dateStart: 'Tarikh mula',\n            dateEnd: 'Tarikh tamat',\n            date: 'Tarikh',\n        },\n        table: {\n            all: 'Semua',\n            total: 'Jumlah {0}',\n            name: 'Nama',\n            type: 'Jenis',\n            status: 'Status',\n            records: 'Rekod',\n            group: 'Kumpulan',\n            createdAt: 'Waktu dicipta',\n            publishedAt: 'Waktu diterbitkan',\n            date: 'Tarikh',\n            updatedAt: 'Waktu dikemas kini',\n            operate: 'Operasi',\n            message: 'Mesej',\n            description: 'Penerangan',\n            interval: 'Selang masa',\n            user: 'Pemilik',\n            title: 'Tajuk',\n            port: 'Port',\n            forward: 'Hadapan',\n            protocol: 'Protokol',\n            tableSetting: 'Tetapan jadual',\n            refreshRate: 'Kadar penyegaran',\n            selectColumn: 'Pilih lajur',\n            local: 'Tempatan',\n            serialNumber: 'Nombor siri',\n            manageGroup: 'Urus Kumpulan',\n            backToList: 'Kembali ke Senarai',\n            keepEdit: 'Teruskan Mengedit',\n            default: 'Lalai',\n            noRefresh: 'Tiada segar semula auto',\n        },\n        loadingText: {\n            Upgrading: 'Peningkatan sistem, sila tunggu...',\n            Restarting: 'Memulakan semula sistem, sila tunggu...',\n            Recovering: 'Memulihkan daripada snapshot, sila tunggu...',\n            Rollbacking: 'Mengembalikan daripada snapshot, sila tunggu...',\n        },\n        msg: {\n            noneData: 'Tiada data tersedia',\n            delete: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',\n            clean: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',\n            closeDrawerHelper: 'Sistem mungkin tidak menyimpan perubahan yang anda buat. Adakah anda ingin teruskan?',\n            deleteSuccess: 'Berjaya dipadam',\n            loginSuccess: 'Berjaya log masuk',\n            operationSuccess: 'Berjaya dilakukan',\n            copySuccess: 'Berjaya disalin',\n            notSupportOperation: 'Operasi ini tidak disokong',\n            requestTimeout: 'Permintaan telah tamat masa, sila cuba lagi nanti',\n            infoTitle: 'Petunjuk',\n            notRecords: 'Tiada rekod pelaksanaan dijana untuk tugas semasa',\n            sureLogOut: 'Adakah anda pasti mahu log keluar?',\n            createSuccess: 'Berjaya dicipta',\n            updateSuccess: 'Berjaya dikemas kini',\n            uploadSuccess: 'Berjaya dimuat naik',\n            operateConfirm: 'Jika anda pasti dengan operasi ini, sila masukkan secara manual: ',\n            inputOrSelect: 'Sila pilih atau masukkan',\n            copyFailed: 'Gagal disalin',\n            operatorHelper:\n                'Operasi \"{1}\" akan dilakukan pada \"{0}\" dan tidak boleh diundur. Adakah anda mahu meneruskan?',\n            notFound: 'Maaf, halaman yang anda minta tidak wujud.',\n            unSupportType: 'Jenis fail semasa tidak disokong.',\n            unSupportSize: 'Fail yang dimuat naik melebihi {0}M, sila sahkan!',\n            fileExist: 'Fail sudah wujud dalam folder semasa. Memuat naik semula tidak disokong.',\n            fileNameErr:\n                'Anda hanya boleh memuat naik fail dengan nama yang mengandungi 1 hingga 256 aksara, termasuk Bahasa Inggeris, Cina, nombor, atau titik (.-_)',\n            confirmNoNull: 'Pastikan nilai {0} tidak kosong.',\n            errPort: 'Maklumat port tidak betul, sila sahkan!',\n            remove: 'Buang',\n            backupHelper: 'Operasi semasa akan membuat sandaran {0}. Adakah anda mahu meneruskan?',\n            recoverHelper:\n                'Memulihkan daripada fail {0}. Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',\n            refreshSuccess: 'Berjaya disegarkan',\n            rootInfoErr: 'Ia sudah menjadi direktori akar',\n            resetSuccess: 'Berjaya ditetapkan semula',\n            creatingInfo: 'Sedang mencipta, operasi ini tidak diperlukan',\n            offlineTips: 'Versi luar talian tidak menyokong operasi ini',\n            errImportFormat: 'Data import atau format adalah tidak normal, sila periksa dan cuba lagi!',\n            importHelper:\n                'Apabila mengimport data yang bercanggah atau pendua, kandungan yang diimport akan digunakan sebagai piawai untuk mengemas kini data pangkalan data asal.',\n            errImport: 'Kandungan fail adalah tidak normal:',\n            backupSuccess: 'Sandaran berjaya',\n            restoreSuccess: 'Pemulihan berjaya',\n            installSuccess: 'Pemasangan berjaya',\n            uninstallSuccess: 'Nyahpasang berjaya',\n        },\n        login: {\n            username: 'Pengguna',\n            password: 'Kata Laluan',\n            passkey: 'Log masuk Passkey',\n            welcome: 'Selamat datang kembali, sila masukkan nama pengguna dan kata laluan anda untuk log masuk!',\n            errorAuthInfo: 'Nama pengguna atau kata laluan yang anda masukkan tidak betul, sila cuba lagi!',\n            errorMfaInfo: 'Maklumat pengesahan tidak betul, sila cuba lagi!',\n            captchaHelper: 'Captcha',\n            errorCaptcha: 'Ralat kod captcha!',\n            notSafe: 'Akses Ditolak',\n            safeEntrance1: 'Log masuk selamat telah diaktifkan dalam persekitaran semasa',\n            safeEntrance2: 'Masukkan arahan berikut pada terminal SSH untuk melihat pintu masuk panel: 1pctl user-info',\n            errIP1: 'Akses alamat IP yang dibenarkan diaktifkan dalam persekitaran semasa',\n            errDomain1: 'Pengikatan nama domain akses diaktifkan dalam persekitaran semasa',\n            errHelper: 'Untuk menetapkan semula maklumat pengikatan, jalankan arahan berikut pada terminal SSH: ',\n            codeInput: 'Sila masukkan kod pengesahan 6 digit dari pengesah MFA',\n            mfaTitle: 'Pengesahan MFA',\n            mfaCode: 'Kod pengesahan MFA',\n            title: 'Panel Pengurusan Pelayan Linux',\n            licenseHelper: '<Perjanjian Lesen Komuniti>',\n            errorAgree: 'Klik untuk bersetuju dengan Lesen Perisian Komuniti',\n            logout: 'Log keluar',\n            agreeTitle: 'Agreement',\n            agreeContent:\n                'Untuk melindungi hak dan kepentingan sah anda dengan lebih baik, sila baca dan setuju dengan perjanjian berikut &laquo; <a href = \"https://www.fit2cloud.com/legal/licenses.html\" target = \"_blank\" > Perjanjian Lesen Komuniti </a> &raquo;',\n            passkeyFailed: 'Log masuk Passkey gagal, sila cuba lagi',\n            passkeyNotSupported: 'Pelayar atau persekitaran semasa tidak menyokong passkey',\n            passkeyToPassword: 'Ada masalah menggunakan passkey? Gunakan kata laluan sahaja',\n        },\n        rule: {\n            username: 'Masukkan nama pengguna',\n            password: 'Masukkan kata laluan',\n            rePassword: 'Pengesahan kata laluan tidak sepadan dengan kata laluan.',\n            requiredInput: 'Ruangan ini wajib diisi.',\n            requiredSelect: 'Pilih satu item dalam senarai',\n            illegalChar: 'Suntikan aksara & ; $ \\' ` ( ) \" > < | tidak disokong buat masa ini',\n            illegalInput: 'Ruangan ini tidak boleh mengandungi aksara tidak sah.',\n            commonName:\n                'Ruangan ini mesti bermula dengan aksara bukan khas dan mesti terdiri daripada aksara rumi, Cina, nombor, \".\", \"-\", dan \"_\" dengan panjang 1-128 aksara.',\n            userName:\n                'Menyokong bermula dengan bukan aksara khas, Bahasa Inggeris, Bahasa Cina, nombor, dan _, panjang 3-30',\n            simpleName:\n                'Ruangan ini tidak boleh bermula dengan aksara garis bawah (\"_\") dan mesti terdiri daripada aksara rumi, nombor, dan \"_\" dengan panjang 3-30 aksara.',\n            simplePassword:\n                'Ruangan ini tidak boleh bermula dengan aksara garis bawah (\"_\") dan mesti terdiri daripada aksara rumi, nombor, dan \"_\" dengan panjang 1-30 aksara.',\n            dbName: 'Ruangan ini tidak boleh bermula dengan aksara garis bawah (\"_\") dan mesti terdiri daripada aksara rumi, nombor, dan \"_\" dengan panjang 1-64 aksara.',\n            imageName: 'Menyokong bermula dengan bukan aksara khas, Bahasa Inggeris, nombor, :@/.-_, panjang 1-256',\n            composeName:\n                'Menyokong aksara bukan khas pada permulaan, huruf kecil, nombor, \"-\", dan \"_\", dengan panjang 1-256 aksara.',\n            volumeName:\n                'Ruangan ini mesti terdiri daripada aksara Bahasa Inggeris, nombor, \".\", \"-\", dan \"_\" dengan panjang 2-30 aksara.',\n            supervisorName:\n                'Ruangan ini mesti bermula dengan aksara bukan khas dan mesti terdiri daripada aksara rumi, nombor, \"-\", dan \"_\" dengan panjang 1-128 aksara.',\n            complexityPassword:\n                'Ruangan ini mesti terdiri daripada aksara rumi, nombor dengan panjang 8-30 aksara dan mengandungi sekurang-kurangnya dua aksara khas.',\n            commonPassword: 'Panjang ruangan ini mesti melebihi 6 aksara.',\n            linuxName:\n                'Panjang ruangan ini mesti antara 1 hingga 128 aksara. Ruangan ini tidak boleh mengandungi aksara khas berikut: \"{0}\".',\n            email: 'Ruangan ini mesti mengandungi alamat emel yang sah.',\n            number: 'Ruangan ini mesti mengandungi nombor.',\n            integer: 'Ruangan ini mesti mengandungi integer positif.',\n            ip: 'Ruangan ini mesti mengandungi alamat IP yang sah.',\n            host: 'Ruangan ini mesti mengandungi alamat IP atau nama domain yang sah.',\n            hostHelper: 'Menyokong input alamat IP atau nama domain',\n            port: 'Ruangan ini mesti mengandungi nombor port yang sah.',\n            selectHelper: 'Sila pilih fail {0} yang betul',\n            domain: 'Ruangan ini mesti dalam format: example.com atau example.com:8080.',\n            databaseName:\n                'Ruangan ini mesti terdiri daripada aksara Bahasa Inggeris, nombor, dan \"_\" dengan panjang 1-30 aksara.',\n            ipErr: 'Ruangan ini mesti mengandungi alamat IP yang sah.',\n            numberRange: 'Ruangan ini mesti mengandungi nombor antara {0} dan {1}.',\n            paramName:\n                'Ruangan ini mesti terdiri daripada aksara rumi, nombor, \".\", \"-\", dan \"_\" dengan panjang 2-30 aksara.',\n            paramComplexity:\n                'Ruangan ini tidak boleh bermula atau berakhir dengan aksara khas dan mesti terdiri daripada aksara rumi, nombor, \"{0}\" dengan panjang 6-128 aksara.',\n            paramUrlAndPort: 'Ruangan ini mesti dalam format \"http(s)://(nama domain/IP):(port)\".',\n            nginxDoc: 'Ruangan ini mesti terdiri daripada aksara rumi, nombor, dan \".\".',\n            appName:\n                'Ruangan ini tidak boleh bermula atau berakhir dengan \"-\" dan \"_\" dan mesti terdiri daripada aksara rumi, nombor, \"-\", dan \"_\" dengan panjang 2-30 aksara.',\n            containerName:\n                'Menyokong huruf, nombor, -, _ dan .; tidak boleh bermula dengan -, _ atau .; panjang: 2-128 aksara.',\n            mirror: 'Alamat pecutan cermin mesti bermula dengan http(s)://, menyokong huruf (huruf besar dan kecil), nombor, ., / dan -, serta tidak boleh mengandungi baris kosong.',\n            disableFunction: 'Hanya menyokong huruf, garis bawah, dan ,',\n            leechExts: 'Hanya menyokong huruf, nombor, dan ,',\n            paramSimple: 'Menyokong huruf kecil dan nombor, panjang 1-128 aksara.',\n            filePermission: 'Ralat Kebenaran Fail',\n            formatErr: 'Ralat format, sila semak dan cuba lagi',\n            phpExtension: 'Hanya menyokong huruf kecil, _, dan nombor',\n            paramHttp: 'Mesti bermula dengan http:// atau https://',\n            phone: 'Format nombor telefon tidak betul.',\n            authBasicPassword: 'Menyokong huruf, nombor, dan aksara khas biasa, panjang 1-72',\n            length128Err: 'Panjang tidak boleh melebihi 128 aksara',\n            maxLength: 'Panjang tidak boleh melebihi {0} aksara',\n            alias: 'Menyokong Bahasa Inggeris, nombor, - dan _, panjang 1-128, dan tidak boleh bermula atau berakhir dengan -_.',\n        },\n        res: {\n            paramError: 'Permintaan gagal, sila cuba lagi nanti!',\n            forbidden: 'Pengguna semasa tidak mempunyai kebenaran',\n            serverError: 'Kecacatan perkhidmatan',\n            notFound: 'Sumber tidak wujud',\n            commonError: 'Permintaan gagal',\n        },\n        service: {\n            serviceNotStarted: 'Perkhidmatan {0} belum dimulakan.',\n        },\n        status: {\n            running: 'Sedang Berjalan',\n            done: 'Selesai',\n            scanFailed: 'Tidak Lengkap',\n            success: 'Berjaya',\n            waiting: 'Menunggu',\n            waitForUpgrade: 'Tunggu Naik Taraf',\n            waiting1: 'Menunggu',\n            failed: 'Gagal',\n            stopped: 'Dihentikan',\n            error: 'Ralat',\n            created: 'Dicipta',\n            restarting: 'Memulakan Semula',\n            uploading: 'Sedang Memuat Naik',\n            unhealthy: 'Tidak Sihat',\n            removing: 'Sedang Membuang',\n            paused: 'Dijeda',\n            exited: 'Keluar',\n            dead: 'Mati',\n            installing: 'Sedang Memasang',\n            enabled: 'Diaktifkan',\n            disabled: 'Dilumpuhkan',\n            normal: 'Normal',\n            building: 'Sedang Membina',\n            upgrading: 'Sedang Meningkatkan',\n            pending: 'Menunggu Edit',\n            rebuilding: 'Sedang Membina Semula',\n            deny: 'Ditolak',\n            accept: 'Diterima',\n            used: 'Digunakan',\n            unUsed: 'Tidak Digunakan',\n            starting: 'Sedang Memulakan',\n            recreating: 'Sedang Mencipta Semula',\n            creating: 'Sedang Mencipta',\n            init: 'Menunggu aplikasi',\n            ready: 'Normal',\n            applying: 'Sedang Memohon',\n            uninstalling: 'Menyahpasang',\n            lost: 'Hilang',\n            bound: 'Terikat',\n            unbind: 'Tidak terikat',\n            exceptional: 'Luar biasa',\n            free: 'Bebas',\n            enable: 'Aktif',\n            disable: 'Dilumpuhkan',\n            deleted: 'Dihapus',\n            downloading: 'Memuat turun',\n            packing: 'Membungkus',\n            sending: 'Menghantar',\n            healthy: 'Sihat',\n            executing: 'Melaksanakan',\n            installerr: 'Pemasangan gagal',\n            applyerror: 'Permohonan gagal',\n            systemrestart: 'Dihentikan',\n            starterr: 'Permulaan gagal',\n            uperr: 'Permulaan gagal',\n            new: 'Baru',\n            conflict: 'Konflik',\n            duplicate: 'Pendua',\n            unexecuted: 'Tidak Dilaksanakan',\n            unused: 'Tidak digunakan',\n        },\n        units: {\n            second: 'saat | saat | saat',\n            minute: 'minit | minit | minit',\n            hour: 'jam | jam | jam',\n            day: 'hari | hari | hari',\n            week: 'minggu | minggu | minggu',\n            month: 'bulan | bulan | bulan',\n            year: 'tahun | tahun | tahun',\n            time: 'masa',\n            core: 'teras | teras | teras',\n            secondUnit: 's',\n            minuteUnit: 'min',\n            hourUnit: 'h',\n            dayUnit: 'd',\n            millisecond: 'Milisaat',\n            semicolon: ';',\n        },\n        log: {\n            noLog: 'Tiada log sedia ada',\n        },\n    },\n    menu: {\n        home: 'Papan Pemuka',\n        apps: 'App Store',\n        website: 'Laman | Laman',\n        project: 'Project | Projects',\n        config: 'Configuration | Configurations',\n        ssh: 'SSH Settings',\n        firewall: 'Firewall',\n        ssl: 'Certificate | Certificates',\n        database: 'Database | Databases',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: 'Container | Containers',\n        cronjob: 'Cron Job | Cron Jobs',\n        system: 'System',\n        security: 'Security',\n        files: 'Files',\n        monitor: 'Monitoring',\n        terminal: 'Terminal',\n        settings: 'Tetapan',\n        toolbox: 'Toolbox',\n        logs: 'Log | Logs',\n        runtime: 'Runtime | Runtimes',\n        processManage: 'Process | Processes',\n        process: 'Process | Processes',\n        network: 'Network | Networks',\n        supervisor: 'Supervisor',\n        tamper: 'Bukti Pengubahsuaian',\n        app: 'Aplikasi',\n        msgCenter: 'Tugas',\n        disk: 'Disk',\n        filter: 'Penapis',\n    },\n    home: {\n        recommend: 'Disyorkan',\n        dir: 'Direktori',\n        alias: 'Alias',\n        quickDir: 'Akses Pantas',\n        minQuickJump: 'Tambah sekurang-kurangnya satu entri akses pantas',\n        maxQuickJump: 'Anda boleh menambah sehingga empat entri akses pantas',\n        database: 'Pangkalan Data - Semua',\n        restart_1panel: 'Mulakan semula panel',\n        restart_system: 'Mulakan semula pelayan',\n        operationSuccess: 'Selesai dengan jayanya. Menyusun semula sekarang, muat semula pelayar kemudian.',\n        entranceHelper:\n            'Pintu masuk selamat tidak diaktifkan. Hidupkan di \"Tetapan -> Keselamatan\" untuk tingkatkan keselamatan.',\n        appInstalled: 'Aplikasi yang dipasang',\n        systemInfo: 'Maklumat sistem',\n        hostname: 'Nama hos',\n        platformVersion: 'Sistem pengendalian',\n        kernelVersion: 'Kernel',\n        kernelArch: 'Seni bina',\n        network: 'Rangkaian',\n        io: 'Disk I/O',\n        ip: 'Local IP',\n        proxy: 'System proxy',\n        baseInfo: 'Base info',\n        totalSend: 'Jumlah dihantar',\n        totalRecv: 'Jumlah diterima',\n        rwPerSecond: 'I/O operations',\n        ioDelay: 'I/O latency',\n        uptime: 'Up since',\n        runningTime: 'Uptime',\n        mem: 'Memori Sistem',\n        swapMem: 'Swap partition',\n        runSmoothly: 'Beban rendah',\n        runNormal: 'Beban sederhana',\n        runSlowly: 'Beban tinggi',\n        runJam: 'Beban berat',\n        core: 'Teras Fizikal',\n        corePercent: 'Penggunaan Teras',\n        cpuFrequency: 'Frekuensi CPU',\n        cpuDetailedPercent: 'Pengagihan Masa CPU',\n        cpuUser: 'Pengguna',\n        cpuSystem: 'Sistem',\n        cpuIdle: 'Tidak Aktif',\n        cpuIrq: 'IRQ',\n        cpuSoftirq: 'Soft IRQ',\n        cpuSteal: 'Dicuri',\n        cpuTop: 'Maklumat Proses 5 Teratas Mengikut Penggunaan CPU',\n        memTop: 'Maklumat Proses 5 Teratas Mengikut Penggunaan Memori',\n        logicCore: 'Teras Logik',\n        loadAverage: 'Purata beban dalam 1 minit terakhir | Purata beban dalam {n} minit terakhir',\n        load: 'Beban',\n        mount: 'Titik Pemasangan',\n        fileSystem: 'Sistem Fail',\n        total: 'Jumlah',\n        used: 'Digunakan',\n        cache: 'Cache',\n        free: 'Bebas',\n        shard: 'Shard',\n        available: 'Tersedia',\n        percent: 'Penggunaan',\n        goInstall: 'Pergi pasang',\n        networkCard: 'Kad rangkaian',\n        disk: 'Disk',\n        memo: 'Catatan',\n        memoPlaceholder: 'Klik butang edit untuk mengaktifkan penyuntingan.',\n        carouselSetting: 'Tetapan karusel',\n        tooltipSensitiveInfo: 'Tunjuk/Sembunyikan maklumat sensitif',\n    },\n    tabs: {\n        more: 'Lagi',\n        hide: 'Sembunyi',\n        closeLeft: 'Tutup kiri',\n        closeRight: 'Tutup kanan',\n        closeCurrent: 'Tutup semasa',\n        closeOther: 'Tutup lain',\n        closeAll: 'Tutup Semua',\n    },\n    header: {\n        logout: 'Log keluar',\n    },\n    database: {\n        manage: 'Pengurusan',\n        deleteBackupHelper: 'Padam sandaran pangkalan data secara serentak',\n        delete: 'Operasi padam tidak boleh diundurkan, sila masukkan \"',\n        deleteHelper: '\" untuk memadam pangkalan data ini',\n        noMysql: 'Perkhidmatan pangkalan data (MySQL atau MariaDB)',\n        noPostgresql: 'Perkhidmatan pangkalan data PostgreSQL',\n        goUpgrade: 'Pergi tingkatkan',\n        goInstall: 'Pergi pasang',\n        isDelete: 'Dihapuskan',\n        permission: 'Kebenaran',\n        format: 'Set Aksara',\n        collation: 'Kolasi',\n        collationHelper: 'Jika kosong, gunakan kolasi lalai set aksara {0}',\n        permissionForIP: 'IP',\n        permissionAll: 'Kesemuanya(%)',\n        localhostHelper:\n            'Mengkonfigurasi kebenaran pangkalan data sebagai \"localhost\" untuk penyebaran kontena akan menghalang akses luar ke kontena. Sila pilih dengan teliti!',\n        databaseConnInfo: 'Sambungan',\n        rootPassword: 'Kata laluan root',\n        serviceName: 'Nama Perkhidmatan',\n        serviceNameHelper: 'Akses antara kontena dalam rangkaian yang sama.',\n        backupList: 'Sandaran',\n        loadBackup: 'Import',\n        localUpload: 'Muat Naik Tempatan',\n        hostSelect: 'Pemilihan Pelayan',\n        selectHelper: 'Adakah anda pasti ingin mengimport fail sandaran {0}?',\n        remoteAccess: 'Akses jauh',\n        remoteHelper: 'Berbilang IP dipisahkan dengan koma, contoh: 172.16.10.111, 172.16.10.112',\n        remoteConnHelper:\n            'Sambungan jauh ke MySQL sebagai pengguna root mungkin mempunyai risiko keselamatan. Oleh itu, lakukan operasi ini dengan berhati-hati.',\n        changePassword: 'Kata laluan',\n        changeConnHelper: 'Operasi ini akan mengubah pangkalan data semasa {0}. Adakah anda ingin meneruskan?',\n        changePasswordHelper:\n            'Pangkalan data telah dikaitkan dengan aplikasi. Menukar kata laluan akan menukar kata laluan pangkalan data aplikasi pada masa yang sama. Perubahan ini akan berkuat kuasa selepas aplikasi dimulakan semula.',\n        recoverTimeoutHelper: '-1 bermaksud tiada had masa tamat',\n        confChange: 'Konfigurasi',\n        confNotFound:\n            'Fail konfigurasi tidak dapat dijumpai. Sila tingkatkan aplikasi ke versi terkini di gedung aplikasi dan cuba lagi!',\n        portHelper:\n            'Port ini adalah port yang didedahkan oleh kontena. Anda perlu menyimpan pengubahsuaian secara berasingan dan memulakan semula kontena!',\n        loadFromRemote: 'Selaras',\n        userBind: 'Kaitkan pengguna',\n        pgBindHelper:\n            'Operasi ini digunakan untuk mencipta pengguna baharu dan mengaitkannya dengan pangkalan data sasaran. Pada masa ini, memilih pengguna yang sudah wujud dalam pangkalan data tidak disokong.',\n        pgSuperUser: 'Pengguna Super',\n        loadFromRemoteHelper:\n            'Ini akan menyelaraskan maklumat pangkalan data di pelayan ke 1Panel. Adakah anda mahu meneruskan?',\n        passwordHelper: 'Tidak dapat diperoleh, klik untuk ubah',\n        remote: 'Jauh',\n        remoteDB: 'Pelayan jauh | Pelayan-pelayan jauh',\n        createRemoteDB: 'Kaitkan @.lower:database.remoteDB',\n        unBindRemoteDB: 'Nyahkaitkan @.lower:database.remoteDB',\n        unBindForce: 'Paksa nyahkait',\n        unBindForceHelper: 'Abaikan semua ralat semasa proses nyahkait untuk memastikan operasi akhir berjaya',\n        unBindRemoteHelper:\n            'Nyahkaitkan pangkalan data jauh hanya akan menghapuskan hubungan pengikatan dan tidak akan terus memadamkan pangkalan data jauh.',\n        editRemoteDB: 'Edit pelayan jauh',\n        localDB: 'Pangkalan data tempatan',\n        address: 'Alamat pangkalan data',\n        version: 'Versi pangkalan data',\n        userHelper:\n            'Pengguna root atau pengguna pangkalan data dengan keistimewaan root boleh mengakses pangkalan data jauh.',\n        pgUserHelper: 'Pengguna pangkalan data dengan keistimewaan superuser.',\n        ssl: 'Gunakan SSL',\n        clientKey: 'Kunci peribadi klien',\n        clientCert: 'Sijil klien',\n        caCert: 'Sijil CA',\n        hasCA: 'Mempunyai sijil CA',\n        skipVerify: 'Abaikan pemeriksaan kesahihan sijil',\n        initialDB: 'Pangkalan Data Awal',\n        formatHelper:\n            'Set aksara pangkalan data semasa adalah {0}, ketidakkonsistenan set aksara mungkin menyebabkan kegagalan pemulihan.',\n        dropHelper: 'Anda boleh seret dan lepaskan fail yang ingin dimuat naik di sini atau',\n        clickHelper: 'klik untuk memuat naik',\n        supportUpType:\n            'Hanya menyokong format fail sql, sql.gz, tar.gz, .zip. Fail termampat yang diimport mesti mengandungi hanya satu fail .sql atau termasuk test.sql',\n        currentStatus: 'Keadaan semasa',\n        baseParam: 'Parameter asas',\n        performanceParam: 'Parameter prestasi',\n        runTime: 'Waktu mula',\n        connections: 'Jumlah sambungan',\n        bytesSent: 'Byte dihantar',\n        bytesReceived: 'Byte diterima',\n        queryPerSecond: 'Pertanyaan per saat',\n        txPerSecond: 'Tx per saat',\n        connInfo: 'sambungan aktif/puncak',\n        connInfoHelper: 'Jika nilai terlalu besar, tingkatkan \"max_connections\".',\n        threadCacheHit: 'Cache benang berjaya',\n        threadCacheHitHelper: 'Jika terlalu rendah, tingkatkan \"thread_cache_size\".',\n        indexHit: 'Indeks berjaya',\n        indexHitHelper: 'Jika terlalu rendah, tingkatkan \"key_buffer_size\".',\n        innodbIndexHit: 'Kadar berjaya indeks Innodb',\n        innodbIndexHitHelper: 'Jika terlalu rendah, tingkatkan \"innodb_buffer_pool_size\".',\n        cacheHit: 'Berjaya pertanyaan cache',\n        cacheHitHelper: 'Jika terlalu rendah, tingkatkan \"query_cache_size\".',\n        tmpTableToDB: 'Jadual sementara ke cakera',\n        tmpTableToDBHelper: 'Jika terlalu besar, cuba tingkatkan \"tmp_table_size\".',\n        openTables: 'Jadual dibuka',\n        openTablesHelper: 'Nilai konfigurasi \"table_open_cache\" mesti lebih besar atau sama dengan nilai ini.',\n        selectFullJoin: 'Pilih penyertaan penuh',\n        selectFullJoinHelper: 'Jika nilai bukan 0, periksa sama ada indeks jadual data adalah betul.',\n        selectRangeCheck: 'Bilangan penyertaan tanpa indeks',\n        selectRangeCheckHelper: 'Jika nilai bukan 0, periksa sama ada indeks jadual data adalah betul.',\n        sortMergePasses: 'Bilangan penggabungan terisih',\n        sortMergePassesHelper: 'Jika nilai terlalu besar, tingkatkan \"sort_buffer_size\".',\n        tableLocksWaited: 'Bilangan kunci jadual',\n        tableLocksWaitedHelper:\n            'Jika nilai terlalu besar, pertimbangkan untuk meningkatkan prestasi pangkalan data anda.',\n        performanceTuning: 'Penalaan prestasi',\n        optimizationScheme: 'Skema pengoptimuman',\n        keyBufferSizeHelper: 'Saiz penimbal untuk indeks',\n        queryCacheSizeHelper: 'Cache pertanyaan. Jika fungsi ini dilumpuhkan, tetapkan parameter ini kepada 0.',\n        tmpTableSizeHelper: 'Saiz cache jadual sementara',\n        innodbBufferPoolSizeHelper: 'Saiz penimbal Innodb',\n        innodbLogBufferSizeHelper: 'Saiz penimbal log Innodb',\n        sortBufferSizeHelper: '* sambungan, saiz penimbal per urutan benang',\n        readBufferSizeHelper: '* sambungan, saiz penimbal bacaan',\n        readRndBufferSizeHelper: '* sambungan, saiz penimbal bacaan rawak',\n        joinBufferSizeHelper: '* sambungan, saiz cache jadual perkaitan',\n        threadStackelper: '* sambungan, saiz tumpukan per benang',\n        binlogCacheSizeHelper: '* sambungan, saiz cache log binari (gandaan 4096)',\n        threadCacheSizeHelper: 'Saiz kolam benang',\n        tableOpenCacheHelper: 'Cache jadual',\n        maxConnectionsHelper: 'Sambungan maksimum',\n        restart: 'Mulakan semula',\n        slowLog: 'Log lambat',\n        noData: 'Tiada log lambat lagi.',\n        isOn: 'Hidup',\n        longQueryTime: 'Ambang (saat)',\n        thresholdRangeHelper: 'Sila masukkan ambang yang betul (1 - 600).',\n        timeout: 'Tamat masa(saat)',\n        timeoutHelper: 'Tempoh tamat masa sambungan tidak aktif. 0 menunjukkan sambungan sentiasa aktif.',\n        maxclients: 'Klien maksimum',\n        requirepassHelper:\n            'Biarkan ruangan ini kosong untuk menunjukkan bahawa tiada kata laluan telah ditetapkan. Perubahan perlu disimpan secara berasingan dan kontena perlu dimulakan semula!',\n        databases: 'Bilangan pangkalan data',\n        maxmemory: 'Penggunaan memori maksimum',\n        maxmemoryHelper: '0 menunjukkan tiada had.',\n        tcpPort: 'Port mendengar semasa.',\n        uptimeInDays: 'Hari beroperasi.',\n        connectedClients: 'Bilangan klien yang disambungkan.',\n        usedMemory: 'Penggunaan memori semasa Redis.',\n        usedMemoryRss: 'Saiz memori yang diminta daripada sistem pengendalian.',\n        usedMemoryPeak: 'Penggunaan memori puncak Redis.',\n        memFragmentationRatio: 'Nisbah pemecahan memori.',\n        totalConnectionsReceived: 'Jumlah bilangan klien yang disambungkan sejak dijalankan.',\n        totalCommandsProcessed: 'Jumlah bilangan arahan yang dilaksanakan sejak dijalankan.',\n        instantaneousOpsPerSec: 'Bilangan arahan yang dilaksanakan oleh pelayan setiap saat.',\n        keyspaceHits: 'Bilangan kali kunci pangkalan data berjaya dijumpai.',\n        keyspaceMisses: 'Bilangan percubaan gagal untuk mencari kunci pangkalan data.',\n        hit: 'Nisbah pencarian kunci pangkalan data yang berjaya.',\n        latestForkUsec: 'Bilangan mikrodetik yang dihabiskan pada operasi fork() terakhir.',\n        redisCliHelper: '\"redis-cli\" perkhidmatan tidak dikesan. Aktifkan perkhidmatan terlebih dahulu.',\n        redisQuickCmd: 'Arahan pantas Redis',\n        recoverHelper: 'Ini akan menimpa data dengan [{0}]. Adakah anda mahu meneruskan?',\n        submitIt: 'Tindih data',\n        baseConf: 'Asas',\n        allConf: 'Semua',\n        restartNow: 'Mula semula sekarang',\n        restartNowHelper1:\n            'Anda perlu memulakan semula sistem selepas perubahan konfigurasi berkuat kuasa. Jika data anda perlu dipelihara, lakukan operasi simpan terlebih dahulu.',\n        restartNowHelper: 'Perubahan ini hanya akan berkuat kuasa selepas sistem dimulakan semula.',\n        persistence: 'Ketekalan',\n        rdbHelper1: 'saat, masukkan',\n        rdbHelper2: 'data',\n        rdbHelper3: 'Memenuhi mana-mana syarat akan mencetuskan ketekalan RDB.',\n        rdbInfo: 'Pastikan nilai dalam senarai peraturan berada dalam julat 1 hingga 100000',\n        containerConn: 'Sambungan kontena',\n        copyConnURL: 'Salin URL sambungan',\n        connAddress: 'Alamat',\n        containerConnHelper:\n            'Alamat sambungan ini boleh digunakan oleh aplikasi yang berjalan pada runtime laman web (PHP, dll.) atau kontena.',\n        remoteConn: 'Sambungan luaran',\n        remoteConnHelper2: 'Gunakan alamat ini untuk persekitaran bukan kontena atau sambungan luar.',\n        remoteConnHelper3:\n            'Alamat akses lalai ialah IP hos. Untuk mengubahnya, pergi ke item konfigurasi \"Alamat Akses Lalai\" pada halaman tetapan panel.',\n        localIP: 'IP Tempatan',\n    },\n    aiTools: {\n        agents: {\n            agents: 'Agen',\n            agent: 'Agen',\n            account: 'Akaun model',\n            noAccountHint: 'Pilih akaun model sedia ada atau tambah yang baharu.',\n            accountCount: 'Akaun model {0}',\n            syncAgents: 'Segerakkan agen berkaitan',\n            syncAgentsHelper: 'Kemas kini openclaw.json untuk agen yang menggunakan akaun model ini',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: 'Versi aplikasi',\n            webuiPort: 'Port WebUI',\n            allowedOrigins: 'Alamat akses',\n            allowedOriginsHelper:\n                'Masukkan satu alamat akses penuh bagi setiap baris. HTTPS disyorkan, contohnya https://192.168.1.2:18789. Isikan secara manual jika alamat akses lalai belum dikonfigurasi.',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: 'Masukkan sekurang-kurangnya satu alamat akses',\n            allowedOriginsInvalid: 'Gunakan format http(s)://hos-atau-ip[:port]',\n            provider: 'Penyedia model',\n            apiKey: 'Kunci API',\n            baseUrl: 'URL asas',\n            accountModels: 'Model Catalog',\n            accountModelsHelper: 'Configure the models this account exposes to OpenClaw for switching and settings',\n            accountModelsRequired: 'Configure at least one model',\n            accountModelsDuplicate: 'Duplicate models exist in the catalog',\n            modelPool: 'Model Pool',\n            modelPoolHelper:\n                'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',\n            modelInputTypes: 'Input Types',\n            reasoning: 'Reasoning Model',\n            token: 'Token',\n            manualModel: 'Input manual',\n            verified: 'Disahkan',\n            verifySkipped: 'Tanpa pengesahan',\n            configTitle: 'Configuration',\n            settingsTab: 'Settings',\n            securityTab: 'Security',\n            otherTab: 'Other',\n            timeZone: 'Zon Waktu',\n            browserEnabled: 'Browser Enabled',\n            switchModelSuccess: 'Model switched successfully',\n            channelsTab: 'Channels',\n            wecom: 'WeCom',\n            dingtalk: 'DingTalk',\n            feishu: 'Feishu',\n            pluginNotInstalled: 'Plugin belum dipasang. Sila pasang dahulu.',\n            dmPolicy: 'DM Policy',\n            groupPolicy: 'Group Policy',\n            policyPairing: 'Pairing',\n            policyAllowlist: 'Allowlist',\n            policyOpen: 'Open',\n            policyDisabled: 'Disabled',\n            botName: 'Bot Name',\n            botId: 'Bot ID',\n            appId: 'App ID',\n            appSecret: 'App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: 'DM Allowlist',\n            allowFromHelper: 'One sender ID per line. Used only when DM Policy is Allowlist.',\n            allowFromPlaceholder: 'One sender ID per line',\n            groupAllowFrom: 'Group Allowlist',\n            groupAllowFromHelper: 'One group ID per line. Used only when Group Policy is Allowlist.',\n            groupAllowFromPlaceholder: 'One group ID per line',\n            allowFromRequired: 'Enter at least one allowlist entry',\n            saveAndRestartGateway: 'Save and restart gateway',\n            pairingCode: 'Pairing Code',\n            pairingCodePlaceholder: 'Enter pairing code',\n            approvePairing: 'Approve Pairing',\n            feishuRequired: 'Please fill botName / appId / appSecret',\n            saveSuccess: 'Saved successfully',\n            pairingCodeRequired: 'Please enter pairing code',\n            pairingApproveSuccess: 'Pairing approved successfully',\n            customProviderHelper: 'Penyedia model tersuai tidak mengesahkan sama ada akaun boleh digunakan',\n            feishuSaveSuccess: 'Disimpan ke Feishu',\n        },\n        model: {\n            model: 'Model',\n            localModel: 'Model Tempatan',\n            create: 'Tambah Model',\n            create_helper: 'Tarik \"{0}\"',\n            ollama_doc: 'Anda boleh melawat laman web rasmi Ollama untuk mencari dan menemui lebih banyak model.',\n            container_conn_helper: 'Gunakan alamat ini untuk akses atau sambungan antara kontena',\n            ollama_sync:\n                'Sincronizando o modelo Ollama, encontrou que os seguintes modelos não existem, deseja excluí-los?',\n            from_remote: 'Este modelo não foi baixado via 1Panel, sem logs de pull relacionados.',\n            no_logs: 'Os logs de pull deste modelo foram excluídos e não podem ser visualizados.',\n            vllmVersionHelper: 'Untuk pelayan FusionXpark GB 10, sila pilih versi -cu130.',\n        },\n        proxy: {\n            proxy: 'Peningkatan Proksi AI',\n            proxyHelper1: 'Ikatkan domain dan aktifkan HTTPS untuk meningkatkan keselamatan penghantaran',\n            proxyHelper2: 'Hadkan akses IP untuk mengelakkan pendedahan di internet awam',\n            proxyHelper3: 'Aktifkan penstriman',\n            proxyHelper4: 'Setelah selesai, anda boleh melihat dan mengurusnya dalam senarai laman web',\n            proxyHelper5:\n                'Selepas diaktifkan, anda boleh melumpuhkan akses luaran ke port dalam App Store - Dipasang - Ollama - Parameter untuk meningkatkan keselamatan.',\n            proxyHelper6: 'Untuk melumpuhkan konfigurasi proksi, anda boleh memadamnya dari senarai laman web.',\n            whiteListHelper: 'Hadkan akses kepada hanya IP dalam senarai putih',\n        },\n        gpu: {\n            gpu: 'Pemantauan GPU',\n            gpuHelper: 'Sistem tidak mengesan arahan NVIDIA-SMI atau XPU-SMI. Sila periksa dan cuba lagi!',\n            process: 'Maklumat Proses',\n            type: 'Jenis',\n            typeG: 'Grafik',\n            typeC: 'Pengiraan',\n            typeCG: 'Pengiraan+Grafik',\n            processName: 'Nama Proses',\n            shr: 'Memori Kongsi',\n            temperatureHelper: 'Suhu GPU tinggi boleh menyebabkan pengurangan frekuensi GPU',\n            gpuUtil: 'Penggunaan GPU',\n            temperature: 'Suhu',\n            performanceState: 'Status Prestasi',\n            powerUsage: 'Penggunaan Kuasa',\n            memoryUsage: 'Penggunaan Memori',\n            fanSpeed: 'Kelajuan Kipas',\n            power: 'Kuasa',\n            powerCurrent: 'Kuasa Semasa',\n            powerLimit: 'Had Kuasa',\n            memory: 'Memori',\n            memoryUsed: 'Memori Digunakan',\n            memoryTotal: 'Jumlah Memori',\n            percent: 'Penggunaan',\n            base: 'Maklumat Asas',\n            driverVersion: 'Versi Pemacu',\n            cudaVersion: 'Versi CUDA',\n            processMemoryUsage: 'Penggunaan Memori',\n            performanceStateHelper: 'Dari P0 (prestasi maksimum) hingga P12 (prestasi minimum)',\n            busID: 'Alamat Bas',\n            persistenceMode: 'Mod Kegigihan',\n            enabled: 'Diaktifkan',\n            disabled: 'Dilumpuhkan',\n            persistenceModeHelper:\n                'Mod kegigihan bertindak balas kepada tugas dengan lebih cepat, tetapi penggunaan kuasa siap sedia akan meningkat dengan sewajarnya',\n            displayActive: 'Permulaan GPU',\n            displayActiveT: 'Ya',\n            displayActiveF: 'Tidak',\n            ecc: 'Teknologi Pemeriksaan dan Pembetulan Ralat',\n            computeMode: 'Mod Pengiraan',\n            default: 'Lalai',\n            exclusiveProcess: 'Proses Eksklusif',\n            exclusiveThread: 'Benang Eksklusif',\n            prohibited: 'Dilarang',\n            defaultHelper: 'Lalai: Proses boleh dilaksanakan serentak',\n            exclusiveProcessHelper:\n                'Proses Eksklusif: Hanya satu konteks CUDA boleh menggunakan GPU, tetapi boleh dikongsi oleh berbilang benang',\n            exclusiveThreadHelper: 'Benang Eksklusif: Hanya satu benang dalam konteks CUDA boleh menggunakan GPU',\n            prohibitedHelper: 'Dilarang: Pelaksanaan proses serentak tidak dibenarkan',\n            migModeHelper:\n                'Digunakan untuk mencipta contoh MIG, melaksanakan pengasingan fizikal GPU pada lapisan pengguna.',\n            migModeNA: 'Tidak Disokong',\n            current: 'Pemantauan Masa Nyata',\n            history: 'Rekod Sejarah',\n            notSupport: 'Versi atau pemacu semasa tidak menyokong paparan parameter ini.',\n            processCount: 'Bilangan proses',\n        },\n        mcp: {\n            server: 'Pelayan MCP',\n            baseUrl: 'Laluan Akses Luar',\n            baseUrlHelper: 'Contoh: http://192.168.1.2:8000',\n            ssePath: 'Laluan SSE',\n            ssePathHelper: 'Contoh: /sse, berhati-hati jangan bertindan dengan pelayan lain',\n            environment: 'Pemboleh Ubah Persekitaran',\n            envKey: 'Nama Pemboleh Ubah',\n            envValue: 'Nilai Pemboleh Ubah',\n            externalUrl: 'Alamat Sambungan Luar',\n            operatorHelper: 'Akan melakukan operasi {1} pada {0}, teruskan?',\n            domain: 'Alamat Akses Lalai',\n            domainHelper: 'Contoh: 192.168.1.1 atau example.com',\n            bindDomain: 'Sematkan Laman Web',\n            commandPlaceHolder: 'Kini hanya menyokong perintah pelancaran npx dan binari',\n            importMcpJson: 'Import Konfigurasi Pelayan MCP',\n            importMcpJsonError: 'Struktur mcpServers tidak betul',\n            bindDomainHelper:\n                'Setelah mengikat laman web, ia akan mengubah alamat akses semua Pelayan MCP yang dipasang dan menutup akses luaran ke pelabuhan',\n            outputTransport: 'Jenis Output',\n            streamableHttpPath: 'Laluan Streaming',\n            streamableHttpPathHelper: 'Contoh: /mcp, elakkan daripada bertindan dengan pelayan lain',\n            npxHelper: 'Sesuai untuk mcp yang dimulakan dengan npx atau binari',\n            uvxHelper: 'Sesuai untuk mcp yang dimulakan dengan uvx',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: 'Direktori Model',\n            commandHelper: 'Jika akses luar diperlukan, tetapkan port dalam arahan sama dengan port aplikasi',\n            imageAlert:\n                'Disebabkan saiz imej yang besar, disyorkan untuk memuat turun imej secara manual ke pelayan sebelum pemasangan',\n            modelSpeedup: 'Dayakan pecutan model',\n            modelType: 'Jenis model',\n        },\n    },\n    container: {\n        createByCommand: 'Cipta melalui perintah',\n        importContainerBackupTip: 'Import fail sandaran kontena, hanya format .tar.gz disokong',\n        importComposeBackupTip: 'Import fail sandaran compose, hanya format .tar.gz disokong',\n        stopContainerBeforeBackup: 'Hentikan kontena sebelum sandaran',\n        stopComposeBeforeBackup: 'Hentikan compose sebelum sandaran',\n        stopBeforeBackupHelper:\n            'Apabila diaktifkan, kontena atau perkhidmatan compose akan dihentikan sebelum sandaran dan dipulihkan secara automatik selepas selesai untuk memastikan ketekalan data.',\n        commandInput: 'Input perintah',\n        commandRule: 'Sila masukkan arahan docker run yang sah untuk mencipta kontena.',\n        commandHelper: 'Arahan ini akan dijalankan pada pelayan untuk mencipta kontena. Teruskan?',\n        updateHelper1: 'Dikesan bahawa kontena ini berasal dari gedung aplikasi. Sila perhatikan dua perkara berikut:',\n        updateHelper2: '1. Pengubahsuaian semasa tidak akan diselaraskan ke aplikasi yang dipasang di gedung aplikasi.',\n        updateHelper3:\n            '2. Jika anda mengubah aplikasi di halaman yang dipasang, kandungan yang sedang diedit akan menjadi tidak sah.',\n        updateHelper4:\n            'Mengedit kontena memerlukan pembinaan semula, dan sebarang data yang tidak berterusan akan hilang. Adakah anda mahu meneruskan?',\n        containerList: 'Senarai kontena',\n        operatorHelper: '{0} akan dilakukan pada kontena berikut. Adakah anda mahu meneruskan?',\n        operatorAppHelper:\n            'Operasi \"{0}\" akan dilakukan pada kontena berikut dan mungkin mempengaruhi perkhidmatan yang sedang berjalan. Adakah anda mahu meneruskan?',\n        containerDeleteHelper:\n            \"Dikesan bahawa bekas ini berasal dari Kedai Apl. Memadam bekas tidak akan mengalihnya sepenuhnya dari 1Panel. Untuk memadam sepenuhnya, sila pergi ke Kedai Apl -> menu 'Dipasang' atau 'Persekitaran Runtime'. Teruskan?\",\n        start: 'Mulakan',\n        stop: 'Hentikan',\n        restart: 'Mulakan semula',\n        kill: 'Hentikan paksa',\n        pause: 'Jeda',\n        unpause: 'Sambung semula',\n        rename: 'Tukar nama',\n        remove: 'Buang',\n        removeAll: 'Buang semua',\n        containerPrune: 'Prune',\n        containerPruneHelper1: 'Ini akan memadam semua kontena yang berada dalam keadaan dihentikan.',\n        containerPruneHelper2:\n            'Jika kontena berasal dari gedung aplikasi, anda perlu ke \"Gedung Aplikasi -> Dipasang\" dan klik butang \"Bangun Semula\" untuk memasangnya semula selepas pembersihan.',\n        containerPruneHelper3: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',\n        imagePrune: 'Prune',\n        imagePruneSome: 'Bersihkan yang tidak berlabel',\n        imagePruneSomeEmpty: 'Tiada imej dengan tag \"none\" yang boleh dibersihkan.',\n        imagePruneSomeHelper: 'Bersihkan imej dengan tag \"none\" yang tidak digunakan oleh mana-mana kontena.',\n        imagePruneAll: 'Bersihkan yang tidak digunakan',\n        imagePruneAllEmpty: 'Tiada imej yang tidak digunakan boleh dibersihkan.',\n        imagePruneAllHelper: 'Bersihkan imej yang tidak digunakan oleh mana-mana kontena.',\n        networkPrune: 'Prune',\n        networkPruneHelper: 'Ini akan membuang semua rangkaian yang tidak digunakan. Adakah anda mahu meneruskan?',\n        volumePrune: 'Prune',\n        volumePruneHelper: 'Ini akan membuang semua volum tempatan yang tidak digunakan. Adakah anda mahu meneruskan?',\n        cleanSuccess: 'Operasi berjaya, bilangan yang dibersihkan kali ini: {0}!',\n        cleanSuccessWithSpace:\n            'Operasi berjaya. Bilangan cakera yang dibersihkan kali ini ialah {0}. Ruang cakera yang dibebaskan ialah {1}!',\n        unExposedPort: 'Alamat pemetaan port semasa ialah 127.0.0.1, yang tidak dapat mengaktifkan akses luaran.',\n        upTime: 'Waktu hidup',\n        fetch: 'Dapatkan',\n        lines: 'Baris',\n        linesHelper: 'Sila masukkan bilangan log yang betul untuk diambil!',\n        downloadLinesHelper: 'Pilih atau masukkan bilangan baris log untuk dimuat turun.',\n        lastDay: 'Hari terakhir',\n        last4Hour: '4 jam terakhir',\n        lastHour: 'Jam terakhir',\n        last10Min: '10 minit terakhir',\n        cleanLog: 'Bersihkan log',\n        downLogHelper1: 'Ini akan memuat turun semua log dari kontena {0}. Adakah anda mahu meneruskan?',\n        downLogHelper2: 'Ini akan memuat turun log terkini {0} dari kontena {0}. Adakah anda mahu meneruskan?',\n        cleanLogHelper: 'Ini memerlukan memulakan semula kontena dan tidak boleh diundur. Adakah anda mahu meneruskan?',\n        newName: 'Nama baru',\n        source: 'Penggunaan sumber',\n        cpuUsage: 'Penggunaan CPU',\n        cpuTotal: 'Jumlah CPU',\n        core: 'Teras',\n        memUsage: 'Penggunaan memori',\n        memTotal: 'Had memori',\n        memCache: 'Cache memori',\n        loadSize: 'Dapatkan Saiz Bekas',\n        ip: 'Alamat IP',\n        cpuShare: 'Bahagian CPU',\n        cpuShareHelper:\n            'Enjin kontena menggunakan nilai asas 1024 untuk bahagian CPU. Anda boleh meningkatkannya untuk memberikan lebih masa CPU kepada kontena.',\n        inputIpv4: 'Contoh: 192.168.1.1',\n        inputIpv6: 'Contoh: 2001:0db8:85a3:0000:0000:8a2e:0370:7334',\n        diskUsage: 'Penggunaan Cakera',\n        localVolume: 'Isipadu Storan Tempatan',\n        buildCache: 'Cache Binaan',\n        usage: 'Digunakan: {0}, Boleh Dibebaskan: {1}',\n        clean: 'Bebaskan',\n        imageClean:\n            'Membersihkan imej akan memadam semua imej yang tidak digunakan. Operasi ini tidak boleh dikembalikan. Teruskan?',\n        containerClean:\n            'Membersihkan bekas akan memadam semua bekas yang dihentikan (termasuk aplikasi berhenti dari Kedai Apl). Operasi ini tidak boleh dikembalikan. Teruskan?',\n        sizeRw: 'Saiz Lapisan Bekas',\n        sizeRwHelper: 'Saiz lapisan boleh tulis yang unik untuk bekas',\n        sizeRootFs: 'Saiz Maya',\n        sizeRootFsHelper: 'Jumlah saiz semua lapisan imej yang disandarkan oleh bekas + lapisan bekas',\n        containerFromAppHelper:\n            'Dikesan bahawa kontena ini berasal dari gedung aplikasi. Operasi aplikasi boleh menyebabkan suntingan semasa menjadi tidak sah.',\n        containerFromAppHelper1:\n            'Klik butang [Param] dalam senarai aplikasi yang dipasang untuk memasuki halaman penyuntingan dan mengubah nama kontena.',\n        command: 'Arahan',\n        console: 'Interaksi kontena',\n        tty: 'Peruntukkan TTY palsu (-t)',\n        openStdin: 'Pastikan STDIN terbuka walaupun tidak disambungkan (-i)',\n        custom: 'Kustom',\n        emptyUser: 'Apabila kosong, anda akan log masuk sebagai lalai',\n        privileged: 'Privileged',\n        privilegedHelper:\n            'Benarkan kontena menjalankan operasi teristimewa tertentu pada hos, yang boleh meningkatkan risiko kontena. Gunakan dengan berhati-hati!',\n        upgradeHelper: 'Nama Repository/Nama Imej: Versi Imej',\n        upgradeWarning2:\n            'Operasi peningkatan memerlukan pembinaan semula kontena, sebarang data yang tidak disimpan akan hilang. Adakah anda mahu meneruskan?',\n        oldImage: 'Imej semasa',\n        sameImageContainer: 'Kontena imej sama',\n        sameImageHelper: 'Kontena yang menggunakan imej sama boleh dinaik taraf secara berkumpulan setelah dipilih',\n        targetImage: 'Imej sasaran',\n        imageLoadErr: 'Tiada nama imej dikesan untuk kontena',\n        imageUpdateTagEmpty: 'Tiada tag imej yang boleh dikemas kini dikesan',\n        appHelper:\n            'Kontena berasal dari gedung aplikasi, dan peningkatan boleh menyebabkan perkhidmatan tidak tersedia.',\n        input: 'Input manual',\n        forcePull: 'Tarik imej sentiasa ',\n        forcePullHelper: 'Ini akan mengabaikan imej sedia ada di pelayan dan menarik imej terkini dari pendaftaran.',\n        imageUpdateHelper:\n            'Semak tag yang sama di registry, jika ada kemas kini barulah tarik dan kemas kini imej tempatan.',\n        server: 'Hos',\n        serverExample: '80, 80-88, ip:80 atau ip:80-88',\n        containerExample: '80 atau 80-88',\n        exposePort: 'Dedahkan port',\n        exposeAll: 'Dedahkan semua',\n        cmdHelper: 'Contoh: nginx -g \"daemon off;\"',\n        entrypointHelper: 'Contoh: docker-entrypoint.sh',\n        autoRemove: 'Buang automatik',\n        cpuQuota: 'Bilangan teras CPU',\n        memoryLimit: 'Memori',\n        limitHelper: 'Jika ditetapkan kepada 0, ia bermakna tiada had. Nilai maksimum ialah {0}',\n        mount: 'Mount',\n        volumeOption: 'Volume',\n        hostOption: 'Hos',\n        serverPath: 'Laluan pelayan',\n        containerDir: 'Laluan kontena',\n        networkEmptyHelper: 'Sila pastikan pemilihan rangkaian bekas adalah betul',\n        modeRW: 'RW',\n        modeR: 'R',\n        sharedLabel: 'Mod Penyebaran',\n        private: 'Peribadi',\n        privateHelper: 'Perubahan pemasangan dalam bekas dan hos tidak saling mempengaruhi',\n        rprivate: 'Peribadi Rekursif',\n        rprivateHelper: 'Semua pemasangan dalam bekas diasingkan sepenuhnya dari hos',\n        shared: 'Berkongsi',\n        sharedHelper: 'Perubahan pemasangan dalam hos dan bekas kelihatan antara satu sama lain',\n        rshared: 'Berkongsi Rekursif',\n        rsharedHelper: 'Semua perubahan pemasangan dalam hos dan bekas kelihatan antara satu sama lain',\n        slave: 'Hamba',\n        slaveHelper: 'Bekas dapat melihat perubahan pemasangan hos, tetapi perubahan sendiri tidak mempengaruhi hos',\n        rslave: 'Hamba Rekursif',\n        rslaveHelper: 'Semua pemasangan dalam bekas dapat melihat perubahan hos, tetapi tidak mempengaruhi hos',\n        mode: 'Mod',\n        env: 'Persekitaran',\n        restartPolicy: 'Polisi Mulakan Semula',\n        always: 'sentiasa',\n        unlessStopped: 'melainkan dihentikan',\n        onFailure: 'gagal (lima kali secara lalai)',\n        no: 'tidak pernah',\n        refreshTime: 'Selang penyegaran',\n        cache: 'Cache',\n        image: 'Imej | Imej-imej',\n        imagePull: 'Tarik',\n        imagePullHelper:\n            'Menyokong pemilihan berbilang imej untuk ditarik, tekan Enter selepas memasukkan setiap imej untuk teruskan',\n        imagePush: 'Tekan',\n        imagePushHelper:\n            'Terdapat pengesahan bahawa imej ini mempunyai beberapa tag. Sila pastikan nama imej yang digunakan untuk menolak adalah: {0}',\n        imageDelete: 'Padam imej',\n        repoName: 'Pendaftaran kontena',\n        imageName: 'Nama imej',\n        pull: 'Tarik',\n        path: 'Laluan',\n        importImage: 'Import',\n        buildArgs: 'Argumen Binaan',\n        imageBuild: 'Bina',\n        pathSelect: 'Laluan',\n        label: 'Label',\n        imageTag: 'Tag imej',\n        imageTagHelper:\n            'Menyokong penetapan berbilang tag imej, tekan Enter selepas memasukkan setiap tag untuk teruskan',\n        push: 'Tekan',\n        fileName: 'Nama fail',\n        export: 'Eksport',\n        exportImage: 'Eksport imej',\n        size: 'Saiz',\n        tag: 'Tag',\n        tagHelper: 'Satu setiap baris. Sebagai contoh,\\nkey1=value1\\nkey2=value2',\n        imageNameHelper: 'Nama dan Tag imej, sebagai contoh: nginx:latest',\n        cleanBuildCache: 'Bersihkan cache bina',\n        delBuildCacheHelper:\n            'Ini akan memadam semua artefak cache yang dijana semasa binaan dan tidak boleh diundur. Adakah anda mahu meneruskan?',\n        urlWarning: 'Awalan URL tidak perlu termasuk http:// atau https://. Sila ubah.',\n        network: 'Rangkaian | Rangkaian-rangkaian',\n        networkHelper:\n            'Ini boleh menyebabkan beberapa aplikasi dan persekitaran runtime tidak berfungsi dengan betul. Adakah anda mahu meneruskan?',\n        networkName: 'Nama',\n        driver: 'Pemacu',\n        option: 'Pilihan',\n        attachable: 'Boleh dilampirkan',\n        parentNetworkCard: 'Kad Rangkaian Induk',\n        subnet: 'Subnet',\n        scope: 'Skop IP',\n        gateway: 'Gerbang',\n        auxAddress: 'Kecualikan IP',\n        volume: 'Volum | Volum-volum',\n        volumeDir: 'Direktori volum',\n        nfsEnable: 'Aktifkan storan NFS',\n        nfsAddress: 'Alamat',\n        mountpoint: 'Titik pemasangan',\n        mountpointNFSHelper: 'contoh: /nfs, /nfs-share',\n        options: 'Pilihan',\n        repo: 'Pendaftaran',\n        httpRepoHelper: 'Mengoperasikan repositori jenis HTTP memerlukan mulakan semula perkhidmatan Docker.',\n        httpRepo:\n            'Memilih protokol HTTP memerlukan memulakan semula perkhidmatan Docker untuk menambahkannya ke pendaftaran tidak selamat.',\n        delInsecure: 'Padamkan pendaftaran tidak selamat',\n        delInsecureHelper:\n            'Ini akan memulakan semula perkhidmatan Docker untuk mengeluarkannya dari pendaftaran tidak selamat. Adakah anda mahu meneruskan?',\n        downloadUrl: 'Pelayan',\n        imageRepo: 'Repo imej',\n        repoHelper: 'Adakah ia termasuk repositori cermin/organisasi/projek?',\n        auth: 'Memerlukan pengesahan',\n        mirrorHelper:\n            'Jika terdapat banyak cermin, baris baru mesti dipaparkan, contohnya:\\nhttp://xxxxxx.m.daocloud.io \\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper:\n            'Jika terdapat banyak repositori persendirian, baris baru mesti dipaparkan, contohnya:\\n172.16.10.111:8081 \\n172.16.10.112:8081',\n        compose: 'Compose | Compose-compose',\n        composeFile: 'Fail Susunan',\n        fromChangeHelper: 'Menukar sumber akan membersihkan kandungan yang sedang diedit. Adakah anda mahu meneruskan?',\n        composePathHelper: 'Laluan simpan fail konfigurasi: {0}',\n        composeHelper:\n            'Komposisi yang dicipta melalui editor atau templat 1Panel akan disimpan dalam direktori {0}/docker/compose.',\n        deleteFile: 'Padam fail',\n        deleteComposeHelper:\n            'Padam semua fail berkaitan komposisi kontena, termasuk fail konfigurasi dan fail berterusan. Sila berhati-hati!',\n        deleteCompose: 'Padam komposisi ini.',\n        composeDirectory: 'Direktori',\n        template: 'Templat',\n        composeTemplate: 'Templat Compose | Templat Compose',\n        content: 'Kandungan',\n        contentEmpty: 'Kandungan Compose tidak boleh kosong, sila masukkan dan cuba lagi!',\n        containerNumber: 'Bilangan kontena',\n        containerStatus: 'Status kontena',\n        exited: 'Keluar',\n        running: 'Berjalan ( {0} / {1} )',\n        composeDetailHelper: 'Komposisi dibuat di luar 1Panel. Operasi mula dan berhenti tidak disokong.',\n        composeOperatorHelper: 'Operasi {1} akan dilakukan pada {0}. Adakah anda mahu meneruskan?',\n        composeDownHelper:\n            'Ini akan menghentikan dan menghapuskan semua kontena dan rangkaian di bawah komposisi {0}. Adakah anda mahu meneruskan?',\n        composeEnvHelper2:\n            'Penyelarasan ini dibuat oleh Kedai Apl 1Panel. Sila ubah pembolehubah persekitaran dalam aplikasi yang dipasang.',\n        composeContentPlaceholder: 'Tulis atau tampal kandungan fail docker-compose anda di sini',\n        setting: 'Tetapan | Tetapan',\n        restartHelper: 'Perubahan konfigurasi memerlukan perkhidmatan Docker dimulakan semula untuk berkuat kuasa',\n        operatorStatusHelper: 'Ini akan \"{0}\" perkhidmatan Docker. Adakah anda mahu meneruskan?',\n        dockerStatus: 'Perkhidmatan Docker',\n        daemonJsonPathHelper: 'Pastikan laluan konfigurasi sama seperti yang dinyatakan dalam docker.service.',\n        mirrors: 'Cermin pendaftaran',\n        mirrorsHelper: '',\n        mirrorsHelper2: 'Untuk maklumat lanjut, lihat dokumen rasmi.',\n        registries: 'Pendaftaran tidak selamat',\n        ipv6Helper:\n            'Apabila IPv6 diaktifkan, anda perlu menambah rangkaian kontena IPv6. Rujuk dokumen rasmi untuk langkah konfigurasi tertentu.',\n        ipv6CidrHelper: 'Julat kolam alamat IPv6 untuk kontena',\n        ipv6TablesHelper: 'Konfigurasi automatik Docker IPv6 untuk peraturan iptables.',\n        experimentalHelper:\n            'Mengaktifkan ip6tables memerlukan konfigurasi ini dihidupkan; jika tidak, ip6tables akan diabaikan.',\n        cutLog: 'Pilihan log',\n        cutLogHelper1: 'Konfigurasi semasa hanya akan mempengaruhi kontena yang baru dicipta.',\n        cutLogHelper2: 'Kontena sedia ada perlu dicipta semula agar konfigurasi berkuat kuasa.',\n        cutLogHelper3:\n            'Harap maklum bahawa mencipta semula kontena boleh menyebabkan kehilangan data. Jika kontena anda mengandungi data penting, pastikan membuat sandaran sebelum melakukan operasi pembinaan semula.',\n        maxSize: 'Saiz maksimum',\n        maxFile: 'Fail maksimum',\n        liveHelper:\n            'Secara lalai, apabila daemon Docker ditamatkan, ia mematikan kontena yang sedang berjalan. Anda boleh mengkonfigurasi daemon supaya kontena kekal berjalan jika daemon menjadi tidak tersedia. Fungsi ini dipanggil pemulihan langsung. Pilihan pemulihan langsung membantu mengurangkan waktu henti kontena akibat kerosakan daemon, pemadaman terancang, atau peningkatan.',\n        liveWithSwarmHelper: 'Konfigurasi daemon live-restore tidak serasi dengan mod swarm.',\n        iptablesDisable: 'Tutup iptables',\n        iptablesHelper1: 'Konfigurasi automatik peraturan iptables untuk Docker.',\n        iptablesHelper2:\n            'Melumpuhkan iptables akan menyebabkan kontena tidak dapat berkomunikasi dengan rangkaian luaran.',\n        daemonJsonPath: 'Laluan Konfigurasi',\n        serviceUnavailable: 'Perkhidmatan Docker tidak dimulakan pada masa ini.',\n        startIn: ' untuk dimulakan',\n        sockPath: 'Socket domain Unix',\n        sockPathHelper: 'Saluran komunikasi antara daemon Docker dan klien.',\n        sockPathHelper1: 'Laluan lalai: /var/run/docker-x.sock',\n        sockPathMsg:\n            'Menyimpan tetapan Laluan Socket boleh menyebabkan perkhidmatan Docker tidak tersedia. Adakah anda mahu meneruskan?',\n        sockPathErr: 'Sila pilih atau masukkan laluan fail sock Docker yang betul',\n        related: 'Berkaitan',\n        includeAppstore: 'Tunjukkan kontena dari gedung aplikasi',\n        excludeAppstore: 'Sembunyikan Kontena Kedai Aplikasi',\n        cleanDockerDiskZone: 'Bersihkan ruang cakera yang digunakan oleh Docker',\n        cleanImagesHelper: '( Bersihkan semua imej yang tidak digunakan oleh mana-mana kontena )',\n        cleanContainersHelper: '( Bersihkan semua kontena yang dihentikan )',\n        cleanVolumesHelper: '( Bersihkan semua volum tempatan yang tidak digunakan )',\n        makeImage: 'Cipta imej',\n        newImageName: 'Nama imej baru',\n        commitMessage: 'Mesej komit',\n        author: 'Pengarang',\n        ifPause: 'Jeda Kontena Semasa Penciptaan',\n        ifMakeImageWithContainer: 'Cipta Imej Baru daripada Kontena Ini?',\n        finishTime: 'Masa berhenti terakhir',\n        workingDir: 'Direktori kerja',\n        resource: 'Sumber',\n        macAddr: 'Alamat MAC',\n        volumeHelper: 'Pastikan kandungan volum storan adalah betul',\n        exportHelper: 'Eksport imej terpilih ke satu fail tar.',\n        goSetting: 'Pergi ke edit',\n    },\n    cronjob: {\n        importHelper:\n            'Tugas terjadual dengan nama sama akan dilangkau secara automatik semasa import. Tugas akan ditetapkan ke status 【Lumpuh】 secara lalai, dan ditetapkan ke status 【Menunggu Edit】 apabila perkaitan data tidak normal.',\n        changeStatus: 'Tukar status',\n        disableMsg:\n            'Ini akan menghentikan tugas berjadual daripada dilaksanakan secara automatik. Adakah anda mahu meneruskan?',\n        enableMsg: 'Ini akan membenarkan tugas berjadual dilaksanakan secara automatik. Adakah anda mahu meneruskan?',\n        taskType: 'Jenis',\n        record: 'Rekod',\n        viewRecords: 'Rekod',\n        shell: 'Shell',\n        stop: 'Hentikan Manual',\n        stopHelper: 'Operasi ini akan memaksa menghentikan pelaksanaan tugas semasa. Teruskan?',\n        log: 'Log sandaran',\n        logHelper: 'Log sistem sandaran',\n        ogHelper1: '1. Log Sistem 1Panel ',\n        logHelper2: '2. Log log masuk SSH pelayan ',\n        logHelper3: '3. Semua log laman web ',\n        containerCheckBox: 'Dalam kontena (tidak perlu masukkan arahan kontena)',\n        containerName: 'Nama kontena',\n        ntp: 'Penyelarasan masa',\n        ntp_helper: 'Anda boleh mengkonfigurasi pelayan NTP di halaman Tetapan Pantas Alat.',\n        app: 'Aplikasi sandaran',\n        website: 'Laman web sandaran',\n        rulesHelper: 'Menyokong pelbagai peraturan pengecualian, dipisahkan dengan koma Inggeris , contoh: *.log,*.sql',\n        lastRecordTime: 'Waktu pelaksanaan terakhir',\n        all: 'Semua',\n        failedRecord: 'Rekod kegagalan',\n        successRecord: 'Rekod berjaya',\n        database: 'Pangkalan data sandaran',\n        backupArgs: 'Argumen Sandaran',\n        backupArgsHelper:\n            'Argumen sandaran yang tidak disenaraikan boleh dimasukkan dan dipilih secara manual. Contoh: Masukkan --no-data dan pilih pilihan pertama dari senarai juntai bawah.',\n        singleTransaction:\n            'Sandaran jadual InnoDB menggunakan transaksi tunggal, sesuai untuk sandaran data isipadu besar',\n        quick: 'Baca data baris demi baris daripada memuatkan keseluruhan jadual ke dalam ingatan, sesuai untuk sandaran data isipadu besar dan mesin ingatan rendah',\n        skipLockTables: 'Sandaran tanpa mengunci semua jadual, sesuai untuk pangkalan data konkuren tinggi',\n        missBackupAccount: 'Akaun sandaran tidak dijumpai',\n        syncDate: 'Waktu penyelarasan',\n        clean: 'Bersihkan cache',\n        curl: 'Akses URL',\n        taskName: 'Nama',\n        cronSpec: 'Kitaran pencetus',\n        cronSpecDoc:\n            'Kitaran pelaksanaan tersuai hanya menyokong format [minit jam hari bulan minggu], contohnya 0 0 * * *. Untuk maklumat lanjut, sila rujuk dokumen rasmi.',\n        cronSpecHelper: 'Masukkan tempoh pelaksanaan yang betul',\n        cleanHelper:\n            'Operasi ini merekodkan semua rekod pelaksanaan tugas, fail sandaran, dan fail log. Adakah anda mahu meneruskan?',\n        directory: 'Direktori sandaran',\n        sourceDir: 'Direktori sumber',\n        snapshot: 'Snapshot sistem',\n        allOptionHelper:\n            'Pelan tugas semasa adalah untuk menyandarkan semua [{0}]. Muat turun terus tidak disokong buat masa ini. Anda boleh menyemak senarai sandaran dalam menu [{0}].',\n        exclusionRules: 'Peraturan pengecualian',\n        exclusionRulesHelper:\n            'Pilih atau masukkan peraturan pengecualian, tekan Enter selepas setiap set untuk teruskan. Peraturan pengecualian akan digunakan untuk semua operasi mampatan dalam sandaran ini',\n        default_download_path: 'Pautan muat turun lalai',\n        saveLocal: 'Simpan sandaran tempatan (sama seperti bilangan salinan storan awan)',\n        url: 'Alamat URL',\n        urlHelper: 'Sila masukkan alamat URL yang sah',\n        targetHelper: 'Akaun sandaran diselenggara dalam tetapan panel.',\n        withImageHelper: 'Sandarkan imej kedai aplikasi, tetapi ini akan meningkatkan saiz fail snapshot.',\n        ignoreApp: 'Kecualikan aplikasi',\n        withImage: 'Sandarkan Imej Aplikasi',\n        retainCopies: 'Simpan salinan',\n        retryTimes: 'Bilangan Cubaan Semula',\n        timeout: 'Masa Tamat',\n        ignoreErr: 'Abaikan ralat',\n        ignoreErrHelper: 'Abaikan ralat semasa sandaran untuk memastikan semua tugas sandaran dilaksanakan',\n        retryTimesHelper: '0 bermaksud tiada cubaan semula selepas gagal',\n        retainCopiesHelper: 'Bilangan salinan untuk menyimpan rekod pelaksanaan dan log',\n        retainCopiesHelper1: 'Bilangan salinan untuk menyimpan fail sandaran',\n        retainCopiesUnit: ' salinan (Lihat)',\n        cronSpecRule: 'Format tempoh pelaksanaan dalam baris {0} adalah salah. Sila semak dan cuba lagi!',\n        perMonthHelper: 'Laksanakan pada hari ke-{0} setiap bulan pada {1}:{2}',\n        perWeekHelper: 'Laksanakan setiap minggu pada hari {0} pada {1}:{2}',\n        perDayHelper: 'Laksanakan setiap hari pada {0}:{1}',\n        perHourHelper: 'Laksanakan setiap jam pada minit ke-{0}',\n        perNDayHelper: 'Laksanakan setiap {0} hari pada {1}:{2}',\n        perNHourHelper: 'Laksanakan setiap {0} jam pada {1}',\n        perNMinuteHelper: 'Laksanakan setiap {0} minit',\n        perNSecondHelper: 'Laksanakan setiap {0} saat',\n        perMonth: 'Setiap bulan',\n        perWeek: 'Setiap minggu',\n        perHour: 'Setiap jam',\n        perNDay: 'Setiap N hari',\n        perDay: 'Setiap hari',\n        perNHour: 'Setiap N jam',\n        perNMinute: 'Setiap N minit',\n        perNSecond: 'Setiap N saat',\n        day: 'hari',\n        dayUnit: 'h',\n        monday: 'Isnin',\n        tuesday: 'Selasa',\n        wednesday: 'Rabu',\n        thursday: 'Khamis',\n        friday: 'Jumaat',\n        saturday: 'Sabtu',\n        sunday: 'Ahad',\n        shellContent: 'Skrip',\n        errRecord: 'Log salah',\n        errHandle: 'Kegagalan pelaksanaan tugas cron',\n        noRecord: 'Picu Tugas Cron, dan anda akan melihat rekod di sini.',\n        cleanData: 'Bersihkan data',\n        cleanRemoteData: 'Padam data jarak jauh',\n        cleanDataHelper: 'Padam fail sandaran yang dijana semasa tugas ini.',\n        noLogs: 'Tiada keluaran tugas lagi...',\n        errPath: 'Laluan sandaran [{0}] salah, tidak boleh dimuat turun!',\n        cutWebsiteLog: 'Putaran log laman web',\n        cutWebsiteLogHelper: 'Fail log yang diputar akan disandarkan ke direktori sandaran 1Panel.',\n        syncIpGroup: 'Segerakkan kumpulan IP WAF',\n        requestExpirationTime: 'Waktu luput permintaan muat naik (Jam)',\n        unitHours: 'Unit: Jam',\n        alertTitle: 'Tugas Terancang - {0} 「{1}」 Amaran Kegagalan Tugas',\n        library: {\n            script: 'Skrip',\n            syncNow: 'Segerakan Sekarang',\n            turnOnSync: 'Hidupkan Segerakan Auto',\n            turnOnSyncHelper:\n                'Menghidupkan segerakan auto akan melakukan penyegerakan automatik pada waktu awal pagi setiap hari',\n            turnOffSync: 'Matikan Segerakan Auto',\n            turnOffSyncHelper: 'Mematikan segerakan auto mungkin menyebabkan kelewatan penyegerakan skrip, sahkan?',\n            isInteractive: 'Interaktif',\n            interactive: 'Skrip interaktif',\n            interactiveHelper:\n                'Memerlukan input pengguna semasa pelaksanaan dan tidak boleh digunakan dalam tugas terjadual.',\n            library: 'Perpustakaan Skrip',\n            remoteLibrary: 'Pustaka Skrip Jauh',\n            create: 'Tambah Skrip',\n            edit: 'Sunting Skrip',\n            groupHelper:\n                'Tetapkan kumpulan yang berbeza berdasarkan ciri skrip, yang membolehkan operasi penapisan skrip dilakukan dengan lebih pantas.',\n            handleHelper: 'Akan melaksanakan skrip {1} pada {0}, teruskan?',\n            noSuchApp:\n                'Perkhidmatan {0} tidak dikesan. Sila pasang dengan cepat menggunakan pustaka skrip terlebih dahulu!',\n            syncHelper: 'Akan menyelaraskan pustaka skrip sistem. Operasi ini hanya melibatkan skrip sistem. Teruskan?',\n        },\n        nextTime: '5 pelaksanaan seterusnya',\n        logHelper1: '1. Log sistem 1Panel',\n        backupContent: 'Kandungan sandaran',\n        cronSpecRule2: 'Format tempoh pelaksanaan tidak betul, semak dan cuba lagi!',\n        executor: 'Pelaksana',\n        cleanLog: 'Bersih log automatik',\n        cleanLogscope: 'Tempoh simpan log',\n    },\n    monitor: {\n        globalFilter: 'Penapis Global',\n        enableMonitor: 'Status Pemantauan',\n        storeDays: 'Hari Penyimpanan',\n        defaultNetwork: 'Kad Rangkaian Lalai',\n        defaultNetworkHelper:\n            'Pilihan kad rangkaian lalai yang dipaparkan dalam antara muka pemantauan dan gambaran keseluruhan',\n        defaultIO: 'Cakera Lalai',\n        defaultIOHelper: 'Pilihan cakera lalai yang dipaparkan dalam antara muka pemantauan dan gambaran keseluruhan',\n        cleanMonitor: 'Kosongkan Rekod Pemantauan',\n        cleanHelper: 'Operasi ini akan membersihkan semua rekod pemantauan termasuk GPU. Teruskan?',\n        avgLoad: 'Purata beban',\n        loadDetail: 'Butiran beban',\n        resourceUsage: 'Penggunaan sumber',\n        networkCard: 'Antara muka rangkaian',\n        read: 'Baca',\n        write: 'Tulis',\n        readWriteCount: 'Operasi I/O',\n        readWriteTime: 'Kelewatan I/O',\n        today: 'Hari ini',\n        yesterday: 'Semalam',\n        lastNDay: '{0} hari terakhir',\n        lastNMonth: '{0} bulan terakhir',\n        lastHalfYear: 'Setengah tahun terakhir',\n        memory: 'Memori',\n        percent: 'Peratusan',\n        cache: 'Cache',\n        disk: 'Cakera',\n        network: 'Rangkaian',\n        up: 'Naik',\n        down: 'Turun',\n        interval: 'Selang Kumpulan',\n        intervalHelper: 'Sila masukkan selang kumpulan pemantauan yang sesuai (10 saat - 12 jam)',\n    },\n    terminal: {\n        local: 'Tempatan',\n        defaultConn: 'Sambungan Lalai',\n        defaultConnHelper:\n            'Operasi ini akan menyambung secara automatik ke terminal nod selepas membuka terminal untuk 【{0}】. Teruskan?',\n        withReset: 'Tetapkan Semula Maklumat Sambungan',\n        localConnJump:\n            'Maklumat sambungan lalai dikekalkan dalam [Terminal - Tetapan]. Jika sambungan gagal, sila edit di sana!',\n        localHelper: 'Nama tempatan hanya digunakan untuk pengenalan sistem tempatan.',\n        connLocalErr: 'Tidak dapat mengesahkan secara automatik, sila isi maklumat log masuk pelayan tempatan.',\n        testConn: 'Uji sambungan',\n        saveAndConn: 'Simpan dan sambung',\n        connTestOk: 'Maklumat sambungan tersedia',\n        connTestFailed: 'Sambungan tidak tersedia, sila semak maklumat sambungan.',\n        host: 'Hos | Hos-hos',\n        createConn: 'Sambungan baru',\n        manageGroup: 'Urus kumpulan',\n        noHost: 'Tiada hos',\n        groupChange: 'Tukar kumpulan',\n        expand: 'Kembangkan semua',\n        fold: 'Kontrak semua',\n        batchInput: 'Pemprosesan kelompok',\n        quickCommand: 'Arahan pantas | Arahan pantas',\n        noSuchCommand: 'Tiada data arahan pantas ditemui dalam fail CSV yang diimport, sila periksa dan cuba lagi!',\n        quickCommandHelper: 'Anda boleh menggunakan arahan pantas di bahagian bawah \"Terminal -> Terminal\".',\n        groupDeleteHelper:\n            'Selepas kumpulan dikeluarkan, semua sambungan dalam kumpulan akan dipindahkan ke kumpulan lalai. Adakah anda mahu meneruskan?',\n        command: 'Arahan',\n        quickCmd: 'Arahan pantas',\n        addHost: 'Tambah',\n        localhost: 'Localhost',\n        ip: 'Alamat',\n        authMode: 'Pengesahan',\n        passwordMode: 'Kata laluan',\n        rememberPassword: 'Ingat maklumat pengesahan',\n        keyMode: 'Kunci Peribadi',\n        key: 'Kunci peribadi',\n        keyPassword: 'Kata laluan kunci peribadi',\n        emptyTerminal: 'Tiada terminal yang sedang disambungkan.',\n        lineHeight: 'Ketinggian baris',\n        letterSpacing: 'Jarak huruf',\n        fontSize: 'Saiz fon',\n        fontFamily: 'Set fon',\n        fontFamilySupportHelper:\n            'Pilih atau masukkan fon. Sama ada berkesan bergantung pada sama ada fon tersebut dipasang pada sistem pelayar; jika tiada, ia akan fallback secara automatik. Jika kosong, fon lalai akan digunakan.',\n        backgroundColor: 'Warna latar belakang',\n        foregroundColor: 'Warna teks',\n        cursorBlink: 'Kedipan kursor',\n        cursorStyle: 'Gaya kursor',\n        cursorUnderline: 'Garis bawah',\n        cursorBlock: 'Blok',\n        cursorBar: 'Bar',\n        scrollback: 'Skrol balik',\n        scrollSensitivity: 'Kepekaan skrol',\n        aiStatus: 'AI Terminal',\n        aiSettings: 'AI Terminal Settings',\n        aiAccountHelper:\n            'Gunakan akaun model yang dipilih untuk menjana dan mengisi arahan. Untuk model tempatan seperti Ollama dan vLLM, gunakan akaun model tersuai.',\n        aiPrefix: 'Trigger Prefix',\n        aiPrefixHelper:\n            'When a line starts with this prefix and you press Enter, AI command generation will be triggered, for example # or //ai.',\n        aiRiskCommands: 'Risk Command Interception',\n        aiRiskCommandsHelper:\n            'Generated commands matching any of these fragments will be blocked and filled back as comments. Supports add, edit, and delete.',\n        aiAddRiskCommand: 'Add Risk Command',\n        aiRemoveRiskCommand: 'Delete',\n        aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',\n        aiPrefixAsciiVisible:\n            'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',\n        saveHelper: 'Adakah anda pasti mahu menyimpan konfigurasi terminal semasa?',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: 'Untuk beberapa isu pemasangan dan penggunaan, sila rujuk kepada',\n        },\n        swap: {\n            swap: 'Partition Swap',\n            swapHelper1:\n                'Saiz partition swap harus 1 hingga 2 kali ganda memori fizikal, boleh disesuaikan berdasarkan keperluan tertentu;',\n            swapHelper2:\n                'Sebelum mencipta fail swap, pastikan cakera sistem mempunyai ruang yang mencukupi, kerana saiz fail swap akan menggunakan ruang cakera yang bersamaan;',\n            swapHelper3:\n                'Swap boleh membantu mengurangkan tekanan memori, tetapi ia hanya alternatif. Kebergantungan berlebihan pada swap boleh menyebabkan penurunan prestasi sistem. Disarankan untuk mengutamakan peningkatan memori atau pengoptimuman penggunaan memori aplikasi;',\n            swapHelper4:\n                'Disarankan untuk memantau penggunaan swap secara berkala bagi memastikan operasi sistem berjalan dengan normal.',\n            swapDeleteHelper:\n                'Operasi ini akan membuang partition Swap {0}. Atas sebab keselamatan sistem, fail yang sepadan tidak akan dipadamkan secara automatik. Jika pemadaman diperlukan, sila lakukan secara manual!',\n            saveHelper: 'Sila simpan tetapan semasa terlebih dahulu!',\n            saveSwap:\n                'Menyimpan konfigurasi semasa akan menyesuaikan saiz partition Swap {0} kepada {1}. Adakah anda mahu meneruskan?',\n            swapMin: 'Saiz minimum partition adalah 40 KB. Sila ubah dan cuba lagi!',\n            swapMax: 'Nilai maksimum untuk saiz partition adalah {0}. Sila ubah dan cuba lagi!',\n            swapOff: 'Saiz minimum partition adalah 40 KB. Menetapkannya kepada 0 akan mematikan partition Swap.',\n        },\n        device: {\n            dnsHelper: 'Pelayan DNS',\n            dnsAlert:\n                'Perhatian! Mengubah konfigurasi fail /etc/resolv.conf akan mengembalikan fail ke nilai lalai selepas sistem dimulakan semula.',\n            dnsHelper1:\n                'Apabila terdapat pelbagai entri DNS, ia perlu dipaparkan pada baris baru. Contoh:\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: 'Resolusi nama hos',\n            hosts: 'Domain',\n            hostAlert: 'Rekod komen tersembunyi, sila klik butang Semua Konfigurasi untuk melihat atau menetapkan',\n            toolbox: 'Tetapan pantas',\n            hostname: 'Nama hos',\n            passwd: 'Kata laluan sistem',\n            passwdHelper: 'Aksara input tidak boleh termasuk $ dan &',\n            timeZone: 'Zon waktu',\n            localTime: 'Waktu pelayan',\n            timeZoneChangeHelper: 'Mengubah zon waktu sistem memerlukan memulakan semula perkhidmatan. Teruskan?',\n            timeZoneHelper:\n                'Jika anda tidak memasang arahan \"timedatectl\", anda mungkin tidak dapat mengubah zon waktu kerana sistem menggunakan arahan itu untuk mengubah zon waktu.',\n            timeZoneCN: 'Beijing',\n            timeZoneAM: 'Los Angeles',\n            timeZoneNY: 'New York',\n            ntpALi: 'Alibaba',\n            ntpGoogle: 'Google',\n            syncSite: 'Pelayan NTP',\n            hostnameHelper:\n                'Pengubahan nama hos bergantung pada arahan \"hostnamectl\". Jika arahan itu tidak dipasang, pengubahan mungkin gagal.',\n            userHelper:\n                'Nama pengguna bergantung pada arahan \"whoami\" untuk pengambilan. Jika arahan itu tidak dipasang, pengambilan mungkin gagal.',\n            passwordHelper:\n                'Pengubahan kata laluan bergantung pada arahan \"chpasswd\". Jika arahan itu tidak dipasang, pengubahan mungkin gagal.',\n            hostHelper:\n                'Terdapat nilai kosong dalam kandungan yang disediakan. Sila semak dan cuba lagi selepas pengubahsuaian!',\n            dnsCheck: 'Uji Ketersediaan',\n            dnsOK: 'Maklumat konfigurasi DNS tersedia!',\n            dnsTestFailed: 'Maklumat konfigurasi DNS tidak tersedia.',\n            syncSiteHelper: 'Gunakan {0} untuk penyegerakan masa sistem. Teruskan?',\n        },\n        fail2ban: {\n            sshPort: 'Dengar pada port SSH',\n            sshPortHelper: 'Fail2ban semasa mendengar pada port sambungan SSH hos',\n            unActive: 'Perkhidmatan Fail2ban tidak diaktifkan pada masa ini.',\n            operation: 'Anda akan melaksanakan operasi \"{0}\" pada perkhidmatan Fail2ban. Adakah anda mahu meneruskan?',\n            fail2banChange: 'Pengubahan Konfigurasi Fail2ban',\n            ignoreHelper:\n                'Senarai IP dalam senarai dibenarkan akan diabaikan untuk disekat. Adakah anda mahu meneruskan?',\n            bannedHelper: 'Senarai IP dalam senarai sekatan akan disekat oleh pelayan. Adakah anda mahu meneruskan?',\n            maxRetry: 'Percubaan maksimum',\n            banTime: 'Waktu sekatan',\n            banTimeHelper: 'Waktu sekatan lalai adalah 10 minit, -1 menunjukkan sekatan kekal',\n            banTimeRule: 'Sila masukkan waktu sekatan yang sah atau -1',\n            banAllTime: 'Sekatan kekal',\n            findTime: 'Tempoh penemuan',\n            banAction: 'Tindakan sekatan',\n            banActionOption: 'Sekat alamat IP tertentu menggunakan {0}',\n            allPorts: ' (Semua Port)',\n            ignoreIP: 'Senarai IP yang dibenarkan',\n            bannedIP: 'Senarai IP yang disekat',\n            logPath: 'Laluan log',\n            logPathHelper: 'Lalai adalah /var/log/secure atau /var/log/auth.log',\n        },\n        ftp: {\n            ftp: 'Akaun FTP | Akaun FTP',\n            notStart: 'Perkhidmatan FTP tidak berjalan pada masa ini, sila mulakan dahulu!',\n            operation: 'Ini akan melaksanakan operasi \"{0}\" pada perkhidmatan FTP. Adakah anda mahu meneruskan?',\n            noPasswdMsg:\n                'Tidak dapat mendapatkan kata laluan akaun FTP semasa, sila tetapkan kata laluan dan cuba lagi!',\n            enableHelper:\n                'Mengaktifkan akaun FTP yang dipilih akan memulihkan kebenaran aksesnya. Adakah anda mahu meneruskan?',\n            disableHelper:\n                'Melumpuhkan akaun FTP yang dipilih akan membatalkan kebenaran aksesnya. Adakah anda mahu meneruskan?',\n            syncHelper: 'Selaraskan data akaun FTP antara pelayan dan pangkalan data. Adakah anda mahu meneruskan?',\n            dirSystem:\n                'Direktori ini dikhaskan untuk sistem. Pengubahsuaian mungkin menyebabkan sistem rosak, sila ubah dan cuba lagi!',\n            dirHelper: 'Membolehkan FTP memerlukan perubahan kebenaran direktori - sila pilih dengan berhati-hati',\n            dirMsg: 'Membolehkan FTP akan mengubah kebenaran untuk seluruh direktori {0}. Teruskan?',\n        },\n        clam: {\n            clam: 'Imbasan virus',\n            cron: 'Imbasan berjadual',\n            cronHelper: 'Versi profesional menyokong ciri imbasan berjadual',\n            specErr: 'Ralat format jadual pelaksanaan, sila semak dan cuba lagi!',\n            disableMsg:\n                'Menghentikan pelaksanaan berjadual akan menghalang tugas imbasan ini daripada berjalan secara automatik. Adakah anda mahu meneruskan?',\n            enableMsg:\n                'Mengaktifkan pelaksanaan berjadual akan membolehkan tugas imbasan ini berjalan secara automatik pada selang waktu yang tetap. Adakah anda mahu meneruskan?',\n            showFresh: 'Tunjukkan perkhidmatan pengemas kini tanda tangan',\n            hideFresh: 'Sembunyikan perkhidmatan pengemas kini tanda tangan',\n            clamHelper:\n                'Konfigurasi minimum yang disyorkan untuk ClamAV ialah: RAM 3 GiB atau lebih, CPU teras tunggal dengan 2.0 GHz atau lebih tinggi, dan sekurang-kurangnya 5 GiB ruang cakera keras yang tersedia.',\n            notStart: 'Perkhidmatan ClamAV tidak berjalan pada masa ini, sila mulakan dahulu!',\n            removeRecord: 'Padam fail laporan',\n            noRecords: 'Klik butang \"Picu\" untuk memulakan imbasan dan anda akan melihat rekod di sini.',\n            removeInfected: 'Padam fail virus',\n            removeInfectedHelper:\n                'Padam fail virus yang dikesan semasa tugas untuk memastikan keselamatan pelayan dan operasi normal.',\n            clamCreate: 'Cipta peraturan imbasan',\n            infectedStrategy: 'Strategi fail dijangkiti',\n            removeHelper: 'Padam fail virus, pilih dengan berhati-hati!',\n            move: 'Pindah',\n            moveHelper: 'Pindahkan fail virus ke direktori yang ditentukan',\n            copyHelper: 'Salin fail virus ke direktori yang ditentukan',\n            none: 'Jangan buat apa-apa',\n            noneHelper: 'Tidak mengambil tindakan ke atas fail virus',\n            scanDir: 'Imbas direktori',\n            infectedDir: 'Direktori fail dijangkiti',\n            scanDate: 'Tarikh Imbasan',\n            scanResult: 'Hujung log imbasan',\n            tail: 'Baris',\n            infectedFiles: 'Fail dijangkiti',\n            log: 'Butiran',\n            clamConf: 'Daemon Clam AV',\n            clamLog: '@:toolbox.clam.clamConf log',\n            freshClam: 'FreshClam',\n            freshClamLog: '@:toolbox.clam.freshClam log',\n            alertHelper: 'Versi profesional menyokong imbasan berjadual dan amaran SMS',\n            alertTitle: 'Tugas imbasan virus 「{0}」 mengesan amaran fail dijangkiti',\n        },\n    },\n    logs: {\n        core: 'Perkhidmatan Panel',\n        agent: 'Pemantauan Nod',\n        panelLog: 'Log Panel',\n        operation: 'Log Operasi',\n        login: 'Log Masuk',\n        loginIP: 'IP Log Masuk',\n        loginAddress: 'Alamat Log Masuk',\n        loginAgent: 'Ejen Log Masuk',\n        loginStatus: 'Status',\n        system: 'Log Sistem',\n        deleteLogs: 'Bersihkan Log',\n        resource: 'Sumber',\n        detail: {\n            dashboard: 'Overview',\n            ai: 'AI',\n            groups: 'Kumpulan',\n            hosts: 'Hos',\n            apps: 'Aplikasi',\n            openresty: 'OpenResty',\n            websites: 'Laman Web',\n            containers: 'Kontena',\n            files: 'Pengurusan Fail',\n            runtimes: 'Persekitaran Jalankan',\n            process: 'Pengurusan Proses',\n            toolbox: 'Kotak Alat',\n            backups: 'Sandaran / Pulihkan',\n            tampers: 'Perlindungan daripada Pinda',\n            xsetting: 'Tetapan Antara Muka',\n            logs: 'Audit Log',\n            settings: 'Tetapan Panel',\n            cronjobs: 'Tugas Terjadual',\n            waf: 'WAF',\n            databases: 'Pangkalan',\n            licenses: 'lesen',\n            nodes: 'nod',\n            commands: 'Perintah Pantas',\n        },\n        websiteLog: 'Log Laman Web',\n        runLog: 'Log Jalankan',\n        errLog: 'Log Ralat',\n        task: 'Log tugas',\n        taskName: 'Nama tugas',\n        taskRunning: 'Sedang berjalan',\n    },\n    file: {\n        fileDirNum: '{0} direktori, {1} fail,',\n        currentDir: 'Direktori Semasa',\n        dir: 'Folder',\n        upload: 'Muat naik',\n        uploadFile: 'Muat naik fail',\n        uploadDirectory: 'Muat naik direktori',\n        download: 'Muat turun',\n        fileName: 'Nama fail',\n        search: 'Cari',\n        mode: 'Kebenaran',\n        editPermissions: 'Edit kebenaran',\n        owner: 'Pemilik',\n        file: 'Fail',\n        remoteFile: 'Muat turun dari jarak jauh',\n        share: 'Kongsi',\n        sync: 'Penyelarasan Data',\n        size: 'Saiz',\n        updateTime: 'Diubah suai',\n        rename: 'Tukar nama',\n        role: 'Kebenaran',\n        info: 'Atribut',\n        linkFile: 'Pautan lembut',\n        batchoperation: 'Operasi kelompok',\n        shareList: 'Senarai kongsi',\n        zip: 'Dimampatkan',\n        group: 'Kumpulan',\n        path: 'Laluan',\n        public: 'Lain-lain',\n        setRole: 'Tetapkan kebenaran',\n        link: 'Pautan fail',\n        rRole: 'Baca',\n        wRole: 'Tulis',\n        xRole: 'Boleh laksana',\n        name: 'Nama',\n        compress: 'Mampatkan',\n        deCompress: 'Nyahmampatkan',\n        compressType: 'Format mampatan',\n        compressDst: 'Laluan mampatan',\n        replace: 'Timpa fail sedia ada',\n        compressSuccess: 'Berjaya dimampatkan',\n        deCompressSuccess: 'Berjaya dinyahmampatkan',\n        deCompressDst: 'Laluan nyahmampatan',\n        linkType: 'Jenis pautan',\n        softLink: 'Pautan lembut',\n        hardLink: 'Pautan keras',\n        linkPath: 'Laluan pautan',\n        selectFile: 'Pilih fail',\n        downloadUrl: 'URL Jarak Jauh',\n        downloadStart: 'Muat turun bermula',\n        moveSuccess: 'Berjaya dipindahkan',\n        copySuccess: 'Berjaya disalin',\n        pasteMsg: 'Sila klik butang \"Tampal\" di bahagian kanan atas direktori sasaran',\n        move: 'Pindah',\n        calculate: 'Kira',\n        remark: 'Catatan',\n        setRemark: 'Tetapkan catatan',\n        remarkPrompt: 'Masukkan catatan',\n        remarkPlaceholder: 'Catatan',\n        remarkToggle: 'Catatan',\n        remarkToggleTip: 'Muatkan catatan fail',\n        canNotDeCompress: 'Tidak dapat nyahmampatkan fail ini',\n        uploadSuccess: 'Berjaya dimuat naik',\n        downloadProcess: 'Kemajuan muat turun',\n        downloading: 'Sedang muat turun...',\n        infoDetail: 'Sifat fail',\n        root: 'Direktori akar',\n        list: 'Senarai fail',\n        sub: 'Subfolder',\n        downloadSuccess: 'Berjaya dimuat turun',\n        theme: 'Tema',\n        language: 'Bahasa',\n        eol: 'Akhir baris',\n        copyDir: 'Salin',\n        paste: 'Tampal',\n        changeOwner: 'Ubah suai pengguna dan kumpulan pengguna',\n        containSub: 'Guna perubahan kebenaran secara rekursif',\n        ownerHelper:\n            'Pengguna lalai persekitaran operasi PHP: kumpulan pengguna adalah 1000:1000, dan adalah normal jika pengguna di dalam dan di luar kontena menunjukkan ketidakkonsistenan',\n        searchHelper: 'Sokong wildcard seperti *',\n        uploadFailed: '[{0}] Gagal memuat naik fail',\n        fileUploadStart: 'Memuat naik [{0}]...',\n        currentSelect: 'Pilihan semasa: ',\n        unsupportedType: 'Jenis fail tidak disokong',\n        deleteHelper:\n            'Adakah anda pasti mahu memadam fail berikut? Secara lalai, ia akan masuk ke tong kitar semula selepas dipadamkan',\n        fileHelper: 'Nota:\\n1. Keputusan carian tidak boleh diisih.\\n2. Folder tidak boleh diisih mengikut saiz.',\n        forceDeleteHelper: 'Padamkan fail secara kekal (tanpa masuk ke tong kitar semula, padam terus)',\n        recycleBin: 'Tong kitar semula',\n        sourcePath: 'Laluan asal',\n        deleteTime: 'Masa padam',\n        confirmReduce: 'Adakah anda pasti mahu memulihkan fail berikut?',\n        reduceSuccess: 'Berjaya dipulihkan',\n        reduce: 'Pulihkan',\n        reduceHelper:\n            'Jika fail atau direktori dengan nama yang sama wujud di laluan asal, ia akan ditimpa. Adakah anda mahu meneruskan?',\n        clearRecycleBin: 'Bersihkan',\n        clearRecycleBinHelper: 'Adakah anda mahu membersihkan tong kitar semula?',\n        favorite: 'Kegemaran',\n        removeFavorite: 'Buang daripada kegemaran?',\n        addFavorite: 'Tambah/Buang ke Kegemaran',\n        clearList: 'Bersihkan senarai',\n        deleteRecycleHelper: 'Adakah anda pasti mahu memadam fail berikut secara kekal?',\n        typeErrOrEmpty: '[{0}] jenis fail salah atau folder kosong',\n        dropHelper: 'Seret fail yang anda mahu muat naik ke sini',\n        fileRecycleBin: 'Aktifkan tong kitar semula',\n        fileRecycleBinMsg: '{0} tong kitar semula',\n        wordWrap: 'Bungkus secara automatik',\n        deleteHelper2: 'Adakah anda pasti mahu memadam fail yang dipilih? Operasi pemadaman tidak boleh dipulihkan',\n        ignoreCertificate: 'Benarkan sambungan pelayan tidak selamat',\n        ignoreCertificateHelper:\n            'Membenarkan sambungan pelayan tidak selamat boleh menyebabkan kebocoran atau pengubahan data. Gunakan pilihan ini hanya jika mempercayai sumber muat turun.',\n        uploadOverLimit: 'Bilangan fail melebihi 1000! Sila mampatkan dan muat naik',\n        clashDidNotSupport: 'Nama fail dilarang mengandungi .1panel_clash',\n        clashDeleteAlert: 'Folder \"Tong Kitar Semula\" tidak boleh dipadamkan',\n        clashOpenAlert: 'Sila klik butang \"Tong Kitar Semula\" untuk membuka direktori tong kitar semula',\n        right: 'Ke hadapan',\n        back: 'Ke belakang',\n        top: 'Pergi ke atas',\n        up: 'Kembali',\n        openWithVscode: 'Buka dengan VS Code',\n        vscodeHelper: 'Pastikan VS Code dipasang secara tempatan dan pemalam SSH Remote dikonfigurasikan',\n        saveContentAndClose: 'Fail telah diubah suai, adakah anda mahu menyimpan dan menutupnya?',\n        saveAndOpenNewFile: 'Fail telah diubah suai, adakah anda mahu menyimpan dan membuka fail baru?',\n        noEdit: 'Fail tidak diubah suai, tiada keperluan untuk tindakan ini!',\n        noNameFolder: 'Folder tanpa nama',\n        noNameFile: 'Fail tanpa nama',\n        minimap: 'Peta mini kod',\n        fileCanNotRead: 'Fail tidak dapat dibaca',\n        previewTruncated: 'Fail terlalu besar, hanya menunjukkan bahagian terakhir',\n        previewEmpty: 'Fail kosong atau bukan fail teks',\n        previewLargeFile: 'Pratonton',\n        panelInstallDir: 'Direktori pemasangan 1Panel tidak boleh dipadamkan',\n        wgetTask: 'Tugas Muat Turun',\n        existFileTitle: 'Amaran fail dengan nama yang sama',\n        existFileHelper: 'Fail yang dimuat naik mengandungi fail dengan nama yang sama. Adakah anda mahu menimpanya?',\n        existFileSize: 'Saiz fail (baru -> lama)',\n        existFileDirHelper: 'Fail/folder yang dipilih mempunyai nama yang sama. Sila berhati-hati!\\n',\n        coverDirHelper: 'Folder yang dipilih untuk ditimpa akan disalin ke laluan destinasi!',\n        noSuchFile: 'Fail atau direktori tidak ditemui. Sila periksa dan cuba lagi.',\n        setting: 'tetapan',\n        showHide: 'Tunjukkan fail tersembunyi',\n        noShowHide: 'Jangan tunjukkan fail tersembunyi',\n        cancelUpload: 'Batalkan Muat Naik',\n        cancelUploadHelper:\n            'Adakah hendak membatalkan muat naik, selepas pembatalan senarai muat naik akan dikosongkan.',\n        keepOneTab: 'Pastikan sekurang-kurangnya satu tab dikekalkan',\n        notCanTab: 'Tidak dapat menambah tab lagi',\n        convert: 'Tukar Format',\n        converting: 'Menukar Ke',\n        fileCanNotConvert: 'Fail ini tidak menyokong penukaran format',\n        formatType: 'Jenis Format',\n        sourceFormat: 'Format Asal',\n        sourceFile: 'Fail Asal',\n        saveDir: 'Direktori Simpanan',\n        deleteSourceFile: 'Padam Fail Asal',\n        convertHelper: 'Tukar fail yang dipilih ke format lain',\n        convertHelper1: 'Sila pilih fail yang hendak ditukar',\n        execConvert: 'Mulakan penukaran. Anda boleh melihat log penukaran di Pusat Tugas',\n        convertLogs: 'Log Penukaran',\n        formatConvert: 'Penukaran Format',\n    },\n    ssh: {\n        autoStart: 'Mula automatik',\n        enable: 'Aktifkan Mula Automatik',\n        disable: 'Nyahaktif Mula Automatik',\n        sshAlert:\n            'Data senarai disusun berdasarkan tarikh log masuk. Menukar zon waktu atau melakukan operasi lain boleh menyebabkan penyimpangan dalam tarikh log log masuk.',\n        sshAlert2:\n            'Anda boleh menggunakan \"Fail2ban\" di \"Kotak Alat\" untuk menyekat alamat IP yang cuba menyerang secara kasar, dan ini akan meningkatkan keselamatan hos.',\n        sshOperate: 'Operasi \"{0}\" pada perkhidmatan SSH akan dilaksanakan. Adakah anda mahu meneruskan?',\n        sshChange: 'Tetapan SSH',\n        sshChangeHelper: 'Tindakan ini mengubah \"{0}\" kepada \"{1}\". Adakah anda mahu meneruskan?',\n        sshFileChangeHelper:\n            'Mengubah fail konfigurasi boleh menyebabkan ketidaktersediaan perkhidmatan. Lakukan operasi ini dengan berhati-hati. Adakah anda mahu meneruskan?',\n        port: 'Port',\n        portHelper: 'Tentukan port yang didengar oleh perkhidmatan SSH.',\n        listenAddress: 'Alamat dengar',\n        allV4V6: '0.0.0.0:{0}(IPv4) dan :::{0}(IPv6)',\n        listenHelper:\n            'Membiarkan tetapan IPv4 dan IPv6 kosong akan mendengar pada \"0.0.0.0:{0}(IPv4)\" dan \":::{0}(IPv6)\".',\n        addressHelper: 'Tentukan alamat yang didengar oleh perkhidmatan SSH.',\n        permitRootLogin: 'Benarkan log masuk pengguna root',\n        rootSettingHelper: 'Kaedah log masuk lalai untuk pengguna root ialah \"Benarkan log masuk SSH\".',\n        rootHelper1: 'Benarkan log masuk SSH',\n        rootHelper2: 'Lumpuhkan log masuk SSH',\n        rootHelper3: 'Hanya log masuk dengan kunci dibenarkan',\n        rootHelper4: 'Hanya arahan yang telah ditetapkan boleh dilaksanakan. Operasi lain tidak dibenarkan.',\n        passwordAuthentication: 'Pengesahan kata laluan',\n        pwdAuthHelper: 'Sama ada untuk mengaktifkan pengesahan kata laluan. Parameter ini diaktifkan secara lalai.',\n        pubkeyAuthentication: 'Pengesahan kunci',\n        privateKey: 'Kunci Persendirian',\n        publicKey: 'Kunci Awam',\n        password: 'Kata Laluan',\n        createMode: 'Kaedah Penciptaan',\n        generate: 'Jana Automatik',\n        unSyncPass: 'Kata laluan kunci tidak dapat diselaraskan',\n        syncHelper:\n            'Operasi segerak akan membersihkan kunci tidak sah dan menyegerakkan pasangan kunci baru yang lengkap. Teruskan?',\n        input: 'Input Manual',\n        import: 'Muat Naik Fail',\n        authKeys: 'Kunci Pengesahan',\n        authKeysHelper: 'Simpan maklumat kunci awam semasa?',\n        pubkey: 'Maklumat kunci',\n        pubKeyHelper: 'Maklumat kunci semasa hanya berkuat kuasa untuk pengguna {0}',\n        encryptionMode: 'Mod penyulitan',\n        passwordHelper: 'Boleh mengandungi 6 hingga 10 angka dan huruf dalam kedua-dua huruf besar dan kecil',\n        reGenerate: 'Jana semula kunci',\n        keyAuthHelper: 'Sama ada untuk mengaktifkan pengesahan kunci.',\n        useDNS: 'Gunakan DNS',\n        dnsHelper:\n            'Kawal sama ada fungsi resolusi DNS diaktifkan pada pelayan SSH untuk mengesahkan identiti sambungan.',\n        analysis: 'Maklumat statistik',\n        denyHelper:\n            \"Melaksanakan operasi 'tolak' pada alamat berikut. Selepas menetapkan, IP akan dilarang mengakses pelayan. Adakah anda mahu meneruskan?\",\n        acceptHelper:\n            \"Melaksanakan operasi 'terima' pada alamat berikut. Selepas menetapkan, IP akan mendapatkan semula akses normal. Adakah anda mahu meneruskan?\",\n        noAddrWarning: 'Tiada alamat [{0}] yang dipilih pada masa ini. Sila periksa dan cuba lagi!',\n        loginLogs: 'Log SSH',\n        loginMode: 'Mod',\n        authenticating: 'Kunci',\n        publickey: 'Kunci',\n        belong: 'Milikan',\n        local: 'Tempatan',\n        session: 'Sesi | Sesi-sesi',\n        loginTime: 'Waktu log masuk',\n        loginIP: 'IP log masuk',\n        stopSSHWarn: 'Adakah anda mahu memutuskan sambungan SSH ini',\n        remote: 'SSH jauh',\n    },\n    setting: {\n        panel: 'Panel',\n        user: 'Pengguna panel',\n        userChange: 'Tukar pengguna panel',\n        userChangeHelper: 'Menukar pengguna panel akan menyebabkan anda log keluar. Teruskan?',\n        passwd: 'Kata laluan panel',\n        emailHelper: 'Untuk pemulihan kata laluan',\n        watermark: 'Tetapan Tanda Air',\n        watermarkContent: 'Kandungan Tanda Air',\n        contentHelper:\n            '{0} mewakili nama nod, {1} mewakili alamat nod. Anda boleh menggunakan pembolehubah atau mengisi nama tersuai.',\n        watermarkColor: 'Warna Tanda Air',\n        watermarkFont: 'Saiz Fon Tanda Air',\n        watermarkHeight: 'Ketinggian Tanda Air',\n        watermarkWidth: 'Lebar Tanda Air',\n        watermarkRotate: 'Sudut Putaran',\n        watermarkGap: 'Jarak',\n        watermarkCloseHelper: 'Adakah anda pasti ingin mematikan tetapan tanda air sistem?',\n        watermarkOpenHelper: 'Adakah anda pasti ingin menyimpan tetapan tanda air sistem semasa?',\n        title: 'Alias panel',\n        panelPort: 'Port panel',\n        titleHelper:\n            'Menyokong panjang 3 hingga 30 aksara dengan huruf Inggeris, huruf Cina, nombor, ruang kosong dan aksara khas yang biasa',\n        portHelper:\n            'Julat port yang disarankan ialah 8888 hingga 65535. Nota: Jika pelayan mempunyai kumpulan keselamatan, benarkan port baru dari kumpulan keselamatan terlebih dahulu',\n        portChange: 'Tukar port',\n        portChangeHelper: 'Ubah port perkhidmatan dan mulakan semula perkhidmatan. Adakah anda mahu meneruskan?',\n        theme: 'Tema',\n        menuTabs: 'Tab menu',\n        dark: 'Gelap',\n        darkGold: 'Emas Gelap',\n        light: 'Terang',\n        auto: 'Ikut Sistem',\n        language: 'Bahasa',\n        runtimeEnv: 'Persekitaran operasi',\n        docSource: 'Sumber dokumentasi',\n        withByRegion: 'Ikut wilayah operasi (Lalai)',\n        withByLang: 'Ikut bahasa sistem',\n        region: 'Wilayah operasi',\n        cn: 'Tanah Besar China',\n        intl: 'Global',\n        regionHelper: 'Menukar wilayah akan menukar sumber berikut:',\n        regionHelper1: 'Pakej pemasangan sistem',\n        regionHelper2: 'Kedai aplikasi dan pustaka skrip',\n        regionHelper3: 'Manual pengguna dan dokumen berkaitan',\n        regionHelper4:\n            'Tindakan ini mungkin menjejaskan muat turun dan akses seterusnya. Sila teruskan dengan berhati-hati.',\n        regionTip: 'Wilayah operasi mempengaruhi sumber kemas kini sistem serta alamat muat turun pakej pemasangan.',\n        docSourceTip: 'Sumber dokumentasi menentukan bahasa pautan untuk dokumentasi dan log kemas kini.',\n        languageHelper:\n            'Secara lalai, ia mengikuti bahasa penyemak imbas. Parameter ini hanya berkuat kuasa pada penyemak imbas semasa',\n        sessionTimeout: 'Tempoh tamat sesi',\n        sessionTimeoutError: 'Tempoh tamat sesi minimum ialah 300 saat',\n        sessionTimeoutHelper: 'Panel akan log keluar secara automatik jika tiada operasi lebih daripada {0} saat.',\n        systemIP: 'Alamat akses lalai',\n        systemIPHelper:\n            'Penghantaran semula aplikasi, akses kontena dan fungsi lain akan menggunakan alamat ini untuk penghalaan. Setiap nod boleh ditetapkan dengan alamat yang berbeza.',\n        proxy: 'Proksi pelayan',\n        proxyHelper: 'Ia akan berkuat kuasa dalam senario berikut selepas anda menyediakan pelayan proksi:',\n        proxyHelper1: 'Muat turun pakej pemasangan dan penyelarasan dari kedai aplikasi (Edisi Profesional sahaja)',\n        proxyHelper2: 'Kemas kini sistem dan pengambilan maklumat kemas kini (Edisi Profesional sahaja)',\n        proxyHelper4:\n            'Permintaan rangkaian seperti tarikan imej Docker akan melalui pelayan proksi (Edisi Profesional sahaja)',\n        proxyHelper3: 'Pengesahan dan penyelarasan lesen sistem',\n        proxyHelper5: 'Muat turun dan penyegerakan bersepadu untuk pustaka skrip jenis sistem (Ciri Edisi Profesional)',\n        proxyHelper6: 'Mohon sijil (Fungsi versi Pro)',\n        proxyType: 'Jenis proksi',\n        proxyUrl: 'Alamat proksi',\n        proxyPort: 'Port proksi',\n        proxyPasswdKeep: 'Ingat Kata Laluan',\n        proxyDocker: 'Proksi Docker',\n        proxyDockerHelper:\n            'Selaraskan konfigurasi pelayan proksi ke Docker, menyokong tarikan imej pelayan luar talian dan operasi lain',\n        syncToNode: 'Penyegerakan ke nod anak',\n        syncToNodeHelper: 'Penyegerakan tetapan ke nod lain',\n        nodes: 'Nod',\n        selectNode: 'Pilih nod',\n        selectNodeError: 'Sila pilih nod',\n        apiInterface: 'Aktifkan API',\n        apiInterfaceClose: 'Setelah ditutup, antara muka API tidak boleh diakses. Adakah anda mahu meneruskan?',\n        apiInterfaceHelper: 'Benarkan aplikasi pihak ketiga mengakses API.',\n        apiInterfaceAlert1:\n            'Jangan aktifkan dalam persekitaran pengeluaran kerana ia mungkin meningkatkan risiko keselamatan pelayan.',\n        apiInterfaceAlert2:\n            'Jangan gunakan aplikasi pihak ketiga untuk memanggil API bagi mengelakkan potensi ancaman keselamatan.',\n        apiInterfaceAlert3: 'Dokumen API',\n        apiInterfaceAlert4: 'Dokumen Penggunaan',\n        apiKey: 'Kunci API',\n        apiKeyHelper: 'Kunci API digunakan untuk aplikasi pihak ketiga mengakses API.',\n        ipWhiteList: 'Senarai putih IP',\n        ipWhiteListEgs: 'Satu per baris. Contoh,\\n172.161.10.111\\n172.161.10.0/24',\n        ipWhiteListHelper: 'IP dalam senarai putih boleh mengakses API, 0.0.0.0/0 (semua IPv4), ::/0 (semua IPv6)',\n        apiKeyValidityTime: 'Tempoh sah kunci antara muka',\n        apiKeyValidityTimeEgs: 'Tempoh sah kunci antara muka (dalam minit)',\n        apiKeyValidityTimeHelper:\n            'Cap waktu antara muka sah jika perbezaannya dengan cap waktu semasa (dalam minit) berada dalam julat yang dibenarkan. Nilai 0 melumpuhkan pengesahan.',\n        apiKeyReset: 'Tetapkan semula kunci antara muka',\n        apiKeyResetHelper:\n            'Perkhidmatan kunci yang berkaitan akan menjadi tidak sah. Sila tambah kunci baru pada perkhidmatan',\n        confDockerProxy: 'Konfigurasi proksi docker',\n        restartNowHelper: 'Mengkonfigurasi proksi Docker memerlukan memulakan semula perkhidmatan Docker.',\n        restartNow: 'Mulakan semula sekarang',\n        restartLater: 'Mulakan semula secara manual nanti',\n        systemIPWarning:\n            'Nod semasa belum mempunyai alamat akses lalai yang dikonfigurasi. Sila pergi ke tetapan panel untuk mengkonfigurasinya!',\n        systemIPWarning1: 'Alamat pelayan semasa ditetapkan kepada {0}, dan pengalihan cepat tidak mungkin!',\n        defaultNetwork: 'Kad rangkaian',\n        syncTime: 'Waktu Pelayan',\n        timeZone: 'Zon Waktu',\n        timeZoneChangeHelper:\n            'Menukar zon waktu memerlukan memulakan semula perkhidmatan. Adakah anda mahu meneruskan?',\n        timeZoneHelper:\n            'Pengubahan zon waktu bergantung pada perkhidmatan sistem timedatectl. Berkuat kuasa selepas mulakan semula perkhidmatan 1Panel.',\n        timeZoneCN: 'Beijing',\n        timeZoneAM: 'Los Angeles',\n        timeZoneNY: 'New York',\n        ntpALi: 'Alibaba',\n        ntpGoogle: 'Google',\n        syncSite: 'Ntp Server',\n        syncSiteHelper:\n            'Operasi ini akan menggunakan {0} sebagai sumber untuk penyegerakan masa sistem. Adakah anda mahu meneruskan?',\n        changePassword: 'Tukar Kata Laluan',\n        oldPassword: 'Kata laluan asal',\n        newPassword: 'Kata laluan baru',\n        retryPassword: 'Sahkan kata laluan',\n        noSpace: 'Maklumat input tidak boleh mengandungi aksara ruang',\n        duplicatePassword: 'Kata laluan baru tidak boleh sama dengan kata laluan asal, sila masukkan semula!',\n        diskClean: 'Pembersihan Cache',\n        developerMode: 'Program Pratonton',\n        developerModeHelper:\n            'Anda akan dapat mengalami ciri dan pembaikan baru sebelum ia dilancarkan secara meluas dan memberikan maklum balas awal.',\n        thirdParty: 'Akaun pihak ketiga',\n        noTypeForCreate: 'Tiada jenis sandaran yang sedang dibuat',\n        LOCAL: 'Cakera pelayan',\n        OSS: 'Ali OSS',\n        S3: 'Amazon S3',\n        mode: 'Mode',\n        MINIO: 'MinIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'WebDAV sambungkan Alist boleh merujuk kepada dokumentasi rasmi',\n        OneDrive: 'Microsoft OneDrive',\n        isCN: 'Century Internet',\n        isNotCN: 'Versi Antarabangsa',\n        client_id: 'ID Klien',\n        client_secret: 'Rahsia Klien',\n        redirect_uri: 'URL Penghalaan Semula',\n        onedrive_helper: 'Konfigurasi tersuai boleh dirujuk dalam dokumentasi rasmi',\n        refreshTime: 'Waktu Penyegaran Token',\n        refreshStatus: 'Status Penyegaran Token',\n        backupDir: 'Direktori Sandaran',\n        codeWarning: 'Format kod kebenaran semasa tidak betul, sila sahkan semula!',\n        code: 'Kod Auth',\n        codeHelper:\n            'Sila klik butang \"Peroleh\", kemudian log masuk ke OneDrive dan salin kandungan selepas \"code\" dalam pautan yang telah diarahkan semula. Tampalkan kandungan tersebut ke dalam kotak input ini. Untuk arahan spesifik, sila rujuk dokumentasi rasmi.',\n        googleHelper:\n            'Sila buat aplikasi Google dan dapatkan maklumat klien terlebih dahulu, isi borang dan klik butang dapatkan. Untuk operasi khusus, sila rujuk dokumentasi rasmi.',\n        loadCode: 'Peroleh',\n        COS: 'Tencent COS',\n        ap_beijing_1: 'Beijing Zone 1',\n        ap_beijing: 'Beijing',\n        ap_nanjing: 'Nanjing',\n        ap_shanghai: 'Shanghai',\n        ap_guangzhou: 'Guangzhou',\n        ap_chengdu: 'Chengdu',\n        ap_chongqing: 'Chongqing',\n        ap_shenzhen_fsi: 'Shenzhen Financial',\n        ap_shanghai_fsi: 'Shanghai Financial',\n        ap_beijing_fsi: 'Beijing Financial',\n        ap_hongkong: 'Hong Kong, China',\n        ap_singapore: 'Singapore',\n        ap_mumbai: 'Mumbai',\n        ap_jakarta: 'Jakarta',\n        ap_seoul: 'Seoul',\n        ap_bangkok: 'Bangkok',\n        ap_tokyo: 'Tokyo',\n        na_siliconvalley: 'Silicon Valley (US West)',\n        na_ashburn: 'Ashburn (US East)',\n        na_toronto: 'Toronto',\n        sa_saopaulo: 'Sao Paulo',\n        eu_frankfurt: 'Frankfurt',\n        KODO: 'Qiniu Kodo',\n        scType: ' Jenis storan',\n        typeStandard: 'Standard',\n        typeStandard_IA: 'Standard_IA',\n        typeArchive: 'Archive',\n        typeDeep_Archive: 'Deep_Archive',\n        scLighthouse: 'Lalai, Penyimpanan objek ringan hanya menyokong jenis penyimpanan ini',\n        scStandard:\n            'Storan Standard sesuai untuk senario perniagaan dengan sejumlah besar fail panas yang memerlukan akses masa nyata, interaksi data yang kerap, dan sebagainya.',\n        scStandard_IA:\n            'Storan kekerapan rendah sesuai untuk senario perniagaan dengan kekerapan akses yang agak rendah dan menyimpan data sekurang-kurangnya 30 hari.',\n        scArchive: 'Storan arkib sesuai untuk senario perniagaan dengan kekerapan akses yang sangat rendah.',\n        scDeep_Archive:\n            'Storan sejuk tahan lama sesuai untuk senario perniagaan dengan kekerapan akses yang sangat rendah.',\n        archiveHelper:\n            'Fail storan arkib tidak boleh dimuat turun secara langsung dan mesti dipulihkan terlebih dahulu melalui laman web penyedia perkhidmatan awan yang berkaitan. Sila gunakan dengan berhati-hati!',\n        backupAlert:\n            'Jika penyedia awan serasi dengan protokol S3, anda boleh menggunakan Amazon S3 secara langsung untuk sandaran.',\n        domain: 'Domain pemecutan',\n        backupAccount: 'Akaun sandaran | Akaun sandaran',\n        loadBucket: 'Dapatkan baldi',\n        accountName: 'Nama akaun',\n        accountKey: 'Kunci akaun',\n        address: 'Alamat',\n        path: 'Laluan',\n        safe: 'Keselamatan',\n        passkey: 'Passkey',\n        passkeyManage: 'Urus',\n        passkeyKeyManagement: 'Pengurusan kunci',\n        passkeyHelper: 'Untuk log masuk pantas, maksimum 5 passkey boleh dipautkan',\n        passkeyRequireSSL: 'Passkey memerlukan nama domain terikat dan akses HTTPS',\n        passkeyTrustedProxies: 'Proksi dipercayai',\n        passkeyTrustedProxiesHelper:\n            'Hanya permintaan daripada IP/CIDR ini akan mempercayai Forwarded dan X-Forwarded-Proto untuk menentukan HTTPS',\n        passkeyNotSupported: 'Pelayar atau persekitaran semasa tidak menyokong passkey',\n        passkeyPrereqTitle: 'Prasyarat berikut diperlukan untuk Passkey',\n        passkeyPrereqBindDomain: 'Pengikatan domain 1Panel dikonfigurasi',\n        passkeyPrereqHttps: 'Akses selamat melalui HTTPS',\n        passkeyPrereqBrowser: 'Persekitaran menyokong Passkey (WebAuthn + pengesah platform)',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable:\n            'Gunakan pelayar sistem dan pastikan panel dibuka melalui HTTPS dengan domain yang telah diikat.',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            'Persekitaran ini tidak boleh menggunakan pengesahan peranti. Cuba pelayar sistem pada versi OS yang lebih baharu.',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            'Aktifkan kunci skrin / cap jari / pengecaman wajah / Windows Hello, kemudian cuba lagi.',\n        passkeyPrereqBrowserDetailDetectFailed:\n            'Pengesanan gagal. Muat semula dan cuba lagi, atau tukar ke pelayar sistem.',\n        passkeyPrereqGoSetup: 'Konfigurasi',\n        passkeyCount: 'Dipaut {0}/{1}',\n        passkeyName: 'Nama',\n        passkeyNameHelper: 'Masukkan nama untuk membezakan peranti',\n        passkeyAdd: 'Tambah Passkey',\n        passkeyCreatedAt: 'Dicipta',\n        passkeyLastUsedAt: 'Terakhir digunakan',\n        passkeyDeleteConfirm: 'Selepas dipadam, passkey ini tidak boleh digunakan untuk log masuk. Teruskan?',\n        passkeyLimit: 'Maksimum 5 passkey boleh dipautkan',\n        passkeyFailed:\n            'Pendaftaran passkey gagal, sila pastikan sertifikat SSL panel adalah sertifikat yang dipercayai',\n        bindInfo: 'Maklumat ikatan',\n        bindAll: 'Dengar Semua',\n        bindInfoHelper:\n            'Mengubah alamat atau protokol perkhidmatan pendengaran boleh menyebabkan ketidaktersediaan perkhidmatan. Adakah anda mahu meneruskan?',\n        ipv6: 'Dengar IPv6',\n        bindAddress: 'Alamat pendengaran',\n        entrance: 'Pintu masuk',\n        showEntrance: 'Tunjukkan amaran dilumpuhkan di halaman \"Tinjauan\"',\n        entranceHelper:\n            'Mengaktifkan pintu masuk keselamatan hanya akan membolehkan log masuk ke panel melalui pintu masuk keselamatan yang ditentukan.',\n        entranceError:\n            'Sila masukkan titik masuk log masuk yang selamat sepanjang 5-116 aksara, hanya nombor atau huruf yang disokong.',\n        entranceInputHelper: 'Biarkan kosong untuk melumpuhkan pintu masuk keselamatan.',\n        randomGenerate: 'Rawak',\n        expirationTime: 'Tarikh Tamat Tempoh',\n        unSetting: 'Tidak ditetapkan',\n        noneSetting:\n            'Tetapkan masa tamat tempoh untuk kata laluan panel. Selepas tamat tempoh, anda perlu menetapkan semula kata laluan',\n        expirationHelper:\n            'Jika masa tamat tempoh kata laluan ialah [0] hari, fungsi tamat tempoh kata laluan dilumpuhkan',\n        days: 'Hari Tamat Tempoh',\n        expiredHelper: 'Kata laluan semasa telah tamat tempoh. Sila tukar kata laluan lagi.',\n        timeoutHelper:\n            '[ {0} hari ] Kata laluan panel akan tamat tempoh. Selepas tamat tempoh, anda perlu menetapkan semula kata laluan',\n        complexity: 'Pengesahan kerumitan',\n        complexityHelper:\n            'Selepas anda mengaktifkannya, peraturan pengesahan kata laluan akan menjadi: 8-30 aksara, termasuk bahasa Inggeris, nombor, dan sekurang-kurangnya dua aksara khas.',\n        bindDomain: 'Ikatan domain',\n        unBindDomain: 'Buka ikatan domain',\n        panelSSL: 'Panel SSL',\n        unBindDomainHelper:\n            'Tindakan membuka ikatan nama domain boleh menyebabkan ketidakamanan sistem. Adakah anda mahu meneruskan?',\n        bindDomainHelper: 'Selepas anda mengikat domain, hanya domain itu yang boleh mengakses perkhidmatan 1Panel.',\n        bindDomainHelper1: 'Biarkan kosong untuk melumpuhkan ikatan nama domain.',\n        bindDomainWarning:\n            'Selepas ikatan domain, anda akan log keluar dan hanya boleh mengakses perkhidmatan 1Panel melalui nama domain yang ditentukan dalam tetapan. Adakah anda mahu meneruskan?',\n        allowIPs: 'IP Dibenarkan',\n        unAllowIPs: 'IP Tidak Dibenarkan',\n        unAllowIPsWarning:\n            'Membenarkan IP kosong akan membolehkan semua IP mengakses sistem, yang boleh menyebabkan ketidakamanan sistem. Adakah anda mahu meneruskan?',\n        allowIPsHelper:\n            'Selepas anda menetapkan senarai alamat IP yang dibenarkan, hanya alamat IP dalam senarai yang boleh mengakses perkhidmatan panel.',\n        allowIPsWarning:\n            'Selepas anda menetapkan senarai alamat IP yang dibenarkan, hanya alamat IP dalam senarai yang boleh mengakses perkhidmatan panel. Adakah anda mahu meneruskan?',\n        allowIPsHelper1: 'Biarkan kosong untuk melumpuhkan sekatan alamat IP.',\n        allowIPEgs: 'Satu per baris. Contoh,\\n172.16.10.111\\n172.16.10.0/24',\n        mfa: 'Pengesahan Dua Faktor',\n        mfaClose: 'Melumpuhkan MFA akan mengurangkan keselamatan perkhidmatan. Adakah anda mahu meneruskan?',\n        secret: 'Rahsia',\n        mfaInterval: 'Selang penyegaran (saat)',\n        mfaTitleHelper:\n            'Judul digunakan untuk membezakan hos 1Panel yang berbeza. Imbas lagi atau tambahkan kunci rahsia secara manual selepas anda mengubah judul.',\n        mfaIntervalHelper:\n            'Selepas mengubah selang penyegaran, imbas semula atau tambahkan kunci rahsia secara manual.\\nSesetengah aplikasi pengesah hanya menyokong selang 30 saat (contohnya, Google/Microsoft Authenticator).',\n        mfaAlert:\n            'Token satu kali ialah nombor 6 digit yang dijana secara dinamik berdasarkan masa semasa. Pastikan masa pelayan disegerakkan.',\n        mfaHelper: 'Selepas anda mengaktifkannya, token satu kali perlu disahkan.',\n        mfaHelper1: 'Muat turun aplikasi pengesahan, contohnya,',\n        mfaHelper2:\n            'Untuk mendapatkan token satu kali, imbas kod QR berikut menggunakan aplikasi pengesahan anda atau salin kunci rahsia ke dalam aplikasi pengesahan anda.',\n        mfaHelper3: 'Masukkan enam digit dari aplikasi',\n        mfaCode: 'Token satu kali',\n        sslChangeHelper: 'Ubah tetapan https dan mulakan semula perkhidmatan. Adakah anda mahu meneruskan?',\n        sslDisable: 'Lumpuhkan',\n        sslDisableHelper:\n            'Jika perkhidmatan https dilumpuhkan, anda perlu memulakan semula panel untuk ia berkuat kuasa. Adakah anda mahu meneruskan?',\n        noAuthSetting: 'Tetapan tidak dibenarkan',\n        noAuthSettingHelper:\n            'Apabila pengguna tidak log masuk dengan pintu masuk keselamatan yang ditentukan, atau tidak mengakses panel dari IP atau nama domain yang ditentukan, respons ini boleh menyembunyikan ciri panel.',\n        responseSetting: 'Tetapan respons',\n        help200: 'Halaman Bantuan',\n        error400: 'Permintaan Buruk',\n        error401: 'Tidak Dibenarkan',\n        error403: 'Dilarang',\n        error404: 'Tidak Dijumpai',\n        error408: 'Permintaan Tamat Masa',\n        error416: 'Julat Tidak Memuaskan',\n        error444: 'Sambungan ditutup',\n        error500: 'Ralat Pelayan',\n        https: 'Mengatur HTTPS untuk panel meningkatkan keselamatan akses',\n        strictHelper: 'Trafik bukan HTTPS tidak boleh menyambung ke panel',\n        muxHelper:\n            'Panel akan mendengar pada kedua-dua protokol HTTP dan HTTPS dan mengarahkan HTTP ke HTTPS, tetapi ini mungkin mengurangkan keselamatan panel',\n        certType: 'Jenis sijil',\n        selfSigned: 'Diterbitkan sendiri',\n        selfSignedHelper:\n            'Pelayar mungkin tidak mempercayai sijil diterbitkan sendiri dan mungkin memaparkan amaran keselamatan.',\n        select: 'Pilih',\n        domainOrIP: 'Domain atau IP:',\n        timeOut: 'Tamat Masa',\n        rootCrtDownload: 'Muat turun sijil akar',\n        primaryKey: 'Kunci utama',\n        certificate: 'Sijil',\n        backupJump:\n            'Fail sandaran tidak berada dalam senarai sandaran semasa, sila cuba muat turun dari direktori fail dan import untuk sandaran.',\n        snapshot: 'Snapshot | Snapshots',\n        noAppData: 'Tiada aplikasi sistem yang boleh dipilih',\n        noBackupData: 'Tiada data sandaran yang boleh dipilih',\n        stepBaseData: 'Data Asas',\n        stepAppData: 'Aplikasi Sistem',\n        stepPanelData: 'Data Sistem',\n        stepBackupData: 'Data Sandaran',\n        stepOtherData: 'Data Lain',\n        operationLog: 'Simpan log operasi',\n        loginLog: 'Simpan log akses',\n        systemLog: 'Simpan log sistem',\n        taskLog: 'Simpan log tugas',\n        monitorData: 'Simpan data pemantauan',\n        dockerConf: 'Simpan Konfigurasi Docker',\n        selectAllImage: 'Simpan semua imej aplikasi',\n        logLabel: 'Log',\n        agentLabel: 'Konfigurasi Nod',\n        appDataLabel: 'Data Aplikasi',\n        appImage: 'Imej Aplikasi',\n        appBackup: 'Sandaran Aplikasi',\n        backupLabel: 'Direktori Sandaran',\n        confLabel: 'Fail Konfigurasi',\n        dockerLabel: 'Konteks',\n        taskLabel: 'Tugas Berjadual',\n        resourceLabel: 'Direktori Sumber Aplikasi',\n        runtimeLabel: 'Persekitaran Runtime',\n        appLabel: 'Aplikasi',\n        databaseLabel: 'Pangkalan Data',\n        snapshotLabel: 'Fail Snapshot',\n        websiteLabel: 'Laman Web',\n        directoryLabel: 'Direktori',\n        appStoreLabel: 'Kedai Aplikasi',\n        shellLabel: 'Skrip',\n        tmpLabel: 'Direktori Sementara',\n        sslLabel: 'Direktori Sijil',\n        reCreate: 'Gagal membuat snapshot',\n        reRollback: 'Gagal membalikkan snapshot',\n        deleteHelper:\n            'Semua fail snapshot termasuk yang ada dalam akaun sandaran pihak ketiga akan dipadamkan. Adakah anda mahu meneruskan?',\n        status: 'Status snapshot',\n        ignoreRule: 'Aturan abaikan',\n        editIgnoreRule: 'Sunting aturan abaikan',\n        ignoreHelper:\n            'Aturan ini akan digunakan untuk memampatkan dan menyandarkan direktori data 1Panel semasa membuat snapshot. Secara lalai, fail soket diabaikan.',\n        ignoreHelper1: 'Satu per baris. Contoh,\\n*.log\\n/opt/1panel/cache',\n        panelInfo: 'Tulis maklumat asas 1Panel',\n        panelBin: 'Sandarkan fail sistem 1Panel',\n        daemonJson: 'Sandarkan fail konfigurasi Docker',\n        appData: 'Sandarkan aplikasi yang dipasang dari 1Panel',\n        panelData: 'Sandarkan direktori data 1Panel',\n        backupData: 'Sandarkan direktori sandaran tempatan untuk 1Panel',\n        compress: 'Cipta fail snapshot',\n        upload: 'Muat naik fail snapshot',\n        recoverDetail: 'Butiran pemulihan',\n        recover: 'Pulihkan',\n        lastRecoverAt: 'Waktu pemulihan terakhir',\n        lastRollbackAt: 'Waktu pemulangan terakhir',\n        reDownload: 'Muat turun semula fail sandaran',\n        recoverErrArch: 'Pemulihan snapshot antara seni bina pelayan yang berbeza tidak disokong!',\n        recoverErrSize: 'Kekurangan ruang cakera dikesan, sila periksa atau bersihkan dan cuba lagi!',\n        recoverHelper: 'Memulakan pemulihan dari snapshot {0}, sila sahkan maklumat berikut sebelum meneruskan:',\n        recoverHelper1: 'Pemulihan memerlukan mulakan semula perkhidmatan Docker dan 1Panel',\n        recoverHelper2:\n            'Pastikan terdapat ruang cakera yang mencukupi pada pelayan (Saiz fail snapshot: {0}, Ruang tersedia: {1})',\n        recoverHelper3:\n            'Pastikan seni bina pelayan sepadan dengan seni bina pelayan di mana snapshot dicipta (Seni bina pelayan semasa: {0})',\n        rollback: 'Pulangkan semula',\n        rollbackHelper:\n            'Pulangkan semula pemulihan ini akan menggantikan semua fail dari pemulihan ini, dan mungkin memerlukan mulakan semula perkhidmatan Docker dan 1Panel. Adakah anda mahu meneruskan?',\n        upgradeHelper: 'Kemas kini memerlukan mulakan semula perkhidmatan 1Panel. Adakah anda mahu meneruskan?',\n        rollbackLocalHelper:\n            'Nod utama tidak menyokong rollback secara langsung. Sila laksanakan arahan [1pctl restore] secara manual untuk rollback!',\n        noUpgrade: 'Ia adalah versi terbaru pada masa ini',\n        upgradeNotes: 'Nota pelepasan',\n        upgradeNow: 'Kemas kini sekarang',\n        source: 'Sumber muat turun',\n        versionNotSame:\n            'Versi nod tidak sepadan dengan nod utama. Sila naik taraf di Pengurusan Nod sebelum mencuba semula.',\n        versionCompare:\n            'Nod {0} telah berada pada versi terkini yang boleh dinaik taraf. Sila periksa versi nod utama dan cuba lagi!',\n        about: 'Mengenai',\n        versionItem: 'Versi Semasa',\n        backupCopies: 'Salinan Sandaran',\n        backupCopiesHelper:\n            'Tetapkan bilangan salinan sandaran naik taraf untuk disimpan untuk pemulihan versi. 0 bermakna simpan semua.',\n        backupCopiesRule: 'Sila simpan sekurang-kurangnya 3 rekod sandaran naik taraf',\n        release: 'Log Kemaskini Versi',\n        releaseHelper:\n            'Pengambilan log kemaskini untuk persekitaran semasa mengalami异常. Anda boleh menyemak dokumentasi rasmi secara manual.',\n        project: 'GitHub',\n        issue: 'Isu',\n        doc: 'Dokumen rasmi',\n        star: 'Bintang',\n        description: 'Panel Pelayan Linux',\n        forum: 'Perbincangan',\n        doc2: 'Dokumen',\n        currentVersion: 'Versi',\n        license: 'Lesen',\n        bindNode: 'Ikatan Nod',\n        menuSetting: 'Tetapan Menu',\n        menuSettingHelper: 'Apabila hanya terdapat 1 submenu, bar menu hanya akan memaparkan submenu tersebut',\n        showAll: 'Papar Semua',\n        hideALL: 'Sembunyikan Semua',\n        ifShow: 'Sama ada untuk Dipaparkan',\n        menu: 'Menu',\n        confirmMessage: 'Halaman akan disegarkan untuk mengemas kini senarai menu lanjutan. Teruskan?',\n        recoverMessage: 'Halaman akan disegarkan dan senarai menu akan dipulihkan ke keadaan asal. Teruskan?',\n        compressPassword: 'Kata laluan mampatan',\n        backupRecoverMessage:\n            'Sila masukkan kata laluan mampatan atau nyahmampatan (biarkan kosong jika tidak menetapkan)',\n        componentSize: 'Saiz komponen',\n        scope: 'Skop',\n        public: 'Awam',\n        publicHelper: 'Akaun sandaran jenis awam akan disegerakkan ke setiap subnod dan boleh digunakan bersama',\n        private: 'Peribadi',\n        privateHelper: 'Akaun sandaran jenis peribadi hanya dicipta pada nod semasa dan hanya untuk nod semasa',\n        UPYUN: 'UPYUN',\n        ALIYUN: 'Aliyun Drive',\n        ALIYUNHelper: 'Had semasa muat turun tanpa klien Aliyun Drive ialah 100 MB. Melebihi had perlu guna klien.',\n        ALIYUNRecover:\n            'Had semasa muat turun tanpa klien Aliyun Drive ialah 100 MB. Jika melebihi, muat turun ke tempatan melalui klien dahulu, kemudian segerakkan syot kilat untuk pemulihan.',\n        GoogleDrive: 'Google Drive',\n        analysis: 'Analisis',\n        analysisHelper:\n            'Tampal keseluruhan kandungan token untuk parse automatik bahagian diperlukan. Rujuk dokumentasi rasmi untuk butiran.',\n        serviceName: 'Nama servis',\n        operator: 'Operator',\n        clickToRefresh: 'Klik untuk segar semula',\n        recoverFailed: 'Pemulihan syot kilat gagal',\n        importHelper: 'Direktori syot kilat: ',\n        upgradeRecord: 'Rekod naik taraf',\n        upgrading: 'Sedang naik taraf, sila tunggu...',\n        versionHelper: 'Peraturan nama: [versi utama].[versi fungsi].[versi pembaikan], seperti contoh berikut:',\n        upgradeCheck: 'Semak kemas kini',\n        panelSSLHelper:\n            'Selepas pembaharuan automatik SSL panel, anda perlu mulakan semula servis 1Panel secara manual.',\n    },\n    license: {\n        offLine: 'Versi Luar Talian',\n        community: 'OSS',\n        oss: 'Perisian Sumber Terbuka',\n        pro: 'Pro',\n        trial: 'Percubaan',\n        add: 'Tambah Edisi Komuniti',\n        licenseBindHelper: 'Kuota nod percuma hanya boleh digunakan apabila lesen terikat pada nod',\n        licenseAlert:\n            'Nod Edisi Komuniti hanya boleh ditambah apabila lesen terikat dengan betul pada nod. Hanya nod yang terikat dengan betul pada lesen menyokong penukaran.',\n        licenseUnbindHelper: 'Nod Edisi Komuniti dikesan untuk lesen ini. Sila lepaskan ikatan dan cuba lagi!',\n        subscription: 'Langganan',\n        perpetual: 'Lesen Perpetual',\n        versionConstraint: '{0} Pembelian versi',\n        forceUnbind: 'Paksakan Nyahikat',\n        forceUnbindHelper:\n            'Memaksa nyahikat akan mengabaikan sebarang ralat yang berlaku semasa proses nyahikat dan akhirnya melepaskan ikatan lesen.',\n        updateForce: 'Kemas kini paksa (abaikan semua ralat semasa nyahikatan untuk memastikan operasi akhir berjaya)',\n        trialInfo: 'Versi',\n        authorizationId: 'ID Langganan',\n        authorizedUser: 'Pengguna yang Dibenarkan',\n        lostHelper:\n            'Lesen telah mencapai jumlah percubaan semula maksimum. Sila klik butang penyegerakan secara manual untuk memastikan ciri versi profesional berfungsi dengan baik. butiran: ',\n        disableHelper:\n            'Pengesahan penyegerakan lesen gagal. Sila klik butang penyegerakan secara manual untuk memastikan ciri versi profesional berfungsi dengan baik. butiran: ',\n        quickUpdate: 'Kemas Kini Pantas',\n        power: 'Kebenaran',\n        unbindHelper: 'Semua Tetapan berkaitan Pro akan dibersihkan selepas nyahikat. Adakah anda mahu meneruskan?',\n        importLicense: 'Lesen',\n        importHelper: 'Sila klik atau seret fail lesen ke sini',\n        technicalAdvice: 'Konsultasi Teknikal',\n        advice: 'Konsultasi',\n        levelUpPro: 'Tingkatkan ke Pro',\n        licenseSync: 'Penyegerakan Lesen',\n        knowMorePro: 'Ketahui Lebih Lanjut',\n        closeAlert: 'Halaman semasa boleh ditutup dalam tetapan panel',\n        introduce: 'Pengenalan Ciri',\n        waf: 'Menaik taraf ke versi profesional boleh menyediakan ciri seperti peta pencegahan, log, rekod blok, sekatan lokasi geografi, peraturan tersuai, halaman pencegahan tersuai, dan sebagainya.',\n        tamper: 'Menaik taraf ke versi profesional boleh melindungi laman web daripada pengubahsuaian atau manipulasi tanpa kebenaran.',\n        setting:\n            'Menaik taraf ke versi profesional membolehkan penyesuaian logo panel, mesej selamat datang, dan maklumat lain.',\n        monitor:\n            'Tingkatkan ke versi profesional untuk melihat status masa nyata laman web, tren pelawat, sumber pelawat, log permintaan dan maklumat lain.',\n        alert: 'Tingkatkan ke versi profesional untuk menerima maklumat amaran melalui SMS dan melihat log amaran, mengawal sepenuhnya pelbagai acara utama, dan memastikan operasi sistem bebas kerisauan.',\n        node: 'Menaik taraf ke Edisi Profesional membolehkan anda menguruskan berbilang pelayan Linux dengan 1Panel.',\n        nodeApp:\n            'Menaik taraf ke Edisi Profesional membolehkan peningkatan bersatu versi aplikasi berbilang nod tanpa menukar nod secara manual.',\n        nodeDashboard:\n            'Menaik taraf ke Edisi Profesional membolehkan pengurusan berpusat aplikasi, laman web, pangkalan data dan tugas berjadual berbilang nod.',\n        fileExchange: 'Naik taraf ke Edisi Professional untuk menghantar fail dengan cepat antara pelbagai pelayan.',\n        app: 'Tingkatkan ke versi profesional untuk melihat maklumat perkhidmatan, pemantauan abnormal, dll melalui aplikasi mudah alih.',\n        cluster:\n            'Versi Profesional menaik taraf membolehkan anda menguruskan kelompok induk-hamba MySQL/Postgres/Redis.',\n        exceptionalHelper:\n            'Pengesahan penyegerakan lesen tidak normal. Klik butang sync secara manual untuk pastikan fungsi Pro berjalan baik. butiran: ',\n        tamperHelper: 'Operasi gagal, fail atau folder mempunyai perlindungan gangguan. Sila semak dan cuba lagi!',\n    },\n    clean: {\n        scan: 'Mulakan imbasan',\n        scanHelper: 'Bersihkan fail sampah dengan mudah yang dihasilkan semasa operasi 1Panel',\n        clean: 'Bersihkan sekarang',\n        reScan: 'Imbas semula',\n        cleanHelper: 'Fail dan direktori terpilih tidak boleh dipulihkan selepas pembersihan. Teruskan?',\n        statusSuggest: '(Disyorkan untuk Pembersihan)',\n        statusClean: '(Sangat bersih)',\n        statusEmpty: 'Sangat bersih, tiada pembersihan diperlukan!',\n        statusWarning: '(Berhati-hati)',\n        lastCleanTime: 'Dibersihkan Terakhir: {0}',\n        lastCleanHelper: 'Fail dan direktori yang dibersihkan: {0}, jumlah yang dibersihkan: {1}',\n        cleanSuccessful: 'Berjaya dibersihkan',\n        currentCleanHelper: 'Fail dan direktori dibersihkan dalam sesi ini: {0}, Jumlah yang dibersihkan: {1}',\n        suggest: '(Disyorkan)',\n        totalScan: 'Jumlah fail sampah untuk dibersihkan: ',\n        selectScan: 'Jumlah fail sampah yang dipilih: ',\n        system: 'Fail Sampah Sistem',\n        systemHelper: 'Fail sementara dijana semasa snapshot, naik taraf, dan proses lain',\n        panelOriginal: 'Fail sandaran sebelum pemulihan snapshot sistem',\n        upgrade: 'Fail sandaran naik taraf sistem',\n        agentPackages: 'Pakej naik taraf/pemasangan nod anak versi sejarah',\n        upgradeHelper: '(Disarankan untuk mengekalkan sandaran peningkatan terbaru untuk pemulihan sistem)',\n        rollback: 'Fail sandaran sebelum pemulihan',\n        backup: 'Sandaran Sistem',\n        backupHelper: 'Fail sandaran tidak dikaitkan dengan akaun sandaran tempatan',\n        unknownBackup: 'Sandaran Sistem',\n        tmpBackup: 'Sandaran Sementara',\n        unknownApp: 'Sandaran Apl Tidak Dikaitkan',\n        unknownDatabase: 'Sandaran Pangkalan Data Tidak Dikaitkan',\n        unknownWebsite: 'Sandaran Laman Web Tidak Dikaitkan',\n        unknownSnapshot: 'Sandaran Snapshot Tidak Dikaitkan',\n        upload: 'Fail Muat Naik Sementara',\n        uploadHelper: 'Fail sementara dimuat naik dari senarai sandaran sistem',\n        download: 'Fail Muat Turun Sementara',\n        downloadHelper: 'Fail sementara dimuat turun dari akaun sandaran pihak ketiga oleh sistem',\n        directory: 'Direktori',\n        systemLog: 'Fail log',\n        systemLogHelper: 'Log sistem, log tugas, fail log laman web',\n        dockerLog: 'Fail log operasi bekas',\n        taskLog: 'Fail log pelaksanaan tugas berjadual',\n        shell: 'Tugas berjadual skrip Shell',\n        containerShell: 'Tugas berjadual skrip Shell dilaksanakan di dalam bekas',\n        curl: 'Tugas berjadual CURL',\n        docker: 'Sampah bekas',\n        dockerHelper: 'Fail seperti bekas, imej, isipadu, cache binaan, dsb.',\n        volumes: 'Isipadu',\n        buildCache: 'Cache Pembinaan Kontena',\n        appTmpDownload: 'Fail muat turun sementara aplikasi',\n        unknownWebsiteLog: 'Fail sandaran log laman web tidak dikaitkan',\n        snapshot: 'Fail sementara syot kilat sistem',\n    },\n    app: {\n        app: 'Aplikasi | Aplikasi',\n        installName: 'Nama',\n        installed: 'Telah Dipasang',\n        all: 'Semua',\n        version: 'Versi',\n        detail: 'Butiran',\n        params: 'Edit',\n        author: 'Pengarang',\n        source: 'Sumber',\n        appName: 'Nama Aplikasi',\n        deleteWarn:\n            'Operasi memadam akan memadam semua data dan sandaran bersama. Operasi ini tidak boleh dipulihkan. Adakah anda mahu meneruskan?',\n        syncSuccess: 'Disegerakkan dengan berjaya',\n        canUpgrade: 'Kemaskini',\n        backupName: 'Nama Fail',\n        backupPath: 'Laluan Fail',\n        backupdate: 'Masa Sandaran',\n        versionSelect: 'Sila pilih versi',\n        operatorHelper: 'Operasi {0} akan dilaksanakan pada aplikasi yang dipilih. Adakah anda mahu meneruskan?',\n        startOperatorHelper: 'Aplikasi akan dimulakan. Adakah anda mahu meneruskan?',\n        stopOperatorHelper: 'Aplikasi akan dihentikan. Adakah anda mahu meneruskan?',\n        restartOperatorHelper: 'Aplikasi akan dimulakan semula. Adakah anda mahu meneruskan?',\n        reloadOperatorHelper: 'Aplikasi akan dimuat semula. Adakah anda mahu meneruskan?',\n        checkInstalledWarn: '\"{0}\" tidak dikesan. Pergi ke \"Kedai Aplikasi\" untuk memasang.',\n        limitHelper: 'Aplikasi ini telah dipasang.',\n        deleteHelper: '\"{0}\" telah dikaitkan dengan sumber berikut. Sila semak dan cuba lagi!',\n        checkTitle: 'Petunjuk',\n        defaultConfig: 'Konfigurasi lalai',\n        defaultConfigHelper: 'Telah dipulihkan ke konfigurasi lalai, akan berkuat kuasa selepas menyimpan',\n        forceDelete: 'Padam Paksa',\n        forceDeleteHelper: 'Padam paksa akan mengabaikan ralat semasa proses pemadaman dan akhirnya memadam metadata.',\n        deleteBackup: 'Padam sandaran',\n        deleteBackupHelper: 'Juga padam sandaran aplikasi',\n        deleteDB: 'Padam pangkalan data',\n        deleteDBHelper: 'Juga padam pangkalan data',\n        noService: 'Tiada {0}',\n        toInstall: 'Pergi ke pasang',\n        param: 'Parameter',\n        alreadyRun: 'Umur',\n        syncAppList: 'Segerak',\n        less1Minute: 'Kurang daripada 1 minit',\n        appOfficeWebsite: 'Laman web rasmi',\n        github: 'Github',\n        document: 'Dokumen',\n        updatePrompt: 'Tiada kemaskini tersedia',\n        installPrompt: 'Belum ada aplikasi yang dipasang',\n        updateHelper: 'Mengedit parameter boleh menyebabkan aplikasi gagal dimulakan. Sila berhati-hati.',\n        updateWarn: 'Kemaskini parameter memerlukan aplikasi dibina semula. Adakah anda mahu meneruskan?',\n        busPort: 'Port',\n        syncStart: 'Mulakan penyegerakan! Sila segar semula kedai aplikasi kemudian',\n        advanced: 'Tetapan lanjutan',\n        cpuCore: 'teras',\n        containerName: 'Nama kontena',\n        containerNameHelper: 'Nama kontena akan dijana secara automatik jika tidak ditetapkan',\n        allowPort: 'Akses luaran',\n        allowPortHelper: 'Membenarkan akses port luaran akan membuka port firewall',\n        appInstallWarn:\n            'Aplikasi tidak membuka port akses luaran secara lalai. Klik \"Tetapan lanjutan\" untuk membukanya.',\n        upgradeStart: 'Mulakan peningkatan! Sila segar semula halaman kemudian',\n        toFolder: 'Buka direktori pemasangan',\n        editCompose: 'Edit fail compose',\n        editComposeHelper: 'Mengedit fail compose boleh menyebabkan pemasangan perisian gagal',\n        composeNullErr: 'Compose tidak boleh kosong',\n        takeDown: 'Henti Operasi',\n        allReadyInstalled: 'Telah Dipasang',\n        installHelper: 'Jika terdapat isu tarikan imej, konfigurasikan pecutan imej.',\n        sortMode: 'Susun',\n        installWarn:\n            'Akses luaran tidak diaktifkan, yang menghalang aplikasi daripada diakses melalui rangkaian luaran. Adakah anda mahu meneruskan?',\n        showIgnore: 'Lihat aplikasi yang diabaikan',\n        cancelIgnore: 'Batal abaikan',\n        ignoreList: 'Aplikasi yang diabaikan',\n        appHelper: 'Pergi ke halaman butiran aplikasi untuk mengetahui arahan pemasangan bagi aplikasi tertentu.',\n        backupApp: 'Sandarkan aplikasi sebelum kemaskini',\n        backupAppHelper: 'Jika kemaskini gagal, sandaran akan dipulihkan secara automatik.',\n        openrestyDeleteHelper: 'Padam paksa OpenResty akan memadam semua laman web. Adakah anda mahu meneruskan?',\n        downloadLogHelper1: 'Semua log aplikasi {0} akan dimuat turun. Adakah anda mahu meneruskan?',\n        downloadLogHelper2: 'Log terkini {1} aplikasi {0} akan dimuat turun. Adakah anda mahu meneruskan?',\n        syncAllAppHelper: 'Semua aplikasi akan disegerakkan. Adakah anda mahu meneruskan?',\n        hostModeHelper: 'Mod rangkaian aplikasi semasa ialah mod hos.',\n        showLocal: 'Papar aplikasi tempatan',\n        reload: 'Muat Semula',\n        upgradeWarn: 'Meningkatkan aplikasi akan menggantikan fail docker-compose.yml.',\n        openclawHttpsUpgradeNoticeTitle:\n            'Nota: Arahan berikut hanya terpakai kepada pengguna yang menaik taraf OpenClaw daripada versi lebih awal daripada 2026.3.13:',\n        openclawHttpsUpgradeNoticeItem1:\n            'Selepas ejen yang telah dipasang selesai dinaik taraf, pergi ke Konfigurasi -> Tetapan -> Keselamatan dan tambah alamat akses secara manual.',\n        openclawHttpsUpgradeNoticeItem2:\n            'Versi baharu kini mewajibkan HTTPS untuk mengakses ejen. Jika anda sebelum ini menggunakan laman reverse proxy, tukar alamat sasaran proksi kepada https://IP:Port.',\n        newVersion: 'Versi baru',\n        oldVersion: 'Versi semasa',\n        composeDiff: 'Perbandingan fail',\n        showDiff: 'Lihat perbandingan',\n        useNew: 'Guna versi tersuai',\n        useDefault: 'Guna versi lalai',\n        useCustom: 'Sesuaikan docker-compose.yml',\n        useCustomHelper: 'Menggunakan fail docker-compose.yml tersuai boleh menyebabkan kemaskini gagal.',\n        diffHelper: 'Bahagian kiri ialah versi lama, kanan ialah versi baru.',\n        pullImage: 'Tarik Imej',\n        pullImageHelper: 'Laksanakan docker pull untuk menarik imej sebelum aplikasi dimulakan.',\n        deleteImage: 'Padam Imej',\n        deleteImageHelper: 'Padam imej yang berkaitan dengan aplikasi. Tugas tidak akan tamat jika pemadaman gagal.',\n        requireMemory: 'Keperluan Memori',\n        supportedArchitectures: 'Seni Bina yang Disokong',\n        link: 'Pautan',\n        showCurrentArch: 'Aplikasi seni bina pelayan semasa',\n        syncLocalApp: 'Segerakkan Aplikasi Tempatan',\n        memoryRequiredHelper: 'Aplikasi semasa memerlukan {0} memori',\n        gpuConfig: 'Aktifkan Sokongan GPU',\n        gpuConfigHelper:\n            'Pastikan mesin mempunyai NVIDIA GPU dan memasang pemacu NVIDIA serta NVIDIA Docker Container Toolkit',\n        webUI: 'Alamat Akses Web',\n        webUIPlaceholder: 'Contoh: example.com:8080/login',\n        defaultWebDomain: 'Alamat Akses Lalai',\n        defaultWebDomainHepler:\n            'Jika port aplikasi adalah 8080, alamat loncatan akan menjadi http(s)://alamat akses lalai:8080',\n        webUIConfig:\n            'Nod semasa belum mempunyai alamat akses lalai yang dikonfigurasi. Sila tetapkan dalam parameter aplikasi atau pergi ke tetapan panel untuk mengkonfigurasinya!',\n        toLink: 'Loncat',\n        customAppHelper:\n            'Sebelum memasang pakej kedai aplikasi tersuai, sila pastikan tidak ada aplikasi yang dipasang.',\n        forceUninstall: 'Paksa Nyahpasang',\n        syncCustomApp: 'Segerakan Aplikasi Tersuai',\n        ignoreAll: 'Abaikan semua versi berikutnya',\n        ignoreVersion: 'Abaikan versi yang ditentukan',\n        specifyIP: 'Bind IP Hos',\n        specifyIPHelper:\n            'Tetapkan alamat hos/antara muka rangkaian untuk mengikat port (jika anda tidak pasti mengenai ini, jangan isi)',\n        uninstallDeleteBackup: 'Cop Terhapus Semasa Nyahpasang Aplikasi',\n        uninstallDeleteImage: 'Imej Terhapus Semasa Nyahpasang Aplikasi',\n        upgradeBackup: 'Sandaran Aplikasi Sebelum Naik Taraf',\n        noAppHelper: 'Tiada aplikasi dikesan, sila pergi ke pusat tugas untuk melihat log penyegerakan kedai aplikasi',\n        isEdirWarn: 'Mengesan pengubahsuaian pada fail docker-compose.yml, sila semak perbandingan',\n    },\n    website: {\n        primaryDomain: 'Domain Utama',\n        otherDomains: 'Domain Lain',\n        static: 'Statik',\n        deployment: 'Penerapan',\n        supportUpType: 'Hanya format fail .tar.gz yang disokong, dan pakej termampat mesti mengandungi fail {0}.json',\n        proxy: 'Proksi Terbalik',\n        alias: 'Alias',\n        ftpUser: 'Akaun FTP',\n        ftpPassword: 'Kata Laluan FTP',\n        ftpHelper:\n            'Selepas membuat laman web, akaun FTP akan dibuat dan direktori FTP akan memautkan ke direktori laman web.',\n        remark: 'Catatan',\n        manageGroup: 'Urus kumpulan',\n        groupSetting: 'Pengurusan Kumpulan',\n        createGroup: 'Cipta kumpulan',\n        appNew: 'Aplikasi Baru',\n        appInstalled: 'Aplikasi yang Dipasang',\n        delete: 'Padam Laman Web',\n        deleteApp: 'Padam Aplikasi',\n        deleteBackup: 'Padam Sandaran',\n        domain: 'Domain',\n        domainHelper: 'Satu domain per baris.\\nSokong wildcard \"*\" dan alamat IP.\\nSokong penambahan port.',\n        addDomain: 'Tambah',\n        domainConfig: 'Domain',\n        defaultDoc: 'Dokumen',\n        perserver: 'Serentak',\n        perserverHelper: 'Hadkan serentak maksimum untuk laman web semasa',\n        perip: 'IP Tunggal',\n        peripHelper: 'Hadkan jumlah maksimum akses serentak untuk satu IP',\n        rate: 'Had trafik',\n        rateLimit: 'Had kadar per permintaan',\n        rateHelper: 'Had aliran setiap permintaan (unit: KB)',\n        rateLimitHelper: 'Hadkan kadar pemindahan bagi setiap permintaan (unit: KB/s)',\n        limitHelper: 'Aktifkan kawalan aliran',\n        other: 'Lain-lain',\n        currentSSL: 'Sijil Semasa',\n        dnsAccount: 'Akaun DNS',\n        applySSL: 'Permohonan Sijil',\n        SSLList: 'Senarai Sijil',\n        createDnsAccount: 'Akaun DNS',\n        aliyun: 'Aliyun',\n        aliEsa: 'Aliyun ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: 'Penyelesaian Manual',\n        key: 'Kunci',\n        check: 'Lihat',\n        acmeAccountManage: 'Pengurusan Akaun ACME',\n        email: 'E-mel',\n        acmeAccount: 'Akaun ACME',\n        provider: 'Kaedah Pengesahan',\n        dnsManual: 'Penyelesaian Manual',\n        expireDate: 'Tarikh Luput',\n        brand: 'Organisasi',\n        deploySSL: 'Penerapan',\n        deploySSLHelper: 'Adakah anda pasti mahu menerapkan sijil?',\n        ssl: 'Sijil | Sijil-Sijil',\n        dnsAccountManage: 'Penyedia DNS',\n        renewSSL: 'Perbaharui',\n        renewHelper: 'Adakah anda pasti mahu memperbaharui sijil?',\n        renewSuccess: 'Sijil diperbaharui',\n        enableHTTPS: 'Aktifkan',\n        aliasHelper: 'Alias ialah nama direktori laman web',\n        lastBackupAt: 'masa sandaran terakhir',\n        null: 'tiada',\n        nginxConfig: 'Konfigurasi Nginx',\n        websiteConfig: 'Tetapan Laman Web',\n        proxySettings: 'Tetapan Proksi',\n        advancedSettings: 'Tetapan Lanjutan',\n        cacheSettings: 'Tetapan Cache',\n        sniSettings: 'Tetapan SNI',\n        basic: 'Asas',\n        source: 'Konfigurasi',\n        security: 'Keselamatan',\n        nginxPer: 'Penalaan Prestasi Nginx',\n        neverExpire: 'Tidak Pernah Luput',\n        setDefault: 'Tetapkan sebagai lalai',\n        default: 'Lalai',\n        deleteHelper: 'Status aplikasi berkaitan tidak normal, sila semak',\n        toApp: 'Pergi ke senarai dipasang',\n        cycle: 'Kitaran',\n        frequency: 'Kekerapan',\n        ccHelper:\n            'Permintaan akumulatif untuk URL yang sama lebih daripada {1} kali dalam {0} saat, mencetuskan pertahanan CC, menyekat IP ini',\n        mustSave: 'Pengubahsuaian perlu disimpan untuk berkuat kuasa',\n        fileExt: 'Sambungan fail',\n        fileExtBlock: 'Senarai blok sambungan fail',\n        value: 'Nilai',\n        enable: 'Aktifkan',\n        proxyAddress: 'Alamat Proksi',\n        proxyHelper: 'Contoh: 127.0.0.1:8080',\n        forceDelete: 'Padam Paksa',\n        forceDeleteHelper: 'Padam paksa akan mengabaikan ralat semasa proses pemadaman dan akhirnya memadam metadata.',\n        deleteAppHelper: 'Padam aplikasi yang berkaitan dan sandaran aplikasi pada masa yang sama',\n        deleteBackupHelper: 'Juga padamkan sandaran laman web.',\n        deleteDatabaseHelper: 'Juga hapuskan pangkalan data yang berkaitan dengan laman web',\n        deleteConfirmHelper:\n            'Operasi pemadaman tidak boleh dibatalkan. Masukkan <span style=\"color:red\"> \"{0}\" </span> untuk mengesahkan pemadaman.',\n        staticPath: 'Direktori utama yang sepadan ialah ',\n        limit: 'Skim',\n        blog: 'Forum/Blog',\n        imageSite: 'Laman Gambar',\n        downloadSite: 'Laman Muat Turun',\n        shopSite: 'Pusat Membeli-belah',\n        doorSite: 'Portal',\n        qiteSite: 'Syarikat',\n        videoSite: 'Video',\n        errLog: 'Log Ralat',\n        stopHelper:\n            'Selepas menghentikan laman web, ia tidak akan dapat diakses dengan normal, dan pengguna akan melihat halaman berhenti laman web semasa apabila mengunjunginya. Adakah anda mahu meneruskan?',\n        startHelper:\n            'Selepas mengaktifkan laman web, pengguna boleh mengakses kandungan laman web seperti biasa. Adakah anda mahu meneruskan?',\n        sitePath: 'Direktori',\n        siteAlias: 'Alias Laman',\n        primaryPath: 'Direktori utama',\n        folderTitle: 'Laman web ini terutamanya mengandungi folder berikut',\n        wafFolder: 'Peraturan firewall',\n        indexFolder: 'Direktori akar laman web',\n        sslFolder: 'Sijil laman web',\n        enableOrNot: 'Aktifkan',\n        oldSSL: 'Sijil sedia ada',\n        manualSSL: 'Import sijil',\n        select: 'Pilih',\n        selectSSL: 'Pilih Sijil',\n        privateKey: 'Kunci (KEY)',\n        certificate: 'Sijil (format PEM)',\n        HTTPConfig: 'Pilihan HTTP',\n        HTTPSOnly: 'Blok permintaan HTTP',\n        HTTPToHTTPS: 'Alihkan ke HTTPS',\n        HTTPAlso: 'Benarkan permintaan HTTP langsung',\n        sslConfig: 'Pilihan SSL',\n        disableHTTPS: 'Lumpuhkan HTTPS',\n        disableHTTPSHelper: 'Melumpuhkan HTTPS akan memadam konfigurasi berkaitan sijil. Adakah anda mahu meneruskan?',\n        SSLHelper:\n            'Nota: Jangan gunakan sijil SSL untuk laman web tidak sah.\\nJika akses HTTPS tidak dapat digunakan selepas diaktifkan, semak sama ada kumpulan keselamatan telah melepaskan port 443 dengan betul.',\n        SSLConfig: 'Tetapan sijil',\n        SSLProConfig: 'Tetapan protokol',\n        supportProtocol: 'Versi protokol',\n        encryptionAlgorithm: 'Algoritma penyulitan',\n        notSecurity: '(tidak selamat)',\n        encryptHelper:\n            \"Let's Encrypt mempunyai had kekerapan untuk mengeluarkan sijil, tetapi mencukupi untuk memenuhi keperluan biasa. Operasi terlalu kerap akan menyebabkan kegagalan pengeluaran. Untuk sekatan tertentu, sila lihat <a target='_blank' href='https://letsencrypt.org/docs/rate-limits/'>dokumen rasmi</a>\",\n        ipValue: 'Nilai',\n        ext: 'sambungan fail',\n        wafInputHelper: 'Masukkan data secara berbaris, satu baris',\n        data: 'data',\n        ever: 'kekal',\n        nextYear: 'Satu tahun kemudian',\n        noLog: 'Tiada log ditemui',\n        defaultServer: 'Tapak lalai',\n        noDefaultServer: 'Tidak ditetapkan',\n        defaultServerHelper:\n            'Setelah menetapkan laman lalai, semua nama domain dan IP yang tidak terikat akan diarahkan ke laman lalai\\nIni dapat mencegah penyelesaian yang jahat secara berkesan\\nNamun, ini juga boleh menyebabkan kegagalan penghalang nama domain tanpa kebenaran WAF',\n        restoreHelper: 'Adakah anda pasti mahu memulihkan menggunakan sandaran ini?',\n        websiteDeploymentHelper: 'Gunakan aplikasi yang dipasang atau buat aplikasi baharu untuk mencipta laman web.',\n        websiteStatictHelper: 'Cipta direktori laman web pada hos.',\n        websiteProxyHelper:\n            'Gunakan proksi terbalik untuk memproksi perkhidmatan sedia ada. Contohnya, jika perkhidmatan dipasang dan berjalan pada port 8080, alamat proksi akan menjadi \"http://127.0.0.1:8080\".',\n        runtimeProxyHelper: 'Gunakan runtime laman web untuk mencipta laman web.',\n        runtime: 'Runtime',\n        deleteRuntimeHelper: 'Aplikasi Runtime perlu dipadamkan bersama laman web, sila berhati-hati',\n        proxyType: 'Jenis Rangkaian',\n        unix: 'Rangkaian Unix',\n        tcp: 'Rangkaian TCP/IP',\n        phpFPM: 'Konfigurasi FPM',\n        phpConfig: 'Konfigurasi PHP',\n        updateConfig: 'Kemas kini Konfigurasi',\n        isOn: 'Hidup',\n        isOff: 'Mati',\n        rewrite: 'Pseudo-statik',\n        rewriteMode: 'Skema',\n        current: 'Semasa',\n        rewriteHelper:\n            'Jika menetapkan pseudo-statik menyebabkan laman web tidak dapat diakses, cuba kembali ke tetapan lalai.',\n        runDir: 'Direktori Jalankan',\n        runUserHelper:\n            'Untuk laman web yang dikerahkan melalui persekitaran runtime kontena PHP, anda perlu menetapkan pemilik dan kumpulan pengguna bagi semua fail dan folder di bawah indeks dan subdirektori ke 1000. Untuk persekitaran PHP tempatan, rujuk tetapan pengguna PHP-FPM tempatan',\n        userGroup: 'Pengguna/Kumpulan',\n        uGroup: 'Kumpulan',\n        proxyPath: 'Laluan Proksi',\n        proxyPass: 'URL Sasaran',\n        cache: 'Cache',\n        cacheTime: 'Tempoh Cache',\n        enableCache: 'Cache',\n        proxyHost: 'Hos Proksi',\n        disabled: 'Dihentikan',\n        startProxy: 'Ini akan memulakan proksi terbalik. Adakah anda ingin meneruskan?',\n        stopProxy: 'Ini akan menghentikan proksi terbalik. Adakah anda ingin meneruskan?',\n        sourceFile: 'Sumber',\n        proxyHelper1: 'Semasa mengakses direktori ini, kandungan URL sasaran akan dikembalikan dan dipaparkan.',\n        proxyPassHelper: 'URL sasaran mesti sah dan boleh diakses.',\n        proxyHostHelper: 'Hantar nama domain dalam header permintaan ke pelayan proksi.',\n        replacementHelper: 'Hingga 5 penggantian boleh ditambah, sila kosongkan jika tiada penggantian diperlukan.',\n        modifier: 'Peraturan padanan',\n        modifierHelper:\n            'Contoh: \"=\" adalah padanan tepat, \"~\" adalah padanan biasa, \"^~\" memadankan permulaan laluan, dan sebagainya.',\n        replace: 'Penggantian Teks',\n        replaceHelper:\n            'Ciri penggantian teks nginx membenarkan penggantian rentetan dalam kandungan respons semasa proksi terbalik. Ia biasanya digunakan untuk mengubah suai pautan, alamat API, dll., dalam HTML, CSS, JavaScript, dan fail lain yang dikembalikan oleh backend. Ia menyokong padanan ungkapan biasa untuk keperluan penggantian kandungan yang kompleks.',\n        addReplace: 'Tambah',\n        replaced: 'String Carian (tidak boleh kosong)',\n        replaceText: 'Ganti dengan string',\n        replacedErr: 'String Carian tidak boleh kosong',\n        replacedErr2: 'String Carian tidak boleh berulang',\n        replacedListEmpty: 'Tiada peraturan penggantian teks',\n        proxySslName: 'Nama SNI Proksi',\n        basicAuth: 'Pengesahan Asas',\n        editBasicAuthHelper:\n            'Kata laluan disulitkan secara tidak simetri dan tidak dapat dipaparkan. Penyuntingan perlu menetapkan semula kata laluan',\n        antiLeech: 'Anti-leech',\n        extends: 'Pelanjutan',\n        browserCache: 'Cache',\n        noModify: 'Tidak Ubah',\n        serverCache: 'Cache Pelayan',\n        leechLog: 'Rekod log anti-leech',\n        accessDomain: 'Domain yang dibenarkan',\n        leechReturn: 'Sumber tindak balas',\n        noneRef: 'Benarkan referrer kosong',\n        disable: 'tidak diaktifkan',\n        disableLeechHelper: 'Adakah anda ingin mematikan anti-leech',\n        disableLeech: 'Matikan anti-leech',\n        ipv6: 'Dengar IPv6',\n        leechReturnError: 'Sila isikan kod status HTTP',\n        blockedRef: 'Benarkan referrer tidak standard',\n        accessControl: 'Kawalan anti-leech',\n        leechcacheControl: 'Kawalan cache',\n        logEnableControl: 'Log permintaan aset statik',\n        leechSpecialValidHelper:\n            \"Apabila 'Benarkan referrer kosong' didayakan, permintaan tanpa referrer (akses terus dan sebagainya) tidak akan disekat; mendayakan 'Benarkan referrer tidak standard' akan membenarkan mana-mana referrer yang tidak bermula dengan http/https (permintaan klien dan sebagainya).\",\n        leechInvalidReturnHelper: 'Kod status HTTP yang dipulangkan selepas menyekat permintaan hotlink',\n        leechlogControlHelper:\n            'Merekod permintaan aset statik; biasanya dimatikan dalam produksi untuk mengelakkan log berlebihan yang tidak perlu',\n        selectAcme: 'Pilih akaun Acme',\n        imported: 'Dibuat secara manual',\n        importType: 'Jenis import',\n        pasteSSL: 'Tampal kod',\n        localSSL: 'Pilih fail pelayan',\n        privateKeyPath: 'Fail kunci peribadi',\n        certificatePath: 'Fail sijil',\n        ipWhiteListHelper: 'Peranan senarai putih IP: semua peraturan tidak sah untuk senarai putih IP',\n        redirect: 'Alihkan',\n        sourceDomain: 'Domain sumber',\n        targetURL: 'Alamat URL Sasaran',\n        keepPath: 'Parameter URI',\n        path: 'laluan',\n        redirectType: 'Jenis pengalihan',\n        redirectWay: 'Cara',\n        keep: 'menyimpan',\n        notKeep: 'Jangan simpan',\n        redirectRoot: 'Alihkan ke halaman utama',\n        redirectHelper: 'Pengalihan kekal 301, pengalihan sementara 302',\n        changePHPVersionWarn:\n            'Menukar versi PHP akan memadamkan kontena PHP asal (kod laman web yang telah dimuatkan tidak akan hilang), teruskan? ',\n        changeVersion: 'Tukar versi',\n        retainConfig: 'Adakah untuk menyimpan fail php-fpm.conf dan php.ini',\n        runDirHelper2: 'Sila pastikan direktori berjalan sekunder berada di bawah direktori indeks',\n        openrestyHelper:\n            'Port HTTP lalai OpenResty: {0} Port HTTPS: {1}, yang mungkin mempengaruhi akses nama domain laman web dan pengalihan HTTPS paksa',\n        primaryDomainHelper: 'Contoh: example.com atau example.com:8080',\n        acmeAccountType: 'Jenis akaun',\n        keyType: 'Algoritma Kunci',\n        tencentCloud: 'Tencent Cloud',\n        containWarn: 'Nama domain mengandungi domain utama, sila masukkan semula',\n        rewriteHelper2:\n            'Aplikasi seperti WordPress yang dipasang dari kedai aplikasi biasanya dilengkapi dengan konfigurasi pseudo-statik praset. Mengkonfigurasi semula mereka boleh menyebabkan ralat.',\n        websiteBackupWarn:\n            'Hanya menyokong pengimportan sandaran tempatan, pengimportan sandaran dari mesin lain boleh menyebabkan kegagalan pemulihan',\n        ipWebsiteWarn:\n            'Laman web dengan IP sebagai nama domain perlu disetkan sebagai laman web lalai untuk diakses secara normal.',\n        hstsHelper: 'Mengaktifkan HSTS boleh meningkatkan keselamatan laman web',\n        includeSubDomains: 'SubDomains',\n        hstsIncludeSubDomainsHelper:\n            'Apabila diaktifkan, dasar HSTS akan digunakan pada semua subdomain bagi domain semasa.',\n        defaultHtml: 'Halaman lalai',\n        website404: 'Halaman ralat 404 laman web',\n        domain404: 'Domain laman web tidak wujud',\n        indexHtml: 'Indeks untuk laman web statik',\n        stopHtml: 'Laman web dihentikan',\n        indexPHP: 'Indeks untuk laman web PHP',\n        sslExpireDate: 'Tarikh Tamat Tempoh Sijil',\n        website404Helper:\n            'Halaman ralat 404 laman web hanya menyokong laman web persekitaran runtime PHP dan laman web statik',\n        sni: 'Sumber SNI',\n        sniHelper:\n            'Apabila backend proksi terbalik adalah HTTPS, anda mungkin perlu menetapkan sumber SNI. Sila rujuk dokumentasi penyedia perkhidmatan CDN untuk butiran.',\n        huaweicloud: 'Huawei Cloud',\n        createDb: 'Cipta Pangkalan Data',\n        enableSSLHelper: 'Kegagalan mengaktifkan SSL tidak akan menjejaskan penciptaan laman web.',\n        batchAdd: 'Tambah Domain Secara Batch',\n        batchInput: 'Input Kelompok',\n        domainNotFQDN: 'Domain ini mungkin tidak boleh diakses di rangkaian awam',\n        domainInvalid: 'Format domain tidak sah',\n        domainBatchHelper:\n            'Satu domain per baris, format: domain:port@ssl\\nContoh: example.com:443@ssl atau example.com',\n        generateDomain: 'Hasilkan',\n        global: 'Global',\n        subsite: 'Sublaman',\n        subsiteHelper:\n            'Sublaman boleh memilih direktori laman web PHP atau statik yang sedia ada sebagai direktori akar.',\n        parentWebsite: 'Laman Web Induk',\n        deleteSubsite: 'Untuk memadam laman web semasa, anda mesti memadam sublaman {0} terlebih dahulu.',\n        loadBalance: 'Pengimbangan Beban',\n        server: 'Nod',\n        algorithm: 'Algoritma',\n        ipHash: 'IP Hash',\n        ipHashHelper:\n            'Mengagihkan permintaan ke pelayan tertentu berdasarkan alamat IP klien, memastikan klien tertentu sentiasa diarahkan ke pelayan yang sama.',\n        leastConn: 'Sambungan Terkecil',\n        leastConnHelper: 'Menghantar permintaan ke pelayan dengan sambungan aktif paling sedikit.',\n        leastTime: 'Masa Terkecil',\n        leastTimeHelper: 'Menghantar permintaan ke pelayan dengan masa sambungan aktif terpendek.',\n        defaultHelper:\n            'Kaedah lalai, permintaan diagihkan secara merata ke setiap pelayan. Jika pelayan mempunyai konfigurasi berat, permintaan diagihkan mengikut berat yang ditentukan. Pelayan dengan berat lebih tinggi menerima lebih banyak permintaan.',\n        weight: 'Berat',\n        maxFails: 'Kegagalan Maksimum',\n        maxConns: 'Sambungan Maksimum',\n        strategy: 'Strategi',\n        strategyDown: 'Lumpuh',\n        strategyBackup: 'Sandaran',\n        ipHashBackupErr: 'Hash IP tidak menyokong nod sandaran',\n        failTimeout: 'Masa tamat kegagalan',\n        failTimeoutHelper:\n            'Panjang tetingkap masa untuk pemeriksaan kesihatan pelayan. Apabila bilangan kegagalan terkumpul mencapai ambang dalam tempoh ini, pelayan akan dikeluarkan buat sementara waktu dan dicuba semula selepas tempoh yang sama. Lalai 10 saat',\n        staticChangePHPHelper: 'Kini laman web statik, boleh ditukar ke laman web PHP.',\n        proxyCache: 'Cache Proksi Terbalik',\n        cacheLimit: 'Had Ruang Cache',\n        shareCache: 'Saiz Memori Kiraan Cache',\n        cacheExpire: 'Masa Tamat Cache',\n        shareCacheHelper: '1M memori boleh menyimpan kira-kira 8000 objek cache.',\n        cacheLimitHelper: 'Melebihi had akan menghapus cache lama secara automatik.',\n        cacheExpireHelper: 'Cache yang tidak dipenuhi dalam masa tamat akan dihapuskan.',\n        realIP: 'IP Sebenar',\n        ipFrom: 'Sumber IP',\n        ipFromHelper:\n            'Dengan mengkonfigurasi sumber IP yang dipercayai, OpenResty akan menganalisis maklumat IP dalam HTTP Header untuk mengenal pasti dan merekodkan alamat IP sebenar pelawat, termasuk dalam log akses.',\n        ipFromExample1: 'Jika frontend adalah alat seperti Frp, anda boleh mengisi alamat IP Frp, seperti 127.0.0.1.',\n        ipFromExample2: 'Jika frontend adalah CDN, anda boleh mengisi julat IP CDN.',\n        ipFromExample3:\n            'Jika tidak pasti, anda boleh mengisi 0.0.0.0/0 (IPv4) atau ::/0 (IPv6). [Nota: Membenarkan sebarang sumber IP tidak selamat.]',\n        http3Helper:\n            'HTTP/3 adalah versi naik taraf HTTP/2, menyediakan kelajuan sambungan yang lebih pantas dan prestasi yang lebih baik. Walau bagaimanapun, tidak semua penyemak imbas menyokong HTTP/3, dan mengaktifkannya mungkin menyebabkan beberapa penyemak imbas tidak dapat mengakses laman web.',\n        cors: 'Perkongsian Sumber Asal Silang (CORS)',\n        enableCors: 'Dayakan CORS',\n        allowOrigins: 'Domain yang dibenarkan',\n        allowMethods: 'Kaedah permintaan yang dibenarkan',\n        allowHeaders: 'Pengepala permintaan yang dibenarkan',\n        allowCredentials: 'Benarkan kuki dihantar',\n        preflight: 'Tindak balas pantas permintaan preflight',\n        preflightHleper:\n            'Apabila didayakan, apabila pelayar menghantar permintaan preflight asal silang (permintaan OPTIONS), sistem akan secara automatik mengembalikan kod status 204 dan menetapkan pengepala respons asal silang yang diperlukan',\n        changeDatabase: 'Tukar Pangkalan Data',\n        changeDatabaseHelper1: 'Perkaitan pangkalan data digunakan untuk sandaran dan pemulihan laman web.',\n        changeDatabaseHelper2:\n            'Menukar ke pangkalan data lain mungkin menyebabkan sandaran sebelumnya tidak dapat dipulihkan.',\n        saveCustom: 'Simpan sebagai Templat',\n        rainyun: 'Rainyun',\n        volcengine: 'Volcengine',\n        runtimePortHelper: 'Persekitaran runtime semasa mempunyai beberapa port. Sila pilih port proksi.',\n        runtimePortWarn: 'Persekitaran runtime semasa tidak mempunyai port, tidak dapat proksi',\n        cacheWarn: 'Sila matikan suis cache dalam pembalikan proksi terlebih dahulu',\n        loadBalanceHelper:\n            'Setelah mencipta pengimbang beban, sila pergi ke \"Reverse Proxy\", tambahkan proksi dan tetapkan alamat backend ke: http://<nama pengimbang beban>.',\n        favorite: 'Kegemaran',\n        cancelFavorite: 'Batalkan Kegemaran',\n        useProxy: 'Gunakan Proksi',\n        useProxyHelper: 'Gunakan alamat pelayan proksi dalam tetapan panel',\n        westCN: 'West Digital',\n        openBaseDir: 'Pencegahan Serangan Lintas Situs',\n        openBaseDirHelper:\n            'open_basedir digunakan untuk membatasi jalur akses file PHP, yang membantu mencegah akses lintas situs dan meningkatkan keamanan',\n        serverCacheTime: 'Masa Cache Pelayan',\n        serverCacheTimeHelper:\n            'Masa permintaan di-cache di pelayan. Semasa tempoh ini, permintaan yang sama akan mengembalikan hasil cache terus tanpa meminta pelayan asal.',\n        browserCacheTime: 'Masa Cache Pelayar',\n        browserCacheTimeHelper:\n            'Masa sumber statik di-cache secara tempatan di pelayar, mengurangkan permintaan berulang. Pengguna akan menggunakan cache tempatan secara langsung sebelum tamat tempoh semasa menyegarkan halaman.',\n        donotLinkeDB: 'Jangan Sambungkan Pangkalan Data',\n        toWebsiteDir: 'Masuk ke Direktori Laman Web',\n        execParameters: 'Parameter Pelaksanaan',\n        extCommand: 'Arahan Tambahan',\n        mirror: 'Sumber Cermin',\n        execUser: 'Pengguna Melaksanakan',\n        execDir: 'Direktori Pelaksanaan',\n        packagist: 'Cermin Penuh China',\n        batchOperate: 'Operasi Pukal',\n        batchOperateHelper: 'Pukal {0} laman web, teruskan operasi?',\n        stream: 'Proksi TCP/UDP',\n        streamPorts: 'Port Mendengar',\n        streamPortsHelper:\n            'Tetapkan nombor port pendengaran luaran, pelanggan akan mengakses perkhidmatan melalui port ini, dipisahkan dengan koma, cth., 5222,5223',\n        streamHelper: 'Penerusan Port dan Pengimbangan Beban TCP/UDP',\n        udp: 'Dayakan UDP',\n        syncHtmlHelper: 'Segerakkan ke PHP dan laman web statik',\n        wafValueHelper: 'Nilai',\n        parentWbeiste: 'Laman induk',\n        shareCahe: 'Saiz memori kiraan cache',\n        shareCaheHelper: 'Kira-kira 8000 objek cache boleh disimpan bagi setiap 1M memori',\n        cacheExpireJHelper: 'Cache akan dipadam jika tiada hit selepas tamat tempoh',\n    },\n    php: {\n        short_open_tag: 'Sokongan tag pendek',\n        max_execution_time: 'Masa maksimum pelaksanaan skrip',\n        max_input_time: 'Masa input maksimum',\n        memory_limit: 'Had memori skrip',\n        post_max_size: 'Saiz maksimum data POST',\n        file_uploads: 'Sama ada membenarkan muat naik fail',\n        upload_max_filesize: 'Saiz maksimum fail yang dibenarkan untuk dimuat naik',\n        max_file_uploads: 'Bilangan maksimum fail yang dibenarkan untuk dimuat naik pada masa yang sama',\n        default_socket_timeout: 'Masa tamat soket',\n        error_reporting: 'Tahap kesilapan',\n        display_errors: 'Sama ada untuk output maklumat ralat terperinci',\n        cgi_fix_pathinfo: 'Sama ada untuk membuka pathinfo',\n        date_timezone: 'Zon waktu',\n        disableFunction: 'Lumpuhkan fungsi',\n        disableFunctionHelper:\n            'Masukkan fungsi yang ingin dilumpuhkan, seperti exec, gunakan pemisah untuk banyak fungsi',\n        uploadMaxSize: 'Had muat naik',\n        indexHelper:\n            'Untuk memastikan operasi laman web PHP berjalan lancar, sila letakkan kod dalam direktori indeks dan elakkan menamakan semula',\n        extensions: 'Templat sambungan',\n        extension: 'Sambungan',\n        extensionHelper: 'Gunakan pemisah untuk banyak sambungan',\n        toExtensionsList: 'Lihat senarai sambungan',\n        containerConfig: 'Konfigurasi Bekas',\n        containerConfigHelper:\n            'Pembolehubah persekitaran dan maklumat lain boleh diubah suai dalam Konfigurasi - Konfigurasi Bekas selepas penciptaan',\n        dateTimezoneHelper: 'Contoh: TZ=Asia/Shanghai (Sila tambahkan jika perlu)',\n        extensionsHelper: 'Untuk berbilang sambungan, asingkan dengan koma',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: 'Saiz jadual hash nama pelayan',\n        clientHeaderBufferSizeHelper: 'Saiz buffer header yang diminta oleh klien',\n        clientMaxBodySizeHelper: 'Fail muat naik maksimum',\n        keepaliveTimeoutHelper: 'Masa tamat sambungan',\n        gzipMinLengthHelper: 'Saiz minimum fail untuk pemampatan',\n        gzipCompLevelHelper: 'Kadar mampatan',\n        gzipHelper: 'Aktifkan pemampatan untuk penghantaran',\n        connections: 'Sambungan aktif',\n        accepts: 'Diterima',\n        handled: 'Diuruskan',\n        requests: 'Permintaan',\n        reading: 'Membaca',\n        writing: 'Menulis',\n        waiting: 'Menunggu',\n        status: 'Status Semasa',\n        configResource: 'Konfigurasi',\n        saveAndReload: 'Simpan dan muat semula',\n        clearProxyCache: 'Bersihkan cache proksi terbalik',\n        clearProxyCacheWarn:\n            'Semua laman web yang dikonfigurasi dengan cache akan terjejas dan \"OpenResty\" akan dimulakan semula. Adakah anda mahu meneruskan?',\n        create: 'Tambah Modul',\n        update: 'Edit Modul',\n        params: 'Parameter',\n        packages: 'Pakej',\n        script: 'Script',\n        module: 'Modul',\n        build: 'Bina',\n        buildWarn:\n            'Membina OpenResty memerlukan menyediakan sejumlah CPU dan memori, dan prosesnya mengambil masa yang lama, sila bersabar.',\n        mirrorUrl: 'Sumber Perisian',\n        paramsHelper: 'Contoh: --add-module=/tmp/ngx_brotli',\n        packagesHelper: 'Contoh: git,curl dipisahkan oleh koma',\n        scriptHelper:\n            'Skrip yang dilaksanakan sebelum penyusunan, biasanya untuk memuat turun sumber kod modul, memasang kebergantungan, dll.',\n        buildHelper:\n            'Klik Bina selepas menambah/mengubah suai modul. Pembinaan yang berjaya akan memulakan semula OpenResty secara automatik.',\n        defaultHttps: 'HTTPS Anti-tampering',\n        defaultHttpsHelper1: 'Mengaktifkan ini dapat menyelesaikan masalah tampering HTTPS.',\n        sslRejectHandshake: 'Tolak jabat tangan SSL lalai',\n        sslRejectHandshakeHelper:\n            'Mengaktifkan ini boleh mengelakkan kebocoran sijil, menetapkan laman web lalai akan membatalkan tetapan ini',\n    },\n    ssl: {\n        create: 'Permintaan',\n        provider: 'Jenis',\n        manualCreate: 'Dicipta secara manual',\n        acmeAccount: 'Akaun ACME',\n        resolveDomain: 'Selesaikan nama domain',\n        err: 'Ralat',\n        value: 'Nilai rekod',\n        dnsResolveHelper: 'Sila pergi ke pembekal perkhidmatan resolusi DNS untuk menambah rekod resolusi berikut:',\n        detail: 'Perincian',\n        msg: 'Maklumat',\n        ssl: 'Sijil',\n        key: 'Kunci peribadi',\n        startDate: 'Waktu berkuatkuasa',\n        organization: 'Organisasi penerbit',\n        renewConfirm: 'Ini akan memperbaharui sijil baru untuk nama domain {0}. Adakah anda mahu meneruskan?',\n        autoRenew: 'Pembaharuan Automatik',\n        autoRenewHelper: 'Perbaharui secara automatik 30 hari sebelum tamat tempoh',\n        renewSuccess: 'Pembaharuan berjaya',\n        renewWebsite:\n            'Sijil ini telah dikaitkan dengan laman web berikut, dan aplikasi akan digunakan pada laman web ini secara serentak',\n        createAcme: 'Buat Akaun',\n        acmeHelper: 'Akaun Acme digunakan untuk memohon sijil percuma',\n        upload: 'Import',\n        applyType: 'Jenis',\n        apply: 'Perbaharui',\n        applyStart: 'Permohonan sijil bermula',\n        getDnsResolve: 'Mendapatkan nilai resolusi DNS, sila tunggu...',\n        selfSigned: 'CA Ditandatangani Sendiri',\n        ca: 'Pihak berkuasa sijil',\n        commonName: 'Nama biasa',\n        caName: 'Nama pihak berkuasa sijil',\n        company: 'Nama organisasi',\n        department: 'Nama unit organisasi',\n        city: 'Nama bandar',\n        province: 'Nama negeri atau wilayah',\n        country: 'Kod negara (2 huruf)',\n        commonNameHelper: 'Sebagai contoh, ',\n        selfSign: 'Keluarkan sijil',\n        days: 'Tempoh sah',\n        domainHelper: 'Satu nama domain setiap baris, menyokong * dan alamat IP',\n        pushDir: 'Tolakkan sijil ke direktori tempatan',\n        dir: 'Direktori',\n        pushDirHelper: 'Fail sijil \"fullchain.pem\" dan fail kunci \"privkey.pem\" akan dihasilkan dalam direktori ini.',\n        organizationDetail: 'Butiran organisasi',\n        fromWebsite: 'Daripada laman web',\n        dnsMauanlHelper:\n            'Dalam mod resolusi manual, anda perlu klik butang mohon selepas penciptaan untuk mendapatkan nilai resolusi DNS',\n        httpHelper:\n            'Menggunakan mod HTTP memerlukan pemasangan OpenResty dan tidak menyokong permohonan sijil domain wildcard.',\n        buypassHelper: 'Buypass tidak boleh diakses di tanah besar China',\n        googleHelper: 'Cara mendapatkan EAB HmacKey dan EAB kid',\n        googleCloudHelper: 'Google Cloud API tidak boleh diakses di kebanyakan kawasan tanah besar China',\n        skipDNSCheck: 'Langkau semakan DNS',\n        skipDNSCheckHelper: 'Semak di sini hanya jika anda menghadapi isu tamat masa semasa permintaan pengesahan.',\n        cfHelper: 'Jangan gunakan Global API Key',\n        deprecated: 'akan dihentikan',\n        deprecatedHelper:\n            'Penyelenggaraan telah dihentikan dan mungkin akan dibuang dalam versi masa hadapan. Sila gunakan kaedah Tencent Cloud untuk analisis',\n        disableCNAME: 'Lumpuhkan CNAME',\n        disableCNAMEHelper: 'Semak di sini jika nama domain mempunyai rekod CNAME dan permintaan gagal.',\n        nameserver: 'Pelayan DNS',\n        nameserverHelper: 'Gunakan pelayan DNS tersuai untuk mengesahkan nama domain.',\n        edit: 'Edit sijil',\n        execShell: 'Jalankan skrip selepas permintaan pengesahan.',\n        shell: 'Kandungan skrip',\n        shellHelper:\n            'Direktori pelaksanaan lalai skrip adalah direktori pemasangan 1Panel. Jika sijil ditolak ke direktori tempatan, direktori pelaksanaan akan menjadi direktori tolak sijil. Tamat masa pelaksanaan lalai ialah 30 minit.',\n        customAcme: 'Perkhidmatan ACME Tersuai',\n        customAcmeURL: 'URL Perkhidmatan ACME',\n        baiduCloud: 'Baidu Cloud',\n        pushNode: 'Segerakan ke Nod Lain',\n        pushNodeHelper: 'Tolak ke nod terpilih selepas permohonan/pembaharuan',\n        fromMaster: 'Tolak dari Nod Utama',\n        hostedZoneID: 'Hosted Zone ID',\n        isIP: 'Sijil IP',\n        useEAB: 'Gunakan pengesahan EAB',\n    },\n    firewall: {\n        create: 'Buat peraturan',\n        edit: 'Edit peraturan',\n        ccDeny: 'Perlindungan CC',\n        ipWhiteList: 'Senarai putih IP',\n        ipBlockList: 'Senarai blok IP',\n        fileExtBlockList: 'Senarai blok sambungan fail',\n        urlWhiteList: 'Senarai putih URL',\n        urlBlockList: 'Senarai blok URL',\n        argsCheck: 'Pengesahan parameter GET',\n        postCheck: 'Pengesahan parameter POST',\n        cookieBlockList: 'Senarai blok Cookie',\n        dockerHelper:\n            'Firewall semasa tidak boleh melumpuhkan pemetaan port bekas. Aplikasi yang dipasang boleh pergi ke halaman [Dipasang] untuk mengedit parameter aplikasi dan mengkonfigurasi peraturan pelepasan port.',\n        iptablesHelper:\n            'Dikesan sistem menggunakan firewall {0}. Untuk beralih ke iptables, sila nyahpasang secara manual dahulu!',\n        quickJump: 'Akses pantas',\n        used: 'Digunakan',\n        unUsed: 'Tidak Digunakan',\n        dockerRestart: 'Operasi firewall memerlukan memulakan semula perkhidmatan Docker',\n        firewallHelper: '{0} firewall sistem',\n        firewallNotStart: 'Firewall sistem belum diaktifkan. Aktifkannya dahulu.',\n        restartFirewallHelper: 'Operasi ini akan memulakan semula firewall semasa. Adakah anda mahu meneruskan?',\n        stopFirewallHelper:\n            'Ini akan menyebabkan pelayan kehilangan perlindungan keselamatan. Adakah anda mahu meneruskan?',\n        startFirewallHelper:\n            'Selepas firewall diaktifkan, keselamatan pelayan boleh dilindungi dengan lebih baik. Adakah anda mahu meneruskan?',\n        noPing: 'Lumpuhkan ping',\n        enableBanPing: 'Sekat Ping',\n        disableBanPing: 'Nyahsekat Ping',\n        noPingTitle: 'Lumpuhkan ping',\n        noPingHelper:\n            'Ini akan melumpuhkan ping, dan pelayan tidak akan memberikan tindak balas ICMP. Adakah anda mahu meneruskan?',\n        onPingHelper:\n            'Ini akan mengaktifkan ping, dan penggodam mungkin menemui pelayan anda. Adakah anda mahu meneruskan?',\n        changeStrategy: 'Tukar strategi {0}',\n        changeStrategyIPHelper1:\n            'Tukar strategi alamat IP kepada [deny]. Selepas alamat IP ditetapkan, akses kepada pelayan dilarang. Adakah anda mahu meneruskan?',\n        changeStrategyIPHelper2:\n            'Tukar strategi alamat IP kepada [allow]. Selepas alamat IP ditetapkan, akses normal dipulihkan. Adakah anda mahu meneruskan?',\n        changeStrategyPortHelper1:\n            'Tukar dasar port kepada [drop]. Selepas dasar port ditetapkan, akses luaran ditolak. Adakah anda mahu meneruskan?',\n        changeStrategyPortHelper2:\n            'Tukar dasar port kepada [accept]. Selepas dasar port ditetapkan, akses port biasa akan dipulihkan. Adakah anda mahu meneruskan?',\n        stop: 'Hentikan',\n        portFormatError: 'Medan ini mesti port yang sah.',\n        portHelper1: 'Pelbagai port, contohnya 8080 dan 8081',\n        portHelper2: 'Port rentang, contohnya 8080-8089',\n        changeStrategyHelper:\n            'Tukar strategi {0} [{1}] kepada [{2}]. Selepas tetapan, {0} akan mengakses {2} secara luaran. Adakah anda mahu meneruskan?',\n        strategy: 'Strategi',\n        accept: 'Terima',\n        drop: 'Lumpuhkan',\n        anyWhere: 'Mana-mana',\n        address: 'Alamat IP tertentu',\n        addressHelper: 'Sokong alamat IP atau segmen IP',\n        allow: 'Benarkan',\n        deny: 'Tolak',\n        addressFormatError: 'Medan ini mesti alamat IP yang sah.',\n        addressHelper1: 'Sokong alamat IP atau julat IP. Sebagai contoh, \"172.16.10.11\" atau \"172.16.10.0/24\".',\n        addressHelper2: 'Untuk pelbagai alamat IP, pisahkan dengan koma. Contohnya, \"172.16.10.11, 172.16.0.0/24\".',\n        allIP: 'Semua IP',\n        portRule: 'Peraturan | Peraturan',\n        forwardRule: 'Peraturan Pemajuan Port | Peraturan Pemajuan Port',\n        ipRule: 'Peraturan IP | Peraturan IP',\n        userAgent: 'Penapis User-Agent',\n        sourcePort: 'Port sumber',\n        targetIP: 'IP sasaran',\n        targetPort: 'Port sasaran',\n        forwardHelper1: 'Jika anda ingin memajukan ke port tempatan, IP sasaran harus ditetapkan kepada \"127.0.0.1\".',\n        forwardHelper2: 'Biarkan IP sasaran kosong untuk memajukan ke port tempatan.',\n        forwardPortHelper: 'Menyokong julat port, cth: 8080-8089',\n        forwardInboundInterface: 'Antara Muka Rangkaian Masukan Penerusan',\n        exportHelper: 'Akan mengeksport {0} peraturan firewall. Teruskan?',\n        importSuccess: '{0} peraturan berjaya diimport',\n        importPartialSuccess: 'Import selesai: {0} berjaya, {1} gagal',\n        ipv4Limit: 'Operasi semasa hanya menyokong alamat IPv4',\n        basicStatus: 'Rantaian semasa {0} tidak terikat, sila ikat dahulu!',\n        baseIptables: 'Perkhidmatan iptables',\n        forwardIptables: 'Perkhidmatan Penerusan Port iptables',\n        advanceIptables: 'Perkhidmatan Konfigurasi Lanjutan iptables',\n        initMsg: 'Akan memulakan {0}, teruskan?',\n        initHelper: 'Mengesan {0} tidak dimulakan. Sila klik butang pemulaan di bar status atas untuk mengkonfigurasi!',\n        bindHelper: 'Ikat - Peraturan firewall hanya akan berkuat kuasa apabila status terikat. Sahkan?',\n        unbindHelper:\n            'Nyahikat - Apabila tidak terikat, semua peraturan firewall yang ditambah akan menjadi tidak sah. Teruskan dengan berhati-hati. Sahkan?',\n        defaultStrategy: 'Dasar lalai untuk rantaian semasa {0} adalah {1}',\n        defaultStrategy2:\n            'Dasar lalai untuk rantaian semasa {0} adalah {1}, status semasa adalah tidak terikat. Peraturan firewall yang ditambah akan berkuat kuasa selepas pengikatan!',\n        filterRule: 'Peraturan Penapis',\n        filterHelper:\n            'Peraturan penapis membolehkan anda mengawal trafik rangkaian pada tahap INPUT/OUTPUT. Konfigurasikan dengan berhati-hati untuk mengelakkan mengunci sistem.',\n        chain: 'Rantai',\n        targetChain: 'Rantai Sasaran',\n        sourceIP: 'IP Sumber',\n        destIP: 'IP Destinasi',\n        inboundDirection: 'Arah Masuk',\n        outboundDirection: 'Arah Keluar',\n        destPort: 'Port Destinasi',\n        action: 'Tindakan',\n        reject: 'Tolak',\n        sourceIPHelper: 'Format CIDR, cth. 192.168.1.0/24. Biarkan kosong untuk semua alamat',\n        destIPHelper: 'Format CIDR, cth. 10.0.0.0/8. Biarkan kosong untuk semua alamat',\n        portHelper: '0 bermaksud mana-mana port',\n        allPorts: 'Semua Port',\n        deleteRuleConfirm: 'Akan memadam {0} peraturan. Teruskan?',\n        advancedControl: 'Kawalan lanjutan',\n        advancedControlNotAvailable: 'Kini menggunakan firewall {0}, peraturan lanjutan hanya menyokong iptables',\n        destination: 'Destinasi',\n    },\n    runtime: {\n        runtime: 'Runtime',\n        workDir: 'Direktori kerja',\n        localHelper:\n            'Untuk masalah pemasangan persekitaran tempatan dan penggunaan persekitaran luar talian, sila rujuk ',\n        versionHelper: 'Versi PHP, contohnya v8.0',\n        buildHelper:\n            'Semakin banyak sambungan, semakin tinggi penggunaan CPU semasa membuat imej. Sambungan boleh dipasang selepas persekitaran dibuat.',\n        openrestyWarn: 'PHP perlu dinaik taraf kepada OpenResty versi 1.21.4.1 atau lebih tinggi untuk digunakan',\n        toupgrade: 'Naik Taraf',\n        edit: 'Edit runtime',\n        extendHelper:\n            'Sambungan yang tidak disenaraikan boleh dimasukkan dan dipilih secara manual. Sebagai contoh, masukkan \"sockets\" dan pilih pilihan pertama dari senarai juntai bawah untuk melihat senarai sambungan.',\n        rebuildHelper: 'Selepas mengedit sambungan, anda perlu membina semula aplikasi PHP untuk ia berkesan',\n        rebuild: 'Bina Semula Aplikasi PHP',\n        source: 'Sumber sambungan PHP',\n        ustc: 'Universiti Sains dan Teknologi China',\n        netease: 'Netease',\n        aliyun: 'Alibaba Cloud',\n        default: 'Default',\n        tsinghua: 'Universiti Tsinghua',\n        xtomhk: 'Stesen Cermin XTOM (Hong Kong)',\n        xtom: 'Stesen Cermin XTOM (Global)',\n        phpsourceHelper: 'Pilih sumber yang sesuai mengikut persekitaran rangkaian anda.',\n        appPort: 'Port aplikasi',\n        externalPort: 'Port luaran',\n        packageManager: 'Pengurus pakej',\n        codeDir: 'Direktori kod',\n        appPortHelper: 'Port yang digunakan oleh aplikasi.',\n        externalPortHelper: 'Port yang terdedah kepada dunia luar.',\n        runScript: 'Skrip run',\n        runScriptHelper: 'Senarai arahan permulaan diuraikan dari fail package.json dalam direktori sumber.',\n        open: 'Buka',\n        operatorHelper:\n            'Operasi {0} akan dilakukan pada persekitaran operasi yang dipilih. Adakah anda mahu meneruskan?',\n        taobao: 'Taobao',\n        tencent: 'Tencent',\n        imageSource: 'Sumber imej',\n        moduleManager: 'Pengurusan Modul',\n        module: 'Modul',\n        nodeOperatorHelper:\n            'Adakah {0} {1} modul? Operasi ini mungkin menyebabkan ketidaknormalan dalam persekitaran operasi. Sila pastikan sebelum meneruskan',\n        customScript: 'Arahan permulaan tersuai',\n        customScriptHelper:\n            'Sila masukkan arahan permulaan penuh, contoh: npm run start. Untuk arahan permulaan PM2, sila gantikan dengan pm2-runtime, jika tidak ia akan gagal bermula.',\n        portError: 'Jangan ulangi port yang sama.',\n        systemRestartHelper: 'Huraian status: Gangguan - status gagal diperoleh kerana sistem dimulakan semula',\n        javaScriptHelper: 'Sediakan arahan permulaan penuh. Contohnya, \"java -jar halo.jar -Xmx1024M -Xms256M\".',\n        javaDirHelper: 'Direktori mesti mengandungi fail jar, subdirektori juga diterima',\n        goHelper: 'Sediakan arahan permulaan penuh. Contohnya, \"go run main.go\" atau \"./main\".',\n        goDirHelper: 'Direktori atau subdirektori mesti mengandungi fail Go atau binari.',\n        pythonHelper:\n            'Sediakan arahan permulaan penuh. Contohnya, \"pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000\".',\n        dotnetHelper: 'Sila isi arahan pelancaran lengkap, contohnya dotnet MyWebApp.dll',\n        dirHelper: 'Nota: Sila isi laluan direktori di dalam bekas',\n        concurrency: 'Skim Serentak',\n        loadStatus: 'Status Beban',\n        extraHosts: 'Pemetaan hos',\n        extension: 'Sambungan',\n        installExtension: 'Adakah anda pasti untuk memasang sambungan {0}',\n        loadedExtension: 'Sambungan dimuatkan',\n        popularExtension: 'Sambungan popular',\n        uninstallExtension: 'Adakah anda pasti untuk nyahpasang sambungan {0}',\n        phpConfigHelper: 'Perubahan konfigurasi memerlukan persekitaran operasi dimulakan semula, teruskan',\n        operateMode: 'mod operasi',\n        dynamic: 'dinamik',\n        static: 'statik',\n        ondemand: 'atas permintaan',\n        dynamicHelper: 'Laraskan bilangan proses secara dinamik, sesuai untuk trafik berubah atau memori rendah',\n        staticHelper: 'Bilangan proses tetap, sesuai untuk trafik stabil dan serentak tinggi, penggunaan sumber tinggi',\n        ondemandHelper:\n            'Proses dimulakan dan ditutup mengikut permintaan, penggunaan sumber optimum tetapi respons awal mungkin perlahan',\n        max_children: 'bilangan maksimum proses yang dibenarkan',\n        start_servers: 'bilangan proses semasa permulaan',\n        min_spare_servers: 'bilangan minimum proses melahu',\n        max_spare_servers: 'bilangan maksimum proses melahu',\n        envKey: 'Nama',\n        envValue: 'Nilai',\n        environment: 'Pembolehubah persekitaran',\n    },\n    process: {\n        pid: 'Process ID',\n        ppid: 'Parent PID',\n        numThreads: 'Threads',\n        memory: 'Memory',\n        diskRead: 'Disk read',\n        diskWrite: 'Disk write',\n        netSent: 'uplink',\n        netRecv: 'downstream',\n        numConnections: 'Connections',\n        startTime: 'Start time',\n        state: 'State',\n        running: 'Running',\n        sleep: 'sleep',\n        stop: 'stop',\n        idle: 'idle',\n        zombie: 'zombie process',\n        wait: 'waiting',\n        lock: 'lock',\n        blocked: 'blocked',\n        cmdLine: 'Start command',\n        basic: 'Basic',\n        mem: 'Memory',\n        openFiles: 'Open files',\n        env: 'Environments',\n        noenv: 'None',\n        net: 'Network connections',\n        laddr: 'Source address/port',\n        raddr: 'Destination address/port',\n        stopProcess: 'End',\n        viewDetails: 'Details',\n        stopProcessWarn: 'Are you sure you want to end this process (PID:{0})?',\n        kill: 'Hentikan Proses',\n        killNow: 'Hentikan Sekarang',\n        killHelper:\n            'Menghentikan proses {0} mungkin menyebabkan beberapa program tidak berfungsi dengan normal. Teruskan?',\n        processName: 'Process name',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: 'Gagal mendapatkan status proses, sila semak status perkhidmatan supervisor.',\n            notSupport:\n                'Perkhidmatan Supervisor tidak dikesan. Sila pergi ke halaman pustaka skrip untuk pemasangan manual',\n            list: 'Proses Daemon | Proses Daemon',\n            config: 'Konfigurasi Supervisor',\n            primaryConfig: 'Lokasi fail konfigurasi utama',\n            notSupportCtl: 'supervisorctl tidak dikesan. Sila pergi ke halaman pustaka skrip untuk pemasangan manual.',\n            user: 'Pengguna',\n            command: 'Perintah',\n            dir: 'Direktori',\n            numprocs: 'Bil. proses',\n            initWarn:\n                'Ini akan mengubah nilai \"files\" dalam bahagian \"[include]\" dalam fail konfigurasi utama. Direktori fail konfigurasi lain akan menjadi: \"{direktori pemasangan 1Panel}/1panel/tools/supervisord/supervisor.d/\".',\n            operatorHelper: 'Operasi {1} akan dilakukan pada {0}, teruskan?',\n            uptime: 'Masa berjalan',\n            notStartWarn: 'Supervisor belum dimulakan. Mulakan dahulu.',\n            serviceName: 'Nama perkhidmatan',\n            initHelper:\n                'Perkhidmatan Supervisor dikesan tetapi belum dimulakan. Sila klik butang permulaan di bar status atas untuk konfigurasi.',\n            serviceNameHelper:\n                'Nama perkhidmatan Supervisor yang diuruskan oleh systemctl, biasanya supervisor atau supervisord',\n            restartHelper:\n                'Ini akan memulakan semula perkhidmatan selepas inisialisasi, menyebabkan semua proses daemon sedia ada berhenti.',\n            RUNNING: 'Berjalan',\n            STOPPED: 'Berhenti',\n            STOPPING: 'Sedang Berhenti',\n            STARTING: 'Sedang Bermula',\n            FATAL: 'Gagal bermula',\n            BACKOFF: 'Pengecualian permulaan',\n            ERROR: 'Ralat',\n            statusCode: 'Kod status',\n            manage: 'Pengurusan',\n            autoRestart: 'Auto Restart',\n            EXITED: 'Telah keluar',\n            autoRestartHelper:\n                'Sama ada untuk memulakan semula program secara automatik selepas ia tamat secara luar jangka',\n            autoStart: 'Mula Automatik',\n            autoStartHelper: 'Sama ada untuk memulakan perkhidmatan secara automatik selepas Supervisor mula',\n        },\n    },\n    disk: {\n        systemDisk: 'Cakera Sistem',\n        unpartitionedDisk: 'Cakera Tidak Dibahagikan',\n        handlePartition: 'Bahagikan Sekarang',\n        filesystem: 'Sistem Fail',\n        unmounted: 'Tidak Dikaitkan',\n        cannotOperate: 'Tidak Boleh Beroperasi',\n        systemDiskHelper: 'Petunjuk: Cakera semasa adalah cakera sistem, tidak boleh dioperasikan.',\n        autoMount: 'Pemasangan Automatik',\n        model: 'Model Peranti',\n        diskType: 'Jenis Cakera',\n        serial: 'Nombor Siri',\n        noFail: 'Kegagalan pemasangan tidak menjejaskan permulaan sistem',\n        management: 'Pengurusan cakera',\n        partition: 'Sekatan',\n        unmount: 'Nyahlekap',\n        unmountHelper: 'Selepas nyahlekap, data mungkin tidak boleh diakses sementara. Teruskan?',\n        mount: 'Lekap',\n        partitionAlert: 'Operasi ini memformat seluruh cakera. Pastikan data telah dibackup!',\n        mountPoint: 'Titik lekap',\n    },\n    xpack: {\n        expiresTrialAlert:\n            'Peringatan mesra: Percubaan Pro anda akan tamat dalam {0} hari, dan semua ciri Pro tidak lagi dapat diakses. Sila perbaharui atau naik taraf ke versi penuh tepat pada masanya.',\n        expiresAlert:\n            'Peringatan mesra: Lesen Pro anda akan tamat dalam {0} hari, dan semua ciri Pro tidak lagi dapat diakses. Sila perbaharui segera untuk memastikan penggunaan berterusan.',\n        menu: 'Pro',\n        upage: 'Pembina Laman Web AI',\n        proAlert: 'Tingkatkan ke Pro untuk menggunakan ciri ini',\n        app: {\n            app: 'APP',\n            title: 'Nama Panel',\n            titleHelper: 'Alias panel digunakan untuk paparan di APP (alias panel lalai)',\n            qrCode: 'Kod QR',\n            apiStatusHelper: 'APP Panel perlu mengaktifkan fungsi API',\n            apiInterfaceHelper:\n                'Menyokong akses antara muka API panel (fungsi ini perlu diaktifkan untuk aplikasi panel)',\n            apiInterfaceHelper1:\n                'Akses aplikasi panel memerlukan penambahan pelawat ke dalam senarai putih, untuk IP yang tidak tetap, disyorkan untuk menambah 0.0.0.0/0(semua IPv4), ::/0 (semua IPv6)',\n            qrCodeExpired: 'Masa penyegaran',\n            apiLeakageHelper: 'Jangan dedahkan kod QR. Pastikan ia hanya digunakan di persekitaran yang dipercayai.',\n        },\n        waf: {\n            name: 'WAF',\n            blackWhite: 'Senarai Hitam dan Putih',\n            globalSetting: 'Tetapan Global',\n            websiteSetting: 'Tetapan Laman Web',\n            blockRecords: 'Rekod Diblok',\n            world: 'Dunia',\n            china: 'China',\n            intercept: 'Sekatan',\n            request: 'Permintaan',\n            count4xx: 'Kuantiti 4xx',\n            count5xx: 'Kuantiti 5xx',\n            todayStatus: 'Status Hari Ini',\n            reqMap: 'Peta Serangan (30 Hari Lepas)',\n            resource: 'Sumber',\n            count: 'Kuantiti',\n            hight: 'Tinggi',\n            low: 'Rendah',\n            reqCount: 'Permintaan',\n            interceptCount: 'Jumlah Sekatan',\n            requestTrends: 'Tren Permintaan (7 Hari Lepas)',\n            interceptTrends: 'Tren Sekatan (7 Hari Lepas)',\n            whiteList: 'Senarai Putih',\n            blackList: 'Senarai Hitam',\n            ipBlackListHelper: 'Alamat IP dalam senarai hitam disekat daripada mengakses laman web',\n            ipWhiteListHelper: 'Alamat IP dalam senarai putih akan melepasi semua sekatan',\n            uaBlackListHelper: 'Permintaan dengan nilai User-Agent dalam senarai hitam akan disekat',\n            uaWhiteListHelper: 'Permintaan dengan nilai User-Agent dalam senarai putih akan melepasi semua sekatan',\n            urlBlackListHelper: 'Permintaan ke URL dalam senarai hitam akan disekat',\n            urlWhiteListHelper: 'Permintaan ke URL dalam senarai putih akan melepasi semua sekatan',\n            ccHelper:\n                'Jika laman web menerima lebih daripada {1} permintaan daripada IP yang sama dalam {0} saat, IP akan disekat selama {2}',\n            blockTime: 'Tempoh Sekatan',\n            attackHelper: 'Jika sekatan terkumpul melebihi {1} dalam {0} saat, IP akan disekat selama {2}',\n            notFoundHelper:\n                'Jika permintaan yang mengembalikan ralat 404 lebih daripada {1} kali dalam {0} saat, IP akan disekat selama {2}',\n            frequencyLimit: 'Had Kekerapan',\n            regionLimit: 'Had Wilayah',\n            defaultRule: 'Peraturan Lalai',\n            accessFrequencyLimit: 'Had Kekerapan Akses',\n            attackLimit: 'Had Kekerapan Serangan',\n            notFoundLimit: 'Had Kekerapan 404',\n            urlLimit: 'Had Kekerapan URL',\n            urlLimitHelper: 'Tetapkan kekerapan akses untuk URL tunggal',\n            sqliDefense: 'Perlindungan Suntikan SQL',\n            sqliHelper: 'Mengesan suntikan SQL dalam permintaan dan menyekatnya',\n            xssHelper: 'Mengesan XSS dalam permintaan dan menyekatnya',\n            xssDefense: 'Perlindungan XSS',\n            uaDefense: 'Peraturan User-Agent Berbahaya',\n            uaHelper: 'Termasuk peraturan untuk mengenal pasti bot berbahaya biasa',\n            argsDefense: 'Peraturan Parameter Berbahaya',\n            argsHelper: 'Menyekat permintaan yang mengandungi parameter berbahaya',\n            cookieDefense: 'Peraturan Cookie Berbahaya',\n            cookieHelper: 'Melarang cookie berbahaya daripada dibawa dalam permintaan',\n            headerDefense: 'Peraturan Header Berbahaya',\n            headerHelper: 'Melarang permintaan daripada mengandungi header berbahaya',\n            httpRule: 'Peraturan Kaedah Permintaan HTTP',\n            httpHelper:\n                'Tetapkan jenis kaedah yang dibenarkan untuk diakses. Jika anda ingin mengehadkan jenis akses tertentu, sila matikan jenis butang ini. Contoh: hanya akses jenis GET dibenarkan, maka anda perlu matikan butang lain selain GET',\n            geoRule: 'Sekatan Akses Wilayah',\n            geoHelper:\n                'Sekat akses ke laman web anda dari wilayah tertentu, sebagai contoh: jika akses dibenarkan dari China Tanah Besar, maka permintaan dari luar China Tanah Besar akan disekat',\n            ipLocation: 'Lokasi IP',\n            action: 'Tindakan',\n            ruleType: 'Jenis Serangan',\n            ipHelper: 'Masukkan alamat IP',\n            attackLog: 'Log Serangan',\n            rule: 'Peraturan',\n            ipArr: 'Julat IPV4',\n            ipStart: 'IP Mula',\n            ipEnd: 'IP Akhir',\n            ipv4: 'IPv4',\n            ipv6: 'IPv6',\n            urlDefense: 'Peraturan URL',\n            urlHelper: 'URL Dilarang',\n            dirFilter: 'Penapis Direktori',\n            sqlInject: 'Suntikan SQL',\n            xss: 'XSS',\n            phpExec: 'Pelaksanaan Skrip PHP',\n            oneWordTrojan: 'Trojan Satu Perkataan',\n            appFilter: 'Penapisan Direktori Berbahaya',\n            webShell: 'Webshell',\n            args: 'Parameter Berbahaya',\n            protocolFilter: 'Penapis Protokol',\n            javaFilter: 'Penapisan Fail Berbahaya Java',\n            scannerFilter: 'Penapis Pencari',\n            escapeFilter: 'Penapis Pengekalan',\n            customRule: 'Peraturan Kustom',\n            httpMethod: 'Penapis Kaedah HTTP',\n            fileExt: 'Had Muat Naik Fail',\n            fileExtHelper: 'Lanjaran fail yang dilarang untuk dimuat naik',\n            deny: 'Dilarang',\n            allow: 'Benarkan',\n            field: 'Objek',\n            pattern: 'Keadaan',\n            ruleContent: 'Kandungan',\n            contain: 'termasuk',\n            equal: 'sama dengan',\n            regex: 'peraturan biasa',\n            notEqual: 'Tidak sama dengan',\n            customRuleHelper: 'Ambil tindakan berdasarkan keadaan yang ditetapkan',\n            actionAllow: 'Benarkan',\n            blockIP: 'Sekat IP',\n            code: 'Kod Status Kembali',\n            noRes: 'Putuskan sambungan (444)',\n            badReq: 'Parameter Tidak Sah (400)',\n            forbidden: 'Akses Dilarang (403)',\n            serverErr: 'Ralat Server (500)',\n            resHtml: 'Halaman Respons',\n            allowHelper: 'Membenarkan akses akan melangkau peraturan WAF seterusnya, sila gunakan dengan berhati-hati',\n            captcha: 'verifikasi manusia-mesin',\n            fiveSeconds: 'Verifikasi 5-Saat',\n            location: 'Wilayah',\n            redisConfig: 'Konfigurasi Redis',\n            redisHelper: 'Aktifkan Redis untuk menyimpan IP yang disekat sementara',\n            wafHelper: 'Semua laman web akan kehilangan perlindungan selepas menutup',\n            attackIP: 'IP Serangan',\n            attackParam: 'Butiran Serangan',\n            execRule: 'Peraturan Dilanggar',\n            acl: 'ACL',\n            sql: 'Suntikan SQL',\n            cc: 'Had Kekerapan Akses',\n            isBlocking: 'Disekat',\n            isFree: 'Tidak Disekat',\n            unLock: 'Buka Kunci',\n            unLockHelper: 'Adakah anda mahu membuka kunci IP: {0}?',\n            saveDefault: 'Simpan Lalai',\n            saveToWebsite: 'Terapkan ke Laman Web',\n            saveToWebsiteHelper: 'Terapkan tetapan semasa ke semua laman web?',\n            websiteHelper:\n                'Ini adalah tetapan lalai untuk mencipta laman web. Pengubahsuaian perlu diterapkan ke laman web untuk berkuat kuasa',\n            websiteHelper2:\n                'Ini adalah tetapan lalai untuk mencipta laman web. Sila ubah suai konfigurasi tertentu di laman web',\n            ipGroup: 'Kumpulan IP',\n            ipGroupHelper:\n                'Satu IP atau segmen IP setiap baris, menyokong IPv4 dan IPv6, sebagai contoh: 192.168.1.1 atau 192.168.1.0/24',\n            ipBlack: 'Senarai Hitam IP',\n            openRestyAlert: 'Versi OpenResty perlu lebih tinggi daripada {0}',\n            initAlert:\n                'Penyediaan diperlukan untuk penggunaan kali pertama, fail konfigurasi laman web akan diubah, dan konfigurasi WAF asal akan hilang. Sila buat sandaran OpenResty terlebih dahulu',\n            initHelper:\n                'Operasi penyediaan akan membersihkan konfigurasi WAF yang sedia ada. Adakah anda pasti mahu menyediakan semula?',\n            mainSwitch: 'Suis Utama',\n            websiteAlert: 'Sila cipta laman web terlebih dahulu',\n            defaultUrlBlack: 'Peraturan URL',\n            htmlRes: 'Halaman Sekat',\n            urlSearchHelper: 'Sila masukkan URL untuk sokongan carian samar',\n            toCreate: 'Cipta',\n            closeWaf: 'Tutup WAF',\n            closeWafHelper: 'Menutup WAF akan menyebabkan laman web kehilangan perlindungan, adakah anda mahu teruskan',\n            addblack: 'Hitam',\n            addwhite: 'Tambah putih',\n            addblackHelper: 'Tambah IP:{0} ke senarai hitam lalai?',\n            addwhiteHelper: 'Tambah IP:{0} ke senarai putih lalai?',\n            defaultUaBlack: 'Peraturan User-Agent',\n            defaultIpBlack: 'Kumpulan IP Berbahaya',\n            cookie: 'Peraturan Cookie',\n            urlBlack: 'Senarai Hitam URL',\n            uaBlack: 'Senarai Hitam User-Agent',\n            attackCount: 'Had Kekerapan Serangan',\n            fileExtCheck: 'Had Muat Naik Fail',\n            geoRestrict: 'Sekatan Akses Wilayah',\n            attacklog: 'Rekod Sekat',\n            unknownWebsite: 'Akses domain yang tidak sah',\n            geoRuleEmpty: 'Wilayah tidak boleh kosong',\n            unknown: 'Laman Web Tidak Wujud',\n            geo: 'Sekatan Wilayah',\n            revertHtml: 'Adakah anda mahu memulihkan {0} sebagai halaman lalai?',\n            five_seconds: 'Verifikasi 5-Saat',\n            header: 'Peraturan Header',\n            methodWhite: 'Peraturan HTTP',\n            expiryDate: 'Tarikh Tamat',\n            expiryDateHelper: 'Selepas lulus verifikasi, ia tidak akan disahkan lagi dalam tempoh sah',\n            defaultIpBlackHelper: 'Beberapa IP berbahaya yang dikumpul dari Internet untuk mencegah akses',\n            notFoundCount: 'Had Kekerapan 404',\n            matchValue: 'Nilai Padanan',\n            headerName: 'Menyokong Bahasa Inggeris, nombor, -, panjang 3-30',\n            cdnHelper: 'Laman web yang menggunakan CDN boleh dibuka di sini untuk mendapatkan IP sumber yang betul',\n            clearLogWarn: 'Pembersihan log tidak akan mungkin, adakah anda mahu meneruskan?',\n            commonRuleHelper: 'Peraturan adalah padanan samar',\n            blockIPHelper:\n                'IP yang disekat disimpan sementara dalam OpenResty dan akan dibuka kunci apabila anda memulakan semula OpenResty. Mereka boleh disekat secara kekal melalui fungsi sekatan',\n            addWhiteUrlHelper: 'Tambah URL {0} ke senarai putih?',\n            dashHelper: 'Versi komuniti juga boleh menggunakan fungsi dalam tetapan global dan tetapan laman web',\n            wafStatusHelper: 'WAF tidak diaktifkan, sila aktifkan dalam tetapan global',\n            ccMode: 'Mod',\n            global: 'Mod Global',\n            uriMode: 'Mod URL',\n            globalHelper:\n                'Mod Global: Digerakkan apabila jumlah permintaan ke mana-mana URL dalam unit masa melebihi had',\n            uriModeHelper: 'Mod URL: Digerakkan apabila jumlah permintaan ke satu URL dalam unit masa melebihi had',\n            ip: 'Senarai Hitam IP',\n            globalSettingHelper:\n                'Tetapan dengan tag [Laman Web] perlu diaktifkan dalam [Tetapan Laman Web], dan tetapan global hanya tetapan lalai untuk laman web yang baru dicipta',\n            globalSettingHelper2:\n                'Tetapan perlu diaktifkan dalam kedua-dua [Tetapan Global] dan [Tetapan Laman Web] pada masa yang sama',\n            urlCCHelper: '{1} kepingatan permintaan URL ini dalam {0} saat, menyekat IP ini {2}',\n            urlCCHelper2: 'URL tidak boleh mengandung parameter',\n            notContain: 'Tidak mengandung',\n            urlcc: 'Had frekuensi URL',\n            method: 'Jenis permintaan',\n            addIpsToBlock: 'Sekat IP secara pukal',\n            addUrlsToWhite: 'Tambah URL ke senarai putih secara pukal',\n            noBlackIp: 'IP telah disekat, tidak perlu disekat semula',\n            noWhiteUrl: 'URL telah dimasukkan ke senarai putih, tidak perlu ditambah semula',\n            spiderIpHelper:\n                'Termasuk Baidu, Bing, Google, 360, Shenma, Sogou, ByteDance, DuckDuckGo, Yandex. Menutup ini akan menyekat semua akses labah-labah.',\n            spiderIp: 'Kolam IP labah-labah',\n            geoIp: 'Pustaka Alamat IP',\n            geoIpHelper: 'Digunakan untuk mengesahkan lokasi geografi IP',\n            stat: 'Laporan Serangan',\n            statTitle: 'Laporan',\n            attackIp: 'IP Serangan',\n            attackCountNum: 'Bilangan Serangan',\n            percent: 'Peratusan',\n            addblackUrlHelper: 'Adakah anda mahu menambah URL: {0} ke senarai hitam lalai?',\n            rce: 'Pelaksanaan Kod Jarak Jauh',\n            software: 'Perisian',\n            cveHelper: 'Mengandungi kelemahan biasa perisian dan rangka kerja',\n            vulnCheck: 'Peraturan Tambahan',\n            ssrf: 'Kelemahan SSRF',\n            afr: 'Pembacaan Fail Arbitrari',\n            ua: 'Akses Tanpa Kebenaran',\n            id: 'Pendedahan Maklumat',\n            aa: 'Mengelakkan Pengesahan',\n            dr: 'Penembusan Direktori',\n            xxe: 'Kelemahan XXE',\n            suid: 'Kelemahan Serialisasi',\n            dos: 'Kelemahan Penafian Perkhidmatan',\n            afd: 'Muat Turun Fail Arbitrari',\n            sqlInjection: 'Tambahan SQL',\n            afw: 'Penulisan Fail Arbitrari',\n            il: 'Pendedahan Maklumat',\n            clearAllLog: 'Kosongkan Semua Log',\n            exportLog: 'Eksport Log',\n            appRule: 'Peraturan Aplikasi',\n            appRuleHelper:\n                'Peraturan aplikasi biasa, membolehkan boleh mengurangkan positif palsu, satu laman web hanya boleh menggunakan satu peraturan',\n            logExternal: 'Kecualikan Jenis Rekod',\n            ipWhite: 'Senarai Putih IP',\n            urlWhite: 'Senarai Putih URL',\n            uaWhite: 'Senarai Putih User-Agent',\n            logExternalHelper:\n                'Jenis rekod yang dikecualikan tidak akan direkodkan dalam log, senarai hitam/putih, sekatan akses wilayah, dan peraturan tersuai akan menghasilkan banyak log, disarankan untuk mengeluarkan',\n            ssti: 'Serangan SSTI',\n            crlf: 'Penyuntikan CRLF',\n            strict: 'Mod Strict',\n            strictHelper: 'Gunakan peraturan yang lebih ketat untuk mengesahkan permintaan',\n            saveLog: 'Simpan Log',\n            remoteURLHelper: 'URL jauh perlu memastikan satu IP setiap baris dan tiada aksara lain',\n            notFound: 'Not Found (404)',\n            serviceUnavailable: 'Perkhidmatan Tidak Tersedia (503)',\n            gatewayTimeout: 'Timeout Gateway (504)',\n            belongToIpGroup: 'Tergolong dalam Kumpulan IP',\n            notBelongToIpGroup: 'Tidak tergolong dalam Kumpulan IP',\n            unknownWebsiteKey: 'Domain Tidak Diketahui',\n            special: 'Peraturan Khas',\n            fileToLarge: 'Fail melebihi 1MB dan tidak dapat dimuat naik',\n            uploadOverLimit: 'Fail yang dimuat naik melebihi had kuantiti, maksimum 1 fail',\n            importRuleHelper: 'Satu peraturan per baris',\n        },\n        monitor: {\n            '360': 'Pencarian 360',\n            name: 'Pemantauan Laman Web',\n            pv: 'Paparan Halaman',\n            uv: 'Pelawat Unik',\n            flow: 'Aliran Trafik',\n            ip: 'IP',\n            spider: 'Spider',\n            visitors: 'Tren Pelawat',\n            today: 'Hari Ini',\n            last7days: '7 Hari Terakhir',\n            last30days: '30 Hari Terakhir',\n            uvMap: 'Peta Pelawat (30th)',\n            qps: 'Permintaan Masa Sebenar (setiap minit)',\n            flowSec: 'Trafik Masa Sebenar (setiap minit)',\n            excludeCode: 'Kecualikan Kod Status',\n            excludeUrl: 'Kecualikan URL',\n            excludeExt: 'Kecualikan Sambungan',\n            cdnHelper: 'Dapatkan IP sebenar dari Header yang disediakan oleh CDN',\n            reqRank: 'Peringkat Lawatan',\n            refererDomain: 'Domain Rujukan',\n            os: 'Sistem',\n            browser: 'Pelayar/Klien',\n            device: 'Peranti',\n            showMore: 'Lebih',\n            unknown: 'Lain-lain',\n            pc: 'Komputer',\n            mobile: 'Peranti Mudah Alih',\n            wechat: 'WeChat',\n            machine: 'Mesin',\n            tencent: 'Pelayar Tencent',\n            ucweb: 'Pelayar UC',\n            '2345explorer': 'Pelayar 2345',\n            huaweibrowser: 'Pelayar Huawei',\n            log: 'Log Permintaan',\n            statusCode: 'Kod Status',\n            requestTime: 'Masa Respons',\n            flowRes: 'Trafik Respons',\n            method: 'Kaedah Permintaan',\n            statusCodeHelper: 'Masukkan kod status di atas',\n            statusCodeError: 'Jenis kod status tidak sah',\n            methodHelper: 'Masukkan kaedah permintaan di atas',\n            all: 'Semua',\n            baidu: 'Baidu',\n            google: 'Google',\n            bing: 'Bing',\n            bytes: 'Tajuk Hari Ini',\n            sogou: 'Sogou',\n            failed: 'Ralat',\n            ipCount: 'Kiraan IP',\n            spiderCount: 'Permintaan Spider',\n            averageReqTime: 'Masa Respons Purata',\n            totalFlow: 'Jumlah Trafik',\n            logSize: 'Saiz Fail Log',\n            realIPType: 'Kaedah pemerolehan IP sebenar',\n            fromHeader: 'Dapatkan dari Header HTTP',\n            fromHeaders: 'Dapatkan dari senarai Header',\n            header: 'Header HTTP',\n            cdnConfig: 'Konfigurasi CDN',\n            xff1: 'Proksi Tahap Pertama dari X-Forwarded-For',\n            xff2: 'Proksi Tahap Kedua dari X-Forwarded-For',\n            xff3: 'Proksi Tahap Ketiga dari X-Forwarded-For',\n            xffHelper:\n                'Contoh: X-Forwarded-For: <client>,<proxy1>,<proxy2>,<proxy3> Proksi tahap atas akan mengambil IP terakhir <proxy3>',\n            headersHelper:\n                'Dapatkan IP sebenar dari header CDN HTTP yang biasa digunakan, memilih nilai pertama yang tersedia',\n            monitorCDNHelper:\n                'Mengubah konfigurasi CDN untuk pemantauan laman web juga akan mengemas kini tetapan WAF CDN',\n            wafCDNHelper: 'Mengubah konfigurasi WAF CDN juga akan mengemas kini tetapan CDN pemantauan laman web',\n            statusErr: 'Format kod status tidak sah',\n            shenma: 'Shenma Search',\n            duckduckgo: 'DuckDuckGo',\n            excludeUri: 'Kecualikan URI',\n            top100Helper: 'Tunjukkan data 100 teratas',\n            logSaveDay: 'Tempoh Penahanan Log (hari)',\n            cros: 'Chrome OS',\n            theworld: 'Pelayar TheWorld',\n            edge: 'Microsoft Edge',\n            maxthon: 'Pelayar Maxthon',\n            monitorStatusHelper: 'Pemantauan tidak diaktifkan, sila aktifkan dalam tetapan',\n            excludeIp: 'Kecualikan Alamat IP',\n            excludeUa: 'Kecualikan User-Agent',\n            remotePort: 'Port Jauh',\n            unknown_browser: 'Tidak Diketahui',\n            unknown_os: 'Tidak Diketahui',\n            unknown_device: 'Tidak Diketahui',\n            logSaveSize: 'Saiz Simpanan Log Maksimum',\n            logSaveSizeHelper: 'Ini adalah saiz simpanan log untuk satu laman web',\n            '360se': '360 Pelayar Keselamatan',\n            websites: 'Senarai Laman Web',\n            trend: 'Statistik Trend',\n            reqCount: 'Jumlah Permintaan',\n            uriHelper: 'Anda boleh menggunakan /test/* atau /*/index.php untuk mengecualikan Uri',\n        },\n        tamper: {\n            tamper: 'Perlindungan daripada peng篡改 laman web',\n            ignoreTemplate: 'Templat Pengecualian',\n            protectTemplate: 'Templat Perlindungan',\n            ignoreTemplateHelper:\n                'Sila masukkan kandungan pengecualian, dipisahkan oleh Enter atau ruang. (Direktori khusus ./log atau nama direktori tmp, untuk mengecualikan fail perlu memasukkan fail khusus ./data/test.html)',\n            protectTemplateHelper:\n                'Sila masukkan kandungan perlindungan, dipisahkan oleh Enter atau ruang. (Fail khusus ./index.html, sambungan fail .html, jenis fail js, untuk melindungi direktori perlu memasukkan direktori khusus ./log)',\n            templateContent: 'Kandungan Templat',\n            template: 'Templat',\n            saveTemplate: 'Simpan sebagai Templat',\n            tamperHelper1:\n                'Untuk laman web jenis penyebaran satu klik, disyorkan untuk mengaktifkan fungsi anti-pengubahsuaian direktori aplikasi; jika laman web tidak dapat digunakan secara normal atau sandaran/pemulihan gagal, sila lumpuhkan fungsi anti-pengubahsuaian terlebih dahulu;',\n            tamperHelper2:\n                'Akan menyekat operasi baca/tulis, padam, keizinan, dan pengubahsuaian pemilik untuk fail dilindungi dalam direktori tidak dikecualikan',\n            tamperPath: 'Direktori Perlindungan',\n            tamperPathEdit: 'Ubah Laluan',\n            log: 'Log Sekatan',\n            totalProtect: 'Perlindungan Total',\n            todayProtect: 'Perlindungan Hari Ini',\n            templateRule: 'Panjang 1-512, nama tidak boleh mengandungi {0} dan simbol lain',\n            ignore: 'Kecualikan',\n            ignoreHelper:\n                'Sila pilih atau masukkan kandungan pengecualian, dipisahkan oleh Enter atau ruang. (Direktori khusus ./log atau nama direktori tmp, untuk mengecualikan fail perlu memasukkan atau memilih fail khusus ./data/test.html)',\n            protect: 'Lindungi',\n            protectHelper:\n                'Sila pilih atau masukkan kandungan perlindungan, dipisahkan oleh Enter atau ruang. (Fail khusus ./index.html, sambungan fail .html, jenis fail js, untuk melindungi direktori perlu memasukkan atau memilih direktori khusus ./log)',\n            tamperHelper00: 'Pengecualian dan perlindungan hanya menyokong laluan relatif;',\n            tamperHelper01:\n                'Selepas mengaktifkan perlindungan anti-pengubahsuaian, sistem akan menyekat operasi penciptaan, penyuntingan, dan pemadaman fail dilindungi dalam direktori tidak dikecualikan;',\n            tamperHelper02:\n                'Keutamaan: Perlindungan laluan khusus > Pengecualian laluan khusus > Perlindungan > Pengecualian',\n            tamperHelper03:\n                'Operasi pemantauan hanya mensasarkan direktori tidak dikecualikan, memantau penciptaan fail tidak dilindungi dalam direktori ini.',\n            disableHelper: 'Akan melumpuhkan fungsi anti-pengubahsuaian untuk laman web berikut, teruskan?',\n            appendOnly: 'Hanya Tambahan',\n            appendOnlyHelper:\n                'Menyekat operasi pemadaman fail dalam direktori ini, hanya membenarkan penambahan direktori yang dikecualikan atau fail tidak dilindungi',\n            immutable: 'Tidak Berubah',\n            immutableHelper:\n                'Menyekat operasi suntingan, pemadaman, keizinan, dan pengubahsuaian pemilik untuk fail ini',\n            onWatch: 'Pantau',\n            onWatchHelper:\n                'Memantau dan mencegat penciptaan fail dilindungi atau direktori tidak dikecualikan dalam direktori ini',\n            forceStop: 'Tutup Paksa',\n            forceStopHelper:\n                'Akan melumpuhkan fungsi anti-pengubahsuaian untuk direktori laman web ini secara paksa. Teruskan?',\n        },\n        setting: {\n            setting: 'Tetapan Panel',\n            title: 'Deskripsi Panel',\n            titleHelper:\n                'Akan dipaparkan pada halaman log masuk pengguna (contoh: Panel pengurusan operasi dan penyelenggaraan pelayan Linux, disarankan 8-15 aksara)',\n            logo: 'Logo (Tanpa Teks)',\n            logoHelper:\n                'Akan dipaparkan di sudut kiri atas halaman pengurusan apabila menu dikurangkan (saiz imej yang disarankan: 82px*82px)',\n            logoWithText: 'Logo (Dengan Teks)',\n            logoWithTextHelper:\n                'Akan dipaparkan di sudut kiri atas halaman pengurusan apabila menu diperluaskan (saiz imej yang disarankan: 185px*55px)',\n            favicon: 'Ikon Laman Web',\n            faviconHelper: 'Ikon laman web (saiz imej yang disarankan: 16px*16px)',\n            setDefault: 'Pulihkan Tetapan Asal',\n            setHelper: 'Tetapan semasa akan disimpan. Adakah anda ingin meneruskan?',\n            setDefaultHelper: 'Semua tetapan panel akan dikembalikan ke asal. Adakah anda ingin meneruskan?',\n            logoGroup: 'Logo',\n            imageGroup: 'Imej',\n            loginImage: 'Imej Halaman Log Masuk',\n            loginImageHelper: 'Akan dipaparkan di halaman log masuk (Saiz disyorkan: 500*416px)',\n            loginBgType: 'Jenis Latar Halaman Log Masuk',\n            loginBgImage: 'Imej Latar Halaman Log Masuk',\n            loginBgImageHelper:\n                'Akan dipaparkan sebagai latar belakang halaman log masuk (Saiz disyorkan: 1920*1080px)',\n            loginBgColor: 'Warna Latar Halaman Log Masuk',\n            loginBgColorHelper: 'Akan dipaparkan sebagai warna latar belakang halaman log masuk',\n            image: 'Imej',\n            bgColor: 'Warna Latar',\n            loginGroup: 'Halaman Log Masuk',\n            loginBtnLinkColor: 'Warna Butang/Pautan',\n            loginBtnLinkColorHelper: 'Akan dipaparkan sebagai warna butang/pautan di halaman log masuk',\n        },\n        helper: {\n            wafTitle1: 'Peta Pencegahan',\n            wafContent1: 'Memaparkan taburan geografi pencegahan dalam tempoh 30 hari yang lalu',\n            wafTitle2: 'Sekatan Akses Wilayah',\n            wafContent2: 'Menyekat sumber akses laman web mengikut lokasi geografi',\n            wafTitle3: 'Halaman Pencegahan Tersuai',\n            wafContent3: 'Cipta halaman tersuai untuk dipaparkan selepas permintaan disekat',\n            wafTitle4: 'Peraturan Tersuai (ACL)',\n            wafContent4: 'Sekat permintaan mengikut peraturan tersuai',\n            tamperTitle1: 'Pemantauan Integriti Fail',\n            tamperContent1: 'Pantau integriti fail laman web, termasuk fail teras, skrip, dan fail konfigurasi.',\n            tamperTitle2: 'Pengimbasan dan Pengesanan Masa Nyata',\n            tamperContent2:\n                'Kesan fail yang tidak normal atau diubah suai dengan mengimbas sistem fail laman web secara masa nyata.',\n            tamperTitle3: 'Tetapan Kebenaran Keselamatan',\n            tamperContent3:\n                'Hadkan akses ke fail laman web melalui tetapan kebenaran yang sesuai dan dasar kawalan akses, mengurangkan permukaan serangan yang berpotensi.',\n            tamperTitle4: 'Log dan Analisis',\n            tamperContent4:\n                'Rekod log akses dan operasi fail untuk audit dan analisis selanjutnya oleh pentadbir serta mengenal pasti potensi ancaman keselamatan.',\n            settingTitle1: 'Mesej Selamat Datang Tersuai',\n            settingContent1: 'Tetapkan mesej selamat datang tersuai pada halaman log masuk 1Panel.',\n            settingTitle2: 'Logo Tersuai',\n            settingContent2: 'Benarkan memuat naik gambar logo yang mengandungi nama jenama atau teks lain.',\n            settingTitle3: 'Ikon Laman Web Tersuai',\n            settingContent3:\n                'Benarkan memuat naik ikon tersuai untuk menggantikan ikon pelayar lalai, meningkatkan pengalaman pengguna.',\n            monitorTitle1: 'Trend Pelawat',\n            monitorContent1: 'Statistik dan memaparkan trend pelawat laman web',\n            monitorTitle2: 'Peta Pelawat',\n            monitorContent2: 'Statistik dan memaparkan taburan geografi pelawat laman web',\n            monitorTitle3: 'Statistik Akses',\n            monitorContent3:\n                'Statistik maklumat permintaan laman web, termasuk labah-labah, peranti akses, status permintaan, dan sebagainya.',\n            monitorTitle4: 'Pemantauan Masa Nyata',\n            monitorContent4:\n                'Pemantauan masa nyata maklumat permintaan laman web, termasuk bilangan permintaan, trafik, dan sebagainya.',\n            alertTitle1: 'Amaran SMS',\n            alertContent1:\n                'Apabila berlaku penggunaan sumber pelayan yang tidak normal, tamat tempoh laman web dan sijil, kemas kini versi baru, tamat tempoh kata laluan, dan sebagainya, pengguna akan diberitahu melalui amaran SMS untuk memastikan pemprosesan tepat pada masanya.',\n            alertTitle2: 'Log Amaran',\n            alertContent2:\n                'Memberikan fungsi kepada pengguna untuk melihat log amaran bagi memudahkan penjejakan dan analisis peristiwa amaran sejarah.',\n            alertTitle3: 'Tetapan Amaran',\n            alertContent3:\n                'Memberikan pengguna konfigurasi nombor telefon tersuai, kekerapan push harian, dan masa push harian, memudahkan pengguna untuk menetapkan amaran push yang lebih munasabah.',\n            nodeDashTitle1: 'Pengurusan Aplikasi',\n            nodeDashContent1:\n                'Pengurusan bersatu aplikasi berbilang nod, menyokong pemantauan status, mula/henti pantas, sambungan terminal dan sandaran',\n            nodeDashTitle2: 'Pengurusan Laman Web',\n            nodeDashContent2:\n                'Pengurusan bersatu laman web berbilang nod, pemantauan status masa nyata, menyokong mula/henti kelompok dan sandaran pantas',\n            nodeDashTitle3: 'Pengurusan Pangkalan Data',\n            nodeDashContent3:\n                'Pengurusan bersatu pangkalan data berbilang nod, status utama sekilas, menyokong sandaran satu klik',\n            nodeDashTitle4: 'Pengurusan Tugas Berjadual',\n            nodeDashContent4:\n                'Pengurusan bersatu tugas berjadual berbilang nod, menyokong pemantauan status, mula/henti pantas dan pelaksanaan pencetus manual',\n            nodeTitle1: 'Satu Klik Tambah Node',\n            nodeContent1: 'Mengintegrasikan pelbagai nod pelayan dengan cepat',\n            nodeTitle2: 'Kelompok Naik Taraf',\n            nodeContent2: 'Segerakan dan naik taraf semua nod dengan satu operasi',\n            nodeTitle3: 'Pemantauan Status Node',\n            nodeContent3: 'Memantau status operasi setiap nod secara real-time',\n            nodeTitle4: 'Sambungan Jauh Pantas',\n            nodeContent4: 'Sambung terus ke terminal jauh nod dengan satu klik',\n            fileExchangeTitle1: 'Pengangkutan Pengesahan Kunci',\n            fileExchangeContent1: 'Mengesahkan melalui kunci SSH untuk memastikan keselamatan pengangkutan.',\n            fileExchangeTitle2: 'Segerakan Fail Cekap',\n            fileExchangeContent2:\n                'Hanya menyegerakkan kandungan yang berubah untuk meningkatkan kelajuan dan kestabilan pengangkutan secara signifikan.',\n            fileExchangeTitle3: 'Sokongan Pertukaran Inter-Node',\n            fileExchangeContent3:\n                'Mudah memindahkan fail projek antara nod yang berbeza, mengurus pelbagai pelayan dengan fleksibiliti.',\n            nodeAppTitle1: 'Pengurusan Naik Taraf Aplikasi',\n            nodeAppContent1: 'Pemantauan bersatu kemas kini aplikasi berbilang nod, menyokong naik taraf satu klik',\n            appTitle1: 'Pengurusan Panel yang Fleksibel',\n            appContent1: 'Uruskan pelayan 1Panel anda dengan mudah pada bila-bila masa dan di mana sahaja.',\n            appTitle2: 'Maklumat Perkhidmatan Komprehensif',\n            appContent2:\n                'Uruskan aplikasi, laman web, Docker, pangkalan data, dll melalui aplikasi mudah alih dan sokong pembinaan pantas aplikasi dan laman web.',\n            appTitle3: 'Pemantauan Anomali Masa Nyata',\n            appContent3:\n                'Lihat status pelayan, pemantauan keselamatan WAF, statistik pelawat laman web dan status kesihatan proses di aplikasi mudah alih secara masa nyata.',\n            clusterTitle1: 'Penyebaran Utama-Hamba',\n            clusterContent1:\n                'Menyokong penciptaan contoh utama-hamba MySQL/Postgres/Redis pada nod yang berbeza, secara automatik melengkapkan perhubungan utama-hamba dan permulaan',\n            clusterTitle2: 'Pengurusan Utama-Hamba',\n            clusterContent2:\n                'Halaman terpadu untuk menguruskan pelbagai nod utama-hamba, lihat peranan, status berjalan, dsb.',\n            clusterTitle3: 'Status Replikasi',\n            clusterContent3:\n                'Memaparkan status replikasi utama-hamba dan maklumat kelewatan, membantu menyelesaikan masalah sinkronisasi',\n        },\n        node: {\n            master: 'Nod Utama',\n            masterBackup: 'Sandaran Nod Master',\n            backupNode: 'Nod Sandaran',\n            backupFrequency: 'Kekerapan Sandaran (jam)',\n            backupCopies: 'Salinan Sandaran',\n            noBackupNode: 'Nod sandaran kosong. Sila pilih nod sandaran untuk disimpan dan cuba lagi!',\n            masterBackupAlert:\n                'Sandaran nod master belum dikonfigurasikan. Untuk memastikan keselamatan data, sila sediakan nod sandaran secepat mungkin untuk memudahkan pertukaran manual ke nod master baru sekiranya berlaku kegagalan.',\n            node: 'Nod',\n            addr: 'Alamat',\n            nodeUpgrade: 'Tetapan Kemas Kini',\n            nodeUpgradeHelper:\n                'Nod terpilih akan mula dinaik taraf secara automatik selepas naik taraf nod induk selesai, tiada operasi manual diperlukan.',\n            nodeUnhealthy: 'Status nod tidak normal',\n            deletedNode: 'Nod {0} yang telah dipadam tidak menyokong operasi naik taraf buat masa ini!',\n            nodeUnhealthyHelper: 'Status nod tidak normal dikesan. Sila semak dalam [Pengurusan Nod] dan cuba lagi!',\n            nodeUnbind: 'Nod tidak terikat pada lesen',\n            nodeUnbindHelper:\n                'Terdeteksi nod ini tidak terikat pada lesen. Sila ikat dalam menu [Tetapan Panel - Lesen] dan cuba lagi!',\n            memTotal: 'Jumlah Memori',\n            nodeManagement: 'Pengurusan Multi-Mesin',\n            nodeItem: 'Pengurusan Nod',\n            panelItem: 'Pengurusan Panel',\n            addPanel: 'Tambah Panel',\n            addPanelHelper:\n                'Selepas berjaya menambah panel, anda boleh mengakses panel sasaran dengan pantas di [Gambaran Keseluruhan - Panel].',\n            panel: 'Panel 1Panel',\n            others: 'Panel Lain',\n            addNode: 'Tambah Nod',\n            connInfo: 'Maklumat Sambungan',\n            nodeInfo: 'Maklumat Nod',\n            withProxy: 'Dayakan Akses Proksi',\n            withoutProxy: 'Lumpuhkan Akses Proksi',\n            withProxyHelper:\n                'Akan menggunakan proksi sistem {0} yang dikekalkan dalam tetapan panel untuk mengakses nod anak. Teruskan?',\n            withoutProxyHelper:\n                'Akan berhenti menggunakan proksi sistem yang dikekalkan dalam tetapan panel untuk mengakses nod anak. Teruskan?',\n            syncInfo: 'Penyegerakan data,',\n            syncHelper: 'Apabila data nod induk berubah, ia akan disegerakkan ke nod anak ini secara masa nyata,',\n            syncBackupAccount: 'Tetapan akaun sandaran',\n            syncWithMaster:\n                'Selepas menaik taraf ke Pro, semua data akan diselaraskan secara lalai. Dasar penyelarasan boleh disesuaikan secara manual dalam pengurusan nod.',\n            syncProxy: 'Tetapan proksi sistem',\n            syncProxyHelper: 'Penyelarasan tetapan proksi sistem memerlukan mulakan semula Docker',\n            syncProxyHelper1: 'Memulakan semula Docker mungkin menjejaskan perkhidmatan kontena yang sedang berjalan.',\n            syncProxyHelper2: 'Anda boleh mulakan semula secara manual di halaman Kontena - Konfigurasi.',\n            syncProxyHelper3:\n                'Penyelarasan tetapan proksi sistem memerlukan mulakan semula Docker, yang mungkin menjejaskan perkhidmatan kontena yang sedang berjalan',\n            syncProxyHelper4:\n                'Penyelarasan tetapan proksi sistem memerlukan mulakan semula Docker. Anda boleh mulakan semula secara manual di halaman Kontena - Konfigurasi nanti.',\n            syncCustomApp: 'Segerakan Repositori Aplikasi Tersuai',\n            syncAlertSetting: 'Tetapan amaran sistem',\n            syncNodeInfo: 'Data asas nod,',\n            nodeSyncHelper: 'Penyelarasan maklumat nod akan menyelaraskan maklumat berikut:',\n            nodeSyncHelper1: '1. Maklumat akaun sandaran awam',\n            nodeSyncHelper2: '2. Maklumat sambungan antara nod utama dan nod sub',\n            nodeCheck: 'Semakan ketersediaan',\n            checkSSH: 'Periksa sambungan SSH nod',\n            checkUserPermission: 'Semak kebenaran pengguna nod',\n            isNotRoot: 'Dikesan sudo tanpa kata laluan tidak disokong pada nod ini dan pengguna semasa bukan root',\n            checkLicense: 'Periksa status lesen nod',\n            checkService: 'Periksa maklumat perkhidmatan sedia ada pada nod',\n            checkPort: 'Periksa kebolehcapaian port nod',\n            panelExist:\n                'Mengesan nod ini sedang menjalankan perkhidmatan 1Panel V1. Sila naik taraf ke V2 menggunakan skrip migrasi sebelum menambah.',\n            coreExist:\n                'Nod semasa telah didayakan sebagai nod induk dan tidak boleh ditambah terus sebagai nod hamba. Sila turun taraf kepada nod hamba terlebih dahulu sebelum menambah, rujuk dokumentasi untuk butiran.',\n            agentExist:\n                'Mengesan 1panel-agent telah dipasang pada nod ini. Penerusan akan mengekalkan data sedia ada dan hanya menggantikan perkhidmatan 1panel-agent.',\n            agentNotExist:\n                'Terdapat pengesahan bahawa 1panel-agent tidak dipasang pada nod ini, maklumat nod tidak boleh disunting secara langsung. Sila padam dan tambah semula.',\n            oldDataExist:\n                'Mengesan data sejarah 1Panel V2 pada nod ini. Maklumat berikut akan digunakan untuk menimpa tetapan semasa:',\n            errLicense: 'Lesen yang terikat pada nod ini tidak tersedia. Sila semak dan cuba lagi!',\n            errNodePort:\n                'Port nod [ {0} ] dikesan tidak boleh diakses. Sila semak sama ada firewall atau kumpulan keselamatan telah membenarkan port ini.',\n            reinstallHelper: 'Pasang semula nod {0}, adakah anda ingin meneruskan?',\n            unhealthyCheck: 'Pemeriksaan Tidak Normal',\n            fixOperation: 'Operasi Pembetulan',\n            checkName: 'Item Pemeriksaan',\n            checkSSHConn: 'Periksa Ketersediaan Sambungan SSH',\n            fixSSHConn: 'Edit nod secara manual untuk mengesahkan maklumat sambungan',\n            checkConnInfo: 'Periksa Maklumat Sambungan Ejen',\n            checkStatus: 'Periksa Ketersediaan Perkhidmatan Nod',\n            fixStatus:\n                'Jalankan \"systemctl status 1panel-agent.service\" untuk memeriksa sama ada perkhidmatan sedang berjalan.',\n            checkAPI: 'Periksa Ketersediaan API Nod',\n            fixAPI: 'Semak log nod dan periksa sama ada port firewall dibuka dengan betul.',\n            forceDelete: 'Hapus Secara Paksa',\n            operateHelper: 'Operasi {0} akan dilakukan pada nod berikut, adakah anda ingin meneruskan?',\n            operatePanelHelper: 'Operasi {0} akan dilakukan pada panel berikut. Teruskan?',\n            forceDeleteHelper:\n                'Hapus secara paksa akan mengabaikan ralat penghapusan nod dan menghapus metadata pangkalan data',\n            uninstall: 'Padam data nod',\n            uninstallHelper: 'Ini akan memadamkan semua data berkaitan 1Panel bagi nod. Pilih dengan berhati-hati!',\n            baseDir: 'Direktori Pemasangan',\n            baseDirHelper: 'Apabila direktori pemasangan kosong, secara lalai akan dipasang di direktori /opt',\n            nodePort: 'Port Nod',\n            offline: 'Mod luar talian',\n            freeCount: 'Had percuma [{0}]',\n            offlineHelper: 'Digunakan apabila nod berada dalam persekitaran luar talian',\n            appUpgrade: 'Naik Taraf Apl',\n            appUpgradeHelper: 'Terdapat {0} apl yang perlu dinaik taraf',\n        },\n        customApp: {\n            name: 'Repositori Aplikasi Khusus',\n            appStoreType: 'Sumber Pakej App Store',\n            appStoreUrl: 'URL Repositori',\n            local: 'Laluan Tempatan',\n            remote: 'Pautan Jauh',\n            imagePrefix: 'Awalan Imej',\n            imagePrefixHelper:\n                'Fungsi: Sesuaikan awalan imej dan ubah medan imej dalam fail compose. Contohnya, apabila awalan imej ditetapkan kepada 1panel/custom, medan imej untuk MaxKB akan bertukar kepada 1panel/custom/maxkb:v1.10.0',\n            closeHelper: 'Batalkan penggunaan repositori aplikasi khusus',\n            appStoreUrlHelper: 'Hanya format .tar.gz disokong',\n            postNode: 'Segerakkan ke sub-node',\n            postNodeHelper:\n                'Segerakan pakej kedai tersuai ke tmp/customApp/apps.tar.gz dalam direktori pemasangan nod anak',\n            nodes: 'Pilih Node',\n            selectNode: 'Pilih Node',\n            selectNodeError: 'Sila pilih node',\n            licenseHelper: 'Versi Pro menyokong fungsi gudang aplikasi tersuai',\n            databaseHelper: 'Pangkalan data berkaitan aplikasi, sila pilih pangkalan data nod sasaran',\n            nodeHelper: 'Tidak boleh memilih nod semasa',\n            migrateHelper:\n                'Kini hanya menyokong penghijrahan aplikasi monolitik dan aplikasi yang hanya dikaitkan dengan pangkalan data MySQL, MariaDB, PostgreSQL',\n            opensslHelper:\n                'Jika menggunakan sandaran terenkripsi, versi OpenSSL antara dua nod mesti konsisten, jika tidak penghijrahan mungkin gagal.',\n            installApp: 'Pemasangan kelompok',\n            installAppHelper: 'Pasang aplikasi secara kelompok ke nod yang dipilih',\n        },\n        alert: {\n            isAlert: 'Amaran',\n            alertCount: 'Bilangan Amaran',\n            clamHelper: 'Hantar amaran apabila terdapat fail yang dijangkiti semasa imbasan',\n            cronJobHelper: 'Hantar amaran apabila pelaksanaan tugas gagal',\n            licenseHelper: 'Versi profesional menyokong amaran',\n            alertCountHelper: 'Kekerapan maksimum amaran harian',\n            alert: 'Amaran SMS',\n            logs: 'Log Amaran',\n            list: 'Senarai Amaran',\n            addTask: 'Cipta Amaran',\n            editTask: 'Edit Amaran',\n            alertMethod: 'Kaedah',\n            alertMsg: 'Mesej Amaran',\n            alertRule: 'Peraturan Amaran',\n            titleSearchHelper: 'Masukkan tajuk amaran untuk pencarian kabur',\n            taskType: 'Jenis',\n            ssl: 'Sijil Tamat Tempoh',\n            siteEndTime: 'Tamat Tempoh Laman Web',\n            panelPwdEndTime: 'Kata Laluan Panel Tamat Tempoh',\n            panelUpdate: 'Versi Panel Baharu Tersedia',\n            cpu: 'Amaran CPU Pelayan',\n            memory: 'Amaran Memori Pelayan',\n            load: 'Amaran Beban Pelayan',\n            disk: 'Amaran Cakera Pelayan',\n            website: 'Laman Web',\n            certificate: 'Sijil SSL',\n            remainingDays: 'Hari yang Tinggal',\n            sendCount: 'Bilangan Hantar',\n            sms: 'SMS',\n            wechat: 'WeChat',\n            dingTalk: 'DingTalk',\n            feiShu: 'FeiShu',\n            mail: 'E-mel',\n            weCom: 'WeCom',\n            sendCountRulesHelper: 'Jumlah amaran dihantar sebelum tamat tempoh (sekali sehari)',\n            panelUpdateRulesHelper: 'Jumlah amaran dihantar untuk versi panel baharu (sekali sehari)',\n            oneDaySendCountRulesHelper: 'Maksimum amaran dihantar setiap hari',\n            siteEndTimeRulesHelper: 'Laman web yang tidak pernah tamat tempoh tidak akan mencetuskan amaran',\n            autoRenewRulesHelper:\n                'Sijil dengan pembaharuan automatik diaktifkan dan baki hari kurang daripada 31 tidak akan mencetuskan amaran',\n            panelPwdEndTimeRulesHelper:\n                'Amaran tamat tempoh kata laluan panel tidak tersedia jika tiada tempoh tamat ditetapkan',\n            sslRulesHelper: 'Semua Sijil SSL',\n            diskInfo: 'Cakera',\n            monitoringType: 'Jenis Pemantauan',\n            autoRenew: 'Pembaharuan Automatik',\n            useDisk: 'Penggunaan Cakera',\n            usePercentage: 'Peratusan Penggunaan',\n            changeStatus: 'Tukar Status',\n            disableMsg:\n                'Menghentikan tugas amaran akan menghalang tugas ini daripada menghantar mesej amaran. Adakah anda ingin meneruskan?',\n            enableMsg:\n                'Mengaktifkan tugas amaran akan membolehkan tugas ini menghantar mesej amaran. Adakah anda ingin meneruskan?',\n            useExceed: 'Penggunaan Melebihi',\n            useExceedRulesHelper: 'Cetuskan amaran apabila penggunaan melebihi nilai yang ditetapkan',\n            cpuUseExceedAvg: 'Penggunaan CPU purata melebihi nilai yang ditetapkan',\n            memoryUseExceedAvg: 'Penggunaan memori purata melebihi nilai yang ditetapkan',\n            loadUseExceedAvg: 'Penggunaan beban purata melebihi nilai yang ditetapkan',\n            cpuUseExceedAvgHelper: 'Penggunaan CPU purata dalam masa tertentu melebihi nilai yang ditetapkan',\n            memoryUseExceedAvgHelper: 'Penggunaan memori purata dalam masa tertentu melebihi nilai yang ditetapkan',\n            loadUseExceedAvgHelper: 'Penggunaan beban purata dalam masa tertentu melebihi nilai yang ditetapkan',\n            resourceAlertRulesHelper: 'Nota: Amaran berterusan dalam masa 30 minit hanya akan menghantar satu',\n            specifiedTime: 'Masa Tertentu',\n            deleteTitle: 'Padam Amaran',\n            deleteMsg: 'Adakah anda pasti ingin memadam tugas amaran?',\n            allSslTitle: 'Semua Amaran Tamat Tempoh Sijil SSL Laman Web',\n            sslTitle: 'Amaran Tamat Tempoh Sijil SSL untuk Laman Web {0}',\n            allSiteEndTimeTitle: 'Semua Amaran Tamat Tempoh Laman Web',\n            siteEndTimeTitle: 'Amaran Tamat Tempoh Laman Web {0}',\n            panelPwdEndTimeTitle: 'Amaran Tamat Tempoh Kata Laluan Panel',\n            panelUpdateTitle: 'Pemberitahuan Versi Panel Baharu',\n            cpuTitle: 'Amaran Penggunaan CPU Tinggi',\n            memoryTitle: 'Amaran Penggunaan Memori Tinggi',\n            loadTitle: 'Amaran Beban Tinggi',\n            diskTitle: 'Amaran Penggunaan Cakera Tinggi untuk Direktori {0}',\n            allDiskTitle: 'Amaran Penggunaan Cakera Tinggi',\n            timeRule: 'Masa baki kurang daripada {0} hari (jika tidak diurus, akan dihantar semula keesokan hari)',\n            panelUpdateRule:\n                'Hantar satu amaran apabila versi panel baharu dikesan (jika tidak diurus, akan dihantar semula keesokan hari)',\n            avgRule:\n                'Purata penggunaan {1} melebihi {2}% dalam {0} minit, mencetuskan amaran, dihantar {3} kali sehari',\n            diskRule:\n                'Penggunaan cakera untuk direktori {0} melebihi {1}{2}, mencetuskan amaran, dihantar {3} kali sehari',\n            allDiskRule: 'Penggunaan cakera melebihi {0}{1}, mencetuskan amaran, dihantar {2} kali sehari',\n            cpuName: ' CPU ',\n            memoryName: 'Memori',\n            loadName: 'Beban',\n            diskName: 'Cakera',\n            syncAlertInfo: 'Tekan Manual',\n            syncAlertInfoMsg: 'Adakah anda ingin menekan tugas amaran secara manual?',\n            pushError: 'Tekanan Gagal',\n            pushSuccess: 'Tekanan Berjaya',\n            syncError: 'Penyelarasan Gagal',\n            success: 'Amaran Berjaya',\n            pushing: 'Sedang menghantar...',\n            error: 'Amaran gagal',\n            cleanLog: 'Bersihkan Log',\n            cleanAlertLogs: 'Bersihkan Log Amaran',\n            daily: 'Bilangan Amaran Harian: {0}',\n            cumulative: 'Bilangan Amaran Kumulatif: {0}',\n            clams: 'Imbasan Virus',\n            taskName: 'Nama Tugas',\n            cronJobType: 'Jenis Tugas',\n            clamPath: 'Direktori Imbasan',\n            cronjob: 'Pelaksanaan tugas berjadual {0} gagal',\n            app: 'Sandaran Aplikasi',\n            web: 'Sandaran Laman Web',\n            database: 'Sandaran Pangkalan Data',\n            directory: 'Sandaran Direktori',\n            log: 'Log Sandaran',\n            snapshot: 'Snapshot Sistem',\n            clamsRulesHelper: 'Tugas imbasan virus yang memerlukan amaran',\n            cronJobRulesHelper: 'Jenis tugas berjadual ini perlu dikonfigurasikan',\n            clamsTitle: 'Tugas imbasan virus 「 {0} 」 mengesan amaran fail dijangkiti',\n            cronJobAppTitle: 'Cronjob - Sandaran Aplikasi 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobWebsiteTitle: 'Cronjob - Sandaran Laman Web 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobDatabaseTitle: 'Cronjob - Sandaran Pangkalan Data 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobDirectoryTitle: 'Cronjob - Sandaran Direktori 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobLogTitle: 'Cronjob - Sandaran Log 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobSnapshotTitle: 'Cronjob - Sandaran Snapshot 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobShellTitle: 'Cronjob - Skrip Shell 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobCurlTitle: 'Cronjob - Akses URL 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobCutWebsiteLogTitle: 'Cronjob - Potong log laman web 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobCleanTitle: 'Cronjob - Pembersihan Cache 「 {0} 」 Amaran Kegagalan Tugas',\n            cronJobNtpTitle: 'Cronjob - Penyelarasan masa pelayan 「 {0} 」 Amaran Kegagalan Tugas',\n            clamsRule: 'Imbasan virus mengesan fail dijangkiti, dihantar {0} kali sehari',\n            cronJobAppRule: 'Amaran kegagalan tugas sandaran aplikasi, dihantar {0} kali sehari',\n            cronJobWebsiteRule: 'Amaran kegagalan tugas sandaran laman web, dihantar {0} kali sehari',\n            cronJobDatabaseRule: 'Amaran kegagalan tugas sandaran pangkalan data, dihantar {0} kali sehari',\n            cronJobDirectoryRule: 'Amaran kegagalan tugas sandaran direktori, dihantar {0} kali sehari',\n            cronJobLogRule: 'Amaran kegagalan tugas sandaran log, dihantar {0} kali sehari',\n            cronJobSnapshotRule: 'Amaran kegagalan tugas sandaran snapshot, dihantar {0} kali sehari',\n            cronJobShellRule: 'Amaran kegagalan tugas skrip shell, dihantar {0} kali sehari',\n            cronJobCurlRule: 'Amaran kegagalan tugas akses URL, dihantar {0} kali sehari',\n            cronJobCutWebsiteLogRule: 'Amaran kegagalan tugas potong log laman web, dihantar {0} kali sehari',\n            cronJobCleanRule: 'Amaran kegagalan tugas pembersihan cache, dihantar {0} kali sehari',\n            cronJobNtpRule: 'Amaran kegagalan tugas penyelarasan masa pelayan, dihantar {0} kali sehari',\n            alertSmsHelper: 'Had SMS: jumlah {0} mesej, {1} telah digunakan',\n            goBuy: 'Beli Lebih Banyak',\n            phone: 'Telefon',\n            phoneHelper: 'Berikan nombor telefon sebenar untuk mesej amaran',\n            dailyAlertNum: 'Had Amaran Harian',\n            dailyAlertNumHelper: 'Bilangan maksimum amaran sehari (sehingga 100)',\n            timeRange: 'Julat Masa',\n            sendTimeRange: 'Hantar julat masa',\n            sendTimeRangeHelper: 'Boleh tekan julat masa {0}',\n            to: '-',\n            startTime: 'Masa Mula',\n            endTime: 'Masa Tamat',\n            defaultPhone: 'Secara lalai ke nombor telefon akaun terikat lesen',\n            noticeAlert: 'Amaran Pemberitahuan',\n            resourceAlert: 'Amaran Sumber',\n            agentOfflineAlertHelper:\n                'Apabila amaran luar talian diaktifkan untuk nod, nod utama akan mengimbas setiap 30 minit untuk melaksanakan tugas amaran.',\n            offline: 'Amaran Luar Talian',\n            offlineHelper:\n                'Apabila ditetapkan kepada amaran luar talian, nod utama akan mengimbas setiap 30 minit untuk melaksanakan tugas amaran.',\n            offlineOff: 'Aktifkan Amaran Luar Talian',\n            offlineOffHelper:\n                'Mengaktifkan amaran luar talian akan menyebabkan nod utama mengimbas setiap 30 minit untuk melaksanakan tugas amaran.',\n            offlineClose: 'Lumpuhkan Amaran Luar Talian',\n            offlineCloseHelper:\n                'Melumpuhkan amaran luar talian memerlukan subnod untuk mengendalikan amaran secara kendiri. Sila pastikan sambungan rangkaian adalah baik untuk mengelakkan kegagalan amaran.',\n            alertNotice: 'Pemberitahuan Amaran',\n            methodConfig: 'Konfigurasi Kaedah Pemberitahuan',\n            commonConfig: 'Konfigurasi Global',\n            smsConfig: 'SMS',\n            smsConfigHelper: 'Konfigurasi nombor untuk pemberitahuan SMS',\n            emailConfig: 'E-mel',\n            emailConfigHelper: 'Konfigurasi perkhidmatan penghantaran e-mel SMTP',\n            deleteConfigTitle: 'Padam Konfigurasi Amaran',\n            deleteConfigMsg: 'Adakah anda pasti mahu memadam konfigurasi amaran?',\n            test: 'Ujian',\n            alertTestOk: 'Pemberitahuan ujian berjaya',\n            alertTestFailed: 'Pemberitahuan ujian gagal',\n            displayName: 'Nama',\n            sender: 'Pengirim',\n            password: 'Kata Laluan',\n            host: 'Pelayan SMTP',\n            port: 'Nombor Port',\n            encryption: 'Penyulitan',\n            recipient: 'Penerima',\n            licenseTime: 'Peringatan Tamat Tempoh Lesen',\n            licenseTimeTitle: 'Peringatan Tamat Tempoh Lesen',\n            displayNameHelper: 'Nama paparan pengirim e-mel',\n            senderHelper: 'Alamat e-mel yang digunakan untuk menghantar e-mel',\n            passwordHelper: 'Kod kebenaran untuk perkhidmatan e-mel',\n            hostHelper: 'Alamat pelayan SMTP, contoh: smtp.qq.com',\n            portHelper: 'SSL biasanya 465, TLS biasanya 587',\n            sslHelper: 'Jika port SMTP ialah 465, SSL biasanya diperlukan',\n            tlsHelper: 'Jika port SMTP ialah 587, TLS biasanya diperlukan',\n            triggerCondition: 'Syarat Pencetus',\n            loginFail: ' kegagalan log masuk dalam',\n            nodeException: 'Amaran Kerosakan Nod',\n            licenseException: 'Amaran Kerosakan Lesen',\n            panelLogin: 'Amaran Log Masuk Panel Tidak Normal',\n            sshLogin: 'Amaran Log Masuk SSH Tidak Normal',\n            panelIpLogin: 'Amaran IP Log Masuk Panel Tidak Normal',\n            sshIpLogin: 'Amaran IP Log Masuk SSH Tidak Normal',\n            ipWhiteListHelper:\n                'IP dalam senarai putih tidak tertakluk kepada peraturan, dan tiada amaran akan dikeluarkan apabila log masuk berjaya',\n            nodeExceptionRule: 'Amaran kerosakan nod, dihantar {0} kali sehari',\n            licenseExceptionRule: 'Amaran kerosakan lesen, dihantar {0} kali sehari',\n            panelLoginRule: 'Amaran log masuk panel, dihantar {0} kali sehari',\n            sshLoginRule: 'Amaran log masuk SSH, dihantar {0} kali sehari',\n            userNameHelper: 'Nama pengguna kosong, alamat penghantar akan digunakan secara lalai',\n            alertConfigHelper: 'Konfigurasikan saluran pemberitahuan amaran untuk menerima tolakan mesej panel',\n            weComConfigHelper: 'Konfigurasi pemberitahuan amaran WeCom',\n            wechatConfigHelper: 'Konfigurasi pemberitahuan amaran Akaun Rasmi WeChat',\n            dingTalkConfigHelper: 'Konfigurasi pemberitahuan amaran DingTalk',\n            feiShuConfigHelper: 'Konfigurasi pemberitahuan amaran Feishu',\n            webhookName: 'Nama bot',\n            webhookUrl: 'URL Webhook',\n            alertConfigProHelper: 'Edisi Profesional turut menyokong amaran WeCom, DingTalk, Feishu dan SMS.',\n        },\n        theme: {\n            lingXiaGold: 'Ling Xia Emas',\n            classicBlue: 'Biru Klasik',\n            freshGreen: 'Hijau Segar',\n            customColor: 'Warna Tersuai',\n            setDefault: 'Lalai',\n            setDefaultHelper: 'Skema warna tema akan dipulihkan ke keadaan asalnya. Adakah anda ingin meneruskan?',\n            setHelper: 'Skema warna tema yang dipilih sekarang akan disimpan. Adakah anda ingin meneruskan?',\n        },\n        exchange: {\n            exchange: 'Pertukaran Fail',\n            exchangeConfirm: 'Adakah anda mahu memindahkan fail/folder {1} dari node {0} ke direktori {3} node {2}?',\n        },\n        cluster: {\n            cluster: 'Aplikasi Tinggi Ketersediaan',\n            name: 'Nama Kluster',\n            addCluster: 'Tambah Kluster',\n            installNode: 'Pasang Node',\n            master: 'Node Utama',\n            slave: 'Node Hamba',\n            replicaStatus: 'Utama-Hamba Status',\n            unhealthyDeleteError: 'Status nod pemasangan tidak normal, sila periksa senarai nod dan cuba lagi!',\n            replicaStatusError: 'Pengambilan status tidak normal, sila periksa nod utama.',\n            masterHostError: 'IP nod utama tidak boleh 127.0.0.1',\n        },\n    },\n};\n\nexport default {\n    ...fit2cloudEnLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/pt-br.ts",
    "content": "import fit2cloudPtBrLocale from 'fit2cloud-ui-plus/src/locale/lang/pt-br';\n\nconst message = {\n    commons: {\n        true: 'Verdadeiro',\n        false: 'falso',\n        colon: ': ',\n        example: 'ex.: ',\n        fit2cloud: 'FIT2CLOUD',\n        lingxia: 'Lingxia',\n        button: {\n            run: 'Executar',\n            create: 'Criar',\n            add: 'Adicionar',\n            save: 'Salvar',\n            set: 'Editar configuração',\n            sync: 'Sincronizar',\n            delete: 'Excluir',\n            edit: 'Editar',\n            enable: 'Ativar',\n            disable: 'Desativar',\n            confirm: 'Confirmar',\n            cancel: 'Cancelar',\n            reset: 'Redefinir',\n            restart: 'Reiniciar',\n            conn: 'Conectar',\n            disConn: 'Desconectar',\n            clean: 'Limpar',\n            login: 'Entrar',\n            close: 'Fechar',\n            off: 'Fechar',\n            stop: 'Parar',\n            start: 'Iniciar',\n            view: 'Visualizar',\n            watch: 'Monitorar',\n            handle: 'Disparar',\n            clone: 'Clonar',\n            expand: 'Expandir',\n            collapse: 'Recolher',\n            log: 'Logs',\n            back: 'Voltar',\n            backup: 'Backup',\n            recover: 'Restaurar',\n            retry: 'Tentar novamente',\n            upload: 'Fazer upload',\n            download: 'Fazer download',\n            init: 'Inicializar',\n            verify: 'Verificar',\n            saveAndEnable: 'Salvar e ativar',\n            import: 'Importar',\n            export: 'Exportar',\n            power: 'Autorização',\n            search: 'Pesquisar',\n            refresh: 'Atualizar',\n            get: 'Obter',\n            upgrade: 'Atualizar versão',\n            update: 'atualizar',\n            updateNow: 'Atualizar Agora',\n            ignore: 'Ignorar atualização',\n            install: 'instalar',\n            copy: 'Copiar',\n            random: 'Aleatório',\n            uninstall: 'Desinstalar',\n            fullscreen: 'Entrar em tela cheia',\n            quitFullscreen: 'Sair da tela cheia',\n            showAll: 'Exibir tudo',\n            hideSome: 'Ocultar alguns',\n            agree: 'Concordo',\n            notAgree: 'Não concordo',\n            preview: 'Pré-visualizar',\n            open: 'Abrir',\n            notSave: 'Não salvar',\n            createNewFolder: 'Criar nova pasta',\n            createNewFile: 'Criar novo arquivo',\n            helpDoc: 'Documento de ajuda',\n            unbind: 'Desvincular',\n            cover: 'Substituir',\n            skip: 'Pular',\n            fix: 'Corrigir',\n            down: 'Parar',\n            up: 'Iniciar',\n            sure: 'Confirmar',\n            show: 'Exibir',\n            hide: 'Ocultar',\n            visit: 'Visitar',\n            migrate: 'Migrar',\n            prev: 'Anterior',\n            next: 'Próximo',\n            setDefault: 'Restaurar padrão',\n            bind: 'Vincular',\n        },\n        operate: {\n            start: 'Iniciar',\n            stop: 'Parar',\n            restart: 'Reiniciar',\n            reload: 'Recarregar',\n            rebuild: 'Reconstruir',\n            sync: 'Sincronizar',\n            up: 'Iniciar',\n            down: 'Parar',\n            delete: 'Excluir',\n        },\n        search: {\n            timeStart: 'Hora inicial',\n            timeEnd: 'Hora final',\n            timeRange: 'Até',\n            dateStart: 'Data inicial',\n            dateEnd: 'Data final',\n            date: 'Data',\n        },\n        table: {\n            all: 'Todos',\n            total: 'Total de {0}',\n            name: 'Nome',\n            type: 'Tipo',\n            status: 'Status',\n            records: 'Registros',\n            group: 'Grupo',\n            createdAt: 'Data de criação',\n            publishedAt: 'Data de publicação',\n            date: 'Data',\n            updatedAt: 'Data de atualização',\n            operate: 'Operações',\n            message: 'Mensagem',\n            description: 'Descrição',\n            interval: 'Intervalo',\n            user: 'Responsável',\n            title: 'Título',\n            port: 'Porta',\n            forward: 'Encaminhar',\n            protocol: 'Protocolo',\n            tableSetting: 'Configuração da tabela',\n            refreshRate: 'Taxa de atualização',\n            selectColumn: 'Selecionar coluna',\n            local: 'Local',\n            serialNumber: 'Número de série',\n            manageGroup: 'Gerenciar Grupos',\n            backToList: 'Voltar à Lista',\n            keepEdit: 'Continuar Editando',\n            default: 'Padrão',\n            noRefresh: 'Sem atualização',\n        },\n        loadingText: {\n            Upgrading: 'Atualizando o sistema, por favor, aguarde...',\n            Restarting: 'Reiniciando o sistema, por favor, aguarde...',\n            Recovering: 'Restaurando a partir de um snapshot, por favor, aguarde...',\n            Rollbacking: 'Revertendo para um snapshot, por favor, aguarde...',\n        },\n        msg: {\n            noneData: 'Nenhum dado disponível',\n            delete: 'Esta operação de exclusão não pode ser desfeita. Deseja continuar?',\n            clean: 'Esta operação de limpeza não pode ser desfeita. Deseja continuar?',\n            closeDrawerHelper: 'O sistema pode não salvar as alterações que você fez. Deseja continuar?',\n            deleteSuccess: 'Excluído com sucesso',\n            loginSuccess: 'Login realizado com sucesso',\n            operationSuccess: 'Operação concluída com sucesso',\n            copySuccess: 'Copiado com sucesso',\n            notSupportOperation: 'Esta operação não é suportada',\n            requestTimeout: 'A solicitação expirou, tente novamente mais tarde',\n            infoTitle: 'Aviso',\n            notRecords: 'Nenhum registro de execução foi gerado para a tarefa atual',\n            sureLogOut: 'Tem certeza de que deseja sair?',\n            createSuccess: 'Criado com sucesso',\n            updateSuccess: 'Atualizado com sucesso',\n            uploadSuccess: 'Enviado com sucesso',\n            operateConfirm: 'Se você tem certeza da operação, insira-a manualmente: ',\n            inputOrSelect: 'Por favor, selecione ou insira',\n            copyFailed: 'Falha ao copiar',\n            operatorHelper: 'A operação \"{1}\" será realizada em \"{0}\" e não poderá ser desfeita. Deseja continuar?',\n            notFound: 'Desculpe, a página solicitada não existe.',\n            unSupportType: 'O tipo de arquivo atual não é suportado.',\n            unSupportSize: 'O arquivo enviado excede {0}M, por favor confirme!',\n            fileExist: 'O arquivo já existe na pasta atual. Não é possível enviar novamente.',\n            fileNameErr:\n                'Você pode enviar apenas arquivos cujo nome contenha de 1 a 256 caracteres, incluindo letras, números ou os caracteres (. - _)',\n            confirmNoNull: 'Certifique-se de que o valor {0} não está vazio.',\n            errPort: 'Informação de porta incorreta, por favor confirme!',\n            remove: 'Remover',\n            backupHelper: 'A operação atual fará o backup de {0}. Deseja continuar?',\n            recoverHelper: 'Restaurando a partir do arquivo {0}. Esta operação é irreversível. Deseja continuar?',\n            refreshSuccess: 'Atualizado com sucesso',\n            rootInfoErr: 'Já está no diretório raiz',\n            resetSuccess: 'Redefinido com sucesso',\n            creatingInfo: 'Criando, não é necessário realizar esta operação',\n            offlineTips: 'A versão offline não suporta esta operação',\n            errImportFormat: 'Dados de importação ou formato estão anormais, verifique e tente novamente!',\n            importHelper:\n                'Ao importar dados conflitantes ou duplicados, o conteúdo importado será usado como padrão para atualizar os dados originais do banco de dados.',\n            errImport: 'O conteúdo do arquivo está anormal:',\n            backupSuccess: 'Backup concluído',\n            restoreSuccess: 'Restauração concluída',\n            installSuccess: 'Instalado com sucesso',\n            uninstallSuccess: 'Desinstalado com sucesso',\n        },\n        login: {\n            username: 'Usuário',\n            password: 'Senha',\n            passkey: 'Login com Passkey',\n            welcome: 'Bem-vindo de volta, insira seu usuário e senha para acessar!',\n            errorAuthInfo: 'O nome de usuário ou senha que você inseriu está incorreto, tente novamente!',\n            errorMfaInfo: 'Informações de autenticação incorretas, tente novamente!',\n            captchaHelper: 'Captcha',\n            errorCaptcha: 'Erro no código Captcha!',\n            notSafe: 'Acesso Negado',\n            safeEntrance1: 'O login seguro foi habilitado no ambiente atual',\n            safeEntrance2:\n                'Digite o seguinte comando no terminal SSH para visualizar a entrada do painel: 1pctl user-info',\n            errIP1: 'O acesso por endereço IP autorizado está habilitado no ambiente atual',\n            errDomain1: 'O acesso por nome de domínio vinculado está habilitado no ambiente atual',\n            errHelper: 'Para redefinir as informações de vinculação, execute o seguinte comando no terminal SSH:',\n            codeInput: 'Por favor, insira o código de verificação de 6 dígitos do validador MFA',\n            mfaTitle: 'Autenticação MFA',\n            mfaCode: 'Código de verificação MFA',\n            title: 'Painel de Gerenciamento de Servidores Linux',\n            licenseHelper: '<Acordo de Licença Comunitária>',\n            errorAgree: 'Clique para concordar com o Acordo de Licença de Software Comunitário',\n            logout: 'Sair',\n            agreeTitle: 'Termo de Aceite',\n            agreeContent:\n                'Para proteger melhor seus direitos e interesses legítimos, leia e concorde com o seguinte termo &laquo; <a href=\"https://www.fit2cloud.com/legal/licenses.html\" target=\"_blank\">Acordo de Licença Comunitária</a> &raquo;',\n            passkeyFailed: 'Falha no login com Passkey, tente novamente',\n            passkeyNotSupported: 'O navegador ou ambiente atual não suporta passkeys',\n            passkeyToPassword: 'Está com problemas para usar uma passkey? Use a senha',\n        },\n        rule: {\n            username: 'Insira um nome de usuário',\n            password: 'Insira uma senha',\n            rePassword: 'A confirmação da senha não corresponde à senha.',\n            requiredInput: 'Este campo é obrigatório.',\n            requiredSelect: 'Selecione um item na lista',\n            illegalChar: 'Atualmente não há suporte para injeção dos caracteres & ; $ \\' ` ( ) \" > < |',\n            illegalInput: 'Este campo não deve conter caracteres ilegais.',\n            commonName:\n                'Este campo deve começar com caracteres não especiais e consistir em letras, números, \".\", \"-\", e \"_\" com comprimento de 1-128.',\n            userName: 'Suporta não começar com caracteres especiais, inglês, chinês, números e _, comprimento 3-30',\n            simpleName:\n                'Este campo não deve começar com \"_\" e deve conter letras, números e \"_\" com comprimento de 3-30.',\n            simplePassword:\n                'Este campo não deve começar com \"_\" e deve conter letras, números e \"_\" com comprimento de 1-30.',\n            dbName: 'Este campo não deve começar com \"_\" e deve conter letras, números e \"_\" com comprimento de 1-64.',\n            imageName: 'Suporta não começar com caracteres especiais, inglês, números, :@/.-_, comprimento 1-256',\n            composeName:\n                'Deve começar com caracteres não especiais, conter letras minúsculas, números, \"-\" e \"_\" com comprimento de 1-256.',\n            volumeName: 'Este campo deve conter letras, números, \".\", \"-\", e \"_\" com comprimento de 2-30.',\n            supervisorName:\n                'Este campo deve começar com caracteres não especiais e conter letras, números, \"-\" e \"_\" com comprimento de 1-128.',\n            complexityPassword:\n                'Este campo deve conter letras, números com comprimento de 8-30 e pelo menos dois caracteres especiais.',\n            commonPassword: 'O comprimento deste campo deve ser maior que 6.',\n            linuxName:\n                'O comprimento deste campo deve estar entre 1 e 128. Não pode conter os seguintes caracteres especiais: \"{0}\".',\n            email: 'Este campo deve ser um endereço de email válido.',\n            number: 'Este campo deve ser um número.',\n            integer: 'Este campo deve ser um número inteiro positivo.',\n            ip: 'Este campo deve ser um endereço IP válido.',\n            host: 'Este campo deve ser um endereço IP ou nome de domínio válido.',\n            hostHelper: 'Suporta a entrada de endereço IP ou nome de domínio',\n            port: 'Este campo deve ser um número de porta válido.',\n            selectHelper: 'Selecione o arquivo {0} correto',\n            domain: 'Este campo deve estar no formato: exemplo.com ou exemplo.com:8080.',\n            databaseName: 'Este campo deve conter letras, números e \"_\" com comprimento de 1-30.',\n            ipErr: 'Este campo deve ser um endereço IP válido.',\n            numberRange: 'Este campo deve ser um número entre {0} e {1}.',\n            paramName: 'Este campo deve conter letras, números, \".\", \"-\", e \"_\" com comprimento de 2-30.',\n            paramComplexity:\n                'Este campo não deve começar ou terminar com caracteres especiais e deve conter letras, números e \"{0}\" com comprimento de 6-128.',\n            paramUrlAndPort: 'Este campo deve estar no formato \"http(s)://(nome do domínio/IP):(porta)\".',\n            nginxDoc: 'Este campo deve conter letras, números e \".\".',\n            appName:\n                'Este campo não deve começar ou terminar com \"-\" ou \"_\" e deve conter letras, números, \"-\", e \"_\" com comprimento de 2-30.',\n            containerName:\n                'Suporta letras, números, \"-\", \"_\" e \".\"; não pode começar com \"-\", \"_\" ou \".\"; comprimento: 2-128.',\n            mirror: 'O endereço de aceleração do mirror deve começar com http(s)://, suportar letras (maiúsculas e minúsculas), números, \".\", \"/\" e \"-\", e não deve conter linhas em branco.',\n            disableFunction: 'Suporta apenas letras, underscores e,',\n            leechExts: 'Suporta apenas letras, números e,',\n            paramSimple: 'Suporta letras minúsculas e números, comprimento 1-128',\n            filePermission: 'Erro de permissão de arquivo',\n            formatErr: 'Erro de formato, verifique e tente novamente',\n            phpExtension: 'Suporta apenas _, letras minúsculas e números',\n            paramHttp: 'Deve começar com http:// ou https://',\n            phone: 'O formato do número de telefone está incorreto',\n            authBasicPassword: 'Suporta letras, números e caracteres especiais comuns, comprimento 1-72',\n            length128Err: 'O comprimento não pode exceder 128 caracteres',\n            maxLength: 'O comprimento não pode exceder {0} caracteres',\n            alias: 'Suporta letras, números, - e _, comprimento de 1 a 128, e não pode começar ou terminar com -_.',\n        },\n        res: {\n            paramError: 'A solicitação falhou, por favor, tente novamente mais tarde!',\n            forbidden: 'O usuário atual não tem permissão',\n            serverError: 'Exceção no serviço',\n            notFound: 'O recurso não existe',\n            commonError: 'A solicitação falhou',\n        },\n        service: {\n            serviceNotStarted: 'O serviço {0} não está iniciado.',\n        },\n        status: {\n            running: 'Em execução',\n            done: 'Concluído',\n            scanFailed: 'Incompleto',\n            success: 'Sucesso',\n            waiting: 'Aguardando',\n            waitForUpgrade: 'Aguardar Atualização',\n            waiting1: 'Aguardando',\n            failed: 'Falhou',\n            stopped: 'Parado',\n            error: 'Erro',\n            created: 'Criado',\n            restarting: 'Reiniciando',\n            uploading: 'Enviando',\n            unhealthy: 'Indisponível',\n            removing: 'Removendo',\n            paused: 'Pausado',\n            exited: 'Finalizado',\n            dead: 'Morto',\n            installing: 'Instalando',\n            enabled: 'Habilitado',\n            disabled: 'Desabilitado',\n            normal: 'Normal',\n            building: 'Construindo',\n            upgrading: 'Atualizando',\n            pending: 'Aguardando Edição',\n            rebuilding: 'Reconstruindo',\n            deny: 'Negado',\n            accept: 'Aceito',\n            used: 'Usado',\n            unUsed: 'Não usado',\n            starting: 'Iniciando',\n            recreating: 'Reconstruindo',\n            creating: 'Criando',\n            init: 'Aguardando aplicação',\n            ready: 'Normal',\n            applying: 'Aplicando',\n            uninstalling: 'Desinstalando',\n            lost: 'Perdido',\n            bound: 'Vinculado',\n            unbind: 'Desvinculado',\n            exceptional: 'Excepcional',\n            free: 'Livre',\n            enable: 'Habilitado',\n            disable: 'Desabilitado',\n            deleted: 'Excluído',\n            downloading: 'Baixando',\n            packing: 'Empacotando',\n            sending: 'Enviando',\n            healthy: 'Saudável',\n            executing: 'Executando',\n            installerr: 'Falha na instalação',\n            applyerror: 'Falha na aplicação',\n            systemrestart: 'Interrompido',\n            starterr: 'Falha na inicialização',\n            uperr: 'Falha na inicialização',\n            new: 'Novo',\n            conflict: 'Conflito',\n            duplicate: 'Duplicado',\n            unexecuted: 'Não Executado',\n            unused: 'Não usado',\n        },\n        units: {\n            second: 'segundo | segundos | segundos',\n            minute: 'minuto | minutos | minutos',\n            hour: 'hora | horas | horas',\n            day: 'dia | dias | dias',\n            week: 'semana | semanas | semanas',\n            month: 'mês | meses | meses',\n            year: 'ano | anos | anos',\n            time: 'rqm',\n            core: 'núcleo | núcleos | núcleos',\n            secondUnit: 's',\n            minuteUnit: 'min',\n            hourUnit: 'h',\n            dayUnit: 'd',\n            millisecond: 'Milissegundo',\n            semicolon: ';',\n        },\n        log: {\n            noLog: 'Nenhum log disponível',\n        },\n    },\n    menu: {\n        home: 'Painel',\n        apps: 'Loja de Aplicativos',\n        website: 'Site | Sites',\n        project: 'Projeto | Projetos',\n        config: 'Configuração | Configurações',\n        ssh: 'Configurações SSH',\n        firewall: 'Firewall',\n        ssl: 'Certificado | Certificados',\n        database: 'Banco de Dados | Bancos de Dados',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: 'Container | Containers',\n        cronjob: 'Tarefa Cron | Tarefas Cron',\n        system: 'Sistema',\n        security: 'Segurança',\n        files: 'Arquivos',\n        monitor: 'Monitoramento',\n        terminal: 'Terminal',\n        settings: 'Configurações',\n        toolbox: 'Caixa de Ferramentas',\n        logs: 'Log | Logs',\n        runtime: 'Tempo de Execução | Tempos de Execução',\n        processManage: 'Processo | Processos',\n        process: 'Processo | Processos',\n        network: 'Rede | Redes',\n        supervisor: 'Supervisor',\n        tamper: 'À prova de violação',\n        app: 'Aplicativo',\n        msgCenter: 'Tarefas',\n        disk: 'Disco',\n        filter: 'Filtro',\n    },\n    home: {\n        recommend: 'Recomendado',\n        dir: 'Diretório',\n        alias: 'Apelido',\n        quickDir: 'Acesso rápido',\n        minQuickJump: 'Adicione pelo menos uma entrada de acesso rápido',\n        maxQuickJump: 'Você pode adicionar até quatro entradas de acesso rápido',\n        database: 'Banco de Dados - Todos',\n        restart_1panel: 'Reiniciar painel',\n        restart_system: 'Reiniciar servidor',\n        operationSuccess: 'Concluído com sucesso. Reiniciando agora, atualize o navegador depois.',\n        entranceHelper:\n            'A entrada segura não está ativada. Ative em \"Configurações -> Segurança\" para reforçar a proteção.',\n        appInstalled: 'Aplicações',\n        systemInfo: 'Informações do sistema',\n        hostname: 'Nome do host',\n        platformVersion: 'Sistema operacional',\n        kernelVersion: 'Versão do Kernel',\n        kernelArch: 'Arquitetura',\n        network: 'Rede',\n        io: 'Leitura/Gravação de Disco',\n        ip: 'IP local',\n        proxy: 'Proxy do sistema',\n        baseInfo: 'Informações básicas',\n        totalSend: 'Total enviado',\n        totalRecv: 'Total recebido',\n        rwPerSecond: 'Operações de I/O',\n        ioDelay: 'Latência de I/O',\n        uptime: 'Tempo de atividade',\n        runningTime: 'Tempo de execução',\n        mem: 'Memória do Sistema',\n        swapMem: 'Partição Swap',\n        runSmoothly: 'Baixo carregamento',\n        runNormal: 'Carregamento moderado',\n        runSlowly: 'Carregamento alto',\n        runJam: 'Carregamento pesado',\n        core: 'Núcleo físico',\n        logicCore: 'Núcleo lógico',\n        corePercent: 'Uso do Núcleo',\n        cpuFrequency: 'Frequência CPU',\n        cpuDetailedPercent: 'Distribuição do Tempo de CPU',\n        cpuUser: 'Usuário',\n        cpuSystem: 'Sistema',\n        cpuIdle: 'Ocioso',\n        cpuIrq: 'IRQ',\n        cpuSoftirq: 'Soft IRQ',\n        cpuSteal: 'Roubado',\n        cpuTop: 'Top 5 Processos por Uso de CPU',\n        memTop: 'Top 5 Processos por Uso de Memória',\n        loadAverage: 'Média de carga nos últimos 1 minuto | Média de carga nos últimos {n} minutos',\n        load: 'Carga',\n        mount: 'Ponto de montagem',\n        fileSystem: 'Sistema de arquivos',\n        total: 'Total',\n        used: 'Usado',\n        cache: 'Cache',\n        free: 'Livre',\n        shard: 'Fragmentado',\n        available: 'Disponível',\n        percent: 'Utilização',\n        goInstall: 'Ir para instalação',\n        networkCard: 'Placa de rede',\n        disk: 'Disco',\n        memo: 'Memo',\n        memoPlaceholder: 'Clique no botão de editar para habilitar a edição.',\n        carouselSetting: 'Configuração do carrossel',\n        tooltipSensitiveInfo: 'Mostrar/Ocultar informações sensíveis',\n    },\n    tabs: {\n        more: 'Mais',\n        hide: 'Esconder',\n        closeLeft: 'Fechar à esquerda',\n        closeRight: 'Fechar à direita',\n        closeCurrent: 'Fechar atual',\n        closeOther: 'Fechar outros',\n        closeAll: 'Fechar todos',\n    },\n    header: {\n        logout: 'Logout',\n    },\n    database: {\n        manage: 'Gerenciamento',\n        deleteBackupHelper: 'Excluir backups do banco de dados simultaneamente',\n        delete: 'A operação de exclusão não pode ser desfeita, insira \"',\n        deleteHelper: '\" para excluir este banco de dados',\n        noMysql: 'Serviço de banco de dados (MySQL ou MariaDB)',\n        noPostgresql: 'Serviço de banco de dados PostgreSQL',\n        goUpgrade: 'Ir para atualização',\n        goInstall: 'Ir para instalação',\n        isDelete: 'Excluído',\n        permission: 'Permissões',\n        format: 'Conjunto de Caracteres',\n        collation: 'Collation',\n        collationHelper: 'Se vazio, use a collation padrão do conjunto de caracteres {0}',\n        permissionForIP: 'IP',\n        permissionAll: 'Todos (% de)',\n        localhostHelper:\n            'Configurar permissões de banco de dados como \"localhost\" para implantação em contêiner impedirá o acesso externo ao contêiner. Por favor, escolha com cuidado!',\n        databaseConnInfo: 'Conexão',\n        rootPassword: 'Senha root',\n        serviceName: 'Nome do serviço',\n        serviceNameHelper: 'Acesso entre containers na mesma rede.',\n        backupList: 'Backup',\n        loadBackup: 'Importar',\n        localUpload: 'Upload Local',\n        hostSelect: 'Seleção de Servidor',\n        selectHelper: 'Tem certeza de que deseja importar o arquivo de backup {0}?',\n        remoteAccess: 'Acesso remoto',\n        remoteHelper: 'Vários IPs separados por vírgula, exemplo: 172.16.10.111, 172.16.10.112',\n        remoteConnHelper:\n            'Conectar-se ao MySQL como usuário root pode representar riscos de segurança. Realize esta operação com cautela.',\n        changePassword: 'Senha',\n        changeConnHelper: 'Esta operação modificará o banco de dados atual {0}. Deseja continuar?',\n        changePasswordHelper:\n            'O banco de dados está associado a um aplicativo. Alterar a senha alterará a senha do banco de dados do aplicativo ao mesmo tempo. A mudança surtirá efeito após a reinicialização do aplicativo.',\n        recoverTimeoutHelper: '-1 significa sem limite de tempo limite',\n        confChange: 'Configuração',\n        confNotFound:\n            'Não foi possível encontrar o arquivo de configuração. Atualize o aplicativo para a versão mais recente na loja de aplicativos e tente novamente!',\n        portHelper:\n            'Esta porta é a porta exposta do container. Você precisa salvar a modificação separadamente e reiniciar o container!',\n        loadFromRemote: 'Sincronizar',\n        userBind: 'Vincular usuário',\n        pgBindHelper:\n            'Esta operação cria um novo usuário e o vincula ao banco de dados alvo. A seleção de usuários já existentes no banco de dados não é suportada.',\n        pgSuperUser: 'Superusuário',\n        loadFromRemoteHelper:\n            'Isso sincronizará as informações do banco de dados no servidor para o 1Panel. Deseja continuar?',\n        passwordHelper: 'Não é possível obter, clique para modificar',\n        remote: 'Remoto',\n        remoteDB: 'Servidor remoto | Servidores remotos',\n        createRemoteDB: 'Vincular @.lower:database.remoteDB',\n        unBindRemoteDB: 'Desvincular @.lower:database.remoteDB',\n        unBindForce: 'Forçar desvinculação',\n        unBindForceHelper:\n            'Ignorar todos os erros durante o processo de desvinculação para garantir que a operação final seja bem-sucedida',\n        unBindRemoteHelper:\n            'Desvincular o banco de dados remoto removerá apenas a relação de vinculação e não excluirá diretamente o banco de dados remoto',\n        editRemoteDB: 'Editar servidor remoto',\n        localDB: 'Banco de dados local',\n        address: 'Endereço do banco de dados',\n        version: 'Versão do banco de dados',\n        userHelper:\n            'O usuário root ou um usuário do banco de dados com privilégios de root pode acessar o banco de dados remoto.',\n        pgUserHelper: 'Usuário do banco de dados com privilégios de superusuário.',\n        ssl: 'Usar SSL',\n        clientKey: 'Chave privada do cliente',\n        clientCert: 'Certificado do cliente',\n        caCert: 'Certificado CA',\n        hasCA: 'Possui certificado CA',\n        skipVerify: 'Ignorar verificação de validade do certificado',\n        initialDB: 'Banco de Dados Inicial',\n        formatHelper:\n            'O conjunto de caracteres atual do banco de dados é {0}, a inconsistência no conjunto de caracteres pode causar falha na recuperação',\n        dropHelper: 'Você pode arrastar e soltar o arquivo carregado aqui ou',\n        clickHelper: 'clicar para fazer upload',\n        supportUpType:\n            'Suporta apenas os formatos de arquivo sql, sql.gz, tar.gz, .zip. O arquivo compactado importado deve conter apenas um arquivo .sql ou incluir test.sql',\n        currentStatus: 'Estado atual',\n        baseParam: 'Parâmetro básico',\n        performanceParam: 'Parâmetro de desempenho',\n        runTime: 'Tempo de inicialização',\n        connections: 'Conexões totais',\n        bytesSent: 'Bytes enviados',\n        bytesReceived: 'Bytes recebidos',\n        queryPerSecond: 'Consultas por segundo',\n        txPerSecond: 'Tx por segundo',\n        connInfo: 'conexões ativas/pico',\n        connInfoHelper: 'Se o valor for muito grande, aumente \"max_connections\".',\n        threadCacheHit: 'Acuracidade do cache de threads',\n        threadCacheHitHelper: 'Se for muito baixo, aumente \"thread_cache_size\".',\n        indexHit: 'Acuracidade de índice',\n        indexHitHelper: 'Se for muito baixo, aumente \"key_buffer_size\".',\n        innodbIndexHit: 'Taxa de acerto de índice InnoDB',\n        innodbIndexHitHelper: 'Se for muito baixo, aumente \"innodb_buffer_pool_size\".',\n        cacheHit: 'Acuracidade da consulta em cache',\n        cacheHitHelper: 'Se for muito baixo, aumente \"query_cache_size\".',\n        tmpTableToDB: 'Tabela temporária para disco',\n        tmpTableToDBHelper: 'Se for muito grande, tente aumentar \"tmp_table_size\".',\n        openTables: 'Tabelas abertas',\n        openTablesHelper: 'O valor da configuração de \"table_open_cache\" deve ser maior ou igual a este valor.',\n        selectFullJoin: 'Join completo de seleção',\n        selectFullJoinHelper: 'Se o valor não for 0, verifique se o índice da tabela de dados está correto.',\n        selectRangeCheck: 'Número de joins sem índice',\n        selectRangeCheckHelper: 'Se o valor não for 0, verifique se o índice da tabela de dados está correto.',\n        sortMergePasses: 'Número de mesclagens ordenadas',\n        sortMergePassesHelper: 'Se o valor for muito grande, aumente \"sort_buffer_size\".',\n        tableLocksWaited: 'Número de bloqueios de tabela',\n        tableLocksWaitedHelper: 'Se o valor for muito grande, considere aumentar o desempenho do banco de dados.',\n        performanceTuning: 'Ajuste de desempenho',\n        optimizationScheme: 'Plano de otimização',\n        keyBufferSizeHelper: 'Tamanho do buffer para índice',\n        queryCacheSizeHelper: 'Cache de consulta. Se essa função estiver desativada, defina este parâmetro como 0.',\n        tmpTableSizeHelper: 'Tamanho do cache de tabela temporária',\n        innodbBufferPoolSizeHelper: 'Tamanho do buffer InnoDB',\n        innodbLogBufferSizeHelper: 'Tamanho do buffer de log InnoDB',\n        sortBufferSizeHelper: '* conexões, buffer de cada thread de ordenação',\n        readBufferSizeHelper: '* conexões, tamanho do buffer de leitura',\n        readRndBufferSizeHelper: '* conexões, tamanho do buffer de leitura aleatória',\n        joinBufferSizeHelper: '* conexões, tamanho do cache da tabela de associação',\n        threadStackelper: '* conexões, tamanho do stack por thread',\n        binlogCacheSizeHelper: '* conexões, tamanho do cache de log binário (múltiplos de 4096)',\n        threadCacheSizeHelper: 'Tamanho do pool de threads',\n        tableOpenCacheHelper: 'Cache de tabelas',\n        maxConnectionsHelper: 'Conexões máximas',\n        restart: 'Reiniciar',\n        slowLog: 'Logs lentos',\n        noData: 'Ainda não há logs lentos.',\n        isOn: 'Ligado',\n        longQueryTime: 'limite (segundos)',\n        thresholdRangeHelper: 'Por favor, insira o limite correto (1 - 600).',\n        timeout: 'Tempo limite(segundos)',\n        timeoutHelper: 'Período de timeout de conexão ociosa. 0 indica que a conexão permanece ativa continuamente.',\n        maxclients: 'Máximo de clientes',\n        requirepassHelper:\n            'Deixe este campo em branco para indicar que nenhuma senha foi definida. Mudanças precisam ser salvas separadamente e o container reiniciado!',\n        databases: 'Número de bancos de dados',\n        maxmemory: 'Uso máximo de memória',\n        maxmemoryHelper: '0 indica sem restrição.',\n        tcpPort: 'Porta de escuta atual.',\n        uptimeInDays: 'Dias de operação.',\n        connectedClients: 'Número de clientes conectados.',\n        usedMemory: 'Uso atual de memória do Redis.',\n        usedMemoryRss: 'Tamanho da memória solicitado ao sistema operacional.',\n        usedMemoryPeak: 'Pico de consumo de memória do Redis.',\n        memFragmentationRatio: 'Taxa de fragmentação de memória.',\n        totalConnectionsReceived: 'Total de clientes conectados desde a execução.',\n        totalCommandsProcessed: 'Número total de comandos executados desde a execução.',\n        instantaneousOpsPerSec: 'Número de comandos executados pelo servidor por segundo.',\n        keyspaceHits: 'Número de vezes que uma chave de banco de dados foi encontrada.',\n        keyspaceMisses: 'Número de tentativas falhas de encontrar a chave do banco de dados.',\n        hit: 'Taxa de acerto de chave de banco de dados.',\n        latestForkUsec: 'Número de microssegundos gastos na última operação fork()',\n        redisCliHelper: '\"redis-cli\" não foi detectado. Habilite o serviço primeiro.',\n        redisQuickCmd: 'Comandos rápidos Redis',\n        recoverHelper: 'Isso sobrescreverá os dados com [{0}]. Deseja continuar?',\n        submitIt: 'Sobrescrever os dados',\n        baseConf: 'Básico',\n        allConf: 'Todos',\n        restartNow: 'Reiniciar agora',\n        restartNowHelper1:\n            'Você precisa reiniciar o sistema após as mudanças na configuração entrarem em vigor. Se seus dados precisarem ser persistidos, execute primeiro a operação de salvar.',\n        restartNowHelper: 'Isso só terá efeito após o sistema reiniciar.',\n        persistence: 'Persistência',\n        rdbHelper1: 'segundo(s), inserir',\n        rdbHelper2: 'registros de dados',\n        rdbHelper3: 'Atender a qualquer uma das condições acionará a persistência RDB.',\n        rdbInfo: 'Certifique-se de que o valor na lista de regras esteja entre 1 e 100000',\n        containerConn: 'Conexão do contêiner',\n        copyConnURL: 'Copiar URL de conexão',\n        connAddress: 'Endereço',\n        containerConnHelper:\n            'Este endereço de conexão pode ser utilizado por aplicações que estão em execução nos ambientes do site (PHP, etc.) ou no contêiner.',\n        remoteConn: 'Conexão externa',\n        remoteConnHelper2: 'Use este endereço para ambientes não-container ou conexões externas.',\n        remoteConnHelper3:\n            'O endereço de acesso padrão é o IP do host. Para modificá-lo, acesse o item de configuração \"Endereço de Acesso Padrão\" na página de configurações do painel.',\n        localIP: 'IP local',\n    },\n    aiTools: {\n        agents: {\n            agents: 'Agentes',\n            agent: 'Agente',\n            account: 'Conta de modelo',\n            noAccountHint: 'Selecione uma conta de modelo existente ou adicione uma nova.',\n            accountCount: '{0} contas de modelo',\n            syncAgents: 'Sincronizar agentes vinculados',\n            syncAgentsHelper: 'Atualize o openclaw.json para agentes que usam esta conta de modelo',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: 'Versão do app',\n            webuiPort: 'Porta WebUI',\n            allowedOrigins: 'Endereços de acesso',\n            allowedOriginsHelper:\n                'Informe um endereço de acesso completo por linha. O uso de HTTPS é recomendado, por exemplo https://192.168.1.2:18789. Preencha manualmente se o endereço padrão não estiver configurado.',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: 'Informe pelo menos um endereço de acesso',\n            allowedOriginsInvalid: 'Use o formato http(s)://host-ou-ip[:porta]',\n            provider: 'Provedor de modelos',\n            apiKey: 'Chave API',\n            baseUrl: 'URL base',\n            accountModels: 'Model Catalog',\n            accountModelsHelper: 'Configure the models this account exposes to OpenClaw for switching and settings',\n            accountModelsRequired: 'Configure at least one model',\n            accountModelsDuplicate: 'Duplicate models exist in the catalog',\n            modelPool: 'Model Pool',\n            modelPoolHelper:\n                'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',\n            modelInputTypes: 'Input Types',\n            reasoning: 'Reasoning Model',\n            token: 'Token',\n            manualModel: 'Entrada manual',\n            verified: 'Verificado',\n            verifySkipped: 'Sem verificacao',\n            configTitle: 'Configuration',\n            settingsTab: 'Settings',\n            securityTab: 'Security',\n            otherTab: 'Other',\n            timeZone: 'Fuso horário',\n            browserEnabled: 'Browser Enabled',\n            switchModelSuccess: 'Model switched successfully',\n            channelsTab: 'Channels',\n            wecom: 'WeCom',\n            dingtalk: 'DingTalk',\n            feishu: 'Feishu',\n            pluginNotInstalled: 'O plugin não está instalado. Instale-o primeiro.',\n            dmPolicy: 'DM Policy',\n            groupPolicy: 'Group Policy',\n            policyPairing: 'Pairing',\n            policyAllowlist: 'Allowlist',\n            policyOpen: 'Open',\n            policyDisabled: 'Disabled',\n            botName: 'Bot Name',\n            botId: 'Bot ID',\n            appId: 'App ID',\n            appSecret: 'App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: 'DM Allowlist',\n            allowFromHelper: 'One sender ID per line. Used only when DM Policy is Allowlist.',\n            allowFromPlaceholder: 'One sender ID per line',\n            groupAllowFrom: 'Group Allowlist',\n            groupAllowFromHelper: 'One group ID per line. Used only when Group Policy is Allowlist.',\n            groupAllowFromPlaceholder: 'One group ID per line',\n            allowFromRequired: 'Enter at least one allowlist entry',\n            saveAndRestartGateway: 'Save and restart gateway',\n            pairingCode: 'Pairing Code',\n            pairingCodePlaceholder: 'Enter pairing code',\n            approvePairing: 'Approve Pairing',\n            feishuRequired: 'Please fill botName / appId / appSecret',\n            saveSuccess: 'Saved successfully',\n            pairingCodeRequired: 'Please enter pairing code',\n            pairingApproveSuccess: 'Pairing approved successfully',\n            customProviderHelper: 'Provedores de modelo personalizados não validam se a conta está disponível',\n            feishuSaveSuccess: 'Salvo no Feishu',\n        },\n        model: {\n            model: 'Modelo',\n            localModel: 'Modelos locais',\n            create: 'Adicionar Modelo',\n            create_helper: 'Puxar \"{0}\"',\n            ollama_doc: 'Você pode visitar o site oficial da Ollama para pesquisar e encontrar mais modelos.',\n            container_conn_helper: 'Use este endereço para acesso ou conexão entre contêineres',\n            ollama_sync:\n                'Menyelaraskan model Ollama mendapati model berikut tidak wujud, adakah anda ingin memadamnya?',\n            from_remote: 'Model ini tidak dimuat turun melalui 1Panel, tiada log pengambilan berkaitan.',\n            no_logs: 'Log pengambilan untuk model ini telah dipadam dan tidak dapat dilihat.',\n            vllmVersionHelper: 'Para servidores FusionXpark GB 10, selecione a versão -cu130.',\n        },\n        proxy: {\n            proxy: 'Melhoria de Proxy AI',\n            proxyHelper1: 'Vincule o domínio e habilite o HTTPS para aumentar a segurança na transmissão',\n            proxyHelper2: 'Limite o acesso por IP para evitar exposição na internet pública',\n            proxyHelper3: 'Habilite a transmissão em fluxo',\n            proxyHelper4: 'Após a criação, você pode visualizar e gerenciar no lista de sites',\n            proxyHelper6: 'Para desativar a configuração de proxy, você pode excluí-la da lista de sites.',\n            whiteListHelper: 'Restringir o acesso apenas aos IPs na lista branca',\n            proxyHelper5:\n                'Após ativar, você pode desativar o acesso externo à porta em Loja de Apps - Instalado - Ollama - Parâmetros para melhorar a segurança.',\n        },\n        gpu: {\n            gpu: 'Monitoramento de GPU',\n            gpuHelper: 'O sistema não detectou comandos NVIDIA-SMI ou XPU-SMI. Verifique e tente novamente!',\n            process: 'Informações do Processo',\n            type: 'Tipo',\n            typeG: 'Gráficos',\n            typeC: 'Computação',\n            typeCG: 'Computação+Gráficos',\n            processName: 'Nome do Processo',\n            shr: 'Memória Compartilhada',\n            temperatureHelper: 'Alta temperatura da GPU pode causar redução na frequência da GPU',\n            gpuUtil: 'Utilização da GPU',\n            temperature: 'Temperatura',\n            performanceState: 'Estado de Desempenho',\n            powerUsage: 'Consumo de Energia',\n            memoryUsage: 'Utilização de Memória',\n            fanSpeed: 'Velocidade do Ventilador',\n            power: 'Energia',\n            powerCurrent: 'Energia Atual',\n            powerLimit: 'Limite de Energia',\n            memory: 'Memória',\n            memoryUsed: 'Memória Usada',\n            memoryTotal: 'Memória Total',\n            percent: 'Utilização',\n            base: 'Informações Básicas',\n            driverVersion: 'Versão do Driver',\n            cudaVersion: 'Versão do CUDA',\n            processMemoryUsage: 'Uso de Memória',\n            performanceStateHelper: 'De P0 (desempenho máximo) a P12 (desempenho mínimo)',\n            busID: 'Endereço do Barramento',\n            persistenceMode: 'Modo de Persistência',\n            enabled: 'Habilitado',\n            disabled: 'Desabilitado',\n            persistenceModeHelper:\n                'O modo de persistência responde às tarefas mais rapidamente, mas o consumo de energia em espera aumentará correspondentemente',\n            displayActive: 'Inicialização da GPU',\n            displayActiveT: 'Sim',\n            displayActiveF: 'Não',\n            ecc: 'Tecnologia de Verificação e Correção de Erros',\n            computeMode: 'Modo de Computação',\n            default: 'Padrão',\n            exclusiveProcess: 'Processo Exclusivo',\n            exclusiveThread: 'Thread Exclusiva',\n            prohibited: 'Proibido',\n            defaultHelper: 'Padrão: Os processos podem executar simultaneamente',\n            exclusiveProcessHelper:\n                'Processo Exclusivo: Apenas um contexto CUDA pode usar a GPU, mas pode ser compartilhado por múltiplas threads',\n            exclusiveThreadHelper: 'Thread Exclusiva: Apenas uma thread em um contexto CUDA pode usar a GPU',\n            prohibitedHelper: 'Proibido: A execução simultânea de processos não é permitida',\n            migModeHelper:\n                'Usado para criar instâncias MIG, implementando isolamento físico da GPU na camada do usuário.',\n            migModeNA: 'Não Suportado',\n            current: 'Monitoramento em Tempo Real',\n            history: 'Registros Históricos',\n            notSupport: 'A versão atual ou o driver não suportam exibir este parâmetro.',\n            processCount: 'Qtd. de processos',\n        },\n        mcp: {\n            server: 'Servidor MCP',\n            baseUrl: 'Caminho de Acesso Externo',\n            baseUrlHelper: 'Por exemplo: http://192.168.1.2:8000',\n            ssePath: 'Caminho SSE',\n            ssePathHelper: 'Por exemplo: /sse, tome cuidado para não duplicar com outros servidores',\n            environment: 'Variáveis de Ambiente',\n            envKey: 'Nome da Variável',\n            envValue: 'Valor da Variável',\n            externalUrl: 'Endereço de Conexão Externo',\n            operatorHelper: 'Será realizada a operação {1} no {0}, continuar?',\n            domain: 'Endereço de Acesso Padrão',\n            domainHelper: 'Por exemplo: 192.168.1.1 ou example.com',\n            bindDomain: 'Vincular Site',\n            commandPlaceHolder: 'Atualmente, apenas comandos de inicialização npx e binários são suportados',\n            importMcpJson: 'Importar Configuração do Servidor MCP',\n            importMcpJsonError: 'A estrutura mcpServers está incorreta',\n            bindDomainHelper:\n                'Após vincular o site, ele modificará o endereço de acesso de todos os servidores MCP instalados e fechará o acesso externo às portas',\n            outputTransport: 'Tipo de Saída',\n            streamableHttpPath: 'Caminho de Streaming',\n            streamableHttpPathHelper: 'Por exemplo: /mcp, certifique-se de que não se sobreponha a outros Servidores',\n            npxHelper: 'Adequado para mcp iniciado com npx ou binário',\n            uvxHelper: 'Adequado para mcp iniciado com uvx',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: 'Diretório do Modelo',\n            commandHelper:\n                'Se for necessário acesso externo, defina a porta no comando para ser a mesma que a porta do aplicativo',\n            imageAlert:\n                'Devido ao grande tamanho da imagem, recomenda-se baixar manualmente a imagem para o servidor antes da instalação',\n            modelSpeedup: 'Ativar aceleração de modelo',\n            modelType: 'Tipo de modelo',\n        },\n    },\n    container: {\n        createByCommand: 'Criar por comando',\n        importContainerBackupTip: 'Importar arquivo de backup de contêiner, apenas .tar.gz é suportado',\n        importComposeBackupTip: 'Importar arquivo de backup de compose, apenas .tar.gz é suportado',\n        stopContainerBeforeBackup: 'Parar contêiner antes do backup',\n        stopComposeBeforeBackup: 'Parar compose antes do backup',\n        stopBeforeBackupHelper:\n            'Quando habilitado, o contêiner ou serviço compose será interrompido antes do backup e restaurado automaticamente após a conclusão para garantir consistência dos dados.',\n        commandInput: 'Entrada de comando',\n        commandRule: 'Insira um comando docker run válido para criar o contêiner.',\n        commandHelper: 'Este comando será executado no servidor para criar o contêiner. Continuar?',\n        updateHelper1: 'Detectamos que este contêiner vem da loja de aplicativos. Observe os seguintes dois pontos:',\n        updateHelper2:\n            '1. As modificações atuais não serão sincronizadas com os aplicativos instalados na loja de aplicativos.',\n        updateHelper3:\n            '2. Se você modificar o aplicativo na página de instalados, o conteúdo atualmente editado se tornará inválido.',\n        updateHelper4:\n            'Editar o contêiner requer reconstrução, e qualquer dado não persistente será perdido. Deseja continuar?',\n        containerList: 'Lista de contêineres',\n        operatorHelper: '{0} será realizado no seguinte contêiner. Deseja continuar?',\n        operatorAppHelper:\n            'A operação \"{0}\" será realizada no(s) seguinte(s) contêiner(es) e pode afetar os serviços em execução. Deseja continuar?',\n        containerDeleteHelper:\n            \"Detectado que o contêiner é da App Store. Excluir o contêiner não o removerá completamente do 1Panel. Para excluí-lo completamente, vá para a App Store -> menus 'Instalado' ou 'Ambiente de Execução'. Continuar?\",\n        start: 'Iniciar',\n        stop: 'Parar',\n        restart: 'Reiniciar',\n        kill: 'Finalizar',\n        pause: 'Pausar',\n        unpause: 'Retomar',\n        rename: 'Renomear',\n        remove: 'Remover',\n        removeAll: 'Remover todos',\n        containerPrune: 'Limpar',\n        containerPruneHelper1: 'Isso excluirá todos os contêineres que estão no estado parado.',\n        containerPruneHelper2:\n            'Se os contêineres forem da loja de aplicativos, você precisará ir para \"Loja de Aplicativos -> Instalados\" e clicar no botão \"Reconstruir\" para reinstalá-los após a limpeza.',\n        containerPruneHelper3: 'Esta operação não pode ser desfeita. Deseja continuar?',\n        imagePrune: 'Limpar',\n        imagePruneSome: 'Limpar sem rótulo',\n        imagePruneSomeEmpty: 'Não há imagens com a tag \"none\" para limpar.',\n        imagePruneSomeHelper: 'Limpar as imagens com a tag \"none\" que não são usadas por nenhum contêiner.',\n        imagePruneAll: 'Limpar não utilizadas',\n        imagePruneAllEmpty: 'Não há imagens não utilizadas para limpar.',\n        imagePruneAllHelper: 'Limpar as imagens que não são usadas por nenhum contêiner.',\n        networkPrune: 'Limpar',\n        networkPruneHelper: 'Isso removerá todas as redes não utilizadas. Deseja continuar?',\n        volumePrune: 'Limpar',\n        volumePruneHelper: 'Isso removerá todos os volumes locais não utilizados. Deseja continuar?',\n        cleanSuccess: 'A operação foi bem-sucedida, o número desta limpeza: {0}!',\n        cleanSuccessWithSpace:\n            'A operação foi bem-sucedida. O número de discos limpos desta vez é {0}. O espaço em disco liberado foi {1}!',\n        unExposedPort: 'O endereço atual de mapeamento de porta é 127.0.0.1, o que não permite o acesso externo.',\n        upTime: 'Tempo de atividade',\n        fetch: 'Buscar',\n        lines: 'Linhas',\n        linesHelper: 'Por favor, insira o número correto de logs a serem recuperados!',\n        downloadLinesHelper: 'Selecione ou insira o número de linhas de log para download.',\n        lastDay: 'Último dia',\n        last4Hour: 'Últimas 4 horas',\n        lastHour: 'Última hora',\n        last10Min: 'Últimos 10 minutos',\n        cleanLog: 'Limpar log',\n        downLogHelper1: 'Isso fará o download de todos os logs do contêiner {0}. Deseja continuar?',\n        downLogHelper2: 'Isso fará o download dos últimos {0} logs do contêiner {0}. Deseja continuar?',\n        cleanLogHelper: 'Isso exigirá a reinicialização do contêiner e não poderá ser desfeito. Deseja continuar?',\n        newName: 'Novo nome',\n        source: 'Uso de recursos',\n        cpuUsage: 'Uso de CPU',\n        cpuTotal: 'CPU total',\n        core: 'Núcleo',\n        memUsage: 'Uso de memória',\n        memTotal: 'Limite de memória',\n        memCache: 'Cache de memória',\n        loadSize: 'Obter Tamanho do Contêiner',\n        ip: 'Endereço IP',\n        cpuShare: 'Atribuição de CPU',\n        cpuShareHelper:\n            'O mecanismo de contêiner usa um valor base de 1024 para a atribuição de CPU. Você pode aumentá-lo para dar mais tempo de CPU ao contêiner.',\n        inputIpv4: 'Exemplo: 192.168.1.1',\n        inputIpv6: 'Exemplo: 2001:0db8:85a3:0000:0000:8a2e:0370:7334',\n        diskUsage: 'Uso do Disco',\n        localVolume: 'Volume de Armazenamento Local',\n        buildCache: 'Cache de Build',\n        usage: 'Usado: {0}, Liberável: {1}',\n        clean: 'Liberar',\n        imageClean:\n            'Limpar imagens excluirá todas as imagens não utilizadas. Esta operação não pode ser desfeita. Continuar?',\n        containerClean:\n            'Limpar contêineres excluirá todos os contêineres parados (incluindo aplicativos parados da Loja de Aplicativos). Esta operação não pode ser desfeita. Continuar?',\n        sizeRw: 'Tamanho da Camada do Contêiner',\n        sizeRwHelper: 'Tamanho da camada gravável exclusiva do contêiner',\n        sizeRootFs: 'Tamanho Virtual',\n        sizeRootFsHelper:\n            'Tamanho total de todas as camadas de imagem das quais o contêiner depende + camada do contêiner',\n        containerFromAppHelper:\n            'Detectamos que este contêiner vem da loja de aplicativos. As operações no aplicativo podem fazer com que as edições atuais sejam invalidadas.',\n        containerFromAppHelper1:\n            'Clique no botão [Param] na lista de aplicativos instalados para acessar a página de edição e modificar o nome do contêiner.',\n        command: 'Comando',\n        console: 'Interação com o contêiner',\n        tty: 'Atribuir pseudo-TTY (-t)',\n        openStdin: 'Manter STDIN aberto mesmo que não esteja anexado (-i)',\n        custom: 'Personalizado',\n        emptyUser: 'Se estiver vazio, você fará login com o padrão',\n        privileged: 'Privilegiado',\n        privilegedHelper:\n            'Permite que o contêiner execute determinadas operações privilegiadas no host, o que pode aumentar os riscos do contêiner. Use com cautela!',\n        upgradeHelper: 'Nome do Repositório/Nome da Imagem: Versão da Imagem',\n        upgradeWarning2:\n            'A operação de upgrade requer a reconstrução do contêiner, e qualquer dado não persistente será perdido. Deseja continuar?',\n        oldImage: 'Imagem atual',\n        sameImageContainer: 'Contêineres com mesma imagem',\n        sameImageHelper: 'Contêineres usando a mesma imagem podem ser atualizados em lote após seleção',\n        targetImage: 'Imagem alvo',\n        imageLoadErr: 'Nenhum nome de imagem detectado para o contêiner',\n        imageUpdateTagEmpty: 'Nenhuma tag de imagem atualizável foi detectada',\n        appHelper: 'O contêiner vem da loja de aplicativos, e o upgrade pode tornar o serviço indisponível.',\n        input: 'Entrada manual',\n        forcePull: 'Sempre puxar imagem',\n        forcePullHelper:\n            'Isso ignorará as imagens existentes no servidor e puxará a imagem mais recente do repositório.',\n        imageUpdateHelper:\n            'Verificar a mesma tag no registry e, se houver atualização, fazer pull e atualizar a imagem local.',\n        server: 'Host',\n        serverExample: '80, 80-88, ip:80 ou ip:80-88',\n        containerExample: '80 ou 80-88',\n        exposePort: 'Expor porta',\n        exposeAll: 'Expor todas',\n        cmdHelper: 'Exemplo: nginx -g \"daemon off;\"',\n        entrypointHelper: 'Exemplo: docker-entrypoint.sh',\n        autoRemove: 'Remover automaticamente',\n        cpuQuota: 'Número de núcleos de CPU',\n        memoryLimit: 'Memória',\n        limitHelper: 'Se definido como 0, significa que não há limitação. O valor máximo é {0}',\n        mount: 'Montagem',\n        volumeOption: 'Volume',\n        hostOption: 'Host',\n        serverPath: 'Caminho do servidor',\n        containerDir: 'Caminho do contêiner',\n        networkEmptyHelper: 'Por favor confirme que a seleção de rede do contêiner está correta',\n        modeRW: 'RW',\n        modeR: 'R',\n        sharedLabel: 'Modo de Propagação',\n        private: 'Privado',\n        privateHelper: 'As alterações de montagem no container e no host não se afetam mutuamente',\n        rprivate: 'Privado Recursivo',\n        rprivateHelper: 'Todas as montagens no container estão completamente isoladas do host',\n        shared: 'Compartilhado',\n        sharedHelper: 'As alterações de montagem no host e no container são visíveis entre si',\n        rshared: 'Compartilhado Recursivo',\n        rsharedHelper: 'Todas as alterações de montagem no host e no container são visíveis entre sí',\n        slave: 'Escravo',\n        slaveHelper:\n            'O container pode ver as alterações de montagem do host, mas suas próprias alterações não afetam o host',\n        rslave: 'Escravo Recursivo',\n        rslaveHelper: 'Todas as montagens no container podem ver as alterações do host, mas não afetam o host',\n        mode: 'Modo',\n        env: 'Ambientes',\n        restartPolicy: 'Política de reinício',\n        always: 'sempre',\n        unlessStopped: 'a menos que parado',\n        onFailure: 'em falha (cinco vezes por padrão)',\n        no: 'nunca',\n        refreshTime: 'Intervalo de atualização',\n        cache: 'Cache',\n        image: 'Imagem | Imagens',\n        imagePull: 'Puxar',\n        imagePullHelper:\n            'Suporta selecionar múltiplas imagens para puxar, pressione Enter após inserir cada imagem para continuar',\n        imagePush: 'Enviar',\n        imagePushHelper:\n            'Detectado que esta imagem possui múltiplas tags. Por favor, confirme que o nome da imagem usada para push é: {0}',\n        imageDelete: 'Excluir imagem',\n        repoName: 'Registro de contêiner',\n        imageName: 'Nome da imagem',\n        pull: 'Puxar',\n        path: 'Caminho',\n        importImage: 'Importar',\n        buildArgs: 'Argumentos de Build',\n        imageBuild: 'Construção de imagem',\n        pathSelect: 'Caminho',\n        label: 'Etiqueta',\n        imageTag: 'Tag de imagem',\n        imageTagHelper:\n            'Suporta definir múltiplas tags de imagem, pressione Enter após inserir cada tag para continuar',\n        push: 'Enviar',\n        fileName: 'Nome do arquivo',\n        export: 'Exportar',\n        exportImage: 'Exportar imagem',\n        size: 'Tamanho',\n        tag: 'Tags',\n        tagHelper: 'Uma por linha. Por exemplo,\\nchave1=valor1\\nchave2=valor2',\n        imageNameHelper: 'Nome da imagem e tag, por exemplo: nginx:latest',\n        cleanBuildCache: 'Limpar cache de construção',\n        delBuildCacheHelper:\n            'Isso excluirá todos os artefatos em cache gerados durante as construções e não poderá ser desfeito. Deseja continuar?',\n        urlWarning: 'O prefixo da URL não precisa incluir http:// ou https://. Por favor, modifique.',\n        network: 'Rede | Redes',\n        networkHelper:\n            'Isso pode fazer com que alguns aplicativos e ambientes de execução não funcionem corretamente. Deseja continuar?',\n        networkName: 'Nome',\n        driver: 'Driver',\n        option: 'Opção',\n        attachable: 'Anexável',\n        parentNetworkCard: 'Placa de Rede Principal',\n        subnet: 'Sub-rede',\n        scope: 'Escopo IP',\n        gateway: 'Gateway',\n        auxAddress: 'Excluir IP',\n        volume: 'Volume | Volumes',\n        volumeDir: 'Diretório do volume',\n        nfsEnable: 'Habilitar armazenamento NFS',\n        nfsAddress: 'Endereço',\n        mountpoint: 'Ponto de montagem',\n        mountpointNFSHelper: 'Exemplo: /nfs, /nfs-share',\n        options: 'Opções',\n        repo: 'Registries',\n        httpRepoHelper: 'Operar um repositório do tipo HTTP requer reinicialização do serviço Docker.',\n        httpRepo:\n            'Escolher o protocolo HTTP requer reiniciar o serviço Docker para adicioná-lo a registries inseguros.',\n        delInsecure: 'Remover da lista de segurança',\n        delInsecureHelper:\n            'Isso reiniciará o serviço Docker para removê-lo dos registries inseguros. Deseja continuar?',\n        downloadUrl: 'Servidor',\n        imageRepo: 'Repositório de imagens',\n        repoHelper: 'Inclui repositório espelho/organização/projeto?',\n        auth: 'Exigir autenticação',\n        mirrorHelper:\n            'Se houver múltiplos espelhos, devem ser exibidos em novas linhas, por exemplo:\\nhttp://xxxxxx.m.daocloud.io \\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper:\n            'Se houver múltiplos repositórios privados, eles devem ser exibidos em novas linhas, por exemplo:\\n172.16.10.111:8081 \\n172.16.10.112:8081',\n        compose: 'Compose | Composições',\n        composeFile: 'Arquivo de Orquestração',\n        fromChangeHelper: 'Trocar a origem limpará o conteúdo editado atual. Deseja continuar?',\n        composePathHelper: 'Caminho de salvamento do arquivo de configuração: {0}',\n        composeHelper:\n            'A composição criada através do editor ou template do 1Panel será salva no diretório {0}/docker/compose.',\n        deleteFile: 'Excluir arquivo',\n        deleteComposeHelper:\n            'Excluir todos os arquivos relacionados à composição do container, incluindo arquivos de configuração e arquivos persistentes. Prossiga com cautela!',\n        deleteCompose: 'Excluir esta composição.',\n        composeDirectory: 'Diretório',\n        template: 'Template',\n        composeTemplate: 'Template de composição | Templates de composição',\n        content: 'Conteúdo',\n        contentEmpty: 'O conteúdo da composição não pode estar vazio, por favor, insira algo e tente novamente!',\n        containerNumber: 'Número de containers',\n        containerStatus: 'Status do container',\n        exited: 'Finalizado',\n        running: 'Em execução ( {0} / {1} )',\n        composeDetailHelper:\n            'A composição foi criada externamente ao 1Panel. As operações de iniciar e parar não são suportadas.',\n        composeOperatorHelper: 'A operação {1} será realizada no {0}. Deseja continuar?',\n        composeDownHelper:\n            'Isso irá parar e remover todos os containers e redes sob a composição {0}. Deseja continuar?',\n        composeEnvHelper2:\n            'Esta orquestração foi criada pela Loja de Aplicativos 1Panel. Modifique as variáveis de ambiente nos aplicativos instalados.',\n        composeContentPlaceholder: 'Escreva ou cole aqui o conteúdo do seu arquivo docker-compose',\n        setting: 'Configuração | Configurações',\n        restartHelper: 'As alterações de configuração exigem reiniciar o serviço Docker para entrarem em vigor',\n        operatorStatusHelper: 'Isso irá \"{0}\" o serviço Docker. Deseja continuar?',\n        dockerStatus: 'Serviço Docker',\n        daemonJsonPathHelper:\n            'Certifique-se de que o caminho de configuração seja o mesmo especificado no docker.service.',\n        mirrors: 'Espelhos de registro',\n        mirrorsHelper: '',\n        mirrorsHelper2: 'Para mais detalhes, consulte a documentação oficial.',\n        registries: 'Registries inseguros',\n        ipv6Helper:\n            'Ao habilitar o IPv6, é necessário adicionar uma rede de containers IPv6. Consulte a documentação oficial para etapas específicas de configuração.',\n        ipv6CidrHelper: 'Faixa de endereços IPv6 para containers',\n        ipv6TablesHelper: 'Configuração automática do Docker IPv6 para regras do iptables.',\n        experimentalHelper:\n            'Habilitar ip6tables requer que esta configuração esteja ativada; caso contrário, ip6tables será ignorado',\n        cutLog: 'Opção de log',\n        cutLogHelper1: 'A configuração atual afetará apenas containers recém-criados.',\n        cutLogHelper2: 'Containers existentes precisam ser recriados para que a configuração tenha efeito.',\n        cutLogHelper3:\n            'Observe que recriar containers pode resultar em perda de dados. Se seus containers contiverem dados importantes, faça backup antes de realizar a operação de reconstrução.',\n        maxSize: 'Tamanho máximo',\n        maxFile: 'Arquivo máximo',\n        liveHelper:\n            'Por padrão, quando o daemon Docker termina, ele desliga os containers em execução. Você pode configurar o daemon para que os containers permaneçam em execução se o daemon ficar indisponível. Essa funcionalidade é chamada de \"restauração ao vivo\". A opção de restauração ao vivo ajuda a reduzir o tempo de inatividade dos containers devido a falhas do daemon, interrupções planejadas ou atualizações.',\n        liveWithSwarmHelper: 'A configuração de restauração ao vivo é incompatível com o modo swarm.',\n        iptablesDisable: 'Desabilitar iptables',\n        iptablesHelper1: 'Configuração automática das regras do iptables para Docker.',\n        iptablesHelper2:\n            'Desabilitar iptables fará com que os containers não consigam se comunicar com redes externas.',\n        daemonJsonPath: 'Caminho de configuração',\n        serviceUnavailable: 'O serviço Docker não foi iniciado no momento.',\n        startIn: 'para iniciar',\n        sockPath: 'Caminho do socket Unix',\n        sockPathHelper: 'Canal de comunicação entre o daemon Docker e o cliente.',\n        sockPathHelper1: 'Caminho padrão: /var/run/docker-x.sock',\n        sockPathMsg:\n            'Salvar a configuração do Caminho do Socket pode tornar o serviço Docker indisponível. Deseja continuar?',\n        sockPathErr: 'Por favor, selecione ou insira o caminho correto do arquivo do Docker sock',\n        related: 'Relacionado',\n        includeAppstore: 'Exibir containers da loja de aplicativos',\n        excludeAppstore: 'Ocultar Contêiner da Loja de Aplicativos',\n        cleanDockerDiskZone: 'Limpar o espaço em disco usado pelo Docker',\n        cleanImagesHelper: '(Limpar todas as imagens que não são usadas por nenhum container)',\n        cleanContainersHelper: '(Limpar todos os containers parados)',\n        cleanVolumesHelper: '(Limpar todos os volumes locais não usados)',\n        makeImage: 'Criar imagem',\n        newImageName: 'Novo nome da imagem',\n        commitMessage: 'Mensagem de commit',\n        author: 'Autor',\n        ifPause: 'Pausar container durante a criação',\n        ifMakeImageWithContainer: 'Criar nova imagem a partir deste container?',\n        finishTime: 'Horário da última parada',\n        workingDir: 'Diretório de trabalho',\n        resource: 'Recurso',\n        macAddr: 'Endereço MAC',\n        volumeHelper: 'Garanta que o conteúdo do volume esteja correto',\n        exportHelper: 'Exporte as imagens selecionadas em um único arquivo tar.',\n        goSetting: 'Ir para editar',\n    },\n    cronjob: {\n        importHelper:\n            'Tarefas agendadas duplicadas serão automaticamente ignoradas durante a importação. As tarefas serão definidas como status 【Desativado】 por padrão, e como status 【Aguardando Edição】 quando a associação de dados for anormal.',\n        changeStatus: 'Alterar status',\n        disableMsg: 'Isso irá parar a execução automática da tarefa agendada. Você deseja continuar?',\n        enableMsg: 'Isso permitirá que a tarefa agendada seja executada automaticamente. Você deseja continuar?',\n        taskType: 'Tipo',\n        record: 'Registros',\n        viewRecords: 'Visualizar registros',\n        shell: 'Shell',\n        stop: 'Parada Manual',\n        stopHelper: 'Esta operação forçará a parada da execução da tarefa atual. Continuar?',\n        log: 'Logs de backup',\n        logHelper: 'Backup do log do sistema',\n        ogHelper1: '1. Log do sistema 1Panel',\n        logHelper2: '2. Log de login SSH do servidor',\n        logHelper3: '3. Todos os logs do site',\n        containerCheckBox: 'No container (não é necessário inserir o comando do container)',\n        containerName: 'Nome do container',\n        ntp: 'Sincronização de tempo',\n        ntp_helper: 'Você pode configurar o servidor NTP na página de Configuração Rápida da Caixa de Ferramentas.',\n        app: 'Backup de app',\n        website: 'Backup de site',\n        rulesHelper: 'Suporta múltiplas regras de exclusão, separadas por vírgulas inglesas , ex.: *.log,*.sql',\n        lastRecordTime: 'Última execução',\n        all: 'Todos',\n        failedRecord: 'Registros de falha',\n        successRecord: 'Registros de sucesso',\n        database: 'Backup de banco de dados',\n        backupArgs: 'Argumentos de Backup',\n        backupArgsHelper:\n            'Argumentos de backup não listados podem ser inseridos e selecionados manualmente. Por exemplo: Digite --no-data e selecione a primeira opção da lista suspensa.',\n        singleTransaction:\n            'Faz backup de tabelas InnoDB usando uma única transação, adequado para backups de dados de grande volume',\n        quick: 'Lê dados linha por linha em vez de carregar toda a tabela na memória, adequado para backups de dados de grande volume e máquinas com pouca memória',\n        skipLockTables: 'Backup sem bloquear todas as tabelas, adequado para bancos de dados altamente concorrentes',\n        missBackupAccount: 'A conta de backup não foi encontrada',\n        syncDate: 'Data de sincronização',\n        clean: 'Limpeza de cache',\n        curl: 'URL de acesso',\n        taskName: 'Nome',\n        cronSpec: 'Ciclo de execução',\n        cronSpeDoc:\n            'Ciclos de execução personalizados suportam apenas o formato [minuto hora dia mês semana], por exemplo, 0 0 * * *. Consulte a documentação oficial para obter detalhes.',\n        cronSpecHelper: 'Digite o período correto de execução',\n        cleanHelper:\n            'Esta operação registra todos os registros de execução de tarefas, arquivos de backup e logs. Você deseja continuar?',\n        directory: 'Diretório de backup',\n        sourceDir: 'Diretório de backup',\n        snapshot: 'Snapshot do sistema',\n        allOptionHelper:\n            'O plano de tarefa atual é fazer backup de todos os [{0}]. O download direto não é suportado no momento. Você pode verificar a lista de backups no menu [{0}].',\n        exclusionRules: 'Regras de exclusão',\n        exclusionRulesHelper:\n            'Selecione ou insira regras de exclusão, pressione Enter após cada conjunto para continuar. As regras de exclusão se aplicarão a todas as operações de compactação neste backup',\n        default_download_path: 'Link de download padrão',\n        saveLocal: 'Manter backups locais (o mesmo número de cópias na nuvem)',\n        url: 'Endereço URL',\n        urlHelper: 'Por favor insira um endereço URL válido',\n        targetHelper: 'As contas de backup são mantidas nas configurações do painel.',\n        withImageHelper:\n            'Fazer backup das imagens da loja de aplicativos, mas isso aumentará o tamanho do arquivo de snapshot.',\n        ignoreApp: 'Excluir aplicativos',\n        withImage: 'Backup da Imagem do Aplicativo',\n        retainCopies: 'Manter cópias',\n        retryTimes: 'Tentativas de Repetição',\n        timeout: 'Tempo Limite',\n        ignoreErr: 'Ignorar erros',\n        ignoreErrHelper: 'Ignorar erros durante o backup para garantir a execução de todas as tarefas de backup',\n        retryTimesHelper: '0 significa não repetir após falha',\n        retainCopiesHelper: 'Número de cópias a serem mantidas para registros de execução e logs',\n        retainCopiesHelper1: 'Número de cópias a serem mantidas para arquivos de backup',\n        retainCopiesUnit: ' cópias (Visualizar)',\n        cronSpecRule: 'O formato do período de execução na linha {0} está incorreto. Verifique e tente novamente!',\n        perMonth: 'Todo mês',\n        perWeek: 'Toda semana',\n        perHour: 'Toda hora',\n        perNDay: 'A cada N dia(s)',\n        perDay: 'Todo dia',\n        perNHour: 'A cada N hora(s)',\n        perNMinute: 'A cada N minuto(s)',\n        perNSecond: 'A cada N segundo(s)',\n        per: 'A cada ',\n        handle: '',\n        day: 'dia(s)',\n        dayUnit: 'd',\n        monday: 'Segunda-feira',\n        tuesday: 'Terça-feira',\n        wednesday: 'Quarta-feira',\n        thursday: 'Quinta-feira',\n        friday: 'Sexta-feira',\n        saturday: 'Sábado',\n        sunday: 'Domingo',\n        shellContent: 'Script',\n        errRecord: 'Registro incorreto',\n        errHandle: 'Falha na execução do Cronjob',\n        noRecord: 'Acione a tarefa Cron e você verá os registros aqui.',\n        cleanData: 'Limpar dados',\n        cleanRemoteData: 'Excluir dados remotos',\n        cleanDataHelper: 'Excluir o arquivo de backup gerado durante esta tarefa.',\n        noLogs: 'Ainda não há saída de tarefa...',\n        errPath: 'Caminho de backup [{0}] com erro, não é possível fazer o download!',\n        cutWebsiteLog: 'Rotação de log do site',\n        cutWebsiteLogHelper: 'Os arquivos de log rotacionados serão salvos no diretório de backup do 1Panel.',\n        syncIpGroup: 'Sincronizar grupos de IP do WAF',\n        requestExpirationTime: 'Tempo de expiração da solicitação de upload (Horas)',\n        unitHours: 'Unidade: Horas',\n        alertTitle: 'Tarefa Planejada - {0} 「{1}」 Alerta de Falha na Tarefa',\n        library: {\n            script: 'Script',\n            syncNow: 'Sincronizar Agora',\n            turnOnSync: 'Ativar Sincronização Automática',\n            turnOnSyncHelper:\n                'Ativar a sincronização automática realizará sincronizações automáticas durante as primeiras horas da manhã diariamente',\n            turnOffSync: 'Desativar Sincronização Automática',\n            turnOffSyncHelper:\n                'Desativar a sincronização automática pode causar atrasos na sincronização de scripts, confirmar?',\n            isInteractive: 'Interativo',\n            interactive: 'Script interativo',\n            interactiveHelper:\n                'Requer entrada do usuário durante a execução e não pode ser usado em tarefas agendadas.',\n            library: 'Biblioteca de Scripts',\n            remoteLibrary: 'Biblioteca de Scripts Remota',\n            create: 'Adicionar Script',\n            edit: 'Editar Script',\n            groupHelper:\n                'Defina grupos diferentes com base nas características do script, o que permite operações de filtragem de scripts mais rápidas.',\n            handleHelper: 'Executar o script {1} em {0}, continuar?',\n            noSuchApp:\n                'O serviço {0} não foi detectado. Por favor, instale-o rapidamente usando a biblioteca de scripts primeiro!',\n            syncHelper:\n                'Preparando para sincronizar a biblioteca de scripts do sistema. Esta operação afeta apenas scripts do sistema. Continuar?',\n        },\n        nextTime: 'Próximas 5 execuções',\n        logHelper1: '1. Log do sistema 1Panel',\n        cronSpecDoc:\n            'Ciclos personalizados suportam apenas o formato [minuto hora dia mês semana], ex.: 0 0 * * *. Veja a documentação oficial.',\n        backupContent: 'Conteúdo do backup',\n        cronSpecRule2: 'Formato do período de execução inválido, verifique e tente novamente!',\n        perMonthHelper: 'Executar no dia {0} de cada mês às {1}:{2}',\n        perWeekHelper: 'Executar toda semana em {0} às {1}:{2}',\n        perDayHelper: 'Executar todos os dias às {0}:{1}',\n        perHourHelper: 'Executar a cada hora no minuto {0}',\n        perNDayHelper: 'Executar a cada {0} dias às {1}:{2}',\n        perNHourHelper: 'Executar a cada {0} horas em {1}',\n        perNMinuteHelper: 'Executar a cada {0} minutos',\n        perNSecondHelper: 'Executar a cada {0} segundos',\n        executor: 'Executor',\n        cleanLog: 'Limpeza automática de logs',\n        cleanLogscope: 'Retenção de logs',\n    },\n    monitor: {\n        globalFilter: 'Filtro Global',\n        enableMonitor: 'Status de Monitoramento',\n        storeDays: 'Dias de Retenção',\n        defaultNetwork: 'Placa de Rede Padrão',\n        defaultNetworkHelper: 'Opção de placa de rede padrão exibida nas interfaces de monitoramento e visão geral',\n        defaultIO: 'Disco Padrão',\n        defaultIOHelper: 'Opção de disco padrão exibida nas interfaces de monitoramento e visão geral',\n        cleanMonitor: 'Limpar Registros de Monitoramento',\n        cleanHelper: 'Esta operação limpará todos os registros de monitoramento, incluindo GPU. Continuar?',\n        avgLoad: 'Média de carga',\n        loadDetail: 'Detalhes da carga',\n        resourceUsage: 'Utilização',\n        networkCard: 'Interface de rede',\n        read: 'Leitura',\n        write: 'Gravação',\n        readWriteCount: 'Operações de I/O',\n        readWriteTime: 'Latência de I/O',\n        today: 'Hoje',\n        yesterday: 'Ontem',\n        lastNDay: 'Últimos {0} dias',\n        lastNMonth: 'Últimos {0} meses',\n        lastHalfYear: 'Último semestre',\n        memory: 'Memória',\n        percent: 'Percentual',\n        cache: 'Cache',\n        disk: 'Disco',\n        network: 'Rede',\n        up: 'Para cima',\n        down: 'Para baixo',\n        interval: 'Intervalo de Coleta',\n        intervalHelper: 'Insira um intervalo de coleta de monitoramento apropriado (10 segundos - 12 horas)',\n    },\n    terminal: {\n        local: 'Local',\n        defaultConn: 'Conexão Padrão',\n        defaultConnHelper:\n            'Esta operação conectará automaticamente ao terminal do nó após abrir o terminal para 【{0}】. Continuar?',\n        withReset: 'Redefinir Informações de Conexão',\n        localConnJump:\n            'As informações de conexão padrão são mantidas em [Terminal - Configurações]. Se a conexão falhar, edite lá!',\n        localHelper: 'O nome local é usado apenas para identificação local do sistema.',\n        connLocalErr:\n            'Невозможно автоматически аутентифицироваться, пожалуйста, заполните информацию для входа на локальный сервер.',\n        testConn: 'Testar conexão',\n        saveAndConn: 'Salvar e conectar',\n        connTestOk: 'Informações de conexão disponíveis',\n        connTestFailed: 'Conexão indisponível, por favor, verifique as informações de conexão.',\n        host: 'Host | Hosts',\n        createConn: 'Nova conexão',\n        manageGroup: 'Gerenciar grupos',\n        noHost: 'Nenhum host',\n        groupChange: 'Alterar grupo',\n        expand: 'Expandir todos',\n        fold: 'Contrair tudo',\n        batchInput: 'Processamento em lote',\n        quickCommand: 'Comando rápido | Comandos rápidos',\n        noSuchCommand:\n            'Nenhum dado de comando rápido encontrado no arquivo CSV importado, verifique e tente novamente!',\n        quickCommandHelper: 'Você pode usar os comandos rápidos na parte inferior de \"Terminais -> Terminais\".',\n        groupDeleteHelper:\n            'Após o grupo ser removido, todas as conexões no grupo serão migradas para o grupo padrão. Você deseja continuar?',\n        command: 'Comando',\n        quickCmd: 'Comando rápido',\n        addHost: 'Adicionar',\n        localhost: 'Localhost',\n        ip: 'Endereço',\n        authMode: 'Autenticação',\n        passwordMode: 'Senha',\n        rememberPassword: 'Lembrar informações de autenticação',\n        keyMode: 'Chave privada',\n        key: 'Chave privada',\n        keyPassword: 'Senha da chave privada',\n        emptyTerminal: 'Nenhum terminal está conectado no momento.',\n        lineHeight: 'Altura da linha',\n        letterSpacing: 'Espaçamento entre letras',\n        fontSize: 'Tamanho da fonte',\n        fontFamily: 'Conjunto de fontes',\n        fontFamilySupportHelper:\n            'Selecione ou digite fontes. O efeito depende de a fonte estar instalada no sistema do navegador; se não estiver, haverá fallback automático. Se estiver vazio, a fonte padrão será usada.',\n        backgroundColor: 'Cor de fundo',\n        foregroundColor: 'Cor do texto',\n        cursorBlink: 'Piscar do cursor',\n        cursorStyle: 'Estilo do cursor',\n        cursorUnderline: 'Sublinhado',\n        cursorBlock: 'Bloco',\n        cursorBar: 'Barra',\n        scrollback: 'Scrollback',\n        scrollSensitivity: 'Sensibilidade de rolagem',\n        aiStatus: 'AI Terminal',\n        aiSettings: 'AI Terminal Settings',\n        aiAccountHelper:\n            'Use a conta de modelo selecionada para gerar e preencher comandos. Para modelos locais como Ollama e vLLM, use uma conta de modelo personalizada.',\n        aiPrefix: 'Trigger Prefix',\n        aiPrefixHelper:\n            'When a line starts with this prefix and you press Enter, AI command generation will be triggered, for example # or //ai.',\n        aiRiskCommands: 'Risk Command Interception',\n        aiRiskCommandsHelper:\n            'Generated commands matching any of these fragments will be blocked and filled back as comments. Supports add, edit, and delete.',\n        aiAddRiskCommand: 'Add Risk Command',\n        aiRemoveRiskCommand: 'Delete',\n        aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',\n        aiPrefixAsciiVisible:\n            'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',\n        saveHelper: 'Tem certeza de que deseja salvar a configuração atual do terminal?',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: 'Para alguns problemas de instalação e uso, consulte',\n        },\n        swap: {\n            swap: 'Partição Swap',\n            swapHelper1:\n                'O tamanho do swap deve ser de 1 a 2 vezes a memória física, ajustável conforme os requisitos específicos;',\n            swapHelper2:\n                'Antes de criar um arquivo swap, verifique se o disco do sistema tem espaço disponível suficiente, pois o tamanho do arquivo swap ocupará o espaço correspondente no disco;',\n            swapHelper3:\n                'Swap pode ajudar a aliviar a pressão de memória, mas é apenas uma alternativa. A dependência excessiva de swap pode levar a uma diminuição no desempenho do sistema. É recomendável priorizar o aumento de memória ou otimizar o uso de memória do aplicativo;',\n            swapHelper4:\n                'É aconselhável monitorar regularmente o uso de swap para garantir o funcionamento normal do sistema.',\n            swapDeleteHelper:\n                'Esta operação removerá a partição Swap {0}. Por motivos de segurança do sistema, o arquivo correspondente não será excluído automaticamente. Se a exclusão for necessária, por favor, faça manualmente!',\n            saveHelper: 'Por favor, salve as configurações atuais primeiro!',\n            saveSwap:\n                'Salvar a configuração atual ajustará o tamanho da partição Swap {0} para {1}. Você deseja continuar?',\n            swapMin: 'O tamanho mínimo da partição é 40 KB. Por favor, modifique e tente novamente!',\n            swapMax: 'O valor máximo para o tamanho da partição é {0}. Por favor, modifique e tente novamente!',\n            swapOff: 'O tamanho mínimo da partição é 40 KB. Definir como 0 desabilitará a partição Swap.',\n        },\n        device: {\n            dnsHelper: 'Servidor DNS',\n            dnsAlert: 'Atenção! Alterar /etc/resolv.conf fará o arquivo voltar ao padrão após reiniciar o sistema.',\n            dnsHelper1:\n                'Quando houver múltiplos DNS, coloque cada um em uma nova linha. Ex.:\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: 'Resolução de hostname',\n            hosts: 'Domínio',\n            toolbox: 'Configurações rápidas',\n            hostname: 'Hostname',\n            passwd: 'Senha do sistema',\n            passwdHelper: 'Os caracteres de entrada não podem incluir $ e &',\n            timeZone: 'Fuso horário do sistema',\n            localTime: 'Hora do servidor',\n            timeZoneChangeHelper: 'Alterar o fuso horário do sistema exige reiniciar o serviço. Continuar?',\n            timeZoneHelper:\n                'Se o comando \"timedatectl\" não estiver instalado, talvez não seja possível alterar o fuso horário.',\n            timeZoneCN: 'Pequim',\n            timeZoneAM: 'Los Angeles',\n            timeZoneNY: 'Nova York',\n            ntpALi: 'Alibaba',\n            ntpGoogle: 'Google',\n            syncSite: 'Servidor NTP',\n            syncSiteHelper: 'Sincronizar hora do sistema com {0}?',\n            hostnameHelper:\n                'A alteração do hostname depende do comando \"hostnamectl\". Se não estiver instalado, a alteração pode falhar.',\n            userHelper:\n                'O nome de usuário depende do comando \"whoami\". Se não estiver instalado, a leitura pode falhar.',\n            passwordHelper:\n                'A alteração de senha depende do comando \"chpasswd\". Se não estiver instalado, a alteração pode falhar.',\n            hostHelper: 'Há valor vazio no conteúdo fornecido. Verifique e tente novamente após ajustar!',\n            dnsCheck: 'Testar disponibilidade',\n            dnsOK: 'Configuração DNS disponível!',\n            dnsTestFailed: 'Configuração DNS indisponível.',\n        },\n        fail2ban: {\n            sshPort: 'Monitorar porta SSH',\n            sshPortHelper: 'O Fail2ban atual monitora a porta SSH de conexão do host',\n            unActive: 'O serviço Fail2ban não está habilitado no momento.',\n            operation: 'Você executará a operação \"{0}\" no serviço Fail2ban. Continuar?',\n            fail2banChange: 'Modificar configuração do Fail2ban',\n            ignoreHelper: 'Os IPs da lista de permissão serão ignorados no bloqueio. Continuar?',\n            bannedHelper: 'Os IPs da lista de bloqueio serão bloqueados pelo servidor. Continuar?',\n            maxRetry: 'Máximo de tentativas',\n            banTime: 'Tempo de bloqueio',\n            banTimeHelper: 'Tempo padrão de bloqueio é 10 minutos; -1 indica bloqueio permanente',\n            banTimeRule: 'Digite um tempo de bloqueio válido ou -1',\n            banAllTime: 'Bloqueio permanente',\n            findTime: 'Período de detecção',\n            banAction: 'Ação de bloqueio',\n            banActionOption: 'Bloquear IPs especificados usando {0}',\n            allPorts: ' (Todas as portas)',\n            ignoreIP: 'Lista de permissão de IP',\n            bannedIP: 'Lista de bloqueio de IP',\n            logPath: 'Caminho do log',\n            logPathHelper: 'Padrão: /var/log/secure ou /var/log/auth.log',\n        },\n        ftp: {\n            ftp: 'Conta FTP | Contas FTP',\n            notStart: 'O serviço FTP não está em execução. Inicie-o primeiro!',\n            operation: 'Isto executará a operação \"{0}\" no serviço FTP. Continuar?',\n            noPasswdMsg: 'Não foi possível obter a senha atual da conta FTP. Defina a senha e tente novamente.',\n            enableHelper: 'Ativar a conta FTP selecionada restaurará suas permissões de acesso. Continuar?',\n            disableHelper: 'Desativar a conta FTP selecionada revogará suas permissões de acesso. Continuar?',\n            syncHelper: 'Sincronizar dados de conta FTP entre servidor e banco de dados. Continuar?',\n            dirSystem:\n                'Este diretório é reservado pelo sistema. Alterar pode causar falha do sistema; ajuste e tente novamente!',\n            dirHelper: 'Ativar FTP exige alteração de permissões de diretório; escolha com cuidado',\n            dirMsg: 'Ativar FTP modificará permissões de todo o diretório {0}. Continuar?',\n        },\n        clam: {\n            clam: 'Varredura de vírus',\n            cron: 'Varredura agendada',\n            cronHelper: 'A versão profissional suporta varredura agendada',\n            specErr: 'Formato do agendamento inválido, verifique e tente novamente!',\n            disableMsg: 'Parar a execução agendada impedirá a execução automática desta tarefa. Continuar?',\n            enableMsg:\n                'Ativar a execução agendada fará esta tarefa executar automaticamente em intervalos regulares. Continuar?',\n            showFresh: 'Mostrar serviço de atualização de assinaturas',\n            hideFresh: 'Ocultar serviço de atualização de assinaturas',\n            clamHelper:\n                'Configuração mínima recomendada para ClamAV: 3 GiB de RAM ou mais, CPU de 1 núcleo com 2.0 GHz+ e ao menos 5 GiB de disco livre.',\n            notStart: 'O serviço ClamAV não está em execução. Inicie-o primeiro!',\n            removeRecord: 'Excluir arquivos de relatório',\n            noRecords: 'Clique no botão \"Executar\" para iniciar a varredura e ver os registros aqui.',\n            removeInfected: 'Excluir arquivos de vírus',\n            removeInfectedHelper:\n                'Excluir arquivos infectados detectados na tarefa para garantir segurança e operação normal do servidor.',\n            clamCreate: 'Criar regra de varredura',\n            infectedStrategy: 'Estratégia para infectados',\n            removeHelper: 'Excluir arquivos de vírus, escolha com cuidado!',\n            move: 'Mover',\n            moveHelper: 'Mover arquivos de vírus para o diretório especificado',\n            copyHelper: 'Copiar arquivos de vírus para o diretório especificado',\n            none: 'Não fazer nada',\n            noneHelper: 'Não executar ação em arquivos de vírus',\n            scanDir: 'Diretório da varredura',\n            infectedDir: 'Diretório infectado',\n            scanDate: 'Data da varredura',\n            scanResult: 'Final do log de varredura',\n            tail: 'Linhas',\n            infectedFiles: 'Arquivos infectados',\n            log: 'Detalhes',\n            clamConf: 'Daemon Clam AV',\n            clamLog: 'Logs de @:toolbox.clam.clamConf',\n            freshClam: 'FreshClam',\n            freshClamLog: 'Logs de @:toolbox.clam.freshClam',\n            alertHelper: 'A versão profissional suporta varredura agendada e alerta por SMS',\n            alertTitle: 'Tarefa de varredura de vírus \"{0}\" detectou arquivo infectado',\n        },\n    },\n    device: {\n        dnsHelper: 'Servidor DNS',\n        dnsAlert:\n            'Atenção! Modificar a configuração do arquivo /etc/resolv.conf restaurará o arquivo para seus valores padrão após a reinicialização do sistema.',\n        dnsHelper1:\n            'Quando houver várias entradas DNS, elas devem ser exibidas em novas linhas. Exemplo:\\n114.114.114.114\\n8.8.8.8',\n        hostsHelper: 'Resolução de hostname',\n        hosts: 'Domínio',\n        hostAlert:\n            'Registros comentados ocultos, clique no botão \"Todas as configurações\" para visualizar ou configurar',\n        toolbox: 'Configurações rápidas',\n        hostname: 'Nome do host',\n        passwd: 'Senha do sistema',\n        passwdHelper: 'Os caracteres de entrada não podem incluir $ e &',\n        timeZone: 'Fuso horário',\n        localTime: 'Hora do servidor',\n        timeZoneChangeHelper:\n            'Modificar o fuso horário do sistema requer a reinicialização do serviço. Deseja continuar?',\n        timeZoneHelper:\n            'Se você não instalar o comando \"timedatectl\", poderá não conseguir alterar o fuso horário. O sistema usa esse comando para alterar o fuso horário.',\n        timeZoneCN: 'Pequim',\n        timeZoneAM: 'Los Angeles',\n        timeZoneNY: 'Nova York',\n        ntpALi: 'Alibaba',\n        ntpGoogle: 'Google',\n        syncSite: 'Servidor NTP',\n        hostnameHelper:\n            'A modificação do nome do host depende do comando \"hostnamectl\". Se o comando não estiver instalado, a modificação pode falhar.',\n        userHelper:\n            'O nome de usuário depende do comando \"whoami\" para recuperação. Se o comando não estiver instalado, a recuperação pode falhar.',\n        passwordHelper:\n            'A modificação da senha depende do comando \"chpasswd\". Se o comando não estiver instalado, a modificação pode falhar.',\n        hostHelper: 'Há um valor vazio no conteúdo fornecido. Verifique e tente novamente após a modificação!',\n        dnsCheck: 'Testar disponibilidade',\n        dnsOK: 'As informações de configuração do DNS estão disponíveis!',\n        dnsTestFailed: 'As informações de configuração do DNS não estão disponíveis.',\n    },\n    fail2ban: {\n        sshPort: 'Porta de escuta do SSH',\n        sshPortHelper: 'O Fail2ban atual escuta a porta de conexão SSH do host',\n        unActive: 'O serviço Fail2ban não está ativado no momento.',\n        operation: 'Você realizará a operação \"{0}\" no serviço Fail2ban. Deseja continuar?',\n        fail2banChange: 'Modificação da configuração do Fail2ban',\n        ignoreHelper: 'A lista de IPs na lista de permissão será ignorada para bloqueio. Deseja continuar?',\n        bannedHelper: 'A lista de IPs na lista de bloqueio será bloqueada pelo servidor. Deseja continuar?',\n        maxRetry: 'Máximo de tentativas de reclusão',\n        banTime: 'Tempo de banimento',\n        banTimeHelper: 'O tempo de banimento padrão é 10 minutos, -1 indica banimento permanente',\n        banTimeRule: 'Por favor, insira um tempo de banimento válido ou -1',\n        banAllTime: 'Banimento permanente',\n        findTime: 'Período de descoberta',\n        banAction: 'Ação de bloqueio',\n        banActionOption: 'Bloquear endereços IP especificados usando {0}',\n        allPorts: ' (Todas as portas)',\n        ignoreIP: 'Lista de permissão de IP',\n        bannedIP: 'Lista de bloqueio de IP',\n        logPath: 'Caminho do log',\n        logPathHelper: 'O padrão é /var/log/secure ou /var/log/auth.log',\n    },\n    ftp: {\n        ftp: 'Conta FTP | Contas FTP',\n        notStart: 'O serviço FTP não está em execução, por favor, inicie-o primeiro!',\n        operation: 'Isso realizará a operação \"{0}\" no serviço FTP. Deseja continuar?',\n        noPasswdMsg: 'Não foi possível obter a senha atual da conta FTP, por favor, defina a senha e tente novamente!',\n        enableHelper: 'Ativar a conta FTP selecionada restaurará suas permissões de acesso. Deseja continuar?',\n        disableHelper: 'Desativar a conta FTP selecionada revogará suas permissões de acesso. Deseja continuar?',\n        syncHelper: 'Sincronizar os dados da conta FTP entre o servidor e o banco de dados. Deseja continuar?',\n        dirSystem:\n            'Este diretório é reservado do sistema. Modificações podem causar falhas no sistema. Por favor, modifique e tente novamente!',\n        dirHelper: 'Habilitar FTP requer alterações nas permissões do diretório - por favor, escolha com cuidado',\n        dirMsg: 'Habilitar FTP modificará as permissões de todo o diretório {0}. Continuar?',\n    },\n    clam: {\n        clam: 'Scan de vírus',\n        cron: 'Scan agendado',\n        cronHelper: 'A versão profissional suporta a funcionalidade de scan agendado',\n        specErr: 'Erro no formato do agendamento, por favor, verifique e tente novamente!',\n        disableMsg:\n            'Parar a execução agendada impedirá que esta tarefa de scan seja executada automaticamente. Deseja continuar?',\n        enableMsg:\n            'Ativar a execução agendada permitirá que esta tarefa de scan seja executada automaticamente em intervalos regulares. Deseja continuar?',\n        showFresh: 'Mostrar serviço de atualização de assinaturas',\n        hideFresh: 'Ocultar serviço de atualização de assinaturas',\n        clamHelper:\n            'A configuração mínima recomendada para o ClamAV é: 3 GiB de RAM ou mais, CPU de 1 núcleo com 2.0 GHz ou superior, e pelo menos 5 GiB de espaço livre no disco rígido.',\n        notStart: 'O serviço ClamAV não está em execução, por favor, inicie-o primeiro!',\n        removeRecord: 'Excluir arquivos de relatório',\n        noRecords: 'Clique no botão \"Acionar\" para iniciar o scan e você verá registros aqui.',\n        removeInfected: 'Excluir arquivos infectados',\n        removeInfectedHelper:\n            'Excluir arquivos de vírus detectados durante a tarefa para garantir a segurança e o funcionamento normal do servidor.',\n        clamCreate: 'Criar regra de scan',\n        infectedStrategy: 'Estratégia de arquivos infectados',\n        removeHelper: 'Excluir arquivos de vírus, escolha com cuidado!',\n        move: 'Mover',\n        moveHelper: 'Mover arquivos de vírus para o diretório especificado',\n        copyHelper: 'Copiar arquivos de vírus para o diretório especificado',\n        none: 'Não fazer nada',\n        noneHelper: 'Não tomar nenhuma ação sobre arquivos de vírus',\n        scanDir: 'Diretório de scan',\n        infectedDir: 'Diretório de arquivos infectados',\n        scanDate: 'Data do scan',\n        scanResult: 'Últimos logs de scan',\n        tail: 'Linhas',\n        infectedFiles: 'Arquivos infectados',\n        log: 'Detalhes',\n        clamConf: 'Daemon Clam AV',\n        clamLog: '@:toolbox.clam.clamConf logs',\n        freshClam: 'FreshClam',\n        freshClamLog: '@:toolbox.clam.freshClam logs',\n        alertHelper: 'A versão profissional suporta scan agendado e alerta por SMS',\n        alertTitle: 'Tarefa de scan de vírus 「{0}」 detectou alerta de arquivo infectado',\n    },\n    logs: {\n        core: 'Serviço de Painel',\n        agent: 'Monitoramento de Nós',\n        panelLog: 'Logs do painel',\n        operation: 'Logs de operação',\n        login: 'Logs de login',\n        loginIP: 'IP de login',\n        loginAddress: 'Endereço de login',\n        loginAgent: 'Agente de login',\n        loginStatus: 'Status',\n        system: 'Logs do sistema',\n        deleteLogs: 'Limpar logs',\n        resource: 'Recurso',\n        detail: {\n            dashboard: 'Visão Geral',\n            ai: 'AI',\n            groups: 'Grupos',\n            hosts: 'Hosts',\n            apps: 'Aplicativos',\n            openresty: 'OpenResty',\n            websites: 'Sites',\n            containers: 'Contêineres',\n            files: 'Gerenciamento de Arquivos',\n            runtimes: 'Ambientes de Execução',\n            process: 'Gerenciamento de Processos',\n            toolbox: 'Caixa de Ferramentas',\n            backups: 'Backup / Restauração',\n            tampers: 'Proteção contra Alterações',\n            xsetting: 'Configurações da Interface',\n            logs: 'Auditoria de Logs',\n            settings: 'Configurações do Painel',\n            cronjobs: 'Tarefas Agendadas',\n            waf: 'WAF',\n            databases: 'Bancos de Dados',\n            licenses: 'licenças',\n            nodes: 'nós',\n            commands: 'Comandos Rápidos',\n        },\n        websiteLog: 'Logs do website',\n        runLog: 'Logs de execução',\n        errLog: 'Logs de erro',\n        task: 'Log de tarefas',\n        taskName: 'Nome da tarefa',\n        taskRunning: 'Em execução',\n    },\n    file: {\n        fileDirNum: '{0} diretórios, {1} arquivos,',\n        currentDir: 'Diretório atual',\n        dir: 'Pasta',\n        upload: 'Carregar',\n        uploadFile: '@:file.upload @.lower:file.file',\n        uploadDirectory: '@:file.upload @.lower:file.dir',\n        download: 'Baixar',\n        fileName: 'Nome do arquivo',\n        search: 'Pesquisar',\n        mode: 'Permissões',\n        editPermissions: '@:file.mode',\n        owner: 'Proprietário',\n        file: 'Arquivo',\n        remoteFile: 'Baixar de remoto',\n        share: 'Compartilhar',\n        sync: 'Sincronização de dados',\n        size: 'Tamanho',\n        updateTime: 'Modificado',\n        rename: 'Renomear',\n        role: 'Permissões',\n        info: 'Atributos',\n        linkFile: 'Link simbólico',\n        batchoperation: 'Operação em lote',\n        shareList: 'Lista de compartilhamento',\n        zip: 'Compactado',\n        group: 'Grupo',\n        path: 'Caminho',\n        public: 'Outros',\n        setRole: 'Configurar permissões',\n        link: 'Link do arquivo',\n        rRole: 'Leitura',\n        wRole: 'Escrita',\n        xRole: 'Executável',\n        name: 'Nome',\n        compress: 'Compactar',\n        deCompress: 'Descompactar',\n        compressType: 'Formato de compactação',\n        compressDst: 'Caminho de compactação',\n        replace: 'Substituir arquivos existentes',\n        compressSuccess: 'Compactado com sucesso',\n        deCompressSuccess: 'Descompactado com sucesso',\n        deCompressDst: 'Caminho de descompactação',\n        linkType: 'Tipo de link',\n        softLink: 'Link simbólico',\n        hardLink: 'Link físico',\n        linkPath: 'Caminho do link',\n        selectFile: 'Selecionar arquivo',\n        downloadUrl: 'URL remota',\n        downloadStart: 'Download iniciado',\n        moveSuccess: 'Movido com sucesso',\n        copySuccess: 'Copiado com sucesso',\n        pasteMsg: 'Clique no botão \"Colar\" no canto superior direito do diretório de destino',\n        move: 'Mover',\n        calculate: 'Calcular',\n        remark: 'Observação',\n        setRemark: 'Definir observação',\n        remarkPrompt: 'Digite uma observação',\n        remarkPlaceholder: 'Observação',\n        remarkToggle: 'Observações',\n        remarkToggleTip: 'Carregar observações do arquivo',\n        canNotDeCompress: 'Não é possível descompactar este arquivo',\n        uploadSuccess: 'Upload bem-sucedido',\n        downloadProcess: 'Progresso do download',\n        downloading: 'Baixando...',\n        infoDetail: 'Propriedades do arquivo',\n        root: 'Diretório raiz',\n        list: 'Lista de arquivos',\n        sub: 'Subpastas',\n        downloadSuccess: 'Baixado com sucesso',\n        theme: 'Tema',\n        language: 'Idioma',\n        eol: 'Fim de linha',\n        copyDir: 'Copiar',\n        paste: 'Colar',\n        changeOwner: 'Modificar usuário e grupo de usuários',\n        containSub: 'Aplicar mudança de permissões recursivamente',\n        ownerHelper:\n            'O usuário padrão do ambiente PHP: o grupo de usuários é 1000:1000, é normal que os usuários dentro e fora do container mostrem inconsistências',\n        searchHelper: 'Suporte a curingas como *',\n        uploadFailed: '[{0}] Falha no upload do arquivo',\n        fileUploadStart: 'Carregando [{0}]...',\n        currentSelect: 'Seleção atual: ',\n        unsupportedType: 'Tipo de arquivo não suportado',\n        deleteHelper:\n            'Tem certeza de que deseja excluir os seguintes arquivos? Por padrão, eles irão para a lixeira após a exclusão',\n        fileHelper:\n            'Nota:\\n1. Os resultados da pesquisa não podem ser ordenados.\\n2. Pastas não podem ser ordenadas por tamanho.',\n        forceDeleteHelper: 'Excluir permanentemente o arquivo (sem entrar na lixeira, excluí-lo diretamente)',\n        recycleBin: 'Lixeira',\n        sourcePath: 'Caminho original',\n        deleteTime: 'Hora da exclusão',\n        confirmReduce: 'Tem certeza de que deseja restaurar os seguintes arquivos?',\n        reduceSuccess: 'Restaurado com sucesso',\n        reduce: 'Restaurar',\n        reduceHelper:\n            'Se um arquivo ou diretório com o mesmo nome existir no caminho original, ele será substituído. Deseja continuar?',\n        clearRecycleBin: 'Limpar',\n        clearRecycleBinHelper: 'Você deseja limpar a lixeira?',\n        favorite: 'Favoritos',\n        removeFavorite: 'Remover dos favoritos?',\n        addFavorite: 'Adicionar/Remover aos favoritos',\n        clearList: 'Limpar lista',\n        deleteRecycleHelper: 'Tem certeza de que deseja excluir permanentemente os seguintes arquivos?',\n        typeErrOrEmpty: '[{0}] tipo de arquivo errado ou pasta vazia',\n        dropHelper: 'Arraste os arquivos que deseja carregar aqui',\n        fileRecycleBin: 'Habilitar lixeira',\n        fileRecycleBinMsg: '{0} lixeira',\n        wordWrap: 'Quebra automática de linha',\n        deleteHelper2:\n            'Tem certeza de que deseja excluir o arquivo selecionado? A operação de exclusão não pode ser desfeita',\n        ignoreCertificate: 'Permitir conexões inseguras com o servidor',\n        ignoreCertificateHelper:\n            'Permitir conexões inseguras com o servidor pode levar a vazamento ou adulteração de dados. Use esta opção apenas quando confiar na fonte de download.',\n        uploadOverLimit: 'O número de arquivos excede 1000! Por favor, compacte e envie novamente',\n        clashDidNotSupport: 'Os nomes de arquivos são proibidos de conter .1panel_clash',\n        clashDeleteAlert: 'A pasta \"Lixeira\" não pode ser excluída',\n        clashOpenAlert: 'Clique no botão \"Lixeira\" para abrir o diretório da lixeira',\n        right: 'Avançar',\n        back: 'Voltar',\n        top: 'Voltar ao topo',\n        up: 'Voltar',\n        openWithVscode: 'Abrir com VS Code',\n        vscodeHelper:\n            'Por favor, certifique-se de que o VS Code está instalado localmente e o plugin SSH Remote está configurado',\n        saveContentAndClose: 'O arquivo foi modificado, deseja salvar e fechar?',\n        saveAndOpenNewFile: 'O arquivo foi modificado, deseja salvar e abrir o novo arquivo?',\n        noEdit: 'O arquivo não foi modificado, não é necessário fazer isso!',\n        noNameFolder: 'Pasta sem nome',\n        noNameFile: 'Arquivo sem nome',\n        minimap: 'Mini mapa de código',\n        fileCanNotRead: 'O arquivo não pode ser lido',\n        previewTruncated: 'O arquivo é muito grande, mostrando apenas a última parte',\n        previewEmpty: 'O arquivo está vazio ou não é um arquivo de texto',\n        previewLargeFile: 'Visualizar',\n        panelInstallDir: 'O diretório de instalação do 1Panel não pode ser excluído',\n        wgetTask: 'Tarefa de Download',\n        existFileTitle: 'Aviso de arquivo com o mesmo nome',\n        existFileHelper: 'O arquivo enviado contém um arquivo com o mesmo nome. Deseja substituí-lo?',\n        existFileSize: 'Tamanho do arquivo (novo -> antigo)',\n        existFileDirHelper: 'O arquivo/pasta selecionado tem um nome duplicado. Por favor, prossiga com cautela!\\n',\n        coverDirHelper: 'As pastas selecionadas para substituição serão copiadas para o caminho de destino!',\n        noSuchFile: 'O arquivo ou diretório não foi encontrado. Por favor, verifique e tente novamente.',\n        setting: 'configuração',\n        showHide: 'Mostrar arquivos ocultos',\n        noShowHide: 'Não mostrar arquivos ocultos',\n        cancelUpload: 'Cancelar Upload',\n        cancelUploadHelper: 'Deseja cancelar o upload, após o cancelamento, a lista de upload será limpa.',\n        keepOneTab: 'Mantenha pelo menos uma aba',\n        notCanTab: 'Não é possível adicionar mais abas',\n        convert: 'Converter Formato',\n        converting: 'Convertendo Para',\n        fileCanNotConvert: 'Este arquivo não suporta conversão de formato',\n        formatType: 'Tipo de Formato',\n        sourceFormat: 'Formato de Origem',\n        sourceFile: 'Arquivo de Origem',\n        saveDir: 'Diretório de Salvamento',\n        deleteSourceFile: 'Excluir Arquivo de Origem',\n        convertHelper: 'Converter os arquivos selecionados para outro formato',\n        convertHelper1: 'Por favor, selecione os arquivos a serem convertidos',\n        execConvert: 'Iniciar conversão. Você pode visualizar os logs de conversão no Centro de Tarefas',\n        convertLogs: 'Logs de Conversão',\n        formatConvert: 'Conversão de Formato',\n    },\n    ssh: {\n        autoStart: 'Início automático',\n        enable: 'Habilitar início automático',\n        disable: 'Desabilitar início automático',\n        sshAlert:\n            'Os dados da lista são classificados com base na data de login. Alterar o fuso horário ou realizar outras operações pode causar desvios na data dos logs de login.',\n        sshAlert2:\n            'Você pode usar o \"Fail2ban\" na \"Caixa de ferramentas\" para bloquear endereços IP que tentam ataques de força bruta, o que aumentará a segurança do host.',\n        sshOperate: 'A operação \"{0}\" no serviço SSH será realizada. Você deseja continuar?',\n        sshChange: 'Configuração SSH',\n        sshChangeHelper: 'Esta ação alterou \"{0}\" para \"{1}\". Você deseja continuar?',\n        sshFileChangeHelper:\n            'Modificar o arquivo de configuração pode afetar a disponibilidade do serviço. Tenha cautela ao realizar esta operação. Você deseja continuar?',\n        port: 'Porta',\n        portHelper: 'Especifique a porta na qual o serviço SSH escutará.',\n        listenAddress: 'Endereço de escuta',\n        allV4V6: '0.0.0.0:{0}(IPv4) e :::{0}(IPv6)',\n        listenHelper:\n            'Deixar os campos de IPv4 e IPv6 em branco fará com que o serviço escute em \"0.0.0.0:{0}(IPv4)\" e \":::{0}(IPv6)\"',\n        addressHelper: 'Especifique o endereço em que o serviço SSH irá escutar.',\n        permitRootLogin: 'Permitir login de usuário root',\n        rootSettingHelper: 'O método de login padrão para o usuário root é \"Permitir login SSH\".',\n        rootHelper1: 'Permitir login SSH',\n        rootHelper2: 'Desabilitar login SSH',\n        rootHelper3: 'Somente login com chave é permitido',\n        rootHelper4: 'Somente comandos pré-definidos podem ser executados. Nenhuma outra operação pode ser realizada.',\n        passwordAuthentication: 'Autenticação por senha',\n        pwdAuthHelper: 'Se deve ou não habilitar a autenticação por senha. Esse parâmetro está habilitado por padrão.',\n        pubkeyAuthentication: 'Autenticação por chave',\n        privateKey: 'Chave Privada',\n        publicKey: 'Chave Pública',\n        password: 'Senha',\n        createMode: 'Método de Criação',\n        generate: 'Gerar Automaticamente',\n        unSyncPass: 'Senha da chave não pode ser sincronizada',\n        syncHelper:\n            'A operação de sincronização limpará chaves inválidas e sincronizará novos pares de chaves completos. Continuar?',\n        input: 'Entrada Manual',\n        import: 'Upload de Arquivo',\n        authKeys: 'Chaves de Autorização',\n        authKeysHelper: 'Salvar informações atuais da chave pública?',\n        pubkey: 'Informações da chave',\n        pubKeyHelper: 'A informação da chave atual só tem efeito para o usuário {0}',\n        encryptionMode: 'Modo de criptografia',\n        passwordHelper: 'Pode conter de 6 a 10 dígitos e letras maiúsculas e minúsculas',\n        reGenerate: 'Regenerar chave',\n        keyAuthHelper: 'Se deve ou não habilitar a autenticação por chave.',\n        useDNS: 'Usar DNS',\n        dnsHelper:\n            'Controla se a função de resolução DNS está habilitada no servidor SSH para verificar a identidade da conexão.',\n        analysis: 'Informações estatísticas',\n        denyHelper:\n            \"Realizando uma operação de 'negar' nos seguintes endereços. Após a configuração, o IP será proibido de acessar o servidor. Você deseja continuar?\",\n        acceptHelper:\n            \"Realizando uma operação de 'aceitar' nos seguintes endereços. Após a configuração, o IP recuperará o acesso normal. Você deseja continuar?\",\n        noAddrWarning: 'Nenhum endereço [{0}] foi selecionado atualmente. Por favor, verifique e tente novamente!',\n        loginLogs: 'Logs de SSH',\n        loginMode: 'Modo',\n        authenticating: 'Chave',\n        publickey: 'Chave',\n        belong: 'Pertence',\n        local: 'Local',\n        session: 'Sessão | Sessões',\n        loginTime: 'Hora do login',\n        loginIP: 'IP de login',\n        stopSSHWarn: 'Deseja desconectar esta conexão SSH?',\n        remote: 'SSH remoto',\n    },\n    setting: {\n        panel: 'Painel',\n        user: 'Usuário do painel',\n        userChange: 'Alterar usuário do painel',\n        userChangeHelper: 'Alterar o usuário do painel irá desconectá-lo. Continuar?',\n        passwd: 'Senha do painel',\n        emailHelper: 'Para recuperação de senha',\n        watermark: 'Configurações de Marca d Água',\n        watermarkContent: 'Conteúdo da Marca d Água',\n        contentHelper:\n            '{0} representa o nome do nó, {1} representa o endereço do nó. Você pode usar variáveis ou preencher nomes personalizados.',\n        watermarkColor: 'Cor da Marca d Água',\n        watermarkFont: 'Tamanho da Fonte da Marca d Água',\n        watermarkHeight: 'Altura da Marca d Água',\n        watermarkWidth: 'Largura da Marca d Água',\n        watermarkRotate: 'Ângulo de Rotação',\n        watermarkGap: 'Espaçamento',\n        watermarkCloseHelper: 'Tem certeza de que deseja desativar as configurações de marca d água do sistema?',\n        watermarkOpenHelper: 'Tem certeza de que deseja salvar as configurações atuais de marca d água do sistema?',\n        title: 'Alias do painel',\n        panelPort: 'Porta do painel',\n        titleHelper:\n            'Suporta nomes com comprimento de 3 a 30 caracteres, incluindo letras, caracteres chineses, números, espaços e caracteres especiais comuns',\n        portHelper:\n            'O intervalo recomendado de portas é de 8888 a 65535. Nota: Se o servidor tiver um grupo de segurança, permita a nova porta do grupo de segurança antecipadamente',\n        portChange: 'Alteração de porta',\n        portChangeHelper: 'Modificar a porta do serviço e reiniciar o serviço. Deseja continuar?',\n        theme: 'Tema',\n        menuTabs: 'Guias do menu',\n        dark: 'Escuro',\n        darkGold: 'Ouro escuro',\n        light: 'Claro',\n        auto: 'Seguir o sistema',\n        language: 'Idioma',\n        runtimeEnv: 'Ambiente de execução',\n        docSource: 'Fonte da documentação',\n        withByRegion: 'Seguir região de operação (Padrão)',\n        withByLang: 'Seguir idioma do sistema',\n        region: 'Região de operação',\n        cn: 'China continental',\n        intl: 'Global',\n        regionHelper: 'Ao alterar a região, as seguintes fontes de recursos serão ajustadas:',\n        regionHelper1: 'Pacotes de instalação do sistema',\n        regionHelper2: 'Loja de aplicativos e biblioteca de scripts',\n        regionHelper3: 'Manual do usuário e documentação relacionada',\n        regionHelper4: 'Isso pode afetar downloads e acessos futuros. Prossiga com cautela.',\n        regionTip:\n            'A região de operação afeta a fonte de atualização do sistema e o endereço de download dos pacotes de instalação.',\n        docSourceTip:\n            'A fonte da documentação determina o idioma de redirecionamento da documentação e do registro de atualizações.',\n        languageHelper: 'Por padrão, segue o idioma do navegador. Este parâmetro tem efeito apenas no navegador atual',\n        sessionTimeout: 'Tempo limite de sessão',\n        sessionTimeoutError: 'O tempo mínimo de sessão é de 300 segundos',\n        sessionTimeoutHelper: 'O painel será desconectado automaticamente após {0} segundo(s) de inatividade.',\n        systemIP: 'Endereço de acesso padrão',\n        systemIPHelper:\n            'Redirecionamentos de aplicativos, acesso a containers e outras funcionalidades usarão este endereço para roteamento. Cada nó pode ser configurado com um endereço diferente.',\n        proxy: 'Proxy do servidor',\n        proxyHelper: 'Será eficaz nos seguintes cenários após configurar o servidor proxy:',\n        proxyHelper1:\n            'Download de pacotes de instalação e sincronização da loja de aplicativos (apenas edição profissional)',\n        proxyHelper2: 'Atualização do sistema e recuperação de informações de atualização (apenas edição profissional)',\n        proxyHelper4:\n            'As solicitações de rede, como o pull de imagens do Docker, serão feitas por meio do servidor proxy (apenas edição profissional)',\n        proxyHelper3: 'Verificação e sincronização da licença do sistema',\n        proxyHelper5:\n            'Download e sincronização unificados para bibliotecas de scripts do tipo sistema (Recurso da Edição Profissional)',\n        proxyHelper6: 'Solicitar certificado (Funcionalidade da versão Pro)',\n        proxyType: 'Tipo de proxy',\n        proxyUrl: 'Endereço do proxy',\n        proxyPort: 'Porta do proxy',\n        proxyPasswdKeep: 'Lembrar senha',\n        proxyDocker: 'Proxy Docker',\n        proxyDockerHelper:\n            'Sincronize a configuração do servidor proxy com o Docker, suportando operações de puxar imagens de servidor offline e outras',\n        syncToNode: 'Sincronizar para o nó filho',\n        syncToNodeHelper: 'Sincronizar de alerta para outros nós',\n        nodes: 'Nós',\n        selectNode: 'Selecionar nó',\n        selectNodeError: 'Por favor, selecione um nó',\n        apiInterface: 'Habilitar API',\n        apiInterfaceClose: 'Uma vez fechado, as interfaces da API não poderão ser acessadas. Deseja continuar?',\n        apiInterfaceHelper: 'Permitir que aplicativos de terceiros acessem a API.',\n        apiInterfaceAlert1:\n            'Não habilite em ambientes de produção, pois pode aumentar os riscos de segurança do servidor.',\n        apiInterfaceAlert2:\n            'Não use aplicativos de terceiros para chamar a API, para evitar potenciais ameaças à segurança.',\n        apiInterfaceAlert3: 'Documentação da API',\n        apiInterfaceAlert4: 'Documentação de uso',\n        apiKey: 'Chave API',\n        apiKeyHelper: 'A chave da API é usada para aplicativos de terceiros acessarem a API.',\n        ipWhiteList: 'Lista de IPs permitidos',\n        ipWhiteListEgs: 'Um por linha. Exemplo: \\n172.161.10.111\\n172.161.10.0/24',\n        ipWhiteListHelper:\n            'IPs na lista de permitidos podem acessar a API, 0.0.0.0/0 (todos os IPv4), ::/0 (todos os IPv6)',\n        apiKeyReset: 'Redefinir chave da interface',\n        apiKeyResetHelper:\n            'O serviço associado à chave se tornará inválido. Por favor, adicione uma nova chave ao serviço',\n        confDockerProxy: 'Configurar proxy do Docker',\n        restartNowHelper: 'Configurar o proxy do Docker exige reiniciar o serviço Docker.',\n        restartNow: 'Reiniciar imediatamente',\n        restartLater: 'Reiniciar manualmente mais tarde',\n        systemIPWarning:\n            'O nó atual não tem um endereço de acesso padrão configurado. Por favor, vá para as configurações do painel para configurá-lo!',\n        systemIPWarning1:\n            'O endereço atual do servidor está configurado como {0}, e o redirecionamento rápido não é possível!',\n        defaultNetwork: 'Placa de rede',\n        syncTime: 'Hora do servidor',\n        timeZone: 'Fuso horário',\n        timeZoneChangeHelper: 'Alterar o fuso horário exige reiniciar o serviço. Deseja continuar?',\n        timeZoneHelper:\n            'A modificação de fuso horário depende do serviço timedatectl do sistema. Entrará em vigor após reiniciar o serviço 1Panel.',\n        timeZoneCN: 'Pequim',\n        timeZoneAM: 'Los Angeles',\n        timeZoneNY: 'Nova York',\n        ntpALi: 'Alibaba',\n        ntpGoogle: 'Google',\n        syncSite: 'Servidor NTP',\n        syncSiteHelper:\n            'Esta operação usará {0} como fonte para a sincronização do horário do sistema. Deseja continuar?',\n        changePassword: 'Alterar senha',\n        oldPassword: 'Senha original',\n        newPassword: 'Nova senha',\n        retryPassword: 'Confirmar senha',\n        noSpace: 'As informações inseridas não podem conter caracteres de espaço',\n        duplicatePassword: 'A nova senha não pode ser igual à senha original, por favor, insira novamente!',\n        diskClean: 'Limpeza de cache',\n        developerMode: 'Modo de desenvolvimento',\n        developerModeHelper:\n            'Você terá a oportunidade de testar novos recursos e correções antes de serem amplamente lançados e fornecer feedback precoce.',\n        thirdParty: 'Contas de terceiros',\n        noTypeForCreate: 'Nenhum tipo de backup foi criado até o momento',\n        LOCAL: 'Disco do servidor',\n        OSS: 'Ali OSS',\n        S3: 'Amazon S3',\n        mode: 'Modo',\n        MINIO: 'MinIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'Conectar WebDAV Alist pode referir-se à documentação oficial',\n        OneDrive: 'Microsoft OneDrive',\n        isCN: 'Internet da China',\n        isNotCN: 'Versão internacional',\n        client_id: 'ID do cliente',\n        client_secret: 'Segredo do cliente',\n        redirect_uri: 'URL de redirecionamento',\n        onedrive_helper: 'A configuração personalizada pode ser referida na documentação oficial',\n        refreshTime: 'Tempo de atualização do token',\n        refreshStatus: 'Status da atualização do token',\n        backupDir: 'Diretório de backup',\n        codeWarning: 'O formato atual do código de autorização está incorreto, por favor, verifique novamente!',\n        code: 'Código de autorização',\n        codeHelper:\n            'Clique no botão \"Adquirir\", faça login no OneDrive e copie o conteúdo após \"code\" no link redirecionado. Cole-o neste campo. Para instruções específicas, consulte a documentação oficial.',\n        googleHelper:\n            'Por favor, primeiro crie um aplicativo Google e obtenha as informações do cliente, preencha o formulário e clique no botão obter. Para operações específicas, consulte a documentação oficial.',\n        loadCode: 'Obter',\n        COS: 'Tencent COS',\n        ap_beijing_1: 'Beijing Zona 1',\n        ap_beijing: 'Pequim',\n        ap_nanjing: 'Nanjing',\n        ap_shanghai: 'Xangai',\n        ap_guangzhou: 'Cantão',\n        ap_chengdu: 'Chengdu',\n        ap_chongqing: 'Chongqing',\n        ap_shenzhen_fsi: 'Shenzhen Financeiro',\n        ap_shanghai_fsi: 'Xangai Financeiro',\n        ap_beijing_fsi: 'Pequim Financeiro',\n        ap_hongkong: 'Hong Kong, China',\n        ap_singapore: 'Cingapura',\n        ap_mumbai: 'Bombaim',\n        ap_jakarta: 'Jacarta',\n        ap_seoul: 'Seul',\n        ap_bangkok: 'Bangkok',\n        ap_tokyo: 'Tóquio',\n        na_siliconvalley: 'Silicon Valley (EUA Oeste)',\n        na_ashburn: 'Ashburn (EUA Leste)',\n        na_toronto: 'Toronto',\n        sa_saopaulo: 'São Paulo',\n        eu_frankfurt: 'Frankfurt',\n        KODO: 'Qiniu Kodo',\n        scType: 'Tipo de armazenamento',\n        typeStandard: 'Padrão',\n        typeStandard_IA: 'Padrão_IA',\n        typeArchive: 'Arquivo',\n        typeDeep_Archive: 'Arquivo Profundo',\n        scLighthouse: 'Padrão, O armazenamento de objetos leve só suporta este tipo de armazenamento',\n        scStandard:\n            'O armazenamento padrão é adequado para cenários de negócios com grande volume de arquivos quentes que exigem acesso em tempo real, interação frequente de dados, etc.',\n        scStandard_IA:\n            'O armazenamento de baixa frequência é adequado para cenários de negócios com frequência de acesso relativamente baixa, e armazena dados por pelo menos 30 dias.',\n        scArchive:\n            'Armazenamento para arquivos é adequado para cenários de negócios com frequência de acesso extremamente baixa.',\n        scDeep_Archive:\n            'Armazenamento frio durável é adequado para cenários de negócios com frequência de acesso extremamente baixa.',\n        archiveHelper:\n            'Arquivos de armazenamento arquivado não podem ser baixados diretamente e devem ser restaurados primeiro através do site do provedor de serviços de nuvem correspondente. Use com cautela!',\n        backupAlert:\n            'Se um provedor de nuvem for compatível com o protocolo S3, você pode usar diretamente o Amazon S3 para backup.',\n        domain: 'Domínio de aceleração',\n        backupAccount: 'Conta de backup | Contas de backup',\n        loadBucket: 'Obter bucket',\n        accountName: 'Nome da conta',\n        accountKey: 'Chave da conta',\n        address: 'Endereço',\n        path: 'Caminho',\n        safe: 'Segurança',\n        passkey: 'Passkey',\n        passkeyManage: 'Gerenciar',\n        passkeyKeyManagement: 'Gerenciamento de chaves',\n        passkeyHelper: 'Para login rápido, é possível vincular até 5 passkeys',\n        passkeyRequireSSL: 'Passkeys exigem domínio vinculado e acesso via HTTPS',\n        passkeyTrustedProxies: 'Proxies confiáveis',\n        passkeyTrustedProxiesHelper:\n            'Somente requisições originadas nesses IP/CIDR confiarão em Forwarded e X-Forwarded-Proto para detectar HTTPS',\n        passkeyNotSupported: 'O navegador ou ambiente atual não suporta passkeys',\n        passkeyPrereqTitle: 'Os seguintes pré-requisitos são necessários para Passkey',\n        passkeyPrereqBindDomain: 'Vinculação de domínio do 1Panel configurada',\n        passkeyPrereqHttps: 'Acesso seguro via HTTPS',\n        passkeyPrereqBrowser: 'O ambiente suporta Passkey (WebAuthn + autenticador de plataforma)',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable:\n            'Use um navegador do sistema e confirme que o painel está aberto em HTTPS com domínio vinculado.',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            'Este ambiente não pode usar autenticação do dispositivo. Tente um navegador do sistema em uma versão mais nova do SO.',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            'Ative bloqueio de tela / impressão digital / reconhecimento facial / Windows Hello e tente novamente.',\n        passkeyPrereqBrowserDetailDetectFailed:\n            'Falha na detecção. Atualize a página e tente novamente, ou troque para um navegador do sistema.',\n        passkeyPrereqGoSetup: 'Configurar',\n        passkeyCount: 'Vinculadas {0}/{1}',\n        passkeyName: 'Nome',\n        passkeyNameHelper: 'Digite um nome para distinguir dispositivos',\n        passkeyAdd: 'Adicionar Passkey',\n        passkeyCreatedAt: 'Criado em',\n        passkeyLastUsedAt: 'Último uso',\n        passkeyDeleteConfirm: 'Após excluir, esta passkey não poderá ser usada para login. Continuar?',\n        passkeyLimit: 'É possível vincular até 5 passkeys',\n        passkeyFailed:\n            'Falha no cadastro da passkey, por favor, certifique-se de que o certificado SSL do painel é confiável',\n        bindInfo: 'Informações de Vinculação',\n        bindAll: 'Ouvir Todos',\n        bindInfoHelper:\n            'Alterar o endereço de escuta ou protocolo do serviço pode resultar em indisponibilidade do serviço. Você deseja continuar?',\n        ipv6: 'Ouvir IPv6',\n        bindAddress: 'Endereço de escuta',\n        entrance: 'Entrada',\n        showEntrance: 'Mostrar alerta desativado na página \"Visão Geral\"',\n        entranceHelper:\n            'Habilitar a entrada de segurança permitirá o login no painel apenas através do ponto de entrada de segurança especificado.',\n        entranceError:\n            'Por favor, insira um ponto de entrada seguro de 5-116 caracteres, somente números ou letras são suportados.',\n        entranceInputHelper: 'Deixe em branco para desativar a entrada de segurança.',\n        randomGenerate: 'Aleatório',\n        expirationTime: 'Data de Expiração',\n        unSetting: 'Não definido',\n        noneSetting:\n            'Defina o tempo de expiração da senha do painel. Após a expiração, será necessário redefinir a senha.',\n        expirationHelper:\n            'Se o tempo de expiração da senha for [0] dias, a função de expiração da senha estará desativada.',\n        days: 'Dias de Expiração',\n        expiredHelper: 'A senha atual expirou. Por favor, altere a senha novamente.',\n        timeoutHelper:\n            '[ {0} dias ] A senha do painel está prestes a expirar. Após a expiração, será necessário redefinir a senha.',\n        complexity: 'Validação de Complexidade',\n        complexityHelper:\n            'Após ativar, a regra de validação de senha será: 8-30 caracteres, incluindo letras, números e pelo menos dois caracteres especiais.',\n        bindDomain: 'Vincular domínio',\n        unBindDomain: 'Desvincular domínio',\n        panelSSL: 'SSL do Painel',\n        unBindDomainHelper:\n            'A ação de desvincular um domínio pode causar insegurança no sistema. Você deseja continuar?',\n        bindDomainHelper: 'Após vincular o domínio, somente esse domínio poderá acessar o serviço 1Panel.',\n        bindDomainHelper1: 'Deixe em branco para desabilitar o vínculo de domínio.',\n        bindDomainWarning:\n            'Após vincular o domínio, você será desconectado e poderá acessar o serviço 1Panel apenas através do domínio especificado nas configurações. Você deseja continuar?',\n        allowIPs: 'IP Autorizado',\n        unAllowIPs: 'IP Não Autorizado',\n        unAllowIPsWarning:\n            'Autorizar um IP vazio permitirá que todos os IPs acessem o sistema, o que pode causar insegurança. Você deseja continuar?',\n        allowIPsHelper:\n            'Após definir a lista de IPs autorizados, apenas os IPs da lista poderão acessar o serviço do painel.',\n        allowIPsWarning:\n            'Após definir a lista de IPs autorizados, somente os IPs da lista poderão acessar o serviço do painel. Você deseja continuar?',\n        allowIPsHelper1: 'Deixe em branco para desabilitar a restrição de IP.',\n        allowIPEgs: 'Um por linha. Por exemplo,\\n172.16.10.111\\n172.16.10.0/24',\n        mfa: 'Autenticação em Dois Fatores',\n        mfaClose: 'Desabilitar MFA reduzirá a segurança do serviço. Você deseja continuar?',\n        secret: 'Segredo',\n        mfaInterval: 'Intervalo de atualização (s)',\n        mfaTitleHelper:\n            'O título é usado para distinguir diferentes hosts do 1Panel. Escaneie novamente ou adicione manualmente a chave secreta após modificar o título.',\n        mfaIntervalHelper:\n            'Após alterar o intervalo de atualização, escaneie novamente ou adicione a chave secreta manualmente.\\nAlguns aplicativos autenticadores suportam apenas intervalo de 30 segundos (por exemplo, Google/Microsoft Authenticator).',\n        mfaAlert:\n            'O token de uso único é um número dinâmico de 6 dígitos e baseado no tempo atual. Certifique-se de que o horário do servidor esteja sincronizado.',\n        mfaHelper: 'Após ativá-lo, o token de uso único precisará ser verificado.',\n        mfaHelper1: 'Baixe um aplicativo autenticador, por exemplo,',\n        mfaHelper2:\n            'Para obter o token de uso único, escaneie o código QR abaixo usando seu aplicativo autenticador ou copie a chave secreta para o aplicativo de autenticação.',\n        mfaHelper3: 'Digite os seis dígitos do aplicativo',\n        mfaCode: 'Token de uso único',\n        sslChangeHelper: 'Modificar a configuração de https e reiniciar o serviço. Você deseja continuar?',\n        sslDisable: 'Desabilitar',\n        sslDisableHelper:\n            'Se o serviço https for desabilitado, será necessário reiniciar o painel para que a alteração tenha efeito. Você deseja continuar?',\n        noAuthSetting: 'Configuração não autorizada',\n        noAuthSettingHelper:\n            'Quando os usuários não fizerem login com a entrada de segurança especificada, ou não acessarem o painel a partir de IP ou domínio especificado, essa resposta pode ocultar características do painel.',\n        responseSetting: 'Configuração de resposta',\n        help200: 'Página de ajuda',\n        error400: 'Requisição inválida',\n        error401: 'Não autorizado',\n        error403: 'Proibido',\n        error404: 'Não encontrado',\n        error408: 'Tempo de solicitação expirado',\n        error416: 'Faixa não satisfatória',\n        error444: 'Conexão fechada',\n        error500: 'Erro no servidor',\n        https: 'Configurar HTTPS para o painel melhora a segurança de acesso',\n        strictHelper: 'Tráfego não HTTPS não pode se conectar ao painel',\n        muxHelper:\n            'O painel ouvirá nas protocolos HTTP e HTTPS e redirecionará HTTP para HTTPS, mas isso pode reduzir a segurança do painel',\n        certType: 'Tipo de certificado',\n        selfSigned: 'Autoassinado',\n        selfSignedHelper:\n            'Os navegadores podem não confiar em certificados autoassinados e podem exibir avisos de segurança.',\n        select: 'Selecionar',\n        domainOrIP: 'Domínio ou IP:',\n        timeOut: 'Tempo limite',\n        rootCrtDownload: 'Download do certificado raiz',\n        primaryKey: 'Chave primária',\n        certificate: 'Certificado',\n        backupJump:\n            'Arquivos de backup não estão na lista de backup atual, tente fazer o download do diretório de arquivos e importar para o backup.',\n        snapshot: 'Snapshot | Snapshots',\n        noAppData: 'Nenhum aplicativo do sistema disponível para seleção',\n        noBackupData: 'Nenhum dado de backup disponível para seleção',\n        stepBaseData: 'Dados Básicos',\n        stepAppData: 'Aplicativos do Sistema',\n        stepPanelData: 'Dados do Sistema',\n        stepBackupData: 'Dados de Backup',\n        stepOtherData: 'Outros Dados',\n        operationLog: 'Manter logs de operações',\n        loginLog: 'Manter logs de acesso',\n        systemLog: 'Manter logs do sistema',\n        taskLog: 'Manter logs de tarefas',\n        monitorData: 'Manter dados de monitoramento',\n        dockerConf: 'Manter Configuração do Docker',\n        selectAllImage: 'Fazer backup de todas as imagens de aplicativos',\n        logLabel: 'Log',\n        agentLabel: 'Configuração do Nó',\n        appDataLabel: 'Dados de Aplicativos',\n        appImage: 'Imagem do Aplicativo',\n        appBackup: 'Backup de Aplicativo',\n        backupLabel: 'Diretório de Backup',\n        confLabel: 'Arquivos de Configuração',\n        dockerLabel: 'Contêineres',\n        taskLabel: 'Tarefas Agendadas',\n        resourceLabel: 'Diretório de Recursos do Aplicativo',\n        runtimeLabel: 'Ambiente de Execução',\n        appLabel: 'Aplicativo',\n        databaseLabel: 'Banco de Dados',\n        snapshotLabel: 'Arquivos de Snapshot',\n        websiteLabel: 'Site',\n        directoryLabel: 'Diretório',\n        appStoreLabel: 'Loja de Aplicativos',\n        shellLabel: 'Script',\n        tmpLabel: 'Diretório Temporário',\n        sslLabel: 'Diretório de Certificados',\n        reCreate: 'Falha ao criar snapshot',\n        reRollback: 'Falha ao reverter snapshot',\n        deleteHelper:\n            'Todos os arquivos de snapshot, incluindo os da conta de backup de terceiros, serão excluídos. Você deseja continuar?',\n        status: 'Status do snapshot',\n        ignoreRule: 'Ignorar regra',\n        editIgnoreRule: '@:commons.button.edit @.lower:setting.ignoreRule',\n        ignoreHelper:\n            'Esta regra será usada para comprimir e fazer backup do diretório de dados do 1Panel durante a criação do snapshot. Por padrão, arquivos de socket são ignorados.',\n        ignoreHelper1: 'Um por linha. Exemplo,\\n*.log\\n/opt/1panel/cache',\n        panelInfo: 'Escrever informações básicas do 1Panel',\n        panelBin: 'Fazer backup dos arquivos do sistema 1Panel',\n        daemonJson: 'Fazer backup do arquivo de configuração do Docker',\n        appData: 'Fazer backup dos aplicativos instalados do 1Panel',\n        panelData: 'Fazer backup do diretório de dados do 1Panel',\n        backupData: 'Fazer backup do diretório de backup local do 1Panel',\n        compress: 'Criar arquivo de snapshot',\n        upload: 'Fazer upload do arquivo de snapshot',\n        recoverDetail: 'Detalhes da recuperação',\n        importHelper: 'Diretório do snapshot: ',\n        lastRecoverAt: 'Última recuperação realizada',\n        lastRollbackAt: 'Último rollback realizado',\n        reDownload: 'Baixar o arquivo de backup novamente',\n        recoverErrArch: 'A recuperação de snapshot entre diferentes arquiteturas de servidor não é suportada!',\n        recoverErrSize:\n            'Espaço em disco insuficiente detectado, por favor, verifique ou libere espaço e tente novamente!',\n        recoverHelper:\n            'Iniciando a recuperação do snapshot {0}, por favor, confirme as seguintes informações antes de prosseguir:',\n        recoverHelper1: 'A recuperação requer reiniciar os serviços Docker e 1Panel',\n        recoverHelper2:\n            'Por favor, assegure-se de que há espaço suficiente em disco no servidor (Tamanho do arquivo de snapshot: {0}, Espaço disponível: {1})',\n        recoverHelper3:\n            'Por favor, assegure-se de que a arquitetura do servidor corresponda à arquitetura do servidor onde o snapshot foi criado (Arquitetura do servidor atual: {0})',\n        rollback: 'Rollback',\n        rollbackHelper:\n            'Reverter essa recuperação substituirá todos os arquivos dessa recuperação e pode exigir reiniciar os serviços Docker e 1Panel. Você deseja continuar?',\n        upgradeHelper: 'A atualização requer reiniciar o serviço 1Panel. Você deseja continuar?',\n        rollbackLocalHelper:\n            'O nó principal não suporta rollback direto. Por favor, execute manualmente o comando [1pctl restore] para fazer o rollback!',\n        noUpgrade: 'Esta é a versão mais recente',\n        upgradeNotes: 'Notas de versão',\n        upgradeNow: 'Atualizar agora',\n        source: 'Fonte para download',\n        versionNotSame:\n            'A versão do nó não corresponde à do nó principal. Atualize na Gestão de Nós antes de tentar novamente.',\n        versionCompare:\n            'Detectado que o nó {0} já está na última versão atualizável. Por favor, verifique a versão do nó principal e tente novamente!',\n        about: 'Sobre',\n        versionItem: 'Versão Atual',\n        backupCopies: 'Cópias de Backup',\n        backupCopiesHelper:\n            'Defina o número de cópias de backup de atualização para manter para reversão de versão. 0 significa manter todas.',\n        backupCopiesRule: 'Mantenha pelo menos 3 registros de backup de atualização',\n        release: 'Registro de Atualizações de Versão',\n        releaseHelper:\n            'Falha ao obter o registro de atualizações para o ambiente atual. Você pode verificar a documentação oficial manualmente.',\n        project: 'GitHub',\n        issue: 'Problema',\n        doc: 'Documento oficial',\n        star: 'Estrela',\n        description: 'Painel de Servidor Linux',\n        forum: 'Discussões',\n        doc2: 'Docs',\n        currentVersion: 'Versão',\n        license: 'Licença',\n        bindNode: 'Vincular Nó',\n        menuSetting: 'Configurações do Menu',\n        menuSettingHelper: 'Quando apenas 1 submenu existir, a barra de menus exibirá apenas esse submenu',\n        showAll: 'Mostrar Tudo',\n        hideALL: 'Ocultar Tudo',\n        ifShow: 'Exibir?',\n        menu: 'Menu',\n        confirmMessage: 'A página será atualizada para atualizar a lista de menus avançados. Continuar?',\n        recoverMessage:\n            'A página será atualizada e a lista de menus será restaurada ao estado inicial. Deseja continuar?',\n        compressPassword: 'Senha de compressão',\n        backupRecoverMessage:\n            'Por favor, insira a senha de compressão ou descompressão (deixe em branco para não definir)',\n        componentSize: 'Tamanho do componente',\n        apiKeyValidityTime: 'Período de validade da chave da API',\n        apiKeyValidityTimeEgs: 'Período de validade da chave da API (em minutos)',\n        apiKeyValidityTimeHelper:\n            'O timestamp da interface é válido se a diferença para o timestamp atual (em minutos) estiver no intervalo permitido. Valor 0 desativa a verificação.',\n        scope: 'Escopo',\n        public: 'Público',\n        publicHelper: 'Contas de backup públicas serão sincronizadas para cada subnó e podem ser usadas em conjunto',\n        private: 'Privado',\n        privateHelper: 'Contas de backup privadas são criadas apenas no nó atual e usadas somente por ele',\n        UPYUN: 'UPYUN',\n        ALIYUN: 'Aliyun Drive',\n        ALIYUNHelper: 'O limite atual para download sem cliente no Aliyun Drive é 100 MB. Acima disso, use o cliente.',\n        ALIYUNRecover:\n            'O limite atual para download sem cliente no Aliyun Drive é 100 MB. Acima disso, baixe localmente via cliente e depois sincronize o snapshot para restaurar.',\n        GoogleDrive: 'Google Drive',\n        analysis: 'Análise',\n        analysisHelper:\n            'Cole todo o conteúdo do token para analisar automaticamente as partes necessárias. Para detalhes, veja a documentação oficial.',\n        serviceName: 'Nome do serviço',\n        operator: 'Operador',\n        clickToRefresh: 'Clique para atualizar',\n        recoverFailed: 'Falha na restauração do snapshot',\n        upgradeRecord: 'Histórico de atualização',\n        upgrading: 'Atualizando, aguarde...',\n        versionHelper:\n            'Regras de nome: [versão principal].[versão funcional].[versão de correção], como no exemplo abaixo:',\n        upgradeCheck: 'Verificar atualizações',\n        panelSSLHelper:\n            'Após a renovação automática do SSL do painel, reinicie manualmente o serviço 1Panel para aplicar as mudanças.',\n    },\n    license: {\n        offLine: 'Versão Offline',\n        community: 'Gratuito',\n        oss: 'Open Source Software',\n        pro: 'Pro',\n        trial: 'Teste',\n        add: 'Adicionar Edição Comunitária',\n        licenseBindHelper: 'As cotas de nó gratuitas só podem ser usadas quando a licença está vinculada a um nó',\n        licenseAlert:\n            'Nós da Edição Comunitária só podem ser adicionados quando a licença está devidamente vinculada a um nó. Apenas nós devidamente vinculados à licença suportam troca.',\n        licenseUnbindHelper:\n            'Nós da Edição Comunitária detectados para esta licença. Por favor, desvincule e tente novamente!',\n        subscription: 'Assinatura',\n        perpetual: 'Licença Perpétua',\n        versionConstraint: '{0} Compra do versão',\n        forceUnbind: 'Forçar Desvinculação',\n        forceUnbindHelper:\n            'Forçar a desvinculação ignorará quaisquer erros que ocorram durante o processo de desvinculação e, em última análise, liberará a vinculação da licença.',\n        updateForce:\n            'Atualização forçada (ignora todos os erros durante o desvinculamento para garantir o sucesso da operação final)',\n        trialInfo: 'Versão',\n        authorizationId: 'ID da Assinatura',\n        authorizedUser: 'Usuário autorizado',\n        lostHelper:\n            'A licença atingiu o número máximo de tentativas de reenvio. Por favor, clique manualmente no botão de sincronização para garantir que os recursos da versão profissional estão funcionando corretamente. detalhes: ',\n        disableHelper:\n            'A verificação de sincronização da licença falhou. Por favor, clique manualmente no botão de sincronização para garantir que os recursos da versão profissional estão funcionando corretamente. detalhes: ',\n        quickUpdate: 'Atualização rápida',\n        power: 'Autorizar',\n        unbindHelper:\n            'Todas as configurações relacionadas ao Pro serão limpas após a desvinculação. Você deseja continuar?',\n        importLicense: 'Licença',\n        importHelper: 'Clique ou arraste o arquivo de licença aqui',\n        technicalAdvice: 'Consultoria técnica',\n        advice: 'Consultoria',\n        levelUpPro: 'Upgrade para Pro',\n        licenseSync: 'Sincronização de Licença',\n        knowMorePro: 'Saiba mais',\n        closeAlert: 'A página atual pode ser fechada nas configurações do painel',\n        introduce: 'Introdução de recursos',\n        waf: 'O upgrade para a versão profissional pode fornecer recursos como mapa de intercepção, logs, registros de bloqueio, bloqueio por localização geográfica, regras personalizadas, páginas de intercepção personalizadas, etc.',\n        tamper: 'O upgrade para a versão profissional pode proteger sites contra modificações ou adulterações não autorizadas.',\n        setting:\n            'O upgrade para a versão profissional permite a personalização do logo do painel, mensagem de boas-vindas e outras informações.',\n        monitor:\n            'Upgrade para a versão profissional para visualizar o status em tempo real do site, tendências de visitantes, fontes de visitantes, logs de solicitações e outras informações.',\n        alert: 'Upgrade para a versão profissional para receber informações de alarme via SMS e visualizar logs de alarmes, controlar completamente vários eventos chave e garantir a operação sem preocupações do sistema',\n        node: 'Atualizar para a Edição Profissional permite gerenciar vários servidores Linux com o 1Panel.',\n        nodeApp:\n            'Atualizar para a Edição Profissional permite a atualização unificada de versões de aplicativos multi-nó sem a necessidade de alternar manualmente entre nós.',\n        nodeDashboard:\n            'Atualizar para a Edição Profissional permite o gerenciamento centralizado de aplicativos, sites, bancos de dados e tarefas agendadas multi-nó.',\n        fileExchange:\n            'Atualize para a Edição Profissional para transferir arquivos rapidamente entre vários servidores.',\n        app: 'Upgrade para a versão profissional para visualizar informações do serviço, monitoramento anômalo, etc., através do aplicativo móvel.',\n        cluster:\n            'A atualização para a Edição Profissional permite gerenciar clusters mestre-escravo MySQL/Postgres/Redis.',\n        exceptionalHelper:\n            'A verificação de sincronização da licença está anormal. Clique em sincronizar manualmente para garantir o funcionamento da versão profissional. detalhe: ',\n        tamperHelper:\n            'Falha na operação, o arquivo ou pasta tem proteção contra alteração habilitada. Verifique e tente novamente!',\n    },\n    clean: {\n        scan: 'Iniciar escaneamento',\n        scanHelper: 'Limpeza fácil de arquivos inúteis gerados durante a execução do 1Panel',\n        clean: 'Limpar agora',\n        reScan: 'Repetir escaneamento',\n        cleanHelper: 'Arquivos e diretórios selecionados não podem ser revertidos após a limpeza. Continuar?',\n        statusSuggest: '(Limpeza recomendada)',\n        statusClean: '(Muito limpo)',\n        statusEmpty: 'Muito limpo, nenhuma limpeza necessária!',\n        statusWarning: '(Prossiga com cautela)',\n        lastCleanTime: 'Última limpeza: {0}',\n        lastCleanHelper: 'Arquivos e diretórios limpos: {0}, total limpo: {1}',\n        cleanSuccessful: 'Limpeza realizada com sucesso',\n        currentCleanHelper: 'Arquivos e diretórios limpos nesta sessão: {0}, total limpo: {1}',\n        suggest: '(Recomendado)',\n        totalScan: 'Total de arquivos inúteis a serem limpos: ',\n        selectScan: 'Total de arquivos inúteis selecionados: ',\n        system: 'Arquivos inúteis do sistema',\n        systemHelper: 'Arquivos temporários gerados durante processos como snapshots e atualizações.',\n        panelOriginal: 'Arquivos de backup antes da restauração do snapshot do sistema.',\n        upgrade: 'Arquivos de backup de atualização do sistema.',\n        agentPackages: 'Pacotes de atualização/instalação de nós filhos de versões históricas.',\n        upgradeHelper: '(Recomenda-se manter o backup de atualização mais recente para rollback do sistema)',\n        rollback: 'Arquivos de backup antes da recuperação',\n        backup: 'Backup do Sistema',\n        backupHelper: 'Arquivos de backup não associados a contas de backup locais',\n        unknownBackup: 'Backup do Sistema',\n        tmpBackup: 'Backup Temporário',\n        unknownApp: 'Backup de Aplicativo não Associado',\n        unknownDatabase: 'Backup de Banco de Dados não Associado',\n        unknownWebsite: 'Backup de Site não Associado',\n        unknownSnapshot: 'Backup de Instantâneo não Associado',\n        upload: 'Arquivos temporários de upload',\n        uploadHelper: 'Arquivos temporários enviados da lista de backup do sistema',\n        download: 'Arquivos temporários de download',\n        downloadHelper: 'Arquivos temporários baixados de contas de backup de terceiros pelo sistema',\n        directory: 'Diretório',\n        systemLog: 'Arquivo de log',\n        systemLogHelper: 'Logs do sistema, logs de tarefas, arquivos de log do site',\n        dockerLog: 'Arquivos de log de operação de contêineres',\n        taskLog: 'Arquivos de log de execução de tarefas agendadas',\n        shell: 'Tarefas agendadas de script de shell',\n        containerShell: 'Tarefas agendadas de script de shell executadas dentro de contêineres',\n        curl: 'Tarefas agendadas de CURL',\n        docker: 'Lixo de contêineres',\n        dockerHelper: 'Arquivos como contêineres, imagens, volumes, cache de compilação, etc.',\n        volumes: 'Volumes',\n        buildCache: 'Cache de construção do container',\n        appTmpDownload: 'Arquivo de download temporário do aplicativo',\n        unknownWebsiteLog: 'Fail sandaran log laman web tidak dikaitkan',\n        snapshot: 'Arquivos temporários de snapshot do sistema',\n    },\n    app: {\n        app: 'Aplicativo | Aplicativos',\n        installName: 'Nome',\n        installed: 'Instalado',\n        all: 'Todos',\n        version: 'Versão',\n        detail: 'Detalhes',\n        params: 'Editar',\n        author: 'Autor',\n        source: 'Fonte',\n        appName: 'Nome do Aplicativo',\n        deleteWarn:\n            'A operação de exclusão excluirá todos os dados e backups juntos. Esta operação não pode ser desfeita. Deseja continuar?',\n        syncSuccess: 'Sincronizado com sucesso',\n        canUpgrade: 'Atualizações',\n        backupName: 'Nome do Arquivo',\n        backupPath: 'Caminho do Arquivo',\n        backupdate: 'Hora do Backup',\n        versionSelect: 'Por favor, selecione uma versão',\n        operatorHelper: 'A operação {0} será realizada no aplicativo selecionado. Deseja continuar?',\n        startOperatorHelper: 'O aplicativo será iniciado. Deseja continuar?',\n        stopOperatorHelper: 'O aplicativo será parado. Deseja continuar?',\n        restartOperatorHelper: 'O aplicativo será reiniciado. Deseja continuar?',\n        reloadOperatorHelper: 'O aplicativo será recarregado. Deseja continuar?',\n        checkInstalledWarn: '\"{0}\" não foi detectado. Vá para \"Loja de Aplicativos\" para instalar.',\n        limitHelper: 'O aplicativo já foi instalado.',\n        deleteHelper: '\"{0}\" foi associado aos seguintes recursos. Por favor, verifique e tente novamente!',\n        checkTitle: 'Dica',\n        defaultConfig: 'Configuração padrão',\n        defaultConfigHelper: 'Foi restaurado para a configuração padrão, ela entrará em vigor após salvar',\n        forceDelete: 'Excluir forçadamente',\n        forceDeleteHelper: 'A exclusão forçada ignorará erros durante o processo de exclusão e eliminará os metadados.',\n        deleteBackup: 'Excluir backup',\n        deleteBackupHelper: 'Excluir também o backup do aplicativo',\n        deleteDB: 'Excluir banco de dados',\n        deleteDBHelper: 'Excluir também o banco de dados',\n        noService: 'Sem {0}',\n        toInstall: 'Ir para instalar',\n        param: 'Parâmetros',\n        alreadyRun: 'Idade',\n        syncAppList: 'Sincronizar',\n        less1Minute: 'Menos de 1 minuto',\n        appOfficeWebsite: 'Site oficial',\n        github: 'Github',\n        document: 'Documento',\n        updatePrompt: 'Nenhuma atualização disponível',\n        installPrompt: 'Nenhum aplicativo instalado ainda',\n        updateHelper: 'Editar parâmetros pode causar falha no início do aplicativo. Por favor, proceda com cautela.',\n        updateWarn: 'Os parâmetros de atualização precisam reconstruir o aplicativo. Deseja continuar?',\n        busPort: 'Porta',\n        syncStart: 'Iniciando a sincronização! Por favor, atualize a loja de aplicativos mais tarde',\n        advanced: 'Configurações avançadas',\n        cpuCore: 'núcleo(s)',\n        containerName: 'Nome do container',\n        containerNameHelper: 'O nome do container será gerado automaticamente quando não definido',\n        allowPort: 'Acesso externo',\n        allowPortHelper: 'Permitir o acesso externo irá liberar a porta do firewall',\n        appInstallWarn:\n            'O aplicativo não expõe a porta de acesso externo por padrão. Clique em \"Configurações Avançadas\" para expô-la.',\n        upgradeStart: 'Iniciando a atualização! Por favor, atualize a página mais tarde',\n        toFolder: 'Abrir o diretório de instalação',\n        editCompose: 'Editar arquivo compose',\n        editComposeHelper: 'Editar o arquivo compose pode resultar em falha na instalação do software',\n        composeNullErr: 'O compose não pode estar vazio',\n        takeDown: 'Retirar',\n        allReadyInstalled: 'Instalado',\n        installHelper: 'Se houver problemas ao puxar a imagem, configure a aceleração da imagem.',\n        sortMode: 'Ordenar',\n        upgradeHelper:\n            'Coloque aplicativos anormais de volta ao estado normal antes de atualizar. Se a atualização falhar, vá para \"Logs > Logs do Sistema\" para verificar a razão da falha.',\n        installWarn:\n            'O acesso externo não foi habilitado, o que impede que o aplicativo seja acessado via redes externas. Deseja continuar?',\n        showIgnore: 'Exibir aplicativos ignorados',\n        cancelIgnore: 'Cancelar ignorar',\n        ignoreList: 'Aplicativos ignorados',\n        appHelper:\n            'Vá para a página de detalhes do aplicativo para aprender as instruções de instalação para alguns aplicativos especiais.',\n        backupApp: 'Fazer backup do aplicativo antes de atualizar',\n        backupAppHelper:\n            'Se a atualização falhar, o backup será automaticamente revertido. Verifique a razão da falha no log de auditoria-sistema. O backup manterá as últimas 3 cópias por padrão',\n        openrestyDeleteHelper: 'Excluir forçadamente o OpenResty excluirá todos os sites. Deseja continuar?',\n        downloadLogHelper1: 'Todos os logs do aplicativo {0} estão prestes a ser baixados. Deseja continuar?',\n        downloadLogHelper2: 'Os últimos {1} logs do aplicativo {0} estão prestes a ser baixados. Deseja continuar?',\n        syncAllAppHelper: 'Todos os aplicativos serão sincronizados. Deseja continuar?',\n        hostModeHelper:\n            'O modo de rede atual do aplicativo é o modo host. Se precisar abrir a porta, abra-a manualmente na página do firewall.',\n        showLocal: 'Exibir aplicativos locais',\n        reload: 'Recarregar',\n        upgradeWarn:\n            'Atualizar o aplicativo substituirá o arquivo docker-compose.yml. Se houver alterações, você pode clicar para visualizar a comparação do arquivo',\n        openclawHttpsUpgradeNoticeTitle:\n            'Nota: As instruções a seguir se aplicam apenas a usuários que estão atualizando o OpenClaw de versões anteriores à 2026.3.13:',\n        openclawHttpsUpgradeNoticeItem1:\n            'Depois que o agente implantado for atualizado, vá para Configuração -> Ajustes -> Segurança e adicione manualmente o endereço de acesso.',\n        openclawHttpsUpgradeNoticeItem2:\n            'A nova versão agora exige HTTPS para acessar o agente. Se você usava um site com proxy reverso, altere o destino do proxy para https://IP:Port.',\n        newVersion: 'Nova versão',\n        oldVersion: 'Versão atual',\n        composeDiff: 'Comparação de arquivo',\n        showDiff: 'Exibir comparação',\n        useNew: 'Usar versão personalizada',\n        useDefault: 'Usar versão padrão',\n        useCustom: 'Personalizar docker-compose.yml',\n        useCustomHelper:\n            'Usar um arquivo docker-compose.yml personalizado pode causar falha na atualização do aplicativo. Se não for necessário, não marque esta opção.',\n        diffHelper:\n            'O lado esquerdo é a versão antiga, o lado direito é a nova versão. Após editar, clique para salvar a versão personalizada',\n        pullImage: 'Puxar Imagem',\n        pullImageHelper: 'Execute o comando docker pull para puxar a imagem antes de iniciar o aplicativo',\n        deleteImage: 'Excluir Imagem',\n        deleteImageHelper:\n            'Exclua a imagem relacionada ao aplicativo. A tarefa não será encerrada se a exclusão falhar.',\n        requireMemory: 'Requisito de Memória',\n        supportedArchitectures: 'Arquiteturas Suportadas',\n        link: 'Link',\n        showCurrentArch: 'Aplicações da arquitetura atual do servidor',\n        syncLocalApp: 'Sincronizar Aplicativo Local',\n        memoryRequiredHelper: 'O aplicativo atual requer {0} de memória',\n        gpuConfig: 'Ativar Suporte a GPU',\n        gpuConfigHelper:\n            'Certifique-se de que a máquina possui uma GPU NVIDIA e tenha os drivers NVIDIA e NVIDIA Docker Container Toolkit instalados',\n        webUI: 'Endereço de Acesso Web',\n        webUIPlaceholder: 'Exemplo: example.com:8080/login',\n        defaultWebDomain: 'Endereço de Acesso Padrão',\n        defaultWebDomainHepler: 'Se a porta do aplicativo for 8080, o endereço será http(s)://endereço padrão:8080',\n        webUIConfig:\n            'O nó atual não tem um endereço de acesso padrão configurado. Por favor, defina-o nos parâmetros do aplicativo ou vá para as configurações do painel para configurar!',\n        toLink: 'Ir para',\n        customAppHelper:\n            'Antes de instalar um pacote de loja de aplicativos personalizado, certifique-se de que não há aplicativos instalados.',\n        forceUninstall: 'Desinstalação Forçada',\n        syncCustomApp: 'Sincronizar Aplicativo Personalizado',\n        ignoreAll: 'Ignorar todas as versões subsequentes',\n        ignoreVersion: 'Ignorar versão especificada',\n        specifyIP: 'Vincular IP do Host',\n        specifyIPHelper:\n            'Defina o endereço do host/interface de rede para vincular a porta (se você não tiver certeza sobre isso, por favor, não preencha)',\n        uninstallDeleteBackup: 'Desinstalar Aplicativo - Excluir Backup',\n        uninstallDeleteImage: 'Desinstalar Aplicativo - Excluir Imagem',\n        upgradeBackup: 'Fazer Backup do Aplicativo Antes de Atualizar',\n        noAppHelper:\n            'Nenhuma aplicação detectada, por favor vá ao centro de tarefas para visualizar o log de sincronização da loja de aplicativos',\n        isEdirWarn: 'Detectada modificação no arquivo docker-compose.yml, por favor verifique a comparação',\n    },\n    website: {\n        primaryDomain: 'Domínio principal',\n        otherDomains: 'Outros domínios',\n        static: 'Estático',\n        deployment: 'Implantação',\n        supportUpType:\n            'Apenas o formato de arquivo .tar.gz é suportado, e o pacote compactado deve conter o arquivo {0}.json',\n        proxy: 'Proxy reverso',\n        alias: 'Alias',\n        ftpUser: 'Conta FTP',\n        ftpPassword: 'Senha FTP',\n        ftpHelper:\n            'Após criar um site, uma conta FTP correspondente será criada e o diretório FTP será vinculado ao diretório do site.',\n        remark: 'Observação',\n        manageGroup: 'Gerenciar grupos',\n        groupSetting: 'Gerenciamento de grupos',\n        createGroup: 'Criar grupo',\n        appNew: 'Novo Aplicativo',\n        appInstalled: 'Aplicativo instalado',\n        delete: 'Excluir site',\n        deleteApp: 'Excluir Aplicativo',\n        deleteBackup: 'Excluir Backup',\n        domain: 'Domínio',\n        domainHelper: 'Um domínio por linha.\\nSuporta curinga \"*\" e endereço IP.\\nSuporta adição de porta.',\n        addDomain: 'Adicionar',\n        domainConfig: 'Domínios',\n        defaultDoc: 'Documento',\n        perserver: 'Concorrência',\n        perserverHelper: 'Limitar a concorrência máxima do site atual',\n        perip: 'IP único',\n        peripHelper: 'Limitar o número máximo de acessos simultâneos a um único IP',\n        rate: 'Limites de tráfego',\n        rateLimit: 'Limite de taxa por requisição',\n        rateHelper: 'Limitar o fluxo de cada requisição (unidade: KB)',\n        rateLimitHelper: 'Limita a taxa de transferência por requisição (unidade: KB/s)',\n        limitHelper: 'Habilitar controle de fluxo',\n        other: 'Outro',\n        currentSSL: 'Certificado atual',\n        dnsAccount: 'Conta DNS',\n        applySSL: 'Solicitação de certificado',\n        SSLList: 'Lista de certificados',\n        createDnsAccount: 'Conta DNS',\n        aliyun: 'Aliyun',\n        aliEsa: 'Aliyun ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: 'Análise manual',\n        key: 'Chave',\n        check: 'Ver',\n        acmeAccountManage: 'Contas ACME',\n        email: 'Email',\n        acmeAccount: 'Conta ACME',\n        provider: 'Método de verificação',\n        dnsManual: 'Resolução manual',\n        expireDate: 'Data de expiração',\n        brand: 'Organização',\n        deploySSL: 'Implantação',\n        deploySSLHelper: 'Tem certeza de que deseja implantar o certificado?',\n        ssl: 'Certificado | Certificados',\n        dnsAccountManage: 'Provedores DNS',\n        renewSSL: 'Renovar',\n        renewHelper: 'Tem certeza de que deseja renovar o certificado?',\n        renewSuccess: 'Certificado renovado',\n        enableHTTPS: 'Habilitar',\n        aliasHelper: 'Alias é o nome do diretório do site',\n        lastBackupAt: 'Último backup realizado em',\n        null: 'nenhum',\n        nginxConfig: 'Configuração Nginx',\n        websiteConfig: 'Configurações do site',\n        proxySettings: 'Configurações de Proxy',\n        advancedSettings: 'Configurações Avançadas',\n        cacheSettings: 'Configurações de Cache',\n        sniSettings: 'Configurações de SNI',\n        basic: 'Básico',\n        source: 'Configuração',\n        security: 'Segurança',\n        nginxPer: 'Ajuste de desempenho',\n        neverExpire: 'Nunca',\n        setDefault: 'Definir como padrão',\n        default: 'Padrão',\n        deleteHelper: 'O status do aplicativo relacionado está anômalo, por favor verifique',\n        toApp: 'Ir para a lista de aplicativos instalados',\n        cycle: 'Ciclo',\n        frequency: 'Frequência',\n        ccHelper:\n            'Solicitar cumulativamente a mesma URL mais de {1} vezes em {0} segundos, aciona a defesa CC, bloqueia este IP',\n        mustSave: 'A modificação precisa ser salva para ter efeito',\n        fileExt: 'Extensão de arquivo',\n        fileExtBlock: 'Lista negra de extensões de arquivo',\n        value: 'valor',\n        enable: 'Habilitar',\n        proxyAddress: 'Endereço Proxy',\n        proxyHelper: 'Exemplo: 127.0.0.1:8080',\n        forceDelete: 'Excluir forçadamente',\n        forceDeleteHelper: 'A exclusão forçada ignorará erros durante o processo de exclusão e excluirá os metadados.',\n        deleteAppHelper: 'Excluir aplicativos associados e backups de aplicativos ao mesmo tempo',\n        deleteBackupHelper: 'Excluir também backups do site.',\n        deleteDatabaseHelper: 'Também excluir o banco de dados associado ao site',\n        deleteConfirmHelper:\n            'A operação de exclusão não pode ser desfeita. Digite <span style=\"color:red\"> \"{0}\" </span> para confirmar a exclusão.',\n        staticPath: 'O diretório principal correspondente é ',\n        limit: 'Plano',\n        blog: 'Fórum/Blog',\n        imageSite: 'Site de Imagens',\n        downloadSite: 'Site de Download',\n        shopSite: 'Loja',\n        doorSite: 'Portal',\n        qiteSite: 'Empresarial',\n        videoSite: 'Site de Vídeo',\n        errLog: 'Erro de log',\n        stopHelper:\n            'Após parar o site, ele não poderá ser acessado normalmente, e o usuário verá a página de parada do site atual ao visitar. Deseja continuar?',\n        startHelper: 'Após ativar o site, os usuários podem acessar o conteúdo do site normalmente, deseja continuar?',\n        sitePath: 'Diretório',\n        siteAlias: 'Alias do site',\n        primaryPath: 'Diretório raiz',\n        folderTitle: 'O site contém principalmente as seguintes pastas',\n        wafFolder: 'Regras de firewall',\n        indexFolder: 'Diretório raiz do site',\n        sslFolder: 'Certificado do site',\n        enableOrNot: 'Habilitar',\n        oldSSL: 'Certificado existente',\n        manualSSL: 'Importar certificado',\n        select: 'Selecionar',\n        selectSSL: 'Selecionar certificado',\n        privateKey: 'Chave (KEY)',\n        certificate: 'Certificado (formato PEM)',\n        HTTPConfig: 'Opções HTTP',\n        HTTPSOnly: 'Bloquear requisições HTTP',\n        HTTPToHTTPS: 'Redirecionar para HTTPS',\n        HTTPAlso: 'Permitir requisições HTTP diretas',\n        sslConfig: 'Opções SSL',\n        disableHTTPS: 'Desabilitar HTTPS',\n        disableHTTPSHelper: 'Desabilitar HTTPS removerá a configuração do certificado relacionada, deseja continuar?',\n        SSLHelper:\n            'Nota: Não use certificados SSL para sites ilegais.\\nSe o acesso HTTPS não funcionar após a ativação, verifique se o grupo de segurança liberou corretamente a porta 443.',\n        SSLConfig: 'Configurações do certificado',\n        SSLProConfig: 'Configurações do protocolo',\n        supportProtocol: 'Versão do protocolo',\n        encryptionAlgorithm: 'Algoritmo de criptografia',\n        notSecurity: '(não seguro)',\n        encryptHelper:\n            \"O Let's Encrypt tem um limite de frequência para a emissão de certificados, mas é suficiente para atender necessidades normais. Operações muito frequentes podem causar falha na emissão. Para restrições específicas, consulte <a target='_blank' href='https://letsencrypt.org/zh-cn/docs/rate-limits/'>documentação oficial</a>\",\n        ipValue: 'Valor',\n        ext: 'Extensão de arquivo',\n        wafInputHelper: 'Digite os dados por linha, uma linha por vez',\n        data: 'dados',\n        ever: 'permanente',\n        nextYear: 'Um ano depois',\n        noLog: 'Nenhum log encontrado',\n        defaultServer: 'Site padrão',\n        noDefaultServer: 'Não definido',\n        defaultServerHelper:\n            'Após definir o site padrão, todos os nomes de domínio e IPs desvinculados serão redirecionados para o site padrão\\nIsso pode evitar eficazmente a resolução maliciosa\\nNo entanto, isso também pode causar falha na interceptação de nomes de domínio não autorizados pelo WAF',\n        restoreHelper: 'Tem certeza de que deseja restaurar com este backup?',\n        websiteDeploymentHelper: 'Use um aplicativo instalado ou crie um novo aplicativo para criar um site.',\n        websiteStatictHelper: 'Crie um diretório de site no host.',\n        websiteProxyHelper:\n            'Use proxy reverso para redirecionar um serviço existente. Por exemplo, se um serviço estiver instalado e rodando na porta 8080, o endereço do proxy será \"http://127.0.0.1:8080\".',\n        runtimeProxyHelper: 'Use o ambiente de execução do site para criar um site.',\n        runtime: 'Ambiente de execução',\n        deleteRuntimeHelper:\n            'O aplicativo de Runtime precisa ser excluído junto com o site, por favor, manuseie com cuidado',\n        proxyType: 'Tipo de rede',\n        unix: 'Rede Unix',\n        tcp: 'Rede TCP/IP',\n        phpFPM: 'Configuração FPM',\n        phpConfig: 'Configuração PHP',\n        updateConfig: 'Atualizar configurações',\n        isOn: 'Ligado',\n        isOff: 'Desligado',\n        rewrite: 'Pseudo-estático',\n        rewriteMode: 'Plano',\n        current: 'Atual',\n        rewriteHelper:\n            'Se configurar pseudo-estático e o site se tornar inacessível, tente reverter para as configurações padrão.',\n        runDir: 'Diretório de execução',\n        runUserHelper:\n            'Para sites implantados através do ambiente de execução PHP, você deve definir o proprietário e o grupo dos arquivos e pastas de índice e subdiretórios para 1000. Para o ambiente PHP local, consulte as configurações de usuário e grupo do PHP-FPM local',\n        userGroup: 'Usuário/Grupo',\n        uGroup: 'Grupo',\n        proxyPath: 'Caminho do proxy',\n        proxyPass: 'URL de destino',\n        cache: 'Cache',\n        cacheTime: 'Duração do cache',\n        enableCache: 'Habilitar cache',\n        proxyHost: 'Host do proxy',\n        disabled: 'Parado',\n        startProxy: 'Isso iniciará o proxy reverso. Deseja continuar?',\n        stopProxy: 'Isso interromperá o proxy reverso. Deseja continuar?',\n        sourceFile: 'Fonte',\n        proxyHelper1: 'Ao acessar este diretório, o conteúdo da URL de destino será retornado e exibido.',\n        proxyPassHelper: 'A URL de destino deve ser válida e acessível.',\n        proxyHostHelper: 'Passe o nome de domínio no cabeçalho da requisição para o servidor proxy.',\n        replacementHelper:\n            'Até 5 substituições podem ser adicionadas, deixe em branco se não houver substituição necessária.',\n        modifier: 'Regras de correspondência',\n        modifierHelper:\n            'Exemplo: \"=\" é correspondência exata, \"~\" é correspondência regular, \"^~\" corresponde ao início do caminho, etc.',\n        replace: 'Substituições de texto',\n        replaceHelper:\n            'O recurso de substituição de texto do nginx permite a substituição de strings no conteúdo da resposta durante o proxy reverso. É comumente usado para modificar links, endereços de API, etc., em arquivos HTML, CSS, JavaScript e outros retornados pelo backend. Suporta correspondência de expressão regular para necessidades complexas de substituição de conteúdo.',\n        addReplace: 'Adicionar',\n        replaced: 'String de busca (não pode estar vazia)',\n        replaceText: 'Substituir por string',\n        replacedErr: 'A string de busca não pode estar vazia',\n        replacedErr2: 'A string de busca não pode ser repetida',\n        replacedListEmpty: 'Nenhuma regra de substituição de texto',\n        proxySslName: 'Nome SNI do Proxy',\n        basicAuth: 'Autenticação básica',\n        editBasicAuthHelper:\n            'A senha é criptografada de forma assimétrica e não pode ser exibida. A edição requer a redefinição da senha',\n        antiLeech: 'Anti-leech',\n        extends: 'Extensão',\n        browserCache: 'Cache',\n        noModify: 'Não Modificar',\n        serverCache: 'Cache do servidor',\n        leechLog: 'Registrar log anti-leech',\n        accessDomain: 'Domínios permitidos',\n        leechReturn: 'Recurso de resposta',\n        noneRef: 'Permitir referrer vazio',\n        disable: 'não habilitado',\n        disableLeechHelper: 'Se deseja desabilitar o anti-leech',\n        disableLeech: 'Desabilitar anti-leech',\n        ipv6: 'Ouvir IPv6',\n        leechReturnError: 'Por favor, preencha o código de status HTTP',\n        blockedRef: 'Permitir Referer não padrão',\n        accessControl: 'Controle anti-leech',\n        leechcacheControl: 'Controle de cache',\n        logEnableControl: 'Registrar solicitações de ativos estáticos',\n        leechSpecialValidHelper:\n            \"Quando 'Permitir Referer vazio' estiver ativado, as solicitações sem Referer (acesso direto etc.) não serão bloqueadas; ao ativar 'Permitir Referer não padrão', qualquer Referer que não comece com http/https será permitido (solicitações de cliente etc.).\",\n        leechInvalidReturnHelper: 'Código de status HTTP retornado após bloquear solicitações de hotlink',\n        leechlogControlHelper:\n            'Registra solicitações de ativos estáticos; geralmente desativado em produção para evitar logs excessivos e ruidosos',\n        selectAcme: 'Selecionar conta Acme',\n        imported: 'Criado manualmente',\n        importType: 'Tipo de importação',\n        pasteSSL: 'Colar código',\n        localSSL: 'Selecionar arquivo do servidor',\n        privateKeyPath: 'Caminho do arquivo da chave privada',\n        certificatePath: 'Caminho do arquivo do certificado',\n        ipWhiteListHelper:\n            'O papel da lista de permissões de IP: todas as regras são inválidas para a lista de permissões de IP',\n        redirect: 'Redirecionar',\n        sourceDomain: 'Domínio de origem',\n        targetURL: 'Endereço da URL de destino',\n        keepPath: 'Parâmetros URI',\n        path: 'caminho',\n        redirectType: 'Tipo de redirecionamento',\n        redirectWay: 'Modo',\n        keep: 'manter',\n        notKeep: 'Não manter',\n        redirectRoot: 'Redirecionar para a página inicial',\n        redirectHelper: 'Redirecionamento permanente 301, redirecionamento temporário 302',\n        changePHPVersionWarn:\n            'Trocar a versão do PHP vai excluir o contêiner PHP original (o código do site montado não será perdido), continuar?',\n        changeVersion: 'Trocar versão',\n        retainConfig: 'Deseja manter os arquivos php-fpm.conf e php.ini?',\n        runDirHelper2:\n            'Por favor, certifique-se de que o diretório de execução secundário esteja no diretório do índice',\n        openrestyHelper:\n            'Porta HTTP padrão do OpenResty: {0} Porta HTTPS: {1}, o que pode afetar o acesso ao domínio do site e o redirecionamento forçado para HTTPS',\n        primaryDomainHelper: 'Exemplo: exemplo.com ou exemplo.com:8080',\n        acmeAccountType: 'Tipo de conta',\n        keyType: 'Algoritmo de chave',\n        tencentCloud: 'Tencent Cloud',\n        containWarn: 'O nome de domínio contém o domínio principal, por favor, reentre',\n        rewriteHelper2:\n            'Aplicações como o WordPress instaladas a partir da loja de aplicativos geralmente vêm com configuração de pseudo-estática predefinida. Reconfigurá-las pode causar erros.',\n        websiteBackupWarn:\n            'Somente suporta importar backups locais, importar backups de outras máquinas pode causar falha na recuperação',\n        ipWebsiteWarn:\n            'Sites com IP como nomes de domínio precisam ser configurados como site padrão para serem acessados normalmente',\n        hstsHelper: 'Ativar HSTS pode aumentar a segurança do site',\n        includeSubDomains: 'SubDomains',\n        hstsIncludeSubDomainsHelper:\n            'Quando ativado, a política HSTS será aplicada a todos os subdomínios do domínio atual.',\n        defaultHtml: 'Página padrão',\n        website404: 'Página de erro 404 do site',\n        domain404: 'O domínio do site não existe',\n        indexHtml: 'Índice para site estático',\n        stopHtml: 'Site parado',\n        indexPHP: 'Índice para site PHP',\n        sslExpireDate: 'Data de expiração do certificado',\n        website404Helper:\n            'A página de erro 404 do site suporta apenas sites com ambiente de execução PHP e sites estáticos',\n        sni: 'SNI de origem',\n        sniHelper:\n            'Quando o proxy reverso de backend for HTTPS, você pode precisar configurar o SNI de origem. Consulte a documentação do provedor de serviços CDN para mais detalhes.',\n        huaweicloud: 'Huawei Cloud',\n        createDb: 'Criar Banco de Dados',\n        enableSSLHelper: 'A falha ao ativar o SSL não afetará a criação do site.',\n        batchAdd: 'Adicionar Domínios em Lote',\n        batchInput: 'Entrada em Lote',\n        domainNotFQDN: 'Este domínio pode não estar acessível na rede pública',\n        domainInvalid: 'Formato de domínio inválido',\n        domainBatchHelper:\n            'Um domínio por linha, formato: domínio:porta@ssl\\nExemplo: example.com:443@ssl ou example.com',\n        generateDomain: 'Gerar',\n        global: 'Global',\n        subsite: 'Subsite',\n        subsiteHelper:\n            'Um subsite pode selecionar o diretório de um site PHP ou estático existente como seu diretório raiz.',\n        parentWebsite: 'Site Pai',\n        deleteSubsite: 'Para excluir o site atual, você deve primeiro excluir o subsite {0}.',\n        loadBalance: 'Balanceamento de Carga',\n        server: 'Nó',\n        algorithm: 'Algoritmo',\n        ipHash: 'Hash de IP',\n        ipHashHelper:\n            'Distribui as solicitações para um servidor específico com base no endereço IP do cliente, garantindo que um cliente específico seja sempre roteado para o mesmo servidor.',\n        leastConn: 'Menos Conexões',\n        leastConnHelper: 'Envia solicitações para o servidor com o menor número de conexões ativas.',\n        leastTime: 'Menor Tempo',\n        leastTimeHelper: 'Envia solicitações para o servidor com o menor tempo de conexão ativa.',\n        defaultHelper:\n            'Método padrão, as solicitações são distribuídas uniformemente para cada servidor. Se o servidor tiver configuração de peso, as solicitações são distribuídas de acordo com o peso especificado. Servidores com pesos mais altos recebem mais solicitações.',\n        weight: 'Peso',\n        maxFails: 'Máximo de Falhas',\n        maxConns: 'Máximo de Conexões',\n        strategy: 'Estratégia',\n        strategyDown: 'Desativar',\n        strategyBackup: 'Backup',\n        ipHashBackupErr: 'Hash IP não suporta nós de backup',\n        failTimeout: 'Tempo limite de falha',\n        failTimeoutHelper:\n            'O comprimento da janela de tempo para verificações de integridade do servidor. Quando o número acumulado de falhas atinge o limite dentro deste período, o servidor será removido temporariamente e repetido após a mesma duração. Padrão 10 segundos',\n        staticChangePHPHelper: 'Atualmente um site estático, pode ser alterado para um site PHP.',\n        proxyCache: 'Cache de Proxy Reverso',\n        cacheLimit: 'Limite de Espaço de Cache',\n        shareCache: 'Tamanho da Memória de Contagem de Cache',\n        cacheExpire: 'Tempo de Expiração do Cache',\n        shareCacheHelper: '1M de memória pode armazenar aproximadamente 8000 objetos de cache.',\n        cacheLimitHelper: 'Exceder o limite excluirá automaticamente os caches antigos.',\n        cacheExpireHelper: 'Caches não acessados dentro do tempo de expiração serão excluídos.',\n        realIP: 'IP Real',\n        ipFrom: 'Fonte do IP',\n        ipFromHelper:\n            'Ao configurar fontes de IP confiáveis, o OpenResty analisará as informações de IP no cabeçalho HTTP para identificar e registrar com precisão o endereço IP real do visitante, incluindo nos logs de acesso.',\n        ipFromExample1:\n            'Se o frontend for uma ferramenta como o Frp, você pode preencher o endereço IP do Frp, como 127.0.0.1.',\n        ipFromExample2: 'Se o frontend for um CDN, você pode preencher o intervalo de IPs do CDN.',\n        ipFromExample3:\n            'Se não tiver certeza, você pode preencher 0.0.0.0/0 (IPv4) ou ::/0 (IPv6). [Nota: Permitir qualquer fonte de IP não é seguro.]',\n        http3Helper:\n            'O HTTP/3 é uma versão atualizada do HTTP/2, fornecendo velocidades de conexão mais rápidas e melhor desempenho. No entanto, nem todos os navegadores suportam HTTP/3, e ativá-lo pode fazer com que alguns navegadores não consigam acessar o site.',\n        cors: 'Compartilhamento de Recursos de Origem Cruzada (CORS)',\n        enableCors: 'Habilitar CORS',\n        allowOrigins: 'Domínios permitidos',\n        allowMethods: 'Métodos de solicitação permitidos',\n        allowHeaders: 'Cabeçalhos de solicitação permitidos',\n        allowCredentials: 'Permitir envio de cookies',\n        preflight: 'Resposta rápida a solicitação de preflight',\n        preflightHleper:\n            'Quando habilitado, quando o navegador envia uma solicitação de preflight de origem cruzada (solicitação OPTIONS), o sistema retornará automaticamente um código de status 204 e definirá os cabeçalhos de resposta de origem cruzada necessários',\n        changeDatabase: 'Alterar Banco de Dados',\n        changeDatabaseHelper1: 'A associação do banco de dados é usada para backup e restauração do site.',\n        changeDatabaseHelper2: 'Alternar para outro banco de dados pode tornar backups anteriores irrecuperáveis.',\n        saveCustom: 'Salvar como Modelo',\n        rainyun: 'Rainyun',\n        volcengine: 'Volcengine',\n        runtimePortHelper: 'O ambiente de runtime atual possui várias portas. Por favor, selecione uma porta de proxy.',\n        runtimePortWarn: 'O ambiente de execução atual não possui portos, não é possível proxiar',\n        cacheWarn: 'Por favor, desligue o interruptor de cache no proxy reverso primeiro',\n        loadBalanceHelper:\n            'Após criar o balanceamento de carga, vá para \"Proxy Reverso\", adicione um proxy e configure o endereço de backend para: http://<nome do balanceamento de carga>.',\n        favorite: 'Favorito',\n        cancelFavorite: 'Cancelar Favorito',\n        useProxy: 'Usar Proxy',\n        useProxyHelper: 'Usar o endereço do servidor proxy nas configurações do painel',\n        westCN: 'West Digital',\n        openBaseDir: 'Prevenir Ataques entre Sites',\n        openBaseDirHelper:\n            'open_basedir é usado para restringir o caminho de acesso a arquivos PHP, ajudando a prevenir acesso entre sites e aumentar a segurança',\n        serverCacheTime: 'Tempo de Cache do Servidor',\n        serverCacheTimeHelper:\n            'O tempo que uma requisição é armazenada em cache no servidor. Durante este período, requisições idênticas retornarão o resultado em cache diretamente, sem pedir ao servidor de origem.',\n        browserCacheTime: 'Tempo de Cache do Navegador',\n        browserCacheTimeHelper:\n            'O tempo que os recursos estáticos são armazenados em cache localmente no navegador, reduzindo requisições redundantes. Os usuários usarão o cache local diretamente antes de expirar ao atualizar a página.',\n        donotLinkeDB: 'Não Vincular Banco de Dados',\n        toWebsiteDir: 'Entrar no Diretório do Site',\n        execParameters: 'Parâmetros de Execução',\n        extCommand: 'Comando Suplementar',\n        mirror: 'Fonte de Espelho',\n        execUser: 'Usuário Executando',\n        execDir: 'Diretório de Execução',\n        packagist: 'Espelho Completo da China',\n        batchOperate: 'Operação em Lote',\n        batchOperateHelper: 'Lote {0} sites, continuar operação?',\n        stream: 'Proxy TCP/UDP',\n        streamPorts: 'Portas de escuta',\n        streamPortsHelper:\n            'Defina o número da porta de escuta externa, os clientes acessarão o serviço através desta porta, separados por vírgulas, por exemplo: 5222,5223',\n        streamHelper: 'Encaminhamento de Portas e Balanceamento de Carga TCP/UDP',\n        udp: 'Habilitar UDP',\n        syncHtmlHelper: 'Sincronizar com PHP e sites estáticos',\n        wafValueHelper: 'Valor',\n        parentWbeiste: 'Site pai',\n        shareCahe: 'Tamanho de memória da contagem de cache',\n        shareCaheHelper: 'Aproximadamente 8000 objetos de cache podem ser armazenados por 1M de memória',\n        cacheExpireJHelper: 'O cache será excluído se não houver acesso após o tempo de expiração',\n    },\n    php: {\n        short_open_tag: 'Suporte para short tags',\n        max_execution_time: 'Tempo máximo de execução do script',\n        max_input_time: 'Tempo máximo de entrada de dados',\n        memory_limit: 'Limite de memória para scripts',\n        post_max_size: 'Tamanho máximo para dados enviados via POST',\n        file_uploads: 'Permitir upload de arquivos',\n        upload_max_filesize: 'Tamanho máximo permitido para upload de arquivos',\n        max_file_uploads: 'Número máximo de arquivos permitidos para upload simultâneo',\n        default_socket_timeout: 'Tempo limite do socket',\n        error_reporting: 'Nível de relatório de erros',\n        display_errors: 'Exibir informações detalhadas de erro',\n        cgi_fix_pathinfo: 'Ativar suporte a pathinfo',\n        date_timezone: 'Fuso horário',\n        disableFunction: 'Desabilitar função',\n        disableFunctionHelper:\n            'Informe a função a ser desabilitada, como exec. Para múltiplas funções, separe com vírgulas',\n        uploadMaxSize: 'Limite de upload',\n        indexHelper:\n            'Para garantir o funcionamento normal do site em PHP, coloque o código no diretório de índice e evite renomeações',\n        extensions: 'Modelos de extensão',\n        extension: 'Extensão',\n        extensionHelper: 'Para múltiplas extensões, separe com vírgulas',\n        toExtensionsList: 'Ver lista de extensões',\n        containerConfig: 'Configuração do Contêiner',\n        containerConfigHelper:\n            'Variáveis de ambiente e outras informações podem ser modificadas em Configuração - Configuração do Contêiner após a criação',\n        dateTimezoneHelper: 'Exemplo: TZ=Asia/Shanghai (Adicione conforme necessário)',\n        extensionsHelper: 'Para várias extensões, separe por vírgulas',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: 'The hash table size of the server name',\n        clientHeaderBufferSizeHelper: 'The header buffer size requested by the client',\n        clientMaxBodySizeHelper: 'Maximum Upload File',\n        keepaliveTimeoutHelper: 'Connection Timeout',\n        gzipMinLengthHelper: 'Minimum Compressed File',\n        gzipCompLevelHelper: 'Compression Rate',\n        gzipHelper: 'Enable compression for transmission',\n        connections: 'Active connections',\n        accepts: 'Accepts',\n        handled: 'Handled',\n        requests: 'Requests',\n        reading: 'Reading',\n        writing: 'Writing',\n        waiting: 'Waiting',\n        status: 'Current Status',\n        configResource: 'Configuration',\n        saveAndReload: 'Save and reload',\n        clearProxyCache: 'Clean reverse proxy cache',\n        clearProxyCacheWarn:\n            'All websites that have configured with cache will be affected and \"OpenResty\" will be restarted. Do you want to continue?',\n        create: 'Criar Módulo',\n        update: 'Editar Módulo',\n        params: 'Parâmetros',\n        packages: 'Pacotes',\n        script: 'Script',\n        module: 'Módulo',\n        build: 'Construir',\n        buildWarn:\n            'Construir OpenResty requer a reserva de certa quantidade de CPU e memória, e o processo pode ser demorado, por favor, seja paciente.',\n        mirrorUrl: 'Fonte de Software',\n        paramsHelper: 'Por exemplo: --add-module=/tmp/ngx_brotli',\n        packagesHelper: 'Por exemplo: git,curl separados por vírgulas',\n        scriptHelper:\n            'Script a ser executado antes da compilação, geralmente para baixar o código-fonte do módulo, instalar dependências, etc.',\n        buildHelper:\n            'Clique em Construir após adicionar/modificar um módulo. Construção bem-sucedida reiniciará automaticamente o OpenResty.',\n        defaultHttps: 'HTTPS Anti-tampering',\n        defaultHttpsHelper1: 'A ativação desta opção pode resolver problemas de adulteração HTTPS.',\n        sslRejectHandshake: 'Rejeitar handshake SSL padrão',\n        sslRejectHandshakeHelper:\n            'Ativar isso pode evitar vazamento de certificados, definir um site padrão invalidará esta configuração',\n    },\n    ssl: {\n        create: 'Solicitar',\n        provider: 'Tipo',\n        manualCreate: 'Criado manualmente',\n        acmeAccount: 'Conta ACME',\n        resolveDomain: 'Resolver nome de domínio',\n        err: 'Erro',\n        value: 'Valor do registro',\n        dnsResolveHelper: 'Vá ao provedor de serviço de DNS para adicionar os seguintes registros de resolução:',\n        detail: 'Detalhes',\n        msg: 'Informação',\n        ssl: 'Certificado',\n        key: 'Chave privada',\n        startDate: 'Data de início',\n        organization: 'Organização emissora',\n        renewConfirm: 'Isso renovará um novo certificado para o domínio {0}. Deseja continuar?',\n        autoRenew: 'Renovação automática',\n        autoRenewHelper: 'Renova automaticamente 30 dias antes da expiração',\n        renewSuccess: 'Renovação bem-sucedida',\n        renewWebsite: 'Este certificado está associado aos seguintes sites e será aplicado a eles simultaneamente',\n        createAcme: 'Criar conta',\n        acmeHelper: 'A conta ACME é usada para solicitar certificados gratuitos',\n        upload: 'Importar',\n        applyType: 'Tipo',\n        apply: 'Renovar',\n        applyStart: 'Início da solicitação do certificado',\n        getDnsResolve: 'Obtendo o valor de resolução DNS, por favor, aguarde...',\n        selfSigned: 'CA autoassinado',\n        ca: 'Autoridade certificadora',\n        commonName: 'Nome comum',\n        caName: 'Nome da autoridade certificadora',\n        company: 'Nome da organização',\n        department: 'Nome da unidade organizacional',\n        city: 'Nome da localidade',\n        province: 'Estado ou província',\n        country: 'Nome do país (código de 2 letras)',\n        commonNameHelper: 'Por exemplo, ',\n        selfSign: 'Emitir certificado',\n        days: 'Período de validade',\n        domainHelper: 'Um domínio por linha, suporta * e endereços IP',\n        pushDir: 'Enviar o certificado para o diretório local',\n        dir: 'Diretório',\n        pushDirHelper:\n            'O arquivo do certificado \"fullchain.pem\" e o arquivo de chave \"privkey.pem\" serão gerados neste diretório.',\n        organizationDetail: 'Detalhes da organização',\n        fromWebsite: 'Do site',\n        dnsMauanlHelper:\n            'No modo de resolução manual, você precisa clicar no botão de solicitação após a criação para obter o valor de resolução DNS',\n        httpHelper:\n            'O uso do modo HTTP requer a instalação do OpenResty e não suporta a solicitação de certificados de domínio curinga.',\n        buypassHelper: 'O Buypass não está acessível na China continental',\n        googleHelper: 'Como obter EAB HmacKey e EAB kid',\n        googleCloudHelper: 'A API do Google Cloud não está acessível na maior parte da China continental',\n        skipDNSCheck: 'Pular verificação DNS',\n        skipDNSCheckHelper:\n            'Marque esta opção apenas se enfrentar problemas de timeout durante a solicitação de certificação.',\n        cfHelper: 'Não use a Global API Key',\n        deprecated: 'será descontinuado',\n        deprecatedHelper:\n            'A manutenção foi interrompida e pode ser abandonada em uma versão futura. Use o método Tencent Cloud para análise',\n        disableCNAME: 'Desativar CNAME',\n        disableCNAMEHelper: 'Marque esta opção se o domínio tiver um registro CNAME e a solicitação falhar.',\n        nameserver: 'Servidor DNS',\n        nameserverHelper: 'Use um servidor DNS personalizado para verificar os nomes de domínio.',\n        edit: 'Editar certificado',\n        execShell: 'Executar o script após a solicitação de certificação.',\n        shell: 'Conteúdo do script',\n        shellHelper:\n            'O diretório padrão de execução do script é o diretório de instalação do 1Panel. Se um certificado for enviado para o diretório local, o diretório de execução será o diretório de envio do certificado. O tempo limite padrão de execução é de 30 minutos.',\n        customAcme: 'Serviço ACME Personalizado',\n        customAcmeURL: 'URL do Serviço ACME',\n        baiduCloud: 'Baidu Cloud',\n        pushNode: 'Sincronizar com Outros Nós',\n        pushNodeHelper: 'Enviar para os nós selecionados após a aplicação/renovação',\n        fromMaster: 'Envio do Nó Mestre',\n        hostedZoneID: 'Hosted Zone ID',\n        isIP: 'Certificado de IP',\n        useEAB: 'Usar autenticação EAB',\n    },\n    firewall: {\n        create: 'Criar regra',\n        edit: 'Editar regra',\n        ccDeny: 'Proteção contra CC',\n        ipWhiteList: 'Lista de IPs permitidos',\n        ipBlockList: 'Lista de IPs bloqueados',\n        fileExtBlockList: 'Lista de extensões de arquivo bloqueadas',\n        urlWhiteList: 'Lista de URLs permitidas',\n        urlBlockList: 'Lista de URLs bloqueadas',\n        argsCheck: 'Verificação de parâmetros GET',\n        postCheck: 'Verificação de parâmetros POST',\n        cookieBlockList: 'Lista de cookies bloqueados',\n        dockerHelper:\n            'O firewall atual não pode desativar o mapeamento de porta de contêiner. Aplicativos instalados podem ir para a página [Instalados] para editar parâmetros do aplicativo e configurar regras de liberação de porta.',\n        iptablesHelper:\n            'Detectado que o sistema está usando o firewall {0}. Para mudar para iptables, desinstale-o manualmente primeiro!',\n        quickJump: 'Acesso rápido',\n        used: 'Usado',\n        unUsed: 'Não usado',\n        dockerRestart: 'Operações de firewall exigem reinicialização do serviço Docker',\n        firewallHelper: 'Firewall do sistema {0}',\n        firewallNotStart: 'O firewall do sistema não está habilitado atualmente. Habilite-o primeiro.',\n        restartFirewallHelper: 'Esta operação reiniciará o firewall atual. Deseja continuar?',\n        stopFirewallHelper: 'Isso fará com que o servidor perca a proteção de segurança. Deseja continuar?',\n        startFirewallHelper:\n            'Depois que o firewall for habilitado, a segurança do servidor será melhor protegida. Deseja continuar?',\n        noPing: 'Desativar ping',\n        enableBanPing: 'Bloquear Ping',\n        disableBanPing: 'Desbloquear Ping',\n        noPingTitle: 'Desativar ping',\n        noPingHelper: 'Isso desativará o ping, e o servidor não responderá ao ICMP. Deseja continuar?',\n        onPingHelper: 'Isso ativará o ping, permitindo que hackers descubram seu servidor. Deseja continuar?',\n        changeStrategy: 'Alterar a estratégia {0}',\n        changeStrategyIPHelper1:\n            'Altere a estratégia de endereço IP para [negar]. Após definir o endereço IP, o acesso ao servidor será proibido. Deseja continuar?',\n        changeStrategyIPHelper2:\n            'Altere a estratégia de endereço IP para [permitir]. Após definir o endereço IP, o acesso normal será restaurado. Deseja continuar?',\n        changeStrategyPortHelper1:\n            'Altere a política de porta para [bloquear]. Após definir a política de porta, o acesso externo será negado. Deseja continuar?',\n        changeStrategyPortHelper2:\n            'Altere a política de porta para [aceitar]. Após definir a política de porta, o acesso normal será restaurado. Deseja continuar?',\n        stop: 'Parar',\n        portFormatError: 'Este campo deve ser uma porta válida.',\n        portHelper1: 'Várias portas, ex.: 8080 e 8081',\n        portHelper2: 'Faixa de portas, ex.: 8080-8089',\n        changeStrategyHelper:\n            'Alterar a estratégia [{1}] {0} para [{2}]. Após a definição, {0} acessará {2} externamente. Deseja continuar?',\n        strategy: 'Estratégia',\n        accept: 'Aceitar',\n        drop: 'Bloquear',\n        anyWhere: 'Qualquer',\n        address: 'IPs especificados',\n        addressHelper: 'Suporta endereço IP ou segmento de IP',\n        allow: 'Permitir',\n        deny: 'Negar',\n        addressFormatError: 'Este campo deve ser um endereço IP válido.',\n        addressHelper1: 'Suporta endereço IP ou intervalo de IP. Por exemplo, \"172.16.10.11\" ou \"172.16.10.0/24\".',\n        addressHelper2: 'Para vários endereços IP, separe por vírgula. Por exemplo, \"172.16.10.11, 172.16.0.0/24\".',\n        allIP: 'Todos os IPs',\n        portRule: 'Regra | Regras',\n        forwardRule: 'Regra de redirecionamento de porta | Regras de redirecionamento de porta',\n        ipRule: 'Regra de IP | Regras de IP',\n        userAgent: 'Filtro User-Agent',\n        sourcePort: 'Porta de origem',\n        targetIP: 'IP de destino',\n        targetPort: 'Porta de destino',\n        forwardHelper1:\n            'Se você deseja redirecionar para a porta local, o IP de destino deve ser definido como \"127.0.0.1\".',\n        forwardHelper2: 'Deixe o IP de destino em branco para redirecionar para a porta local.',\n        forwardPortHelper: 'Suporta intervalos de portas, ex. 8080-8089',\n        forwardInboundInterface: 'Interface de Rede de Entrada para Encaminhamento',\n        exportHelper: 'Prestes a exportar {0} regras de firewall. Continuar?',\n        importSuccess: '{0} regras importadas com sucesso',\n        importPartialSuccess: 'Importação concluída: {0} sucesso, {1} falha',\n        ipv4Limit: 'A operação atual suporta apenas endereços IPv4',\n        basicStatus: 'A cadeia atual {0} não está vinculada, vincule primeiro!',\n        baseIptables: 'Serviço iptables',\n        forwardIptables: 'Serviço de Encaminhamento de Porta iptables',\n        advanceIptables: 'Serviço de Configuração Avançada do iptables',\n        initMsg: 'Prestes a inicializar {0}, continuar?',\n        initHelper:\n            'Detectado que {0} não está inicializado. Clique no botão de inicialização na barra de status superior para configurar!',\n        bindHelper:\n            'Vincular - As regras de firewall só entrarão em vigor quando o status estiver vinculado. Confirmar?',\n        unbindHelper:\n            'Desvincular - Quando desvinculado, todas as regras de firewall adicionadas se tornarão inválidas. Prossiga com cautela. Confirmar?',\n        defaultStrategy: 'A política padrão para a cadeia atual {0} é {1}',\n        defaultStrategy2:\n            'A política padrão para a cadeia atual {0} é {1}, o status atual é não vinculado. As regras de firewall adicionadas entrarão em vigor após a vinculação!',\n        filterRule: 'Regra de Filtro',\n        filterHelper:\n            'As regras de filtro permitem controlar o tráfego de rede no nível INPUT/OUTPUT. Configure com cuidado para evitar bloquear o sistema.',\n        chain: 'Cadeia',\n        targetChain: 'Cadeia de Destino',\n        sourceIP: 'IP de Origem',\n        destIP: 'IP de Destino',\n        inboundDirection: 'Direção de Entrada',\n        outboundDirection: 'Direção de Saída',\n        destPort: 'Porta de Destino',\n        action: 'Ação',\n        reject: 'Rejeitar',\n        sourceIPHelper: 'Formato CIDR, ex. 192.168.1.0/24. Deixe vazio para todos os endereços',\n        destIPHelper: 'Formato CIDR, ex. 10.0.0.0/8. Deixe vazio para todos os endereços',\n        portHelper: '0 significa qualquer porta',\n        allPorts: 'Todas as Portas',\n        deleteRuleConfirm: 'Excluirá {0} regras. Continuar?',\n        advancedControl: 'Controle avançado',\n        advancedControlNotAvailable: 'Atualmente usando firewall {0}; regras avançadas suportam apenas iptables',\n        destination: 'Destino',\n    },\n    runtime: {\n        runtime: 'Runtime',\n        workDir: 'Diretório de trabalho',\n        localHelper: 'Para problemas de instalação em ambiente local e uso em ambiente offline, consulte ',\n        versionHelper: 'Versão do PHP, por exemplo, v8.0',\n        buildHelper:\n            'Quanto mais extensões, maior será o uso de CPU durante a criação da imagem. As extensões podem ser instaladas após a criação do ambiente.',\n        openrestyWarn: 'É necessário atualizar o PHP para OpenResty versão 1.21.4.1 ou superior para usar',\n        toupgrade: 'Atualizar',\n        edit: 'Editar runtime',\n        extendHelper:\n            'Extensões não listadas podem ser inseridas e selecionadas manualmente. Por exemplo, digite \"sockets\" e escolha a primeira opção da lista suspensa para visualizar a lista de extensões.',\n        rebuildHelper: 'Após editar as extensões, é necessário recriar a aplicação PHP para aplicar as alterações',\n        rebuild: 'Recriar Aplicação PHP',\n        source: 'Fonte de extensões PHP',\n        ustc: 'Universidade de Ciência e Tecnologia da China',\n        netease: 'Netease',\n        aliyun: 'Alibaba Cloud',\n        default: 'Padrão',\n        tsinghua: 'Universidade Tsinghua',\n        xtomhk: 'Estação de Espelhos XTOM (Hong Kong)',\n        xtom: 'Estação de Espelhos XTOM (Global)',\n        phpsourceHelper: 'Escolha uma fonte adequada de acordo com o ambiente de rede.',\n        appPort: 'Porta da aplicação',\n        externalPort: 'Porta externa',\n        packageManager: 'Gerenciador de pacotes',\n        codeDir: 'Diretório do código',\n        appPortHelper: 'A porta usada pela aplicação.',\n        externalPortHelper: 'A porta exposta para o ambiente externo.',\n        runScript: 'Executar script',\n        runScriptHelper:\n            'A lista de comandos de inicialização é gerada a partir do arquivo package.json no diretório de origem.',\n        open: 'Abrir',\n        operatorHelper: 'A operação {0} será realizada no ambiente selecionado. Deseja continuar?',\n        taobao: 'Taobao',\n        tencent: 'Tencent',\n        imageSource: 'Fonte da imagem',\n        moduleManager: 'Gerenciamento de Módulos',\n        module: 'Módulo',\n        nodeOperatorHelper:\n            'Deseja {0} o módulo {1}? Esta operação pode causar instabilidade no ambiente, confirme antes de prosseguir.',\n        customScript: 'Comando de inicialização personalizado',\n        customScriptHelper:\n            'Por favor, insira o comando de inicialização completo, por exemplo: npm run start. Para comandos de inicialização do PM2, substitua por pm2-runtime, caso contrário irá falhar ao iniciar.',\n        portError: 'Evite repetir a mesma porta.',\n        systemRestartHelper:\n            'Descrição do status: Interrupção - falha ao adquirir status devido à reinicialização do sistema.',\n        javaScriptHelper:\n            'Forneça um comando completo de inicialização. Por exemplo, \"java -jar halo.jar -Xmx1024M -Xms256M\".',\n        javaDirHelper: 'O diretório deve conter arquivos jar, subdiretórios também são aceitos.',\n        goHelper: 'Forneça um comando completo de inicialização. Por exemplo, \"go run main.go\" ou \"./main\".',\n        goDirHelper: 'O diretório ou subdiretório deve conter arquivos Go ou binários.',\n        pythonHelper:\n            'Forneça um comando completo de inicialização. Por exemplo, \"pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000\".',\n        dotnetHelper: 'Por favor, preencha o comando completo de inicialização, por exemplo, dotnet MyWebApp.dll',\n        dirHelper: 'Nota: Preencha o caminho do diretório dentro do contêiner',\n        concurrency: 'Esquema de Concorrência',\n        loadStatus: 'Status de Carga',\n        extraHosts: 'Mapeamento de host',\n        extension: 'Extensão',\n        installExtension: 'Confirma instalar a extensão {0}',\n        loadedExtension: 'Extensão carregada',\n        popularExtension: 'Extensão popular',\n        uninstallExtension: 'Tem certeza de que deseja desinstalar a extensão {0}',\n        phpConfigHelper: 'Alterar a configuração exige reiniciar o ambiente. Deseja continuar',\n        operateMode: 'modo de operação',\n        dynamic: 'dinâmico',\n        static: 'estático',\n        ondemand: 'sob demanda',\n        dynamicHelper:\n            'Ajusta dinamicamente o número de processos; alta flexibilidade para tráfego variável ou baixa memória',\n        staticHelper:\n            'Número fixo de processos, adequado para tráfego estável e alta concorrência, com maior consumo de recursos',\n        ondemandHelper:\n            'Processos iniciam e encerram sob demanda; melhor uso de recursos, mas resposta inicial pode ser lenta',\n        max_children: 'número máximo de processos permitidos',\n        start_servers: 'número de processos ao iniciar',\n        min_spare_servers: 'número mínimo de processos ociosos',\n        max_spare_servers: 'número máximo de processos ociosos',\n        envKey: 'Nome',\n        envValue: 'Valor',\n        environment: 'Variável de ambiente',\n    },\n    process: {\n        pid: 'ID do Processo',\n        ppid: 'ID do Processo Pai',\n        numThreads: 'Threads',\n        memory: 'Memória',\n        diskRead: 'Leitura de Disco',\n        diskWrite: 'Escrita de Disco',\n        netSent: 'Envio (uplink)',\n        netRecv: 'Recebimento (downlink)',\n        numConnections: 'Conexões',\n        startTime: 'Hora de Início',\n        state: 'Estado',\n        running: 'Executando',\n        sleep: 'Dormindo',\n        stop: 'Parado',\n        idle: 'Ocioso',\n        zombie: 'Processo zumbi',\n        wait: 'Aguardando',\n        lock: 'Travado',\n        blocked: 'Bloqueado',\n        cmdLine: 'Comando de Inicialização',\n        basic: 'Básico',\n        mem: 'Memória',\n        openFiles: 'Arquivos Abertos',\n        env: 'Ambientes',\n        noenv: 'Nenhum',\n        net: 'Conexões de Rede',\n        laddr: 'Endereço/porta de origem',\n        raddr: 'Endereço/porta de destino',\n        stopProcess: 'Encerrar',\n        viewDetails: 'Detalhes',\n        stopProcessWarn: 'Tem certeza de que deseja encerrar este processo (PID:{0})?',\n        kill: 'Encerrar Processo',\n        killNow: 'Encerrar Agora',\n        killHelper: 'Encerrar o processo {0} pode fazer com que alguns programas funcionem incorretamente. Continuar?',\n        processName: 'Nome do Processo',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: 'Falha ao recuperar o status do processo, verifique o status do serviço do supervisor.',\n            notSupport:\n                'Serviço Supervisor não detectado. Vá para a página da biblioteca de scripts para instalar manualmente',\n            list: 'Processo daemon | Processos daemon',\n            config: 'Configuração do Supervisor',\n            primaryConfig: 'Localização do arquivo de configuração principal',\n            notSupportCtl:\n                'O supervisorctl não foi detectado. Vá para a página da biblioteca de scripts para instalar manualmente.',\n            user: 'Usuário',\n            command: 'Comando',\n            dir: 'Diretório',\n            numprocs: 'Nº de processos',\n            initWarn:\n                'Isso irá modificar o valor \"files\" na seção \"[include\"] do arquivo de configuração principal. O diretório de outros arquivos de configuração será: \"{diretório de instalação do 1Panel}/1panel/tools/supervisord/supervisor.d/\".',\n            operatorHelper: 'A operação {1} será realizada em {0}, deseja continuar? ',\n            uptime: 'Tempo de execução',\n            notStartWarn: 'O Supervisor não foi iniciado. Inicie-o primeiro.',\n            serviceName: 'Nome do serviço',\n            initHelper:\n                'O serviço Supervisor foi detectado, mas não está inicializado. Clique no botão de inicialização na barra de status superior para configurá-lo.',\n            serviceNameHelper:\n                'Nome do serviço do Supervisor gerenciado pelo systemctl, geralmente supervisor ou supervisord',\n            restartHelper:\n                'Isso reiniciará o serviço após a inicialização, o que fará com que todos os processos daemon existentes sejam interrompidos.',\n            RUNNING: 'Executando',\n            STOPPED: 'Parado',\n            STOPPING: 'Parando',\n            STARTING: 'Iniciando',\n            FATAL: 'Falha ao iniciar',\n            BACKOFF: 'Exceção ao iniciar',\n            ERROR: 'Erro',\n            statusCode: 'Código de status',\n            manage: 'Gerenciamento',\n            autoRestart: 'Reinicialização Automática',\n            EXITED: 'Saiu',\n            autoRestartHelper: 'Se o programa falhar, reiniciar automaticamente',\n            autoStart: 'Início Automático',\n            autoStartHelper: 'Se o serviço deve ser iniciado automaticamente após o Supervisor iniciar',\n        },\n    },\n    disk: {\n        management: 'Gerenciamento de Disco',\n        partition: 'Partição',\n        unmount: 'Desmontar',\n        unmountHelper: 'Deseja desmontar a partição {0}?',\n        mount: 'Montar',\n        partitionAlert:\n            'O particionamento de disco requer formatação do disco, e os dados existentes serão excluídos. Salve ou tire snapshots dos dados com antecedência.',\n        mountPoint: 'Diretório de Montagem',\n        systemDisk: 'Disco do Sistema',\n        unpartitionedDisk: 'Disco Não Particionado',\n        handlePartition: 'Particionar Agora',\n        filesystem: 'Sistema de Arquivos',\n        unmounted: 'Desmontado',\n        cannotOperate: 'Não Pode Operar',\n        systemDiskHelper: 'Dica: O disco atual é o disco do sistema, não pode ser operado.',\n        autoMount: 'Montagem Automática',\n        model: 'Modelo do Dispositivo',\n        diskType: 'Tipo de Disco',\n        serial: 'Número de Série',\n        noFail: 'Falha na montagem não afeta a inicialização do sistema',\n    },\n    xpack: {\n        expiresTrialAlert:\n            'Lembrete: Sua versão de avaliação profissional expirará em {0} dias. Após isso, todas as funcionalidades da versão profissional não estarão mais disponíveis. Por favor, renove ou faça upgrade para a versão oficial a tempo.',\n        expiresAlert:\n            'Lembrete: Sua licença profissional expirará em {0} dias. Após isso, todas as funcionalidades da versão profissional não estarão mais disponíveis. Por favor, renove sua licença para garantir o uso contínuo.',\n        menu: 'Pro',\n        upage: 'Construtor de Sites com IA',\n        proAlert: 'Atualize para Pro para usar este recurso',\n        app: {\n            app: 'APP',\n            title: 'Apelido do Painel',\n            titleHelper: 'O alias do painel é usado para exibição no APP (alias do painel padrão)',\n            qrCode: 'QR Code',\n            apiStatusHelper: 'O APP do Painel precisa habilitar a funcionalidade da interface API',\n            apiInterfaceHelper:\n                'Suporta acesso à interface de API do painel (essa funcionalidade precisa ser ativada no aplicativo do painel)',\n            apiInterfaceHelper1:\n                'O acesso ao aplicativo do painel requer que o visitante seja adicionado à lista de permissões; para IPs não fixos, recomenda-se adicionar 0.0.0.0/0(todos os IPv4), ::/0 (todos os IPv6)',\n            qrCodeExpired: 'Tempo de atualização',\n            apiLeakageHelper: 'Não revele o QR code. Garanta que ele seja usado apenas em ambientes confiáveis.',\n        },\n        waf: {\n            name: 'WAF',\n            blackWhite: 'Lista negra e branca',\n            globalSetting: 'Configurações globais',\n            websiteSetting: 'Configurações do site',\n            blockRecords: 'Registros de bloqueio',\n            world: 'Mundo',\n            china: 'China',\n            intercept: 'Interceptar',\n            request: 'Requisição',\n            count4xx: 'Quantidade de 4xx',\n            count5xx: 'Quantidade de 5xx',\n            todayStatus: 'Status de hoje',\n            reqMap: 'Mapa de interceptação (30 dias)',\n            resource: 'Origem',\n            count: 'Quantidade',\n            hight: 'Alto',\n            low: 'Baixo',\n            reqCount: 'Requisições',\n            interceptCount: 'Número de interceptações',\n            requestTrends: 'Tendências de requisições (últimos 7 dias)',\n            interceptTrends: 'Tendências de interceptação (últimos 7 dias)',\n            whiteList: 'Lista branca',\n            blackList: 'Lista negra',\n            ipBlackListHelper: 'Endereços IP na lista negra estão bloqueados de acessar o site',\n            ipWhiteListHelper: 'Endereços IP na lista branca contornam todas as restrições',\n            uaBlackListHelper: 'Requisições com valores de User-Agent na lista negra serão bloqueadas',\n            uaWhiteListHelper: 'Requisições com valores de User-Agent na lista branca contornam todas as restrições',\n            urlBlackListHelper: 'Requisições para URLs na lista negra serão bloqueadas',\n            urlWhiteListHelper: 'Requisições para URLs na lista branca contornam todas as restrições',\n            ccHelper:\n                'Se um site receber mais de {1} requisições do mesmo IP dentro de {0} segundos, o IP será bloqueado por {2}',\n            blockTime: 'Duração do bloqueio',\n            attackHelper:\n                'Se as interceptações acumuladas excederem {1} dentro de {0} segundos, o IP será bloqueado por {2}',\n            notFoundHelper:\n                'Se requisições acumuladas retornarem erros 404 mais de {1} vezes dentro de {0} segundos, o IP será bloqueado por {2}',\n            frequencyLimit: 'Limite de frequência',\n            regionLimit: 'Limite de região',\n            defaultRule: 'Regras padrão',\n            accessFrequencyLimit: 'Limite de frequência de acesso',\n            attackLimit: 'Limite de frequência de ataque',\n            notFoundLimit: 'Limite de frequência de 404',\n            urlLimit: 'Limite de frequência de URL',\n            urlLimitHelper: 'Defina a frequência de acesso para uma única URL',\n            sqliDefense: 'Proteção contra injeção SQL',\n            sqliHelper: 'Detectar injeção SQL em requisições e bloqueá-las',\n            xssHelper: 'Detectar XSS em requisições e bloqueá-las',\n            xssDefense: 'Proteção contra XSS',\n            uaDefense: 'Regras de User-Agent maliciosos',\n            uaHelper: 'Inclui regras para identificar bots maliciosos comuns',\n            argsDefense: 'Regras de parâmetros maliciosos',\n            argsHelper: 'Bloqueia requisições que contenham parâmetros maliciosos',\n            cookieDefense: 'Regras de cookies maliciosos',\n            cookieHelper: 'Proíbe cookies maliciosos de serem carregados nas requisições',\n            headerDefense: 'Regras de cabeçalhos maliciosos',\n            headerHelper: 'Proíbe requisições que contenham cabeçalhos maliciosos',\n            httpRule: 'Regras de método de requisição HTTP',\n            httpHelper:\n                'Defina os tipos de métodos permitidos para acessar. Se você deseja restringir certos tipos de acesso, desative esse tipo de botão. Por exemplo: se apenas o acesso do tipo GET for permitido, desative todos os outros botões, exceto o GET',\n            geoRule: 'Restrições de acesso regional',\n            geoHelper:\n                'Restringe o acesso ao seu site a partir de certas regiões, por exemplo: se o acesso for permitido a partir da China continental, então requisições de fora da China continental serão bloqueadas',\n            ipLocation: 'Localização do IP',\n            action: 'Ação',\n            ruleType: 'Tipo de ataque',\n            ipHelper: 'Digite o endereço IP',\n            attackLog: 'Log de ataques',\n            rule: 'Regra',\n            ipArr: 'Intervalo IPV4',\n            ipStart: 'IP de início',\n            ipEnd: 'IP de fim',\n            ipv4: 'IPv4',\n            ipv6: 'IPv6',\n            urlDefense: 'Regras de URL',\n            urlHelper: 'URL proibida',\n            dirFilter: 'Filtro de diretório',\n            sqlInject: 'Injeção de SQL',\n            xss: 'XSS',\n            phpExec: 'Execução de script PHP',\n            oneWordTrojan: 'Cavalo de Troia de uma palavra',\n            appFilter: 'Filtragem de diretórios perigosos',\n            webShell: 'Webshell',\n            args: 'Parâmetros maliciosos',\n            protocolFilter: 'Filtro de protocolo',\n            javaFilter: 'Filtragem de arquivos Java perigosos',\n            scannerFilter: 'Filtro de scanner',\n            escapeFilter: 'Filtro de escape',\n            customRule: 'Regras personalizadas',\n            httpMethod: 'Filtro de método HTTP',\n            fileExt: 'Limite de extensão de arquivo',\n            fileExtHelper: 'Extensões de arquivos proibidas para upload',\n            deny: 'Proibido',\n            allow: 'Permitir',\n            field: 'Objeto',\n            pattern: 'Condição',\n            ruleContent: 'Conteúdo',\n            contain: 'incluir',\n            equal: 'igual',\n            regex: 'expressão regular',\n            notEqual: 'Diferente de',\n            customRuleHelper: 'Realize ações com base nas condições especificadas',\n            actionAllow: 'Permitir',\n            blockIP: 'Bloquear IP',\n            code: 'Código de status retornado',\n            noRes: 'Desconectar (444)',\n            badReq: 'Parâmetros inválidos (400)',\n            forbidden: 'Acesso proibido (403)',\n            serverErr: 'Erro no servidor (500)',\n            resHtml: 'Página de resposta',\n            allowHelper: 'Permitir acesso pulará as regras subsequentes do WAF, use com cautela',\n            captcha: 'verificação humano-máquina',\n            fiveSeconds: 'Verificação de 5 segundos',\n            location: 'Região',\n            redisConfig: 'Configuração do Redis',\n            redisHelper: 'Ativar o Redis para persistir IPs temporariamente bloqueados',\n            wafHelper: 'Todos os sites perderão proteção após o fechamento',\n            attackIP: 'IP atacante',\n            attackParam: 'Detalhes do ataque',\n            execRule: 'Regra atingida',\n            acl: 'ACL',\n            sql: 'Injeção de SQL',\n            cc: 'Limite de frequência de acesso',\n            isBlocking: 'Bloqueado',\n            isFree: 'Desbloqueado',\n            unLock: 'Desbloquear',\n            unLockHelper: 'Você quer desbloquear o IP: {0}?',\n            saveDefault: 'Salvar padrão',\n            saveToWebsite: 'Aplicar ao site',\n            saveToWebsiteHelper: 'Aplicar as configurações atuais a todos os sites?',\n            websiteHelper:\n                'Aqui estão as configurações padrão para criar um site. As modificações precisam ser aplicadas ao site para terem efeito',\n            websiteHelper2:\n                'Aqui estão as configurações padrão para criar um site. Por favor, modifique a configuração específica no site',\n            ipGroup: 'Grupo de IP',\n            ipGroupHelper:\n                'Um IP ou segmento de IP por linha, suporta IPv4 e IPv6, por exemplo: 192.168.1.1 ou 192.168.1.0/24',\n            ipBlack: 'Lista negra de IP',\n            openRestyAlert: 'A versão do OpenResty precisa ser superior a {0}',\n            initAlert:\n                'A inicialização é necessária para o primeiro uso, o arquivo de configuração do site será modificado e a configuração original do WAF será perdida. Por favor, faça backup do OpenResty com antecedência',\n            initHelper:\n                'A operação de inicialização apagará a configuração WAF existente. Tem certeza de que deseja inicializar?',\n            mainSwitch: 'Interruptor principal',\n            websiteAlert: 'Por favor, crie um site primeiro',\n            defaultUrlBlack: 'Regras de URL',\n            htmlRes: 'Página de interceptação',\n            urlSearchHelper: 'Por favor, insira a URL para suportar a pesquisa difusa',\n            toCreate: 'Criar',\n            closeWaf: 'Fechar WAF',\n            closeWafHelper: 'Fechar o WAF fará com que o site perca a proteção, você deseja continuar?',\n            addblack: 'Negro',\n            addwhite: 'Adicionar branco',\n            addblackHelper: 'Adicionar IP:{0} à lista negra padrão?',\n            addwhiteHelper: 'Adicionar IP:{0} à lista branca padrão?',\n            defaultUaBlack: 'Regra do User-Agent',\n            defaultIpBlack: 'Grupo de IPs Maliciosos',\n            cookie: 'Regras de Cookies',\n            urlBlack: 'Lista negra de URLs',\n            uaBlack: 'Lista negra de User-Agent',\n            attackCount: 'Limite de frequência de ataques',\n            fileExtCheck: 'Limite de upload de arquivos',\n            geoRestrict: 'Restrição de acesso regional',\n            attacklog: 'Registro de Interceptação',\n            unknownWebsite: 'Acesso a nome de domínio não autorizado',\n            geoRuleEmpty: 'A região não pode estar vazia',\n            unknown: 'Site não existe',\n            geo: 'Restrição Regional',\n            revertHtml: 'Você deseja restaurar {0} como a página padrão?',\n            five_seconds: 'Verificação de 5 segundos',\n            header: 'Regras de cabeçalho',\n            methodWhite: 'Regras HTTP',\n            expiryDate: 'Data de expiração',\n            expiryDateHelper: 'Após passar na verificação, não será mais verificado dentro do período de validade',\n            defaultIpBlackHelper: 'Alguns IPs maliciosos coletados da Internet para impedir o acesso',\n            notFoundCount: 'Limite de Frequência 404',\n            matchValue: 'Valor de correspondência',\n            headerName: 'Suporta inglês, números, -, comprimento de 3-30',\n            cdnHelper: 'Sites que usam CDN podem abrir aqui para obter o IP de origem correto',\n            clearLogWarn: 'Limpar o log não será possível, você deseja continuar?',\n            commonRuleHelper: 'A regra é correspondência difusa',\n            blockIPHelper:\n                'IPs bloqueados são armazenados temporariamente no OpenResty e serão desbloqueados quando você reiniciar o OpenResty. Eles podem ser bloqueados permanentemente através da função de bloqueio',\n            addWhiteUrlHelper: 'Adicionar URL {0} à lista branca?',\n            dashHelper:\n                'A versão comunitária também pode usar as funções nas configurações globais e configurações de site',\n            wafStatusHelper: 'O WAF não está ativado, por favor, ative-o nas configurações globais',\n            ccMode: 'Modo',\n            global: 'Modo Global',\n            uriMode: 'Modo URL',\n            globalHelper:\n                'Modo Global: Ativado quando o número total de solicitações para qualquer URL dentro de uma unidade de tempo excede o limite',\n            uriModeHelper:\n                'Modo URL: Ativado quando o número de solicitações para uma única URL dentro de uma unidade de tempo excede o limite',\n            ip: 'Lista negra de IPs',\n            globalSettingHelper:\n                'Configurações com a tag [Website] precisam ser ativadas em [Configurações do Website], e as configurações globais são apenas as configurações padrão para sites recém-criados',\n            globalSettingHelper2:\n                'As configurações precisam ser ativadas tanto em [Configurações Globais] quanto em [Configurações do Website] ao mesmo tempo',\n            urlCCHelper: 'Mais de {1} solicitações para este URL dentro de {0} segundos, bloqueando este IP {2}',\n            urlCCHelper2: 'O URL não pode conter parâmetros',\n            notContain: 'Não contém',\n            urlcc: 'Limitação de frequência de URL',\n            method: 'Tipo de solicitação',\n            addIpsToBlock: 'Bloquear IPs em massa',\n            addUrlsToWhite: 'Adicionar URLs à lista branca em massa',\n            noBlackIp: 'O IP já está bloqueado, não é necessário bloquear novamente',\n            noWhiteUrl: 'O URL já está na lista branca, não é necessário adicionar novamente',\n            spiderIpHelper:\n                'Inclui Baidu, Bing, Google, 360, Shenma, Sogou, ByteDance, DuckDuckGo, Yandex. Fechar isso bloqueará todos os acessos de spiders.',\n            spiderIp: 'Pool de IPs de spiders',\n            geoIp: 'Biblioteca de endereços IP',\n            geoIpHelper: 'Usado para determinar a localização geográfica do IP',\n            stat: 'Relatório de ataques',\n            statTitle: 'Relatório',\n            attackIp: 'IP de ataque',\n            attackCountNum: 'Número de ataques',\n            percent: 'Porcentagem',\n            addblackUrlHelper: 'Adicionar URL: {0} à lista negra padrão?',\n            rce: 'Execução remota de código',\n            software: 'Software',\n            cveHelper: 'Contém vulnerabilidades comuns de software e frameworks',\n            vulnCheck: 'Regras complementares',\n            ssrf: 'Vulnerabilidade SSRF',\n            afr: 'Leitura arbitrária de arquivos',\n            ua: 'Acesso não autorizado',\n            id: 'Dicas de informações',\n            aa: 'Desvio de autenticação',\n            dr: 'Trasversal de diretórios',\n            xxe: 'Vulnerabilidade XXE',\n            suid: 'Vulnerabilidade de serialização',\n            dos: 'Vulnerabilidade de negação de serviço',\n            afd: 'Download arbitrário de arquivos',\n            sqlInjection: 'Injeção de SQL',\n            afw: 'Escrita arbitrária de arquivos',\n            il: 'Dicas de informações',\n            clearAllLog: 'Limpar todos os logs',\n            exportLog: 'Exportar logs',\n            appRule: 'Regras de aplicação',\n            appRuleHelper:\n                'Regras comuns de aplicações, habilitar pode reduzir falsos positivos, um site pode usar apenas uma regra',\n            logExternal: 'Excluir tipos de registro',\n            ipWhite: 'Lista branca de IPs',\n            urlWhite: 'Lista branca de URLs',\n            uaWhite: 'Lista branca de User-Agent',\n            logExternalHelper:\n                'Tipos de registro excluídos não serão registrados nos logs, listas negras/brancas, restrições de acesso de região e regras personalizadas geram muitos logs, recomenda-se excluir',\n            ssti: 'Ataque SSTI',\n            crlf: 'Injeção CRLF',\n            strict: 'Modo estrito',\n            strictHelper: 'Usa regras mais rigorosas para validar solicitações',\n            saveLog: 'Salvar log',\n            remoteURLHelper: 'O URL remoto precisa garantir um IP por linha e nenhum outro caractere',\n            notFound: 'Not Found (404)',\n            serviceUnavailable: 'Serviço Indisponível (503)',\n            gatewayTimeout: 'Tempo Limite da Porta de Entrada (504)',\n            belongToIpGroup: 'Pertence ao Grupo de IP',\n            notBelongToIpGroup: 'Não pertence ao Grupo de IP',\n            unknownWebsiteKey: 'Domínio Desconhecido',\n            special: 'Domínio Especial',\n            fileToLarge: 'O arquivo excede 1MB e não pode ser enviado',\n            uploadOverLimit: 'O arquivo enviado excede o limite de quantidade, máximo 1 arquivo',\n            importRuleHelper: 'Uma regra por linha',\n        },\n        monitor: {\n            '360': 'Busca 360',\n            name: 'Monitoramento de Websites',\n            pv: 'Visualizações de Página',\n            uv: 'Visitantes Únicos',\n            flow: 'Fluxo de Tráfego',\n            ip: 'IP',\n            spider: 'Spider',\n            visitors: 'Tendências de Visitantes',\n            today: 'Hoje',\n            last7days: 'Últimos 7 Dias',\n            last30days: 'Últimos 30 Dias',\n            uvMap: 'Mapa de Visitantes (30 dias)',\n            qps: 'Requisições em Tempo Real (por minuto)',\n            flowSec: 'Tráfego em Tempo Real (por minuto)',\n            excludeCode: 'Excluir Códigos de Status',\n            excludeUrl: 'Excluir URLs',\n            excludeExt: 'Excluir Extensões',\n            cdnHelper: 'Obter o IP real do Header fornecido pela CDN',\n            reqRank: 'Ranking de Visitas',\n            refererDomain: 'Domínio de Referência',\n            os: 'Sistema',\n            browser: 'Navegador/Cliente',\n            device: 'Dispositivo',\n            showMore: 'Mais',\n            unknown: 'Outro',\n            pc: 'Computador',\n            mobile: 'Dispositivo Móvel',\n            wechat: 'WeChat',\n            machine: 'Máquina',\n            tencent: 'Navegador Tencent',\n            ucweb: 'Navegador UC',\n            '2345explorer': 'Navegador 2345',\n            huaweibrowser: 'Navegador Huawei',\n            log: 'Logs de Requisição',\n            statusCode: 'Código de Status',\n            requestTime: 'Tempo de Resposta',\n            flowRes: 'Tráfego de Resposta',\n            method: 'Método de Requisição',\n            statusCodeHelper: 'Insira o código de status acima',\n            statusCodeError: 'Tipo de código de status inválido',\n            methodHelper: 'Insira o método de requisição acima',\n            all: 'Todos',\n            baidu: 'Baidu',\n            google: 'Google',\n            bing: 'Bing',\n            bytes: 'Manchetes do Dia',\n            sogou: 'Sogou',\n            failed: 'Erro',\n            ipCount: 'Contagem de IPs',\n            spiderCount: 'Requisições de Spider',\n            averageReqTime: 'Tempo Médio de Resposta',\n            totalFlow: 'Fluxo Total',\n            logSize: 'Tamanho do Arquivo de Log',\n            realIPType: 'Método de Obtenção do IP Real',\n            fromHeader: 'Obter do Header HTTP',\n            fromHeaders: 'Obter da lista de Headers',\n            header: 'Header HTTP',\n            cdnConfig: 'Configuração da CDN',\n            xff1: 'Proxy de Primeiro Nível de X-Forwarded-For',\n            xff2: 'Proxy de Segundo Nível de X-Forwarded-For',\n            xff3: 'Proxy de Terceiro Nível de X-Forwarded-For',\n            xffHelper:\n                'Por exemplo: X-Forwarded-For: <cliente>,<proxy1>,<proxy2>,<proxy3>. O proxy superior pegará o último IP <proxy3>',\n            headersHelper:\n                'Obter o IP real dos Headers HTTP comuns fornecidos pela CDN, selecionando o primeiro valor disponível',\n            monitorCDNHelper:\n                'Modificar a configuração da CDN para monitoramento do site também atualizará as configurações de CDN do WAF',\n            wafCDNHelper:\n                'Modificar a configuração de CDN do WAF também atualizará as configurações de CDN do monitoramento do site',\n            statusErr: 'Formato de código de status inválido',\n            shenma: 'Busca Shenma',\n            duckduckgo: 'DuckDuckGo',\n            excludeUri: 'Excluir URIs',\n            top100Helper: 'Mostrar os 100 principais dados',\n            logSaveDay: 'Período de Retenção de Logs (dias)',\n            cros: 'Chrome OS',\n            theworld: 'Navegador TheWorld',\n            edge: 'Microsoft Edge',\n            maxthon: 'Navegador Maxthon',\n            monitorStatusHelper: 'O monitoramento não está habilitado, por favor habilite-o nas configurações',\n            excludeIp: 'Excluir Endereços IP',\n            excludeUa: 'Excluir User-Agent',\n            remotePort: 'Porta Remota',\n            unknown_browser: 'Desconhecido',\n            unknown_os: 'Desconhecido',\n            unknown_device: 'Desconhecido',\n            logSaveSize: 'Tamanho Máximo de Salvamento de Log',\n            logSaveSizeHelper: 'Este é o tamanho de salvamento de log para um único site',\n            '360se': 'Navegador de Segurança 360',\n            websites: 'Lista de Sites',\n            trend: 'Estatísticas de Tendência',\n            reqCount: 'Contagem de Solicitações',\n            uriHelper: 'Você pode usar /test/* ou /*/index.php para excluir Uri',\n        },\n        tamper: {\n            tamper: 'Proteção contra adulteração do site',\n            ignoreTemplate: 'Modelo de Exclusão',\n            protectTemplate: 'Modelo de Proteção',\n            ignoreTemplateHelper:\n                'Digite o conteúdo de exclusão, separado por Enter ou espaço. (Diretório específico ./log ou nome do diretório tmp, para excluir arquivos precisa inserir arquivo específico ./data/test.html)',\n            protectTemplateHelper:\n                'Digite o conteúdo de proteção, separado por Enter ou espaço. (Arquivo específico ./index.html, extensão de arquivo .html, tipo de arquivo js, para proteger diretórios precisa inserir diretório específico ./log)',\n            templateContent: 'Conteúdo do Modelo',\n            template: 'Modelo',\n            saveTemplate: 'Salvar como Modelo',\n            tamperHelper1:\n                'Para sites de implantação com um clique, é recomendável ativar a proteção contra violação de diretório de aplicativos; se o site não puder ser usado normalmente ou o backup/restauração falhar, desative primeiro a proteção contra violação;',\n            tamperHelper2:\n                'Restringirá operações de leitura/gravação, exclusão, permissão e modificação de proprietário para arquivos protegidos em diretórios não excluídos',\n            tamperPath: 'Diretório de Proteção',\n            tamperPathEdit: 'Modificar Caminho',\n            log: 'Log de Bloqueio',\n            totalProtect: 'Proteção Total',\n            todayProtect: 'Proteção de Hoje',\n            templateRule: 'Comprimento 1-512, o nome não pode conter {0} e outros símbolos',\n            ignore: 'Excluir',\n            ignoreHelper:\n                'Selecione ou digite o conteúdo de exclusão, separado por Enter ou espaço. (Diretório específico ./log ou nome do diretório tmp, para excluir arquivos precisa inserir ou selecionar arquivo específico ./data/test.html)',\n            protect: 'Proteger',\n            protectHelper:\n                'Selecione ou digite o conteúdo de proteção, separado por Enter ou espaço. (Arquivo específico ./index.html, extensão de arquivo .html, tipo de arquivo js, para proteger diretórios precisa inserir ou selecionar diretório específico ./log)',\n            tamperHelper00: 'Exclusão e proteção suportam apenas caminhos relativos;',\n            tamperHelper01:\n                'Após ativar a proteção contra violação, o sistema restringirá as operações de criação, edição e exclusão de arquivos protegidos em diretórios não excluídos;',\n            tamperHelper02:\n                'Prioridade: Proteção de caminho específico > Exclusão de caminho específico > Proteção > Exclusão',\n            tamperHelper03:\n                'As operações de monitoramento visam apenas diretórios não excluídos, monitorando a criação de arquivos não protegidos nesses diretórios.',\n            disableHelper: 'Prestes a desativar a proteção contra violação para os seguintes sites, continuar?',\n            appendOnly: 'Somente Acréscimo',\n            appendOnlyHelper:\n                'Restringe operações de exclusão de arquivos neste diretório, permitindo apenas adição de diretórios excluídos ou arquivos não protegidos',\n            immutable: 'Imutável',\n            immutableHelper:\n                'Restringe operações de edição, exclusão, permissão e modificação de proprietário para este arquivo',\n            onWatch: 'Monitorar',\n            onWatchHelper:\n                'Monitora e intercepta a criação de arquivos protegidos ou diretórios não excluídos neste diretório',\n            forceStop: 'Forçar Fechamento',\n            forceStopHelper: 'Desativará forçadamente a função anti-violência para este diretório do site. Continuar?',\n        },\n        setting: {\n            setting: 'Configurações do Painel',\n            title: 'Descrição do Painel',\n            titleHelper:\n                'Será exibido na página de login do usuário (ex.: Painel de gerenciamento de manutenção de servidores Linux, recomendado 8-15 caracteres)',\n            logo: 'Logo (Sem Texto)',\n            logoHelper:\n                'Será exibido no canto superior esquerdo da página de gerenciamento quando o menu estiver recolhido (tamanho recomendado da imagem: 82px*82px)',\n            logoWithText: 'Logo (Com Texto)',\n            logoWithTextHelper:\n                'Será exibido no canto superior esquerdo da página de gerenciamento quando o menu estiver expandido (tamanho recomendado da imagem: 185px*55px)',\n            favicon: 'Ícone do Site',\n            faviconHelper: 'Ícone do site (tamanho recomendado da imagem: 16px*16px)',\n            setDefault: 'Restaurar Padrão',\n            setHelper: 'As configurações atuais serão salvas. Deseja continuar?',\n            setDefaultHelper: 'Todas as configurações do painel serão restauradas para o padrão. Deseja continuar?',\n            logoGroup: 'Logo',\n            imageGroup: 'Imagem',\n            loginImage: 'Imagem da Página de Login',\n            loginImageHelper: 'Será exibida na página de login (Tamanho recomendado: 500x416px)',\n            loginBgType: 'Tipo de Fundo da Página de Login',\n            loginBgImage: 'Imagem de Fundo da Página de Login',\n            loginBgImageHelper:\n                'Será exibida como imagem de fundo na página de login (Tamanho recomendado: 1920x1080px)',\n            loginBgColor: 'Cor de Fundo da Página de Login',\n            loginBgColorHelper: 'Será exibida como cor de fundo na página de login',\n            image: 'Imagem',\n            bgColor: 'Cor de Fundo',\n            loginGroup: 'Página de login',\n            loginBtnLinkColor: 'Cor do botão/link',\n            loginBtnLinkColorHelper: 'Será exibido como a cor do botão/link na página de login',\n        },\n        helper: {\n            wafTitle1: 'Mapa de Interceptação',\n            wafContent1: 'Exibe a distribuição geográfica das interceptações nos últimos 30 dias',\n            wafTitle2: 'Restrições de Acesso Regional',\n            wafContent2: 'Restringe o acesso ao site com base em localizações geográficas',\n            wafTitle3: 'Página de Interceptação Personalizada',\n            wafContent3: 'Crie uma página personalizada para exibir após uma solicitação ser interceptada',\n            wafTitle4: 'Regras Personalizadas (ACL)',\n            wafContent4: 'Intercepta solicitações com base em regras personalizadas',\n            tamperTitle1: 'Monitoramento de Integridade de Arquivos',\n            tamperContent1:\n                'Monitora a integridade dos arquivos do site, incluindo arquivos principais, scripts e arquivos de configuração.',\n            tamperTitle2: 'Varredura e Detecção em Tempo Real',\n            tamperContent2:\n                'Detecta arquivos anormais ou alterados por meio de varredura em tempo real do sistema de arquivos do site.',\n            tamperTitle3: 'Configurações de Permissão de Segurança',\n            tamperContent3:\n                'Restringe o acesso aos arquivos do site através de configurações adequadas de permissões e políticas de controle de acesso, reduzindo a superfície de ataque.',\n            tamperTitle4: 'Registro e Análise',\n            tamperContent4:\n                'Registra logs de acesso e operações em arquivos para auditoria e análise posteriores, ajudando administradores a identificar ameaças de segurança potenciais.',\n            settingTitle1: 'Mensagem de Boas-vindas Personalizada',\n            settingContent1: 'Defina uma mensagem de boas-vindas personalizada na página de login do 1Panel.',\n            settingTitle2: 'Logo Personalizado',\n            settingContent2: 'Permite o upload de imagens de logotipo contendo nomes de marcas ou outros textos.',\n            settingTitle3: 'Ícone Personalizado do Site',\n            settingContent3:\n                'Permite o upload de ícones personalizados para substituir o ícone padrão do navegador, melhorando a experiência do usuário.',\n            monitorTitle1: 'Tendência de Visitantes',\n            monitorContent1: 'Estatísticas e exibição das tendências de visitantes do site',\n            monitorTitle2: 'Mapa de Visitantes',\n            monitorContent2: 'Estatísticas e exibição da distribuição geográfica dos visitantes do site',\n            monitorTitle3: 'Estatísticas de Acesso',\n            monitorContent3:\n                'Estatísticas sobre informações de solicitação do site, incluindo robôs de busca, dispositivos de acesso, status das solicitações, etc.',\n            monitorTitle4: 'Monitoramento em Tempo Real',\n            monitorContent4:\n                'Monitoramento em tempo real de informações de solicitações do site, incluindo número de solicitações, tráfego, etc.',\n            alertTitle1: 'Alertas por SMS',\n            alertContent1:\n                'Quando houver uso anormal de recursos do servidor, expiração de sites e certificados, nova versão disponível, expiração de senha, entre outros, os usuários serão notificados via SMS para garantir um processamento oportuno.',\n            alertTitle2: 'Registro de Alertas',\n            alertContent2:\n                'Oferece aos usuários a função de visualizar logs de alertas, facilitando o rastreamento e a análise de eventos históricos de alerta.',\n            alertTitle3: 'Configurações de Alertas',\n            alertContent3:\n                'Permite que os usuários configurem números de telefone personalizados, frequência de envio diário e horários de envio, facilitando a criação de alertas mais adequados.',\n            nodeDashTitle1: 'Gerenciamento de Aplicativos',\n            nodeDashContent1:\n                'Gerenciamento unificado de aplicativos multi-nó, suporta monitoramento de status, início/parada rápida, conexão de terminal e backup',\n            nodeDashTitle2: 'Gerenciamento de Sites',\n            nodeDashContent2:\n                'Gerenciamento unificado de sites multi-nó, monitoramento de status em tempo real, suporta início/parada em lote e backup rápido',\n            nodeDashTitle3: 'Gerenciamento de Bancos de Dados',\n            nodeDashContent3:\n                'Gerenciamento unificado de bancos de dados multi-nó, status chave de relance, suporta backup com um clique',\n            nodeDashTitle4: 'Gerenciamento de Tarefas Agendadas',\n            nodeDashContent4:\n                'Gerenciamento unificado de tarefas agendadas multi-nó, suporta monitoramento de status, início/parada rápida e execução manual por acionamento',\n            nodeTitle1: 'Adição de Nó com Um Clique',\n            nodeContent1: 'Integre rapidamente vários nós de servidor',\n            nodeTitle2: 'Atualização em Lote',\n            nodeContent2: 'Sincronize e atualize todos os nós com uma única operação',\n            nodeTitle3: 'Monitoramento de Status do Nó',\n            nodeContent3: 'Acompanhe em tempo real o status operacional de cada nó',\n            nodeTitle4: 'Conexão Remota Rápida',\n            nodeContent4: 'Conecte-se diretamente a terminais remotos de nós com um clique',\n            fileExchangeTitle1: 'Transmissão de Autenticação por Chave',\n            fileExchangeContent1: 'Autentique via chaves SSH para garantir a segurança da transmissão.',\n            fileExchangeTitle2: 'Sincronização de Arquivos Eficiente',\n            fileExchangeContent2:\n                'Sincronize apenas o conteúdo alterado para melhorar significativamente a velocidade e estabilidade da transmissão.',\n            fileExchangeTitle3: 'Suporte a Troca Entre Múltiplos Nós',\n            fileExchangeContent3:\n                'Transfira facilmente arquivos de projeto entre diferentes nós, gerencie vários servidores de forma flexível.',\n            nodeAppTitle1: 'Gerenciamento de Atualização de Aplicativos',\n            nodeAppContent1:\n                'Monitoramento unificado de atualizações de aplicativos multi-nó, suporta atualização com um clique',\n            appTitle1: 'Gerenciamento Flexível do Painel',\n            appContent1: 'Gerencie facilmente seu servidor 1Panel a qualquer hora e em qualquer lugar.',\n            appTitle2: 'Informações Completas do Serviço',\n            appContent2:\n                'Gerencie aplicativos, sites, Docker, bancos de dados, etc. pelo app móvel e crie rapidamente aplicativos e sites.',\n            appTitle3: 'Monitoramento Anômalo em Tempo Real',\n            appContent3:\n                'Veja o status do servidor, monitoramento de segurança WAF, estatísticas de visitas ao site e saúde dos processos em tempo real no aplicativo móvel.',\n            clusterTitle1: 'Implantação Mestre-Escravo',\n            clusterContent1:\n                'Suporta a criação de instâncias mestre-escravo MySQL/Postgres/Redis em diferentes nós, completando automaticamente a associação e inicialização mestre-escravo',\n            clusterTitle2: 'Gestão Mestre-Escravo',\n            clusterContent2:\n                'Página unificada para gerir centralmente vários nós mestre-escravo, visualizar seus papéis, status de execução, etc.',\n            clusterTitle3: 'Estado de Replicação',\n            clusterContent3:\n                'Exibe o estado de replicação mestre-escravo e informações de atraso, auxiliando na resolução de problemas de sincronização',\n        },\n        node: {\n            master: 'Nó Principal',\n            masterBackup: 'Backup do Nó Mestre',\n            backupNode: 'Nó de Backup',\n            backupFrequency: 'Frequência de Backup (horas)',\n            backupCopies: 'Cópias de Backup',\n            noBackupNode:\n                'O nó de backup está vazio atualmente. Selecione um nó de backup para salvar e tente novamente!',\n            masterBackupAlert:\n                'O backup do nó mestre não está configurado atualmente. Para garantir a segurança dos dados, configure um nó de backup o mais rápido possível para facilitar a troca manual para um novo nó mestre em caso de falha.',\n            node: 'Nó',\n            addr: 'Endereço',\n            nodeUpgrade: 'Configurações de Atualização',\n            nodeUpgradeHelper:\n                'Os nós selecionados começarão a atualizar automaticamente após a conclusão da atualização do nó mestre, nenhuma operação manual necessária.',\n            nodeUnhealthy: 'Estado do nó anormal',\n            deletedNode: 'O nó excluído {0} não suporta atualmente operações de atualização!',\n            nodeUnhealthyHelper:\n                'Estado anormal do nó detectado. Por favor verifique em [Gestão de Nós] e tente novamente!',\n            nodeUnbind: 'Nó não vinculado à licença',\n            nodeUnbindHelper:\n                'Detectamos que este nó não está vinculado a uma licença. Por favor vincule no menu [Configurações do Painel - Licença] e tente novamente!',\n            memTotal: 'Memória Total',\n            nodeManagement: 'Gerenciamento Multi-Máquina',\n            nodeItem: 'Gerenciamento de Nós',\n            panelItem: 'Gerenciamento de Painéis',\n            addPanel: 'Adicionar Painel',\n            addPanelHelper:\n                'Após adicionar o painel com sucesso, você pode acessar rapidamente o painel de destino em [Visão Geral - Painéis].',\n            panel: 'Painel 1Panel',\n            others: 'Outros Painéis',\n            addNode: 'Adicionar Nó',\n            connInfo: 'Informações de Conexão',\n            nodeInfo: 'Informações do Nó',\n            withProxy: 'Включить Доступ через Прокси',\n            withoutProxy: 'Отключить Доступ через Прокси',\n            withProxyHelper:\n                'Будет использовать системный прокси {0}, поддерживаемый в настройках панели, для доступа к дочерним узлам. Продолжить?',\n            withoutProxyHelper:\n                'Прекратит использование системного прокси, поддерживаемого в настройках панели, для доступа к дочерним узлам. Продолжить?',\n            syncInfo: 'Sincronização de dados,',\n            syncHelper: 'Quando os dados do nó mestre mudam, são sincronizados em tempo real para este nó filho,',\n            syncBackupAccount: 'Configurações de conta de backup',\n            syncWithMaster:\n                'Após atualizar para Pro, todos os dados serão sincronizados por padrão. As políticas de sincronização podem ser ajustadas manualmente no gerenciamento de nós.',\n            syncProxy: 'Configurações de proxy do sistema',\n            syncProxyHelper: 'Sincronizar configurações de proxy do sistema requer reinicialização do Docker',\n            syncProxyHelper1: 'Reiniciar o Docker pode afetar os serviços de contêiner em execução.',\n            syncProxyHelper2: 'Você pode reiniciar manualmente na página Contêineres - Configuração.',\n            syncProxyHelper3:\n                'Sincronizar configurações de proxy do sistema requer reinicialização do Docker, o que pode afetar os serviços de contêiner em execução',\n            syncProxyHelper4:\n                'Sincronizar configurações de proxy do sistema requer reinicialização do Docker. Você pode reiniciar manualmente mais tarde na página Contêineres - Configuração.',\n            syncCustomApp: 'Sincronizar Repositório de Aplicativos Personalizados',\n            syncAlertSetting: 'Configurações de alerta do sistema',\n            syncNodeInfo: 'Dados básicos do nó,',\n            nodeSyncHelper: 'A sincronização das informações do nó irá sincronizar as seguintes informações:',\n            nodeSyncHelper1: '1. Informações da conta de backup pública',\n            nodeSyncHelper2: '2. Informações de conexão entre o nó principal e os sub-nós',\n            nodeCheck: 'Verificação de disponibilidade',\n            checkSSH: 'Verificar conexão SSH do nó',\n            checkUserPermission: 'Verificar permissões de usuário do nó',\n            isNotRoot: 'Detectado que sudo sem senha não é suportado neste nó e o usuário atual não é root',\n            checkLicense: 'Verificar status da licença do nó',\n            checkService: 'Verificar informações de serviço existentes no nó',\n            checkPort: 'Verificar acessibilidade da porta do nó',\n            panelExist:\n                'Detectado que este nó está executando o serviço 1Panel V1. Atualize para V2 usando o script de migração antes de adicionar.',\n            coreExist:\n                'O nó atual já está habilitado como nó mestre e não pode ser adicionado diretamente como nó escravo. Por favor, faça o downgrade para nó escravo primeiro antes de adicionar, consulte a documentação para detalhes.',\n            agentExist:\n                'Detectado que 1panel-agent já está instalado neste nó. Continuar irá reter os dados existentes e apenas substituir o serviço 1panel-agent.',\n            agentNotExist:\n                'Foi detectado que o 1panel-agent não está instalado neste nó, portanto, as informações do nó não podem ser editadas diretamente. Por favor, exclua e adicione novamente.',\n            oldDataExist:\n                'Detectados dados históricos do 1Panel V2 neste nó. As seguintes informações serão usadas para sobrescrever as configurações atuais:',\n            errLicense: 'A licença vinculada a este nó está indisponível. Por favor verifique e tente novamente!',\n            errNodePort:\n                'A porta do nó [ {0} ] foi detectada como inacessível. Verifique se o firewall ou grupo de segurança liberou esta porta.',\n            reinstallHelper: 'Reinstalar o nó {0}, deseja continuar?',\n            unhealthyCheck: 'Verificação Anormal',\n            fixOperation: 'Operação de Correção',\n            checkName: 'Item de Verificação',\n            checkSSHConn: 'Verificar Disponibilidade da Conexão SSH',\n            fixSSHConn: 'Edite o nó manualmente para confirmar as informações de conexão',\n            checkConnInfo: 'Verificar Informações de Conexão do Agente',\n            checkStatus: 'Verificar Disponibilidade do Serviço do Nó',\n            fixStatus: 'Execute \"systemctl status 1panel-agent.service\" para verificar se o serviço está em execução.',\n            checkAPI: 'Verificar Disponibilidade da API do Nó',\n            fixAPI: 'Verifique os logs do nó e confirme se as portas do firewall estão devidamente abertas.',\n            forceDelete: 'Excluir Forçadamente',\n            operateHelper: 'A operação {0} será realizada nos seguintes nós, deseja continuar?',\n            operatePanelHelper: 'A operação {0} será executada nos painéis a seguir. Continuar?',\n            forceDeleteHelper:\n                'Excluir forçadamente ignorará erros de exclusão do nó e removerá os metadados do banco de dados',\n            uninstall: 'Excluir dados do nó',\n            uninstallHelper: 'Isso excluirá todos os dados relacionados ao 1Panel do nó. Escolha com cuidado!',\n            baseDir: 'Diretório de Instalação',\n            baseDirHelper: 'Quando o diretório de instalação está vazio, será instalado por padrão no diretório /opt',\n            nodePort: 'Porta do Nó',\n            offline: 'Modo offline',\n            freeCount: 'Cota gratuita [{0}]',\n            offlineHelper: 'Usado quando o nó está em ambiente offline',\n            appUpgrade: 'Atualização do Aplicativo',\n            appUpgradeHelper: 'Existem {0} aplicativos que precisam ser atualizados',\n        },\n        customApp: {\n            name: 'Repositório de Aplicativos Personalizados',\n            appStoreType: 'Fonte do Pacote da App Store',\n            appStoreUrl: 'URL do Repositório',\n            local: 'Caminho Local',\n            remote: 'Link Remoto',\n            imagePrefix: 'Prefixo de Imagem',\n            imagePrefixHelper:\n                'Função: Personalize o prefixo da imagem e modifique o campo de imagem no arquivo compose. Por exemplo, quando o prefixo da imagem é configurado como 1panel/custom, o campo de imagem do MaxKB mudará para 1panel/custom/maxkb:v1.10.0',\n            closeHelper: 'Cancelar o uso do repositório de aplicativos personalizados',\n            appStoreUrlHelper: 'Apenas formato .tar.gz é suportado',\n            postNode: 'Sincronizar para subnó',\n            postNodeHelper:\n                'Sincronize o pacote da loja personalizada para tmp/customApp/apps.tar.gz no diretório de instalação do sub-nó',\n            nodes: 'Selecionar Nós',\n            selectNode: 'Selecionar Node',\n            selectNodeError: 'Por favor, selecione um nó',\n            licenseHelper: 'A versão Pro suporta o recurso de repositório de aplicativos personalizados',\n            databaseHelper: 'Banco de dados associado ao aplicativo, selecione o banco de dados do nó de destino',\n            nodeHelper: 'Não é possível selecionar o nó atual',\n            migrateHelper:\n                'Atualmente suporta apenas a migração de aplicações monolíticas e aplicações associadas apenas a bancos de dados MySQL, MariaDB, PostgreSQL',\n            opensslHelper:\n                'Se usar backup criptografado, as versões do OpenSSL entre os dois nós devem ser consistentes, caso contrário a migração pode falhar.',\n            installApp: 'Instalação em lote',\n            installAppHelper: 'Instalar aplicativos em lote nos nós selecionados',\n        },\n        alert: {\n            isAlert: 'Alerta',\n            alertCount: 'Contagem de Alertas',\n            clamHelper: 'Dispara alerta via ao detectar arquivos infectados durante a varredura',\n            cronJobHelper: 'Dispara alerta via ao falhar na execução de tarefas',\n            licenseHelper: 'A versão profissional suporta alertas via',\n            alertCountHelper: 'Frequência máxima diária de alertas',\n            alert: 'Alerta por SMS',\n            logs: 'Registros de Alerta',\n            list: 'Lista de Alertas',\n            addTask: 'Criar Alerta',\n            editTask: 'Editar Alerta',\n            alertMethod: 'Método',\n            alertMsg: 'Mensagem de Alerta',\n            alertRule: 'Regras de Alerta',\n            titleSearchHelper: 'Digite o título do alerta para busca aproximada',\n            taskType: 'Tipo',\n            ssl: 'Expiração do Certificado',\n            siteEndTime: 'Expiração do Site',\n            panelPwdEndTime: 'Expiração da Senha do Painel',\n            panelUpdate: 'Nova Versão do Painel Disponível',\n            cpu: 'Alerta de CPU do Servidor',\n            memory: 'Alerta de Memória do Servidor',\n            load: 'Alerta de Carga do Servidor',\n            disk: 'Alerta de Disco do Servidor',\n            website: 'Site',\n            certificate: 'Certificado SSL',\n            remainingDays: 'Dias Restantes',\n            sendCount: 'Contagem de Envio',\n            sms: 'SMS',\n            wechat: 'WeChat',\n            dingTalk: 'DingTalk',\n            feiShu: 'FeiShu',\n            mail: 'E-mail',\n            weCom: 'WeCom',\n            sendCountRulesHelper: 'Alertas totais enviados antes da expiração (uma vez por dia)',\n            panelUpdateRulesHelper: 'Alertas totais enviados para nova versão do painel (uma vez por dia)',\n            oneDaySendCountRulesHelper: 'Número máximo de alertas enviados por dia',\n            siteEndTimeRulesHelper: 'Sites que nunca expiram não dispararão alertas',\n            autoRenewRulesHelper:\n                'Certificados com renovação automática ativada e menos de 31 dias restantes não dispararão alertas',\n            panelPwdEndTimeRulesHelper:\n                'Alertas de expiração da senha do painel não estão disponíveis se nenhuma expiração estiver definida',\n            sslRulesHelper: 'Todos os Certificados SSL',\n            diskInfo: 'Disco',\n            monitoringType: 'Tipo de Monitoramento',\n            autoRenew: 'Renovação Automática',\n            useDisk: 'Uso de Disco',\n            usePercentage: 'Porcentagem de Uso',\n            changeStatus: 'Alterar Status',\n            disableMsg:\n                'Parar a tarefa de alerta impedirá que esta tarefa envie mensagens de alerta. Deseja continuar?',\n            enableMsg:\n                'Ativar a tarefa de alerta permitirá que esta tarefa envie mensagens de alerta. Deseja continuar?',\n            useExceed: 'Uso Excedeu',\n            useExceedRulesHelper: 'Dispara alerta quando o uso excede o valor configurado',\n            cpuUseExceedAvg: 'O uso médio da CPU excede o valor especificado',\n            memoryUseExceedAvg: 'O uso médio da memória excede o valor especificado',\n            loadUseExceedAvg: 'O uso médio da carga excede o valor especificado',\n            cpuUseExceedAvgHelper: 'O uso médio da CPU dentro do tempo especificado excede o valor especificado',\n            memoryUseExceedAvgHelper: 'O uso médio da memória dentro do tempo especificado excede o valor especificado',\n            loadUseExceedAvgHelper: 'O uso médio da carga dentro do tempo especificado excede o valor especificado',\n            resourceAlertRulesHelper: 'Nota: Alertas contínuos em 30 minutos enviarão apenas um',\n            specifiedTime: 'Hora Especificada',\n            deleteTitle: 'Excluir Alerta',\n            deleteMsg: 'Tem certeza de que deseja excluir a tarefa de alerta?',\n            allSslTitle: 'Alertas de Expiração de Todos os Certificados SSL do Site',\n            sslTitle: 'Alerta de Expiração do Certificado SSL do Site {0}',\n            allSiteEndTimeTitle: 'Alertas de Expiração de Todos os Sites',\n            siteEndTimeTitle: 'Alerta de Expiração do Site {0}',\n            panelPwdEndTimeTitle: 'Alerta de Expiração da Senha do Painel',\n            panelUpdateTitle: 'Notificação de Nova Versão do Painel',\n            cpuTitle: 'Alerta de Alto Uso de CPU',\n            memoryTitle: 'Alerta de Alto Uso de Memória',\n            loadTitle: 'Alerta de Alta Carga',\n            diskTitle: 'Alerta de Alto Uso de Disco para o Diretório de Montagem {0}',\n            allDiskTitle: 'Alerta de Alto Uso de Disco',\n            timeRule: 'Tempo restante menor que {0} dias (se não tratado, será reenviado no próximo dia)',\n            panelUpdateRule:\n                'Envia um alerta quando uma nova versão do painel é detectada (se não tratado, será reenviado no próximo dia)',\n            avgRule: 'Uso médio de {1} excede {2}% em {0} minutos, dispara alerta, envia {3} vezes por dia',\n            diskRule:\n                'Uso de disco para o diretório de montagem {0} excede {1}{2}, dispara alerta, envia {3} vezes por dia',\n            allDiskRule: 'Uso de disco excede {0}{1}, dispara alerta, envia {2} vezes por dia',\n            cpuName: 'CPU',\n            memoryName: 'Memória',\n            loadName: 'Carga',\n            diskName: 'Disco',\n            syncAlertInfo: 'Envio Manual',\n            syncAlertInfoMsg: 'Deseja enviar a tarefa de alerta manualmente?',\n            pushError: 'Envio Falhou',\n            pushSuccess: 'Envio Bem-sucedido',\n            syncError: 'Sincronização Falhou',\n            success: 'Alerta Bem-sucedido',\n            pushing: 'Enviando...',\n            error: 'Falha no alerta',\n            cleanLog: 'Limpar Registros',\n            cleanAlertLogs: 'Limpar Registros de Alertas',\n            daily: 'Contagem Diária de Alertas: {0}',\n            cumulative: 'Contagem Acumulada de Alertas: {0}',\n            clams: 'Varredura de Vírus',\n            taskName: 'Nome da Tarefa',\n            cronJobType: 'Tipo de Tarefa',\n            clamPath: 'Diretório de Varredura',\n            cronjob: 'Tarefa Cron agendada {0} falhou',\n            app: 'Backup do Aplicativo',\n            web: 'Backup do Site',\n            database: 'Backup do Banco de Dados',\n            directory: 'Backup do Diretório',\n            log: 'Registros de Backup',\n            snapshot: 'Instantâneo do Sistema',\n            clamsRulesHelper: 'Tarefas de varredura de vírus que exigem alertas por',\n            cronJobRulesHelper: 'Este tipo de tarefa cron precisa ser configurado',\n            clamsTitle: 'Tarefa de Varredura de Vírus 「 {0} 」 detectou arquivo infectado',\n            cronJobAppTitle: 'Tarefa Cron - Falha no Backup do Aplicativo 「 {0} 」',\n            cronJobWebsiteTitle: 'Tarefa Cron - Falha no Backup do Site 「 {0} 」',\n            cronJobDatabaseTitle: 'Tarefa Cron - Falha no Backup do Banco de Dados 「 {0} 」',\n            cronJobDirectoryTitle: 'Tarefa Cron - Falha no Backup do Diretório 「 {0} 」',\n            cronJobLogTitle: 'Tarefa Cron - Falha nos Registros de Backup 「 {0} 」',\n            cronJobSnapshotTitle: 'Tarefa Cron - Falha no Instantâneo do Sistema 「 {0} 」',\n            cronJobShellTitle: 'Tarefa Cron - Falha no Script Shell 「 {0} 」',\n            cronJobCurlTitle: 'Tarefa Cron - Falha no Acesso à URL 「 {0} 」',\n            cronJobCutWebsiteLogTitle: 'Tarefa Cron - Falha no Corte do Registro do Site 「 {0} 」',\n            cronJobCleanTitle: 'Tarefa Cron - Falha na Limpeza de Cache 「 {0} 」',\n            cronJobNtpTitle: 'Tarefa Cron - Falha na Sincronização do Horário do Servidor 「 {0} 」',\n            clamsRule: 'Varredura de vírus detectou arquivo infectado, enviado {0} vezes por dia',\n            cronJobAppRule: 'Falha na tarefa de backup do aplicativo, enviada {0} vezes por dia',\n            cronJobWebsiteRule: 'Falha na tarefa de backup do site, enviada {0} vezes por dia',\n            cronJobDatabaseRule: 'Falha na tarefa de backup do banco de dados, enviada {0} vezes por dia',\n            cronJobDirectoryRule: 'Falha na tarefa de backup do diretório, enviada {0} vezes por dia',\n            cronJobLogRule: 'Falha na tarefa de backup de registros, enviada {0} vezes por dia',\n            cronJobSnapshotRule: 'Falha na tarefa de backup de instantâneo, enviada {0} vezes por dia',\n            cronJobShellRule: 'Falha na tarefa de script shell, enviada {0} vezes por dia',\n            cronJobCurlRule: 'Falha na tarefa de acesso à URL, enviada {0} vezes por dia',\n            cronJobCutWebsiteLogRule: 'Falha na tarefa de corte do registro do site, enviada {0} vezes por dia',\n            cronJobCleanRule: 'Falha na tarefa de limpeza de cache, enviada {0} vezes por dia',\n            cronJobNtpRule: 'Falha na tarefa de sincronização do horário do servidor, enviada {0} vezes por dia',\n            alertSmsHelper: 'Limite de SMS: total de {0} mensagens, {1} já usadas',\n            goBuy: 'Comprar Mais',\n            phone: 'Telefone',\n            phoneHelper: 'Forneça um número de telefone real para mensagens de alerta',\n            dailyAlertNum: 'Número de alertas diários',\n            dailyAlertNumHelper: 'Número total de alertas diários, até um máximo de 100 alertas',\n            timeRange: 'Intervalo de Tempo',\n            sendTimeRange: 'Intervalo de tempo para envio de',\n            sendTimeRangeHelper: 'Pode enviar alerta dentro do intervalo de tempo {0}',\n            to: '-',\n            startTime: 'hora de início',\n            endTime: 'hora de término',\n            defaultPhone: 'Número de telefone padrão vinculado à conta de licença',\n            noticeAlert: 'Alerta de Notificação',\n            resourceAlert: 'Alerta de Recursos',\n            agentOfflineAlertHelper:\n                'Quando o alerta offline estiver ativado para o nó, o nó principal verificará a cada 30 minutos para executar as tarefas de alerta.',\n            offline: 'Alerta Offline',\n            offlineHelper:\n                'Ao definir como alerta offline, o nó principal verificará a cada 30 minutos para executar tarefas de alerta.',\n            offlineOff: 'Ativar Alerta Offline',\n            offlineOffHelper:\n                'Ao ativar o alerta offline, o nó principal verificará a cada 30 minutos para executar as tarefas de alerta.',\n            offlineClose: 'Desativar Alerta Offline',\n            offlineCloseHelper:\n                'Ao desativar o alerta offline, os nós secundários deverão lidar com os alertas por conta própria. Certifique-se de que a conectividade de rede está boa para evitar falhas.',\n            alertNotice: 'Notificação de Alerta',\n            methodConfig: 'Configuração do Método de Notificação',\n            commonConfig: 'Configuração Global',\n            smsConfig: 'SMS',\n            smsConfigHelper: 'Configure os números para notificação por SMS',\n            emailConfig: 'E-mail',\n            emailConfigHelper: 'Configure o serviço de envio de e-mail SMTP',\n            deleteConfigTitle: 'Excluir Configuração de Alerta',\n            deleteConfigMsg: 'Tem certeza de que deseja excluir a configuração de alerta?',\n            test: 'Testar',\n            alertTestOk: 'Notificação de teste bem-sucedida',\n            alertTestFailed: 'Falha na notificação de teste',\n            displayName: 'Nome',\n            sender: 'Remetente',\n            password: 'Senha',\n            host: 'Servidor SMTP',\n            port: 'Porta',\n            encryption: 'Criptografia',\n            recipient: 'Destinatário',\n            licenseTime: 'Lembrete de Expiração da Licença',\n            licenseTimeTitle: 'Lembrete de Expiração da Licença',\n            displayNameHelper: 'Nome exibido do remetente do e-mail',\n            senderHelper: 'Endereço de e-mail usado para envio',\n            passwordHelper: 'Código de autorização do serviço de e-mail',\n            hostHelper: 'Endereço do servidor SMTP, ex: smtp.qq.com',\n            portHelper: 'SSL geralmente usa 465, TLS geralmente usa 587',\n            sslHelper: 'Se a porta SMTP for 465, normalmente é necessário SSL',\n            tlsHelper: 'Se a porta SMTP for 587, normalmente é necessário TLS',\n            triggerCondition: 'Condição de Disparo',\n            loginFail: ' falhas de login em',\n            nodeException: 'Alerta de Exceção de Nó',\n            licenseException: 'Alerta de Exceção de Licença',\n            panelLogin: 'Alerta de Exceção de Login no Painel',\n            sshLogin: 'Alerta de Exceção de Login SSH',\n            panelIpLogin: 'Alerta de Exceção de IP de Login no Painel',\n            sshIpLogin: 'Alerta de Exceção de IP de Login SSH',\n            ipWhiteListHelper:\n                'Os IPs na lista de permissões não são restringidos pelas regras e não haverá alerta em caso de login bem-sucedido',\n            nodeExceptionRule: 'Alerta de exceção de nó, enviado {0} vezes por dia',\n            licenseExceptionRule: 'Alerta de exceção de licença, enviado {0} vezes por dia',\n            panelLoginRule: 'Alerta de login no painel, enviado {0} vezes por dia',\n            sshLoginRule: 'Alerta de login SSH, enviado {0} vezes por dia',\n            userNameHelper: 'O nome de usuário está vazio, o endereço do remetente será usado por padrão',\n            alertConfigHelper: 'Configurar canais de notificação de alerta para receber push de mensagens do painel',\n            weComConfigHelper: 'Configuração de notificação de alerta WeCom',\n            wechatConfigHelper: 'Configuração de notificação de alerta da Conta Oficial WeChat',\n            dingTalkConfigHelper: 'Configuração de notificação de alerta DingTalk',\n            feiShuConfigHelper: 'Configuração de notificação de alerta Feishu',\n            webhookName: 'Nome do bot',\n            webhookUrl: 'URL do Webhook',\n            alertConfigProHelper:\n                'A edição Profissional oferece suporte adicional a alertas via WeCom, DingTalk, Feishu e SMS.',\n        },\n        theme: {\n            lingXiaGold: 'Ling Xia Gold',\n            classicBlue: 'Azul Clássico',\n            freshGreen: 'Verde Fresco',\n            customColor: 'Cor do Tema Personalizada',\n            setDefault: 'Restaurar Padrão',\n            setDefaultHelper: 'O esquema de cores do tema será restaurado para o estado inicial. Deseja continuar?',\n            setHelper: 'O esquema de cores do tema selecionado será salvo. Deseja continuar?',\n        },\n        exchange: {\n            exchange: 'Troca de Arquivos',\n            exchangeConfirm: 'Deseja transferir o arquivo/pasta {1} do nó {0} para o diretório {3} do nó {2}?',\n        },\n        cluster: {\n            cluster: 'Alta Disponibilidade de Aplicações',\n            name: 'Nome do Cluster',\n            addCluster: 'Adicionar Cluster',\n            installNode: 'Instalar Nó',\n            master: 'Nó Mestre',\n            slave: 'Nó Escravo',\n            replicaStatus: 'Status Mestre-Escravo',\n            unhealthyDeleteError:\n                'O status do nó de instalação está anormal, verifique a lista de nós e tente novamente!',\n            replicaStatusError: 'A aquisição do status está anormal, verifique o nó mestre.',\n            masterHostError: 'O IP do nó mestre não pode ser 127.0.0.1',\n        },\n    },\n};\n\nexport default {\n    ...fit2cloudPtBrLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/ru.ts",
    "content": "import fit2cloudEnLocale from 'fit2cloud-ui-plus/src/locale/lang/ru';\n\nconst message = {\n    commons: {\n        true: 'Да',\n        false: 'Нет',\n        colon: ': ',\n        example: 'например, ',\n        fit2cloud: 'FIT2CLOUD',\n        lingxia: 'Lingxia',\n        button: {\n            run: 'Запуск',\n            create: 'Создать ',\n            add: 'Добавить ',\n            save: 'Сохранить ',\n            set: 'Изменить конфигурацию',\n            sync: 'Синхронизировать ',\n            delete: 'Удалить',\n            edit: 'Редактировать ',\n            enable: 'Включить',\n            disable: 'Отключить',\n            confirm: 'Подтвердить',\n            cancel: 'Отмена',\n            reset: 'Сбросить',\n            restart: 'Перезапустить',\n            conn: 'Подключить',\n            disConn: 'Отключить',\n            clean: 'Очистить',\n            login: 'Войти',\n            close: 'Закрыть',\n            off: 'Закрыть',\n            stop: 'Остановить',\n            start: 'Запустить',\n            view: 'Просмотр',\n            watch: 'Наблюдать',\n            handle: 'Запустить',\n            expand: 'Развернуть',\n            clone: 'Клонировать',\n            collapse: 'Свернуть',\n            log: 'Логи',\n            back: 'Назад',\n            backup: 'Бэкап',\n            recover: 'Восстановить',\n            retry: 'Повторить',\n            upload: 'Загрузить',\n            download: 'Скачать',\n            init: 'Инициализировать',\n            verify: 'Проверить',\n            saveAndEnable: 'Сохранить и включить',\n            import: 'Импорт',\n            export: 'Экспорт',\n            power: 'Авторизация',\n            search: 'Поиск',\n            refresh: 'Обновить',\n            get: 'Получить',\n            upgrade: 'Обновить',\n            update: 'обновление',\n            updateNow: 'Обновить Сейчас',\n            ignore: 'Игнорировать обновление',\n            install: 'установить',\n            copy: 'Копировать',\n            random: 'Случайно',\n            uninstall: 'Удалить',\n            fullscreen: 'Полный экран',\n            quitFullscreen: 'Выйти из полного экрана',\n            showAll: 'Показать все',\n            hideSome: 'Скрыть некоторые',\n            agree: 'Согласен',\n            notAgree: 'Не согласен',\n            preview: 'Предпросмотр',\n            open: 'Открыть',\n            notSave: 'Не сохранять',\n            createNewFolder: 'Создать новую папку',\n            createNewFile: 'Создать новый файл',\n            helpDoc: 'Справка',\n            unbind: 'Отвязать',\n            cover: 'Заменить',\n            skip: 'Пропустить',\n            fix: 'Исправить',\n            down: 'Остановить',\n            up: 'Запустить',\n            sure: 'Подтвердить',\n            show: 'Показать',\n            hide: 'Скрыть',\n            visit: 'Посетить',\n            migrate: 'Мигрировать',\n            prev: 'Назад',\n            next: 'Далее',\n            setDefault: 'Сбросить по умолчанию',\n            bind: 'Привязать',\n        },\n        operate: {\n            start: 'Запустить',\n            stop: 'Остановить',\n            restart: 'Перезапустить',\n            reload: 'Перезагрузить',\n            rebuild: 'Перестроить',\n            sync: 'Синхронизировать',\n            up: 'Запустить',\n            down: 'Остановить',\n            delete: 'Удалить',\n        },\n        search: {\n            timeStart: 'Время начала',\n            timeEnd: 'Время окончания',\n            timeRange: 'До',\n            dateStart: 'Дата начала',\n            dateEnd: 'Дата окончания',\n            date: 'Дата',\n        },\n        table: {\n            all: 'Все',\n            total: 'Всего {0}',\n            name: 'Имя',\n            type: 'Тип',\n            status: 'Статус',\n            records: 'Записи',\n            group: 'Группа',\n            createdAt: 'Время создания',\n            publishedAt: 'Время публикации',\n            date: 'Дата',\n            updatedAt: 'Время обновления',\n            operate: 'Операции',\n            message: 'Сообщение',\n            description: 'Описание',\n            interval: 'Интервал',\n            user: 'Владелец',\n            title: 'Заголовок',\n            port: 'Порт',\n            forward: 'Переадресация',\n            protocol: 'Протокол',\n            tableSetting: 'Настройки таблицы',\n            refreshRate: 'Частота обновления',\n            selectColumn: 'Выбрать столбец',\n            local: 'локальный',\n            serialNumber: 'Серийный номер',\n            manageGroup: 'Управление группами',\n            backToList: 'Вернуться к списку',\n            keepEdit: 'Продолжить редактирование',\n            default: 'По умолчанию',\n            noRefresh: 'Без автообновления',\n        },\n        loadingText: {\n            Upgrading: 'Обновление системы, пожалуйста, подождите...',\n            Restarting: 'Перезагрузка системы, пожалуйста, подождите...',\n            Recovering: 'Восстановление из снапшота, пожалуйста, подождите...',\n            Rollbacking: 'Откат из снапшота, пожалуйста, подождите...',\n        },\n        msg: {\n            noneData: 'Нет данных',\n            delete: 'Эта операция удаления не может быть отменена. Хотите продолжить?',\n            clean: 'Эта операция очистки не может быть отменена. Хотите продолжить?',\n            closeDrawerHelper: 'Система может не сохранить внесённые вами изменения. Продолжить?',\n            deleteSuccess: 'Успешно удалено',\n            loginSuccess: 'Успешный вход',\n            operationSuccess: 'Операция выполнена успешно',\n            copySuccess: 'Успешно скопировано',\n            notSupportOperation: 'Эта операция не поддерживается',\n            requestTimeout: 'Время запроса истекло, попробуйте позже',\n            infoTitle: 'Подсказка',\n            notRecords: 'Для текущей задачи не создано записей выполнения',\n            sureLogOut: 'Вы уверены, что хотите выйти?',\n            createSuccess: 'Успешно создано',\n            updateSuccess: 'Успешно обновлено',\n            uploadSuccess: 'Успешно загружено',\n            operateConfirm: 'Если вы уверены в операции, введите её вручную: ',\n            inputOrSelect: 'Пожалуйста, выберите или введите',\n            copyFailed: 'Не удалось скопировать',\n            operatorHelper: 'Операция \"{1}\" будет выполнена над \"{0}\" и не может быть отменена. Хотите продолжить?',\n            notFound: 'Извините, запрошенная страница не существует.',\n            unSupportType: 'Текущий тип файла не поддерживается.',\n            unSupportSize: 'Загруженный файл превышает {0}M, пожалуйста, подтвердите!',\n            fileExist: 'Файл уже существует в текущей папке. Повторная загрузка не поддерживается.',\n            fileNameErr:\n                'Вы можете загружать только файлы, имя которых содержит от 1 до 256 символов, включая английские буквы, китайские иероглифы, цифры или точки (.-_)',\n            confirmNoNull: 'Убедитесь, что значение {0} не пустое.',\n            errPort: 'Неверная информация о порте, пожалуйста, проверьте!',\n            remove: 'Удалить',\n            backupHelper: 'Текущая операция создаст резервную копию {0}. Хотите продолжить?',\n            recoverHelper: 'Восстановление из файла {0}. Эта операция необратима. Хотите продолжить?',\n            refreshSuccess: 'Обновление успешно',\n            rootInfoErr: 'Это уже корневой каталог',\n            resetSuccess: 'Сброс успешен',\n            creatingInfo: 'Создание, эта операция не нужна',\n            offlineTips: 'Офлайн версия не поддерживает эту операцию',\n            errImportFormat: 'Данные импорта или формат ненормальны, проверьте и повторите попытку!',\n            importHelper:\n                'При импорте конфликтующих или дублирующихся данных импортированное содержимое будет использоваться в качестве стандарта для обновления исходных данных базы данных.',\n            errImport: 'Содержимое файла ненормально:',\n            backupSuccess: 'Резервное копирование выполнено',\n            restoreSuccess: 'Восстановление выполнено',\n            installSuccess: 'Установка выполнена',\n            uninstallSuccess: 'Удаление выполнено',\n        },\n        login: {\n            username: 'пользователя',\n            password: 'Пароль',\n            passkey: 'Вход по passkey',\n            welcome: 'Добро пожаловать, введите имя пользователя и пароль для входа!',\n            errorAuthInfo: 'Введенное имя пользователя или пароль неверны, пожалуйста, введите заново!',\n            errorMfaInfo: 'Неверная информация аутентификации, попробуйте еще раз!',\n            captchaHelper: 'Капча',\n            errorCaptcha: 'Ошибка кода капчи!',\n            notSafe: 'Доступ запрещен',\n            safeEntrance1: 'В текущей среде включен безопасный вход',\n            safeEntrance2: 'Введите следующую команду в SSH-терминале для просмотра входа в панель: 1pctl user-info',\n            errIP1: 'В текущей среде включен доступ по авторизованному IP-адресу',\n            errDomain1: 'В текущей среде включена привязка домена доступа',\n            errHelper: 'Для сброса информации о привязке выполните следующую команду в SSH-терминале: ',\n            codeInput: 'Пожалуйста, введите 6-значный код подтверждения MFA',\n            mfaTitle: 'MFA Сертификация',\n            mfaCode: 'MFA код подтверждения',\n            title: 'Панель управления Linux сервером',\n            licenseHelper: '<Лицензионное соглашение сообщества>',\n            errorAgree: 'Нажмите, чтобы согласиться с Лицензией программного обеспечения сообщества',\n            logout: 'Выход',\n            agreeTitle: 'Соглашение',\n            agreeContent:\n                'Для лучшей защиты ваших законных прав и интересов, пожалуйста, прочитайте и согласитесь со следующим соглашением &laquo; <a href = \"https://www.fit2cloud.com/legal/licenses.html\" target = \"_blank\" > Лицензионное соглашение сообщества </a> &raquo;',\n            passkeyFailed: 'Вход по passkey не удался, попробуйте снова',\n            passkeyNotSupported: 'Текущий браузер или среда не поддерживает passkey',\n            passkeyToPassword: 'Не получается использовать passkey? Войдите по паролю',\n        },\n        rule: {\n            username: 'Введите имя пользователя',\n            password: 'Введите пароль',\n            rePassword: 'Подтверждение пароля не совпадает с паролем.',\n            requiredInput: 'Это поле обязательно для заполнения.',\n            requiredSelect: 'Выберите элемент из списка',\n            illegalChar: 'В настоящее время не поддерживается вставка символов & ; $ \\' ` ( ) \" > < |',\n            illegalInput: 'Это поле не должно содержать недопустимых символов.',\n            commonName:\n                'Это поле должно начинаться с неспециальных символов и должно состоять из английских букв, китайских иероглифов, цифр, \".\", \"-\" и \"_\" длиной 1-128.',\n            userName: 'Поддерживает начало без специальных символов, английский, китайский, цифры и _, длина 3-30',\n            simpleName:\n                'Это поле не должно начинаться с подчеркивания и должно состоять из английских букв, цифр и \"_\" длиной 3-30.',\n            simplePassword:\n                'Это поле не должно начинаться с подчеркивания и должно состоять из английских букв, цифр и \"_\" длиной 1-30.',\n            dbName: 'Это поле не должно начинаться с подчеркивания и должно состоять из английских букв, цифр и \"_\" длиной 1-64.',\n            imageName: 'Поддерживает начало без специальных символов, английский, цифры, :@/.-_, длина 1-256',\n            composeName: 'Поддерживаются неспециальные символы в начале, строчные буквы, цифры, - и _, длина 1-256',\n            volumeName: 'Это поле должно состоять из английских букв, цифр, \".\", \"-\" и \"_\" длиной 2-30.',\n            supervisorName:\n                'Это поле должно начинаться с неспециальных символов и должно состоять из английских букв, цифр, \"-\" и \"_\" длиной 1-128.',\n            complexityPassword:\n                'Это поле должно состоять из английских букв, цифр длиной 8-30 и содержать как минимум два специальных символа.',\n            commonPassword: 'Длина этого поля должна быть больше 6.',\n            linuxName:\n                'Длина этого поля должна быть от 1 до 128. Поле не должно содержать эти специальные символы: \"{0}\".',\n            email: 'Это поле должно быть действительным адресом электронной почты.',\n            number: 'Это поле должно быть числом.',\n            integer: 'Это поле должно быть положительным целым числом.',\n            ip: 'Это поле должно быть действительным IP-адресом.',\n            host: 'Это поле должно быть действительным IP-адресом или доменным именем.',\n            hostHelper: 'Поддерживается ввод IP-адреса или доменного имени',\n            port: 'Это поле должно быть действительным номером порта.',\n            selectHelper: 'Пожалуйста, выберите правильный файл {0}',\n            domain: 'Это поле должно быть в формате: example.com или example.com:8080.',\n            databaseName: 'Это поле должно состоять из английских букв, цифр и \"_\" длиной 1-30.',\n            ipErr: 'Это поле должно быть действительным IP-адресом.',\n            numberRange: 'Это поле должно быть числом между {0} и {1}.',\n            paramName: 'Это поле должно состоять из английских букв, цифр, \".\", \"-\" и \"_\" длиной 2-30.',\n            paramComplexity:\n                'Это поле не должно начинаться и заканчиваться специальными символами и должно состоять из английских букв, цифр, \"{0}\" длиной 6-128.',\n            paramUrlAndPort: 'Это поле должно быть в формате \"http(s)://(доменное имя/ip):(порт)\".',\n            nginxDoc: 'Это поле должно состоять из английских букв, цифр и \".\".',\n            appName:\n                'Это поле не должно начинаться и заканчиваться символами \"-\" и \"_\" и должно состоять из английских букв, цифр, \"-\" и \"_\" длиной 2-30.',\n            containerName: 'Поддерживаются буквы, цифры, -, _ и .; не может начинаться с - _ или .; длина: 2-128',\n            mirror: 'Адрес ускорения зеркала должен начинаться с http(s)://, поддерживает английские буквы (как заглавные, так и строчные), цифры, . / и -, и не должен содержать пустых строк.',\n            disableFunction: 'Поддерживаются только буквы, подчеркивания и запятые',\n            leechExts: 'Поддерживаются только буквы, цифры и запятые',\n            paramSimple: 'Поддерживаются строчные буквы и цифры, длина 1-128',\n            filePermission: 'Ошибка прав доступа к файлу',\n            formatErr: 'Ошибка формата, пожалуйста, проверьте и повторите попытку',\n            phpExtension: 'Поддерживаются только запятые, подчеркивания, строчные английские буквы и цифры',\n            paramHttp: 'Должно начинаться с http:// или https://',\n            phone: 'Неверный формат номера телефона',\n            authBasicPassword: 'Поддерживает буквы, цифры и общие специальные символы, длина 1-72',\n            length128Err: 'Длина не может превышать 128 символов',\n            maxLength: 'Длина не может превышать {0} символов',\n            alias: 'Поддерживает английский, цифры, - и _, длина 1-128, и не может начинаться или заканчиваться на -_.',\n        },\n        res: {\n            paramError: 'Запрос не удался, попробуйте позже!',\n            forbidden: 'У текущего пользователя нет прав',\n            serverError: 'Ошибка сервиса',\n            notFound: 'Ресурс не существует',\n            commonError: 'Запрос не удался',\n        },\n        service: {\n            serviceNotStarted: 'Сервис {0} не запущен.',\n        },\n        status: {\n            running: 'Работает',\n            done: 'Завершено',\n            scanFailed: 'Неполный',\n            success: 'Успешно',\n            waiting: 'Ожидание',\n            waitForUpgrade: 'Ожидание Обновления',\n            waiting1: 'Ожидание',\n            failed: 'Ошибка',\n            stopped: 'Остановлен',\n            error: 'Ошибка',\n            created: 'Создан',\n            restarting: 'Перезапуск',\n            uploading: 'Загрузка',\n            unhealthy: 'Нездоровый',\n            removing: 'Удаление',\n            paused: 'Приостановлен',\n            exited: 'Вышел',\n            dead: 'Мертв',\n            installing: 'Установка',\n            enabled: 'Включен',\n            disabled: 'Отключен',\n            normal: 'Нормально',\n            building: 'Сборка',\n            upgrading: 'Обновление',\n            pending: 'Ожидает редактирования',\n            rebuilding: 'Пересборка',\n            deny: 'Отказано',\n            accept: 'Принято',\n            used: 'Используется',\n            unUsed: 'Не используется',\n            starting: 'Запуск',\n            recreating: 'Пересоздание',\n            creating: 'Создание',\n            init: 'Ожидание приложения',\n            ready: 'нормально',\n            applying: 'Применение',\n            uninstalling: 'Удаление',\n            lost: 'Потеряно',\n            bound: 'Привязано',\n            unbind: 'Не привязано',\n            exceptional: 'Исключение',\n            free: 'Свободно',\n            enable: 'Включено',\n            disable: 'Отключено',\n            deleted: 'Удалено',\n            downloading: 'Загрузка',\n            packing: 'Упаковка',\n            sending: 'Отправка',\n            healthy: 'Нормально',\n            executing: 'Выполнение',\n            installerr: 'Ошибка установки',\n            applyerror: 'Ошибка применения',\n            systemrestart: 'Прервано',\n            starterr: 'Ошибка запуска',\n            uperr: 'Ошибка запуска',\n            new: 'Новый',\n            conflict: 'Конфликт',\n            duplicate: 'Дубликат',\n            unexecuted: 'Не Выполнено',\n            unused: 'Не используется',\n        },\n        units: {\n            second: ' секунда | секунда | секунд',\n            minute: 'минута | минута | минут',\n            hour: 'час | час | часов',\n            day: 'день | день | дней',\n            week: 'неделя | неделя | недель',\n            month: 'месяц | месяц | месяцев',\n            year: 'год | год | лет',\n            time: 'раз',\n            core: 'ядро | ядро | ядер',\n            secondUnit: 'с',\n            minuteUnit: 'мин',\n            hourUnit: 'ч',\n            dayUnit: 'д',\n            millisecond: 'Миллисекунда',\n            semicolon: ';',\n        },\n        log: {\n            noLog: 'Нет доступных логов',\n        },\n    },\n    menu: {\n        home: 'Панель',\n        apps: 'Приложения',\n        website: 'Сайт | Сайты',\n        project: 'Проект | Проекты',\n        config: 'Конфигурация | Конфигурации',\n        ssh: 'Настройки SSH',\n        firewall: 'Firewall',\n        ssl: 'Сертификат | Сертификаты',\n        database: 'База данных | Базы данных',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: 'Контейнер | Контейнеры',\n        cronjob: 'Cron | Задачи Cron',\n        system: 'Система',\n        security: 'Безопасность',\n        files: 'Файлы',\n        monitor: 'Мониторинг',\n        terminal: 'Терминалы',\n        settings: 'Настройки',\n        toolbox: 'Инструменты',\n        logs: 'Лог | Логи',\n        runtime: 'Среда исполнения | Среды исполнения',\n        processManage: 'Процесс | Процессы',\n        process: 'Процесс | Процессы',\n        network: 'Сеть | Сети',\n        supervisor: 'Супервизор',\n        tamper: 'Защита от несанкционированного доступа',\n        app: 'Приложение',\n        msgCenter: 'Задачи',\n        disk: 'Диск',\n        filter: 'Фильтр',\n    },\n    home: {\n        recommend: 'Рекомендуемое',\n        dir: 'Каталог',\n        alias: 'Псевдоним',\n        quickDir: 'Быстрый доступ',\n        minQuickJump: 'Добавьте хотя бы одну запись быстрого доступа',\n        maxQuickJump: 'Вы можете добавить до четырех записей быстрого доступа',\n        database: 'База данных - Все',\n        restart_1panel: 'Перезапустить панель',\n        restart_system: 'Перезапустить сервер',\n        operationSuccess: 'Готово. Перезагрузка. Обновите браузер позже.',\n        entranceHelper: 'Безопасный вход не включен. Включите его в \"Настройки -> Безопасность\" для повышения защиты.',\n        appInstalled: 'Приложения',\n        systemInfo: 'Системная информация',\n        hostname: 'Имя хоста',\n        platformVersion: 'Операционная система',\n        kernelVersion: 'Ядро',\n        kernelArch: 'Архитектура',\n        network: 'Сеть',\n        io: 'Диск I/O',\n        ip: 'Локальный IP',\n        proxy: 'Системный прокси',\n        baseInfo: 'Базовая информация',\n        totalSend: 'Всего отправлено',\n        totalRecv: 'Всего получено',\n        rwPerSecond: 'Операции ввода/вывода',\n        ioDelay: 'Задержка ввода/вывода',\n        uptime: 'Работает с',\n        runningTime: 'Время работы',\n        mem: 'Системная Память',\n        swapMem: 'Раздел подкачки',\n        runSmoothly: 'Низкая нагрузка',\n        runNormal: 'Средняя нагрузка',\n        runSlowly: 'Высокая нагрузка',\n        runJam: 'Тяжелая нагрузка',\n        core: 'Физических ядер',\n        logicCore: 'Логических ядер',\n        corePercent: 'Использование Ядра',\n        cpuFrequency: 'Частота CPU',\n        cpuDetailedPercent: 'Распределение Времени CPU',\n        cpuUser: 'Пользователь',\n        cpuSystem: 'Система',\n        cpuIdle: 'Простой',\n        cpuIrq: 'IRQ',\n        cpuSoftirq: 'Soft IRQ',\n        cpuSteal: 'Steal',\n        cpuTop: 'Топ 5 Процессов по Использованию ЦПУ',\n        memTop: 'Топ 5 Процессов по Использованию Памяти',\n        loadAverage: 'Средняя нагрузка за последнюю минуту | Средняя нагрузка за последние {n} минут',\n        load: 'Нагрузка',\n        mount: 'Точка монтирования',\n        fileSystem: 'Файловая система',\n        total: 'Всего',\n        used: 'Использовано',\n        cache: 'Кэш',\n        free: 'Свободно',\n        shard: 'Шардированный',\n        available: 'Доступно',\n        percent: 'Утилизация',\n        goInstall: 'Установить',\n        networkCard: 'Интерфейс',\n        disk: 'Диск',\n        memo: 'Заметка',\n        memoPlaceholder: 'Нажмите кнопку редактирования, чтобы включить редактирование.',\n        carouselSetting: 'Настройки карусели',\n        tooltipSensitiveInfo: 'Показать/скрыть конфиденциальную информацию',\n    },\n    tabs: {\n        more: 'Больше',\n        hide: 'Скрыть',\n        closeLeft: 'Закрыть слева',\n        closeRight: 'Закрыть справа',\n        closeCurrent: 'Закрыть текущую',\n        closeOther: 'Закрыть другие',\n        closeAll: 'Закрыть все',\n    },\n    header: {\n        logout: 'Выход',\n    },\n    database: {\n        manage: 'Управление',\n        deleteBackupHelper: 'Удалить резервные копии базы данных одновременно',\n        delete: 'Операция удаления не может быть отменена, пожалуйста, введите \"',\n        deleteHelper: '\" для удаления этой базы данных',\n        noMysql: 'Сервис базы данных (MySQL или MariaDB)',\n        noPostgresql: 'Сервис базы данных PostgreSQL',\n        goUpgrade: 'Обновить',\n        goInstall: 'Установить',\n        isDelete: 'Удалено',\n        permission: 'Разрешения',\n        format: 'Набор Символов',\n        collation: 'Сопоставление',\n        collationHelper: 'Если пусто, используйте сопоставление по умолчанию для набора символов {0}',\n        permissionForIP: 'IP',\n        permissionAll: 'Все (%)',\n        databaseConnInfo: 'Подключение',\n        rootPassword: 'Пароль root',\n        serviceName: 'Имя сервиса',\n        serviceNameHelper: 'Доступ между контейнерами в одной сети.',\n        backupList: 'Резервное копирование',\n        loadBackup: 'Импорт',\n        localUpload: 'Локальная загрузка',\n        hostSelect: 'Выбор сервера',\n        selectHelper: 'Вы уверены, что хотите импортировать файл резервной копии {0}?',\n        remoteAccess: 'Удаленный доступ',\n        remoteHelper: 'Несколько IP через запятую, например: 172.16.10.111, 172.16.10.112',\n        remoteConnHelper:\n            'Удаленное подключение к MySQL как пользователь root может иметь риски безопасности. Поэтому выполняйте эту операцию с осторожностью.',\n        changePassword: 'Пароль',\n        changePasswordHelper:\n            'База данных была связана с приложением. Изменение пароля изменит пароль базы данных приложения одновременно. Изменение вступит в силу после перезапуска приложения.',\n        recoverTimeoutHelper: '-1 означает отсутствие ограничения по времени ожидания',\n        confChange: 'Конфигурация',\n        confNotFound:\n            'Файл конфигурации не найден. Пожалуйста, обновите приложение до последней версии в магазине приложений и попробуйте снова!',\n        portHelper:\n            'Этот порт является открытым портом контейнера. Вам нужно сохранить изменение отдельно и перезапустить контейнер!',\n        loadFromRemote: 'Синхронизировать',\n        userBind: 'Привязать пользователя',\n        pgBindHelper:\n            'Эта операция используется для создания нового пользователя и привязки его к целевой базе данных. В настоящее время выбор уже существующих пользователей в базе данных не поддерживается.',\n        pgSuperUser: 'Суперпользователь',\n        loadFromRemoteHelper: 'Это синхронизирует информацию о базе данных на сервере с 1Panel. Хотите продолжить?',\n        passwordHelper: 'Не удается получить, нажмите для изменения',\n        remote: 'Удаленный',\n        remoteDB: 'Удаленный сервер | Удаленные серверы',\n        createRemoteDB: 'Привязать @.lower:database.remoteDB',\n        unBindRemoteDB: 'Отвязать @.lower:database.remoteDB',\n        unBindForce: 'Принудительная отвязка',\n        unBindForceHelper:\n            'Игнорировать все ошибки во время процесса отвязки, чтобы обеспечить успешное завершение операции',\n        unBindRemoteHelper:\n            'Отвязка удаленной базы данных только удалит связь привязки и не будет напрямую удалять удаленную базу данных',\n        editRemoteDB: 'Редактировать удаленный сервер',\n        localDB: 'Локальная база данных',\n        address: 'Адрес базы данных',\n        version: 'Версия базы данных',\n        userHelper:\n            'Пользователь root или пользователь базы данных с привилегиями root может получить доступ к удаленной базе данных.',\n        pgUserHelper: 'Пользователь базы данных с привилегиями суперпользователя.',\n        ssl: 'Использовать SSL',\n        clientKey: 'Приватный ключ клиента',\n        clientCert: 'Сертификат клиента',\n        caCert: 'Сертификат CA',\n        hasCA: 'Есть сертификат CA',\n        skipVerify: 'Игнорировать проверку действительности сертификата',\n        initialDB: 'Исходная База Данных',\n        formatHelper:\n            'Текущая кодировка базы данных - {0}, несоответствие кодировок может привести к ошибке восстановления',\n        selectFile: 'Выбрать файл',\n        dropHelper: 'Вы можете перетащить загружаемый файл сюда или',\n        clickHelper: 'нажмите для загрузки',\n        supportUpType:\n            'Поддерживаются только форматы файлов sql, sql.gz, tar.gz, .zip. Импортируемый сжатый файл должен содержать только один файл .sql или включать test.sql',\n        currentStatus: 'Текущее состояние',\n        baseParam: 'Базовые параметры',\n        performanceParam: 'Параметры производительности',\n        runTime: 'Время запуска',\n        connections: 'Всего подключений',\n        bytesSent: 'Отправлено байт',\n        bytesReceived: 'Получено байт',\n        queryPerSecond: 'Запросов в секунду',\n        txPerSecond: 'Транзакций в секунду',\n        connInfo: 'активные/пиковые подключения',\n        connInfoHelper: 'Если значение слишком большое, увеличьте \"max_connections\".',\n        threadCacheHit: 'Попадания в кэш потоков',\n        threadCacheHitHelper: 'Если значение слишком низкое, увеличьте \"thread_cache_size\".',\n        indexHit: 'Попадания в индекс',\n        indexHitHelper: 'Если значение слишком низкое, увеличьте \"key_buffer_size\".',\n        innodbIndexHit: 'Попадания в индекс InnoDB',\n        innodbIndexHitHelper: 'Если значение слишком низкое, увеличьте \"innodb_buffer_pool_size\".',\n        cacheHit: 'Попадания в кэш запросов',\n        cacheHitHelper: 'Если значение слишком низкое, увеличьте \"query_cache_size\".',\n        tmpTableToDB: 'Временные таблицы на диске',\n        tmpTableToDBHelper: 'Если значение слишком большое, попробуйте увеличить \"tmp_table_size\".',\n        openTables: 'Открытые таблицы',\n        openTablesHelper: 'Значение конфигурации \"table_open_cache\" должно быть больше или равно этому значению.',\n        selectFullJoin: 'Полные соединения',\n        selectFullJoinHelper: 'Если значение не 0, проверьте правильность индексов таблиц данных.',\n        selectRangeCheck: 'Количество соединений без индекса',\n        selectRangeCheckHelper: 'Если значение не 0, проверьте правильность индексов таблиц данных.',\n        sortMergePasses: 'Количество сортировок слиянием',\n        sortMergePassesHelper: 'Если значение слишком большое, увеличьте \"sort_buffer_size\".',\n        tableLocksWaited: 'Количество блокировок таблиц',\n        tableLocksWaitedHelper:\n            'Если значение слишком большое, рассмотрите возможность увеличения производительности базы данных.',\n        performanceTuning: 'Настройка производительности',\n        optimizationScheme: 'Схема оптимизации',\n        keyBufferSizeHelper: 'Размер буфера для индексов',\n        queryCacheSizeHelper: 'Кэш запросов. Если эта функция отключена, установите этот параметр в 0.',\n        tmpTableSizeHelper: 'Размер кэша временных таблиц',\n        innodbBufferPoolSizeHelper: 'Размер буфера InnoDB',\n        innodbLogBufferSizeHelper: 'Размер буфера журнала InnoDB',\n        sortBufferSizeHelper: '* подключений, размер буфера сортировки на поток',\n        readBufferSizeHelper: '* подключений, размер буфера чтения',\n        readRndBufferSizeHelper: '* подключений, размер буфера случайного чтения',\n        joinBufferSizeHelper: '* подключений, размер кэша таблиц соединений',\n        threadStackelper: '* подключений, размер стека на поток',\n        binlogCacheSizeHelper: '* подключений, размер кэша бинарного журнала (кратно 4096)',\n        threadCacheSizeHelper: 'Размер пула потоков',\n        tableOpenCacheHelper: 'Кэш таблиц',\n        maxConnectionsHelper: 'Максимум подключений',\n        restart: 'Перезапустить',\n        slowLog: 'Медленные запросы',\n        noData: 'Пока нет медленных запросов.',\n        isOn: 'Включено',\n        longQueryTime: 'порог (сек)',\n        thresholdRangeHelper: 'Пожалуйста, введите корректный порог (1 - 600).',\n        timeout: 'Таймаут(сек)',\n        timeoutHelper: 'Период таймаута неактивного подключения. 0 означает, что подключение постоянно активно.',\n        maxclients: 'Макс. клиентов',\n        requirepassHelper:\n            'Оставьте пустым, если пароль не установлен. Изменения нужно сохранить отдельно и перезапустить контейнер!',\n        databases: 'Количество баз данных',\n        maxmemory: 'Максимальное использование памяти',\n        maxmemoryHelper: '0 означает без ограничений.',\n        tcpPort: 'Текущий порт прослушивания.',\n        uptimeInDays: 'Дней в работе.',\n        connectedClients: 'Количество подключенных клиентов.',\n        usedMemory: 'Текущее использование памяти Redis.',\n        usedMemoryRss: 'Размер памяти, запрошенный у операционной системы.',\n        usedMemoryPeak: 'Пиковое потребление памяти Redis.',\n        memFragmentationRatio: 'Коэффициент фрагментации памяти.',\n        totalConnectionsReceived: 'Общее количество подключенных клиентов с момента запуска.',\n        totalCommandsProcessed: 'Общее количество выполненных команд с момента запуска.',\n        instantaneousOpsPerSec: 'Количество команд, выполняемых сервером в секунду.',\n        keyspaceHits: 'Количество успешных поисков ключа в базе данных.',\n        keyspaceMisses: 'Количество неудачных попыток найти ключ в базе данных.',\n        hit: 'Коэффициент попаданий при поиске ключей.',\n        latestForkUsec: 'Количество микросекунд, затраченных на последнюю операцию fork().',\n        redisCliHelper: 'Сервис \"redis-cli\" не обнаружен. Сначала включите сервис.',\n        redisQuickCmd: 'Быстрые команды Redis',\n        recoverHelper: 'Это перезапишет данные с [{0}]. Хотите продолжить?',\n        submitIt: 'Перезаписать данные',\n        baseConf: 'Базовая',\n        allConf: 'Все',\n        restartNow: 'Перезапустить сейчас',\n        restartNowHelper1:\n            'Необходимо перезапустить систему после вступления изменений конфигурации в силу. Если ваши данные требуют сохранения, сначала выполните операцию сохранения.',\n        restartNowHelper: 'Это вступит в силу только после перезапуска системы.',\n        persistence: 'Сохранение',\n        rdbHelper1: 'секунд(ы), вставить',\n        rdbHelper2: 'элементов данных',\n        rdbHelper3: 'Выполнение любого из условий запустит сохранение RDB.',\n        rdbInfo: 'Убедитесь, что значение в списке правил находится в диапазоне от 1 до 100000',\n        containerConn: 'Подключение контейнера',\n        copyConnURL: 'Копировать URL подключения',\n        connAddress: 'Адрес',\n        containerConnHelper:\n            'Этот адрес подключения может использоваться приложениями, работающими в среде выполнения веб-сайта (PHP и т.д.) или контейнере.',\n        remoteConn: 'Внешнее подключение',\n        remoteConnHelper2: 'Используйте этот адрес для неконтейнерных сред или внешних подключений.',\n        remoteConnHelper3:\n            'Адрес доступа по умолчанию - это IP хоста. Для изменения перейдите к пункту конфигурации \"Адрес доступа по умолчанию\" на странице настроек панели.',\n        localIP: 'Локальный IP',\n        localhostHelper:\n            'Если для БД в контейнере указать \"localhost\", внешний доступ будет недоступен. Выбирайте внимательно!',\n        changeConnHelper: 'Это изменит базу данных {0}. Продолжить?',\n    },\n    aiTools: {\n        agents: {\n            agents: 'Агенты',\n            agent: 'Агент',\n            account: 'Аккаунт модели',\n            noAccountHint: 'Выберите существующий аккаунт модели или добавьте новый.',\n            accountCount: '{0} аккаунтов модели',\n            syncAgents: 'Синхронизировать связанные агенты',\n            syncAgentsHelper: 'Обновите openclaw.json для агентов, использующих этот аккаунт модели',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: 'Версия приложения',\n            webuiPort: 'Порт WebUI',\n            allowedOrigins: 'Адреса доступа',\n            allowedOriginsHelper:\n                'Указывайте по одному полному адресу доступа в строке. Рекомендуется использовать HTTPS, например https://192.168.1.2:18789. Если адрес по умолчанию не настроен, введите его вручную.',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: 'Укажите хотя бы один адрес доступа',\n            allowedOriginsInvalid: 'Используйте формат http(s)://host-or-ip[:port]',\n            provider: 'Поставщик моделей',\n            apiKey: 'API ключ',\n            baseUrl: 'Базовый URL',\n            accountModels: 'Model Catalog',\n            accountModelsHelper: 'Configure the models this account exposes to OpenClaw for switching and settings',\n            accountModelsRequired: 'Configure at least one model',\n            accountModelsDuplicate: 'Duplicate models exist in the catalog',\n            modelPool: 'Model Pool',\n            modelPoolHelper:\n                'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',\n            modelInputTypes: 'Input Types',\n            reasoning: 'Reasoning Model',\n            token: 'Токен',\n            manualModel: 'Ручной ввод',\n            verified: 'Проверено',\n            verifySkipped: 'Без проверки',\n            configTitle: 'Configuration',\n            settingsTab: 'Settings',\n            securityTab: 'Security',\n            otherTab: 'Other',\n            timeZone: 'Часовой пояс',\n            browserEnabled: 'Browser Enabled',\n            switchModelSuccess: 'Model switched successfully',\n            channelsTab: 'Channels',\n            wecom: 'WeCom',\n            dingtalk: 'DingTalk',\n            feishu: 'Feishu',\n            pluginNotInstalled: 'Плагин не установлен. Сначала установите его.',\n            dmPolicy: 'DM Policy',\n            groupPolicy: 'Group Policy',\n            policyPairing: 'Pairing',\n            policyAllowlist: 'Allowlist',\n            policyOpen: 'Open',\n            policyDisabled: 'Disabled',\n            botName: 'Bot Name',\n            botId: 'Bot ID',\n            appId: 'App ID',\n            appSecret: 'App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: 'DM Allowlist',\n            allowFromHelper: 'One sender ID per line. Used only when DM Policy is Allowlist.',\n            allowFromPlaceholder: 'One sender ID per line',\n            groupAllowFrom: 'Group Allowlist',\n            groupAllowFromHelper: 'One group ID per line. Used only when Group Policy is Allowlist.',\n            groupAllowFromPlaceholder: 'One group ID per line',\n            allowFromRequired: 'Enter at least one allowlist entry',\n            saveAndRestartGateway: 'Save and restart gateway',\n            pairingCode: 'Pairing Code',\n            pairingCodePlaceholder: 'Enter pairing code',\n            approvePairing: 'Approve Pairing',\n            feishuRequired: 'Please fill botName / appId / appSecret',\n            saveSuccess: 'Saved successfully',\n            pairingCodeRequired: 'Please enter pairing code',\n            pairingApproveSuccess: 'Pairing approved successfully',\n            customProviderHelper: 'Для пользовательского провайдера модели доступность учетной записи не проверяется',\n            feishuSaveSuccess: 'Сохранено в Feishu',\n        },\n        model: {\n            model: 'Модель',\n            localModel: 'Локальные модели',\n            create: 'Добавить модель',\n            create_helper: 'Загрузить \"{0}\"',\n            ollama_doc: 'Вы можете посетить официальный сайт Ollama, чтобы искать и находить больше моделей.',\n            container_conn_helper: 'Используйте этот адрес для доступа или подключения между контейнерами',\n            ollama_sync:\n                'Синхронизация модели Ollama обнаружила, что следующие модели не существуют, хотите удалить их?',\n            from_remote: 'Эта модель не была загружена через 1Panel, нет связанных журналов извлечения.',\n            no_logs: 'Журналы извлечения для этой модели были удалены и не могут быть просмотрены.',\n            vllmVersionHelper: 'Для серверов FusionXpark GB 10 выберите версию -cu130.',\n        },\n        proxy: {\n            proxy: 'Усиление AI-прокси',\n            proxyHelper1: 'Привяжите домен и включите HTTPS для повышения безопасности передачи данных',\n            proxyHelper2: 'Ограничьте доступ по IP, чтобы предотвратить утечку данных в публичной сети',\n            proxyHelper3: 'Включите потоковую передачу',\n            proxyHelper4: 'После создания вы можете просматривать и управлять этим в списке сайтов',\n            proxyHelper5:\n                'После включения вы можете отключить внешний доступ к порту в Магазине приложений - Установленные - Ollama - Параметры для повышения безопасности.',\n            proxyHelper6: 'Чтобы отключить настройку прокси, вы можете удалить её из списка сайтов.',\n            whiteListHelper: 'Ограничить доступ только для IP-адресов из белого списка',\n        },\n        gpu: {\n            gpu: 'Мониторинг GPU',\n            gpuHelper: 'Система не обнаружила команды NVIDIA-SMI или XPU-SMI. Проверьте и повторите попытку!',\n            process: 'Информация о Процессе',\n            type: 'Тип',\n            typeG: 'Графика',\n            typeC: 'Вычисления',\n            typeCG: 'Вычисления+Графика',\n            processName: 'Имя Процесса',\n            shr: 'Общая Память',\n            temperatureHelper: 'Высокая температура GPU может вызвать снижение частоты GPU',\n            gpuUtil: 'Использование GPU',\n            temperature: 'Температура',\n            performanceState: 'Состояние Производительности',\n            powerUsage: 'Потребление Мощности',\n            memoryUsage: 'Использование Памяти',\n            fanSpeed: 'Скорость Вентилятора',\n            power: 'Мощность',\n            powerCurrent: 'Текущая Мощность',\n            powerLimit: 'Лимит Мощности',\n            memory: 'Память',\n            memoryUsed: 'Использованная Память',\n            memoryTotal: 'Общая Память',\n            percent: 'Использование',\n            base: 'Основная Информация',\n            driverVersion: 'Версия Драйвера',\n            cudaVersion: 'Версия CUDA',\n            processMemoryUsage: 'Использование Памяти',\n            performanceStateHelper: 'От P0 (максимальная производительность) до P12 (минимальная производительность)',\n            busID: 'Адрес Шины',\n            persistenceMode: 'Режим Постоянства',\n            enabled: 'Включено',\n            disabled: 'Выключено',\n            persistenceModeHelper:\n                'Режим постоянства быстрее реагирует на задачи, но потребление энергии в режиме ожидания соответственно увеличивается',\n            displayActive: 'Инициализация GPU',\n            displayActiveT: 'Да',\n            displayActiveF: 'Нет',\n            ecc: 'Технология Обнаружения и Исправления Ошибок',\n            computeMode: 'Режим Вычислений',\n            default: 'По Умолчанию',\n            exclusiveProcess: 'Эксклюзивный Процесс',\n            exclusiveThread: 'Эксклюзивный Поток',\n            prohibited: 'Запрещено',\n            defaultHelper: 'По умолчанию: Процессы могут выполняться параллельно',\n            exclusiveProcessHelper:\n                'Эксклюзивный Процесс: Только один контекст CUDA может использовать GPU, но он может быть разделен несколькими потоками',\n            exclusiveThreadHelper: 'Эксклюзивный Поток: Только один поток в контексте CUDA может использовать GPU',\n            prohibitedHelper: 'Запрещено: Параллельное выполнение процессов не разрешено',\n            migModeHelper:\n                'Используется для создания экземпляров MIG, реализуя физическую изоляцию GPU на пользовательском уровне.',\n            migModeNA: 'Не Поддерживается',\n            current: 'Мониторинг в Реальном Времени',\n            history: 'Исторические Записи',\n            notSupport: 'Текущая версия или драйвер не поддерживает отображение этого параметра.',\n            processCount: 'Кол-во процессов',\n        },\n        mcp: {\n            server: 'Сервер MCP',\n            baseUrl: 'Внешний путь доступа',\n            baseUrlHelper: 'Например: http://192.168.1.2:8000',\n            ssePath: 'Путь SSE',\n            ssePathHelper: 'Например: /sse, будьте осторожны, чтобы не дублировать с другими серверами',\n            environment: 'Переменные среды',\n            envKey: 'Имя переменной',\n            envValue: 'Значение переменной',\n            externalUrl: 'Внешний адрес подключения',\n            operatorHelper: 'Будет выполнена операция {1} на {0}, продолжить?',\n            domain: 'Адрес доступа по умолчанию',\n            domainHelper: 'Например: 192.168.1.1 или example.com',\n            bindDomain: 'Привязать сайт',\n            commandPlaceHolder: 'В настоящее время поддерживаются только команды запуска npx и двоичных файлов',\n            importMcpJson: 'Импортировать конфигурацию сервера MCP',\n            importMcpJsonError: 'Структура mcpServers некорректна',\n            bindDomainHelper:\n                'После привязки веб-сайта он изменит адрес доступа для всех установленных серверов MCP и закроет внешний доступ к портам',\n            outputTransport: 'Тип вывода',\n            streamableHttpPath: 'Путь потоковой передачи',\n            streamableHttpPathHelper: 'Например: /mcp, обратите внимание, чтобы не перекрывать другие серверы',\n            npxHelper: 'Подходит для mcp, запущенного с помощью npx или бинарного файла',\n            uvxHelper: 'Подходит для mcp, запущенного с помощью uvx',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: 'Каталог модели',\n            commandHelper: 'Если требуется внешний доступ, установите порт в команде таким же, как порт приложения',\n            imageAlert:\n                'Из-за большого размера образа рекомендуется вручную загрузить образ на сервер перед установкой',\n            modelSpeedup: 'Включить ускорение модели',\n            modelType: 'Тип модели',\n        },\n    },\n    container: {\n        createByCommand: 'Создать с помощью команды',\n        importContainerBackupTip: 'Импорт файла резервной копии контейнера, поддерживается только .tar.gz',\n        importComposeBackupTip: 'Импорт файла резервной копии compose, поддерживается только .tar.gz',\n        stopContainerBeforeBackup: 'Остановить контейнер перед резервным копированием',\n        stopComposeBeforeBackup: 'Остановить compose перед резервным копированием',\n        stopBeforeBackupHelper:\n            'При включении контейнер или сервис compose будет остановлен перед резервным копированием и автоматически восстановлен после завершения для обеспечения целостности данных.',\n        commandInput: 'Ввод команды',\n        commandRule: 'Введите корректную команду docker run для создания контейнера.',\n        commandHelper: 'Эта команда будет выполнена на сервере для создания контейнера. Продолжить?',\n        updateHelper1:\n            'Обнаружено, что этот контейнер происходит из магазина приложений. Обратите внимание на следующие два пункта:',\n        updateHelper2:\n            '1. Текущие изменения не будут синхронизированы с установленными приложениями в магазине приложений.',\n        updateHelper3:\n            '2. Если вы измените приложение на странице установки, текущее отредактированное содержимое станет недействительным.',\n        updateHelper4:\n            'Редактирование контейнера требует пересборки, и все непостоянные данные будут потеряны. Хотите продолжить?',\n        containerList: 'Список контейнеров',\n        operatorHelper: 'Действие {0} будет выполнено для следующего контейнера. Хотите продолжить?',\n        operatorAppHelper:\n            'Операция \"{0}\" будет выполнена для следующего(-их) контейнера(-ов) и может повлиять на работающие сервисы. Хотите продолжить?',\n        containerDeleteHelper:\n            \"Обнаружено, что контейнер создан из Магазина приложений. Удаление контейнера не удалит его полностью из 1Panel. Для полного удаления перейдите в Магазин приложений -> меню 'Установленные' или 'Среда выполнения'. Продолжить?\",\n        start: 'Запустить',\n        stop: 'Остановить',\n        restart: 'Перезапустить',\n        kill: 'Завершить',\n        pause: 'Приостановить',\n        unpause: 'Возобновить',\n        rename: 'Переименовать',\n        remove: 'Удалить',\n        removeAll: 'Удалить все',\n        containerPrune: 'Очистить',\n        containerPruneHelper1: 'Это удалит все контейнеры, которые находятся в остановленном состоянии.',\n        containerPruneHelper2:\n            'Если контейнеры из магазина приложений, вам нужно перейти в \"Магазин приложений -> Установленные\" и нажать кнопку \"Пересобрать\" для их переустановки после выполнения очистки.',\n        containerPruneHelper3: 'Это действие нельзя отменить. Хотите продолжить?',\n        imagePrune: 'Очистить',\n        imagePruneSome: 'Очистить непомеченные',\n        imagePruneSomeEmpty: 'Нет образов с тегом \"none\" для очистки.',\n        imagePruneSomeHelper: 'Очистить образы с тегом \"none\", которые не используются никакими контейнерами.',\n        imagePruneAll: 'Очистить неиспользуемые',\n        imagePruneAllEmpty: 'Нет неиспользуемых образов для очистки.',\n        imagePruneAllHelper: 'Очистить образы, которые не используются никакими контейнерами.',\n        networkPrune: 'Очистить',\n        networkPruneHelper: 'Это удалит все неиспользуемые сети. Хотите продолжить?',\n        volumePrune: 'Очистить',\n        volumePruneHelper: 'Это удалит все неиспользуемые локальные тома. Хотите продолжить?',\n        cleanSuccess: 'Операция успешна, количество очищенных элементов: {0}!',\n        cleanSuccessWithSpace:\n            'Операция успешна. Количество очищенных дисков: {0}. Освобождено дискового пространства: {1}!',\n        unExposedPort: 'Текущий адрес сопоставления портов - 127.0.0.1, что не позволяет внешний доступ.',\n        upTime: 'Время работы',\n        fetch: 'Получить',\n        lines: 'Строки',\n        linesHelper: 'Пожалуйста, введите правильное количество логов для получения!',\n        downloadLinesHelper: 'Выберите или введите количество строк логов для скачивания.',\n        lastDay: 'Последний день',\n        last4Hour: 'Последние 4 часа',\n        lastHour: 'Последний час',\n        last10Min: 'Последние 10 минут',\n        cleanLog: 'Очистить лог',\n        downLogHelper1: 'Это загрузит все логи из контейнера {0}. Хотите продолжить?',\n        downLogHelper2: 'Это загрузит последние {0} логов из контейнера {1}. Хотите продолжить?',\n        cleanLogHelper: 'Это потребует перезапуска контейнера и не может быть отменено. Хотите продолжить?',\n        newName: 'Новое имя',\n        source: 'Использование ресурсов',\n        cpuUsage: 'Использование CPU',\n        cpuTotal: 'Всего CPU',\n        core: 'Ядро',\n        memUsage: 'Использование памяти',\n        memTotal: 'Лимит памяти',\n        memCache: 'Кэш памяти',\n        loadSize: 'Получить Размер Контейнера',\n        ip: 'IP-адрес',\n        cpuShare: 'Доли CPU',\n        cpuShareHelper:\n            'Движок контейнера использует базовое значение 1024 для долей CPU. Вы можете увеличить его, чтобы дать контейнеру больше времени CPU.',\n        inputIpv4: 'Пример: 192.168.1.1',\n        inputIpv6: 'Пример: 2001:0db8:85a3:0000:0000:8a2e:0370:7334',\n        diskUsage: 'Использование Диска',\n        localVolume: 'Локальный Том Хранилища',\n        buildCache: 'Кэш Сборки',\n        usage: 'Использовано: {0}, Можно освободить: {1}',\n        clean: 'Освободить',\n        imageClean: 'Очистка образов удалит все неиспользуемые образы. Это действие нельзя отменить. Продолжить?',\n        containerClean:\n            'Очистка контейнеров удалит все остановленные контейнеры (включая остановленные приложения из Магазина приложений). Это действие нельзя отменить. Продолжить?',\n        sizeRw: 'Размер Слоя Контейнера',\n        sizeRwHelper: 'Размер записываемого слоя, уникального для контейнера',\n        sizeRootFs: 'Виртуальный Размер',\n        sizeRootFsHelper: 'Общий размер всех слоев образа, от которых зависит контейнер + слой контейнера',\n        containerFromAppHelper:\n            'Обнаружено, что этот контейнер происходит из магазина приложений. Операции с приложением могут привести к недействительности текущих изменений.',\n        containerFromAppHelper1:\n            'Нажмите кнопку [Параметры] в списке установленных приложений, чтобы перейти на страницу редактирования и изменить имя контейнера.',\n        command: 'Команда',\n        console: 'Взаимодействие с контейнером',\n        tty: 'Выделить псевдо-TTY (-t)',\n        openStdin: 'Держать STDIN открытым, даже если не подключен (-i)',\n        custom: 'Пользовательский',\n        emptyUser: 'Если пусто, вы войдете как пользователь по умолчанию',\n        privileged: 'Привилегированный',\n        privilegedHelper:\n            'Разрешить контейнеру выполнять определенные привилегированные операции на хосте, что может повысить риски контейнера. Используйте с осторожностью!',\n        upgradeHelper: 'Имя репозитория/Имя образа: Версия образа',\n        upgradeWarning2:\n            'Операция обновления требует пересборки контейнера, все несохраненные данные будут потеряны. Хотите продолжить?',\n        oldImage: 'Текущий образ',\n        sameImageContainer: 'Контейнеры с одинаковым образом',\n        sameImageHelper: 'Контейнеры, использующие один образ, можно массово обновить после выбора',\n        targetImage: 'Целевой образ',\n        imageLoadErr: 'Не обнаружено имя образа для контейнера',\n        imageUpdateTagEmpty: 'Не обнаружены теги образов, доступные для обновления',\n        appHelper: 'Контейнер происходит из магазина приложений, и обновление может сделать сервис недоступным.',\n        input: 'Ручной ввод',\n        forcePull: 'Всегда загружать образ',\n        forcePullHelper:\n            'Это будет игнорировать существующие образы на сервере и загружать последний образ из реестра.',\n        imageUpdateHelper:\n            'Проверить одноимённый тег в реестре и, если есть обновление, загрузить и обновить локальный образ.',\n        server: 'Хост',\n        serverExample: '80, 80-88, ip:80 или ip:80-88',\n        containerExample: '80 или 80-88',\n        exposePort: 'Открыть порт',\n        exposeAll: 'Открыть все',\n        cmdHelper: 'Пример: nginx -g \"daemon off;\"',\n        entrypointHelper: 'Пример: docker-entrypoint.sh',\n        autoRemove: 'Автоудаление',\n        cpuQuota: 'Количество ядер CPU',\n        memoryLimit: 'Память',\n        limitHelper: 'Если установлено 0, это означает отсутствие ограничений. Максимальное значение {0}',\n        mount: 'Монтирование',\n        volumeOption: 'Том',\n        hostOption: 'Хост',\n        serverPath: 'Путь на сервере',\n        containerDir: 'Путь в контейнере',\n        volumeHelper: 'Убедитесь, что содержимое тома хранения корректно',\n        networkEmptyHelper: 'Пожалуйста, подтвердите, что выбор сети контейнера правильный',\n        modeRW: 'Чтение-Запись',\n        modeR: 'Только чтение',\n        sharedLabel: 'Режим Распространения',\n        private: 'Приватный',\n        privateHelper: 'Изменения монтирования в контейнере и хосте не влияют друг на друга',\n        rprivate: 'Рекурсивный Приватный',\n        rprivateHelper: 'Все монтирования в контейнере полностью изолированы от хоста',\n        shared: 'Общий',\n        sharedHelper: 'Изменения монтирования в хосте и контейнере видны друг другу',\n        rshared: 'Рекурсивный Общий',\n        rsharedHelper: 'Все изменения монтирования в хосте и контейнере видны друг другу',\n        slave: 'Подчиненный',\n        slaveHelper:\n            'Контейнер может видеть изменения монтирования хоста, но его собственные изменения не влияют на хост',\n        rslave: 'Рекурсивный Подчиненный',\n        rslaveHelper: 'Все монтирования в контейнере могут видеть изменения хоста, но не влияют на хост',\n        mode: 'Режим',\n        env: 'Переменные окружения',\n        restartPolicy: 'Политика перезапуска',\n        always: 'всегда',\n        unlessStopped: 'если не остановлен',\n        onFailure: 'при сбое (по умолчанию пять раз)',\n        no: 'никогда',\n        refreshTime: 'Интервал обновления',\n        cache: 'Кэш',\n        image: 'Образ | Образы',\n        imagePull: 'Загрузить',\n        imagePullHelper:\n            'Поддерживает выбор нескольких образов для загрузки, нажмите Enter после ввода каждого образа для продолжения',\n        imagePush: 'Отправить',\n        imagePushHelper:\n            'Обнаружено, что у этого образа несколько тегов. Подтвердите, что имя образа, используемое для отправки: {0}',\n        imageDelete: 'Удалить образ',\n        repoName: 'Реестр контейнеров',\n        imageName: 'Имя образа',\n        pull: 'Загрузить',\n        path: 'Путь',\n        importImage: 'Импорт',\n        buildArgs: 'Аргументы Сборки',\n        imageBuild: 'Сборка',\n        pathSelect: 'Путь',\n        label: 'Метка',\n        imageTag: 'Тег образа',\n        imageTagHelper:\n            'Поддерживает установку нескольких тегов образов, нажмите Enter после ввода каждого тега для продолжения',\n        push: 'Отправить',\n        fileName: 'Имя файла',\n        export: 'Экспорт',\n        exportImage: 'Экспорт образа',\n        size: 'Размер',\n        tag: 'Теги',\n        tagHelper: 'По одному в строке. Например,\\nkey1=value1\\nkey2=value2',\n        imageNameHelper: 'Имя образа и тег, например: nginx:latest',\n        cleanBuildCache: 'Очистить кэш сборки',\n        delBuildCacheHelper:\n            'Это удалит все кэшированные артефакты, созданные во время сборки, и не может быть отменено. Хотите продолжить?',\n        urlWarning: 'Префикс URL не должен включать http:// или https://. Пожалуйста, измените.',\n        network: 'Сеть | Сети',\n        networkHelper:\n            'Это может привести к неправильной работе некоторых приложений и сред выполнения. Хотите продолжить?',\n        networkName: 'Имя',\n        driver: 'Драйвер',\n        option: 'Опция',\n        attachable: 'Подключаемая',\n        parentNetworkCard: 'Родительская Сетевая Карта',\n        subnet: 'Подсеть',\n        scope: 'Диапазон IP',\n        gateway: 'Шлюз',\n        auxAddress: 'Исключить IP',\n        volume: 'Том | Тома',\n        volumeDir: 'Директория тома',\n        nfsEnable: 'Включить NFS хранилище',\n        nfsAddress: 'Адрес',\n        mountpoint: 'Точка монтирования',\n        mountpointNFSHelper: 'например, /nfs, /nfs-share',\n        options: 'Опции',\n        repo: 'Реестры',\n        httpRepoHelper: 'Работа с репозиторием HTTP-типа требует перезапуска службы Docker.',\n        httpRepo: 'Выбор HTTP протокола требует перезапуска службы Docker для добавления в небезопасные реестры.',\n        delInsecure: 'Удаление учетных данных',\n        delInsecureHelper: 'Это перезапустит службу Docker для удаления из небезопасных реестров. Хотите продолжить?',\n        downloadUrl: 'Сервер',\n        imageRepo: 'Репозиторий образов',\n        repoHelper: 'Включает ли зеркало репозитория/организации/проекта?',\n        auth: 'Требуется аутентификация',\n        mirrorHelper:\n            'Если есть несколько зеркал, они должны быть разделены новой строкой, например:\\nhttp://xxxxxx.m.daocloud.io \\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper:\n            'Если существует несколько частных репозиториев, они должны быть разделены новой строкой, например:\\n172.16.10.111:8081 \\n172.16.10.112:8081',\n        compose: 'Compose | Composes',\n        composeFile: 'Файл Оркестрации',\n        fromChangeHelper: 'Переключение источника очистит текущее отредактированное содержимое. Хотите продолжить?',\n        composePathHelper: 'Путь сохранения файла конфигурации: {0}',\n        composeHelper:\n            'Композиция, созданная через редактор 1Panel или шаблон, будет сохранена в директории {0}/docker/compose.',\n        deleteFile: 'Удалить файл',\n        deleteComposeHelper:\n            'Удалить все файлы, связанные с compose контейнера, включая файлы конфигурации и постоянные файлы. Пожалуйста, действуйте с осторожностью!',\n        deleteCompose: '\" Удалить эту композицию.',\n        composeDirectory: 'Директория',\n        template: 'Шаблон',\n        composeTemplate: 'Шаблон Compose | Шаблоны Compose',\n        content: 'Содержимое',\n        contentEmpty: 'Содержимое Compose не может быть пустым, пожалуйста, введите и попробуйте снова!',\n        containerNumber: 'Количество контейнеров',\n        containerStatus: 'Статус контейнера',\n        exited: 'Завершен',\n        running: 'Работает ( {0} / {1} )',\n        composeDetailHelper: 'Compose создан вне 1Panel. Операции запуска и остановки не поддерживаются.',\n        composeOperatorHelper: 'Операция {1} будет выполнена для {0}. Хотите продолжить?',\n        composeDownHelper: 'Это остановит и удалит все контейнеры и сети под compose {0}. Хотите продолжить?',\n        composeEnvHelper2:\n            'Эта оркестрация создана Магазином приложений 1Panel. Измените переменные среды в установленных приложениях.',\n        composeContentPlaceholder: 'Введите или вставьте здесь содержимое вашего файла docker-compose',\n        setting: 'Настройка | Настройки',\n        restartHelper: 'Изменения конфигурации вступят в силу после перезапуска службы Docker',\n        operatorStatusHelper: 'Это выполнит \"{0}\" службы Docker. Хотите продолжить?',\n        dockerStatus: 'Служба Docker',\n        daemonJsonPathHelper: 'Убедитесь, что путь конфигурации совпадает с указанным в docker.service.',\n        mirrors: 'Зеркала реестра',\n        mirrorsHelper: '',\n        mirrorsHelper2: 'Подробности см. в официальной документации. ',\n        registries: 'Небезопасные реестры',\n        ipv6Helper:\n            'При включении IPv6 необходимо добавить сеть контейнера IPv6. Конкретные шаги настройки см. в официальной документации.',\n        ipv6CidrHelper: 'Диапазон пула IPv6-адресов для контейнеров',\n        ipv6TablesHelper: 'Автоматическая настройка Docker IPv6 для правил iptables.',\n        experimentalHelper:\n            'Включение ip6tables требует включения этой конфигурации; в противном случае ip6tables будет игнорироваться',\n        cutLog: 'Опция логирования',\n        cutLogHelper1: 'Текущая конфигурация будет влиять только на вновь созданные контейнеры.',\n        cutLogHelper2: 'Существующие контейнеры необходимо пересоздать для применения конфигурации.',\n        cutLogHelper3:\n            'Обратите внимание, что пересоздание контейнеров может привести к потере данных. Если ваши контейнеры содержат важные данные, обязательно сделайте резервную копию перед выполнением операции пересборки.',\n        maxSize: 'Максимальный размер',\n        maxFile: 'Максимум файлов',\n        liveHelper:\n            'По умолчанию, когда демон Docker завершает работу, он останавливает работающие контейнеры. Вы можете настроить демон так, чтобы контейнеры продолжали работать, если демон становится недоступным. Эта функциональность называется live restore. Опция live restore помогает уменьшить время простоя контейнера из-за сбоев демона, плановых простоев или обновлений.',\n        liveWithSwarmHelper: 'Конфигурация демона live-restore несовместима с режимом swarm.',\n        iptablesDisable: 'Отключить iptables',\n        iptablesHelper1: 'Автоматическая настройка правил iptables для Docker.',\n        iptablesHelper2:\n            'Отключение iptables приведет к тому, что контейнеры не смогут взаимодействовать с внешними сетями.',\n        daemonJsonPath: 'Путь конфигурации',\n        serviceUnavailable: 'Служба Docker в настоящее время не запущена.',\n        startIn: ' для запуска',\n        sockPath: 'Unix domain socket',\n        sockPathHelper: 'Канал связи между демоном Docker и клиентом.',\n        sockPathHelper1: 'Путь по умолчанию: /var/run/docker-x.sock',\n        sockPathMsg:\n            'Сохранение настройки Socket Path может привести к недоступности службы Docker. Хотите продолжить?',\n        sockPathErr: 'Пожалуйста, выберите или введите правильный путь к файлу Docker sock',\n        related: 'Связанные',\n        includeAppstore: 'Показывать контейнеры из магазина приложений',\n        excludeAppstore: 'Скрыть контейнер магазина приложений',\n        cleanDockerDiskZone: 'Очистить дисковое пространство, используемое Docker',\n        cleanImagesHelper: '(Очистить все образы, не используемые контейнерами)',\n        cleanContainersHelper: '(Очистить все остановленные контейнеры)',\n        cleanVolumesHelper: '(Очистить все неиспользуемые локальные тома)',\n        makeImage: 'Создать образ',\n        newImageName: 'Новое имя образа',\n        commitMessage: 'Сообщение коммита',\n        author: 'Автор',\n        ifPause: 'Приостановить контейнер во время создания',\n        ifMakeImageWithContainer: 'Создать новый образ из этого контейнера?',\n        finishTime: 'Время последней остановки',\n        workingDir: 'Рабочий каталог',\n        resource: 'Ресурс',\n        macAddr: 'MAC-адрес',\n        exportHelper: 'Экспортировать выбранные образы в один tar-файл.',\n        goSetting: 'Перейти к редактированию',\n    },\n    cronjob: {\n        importHelper:\n            'Повторяющиеся запланированные задачи будут автоматически пропущены при импорте. По умолчанию задачи устанавливаются в статус 【Отключено】, а при аномальной ассоциации данных - в статус 【Ожидает редактирования】.',\n        changeStatus: 'Изменить статус',\n        disableMsg: 'Это остановит автоматическое выполнение запланированной задачи. Хотите продолжить?',\n        enableMsg: 'Это позволит запланированной задаче автоматически выполняться. Хотите продолжить?',\n        taskType: 'Тип',\n        record: 'Записи',\n        viewRecords: 'Записи',\n        shell: 'Shell',\n        stop: 'Ручная Остановка',\n        stopHelper: 'Эта операция принудительно остановит выполнение текущей задачи. Продолжить?',\n        log: 'Логи резервного копирования',\n        logHelper: 'Резервное копирование системного лога',\n        ogHelper1: '1. Системный лог 1Panel',\n        logHelper2: '2. Лог SSH-входов на сервер',\n        logHelper3: '3. Все логи сайта',\n        containerCheckBox: 'В контейнере (не нужно вводить команду контейнера)',\n        containerName: 'Имя контейнера',\n        ntp: 'Синхронизация времени',\n        ntp_helper: 'Вы можете настроить NTP сервер на странице Быстрой настройки в Инструментах.',\n        app: 'Резервное копирование приложения',\n        website: 'Резервное копирование сайта',\n        rulesHelper: 'Поддерживает несколько правил исключения, разделяются английскими запятыми , напр.: *.log,*.sql',\n        lastRecordTime: 'Время последнего выполнения',\n        all: 'Все',\n        failedRecord: 'Неудачные записи',\n        successRecord: 'Успешные записи',\n        database: 'Резервное копирование базы данных',\n        backupArgs: 'Аргументы Резервного Копирования',\n        backupArgsHelper:\n            'Неперечисленные аргументы резервного копирования могут быть введены и выбраны вручную. Например: Введите --no-data и выберите первый вариант из выпадающего списка.',\n        singleTransaction:\n            'Резервное копирование таблиц InnoDB с использованием одной транзакции, подходит для резервного копирования данных большого объема',\n        quick: 'Чтение данных построчно вместо загрузки всей таблицы в память, подходит для резервного копирования данных большого объема и машин с низкой памятью',\n        skipLockTables: 'Резервное копирование без блокировки всех таблиц, подходит для высококонкурентных баз данных',\n        missBackupAccount: 'Не удалось найти учетную запись резервного копирования',\n        syncDate: 'Время синхронизации',\n        clean: 'Очистка кэша',\n        curl: 'Доступ к URL',\n        taskName: 'Имя',\n        cronSpec: 'Цикл запуска',\n        cronSpecDoc:\n            'Пользовательское расписание поддерживает только формат [минута час день месяц неделя], например, 0 0 * * *. Подробности см. в официальной документации.',\n        cronSpecHelper: 'Введите правильный период выполнения',\n        cleanHelper:\n            'Эта операция записывает все записи выполнения задач, файлы резервных копий и файлы логов. Хотите продолжить?',\n        directory: 'Директория резервного копирования',\n        sourceDir: 'Директория резервного копирования',\n        snapshot: 'Снапшот системы',\n        allOptionHelper:\n            'Текущий план задачи - резервное копирование всех [{0}]. Прямое скачивание сейчас не поддерживается. Вы можете проверить список резервных копий в меню [{0}].',\n        exclusionRules: 'Правило исключения',\n        exclusionRulesHelper:\n            'Выберите или введите правила исключения, нажмите Enter после каждого набора для продолжения. Правила исключения будут применяться ко всем операциям сжатия в этой резервной копии',\n        default_download_path: 'Ссылка для скачивания по умолчанию',\n        saveLocal: 'Сохранять локальные резервные копии (столько же, сколько копий в облачном хранилище)',\n        url: 'URL-адрес',\n        urlHelper: 'Пожалуйста, введите действительный URL-адрес',\n        targetHelper: 'Учетные записи резервного копирования управляются в настройках панели.',\n        withImageHelper: 'Резервное копирование образов из магазина приложений увеличит размер файла снимка.',\n        ignoreApp: 'Исключить приложения',\n        withImage: 'Резервная Копия Образа Приложения',\n        retainCopies: 'Сохранять записи',\n        retryTimes: 'Количество повторов',\n        timeout: 'Таймаут',\n        ignoreErr: 'Игнорировать ошибки',\n        ignoreErrHelper:\n            'Игнорировать ошибки во время резервного копирования для выполнения всех задач резервного копирования',\n        retryTimesHelper: '0 означает отсутствие повторов после сбоя',\n        retainCopiesHelper: 'Количество копий для сохранения записей выполнения и логов',\n        retainCopiesHelper1: 'Количество копий для сохранения файлов резервных копий',\n        retainCopiesUnit: ' копий (Просмотр)',\n        cronSpecRule: 'Формат периода выполнения в строке {0} неверен. Пожалуйста, проверьте и попробуйте снова!',\n        perMonthHelper: 'Выполнять {0} числа каждого месяца в {1}:{2}',\n        perWeekHelper: 'Выполнять каждую неделю в {0} в {1}:{2}',\n        perDayHelper: 'Выполнять каждый день в {0}:{1}',\n        perHourHelper: 'Выполнять каждый час в {0} минут',\n        perNDayHelper: 'Выполнять каждые {0} дней в {1}:{2}',\n        perNHourHelper: 'Выполнять каждые {0} часов в {1}',\n        perNMinuteHelper: 'Выполнять каждые {0} минут',\n        perNSecondHelper: 'Выполнять каждые {0} секунд',\n        perMonth: 'Каждый месяц',\n        perWeek: 'Каждую неделю',\n        perHour: 'Каждый час',\n        perNDay: 'Каждые N дней',\n        perDay: 'Каждый день',\n        perNHour: 'Каждые N часов',\n        perNMinute: 'Каждые N минут',\n        perNSecond: 'Каждые N секунд',\n        day: 'дней',\n        dayUnit: 'д',\n        monday: 'Понедельник',\n        tuesday: 'Вторник',\n        wednesday: 'Среда',\n        thursday: 'Четверг',\n        friday: 'Пятница',\n        saturday: 'Суббота',\n        sunday: 'Воскресенье',\n        shellContent: 'Скрипт',\n        errRecord: 'Неправильное логирование',\n        errHandle: 'Сбой выполнения задачи Cron',\n        noRecord: 'Запустите задачу Cron, и вы увидите записи здесь.',\n        cleanData: 'Очистить данные',\n        cleanRemoteData: 'Удалить удалённые данные',\n        cleanDataHelper: 'Удалить файл резервной копии, созданный во время этой задачи.',\n        noLogs: 'Пока нет вывода задачи...',\n        errPath: 'Ошибка пути резервной копии [{0}], невозможно скачать!',\n        cutWebsiteLog: 'Ротация логов сайта',\n        cutWebsiteLogHelper: 'Ротированные файлы логов будут сохранены в директории резервных копий 1Panel.',\n        syncIpGroup: 'Синхронизировать группы IP WAF',\n        requestExpirationTime: 'Время истечения запроса на загрузку (часы)',\n        unitHours: 'Единица: часы',\n        alertTitle: 'Плановая задача - {0} «{1}» Оповещение о сбое задачи',\n        library: {\n            script: 'Скрипт',\n            syncNow: 'Синхронизировать Сейчас',\n            turnOnSync: 'Включить Автосинхронизацию',\n            turnOnSyncHelper:\n                'Включение автосинхронизации будет выполнять автоматическую синхронизацию в ранние утренние часы ежедневно',\n            turnOffSync: 'Отключить Автосинхронизацию',\n            turnOffSyncHelper:\n                'Отключение автосинхронизации может вызвать задержки синхронизации скриптов, подтвердить?',\n            isInteractive: 'Интерактивный',\n            interactive: 'Интерактивный скрипт',\n            interactiveHelper:\n                'Требует ввода пользователя во время выполнения и не может использоваться в запланированных задачах.',\n            library: 'Библиотека скриптов',\n            remoteLibrary: 'Удаленная Библиотека Скриптов',\n            create: 'Добавить скрипт',\n            edit: 'Редактировать скрипт',\n            groupHelper:\n                'Установите разные группы на основе характеристик скрипта, что позволяет быстрее выполнять операции фильтрации скриптов.',\n            handleHelper: 'Выполнить сценарий {1} на {0}, продолжить?',\n            noSuchApp:\n                'Служба {0} не обнаружена. Пожалуйста, сначала быстро установите её, используя библиотеку скриптов!',\n            syncHelper:\n                'Выполнить синхронизацию библиотеки системных скриптов? Это действие затрагивает только системные скрипты. Продолжить?',\n        },\n        nextTime: 'Следующие 5 запусков',\n        logHelper1: '1. Системный лог 1Panel',\n        backupContent: 'Содержимое бэкапа',\n        cronSpecRule2: 'Неверный формат периода выполнения, проверьте и попробуйте снова!',\n        executor: 'Исполнитель',\n        cleanLog: 'Автоочистка логов',\n        cleanLogscope: 'Хранение логов',\n    },\n    monitor: {\n        globalFilter: 'Глобальный Фильтр',\n        enableMonitor: 'Статус Мониторинга',\n        storeDays: 'Дни Хранения',\n        defaultNetwork: 'Сетевая Карта по Умолчанию',\n        defaultNetworkHelper: 'Опция сетевой карты по умолчанию, отображаемая в интерфейсах мониторинга и обзора',\n        defaultIO: 'Диск по Умолчанию',\n        defaultIOHelper: 'Опция диска по умолчанию, отображаемая в интерфейсах мониторинга и обзора',\n        cleanMonitor: 'Очистить Записи Мониторинга',\n        cleanHelper: 'Эта операция очистит все записи мониторинга, включая GPU. Продолжить?',\n        avgLoad: 'Средняя нагрузка',\n        loadDetail: 'Детали нагрузки',\n        resourceUsage: 'Использование',\n        networkCard: 'Сетевой интерфейс',\n        read: 'Чтение',\n        write: 'Запись',\n        readWriteCount: 'Операции ввода/вывода',\n        readWriteTime: 'Задержка ввода/вывода',\n        today: 'Сегодня',\n        yesterday: 'Вчера',\n        lastNDay: 'Последние {0} дней',\n        lastNMonth: 'Последние {0} месяцев',\n        lastHalfYear: 'Последние полгода',\n        memory: 'Память',\n        percent: 'Процент',\n        cache: 'Кэш',\n        disk: 'Диск',\n        network: 'Сеть',\n        up: 'Исходящий',\n        down: 'Входящий',\n        interval: 'Интервал Сбора',\n        intervalHelper: 'Пожалуйста, введите подходящий интервал сбора мониторинга (10 секунд - 12 часов)',\n    },\n    terminal: {\n        local: 'Локальный',\n        defaultConn: 'Соединение по умолчанию',\n        defaultConnHelper:\n            'Эта операция автоматически подключится к узловому терминалу после открытия терминала для 【{0}】. Продолжить?',\n        withReset: 'Сбросить Информацию о Подключении',\n        localConnJump:\n            'Информация о соединении по умолчанию поддерживается в [Терминал - Настройки]. Если соединение не удается, отредактируйте там!',\n        localHelper: 'Локальное имя используется только для локальной идентификации системы.',\n        connLocalErr:\n            'Невозможно выполнить автоматическую аутентификацию, пожалуйста, заполните информацию для входа на локальный сервер！',\n        testConn: 'Проверить подключение',\n        saveAndConn: 'Сохранить и подключиться',\n        connTestOk: 'Информация о подключении доступна',\n        connTestFailed: 'Подключение недоступно, пожалуйста, проверьте информацию подключения.',\n        host: 'Хост | Хосты',\n        createConn: 'Новое подключение',\n        manageGroup: 'Управление группами',\n        noHost: 'Нет хостов',\n        groupChange: 'Изменить группу',\n        expand: 'Развернуть все',\n        fold: 'Свернуть все',\n        batchInput: 'Пакетная обработка',\n        quickCommand: 'Быстрая команда | Быстрые команды',\n        noSuchCommand: 'В импортированном CSV-файле не найдены данные быстрых команд, проверьте и повторите попытку!',\n        quickCommandHelper: 'Вы можете использовать быстрые команды внизу страницы \"Терминалы -> Терминалы\".',\n        groupDeleteHelper:\n            'После удаления группы все подключения в группе будут перемещены в группу по умолчанию. Хотите продолжить?',\n        command: 'Команда',\n        quickCmd: 'Быстрая команда',\n        addHost: 'Добавить',\n        localhost: 'Локальный хост',\n        ip: 'Адрес',\n        authMode: 'Аутентификация',\n        passwordMode: 'Пароль',\n        rememberPassword: 'Запомнить информацию аутентификации',\n        keyMode: 'Приватный ключ',\n        key: 'Приватный ключ',\n        keyPassword: 'Пароль приватного ключа',\n        emptyTerminal: 'В настоящее время нет подключенных терминалов.',\n        lineHeight: 'Высота строки',\n        letterSpacing: 'Межбуквенный интервал',\n        fontSize: 'Размер шрифта',\n        fontFamily: 'Набор шрифтов',\n        fontFamilySupportHelper:\n            'Выберите или введите шрифт. Применение зависит от того, установлен ли шрифт в системе браузера; если нет, будет автоматический fallback. Если пусто, используется шрифт по умолчанию.',\n        backgroundColor: 'Цвет фона',\n        foregroundColor: 'Цвет текста',\n        cursorBlink: 'Мигание курсора',\n        cursorStyle: 'Стиль курсора',\n        cursorUnderline: 'Подчёркивание',\n        cursorBlock: 'Блок',\n        cursorBar: 'Полоса',\n        scrollback: 'Буфер прокрутки',\n        scrollSensitivity: 'Чувствительность прокрутки',\n        aiStatus: 'AI Terminal',\n        aiSettings: 'AI Terminal Settings',\n        aiAccountHelper:\n            'Используйте выбранный модельный аккаунт для генерации и подстановки команд. Для локальных моделей, таких как Ollama и vLLM, используйте пользовательский модельный аккаунт.',\n        aiPrefix: 'Trigger Prefix',\n        aiPrefixHelper:\n            'When a line starts with this prefix and you press Enter, AI command generation will be triggered, for example # or //ai.',\n        aiRiskCommands: 'Risk Command Interception',\n        aiRiskCommandsHelper:\n            'Generated commands matching any of these fragments will be blocked and filled back as comments. Supports add, edit, and delete.',\n        aiAddRiskCommand: 'Add Risk Command',\n        aiRemoveRiskCommand: 'Delete',\n        aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',\n        aiPrefixAsciiVisible:\n            'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',\n        saveHelper: 'Вы уверены, что хотите сохранить текущую конфигурацию терминала?',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: 'Некоторые проблемы с установкой и использованием описаны в',\n        },\n        swap: {\n            swap: 'Раздел Swap',\n            swapHelper1:\n                'Размер swap должен быть в 1-2 раза больше физической памяти, регулируется в зависимости от конкретных требований;',\n            swapHelper2:\n                'Перед созданием файла подкачки убедитесь, что на системном диске достаточно свободного места, так как размер файла подкачки будет занимать соответствующее дисковое пространство;',\n            swapHelper3:\n                'Swap может помочь снизить нагрузку на память, но это только альтернатива. Чрезмерная зависимость от swap может привести к снижению производительности системы. Рекомендуется в первую очередь увеличить память или оптимизировать использование памяти приложениями;',\n            swapHelper4:\n                'Рекомендуется регулярно отслеживать использование swap для обеспечения нормальной работы системы.',\n            swapDeleteHelper:\n                'Эта операция удалит раздел Swap {0}. В целях безопасности системы соответствующий файл не будет удален автоматически. Если требуется удаление, выполните его вручную!',\n            saveHelper: 'Пожалуйста, сначала сохраните текущие настройки!',\n            saveSwap: 'Сохранение текущей конфигурации изменит размер раздела Swap {0} на {1}. Хотите продолжить?',\n            swapMin: 'Минимальный размер раздела - 40 КБ. Пожалуйста, измените и попробуйте снова!',\n            swapMax: 'Максимальное значение для размера раздела - {0}. Пожалуйста, измените и попробуйте снова!',\n            swapOff: 'Минимальный размер раздела - 40 КБ. Установка значения 0 отключит раздел Swap.',\n        },\n        device: {\n            dnsHelper: 'DNS сервер',\n            dnsAlert:\n                'Внимание! Изменение конфигурации файла /etc/resolv.conf будет восстановлено до значений по умолчанию после перезагрузки системы.',\n            dnsHelper1:\n                'При наличии нескольких DNS-записей они должны отображаться с новой строки. Например:\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: 'Разрешение имен хостов',\n            hosts: 'Домен',\n            hostAlert: 'Скрыты закомментированные записи, нажмите кнопку \"Все настройки\" для просмотра или настройки',\n            toolbox: 'Быстрые настройки',\n            hostname: 'Имя хоста',\n            passwd: 'Системный пароль',\n            passwdHelper: 'Вводимые символы не могут включать $ и &',\n            timeZone: 'Часовой пояс',\n            localTime: 'Время сервера',\n            timeZoneChangeHelper: 'Изменение системного часового пояса требует перезапуска службы. Продолжить?',\n            timeZoneHelper:\n                'Если команда \"timedatectl\" не установлена, вы не сможете изменить часовой пояс. Система использует эту команду для изменения часового пояса.',\n            timeZoneCN: 'Пекин',\n            timeZoneAM: 'Лос-Анджелес',\n            timeZoneNY: 'Нью-Йорк',\n            ntpALi: 'Alibaba',\n            ntpGoogle: 'Google',\n            syncSite: 'NTP сервер',\n            hostnameHelper:\n                'Изменение имени хоста зависит от команды \"hostnamectl\". Если команда не установлена, изменение может не удаться.',\n            userHelper:\n                'Имя пользователя зависит от команды \"whoami\" для получения. Если команда не установлена, получение может не удаться.',\n            passwordHelper:\n                'Изменение пароля зависит от команды \"chpasswd\". Если команда не установлена, изменение может не удаться.',\n            hostHelper:\n                'В предоставленном содержимом есть пустое значение. Пожалуйста, проверьте и попробуйте снова после изменения!',\n            dnsCheck: 'Проверить доступность',\n            dnsOK: 'Информация о конфигурации DNS доступна!',\n            dnsTestFailed: 'Информация о конфигурации DNS недоступна.',\n            syncSiteHelper: 'Синхронизировать системное время с {0}?',\n        },\n        fail2ban: {\n            sshPort: 'Прослушивание SSH порта',\n            sshPortHelper: 'Текущий Fail2ban прослушивает порт SSH-подключения хоста',\n            unActive: 'Служба Fail2ban в настоящее время не включена.',\n            operation: 'Вы собираетесь выполнить операцию \"{0}\" над службой Fail2ban. Хотите продолжить?',\n            fail2banChange: 'Изменение конфигурации Fail2ban',\n            ignoreHelper: 'IP-адреса из белого списка будут игнорироваться при блокировке. Хотите продолжить?',\n            bannedHelper: 'IP-адреса из черного списка будут заблокированы сервером. Хотите продолжить?',\n            maxRetry: 'Максимальное количество попыток',\n            banTime: 'Время блокировки',\n            banTimeHelper: 'Время блокировки по умолчанию 10 минут, -1 означает постоянную блокировку',\n            banTimeRule: 'Пожалуйста, введите допустимое время блокировки или -1',\n            banAllTime: 'Постоянная блокировка',\n            findTime: 'Период обнаружения',\n            banAction: 'Действие блокировки',\n            banActionOption: 'Блокировать указанные IP-адреса используя {0}',\n            allPorts: ' (Все порты)',\n            ignoreIP: 'Белый список IP',\n            bannedIP: 'Черный список IP',\n            logPath: 'Путь к логам',\n            logPathHelper: 'По умолчанию /var/log/secure или /var/log/auth.log',\n        },\n        ftp: {\n            ftp: 'FTP аккаунт | FTP аккаунты',\n            notStart: 'Служба FTP в настоящее время не запущена, пожалуйста, сначала запустите её!',\n            operation: 'Это выполнит операцию \"{0}\" над службой FTP. Хотите продолжить?',\n            noPasswdMsg:\n                'Невозможно получить текущий пароль FTP аккаунта, пожалуйста, установите пароль и попробуйте снова!',\n            enableHelper: 'Включение выбранного FTP аккаунта восстановит его права доступа. Хотите продолжить?',\n            disableHelper: 'Отключение выбранного FTP аккаунта отзовет его права доступа. Хотите продолжить?',\n            syncHelper: 'Синхронизировать данные FTP аккаунта между сервером и базой данных. Хотите продолжить?',\n            dirSystem:\n                'Это системный каталог. Его изменение может вызвать сбой системы. Пожалуйста, измените и повторите попытку!',\n            dirHelper: 'Для включения FTP требуется изменение прав доступа к каталогу - выбирайте осторожно',\n            dirMsg: 'Включение FTP изменит права доступа для всего каталога {0}. Продолжить?',\n        },\n        clam: {\n            clam: 'Антивирусное сканирование',\n            cron: 'Запланированное сканирование',\n            cronHelper: 'Профессиональная версия поддерживает функцию запланированного сканирования',\n            specErr: 'Ошибка формата расписания выполнения, пожалуйста, проверьте и попробуйте снова!',\n            disableMsg:\n                'Остановка запланированного выполнения предотвратит автоматический запуск этой задачи сканирования. Хотите продолжить?',\n            enableMsg:\n                'Включение запланированного выполнения позволит этой задаче сканирования запускаться автоматически через регулярные интервалы. Хотите продолжить?',\n            showFresh: 'Показать службу обновления сигнатур',\n            hideFresh: 'Скрыть службу обновления сигнатур',\n            clamHelper:\n                'Минимальная рекомендуемая конфигурация для ClamAV: 3 ГБ ОЗУ или больше, одноядерный процессор с частотой 2.0 ГГц или выше, и не менее 5 ГБ свободного места на жестком диске.',\n            notStart: 'Служба ClamAV в настоящее время не запущена, пожалуйста, сначала запустите её!',\n            removeRecord: 'Удалить файлы отчетов',\n            noRecords: 'Нажмите кнопку \"Запустить\" для начала сканирования, и вы увидите записи здесь.',\n            removeInfected: 'Удалить зараженные файлы',\n            removeInfectedHelper:\n                'Удалить зараженные файлы, обнаруженные во время задачи, для обеспечения безопасности и нормальной работы сервера.',\n            clamCreate: 'Создать правило сканирования',\n            infectedStrategy: 'Стратегия для зараженных файлов',\n            removeHelper: 'Удалить зараженные файлы, выбирайте осторожно!',\n            move: 'Переместить',\n            moveHelper: 'Переместить зараженные файлы в указанную директорию',\n            copyHelper: 'Копировать зараженные файлы в указанную директорию',\n            none: 'Ничего не делать',\n            noneHelper: 'Не предпринимать действий с зараженными файлами',\n            scanDir: 'Директория сканирования',\n            infectedDir: 'Директория зараженных файлов',\n            scanDate: 'Дата сканирования',\n            scanResult: 'Последние строки лога сканирования',\n            tail: 'Строк',\n            infectedFiles: 'Зараженные файлы',\n            log: 'Подробности',\n            clamConf: 'Демон Clam AV',\n            clamLog: '@:toolbox.clam.clamConf логи',\n            freshClam: 'FreshClam',\n            freshClamLog: '@:toolbox.clam.freshClam логи',\n            alertHelper: 'Профессиональная версия поддерживает запланированное сканирование и SMS-оповещения',\n            alertTitle: 'Задача антивирусного сканирования 「{0}」 обнаружила зараженный файл',\n        },\n    },\n    logs: {\n        core: 'Сервис панели',\n        agent: 'Мониторинг узлов',\n        panelLog: 'Логи панели',\n        operation: 'Логи операций',\n        login: 'Логи входа',\n        loginIP: 'IP входа',\n        loginAddress: 'Адрес входа',\n        loginAgent: 'Агент входа',\n        loginStatus: 'Статус',\n        system: 'Системные логи',\n        deleteLogs: 'Очистить логи',\n        resource: 'Ресурс',\n        detail: {\n            dashboard: 'Обзор',\n            ai: 'AI',\n            groups: 'Группы',\n            hosts: 'Хосты',\n            apps: 'Приложения',\n            openresty: 'OpenResty',\n            websites: 'Вебсайты',\n            containers: 'Контейнеры',\n            files: 'Управление файлами',\n            runtimes: 'Среды выполнения',\n            process: 'Управление процессами',\n            toolbox: 'Инструментальный ящик',\n            backups: 'Резервное копирование / Восстановление',\n            tampers: 'Защита от подделки',\n            xsetting: 'Настройки интерфейса',\n            logs: 'Аудит журналов',\n            settings: 'Настройки панели',\n            cronjobs: 'Запланированные задачи',\n            waf: 'WAF',\n            databases: 'Базы данных',\n            licenses: 'лицензии',\n            nodes: 'ноды',\n            commands: 'Быстрые команды',\n        },\n        websiteLog: 'Логи веб-сайта',\n        runLog: 'Логи выполнения',\n        errLog: 'Логи ошибок',\n        task: 'Лог задач',\n        taskName: 'Имя задачи',\n        taskRunning: 'Выполняется',\n    },\n    file: {\n        fileDirNum: '{0} каталогов, {1} файлов,',\n        currentDir: 'Текущий каталог',\n        dir: 'Папка',\n        upload: 'Загрузить',\n        uploadFile: '@:file.upload @.lower:file.file',\n        uploadDirectory: '@:file.upload @.lower:file.dir',\n        download: 'Скачать',\n        fileName: 'Имя файла',\n        search: 'Поиск',\n        mode: 'Права доступа',\n        editPermissions: '@:file.mode',\n        owner: 'Владелец',\n        file: 'Файл',\n        remoteFile: 'Удалённая загрузка',\n        share: 'Поделиться',\n        sync: 'Синхронизация данных',\n        size: 'Размер',\n        updateTime: 'Изменен',\n        rename: 'Переименовать',\n        role: 'Права доступа',\n        info: 'Атрибуты',\n        linkFile: 'Символическая ссылка',\n        batchoperation: 'Пакетная операция',\n        shareList: 'Список общих ресурсов',\n        zip: 'Сжатый',\n        group: 'Группа',\n        path: 'Путь',\n        public: 'Другие',\n        setRole: 'Установить права',\n        link: 'Ссылка на файл',\n        rRole: 'Чтение',\n        wRole: 'Запись',\n        xRole: 'Исполнение',\n        name: 'Имя',\n        compress: 'Сжать',\n        deCompress: 'Распаковать',\n        compressType: 'Формат сжатия',\n        compressDst: 'Путь сжатия',\n        replace: 'Перезаписать существующие файлы',\n        compressSuccess: 'Успешно сжато',\n        deCompressSuccess: 'Успешно распаковано',\n        deCompressDst: 'Путь распаковки',\n        linkType: 'Тип ссылки',\n        softLink: 'Символическая ссылка',\n        hardLink: 'Жесткая ссылка',\n        linkPath: 'Путь ссылки',\n        selectFile: 'Выбрать файл',\n        downloadUrl: 'Удаленный URL',\n        downloadStart: 'Загрузка начата',\n        moveSuccess: 'Успешно перемещено',\n        copySuccess: 'Успешно скопировано',\n        pasteMsg: 'Нажмите кнопку «Вставить» в правом верхнем углу целевой директории',\n        move: 'Переместить',\n        calculate: 'Вычислить',\n        remark: 'Примечание',\n        setRemark: 'Задать примечание',\n        remarkPrompt: 'Введите примечание',\n        remarkPlaceholder: 'Примечание',\n        remarkToggle: 'Примечания',\n        remarkToggleTip: 'Загружать примечания файлов',\n        canNotDeCompress: 'Невозможно распаковать этот файл',\n        uploadSuccess: 'Успешно загружено',\n        downloadProcess: 'Прогресс загрузки',\n        downloading: 'Загрузка...',\n        infoDetail: 'Свойства файла',\n        root: 'Корневая директория',\n        list: 'Список файлов',\n        sub: 'Подпапки',\n        downloadSuccess: 'Успешно скачано',\n        theme: 'Тема',\n        language: 'Язык',\n        eol: 'Конец строки',\n        copyDir: 'Копировать',\n        paste: 'Вставить',\n        changeOwner: 'Изменить пользователя и группу',\n        containSub: 'Применить изменение прав рекурсивно',\n        ownerHelper:\n            'Пользователь по умолчанию для среды PHP: группа пользователей 1000:1000, нормально, что пользователи внутри и снаружи контейнера показывают несоответствия',\n        searchHelper: 'Поддерживает подстановочные знаки, такие как *',\n        uploadFailed: '[{0}] Ошибка загрузки файла',\n        fileUploadStart: 'Загрузка [{0}]...',\n        currentSelect: 'Текущий выбор: ',\n        unsupportedType: 'Неподдерживаемый тип файла',\n        deleteHelper: 'Вы уверены, что хотите удалить следующие файлы? По умолчанию они будут помещены в корзину',\n        fileHelper: 'Примечание:\\n1. Результаты поиска нельзя сортировать.\\n2. Папки нельзя сортировать по размеру.',\n        forceDeleteHelper: 'Удалить файл навсегда (без помещения в корзину, удалить напрямую)',\n        recycleBin: 'Корзина',\n        sourcePath: 'Исходный путь',\n        deleteTime: 'Время удаления',\n        confirmReduce: 'Вы уверены, что хотите восстановить следующие файлы?',\n        reduceSuccess: 'Успешно восстановлено',\n        reduce: 'Восстановление',\n        reduceHelper:\n            'Если файл или директория с таким же именем существует в исходном пути, он будет перезаписан. Хотите продолжить?',\n        clearRecycleBin: 'Очистить',\n        clearRecycleBinHelper: 'Вы хотите очистить корзину?',\n        favorite: 'Избранное',\n        removeFavorite: 'Удалить из избранного?',\n        addFavorite: 'Добавить/Удалить в избранное',\n        clearList: 'Очистить список',\n        deleteRecycleHelper: 'Вы уверены, что хотите навсегда удалить следующие файлы?',\n        typeErrOrEmpty: '[{0}] неверный тип файла или пустая папка',\n        dropHelper: 'Перетащите сюда файлы для загрузки',\n        fileRecycleBin: 'Включить корзину',\n        fileRecycleBinMsg: '{0} корзина',\n        wordWrap: 'Автоматический перенос строк',\n        deleteHelper2: 'Вы уверены, что хотите удалить выбранный файл? Операцию удаления нельзя отменить',\n        ignoreCertificate: 'Разрешить небезопасные подключения к серверу',\n        ignoreCertificateHelper:\n            'Разрешение небезопасных подключений к серверу может привести к утечке или подмене данных. Используйте эту опцию только если доверяете источнику загрузки.',\n        uploadOverLimit: 'Количество файлов превышает 1000! Пожалуйста, сожмите и загрузите',\n        clashDidNotSupport: 'Имена файлов не должны содержать .1panel_clash',\n        clashDeleteAlert: 'Папку \"Корзина\" нельзя удалить',\n        clashOpenAlert: 'Пожалуйста, нажмите кнопку \"Корзина\" чтобы открыть директорию корзины',\n        right: 'Вперед',\n        back: 'Назад',\n        top: 'Вернуться',\n        up: 'Назад',\n        openWithVscode: 'Открыть в VS Code',\n        vscodeHelper: 'Пожалуйста, убедитесь что VS Code установлен локально и настроен плагин SSH Remote',\n        saveContentAndClose: 'Файл был изменен, хотите сохранить и закрыть его?',\n        saveAndOpenNewFile: 'Файл был изменен, хотите сохранить и открыть новый файл?',\n        noEdit: 'Файл не был изменен, не нужно этого делать!',\n        noNameFolder: 'Безымянная папка',\n        noNameFile: 'Безымянный файл',\n        minimap: 'Мини-карта кода',\n        fileCanNotRead: 'Файл не может быть прочитан',\n        previewTruncated: 'Файл слишком большой, отображается только последняя часть',\n        previewEmpty: 'Файл пуст или не является текстовым файлом',\n        previewLargeFile: 'Предпросмотр',\n        panelInstallDir: 'Директорию установки 1Panel нельзя удалить',\n        wgetTask: 'Задача загрузки',\n        existFileTitle: 'Предупреждение о файле с тем же именем',\n        existFileHelper: 'Загруженный файл содержит файл с таким же именем. Заменить его?',\n        existFileSize: 'Размер файла (новый -> старый)',\n        existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!\\n',\n        coverDirHelper: 'При выборе перезаписываемой папки она будет скопирована в целевой путь!',\n        noSuchFile: 'Файл или каталог не найдены. Пожалуйста, проверьте и повторите попытку.',\n        setting: 'настройка',\n        showHide: 'Показывать скрытые файлы',\n        noShowHide: 'Не показывать скрытые файлы',\n        cancelUpload: 'Отменить загрузку',\n        cancelUploadHelper: 'Отменить загрузку или нет, после отмены список загрузок будет очищен.',\n        keepOneTab: 'Необходимо оставить как минимум одну вкладку',\n        notCanTab: 'Невозможно добавить больше вкладок',\n        convert: 'Конвертировать формат',\n        converting: 'Конвертация в',\n        fileCanNotConvert: 'Этот файл не поддерживает конвертацию формата',\n        formatType: 'Тип формата',\n        sourceFormat: 'Исходный формат',\n        sourceFile: 'Исходный файл',\n        saveDir: 'Каталог сохранения',\n        deleteSourceFile: 'Удалить исходный файл',\n        convertHelper: 'Конвертировать выбранные файлы в другой формат',\n        convertHelper1: 'Пожалуйста, выберите файлы для конвертации',\n        execConvert: 'Начать конвертацию. Вы можете просмотреть журналы конвертации в Центре задач',\n        convertLogs: 'Журналы конвертации',\n        formatConvert: 'Конвертация формата',\n    },\n    ssh: {\n        autoStart: 'Автозапуск',\n        enable: 'Включить автозапуск',\n        disable: 'Отключить автозапуск',\n        sshAlert:\n            'Данные списка отсортированы по дате входа. Изменение часового пояса или выполнение других операций может вызвать отклонения в дате логов входа.',\n        sshAlert2:\n            'Вы можете использовать \"Fail2ban\" в \"Инструментах\" для блокировки IP-адресов, пытающихся выполнить брутфорс-атаки, это повысит безопасность хоста.',\n        sshOperate: 'Будет выполнена операция \"{0}\" над службой SSH. Хотите продолжить?',\n        sshChange: 'Настройки SSH',\n        sshChangeHelper: 'Это действие изменит \"{0}\" на \"{1}\". Хотите продолжить?',\n        sshFileChangeHelper:\n            'Изменение конфигурационного файла может повлиять на доступность службы. Будьте осторожны при выполнении этой операции. Хотите продолжить?',\n        port: 'Порт',\n        portHelper: 'Указывает порт, который прослушивает служба SSH.',\n        listenAddress: 'Адрес прослушивания',\n        allV4V6: '0.0.0.0:{0}(IPv4) и :::{0}(IPv6)',\n        listenHelper:\n            'Если оставить пустыми настройки IPv4 и IPv6, будет прослушиваться \"0.0.0.0:{0}(IPv4)\" и \":::{0}(IPv6)\".',\n        addressHelper: 'Указывает адрес, который прослушивает служба SSH.',\n        permitRootLogin: 'Разрешить вход пользователя root',\n        rootSettingHelper: 'Метод входа по умолчанию для пользователя root - \"Разрешить вход по SSH\".',\n        rootHelper1: 'Разрешить вход по SSH',\n        rootHelper2: 'Отключить вход по SSH',\n        rootHelper3: 'Разрешен только вход по ключу',\n        rootHelper4: 'Можно выполнять только предопределенные команды. Другие операции недоступны.',\n        passwordAuthentication: 'Аутентификация по паролю',\n        pwdAuthHelper: 'Включить ли аутентификацию по паролю. Этот параметр включен по умолчанию.',\n        pubkeyAuthentication: 'Аутентификация по ключу',\n        privateKey: 'Приватный ключ',\n        publicKey: 'Публичный ключ',\n        password: 'Пароль',\n        createMode: 'Способ создания',\n        generate: 'Автогенерация',\n        unSyncPass: 'Пароль ключа не может быть синхронизирован',\n        syncHelper:\n            'Операция синхронизации удалит недействительные ключи и синхронизирует новые полные ключевые пары. Продолжить?',\n        input: 'Ручной ввод',\n        import: 'Загрузка файла',\n        authKeys: 'Ключи Авторизации',\n        authKeysHelper: 'Сохранить текущую информацию об открытом ключе?',\n        pubkey: 'Информация о ключе',\n        pubKeyHelper: 'Текущая информация о ключе действительна только для пользователя {0}',\n        encryptionMode: 'Режим шифрования',\n        passwordHelper: 'Может содержать от 6 до 10 цифр и английских букв в разных регистрах',\n        reGenerate: 'Перегенерировать ключ',\n        keyAuthHelper: 'Включить ли аутентификацию по ключу.',\n        useDNS: 'useDNS',\n        dnsHelper: 'Управляет включением функции DNS-разрешения на SSH-сервере для проверки подлинности подключения.',\n        analysis: 'Статистическая информация',\n        denyHelper:\n            'Выполнение операции \"запретить\" для следующих адресов. После установки IP будет запрещен доступ к серверу. Хотите продолжить?',\n        acceptHelper:\n            'Выполнение операции \"разрешить\" для следующих адресов. После установки IP восстановит нормальный доступ. Хотите продолжить?',\n        noAddrWarning: 'В настоящее время не выбраны адреса [{0}]. Пожалуйста, проверьте и попробуйте снова!',\n        loginLogs: 'Журналы SSH',\n        loginMode: 'Режим',\n        authenticating: 'Ключ',\n        publickey: 'Ключ',\n        belong: 'Принадлежность',\n        local: 'Локальный',\n        session: 'Сессия | Сессии',\n        loginTime: 'Время входа',\n        loginIP: 'IP входа',\n        stopSSHWarn: 'Отключить это SSH-соединение',\n        remote: 'Удаленный SSH',\n    },\n    setting: {\n        panel: 'Панель',\n        user: 'Пользователь панели',\n        userChange: 'Изменить пользователя панели',\n        userChangeHelper: 'Изменение пользователя панели приведет к выходу из системы. Продолжить?',\n        passwd: 'Пароль панели',\n        emailHelper: 'Для восстановления пароля',\n        watermark: 'Настройки Водяного Знака',\n        watermarkContent: 'Содержимое Водяного Знака',\n        contentHelper:\n            '{0} представляет имя узла, {1} представляет адрес узла. Вы можете использовать переменные или ввести пользовательские имена.',\n        watermarkColor: 'Цвет Водяного Знака',\n        watermarkFont: 'Размер Шрифта Водяного Знака',\n        watermarkHeight: 'Высота Водяного Знака',\n        watermarkWidth: 'Ширина Водяного Знака',\n        watermarkRotate: 'Угол Поворота',\n        watermarkGap: 'Интервал',\n        watermarkCloseHelper: 'Вы уверены, что хотите отключить настройки системного водяного знака?',\n        watermarkOpenHelper: 'Вы уверены, что хотите сохранить текущие настройки системного водяного знака?',\n        title: 'Псевдоним панели',\n        panelPort: 'Порт панели',\n        titleHelper:\n            'Поддерживаются строки длиной от 3 до 30 символов, включающие буквы, китайские иероглифы, цифры, пробелы и распространённые специальные символы',\n        portHelper:\n            'Рекомендуемый диапазон портов от 8888 до 65535. Примечание: Если на сервере есть группа безопасности, заранее разрешите новый порт в группе безопасности',\n        portChange: 'Изменение порта',\n        portChangeHelper: 'Изменить порт службы и перезапустить службу. Хотите продолжить?',\n        theme: 'Тема',\n        menuTabs: 'Вкладки меню',\n        dark: 'Тёмная',\n        darkGold: 'Тёмное золото',\n        light: 'Светлая',\n        auto: 'Как в системе',\n        language: 'Язык',\n        runtimeEnv: 'Среда выполнения',\n        docSource: 'Источник документации',\n        withByRegion: 'Следовать региону работы (по умолчанию)',\n        withByLang: 'Следовать языку системы',\n        region: 'Регион работы',\n        cn: 'Материковый Китай',\n        intl: 'Глобальный',\n        regionHelper: 'При смене региона будут изменены следующие источники ресурсов:',\n        regionHelper1: 'Установочные пакеты системы',\n        regionHelper2: 'Магазин приложений и библиотека скриптов',\n        regionHelper3: 'Руководство пользователя и сопутствующая документация',\n        regionHelper4: 'Это может повлиять на последующие загрузки и доступ. Действуйте осторожно.',\n        regionTip: 'Регион работы влияет на источник обновления системы и адрес загрузки установочных пакетов.',\n        docSourceTip: 'Источник документации определяет язык перехода для документации и журнала обновлений.',\n        languageHelper: 'По умолчанию следует языку браузера. Этот параметр действует только в текущем браузере',\n        sessionTimeout: 'Время сессии',\n        sessionTimeoutError: 'Минимальное время сессии 300 секунд',\n        sessionTimeoutHelper: 'Панель автоматически выйдет из системы, если не будет операций более {0} секунд.',\n        systemIP: 'Адрес доступа по умолчанию',\n        systemIPHelper:\n            'Перенаправления приложений, доступ к контейнерам и другие функции будут использовать этот адрес для маршрутизации. Каждый узел можно настроить с разным адресом.',\n        proxy: 'Прокси',\n        proxyHelper: 'После настройки прокси-сервера он будет действовать в следующих сценариях:',\n        proxyHelper1:\n            'Загрузка установочного пакета и синхронизация из магазина приложений (только профессиональная версия)',\n        proxyHelper2: 'Обновление системы и получение информации об обновлениях (только профессиональная версия)',\n        proxyHelper4:\n            'Сетевые запросы, такие как загрузка образов Docker, будут выполняться через прокси-сервер (только профессиональная версия)',\n        proxyHelper3: 'Проверка и синхронизация системной лицензии',\n        proxyHelper5: 'Единая загрузка и синхронизация системных скрипт-библиотек (функция Профессиональной версии)',\n        proxyHelper6: 'Подать заявку на сертификат (функция профессиональной версии)',\n        proxyType: 'Тип прокси',\n        proxyUrl: 'Адрес прокси',\n        proxyPort: 'Порт прокси',\n        proxyPasswdKeep: 'Запомнить пароль',\n        proxyDocker: 'Прокси Docker',\n        proxyDockerHelper:\n            'Синхронизировать конфигурацию прокси-сервера с Docker, поддержка офлайн загрузки образов и других операций',\n        syncToNode: 'Синхронизация с дочерним узлом',\n        syncToNodeHelper: 'Синхронизация настроек с другими узлами',\n        nodes: 'Узлы',\n        selectNode: 'Выберите узел',\n        selectNodeError: 'Пожалуйста, выберите узел',\n        apiInterface: 'Включить API',\n        apiInterfaceClose: 'После закрытия API-интерфейсы будут недоступны. Хотите продолжить?',\n        apiInterfaceHelper: 'Разрешить сторонним приложениям доступ к API.',\n        apiInterfaceAlert1:\n            'Не включайте в производственной среде, так как это может повысить риски безопасности сервера.',\n        apiInterfaceAlert2:\n            'Не используйте сторонние приложения для вызова API во избежание потенциальных угроз безопасности.',\n        apiInterfaceAlert3: 'API документация',\n        apiInterfaceAlert4: 'Руководство по использованию',\n        apiKey: 'API ключ',\n        apiKeyHelper: 'API ключ используется сторонними приложениями для доступа к API.',\n        ipWhiteList: 'Белый список IP',\n        ipWhiteListEgs: 'По одному в строке. Например,\\n172.161.10.111\\n172.161.10.0/24',\n        ipWhiteListHelper:\n            'IP-адреса из белого списка могут получить доступ к API, 0.0.0.0/0 (все IPv4), ::/0 (все IPv6)',\n        apiKeyValidityTime: 'Срок действия ключа интерфейса',\n        apiKeyValidityTimeEgs: 'Срок действия ключа интерфейса (в единицах)',\n        apiKeyValidityTimeHelper:\n            'Интерфейс времени метки между текущей меткой времени на момент запроса действителен (в единицах), установлен как 0, не проводится проверка метки времени',\n        apiKeyReset: 'Сброс ключа интерфейса',\n        apiKeyResetHelper:\n            'связанный ключевой сервис станет недействительным. Пожалуйста, добавьте новый ключ к сервису',\n        confDockerProxy: 'Настроить прокси docker',\n        restartNowHelper: 'Настройка прокси Docker требует перезапуска службы Docker.',\n        restartNow: 'Перезапустить немедленно',\n        restartLater: 'Перезагрузить вручную позже',\n        systemIPWarning:\n            'Текущий узел не имеет настроенного адреса доступа по умолчанию. Пожалуйста, перейдите в настройки панели для его настройки!',\n        systemIPWarning1: 'Текущий адрес сервера установлен на {0}, быстрое перенаправление невозможно!',\n        defaultNetwork: 'Сетевой интерфейс',\n        syncTime: 'Время сервера',\n        timeZone: 'Часовой пояс',\n        timeZoneChangeHelper: 'Изменение часового пояса требует перезапуска службы. Хотите продолжить?',\n        timeZoneHelper:\n            'Изменение часового пояса зависит от системной службы timedatectl. Вступит в силу после перезапуска службы 1Panel.',\n        timeZoneCN: 'Пекин',\n        timeZoneAM: 'Лос-Анджелес',\n        timeZoneNY: 'Нью-Йорк',\n        ntpALi: 'Alibaba',\n        ntpGoogle: 'Google',\n        syncSite: 'NTP сервер',\n        syncSiteHelper:\n            'Эта операция будет использовать {0} как источник для синхронизации системного времени. Хотите продолжить?',\n        changePassword: 'Изменить пароль',\n        oldPassword: 'Текущий пароль',\n        newPassword: 'Новый пароль',\n        retryPassword: 'Подтвердите пароль',\n        noSpace: 'Вводимая информация не может включать пробелы',\n        duplicatePassword: 'Новый пароль не может совпадать с текущим, пожалуйста, введите заново!',\n        diskClean: 'Очистка кэша',\n        developerMode: 'Режим предварительного просмотра',\n        developerModeHelper:\n            'Вы сможете опробовать новые функции и исправления до их широкого релиза и оставить ранний отзыв.',\n        thirdParty: 'Сторонние аккаунты',\n        noTypeForCreate: 'В настоящее время не создано типов резервного копирования',\n        LOCAL: 'Диск сервера',\n        OSS: 'Ali OSS',\n        S3: 'Amazon S3',\n        mode: 'Режим',\n        MINIO: 'MinIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'Подключение WebDAV к Alist можно найти в официальной документации',\n        OneDrive: 'Microsoft OneDrive',\n        isCN: 'Китайский интернет',\n        isNotCN: 'Международная версия',\n        client_id: 'ID клиента',\n        client_secret: 'Секрет клиента',\n        redirect_uri: 'URL перенаправления',\n        onedrive_helper: 'Пользовательская конфигурация описана в официальной документации',\n        refreshTime: 'Время обновления токена',\n        refreshStatus: 'Статус обновления токена',\n        backupDir: 'Директория резервных копий',\n        codeWarning: 'Текущий формат кода авторизации неверен, пожалуйста, проверьте еще раз!',\n        code: 'Код авторизации',\n        codeHelper:\n            'Пожалуйста, нажмите кнопку \"Получить\", затем войдите в OneDrive и скопируйте содержимое после \"code\" в перенаправленной ссылке. Вставьте его в это поле ввода. Подробные инструкции смотрите в официальной документации.',\n        googleHelper:\n            'Сначала создайте приложение Google и получите информацию о клиенте, заполните форму и нажмите кнопку получения. Конкретные операции см. в официальной документации.',\n        loadCode: 'Получить',\n        COS: 'Tencent COS',\n        ap_beijing_1: 'Пекин Зона 1',\n        ap_beijing: 'Пекин',\n        ap_nanjing: 'Нанкин',\n        ap_shanghai: 'Шанхай',\n        ap_guangzhou: 'Гуанчжоу',\n        ap_chengdu: 'Чэнду',\n        ap_chongqing: 'Чунцин',\n        ap_shenzhen_fsi: 'Шэньчжэнь Финансовый',\n        ap_shanghai_fsi: 'Шанхай Финансовый',\n        ap_beijing_fsi: 'Пекин Финансовый',\n        ap_hongkong: 'Гонконг, Китай',\n        ap_singapore: 'Сингапур',\n        ap_mumbai: 'Мумбаи',\n        ap_jakarta: 'Джакарта',\n        ap_seoul: 'Сеул',\n        ap_bangkok: 'Бангкок',\n        ap_tokyo: 'Токио',\n        na_siliconvalley: 'Силиконовая долина (США Запад)',\n        na_ashburn: 'Ашберн (США Восток)',\n        na_toronto: 'Торонто',\n        sa_saopaulo: 'Сан-Паулу',\n        eu_frankfurt: 'Франкфурт',\n        KODO: 'Qiniu Kodo',\n        scType: 'Тип хранилища',\n        typeStandard: 'Стандартный',\n        typeStandard_IA: 'Стандартный_IA',\n        typeArchive: 'Архивный',\n        typeDeep_Archive: 'Глубокий_Архив',\n        scLighthouse: 'По умолчанию, Легковесное объектное хранилище поддерживает только этот тип хранилища',\n        scStandard:\n            'Стандартное хранилище подходит для бизнес-сценариев с большим количеством горячих файлов, требующих мгновенного доступа, частого обмена данными и т.д.',\n        scStandard_IA:\n            'Низкочастотное хранилище подходит для бизнес-сценариев с относительно низкой частотой доступа и хранит данные не менее 30 дней.',\n        scArchive: 'Архивное хранилище подходит для бизнес-сценариев с крайне низкой частотой доступа.',\n        scDeep_Archive:\n            'Долговечное холодное хранилище подходит для бизнес-сценариев с крайне низкой частотой доступа.',\n        archiveHelper:\n            'Файлы архивного хранилища нельзя скачать напрямую, сначала их нужно восстановить через веб-сайт соответствующего облачного провайдера. Пожалуйста, используйте с осторожностью!',\n        backupAlert:\n            'Если облачный провайдер совместим с протоколом S3, вы можете напрямую использовать Amazon S3 для резервного копирования.',\n        domain: 'Домен ускорения',\n        backupAccount: 'Аккаунт резервного копирования | Аккаунты резервного копирования',\n        loadBucket: 'Получить корзину',\n        accountName: 'Имя аккаунта',\n        accountKey: 'Ключ аккаунта',\n        address: 'Адрес',\n        path: 'Путь',\n        safe: 'Безопасность',\n        passkey: 'Passkey',\n        passkeyManage: 'Управление',\n        passkeyKeyManagement: 'Управление ключами',\n        passkeyHelper: 'Для быстрого входа можно привязать до 5 passkey',\n        passkeyRequireSSL: 'Для passkey нужно привязать домен и заходить по HTTPS',\n        passkeyTrustedProxies: 'Доверенные прокси',\n        passkeyTrustedProxiesHelper:\n            'Только запросы с этих IP/CIDR будут доверять заголовкам Forwarded и X-Forwarded-Proto при определении HTTPS',\n        passkeyNotSupported: 'Текущий браузер или среда не поддерживает passkey; проверьте домен и HTTPS',\n        passkeyPrereqTitle: 'Для использования Passkey необходимо выполнить следующие условия',\n        passkeyPrereqBindDomain: 'Настроена привязка домена 1Panel',\n        passkeyPrereqHttps: 'Безопасный доступ по HTTPS',\n        passkeyPrereqBrowser: 'Среда поддерживает Passkey (WebAuthn + платформенный аутентификатор)',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable:\n            'Используйте системный браузер и убедитесь, что панель открыта по HTTPS с привязанным доменом.',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            'В этой среде нельзя использовать аутентификацию устройства. Попробуйте системный браузер на более новой версии ОС.',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            'Включите блокировку экрана / отпечаток / Face ID / Windows Hello и повторите попытку.',\n        passkeyPrereqBrowserDetailDetectFailed:\n            'Не удалось выполнить проверку. Обновите страницу и попробуйте снова или перейдите в системный браузер.',\n        passkeyPrereqGoSetup: 'Настроить',\n        passkeyCount: 'Привязано {0}/{1}',\n        passkeyName: 'Название',\n        passkeyNameHelper: 'Введите название для различения устройств',\n        passkeyAdd: 'Добавить passkey',\n        passkeyCreatedAt: 'Создано',\n        passkeyLastUsedAt: 'Последнее использование',\n        passkeyDeleteConfirm: 'После удаления эту passkey нельзя использовать для входа. Продолжить?',\n        passkeyLimit: 'Можно привязать до 5 passkey',\n        passkeyFailed:\n            'Не удалось зарегистрировать passkey, пожалуйста, убедитесь, что сертификат SSL панели является доверенным',\n        bindInfo: 'IP-адрес',\n        bindAll: 'Прослушивать все',\n        bindInfoHelper:\n            'Изменение адреса прослушивания службы или протокола может привести к недоступности службы. Хотите продолжить?',\n        ipv6: 'Прослушивать IPv6',\n        bindAddress: 'IP-адрес прослушивания',\n        entrance: 'Точка входа',\n        showEntrance: 'Показывать уведомление об отключении на странице \"Обзор\"',\n        entranceHelper:\n            'Включение безопасной точки входа позволит входить в панель только через указанную точку входа.',\n        entranceError:\n            'Пожалуйста, введите безопасную точку входа длиной 5-116 символов, поддерживаются только цифры или буквы.',\n        entranceInputHelper: 'Оставьте пустым, чтобы отключить безопасную точку входа.',\n        randomGenerate: 'Случайно',\n        expirationTime: 'Дата истечения',\n        unSetting: 'Не задано',\n        noneSetting: 'Установите срок действия пароля панели. После истечения срока необходимо сбросить пароль',\n        expirationHelper: 'Если срок действия пароля [0] дней, функция истечения срока действия пароля отключена',\n        days: 'Дней до истечения',\n        expiredHelper: 'Текущий пароль истек. Пожалуйста, измените пароль снова.',\n        timeoutHelper:\n            '[ {0} дней ] Срок действия пароля панели скоро истечет. После истечения срока необходимо сбросить пароль',\n        complexity: 'Проверка сложности',\n        complexityHelper:\n            'После включения правило проверки пароля будет: 8-30 символов, включая английские буквы, цифры и как минимум два специальных символа.',\n        bindDomain: 'Привязать домен',\n        unBindDomain: 'Отвязать домен',\n        panelSSL: 'SSL панели',\n        unBindDomainHelper:\n            'Действие по отвязке доменного имени может привести к небезопасности системы. Хотите продолжить?',\n        bindDomainHelper: 'После привязки домена только этот домен сможет получить доступ к службе 1Panel.',\n        bindDomainHelper1: 'Оставьте пустым, чтобы отключить привязку доменного имени.',\n        bindDomainWarning:\n            'После привязки домена вы будете выходить из системы и сможете получить доступ к службе 1Panel только через указанное в настройках доменное имя. Хотите продолжить?',\n        allowIPs: 'Авторизованные IP',\n        unAllowIPs: 'Неавторизованные IP',\n        unAllowIPsWarning:\n            'Авторизация пустого IP позволит всем IP получить доступ к системе, что может привести к небезопасности системы. Хотите продолжить?',\n        allowIPsHelper:\n            'После установки списка авторизованных IP-адресов только IP-адреса из списка смогут получить доступ к службе панели.',\n        allowIPsWarning:\n            'После установки списка авторизованных IP-адресов только IP-адреса из списка смогут получить доступ к службе панели. Хотите продолжить?',\n        allowIPsHelper1: 'Оставьте пустым, чтобы отключить ограничение IP-адресов.',\n        allowIPEgs: 'По одному в строке. Например,\\n172.16.10.111\\n172.16.10.0/24',\n        mfa: '2FA авторизация',\n        mfaClose: 'Отключение MFA снизит безопасность службы. Хотите продолжить?',\n        secret: 'Секрет',\n        mfaInterval: 'Интервал обновления(с)',\n        mfaTitleHelper:\n            'Заголовок используется для различения разных хостов 1Panel. Отсканируйте снова или вручную добавьте секретный ключ после изменения заголовка.',\n        mfaIntervalHelper:\n            'После изменения интервала обновления снова отсканируйте QR-код или вручную добавьте секретный ключ.\\nНекоторые приложения-аутентификаторы поддерживают только интервал 30 секунд (например, Google/Microsoft Authenticator).',\n        mfaAlert:\n            'Одноразовый токен - это динамически генерируемое 6-значное число, основанное на текущем времени. Убедитесь, что время сервера синхронизировано.',\n        mfaHelper: 'После включения потребуется проверка одноразового токена.',\n        mfaHelper1: 'Загрузите приложение-аутентификатор, например,',\n        mfaHelper2:\n            'Чтобы получить одноразовый токен, отсканируйте следующий QR-код с помощью приложения-аутентификатора или скопируйте секретный ключ в приложение аутентификации.',\n        mfaHelper3: 'Введите шесть цифр из приложения',\n        mfaCode: 'Одноразовый токен',\n        sslChangeHelper: 'Изменить настройку https и перезапустить службу. Хотите продолжить?',\n        sslDisable: 'Отключить',\n        sslDisableHelper:\n            'Если служба https отключена, необходимо перезапустить панель, чтобы изменения вступили в силу. Хотите продолжить?',\n        noAuthSetting: 'Unauthorized',\n        noAuthSettingHelper:\n            'Когда пользователи не входят через указанную точку входа безопасности или не получают доступ к панели с указанного IP или доменного имени, этот ответ может скрыть характеристики панели.',\n        responseSetting: 'Настройка ответа',\n        help200: 'Страница помощи',\n        error400: 'Неверный запрос',\n        error401: 'Не авторизован',\n        error403: 'Запрещено',\n        error404: 'Не найдено',\n        error408: 'Тайм-аут запроса',\n        error416: 'Диапазон не удовлетворяется',\n        error444: 'Соединение закрыто',\n        error500: 'Ошибка сервера',\n        https: 'Настройка HTTPS для панели повышает безопасность доступа',\n        strictHelper: 'Трафик без HTTPS не может подключиться к панели',\n        muxHelper:\n            'Панель будет прослушивать протоколы HTTP и HTTPS и перенаправлять HTTP на HTTPS, но это может снизить безопасность панели',\n        certType: 'Тип сертификата',\n        selfSigned: 'Самоподписанный',\n        selfSignedHelper:\n            'Браузеры могут не доверять самоподписанным сертификатам и отображать предупреждения безопасности.',\n        select: 'Выбрать',\n        domainOrIP: 'Домен или IP:',\n        timeOut: 'Тайм-аут',\n        rootCrtDownload: 'Скачать корневой сертификат',\n        primaryKey: 'Закрытый ключ',\n        certificate: 'Сертификат',\n        backupJump:\n            'Файлы резервной копии отсутствуют в текущем списке резервных копий, попробуйте скачать из директории файлов и импортировать для резервного копирования.',\n        snapshot: 'Снапшот | Снапшоты',\n        noAppData: 'Нет доступных системных приложений для выбора',\n        noBackupData: 'Нет доступных данных для резервного копирования',\n        stepBaseData: 'Основные данные',\n        stepAppData: 'Системные приложения',\n        stepPanelData: 'Системные данные',\n        stepBackupData: 'Резервные данные',\n        stepOtherData: 'Другие данные',\n        operationLog: 'Сохранять журнал операций',\n        loginLog: 'Сохранять журнал доступа',\n        systemLog: 'Сохранять системный журнал',\n        taskLog: 'Сохранять журнал задач',\n        monitorData: 'Сохранять данные мониторинга',\n        dockerConf: 'Сохранять Конфигурация Docker',\n        selectAllImage: 'Резервное копирование всех образов приложений',\n        logLabel: 'Журнал',\n        agentLabel: 'Конфигурация узла',\n        appDataLabel: 'Данные приложения',\n        appImage: 'Образ приложения',\n        appBackup: 'Резервная копия приложения',\n        backupLabel: 'Каталог резервных копий',\n        confLabel: 'Конфигурационные файлы',\n        dockerLabel: 'Контейнеры',\n        taskLabel: 'Планировщик задач',\n        resourceLabel: 'Каталог ресурсов приложения',\n        runtimeLabel: 'Среда выполнения',\n        appLabel: 'Приложение',\n        databaseLabel: 'База данных',\n        snapshotLabel: 'Файлы снимков',\n        websiteLabel: 'Веб-сайт',\n        directoryLabel: 'Каталог',\n        appStoreLabel: 'Магазин приложений',\n        shellLabel: 'Скрипт',\n        tmpLabel: 'Временный каталог',\n        sslLabel: 'Каталог сертификатов',\n        reCreate: 'Не удалось создать снимок',\n        reRollback: 'Не удалось откатить снимок',\n        deleteHelper:\n            'Все файлы снапшотов, включая те, что находятся в сторонних аккаунтах резервного копирования, будут удалены. Хотите продолжить?',\n        status: 'Статус снапшота',\n        ignoreRule: 'Правило игнорирования',\n        editIgnoreRule: '@:commons.button.edit @.lower:setting.ignoreRule',\n        ignoreHelper:\n            'Это правило будет использоваться для сжатия и резервного копирования директории данных 1Panel при создании снапшота. По умолчанию игнорируются файлы сокетов.',\n        ignoreHelper1: 'По одному в строке. Например,\\n*.log\\n/opt/1panel/cache',\n        panelInfo: 'Записать базовую информацию 1Panel',\n        panelBin: 'Резервное копирование системных файлов 1Panel',\n        daemonJson: 'Резервное копирование конфигурационного файла Docker',\n        appData: 'Резервное копирование установленных приложений из 1Panel',\n        panelData: 'Резервное копирование директории данных 1Panel',\n        backupData: 'Резервное копирование локальной директории резервных копий для 1Panel',\n        compress: 'Создать файл снапшота',\n        upload: 'Загрузить файл снапшота',\n        recoverDetail: 'Детали восстановления',\n        importHelper: 'Директория снапшот: ',\n        lastRecoverAt: 'Время последнего восстановления',\n        lastRollbackAt: 'Время последнего отката',\n        reDownload: 'Скачать файл резервной копии снова',\n        recoverErrArch: 'Восстановление снапшотов между разными архитектурами серверов не поддерживается!',\n        recoverErrSize:\n            'Обнаружено недостаточно места на диске, пожалуйста, проверьте или очистите и попробуйте снова!',\n        recoverHelper:\n            'Начало восстановления из снапшота {0}, пожалуйста, подтвердите следующую информацию перед продолжением:',\n        recoverHelper1: 'Восстановление требует перезапуска служб Docker и 1Panel',\n        recoverHelper2:\n            'Пожалуйста, убедитесь, что на сервере достаточно места на диске (Размер файла снапшота: {0}, Доступное место: {1})',\n        recoverHelper3:\n            'Пожалуйста, убедитесь, что архитектура сервера соответствует архитектуре сервера, где был создан снапшот (Текущая архитектура сервера: {0})',\n        rollback: 'Откатить',\n        rollbackHelper:\n            'Откат этого восстановления заменит все файлы из этого восстановления и может потребовать перезапуска служб Docker и 1Panel. Хотите продолжить?',\n        upgradeHelper: 'Обновление требует перезапуска службы 1Panel. Хотите продолжить?',\n        rollbackLocalHelper:\n            'Основной узел не поддерживает прямой откат. Пожалуйста, вручную выполните команду [1pctl restore] для отката!',\n        noUpgrade: 'В настоящее время это последняя версия',\n        upgradeNotes: 'Примечания к выпуску',\n        upgradeNow: 'Обновить сейчас',\n        source: 'Источник загрузки',\n        versionNotSame:\n            'Версия узла не совпадает с основной. Пожалуйста, обновите в Управлении узлами перед повторной попыткой.',\n        versionCompare:\n            'Обнаружено, что узел {0} уже имеет последнюю обновляемую версию. Пожалуйста, проверьте версию основного узла и повторите попытку!',\n        about: 'О программе',\n        versionItem: 'Текущая Версия',\n        backupCopies: 'Копии Бэкапа',\n        backupCopiesHelper:\n            'Установите количество копий резервных копий обновления для сохранения для отката версии. 0 означает сохранить все.',\n        backupCopiesRule: 'Пожалуйста, сохраните как минимум 3 записи резервных копий обновления',\n        release: 'Журнал обновлений версий',\n        releaseHelper:\n            'Не удалось получить журнал обновлений для текущей среды. Вы можете вручную проверить официальную документацию.',\n        project: 'GitHub',\n        issue: 'Проблема',\n        doc: 'Официальная документация',\n        star: 'Звезда',\n        description: 'Панель управления Linux сервером',\n        forum: 'Обсуждения',\n        doc2: 'Документация',\n        currentVersion: 'Версия',\n        license: 'Лицензия',\n        bindNode: 'Привязать Узел',\n        menuSetting: 'Настройки меню',\n        menuSettingHelper: 'Если существует только 1 подменю, в панели меню будет отображаться только это подменю',\n        showAll: 'Показать все',\n        hideALL: 'Скрыть все',\n        ifShow: 'Показывать',\n        menu: 'Меню',\n        confirmMessage: 'Страница будет обновлена для обновления списка расширенного меню. Продолжить?',\n        recoverMessage:\n            'Страница будет обновлена, и список меню будет восстановлен до исходного состояния. Продолжить?',\n        compressPassword: 'Пароль сжатия',\n        backupRecoverMessage:\n            'Пожалуйста, введите пароль для сжатия или распаковки (оставьте пустым, чтобы не устанавливать)',\n        componentSize: 'Размер компонента',\n        scope: 'Область',\n        public: 'Публичный',\n        publicHelper:\n            'Публичные аккаунты резервного копирования синхронизируются на все подузлы и могут использоваться совместно',\n        private: 'Приватный',\n        privateHelper:\n            'Приватные аккаунты резервного копирования создаются только на текущем узле и используются только им',\n        UPYUN: 'UPYUN',\n        ALIYUN: 'Aliyun Drive',\n        ALIYUNHelper:\n            'Текущий лимит скачивания Aliyun Drive без клиента — 100MB. Больше можно скачать только через клиент.',\n        ALIYUNRecover:\n            'Текущий лимит скачивания Aliyun Drive без клиента — 100MB. При превышении скачайте через клиент локально, затем синхронизируйте снимок для восстановления.',\n        GoogleDrive: 'Google Drive',\n        analysis: 'Анализ',\n        analysisHelper:\n            'Вставьте весь токен, нужные поля будут разобраны автоматически. Подробности смотрите в официальной документации.',\n        serviceName: 'Имя сервиса',\n        operator: 'Оператор',\n        clickToRefresh: 'Нажмите для обновления',\n        recoverFailed: 'Не удалось восстановить снимок',\n        upgradeRecord: 'История обновлений',\n        upgrading: 'Идет обновление, пожалуйста, подождите...',\n        versionHelper: 'Правило именования: [мажор].[функции].[исправления], пример ниже:',\n        upgradeCheck: 'Проверить обновления',\n        panelSSLHelper:\n            'После автообновления SSL панели нужно вручную перезапустить сервис 1Panel, чтобы изменения вступили в силу.',\n    },\n    license: {\n        offLine: 'Офлайн версия',\n        community: 'OSS',\n        oss: 'Open Source Software',\n        pro: 'Pro',\n        trial: 'Пробная версия',\n        add: 'Добавить Community Edition',\n        licenseBindHelper: 'Бесплатные квоты узлов можно использовать только тогда, когда лицензия привязана к узлу',\n        licenseAlert:\n            'Узлы Community Edition можно добавлять только при правильной привязке лицензии к узлу. Переключение поддерживается только для узлов, правильно привязанных к лицензии.',\n        licenseUnbindHelper:\n            'Обнаружены узлы Community Edition для этой лицензии. Отвяжите лицензию и повторите попытку!',\n        subscription: 'Подписка',\n        perpetual: 'Пожизненная лицензия',\n        versionConstraint: '{0} Выкуп версии',\n        forceUnbind: 'Принудительное отвязывание',\n        forceUnbindHelper:\n            'Принудительное отвязывание будет игнорировать любые ошибки, возникающие в процессе отвязывания, и в конечном итоге освободит привязку лицензии.',\n        updateForce:\n            'Принудительное обновление (игнорировать все ошибки при отвязке для гарантии успешного завершения операции)',\n        trialInfo: 'Версия',\n        authorizationId: 'ID подписки',\n        authorizedUser: 'Авторизованный пользователь',\n        lostHelper:\n            'Лицензия достигла максимального количества попыток повторной проверки. Пожалуйста, вручную нажмите кнопку синхронизации, чтобы убедиться, что функции профессиональной версии работают правильно. Детали: ',\n        disableHelper:\n            'Синхронизация лицензии не удалась. Пожалуйста, вручную нажмите кнопку синхронизации, чтобы убедиться, что функции профессиональной версии работают правильно. Детали: ',\n        quickUpdate: 'Быстрое обновление',\n        power: 'Авторизовать',\n        unbindHelper: 'Все настройки Pro будут очищены после отвязки. Хотите продолжить?',\n        importLicense: 'Лицензия',\n        importHelper: 'Пожалуйста, нажмите или перетащите файл лицензии сюда',\n        technicalAdvice: 'Техническая консультация',\n        advice: 'Консультация',\n        levelUpPro: 'Обновить до Pro',\n        licenseSync: 'Синхронизация лицензии',\n        knowMorePro: 'Узнать больше',\n        closeAlert: 'Текущую страницу можно закрыть в настройках панели',\n        introduce: 'Описание функций',\n        waf: 'Обновление до профессиональной версии предоставляет такие функции, как карта перехватов, логи, записи блокировок, блокировка по географическому положению, пользовательские правила, пользовательские страницы перехвата и т.д.',\n        tamper: 'Обновление до профессиональной версии может защитить веб-сайты от несанкционированных изменений или подделок.',\n        setting:\n            'Обновление до профессиональной версии позволяет настраивать логотип панели, приветственное сообщение и другую информацию.',\n        monitor:\n            'Обновление до профессиональной версии позволяет просматривать статус веб-сайта в реальном времени, тенденции посещений, источники посетителей, логи запросов и другую информацию.',\n        alert: 'Обновление до профессиональной версии позволяет получать информацию о тревогах через SMS и просматривать логи тревог, полностью контролировать различные ключевые события и обеспечивать беспроблемную работу системы',\n        node: 'Обновление до профессиональной редакции позволяет управлять несколькими серверами Linux с помощью 1Panel.',\n        nodeApp:\n            'Обновление до профессиональной редакции позволяет унифицированно обновлять версии приложений на нескольких узлах без необходимости ручного переключения узлов.',\n        nodeDashboard:\n            'Обновление до профессиональной редакции позволяет централизованно управлять приложениями, веб-сайтами, базами данных и запланированными задачами на нескольких узлах.',\n        fileExchange: 'Обновите до Профессиональной версии, чтобы быстро передавать файлы между несколькими серверами.',\n        app: 'Обновите до профессиональной версии, чтобы просматривать информацию о сервисах, мониторинг аномалий и т.д. через мобильное приложение.',\n        cluster:\n            'Обновление до профессиональной версии позволяет управлять кластерами мастер-слейв MySQL/Postgres/Redis.',\n        exceptionalHelper:\n            'Проверка синхронизации лицензии выполнена с ошибкой. Нажмите синхронизацию вручную для корректной работы Pro. Детали: ',\n        tamperHelper: 'Операция не выполнена: для файла или папки включена защита от изменений. Проверьте и повторите!',\n    },\n    clean: {\n        scan: 'Начать сканирование',\n        scanHelper: 'Легко очищайте мусорные файлы, созданные во время работы 1Panel',\n        clean: 'Очистить сейчас',\n        reScan: 'Пересканировать',\n        cleanHelper: 'Выбранные файлы и каталоги нельзя откатить после очистки. Продолжить?',\n        statusSuggest: '(Рекомендуется очистка)',\n        statusClean: '(Очень чисто)',\n        statusEmpty: 'Очень чисто, очистка не требуется!',\n        statusWarning: '(Действуйте с осторожностью)',\n        lastCleanTime: 'Последняя очистка: {0}',\n        lastCleanHelper: 'Очищено файлов и директорий: {0}, всего очищено: {1}',\n        cleanSuccessful: 'Успешно очищено',\n        currentCleanHelper: 'Очищено файлов и директорий в этой сессии: {0}, Всего очищено: {1}',\n        suggest: '(Рекомендуется)',\n        totalScan: 'Всего мусорных файлов для очистки: ',\n        selectScan: 'Всего выбранных мусорных файлов: ',\n        system: 'Системные мусорные файлы',\n        systemHelper: 'Временные файлы, созданные во время снимков, обновлений и других процессов',\n        panelOriginal: 'Файлы резервных копий перед восстановлением снимка системы',\n        upgrade: 'Файлы резервных копий обновления системы',\n        agentPackages: 'Пакеты обновления/установки дочерних узлов исторических версий',\n        upgradeHelper: '(Рекомендуется сохранять последнюю резервную копию обновления для отката системы)',\n        rollback: 'Файлы резервных копий перед восстановлением',\n        backup: 'Резервная Копия Системы',\n        backupHelper: 'Файлы резервных копий, не связанные с локальными учетными записями резервного копирования',\n        unknownBackup: 'Резервная Копия Системы',\n        tmpBackup: 'Временная Резервная Копия',\n        unknownApp: 'Несвязанная Резервная Копия Приложения',\n        unknownDatabase: 'Несвязанная Резервная Копия Базы Данных',\n        unknownWebsite: 'Несвязанная Резервная Копия Веб-сайта',\n        unknownSnapshot: 'Несвязанная Резервная Копия Снимка',\n        upload: 'Временные файлы загрузки',\n        uploadHelper: 'Временные файлы, загруженные из списка системных резервных копий',\n        download: 'Временные файлы скачивания',\n        downloadHelper: 'Временные файлы, скачанные из сторонних аккаунтов резервного копирования системой',\n        directory: 'Директория',\n        systemLog: 'Файл журнала',\n        systemLogHelper: 'Системные журналы, журналы задач, файлы журналов веб-сайта',\n        dockerLog: 'Файлы журналов операций с контейнерами',\n        taskLog: 'Файлы журналов выполнения запланированных задач',\n        shell: 'Запланированные задачи скриптов оболочки',\n        containerShell: 'Запланированные задачи скриптов оболочки, выполняемые внутри контейнеров',\n        curl: 'Запланированные задачи CURL',\n        docker: 'Мусор контейнеров',\n        dockerHelper: 'Файлы, такие как контейнеры, образы, тома, кэш сборки и т.д.',\n        volumes: 'Тома',\n        buildCache: 'Кэш сборки контейнеров',\n        appTmpDownload: 'Временный файл загрузки приложения',\n        unknownWebsiteLog: 'Arquivo de backup de log de site não vinculado',\n        snapshot: 'Временные файлы системного снимка',\n    },\n    app: {\n        app: 'Приложение | Приложения',\n        installName: 'Имя',\n        installed: 'Установленные',\n        all: 'Все',\n        version: 'Версия',\n        detail: 'Детали',\n        params: 'Редактировать',\n        author: 'Автор',\n        source: 'Источник',\n        appName: 'Название приложения',\n        deleteWarn:\n            'Операция удаления удалит все данные и резервные копии. Эту операцию нельзя отменить. Хотите продолжить?',\n        syncSuccess: 'Синхронизация выполнена успешно',\n        canUpgrade: 'Обновления',\n        backupName: 'Имя файла',\n        backupPath: 'Путь к файлу',\n        backupdate: 'Время резервного копирования',\n        versionSelect: 'Пожалуйста, выберите версию',\n        operatorHelper: 'Операция {0} будет выполнена для выбранного приложения. Хотите продолжить?',\n        startOperatorHelper: 'Приложение будет запущено. Хотите продолжить?',\n        stopOperatorHelper: 'Приложение будет остановлено. Хотите продолжить?',\n        restartOperatorHelper: 'Приложение будет перезапущено. Хотите продолжить?',\n        reloadOperatorHelper: 'Приложение будет перезагружено. Хотите продолжить?',\n        checkInstalledWarn: '\"{0}\" не обнаружено. Перейдите в \"Магазин приложений\" для установки.',\n        limitHelper: 'Приложение уже установлено.',\n        deleteHelper: '\"{0}\" связано со следующими ресурсами. Пожалуйста, проверьте и попробуйте снова!',\n        checkTitle: 'Подсказка',\n        defaultConfig: 'Конфигурация по умолчанию',\n        defaultConfigHelper: 'Восстановлено до конфигурации по умолчанию, вступит в силу после сохранения',\n        forceDelete: 'Принудительное удаление',\n        forceDeleteHelper:\n            'Принудительное удаление будет игнорировать ошибки во время процесса удаления и в итоге удалит метаданные.',\n        deleteBackup: 'Удалить резервную копию',\n        deleteBackupHelper: 'Также удалить резервную копию приложения',\n        deleteDB: 'Удалить базу данных',\n        deleteDBHelper: 'Также удалить базу данных',\n        noService: 'Нет {0}',\n        toInstall: 'Перейти к установке',\n        param: 'Параметры',\n        alreadyRun: 'Возраст',\n        syncAppList: 'Синхронизировать',\n        less1Minute: 'Меньше 1 минуты',\n        appOfficeWebsite: 'Официальный сайт',\n        github: 'Github',\n        document: 'Документация',\n        updatePrompt: 'Нет доступных обновлений',\n        installPrompt: 'Пока нет установленных приложений',\n        updateHelper:\n            'Редактирование параметров может привести к сбою запуска приложения. Пожалуйста, действуйте с осторожностью.',\n        updateWarn: 'Обновление параметров требует пересборки приложения. Хотите продолжить?',\n        busPort: 'Порт',\n        syncStart: 'Начало синхронизации! Пожалуйста, обновите магазин приложений позже',\n        advanced: 'Расширенные настройки',\n        cpuCore: 'ядро(а)',\n        containerName: 'Имя контейнера',\n        containerNameHelper: 'Имя контейнера будет автоматически сгенерировано, если не задано',\n        allowPort: 'Внешний доступ',\n        allowPortHelper: 'Разрешение внешнего доступа к порту откроет порт в брандмауэре',\n        appInstallWarn:\n            'Приложение по умолчанию не открывает порт для внешнего доступа. Нажмите \"Расширенные настройки\" для открытия.',\n        upgradeStart: 'Начало обновления! Пожалуйста, обновите страницу позже',\n        toFolder: 'Открыть директорию установки',\n        editCompose: 'Редактировать файл compose',\n        editComposeHelper: 'Редактирование файла compose может привести к сбою установки программного обеспечения',\n        composeNullErr: 'compose не может быть пустым',\n        takeDown: 'Отключить',\n        allReadyInstalled: 'Установлено',\n        installHelper: 'Если есть проблемы с загрузкой образа, настройте ускорение образов.',\n        sortMode: 'Сортировка',\n        installWarn:\n            'Внешний доступ не включен, что делает приложение недоступным через внешние сети. Хотите продолжить?',\n        showIgnore: 'Просмотреть игнорируемые приложения',\n        cancelIgnore: 'Отменить игнорирование',\n        ignoreList: 'Игнорируемые приложения',\n        appHelper: 'Перейдите на страницу приложения, чтобы узнать инструкции и подробности данного приложения.',\n        backupApp: 'Создать резервную копию приложения перед обновлением',\n        backupAppHelper:\n            'Если обновление не удастся, резервная копия будет автоматически восстановлена. Пожалуйста, проверьте причину сбоя в логе аудита-системном логе. Резервная копия по умолчанию сохранит последние 3 копии',\n        openrestyDeleteHelper: 'Принудительное удаление OpenResty удалит все веб-сайты. Хотите продолжить?',\n        downloadLogHelper1: 'Будут загружены все логи приложения {0}. Хотите продолжить?',\n        downloadLogHelper2: 'Будут загружены последние {1} логов приложения {0}. Хотите продолжить?',\n        syncAllAppHelper: 'Все приложения будут синхронизированы. Хотите продолжить?',\n        hostModeHelper:\n            'Текущий режим сети приложения - режим хоста. Если нужно открыть порт, пожалуйста, откройте его вручную на странице брандмауэра.',\n        showLocal: 'Показать локальные приложения',\n        reload: 'Перезагрузить',\n        upgradeWarn:\n            'Обновление приложения заменит файл docker-compose.yml. Если есть изменения, вы можете нажать для просмотра сравнения файлов',\n        openclawHttpsUpgradeNoticeTitle:\n            'Примечание: Следующие инструкции применяются только к пользователям, обновляющим OpenClaw с версий ниже 2026.3.13:',\n        openclawHttpsUpgradeNoticeItem1:\n            'После завершения обновления развернутого агента перейдите в Конфигурация -> Настройки -> Безопасность и вручную добавьте адрес доступа.',\n        openclawHttpsUpgradeNoticeItem2:\n            'Новая версия теперь требует HTTPS для доступа к агенту. Если раньше использовался сайт с обратным прокси, измените адрес назначения прокси на https://IP:Port.',\n        newVersion: 'Новая версия',\n        oldVersion: 'Текущая версия',\n        composeDiff: 'Сравнение файлов',\n        showDiff: 'Просмотреть сравнение',\n        useNew: 'Использовать пользовательскую версию',\n        useDefault: 'Использовать версию по умолчанию',\n        useCustom: 'Настроить docker-compose.yml',\n        useCustomHelper:\n            'Использование пользовательского файла docker-compose.yml может привести к сбою обновления приложения. Если это не необходимо, не отмечайте это.',\n        diffHelper:\n            'Слева старая версия, справа новая версия. После редактирования нажмите для сохранения пользовательской версии',\n        pullImage: 'Загрузить образ',\n        pullImageHelper: 'Выполнить docker pull для загрузки образа перед запуском приложения',\n        deleteImage: 'Удалить изображение',\n        deleteImageHelper:\n            'Удалите изображение, связанное с приложением. Задача не завершится, если удаление не удастся.',\n        requireMemory: 'Требуемая память',\n        supportedArchitectures: 'Поддерживаемые архитектуры',\n        link: 'Ссылка',\n        showCurrentArch: 'Приложения для текущей архитектуры сервера',\n        syncLocalApp: 'Синхронизировать локальное приложение',\n        memoryRequiredHelper: 'Текущее приложение требует {0} памяти',\n        gpuConfig: 'Включить поддержку GPU',\n        gpuConfigHelper:\n            'Убедитесь, что на машине установлен NVIDIA GPU и драйверы NVIDIA, а также NVIDIA Docker Container Toolkit',\n        webUI: 'Веб-адрес доступа',\n        webUIPlaceholder: 'Например: example.com:8080/login',\n        defaultWebDomain: 'Адрес доступа по умолчанию',\n        defaultWebDomainHepler: 'Если порт приложения 8080, то адрес будет http(s)://адрес по умолчанию:8080',\n        webUIConfig:\n            'Текущий узел не имеет настроенного адреса доступа по умолчанию. Пожалуйста, настройте его в параметрах приложения или перейдите в настройки панели!',\n        toLink: 'Перейти',\n        customAppHelper:\n            'Перед установкой пользовательского пакета из магазина приложений убедитесь, что нет установленных приложений.',\n        forceUninstall: 'Принудительное удаление',\n        syncCustomApp: 'Синхронизировать пользовательское приложение',\n        ignoreAll: 'Игнорировать все последующие версии',\n        ignoreVersion: 'Игнорировать указанную версию',\n        specifyIP: 'Привязать IP хоста',\n        specifyIPHelper:\n            'Установите адрес хоста/сетевого интерфейса для привязки порта (если вы не уверены в этом, пожалуйста, не заполняйте)',\n        uninstallDeleteBackup: 'Деинсталляция приложения - Удаление резервной копии',\n        uninstallDeleteImage: 'Деинсталляция приложения - Удаление образа',\n        upgradeBackup: 'Резервное копирование приложения перед обновлением',\n        noAppHelper:\n            'Приложения не обнаружены, пожалуйста, перейдите в центр задач для просмотра журнала синхронизации магазина приложений',\n        isEdirWarn: 'Обнаружено изменение файла docker-compose.yml, пожалуйста, проверьте сравнение',\n    },\n    website: {\n        primaryDomain: 'Основной домен',\n        otherDomains: 'Другие домены',\n        static: 'Статический',\n        deployment: 'Развертывание',\n        supportUpType: 'Поддерживается только формат файла .tar.gz, и сжатый пакет должен содержать файл {0}.json',\n        proxy: 'Обратный прокси',\n        alias: 'Псевдоним',\n        ftpUser: 'FTP аккаунт',\n        ftpPassword: 'FTP пароль',\n        ftpHelper:\n            'После создания веб-сайта будет создан соответствующий FTP-аккаунт, и FTP-директория будет связана с директорией веб-сайта.',\n        remark: 'Примечание',\n        manageGroup: 'Управление группами',\n        groupSetting: 'Управление группами',\n        createGroup: 'Создать группу',\n        appNew: 'Новое приложение',\n        appInstalled: 'Установленное приложение',\n        delete: 'Удалить веб-сайт',\n        deleteApp: 'Удалить приложение',\n        deleteBackup: 'Удалить резервную копию',\n        domain: 'Домен',\n        domainHelper: 'Один домен в строке.\\nПоддерживает wildcard \"*\" и IP-адреса.\\nПоддерживает добавление порта.',\n        addDomain: 'Добавить',\n        domainConfig: 'Домены',\n        defaultDoc: 'Документ',\n        perserver: 'Параллельные подключения',\n        perserverHelper: 'Ограничить максимальное количество параллельных подключений для текущего сайта',\n        perip: 'Один IP',\n        peripHelper: 'Ограничить максимальное количество параллельных подключений с одного IP',\n        rate: 'Ограничения трафика',\n        rateLimit: 'Ограничение скорости на запрос',\n        rateHelper: 'Ограничить поток каждого запроса (единица: КБ)',\n        rateLimitHelper: 'Ограничивает скорость передачи для каждого запроса (единица: КБ/с)',\n        limitHelper: 'Включить контроль потока',\n        other: 'Другое',\n        currentSSL: 'Текущий сертификат',\n        dnsAccount: 'DNS аккаунт',\n        applySSL: 'Заявка на сертификат',\n        SSLList: 'Список сертификатов',\n        createDnsAccount: 'DNS аккаунт',\n        aliyun: 'Aliyun',\n        aliEsa: 'Aliyun ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: 'Ручная настройка',\n        key: 'Ключ',\n        check: 'Просмотр',\n        acmeAccountManage: 'ACME аккаунты',\n        email: 'Email',\n        acmeAccount: 'ACME аккаунт',\n        provider: 'Метод проверки',\n        dnsManual: 'Ручное разрешение',\n        expireDate: 'Дата истечения',\n        brand: 'Организация',\n        deploySSL: 'Развертывание',\n        deploySSLHelper: 'Вы уверены, что хотите развернуть сертификат?',\n        ssl: 'Сертификат | Сертификаты',\n        dnsAccountManage: 'DNS провайдеры',\n        renewSSL: 'Обновить',\n        renewHelper: 'Вы уверены, что хотите обновить сертификат?',\n        renewSuccess: 'Обновить сертификат',\n        enableHTTPS: 'Включить',\n        aliasHelper: 'Псевдоним - это имя директории веб-сайта',\n        lastBackupAt: 'время последнего резервного копирования',\n        null: 'нет',\n        nginxConfig: 'Конфигурация Nginx',\n        websiteConfig: 'Настройки веб-сайта',\n        proxySettings: 'Proxy Ayarları',\n        advancedSettings: 'Gelişmiş Ayarlar',\n        cacheSettings: 'Önbellek Ayarları',\n        sniSettings: 'SNI Ayarları',\n        basic: 'Основные',\n        source: 'Конфигурация',\n        security: 'Безопасность',\n        nginxPer: 'Настройка производительности',\n        neverExpire: 'Никогда',\n        setDefault: 'Установить по умолчанию',\n        default: 'По умолчанию',\n        deleteHelper: 'Статус связанного приложения аномальный, пожалуйста, проверьте',\n        toApp: 'Перейти к списку установленных',\n        cycle: 'Цикл',\n        frequency: 'Частота',\n        ccHelper:\n            'При накоплении более {1} запросов к одному URL в течение {0} секунд срабатывает защита CC и блокируется этот IP',\n        mustSave: 'Изменения нужно сохранить, чтобы они вступили в силу',\n        fileExt: 'расширение файла',\n        fileExtBlock: 'черный список расширений файлов',\n        value: 'значение',\n        enable: 'Включить',\n        proxyAddress: 'Адрес прокси',\n        proxyHelper: 'Пример: 127.0.0.1:8080',\n        forceDelete: 'Принудительное удаление',\n        forceDeleteHelper:\n            'Принудительное удаление будет игнорировать ошибки во время процесса удаления и в итоге удалит метаданные.',\n        deleteAppHelper: 'Удалить связанные приложения и резервные копии приложений одновременно',\n        deleteBackupHelper: 'Также удалить резервные копии веб-сайта.',\n        deleteDatabaseHelper: 'Также удалить базу данных, связанную с веб-сайтом',\n        deleteConfirmHelper:\n            'Операцию удаления нельзя отменить. Введите <span style=\"color:red\"> \"{0}\" </span> для подтверждения удаления.',\n        staticPath: 'Соответствующая основная директория: ',\n        limit: 'Схема',\n        blog: 'Форум/Блог',\n        imageSite: 'Сайт изображений',\n        downloadSite: 'Сайт загрузок',\n        shopSite: 'Магазин',\n        doorSite: 'Портал',\n        qiteSite: 'Корпоративный',\n        videoSite: 'Видео',\n        errLog: 'Лог ошибок',\n        stopHelper:\n            'После остановки сайта он не будет доступен для нормального доступа, и пользователи будут видеть страницу остановки при посещении. Хотите продолжить?',\n        startHelper:\n            'После включения сайта пользователи смогут нормально получить доступ к содержимому сайта, хотите продолжить?',\n        sitePath: 'Директория',\n        siteAlias: 'Псевдоним сайта',\n        primaryPath: 'Корневая директория',\n        folderTitle: 'Веб-сайт в основном содержит следующие папки',\n        wafFolder: 'Правила межсетевого экрана',\n        indexFolder: 'Корневая директория веб-сайта',\n        sslFolder: 'Сертификат веб-сайта',\n        enableOrNot: 'Включить',\n        oldSSL: 'Существующий сертификат',\n        manualSSL: 'Импорт сертификата',\n        select: 'Выбрать',\n        selectSSL: 'Выбрать сертификат',\n        privateKey: 'Ключ (KEY)',\n        certificate: 'Сертификат (формат PEM)',\n        HTTPConfig: 'Опции HTTP',\n        HTTPSOnly: 'Блокировать HTTP запросы',\n        HTTPToHTTPS: 'Перенаправлять на HTTPS',\n        HTTPAlso: 'Разрешить прямые HTTP запросы',\n        sslConfig: 'Опции SSL',\n        disableHTTPS: 'Отключить HTTPS',\n        disableHTTPSHelper: 'Отключение HTTPS удалит конфигурацию, связанную с сертификатом. Хотите продолжить?',\n        SSLHelper:\n            'Примечание: Не используйте SSL-сертификаты для нелегальных веб-сайтов.\\nЕсли после открытия невозможно использовать доступ по HTTPS, проверьте, правильно ли открыт порт 443 в группе безопасности.',\n        SSLConfig: 'Настройки сертификата',\n        SSLProConfig: 'Настройки протокола',\n        supportProtocol: 'Версия протокола',\n        encryptionAlgorithm: 'Алгоритм шифрования',\n        notSecurity: '(небезопасно)',\n        encryptHelper:\n            \"Let's Encrypt имеет ограничение частоты выдачи сертификатов, но его достаточно для нормальных потребностей. Слишком частые операции приведут к сбою выдачи. Конкретные ограничения см. в <a target='_blank' href='https://letsencrypt.org/zh-cn/docs/rate-limits/'>официальной документации</a>\",\n        ipValue: 'Значение',\n        ext: 'расширение файла',\n        wafInputHelper: 'Вводите данные по строкам, одна строка',\n        data: 'данные',\n        ever: 'постоянно',\n        nextYear: 'Через год',\n        noLog: 'Логи не найдены',\n        defaultServer: 'Сайт по умолчанию',\n        noDefaultServer: 'Не установлен',\n        defaultServerHelper:\n            'После установки сайта по умолчанию все несвязанные доменные имена и IP-адреса будут перенаправлены на сайт по умолчанию\\nЭто может эффективно предотвратить вредоносное разрешение\\nОднако это также может привести к сбою блокировки неавторизованных доменных имен WAF',\n        restoreHelper: 'Вы уверены, что хотите восстановить из этой резервной копии?',\n        websiteDeploymentHelper:\n            'Используйте установленное приложение или создайте новое приложение для создания веб-сайта.',\n        websiteStatictHelper: 'Создать директорию веб-сайта на хосте.',\n        websiteProxyHelper:\n            'Использовать обратный прокси для проксирования существующей службы. Например, если служба установлена и работает на порту 8080, адрес прокси будет \"http://127.0.0.1:8080\".',\n        runtimeProxyHelper: 'Использовать среду выполнения веб-сайта для создания веб-сайта.',\n        runtime: 'Среда выполнения',\n        deleteRuntimeHelper:\n            'Приложение среды выполнения необходимо удалять вместе с веб-сайтом, пожалуйста, обращайтесь с этим осторожно',\n        proxyType: 'Тип сети',\n        unix: 'Unix сеть',\n        tcp: 'TCP/IP сеть',\n        phpFPM: 'Конфигурация FPM',\n        phpConfig: 'Конфигурация PHP',\n        updateConfig: 'Обновить конфигурацию',\n        isOn: 'Вкл',\n        isOff: 'Выкл',\n        rewrite: 'Псевдостатика',\n        rewriteMode: 'Схема',\n        current: 'Текущий',\n        rewriteHelper:\n            'Если установка псевдостатики делает веб-сайт недоступным, попробуйте вернуться к настройкам по умолчанию.',\n        runDir: 'Рабочая директория',\n        runUserHelper:\n            'Для веб-сайтов, развернутых через среду выполнения контейнера PHP, необходимо установить владельца и группу пользователей всех файлов и папок в index и поддиректориях на 1000. Для локальной среды PHP обратитесь к локальным настройкам пользователя и группы PHP-FPM',\n        userGroup: 'Пользователь/Группа',\n        uGroup: 'Группа',\n        proxyPath: 'Путь прокси',\n        proxyPass: 'Целевой URL',\n        cache: 'Кэш',\n        cacheTime: 'Длительность кэширования',\n        enableCache: 'Кэш',\n        proxyHost: 'Прокси хост',\n        disabled: 'Остановлен',\n        startProxy: 'Это запустит обратный прокси. Хотите продолжить?',\n        stopProxy: 'Это остановит обратный прокси. Хотите продолжить?',\n        sourceFile: 'Источник',\n        proxyHelper1: 'При доступе к этой директории будет возвращено и отображено содержимое целевого URL.',\n        proxyPassHelper: 'Целевой URL должен быть действительным и доступным.',\n        proxyHostHelper: 'Передать доменное имя в заголовке запроса прокси-серверу.',\n        replacementHelper: 'Можно добавить до 5 замен, оставьте пустым, если замена не требуется.',\n        modifier: 'Правила сопоставления',\n        modifierHelper: 'Пример: \"=\" точное совпадение, \"~\" регулярное совпадение, \"^~\" совпадение начала пути и т.д.',\n        replace: 'Замены текста',\n        replaceHelper:\n            'Функция замены текста в nginx позволяет заменять строки в содержимом ответа при обратном проксировании. Она обычно используется для изменения ссылок, адресов API и т.д. в файлах HTML, CSS, JavaScript и других, возвращаемых бэкендом. Поддерживает сопоставление с регулярными выражениями для сложных потребностей в замене содержимого.',\n        addReplace: 'Добавить',\n        replaced: 'Искомая строка (не может быть пустой)',\n        replaceText: 'Заменить на строку',\n        replacedErr: 'Искомая строка не может быть пустой',\n        replacedErr2: 'Искомая строка не может повторяться',\n        replacedListEmpty: 'Нет правил замены текста',\n        proxySslName: 'Имя прокси SNI',\n        basicAuth: 'Базовая аутентификация',\n        editBasicAuthHelper:\n            'Пароль асимметрично зашифрован и не может быть показан. При редактировании нужно сбросить пароль',\n        antiLeech: 'Анти-лич',\n        extends: 'Расширение',\n        browserCache: 'Кэш',\n        noModify: 'Не изменять',\n        serverCache: 'Кэш сервера',\n        leechLog: 'Записывать лог анти-лича',\n        accessDomain: 'Разрешенные домены',\n        leechReturn: 'Ответ ресурса',\n        noneRef: 'Разрешить пустой referrer',\n        disable: 'не включено',\n        disableLeechHelper: 'Отключить ли анти-лич',\n        disableLeech: 'Отключить анти-лич',\n        ipv6: 'Прослушивать IPv6',\n        leechReturnError: 'Пожалуйста, заполните HTTP код статуса',\n        blockedRef: 'Разрешить нестандартный Referer',\n        accessControl: 'Управление анти-личем',\n        leechcacheControl: 'Управление кэшем',\n        logEnableControl: 'Логировать запросы статических ресурсов',\n        leechSpecialValidHelper:\n            'При включённой опции «Разрешить пустой referrer» запросы без Referer (прямой доступ и т. п.) не блокируются; включение «Разрешить нестандартный Referer» пропускает любой Referer, не начинающийся с http/https (клиентские запросы и т. п.).',\n        leechInvalidReturnHelper: 'HTTP‑код статуса, возвращаемый после блокировки хотлинкинга',\n        leechlogControlHelper:\n            'Записывает запросы к статическим ресурсам; в продакшене обычно отключают, чтобы избежать избыточных и шумных логов',\n        selectAcme: 'Выберите Acme аккаунт',\n        imported: 'Создан вручную',\n        importType: 'Тип импорта',\n        pasteSSL: 'Вставить код',\n        localSSL: 'Выбрать файл сервера',\n        privateKeyPath: 'Файл приватного ключа',\n        certificatePath: 'Файл сертификата',\n        ipWhiteListHelper: 'Роль белого списка IP: все правила недействительны для белого списка IP',\n        redirect: 'Перенаправление',\n        sourceDomain: 'Исходный домен',\n        targetURL: 'Целевой URL адрес',\n        keepPath: 'URI параметры',\n        path: 'путь',\n        redirectType: 'тип перенаправления',\n        redirectWay: 'Способ',\n        keep: 'сохранить',\n        notKeep: 'Не сохранять',\n        redirectRoot: 'Перенаправить на главную страницу',\n        redirectHelper: '301 постоянное перенаправление, 302 временное перенаправление',\n        changePHPVersionWarn:\n            'Переключение версии PHP удалит оригинальный контейнер PHP (смонтированный код веб-сайта не будет потерян), продолжить?',\n        changeVersion: 'Переключить версию',\n        retainConfig: 'Сохранить ли файлы php-fpm.conf и php.ini',\n        runDirHelper2: 'Пожалуйста, убедитесь, что вторичная рабочая директория находится в директории index',\n        openrestyHelper:\n            'OpenResty порт HTTP по умолчанию: {0} порт HTTPS: {1}, что может повлиять на доступ к доменному имени веб-сайта и принудительное перенаправление HTTPS',\n        primaryDomainHelper: 'Пример: example.com или example.com:8080',\n        acmeAccountType: 'Тип аккаунта',\n        keyType: 'Алгоритм ключа',\n        tencentCloud: 'Tencent Cloud',\n        containWarn: 'Доменное имя содержит основной домен, пожалуйста, введите заново',\n        rewriteHelper2:\n            'Приложения типа WordPress, установленные из магазина приложений, обычно поставляются с предустановленной конфигурацией псевдостатики. Их перенастройка может привести к ошибкам.',\n        websiteBackupWarn:\n            'Поддерживается только импорт локальных резервных копий, импорт резервных копий с других машин может привести к сбою восстановления',\n        ipWebsiteWarn:\n            'Веб-сайты с IP в качестве домена должны быть установлены как сайт по умолчанию для нормального доступа.',\n        hstsHelper: 'Включение HSTS может повысить безопасность веб-сайта',\n        includeSubDomains: 'Поддомены',\n        hstsIncludeSubDomainsHelper:\n            'После включения политика HSTS будет применяться ко всем поддоменам текущего домена.',\n        defaultHtml: 'Страница по умолчанию',\n        website404: 'Страница ошибки 404 веб-сайта',\n        domain404: 'Домен веб-сайта не существует',\n        indexHtml: 'Индекс для статического веб-сайта',\n        stopHtml: 'Остановленный веб-сайт',\n        indexPHP: 'Индекс для PHP веб-сайта',\n        sslExpireDate: 'Дата истечения сертификата',\n        website404Helper:\n            'Страница ошибки 404 веб-сайта поддерживается только для веб-сайтов со средой выполнения PHP и статических веб-сайтов',\n        sni: 'Origin SNI',\n        sniHelper:\n            'Когда бэкенд обратного прокси использует HTTPS, может потребоваться установить origin SNI. Подробности см. в документации провайдера CDN.',\n        huaweicloud: 'Huawei Cloud',\n        rcreateDb: 'Создать Базу Данных',\n        enableSSLHelper: 'Неудача при включении SSL не повлияет на создание сайта.',\n        batchAdd: 'Пакетное Добавление Доменов',\n        batchInput: 'Пакетный ввод',\n        domainNotFQDN: 'Этот домен может быть недоступен в публичной сети',\n        domainInvalid: 'Неверный формат домена',\n        domainBatchHelper: 'Один домен на строку, формат: domain:port@ssl\\nПример: example.com:443@ssl или example.com',\n        generateDomain: 'Сгенерировать',\n        global: 'Глобальный',\n        subsite: 'Подсайт',\n        subsiteHelper:\n            'Подсайт может выбрать каталог существующего PHP или статического сайта в качестве корневого каталога.',\n        parentWebsite: 'Родительский Сайт',\n        deleteSubsite: 'Чтобы удалить текущий сайт, сначала необходимо удалить подсайт {0}.',\n        loadBalance: 'Балансировка Нагрузки',\n        server: 'Узел',\n        algorithm: 'Алгоритм',\n        ipHash: 'IP Хэш',\n        ipHashHelper:\n            'Распределяет запросы на определенный сервер на основе IP-адреса клиента, гарантируя, что конкретный клиент всегда направляется на один и тот же сервер.',\n        leastConn: 'Наименьшее Количество Соединений',\n        leastConnHelper: 'Отправляет запросы на сервер с наименьшим количеством активных соединений.',\n        leastTime: 'Наименьшее Время',\n        leastTimeHelper: 'Отправляет запросы на сервер с наименьшим временем активного соединения.',\n        defaultHelper:\n            'Метод по умолчанию, запросы равномерно распределяются между серверами. Если сервер имеет настройку веса, запросы распределяются в соответствии с указанным весом. Серверы с большим весом получают больше запросов.',\n        weight: 'Вес',\n        maxFails: 'Максимальное Количество Ошибок',\n        maxConns: 'Максимальное Количество Соединений',\n        strategy: 'Стратегия',\n        strategyDown: 'Отключить',\n        strategyBackup: 'Резервный',\n        ipHashBackupErr: 'IP хэш не поддерживает резервные узлы',\n        failTimeout: 'Таймаут отказа',\n        failTimeoutHelper:\n            'Длина временного окна для проверки работоспособности сервера. Когда кумулятивное количество отказов достигает порога в течение этого периода, сервер будет временно удален и повторно проверен через тот же промежуток времени. По умолчанию 10 секунд',\n        staticChangePHPHelper: 'В настоящее время статический сайт, можно переключить на PHP сайт.',\n        proxyCache: 'Кэш Обратного Прокси',\n        cacheLimit: 'Ограничение Пространства Кэша',\n        shareCache: 'Размер Памяти для Подсчета Кэша',\n        cacheExpire: 'Время Истечения Кэша',\n        shareCacheHelper: '1M памяти может хранить примерно 8000 объектов кэша.',\n        cacheLimitHelper: 'Превышение лимита автоматически удалит старые кэши.',\n        cacheExpireHelper: 'Кэши, не попавшие в срок истечения, будут удалены.',\n        realIP: 'Реальный IP',\n        ipFrom: 'Источник IP',\n        ipFromHelper:\n            'Настроив доверенные источники IP, OpenResty проанализирует информацию об IP в HTTP-заголовке, чтобы точно идентифицировать и записать реальный IP-адрес посетителя, включая журналы доступа.',\n        ipFromExample1:\n            'Если фронтенд — это инструмент, такой как Frp, вы можете указать IP-адрес Frp, например, 127.0.0.1.',\n        ipFromExample2: 'Если фронтенд — это CDN, вы можете указать диапазон IP-адресов CDN.',\n        ipFromExample3:\n            'Если вы не уверены, вы можете указать 0.0.0.0/0 (IPv4) или ::/0 (IPv6). [Примечание: Разрешение любого источника IP небезопасно.]',\n        http3Helper:\n            'HTTP/3 — это обновленная версия HTTP/2, обеспечивающая более высокую скорость соединения и лучшую производительность. Однако не все браузеры поддерживают HTTP/3, и его включение может привести к тому, что некоторые браузеры не смогут получить доступ к сайту.',\n        cors: 'Межсайтовый обмен ресурсами (CORS)',\n        enableCors: 'Включить CORS',\n        allowOrigins: 'Разрешенные домены',\n        allowMethods: 'Разрешенные методы запроса',\n        allowHeaders: 'Разрешенные заголовки запроса',\n        allowCredentials: 'Разрешить отправку cookies',\n        preflight: 'Быстрый ответ на предварительный запрос',\n        preflightHleper:\n            'При включении, когда браузер отправляет межсайтовый предварительный запрос (запрос OPTIONS), система автоматически вернет статус 204 и установит необходимые межсайтовые заголовки ответа',\n        changeDatabase: 'Сменить Базу Данных',\n        changeDatabaseHelper1: 'Связь базы данных используется для резервного копирования и восстановления сайта.',\n        changeDatabaseHelper2:\n            'Переключение на другую базу данных может сделать предыдущие резервные копии невосстановимыми.',\n        saveCustom: 'Сохранить как Шаблон',\n        rainyun: 'Rainyun',\n        volcengine: 'Volcengine',\n        runtimePortHelper: 'O ambiente de runtime atual possui várias portas. Por favor, selecione uma porta de proxy.',\n        runtimePortWarn: 'В текущей среде выполнения нет портов, невозможно проксировать',\n        cacheWarn: 'Пожалуйста, сначала выключите кэш в обратном прокси',\n        loadBalanceHelper:\n            'После создания балансировки нагрузки, пожалуйста, перейдите в \"Обратный прокси\", добавьте прокси и установите адрес бэкенда на: http://<название балансировки нагрузки>.',\n        favorite: 'Избранное',\n        cancelFavorite: 'Отменить избранное',\n        useProxy: 'Использовать прокси',\n        useProxyHelper: 'Использовать адрес прокси-сервера в настройках панели',\n        westCN: 'Западный цифровой',\n        openBaseDir: 'Предотвращение межсайтовых атак',\n        openBaseDirHelper:\n            'open_basedir используется для ограничения пути доступа к файлам PHP, что помогает предотвратить межсайтовый доступ и повысить безопасность',\n        serverCacheTime: 'Время кеширования на сервере',\n        serverCacheTimeHelper:\n            'Время, в течение которого запрос кешируется на сервере. В этот период идентичные запросы будут возвращать кешированный результат напрямую, без запроса к исходному серверу.',\n        browserCacheTime: 'Время кеширования в браузере',\n        browserCacheTimeHelper:\n            'Время, в течение которого статические ресурсы кешируются локально в браузере, уменьшая повторные запросы. Пользователи будут использовать локальный кеш напрямую, если срок его действия не истек при обновлении страницы.',\n        donotLinkeDB: 'Не связывать с базой данных',\n        toWebsiteDir: 'Перейти в каталог сайта',\n        execParameters: 'Параметры выполнения',\n        extCommand: 'Дополнительная команда',\n        mirror: 'Зеркальный источник',\n        execUser: 'Пользователь выполнения',\n        execDir: 'Каталог выполнения',\n        packagist: 'Полное зеркало Китая',\n        batchOperate: 'Пакетная операция',\n        batchOperateHelper: 'Пакетное {0} веб-сайтов, продолжить операцию?',\n        stream: 'Прокси TCP/UDP',\n        streamPorts: 'Порты прослушивания',\n        streamPortsHelper:\n            'Установите номер внешнего порта прослушивания, клиенты будут получать доступ к службе через этот порт, разделяйте запятыми, например: 5222,5223',\n        streamHelper: 'Перенаправление портов и балансировка нагрузки TCP/UDP',\n        udp: 'Включить UDP',\n        syncHtmlHelper: 'Синхронизировать с PHP и статическими сайтами',\n        wafValueHelper: 'Значение',\n        createDb: 'Создать базу данных',\n        parentWbeiste: 'Родительский сайт',\n        shareCahe: 'Размер памяти под кэш',\n        shareCaheHelper: 'На 1M памяти можно хранить около 8000 кэш-объектов',\n        cacheExpireJHelper: 'Кэш будет удален, если после истечения срока по нему не было обращений',\n    },\n    php: {\n        short_open_tag: 'Поддержка коротких тегов',\n        max_execution_time: 'Максимальное время выполнения скрипта',\n        max_input_time: 'Максимальное время ввода',\n        memory_limit: 'Лимит памяти скрипта',\n        post_max_size: 'Максимальный размер данных POST',\n        file_uploads: 'Разрешить загрузку файлов',\n        upload_max_filesize: 'Максимальный разрешенный размер загружаемых файлов',\n        max_file_uploads: 'Максимальное количество файлов, разрешенных для одновременной загрузки',\n        default_socket_timeout: 'Тайм-аут сокета',\n        error_reporting: 'Уровень ошибок',\n        display_errors: 'Выводить подробную информацию об ошибках',\n        cgi_fix_pathinfo: 'Включить pathinfo',\n        date_timezone: 'Часовой пояс',\n        disableFunction: 'Отключить функции',\n        disableFunctionHelper: 'Введите функции для отключения, например exec, разделяйте запятыми',\n        uploadMaxSize: 'Ограничение загрузки',\n        indexHelper:\n            'Для обеспечения нормальной работы PHP-сайта разместите код в директории index и избегайте переименования',\n        extensions: 'Шаблоны расширений',\n        extension: 'Расширение',\n        extensionHelper: 'Используйте несколько расширений, разделяйте запятыми',\n        toExtensionsList: 'Просмотр списка расширений',\n        containerConfig: 'Конфигурация контейнера',\n        containerConfigHelper:\n            'Переменные окружения и другие данные можно изменить в разделе Конфигурация - Конфигурация контейнера после создания',\n        dateTimezoneHelper: 'Пример: TZ=Asia/Shanghai (Пожалуйста, добавьте по мере необходимости)',\n        extensionsHelper: 'Для нескольких расширений используйте запятую',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: 'Размер хэш-таблицы для имен серверов',\n        clientHeaderBufferSizeHelper: 'Размер буфера заголовка для запросов клиента',\n        clientMaxBodySizeHelper: 'Максимальный размер загружаемого файла',\n        keepaliveTimeoutHelper: 'Тайм-аут соединения',\n        gzipMinLengthHelper: 'Минимальный размер сжатого файла',\n        gzipCompLevelHelper: 'Степень сжатия',\n        gzipHelper: 'Включить сжатие для передачи',\n        connections: 'Активные соединения',\n        accepts: 'Принято',\n        handled: 'Обработано',\n        requests: 'Запросы',\n        reading: 'Чтение',\n        writing: 'Запись',\n        waiting: 'Ожидание',\n        status: 'Текущий статус',\n        configResource: 'Конфигурация',\n        saveAndReload: 'Сохранить и перезагрузить',\n        clearProxyCache: 'Очистить кэш обратного прокси',\n        clearProxyCacheWarn:\n            'Это повлияет на все веб-сайты с настроенным кэшем и перезапустит \"OpenResty\". Хотите продолжить?',\n        create: 'Создать модуль',\n        update: 'Редактировать модуль',\n        params: 'Параметры',\n        packages: 'Пакеты',\n        script: 'Скрипт',\n        module: 'Модуль',\n        build: 'Сборка',\n        buildWarn:\n            'Сборка OpenResty требует резервирования определенного количества CPU и памяти, процесс может занять много времени, пожалуйста, подождите.',\n        mirrorUrl: 'Источник программного обеспечения',\n        paramsHelper: 'Например: --add-module=/tmp/ngx_brotli',\n        packagesHelper: 'Например: git,curl разделенные запятыми',\n        scriptHelper:\n            'Скрипт, выполняемый перед компиляцией, обычно для загрузки исходного кода модуля, установки зависимостей и т.д.',\n        buildHelper:\n            'Нажмите Сборка после добавления/изменения модуля. Успешная сборка автоматически перезапустит OpenResty.',\n        defaultHttps: 'HTTPS Анти-вмешательство',\n        defaultHttpsHelper1: 'Включение этого параметра может решить проблему вмешательства в HTTPS.',\n        sslRejectHandshake: 'Отклонить стандартное SSL-рукопожатие',\n        sslRejectHandshakeHelper:\n            'Включение этого может предотвратить утечку сертификатов, установка веб-сайта по умолчанию сделает эту настройку недействительной',\n    },\n    ssl: {\n        create: 'Запросить',\n        provider: 'Тип',\n        manualCreate: 'Создан вручную',\n        acmeAccount: 'ACME аккаунт',\n        resolveDomain: 'Разрешить доменное имя',\n        err: 'Ошибка',\n        value: 'значение записи',\n        dnsResolveHelper: 'Пожалуйста, добавьте следующие записи разрешения у провайдера DNS:',\n        detail: 'Подробности',\n        msg: 'Информация',\n        ssl: 'Сертификат',\n        key: 'Закрытый ключ',\n        startDate: 'Время начала действия',\n        organization: 'организация-издатель',\n        renewConfirm: 'Это обновит сертификат для доменного имени {0}. Хотите продолжить?',\n        autoRenew: 'Автопродление',\n        autoRenewHelper: 'Автоматически продлевать за 30 дней до истечения срока',\n        renewSuccess: 'Успешно продлено',\n        renewWebsite:\n            'Этот сертификат связан со следующими веб-сайтами, и заявка будет применена к этим сайтам одновременно',\n        createAcme: 'Создать аккаунт',\n        acmeHelper: 'Acme аккаунт используется для запроса бесплатных сертификатов',\n        upload: 'Импорт',\n        applyType: 'Тип',\n        apply: 'Продлить',\n        applyStart: 'Начало запроса сертификата',\n        getDnsResolve: 'Получение значения DNS-разрешения, пожалуйста, подождите...',\n        selfSigned: 'Самоподписанный CA',\n        ca: 'Центр сертификации',\n        commonName: 'Общее имя',\n        caName: 'Имя центра сертификации',\n        company: 'Название организации',\n        department: 'Название подразделения',\n        city: 'Название населенного пункта',\n        province: 'Название штата или области',\n        country: 'Название страны (2-буквенный код)',\n        commonNameHelper: 'Например, ',\n        selfSign: 'Выпустить сертификат',\n        days: 'срок действия',\n        domainHelper: 'Одно доменное имя в строке, поддерживает * и IP-адрес',\n        pushDir: 'Отправить сертификат в локальную директорию',\n        dir: 'Директория',\n        pushDirHelper: 'В этой директории будут созданы файл сертификата \"fullchain.pem\" и файл ключа \"privkey.pem\".',\n        organizationDetail: 'Детали организации',\n        fromWebsite: 'С веб-сайта',\n        dnsMauanlHelper:\n            'В режиме ручного разрешения необходимо нажать кнопку применить после создания для получения значения DNS-разрешения',\n        httpHelper:\n            'Использование режима HTTP требует установки OpenResty и не поддерживает запрос сертификатов с подстановочными доменными именами.',\n        buypassHelper: 'Buypass недоступен в материковом Китае',\n        googleHelper: 'Как получить EAB HmacKey и EAB kid',\n        googleCloudHelper: 'Google Cloud API недоступен в большинстве регионов материкового Китая',\n        skipDNSCheck: 'Пропустить проверку DNS',\n        skipDNSCheckHelper: 'Отметьте здесь только если возникает проблема тайм-аута при запросе сертификата.',\n        cfHelper: 'Не использовать Global API Key',\n        deprecated: 'будет устарелым',\n        deprecatedHelper:\n            'Обслуживание остановлено и может быть удалено в будущей версии. Пожалуйста, используйте метод Tencent Cloud для анализа',\n        disableCNAME: 'Отключить CNAME',\n        disableCNAMEHelper: 'Отметьте здесь, если доменное имя имеет запись CNAME и запрос не удается.',\n        nameserver: 'DNS сервер',\n        nameserverHelper: 'Использовать пользовательский DNS сервер для проверки доменных имен.',\n        edit: 'Редактировать сертификат',\n        execShell: 'Выполнить скрипт после запроса сертификата.',\n        shell: 'Содержимое скрипта',\n        shellHelper:\n            'Директория выполнения скрипта по умолчанию - директория установки 1Panel. Если сертификат отправляется в локальную директорию, директорией выполнения будет директория отправки сертификата. Тайм-аут выполнения по умолчанию - 30 минут.',\n        customAcme: 'Пользовательская служба ACME',\n        customAcmeURL: 'URL службы ACME',\n        baiduCloud: 'Baidu Cloud',\n        pushNode: 'Синхронизация с другими узлами',\n        pushNodeHelper: 'Отправить на выбранные узлы после заявки/продления',\n        fromMaster: 'Отправка с главного узла',\n        hostedZoneID: 'Hosted Zone ID',\n        isIP: 'IP-сертификат',\n        useEAB: 'Использовать аутентификацию EAB',\n    },\n    firewall: {\n        create: 'Создать правило',\n        edit: 'Редактировать правило',\n        ccDeny: 'CC защита',\n        ipWhiteList: 'Белый список IP',\n        ipBlockList: 'Черный список IP',\n        fileExtBlockList: 'Черный список расширений файлов',\n        urlWhiteList: 'Белый список URL',\n        urlBlockList: 'Черный список URL',\n        argsCheck: 'Проверка GET параметров',\n        postCheck: 'Проверка POST параметров',\n        cookieBlockList: 'Черный список Cookie',\n        dockerHelper:\n            'Текущий брандмауэр не может отключить сопоставление портов контейнера. Установленные приложения могут перейти на страницу [Установленные], чтобы редактировать параметры приложения и настраивать правила открытия портов.',\n        iptablesHelper:\n            'Обнаружено, что система использует брандмауэр {0}. Чтобы переключиться на iptables, сначала удалите его вручную!',\n        quickJump: 'Быстрый доступ',\n        used: 'Используется',\n        unUsed: 'Не используется',\n        dockerRestart: 'Операции с брандмауэром требуют перезапуска службы Docker',\n        firewallHelper: '{0} межсетевой экран',\n        firewallNotStart: 'Межсетевой экран в настоящее время не включен. Сначала включите его.',\n        restartFirewallHelper: 'Эта операция перезапустит текущий межсетевой экран. Хотите продолжить?',\n        stopFirewallHelper: 'Это лишит сервер защиты безопасности. Хотите продолжить?',\n        startFirewallHelper:\n            'После включения межсетевого экрана безопасность сервера будет лучше защищена. Хотите продолжить?',\n        noPing: 'Отключить ping',\n        enableBanPing: 'Блокировать Ping',\n        disableBanPing: 'Разблокировать Ping',\n        noPingTitle: 'Отключить ping',\n        noPingHelper: 'Это отключит ping, и сервер не будет отвечать на ICMP-запросы. Хотите продолжить?',\n        onPingHelper: 'Это включит ping, и хакеры смогут обнаружить ваш сервер. Хотите продолжить?',\n        changeStrategy: 'Изменить стратегию {0}',\n        changeStrategyIPHelper1:\n            'Изменить стратегию IP-адреса на [deny]. После установки IP-адреса доступ к серверу будет запрещен. Хотите продолжить?',\n        changeStrategyIPHelper2:\n            'Изменить стратегию IP-адреса на [allow]. После установки IP-адреса нормальный доступ будет восстановлен. Хотите продолжить?',\n        changeStrategyPortHelper1:\n            'Изменить политику портов на [drop]. После установки политики портов внешний доступ будет запрещен. Хотите продолжить?',\n        changeStrategyPortHelper2:\n            'Изменить политику портов на [accept]. После установки политики портов нормальный доступ к портам будет восстановлен. Хотите продолжить?',\n        stop: 'Остановить',\n        portFormatError: 'Это поле должно быть действительным портом.',\n        portHelper1: 'Несколько портов, например 8080 и 8081',\n        portHelper2: 'Диапазон портов, например 8080-8089',\n        changeStrategyHelper:\n            'Изменить стратегию {0} [{1}] на [{2}]. После установки {0} будет иметь внешний доступ {2}. Хотите продолжить?',\n        strategy: 'Стратегия',\n        accept: 'Принять',\n        drop: 'Отбросить',\n        anyWhere: 'Любой',\n        address: 'Указанные IP',\n        addressHelper: 'Поддерживает IP-адрес или сегмент IP',\n        allow: 'Разрешить',\n        deny: 'Запретить',\n        addressFormatError: 'Это поле должно быть действительным IP-адресом.',\n        addressHelper1: 'Поддерживает IP-адрес или диапазон IP. Например, \"172.16.10.11\" или \"172.16.10.0/24\".',\n        addressHelper2: 'Для нескольких IP-адресов разделяйте запятой. Например, \"172.16.10.11, 172.16.0.0/24\".',\n        allIP: 'Все IP',\n        portRule: 'Правило | Правила',\n        forwardRule: 'Правило переадресации портов | Правила переадресации портов',\n        ipRule: 'IP правило | IP правила',\n        userAgent: 'Фильтр User-Agent',\n        sourcePort: 'Исходный порт',\n        targetIP: 'Целевой IP',\n        targetPort: 'Целевой порт',\n        forwardHelper1:\n            'Если вы хотите перенаправить на локальный порт, целевой IP должен быть установлен как \"127.0.0.1\".',\n        forwardHelper2: 'Оставьте целевой IP пустым для перенаправления на локальный порт.',\n        forwardPortHelper: 'Поддерживает диапазоны портов, напр. 8080-8089',\n        forwardInboundInterface: 'Сетевой интерфейс для пересылки входящего трафика',\n        exportHelper: 'Собираюсь экспортировать {0} правил брандмауэра. Продолжить?',\n        importSuccess: 'Успешно импортировано {0} правил',\n        importPartialSuccess: 'Импорт завершён: {0} успешно, {1} с ошибкой',\n        ipv4Limit: 'Текущая операция поддерживает только адреса IPv4',\n        basicStatus: 'Текущая цепочка {0} не привязана, сначала привяжите!',\n        baseIptables: 'Сервис iptables',\n        forwardIptables: 'Сервис Переадресации Порта iptables',\n        advanceIptables: 'Сервис Расширенной Конфигурации iptables',\n        initMsg: 'Собираюсь инициализировать {0}, продолжить?',\n        initHelper:\n            'Обнаружено, что {0} не инициализирован. Нажмите кнопку инициализации в верхней строке состояния для настройки!',\n        bindHelper: 'Привязать - Правила брандмауэра вступят в силу только когда статус привязан. Подтвердить?',\n        unbindHelper:\n            'Отвязать - При отвязке все добавленные правила брандмауэра станут недействительными. Действуйте осторожно. Подтвердить?',\n        defaultStrategy: 'Политика по умолчанию для текущей цепочки {0} - {1}',\n        defaultStrategy2:\n            'Политика по умолчанию для текущей цепочки {0} - {1}, текущий статус - не привязан. Добавленные правила брандмауэра вступят в силу после привязки!',\n        filterRule: 'Правило Фильтра',\n        filterHelper:\n            'Правила фильтра позволяют управлять сетевым трафиком на уровне INPUT/OUTPUT. Настраивайте осторожно, чтобы избежать блокировки системы.',\n        chain: 'Цепочка',\n        targetChain: 'Целевая Цепочка',\n        sourceIP: 'Исходный IP',\n        destIP: 'Целевой IP',\n        inboundDirection: 'Входящее Направление',\n        outboundDirection: 'Исходящее Направление',\n        destPort: 'Целевой Порт',\n        action: 'Действие',\n        reject: 'Отклонить',\n        sourceIPHelper: 'Формат CIDR, напр. 192.168.1.0/24. Оставьте пустым для всех адресов',\n        destIPHelper: 'Формат CIDR, напр. 10.0.0.0/8. Оставьте пустым для всех адресов',\n        portHelper: '0 означает любой порт',\n        allPorts: 'Все Порта',\n        deleteRuleConfirm: 'Удалит {0} правил. Продолжить?',\n        advancedControl: 'Расширенное управление',\n        advancedControlNotAvailable:\n            'Сейчас используется firewall {0}, расширенные правила поддерживаются только для iptables',\n        destination: 'Назначение',\n    },\n    runtime: {\n        runtime: 'Среда выполнения',\n        workDir: 'Рабочая директория',\n        localHelper: 'По вопросам установки в локальной среде и использования в автономном режиме обратитесь к ',\n        versionHelper: 'Версия PHP, например v8.0',\n        buildHelper:\n            'Чем больше расширений, тем выше загрузка процессора при создании образа. Расширения можно установить после создания среды.',\n        openrestyWarn: 'PHP требует обновления OpenResty до версии 1.21.4.1 или выше для использования',\n        toupgrade: 'Обновить',\n        edit: 'Редактировать среду выполнения',\n        extendHelper:\n            'Неназванные расширения можно вручную ввести и выбрать. Например, введите \"sockets\" и выберите первый вариант из выпадающего списка, чтобы увидеть список расширений.',\n        rebuildHelper: 'После редактирования расширения необходимо пересобрать PHP приложение для применения изменений',\n        rebuild: 'Пересобрать PHP приложение',\n        source: 'Источник расширений PHP',\n        ustc: 'Научно-технический университет Китая',\n        netease: 'Netease',\n        aliyun: 'Alibaba Cloud',\n        default: 'по умолчанию',\n        tsinghua: 'Университет Цинхуа',\n        xtomhk: 'Зеркало XTOM (Гонконг)',\n        xtom: 'Зеркало XTOM (Глобальное)',\n        phpsourceHelper: 'Выберите подходящий источник в соответствии с вашей сетевой средой.',\n        appPort: 'Порт приложения',\n        externalPort: 'Внешний порт',\n        packageManager: 'Менеджер пакетов',\n        codeDir: 'Директория кода',\n        appPortHelper: 'Порт, используемый приложением.',\n        externalPortHelper: 'Порт, открытый для внешнего мира.',\n        runScript: 'Скрипт запуска',\n        runScriptHelper: 'Список команд запуска анализируется из файла package.json в исходной директории.',\n        open: 'Открыть',\n        operatorHelper: 'Операция {0} будет выполнена для выбранной среды выполнения. Хотите продолжить?',\n        taobao: 'Taobao',\n        tencent: 'Tencent',\n        imageSource: 'Источник образа',\n        moduleManager: 'Управление модулями',\n        module: 'Модуль',\n        nodeOperatorHelper:\n            'Выполнить {0} модуля {1}? Операция может вызвать аномалии в среде выполнения, пожалуйста, подтвердите перед продолжением',\n        customScript: 'Пользовательская команда запуска',\n        customScriptHelper:\n            'Пожалуйста, введите полную команду запуска, например: npm run start. Для команд запуска PM2 замените на pm2-runtime, иначе запуск завершится ошибкой.',\n        portError: 'Не повторяйте один и тот же порт.',\n        systemRestartHelper: 'Описание статуса: Прерывание - получение статуса не удалось из-за перезагрузки системы',\n        javaScriptHelper: 'Укажите полную команду запуска. Например, \"java -jar halo.jar -Xmx1024M -Xms256M\".',\n        javaDirHelper: 'Директория должна содержать jar файлы, поддиректории также допустимы',\n        goHelper: 'Укажите полную команду запуска. Например, \"go run main.go\" или \"./main\".',\n        goDirHelper: 'Директория или поддиректория должна содержать файлы Go или бинарные файлы.',\n        pythonHelper:\n            'Укажите полную команду запуска. Например, \"pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000\".',\n        dotnetHelper: 'Пожалуйста, укажите полную команду запуска, например, dotnet MyWebApp.dll',\n        dirHelper: 'Примечание: Укажите путь к каталогу внутри контейнера',\n        concurrency: 'Схема параллелизма',\n        loadStatus: 'Состояние нагрузки',\n        extraHosts: 'Сопоставление хостов',\n        extension: 'Расширение',\n        installExtension: 'Подтвердите установку расширения {0}',\n        loadedExtension: 'Загруженные расширения',\n        popularExtension: 'Популярные расширения',\n        uninstallExtension: 'Вы уверены, что хотите удалить расширение {0}',\n        phpConfigHelper: 'Для изменения конфигурации требуется перезапуск окружения. Продолжить',\n        operateMode: 'Режим работы',\n        dynamic: 'динамический',\n        static: 'статический',\n        ondemand: 'по требованию',\n        dynamicHelper:\n            'Динамически регулирует число процессов. Подходит для сайтов с плавающей нагрузкой или малой памятью',\n        staticHelper:\n            'Фиксированное число процессов, подходит для стабильного высокого трафика, но потребляет больше ресурсов',\n        ondemandHelper:\n            'Процессы запускаются/завершаются по требованию, ресурсы используются лучше, но первый отклик может быть медленнее',\n        max_children: 'максимальное число создаваемых процессов',\n        start_servers: 'число процессов при запуске',\n        min_spare_servers: 'минимальное число свободных процессов',\n        max_spare_servers: 'максимальное число свободных процессов',\n        envKey: 'Имя',\n        envValue: 'Значение',\n        environment: 'Переменная окружения',\n    },\n    process: {\n        pid: 'ID процесса',\n        ppid: 'Родительский PID',\n        numThreads: 'Потоки',\n        memory: 'Память',\n        diskRead: 'Чтение диска',\n        diskWrite: 'Запись диска',\n        netSent: 'исходящий',\n        netRecv: 'входящий',\n        numConnections: 'Соединения',\n        startTime: 'Время запуска',\n        state: 'Состояние',\n        running: 'Работает',\n        sleep: 'сон',\n        stop: 'остановлен',\n        idle: 'простой',\n        zombie: 'зомби-процесс',\n        wait: 'ожидание',\n        lock: 'блокировка',\n        blocked: 'заблокирован',\n        cmdLine: 'Команда запуска',\n        basic: 'Основное',\n        mem: 'Память',\n        openFiles: 'Открытые файлы',\n        env: 'Переменные окружения',\n        noenv: 'Нет',\n        net: 'Сетевые подключения',\n        laddr: 'Исходный адрес/порт',\n        raddr: 'Целевой адрес/порт',\n        stopProcess: 'Завершить',\n        viewDetails: 'Подробности',\n        stopProcessWarn: 'Вы уверены, что хотите завершить этот процесс (PID:{0})?',\n        kill: 'Завершить Процесс',\n        killNow: 'Завершить Сейчас',\n        killHelper: 'Завершение процесса {0} может привести к некорректной работе некоторых программ. Продолжить?',\n        processName: 'Имя процесса',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: 'Не удалось получить статус процесса, пожалуйста, проверьте состояние службы supervisor.',\n            notSupport:\n                'Служба Supervisor не обнаружена. Перейдите на страницу библиотеки скриптов для ручной установки',\n            list: 'Демон-процесс | Демон-процессы',\n            config: 'Конфигурация Supervisor',\n            primaryConfig: 'Расположение основного конфигурационного файла',\n            notSupportCtl:\n                'supervisorctl не обнаружен. Перейдите на страницу библиотеки скриптов для ручной установки.',\n            user: 'Пользователь',\n            command: 'Команда',\n            dir: 'Директория',\n            numprocs: 'Кол-во процессов',\n            initWarn:\n                'Это изменит значение \"files\" в секции \"[include\"] в основном конфигурационном файле. Директория других конфигурационных файлов будет: \"{директория установки 1Panel}/1panel/tools/supervisord/supervisor.d/\".',\n            operatorHelper: 'Операция {1} будет выполнена для {0}, продолжить?',\n            uptime: 'Время работы',\n            notStartWarn: 'Supervisor не запущен. Сначала запустите его.',\n            serviceName: 'Имя службы',\n            initHelper:\n                'Обнаружен сервис Supervisor, но он не инициализирован. Нажмите кнопку инициализации в верхней панели состояния для настройки.',\n            serviceNameHelper: 'Имя службы Supervisor, управляемой systemctl, обычно supervisor или supervisord',\n            restartHelper:\n                'Это перезапустит службу после инициализации, что приведет к остановке всех существующих демон-процессов.',\n            RUNNING: 'Работает',\n            STOPPED: 'Остановлен',\n            STOPPING: 'Останавливается',\n            STARTING: 'Запускается',\n            FATAL: 'Не удалось запустить',\n            BACKOFF: 'Исключение при запуске',\n            ERROR: 'Ошибка',\n            statusCode: 'Код статуса',\n            manage: 'Управление',\n            autoRestart: 'Автоматический перезапуск',\n            EXITED: 'Вышел',\n            autoRestartHelper: 'Автоматически перезапускать программу после её аварийного завершения',\n            autoStart: 'Автозапуск',\n            autoStartHelper: 'Автоматически запускать сервис после запуска Supervisor',\n        },\n    },\n    disk: {\n        management: 'Управление дисками',\n        partition: 'Раздел',\n        unmount: 'Отмонтировать',\n        unmountHelper: 'Вы хотите отмонтировать раздел {0}?',\n        mount: 'Подключить',\n        partitionAlert:\n            'Разделение диска требует форматирования диска, и существующие данные будут удалены. Пожалуйста, сохраните или сделайте снимки данных заранее.',\n        mountPoint: 'Точка монтирования',\n        systemDisk: 'Системный диск',\n        unpartitionedDisk: 'Неразделенный диск',\n        handlePartition: 'Разделить сейчас',\n        filesystem: 'Файловая система',\n        unmounted: 'Отмонтирован',\n        cannotOperate: 'Невозможно выполнить операцию',\n        systemDiskHelper: 'Подсказка: Текущий диск является системным диском, операции невозможны.',\n        autoMount: 'Автоматическое монтирование',\n        model: 'Модель устройства',\n        diskType: 'Тип диска',\n        serial: 'Серийный номер',\n        noFail: 'Сбой монтирования не влияет на запуск системы',\n    },\n    xpack: {\n        expiresTrialAlert:\n            'Дружеское напоминание: ваша пробная версия Pro истечет через {0} дней, и все функции Pro станут недоступны. Пожалуйста, своевременно продлите или обновите до полной версии.',\n        expiresAlert:\n            'Дружеское напоминание: ваша лицензия Pro истечет через {0} дней, и все функции Pro станут недоступны. Пожалуйста, продлите лицензию вовремя, чтобы обеспечить дальнейшее использование.',\n        menu: 'Рro',\n        upage: 'AI Конструктор сайтов',\n        proAlert: 'Обновитесь до Pro, чтобы использовать эту функцию',\n        app: {\n            app: 'APP',\n            title: 'Псевдоним панели',\n            titleHelper: 'Псевдоним панели используется для отображения в приложении (псевдоним панели по умолчанию)',\n            qrCode: 'QR код',\n            apiStatusHelper: 'APP панели необходимо включить функцию API интерфейса',\n            apiInterfaceHelper:\n                'Поддержка доступа к API-интерфейсу панели (эта функция должна быть включена для мобильного приложения панели)',\n            apiInterfaceHelper1:\n                'Для доступа к приложению панели необходимо добавить посетителя в белый список. Для нестабильных IP рекомендуется добавить 0.0.0.0/0 (все IPv4), ::/0 (все IPv6)',\n            qrCodeExpired: 'Время обновления',\n            apiLeakageHelper: 'Не раскрывайте QR-код. Убедитесь, что он используется только в доверенных средах.',\n        },\n        waf: {\n            name: 'WAF',\n            blackWhite: 'Черный и белый список',\n            globalSetting: 'Глобальные настройки',\n            websiteSetting: 'Настройки сайта',\n            blockRecords: 'Записи блокировки',\n            world: 'Мир',\n            china: 'Китай',\n            intercept: 'Перехват',\n            request: 'Запросы',\n            count4xx: 'Количество 4xx',\n            count5xx: 'Количество 5xx',\n            todayStatus: 'Статус на сегодня',\n            reqMap: 'Карта атак (последние 30 дней)',\n            resource: 'Источник',\n            count: 'Количество',\n            hight: 'Высокий',\n            low: 'Низкий',\n            reqCount: 'Запросы',\n            interceptCount: 'Количество перехватов',\n            requestTrends: 'Тренды запросов (последние 7 дней)',\n            interceptTrends: 'Тренды перехватов (последние 7 дней)',\n            whiteList: 'Белый список',\n            blackList: 'Черный список',\n            ipBlackListHelper: 'IP-адреса в черном списке блокируются от доступа к сайту',\n            ipWhiteListHelper: 'IP-адреса в белом списке обходят все ограничения',\n            uaBlackListHelper: 'Запросы с значениями User-Agent в черном списке будут заблокированы',\n            uaWhiteListHelper: 'Запросы с значениями User-Agent в белом списке обходят все ограничения',\n            urlBlackListHelper: 'Запросы к URL-адресам в черном списке будут заблокированы',\n            urlWhiteListHelper: 'Запросы к URL-адресам в белом списке обходят все ограничения',\n            ccHelper:\n                'Если сайт получает более {1} запросов с одного IP-адреса в течение {0} секунд, этот IP будет заблокирован на {2}',\n            blockTime: 'Время блокировки',\n            attackHelper: 'Если кумулятивные перехваты превышают {1} за {0} секунд, IP будет заблокирован на {2}',\n            notFoundHelper:\n                'Если кумулятивные запросы возвращают ошибку 404 более {1} раз за {0} секунд, IP будет заблокирован на {2}',\n            frequencyLimit: 'Ограничение частоты',\n            regionLimit: 'Ограничение по региону',\n            defaultRule: 'Правила по умолчанию',\n            accessFrequencyLimit: 'Ограничение частоты доступа',\n            attackLimit: 'Ограничение частоты атак',\n            notFoundLimit: 'Ограничение частоты 404',\n            urlLimit: 'Ограничение частоты URL',\n            urlLimitHelper: 'Установите частоту доступа для одного URL',\n            sqliDefense: 'Защита от SQL-инъекций',\n            sqliHelper: 'Обнаружение SQL-инъекций в запросах и их блокировка',\n            xssHelper: 'Обнаружение XSS в запросах и их блокировка',\n            xssDefense: 'Защита от XSS',\n            uaDefense: 'Правила для вредоносных User-Agent',\n            uaHelper: 'Включает правила для определения общих вредоносных ботов',\n            argsDefense: 'Правила для вредоносных параметров',\n            argsHelper: 'Блокирует запросы, содержащие вредоносные параметры',\n            cookieDefense: 'Правила для вредоносных Cookies',\n            cookieHelper: 'Запрещает передачу вредоносных Cookies в запросах',\n            headerDefense: 'Правила для вредоносных заголовков',\n            headerHelper: 'Запрещает запросы, содержащие вредоносные заголовки',\n            httpRule: 'Правила HTTP-запросов',\n            httpHelper:\n                'Установите типы методов, которые могут получить доступ. Если вы хотите ограничить определенные типы доступа, отключите эти кнопки. Например: если разрешен только доступ GET, то нужно отключить другие методы, кроме GET',\n            geoRule: 'Ограничения по региону',\n            geoHelper:\n                'Ограничьте доступ к вашему сайту с определенных регионов. Например, если доступ разрешен только из Китая, запросы из-за пределов Китая будут заблокированы',\n            ipLocation: 'Местоположение IP',\n            action: 'Действие',\n            ruleType: 'Тип атаки',\n            ipHelper: 'Введите IP-адрес',\n            attackLog: 'Журнал атак',\n            rule: 'Правило',\n            ipArr: 'Диапазон IPV4',\n            ipStart: 'Начальный IP',\n            ipEnd: 'Конечный IP',\n            ipv4: 'IPv4',\n            ipv6: 'IPv6',\n            urlDefense: 'Правила для URL',\n            urlHelper: 'Запрещенный URL',\n            dirFilter: 'Фильтрация по директориям',\n            sqlInject: 'SQL-инъекция',\n            xss: 'XSS',\n            phpExec: 'Выполнение PHP-скриптов',\n            oneWordTrojan: 'Троян одно слово',\n            appFilter: 'Фильтрация опасных директорий',\n            webShell: 'Webshell',\n            args: 'Вредоносные параметры',\n            protocolFilter: 'Фильтр протоколов',\n            javaFilter: 'Фильтрация опасных файлов Java',\n            scannerFilter: 'Фильтрация сканеров',\n            escapeFilter: 'Фильтрация экранирования',\n            customRule: 'Пользовательские правила',\n            httpMethod: 'Фильтрация HTTP-методов',\n            fileExt: 'Ограничение на загрузку файлов',\n            fileExtHelper: 'Запрещенные расширения файлов для загрузки',\n            deny: 'Запрещено',\n            allow: 'Разрешить',\n            field: 'Объект',\n            pattern: 'Условие',\n            ruleContent: 'Содержание',\n            contain: 'содержит',\n            equal: 'равно',\n            regex: 'регулярное выражение',\n            notEqual: 'Не равно',\n            customRuleHelper: 'Применяйте действия на основе указанных условий',\n            actionAllow: 'Разрешить',\n            blockIP: 'Заблокировать IP',\n            code: 'Код статуса ответа',\n            noRes: 'Отключить (444)',\n            badReq: 'Неверные параметры (400)',\n            forbidden: 'Запрещено (403)',\n            serverErr: 'Ошибка сервера (500)',\n            resHtml: 'Страница ответа',\n            allowHelper: 'Разрешение доступа пропустит последующие WAF-правила, используйте с осторожностью',\n            captcha: 'Проверка человеком',\n            fiveSeconds: '5-секундная проверка',\n            location: 'Регион',\n            redisConfig: 'Конфигурация Redis',\n            redisHelper: 'Включите Redis для сохранения временно заблокированных IP-адресов',\n            wafHelper: 'Все сайты потеряют защиту после отключения',\n            attackIP: 'IP атакующего',\n            attackParam: 'Детали атаки',\n            execRule: 'Срабатывающее правило',\n            acl: 'ACL',\n            sql: 'SQL-инъекция',\n            cc: 'Ограничение частоты доступа',\n            isBlocking: 'Заблокирован',\n            isFree: 'Не заблокирован',\n            unLock: 'Разблокировать',\n            unLockHelper: 'Вы хотите разблокировать IP: {0}?',\n            saveDefault: 'Сохранить по умолчанию',\n            saveToWebsite: 'Применить к сайту',\n            saveToWebsiteHelper: 'Применить текущие настройки ко всем сайтам?',\n            websiteHelper:\n                'Это настройки по умолчанию для создания сайта. Изменения должны быть применены к сайту, чтобы вступить в силу',\n            websiteHelper2:\n                'Это настройки по умолчанию для создания сайта. Пожалуйста, измените конкретную конфигурацию на сайте',\n            ipGroup: 'Группа IP',\n            ipGroupHelper:\n                'По одному IP или диапазону IP на строку, поддерживает IPv4 и IPv6, например: 192.168.1.1 или 192.168.1.0/24',\n            ipBlack: 'Черный список IP',\n            openRestyAlert: 'Версия OpenResty должна быть выше {0}',\n            initAlert:\n                'Необходима инициализация для первого использования, файл конфигурации сайта будет изменен, и оригинальная конфигурация WAF будет потеряна. Пожалуйста, сделайте резервную копию OpenResty заранее',\n            initHelper: 'Инициализация удалит текущую конфигурацию WAF. Вы уверены, что хотите инициализировать?',\n            mainSwitch: 'Главный переключатель',\n            websiteAlert: 'Пожалуйста, создайте сайт сначала',\n            defaultUrlBlack: 'Правила для URL',\n            htmlRes: 'Страница перехвата',\n            urlSearchHelper: 'Введите URL для поддержки нечеткого поиска',\n            toCreate: 'Создать',\n            closeWaf: 'Закрыть WAF',\n            closeWafHelper: 'Закрытие WAF приведет к потере защиты сайта, продолжить?',\n            addblack: 'Черный',\n            addwhite: 'Добавить белый',\n            addblackHelper: 'Добавить IP:{0} в черный список по умолчанию?',\n            addwhiteHelper: 'Добавить IP:{0} в белый список по умолчанию?',\n            defaultUaBlack: 'Правило для User-Agent',\n            defaultIpBlack: 'Вредоносная группа IP',\n            cookie: 'Правила для Cookies',\n            urlBlack: 'Черный список URL',\n            uaBlack: 'Черный список User-Agent',\n            attackCount: 'Ограничение частоты атак',\n            fileExtCheck: 'Ограничение на загрузку файлов',\n            geoRestrict: 'Ограничение по региону',\n            attacklog: 'Запись перехвата',\n            unknownWebsite: 'Неавторизованный доступ к домену',\n            geoRuleEmpty: 'Регион не может быть пустым',\n            unknown: 'Сайт не существует',\n            geo: 'Ограничение по региону',\n            revertHtml: 'Вы хотите восстановить {0} как страницу по умолчанию?',\n            five_seconds: '5-секундная проверка',\n            header: 'Правила заголовков',\n            methodWhite: 'HTTP-правила',\n            expiryDate: 'Дата истечения',\n            expiryDateHelper: 'После прохождения проверки она больше не будет выполняться в пределах срока действия',\n            defaultIpBlackHelper: 'Некоторые вредоносные IP-адреса, собранные с интернета, чтобы предотвратить доступ',\n            notFoundCount: 'Ограничение частоты 404',\n            matchValue: 'Значение для совпадения',\n            headerName: 'Поддерживает английский, цифры, -, длина 3-30',\n            cdnHelper: 'Сайты, использующие CDN, могут включить это для получения правильного исходного IP',\n            clearLogWarn: 'Очистка журнала невозможна, хотите продолжить?',\n            commonRuleHelper: 'Правило поддерживает нечеткое совпадение',\n            blockIPHelper:\n                'Заблокированные IP-адреса временно хранятся в OpenResty и будут разблокированы при перезапуске OpenResty. Они могут быть заблокированы навсегда через функцию блокировки',\n            addWhiteUrlHelper: 'Добавить URL {0} в белый список?',\n            dashHelper:\n                'В общественной версии также можно использовать функции в глобальных настройках и настройках сайта',\n            wafStatusHelper: 'WAF не включен, пожалуйста, включите его в глобальных настройках',\n            ccMode: 'Режим',\n            global: 'Глобальный режим',\n            uriMode: 'Режим URL',\n            globalHelper:\n                'Глобальный режим: активируется, когда общее количество запросов к любому URL за определенный промежуток времени превышает порог',\n            uriModeHelper:\n                'Режим URL: активируется, когда количество запросов к одному URL за определенный промежуток времени превышает порог',\n            ip: 'Черный список IP',\n            globalSettingHelper:\n                'Настройки с тегом [Website] должны быть включены в [Настройки сайта], а глобальные настройки являются только настройками по умолчанию для новых сайтов',\n            globalSettingHelper2:\n                'Настройки должны быть включены как в [Глобальных настройках], так и в [Настройках сайта]',\n            urlCCHelper: 'Более {1} запросов к этому URL за {0} секунд, блокируем этот IP {2}',\n            urlCCHelper2: 'URL не может содержать параметры',\n            notContain: 'Не содержит',\n            urlcc: 'Ограничение частоты URL',\n            method: 'Тип запроса',\n            addIpsToBlock: 'Массовая блокировка IP',\n            addUrlsToWhite: 'Массовое добавление URL в белый список',\n            noBlackIp: 'IP уже заблокирован, не нужно блокировать снова',\n            noWhiteUrl: 'URL уже в белом списке, не нужно добавлять снова',\n            spiderIpHelper:\n                'Включает Baidu, Bing, Google, 360, Shenma, Sogou, ByteDance, DuckDuckGo, Yandex. Закрытие этого параметра заблокирует доступ всех пауков.',\n            spiderIp: 'Пул IP-адресов поисковых роботов',\n            geoIp: 'Библиотека IP-адресов',\n            geoIpHelper: 'Используется для определения географического положения IP',\n            stat: 'Отчет об атаках',\n            statTitle: 'Отчет',\n            attackIp: 'IP-адрес атаки',\n            attackCountNum: 'Количество атак',\n            percent: 'Процент',\n            addblackUrlHelper: 'Добавить URL: {0} в черный список по умолчанию?',\n            rce: 'Удаленное выполнение кода',\n            software: 'Программное обеспечение',\n            cveHelper: 'Содержит уязвимости распространенных программ и фреймворков',\n            vulnCheck: 'Дополнительные правила',\n            ssrf: 'Уязвимость SSRF',\n            afr: 'Чтение произвольных файлов',\n            ua: 'Неавторизованный доступ',\n            id: 'Утечка информации',\n            aa: 'Обход аутентификации',\n            dr: 'Обход каталогов',\n            xxe: 'Уязвимость XXE',\n            suid: 'Уязвимость сериализации',\n            dos: 'Уязвимость отказа в обслуживании',\n            afd: 'Загрузка произвольных файлов',\n            sqlInjection: 'Внедрение SQL',\n            afw: 'Запись произвольных файлов',\n            il: 'Утечка информации',\n            clearAllLog: 'Очистить все логи',\n            exportLog: 'Экспортировать логи',\n            appRule: 'Правила приложений',\n            appRuleHelper:\n                'Распространенные правила приложений, включение может уменьшить ложные срабатывания, один сайт может использовать только одно правило',\n            logExternal: 'Исключить типы записей',\n            ipWhite: 'Белый список IP',\n            urlWhite: 'Белый список URL',\n            uaWhite: 'Белый список User-Agent',\n            logExternalHelper:\n                'Исключенные типы записей не будут регистрироваться в логах, черные/белые списки, региональные ограничения доступа и пользовательские правила генерируют много логов, рекомендуется исключить',\n            ssti: 'Атака SSTI',\n            crlf: 'Инъекция CRLF',\n            strict: 'Строгий режим',\n            strictHelper: 'Использует более строгие правила для проверки запросов',\n            saveLog: 'Сохранить лог',\n            remoteURLHelper: 'Удаленный URL должен содержать один IP на строку и не содержать других символов',\n            notFound: 'Not Found (404)',\n            serviceUnavailable: 'Сервис недоступен (503)',\n            gatewayTimeout: 'Тайм-аут шлюза (504)',\n            belongToIpGroup: 'Принадлежит к группе IP',\n            notBelongToIpGroup: 'Не принадлежит к группе IP',\n            unknownWebsiteKey: 'Неизвестный домен',\n            special: 'Специальный',\n            fileToLarge: 'Файл превышает 1MB и не может быть загружен',\n            uploadOverLimit: 'Загруженный файл превышает лимит количества, максимум 1 файл',\n            importRuleHelper: 'Одно правило на строку',\n        },\n        monitor: {\n            '360': '360 Search',\n            name: 'Мониторинг веб-сайта',\n            pv: 'Просмотры страниц',\n            uv: 'Уникальные посетители',\n            flow: 'Трафик',\n            ip: 'IP',\n            spider: 'Поисковые роботы',\n            visitors: 'Тренды посетителей',\n            today: 'Сегодня',\n            last7days: 'Последние 7 дней',\n            last30days: 'Последние 30 дней',\n            uvMap: 'Карта посетителей (30-е число)',\n            qps: 'Запросы в реальном времени (в минуту)',\n            flowSec: 'Трафик в реальном времени (в минуту)',\n            excludeCode: 'Исключить статус-коды',\n            excludeUrl: 'Исключить URL',\n            excludeExt: 'Исключить расширения',\n            cdnHelper: 'Получение реального IP из заголовка, предоставленного CDN',\n            reqRank: 'Рейтинг посещений',\n            refererDomain: 'Домен-реферер',\n            os: 'Операционная система',\n            browser: 'Браузер/Клиент',\n            device: 'Устройство',\n            showMore: 'Подробнее',\n            unknown: 'Прочее',\n            pc: 'Компьютер',\n            mobile: 'Мобильное устройство',\n            wechat: 'WeChat',\n            machine: 'Машина',\n            tencent: 'Браузер Tencent',\n            ucweb: 'UC Browser',\n            '2345explorer': 'Браузер 2345',\n            huaweibrowser: 'Браузер Huawei',\n            log: 'Логи запросов',\n            statusCode: 'Статус-код',\n            requestTime: 'Время отклика',\n            flowRes: 'Трафик отклика',\n            method: 'Метод запроса',\n            statusCodeHelper: 'Введите статус-код выше',\n            statusCodeError: 'Неверный тип статус-кода',\n            methodHelper: 'Введите метод запроса выше',\n            all: 'Все',\n            baidu: 'Baidu',\n            google: 'Google',\n            bing: 'Bing',\n            bytes: 'Заголовки сегодня',\n            sogou: 'Sogou',\n            failed: 'Ошибка',\n            ipCount: 'Количество IP',\n            spiderCount: 'Запросы от поисковых роботов',\n            averageReqTime: 'Среднее время отклика',\n            totalFlow: 'Общий трафик',\n            logSize: 'Размер файла лога',\n            realIPType: 'Метод получения реального IP',\n            fromHeader: 'Получение из заголовка HTTP',\n            fromHeaders: 'Получение из списка заголовков',\n            header: 'HTTP-заголовок',\n            cdnConfig: 'Конфигурация CDN',\n            xff1: 'Первичный прокси из X-Forwarded-For',\n            xff2: 'Вторичный прокси из X-Forwarded-For',\n            xff3: 'Третичный прокси из X-Forwarded-For',\n            xffHelper:\n                'Например: X-Forwarded-For: <client>,<proxy1>,<proxy2>,<proxy3>. Последний IP <proxy3> будет считаться прокси верхнего уровня',\n            headersHelper:\n                'Получение реального IP из распространённых заголовков CDN HTTP, выбирая первое доступное значение',\n            monitorCDNHelper: 'Изменение конфигурации CDN для мониторинга веб-сайта также обновит настройки WAF CDN',\n            wafCDNHelper: 'Изменение конфигурации WAF CDN также обновит настройки мониторинга веб-сайта',\n            statusErr: 'Неверный формат статус-кода',\n            shenma: 'Shenma Search',\n            duckduckgo: 'DuckDuckGo',\n            excludeUri: 'Исключить URI',\n            top100Helper: 'Показать топ 100 данных',\n            logSaveDay: 'Период хранения логов (дни)',\n            cros: 'Chrome OS',\n            theworld: 'Браузер TheWorld',\n            edge: 'Microsoft Edge',\n            maxthon: 'Браузер Maxthon',\n            monitorStatusHelper: 'Мониторинг не включён, пожалуйста, включите его в настройках',\n            excludeIp: 'Исключить IP-адреса',\n            excludeUa: 'Исключить User-Agent',\n            remotePort: 'Удаленный порт',\n            unknown_browser: 'Неизвестно',\n            unknown_os: 'Неизвестно',\n            unknown_device: 'Неизвестно',\n            logSaveSize: 'Максимальный размер сохранения логов',\n            logSaveSizeHelper: 'Это размер сохранения логов для одного сайта',\n            '360se': '360 Secure Browser',\n            websites: 'Список веб-сайтов',\n            trend: 'Статистика тренда',\n            reqCount: 'Количество запросов',\n            uriHelper: 'Вы можете использовать /test/* или /*/index.php для исключения Uri',\n        },\n        tamper: {\n            tamper: 'Защита от подделки сайта',\n            ignoreTemplate: 'Шаблон Исключения',\n            protectTemplate: 'Шаблон Защиты',\n            ignoreTemplateHelper:\n                'Введите содержание исключения, разделенные Enter или пробелом. (Конкретный каталог ./log или имя каталога tmp, для исключения файлов нужно ввести конкретный файл ./data/test.html)',\n            protectTemplateHelper:\n                'Введите содержание защиты, разделенные Enter или пробелом. (Конкретный файл ./index.html, расширение файла .html, тип файла js, для защиты каталогов нужно ввести конкретный каталог ./log)',\n            templateContent: 'Содержание Шаблона',\n            template: 'Шаблон',\n            saveTemplate: 'Сохранить как Шаблон',\n            tamperHelper1:\n                'Для веб-сайтов с развертыванием в один клик рекомендуется включить защиту от несанкционированного доступа к каталогу приложений; если веб-сайт не может нормально использоваться или резервное копирование/восстановление не удается, сначала отключите защиту от несанкционированного доступа;',\n            tamperHelper2:\n                'Ограничит операции чтения/записи, удаления, разрешений и изменения владельца для защищенных файлов в неисключенных каталогах',\n            tamperPath: 'Каталог Защиты',\n            tamperPathEdit: 'Изменить Путь',\n            log: 'Журнал Блокировки',\n            totalProtect: 'Общая Защита',\n            todayProtect: 'Сегодняшняя Защита',\n            templateRule: 'Длина 1-512, имя не может содержать {0} и другие символы',\n            ignore: 'Исключить',\n            ignoreHelper:\n                'Выберите или введите содержание исключения, разделенные Enter или пробелом. (Конкретный каталог ./log или имя каталога tmp, для исключения файлов нужно ввести или выбрать конкретный файл ./data/test.html)',\n            protect: 'Защитить',\n            protectHelper:\n                'Выберите или введите содержание защиты, разделенные Enter или пробелом. (Конкретный файл ./index.html, расширение файла .html, тип файла js, для защиты каталогов нужно ввести или выбрать конкретный каталог ./log)',\n            tamperHelper00: 'Исключение и защита поддерживают только относительные пути;',\n            tamperHelper01:\n                'После включения защиты от несанкционированного доступа система ограничит операции создания, редактирования и удаления защищенных файлов в неисключенных каталогах;',\n            tamperHelper02: 'Приоритет: Защита конкретного пути > Исключение конкретного пути > Защита > Исключение',\n            tamperHelper03:\n                'Операции мониторинга направлены только на неисключенные каталоги, отслеживая создание незащищенных файлов в этих каталогах.',\n            disableHelper:\n                'Собираетесь отключить защиту от несанкционированного доступа для следующих веб-сайтов, продолжить?',\n            appendOnly: 'Только Добавление',\n            appendOnlyHelper:\n                'Ограничивает операции удаления файлов в этом каталоге, разрешая только добавление исключенных каталогов или незащищенных файлов',\n            immutable: 'Неизменяемый',\n            immutableHelper:\n                'Ограничивает операции редактирования, удаления, изменения разрешений и владельца для этого файла',\n            onWatch: 'Наблюдение',\n            onWatchHelper:\n                'Отслеживает и перехватывает создание защищенных файлов или неисключенных каталогов в этом каталоге',\n            forceStop: 'Принудительное закрытие',\n            forceStopHelper:\n                'Собираетесь принудительно отключить функцию защиты от несанкционированного доступа для этого каталога веб-сайта. Продолжить?',\n        },\n        setting: {\n            setting: 'Настройки Панели',\n            title: 'Описание Панели',\n            titleHelper:\n                'Будет отображаться на странице входа пользователя (например, панель управления Linux сервером, рекомендуется от 8 до 15 символов)',\n            logo: 'Логотип (Без Текста)',\n            logoHelper:\n                'Будет отображаться в верхнем левом углу страницы управления при свёрнутом меню (рекомендуемый размер изображения: 82px*82px)',\n            logoWithText: 'Логотип (С Текстом)',\n            logoWithTextHelper:\n                'Будет отображаться в верхнем левом углу страницы управления при развернутом меню (рекомендуемый размер изображения: 185px*55px)',\n            favicon: 'Иконка Сайта',\n            faviconHelper: 'Иконка сайта (рекомендуемый размер изображения: 16px*16px)',\n            setDefault: 'Восстановить По Умолчанию',\n            setHelper: 'Текущие настройки будут сохранены. Вы хотите продолжить?',\n            setDefaultHelper: 'Все настройки панели будут восстановлены по умолчанию. Вы хотите продолжить?',\n            logoGroup: 'Логотип',\n            imageGroup: 'Изображение',\n            loginImage: 'Изображение страницы входа',\n            loginImageHelper: 'Будет отображаться на странице входа (Рекомендуемый размер: 500x416px)',\n            loginBgType: 'Тип фона страницы входа',\n            loginBgImage: 'Фоновое изображение страницы входа',\n            loginBgImageHelper: 'Будет отображаться как фон страницы входа (Рекомендуемый размер: 1920x1080px)',\n            loginBgColor: 'Цвет фона страницы входа',\n            loginBgColorHelper: 'Будет отображаться как цвет фона страницы входа',\n            image: 'Изображение',\n            bgColor: 'Цвет фона',\n            loginGroup: 'Страница входа',\n            loginBtnLinkColor: 'Цвет кнопки/ссылки',\n            loginBtnLinkColorHelper: 'Будет отображаться как цвет кнопки/ссылки на странице входа',\n        },\n        helper: {\n            wafTitle1: 'Карта Перехватов',\n            wafContent1: 'Отображает географическое распределение перехватов за последние 30 дней',\n            wafTitle2: 'Ограничения Регионального Доступа',\n            wafContent2: 'Ограничивайте источники доступа к сайту в зависимости от географического расположения',\n            wafTitle3: 'Пользовательская Страница Перехвата',\n            wafContent3: 'Создайте пользовательскую страницу для отображения после перехвата запроса',\n            wafTitle4: 'Пользовательские Правила (ACL)',\n            wafContent4: 'Перехватывайте запросы в соответствии с пользовательскими правилами',\n            tamperTitle1: 'Мониторинг Целостности Файлов',\n            tamperContent1:\n                'Мониторинг целостности файлов веб-сайта, включая основные файлы, скрипты и файлы конфигурации.',\n            tamperTitle2: 'Реальное Сканирование и Обнаружение',\n            tamperContent2:\n                'Обнаруживайте аномальные или измененные файлы, выполняя сканирование файловой системы веб-сайта в реальном времени.',\n            tamperTitle3: 'Настройки Разрешений Безопасности',\n            tamperContent3:\n                'Ограничивайте доступ к файлам веб-сайта с помощью правильных настроек разрешений и политик контроля доступа, снижая потенциальную поверхность атаки.',\n            tamperTitle4: 'Ведение Логов и Анализ',\n            tamperContent4:\n                'Записывайте логи доступа и операций с файлами для последующего аудита и анализа администраторами, а также для выявления потенциальных угроз безопасности.',\n            settingTitle1: 'Пользовательское Приветственное Сообщение',\n            settingContent1: 'Установите пользовательское приветственное сообщение на странице входа в 1Panel.',\n            settingTitle2: 'Пользовательский Логотип',\n            settingContent2: 'Разрешите загрузку изображений логотипов, содержащих названия брендов или другой текст.',\n            settingTitle3: 'Пользовательская Иконка Сайта',\n            settingContent3:\n                'Разрешите загрузку пользовательских иконок для замены стандартной иконки браузера, улучшая пользовательский опыт.',\n            monitorTitle1: 'Тренд Посетителей',\n            monitorContent1: 'Статистика и отображение тенденций посещаемости веб-сайта',\n            monitorTitle2: 'Карта Посетителей',\n            monitorContent2: 'Статистика и отображение географического распределения посетителей веб-сайта',\n            monitorTitle3: 'Статистика Доступа',\n            monitorContent3:\n                'Статистика запросов веб-сайта, включая поисковые боты, устройства доступа, статус запросов и т. д.',\n            monitorTitle4: 'Мониторинг в Реальном Времени',\n            monitorContent4:\n                'Мониторинг запросов веб-сайта в реальном времени, включая количество запросов, трафик и т. д.',\n            alertTitle1: 'SMS Уведомления',\n            alertContent1:\n                'При аномальном использовании ресурсов сервера, истечении срока действия сайта и сертификата, появлении новой версии или истечении срока действия пароля пользователи будут уведомлены через SMS, чтобы обеспечить своевременную обработку.',\n            alertTitle2: 'Журнал Уведомлений',\n            alertContent2:\n                'Предоставляет пользователям возможность просмотра журналов уведомлений, чтобы облегчить отслеживание и анализ исторических событий.',\n            alertTitle3: 'Настройки Уведомлений',\n            alertContent3:\n                'Предоставляет пользователям возможность настройки номеров телефонов, частоты и времени отправки уведомлений в день, что позволяет настраивать более удобные уведомления.',\n            nodeDashTitle1: 'Управление Приложениями',\n            nodeDashContent1:\n                'Единое управление приложениями на нескольких узлах, поддерживает мониторинг состояния, быстрое запуск/остановку, подключение терминала и резервное копирование',\n            nodeDashTitle2: 'Управление Веб-сайтами',\n            nodeDashContent2:\n                'Единое управление веб-сайтами на нескольких узлах, мониторинг состояния в реальном времени, поддерживает пакетный запуск/остановку и быстрое резервное копирование',\n            nodeDashTitle3: 'Управление Базами Данных',\n            nodeDashContent3:\n                'Единое управление базами данных на нескольких узлах, ключевые состояния с первого взгляда, поддерживает резервное копирование в один клик',\n            nodeDashTitle4: 'Управление Запланированными Задачами',\n            nodeDashContent4:\n                'Единое управление запланированными задачами на нескольких узлах, поддерживает мониторинг состояния, быстрый запуск/остановку и выполнение по ручному триггеру',\n            nodeTitle1: 'Добавление узла одним кликом',\n            nodeContent1: 'Быстро интегрируйте несколько серверных узлов',\n            nodeTitle2: 'Пакетное обновление',\n            nodeContent2: 'Синхронизируйте и обновите все узлы одной операцией',\n            nodeTitle3: 'Мониторинг статуса узла',\n            nodeContent3: 'Реальное наблюдение за рабочим статусом каждого узла',\n            nodeTitle4: 'Быстрое удаленное подключение',\n            nodeContent4: 'Одним кликом подключитесь к удаленным терминалам узлов',\n            fileExchangeTitle1: 'Передача с аутентификацией по ключу',\n            fileExchangeContent1: 'Аутентифицируйтесь через SSH-ключи, чтобы обеспечить безопасность передачи.',\n            fileExchangeTitle2: 'Эффективная синхронизация файлов',\n            fileExchangeContent2:\n                'Только синхронизируйте измененное содержимое, чтобы значительно повысить скорость и стабильность передачи.',\n            fileExchangeTitle3: 'Поддержка обмена между несколькими узлами',\n            fileExchangeContent3:\n                'Легко передавайте проектные файлы между разными узлами, гибко управляйте несколькими серверами.',\n            nodeAppTitle1: 'Управление Обновлениями Приложений',\n            nodeAppContent1:\n                'Единый мониторинг обновлений приложений на нескольких узлах, поддерживает обновление в один клик',\n            appTitle1: 'Гибкое управление панелью',\n            appContent1: 'Легко управляйте сервером 1Panel в любое время и в любом месте.',\n            appTitle2: 'Полная информация о сервисе',\n            appContent2:\n                'Управляйте приложениями, сайтами, Docker, базами данных и т. д. через мобильное приложение и создавайте приложения и сайты быстро.',\n            appTitle3: 'Мониторинг аномалий в реальном времени',\n            appContent3:\n                'Просматривайте статус сервера, мониторинг безопасности WAF, статистику посещений сайта и состояние процессов в реальном времени через мобильное приложение.',\n            clusterTitle1: 'Развертывание мастер-слейв',\n            clusterContent1:\n                'Поддерживает создание мастер-слейв экземпляров MySQL/Postgres/Redis на разных узлах, автоматически завершая связь мастер-слейв и инициализацию',\n            clusterTitle2: 'Управление мастер-слейв',\n            clusterContent2:\n                'Единая страница для централизованного управления несколькими узлами мастер-слейв, просмотр их ролей, статуса выполнения и т.д.',\n            clusterTitle3: 'Состояние репликации',\n            clusterContent3:\n                'Отображает состояние репликации мастер-слейв и информацию о задержке, помогая в устранении проблем синхронизации',\n        },\n        node: {\n            master: 'Главный узел',\n            masterBackup: 'Резервная копия главного узла',\n            backupNode: 'Резервный узел',\n            backupFrequency: 'Частота резервного копирования (часы)',\n            backupCopies: 'Копии Бэкапа',\n            noBackupNode:\n                'Резервный узел в настоящее время пуст. Выберите резервный узел для сохранения и повторите попытку!',\n            masterBackupAlert:\n                'Резервное копирование главного узла не настроено. Для обеспечения безопасности данных, пожалуйста, настройте резервный узел как можно скорее, чтобы можно было вручную переключиться на новый главный узел в случае сбоя.',\n            node: 'Узел',\n            addr: 'Адрес',\n            nodeUpgrade: 'Настройки Обновления',\n            nodeUpgradeHelper:\n                'Выбранные узлы автоматически начнут обновляться после завершения обновления основного узла, ручное вмешательство не требуется.',\n            nodeUnhealthy: 'Некорректное состояние узла',\n            deletedNode: 'Удалённый узел {0} в настоящее время не поддерживает операции обновления!',\n            nodeUnhealthyHelper:\n                'Обнаружено некорректное состояние узла. Проверьте в [Управление узлами] и повторите попытку!',\n            nodeUnbind: 'Узел не привязан к лицензии',\n            nodeUnbindHelper:\n                'Обнаружено, что узел не привязан к лицензии. Привяжите в меню [Настройки панели - Лицензия] и повторите попытку!',\n            memTotal: 'Общая память',\n            nodeManagement: 'Управление Несколькими Машинами',\n            nodeItem: 'Управление Узлами',\n            panelItem: 'Управление Панелями',\n            addPanel: 'Добавить Панель',\n            addPanelHelper:\n                'После успешного добавления панели вы можете быстро получить доступ к целевой панели в [Обзор - Панели].',\n            panel: 'Панель 1Panel',\n            others: 'Другие Панели',\n            addNode: 'Добавить узел',\n            connInfo: 'Информация о подключении',\n            nodeInfo: 'Информация об узле',\n            withProxy: 'Proxy Erişimini Etkinleştir',\n            withoutProxy: 'Proxy Erişimini Devre Dışı Bırak',\n            withProxyHelper:\n                'Alt düğümlere erişmek için panel ayarlarında tutulan sistem proxy {0} kullanılacak. Devam etmek istiyor musunuz?',\n            withoutProxyHelper:\n                'Alt düğümlere erişmek için panel ayarlarında tutulan sistem proxy kullanımı durdurulacak. Devam etmek istiyor musunuz?',\n            syncInfo: 'Синхронизация данных,',\n            syncHelper:\n                'При изменении данных главного узла, происходит синхронизация с этим дочерним узлом в реальном времени,',\n            syncBackupAccount: 'Настройки резервной учётной записи',\n            syncWithMaster:\n                'После обновления до Pro все данные будут синхронизироваться по умолчанию. Политики синхронизации можно настроить вручную в управлении узлами.',\n            syncProxy: 'Настройки системного прокси',\n            syncProxyHelper: 'Синхронизация настроек системного прокси требует перезапуска Docker',\n            syncProxyHelper1: 'Перезапуск Docker может повлиять на работающие сервисы контейнеров.',\n            syncProxyHelper2: 'Вы можете перезапустить вручную на странице Контейнеры - Конфигурация.',\n            syncProxyHelper3:\n                'Синхронизация настроек системного прокси требует перезапуска Docker, что может повлиять на работающие сервисы контейнеров',\n            syncProxyHelper4:\n                'Синхронизация настроек системного прокси требует перезапуска Docker. Вы можете перезапустить вручную позже на странице Контейнеры - Конфигурация.',\n            syncCustomApp: 'Синхронизировать пользовательский репозиторий приложений',\n            syncAlertSetting: 'Настройки системных предупреждений',\n            syncNodeInfo: 'Базовые данные узла,',\n            nodeSyncHelper: 'Синхронизация информации о узле будет синхронизировать следующую информацию:',\n            nodeSyncHelper1: '1. Информация о публичной резервной учетной записи',\n            nodeSyncHelper2: '2. Информация о соединении между основным узлом и подузлами',\n            nodeCheck: 'Проверка доступности',\n            checkSSH: 'Проверить SSH-подключение узла',\n            checkUserPermission: 'Проверка прав пользователя узла',\n            isNotRoot:\n                'Обнаружено, что sudo без пароля не поддерживается на этом узле и текущий пользователь не является root',\n            checkLicense: 'Проверить статус лицензии узла',\n            checkService: 'Проверить информацию о существующих службах на узле',\n            checkPort: 'Проверить доступность порта узла',\n            panelExist:\n                'Обнаружено, что на этом узле работает служба 1Panel V1. Перед добавлением обновите до V2 с помощью скрипта миграции.',\n            coreExist:\n                'Текущий узел уже активирован как мастер-узел и не может быть добавлен напрямую как подчинённый узел. Пожалуйста, сначала понизьте его до подчинённого узла перед добавлением, обратитесь к документации для подробностей.',\n            agentExist:\n                'Обнаружено, что 1panel-agent уже установлен на этом узле. Продолжение сохранит существующие данные и заменит только службу 1panel-agent.',\n            agentNotExist:\n                'Обнаружено, что на этом узле не установлен 1panel-agent, поэтому информацию об узле нельзя редактировать напрямую. Удалите его и добавьте снова.',\n            oldDataExist:\n                'Обнаружены исторические данные 1Panel V2 на этом узле. Текущие настройки будут перезаписаны следующей информацией:',\n            errLicense: 'Лицензия, привязанная к этому узлу, недоступна. Пожалуйста, проверьте и повторите попытку!',\n            errNodePort:\n                'Обнаружено, что порт узла [ {0} ] недоступен. Проверьте, разрешен ли этот порт в брандмауэре или группе безопасности.',\n            reinstallHelper: 'Переустановить узел {0}, вы хотите продолжить?',\n            unhealthyCheck: 'Проверка на неисправности',\n            fixOperation: 'Решение проблемы',\n            checkName: 'Элемент проверки',\n            checkSSHConn: 'Проверка доступности SSH-соединения',\n            fixSSHConn: 'Вручную отредактируйте узел, чтобы подтвердить информацию о подключении',\n            checkConnInfo: 'Проверка информации о подключении агента',\n            checkStatus: 'Проверка доступности службы узла',\n            fixStatus: 'Запустите \"systemctl status 1panel-agent.service\", чтобы проверить, запущена ли служба.',\n            checkAPI: 'Проверка доступности API узла',\n            fixAPI: 'Проверьте журналы узла и убедитесь, что порты брандмауэра правильно открыты.',\n            forceDelete: 'Принудительное удаление',\n            operateHelper: 'Будет выполнена операция {0} для следующих узлов, вы хотите продолжить?',\n            operatePanelHelper: 'Операция {0} будет выполнена на следующих панелях. Продолжить?',\n            forceDeleteHelper:\n                'Принудительное удаление проигнорирует ошибки удаления узла и удалит метаданные базы данных',\n            uninstall: 'Удалить данные узла',\n            uninstallHelper: 'Это удалит все данные 1Panel, связанные с узлом. Выбирайте осторожно!',\n            baseDir: 'Каталог установки',\n            baseDirHelper: 'Если каталог установки пуст, по умолчанию он будет установлен в каталоге /opt',\n            nodePort: 'Порт узла',\n            offline: 'Автономный режим',\n            freeCount: 'Бесплатная квота [{0}]',\n            offlineHelper: 'Используется, когда узел находится в автономной среде',\n            appUpgrade: 'Обновление приложения',\n            appUpgradeHelper: 'Есть {0} приложений, которые необходимо обновить',\n        },\n        customApp: {\n            name: 'Пользовательское хранилище приложений',\n            appStoreType: 'Источник пакета App Store',\n            appStoreUrl: 'URL хранилища',\n            local: 'Локальный путь',\n            remote: 'Удаленная ссылка',\n            imagePrefix: 'Префикс образа',\n            imagePrefixHelper:\n                'Функция: Настройка префикса образа и изменение поля образа в файле compose. Например, если префикс образа установлен как 1panel/custom, поле образа для MaxKB изменится на 1panel/custom/maxkb:v1.10.0',\n            closeHelper: 'Отменить использование пользовательского хранилища приложений',\n            appStoreUrlHelper: 'Поддерживается только формат .tar.gz',\n            postNode: 'Синхронизировать с подузлом',\n            postNodeHelper:\n                'Синхронизируйте пользовательский пакет магазина с tmp/customApp/apps.tar.gz в каталоге установки дочернего узла',\n            nodes: 'Выбрать узлы',\n            selectNode: 'Выбрать узел',\n            selectNodeError: 'Пожалуйста, выберите узел',\n            licenseHelper: 'Профессиональная версия поддерживает функцию пользовательского репозитория приложений',\n            databaseHelper: 'База данных, связанная с приложением, выберите базу данных целевого узла',\n            nodeHelper: 'Нельзя выбрать текущий узел',\n            migrateHelper:\n                'В настоящее время поддерживает миграцию только монолитных приложений и приложений, связанных только с базами данных MySQL, MariaDB, PostgreSQL',\n            opensslHelper:\n                'При использовании зашифрованного резервного копирования версии OpenSSL между двумя узлами должны быть согласованы, иначе миграция может завершиться неудачей.',\n            installApp: 'Пакетная установка',\n            installAppHelper: 'Пакетная установка приложений на выбранные узлы',\n        },\n        alert: {\n            isAlert: 'Оповещение',\n            alertCount: 'Количество оповещений',\n            clamHelper: 'Отправлять оповещение при обнаружении зараженных файлов',\n            cronJobHelper: 'Отправлять оповещение при сбое выполнения задачи',\n            licenseHelper: 'Профессиональная версия поддерживает оповещения',\n            alertCountHelper: 'Максимальная дневная частота оповещений',\n            alert: 'SMS Уведомление',\n            logs: 'Журнал Уведомлений',\n            list: 'Список Уведомлений',\n            addTask: 'Создать Уведомление',\n            editTask: 'Редактировать Уведомление',\n            alertMethod: 'Метод',\n            alertMsg: 'Сообщение Уведомления',\n            alertRule: 'Правила Уведомлений',\n            titleSearchHelper: 'Введите название уведомления для нечеткого поиска',\n            taskType: 'Тип',\n            ssl: 'Срок действия сертификата',\n            siteEndTime: 'Истечение срока действия сайта',\n            panelPwdEndTime: 'Истечение срока действия пароля панели',\n            panelUpdate: 'Доступна новая версия панели',\n            cpu: 'Уведомление о загрузке процессора сервера',\n            memory: 'Уведомление о памяти сервера',\n            load: 'Уведомление о нагрузке сервера',\n            disk: 'Уведомление о диске сервера',\n            website: 'Веб-сайт',\n            certificate: 'SSL Сертификат',\n            remainingDays: 'Оставшиеся дни',\n            sendCount: 'Количество Отправок',\n            sms: 'SMS',\n            wechat: 'WeChat',\n            dingTalk: 'DingTalk',\n            feiShu: 'FeiShu',\n            mail: 'Электронная Почта',\n            weCom: 'WeCom',\n            sendCountRulesHelper: 'Общее количество уведомлений до истечения срока действия (раз в день)',\n            panelUpdateRulesHelper: 'Общее количество уведомлений для новой версии панели (раз в день)',\n            oneDaySendCountRulesHelper: 'Максимальное количество уведомлений в день',\n            siteEndTimeRulesHelper: 'Сайты с неограниченным сроком действия не будут вызывать уведомления',\n            autoRenewRulesHelper:\n                'Сертификаты с включенным автоматическим продлением и оставшимися днями менее 31 не будут вызывать уведомления',\n            panelPwdEndTimeRulesHelper:\n                'Уведомления об истечении срока действия пароля панели недоступны, если срок действия не задан',\n            sslRulesHelper: 'Все SSL Сертификаты',\n            diskInfo: 'Диск',\n            monitoringType: 'Тип Мониторинга',\n            autoRenew: 'Автопродление',\n            useDisk: 'Использование Диска',\n            usePercentage: 'Процент Использования',\n            changeStatus: 'Изменить Статус',\n            disableMsg:\n                'Остановка задачи уведомления предотвратит отправку сообщений этой задачей. Вы хотите продолжить?',\n            enableMsg: 'Включение задачи уведомления позволит этой задаче отправлять сообщения. Вы хотите продолжить?',\n            useExceed: 'Использование Превышает',\n            useExceedRulesHelper: 'Отправить уведомление, если использование превышает установленное значение',\n            cpuUseExceedAvg: 'Среднее использование процессора превышает заданное значение',\n            memoryUseExceedAvg: 'Среднее использование памяти превышает заданное значение',\n            loadUseExceedAvg: 'Средняя нагрузка превышает заданное значение',\n            cpuUseExceedAvgHelper: 'Среднее использование процессора за указанное время превышает заданное значение',\n            memoryUseExceedAvgHelper: 'Среднее использование памяти за указанное время превышает заданное значение',\n            loadUseExceedAvgHelper: 'Средняя нагрузка за указанное время превышает заданное значение',\n            resourceAlertRulesHelper: 'Примечание: Непрерывные уведомления в течение 30 минут отправят только одно',\n            specifiedTime: 'Указанное Время',\n            deleteTitle: 'Удалить Уведомление',\n            deleteMsg: 'Вы уверены, что хотите удалить задачу уведомления?',\n            allSslTitle: 'Уведомления об истечении срока действия всех SSL сертификатов сайтов',\n            sslTitle: 'Уведомление об истечении срока действия SSL сертификата для сайта {0}',\n            allSiteEndTimeTitle: 'Уведомления об истечении срока действия всех сайтов',\n            siteEndTimeTitle: 'Уведомление об истечении срока действия сайта {0}',\n            panelPwdEndTimeTitle: 'Уведомление об истечении срока действия пароля панели',\n            panelUpdateTitle: 'Уведомление о новой версии панели',\n            cpuTitle: 'Уведомление о высокой загрузке процессора',\n            memoryTitle: 'Уведомление о высокой загрузке памяти',\n            loadTitle: 'Уведомление о высокой нагрузке',\n            diskTitle: 'Уведомление о высокой загрузке диска для точки монтирования {0}',\n            allDiskTitle: 'Уведомление о высокой загрузке диска',\n            timeRule:\n                'Оставшееся время менее {0} дней (если не обработано, будет отправлено повторно на следующий день)',\n            panelUpdateRule:\n                'Отправить уведомление один раз при обнаружении новой версии панели (если не обработано, будет отправлено повторно на следующий день)',\n            avgRule: 'Среднее использование {1} превышает {2}% в течение {0} минут, отправляется {3} раз в день',\n            diskRule: 'Использование диска для точки монтирования {0} превышает {1}{2}, отправляется {3} раз в день',\n            allDiskRule: 'Использование диска превышает {0}{1}, отправляется {2} раз в день',\n            cpuName: 'Процессор',\n            memoryName: 'Память',\n            loadName: 'Нагрузка',\n            diskName: 'Диск',\n            syncAlertInfo: 'Ручная отправка',\n            syncAlertInfoMsg: 'Вы хотите вручную отправить задачу уведомления?',\n            pushError: 'Не удалось отправить',\n            pushSuccess: 'Отправка успешна',\n            syncError: 'Ошибка синхронизации',\n            success: 'Уведомление успешно',\n            pushing: 'В процессе отправки...',\n            error: 'Ошибка оповещения',\n            cleanLog: 'Очистить логи',\n            cleanAlertLogs: 'Очистить журналы уведомлений',\n            daily: 'Ежедневное количество уведомлений: {0}',\n            cumulative: 'Общее количество уведомлений: {0}',\n            clams: 'Антивирусная проверка',\n            taskName: 'Имя задачи',\n            cronJobType: 'Тип задачи',\n            clamPath: 'Директория проверки',\n            cronjob: 'Ошибка при выполнении Задача cron {0}',\n            app: 'Резервное копирование приложения',\n            web: 'Резервное копирование сайта',\n            database: 'Резервное копирование базы данных',\n            directory: 'Резервное копирование директории',\n            log: 'Резервное копирование логов',\n            snapshot: 'Системный снимок',\n            clamsRulesHelper: 'Задачи сканирования на вирусы, требующие уведомлений',\n            cronJobRulesHelper: 'Этот тип планируемой задачи требует конфигурации',\n            clamsTitle: 'Задача сканирования на вирусы 「 {0} 」 обнаружила заражённый файл',\n            cronJobAppTitle: 'Задача Cron - ошибка задачи резервного копирования приложения 「 {0} 」',\n            cronJobWebsiteTitle: 'Задача Cron - ошибка задачи резервного копирования сайта 「 {0} 」',\n            cronJobDatabaseTitle: 'Задача Cron - ошибка задачи резервного копирования базы данных 「 {0} 」',\n            cronJobDirectoryTitle: 'Задача Cron - ошибка задачи резервного копирования директории 「 {0} 」',\n            cronJobLogTitle: 'Задача Cron - ошибка задачи резервного копирования логов 「 {0} 」',\n            cronJobSnapshotTitle: 'Задача Cron - ошибка задачи резервного копирования снимка системы 「 {0} 」',\n            cronJobShellTitle: 'Задача Cron - ошибка выполнения скрипта Shell 「 {0} 」',\n            cronJobCurlTitle: 'Задача Cron - ошибка доступа к URL 「 {0} 」',\n            cronJobCutWebsiteLogTitle: 'Задача Cron - ошибка задачи нарезки логов сайта 「 {0} 」',\n            cronJobCleanTitle: 'Задача Cron - ошибка задачи очистки кэша 「 {0} 」',\n            cronJobNtpTitle: 'Задача Cron - ошибка задачи синхронизации времени сервера 「 {0} 」',\n            clamsRule: 'Уведомление об обнаружении заражённого файла, отправляется {0} раз в день',\n            cronJobAppRule:\n                'Уведомление об ошибке задачи резервного копирования приложения, отправляется {0} раз в день',\n            cronJobWebsiteRule:\n                'Уведомление об ошибке задачи резервного копирования сайта, отправляется {0} раз в день',\n            cronJobDatabaseRule:\n                'Уведомление об ошибке задачи резервного копирования базы данных, отправляется {0} раз в день',\n            cronJobDirectoryRule:\n                'Уведомление об ошибке задачи резервного копирования директории, отправляется {0} раз в день',\n            cronJobLogRule: 'Уведомление об ошибке задачи резервного копирования логов, отправляется {0} раз в день',\n            cronJobSnapshotRule:\n                'Уведомление об ошибке задачи резервного копирования снимка системы, отправляется {0} раз в день',\n            cronJobShellRule: 'Уведомление об ошибке выполнения скрипта Shell, отправляется {0} раз в день',\n            cronJobCurlRule: 'Уведомление об ошибке задачи доступа к URL, отправляется {0} раз в день',\n            cronJobCutWebsiteLogRule: 'Уведомление об ошибке задачи нарезки логов сайта, отправляется {0} раз в день',\n            cronJobCleanRule: 'Уведомление об ошибке задачи очистки кэша, отправляется {0} раз в день',\n            cronJobNtpRule: 'Уведомление об ошибке задачи синхронизации времени сервера, отправляется {0} раз в день',\n            alertSmsHelper: 'Лимит SMS: всего {0} сообщений, уже использовано {1}',\n            goBuy: 'Купить больше',\n            phone: 'Телефон',\n            phoneHelper: 'Укажите реальный номер телефона для получения уведомлений',\n            dailyAlertNum: 'Дневной Лимит Уведомлений',\n            dailyAlertNumHelper: 'Максимальное количество уведомлений в день (до 100)',\n            timeRange: 'Диапазон Времени',\n            sendTimeRange: 'Временной интервал отправки',\n            sendTimeRangeHelper: 'Можно отправлять в диапазоне {0}',\n            to: 'до',\n            startTime: 'Время Начала',\n            endTime: 'Время Завершения',\n            defaultPhone: 'По умолчанию используется номер телефона, привязанный к лицензии',\n            noticeAlert: 'Уведомление',\n            resourceAlert: 'Уведомление о Ресурсах',\n            agentOfflineAlertHelper:\n                'При включении офлайн-оповещений для узла главный узел будет каждые 30 минут выполнять проверку и запускать задачи оповещения.',\n            offline: 'Оповещение об отключении',\n            offlineHelper:\n                'Если выбрано офлайн-оповещение, главный узел будет каждые 30 минут выполнять проверку и запускать задачи оповещения.',\n            offlineOff: 'Включить офлайн-оповещение',\n            offlineOffHelper:\n                'Включение офлайн-оповещений заставит главный узел каждые 30 минут выполнять задачи оповещения.',\n            offlineClose: 'Отключить офлайн-оповещение',\n            offlineCloseHelper:\n                'Отключив офлайн-оповещения, вы передаёте ответственность за оповещения на подчинённые узлы. Убедитесь, что сеть работает стабильно, чтобы избежать сбоев.',\n            alertNotice: 'Уведомление об оповещении',\n            methodConfig: 'Настройка способа уведомления',\n            commonConfig: 'Глобальная настройка',\n            smsConfig: 'SMS',\n            smsConfigHelper: 'Настройка номеров для SMS-уведомлений',\n            emailConfig: 'электронной почты',\n            emailConfigHelper: 'Настройка службы отправки SMTP-писем',\n            deleteConfigTitle: 'Удалить конфигурацию оповещения',\n            deleteConfigMsg: 'Вы уверены, что хотите удалить конфигурацию оповещения?',\n            test: 'Тест',\n            alertTestOk: 'Тестовое уведомление успешно',\n            alertTestFailed: 'Не удалось отправить тестовое уведомление',\n            displayName: 'имя',\n            sender: 'отправителя',\n            password: 'Пароль',\n            host: 'SMTP сервер',\n            port: 'Порт',\n            encryption: 'шифрования',\n            recipient: 'Получатель',\n            licenseTime: 'Напоминание об истечении лицензии',\n            licenseTimeTitle: 'Напоминание об истечении лицензии',\n            displayNameHelper: 'Отображаемое имя отправителя письма',\n            senderHelper: 'Адрес электронной почты для отправки сообщений',\n            passwordHelper: 'Код авторизации почтового сервиса',\n            hostHelper: 'Адрес SMTP-сервера, например: smtp.qq.com',\n            portHelper: 'SSL обычно использует 465, TLS — 587',\n            sslHelper: 'Если порт SMTP — 465, обычно требуется SSL',\n            tlsHelper: 'Если порт SMTP — 587, обычно требуется TLS',\n            triggerCondition: 'Условие срабатывания',\n            loginFail: ' неудачных попыток входа в течение',\n            nodeException: 'Оповещение о сбое узла',\n            licenseException: 'Оповещение о сбое лицензии',\n            panelLogin: 'Оповещение о сбое входа в панель',\n            sshLogin: 'Оповещение о сбое входа по SSH',\n            panelIpLogin: 'Оповещение о сбое IP входа в панель',\n            sshIpLogin: 'Оповещение о сбое IP входа по SSH',\n            ipWhiteListHelper:\n                'IP-адреса в белом списке не ограничиваются правилами, и при успешном входе оповещение не создается',\n            nodeExceptionRule: 'Оповещение о сбое узла, отправляется {0} раз в день',\n            licenseExceptionRule: 'Оповещение о сбое лицензии, отправляется {0} раз в день',\n            panelLoginRule: 'Оповещение о входе в панель, отправляется {0} раз в день',\n            sshLoginRule: 'Оповещение о входе по SSH, отправляется {0} раз в день',\n            userNameHelper: 'Имя пользователя не указано, по умолчанию будет использоваться адрес отправителя',\n            alertConfigHelper: 'Настройка каналов уведомлений для получения push-уведомлений от панели',\n            weComConfigHelper: 'Конфигурация уведомлений WeCom',\n            wechatConfigHelper: 'Конфигурация уведомлений официального аккаунта WeChat',\n            dingTalkConfigHelper: 'Конфигурация уведомлений DingTalk',\n            feiShuConfigHelper: 'Конфигурация уведомлений Feishu',\n            webhookName: 'Имя бота',\n            webhookUrl: 'URL Webhook',\n            alertConfigProHelper:\n                'Профессиональная версия дополнительно поддерживает уведомления через WeCom, DingTalk, Feishu и SMS.',\n        },\n        theme: {\n            lingXiaGold: 'Лин Ся Золотой',\n            classicBlue: 'Классический Синий',\n            freshGreen: 'Свежий Зелёный',\n            customColor: 'Пользовательский Цвет',\n            setDefault: 'По умолчанию',\n            setDefaultHelper: 'Цветовая схема темы будет восстановлена до исходного состояния. Вы хотите продолжить?',\n            setHelper: 'Выбранная в данный момент цветовая схема темы будет сохранена. Вы хотите продолжить?',\n        },\n        exchange: {\n            exchange: 'Обмен файлами',\n            exchangeConfirm: 'Хотите перенести файл/папку {1} с узла {0} в каталог {3} узла {2}?',\n        },\n        cluster: {\n            cluster: 'Высокая доступность приложений',\n            name: 'Имя кластера',\n            addCluster: 'Добавить кластер',\n            installNode: 'Установить узел',\n            master: 'Главный узел',\n            slave: 'Подчиненный узел',\n            replicaStatus: 'Состояние мастер-слейв',\n            unhealthyDeleteError:\n                'Состояние узла установки аномально, пожалуйста, проверьте список узлов и повторите попытку!',\n            replicaStatusError: 'Получение статуса аномально, пожалуйста, проверьте главный узел.',\n            masterHostError: 'IP главного узла не может быть 127.0.0.1',\n        },\n    },\n};\n\nexport default {\n    ...fit2cloudEnLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/tr.ts",
    "content": "import fit2cloudEnLocale from 'fit2cloud-ui-plus/src/locale/lang/en';\n\nconst message = {\n    commons: {\n        true: 'Doğru',\n        false: 'Yanlış',\n        colon: ': ',\n        example: 'örnek: ',\n        fit2cloud: 'FIT2CLOUD',\n        lingxia: 'Lingxia',\n        button: {\n            run: 'Çalıştır',\n            prev: 'Önceki',\n            next: 'Sonraki',\n            create: 'Oluştur ',\n            add: 'Ekle ',\n            save: 'Kaydet ',\n            set: 'Ayarla',\n            sync: 'Senkronize Et ',\n            delete: 'Sil',\n            edit: 'Düzenle ',\n            enable: 'Etkinleştir',\n            disable: 'Devre Dışı Bırak',\n            confirm: 'Onayla',\n            cancel: 'İptal',\n            reset: 'Sıfırla',\n            setDefault: 'Varsayılanı Geri Yükle',\n            restart: 'Yeniden Başlat',\n            conn: 'Bağlan',\n            disconn: 'Bağlantıyı Kes',\n            clean: 'Temizle',\n            login: 'Giriş Yap',\n            close: 'Kapat',\n            stop: 'Durdur',\n            start: 'Başlat',\n            view: 'Görüntüle',\n            watch: 'İzle',\n            handle: 'Tetikle',\n            clone: 'Klonla',\n            expand: 'Genişlet',\n            collapse: 'Daralt',\n            log: 'Günlükleri görüntüle',\n            back: 'Geri',\n            backup: 'Yedekle',\n            recover: 'Kurtar',\n            retry: 'Tekrar Dene',\n            upload: 'Yükle',\n            download: 'İndir',\n            init: 'Başlat',\n            verify: 'Doğrula',\n            saveAndEnable: 'Kaydet ve etkinleştir',\n            import: 'İçe Aktar',\n            export: 'Dışa Aktar',\n            power: 'Yetkilendirme',\n            search: 'Ara',\n            refresh: 'Yenile',\n            get: 'Al',\n            upgrade: 'Yükselt',\n            update: 'Güncelle',\n            updateNow: 'Şimdi Güncelle',\n            ignore: 'Yükseltmeyi yoksay',\n            copy: 'Kopyala',\n            random: 'Rastgele',\n            install: 'Yükle',\n            uninstall: 'Kaldır',\n            fullscreen: 'Tam ekrana geç',\n            quitFullscreen: 'Tam ekrandan çık',\n            showAll: 'Tümünü Göster',\n            hideSome: 'Bazılarını Gizle',\n            agree: 'Kabul Et',\n            notAgree: 'Kabul Etme',\n            preview: 'Önizleme',\n            open: 'Aç',\n            notSave: 'Kaydetme',\n            createNewFolder: 'Yeni klasör oluştur',\n            createNewFile: 'Yeni dosya oluştur',\n            helpDoc: 'Yardım Belgesi',\n            bind: 'Bağla',\n            unbind: 'Bağlantıyı Çöz',\n            cover: 'kapla',\n            skip: 'atla',\n            fix: 'Düzelt',\n            down: 'Durdur',\n            up: 'Başlat',\n            sure: 'Onayla',\n            show: 'Göster',\n            hide: 'Gizle',\n            visit: 'Visit',\n            migrate: 'Taşı',\n            disConn: 'Bağlantıyı kes',\n        },\n        operate: {\n            start: 'Başlat',\n            stop: 'Durdur',\n            restart: 'Yeniden Başlat',\n            reload: 'Yeniden Yükle',\n            rebuild: 'Yeniden İnşa Et',\n            sync: 'Senkronize Et',\n            up: 'Yukarı',\n            down: 'Aşağı',\n            delete: 'Sil',\n        },\n        search: {\n            timeStart: 'Başlangıç zamanı',\n            timeEnd: 'Bitiş zamanı',\n            timeRange: 'İle',\n            dateStart: 'Başlangıç tarihi',\n            dateEnd: 'Bitiş tarihi',\n            date: 'Tarih',\n        },\n        table: {\n            all: 'Tümü',\n            total: 'Toplam {0}',\n            name: 'Ad',\n            type: 'Tür',\n            status: 'Durum',\n            statusSuccess: 'Başarılı',\n            statusFailed: 'Başarısız',\n            statusWaiting: 'Bekliyor...',\n            records: 'Kayıtlar',\n            group: 'Grup',\n            default: 'Varsayılan',\n            createdAt: 'Oluşturulma zamanı',\n            publishedAt: 'Yayınlanma zamanı',\n            date: 'Tarih',\n            updatedAt: 'Güncellenme zamanı',\n            operate: 'İşlemler',\n            message: 'Mesaj',\n            description: 'Açıklama',\n            interval: 'Aralık',\n            user: 'Sahip',\n            title: 'Başlık',\n            port: 'Port',\n            forward: 'İleri',\n            protocol: 'Protokol',\n            tableSetting: 'Tablo ayarı',\n            refreshRate: 'Yenileme hızı',\n            noRefresh: 'Yenileme yok',\n            selectColumn: 'Sütun seç',\n            local: 'yerel',\n            serialNumber: 'Seri numarası',\n            manageGroup: 'Grupları Yönet',\n            backToList: 'Listeye Dön',\n            keepEdit: 'Düzenlemeye Devam Et',\n        },\n        loadingText: {\n            Upgrading: 'Sistem yükseltiliyor, lütfen bekleyin...',\n            Restarting: 'Sistem yeniden başlatılıyor, lütfen bekleyin...',\n            Recovering: 'Anlık görüntüden kurtarılıyor, lütfen bekleyin...',\n            Rollbacking: 'Anlık görüntüden geri alınıyor, lütfen bekleyin...',\n        },\n        msg: {\n            noneData: 'Veri mevcut değil',\n            delete: 'Bu silme işlemi geri alınamaz. Devam etmek istiyor musunuz?',\n            clean: 'Bu temizleme işlemi geri alınamaz. Devam etmek istiyor musunuz?',\n            closeDrawerHelper: 'Sistem yaptığınız değişiklikleri kaydetmeyebilir. Devam etmek istiyor musunuz?',\n            deleteSuccess: 'Başarıyla silindi',\n            loginSuccess: 'Başarıyla giriş yapıldı',\n            operationSuccess: 'Başarıyla tamamlandı',\n            copySuccess: 'Başarıyla kopyalandı',\n            notSupportOperation: 'Bu işlem desteklenmiyor',\n            requestTimeout: 'İstek zaman aşımına uğradı, lütfen daha sonra tekrar deneyin',\n            infoTitle: 'İpucu',\n            notRecords: 'Mevcut görev için yürütme kaydı oluşturulmadı',\n            sureLogOut: 'Çıkış yapmak istediğinizden emin misiniz?',\n            createSuccess: 'Başarıyla oluşturuldu',\n            updateSuccess: 'Başarıyla güncellendi',\n            uploadSuccess: 'Başarıyla yüklendi',\n            operateConfirm: 'İşlemden eminseniz, lütfen manuel olarak girin: ',\n            inputOrSelect: 'Lütfen seçin veya girin',\n            copyFailed: 'Kopyalama başarısız',\n            operatorHelper:\n                '\"{0}\" üzerinde \"{1}\" işlemi gerçekleştirilecek ve geri alınamaz. Devam etmek istiyor musunuz?',\n            notFound: 'Üzgünüz, istediğiniz sayfa mevcut değil.',\n            unSupportType: 'Mevcut dosya türü desteklenmiyor.',\n            unSupportSize: 'Yüklenen dosya {0}Myi aşıyor, lütfen onaylayın!',\n            fileExist: 'Dosya mevcut klasörde zaten var. Tekrar yükleme desteklenmiyor.',\n            fileNameErr:\n                'Sadece 1 ile 256 karakter arasında İngilizce, Çince, rakam veya nokta (.-_) içeren dosyalar yükleyebilirsiniz',\n            confirmNoNull: '{0} değerinin boş olmadığından emin olun.',\n            errPort: 'Yanlış port bilgisi, lütfen onaylayın!',\n            remove: 'Kaldır',\n            backupHelper: 'Mevcut işlem {0}i yedekleyecek. Devam etmek istiyor musunuz?',\n            recoverHelper: '{0} dosyasından geri yükleniyor. Bu işlem geri alınamaz. Devam etmek istiyor musunuz?',\n            refreshSuccess: 'Yenileme başarılı',\n            rootInfoErr: 'Zaten kök dizinde',\n            resetSuccess: 'Sıfırlama başarılı',\n            creatingInfo: 'Oluşturuluyor, bu işlem gerekli değil',\n            installSuccess: 'Yükleme başarılı',\n            uninstallSuccess: 'Kaldırma başarılı',\n            offlineTips: 'A versão offline não suporta esta operação',\n            errImportFormat: 'İçe aktarma verisi veya biçimi anormal, lütfen kontrol edip tekrar deneyin!',\n            importHelper:\n                'Çakışan veya yinelenen verileri içe aktarırken, içe aktarılan içerik orijinal veritabanı verilerini güncellemek için standart olarak kullanılacaktır.',\n            errImport: 'Dosya içeriği anormal:',\n            backupSuccess: 'Yedekleme başarılı',\n            restoreSuccess: 'Geri yükleme başarılı',\n        },\n        login: {\n            username: 'Kullanıcı adı',\n            password: 'Şifre',\n            passkey: 'Passkey ile giriş',\n            welcome: 'Tekrar hoş geldiniz, giriş yapmak için kullanıcı adınızı ve şifrenizi girin!',\n            errorAuthInfo: 'Girdiğiniz kullanıcı adı veya şifre yanlış, lütfen tekrar girin!',\n            errorMfaInfo: 'Yanlış kimlik doğrulama bilgisi, lütfen tekrar deneyin!',\n            captchaHelper: 'Güvenlik kodu',\n            errorCaptcha: 'Güvenlik kodu hatası!',\n            notSafe: 'Erişim Reddedildi',\n            safeEntrance1: 'Mevcut ortamda güvenli giriş etkinleştirildi',\n            safeEntrance2: 'Panel girişini görüntülemek için SSH terminalinde şu komutu girin: 1pctl user-info',\n            errIP1: 'Mevcut ortamda yetkili IP adresi erişimi etkinleştirildi',\n            errDomain1: 'Mevcut ortamda erişim alan adı bağlama etkinleştirildi',\n            errHelper: 'Bağlama bilgilerini sıfırlamak için SSH terminalinde şu komutu çalıştırın: ',\n            codeInput: 'MFA doğrulayıcının 6 haneli doğrulama kodunu girin',\n            mfaTitle: 'MFA Sertifikasyonu',\n            mfaCode: 'MFA doğrulama kodu',\n            title: 'Linux Sunucu Yönetim Paneli',\n            licenseHelper: '<Topluluk Lisans Sözleşmesi>',\n            errorAgree: 'Topluluk Yazılım Lisansını kabul etmek için tıklayın',\n            logout: 'Çıkış',\n            agreeTitle: 'Sözleşme',\n            agreeContent:\n                'Meşru hak ve çıkarlarınızı daha iyi korumak için lütfen aşağıdaki sözleşmeyi okuyun ve kabul edin &laquo; <a href = \"https://www.fit2cloud.com/legal/licenses.html\" target = \"_blank\" > Topluluk Lisans Sözleşmesi </a> &raquo;',\n            passkeyFailed: 'Passkey ile giriş başarısız oldu, lütfen tekrar deneyin',\n            passkeyNotSupported: 'Mevcut tarayıcı veya ortam passkey desteklemiyor',\n            passkeyToPassword: 'Passkey kullanırken sorun mu yaşıyorsunuz? Şifreyi kullanın',\n        },\n        rule: {\n            username: 'Kullanıcı adı girin',\n            password: 'Şifre girin',\n            rePassword: 'Şifre onayı, şifre ile tutarsız.',\n            requiredInput: 'Bu alan gereklidir.',\n            requiredSelect: 'Listeden bir öğe seçin',\n            illegalChar: '& ; $ ` ( ) \" > < | karakterlerinin enjekte edilmesi şu anda desteklenmiyor',\n            illegalInput: 'Bu alan yasadışı karakterler içermemelidir.',\n            commonName:\n                'Bu alan özel olmayan karakterlerle başlamalı ve İngilizce, Çince, rakam, \".\", \"-\", ve \"_\" karakterlerinden oluşmalı, uzunluk 1-128 olmalıdır.',\n            userName: 'Bu alan İngilizce, Çince, rakam ve \"_\" karakterlerinden oluşmalı, uzunluk 3-30 olmalıdır.',\n            simpleName:\n                'Bu alan alt çizgi karakteriyle başlamamalı ve İngilizce, rakam, ve \"_\" karakterlerinden oluşmalı, uzunluk 3-30 olmalıdır.',\n            simplePassword:\n                'Bu alan alt çizgi karakteriyle başlamamalı ve İngilizce, rakam, ve \"_\" karakterlerinden oluşmalı, uzunluk 1-30 olmalıdır.',\n            dbName: 'Bu alan alt çizgi karakteriyle başlamamalı ve İngilizce, rakam, ve \"_\" karakterlerinden oluşmalı, uzunluk 1-64 olmalıdır.',\n            imageName:\n                'Bu alan İngilizce, rakam, \":\", \"@\", \"/\", \".\", \"-\", ve \"_\" karakterlerinden oluşmalı, uzunluk 1-256 olmalıdır.',\n            volumeName: 'Bu alan İngilizce, rakam, \".\", \"-\", ve \"_\" karakterlerinden oluşmalı, uzunluk 2-30 olmalıdır.',\n            supervisorName:\n                'Bu alan özel olmayan karakterlerle başlamalı ve İngilizce, rakam, \"-\", ve \"_\" karakterlerinden oluşmalı, uzunluk 1-128 olmalıdır.',\n            composeName:\n                'Başlangıçta özel olmayan karakterleri, küçük harfleri, rakamları, - ve _ destekler, uzunluk 1-256',\n            complexityPassword:\n                'Bu alan İngilizce, rakamlardan oluşmalı, uzunluk 8-30 olmalı ve en az iki özel karakter içermelidir.',\n            commonPassword: 'Bu alanın uzunluğu 6dan fazla olmalıdır.',\n            linuxName:\n                'Bu alanın uzunluğu 1 ile 128 arasında olmalıdır. Alan şu özel karakterleri içermemelidir: \"{0}\".',\n            email: 'Bu alan geçerli bir e-posta adresi olmalıdır.',\n            number: 'Bu alan bir sayı olmalıdır.',\n            integer: 'Bu alan pozitif bir tam sayı olmalıdır.',\n            ip: 'Bu alan geçerli bir IP adresi olmalıdır.',\n            host: 'Bu alan geçerli bir IP adresi veya alan adı olmalıdır.',\n            hostHelper: 'IP adres veya alan adı girişini destekler',\n            port: 'Bu alan geçerli bir port numarası olmalıdır.',\n            selectHelper: 'Lütfen doğru {0} dosyasını seçin',\n            domain: 'Bu alan şu şekilde olmalıdır: example.com veya example.com:8080.',\n            databaseName: 'Bu alan İngilizce, rakam, ve \"_\" karakterlerinden oluşmalı, uzunluk 1-30 olmalıdır.',\n            ipErr: 'Bu alan geçerli bir IP adresi olmalıdır.',\n            numberRange: 'Bu alan {0} ile {1} arasında bir sayı olmalıdır.',\n            paramName: 'Bu alan İngilizce, rakam, \".\", \"-\", ve \"_\" karakterlerinden oluşmalı, uzunluk 2-30 olmalıdır.',\n            paramComplexity:\n                'Bu alan özel karakterlerle başlayıp bitmemeli ve İngilizce, rakam, \"{0}\" karakterlerinden oluşmalı, uzunluk 6-128 olmalıdır.',\n            paramUrlAndPort: 'Bu alan \"http(s)://(alan adı/ip):(port)\" formatında olmalıdır.',\n            nginxDoc: 'Bu alan İngilizce, rakam ve \".\" karakterlerinden oluşmalıdır.',\n            appName:\n                'Bu alan \"-\" ve \"_\" karakterleriyle başlayıp bitmemeli ve İngilizce, rakam, \"-\", ve \"_\" karakterlerinden oluşmalı, uzunluk 2-30 olmalıdır.',\n            containerName: 'Harf, rakam, -, _ ve . destekler; - _ veya . ile başlayamaz; uzunluk: 2-128',\n            mirror: 'Ayna hızlandırma adresi http(s):// ile başlamalı, İngilizce harfleri (büyük ve küçük), rakam, . / ve - desteklemeli, boş satır içermemelidir.',\n            disableFunction: 'Sadece harf, alt çizgi ve virgül destekler',\n            leechExts: 'Sadece harf, rakam ve virgül destekler',\n            paramSimple: 'Küçük harf ve rakam destekler, uzunluk 1-128',\n            filePermission: 'Dosya İzin Hatası',\n            formatErr: 'Format hatası, lütfen kontrol edin ve tekrar deneyin',\n            phpExtension: 'Sadece , _ küçük İngilizce ve rakam destekler',\n            paramHttp: 'http:// veya https:// ile başlamalıdır',\n            phone: 'Telefon numarası formatı yanlış',\n            authBasicPassword: 'Harf, rakam ve yaygın özel karakterler destekler, uzunluk 1-72',\n            length128Err: 'Uzunluk 128 karakteri geçemez',\n            maxLength: 'Uzunluk {0} karakteri geçemez',\n            alias: 'İngilizce, rakamlar, - ve _ destekler, uzunluk 1-128, ve -_ ile başlayamaz veya bitiremez.',\n        },\n        res: {\n            paramError: 'İstek başarısız, lütfen daha sonra tekrar deneyin!',\n            forbidden: 'Mevcut kullanıcının izni yok',\n            serverError: 'Servis istisnası',\n            notFound: 'Kaynak mevcut değil',\n            commonError: 'İstek başarısız',\n        },\n        service: {\n            serviceNotStarted: '{0} servisi başlatılmadı.',\n        },\n        status: {\n            running: 'Çalışıyor',\n            done: 'Tamamlandı',\n            scanFailed: 'Eksik',\n            success: 'Başarılı',\n            waiting: 'Bekliyor',\n            waitForUpgrade: 'Yükseltme Bekleniyor',\n            failed: 'Başarısız',\n            stopped: 'Durduruldu',\n            error: 'Hata',\n            created: 'Oluşturuldu',\n            restarting: 'Yeniden Başlatılıyor',\n            uploading: 'Yükleniyor',\n            unhealthy: 'Sağlıksız',\n            removing: 'Kaldırılıyor',\n            paused: 'Duraklatıldı',\n            exited: 'Çıkıldı',\n            dead: 'Ölü',\n            installing: 'Yükleniyor',\n            enabled: 'Etkinleştirildi',\n            disabled: 'Devre Dışı',\n            normal: 'Normal',\n            building: 'İnşa Ediliyor',\n            upgrading: 'Yükseltiliyor',\n            pending: 'Düzenleme Bekliyor',\n            rebuilding: 'Yeniden İnşa Ediliyor',\n            deny: 'Reddedildi',\n            accept: 'Kabul Edildi',\n            used: 'Kullanıldı',\n            unused: 'Kullanılmadı',\n            starting: 'Başlatılıyor',\n            recreating: 'Yeniden Oluşturuluyor',\n            creating: 'Oluşturuluyor',\n            init: 'Uygulama bekleniyor',\n            ready: 'normal',\n            applying: 'Uygulanıyor',\n            uninstalling: 'Kaldırılıyor',\n            lost: 'İletişim Kesildi',\n            bound: 'Bağlandı',\n            unbind: 'Bağlantısı Kesildi',\n            exceptional: 'İstisnai',\n            free: 'Ücretsiz',\n            enable: 'Etkinleştirildi',\n            disable: 'Devre Dışı',\n            deleted: 'Silindi',\n            downloading: 'İndiriliyor',\n            packing: 'Paketleniyor',\n            sending: 'Gönderiliyor',\n            healthy: 'Normal',\n            executing: 'Yürütülüyor',\n            installerr: 'Yükleme başarısız',\n            applyerror: 'Uygulama başarısız',\n            systemrestart: 'Kesintiye Uğradı',\n            starterr: 'Başlatma başarısız',\n            uperr: 'Başlatma başarısız',\n            new: 'Yeni',\n            conflict: 'Çakışma',\n            duplicate: 'Yinelenen',\n            unexecuted: 'Yürütülmedi',\n        },\n        units: {\n            second: ' saniye | saniye | saniye',\n            minute: 'dakika | dakika | dakika',\n            hour: 'saat | saat | saat',\n            day: 'gün | gün | gün',\n            week: 'hafta | hafta | hafta',\n            month: 'ay | ay | ay',\n            year: 'yıl | yıl | yıl',\n            time: 'rqm',\n            core: 'çekirdek | çekirdek | çekirdek',\n            millisecond: 'milisaniye | milisaniye',\n            secondUnit: 's',\n            minuteUnit: 'dk',\n            hourUnit: 'sa',\n            dayUnit: 'g',\n            semicolon: ';',\n        },\n        log: {\n            noLog: 'Günlük yok',\n        },\n    },\n    menu: {\n        home: 'Kontrol Paneli',\n        apps: 'Uygulama Mağazası',\n        website: 'Web Sitesi | Web Siteleri',\n        project: 'Proje | Projeler',\n        config: 'Yapılandırma | Yapılandırmalar',\n        ssh: 'SSH Ayarları',\n        firewall: 'Güvenlik Duvarı',\n        ssl: 'Sertifika | Sertifikalar',\n        database: 'Veritabanı | Veritabanları',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: 'Konteyner | Konteynerler',\n        cronjob: 'Cron İşi | Cron İşleri',\n        system: 'Sistem',\n        security: 'Güvenlik',\n        files: 'Dosya Tarayıcısı',\n        monitor: 'İzleme',\n        terminal: 'Terminal | Terminaller',\n        settings: 'Ayarlar',\n        toolbox: 'Araç Kutusu',\n        logs: 'Günlük | Günlükler',\n        runtime: 'Çalışma Zamanı | Çalışma Zamanları',\n        processManage: 'Süreç | Süreçler',\n        process: 'Süreç | Süreçler',\n        network: 'Ağ | Ağlar',\n        supervisor: 'Supervisor',\n        tamper: 'Kurcalama Koruması',\n        app: 'Uygulama',\n        msgCenter: 'Görevler',\n        filter: 'Filtre',\n        disk: 'Disk',\n    },\n    home: {\n        recommend: 'Önerilen',\n        dir: 'Dizin',\n        alias: 'Takma Ad',\n        quickDir: 'Hızlı Erişim',\n        minQuickJump: 'En az bir hızlı erişim girdisi ekleyin',\n        maxQuickJump: 'En fazla dört hızlı erişim girdisi ekleyebilirsiniz',\n        database: 'Veritabanı - Tümü',\n        restart_1panel: 'Paneli yeniden başlat',\n        restart_system: 'Sunucuyu yeniden başlat',\n        operationSuccess: 'Tamamlandı. Yeniden başlatılıyor, tarayıcıyı daha sonra yenileyin.',\n        entranceHelper: 'Güvenli giriş etkin değil. \"Ayarlar -> Güvenlik\" üzerinden etkinleştirerek güvenliği artırın.',\n        appInstalled: 'Uygulamalar',\n        systemInfo: 'Sistem bilgisi',\n        hostname: 'Host adı',\n        platformVersion: 'İşletim sistemi',\n        kernelVersion: 'Çekirdek',\n        kernelArch: 'Mimari',\n        network: 'Ağ',\n        io: 'Disk G/Ç',\n        ip: 'Yerel IP',\n        proxy: 'Sistem proxy',\n        baseInfo: 'Temel bilgi',\n        totalSend: 'Toplam gönderilen',\n        totalRecv: 'Toplam alınan',\n        rwPerSecond: 'G/Ç işlemleri',\n        ioDelay: 'G/Ç gecikmesi',\n        uptime: 'Çalışma süresi',\n        runningTime: 'Şu tarihten beri açık',\n        mem: 'Sistem Belleği',\n        swapMem: 'Swap Bölümü',\n        runSmoothly: 'Düşük yük',\n        runNormal: 'Orta yük',\n        runSlowly: 'Yüksek yük',\n        runJam: 'Ağır yük',\n        core: 'Fiziksel çekirdek',\n        logicCore: 'Mantıksal çekirdek',\n        corePercent: 'Çekirdek Kullanımı',\n        cpuFrequency: 'CPU Frekansı',\n        cpuDetailedPercent: 'CPU Zaman Dağılımı',\n        cpuUser: 'Kullanıcı',\n        cpuSystem: 'Sistem',\n        cpuIdle: 'Boşta',\n        cpuIrq: 'IRQ',\n        cpuSoftirq: 'Soft IRQ',\n        cpuSteal: 'Çalıntı',\n        cpuTop: 'CPU Kullanımına Göre İlk 5 İşlem',\n        memTop: 'Bellek Kullanımına Göre İlk 5 İşlem',\n        loadAverage: 'Son 1 dakikadaki yük ortalaması | Son {n} dakikadaki yük ortalaması',\n        load: 'Yük',\n        mount: 'Bağlama noktası',\n        fileSystem: 'Dosya sistemi',\n        total: 'Toplam',\n        used: 'Kullanılan',\n        cache: 'Önbellek',\n        free: 'Boş',\n        shard: 'Parçalı',\n        available: 'Kullanılabilir',\n        percent: 'Kullanım',\n        goInstall: 'Yüklemeye git',\n        networkCard: 'Ağ kartı',\n        disk: 'Disk',\n        memo: 'Not',\n        memoPlaceholder: 'Düzenlemeyi etkinleştirmek için düzenle düğmesine tıklayın.',\n        carouselSetting: 'Karusel ayarları',\n        tooltipSensitiveInfo: 'Hassas bilgileri göster/gizle',\n    },\n    tabs: {\n        more: 'Daha Fazla',\n        hide: 'Gizle',\n        closeLeft: 'Solları kapat',\n        closeRight: 'Sağları kapat',\n        closeCurrent: 'Mevcut olanı kapat',\n        closeOther: 'Diğerlerini kapat',\n        closeAll: 'Tümünü Kapat',\n    },\n    header: {\n        logout: 'Çıkış',\n    },\n    database: {\n        manage: 'Veritabanını yönet',\n        deleteBackupHelper: 'Veritabanı yedeklerini aynı anda sil',\n        delete: 'Silme işlemi geri alınamaz, lütfen \"',\n        deleteHelper: '\" girerek bu veritabanını silin',\n        noMysql: 'Veritabanı hizmeti (MySQL veya MariaDB)',\n        noPostgresql: 'Veritabanı hizmeti PostgreSQL',\n        goUpgrade: 'Yükseltmeye git',\n        goInstall: 'Yüklemeye git',\n        isDelete: 'Silindi',\n        permission: 'İzinleri değiştir',\n        format: 'Karakter Seti',\n        collation: 'Karşılaştırma',\n        collationHelper: 'Boşsa, {0} karakter setinin varsayılan karşılaştırmasını kullanın',\n        permissionForIP: 'IP',\n        permissionAll: 'Tümü(%)',\n        localhostHelper:\n            'Konteyner dağıtımı için veritabanı izinlerini \"localhost\" olarak yapılandırmak konteynere dış erişimi engelleyecektir. Lütfen dikkatli seçin!',\n        databaseConnInfo: 'Bağlantı',\n        rootPassword: 'Root şifresi',\n        serviceName: 'Servis Adı',\n        serviceNameHelper: 'Aynı ağdaki konteynerler arası erişim.',\n        backupList: 'Yedekleme',\n        loadBackup: 'İçe Aktar',\n        localUpload: 'Yerel Yükleme',\n        hostSelect: 'Sunucu Seçimi',\n        selectHelper: '{0} yedek dosyasını içe aktarmak istediğinizden emin misiniz?',\n        remoteAccess: 'Uzaktan erişim',\n        remoteHelper: 'Birden fazla IP virgülle ayrılır, örnek: 172.16.10.111, 172.16.10.112',\n        remoteConnHelper:\n            'Root kullanıcısı olarak MySQLe uzaktan bağlantı güvenlik riski oluşturabilir. Bu nedenle bu işlemi dikkatli yapın.',\n        changePassword: 'Şifre değiştir',\n        changeConnHelper: 'Bu işlem mevcut {0} veritabanını değiştirecek. Devam etmek istiyor musunuz?',\n        changePasswordHelper:\n            'Veritabanı bir uygulamayla ilişkilendirildi. Şifre değiştirmek aynı zamanda uygulamanın veritabanı şifresini de değiştirecek. Değişiklik uygulama yeniden başlatıldıktan sonra etkili olur.',\n        recoverTimeoutHelper: '-1, zaman aşımı sınırı olmadığı anlamına gelir',\n        confChange: 'Yapılandırma',\n        confNotFound:\n            'Yapılandırma dosyası bulunamadı. Lütfen uygulama mağazasından uygulamayı en son sürüme yükseltin ve tekrar deneyin!',\n        portHelper:\n            'Bu port konteynerin açığa çıkan portudur. Değişikliği ayrı olarak kaydetmeniz ve konteyneri yeniden başlatmanız gerekir!',\n        loadFromRemote: 'Sunucudan senkronize et',\n        userBind: 'Kullanıcı bağla',\n        pgBindHelper:\n            'Bu işlem yeni bir kullanıcı oluşturmak ve hedef veritabanına bağlamak için kullanılır. Şu anda veritabanında mevcut olan kullanıcıları seçmek desteklenmiyor.',\n        pgSuperUser: 'Süper Kullanıcı',\n        loadFromRemoteHelper:\n            'Bu, sunucudaki veritabanı bilgilerini 1Panele senkronize edecek. Devam etmek istiyor musunuz?',\n        passwordHelper: 'Alınamıyor, değiştirmek için tıklayın',\n        remote: 'Uzak',\n        remoteDB: 'Uzak sunucu | Uzak sunucular',\n        createRemoteDB: '@.lower:database.remoteDB Bağla',\n        unBindRemoteDB: '@.lower:database.remoteDB Bağlantısını Çöz',\n        unBindForce: 'Zorla bağlantıyı çöz',\n        unBindForceHelper:\n            'Son işlemin başarılı olmasını sağlamak için bağlantı çözme işlemi sırasındaki tüm hataları yoksay',\n        unBindRemoteHelper:\n            'Uzak veritabanının bağlantısını çözmek sadece bağlantı ilişkisini kaldıracak ve uzak veritabanını doğrudan silmeyecek',\n        editRemoteDB: 'Uzak sunucuyu düzenle',\n        localDB: 'Yerel veritabanı',\n        address: 'Veritabanı adresi',\n        version: 'Veritabanı sürümü',\n        userHelper:\n            'Root kullanıcı veya root yetkilerine sahip bir veritabanı kullanıcısı uzak veritabanına erişebilir.',\n        pgUserHelper: 'Süper kullanıcı yetkilerine sahip veritabanı kullanıcısı.',\n        ssl: 'SSL Kullan',\n        clientKey: 'İstemci özel anahtarı',\n        clientCert: 'İstemci sertifikası',\n        caCert: 'CA sertifikası',\n        hasCA: 'CA sertifikası var',\n        skipVerify: 'Sertifika geçerlilik kontrolünü yoksay',\n        initialDB: 'Başlangıç Veritabanı',\n        formatHelper:\n            'Mevcut veritabanı karakter seti {0}, karakter seti tutarsızlığı kurtarma işleminin başarısız olmasına neden olabilir',\n        dropHelper: 'Yüklenen dosyayı buraya sürükleyip bırakabilir veya',\n        clickHelper: 'yüklemek için tıklayın',\n        supportUpType:\n            'Yalnızca sql, sql.gz, tar.gz, .zip dosya formatlarını destekler. İçe aktarılan sıkıştırılmış dosya yalnızca bir .sql dosyası içermeli veya test.sql içermelidir',\n        currentStatus: 'Mevcut durum',\n        baseParam: 'Temel parametre',\n        performanceParam: 'Performans parametresi',\n        runTime: 'Başlatma zamanı',\n        connections: 'Toplam bağlantılar',\n        bytesSent: 'Gönderilen baytlar',\n        bytesReceived: 'Alınan baytlar',\n        queryPerSecond: 'Saniye başına sorgu',\n        txPerSecond: 'Saniye başına işlem',\n        connInfo: 'aktif/en yüksek bağlantılar',\n        connInfoHelper: 'Değer çok büyükse, \"max_connections\" değerini artırın.',\n        threadCacheHit: 'Thread önbellek isabet oranı',\n        threadCacheHitHelper: 'Çok düşükse, \"thread_cache_size\" değerini artırın.',\n        indexHit: 'İndeks isabet oranı',\n        indexHitHelper: 'Çok düşükse, \"key_buffer_size\" değerini artırın.',\n        innodbIndexHit: 'Innodb indeks isabet oranı',\n        innodbIndexHitHelper: 'Çok düşükse, \"innodb_buffer_pool_size\" değerini artırın.',\n        cacheHit: 'Sorgu önbellek isabet oranı',\n        cacheHitHelper: 'Çok düşükse, \"query_cache_size\" değerini artırın.',\n        tmpTableToDB: 'Diske geçici tablo',\n        tmpTableToDBHelper: 'Çok büyükse, \"tmp_table_size\" değerini artırmayı deneyin.',\n        openTables: 'Açık tablolar',\n        openTablesHelper: '\"table_open_cache\" yapılandırma değeri bu değerden büyük veya eşit olmalıdır.',\n        selectFullJoin: 'Tam birleştirme seçimi',\n        selectFullJoinHelper: 'Değer 0 değilse, veri tablosunun indeksinin doğru olup olmadığını kontrol edin.',\n        selectRangeCheck: 'İndekssiz birleştirme sayısı',\n        selectRangeCheckHelper: 'Değer 0 değilse, veri tablosunun indeksinin doğru olup olmadığını kontrol edin.',\n        sortMergePasses: 'Sıralı birleştirme sayısı',\n        sortMergePassesHelper: 'Değer çok büyükse, \"sort_buffer_size\" değerini artırın.',\n        tableLocksWaited: 'Kilitli tablo sayısı',\n        tableLocksWaitedHelper: 'Değer çok büyükse, veritabanı performansınızı artırmayı düşünün.',\n        performanceTuning: 'Performans ayarlama',\n        optimizationScheme: 'Optimizasyon şeması',\n        keyBufferSizeHelper: 'İndeks için tampon boyutu',\n        queryCacheSizeHelper: 'Sorgu önbelleği. Bu işlev devre dışıysa, bu parametreyi 0 olarak ayarlayın.',\n        tmpTableSizeHelper: 'Geçici tablo önbellek boyutu',\n        innodbBufferPoolSizeHelper: 'Innodb tampon boyutu',\n        innodbLogBufferSizeHelper: 'Innodb log tampon boyutu',\n        sortBufferSizeHelper: '* bağlantılar, thread başına sıralama tampon boyutu',\n        readBufferSizeHelper: '* bağlantılar, okuma tampon boyutu',\n        readRndBufferSizeHelper: '* bağlantılar, rastgele okuma tampon boyutu',\n        joinBufferSizeHelper: '* bağlantılar, ilişki tablosu önbellek boyutu',\n        threadStackelper: '* bağlantılar, thread başına yığın boyutu',\n        binlogCacheSizeHelper: '* bağlantılar, ikili log önbellek boyutu (4096nın katları)',\n        threadCacheSizeHelper: 'Thread havuzu boyutu',\n        tableOpenCacheHelper: 'Tablo önbelleği',\n        maxConnectionsHelper: 'Maksimum bağlantılar',\n        restart: 'Yeniden başlat',\n        slowLog: 'Yavaş loglar',\n        noData: 'Henüz yavaş log yok.',\n        isOn: 'Açık',\n        longQueryTime: 'eşik(saniye)',\n        thresholdRangeHelper: 'Lütfen doğru eşik değerini girin (1 - 600).',\n        timeout: 'Zaman aşımı(saniye)',\n        timeoutHelper: 'Boştaki bağlantı zaman aşımı süresi. 0, bağlantının sürekli açık olduğunu gösterir.',\n        maxclients: 'Maksimum istemci',\n        requirepassHelper:\n            'Boş bırakırsanız şifre ayarlanmadığını gösterir. Değişikliklerin ayrı olarak kaydedilmesi ve konteyner yeniden başlatılması gerekir!',\n        databases: 'Veritabanı sayısı',\n        maxmemory: 'Maksimum bellek kullanımı',\n        maxmemoryHelper: '0 kısıtlama olmadığını gösterir.',\n        tcpPort: 'Mevcut dinleme portu.',\n        uptimeInDays: 'Çalışma günü.',\n        connectedClients: 'Bağlı istemci sayısı.',\n        usedMemory: 'Redisin mevcut bellek kullanımı.',\n        usedMemoryRss: 'İşletim sisteminden talep edilen bellek boyutu.',\n        usedMemoryPeak: 'Redisin en yüksek bellek tüketimi.',\n        memFragmentationRatio: 'Bellek parçalanma oranı.',\n        totalConnectionsReceived: 'Çalışma başlangıcından itibaren bağlanan toplam istemci sayısı.',\n        totalCommandsProcessed: 'Çalışma başlangıcından itibaren yürütülen toplam komut sayısı.',\n        instantaneousOpsPerSec: 'Sunucunun saniye başına yürüttüğü komut sayısı.',\n        keyspaceHits: 'Veritabanı anahtarının başarıyla bulunma sayısı.',\n        keyspaceMisses: 'Veritabanı anahtarını bulma girişimlerinin başarısız olma sayısı.',\n        hit: 'Veritabanı anahtarı isabet oranı.',\n        latestForkUsec: 'Son fork() işleminde harcanan mikrosaniye sayısı.',\n        redisCliHelper: '\"redis-cli\" servisi algılanmadı. Önce servisi etkinleştirin.',\n        redisQuickCmd: 'Redis hızlı komutları',\n        recoverHelper: 'Bu işlem verileri [{0}] ile üzerine yazacak. Devam etmek istiyor musunuz?',\n        submitIt: 'Verilerin üzerine yaz',\n        baseConf: 'Temel',\n        allConf: 'Tümü',\n        restartNow: 'Şimdi yeniden başlat',\n        restartNowHelper1:\n            'Yapılandırma değişikliklerinin etkili olması için sistemi yeniden başlatmanız gerekir. Verilerinizin kalıcı olması gerekiyorsa, önce kaydetme işlemini gerçekleştirin.',\n        restartNowHelper: 'Bu yalnızca sistem yeniden başlatıldıktan sonra etkili olacaktır.',\n        persistence: 'Kalıcılık',\n        rdbHelper1: 'saniye, ekleme',\n        rdbHelper2: 'veri parçası',\n        rdbHelper3: 'Koşullardan herhangi birinin karşılanması RDB kalıcılığını tetikleyecektir.',\n        rdbInfo: 'Kural listesindeki değerin 1 ile 100000 arasında olduğundan emin olun',\n        containerConn: 'Konteyner bağlantısı',\n        copyConnURL: \"Bağlantı URL'sini kopyala\",\n        connAddress: 'Adres',\n        containerConnHelper:\n            'Bu bağlantı adresi PHP yürütme ortamı/konteyner kurulumunda çalışan uygulamalar tarafından kullanılır.',\n        remoteConn: 'Harici bağlantı',\n        remoteConnHelper2:\n            'Bu bağlantı adresi konteyner dışında veya harici uygulamalarda çalışan uygulamalar tarafından kullanılabilir.',\n        remoteConnHelper3:\n            'Varsayılan erişim adresi ana bilgisayar IPsidir. Değiştirmek için panel ayarları sayfasındaki \"Varsayılan Erişim Adresi\" yapılandırma öğesine gidin.',\n        localIP: 'Yerel IP',\n    },\n    aiTools: {\n        agents: {\n            agents: 'Ajanlar',\n            agent: 'Ajan',\n            account: 'Model hesabı',\n            noAccountHint: 'Mevcut bir model hesabını seçin veya yeni bir tane ekleyin.',\n            accountCount: '{0} model hesabı',\n            syncAgents: 'İlişkili ajanları senkronize et',\n            syncAgentsHelper: 'Bu model hesabını kullanan ajanlar için openclaw.json dosyasını güncelleyin',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: 'Uygulama sürümü',\n            webuiPort: 'WebUI portu',\n            allowedOrigins: 'Erişim adresleri',\n            allowedOriginsHelper:\n                'Her satıra bir tam erişim adresi girin. HTTPS kullanmanız önerilir. Örnek: https://192.168.1.2:18789. Varsayılan erişim adresi yapılandırılmamışsa elle girin.',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: 'En az bir erişim adresi girin',\n            allowedOriginsInvalid: 'http(s)://host-veya-ip[:port] biçimini kullanın',\n            provider: 'Model sağlayıcı',\n            apiKey: 'API anahtarı',\n            baseUrl: 'Temel URL',\n            accountModels: 'Model Catalog',\n            accountModelsHelper: 'Configure the models this account exposes to OpenClaw for switching and settings',\n            accountModelsRequired: 'Configure at least one model',\n            accountModelsDuplicate: 'Duplicate models exist in the catalog',\n            modelPool: 'Model Pool',\n            modelPoolHelper:\n                'Manage the models exposed by this account here. Agent creation and OpenClaw model switching both use this pool.',\n            modelInputTypes: 'Input Types',\n            reasoning: 'Reasoning Model',\n            token: 'Token',\n            manualModel: 'Manuel giriş',\n            verified: 'Doğrulandı',\n            verifySkipped: 'Doğrulama yok',\n            configTitle: 'Configuration',\n            settingsTab: 'Settings',\n            securityTab: 'Security',\n            otherTab: 'Other',\n            timeZone: 'Saat Dilimi',\n            browserEnabled: 'Browser Enabled',\n            switchModelSuccess: 'Model switched successfully',\n            channelsTab: 'Channels',\n            wecom: 'WeCom',\n            dingtalk: 'DingTalk',\n            feishu: 'Feishu',\n            pluginNotInstalled: 'Eklenti yüklü değil. Lütfen önce yükleyin.',\n            dmPolicy: 'DM Policy',\n            groupPolicy: 'Group Policy',\n            policyPairing: 'Pairing',\n            policyAllowlist: 'Allowlist',\n            policyOpen: 'Open',\n            policyDisabled: 'Disabled',\n            botName: 'Bot Name',\n            botId: 'Bot ID',\n            appId: 'App ID',\n            appSecret: 'App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: 'DM Allowlist',\n            allowFromHelper: 'One sender ID per line. Used only when DM Policy is Allowlist.',\n            allowFromPlaceholder: 'One sender ID per line',\n            groupAllowFrom: 'Group Allowlist',\n            groupAllowFromHelper: 'One group ID per line. Used only when Group Policy is Allowlist.',\n            groupAllowFromPlaceholder: 'One group ID per line',\n            allowFromRequired: 'Enter at least one allowlist entry',\n            saveAndRestartGateway: 'Save and restart gateway',\n            pairingCode: 'Pairing Code',\n            pairingCodePlaceholder: 'Enter pairing code',\n            approvePairing: 'Approve Pairing',\n            feishuRequired: 'Please fill botName / appId / appSecret',\n            saveSuccess: 'Saved successfully',\n            pairingCodeRequired: 'Please enter pairing code',\n            pairingApproveSuccess: 'Pairing approved successfully',\n            customProviderHelper: 'Özel model sağlayıcısında hesabın kullanılabilirliği doğrulanmaz',\n            feishuSaveSuccess: \"Feishu'ya kaydedildi\",\n        },\n        model: {\n            model: 'Model',\n            localModel: 'Yerel Modeller',\n            create: 'Model Ekle',\n            create_helper: '\"{0}\" çek',\n            ollama_doc: 'Daha fazla model aramak ve bulmak için Ollama resmi web sitesini ziyaret edebilirsiniz.',\n            container_conn_helper: 'Konteynerler arası erişim veya bağlantı için bu adresi kullanın',\n            ollama_sync:\n                'Ollama modelini senkronize ederken aşağıdaki modellerin mevcut olmadığı tespit edildi, bunları silmek istiyor musunuz?',\n            from_remote: 'Bu model 1Panel aracılığıyla indirilmedi, ilgili çekme logları yok.',\n            no_logs: 'Bu modelin çekme logları silindi ve görüntülenemiyor.',\n            vllmVersionHelper: 'FusionXpark GB 10 sunucuları için lütfen -cu130 sürümünü seçin.',\n        },\n        proxy: {\n            proxy: 'AI Proxy Geliştirmesi',\n            proxyHelper1: 'Alan adı bağlayın ve gelişmiş iletim güvenliği için HTTPSi etkinleştirin',\n            proxyHelper2: 'Genel internette maruz kalmayı önlemek için IP erişimini sınırlayın',\n            proxyHelper3: 'Akışı etkinleştir',\n            proxyHelper4: 'Oluşturulduktan sonra web sitesi listesinde görüntüleyebilir ve yönetebilirsiniz',\n            proxyHelper5:\n                'Etkinleştirdikten sonra, güvenliği artırmak için Uygulama Mağazası - Kurulu - Ollama - Parametrelerden porta harici erişimi devre dışı bırakabilirsiniz.',\n            proxyHelper6: 'Proxy yapılandırmasını devre dışı bırakmak için web sitesi listesinden silebilirsiniz.',\n            whiteListHelper: 'Erişimi yalnızca beyaz listedeki IPlerle sınırlayın',\n        },\n        gpu: {\n            gpu: 'GPU İzleme',\n            gpuHelper: 'Sistem NVIDIA-SMI veya XPU-SMI komutlarını algılamadı. Lütfen kontrol edip tekrar deneyin!',\n            process: 'İşlem Bilgisi',\n            type: 'Tür',\n            typeG: 'Grafik',\n            typeC: 'Hesaplama',\n            typeCG: 'Hesaplama+Grafik',\n            processName: 'İşlem Adı',\n            shr: 'Paylaşılan Bellek',\n            temperatureHelper: 'Yüksek GPU sıcaklığı GPU frekansında düşüşe neden olabilir',\n            gpuUtil: 'GPU Kullanımı',\n            temperature: 'Sıcaklık',\n            performanceState: 'Performans Durumu',\n            powerUsage: 'Güç Tüketimi',\n            memoryUsage: 'Bellek Kullanımı',\n            fanSpeed: 'Fan Hızı',\n            power: 'Güç',\n            powerCurrent: 'Mevcut Güç',\n            powerLimit: 'Güç Limiti',\n            memory: 'Bellek',\n            memoryUsed: 'Kullanılan Bellek',\n            memoryTotal: 'Toplam Bellek',\n            percent: 'Kullanım',\n            base: 'Temel Bilgiler',\n            driverVersion: 'Sürücü Sürümü',\n            cudaVersion: 'CUDA Sürümü',\n            processMemoryUsage: 'Bellek Kullanımı',\n            performanceStateHelper: 'P0 (maksimum performans) ile P12 (minimum performans) arası',\n            busID: 'Veriyolu Adresi',\n            persistenceMode: 'Kalıcılık Modu',\n            enabled: 'Etkin',\n            disabled: 'Devre Dışı',\n            persistenceModeHelper:\n                'Kalıcılık modu görevlere daha hızlı yanıt verir, ancak bekleme güç tüketimi buna bağlı olarak artar',\n            displayActive: 'GPU Başlatma',\n            displayActiveT: 'Evet',\n            displayActiveF: 'Hayır',\n            ecc: 'Hata Denetleme ve Düzeltme Teknolojisi',\n            computeMode: 'Hesaplama Modu',\n            default: 'Varsayılan',\n            exclusiveProcess: 'Özel Süreç',\n            exclusiveThread: 'Özel İş Parçacığı',\n            prohibited: 'Yasak',\n            defaultHelper: 'Varsayılan: Süreçler eşzamanlı olarak yürütülebilir',\n            exclusiveProcessHelper:\n                \"Özel Süreç: Yalnızca bir CUDA bağlamı GPU'yu kullanabilir, ancak birden çok iş parçacığı tarafından paylaşılabilir\",\n            exclusiveThreadHelper: \"Özel İş Parçacığı: CUDA bağlamındaki yalnızca bir iş parçacığı GPU'yu kullanabilir\",\n            prohibitedHelper: 'Yasak: Eşzamanlı süreç yürütmeye izin verilmez',\n            migModeHelper:\n                \"MIG örnekleri oluşturmak için kullanılır, GPU'nun kullanıcı katmanında fiziksel izolasyonunu uygular.\",\n            migModeNA: 'Desteklenmiyor',\n            current: 'Gerçek Zamanlı İzleme',\n            history: 'Geçmiş Kayıtlar',\n            notSupport: 'Mevcut sürüm veya sürücü bu parametrenin görüntülenmesini desteklemiyor.',\n            processCount: 'Süreç sayısı',\n        },\n        mcp: {\n            server: 'MCP Sunucusu',\n            baseUrl: 'Harici Erişim Yolu',\n            baseUrlHelper: 'Örneğin: http://192.168.1.2:8000',\n            ssePath: 'SSE Yolu',\n            ssePathHelper: 'Örneğin: /sse, diğer sunucularla çoğaltmamaya dikkat edin',\n            environment: 'Ortam Değişkenleri',\n            envKey: 'Değişken Adı',\n            envValue: 'Değişken Değeri',\n            externalUrl: 'Harici Bağlantı Adresi',\n            operatorHelper: '{0} üzerinde {1} işlemi gerçekleştirilecek, devam edilsin mi?',\n            domain: 'Varsayılan Erişim Adresi',\n            domainHelper: 'Örneğin: 192.168.1.1 veya example.com',\n            bindDomain: 'Web Sitesi Bağla',\n            commandPlaceHolder: 'Şu anda yalnızca npx ve ikili başlatma komutları desteklenir',\n            importMcpJson: 'MCP Sunucu Yapılandırmasını İçe Aktar',\n            importMcpJsonError: 'mcpServers yapısı yanlış',\n            bindDomainHelper:\n                'Web sitesini bağladıktan sonra, kurulu tüm MCP Sunucularının erişim adresini değiştirecek ve portlara harici erişimi kapatacaktır',\n            outputTransport: 'Çıktı Türü',\n            streamableHttpPath: 'Akış Yolu',\n            streamableHttpPathHelper: 'Örneğin: /mcp, diğer Sunucularla çakışmaması gerektiğine dikkat edin',\n            npxHelper: 'npx veya ikili dosya ile başlatılan mcp için uygundur',\n            uvxHelper: 'uvx ile başlatılan mcp için uygundur',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: 'Model Dizini',\n            commandHelper:\n                'Harici erişim gerekiyorsa, komuttaki bağlantı noktasını uygulama bağlantı noktasıyla aynı olacak şekilde ayarlayın',\n            imageAlert:\n                'Görüntü boyutu büyük olduğundan, kurulumdan önce görüntüyü sunucuya manuel olarak indirmeniz önerilir',\n            modelSpeedup: 'Model hızlandırmayı etkinleştir',\n            modelType: 'Model türü',\n        },\n    },\n    container: {\n        createByCommand: 'Komutla oluştur',\n        importContainerBackupTip: 'Konteyner yedek dosyasını içe aktar, yalnızca .tar.gz desteklenir',\n        importComposeBackupTip: 'Compose yedek dosyasını içe aktar, yalnızca .tar.gz desteklenir',\n        stopContainerBeforeBackup: 'Yedekleme öncesi konteyneri durdur',\n        stopComposeBeforeBackup: 'Yedekleme öncesi compose’u durdur',\n        stopBeforeBackupHelper:\n            'Etkinleştirildiğinde, yedekleme öncesinde konteyner veya compose servisi durdurulur ve veri tutarlılığını sağlamak için işlem tamamlandıktan sonra otomatik olarak geri açılır.',\n        commandInput: 'Komut girişi',\n        commandRule: 'Lütfen doğru docker run konteyner oluşturma komutunu girin!',\n        commandHelper: 'Bu komut konteyneri oluşturmak için sunucuda çalıştırılacak. Devam etmek istiyor musunuz?',\n        updateHelper1:\n            'Bu konteynerin uygulama mağazasından geldiği tespit edildi. Lütfen aşağıdaki iki noktaya dikkat edin:',\n        updateHelper2: '1. Mevcut değişiklikler uygulama mağazasındaki kurulu uygulamalarla senkronize edilmeyecektir.',\n        updateHelper3:\n            '2. Kurulu sayfasında uygulamayı değiştirirseniz, şu anda düzenlenen içerik geçersiz hale gelecektir.',\n        updateHelper4:\n            'Konteyneri düzenlemek yeniden oluşturma gerektirir ve kalıcı olmayan tüm veriler kaybedilecektir. Devam etmek istiyor musunuz?',\n        containerList: 'Konteyner listesi',\n        operatorHelper: 'Aşağıdaki konteynerde {0} işlemi gerçekleştirilecek, devam etmek istiyor musunuz?',\n        operatorAppHelper:\n            'Aşağıdaki konteyner(ler)de \"{0}\" işlemi gerçekleştirilecek ve çalışan hizmetleri etkileyebilir. Devam etmek istiyor musunuz?',\n        containerDeleteHelper:\n            \"Kapsayıcının Uygulama Mağazası'ndan geldiği tespit edildi. Kapsayıcıyı silmek, onu 1Panel'den tamamen kaldırmaz. Tamamen silmek için lütfen Uygulama Mağazası -> 'Yüklü' veya 'Çalışma Ortamı' menülerine gidin. Devam edilsin mi?\",\n        start: 'Başlat',\n        stop: 'Durdur',\n        restart: 'Yeniden başlat',\n        kill: 'Öldür',\n        pause: 'Duraklat',\n        unpause: 'Devam et',\n        rename: 'Yeniden adlandır',\n        remove: 'Kaldır',\n        removeAll: 'Tümünü kaldır',\n        containerPrune: 'Temizle',\n        containerPruneHelper1: 'Bu, durmuş durumdaki tüm konteynerleri silecektir.',\n        containerPruneHelper2:\n            'Konteynerler uygulama mağazasından geliyorsa, temizleme işleminden sonra \"Uygulama Mağazası -> Kurulu\" bölümüne gidip \"Yeniden Oluştur\" düğmesine tıklayarak onları yeniden kurmanız gerekecektir.',\n        containerPruneHelper3: 'Bu işlem geri alınamaz. Devam etmek istiyor musunuz?',\n        imagePrune: 'Temizle',\n        imagePruneSome: 'Etiketlenmemiş temizle',\n        imagePruneSomeEmpty: '\"none\" etiketli temizlenebilecek imaj yok.',\n        imagePruneSomeHelper: 'Herhangi bir konteyner tarafından kullanılmayan \"none\" etiketli imajları temizle.',\n        imagePruneAll: 'Kullanılmayan temizle',\n        imagePruneAllEmpty: 'Temizlenebilecek kullanılmayan imaj yok.',\n        imagePruneAllHelper: 'Herhangi bir konteyner tarafından kullanılmayan imajları temizle.',\n        networkPrune: 'Temizle',\n        networkPruneHelper: 'Bu, kullanılmayan tüm ağları kaldıracaktır. Devam etmek istiyor musunuz?',\n        volumePrune: 'Temizle',\n        volumePruneHelper: 'Bu, kullanılmayan tüm yerel birimleri kaldıracaktır. Devam etmek istiyor musunuz?',\n        cleanSuccess: 'İşlem başarılı, bu temizleme sayısı: {0}!',\n        cleanSuccessWithSpace: 'İşlem başarılı. Bu sefer temizlenen disk sayısı {0}. Boşaltılan disk alanı {1}!',\n        unExposedPort: 'Mevcut port eşleme adresi 127.0.0.1, dış erişimi etkinleştiremez.',\n        upTime: 'Çalışma süresi',\n        fetch: 'Getir',\n        lines: 'Satırlar',\n        linesHelper: 'Lütfen alınacak log sayısını doğru girin!',\n        downloadLinesHelper: 'İndirilecek log satırı sayısını seçin veya girin.',\n        lastDay: 'Son gün',\n        last4Hour: 'Son 4 saat',\n        lastHour: 'Son saat',\n        last10Min: 'Son 10 dakika',\n        cleanLog: 'Log temizle',\n        downLogHelper1: 'Bu, {0} konteynerinden tüm logları indirecektir. Devam etmek istiyor musunuz?',\n        downLogHelper2: 'Bu, {0} konteynerinden son {0} logunu indirecektir. Devam etmek istiyor musunuz?',\n        cleanLogHelper:\n            'Bu, konteynerin yeniden başlatılmasını gerektirir ve geri alınamaz. Devam etmek istiyor musunuz?',\n        newName: 'Yeni ad',\n        workingDir: 'Çalışma Dizini',\n        source: 'Kaynak kullanımı',\n        cpuUsage: 'CPU kullanımı',\n        cpuTotal: 'CPU toplam',\n        core: 'Çekirdek',\n        memUsage: 'Bellek kullanımı',\n        memTotal: 'Bellek sınırı',\n        memCache: 'Bellek önbelleği',\n        loadSize: 'Konteyner Boyutunu Al',\n        ip: 'IP adresi',\n        cpuShare: 'CPU paylaşımları',\n        cpuShareHelper:\n            'Konteyner motoru CPU paylaşımları için 1024 temel değerini kullanır. Konteynere daha fazla CPU zamanı vermek için bunu artırabilirsiniz.',\n        inputIpv4: 'Örnek: 192.168.1.1',\n        inputIpv6: 'Örnek: 2001:0db8:85a3:0000:0000:8a2e:0370:7334',\n        diskUsage: 'Disk Kullanımı',\n        localVolume: 'Yerel Depolama Birimi',\n        buildCache: 'Derleme Önbelleği',\n        usage: 'Kullanılan: {0}, Serbest Bırakılabilir: {1}',\n        clean: 'Serbest Bırak',\n        imageClean:\n            'Görüntüleri temizlemek, kullanılmayan tüm görüntüleri silecektir. Bu işlem geri alınamaz. Devam etmek istiyor musunuz?',\n        containerClean:\n            'Konteynerleri temizlemek, durdurulmuş tüm konteynerleri (Uygulama Mağazası ndaki durdurulmuş uygulamalar dahil) silecektir. Bu işlem geri alınamaz. Devam etmek istiyor musunuz?',\n        sizeRw: 'Konteyner Katman Boyutu',\n        sizeRwHelper: 'Konteynere özel yazılabilir katman boyutu',\n        sizeRootFs: 'Sanal Boyut',\n        sizeRootFsHelper: 'Konteynerin bağımlı olduğu tüm görüntü katmanları + konteyner katmanının toplam boyutu',\n        containerFromAppHelper:\n            'Bu konteynerin uygulama mağazasından geldiği tespit edildi. Uygulama işlemleri mevcut düzenlemelerin geçersiz hale gelmesine neden olabilir.',\n        containerFromAppHelper1:\n            'Düzenleme sayfasına girmek ve konteyner adını değiştirmek için kurulu uygulamalar listesindeki [Param] düğmesine tıklayın.',\n        command: 'Komut',\n        console: 'Konteyner etkileşimi',\n        tty: 'Sözde-TTY tahsis et (-t)',\n        openStdin: 'Bağlı değilse bile STDINi açık tut (-i)',\n        custom: 'Özel',\n        emptyUser: 'Boş olduğunda, varsayılan olarak giriş yapacaksınız',\n        privileged: 'Ayrıcalıklı',\n        privilegedHelper:\n            'Konteynerin ana bilgisayarda belirli ayrıcalıklı işlemler gerçekleştirmesine izin verir, bu da konteyner risklerini artırabilir. Dikkatli kullanın!',\n        upgradeHelper: 'Depo Adı/İmaj Adı: İmaj Sürümü',\n        upgradeWarning2:\n            'Yükseltme işlemi konteynerin yeniden oluşturulmasını gerektirir, kalıcı olmayan tüm veriler kaybedilecektir. Devam etmek istiyor musunuz?',\n        oldImage: 'Mevcut imaj',\n        sameImageContainer: 'Aynı imajlı konteynerler',\n        sameImageHelper: 'Aynı imajı kullanan konteynerlar seçilerek toplu şekilde güncellenebilir',\n        targetImage: 'Hedef imaj',\n        imageLoadErr: 'Konteyner için imaj adı algılanmadı',\n        imageUpdateTagEmpty: 'Güncellenebilir imaj etiketi algılanmadı',\n        appHelper: 'Konteyner uygulama mağazasından geliyor ve yükseltme hizmeti kullanılamaz hale getirebilir.',\n        resource: 'Kaynak',\n        input: 'Manuel giriş',\n        forcePull: 'Her zaman imajı çek ',\n        forcePullHelper: 'Bu, sunucudaki mevcut imajları yok sayacak ve kayıt defterinden en son imajı çekecektir.',\n        imageUpdateHelper: 'Kayıt deposundaki aynı etiketi kontrol et, güncelleme varsa çekip yerel imajı güncelle.',\n        server: 'Ana bilgisayar',\n        serverExample: '80, 80-88, ip:80 veya ip:80-88',\n        containerExample: '80 veya 80-88',\n        exposePort: 'Portu göster',\n        exposeAll: 'Tümünü göster',\n        cmdHelper: 'Örnek: nginx -g \"daemon off;\"',\n        entrypointHelper: 'Örnek: docker-entrypoint.sh',\n        autoRemove: 'Otomatik kaldır',\n        cpuQuota: 'CPU çekirdek sayısı',\n        memoryLimit: 'Bellek',\n        limitHelper: '0 olarak ayarlanırsa, sınırlama olmadığı anlamına gelir. Maksimum değer {0}',\n        macAddr: 'MAC Adresi',\n        mount: 'Bağla',\n        volumeOption: 'Birim',\n        hostOption: 'Ana bilgisayar',\n        serverPath: 'Sunucu yolu',\n        containerDir: 'Konteyner yolu',\n        volumeHelper: 'Depolama biriminin içeriğinin doğru olduğundan emin olun',\n        networkEmptyHelper: 'Lütfen konteyner ağ seçiminin doğru olduğunu onaylayın',\n        modeRW: 'RW',\n        modeR: 'R',\n        sharedLabel: 'Yayılma Modu',\n        private: 'Özel',\n        privateHelper: 'Konteyner ve hosttaki bağlama değişiklikleri birbirini etkilemez',\n        rprivate: 'Özyinelemeli Özel',\n        rprivateHelper: 'Konteynerdeki tüm bağlamalar hosttan tamamen izole edilmiştir',\n        shared: 'Paylaşılan',\n        sharedHelper: 'Host ve konteynerdeki bağlama değişiklikleri birbirine görünür',\n        rshared: 'Özyinelemeli Paylaşılan',\n        rsharedHelper: 'Host ve konteynerdeki tüm bağlama değişiklikleri birbirine görünür',\n        slave: 'Bağımlı',\n        slaveHelper: 'Konteyner host bağlama değişikliklerini görebilir, ancak kendi değişiklikleri hostu etkilemez',\n        rslave: 'Özyinelemeli Bağımlı',\n        rslaveHelper: 'Konteynerdeki tüm bağlamalar host değişikliklerini görebilir, ancak hostu etkilemez',\n        mode: 'Mod',\n        env: 'Ortamlar',\n        restartPolicy: 'Yeniden başlatma politikası',\n        always: 'always',\n        unlessStopped: 'unless-stopped',\n        onFailure: 'on-failure (varsayılan olarak beş kere)',\n        no: 'never',\n        refreshTime: 'Yenileme aralığı',\n        cache: 'Önbellek',\n        image: 'İmaj | İmajlar',\n        imagePull: 'Çek',\n        imagePullHelper:\n            'Birden fazla görüntü seçmeyi destekler, her görüntü girdikten sonra Entera basarak devam edin',\n        imagePush: 'Gönder',\n        imagePushHelper:\n            'Bu imgenin birden fazla etiketi olduğu tespit edildi. Lütfen gönderimde kullanılan imge adının şu olduğunu onaylayın: {0}',\n        imageDelete: 'İmaj sil',\n        repoName: 'Konteyner kayıt defteri',\n        imageName: 'İmaj adı',\n        pull: 'Çek',\n        path: 'Yol',\n        importImage: 'İçe aktar',\n        buildArgs: 'Derleme Argümanları',\n        imageBuild: 'Oluştur',\n        pathSelect: 'Yol',\n        label: 'Etiket',\n        imageTag: 'İmaj etiketi',\n        imageTagHelper:\n            \"Birden fazla görüntü etiketi ayarlamayı destekler, her etiket girdikten sonra Enter'a basarak devam edin\",\n        push: 'Gönder',\n        fileName: 'Dosya adı',\n        export: 'Dışa aktar',\n        exportImage: 'İmaj dışa aktarma',\n        size: 'Boyut',\n        tag: 'Etiketler',\n        tagHelper: 'Satır başına bir tane. Örneğin,\\nkey1=value1\\nkey2=value2',\n        imageNameHelper: 'İmaj adı ve Etiketi, örneğin: nginx:latest',\n        cleanBuildCache: 'Oluşturma önbelleğini temizle',\n        delBuildCacheHelper:\n            'Bu, oluşturma sırasında üretilen tüm önbelleğe alınmış yapıları silecek ve geri alınamaz. Devam etmek istiyor musunuz?',\n        urlWarning: 'URL öneki http:// veya https:// içermemelidir. Lütfen değiştirin.',\n        network: 'Ağ | Ağlar',\n        networkHelper:\n            'Bu, bazı uygulamaların ve çalışma zamanı ortamlarının düzgün çalışmamasına neden olabilir. Devam etmek istiyor musunuz?',\n        networkName: 'Ad',\n        driver: 'Sürücü',\n        option: 'Seçenek',\n        attachable: 'Eklenebilir',\n        parentNetworkCard: 'Ana Ağ Kartı',\n        subnet: 'Alt ağ',\n        scope: 'IP kapsamı',\n        gateway: 'Ağ geçidi',\n        auxAddress: 'IP hariç tut',\n        volume: 'Birim | Birimler',\n        volumeDir: 'Birim dizini',\n        nfsEnable: 'NFS depolamayı etkinleştir',\n        nfsAddress: 'Adres',\n        mountpoint: 'Bağlama noktası',\n        mountpointNFSHelper: 'örn. /nfs, /nfs-share',\n        options: 'Seçenekler',\n        repo: 'Depo | Depolar',\n        httpRepoHelper: 'HTTP tipinde bir depo işlemi Docker servisinin yeniden başlatılmasını gerektirir.',\n        httpRepo:\n            'HTTP protokolü seçilmesi Docker servisinin güvenli olmayan kayıt defterlerine eklemek için yeniden başlatılmasını gerektirir.',\n        delInsecure: 'Kredinin silinmesi',\n        delInsecureHelper:\n            'Bu, güvenli olmayan kayıt defterlerinden kaldırmak için Docker servisini yeniden başlatacaktır. Devam etmek istiyor musunuz?',\n        downloadUrl: 'Sunucu',\n        imageRepo: 'İmaj deposu',\n        repoHelper: 'Ayna depo/organizasyon/proje içeriyor mu?',\n        auth: 'Kimlik doğrulama gerekli',\n        mirrorHelper:\n            'Birden fazla ayna varsa, yeni satırlar gösterilmelidir, örneğin:\\nhttp://xxxxxx.m.daocloud.io \\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper:\n            'Birden fazla özel depo varsa, yeni satırlar gösterilmelidir, örneğin:\\n172.16.10.111:8081 \\n172.16.10.112:8081',\n        compose: 'Compose | Composelar',\n        composeFile: 'Düzenleme Dosyası',\n        fromChangeHelper:\n            'Kaynağın değiştirilmesi mevcut düzenlenen içeriği temizleyecektir. Devam etmek istiyor musunuz?',\n        composePathHelper: 'Yapılandırma dosyası kaydetme yolu: {0}',\n        composeHelper:\n            '1Panel editörü veya şablonu aracılığıyla oluşturulan kompozisyon {0}/docker/compose dizinine kaydedilecektir.',\n        deleteFile: 'Dosyayı sil',\n        deleteComposeHelper:\n            'Yapılandırma dosyaları ve kalıcı dosyalar dahil olmak üzere konteyner compose ile ilgili tüm dosyaları silin. Lütfen dikkatli ilerleyin!',\n        deleteCompose: '\" Bu kompozisyonu sil.',\n        composeDirectory: 'Compose dizini',\n        template: 'Şablon',\n        composeTemplate: 'Compose şablonu | Compose şablonları',\n        content: 'İçerik',\n        contentEmpty: 'Compose içeriği boş olamaz, lütfen girin ve tekrar deneyin!',\n        containerNumber: 'Konteyner sayısı',\n        containerStatus: 'Konteyner durumu',\n        exited: 'Çıktı',\n        running: 'Çalışıyor ( {0} / {1} )',\n        composeDetailHelper: 'Compose, 1Panel dışında oluşturulmuştur. Başlatma ve durdurma işlemleri desteklenmez.',\n        composeOperatorHelper: '{0} üzerinde {1} işlemi gerçekleştirilecek. Devam etmek istiyor musunuz?',\n        composeDownHelper:\n            'Bu, {0} compose altındaki tüm konteynerleri ve ağları durduracak ve kaldıracaktır. Devam etmek istiyor musunuz?',\n        composeEnvHelper2:\n            'Bu düzenleme 1Panel Uygulama Mağazası tarafından oluşturuldu. Lütfen ortam değişkenlerini yüklü uygulamalarda değiştirin.',\n        composeContentPlaceholder: 'Docker-compose dosyanızın içeriğini buraya yazın veya yapıştırın',\n        setting: 'Ayar | Ayarlar',\n        goSetting: 'Düzenlemeye git',\n        restartHelper:\n            'Yapılandırma değişikliklerinin geçerli olması için Docker servisinin yeniden başlatılması gerekir',\n        operatorStatusHelper: 'Bu, Docker servisini \"{0}\" yapacaktır. Devam etmek istiyor musunuz?',\n        dockerStatus: 'Docker Servisi',\n        daemonJsonPathHelper: 'Yapılandırma yolunun docker.servicede belirtilen ile aynı olduğundan emin olun.',\n        mirrors: 'Kayıt defteri aynaları',\n        mirrorsHelper: '',\n        mirrorsHelper2: 'Ayrıntılar için resmi belgelere bakın. ',\n        registries: 'Güvenli olmayan kayıt defterleri',\n        ipv6Helper:\n            'IPv6yı etkinleştirirken, bir IPv6 konteyner ağı eklemeniz gerekir. Belirli yapılandırma adımları için resmi belgelere bakın.',\n        ipv6CidrHelper: 'Konteynerler için IPv6 adres havuzu aralığı',\n        ipv6TablesHelper: 'iptables kuralları için Docker IPv6nın otomatik yapılandırması.',\n        experimentalHelper:\n            'ip6tablesı etkinleştirmek bu yapılandırmanın açılmasını gerektirir; aksi takdirde ip6tables yok sayılacaktır',\n        cutLog: 'Log seçeneği',\n        cutLogHelper1: 'Mevcut yapılandırma yalnızca yeni oluşturulan konteynerleri etkileyecektir.',\n        cutLogHelper2: 'Mevcut konteynerler yapılandırmanın etkili olması için yeniden oluşturulmalıdır.',\n        cutLogHelper3:\n            'Konteynerleri yeniden oluşturmanın veri kaybına neden olabileceğini unutmayın. Konteynerleriniz önemli veriler içeriyorsa, yeniden oluşturma işlemini gerçekleştirmeden önce yedeklediğinizden emin olun.',\n        maxSize: 'Maksimum boyut',\n        maxFile: 'Maksimum dosya',\n        liveHelper:\n            'Varsayılan olarak, Docker daemonı sonlandığında, çalışan konteynerleri kapatır. Daemon kullanılamaz hale gelirse konteynerlerin çalışmaya devam etmesi için daemonı yapılandırabilirsiniz. Bu işlevsellik canlı geri yükleme olarak adlandırılır. Canlı geri yükleme seçeneği, daemon çökmesi, planlı kesintiler veya yükseltmeler nedeniyle konteyner kesinti süresini azaltmaya yardımcı olur.',\n        liveWithSwarmHelper: 'live-restore daemon yapılandırması swarm modu ile uyumlu değildir.',\n        iptablesDisable: 'iptables kapat',\n        iptablesHelper1: 'Docker için iptables kurallarının otomatik yapılandırması.',\n        iptablesHelper2:\n            'iptablesı devre dışı bırakmak konteynerlerin dış ağlarla iletişim kuramamasına neden olacaktır.',\n        daemonJsonPath: 'Yapılandırma Yolu',\n        serviceUnavailable: 'Docker servisi şu anda başlatılmamış.',\n        startIn: ' başlatmak için',\n        sockPath: 'Unix domain socket',\n        sockPathHelper: 'Docker daemon ile istemci arasındaki iletişim kanalı.',\n        sockPathHelper1: 'Varsayılan yol: /var/run/docker-x.sock',\n        sockPathMsg:\n            'Socket Path ayarını kaydetmek Docker servisinin kullanılamaz hale gelmesine neden olabilir. Devam etmek istiyor musunuz?',\n        sockPathErr: 'Lütfen doğru Docker sock dosya yolunu seçin veya girin',\n        related: 'İlgili',\n        includeAppstore: 'Uygulama mağazasından konteynerleri göster',\n        excludeAppstore: 'Uygulama Mağazası Konteynerini Gizle',\n        cleanDockerDiskZone: 'Docker tarafından kullanılan disk alanını temizle',\n        cleanImagesHelper: '( Herhangi bir konteyner tarafından kullanılmayan tüm imajları temizle )',\n        cleanContainersHelper: '( Durmuş olan tüm konteynerleri temizle )',\n        cleanVolumesHelper: '( Kullanılmayan tüm yerel birimleri temizle )',\n        makeImage: 'İmaj oluştur',\n        newImageName: 'Yeni imaj adı',\n        commitMessage: 'Commit mesajı',\n        author: 'Yazar',\n        ifPause: 'Oluşturma Sırasında Konteyneri Duraklat',\n        ifMakeImageWithContainer: 'Bu Konteynerden Yeni İmaj Oluşturulsun mu?',\n        finishTime: 'Son durdurma zamanı',\n        exportHelper: 'Seçilen imajları tek bir tar dosyasına aktarır.',\n    },\n    cronjob: {\n        importHelper:\n            'İçe aktarım sırasında aynı isimli zamanlanmış görevler otomatik olarak atlanacaktır. Görevler varsayılan olarak 【Devre Dışı】 durumuna ayarlanır ve veri ilişkilendirme anormalse 【Düzenleme Bekliyor】 durumuna ayarlanır.',\n        changeStatus: 'Durumu değiştir',\n        disableMsg: 'Bu, zamanlanmış görevin otomatik olarak yürütülmesini durduracaktır. Devam etmek istiyor musunuz?',\n        enableMsg:\n            'Bu, zamanlanmış görevin otomatik olarak yürütülmesine izin verecektir. Devam etmek istiyor musunuz?',\n        taskType: 'Tür',\n        nextTime: 'Sonraki 5 yürütme',\n        record: 'Kayıtlar',\n        viewRecords: 'Kayıtları görüntüle',\n        shell: 'Shell',\n        stop: 'Manuel Durdur',\n        stopHelper: 'Bu işlem mevcut görev yürütmesini zorla durduracaktır. Devam etmek istiyor musunuz?',\n        log: 'Yedekleme logları',\n        logHelper: 'Sistem logunu yedekle',\n        ogHelper1: '1.1Panel Sistem logu ',\n        logHelper2: '2. Sunucunun SSH giriş logu ',\n        logHelper3: '3. Tüm site logları ',\n        containerCheckBox: 'Konteynerde (konteyner komutunu girmeye gerek yok)',\n        containerName: 'Konteyner adı',\n        ntp: 'Zaman senkronizasyonu',\n        ntp_helper: 'NTP sunucusunu Araç Kutusunun Hızlı Kurulum sayfasından yapılandırabilirsiniz.',\n        app: 'Uygulamayı yedekle',\n        website: 'Web sitesini yedekle',\n        rulesHelper:\n            'Birden fazla sıkıştırma hariç tutma kuralı olduğunda, satır sonları ile gösterilmeleri gerekir. Örneğin,\\n*.log \\n*.sql',\n        lastRecordTime: 'Son yürütme zamanı',\n        all: 'Tümü',\n        failedRecord: 'Başarısız kayıtlar',\n        successRecord: 'Başarılı kayıtlar',\n        database: 'Veritabanını yedekle',\n        backupArgs: 'Yedekleme Argümanları',\n        backupArgsHelper:\n            'Listelenmemiş yedekleme argümanları manuel olarak girilip seçilebilir. Örneğin: --no-data yazın ve açılır listeden ilk seçeneği seçin.',\n        singleTransaction:\n            'InnoDB tablolarını tek bir işlem kullanarak yedekler, büyük hacimli veri yedeklemeleri için uygundur',\n        quick: 'Tüm tabloyu belleğe yüklemek yerine verileri satır satır okur, büyük hacimli veri ve düşük bellekli makine yedeklemeleri için uygundur',\n        skipLockTables: 'Tüm tabloları kilitlemeden yedekleme, yüksek eşzamanlılığa sahip veritabanları için uygundur',\n        missBackupAccount: 'Yedekleme hesabı bulunamadı',\n        syncDate: 'Senkronizasyon zamanı ',\n        clean: 'Önbellek temizleme',\n        curl: 'URLe erişim',\n        taskName: 'Ad',\n        cronSpec: 'Tetikleme döngüsü',\n        cronSpecDoc:\n            'Özel çalışma döngüleri yalnızca [dakika saat gün ay hafta] formatını destekler, örneğin, 0 0 * * *. Ayrıntılar için resmi belgelere bakın.',\n        cronSpecHelper: 'Doğru yürütme dönemini girin',\n        cleanHelper:\n            'Bu işlem tüm görev yürütme kayıtlarını, yedekleme dosyalarını ve log dosyalarını kaydeder. Devam etmek istiyor musunuz?',\n        backupContent: 'Yedekleme içeriği',\n        directory: 'Yedekleme dizini',\n        sourceDir: 'Yedekleme dizini',\n        snapshot: 'Sistem anlık görüntüsü',\n        allOptionHelper:\n            'Mevcut görev planı tüm [{0}] öğelerini yedeklemektir. Doğrudan indirme şu anda desteklenmiyor. [{0}] menüsünün yedekleme listesini kontrol edebilirsiniz.',\n        exclusionRules: 'Hariç tutma kuralı',\n        exclusionRulesHelper:\n            'Hariç tutma kurallarını seçin veya girin, her setten sonra Enter basarak devam edin. Hariç tutma kuralları bu yedeklemedeki tüm sıkıştırma işlemlerine uygulanacaktır',\n        default_download_path: 'Varsayılan indirme bağlantısı',\n        saveLocal: 'Yerel yedeklemeleri sakla (bulut depolama kopyalarının sayısı ile aynı)',\n        url: 'URL Adresi',\n        urlHelper: 'Lütfen geçerli bir URL adresi girin',\n        targetHelper: 'Yedekleme hesapları panel ayarlarında sürdürülür.',\n        withImageHelper: 'Uygulama mağazası imajlarını yedekle, ancak bu anlık görüntü dosya boyutunu artıracaktır.',\n        ignoreApp: 'Uygulamaları hariç tut',\n        withImage: 'Uygulama Görüntüsünü Yedekle',\n        retainCopies: 'Kayıtları sakla',\n        retryTimes: 'Yeniden Deneme Girişimleri',\n        timeout: 'Zaman aşımı',\n        retryTimesHelper: '0, başarısızlık sonrası yeniden deneme yok demektir',\n        retainCopiesHelper: 'Yürütme kayıtları ve loglar için saklanacak kopya sayısı',\n        retainCopiesHelper1: 'Yedekleme dosyaları için saklanacak kopya sayısı',\n        retainCopiesUnit: ' kopya (Görüntüle)',\n        cronSpecRule: '{0} satırındaki yürütme dönemi biçimi yanlış. Lütfen kontrol edin ve tekrar deneyin!',\n        cronSpecRule2: 'Yürütme dönemi biçimi yanlış, lütfen kontrol edin ve tekrar deneyin!',\n        perMonthHelper: 'Her ayın {0}. günü {1}:{2}de yürüt',\n        perWeekHelper: 'Her hafta {0}da {1}:{2}de yürüt',\n        perDayHelper: 'Her gün {0}:{1}de yürüt',\n        perHourHelper: 'Her saat {0}. dakikada yürüt',\n        perNDayHelper: 'Her {0} günde bir {1}:{2}de yürüt',\n        perNHourHelper: 'Her {0} saatte bir {1}de yürüt',\n        perNMinuteHelper: 'Her {0} dakikada bir yürüt',\n        perNSecondHelper: 'Her {0} saniyede bir yürüt',\n        perMonth: 'Her ay',\n        perWeek: 'Her hafta',\n        perHour: 'Her saat',\n        perNDay: 'Her N gün',\n        perDay: 'Her gün',\n        perNHour: 'Her N saat',\n        perNMinute: 'Her N dakika',\n        perNSecond: 'Her N saniye',\n        day: 'gün',\n        monday: 'Pazartesi',\n        tuesday: 'Salı',\n        wednesday: 'Çarşamba',\n        thursday: 'Perşembe',\n        friday: 'Cuma',\n        saturday: 'Cumartesi',\n        sunday: 'Pazar',\n        shellContent: 'Betik',\n        executor: 'Yürütücü',\n        errRecord: 'Hatalı kayıt',\n        errHandle: 'Cronjob yürütme başarısız',\n        noRecord: 'Cron Görevini tetikleyin, kayıtları burada göreceksiniz.',\n        cleanData: 'Veriyi temizle',\n        cleanRemoteData: 'Uzak veriyi sil',\n        cleanDataHelper: 'Bu görev sırasında oluşturulan yedekleme dosyasını sil.',\n        noLogs: 'Henüz görev çıktısı yok...',\n        errPath: 'Yedek yolu [{0}] hatası, indirilemez!',\n        cutWebsiteLog: 'Website log döndürme',\n        cutWebsiteLogHelper: 'Döndürülen log dosyaları 1Panel yedek dizinine yedeklenecektir.',\n        syncIpGroup: 'WAF IP gruplarını senkronize et',\n        requestExpirationTime: 'Yükleme isteği son kullanma süresi(Saat)',\n        unitHours: 'Birim: Saat',\n        alertTitle: 'Planlanmış Görev - {0} 「{1}」 Görev Başarısızlık Uyarısı',\n        library: {\n            script: 'Script',\n            syncNow: 'Hemen Senkronize Et',\n            turnOnSync: 'Otomatik Senkronizasyonu Aç',\n            turnOnSyncHelper:\n                'Otomatik senkronizasyonu açmak, her gün sabahın erken saatlerinde otomatik senkronizasyon gerçekleştirecektir',\n            turnOffSync: 'Otomatik Senkronizasyonu Kapat',\n            turnOffSyncHelper:\n                'Otomatik senkronizasyonu kapatmak, betik senkronizasyon gecikmelerine neden olabilir, onaylıyor musunuz?',\n            isInteractive: 'Etkileşimli',\n            interactive: 'Etkileşimli script',\n            interactiveHelper: 'Yürütme sırasında kullanıcı girişi gerektirir ve zamanlanmış görevlerde kullanılamaz.',\n            library: 'Script Kütüphanesi',\n            remoteLibrary: 'Uzak Komut Dosyası Kütüphanesi',\n            create: 'Script Ekle',\n            edit: 'Script Düzenle',\n            groupHelper:\n                'Script özelliklerine göre farklı gruplar ayarlayın, bu daha hızlı script filtreleme işlemlerine olanak tanır.',\n            handleHelper: '{0} üzerinde {1} scriptini çalıştır, devam edilsin mi?',\n            noSuchApp: '{0} servisi algılanmadı. Lütfen önce script kütüphanesi kullanarak hızlıca yükleyin!',\n            syncHelper:\n                'Sistem script kütüphanesini senkronize etmek üzere. Bu işlem sadece sistem scriptlerini etkiler. Devam edilsin mi?',\n        },\n        logHelper1: '1. 1Panel sistem günlüğü',\n        ignoreErr: 'Hataları yok say',\n        ignoreErrHelper: 'Tüm görevlerin tamamlanması için yedekleme hatalarını yok say',\n        cleanLog: 'Günlükleri otomatik temizle',\n        cleanLogscope: 'Günlük saklama',\n    },\n    monitor: {\n        globalFilter: 'Genel Filtre',\n        enableMonitor: 'İzleme Durumu',\n        storeDays: 'Saklama Günleri',\n        defaultNetwork: 'Varsayılan Ağ Kartı',\n        defaultNetworkHelper: 'İzleme ve genel bakış arayüzlerinde görüntülenen varsayılan ağ kartı seçeneği',\n        defaultIO: 'Varsayılan Disk',\n        defaultIOHelper: 'İzleme ve genel bakış arayüzlerinde görüntülenen varsayılan disk seçeneği',\n        cleanMonitor: 'İzleme Kayıtlarını Temizle',\n        cleanHelper: 'Bu işlem GPU dahil tüm izleme kayıtlarını temizleyecektir. Devam etmek istiyor musunuz?',\n        avgLoad: 'Ortalama yük',\n        loadDetail: 'Yük detayı',\n        resourceUsage: 'Kullanım',\n        networkCard: 'Ağ arayüzü',\n        read: 'Okuma',\n        write: 'Yazma',\n        readWriteCount: 'I/O işlemleri',\n        readWriteTime: 'I/O gecikmesi',\n        today: 'Bugün',\n        yesterday: 'Dün',\n        lastNDay: 'Son {0} gün',\n        lastNMonth: 'Son {0} ay',\n        lastHalfYear: 'Son yarım yıl',\n        memory: 'Bellek',\n        percent: 'Yüzde',\n        cache: 'Önbellek',\n        disk: 'Disk',\n        network: 'Ağ',\n        up: 'Yukarı',\n        down: 'Aşağı',\n        interval: 'Toplama Aralığı',\n        intervalHelper: 'Lütfen uygun bir izleme toplama aralığı girin (10 saniye - 12 saat)',\n    },\n    terminal: {\n        local: 'Yerel',\n        defaultConn: 'Varsayılan Bağlantı',\n        defaultConnHelper:\n            'Bu işlem, 【{0}】 için terminal açıldıktan sonra otomatik olarak düğüm terminaline bağlanacaktır. Devam etmek istiyor musunuz?',\n        withReset: 'Bağlantı Bilgilerini Sıfırla',\n        localConnJump:\n            'Varsayılan bağlantı bilgileri [Terminal - Ayarlar] bölümünde yönetilir. Bağlantı başarısız olursa lütfen oradan düzenleyin!',\n        localHelper: '`local` adı sadece sistem yerel tanımlaması için kullanılır',\n        connLocalErr: 'Otomatik kimlik doğrulama yapılamıyor, lütfen yerel sunucu giriş bilgilerini doldurun.',\n        testConn: 'Bağlantıyı test et',\n        saveAndConn: 'Kaydet ve bağlan',\n        connTestOk: 'Bağlantı bilgileri mevcut',\n        connTestFailed: 'Bağlantı mevcut değil, lütfen bağlantı bilgilerini kontrol edin.',\n        host: 'Ana Bilgisayar | Ana Bilgisayarlar',\n        createConn: 'Yeni bağlantı',\n        noHost: 'Ana bilgisayar yok',\n        groupChange: 'Grup değiştir',\n        expand: 'Tümünü genişlet',\n        fold: 'Tümünü daralt',\n        batchInput: 'Toplu işleme',\n        quickCommand: 'Hızlı komut | Hızlı komutlar',\n        noSuchCommand:\n            'İçe aktarılan CSV dosyasında hızlı komut verisi bulunamadı, lütfen kontrol edip tekrar deneyin!',\n        quickCommandHelper: '\"Terminaller -> Terminaller\" altındaki hızlı komutları kullanabilirsiniz.',\n        groupDeleteHelper:\n            'Grup kaldırıldıktan sonra, gruptaki tüm bağlantılar varsayılan gruba taşınacaktır. Devam etmek istiyor musunuz?',\n        command: 'Komut',\n        quickCmd: 'Hızlı komut',\n        addHost: 'Ekle',\n        localhost: 'Yerel sunucu',\n        ip: 'Adres',\n        authMode: 'Kimlik doğrulama',\n        passwordMode: 'Şifre',\n        rememberPassword: 'Kimlik doğrulama bilgilerini hatırla',\n        keyMode: 'Özel Anahtar',\n        key: 'Özel anahtar',\n        keyPassword: 'Özel anahtar şifresi',\n        emptyTerminal: 'Şu anda bağlı terminal yok.',\n        lineHeight: 'Satır Yüksekliği',\n        letterSpacing: 'Harf Aralığı',\n        fontSize: 'Font Boyutu',\n        fontFamily: 'Yazı tipi kümesi',\n        fontFamilySupportHelper:\n            'Yazı tiplerini seçin veya girin. Etkin olup olmaması, tarayıcının bulunduğu sistemde bu yazı tipinin kurulu olmasına bağlıdır; kurulu değilse otomatik olarak geri dönüş yapılır. Boşsa varsayılan yazı tipi kullanılır.',\n        backgroundColor: 'Arka plan rengi',\n        foregroundColor: 'Ön plan rengi',\n        cursorBlink: 'İmleç Yanıp Sönme',\n        cursorStyle: 'İmleç Stili',\n        cursorUnderline: 'Alt Çizgi',\n        cursorBlock: 'Blok',\n        cursorBar: 'Çubuk',\n        scrollback: 'Geri Kaydırma',\n        scrollSensitivity: 'Kaydırma Hassasiyeti',\n        aiStatus: 'AI Terminal',\n        aiSettings: 'AI Terminal Settings',\n        aiAccountHelper:\n            'Komut üretmek ve doldurmak için seçili model hesabını kullanın. Ollama ve vLLM gibi yerel modeller için özel model hesabı kullanın.',\n        aiPrefix: 'Trigger Prefix',\n        aiPrefixHelper:\n            'When a line starts with this prefix and you press Enter, AI command generation will be triggered, for example # or //ai.',\n        aiRiskCommands: 'Risk Command Interception',\n        aiRiskCommandsHelper:\n            'Generated commands matching any of these fragments will be blocked and filled back as comments. Supports add, edit, and delete.',\n        aiAddRiskCommand: 'Add Risk Command',\n        aiRemoveRiskCommand: 'Delete',\n        aiSummary: 'When a line starts with the {0} prefix and you press Enter, AI command generation is triggered.',\n        aiPrefixAsciiVisible:\n            'Only ASCII visible characters are supported. Spaces, CJK characters, and full-width symbols are not allowed.',\n        saveHelper: 'Mevcut terminal yapılandırmasını kaydetmek istediğinizden emin misiniz?',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: 'Bazı kurulum ve kullanım sorunları için lütfen başvurun',\n        },\n        swap: {\n            swap: 'Swap Bölümü',\n            swapHelper1: 'Swap boyutu fiziksel belleğin 1 ila 2 katı olmalı, özel gereksinimlere göre ayarlanabilir;',\n            swapHelper2:\n                'Swap dosyası oluşturmadan önce sistem diskinin yeterli kullanılabilir alanı olduğundan emin olun, çünkü swap dosya boyutu karşılık gelen disk alanını kaplayacaktır;',\n            swapHelper3:\n                'Swap bellek baskısını hafifletmeye yardımcı olabilir, ancak sadece bir alternatiftir. Swap`a aşırı bağımlılık sistem performansında düşüşe yol açabilir. Öncelikle bellek artırma veya uygulama bellek kullanımını optimize etme önerilir;',\n            swapHelper4: 'Normal sistem çalışmasını sağlamak için swap kullanımını düzenli olarak izlemeniz önerilir.',\n            swapDeleteHelper:\n                'Bu işlem {0} Swap bölümünü kaldıracaktır. Sistem güvenliği nedeniyle, karşılık gelen dosya otomatik olarak silinmeyecektir. Silme gerekiyorsa, lütfen manuel olarak ilerleyin!',\n            saveHelper: 'Lütfen önce mevcut ayarları kaydedin!',\n            saveSwap:\n                'Mevcut yapılandırmayı kaydetmek {0} Swap bölüm boyutunu {1} olarak ayarlayacaktır. Devam etmek istiyor musunuz?',\n            swapMin: 'Minimum bölüm boyutu 40 KB`dir. Lütfen değiştirin ve tekrar deneyin!',\n            swapMax: 'Bölüm boyutu için maksimum değer {0}`dır. Lütfen değiştirin ve tekrar deneyin!',\n            swapOff: 'Minimum bölüm boyutu 40 KB`dir. 0`a ayarlamak Swap bölümünü devre dışı bırakacaktır.',\n        },\n        device: {\n            dnsHelper: 'DNS sunucusu',\n            dnsAlert:\n                'Dikkat! /etc/resolv.conf dosyasının yapılandırmasını değiştirmek, sistem yeniden başlatıldıktan sonra dosyayı varsayılan değerlerine geri yükleyecektir.',\n            dnsHelper1:\n                'Birden fazla DNS girişi olduğunda, yeni satırlarda görüntülenmelidirler. örn.\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: 'Ana bilgisayar adı çözümlemesi',\n            hosts: 'Domain',\n            hostAlert:\n                'Gizli yorumlanmış kayıtlar, lütfen görüntülemek veya ayarlamak için Tüm yapılandırma düğmesine tıklayın',\n            toolbox: 'Hızlı ayarlar',\n            hostname: 'Ana bilgisayar adı',\n            passwd: 'Sistem şifresi',\n            passwdHelper: 'Giriş karakterleri $ ve & içeremez',\n            timeZone: 'Sistem saat dilimi',\n            localTime: 'Sunucu saati',\n            timeZoneChangeHelper: 'Sistem saat dilimini değiştirmek servisi yeniden başlatmayı gerektirir. Devam et?',\n            timeZoneHelper:\n                '\"timedatectl\" komutunu yüklemezseniz, saat dilimini değiştiremeyebilirsiniz. Çünkü sistem saat dilimini değiştirmek için bu komutu kullanır.',\n            timeZoneCN: 'Pekin',\n            timeZoneAM: 'Los Angeles',\n            timeZoneNY: 'New York',\n            ntpALi: 'Alibaba',\n            ntpGoogle: 'Google',\n            syncSite: 'NTP sunucusu',\n            hostnameHelper:\n                'Ana bilgisayar adı değişikliği \"hostnamectl\" komutuna bağlıdır. Komut yüklü değilse, değişiklik başarısız olabilir.',\n            userHelper:\n                'Kullanıcı adı alma için \"whoami\" komutuna bağlıdır. Komut yüklü değilse, alma başarısız olabilir.',\n            passwordHelper:\n                'Şifre değişikliği \"chpasswd\" komutuna bağlıdır. Komut yüklü değilse, değişiklik başarısız olabilir.',\n            hostHelper:\n                'Sağlanan içerikte boş değer var. Lütfen kontrol edin ve değişiklik yaptıktan sonra tekrar deneyin!',\n            dnsCheck: 'Kullanılabilirliği Test Et',\n            dnsOK: 'DNS yapılandırma bilgileri mevcut!',\n            dnsTestFailed: 'DNS yapılandırma bilgileri mevcut değil.',\n            syncSiteHelper: 'Sistem saati {0} ile eşitlensin mi?',\n        },\n        fail2ban: {\n            sshPort: 'SSH portunu dinle',\n            sshPortHelper: 'Mevcut Fail2ban ana bilgisayarın SSH bağlantı portunu dinler',\n            unActive: 'Fail2ban servisi şu anda etkin değil.',\n            operation: 'Fail2ban servisinde \"{0}\" işlemini gerçekleştireceksiniz. Devam etmek istiyor musunuz?',\n            fail2banChange: 'Fail2ban Yapılandırma Değişikliği',\n            ignoreHelper:\n                'İzin listesindeki IP listesi engelleme için göz ardı edilecektir. Devam etmek istiyor musunuz?',\n            bannedHelper:\n                'Engelleme listesindeki IP listesi sunucu tarafından engellenecektir. Devam etmek istiyor musunuz?',\n            maxRetry: 'Maksimum yeniden deneme girişimi',\n            banTime: 'Engelleme süresi',\n            banTimeHelper: 'Varsayılan engelleme süresi 10 dakikadır, -1 kalıcı engellemeyi belirtir',\n            banTimeRule: 'Lütfen geçerli bir engelleme süresi veya -1 girin',\n            banAllTime: 'Kalıcı engelleme',\n            findTime: 'Keşif dönemi',\n            banAction: 'Engelleme eylemi',\n            banActionOption: '{0} kullanarak belirtilen IP adreslerini engelle',\n            allPorts: ' (Tüm Portlar)',\n            ignoreIP: 'IP izin listesi',\n            bannedIP: 'IP engelleme listesi',\n            logPath: 'Log yolu',\n            logPathHelper: 'Varsayılan /var/log/secure veya /var/log/auth.log`dur',\n        },\n        ftp: {\n            ftp: 'FTP hesabı | FTP hesapları',\n            notStart: 'FTP servisi şu anda çalışmıyor, lütfen önce başlatın!',\n            operation: 'Bu FTP servisi üzerinde \"{0}\" işlemi gerçekleştirecektir. Devam etmek istiyor musunuz?',\n            noPasswdMsg: 'Mevcut FTP hesap şifresi alınamıyor, lütfen şifreyi ayarlayın ve tekrar deneyin! ',\n            enableHelper:\n                'Seçilen FTP hesabını etkinleştirmek erişim izinlerini geri yükleyecektir. Devam etmek istiyor musunuz?',\n            disableHelper:\n                'Seçilen FTP hesabını devre dışı bırakmak erişim izinlerini iptal edecektir. Devam etmek istiyor musunuz?',\n            syncHelper:\n                'Sunucu ve veritabanı arasında FTP hesap verilerini senkronize et. Devam etmek istiyor musunuz?',\n            dirSystem:\n                'Bu dizin sistem tarafından ayrılmıştır. Değişiklik sistemin çökmesine neden olabilir, kontrol edip tekrar deneyin!',\n            dirHelper: \"FTP'yi etkinleştirmek dizin izinlerini değiştirir, lütfen dikkatle seçin\",\n            dirMsg: 'FTP etkinleştirildiğinde {0} dizininin tamamının izinleri değiştirilecektir. Devam edilsin mi?',\n        },\n        clam: {\n            clam: 'Virüs taraması',\n            cron: 'Zamanlanmış tarama',\n            cronHelper: 'Profesyonel sürüm zamanlanmış tarama özelliğini destekler',\n            specErr: 'Yürütme programı format hatası, lütfen kontrol edin ve tekrar deneyin!',\n            disableMsg:\n                'Zamanlanmış yürütmeyi durdurmak bu tarama görevinin otomatik olarak çalışmasını engelleyecektir. Devam etmek istiyor musunuz?',\n            enableMsg:\n                'Zamanlanmış yürütmeyi etkinleştirmek bu tarama görevinin düzenli aralıklarla otomatik olarak çalışmasına olanak tanıyacaktır. Devam etmek istiyor musunuz?',\n            showFresh: 'İmza güncelleyici servisini göster',\n            hideFresh: 'İmza güncelleyici servisini gizle',\n            clamHelper:\n                'ClamAV için önerilen minimum yapılandırma: 3 GiB RAM veya daha fazla, 2.0 GHz veya daha yüksek tek çekirdekli CPU ve en az 5 GiB kullanılabilir sabit disk alanı.',\n            notStart: 'ClamAV servisi şu anda çalışmıyor, lütfen önce başlatın!',\n            removeRecord: 'Rapor dosyalarını sil',\n            noRecords: 'Taramayı başlatmak için \"Tetikle\" düğmesine tıklayın ve kayıtları burada göreceksiniz.',\n            removeInfected: 'Virüs dosyalarını sil',\n            removeInfectedHelper:\n                'Sunucu güvenliğini ve normal çalışmasını sağlamak için görev sırasında tespit edilen virüs dosyalarını silin.',\n            clamCreate: 'Tarama kuralı oluştur',\n            infectedStrategy: 'Enfekte strateji',\n            removeHelper: 'Virüs dosyalarını sil, dikkatli seçin!',\n            move: 'Taşı',\n            moveHelper: 'Virüs dosyalarını belirtilen dizine taşı',\n            copyHelper: 'Virüs dosyalarını belirtilen dizine kopyala',\n            none: 'Hiçbir şey yapma',\n            noneHelper: 'Virüs dosyalarında herhangi bir işlem yapma',\n            scanDir: 'Tarama dizini',\n            infectedDir: 'Enfekte dizin',\n            scanDate: 'Tarama Tarihi',\n            scanResult: 'Tarama logları sonu',\n            tail: 'Satırlar',\n            infectedFiles: 'Enfekte dosyalar',\n            log: 'Detaylar',\n            clamConf: 'Clam AV daemon',\n            clamLog: '@:toolbox.clam.clamConf logları',\n            freshClam: 'FreshClam',\n            freshClamLog: '@:toolbox.clam.freshClam logları',\n            alertHelper: 'Profesyonel sürüm zamanlanmış tarama ve SMS uyarısını destekler',\n            alertTitle: 'Virüs tarama görevi「{0}」enfekte dosya tespit uyarısı',\n        },\n    },\n    logs: {\n        core: 'Panel Servisi',\n        agent: 'Düğüm İzleme',\n        panelLog: 'Panel logları',\n        operation: 'İşlem logları',\n        login: 'Giriş logları',\n        loginIP: 'Giriş IP',\n        loginAddress: 'Giriş adresi',\n        loginAgent: 'Giriş aracısı',\n        loginStatus: 'Durum',\n        system: 'Sistem logları',\n        deleteLogs: 'Logları temizle',\n        resource: 'Kaynak',\n        detail: {\n            dashboard: 'Genel Bakış',\n            ai: 'AI',\n            groups: 'Grup',\n            hosts: 'Ana Bilgisayar',\n            apps: 'Uygulama',\n            openresty: 'OpenResty',\n            websites: 'Website',\n            containers: 'Konteyner',\n            files: 'Dosya',\n            runtimes: 'Çalışma Zamanı',\n            process: 'İşlem',\n            toolbox: 'Araç Kutusu',\n            backups: 'Yedekleme / Geri Yükleme',\n            tampers: 'Kurcalama',\n            xsetting: 'Arayüz Ayarları',\n            logs: 'Log',\n            settings: 'Ayar',\n            cronjobs: 'Cronjob',\n            databases: 'Veritabanı',\n            waf: 'WAF',\n            licenses: 'Lisans',\n            nodes: 'Düğüm',\n            commands: 'Hızlı Komutlar',\n        },\n        websiteLog: 'Website logları',\n        runLog: 'Çalıştırma logları',\n        errLog: 'Hata logları',\n        task: 'Görev Logu',\n        taskName: 'Görev Adı',\n        taskRunning: 'Çalışıyor',\n    },\n    file: {\n        fileDirNum: '{0} dizin, {1} dosya,',\n        currentDir: 'Dizin',\n        dir: 'Klasör',\n        fileName: 'Dosya adı',\n        search: 'Ara',\n        mode: 'İzinler',\n        editPermissions: '@.lower:file.mode düzenle',\n        owner: 'Sahip',\n        file: 'Dosya',\n        remoteFile: 'Uzak sunucudan indir',\n        share: 'Paylaş',\n        sync: 'Veri Senkronizasyonu',\n        size: 'Boyut',\n        updateTime: 'Değiştirilme',\n        rename: 'Yeniden adlandır',\n        role: 'İzinler',\n        info: 'Özellikleri görüntüle',\n        linkFile: 'Yumuşak bağlantı',\n        shareList: 'Paylaşım listesi',\n        zip: 'Sıkıştırılmış',\n        group: 'Grup',\n        path: 'Yol',\n        public: 'Diğerleri',\n        setRole: 'İzinleri ayarla',\n        link: 'Dosya bağlantısı',\n        rRole: 'Oku',\n        wRole: 'Yaz',\n        xRole: 'Çalıştırılabilir',\n        name: 'Ad',\n        compress: 'Sıkıştır',\n        deCompress: 'Sıkıştırmayı aç',\n        compressType: 'Sıkıştırma formatı',\n        compressDst: 'Sıkıştırma yolu',\n        replace: 'Mevcut dosyaların üzerine yaz',\n        compressSuccess: 'Başarıyla sıkıştırıldı',\n        deCompressSuccess: 'Sıkıştırma başarıyla açıldı',\n        deCompressDst: 'Sıkıştırma açma yolu',\n        linkType: 'Bağlantı türü',\n        softLink: 'Yumuşak bağlantı',\n        hardLink: 'Sert bağlantı',\n        linkPath: 'Bağlantı yolu',\n        selectFile: 'Dosya seç',\n        downloadUrl: 'Uzak URL',\n        downloadStart: 'İndirme başladı',\n        moveSuccess: 'Başarıyla taşındı',\n        copySuccess: 'Başarıyla kopyalandı',\n        pasteMsg: 'Hedef dizinin sağ üst köşesindeki \"Yapıştır\" düğmesine tıklayın',\n        move: 'Taşı',\n        calculate: 'Hesapla',\n        remark: 'Not',\n        setRemark: 'Not ekle',\n        remarkPrompt: 'Bir not girin',\n        remarkPlaceholder: 'Not',\n        remarkToggle: 'Notlar',\n        remarkToggleTip: 'Dosya notlarını yükle',\n        canNotDeCompress: 'Bu dosyanın sıkıştırması açılamaz',\n        uploadSuccess: 'Başarıyla yüklendi',\n        downloadProcess: 'İndirme ilerlemesi',\n        downloading: 'İndiriliyor...',\n        infoDetail: 'Dosya özellikleri',\n        root: 'Kök dizin',\n        list: 'Dosya listesi',\n        sub: 'Alt dizinleri dahil et',\n        downloadSuccess: 'Başarıyla indirildi',\n        theme: 'Tema',\n        language: 'Dil',\n        eol: 'Satır sonu',\n        copyDir: 'Kopyala',\n        paste: 'Yapıştır',\n        changeOwner: 'Kullanıcı ve kullanıcı grubunu değiştir',\n        containSub: 'İzin değişikliğini özyinelemeli olarak uygula',\n        ownerHelper:\n            'PHP çalışma ortamının varsayılan kullanıcısı: kullanıcı grubu 1000:1000, kapsayıcı içindeki ve dışındaki kullanıcıların tutarsız görünmesi normaldir',\n        searchHelper: '* gibi joker karakterleri destekler',\n        uploadFailed: '[{0}] Dosya yükleme hatası',\n        fileUploadStart: '[{0}] yükleniyor...',\n        currentSelect: 'Geçerli seçim: ',\n        unsupportedType: 'Desteklenmeyen dosya türü',\n        deleteHelper:\n            'Aşağıdaki dosyaları silmek istediğinizden emin misiniz? Varsayılan olarak, silme işleminden sonra geri dönüşüm kutusuna gider',\n        fileHelper: 'Not:\\n1. Arama sonuçları sıralanamaz.\\n2. Klasörler boyuta göre sıralanamaz.',\n        forceDeleteHelper: 'Dosyayı kalıcı olarak sil (geri dönüşüm kutusuna girmeden doğrudan silinir)',\n        recycleBin: 'Geri dönüşüm kutusu',\n        sourcePath: 'Orijinal yol',\n        deleteTime: 'Silme zamanı',\n        confirmReduce: 'Aşağıdaki dosyaları geri yüklemek istediğinizden emin misiniz?',\n        reduceSuccess: 'Geri yükleme başarılı',\n        reduce: 'Geri yükle',\n        reduceHelper:\n            'Orijinal yolda aynı ada sahip bir dosya veya dizin varsa, üzerine yazılacaktır. Devam etmek istiyor musunuz?',\n        clearRecycleBin: 'Temizle',\n        clearRecycleBinHelper: 'Geri dönüşüm kutusunu temizlemek istiyor musunuz?',\n        favorite: 'Favoriler',\n        removeFavorite: 'Favorilerden kaldır?',\n        addFavorite: 'Favorilere Ekle/Kaldır',\n        clearList: 'Listeyi temizle',\n        deleteRecycleHelper: 'Aşağıdaki dosyaları kalıcı olarak silmek istediğinizden emin misiniz?',\n        typeErrOrEmpty: '[{0}] dosya türü yanlış veya boş klasör',\n        dropHelper: 'Yüklemek istediğiniz dosyaları buraya sürükleyin',\n        fileRecycleBin: 'Geri dönüşüm kutusunu etkinleştir',\n        fileRecycleBinMsg: '{0} geri dönüşüm kutusu',\n        wordWrap: 'Otomatik satır sonu',\n        deleteHelper2: 'Seçilen dosyayı silmek istediğinizden emin misiniz? Silme işlemi geri alınamaz',\n        ignoreCertificate: 'Güvensiz sunucu bağlantılarına izin ver',\n        ignoreCertificateHelper:\n            'Güvensiz sunucu bağlantılarına izin vermek, veri sızıntısına veya değiştirilmesine yol açabilir. Bu seçeneği yalnızca indirme kaynağına güvendiğinizde kullanın.',\n        uploadOverLimit: 'Dosya sayısı 1000’i aşıyor! Lütfen sıkıştırıp yükleyin',\n        clashDidNotSupport: 'Dosya adlarının .1panel_clash içermesi yasaktır',\n        clashDeleteAlert: '\"Geri Dönüşüm Kutusu\" klasörü silinemez',\n        clashOpenAlert: 'Geri dönüşüm kutusu dizinini açmak için lütfen \"Geri Dönüşüm Kutusu\" düğmesine tıklayın',\n        right: 'İleri',\n        back: 'Geri',\n        top: 'Yukarı',\n        up: 'Geri dön',\n        openWithVscode: 'VS Code ile aç',\n        vscodeHelper:\n            'Lütfen VS Code’un yerel olarak yüklü olduğundan ve SSH Remote eklentisinin yapılandırıldığından emin olun',\n        saveContentAndClose: 'Dosya değiştirildi, kaydedip kapatmak istiyor musunuz?',\n        saveAndOpenNewFile: 'Dosya değiştirildi, kaydedip yeni dosyayı açmak istiyor musunuz?',\n        noEdit: 'Dosya değiştirilmedi, buna gerek yok!',\n        noNameFolder: 'İsimsiz klasör',\n        noNameFile: 'İsimsiz dosya',\n        minimap: 'Kod mini haritası',\n        fileCanNotRead: 'Dosya okunamıyor',\n        previewTruncated: 'Dosya çok büyük, yalnızca son kısım gösteriliyor',\n        previewEmpty: 'Dosya boş veya metin dosyası değil',\n        previewLargeFile: 'Önizleme',\n        panelInstallDir: '1Panel kurulum dizini silinemez',\n        wgetTask: 'İndirme Görevi',\n        existFileTitle: 'Aynı ada sahip dosya uyarısı',\n        existFileHelper: 'Yüklenen dosya, aynı ada sahip bir dosya içeriyor, üzerine yazmak istiyor musunuz?',\n        existFileSize: 'Dosya boyutu (yeni -> eski)',\n        existFileDirHelper: 'Seçilen dosya/klasörün adı çakışıyor. Lütfen dikkatle ilerleyin! \\n',\n        coverDirHelper: 'Yerine geçecek seçilen klasörler hedef yola kopyalanacak!',\n        noSuchFile: 'Dosya veya dizin bulunamadı. Lütfen kontrol edin ve tekrar deneyin.',\n        setting: 'Ayar',\n        showHide: 'Gizli dosyaları göster',\n        noShowHide: 'Gizli dosyaları gösterme',\n        cancelUpload: 'Yüklemeyi İptal Et',\n        cancelUploadHelper: 'Yüklemeyi iptal etmek ister misiniz, iptal sonrası yükleme listesi temizlenecektir.',\n        keepOneTab: 'En az bir sekme açık kalmalıdır',\n        notCanTab: 'Daha fazla sekme eklenemez',\n        convert: 'Formatı Dönüştür',\n        converting: 'Dönüştürülüyor',\n        fileCanNotConvert: 'Bu dosya format dönüşümünü desteklemiyor',\n        formatType: 'Format Türü',\n        sourceFormat: 'Kaynak Format',\n        sourceFile: 'Kaynak Dosya',\n        saveDir: 'Kaydetme Dizini',\n        deleteSourceFile: 'Kaynak Dosyayı Sil',\n        convertHelper: 'Seçilen dosyaları başka bir formata dönüştür',\n        convertHelper1: 'Lütfen dönüştürülecek dosyaları seçin',\n        execConvert: 'Dönüştürmeyi başlatın. Dönüştürme günlüklerini Görev Merkezi’nde görüntüleyebilirsiniz',\n        convertLogs: 'Dönüştürme Günlükleri',\n        formatConvert: 'Format Dönüştürme',\n    },\n    ssh: {\n        autoStart: 'Otomatik başlat',\n        enable: 'Otomatik başlatmayı etkinleştir',\n        disable: 'Otomatik başlatmayı devre dışı bırak',\n        sshAlert:\n            'Liste verileri, oturum açma tarihine göre sıralanır. Saat dilimini değiştirmek veya başka işlemler yapmak, oturum açma günlüklerinin tarihinde sapmalara neden olabilir.',\n        sshAlert2:\n            'Kaba kuvvet saldırılarını deneyen IP adreslerini engellemek için \"Araç Kutusu\"nda \"Fail2ban\" kullanabilirsiniz, bu ana bilgisayarın güvenliğini artırır.',\n        sshOperate: 'SSH servisinde \"{0}\" işlemi gerçekleştirilecek. Devam etmek istiyor musunuz?',\n        sshChange: 'SSH Ayarı',\n        sshChangeHelper: 'Bu işlem \"{0}\" değerini \"{1}\" olarak değiştirdi. Devam etmek istiyor musunuz?',\n        sshFileChangeHelper:\n            'Yapılandırma dosyasını değiştirmek, hizmet kullanılabilirliğini etkileyebilir. Bu işlemi yaparken dikkatli olun. Devam etmek istiyor musunuz?',\n        port: 'Port',\n        portHelper: 'SSH servisinin dinlediği portu belirtir.',\n        listenAddress: 'Dinleme adresi',\n        allV4V6: '0.0.0.0:{0}(IPv4) ve :::{0}(IPv6)',\n        listenHelper:\n            'IPv4 ve IPv6 ayarlarını boş bırakmak, \"0.0.0.0:{0}(IPv4)\" ve \":::{0}(IPv6)\" üzerinde dinlemeyi sağlar.',\n        addressHelper: 'SSH servisinin dinlediği adresi belirtir.',\n        permitRootLogin: 'Kök kullanıcı girişine izin ver',\n        rootSettingHelper: 'Kök kullanıcı için varsayılan giriş yöntemi \"SSH girişine izin ver\"dir.',\n        rootHelper1: 'SSH girişine izin ver',\n        rootHelper2: 'SSH girişini devre dışı bırak',\n        rootHelper3: 'Yalnızca anahtar girişine izin verilir',\n        rootHelper4: 'Yalnızca önceden tanımlanmış komutlar çalıştırılabilir. Başka işlemler yapılamaz.',\n        passwordAuthentication: 'Parola kimlik doğrulaması',\n        pwdAuthHelper:\n            'Parola kimlik doğrulamasının etkinleştirilip etkinleştirilmeyeceği. Bu parametre varsayılan olarak etkindir.',\n        pubkeyAuthentication: 'Anahtar kimlik doğrulaması',\n        privateKey: 'Özel Anahtar',\n        publicKey: 'Genel Anahtar',\n        password: 'Parola',\n        createMode: 'Oluşturma Yöntemi',\n        generate: 'Otomatik Oluştur',\n        unSyncPass: 'Anahtar parolası senkronize edilemez',\n        syncHelper:\n            'Eşitleme işlemi geçersiz anahtarları temizleyecek ve yeni tam anahtar çiftlerini eşitleyecek. Devam edilsin mi?',\n        input: 'Manuel Giriş',\n        import: 'Dosya Yükleme',\n        authKeys: 'Yetki Anahtarları',\n        authKeysHelper: 'Mevcut ortak anahtar bilgilerini kaydet?',\n        pubkey: 'Anahtar bilgisi',\n        pubKeyHelper: 'Mevcut anahtar bilgileri yalnızca {0} kullanıcısı için geçerlidir',\n        encryptionMode: 'Şifreleme modu',\n        passwordHelper: '6 ila 10 hane ve İngilizce harfler içerebilir',\n        reGenerate: 'Anahtarı yeniden oluştur',\n        keyAuthHelper: 'Anahtar kimlik doğrulamasının etkinleştirilip etkinleştirilmeyeceği.',\n        useDNS: 'DNS kullanımı',\n        dnsHelper:\n            'SSH sunucusunda DNS çözümleme işlevinin, bağlantının kimliğini doğrulamak için etkinleştirilip etkinleştirilmeyeceğini kontrol eder.',\n        analysis: 'İstatistiksel bilgi',\n        denyHelper:\n            'Aşağıdaki adreslerde \"reddet\" işlemi gerçekleştiriliyor. Ayar yapıldıktan sonra IP, sunucuya erişimden yasaklanacak. Devam etmek istiyor musunuz?',\n        acceptHelper:\n            'Aşağıdaki adreslerde \"kabul et\" işlemi gerçekleştiriliyor. Ayar yapıldıktan sonra IP, normal erişimi geri kazanacak. Devam etmek istiyor musunuz?',\n        noAddrWarning: 'Şu anda [{0}] adres seçilmedi. Lütfen kontrol edin ve tekrar deneyin!',\n        loginLogs: 'SSH Günlükleri',\n        loginMode: 'Mod',\n        authenticating: 'Anahtar',\n        publickey: 'Anahtar',\n        belong: 'Ait',\n        local: 'Yerel',\n        session: 'Oturum | Oturumlar',\n        loginTime: 'Oturum açma zamanı',\n        loginIP: 'Oturum açma IP’si',\n        stopSSHWarn: 'Bu SSH bağlantısını kesmek ister misiniz?',\n        remote: 'Uzak SSH',\n    },\n    setting: {\n        panel: 'Panel',\n        user: 'Panel kullanıcısı',\n        userChange: 'Panel kullanıcısını değiştir',\n        userChangeHelper: 'Panel kullanıcısını değiştirmek sizi oturumdan çıkaracak. Devam etmek istiyor musunuz?',\n        passwd: 'Panel parolası',\n        emailHelper: 'Parola kurtarma için',\n        watermark: 'Filigran Ayarları',\n        watermarkContent: 'Filigran İçeriği',\n        contentHelper:\n            '{0} düğüm adını, {1} düğüm adresini temsil eder. Değişkenleri kullanabilir veya özel adlar girebilirsiniz.',\n        watermarkColor: 'Filigran Rengi',\n        watermarkFont: 'Filigran Yazı Tipi Boyutu',\n        watermarkHeight: 'Filigran Yüksekliği',\n        watermarkWidth: 'Filigran Genişliği',\n        watermarkRotate: 'Döndürme Açısı',\n        watermarkGap: 'Aralık',\n        watermarkCloseHelper: 'Sistem filigran ayarlarını kapatmak istediğinizden emin misiniz?',\n        watermarkOpenHelper: 'Mevcut sistem filigran ayarlarını kaydetmek istediğinizden emin misiniz?',\n        title: 'Panel takma adı',\n        panelPort: 'Panel portu',\n        titleHelper:\n            '3 ila 30 karakter uzunluğunda, İngilizce harfler, Çince karakterler, sayılar, boşluklar ve yaygın özel karakterler destekler',\n        portHelper:\n            'Önerilen port aralığı 8888 ila 65535’tir. Not: Sunucuda bir güvenlik grubu varsa, yeni portu önceden güvenlik grubundan izin verin',\n        portChange: 'Port değişikliği',\n        portChangeHelper: 'Servis portunu değiştir ve servisi yeniden başlat. Devam etmek istiyor musunuz?',\n        theme: 'Tema',\n        menuTabs: 'Menü sekmeleri',\n        dark: 'Koyu',\n        darkGold: 'Koyu Altın',\n        light: 'Açık',\n        auto: 'Sistemi takip et',\n        language: 'Dil',\n        runtimeEnv: 'Çalışma ortamı',\n        docSource: 'Doküman kaynağı',\n        withByRegion: 'Çalışma bölgesini takip et (Varsayılan)',\n        withByLang: 'Sistem dilini takip et',\n        region: 'Çalışma bölgesi',\n        cn: 'Çin anakarası',\n        intl: 'Küresel',\n        regionHelper: 'Bölge değiştirildiğinde aşağıdaki kaynaklar güncellenir:',\n        regionHelper1: 'Sistem kurulum paketleri',\n        regionHelper2: 'Uygulama mağazası ve betik kütüphanesi',\n        regionHelper3: 'Kullanım kılavuzu ve ilgili dokümanlar',\n        regionHelper4: 'Bu işlem sonraki indirme ve erişimleri etkileyebilir. Lütfen dikkatli olun.',\n        regionTip:\n            'Çalışma bölgesi, sistem güncelleme kaynağını ve kurulum paketi indirme adresini belirgin şekilde etkiler.',\n        docSourceTip: 'Doküman kaynağı, dokümantasyon ve güncelleme günlüğü bağlantılarının dilini belirler.',\n        languageHelper:\n            'Varsayılan olarak tarayıcı dilini takip eder. Bu parametre yalnızca geçerli tarayıcıda etkilidir',\n        sessionTimeout: 'Oturum zaman aşımı',\n        sessionTimeoutError: 'Minimum oturum zaman aşımı 300 saniyedir',\n        sessionTimeoutHelper: 'Panel, {0} saniye boyunca işlem yapılmazsa otomatik olarak oturumu kapatır.',\n        systemIP: 'Sistem adresi',\n        systemIPHelper:\n            'Adres, uygulama yönlendirmesi, konteyner erişimi ve diğer işlevler için kullanılacaktır. Her düğüm farklı bir adresle yapılandırılabilir.',\n        proxy: 'Sunucu vekili',\n        proxyHelper: 'Vekil sunucuyu ayarladıktan sonra aşağıdaki senaryolarda etkili olacaktır:',\n        proxyHelper1: 'Uygulama mağazasından kurulum paketi indirme ve senkronizasyon (Yalnızca Profesyonel sürüm)',\n        proxyHelper2: 'Sistem güncelleme ve güncelleme bilgisi alma (Yalnızca Profesyonel sürüm)',\n        proxyHelper3: 'Sistem lisansı doğrulama ve senkronizasyon',\n        proxyHelper4:\n            'Docker imajı çekme gibi ağ istekleri vekil sunucu üzerinden yapılacaktır (Yalnızca Profesyonel sürüm)',\n        proxyHelper5: 'Sistem tipi komut kütüphaneleri için birleşik indirme ve senkronizasyon (Profesyonel)',\n        proxyHelper6: 'Sertifika başvurusu (Profesyonel)',\n        proxyType: 'Vekil türü',\n        proxyUrl: 'Vekil Adresi',\n        proxyPort: 'Vekil Portu',\n        proxyPasswdKeep: 'Parolayı Hatırla',\n        proxyDocker: 'Docker Vekili',\n        proxyDockerHelper:\n            'Vekil sunucu yapılandırmasını Docker’a senkronize et, çevrimdışı sunucu imaj çekme gibi işlemleri destekler',\n        syncToNode: 'Düğüme Senkronize Et',\n        syncToNodeHelper: 'Ayarları diğer düğümlere senkronize et',\n        nodes: 'Düğüm',\n        selectNode: 'Düğüm Seç',\n        selectNodeError: 'Lütfen bir düğüm seçin',\n        apiInterface: 'API’yi Etkinleştir',\n        apiInterfaceClose: 'Kapatıldığında, API arayüzlerine erişilemez. Devam etmek istiyor musunuz?',\n        apiInterfaceHelper: 'Üçüncü taraf uygulamaların API’ye erişmesine izin ver.',\n        apiInterfaceAlert1: 'Üretim ortamlarında etkinleştirmeyin çünkü bu, sunucu güvenlik risklerini artırabilir.',\n        apiInterfaceAlert2:\n            'Potansiyel güvenlik tehditlerini önlemek için üçüncü taraf uygulamaları API’yi çağırmak için kullanmayın.',\n        apiInterfaceAlert3: 'API belgesi:',\n        apiInterfaceAlert4: 'Kullanım belgesi:',\n        apiKey: 'API anahtarı',\n        apiKeyHelper: 'API anahtarı, üçüncü taraf uygulamaların API’ye erişmesi için kullanılır.',\n        ipWhiteList: 'IP izin listesi',\n        ipWhiteListEgs: 'Her satıra bir tane. Örneğin,\\n172.161.10.111\\n172.161.10.0/24',\n        ipWhiteListHelper: 'İzin listesindeki IP’ler API’ye erişebilir, 0.0.0.0/0 (tüm IPv4), ::/0 (tüm IPv6)',\n        apiKeyValidityTime: 'Arayüz anahtarının geçerlilik süresi',\n        apiKeyValidityTimeEgs: 'Arayüz anahtarının geçerlilik süresi (dakika cinsinden)',\n        apiKeyValidityTimeHelper:\n            'Arayüz zaman damgası, mevcut zaman damgasıyla farkı (dakika cinsinden) izin verilen aralıktaysa geçerlidir. 0 değeri doğrulamayı devre dışı bırakır.',\n        apiKeyReset: 'Arayüz anahtarı sıfırlama',\n        apiKeyResetHelper: 'İlişkili anahtar servisi geçersiz hale gelecektir. Lütfen servise yeni bir anahtar ekleyin',\n        confDockerProxy: 'Docker vekilini yapılandır',\n        restartNowHelper: 'Docker vekilini yapılandırmak, Docker servisinin yeniden başlatılmasını gerektirir.',\n        restartNow: 'Hemen yeniden başlat',\n        restartLater: 'Daha sonra manuel olarak yeniden başlat',\n        systemIPWarning: 'Sunucu adresi şu anda ayarlanmadı. Önce kontrol panelinde ayarlayın.',\n        systemIPWarning1: 'Geçerli sunucu adresi {0} olarak ayarlandı ve hızlı yönlendirme mümkün değil!',\n        syncTime: 'Sunucu Saati',\n        timeZone: 'Saat Dilimi',\n        timeZoneChangeHelper:\n            'Saat dilimini değiştirmek servisin yeniden başlatılmasını gerektirir. Devam etmek istiyor musunuz?',\n        timeZoneHelper:\n            'Saat dilimi değişikliği, sistem timedatectl servisine bağlıdır. 1Panel servisi yeniden başlatıldıktan sonra etkili olur.',\n        timeZoneCN: 'Pekin',\n        timeZoneAM: 'Los Angeles',\n        timeZoneNY: 'New York',\n        ntpALi: 'Alibaba',\n        ntpGoogle: 'Google',\n        syncSite: 'NTP Sunucusu',\n        syncSiteHelper:\n            'Bu işlem, sistem saati senkronizasyonu için {0}’ı kaynak olarak kullanacak. Devam etmek istiyor musunuz?',\n        changePassword: 'Parolayı Değiştir',\n        oldPassword: 'Orijinal parola',\n        newPassword: 'Yeni parola',\n        retryPassword: 'Parolayı onayla',\n        noSpace: 'Girilen bilgiler boşluk karakteri içeremez',\n        duplicatePassword: 'Yeni parola, orijinal parolayla aynı olamaz, lütfen tekrar girin!',\n        diskClean: 'Önbellek temizleme',\n        developerMode: 'Önizleme Programı',\n        developerModeHelper:\n            'Yayınlanmadan önce yeni özellikleri ve düzeltmeleri deneyimleyebilir ve erken geri bildirim sağlayabilirsiniz.',\n        thirdParty: 'Üçüncü taraf hesaplar',\n        scope: 'Kapsam',\n        public: 'Genel',\n        publicHelper:\n            'Genel tip yedekleme hesapları her alt düğüme senkronize edilir ve alt düğümler bunları birlikte kullanabilir',\n        private: 'Özel',\n        privateHelper:\n            'Özel tip yedekleme hesapları yalnızca mevcut düğümde oluşturulur ve yalnızca mevcut düğümün kullanımı içindir',\n        noTypeForCreate: 'Şu anda oluşturulmuş bir yedekleme türü yok',\n        LOCAL: 'Sunucu diski',\n        OSS: 'Ali OSS',\n        S3: 'Amazon S3',\n        mode: 'Mod',\n        MINIO: 'MinIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'WebDAV, Alist bağlantısı için resmi belgelere bakabilirsiniz',\n        UPYUN: 'UPYUN',\n        ALIYUN: 'Aliyun Drive',\n        ALIYUNHelper:\n            'Aliyun Drive’da istemci dışı indirmeler için mevcut maksimum sınır 100 MB’dir. Bu sınırı aşmak, istemci üzerinden indirme gerektirir.',\n        ALIYUNRecover:\n            'Aliyun Drive’da istemci dışı indirmeler için mevcut maksimum sınır 100 MB’dir. Bu sınırı aşmak, istemci üzerinden yerel cihaza indirme ve ardından anlık görüntü senkronizasyonu ile kurtarma gerektirir.',\n        GoogleDrive: 'Google Drive',\n        analysis: 'Analiz',\n        analysisHelper:\n            'Gerekli parçaları otomatik olarak ayrıştırmak için tüm token içeriğini yapıştırın. Özel işlemler için lütfen resmi belgelere bakın.',\n        serviceName: 'Servis Adı',\n        operator: 'Operatör',\n        OneDrive: 'Microsoft OneDrive',\n        isCN: 'Yüzyıl İnternet',\n        isNotCN: 'Uluslararası Sürüm',\n        client_id: 'İstemci Kimliği',\n        client_secret: 'İstemci sırrı',\n        redirect_uri: 'Yönlendirme URL’si',\n        onedrive_helper: 'Özel yapılandırma için resmi belgelere bakılabilir',\n        clickToRefresh: 'Yenilemek için tıklayın',\n        refreshTime: 'Token Yenileme Zamanı',\n        refreshStatus: 'Token Yenileme Durumu',\n        backupDir: 'Yedekleme dizini',\n        codeWarning: 'Geçerli yetkilendirme kodu formatı yanlış, lütfen tekrar kontrol edin!',\n        code: 'Yetki kodu',\n        codeHelper:\n            '\"Edin\" düğmesine tıklayın, ardından {0}’a giriş yapın ve yönlendirilen bağlantıdaki \"code\" sonrası içeriği kopyalayın. Bu içeriği giriş kutusuna yapıştırın. Özel talimatlar için lütfen resmi belgelere bakın.',\n        googleHelper:\n            'Lütfen önce bir Google uygulaması oluşturun ve istemci bilgilerini alın, formu doldurun ve al butonuna tıklayın. Belirli işlemler için lütfen resmi belgelere bakın.',\n        loadCode: 'Edin',\n        COS: 'Tencent COS',\n        ap_beijing_1: 'Pekin Bölgesi 1',\n        ap_beijing: 'Pekin',\n        ap_nanjing: 'Nanjing',\n        ap_shanghai: 'Şanghay',\n        ap_guangzhou: 'Guangzhou',\n        ap_chengdu: 'Chengdu',\n        ap_chongqing: 'Chongqing',\n        ap_shenzhen_fsi: 'Shenzhen Finansal',\n        ap_shanghai_fsi: 'Şanghay Finansal',\n        ap_beijing_fsi: 'Pekin Finansal',\n        ap_hongkong: 'Hong Kong, Çin',\n        ap_singapore: 'Singapur',\n        ap_mumbai: 'Mumbai',\n        ap_jakarta: 'Cakarta',\n        ap_seoul: 'Seul',\n        ap_bangkok: 'Bangkok',\n        ap_tokyo: 'Tokyo',\n        na_siliconvalley: 'Silikon Vadisi (ABD Batı)',\n        na_ashburn: 'Ashburn (ABD Doğu)',\n        na_toronto: 'Toronto',\n        sa_saopaulo: 'Sao Paulo',\n        eu_frankfurt: 'Frankfurt',\n        KODO: 'Qiniu Kodo',\n        scType: 'Depolama türü',\n        typeStandard: 'Standart',\n        typeStandard_IA: 'Standart_IA',\n        typeArchive: 'Arşiv',\n        typeDeep_Archive: 'Derin_Arşiv',\n        scLighthouse: 'Varsayılan, Hafif nesne depolama yalnızca bu depolama türünü destekler',\n        scStandard:\n            'Standart Depolama, gerçek zamanlı erişim gerektiren çok sayıda sıcak dosya içeren iş senaryoları için uygundur, sık veri etkileşimi vb.',\n        scStandard_IA:\n            'Düşük frekanslı depolama, nispeten düşük erişim frekansına sahip iş senaryoları için uygundur ve verileri en az 30 gün saklar.',\n        scArchive: 'Arşiv depolama, son derece düşük erişim frekansına sahip iş senaryoları için uygundur.',\n        scDeep_Archive:\n            'Dayanıklı soğuk depolama, son derece düşük erişim frekansına sahip iş senaryoları için uygundur.',\n        archiveHelper:\n            'Arşiv depolama dosyaları doğrudan indirilemez ve önce ilgili bulut hizmeti sağlayıcısının web sitesi üzerinden geri yüklenmelidir. Lütfen dikkatli kullanın!',\n        backupAlert:\n            'Bir bulut sağlayıcısı S3 protokolüyle uyumluysa, yedekleme için doğrudan Amazon S3 kullanılabilir.',\n        domain: 'Hızlandırma alanı',\n        backupAccount: 'Yedekleme hesabı | Yedekleme hesapları',\n        loadBucket: 'Kova al',\n        accountName: 'Hesap adı',\n        accountKey: 'Hesap anahtarı',\n        address: 'Adres',\n        path: 'Yol',\n        safe: 'Güvenlik',\n        passkey: 'Passkey',\n        passkeyManage: 'Yönet',\n        passkeyKeyManagement: 'Anahtar yönetimi',\n        passkeyHelper: 'Hızlı giriş için en fazla 5 passkey bağlanabilir',\n        passkeyRequireSSL: 'Passkey kullanmak için alan adı bağlayın ve HTTPS erişimi sağlayın',\n        passkeyTrustedProxies: 'Güvenilir proxyler',\n        passkeyTrustedProxiesHelper:\n            'Sadece bu IP/CIDR kaynaklarından gelen isteklerde Forwarded ve X-Forwarded-Proto HTTPS belirlenmesinde kullanılır',\n        passkeyNotSupported: 'Tarayıcı/ortam passkey desteklemiyor; alan adı ve HTTPS erişimini doğrulayın',\n        passkeyPrereqTitle: 'Passkey için aşağıdaki ön koşullar gereklidir',\n        passkeyPrereqBindDomain: '1Panel alan adı bağlama yapılandırıldı',\n        passkeyPrereqHttps: 'HTTPS üzerinden güvenli erişim sağlandı',\n        passkeyPrereqBrowser: 'Ortam Passkey destekliyor (WebAuthn + platform doğrulayıcı)',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable:\n            'Sistem tarayıcısı kullanın ve panelin HTTPS üzerinden, bağlı bir alan adıyla açıldığını doğrulayın.',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            'Bu ortam cihaz kimlik doğrulamasını kullanamıyor. Daha yeni bir işletim sisteminde sistem tarayıcısını deneyin.',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            \"Ekran kilidi / parmak izi / yüz tanıma / Windows Hello'yu etkinleştirip tekrar deneyin.\",\n        passkeyPrereqBrowserDetailDetectFailed:\n            'Algılama başarısız oldu. Sayfayı yenileyip tekrar deneyin veya sistem tarayıcısına geçin.',\n        passkeyPrereqGoSetup: 'Yapılandır',\n        passkeyCount: 'Bağlı {0}/{1}',\n        passkeyName: 'Ad',\n        passkeyNameHelper: 'Cihazları ayırt etmek için bir ad girin',\n        passkeyAdd: 'Passkey ekle',\n        passkeyCreatedAt: 'Oluşturulma',\n        passkeyLastUsedAt: 'Son kullanım',\n        passkeyDeleteConfirm: 'Silindikten sonra bu passkey ile giriş yapılamaz. Devam edilsin mi?',\n        passkeyLimit: 'En fazla 5 passkey bağlanabilir',\n        passkeyFailed: 'Passkey kaydı başarısız oldu, lütfen panel SSL sertifikasının güvenilir olduğundan emin olun',\n        bindInfo: 'Bağlama bilgisi',\n        bindAll: 'Tümünü Dinle',\n        bindInfoHelper:\n            'Servis dinleme adresini veya protokolü değiştirmek, hizmetin kullanılamamasına neden olabilir. Devam etmek istiyor musunuz?',\n        ipv6: 'IPv6’yı Dinle',\n        bindAddress: 'Dinleme adresi',\n        entrance: 'Giriş',\n        showEntrance: '\"Genel Bakış\" sayfasında devre dışı uyarısını göster',\n        entranceHelper:\n            'Güvenlik girişini etkinleştirmek, panele yalnızca belirtilen güvenlik girişi üzerinden oturum açılmasına izin verecektir.',\n        entranceError:\n            'Lütfen 5-116 karakterlik bir güvenli oturum açma giriş noktası girin, yalnızca sayılar veya harfler desteklenir.',\n        entranceInputHelper: 'Güvenlik girişini devre dışı bırakmak için boş bırakın.',\n        randomGenerate: 'Rastgele',\n        expirationTime: 'Son Kullanım Tarihi',\n        unSetting: 'Ayarlanmadı',\n        noneSetting:\n            'Panel parolasının son kullanma tarihini ayarlayın. Son kullanma tarihinden sonra parolayı sıfırlamanız gerekir',\n        expirationHelper: 'Parola son kullanma süresi [0] gün ise, parola son kullanma işlevi devre dışı bırakılır',\n        days: 'Son Kullanım Günleri',\n        expiredHelper: 'Geçerli parola süresi doldu. Lütfen parolayı tekrar değiştirin.',\n        timeoutHelper:\n            '[ {0} gün ] Panel parolası süresi dolmak üzere. Süre dolduktan sonra parolayı sıfırlamanız gerekir',\n        complexity: 'Karmaşıklık doğrulaması',\n        complexityHelper:\n            'Etkinleştirildiğinde, parola doğrulama kuralı şu olacaktır: 8-30 karakter, İngilizce, sayılar ve en az iki özel karakter içerir.',\n        bindDomain: 'Alan adı bağla',\n        unBindDomain: 'Alan adı bağlamasını kaldır',\n        panelSSL: 'Panel SSL',\n        unBindDomainHelper:\n            'Alan adı bağlamasını kaldırma işlemi sistem güvenliğini etkileyebilir. Devam etmek istiyor musunuz?',\n        bindDomainHelper: 'Alan adı bağlandıktan sonra, yalnızca o alan adı 1Panel servisine erişebilir.',\n        bindDomainHelper1: 'Alan adı bağlamasını devre dışı bırakmak için boş bırakın.',\n        bindDomainWarning:\n            'Alan adı bağlamasından sonra oturumunuz kapatılacak ve yalnızca ayarlarınızda belirtilen alan adı üzerinden 1Panel servisine erişebilirsiniz. Devam etmek istiyor musunuz?',\n        allowIPs: 'Yetkili IP',\n        unAllowIPs: 'Yetkisiz IP',\n        unAllowIPsWarning:\n            'Boş bir IP’yi yetkilendirmek, tüm IP’lerin sisteme erişmesine izin verecek ve bu, sistem güvenliğini etkileyebilir. Devam etmek istiyor musunuz?',\n        allowIPsHelper:\n            'Yetkili IP adres listesini ayarladıktan sonra, yalnızca listedeki IP adresi panel servisine erişebilir.',\n        allowIPsWarning:\n            'Yetkili IP adres listesini ayarladıktan sonra, yalnızca listedeki IP adresi panel servisine erişebilir. Devam etmek istiyor musunuz?',\n        allowIPsHelper1: 'IP adresi kısıtlamasını devre dışı bırakmak için boş bırakın.',\n        allowIPEgs: 'Her satıra bir tane. Örneğin,\\n172.16.10.111\\n172.16.10.0/24',\n        mfa: 'İki Aşamalı Doğrulama',\n        mfaClose: 'MFA’yı devre dışı bırakmak servisin güvenliğini azaltabilir. Devam etmek istiyor musunuz?',\n        secret: 'Gizli',\n        mfaInterval: 'Yenileme aralığı(saniye)',\n        mfaTitleHelper:\n            'Başlık, farklı 1Panel ana bilgisayarlarını ayırt etmek için kullanılır. Başlığı değiştirdikten sonra QR kodunu tekrar tarayın veya gizli anahtarı manuel olarak ekleyin.',\n        mfaIntervalHelper:\n            'Yenileme aralığını değiştirdikten sonra QR kodunu tekrar tarayın veya gizli anahtarı manuel olarak ekleyin.\\nBazı doğrulayıcı uygulamalar yalnızca 30 saniyelik aralığı destekler (ör. Google/Microsoft Authenticator).',\n        mfaAlert:\n            'Tek kullanımlık token, geçerli zamana dayalı olarak dinamik olarak oluşturulan 6 haneli bir sayıdır. Sunucu saatinin senkronize olduğundan emin olun.',\n        mfaHelper: 'Etkinleştirildiğinde, tek kullanımlık token doğrulanmalıdır.',\n        mfaHelper1: 'Bir kimlik doğrulama uygulaması indirin, örneğin,',\n        mfaHelper2:\n            'Tek kullanımlık token’ı elde etmek için, kimlik doğrulama uygulamanızla aşağıdaki QR kodunu tarayın veya gizli anahtarı kimlik doğrulama uygulamanıza kopyalayın.',\n        mfaHelper3: 'Uygulamadan altı haneli rakamı girin',\n        mfaCode: 'Tek kullanımlık token',\n        sslChangeHelper: 'Https ayarını değiştir ve servisi yeniden başlat. Devam etmek istiyor musunuz?',\n        sslDisable: 'Devre dışı bırak',\n        sslDisableHelper:\n            'Https servisi devre dışı bırakılırsa, değişikliğin etkili olması için panelin yeniden başlatılması gerekir. Devam etmek istiyor musunuz?',\n        noAuthSetting: 'Yetkisiz ayar',\n        noAuthSettingHelper:\n            'Kullanıcılar belirtilen güvenlik girişiyle oturum açmadığında veya panele belirtilen IP veya alan adından erişmediğinde, bu yanıt panel özelliklerini gizleyebilir.',\n        responseSetting: 'Yanıt ayarı',\n        help200: 'Yardım Sayfası',\n        error400: 'Hatalı İstek',\n        error401: 'Yetkisiz',\n        error403: 'Yasak',\n        error404: 'Bulunamadı',\n        error408: 'İstek Zaman Aşımı',\n        error416: 'Aralık Karşılanamadı',\n        error444: 'Bağlantı Kapalı',\n        error500: 'Dahili Sunucu Hatası',\n        https: 'Panel için HTTPS ayarlamak erişim güvenliğini artırır',\n        strictHelper: 'HTTPS olmayan trafik panele bağlanamaz',\n        muxHelper:\n            \"Panel hem HTTP hem de HTTPS protokollarını dinleyecek ve HTTP'yi HTTPS'ye yönlendirecektir, bu modda panel güvenliğini azaltabilir\",\n        certType: 'Sertifika türü',\n        selfSigned: 'Kendi kendine imzalı',\n        selfSignedHelper:\n            'Tarayıcılar kendi kendine imzalı sertifikalara güvenmeyebilir ve güvenlik uyarıları gösterebilir.',\n        select: 'Seç',\n        domainOrIP: 'Alan adı veya IP:',\n        timeOut: 'Zaman aşımı',\n        rootCrtDownload: 'Kök sertifika indirme',\n        primaryKey: 'Birincil anahtar',\n        certificate: 'Sertifika',\n        backupJump:\n            'Geçerli yedekleme listesinde olmayan yedekleme dosyaları, lütfen dosya dizininden indirmeyi deneyin ve yedekleme için içe aktarın.',\n        snapshot: 'Anlık Görüntü | Anlık Görüntüler',\n        noAppData: 'Seçim için mevcut sistem uygulaması yok',\n        noBackupData: 'Seçim için mevcut yedekleme verisi yok',\n        stepBaseData: 'Temel Veri',\n        stepAppData: 'Sistem Uygulaması',\n        stepPanelData: 'Sistem Verisi',\n        stepBackupData: 'Yedekleme Verisi',\n        stepOtherData: 'Diğer Veri',\n        operationLog: 'İşlem Günlüğünü Sakla',\n        loginLog: 'Erişim Günlüğünü Sakla',\n        systemLog: 'Sistem Günlüğünü Sakla',\n        taskLog: 'Görev Günlüğünü Sakla',\n        monitorData: 'İzleme Verilerini Sakla',\n        selectAllImage: 'Tüm Uygulama Görüntülerini Yedekle',\n        logLabel: 'Günlük',\n        agentLabel: 'Düğüm Yapılandırması',\n        appDataLabel: 'Uygulama Verisi',\n        appImage: 'Uygulama Görüntüsü',\n        appBackup: 'Uygulama Yedeklemesi',\n        backupLabel: 'Yedekleme Dizini',\n        confLabel: 'Yapılandırma Dosyası',\n        dockerLabel: 'Konteyner',\n        taskLabel: 'Zamanlanmış Görev',\n        resourceLabel: 'Uygulama Kaynak Dizini',\n        runtimeLabel: 'Çalışma Zamanı Ortamı',\n        appLabel: 'Uygulama',\n        databaseLabel: 'Veritabanı',\n        snapshotLabel: 'Anlık Görüntü Dosyası',\n        websiteLabel: 'Web sitesi',\n        directoryLabel: 'Dizin',\n        appStoreLabel: 'Uygulama Mağazası',\n        shellLabel: 'Komut Dosyası',\n        tmpLabel: 'Geçici Dizin',\n        sslLabel: 'Sertifika Dizini',\n        reCreate: 'Anlık görüntü oluşturma başarısız',\n        reRollback: 'Anlık görüntü geri alma başarısız',\n        deleteHelper:\n            'Üçüncü taraf yedekleme hesabındaki dosyalar da dahil olmak üzere tüm anlık görüntü dosyaları silinecek. Devam etmek istiyor musunuz?',\n        status: 'Anlık görüntü durumu',\n        ignoreRule: 'Yoksayma kuralı',\n        editIgnoreRule: '@:commons.button.edit @.lower:setting.ignoreRule',\n        ignoreHelper:\n            'Bu kural, anlık görüntü oluştururken 1Panel veri dizinini sıkıştırmak ve yedeklemek için kullanılacaktır. Varsayılan olarak, soket dosyaları yok sayılır.',\n        ignoreHelper1: 'Her satıra bir tane. Örneğin,\\n*.log\\n/opt/1panel/cache',\n        panelInfo: '1Panel temel bilgilerini yaz',\n        panelBin: '1Panel sistem dosyalarını yedekle',\n        daemonJson: 'Docker yapılandırma dosyasını yedekle',\n        appData: '1Panel’den yüklenen uygulamaları yedekle',\n        panelData: '1Panel veri dizinini yedekle',\n        backupData: '1Panel için yerel yedekleme dizinini yedekle',\n        compress: 'Anlık görüntü dosyası oluştur',\n        upload: 'Anlık görüntü dosyasını yükle',\n        recoverDetail: 'Kurtarma detayı',\n        importHelper: 'Anlık görüntü dizini: ',\n        lastRecoverAt: 'Son kurtarma zamanı',\n        lastRollbackAt: 'Son geri alma zamanı',\n        reDownload: 'Yedekleme dosyasını tekrar indir',\n        recoverErrArch: 'Farklı sunucu mimarileri arasında anlık görüntü kurtarma desteklenmez!',\n        recoverErrSize: 'Yetersiz disk alanı tespit edildi, lütfen kontrol edin veya temizleyin ve tekrar deneyin!',\n        recoverHelper:\n            '{0} anlık görüntüsünden kurtarma başlatılıyor, lütfen devam etmeden önce aşağıdaki bilgileri onaylayın:',\n        recoverHelper1: 'Kurtarma, Docker ve 1Panel servislerinin yeniden başlatılmasını gerektirir',\n        recoverHelper2:\n            'Sunucuda yeterli disk alanının olduğundan emin olun (Anlık görüntü dosyası boyutu: {0}, Mevcut alan: {1})',\n        recoverHelper3:\n            'Sunucu mimarisinin, anlık görüntünün oluşturulduğu sunucu mimarisiyle eşleştiğinden emin olun (Geçerli sunucu mimarisi: {0})',\n        rollback: 'Geri al',\n        rollbackHelper:\n            'Bu kurtarmayı geri almak, bu kurtarmadan gelen tüm dosyaları değiştirecek ve Docker ile 1Panel servislerinin yeniden başlatılmasını gerektirebilir. Devam etmek istiyor musunuz?',\n        upgradeRecord: 'Yükseltme kaydı',\n        upgrading: 'Yükseltiliyor, lütfen bekleyin...',\n        upgradeHelper: 'Yükseltme, 1Panel servisinin yeniden başlatılmasını gerektirir. Devam etmek istiyor musunuz?',\n        noUpgrade: 'Şu anda en son sürüm',\n        versionHelper:\n            'Ad kuralları: [ana sürüm].[fonksiyonel sürüm].[Hata düzeltme sürümü], aşağıdaki örnekte gösterildiği gibi:',\n        rollbackLocalHelper:\n            'Birincil düğüm doğrudan geri almayı desteklemez. Lütfen geri almak için [1pctl restore] komutunu manuel olarak çalıştırın!',\n        upgradeCheck: 'Güncellemeleri kontrol et',\n        upgradeNotes: 'Sürüm notu',\n        upgradeNow: 'Şimdi yükselt',\n        source: 'İndirme kaynağı',\n        versionNotSame: 'Düğüm sürümü ana düğümle uyuşmuyor. Lütfen Düğüm Yönetiminde yükseltin ve tekrar deneyin.',\n        versionCompare:\n            '{0} düğümünün zaten en son yükseltilebilir sürümde olduğu tespit edildi. Lütfen birincil düğüm sürümünü kontrol edin ve tekrar deneyin!',\n        about: 'Hakkında',\n        versionItem: 'Mevcut Sürüm',\n        backupCopies: 'Yedek Kopya',\n        backupCopiesHelper:\n            'Sürüm geri alma için saklanacak yükseltme yedek kopya sayısını ayarlayın. 0, tümünü sakla anlamına gelir.',\n        backupCopiesRule: 'Lütfen en az 3 yükseltme yedek kaydı saklayın',\n        release: 'Sürüm Güncelleme Günlüğü',\n        releaseHelper:\n            'Mevcut ortam için güncelleme günlükleri alınırken hata oluştu. Resmi belgeleri manuel olarak kontrol edebilirsiniz.',\n        project: 'GitHub',\n        issue: 'Geri bildirim',\n        doc: 'Resmi belge',\n        star: 'Yıldız',\n        description: 'Linux Sunucu Paneli',\n        forum: 'Tartışmalar',\n        doc2: 'Belgeler',\n        currentVersion: 'Sürüm',\n        license: 'Lisans',\n        bindNode: 'Düğüm Bağla',\n        menuSetting: 'Menü Ayarları',\n        menuSettingHelper: 'Yalnızca 1 alt menü varsa, menü çubuğu yalnızca o alt menüyü gösterecektir',\n        showAll: 'Tümünü Göster',\n        hideALL: 'Tümünü Gizle',\n        ifShow: 'Gösterme Durumu',\n        menu: 'Menü',\n        confirmMessage: 'Gelişmiş menü listesini güncellemek için sayfa yenilenecek. Devam etmek istiyor musunuz?',\n        recoverMessage:\n            'Sayfa yenilenecek ve menü listesi başlangıç durumuna geri yüklenecektir. Devam etmek istiyor musunuz?',\n        compressPassword: 'Sıkıştırma parolası',\n        backupRecoverMessage: 'Lütfen sıkıştırma veya sıkıştırma açma parolasını girin (ayarlamamak için boş bırakın)',\n        componentSize: 'Bileşen boyutu',\n        dockerConf: 'Docker yapılandırmasını koru',\n        recoverFailed: 'Anlık görüntü geri yükleme başarısız',\n    },\n    license: {\n        offLine: 'Çevrimdışı Sürüm',\n        community: 'OSS',\n        oss: 'Açık Kaynak Yazılım',\n        pro: 'Pro',\n        trial: 'Deneme',\n        add: 'Topluluk Sürümünü Ekle',\n        licenseBindHelper: 'Ücretsiz düğüm kotası yalnızca lisans bir düğüme bağlı olduğunda kullanılabilir',\n        licenseAlert:\n            'Topluluk Sürümü düğümleri yalnızca lisans bir düğüme düzgün şekilde bağlandığında eklenebilir. Yalnızca düzgün şekilde bağlanmış düğümler geçiş yapmayı destekler.',\n        licenseUnbindHelper:\n            'Bu lisans için Topluluk Sürümü düğümleri tespit edildi. Lütfen bağlamayı kaldırın ve tekrar deneyin!',\n        subscription: 'Abonelik',\n        perpetual: 'Süresiz',\n        versionConstraint: '{0} Sürüm Satın Alma',\n        forceUnbind: 'Zorla Bağlantıyı Kaldır',\n        forceUnbindHelper:\n            'Zorla bağlantı kaldırma, bağlantı kaldırma işlemi sırasında oluşan hataları yok sayar ve nihayetinde lisans bağını serbest bırakır.',\n        updateForce:\n            'Zorla güncelle (bağlantı kaldırma sırasında tüm hataları yok sayarak son işlemin başarılı olmasını sağla)',\n        trialInfo: 'Sürüm',\n        authorizationId: 'Abonelik ID',\n        authorizedUser: 'Yetkili kullanıcı',\n        lostHelper:\n            'Lisans maksimum yeniden deneme sayısına ulaştı. Profesyonel sürüm özelliklerinin düzgün çalıştığından emin olmak için lütfen senkronizasyon düğmesine manuel olarak tıklayın.',\n        exceptionalHelper:\n            'Lisans senkronizasyon doğrulaması anormal. Profesyonel sürüm işlevlerinin düzgün çalıştığından emin olmak için lütfen senkronizasyon düğmesine manuel olarak tıklayın. detay: ',\n        quickUpdate: 'Hızlı güncelleme',\n        import: 'İçe aktar',\n        power: 'Yetkilendir',\n        unbindHelper: 'Bağlantı kaldırıldığında tüm Pro ile ilgili ayarlar temizlenecek. Devam etmek istiyor musunuz? ',\n        importLicense: 'Lisansı içe aktar',\n        importHelper: 'Lütfen lisans dosyasını buraya tıklayın veya sürükleyin',\n        levelUpPro: 'Profesyonel Sürüme Yükselt',\n        licenseSync: 'Lisans Senkronizasyonu',\n        knowMorePro: 'Daha Fazla Bilgi Edinin',\n        closeAlert: 'Geçerli sayfa panel ayarlarında kapatılabilir',\n        introduce: 'Özellik Tanıtımı',\n        waf: 'Profesyonel sürüme yükseltme, engelleme haritası, günlükler, engelleme kayıtları, coğrafi konum engelleme, özel kurallar, özel engelleme sayfaları gibi özellikler sağlayabilir.',\n        tamper: 'Profesyonel sürüme yükseltme, web sitelerini yetkisiz değişikliklerden veya kurcalamalardan koruyabilir.',\n        tamperHelper:\n            'İşlem başarısız oldu, dosya veya klasörde kurcalama koruması etkin. Lütfen kontrol edin ve tekrar deneyin!',\n        setting:\n            'Profesyonel sürüme yükseltme, panel logosu, hoş geldiniz mesajı ve diğer bilgilerin özelleştirilmesine olanak tanır.',\n        monitor:\n            'Profesyonel sürüme yükseltme, web sitesinin gerçek zamanlı durumunu, ziyaretçi trendlerini, ziyaretçi kaynaklarını, istek günlüklerini ve diğer bilgileri görüntülemenize olanak tanır. ',\n        alert: 'Profesyonel sürüme yükseltme, SMS yoluyla alarm bilgisi almanızı, alarm günlüklerini görüntülemenizi, çeşitli önemli olayları tamamen kontrol etmenizi ve sistemin sorunsuz çalışmasını sağlar.',\n        node: 'Profesyonel Sürüme yükseltme, 1Panel ile birden fazla Linux sunucusunu yönetmenize olanak tanır.',\n        nodeApp:\n            'Profesyonel Sürüme yükseltme, düğümleri manuel olarak değiştirmeye gerek kalmadan çoklu düğüm uygulama sürümlerini birleşik olarak yükseltmenize olanak tanır.',\n        nodeDashboard:\n            'Profesyonel Sürüme yükseltme, çoklu düğüm uygulamaları, web siteleri, veritabanları ve zamanlanmış görevleri merkezi olarak yönetmenizi sağlar.',\n        fileExchange: 'Profesyonel Sürüme yükseltme, birden fazla sunucu arasında hızlı dosya aktarımı sağlar.',\n        app: 'Profesyonel sürüme yükseltme, mobil uygulama üzerinden hizmet bilgilerini, anormal izlemeyi vb. görüntülemenize olanak tanır. ',\n        cluster: 'Profesyonel Sürüme Yükseltme, MySQL/Postgers/Redis ana-çalışan kümelerini yönetmenizi sağlar.',\n    },\n    clean: {\n        scan: 'Taramayı başlat',\n        scanHelper: '1Panel çalışma zamanında üretilen gereksiz dosyaları kolayca temizleyin',\n        clean: 'Şimdi temizle',\n        reScan: 'Yeniden tara',\n        cleanHelper: 'Seçilen dosya ve dizinler temizlendikten sonra geri alınamaz. Devam etmek istiyor musunuz?',\n        statusStatusSuggest: '(Temizlik Önerilir)',\n        statusClean: '(Çok temiz)',\n        statusEmpty: 'Çok temiz, temizlik gerekmiyor!',\n        statusWarning: '(Dikkatli İlerleyin)',\n        lastCleanTime: 'Son Temizleme: {0}',\n        lastCleanHelper: 'Temizlenen dosya ve dizinler: {0}, toplam temizlenen: {1}',\n        cleanSuccessful: 'Başarıyla temizlendi',\n        currentCleanHelper: 'Bu oturumda temizlenen dosya ve dizinler: {0}, Toplam temizlenen: {1}',\n        suggest: '(Önerilen)',\n        totalScan: 'Temizlenecek toplam gereksiz dosya: ',\n        selectScan: 'Seçilen toplam gereksiz dosya: ',\n        system: 'Sistem Gereksiz Dosyaları',\n        systemHelper: 'Anlık görüntü, yükseltme ve diğer işlemler sırasında oluşturulan geçici dosyalar',\n        panelOriginal: 'Sistem anlık görüntüsü geri yükleme öncesi yedekleme dosyaları',\n        upgrade: 'Sistem yükseltme yedekleme dosyaları',\n        agentPackages: 'Geçmiş sürüm alt düğüm yükseltme/kurulum paketleri',\n        upgradeHelper: '(Sistem geri alımı için en son yükseltme yedeğini tutmanız önerilir)',\n        snapshot: 'Sistem anlık görüntü geçici dosyaları',\n        rollback: 'Kurtarma öncesi yedek dosyaları',\n        backup: 'Sistem Yedekleme',\n        backupHelper: 'Yerel yedekleme hesaplarıyla ilişkili olmayan yedekleme dosyaları',\n        unknownBackup: 'Sistem Yedekleme',\n        tmpBackup: 'Geçici Yedekleme',\n        unknownApp: 'İlişkili Olmayan Uygulama Yedeklemesi',\n        unknownDatabase: 'İlişkili Olmayan Veritabanı Yedeklemesi',\n        unknownWebsite: 'İlişkili Olmayan Web Sitesi Yedeklemesi',\n        unknownSnapshot: 'İlişkili Olmayan Anlık Görüntü Yedeklemesi',\n        upload: 'Geçici Yükleme Dosyaları',\n        uploadHelper: 'Sistem yedek listesinden yüklenen geçici dosyalar',\n        download: 'Geçici İndirme Dosyaları',\n        downloadHelper: 'Sistem tarafından üçüncü taraf yedek hesaplarından indirilen geçici dosyalar',\n        directory: 'Dizin',\n        systemLog: 'Günlük dosyası',\n        systemLogHelper: 'Sistem günlükleri, görev günlükleri, web sitesi günlük dosyaları',\n        dockerLog: 'Konteyner işlem günlüğü dosyaları',\n        taskLog: 'Zamanlanmış görev yürütme günlüğü dosyaları',\n        shell: 'Shell script zamanlanmış görevler',\n        containerShell: 'Konteynerler içinde yürütülen shell script zamanlanmış görevler',\n        curl: 'CURL zamanlanmış görevler',\n        docker: 'Konteyner çöpü',\n        dockerHelper: 'Konteynerler, görüntüler, hacimler, derleme önbelleği vb. dosyalar',\n        volumes: 'Birimler',\n        buildCache: 'Konteyner Oluşturma Önbelleği',\n        appTmpDownload: 'Uygulama geçici indirme dosyası',\n        unknownWebsiteLog: 'Bağlantısız web sitesi günlük yedekleme dosyası',\n        statusSuggest: 'Disk ekleme/silme/değiştirme sonrası son durumun doğru olması için taramayı tekrar yapın',\n    },\n    app: {\n        app: 'Uygulama | Uygulamalar',\n        installName: 'Ad',\n        installed: 'Kurulu',\n        all: 'Tümü',\n        version: 'Sürüm',\n        detail: 'Detaylar',\n        params: 'Parametreleri düzenle',\n        author: 'Yazar',\n        source: 'Kaynak',\n        appName: 'Uygulama Adı',\n        deleteWarn:\n            'Silme işlemi tüm verileri ve yedekleri birlikte silecektir. Bu işlem geri alınamaz. Devam etmek istiyor musunuz? ',\n        syncSuccess: 'Başarıyla senkronize edildi',\n        canUpgrade: 'Güncellemeler',\n        backupName: 'Dosya Adı',\n        backupPath: 'Dosya Yolu',\n        backupdate: 'Yedekleme zamanı',\n        versionSelect: 'Lütfen bir the sürüm seçin',\n        operatorHelper: 'Seçilen uygulamada {0} işlemi gerçekleştirilecek. Devam etmek istiyor musunuz?',\n        startOperatorHelper: 'Uygulama başlatılacak. Devam etmek istiyor musunuz?',\n        stopOperatorHelper: 'Uygulama durdurulacak. Devam etmek istiyor musunuz?',\n        restartOperatorHelper: 'Uygulama yeniden başlatılacak. Devam etmek istiyor musunuz?',\n        reloadOperatorHelper: 'Uygulama yeniden yüklenecek. Devam etmek istiyor musunuz?',\n        checkInstalledWarn: '\"{0}\" tespit edilmedi. Kurulum için \"Uygulama Mağazası\"na gidin.',\n        limitHelper: 'Uygulama zaten kurulmuş.',\n        deleteHelper: '\"{0}\" aşağıdaki kaynak(lar) ile ilişkilendirilmiş ve silinemez',\n        checkTitle: 'İpucu',\n        defaultConfig: 'Varsayılan yapılandırma',\n        defaultConfigHelper: 'Varsayılan yapılandırmaya geri yüklendi, kaydetmeden sonra etkili olacaktır',\n        forceDelete: 'Zorla sil',\n        forceDeleteHelper:\n            'Zorla silme, silme işlemi sırasında oluşan hataları yok sayar ve nihayetinde meta verileri siler.',\n        deleteBackup: 'Yedeği sil',\n        deleteBackupHelper: 'Uygulama yedeğini de sil',\n        deleteDB: 'Veritabanını sil',\n        deleteDBHelper: 'Veritabanını da sil',\n        noService: '{0} Yok',\n        toInstall: 'Kurulum için git',\n        param: 'Parametreler',\n        alreadyRun: 'Yaş',\n        syncAppList: 'Senkronize et',\n        less1Minute: '1 dakikadan az',\n        appOfficeWebsite: 'Web sitesi',\n        github: 'Github',\n        document: 'Belge',\n        updatePrompt: 'Kullanılabilir güncelleme yok',\n        installPrompt: 'Henüz uygulama kurulmadı',\n        updateHelper:\n            'Parametrelerin düzenlenmesi uygulamanın başlatılamamasına neden olabilir. Lütfen dikkatli ilerleyin.',\n        updateWarn:\n            'Parametre güncellemesi uygulamanın yeniden oluşturulmasını gerektirir, Devam etmek istiyor musunuz? ',\n        busPort: 'Bağlantı Noktası',\n        syncStart: 'Senkronizasyon başlıyor! Lütfen uygulama mağazasını daha sonra yenileyin',\n        advanced: 'Gelişmiş ayarlar',\n        cpuCore: 'çekirdek(ler)',\n        containerName: 'Konteyner adı',\n        containerNameHelper: 'Konteyner adı ayarlanmadığında otomatik olarak oluşturulacaktır',\n        allowPort: 'Dış erişim',\n        allowPortHelper:\n            'Dış bağlantı noktası erişimine izin vermek, güvenlik duvarı bağlantı noktasını serbest bırakacaktır',\n        appInstallWarn:\n            'Uygulama varsayılan olarak dış erişim bağlantı noktasını açmaz. Açmak için \"Gelişmiş ayarlar\"a tıklayın.',\n        upgradeStart: 'Yükseltme başlıyor! Lütfen sayfayı daha sonra yenileyin',\n        toFolder: 'Kurulum dizinini aç',\n        editCompose: 'Compose dosyasını düzenle',\n        editComposeHelper: 'Compose dosyasını düzenlemek, yazılım kurulumunun başarısız olmasına neden olabilir',\n        composeNullErr: 'compose boş olamaz',\n        takeDown: 'Kaldır',\n        allReadyInstalled: 'Kurulu',\n        installHelper: 'Görüntü çekme sorunlarınız varsa, görüntü hızlandırmasını yapılandırın.',\n        sortMode: 'Sırala',\n        installWarn:\n            'Dış erişim işaretlenmedi, bu uygulama dış ağ üzerinden erişilemez hale getirecek. Devam etmek istiyor musunuz?',\n        showIgnore: 'Yoksayılan uygulamaları görüntüle',\n        cancelIgnore: 'Yoksaymayı iptal et',\n        ignoreList: 'Yoksayılan uygulamalar',\n        appHelper: 'Bazı özel uygulamalar için kurulum talimatlarını öğrenmek için uygulama detayları sayfasına gidin.',\n        backupApp: 'Yükseltmeden önce uygulamayı yedekle',\n        backupAppHelper:\n            'Yükseltme başarısız olursa, yedek otomatik olarak geri alınır. Lütfen hata nedenini günlük denetim-sistem günlüğünde kontrol edin',\n        openrestyDeleteHelper:\n            'OpenResty`nin zorla silinmesi tüm web sitelerini silecektir. Devam etmek istiyor musunuz?',\n        downloadLogHelper1: '{0} uygulamasının tüm günlükleri indirilmek üzere. Devam etmek istiyor musunuz? ',\n        downloadLogHelper2: '{0} uygulamasının en son {1} günlüğü indirilmek üzere. Devam etmek istiyor musunuz? ',\n        syncAllAppHelper: 'Tüm uygulamalar senkronize edilecek. Devam etmek istiyor musunuz? ',\n        hostModeHelper:\n            'Geçerli uygulama ağ modu ana bilgisayar modudur. Bağlantı noktasını açmanız gerekiyorsa, lütfen güvenlik duvarı sayfasında manuel olarak açın.',\n        showLocal: 'Yerel uygulamaları göster',\n        reload: 'Yeniden yükle',\n        upgradeWarn:\n            'Uygulamanın yükseltilmesi docker-compose.yml dosyasını değiştirecektir. Herhangi bir değişiklik varsa, dosya karşılaştırmasını görüntülemek için tıklayabilirsiniz',\n        openclawHttpsUpgradeNoticeTitle:\n            \"Not: Aşağıdaki talimatlar yalnızca OpenClaw'ı 2026.3.13 öncesi sürümlerden yükselten kullanıcılar için geçerlidir:\",\n        openclawHttpsUpgradeNoticeItem1:\n            'Dağıtılmış ajan yükseltmesi tamamlandıktan sonra Yapılandırma -> Ayarlar -> Güvenlik sayfasına gidip erişim adresini manuel olarak ekleyin.',\n        openclawHttpsUpgradeNoticeItem2:\n            'Yeni sürüm artık ajana erişim için HTTPS zorunlu kılıyor. Daha önce ters proxy sitesi kullanıyorsanız proxy hedef adresini https://IP:Port olarak değiştirin.',\n        newVersion: 'Yeni sürüm',\n        oldVersion: 'Mevcut sürüm',\n        composeDiff: 'Dosya karşılaştırması',\n        showDiff: 'Karşılaştırmayı görüntüle',\n        useNew: 'Özel sürümü kullan',\n        useDefault: 'Varsayılan sürümü kullan',\n        useCustom: 'docker-compose.yml dosyasını özelleştir',\n        useCustomHelper:\n            'Özel bir docker-compose.yml dosyası kullanmak, uygulama yükseltmesinin başarısız olmasına neden olabilir. Gerekli değilse, işaretlemeyin.',\n        diffHelper:\n            'Sol taraf eski sürüm, sağ taraf yeni sürümdür. Düzenlemeden sonra, özel sürümü kaydetmek için tıklayın',\n        pullImage: 'Görüntüyü Çek',\n        pullImageHelper: 'Uygulama başlamadan önce docker pull ile görüntüyü çekin',\n        deleteImage: 'Görüntüyü Sil',\n        deleteImageHelper: 'Uygulamayla ilgili görüntüyü silin. Silme başarısız olursa görev durmaz',\n        requireMemory: 'Bellek',\n        supportedArchitectures: 'Mimariler',\n        link: 'Bağlantı',\n        showCurrentArch: 'Mimari',\n        syncLocalApp: 'Yerel Uygulamayı Senkronize Et',\n        memoryRequiredHelper: 'Geçerli uygulama bellek gereksinimi {0}',\n        gpuConfig: 'GPU Desteğini Etkinleştir',\n        gpuConfigHelper:\n            'Makinenin bir NVIDIA GPU`su olduğundan ve NVIDIA sürücülerinin ve NVIDIA Docker Container Toolkit`in kurulu olduğundan emin olun',\n        webUI: 'Web Erişim Adresi',\n        webUIPlaceholder: 'Örneğin: example.com:8080/login',\n        defaultWebDomain: 'Varsayılan Erişim Adresi',\n        defaultWebDomainHepler:\n            'Uygulama bağlantı noktası 8080 ise, yönlendirme adresi http(s)://varsayılan erişim adresi:8080 olacaktır',\n        webUIConfig:\n            'Geçerli düğümde varsayılan erişim adresi yapılandırılmamış. Lütfen bunu uygulama parametrelerinde veya panel ayarlarına giderek yapılandırın!',\n        toLink: 'Aç',\n        customAppHelper: 'Özel uygulama mağazası paketini kurmadan önce, kurulu uygulama olmadığından emin olun.',\n        forceUninstall: 'Zorla Kaldır',\n        syncCustomApp: 'Özel Uygulamayı Senkronize Et',\n        ignoreAll: 'Tüm sonraki sürümleri yoksay',\n        ignoreVersion: 'Belirtilen sürümü yoksay',\n        specifyIP: 'Ana Bilgisayar IP`sini Bağla',\n        specifyIPHelper:\n            'Bağlantı noktasını bağlamak için ana bilgisayar adresini/ağ arayüzünü ayarlayın (bunda emin değilseniz, lütfen doldurmayın)',\n        uninstallDeleteBackup: 'Uygulamayı Kaldır - Yedeği Sil',\n        uninstallDeleteImage: 'Uygulamayı Kaldır - Görüntüyü Sil',\n        upgradeBackup: 'Yükseltmeden Önce Uygulamayı Yedekle',\n        noAppHelper:\n            'Uygulama tespit edilmedi, lütfen uygulama mağazası senkronizasyon günlüğünü görüntülemek için görev merkezine gidin',\n        isEdirWarn: 'docker-compose.yml dosyasında değişiklik tespit edildi, lütfen karşılaştırmayı kontrol edin',\n    },\n    website: {\n        primaryDomain: 'Birincil alan adı',\n        otherDomains: 'Diğer alan adları',\n        static: 'Statik',\n        deployment: 'Dağıtım',\n        supportUpType: 'Yalnızca .tar.gz dosya formatı desteklenir ve sıkıştırılmış paket {0}.json dosyası içermelidir',\n        proxy: 'Ters vekil',\n        alias: 'Takma ad',\n        ftpUser: 'FTP hesabı',\n        ftpPassword: 'FTP şifresi',\n        ftpHelper:\n            'Bir web sitesi oluşturulduktan sonra, ilgili bir FTP hesabı oluşturulacak ve FTP dizini web sitesi dizinine bağlanacaktır.',\n        remark: 'Açıklama',\n        groupSetting: 'Grup Yönetimi',\n        createGroup: 'Grup Oluştur',\n        appNew: 'Yeni Uygulama',\n        appInstalled: 'Kurulu uygulama',\n        delete: 'Web Sitesini Sil',\n        deleteApp: 'Uygulamayı Sil',\n        deleteBackup: 'Yedeği Sil',\n        domain: 'Alan Adı',\n        domainHelper:\n            'Her satırda bir alan adı.\\nJoker karakter \"*\" ve IP adresi desteklenir.\\nBağlantı noktası ekleme desteklenir.',\n        addDomain: 'Ekle',\n        domainConfig: 'Alan Adları',\n        defaultDoc: 'Belge',\n        perserver: 'Eşzamanlılık',\n        perserverHelper: 'Geçerli sitenin maksimum eşzamanlılığını sınırlandır',\n        perip: 'Tek IP',\n        peripHelper: 'Tek bir IP için eşzamanlı erişim sayısını sınırlandır',\n        rate: 'Trafik sınırları',\n        rateLimit: 'İstek başına hız sınırı',\n        rateHelper: 'Her istek için akış sınırlandır (birim: KB)',\n        rateLimitHelper: 'Her istek için aktarım hızını sınırlar (birim: KB/s)',\n        limitHelper: 'Akış kontrolünü etkinleştir',\n        other: 'Diğer',\n        currentSSL: 'Geçerli Sertifika',\n        dnsAccount: 'DNS hesabı',\n        applySSL: 'Sertifika Başvurusu',\n        SSLList: 'Sertifika Listesi',\n        createDnsAccount: 'DNS hesabı',\n        aliyun: 'Aliyun DNS',\n        aliEsa: 'Aliyun ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: 'Manuel çözümleme',\n        key: 'Anahtar',\n        check: 'Görüntüle',\n        acmeAccountManage: 'ACME hesaplarını yönet',\n        email: 'E-posta',\n        acmeAccount: 'ACME hesabı',\n        provider: 'Doğrulama yöntemi',\n        dnsManual: 'Manuel Çözümleme',\n        expireDate: 'Son kullanma tarihi',\n        brand: 'Organizasyon',\n        deploySSL: 'Dağıtım',\n        deploySSLHelper: 'Sertifikayı dağıtmaya emin misiniz? ',\n        ssl: 'Sertifika | Sertifikalar',\n        dnsAccountManage: 'DNS sağlayıcılarını yönet',\n        renewSSL: 'Yenile',\n        renewHelper: 'Sertifikayı yenilemeye emin misiniz? ',\n        renewSuccess: 'Sertifika yenileme',\n        enableHTTPS: 'Etkinleştir',\n        aliasHelper: 'Takma ad, web sitesinin dizin adıdır',\n        lastBackupAt: 'son yedekleme zamanı',\n        null: 'hiçbiri',\n        nginxConfig: 'Nginx yapılandırması',\n        websiteConfig: 'Web sitesi ayarları',\n        proxySettings: 'Proxy Ayarları',\n        advancedSettings: 'Gelişmiş Ayarlar',\n        cacheSettings: 'Önbellek Ayarları',\n        sniSettings: 'SNI Ayarları',\n        basic: 'Temel',\n        source: 'Yapılandırma',\n        security: 'Güvenlik',\n        nginxPer: 'Performans ayarı',\n        neverExpire: 'Asla',\n        setDefault: 'Varsayılan olarak ayarla',\n        deleteHelper: 'İlgili uygulama durumu anormal, lütfen kontrol edin',\n        toApp: 'Kurulu listeye git',\n        cycle: 'Döngü',\n        frequency: 'Frekans',\n        ccHelper:\n            '{0} saniye içinde aynı URL’ye {1} defadan fazla istekte bulunulursa, CC savunması tetiklenir ve bu IP engellenir',\n        mustSave: 'Değişikliğin etkili olması için kaydedilmesi gerekiyor',\n        fileExt: 'dosya uzantısı',\n        fileExtBlock: 'dosya uzantısı engelleme listesi',\n        value: 'değer',\n        enable: 'Etkinleştir',\n        proxyAddress: 'Vekil Adresi',\n        proxyHelper: 'Örnek: 127.0.0.1:8080',\n        forceDelete: 'Zorla Sil',\n        forceDeleteHelper:\n            'Zorla silme, silme işlemi sırasında oluşan hataları yok sayar ve nihayetinde meta verileri siler.',\n        deleteAppHelper: 'İlgili uygulamaları ve uygulama yedeklerini aynı anda sil',\n        deleteBackupHelper: 'Web sitesi yedeklerini de sil.',\n        deleteDatabaseHelper: 'Ayrıca web sitesi ile ilişkili veritabanını da sil',\n        deleteConfirmHelper:\n            'Silme işlemi geri alınamaz. Silmeyi onaylamak için <span style=\"color:red\"> \"{0}\" </span> yazın.',\n        staticPath: 'Karşılık gelen ana dizin ',\n        limit: 'Şema',\n        blog: 'Forum/Blog',\n        imageSite: 'Resim Sitesi',\n        downloadSite: 'İndirme Sitesi',\n        shopSite: 'Alışveriş Merkezi',\n        doorSite: 'Portal',\n        qiteSite: 'Kurumsal',\n        videoSite: 'Video',\n        errLog: 'Hata günlüğü',\n        stopHelper:\n            'Site durdurulduktan sonra normal şekilde erişilemeyecek ve kullanıcı siteyi ziyaret ettiğinde mevcut sitenin durdurma sayfası görüntülenecektir. Devam etmek istiyor musunuz? ',\n        startHelper:\n            'Site etkinleştirildikten sonra kullanıcılar site içeriğine normal şekilde erişebilir, devam etmek istiyor musunuz? ',\n        sitePath: 'Dizin',\n        siteAlias: 'Site takma adı',\n        primaryPath: 'Kök dizin',\n        folderTitle: 'Web sitesi esas olarak aşağıdaki klasörleri içerir',\n        wafFolder: 'Güvenlik duvarı kuralları',\n        indexFolder: 'Web sitesi kök dizini',\n        sslFolder: 'Web Sitesi Sertifikası',\n        enableOrNot: 'Etkinleştir',\n        oldSSL: 'Mevcut sertifika',\n        manualSSL: 'Sertifikayı içe aktar',\n        select: 'Seç',\n        selectSSL: 'Sertifika Seç',\n        privateKey: 'Anahtar (KEY)',\n        certificate: 'Sertifika (PEM formatı)',\n        HTTPConfig: 'HTTP Seçenekleri',\n        HTTPSOnly: 'HTTP isteklerini engelle',\n        HTTPToHTTPS: 'HTTPS’ye yönlendir',\n        HTTPAlso: 'Doğrudan HTTP isteklerine izin ver',\n        sslConfig: 'SSL seçenekleri',\n        disableHTTPS: 'HTTPS’yi devre dışı bırak',\n        disableHTTPSHelper:\n            'HTTPS’nin devre dışı bırakılması, sertifika ile ilgili yapılandırmayı silecektir, Devam etmek istiyor musunuz?',\n        SSLHelper:\n            'Not: SSL sertifikalarını yasa dışı web siteleri için kullanmayın.\\nHTTPS erişimi açıldıktan sonra kullanılamıyorsa, güvenlik grubunun 443 numaralı bağlantı noktasını doğru şekilde serbest bırakıp bırakmadığını kontrol edin.',\n        SSLConfig: 'Sertifika ayarları',\n        SSLProConfig: 'Protokol ayarları',\n        supportProtocol: 'Protokol sürümü',\n        encryptionAlgorithm: 'Şifreleme algoritması',\n        notSecurity: '(güvenli değil)',\n        encryptHelper:\n            \"Let's Encrypt, sertifika verme sıklığı için bir sınırlama getirir, ancak bu normal ihtiyaçları karşılamak için yeterlidir. Çok sık işlemler, verme işleminin başarısız olmasına neden olur. Özel kısıtlamalar için lütfen <a target='_blank' href='https://letsencrypt.org/zh-cn/docs /rate-limits/'>resmi belgeye</a> bakın \",\n        ipValue: 'Değer',\n        ext: 'dosya uzantısı',\n        wafInputHelper: 'Verileri satır satır girin, her satır bir veri',\n        data: 'veri',\n        ever: 'kalıcı',\n        nextYear: 'Bir yıl sonra',\n        noLog: 'Günlük bulunamadı',\n        defaultServer: 'Varsayılan siteyi ayarla',\n        noDefaultServer: 'Ayarlanmadı',\n        defaultServerHelper:\n            'Varsayılan site ayarlandıktan sonra, bağlanmamış tüm alan adları ve IP’ler varsayılan siteye yönlendirilecektir\\nBu, kötü niyetli çözümlemeyi etkili bir şekilde önleyebilir\\nAncak, bu aynı zamanda WAF yetkisiz alan adı engellemesinin başarısız olmasına neden olur',\n        restoreHelper: 'Bu yedeği kullanarak geri yüklemeye emin misiniz?',\n        websiteDeploymentHelper:\n            'Bir web sitesi oluşturmak için kurulu bir uygulamayı kullanın veya yeni bir uygulama oluşturun.',\n        websiteStatictHelper: 'Ana bilgisayarda bir web sitesi dizini oluşturun.',\n        websiteProxyHelper:\n            'Mevcut bir servisi vekil etmek için ters vekil kullanın. Örneğin, bir servis 8080 numaralı bağlantı noktasında kurulu ve çalışıyorsa, vekil adresi \"http://127.0.0.1:8080\" olacaktır.',\n        runtimeProxyHelper: 'Bir web sitesi oluşturmak için bir web sitesi çalışma zamanını kullanın.',\n        runtime: 'Çalışma Zamanı',\n        deleteRuntimeHelper:\n            'Çalışma zamanı uygulaması, web sitesiyle birlikte silinmelidir, lütfen dikkatli bir şekilde işlem yapın',\n        proxyType: 'Ağ Türü',\n        unix: 'Unix Ağı',\n        tcp: 'TCP/IP Ağı',\n        phpFPM: 'FPM Yapılandırması',\n        phpConfig: 'PHP Yapılandırması',\n        updateConfig: 'Yapılandırmayı Güncelle',\n        isOn: 'Açık',\n        isOff: 'Kapalı',\n        rewrite: 'Sahte statik',\n        rewriteMode: 'Şema',\n        current: 'Mevcut',\n        rewriteHelper:\n            'Sahte statik ayarlarının yapılması web sitesinin erişilemez hale gelmesine neden oluyorsa, varsayılan ayarlara geri dönmeyi deneyin.',\n        runDir: 'Çalışma Dizini',\n        runUserHelper:\n            'PHP konteyner çalışma zamanı ortamı üzerinden dağıtılan web siteleri için, index ve alt dizinler altındaki tüm dosya ve klasörlerin sahibi ve kullanıcı grubu 1000 olarak ayarlanmalıdır. Yerel PHP ortamı için, yerel PHP-FPM kullanıcı ve kullanıcı grubu ayarlarına bakın',\n        userGroup: 'Kullanıcı/Grup',\n        uGroup: 'Grup',\n        proxyPath: 'Vekil yolu',\n        proxyPass: 'Hedef URL',\n        cache: 'Önbellek',\n        cacheTime: 'Önbellek süresi',\n        enableCache: 'Önbellek',\n        proxyHost: 'Vekil ana bilgisayarı',\n        disabled: 'Durduruldu',\n        startProxy: 'Bu, ters vekili başlatacak. Devam etmek istiyor musunuz?',\n        stopProxy: 'Bu, ters vekili durduracak. Devam etmek istiyor musunuz?',\n        sourceFile: 'Kaynağı görüntüle',\n        proxyHelper1: 'Bu dizine erişildiğinde, hedef URL’nin içeriği döndürülecek ve görüntülenecektir.',\n        proxyPassHelper: 'Hedef URL geçerli ve erişilebilir olmalıdır.',\n        proxyHostHelper: 'İstek başlığındaki alan adını vekil sunucusuna ilet.',\n        modifier: 'Eşleştirme kuralları',\n        modifierHelper: 'Örnek: \"=\" tam eşleşme, \"~\" düzenli eşleşme, \"^~\" yolun başlangıcıyla eşleşme vb.',\n        replace: 'Metin değiştirmeleri',\n        replaceHelper:\n            'Nginx metin değiştirme özelliği, ters proxy sırasında yanıt içeriğindeki dizelerin değiştirilmesine olanak tanır. Genellikle arka uç tarafından döndürülen HTML, CSS, JavaScript ve diğer dosyalardaki bağlantıları, API adreslerini vb. değiştirmek için kullanılır. Karmaşık içerik değiştirme ihtiyaçları için normal ifade eşleştirmeyi destekler.',\n        addReplace: 'Ekle',\n        replaced: 'Arama Dizisi (boş olamaz)',\n        replaceText: 'Şununla değiştir',\n        replacedErr: 'Arama Dizisi boş olamaz',\n        replacedErr2: 'Arama Dizisi tekrarlanamaz',\n        replacedListEmpty: 'Metin değiştirme kuralı yok',\n        proxySslName: 'Proxy SNI Adı',\n        basicAuth: 'Temel kimlik doğrulama',\n        editBasicAuthHelper:\n            'Şifre asimetrik olarak şifrelenir ve geri alınamaz. Düzenleme, şifrenin sıfırlanmasını gerektirir',\n        antiLeech: 'Sömürü karşıtı',\n        extends: 'Uzantı',\n        browserCache: 'Önbellek',\n        noModify: 'Değiştirme',\n        serverCache: 'Sunucu önbelleği',\n        leechLog: 'Sömürü karşıtı günlüğü kaydet',\n        accessDomain: 'İzin verilen alan adları',\n        leechReturn: 'Yanıt kaynağı',\n        noneRef: 'Boş yönlendirme izni ver',\n        disable: 'etkin değil',\n        disableLeechHelper: 'Sömürü karşıtını devre dışı bırakıp bırakmamak',\n        disableLeech: 'Sömürü karşıtını devre dışı bırak',\n        ipv6: 'IPv6’yı dinle',\n        leechReturnError: 'Lütfen HTTP durum kodunu doldurun',\n        blockedRef: 'Standart olmayan Referer’e izin ver',\n        accessControl: 'Sömürü karşıtı kontrol',\n        leechcacheControl: 'Önbellek kontrolü',\n        logEnableControl: 'Statik varlık isteklerini günlüğe al',\n        leechSpecialValidHelper:\n            \"'Boş yönlendirme izni ver' etkinse yönlendiricisi olmayan istekler (doğrudan erişim vb.) engellenmez; 'Standart olmayan Referer’e izin ver' etkinse http/https ile başlamayan tüm Referer isteklerine (istemci istekleri vb.) izin verilir.\",\n        leechInvalidReturnHelper: 'Hotlink isteklerini engelledikten sonra döndürülecek HTTP durum kodu',\n        leechlogControlHelper:\n            'Statik varlık isteklerini kaydeder; üretimde genellikle aşırı ve gereksiz günlüklerden kaçınmak için kapatılır',\n        selectAcme: 'Acme hesabını seç',\n        imported: 'Manuel olarak oluşturuldu',\n        importType: 'İçe aktarma türü',\n        pasteSSL: 'Kodu yapıştır',\n        localSSL: 'Sunucu dosyasını seç',\n        privateKeyPath: 'Özel anahtar dosyası',\n        certificatePath: 'Sertifika dosyası',\n        ipWhiteListHelper: 'IP izin listesinin rolü: IP izin listesindeki tüm kurallar geçersizdir',\n        redirect: 'Yönlendirme',\n        sourceDomain: 'Kaynak alan adı',\n        targetURL: 'Hedef URL adresi',\n        keepPath: 'URI parametreleri',\n        path: 'yol',\n        redirectType: 'yönlendirme türü',\n        redirectWay: 'Yol',\n        keep: 'tut',\n        notKeep: 'Tutma',\n        redirectRoot: 'Ana sayfaya yönlendir',\n        redirectHelper: '301 kalıcı yönlendirme, 302 geçici yönlendirme',\n        changePHPVersionWarn: 'Bu işlem geri alınamaz, devam etmek istiyor musunuz?',\n        changeVersion: 'Sürümü değiştir',\n        retainConfig: 'php-fpm.conf ve php.ini dosyalarını saklayıp saklamamak',\n        runDirHelper2: 'Lütfen ikincil çalışma dizininin index dizini altında olduğundan emin olun',\n        openrestyHelper:\n            'OpenResty varsayılan HTTP bağlantı noktası: {0} HTTPS bağlantı noktası: {1}, bu web sitesi alan adı erişimini ve HTTPS zorunlu yönlendirmesini etkileyebilir',\n        primaryDomainHelper: 'Örnek: example.com veya example.com:8080',\n        acmeAccountType: 'Hesap türü',\n        keyType: 'Anahtar algoritması',\n        tencentCloud: 'Tencent Cloud',\n        containWarn: 'Alan adı ana alanı içeriyor, lütfen tekrar girin',\n        rewriteHelper2:\n            'Uygulama mağazasından yüklenen WordPress gibi uygulamalar genellikle önceden ayarlanmış sahte statik yapılandırmayla gelir. Bunları yeniden yapılandırmak hatalara yol açabilir.',\n        websiteBackupWarn:\n            'Yalnızca yerel yedeklemelerin içe aktarılması desteklenir, diğer makinelerden yedeklerin içe aktarılması kurtarma hatasına neden olabilir',\n        ipWebsiteWarn:\n            'IP alan adlarına sahip web sitelerinin normal şekilde erişilebilmesi için varsayılan site olarak ayarlanması gerekir.',\n        hstsHelper: 'HSTS’nin etkinleştirilmesi web sitesi güvenliğini artırabilir',\n        includeSubDomains: 'Alt Alan Adları',\n        hstsIncludeSubDomainsHelper:\n            'Etkinleştirildiğinde, HSTS politikası geçerli etki alanının tüm alt alan adlarına uygulanacaktır.',\n        defaultHtml: 'Varsayılan sayfayı ayarla',\n        website404: 'Web sitesi 404 hata sayfası',\n        domain404: 'Web sitesi sayfası mevcut değil',\n        indexHtml: 'Statik web sitesi varsayılan sayfası',\n        stopHtml: 'Web sitesi durdurma sayfası',\n        indexPHP: 'PHP web sitesi varsayılan sayfası',\n        sslExpireDate: 'SSL Son Kullanma Tarihi',\n        website404Helper:\n            'Web sitesi 404 hata sayfası yalnızca PHP çalışma zamanı ortamı web sitelerini ve statik web sitelerini destekler',\n        sni: 'Kaynak SNI',\n        sniHelper:\n            'Ters vekil arka ucu HTTPS olduğunda, kaynak SNI’yi ayarlamanız gerekebilir. Ayrıntılar için CDN hizmet sağlayıcısının belgelerine bakın.',\n        huaweicloud: 'Huawei Cloud',\n        createDb: 'Veritabanı Oluştur',\n        enableSSLHelper: 'Etkinleştirme başarısızlığı web sitesinin oluşturulmasını etkilemez',\n        batchAdd: 'Toplu Alan Adı Ekle',\n        batchInput: 'Toplu Giriş',\n        domainNotFQDN: 'Bu alan adı genel ağda erişilebilir olmayabilir',\n        domainInvalid: 'Geçersiz alan adı biçimi',\n        domainBatchHelper:\n            'Bir satırda bir alan adı, format: domain:port@ssl\\nÖrnek: example.com:443@ssl veya example.com',\n        generateDomain: 'Oluştur',\n        global: 'Küresel',\n        subsite: 'Alt site',\n        subsiteHelper: 'Bir alt site, mevcut bir PHP veya statik web sitesi dizinini ana dizin olarak seçebilir.',\n        parentWbeiste: 'Ana Web Sitesi',\n        deleteSubsite: 'Geçerli web sitesini silmek için önce alt site(ler) {0} silinmelidir',\n        loadBalance: 'Yük Dengeleme',\n        server: 'Sunucu',\n        algorithm: 'Algoritma',\n        ipHash: 'IP Hash',\n        ipHashHelper:\n            'İstemci IP adresine dayalı olarak istekleri belirli bir sunucuya dağıtır, belirli bir istemcinin her zaman aynı sunucuya yönlendirilmesini sağlar.',\n        leastConn: 'En Az Bağlantı',\n        leastConnHelper: 'En az aktif bağlantıya sahip sunucuya istek gönderir.',\n        leastTime: 'En Az Süre',\n        leastTimeHelper: 'En kısa aktif bağlantı süresine sahip sunucuya istek gönderir.',\n        defaultHelper:\n            'Varsayılan yöntem, istekleri her sunucuya eşit şekilde dağıtır. Sunucuların ağırlıkları yapılandırılmışsa, istekler belirtilen ağırlıklara göre dağıtılır, daha yüksek ağırlıklı sunucular daha fazla istek alır.',\n        weight: 'Ağırlık',\n        maxFails: 'Maksimum Başarısızlık',\n        maxConns: 'Maksimum Bağlantılar',\n        strategy: 'Strateji',\n        strategyDown: 'Kapalı',\n        strategyBackup: 'Yedek',\n        ipHashBackupErr: 'IP 哈希不支持备用节点',\n        failTimeout: 'Hata zaman aşımı',\n        failTimeoutHelper:\n            'Sunucu sağlık kontrolleri için zaman penceresi uzunluğu. Bu süre içinde biriken hata sayısı eşiğe ulaştığında, sunucu geçici olarak kaldırılacak ve aynı süre sonunda yeniden denenir. Varsayılan 10 saniye',\n        staticChangePHPHelper: 'Şu anda statik bir web sitesi, PHP web sitesine geçiş yapabilirsiniz',\n        proxyCache: 'Ters Vekil Önbelleği',\n        cacheLimit: 'Önbellek Alanı Sınırı',\n        shareCahe: 'Önbellek Sayısı Bellek Boyutu',\n        cacheExpire: 'Önbellek Son Kullanma Süresi',\n        shareCaheHelper: 'Yaklaşık olarak 1M bellek başına 8000 önbellek nesnesi saklanabilir',\n        cacheLimitHelper: 'Sınır aşıldığında eski önbellek otomatik olarak silinir',\n        cacheExpireJHelper: 'Önbellek, son kullanma süresinden sonra kaçırılırsa silinir',\n        realIP: 'Gerçek IP',\n        ipFrom: 'IP Kaynağı',\n        ipFromHelper:\n            'Güvenilir IP kaynaklarını yapılandırarak, OpenResty HTTP başlıklarında IP bilgilerini analiz eder, ziyaretçilerin gerçek IP adreslerini doğru bir şekilde tanımlar ve kaydeder, erişim günlükleri dahil',\n        ipFromExample1: \"Ön uç bir Frp gibi bir araçsa, Frp'nin IP adresini girebilirsiniz, örneğin 127.0.0.1\",\n        ipFromExample2: 'Ön uç bir CDN ise, CDN’nin IP adres aralığını girebilirsiniz',\n        ipFromExample3:\n            'Emin değilseniz, 0.0.0.0/0 (ipv4) ::/0 (ipv6) girebilirsiniz [Not: Herhangi bir kaynak IP’ye izin vermek güvenli değildir]',\n        http3Helper:\n            'HTTP/3, HTTP/2’nin bir yükseltmesidir, daha hızlı bağlantı hızları ve daha iyi performans sunar, ancak tüm tarayıcılar HTTP/3’ü desteklemez. Etkinleştirilmesi bazı tarayıcıların siteye erişememesine neden olabilir.',\n        cors: 'Çapraz Kaynak Paylaşımı (CORS)',\n        enableCors: 'CORS etkinleştir',\n        allowOrigins: 'İzin verilen alan adları',\n        allowMethods: 'İzin verilen istek yöntemleri',\n        allowHeaders: 'İzin verilen istek başlıkları',\n        allowCredentials: 'Çerezlerin gönderilmesine izin ver',\n        preflight: 'Ön istek hızlı yanıtı',\n        preflightHleper:\n            'Etkinleştirildiğinde, tarayıcı çapraz kaynak ön isteği (OPTIONS isteği) gönderdiğinde, sistem otomatik olarak 204 durum kodunu döndürecek ve gerekli çapraz kaynak yanıt başlıklarını ayarlayacaktır',\n        changeDatabase: 'Veritabanını Değiştir',\n        changeDatabaseHelper1:\n            'Veritabanı ilişkilendirmesi, web sitesinin yedeklenmesi ve geri yüklenmesi için kullanılır.',\n        changeDatabaseHelper2: 'Başka bir veritabanına geçiş, önceki yedeklerin kurtarılamamasına neden olur.',\n        saveCustom: 'Şablon Olarak Kaydet',\n        rainyun: 'Rain Yun',\n        volcengine: 'Volcengine',\n        runtimePortHelper:\n            'Geçerli çalışma zamanı ortamı birden fazla bağlantı noktasına sahip. Lütfen bir vekil bağlantı noktası seçin.',\n        runtimePortWarn: 'Geçerli çalışma zamanı ortamında bağlantı noktası yok, vekil yapılamıyor',\n        cacheWarn: 'Lütfen önce ters vekildeki önbellek anahtarını kapatın',\n        loadBalanceHelper:\n            'Bu bölüm yalnızca yük dengeleme kuralları oluşturur, kuralları kullanmak için lütfen http(s)://<yük dengeleme adı> adresine ters vekil yapın',\n        favorite: 'Favori',\n        cancelFavorite: 'Favoriyi İptal Et',\n        useProxy: 'Vekil Kullan',\n        useProxyHelper: 'Panel ayarlarındaki vekil sunucu adresini kullan',\n        westCN: 'Batı Dijital',\n        openBaseDir: 'Siteler Arası Saldırıları Önle',\n        openBaseDirHelper:\n            'open_basedir, PHP dosya erişim yolunu kısıtlamak için kullanılır, bu siteler arası erişimi önlemeye ve güvenliği artırmaya yardımcı olur',\n        serverCacheTime: 'Sunucu Önbellek Süresi',\n        serverCacheTimeHelper:\n            'Bir isteğin sunucuda önbelleğe alındığı süre. Bu süre boyunca, aynı istekler önbelleğe alınmış sonucu doğrudan döndürür ve kaynak sunucuya istekte bulunmaz.',\n        browserCacheTime: 'Tarayıcı Önbellek Süresi',\n        browserCacheTimeHelper:\n            'Statik kaynakların tarayıcıda yerel olarak önbelleğe alındığı süre, tekrarlayan istekleri azaltır. Kullanıcılar süre dolmadan önce sayfayı yenilediğinde yerel önbelleği doğrudan kullanır.',\n        donotLinkeDB: 'Veritabanına Bağlanma',\n        toWebsiteDir: 'Web Sitesi Dizinine Gir',\n        execParameters: 'Çalıştırma Parametreleri',\n        extCommand: 'Ek Komut',\n        mirror: 'Ayna Kaynağı',\n        execUser: 'Çalıştıran Kullanıcı',\n        execDir: 'Çalıştırma Dizini',\n        packagist: 'Çin Tam Aynası',\n        batchOperate: 'Toplu İşlem',\n        batchOperateHelper: 'Toplu {0} web siteleri, işlemi devam ettir?',\n        stream: 'TCP/UDP Proxy',\n        streamPorts: 'Dinleme Portları',\n        streamPortsHelper:\n            'Harici dinleme bağlantı noktası numarasını ayarlayın, istemciler bu bağlantı noktası üzerinden hizmete erişecektir, virgülle ayırın, örn.: 5222,5223',\n        streamHelper: 'TCP/UDP Port Yönlendirme ve Yük Dengeleme',\n        udp: \"UDP'yi etkinleştir\",\n        syncHtmlHelper: 'PHP ve statik web siteleriyle senkronize et',\n        wafValueHelper: 'Değer',\n    },\n    php: {\n        short_open_tag: 'Kısa etiket desteği',\n        max_execution_time: 'Maksimum betik yürütme süresi',\n        max_input_time: 'Maksimum giriş süresi',\n        memory_limit: 'Betik bellek sınırı',\n        post_max_size: 'POST veri maksimum boyutu',\n        file_uploads: 'Dosya yüklemeye izin verilip verilmeyeceği',\n        upload_max_filesize: 'Yüklenebilecek maksimum dosya boyutu',\n        max_file_uploads: 'Aynı anda yüklenebilecek maksimum dosya sayısı',\n        default_socket_timeout: 'Soket zaman aşımı',\n        error_reporting: 'Hata seviyesi',\n        display_errors: 'Ayrıntılı hata bilgilerinin 출력lenip 출력lenmeyeceği',\n        cgi_fix_pathinfo: 'Pathinfo’nun açılıp açılmayacağı',\n        date_timezone: 'Zaman dilimi',\n        disableFunction: 'Devre dışı bırakma fonksiyonu',\n        disableFunctionHelper:\n            'Devre dışı bırakılacak fonksiyonu girin, örneğin exec, birden fazla için virgülle ayırın',\n        uploadMaxSize: 'Yükleme sınırı',\n        indexHelper:\n            'PHP web sitesinin normal çalışmasını sağlamak için, lütfen kodu index dizinine yerleştirin ve yeniden adlandırmaktan kaçının',\n        extensions: 'Eklenti Şablonları',\n        extension: 'Uzantı',\n        extensionHelper: 'Lütfen birden fazla uzantı için virgülle ayırın',\n        toExtensionsList: 'Uzantı listesini görüntüle',\n        containerConfig: 'Konteyner Yapılandırması',\n        containerConfigHelper:\n            'Oluşturulduktan sonra Yapılandırma - Konteyner Yapılandırması’nda ortam değişkenleri ve diğer bilgiler değiştirilebilir',\n        dateTimezoneHelper: 'Örnek: TZ=Asia/Shanghai (Gerektiğinde ekleyin)',\n        extensionsHelper: 'Birden fazla uzantı için virgül ile ayırın',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: 'Sunucu adının karma tablosu boyutu',\n        clientHeaderBufferSizeHelper: 'İstemci tarafından istenen başlık arabelleği boyutu',\n        clientMaxBodySizeHelper: 'Maksimum Yükleme Dosyası',\n        keepaliveTimeoutHelper: 'Bağlantı Zaman Aşımı',\n        gzipMinLengthHelper: 'Minimum Sıkıştırılmış Dosya',\n        gzipCompLevelHelper: 'Sıkıştırma Oranı',\n        gzipHelper: 'İletim için sıkıştırmayı etkinleştir',\n        connections: 'Aktif bağlantılar',\n        accepts: 'Kabul edilenler',\n        handled: 'İşlenenler',\n        requests: 'İstekler',\n        reading: 'Okuma',\n        writing: 'Yazma',\n        waiting: 'Bekleme',\n        status: 'Geçerli Durum',\n        configResource: 'Yapılandırma',\n        saveAndReload: 'Kaydet ve yeniden yükle',\n        clearProxyCache: 'Ters vekil önbelleğini temizle',\n        clearProxyCacheWarn: 'Bu işlem önbellek dizinindeki tüm dosyaları silecektir. Devam etmek istiyor musunuz?',\n        create: 'Yeni bir modül ekle',\n        update: 'Bir modülü düzenle',\n        params: 'Parametreler',\n        packages: 'Paketler',\n        script: 'Betikler',\n        module: 'Modüller',\n        build: 'Oluştur',\n        buildWarn:\n            'OpenResty’nin oluşturulması belirli miktarda CPU ve bellek ayırmayı gerektirir, bu uzun sürebilir, lütfen sabırlı olun',\n        mirrorUrl: 'Yazılım Kaynağı',\n        paramsHelper: 'Örnek: --add-module=/tmp/ngx_brotli',\n        packagesHelper: 'Örnek: git, curl (virgülle ayrılmış)',\n        scriptHelper:\n            'Derlemeden önce çalıştırılacak betikler, genellikle modül kaynak kodunu indirmek, bağımlılıkları kurmak vb. için',\n        buildHelper:\n            'Modül ekledikten/düzenledikten sonra oluştur’a tıklayın. OpenResty, başarılı oluşturma üzerine otomatik olarak yeniden başlatılacaktır.',\n        defaultHttps: 'HTTPS Anti-sızdırma',\n        defaultHttpsHelper1: 'Bu özelliği etkinleştirerek HTTPS sızdırma sorunlarını çözebilirsiniz.',\n        sslRejectHandshake: 'Varsayılan SSL el sıkışmasını reddet',\n        sslRejectHandshakeHelper:\n            'Etkinleştirilmesi sertifika sızıntısını önleyebilir, varsayılan bir web sitesi ayarlamak bu ayarı geçersiz kılar',\n    },\n    ssl: {\n        create: 'İstek',\n        provider: 'Tür',\n        manualCreate: 'Manuel olarak oluşturuldu',\n        acmeAccount: 'ACME hesabı',\n        resolveDomain: 'Alan adını çözümle',\n        err: 'Hata',\n        value: 'kayıt değeri',\n        dnsResolveHelper:\n            'Lütfen aşağıdaki çözümleme kayıtlarını eklemek için DNS çözümleme servis sağlayıcısına gidin:',\n        detail: 'Detayları görüntüle',\n        msg: 'Bilgi',\n        ssl: 'Sertifika',\n        key: 'Özel anahtar',\n        startDate: 'Geçerlilik zamanı',\n        organization: 'veren organizasyon',\n        renewConfirm: '{0} alan adı için yeni bir sertifika yenilenecek. Devam etmek istiyor musunuz?',\n        autoRenew: 'Otomatik yenileme',\n        autoRenewHelper: 'Son kullanma tarihinden 30 gün önce otomatik olarak yenilenir',\n        renewSuccess: 'Yenileme başarılı',\n        renewWebsite:\n            'Bu sertifika aşağıdaki web siteleriyle ilişkilendirilmiştir ve başvuru aynı anda bu web sitelerine uygulanacaktır',\n        createAcme: 'Hesap Oluştur',\n        acmeHelper: 'Acme Hesabı ücretsiz sertifikalar başvurusu için kullanılır',\n        upload: 'İçe aktar',\n        applyType: 'Başvuru yöntemi',\n        apply: 'Yenile',\n        applyStart: 'Sertifika başvurusu başlıyor',\n        getDnsResolve: 'DNS çözümleme değeri alınıyor, lütfen bekleyin...',\n        selfSigned: 'Kendi kendine imzalı CA’yı yönet',\n        ca: 'Sertifika yetkilisi',\n        commonName: 'Ortak ad',\n        caName: 'Sertifika yetkilisi adı',\n        company: 'Organizasyon adı',\n        department: 'Organizasyon birimi adı',\n        city: 'Yer adı',\n        province: 'Eyalet veya il adı',\n        country: 'Ülke adı (2 harfli kod)',\n        commonNameHelper: 'Örnek: ',\n        selfSign: 'Sertifika ver',\n        days: 'geçerlilik süresi',\n        domainHelper: 'Her satırda bir alan adı, * ve IP adresini destekler',\n        pushDir: 'Sertifikayı yerel dizine aktar',\n        dir: 'Dizin',\n        pushDirHelper:\n            'Sertifika dosyası \"fullchain.pem\" ve anahtar dosyası \"privkey.pem\" bu dizinde oluşturulacaktır.',\n        organizationDetail: 'Organizasyon detayları',\n        fromWebsite: 'Web sitesinden',\n        dnsMauanlHelper:\n            'Manuel çözümleme modunda, oluşturduktan sonra DNS çözümleme değerini almak için başvuru düğmesine tıklamanız gerekir',\n        httpHelper:\n            'HTTP modunu kullanmak, OpenResty’nin kurulmasını gerektirir ve joker karakter alan adı sertifikaları için başvuruyu desteklemez.',\n        buypassHelper: 'Buypass, Çin anakarasında erişilebilir değil',\n        googleHelper: 'EAB HmacKey ve EAB kid nasıl alınır',\n        googleCloudHelper: 'Google Cloud API, Çin anakarasının çoğu yerinde erişilebilir değil',\n        skipDNSCheck: 'DNS kontrolünü atla',\n        skipDNSCheckHelper: 'Sertifika isteği sırasında zaman aşımı sorunuyla karşılaşırsanız burayı işaretleyin.',\n        cfHelper: 'Küresel API Anahtarını kullanmayın',\n        deprecated: 'kaldırılacak',\n        deprecatedHelper:\n            'Bakım durduruldu ve gelecek bir sürümde terk edilebilir. Lütfen analiz için Tencent Cloud yöntemini kullanın',\n        disableCNAME: 'CNAME’yi devre dışı bırak',\n        disableCNAMEHelper: 'Alan adında bir CNAME kaydı varsa ve istek başarısız olursa burayı işaretleyin.',\n        nameserver: 'DNS sunucusu',\n        nameserverHelper: 'Alan adlarını doğrulamak için özel bir DNS sunucusu kullanın.',\n        edit: 'Sertifikayı düzenle',\n        execShell: 'Sertifika isteğinden sonra betiği çalıştır.',\n        shell: 'Betik içeriği',\n        shellHelper:\n            'Betiğin varsayılan yürütme dizini, 1Panel kurulum dizinidir. Sertifika yerel dizine aktarılırsa, yürütme dizini sertifika aktarma dizini olacaktır. Varsayılan yürütme zaman aşımı 30 dakikadır.',\n        customAcme: 'Özel ACME Servisi',\n        customAcmeURL: 'ACME Servis URL’si',\n        baiduCloud: 'Baidu Cloud',\n        pushNode: 'Diğer Düğümlere Senkronize Et',\n        pushNodeHelper: 'Başvuru/yenilemeden sonra seçilen düğümlere gönder',\n        fromMaster: 'Ana Düğümden Gönder',\n        hostedZoneID: 'Hosted Zone ID',\n        isIP: 'IP Sertifikası',\n        useEAB: 'EAB kimlik doğrulamasını kullan',\n    },\n    firewall: {\n        create: 'Kural oluştur',\n        edit: 'Kuralı düzenle',\n        ccDeny: 'CC Koruması',\n        ipWhiteList: 'IP izin listesi',\n        ipBlockList: 'IP engelleme listesi',\n        fileExtBlockList: 'Dosya uzantısı engelleme listesi',\n        urlWhiteList: 'URL izin listesi',\n        urlBlockList: 'URL engelleme listesi',\n        argsCheck: 'GET parametre kontrolü',\n        postCheck: 'POST parametre doğrulaması',\n        cookieBlockList: 'Çerez engelleme listesi',\n        dockerHelper:\n            'Mevcut güvenlik duvarı konteyner port eşlemesini devre dışı bırakamaz. Yüklü uygulamalar, uygulama parametrelerini düzenlemek ve port serbest bırakma kurallarını yapılandırmak için [Yüklü] sayfasına gidebilir.',\n        iptablesHelper:\n            \"Sistemin {0} güvenlik duvarını kullandığı tespit edildi. iptables'a geçmek için lütfen önce manuel olarak kaldırın!\",\n        quickJump: 'Hızlı erişim',\n        used: 'Kullanıldı',\n        unUsed: 'Kullanılmadı',\n        dockerRestart: 'Güvenlik duvarı işlemleri Docker hizmetinin yeniden başlatılmasını gerektirir',\n        firewallHelper: '{0} sistem güvenlik duvarı',\n        firewallNotStart: 'Sistem güvenlik duvarı şu anda etkin değil. Önce etkinleştirin.',\n        restartFirewallHelper: 'Bu işlem mevcut güvenlik duvarını yeniden başlatacak. Devam etmek istiyor musunuz?',\n        stopFirewallHelper: 'Bu, sunucunun güvenlik korumasını kaybetmesine neden olacak. Devam etmek istiyor musunuz?',\n        startFirewallHelper:\n            'Güvenlik duvarı etkinleştirildiğinde, sunucu güvenliği daha iyi korunabilir. Devam etmek istiyor musunuz?',\n        noPing: 'Ping’i devre dışı bırak',\n        enableBanPing: 'Ping Engelle',\n        disableBanPing: 'Ping Engellemeyi Kaldır',\n        noPingTitle: 'Ping’i devre dışı bırak',\n        noPingHelper:\n            'Bu, ping’i devre dışı bırakacak ve sunucu ICMP yanıtını geri göndermeyecek. Devam etmek istiyor musunuz?',\n        onPingHelper: 'Bu, ping’i etkinleştirecek ve hackerlar sunucunuzu keşfedebilir. Devam etmek istiyor musunuz?',\n        changeStrategy: '{0} stratejisini değiştir',\n        changeStrategyIPHelper1:\n            'IP adresi stratejisini [reddet] olarak değiştirin. IP adresi ayarlandıktan sonra sunucuya erişim yasaklanır. Devam etmek istiyor musunuz?',\n        changeStrategyIPHelper2:\n            'IP adresi stratejisini [izin ver] olarak değiştirin. IP adresi ayarlandıktan sonra normal erişim geri yüklenir. Devam etmek istiyor musunuz?',\n        changeStrategyPortHelper1:\n            'Bağlantı noktası politikasını [bırak] olarak değiştirin. Bağlantı noktası politikası ayarlandıktan sonra dış erişim reddedilir. Devam etmek istiyor musunuz?',\n        changeStrategyPortHelper2:\n            'Port politikasını [accept] olarak değiştirin. Port politikası ayarlandıktan sonra normal port erişimi geri yüklenecek. Devam etmek istiyor musunuz?',\n        stop: 'Durdur',\n        portFormatError: 'Bu alan geçerli bir port olmalıdır.',\n        portHelper1: 'Birden fazla port, ör. 8080 ve 8081',\n        portHelper2: 'Aralık portu, ör. 8080-8089',\n        changeStrategyHelper:\n            '[{1}] {0} stratejisini [{2}] olarak değiştirin. Ayar yapıldıktan sonra {0}, dışarıdan {2} erişimi sağlayacak. Devam etmek istiyor musunuz?',\n        strategy: 'Strateji',\n        accept: 'Kabul Et',\n        drop: 'Reddet',\n        anyWhere: 'Herhangi',\n        address: 'Belirtilen IP’ler',\n        addressHelper: 'IP adresi veya IP segmentini destekler',\n        allow: 'İzin Ver',\n        deny: 'Reddet',\n        addressFormatError: 'Bu alan geçerli bir IP adresi olmalıdır.',\n        addressHelper1: 'IP adresi veya IP aralığını destekler. Örneğin, \"172.16.10.11\" veya \"172.16.10.0/24\".',\n        addressHelper2: 'Birden fazla IP adresi için virgülle ayırın. Örneğin, \"172.16.10.11, 172.16.10.0/24\".',\n        allIP: 'Tüm IP’ler',\n        portRule: 'Kural | Kurallar',\n        forwardRule: 'Port-Yönlendirme kuralı | Port-Yönlendirme kuralları',\n        ipRule: 'IP kuralı | IP kuralları',\n        userAgent: 'Kullanıcı-Aracısı filtresi',\n        sourcePort: 'Kaynak port',\n        targetIP: 'Hedef IP',\n        targetPort: 'Hedef port',\n        forwardHelper1: 'Yerel porta yönlendirmek istiyorsanız, hedef IP \"127.0.0.1\" olarak ayarlanmalıdır.',\n        forwardHelper2: 'Yerel porta yönlendirmek için hedef IP’yi boş bırakın.',\n        forwardPortHelper: 'Port aralıklarını destekler, örn.: 8080-8089',\n        forwardInboundInterface: 'İletme Gelen Ağ Arayüzü',\n        exportHelper: '{0} güvenlik duvarı kuralını dışa aktarmak üzere. Devam etmek istiyor musunuz?',\n        importSuccess: '{0} kural başarıyla içe aktarıldı',\n        importPartialSuccess: 'İçe aktarma tamamlandı: {0} başarılı, {1} başarısız',\n        ipv4Limit: 'Mevcut işlem yalnızca IPv4 adreslerini destekler',\n        basicStatus: 'Mevcut zincir {0} bağlı değil, lütfen önce bağlayın!',\n        baseIptables: 'iptables Servisi',\n        forwardIptables: 'iptables Port Yönlendirme Servisi',\n        advanceIptables: 'iptables Gelişmiş Yapılandırma Servisi',\n        initMsg: '{0} başlatılmak üzere, devam etmek istiyor musunuz?',\n        initHelper:\n            '{0} başlatılmadığı tespit edildi. Lütfen üst durum çubuğundaki başlatma düğmesine tıklayarak yapılandırın!',\n        bindHelper: 'Bağla - Güvenlik duvarı kuralları yalnızca durum bağlı olduğunda etkili olur. Onaylıyor musunuz?',\n        unbindHelper:\n            'Bağlantıyı Kaldır - Bağlantı kaldırıldığında, eklenen tüm güvenlik duvarı kuralları geçersiz olacaktır. Dikkatli ilerleyin. Onaylıyor musunuz?',\n        defaultStrategy: 'Mevcut zincir {0} için varsayılan politika {1}',\n        defaultStrategy2:\n            'Mevcut zincir {0} için varsayılan politika {1}, mevcut durum bağlı değil. Eklenen güvenlik duvarı kuralları bağlandıktan sonra etkili olacak!',\n        filterRule: 'Filtre Kuralı',\n        filterHelper:\n            'Filtre kuralları, INPUT/OUTPUT seviyesinde ağ trafiğini kontrol etmenize izin verir. Sistemi kilitlememek için dikkatli yapılandırın.',\n        chain: 'Zincir',\n        targetChain: 'Hedef Zincir',\n        sourceIP: 'Kaynak IP',\n        destIP: 'Hedef IP',\n        inboundDirection: 'Gelen Yön',\n        outboundDirection: 'Giden Yön',\n        destPort: 'Hedef Port',\n        action: 'Eylem',\n        reject: 'Reddet',\n        sourceIPHelper: 'CIDR formatı, örn. 192.168.1.0/24. Tüm adresler için boş bırakın',\n        destIPHelper: 'CIDR formatı, örn. 10.0.0.0/8. Tüm adresler için boş bırakın',\n        portHelper: '0 herhangi bir port anlamına gelir',\n        allPorts: 'Tüm Portlar',\n        deleteRuleConfirm: '{0} kural silinecek. Devam etmek istiyor musunuz?',\n        advancedControl: 'Gelişmiş kontrol',\n        advancedControlNotAvailable:\n            'Şu anda {0} güvenlik duvarı kullanılıyor, gelişmiş kurallar yalnızca iptables destekler',\n        destination: 'Hedef',\n    },\n    runtime: {\n        runtime: 'Çalışma Zamanı',\n        workDir: 'Çalışma dizini',\n        localHelper: 'Yerel ortam kurulumu ve çevrimdışı ortam kullanımı sorunları için lütfen bakın ',\n        versionHelper: 'PHP sürümü, ör. v8.0',\n        buildHelper:\n            'Daha fazla eklenti seçilirse, görüntü oluşturma sürecinde CPU kullanımı artar. Tüm eklentileri seçmekten kaçının.',\n        openrestyWarn: 'PHP’nin OpenResty’ye yükseltilmesi için 1.21.4.1 veya daha yeni bir sürüm gereklidir',\n        toupgrade: 'Yükseltmek İçin',\n        edit: 'Çalışma zamanını düzenle',\n        extendHelper:\n            'İhtiyacınız olan eklentiler listede yoksa, eklenti adını manuel olarak girebilirsiniz. Örneğin, \"sockets\" girin ve ardından ilkini seçin.',\n        rebuildHelper: 'Eklenti düzenlendikten sonra PHP uygulamasının yeniden oluşturulması gerekir',\n        rebuild: 'PHP Uygulamasını Yeniden Oluştur',\n        source: 'PHP eklenti kaynağı',\n        ustc: 'Çin Bilim ve Teknoloji Üniversitesi',\n        netease: 'Netease',\n        aliyun: 'Alibaba Cloud',\n        tsinghua: 'Tsinghua Üniversitesi',\n        xtomhk: 'XTOM Ayna İstasyonu (Hong Kong)',\n        xtom: 'XTOM Ayna İstasyonu (Global)',\n        phpsourceHelper: 'Ağ ortamınıza göre uygun bir kaynak seçin.',\n        appPort: 'Uygulama portu',\n        externalPort: 'Dış port',\n        packageManager: 'Paket yöneticisi',\n        codeDir: 'Kod dizini',\n        appPortHelper: 'Uygulama tarafından kullanılan port.',\n        externalPortHelper: 'Dış dünyaya açılan port.',\n        runScript: 'Çalıştırma betiği',\n        runScriptHelper: 'Kaynak dizindeki package.json dosyasından ayrıştırılan başlatma komutları listesi.',\n        open: 'Aç',\n        operatorHelper: 'Seçilen çalışma ortamında {0} işlemi gerçekleştirilecek. Devam etmek istiyor musunuz?',\n        taobao: 'Taobao',\n        tencent: 'Tencent',\n        imageSource: 'Görüntü kaynağı',\n        moduleManager: 'Modül Yönetimi',\n        module: 'Modül',\n        nodeOperatorHelper:\n            '{0} {1} modülü mü? İşlem, çalışma ortamında anormalliklere neden olabilir, lütfen devam etmeden önce onaylayın',\n        customScript: 'Özel başlatma komutu',\n        customScriptHelper:\n            'Lütfen tam başlatma komutunu girin, örneğin: npm run start. PM2 başlatma komutları için lütfen pm2-runtime ile değiştirin, aksi takdirde başlatma başarısız olacaktır.',\n        portError: 'Aynı portu tekrarlamayın.',\n        systemRestartHelper:\n            'Durum açıklaması: Kesinti - sistem yeniden başlatılması nedeniyle durum alımı başarısız oldu',\n        javaScriptHelper: 'Tam bir başlatma komutu sağlayın. Örneğin, \"java -jar halo.jar -Xmx1024M -Xms256M\".',\n        javaDirHelper: 'Dizin, jar dosyalarını içermeli, alt dizinler de kabul edilir',\n        goHelper: 'Tam bir başlatma komutu sağlayın. Örneğin, \"go run main.go\" veya \"./main\".',\n        goDirHelper: 'Dizin veya alt dizin, Go veya ikili dosyaları içermelidir.',\n        extension: 'Eklenti',\n        installExtension: '{0} eklentisini yüklemeyi onaylıyor musunuz?',\n        loadedExtension: 'Yüklü Eklenti',\n        popularExtension: 'Popüler Eklenti',\n        uninstallExtension: '{0} eklentisini kaldırmak istediğinizden emin misiniz?',\n        phpConfigHelper:\n            'Yapılandırmayı değiştirmek, çalışma ortamını yeniden başlatmayı gerektirir, devam etmek istiyor musunuz?',\n        operateMode: 'çalışma modu',\n        dynamic: 'dinamik',\n        static: 'statik',\n        ondemand: 'isteğe bağlı',\n        dynamicHelper:\n            'işlem sayısını dinamik olarak ayarlar, yüksek esneklik, büyük trafik dalgalanmaları olan veya düşük bellekli web siteleri için uygundur',\n        staticHelper:\n            'sabit işlem sayısı, yüksek eşzamanlılık ve sabit trafikli web siteleri için uygundur, yüksek kaynak tüketimi',\n        ondemandHelper:\n            'işlemler gerektiğinde başlatılır ve yok edilir, kaynak kullanımı optimize edilir, ancak ilk yanıt yavaş olabilir',\n        max_children: 'oluşturulmasına izin verilen maksimum işlem sayısı',\n        start_servers: 'başlangıçta oluşturulan işlem sayısı',\n        min_spare_servers: 'boşta kalan minimum işlem sayısı',\n        max_spare_servers: 'boşta kalan maksimum işlem sayısı',\n        envKey: 'Ad',\n        envValue: 'Değer',\n        environment: 'Çevre Değişkeni',\n        pythonHelper:\n            'Tam bir başlatma komutu sağlayın. Örneğin, \"pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000\".',\n        dotnetHelper: 'Tam bir başlatma komutu sağlayın. Örneğin, \"dotnet MyWebApp.dll\".',\n        dirHelper: 'Not: Lütfen kapsayıcı içindeki dizin yolunu doldurun',\n        concurrency: 'Eşzamanlılık Şeması',\n        loadStatus: 'Yük Durumu',\n        extraHosts: 'Ana bilgisayar eşleme',\n    },\n    process: {\n        pid: 'İşlem Kimliği',\n        ppid: 'Üst işlem Kimliği',\n        numThreads: 'Konular',\n        memory: 'Bellek',\n        diskRead: 'Disk okuma',\n        diskWrite: 'Disk yazma',\n        netSent: 'yukarı bağlantı',\n        netRecv: 'aşağı bağlantı',\n        numConnections: 'Bağlantılar',\n        startTime: 'Başlangıç zamanı',\n        running: 'Çalışıyor',\n        sleep: 'uyku',\n        stop: 'durdur',\n        idle: 'boşta',\n        zombie: 'zombi işlem',\n        wait: 'bekliyor',\n        lock: 'kilit',\n        blocked: 'engellendi',\n        cmdLine: 'Başlatma komutu',\n        basic: 'Temel',\n        mem: 'Bellek',\n        openFiles: 'Açık dosyalar',\n        env: 'Çevreler',\n        noenv: 'Hiçbiri',\n        net: 'Ağ bağlantıları',\n        laddr: 'Yerel adres/port',\n        raddr: 'Uzak adres/port',\n        stopProcess: 'Sonlandır',\n        viewDetails: 'Detayları görüntüle',\n        stopProcessWarn: 'Bu işlemi (PID:{0}) sonlandırmak istediğinizden emin misiniz?',\n        kill: 'İşlemi Sonlandır',\n        killNow: 'Hemen Sonlandır',\n        killHelper:\n            '{0} işlemini sonlandırmak bazı programların düzgün çalışmamasına neden olabilir. Devam etmek istiyor musunuz?',\n        processName: 'İşlem adı',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: 'İşlem durumu alınamadı, lütfen supervisor hizmetinin durumunu kontrol edin.',\n            notSupport: 'Supervisor hizmeti algılanmadı, lütfen betik kütüphanesi sayfasından manuel olarak kurun',\n            list: 'Arka plan işlemi',\n            config: 'Supervisor yapılandırması',\n            primaryConfig: 'Ana yapılandırma dosyası konumu',\n            notSupportCtl: 'supervisorctl algılanmadı, lütfen betik kütüphanesi sayfasından manuel olarak kurun',\n            user: 'Kullanıcı',\n            command: 'Komut',\n            dir: 'Dizin',\n            numprocs: 'İşlem sayısı',\n            initWarn:\n                'Bu, ana yapılandırma dosyasındaki \"[include]\" bölümündeki \"files\" değerini değiştirecektir. Diğer yapılandırma dosyasının dizini şu şekilde olacaktır: \"{1Panel kurulum dizini}/1panel/tools/supervisord/supervisor.d/\".',\n            operatorHelper: '{0} üzerinde {1} işlemi gerçekleştirilecek, devam etmek istiyor musunuz?',\n            uptime: 'Çalışma süresi',\n            notStartWarn: 'Supervisor başlatılmadı. Önce başlatın.',\n            serviceName: 'Hizmet adı',\n            initHelper:\n                'Supervisor hizmeti algılandı ancak başlatılmadı. Lütfen üst durum çubuğundaki başlatma düğmesine tıklayarak yapılandırın.',\n            serviceNameHelper:\n                'systemctl tarafından yönetilen Supervisor hizmet adı, genellikle supervisor veya supervisord',\n            restartHelper:\n                'Bu, başlatma işleminden sonra hizmeti yeniden başlatacak ve mevcut tüm arka plan işlemlerinin durmasına neden olacaktır.',\n            RUNNING: 'Çalışıyor',\n            STOPPED: 'Durduruldu',\n            STOPPING: 'Durduruluyor',\n            STARTING: 'Başlatılıyor',\n            FATAL: 'Başlatma başarısız',\n            BACKOFF: 'Başlatma istisnası',\n            ERROR: 'Hata',\n            statusCode: 'Durum kodu',\n            manage: 'Yönetim',\n            autoRestart: 'Otomatik Yeniden Başlatma',\n            EXITED: 'Çıkıldı',\n            autoRestartHelper: 'Program çöktükten sonra otomatik olarak yeniden başlatılsın mı?',\n            autoStart: 'Otomatik Başlat',\n            autoStartHelper: 'Supervisor başlatıldıktan sonra servis otomatik olarak başlatılsın mı?',\n        },\n    },\n    disk: {\n        management: 'Disk Yönetimi',\n        partition: 'Bölüm',\n        unmount: 'Bağını Kaldır',\n        unmountHelper: \"Bölüm {0}'ın bağını kaldırmak istiyor musunuz?\",\n        mount: 'Bağla',\n        partitionAlert:\n            'Disk bölümleme, diske biçimlendirme gerektirir ve mevcut veriler silinir. Lütfen verilerinizi önceden kaydedin veya anlık görüntü alın.',\n        mountPoint: 'Bağlama Noktası',\n        systemDisk: 'Sistem Diski',\n        unpartitionedDisk: 'Bölümlendirilmemiş Disk',\n        handlePartition: 'Şimdi Bölümle',\n        filesystem: 'Dosya Sistemi',\n        unmounted: 'Bağlı Değil',\n        cannotOperate: 'Operasyon Yapılamıyor',\n        systemDiskHelper: 'İpucu: Mevcut disk sistem diskidir, işlem yapılamaz.',\n        autoMount: 'Otomatik Bağlama',\n        model: 'Cihaz Modeli',\n        diskType: 'Disk Türü',\n        serial: 'Seri Numarası',\n        noFail: 'Bağlama hatası sistem başlangıcını etkilemez',\n    },\n    xpack: {\n        expiresTrialAlert:\n            'Nazik hatırlatma: Pro deneme sürümünüz {0} gün içinde sona erecek ve tüm Pro özellikleri kullanılamaz hale gelecektir. Lütfen zamanında yenileyin veya tam sürüme yükseltin.',\n        expiresAlert:\n            'Nazik hatırlatma: Pro lisansınız {0} gün içinde sona erecek ve tüm Pro özellikleri kullanılamaz hale gelecektir. Lütfen devam eden kullanım için zamanında yenileyin.',\n        menu: 'Pro',\n        upage: 'AI Web Sitesi Oluşturucu',\n        proAlert: 'Bu özelliği kullanmak için Proya yükseltin',\n        app: {\n            app: 'Uygulama',\n            title: 'Panel Takma Adı',\n            titleHelper: 'Uygulamada gösterim için kullanılan panel takma adı (varsayılan panel takma adı)',\n            qrCode: 'QR Kodu',\n            apiStatusHelper: 'Panel Uygulaması, API arayüz özelliğinin etkinleştirilmesini gerektirir',\n            apiInterfaceHelper:\n                'Panel API arayüz erişimini destekler (bu özellik, panel uygulaması için etkinleştirilmelidir)',\n            apiInterfaceHelper1:\n                'Panel uygulama erişimi, ziyaretçinin beyaz listeye eklenmesini gerektirir; sabit olmayan IP’ler için 0.0.0.0/0(tüm IPv4), ::/0 (tüm IPv6) eklenmesi önerilir',\n            qrCodeExpired: 'Yenileme zamanı',\n            apiLeakageHelper: 'QR kodunu paylaşmayın. Yalnızca güvenilir ortamlarda kullanıldığından emin olun.',\n        },\n        waf: {\n            name: 'WAF',\n            blackWhite: 'Kara ve Beyaz Liste',\n            globalSetting: 'Genel Ayarlar',\n            websiteSetting: 'Web Sitesi Ayarları',\n            blockRecords: 'Engellenen Kayıtlar',\n            world: 'Dünya',\n            china: 'Çin',\n            intercept: 'Engelleme',\n            request: 'İstekler',\n            count4xx: '4xx Miktarı',\n            count5xx: '5xx Miktarı',\n            todayStatus: 'Bugünün Durumu',\n            reqMap: 'Saldırı Haritası (Son 30 gün)',\n            resource: 'Kaynak',\n            count: 'Miktar',\n            hight: 'Yüksek',\n            low: 'Düşük',\n            reqCount: 'İstekler',\n            interceptCount: 'Engelleme Sayısı',\n            requestTrends: 'İstek Trendleri (Son 7 Gün)',\n            interceptTrends: 'Engelleme Trendleri (Son 7 Gün)',\n            whiteList: 'Beyaz Liste',\n            blackList: 'Kara Liste',\n            ipBlackListHelper: 'Kara listedeki IP adresleri web sitesine erişimden engellenir',\n            ipWhiteListHelper: 'Beyaz listedeki IP adresleri tüm kısıtlamaları bypass eder',\n            uaBlackListHelper: 'Kara listedeki Kullanıcı-Aracısı değerlerine sahip istekler engellenir',\n            uaWhiteListHelper:\n                'Beyaz listedeki Kullanıcı-Aracısı değerlerine sahip istekler tüm kısıtlamaları bypass eder',\n            urlBlackListHelper: 'Kara listedeki URL’lere yapılan istekler engellenir',\n            urlWhiteListHelper: 'Beyaz listedeki URL’lere yapılan istekler tüm kısıtlamaları bypass eder',\n            ccHelper: 'Bir web sitesi, aynı IP’den {0} saniye içinde {1} istekten fazla alırsa, IP {2} için engellenir',\n            blockTime: 'Engelleme Süresi',\n            attackHelper: 'Toplam engellemeler {0} saniye içinde {1}’i aşarsa, IP {2} için engellenir',\n            notFoundHelper: 'Toplam istekler {0} saniye içinde {1} kez 404 hatası döndürürse, IP {2} için engellenir',\n            frequencyLimit: 'Sıklık Sınırı',\n            regionLimit: 'Bölge Sınırı',\n            defaultRule: 'Varsayılan Kurallar',\n            accessFrequencyLimit: 'Erişim Sıklığı Sınırı',\n            attackLimit: 'Saldırı Sıklığı Sınırı',\n            notFoundLimit: '404 Sıklık Sınırı',\n            urlLimit: 'URL Sıklık Sınırı',\n            urlLimitHelper: 'Tek bir URL için erişim sıklığını ayarlayın',\n            sqliDefense: 'SQL Enjeksiyon Koruması',\n            sqliHelper: 'İsteklerde SQL enjeksiyonunu algılar ve engeller',\n            xssHelper: 'İsteklerde XSS’i algılar ve engeller',\n            xssDefense: 'XSS Koruması',\n            uaDefense: 'Kötü Amaçlı Kullanıcı-Aracısı Kuralları',\n            uaHelper: 'Yaygın kötü amaçlı botları tanımlamak için kuralları içerir',\n            argsDefense: 'Kötü Amaçlı Parametre Kuralları',\n            argsHelper: 'Kötü amaçlı parametreler içeren istekleri engeller',\n            cookieDefense: 'Kötü Amaçlı Çerez Kuralları',\n            cookieHelper: 'Kötü amaçlı çerezlerin isteklerde taşınmasını yasaklar',\n            headerDefense: 'Kötü Amaçlı Başlık Kuralları',\n            headerHelper: 'Kötü amaçlı başlıklar içeren istekleri yasaklar',\n            httpRule: 'HTTP İstek Yöntemi Kuralları',\n            httpHelper:\n                'İzin verilen yöntem türlerini ayarlayın. Belirli erişim türlerini kısıtlamak istiyorsanız, bu tür düğmeyi kapatın. Örneğin: yalnızca GET türüne izin veriliyorsa, GET dışındaki diğer tür düğmeleri kapatmanız gerekir',\n            geoRule: 'Bölgesel Erişim Kısıtlamaları',\n            geoHelper:\n                'Web sitenize belirli bölgelerden erişimi kısıtlayın, örneğin: Çin anakarasına erişime izin veriliyorsa, Çin anakarası dışındaki istekler engellenir',\n            ipLocation: 'IP Konumu',\n            action: 'Eylem',\n            ruleType: 'Saldırı Türü',\n            ipHelper: 'IP adresini girin',\n            attackLog: 'Saldırı Günlüğü',\n            rule: 'Kural',\n            ipArr: 'IPV4 Aralığı',\n            ipStart: 'Başlangıç IP',\n            ipEnd: 'Bitiş IP',\n            ipv4: 'IPv4',\n            ipv6: 'IPv6',\n            urlDefense: 'URL Kuralları',\n            urlHelper: 'Yasak URL',\n            dirFilter: 'Dizin Filtresi',\n            sqlInject: 'SQL Enjeksiyonu',\n            xss: 'XSS',\n            phpExec: 'PHP Betiği Yürütme',\n            oneWordTrojan: 'Tek Kelime Truva Atı',\n            appFilter: 'Tehlikeli Dizin Filtreleme',\n            webShell: 'Webshell',\n            args: 'Kötü Amaçlı Parametreler',\n            protocolFilter: 'Protokol Filtresi',\n            javaFilter: 'Java Tehlikeli Dosya Filtreleme',\n            scannerFilter: 'Tarayıcı Filtresi',\n            escapeFilter: 'Kaçış Filtresi',\n            customRule: 'Özel Kurallar',\n            httpMethod: 'HTTP Yöntemi Filtresi',\n            fileExt: 'Dosya Yükleme Sınırı',\n            fileExtHelper: 'Yükleme için yasak dosya uzantıları',\n            deny: 'Yasakla',\n            allow: 'İzin Ver',\n            field: 'Nesne',\n            pattern: 'Koşul',\n            ruleContent: 'İçerik',\n            contain: 'içerir',\n            equal: 'eşittir',\n            regex: 'düzenli ifade',\n            notEqual: 'Eşit değil',\n            customRuleHelper: 'Belirtilen koşullara göre eylemler gerçekleştirin',\n            actionAllow: 'İzin Ver',\n            blockIP: 'IP’yi Engelle',\n            code: 'Dönüş Durum Kodu',\n            noRes: 'Bağlantıyı Kes (444)',\n            badReq: 'Geçersiz Parametreler (400)',\n            forbidden: 'Erişim Yasak (403)',\n            serverErr: 'Sunucu Hatası (500)',\n            resHtml: 'Yanıt Sayfası',\n            allowHelper: 'Erişime izin vermek, sonraki WAF kurallarını atlar, lütfen dikkatli kullanın',\n            captcha: 'insan-makine doğrulaması',\n            fiveSeconds: '5 Saniye doğrulama',\n            location: 'Bölge',\n            redisConfig: 'Redis Yapılandırması',\n            redisHelper: 'Geçici olarak engellenen IP’leri sürdürmek için Redis’i etkinleştirin',\n            wafHelper: 'WAF kapatıldığında tüm web siteleri korumayı kaybeder',\n            attackIP: 'Saldıran IP',\n            attackParam: 'Saldırı Detayları',\n            execRule: 'Vurulan Kural',\n            acl: 'ACL',\n            sql: 'SQL Enjeksiyonu',\n            cc: 'Erişim Sıklığı Sınırı',\n            isBlocking: 'Engellendi',\n            isFree: 'Engel Kaldırıldı',\n            unLock: 'Kilidi Aç',\n            unLockHelper: 'IP: {0} kilidini açmak istiyor musunuz?',\n            saveDefault: 'Varsayılanı Kaydet',\n            saveToWebsite: 'Web Sitesine Uygula',\n            saveToWebsiteHelper: 'Mevcut ayarları tüm web sitelerine uygulamak mı?',\n            websiteHelper:\n                'Web sitesi oluşturmak için varsayılan ayarlar buradadır. Değişikliklerin etkili olması için web sitesine uygulanması gerekir',\n            websiteHelper2:\n                'Web sitesi oluşturmak için varsayılan ayarlar buradadır. Lütfen web sitesinde özel yapılandırmayı değiştirin',\n            ipGroup: 'IP Grubu',\n            ipGroupHelper:\n                'Her satırda bir IP veya IP segmenti, IPv4 ve IPv6 destekler, örneğin: 192.168.1.1 veya 192.168.1.0/24',\n            ipBlack: 'IP kara listesi',\n            openRestyAlert: 'OpenResty sürümünün {0} üzerinde olması gerekir',\n            initAlert:\n                'İlk kullanım için başlatma gereklidir, web sitesi yapılandırma dosyası değiştirilecek ve mevcut WAF yapılandırması kaybolacaktır. Lütfen önceden OpenResty’yi yedeklediğinizden emin olun',\n            initHelper: 'Başlatma işlemi mevcut WAF yapılandırmasını temizler. Başlatmak istediğinizden emin misiniz?',\n            mainSwitch: 'Ana Anahtar',\n            websiteAlert: 'Lütfen önce bir web sitesi oluşturun',\n            defaultUrlBlack: 'URL Kuralları',\n            htmlRes: 'Engelleme Sayfası',\n            urlSearchHelper: 'Bulanık arama için URL girin',\n            toCreate: 'Oluştur',\n            closeWaf: 'WAF’ı Kapat',\n            closeWafHelper:\n                'WAF’ı kapatmak web sitesinin korumasını kaybetmesine neden olur, devam etmek istiyor musunuz?',\n            addblack: 'Kara',\n            addwhite: 'Beyaz Ekle',\n            addblackHelper: 'IP:{0}’ı varsayılan kara listeye eklemek mi?',\n            addwhiteHelper: 'IP:{0}’ı varsayılan beyaz listeye eklemek mi?',\n            defaultUaBlack: 'Kullanıcı-Aracısı kuralı',\n            defaultIpBlack: 'Kötü Amaçlı IP Grubu',\n            cookie: 'Çerez Kuralları',\n            urlBlack: 'URL Kara Listesi',\n            uaBlack: 'Kullanıcı-Aracısı kara listesi',\n            attackCount: 'Saldırı sıklığı sınırı',\n            fileExtCheck: 'Dosya yükleme sınırı',\n            geoRestrict: 'Bölgesel erişim kısıtlaması',\n            attacklog: 'Engelleme Kaydı',\n            unknownWebsite: 'Yetkisiz alan adı erişimi',\n            geoRuleEmpty: 'Bölge boş olamaz',\n            unknown: 'Web Sitesi Mevcut Değil',\n            geo: 'Bölge Kısıtlaması',\n            revertHtml: '{0}’ı varsayılan sayfa olarak geri yüklemek istiyor musunuz?',\n            five_seconds: '5 Saniye doğrulama',\n            header: 'Başlık kuralları',\n            methodWhite: 'HTTP kuralları',\n            expiryDate: 'Son Kullanım Tarihi',\n            expiryDateHelper: 'Doğrulama geçildikten sonra, geçerlilik süresi içinde tekrar doğrulanmaz',\n            defaultIpBlackHelper: 'İnternetten toplanan bazı kötü amaçlı IP’ler erişimi önlemek için',\n            notFoundCount: '404 Sıklık Sınırı',\n            matchValue: 'Eşleşme değeri',\n            headerName: 'İngilizce, sayılar, - ile başlayan özel olmayan karakterleri destekler, uzunluk 3-30',\n            cdnHelper: 'CDN kullanan web siteleri, doğru kaynak IP’yi almak için burayı açabilir',\n            clearLogWarn: 'Günlük temizleme geri alınamaz, devam etmek istiyor musunuz?',\n            commonRuleHelper: 'Kural bulanık eşleşmedir',\n            blockIPHelper:\n                'Engellenen IP’ler geçici olarak OpenResty’de saklanır ve OpenResty yeniden başlatıldığında engel kaldırılır. Engelleme fonksiyonu ile kalıcı olarak engellenebilir',\n            addWhiteUrlHelper: 'URL {0}’ı beyaz listeye eklemek mi?',\n            dashHelper: 'Topluluk sürümü, genel ayarlar ve web sitesi ayarlarındaki işlevleri de kullanabilir',\n            wafStatusHelper: 'WAF etkin değil, lütfen genel ayarlarda etkinleştirin',\n            ccMode: 'Mod',\n            global: 'Genel Mod',\n            uriMode: 'URL Modu',\n            globalHelper:\n                'Genel Mod: Birim zamanda herhangi bir URL’ye yapılan toplam istek sayısı eşiği aştığında tetiklenir',\n            uriModeHelper: 'URL Modu: Birim zamanda tek bir URL’ye yapılan istek sayısı eşiği aştığında tetiklenir',\n            ip: 'IP Kara Listesi',\n            globalSettingHelper:\n                '[Web Sitesi] etiketli ayarlar [Web Sitesi Ayarları]’nda etkinleştirilmelidir, genel ayarlar yalnızca yeni oluşturulan web siteleri için varsayılan ayarlardır',\n            globalSettingHelper2:\n                'Ayarların hem [Genel Ayarlar] hem de [Web Sitesi Ayarları]’nda aynı anda etkinleştirilmesi gerekir',\n            urlCCHelper: 'Bu URL’ye {0} saniye içinde {1} istekten fazla olursa, bu IP {2} için engellenir',\n            urlCCHelper2: 'URL parametre içeremez',\n            notContain: 'İçermez',\n            urlcc: 'URL sıklık sınırı',\n            method: 'İstek türü',\n            addIpsToBlock: 'Toplu IP engelleme',\n            addUrlsToWhite: 'Toplu URL’yi beyaz listeye ekleme',\n            noBlackIp: 'IP zaten engellenmiş, tekrar engellemeye gerek yok',\n            noWhiteUrl: 'URL zaten beyaz listede, tekrar eklemeye gerek yok',\n            spiderIpHelper:\n                \"Baidu, Bing, Google, 360, Shenma, Sogou, ByteDance, DuckDuckGo, Yandex'i içerir. Bunu kapatmak tüm örümcek erişimlerini engeller.\",\n            spiderIp: 'Örümcek IP Havuzu',\n            geoIp: 'IP Adres Kütüphanesi',\n            geoIpHelper: 'IP’nin coğrafi konumunu doğrulamak için kullanılır',\n            stat: 'Saldırı Raporu',\n            statTitle: 'Rapor',\n            attackIp: 'IP',\n            attackCountNum: 'Sayılar',\n            percent: 'Yüzde',\n            addblackUrlHelper: 'URL: {0}’ı varsayılan kara listeye eklemek mi?',\n            rce: 'Uzaktan Kod Yürütme',\n            software: 'Yazılım',\n            cveHelper: 'Yaygın yazılım ve çerçevelerdeki güvenlik açıklarını içerir',\n            vulnCheck: 'Tamamlayıcı Kurallar',\n            ssrf: 'SSRF Güvenlik Açığı',\n            afr: 'Keyfi Dosya Okuma',\n            ua: 'Yetkisiz Erişim',\n            id: 'Bilgi Sızıntısı',\n            aa: 'Kimlik Doğrulama Atlatma',\n            dr: 'Dizin Geçişi',\n            xxe: 'XXE Güvenlik Açığı',\n            suid: 'Serileştirme Güvenlik Açığı',\n            dos: 'Hizmet Engelleme Güvenlik Açığı',\n            afd: 'Keyfi Dosya İndirme',\n            sqlInjection: 'SQL Enjeksiyonu',\n            afw: 'Keyfi Dosya Yazma',\n            il: 'Bilgi Sızıntısı',\n            clearAllLog: 'Tüm günlükleri temizle',\n            exportLog: 'Günlükleri dışa aktar',\n            appRule: 'Uygulama Kuralları',\n            appRuleHelper:\n                'Yaygın uygulama kuralları, etkinleştirme yanlış pozitifleri azaltabilir, bir web sitesi yalnızca bir kural kullanabilir',\n            logExternal: 'Kayıt Türlerini Hariç Tut',\n            ipWhite: 'IP Beyaz Listesi',\n            urlWhite: 'URL Beyaz Listesi',\n            uaWhite: 'Kullanıcı-Aracısı Beyaz Listesi',\n            logExternalHelper:\n                'Hariç tutulan kayıt türleri günlüklerde kaydedilmez, kara liste/beyaz liste, bölgesel erişim kısıtlamaları ve özel kurallar çok sayıda günlük oluşturur, hariç tutulması önerilir',\n            ssti: 'SSTI Saldırısı',\n            crlf: 'CRLF Enjeksiyonu',\n            strict: 'Katı Mod',\n            strictHelper: 'İstekleri doğrulamak için daha katı kurallar kullanın',\n            saveLog: 'Günlüğü Kaydet',\n            remoteURLHelper: 'Uzak URL, her satırda bir IP içermeli ve başka karakter olmamalıdır',\n            notFound: 'Bulunamadı (404)',\n            serviceUnavailable: 'Hizmet Kullanılamıyor (503)',\n            gatewayTimeout: 'Ağ Geçidi Zaman Aşımı (504)',\n            belongToIpGroup: 'IP Grubuna Ait',\n            notBelongToIpGroup: 'IP Grubuna Ait Değil',\n            unknownWebsiteKey: 'Bilinmeyen Alan',\n            special: 'Özel',\n            fileToLarge: \"Dosya 1MB'yi aştığı için yüklenemez\",\n            uploadOverLimit: 'Yüklenen dosya sayısı sınırı aşıyor, maksimum 1 dosya',\n            importRuleHelper: 'Her satıra bir kural',\n        },\n        monitor: {\n            '360': '360 Arama',\n            name: 'Web Sitesi İzleme',\n            pv: 'Sayfa Görüntülemeleri',\n            uv: 'Benzersiz Ziyaretçiler',\n            flow: 'Trafik Akışı',\n            ip: 'IP',\n            spider: 'Örümcek',\n            visitors: 'Ziyaretçi Trendleri',\n            today: 'Bugün',\n            last7days: 'Son 7 Gün',\n            last30days: 'Son 30 Gün',\n            uvMap: 'Ziyaretçi Haritası (30’uncu)',\n            qps: 'Gerçek Zamanlı İstekler (dakikada)',\n            flowSec: 'Gerçek Zamanlı Trafik (dakikada)',\n            excludeCode: 'Durum Kodlarını Hariç Tut',\n            excludeUrl: 'URL’leri Hariç Tut',\n            excludeExt: 'Uzantıları Hariç Tut',\n            cdnHelper: 'CDN tarafından sağlanan Başlıktan gerçek IP’yi alın',\n            reqRank: 'Ziyaret Sıralaması',\n            refererDomain: 'Yönlendiren Alan Adı',\n            os: 'Sistem',\n            browser: 'Tarayıcı/İstemci',\n            device: 'Cihaz',\n            showMore: 'Daha Fazla',\n            unknown: 'Diğer',\n            pc: 'Bilgisayar',\n            mobile: 'Mobil Cihaz',\n            wechat: 'WeChat',\n            machine: 'makine',\n            tencent: 'Tencent Tarayıcı',\n            ucweb: 'UC Tarayıcı',\n            '2345explorer': '2345 tarayıcı',\n            huaweibrowser: 'Huawei Tarayıcı',\n            log: 'İstek Günlükleri',\n            statusCode: 'Durum Kodu',\n            requestTime: 'Yanıt Süresi',\n            flowRes: 'Yanıt Trafiği',\n            method: 'İstek Yöntemi',\n            statusCodeHelper: 'Yukarıdaki durum kodunu girin',\n            statusCodeError: 'Geçersiz durum kodu türü',\n            methodHelper: 'Yukarıdaki istek yöntemini girin',\n            all: 'Tümü',\n            baidu: 'Baidu',\n            google: 'Google',\n            bing: 'Bing',\n            bytes: 'Bugün manşetler',\n            sogou: 'Sogou',\n            failed: 'Hata',\n            ipCount: 'IP Sayısı',\n            spiderCount: 'Örümcek İstekleri',\n            averageReqTime: 'Ortalama Yanıt Süresi',\n            totalFlow: 'Toplam Trafik',\n            logSize: 'Günlük Dosyası Boyutu',\n            realIPType: 'Gerçek IP alma yöntemi',\n            fromHeader: 'HTTP Başlığından Al',\n            fromHeaders: 'Başlık listesinden Al',\n            header: 'HTTP Başlığı',\n            cdnConfig: 'CDN Yapılandırması',\n            xff1: 'X-Forwarded-For’dan birinci seviye Proxy',\n            xff2: 'X-Forwarded-For’dan ikinci seviye Proxy',\n            xff3: 'X-Forwarded-For’dan üçüncü seviye Proxy',\n            xffHelper:\n                'Örneğin: X-Forwarded-For: <istemci>,<proxy1>,<proxy2>,<proxy3> Üst seviye proxy, son IP’yi <proxy3> alır',\n            headersHelper: 'Yaygın CDN HTTP başlıklarından gerçek IP’yi alın, ilk mevcut değeri seçer',\n            monitorCDNHelper:\n                'Web sitesi izleme için CDN yapılandırmasını değiştirmek, WAF CDN ayarlarını da güncelleyecektir',\n            wafCDNHelper: 'WAF CDN yapılandırmasını değiştirmek, web sitesi izleme CDN ayarlarını da güncelleyecektir',\n            statusErr: 'Geçersiz durum kodu formatı',\n            shenma: 'Shenma Arama',\n            duckduckgo: 'DuckDuckGo',\n            excludeUri: 'URI’leri Hariç Tut',\n            top100Helper: 'İlk 100 veriyi göster',\n            logSaveDay: 'Günlük Saklama Süresi (gün)',\n            cros: 'Chrome OS',\n            theworld: 'TheWorld Tarayıcı',\n            edge: 'Microsoft Edge',\n            maxthon: 'Maxthon Tarayıcı',\n            monitorStatusHelper: 'İzleme etkin değil, lütfen ayarlarda etkinleştirin',\n            excludeIp: 'IP Adreslerini Hariç Tut',\n            excludeUa: 'Kullanıcı-Aracısını Hariç Tut',\n            remotePort: 'Uzak Port',\n            unknown_browser: 'Bilinmeyen',\n            unknown_os: 'Bilinmeyen',\n            unknown_device: 'Bilinmeyen',\n            logSaveSize: 'Maksimum Günlük Saklama Boyutu',\n            logSaveSizeHelper: 'Bu, tek bir web sitesi için günlük saklama boyutudur',\n            '360se': '360 Güvenlik Tarayıcı',\n            websites: 'Web Sitesi Listesi',\n            trend: 'Trend İstatistikleri',\n            reqCount: 'İstek Sayısı',\n            uriHelper: 'Uri’yi hariç tutmak için /test/* veya /*/index.php kullanabilirsiniz',\n        },\n        tamper: {\n            tamper: 'Web Sitesi Değiştirme Koruması',\n            ignoreTemplate: 'Hariç Tutma Şablonu',\n            protectTemplate: 'Koruma Şablonu',\n            ignoreTemplateHelper:\n                'Hariç tutma içeriğini girin, Enter veya boşlukla ayrılmış. (Belirli dizin ./log veya dizin adı tmp, dosyaları hariç tutmak için belirli dosya ./data/test.html girmeniz gerekir)',\n            protectTemplateHelper:\n                'Koruma içeriğini girin, Enter veya boşlukla ayrılmış. (Belirli dosya ./index.html, dosya uzantısı .html, dosya türü js, dizinleri korumak için belirli dizin ./log girmeniz gerekir)',\n            templateContent: 'Şablon İçeriği',\n            template: 'Şablon',\n            saveTemplate: 'Şablon Olarak Kaydet',\n            tamperHelper1:\n                'Tek tıklamayla dağıtım web siteleri için uygulama dizini değişiklik korumasını etkinleştirmeniz önerilir; web sitesi normal olarak kullanılamıyorsa veya yedekleme/geri yükleme başarısız olursa, lütfen önce değişiklik korumasını devre dışı bırakın;',\n            tamperHelper2:\n                'Hariç tutulmayan dizinlerdeki korumalı dosyalar için okuma/yazma, silme, izin ve sahip değiştirme işlemlerini kısıtlayacaktır',\n            tamperPath: 'Koruma Dizini',\n            tamperPathEdit: 'Yolu Değiştir',\n            log: 'Engelleme Günlüğü',\n            totalProtect: 'Toplam Koruma',\n            todayProtect: 'Bugünkü Koruma',\n            templateRule: 'Uzunluk 1-512, isim {0} ve diğer sembolleri içeremez',\n            ignore: 'Hariç Tut',\n            ignoreHelper:\n                'Hariç tutma içeriğini seçin veya girin, Enter veya boşlukla ayrılmış. (Belirli dizin ./log veya dizin adı tmp, dosyaları hariç tutmak için belirli dosya ./data/test.html girmeniz veya seçmeniz gerekir)',\n            protect: 'Koru',\n            protectHelper:\n                'Koruma içeriğini seçin veya girin, Enter veya boşlukla ayrılmış. (Belirli dosya ./index.html, dosya uzantısı .html, dosya türü js, dizinleri korumak için belirli dizin ./log girmeniz veya seçmeniz gerekir)',\n            tamperHelper00: 'Hariç tutma ve koruma yalnızca göreli yolları destekler;',\n            tamperHelper01:\n                'Değişiklik korumasını etkinleştirdikten sonra sistem, hariç tutulmayan dizinlerdeki korumalı dosyaların oluşturulmasını, düzenlenmesini ve silinmesini kısıtlayacaktır;',\n            tamperHelper02: 'Öncelik: Belirli yol koruması > Belirli yol hariç tutma > Koruma > Hariç tutma',\n            tamperHelper03:\n                'İzleme işlemleri yalnızca hariç tutulmayan dizinleri hedefler, bu dizinlerdeki korunmayan dosyaların oluşturulmasını izler.',\n            disableHelper:\n                'Aşağıdaki web siteleri için değişiklik korumasını devre dışı bırakmak üzeresiniz, devam etmek istiyor musunuz?',\n            appendOnly: 'Sadece Ekleme',\n            appendOnlyHelper:\n                'Bu dizindeki dosya silme işlemlerini kısıtlar, yalnızca hariç tutulan dizinlerin veya korunmayan dosyaların eklenmesine izin verir',\n            immutable: 'Değişmez',\n            immutableHelper: 'Bu dosya için düzenleme, silme, izin ve sahip değiştirme işlemlerini kısıtlar',\n            onWatch: 'İzleme',\n            onWatchHelper:\n                'Bu dizinde korumalı dosyaların veya hariç tutulmayan dizinlerin oluşturulmasını izler ve kesintiye uğratır',\n            forceStop: 'Zorla Kapat',\n            forceStopHelper:\n                'Bu web sitesi dizini için değişiklik önleme işlevini zorla devre dışı bırakacak. Devam etmek istiyor musunuz?',\n        },\n        setting: {\n            setting: 'Panel Ayarları',\n            title: 'Panel Açıklaması',\n            titleHelper:\n                'Kullanıcı giriş sayfasında gösterilecektir (ör. Linux sunucu operasyon ve bakım yönetim paneli, önerilen 8-15 karakter)',\n            logo: 'Logo (Metinsiz)',\n            logoHelper:\n                'Menü daraltıldığında yönetim sayfasının sol üst köşesinde gösterilecektir (önerilen görüntü boyutu: 82px*82px)',\n            logoWithText: 'Logo (Metinli)',\n            logoWithTextHelper:\n                'Menü genişletildiğinde yönetim sayfasının sol üst köşesinde gösterilecektir (önerilen görüntü boyutu: 185px*55px)',\n            favicon: 'Web Sitesi Simgesi',\n            faviconHelper: 'Web sitesi simgesi (önerilen görüntü boyutu: 16px*16px)',\n            setDefault: 'Varsayılana Geri Yükle',\n            setHelper: 'Mevcut ayarlar kaydedilecek. Devam etmek istiyor musunuz?',\n            setDefaultHelper: 'Tüm panel ayarları varsayılana geri yüklenecek. Devam etmek istiyor musunuz?',\n            logoGroup: 'Logo',\n            imageGroup: 'Görüntü',\n            loginImage: 'Giriş Sayfası Görüntüsü',\n            loginImageHelper: 'Giriş sayfasında gösterilir (önerilen görüntü boyutu: 500*416px)',\n            loginBgType: 'Giriş Sayfası Arka Plan Türü',\n            loginBgImage: 'Giriş Sayfası Arka Plan Görüntüsü',\n            loginBgImageHelper:\n                'Giriş sayfasında arka plan görüntüsü olarak gösterilir (önerilen görüntü boyutu: 1920*1080px)',\n            loginBgColor: 'Giriş Sayfası Arka Plan Rengi',\n            loginBgColorHelper: 'Giriş sayfasında arka plan rengi olarak gösterilir',\n            image: 'Görüntü',\n            bgColor: 'Arka Plan Rengi',\n            loginGroup: 'Giriş Sayfası',\n            loginBtnLinkColor: 'Buton/Bağlantı Rengi',\n            loginBtnLinkColorHelper: 'Giriş sayfasındaki buton/bağlantı rengi olarak gösterilecektir',\n        },\n        helper: {\n            wafTitle1: 'Engelleme Haritası',\n            wafContent1: 'Son 30 gün içindeki engellemelerin coğrafi dağılımını gösterir',\n            wafTitle2: 'Bölgesel Erişim Kısıtlamaları',\n            wafContent2: 'Web sitesi erişim kaynaklarını coğrafi konumlara göre kısıtlar',\n            wafTitle3: 'Özel Engelleme Sayfası',\n            wafContent3: 'Bir istek engellendiğinde gösterilecek özel bir sayfa oluşturun',\n            wafTitle4: 'Özel Kurallar (ACL)',\n            wafContent4: 'İstekleri özel kurallara göre engelleyin',\n            tamperTitle1: 'Dosya Bütünlüğü İzleme',\n            tamperContent1:\n                'Web sitesi dosyalarının bütünlüğünü izler, çekirdek dosyalar, betik dosyaları ve yapılandırma dosyaları dahil.',\n            tamperTitle2: 'Gerçek Zamanlı Tarama ve Algılama',\n            tamperContent2:\n                'Web sitesi dosya sistemini gerçek zamanlı tarayarak anormal veya değiştirilmiş dosyaları algılar.',\n            tamperTitle3: 'Güvenlik İzin Ayarları',\n            tamperContent3:\n                'Uygun izin ayarları ve erişim kontrol politikaları aracılığıyla web sitesi dosyalarına erişimi kısıtlar, potansiyel saldırı yüzeyini azaltır.',\n            tamperTitle4: 'Günlük Kaydı ve Analiz',\n            tamperContent4:\n                'Yöneticilerin sonraki denetim ve analiz için dosya erişimi ve işlem günlüklerini kaydeder, potansiyel güvenlik tehditlerini belirler.',\n            settingTitle1: 'Özel Hoş Geldiniz Mesajı',\n            settingContent1: '1Panel giriş sayfasında özel bir hoş geldiniz mesajı ayarlayın.',\n            settingTitle2: 'Özel Logo',\n            settingContent2: 'Marka adları veya başka metinler içeren logo görüntülerinin yüklenmesine izin verir.',\n            settingTitle3: 'Özel Web Sitesi Simgesi',\n            settingContent3:\n                'Varsayılan tarayıcı simgesini değiştirmek için özel simgelerin yüklenmesine izin verir, kullanıcı deneyimini iyileştirir.',\n            monitorTitle1: 'Ziyaretçi Trendi',\n            monitorContent1: 'Web sitesi ziyaretçi trendlerini istatistiksel olarak toplar ve gösterir',\n            monitorTitle2: 'Ziyaretçi Haritası',\n            monitorContent2:\n                'Web sitesine gelen ziyaretçilerin coğrafi dağılımını istatistiksel olarak toplar ve gösterir',\n            monitorTitle3: 'Erişim İstatistikleri',\n            monitorContent3:\n                'Web sitesi istek bilgileri hakkında istatistikler, örümcekler, erişim cihazları, istek durumu vb. içerir.',\n            monitorTitle4: 'Gerçek Zamanlı İzleme',\n            monitorContent4: 'Web sitesi istek bilgilerinin gerçek zamanlı izlenmesi, istek sayısı, trafik vb. içerir.',\n            alertTitle1: 'SMS Uyarıları',\n            alertContent1:\n                'Anormal sunucu kaynak kullanımı, web sitesi ve sertifika sona ermesi, yeni sürüm güncellemesi, şifre sona ermesi vb. durumlarda kullanıcılar SMS alarmı ile bilgilendirilir, zamanında işlem yapılmasını sağlar.',\n            alertTitle2: 'Uyarı Günlüğü',\n            alertContent2:\n                'Kullanıcılara geçmiş uyarı olaylarını takip etme ve analiz etme için uyarı günlüklerini görüntüleme işlevi sağlar.',\n            alertTitle3: 'Uyarı Ayarları',\n            alertContent3:\n                'Kullanıcılara özel telefon numaraları, günlük gönderim sıklığı ve günlük gönderim zamanı yapılandırmaları sağlar, kullanıcıların daha makul gönderim uyarıları ayarlamasını kolaylaştırır.',\n            nodeDashTitle1: 'Uygulama Yönetimi',\n            nodeDashContent1:\n                'Çoklu düğüm uygulamalarının birleşik yönetimi, durum izleme, hızlı başlatma/durdurma, terminal bağlantısı ve yedekleme desteği',\n            nodeDashTitle2: 'Web Sitesi Yönetimi',\n            nodeDashContent2:\n                'Çoklu düğüm web sitelerinin birleşik yönetimi, gerçek zamanlı durum izleme, toplu başlatma/durdurma ve hızlı yedekleme desteği',\n            nodeDashTitle3: 'Veritabanı Yönetimi',\n            nodeDashContent3:\n                'Çoklu düğüm veritabanlarının birleşik yönetimi, önemli durumlar tek bakışta, tek tıklamayla yedekleme desteği',\n            nodeDashTitle4: 'Zamanlanmış Görev Yönetimi',\n            nodeDashContent4:\n                'Çoklu düğüm zamanlanmış görevlerinin birleşik yönetimi, durum izleme, hızlı başlatma/durdurma ve manuel tetikleme çalıştırma desteği',\n            nodeTitle1: 'Tek Tıkla Düğüm Ekleme',\n            nodeContent1: 'Birden fazla sunucu düğümünü hızlıca entegre eder',\n            nodeTitle2: 'Toplu Yükseltme',\n            nodeContent2: 'Tüm düğümleri tek bir işlemle senkronize eder ve yükseltir',\n            nodeTitle3: 'Düğüm Durumu İzleme',\n            nodeContent3: 'Her düğümün çalışma durumunu gerçek zamanlı olarak izler',\n            nodeTitle4: 'Hızlı Uzak Bağlantı',\n            nodeContent4: 'Düğümlere tek tıkla doğrudan uzak terminal bağlantısı',\n            fileExchangeTitle1: 'Anahtar Kimlik Doğrulama İletimi',\n            fileExchangeContent1: 'İletim güvenliğini sağlamak için SSH anahtarları ile kimlik doğrulaması yapar.',\n            fileExchangeTitle2: 'Verimli Dosya Senkronizasyonu',\n            fileExchangeContent2:\n                'Yalnızca değiştirilen içeriği senkronize ederek iletim hızını ve kararlılığını önemli ölçüde artırır.',\n            fileExchangeTitle3: 'Çoklu Düğüm İletişimini Destekler',\n            fileExchangeContent3:\n                'Farklı düğümler arasında proje dosyalarını kolayca aktarır, birden fazla sunucuyu esnek bir şekilde yönetir.',\n            nodeAppTitle1: 'Uygulama Yükseltme Yönetimi',\n            nodeAppContent1:\n                'Çoklu düğüm uygulama güncellemelerinin birleşik izlenmesi, tek tıklamayla yükseltme desteği',\n            appTitle1: 'Esnek Panel Yönetimi',\n            appContent1: '1Panel sunucunuzu her zaman, her yerde kolayca yönetin.',\n            appTitle2: 'Kapsamlı Hizmet Bilgisi',\n            appContent2:\n                'Temel uygulamaları, web sitelerini, Docker’ı, veritabanlarını vb. yönetin ve mobil uygulama üzerinden hızlıca uygulamalar ve web siteleri oluşturun.',\n            appTitle3: 'Gerçek Zamanlı Anormallik İzleme',\n            appContent3:\n                'Mobil uygulama üzerinden gerçek zamanlı sunucu durumu, WAF güvenlik izleme, web sitesi trafik istatistikleri ve işlem sağlığı durumunu görüntüleyin.',\n            clusterTitle1: 'Ana-Çalışan Dağıtımı',\n            clusterContent1:\n                'Farklı düğümlerde MySQL/Postgres/Redis ana-çalışan örnekleri oluşturmayı destekler, ana-çalışan ilişkisi ve başlatmayı otomatik olarak tamamlar',\n            clusterTitle2: 'Ana-Çalışan Yönetimi',\n            clusterContent2:\n                'Birleşik bir sayfa ile birden çok ana-çalışan düğümünü merkezi olarak yönetin, rollerini, çalışma durumlarını vb. görüntüleyin',\n            clusterTitle3: 'Çoğaltma Durumu',\n            clusterContent3:\n                'Ana-çalışan çoğaltma durumunu ve gecikme bilgilerini görüntüleyerek senkronizasyon sorunlarını gidermeye yardımcı olur',\n        },\n        node: {\n            master: 'Ana Düğüm',\n            masterBackup: 'Ana Düğüm Yedekleme',\n            backupNode: 'Yedek Düğüm',\n            backupFrequency: 'Yedekleme Sıklığı (saat)',\n            backupCopies: 'Yedek Kopya',\n            noBackupNode: 'Yedek düğüm şu anda boş. Lütfen kaydetmek için bir yedek düğüm seçin ve tekrar deneyin!',\n            masterBackupAlert:\n                'Ana düğüm yedeklemesi şu anda yapılandırılmamış. Veri güvenliği için, lütfen arıza durumunda yeni bir ana düğüme manuel geçiş yapabilmek amacıyla en kısa sürede bir yedek düğüm ayarlayın.',\n            node: 'Düğüm',\n            addr: 'Adres',\n            nodeUpgrade: 'Güncelleme Ayarları',\n            nodeUpgradeHelper:\n                'Seçilen düğümler, ana düğüm yükseltmesi tamamlandıktan sonra otomatik olarak yükseltmeye başlayacaktır, manuel işlem gerekmez.',\n            nodeUnhealthy: 'Düğüm durumu anormal',\n            deletedNode: 'Silinmiş düğüm {0} şu anda yükseltme işlemlerini desteklemiyor!',\n            nodeUnhealthyHelper:\n                'Anormal düğüm durumu algılandı. Lütfen [Düğüm Yönetimi]’nde kontrol edin ve tekrar deneyin!',\n            nodeUnbind: 'Düğüm lisansa bağlı değil',\n            nodeUnbindHelper:\n                'Bu düğümün lisansa bağlı olmadığı algılandı. Lütfen [Panel Ayarları - Lisans] menüsünde bağlayın ve tekrar deneyin!',\n            memTotal: 'Toplam Bellek',\n            nodeManagement: 'Çoklu Makine Yönetimi',\n            nodeItem: 'Düğüm Yönetimi',\n            panelItem: 'Panel Yönetimi',\n            addPanel: 'Panel Ekle',\n            addPanelHelper:\n                'Panel başarıyla eklendikten sonra, [Genel Bakış - Paneller] bölümünden hedef panele hızlıca erişebilirsiniz.',\n            panel: '1Panel Paneli',\n            others: 'Diğer Paneller',\n            addNode: 'Düğüm Ekle',\n            connInfo: 'Bağlantı Bilgileri',\n            nodeInfo: 'Düğüm Bilgileri',\n            withProxy: 'Proxy Erişimini Etkinleştir',\n            withoutProxy: 'Proxy Erişimini Devre Dışı Bırak',\n            withProxyHelper:\n                'Alt düğümlere erişmek için panel ayarlarında tutulan sistem proxy {0} kullanılacak. Devam etmek istiyor musunuz?',\n            withoutProxyHelper:\n                'Alt düğümlere erişmek için panel ayarlarında tutulan sistem proxy kullanımı durdurulacak. Devam etmek istiyor musunuz?',\n            syncInfo: 'Senkronizasyon',\n            syncHelper: 'Ana düğüm verileri değiştiğinde, bu alt düğüme gerçek zamanlı olarak senkronize edilir',\n            syncBackupAccount: 'Yedekleme hesabı ayarları',\n            syncWithMaster:\n                'Pro’ya yükseltildikten sonra, tüm veriler varsayılan olarak senkronize edilir. Senkronizasyon politikaları düğüm yönetiminde manuel olarak ayarlanabilir.',\n            syncProxy: 'Sistem proxy ayarları',\n            syncProxyHelper: 'Sistem proxy ayarlarını senkronize etmek Docker yeniden başlatılmasını gerektirir',\n            syncProxyHelper1: 'Docker’ın yeniden başlatılması, şu anda çalışan kapsayıcı hizmetlerini etkileyebilir.',\n            syncProxyHelper2: 'Kapsayıcılar - Yapılandırma sayfasında manuel olarak yeniden başlatabilirsiniz.',\n            syncProxyHelper3:\n                'Sistem proxy ayarlarını senkronize etmek Docker yeniden başlatılmasını gerektirir, bu da şu anda çalışan kapsayıcı hizmetlerini etkileyebilir',\n            syncProxyHelper4:\n                'Sistem proxy ayarlarını senkronize etmek Docker yeniden başlatılmasını gerektirir. Kapsayıcılar - Yapılandırma sayfasında daha sonra manuel olarak yeniden başlatabilirsiniz.',\n            syncCustomApp: 'Özel Uygulama Deposunu Senkronize Et',\n            syncAlertSetting: 'Sistem uyarı ayarları',\n            syncNodeInfo: 'Düğüm temel verileri,',\n            nodeSyncHelper: 'Düğüm bilgisi senkronizasyonu aşağıdaki bilgileri senkronize eder:',\n            nodeSyncHelper1: '1. Genel yedekleme hesabı bilgileri',\n            nodeSyncHelper2: '2. Ana düğüm ile alt düğümler arasındaki bağlantı bilgileri',\n            nodeCheck: 'Kullanılabilirlik kontrolü',\n            checkSSH: 'Düğüm SSH bağlantısını kontrol et',\n            checkUserPermission: 'Düğüm kullanıcı izinlerini kontrol et',\n            isNotRoot: 'Bu düğümde şifresiz sudo desteklenmediği ve mevcut kullanıcının root olmadığı algılandı',\n            checkLicense: 'Düğüm lisans durumunu kontrol et',\n            checkService: 'Düğümdeki mevcut hizmet bilgilerini kontrol et',\n            checkPort: 'Düğüm port erişilebilirliğini kontrol et',\n            panelExist:\n                'Bu düğümde 1Panel V1 hizmeti çalıştığı algılandı. Lütfen eklemeden önce geçiş betiğini kullanarak V2’ye yükseltin.',\n            coreExist:\n                'Mevcut düğüm zaten ana düğüm olarak etkinleştirilmiş durumda ve doğrudan alt düğüm olarak eklenemez. Lütfen eklemeden önce alt düğüme dönüştürün, ayrıntılar için belgelere bakın.',\n            agentExist:\n                'Bu düğümde 1panel-agent’ın zaten kurulu olduğu algılandı. Devam edilmesi mevcut verileri koruyacak ve yalnızca 1panel-agent hizmetini değiştirecektir.',\n            agentNotExist:\n                'Bu düğümde 1panel-agent yüklü olmadığı tespit edildi, düğüm bilgileri doğrudan düzenlenemez. Lütfen silip tekrar ekleyin.',\n            oldDataExist:\n                'Bu düğümde geçmiş 1Panel V2 verileri algılandı. Aşağıdaki bilgiler mevcut ayarları üzerine yazmak için kullanılacaktır:',\n            errLicense: 'Bu düğüme bağlı lisans kullanılamıyor. Lütfen kontrol edin ve tekrar deneyin!',\n            errNodePort:\n                'Düğüm portu [ {0} ] erişilemez olarak algılandı. Lütfen güvenlik duvarı veya güvenlik grubunun bu portu izin verdiğinden emin olun.',\n            reinstallHelper: '{0} düğümünü yeniden kurmak istiyor musunuz?',\n            unhealthyCheck: 'Anormal Kontrol',\n            fixOperation: 'Düzeltme İşlemi',\n            checkName: 'Kontrol Öğesi',\n            checkSSHConn: 'SSH Bağlantı Kullanılabilirliğini Kontrol Et',\n            fixSSHConn: 'Bağlantı bilgilerini doğrulamak için düğümü manuel olarak düzenleyin',\n            checkConnInfo: 'Agent Bağlantı Bilgilerini Kontrol Et',\n            checkStatus: 'Düğüm Hizmeti Kullanılabilirliğini Kontrol Et',\n            fixStatus:\n                'Hizmetin çalıştığından emin olmak için \"systemctl status 1panel-agent.service\" komutunu çalıştırın.',\n            checkAPI: 'Düğüm API Kullanılabilirliğini Kontrol Et',\n            fixAPI: 'Düğüm günlüklerini kontrol edin ve güvenlik duvarı portlarının doğru şekilde açıldığını doğrulayın.',\n            forceDelete: 'Zorla Sil',\n            operateHelper: 'Aşağıdaki düğümler {0} işlemini geçirecek, devam etmek istiyor musunuz?',\n            operatePanelHelper: 'Aşağıdaki panellerde {0} işlemi gerçekleştirilecek. Devam etmek istiyor musunuz?',\n            forceDeleteHelper: 'Zorla silme, düğüm silme hatalarını yok sayar ve veritabanı meta verilerini siler',\n            uninstall: 'Düğüm verilerini sil',\n            uninstallHelper: 'Bu, düğümle ilgili tüm 1Panel verilerini silecektir. Dikkatli ilerleyin!',\n            baseDir: 'Kurulum Dizini',\n            baseDirHelper: 'Kurulum dizini boş olduğunda, varsayılan olarak /opt dizinine kurulur',\n            nodePort: 'Düğüm Portu',\n            offline: 'Çevrimdışı mod',\n            freeCount: 'Ücretsiz kota [{0}]',\n            offlineHelper: 'Düğüm çevrimdışı bir ortamda olduğunda kullanılır',\n            appUpgrade: 'Uygulama Yükseltme',\n            appUpgradeHelper: 'Yükseltilmesi gereken {0} uygulama var',\n        },\n        customApp: {\n            name: 'Özel Uygulama Deposu',\n            appStoreType: 'Uygulama Mağazası Paket Kaynağı',\n            appStoreUrl: 'Depo URL’si',\n            local: 'Yerel Yol',\n            remote: 'Uzak Bağlantı',\n            imagePrefix: 'Görüntü Öneki',\n            imagePrefixHelper:\n                'İşlev: Görüntü önekini özelleştirir ve compose dosyasındaki görüntü alanını değiştirir. Örneğin, görüntü öneki 1panel/custom olarak ayarlandığında, MaxKB için görüntü alanı 1panel/custom/maxkb:v1.10.0 olarak değişir',\n            closeHelper: 'Özel uygulama deposunu kullanmayı iptal et',\n            appStoreUrlHelper: 'Yalnızca .tar.gz formatı desteklenir',\n            postNode: 'Alt düğüme senkronize et',\n            postNodeHelper:\n                'Özel mağaza paketini alt düğümün kurulum dizinindeki tmp/customApp/apps.tar.gz’e senkronize eder',\n            nodes: 'Düğümler',\n            selectNode: 'Düğüm Seç',\n            selectNodeError: 'Lütfen bir düğüm seçin',\n            licenseHelper: 'Pro sürümü, özel uygulama deposu özelliğini destekler',\n            databaseHelper: 'Uygulama ilişkili veritabanı, lütfen hedef düğüm veritabanını seçin',\n            nodeHelper: 'Geçerli düğüm seçilemez',\n            migrateHelper:\n                'Şu anda yalnızca tek parça uygulamaların ve yalnızca MySQL, MariaDB, PostgreSQL veritabanlarıyla ilişkili uygulamaların taşınmasını destekler',\n            opensslHelper:\n                'Şifreli yedekleme kullanılıyorsa, iki düğüm arasındaki OpenSSL sürümleri tutarlı olmalıdır, aksi takdirde geçiş başarısız olabilir.',\n            installApp: 'Toplu kurulum',\n            installAppHelper: 'Seçilen düğümlere uygulamaları toplu kur',\n        },\n        alert: {\n            isAlert: 'Uyarı',\n            alertCount: 'Uyarı Sayısı',\n            clamHelper: 'Enfekte dosyalar tarandığında uyarısını tetikle',\n            cronJobHelper: 'Görev yürütme başarısız olduğunda uyarısını tetikle',\n            licenseHelper: 'Profesyonel sürüm more uyarısını destekler',\n            alertCountHelper: 'Günlük maksimum uyarı sıklığı',\n            alert: 'SMS Uyarısı',\n            logs: 'Uyarı Günlükleri',\n            list: 'Uyarı Listesi',\n            addTask: 'Uyarı Oluştur',\n            editTask: 'Uyarıyı Düzenle',\n            alertMethod: 'Yöntem',\n            alertMsg: 'Uyarı Mesajı',\n            alertRule: 'Uyarı Kuralları',\n            titleSearchHelper: 'Bulanık arama için uyarı başlığını girin',\n            taskType: 'Tür',\n            ssl: 'Sertifika Sona Ermesi',\n            siteEndTime: 'Web Sitesi Sona Ermesi',\n            panelPwdEndTime: 'Panel Şifresi Sona Ermesi',\n            panelUpdate: 'Yeni Panel Sürümü Mevcut',\n            cpu: 'Sunucu CPU Uyarısı',\n            memory: 'Sunucu Bellek Uyarısı',\n            load: 'Sunucu Yük Uyarısı',\n            disk: 'Sunucu Disk Uyarısı',\n            website: 'Web Sitesi',\n            certificate: 'SSL Sertifikası',\n            remainingDays: 'Kalan Günler',\n            sendCount: 'Gönderim Sayısı',\n            sms: 'SMS',\n            wechat: 'WeChat',\n            dingTalk: 'DingTalk',\n            feiShu: 'FeiShu',\n            mail: 'E-posta',\n            weCom: 'WeCom',\n            sendCountRulesHelper: 'Sona ermeden önce gönderilen toplam uyarılar (günde bir kez)',\n            panelUpdateRulesHelper:\n                'Yeni panel sürümü algılandığında bir kez uyarı gönder (işlenmezse ertesi gün tekrar gönderilir)',\n            oneDaySendCountRulesHelper: 'Günde gönderilen maksimum uyarılar',\n            siteEndTimeRulesHelper: 'Asla sona ermeyen web siteleri uyarı tetiklemez',\n            autoRenewRulesHelper:\n                'Otomatik yenileme etkin olan ve kalan gün sayısı 31’den az olan sertifikalar uyarı tetiklemez',\n            panelPwdEndTimeRulesHelper: 'Panel şifresi sona erme uyarıları, sona erme ayarlanmadıysa kullanılamaz',\n            sslRulesHelper: 'Tüm SSL Sertifikaları',\n            diskInfo: 'Disk',\n            monitoringType: 'İzleme Türü',\n            autoRenew: 'Otomatik Yenile',\n            useDisk: 'Disk Kullanımı',\n            usePercentage: 'Kullanım Yüzdesi',\n            changeStatus: 'Durumu Değiştir',\n            disableMsg:\n                'Uyarı görevini durdurmak, bu görevin uyarı mesajları göndermesini engeller. Devam etmek istiyor musunuz?',\n            enableMsg:\n                'Uyarı görevini etkinleştirmek, bu görevin uyarı mesajları göndermesini sağlar. Devam etmek istiyor musunuz?',\n            useExceed: 'Kullanım Aşımı',\n            useExceedRulesHelper: 'Kullanım ayarlanan değeri aştığında uyarı tetiklenir',\n            cpuUseExceedAvg: 'Ortalama CPU kullanımı belirtilen değeri aşar',\n            memoryUseExceedAvg: 'Ortalama bellek kullanımı belirtilen değeri aşar',\n            loadUseExceedAvg: 'Ortalama yük kullanımı belirtilen değeri aşar',\n            cpuUseExceedAvgHelper: 'Belirtilen süre içinde ortalama CPU kullanımı belirtilen değeri aşar',\n            memoryUseExceedAvgHelper: 'Belirtilen süre içinde ortalama bellek kullanımı belirtilen değeri aşar',\n            loadUseExceedAvgHelper: 'Belirtilen süre içinde ortalama yük kullanımı belirtilen değeri aşar',\n            resourceAlertRulesHelper: 'Not: 30 dakika içinde sürekli uyarılar yalnızca bir gönderir',\n            specifiedTime: 'Belirtilen Süre',\n            deleteTitle: 'Uyarıyı Sil',\n            deleteMsg: 'Uyarı görevini silmek istediğinizden emin misiniz?',\n            allSslTitle: 'Tüm Web Sitesi SSL Sertifikası Sona Erme Uyarıları',\n            sslTitle: 'Web Sitesi {0} için SSL Sertifikası Sona Erme Uyarısı',\n            allSiteEndTimeTitle: 'Tüm Web Sitesi Sona Erme Uyarıları',\n            siteEndTimeTitle: 'Web Sitesi {0} Sona Erme Uyarısı',\n            panelPwdEndTimeTitle: 'Panel Şifresi Sona Erme Uyarısı',\n            panelUpdateTitle: 'Yeni Panel Sürümü Bildirimi',\n            cpuTitle: 'Yüksek CPU Kullanımı Uyarısı',\n            memoryTitle: 'Yüksek Bellek Kullanımı Uyarısı',\n            loadTitle: 'Yüksek Yük Uyarısı',\n            diskTitle: 'Bağlama Dizini {0} için Yüksek Disk Kullanımı Uyarısı',\n            allDiskTitle: 'Yüksek Disk Kullanımı Uyarısı',\n            timeRule: 'Kalan süre {0} günden az olduğunda (işlenmezse ertesi gün tekrar gönderilir)',\n            panelUpdateRule:\n                'Yeni bir panel sürümü algılandığında bir kez uyarı gönder (işlenmezse ertesi gün tekrar gönderilir)',\n            avgRule:\n                '{0} dakika içinde ortalama {1} kullanımı {2}%’yi aşarsa, uyarı tetiklenir, günde {3} kez gönderilir',\n            diskRule:\n                '{0} bağlama dizini için disk kullanımı {1}{2}’yi aşarsa, uyarı tetiklenir, günde {3} kez gönderilir',\n            allDiskRule: 'Disk kullanımı {0}{1}’yi aşarsa, uyarı tetiklenir, günde {2} kez gönderilir',\n            cpuName: ' CPU ',\n            memoryName: 'Bellek',\n            loadName: 'Yük',\n            diskName: 'Disk',\n            syncAlertInfo: 'Manuel Gönderim',\n            syncAlertInfoMsg: 'Uyarı görevini manuel olarak göndermek istiyor musunuz?',\n            pushError: 'Gönderim Başarısız',\n            pushSuccess: 'Gönderim Başarılı',\n            syncError: 'Senkronizasyon Başarısız',\n            success: 'Uyarı Başarılı',\n            pushing: 'Gönderiliyor...',\n            error: 'Uyarı Başarısız',\n            cleanLog: 'Zamanlanmış görev yürütmesi {0} hatası',\n            cleanAlertLogs: 'Uyarı Günlüklerini Temizle',\n            daily: 'Günlük Uyarı Sayısı: {0}',\n            cumulative: 'Toplam Uyarı Sayısı: {0}',\n            clams: 'Virüs tarama',\n            taskName: 'Görev Adı',\n            cronJobType: 'Görev Türü',\n            clamPath: 'Tarama Dizini',\n            cronjob: 'Zamanlanmış Görev',\n            app: 'Yedekleme Uygulaması',\n            web: 'Yedekleme Web Sitesi',\n            database: 'Yedekleme Veritabanı',\n            directory: 'Yedekleme Dizini',\n            log: 'Yedekleme Günlükleri',\n            snapshot: 'Sistem Anlık Görüntüsü',\n            clamsRulesHelper: 'Uyarısı gerektiren virüs tarama görevleri',\n            cronJobRulesHelper: 'Bu tür zamanlanmış görevlerin yapılandırılması gerekir',\n            clamsTitle: 'Virüs tarama görevi 「 {0} 」 enfekte dosya algıladı uyarısı',\n            cronJobAppTitle: 'Zamanlanmış Görev - Yedekleme Uygulaması 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobWebsiteTitle: 'Zamanlanmış Görev - Yedekleme Web Sitesi 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobDatabaseTitle: 'Zamanlanmış Görev - Yedekleme Veritabanı 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobDirectoryTitle: 'Zamanlanmış Görev - Yedekleme Dizini 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobLogTitle: 'Zamanlanmış Görev - Yedekleme Günlükleri 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobSnapshotTitle: 'Zamanlanmış Görev - Yedekleme Anlık Görüntüsü 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobShellTitle: 'Zamanlanmış Görev - Kabuk betiği 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobCurlTitle: 'Zamanlanmış Görev - URL erişimi 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobCutWebsiteLogTitle:\n                'Zamanlanmış Görev - Web sitesi günlüğünü kes 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobCleanTitle: 'Zamanlanmış Görev - Önbellek temizleme 「 {0} 」 Görev Başarısızlık Uyarısı',\n            cronJobNtpTitle: 'Zamanlanmış Görev - Sunucu zamanını senkronize etme 「 {0} 」 Görev Başarısızlık Uyarısı',\n            clamsRule: 'Virüs tarama enfekte dosya algıladı uyarısı, günde {0} kez gönderilir',\n            cronJobAppRule: 'Yedekleme uygulaması görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobWebsiteRule: 'Yedekleme web sitesi görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobDatabaseRule: 'Yedekleme veritabanı görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobDirectoryRule: 'Yedekleme dizini görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobLogRule: 'Yedekleme günlükleri görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobSnapshotRule: 'Yedekleme anlık görüntüsü görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobShellRule: 'Kabuk betiği görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobCurlRule: 'URL erişimi görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobCutWebsiteLogRule: 'Web sitesi günlüğünü kesme görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobCleanRule: 'Önbellek temizleme görevi başarısız uyarısı, günde {0} kez gönderilir',\n            cronJobNtpRule: 'Sunucu zamanını senkronize etme görevi başarısız uyarısı, günde {0} kez gönderilir',\n            alertSmsHelper: 'SMS sınırı: toplam {0} mesaj, {1} zaten kullanıldı',\n            goBuy: 'Daha Fazla Satın Al',\n            phone: 'Telefon',\n            phoneHelper: 'Uyarı mesajları için gerçek telefon numarası sağlayın',\n            dailyAlertNum: 'Günlük Uyarı Sınırı',\n            dailyAlertNumHelper: 'Günde maksimum uyarı sayısı (100’e kadar)',\n            timeRange: 'Zaman Aralığı',\n            sendTimeRange: 'gönderim zaman aralığı',\n            sendTimeRangeHelper: '{0} zaman aralığında gönderim yapılabilir',\n            to: 'kadar',\n            startTime: 'Başlangıç Zamanı',\n            endTime: 'Bitiş Zamanı',\n            defaultPhone: 'Varsayılan olarak lisansa bağlı hesap telefon numarasına',\n            noticeAlert: 'Bildirim Uyarısı',\n            resourceAlert: 'Kaynak Uyarısı',\n            agentOfflineAlertHelper:\n                'Düğüm için çevrimdışı uyarı etkinleştirildiğinde, ana düğüm her 30 dakikada bir tarama yaparak uyarı görevlerini yürütür.',\n            offline: 'Çevrimdışı Uyarı',\n            offlineHelper:\n                'Çevrimdışı uyarı olarak ayarlandığında, ana düğüm her 30 dakikada bir tarama yaparak uyarı görevlerini yürütür.',\n            offlineOff: 'Çevrimdışı Uyarısını Etkinleştir',\n            offlineOffHelper:\n                'Çevrimdışı uyarının etkinleştirilmesi, ana düğümün her 30 dakikada bir tarama yaparak uyarı görevlerini yürütmesini sağlar.',\n            offlineClose: 'Çevrimdışı Uyarısını Devre Dışı Bırak',\n            offlineCloseHelper:\n                'Çevrimdışı uyarının devre dışı bırakılması, alt düğümlerin uyarıları bağımsız olarak işlemesini gerektirir. Uyarı başarısızlığını önlemek için ağ bağlantısını sağlayın.',\n            alertNotice: 'Uyarı Bildirimi',\n            methodConfig: 'Bildirim Yöntemi Ayarı',\n            commonConfig: 'Genel Ayar',\n            smsConfig: 'SMS',\n            smsConfigHelper: 'SMS bildirimi için telefon numaralarını yapılandırın',\n            emailConfig: 'E-posta',\n            emailConfigHelper: 'SMTP e-posta gönderme hizmetini yapılandırın',\n            deleteConfigTitle: 'Uyarı Yapılandırmasını Sil',\n            deleteConfigMsg: 'Uyarı yapılandırmasını silmek istediğinizden emin misiniz?',\n            test: 'Test',\n            alertTestOk: 'Test bildirimi başarılı',\n            alertTestFailed: 'Test bildirimi başarısız',\n            displayName: 'Görünen Ad',\n            sender: 'Gönderen',\n            password: 'Şifre',\n            host: 'SMTP',\n            port: 'Port',\n            encryption: 'Şifreleme',\n            recipient: 'Alıcı',\n            licenseTime: 'Lisans Süresi Hatırlatması',\n            licenseTimeTitle: 'Lisans Süresi Hatırlatması',\n            displayNameHelper: 'E-posta göndericisinin görünen adı',\n            senderHelper: 'E-posta göndermek için kullanılan adres',\n            passwordHelper: 'E-posta servisinin yetkilendirme kodu',\n            hostHelper: 'SMTP sunucu adresi, örneğin: smtp.qq.com',\n            portHelper: 'SSL genellikle 465, TLS genellikle 587',\n            sslHelper: 'SMTP portu 465 ise genellikle SSL gerekir',\n            tlsHelper: 'SMTP portu 587 ise genellikle TLS gerekir',\n            triggerCondition: 'Tetikleme Koşulu',\n            loginFail: ' içinde oturum açma başarısızlığı',\n            nodeException: 'Düğüm Hatası Uyarısı',\n            licenseException: 'Lisans Hatası Uyarısı',\n            panelLogin: 'Panel Girişi Hatası Uyarısı',\n            sshLogin: 'SSH Girişi Hatası Uyarısı',\n            panelIpLogin: 'Panel Girişi IP Hatası Uyarısı',\n            sshIpLogin: 'SSH Girişi IP Hatası Uyarısı',\n            ipWhiteListHelper:\n                'Beyaz listedeki IP’ler kurallara tabi değildir ve başarılı girişlerde uyarı oluşturulmaz',\n            nodeExceptionRule: 'Düğüm hatası uyarısı, günde {0} kez gönderilir',\n            licenseExceptionRule: 'Lisans hatası uyarısı, günde {0} kez gönderilir',\n            panelLoginRule: 'Panel girişi uyarısı, günde {0} kez gönderilir',\n            sshLoginRule: 'SSH girişi uyarısı, günde {0} kez gönderilir',\n            userNameHelper: 'Kullanıcı adı boşsa, varsayılan olarak gönderici adresi kullanılacaktır',\n            alertConfigHelper: 'Panel mesaj gönderimini almak için uyarı bildirim kanallarını yapılandırın',\n            weComConfigHelper: 'WeCom uyarı bildirim yapılandırması',\n            wechatConfigHelper: 'WeChat Resmi Hesap uyarı bildirim yapılandırması',\n            dingTalkConfigHelper: 'DingTalk uyarı bildirim yapılandırması',\n            feiShuConfigHelper: 'Feishu uyarı bildirim yapılandırması',\n            webhookName: 'Bot adı',\n            webhookUrl: 'Webhook URL',\n            alertConfigProHelper:\n                'Profesyonel sürüm, WeCom, DingTalk, Feishu ve SMS bildirimlerini ek olarak destekler.',\n        },\n        theme: {\n            lingXiaGold: 'Ling Xia Altın',\n            classicBlue: 'Klasik Mavi',\n            freshGreen: 'Taze Yeşil',\n            customColor: 'Özel Renk',\n            setDefault: 'Varsayılan',\n            setDefaultHelper: 'Tema renk şeması başlangıç durumuna geri yüklenecek. Devam etmek istiyor musunuz?',\n            setHelper: 'Seçilen tema renk şeması kaydedilecek. Devam etmek istiyor musunuz?',\n        },\n        exchange: {\n            exchange: 'Dosya Değişimi',\n            exchangeConfirm: '{0} düğümünden {1} dosya/klasörünü {2} düğümünün {3} dizinine aktarmak istiyor musunuz?',\n        },\n        cluster: {\n            cluster: 'Высокая доступность приложений',\n            name: 'Имя кластера',\n            addCluster: 'Добавить кластер',\n            installNode: 'Установить узел',\n            master: 'Главный узел',\n            slave: 'Подчиненный узел',\n            replicaStatus: 'Ana-Çalışan Durumu',\n            unhealthyDeleteError:\n                'Yükleme düğümü durumu anormal, lütfen düğüm listesini kontrol edin ve tekrar deneyin!',\n            replicaStatusError: 'Durum alımı anormal, lütfen ana düğümü kontrol edin.',\n            masterHostError: \"Ana düğüm IP'si 127.0.0.1 olamaz\",\n        },\n    },\n};\n\nexport default {\n    ...fit2cloudEnLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/zh-Hant.ts",
    "content": "import fit2cloudTwLocale from 'fit2cloud-ui-plus/src/locale/lang/zh-tw';\n\nconst message = {\n    commons: {\n        true: '是',\n        false: '否',\n        example: '例：',\n        fit2cloud: '飛致雲',\n        lingxia: '凌霞',\n        colon: '：',\n        button: {\n            run: '執行',\n            prev: '上一步',\n            next: '下一步',\n            create: '建立',\n            add: '新增',\n            save: '儲存',\n            set: '設定',\n            sync: '同步',\n            delete: '刪除',\n            edit: '編輯',\n            enable: '啟用',\n            disable: '停用',\n            confirm: '確認',\n            cancel: '取消',\n            reset: '重設',\n            setDefault: '復原預設',\n            restart: '重新啟動',\n            conn: '連接',\n            disConn: '斷開',\n            clean: '清空',\n            login: '登入',\n            close: '關閉',\n            stop: '關閉',\n            start: '開啟',\n            view: '詳情',\n            watch: '追蹤',\n            handle: '執行',\n            clone: '複製',\n            expand: '展開',\n            collapse: '收起',\n            log: '日誌',\n            back: '返回',\n            backup: '備份',\n            recover: '復原',\n            retry: '重試',\n            upload: '上傳',\n            download: '下載',\n            init: '初始化',\n            verify: '驗證',\n            saveAndEnable: '儲存並啟用',\n            import: '匯入',\n            export: '匯出',\n            power: '授權',\n            search: '搜尋',\n            refresh: '重新整理',\n            get: '取得',\n            upgrade: '升級',\n            update: '更新',\n            updateNow: '立即更新',\n            ignore: '忽略升級',\n            copy: '複製',\n            random: '隨機密碼',\n            install: '安裝',\n            uninstall: '移除',\n            fullscreen: '網頁全螢幕',\n            quitFullscreen: '退出全螢幕',\n            showAll: '顯示所有',\n            hideSome: '隱藏部分',\n            agree: '同意',\n            notAgree: '不同意',\n            preview: '預覽',\n            open: '開啟',\n            notSave: '不儲存',\n            createNewFolder: '建立資料夾',\n            createNewFile: '建立檔案',\n            helpDoc: '說明文件',\n            bind: '綁定',\n            unbind: '解綁',\n            cover: '覆蓋',\n            skip: '跳過',\n            fix: '修復',\n            down: '停止',\n            up: '啟動',\n            sure: '確定',\n            show: '顯示',\n            hide: '隱藏',\n            visit: '存取',\n            migrate: '遷移',\n        },\n        operate: {\n            start: '啟動',\n            stop: '停止',\n            restart: '重新啟動',\n            reload: '重新載入',\n            rebuild: '重建',\n            sync: '同步',\n            up: '啟動',\n            down: '停止',\n            delete: '刪除',\n        },\n        search: {\n            timeStart: '開始時間',\n            timeEnd: '結束時間',\n            timeRange: '至',\n            dateStart: '開始日期',\n            dateEnd: '結束日期',\n            date: '日期',\n        },\n        table: {\n            all: '所有',\n            total: '共 {0} 條',\n            name: '名稱',\n            type: '類型',\n            status: '狀態',\n            records: '任務輸出',\n            group: '分組',\n            default: '預設',\n            createdAt: '建立時間',\n            publishedAt: '發布時間',\n            date: '時間',\n            updatedAt: '更新時間',\n            operate: '操作',\n            message: '資訊',\n            description: '描述',\n            interval: '耗時',\n            user: '使用者',\n            title: '標題',\n            port: '埠',\n            forward: '轉發',\n            protocol: '協議',\n            tableSetting: '列表設定',\n            refreshRate: '重新整理頻率',\n            noRefresh: '不重新整理',\n            selectColumn: '選擇列',\n            local: '本機',\n            serialNumber: '序號',\n            manageGroup: '管理群組',\n            backToList: '返回列表',\n            keepEdit: '繼續編輯',\n        },\n        loadingText: {\n            Upgrading: '系統升級中，請稍候',\n            Restarting: '系統重新啟動中，請稍候',\n            Recovering: '快照復原中，請稍候',\n            Rollbacking: '快照回滾中，請稍候',\n        },\n        msg: {\n            noneData: '暫無資料',\n            delete: '刪除 操作不可回滾，是否繼續？',\n            clean: '清空 操作不可回滾，是否繼續？',\n            closeDrawerHelper: '系統可能不會儲存您所做的變更，是否繼續？',\n            deleteSuccess: '刪除成功',\n            loginSuccess: '登入成功',\n            operationSuccess: '操作成功',\n            copySuccess: '複製成功',\n            notSupportOperation: '不支援此操作',\n            requestTimeout: '請求逾時，請稍後重試',\n            infoTitle: '提示',\n            notRecords: '當前任務無執行記錄',\n            sureLogOut: '確認登出？',\n            createSuccess: '建立成功',\n            updateSuccess: '更新成功',\n            uploadSuccess: '上傳成功',\n            operateConfirm: '確認操作請輸入 ',\n            inputOrSelect: '請選擇或輸入',\n            copyFailed: '複製失敗',\n            operatorHelper: '將對以下{0}進行{1}操作，是否繼續？',\n            backupSuccess: '備份成功',\n            restoreSuccess: '復原成功',\n            notFound: '抱歉，頁面不存在',\n            unSupportType: '不支援目前檔案類型',\n            unSupportSize: '上傳檔案超過 {0}M，請確認',\n            fileExist: '資料夾已有同名檔案，不支援重複上傳',\n            fileNameErr: '僅支援英文/中文/數字/.-_，長度 1-256 的檔名',\n            confirmNoNull: '請確認 {0} 值不為空',\n            errPort: '錯誤的埠資訊，請確認',\n            remove: '移出',\n            backupHelper: '目前操作將對 {0} 進行備份，是否繼續？',\n            recoverHelper: '將從 {0} 檔案復原，此操作不可回滾，是否繼續？',\n            refreshSuccess: '重新整理成功',\n            rootInfoErr: '已經是根目錄了',\n            resetSuccess: '重設成功',\n            creatingInfo: '正在建立，無需此操作',\n            installSuccess: '安裝成功',\n            uninstallSuccess: '移除成功',\n            offlineTips: '離線版本不支援此操作',\n            errImportFormat: '匯入資料或格式異常，請檢查後重試',\n            importHelper: '匯入衝突或重複資料時，將以匯入內容為標準，更新原資料庫資料。',\n            errImport: '檔案內容異常：',\n        },\n        login: {\n            username: '使用者名稱',\n            password: '密碼',\n            passkey: '通行金鑰登入',\n            welcome: '歡迎回來，請輸入帳號密碼',\n            errorAuthInfo: '您輸入的使用者名稱或密碼不正確，請重新輸入',\n            errorMfaInfo: '錯誤的驗證資訊，請重試',\n            captchaHelper: '驗證碼',\n            errorCaptcha: '驗證碼錯誤',\n            notSafe: '暫無權限存取',\n            safeEntrance1: '當前環境已啟用安全入口',\n            safeEntrance2: '在 SSH 終端輸入以下指令來檢視面板入口: 1pctl user-info',\n            errIP1: '當前環境已啟用授權 IP',\n            errDomain1: '當前環境已啟用網域綁定',\n            errHelper: '可在 SSH 終端輸入以下指令來重設綁定資訊: ',\n            codeInput: '請輸入 MFA 驗證器的 6 位驗證碼',\n            mfaTitle: 'MFA 認證',\n            mfaCode: 'MFA 驗證碼',\n            title: 'Linux 伺服器維運面板',\n            licenseHelper: '《飛致雲社區軟體許可協議》',\n            errorAgree: '請點選同意社區軟體許可協議',\n            agreeTitle: '服務協議及隱私保護',\n            agreeContent:\n                '為了更好的保障您的合法權益，請您閱讀並同意以下協議 &laquo; <a href=\"https://www.fit2cloud.com/legal/licenses.html\" target=\"_blank\"> 飛致雲社區軟體許可協議 </a> &raquo;',\n            logout: '登出',\n            passkeyFailed: '通行金鑰登入失敗，請重試',\n            passkeyNotSupported: '目前瀏覽器或環境不支援通行金鑰',\n            passkeyToPassword: '無法使用通行金鑰？改用帳密登入',\n        },\n        rule: {\n            username: '請輸入使用者名稱',\n            password: '請輸入密碼',\n            rePassword: '密碼不一致，請檢查後重新輸入',\n            requiredInput: '請填寫必填項',\n            requiredSelect: '請選擇必選項',\n            illegalChar: '暫不支援注入字元 & ; $ \\' ` ( ) \" > < |',\n            illegalInput: '輸入框中存在不合法字元',\n            commonName: '支援非特殊字元開頭,英文、中文、數字、.-和_,長度1-128',\n            userName: '支援非特殊字元開頭、英文、中文、數字和_,長度3-30',\n            simpleName: '支援非底線開頭，英文、數字、_,長度3-30',\n            simplePassword: '支援非底線開頭，英文、數字、_,長度1-30',\n            dbName: '支援非特殊字元開頭，英文、中文、數字、.-_，長度1-64',\n            composeName: '支援非特殊字元開頭，小寫英文、數字、-和_，長度1-256',\n            imageName: '支援非特殊字元開頭、英文、數字、:@/.-_,長度1-256',\n            volumeName: '支援英文、數字、.-和_,長度2-30',\n            supervisorName: '支援非特殊字元開頭,英文、數字、-和_,長度1-128',\n            complexityPassword: '請輸入長度為 8-30 位，並包含字母、數字、至少兩種特殊字元的密碼組合',\n            commonPassword: '請輸入 6 位以上長度密碼',\n            linuxName: '長度1-128，名稱不能含有{0}等符號',\n            email: '請輸入正確的信箱',\n            number: '請輸入正確的數字',\n            integer: '請輸入正確的正整數',\n            ip: '請輸入正確的 IP 位址',\n            host: '請輸入正確的 IP 或域名',\n            hostHelper: '支援輸入 ip 或域名',\n            port: '請輸入正確的埠,1-65535',\n            domain: '網域格式錯誤',\n            databaseName: '支援英文、數字、_,長度1-30',\n            numberRange: '數字範圍: {0} - {1}',\n            paramName: '支援英文、數字、.-和_,長度2-64',\n            paramComplexity: '支援英文、數字、{0},長度6-128，特殊字元不能在首尾',\n            paramUrlAndPort: '格式為 http(s)://(域名/ip):(埠)',\n            nginxDoc: '僅支援英文大小寫，數字，和.',\n            appName: '支援英文、數字、-和_,長度2-30,並且不能以-_開頭和結尾',\n            containerName: '支援字母、數字、_-和.,不能以-_或.開頭,長度2-128',\n            mirror: '支援以 http(s):// 開頭，英文大小寫，數字，. / 和 - 的映像加速地址，且不能有空行',\n            disableFunction: '僅支援字母、下劃線和,',\n            leechExts: '僅支援字母數字和,',\n            paramSimple: '支援小寫字母和數字,長度 1-128',\n            filePermission: '權限錯誤',\n            formatErr: '格式錯誤，檢查後重試',\n            phpExtension: '僅支援 , _ 小寫英文和數字',\n            paramHttp: '必須以 http:// 或 https:// 開頭',\n            phone: '手機號碼格式不正確',\n            authBasicPassword: '支援字母、數字以及常見特殊字元，長度1-72',\n            length128Err: '長度不能超過128位',\n            maxLength: '長度不能超過 {0} 位',\n            alias: '支援英文、數字、-和_,長度1-128,並且不能以-、_開頭或結尾',\n        },\n        res: {\n            paramError: '請求失敗,請稍後重試!',\n            forbidden: '目前使用者無權限',\n            serverError: '服務異常',\n            notFound: '資源不存在',\n            commonError: '請求失敗',\n        },\n        service: {\n            serviceNotStarted: '尚未啟動 {0} 服務',\n        },\n        status: {\n            running: '已啟動',\n            done: '已完成',\n            scanFailed: '未完成',\n            success: '成功',\n            waiting: '請等待',\n            waitForUpgrade: '等待升級',\n            failed: '失敗',\n            stopped: '已停止',\n            error: '失敗',\n            created: '已建立',\n            restarting: '重新啟動中',\n            uploading: '上傳中',\n            unhealthy: '異常',\n            removing: '移除中',\n            paused: '已暫停',\n            exited: '已停止',\n            dead: '已結束',\n            installing: '安裝中',\n            enabled: '已啟用',\n            disabled: '已停止',\n            normal: '正常',\n            building: '製作映像中',\n            upgrading: '升級中',\n            pending: '待編輯',\n            rebuilding: '重建中',\n            deny: '已封鎖',\n            accept: '已放行',\n            used: '已使用',\n            unused: '未使用',\n            starting: '啟動中',\n            recreating: '重建中',\n            creating: '建立中',\n            init: '等待申請',\n            ready: '正常',\n            applying: '申請中',\n            uninstalling: '移除中',\n            lost: '已失聯',\n            bound: '已綁定',\n            unbind: '未綁定',\n            exceptional: '異常',\n            free: '空閒',\n            enable: '已啟用',\n            disable: '已停止',\n            deleted: '已刪除',\n            downloading: '下載中',\n            packing: '打包中',\n            sending: '下發中',\n            healthy: '正常',\n            executing: '執行中',\n            installerr: '安裝失敗',\n            applyerror: '申請失敗',\n            systemrestart: '中斷',\n            starterr: '啟動失敗',\n            uperr: '啟動失敗',\n            new: '新',\n            conflict: '衝突',\n            duplicate: '重複',\n            unexecuted: '未執行',\n        },\n        units: {\n            second: '秒',\n            minute: '分鐘',\n            hour: '小時',\n            day: '天',\n            week: '周',\n            month: '月',\n            year: '年',\n            time: '次',\n            core: '核',\n            secondUnit: '秒',\n            minuteUnit: '分鐘',\n            hourUnit: '小時',\n            dayUnit: '天',\n            millisecond: '毫秒',\n            semicolon: '；',\n        },\n        log: {\n            noLog: '暫無日誌',\n        },\n    },\n    menu: {\n        home: '概覽',\n        apps: '應用商店',\n        website: '網站',\n        project: '項目',\n        config: '設定',\n        ssh: 'SSH 設定',\n        firewall: '防火牆',\n        ssl: '憑證',\n        database: '資料庫',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: '容器',\n        cronjob: '計劃任務',\n        system: '系統',\n        files: '檔案',\n        monitor: '監控',\n        terminal: '終端',\n        settings: '面板設定',\n        toolbox: '工具箱',\n        logs: '日誌審計',\n        runtime: '執行環境',\n        processManage: '行程管理',\n        process: '行程',\n        network: '網路',\n        supervisor: '行程守護',\n        tamper: '防篡改',\n        app: '應用',\n        msgCenter: '任務中心',\n        disk: '磁碟管理',\n        filter: '篩選器',\n    },\n    home: {\n        recommend: '推薦',\n        dir: '目錄',\n        alias: '別名',\n        quickDir: '快捷目錄',\n        minQuickJump: '請至少設定一個快速跳轉入口',\n        maxQuickJump: '最多可設定四個快速跳轉入口',\n        database: '資料庫 - 全部',\n        restart_1panel: '重新啟動面板',\n        restart_system: '重新啟動伺服器',\n        operationSuccess: '操作成功，正在重新啟動，請稍後手動重新整理瀏覽器',\n        entranceHelper: '設定安全入口有利於提高系統的安全性，如有需要，前往 面板設定-安全 中，啟用安全入口',\n        appInstalled: '已安裝應用',\n        systemInfo: '系統資訊',\n        hostname: '主機名稱',\n        platformVersion: '發行版本',\n        kernelVersion: '核心版本',\n        kernelArch: '系統類型',\n        network: '流量',\n        io: '磁碟 IO',\n        ip: '主機位址',\n        proxy: '系統代理',\n        baseInfo: '基本資訊',\n        totalSend: '總發送',\n        totalRecv: '總接收',\n        rwPerSecond: '讀寫次數',\n        ioDelay: '讀寫延遲',\n        uptime: '啟動時間',\n        runningTime: '執行時間',\n        mem: '系統記憶體',\n        swapMem: 'Swap 分區',\n        runSmoothly: '執行流暢',\n        runNormal: '執行正常',\n        runSlowly: '執行緩慢',\n        runJam: '執行堵塞',\n        core: '物理核心',\n        logicCore: '邏輯核心',\n        corePercent: '核心使用率',\n        cpuFrequency: 'CPU 頻率',\n        cpuDetailedPercent: 'CPU 佔用',\n        cpuUser: '使用者態',\n        cpuSystem: '內核態',\n        cpuIdle: '空閒',\n        cpuIrq: '硬中斷',\n        cpuSoftirq: '軟中斷',\n        cpuSteal: '被VM搶佔',\n        cpuTop: 'CPU 佔用率 Top5 的行程資訊',\n        memTop: '記憶體佔用率 Top5 的行程資訊',\n        loadAverage: '最近 {0} 分鐘平均負載',\n        load: '負載',\n        mount: '掛載點',\n        fileSystem: '檔案系統',\n        total: '總計',\n        used: '已用',\n        cache: '快取',\n        free: '空閒',\n        shard: '分片',\n        available: '可用',\n        percent: '使用率',\n        goInstall: '去安裝',\n        networkCard: '網卡',\n        disk: '磁碟',\n        memo: '備忘錄',\n        memoPlaceholder: '點選編輯按鈕啟用編輯',\n        carouselSetting: '輪播設定',\n        tooltipSensitiveInfo: '顯示/隱藏敏感資訊',\n    },\n    tabs: {\n        more: '更多',\n        hide: '收起',\n        closeLeft: '關閉左側',\n        closeRight: '關閉右側',\n        closeCurrent: '關閉目前',\n        closeOther: '關閉其它',\n        closeAll: '關閉所有',\n    },\n    header: {\n        logout: '登出',\n    },\n    database: {\n        manage: '管理',\n        deleteBackupHelper: '同時刪除資料庫備份',\n        delete: '刪除操作無法回滾，請輸入 \"',\n        deleteHelper: '\" 刪除此資料庫',\n        noMysql: '資料庫服務 (MySQL 或 MariaDB)',\n        noPostgresql: '資料庫服務 PostgreSQL',\n        goUpgrade: '去應用列表升級',\n        goInstall: '去應用商店安裝',\n        isDelete: '已刪除',\n        permission: '權限',\n        format: '字元集',\n        collation: '排序規則',\n        collationHelper: '為空則使用 {0} 字元集的預設排序規則',\n        permissionForIP: '指定 IP',\n        permissionAll: '所有人(%)',\n        localhostHelper: '將容器部署的資料庫權限設定為\"localhost\"會導致容器外部無法存取，請謹慎選擇',\n        databaseConnInfo: '連接資訊',\n        rootPassword: 'root 密碼',\n        serviceName: '服務名稱',\n        serviceNameHelper: '用於同一 network 下的容器間存取',\n        backupList: '備份列表',\n        loadBackup: '匯入備份',\n        localUpload: '本機上傳',\n        hostSelect: '伺服器選擇',\n        selectHelper: '是否確認匯入備份檔案 {0}？',\n        remoteAccess: '遠端存取',\n        remoteHelper: '多個 ip 以逗號分隔，例：172.16.10.111,172.16.10.112',\n        remoteConnHelper: 'root 帳號遠端連接 MySQL 有安全風險，開啟需謹慎',\n        changePassword: '改密',\n        changeConnHelper: '此操作將修改目前資料庫 {0}，是否繼續？',\n        changePasswordHelper: '目前資料庫已經關聯應用，修改密碼將同步修改應用中資料庫密碼，修改後重新啟動生效。',\n        recoverTimeoutHelper: '-1 表示不限制逾時時間',\n        portHelper: '該埠為容器對外暴露埠，修改需要單獨儲存並且重新啟動容器',\n        confChange: '設定修改',\n        confNotFound: '未能找到該應用設定檔，請在應用商店升級該應用至最新版本後重試',\n        loadFromRemote: '從伺服器同步',\n        userBind: '綁定使用者',\n        pgBindHelper: '此操作用於建立新使用者並將其綁定到目標資料庫，暫不支援選擇已存在於資料庫中的使用者。',\n        pgSuperUser: '超級使用者',\n        loadFromRemoteHelper: '此操作將同步伺服器上資料庫資訊到 1Panel，是否繼續？',\n        passwordHelper: '無法取得，可點選修改',\n        remote: '遠端',\n        remoteDB: '遠端伺服器',\n        createRemoteDB: '新增遠端伺服器',\n        unBindRemoteDB: '解綁遠端伺服器',\n        unBindForce: '強制解綁',\n        unBindForceHelper: '忽略解綁過程中的所有錯誤，確保最終操作成功',\n        unBindRemoteHelper: '解綁遠端資料庫只會刪除綁定關係，不會直接刪除遠端資料庫',\n        editRemoteDB: '編輯遠端伺服器',\n        localDB: '本機資料庫',\n        address: '資料庫位址',\n        version: '資料庫版本',\n        userHelper: 'root 使用者或擁有 root 權限的資料庫使用者',\n        pgUserHelper: '具有超級管理員權限的資料庫使用者',\n        ssl: '使用 SSL',\n        clientKey: '用戶端私鑰',\n        clientCert: '用戶端憑證',\n        hasCA: '擁有 CA 憑證',\n        caCert: 'CA 憑證',\n        skipVerify: '忽略校驗憑證可用性偵測',\n        initialDB: '初始資料庫',\n        formatHelper: '目前資料庫字元集為 {0}，字元集不一致可能導致復原失敗',\n        dropHelper: '將上傳檔案拖曳到此處，或',\n        clickHelper: '點選上傳',\n        supportUpType:\n            '僅支援 sql、sql.gz、tar.gz、.zip 檔案格式，匯入的壓縮檔案必須保證只有一個 .sql 檔案或包含 test.sql',\n        currentStatus: '當前狀態',\n        baseParam: '基礎參數',\n        performanceParam: '效能參數',\n        runTime: '啟動時間',\n        connections: '總連接數',\n        bytesSent: '發送',\n        bytesReceived: '接收',\n        queryPerSecond: '每秒查詢',\n        txPerSecond: '每秒事務',\n        connInfo: '活動/峰值連接數',\n        connInfoHelper: '若值過大，增加 max_connections',\n        threadCacheHit: '執行緒快取命中率',\n        threadCacheHitHelper: '若過低,增加 thread_cache_size',\n        indexHit: '索引命中率',\n        indexHitHelper: '若過低,增加 key_buffer_size',\n        innodbIndexHit: 'Innodb 索引命中率',\n        innodbIndexHitHelper: '若過低,增加 innodb_buffer_pool_size',\n        cacheHit: '查詢快取命中率',\n        cacheHitHelper: '若過低,增加 query_cache_size',\n        tmpTableToDB: '建立臨時表到磁碟',\n        tmpTableToDBHelper: '若過大,嘗試增加 tmp_table_size',\n        openTables: '已開啟的表',\n        openTablesHelper: 'table_open_cache 設定值應大於等於此值',\n        selectFullJoin: '沒有使用索引的量',\n        selectFullJoinHelper: '若不為0，請檢查資料表的索引是否合理',\n        selectRangeCheck: '沒有索引的 JOIN 量',\n        selectRangeCheckHelper: '若不為0，請檢查資料表的索引是否合理',\n        sortMergePasses: '排序後的合併次數',\n        sortMergePassesHelper: '若值過大，增加sort_buffer_size',\n        tableLocksWaited: '鎖表次數',\n        tableLocksWaitedHelper: '若值過大，請考慮增加您的資料庫效能',\n        performanceTuning: '效能調整',\n        optimizationScheme: '最佳化方案',\n        keyBufferSizeHelper: '用於索引的緩衝區大小',\n        queryCacheSizeHelper: '查詢快取，不開啟請設為0',\n        tmpTableSizeHelper: '臨時表快取大小',\n        innodbBufferPoolSizeHelper: 'Innodb 緩衝區大小',\n        innodbLogBufferSizeHelper: 'Innodb 日誌緩衝區大小',\n        sortBufferSizeHelper: '* 連接數, 每個執行緒排序的緩衝大小',\n        readBufferSizeHelper: '* 連接數, 讀入緩衝區大小',\n        readRndBufferSizeHelper: '* 連接數, 隨機讀取緩衝區大小',\n        joinBufferSizeHelper: '* 連接數, 關聯表快取大小',\n        threadStackelper: '* 連接數, 每個執行緒的堆疊大小',\n        binlogCacheSizeHelper: '* 連接數, 二進制日誌快取大小(4096的倍數)',\n        threadCacheSizeHelper: '執行緒池大小',\n        tableOpenCacheHelper: '表快取',\n        maxConnectionsHelper: '最大連接數',\n        restart: '重新啟動資料庫',\n        slowLog: '慢日誌',\n        noData: '暫無慢日誌...',\n        isOn: '開啟',\n        longQueryTime: '閾值(秒)',\n        thresholdRangeHelper: '請輸入正確的閾值(1 - 600)',\n        timeout: '逾時時間(秒)',\n        timeoutHelper: '空閒連接逾時時間，0表示不斷開',\n        maxclients: '最大連接數',\n        requirepassHelper: '留空代表沒有設定密碼，修改需要單獨儲存並且重新啟動容器',\n        databases: '資料庫數量',\n        maxmemory: '最大記憶體使用',\n        maxmemoryHelper: '0 表示不做限制',\n        tcpPort: '目前監聽埠',\n        uptimeInDays: '已執行天數',\n        connectedClients: '連接的用戶端數量',\n        usedMemory: '目前 Redis 使用的記憶體大小',\n        usedMemoryRss: '向作業系統申請的記憶體大小',\n        usedMemoryPeak: 'Redis 的記憶體消耗峰值',\n        memFragmentationRatio: '記憶體碎片比率',\n        totalConnectionsReceived: '執行以來連接過的用戶端的總數量',\n        totalCommandsProcessed: '執行以來執行過的指令的總數量',\n        instantaneousOpsPerSec: '伺服器每秒鐘執行的指令數量',\n        keyspaceHits: '尋找資料庫鍵成功的次數',\n        keyspaceMisses: '尋找資料庫鍵失敗的次數',\n        hit: '尋找資料庫鍵命中率',\n        latestForkUsec: '最近一次 fork() 操作耗費的微秒數',\n        redisCliHelper: '未偵測到 redis-cli 服務，請先啟用服務',\n        redisQuickCmd: 'Redis 快速指令',\n        recoverHelper: '即將使用 [{0}] 對資料進行覆蓋，是否繼續？',\n        submitIt: '覆蓋資料',\n        baseConf: '基本設定',\n        allConf: '全部設定',\n        restartNow: '立即重新啟動',\n        restartNowHelper1: '修改設定後需要重新啟動生效，若您的資料需要持久化請先執行 save 操作。',\n        restartNowHelper: '修改設定後需要重新啟動生效。',\n        persistence: '持久化',\n        rdbHelper1: '秒內,插入',\n        rdbHelper2: '條資料',\n        rdbHelper3: '符合任意一個條件將會觸發RDB持久化',\n        rdbInfo: '請確認規則列表中值在 1-100000 之間',\n        containerConn: '容器連接',\n        copyConnURL: '複製連接 URL',\n        connAddress: '地址',\n        containerConnHelper: 'PHP 執行環境/容器安裝的應用程式使用此連接地址',\n        remoteConn: '外部連接',\n        remoteConnHelper2: '非容器環境或外部連接需使用此地址。',\n        remoteConnHelper3: '預設存取地址為主機IP，修改請前往面板設定頁面的「預設存取地址」設定項目。',\n        localIP: '本機 IP',\n    },\n    aiTools: {\n        agents: {\n            agents: '智能體',\n            agent: '智能體',\n            account: '模型帳號',\n            noAccountHint: '選擇已有模型帳號，或直接建立',\n            accountCount: '模型帳號 {0} 個',\n            syncAgents: '同步關聯智能體',\n            syncAgentsHelper: '更新使用該模型帳號的智能體 openclaw.json',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: '應用版本',\n            webuiPort: 'WebUI 埠',\n            allowedOrigins: '訪問地址',\n            allowedOriginsHelper:\n                '一行一個完整訪問地址，建議優先使用 HTTPS，例如 https://192.168.1.2:18789；未設定預設訪問地址時請手動填寫',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: '請至少填寫一個訪問地址',\n            allowedOriginsInvalid: '訪問地址格式錯誤，請輸入 http(s)://網域或IP[:埠]',\n            provider: '模型供應商',\n            apiKey: 'API Key',\n            baseUrl: 'Base URL',\n            accountModels: '模型池',\n            accountModelsHelper: '配置該帳號可提供給 OpenClaw 使用與切換的模型列表',\n            accountModelsRequired: '請至少配置一個模型',\n            accountModelsDuplicate: '模型池中存在重複模型，請檢查後重試',\n            modelPool: '模型池',\n            modelPoolHelper: '在此維護該模型帳號的模型池，建立智能體與 OpenClaw 模型切換都會使用這些模型',\n            modelInputTypes: '輸入類型',\n            reasoning: '推理模型',\n            token: 'Token',\n            manualModel: '手動輸入模型',\n            verified: '驗證狀態',\n            verifySkipped: '不驗證',\n            configTitle: '設定',\n            settingsTab: '設定',\n            securityTab: '安全',\n            otherTab: '其他',\n            timeZone: '時區',\n            browserEnabled: '瀏覽器開關',\n            switchModelSuccess: '模型切換成功',\n            channelsTab: '頻道',\n            wecom: '企業微信',\n            dingtalk: '釘釘',\n            feishu: '飛書',\n            pluginNotInstalled: '插件未安裝，請先安裝插件',\n            dmPolicy: '私聊策略',\n            groupPolicy: '群組策略',\n            policyPairing: '配對',\n            policyAllowlist: '白名單',\n            policyOpen: '開放',\n            policyDisabled: '禁用',\n            botName: '機器人名稱',\n            botId: 'Bot ID',\n            appId: '應用 App ID',\n            appSecret: '應用 App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: '私聊白名單',\n            allowFromHelper: '一行一個發送方識別碼，僅在私聊策略為白名單時生效',\n            allowFromPlaceholder: '一行一個發送方識別碼',\n            groupAllowFrom: '群組白名單',\n            groupAllowFromHelper: '一行一個群組識別碼，僅在群組策略為白名單時生效',\n            groupAllowFromPlaceholder: '一行一個群組識別碼',\n            allowFromRequired: '請至少填寫一個白名單項',\n            saveAndRestartGateway: '保存並重新啟動網關',\n            pairingCode: '配對碼',\n            pairingCodePlaceholder: '請輸入配對碼',\n            approvePairing: '批准配對',\n            feishuRequired: '請填寫 botName / appId / appSecret',\n            saveSuccess: '保存成功',\n            pairingCodeRequired: '請輸入配對碼',\n            pairingApproveSuccess: '配對成功',\n            customProviderHelper: '自訂模型供應商不驗證帳號是否可用',\n            feishuSaveSuccess: '儲存成功',\n        },\n        model: {\n            model: '模型',\n            localModel: '本地模型',\n            create: '新增模型',\n            create_helper: '拉取 \"{0}\"',\n            ollama_doc: '您可以瀏覽 Ollama 官方網站，搜尋並尋找更多模型。',\n            container_conn_helper: '容器間瀏覽或連接使用此地址',\n            ollama_sync: '同步 Ollama 模型發現下列模型不存在，是否刪除？',\n            from_remote: '該模型並非透過 1Panel 下載，無相關拉取日誌。',\n            no_logs: '該模型的拉取日誌已被刪除，無法檢視相關日誌。',\n            vllmVersionHelper: 'FusionXpark GB 10 伺服器請選擇 -cu130 版本',\n        },\n        proxy: {\n            proxy: 'AI 代理增強',\n            proxyHelper1: '綁定域名並啟用 HTTPS，提高傳輸安全性',\n            proxyHelper2: '限制 IP 瀏覽，防止在網路上暴露',\n            proxyHelper3: '啟用即時串流',\n            proxyHelper4: '建立後，您可以在網站列表中檢視並管理',\n            proxyHelper5: '啟用後，您可以在應用商店 - 已安裝 - Ollama - 參數中取消埠外部瀏覽以提高安全性',\n            proxyHelper6: '如需關閉代理設定，可以在網站列表中刪除',\n            whiteListHelper: '限制僅白名單中的 IP 可瀏覽',\n        },\n        gpu: {\n            gpu: 'GPU 監控',\n            gpuHelper: '目前系統未偵測到 NVIDIA-SMI 或 XPU-SMI 指令，請檢查後重試',\n            process: '行程資訊',\n            type: '類型',\n            typeG: '圖形',\n            typeC: '計算',\n            typeCG: '計算+圖形',\n            processName: '行程名稱',\n            shr: '共享顯存',\n            temperatureHelper: 'GPU 溫度過高會導致 GPU 頻率下降',\n            gpuUtil: 'GPU 使用率',\n            temperature: '溫度',\n            performanceState: '效能狀態',\n            powerUsage: '功耗',\n            memoryUsage: '顯存使用率',\n            fanSpeed: '風扇轉速',\n            power: '功率',\n            powerCurrent: '目前功率',\n            powerLimit: '功率上限',\n            memory: '顯存',\n            memoryUsed: '顯存使用',\n            memoryTotal: '顯存總計',\n            percent: '使用率',\n            base: '基礎資訊',\n            driverVersion: '驅動程式版本',\n            cudaVersion: 'CUDA 版本',\n            processMemoryUsage: '顯存使用',\n            performanceStateHelper: '從 P0 (最大效能) 到 P12 (最小效能)',\n            busID: '匯流排位址',\n            persistenceMode: '持續模式',\n            enabled: '開啟',\n            disabled: '關閉',\n            persistenceModeHelper: '持續模式能更加快速地響應任務，但相應待機功耗也會增加',\n            displayActive: '顯示卡初始化',\n            displayActiveT: '是',\n            displayActiveF: '否',\n            ecc: '是否開啟錯誤檢查和糾正技術',\n            computeMode: '計算模式',\n            default: '預設',\n            exclusiveProcess: '行程排他',\n            exclusiveThread: '執行緒排他',\n            prohibited: '禁止',\n            defaultHelper: '預設: 行程可以並發執行',\n            exclusiveProcessHelper: '行程排他: 只有一個 CUDA 上下文可以使用 GPU，但可以由多個執行緒共享',\n            exclusiveThreadHelper: '執行緒排他: 只有一個執行緒在 CUDA 上下文中可以使用 GPU',\n            prohibitedHelper: '禁止: 不允許行程同時執行',\n            migModeHelper: '用於建立 MIG 實例，在使用者層實現 GPU 的物理隔離。',\n            migModeNA: '不支援',\n            current: '即時監控',\n            history: '歷史記錄',\n            notSupport: '當前版本或驅動不支援顯示該參數。',\n            processCount: '行程數',\n        },\n        mcp: {\n            server: 'MCP Server',\n            baseUrl: '外部存取路徑',\n            baseUrlHelper: '例：http://192.168.1.1:8000',\n            ssePath: 'SSE 路徑',\n            ssePathHelper: '例：/sse,注意不要與其他 Server 重複',\n            environment: '環境變數',\n            envKey: '變數名',\n            envValue: '變數值',\n            externalUrl: '外部連接地址',\n            operatorHelper: '將對 {0} 進行 {1} 操作，是否繼續？',\n            domain: '預設存取地址',\n            domainHelper: '例：192.168.1.1 或 example.com',\n            bindDomain: '綁定網站',\n            commandPlaceHolder: '僅支援 npx 和 二進制啟動的指令',\n            importMcpJson: '匯入 MCP Server 設定',\n            importMcpJsonError: 'mcpServers 結構不正確',\n            bindDomainHelper: '綁定網站之後會修改所有已安裝 MCP Server 的存取地址，並關閉埠的外部存取',\n            outputTransport: '輸出類型',\n            streamableHttpPath: '流式傳輸路徑',\n            streamableHttpPathHelper: '例：/mcp, 注意不要與其他 Server 重複',\n            npxHelper: '適合 npx 或 二進制啟動的 mcp',\n            uvxHelper: '適合 uvx 啟動的 mcp',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: '模型目錄',\n            commandHelper: '若需外部存取，請將命令中的埠設置為與應用埠相同',\n            imageAlert: '由於映像較大，建議先手動將映像下載到伺服器後再進行安裝',\n            modelSpeedup: '啟用模型加速',\n            modelType: '模型類型',\n        },\n    },\n    container: {\n        createByCommand: '指令建立',\n        importContainerBackupTip: '匯入容器備份檔案，僅支援 .tar.gz 格式',\n        importComposeBackupTip: '匯入編排備份檔案，僅支援 .tar.gz 格式',\n        stopContainerBeforeBackup: '備份前停止容器',\n        stopComposeBeforeBackup: '備份前停止編排',\n        stopBeforeBackupHelper: '啟用後備份前將停止容器或編排服務，完成後自動恢復，以確保資料一致性',\n        commandInput: '指令輸入',\n        commandRule: '請輸入正確的 docker run 容器建立指令',\n        commandHelper: '將在伺服器上執行該條指令以建立容器，是否繼續？',\n        updateHelper1: '偵測到該容器來源於應用商店，請注意以下兩點：',\n        updateHelper2: '1. 目前修改內容不會同步到應用商店的已安裝應用。',\n        updateHelper3: '2. 如果在已安裝頁面修改應用，目前編輯的部分內容將失效。',\n        updateHelper4: '編輯容器需要重建，任何未持久化的資料將遺失，是否繼續？',\n        containerList: '容器列表',\n        operatorHelper: '將對以下容器進行 {0} 操作，是否繼續？',\n        operatorAppHelper:\n            '將對以下容器進行 {0} 操作，\\n其中部分來源於應用商店，該操作可能會影響到該服務的正常使用。\\n是否確認？',\n        containerDeleteHelper:\n            '偵測到容器來源於應用商店，刪除容器不會將其從 1Panel 中完全移除。如需徹底刪除，請前往應用商店-『已安裝』或『運行環境』等選單中操作。是否繼續？',\n        start: '啟動',\n        stop: '停止',\n        restart: '重新啟動',\n        kill: '強制停止',\n        pause: '暫停',\n        unpause: '復原',\n        rename: '重新命名',\n        remove: '刪除',\n        removeAll: '刪除所有',\n        containerPrune: '清理容器',\n        containerPruneHelper1: '清理容器 將刪除所有處於停止狀態的容器。',\n        containerPruneHelper2:\n            '若容器來自於應用商店，在執行清理操作後，您需要前往 [應用商店] 的 [已安裝] 列表，點選 [重建] 按鈕進行重新安裝。',\n        containerPruneHelper3: '該操作無法回滾，是否繼續？',\n        imagePrune: '清理映像',\n        imagePruneSome: '未標籤映像',\n        imagePruneSomeEmpty: '暫無待清理的未使用 none 標籤映像',\n        imagePruneSomeHelper: '清理下列標籤為 none 且未被任何容器使用的映像',\n        imagePruneAll: '未使用映像',\n        imagePruneAllEmpty: '暫無待清理的未使用映像',\n        imagePruneAllHelper: '清理下列未被任何容器使用的映像',\n        networkPrune: '清理網路',\n        networkPruneHelper: '清理網路 將刪除所有未被使用的網路，該操作無法回滾，是否繼續？',\n        volumePrune: '清理磁碟區',\n        volumePruneHelper: '清理磁碟區 將刪除所有未被使用的本機磁碟區，該操作無法回滾，是否繼續？',\n        cleanSuccess: '操作成功，本次清理數量: {0} 個',\n        cleanSuccessWithSpace: '操作成功，本次清理數量: {0} 個，釋放磁碟空間: {1}',\n        unExposedPort: '目前埠映射地址為 127.0.0.1，無法實現外部存取',\n        upTime: '執行時長',\n        fetch: '過濾',\n        lines: '條數',\n        linesHelper: '請輸入正確的日誌取得條數',\n        downloadLinesHelper: '選擇或輸入日誌下載行數',\n        lastDay: '最近一天',\n        last4Hour: '最近 4 小時',\n        lastHour: '最近 1 小時',\n        last10Min: '最近 10 分鐘',\n        cleanLog: '清空日誌',\n        downLogHelper1: '即將下載 {0} 容器所有日誌，是否繼續？',\n        downLogHelper2: '即將下載 {0} 容器最近 {1} 條日誌，是否繼續？',\n        cleanLogHelper: '清空日誌需要重新啟動容器，該操作無法回滾，是否繼續？',\n        newName: '新名稱',\n        workingDir: '工作目錄',\n        source: '資源使用率',\n        cpuUsage: 'CPU 使用',\n        cpuTotal: 'CPU 總計',\n        core: '核心數',\n        memUsage: '記憶體使用',\n        memTotal: '記憶體限額',\n        memCache: '快取使用',\n        loadSize: '取得容器大小',\n        ip: 'IP 位址',\n        cpuShare: 'CPU 權重',\n        cpuShareHelper: '容器預設份額為 1024 個 CPU，增大可使目前容器獲得更多的 CPU 時間',\n        inputIpv4: '請輸入 IPv4 位址',\n        inputIpv6: '請輸入 IPv6 位址',\n        diskUsage: '磁碟佔用',\n        localVolume: '本機儲存卷',\n        buildCache: '建置快取',\n        usage: '已佔用：{0}, 可釋放：{1}',\n        clean: '釋放',\n        imageClean: '清理映像將刪除所有未被使用的映像，該操作無法復原，是否繼續？',\n        containerClean: '清理容器將刪除所有處於停止中狀態的容器（包括應用商店停止應用），該操作無法復原，是否繼續？',\n        sizeRw: '容器層大小',\n        sizeRwHelper: '容器獨有的可寫層大小',\n        sizeRootFs: '虛擬大小',\n        sizeRootFsHelper: '容器依賴的所有映像層 + 容器層的總大小',\n        containerFromAppHelper: '偵測到該容器來源於應用商店，應用操作可能會導致目前編輯失效',\n        containerFromAppHelper1: '在已安裝應用程式列表點選 [參數] 按鈕，進入編輯頁面即可修改容器名稱。',\n        command: '指令',\n        console: '控制臺互動',\n        tty: '偽終端 ( -t )',\n        openStdin: '標準輸入 ( -i )',\n        custom: '自訂',\n        emptyUser: '為空時，將使用容器預設的使用者登入',\n        privileged: '特權模式',\n        privilegedHelper: '允許容器在主機上執行某些特權操作，可能會增加容器風險，請謹慎開啟',\n        upgradeHelper: '倉庫名稱/映像名稱:映像版本',\n        upgradeWarning2: '升級操作需要重建容器，任何未持久化的資料將會遺失，是否繼續？',\n        oldImage: '目前映像',\n        sameImageContainer: '同映像容器',\n        sameImageHelper: '同映像容器可勾選後批次升級',\n        targetImage: '目標映像',\n        imageLoadErr: '未偵測到容器的映像名稱',\n        imageUpdateTagEmpty: '未偵測到可更新的映像標籤',\n        appHelper: '該容器來源於應用商店，升級可能導致該服務不可用',\n        resource: '資源',\n        input: '手動輸入',\n        forcePull: '強制拉取映像',\n        forcePullHelper: '忽略伺服器已存在的映像，重新拉取一次',\n        imageUpdateHelper: '將檢查映像倉庫中的同名標籤，若有更新則拉取並更新本地映像。',\n        server: '伺服器',\n        serverExample: '80, 80-88, ip:80 或 ip:80-88',\n        containerExample: '80 或 80-88',\n        exposePort: '暴露埠',\n        exposeAll: '暴露所有',\n        cmdHelper: '例： nginx -g \"daemon off;\"',\n        entrypointHelper: '例： docker-entrypoint.sh',\n        autoRemove: '容器退出後自動刪除容器',\n        cpuQuota: 'CPU 限制',\n        memoryLimit: '記憶體限制',\n        limitHelper: '限制為 0 則關閉限制，最大可用為 {0}',\n        macAddr: 'MAC 地址',\n        mount: '掛載',\n        volumeOption: '掛載卷',\n        hostOption: '本機目錄',\n        serverPath: '伺服器目錄',\n        containerDir: '容器目錄',\n        volumeHelper: '請確認磁碟區內容輸入正確',\n        networkEmptyHelper: '請確認容器網路選擇正確',\n        modeRW: '讀寫',\n        modeR: '唯讀',\n        sharedLabel: '傳播模式',\n        private: '私有',\n        privateHelper: '容器裡的掛載變化和主機互不干擾',\n        rprivate: '遞迴私有',\n        rprivateHelper: '容器裡所有掛載都和主機完全隔離',\n        shared: '共享',\n        sharedHelper: '主機和容器裡的掛載變化互相可見',\n        rshared: '遞迴共享',\n        rsharedHelper: '主機和容器裡所有掛載變化都互相可見',\n        slave: '從屬',\n        slaveHelper: '容器能看見主機的掛載變化，但自己的變化不影響主機',\n        rslave: '遞迴從屬',\n        rslaveHelper: '容器裡所有掛載都能看見主機變化，但不影響主機',\n        mode: '權限',\n        env: '環境變數',\n        restartPolicy: '重新啟動規則',\n        always: '一直重新啟動',\n        unlessStopped: '未手動停止則重新啟動',\n        onFailure: '失敗後重新啟動（預設重新啟動 5 次）',\n        no: '不重新啟動',\n        refreshTime: '重新整理間隔',\n        cache: '快取',\n        image: '映像',\n        imagePull: '拉取映像',\n        imagePullHelper: '支援選擇拉取多個映像，輸入一組映像後Enter繼續',\n        imagePush: '推送映像',\n        imagePushHelper: '偵測到該映像存在多個標籤，請確認推送時使用的映像名稱為：{0}',\n        imageDelete: '刪除映像',\n        repoName: '倉庫名',\n        imageName: '映像名',\n        httpRepo: 'http 倉庫新增授信需要重新啟動 docker 服務',\n        delInsecure: '刪除授信',\n        delInsecureHelper: '刪除授信需要重新啟動 docker 服務，是否刪除？',\n        pull: '拉取',\n        path: '路徑',\n        importImage: '匯入映像',\n        imageBuild: '構建映像',\n        buildArgs: '構建參數',\n        pathSelect: '路徑選擇',\n        label: '標籤',\n        imageTag: '映像標籤',\n        imageTagHelper: '支援設定多個映像標籤，輸入一個標籤後Enter繼續',\n        push: '推送',\n        fileName: '檔案名',\n        export: '匯出',\n        exportImage: '匯出映像',\n        size: '大小',\n        tag: '標籤',\n        tagHelper: '一行一個，例： \\nkey1=value1\\nkey2=value2',\n        imageNameHelper: '映像名稱及 Tag，例：nginx:latest',\n        cleanBuildCache: '清理建置快取',\n        delBuildCacheHelper: '清理建置快取將刪除所有建置所產生的快取，此操作無法回復。是否繼續？',\n        urlWarning: '路徑前綴不需要新增 http:// 或 https://，請修改',\n        network: '網路',\n        networkHelper: '刪除 1panel-network 容器網路將影響部分應用和執行環境的正常使用，是否繼續？',\n        networkName: '網路名',\n        driver: '模式',\n        option: '參數',\n        attachable: '可用',\n        parentNetworkCard: '父網卡',\n        subnet: '子網',\n        scope: 'IP 範圍',\n        gateway: '閘道器',\n        auxAddress: '排除 IP',\n        volume: '磁碟區',\n        volumeDir: '磁碟區目錄',\n        nfsEnable: '啟用 NFS 儲存',\n        nfsAddress: '地址',\n        mountpoint: '掛載點',\n        mountpointNFSHelper: '例：/nfs, /nfs-share',\n        options: '可選參數',\n        repo: '倉庫',\n        httpRepoHelper: '操作 HTTP 類型倉庫需要重新啟動 Docker 服務。',\n        downloadUrl: '下載網址',\n        imageRepo: '映像倉庫',\n        repoHelper: '是否包含映像倉庫/組織/項目?',\n        auth: '認證',\n        mirrorHelper:\n            '當存在多個加速器時，需要換行顯示，例： \\nhttp://xxxxxx.m.daocloud.io \\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper: '當存在多個私有倉庫時，需要換行顯示，例：\\n172.16.10.111:8081 \\n172.16.10.112:8081',\n        compose: '編排',\n        composeFile: '編排檔案',\n        fromChangeHelper: '切換來源將清空當前已編輯內容，是否繼續？',\n        composePathHelper: '設定檔儲存路徑: {0}',\n        composeHelper: '通過 1Panel 編輯或範本建立的編排，將儲存在 {0}/docker/compose 路徑下',\n        deleteFile: '刪除檔案',\n        deleteComposeHelper: '刪除容器編排的所有檔案，包括設定檔和持久化檔案，請謹慎操作',\n        deleteCompose: '\" 刪除此編排',\n        composeDirectory: '編排目錄',\n        template: '範本',\n        composeTemplate: '編排範本',\n        content: '內容',\n        contentEmpty: '編排內容不能為空，請輸入後重試',\n        containerNumber: '容器數量',\n        containerStatus: '容器狀態',\n        exited: '已停止',\n        running: '執行中 ( {0} / {1} )',\n        composeDetailHelper: '該 compose 為 1Panel 編排外部建立。暫不支援啟停操作。',\n        composeOperatorHelper: '將對 {0} 進行 {1} 操作，是否繼續？',\n        composeDownHelper: '將停止並刪除 {0} 編排下所有容器及網路，是否繼續？',\n        composeEnvHelper2: '該編排為 1Panel 應用商店建立，請在已安裝應用中修改環境變數。',\n        composeContentPlaceholder: '在此寫入或貼上 docker-compose 檔案內容',\n        setting: '設定',\n        goSetting: '去修改',\n        restartHelper: '修改設定後需要重新啟動 Docker 服務才能生效',\n        operatorStatusHelper: '此操作將{0}Docker 服務，是否繼續？',\n        dockerStatus: 'Docker 服務',\n        daemonJsonPathHelper: '請保證設定路徑與 docker.service 中指定的設定路徑保持一致。',\n        mirrors: '映像加速',\n        mirrorsHelper: '優先使用加速 URL 執行操作，設定為空則取消映像加速。',\n        mirrorsHelper2: '設定請參考官方文件',\n        registries: '私有倉庫',\n        ipv6Helper: '開啟 IPv6 後，需要增加 IPv6 的容器網路，設定請參考官方文件',\n        ipv6CidrHelper: '容器的 IPv6 位址池範圍',\n        ipv6TablesHelper: 'Docker IPv6 對 iptables 規則的自動設定',\n        experimentalHelper: '開啟 ip6tables 必須開啟此設定，否則 ip6tables 會被忽略',\n        cutLog: '日誌切割',\n        cutLogHelper1: '目前設定只會影響新建立的容器；',\n        cutLogHelper2: '已經建立的容器需要重新建立使設定生效；',\n        cutLogHelper3:\n            '注意，重新建立容器可能會導致資料遺失。如果你的容器中有重要資料，確保在執行重建操作之前進行備份。',\n        maxSize: '檔案大小',\n        maxFile: '保留份數',\n        liveHelper: '允許在 Docker 守護行程發生意外停機或崩潰時保留正在執行的容器狀態',\n        liveWithSwarmHelper: 'live-restore 守護行程設定與 Swarm 模式不相容',\n        iptablesDisable: '關閉 iptables',\n        iptablesHelper1: 'Docker 對 iptables 規則的自動設定',\n        iptablesHelper2: '關閉 iptables 會導致容器無法與外部網路通訊。',\n        daemonJsonPath: '設定路徑',\n        serviceUnavailable: '尚未啟動 Docker 服務，請在',\n        startIn: '中開啟',\n        sockPath: 'Socket 路徑',\n        sockPathHelper: 'Docker 守護行程（Docker Daemon）與用戶端之間的通訊通道',\n        sockPathHelper1: '預設路徑：/var/run/docker.sock',\n        sockPathMsg: '儲存設定 Socket 路徑可能導致 Docker 服務不可用，是否繼續？',\n        sockPathErr: '請選擇或輸入正確的 Docker sock 檔案路徑',\n        related: '相關資源',\n        includeAppstore: '顯示應用商店容器',\n        excludeAppstore: '隱藏應用商店容器',\n        cleanDockerDiskZone: '清理 Docker 使用的磁碟空間',\n        cleanImagesHelper: '( 清理所有未被任何容器使用的映像 )',\n        cleanContainersHelper: '( 清理所有處於停止狀態的容器 )',\n        cleanVolumesHelper: '( 清理所有未被使用的本機磁碟區 )',\n        makeImage: '製作映像',\n        newImageName: '新映像名稱',\n        commitMessage: '提交資訊',\n        author: '作者',\n        ifPause: '製作過程中是否暫停容器',\n        ifMakeImageWithContainer: '是否根據此容器製作新映像？',\n        finishTime: '上一次停止時間',\n        exportHelper: '即將匯出 {0} 條編排範本，是否繼續？',\n    },\n    cronjob: {\n        importHelper:\n            '匯入時將自動跳過重名計劃任務。任務預設設定為【停用】狀態，資料關聯異常時，設定為【待編輯】狀態。',\n        changeStatus: '狀態修改',\n        disableMsg: '停止計劃任務會導致該任務不再自動執行。是否繼續？',\n        enableMsg: '啟用計劃任務會讓該任務定期自動執行。是否繼續？',\n        taskType: '任務類型',\n        nextTime: '近 5 次執行',\n        record: '報告',\n        viewRecords: '檢視報告',\n        shell: 'Shell 腳本',\n        stop: '手動結束',\n        stopHelper: '該操作將強制停止該次任務執行，是否繼續？',\n        log: '備份日誌',\n        logHelper: '備份系統日誌',\n        logHelper1: '1. 1Panel 系統日誌',\n        logHelper2: '2. 伺服器的 SSH 登入日誌',\n        logHelper3: '3. 所有網站日誌',\n        containerCheckBox: '在容器中執行（無需再輸入進入容器指令）',\n        containerName: '容器名稱',\n        ntp: '同步伺服器時間',\n        ntp_helper: '您可以在工具箱的快速設定頁面設定 NTP 伺服器',\n        app: '備份應用',\n        website: '備份網站',\n        rulesHelper: '支援多個排除規則，使用英文逗號 , 分隔，例：*.log,*.sql',\n        lastRecordTime: '上次執行情況',\n        database: '備份資料庫',\n        backupArgs: '備份參數',\n        backupArgsHelper: '未列出的備份參數可手動輸入並選擇，例：輸入 --no-data 後選擇下拉列表中的第一個選項',\n        singleTransaction: '使用單一事務備份 InnoDB 表，適用於大資料量的備份',\n        quick: '逐行讀取資料，而不是將整個表載入到記憶體中，適用於大資料量和低記憶體機器的備份',\n        skipLockTables: '不鎖定所有表進行備份，適用於高併發的資料庫',\n        missBackupAccount: '未能找到備份帳號',\n        syncDate: '同步時間 ',\n        clean: '快取清理',\n        curl: '存取 URL',\n        taskName: '任務名稱',\n        cronSpec: '執行週期',\n        cronSpecDoc: '自訂執行週期僅支援【分 時 日 月 週】格式，例如 0 0 * * *，詳細參考官方文件。',\n        cronSpecHelper: '請輸入正確的執行週期',\n        cleanHelper: '該操作將清理所有任務執行記錄、備份檔案和日誌檔案，是否繼續？',\n        backupContent: '備份內容',\n        directory: '備份目錄 / 檔案',\n        sourceDir: '備份目錄',\n        snapshot: '系統快照',\n        allOptionHelper: '目前計劃任務為備份所有【{0}】，暫不支援直接下載，可在【{0}】備份列表中檢視',\n        exclusionRules: '排除規則',\n        exclusionRulesHelper: '選擇或輸入排除規則，輸入完一組後Enter繼續，排除規則將對此次備份的所有壓縮操作生效',\n        default_download_path: '預設下載網址',\n        saveLocal: '同時保留本機備份（和雲端儲存保留份數一致）',\n        url: 'URL 位址',\n        urlHelper: '請輸入正確的 URL 位址',\n        targetHelper: '備份帳號可在面板設定中維護',\n        ignoreApp: '排除應用',\n        withImage: '備份應用映像',\n        retainCopies: '保留份數',\n        retryTimes: '失敗重試次數',\n        timeout: '逾時時間',\n        ignoreErr: '忽略錯誤',\n        ignoreErrHelper: '忽略備份過程中出現的錯誤，保證所有備份任務執行',\n        retryTimesHelper: '為 0 表示失敗後不重試',\n        retainCopiesHelper: '執行記錄及日誌保留份數',\n        retainCopiesHelper1: '備份檔案保留份數',\n        retainCopiesUnit: ' 份 (檢視)',\n        cronSpecRule: '第 {0} 行中執行週期格式錯誤，請檢查後重試',\n        cronSpecRule2: '執行週期格式錯誤，請檢查後重試',\n        perMonthHelper: '每月 {0} 日 {1}:{2} 執行',\n        perWeekHelper: '每週 {0} {1}:{2} 執行',\n        perDayHelper: '每日 {0}:{1} 執行',\n        perHourHelper: '每小時 {0}分 執行',\n        perNDayHelper: '每 {0} 日 {1}:{2} 執行',\n        perNHourHelper: '每 {0}小時 {1}分 執行',\n        perNMinuteHelper: '每 {0}分 執行',\n        perNSecondHelper: '每 {0}秒 執行',\n        perMonth: '每月',\n        perWeek: '每週',\n        perHour: '每小時',\n        perNDay: '每 N 日',\n        perDay: '每天',\n        perNHour: '每 N 時',\n        perNMinute: '每 N 分鐘',\n        perNSecond: '每 N 秒',\n        day: '日',\n        monday: '週一',\n        tuesday: '週二',\n        wednesday: '週三',\n        thursday: '週四',\n        friday: '週五',\n        saturday: '週六',\n        sunday: '週日',\n        shellContent: '腳本內容',\n        executor: '解釋器',\n        errRecord: '錯誤的日誌記錄',\n        errHandle: '任務執行失敗',\n        noRecord: '目前計劃任務暫未產生記錄',\n        cleanData: '刪除備份檔案',\n        cleanRemoteData: '刪除遠端備份檔案',\n        cleanDataHelper: '刪除該任務執行過程中產生的備份檔案',\n        noLogs: '暫無任務輸出...',\n        errPath: '備份路徑 [{0}] 錯誤，無法下載',\n        cutWebsiteLog: '切割網站日誌',\n        cutWebsiteLogHelper: '切割的日誌檔案會備份到 1Panel 的 backup 目錄下',\n        syncIpGroup: '同步 WAF IP 組',\n        requestExpirationTime: '上傳請求過期時間（小時）',\n        unitHours: '單位：小時',\n        alertTitle: '計劃任務-{0}「{1}」任務失敗告警',\n        library: {\n            script: '腳本',\n            syncNow: '立即同步',\n            turnOnSync: '開啟自動同步',\n            turnOnSyncHelper: '開啟自動同步將在每天凌晨時段進行自動同步',\n            turnOffSync: '關閉自動同步',\n            turnOffSyncHelper: '關閉自動同步可能導致腳本同步不及時，是否確認？',\n            isInteractive: '互動式',\n            interactive: '互動式腳本',\n            interactiveHelper: '在腳本執行過程中需要使用者輸入參數或做出選擇，且無法用於計劃任務中。',\n            library: '腳本庫',\n            remoteLibrary: '遠端腳本庫',\n            create: '新增腳本',\n            edit: '修改腳本',\n            groupHelper: '根據腳本特徵設定不同的分組，可以更快地對腳本進行篩選操作。',\n            handleHelper: '將在 {0} 上執行 {1} 腳本，是否繼續？',\n            noSuchApp: '未偵測到 {0} 服務，請前往腳本庫頁面手動安裝',\n            syncHelper: '即將同步系統腳本庫，該操作僅針對系統腳本，是否繼續？',\n        },\n        withImageHelper: '備份應用商店映像，但會增加快照檔案體積。',\n        cleanLog: '清理日誌',\n        cleanLogscope: '清理類型',\n    },\n    monitor: {\n        globalFilter: '全域過濾',\n        enableMonitor: '監控狀態',\n        storeDays: '保存天數',\n        defaultNetwork: '預設網卡',\n        defaultNetworkHelper: '預設監控和概覽介面顯示的網卡選項',\n        defaultIO: '預設磁碟',\n        defaultIOHelper: '預設監控和概覽介面顯示的磁碟選項',\n        cleanMonitor: '清空監控記錄',\n        cleanHelper: '該操作將清空包括 GPU 在內的所有監控記錄，是否繼續？',\n        avgLoad: '平均負載',\n        loadDetail: '負載詳情',\n        resourceUsage: '資源使用率',\n        networkCard: '網卡',\n        read: '讀取',\n        write: '寫入',\n        readWriteCount: '讀寫次數',\n        readWriteTime: '讀寫延遲',\n        today: '今天',\n        yesterday: '昨天',\n        lastNDay: '近 {0} 天',\n        lastNMonth: '近 {0} 月',\n        lastHalfYear: '近半年',\n        memory: '記憶體',\n        percent: '佔比',\n        cache: '快取',\n        disk: '磁碟',\n        network: '網路',\n        up: '上行',\n        down: '下行',\n        interval: '採集間隔',\n        intervalHelper: '請輸入合適的監控採集時間間隔（10秒 - 12小時）',\n    },\n    terminal: {\n        local: '本機',\n        defaultConn: '預設連接',\n        defaultConnHelper: '該操作將【{0}】開啟終端後自動連線到所在節點終端，是否繼續？',\n        withReset: '重置連線資訊',\n        localConnJump: '預設連線資訊在【終端 - 設定】中維護，連線失敗可前往編輯',\n        localHelper: 'local 名稱僅用於系統本機標識',\n        connLocalErr: '無法自動認證，請填寫本機伺服器的登入資訊',\n        testConn: '連接測試',\n        saveAndConn: '儲存並連接',\n        connTestOk: '連接資訊可用',\n        connTestFailed: '連接不可用，請檢查連接資訊',\n        host: '主機',\n        createConn: '建立連接',\n        noHost: '暫無主機',\n        groupChange: '切換分組',\n        expand: '全部展開',\n        fold: '全部收縮',\n        batchInput: '批次輸入',\n        quickCommand: '快速指令',\n        noSuchCommand: '匯入的CSV檔案中未能發現快速指令資料，請檢查後重試',\n        quickCommandHelper: '常用命令列表，用於在終端介面底部快速選擇',\n        groupDeleteHelper: '移除組後，組內所有連接將遷移到 default 組內，是否繼續？',\n        command: '指令',\n        addHost: '新增主機',\n        localhost: '本機伺服器',\n        ip: '主機位址',\n        authMode: '認證方式',\n        passwordMode: '密碼認證',\n        rememberPassword: '記住認證資訊',\n        keyMode: '私鑰認證',\n        key: '私鑰',\n        keyPassword: '私鑰密碼',\n        emptyTerminal: '暫無終端連接',\n        lineHeight: '字體行高',\n        letterSpacing: '字體間距',\n        fontSize: '字體大小',\n        fontFamily: '字體集',\n        fontFamilySupportHelper:\n            '可選擇或輸入字體，是否生效取決於瀏覽器所在系統是否已安裝該字體，未安裝會自動回退；為空則使用預設字體',\n        backgroundColor: '背景色',\n        foregroundColor: '前景色',\n        cursorBlink: '游標閃爍',\n        cursorStyle: '游標樣式',\n        cursorUnderline: '下劃線',\n        cursorBlock: '塊狀',\n        cursorBar: '條形',\n        scrollback: '滾動行數',\n        scrollSensitivity: '滾動速度',\n        aiStatus: 'AI 終端',\n        aiSettings: 'AI 終端設定',\n        aiAccountHelper: '使用所選模型帳號生成並回填命令。Ollama、vLLM 本地模型請使用自訂模型帳號。',\n        aiPrefix: '觸發前綴',\n        aiPrefixHelper: '以該前綴開頭並按下 Enter 時，會觸發 AI 指令生成，例如 # 或 //ai',\n        aiRiskCommands: '風險命令攔截',\n        aiRiskCommandsHelper: '命中以下片段的生成命令會被攔截，並以註解形式回填，支援增刪改',\n        aiAddRiskCommand: '新增風險命令',\n        aiRemoveRiskCommand: '刪除',\n        aiSummary: '以 {0} 前綴開頭並按下 Enter 時，會觸發 AI 命令生成',\n        aiPrefixAsciiVisible: '僅支援 ASCII 可見字元，不支援空格、中文或全形符號',\n        saveHelper: '是否確認儲存目前終端設定？',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: '部分安裝和使用問題，可參考',\n        },\n        swap: {\n            swap: 'Swap',\n            swapHelper1: 'Swap 的大小應該是物理記憶體的 1 到 2 倍，可根據詳細情況進行調整；',\n            swapHelper2: '在建立 Swap 檔案之前，請確保系統硬碟有足夠的可用空間，Swap 檔案的大小將佔用相應的磁碟空間；',\n            swapHelper3:\n                'Swap 可以幫助紓解記憶體壓力，但僅是一個備選項，過多依賴可能導致系統效能下降，建議優先考慮增加記憶體或最佳化應用程式記憶體使用；',\n            swapHelper4: '建議定期監控 Swap 的使用情況，以確保系統正常執行。',\n            swapDeleteHelper: '此操作將移除 Swap 分區 {0}，出於系統安全考慮，不會自動刪除該檔案，如需刪除請手動操作',\n            saveHelper: '請先儲存目前設定',\n            saveSwap: '儲存目前設定將調整 Swap 分區 {0} 大小到 {1}，是否繼續？',\n            swapMin: '分區大小最小值為 40 KB，請修改後重試',\n            swapMax: '分區大小最大值為 {0}，請修改後重試',\n            swapOff: '分區大小最小值為 40 KB，設定為 0 則關閉 Swap 分區。',\n        },\n        device: {\n            dnsHelper: '伺服器地址域名解析',\n            dnsAlert: '請注意修改 /etc/resolv.conf 檔案的設定時，重新啟動系統後會將檔案復原為預設值',\n            dnsHelper1: '當存在多個DNS時，需換行顯示，例：\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: '主機名稱解析',\n            hosts: '域名',\n            hostAlert: '隱藏了已註釋的紀錄，請點選 全部設定 按鈕以檢視或設定',\n            toolbox: '快速設定',\n            hostname: '主機名稱',\n            passwd: '系統密碼',\n            passwdHelper: '輸入的字元不能包含 $ 和 &',\n            timeZone: '系統時區',\n            localTime: '伺服器時間',\n            timeZoneChangeHelper: '修改系統時區需要重新啟動服務，是否繼續？',\n            timeZoneHelper: '時區修改依賴於 timedatectl 指令，如未安裝可能導致修改失敗',\n            timeZoneCN: '北京',\n            timeZoneAM: '洛杉磯',\n            timeZoneNY: '紐約',\n            ntpALi: '阿里',\n            ntpGoogle: 'Google',\n            syncSite: 'NTP 伺服器',\n            syncSiteHelper: '該操作將使用 {0} 作為源進行系統時間同步，是否繼續？',\n            hostnameHelper: '主機名稱修改依賴於 hostnamectl 指令，如未安裝可能導致修改失敗',\n            userHelper: '使用者名稱依賴於 whoami 指令取得，如未安裝可能導致取得失敗。',\n            passwordHelper: '密碼修改依賴於 chpasswd 指令，如未安裝可能導致修改失敗',\n            hostHelper: '填寫的內容中存在空值，請檢查修改後重試',\n            dnsCheck: '測試可用性',\n            dnsOK: 'DNS 設定資訊可用',\n            dnsTestFailed: 'DNS 設定資訊不可用，請修改後重試',\n        },\n        fail2ban: {\n            sshPort: '監聽 SSH 埠',\n            sshPortHelper: '目前 Fail2ban 監聽主機 SSH 連接埠',\n            unActive: '尚未啟用 Fail2ban 服務，請先啟用',\n            operation: '對 Fail2ban 服務進行 [{0}] 操作，是否繼續？',\n            fail2banChange: 'Fail2ban 設定修改',\n            ignoreHelper: '白名單中的 IP 列表將被忽略封鎖，是否繼續？',\n            bannedHelper: '黑名單中的 IP 列表將被伺服器封鎖，是否繼續？',\n            maxRetry: '最大重試次數',\n            banTime: '停用時間',\n            banTimeHelper: '預設停用時間為 10 分鐘，停用時間為 -1 則表示永久停用',\n            banTimeRule: '請輸入正確的停用時間或 -1',\n            banAllTime: '永久停用',\n            findTime: '發現週期',\n            banAction: '停用方式',\n            banActionOption: '通過 {0} 來停用指定的 IP 位址',\n            allPorts: ' (所有埠)',\n            ignoreIP: 'IP 白名單',\n            bannedIP: 'IP 黑名單',\n            logPath: '日誌路徑',\n            logPathHelper: '預設為 /var/log/secure 或 /var/log/auth.log',\n        },\n        ftp: {\n            ftp: 'FTP 帳戶',\n            notStart: '尚未啟用 FTP 服務，請先啟用',\n            operation: '對 FTP 服務進行 [{0}] 操作，是否繼續？',\n            noPasswdMsg: '無法取得目前 FTP 帳號密碼，請先設定密碼後重試',\n            enableHelper: '啟用選取的 FTP 帳號後，該 FTP 帳號將復原存取權限，是否繼續？',\n            disableHelper: '停用選取的 FTP 帳號後，該 FTP 帳號將失去存取權限，是否繼續？',\n            syncHelper: '同步伺服器與資料庫中的 FTP 帳戶資料，是否繼續？',\n            dirSystem: '該目錄為系統保留目錄，修改可能導致系統崩潰，請修改後重試',\n            dirHelper: '開啟 FTP 需要修改目錄權限，請謹慎選擇',\n            dirMsg: '開啟 FTP 將修改整個 {0} 目錄權限，是否繼續？',\n        },\n        clam: {\n            clam: '病毒掃描',\n            cron: '定時掃描',\n            cronHelper: '專業版支援定時掃描功能',\n            specErr: '執行週期格式錯誤，請檢查後重試',\n            disableMsg: '停止定時執行會導致該掃描任務不再自動執行。是否繼續？',\n            enableMsg: '啟用定時執行會讓該掃描任務定期自動執行。是否繼續？',\n            showFresh: '顯示病毒庫服務',\n            hideFresh: '隱藏病毒庫服務',\n            clamHelper: 'ClamAV 建議最低設定：3 GiB 以上記憶體、單核 2.0 GHz 以上 CPU，以及至少 5 GiB 可用硬碟空間。',\n            notStart: '尚未啟用 ClamAV 服務，請先啟用',\n            removeRecord: '刪除報告檔案',\n            noRecords: '點選「執行」按鈕開始掃描，掃描結果將會記錄在這裡。',\n            removeInfected: '刪除病毒檔案',\n            removeInfectedHelper: '刪除任務偵測到的病毒檔案，以確保伺服器的安全和正常執行。',\n            clamCreate: '建立掃描規則',\n            infectedStrategy: '感染檔案策略',\n            removeHelper: '刪除病毒檔案，請謹慎選擇',\n            move: '移動',\n            moveHelper: '將病毒檔案移動到指定目錄下',\n            copyHelper: '將病毒檔案複製到指定目錄下',\n            none: '不操作',\n            noneHelper: '不對病毒檔案採取任何操作',\n            scanDir: '掃描目錄',\n            infectedDir: '隔離目錄',\n            scanDate: '掃描時間',\n            scanResult: '掃描報告條數',\n            tail: '日誌顯示行數',\n            infectedFiles: '感染檔案數',\n            log: '詳情',\n            clamConf: '掃描設定',\n            clamLog: '掃描日誌',\n            freshClam: '病毒庫重新整理設定',\n            freshClamLog: '病毒庫重新整理日誌',\n            alertHelper: '專業版支援定時掃描和簡訊告警功能',\n            alertTitle: '病毒掃描「{0}」任務偵測到感染檔案告警',\n        },\n    },\n    logs: {\n        core: '面板服務',\n        agent: '節點監控',\n        panelLog: '面板日誌',\n        operation: '操作日誌',\n        login: '存取日誌',\n        loginIP: '登入 IP',\n        loginAddress: '登入地址',\n        loginAgent: '使用者代理',\n        loginStatus: '登入狀態',\n        system: '系統日誌',\n        deleteLogs: '清空日誌',\n        resource: '資源',\n        detail: {\n            dashboard: '概覽',\n            ai: 'AI',\n            groups: '分組',\n            hosts: '主機',\n            apps: '應用',\n            openresty: 'OpenResty',\n            websites: '網站',\n            containers: '容器',\n            files: '檔案管理',\n            runtimes: '執行環境',\n            process: '行程管理',\n            toolbox: '工具箱',\n            backups: '備份 / 還原',\n            tampers: '防篡改',\n            xsetting: '介面設定',\n            logs: '日誌審計',\n            settings: '面板設定',\n            cronjobs: '計劃任務',\n            databases: '資料庫',\n            waf: 'WAF',\n            licenses: '許可證',\n            nodes: '節點',\n            commands: '快速指令',\n        },\n        websiteLog: '網站日誌',\n        runLog: '執行日誌',\n        errLog: '錯誤日誌',\n        task: '任務日誌',\n        taskName: '任務名稱',\n        taskRunning: '執行中',\n    },\n    file: {\n        fileDirNum: '共 {0} 個目錄，{1} 個檔案，',\n        currentDir: '當前目錄',\n        dir: '資料夾',\n        upload: '上傳',\n        download: '下載',\n        uploadFile: '@:file.upload@:file.file',\n        uploadDirectory: '@:file.upload@:file.dir',\n        fileName: '檔案名',\n        search: '在當前目錄下尋找',\n        mode: '權限',\n        editPermissions: '編輯@:file.mode',\n        owner: '所有者',\n        file: '檔案',\n        remoteFile: '遠端下載',\n        share: '分享',\n        sync: '資料同步',\n        size: '大小',\n        updateTime: '修改時間',\n        rename: '重新命名',\n        role: '權限',\n        info: '屬性',\n        linkFile: '軟連結檔案',\n        shareList: '分享列表',\n        zip: '壓縮',\n        group: '使用者群組',\n        path: '路徑',\n        public: '公共',\n        setRole: '設定權限',\n        link: '是否連結',\n        rRole: '讀取',\n        wRole: '寫入',\n        xRole: '可執行',\n        compress: '壓縮',\n        deCompress: '解壓',\n        compressType: '壓縮格式',\n        compressDst: '壓縮路徑',\n        replace: '覆蓋已存在的檔案',\n        compressSuccess: '壓縮成功',\n        deCompressSuccess: '解壓成功',\n        deCompressDst: '解壓路徑',\n        linkType: '連結類型',\n        softLink: '軟連結',\n        hardLink: '硬連結',\n        linkPath: '連結路徑',\n        selectFile: '選擇檔案',\n        downloadSuccess: '下載成功',\n        downloadUrl: '下載網址',\n        downloadStart: '下載開始!',\n        moveSuccess: '移動成功',\n        copySuccess: '複製成功',\n        pasteMsg: '請在目標目錄點選右上角【貼上】按鈕',\n        move: '移動',\n        calculate: '計算',\n        remark: '備註',\n        setRemark: '設定備註',\n        remarkPrompt: '請輸入備註內容',\n        remarkPlaceholder: '備註內容',\n        remarkToggle: '備註',\n        remarkToggleTip: '載入檔案備註',\n        canNotDeCompress: '無法解壓此檔案',\n        uploadSuccess: '上傳成功!',\n        downloadProcess: '下載進度',\n        downloading: '正在下載...',\n        infoDetail: '檔案屬性',\n        root: '根目錄',\n        list: '檔案列表',\n        sub: '子目錄',\n        theme: '主題',\n        language: '語言',\n        eol: '行尾符',\n        copyDir: '複製路徑',\n        paste: '貼上',\n        changeOwner: '修改使用者和使用者群組',\n        containSub: '同時修改子檔案屬性',\n        ownerHelper: 'PHP 執行環境預設使用者:使用者群組為 1000:1000, 容器內外使用者顯示不一致為正常現象',\n        searchHelper: '支援 * 等萬用字元',\n        uploadFailed: '【{0}】 檔案上傳失敗',\n        fileUploadStart: '正在上傳【{0}】...',\n        currentSelect: '當前選中: ',\n        unsupportedType: '不支援的檔案類型',\n        deleteHelper: '確定刪除所選檔案？ 預設刪除之後將進入回收站？',\n        fileHelper: '注意：1. 搜尋結果不支援排序功能 2. 資料夾無法依大小排序。',\n        forceDeleteHelper: '永久刪除檔案（不進入回收站，直接刪除）',\n        recycleBin: '回收站',\n        sourcePath: '原路徑',\n        deleteTime: '刪除時間',\n        reduce: '還原',\n        confirmReduce: '確定還原以下檔案？',\n        reduceSuccess: '還原成功',\n        reduceHelper: '如果原路徑存在同名檔案或目錄，將會被覆蓋，是否繼續？',\n        clearRecycleBin: '清空回收站',\n        clearRecycleBinHelper: '是否清空回收站？',\n        favorite: '收藏夾',\n        removeFavorite: '是否從收藏夾移出？',\n        addFavorite: '新增/移出收藏夾',\n        clearList: '清空列表',\n        deleteRecycleHelper: '確定永久刪除以下檔案？',\n        typeErrOrEmpty: '【{0}】 檔案類型錯誤或為空資料夾',\n        dropHelper: '將需要上傳的檔案拖曳到此處',\n        fileRecycleBin: '檔案回收站',\n        fileRecycleBinMsg: '已{0}回收站',\n        wordWrap: '自動換行',\n        deleteHelper2: '確定刪除所選檔案？ 刪除操作不可回滾',\n        ignoreCertificate: '忽略不可信憑證',\n        ignoreCertificateHelper:\n            '下載時忽略不可信憑證可能導致資料洩露或篡改。請謹慎使用此選項，僅在信任下載源的情況下啟用',\n        uploadOverLimit: '檔案數量超過 1000請壓縮後上傳',\n        clashDidNotSupport: '檔名禁止包含 .1panel_clash',\n        clashDeleteAlert: '回收站資料夾不能刪除',\n        clashOpenAlert: '回收站目錄請點選【回收站】按鈕開啟',\n        right: '前進',\n        back: '後退',\n        top: '返回上一層',\n        up: '上一層',\n        openWithVscode: 'VS Code 開啟',\n        vscodeHelper: '請確保本機已安裝 VS Code 並設定了 SSH Remote 外掛程式',\n        saveContentAndClose: '檔案已被修改，是否儲存並關閉？',\n        saveAndOpenNewFile: '檔案已被修改，是否儲存並開啟新檔案？',\n        noEdit: '檔案未修改，無需此操作',\n        noNameFolder: '未命名資料夾',\n        noNameFile: '未命名檔案',\n        minimap: '縮圖',\n        fileCanNotRead: '此檔案不支援預覽',\n        previewTruncated: '檔案過大，僅顯示末尾內容',\n        previewEmpty: '檔案內容為空或不是文字檔案',\n        previewLargeFile: '預覽',\n        panelInstallDir: '1Panel 安裝目錄不能刪除',\n        wgetTask: '下載任務',\n        existFileTitle: '同名檔案提示',\n        existFileHelper: '上傳的檔案存在同名檔案，是否覆蓋？',\n        existFileSize: '檔案大小（新->舊）',\n        existFileDirHelper: '選擇的檔案/資料夾存在同名，請謹慎操作',\n        coverDirHelper: '選取要覆蓋的資料夾，將複製到目標路徑',\n        noSuchFile: '找不到該檔案或目錄，請檢查後重試。',\n        setting: '設定',\n        showHide: '顯示隱藏檔案',\n        noShowHide: '不顯示隱藏檔案',\n        cancelUpload: '取消上傳',\n        cancelUploadHelper: '是否取消上傳，取消後將清空上傳列表',\n        keepOneTab: '至少保留一個分頁',\n        notCanTab: '無法新增更多分頁',\n        convert: '檔案格式轉換',\n        converting: '轉換為',\n        fileCanNotConvert: '此檔案不支援轉換格式',\n        formatType: '格式類型',\n        sourceFormat: '來源格式',\n        sourceFile: '來源檔案',\n        saveDir: '儲存目錄',\n        deleteSourceFile: '是否刪除來源檔案',\n        convertHelper: '將選中的檔案轉換為其他格式',\n        convertHelper1: '請選擇需要轉換格式的檔案',\n        execConvert: '開始轉換，可在任務中心檢視轉換日誌',\n        convertLogs: '轉換日誌',\n        formatConvert: '格式轉換',\n    },\n    ssh: {\n        autoStart: '開機自啟',\n        enable: '設定開機自啟',\n        disable: '關閉開機自啟',\n        sshAlert: '列表資料根據登入時間排序，但請注意，切換時區或其他操作可能導致登入日誌的時間出現偏差。',\n        sshAlert2: '您可以透過工具箱中的 Fail2ban 封鎖嘗試暴力破解的 IP 位址，從而提高主機的安全性。',\n        sshOperate: '對 SSH 服務進行 [{0}] 操作，是否繼續？',\n        sshChange: 'SSH 設定修改',\n        sshChangeHelper: '此操作將 {0} 修改為 [{1}] ，是否繼續？',\n        sshFileChangeHelper: '直接修改設定檔可能會導致服務不可用，請謹慎操作，是否繼續？',\n        port: '連接埠',\n        portHelper: '指定 SSH 服務監聽的埠號，預設為 22。',\n        listenAddress: '監聽地址',\n        allV4V6: '0.0.0.0:{0}(IPv4)和 :::{0}(IPv6)',\n        listenHelper: '同時取消 IPv4 和 IPv6 設定，將會同時監聽 0.0.0.0:{0}(IPv4) 和 :::{0}(IPv6)',\n        addressHelper: '指定 SSH 服務監聽的 IP 位址',\n        permitRootLogin: 'root 使用者',\n        rootSettingHelper: 'root 使用者 SSH 登入方式，預設所有 SSH 登入。',\n        rootHelper1: '允許 SSH 登入',\n        rootHelper2: '禁止 SSH 登入',\n        rootHelper3: '僅允許金鑰登入',\n        rootHelper4: '僅允許執行預先定義的指令，不能進行其他操作',\n        passwordAuthentication: '密碼認證',\n        pwdAuthHelper: '是否啟用密碼認證，預設啟用。',\n        pubkeyAuthentication: '金鑰認證',\n        privateKey: '私鑰',\n        publicKey: '公鑰',\n        password: '密碼',\n        createMode: '建立方式',\n        generate: '自動生成',\n        unSyncPass: '金鑰密碼無法同步',\n        syncHelper: '同步操作將清理失效金鑰並同步新增的完整金鑰對，是否繼續？',\n        input: '手動輸入',\n        import: '檔案上傳',\n        authKeys: '授權金鑰',\n        authKeysHelper: '是否儲存目前公鑰資訊？',\n        pubkey: '金鑰資訊',\n        pubKeyHelper: '目前金鑰資訊僅對使用者 {0} 生效',\n        encryptionMode: '加密方式',\n        passwordHelper: '支援大小寫英文、數字,長度6-10',\n        reGenerate: '重新生成金鑰',\n        keyAuthHelper: '是否啟用金鑰認證，預設啟用。',\n        useDNS: '反向解析',\n        dnsHelper: '控制 SSH 伺服器是否啟用 DNS 解析功能，從而驗證連接方的身份。',\n        analysis: '統計資訊',\n        denyHelper: '將對下列地址進行【封鎖】操作，設定後該 IP 將禁止存取伺服器，是否繼續？',\n        acceptHelper: '將對下列地址進行【放行】操作，設定後該 IP 將復原正常存取，是否繼續？',\n        noAddrWarning: '目前未選中任何可【{0}】地址，請檢查後重試',\n        loginLogs: 'SSH 日誌',\n        loginMode: '登入方式',\n        authenticating: '金鑰',\n        publickey: '金鑰',\n        belong: '歸屬地',\n        local: '內網',\n        remote: '外網',\n        session: '工作階段',\n        loginTime: '登入時間',\n        loginIP: '登入IP',\n        stopSSHWarn: '是否斷開此SSH連接',\n    },\n    setting: {\n        panel: '面板',\n        user: '面板使用者',\n        userChange: '修改面板使用者',\n        userChangeHelper: '修改面板使用者將登出，是否繼續？',\n        passwd: '面板密碼',\n        emailHelper: '用於密碼找回',\n        watermark: '浮水印設定',\n        watermarkContent: '浮水印內容',\n        contentHelper: '{0} 表示節點名稱，{1} 表示節點地址，既可以使用變數，也可以填寫自訂名稱。',\n        watermarkColor: '浮水印顏色',\n        watermarkFont: '浮水印字型大小',\n        watermarkHeight: '浮水印高度',\n        watermarkWidth: '浮水印寬度',\n        watermarkRotate: '旋轉角度',\n        watermarkGap: '間距',\n        watermarkCloseHelper: '是否確認關閉系統浮水印設定？',\n        watermarkOpenHelper: '是否確認儲存目前系統浮水印設定？',\n        title: '面板別名',\n        titleHelper: '支援長度 3 至 30 的英文字母、中文、數字、空格和常見的特殊符號。',\n        panelPort: '面板埠',\n        portHelper: '建議埠範圍8888 - 65535，注意：有安全組的伺服器請提前在安全組放行新埠',\n        portChange: '埠修改',\n        portChangeHelper: '服務埠修改需要重新啟動服務，是否繼續？',\n        theme: '主題顏色',\n        menuTabs: '選單分頁',\n        componentSize: '元件大小',\n        dark: '暗色',\n        darkGold: '黑金',\n        light: '亮色',\n        auto: '跟隨系統',\n        language: '系統語言',\n        runtimeEnv: '運行環境',\n        docSource: '文件來源',\n        withByRegion: '跟隨運行區域（預設）',\n        withByLang: '跟隨系統語言',\n        region: '運行區域',\n        cn: '中國大陸',\n        intl: '全球',\n        regionHelper: '變更運行區域後，將調整以下資源來源：',\n        regionHelper1: '系統安裝套件',\n        regionHelper2: '應用商店與腳本庫',\n        regionHelper3: '使用手冊及相關文件',\n        regionHelper4: '此操作可能影響後續資源下載與存取，請謹慎操作。',\n        regionTip: '運行區域影響系統更新源以及安裝包下載地址。',\n        docSourceTip: '文件來源決定文件與更新日誌的跳轉語言。',\n        languageHelper: '預設跟隨瀏覽器語言，設定後只對目前瀏覽器生效，更換瀏覽器後需要重新設定',\n        sessionTimeout: '逾時時間',\n        sessionTimeoutError: '最小逾時時間為 300 秒',\n        sessionTimeoutHelper: '如果使用者超過 {0} 秒未操作面板，面板將自動登出',\n        systemIP: '預設存取地址',\n        systemIPHelper: '應用跳轉、容器存取等功能將使用此地址進行路由。每個節點可設定不同地址。',\n        proxy: '代理伺服器',\n        proxyHelper: '設定代理伺服器後，將在以下場景中生效：',\n        proxyHelper1: '應用商店的安裝包下載和同步（專業版功能）',\n        proxyHelper2: '系統版本升級及取得更新說明（專業版功能）',\n        proxyHelper3: '系統許可證的驗證和同步',\n        proxyHelper4: 'Docker 拉取映像等網路請求將透過代理伺服器存取（專業版功能）',\n        proxyHelper5: '系統類型腳本庫的統一下載與同步（專業版功能）',\n        proxyHelper6: '申請憑證（專業版功能）',\n        proxyType: '代理類型',\n        proxyUrl: '代理地址',\n        proxyPort: '代理埠',\n        proxyPasswdKeep: '記住密碼',\n        proxyDocker: 'Docker 代理',\n        proxyDockerHelper: '將代理伺服器設定同步至 Docker，支援離線伺服器拉取映像等操作',\n        syncToNode: '同步至子節點',\n        syncToNodeHelper: '同步設定至其他節點',\n        nodes: '節點',\n        selectNode: '選擇節點',\n        selectNodeError: '請選擇節點',\n        apiInterface: 'API 介面',\n        apiInterfaceClose: '關閉後將不能使用 API 介面進行存取，是否繼續？',\n        apiInterfaceHelper: '提供面板支援 API 介面存取',\n        apiInterfaceAlert1: '請不要在生產環境開啟，這可能新增伺服器安全風險',\n        apiInterfaceAlert2: '請不要使用協力廠商應用呼叫面板 API，以防止潛在的安全威脅。',\n        apiInterfaceAlert3: 'API 介面檔案',\n        apiInterfaceAlert4: '使用檔案',\n        apiKey: '介面金鑰',\n        apiKeyHelper: '介面金鑰用於外部應用存取 API 介面',\n        ipWhiteList: 'IP白名單',\n        ipWhiteListEgs: '當存在多個 IP 時，需要換行顯示，例：\\n172.16.10.111 \\n172.16.10.0/24',\n        ipWhiteListHelper:\n            '必需在 IP 白名單清單中的 IP 才能存取面板 API 介面，0.0.0.0/0（所有 IPv4），::/0（所有 IPv6）',\n        apiKeyValidityTime: '介面金鑰有效期',\n        apiKeyValidityTimeEgs: '介面金鑰有效期（組織分）',\n        apiKeyValidityTimeHelper: '介面時間戳記到請求時的目前時間戳之間有效（組織分），設定為0時，不做時間戳記校驗',\n        apiKeyReset: '介面金鑰重設',\n        apiKeyResetHelper: '重設金鑰後，已關聯金鑰服務將失效，請重新新增新金鑰至服務。',\n        confDockerProxy: '設定 Docker 代理',\n        restartNowHelper: '設定 Docker 代理需要重新啟動 Docker 服務。',\n        restartNow: '立即重新啟動',\n        restartLater: '稍後手動重新啟動',\n        systemIPWarning: '當前節點尚未設定預設存取地址，請前往面板設定進行設定',\n        systemIPWarning1: '當前伺服器地址為 {0}，無法快速跳轉',\n        changePassword: '密碼修改',\n        oldPassword: '原密碼',\n        newPassword: '新密碼',\n        retryPassword: '確認密碼',\n        noSpace: '輸入資訊不能包括空格符號',\n        duplicatePassword: '新密碼不能與原始密碼一致，請重新輸入',\n        diskClean: '快取清理',\n        developerMode: '預覽體驗計劃',\n        developerModeHelper: '取得 1Panel 的預覽版本，以分享有關新功能和更新的回饋',\n        thirdParty: '第三方帳號',\n        scope: '使用範圍',\n        public: '公有',\n        publicHelper: '公有類型的備份帳號會同步到各個子節點，子節點可以一起使用',\n        private: '私有',\n        privateHelper: '私有類型的備份帳號只建立在目前節點上，僅供目前節點使用',\n        noTypeForCreate: '目前無可建立備份類型',\n        LOCAL: '伺服器磁碟',\n        OSS: '阿里雲 OSS',\n        S3: '亞馬遜 S3 雲端儲存',\n        mode: '模式',\n        MINIO: 'MINIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'WebDAV 連接 Alist 參考官方文件',\n        UPYUN: '又拍雲',\n        ALIYUN: '阿里雲端硬碟',\n        ALIYUNHelper: '目前阿里雲端硬碟非用戶端下載最大限制為 100 MB，超過限制需要透過用戶端下載',\n        ALIYUNRecover:\n            '目前阿里雲端硬碟非用戶端下載最大限制為 100 MB，超過限制需要透過用戶端下載到本機後，同步快照進行復原',\n        GoogleDrive: 'Google雲端硬碟',\n        analysis: '解析',\n        analysisHelper: '貼上整個 token 內容，自動解析所需部分，詳細操作參考官方文件',\n        serviceName: '服務名稱',\n        operator: '操作員',\n        OneDrive: '微軟 OneDrive',\n        isCN: '世紀互聯',\n        isNotCN: '國際版',\n        client_id: '用戶端 ID',\n        client_secret: '用戶端金鑰',\n        redirect_uri: '重定向 URL',\n        onedrive_helper: '自訂設定請參考官方文件',\n        clickToRefresh: '單擊可手動重新整理',\n        refreshTime: '令牌重新整理時間',\n        refreshStatus: '令牌重新整理狀態',\n        codeWarning: '目前授權碼格式錯誤，請重新確認',\n        backupDir: '備份目錄',\n        code: '授權碼',\n        codeHelper:\n            '請點選取得按鈕，然後登入 {0} 複製跳轉連結中 code 後面的內容，貼到該輸入框中，詳細操作參考官方文件。',\n        googleHelper: '請先建立Google應用並取得用戶端資訊，填寫表單後點選取得按鈕，詳細操作參考官方文件。',\n        loadCode: '取得',\n        COS: '騰訊雲 COS',\n        ap_beijing_1: '北京一區',\n        ap_beijing: '北京',\n        ap_nanjing: '南京',\n        ap_shanghai: '上海',\n        ap_guangzhou: '廣州',\n        ap_chengdu: '成都',\n        ap_chongqing: '重慶',\n        ap_shenzhen_fsi: '深圳金融',\n        ap_shanghai_fsi: '上海金融',\n        ap_beijing_fsi: '北京金融',\n        ap_hongkong: '中國香港',\n        ap_singapore: '新加坡',\n        ap_mumbai: '孟買',\n        ap_jakarta: '雅加達',\n        ap_seoul: '首爾',\n        ap_bangkok: '曼谷',\n        ap_tokyo: '東京',\n        na_siliconvalley: '矽谷（美西）',\n        na_ashburn: '維吉尼亞（美東）',\n        na_toronto: '多倫多',\n        sa_saopaulo: '聖保羅',\n        eu_frankfurt: '法蘭克福',\n        KODO: '七牛雲 Kodo',\n        scType: '儲存類型',\n        typeStandard: '標準儲存',\n        typeStandard_IA: '低頻儲存',\n        typeArchive: '歸檔儲存',\n        typeDeep_Archive: '深度歸檔儲存',\n        scLighthouse: '預設，輕量物件儲存僅支援該儲存類型',\n        scStandard: '標準儲存，適用於即時存取的大量熱點檔案、頻繁的資料互動等業務場景。',\n        scStandard_IA: '低頻儲存，適用於較低存取頻率（例如平均每月存取頻率1到2次）的業務場景，最少儲存30天。',\n        scArchive: '歸檔儲存，適用於極低存取頻率（例如半年存取1次）的業務場景。',\n        scDeep_Archive: '深度歸檔儲存，適用於極低存取頻率（例如1年存取1~2次）的業務場景。',\n        archiveHelper: '歸檔儲存的檔案無法直接下載，需要先在對應的雲服務商網站進行復原操作，請謹慎使用',\n        backupAlert: '理論上只要雲廠商相容 S3 協議，就可以用現有的亞馬遜 S3 雲端儲存來備份，詳細設定請參考 ',\n        domain: '加速域名',\n        backupAccount: '備份帳號',\n        loadBucket: '取得桶',\n        accountName: '帳戶名稱',\n        accountKey: '帳戶金鑰',\n        address: '地址',\n        path: '路徑',\n        backupJump: '未在目前備份列表中的備份檔案，請嘗試從檔案目錄中下載後匯入備份。',\n        snapshot: '快照',\n        noAppData: '暫無可選擇系統應用',\n        noBackupData: '暫無可選擇備份資料',\n        stepBaseData: '基礎資料',\n        stepAppData: '系統應用',\n        stepPanelData: '系統資料',\n        stepBackupData: '備份資料',\n        stepOtherData: '其他資料',\n        operationLog: '保留操作日誌',\n        loginLog: '保留存取日誌',\n        systemLog: '保留系統日誌',\n        taskLog: '保留任務日誌',\n        monitorData: '保留監控資料',\n        dockerConf: '保留 Docker 設定',\n        selectAllImage: '備份所有應用映像',\n        logLabel: '日誌',\n        agentLabel: '節點設定',\n        appDataLabel: '應用資料',\n        appImage: '應用映像',\n        appBackup: '應用備份',\n        backupLabel: '備份目錄',\n        confLabel: '設定檔',\n        dockerLabel: '容器',\n        taskLabel: '計劃任務',\n        resourceLabel: '應用資源目錄',\n        runtimeLabel: '執行環境',\n        appLabel: '應用',\n        databaseLabel: '資料庫',\n        snapshotLabel: '快照檔案',\n        websiteLabel: '網站',\n        directoryLabel: '目錄',\n        appStoreLabel: '應用商店',\n        shellLabel: '腳本',\n        tmpLabel: '暫存資料夾',\n        sslLabel: '憑證目錄',\n        reCreate: '建立快照失敗',\n        reRollback: '回滾快照失敗',\n        deleteHelper: '將刪除該快照的所有備份檔案，包括第三方備份帳號中的檔案。',\n        ignoreRule: '排除規則',\n        editIgnoreRule: '@:commons.button.edit@:setting.ignoreRule',\n        ignoreHelper: '快照時將使用該規則對 1Panel 資料目錄進行壓縮備份，請謹慎修改。',\n        ignoreHelper1: '一行一個，例： \\n*.log\\n/opt/1panel/cache',\n        status: '快照狀態',\n        panelInfo: '寫入 1Panel 基礎資訊',\n        panelBin: '備份 1Panel 系統檔案',\n        daemonJson: '備份 Docker 設定檔',\n        appData: '備份 1Panel 已安裝應用',\n        panelData: '備份 1Panel 資料目錄',\n        backupData: '備份 1Panel 本機備份目錄',\n        compress: '製作快照檔案',\n        upload: '上傳快照檔案',\n        recoverDetail: '復原詳情',\n        recoverFailed: '快照復原失敗',\n        importHelper: '快照檔案目錄：',\n        lastRecoverAt: '上次復原時間',\n        lastRollbackAt: '上次回滾時間',\n        reDownload: '重新下載備份檔案',\n        recoverErrArch: '不支援在不同伺服器架構之間進行快照復原操作!',\n        recoverErrSize: '偵測到目前磁碟空間不足，請檢查或清理後重試!',\n        recoverHelper: '即將從快照 {0} 開始復原，復原前請確認以下資訊：',\n        recoverHelper1: '復原需要重新啟動 Docker 以及 1Panel 服務',\n        recoverHelper2: '請確保伺服器磁碟空間充足 ( 快照檔案大小: {0}, 可用空間: {1} )',\n        recoverHelper3: '請確保伺服器架構與建立快照伺服器架構資訊保持一致 (目前伺服器架構: {0} )',\n        rollback: '回滾',\n        rollbackHelper:\n            '即將回滾本次復原，回滾將取代所有本次復原的檔案，過程中可能需要重新啟動 Docker 以及 1Panel 服務，是否繼續？',\n        upgradeRecord: '升級記錄',\n        upgrading: '正在升級中，請稍候',\n        upgradeHelper: '升級操作需要重新啟動 1Panel 服務，是否繼續？',\n        noUpgrade: '已是最新版本',\n        versionHelper: '1Panel 版本號命名規則為： [大版本].[功能版本].[Bug 修復版本]，例：',\n        rollbackLocalHelper: '主節點暫不支援直接回滾，請手動執行「1pctl restore」指令回滾',\n        upgradeCheck: '檢查更新',\n        upgradeNotes: '更新內容',\n        upgradeNow: '立即更新',\n        source: '下載源',\n        versionNotSame: '節點版本與主節點不一致，請在節點管理中升級後重試。',\n        versionCompare: '偵測到節點 {0} 版本已是目前可升級最新版本，請檢查主節點版本後重試',\n        safe: '安全',\n        passkey: '通行金鑰',\n        passkeyManage: '管理',\n        passkeyKeyManagement: '密鑰管理',\n        passkeyHelper: '用於快速登入，最多可綁定 5 個通行金鑰',\n        passkeyRequireSSL: '需設定網域綁定並透過 HTTPS 存取後才能使用通行金鑰',\n        passkeyTrustedProxies: '可信代理',\n        passkeyTrustedProxiesHelper:\n            '僅當請求來源命中以下 IP/CIDR 時，才會信任 Forwarded 與 X-Forwarded-Proto 判斷 HTTPS',\n        passkeyNotSupported: '目前瀏覽器或環境不支援通行金鑰，請確認網域與 HTTPS',\n        passkeyPrereqTitle: '使用通行金鑰需滿足以下前置條件',\n        passkeyPrereqBindDomain: '已設定 1Panel 網域綁定',\n        passkeyPrereqHttps: '已透過 HTTPS 安全存取',\n        passkeyPrereqBrowser: '目前環境支援 Passkey（WebAuthn + 平台驗證器）',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable: '請使用系統瀏覽器存取，並確認已綁定網域且透過 HTTPS 開啟面板',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            '目前環境無法使用裝置認證，請改用系統瀏覽器並確認系統版本較新',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator:\n            '請先在系統中啟用螢幕鎖定/指紋/臉部辨識/Windows Hello 後重試',\n        passkeyPrereqBrowserDetailDetectFailed: '環境檢測失敗，請重新整理後重試，或改用系統瀏覽器存取',\n        passkeyPrereqGoSetup: '去設定',\n        passkeyCount: '已綁定 {0}/{1}',\n        passkeyName: '名稱',\n        passkeyNameHelper: '請輸入用於區分裝置的名稱',\n        passkeyAdd: '新增通行金鑰',\n        passkeyCreatedAt: '建立時間',\n        passkeyLastUsedAt: '最近使用',\n        passkeyDeleteConfirm: '刪除後將無法使用此通行金鑰登入，是否繼續？',\n        passkeyLimit: '最多可綁定 5 個通行金鑰',\n        passkeyFailed: '通行金鑰註冊失敗，請確認面板SSL憑證是否為可信憑證',\n        bindInfo: '監聽地址',\n        bindAll: '監聽所有',\n        bindInfoHelper: '修改服務監聽地址或協議可能導致服務不可用，是否繼續？',\n        ipv6: '監聽 IPv6',\n        bindAddress: '監聽地址',\n        entrance: '安全入口',\n        showEntrance: '啟用概覽頁未開啟提醒',\n        entranceHelper: '開啟安全入口後只能透過指定安全入口登入面板',\n        entranceError: '請輸入 5-116 位安全登入入口，僅支援輸入數字或字母',\n        entranceInputHelper: '安全入口設定為空時，則取消安全入口',\n        randomGenerate: '隨機生成',\n        expirationTime: '密碼過期時間',\n        unSetting: '未設定',\n        noneSetting: '為面板密碼設定過期時間，過期後需要重新設定密碼',\n        expirationHelper: '密碼過期時間為 [0] 天時，則關閉密碼過期功能',\n        days: '過期天數',\n        expiredHelper: '目前密碼已過期，請重新修改密碼：',\n        timeoutHelper: '【 {0} 天後 】面板密碼即將過期，過期後需要重新設定密碼',\n        complexity: '密碼複雜度驗證',\n        complexityHelper: '開啟後密碼必須滿足長度為 8-30 位，並包含字母、數字、至少兩種特殊字元',\n        bindDomain: '網域綁定',\n        unBindDomain: '域名解綁',\n        panelSSL: '面板 SSL',\n        unBindDomainHelper: '解除網域綁定可能造成系統不安全，是否繼續？',\n        bindDomainHelper: '設定網域綁定後，僅能透過設定中域名存取 1Panel 服務',\n        bindDomainHelper1: '綁定域名為空時，則取消網域綁定',\n        bindDomainWarning: '設定網域綁定後，將退出目前登入，且僅能透過設定中域名存取 1Panel 服務，是否繼續？',\n        allowIPs: '授權 IP',\n        unAllowIPs: '取消授權',\n        unAllowIPsWarning: '授權 IP 為空將允許所有 IP 存取系統，可能造成系統不安全，是否繼續？',\n        allowIPsHelper: '設定授權 IP 後，僅有設定中的 IP 可以存取 1Panel 服務',\n        allowIPsWarning: '設定授權 IP 後，僅有設定中的 IP 可以存取 1Panel 服務，是否繼續？',\n        allowIPsHelper1: '授權 IP 為空時，則取消授權 IP',\n        allowIPEgs: '當存在多個授權 IP 時，需要換行顯示，例： \\n172.16.10.111 \\n172.16.10.0/24',\n        mfa: '兩步驗證',\n        mfaClose: '關閉兩步驗證將導致服務安全性降低，是否繼續？',\n        secret: '金鑰',\n        mfaAlert: '兩步驗證密碼是基於目前時間生成，請確保伺服器時間已同步',\n        mfaHelper: '開啟後會驗證手機應用驗證碼',\n        mfaHelper1: '下載兩步驗證手機應用 如:',\n        mfaHelper2: '使用手機應用掃描以下二維碼，取得 6 位驗證碼',\n        mfaHelper3: '輸入手機應用上的 6 位數字',\n        mfaCode: '驗證碼',\n        mfaInterval: '重新整理時間（秒）',\n        mfaTitleHelper: '用於區分不同 1Panel 主機，修改後請重新掃描或手動新增金鑰資訊',\n        mfaIntervalHelper:\n            '修改重新整理時間後，請重新掃描或手動新增金鑰資訊。\\n部分驗證器僅支援 30 秒重新整理時間（如 Google/Microsoft Authenticator），請確認相容性。',\n        sslChangeHelper: 'https 設定修改需要重新啟動服務，是否繼續？',\n        sslDisable: '停用',\n        sslDisableHelper: '停用 https 服務，需要重新啟動面板才能生效，是否繼續？',\n        noAuthSetting: '未認證設定',\n        noAuthSettingHelper: '當使用者未登入且未正確輸入安全入口、授權 IP 或綁定域名時，此回應可以隱藏面板特徵。',\n        responseSetting: '響應設定',\n        help200: '幫助頁面',\n        error400: '錯誤請求',\n        error401: '未授權',\n        error403: '禁止存取',\n        error404: '未找到',\n        error408: '請求逾時',\n        error416: '無效請求',\n        error444: '連線已關閉',\n        error500: '內部伺服器錯誤',\n        https: '為面板設置 HTTPS 可提升存取安全性',\n        strictHelper: '非 HTTPS 流量無法連接面板',\n        muxHelper: '面板將同時監聽 HTTP 和 HTTPS 協議，並將 HTTP 重定向到 HTTPS，此模式下可能會降低面板安全性',\n        certType: '憑證類型',\n        selfSigned: '自簽名',\n        selfSignedHelper: '自簽憑證，不被瀏覽器信任，顯示不安全是正常現象',\n        select: '選擇已有',\n        domainOrIP: '域名或 IP：',\n        timeOut: '過期時間：',\n        rootCrtDownload: '根憑證下載',\n        primaryKey: '金鑰',\n        certificate: '憑證',\n        about: '關於',\n        versionItem: '當前版本',\n        backupCopies: '備份份數',\n        backupCopiesHelper: '設定用於版本回滾的升級備份保留份數。0 表示保留所有。',\n        backupCopiesRule: '請至少儲存 3 份升級備份記錄',\n        release: '版本更新日誌',\n        releaseHelper: '目前環境更新日誌取得異常，可手動前往官方文件檢視',\n        project: '項目地址',\n        issue: '問題回饋',\n        doc: '官方文件',\n        star: '點亮 Star',\n        description: 'Linux 伺服器維運面板',\n        forum: '論壇求助',\n        doc2: '使用手冊',\n        currentVersion: '當前執行版本：',\n        license: '許可證',\n        bindNode: '綁定節點',\n        menuSetting: '選單設定',\n        menuSettingHelper: '當只存在 1 個子選單時，選單列將僅展示該子選單',\n        showAll: '全部顯示',\n        hideALL: '全部隱藏',\n        ifShow: '是否顯示',\n        menu: '選單',\n        confirmMessage: '即將重新整理頁面更新進階功能選單列表，是否繼續？',\n        recoverMessage: '即將重新整理頁面，並復原選單列表至初始狀態。是否繼續？',\n        compressPassword: '壓縮密碼',\n        backupRecoverMessage: '請輸入壓縮或解壓縮密碼（留空則不設定）',\n    },\n    license: {\n        offLine: '離線版',\n        community: '社區版',\n        oss: '社區版',\n        pro: '專業版：',\n        trial: '試用',\n        add: '新增社區版',\n        licenseBindHelper: '僅當授權已綁定節點時，才能使用其免費節點額度',\n        licenseAlert: '僅當許可證正常綁定到節點時，該許可證才能新增社區版節點，只有正常綁定到許可證的節點支援切換。',\n        licenseUnbindHelper: '偵測到該許可證存在社區版節點，請解除綁定後重試',\n        subscription: '訂閱',\n        perpetual: '永久授權',\n        versionConstraint: '{0} 版本買斷',\n        forceUnbind: '強制解除綁定',\n        forceUnbindHelper: '強制解除綁定將忽略解除過程中產生的錯誤，最終解除許可證綁定。',\n        updateForce: '強制更新（忽略解除綁定過程中的所有錯誤，確保最終操作成功）',\n        trialInfo: '版本',\n        authorizationId: '訂閱授權 ID',\n        authorizedUser: '被授權方',\n        lostHelper: '許可證已達到最大重試次數，請手動點選同步按鈕，以確保專業版功能正常使用，詳情：',\n        exceptionalHelper: '許可證同步驗證異常，請手動點選同步按鈕，以確保專業版功能正常使用，詳情：',\n        quickUpdate: '快速更新',\n        power: '授權',\n        unbindHelper: '解除綁定後將清除該節點所有專業版相關設定，是否繼續？',\n        importLicense: '匯入許可證',\n        importHelper: '請點選或拖動許可檔案到此處',\n        levelUpPro: '升級專業版',\n        licenseSync: '許可證同步',\n        knowMorePro: '了解更多',\n        closeAlert: '此頁可在面板設定中關閉顯示',\n        introduce: '功能介紹',\n        waf: '升級專業版可以獲得攔截地圖、日誌、封鎖記錄、地理位置封禁、自訂規則、自訂攔截頁面等功能。',\n        tamper: '升級專業版可以保護網站免受未經授權的修改或篡改。',\n        tamperHelper: '操作失敗，該檔案或資料夾已經開啟防篡改，請檢查後重試',\n        setting: '升級專業版可以自訂面板 Logo、歡迎簡介等資訊。',\n        monitor: '升級專業版可以檢視網站的即時狀態、訪客趨勢、訪客來源、請求日誌等資訊。',\n        alert: '升級專業版可透過簡訊接收告警資訊，並檢視告警日誌，全面掌控各類關鍵事件，確保系統執行無憂。',\n        node: '升級專業版可以使用 1Panel 管理多台 Linux 伺服器。',\n        nodeApp: '升級專業版可以統一升級多節點應用版本，無需手動切換節點。',\n        nodeDashboard: '升級專業版可以集中管理多節點應用、網站、資料庫和計劃任務。',\n        fileExchange: '升級專業版可以在多台伺服器之間快速傳輸檔案。',\n        app: '升級專業版可透過手機APP，檢視服務資訊、異常監控等。',\n        cluster: '升級專業版可以管理 MySQL/Postgres/Redis 主從叢集。',\n        vllm: '升級專業版可將 Vllm 服務統一納入 1Panel 管理，集中完成部署、設定調整、狀態維運與任務追蹤，降低本地模型服務維護成本。',\n    },\n    clean: {\n        scan: '開始掃描',\n        scanHelper: '輕鬆梳理 1Panel 執行期間積累的垃圾檔案',\n        clean: '立即清理',\n        reScan: '重新掃描',\n        cleanHelper: '已勾選檔案及目錄清理後無法回滾，是否繼續？',\n        statusSuggest: '( 建議清理 )',\n        statusClean: '( 很乾淨 )',\n        statusEmpty: '非常乾淨，無需清理',\n        statusWarning: '( 謹慎操作 )',\n        lastCleanTime: '上次清理時間: {0}',\n        lastCleanHelper: '清理檔案及目錄：{0} 個， 總計清理：{1}',\n        cleanSuccessful: '清理成功',\n        currentCleanHelper: '本次清理檔案及目錄：{0} 個， 總計清理：{1}',\n        suggest: '( 建議清理 )',\n        totalScan: '待清理垃圾檔案共計： ',\n        selectScan: '已選中垃圾檔案共計： ',\n        system: '系統垃圾',\n        systemHelper: '快照、升級等過程中產生的臨時檔案',\n        panelOriginal: '系統快照恢復前備份檔案',\n        upgrade: '系統升級備份檔案',\n        agentPackages: '歷史版本子節點升級/安裝套件',\n        upgradeHelper: '( 建議保留最新的升級備份用於系統回滾 )',\n        snapshot: '系統快照暫存檔',\n        rollback: '復原前備份目錄',\n        backup: '系統備份',\n        backupHelper: '本地備份帳號中未關聯的備份檔案',\n        unknownBackup: '系統備份',\n        tmpBackup: '臨時備份',\n        unknownApp: '未關聯應用備份',\n        unknownDatabase: '未關聯資料庫備份',\n        unknownWebsite: '未關聯網站備份',\n        unknownSnapshot: '未關聯快照備份',\n        upload: '臨時上傳檔案',\n        uploadHelper: '系統上傳備份列表中上傳的暫存檔',\n        download: '臨時下載檔案',\n        downloadHelper: '系統從第三方備份帳號下載的暫存檔',\n        directory: '資料夾',\n        systemLog: '日誌檔案',\n        systemLogHelper: '系統日誌、任務日誌、網站日誌檔案',\n        dockerLog: '容器操作日誌檔案',\n        taskLog: '計劃任務執行日誌檔案',\n        shell: 'Shell 腳本計劃任務',\n        containerShell: '容器內執行 Shell 腳本計劃任務',\n        curl: 'CURL 計劃任務',\n        docker: '容器垃圾',\n        dockerHelper: '容器、映像、儲存卷、建置快取等檔案',\n        volumes: '磁碟區',\n        buildCache: '容器建置快取',\n        appTmpDownload: '應用程式暫存下載檔案',\n        unknownWebsiteLog: '未關聯網站日誌備份檔案',\n    },\n    app: {\n        app: '應用',\n        installName: '安裝名稱',\n        installed: '已安裝',\n        all: '全部',\n        version: '版本',\n        detail: '詳情',\n        params: '參數',\n        author: '作者',\n        source: '來源',\n        appName: '應用名稱',\n        deleteWarn: '刪除操作會把所有資料和備份一併刪除，此操作不可回滾，是否繼續？',\n        syncSuccess: '同步成功',\n        canUpgrade: '可升級',\n        backupName: '檔案名稱',\n        backupPath: '檔案路徑',\n        backupdate: '備份時間',\n        versionSelect: '請選擇版本',\n        operatorHelper: '將對選中應用進行 {0} 操作，是否繼續？',\n        checkInstalledWarn: '未偵測到 {0} ,請進入應用商店點選安裝',\n        limitHelper: '該應用已安裝，不支援重複安裝',\n        deleteHelper: '{0}已經關聯以下資源，請檢查後重試',\n        checkTitle: '提示',\n        defaultConfig: '預設設定',\n        defaultConfigHelper: '已復原為預設設定，儲存後生效',\n        forceDelete: '強制刪除',\n        forceDeleteHelper: '強制刪除，會忽略刪除過程中產生的錯誤並最終刪除中繼資料',\n        deleteBackup: '刪除備份',\n        deleteBackupHelper: '同時刪除應用備份',\n        deleteDB: '刪除資料庫',\n        deleteDBHelper: '同時刪除與應用關聯的資料庫',\n        noService: '無{0}',\n        toInstall: '去安裝',\n        param: '參數設定',\n        syncAppList: '更新應用列表',\n        alreadyRun: '已安裝',\n        less1Minute: '小於1分鐘',\n        appOfficeWebsite: '官方網站',\n        github: '開源社區',\n        document: '檔案說明',\n        updatePrompt: '目前應用均為最新版本',\n        installPrompt: '尚未安裝任何應用',\n        updateHelper: '更新參數可能導致應用無法啟動，請提前備份並謹慎操作',\n        updateWarn: '更新參數需要重建應用，是否繼續？',\n        busPort: '服務埠',\n        syncStart: '開始同步請稍後重新整理應用商店',\n        advanced: '進階設定',\n        cpuCore: '核心數',\n        containerName: '容器名稱',\n        containerNameHelper: '可以為空，為空自動生成',\n        allowPort: '埠外部存取',\n        allowPortHelper: '允許埠外部存取會放開防火牆埠',\n        appInstallWarn: '應用埠預設不允許外部存取，可以在下方進階設定中選擇放開',\n        upgradeStart: '開始升級請稍後重新整理頁面',\n        toFolder: '進入安裝目錄',\n        editCompose: '編輯 compose 檔案',\n        editComposeHelper: '編輯 compose 檔案可能導致軟體安裝失敗',\n        composeNullErr: 'compose 不能為空',\n        takeDown: '已廢棄',\n        allReadyInstalled: '已安裝',\n        installHelper: '設定映像加速可以解決映像拉取失敗的問題',\n        sortMode: '排序',\n        installWarn: '目前未勾選埠外部存取，將無法透過外網IP:埠存取，是否繼續？ ',\n        showIgnore: '檢視忽略應用',\n        cancelIgnore: '取消忽略',\n        ignoreList: '忽略列表',\n        appHelper: '部分應用的安裝使用說明請在應用詳情頁檢視',\n        backupApp: '升級前備份應用',\n        backupAppHelper: '升級失敗會使用備份自動回滾,請在日誌審計-系統日誌中檢視失敗原因',\n        openrestyDeleteHelper: '強制刪除 OpenResty 會刪除所有的網站，請確認風險後操作',\n        downloadLogHelper1: '即將下載 {0} 套用所有日誌，是否繼續？ ',\n        downloadLogHelper2: '即將下載 {0} 應用最近 {1} 條日誌，是否繼續？ ',\n        syncAllAppHelper: '即將同步所有應用，是否繼續？ ',\n        hostModeHelper: '目前應用網路模式為 host 模式，如需放開埠，請在防火牆頁面手動放開',\n        showLocal: '顯示本機應用程式',\n        reload: '重載',\n        upgradeWarn: '升級應用程式會取代 docker-compose.yml 檔案，如有更改，可以點選檢視檔案對比',\n        openclawHttpsUpgradeNoticeTitle: '注意：以下說明僅適用於 OpenClaw 從 2026.3.13 之前版本升級的使用者：',\n        openclawHttpsUpgradeNoticeItem1: '已部署的智慧體升級完成後，需要進入 配置 → 設定 → 安全 頁面手動新增存取地址。',\n        openclawHttpsUpgradeNoticeItem2:\n            '新版本已強制使用 HTTPS 存取智慧體。如果之前透過反向代理網站存取，請將代理目標地址修改為 https://IP:Port。',\n        newVersion: '新版本',\n        oldVersion: '當前版本',\n        composeDiff: '檔案對比',\n        showDiff: '看對比',\n        useNew: '使用自訂版本',\n        useDefault: '使用預設版本',\n        useCustom: '自訂 docker-compose.yml',\n        useCustomHelper: '使用自訂 docker-compose.yml 檔案，可能會導致應用程式升級失敗，如無必要，請勿勾選',\n        diffHelper: '左側為舊版本，右側為新版，編輯之後點選使用自訂版本儲存',\n        pullImage: '拉取映像',\n        pullImageHelper: '在應用啟動之前執行 docker pull 來拉取映像',\n        deleteImage: '刪除映像',\n        deleteImageHelper: '刪除應用相關映像，刪除失敗任務不會終止',\n        requireMemory: '最低記憶體',\n        supportedArchitectures: '支援架構',\n        link: '連結',\n        showCurrentArch: '目前伺服器架構應用',\n        syncLocalApp: '同步本機應用',\n        memoryRequiredHelper: '目前應用記憶體需求 {0}',\n        gpuConfig: '開啟 GPU 支援',\n        gpuConfigHelper: '請確保機器有 NVIDIA GPU 並且安裝 NVIDIA 驅動 和 NVIDIA docker Container Toolkit',\n        webUI: 'Web 存取地址',\n        webUIPlaceholder: '例：example.com:8080/login',\n        defaultWebDomain: '預設存取地址',\n        defaultWebDomainHepler: '預設存取用於應用埠跳轉，例如應用埠為 8080 則跳轉地址為 http(s)://預設存取地址:8080',\n        webUIConfig: '當前節點尚未設定預設存取地址，請在應用參數或前往面板設定進行設定',\n        toLink: '連結',\n        customAppHelper: '在安裝自訂應用商店包之前，請確保沒有任何已安裝的應用。',\n        forceUninstall: '強制移除',\n        syncCustomApp: '同步自訂應用',\n        ignoreAll: '忽略後續所有版本',\n        ignoreVersion: '忽略指定版本',\n        specifyIP: '綁定主機 IP',\n        specifyIPHelper: '設定埠綁定的主機位址/網卡（如果你不清楚這個的作用，請不要填寫）',\n        uninstallDeleteBackup: '移除應用-刪除備份',\n        uninstallDeleteImage: '移除應用-刪除映像',\n        upgradeBackup: '應用升級前備份應用',\n        noAppHelper: '未偵測到應用程式，請前往任務中心檢視應用商店同步日誌',\n        isEdirWarn: '偵測到 docker-compose.yml 檔案被修改，請檢視對比',\n    },\n    website: {\n        primaryDomain: '主域名',\n        otherDomains: '其他域名',\n        static: '靜態網站',\n        deployment: '一鍵部署',\n        supportUpType: '僅支援 .tar.gz 檔案格式，且壓縮檔內必須包含 {0}.json 檔案',\n        proxy: '反向代理',\n        alias: '代號',\n        ftpUser: 'FTP 帳號',\n        ftpPassword: 'FTP 密碼',\n        ftpHelper: '建立站點的同時，為站點建立一個對應 FTP 帳戶，並且 FTP 目錄指向站點所在目錄。',\n        remark: '備註',\n        groupSetting: '分組管理',\n        createGroup: '建立分組',\n        appNew: '新裝應用',\n        appInstalled: '已裝應用',\n        delete: '刪除網站',\n        deleteApp: '刪除應用',\n        deleteBackup: '刪除備份',\n        domain: '域名',\n        domainHelper: '一行一個域名,支援*和IP位址,支援域名:埠',\n        addDomain: '新增域名',\n        domainConfig: '域名設定',\n        defaultDoc: '預設檔案',\n        perserver: '並發限制',\n        perserverHelper: '限制目前站點最大並發數',\n        perip: '單 IP 限制',\n        peripHelper: '限制單個 IP 存取最大並發數',\n        rate: '流量限制',\n        rateLimit: '單請求限速',\n        rateHelper: '限制每個請求的流量上限(單位:KB)',\n        rateLimitHelper: '限制每個請求的每秒傳輸速率（單位：KB/s）',\n        limitHelper: '啟用流量控制',\n        other: '其他',\n        currentSSL: '目前憑證',\n        dnsAccount: 'DNS 帳號',\n        applySSL: '憑證申請',\n        SSLList: '憑證列表',\n        createDnsAccount: 'DNS 帳戶',\n        aliyun: '阿里雲 DNS',\n        aliEsa: '阿里雲 ESA',\n        awsRoute53: 'Amazon Route 53（亞馬遜）',\n        manual: '手動解析',\n        key: '金鑰',\n        check: '檢視',\n        acmeAccountManage: 'Acme 帳戶',\n        email: '信箱',\n        acmeAccount: 'Acme 帳戶',\n        provider: '驗證方式',\n        dnsManual: '手動解析',\n        expireDate: '過期時間',\n        brand: '組織',\n        deploySSL: '部署',\n        deploySSLHelper: '確定部署憑證？',\n        ssl: '憑證',\n        dnsAccountManage: 'DNS 帳戶',\n        renewSSL: '續簽',\n        renewHelper: '確定續簽憑證？',\n        renewSuccess: '續簽憑證',\n        enableHTTPS: '啟用 HTTPS',\n        aliasHelper: '代號是網站目錄的資料夾名稱',\n        lastBackupAt: '上次備份時間',\n        null: '無',\n        nginxConfig: 'Nginx 設定',\n        websiteConfig: '網站設定',\n        proxySettings: '代理設定',\n        advancedSettings: '進階設定',\n        cacheSettings: '快取設定',\n        sniSettings: 'SNI 設定',\n        basic: '基本',\n        source: '設定檔',\n        security: '安全',\n        nginxPer: '效能調整',\n        neverExpire: '永不過期',\n        setDefault: '設為預設',\n        deleteHelper: '相關應用狀態不正常，請檢查',\n        toApp: '去已安裝列表',\n        cycle: '週期',\n        frequency: '頻率',\n        ccHelper: '{0} 秒內累計請求同一URL超過 {1} 次,觸發CC防禦,封鎖此IP',\n        mustSave: '修改之後需要儲存才能生效',\n        fileExt: '檔案副檔名',\n        fileExtBlock: '檔案副檔名黑名單',\n        value: '值',\n        enable: '開啟',\n        proxyAddress: '代理地址',\n        proxyHelper: '例: 127.0.0.1:8080',\n        forceDelete: '強制刪除',\n        forceDeleteHelper: '強制刪除，會忽略刪除過程中產生的錯誤並最終刪除中繼資料',\n        deleteAppHelper: '同時刪除關聯應用、資料庫以及應用備份',\n        deleteBackupHelper: '同時刪除網站備份',\n        deleteDatabaseHelper: '同時刪除網站關聯資料庫',\n        deleteConfirmHelper: '刪除操作無法回滾，請輸入 <span style=\"color:red\"> \"{0}\" </span> 刪除',\n        staticPath: '對應主目錄:',\n        limit: '限制方案',\n        blog: '論壇/部落格',\n        imageSite: '圖片站',\n        downloadSite: '下載站',\n        shopSite: '商城',\n        doorSite: '門戶',\n        qiteSite: '企業',\n        videoSite: '影片',\n        errLog: '錯誤日誌',\n        stopHelper: '停止站點後將無法正常存取，使用者瀏覽會顯示目前站點停止頁面，是否繼續？',\n        startHelper: '啟用站點後，使用者可以正常瀏覽網站內容，是否繼續？',\n        sitePath: '網站目錄',\n        siteAlias: '網站代號',\n        primaryPath: 'root 目錄',\n        folderTitle: '網站主要包含以下資料夾',\n        wafFolder: '防火牆規則',\n        indexFolder: '網站root目錄',\n        sslFolder: '網站憑證',\n        enableOrNot: '是否啟用',\n        oldSSL: '選擇已有憑證',\n        manualSSL: '手動匯入憑證',\n        select: '選擇',\n        selectSSL: '選擇憑證',\n        privateKey: '私鑰(KEY)',\n        certificate: '憑證(PEM格式)',\n        HTTPConfig: 'HTTP 選項',\n        HTTPSOnly: '禁止 HTTP',\n        HTTPToHTTPS: '存取HTTP自動跳轉到HTTPS',\n        HTTPAlso: 'HTTP可直接存取',\n        sslConfig: 'SSL 選項',\n        disableHTTPS: '停用 HTTPS',\n        disableHTTPSHelper: '停用 HTTPS會刪除憑證相關設定，是否繼續？',\n        SSLHelper: '注意：請勿將SSL憑證用於非法網站 \\n 如開啟後無法使用HTTPS存取，請檢查安全組是否正確放行443埠',\n        SSLConfig: '憑證設定',\n        SSLProConfig: 'SSL 協議設定',\n        supportProtocol: '支援的協議版本',\n        encryptionAlgorithm: '加密演算法',\n        notSecurity: '（不安全）',\n        encryptHelper:\n            \"Let's Encrypt 簽發憑證有頻率限制，但足以滿足正常需求，過於頻繁操作會導致簽發失敗。詳細限制請看 <a target=「_blank」 href='https://letsencrypt.org/zh-cn/docs/rate-limits/'>官方文件</a> \",\n        ipValue: '值',\n        ext: '檔案副檔名',\n        wafInputHelper: '按行輸入資料，一行一個',\n        data: '資料',\n        ever: '永久',\n        nextYear: '一年後',\n        noLog: '暫無日誌',\n        defaultServer: '預設站點',\n        noDefaultServer: '未設定',\n        defaultServerHelper:\n            '設定預設站點後,所有未綁定的域名和IP都被定向到預設站點\\n可有效防止惡意解析\\n但同時會導致 WAF 未授權域名攔截失敗',\n        websiteDeploymentHelper: '使用從 1Panel 部署的應用建立網站',\n        websiteStatictHelper: '在主機上建立網站目錄',\n        websiteProxyHelper: '代理已有服務,例如本機已安裝使用 8080 埠的 halo 服務，那麼代理地址為 http://127.0.0.1:8080',\n        restoreHelper: '確認使用此備份復原？',\n        wafValueHelper: '值',\n        runtimeProxyHelper: '使用從 1Panel 建立的執行環境',\n        runtime: '執行環境',\n        deleteRuntimeHelper: '執行環境應用需要跟網站一併刪除，請謹慎處理',\n        proxyType: '監聽網路類型',\n        unix: 'Unix 網路',\n        tcp: 'TCP/IP 網路',\n        phpFPM: 'FPM 設定檔',\n        phpConfig: 'PHP 設定檔',\n        updateConfig: '設定修改',\n        isOn: '開啟',\n        isOff: '關閉',\n        rewrite: '偽靜態',\n        rewriteMode: '方案',\n        current: '目前',\n        rewriteHelper: '若設定偽靜態後，網站無法正常存取，請嘗試設定回default',\n        runDir: '執行目錄',\n        runUserHelper:\n            '透過 PHP 容器執行環境部署的網站，需要將 index 和子目錄下的所有檔案、資料夾擁有者和使用者群組設定為 1000，本機 PHP 環境需要參考本機 PHP-FPM 使用者和使用者群組設定',\n        userGroup: '執行使用者/組',\n        uGroup: '使用者群組',\n        proxyPath: '前端請求路徑',\n        proxyPass: '後端代理地址',\n        cache: '快取',\n        cacheTime: '快取時間',\n        enableCache: '開啟快取',\n        proxyHost: '後端域名',\n        disabled: '已停止',\n        startProxy: '開啟反向代理',\n        stopProxy: '關閉反向代理',\n        sourceFile: '源文',\n        proxyHelper1: '存取這個目錄時將會把目標URL的內容返回並顯示',\n        proxyPassHelper: '代理的站點，必須為可正常存取的URL',\n        proxyHostHelper: '將域名新增到請求頭傳遞到代理伺服器',\n        modifier: '匹配規則',\n        modifierHelper: '例：= 精確匹配，~ 正則匹配，^~ 匹配路徑開頭 等',\n        replace: '文字取代',\n        replaceHelper:\n            'nginx文字取代功能可以在反向代理時對響應內容進行字串取代。常用於修改代理後端返回的HTML、CSS、JavaScript等檔案中的連結地址、API介面地址等。支援正規表示式匹配，可實現複雜的內容取代需求。',\n        addReplace: '新增文字取代',\n        replaced: '搜尋字串（不能為空）',\n        replaceText: '取代為字串',\n        replacedErr: '搜尋字串不能為空',\n        replacedErr2: '搜尋字串不能重複',\n        replacedListEmpty: '無文字取代規則',\n        proxySslName: '代理 SNI 名稱',\n        basicAuth: '密碼存取',\n        editBasicAuthHelper: '密碼為非對稱加密，無法回顯，編輯需要重新設定密碼',\n        antiLeech: '防盜鏈',\n        extends: '副檔名',\n        browserCache: '瀏覽器快取',\n        noModify: '不修改',\n        serverCache: '伺服器快取',\n        leechLog: '記錄防盜鏈日誌',\n        accessDomain: '允許的域名',\n        leechReturn: '響應資源',\n        noneRef: '允許來源為空',\n        disable: '未啟用',\n        disableLeechHelper: '是否停用防盜鏈',\n        disableLeech: '停用防盜鏈',\n        ipv6: '監聽 IPV6',\n        leechReturnError: '請填寫 HTTP 狀態碼',\n        blockedRef: '允許非標準 Referer',\n        accessControl: '防盜鏈控制',\n        leechcacheControl: '快取控制',\n        logEnableControl: '記錄靜態資源請求日誌',\n        leechSpecialValidHelper:\n            '啟用「允許 Referer 為空」時，不會阻止無 Referer 的請求（直接存取等）；啟用「允許非標準 Referer」時，會放行任何不以 http/https 開頭的 Referer 請求（用戶端請求等）',\n        leechInvalidReturnHelper: '攔截盜鏈請求後返回的 HTTP 狀態碼',\n        leechlogControlHelper: '記錄靜態資源的請求，生產環境通常可關閉以避免過多無意義的日誌',\n        selectAcme: '選擇 Acme 帳號',\n        imported: '手動建立',\n        importType: '匯入方式',\n        pasteSSL: '貼上程式碼',\n        localSSL: '選擇伺服器檔案',\n        privateKeyPath: '私鑰檔案',\n        certificatePath: '憑證檔案',\n        ipWhiteListHelper: 'IP白名單的作用：所有規則對IP白名單無效',\n        redirect: '重定向',\n        sourceDomain: '源域名/路徑',\n        targetURL: '目標URL地址',\n        keepPath: '保留URI參數',\n        path: '路徑',\n        redirectType: '重定向類型',\n        redirectWay: '方式',\n        keep: '保留',\n        notKeep: '不保留',\n        redirectRoot: '重定向到首頁',\n        redirectHelper: '301永久重定向，302臨時重定向',\n        changePHPVersionWarn: '此動作不可回滾，是否繼續',\n        changeVersion: '切換版本',\n        retainConfig: '是否保留 php-fpm.conf 和 php.ini 檔案',\n        runDirHelper2: '請確保二級執行目錄位於 index 目錄下',\n        openrestyHelper: 'OpenResty 預設 HTTP 埠：{0} HTTPS 埠：{1}，可能影響網站域名存取和 HTTPS 強制跳轉',\n        primaryDomainHelper: '支援網域:port',\n        acmeAccountType: '帳號類型',\n        keyType: '金鑰演算法',\n        tencentCloud: '騰訊雲',\n        containWarn: '其他域名中包含主域名，請重新輸入',\n        rewriteHelper2: '從應用程式商店安裝的 WordPress 等應用，預設已經設定好偽靜態，重複設定可能會報錯',\n        websiteBackupWarn: '僅支援匯入本機備份，匯入其他機器備份可能會復原失敗',\n        ipWebsiteWarn: 'IP 為網域名稱的網站，需要設定為預設網站才能正常存取',\n        hstsHelper: '開啟 HSTS 可以增加網站安全性',\n        includeSubDomains: '子域',\n        hstsIncludeSubDomainsHelper: '啟用後，HSTS策略將應用於目前域名的所有子域名',\n        defaultHtml: '預設頁面',\n        website404: '網站 404 錯誤頁',\n        domain404: '網站不存在頁面',\n        indexHtml: '靜態網站預設頁',\n        stopHtml: '網站停用頁',\n        indexPHP: 'PHP 網站預設頁',\n        sslExpireDate: '憑證過期時間',\n        website404Helper: '網站 404 錯誤頁僅支援 PHP 執行環境網站和靜態網站',\n        sni: '回源 SNI',\n        sniHelper: '反代後端為 https 的時候可能需要設定回源 SNI，詳細需要看 CDN 服務商檔案',\n        huaweicloud: '華為雲',\n        createDb: '建立資料庫',\n        enableSSLHelper: '開啟失敗不會影響網站建立',\n        batchAdd: '批次新增域名',\n        batchInput: '批次輸入',\n        domainNotFQDN: '此域名可能無法在公網存取',\n        domainInvalid: '網域格式不正確',\n        domainBatchHelper: '一行一個域名，格式：域名:埠@ssl\\n範例：example.com:443@ssl 或 example.com',\n        generateDomain: '生成',\n        global: '全域',\n        subsite: '子網站',\n        subsiteHelper: '子網站可以選擇已存在的 PHP 和靜態網站的目錄作為主目錄。',\n        parentWbeiste: '父級網站',\n        deleteSubsite: '刪除目前網站需要先刪除子網站 {0}',\n        loadBalance: '負載均衡',\n        server: '節點',\n        algorithm: '演算法',\n        ipHash: 'IP 雜湊',\n        ipHashHelper: '基於用戶端 IP 位址將請求分配到特定伺服器，可以確保特定用戶端總是被路由到同一伺服器。',\n        leastConn: '最少連接',\n        leastConnHelper: '將請求發送到目前活動連接數最少的伺服器。',\n        leastTime: '最少時間',\n        leastTimeHelper: '將請求發送到目前活動連接時間最短的伺服器。',\n        defaultHelper:\n            '預設方法，請求被均勻分配到每個伺服器。如果伺服器有權重設定，則根據指定的權重分配請求，權重越高的伺服器接收更多請求。',\n        weight: '權重',\n        maxFails: '最大失敗次數',\n        maxConns: '最大連接數',\n        strategy: '策略',\n        strategyDown: '停用',\n        strategyBackup: '備用',\n        ipHashBackupErr: 'IP 雜湊不支援備用節點',\n        failTimeout: '故障逾時',\n        failTimeoutHelper:\n            '伺服器健康檢查的時間視窗長度。在該時間段內累計失敗次數達到閾值時，伺服器將被暫時移除，並在經過相同時長後重新嘗試。預設 10 秒',\n        staticChangePHPHelper: '目前為靜態網站，可切換為 PHP 網站',\n        proxyCache: '反向代理快取',\n        cacheLimit: '快取空間限制',\n        shareCahe: '快取計數記憶體大小',\n        cacheExpire: '快取過期時間',\n        shareCaheHelper: '每1M記憶體可以儲存約8000個快取物件',\n        cacheLimitHelper: '超過限制會自動刪除舊的快取',\n        cacheExpireJHelper: '超出時間快取未命中將會被刪除',\n        realIP: '真實 IP',\n        ipFrom: 'IP 來源',\n        ipFromHelper:\n            '透過設定可信 IP 來源，OpenResty 會分析 HTTP Header 中的 IP 資訊，準確識別並記錄訪客的真實 IP 位址，包括在存取日誌中',\n        ipFromExample1: '如果前端是 Frp 等工具，可以填寫 Frp 的 IP 位址，類似 127.0.0.1',\n        ipFromExample2: '如果前端是 CDN，可以填寫 CDN 的 IP 位址段',\n        ipFromExample3: '如果不確定，可以填 0.0.0.0/0（ipv4） ::/0（ipv6） [注意：允許任意來源 IP 不安全]',\n        http3Helper:\n            'HTTP/3 是 HTTP/2 的升級版本，提供更快的連線速度和更好的效能，但並非所有瀏覽器都支援 HTTP/3，啟用後可能會導致部分瀏覽器無法存取',\n        cors: '跨域存取(CORS)',\n        enableCors: '開啟跨域',\n        allowOrigins: '允許存取的網域名稱',\n        allowMethods: '允許的請求方法',\n        allowHeaders: '允許的請求標頭',\n        allowCredentials: '允許攜帶Cookie',\n        preflight: '預檢請求快速回應',\n        preflightHleper:\n            '開啟後，當瀏覽器發送跨域預檢請求（OPTIONS 請求）時，系統會自動返回 204 狀態碼並設定必要的跨域回應標頭',\n        changeDatabase: '切換資料庫',\n        changeDatabaseHelper1: '資料庫關聯用於備份復原網站。',\n        changeDatabaseHelper2: '切換其他資料庫會導致以前的備份無法復原。',\n        saveCustom: '另存為範本',\n        rainyun: '雨雲',\n        volcengine: 'Volcengine',\n        runtimePortHelper: '目前執行環境存在多個埠，請選擇一個代理埠。',\n        runtimePortWarn: '目前執行環境沒有埠，無法代理',\n        cacheWarn: '請先關閉反代中的快取開關',\n        loadBalanceHelper: '建立負載均衡後，請前往『反向代理』，新增代理並將後端地址設定為：http://<負載均衡名稱>',\n        favorite: '收藏',\n        cancelFavorite: '取消收藏',\n        useProxy: '使用代理',\n        useProxyHelper: '使用面板設定中的代理伺服器地址',\n        westCN: '西部數碼',\n        openBaseDir: '防跨站攻擊',\n        openBaseDirHelper: 'open_basedir 用於限制 PHP 檔案存取路徑，有助於防止跨站存取和提升安全性',\n        serverCacheTime: '伺服器快取時間',\n        serverCacheTimeHelper: '請求在伺服器端快取的時間，到期前相同請求會直接返回快取結果，不再請求源站。',\n        browserCacheTime: '瀏覽器快取時間',\n        browserCacheTimeHelper:\n            '靜態資源在瀏覽器本機快取的時間，減少重複請求。到期前使用者重新整理頁面會直接使用本機快取。',\n        donotLinkeDB: '不關聯資料庫',\n        toWebsiteDir: '進入網站目錄',\n        execParameters: '執行參數',\n        extCommand: '補充指令',\n        mirror: '映像源',\n        execUser: '執行使用者',\n        execDir: '執行目錄',\n        packagist: '中國全量映像',\n        batchOperate: '批次操作',\n        batchOperateHelper: '批次{0}網站，是否繼續？',\n        stream: 'TCP/UDP 代理',\n        streamPorts: '監聽埠',\n        streamPortsHelper: '設定對外監聽的埠號，客戶端將透過此埠存取服務，按逗號分割，例：5222,5223',\n        streamHelper: 'TCP/UDP 埠轉發與負載均衡',\n        udp: '啟用 UDP',\n        syncHtmlHelper: '同步到 PHP 和靜態網站',\n    },\n    php: {\n        short_open_tag: '短標籤支援',\n        max_execution_time: '最大腳本執行時間',\n        max_input_time: '最大輸入時間',\n        memory_limit: ' 腳本記憶體限制',\n        post_max_size: 'POST資料最大尺寸',\n        file_uploads: '是否允許上傳檔案',\n        upload_max_filesize: '允許上傳檔案的最大尺寸',\n        max_file_uploads: '允許同時上傳檔案的最大數量',\n        default_socket_timeout: 'Socket 逾時時間',\n        error_reporting: '錯誤級別',\n        display_errors: '是否輸出詳細錯誤資訊',\n        cgi_fix_pathinfo: '是否開啟pathinfo',\n        date_timezone: '時區',\n        disableFunction: '停用函式',\n        disableFunctionHelper: '輸入要停用的函式，例如exec，多個請用,分割',\n        uploadMaxSize: '上傳限制',\n        indexHelper: '為保障PHP網站正常執行，請將程式碼放置於 index 目錄，並避免重新命名',\n        extensions: '擴充範本',\n        extension: '擴充',\n        extensionHelper: '多個擴充功能,分割',\n        toExtensionsList: '檢視擴充清單',\n        containerConfig: '容器設定',\n        containerConfigHelper: '環境變數等資訊可以在建立完成之後在設定-容器設定中修改',\n        dateTimezoneHelper: '範例：TZ=Asia/Shanghai（請根據需要自行新增）',\n        extensionsHelper: '多個擴展請用 , 分隔',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: '伺服器名字的hash表大小',\n        clientHeaderBufferSizeHelper: '用戶端請求的Header Buffer 大小',\n        clientMaxBodySizeHelper: '最大上傳檔案',\n        keepaliveTimeoutHelper: '連接逾時時間',\n        gzipMinLengthHelper: '最小壓縮檔案',\n        gzipCompLevelHelper: '壓縮率',\n        gzipHelper: '是否開啟壓縮傳輸',\n        connections: '活動連接(Active connections)',\n        accepts: '總連接次數(accepts)',\n        handled: '總握手次數(handled)',\n        requests: '總請求數(requests)',\n        reading: '請求數(Reading)',\n        writing: '響應數(Writing)',\n        waiting: '駐留行程(Waiting)',\n        status: '當前狀態',\n        configResource: '設定修改',\n        saveAndReload: '儲存並重載',\n        clearProxyCache: '清除反代快取',\n        clearProxyCacheWarn: '此操作將刪除快取目錄下的所有檔案，是否繼續？',\n        create: '新增模組',\n        update: '編輯模組',\n        params: '參數',\n        packages: '軟體包',\n        script: '腳本',\n        module: '模組',\n        build: '建構',\n        buildWarn: '建構 OpenResty 需要預留一定的 CPU 和記憶體，時間較長，請耐心等待',\n        mirrorUrl: '軟體源',\n        paramsHelper: '例：--add-module=/tmp/ngx_brotli',\n        packagesHelper: '例：git,curl 以逗號分割',\n        scriptHelper: '編譯之前執行的腳本，通常用於下載模組原始碼，安裝依賴等',\n        buildHelper: '新增/修改模組後點選構建，構建成功後會自動重新啟動 OpenResty',\n        defaultHttps: 'HTTPS 防竄站',\n        defaultHttpsHelper1: '開啟後可以解決 HTTPS 竄站問題',\n        sslRejectHandshake: '拒絕預設 SSL 握手',\n        sslRejectHandshakeHelper: '開啟之後可以避免憑證洩露，設置預設網站會讓此設置失效',\n    },\n    ssl: {\n        create: '申請憑證',\n        provider: '類型',\n        manualCreate: '手動建立',\n        acmeAccount: 'Acme 帳號',\n        resolveDomain: '解析域名',\n        err: '錯誤',\n        value: '記錄值',\n        dnsResolveHelper: '請到DNS解析服務商處新增以下解析記錄：',\n        detail: '詳情',\n        msg: '憑證資訊',\n        ssl: '憑證',\n        key: '私鑰',\n        startDate: '生效時間',\n        organization: '簽發機構',\n        renewConfirm: '是否確定給網域名稱 {0} 申請憑證？ ',\n        autoRenew: '自動續簽',\n        autoRenewHelper: '距離到期時間30天自動續約',\n        renewSuccess: '續簽成功',\n        renewWebsite: '該憑證已經和以下網站關聯，申請會同步應用到這些網站',\n        createAcme: '建立帳戶',\n        acmeHelper: 'Acme 帳戶用於申請免費憑證',\n        upload: '上傳憑證',\n        applyType: '申請方式',\n        apply: '申請',\n        applyStart: '憑證申請開始',\n        getDnsResolve: '正在取得 DNS 解析值,請稍後 ...',\n        selfSigned: '自簽憑證',\n        ca: '憑證頒發機構',\n        commonName: '憑證主體名稱(CN)',\n        caName: '機構名稱',\n        company: '公司/組織',\n        department: '部門',\n        city: '城市',\n        province: '省份',\n        country: '國家代號',\n        commonNameHelper: '例如:',\n        selfSign: '簽發憑證',\n        days: '有效期限',\n        domainHelper: '一行一個網域名稱,支援*和IP位址',\n        pushDir: '推送憑證到本機目錄',\n        dir: '目錄',\n        pushDirHelper: '會在此目錄下產生兩個檔案，憑證檔案：fullchain.pem 金鑰檔案：privkey.pem',\n        organizationDetail: '機構詳情',\n        fromWebsite: '從網站取得',\n        dnsMauanlHelper: '手動解析模式需要在建立完之後點選申請按鈕取得 DNS 解析值',\n        httpHelper: '使用 HTTP 模式需安裝 OpenResty，且不支援申請泛域名憑證。',\n        buypassHelper: 'Buypass 大陸地區無法存取',\n        googleHelper: '如何取得EAB HmacKey 和EAB kid',\n        googleCloudHelper: 'Google Cloud API 大陸大部分地區無法存取',\n        skipDNSCheck: '跳過 DNS 校驗',\n        skipDNSCheckHelper: '如果出現申請逾時問題，請勾選此處，其他情況請勿勾選',\n        cfHelper: '請勿使用 Global API Key',\n        deprecated: '即將廢棄',\n        deprecatedHelper: '已經停止維護，可能會在以後的某個版本廢棄，請使用騰訊雲方式解析',\n        disableCNAME: '停用 CNAME',\n        disableCNAMEHelper: '有 CNAME 設定的域名，如果申請失敗，可以勾選此處',\n        nameserver: 'DNS 伺服器',\n        nameserverHelper: '使用自訂的 DNS 伺服器來校驗網域名稱',\n        edit: '編輯憑證',\n        execShell: '申請憑證之後執行腳本',\n        shell: '腳本內容',\n        shellHelper:\n            '腳本預設執行目錄為 1Panel 安裝目錄，如果有推送憑證，那麼執行目錄為憑證推送目錄。預設逾時時間 30 分鐘',\n        customAcme: '自訂 ACME 服務',\n        customAcmeURL: 'ACME 服務 URL',\n        baiduCloud: '百度雲',\n        pushNode: '同步到其他節點',\n        pushNodeHelper: '申請/續期之後推送到選擇的節點',\n        fromMaster: '主節點推送',\n        hostedZoneID: '託管區域 ID',\n        isIP: 'IP 憑證',\n        useEAB: '使用 EAB 認證',\n    },\n    firewall: {\n        create: '建立規則',\n        edit: '編輯規則',\n        ccDeny: 'CC 防護',\n        ipWhiteList: 'IP 白名單',\n        ipBlockList: 'IP 黑名單',\n        fileExtBlockList: '檔案副檔名黑名單',\n        urlWhiteList: 'URL 白名單',\n        urlBlockList: 'URL 黑名單',\n        argsCheck: 'GET 參數校驗',\n        postCheck: 'POST 參數校驗',\n        cookieBlockList: 'Cookie 黑名單',\n        dockerHelper: '目前防火牆無法停用容器埠映射，已安裝應用可前往【已安裝】頁面編輯應用參數，設定埠放行規則。',\n        iptablesHelper: '偵測到系統正在使用 {0} 防火牆，如需切換至 iptables，請先手動解除安裝',\n        quickJump: '快速跳轉',\n        used: '已使用',\n        unUsed: '未使用',\n        dockerRestart: '防火牆操作需要重新啟動 Docker 服務',\n        firewallHelper: '{0}系統防火牆',\n        firewallNotStart: '尚未啟用系統防火牆，請先啟用',\n        restartFirewallHelper: '該操作將對目前防火牆進行重新啟動操作，是否繼續？',\n        stopFirewallHelper: '系統防火牆關閉後，伺服器將失去安全防護，是否繼續？',\n        startFirewallHelper: '系統防火牆開啟後，可以更好的防護伺服器安全，是否繼續？',\n        noPing: '禁 ping',\n        enableBanPing: '禁 Ping',\n        disableBanPing: '解除禁 Ping',\n        noPingTitle: '是否禁 ping',\n        noPingHelper: '禁 ping 後將無法 ping 通伺服器，是否繼續？',\n        onPingHelper: '解除禁 ping 後您的伺服器可能會被駭客發現，是否繼續？',\n        changeStrategy: '修改{0}策略',\n        changeStrategyIPHelper1: 'IP 策略修改為【封鎖】，設定後該 IP 將禁止存取伺服器，是否繼續？',\n        changeStrategyIPHelper2: 'IP 策略修改為【放行】，設定後該 IP 將復原正常存取，是否繼續？',\n        changeStrategyPortHelper1: '埠策略修改為【拒絕】，設定後埠將拒絕外部存取，是否繼續？',\n        changeStrategyPortHelper2: '埠策略修改為【允許】，設定後埠將復原正常存取，是否繼續？',\n        stop: '禁止',\n        portFormatError: '請輸入正確的埠資訊',\n        portHelper1: '多個埠，如：8080,8081',\n        portHelper2: '範圍埠，如：8080-8089',\n        strategy: '策略',\n        accept: '允許',\n        drop: '拒絕',\n        anyWhere: '所有 IP',\n        address: '指定 IP',\n        addressHelper: '支援輸入 IP 或 IP 段',\n        allow: '放行',\n        deny: '封鎖',\n        addressFormatError: '請輸入合法的 ip 地址',\n        addressHelper1: '支援輸入 IP 或 IP 段：172.16.10.11 或 172.16.0.0/24',\n        addressHelper2: '多個 IP 或 IP 段 請用 \",\" 隔開：172.16.10.11,172.16.0.0/24',\n        allIP: '所有 IP',\n        portRule: '埠規則',\n        forwardRule: '埠轉發',\n        ipRule: 'IP 規則',\n        userAgent: 'User-Agent 過濾',\n        sourcePort: '來源埠',\n        targetIP: '目標 IP',\n        targetPort: '目標埠',\n        forwardHelper1: '如果是本機埠轉發，目標 IP 為：127.0.0.1',\n        forwardHelper2: '如果目標 IP 不填寫，預設為本機埠轉發',\n        forwardPortHelper: '支援埠範圍，如：8080-8089',\n        forwardInboundInterface: '轉發入站網路介面',\n        exportHelper: '即將匯出 {0} 條防火牆規則，是否繼續？',\n        importSuccess: '成功匯入 {0} 條規則',\n        importPartialSuccess: '匯入完成：成功 {0} 條，失敗 {1} 條',\n        ipv4Limit: '目前操作暫僅支援 IPv4 地址',\n        basicStatus: '目前未綁定鏈 {0} ，請先綁定',\n        baseIptables: 'iptables 服務',\n        forwardIptables: 'iptables 埠轉發服務',\n        advanceIptables: 'iptables 進階設定服務',\n        initMsg: '即將初始化 {0}, 是否繼續？',\n        initHelper: '偵測到 {0} 未初始化，請點選頂部狀態列的初始化按鈕進行設定',\n        bindHelper: '綁定 僅當狀態為綁定時，防火牆規則才能生效，是否確認？',\n        unbindHelper: '解除綁定 解除綁定時，已新增的所有防火牆規則將失效，請謹慎操作，是否確認？',\n        defaultStrategy: '目前鏈 {0} 的預設策略為 {1}',\n        defaultStrategy2: '目前鏈 {0} 的預設策略為 {1}，當前狀態為未綁定，已新增的防火牆規則需要綁定後生效',\n        filterRule: 'Filter 規則',\n        filterHelper: 'Filter 規則允許您在 INPUT/OUTPUT 層級控制網路流量。請謹慎設定，避免鎖定系統。',\n        chain: '鏈',\n        targetChain: '目標鏈',\n        sourceIP: '來源 IP',\n        destIP: '目標 IP',\n        inboundDirection: '入站方向',\n        outboundDirection: '出站方向',\n        destPort: '目標埠',\n        action: '動作',\n        reject: '拒絕',\n        sourceIPHelper: 'CIDR 格式，如 192.168.1.0/24，留空表示所有地址',\n        destIPHelper: 'CIDR 格式，如 10.0.0.0/8，留空表示所有地址',\n        portHelper: '0 表示任意埠',\n        allPorts: '所有埠',\n        deleteRuleConfirm: '將刪除 {0} 條規則，是否繼續？',\n        advancedControl: '進階控制',\n        advancedControlNotAvailable: '目前使用 {0} 防火牆，進階規則僅支援 iptables',\n        destination: '目的地',\n    },\n    runtime: {\n        runtime: '執行環境',\n        workDir: '工作目錄',\n        localHelper: '本機環境安裝及離線環境使用相關問題，可參考 ',\n        versionHelper: 'PHP的版本,例 v8.0',\n        buildHelper: '擴展越多，製作映像檔時占用的 CPU 越高，可在建立環境後再安裝擴展。',\n        openrestyWarn: 'PHP 需要升級 OpenResty 至 1.21.4.1 版本以上才能使用',\n        toupgrade: '去升級',\n        edit: '編輯執行環境',\n        extendHelper: '列表中不存在的擴展，可以手動輸入之後選擇，例:輸入 sockets ，然後在下拉選單中選擇第一個',\n        rebuildHelper: '編輯擴展後需要【重建】PHP 應用之後才能生效',\n        rebuild: '重建 PHP 應用',\n        source: 'PHP 擴展源',\n        ustc: '中國科學技術大學',\n        netease: '網易',\n        aliyun: '阿里雲',\n        tsinghua: '清華大學',\n        xtomhk: 'XTOM 映像站（香港）',\n        xtom: 'XTOM 映像站（全球）',\n        phpsourceHelper: '根據你的網路環境選擇合適的源',\n        appPort: '應用埠',\n        externalPort: '外部映射埠',\n        packageManager: '包管理器',\n        codeDir: '原始碼目錄',\n        appPortHelper: '應用埠是指容器內部執行的埠',\n        externalPortHelper: '外部映射埠是指將容器內部埠映射到外部的埠',\n        runScript: '啟動指令',\n        runScriptHelper: '啟動指令是指容器啟動後執行的指令',\n        open: '開啟',\n        operatorHelper: '將對選取的執行環境進行 {0} 操作，是否繼續？ ',\n        taobao: '淘寶',\n        tencent: '騰訊',\n        imageSource: '映像源',\n        moduleManager: '模組管理',\n        module: '模組',\n        nodeOperatorHelper: '是否{0} {1} 模組？ 操作可能導致運轉環境異常，請確認後操作',\n        customScript: '自訂啟動指令',\n        customScriptHelper: '請填寫完整的啟動指令，例：npm run start，pm2 啟動指令請換為 pm2-runtime，否則會啟動失敗',\n        portError: '不能填寫相同連接埠',\n        systemRestartHelper: '狀態說明：中斷-系統重新啟動導致狀態取得失敗',\n        javaScriptHelper: '請填寫完整啟動指令，例：java -jar halo.jar -Xmx1024M -Xms256M',\n        javaDirHelper: '目錄中要包含 jar 包，子目錄中包含也可',\n        goHelper: '請填寫完整啟動指令，例：go run main.go 或 ./main',\n        goDirHelper: '目錄中要包含 go 檔案或二進制檔案，子目錄中包含也可',\n        extension: '擴充',\n        installExtension: '是否確認安裝擴充功能 {0}',\n        loadedExtension: '已載入擴充功能',\n        popularExtension: '常用擴充',\n        uninstallExtension: '是否確認移除擴充功能 {0}',\n        phpConfigHelper: '修改設定需要重新啟動執行環境，是否繼續',\n        operateMode: '執行模式',\n        dynamic: '動態',\n        static: '靜態',\n        ondemand: '按需',\n        dynamicHelper: '動態調整行程數，彈性高，適合流量波動較大或低記憶體的網站',\n        staticHelper: '固定行程數，適合高併發穩定流量的網站，資源消耗較高',\n        ondemandHelper: '行程按需啟動和銷毀，資源利用最優，但初始回應可能較慢',\n        max_children: '允許建立的最大行程數',\n        start_servers: '啟動時所建立的行程數',\n        min_spare_servers: '最小空閒行程數',\n        max_spare_servers: '最大空閒行程數',\n        envKey: '名稱',\n        envValue: '值',\n        environment: '環境變數',\n        pythonHelper:\n            '請填寫完整啟動指令，例：pip install -r requirements.txt && python manage.py runserver 0.0.0.0:5000',\n        dotnetHelper: '請填寫完整的啟動指令，例如 dotnet MyWebApp.dll',\n        dirHelper: '說明：請填寫容器內的目錄路徑',\n        concurrency: '並發方案',\n        loadStatus: '負載狀態',\n        extraHosts: '主機映射',\n    },\n    process: {\n        pid: '行程ID',\n        ppid: '父行程ID',\n        numThreads: '執行緒',\n        memory: '記憶體',\n        diskRead: '磁碟讀',\n        diskWrite: '磁碟寫',\n        netSent: '上行',\n        netRecv: '下行',\n        numConnections: '連接',\n        startTime: '啟動時間',\n        running: '執行中',\n        sleep: '睡眠',\n        stop: '停止',\n        idle: '空閒',\n        zombie: '僵屍行程',\n        wait: '等待',\n        lock: '鎖定',\n        blocked: '阻塞',\n        cmdLine: '啟動指令',\n        basic: '基本資訊',\n        mem: '記憶體資訊',\n        openFiles: '檔案開啟',\n        env: '環境變數',\n        noenv: '無',\n        net: '網路連接',\n        laddr: '本機地址/埠',\n        raddr: '遠端地址/埠',\n        stopProcess: '結束',\n        viewDetails: '檢視詳情',\n        stopProcessWarn: '是否確定結束此行程 (PID:{0})？',\n        kill: '結束行程',\n        killNow: '立即結束',\n        killHelper: '結束行程 {0} 可能導致部分程式無法正常運作，是否繼續？',\n        processName: '行程名稱',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: '取得行程狀態失敗，請檢查 supervisor 服務狀態',\n            notSupport: '未偵測到 Supervisor 服務，請前往腳本庫頁面手動安裝',\n            list: '守護行程',\n            config: 'Supervisor 設定',\n            primaryConfig: '主設定檔案位置',\n            notSupportCtl: '未偵測到 supervisorctl，請前往腳本庫頁面手動安裝',\n            user: '啟動使用者',\n            command: '啟動指令',\n            dir: '執行目錄',\n            numprocs: '行程數量',\n            initWarn:\n                '初始化操作需要修改設定檔的 [include] files 參數，修改後的服務設定檔所在目錄: 1panel安裝目錄/1panel/tools/supervisord/supervisor.d/',\n            operatorHelper: '將對 {0} 進行 {1} 操作，是否繼續？ ',\n            uptime: '執行時長',\n            notStartWarn: 'Supervisor 未啟動，請先啟動',\n            serviceName: '服務名稱',\n            initHelper: '偵測到 Supervisor 服務尚未初始化，請點選上方狀態列的初始化按鈕進行設定。',\n            serviceNameHelper: 'systemctl 管理的 Supervisor 服務名稱，一般為 supervisor 或 supervisord',\n            restartHelper: '初始化會重新啟動服務，導致原有的守護行程全部關閉',\n            RUNNING: '執行中',\n            STOPPED: '已停止',\n            STOPPING: '停止中',\n            STARTING: '啟動中',\n            FATAL: '啟動失敗',\n            BACKOFF: '啟動異常',\n            ERROR: '錯誤',\n            statusCode: '狀態碼',\n            manage: '管理',\n            autoRestart: '自動重新啟動',\n            EXITED: '已退出',\n            autoRestartHelper: '程式異常退出後是否自動重新啟動',\n            autoStart: '自動啟動',\n            autoStartHelper: 'Supervisor 啟動後是否自動啟動服務',\n        },\n    },\n    disk: {\n        management: '磁碟管理',\n        partition: '分區',\n        unmount: '取消掛載',\n        unmountHelper: '是否取消掛載分區 {0}？',\n        mount: '掛載',\n        partitionAlert: '進行磁碟分區時需要格式化磁碟，原有資料將被刪除，請提前儲存或快照資料',\n        mountPoint: '掛載目錄',\n        systemDisk: '系統磁碟',\n        unpartitionedDisk: '未分區磁碟',\n        handlePartition: '立即分區',\n        filesystem: '檔案系統',\n        unmounted: '未掛載',\n        cannotOperate: '無法操作',\n        systemDiskHelper: '提示：目前磁碟為系統盤，無法進行操作',\n        autoMount: '自動掛載',\n        model: '裝置型號',\n        diskType: '磁碟類型',\n        serial: '序號',\n        noFail: '掛載失敗不影響系統啟動',\n    },\n    xpack: {\n        expiresAlert: '溫馨提醒：您的專業版許可證將在 {0} 天後到期，屆時所有專業版功能將無法繼續使用。',\n        name: '專業版',\n        menu: '進階功能',\n        upage: 'AI 建站',\n        proAlert: '升級專業版以使用此功能',\n        waf: {\n            name: 'WAF',\n            blackWhite: '黑白名單',\n            globalSetting: '全域設定',\n            websiteSetting: '網站設定',\n            blockRecords: '封鎖紀錄',\n            world: '世界',\n            china: '中國',\n            intercept: '攔截',\n            request: '請求',\n            count4xx: '4xx 數量',\n            count5xx: '5xx 數量',\n            todayStatus: '今日狀態',\n            reqMap: '攔截地圖（30日）',\n            resource: '來源',\n            count: '數量',\n            hight: '高',\n            low: '低',\n            reqCount: '請求數',\n            interceptCount: '攔截數',\n            requestTrends: '請求趨勢（7天）',\n            interceptTrends: '攔截趨勢（7天）',\n            whiteList: '白名單',\n            blackList: '黑名單',\n            ipBlackListHelper: '黑名單中的 IP 無法存取網站',\n            ipWhiteListHelper: '白名單中的 IP 不受任何規則限制',\n            uaBlackListHelper: '攜帶黑名單中的 User-Agent 的請求將被攔截',\n            uaWhiteListHelper: '攜帶白名單中的 User-Agent 的請求不受任何規則限制',\n            urlBlackListHelper: '請求黑名單中的 URL 將被攔截',\n            urlWhiteListHelper: '請求白名單中的 URL 請求不受任何規則限制',\n            ccHelper: '{0} 秒內累積請求任意網站超過 {1} 次，封鎖此 IP {2}',\n            blockTime: '封鎖時間',\n            attackHelper: '{0} 秒內累計攔截超過 {1} 次，封鎖此 IP {2}',\n            notFoundHelper: '{0} 秒內累計請求回傳 404 超過 {1} 次，封鎖此 IP {2}',\n            frequencyLimit: '頻率限制',\n            regionLimit: '地區限制',\n            defaultRule: '預設規則',\n            accessFrequencyLimit: '存取頻率限制',\n            attackLimit: '攻擊頻率限制',\n            notFoundLimit: '404 頻率限制',\n            urlLimit: 'URL 頻率限制',\n            urlLimitHelper: '為單一 URL 設定存取頻率',\n            sqliDefense: 'SQL 注入防禦',\n            sqliHelper: '辨識請求中的 SQL 注入並攔截',\n            xssHelper: '辨識請求中的 XSS 並攔截',\n            xssDefense: 'XSS 防禦',\n            uaDefense: '惡意 User-Agent 規則',\n            uaHelper: '包含常見的惡意爬蟲規則',\n            argsDefense: '惡意參數規則',\n            argsHelper: '在禁止請求中攜帶惡意參數',\n            cookieDefense: '惡意 Cookie 規則',\n            cookieHelper: '禁止請求中攜帶惡意 Cookie',\n            headerDefense: '惡意 Header 規則',\n            headerHelper: '禁止請求中攜帶惡意 Header',\n            httpRule: 'HTTP 請求方法規則',\n            httpHelper:\n                '設定允許存取的方法類型，如果想限制某些類型瀏覽，請關閉這個類型的按鈕，例：僅允許 GET 類型瀏覽，那麼需要關閉除了 GET 之外的其他類型按鈕',\n            geoRule: '地區存取限制',\n            geoHelper: '限制某些地區瀏覽你的網站，例：允許中國大陸瀏覽，那麼中國大陸以外的請求都會被攔截',\n            ipLocation: 'IP 歸屬地',\n            action: '動作',\n            ruleType: '攻擊類型',\n            ipHelper: '請輸入 IP',\n            attackLog: '攻擊日誌',\n            rule: '規則',\n            ipArr: 'IPV4 範圍',\n            ipStart: '起始 IP',\n            ipEnd: '結束 IP',\n            ipv4: 'IPV4',\n            ipv6: 'IPV6',\n            urlDefense: 'URL 規則',\n            urlHelper: '禁止存取的 URL',\n            dirFilter: '目錄過濾',\n            sqlInject: 'SQL 注入',\n            xss: 'XSS',\n            phpExec: 'PHP 腳本執行',\n            oneWordTrojan: '一句話木馬',\n            appFilter: '套用危險目錄過濾',\n            webShell: 'Webshell',\n            args: '惡意參數',\n            protocolFilter: '協議過濾',\n            javaFilter: 'Java 危險檔案過濾',\n            scannerFilter: '掃描器過濾',\n            escapeFilter: '轉義過濾',\n            customRule: '自訂規則',\n            httpMethod: 'HTTP 方法過濾',\n            fileExt: '檔案上傳限制',\n            fileExtHelper: '禁止上傳的檔案副檔名',\n            deny: '禁止',\n            allow: '允許',\n            field: '匹配物件',\n            pattern: '符合條件',\n            ruleContent: '符合內容',\n            contain: '包含',\n            equal: '等於',\n            regex: '正規表示式',\n            notEqual: '不等於',\n            customRuleHelper: '根據條件匹配執行對應動作',\n            actionAllow: '允許',\n            blockIP: '封鎖 IP',\n            code: '返回狀態碼',\n            noRes: '斷開連線 (444)',\n            badReq: '參數錯誤 (400)',\n            forbidden: '禁止瀏覽 (403)',\n            serverErr: '伺服器錯誤 (500)',\n            resHtml: '回應頁面',\n            allowHelper: '允許瀏覽會跳過後續的 WAF 規則，請謹慎使用',\n            captcha: '人機驗證',\n            fiveSeconds: '5 秒驗證',\n            location: '地區',\n            redisConfig: 'Redis 設定',\n            redisHelper: '開啟 Redis 可以將暫時封鎖的 IP 持久化',\n            wafHelper: '關閉之後所有網站將失去防護',\n            attackIP: '攻擊 IP',\n            attackParam: '攻擊訊息',\n            execRule: '命中規則',\n            acl: 'ACL',\n            sql: 'SQL 注入',\n            cc: '瀏覽頻率限制',\n            isBlocking: '封鎖中',\n            isFree: '已解封',\n            unLock: '解封',\n            unLockHelper: '是否解封 IP:{0}?',\n            saveDefault: '儲存預設',\n            saveToWebsite: '應用在網站',\n            saveToWebsiteHelper: '是否將目前設定套用到所有網站？ ',\n            websiteHelper: '此處為建立網站的預設設定，修改之後需要應用到網站才能生效',\n            websiteHelper2: '此處為建立網站的預設設定，詳細設定請在網站處修改',\n            ipGroup: 'IP 組',\n            ipGroupHelper: '一行一個 IP 或 IP 段，支援 IPv4 和 IPv6， 例：192.168.1.1 或 192.168.1.0/24',\n            ipBlack: 'IP 黑名單',\n            openRestyAlert: 'OpenResty 版本需要高於 {0}',\n            initAlert: '首次使用需要初始化，會修改網站設定檔案，原有的 WAF 設定會遺失，請一定提前備份 OpenResty',\n            initHelper: '初始化操作將清除現有的 WAF 設定，您確定要進行初始化嗎？ ',\n            mainSwitch: '總開關',\n            websiteAlert: '請先建立網站',\n            defaultUrlBlack: 'URL 規則',\n            htmlRes: '攔截頁面',\n            urlSearchHelper: '請輸入 URL，支援模糊搜尋',\n            toCreate: '去建立',\n            closeWaf: '關閉 WAF',\n            closeWafHelper: '關閉 WAF 會使網站失去防護，是否繼續',\n            addblack: '封鎖',\n            addwhite: '加白',\n            addblackHelper: '是否把 IP:{0} 加到預設黑名單?',\n            addwhiteHelper: '是否把 IP:{0} 加到預設白名單?',\n            defaultUaBlack: 'User-Agent 規則',\n            defaultIpBlack: '惡意 IP 群組',\n            cookie: 'Cookie 規則',\n            urlBlack: 'URL 黑名單',\n            uaBlack: 'User-Agent 黑名單',\n            attackCount: '攻擊頻率限制',\n            fileExtCheck: '檔案上傳限制',\n            geoRestrict: '地區存取限制',\n            attacklog: '攔截紀錄',\n            unknownWebsite: '未授權網域存取',\n            geoRuleEmpty: '地區不能為空',\n            unknown: '網站不存在',\n            geo: '地區限制',\n            revertHtml: '是否還原{0}為預設頁面？',\n            five_seconds: '5 秒驗證',\n            header: 'Header 規則',\n            methodWhite: 'HTTP 規則',\n            expiryDate: '有效期限',\n            expiryDateHelper: '驗證通過後有效期內不再驗證',\n            defaultIpBlackHelper: '從網路收集的一些惡意 IP，阻止其存取',\n            notFoundCount: '404 頻率限制',\n            matchValue: '匹配值',\n            headerName: '支援非特殊字元開頭、英文、數字、-，長度3-30',\n            cdnHelper: '使用 CDN 的網站可以開啟此處來取得正確來源 IP',\n            clearLogWarn: '清空日誌將無法復原，是否繼續？',\n            commonRuleHelper: '規則為模糊匹配',\n            blockIPHelper: '封鎖 IP 暫時儲存在 OpenResty 中，重新啟動 OpenResty 會解封，可以透過封鎖功能永久封鎖',\n            addWhiteUrlHelper: '是否把 URL {0} 加到白名單?',\n            dashHelper: '社群版也可使用全域設定和網站設定中的功能',\n            wafStatusHelper: 'WAF 未開啟，請在全域設定中開啟',\n            ccMode: '模式',\n            global: '全域模式',\n            uriMode: 'URL 模式',\n            globalHelper: '全域模式：當單位時間內任意URL的請求總數超過閾值時觸發',\n            uriModeHelper: 'URL模式：單位時間內對單一URL的請求數量超過閾值時觸發',\n            ip: 'IP 黑名單',\n            globalSettingHelper: '有【網站】標籤的設定，需要在【網站設定】生效，全域設定僅為建立網站的預設設定',\n            globalSettingHelper2: '設定生效需要【全域設定】和【網站設定】的開關同時開啟',\n            urlCCHelper: '{0} 秒內累計請求此 URL 超過 {1} 次，封鎖此 IP {2}',\n            urlCCHelper2: 'URL 不能帶參數',\n            notContain: '不包含',\n            urlcc: 'URL 頻率限制',\n            method: '請求類型',\n            addIpsToBlock: '批次封鎖 IP',\n            addUrlsToWhite: '批次加白 URL',\n            noBlackIp: 'IP 已封鎖，無需再次封鎖',\n            noWhiteUrl: 'URL 已加白，無需再次加白',\n            spiderIpHelper:\n                '包含百度、Bing、Google、360、神馬、搜狗、位元組、DuckDuckGo、Yandex，關閉之後會攔截所有蜘蛛存取',\n            spiderIp: '蜘蛛 IP 池',\n            geoIp: 'IP 位址庫',\n            geoIpHelper: '用來確認 IP 的地理位置',\n            stat: '攻擊報表',\n            statTitle: '報表',\n            attackIp: '攻擊 IP',\n            attackCountNum: '攻擊次數',\n            percent: '佔比',\n            addblackUrlHelper: '是否把 URL:{0} 新增到預設黑名單？',\n            rce: '遠端程式碼執行',\n            software: '軟體',\n            cveHelper: '包含常見軟體、框架的漏洞',\n            vulnCheck: '補充規則',\n            ssrf: 'SSRF 漏洞',\n            afr: '任意檔案讀取',\n            ua: '未授權存取',\n            id: '資訊洩露',\n            aa: '認證繞過',\n            dr: '目錄遍歷',\n            xxe: 'XXE 漏洞',\n            suid: '序列化漏洞',\n            dos: '拒絕服務漏洞',\n            afd: '任意檔案下載',\n            sqlInjection: 'SQL 注入',\n            afw: '任意檔案寫入',\n            il: '資訊洩露',\n            clearAllLog: '清空所有日誌',\n            exportLog: '匯出日誌',\n            appRule: '應用規則',\n            appRuleHelper: '常見應用的規則，開啟之後可以減少誤報，一個網站只能使用一個規則',\n            logExternal: '排除記錄類型',\n            ipWhite: 'IP 白名單',\n            urlWhite: 'URL 白名單',\n            uaWhite: 'User-Agent 白名單',\n            logExternalHelper:\n                '排除記錄類型不會被記錄到日誌中，黑白名單、地區存取限制、自訂規則會產生大量日誌，建議排除',\n            ssti: 'SSTI 攻擊',\n            crlf: 'CRLF 注入',\n            strict: '嚴格模式',\n            strictHelper: '使用更嚴格的規則來校驗請求',\n            saveLog: '儲存日誌',\n            remoteURLHelper: '遠端 URL 需要保證每行一個 IP 並且沒有其他字元',\n            notFound: 'Not Found (404)',\n            serviceUnavailable: '服務不可用 (503)',\n            gatewayTimeout: '閘道器逾時 (504)',\n            belongToIpGroup: '屬於 IP 組',\n            notBelongToIpGroup: '不屬於 IP 組',\n            unknownWebsiteKey: '未知域名',\n            special: '指定',\n            fileToLarge: '檔案超過 1MB 無法上傳',\n            uploadOverLimit: '上傳檔案超過數量限制，最大1個',\n            importRuleHelper: '一行一個規則',\n        },\n        monitor: {\n            '360': '360 搜尋',\n            name: '網站監控',\n            pv: '瀏覽量',\n            uv: '訪客數',\n            flow: '流量',\n            ip: '獨立 IP',\n            spider: '蜘蛛',\n            visitors: '訪客趨勢',\n            today: '今天',\n            last7days: '最近 7 天',\n            last30days: '最近 30 天',\n            uvMap: '訪客地圖 (30日)',\n            qps: '即時請求數（1分鐘）',\n            flowSec: '即時流量（1分鐘）',\n            excludeCode: '排除狀態碼',\n            excludeUrl: '排除 URL',\n            excludeExt: '排除副檔名',\n            cdnHelper: '透過 CDN 設定的 Header 來取得真實 IP',\n            reqRank: '瀏覽排行',\n            refererDomain: '來源網域',\n            os: '作業系統',\n            browser: '瀏覽器/用戶端',\n            device: '裝置',\n            showMore: '看更多',\n            unknown: '其他',\n            pc: '電腦',\n            mobile: '行動端',\n            wechat: '微信',\n            machine: '機器',\n            tencent: '騰訊瀏覽器',\n            ucweb: 'UC 瀏覽器',\n            '2345explorer': '2345 瀏覽器',\n            huaweibrowser: '華為瀏覽器',\n            log: '請求日誌',\n            statusCode: '狀態碼',\n            requestTime: '回應時間',\n            flowRes: '回應流量',\n            method: '請求類型',\n            statusCodeHelper: '可在上方輸入狀態碼',\n            statusCodeError: '狀態碼型別錯誤',\n            methodHelper: '可在上方輸入請求類型',\n            all: '所有',\n            baidu: '百度',\n            google: 'Google',\n            bing: '必應',\n            bytes: '今日頭條',\n            sogou: '搜狗',\n            failed: '錯誤',\n            ipCount: 'IP 數',\n            spiderCount: '蜘蛛請求',\n            averageReqTime: '平均回應時間',\n            totalFlow: '總流量',\n            logSize: '日誌檔案大小',\n            realIPType: '真實IP取得方式',\n            fromHeader: '從 HTTP Header 取得',\n            fromHeaders: '從 Header 清單中取得',\n            header: 'HTTP Header',\n            cdnConfig: 'CDN 適配',\n            xff1: '取得 X-Forwarded-For 的上一級代理程式',\n            xff2: '取得 X-Forwarded-For 的上上一級代理程式',\n            xff3: '取得 X-Forwarded-For 的上上上一級代理程式',\n            xffHelper:\n                '例：X-Forwarded-For: <client>,<proxy1>,<proxy2>,<proxy3> 上一階代理程式會取最後一個 IP <proxy3>',\n            headersHelper: '從下列常用的 CDN 攜帶真實 IP 的 HTTP Header 中取得，取第一個能取得到的值',\n            monitorCDNHelper: '修改網站監控的 CDN 設定會同步更新 WAF 的 CDN 設定',\n            wafCDNHelper: '修改 WAF 的 CDN 設定會同步更新網站監控的 CDN 設定',\n            statusErr: '狀態碼格式錯誤',\n            shenma: '神馬搜尋',\n            duckduckgo: 'DuckDuckGo',\n            exceptUri: '排除 Uri',\n            top100Helper: '顯示 Top 100 的資料',\n            logSaveDay: '日誌儲存天數',\n            cros: 'Chrome OS',\n            theworld: '世界之窗瀏覽器',\n            edge: 'Edge',\n            maxthon: '遨遊瀏覽器',\n            monitorStatusHelper: '監控未開啟，請在設定中開啟',\n            excludeIp: '排除 IP',\n            excludeUa: '排除 User-Agent',\n            remotePort: '遠端埠',\n            unknown_browser: '未知',\n            unknown_os: '未知',\n            unknown_device: '未知',\n            logSaveSize: '最大日誌儲存大小',\n            logSaveSizeHelper: '此處為單個網站的日誌儲存大小',\n            '360se': '360 安全瀏覽器',\n            websites: '網站列表',\n            trend: '趨勢統計',\n            reqCount: '請求數',\n            uriHelper: '可以使用 /test/* 或 /*/index.php 來排除 Uri',\n            excludeUri: '排除 Uri',\n        },\n        tamper: {\n            tamper: '網站防篡改',\n            ignoreTemplate: '排除範本',\n            protectTemplate: '保護範本',\n            ignoreTemplateHelper:\n                '請輸入排除內容，Enter或空格分隔。（詳細目錄 ./log 或目錄名 tmp，排除檔案需要輸入詳細檔案 ./data/test.html）',\n            protectTemplateHelper:\n                '請輸入保護內容，Enter或空格分隔。（詳細檔案 ./index.html、檔案後綴 .html、檔案類型 js，保護目錄需要輸入詳細目錄 ./log）',\n            templateContent: '範本內容',\n            template: '範本',\n            saveTemplate: '儲存為範本',\n            tamperHelper1:\n                '一鍵部署類型的網站，建議啟用應用目錄防篡改功能；如出現網站無法正常使用或備份、復原失敗的情況，請先關閉防篡改功能；',\n            tamperHelper2: '將限制非排除目錄下受保護檔案的讀寫、刪除、權限和所有者修改操作',\n            tamperPath: '防護目錄',\n            tamperPathEdit: '修改路徑',\n            log: '攔截日誌',\n            totalProtect: '總防護',\n            todayProtect: '今日防護',\n            templateRule: '長度1-512，名稱不能含有{0}等符號',\n            ignore: '排除',\n            ignoreHelper:\n                '請選擇或輸入排除內容，Enter或空格分隔。（詳細目錄 ./log 或目錄名 tmp，排除檔案需要輸入或選擇詳細檔案 ./data/test.html）',\n            protect: '保護',\n            protectHelper:\n                '請選擇或輸入保護內容，Enter或空格分隔。（詳細檔案 ./index.html、檔案後綴 .html、檔案類型 js，保護目錄需要輸入或選擇詳細目錄 ./log）',\n            tamperHelper00: '排除和保護僅支援輸入相對路徑；',\n            tamperHelper01: '開啟防篡改後，系統將限制非排除目錄下保護檔案的建立、編輯和刪除操作等操作；',\n            tamperHelper02: '優先度：詳細路徑保護 > 詳細路徑排除 > 保護 > 排除',\n            tamperHelper03: '監聽操作只針對於非排除目錄，監聽該目錄下非保護檔案的建立操作。',\n            disableHelper: '即將關閉下列網站的防篡改功能，是否繼續？',\n            appendOnly: '僅追加',\n            appendOnlyHelper: '將限制該目錄下檔案的刪除操作，僅允許新增排除目錄或非保護檔案',\n            immutable: '不可變',\n            immutableHelper: '將限制該檔案的編輯、刪除、權限和所有者修改操作',\n            onWatch: '監聽',\n            onWatchHelper: '監聽該目錄下保護檔案或非排除目錄的建立攔截',\n            forceStop: '強制關閉',\n            forceStopHelper: '即將強制停用該網站目錄的防篡改功能，是否繼續？',\n        },\n        setting: {\n            setting: '介面設定',\n            title: '面板描述',\n            titleHelper: '將會顯示在使用者登入頁面 (例: Linux 伺服器維運面板，建議 8-15 位)',\n            logo: 'Logo (不帶文字)',\n            logoHelper: '將會顯示在選單收縮時管理頁面左上方 (建議圖片大小為: 82px*82px)',\n            logoWithText: 'Logo (帶文字)',\n            logoWithTextHelper: '將會顯示在選單展開時管理頁面左上方 (建議圖片大小為: 185px*55px)',\n            favicon: '網站圖示',\n            faviconHelper: '網站圖示 (建議圖片大小為: 16px*16px)',\n            setDefault: '復原預設',\n            setHelper: '即將儲存目前介面設定內容，是否繼續？',\n            setDefaultHelper: '即將復原所有介面設定到初始狀態，是否繼續？',\n            logoGroup: 'Logo',\n            imageGroup: '圖片',\n            loginImage: '登入頁圖片',\n            loginImageHelper: '將會顯示在登入頁面（建議圖片大小為：500*416px）',\n            loginBgType: '登入頁背景類型',\n            loginBgImage: '登入頁背景圖片',\n            loginBgImageHelper: '將會顯示在登入頁面背景圖片（建議圖片大小為：1920*1080px）',\n            loginBgColor: '登入頁背景顏色',\n            loginBgColorHelper: '將會顯示在登入頁面背景顏色',\n            image: '圖片',\n            bgColor: '背景色',\n            loginGroup: '登入頁面',\n            loginBtnLinkColor: '按鈕顏色',\n            loginBtnLinkColorHelper: '將顯示為登入頁面上的按鈕顏色',\n        },\n        helper: {\n            wafTitle1: '攔截地圖',\n            wafContent1: '統計並顯示 30 天內的攔截地理位置分佈',\n            wafTitle2: '地區瀏覽限制',\n            wafContent2: '依地理位置限制網站的存取來源',\n            wafTitle3: '自訂攔截頁面',\n            wafContent3: '自訂請求被攔截之後的顯示頁面',\n            wafTitle4: '自訂規則（ACL）',\n            wafContent4: '根據自訂的規則攔截請求',\n            tamperTitle1: '檔案完整性監控',\n            tamperContent1: '監控網站檔案的完整性，包括核心檔案、腳本檔案和設定檔案等。',\n            tamperTitle2: '即時掃描和偵測',\n            tamperContent2: '透過即時掃描網站檔案系統，偵測是否存在異常或被篡改的檔案。',\n            tamperTitle3: '安全權限設定',\n            tamperContent3:\n                '透過合理的權限設定和瀏覽控制策略，網站防篡改功能可以限制對網站檔案的瀏覽權限，減少潛在的攻擊面。',\n            tamperTitle4: '日誌紀錄與分析',\n            tamperContent4: '紀錄檔案瀏覽和操作日誌，以便管理員進行後續的審計和分析，以及發現潛在的安全威脅。',\n            settingTitle1: '自訂歡迎語',\n            settingContent1: '在 1Panel 登入頁上設定自訂的歡迎語。',\n            settingTitle2: '自訂 Logo',\n            settingContent2: '允許上傳包含品牌名稱或其他文字的 Logo 圖像。',\n            settingTitle3: '自訂網站圖示',\n            settingContent3: '允許上傳自訂的圖示，以替代預設的瀏覽器圖示，提升使用者體驗。',\n            monitorTitle1: '訪客趨勢',\n            monitorContent1: '統計並顯示網站的訪客趨勢',\n            monitorTitle2: '訪客地圖',\n            monitorContent2: '統計並顯示網站的訪客地理位置分佈',\n            monitorTitle3: '瀏覽統計',\n            monitorContent3: '統計網站的請求資訊，包括蜘蛛，瀏覽裝置，請求狀態等',\n            monitorTitle4: '即時監控',\n            monitorContent4: '即時監控網站的請求訊息，包括請求數，流量等',\n            alertTitle1: '簡訊告警',\n            alertContent1:\n                '當伺服器資源使用異常、網站及憑證過期、新版本更新、密碼過期等情況發生時，透過簡訊告警通知使用者，確保及時處理。',\n            alertTitle2: '告警日誌',\n            alertContent2: '為使用者提供檢視告警日誌的功能，方便追跡和分析歷史告警事件。',\n            alertTitle3: '告警設定',\n            alertContent3:\n                '為使用者提供自訂手機號碼、每日推送次數、每日推送時間的設定，方便使用者的設定來更加合理的進行推送告警。',\n            nodeDashTitle1: '應用管理',\n            nodeDashContent1: '統一管理多節點應用，支援狀態監控、快速起停、終端連線及備份',\n            nodeDashTitle2: '網站管理',\n            nodeDashContent2: '統一管理多節點網站，即時監控狀態，支援批次起停與快速備份',\n            nodeDashTitle3: '資料庫管理',\n            nodeDashContent3: '統一管理多節點資料庫，關鍵狀態一目了然，支援一鍵備份',\n            nodeDashTitle4: '計劃任務管理',\n            nodeDashContent4: '統一管理多節點計劃任務，支援狀態監控、快速啟停與手動觸發執行',\n            nodeTitle1: '一鍵新增節點',\n            nodeContent1: '快速接入多台伺服器節點',\n            nodeTitle2: '批次升級',\n            nodeContent2: '一次操作同步升級所有節點',\n            nodeTitle3: '節點狀態監控',\n            nodeContent3: '即時掌握各節點執行狀態',\n            nodeTitle4: '快速遠端連接',\n            nodeContent4: '一鍵直連節點遠端終端',\n            fileExchangeTitle1: '金鑰認證傳輸',\n            fileExchangeContent1: '通過 SSH 金鑰進行身份驗證，確保傳輸的安全性。',\n            fileExchangeTitle2: '高效檔案同步',\n            fileExchangeContent2: '僅同步變化內容，大幅提高傳輸速度與穩定性。',\n            fileExchangeTitle3: '支援多節點互傳',\n            fileExchangeContent3: '可在不同節點間便捷傳送項目檔案，靈活管理多台伺服器。',\n            nodeAppTitle1: '應用升級管理',\n            nodeAppContent1: '統一監控多節點應用更新情況，支援一鍵升級',\n            appTitle1: '靈活管理面板',\n            appContent1: '隨時隨地輕鬆管理你的 1Panel 伺服器。',\n            appTitle2: '全面服務資訊',\n            appContent2: '在行動端進行應用、網站、Docker、資料庫等基礎管理，支援快速建立應用與網站。',\n            appTitle3: '即時異常監控',\n            appContent3: '行動端即時檢視伺服器狀態、WAF 安全監控、網站存取統計與行程健康狀況。',\n            clusterTitle1: '主從部署',\n            clusterContent1: '支援在不同節點建立 MySQL/Postgres/Redis 主從實例，自動完成主從關聯與初始化',\n            clusterTitle2: '主從管理',\n            clusterContent2: '統一頁面集中管理多個主從節點，檢視其角色、執行狀態等資訊',\n            clusterTitle3: '複製狀態',\n            clusterContent3: '展示主從複製狀態與延遲資訊，輔助排查同步異常問題',\n            vllmTitle1: '統一納管',\n            vllmContent1: '將分散的 Vllm 服務統一接入 1Panel，集中檢視實例、版本、狀態與執行目錄。',\n            vllmTitle2: '快速部署',\n            vllmContent2: '透過視覺化表單完成版本選擇、模型目錄掛載與啟動參數設定，減少手動編排成本。',\n            vllmTitle3: '維運便捷',\n            vllmContent3: '在同一頁面完成建立、編輯、啟停、重啟、刪除與任務追蹤，提升本地模型服務維護效率。',\n            vllmTitle4: '設定可控',\n            vllmContent4: '結合進階設定統一管理連接埠、容器、啟動命令與 Compose 設定，便於標準化交付與後續調整。',\n        },\n        node: {\n            master: '主節點',\n            masterBackup: '主節點備份',\n            backupNode: '備份節點',\n            backupFrequency: '備份頻率（小時）',\n            backupCopies: '備份份數',\n            noBackupNode: '目前備份節點為空，請選擇備份節點儲存後重試',\n            masterBackupAlert:\n                '目前未設定主節點備份，為保障資料安全，請盡快設定備份節點，便於主節點故障時可人工切換新主節點。',\n            node: '節點',\n            addr: '地址',\n            nodeUpgrade: '更新設定',\n            nodeUpgradeHelper: '選中的節點將在主節點升級完成後自動開始升級，無需手動操作。',\n            nodeUnhealthy: '節點狀態異常',\n            deletedNode: '已刪除節點 {0} 暫不支援升級操作',\n            nodeUnhealthyHelper: '偵測到該節點狀態異常，請在 [節點管理] 中檢查後重試',\n            nodeUnbind: '節點未綁定許可證',\n            nodeUnbindHelper: '偵測到該節點未綁定許可證，請在 [面板設定 - 許可證] 選單中綁定後重試',\n            memTotal: '記憶體總計',\n            nodeManagement: '多機管理',\n            nodeItem: '節點管理',\n            panelItem: '面板管理',\n            addPanel: '添加面板',\n            addPanelHelper: '面板新增成功後，您可在 [ 概覽 - 面板 ] 中快速存取目標面板。',\n            panel: '1Panel 面板',\n            others: '其他面板',\n            addNode: '新增節點',\n            connInfo: '連接資訊',\n            nodeInfo: '節點資訊',\n            withProxy: '開啟代理存取',\n            withoutProxy: '關閉代理存取',\n            withProxyHelper: '將使用面板設定中維護的系統代理 {0} 存取子節點，是否繼續？',\n            withoutProxyHelper: '將停止使用面板設定中維護的系統代理存取子節點，是否繼續？',\n            syncInfo: '資料同步,',\n            syncHelper: '當主節點資料發生變化時，即時同步到該子節點,',\n            syncBackupAccount: '備份帳號設定',\n            syncWithMaster: '升級為專業版後，將預設同步所有資料，可在節點管理中手動調整同步策略。',\n            syncProxy: '系統代理設定',\n            syncProxyHelper: '同步系統代理設定需要重新啟動 Docker',\n            syncProxyHelper1: '重新啟動 Docker 可能會影響目前正在執行的容器服務。',\n            syncProxyHelper2: '可前往 容器 - 設定 頁面手動重新啟動。',\n            syncProxyHelper3: '同步系統代理設定需要重新啟動 Docker，重新啟動可能會影響目前正在執行的容器服務',\n            syncProxyHelper4: '同步系統代理設定需要重新啟動 Docker，可稍後前往 容器 - 設定 頁面手動重新啟動。',\n            syncCustomApp: '同步自訂應用倉庫',\n            syncAlertSetting: '系統告警設定',\n            syncNodeInfo: '節點基礎資料,',\n            nodeSyncHelper: '節點資訊同步將同步以下資訊：',\n            nodeSyncHelper1: '1. 公用的備份帳號資訊',\n            nodeSyncHelper2: '2. 主節點與子節點的連接資訊',\n            nodeCheck: '可用性檢查',\n            checkSSH: '檢查節點 SSH 連接',\n            checkUserPermission: '檢查節點使用者權限',\n            isNotRoot: '偵測到該節點不支援免密 sudo，且目前為非 root 使用者',\n            checkLicense: '檢查節點許可證狀態',\n            checkService: '檢查節點已存在服務資訊',\n            checkPort: '檢查節點埠可達',\n            panelExist: '檢查到該節點正在執行 1Panel V1 服務，請先透過遷移腳本升級至 V2 後再進行新增。',\n            coreExist:\n                '目前節點已作為主節點啟用，無法直接作為從節點新增。請先將其降級為從節點後再新增，詳細可參考檔案。',\n            agentExist: '檢查到該節點已安裝 1panel-agent，繼續新增將保留現有資料，僅取代 1panel-agent 服務。',\n            agentNotExist: '偵測到該節點未安裝 1panel-agent，無法直接編輯該節點資訊，請先刪除後重新新增。',\n            oldDataExist: '檢查到該節點存在 1Panel V2 歷史資料，將使用以下資訊覆蓋目前設定',\n            errLicense: '檢查到該節點綁定的許可證不可用，請檢查後重試',\n            errNodePort: '檢查到節點埠 [ {0} ] 無法存取，請檢查防火牆或安全組是否已放行該埠。',\n            reinstallHelper: '重新安裝節點 {0}，是否繼續？',\n            unhealthyCheck: '異常檢查',\n            fixOperation: '修復方案',\n            checkName: '檢查項目',\n            checkSSHConn: '檢查 SSH 連接可用性',\n            fixSSHConn: '手動編輯節點，確認連接資訊',\n            checkConnInfo: '檢查 Agent 連接資訊',\n            checkStatus: '檢查節點服務可用性',\n            fixStatus: '執行 \"systemctl status 1panel-agent.service\" 以檢查服務是否已啟動。',\n            checkAPI: '檢查節點 API 可用性',\n            fixAPI: '檢查節點日誌，確認防火牆埠是否正常放行。',\n            forceDelete: '強制刪除',\n            operateHelper: '將對以下節點執行 {0} 操作，是否繼續？',\n            operatePanelHelper: '將對以下面板進行 {0} 操作，是否繼續？',\n            uninstall: '刪除節點資料',\n            uninstallHelper: '此操作將刪除與節點相關的所有 1Panel 資料，請謹慎選擇',\n            forceDeleteHelper: '強制刪除將忽略節點刪除錯誤並刪除資料庫中繼資料',\n            baseDir: '安裝目錄',\n            baseDirHelper: '安裝目錄為空時，預設安裝於 /opt 目錄下',\n            nodePort: '節點埠',\n            offline: '離線模式',\n            freeCount: '免費額度 [{0}]',\n            offlineHelper: '當節點處於離線環境時使用',\n            appUpgrade: '應用程式升級',\n            appUpgradeHelper: '有 {0} 個應用程式需要升級',\n        },\n        customApp: {\n            name: '自訂應用倉庫',\n            appStoreType: '應用商店包來源',\n            appStoreUrl: '倉庫地址',\n            local: '本機路徑',\n            remote: '遠端連結',\n            imagePrefix: '映像前綴',\n            imagePrefixHelper:\n                '作用：自訂映像前綴，修改 compose 檔案中的映像欄位，例：當映像前綴設定為 1panel/custom 時，MaxKB 的映像欄位將變更為 1panel/custom/maxkb:v1.10.0',\n            closeHelper: '是否取消使用自訂應用倉庫',\n            appStoreUrlHelper: '僅支援 .tar.gz 格式',\n            postNode: '同步至子節點',\n            postNodeHelper: '把自訂商店包同步至子節點的安裝目錄下的 tmp/customApp/apps.tar.gz 中',\n            nodes: '節點',\n            selectNode: '選擇節點',\n            selectNodeError: '請選擇節點',\n            licenseHelper: '專業版支援自訂應用倉庫功能',\n            databaseHelper: '應用關聯資料庫，請選擇目標節點資料庫',\n            nodeHelper: '不能選擇當前節點',\n            migrateHelper: '僅支援遷移單體應用和只關聯 MySQL、MariaDB、PostgreSQL 資料庫的應用',\n            opensslHelper: '如果使用加密備份，兩個節點之間的 OpenSSL 版本必須保持一致，否則可能導致遷移失敗。',\n            installApp: '批量安裝',\n            installAppHelper: '批量安裝應用到選擇的節點中',\n        },\n        alert: {\n            isAlert: '是否告警',\n            alertCount: '告警次數',\n            clamHelper: '掃描到感染檔案時觸發告警',\n            cronJobHelper: '定時任務執行失敗時將觸發告警',\n            licenseHelper: '專業版支援更多告警功能',\n            alertCountHelper: '每日最大告警次數',\n            alert: '簡訊告警',\n            logs: '告警日誌',\n            list: '告警清單',\n            addTask: '建立簡訊告警',\n            editTask: '編輯簡訊告警',\n            alertMethod: '告警方式',\n            alertMsg: '告警內容',\n            alertRule: '告警規則',\n            titleSearchHelper: '請輸入告警標題，支援模糊搜尋',\n            taskType: '告警類型',\n            ssl: '網站憑證到期',\n            siteEndTime: '網站到期',\n            panelPwdEndTime: '面板密碼到期',\n            panelUpdate: '面板新版本提醒',\n            cpu: '面板伺服器 CPU 佔用過高告警',\n            memory: '面板伺服器記憶體佔用過高告警',\n            load: '面板伺服器負載佔用過高告警',\n            disk: '面板伺服器磁碟佔用過高告警',\n            website: '網站',\n            certificate: '憑證',\n            remainingDays: '剩餘天數',\n            sendCount: '告警次數',\n            sms: '簡訊通知',\n            wechat: '微信公眾號',\n            dingTalk: '釘釘通知',\n            feiShu: '飛書通知',\n            mail: '信箱通知',\n            weCom: '企業微信',\n            sendCountRulesHelper: '到期前發送告警的總數（每日僅發送一次）',\n            panelUpdateRulesHelper: '新版本發送告警總數（每日僅發送一次）',\n            oneDaySendCountRulesHelper: '每日發送告警的總數',\n            siteEndTimeRulesHelper: '永不過期的網站，不觸發告警',\n            autoRenewRulesHelper: '憑證開啟自動續期，剩餘天數小於31天，不觸發告警',\n            panelPwdEndTimeRulesHelper: '面板未設定密碼到期時長，不能使用密碼到期告警',\n            sslRulesHelper: '所有ssl憑證',\n            diskInfo: '磁碟資訊',\n            monitoringType: '監測類型',\n            autoRenew: '自動續簽',\n            useDisk: '佔用磁碟',\n            usePercentage: '佔用百分比',\n            changeStatus: '狀態修改',\n            disableMsg: '停止告警任務會導致該任務不再發送告警消息。 是否繼續？',\n            enableMsg: '啟用告警任務會讓該任務發送告警消息。 是否繼續？',\n            useExceed: '使用超過',\n            useExceedRulesHelper: '使用超過指定值觸發告警',\n            cpuUseExceedAvg: 'CPU 平均使用率超過指定值',\n            memoryUseExceedAvg: '記憶體平均使用率超過指定值',\n            loadUseExceedAvg: '負載平均使用率超過指定值',\n            cpuUseExceedAvgHelper: '指定時間內 CPU 平均使用率超過指定值',\n            memoryUseExceedAvgHelper: '指定時間內記憶體平均使用率超過指定值',\n            loadUseExceedAvgHelper: '指定時間內負載平均使用率超過指定值',\n            resourceAlertRulesHelper: '注意：30分鐘內持續告警只發送一次',\n            specifiedTime: '指定時間',\n            deleteTitle: '刪除告警',\n            deleteMsg: '是否確認刪除告警任務？',\n            allSslTitle: '所有網站憑證到期告警',\n            sslTitle: '網站「{0}」憑證到期告警',\n            allSiteEndTimeTitle: '所有網站到期告警',\n            siteEndTimeTitle: '網站「{0}」到期告警',\n            panelPwdEndTimeTitle: '面板密碼到期告警',\n            panelUpdateTitle: '面板新版本提醒',\n            cpuTitle: 'CPU 佔用過高告警',\n            memoryTitle: '記憶體佔用過高告警',\n            loadTitle: '負載佔用過高告警',\n            diskTitle: '掛載目錄「{0}」的磁碟佔用過高告警',\n            allDiskTitle: '磁碟佔用過高告警',\n            timeRule: '剩餘時間小於{0}天（如未處理，次日會重新發送）',\n            panelUpdateRule: '偵測到面板有新版本時發送一次（如未處理，次日會重新發送）',\n            avgRule: '{0}分鐘內平均{1}佔用超過{2}%觸發，每天發送{3}次',\n            diskRule: '掛載目錄「{0}」的磁碟佔用超過{1}{2}觸發，每天發送{3}次',\n            allDiskRule: '磁碟佔用超過{0}{1}觸發，每天發送{2}次',\n            cpuName: ' CPU ',\n            memoryName: '記憶體',\n            loadName: '負載',\n            diskName: '磁碟',\n            syncAlertInfo: '同步告警資訊',\n            syncAlertInfoMsg: '是否同步告警資訊內容狀態？',\n            pushError: '推送失敗',\n            pushSuccess: '推送成功',\n            syncError: '同步失敗',\n            success: '告警成功',\n            pushing: '推送中...',\n            error: '告警失敗',\n            cleanLog: '清空日誌',\n            cleanAlertLogs: '清空告警日誌',\n            daily: '當日第 {0} 次告警',\n            cumulative: '累計第 {0} 次告警',\n            clams: '病毒掃描告警',\n            taskName: '任務名稱',\n            cronJobType: '任務類型',\n            clamPath: '掃描目錄',\n            cronjob: '計劃任務執行{0}異常',\n            app: '備份應用',\n            web: '備份網站',\n            database: '備份資料庫',\n            directory: '備份目錄',\n            log: '備份日誌',\n            snapshot: '系統快照',\n            clamsRulesHelper: '需要開啟告警的病毒掃描任務',\n            cronJobRulesHelper: '需要設定此類型的計劃任務',\n            clamsTitle: '病毒掃描「{0}」任務偵測到感染檔案告警',\n            cronJobAppTitle: '計劃任務-備份應用「{0}」任務失敗告警',\n            cronJobWebsiteTitle: '計劃任務-備份網站「{0}」任務失敗告警',\n            cronJobDatabaseTitle: '計劃任務-備份資料庫「{0}」任務失敗告警',\n            cronJobDirectoryTitle: '計劃任務-備份目錄「{0}」任務失敗告警',\n            cronJobLogTitle: '計劃任務-備份日誌「{0}」任務失敗告警',\n            cronJobSnapshotTitle: '計劃任務-系統快照「{0}」任務失敗告警',\n            cronJobShellTitle: '計劃任務-Shell 腳本「 {0} 」任務失敗告警',\n            cronJobCurlTitle: '計劃任務-瀏覽 URL「 {0} 」任務失敗告警',\n            cronJobCutWebsiteLogTitle: '計劃任務-切割網站日誌「 {0} 」任務失敗告警',\n            cronJobCleanTitle: '計劃任務-快取清理「 {0} 」任務失敗告警',\n            cronJobNtpTitle: '計劃任務-同步伺服器時間「 {0} 」任務失敗告警',\n            clamsRule: '病毒掃描任務偵測到感染檔案告警，每天發送{0}次',\n            cronJobAppRule: '備份應用任務失敗告警，每天發送{0}次',\n            cronJobWebsiteRule: '備份網站任務失敗告警，每天發送{0}次',\n            cronJobDatabaseRule: '備份資料庫任務失敗告警，每天發送{0}次',\n            cronJobDirectoryRule: '備份目錄任務失敗告警，每天發送{0}次',\n            cronJobLogRule: '備份日誌任務失敗告警，每天發送{0}次',\n            cronJobSnapshotRule: '系統快照任務失敗告警，每天發送{0}次',\n            cronJobShellRule: 'Shell 腳本任務失敗告警，每天發送{0}次',\n            cronJobCurlRule: '瀏覽 URL 任務失敗告警，每天發送{0}次',\n            cronJobCutWebsiteLogRule: '切割網站日誌任務失敗告警，每天發送{0}次',\n            cronJobCleanRule: '快取清理任務失敗告警，每天發送{0}次',\n            cronJobNtpRule: '同步伺服器時間任務失敗告警，每天發送{0}次',\n            alertSmsHelper: '簡訊額度：總量{0}條，已使用{1}條',\n            goBuy: '去購買',\n            phone: '手機號碼',\n            phoneHelper: '請請填寫真實的手機號碼，以免不能正常接收告警資訊',\n            dailyAlertNum: '每日告警次數',\n            dailyAlertNumHelper: '每日告警通知的總次數，最多通知 100 次',\n            timeRange: '時間範圍',\n            sendTimeRange: '可發送時間範圍',\n            sendTimeRangeHelper: '可推送{0}時間範圍',\n            to: '至',\n            startTime: '開始時間',\n            endTime: '結束時間',\n            defaultPhone: '預設使用與許可證綁定的帳戶手機號碼',\n            noticeAlert: '通知告警',\n            resourceAlert: '資源告警',\n            agentOfflineAlertHelper: '當節點啟用離線告警時，主節點將每半小時掃描並執行一次告警任務。',\n            offline: '離線告警',\n            offlineHelper: '設為離線告警時，主節點將每半小時掃描並執行一次告警任務。',\n            offlineOff: '開啟離線告警',\n            offlineOffHelper: '開啟離線告警後，主節點將每半小時掃描並執行一次告警任務。',\n            offlineClose: '關閉離線告警',\n            offlineCloseHelper: '關閉離線告警後，需由子節點自行告警，請確保子節點網路通暢，以免告警失敗。',\n            alertNotice: '警報通知',\n            methodConfig: '發送方式設定',\n            commonConfig: '全域設定',\n            smsConfig: '簡訊',\n            smsConfigHelper: '設定簡訊通知號碼',\n            emailConfig: '郵件',\n            emailConfigHelper: '設定 SMTP 郵件發送服務',\n            deleteConfigTitle: '刪除警報設定',\n            deleteConfigMsg: '是否確定刪除此警報設定？',\n            test: '測試',\n            alertTestOk: '測試通知成功',\n            alertTestFailed: '測試通知失敗',\n            displayName: '顯示名稱',\n            sender: '寄件地址',\n            password: '密碼',\n            host: 'SMTP 伺服器',\n            port: '連接埠',\n            encryption: '加密方式',\n            recipient: '收件者',\n            licenseTime: '授權到期提醒',\n            licenseTimeTitle: '授權到期提醒',\n            displayNameHelper: '郵件的寄件人顯示名稱',\n            senderHelper: '用於發送郵件的電子信箱地址',\n            passwordHelper: '郵件服務的授權碼',\n            hostHelper: 'SMTP 伺服器地址，例：smtp.qq.com',\n            portHelper: 'SSL 通常為 465，TLS 通常為 587',\n            sslHelper: '若 SMTP 連接埠為 465，通常需要啟用 SSL',\n            tlsHelper: '若 SMTP 連接埠為 587，通常需要啟用 TLS',\n            triggerCondition: '觸發條件',\n            loginFail: '內，登入失敗',\n            nodeException: '節點異常告警',\n            licenseException: '許可證異常告警',\n            panelLogin: '面板登入異常告警',\n            sshLogin: 'SSH 登入異常告警',\n            panelIpLogin: '面板登入 IP 異常告警',\n            sshIpLogin: 'SSH 登入 IP 異常告警',\n            ipWhiteListHelper: '在白名單中的 IP 不受規則限制，成功登入時也不會產生警報',\n            nodeExceptionRule: '節點異常告警，每天發送 {0} 次',\n            licenseExceptionRule: '許可證異常告警，每天發送 {0} 次',\n            panelLoginRule: '面板登入告警，每天發送 {0} 次',\n            sshLoginRule: 'SSH 登入告警，每天發送 {0} 次',\n            userNameHelper: '使用者名稱為空時，將預設使用寄件者地址',\n            alertConfigHelper: '設定告警通知通道，用於接收面板訊息推送',\n            weComConfigHelper: '企業微信告警通知設定',\n            wechatConfigHelper: '微信公眾號告警通知設定',\n            dingTalkConfigHelper: '釘釘告警通知設定',\n            feiShuConfigHelper: '飛書告警通知設定',\n            webhookName: '機器人名稱',\n            webhookUrl: 'Webhook 位址',\n            alertConfigProHelper: '專業版額外支援企業微信、釘釘、飛書及簡訊告警。',\n        },\n        theme: {\n            lingXiaGold: '凌霞金',\n            classicBlue: '經典藍',\n            freshGreen: '清新綠',\n            customColor: '自訂主題色',\n            setDefault: '復原預設',\n            setDefaultHelper: '即將復原主題配色到初始狀態，是否繼續？',\n            setHelper: '即將儲存目前選定的主題配色，是否繼續？',\n        },\n        app: {\n            app: 'APP',\n            title: '面板別名',\n            titleHelper: '面板別名用於 APP 端的顯示（預設面板別名）',\n            qrCode: '二維碼',\n            apiStatusHelper: '面板 APP 需要開啟 API 介面功能',\n            apiInterfaceHelper: '支援面板 API 介面存取功能（面板 APP 需要開啟該功能）',\n            apiInterfaceHelper1:\n                '面板 APP 存取需將使用者新增至白名單，非固定 IP 建議新增 0.0.0.0/0（所有 IPv4），::/0（所有 IPv6）',\n            qrCodeExpired: '重新整理時間',\n            apiLeakageHelper: '請勿洩露二維碼，確保僅在受信任的環境中使用',\n        },\n        exchange: {\n            exchange: '檔案對傳',\n            exchangeConfirm: '是否將 {0} 節點檔案/資料夾 {1} 傳輸到 {2} 節點 {3} 目錄？',\n        },\n        cluster: {\n            cluster: '應用高可用',\n            name: '叢集名稱',\n            addCluster: '新增叢集',\n            installNode: '安裝節點',\n            master: '主節點',\n            slave: '從節點',\n            replicaStatus: '主從狀態',\n            unhealthyDeleteError: '安裝節點狀態異常，請在節點列表檢查後重試',\n            replicaStatusError: '狀態取得異常，請檢查主節點。',\n            masterHostError: '主節點 IP 不能為 127.0.0.1',\n        },\n    },\n};\nexport default {\n    ...fit2cloudTwLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/lang/modules/zh.ts",
    "content": "import fit2cloudZhLocale from 'fit2cloud-ui-plus/src/locale/lang/zh-cn';\n\nconst message = {\n    commons: {\n        true: '是',\n        false: '否',\n        example: '例：',\n        fit2cloud: '飞致云',\n        lingxia: '凌霞',\n        colon: '：',\n        button: {\n            run: '运行',\n            prev: '上一步',\n            next: '下一步',\n            create: '创建',\n            add: '添加',\n            save: '保存',\n            set: '设置',\n            sync: '同步',\n            delete: '删除',\n            edit: '编辑',\n            enable: '启用',\n            disable: '停用',\n            confirm: '确认',\n            cancel: '取消',\n            reset: '重置',\n            setDefault: '恢复默认',\n            restart: '重启',\n            conn: '连接',\n            disConn: '断开',\n            clean: '清空',\n            login: '登录',\n            close: '关闭',\n            stop: '关闭',\n            start: '开启',\n            view: '详情',\n            watch: '追踪',\n            handle: '执行',\n            clone: '克隆',\n            expand: '展开',\n            collapse: '收起',\n            log: '日志',\n            back: '返回',\n            backup: '备份',\n            recover: '恢复',\n            retry: '重试',\n            upload: '上传',\n            download: '下载',\n            init: '初始化',\n            verify: '验证',\n            saveAndEnable: '保存并启用',\n            import: '导入',\n            export: '导出',\n            power: '授权',\n            search: '搜索',\n            refresh: '刷新',\n            get: '获取',\n            upgrade: '升级',\n            update: '更新',\n            updateNow: '立即更新',\n            ignore: '忽略升级',\n            copy: '复制',\n            random: '随机密码',\n            install: '安装',\n            uninstall: '卸载',\n            fullscreen: '网页全屏',\n            quitFullscreen: '退出网页全屏',\n            showAll: '显示所有',\n            hideSome: '隐藏部分',\n            agree: '同意',\n            notAgree: '不同意',\n            preview: '预览',\n            open: '打开',\n            notSave: '不保存',\n            createNewFolder: '新建文件夹',\n            createNewFile: '新建文件',\n            helpDoc: '帮助文档',\n            bind: '绑定',\n            unbind: '解绑',\n            cover: '覆盖',\n            skip: '跳过',\n            fix: '修复',\n            down: '停止',\n            up: '启动',\n            sure: '确定',\n            show: '显示',\n            hide: '隐藏',\n            visit: '访问',\n            migrate: '迁移',\n        },\n        operate: {\n            start: '启动',\n            stop: '停止',\n            restart: '重启',\n            reload: '重载',\n            rebuild: '重建',\n            sync: '同步',\n            up: '启动',\n            down: '停止',\n            delete: '删除',\n        },\n        search: {\n            timeStart: '开始时间',\n            timeEnd: '结束时间',\n            date: '日期',\n        },\n        table: {\n            all: '所有',\n            name: '名称',\n            type: '类型',\n            status: '状态',\n            group: '分组',\n            default: '默认',\n            createdAt: '创建时间',\n            date: '时间',\n            updatedAt: '更新时间',\n            operate: '操作',\n            message: '信息',\n            description: '描述',\n            interval: '耗时',\n            user: '用户',\n            title: '标题',\n            port: '端口',\n            protocol: '协议',\n            noRefresh: '不刷新',\n            selectColumn: '选择列',\n            local: '本地',\n            serialNumber: '序号',\n            manageGroup: '管理分组',\n            backToList: '返回列表',\n            keepEdit: '继续编辑',\n        },\n        loadingText: {\n            Upgrading: '系统升级中，请稍候...',\n            Restarting: '系统重启中，请稍候...',\n            Recovering: '快照恢复中，请稍候...',\n            Rollbacking: '快照回滚中，请稍候...',\n        },\n        msg: {\n            noneData: '暂无数据',\n            delete: '删除 操作不可回滚，是否继续？',\n            clean: '清空 操作不可回滚，是否继续？',\n            closeDrawerHelper: '系统可能不会保存您所做的更改，是否继续？',\n            deleteSuccess: '删除成功',\n            loginSuccess: '登录成功',\n            operationSuccess: '操作成功',\n            copySuccess: '复制成功',\n            notSupportOperation: '不支持的当前操作',\n            requestTimeout: '请求超时，请稍后重试',\n            infoTitle: '提示',\n            notRecords: '当前任务未产生执行记录',\n            sureLogOut: '您是否确认退出登录?',\n            createSuccess: '创建成功',\n            updateSuccess: '更新成功',\n            uploadSuccess: '上传成功',\n            operateConfirm: '如果确认操作，请手动输入 ',\n            inputOrSelect: '请选择或输入',\n            copyFailed: '复制失败',\n            operatorHelper: '将对以下{0}进行 {1} 操作，是否继续？',\n            backupSuccess: '备份成功',\n            restoreSuccess: '备份成功',\n            notFound: '抱歉，您访问的页面不存在',\n            unSupportType: '不支持当前文件类型！',\n            unSupportSize: '上传文件超过 {0}M，请确认！',\n            fileExist: '当前文件夹已存在该文件，不支持重复上传！',\n            fileNameErr: '仅支持上传名称包含英文、中文、数字或 .-_ ,长度 1-256 位的文件',\n            confirmNoNull: '请确认 {0} 值不为空',\n            errPort: '错误的端口信息，请确认！',\n            remove: '移出',\n            backupHelper: '当前操作将对 {0} 进行备份，是否继续？',\n            recoverHelper: '将从 {0} 文件进行恢复，该操作不可回滚，是否继续？',\n            refreshSuccess: '刷新成功',\n            rootInfoErr: '已经是根目录了',\n            resetSuccess: '重置成功',\n            creatingInfo: '正在创建，无需此操作',\n            installSuccess: '安装成功',\n            uninstallSuccess: '卸载成功',\n            offlineTips: '离线版不支持此操作',\n            errImportFormat: '导入数据或格式异常，请检查后重试！',\n            importHelper: '导入冲突或重复数据时，将以导入内容为标准，更新原数据库数据。',\n            errImport: '文件内容异常：',\n        },\n        login: {\n            username: '用户名',\n            password: '密码',\n            errorAuthInfo: '您输入的用户名或密码不正确，请重新输入！',\n            errorMfaInfo: '错误的验证信息，请重试！',\n            captchaHelper: '验证码',\n            errorCaptcha: '验证码错误！',\n            notSafe: '暂无权限访问',\n            safeEntrance1: '当前环境已经开启了安全入口登录',\n            safeEntrance2: '在 SSH 终端输入以下命令来查看面板入口: 1pctl user-info',\n            errIP1: '当前环境已经开启了授权 IP 访问',\n            errDomain1: '当前环境已经开启了访问域名绑定',\n            errHelper: '可在 SSH 终端输入以下命令来重置绑定信息: ',\n            mfaTitle: 'MFA 认证',\n            mfaCode: 'MFA 验证码',\n            passkey: '使用通行密钥',\n            passkeyFailed: '通行密钥登录失败，请重试',\n            passkeyNotSupported: '当前浏览器或环境不支持通行密钥',\n            passkeyToPassword: '无法使用通行密钥? 使用账号密码登录',\n            licenseHelper: '《飞致云社区软件许可协议》',\n            errorAgree: '请点击同意社区软件许可协议',\n            agreeTitle: '服务协议及隐私保护',\n            agreeContent:\n                '为了更好的保障您的合法权益，请您阅读并同意以下协议 &laquo; <a href=\"https://www.fit2cloud.com/legal/licenses.html\" target=\"_blank\"> 飞致云社区软件许可协议 </a> &raquo;',\n            logout: '退出登录',\n        },\n        rule: {\n            username: '请输入用户名',\n            password: '请输入密码',\n            rePassword: '密码不一致，请检查后重新输入',\n            requiredInput: '请填写必填项',\n            requiredSelect: '请选择必选项',\n            illegalChar: '暂不支持注入字符 & ; $ \\' ` ( ) \" > < |',\n            illegalInput: '输入框中存在不合法字符',\n            commonName: '支持非特殊字符开头,英文、中文、数字、.-和_,长度1-128',\n            userName: '支持非特殊字符开头、英文、中文、数字和_,长度3-30',\n            simpleName: '支持非下划线开头，英文、数字、_,长度3-30',\n            simplePassword: '支持非下划线开头，英文、数字、_,长度1-30',\n            dbName: '支持非特殊字符开头，英文、中文、数字、.-_,长度1-64',\n            composeName: '支持非特殊字符开头，小写英文、数字、-和_,长度1-256',\n            imageName: '支持非特殊字符开头、英文、数字、:@/.-_,长度1-256',\n            volumeName: '支持英文、数字、.-和_,长度2-30',\n            supervisorName: '支持非特殊字符开头,英文、数字、-和_,长度1-128',\n            complexityPassword: '请输入长度为 8-30 位且包含字母、数字、特殊字符至少两项的密码组合',\n            linuxName: '长度1-128，名称不能含有{0}等符号',\n            email: '请输入正确的邮箱',\n            number: '请输入正确的数字',\n            integer: '请输入正确的正整数',\n            ip: '请输入正确的 IP 地址',\n            host: '请输入正确的 IP 或域名',\n            hostHelper: '支持输入 ip 或域名',\n            port: '请输入正确的端口,1-65535',\n            domain: '域名格式错误',\n            databaseName: '支持英文、数字、_,长度1-30',\n            numberRange: '数字范围: {0} - {1}',\n            paramName: '支持英文、数字、.-和_,长度2-64',\n            paramComplexity: '支持英文、数字、{0},长度6-128，特殊字符不能在首尾',\n            paramUrlAndPort: '格式为 http(s)://(域名/ip):(端口)',\n            nginxDoc: '仅支持英文大小写，数字，和.',\n            appName: '支持英文、数字、-和_,长度2-30,并且不能以-_开头和结尾',\n            containerName: '支持字母、数字、_-和.,不能以-_或.开头,长度2-128',\n            mirror: '支持以 http(s):// 开头，英文大小写，数字，. / 和 - 的镜像加速地址，且不能有空行',\n            disableFunction: '仅支持字母、下划线和,',\n            leechExts: '仅支持字母数字和,',\n            paramSimple: '支持小写字母和数字,长度1-128',\n            filePermission: '权限错误',\n            formatErr: '格式错误，检查后重试',\n            phpExtension: '仅支持 , _ 小写英文和数字',\n            paramHttp: '必须以 http:// 或 https:// 开头',\n            phone: '手机号码格式不正确',\n            authBasicPassword: '支持字母、数字以及常见特殊字符，长度1-72',\n            length128Err: '长度不能超过128位',\n            maxLength: '长度不能超过 {0} 位',\n            alias: '支持英文、数字、-和_,长度1-128,并且不能以-_开头和结尾',\n        },\n        res: {\n            paramError: '请求失败,请稍后重试!',\n            forbidden: '当前用户无权限',\n            serverError: '服务异常',\n            notFound: '资源不存在',\n            commonError: '请求失败',\n        },\n        service: {\n            serviceNotStarted: '当前未启动 {0} 服务',\n        },\n        status: {\n            running: '已启动',\n            done: '已完成',\n            scanFailed: '未完成',\n            success: '成功',\n            waiting: '请等待',\n            waitForUpgrade: '等待升级',\n            failed: '失败',\n            stopped: '已停止',\n            error: '失败',\n            created: '已创建',\n            restarting: '重启中',\n            uploading: '上传中',\n            unhealthy: '异常',\n            removing: '移除中',\n            paused: '已暂停',\n            exited: '已停止',\n            dead: '已结束',\n            installing: '安装中',\n            enabled: '已启用',\n            disabled: '已停止',\n            normal: '正常',\n            building: '制作镜像中',\n            upgrading: '升级中',\n            pending: '待编辑',\n            rebuilding: '重建中',\n            deny: '已屏蔽',\n            accept: '已放行',\n            used: '已使用',\n            unused: '未使用',\n            starting: '启动中',\n            recreating: '重建中',\n            creating: '创建中',\n            init: '等待申请',\n            ready: '正常',\n            applying: '申请中',\n            uninstalling: '卸载中',\n            lost: '已失联',\n            bound: '已绑定',\n            unbind: '未绑定',\n            exceptional: '异常',\n            free: '空闲',\n            enable: '已启用',\n            disable: '已停止',\n            deleted: '已删除',\n            downloading: '下载中',\n            packing: '打包中',\n            sending: '下发中',\n            healthy: '正常',\n            executing: '执行中',\n            installerr: '安装失败',\n            applyerror: '申请失败',\n            systemrestart: '中断',\n            starterr: '启动失败',\n            uperr: '启动失败',\n            new: '新',\n            conflict: '冲突',\n            duplicate: '重复',\n            unexecuted: '未执行',\n        },\n        units: {\n            second: '秒',\n            minute: '分钟',\n            hour: '小时',\n            day: '天',\n            week: '周',\n            month: '月',\n            year: '年',\n            time: '次',\n            core: '核',\n            secondUnit: '秒',\n            minuteUnit: '分钟',\n            hourUnit: '小时',\n            dayUnit: '天',\n            millisecond: '毫秒',\n            semicolon: '；',\n        },\n        log: {\n            noLog: '暂无日志',\n        },\n    },\n    menu: {\n        home: '概览',\n        apps: '应用商店',\n        website: '网站',\n        project: '项目',\n        config: '配置',\n        ssh: 'SSH 管理',\n        firewall: '防火墙',\n        filter: '过滤器',\n        ssl: '证书',\n        database: '数据库',\n        aiTools: 'AI',\n        mcp: 'MCP',\n        container: '容器',\n        cronjob: '计划任务',\n        system: '系统',\n        files: '文件',\n        monitor: '监控',\n        terminal: '终端',\n        settings: '面板设置',\n        toolbox: '工具箱',\n        logs: '日志审计',\n        runtime: '运行环境',\n        processManage: '进程管理',\n        process: '进程',\n        network: '网络',\n        supervisor: '进程守护',\n        tamper: '防篡改',\n        app: '应用',\n        msgCenter: '任务中心',\n        disk: '磁盘管理',\n    },\n    home: {\n        dir: '目录',\n        alias: '别名',\n        quickDir: '快捷目录',\n        minQuickJump: '请至少设置一个快速跳转入口！',\n        maxQuickJump: '最多可设置四个快速跳转入口！',\n        database: '数据库 - 所有',\n        restart_1panel: '重启面板',\n        restart_system: '重启服务器',\n        operationSuccess: '操作成功，正在重启，请稍后手动刷新浏览器！',\n        entranceHelper: '设置安全入口有利于提高系统的安全性，如有需要，前往 面板设置-安全 中，启用安全入口',\n        appInstalled: '已安装应用',\n        systemInfo: '系统信息',\n        hostname: '主机名称',\n        platformVersion: '发行版本',\n        kernelVersion: '内核版本',\n        kernelArch: '系统类型',\n        network: '流量',\n        io: '磁盘 IO',\n        ip: '主机地址',\n        proxy: '系统代理',\n        baseInfo: '基本信息',\n        totalSend: '总发送',\n        totalRecv: '总接收',\n        rwPerSecond: '读写次数',\n        ioDelay: '读写延迟',\n        uptime: '启动时间',\n        runningTime: '运行时间',\n        mem: '系统内存',\n        swapMem: 'Swap 分区',\n\n        runSmoothly: '运行流畅',\n        runNormal: '运行正常',\n        runSlowly: '运行缓慢',\n        runJam: '运行堵塞',\n\n        core: '物理核心',\n        logicCore: '逻辑核心',\n        corePercent: '核心使用率',\n        cpuFrequency: 'CPU 频率',\n        cpuDetailedPercent: 'CPU 占用',\n        cpuUser: '用户态',\n        cpuSystem: '内核态',\n        cpuIdle: '空闲',\n        cpuIrq: '硬中断',\n        cpuSoftirq: '软中断',\n        cpuSteal: '被VM抢占',\n        cpuTop: 'CPU 占用率 Top5 的进程信息',\n        memTop: '内存占用率 Top5 的进程信息',\n        loadAverage: '最近 {0} 分钟平均负载',\n        load: '负载',\n        mount: '挂载点',\n        fileSystem: '文件系统',\n        total: '总数',\n        used: '已用',\n        cache: '缓存',\n        free: '空闲',\n        shard: '共享',\n        available: '可用',\n        percent: '使用率',\n\n        networkCard: '网卡',\n        disk: '磁盘',\n\n        memo: '备忘录',\n        memoPlaceholder: '点击编辑按钮启用编辑',\n        carouselSetting: '轮播设置',\n        tooltipSensitiveInfo: '显示/隐藏敏感信息',\n    },\n    tabs: {\n        more: '更多',\n        hide: '收起',\n        closeLeft: '关闭左侧',\n        closeRight: '关闭右侧',\n        closeOther: '关闭其它',\n        closeAll: '关闭所有',\n    },\n    header: {},\n    database: {\n        manage: '管理',\n        deleteBackupHelper: '同时删除数据库备份',\n        delete: '删除操作无法回滚，请输入 \"',\n        deleteHelper: '\" 删除此数据库',\n        noMysql: '数据库服务 (MySQL 或 MariaDB)',\n        noPostgresql: '数据库服务 PostgreSQL',\n        goUpgrade: '去应用列表升级',\n        goInstall: '去应用商店安装',\n        isDelete: '已删除',\n        permission: '权限',\n        format: '字符集',\n        collation: '排序规则',\n        collationHelper: '为空则使用 {0} 字符集的默认排序规则',\n        permissionForIP: '指定 IP',\n        permissionAll: '所有人(%)',\n        localhostHelper: '将容器部署的数据库权限配置为 localhost 会导致容器外部无法访问，请谨慎选择！',\n        databaseConnInfo: '连接信息',\n        rootPassword: 'root 密码',\n        serviceName: '服务名称',\n        serviceNameHelper: '用于同一 network 下的容器间访问',\n        backupList: '备份列表',\n        loadBackup: '导入备份',\n        localUpload: '本地上传',\n        hostSelect: '服务器选择',\n        selectHelper: '是否确认导入备份文件 {0}？',\n        remoteAccess: '远程访问',\n        remoteHelper: '多个 ip 以逗号分隔，例：172.16.10.111,172.16.10.112',\n        remoteConnHelper: 'root 帐号远程连接 MySQL 有安全风险，开启需谨慎！',\n        changePassword: '改密',\n        changeConnHelper: '此操作将修改当前数据库 {0}，是否继续？',\n        changePasswordHelper: '当前数据库已经关联应用，修改密码将同步修改应用中数据库密码，修改后重启生效。',\n        recoverTimeoutHelper: '-1 表示不限制超时时间',\n\n        portHelper: '该端口为容器对外暴露端口，修改需要单独保存并且重启容器！',\n\n        confChange: '配置修改',\n        confNotFound: '未能找到该应用配置文件，请在应用商店升级该应用至最新版本后重试！',\n\n        loadFromRemote: '从服务器同步',\n        userBind: '绑定用户',\n        pgBindHelper: '该操作用于创建新用户并将其绑定到目标数据库，暂不支持选择已存在于数据库中的用户。',\n        pgSuperUser: '超级用户',\n        loadFromRemoteHelper: '此操作将同步服务器上数据库信息到 1Panel，是否继续？',\n        passwordHelper: '无法获取，可点击修改',\n        remote: '远程',\n        remoteDB: '远程服务器',\n        createRemoteDB: '添加远程服务器',\n        unBindRemoteDB: '解绑远程服务器',\n        unBindForce: '强制解绑',\n        unBindForceHelper: '忽略解绑过程中的所有错误，确保最终操作成功',\n        unBindRemoteHelper: '解绑远程数据库只会删除绑定关系，不会直接删除远程数据库',\n        editRemoteDB: '编辑远程服务器',\n        localDB: '本地数据库',\n        address: '数据库地址',\n        version: '数据库版本',\n        userHelper: 'root 用户或拥有 root 权限的数据库用户',\n        pgUserHelper: '拥有超级管理员权限的数据库用户',\n        ssl: '使用 SSL',\n        clientKey: '客户端私钥',\n        clientCert: '客户端证书',\n        hasCA: '拥有 CA 证书',\n        caCert: 'CA 证书',\n        skipVerify: '忽略校验证书可用性检测',\n        initialDB: '初始数据库',\n\n        formatHelper: '当前数据库字符集为 {0}，字符集不一致可能导致恢复失败',\n        dropHelper: '将上传文件拖拽到此处，或',\n        clickHelper: '点击上传',\n        supportUpType:\n            '仅支持 sql、sql.gz、tar.gz、.zip 文件格式，导入的压缩文件必须保证只有一个 .sql 文件或包含 test.sql',\n\n        currentStatus: '当前状态',\n        baseParam: '基础参数',\n        performanceParam: '性能参数',\n        runTime: '启动时间',\n        connections: '总连接数',\n        bytesSent: '发送',\n        bytesReceived: '接收',\n        queryPerSecond: '每秒查询',\n        txPerSecond: '每秒事务',\n        connInfo: '活动/峰值连接数',\n        connInfoHelper: '若值过大，增加 max_connections',\n        threadCacheHit: '线程缓存命中率',\n        threadCacheHitHelper: '若过低,增加 thread_cache_size',\n        indexHit: '索引命中率',\n        indexHitHelper: '若过低,增加 key_buffer_size',\n        innodbIndexHit: 'Innodb 索引命中率',\n        innodbIndexHitHelper: '若过低,增加 innodb_buffer_pool_size',\n        cacheHit: '查询缓存命中率',\n        cacheHitHelper: '若过低,增加 query_cache_size',\n        tmpTableToDB: '创建临时表到磁盘',\n        tmpTableToDBHelper: '若过大,尝试增加 tmp_table_size',\n        openTables: '已打开的表',\n        openTablesHelper: 'table_open_cache 配置值应大于等于此值',\n        selectFullJoin: '没有使用索引的量',\n        selectFullJoinHelper: '若不为0，请检查数据表的索引是否合理',\n        selectRangeCheck: '没有索引的 JOIN 量',\n        selectRangeCheckHelper: '若不为0，请检查数据表的索引是否合理',\n        sortMergePasses: '排序后的合并次数',\n        sortMergePassesHelper: '若值过大，增加sort_buffer_size',\n        tableLocksWaited: '锁表次数',\n        tableLocksWaitedHelper: '若值过大，请考虑增加您的数据库性能',\n\n        performanceTuning: '性能调整',\n        optimizationScheme: '优化方案',\n        keyBufferSizeHelper: '用于索引的缓冲区大小',\n        queryCacheSizeHelper: '查询缓存，不开启请设为0',\n        tmpTableSizeHelper: '临时表缓存大小',\n        innodbBufferPoolSizeHelper: 'Innodb 缓冲区大小',\n        innodbLogBufferSizeHelper: 'Innodb 日志缓冲区大小',\n        sortBufferSizeHelper: '* 连接数, 每个线程排序的缓冲大小',\n        readBufferSizeHelper: '* 连接数, 读入缓冲区大小',\n        readRndBufferSizeHelper: '* 连接数, 随机读取缓冲区大小',\n        joinBufferSizeHelper: '* 连接数, 关联表缓存大小',\n        threadStackelper: '* 连接数, 每个线程的堆栈大小',\n        binlogCacheSizeHelper: '* 连接数, 二进制日志缓存大小(4096的倍数)',\n        threadCacheSizeHelper: '线程池大小',\n        tableOpenCacheHelper: '表缓存',\n        maxConnectionsHelper: '最大连接数',\n        restart: '重启数据库',\n\n        slowLog: '慢日志',\n        noData: '暂无慢日志...',\n\n        isOn: '开启',\n        longQueryTime: '阈值(秒)',\n        thresholdRangeHelper: '请输入正确的阈值(1 - 600)',\n\n        timeout: '超时时间(秒)',\n        timeoutHelper: '空闲连接超时时间，0表示不断开',\n        maxclients: '最大连接数',\n        requirepassHelper: '留空代表没有设置密码，修改需要单独保存并且重启容器！',\n        databases: '数据库数量',\n        maxmemory: '最大内存使用',\n        maxmemoryHelper: '0 表示不做限制',\n        tcpPort: '当前监听端口',\n        uptimeInDays: '已运行天数',\n        connectedClients: '连接的客户端数量',\n        usedMemory: '当前 Redis 使用的内存大小',\n        usedMemoryRss: '向操作系统申请的内存大小',\n        usedMemoryPeak: 'Redis 的内存消耗峰值',\n        memFragmentationRatio: '内存碎片比率',\n        totalConnectionsReceived: '运行以来连接过的客户端的总数量',\n        totalCommandsProcessed: '运行以来执行过的命令的总数量',\n        instantaneousOpsPerSec: '服务器每秒钟执行的命令数量',\n        keyspaceHits: '查找数据库键成功的次数',\n        keyspaceMisses: '查找数据库键失败的次数',\n        hit: '查找数据库键命中率',\n        latestForkUsec: '最近一次 fork() 操作耗费的微秒数',\n        redisCliHelper: '未检测到 redis-cli 服务，请先启用服务！',\n        redisQuickCmd: 'Redis 快速命令',\n\n        recoverHelper: '即将使用 [{0}] 对数据进行覆盖，是否继续?',\n        submitIt: '覆盖数据',\n\n        baseConf: '基础配置',\n        allConf: '全部配置',\n        restartNow: '立即重启',\n        restartNowHelper1: '修改配置后需要重启生效，若您的数据需要持久化请先执行 save 操作。',\n        restartNowHelper: '修改配置后需要重启生效。',\n\n        persistence: '持久化',\n        rdbHelper1: '秒內,插入',\n        rdbHelper2: '条数据',\n        rdbHelper3: '符合任意一个条件将会触发RDB持久化',\n        rdbInfo: '请确认规则列表中值在 1-100000 之间',\n\n        containerConn: '容器连接',\n        copyConnURL: '复制连接 URL',\n        connAddress: '地址',\n        containerConnHelper: 'PHP 运行环境/容器安装的应用使用此连接地址',\n        remoteConn: '外部连接',\n        remoteConnHelper2: '非容器环境或外部连接需使用此地址。',\n        remoteConnHelper3: '默认访问地址为主机IP，修改请前往面板设置页面的「默认访问地址」配置项。',\n        localIP: '本机 IP',\n    },\n    aiTools: {\n        agents: {\n            agents: '智能体',\n            agent: '智能体',\n            account: '模型账号',\n            noAccountHint: '选择已有模型账号，或直接创建',\n            accountCount: '模型账号 {0} 个',\n            syncAgents: '同步关联智能体',\n            syncAgentsHelper: '更新使用该模型账号的智能体 openclaw.json',\n            openclawType: 'OpenClaw',\n            copawType: 'CoPaw',\n            appVersion: '应用版本',\n            webuiPort: 'WebUI 端口',\n            allowedOrigins: '访问地址',\n            allowedOriginsHelper:\n                '一行一个完整访问地址，建议优先使用 HTTPS，例如 https://192.168.1.2:18789；未配置默认访问地址时请手动填写',\n            allowedOriginsPlaceholder: 'https://192.168.1.2:18789',\n            allowedOriginsRequired: '请至少填写一个访问地址',\n            allowedOriginsInvalid: '访问地址格式错误，请输入 http(s)://域名或IP[:端口]',\n            provider: '模型供应商',\n            apiKey: 'API Key',\n            baseUrl: 'Base URL',\n            accountModels: '模型池',\n            accountModelsHelper: '配置账号可提供给 OpenClaw 使用和切换的模型列表',\n            accountModelsRequired: '请至少配置一个模型',\n            accountModelsDuplicate: '模型池中存在重复模型，请检查后重试',\n            modelPool: '模型池',\n            modelPoolHelper: '在这里维护该模型账号的模型池，智能体创建和 OpenClaw 模型切换都会使用这些模型',\n            modelInputTypes: '输入类型',\n            reasoning: '推理模型',\n            token: 'Token',\n            manualModel: '手动输入模型',\n            verified: '验证状态',\n            verifySkipped: '不验证',\n            configTitle: '配置',\n            settingsTab: '设置',\n            securityTab: '安全',\n            otherTab: '其他',\n            timeZone: '时区',\n            browserEnabled: '浏览器开关',\n            switchModelSuccess: '模型切换成功',\n            channelsTab: '频道',\n            wecom: '企业微信',\n            dingtalk: '钉钉',\n            feishu: '飞书',\n            pluginNotInstalled: '插件未安装，请先安装插件',\n            dmPolicy: '私聊策略',\n            groupPolicy: '群组策略',\n            policyPairing: '配对码',\n            policyAllowlist: '白名单',\n            policyOpen: '开放',\n            policyDisabled: '禁用',\n            botName: '机器人名称',\n            botId: 'Bot ID',\n            appId: '应用 App ID',\n            appSecret: '应用 App Secret',\n            clientId: 'Client ID',\n            clientSecret: 'Client Secret',\n            allowFrom: '私聊白名单',\n            allowFromHelper: '一行一个发送方标识，仅在私聊策略为白名单时生效',\n            allowFromPlaceholder: '一行一个发送方标识',\n            groupAllowFrom: '群组白名单',\n            groupAllowFromHelper: '一行一个群组标识，仅在群组策略为白名单时生效',\n            groupAllowFromPlaceholder: '一行一个群组标识',\n            allowFromRequired: '请至少填写一个白名单项',\n            pairingCode: '配对码',\n            pairingCodePlaceholder: '请输入配对码',\n            approvePairing: '批准配对',\n            feishuRequired: '请填写 botName / appId / appSecret',\n            saveSuccess: '保存成功',\n            feishuSaveSuccess: '保存成功',\n            pairingCodeRequired: '请输入配对码',\n            pairingApproveSuccess: '配对成功',\n            customProviderHelper: '自定义模型供应商不验证账号是否可用',\n        },\n        model: {\n            model: '模型',\n            localModel: '本地模型',\n            create: '添加模型',\n            create_helper: '拉取 \"{0}\"',\n            ollama_doc: '您可以访问 Ollama 官网，搜索并查找更多模型。',\n            container_conn_helper: '容器间访问或连接使用此地址',\n            ollama_sync: '同步 Ollama 模型发现下列模型不存在，是否删除？',\n            from_remote: '该模型并非通过 1Panel 下载，无相关拉取日志。',\n            no_logs: '该模型的拉取日志已被删除，无法查看相关日志。',\n            vllmVersionHelper: 'FusionXpark GB 10 服务器请选择 -cu130 版本',\n        },\n        proxy: {\n            proxy: 'AI 代理增强',\n            proxyHelper1: '绑定域名并开启 HTTPS，增强传输安全性',\n            proxyHelper2: '限制 IP 访问，防止在公网暴露',\n            proxyHelper3: '开启流式传输',\n            proxyHelper4: '创建完成之后可以在网站列表中查看并管理',\n            proxyHelper5: '创建完成之后可以在应用商店 - 已安装 - ollama - 参数中取消端口外部访问以提高安全性',\n            proxyHelper6: '如需关闭代理配置，可以在网站列表中删除',\n            whiteListHelper: '限制仅白名单中的 IP 可访问',\n        },\n        gpu: {\n            gpu: 'GPU 监控',\n            gpuHelper: '当前系统未检测到 NVIDIA-SMI 或 XPU-SMI 指令，请检查后重试！',\n            process: '进程信息',\n            processCount: '进程数',\n            type: '类型',\n            typeG: '图形',\n            typeC: '计算',\n            typeCG: '计算+图形',\n            processName: '进程名称',\n            shr: '共享显存',\n            temperatureHelper: 'GPU 温度过高会导致 GPU 频率下降',\n            gpuUtil: 'GPU 使用率',\n            temperature: '温度',\n            performanceState: '性能状态',\n            powerUsage: '功耗',\n            memoryUsage: '显存使用率',\n            fanSpeed: '风扇转速',\n            power: '功率',\n            powerCurrent: '当前功率',\n            powerLimit: '功率上限',\n            memory: '显存',\n            memoryUsed: '显存使用',\n            memoryTotal: '显存总计',\n            percent: '使用率',\n\n            base: '基础信息',\n            driverVersion: '驱动版本',\n            cudaVersion: 'CUDA 版本',\n            processMemoryUsage: '显存使用',\n            performanceStateHelper: '从 P0 (最大性能) 到 P12 (最小性能)',\n            busID: '总线地址',\n            persistenceMode: '持续模式',\n            enabled: '开启',\n            disabled: '关闭',\n            persistenceModeHelper: '持续模式能更加快速地响应任务，但相应待机功耗也会增加',\n            displayActive: '显卡初始化',\n            displayActiveT: '是',\n            displayActiveF: '否',\n            ecc: '是否开启错误检查和纠正技术',\n            computeMode: '计算模式',\n            default: '默认',\n            exclusiveProcess: '进程排他',\n            exclusiveThread: '线程排他',\n            prohibited: '禁止',\n            defaultHelper: '默认: 进程可以并发执行',\n            exclusiveProcessHelper: '进程排他: 只有一个 CUDA 上下文可以使用 GPU, 但可以由多个线程共享',\n            exclusiveThreadHelper: '线程排他: 只有一个线程在 CUDA 上下文中可以使用 GPU',\n            prohibitedHelper: '禁止: 不允许进程同时执行',\n            migModeHelper: '用于创建 MIG 实例，在用户层实现 GPU 的物理隔离。',\n            migModeNA: '不支持',\n            current: '实时监控',\n            history: '历史记录',\n            notSupport: '当前版本或驱动不支持显示该参数。',\n        },\n        mcp: {\n            server: 'MCP Server',\n            baseUrl: '外部访问路径',\n            baseUrlHelper: '例如：http://192.168.1.1:8000',\n            ssePath: 'SSE 路径',\n            ssePathHelper: '例如：/sse,注意不要与其他 Server 重复',\n            environment: '环境变量',\n            envKey: '变量名',\n            envValue: '变量值',\n            externalUrl: '外部连接地址',\n            operatorHelper: '将对 {0} 进行 {1} 操作，是否继续？',\n            domain: '默认访问地址',\n            domainHelper: '例如：192.168.1.1 或 example.com',\n            bindDomain: '绑定网站',\n            commandPlaceHolder: '当前仅支持 npx 和 二进制启动的命令',\n            importMcpJson: '导入 MCP Server 配置',\n            importMcpJsonError: 'mcpServers 结构不正确',\n            bindDomainHelper: '绑定网站之后会修改所有已安装 MCP Server 的访问地址，并关闭端口的外部访问',\n            outputTransport: '输出类型',\n            streamableHttpPath: '流式传输路径',\n            streamableHttpPathHelper: '例如：/mcp, 注意不要与其他 Server 重复',\n            npxHelper: '适合 npx 或 二进制启动的 mcp',\n            uvxHelper: '适合 uvx 启动的 mcp',\n        },\n        tensorRT: {\n            llm: 'TensorRT LLM',\n            modelDir: '模型目录',\n            commandHelper: '若需外部访问，请将命令中的端口设置为与应用端口相同',\n            imageAlert: '由于镜像较大，建议先手动将镜像下载到服务器后再进行安装',\n            modelSpeedup: '启用模型加速',\n            modelType: '模型类型',\n        },\n    },\n    container: {\n        createByCommand: '命令创建',\n        importContainerBackupTip: '导入容器备份文件，仅支持 .tar.gz 格式',\n        importComposeBackupTip: '导入编排备份文件，仅支持 .tar.gz 格式',\n        stopContainerBeforeBackup: '备份前停止容器',\n        stopComposeBeforeBackup: '备份前停止编排',\n        stopBeforeBackupHelper: '启用后备份前将停止容器或编排服务，完成后自动恢复，以确保数据一致性',\n        commandInput: '命令输入',\n        commandRule: '请输入正确的 docker run 容器创建命令！',\n        commandHelper: '将在服务器上执行该条命令以创建容器，是否继续？',\n        updateHelper1: '检测到该容器来源于应用商店，请注意以下两点：',\n        updateHelper2: '1. 当前修改内容不会同步到应用商店的已安装应用。',\n        updateHelper3: '2. 如果在已安装页面修改应用，当前编辑的部分内容将失效。',\n        updateHelper4: '编辑容器需要重建，任何未持久化的数据将丢失，是否继续操作？',\n        containerList: '容器列表',\n        operatorHelper: '将对以下容器进行 {0} 操作，是否继续？',\n        operatorAppHelper:\n            '将对以下容器进行 {0} 操作，\\n其中部分来源于应用商店，该操作可能会影响到该服务的正常使用。\\n是否继续？',\n        containerDeleteHelper:\n            '检测到容器来源于应用商店，删除容器不会将其从 1Panel 中完全移除。如需彻底删除，请前往应用商店-『已安装』或『运行环境』等菜单中操作。是否继续？',\n        start: '启动',\n        stop: '停止',\n        restart: '重启',\n        kill: '强制停止',\n        pause: '暂停',\n        unpause: '恢复',\n        rename: '重命名',\n        remove: '删除',\n        removeAll: '删除所有',\n        containerPrune: '清理容器',\n        containerPruneHelper1: '清理容器 将删除所有处于停止状态的容器。',\n        containerPruneHelper2:\n            '若容器来自于应用商店，在执行清理操作后，您需要前往 [应用商店] 的 [已安装] 列表，点击 [重建] 按钮进行重新安装。',\n        containerPruneHelper3: '该操作无法回滚，是否继续？',\n        imagePrune: '清理镜像',\n        imagePruneSome: '未标签镜像',\n        imagePruneSomeEmpty: '暂无待清理的未使用 none 标签镜像',\n        imagePruneSomeHelper: '清理下列标签为 none 且未被任何容器使用的镜像',\n        imagePruneAll: '未使用镜像',\n        imagePruneAllEmpty: '暂无待清理的未使用镜像',\n        imagePruneAllHelper: '清理下列未被任何容器使用的镜像',\n        networkPrune: '清理网络',\n        networkPruneHelper: '清理网络 将删除所有未被使用的网络，该操作无法回滚，是否继续？',\n        volumePrune: '清理存储卷',\n        volumePruneHelper: '清理存储卷 将删除所有未被使用的本地存储卷，该操作无法回滚，是否继续？',\n        cleanSuccess: '操作成功，本次清理数量: {0} 个！',\n        cleanSuccessWithSpace: '操作成功，本次清理数量: {0} 个，释放磁盘空间: {1}！',\n        unExposedPort: '当前端口映射地址为 127.0.0.1，无法实现外部访问',\n        upTime: '运行时长',\n        fetch: '过滤',\n        lines: '条数',\n        linesHelper: '请输入正确的日志获取条数！',\n        downloadLinesHelper: '选择或者输入日志下载行数',\n        lastDay: '最近一天',\n        last4Hour: '最近 4 小时',\n        lastHour: '最近 1 小时',\n        last10Min: '最近 10 分钟',\n        cleanLog: '清空日志',\n        downLogHelper1: '即将下载 {0} 容器所有日志，是否继续？',\n        downLogHelper2: '即将下载 {0} 容器最近 {1} 条日志，是否继续？',\n        cleanLogHelper: '清空日志需要重启容器，该操作无法回滚，是否继续？',\n        newName: '新名称',\n        workingDir: '工作目录',\n        source: '资源使用率',\n        cpuUsage: 'CPU 使用',\n        cpuTotal: 'CPU 总计',\n        core: '核心数',\n        memUsage: '内存使用',\n        memTotal: '内存限额',\n        memCache: '缓存使用',\n        loadSize: '获取容器大小',\n        ip: 'IP 地址',\n        cpuShare: 'CPU 权重',\n        cpuShareHelper: '容器默认份额为 1024 个 CPU，增大可使当前容器获得更多的 CPU 时间',\n        inputIpv4: '请输入 IPv4 地址',\n        inputIpv6: '请输入 IPv6 地址',\n\n        diskUsage: '磁盘占用',\n        localVolume: '本地存储卷',\n        buildCache: '构建缓存',\n        usage: '已占用：{0}, 可释放：{1}',\n        clean: '释放',\n        imageClean: '清理镜像 将删除所有未被使用的镜像，该操作无法回滚，是否继续？',\n        containerClean: '清理容器 将删除所有处于停止中状态的容器（包括应用商店停止应用），该操作无法回滚，是否继续？',\n        sizeRw: '容器层大小',\n        sizeRwHelper: '容器独有的可写层大小',\n        sizeRootFs: '虚拟大小',\n        sizeRootFsHelper: ' 容器依赖的所有镜像层 + 容器层的总大小',\n\n        containerFromAppHelper: '检测到该容器来源于应用商店，应用操作可能会导致当前编辑失效',\n        containerFromAppHelper1: '在应用商店的已安装页面，点击 [参数] 按钮，进入编辑页面修改容器名称。',\n        command: '命令',\n        console: '控制台交互',\n        tty: '伪终端 ( -t )',\n        openStdin: '标准输入 ( -i )',\n        custom: '自定义',\n        emptyUser: '为空时，将使用容器默认的用户登录',\n        privileged: '特权模式',\n        privilegedHelper: '允许容器在主机上执行某些特权操作，可能会增加容器风险，谨慎开启！',\n\n        upgradeHelper: '仓库名称/镜像名称:镜像版本',\n        upgradeWarning2: '升级操作需要重建容器，任何未持久化的数据将会丢失，是否继续？',\n        oldImage: '当前镜像',\n        sameImageContainer: '同镜像容器',\n        sameImageHelper: '同镜像容器可勾选后批量升级',\n        targetImage: '目标镜像',\n        imageLoadErr: '未检测到容器的镜像名称',\n        imageUpdateTagEmpty: '未检测到可更新的镜像标签',\n        appHelper: '该容器来源于应用商店，升级可能导致该服务不可用',\n\n        resource: '资源',\n        input: '手动输入',\n        forcePull: '强制拉取镜像',\n        forcePullHelper: '忽略服务器已存在的镜像，重新拉取一次',\n        imageUpdateHelper: '将检查镜像仓库中的同名标签，若有更新则拉取并更新本地镜像。',\n        server: '服务器',\n        serverExample: '80, 80-88, ip:80 或 ip:80-88',\n        containerExample: '80 或 80-88',\n        exposePort: '暴露端口',\n        exposeAll: '暴露所有',\n        cmdHelper: '例： nginx -g \"daemon off;\"',\n        entrypointHelper: '例： docker-entrypoint.sh',\n        autoRemove: '容器退出后自动删除容器',\n        cpuQuota: 'CPU 限制',\n        memoryLimit: '内存限制',\n        limitHelper: '限制为 0 则关闭限制，最大可用为 {0}',\n        macAddr: 'MAC 地址',\n        mount: '挂载',\n        volumeOption: '挂载卷',\n        hostOption: '本机目录',\n        serverPath: '服务器目录',\n        containerDir: '容器目录',\n        volumeHelper: '请确认存储卷内容输入正确',\n        networkEmptyHelper: '请确认容器网络选择正确',\n        modeRW: '读写',\n        modeR: '只读',\n        sharedLabel: '传播模式',\n        private: '私有',\n        privateHelper: '容器里的挂载变化和主机互不干扰',\n        rprivate: '递归私有',\n        rprivateHelper: '容器里所有挂载都和主机完全隔离',\n        shared: '共享',\n        sharedHelper: '主机和容器里的挂载变化互相可见',\n        rshared: '递归共享',\n        rsharedHelper: '主机和容器里所有挂载变化都互相可见',\n        slave: '从属',\n        slaveHelper: '容器能看见主机的挂载变化，但自己的变化不影响主机',\n        rslave: '递归从属',\n        rslaveHelper: '容器里所有挂载都能看见主机变化，但不影响主机',\n        mode: '权限',\n        env: '环境变量',\n        restartPolicy: '重启规则',\n        always: '一直重启',\n        unlessStopped: '未手动停止则重启',\n        onFailure: '失败后重启（默认重启 5 次）',\n        no: '不重启',\n\n        refreshTime: '刷新间隔',\n        cache: '缓存',\n\n        image: '镜像',\n        imagePull: '拉取镜像',\n        imagePullHelper: '支持选择拉取多个镜像，输入一组镜像后回车继续',\n        imagePush: '推送镜像',\n        imagePushHelper: '检测到该镜像存在多个标签，请确认推送时使用的镜像名称为：{0}',\n        imageDelete: '删除镜像',\n        repoName: '仓库名',\n        imageName: '镜像名',\n        httpRepo: 'http 仓库添加授信需要重启 docker 服务',\n        delInsecure: '删除授信',\n        delInsecureHelper: '删除授信需要重启 docker 服务，是否删除？',\n        pull: '拉取',\n        path: '路径',\n        importImage: '导入镜像',\n        imageBuild: '构建镜像',\n        buildArgs: '构建参数',\n        pathSelect: '路径选择',\n        label: '标签',\n        imageTag: '镜像标签',\n        imageTagHelper: '支持设置多个镜像 tag，输入一个 tag 后回车继续',\n        push: '推送',\n        fileName: '文件名',\n        export: '导出',\n        exportImage: '导出镜像',\n        size: '大小',\n        tag: '标签',\n        tagHelper: '一行一个，例： \\nkey1=value1\\nkey2=value2',\n        imageNameHelper: '镜像名称及 Tag，例：nginx:latest',\n        cleanBuildCache: '清理构建缓存',\n        delBuildCacheHelper: '清理构建缓存 将删除所有构建产生的缓存，该操作无法回滚，是否继续？',\n        urlWarning: '路径前缀不需要添加 http:// 或 https://, 请修改',\n\n        network: '网络',\n        networkHelper: '删除 1panel-network 容器网络将影响部分应用和运行环境的正常使用，是否继续？',\n        networkName: '网络名',\n        driver: '模式',\n        option: '参数',\n        attachable: '可用',\n        parentNetworkCard: '父网卡',\n        subnet: '子网',\n        scope: 'IP 范围',\n        gateway: '网关',\n        auxAddress: '排除 IP',\n\n        volume: '存储卷',\n        volumeDir: '存储卷目录',\n        nfsEnable: '启用 NFS 存储',\n        nfsAddress: '地址',\n        mountpoint: '挂载点',\n        mountpointNFSHelper: '例：/nfs, /nfs-share',\n        options: '可选参数',\n\n        repo: '仓库',\n        httpRepoHelper: '操作 http 类型仓库需要重启 Docker 服务。',\n        downloadUrl: '下载地址',\n        imageRepo: '镜像仓库',\n        repoHelper: '是否包含镜像仓库/组织/项目?',\n        auth: '认证',\n        mirrorHelper:\n            '当存在多个加速器时，需要换行显示，例： \\nhttp://xxxxxx.m.daocloud.io \\nhttps://xxxxxx.mirror.aliyuncs.com',\n        registrieHelper: '当存在多个私有仓库时，需要换行显示，例：\\n172.16.10.111:8081 \\n172.16.10.112:8081',\n\n        compose: '编排',\n        composeFile: '编排文件',\n        fromChangeHelper: '切换来源将清空当前已编辑内容，是否继续？',\n        composePathHelper: '配置文件保存路径: {0}',\n        composeHelper: '通过 1Panel 编辑或模版创建的编排，将保存在 {0}/docker/compose 路径下',\n        deleteFile: '删除文件',\n        deleteComposeHelper: '删除容器编排的所有文件，包括配置文件和持久化文件，请谨慎操作！',\n        deleteCompose: '\" 删除此编排',\n        composeDirectory: '编排目录',\n        template: '模版',\n        composeTemplate: '编排模版',\n        exportHelper: '即将导出 {0} 条编排模版，是否继续？',\n        content: '内容',\n        contentEmpty: '编排内容不能为空，请输入后重试！',\n        containerNumber: '容器数量',\n        containerStatus: '容器状态',\n        exited: '已停止',\n        running: '运行中 ( {0} / {1} )',\n        composeDetailHelper: '该 compose 为 1Panel 编排外部创建。暂不支持启停操作。',\n        composeOperatorHelper: '将对 {0} 进行 {1} 操作，是否继续？',\n        composeDownHelper: '将停止并删除 {0} 编排下所有容器及网络，是否继续？',\n        composeEnvHelper2: '该编排为 1Panel 应用商店创建，请在已安装应用中修改环境变量。',\n        composeContentPlaceholder: '在此写入或粘贴 docker-compose 文件内容',\n\n        setting: '配置',\n        goSetting: '去修改',\n        restartHelper: '修改配置后需要重启 Docker 服务生效',\n        operatorStatusHelper: '此操作将{0}Docker 服务，是否继续？',\n        dockerStatus: 'Docker 服务',\n        daemonJsonPathHelper: '请保证配置路径与 docker.service 中指定的配置路径保持一致。',\n        mirrors: '镜像加速',\n        mirrorsHelper: '优先使用加速 URL 执行操作，设置为空则取消镜像加速。',\n        mirrorsHelper2: '具体操作配置请参照官方文档',\n        registries: '私有仓库',\n        ipv6Helper: '开启 IPv6 后，需要增加 IPv6 的容器网络，具体操作配置请参照官方文档',\n        ipv6CidrHelper: '容器的 IPv6 地址池范围',\n        ipv6TablesHelper: 'Docker IPv6 对 iptables 规则的自动配置',\n        experimentalHelper: '开启 ip6tables 必须开启此配置，否则 ip6tables 会被忽略',\n        cutLog: '日志切割',\n        cutLogHelper1: '当前配置只会影响新创建的容器；',\n        cutLogHelper2: '已经创建的容器需要重新创建使配置生效；',\n        cutLogHelper3:\n            '注意，重新创建容器可能会导致数据丢失。如果你的容器中有重要数据，确保在执行重建操作之前进行备份。',\n        maxSize: '文件大小',\n        maxFile: '保留份数',\n        liveHelper: '允许在 Docker 守护进程发生意外停机或崩溃时保留正在运行的容器状态',\n        liveWithSwarmHelper: 'live-restore 守护进程配置与 Swarm 模式不兼容',\n        iptablesDisable: '关闭 iptables',\n        iptablesHelper1: 'Docker 对 iptables 规则的自动配置',\n        iptablesHelper2: '关闭 iptables 会导致容器无法与外部网络通信。',\n        daemonJsonPath: '配置路径',\n        serviceUnavailable: '当前未启动 Docker 服务，请在',\n        startIn: '中开启',\n        sockPath: 'Socket 路径',\n        sockPathHelper: 'Docker 守护进程（Docker Daemon）与客户端之间的通信通道',\n        sockPathHelper1: '默认路径：/var/run/docker.sock',\n        sockPathMsg: '保存设置 Socket 路径可能导致 Docker 服务不可用，是否继续？',\n        sockPathErr: '请选择或输入正确的 Docker sock 文件路径',\n        related: '关联资源',\n        includeAppstore: '显示应用商店容器',\n        excludeAppstore: '隐藏应用商店容器',\n\n        cleanDockerDiskZone: '清理 Docker 使用的磁盘空间',\n        cleanImagesHelper: '( 清理所有未被任何容器使用的镜像 )',\n        cleanContainersHelper: '( 清理所有处于停止状态的容器 )',\n        cleanVolumesHelper: '( 清理所有未被使用的本地存储卷 )',\n\n        makeImage: '制作镜像',\n        newImageName: '新镜像名称',\n        commitMessage: '提交信息',\n        author: '作者',\n        ifPause: '制作过程中是否暂停容器',\n        ifMakeImageWithContainer: '是否根据此容器制作新镜像？',\n        finishTime: '上一次停止时间',\n    },\n    cronjob: {\n        importHelper:\n            '导入时将自动跳过重名计划任务。任务默认设置为【停用】状态，数据关联异常时，设置为【待编辑】状态。',\n        changeStatus: '状态修改',\n        disableMsg: '停止计划任务会导致该任务不再自动执行。是否继续？',\n        enableMsg: '启用计划任务会让该任务定期自动执行。是否继续？',\n        taskType: '任务类型',\n        nextTime: '近 5 次执行',\n        record: '报告',\n        viewRecords: '查看报告',\n        shell: 'Shell 脚本',\n        stop: '手动结束',\n        stopHelper: '该操作将强制停止该次任务执行，是否继续？',\n        log: '备份日志',\n        logHelper: '备份系统日志',\n        logHelper1: '1. 1Panel 系统日志',\n        logHelper2: '2. 服务器的 SSH 登录日志',\n        logHelper3: '3. 所有网站日志',\n        containerCheckBox: '在容器中执行（无需再输入进入容器命令）',\n        containerName: '容器名称',\n        ntp: '同步服务器时间',\n        ntp_helper: '您可以在工具箱的快速设置页面配置 NTP 服务器',\n        app: '备份应用',\n        website: '备份网站',\n        rulesHelper: '支持多个排除规则，使用英文逗号 , 分隔，例如：*.log,*.sql',\n        lastRecordTime: '上次执行情况',\n        database: '备份数据库',\n        backupArgs: '备份参数',\n        backupArgsHelper: '未列出的备份参数可手动输入并选择，如: 输入 --no-data 后选择下拉列表中的第一个',\n        singleTransaction: '使用单一事务备份 InnoDB 表，适用于大数据量的备份',\n        quick: '逐行读取数据，而不是将整个表加载到内存中，适用于大数据量和低内存机器的备份',\n        skipLockTables: '不锁定所有表进行备份，适用于高并发的数据库',\n        missBackupAccount: '未能找到备份账号',\n        syncDate: '同步时间 ',\n        clean: '缓存清理',\n        curl: '访问 URL',\n        taskName: '任务名称',\n        cronSpec: '执行周期',\n        cronSpecDoc: '自定义执行周期仅支持【分时日月周】格式，如 0 0 * * * ，具体可参考官方文档',\n        cronSpecHelper: '请输入正确的执行周期',\n        cleanHelper: '该操作将清理所有任务执行记录、备份文件和日志文件，是否继续？',\n        backupContent: '备份内容',\n        directory: '备份目录 / 文件',\n        sourceDir: '备份目录',\n        snapshot: '系统快照',\n        allOptionHelper: '当前计划任务为备份所有【{0}】，暂不支持直接下载，可在【{0}】备份列表中查看',\n        exclusionRules: '排除规则',\n        exclusionRulesHelper: '选择或输入排除规则，输入完一组后回车继续，排除规则将对此次备份的所有压缩操作生效',\n        default_download_path: '默认下载地址',\n        saveLocal: '同时保留本地备份（和云存储保留份数一致）',\n        url: 'URL 地址',\n        urlHelper: '请输入正确的 URL 地址',\n        targetHelper: '备份账号可在面板设置中维护',\n        withImageHelper: '备份应用商店镜像，但是会增大快照文件体积。',\n        ignoreApp: '排除应用',\n        withImage: '备份应用镜像',\n        retainCopies: '保留份数',\n        retryTimes: '失败重试次数',\n        timeout: '超时时间',\n        ignoreErr: '忽略错误',\n        ignoreErrHelper: '忽略备份过程中出现的错误，保证所有备份任务执行',\n        retryTimesHelper: '为 0 表示失败后不重试',\n        retainCopiesHelper: '执行记录及日志保留份数',\n        retainCopiesHelper1: '备份文件保留份数',\n        retainCopiesUnit: ' 份 (查看)',\n        cronSpecRule: '第 {0} 行中执行周期格式错误，请检查后重试！',\n        cronSpecRule2: '执行周期格式错误，请检查后重试！',\n        perMonthHelper: '每月 {0} 日 {1}:{2} 执行',\n        perWeekHelper: '每周 {0} {1}:{2} 执行',\n        perDayHelper: '每日 {0}:{1} 执行',\n        perHourHelper: '每小时 {0}分 执行',\n        perNDayHelper: '每 {0} 日 {1}:{2} 执行',\n        perNHourHelper: '每 {0}小时 {1}分 执行',\n        perNMinuteHelper: '每 {0}分 执行',\n        perNSecondHelper: '每 {0}秒 执行',\n        perMonth: '每月',\n        perWeek: '每周',\n        perHour: '每小时',\n        perNDay: '每 N 日',\n        perDay: '每天',\n        perNHour: '每 N 时',\n        perNMinute: '每 N 分钟',\n        perNSecond: '每 N 秒',\n        day: '日',\n        monday: '周一',\n        tuesday: '周二',\n        wednesday: '周三',\n        thursday: '周四',\n        friday: '周五',\n        saturday: '周六',\n        sunday: '周日',\n        shellContent: '脚本内容',\n        executor: '解释器',\n        errRecord: '错误的日志记录',\n        errHandle: '任务执行失败',\n        noRecord: '当前计划任务暂未产生记录',\n        cleanData: '删除备份文件',\n        cleanRemoteData: '删除远程备份文件',\n        cleanDataHelper: '删除该任务执行过程中产生的备份文件',\n        noLogs: '暂无任务输出...',\n        errPath: '备份路径 [{0}] 错误，无法下载！',\n        cutWebsiteLog: '切割网站日志',\n        cutWebsiteLogHelper: '切割的日志文件会备份到 1Panel 的 backup 目录下',\n        syncIpGroup: '同步 WAF IP 组',\n\n        requestExpirationTime: '上传请求过期时间（小时）',\n        unitHours: '单位：小时',\n        alertTitle: '计划任务-{0}「 {1} 」任务失败告警',\n        library: {\n            script: '脚本',\n            syncNow: '立即同步',\n            turnOnSync: '开启自动同步',\n            turnOnSyncHelper: '开启自动同步将在每天凌晨时段进行自动同步',\n            turnOffSync: '关闭自动同步',\n            turnOffSyncHelper: '关闭自动同步可能导致脚本同步不及时，是否确认？',\n            isInteractive: '交互式',\n            interactive: '交互式脚本',\n            interactiveHelper: '在脚本执行过程中需要用户输入参数或做出选择，且无法用于计划任务中。',\n            library: '脚本库',\n            remoteLibrary: '远程脚本库',\n            create: '添加脚本',\n            edit: '修改脚本',\n            groupHelper: '针对脚本特征设置不同的分组，可以更加快速的对脚本进行筛选操作。',\n            handleHelper: '将在 {0} 上执行 {1} 脚本，是否继续？',\n            noSuchApp: '未检测到 {0} 服务，请前往脚本库页面手动安装！',\n            syncHelper: '即将同步系统脚本库，该操作仅针对系统脚本，是否继续？',\n        },\n\n        cleanLog: '清理日志',\n        cleanLogscope: '清理类型',\n    },\n    monitor: {\n        enableMonitor: '监控状态',\n        storeDays: '保存天数',\n        defaultNetwork: '默认网卡',\n        defaultNetworkHelper: '默认监控和概览界面显示的网卡选项',\n        defaultIO: '默认磁盘',\n        defaultIOHelper: '默认监控和概览界面显示的磁盘选项',\n        cleanMonitor: '清空监控记录',\n        cleanHelper: '该操作将清空包括 GPU 在内的所有监控记录，是否继续？',\n\n        avgLoad: '平均负载',\n        loadDetail: '负载详情',\n        resourceUsage: '资源使用率',\n        read: '读取',\n        write: '写入',\n        readWriteCount: '读写次数',\n        readWriteTime: '读写延迟',\n        today: '今天',\n        yesterday: '昨天',\n        lastNDay: '近 {0} 天',\n        lastNMonth: '近 {0} 月',\n        lastHalfYear: '近半年',\n        memory: '内存',\n        percent: '占比',\n        disk: '磁盘',\n        network: '网络',\n        up: '上行',\n        down: '下行',\n        interval: '采集间隔',\n        intervalHelper: '请输入合适的监控采集时间间隔（10秒 - 12小时）',\n    },\n    terminal: {\n        local: '本机',\n        defaultConn: '默认连接',\n        defaultConnHelper: '该操作将【{0}】打开终端后自动连接所在节点终端，是否继续？',\n        withReset: '重置连接信息',\n        localConnJump: '默认连接信息在 【 终端 - 配置 】中维护，连接失败可前往编辑！',\n        localHelper: 'local 名称仅用于系统本机标识',\n        connLocalErr: '无法自动认证，请填写本地服务器的登录信息！',\n        testConn: '连接测试',\n        saveAndConn: '保存并连接',\n        connTestOk: '连接信息可用',\n        connTestFailed: '连接不可用，请检查连接信息！',\n        host: '主机',\n        createConn: '新建连接',\n        noHost: '暂无主机',\n        groupChange: '切换分组',\n        batchInput: '批量输入',\n        quickCommand: '快速命令',\n        noSuchCommand: '导入的 csv 文件中未能发现快速命令数据，请检查后重试！',\n        quickCommandHelper: '常用命令列表，用于在终端界面底部快速选择',\n        command: '命令',\n        addHost: '添加主机',\n        localhost: '本地服务器',\n        ip: '主机地址',\n        authMode: '认证方式',\n        passwordMode: '密码认证',\n        rememberPassword: '记住认证信息',\n        keyMode: '私钥认证',\n        key: '私钥',\n        keyPassword: '私钥密码',\n        emptyTerminal: '暂无终端连接',\n        lineHeight: '字体行高',\n        letterSpacing: '字体间距',\n        fontSize: '字体大小',\n        fontFamily: '字体集',\n        fontFamilySupportHelper:\n            '选择或输入字体，是否生效取决于浏览器所在系统是否已安装该字体，未安装会自动回退，为空则使用默认字体',\n        backgroundColor: '背景色',\n        foregroundColor: '前景色',\n        cursorBlink: '光标闪烁',\n        cursorStyle: '光标样式',\n        cursorUnderline: '下划线',\n        cursorBlock: '块状',\n        cursorBar: '条形',\n        scrollback: '滚动行数',\n        scrollSensitivity: '滚动速度',\n        aiStatus: 'AI 终端',\n        aiSettings: 'AI 终端设置',\n        aiAccountHelper: '使用所选模型账号生成并回填命令。Ollama、vLLM 本地模型请用自定义模型账号。',\n        aiPrefix: '触发前缀',\n        aiPrefixHelper: '以该前缀开头并回车时，会触发 AI 命令生成，例如 # 或 //ai',\n        aiRiskCommands: '风险命令拦截',\n        aiRiskCommandsHelper: '命中以下片段的生成命令会被拦截，并以注释形式回填，支持增删改',\n        aiAddRiskCommand: '新增风险命令',\n        aiRemoveRiskCommand: '删除',\n        aiSummary: '以 {0} 前缀开头并回车时，会触发 AI 命令生成',\n        aiPrefixAsciiVisible: '仅支持 ASCII 可见字符，不支持空格、中文或全角符号',\n        saveHelper: '是否确认保存当前终端配置？',\n    },\n    toolbox: {\n        common: {\n            toolboxHelper: '部分安装和使用问题，可参考',\n        },\n        swap: {\n            swapHelper1: 'Swap 的大小应该是物理内存的 1 到 2 倍，可根据具体情况进行调整；',\n            swapHelper2: '在创建 Swap 文件之前，请确保系统硬盘有足够的可用空间，Swap 文件的大小将占用相应的磁盘空间；',\n            swapHelper3:\n                'Swap 可以帮助缓解内存压力，但仅是一个备选项，过多依赖可能导致系统性能下降，建议优先考虑增加内存或优化应用程序内存使用；',\n            swapHelper4: '建议定期监控 Swap 的使用情况，以确保系统正常运行。',\n            saveSwap: '保存当前配置将调整 Swap 分区 {0} 大小到 {1}，是否继续？',\n            swapMin: '分区大小最小值为 40 KB，请修改后重试！',\n            swapMax: '分区大小最大值为 {0}，请修改后重试！',\n            swapOff: '分区大小最小值为 40 KB，设置成 0 则关闭 Swap 分区。',\n        },\n        device: {\n            dnsHelper: '服务器地址域名解析',\n            dnsAlert: '请注意！修改 /etc/resolv.conf 文件的配置时，重启系统后会将文件恢复为默认值',\n            dnsHelper1: '当存在多个 DNS 时，需换行显示，例：\\n114.114.114.114\\n8.8.8.8',\n            hostsHelper: '主机名解析',\n            hosts: '域名',\n            toolbox: '快速设置',\n            hostname: '主机名',\n            passwd: '系统密码',\n            passwdHelper: '输入字符不能包含 $ 和 &',\n            timeZone: '系统时区',\n            localTime: '服务器时间',\n            timeZoneChangeHelper: '系统时区修改需要重启服务，是否继续？',\n            timeZoneHelper: '时区修改依赖于 timedatectl 命令，如未安装可能导致修改失败',\n            timeZoneCN: '北京',\n            timeZoneAM: '洛杉矶',\n            timeZoneNY: '纽约',\n            ntpALi: '阿里',\n            ntpGoogle: '谷歌',\n            syncSite: 'NTP 服务器',\n            syncSiteHelper: '该操作将使用 {0} 作为源进行系统时间同步，是否继续？',\n            hostnameHelper: '主机名修改依赖于 hostnamectl 命令，如未安装可能导致修改失败',\n            userHelper: '用户名依赖于 whoami 命令获取，如未安装可能导致获取失败。',\n            passwordHelper: '密码修改依赖于 chpasswd 命令，如未安装可能导致修改失败',\n            hostHelper: '填写的内容中存在空值，请检查修改后重试！',\n            dnsCheck: '测试可用性',\n            dnsOK: 'DNS 配置信息可用！',\n            dnsTestFailed: 'DNS 配置信息不可用，请修改后重试！',\n        },\n        fail2ban: {\n            sshPort: '监听 SSH 端口',\n            sshPortHelper: '当前 Fail2ban 监听主机 SSH 连接端口',\n            unActive: '当前未开启 Fail2ban 服务，请先开启！',\n            operation: '对 Fail2ban 服务进行 [{0}] 操作，是否继续？',\n            fail2banChange: 'Fail2ban 配置修改',\n            ignoreHelper: '白名单中的 IP 列表将被忽略屏蔽，是否继续？',\n            bannedHelper: '黑名单中的 IP 列表将被服务器屏蔽，是否继续？',\n            maxRetry: '最大重试次数',\n            banTime: '禁用时间',\n            banTimeHelper: '默认禁用时间为 10 分钟，禁用时间为 -1 则表示永久禁用',\n            banTimeRule: '请输入正确的禁用时间或 -1',\n            banAllTime: '永久禁用',\n            findTime: '发现周期',\n            banAction: '禁用方式',\n            banActionOption: '通过 {0} 来禁用指定的 IP 地址',\n            allPorts: ' (所有端口)',\n            ignoreIP: 'IP 白名单',\n            bannedIP: 'IP 黑名单',\n            logPath: '日志路径',\n            logPathHelper: '默认 /var/log/secure 或 /var/log/auth.log',\n        },\n        ftp: {\n            ftp: 'FTP 账户',\n            notStart: '当前未开启 FTP 服务，请先开启！',\n            operation: '对 FTP 服务进行 [{0}] 操作，是否继续？',\n            noPasswdMsg: '无法获取当前 FTP 账号密码，请先设置密码后重试！',\n            enableHelper: '启用选中的 FTP 账号后，该 FTP 账号恢复访问权限，是否继续操作？',\n            disableHelper: '停用选中的 FTP 账号后，该 FTP 账号将失去访问权限，是否继续操作？',\n            syncHelper: '同步服务器与数据库中的 FTP 账户数据，是否继续操作？',\n            dirSystem: '该目录为系统保留目录，修改可能导致系统崩溃，请修改后重试！',\n            dirHelper: '开启 FTP 需要修改目录权限，请谨慎选择',\n            dirMsg: '开启 FTP 将修改整个 {0} 目录权限，是否继续？',\n        },\n        clam: {\n            clam: '病毒扫描',\n            cron: '定时扫描',\n            cronHelper: '专业版支持定时扫描功能 ',\n            specErr: '执行周期格式错误，请检查后重试！',\n            disableMsg: '停止定时执行会导致该扫描任务不再自动执行。是否继续？',\n            enableMsg: '启用定时执行会让该扫描任务定期自动执行。是否继续？',\n            showFresh: '显示病毒库服务',\n            hideFresh: '隐藏病毒库服务',\n            clamHelper: 'ClamAV 建议最低配置：3 GiB 以上内存、单核 2.0 GHz 以上 CPU，以及至少 5 GiB 可用硬盘空间。',\n            notStart: '当前未开启 ClamAV 服务，请先开启！',\n            removeRecord: '删除报告文件',\n            noRecords: '点击“执行”按钮开始扫描，扫描结果将会记录在这里。',\n            removeInfected: '删除病毒文件',\n            removeInfectedHelper: '删除任务检测到的病毒文件，以确保服务器的安全和正常运行。',\n            clamCreate: '创建扫描规则',\n            infectedStrategy: '感染文件策略',\n            removeHelper: '删除病毒文件，请谨慎选择！',\n            move: '移动',\n            moveHelper: '将病毒文件移动到指定目录下',\n            copyHelper: '将病毒文件复制到指定目录下',\n            none: '不操作',\n            noneHelper: '不对病毒文件采取任何操作',\n            scanDir: '扫描目录',\n            infectedDir: '隔离目录',\n            scanDate: '扫描时间',\n            scanResult: '扫描报告条数',\n            tail: '日志显示行数',\n            infectedFiles: '感染文件数',\n            log: '详情',\n            clamConf: '扫描配置',\n            clamLog: '扫描日志',\n            freshClam: '病毒库刷新配置',\n            freshClamLog: '病毒库刷新日志',\n            alertHelper: '专业版支持定时扫描和短信告警功能',\n            alertTitle: '病毒扫描「 {0} 」任务检测到感染文件告警',\n        },\n    },\n    logs: {\n        core: '面板服务',\n        agent: '节点监控',\n        panelLog: '面板日志',\n        operation: '操作日志',\n        login: '访问日志',\n        loginIP: '登录 IP',\n        loginAddress: '登录地址',\n        loginAgent: '用户代理',\n        loginStatus: '登录状态',\n        system: '系统日志',\n        deleteLogs: '清空日志',\n        resource: '资源',\n        detail: {\n            dashboard: '概览',\n            ai: 'AI',\n            groups: '分组',\n            hosts: '主机',\n            apps: '应用',\n            openresty: 'OpenResty',\n            websites: '网站',\n            containers: '容器',\n            files: '文件管理',\n            runtimes: '运行环境',\n            process: '进程管理',\n            toolbox: '工具箱',\n            backups: '备份 / 恢复',\n            tampers: '防篡改',\n            xsetting: '界面设置',\n            logs: '日志审计',\n            settings: '面板设置',\n            cronjobs: '计划任务',\n            databases: '数据库',\n            waf: 'WAF',\n            licenses: '许可证',\n            nodes: '节点',\n            commands: '快速命令',\n        },\n        websiteLog: '网站日志',\n        runLog: '运行日志',\n        errLog: '错误日志',\n        task: '任务日志',\n        taskName: '任务名称',\n        taskRunning: '执行中',\n    },\n    file: {\n        fileDirNum: '共 {0} 个目录，{1} 个文件，',\n        currentDir: '当前目录',\n        dir: '文件夹',\n        fileName: '文件名',\n        search: '在当前目录下查找',\n        mode: '权限',\n        editPermissions: '编辑@:file.mode',\n        owner: '所有者',\n        file: '文件',\n        remoteFile: '远程下载',\n        share: '分享',\n        sync: '数据同步',\n        size: '大小',\n        updateTime: '修改时间',\n        rename: '重命名',\n        role: '权限',\n        info: '属性',\n        linkFile: '软连接文件',\n        shareList: '分享列表',\n        zip: '压缩',\n        group: '用户组',\n        path: '路径',\n        public: '公共',\n        setRole: '设置权限',\n        link: '是否链接',\n        rRole: '读取',\n        wRole: '写入',\n        xRole: '可执行',\n        compress: '压缩',\n        deCompress: '解压',\n        compressType: '压缩格式',\n        compressDst: '压缩路径',\n        replace: '覆盖已存在的文件',\n        compressSuccess: '压缩成功',\n        deCompressSuccess: '解压成功',\n        deCompressDst: '解压路径',\n        linkType: '链接类型',\n        softLink: '软链接',\n        hardLink: '硬链接',\n        linkPath: '链接路径',\n        selectFile: '选择文件',\n        downloadSuccess: '下载成功',\n        downloadUrl: '下载地址',\n        downloadStart: '下载开始!',\n        moveSuccess: '移动成功',\n        copySuccess: '复制成功',\n        pasteMsg: '请在目标目录点击右上角【粘贴】按钮',\n        move: '移动',\n        calculate: '计算',\n        remark: '备注',\n        setRemark: '设置备注',\n        remarkPrompt: '请输入备注内容',\n        remarkPlaceholder: '备注内容',\n        remarkToggle: '备注',\n        remarkToggleTip: '加载文件备注',\n        canNotDeCompress: '无法解压此文件',\n        uploadSuccess: '上传成功!',\n        downloadProcess: '下载进度',\n        downloading: '正在下载...',\n        infoDetail: '文件属性',\n        root: '根目录',\n        list: '文件列表',\n        sub: '子目录',\n        theme: '主题',\n        language: '语言',\n        eol: '行尾符',\n        copyDir: '复制路径',\n        paste: '粘贴',\n        changeOwner: '修改用户和用户组',\n        containSub: '同时修改子文件属性',\n        ownerHelper: 'PHP 运行环境默认用户:用户组为 1000:1000, 容器内外用户显示不一致为正常现象',\n        searchHelper: '支持 * 等通配符',\n        uploadFailed: '【{0}】 文件上传失败',\n        fileUploadStart: '正在上传【{0}】...',\n        currentSelect: '当前选中: ',\n        unsupportedType: '不支持的文件类型',\n        deleteHelper: '确定删除所选文件？ 默认删除之后将进入回收站',\n        fileHelper: '注意：1. 搜索结果不支持排序功能 2. 文件夹无法按大小排序。',\n        forceDeleteHelper: '永久删除文件（不进入回收站，直接删除）',\n        recycleBin: '回收站',\n        sourcePath: '原路径',\n        deleteTime: '删除时间',\n        reduce: '还原',\n        confirmReduce: '确定还原以下文件？',\n        reduceSuccess: '还原成功',\n        reduceHelper: '如果原路径存在同名文件或目录，将会被覆盖，是否继续？',\n        clearRecycleBin: '清空回收站',\n        clearRecycleBinHelper: '是否清空回收站？',\n        favorite: '收藏夹',\n        removeFavorite: '是否从收藏夹移出？',\n        addFavorite: '添加/移出收藏夹',\n        clearList: '清空列表',\n        deleteRecycleHelper: '确定永久删除以下文件？',\n        typeErrOrEmpty: '【{0}】 文件类型错误或为空文件夹',\n        dropHelper: '将需要上传的文件拖曳到此处',\n        fileRecycleBin: '文件回收站',\n        fileRecycleBinMsg: '已{0}回收站',\n        wordWrap: '自动换行',\n        deleteHelper2: '确定删除所选文件？删除操作不可回滚',\n        ignoreCertificate: '忽略不可信证书',\n        ignoreCertificateHelper:\n            '下载时忽略不可信证书可能导致数据泄露或篡改。请谨慎使用此选项，仅在信任下载源的情况下启用',\n        uploadOverLimit: '文件数量超过 1000！请压缩后上传',\n        clashDidNotSupport: '文件名禁止包含 .1panel_clash',\n        clashDeleteAlert: '回收站文件夹不能删除',\n        clashOpenAlert: '回收站目录请点击【回收站】按钮打开',\n        right: '前进',\n        back: '后退',\n        top: '返回上一级',\n        up: '上一级',\n        openWithVscode: 'VS Code 打开',\n        vscodeHelper: '请确保本地已安装 VS Code 并配置了 SSH Remote 插件',\n        saveContentAndClose: '文件已被修改，是否保存并关闭？',\n        saveAndOpenNewFile: '文件已被修改，是否保存并打开新文件？',\n        noEdit: '文件未修改,无需此操作！',\n        noNameFolder: '未命名文件夹',\n        noNameFile: '未命名文件',\n        minimap: '缩略图',\n        fileCanNotRead: '此文件不支持预览',\n        previewTruncated: '文件过大，仅显示末尾内容',\n        previewEmpty: '文件内容为空或不是文本文件',\n        previewLargeFile: '预览',\n        panelInstallDir: '1Panel 安装目录不能删除',\n        wgetTask: '下载任务',\n        existFileTitle: '同名文件提示',\n        existFileHelper: '上传的文件存在同名文件，是否覆盖？',\n        existFileSize: '文件大小 (新 -> 旧)',\n        existFileDirHelper: '选择的文件/文件夹存在同名，请谨慎操作！',\n        coverDirHelper: '选中覆盖的文件夹，将复制到目标路径！',\n        noSuchFile: '未能找到该文件或目录，请检查后重试',\n        setting: '设置',\n        showHide: '显示隐藏文件',\n        noShowHide: '不显示隐藏文件',\n        cancelUpload: '取消上传',\n        cancelUploadHelper: '是否取消上传，取消后将清空上传列表',\n        keepOneTab: '至少保留一个标签页',\n        notCanTab: '不可增加更多的标签页',\n        convert: '转换格式',\n        converting: '转换为',\n        fileCanNotConvert: '此文件不支持转换格式',\n        formatType: '格式类型',\n        sourceFormat: '源格式',\n        sourceFile: '源文件',\n        saveDir: '保存目录',\n        deleteSourceFile: '是否删除源文件',\n        convertHelper: '是否为选中的文件进行格式转换',\n        convertHelper1: '请选择需要转换格式的文件',\n        execConvert: '开始转换，可以在任务中心查看转换日志',\n        convertLogs: '转换日志',\n        formatConvert: '格式转换',\n    },\n    ssh: {\n        autoStart: '开机自启',\n        enable: '设置开机自启',\n        disable: '关闭开机自启',\n        sshAlert: '列表数据根据登录时间排序，但请注意，切换时区或其他操作可能导致登录日志的时间出现偏差。',\n        sshAlert2: '可通过工具箱中的 Fail2ban 屏蔽暴力破解 IP，从而保护主机安全。',\n        sshOperate: '对 SSH 服务进行 [{0}] 操作，是否继续？',\n        sshChange: 'SSH 配置修改',\n        sshChangeHelper: '此操作将 {0} 修改为 [{1}] ，是否继续？',\n        sshFileChangeHelper: '直接修改配置文件可能会导致服务不可用，请谨慎操作，是否继续？',\n        port: '连接端口',\n        portHelper: '指定 SSH 服务监听的端口号，默认为 22。',\n        listenAddress: '监听地址',\n        allV4V6: '0.0.0.0:{0}(IPv4) 和 :::{0}(IPv6)',\n        listenHelper: '同时取消 IPv4 和 IPv6 设置，将会同时监听 0.0.0.0:{0}(IPv4) 和 :::{0}(IPv6)',\n        addressHelper: '指定 SSH 服务监听的 IP 地址',\n        permitRootLogin: 'root 用户',\n        rootSettingHelper: 'root 用户 SSH 登录方式，默认所有 SSH 登录。',\n        rootHelper1: '允许 SSH 登录',\n        rootHelper2: '禁止 SSH 登录',\n        rootHelper3: '仅允许密钥登录',\n        rootHelper4: '仅允许执行预先定义的命令，不能进行其他操作',\n        passwordAuthentication: '密码认证',\n        pwdAuthHelper: '是否启用密码认证，默认启用。',\n        pubkeyAuthentication: '密钥认证',\n        privateKey: '私钥',\n        publicKey: '公钥',\n        password: '密码',\n        createMode: '创建方式',\n        generate: '自动生成',\n        unSyncPass: '密钥密码无法同步',\n        syncHelper: '同步操作将清理失效密钥并同步新增的完整密钥对，是否继续？',\n        input: '手动输入',\n        import: '文件上传',\n        authKeys: '授权密钥',\n        authKeysHelper: '是否保存当前公钥信息？',\n        pubkey: '密钥信息',\n        pubKeyHelper: '当前密钥信息仅对用户 {0} 生效',\n        encryptionMode: '加密方式',\n        passwordHelper: '支持大小写英文、数字,长度6-10',\n        reGenerate: '重新生成密钥',\n        keyAuthHelper: '是否启用密钥认证，默认启用。',\n        useDNS: '反向解析',\n        dnsHelper: '控制 SSH 服务器是否启用 DNS 解析功能，从而验证连接方的身份。',\n        analysis: '统计信息',\n        denyHelper: '将对下列地址进行【屏蔽】操作，设置后该 IP 将禁止访问服务器，是否继续？',\n        acceptHelper: '将对下列地址进行【放行】操作，设置后该 IP 将恢复正常访问，是否继续？',\n        noAddrWarning: '当前未选中任何可【{0}】地址，请检查后重试！',\n        loginLogs: '登录日志',\n        loginMode: '登录方式',\n        authenticating: '密钥',\n        publickey: '密钥',\n        belong: '归属地',\n        local: '内网',\n        remote: '外网',\n        session: '会话',\n        loginTime: '登录时间',\n        loginIP: '登录IP',\n        stopSSHWarn: '是否断开此SSH连接',\n    },\n    setting: {\n        panel: '面板',\n        user: '面板用户',\n        userChange: '修改面板用户',\n        userChangeHelper: '修改面板用户将退出登录，是否继续？',\n        passwd: '面板密码',\n        emailHelper: '用于密码找回',\n        watermark: '水印设置',\n        watermarkContent: '水印内容',\n        contentHelper: '{0} 表示节点名称，{1} 表示节点地址，既可以使用变量，也可以填写自定义名称。',\n        watermarkColor: '水印颜色',\n        watermarkFont: '水印字号',\n        watermarkHeight: '水印高度',\n        watermarkWidth: '水印宽度',\n        watermarkRotate: '旋转角',\n        watermarkGap: '间距',\n        watermarkCloseHelper: '是否确认关闭系统水印设置',\n        watermarkOpenHelper: '是否确认保存当前系统水印设置',\n        title: '面板别名',\n        titleHelper: '支持长度3-30的英文、中文、数字、空格和常见的特殊字符',\n        panelPort: '面板端口',\n        portHelper: '建议端口范围8888 - 65535，注意：有安全组的服务器请提前在安全组放行新端口',\n        portChange: '端口修改',\n        portChangeHelper: '服务端口修改需要重启服务，是否继续？',\n        theme: '主题颜色',\n        menuTabs: '菜单标签页',\n        componentSize: '组件大小',\n        dark: '暗色',\n        darkGold: '黑金',\n        light: '亮色',\n        auto: '跟随系统',\n        language: '系统语言',\n        docSource: '文档来源',\n        withByRegion: '跟随运行区域（默认）',\n        withByLang: '跟随系统语言',\n        runtimeEnv: '运行环境',\n        region: '运行区域',\n        cn: '中国大陆',\n        intl: '全球',\n        regionHelper: '该操作可能影响后续资源下载与访问，是否继续？',\n        regionTip: '运行区域影响系统更新源以及安装包下载地址。',\n        docSourceTip: '文档来源决定文档与更新日志的跳转语言。',\n        languageHelper: '默认跟随浏览器语言，设置后只对当前浏览器生效，更换浏览器后需要重新设置',\n        sessionTimeout: '超时时间',\n        sessionTimeoutError: '最小超时时间为 300 秒',\n        sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板，面板将自动退出登录',\n        systemIP: '默认访问地址',\n        systemIPHelper: '应用跳转、容器访问等功能将使用此地址进行跳转，每个节点可设置不同地址。',\n        proxy: '代理服务器',\n        proxyHelper: '设置代理服务器后，将在以下场景中生效：',\n        proxyHelper1: '应用商店的安装包下载和同步（专业版功能）',\n        proxyHelper2: '系统版本升级及获取更新说明（专业版功能）',\n        proxyHelper3: '系统许可证的验证和同步',\n        proxyHelper4: 'Docker 拉取镜像等网络请求将通过代理服务器访问（专业版功能）',\n        proxyHelper5: '系统类型脚本库的统一下载与同步（专业版功能）',\n        proxyHelper6: '申请证书（专业版功能）',\n        proxyType: '代理类型',\n        proxyUrl: '代理地址',\n        proxyPort: '代理端口',\n        proxyPasswdKeep: '记住密码',\n        proxyDocker: 'Docker 代理',\n        proxyDockerHelper: '将代理服务器配置同步至 Docker，支持离线服务器拉取镜像等操作',\n        syncToNode: '同步至子节点',\n        syncToNodeHelper: '同步设置至所选节点',\n        nodes: '节点',\n        selectNode: '选择节点',\n        selectNodeError: '请选择节点',\n        apiInterface: 'API 接口',\n        apiInterfaceClose: '关闭后将不能使用 API 接口进行访问，是否继续？',\n        apiInterfaceHelper: '提供面板支持 API 接口访问',\n        apiInterfaceAlert1: '请不要在生产环境开启，这可能增加服务器安全风险',\n        apiInterfaceAlert2: '请不要使用第三方应用调用面板 API，以防止潜在的安全威胁。',\n        apiInterfaceAlert3: 'API 接口文档',\n        apiInterfaceAlert4: '使用文档',\n        apiKey: '接口密钥',\n        apiKeyHelper: '接口密钥用于外部应用访问 API 接口',\n        ipWhiteList: 'IP 白名单',\n        ipWhiteListEgs: '当存在多个 IP 时，需要换行显示，例： \\n172.16.10.111 \\n172.16.10.0/24',\n        ipWhiteListHelper:\n            '必需在 IP 白名单列表中的 IP 才能访问面板 API 接口，0.0.0.0/0（所有 IPv4），::/0（所有 IPv6）',\n        apiKeyValidityTime: '接口密钥有效期',\n        apiKeyValidityTimeEgs: '接口密钥有效期（单位分）',\n        apiKeyValidityTimeHelper: '接口时间戳到请求时的当前时间戳之间有效（单位分），设置为 0 时，不做时间戳校验',\n        apiKeyReset: '接口密钥重置',\n        apiKeyResetHelper: '重置密钥后，已关联密钥服务将失效，请重新添加新密钥至服务。',\n        confDockerProxy: '配置 Docker 代理',\n        restartNowHelper: '配置 Docker 代理需要重启 Docker 服务。',\n        restartNow: '立即重启',\n        restartLater: '稍后手动重启',\n        systemIPWarning: '当前节点尚未配置默认访问地址，请前往面板设置进行设置！',\n        systemIPWarning1: '当前服务器地址设置为 {0}，无法快速跳转！',\n        changePassword: '密码修改',\n        oldPassword: '原密码',\n        newPassword: '新密码',\n        retryPassword: '确认密码',\n        noSpace: '输入信息不能包含空格符',\n        duplicatePassword: '新密码不能与原始密码一致，请重新输入！',\n        diskClean: '缓存清理',\n        developerMode: '预览体验计划',\n        developerModeHelper: '获取 1Panel 的预览版本，以分享有关新功能和更新的反馈',\n\n        thirdParty: '第三方账号',\n        scope: '使用范围',\n        public: '公有',\n        publicHelper: '公有类型的备份账号会同步到各个子节点，子节点可以一起使用',\n        private: '私有',\n        privateHelper: '私有类型的备份账号只创建在当前节点上，仅供当前节点使用',\n        noTypeForCreate: '当前无可创建备份类型',\n        LOCAL: '服务器磁盘',\n        OSS: '阿里云 OSS',\n        S3: '亚马逊 S3 云存储',\n        mode: '模式',\n        MINIO: 'MINIO',\n        SFTP: 'SFTP',\n        WebDAV: 'WebDAV',\n        WebDAVAlist: 'WebDAV 连接 Alist 可参考官方文档',\n        UPYUN: '又拍云',\n        ALIYUN: '阿里云盘',\n        ALIYUNHelper: '当前阿里云盘非客户端下载最大限制为 100 MB，超过限制需要通过客户端下载',\n        ALIYUNRecover:\n            '当前阿里云盘非客户端下载最大限制为 100 MB，超过限制需要通过客户端下载到本地后，同步快照进行恢复',\n        GoogleDrive: '谷歌云盘',\n        analysis: '解析',\n        analysisHelper: '粘贴整个 token 内容，自动解析所需部分，具体操作可参考官方文档',\n        serviceName: '服务名称',\n        operator: '操作员',\n        OneDrive: '微软 OneDrive',\n        isCN: '世纪互联',\n        isNotCN: '国际版',\n        client_id: '客户端 ID',\n        client_secret: '客户端密钥',\n        redirect_uri: '重定向 Url',\n        onedrive_helper: '自定义配置可参考官方文档',\n        clickToRefresh: '单击可手动刷新',\n        refreshTime: '令牌刷新时间',\n        refreshStatus: '令牌刷新状态',\n        codeWarning: '当前授权码格式错误，请重新确认！',\n        backupDir: '备份目录',\n        code: '授权码',\n        codeHelper:\n            '请点击获取按钮，然后登录 {0} 复制跳转链接中 code 后面的内容，粘贴到该输入框中，具体操作可参考官方文档。',\n        googleHelper: '请先在创建 google 应用并获取客户端信息，写入表单后点击获取按钮，具体操作可参考官方文档。',\n        loadCode: '获取',\n        COS: '腾讯云 COS',\n        ap_beijing_1: '北京一区',\n        ap_beijing: '北京',\n        ap_nanjing: '南京',\n        ap_shanghai: '上海',\n        ap_guangzhou: '广州',\n        ap_chengdu: '成都',\n        ap_chongqing: '重庆',\n        ap_shenzhen_fsi: '深圳金融',\n        ap_shanghai_fsi: '上海金融',\n        ap_beijing_fsi: '北京金融',\n        ap_hongkong: '中国香港',\n        ap_singapore: '新加坡',\n        ap_mumbai: '孟买',\n        ap_jakarta: '雅加达',\n        ap_seoul: '首尔',\n        ap_bangkok: '曼谷',\n        ap_tokyo: '东京',\n        na_siliconvalley: '硅谷（美西）',\n        na_ashburn: '弗吉尼亚（美东）',\n        na_toronto: '多伦多',\n        sa_saopaulo: '圣保罗',\n        eu_frankfurt: '法兰克福',\n        KODO: '七牛云 Kodo',\n        scType: '存储类型',\n        typeStandard: '标准存储',\n        typeStandard_IA: '低频存储',\n        typeArchive: '归档存储',\n        typeDeep_Archive: '深度归档存储',\n        scLighthouse: '默认，轻量对象存储仅支持该存储类型',\n        scStandard: '标准存储，适用于实时访问的大量热点文件、频繁的数据交互等业务场景。',\n        scStandard_IA: '低频存储，适用于较低访问频率（例如平均每月访问频率1到2次）的业务场景，最少存储30天。',\n        scArchive: '归档存储，适用于极低访问频率（例如半年访问1次）的业务场景。',\n        scDeep_Archive: '深度归档存储，适用于极低访问频率（例如1年访问1~2次）的业务场景。',\n        archiveHelper: '归档存储的文件无法直接下载，需要先在对应的云服务商网站进行恢复操作，请谨慎使用！',\n        backupAlert: '理论上只要云厂商兼容 S3 协议，就可以用现有的亚马逊 S3 云存储来备份，具体配置参考',\n        domain: '加速域名',\n        backupAccount: '备份账号',\n        loadBucket: '获取桶',\n        accountName: '账户名称',\n        accountKey: '账户密钥',\n        address: '地址',\n        path: '路径',\n        backupJump: '未在当前备份列表中的备份文件，请尝试从文件目录中下载后导入备份。',\n\n        snapshot: '快照',\n        noAppData: '暂无可选择系统应用',\n        noBackupData: '暂无可选择备份数据',\n        stepBaseData: '基础数据',\n        stepAppData: '系统应用',\n        stepPanelData: '系统数据',\n        stepBackupData: '备份数据',\n        stepOtherData: '其他数据',\n        monitorData: '监控数据',\n        dockerConf: 'Docker 配置',\n        selectAllImage: '备份所有应用镜像',\n        logLabel: '日志',\n        agentLabel: '节点配置',\n        appDataLabel: '应用数据',\n        appImage: '应用镜像',\n        appBackup: '应用备份',\n        backupLabel: '备份目录',\n        confLabel: '配置文件',\n        dockerLabel: '容器',\n        taskLabel: '计划任务',\n        resourceLabel: '应用资源目录',\n        runtimeLabel: '运行环境',\n        appLabel: '应用',\n        databaseLabel: '数据库',\n        snapshotLabel: '快照文件',\n        websiteLabel: '网站',\n        directoryLabel: '目录',\n        appStoreLabel: '应用商店',\n        shellLabel: '脚本',\n        tmpLabel: '临时目录',\n        sslLabel: '证书目录',\n        reCreate: '创建快照失败',\n        reRollback: '回滚快照失败',\n        deleteHelper: '将删除该快照的所有备份文件，包括第三方备份账号中的文件。',\n        ignoreRule: '排除规则',\n        editIgnoreRule: '@:commons.button.edit@:setting.ignoreRule',\n        ignoreHelper: '快照时将使用该规则对 1Panel 数据目录进行压缩备份，请谨慎修改。',\n        ignoreHelper1: '一行一个，例： \\n*.log\\n/opt/1panel/cache',\n        status: '快照状态',\n        panelInfo: '写入 1Panel 基础信息',\n        panelBin: '备份 1Panel 系统文件',\n        daemonJson: '备份 Docker 配置文件',\n        appData: '备份 1Panel 已安装应用',\n        panelData: '备份 1Panel 数据目录',\n        backupData: '备份 1Panel 本地备份目录',\n        compress: '制作快照文件',\n        upload: '上传快照文件',\n        recoverDetail: '恢复详情',\n        recoverFailed: '快照恢复失败',\n        importHelper: '快照文件目录：',\n        lastRecoverAt: '上次恢复时间',\n        lastRollbackAt: '上次回滚时间',\n        reDownload: '重新下载',\n        recoverErrArch: '不支持在不同服务器架构之间进行快照恢复操作!',\n        recoverErrSize: '检测到当前磁盘空间不足，请检查或清理后重试!',\n        recoverHelper: '即将从快照 {0} 开始恢复，恢复前请确认以下信息：',\n        recoverHelper1: '恢复需要重启 Docker 以及 1Panel 服务',\n        recoverHelper2: '请确保服务器磁盘空间充足 ( 快照文件大小: {0}, 可用空间: {1} )',\n        recoverHelper3: '请确保服务器架构与创建快照服务器架构信息保持一致 (当前服务器架构: {0} )',\n        rollback: '回滚',\n        rollbackHelper:\n            '即将回滚本次恢复，回滚将替换所有本次恢复的文件，过程中可能需要重启 Docker 以及 1Panel 服务，是否继续？',\n\n        upgradeRecord: '更新记录',\n        upgrading: '正在升级中，请稍候...',\n        upgradeHelper: '升级操作需要重启 1Panel 服务，是否继续？',\n        noUpgrade: '当前已经是最新版本',\n        versionHelper: '1Panel 版本号命名规则为： [大版本].[功能版本].[Bug 修复版本]，例：',\n        rollbackLocalHelper: '主节点暂不支持直接回滚，请手动执行 1pctl restore 命令回滚！',\n        upgradeCheck: '检查更新',\n        upgradeNotes: '更新内容',\n        upgradeNow: '立即更新',\n        source: '下载源',\n        versionNotSame: '节点版本与主节点不一致，暂不支持切换，请在节点管理中升级后重试！',\n        versionCompare: '检测到节点 {0} 版本已是当前可升级最新版本，请检查主节点版本后重试！',\n\n        safe: '安全',\n        bindInfo: '监听地址',\n        bindAll: '监听所有',\n        bindInfoHelper: '修改服务监听地址或协议可能导致服务不可用，是否继续？',\n        ipv6: '监听 IPv6',\n        bindAddress: '监听地址',\n        entrance: '安全入口',\n        showEntrance: '启用概览页未开启提醒',\n        entranceHelper: '开启安全入口后只能通过指定安全入口登录面板',\n        entranceError: '请输入 5-116 位安全登录入口，仅支持输入数字或字母',\n        entranceInputHelper: '安全入口设置为空时，则取消安全入口',\n        randomGenerate: '随机生成',\n        expirationTime: '密码过期时间',\n        unSetting: '未设置',\n        noneSetting: '为面板密码设置过期时间，过期后需要重新设置密码',\n        expirationHelper: '密码过期时间为 [0] 天时，则关闭密码过期功能',\n        days: '过期天数',\n        expiredHelper: '当前密码已过期，请重新修改密码：',\n        timeoutHelper: '【 {0} 天后 】面板密码即将过期，过期后需要重新设置密码',\n        complexity: '密码复杂度验证',\n        complexityHelper: '开启后密码必须满足长度为 8-30 位且包含字母、数字、特殊字符至少两项',\n        bindDomain: '域名绑定',\n        unBindDomain: '域名解绑',\n        panelSSL: '面板 SSL',\n        unBindDomainHelper: '解除域名绑定可能造成系统不安全，是否继续？',\n        bindDomainHelper: '设置域名绑定后，仅能通过设置中域名访问 1Panel 服务',\n        bindDomainHelper1: '绑定域名为空时，则取消域名绑定',\n        bindDomainWarning: '设置域名绑定后，将退出当前登录，且仅能通过设置中域名访问 1Panel 服务，是否继续？',\n        allowIPs: '授权 IP',\n        unAllowIPs: '取消授权',\n        unAllowIPsWarning: '授权 IP 为空将允许所有 IP 访问系统，可能造成系统不安全，是否继续？',\n        allowIPsHelper: '设置授权 IP 后，仅有设置中的 IP 可以访问 1Panel 服务',\n        allowIPsWarning: '设置授权 IP 后，仅有设置中的 IP 可以访问 1Panel 服务，是否继续？',\n        allowIPsHelper1: '授权 IP 为空时，则取消授权 IP',\n        allowIPEgs: '当存在多个授权 IP 时，需要换行显示，例： \\n172.16.10.111 \\n172.16.10.0/24',\n        mfa: '两步验证',\n        mfaClose: '关闭两步验证将导致服务安全性降低，是否继续？',\n        secret: '密钥',\n        mfaAlert: '两步验证密码是基于当前时间生成，请确保服务器时间已同步',\n        mfaHelper: '开启后会验证手机应用验证码',\n        mfaHelper1: '下载两步验证手机应用 如:',\n        mfaHelper2: '使用手机应用扫描以下二维码，获取 6 位验证码',\n        mfaHelper3: '输入手机应用上的 6 位数字',\n        mfaCode: '验证码',\n        mfaInterval: '刷新时间（秒）',\n        mfaTitleHelper: '用于区分不同 1Panel 主机，修改后请重新扫描或手动添加密钥信息！',\n        mfaIntervalHelper:\n            '修改刷新时间后，请重新扫描或手动添加密钥信息！\\n部分验证器仅支持 30 秒刷新时间（如 Google/Microsoft Authenticator），请确认兼容性。',\n        passkey: '通行密钥',\n        passkeyManage: '管理',\n        passkeyKeyManagement: '密钥管理',\n        passkeyHelper: '用于快速登录，最多可绑定 5 个通行密钥',\n        passkeyRequireSSL: '需设置域名绑定并通过 HTTPS 访问后才可使用通行密钥',\n        passkeyTrustedProxies: '可信代理',\n        passkeyTrustedProxiesHelper: '仅当请求来源命中以下 IP/CIDR 时，才信任 Forwarded 与 X-Forwarded-Proto 头',\n        passkeyNotSupported: '当前浏览器或环境不支持通行密钥',\n        passkeyPrereqTitle: '使用通行密钥需满足以下前置条件',\n        passkeyPrereqBindDomain: '已设置 1Panel 域名绑定',\n        passkeyPrereqHttps: '已通过 HTTPS 安全访问',\n        passkeyPrereqBrowser: '当前环境支持 Passkey（WebAuthn + 平台认证器）',\n        passkeyPrereqBrowserDetailWebAuthnUnavailable: '请使用系统浏览器访问，并确认已绑定域名且通过 HTTPS 打开面板',\n        passkeyPrereqBrowserDetailPlatformCapabilityUnavailable:\n            '当前环境无法使用设备认证，请改用系统浏览器并确认系统版本较新',\n        passkeyPrereqBrowserDetailNoPlatformAuthenticator: '请先在系统中启用屏幕锁/指纹/面容/Windows Hello 后重试',\n        passkeyPrereqBrowserDetailDetectFailed: '环境检测失败，请刷新页面重试，或改用系统浏览器访问',\n        passkeyPrereqGoSetup: '去设置',\n        passkeyCount: '已绑定 {0}/{1}',\n        passkeyName: '名称',\n        passkeyNameHelper: '请输入用于区分设备的名称',\n        passkeyAdd: '添加通行密钥',\n        passkeyCreatedAt: '创建时间',\n        passkeyLastUsedAt: '最近使用',\n        passkeyDeleteConfirm: '删除后将无法使用该通行密钥登录，是否继续？',\n        passkeyLimit: '最多可绑定 5 个通行密钥',\n        passkeyFailed: '通行密钥注册失败，请确认面板 SSL 证书是否为可信证书',\n        sslChangeHelper: 'https 设置修改需要重启服务，是否继续？',\n        sslDisable: '禁用',\n        sslDisableHelper: '禁用 https 服务，需要重启面板才能生效，是否继续？',\n        noAuthSetting: '未认证设置',\n        noAuthSettingHelper: '用户在未登录且未正确输入安全入口、授权 IP、或绑定域名时，该响应可隐藏面板特征。',\n        responseSetting: '响应设置',\n        help200: '帮助页面',\n        error400: '错误请求',\n        error401: '未授权',\n        error403: '禁止访问',\n        error404: '未找到',\n        error408: '请求超时',\n        error416: '无效请求',\n        error444: '连接被关闭',\n        error500: '内部错误',\n\n        https: '为面板设置HTTPS可提升访问安全性',\n        strictHelper: '非 HTTPS 流量无法连接面板',\n        muxHelper: '面板将同时监听 HTTP 和 HTTPS 协议，并将 HTTP 重定向到 HTTPS，此模式下可能会降低面板安全性',\n        certType: '证书类型',\n        selfSigned: '自签名',\n        selfSignedHelper: '自签证书，不被浏览器信任，显示不安全是正常现象',\n        select: '选择已有',\n        domainOrIP: '域名或 IP：',\n        timeOut: '过期时间：',\n        rootCrtDownload: '根证书下载',\n        primaryKey: '密钥',\n        certificate: '证书',\n\n        about: '关于',\n        versionItem: '当前版本',\n        backupCopies: '备份份数',\n        backupCopiesHelper: '设置用于版本回滚的升级备份保留份数，为 0 则保留所有。',\n        backupCopiesRule: '请至少保存 3 份升级备份记录',\n        release: '版本更新日志',\n        releaseHelper: '当前环境更新日志获取异常，可手动前往官方文档查看',\n        project: '项目地址',\n        issue: '问题反馈',\n        doc: '官方文档',\n        star: '点亮 Star',\n        description: 'Linux 服务器运维管理面板',\n        forum: '论坛求助',\n        doc2: '使用手册',\n        currentVersion: '当前运行版本：',\n\n        license: '许可证',\n        bindNode: '绑定节点',\n        menuSetting: '菜单设置',\n        menuSettingHelper: '当只存在 1 个子菜单时，菜单栏将仅展示该子菜单',\n        showAll: '全部显示',\n        hideALL: '全部隐藏',\n        ifShow: '是否显示',\n        menu: '菜单',\n        confirmMessage: '即将刷新页面更新高级功能菜单列表，是否继续？',\n        recoverMessage: '即将刷新页面恢复菜单列表到初始状态，是否继续？',\n        compressPassword: '压缩密码',\n        backupRecoverMessage: '请输入压缩或解压缩密码（留空则不设置）',\n    },\n    license: {\n        offLine: '离线版',\n        community: '社区版',\n        oss: '社区版',\n        pro: '专业版',\n        trial: '试用',\n        add: '添加社区版',\n        licenseBindHelper: '仅当许可证已绑定节点时，才能使用其免费节点额度',\n        licenseAlert: '仅当许可证正常绑定到节点时，该许可证才能添加社区版节点，只有正常绑定到许可证的节点支持切换。',\n        licenseUnbindHelper: '检测到该许可证存在社区版节点，请解绑后重试！',\n        subscription: '订阅',\n        perpetual: '永久授权',\n        versionConstraint: '{0} 版本买断',\n        forceUnbind: '强制解绑',\n        forceUnbindHelper: '强制解绑，会忽略解绑过程中产生的错误并最终解除许可证绑定',\n        updateForce: '强制更新（忽略解绑过程中的所有错误，确保最终操作成功）',\n        trialInfo: '版本',\n        authorizationId: '订阅授权 ID',\n        authorizedUser: '被授权方',\n        lostHelper: '许可证已达到最大重试次数，请手动点击同步按钮，以确保专业版功能正常使用，详情：',\n        exceptionalHelper: '许可证同步验证异常，请手动点击同步按钮，以确保专业版功能正常使用，详情：',\n        quickUpdate: '快速更新',\n        unbindHelper: '解除绑定后将清除该节点所有专业版相关设置，是否继续？',\n        importLicense: '导入许可证',\n        importHelper: '请点击或拖动许可文件到此处',\n        levelUpPro: '升级专业版',\n        licenseSync: '许可证同步',\n        knowMorePro: '了解更多',\n        closeAlert: '当前页面可在面板设置中关闭显示',\n        introduce: '功能介绍',\n        waf: '升级专业版可以获得拦截地图、日志、封锁记录、地理位置封禁、自定义规则、自定义拦截页面等功能。',\n        tamper: '升级专业版可以保护网站免受未经授权的修改或篡改。',\n        tamperHelper: '操作失败，该文件或文件夹已经开启防篡改，请检查后重试！',\n        setting: '升级专业版可以自定义面板 Logo、欢迎简介等信息。',\n        monitor: '升级专业版可以查看网站的实时状态、访客趋势、访客来源、请求日志等信息。',\n        alert: '升级专业版可通过短信接收告警信息，并查看告警日志，全面掌控各类关键事件，确保系统运行无忧。',\n        node: '升级专业版可以使用 1Panel 管理多台 linux 服务器。',\n        nodeApp: '升级专业版可以统一升级多节点应用版本，无需手动切换节点。',\n        nodeDashboard: '升级专业版可以集中管理多节点应用、网站、数据库和计划任务。',\n        fileExchange: '升级专业版可以在多台服务器之间快速传输文件。',\n        app: '升级专业版可通过手机 APP，查看服务信息、异常监控等。',\n        cluster: '升级专业版可以管理 MySQL/Postgres/Redis 主从集群。',\n        vllm: '升级专业版可将 Vllm 服务统一纳入 1Panel 管理，集中完成部署、配置调整、状态运维与任务追踪，降低本地模型服务维护成本。',\n    },\n    clean: {\n        scan: '开始扫描',\n        scanHelper: '轻松梳理 1Panel 运行期间积累的垃圾文件',\n        clean: '立即清理',\n        cleanHelper: '已勾选文件及目录清理后无法回滚，是否继续？',\n        statusSuggest: '( 建议清理 )',\n        statusClean: '( 很干净 )',\n        statusEmpty: '非常干净，无需清理！',\n        statusWarning: '( 谨慎操作 )',\n        lastCleanTime: '上次清理时间: {0}',\n        lastCleanHelper: '清理文件及目录：{0} 个， 总计清理：{1}',\n        cleanSuccessful: '清理成功！',\n        currentCleanHelper: '本次清理文件及目录：{0} 个， 总计清理：{1}',\n        totalScan: '待清理垃圾文件共计： ',\n        selectScan: '已选中垃圾文件共计： ',\n\n        system: '系统垃圾',\n        systemHelper: '快照、升级等过程中产生的临时文件',\n        panelOriginal: '系统快照恢复前备份文件',\n        upgrade: '系统升级备份文件',\n        agentPackages: '历史版本子节点升级 / 安装包',\n        upgradeHelper: '( 建议保留最新的升级备份用于系统回滚 )',\n        snapshot: '系统快照临时文件',\n        rollback: '恢复前备份目录',\n\n        backup: '系统备份',\n        backupHelper: '本地备份账号中未关联的备份文件',\n        unknownBackup: '系统备份',\n        tmpBackup: '临时备份',\n        unknownApp: '未关联应用备份',\n        unknownDatabase: '未关联数据库备份',\n        unknownWebsite: '未关联网站备份',\n        unknownSnapshot: '未关联快照备份',\n\n        upload: '临时上传文件',\n        uploadHelper: '系统上传备份列表中上传的临时文件',\n        download: '临时下载文件',\n        downloadHelper: '系统从第三方备份账号下载的临时文件',\n        directory: '文件夹',\n\n        systemLog: '日志文件',\n        systemLogHelper: '系统日志、任务日志、网站日志文件',\n        dockerLog: '容器操作日志文件',\n        shell: 'Shell 脚本计划任务',\n        containerShell: '容器内执行 Shell 脚本计划任务',\n        curl: 'CURL 计划任务',\n\n        docker: '容器垃圾',\n        dockerHelper: '容器、镜像、存储卷、构建缓存等文件',\n        volumes: '存储卷',\n        buildCache: '构建缓存',\n\n        appTmpDownload: '应用临时下载文件',\n        unknownWebsiteLog: '未关联网站日志备份文件',\n    },\n    app: {\n        app: '应用',\n        installName: '安装名称',\n        installed: '已安装',\n        all: '全部',\n        version: '版本',\n        detail: '详情',\n        params: '参数',\n        author: '作者',\n        source: '来源',\n        appName: '应用名称',\n        deleteWarn: '删除操作会把所有数据和备份一并删除，此操作不可回滚，是否继续？',\n        syncSuccess: '同步成功',\n        canUpgrade: '可升级',\n        backupName: '文件名称',\n        backupPath: '文件路径',\n        backupdate: '备份时间',\n        versionSelect: '请选择版本',\n        operatorHelper: '将对选中应用进行 {0} 操作，是否继续？',\n        checkInstalledWarn: '未检测到 {0} ,请进入应用商店点击安装！',\n        limitHelper: '该应用已安装，不支持重复安装',\n        deleteHelper: '{0}已经关联以下资源，请检查后重试！',\n        checkTitle: '提示',\n        defaultConfig: '默认配置',\n        defaultConfigHelper: '已恢复为默认配置，保存后生效',\n        forceDelete: '强制删除',\n        forceDeleteHelper: '强制删除，会忽略删除过程中产生的错误并最终删除元数据',\n        deleteBackup: '删除备份',\n        deleteBackupHelper: '同时删除应用备份',\n        deleteDB: '删除数据库',\n        deleteDBHelper: '同时删除与应用关联的数据库',\n        noService: '无{0}',\n        toInstall: '去安装',\n        param: '参数配置',\n        syncAppList: '更新远程应用',\n        alreadyRun: '已安装',\n        less1Minute: '小于1分钟',\n        appOfficeWebsite: '官方网站',\n        github: '开源社区',\n        document: '文档说明',\n        updatePrompt: '当前应用均为最新版本',\n        installPrompt: '尚未安装任何应用',\n        updateHelper: '更新参数可能导致应用无法启动，请提前备份并谨慎操作',\n        updateWarn: '更新参数需要重建应用，是否继续？',\n        busPort: '服务端口',\n        syncStart: '开始同步！请稍后刷新应用商店',\n        advanced: '高级设置',\n        cpuCore: '核心数',\n        containerName: '容器名称',\n        containerNameHelper: '可以为空，为空自动生成',\n        allowPort: '端口外部访问',\n        allowPortHelper: '允许端口外部访问会放开防火墙端口',\n        appInstallWarn: '应用端口默认不允许外部访问，可以在下方高级设置中选择放开',\n        upgradeStart: '开始升级！请稍后刷新页面',\n        toFolder: '进入安装目录',\n        editCompose: '编辑 compose 文件',\n        editComposeHelper: '编辑 compose 文件可能导致软件安装失败',\n        composeNullErr: 'compose 不能为空',\n        takeDown: '已废弃',\n        allReadyInstalled: '已安装',\n        installHelper: '配置镜像加速可以解决镜像拉取失败的问题',\n        sortMode: '排序',\n        installWarn: '当前未勾选端口外部访问，将无法通过外网IP:端口访问，是否继续？',\n        showIgnore: '查看忽略应用',\n        cancelIgnore: '取消忽略',\n        ignoreList: '忽略列表',\n        appHelper: '部分应用的安装使用说明请在应用详情页查看',\n        backupApp: '升级前备份应用',\n        backupAppHelper: '升级失败会使用备份自动回滚,请在日志审计-系统日志中查看失败原因',\n        openrestyDeleteHelper: '强制删除 OpenResty 会删除所有的网站，请确认风险之后操作',\n        downloadLogHelper1: '即将下载 {0} 应用所有日志，是否继续？',\n        downloadLogHelper2: '即将下载 {0} 应用最近 {1} 条日志，是否继续？',\n        syncAllAppHelper: '即将同步所有应用，是否继续？',\n        hostModeHelper: '当前应用网络模式为 host 模式，如需放开端口，请在防火墙页面手动放开',\n        showLocal: '本地应用',\n        reload: '重载',\n        upgradeWarn: '升级应用会替换 docker-compose.yml 文件，如有更改，可以点击查看文件对比',\n        openclawHttpsUpgradeNoticeTitle: '注意：以下说明仅适用于 OpenClaw 从 2026.3.13 之前版本升级的用户：',\n        openclawHttpsUpgradeNoticeItem1: '已部署的智能体升级完成后，需要进入 配置 → 设置 → 安全 页面手动添加访问地址。',\n        openclawHttpsUpgradeNoticeItem2:\n            '新版本已强制使用 HTTPS 访问智能体。如果之前通过反向代理网站访问，请将代理目标地址修改为 https://IP:Port。',\n        newVersion: '新版本',\n        oldVersion: '当前版本',\n        composeDiff: '文件对比',\n        showDiff: '查看对比',\n        useNew: '使用自定义版本',\n        useDefault: '使用默认版本',\n        useCustom: '自定义 docker-compose.yml',\n        useCustomHelper: '使用自定义 docker-compose.yml 文件，可能会导致应用升级失败，如无必要，请勿勾选',\n        diffHelper: '左侧为旧版本，右侧为新版，编辑之后点击使用自定义版本保存',\n        pullImage: '拉取镜像',\n        pullImageHelper: '在应用启动之前执行 docker pull 来拉取镜像',\n        deleteImage: '删除镜像',\n        deleteImageHelper: '删除应用相关镜像，删除失败任务不会终止',\n        requireMemory: '内存需求',\n        supportedArchitectures: '支持架构',\n        link: '链接',\n        showCurrentArch: '本服务器架构应用',\n        syncLocalApp: '同步本地应用',\n        memoryRequiredHelper: '当前应用内存需求 {0}',\n        gpuConfig: '开启 GPU 支持',\n        gpuConfigHelper: '请确保机器有 NVIDIA GPU 并且安装 NVIDIA 驱动 和 NVIDIA docker Container Toolkit',\n        webUI: 'Web 访问地址',\n        webUIPlaceholder: '例如：example.com:8080/login',\n        defaultWebDomain: '默认访问地址',\n        defaultWebDomainHepler: '如果应用端口为 8080，则跳转地址为 http(s)://默认访问地址:8080',\n        webUIConfig: '当前应用尚未配置访问地址，请在应用参数或前往面板设置进行设置！',\n        toLink: '跳转',\n        customAppHelper: '在使用自定义应用商店仓库之前，请确保没有任何已安装的应用。',\n        forceUninstall: '强制卸载',\n        syncCustomApp: '同步自定义应用',\n        ignoreAll: '忽略后续所有版本',\n        ignoreVersion: '忽略指定版本',\n        specifyIP: '绑定主机 IP',\n        specifyIPHelper: '设置端口绑定的主机地址/网卡（如果你不清楚这个的作用，请不要填写）',\n        uninstallDeleteBackup: '卸载应用-删除备份',\n        uninstallDeleteImage: '卸载应用-删除镜像',\n        upgradeBackup: '应用升级前备份应用',\n        noAppHelper: '未检测到应用，请前往任务中心查看应用商店同步日志',\n        isEdirWarn: '检测到 docker-compose.yml 文件被修改，请查看对比',\n    },\n    website: {\n        primaryDomain: '主域名',\n        otherDomains: '其他域名',\n        static: '静态网站',\n        deployment: '一键部署',\n        supportUpType: '仅支持 .tar.gz 文件格式，且压缩包内必须包含 {0}.json 文件',\n        proxy: '反向代理',\n        alias: '代号',\n        ftpUser: 'FTP 账号',\n        ftpPassword: 'FTP 密码',\n        ftpHelper: '创建站点的同时，为站点创建一个对应 FTP 帐户，并且 FTP 目录指向站点所在目录。',\n        remark: '备注',\n        groupSetting: '分组管理',\n        createGroup: '创建分组',\n        appNew: '新装应用',\n        appInstalled: '已装应用',\n        delete: '删除网站',\n        deleteApp: '删除应用',\n        deleteBackup: '删除备份',\n        domain: '域名',\n        domainHelper: '一行一个域名，支持*和IP地址，支持\"域名:端口\"',\n        addDomain: '新增域名',\n        domainConfig: '域名设置',\n        defaultDoc: '默认文档',\n        perserver: '并发限制',\n        perserverHelper: '限制当前站点最大并发数',\n        perip: '单 IP 限制',\n        peripHelper: '限制单个 IP 访问最大并发数',\n        rate: '流量限制',\n        rateLimit: '单请求限速',\n        rateHelper: '限制每个请求的流量上限(单位:KB)',\n        rateLimitHelper: '限制每个请求的每秒传输速率（单位：KB/s）',\n        limitHelper: '启用流量控制',\n        other: '其他',\n        currentSSL: '当前证书',\n        dnsAccount: 'DNS 账号',\n        applySSL: '证书申请',\n        SSLList: '证书列表',\n        createDnsAccount: 'DNS 账户',\n        aliyun: '阿里云',\n        aliEsa: '阿里云 ESA',\n        awsRoute53: 'Amazon Route 53',\n        manual: '手动解析',\n        key: '密钥',\n        check: '查看',\n        acmeAccountManage: 'Acme 账户',\n        email: '邮箱',\n        acmeAccount: 'Acme 账户',\n        provider: '验证方式',\n        dnsManual: '手动解析',\n        expireDate: '过期时间',\n        brand: '颁发组织',\n        deploySSL: '部署',\n        deploySSLHelper: '确定部署证书？',\n        ssl: '证书',\n        dnsAccountManage: 'DNS 账户',\n        renewSSL: '续签',\n        renewHelper: '确定续签证书？',\n        renewSuccess: '续签证书',\n        enableHTTPS: '启用 HTTPS',\n        aliasHelper: '代号是网站目录的文件夹名称',\n        lastBackupAt: '上次备份时间',\n        null: '无',\n        nginxConfig: 'Nginx配置',\n        websiteConfig: '网站设置',\n        proxySettings: '代理设置',\n        advancedSettings: '高级设置',\n        cacheSettings: '缓存设置',\n        sniSettings: 'SNI 设置',\n        basic: '基本',\n        source: '配置文件',\n        security: '安全',\n        nginxPer: '性能调整',\n        neverExpire: '永不过期',\n        setDefault: '设为默认',\n        deleteHelper: '相关应用状态不正常，请检查',\n        toApp: '去已安装列表',\n        cycle: '周期',\n        frequency: '频率',\n        ccHelper: '{0} 秒内累计请求同一URL超过 {1} 次,触发CC防御,封锁此IP',\n        mustSave: '修改之后需要保存才能生效',\n        fileExt: '文件扩展名',\n        fileExtBlock: '文件扩展名黑名单',\n        value: '值',\n        enable: '开启',\n        proxyAddress: '代理地址',\n        proxyHelper: '例: 127.0.0.1:8080',\n        forceDelete: '强制删除',\n        forceDeleteHelper: '强制删除，会忽略删除过程中产生的错误并最终删除元数据',\n        deleteAppHelper: '同时删除关联应用、数据库以及应用备份',\n        deleteBackupHelper: '同时删除网站备份',\n        deleteDatabaseHelper: '同时删除网站关联数据库',\n        deleteConfirmHelper: '删除操作无法回滚，请输入 <span style=\"color:red\"> \"{0}\" </span> 删除',\n        staticPath: '对应主目录:',\n        limit: '限制方案',\n        blog: '论坛/博客',\n        imageSite: '图片站',\n        downloadSite: '下载站',\n        shopSite: '商城',\n        doorSite: '门户',\n        qiteSite: '企业',\n        videoSite: '视频',\n        errLog: '错误日志',\n        stopHelper: '停止站点后将无法正常访问，用户访问会显示当前站点停止页面，是否继续操作？',\n        startHelper: '启用站点后，用户可以正常访问网站内容，是否继续操作？',\n        sitePath: '网站目录',\n        siteAlias: '网站代号',\n        primaryPath: 'root 目录',\n        folderTitle: '网站主要包含以下文件夹',\n        wafFolder: '防火墙规则',\n        indexFolder: '网站 root 目录（PHP 运行环境 静态网站代码存放目录）',\n        sslFolder: '网站证书',\n        enableOrNot: '是否启用',\n        oldSSL: '选择已有证书',\n        manualSSL: '手动导入证书',\n        select: '选择',\n        selectSSL: '选择证书',\n        privateKey: '私钥(KEY)',\n        certificate: '证书(PEM格式)',\n        HTTPConfig: 'HTTP 选项',\n        HTTPSOnly: '禁止 HTTP',\n        HTTPToHTTPS: '访问HTTP自动跳转到HTTPS',\n        HTTPAlso: 'HTTP可直接访问',\n        sslConfig: 'SSL 选项',\n        disableHTTPS: '禁用 HTTPS',\n        disableHTTPSHelper: '禁用 HTTPS会删除证书相关配置，是否继续？',\n        SSLHelper: '注意：请勿将SSL证书用于非法网站 \\n 如开启后无法使用HTTPS访问，请检查安全组是否正确放行443端口',\n        SSLConfig: '证书设置',\n        SSLProConfig: 'SSL 协议设置',\n        supportProtocol: '支持的协议版本',\n        encryptionAlgorithm: '加密算法',\n        notSecurity: '（不安全）',\n        encryptHelper:\n            \"Let's Encrypt 签发证书有频率限制，但足以满足正常需求，过于频繁操作会导致签发失败。具体限制请看 <a target=“_blank” href='https://letsencrypt.org/zh-cn/docs/rate-limits/'>官方文档</a> \",\n        ipValue: '值',\n        ext: '文件扩展名',\n        wafInputHelper: '按行输入数据，一行一个',\n        data: '数据',\n        ever: '永久',\n        nextYear: '一年后',\n        noLog: '当前没有日志...',\n        defaultServer: '默认站点',\n        noDefaultServer: '未设置',\n        defaultServerHelper:\n            '设置默认站点后,所有未绑定的域名和IP都被定向到默认站点\\n可有效防止恶意解析\\n但同时会导致 WAF 未授权域名拦截失败',\n        websiteDeploymentHelper: '使用从 1Panel 部署的应用创建网站',\n        websiteStatictHelper: '在主机上创建网站目录',\n        websiteProxyHelper:\n            '代理已有服务。例如本机已安装使用 8080 端口的 halo 服务，那么代理地址为 http://127.0.0.1:8080',\n        restoreHelper: '确认使用此备份恢复？',\n        wafValueHelper: '值',\n        runtimeProxyHelper: '使用从 1Panel 创建的运行环境',\n        runtime: '运行环境',\n        deleteRuntimeHelper: '运行环境应用需要跟网站一并删除，请谨慎处理',\n        proxyType: '监听网络类型',\n        unix: 'Unix 网络',\n        tcp: 'TCP/IP 网络',\n        phpFPM: 'FPM 配置文件',\n        phpConfig: 'PHP 配置文件',\n        updateConfig: '配置修改',\n        isOn: '开启',\n        isOff: '关闭',\n        rewrite: '伪静态',\n        rewriteMode: '方案',\n        current: '当前',\n        rewriteHelper: '若设置伪静态后，网站无法正常访问，请尝试设置回default',\n        runDir: '运行目录',\n        runUserHelper:\n            '通过 PHP 容器运行环境部署的网站，需要将 index 和子目录下的所有文件、文件夹所有者和用户组设置为 1000，本地 PHP 环境需要参考本地 PHP-FPM 用户和用户组设置',\n        userGroup: '运行用户/组',\n        uGroup: '用户组',\n        proxyPath: '前端请求路径',\n        proxyPass: '后端代理地址',\n        cache: '缓存',\n        cacheTime: '缓存时间',\n        enableCache: '开启缓存',\n        proxyHost: '后端域名',\n        disabled: '已停止',\n        startProxy: '开启反向代理',\n        stopProxy: '关闭反向代理',\n        sourceFile: '源文',\n        proxyHelper1: '访问这个目录时将会把目标URL的内容返回并显示',\n        proxyPassHelper: '代理的站点，必须为可正常访问的URL',\n        proxyHostHelper: '将域名添加到请求头传递到代理服务器',\n        modifier: '匹配规则',\n        modifierHelper: '例：= 精确匹配，~ 正则匹配，^~ 匹配路径开头 等',\n        replace: '文本替换',\n        replaceHelper:\n            'nginx文本替换功能可以在反向代理时对响应内容进行字符串替换。常用于修改代理后端返回的HTML、CSS、JavaScript等文件中的链接地址、API接口地址等。支持正则表达式匹配，可实现复杂的内容替换需求。',\n        addReplace: '添加文本替换',\n        replaced: '搜索字符串（不能为空）',\n        replaceText: '替换为字符串',\n        replacedErr: '搜索字符串不能为空',\n        replacedErr2: '搜索字符串不能重复',\n        replacedListEmpty: '无文本替换规则',\n        proxySslName: '代理 SNI 名称',\n        basicAuth: '密码访问',\n        editBasicAuthHelper: '密码为非对称加密，无法回显，编辑需要重新设置密码',\n        antiLeech: '防盗链',\n        extends: '扩展名',\n        browserCache: '浏览器缓存',\n        noModify: '不修改',\n        serverCache: '服务器缓存',\n        leechLog: '记录防盗链日志',\n        accessDomain: '允许的域名',\n        leechReturn: '响应资源',\n        noneRef: '允许 Referer 为空',\n        disable: '未启用',\n        disableLeechHelper: '是否禁用防盗链',\n        disableLeech: '禁用防盗链',\n        ipv6: '监听 IPV6',\n        leechReturnError: '请填写 HTTP 状态码',\n        blockedRef: '允许非标准 Referer',\n        accessControl: '防盗链控制',\n        leechcacheControl: '缓存控制',\n        logEnableControl: '记录请求日志',\n        leechSpecialValidHelper:\n            '允许 Referer 为空启用时，不会阻止无 Referer 的请求（直接访问等），启用非标准 Referer 时会放行任何不以 http/https 开头的 Referer 请求（客户端请求等）',\n        leechInvalidReturnHelper: '拦截盗链请求后，返回的 HTTP 状态码',\n        leechlogControlHelper: '记录静态资源的请求，生产环境通常可以关闭避免过多无意义的日志',\n        selectAcme: '选择 acme 账号',\n        imported: '手动创建',\n        importType: '导入方式',\n        pasteSSL: '粘贴代码',\n        localSSL: '选择服务器文件',\n        privateKeyPath: '私钥文件',\n        certificatePath: '证书文件',\n        ipWhiteListHelper: 'IP 白名单的作用：所有规则对IP白名单无效',\n        redirect: '重定向',\n        sourceDomain: '源域名/路径',\n        targetURL: '目标URL地址',\n        keepPath: '保留URI参数',\n        path: '路径',\n        redirectType: '重定向类型',\n        redirectWay: '方式',\n        keep: '保留',\n        notKeep: '不保留',\n        redirectRoot: '重定向到首页',\n        redirectHelper: '301永久重定向，302临时重定向',\n        changePHPVersionWarn: '此操作不可回滚，是否继续',\n        changeVersion: '切换版本',\n        retainConfig: '是否保留 php-fpm.conf 和 php.ini 文件',\n        runDirHelper2: '请确保二级运行目录位于 index 目录下',\n        openrestyHelper: 'OpenResty 默认 HTTP 端口：{0} HTTPS 端口 ：{1}，可能影响网站域名访问和 HTTPS 强制跳转',\n        primaryDomainHelper: '支持域名:端口',\n        acmeAccountType: '账号类型',\n        keyType: '密钥算法',\n        tencentCloud: '腾讯云',\n        containWarn: '其他域名中包含主域名，请重新输入',\n        rewriteHelper2: '从应用商店安装的 WordPress 等应用，默认已经配置好伪静态，重复配置可能会报错',\n        websiteBackupWarn: '仅支持导入本机备份，导入其他机器备份可能会恢复失败',\n        ipWebsiteWarn: 'IP 为域名的网站，需要设置为默认站点才能正常访问',\n        hstsHelper: '开启 HSTS 可以增加网站安全性',\n        includeSubDomains: '子域',\n        hstsIncludeSubDomainsHelper: '启用后，HSTS策略将应用于当前域名的所有子域名',\n        defaultHtml: '默认页面',\n        website404: '网站 404 错误页',\n        domain404: '网站不存在页',\n        indexHtml: '静态网站默认页',\n        stopHtml: '网站停用页',\n        indexPHP: 'PHP 网站默认页',\n        sslExpireDate: '证书过期时间',\n        website404Helper: '网站 404 错误页仅支持 PHP 运行环境网站和静态网站',\n        sni: '回源 SNI',\n        sniHelper: '反代后端为 https 的时候可能需要设置回源 SNI，具体需要看 CDN 服务商文档',\n        huaweicloud: '华为云',\n        createDb: '创建数据库',\n        enableSSLHelper: '开启失败不会影响网站创建',\n        batchAdd: '批量添加域名',\n        batchInput: '批量编辑',\n        domainNotFQDN: '该域名可能无法在公网访问',\n        domainInvalid: '域名格式不正确',\n        domainBatchHelper: '一行一个域名，格式：域名:端口\\n示例：example.com',\n        generateDomain: '生成',\n        global: '全局',\n        subsite: '子网站',\n        subsiteHelper: '子网站可以选择已存在的 PHP 和静态网站的目录作为主目录',\n        parentWbeiste: '父级网站',\n        deleteSubsite: '删除当前网站需要先删除子网站 {0}',\n        loadBalance: '负载均衡',\n        server: '节点',\n        algorithm: '算法',\n        ipHash: 'IP 哈希',\n        ipHashHelper: '基于客户端 IP 地址将请求分配到特定服务器，可以确保特定客户端总是被路由到同一服务器',\n        leastConn: '最小连接',\n        leastConnHelper: '将请求发送到当前活动连接数最少的服务器',\n        leastTime: '最小时间',\n        leastTimeHelper: '将请求发送到当前活动连接时间最短的服务器',\n        defaultHelper:\n            '默认方法，请求被均匀分配到每个服务器，如果服务器有权重配置，则根据指定的权重分配请求，权重越高的服务器接收更多请求',\n        weight: '权重',\n        maxFails: '最大失败次数',\n        maxConns: '最大连接数',\n        strategy: '策略',\n        strategyDown: '停用',\n        strategyBackup: '备用',\n        ipHashBackupErr: 'IP 哈希不支持备用节点',\n        failTimeout: '故障超时',\n        failTimeoutHelper:\n            '服务器健康检查的时间窗口长度。在该时间段内累计失败次数达到阈值时，服务器将被暂时移除，并在经过相同时长后重新尝试。默认 10 秒',\n\n        staticChangePHPHelper: '当前为静态网站，可以切换为 PHP 网站',\n        proxyCache: '反代缓存',\n        cacheLimit: '缓存空间限制',\n        shareCahe: '缓存计数内存大小',\n        cacheExpire: '缓存过期时间',\n        shareCaheHelper: '每1M内存可以存储约8000个缓存对象',\n        cacheLimitHelper: '超过限制会自动删除旧的缓存',\n        cacheExpireJHelper: '超出时间缓存未命中将会被删除',\n        realIP: '真实 IP',\n        ipFrom: 'IP 来源',\n        ipFromHelper:\n            '通过配置可信 IP 来源，OpenResty 会分析 HTTP Header 中的 IP 信息，准确识别并记录访客的真实 IP 地址，包括在访问日志中',\n        ipFromExample1: '如果前端是 Frp 等工具，可以填写 Frp 的 IP 地址，类似 127.0.0.1',\n        ipFromExample2: '如果前端是 CDN，可以填写 CDN 的 IP 地址段',\n        ipFromExample3: '如果不确定，可以填 0.0.0.0/0（ipv4）  ::/0（ipv6） [注意：允许任意来源 IP 不安全]',\n        http3Helper:\n            'HTTP/3 是 HTTP/2 的升级版本，提供更快的连接速度和更好的性能，但是不是所有浏览器都支持 HTTP/3，开启后可能会导致部分浏览器无法访问',\n        cors: '跨域访问',\n        enableCors: '开启跨域',\n        allowOrigins: '允许访问的域名',\n        allowMethods: '允许的请求方法',\n        allowHeaders: '允许的请求头',\n        allowCredentials: '允许携带cookies',\n        preflight: '预检请求快速响应',\n        preflightHleper:\n            '开启后，当浏览器发送跨域预检请求（OPTIONS 请求）时，系统会自动返回 204 状态码并设置必要的跨域响应头',\n\n        changeDatabase: '切换数据库',\n        changeDatabaseHelper1: '数据库关联用于备份恢复网站',\n        changeDatabaseHelper2: '切换其他数据库会导致以前的备份无法恢复',\n        saveCustom: '另存为模版',\n        rainyun: '雨云',\n        volcengine: '火山引擎',\n        runtimePortHelper: '当前运行环境存在多个端口，请选择一个代理端口',\n        runtimePortWarn: '当前运行环境没有端口，无法代理',\n        cacheWarn: '请先关闭反代中的缓存开关',\n        loadBalanceHelper: '创建负载均衡后，请前往‘反向代理’，添加代理并将后端地址设置为：http://<负载均衡名称>。',\n        favorite: '收藏',\n        cancelFavorite: '取消收藏',\n        useProxy: '使用代理',\n        useProxyHelper: '使用面板设置中的代理服务器地址',\n        westCN: '西部数码',\n        openBaseDir: '防跨站攻击',\n        openBaseDirHelper: 'open_basedir 用于限制 PHP 文件访问路径，有助于防止跨站访问和提升安全性',\n        serverCacheTime: '服务器缓存时间',\n        serverCacheTimeHelper: '请求在服务器端缓存的时间，到期前相同请求会直接返回缓存结果，不再请求源站。',\n        browserCacheTime: '浏览器缓存时间',\n        browserCacheTimeHelper: '静态资源在浏览器本地缓存的时间，减少重复请求。到期前用户刷新页面会直接使用本地缓存。',\n        donotLinkeDB: '不关联数据库',\n        toWebsiteDir: '进入网站目录',\n        execParameters: '执行参数',\n        extCommand: '补充命令',\n        mirror: '镜像源',\n        execUser: '执行用户',\n        execDir: '执行目录',\n        packagist: '中国全量镜像',\n\n        batchOperate: '批量操作',\n        batchOperateHelper: '批量{0}网站，是否继续操作？',\n        stream: 'TCP/UDP 代理',\n        streamPorts: '监听端口',\n        streamPortsHelper: '设置对外监听的端口号，客户端将通过此端口访问服务，按逗号分割，例如：5222,5223',\n        streamHelper: 'TCP/UDP 端口转发与负载均衡',\n        udp: '启用 UDP',\n\n        syncHtmlHelper: '同步到 PHP 和静态网站',\n    },\n    php: {\n        short_open_tag: '短标签支持',\n        max_execution_time: '最大脚本运行时间',\n        max_input_time: '最大输入时间',\n        memory_limit: ' 脚本内存限制',\n        post_max_size: 'POST数据最大尺寸',\n        file_uploads: '是否允许上传文件',\n        upload_max_filesize: '允许上传文件的最大尺寸',\n        max_file_uploads: '允许同时上传文件的最大数量',\n        default_socket_timeout: 'Socket超时时间',\n        error_reporting: '错误级别',\n        display_errors: '是否输出详细错误信息',\n        disableFunction: '禁用函数',\n        disableFunctionHelper: '输入要禁用的函数，例如exec，多个请用,分割',\n        uploadMaxSize: '上传限制',\n        indexHelper: '为保障 PHP 网站正常运行，请将代码放置于主目录下的 index 目录，并避免重命名',\n        extensions: '扩展模版',\n        extension: '扩展',\n        extensionsHelper: '多个扩展请用,分割',\n        toExtensionsList: '查看扩展列表',\n        containerConfig: '容器配置',\n        containerConfigHelper: '环境变量等信息可以在创建完成之后在配置-容器配置中修改',\n        dateTimezoneHelper: '示例：TZ=Asia/Shanghai（请根据需要自行添加）',\n    },\n    nginx: {\n        serverNamesHashBucketSizeHelper: '服务器名字的hash表大小',\n        clientHeaderBufferSizeHelper: '客户端请求的头buffer大小',\n        clientMaxBodySizeHelper: '最大上传文件',\n        keepaliveTimeoutHelper: '连接超时时间',\n        gzipMinLengthHelper: '最小压缩文件',\n        gzipCompLevelHelper: '压缩率',\n        gzipHelper: '是否开启压缩传输',\n        connections: '活动连接(Active connections)',\n        accepts: '总连接次数(accepts)',\n        handled: '总握手次数(handled)',\n        requests: '总请求数(requests)',\n        reading: '请求数(Reading)',\n        writing: '响应数(Writing)',\n        waiting: '驻留进程(Waiting)',\n        status: '当前状态',\n        configResource: '配置修改',\n        saveAndReload: '保存并重载',\n        clearProxyCache: '清除反代缓存',\n        clearProxyCacheWarn: '此操作将删除缓存目录下的所有文件, 是否继续？',\n        create: '新增模块',\n        update: '编辑模块',\n        params: '参数',\n        packages: '软件包',\n        script: '脚本',\n        module: '模块',\n        build: '构建',\n        buildWarn: '构建 OpenResty 需要预留一定的 CPU 和内存，时间较长，请耐心等待',\n        mirrorUrl: '软件源',\n        paramsHelper: '例如：--add-module=/tmp/ngx_brotli',\n        packagesHelper: '例如：git,curl 按,分割',\n        scriptHelper: '编译之前执行的脚本，一般为下载模块源码，安装依赖等',\n        buildHelper: '添加/修改模块之后点击构建，构建成功后会自动重启 OpenResty',\n        defaultHttps: 'HTTPS 防窜站',\n        defaultHttpsHelper1: '开启后可以解决 HTTPS 窜站问题',\n        sslRejectHandshake: '拒绝默认 SSL 握手',\n        sslRejectHandshakeHelper: '开启之后可以避免证书泄露，设置默认网站会让此设置失效',\n    },\n    ssl: {\n        create: '申请证书',\n        provider: '类型',\n        manualCreate: '手动创建',\n        acmeAccount: 'Acme 账号',\n        resolveDomain: '解析域名',\n        err: '错误',\n        value: '记录值',\n        dnsResolveHelper: '请到DNS解析服务商处添加以下解析记录：',\n        detail: '详情',\n        msg: '证书信息',\n        ssl: '证书',\n        key: '私钥',\n        startDate: '生效时间',\n        organization: '签发机构',\n        renewConfirm: '是否确定给域名 {0} 申请证书？',\n        autoRenew: '自动续签',\n        autoRenewHelper: '距离到期时间30天自动续签',\n        renewSuccess: '续签成功',\n        renewWebsite: '该证书已经和以下网站关联，申请会同步应用到这些网站',\n        createAcme: '创建账户',\n        acmeHelper: 'Acme 账户用于申请免费证书',\n        upload: '上传证书',\n        applyType: '申请方式',\n        apply: '申请',\n        applyStart: '证书申请开始',\n        getDnsResolve: '正在获取 DNS 解析值,请稍后 ...',\n        selfSigned: '自签证书',\n        ca: '证书颁发机构',\n        commonName: '证书主体名称(CN)',\n        caName: '机构名称',\n        company: '公司/组织',\n        department: '部门',\n        city: '城市',\n        province: '省份',\n        country: '国家代号',\n        commonNameHelper: '例如:',\n        selfSign: '签发证书',\n        days: '有效期',\n        domainHelper: '一行一个域名,支持*和IP地址',\n        pushDir: '推送证书到本地目录',\n        dir: '目录',\n        pushDirHelper: '会在此目录下生成两个文件，证书文件：fullchain.pem 密钥文件：privkey.pem',\n        organizationDetail: '机构详情',\n        fromWebsite: '从网站中获取',\n        dnsMauanlHelper: '手动解析模式需要在创建完之后点击申请按钮获取 DNS 解析值',\n        httpHelper: '使用 HTTP 模式需安装 OpenResty，且不支持申请泛域名证书。',\n        buypassHelper: 'Buypass 大陆地区无法访问',\n        googleHelper: '如何获取 EAB HmacKey 和 EAB kid',\n        googleCloudHelper: 'Google Cloud API 大陆大部分地区无法访问',\n        skipDNSCheck: '跳过 DNS 校验',\n        skipDNSCheckHelper: '如果出现申请超时问题，请勾选此处，其他情况请勿勾选',\n        cfHelper: '请勿使用 Global API Key',\n        deprecated: '即将废弃',\n        deprecatedHelper: '已经停止维护，可能会在以后的某个版本废弃，请使用腾讯云方式解析',\n        disableCNAME: '禁用 CNAME',\n        disableCNAMEHelper: '有 CNAME 配置的域名，如果申请失败，可以勾选此处',\n        nameserver: 'DNS 服务器',\n        nameserverHelper: '使用自定义的 DNS 服务器来校验域名',\n        edit: '编辑证书',\n        execShell: '申请证书之后执行脚本',\n        shell: '脚本内容',\n        shellHelper:\n            '脚本默认执行目录为 1Panel 安装目录，如果有推送证书，那么执行目录为证书推送目录。默认超时时间 30 分钟',\n        customAcme: '自定义 ACME 服务',\n        customAcmeURL: 'ACME 服务 URL',\n        baiduCloud: '百度云',\n        pushNode: '同步到其他节点',\n        pushNodeHelper: '申请/续期之后推送到选择的节点',\n        fromMaster: '主节点推送',\n        hostedZoneID: '托管区域 ID',\n        isIP: 'IP 证书',\n        useEAB: '使用 EAB 认证',\n    },\n    firewall: {\n        create: '创建规则',\n        edit: '编辑规则',\n        advancedControl: '高级控制',\n        advancedControlNotAvailable: '当前使用 {0} 防火墙，高级规则仅支持 iptables',\n        ccDeny: 'CC 防护',\n        ipWhiteList: 'IP 白名单',\n        ipBlockList: 'IP 黑名单',\n        fileExtBlockList: '文件扩展名黑名单',\n        urlWhiteList: 'URL 白名单',\n        urlBlockList: 'URL 黑名单',\n        argsCheck: 'GET 参数校验',\n        postCheck: 'POST 参数校验',\n        cookieBlockList: 'Cookie 黑名单',\n\n        dockerHelper: '当前防火墙无法禁用容器端口映射，已安装应用可前往【已安装】页面编辑应用参数，配置端口放行规则。',\n        iptablesHelper: '检测到系统正在使用 {0} 防火墙，如需切换至 iptables，请先手动卸载！',\n        quickJump: '快速跳转',\n        used: '已使用',\n        unUsed: '未使用',\n        dockerRestart: '防火墙操作需要重启 Docker 服务',\n        firewallHelper: '{0}系统防火墙',\n        firewallNotStart: '当前未开启系统防火墙，请先开启！',\n        restartFirewallHelper: '该操作将对当前防火墙进行重启操作，是否继续？',\n        stopFirewallHelper: '系统防火墙关闭后，服务器将失去安全防护，是否继续？',\n        startFirewallHelper: '系统防火墙开启后，可以更好的防护服务器安全，是否继续？',\n        noPing: '禁 ping',\n        enableBanPing: '禁 ping',\n        disableBanPing: '解除禁 ping',\n        noPingTitle: '是否禁 ping',\n        noPingHelper: '禁 ping 后将无法 ping 通服务器，是否继续？',\n        onPingHelper: '解除禁 ping 后您的服务器可能会被黑客发现，是否继续？',\n        changeStrategy: '修改{0}策略',\n        changeStrategyIPHelper1: 'IP 策略修改为【屏蔽】，设置后该 IP 将禁止访问服务器，是否继续？',\n        changeStrategyIPHelper2: 'IP 策略修改为【放行】，设置后该 IP 将恢复正常访问，是否继续？',\n        changeStrategyPortHelper1: '端口策略修改为【拒绝】，设置后端口将拒绝外部访问，是否继续？',\n        changeStrategyPortHelper2: '端口策略修改为【允许】，设置后端口将恢复正常访问，是否继续？',\n        stop: '禁止',\n        portFormatError: '请输入正确的端口信息！',\n        portHelper1: '多个端口，如：8080,8081',\n        portHelper2: '范围端口，如：8080-8089',\n        strategy: '策略',\n        accept: '允许',\n        drop: '拒绝',\n        anyWhere: '所有 IP',\n        address: '指定 IP',\n        addressHelper: '支持输入 IP 或 IP 段',\n        allow: '放行',\n        deny: '屏蔽',\n        addressFormatError: '请输入合法的 ip 地址！',\n        addressHelper1: '支持输入 IP 或 IP 段：172.16.10.11 或 172.16.0.0/24',\n        addressHelper2: '多个 IP 或 IP 段 请用 \",\" 隔开：172.16.10.11,172.16.0.0/24',\n        allIP: '所有 IP',\n        portRule: '端口规则',\n        forwardRule: '端口转发',\n        ipRule: 'IP 规则',\n        userAgent: 'User-Agent 过滤',\n        destination: '目的地',\n        sourcePort: '源端口',\n        targetIP: '目标 IP',\n        targetPort: '目标端口',\n        forwardHelper1: '如果是本机端口转发，目标IP为：127.0.0.1',\n        forwardHelper2: '如果目标IP不填写，则默认为本机端口转发',\n        forwardPortHelper: '支持端口范围，如：8080-8089',\n        forwardInboundInterface: '转发入站网卡',\n        exportHelper: '即将导出 {0} 条防火墙规则，是否继续？',\n        importSuccess: '成功导入 {0} 条规则',\n        importPartialSuccess: '导入完成：成功 {0} 条，失败 {1} 条',\n\n        ipv4Limit: '当前操作暂仅支持 IPv4 地址',\n        basicStatus: '当前未绑定链 {0} ，请先绑定！',\n        baseIptables: 'iptables 服务',\n        forwardIptables: 'iptables 端口转发服务',\n        advanceIptables: 'iptables 高级配置服务',\n        initMsg: '即将初始化 {0}, 是否继续？',\n        initHelper: '检测到 {0} 未初始化，请点击顶部状态栏的初始化按钮进行配置！',\n        bindHelper: '绑定 仅当状态为绑定时，防火墙规则才能生效，是否确认？',\n        unbindHelper: '解绑 解除绑定时，已添加的所有防火墙规则将失效，请谨慎操作，是否确认？',\n        defaultStrategy: '当前链 {0} 的默认策略为 {1}',\n        defaultStrategy2: '当前链 {0} 的默认策略为 {1}，当前状态为未绑定，已添加的防火墙规则需要绑定后生效！',\n        filterRule: 'Filter 规则',\n        filterHelper: 'Filter 规则允许您在 INPUT/OUTPUT 级别控制网络流量。请谨慎配置，避免锁定系统。',\n        chain: '链',\n        targetChain: '目标链',\n        sourceIP: '源 IP',\n        destIP: '目标 IP',\n        inboundDirection: '入站方向',\n        outboundDirection: '出站方向',\n        destPort: '目标端口',\n        action: '动作',\n        reject: '拒绝',\n        sourceIPHelper: 'CIDR 格式，如 192.168.1.0/24，留空表所有地址',\n        destIPHelper: 'CIDR 格式，如 10.0.0.0/留空表所有地址',\n        portHelper: '0 表示任意端口',\n        allPorts: '所有端口',\n        deleteRuleConfirm: '将删除 {0} 条规则，是否继续？',\n    },\n    runtime: {\n        runtime: '运行环境',\n        workDir: '工作目录',\n        localHelper: '本地环境安装及离线环境使用相关问题，可参考 ',\n        versionHelper: 'PHP的版本,例 v8.0',\n        buildHelper: '扩展越多，制作镜像时占用的 CPU 越高，可在创建环境后再安装扩展',\n        openrestyWarn: 'PHP 需要升级  OpenResty 至 1.21.4.1 版本以上才能使用',\n        toupgrade: '去升级',\n        edit: '编辑运行环境',\n        extendHelper: '未列出的扩展可手动输入并选择，如：输入 sockets 后选择下拉列表中的第一个',\n        rebuildHelper: '编辑扩展后需要【重建】PHP 应用之后才能生效',\n        rebuild: '重建 PHP 应用',\n        source: 'PHP 扩展源',\n        ustc: '中国科学技术大学',\n        netease: '网易',\n        aliyun: '阿里云',\n        tsinghua: '清华大学',\n        xtomhk: 'XTOM 镜像站（香港）',\n        xtom: 'XTOM 镜像站（全球）',\n        phpsourceHelper: '根据你的网络环境选择合适的源',\n        appPort: '应用端口',\n        externalPort: '外部映射端口',\n        packageManager: '包管理器',\n        codeDir: '项目目录',\n        appPortHelper: '应用端口是指容器内部的端口',\n        externalPortHelper: '外部映射端口是指容器对外暴露的端口',\n        runScript: '启动命令',\n        runScriptHelper: '启动命令列表是从源码目录下的 package.json 文件中解析而来',\n        open: '放开',\n        operatorHelper: '将对选中的运行环境进行 {0} 操作，是否继续？',\n        taobao: '淘宝',\n        tencent: '腾讯',\n        imageSource: '镜像源',\n        moduleManager: '模块管理',\n        module: '模块',\n        nodeOperatorHelper: '是否{0} {1} 模块？操作可能导致运行环境异常，请确认后操作',\n        customScript: '自定义启动命令',\n        customScriptHelper: '请填写完整的启动命令，例如：npm run start，pm2 启动命令请换为 pm2-runtime，否则会启动失败',\n        portError: '不能填写相同端口',\n        systemRestartHelper: '状态说明：中断-系统重启导致状态获取失败',\n        javaScriptHelper: '请填写完整启动命令，例如：java -jar halo.jar -Xmx1024M -Xms256M',\n        javaDirHelper: '目录中要包含 jar 包，子目录中包含也可',\n        goHelper: '请填写完整启动命令，例如：go run main.go 或 ./main',\n        goDirHelper: '目录中要包含 go 文件或二进制文件，子目录中包含也可',\n        extension: '扩展',\n        installExtension: '是否确认安装扩展 {0}',\n        loadedExtension: '已加载扩展',\n        popularExtension: '常用扩展',\n        uninstallExtension: '是否确认卸载扩展 {0}',\n        phpConfigHelper: '修改配置需要重启运行环境，是否继续',\n        operateMode: '运行模式',\n        dynamic: '动态',\n        static: '静态',\n        ondemand: '按需',\n        dynamicHelper: '动态调整进程数，灵活性高，适合流量波动较大或低内存的网站',\n        staticHelper: '固定进程数，适合高并发和稳定流量的网站，资源消耗较高',\n        ondemandHelper: '进程按需启动和销毁，资源利用最优，但初始响应可能较慢',\n        max_children: '允许创建的最大进程数',\n        start_servers: '启动时创建的进程数',\n        min_spare_servers: '最小空闲进程数',\n        max_spare_servers: '最大空闲进程数',\n        envKey: '名称',\n        envValue: '值',\n        environment: '环境变量',\n        pythonHelper:\n            '请填写完整启动命令，例如：pip install -r requirements.txt && python  manage.py runserver 0.0.0.0:5000',\n        dotnetHelper: '请填写完整启动命令，例如 dotnet MyWebApp.dll',\n        dirHelper: '说明：请填写容器内的目录路径',\n        concurrency: '并发方案',\n        loadStatus: '负载状态',\n        extraHosts: '主机映射',\n    },\n    process: {\n        pid: '进程ID',\n        ppid: '父进程ID',\n        numThreads: '线程',\n        memory: '内存',\n        diskRead: '磁盘读',\n        diskWrite: '磁盘写',\n        netSent: '上行',\n        netRecv: '下行',\n        numConnections: '连接',\n        startTime: '启动时间',\n        running: '运行中',\n        sleep: '睡眠',\n        stop: '停止',\n        idle: '空闲',\n        zombie: '僵尸进程',\n        wait: '等待',\n        lock: '锁定',\n        blocked: '阻塞',\n        cmdLine: '启动命令',\n        basic: '基本信息',\n        mem: '内存信息',\n        openFiles: '文件打开',\n        env: '环境变量',\n        noenv: '无',\n        net: '网络连接',\n        laddr: '本地地址/端口',\n        raddr: '远程地址/端口',\n        stopProcess: '结束',\n        viewDetails: '查看详情',\n        stopProcessWarn: '是否确定结束此进程 (PID:{0})？',\n        kill: '结束进程',\n        killNow: '立即结束',\n        killHelper: '结束进程 {0} 可能导致部分程序无法正常运行，是否继续？',\n        processName: '进程名称',\n    },\n    tool: {\n        supervisor: {\n            loadStatusErr: '获取进程状态失败，请检查 supervisor 服务状态',\n            notSupport: '未检测到 Supervisor 服务，请前往脚本库页面手动安装',\n            list: '守护进程',\n            config: 'Supervisor 配置',\n            primaryConfig: '主配置文件位置',\n            notSupportCtl: '未检测到 supervisorctl，请前往脚本库页面手动安装',\n            user: '启动用户',\n            command: '启动命令',\n            dir: '运行目录',\n            numprocs: '进程数量',\n            initWarn:\n                '初始化操作需要修改配置文件的 [include] files 参数，修改后的服务配置文件所在目录: 1panel安装目录/1panel/tools/supervisord/supervisor.d/',\n            operatorHelper: '将对 {0} 进行 {1} 操作，是否继续？',\n            uptime: '运行时长',\n            notStartWarn: '当前未开启 Supervisor ，请先启动',\n            serviceName: '服务名称',\n            initHelper: '检测到 Supervisor 服务未初始化，请点击顶部状态栏的初始化按钮进行配置',\n            serviceNameHelper: 'systemctl 管理的 Supervisor 服务名称，一般为 supervisor、supervisord',\n            restartHelper: '初始化会重启服务，导致原有的守护进程全部关闭',\n            RUNNING: '运行中',\n            STOPPED: '已停止',\n            STOPPING: '停止中',\n            STARTING: '启动中',\n            FATAL: '启动失败',\n            BACKOFF: '启动异常',\n            ERROR: '错误',\n            statusCode: '状态码',\n            manage: '管理',\n            autoRestart: '自动重启',\n            EXITED: '已退出',\n            autoRestartHelper: '程序异常退出后是否自动重启',\n            autoStart: '自动启动',\n            autoStartHelper: 'Supervisor 启动后是否自动启动服务',\n        },\n    },\n    disk: {\n        management: '磁盘管理',\n        partition: '分区',\n        unmount: '取消挂载',\n        unmountHelper: '是否取消挂载分区 {0}？',\n        mount: '挂载',\n        partitionAlert: '进行磁盘分区时需要格式化磁盘，原有数据将被删除，请提前保存或快照数据',\n        mountPoint: '挂载目录',\n        systemDisk: '系统磁盘',\n        unpartitionedDisk: '未分区磁盘',\n        handlePartition: '立即分区',\n        filesystem: '文件系统',\n        unmounted: '未挂载',\n        cannotOperate: '无法操作',\n        systemDiskHelper: '提示：当前磁盘为系统盘，无法进行操作',\n        autoMount: '开机自动挂载',\n        model: '设备型号',\n        diskType: '磁盘类型',\n        serial: '序列号',\n        noFail: '挂载失败不影响系统启动',\n    },\n    xpack: {\n        expiresAlert: '温馨提醒：专业版试用将于 [{0}] 天后到期，届时将停止使用所有专业版功能。',\n        menu: '高级功能',\n        upage: 'AI 建站',\n        proAlert: '升级专业版使用此功能',\n        waf: {\n            name: 'WAF',\n            blackWhite: '黑白名单',\n            globalSetting: '全局设置',\n            websiteSetting: '网站设置',\n            blockRecords: '封锁记录',\n            world: '世界',\n            china: '中国',\n            intercept: '拦截',\n            request: '请求',\n            count4xx: '4xx 数量',\n            count5xx: '5xx 数量',\n            todayStatus: '今日状态',\n            reqMap: '拦截地图（30日）',\n            count: '数量',\n            hight: '高',\n            low: '低',\n            reqCount: '请求数',\n            interceptCount: '拦截数',\n            requestTrends: '请求趋势（7日）',\n            interceptTrends: '拦截趋势（7日）',\n            whiteList: '白名单',\n            blackList: '黑名单',\n            ipBlackListHelper: '黑名单中的 IP 无法访问网站',\n            ipWhiteListHelper: '白名单中的 IP 不受任何规则限制',\n            uaBlackListHelper: '携带黑名单中的 User-Agent 的请求将被拦截',\n            uaWhiteListHelper: '携带白名单中的 User-Agent 的请求不受任何规则限制',\n            urlBlackListHelper: '请求黑名单中的 URL 将被拦截',\n            urlWhiteListHelper: '请求白名单中的 URL 不受任何规则限制',\n            ccHelper: '{0} 秒内累计请求任意网站超过 {1} 次，封锁此 IP {2}',\n            blockTime: '封禁时间',\n            attackHelper: '{0} 秒内累计拦截超过 {1} 次，封锁此 IP {2} ',\n            notFoundHelper: '{0} 秒内累计请求返回 404 超过 {1} 次，封锁此 IP {2} ',\n            frequencyLimit: '频率限制',\n            regionLimit: '地区限制',\n            defaultRule: '默认规则',\n            accessFrequencyLimit: '访问频率限制',\n            attackLimit: '攻击频率限制',\n            notFoundLimit: '404 频率限制',\n            urlLimit: 'URL 频率限制',\n            urlLimitHelper: '为单个 URL 设置访问频率',\n            sqliDefense: 'SQL 注入防御',\n            sqliHelper: '识别请求中的 SQL 注入并拦截',\n            xssHelper: '识别请求中的 XSS 并拦截',\n            xssDefense: 'XSS 防御',\n            uaDefense: 'User-Agent 规则',\n            uaHelper: '包含常见的恶意爬虫规则',\n            argsDefense: '参数规则',\n            argsHelper: '禁止请求中携带恶意参数',\n            cookieDefense: 'Cookie 规则',\n            cookieHelper: '禁止请求中携带恶意 Cookie',\n            headerDefense: 'Header 规则',\n            headerHelper: '禁止请求中携带恶意 Header',\n            httpRule: 'HTTP 规则',\n            httpHelper:\n                '设置允许访问的方法类型，如果想限制某些类型访问，请关闭这个类型的按钮，例如：仅允许 GET 类型访问，那么需要关闭除了 GET 之外的其他类型按钮',\n            geoRule: '地区访问限制',\n            geoHelper: '限制某些地区访问你的网站，例如：允许中国大陆访问，那么中国大陆以外的请求都会被拦截',\n            ipLocation: 'IP 归属地',\n            action: '动作',\n            ruleType: '攻击类型',\n            ipHelper: '请输入 IP',\n            attackLog: '攻击日志',\n            rule: '规则',\n            ipArr: 'IPV4 范围',\n            ipStart: '起始 IP',\n            ipEnd: '结束 IP',\n            ipv4: 'IPV4',\n            ipv6: 'IPV6',\n            urlDefense: 'URL 规则',\n            urlHelper: '禁止访问的 URL',\n            dirFilter: '目录过滤',\n            sqlInject: 'SQL 注入',\n            xss: 'XSS',\n            phpExec: 'PHP 脚本执行',\n            oneWordTrojan: '一句话木马',\n            appFilter: '应用危险目录过滤',\n            webShell: 'Webshell',\n            args: '参数规则',\n            protocolFilter: '协议过滤',\n            javaFilter: 'Java 危险文件过滤',\n            scannerFilter: '扫描器过滤',\n            escapeFilter: '转义过滤',\n            customRule: '自定义规则',\n            httpMethod: 'HTTP 方法过滤',\n            fileExt: '文件上传限制',\n            fileExtHelper: '禁止上传的文件扩展名',\n            deny: '禁止',\n            allow: '允许',\n            field: '匹配对象',\n            pattern: '匹配条件',\n            ruleContent: '匹配内容',\n            contain: '包含',\n            equal: '等于',\n            regex: '正则表达式',\n            notEqual: '不等于',\n            customRuleHelper: '根据条件匹配执行相应动作',\n            actionAllow: '允许',\n            blockIP: '封禁 IP',\n            code: '返回状态码',\n            noRes: '断开连接 (444)',\n            badReq: '参数错误 (400)',\n            forbidden: '禁止访问 (403)',\n            serverErr: '服务器错误 (500)',\n            resHtml: '响应页面',\n            allowHelper: '允许访问会跳过 WAF 规则，请谨慎使用',\n            captcha: '人机验证',\n            fiveSeconds: '5 秒验证',\n            location: '地区',\n            redisConfig: 'Redis 配置',\n            redisHelper: '开启 Redis 可以将临时拉黑的 IP 持久化',\n            wafHelper: '关闭之后所有网站将失去防护',\n            attackIP: '攻击 IP',\n            attackParam: '攻击信息',\n            execRule: '命中规则',\n            acl: '自定义规则',\n            sql: 'SQL 注入',\n            cc: '访问频率限制',\n            isBlocking: '封禁中',\n            isFree: '已解封',\n            unLock: '解封',\n            unLockHelper: '是否解封 IP:{0}?',\n            saveDefault: '保存默认',\n            saveToWebsite: '应用到网站',\n            saveToWebsiteHelper: '是否将当前设置应用到所有网站？',\n            websiteHelper: '此处为创建网站的默认设置，修改之后需要应用到网站才能生效',\n            websiteHelper2: '此处为创建网站的默认设置，具体配置请在网站处修改',\n            ipGroup: 'IP 组',\n            ipGroupHelper: '一行一个 IP 或 IP 段，支持 IPv4 和 IPv6， 例如：192.168.1.1 或 192.168.1.0/24',\n            ipBlack: 'IP 黑名单',\n            openRestyAlert: 'OpenResty 版本需要高于 {0}',\n            initAlert: '首次使用需要初始化，会修改网站配置文件，原有的 WAF 配置会丢失，请一定提前备份 OpenResty',\n            initHelper: '初始化操作将清除现有的 WAF 配置，您确定要进行初始化吗？',\n            mainSwitch: '总开关',\n            websiteAlert: '请先创建网站',\n            defaultUrlBlack: 'URL 规则',\n            htmlRes: '拦截页面',\n            urlSearchHelper: '请输入 URL，支持模糊搜索',\n            toCreate: '去创建',\n            closeWaf: '关闭 WAF',\n            closeWafHelper: '关闭 WAF 会使网站失去防护，是否继续',\n            addblack: '拉黑',\n            addwhite: '加白',\n            addblackHelper: '是否把 IP:{0} 添加到默认黑名单?',\n            addwhiteHelper: '是否把 IP:{0} 添加到默认白名单?',\n            defaultUaBlack: 'User-Agent 规则',\n            defaultIpBlack: '恶意 IP 组',\n            cookie: 'Cookie 规则',\n            urlBlack: 'URL 黑名单',\n            uaBlack: 'User-Agent 黑名单',\n            attackCount: '攻击频率限制',\n            fileExtCheck: '文件上传限制',\n            geoRestrict: '地区访问限制',\n            attacklog: '拦截记录',\n            unknownWebsite: '未授权域名访问',\n            geoRuleEmpty: '地区不能为空',\n            unknown: '网站不存在',\n            geo: '地区限制',\n            revertHtml: '是否还原{0}为默认页面？',\n            five_seconds: '5 秒验证',\n            header: 'Header 规则',\n            methodWhite: 'HTTP 规则',\n            expiryDate: '有效期',\n            expiryDateHelper: '验证通过后有效期内不再验证',\n            defaultIpBlackHelper: '从互联网收集的一些恶意 IP，阻止其访问',\n            notFoundCount: '404 频率限制',\n            matchValue: '匹配值',\n            headerName: '支持非特殊字符开头，英文、数字、-,长度3-30',\n            cdnHelper: '使用 CDN 的网站可以打开此处来获取正确来源 IP',\n            clearLogWarn: '清空日志将无法恢复，是否继续？',\n            commonRuleHelper: '规则为模糊匹配',\n            blockIPHelper: '封锁 IP 临时存储在 OpenResty 中，重启 OpenResty 会解封，可以通过拉黑功能永久拉黑',\n            addWhiteUrlHelper: '是否把 URL {0} 添加到白名单?',\n            dashHelper: '社区版也可使用全局设置和网站设置中的功能',\n            wafStatusHelper: 'WAF 未开启，请在全局设置中打开',\n            ccMode: '模式',\n            global: '全局模式',\n            uriMode: 'URL 模式',\n            globalHelper: '全局模式：单位时间请求任意 URL 次数之和超过阈值即触发',\n            uriModeHelper: 'URL 模式：单位时间请求单个 URL 次数超过阈值即触发',\n            ip: 'IP 黑名单',\n            globalSettingHelper: '带有【网站】标签的设置，需要在【网站设置】配置生效，全局设置仅为新建网站的默认设置',\n            globalSettingHelper2: '设置生效需要【全局设置】和【网站设置】的开关同时打开',\n            urlCCHelper: '{0} 秒内累计请求此 URL 超过 {1} 次，封锁此 IP {2} ',\n            urlCCHelper2: 'URL 不能带参数',\n            notContain: '不包含',\n            urlcc: 'URL 频率限制',\n            method: '请求类型',\n            addIpsToBlock: '批量拉黑 IP',\n            addUrlsToWhite: '批量加白 URL',\n            noBlackIp: 'IP 已拉黑，无需再次拉黑',\n            noWhiteUrl: 'URL 已加白，无需再次加白',\n            spiderIpHelper:\n                '包含百度、Bing、谷歌、360、神马、搜狗、字节、DuckDuckGo、Yandex，关闭之后会拦截所有蜘蛛访问',\n            spiderIp: '蜘蛛 IP 池',\n            geoIp: 'IP 地址库',\n            geoIpHelper: '用来确认 IP 的地理位置',\n            stat: '攻击报表',\n            statTitle: '报表',\n            attackIp: '攻击 IP',\n            attackCountNum: '攻击次数',\n            percent: '占比',\n            addblackUrlHelper: '是否把 URL:{0} 添加到默认黑名单?',\n            rce: '远程代码执行',\n            software: '软件',\n            cveHelper: '包含常见软件、框架的漏洞',\n            vulnCheck: '补充规则',\n            ssrf: 'SSRF 漏洞',\n            afr: '任意文件读取',\n            ua: '未授权访问',\n            id: '信息泄露',\n            aa: '认证绕过',\n            dr: '目录遍历',\n            xxe: 'XXE 漏洞',\n            suid: '序列化漏洞',\n            dos: '拒绝服务漏洞',\n            afd: '任意文件下载',\n            sqlInjection: 'SQL 注入',\n            afw: '任意文件写入',\n            il: '信息泄漏',\n            clearAllLog: '清空所有日志',\n            exportLog: '导出日志',\n            appRule: '应用规则',\n            appRuleHelper: '常见应用的规则，开启之后可以减少误报，一个网站只能使用一个规则',\n            logExternal: '排除记录类型',\n            ipWhite: 'IP 白名单',\n            urlWhite: 'URL 白名单',\n            uaWhite: 'User-Agent 白名单',\n            logExternalHelper:\n                '排除记录类型不会被记录到日志中，黑白名单、地区访问限制、自定义规则会产生大量日志，建议排除',\n            ssti: 'SSTI 攻击',\n            crlf: 'CRLF 注入',\n            strict: '严格模式',\n            strictHelper: '使用更严格的规则来校验请求',\n            saveLog: '保存日志',\n            remoteURLHelper: '远程 URL 需要保证每行一个 IP 并且没有其他字符',\n            notFound: 'Not Found (404)',\n            serviceUnavailable: '服务不可用 (503)',\n            gatewayTimeout: '网关超时 (504)',\n            belongToIpGroup: '属于 IP 组',\n            notBelongToIpGroup: '不属于 IP 组',\n            unknownWebsiteKey: '未知域名',\n            special: '指定',\n            fileToLarge: '文件超过 1MB 无法上传',\n            uploadOverLimit: '上传文件超过数量限制，最大1个',\n            importRuleHelper: '一行一个规则',\n        },\n        monitor: {\n            name: '网站监控',\n            pv: '浏览量',\n            uv: '访客数',\n            flow: '流量',\n            ip: '独立 IP',\n            spider: '蜘蛛',\n            visitors: '访客趋势',\n            uvMap: '访客地图 (30日)',\n            qps: '实时请求数（1分钟）',\n            flowSec: '实时流量（1分钟）',\n            excludeCode: '排除状态码',\n            excludeUrl: '排除 URL',\n            excludeExt: '排除扩展名',\n            cdnHelper: '通过 CDN 设置的 Header 来获取真实 IP',\n            reqRank: '访问统计',\n            refererDomain: '来源',\n            os: '操作系统',\n            browser: '浏览器/客户端',\n            device: '设备',\n            showMore: '查看更多',\n            unknown: '其他',\n            pc: '电脑',\n            mobile: '移动端',\n            wechat: '微信',\n            machine: '机器',\n            tencent: '腾讯浏览器',\n            ucweb: 'UC 浏览器',\n            '2345explorer': '2345 浏览器',\n            huaweibrowser: '华为浏览器',\n            log: '请求日志',\n            statusCode: '状态码',\n            requestTime: '响应时间',\n            flowRes: '响应流量',\n            method: '请求类型',\n            statusCodeHelper: '可在上方输入状态码',\n            statusCodeError: '状态码类型错误',\n            methodHelper: '可在上方输入请求类型',\n            baidu: '百度',\n            google: '谷歌',\n            bing: '必应',\n            bytes: '今日头条',\n            sogou: '搜狗',\n            failed: '错误',\n            ipCount: 'IP 数',\n            spiderCount: '蜘蛛请求',\n            averageReqTime: '平均响应时间',\n            totalFlow: '总流量',\n            logSize: '日志文件大小',\n            realIPType: '真实IP获取方式',\n            fromHeader: '从 HTTP Header 中获取',\n            fromHeaders: '从 Header 列表中获取',\n            header: 'HTTP Header',\n            cdnConfig: 'CDN 适配',\n            xff1: '获取 X-Forwarded-For 的上一级代理地址',\n            xff2: '获取 X-Forwarded-For 的上上一级代理地址',\n            xff3: '获取 X-Forwarded-For 的上上上一级代理地址',\n            xffHelper: '例如：X-Forwarded-For: <client>,<proxy1>,<proxy2>,<proxy3> 上一级代理会取最后一个 IP <proxy3>',\n            headersHelper: '从下列常用的 CDN 携带真实 IP 的 HTTP Header 中获取，取第一个能获取到的值',\n            monitorCDNHelper: '修改网站监控的 CDN 配置会同步更新 WAF 的 CDN 配置',\n            wafCDNHelper: '修改 WAF 的 CDN 配置会同步更新网站监控的 CDN 配置',\n            statusErr: '状态码格式错误',\n            shenma: '神马搜索',\n            duckduckgo: 'DuckDuckGo',\n            '360': '360 搜索',\n            excludeUri: '排除 Uri',\n            top100Helper: '显示 Top 100 的数据',\n            logSaveDay: '日志保存天数',\n            cros: 'Chrome OS',\n            theworld: '世界之窗浏览器',\n            edge: 'Edge',\n            maxthon: '遨游浏览器',\n            monitorStatusHelper: '监控未开启，请在设置中打开',\n            excludeIp: '排除 IP',\n            excludeUa: '排除 User-Agent',\n            remotePort: '远程端口',\n            unknown_browser: '未知',\n            unknown_os: '未知',\n            unknown_device: '未知',\n            logSaveSize: '最大日志保存大小',\n            logSaveSizeHelper: '此处为单个网站的日志保存大小',\n            '360se': '360 安全浏览器',\n            websites: '网站列表',\n            trend: '趋势统计',\n            reqCount: '请求数',\n            uriHelper: '可以使用 /test/* 或 /*/index.php 来排除 Uri',\n        },\n        tamper: {\n            tamper: '网站防篡改',\n            ignoreTemplate: '排除模版',\n            protectTemplate: '保护模版',\n            ignoreTemplateHelper:\n                '请输入排除内容，回车或空格分隔。（具体目录 ./log 或目录名 tmp，排除文件需要输入具体文件 ./data/test.html）',\n            protectTemplateHelper:\n                '请输入保护内容，回车或空格分隔。（具体文件 ./index.html、文件后缀 .html、 文件类型 js，保护目录需要输入具体目录 ./log）',\n            templateContent: '模版内容',\n            template: '模版',\n            saveTemplate: '保存为模版',\n            tamperHelper1:\n                '一键部署类型的网站，建议启用应用目录防篡改功能；如出现网站无法正常使用或备份、恢复失败的情况，请先关闭防篡改功能；',\n            tamperHelper2: '将限制非排除目录下受保护文件的读写、删除、权限和所有者修改操作',\n            tamperPath: '防护目录',\n            tamperPathEdit: '修改路径',\n            log: '拦截日志',\n            totalProtect: '总防护',\n            todayProtect: '今日防护',\n            templateRule: '长度1-512，名称不能含有{0}等符号',\n            ignore: '排除',\n            ignoreHelper:\n                '请选择或输入排除内容，回车或空格分隔。（具体目录 ./log 或目录名 tmp，排除文件需要输入或选择具体文件 ./data/test.html）',\n            protect: '保护',\n            protectHelper:\n                '请选择或输入保护内容，回车或空格分隔。（具体文件 ./index.html、文件后缀 .html、文件类型 js, 保护目录需要输入或选择具体目录 ./log）',\n            tamperHelper00: '排除和保护仅支持输入相对路径；',\n            tamperHelper01: '开启防篡改后，系统将限制非排除目录下保护文件的创建、编辑和删除操作等操作；',\n            tamperHelper02: '优先级：具体路径保护 > 具体路径排除 > 保护 > 排除',\n            tamperHelper03: '监听操作只针对于非排除目录，监听该目录下非保护文件的创建操作。',\n            disableHelper: '即将关闭下列网站的防篡改功能，是否继续？',\n            appendOnly: '仅追加',\n            appendOnlyHelper: '将限制该目录下文件的删除操作，仅允许添加排除目录或非保护文件',\n            immutable: '保护',\n            immutableHelper: '保护该文件的编辑、删除、权限和所有者修改操作',\n            onWatch: '监听',\n            onWatchHelper: '监听该目录下保护文件或非排除目录的创建拦截',\n            forceStop: '强制关闭',\n            forceStopHelper: '即将强制关闭该网站目录的防篡改功能，是否继续？',\n        },\n        setting: {\n            setting: '界面设置',\n            logo: 'Logo (不带文字)',\n            logoHelper: '将会显示在菜单收缩时管理页面左上方 (建议图片大小为: 82px*82px)',\n            logoWithText: 'Logo (带文字)',\n            logoWithTextHelper: '将会显示在菜单展开时管理页面左上方 (建议图片大小为: 185px*55px)',\n            favicon: '网站图标',\n            faviconHelper: '网站图标 (建议图片大小为: 16px*16px)',\n            setHelper: '即将保存当前界面设置内容，是否继续？',\n            setDefaultHelper: '即将恢复所有界面设置到初始状态，是否继续？',\n            logoGroup: 'Logo',\n            loginImage: '登录页图片',\n            loginImageHelper: '将会显示在登录页面(建议图片大小为: 500*416px)',\n            loginBgType: '登录页背景类型',\n            loginBgImage: '登录页背景图片',\n            loginBgImageHelper: '将会显示在登录页面背景图片(建议图片大小为: 1920*1080px)',\n            loginBgColor: '登录页背景颜色',\n            image: '图片',\n            bgColor: '背景色',\n            loginGroup: '登录页',\n            loginBtnLinkColor: '按钮/链接颜色',\n        },\n        helper: {\n            wafTitle1: '拦截地图',\n            wafContent1: '统计并展示 30 天内的拦截地理位置分布',\n            wafTitle2: '地区访问限制',\n            wafContent2: '按照地理位置限制网站的访问来源',\n            wafTitle3: '自定义拦截页面',\n            wafContent3: '自定义请求被拦截之后的显示页面',\n            wafTitle4: '自定义规则（ACL）',\n            wafContent4: '根据自定义的规则拦截请求',\n\n            tamperTitle1: '文件完整性监控',\n            tamperContent1: '监控网站文件的完整性，包括核心文件、脚本文件和配置文件等。',\n            tamperTitle2: '实时扫描和检测',\n            tamperContent2: '通过实时扫描网站文件系统，检测是否存在异常或被篡改的文件。',\n            tamperTitle3: '安全权限设置',\n            tamperContent3:\n                '通过合理的权限设置和访问控制策略，网站防篡改功能可以限制对网站文件的访问权限，减少潜在的攻击面。',\n            tamperTitle4: '日志记录与分析',\n            tamperContent4: '记录文件访问和操作日志，以便管理员进行后续的审计和分析，以及发现潜在的安全威胁。',\n\n            settingTitle1: '自定义欢迎语',\n            settingContent1: '在 1Panel 登录页上设置自定义的欢迎语。',\n            settingTitle2: '自定义 Logo',\n            settingContent2: '允许上传包含品牌名称或其他文字的 Logo 图像。',\n            settingTitle3: '自定义网站 icon',\n            settingContent3: '允许上传自定义的图标，以替代默认的浏览器图标，提升用户体验。',\n\n            monitorTitle1: '访客趋势',\n            monitorContent1: '统计并展示网站的访客趋势',\n            monitorTitle2: '访客地图',\n            monitorContent2: '统计并展示网站的访客地理位置分布',\n            monitorTitle3: '访问统计',\n            monitorContent3: '统计网站的请求信息，包括蜘蛛，访问设备，请求状态等',\n            monitorTitle4: '实时监控',\n            monitorContent4: '实时监控网站的请求信息，包括请求数，流量等',\n\n            alertTitle1: '短信告警',\n            alertContent1:\n                '当服务器资源使用异常、网站及证书过期、新版本更新、密码过期等情况发生时，通过短信告警通知用户，确保及时处理。',\n            alertTitle2: '告警日志',\n            alertContent2: '为用户提供查看告警日志的功能，方便追踪和分析历史告警事件。',\n            alertTitle3: '告警设置',\n            alertContent3:\n                '为用户提供自定义手机号、每日推送次数、每日推送时间的配置，方便用户的设置来更加合理的进行推送告警。',\n\n            nodeDashTitle1: '应用管理',\n            nodeDashContent1: '统一管理多节点应用，支持状态监控、快速起停、终端连接及备份',\n            nodeDashTitle2: '网站管理',\n            nodeDashContent2: '统一管理多节点网站，实时监控状态，支持批量起停与快速备份',\n            nodeDashTitle3: '数据库管理',\n            nodeDashContent3: '统一管理多节点数据库，关键状态一目了然，支持一键备份',\n            nodeDashTitle4: '计划任务管理',\n            nodeDashContent4: '统一管理多节点计划任务，支持状态监控、快速启停与手动触发执行',\n\n            nodeTitle1: '一键添加节点',\n            nodeContent1: '快速接入多台服务器节点',\n            nodeTitle2: '批量升级',\n            nodeContent2: '一次操作同步升级所有节点',\n            nodeTitle3: '节点状态监控',\n            nodeContent3: '实时掌握各节点运行状态',\n            nodeTitle4: '快速远程连接',\n            nodeContent4: '一键直连节点远程终端',\n\n            fileExchangeTitle1: '密钥认证传输',\n            fileExchangeContent1: '通过 SSH 密钥进行身份验证，确保传输的安全性。',\n            fileExchangeTitle2: '高效文件同步',\n            fileExchangeContent2: '仅同步变化内容，大幅提高传输速度与稳定性。',\n            fileExchangeTitle3: '支持多节点互传',\n            fileExchangeContent3: '可在不同节点间便捷传送项目文件，灵活管理多台服务器。',\n\n            nodeAppTitle1: '应用升级管理',\n            nodeAppContent1: '统一监控多节点应用更新情况，支持一键升级',\n\n            appTitle1: '灵活管理面板',\n            appContent1: '随时随地轻松管理你的 1Panel 服务器。',\n            appTitle2: '全面服务信息',\n            appContent2: '在移动端进行应用、网站、Docker、数据库等基础管理，支持快速创建应用与网站。',\n            appTitle3: '实时异常监控',\n            appContent3: '移动端实时查看服务器状态、WAF 安全监控、网站访问统计与进程健康状况。',\n\n            clusterTitle1: '主从部署',\n            clusterContent1: '支持在不同节点创建 MySQL/Postgres/Redis 主从实例，自动完成主从关联与初始化',\n            clusterTitle2: '主从管理',\n            clusterContent2: '统一页面集中管理多个主从节点，查看其角色、运行状态等信息',\n            clusterTitle3: '复制状态',\n            clusterContent3: '展示主从复制状态与延迟信息，辅助排查同步异常问题',\n            vllmTitle1: '统一纳管',\n            vllmContent1: '将分散的 Vllm 服务统一接入 1Panel，集中查看实例、版本、状态与运行目录。',\n            vllmTitle2: '快速部署',\n            vllmContent2: '通过可视化表单完成版本选择、模型目录挂载与启动参数配置，减少手工编排成本。',\n            vllmTitle3: '运维便捷',\n            vllmContent3: '在同一页面完成创建、编辑、启停、重启、删除与任务追踪，提升本地模型服务维护效率。',\n            vllmTitle4: '配置可控',\n            vllmContent4: '结合高级设置统一管理端口、容器、启动命令与 Compose 配置，便于标准化交付与后续调整。',\n        },\n        node: {\n            master: '主节点',\n            masterBackup: '主节点备份',\n            backupNode: '备份节点',\n            backupFrequency: '备份频率（小时）',\n            backupCopies: '备份份数',\n            noBackupNode: '当前备份节点为空，请选择备份节点保存后重试！',\n            masterBackupAlert:\n                '当前未配置主节点备份，为保障数据安全，请尽快设置备份节点，便于主节点故障时可人工切换新主节点。',\n            node: '节点',\n            addr: '地址',\n            nodeUpgrade: '更新设置',\n            nodeUpgradeHelper: '选中的节点将在主节点升级完成后自动开始升级，无需手动操作。',\n            nodeUnhealthy: '节点状态异常',\n            deletedNode: '已删除节点 {0} 暂不支持升级操作！',\n            nodeUnhealthyHelper: '检测到该节点状态异常，请在 [节点管理] 中检查后重试！',\n            nodeUnbind: '节点未绑定许可证',\n            nodeUnbindHelper: '检测到该节点未绑定许可证，请在 [ 面板设置 - 许可证 ] 菜单中绑定后重试！',\n            memTotal: '内存总计',\n            nodeManagement: '多机管理',\n            nodeItem: '节点管理',\n            panelItem: '面板管理',\n            addPanel: '添加面板',\n            addPanelHelper: '面板添加成功后，您可在 [ 概览 - 面板 ] 中快速访问目标面板。',\n            panel: '1Panel 面板',\n            others: '其他面板',\n            addNode: '添加节点',\n            connInfo: '连接信息',\n            nodeInfo: '节点信息',\n            withProxy: '开启代理访问',\n            withoutProxy: '关闭代理访问',\n            withProxyHelper: '将使用面板设置中维护的系统代理 {0} 访问子节点，是否继续？',\n            withoutProxyHelper: '将停止使用面板设置中维护的系统代理访问子节点，是否继续？',\n            syncInfo: '数据同步',\n            syncHelper: '主节点数据发生变化时，实时同步到该子节点',\n            syncBackupAccount: '备份账号设置',\n            syncWithMaster: '升级为专业版后，将默认同步所有数据，可在节点管理中手动调整同步策略。',\n            syncProxy: '系统代理设置',\n            syncProxyHelper: '同步系统代理设置需要重启 Docker',\n            syncProxyHelper1: '重启 Docker 可能会影响当前正在运行的容器服务。',\n            syncProxyHelper2: '可前往 容器 - 配置 页面手动重启。',\n            syncProxyHelper3: '同步系统代理设置需要重启 Docker，重启可能会影响当前正在运行的容器服务',\n            syncProxyHelper4: '同步系统代理设置需要重启 Docker，可稍后前往 容器 - 配置 页面手动重启。',\n            syncCustomApp: '同步自定义应用仓库',\n            syncAlertSetting: '系统告警设置',\n            syncNodeInfo: '节点基础数据',\n            nodeSyncHelper: '节点信息同步将同步以下信息：',\n            nodeSyncHelper1: '1. 公用的备份账号信息',\n            nodeSyncHelper2: '2. 主节点与子节点的连接信息',\n\n            nodeCheck: '可用性检查',\n            checkSSH: '检查节点 SSH 连接',\n            checkUserPermission: '检查节点用户权限',\n            isNotRoot: '检查到该节点不支持免密 sudo，且当前为非 root 用户',\n            checkLicense: '检查节点许可证状态',\n            checkService: '检查节点已存在服务信息',\n            checkPort: '检查节点端口可达',\n            panelExist: '检查到该节点正在运行 1Panel V1 服务，请先通过迁移脚本升级至 V2 后再进行添加。',\n            coreExist:\n                '当前节点已作为主节点启用，无法直接作为从节点添加。请先将其降级为从节点后再添加，具体可参考文档。',\n            agentExist: '检查到该节点已安装 1panel-agent，继续添加将保留现有数据，仅替换 1panel-agent 服务。',\n            agentNotExist: '检查到该节点未安装 1panel-agent，无法直接编辑该节点信息，请先删除后重新添加。',\n            oldDataExist: '检查到该节点存在 1Panel V2 历史数据，将使用以下信息覆盖当前设置',\n            errLicense: '检查到该节点绑定的许可证不可用，请检查后重试！',\n            errNodePort: '检查到节点端口 [ {0} ] 无法访问，请检查防火墙或安全组是否已放行该端口。',\n\n            reinstallHelper: '重新安装节点 {0}, 是否继续？',\n            unhealthyCheck: '异常检查',\n            fixOperation: '修复方案',\n            checkName: '检查项',\n            checkSSHConn: '检查 SSH 连接可用性',\n            fixSSHConn: '手动编辑节点，确认连接信息',\n            checkConnInfo: '检查 Agent 连接信息',\n            checkStatus: '检查节点服务可用性',\n            fixStatus: 'systemctl status 1panel-agent.service 检查服务是否已启动',\n            checkAPI: '检查节点 API 可用性',\n            fixAPI: '排查节点日志，检查防火墙端口是否正常放行',\n            forceDelete: '强制删除',\n            operateHelper: '将对以下节点进行 {0} 操作，是否继续？',\n            operatePanelHelper: '将对以下面板进行 {0} 操作，是否继续？',\n            uninstall: '删除节点数据',\n            uninstallHelper: '将删除节点所有 1Panel 相关数据，谨慎选择！',\n            forceDeleteHelper: '强制删除将会忽略节点删除错误，删除数据库元数据',\n            baseDir: '安装目录',\n            baseDirHelper: '安装目录为空时，默认安装在 /opt 目录下',\n            nodePort: '节点端口',\n            offline: '离线模式',\n            freeCount: '免费额度 [{0}] ',\n            offlineHelper: '节点为离线环境时使用',\n\n            appUpgrade: '应用升级',\n            appUpgradeHelper: '有 {0} 个应用需要升级',\n        },\n        customApp: {\n            name: '自定义仓库',\n            appStoreType: '仓库来源',\n            appStoreUrl: '仓库地址',\n            local: '本地',\n            remote: '远程',\n            imagePrefix: '镜像前缀',\n            imagePrefixHelper:\n                '用于自定义镜像前缀，自动修改 Compose 文件中的镜像字段。\\n 例如，当镜像前缀设置为 1panel/custom 时，MaxKB 的镜像将变更为 1panel/custom/maxkb:v1.10.0。',\n            closeHelper: '是否取消使用自定义仓库',\n            appStoreUrlHelper: '仅支持 .tar.gz 格式',\n            selectNode: '选择节点',\n            licenseHelper: '专业版支持自定义应用仓库功能',\n            databaseHelper: '应用关联数据库，请选择目标节点数据库',\n            nodeHelper: '不能选择当前节点',\n            migrateHelper: '当前仅支持迁移单体应用和只关联 MySQL、MariaDB、PostgreSQL 数据库的应用',\n            opensslHelper: '如果使用加密备份，两个节点之间的 openssl 版本必须保持一致，不然会导致迁移失败',\n            installApp: '批量安装',\n            installAppHelper: '批量安装应用到选择的节点中',\n        },\n        alert: {\n            isAlert: '是否告警',\n            alertCount: '告警次数',\n            clamHelper: '扫描到感染文件时触发告警通知',\n            cronJobHelper: '定时任务执行失败时将触发告警通知',\n            licenseHelper: '专业版支持更多告警功能',\n            alertCountHelper: '每日最大告警次数',\n            alert: '短信告警',\n            logs: '告警日志',\n            list: '告警列表',\n            addTask: '创建告警',\n            editTask: '编辑告警',\n            alertMethod: '告警方式',\n            alertMsg: '告警内容',\n            alertRule: '告警规则',\n            titleSearchHelper: '请输入告警标题，支持模糊搜索',\n            taskType: '告警类型',\n            ssl: '网站证书到期',\n            siteEndTime: '网站到期',\n            panelPwdEndTime: '面板密码到期',\n            panelUpdate: '面板新版本提醒',\n            cpu: '面板服务器 CPU 过高告警',\n            memory: '面板服务器内存过高告警',\n            load: '面板服务器负载过高告警',\n            disk: '面板服务器磁盘过高告警',\n            certificate: '证书',\n            remainingDays: '剩余天数',\n            sendCount: '告警次数',\n            sms: '短信通知',\n            wechat: '微信公众号',\n            dingTalk: '钉钉通知',\n            feiShu: '飞书通知',\n            mail: '邮箱通知',\n            weCom: '企业微信',\n            sendCountRulesHelper: '到期前发送告警的总数（每日仅发送一次）',\n            panelUpdateRulesHelper: '新版本发送告警总数（每日仅发送一次）',\n            oneDaySendCountRulesHelper: '每日发送告警的总数',\n            siteEndTimeRulesHelper: '永不过期的网站，不触发告警',\n            autoRenewRulesHelper: '证书开启自动续期，剩余天数小于 31 天，不触发告警',\n            panelPwdEndTimeRulesHelper: '面板未设置密码到期时长，不能使用密码到期告警',\n            sslRulesHelper: '所有 ssl 证书',\n            diskInfo: '磁盘信息',\n            monitoringType: '监测类型',\n            autoRenew: '自动续签',\n            useDisk: '占用磁盘',\n            usePercentage: '占用百分比',\n            changeStatus: '状态修改',\n            disableMsg: '停止告警任务会导致该任务不再发送告警消息。是否继续？',\n            enableMsg: '启用告警任务会让该任务发送告警消息。是否继续？',\n            useExceed: '使用超过',\n            useExceedRulesHelper: '使用超过指定值触发告警',\n            cpuUseExceedAvg: 'CPU 平均使用率超过指定值',\n            memoryUseExceedAvg: '内存平均使用率超过指定值',\n            loadUseExceedAvg: '负载平均使用率超过指定值',\n            cpuUseExceedAvgHelper: '指定时间内 CPU 平均使用率超过指定值',\n            memoryUseExceedAvgHelper: '指定时间内内存平均使用率超过指定值',\n            loadUseExceedAvgHelper: '指定时间内负载平均使用率超过指定值',\n            resourceAlertRulesHelper: '注意：30 分钟内持续告警只发送一次',\n            specifiedTime: '指定时间',\n            deleteTitle: '删除告警',\n            deleteMsg: '是否确认删除告警任务？',\n\n            allSslTitle: '所有网站证书到期告警',\n            sslTitle: '网站「 {0} 」证书到期告警',\n            allSiteEndTimeTitle: '所有网站到期告警',\n            siteEndTimeTitle: '网站「 {0} 」到期告警',\n            panelPwdEndTimeTitle: '面板密码到期告警',\n            panelUpdateTitle: '面板新版本提醒',\n            cpuTitle: 'CPU 占用过高告警',\n            memoryTitle: '内存占用过高告警',\n            loadTitle: '负载占用过高告警',\n            diskTitle: '挂载目录「{0}」的磁盘占用过高告警',\n            allDiskTitle: '磁盘占用过高告警',\n\n            timeRule: '剩余时间小于 {0} 天 (如未处理，次日会重新发送)',\n            panelUpdateRule: '检测到面板有新版本时发送一次 (如未处理，次日会重新发送)',\n            avgRule: '{0} 分钟内平均{1}占用超过 {2}% 触发，每天发送 {3} 次',\n            diskRule: '挂载目录「 {0} 」的磁盘占用超过 {1}{2} 触发，每天发送 {3} 次',\n            allDiskRule: '磁盘占用超过 {0}{1} 触发，每天发送 {2} 次',\n\n            cpuName: ' CPU ',\n            memoryName: '内存',\n            loadName: '负载',\n            diskName: '磁盘',\n\n            syncAlertInfo: '同步告警信息',\n            syncAlertInfoMsg: '是否同步告警信息内容状态？',\n            pushError: '推送失败',\n            pushSuccess: '推送成功',\n            syncError: '同步失败',\n            success: '告警成功',\n            pushing: '推送中...',\n            error: '告警失败',\n            cleanLog: '清空日志',\n            cleanAlertLogs: '清空告警日志',\n            daily: '当日第 {0} 次告警',\n            cumulative: '累计第 {0} 次告警',\n            clams: '病毒扫描告警',\n            taskName: '任务名称',\n            cronJobType: '任务类型',\n            clamPath: '扫描目录',\n            cronjob: '计划任务执行{0}异常',\n            app: '备份应用',\n            web: '备份网站',\n            database: '备份数据库',\n            directory: '备份目录',\n            log: '备份日志',\n            snapshot: '系统快照',\n            clamsRulesHelper: '需要开启告警通知的病毒扫描任务',\n            cronJobRulesHelper: '需要配置此类型的计划任务',\n            clamsTitle: '病毒扫描「 {0} 」任务检测到感染文件告警',\n            cronJobAppTitle: '计划任务-备份应用「 {0} 」任务失败告警',\n            cronJobWebsiteTitle: '计划任务-备份网站「 {0} 」任务失败告警',\n            cronJobDatabaseTitle: '计划任务-备份数据库「 {0} 」任务失败告警',\n            cronJobDirectoryTitle: '计划任务-备份目录「 {0} 」任务失败告警',\n            cronJobLogTitle: '计划任务-备份日志「 {0} 」任务失败告警',\n            cronJobSnapshotTitle: '计划任务-系统快照「 {0} 」任务失败告警',\n            cronJobShellTitle: '计划任务-Shell 脚本「 {0} 」任务失败告警',\n            cronJobCurlTitle: '计划任务-访问 URL「 {0} 」任务失败告警',\n            cronJobCutWebsiteLogTitle: '计划任务-切割网站日志「 {0} 」任务失败告警',\n            cronJobCleanTitle: '计划任务-缓存清理「 {0} 」任务失败告警',\n            cronJobNtpTitle: '计划任务-同步服务器时间「 {0} 」任务失败告警',\n            clamsRule: '病毒扫描任务检测到感染文件告警，每天发送 {0} 次',\n            cronJobAppRule: '备份应用任务失败告警，每天发送 {0} 次',\n            cronJobWebsiteRule: '备份网站任务失败告警，每天发送 {0} 次',\n            cronJobDatabaseRule: '备份数据库任务失败告警，每天发送 {0} 次',\n            cronJobDirectoryRule: '备份目录任务失败告警，每天发送 {0} 次',\n            cronJobLogRule: '备份日志任务失败告警，每天发送 {0} 次',\n            cronJobSnapshotRule: '系统快照任务失败告警，每天发送 {0} 次',\n            cronJobShellRule: 'Shell 脚本任务失败告警，每天发送 {0} 次',\n            cronJobCurlRule: '访问 URL 任务失败告警，每天发送 {0} 次',\n            cronJobCutWebsiteLogRule: '切割网站日志任务失败告警，每天发送 {0} 次',\n            cronJobCleanRule: '缓存清理任务失败告警，每天发送 {0} 次',\n            cronJobNtpRule: '同步服务器时间任务失败告警，每天发送 {0} 次',\n            alertSmsHelper: '短信额度：总量 {0} 条，已使用 {1} 条',\n            goBuy: '去购买',\n            phone: '手机号',\n            phoneHelper: '请填写真实的手机号，以免不能正常接收告警信息',\n            dailyAlertNum: '每日告警次数',\n            dailyAlertNumHelper: '每日告警通知的总次数，最多通知 100 次',\n            timeRange: '时间范围',\n            sendTimeRange: '可发送时间范围',\n            sendTimeRangeHelper: '可推送{0}时间范围',\n            defaultPhone: '默认使用与许可证绑定的账户手机号',\n            noticeAlert: '通知告警',\n            resourceAlert: '资源告警',\n            agentOfflineAlertHelper: '节点开启离线告警，将通过主节点每半小时扫描执行一次告警任务',\n            offline: '离线告警',\n            offlineHelper: '设置为离线告警，将通过主节点每半小时扫描执行一次告警任务',\n            offlineOff: '开启离线告警',\n            offlineOffHelper: '开启离线告警，将通过主节点每半小时扫描执行一次告警任务',\n            offlineClose: '关闭离线告警',\n            offlineCloseHelper: '关闭离线告警，告警需通过子节点自行告警，请保证子节点网络畅通，以避免告警失败',\n            alertNotice: '告警通知',\n            methodConfig: '发送方式配置',\n            commonConfig: '全局配置',\n            smsConfig: '短信通知',\n            smsConfigHelper: '配置短信通知号码',\n            emailConfig: '邮箱',\n            emailConfigHelper: '配置 SMTP 邮件发送服务',\n            deleteConfigTitle: '删除告警配置',\n            deleteConfigMsg: '是否确认删除告警配置？',\n            test: '测试',\n            alertTestOk: '测试通知成功',\n            alertTestFailed: '测试通知失败',\n            displayName: '显示名称',\n            sender: '发信地址',\n            password: '密码',\n            host: 'SMTP 服务器',\n            port: '端口号',\n            encryption: '加密方式',\n            recipient: '收件人',\n            licenseTime: '许可证到期提醒',\n            licenseTimeTitle: '许可证到期提醒',\n            displayNameHelper: '邮件的发件人显示名称',\n            senderHelper: '用于发送邮件的邮箱地址',\n            passwordHelper: '邮件服务的授权码',\n            hostHelper: 'SMTP 服务器地址，例如: smtp.qq.com',\n            portHelper: 'SSL 通常为465，TLS 通常为587',\n            sslHelper: '如果 SMTP 端口是 465，通常需要启用 SSL',\n            tlsHelper: '如果 SMTP 端口是 587，通常需要启用 TLS',\n            triggerCondition: '触发条件',\n            loginFail: '内，登录失败',\n            nodeException: '节点异常告警',\n            licenseException: '许可证异常告警',\n            panelLogin: '面板登录异常告警',\n            sshLogin: 'SSH 登录异常告警',\n            panelIpLogin: '面板登录 IP 异常告警',\n            sshIpLogin: 'SSH 登录 IP 异常告警',\n            ipWhiteListHelper: '白名单中的 IP 不受规则限制，登录成功不进行告警',\n            nodeExceptionRule: '节点异常告警，每天发送 {0} 次',\n            licenseExceptionRule: '许可证异常告警，每天发送 {0} 次',\n            panelLoginRule: '面板登录告警，每天发送 {0} 次',\n            sshLoginRule: 'SSH 登录告警，每天发送 {0} 次',\n            userNameHelper: '用户名为空会默认使用发件箱地址',\n            alertConfigHelper: '配置告警通知通道，用于接收面板消息推送',\n            weComConfigHelper: '企业微信告警通知配置',\n            wechatConfigHelper: '微信公众号告警通知配置',\n            dingTalkConfigHelper: '钉钉告警通知配置',\n            feiShuConfigHelper: '飞书告警通知配置',\n            webhookName: '机器人名称',\n            webhookUrl: 'Webhook 地址',\n            alertConfigProHelper: '专业版额外支持企业微信、钉钉、飞书及短信告警。',\n        },\n        theme: {\n            lingXiaGold: '凌霞金',\n            classicBlue: '经典蓝',\n            freshGreen: '清新绿',\n            customColor: '自定义主题色',\n            setDefaultHelper: '即将恢复主题配色到初始状态，是否继续？',\n            setHelper: '即将保存当前选定的主题配色，是否继续？',\n        },\n        app: {\n            app: 'APP',\n            title: '面板别名',\n            titleHelper: '面板别名用于 APP 端的显示（默认面板别名）',\n            qrCode: '二维码',\n            apiStatusHelper: '面板 APP 需要开启 API 接口功能',\n            apiInterfaceHelper: '支持面板 API 接口访问功能（面板 APP 需要开启该功能）',\n            apiInterfaceHelper1:\n                '面板 APP 访问需将访问者添加至白名单，非固定 IP 建议添加 0.0.0.0/0（所有 IPv4），::/0（所有 IPv6）',\n            qrCodeExpired: '刷新时间',\n            apiLeakageHelper: '请勿泄露二维码，确保仅在受信任的环境中使用',\n        },\n        exchange: {\n            exchange: '文件对传',\n            exchangeConfirm: '是否将 {0} 节点文件/文件夹 {1} 传输到 {2} 节点 {3} 目录？',\n        },\n        cluster: {\n            cluster: '应用高可用',\n            name: '集群名称',\n            addCluster: '添加集群',\n            installNode: '安装节点',\n            master: '主节点',\n            slave: '从节点',\n            replicaStatus: '主从状态',\n            unhealthyDeleteError: '安装节点状态异常，请在节点列表检查后重试！',\n            replicaStatusError: '状态获取异常 请检查主节点',\n            masterHostError: '主节点 IP 不能为 127.0.0.1',\n        },\n    },\n};\nexport default {\n    ...fit2cloudZhLocale,\n    ...message,\n};\n"
  },
  {
    "path": "frontend/src/layout/components/AppFooter.vue",
    "content": "<template>\n    <div class=\"footer\" :style=\"{ height: mobile ? '108px' : '48px' }\">\n        <div class=\"flex w-full flex-col gap-4 md:justify-between md:flex-row\">\n            <div class=\"flex flex-wrap gap-4\">\n                <a v-if=\"!globalStore.isIntl && !globalStore.isFxplay\" href=\"https://fit2cloud.com/\" target=\"_blank\">\n                    Copyright © 2014-{{ year }} {{ $t('commons.fit2cloud') }}\n                </a>\n                <a v-else href=\"https://1panel.pro/\" target=\"_blank\">\n                    Copyright © {{ year }} {{ $t('commons.lingxia') }}\n                </a>\n            </div>\n            <div class=\"flex flex-row gap-2 md:flex-col lg:flex-row\">\n                <SystemUpgrade :footer=\"true\" />\n            </div>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport SystemUpgrade from '@/components/system-upgrade/index.vue';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst year = new Date().getFullYear();\n</script>\n\n<style scoped lang=\"scss\">\n.footer {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    height: 48px;\n    background: var(--panel-footer-bg);\n    border-top: 1px solid var(--panel-footer-border);\n    box-sizing: border-box;\n    padding: 10px 20px;\n    a {\n        font-size: 12px;\n        color: #858585;\n        text-decoration: none;\n        letter-spacing: 0.5px;\n    }\n    span {\n        font-size: 12px;\n        color: #858585;\n        text-decoration: none;\n        letter-spacing: 0.5px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/AppMain.vue",
    "content": "<template>\n    <router-view v-slot=\"{ Component, route }\" :key=\"key\">\n        <transition appear name=\"fade-transform\" mode=\"out-in\">\n            <keep-alive :include=\"include\">\n                <component :is=\"Component\" :key=\"route.path\"></component>\n            </keep-alive>\n        </transition>\n    </router-view>\n</template>\n\n<script setup lang=\"ts\">\nimport cacheRouter from '@/routers/cache-router';\nimport { computed } from 'vue';\n\nconst key = computed(() => {\n    return Math.random();\n});\nconst include = computed(() => {\n    return props.keepAlive || cacheRouter;\n});\nconst props = defineProps({\n    keepAlive: {\n        type: Object,\n        required: false,\n    },\n});\n</script>\n"
  },
  {
    "path": "frontend/src/layout/components/MobileHeader.vue",
    "content": "<script setup lang=\"ts\">\nimport { MenuStore } from '@/store';\nconst menuStore = MenuStore();\n</script>\n<template>\n    <div class=\"mobile-header\">\n        <svg-icon class=\"mobile-menu\" iconName=\"p-caidan\" @click=\"menuStore.setCollapse()\"></svg-icon>\n    </div>\n</template>\n\n<style lang=\"scss\" scoped>\n.mobile-header {\n    background: rgba(255, 255, 255, 0.65);\n    box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2);\n    padding: 10px;\n    text-align: left;\n    z-index: 99;\n    .mobile-menu {\n        margin-left: 10px;\n        font-size: 10px;\n        cursor: pointer;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/components/Collapse.vue",
    "content": "<template>\n    <div>\n        <el-popover\n            placement=\"right-end\"\n            :show-arrow=\"false\"\n            :offset=\"0\"\n            :width=\"200\"\n            trigger=\"click\"\n            @before-enter=\"showPopover\"\n            popper-class=\"custom-popover-dropdown\"\n        >\n            <template #reference>\n                <div class=\"el-dropdown-link\" v-if=\"!menuStore.isCollapse\">\n                    <el-badge is-dot :value=\"taskCount\" :show-zero=\"false\" :offset=\"[5, 5]\">\n                        <el-button link @click=\"openChangeNode\" @mouseenter=\"openChangeNode\">\n                            <SvgIcon class=\"icon\" iconName=\"p-pcm\" />\n                            <span class=\"ellipsis-text\">{{ loadCurrentName() }}</span>\n                        </el-button>\n                    </el-badge>\n                </div>\n                <div v-else class=\"el-dropdown-link\">\n                    <el-badge is-dot :value=\"taskCount\" :show-zero=\"false\" :offset=\"[-5, 5]\">\n                        <SvgIcon class=\"icon\" iconName=\"p-pcm\" />\n                    </el-badge>\n                </div>\n            </template>\n            <div class=\"dropdown-menu\" v-loading=\"loading\">\n                <div class=\"dropdown-item\" @click=\"openTask\">\n                    <SvgIcon class=\"icon\" iconName=\"p-renwuzhongxin1\" />\n                    {{ $t('menu.msgCenter') }}\n                    <el-tag class=\"msg-tag\" v-if=\"taskCount !== 0\" size=\"small\" round>{{ taskCount }}</el-tag>\n                </div>\n                <el-divider v-if=\"showNodes()\" class=\"divider\" />\n\n                <div v-if=\"showNodes()\">\n                    <el-scrollbar max-height=\"218px\" :noresize=\"true\">\n                        <div\n                            class=\"dropdown-item\"\n                            @click=\"changeNode(item.name)\"\n                            :disabled=\"item.status !== 'Healthy'\"\n                            v-for=\"item in nodeOptions\"\n                            :key=\"item.name\"\n                        >\n                            <div class=\"node\">\n                                <SvgIcon class=\"icon\" iconName=\"p-zhuji\" />\n                                <span class=\"node-name\">\n                                    {{ item.name === 'local' ? globalStore.getMasterAlias() : item.name }}\n                                </span>\n                                <el-tooltip\n                                    v-if=\"item.status !== 'Healthy' || !item.isBound\"\n                                    :content=\"\n                                        item.isBound ? $t('xpack.node.nodeUnhealthy') : $t('xpack.node.nodeUnbind')\n                                    \"\n                                    placement=\"right\"\n                                >\n                                    <el-icon class=\"icon-status\" type=\"danger\">\n                                        <Warning />\n                                    </el-icon>\n                                </el-tooltip>\n                            </div>\n                        </div>\n                    </el-scrollbar>\n                </div>\n                <el-input\n                    v-if=\"showNodes() && nodes?.length > 5\"\n                    suffix-icon=\"Search\"\n                    v-model=\"filter\"\n                    @input=\"changeFilter\"\n                    class=\"w-full filter-input\"\n                    size=\"small\"\n                    clearable\n                />\n                <el-divider class=\"divider\" />\n                <div class=\"dropdown-item\" @click=\"logout\">\n                    <SvgIcon class=\"icon\" iconName=\"p-tuichudenglu3\" />\n                    {{ $t('commons.login.logout') }}\n                </div>\n            </div>\n        </el-popover>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { GlobalStore, MenuStore } from '@/store';\nimport { DropdownInstance } from 'element-plus';\nimport { countExecutingTask } from '@/api/modules/log';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { getAgentSettingInfo, listNodeOptions } from '@/api/modules/setting';\nimport { ref, watch } from 'vue';\nimport bus from '@/global/bus';\nimport { logOutApi } from '@/api/modules/auth';\nimport router from '@/routers';\nimport { loadProductProFromDB } from '@/utils/xpack';\nimport { routerToNameWithQuery } from '@/utils/router';\nimport { setDefaultNodeInfo } from '@/utils/node';\n\nconst filter = ref();\nconst globalStore = GlobalStore();\nconst menuStore = MenuStore();\nconst nodes = ref([]);\nconst nodeOptions = ref([]);\nconst loading = ref();\nconst nodeChangeRef = ref<DropdownInstance>();\nconst props = defineProps({\n    version: String,\n});\nconst isMasterPro = computed(() => {\n    return globalStore.isMasterPro();\n});\nwatch(\n    () => globalStore.isMasterPro(),\n    () => {\n        loadNodes();\n    },\n);\n\nconst emit = defineEmits(['openTask']);\nbus.on('refreshTask', () => {\n    checkTask();\n});\n\nconst openChangeNode = () => {\n    nodeChangeRef.value?.handleOpen();\n};\n\nconst loadCurrentName = () => {\n    if (globalStore.currentNode) {\n        if (globalStore.currentNode === 'local') {\n            return globalStore.getMasterAlias();\n        }\n        return globalStore.currentNode;\n    }\n    return globalStore.getMasterAlias();\n};\n\nconst showPopover = () => {\n    filter.value = '';\n    loadNodes();\n    changeFilter();\n};\n\nconst changeFilter = () => {\n    nodeOptions.value = [];\n    for (const item of nodes.value) {\n        if (item.name.indexOf(filter.value) !== -1) {\n            nodeOptions.value.push(item);\n        }\n    }\n};\n\nconst loadNodes = async () => {\n    loading.value = true;\n    nodes.value = [];\n    if (!isMasterPro.value) {\n        setDefaultNodeInfo();\n        loading.value = false;\n        return;\n    }\n    await listNodeOptions('all')\n        .then((res) => {\n            if (!res) {\n                nodes.value = [];\n                setDefaultNodeInfo();\n                loading.value = false;\n                return;\n            }\n            nodes.value = res.data || [];\n            if (nodes.value.length === 0) {\n                setDefaultNodeInfo();\n            }\n            nodes.value.sort((a, b) => {\n                if (a.name === 'local') return -1;\n                if (b.name === 'local') return 1;\n                return 0;\n            });\n            nodeOptions.value = nodes.value || [];\n            loading.value = false;\n        })\n        .catch(() => {\n            nodes.value = [];\n            loading.value = false;\n        });\n};\nconst changeNode = (command: string) => {\n    if (globalStore.currentNode === command) {\n        return;\n    }\n    for (const item of nodes.value) {\n        if (item.name == command) {\n            if (command == 'local') {\n                globalStore.currentNode = 'local';\n                globalStore.currentNodeAddr = item.addr;\n                loadGlobalSetting();\n                localStorage.removeItem('dashboardCache');\n                localStorage.removeItem('upgradeChecked');\n                loadProductProFromDB();\n                routerToNameWithQuery('home', { t: Date.now() });\n                return;\n            }\n            if (!item.isBound) {\n                MsgError(i18n.global.t('xpack.node.nodeUnbindHelper'));\n                return;\n            }\n            if (item.status !== 'Healthy') {\n                MsgError(i18n.global.t('xpack.node.nodeUnhealthyHelper'));\n                return;\n            }\n            if (props.version != item.version) {\n                MsgError(i18n.global.t('setting.versionNotSame'));\n                return;\n            }\n            loadGlobalSetting();\n            localStorage.removeItem('dashboardCache');\n            localStorage.removeItem('upgradeChecked');\n            globalStore.currentNode = command || 'local';\n            globalStore.currentNodeAddr = item.addr;\n            loadProductProFromDB();\n            routerToNameWithQuery('home', { t: Date.now() });\n        }\n    }\n};\n\nconst loadGlobalSetting = async () => {\n    await getAgentSettingInfo().then((res) => {\n        globalStore.defaultNetwork = res.data.defaultNetwork;\n    });\n};\n\nconst showNodes = () => {\n    return nodes.value.length > 0 && isMasterPro;\n};\n\nconst taskCount = ref(0);\nconst checkTask = async () => {\n    try {\n        const res = await countExecutingTask();\n        taskCount.value = res.data;\n    } catch (error) {}\n};\n\nconst openTask = () => {\n    emit('openTask');\n};\n\nconst logout = () => {\n    ElMessageBox.confirm(i18n.global.t('commons.msg.sureLogOut'), i18n.global.t('commons.msg.infoTitle'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'warning',\n    })\n        .then(async () => {\n            await logOutApi();\n            router.push({ name: 'entrance', params: { code: globalStore.entrance } });\n            globalStore.setLogStatus(false);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {});\n};\n\nonMounted(() => {\n    loadNodes();\n    checkTask();\n});\n</script>\n\n<style scoped lang=\"scss\">\n@use '../index';\n\n.el-dropdown-link {\n    display: flex;\n    align-items: center;\n    box-sizing: border-box;\n    border-top: 1px solid var(--panel-footer-border);\n    height: 48px;\n    .icon {\n        margin-left: 25px;\n        font-size: 8px;\n        margin-right: 7px;\n        color: var(--panel-main-bg-color-1);\n    }\n    &:hover {\n        .icon {\n            color: var(--el-color-primary);\n        }\n        .el-button {\n            color: var(--el-color-primary);\n        }\n    }\n}\n.custom-popover-dropdown {\n    padding: 0 !important;\n    border: 1px solid #e4e7ed !important;\n    box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1) !important;\n    background-color: var(--el-menu-item-bg-color);\n    .divider {\n        display: block;\n        height: 1px;\n        width: 91%;\n        margin: 3px 8px;\n        border-top: 1px var(--el-border-color) var(--el-border-style);\n    }\n}\n\n.dropdown-menu {\n    min-width: 120px;\n}\n\n.dropdown-item {\n    display: flex;\n    align-items: center;\n    padding: 2px 8px;\n    cursor: pointer;\n    min-height: 32px;\n    transition: background 0.3s;\n    .icon {\n        font-size: 6px;\n    }\n    .icon-status {\n        font-size: 16px;\n        margin-left: auto;\n    }\n    .node {\n        display: flex;\n        align-items: center;\n        gap: 6px;\n        padding: 3px 0;\n        width: 100%;\n    }\n    .node-name {\n        flex: 1;\n        min-width: 0;\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n    }\n    .msg-tag {\n        margin-left: auto;\n        background-color: transparent;\n        color: var(--panel-main-bg-color-1);\n    }\n    &:hover {\n        color: var(--el-color-primary);\n        .icon {\n            color: var(--el-color-primary);\n        }\n        .msg-tag {\n            color: var(--el-color-primary);\n        }\n    }\n}\n.filter-input {\n    padding: 0 8px;\n    margin-bottom: 4px;\n}\n.dropdown-item:hover {\n    background: var(--el-menu-item-bg-color-active);\n}\n.ellipsis-text {\n    display: inline-block;\n    max-width: 120px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/components/Logo.vue",
    "content": "<template>\n    <div class=\"logo\" style=\"cursor: pointer\" @click=\"goHome\">\n        <template v-if=\"isCollapse\">\n            <img\n                v-if=\"globalStore.themeConfig.logo && !logoLoadFailed\"\n                :src=\"`/api/v2/images/logo?t=${Date.now()}`\"\n                style=\"cursor: pointer\"\n                alt=\"logo\"\n                @error=\"logoLoadFailed = true\"\n            />\n            <MenuLogo v-else />\n        </template>\n        <template v-else>\n            <img\n                v-if=\"globalStore.themeConfig.logoWithText && !logoWithTextLoadFailed\"\n                :src=\"`/api/v2/images/logoWithText?t=${Date.now()}`\"\n                style=\"cursor: pointer\"\n                alt=\"logo\"\n                @error=\"logoWithTextLoadFailed = true\"\n            />\n            <PrimaryLogo v-else />\n        </template>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { GlobalStore } from '@/store';\nimport PrimaryLogo from '@/assets/images/1panel-logo.svg?component';\nimport MenuLogo from '@/assets/images/1panel-menu-logo.svg?component';\nimport { ref } from 'vue';\nimport { routerToNameWithQuery } from '@/utils/router';\n\ndefineProps<{ isCollapse: boolean }>();\n\nconst logoLoadFailed = ref(false);\nconst logoWithTextLoadFailed = ref(false);\nconst globalStore = GlobalStore();\n\nconst goHome = () => {\n    routerToNameWithQuery('home', { t: Date.now() });\n};\n</script>\n\n<style scoped lang=\"scss\">\n.logo {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    height: 49px;\n    z-index: 1;\n    img {\n        object-fit: contain;\n        width: 95%;\n        height: 45px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/components/SubItem.vue",
    "content": "<template>\n    <template v-for=\"subItem in menuList\" :key=\"subItem.name\">\n        <el-sub-menu v-if=\"subItem?.children?.length > 1\" :index=\"subItem.path\" popper-class=\"sidebar-container-popper\">\n            <template #title>\n                <el-icon>\n                    <SvgIcon :iconName=\"(subItem.meta?.icon as string)\" />\n                </el-icon>\n                <span>{{ $t(subItem.meta?.title as string, 2) }}</span>\n            </template>\n            <SubItem :menuList=\"subItem.children\" :level=\"level + 1\" />\n        </el-sub-menu>\n\n        <el-menu-item v-else-if=\"subItem?.children?.length === 1\" :index=\"subItem.children[0].path\">\n            <el-icon>\n                <SvgIcon :iconName=\"(subItem.meta?.icon as string)\" />\n            </el-icon>\n            <template #title>\n                <span>{{ $t(subItem.meta?.title as string, 2) }}</span>\n            </template>\n        </el-menu-item>\n\n        <el-menu-item v-else-if=\"subItem.path === '/xpack/upage'\" :index=\"''\" @click=\"goUpage\">\n            <el-icon v-if=\"subItem.meta?.icon && level === 0\">\n                <SvgIcon :iconName=\"(subItem.meta?.icon as string)\" />\n            </el-icon>\n            <template #title>\n                <span v-if=\"subItem.meta?.icon && level === 0\">{{ $t(subItem.meta?.title as string, 2) }}</span>\n                <span v-else style=\"margin-left: 10px\">{{ $t(subItem.meta?.title as string, 2) }}</span>\n            </template>\n        </el-menu-item>\n\n        <el-menu-item v-else :index=\"subItem.path\">\n            <el-icon v-if=\"subItem.meta?.icon && level === 0\">\n                <SvgIcon :iconName=\"(subItem.meta?.icon as string)\" />\n            </el-icon>\n            <template #title>\n                <span v-if=\"subItem.meta?.icon && level === 0\">{{ $t(subItem.meta?.title as string, 2) }}</span>\n                <span v-else style=\"margin-left: 10px\">{{ $t(subItem.meta?.title as string, 2) }}</span>\n            </template>\n        </el-menu-item>\n    </template>\n</template>\n\n<script setup lang=\"ts\">\nimport { RouteRecordRaw } from 'vue-router';\nimport SvgIcon from '@/components/svg-icon/svg-icon.vue';\n\ndefineProps<{ menuList: RouteRecordRaw[]; level?: number }>();\n\nconst goUpage = () => {\n    window.open('https://www.lxware.cn/upage', '_blank', 'noopener,noreferrer');\n};\n</script>\n\n<style scoped lang=\"scss\">\n@use '../index';\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/index.scss",
    "content": "@use '@/styles/var.scss' as *;\n.el-menu {\n    user-select: none;\n    background: none;\n    width: 100%;\n    padding: 0 7px;\n    .el-menu-item {\n        border-radius: 4px;\n        background-color: var(--el-menu-item-bg-color);\n        margin: 7px 0;\n        height: 42px;\n        box-shadow: 0px 0px 4px rgba(0, 94, 235, 0.1);\n        box-sizing: border-box;\n\n        &:hover {\n            .el-icon {\n                color: var(--el-color-primary);\n            }\n\n            span {\n                color: var(--el-color-primary);\n            }\n        }\n        &.is-active {\n            background-color: var(--el-menu-item-bg-color-active);\n            box-shadow: 0px 0px 4px rgba(0, 94, 235, 0.1), inset 0 0 0 2px var(--el-color-primary);\n\n            &::before {\n                position: absolute;\n                border-radius: 4px;\n                left: 12px;\n                width: 4px;\n                height: 14px;\n                content: '';\n                background: var(--el-color-primary);\n            }\n        }\n    }\n    .el-sub-menu {\n        &.is-active {\n            .el-sub-menu__title {\n                span {\n                    color: var(--el-color-primary);\n                }\n\n                .el-icon {\n                    color: var(--el-color-primary);\n                }\n            }\n        }\n        .el-sub-menu__title {\n            background-color: var(--el-menu-item-bg-color);\n            box-shadow: 0 0 4px rgba(0, 94, 235, 0.1);\n            margin-top: 7px;\n            height: 46px;\n            border-radius: 4px;\n            &:hover {\n                .el-icon {\n                    color: var(--el-color-primary);\n                }\n\n                span {\n                    color: var(--el-color-primary);\n                }\n            }\n        }\n        .el-menu {\n            padding: 0;\n            .el-menu-item {\n                box-shadow: none;\n                padding-left: 35px;\n            }\n        }\n    }\n}\n\n.sidebar-container-popper {\n    .el-menu {\n        background-color: var(--el-menu-bg-color);\n        padding: 4px 8px;\n    }\n}\n\n.el-sub-menu__title {\n    padding-right: 0;\n}\n\n.p-mr-5 {\n    margin-right: 5px;\n}\n"
  },
  {
    "path": "frontend/src/layout/components/Sidebar/index.vue",
    "content": "<template>\n    <div\n        class=\"sidebar-container\"\n        element-loading-text=\"Loading...\"\n        :element-loading-spinner=\"loadingSvg\"\n        element-loading-svg-view-box=\"-10, -10, 50, 50\"\n        element-loading-background=\"rgba(122, 122, 122, 0.01)\"\n    >\n        <div class=\"fixed\" v-if=\"!isCollapse\">\n            <PrimaryMenu />\n        </div>\n        <Logo :isCollapse=\"isCollapse\" />\n        <el-scrollbar>\n            <el-menu\n                :default-active=\"activeMenu\"\n                :router=\"true\"\n                :collapse=\"isCollapse\"\n                :collapse-transition=\"false\"\n                :unique-opened=\"true\"\n                @select=\"handleMenuClick\"\n                class=\"custom-menu\"\n            >\n                <SubItem :menuList=\"routerMenus\" :level=\"0\" />\n            </el-menu>\n        </el-scrollbar>\n        <Collapse :version=\"version\" @open-task=\"openTask\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, onMounted } from 'vue';\nimport { RouteRecordRaw, useRoute } from 'vue-router';\nimport { loadingSvg } from '@/utils/svg';\nimport Logo from './components/Logo.vue';\nimport Collapse from './components/Collapse.vue';\nimport SubItem from './components/SubItem.vue';\nimport { menuList } from '@/routers/router';\nimport { GlobalStore, MenuStore } from '@/store';\nimport { isString } from '@vueuse/core';\nimport { getSettingInfo } from '@/api/modules/setting';\nimport PrimaryMenu from '@/assets/images/menu-bg.svg?component';\n\nconst route = useRoute();\nconst menuStore = MenuStore();\nconst globalStore = GlobalStore();\nconst version = ref();\n\nconst activeMenu = computed(() => {\n    const { meta, path } = route;\n    return isString(meta.activeMenu) ? meta.activeMenu : path;\n});\nconst isCollapse = computed((): boolean => menuStore.isCollapse);\n\nlet routerMenus = computed((): RouteRecordRaw[] => {\n    return menuStore.menuList.filter((route) => route.meta && !route.meta.hideInSidebar) as RouteRecordRaw[];\n});\n\nconst screenWidth = ref(0);\nconst listeningWindow = () => {\n    window.onresize = () => {\n        return (() => {\n            screenWidth.value = document.body.clientWidth;\n            if (!isCollapse.value && screenWidth.value < 1200) menuStore.setCollapse();\n            if (isCollapse.value && screenWidth.value > 1200) menuStore.setCollapse();\n        })();\n    };\n};\nlisteningWindow();\nconst emit = defineEmits(['menuClick', 'openTask']);\nconst handleMenuClick = (path) => {\n    emit('menuClick', path);\n};\n\nfunction getCheckedLabels(menu: any, showSet: Set<string>) {\n    for (const item of menu) {\n        if (item.isShow) {\n            showSet.add(item.label);\n        }\n        if (item.children) {\n            getCheckedLabels(item.children, showSet);\n        }\n    }\n}\n\nconst openTask = () => {\n    emit('openTask');\n};\n\nconst search = async () => {\n    try {\n        const res = await getSettingInfo();\n        version.value = res.data.systemVersion;\n        let hideMenu = JSON.parse(res.data.hideMenu);\n        const showSet = new Set<string>();\n        getCheckedLabels(hideMenu, showSet);\n        const rstMenuList: RouteRecordRaw[] = [];\n        const resMenuList = adjustAndCleanMenu(hideMenu, menuList);\n        for (const menu of resMenuList) {\n            let menuItem = JSON.parse(JSON.stringify(menu));\n            if (!showSet.has(menuItem.name as string)) {\n                continue;\n            } else if (menuItem.name === 'Xpack-Menu') {\n                menuItem.meta.hideInSidebar = false;\n            }\n            const itemChildren =\n                (menuItem.children ?? []).filter(\n                    (item) =>\n                        item.name && showSet.has(item.name as string) && !(item.name === 'Upage' && globalStore.isIntl),\n                ) || [];\n\n            if (itemChildren.length === 1) {\n                menuItem.meta.icon = itemChildren[0].meta.icon;\n                menuItem.meta.title = itemChildren[0].meta.title;\n            }\n            menuItem.children = itemChildren;\n            rstMenuList.push(menuItem);\n        }\n        if (!isSameMenuList(menuStore.menuList as RouteRecordRaw[], rstMenuList)) {\n            menuStore.setMenuList(rstMenuList);\n        }\n    } catch (error) {\n        if (!menuStore.menuList || menuStore.menuList.length === 0) {\n            menuStore.setMenuList(menuList);\n        }\n    }\n};\n\nfunction isSameMenuList(source: RouteRecordRaw[], target: RouteRecordRaw[]) {\n    return JSON.stringify(source) === JSON.stringify(target);\n}\n\nfunction adjustAndCleanMenu(menuItem, list) {\n    const menuList = JSON.parse(JSON.stringify(list));\n    const itemMap = new Map();\n    for (const parent of menuList) {\n        itemMap.set(parent.name, parent);\n        if (Array.isArray(parent.children)) {\n            for (const child of parent.children) {\n                itemMap.set(child.name, child);\n            }\n        }\n    }\n\n    function buildTree(refList) {\n        const result = [];\n\n        for (const ref of refList) {\n            const refName = ref.label;\n            const matched = itemMap.get(refName);\n\n            if (!matched) continue;\n\n            if (Array.isArray(ref.children) && ref.children.length > 0) {\n                matched.children = buildTree(ref.children);\n            } else {\n                delete matched.children;\n            }\n\n            result.push(matched);\n        }\n\n        return result;\n    }\n\n    const newMenu = buildTree(menuItem);\n    for (const menu of newMenu) {\n        if (menu.children?.length === 1) {\n            menu.meta.icon = menu.children[0].meta.icon;\n            menu.meta.title = menu.children[0].meta.title;\n        }\n    }\n\n    return newMenu;\n}\n\nonMounted(() => {\n    if (!menuStore.menuList || menuStore.menuList.length === 0) {\n        menuStore.setMenuList(menuList);\n    }\n    search();\n});\n</script>\n\n<style lang=\"scss\">\n@use 'index';\n\n.background {\n    z-index: 20;\n}\n\n.custom-menu .el-menu-item {\n    white-space: normal !important;\n    word-break: break-word;\n    overflow-wrap: break-word;\n    line-height: normal;\n}\n\n.sidebar-container {\n    position: relative;\n    display: flex;\n    flex-direction: column;\n    height: 100%;\n    background: var(--panel-menu-bg-color) no-repeat top;\n\n    .el-scrollbar {\n        flex: 1;\n        .el-menu {\n            overflow: auto;\n            overflow-x: hidden;\n            border-right: none;\n        }\n    }\n}\n\n.ico {\n    height: 20px !important;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Tabs/components/TabItem.vue",
    "content": "<template>\n    <el-tab-pane :name=\"tabItem.path\">\n        <template #label>\n            <el-dropdown\n                size=\"small\"\n                :id=\"tabItem.path\"\n                ref=\"dropdownRef\"\n                trigger=\"contextmenu\"\n                @visible-change=\"$emit('dropdownVisibleChange', $event, tabItem.path)\"\n            >\n                <span class=\"custom-tabs-label\">\n                    <span>{{ menuName }}</span>\n                </span>\n                <template #dropdown>\n                    <el-dropdown-menu>\n                        <el-dropdown-item\n                            v-if=\"tabsStore.hasCloseDropdown(tabItem.path, 'close')\"\n                            @click=\"$emit('closeTab', tabItem.path)\"\n                        >\n                            <el-icon><Close /></el-icon>\n                            {{ $t('commons.button.close') }}\n                        </el-dropdown-item>\n                        <el-dropdown-item\n                            v-if=\"tabsStore.hasCloseDropdown(tabItem.path, 'left')\"\n                            @click=\"$emit('closeTabs', tabItem.path, 'left')\"\n                        >\n                            <el-icon><DArrowLeft /></el-icon>\n                            {{ $t('tabs.closeLeft') }}\n                        </el-dropdown-item>\n                        <el-dropdown-item\n                            v-if=\"tabsStore.hasCloseDropdown(tabItem.path, 'right')\"\n                            @click=\"$emit('closeTabs', tabItem.path, 'right')\"\n                        >\n                            <el-icon><DArrowRight /></el-icon>\n                            {{ $t('tabs.closeRight') }}\n                        </el-dropdown-item>\n                        <el-dropdown-item\n                            v-if=\"tabsStore.hasCloseDropdown(tabItem.path, 'other')\"\n                            @click=\"$emit('closeOtherTabs', tabItem.path)\"\n                        >\n                            <el-icon><More /></el-icon>\n                            {{ $t('tabs.closeOther') }}\n                        </el-dropdown-item>\n                    </el-dropdown-menu>\n                </template>\n            </el-dropdown>\n        </template>\n    </el-tab-pane>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue';\nimport { TabsStore } from '@/store';\nimport i18n from '@/lang';\nimport { Close, DArrowLeft, DArrowRight, More } from '@element-plus/icons-vue';\n\nconst tabsStore = TabsStore();\n\nconst props = defineProps({\n    tabItem: {\n        type: Object,\n        required: true,\n    },\n});\n\ndefineEmits(['closeTab', 'closeOtherTabs', 'closeTabs', 'dropdownVisibleChange']);\n\nconst menuName = computed(() => {\n    let title;\n    if (props.tabItem.meta.parent) {\n        title = i18n.global.t(props.tabItem.meta.parent) + '-' + i18n.global.t(props.tabItem.meta.title);\n    } else {\n        title = i18n.global.t(props.tabItem.meta.title);\n    }\n    if (props.tabItem.meta.detail) {\n        title = title + '-' + i18n.global.t(props.tabItem.meta.detail);\n    }\n    return title;\n});\n\nconst dropdownRef = ref();\n\ndefineExpose({\n    dropdownRef,\n});\n</script>\n\n<style scoped>\n.common-tabs .custom-tabs-label span {\n    vertical-align: middle;\n    margin-left: 4px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/Tabs/index.vue",
    "content": "<template>\n    <el-tabs\n        v-bind=\"$attrs\"\n        v-model=\"tabsStore.activeTabPath\"\n        class=\"common-tabs\"\n        type=\"card\"\n        :closable=\"tabsStore.openedTabs.length > 1\"\n        @tab-change=\"tabChange\"\n        @tab-remove=\"closeTab\"\n    >\n        <tabs-view-item\n            v-for=\"item in tabsStore.openedTabs\"\n            ref=\"tabItems\"\n            :key=\"item.path\"\n            :tab-item=\"item\"\n            @close-tab=\"closeTab\"\n            @close-other-tabs=\"closeOtherTabs\"\n            @close-tabs=\"closeTabs\"\n            @dropdown-visible-change=\"dropdownVisibleChange\"\n        />\n    </el-tabs>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, ref } from 'vue';\nimport { TabsStore } from '@/store';\nimport { useRoute, useRouter } from 'vue-router';\nimport TabsViewItem from './components/TabItem.vue';\n\nconst router = useRouter();\nconst route = useRoute();\nconst tabsStore = TabsStore();\nconst tabItems = ref();\n\nonMounted(() => {\n    if (!tabsStore.openedTabs.length) {\n        tabsStore.addTab(route);\n    }\n    tabsStore.activeTabPath = route.path;\n});\n\nconst tabChange = (tabPath) => {\n    const tab = tabsStore.findTab(tabPath);\n    if (tab) {\n        router.push({ path: tab.path, query: { uncached: 'true' } });\n        tabsStore.activeTabPath = tab.path;\n    }\n};\n\nconst closeTab = (tabPath) => {\n    const lastTabPath = tabsStore.removeTab(tabPath);\n    if (tabPath !== tabsStore.activeTabPath) {\n        return;\n    }\n    if (lastTabPath) {\n        tabChange(lastTabPath);\n    }\n};\n\nconst closeOtherTabs = (tabPath) => {\n    tabsStore.removeOtherTabs(tabPath);\n    tabChange(tabPath);\n};\n\nconst closeTabs = (tabPath, type) => {\n    tabsStore.removeTabs(tabPath, type);\n    tabChange(tabPath);\n};\n\nconst dropdownVisibleChange = (visible, tabPath) => {\n    if (visible) {\n        // 关闭其他下拉菜单\n        tabItems.value.forEach(({ dropdownRef }) => {\n            if (dropdownRef.id !== tabPath) {\n                dropdownRef.handleClose();\n            }\n        });\n    }\n};\n</script>\n\n<style scoped lang=\"scss\">\n:deep(.el-tabs__header) {\n    margin: 0;\n    .el-tabs__item.is-active {\n        border-bottom-color: var(--el-color-primary) !important;\n        border-bottom-width: 2px !important;\n    }\n}\n.common-tabs {\n    margin-bottom: 7px;\n    margin-left: 20px;\n    margin-right: 20px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/layout/components/index.ts",
    "content": "export { default as Sidebar } from './Sidebar/index.vue';\nexport { default as Footer } from './AppFooter.vue';\nexport { default as AppMain } from './AppMain.vue';\nexport { default as MobileHeader } from './MobileHeader.vue';\nexport { default as Tabs } from '@/layout/components/Tabs/index.vue';\n"
  },
  {
    "path": "frontend/src/layout/hooks/useResize.ts",
    "content": "import { watch, onBeforeMount, onMounted, onBeforeUnmount } from 'vue';\nimport { useRoute } from 'vue-router';\nimport { GlobalStore, MenuStore } from '@/store';\nimport { DeviceType } from '@/enums/app';\n/** 参考 Bootstrap 的响应式设计 WIDTH = 600 */\nconst WIDTH = 600;\n\n/** 根据大小变化重新布局 */\nexport default () => {\n    const route = useRoute();\n    const globalStore = GlobalStore();\n    const menuStore = MenuStore();\n    const _isMobile = () => {\n        const rect = document.body.getBoundingClientRect();\n        return rect.width - 1 < WIDTH;\n    };\n\n    const _resizeHandler = () => {\n        if (!document.hidden) {\n            const isMobile = _isMobile();\n            globalStore.toggleDevice(isMobile ? DeviceType.Mobile : DeviceType.Desktop);\n            if (isMobile) {\n                menuStore.closeSidebar(true);\n            }\n        }\n    };\n\n    watch(\n        () => route.name,\n        () => {\n            if (globalStore.device === DeviceType.Mobile && !menuStore.isCollapse) {\n                menuStore.closeSidebar(false);\n            }\n        },\n    );\n\n    onBeforeMount(() => {\n        window.addEventListener('resize', _resizeHandler);\n    });\n\n    onMounted(() => {\n        if (_isMobile()) {\n            globalStore.toggleDevice(DeviceType.Mobile);\n            menuStore.closeSidebar(true);\n        }\n    });\n\n    onBeforeUnmount(() => {\n        window.removeEventListener('resize', _resizeHandler);\n    });\n};\n"
  },
  {
    "path": "frontend/src/layout/index.vue",
    "content": "<template>\n    <div\n        :class=\"classObj\"\n        class=\"app-wrapper relative\"\n        v-loading=\"loading\"\n        :element-loading-text=\"loadingText\"\n        fullscreen\n    >\n        <div v-if=\"classObj.mobile && classObj.openSidebar\" class=\"drawer-bg\" @click=\"handleClickOutside\" />\n        <el-affix v-if=\"!classObj.mobile\" :offset=\"classObj.openMenuTabs ? 8 : 15\" class=\"affix\">\n            <el-tooltip :content=\"menuStore.isCollapse ? $t('commons.button.expand') : $t('commons.button.collapse')\">\n                <el-button\n                    size=\"small\"\n                    circle\n                    :style=\"{ 'margin-left': menuStore.isCollapse ? '63px' : '168px', position: 'absolute' }\"\n                    :icon=\"menuStore.isCollapse ? 'ArrowRight' : 'ArrowLeft'\"\n                    plain\n                    @click=\"handleCollapse()\"\n                ></el-button>\n            </el-tooltip>\n        </el-affix>\n        <div class=\"app-sidebar\" v-if=\"!globalStore.isFullScreen\">\n            <Sidebar @menu-click=\"handleMenuClick\" :menu-router=\"!classObj.openMenuTabs\" @open-task=\"openTask\" />\n        </div>\n\n        <el-watermark\n            v-if=\"globalStore.isMasterProductPro && globalStore.watermarkShow && globalStore.watermark\"\n            :content=\"loadContent()\"\n            :font=\"{\n                fontSize: globalStore.watermark.fontSize,\n                color: globalStore.isDarkTheme ? globalStore.watermark.darkColor : globalStore.watermark.lightColor,\n                textBaseline: 'top',\n            }\"\n            :rotate=\"globalStore.watermark.rotate\"\n            :gap=\"[globalStore.watermark.gap, globalStore.watermark.gap]\"\n        >\n            <div class=\"main-container\">\n                <mobile-header v-if=\"classObj.mobile\" />\n                <Tabs v-if=\"classObj.openMenuTabs\" />\n                <app-main :keep-alive=\"classObj.openMenuTabs ? tabsStore.cachedTabs : null\" class=\"app-main\" />\n                <Footer class=\"app-footer\" v-if=\"!globalStore.isFullScreen\" />\n            </div>\n        </el-watermark>\n        <div class=\"main-container\" v-else>\n            <mobile-header v-if=\"classObj.mobile\" />\n            <Tabs v-if=\"classObj.openMenuTabs\" />\n            <app-main :keep-alive=\"classObj.openMenuTabs ? tabsStore.cachedTabs : null\" class=\"app-main\" />\n            <Footer class=\"app-footer\" v-if=\"!globalStore.isFullScreen\" />\n        </div>\n        <TaskList ref=\"taskListRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, computed, ref, watch, onBeforeUnmount } from 'vue';\nimport { Sidebar, Footer, AppMain, MobileHeader, Tabs } from './components';\nimport useResize from './hooks/useResize';\nimport { GlobalStore, MenuStore, TabsStore } from '@/store';\nimport { DeviceType } from '@/enums/app';\nimport { getSystemAvailable } from '@/api/modules/setting';\nimport { useRoute, useRouter } from 'vue-router';\nimport { loadMasterProductProFromDB, loadProductProFromDB } from '@/utils/xpack';\nimport { useTheme } from '@/global/use-theme';\nimport TaskList from '@/components/task-list/index.vue';\nimport i18n from '@/lang';\nconst { switchTheme } = useTheme();\n\nuseResize();\n\nconst taskListRef = ref();\nconst openTask = () => {\n    taskListRef.value.acceptParams();\n    if (globalStore.isMobile()) {\n        menuStore.setCollapse();\n    }\n};\nconst router = useRouter();\nconst route = useRoute();\nconst menuStore = MenuStore();\nconst globalStore = GlobalStore();\nconst tabsStore = TabsStore();\n\nconst loading = ref(false);\nconst loadingText = computed(() =>\n    globalStore.loadingText ? i18n.global.t(`commons.loadingText.${globalStore.loadingText}`) : '',\n);\n\nlet timer: NodeJS.Timer | null = null;\n\nconst classObj = computed(() => {\n    return {\n        fullScreen: globalStore.isFullScreen,\n        hideSidebar: menuStore.isCollapse,\n        openSidebar: !menuStore.isCollapse,\n        mobile: globalStore.device === DeviceType.Mobile,\n        openMenuTabs: globalStore.openMenuTabs,\n        withoutAnimation: menuStore.withoutAnimation,\n    };\n});\nconst handleClickOutside = () => {\n    menuStore.closeSidebar(false);\n};\n\nconst handleCollapse = () => {\n    menuStore.setCollapse();\n};\n\nconst loadContent = () => {\n    let itemName = globalStore.watermark.content.replaceAll(\n        '${nodeName}',\n        globalStore.currentNode === 'local' ? globalStore.getMasterAlias() : globalStore.currentNode,\n    );\n    itemName = itemName.replaceAll('${nodeAddr}', globalStore.currentNodeAddr || '127.0.0.1');\n    return itemName;\n};\n\nwatch(\n    () => globalStore.isLoading,\n    () => {\n        if (globalStore.isLoading) {\n            loadStatus();\n        } else {\n            loading.value = globalStore.isLoading;\n        }\n    },\n);\nconst handleMenuClick = async (path) => {\n    await router.push({ path: path });\n    tabsStore.addTab(route);\n    tabsStore.activeTabPath = route.path;\n};\n\nconst toLogin = () => {\n    let baseUrl = window.location.origin;\n    let newUrl = '';\n    if (globalStore.entrance) {\n        newUrl = baseUrl + '/' + globalStore.entrance;\n    } else {\n        newUrl = baseUrl + '/login';\n    }\n    window.open(newUrl, '_self');\n};\n\nconst loadStatus = async () => {\n    loading.value = globalStore.isLoading;\n    if (loading.value) {\n        timer = setInterval(async () => {\n            await getSystemAvailable()\n                .then((res) => {\n                    if (res) {\n                        toLogin();\n                        clearInterval(Number(timer));\n                        timer = null;\n                    }\n                })\n                .catch(() => {\n                    toLogin();\n                    clearInterval(Number(timer));\n                    timer = null;\n                });\n        }, 1000 * 10);\n    }\n};\nonBeforeUnmount(() => {\n    clearInterval(Number(timer));\n    timer = null;\n});\nonMounted(() => {\n    if (globalStore.openMenuTabs && !tabsStore.activeTabPath) {\n        handleMenuClick('/');\n    }\n\n    loadStatus();\n    loadProductProFromDB();\n    loadMasterProductProFromDB();\n    globalStore.isFullScreen = false;\n\n    const mqList = window.matchMedia('(prefers-color-scheme: dark)');\n    if (mqList.addEventListener) {\n        mqList.addEventListener('change', () => {\n            switchTheme();\n        });\n    } else if (mqList.addListener) {\n        mqList.addListener(() => {\n            switchTheme();\n        });\n    }\n});\n</script>\n\n<style scoped lang=\"scss\">\n.app-wrapper {\n    position: relative;\n    width: 100%;\n}\n\n.drawer-bg {\n    background-color: #000;\n    opacity: 0.3;\n    width: 100%;\n    top: 0;\n    height: 100%;\n    position: absolute;\n    z-index: 999;\n}\n\n.main-container {\n    display: flex;\n    flex-direction: column;\n    position: relative;\n    height: 100vh;\n    transition: margin-left 0.3s;\n    margin-left: var(--panel-menu-width);\n    background-color: var(--panel-main-bg-color-9);\n    overflow-x: hidden;\n}\n.app-main {\n    padding: 7px 20px;\n    flex: 1;\n    overflow: auto;\n}\n.app-sidebar {\n    z-index: 2;\n    transition: width 0.3s;\n    width: var(--panel-menu-width) !important;\n    position: fixed;\n    font-size: 0px;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    overflow: hidden;\n    .affix {\n        z-index: 5;\n    }\n}\n\n.hideSidebar {\n    .main-container {\n        margin-left: var(--panel-menu-hide-width);\n    }\n    .app-sidebar {\n        width: var(--panel-menu-hide-width) !important;\n    }\n    .fixed-header {\n        width: calc(100% - var(--panel-menu-hide-width));\n    }\n}\n\n.fullScreen {\n    .main-container {\n        margin-left: 0px;\n    }\n}\n// for mobile response 适配移动端\n.mobile {\n    .main-container {\n        margin-left: 0px;\n    }\n    .app-sidebar {\n        transition: transform 0.3s;\n        width: var(--panel-menu-width) !important;\n        background: #ffffff;\n        z-index: 9999;\n    }\n    .app-footer {\n        display: block;\n        text-align: center;\n    }\n    &.openSidebar {\n        position: fixed;\n        top: 0;\n    }\n    &.hideSidebar {\n        .app-sidebar {\n            pointer-events: none;\n            transition-duration: 0.3s;\n            transform: translate3d(calc(0px - var(--panel-menu-width)), 0, 0);\n        }\n    }\n}\n\n.withoutAnimation {\n    .main-container,\n    .sidebar-container {\n        transition: none;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/main.ts",
    "content": "import { createApp } from 'vue';\nimport App from './App.vue';\n\nimport '@/styles/index.scss';\nimport '@/styles/common.scss';\nimport '@/assets/iconfont/iconfont.css';\nimport '@/assets/iconfont/iconfont.js';\nimport '@/styles/style.css';\n\nconst styleModule = import.meta.glob('xpack/styles/index.scss');\nfor (const path in styleModule) {\n    styleModule[path]?.();\n}\n\nimport router from '@/routers/index';\nimport i18n, { ensureFallbackLocale, loadLocaleMessages } from '@/lang/index';\nimport pinia from '@/store/index';\nimport SvgIcon from './components/svg-icon/svg-icon.vue';\nimport Components from '@/components';\n\nimport ElementPlus from 'element-plus';\nimport Fit2CloudPlus from 'fit2cloud-ui-plus';\nimport * as Icons from '@element-plus/icons-vue';\n\nimport directives from '@/directives/index';\n\nconst bootstrap = async () => {\n    const currentLocale = i18n.global.locale.value;\n\n    await Promise.all([loadLocaleMessages(currentLocale), ensureFallbackLocale()]);\n\n    const app = createApp(App);\n    app.component('SvgIcon', SvgIcon);\n    app.use(ElementPlus);\n    app.use(Fit2CloudPlus, { locale: i18n.global.getLocaleMessage(currentLocale) });\n\n    Object.keys(Icons).forEach((key) => {\n        app.component(key, Icons[key as keyof typeof Icons]);\n    });\n\n    app.use(router);\n    app.use(i18n);\n    app.use(pinia);\n    app.use(Components);\n    app.use(directives);\n\n    app.mount('#app');\n};\n\nbootstrap();\n"
  },
  {
    "path": "frontend/src/routers/cache-router.ts",
    "content": "import { RouteRecordRaw, RouteRecordName } from 'vue-router';\nimport { routerArray } from '@/routers/router';\n\n/**\n * @description 使用递归，过滤需要缓存的路由\n * @param {Array} _route 所有路由表\n * @param {Array} _cache 缓存的路由表\n * @return void\n * */\nlet cacheRouter: RouteRecordName[] = [];\nconst filterKeepAlive = (_route: RouteRecordRaw[], _cache: RouteRecordName[]): void => {\n    _route.forEach((item) => {\n        item.meta?.keepAlive && item.name && _cache.push(item.name);\n        item?.children?.length && filterKeepAlive(item.children, _cache);\n    });\n};\n\nfilterKeepAlive(routerArray, cacheRouter);\n\nexport default cacheRouter;\n"
  },
  {
    "path": "frontend/src/routers/constant.ts",
    "content": "/**\n * @description: default layout\n */\nexport const Layout = () => import('@/layout/index.vue');\n"
  },
  {
    "path": "frontend/src/routers/index.ts",
    "content": "import router from '@/routers/router';\nimport NProgress from '@/config/nprogress';\nimport { GlobalStore } from '@/store';\nimport { AxiosCanceler } from '@/api/helper/axios-cancel';\n\nconst axiosCanceler = new AxiosCanceler();\n\nlet isRedirecting = false;\n\nrouter.beforeEach((to, from, next) => {\n    NProgress.start();\n    axiosCanceler.removeAllPending();\n    const globalStore = GlobalStore();\n    if (to.name !== 'entrance' && !globalStore.isLogin) {\n        next({\n            name: 'entrance',\n            params: to.params,\n        });\n        NProgress.done();\n        return;\n    }\n    if (to.name === 'entrance' && globalStore.isLogin) {\n        if (to.params.code === globalStore.entrance) {\n            next({\n                name: 'home',\n            });\n            NProgress.done();\n            return;\n        }\n        next({ name: '404' });\n        NProgress.done();\n        return;\n    }\n\n    if (to.path === '/apps/all' && to.query.install != undefined) {\n        return next();\n    }\n    const activeMenuKey = 'cachedRoute' + (to.meta.activeMenu || '');\n    if (to.query.uncached != undefined) {\n        const query = { ...to.query };\n        delete query.uncached;\n        localStorage.removeItem(activeMenuKey);\n        return next({ path: to.path, query });\n    }\n\n    const cachedRoute = localStorage.getItem(activeMenuKey);\n    if (\n        to.meta.activeMenu &&\n        to.meta.activeMenu != from.meta.activeMenu &&\n        cachedRoute &&\n        cachedRoute !== to.path &&\n        !isRedirecting\n    ) {\n        isRedirecting = true;\n        next(cachedRoute);\n        NProgress.done();\n        return;\n    }\n\n    if (!to.matched.some((record) => record.meta.requiresAuth)) return next();\n\n    return next();\n});\n\nrouter.afterEach((to) => {\n    if (to.meta.activeMenu && !isRedirecting) {\n        let notMathParam = true;\n        if (to.matched.some((record) => record.path.includes(':'))) {\n            notMathParam = false;\n        }\n        if (notMathParam) {\n            if (to.meta.activeMenu === '/cronjobs' && to.path === '/cronjobs/cronjob/operate') {\n                localStorage.setItem('cachedRoute' + to.meta.activeMenu, '/cronjobs/cronjob');\n            } else if (to.meta.activeMenu === '/containers' && to.path === '/containers/container/operate') {\n                localStorage.setItem('cachedRoute' + to.meta.activeMenu, '/containers/container');\n            } else if (to.meta.activeMenu === '/toolbox' && to.path === '/toolbox/clam/setting') {\n                localStorage.setItem('cachedRoute' + to.meta.activeMenu, '/toolbox/clam');\n            } else {\n                localStorage.setItem('cachedRoute' + to.meta.activeMenu, to.path);\n            }\n        }\n    }\n\n    isRedirecting = false;\n    NProgress.done();\n});\n\nexport default router;\n"
  },
  {
    "path": "frontend/src/routers/modules/ai.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst vllmModules = import.meta.glob('@/xpack/views/vllm/index.vue');\nconst loadVllmView = () => {\n    const loader = vllmModules['/src/xpack/views/vllm/index.vue'];\n    if (loader) {\n        return loader();\n    }\n    return import('@/views/ai/model/vllm/index.vue');\n};\n\nconst databaseRouter = {\n    sort: 4,\n    path: '/ai',\n    name: 'AI-Menu',\n    component: Layout,\n    redirect: '/ai/model',\n    meta: {\n        icon: 'p-jiqiren2',\n        title: 'menu.aiTools',\n    },\n    children: [\n        {\n            path: '/ai/agents/agent',\n            name: 'Agents',\n            component: () => import('@/views/ai/agents/agent/index.vue'),\n            meta: {\n                icon: 'p-jiqiren2',\n                title: 'aiTools.agents.agents',\n                requiresAuth: true,\n            },\n        },\n        {\n            path: '/ai/agents/model',\n            name: 'AgentsModel',\n            component: () => import('@/views/ai/agents/model/index.vue'),\n            meta: {\n                title: 'aiTools.agents.account',\n                activeMenu: '/ai/agents/agent',\n                requiresAuth: true,\n            },\n        },\n        {\n            path: '/ai/model/ollama',\n            name: 'OllamaModel',\n            component: () => import('@/views/ai/model/ollama/index.vue'),\n            meta: {\n                icon: 'p-moxing-menu',\n                title: 'aiTools.model.localModel',\n                activeMenu: '/ai/model/ollama',\n                requiresAuth: true,\n            },\n        },\n        {\n            path: '/ai/mcp',\n            name: 'MCPServer',\n            component: () => import('@/views/ai/mcp/server/index.vue'),\n            meta: {\n                icon: 'p-mcp-menu',\n                title: 'menu.mcp',\n                requiresAuth: true,\n            },\n        },\n        {\n            path: '/ai/model/tensorrt',\n            hidden: true,\n            name: 'TensorRTLLm',\n            component: () => import('@/views/ai/model/tensorrt/index.vue'),\n            meta: {\n                title: 'aiTools.tensorRT.llm',\n                activeMenu: '/ai/model/ollama',\n                requiresAuth: true,\n            },\n        },\n        {\n            path: '/ai/model/vllm',\n            hidden: true,\n            name: 'Vllm',\n            component: loadVllmView,\n            meta: {\n                title: 'vLLM',\n                activeMenu: '/ai/model/ollama',\n                requiresAuth: true,\n            },\n        },\n        {\n            path: '/ai/gpu/current',\n            name: 'GPU',\n            component: () => import('@/views/ai/gpu/current/index.vue'),\n            meta: {\n                icon: 'p-gpu-menu',\n                title: 'aiTools.gpu.gpu',\n                activeMenu: '/ai/gpu/current',\n                requiresAuth: true,\n            },\n        },\n        {\n            path: '/ai/gpu/history',\n            name: 'GPUHistory',\n            component: () => import('@/views/ai/gpu/history/index.vue'),\n            meta: {\n                title: 'aiTools.gpu.history',\n                activeMenu: '/ai/gpu/current',\n                requiresAuth: true,\n            },\n        },\n    ],\n};\n\nexport default databaseRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/app-store.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst appStoreRouter = {\n    sort: 2,\n    path: '/apps',\n    name: 'App-Menu',\n    component: Layout,\n    redirect: '/apps/all',\n    meta: {\n        icon: 'p-appstore',\n        title: 'menu.apps',\n    },\n    children: [\n        {\n            path: '/apps',\n            name: 'App',\n            redirect: '/apps/all',\n            component: () => import('@/views/app-store/index.vue'),\n            meta: {},\n            children: [\n                {\n                    path: 'all',\n                    name: 'AppAll',\n                    component: () => import('@/views/app-store/apps/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/apps',\n                        requiresAuth: false,\n                        parent: 'menu.app',\n                        title: 'app.all',\n                    },\n                },\n                {\n                    path: 'installed',\n                    name: 'AppInstalled',\n                    component: () => import('@/views/app-store/installed/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/apps',\n                        requiresAuth: false,\n                        parent: 'menu.app',\n                        title: 'app.installed',\n                    },\n                },\n                {\n                    path: 'upgrade',\n                    name: 'AppUpgrade',\n                    component: () => import('@/views/app-store/installed/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/apps',\n                        requiresAuth: false,\n                        parent: 'menu.app',\n                        title: 'app.canUpgrade',\n                    },\n                },\n                {\n                    path: 'setting',\n                    name: 'AppStoreSetting',\n                    component: () => import('@/views/app-store/setting/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/apps',\n                        requiresAuth: false,\n                        parent: 'menu.app',\n                        title: 'commons.button.set',\n                    },\n                },\n            ],\n        },\n    ],\n};\n\nexport default appStoreRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/container.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst containerRouter = {\n    sort: 6,\n    path: '/containers',\n    name: 'Container-Menu',\n    component: Layout,\n    redirect: '/containers/container',\n    meta: {\n        icon: 'p-docker1',\n        title: 'menu.container',\n    },\n    children: [\n        {\n            path: '/containers',\n            name: 'Container',\n            redirect: '/containers/dashboard',\n            component: () => import('@/views/container/index.vue'),\n            meta: {},\n            children: [\n                {\n                    path: 'dashboard',\n                    name: 'ContainerDashboard',\n                    component: () => import('@/views/container/dashboard/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'menu.home',\n                    },\n                },\n                {\n                    path: 'container',\n                    name: 'ContainerItem',\n                    component: () => import('@/views/container/container/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'menu.container',\n                    },\n                },\n                {\n                    path: 'container/operate',\n                    name: 'ContainerCreate',\n                    component: () => import('@/views/container/container/operate/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        ignoreTab: true,\n                    },\n                },\n                {\n                    path: 'image',\n                    name: 'Image',\n                    component: () => import('@/views/container/image/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'container.image',\n                    },\n                },\n                {\n                    path: 'network',\n                    name: 'Network',\n                    component: () => import('@/views/container/network/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'container.network',\n                    },\n                },\n                {\n                    path: 'volume',\n                    name: 'Volume',\n                    component: () => import('@/views/container/volume/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'container.volume',\n                    },\n                },\n                {\n                    path: 'repo',\n                    name: 'Repo',\n                    component: () => import('@/views/container/repo/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'container.repo',\n                    },\n                },\n                {\n                    path: 'compose',\n                    name: 'Compose',\n                    component: () => import('@/views/container/compose/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'container.compose',\n                    },\n                },\n                {\n                    path: 'template',\n                    name: 'ComposeTemplate',\n                    component: () => import('@/views/container/template/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'container.composeTemplate',\n                    },\n                },\n                {\n                    path: 'setting',\n                    name: 'ContainerSetting',\n                    component: () => import('@/views/container/setting/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/containers',\n                        requiresAuth: false,\n                        parent: 'menu.container',\n                        title: 'container.setting',\n                    },\n                },\n            ],\n        },\n    ],\n};\n\nexport default containerRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/cronjob.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst cronRouter = {\n    sort: 9,\n    path: '/cronjobs',\n    name: 'Cronjob-Menu',\n    component: Layout,\n    redirect: '/cronjobs/cronjob',\n    meta: {\n        icon: 'p-plan',\n        title: 'menu.cronjob',\n    },\n    children: [\n        {\n            path: '/cronjobs',\n            name: 'Cronjob',\n            redirect: '/cronjobs/cronjob',\n            component: () => import('@/views/cronjob/index.vue'),\n            meta: {},\n            children: [\n                {\n                    path: 'cronjob',\n                    name: 'CronjobItem',\n                    component: () => import('@/views/cronjob/cronjob/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/cronjobs',\n                        requiresAuth: false,\n                        title: 'menu.cronjob',\n                    },\n                },\n                {\n                    path: 'cronjob/operate',\n                    name: 'CronjobOperate',\n                    component: () => import('@/views/cronjob/cronjob/operate/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/cronjobs',\n                        requiresAuth: false,\n                        ignoreTab: true,\n                    },\n                },\n                {\n                    path: 'library',\n                    name: 'Library',\n                    component: () => import('@/views/cronjob/library/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/cronjobs',\n                        requiresAuth: false,\n                        title: 'cronjob.library.library',\n                    },\n                },\n            ],\n        },\n    ],\n};\n\nexport default cronRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/database.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst databaseRouter = {\n    sort: 5,\n    path: '/databases',\n    name: 'Database-Menu',\n    component: Layout,\n    redirect: '/databases/mysql',\n    meta: {\n        icon: 'p-database',\n        title: 'menu.database',\n    },\n    children: [\n        {\n            path: '/databases',\n            name: 'Database',\n            redirect: '/databases/mysql',\n            component: () => import('@/views/database/index.vue'),\n            meta: {},\n            children: [\n                {\n                    path: 'mysql',\n                    name: 'MySQL',\n                    component: () => import('@/views/database/mysql/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/databases',\n                        requiresAuth: false,\n                        parent: 'menu.database',\n                        title: 'MySQL',\n                    },\n                },\n                {\n                    path: 'mysql/setting/:type/:database',\n                    name: 'MySQL-Setting',\n                    component: () => import('@/views/database/mysql/setting/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/databases',\n                        requiresAuth: false,\n                        ignoreTab: true,\n                    },\n                },\n                {\n                    path: 'mysql/remote',\n                    name: 'MySQL-Remote',\n                    component: () => import('@/views/database/mysql/remote/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/databases',\n                        requiresAuth: false,\n                        parent: 'menu.database',\n                        title: 'MySQL',\n                        detail: 'database.remote',\n                    },\n                },\n                {\n                    path: 'postgresql',\n                    name: 'PostgreSQL',\n                    component: () => import('@/views/database/postgresql/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/databases',\n                        requiresAuth: false,\n                        parent: 'menu.database',\n                        title: 'PostgreSQL',\n                    },\n                },\n                {\n                    path: 'postgresql/remote',\n                    name: 'PostgreSQL-Remote',\n                    component: () => import('@/views/database/postgresql/remote/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/databases',\n                        requiresAuth: false,\n                        parent: 'menu.database',\n                        title: 'PostgreSQL',\n                        detail: 'database.remote',\n                    },\n                },\n                {\n                    path: 'postgresql/setting/:type/:database',\n                    name: 'PostgreSQL-Setting',\n                    component: () => import('@/views/database/postgresql/setting/index.vue'),\n                    props: true,\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/databases',\n                        requiresAuth: false,\n                        ignoreTab: true,\n                    },\n                },\n                {\n                    path: 'redis',\n                    name: 'Redis',\n                    component: () => import('@/views/database/redis/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/databases',\n                        requiresAuth: false,\n                        parent: 'menu.database',\n                        title: 'Redis',\n                    },\n                },\n                {\n                    path: 'redis/remote',\n                    name: 'Redis-Remote',\n                    component: () => import('@/views/database/redis/remote/index.vue'),\n                    hidden: true,\n                    meta: {\n                        activeMenu: '/databases',\n                        requiresAuth: false,\n                        parent: 'menu.database',\n                        title: 'Redis',\n                        detail: 'database.remote',\n                    },\n                },\n            ],\n        },\n    ],\n};\n\nexport default databaseRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/error.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst errorRouter = {\n    path: '/error',\n    component: Layout,\n    children: [\n        {\n            path: '404',\n            name: '404',\n            hidden: true,\n            component: () => import('@/components/error-message/404.vue'),\n            meta: {\n                requiresAuth: false,\n                title: '404页面',\n                key: '404',\n            },\n        },\n    ],\n};\nexport default errorRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/host.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst hostRouter = {\n    sort: 7,\n    path: '/hosts',\n    name: 'System-Menu',\n    component: Layout,\n    redirect: '/hosts/security',\n    meta: {\n        icon: 'p-host',\n        title: 'menu.system',\n    },\n    children: [\n        {\n            path: '/hosts/files',\n            name: 'File',\n            props: true,\n            component: () => import('@/views/host/file-management/index.vue'),\n            meta: {\n                icon: 'p-file-menu',\n                title: 'menu.files',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/monitor/monitor',\n            name: 'Monitorx',\n            component: () => import('@/views/host/monitor/monitor/index.vue'),\n            meta: {\n                icon: 'p-system-monitor-menu',\n                title: 'menu.monitor',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/monitor/setting',\n            name: 'HostMonitorSetting',\n            component: () => import('@/views/host/monitor/setting/index.vue'),\n            hidden: true,\n            meta: {\n                activeMenu: '/hosts/monitor/monitor',\n                title: 'menu.monitor',\n                detail: 'commons.button.set',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/firewall/port',\n            name: 'FirewallPort',\n            component: () => import('@/views/host/firewall/port/index.vue'),\n            meta: {\n                icon: 'p-firewalld-menu',\n                activeMenu: '/hosts/firewall/port',\n                title: 'menu.firewall',\n                detail: 'firewall.portRule',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/firewall/forward',\n            name: 'FirewallForward',\n            component: () => import('@/views/host/firewall/forward/index.vue'),\n            hidden: true,\n            meta: {\n                activeMenu: '/hosts/firewall/port',\n                parent: 'menu.firewall',\n                title: 'firewall.forwardRule',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/firewall/ip',\n            name: 'FirewallIP',\n            component: () => import('@/views/host/firewall/ip/index.vue'),\n            hidden: true,\n            meta: {\n                activeMenu: '/hosts/firewall/port',\n                parent: 'menu.firewall',\n                title: 'firewall.ipRule',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/firewall/advance',\n            name: 'FirewallAdvance',\n            component: () => import('@/views/host/firewall/advance/index.vue'),\n            hidden: true,\n            meta: {\n                activeMenu: '/hosts/firewall/port',\n                parent: 'menu.firewall',\n                title: 'firewall.advancedControl',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/disk',\n            name: 'Disk',\n            props: true,\n            component: () => import('@/views/host/disk-management/disk/index.vue'),\n            meta: {\n                icon: 'p-disk-menu',\n                title: 'menu.disk',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/process/process',\n            name: 'Process',\n            component: () => import('@/views/host/process/process/index.vue'),\n            meta: {\n                icon: 'p-process-menu',\n                title: 'menu.processManage',\n                detail: 'menu.process',\n                activeMenu: '/hosts/process/process',\n                keepAlive: true,\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/process/network',\n            name: 'ProcessNetwork',\n            hidden: true,\n            component: () => import('@/views/host/process/network/index.vue'),\n            meta: {\n                parent: 'menu.processManage',\n                title: 'menu.network',\n                activeMenu: '/hosts/process/process',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/ssh/ssh',\n            name: 'SSH',\n            component: () => import('@/views/host/ssh/ssh/index.vue'),\n            meta: {\n                icon: 'p-ssh-menu',\n                title: 'menu.ssh',\n                detail: 'menu.config',\n                activeMenu: '/hosts/ssh/ssh',\n                keepAlive: true,\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/ssh/log',\n            name: 'SSHLog',\n            component: () => import('@/views/host/ssh/log/index.vue'),\n            hidden: true,\n            meta: {\n                parent: 'menu.ssh',\n                title: 'ssh.loginLogs',\n                activeMenu: '/hosts/ssh/ssh',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/hosts/ssh/session',\n            name: 'SSHSession',\n            component: () => import('@/views/host/ssh/session/index.vue'),\n            hidden: true,\n            meta: {\n                parent: 'menu.ssh',\n                title: 'ssh.session',\n                activeMenu: '/hosts/ssh/ssh',\n                requiresAuth: false,\n            },\n        },\n    ],\n};\n\nexport default hostRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/log.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst logsRouter = {\n    sort: 11,\n    path: '/logs',\n    name: 'Log-Menu',\n    component: Layout,\n    redirect: '/logs/operation',\n    meta: {\n        title: 'menu.logs',\n        icon: 'p-log',\n    },\n    children: [\n        {\n            path: '/logs',\n            name: 'Log',\n            redirect: '/logs/operation',\n            component: () => import('@/views/log/index.vue'),\n            meta: {},\n            children: [\n                {\n                    path: 'operation',\n                    name: 'OperationLog',\n                    component: () => import('@/views/log/operation/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.logs',\n                        title: 'logs.operation',\n                        activeMenu: '/logs',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'login',\n                    name: 'LoginLog',\n                    component: () => import('@/views/log/login/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.logs',\n                        title: 'logs.login',\n                        activeMenu: '/logs',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'website',\n                    name: 'WebsiteLog',\n                    component: () => import('@/views/log/website/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.logs',\n                        title: 'logs.websiteLog',\n                        activeMenu: '/logs',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'system',\n                    name: 'SystemLog',\n                    component: () => import('@/views/log/system/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.logs',\n                        title: 'logs.system',\n                        activeMenu: '/logs',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'ssh',\n                    name: 'SSHLog2',\n                    component: () => import('@/views/host/ssh/log/log.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.logs',\n                        title: 'ssh.loginLogs',\n                        activeMenu: '/logs',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'task',\n                    name: 'Task',\n                    component: () => import('@/views/log/task/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.logs',\n                        title: 'logs.task',\n                        activeMenu: '/logs',\n                        requiresAuth: false,\n                    },\n                },\n            ],\n        },\n    ],\n};\n\nexport default logsRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/setting.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst settingRouter = {\n    sort: 12,\n    path: '/settings',\n    name: 'Setting-Menu',\n    component: Layout,\n    redirect: '/settings/panel',\n    meta: {\n        title: 'menu.settings',\n        icon: 'p-config',\n    },\n    children: [\n        {\n            path: '/settings',\n            name: 'Setting',\n            redirect: '/settings/panel',\n            component: () => import('@/views/setting/index.vue'),\n            meta: {},\n            children: [\n                {\n                    path: 'panel',\n                    name: 'Panel',\n                    component: () => import('@/views/setting/panel/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.settings',\n                        title: 'setting.panel',\n                        requiresAuth: true,\n                        activeMenu: '/settings',\n                    },\n                },\n                {\n                    path: 'alert',\n                    name: 'Alert',\n                    component: () => import('@/views/setting/alert/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.settings',\n                        title: 'xpack.alert.alertNotice',\n                        requiresAuth: true,\n                        activeMenu: '/settings',\n                    },\n                },\n                {\n                    path: 'backupaccount',\n                    name: 'BackupAccount',\n                    component: () => import('@/views/setting/backup-account/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.settings',\n                        title: 'setting.backupAccount',\n                        requiresAuth: true,\n                        activeMenu: '/settings',\n                    },\n                },\n                {\n                    path: 'license',\n                    name: 'License',\n                    component: () => import('@/views/setting/license/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.settings',\n                        title: 'setting.license',\n                        requiresAuth: true,\n                        activeMenu: '/settings',\n                    },\n                },\n                {\n                    path: 'about',\n                    name: 'About',\n                    component: () => import('@/views/setting/about/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.settings',\n                        title: 'setting.about',\n                        requiresAuth: true,\n                        activeMenu: '/settings',\n                    },\n                },\n                {\n                    path: 'safe',\n                    name: 'Safe',\n                    component: () => import('@/views/setting/safe/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.settings',\n                        title: 'setting.safe',\n                        requiresAuth: true,\n                        activeMenu: '/settings',\n                    },\n                },\n                {\n                    path: 'snapshot',\n                    name: 'Snapshot',\n                    hidden: true,\n                    component: () => import('@/views/setting/snapshot/index.vue'),\n                    meta: {\n                        parent: 'menu.settings',\n                        title: 'setting.snapshot',\n                        requiresAuth: true,\n                        activeMenu: '/settings',\n                    },\n                },\n                {\n                    path: 'expired',\n                    name: 'Expired',\n                    hidden: true,\n                    component: () => import('@/views/setting/expired.vue'),\n                    meta: {\n                        requiresAuth: true,\n                        activeMenu: 'Expired',\n                        ignoreTab: true,\n                    },\n                },\n            ],\n        },\n    ],\n};\n\nexport default settingRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/terminal.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst terminalRouter = {\n    sort: 8,\n    path: '/terminal',\n    name: 'Terminal-Menu',\n    component: Layout,\n    redirect: '/terminal',\n    meta: {\n        title: 'menu.terminal',\n        icon: 'p-terminal2',\n    },\n    children: [\n        {\n            path: '/terminal',\n            name: 'Terminal',\n            props: true,\n            component: () => import('@/views/terminal/index.vue'),\n            meta: {\n                keepAlive: true,\n                requiresAuth: false,\n            },\n        },\n    ],\n};\n\nexport default terminalRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/toolbox.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst toolboxRouter = {\n    sort: 9,\n    path: '/toolbox',\n    name: 'Toolbox-Menu',\n    component: Layout,\n    redirect: '/toolbox/supervisor',\n    meta: {\n        title: 'menu.toolbox',\n        icon: 'p-toolbox',\n    },\n    children: [\n        {\n            path: '/toolbox',\n            name: 'Toolbox',\n            redirect: '/toolbox/device',\n            component: () => import('@/views/toolbox/index.vue'),\n            meta: {},\n            children: [\n                {\n                    path: 'device',\n                    name: 'Device',\n                    component: () => import('@/views/toolbox/device/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.toolbox',\n                        title: 'toolbox.device.toolbox',\n                        activeMenu: '/toolbox',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'supervisor',\n                    name: 'Supervisor',\n                    component: () => import('@/views/toolbox/supervisor/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.toolbox',\n                        title: 'menu.supervisor',\n                        activeMenu: '/toolbox',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'clam',\n                    name: 'Clam',\n                    component: () => import('@/views/toolbox/clam/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.toolbox',\n                        title: 'toolbox.clam.clam',\n                        activeMenu: '/toolbox',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'clam/setting',\n                    name: 'Clam-Setting',\n                    component: () => import('@/views/toolbox/clam/setting/index.vue'),\n                    hidden: true,\n                    meta: {\n                        ignoreTab: true,\n                        activeMenu: '/toolbox',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'ftp',\n                    name: 'FTP',\n                    component: () => import('@/views/toolbox/ftp/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.toolbox',\n                        title: 'FTP',\n                        activeMenu: '/toolbox',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'fail2ban',\n                    name: 'Fail2ban',\n                    component: () => import('@/views/toolbox/fail2ban/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.toolbox',\n                        title: 'Fail2Ban',\n                        activeMenu: '/toolbox',\n                        requiresAuth: false,\n                    },\n                },\n                {\n                    path: 'clean',\n                    name: 'Clean',\n                    component: () => import('@/views/toolbox/clean/index.vue'),\n                    hidden: true,\n                    meta: {\n                        parent: 'menu.toolbox',\n                        title: 'setting.diskClean',\n                        activeMenu: '/toolbox',\n                        requiresAuth: false,\n                    },\n                },\n            ],\n        },\n    ],\n};\n\nexport default toolboxRouter;\n"
  },
  {
    "path": "frontend/src/routers/modules/website.ts",
    "content": "import { Layout } from '@/routers/constant';\n\nconst webSiteRouter = {\n    sort: 3,\n    path: '/websites',\n    name: 'Website-Menu',\n    component: Layout,\n    redirect: '/websites',\n    meta: {\n        icon: 'p-website',\n        title: 'menu.website',\n    },\n    children: [\n        {\n            path: '/websites',\n            name: 'Website',\n            component: () => import('@/views/website/website/index.vue'),\n            meta: {\n                icon: 'p-website',\n                title: 'menu.website',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/websites/:id/config/:tab',\n            name: 'WebsiteConfig',\n            component: () => import('@/views/website/website/config/index.vue'),\n            hidden: true,\n            props: true,\n            meta: {\n                activeMenu: '/websites',\n                requiresAuth: false,\n                ignoreTab: true,\n            },\n        },\n        {\n            path: '/websites/ssl',\n            name: 'SSL',\n            component: () => import('@/views/website/ssl/index.vue'),\n            meta: {\n                icon: 'p-ssl-menu',\n                title: 'menu.ssl',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/websites/runtimes/php',\n            name: 'PHP',\n            component: () => import('@/views/website/runtime/php/index.vue'),\n            meta: {\n                icon: 'p-run-menu',\n                title: 'menu.runtime',\n                detail: 'PHP',\n                activeMenu: '/websites/runtimes/php',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/websites/runtimes/node',\n            name: 'node',\n            hidden: true,\n            component: () => import('@/views/website/runtime/node/index.vue'),\n            meta: {\n                title: 'menu.runtime',\n                detail: 'Node',\n                activeMenu: '/websites/runtimes/php',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/websites/runtimes/java',\n            name: 'java',\n            hidden: true,\n            component: () => import('@/views/website/runtime/java/index.vue'),\n            meta: {\n                title: 'menu.runtime',\n                detail: 'Java',\n                activeMenu: '/websites/runtimes/php',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/websites/runtimes/go',\n            name: 'go',\n            hidden: true,\n            component: () => import('@/views/website/runtime/go/index.vue'),\n            meta: {\n                title: 'menu.runtime',\n                detail: 'Go',\n                activeMenu: '/websites/runtimes/php',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/websites/runtimes/python',\n            name: 'python',\n            hidden: true,\n            component: () => import('@/views/website/runtime/python/index.vue'),\n            meta: {\n                title: 'menu.runtime',\n                detail: 'Python',\n                activeMenu: '/websites/runtimes/php',\n                requiresAuth: false,\n            },\n        },\n        {\n            path: '/websites/runtimes/dotnet',\n            name: 'dotNet',\n            hidden: true,\n            component: () => import('@/views/website/runtime/dotnet/index.vue'),\n            meta: {\n                title: 'menu.runtime',\n                detail: '.Net',\n                activeMenu: '/websites/runtimes/php',\n                requiresAuth: false,\n            },\n        },\n    ],\n};\n\nexport default webSiteRouter;\n"
  },
  {
    "path": "frontend/src/routers/router.ts",
    "content": "import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';\nimport { Layout } from '@/routers/constant';\n\nlet modules: Record<string, RouteRecordRaw> = import.meta.glob('./modules/*.ts', { eager: true });\nconst xpackModules: Record<string, RouteRecordRaw> = import.meta.glob('../xpack/routers/*.ts', { eager: true });\nmodules = { ...modules, ...xpackModules };\n\nconst homeRouter: RouteRecordRaw = {\n    path: '/',\n    name: 'Home-Menu',\n    component: Layout,\n    redirect: '/',\n    meta: {\n        keepAlive: true,\n        title: 'menu.home',\n        icon: 'p-home',\n    },\n    children: [\n        {\n            path: '/',\n            name: 'home',\n            component: () => import('@/views/home/index.vue'),\n            meta: {\n                requiresAuth: true,\n            },\n        },\n    ],\n};\n\nexport const routerArray: RouteRecordRaw[] = [];\n\nexport const rolesRoutes = [\n    ...Object.keys(modules)\n        .map((key) => modules[key]['default'])\n        .sort((r1, r2) => {\n            r1.sort ??= Number.MAX_VALUE;\n            r2.sort ??= Number.MAX_VALUE;\n            return r1.sort - r2.sort;\n        }),\n];\n\nrolesRoutes.forEach((item) => {\n    const menu = item as RouteRecordRaw;\n    routerArray.push(menu);\n});\n\nexport const menuList: RouteRecordRaw[] = [];\nrolesRoutes.forEach((item) => {\n    let menuItem = JSON.parse(JSON.stringify(item));\n    let menuChildren: RouteRecordRaw[] = [];\n    if (menuItem.children == undefined) {\n        return;\n    }\n    menuItem.children.forEach((child: any) => {\n        if (child.hidden == undefined || child.hidden == false) {\n            menuChildren.push(child);\n        }\n    });\n    menuItem.children = menuChildren as RouteRecordRaw[];\n    menuList.push(menuItem);\n});\nmenuList.unshift(homeRouter);\n\nexport const routes: RouteRecordRaw[] = [\n    homeRouter,\n    {\n        path: '/login',\n        name: 'login',\n        props: true,\n        component: () => import('@/views/login/index.vue'),\n        meta: {\n            requiresAuth: false,\n            key: 'login',\n        },\n    },\n    {\n        path: '/:code?',\n        name: 'entrance',\n        component: () => import('@/views/login/index.vue'),\n        props: true,\n    },\n    ...routerArray,\n    {\n        path: '/:pathMatch(.*)',\n        redirect: { name: '404' },\n    },\n];\nconst router = createRouter({\n    history: createWebHistory('/'),\n    routes: routes as RouteRecordRaw[],\n    strict: false,\n    scrollBehavior: () => ({ left: 0, top: 0 }),\n});\n\nexport default router;\n"
  },
  {
    "path": "frontend/src/store/index.ts",
    "content": "import { createPinia } from 'pinia';\nimport piniaPluginPersistedstate from 'pinia-plugin-persistedstate';\nimport GlobalStore from './modules/global';\nimport MenuStore from './modules/menu';\nimport TabsStore from './modules/tabs';\nimport TerminalStore from './modules/terminal';\nimport ProcessStore from './modules/process';\n\nconst pinia = createPinia();\npinia.use(piniaPluginPersistedstate);\n\nexport { GlobalStore, MenuStore, TabsStore, TerminalStore, ProcessStore };\n\nexport default pinia;\n"
  },
  {
    "path": "frontend/src/store/interface/index.ts",
    "content": "import { RouteRecordRaw } from 'vue-router';\nimport { DeviceType } from '@/enums/app';\nexport interface ThemeConfigProp {\n    panelName: string;\n    primary: string;\n    theme: string; // dark | bright ｜ auto\n    footer: boolean;\n\n    title: string;\n    logo: string;\n    logoWithText: string;\n    favicon: string;\n    loginImage: string;\n    loginBgType: string;\n    loginBackground: string;\n    loginBtnLinkColor: string;\n    themeColor: string;\n}\n\nexport interface Watermark {\n    lightColor: string;\n    darkColor: string;\n    fontSize: number;\n    content: string;\n    rotate: number;\n    gap: number;\n}\n\nexport interface GlobalState {\n    isLoading: boolean;\n    loadingText: string;\n    isLogin: boolean;\n    entrance: string;\n    language: string; // zh | en | tw\n    themeConfig: ThemeConfigProp;\n    isFullScreen: boolean;\n    openMenuTabs: boolean;\n    watermark: Watermark;\n    watermarkShow: boolean;\n    isOnRestart: boolean;\n    agreeLicense: boolean;\n    hasNewVersion: boolean;\n    ignoreCaptcha: boolean;\n    device: DeviceType;\n    lastFilePath: string;\n    currentDB: string;\n    currentPgDB: string;\n    currentRedisDB: string;\n    showEntranceWarn: boolean;\n    defaultNetwork: string;\n    defaultIO: string;\n    isFxplay: boolean;\n\n    isProductPro: boolean;\n    isIntl: boolean;\n    docWithRegion: boolean;\n    productProExpires: number;\n    isMasterProductPro: boolean;\n    isOffLine: boolean;\n\n    masterAlias: string;\n    currentNode: string;\n    currentNodeAddr: string;\n}\n\nexport interface MenuState {\n    isCollapse: boolean;\n    menuList: RouteRecordRaw[];\n    withoutAnimation: boolean;\n}\n\nexport interface TerminalState {\n    lineHeight: number;\n    letterSpacing: number;\n    fontSize: number;\n    fontFamily: string;\n    backgroundColor: string;\n    foregroundColor: string;\n    cursorBlink: string;\n    cursorStyle: string;\n    scrollback: number;\n    scrollSensitivity: number;\n}\n"
  },
  {
    "path": "frontend/src/store/modules/global.ts",
    "content": "import { defineStore } from 'pinia';\nimport piniaPersistConfig from '@/config/pinia-persist';\nimport { GlobalState, ThemeConfigProp } from '../interface';\nimport { DeviceType } from '@/enums/app';\nimport i18n, { setActiveLocale } from '@/lang';\n\nconst CN_DOCS_URL = 'https://1panel.cn/docs/v2';\nconst INTL_DOCS_URL = 'https://docs.1panel.pro/v2';\n\nconst GlobalStore = defineStore({\n    id: 'GlobalState',\n    state: (): GlobalState => ({\n        isLoading: false,\n        loadingText: '',\n        isLogin: false,\n        entrance: '',\n        language: '',\n        themeConfig: {\n            panelName: '',\n            primary: '#005eeb',\n            theme: 'auto',\n            footer: true,\n            themeColor: '',\n            title: '',\n            logo: '',\n            logoWithText: '',\n            favicon: '',\n            loginImage: '',\n            loginBackground: '',\n            loginBgType: '',\n            loginBtnLinkColor: '',\n        },\n        watermark: null,\n        watermarkShow: false,\n        openMenuTabs: false,\n        isFullScreen: false,\n        isOnRestart: false,\n        agreeLicense: false,\n        hasNewVersion: false,\n        ignoreCaptcha: true,\n        device: DeviceType.Desktop,\n        lastFilePath: '',\n        currentDB: '',\n        currentPgDB: '',\n        currentRedisDB: '',\n        showEntranceWarn: true,\n        defaultNetwork: 'all',\n        defaultIO: 'all',\n        isFxplay: false,\n\n        isProductPro: false,\n        isIntl: false,\n        docWithRegion: true,\n        productProExpires: 0,\n        isMasterProductPro: false,\n        isOffLine: false,\n\n        masterAlias: '',\n        currentNode: 'local',\n        currentNodeAddr: '',\n    }),\n    getters: {\n        isDarkTheme: (state) =>\n            state.themeConfig.theme === 'dark' ||\n            (state.themeConfig.theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches),\n        isDarkGoldTheme: (state) => state.themeConfig.primary === '#F0BE96' && state.isProductPro,\n        docsUrl: (state) => {\n            if (state.docWithRegion) {\n                return state.isIntl ? INTL_DOCS_URL : CN_DOCS_URL;\n            }\n            const lang = state.language.toLowerCase();\n            const isChinese = lang === 'zh';\n            return isChinese ? CN_DOCS_URL : INTL_DOCS_URL;\n        },\n        isMaster: (state) => state.currentNode === 'local',\n    },\n    actions: {\n        setOpenMenuTabs(openMenuTabs: boolean) {\n            this.openMenuTabs = openMenuTabs;\n        },\n        setScreenFull() {\n            this.isFullScreen = !this.isFullScreen;\n        },\n        setLogStatus(login: boolean) {\n            this.isLogin = login;\n        },\n        setGlobalLoading(loading: boolean) {\n            this.isLoading = loading;\n        },\n        setLoadingText(text: string) {\n            this.loadingText = text;\n        },\n        setCsrfToken(token: string) {\n            this.csrfToken = token;\n        },\n        async updateLanguage(language: string) {\n            const activeLocale = await setActiveLocale(language);\n            this.language = activeLocale;\n            return activeLocale;\n        },\n        setThemeConfig(themeConfig: ThemeConfigProp) {\n            this.themeConfig = themeConfig;\n        },\n        setAgreeLicense(agree: boolean) {\n            this.agreeLicense = agree;\n        },\n        toggleDevice(value: DeviceType) {\n            this.device = value;\n        },\n        isMobile() {\n            return this.device === DeviceType.Mobile;\n        },\n        getMasterAlias() {\n            return this.masterAlias || i18n.global.t('xpack.node.master');\n        },\n        isMasterPro() {\n            return this.isMasterProductPro;\n        },\n        setLastFilePath(path: string) {\n            this.lastFilePath = path;\n        },\n        setCurrentDB(name: string) {\n            this.currentDB = name;\n        },\n        setCurrentPgDB(name: string) {\n            this.currentPgDB = name;\n        },\n        setCurrentRedisDB(name: string) {\n            this.currentRedisDB = name;\n        },\n        setShowEntranceWarn(show: boolean) {\n            this.showEntranceWarn = show;\n        },\n        setDefaultNetwork(net: string) {\n            this.defaultNetwork = net;\n        },\n        setDefaultIO(net: string) {\n            this.defaultIO = net;\n        },\n    },\n    persist: piniaPersistConfig('GlobalState'),\n});\n\nexport default GlobalStore;\n"
  },
  {
    "path": "frontend/src/store/modules/menu.ts",
    "content": "import { defineStore } from 'pinia';\nimport { MenuState } from '../interface';\nimport piniaPersistConfig from '@/config/pinia-persist';\nimport { RouteRecordRaw } from 'vue-router';\nconst whiteList = ['/login', '/error'];\n\nexport const MenuStore = defineStore({\n    id: 'MenuState',\n    state: (): MenuState => ({\n        isCollapse: false,\n        menuList: [],\n        withoutAnimation: false,\n    }),\n    getters: {},\n    actions: {\n        async setCollapse() {\n            this.isCollapse = !this.isCollapse;\n            this.withoutAnimation = false;\n        },\n        async setMenuList(menuList: RouteRecordRaw[]) {\n            const menus = menuList.filter((item) => {\n                return whiteList.indexOf(item.path) < 0;\n            });\n            this.menuList = menus;\n        },\n        closeSidebar(withoutAnimation: boolean) {\n            this.isCollapse = true;\n            this.withoutAnimation = withoutAnimation;\n        },\n    },\n    persist: piniaPersistConfig('MenuStore'),\n});\n\nexport default MenuStore;\n"
  },
  {
    "path": "frontend/src/store/modules/process.ts",
    "content": "import { defineStore } from 'pinia';\nimport { ref, reactive } from 'vue';\n\nexport interface PsSearch {\n    type: 'ps';\n    pid: number | undefined;\n    username: string;\n    name: string;\n}\n\nexport interface NetSearch {\n    type: 'net';\n    processID: number | undefined;\n    processName: string;\n    port: number | undefined;\n}\n\nexport const ProcessStore = defineStore('ProcessStore', () => {\n    let websocket: WebSocket | null = null;\n    let pollingTimer: ReturnType<typeof setInterval> | null = null;\n    let disconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n    let connectionRefCount = 0;\n\n    const isConnected = ref(false);\n    const isConnecting = ref(false);\n\n    const psData = ref<any[]>([]);\n    const psLoading = ref(false);\n    const psSearch = reactive<PsSearch>({\n        type: 'ps',\n        pid: undefined,\n        username: '',\n        name: '',\n    });\n\n    const netData = ref<any[]>([]);\n    const netLoading = ref(false);\n    const netSearch = reactive<NetSearch>({\n        type: 'net',\n        processID: undefined,\n        processName: '',\n        port: undefined,\n    });\n\n    let pendingRequestType: 'ps' | 'net' | null = null;\n\n    let queuedRequestType: 'ps' | 'net' | null = null;\n\n    const isPsFetching = ref(false);\n    const isNetFetching = ref(false);\n\n    const activePollingType = ref<'ps' | 'net' | null>(null);\n\n    const isWsOpen = () => {\n        return websocket && websocket.readyState === WebSocket.OPEN;\n    };\n\n    const onOpen = () => {\n        isConnected.value = true;\n        isConnecting.value = false;\n    };\n\n    const doSendMessage = (type: 'ps' | 'net') => {\n        pendingRequestType = type;\n\n        if (type === 'ps') {\n            isPsFetching.value = true;\n            psLoading.value = psData.value.length === 0;\n\n            const searchParams = { ...psSearch };\n            if (typeof searchParams.pid === 'string') {\n                searchParams.pid = Number(searchParams.pid);\n            }\n            websocket!.send(JSON.stringify(searchParams));\n        } else {\n            isNetFetching.value = true;\n            netLoading.value = netData.value.length === 0;\n\n            const searchParams = { ...netSearch };\n            if (typeof searchParams.processID === 'string') {\n                searchParams.processID = Number(searchParams.processID);\n            }\n            if (typeof searchParams.port === 'string') {\n                searchParams.port = Number(searchParams.port);\n            }\n            websocket!.send(JSON.stringify(searchParams));\n        }\n    };\n\n    const onMessage = (event: MessageEvent) => {\n        try {\n            const data = JSON.parse(event.data);\n            const responseType = pendingRequestType;\n\n            if (pendingRequestType === 'ps') {\n                isPsFetching.value = false;\n            } else if (pendingRequestType === 'net') {\n                isNetFetching.value = false;\n            }\n            pendingRequestType = null;\n\n            if (responseType === activePollingType.value) {\n                if (responseType === 'ps') {\n                    psData.value = data || [];\n                    psLoading.value = false;\n                } else if (responseType === 'net') {\n                    netData.value = data || [];\n                    netLoading.value = false;\n                }\n            }\n\n            if (queuedRequestType && isWsOpen()) {\n                const typeToSend = queuedRequestType;\n                queuedRequestType = null;\n                doSendMessage(typeToSend);\n            }\n        } catch (e) {\n            console.error('Failed to parse WebSocket message:', e);\n        }\n    };\n\n    const onError = () => {\n        console.error('WebSocket error');\n    };\n\n    const onClose = () => {\n        isConnected.value = false;\n        isConnecting.value = false;\n        websocket = null;\n    };\n\n    const initWebSocket = (currentNode: string) => {\n        if (websocket || isConnecting.value) {\n            return;\n        }\n\n        isConnecting.value = true;\n\n        const href = window.location.href;\n        const protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss';\n        const ipLocal = href.split('//')[1].split('/')[0];\n\n        websocket = new WebSocket(`${protocol}://${ipLocal}/api/v2/process/ws?operateNode=${currentNode}`);\n        websocket.onopen = onOpen;\n        websocket.onmessage = onMessage;\n        websocket.onerror = onError;\n        websocket.onclose = onClose;\n    };\n\n    const closeWebSocket = () => {\n        stopPolling();\n\n        if (websocket) {\n            websocket.close();\n            websocket = null;\n        }\n\n        isConnected.value = false;\n        isConnecting.value = false;\n    };\n\n    const connect = (currentNode: string) => {\n        if (disconnectTimer) {\n            clearTimeout(disconnectTimer);\n            disconnectTimer = null;\n        }\n\n        connectionRefCount++;\n\n        if (!websocket && !isConnecting.value) {\n            initWebSocket(currentNode);\n        }\n    };\n\n    const disconnect = () => {\n        connectionRefCount = Math.max(0, connectionRefCount - 1);\n\n        if (connectionRefCount === 0) {\n            disconnectTimer = setTimeout(() => {\n                if (connectionRefCount === 0) {\n                    closeWebSocket();\n                }\n            }, 500);\n        }\n    };\n\n    const sendPsMessage = () => {\n        if (!isWsOpen()) {\n            return;\n        }\n\n        if (pendingRequestType !== null) {\n            queuedRequestType = 'ps';\n            return;\n        }\n\n        if (isPsFetching.value) {\n            return;\n        }\n\n        doSendMessage('ps');\n    };\n\n    const sendNetMessage = () => {\n        if (!isWsOpen()) {\n            return;\n        }\n\n        if (pendingRequestType !== null) {\n            queuedRequestType = 'net';\n            return;\n        }\n\n        if (isNetFetching.value) {\n            return;\n        }\n\n        doSendMessage('net');\n    };\n\n    const startPolling = (type: 'ps' | 'net', interval = 3000, initialDelay = 0) => {\n        stopPolling();\n        activePollingType.value = type;\n\n        const sendInitial = () => {\n            if (type === 'ps') {\n                sendPsMessage();\n            } else {\n                sendNetMessage();\n            }\n        };\n\n        const scheduleInitialFetch = () => {\n            if (initialDelay > 0) {\n                setTimeout(sendInitial, initialDelay);\n            } else {\n                sendInitial();\n            }\n        };\n\n        if (isWsOpen()) {\n            scheduleInitialFetch();\n        } else {\n            const checkConnection = setInterval(() => {\n                if (isWsOpen()) {\n                    clearInterval(checkConnection);\n                    scheduleInitialFetch();\n                }\n            }, 100);\n            setTimeout(() => clearInterval(checkConnection), 5000);\n        }\n\n        pollingTimer = setInterval(() => {\n            if (type === 'ps') {\n                sendPsMessage();\n            } else {\n                sendNetMessage();\n            }\n        }, interval);\n    };\n\n    const stopPolling = () => {\n        if (pollingTimer) {\n            clearInterval(pollingTimer);\n            pollingTimer = null;\n        }\n        activePollingType.value = null;\n    };\n\n    const updatePsSearch = (params: Partial<Omit<PsSearch, 'type'>>) => {\n        Object.assign(psSearch, params);\n    };\n\n    const updateNetSearch = (params: Partial<Omit<NetSearch, 'type'>>) => {\n        Object.assign(netSearch, params);\n    };\n\n    const resetPsSearch = () => {\n        psSearch.pid = undefined;\n        psSearch.username = '';\n        psSearch.name = '';\n    };\n\n    const resetNetSearch = () => {\n        netSearch.processID = undefined;\n        netSearch.processName = '';\n        netSearch.port = undefined;\n    };\n\n    return {\n        isConnected,\n        isConnecting,\n        psData,\n        psLoading,\n        psSearch,\n        netData,\n        netLoading,\n        netSearch,\n        isPsFetching,\n        isNetFetching,\n        activePollingType,\n\n        isWsOpen,\n        connect,\n        disconnect,\n        initWebSocket,\n        closeWebSocket,\n        sendPsMessage,\n        sendNetMessage,\n        startPolling,\n        stopPolling,\n        updatePsSearch,\n        updateNetSearch,\n        resetPsSearch,\n        resetNetSearch,\n    };\n});\n\nexport default ProcessStore;\n"
  },
  {
    "path": "frontend/src/store/modules/tabs.ts",
    "content": "import { ref } from 'vue';\nimport { defineStore } from 'pinia';\n\nconst TabsStore = defineStore(\n    'TabsStore',\n    () => {\n        const isShowTabIcon = ref(true);\n        // 缓存的KEY，直接给keepalive使用\n        const cachedTabs = ref([]);\n        const openedTabs = ref([]);\n        const activeTabPath = ref('');\n\n        const getActivePath = (path) => {\n            let firstSlashIndex = path.indexOf('/');\n            let lastSlashIndex = path.lastIndexOf('/');\n            if (firstSlashIndex === -1 || firstSlashIndex === lastSlashIndex) {\n                return path;\n            }\n            return path.substring(firstSlashIndex, lastSlashIndex);\n        };\n\n        const getTabIdxByPath = (path) => {\n            return openedTabs.value.findIndex((v) => v.path === path);\n        };\n\n        const removeAllTabs = () => {\n            openedTabs.value = [];\n            cachedTabs.value = [];\n        };\n\n        const removeUnActiveTabs = () => {\n            if (openedTabs.value.length) {\n                let idx = getTabIdxByPath(activeTabPath.value);\n                idx = idx > -1 ? idx : 0;\n                const tab = openedTabs.value[idx];\n                removeOtherTabs(tab);\n            }\n        };\n\n        const findTab = (path) => {\n            const idx = getTabIdxByPath(path);\n            if (idx > -1) {\n                return openedTabs.value[idx];\n            }\n        };\n\n        const addTab = (tab) => {\n            const idx = getTabIdxByPath(tab.path);\n            if (idx < 0) {\n                openedTabs.value.push(Object.assign({}, tab));\n                addCachedTab(tab.name);\n            }\n        };\n\n        const removeTab = (path) => {\n            if (openedTabs.value.length > 1) {\n                const idx = getTabIdxByPath(path);\n                if (idx > -1) {\n                    removeCachedTab(openedTabs.value[idx].name);\n                    openedTabs.value.splice(idx, 1);\n                }\n                return openedTabs.value[openedTabs.value.length - 1].path;\n            }\n        };\n\n        const removeOtherTabs = (path) => {\n            const idx = getTabIdxByPath(path);\n            if (idx > -1) {\n                const tab = openedTabs.value[idx];\n                openedTabs.value = [tab];\n                cachedTabs.value = [];\n                cachedTabs.value = [tab.name];\n            }\n        };\n\n        const removeTabs = (path, type) => {\n            if (path) {\n                const idx = getTabIdxByPath(path);\n                let removeTabs = [];\n                if (type === 'right') {\n                    removeTabs = openedTabs.value.splice(idx + 1);\n                } else if (type === 'left') {\n                    removeTabs = openedTabs.value.splice(0, idx);\n                }\n                if (removeTabs.length) {\n                    removeTabs.forEach((e) => removeCachedTab(e.name));\n                }\n            }\n        };\n\n        const addCachedTab = (name) => {\n            if (name && !cachedTabs.value.includes(name)) {\n                cachedTabs.value.push(name);\n            }\n        };\n\n        const removeCachedTab = (name) => {\n            if (name) {\n                const idx = cachedTabs.value.findIndex((v) => v === name);\n                if (idx > -1) {\n                    cachedTabs.value.splice(idx, 1);\n                }\n            }\n        };\n\n        const hasCloseDropdown = (path, type) => {\n            const idx = getTabIdxByPath(path);\n            switch (type) {\n                case 'close':\n                case 'other':\n                    return openedTabs.value.length > 1;\n                case 'left':\n                    return idx !== 0;\n                case 'right':\n                    return idx !== openedTabs.value.length - 1;\n            }\n        };\n\n        return {\n            isShowTabIcon,\n            activeTabPath,\n            openedTabs,\n            cachedTabs,\n            addTab,\n            findTab,\n            addCachedTab,\n            removeCachedTab,\n            removeTab,\n            removeTabs,\n            removeOtherTabs,\n            removeAllTabs,\n            removeUnActiveTabs,\n            hasCloseDropdown,\n            getActivePath,\n        };\n    },\n    {\n        persist: true,\n    },\n);\n\nexport default TabsStore;\n"
  },
  {
    "path": "frontend/src/store/modules/terminal.ts",
    "content": "import { defineStore } from 'pinia';\nimport piniaPersistConfig from '@/config/pinia-persist';\nimport { TerminalState } from '../interface';\n\nexport const TerminalStore = defineStore({\n    id: 'TerminalState',\n    state: (): TerminalState => ({\n        lineHeight: 1.2,\n        letterSpacing: 1.2,\n        fontSize: 12,\n        fontFamily: \"Monaco, Menlo, Consolas, 'Courier New', monospace\",\n        backgroundColor: '#000000',\n        foregroundColor: '#f5f5f5',\n        cursorBlink: 'enable',\n        cursorStyle: 'underline',\n        scrollback: 1000,\n        scrollSensitivity: 10,\n    }),\n    actions: {\n        setLineHeight(lineHeight: number) {\n            this.lineHeight = lineHeight;\n        },\n        setLetterSpacing(letterSpacing: number) {\n            this.letterSpacing = letterSpacing;\n        },\n        setFontSize(fontSize: number) {\n            this.fontSize = fontSize;\n        },\n        setFontFamily(fontFamily: string) {\n            this.fontFamily = fontFamily;\n        },\n        setBackgroundColor(backgroundColor: string) {\n            this.backgroundColor = backgroundColor;\n        },\n        setForegroundColor(foregroundColor: string) {\n            this.foregroundColor = foregroundColor;\n        },\n        setCursorBlink(cursorBlink: string) {\n            this.cursorBlink = cursorBlink;\n        },\n        setCursorStyle(cursorStyle: string) {\n            this.cursorStyle = cursorStyle;\n        },\n        setScrollback(scrollback: number) {\n            this.scrollback = scrollback;\n        },\n        setScrollSensitivity(scrollSensitivity: number) {\n            this.scrollSensitivity = scrollSensitivity;\n        },\n    },\n    persist: piniaPersistConfig('TerminalState'),\n});\n\nexport default TerminalStore;\n"
  },
  {
    "path": "frontend/src/styles/common.scss",
    "content": "html {\n    font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Arial, sans-serif;\n}\n.flx-center {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n}\n\n.flx-align-center {\n    display: flex;\n    align-items: center;\n}\n\n.sle {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n\n.fade-transform-leave-active,\n.fade-transform-enter-active {\n    transition: all 0.2s;\n}\n.fade-transform-enter-from {\n    opacity: 0;\n    transition: all 0.2s;\n    transform: translateX(-30px);\n}\n.fade-transform-leave-to {\n    opacity: 0;\n    transition: all 0.2s;\n    transform: translateX(30px);\n}\n\n/* Breadcrumb */\n.breadcrumb-enter-active,\n.breadcrumb-leave-active {\n    transition: all 0.2s ease;\n}\n.breadcrumb-enter-from,\n.breadcrumb-leave-active {\n    opacity: 0;\n    transform: translateX(10px);\n}\n.breadcrumb-leave-active {\n    position: absolute;\n    z-index: -1;\n}\n\n::-webkit-scrollbar {\n    width: 8px;\n    height: 8px;\n    background-color: white;\n}\n::-webkit-scrollbar-thumb {\n    background-color: #dddee0;\n    border-radius: 20px;\n    box-shadow: inset 0 0 0 white;\n}\n\n#nprogress {\n    .bar {\n        background: $primary-color !important;\n    }\n    .spinner-icon {\n        border-top-color: $primary-color !important;\n        border-left-color: $primary-color !important;\n    }\n    .peg {\n        box-shadow: 0 0 10px $primary-color, 0 0 5px $primary-color !important;\n    }\n}\n\n.form-button {\n    float: right;\n}\n\n.input-help {\n    font-size: 12px;\n    word-break: keep-all;\n    color: #adb0bc;\n    width: 100%;\n    display: inline-block;\n    white-space: pre-line;\n}\n\n.input-error {\n    font-size: 12px;\n    word-break: keep-all;\n    color: red;\n    transform: scale(0.9);\n    transform-origin: left;\n    width: 110%;\n    display: inline-block;\n    white-space: normal;\n}\n\n.mask {\n    width: 100%;\n    height: 100%;\n    opacity: 0.4;\n    top: 0;\n    left: 0;\n    pointer-events: none;\n}\n\n.mask-prompt {\n    position: absolute;\n    z-index: 1;\n    top: 220px;\n    left: 45%;\n    transform: translate(-50%, -50%);\n    width: 400px;\n    text-align: center;\n    font-size: 14px;\n    .bt {\n        margin-top: -2px;\n    }\n}\n\n.sidebar-container-popper {\n    .el-menu--popup-right-start {\n        background-color: rgba(0, 94, 235, 0.1);\n    }\n}\n\n.search-button {\n    width: 200px;\n}\n\n.drawer-header-button {\n    span {\n        color: currentColor !important;\n        font-size: var(--el-font-size-base) !important;\n    }\n    .active-button {\n        color: var(--el-button-hover-text-color);\n        border-color: var(--el-button-hover-border-color);\n    }\n}\n\n.app-status {\n    font-size: 12px;\n\n    .el-card {\n        --el-card-padding: 9px;\n\n        .buttons {\n            margin-left: 100px;\n        }\n\n        .status-content {\n            margin-left: 50px;\n        }\n    }\n}\n\n.mini-border-card {\n    width: 100%;\n    .el-card__body {\n        --el-card-padding: 12px 12px 0 22px;\n    }\n}\n\n.xterm-viewport::-webkit-scrollbar {\n    width: 8px;\n    height: 8px;\n    background-color: #000000;\n}\n\n.status-count {\n    font-size: 24px;\n}\n.status-label {\n    font-size: 14px;\n    color: #646a73;\n}\n\n.table-link {\n    color: $primary-color;\n    cursor: pointer;\n}\n\n.table-link:hover {\n    opacity: 0.6;\n}\n\n.app-warn {\n    text-align: center;\n    margin-top: 100px;\n    span:first-child {\n        color: #bbbfc4;\n    }\n\n    span:nth-child(2) {\n        color: $primary-color;\n        cursor: pointer;\n    }\n\n    span:nth-child(2):hover {\n        color: var(--el-color-primary-light-7);\n    }\n\n    img {\n        width: 300px;\n        height: 300px;\n    }\n}\n\n.common-prompt {\n    margin-bottom: 20px !important;\n}\n\n.mini-form-item {\n    width: 40% !important;\n}\n\n.pre-select {\n    width: 85px !important;\n}\n\n.el-input-group__append {\n    border-left: 0;\n    background-color: var(--el-fill-color-light) !important;\n    border-top-left-radius: 0;\n    border-bottom-left-radius: 0;\n    box-shadow: 0 1px 0 0 var(--el-input-border-color) inset, 0 -1px 0 0 var(--el-input-border-color) inset,\n        -1px 0 0 0 var(--el-input-border-color) inset;\n\n    &:hover {\n        color: var(--el-color-primary);\n        background-color: var(--el-color-primary-light-9) !important;\n    }\n}\n\n.tag-button {\n    margin-right: 10px;\n    &.no-active {\n        background: none;\n        border: none;\n    }\n}\n\n.limit-height-popover {\n    max-height: 300px;\n    overflow: auto;\n}\n\n.router-button {\n    margin-right: 20px;\n}\n\n.text-ellipsis {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n\n.star-center {\n    height: 16px;\n    display: inline-block;\n    vertical-align: middle;\n}\n\n.p-ml-5 {\n    margin-left: 5px !important;\n}\n\n.p-w-200 {\n    width: 200px !important;\n}\n\n.card-interval {\n    margin-top: 7px !important;\n}\n\n.p-w-300 {\n    width: 300px !important;\n}\n\n.p-w-100 {\n    width: 100px !important;\n}\n\n.p-w-150 {\n    width: 150px !important;\n}\n\n.p-w-400 {\n    width: 400px !important;\n}\n\n.p-ml-20 {\n    margin-left: 20px !important;\n}\n\n.p-mt-20 {\n    margin-top: 20px !important;\n}\n\n.el-tag {\n    cursor: pointer;\n}\n\n.dialog-footer {\n    display: flex;\n    align-items: center;\n    justify-content: flex-end;\n}\n\n.monaco-editor-tree-light .el-tree-node__content:hover {\n    background-color: #e5eefd;\n}\n\n.monaco-editor-tree-light .el-tree-node.is-current > .el-tree-node__content {\n    background-color: #e5eefd;\n}\n\n.monaco-editor-tree-dark .el-tree-node__content:hover {\n    background-color: #111417;\n}\n\n.monaco-editor-tree-dark .el-tree-node.is-current > .el-tree-node__content {\n    background-color: #111417;\n}\n\n.check-label {\n    background: var(--panel-main-bg-color-10) !important;\n    .check-label-a {\n        color: var(--panel-color-primary);\n    }\n}\n.check-content {\n    background: var(--panel-main-bg-color-10);\n    pre {\n        margin: 0;\n        width: 350px;\n        overflow: hidden;\n        text-overflow: ellipsis;\n    }\n}\n\n.el-descriptions {\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n\n.code-dialog {\n    .el-dialog__header {\n        --el-dialog-padding-primary: 0px !important;\n    }\n}\n\n.node-dashboard-card {\n    .header {\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n\n        .header-left {\n            display: flex;\n            align-items: center;\n            gap: 12px;\n\n            .header-span {\n                position: relative;\n                font-size: 16px;\n                font-weight: 500;\n                margin-left: 18px;\n                display: flex;\n                align-items: center;\n\n                &::before {\n                    position: absolute;\n                    top: 50%;\n                    transform: translateY(-50%);\n                    left: -13px;\n                    width: 4px;\n                    height: 14px;\n                    content: '';\n                    background: $primary-color;\n                    border-radius: 10px;\n                }\n            }\n        }\n        .header-right {\n            display: flex;\n            align-items: center;\n        }\n    }\n}\n"
  },
  {
    "path": "frontend/src/styles/element-dark.scss",
    "content": "html.dark {\n    --panel-color-primary: #3d8eff;\n    --panel-color-primary-light-8: #3674cc;\n    --panel-color-primary-light-1: #6eaaff;\n    --panel-color-primary-light-2: #366fc2;\n    --panel-color-primary-light-3: #3364ad;\n    --panel-color-primary-light-4: #2f558f;\n    --panel-color-primary-light-5: #372e46;\n    --panel-color-primary-light-6: #2a4066;\n    --panel-color-primary-light-7: #2d4a7a;\n    --panel-color-primary-light-9: #2d4a7a;\n\n    --panel-main-bg-color-1: #e3e6f3;\n    --panel-main-bg-color-2: #c0c2cf;\n    --panel-main-bg-color-3: #adb0bc;\n    --panel-main-bg-color-4: #9597a4;\n    --panel-main-bg-color-5: #90929f;\n    --panel-main-bg-color-6: #787b88;\n    --panel-main-bg-color-7: #5b5e6a;\n    --panel-main-bg-color-8: #434552;\n    --panel-main-bg-color-9: #2e313d;\n    --panel-main-bg-color-10: #242633;\n    --panel-main-bg-color-11: #60626f;\n    --panel-main-bg-color-12: #1f2329;\n    --panel-main-bg-color-13: #000000;\n\n    --panel-alert-error-bg-color: #fef0f0;\n    --panel-alert-error-text-color: #f56c6c;\n    --panel-alert-error-hover-bg-color: #e9657b;\n\n    --panel-alert-success-bg-color: #e1f3d8;\n    --panel-alert-success-text-color: #67c23a;\n    --panel-alert-success-hover-bg-color: #4dc894;\n\n    --panel-alert-warning-bg-color: #59472a;\n    --panel-alert-warning-text-color: #edac2c;\n    --panel-alert-warning-hover-bg-color: #f1c161;\n\n    --panel-alert-info-bg-color: var(--panel-main-bg-color-7);\n    --panel-alert-info-text-color: var(--panel-text-color-white);\n    --panel-alert-info-hover-bg-color: var(--panel-main-bg-color-4);\n\n    --el-color-success: #3fb950;\n    --el-color-success-light-5: #4dc894;\n    --el-color-success-light-8: #3fb950;\n    --el-color-success-light-9: var(--panel-main-bg-color-9);\n\n    --el-color-warning: #edac2c;\n    --el-color-warning-light-5: #f1c161;\n    --el-color-warning-light-8: #edac2c;\n    --el-color-warning-light-9: var(--panel-main-bg-color-9);\n\n    --el-color-danger: #e2324f;\n    --el-color-danger-light-5: #e9657b;\n    --el-color-danger-light-8: #e2324f;\n    --el-color-danger-light-9: var(--panel-main-bg-color-9);\n\n    --el-color-error: #e2324f;\n    --el-color-error-light-5: #e9657b;\n    --el-color-error-light-8: #e2324f;\n    --el-color-error-light-9: var(--panel-main-bg-color-9);\n\n    --el-color-info: var(--panel-main-bg-color-3);\n    --el-color-info-light-5: var(--panel-main-bg-color-3);\n    --el-color-info-light-8: var(--panel-main-bg-color-3);\n    --el-color-info-light-9: var(--panel-main-bg-color-9);\n\n    --panel-pie-bg-color: #434552;\n    --panel-text-color-white: #ffffff;\n\n    --el-color-primary: var(--panel-color-primary);\n    --el-color-primary-light-1: var(--panel-color-primary-light-1);\n    --el-color-primary-light-2: var(--panel-color-primary-light-2);\n    --el-color-primary-light-3: var(--panel-color-primary-light-3);\n    --el-color-primary-light-4: var(--panel-color-primary-light-4);\n    --el-color-primary-light-5: var(--panel-color-primary-light-5);\n    --el-color-primary-light-6: var(--panel-color-primary-light-6);\n    --el-color-primary-light-7: var(--panel-color-primary-light-7);\n    --el-color-primary-light-8: var(--panel-color-primary-light-8);\n    --el-color-primary-light-9: var(--panel-color-primary-light-9);\n    --el-color-primary-dark-2: var(--panel-color-primary);\n    --el-scrollbar-bg-color: var(--panel-main-bg-color-8);\n    --el-border-color-darker: var(--panel-main-bg-color-6);\n\n    --panel-border: 1px solid var(--panel-main-bg-color-8);\n    --panel-border-color: var(--panel-main-bg-color-8);\n    --panel-button-active: var(--panel-main-bg-color-10);\n    --panel-button-text-color: var(--panel-main-bg-color-10);\n    --panel-button-bg-color: var(--panel-color-primary);\n    --panel-footer-bg: var(--panel-main-bg-color-9);\n    --panel-footer-border: var(--panel-main-bg-color-7);\n    --panel-text-color: var(--panel-main-bg-color-1);\n    --panel-menu-bg-color: var(--panel-main-bg-color-10);\n    --panel-terminal-tag-bg-color: var(--panel-main-bg-color-10);\n    --panel-terminal-tag-active-bg-color: var(--panel-main-bg-color-10);\n    --panel-terminal-bg-color: var(--panel-main-bg-color-10);\n    --panel-terminal-tag-active-text-color: var(--panel-color-primary);\n    --panel-terminal-tag-hover-text-color: var(--panel-color-primary);\n    --panel-logs-bg-color: #1b1b1b;\n    --panel-alert-bg-color: var(--panel-main-bg-color-10);\n\n    --panel-node-card-bg-color: #434552;\n\n    --el-menu-item-bg-color: var(--panel-main-bg-color-9);\n    --el-menu-item-bg-color-active: var(--panel-main-bg-color-8);\n    --el-menu-hover-bg-color: var(--panel-main-bg-color-8);\n    --el-menu-text-color: var(--panel-main-bg-color-2);\n    --el-fill-color-blank: var(--panel-main-bg-color-10);\n    --el-fill-color-light: var(--panel-main-bg-color-10);\n    --el-fill-color-lighter: var(--panel-main-bg-color-9);\n    --el-border-color: var(--panel-main-bg-color-8);\n    --el-border-color-light: var(--panel-main-bg-color-8);\n    --el-border-color-lighter: var(--panel-main-bg-color-8);\n\n    --el-text-color-primary: var(--panel-main-bg-color-2);\n    --el-text-color-regular: var(--panel-main-bg-color-2);\n\n    --el-box-shadow: 0px 12px 32px 4px rgba(36, 38, 51, 0.36), 0px 8px 20px rgba(36, 38, 51, 0.72);\n    --el-box-shadow-light: 0px 0px 12px rgba(36, 38, 51, 0.72);\n    --el-box-shadow-lighter: 0px 0px 6px rgba(36, 38, 51, 0.72);\n    --el-box-shadow-dark: 0px 16px 48px 16px rgba(36, 38, 51, 0.72), 0px 12px 32px #242633, 0px 8px 16px -8px #242633;\n    --el-bg-color: var(--panel-main-bg-color-9);\n    --el-bg-color-overlay: var(--panel-main-bg-color-9);\n    --el-dialog-bg-color: var(--panel-main-bg-color-9);\n    --panel-code-header-footer-color: var(--panel-main-bg-color-12);\n\n    --el-text-color-placeholder: var(--panel-main-bg-color-4);\n\n    .el-radio-button {\n        --el-radio-button-checked-text-color: var(--panel-main-bg-color-10);\n    }\n    .el-descriptions__content:not(.is-bordered-label) {\n        color: var(--panel-main-bg-color-3);\n    }\n\n    .el-menu-item:hover,\n    .el-sub-menu__title:hover {\n        background: var(--panel-main-bg-color-8) !important;\n    }\n\n    .el-menu .el-menu-item {\n        box-shadow: 0 0 4px rgba(36, 38, 51, 0.72);\n    }\n\n    .el-menu .el-sub-menu__title {\n        box-shadow: 0 0 4px rgba(36, 38, 51, 0.72);\n    }\n\n    .el-overlay {\n        background-color: rgb(46 49 61 / 80%);\n    }\n\n    .el-tag.el-tag--primary {\n        --el-tag-bg-color: var(--panel-main-bg-color-9);\n        --el-tag-border-color: var(--panel-main-bg-color-11);\n        --el-tag-hover-color: var(--panel-color-primary);\n    }\n\n    .el-tabs--card > .el-tabs__header .el-tabs__nav {\n        border: 1px solid var(--panel-main-bg-color-8);\n        border-bottom: none;\n    }\n\n    .el-tabs--card > .el-tabs__header .el-tabs__item.is-active {\n        border-bottom-color: var(--panel-color-primary);\n        --el-text-color-regular: var(--panel-color-primary);\n    }\n    .main-container {\n        .el-loading-mask {\n            background-color: #24263375;\n        }\n    }\n\n    .el-loading-mask {\n        background-color: rgba(0, 0, 0, 0.8);\n    }\n\n    .el-input {\n        --el-input-border-color: var(--panel-main-bg-color-8);\n    }\n\n    input:-webkit-autofill {\n        box-shadow: 0 0 0 1000px var(--el-box-shadow) inset;\n        background-color: var(--panel-main-bg-color-1);\n        transition: background-color 1000s ease-out 0.5s;\n    }\n    .el-form-item .el-input__inner:-webkit-autofill {\n        -webkit-text-fill-color: var(--el-text-color-regular) !important;\n    }\n\n    .el-input.is-disabled .el-input__wrapper {\n        --el-disabled-bg-color: var(--panel-main-bg-color-9);\n        --el-disabled-border-color: var(--panel-main-bg-color-8);\n    }\n\n    .el-input > .el-input-group__append:hover {\n        background-color: var(--panel-main-bg-color-9) !important;\n    }\n\n    .el-form-item__label {\n        color: var(--panel-main-bg-color-3);\n    }\n\n    .el-card {\n        --el-card-bg-color: var(--panel-main-bg-color-10);\n    }\n\n    .el-button:hover {\n        --el-button-hover-border-color: var(--panel-main-bg-color-11);\n        --el-button-hover-bg-color: var(--panel-main-bg-color-10);\n    }\n\n    .el-button--primary {\n        --el-button-text-color: var(--panel-main-bg-color-10);\n        --el-button-hover-link-text-color: var(--panel-color-primary-light-1);\n        &.tag-button,\n        &.brief-button {\n            --el-button-text-color: var(--panel-main-bg-color-10);\n            --el-button-hover-text-color: var(--el-color-white);\n            --el-button-hover-border-color: var(--el-color-primary);\n            --el-button-hover-bg-color: var(--el-color-primary);\n        }\n\n        &.app-button {\n            --el-button-text-color: var(--el-color-primary);\n        }\n\n        &.h-app-button {\n            --el-button-text-color: var(--panel-main-bg-color-10);\n            --el-button-hover-text-color: var(--el-color-white);\n            --el-button-hover-border-color: var(--el-color-primary);\n            --el-button-hover-bg-color: var(--el-color-primary);\n        }\n    }\n\n    .el-button--primary.is-plain,\n    .el-button--primary.is-text,\n    .el-button--primary.is-link {\n        --el-button-text-color: var(--panel-main-bg-color-2);\n        --el-button-bg-color: var(--panel-main-bg-color-9);\n        --el-button-border-color: var(--panel-main-bg-color-8);\n        --el-button-hover-bg-color: var(--panel-main-bg-color-9);\n        --el-button-hover-border-color: var(--panel-main-bg-color-8);\n    }\n\n    .el-button--primary.is-text,\n    .el-button--primary.is-link {\n        --el-button-text-color: var(--panel-color-primary);\n    }\n\n    .el-button--primary:hover {\n        --el-button-hover-text-color: var(--panel-main-bg-color-7);\n        --el-button-border-color: var(--el-color-primary);\n        --el-button-hover-bg-color: var(--panel-color-primary-light-2);\n        --el-button-hover-border-color: var(--panel-main-bg-color-8);\n    }\n\n    .el-button--primary.is-plain:hover {\n        --el-button-hover-text-color: var(--panel-main-bg-color-10);\n        --el-button-border-color: var(--el-color-primary);\n        --el-button-hover-bg-color: var(--el-color-primary);\n        --el-button-hover-border-color: var(--el-color-primary);\n    }\n\n    .el-button--primary:active {\n        --el-button-hover-text-color: var(--panel-main-bg-color-7);\n        --el-button-active-bg-color: var(--el-color-primary-light-3);\n        --el-button-active-border-color: var(--el-color-primary-light-3);\n    }\n    .el-button--primary.is-plain:active {\n        color: var(--panel-main-bg-color-10);\n    }\n\n    .el-button:focus-visible {\n        outline: none;\n    }\n\n    .el-button.is-disabled {\n        color: var(--panel-main-bg-color-7);\n        border-color: var(--panel-main-bg-color-8);\n        background: var(--panel-main-bg-color-9);\n    }\n\n    .el-button.is-disabled:hover {\n        border-color: var(--panel-main-bg-color-8);\n        background: var(--panel-main-bg-color-9);\n    }\n\n    .el-button--primary.is-link.is-disabled {\n        color: var(--panel-main-bg-color-8);\n    }\n\n    .el-dropdown-menu__item:hover {\n        background-color: var(--panel-main-bg-color-7);\n    }\n\n    .el-drawer .el-drawer__header span {\n        color: var(--panel-text-color);\n    }\n\n    .el-dialog {\n        background-color: var(--panel-main-bg-color-9);\n        border: 1px solid var(--panel-border-color);\n\n        .el-dialog__header {\n            color: var(--el-text-color-primary);\n\n            .el-dialog__title {\n                color: var(--el-menu-text-color);\n            }\n        }\n    }\n\n    .el-alert--error {\n        --el-alert-bg-color: var(--panel-alert-error-bg-color);\n        --el-color-error: var(--panel-alert-error-text-color);\n    }\n\n    .el-alert--success {\n        --el-alert-bg-color: var(--panel-alert-success-bg-color);\n        --el-color-success: var(--panel-alert-success-text-color);\n    }\n\n    .el-alert--warning {\n        --el-alert-bg-color: var(--panel-alert-warning-bg-color);\n        --el-color-warning: var(--panel-alert-warning-text-color);\n    }\n\n    .el-alert--info {\n        --el-alert-bg-color: var(--panel-alert-info-bg-color);\n        --el-color-info: var(--panel-alert-info-text-color);\n    }\n\n    .md-editor-dark {\n        --md-bk-color: var(--panel-main-bg-color-9);\n    }\n\n    .md-editor-dark .md-editor-preview {\n        --md-theme-color: var(--el-text-color-primary);\n    }\n\n    .md-editor-dark .default-theme a {\n        --md-theme-link-color: var(--el-color-primary);\n    }\n\n    .md-editor-dark .default-theme pre code {\n        background-color: var(--panel-main-bg-color-8);\n    }\n    .md-editor-dark .default-theme pre:before {\n        background-color: var(--panel-main-bg-color-10);\n    }\n\n    .el-descriptions__title {\n        color: var(--el-text-color-primary);\n    }\n    .el-descriptions__content.el-descriptions__cell.is-bordered-content {\n        color: var(--el-text-color-primary);\n    }\n    .el-descriptions--large .el-descriptions__body .el-descriptions__table.is-bordered .el-descriptions__cell {\n        padding: 12px 15px;\n        background-color: transparent;\n    }\n    .el-descriptions__body {\n        background-color: transparent;\n    }\n    .el-descriptions__label {\n        color: var(--el-color-primary) !important;\n        margin-right: 16px;\n    }\n\n    .el-avatar {\n        --el-avatar-bg-color: var(--panel-text-color-white) !important;\n        box-shadow: 0 0 4px rgba(0, 94, 235, 0.1);\n        border: 0.5px solid var(--panel-main-bg-color-7);\n    }\n    .el-drawer {\n        .cm-editor {\n            background-color: var(--panel-logs-bg-color);\n        }\n        .cm-gutters {\n            background-color: var(--panel-logs-bg-color);\n        }\n        .log-container {\n            background-color: var(--panel-logs-bg-color);\n        }\n    }\n\n    .cm-editor {\n        background-color: var(--panel-main-bg-color-9);\n    }\n    .cm-gutters {\n        background-color: var(--panel-main-bg-color-9);\n    }\n\n    // scroll-bar\n    ::-webkit-scrollbar {\n        background-color: var(--el-scrollbar-bg-color) !important;\n    }\n    ::-webkit-scrollbar-thumb {\n        background-color: var(--el-border-color-darker);\n    }\n    ::-webkit-scrollbar-corner {\n        background-color: var(--el-scrollbar-bg-color);\n    }\n\n    .app-warn {\n        span {\n            &:nth-child(2) {\n                color: var(--panel-color-primary);\n\n                &:hover {\n                    color: var(--panel-color-primary-light-3);\n                }\n            }\n        }\n    }\n\n    .el-table {\n        --el-table-bg-color: var(--el-bg-color);\n        --el-table-tr-bg-color: var(--el-bg-color);\n        --el-table-header-bg-color: var(--el-bg-color);\n        --el-table-row-hover-bg-color: var(--panel-main-bg-color-8);\n        --el-table-border: 1px solid var(--panel-main-bg-color-8);\n        --el-table-border-color: var(--panel-main-bg-color-8);\n    }\n\n    .el-message-box {\n        --el-messagebox-title-color: var(--el-menu-text-color);\n        border: 1px solid var(--panel-border-color);\n    }\n\n    .el-popover {\n        --el-popover-title-text-color: var(--panel-main-bg-color-2);\n        border: 1px solid var(--panel-border-color);\n    }\n\n    .app-wrapper {\n        .main-container {\n            background-color: var(--panel-main-bg-color-9) !important;\n        }\n        .app-footer {\n            background-color: var(--panel-main-bg-color-9) !important;\n            border-top: var(--panel-border);\n        }\n        .mobile-header {\n            background-color: var(--panel-main-bg-color-9) !important;\n            border-bottom: var(--panel-border);\n            color: #ffffff;\n        }\n    }\n\n    .router_card_button {\n        .el-radio-button__inner {\n            background: none !important;\n        }\n        .el-radio-button__original-radio:checked + .el-radio-button__inner {\n            color: var(--panel-main-bg-color-10);\n            background-color: var(--panel-color-primary) !important;\n            box-shadow: none !important;\n            border: none !important;\n        }\n    }\n\n    .el-date-table td.in-range .el-date-table-cell {\n        background-color: var(--panel-main-bg-color-8);\n    }\n\n    .el-collapse-item__header {\n        color: #ffffff;\n        background-color: transparent !important;\n    }\n\n    .el-checkbox__input.is-checked .el-checkbox__inner::after {\n        border-color: var(--panel-main-bg-color-10);\n    }\n\n    .el-checkbox__input.is-indeterminate .el-checkbox__inner::before {\n        background-color: var(--panel-main-bg-color-10);\n    }\n\n    .custom-input-textarea {\n        background-color: var(--panel-main-bg-color-10) !important;\n        color: var(--el-color-info) !important;\n    }\n    .custom-input-textarea:hover {\n        background-color: var(--panel-main-bg-color-9) !important;\n        color: var(--el-color-primary) !important;\n    }\n}\n"
  },
  {
    "path": "frontend/src/styles/element.scss",
    "content": ":root {\n    --panel-gradient-end-color: var(--el-color-primary-light-7);\n\n    --el-color-primary-light-1: #196eed;\n    --el-color-primary-light-2: #337eef;\n    --el-color-primary-light-4: #669ef3;\n    --el-color-primary-light-6: #99bef7;\n\n    --panel-color-primary: #005eeb;\n    --panel-color-primary-light-8: #196eed;\n    --panel-color-primary-light-1: #196eed;\n    --panel-color-primary-light-2: #337eef;\n    --panel-color-primary-light-3: #4c8ef1;\n    --panel-color-primary-light-4: #669ef3;\n    --panel-color-primary-light-5: #7faef5;\n    --panel-color-primary-light-6: #99bef7;\n    --panel-color-primary-light-7: #b2cef9;\n    --panel-color-primary-light-9: #e5eefd;\n\n    --el-color-primary: var(--panel-color-primary);\n    --el-color-primary-light-3: var(--panel-color-primary-light-1);\n    --el-color-primary-light-5: var(--panel-color-primary-light-5);\n    --el-color-primary-light-7: var(--panel-color-primary-light-7);\n    --el-color-primary-light-8: var(--panel-color-primary-light-8);\n    --el-color-primary-light-9: var(--panel-color-primary-light-9);\n    --panel-code-header-footer-color: var(--panel-color-primary-light-9);\n\n    --el-text-color-regular: #646a73;\n}\n\nhtml {\n    --el-box-shadow-light: 0px 0px 4px rgba(0, 94, 235, 0.1) !important;\n\n    // * menu\n    --el-menu-item-bg-color: rgba(255, 255, 255, 0.3);\n    --el-menu-item-bg-color-active: #ffffff;\n    --panel-main-bg-color-9: #f4f4f4;\n    --panel-menu-bg-color: rgba(0, 94, 235, 0.1);\n    --panel-menu-width: 180px;\n    --panel-menu-hide-width: 75px;\n    --panel-text-color: #1f2329;\n    --panel-border: 1px solid #f2f2f2;\n    --panel-button-active: #ffffff;\n    --panel-button-text-color: var(--panel-color-primary);\n    --panel-button-bg-color: #ffffff;\n    --panel-footer-border: #e4e7ed;\n    --panel-terminal-tag-bg-color: #efefef;\n    --panel-terminal-tag-active-bg-color: #575758;\n    --panel-terminal-tag-active-text-color: #ebeef5;\n    --panel-terminal-tag-hover-text-color: #575758;\n    --panel-terminal-bg-color: #1b1b1b;\n    --panel-logs-bg-color: #1b1b1b;\n    --panel-alert-bg-color: rgba(0, 94, 235, 0.03);\n\n    --panel-alert-bg: #e2e4ec;\n    --panel-path-bg: #ffffff;\n    --panel-footer-bg: #ffffff;\n    --panel-pie-bg-color: #ffffff;\n\n    --el-fill-color-light: #ffffff;\n    --el-disabled-bg-color: var(--panel-main-bg-color-9) !important;\n\n    --panel-node-card-bg-color: #e3e6f3;\n}\n\n.el-notification {\n    z-index: 99999 !important;\n}\n\n.el-message {\n    z-index: 99999 !important;\n}\n\n.table-box {\n    display: flex;\n    flex-direction: column;\n    height: 100%;\n\n    .table-search {\n        display: flex;\n        margin-bottom: 10px;\n        .el-form {\n            max-width: 1260px;\n            .el-form-item {\n                margin-right: 5px;\n                .el-input,\n                .el-select,\n                .el-date-editor--timerange {\n                    width: 210px;\n                }\n                .el-date-editor--datetimerange,\n                .el-date-editor--daterange {\n                    width: 400px;\n                }\n\n                .el-range-editor.el-input__wrapper {\n                    padding: 0 10px;\n                }\n\n                .el-select__tags {\n                    overflow: hidden;\n                    white-space: nowrap;\n                }\n            }\n            .more-item {\n                display: inline;\n            }\n        }\n        .search-operation {\n            margin-left: 15px;\n            white-space: nowrap;\n            .search-isOpen {\n                margin-left: 20px;\n            }\n        }\n    }\n\n    .table-header {\n        .header-button-lf {\n            float: left;\n            .el-button {\n                margin-bottom: 20px;\n            }\n        }\n        .header-button-ri {\n            float: right;\n            .el-button {\n                margin-bottom: 20px;\n            }\n        }\n    }\n\n    .el-table {\n        flex: 1;\n        .el-table__header th,\n        .el-table__body td {\n            text-align: center;\n        }\n        .el-table__header th {\n            height: 50px;\n            font-size: 15px;\n            font-weight: bold;\n            color: #252525;\n            background: #fafafa;\n        }\n\n        .table-image {\n            width: 50px;\n            height: 50px;\n            border-radius: 50%;\n        }\n\n        .el-table__header .el-table__cell > .cell {\n            white-space: nowrap;\n        }\n        .el-table__row {\n            height: 52px;\n        }\n\n        .el-table__empty-block {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n        }\n        .table-empty {\n            line-height: 30px;\n        }\n    }\n\n    .el-pagination {\n        display: flex;\n        justify-content: flex-end;\n        margin: 23px 0 10px;\n    }\n}\n\n.el-drawer {\n    .el-drawer__header {\n        padding: 15px 20px 14px;\n        margin-bottom: 0;\n        border-bottom: var(--panel-border);\n        span {\n            font-size: 17px;\n            color: #303133;\n        }\n    }\n\n    .el-drawer__footer {\n        border-top: var(--panel-border);\n    }\n    .el-select {\n        width: 100%;\n    }\n\n    .drawer-multiColumn-form {\n        display: flex;\n        flex-wrap: wrap;\n        .el-form-item {\n            width: 47%;\n            &:nth-child(2n-1) {\n                margin-right: 5%;\n            }\n        }\n    }\n}\n\n.el-dialog {\n    border-radius: 5px;\n\n    .el-dialog__header {\n        .el-dialog__title {\n            font-size: 17px;\n        }\n    }\n}\n\n.row-box {\n    display: flex;\n    flex-flow: wrap;\n    .el-card {\n        min-width: 100%;\n        height: 100%;\n        margin-right: 20px;\n        border: 0;\n    }\n}\n\n.el-avatar {\n    --el-avatar-bg-color: #ffffff !important;\n    border: 0.5px solid #eaeaea;\n    box-shadow: 0px 0px 2px 1px rgba(186, 186, 186, 0.22);\n    border-radius: 8px !important;\n    padding: 5px;\n}\n\n.el-card {\n    border: none !important;\n}\n\n.el-input-group__append {\n    button.el-button {\n        span {\n            vertical-align: text-top !important;\n        }\n    }\n}\n\n.el-input.is-disabled .el-input__wrapper {\n    --el-disabled-bg-color: var(--panel-main-bg-color-9);\n}\n\n.el-radio-button__inner {\n    [class*='el-icon'] + span {\n        margin-left: 6px;\n    }\n    span {\n        vertical-align: text-top !important;\n    }\n}\n\n.el-tabs--card > .el-tabs__header .el-tabs__item.is-active {\n    border-bottom-color: var(--panel-color-primary) !important;\n}\n\n.logo {\n    color: var(--el-color-primary);\n}\n\n.custom-input-textarea {\n    background-color: #f5f7fa !important;\n    color: var(--el-color-info) !important;\n}\n\n.custom-input-textarea:hover {\n    color: var(--el-color-primary) !important;\n    background-color: var(--el-color-primary-light-9) !important;\n    border-color: var(--el-button-border-color) !important;\n}\n\n.el-collapse-item__header {\n    color: var(--el-text-color-regular) !important;\n    border: 1px solid var(--panel-main-bg-color-10);\n    background-color: var(--panel-main-bg-color-10) !important;\n}\n"
  },
  {
    "path": "frontend/src/styles/index.scss",
    "content": "@use 'fit2cloud-ui-plus/src/styles/index.scss' as *;\n@use './element.scss';\n@use './element-dark.scss';\n@use './moblie.scss';\n@use './reset.scss';\n@use './var.scss';\n@use 'md-editor-v3/lib/style.css';\n"
  },
  {
    "path": "frontend/src/styles/mixins.scss",
    "content": "@mixin flex-row($justify: flex-start, $align: stretch) {\n    display: flex;\n    @if $justify != flex-start {\n        justify-content: $justify;\n    }\n    @if $align != stretch {\n        align-items: $align;\n    }\n}\n\n@mixin variant($color, $background-color, $border-color) {\n    color: $color;\n    background-color: $background-color;\n    border-color: $border-color;\n}\n"
  },
  {
    "path": "frontend/src/styles/moblie.scss",
    "content": ".mobile {\n    .monitor-tags {\n        position: inherit;\n        top: 13px;\n    }\n    .mobile-monitor-chart {\n        margin-top: 20px !important;\n    }\n    .search-button {\n        float: none !important;\n        .table-button {\n            display: inline-flex !important;\n        }\n    }\n\n    .app-card {\n        .app-button {\n            margin-right: 0 !important;\n        }\n    }\n    .install-card .a-detail {\n        .d-name {\n            height: auto !important;\n            .h-button {\n                float: none !important;\n                margin: 5px;\n            }\n        }\n        .d-button {\n            min-width: auto !important;\n        }\n        .d-description {\n            overflow: inherit !important;\n        }\n    }\n    .router_card_button {\n        padding: 2px 0;\n    }\n    .el-drawer.rtl {\n        width: 80% !important;\n    }\n    .site-form-wrapper {\n        width: 90% !important;\n        min-width: auto !important;\n        .el-form-item__label {\n            width: auto !important;\n        }\n    }\n    .moblie-form {\n        overflow: auto;\n        .el-input {\n            width: 350px;\n        }\n        .el-textarea__inner {\n            width: 350px;\n        }\n    }\n\n    .app-status {\n        font-size: 12px;\n        .el-card {\n            .status-content {\n                margin-left: 20px;\n            }\n        }\n    }\n\n    .mini-form-item {\n        width: 100% !important;\n    }\n\n    .database-status {\n        .title {\n            margin-left: 10px !important;\n        }\n        .content {\n            margin-left: 10px !important;\n        }\n    }\n\n    @media only screen and (max-width: 768px) {\n        .el-col-xs-24 {\n            margin-bottom: 10px;\n        }\n    }\n    .el-dialog {\n        --el-dialog-width: 80% !important;\n    }\n}\n"
  },
  {
    "path": "frontend/src/styles/reset.scss",
    "content": "html,\nbody,\n#app {\n    width: 100%;\n    height: 100%;\n    padding: 0;\n    margin: 0;\n}\n\n:-webkit-any(article, aside, nav, section) h1 {\n    font-size: 2em;\n}\n\n.el-switch--small .el-switch__core {\n    width: 36px;\n}\n\n.el-switch--small .el-switch__core::after {\n    width: 12px;\n    height: 12px;\n}\n\n.el-switch--small.is-checked .el-switch__core::after {\n    margin-left: -13px;\n}\n\n.el-alert__title {\n    display: flex;\n    align-items: center;\n}\n"
  },
  {
    "path": "frontend/src/styles/style.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n"
  },
  {
    "path": "frontend/src/styles/var.scss",
    "content": "$primary-color: var(--el-color-primary);\n"
  },
  {
    "path": "frontend/src/typings/global.d.ts",
    "content": "declare namespace Menu {\n    interface MenuOptions {\n        path: string;\n        title: string;\n        icon?: string;\n        isLink?: string;\n        close?: boolean;\n        children?: MenuOptions[];\n    }\n}\n\ndeclare type Recordable<T = any> = Record<string, T>;\n\ndeclare interface ViteEnv {\n    VITE_API_URL: string;\n    VITE_PORT: number;\n    VITE_OPEN: boolean;\n    VITE_GLOB_APP_TITLE: string;\n    VITE_DROP_CONSOLE: boolean;\n    VITE_PROXY_URL: string;\n    VITE_BUILD_GZIP: boolean;\n    VITE_REPORT: boolean;\n    PANEL_XPACK: boolean;\n}\n\ndeclare interface RouterButton {\n    label: string;\n    path?: string;\n    name?: string;\n    count?: number;\n}\n"
  },
  {
    "path": "frontend/src/typings/plugins.d.ts",
    "content": "declare module 'nprogress';\ndeclare module 'qs';\n"
  },
  {
    "path": "frontend/src/typings/window.d.ts",
    "content": "// * global\ndeclare global {\n    interface Navigator {\n        msSaveOrOpenBlob: (blob: Blob, fileName: string) => void;\n        browserLanguage: string;\n    }\n}\n\nexport {};\n"
  },
  {
    "path": "frontend/src/utils/agent.ts",
    "content": "import i18n from '@/lang';\n\nexport const getAgentProviderDisplayName = (provider: string, displayName?: string): string => {\n    if (provider === 'custom' || displayName === 'Custom') {\n        return i18n.global.t('container.custom');\n    }\n    if (provider === 'vllm' || displayName === 'vLLM') {\n        return 'vLLM';\n    }\n    return displayName || provider;\n};\n\nexport const buildDefaultAllowedOrigin = (systemIP: string, port?: number | string): string => {\n    const target = String(systemIP || '').trim();\n    if (!target || !port) {\n        return '';\n    }\n    const host = target.includes(':') && !target.startsWith('[') && !target.endsWith(']') ? `[${target}]` : target;\n    return `https://${host}:${port}`;\n};\n\nexport const normalizeAllowedOrigin = (value: string): string => {\n    const target = String(value || '').trim();\n    if (!target) {\n        throw new Error(i18n.global.t('aiTools.agents.allowedOriginsInvalid'));\n    }\n    let parsed: URL;\n    try {\n        parsed = new URL(target);\n    } catch (error) {\n        throw new Error(i18n.global.t('aiTools.agents.allowedOriginsInvalid'));\n    }\n    if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n        throw new Error(i18n.global.t('aiTools.agents.allowedOriginsInvalid'));\n    }\n    if (parsed.username || parsed.password || parsed.search || parsed.hash) {\n        throw new Error(i18n.global.t('aiTools.agents.allowedOriginsInvalid'));\n    }\n    if (parsed.pathname && parsed.pathname !== '/') {\n        throw new Error(i18n.global.t('aiTools.agents.allowedOriginsInvalid'));\n    }\n    if (!parsed.host) {\n        throw new Error(i18n.global.t('aiTools.agents.allowedOriginsInvalid'));\n    }\n    return `${parsed.protocol}//${parsed.host}`;\n};\n\nexport const parseAllowedOriginsInput = (value: string): string[] => {\n    const result: string[] = [];\n    const seen = new Set<string>();\n    for (const line of String(value || '').split(/\\r?\\n/)) {\n        const target = line.trim();\n        if (!target) {\n            continue;\n        }\n        const normalized = normalizeAllowedOrigin(target);\n        if (seen.has(normalized)) {\n            continue;\n        }\n        seen.add(normalized);\n        result.push(normalized);\n    }\n    return result;\n};\n\nexport const validateAllowedOriginsInput = (value: string): string => {\n    try {\n        const parsed = parseAllowedOriginsInput(value);\n        if (parsed.length === 0) {\n            return i18n.global.t('aiTools.agents.allowedOriginsRequired');\n        }\n        return '';\n    } catch (error: any) {\n        return error?.message || i18n.global.t('aiTools.agents.allowedOriginsInvalid');\n    }\n};\n"
  },
  {
    "path": "frontend/src/utils/app.ts",
    "content": "import { jumpToPath } from './util';\nimport router from '@/routers';\nimport { GlobalStore } from '@/store';\n\nconst globalStore = GlobalStore();\n\nexport const jumpToInstall = (type: string, key: string) => {\n    switch (type) {\n        case 'php':\n        case 'node':\n        case 'java':\n        case 'go':\n        case 'python':\n        case 'dotnet':\n            jumpToPath(router, '/websites/runtimes/' + type);\n            return true;\n    }\n    switch (key) {\n        case 'openclaw':\n            jumpToPath(router, '/ai/agents/agent');\n            return true;\n        case 'copaw':\n            router.push({\n                path: '/ai/agents/agent',\n                query: {\n                    uncached: 'true',\n                    open: 'create',\n                    agentType: 'copaw',\n                },\n            });\n            return true;\n        case 'vllm':\n            if (globalStore.isProductPro) {\n                router.push({\n                    path: '/ai/model/vllm',\n                    query: {\n                        uncached: 'true',\n                        open: 'create',\n                    },\n                });\n                return true;\n            }\n            return false;\n        case 'mysql-cluster':\n            jumpToPath(router, '/xpack/cluster/mysql');\n            return true;\n        case 'redis-cluster':\n            jumpToPath(router, '/xpack/cluster/redis');\n            return true;\n        case 'postgresql-cluster':\n            jumpToPath(router, '/xpack/cluster/postgres');\n            return true;\n    }\n    return false;\n};\n"
  },
  {
    "path": "frontend/src/utils/bus.ts",
    "content": "class Bus {\n    list: { [key: string]: Array<Function> };\n    constructor() {\n        this.list = {};\n    }\n\n    on(name: string, fn: Function) {\n        this.list[name] = this.list[name] || [];\n        this.list[name].push(fn);\n    }\n\n    emit(name: string, data?: any) {\n        if (this.list[name]) {\n            this.list[name].forEach((fn: Function) => {\n                fn(data);\n            });\n        }\n    }\n\n    off(name: string) {\n        if (this.list[name]) {\n            delete this.list[name];\n        }\n    }\n}\nexport default Bus;\n"
  },
  {
    "path": "frontend/src/utils/dashboardCache.ts",
    "content": "const DASHBOARD_CACHE_KEY = 'dashboardCache';\n\ntype CacheEntry = {\n    value: any;\n    expireAt: number;\n};\n\nconst readCache = (): Record<string, CacheEntry> | null => {\n    try {\n        const cacheRaw = localStorage.getItem(DASHBOARD_CACHE_KEY);\n        return cacheRaw ? JSON.parse(cacheRaw) : {};\n    } catch {\n        return null;\n    }\n};\n\nexport const getDashboardCache = (key: string) => {\n    const cache = readCache();\n    if (!cache) return null;\n    const entry = cache[key];\n    if (entry && entry.expireAt > Date.now()) {\n        return entry.value;\n    }\n    return null;\n};\n\nexport const setDashboardCache = (key: string, value: any, ttl: number) => {\n    try {\n        const cacheRaw = localStorage.getItem(DASHBOARD_CACHE_KEY);\n        const cache = cacheRaw ? JSON.parse(cacheRaw) : {};\n        cache[key] = {\n            value,\n            expireAt: Date.now() + ttl,\n        };\n        localStorage.setItem(DASHBOARD_CACHE_KEY, JSON.stringify(cache));\n    } catch {\n        localStorage.removeItem(DASHBOARD_CACHE_KEY);\n    }\n};\n\nexport const clearDashboardCache = () => {\n    localStorage.removeItem(DASHBOARD_CACHE_KEY);\n};\n\nexport const clearDashboardCacheByPrefix = (prefixes: string[]) => {\n    try {\n        const cacheRaw = localStorage.getItem(DASHBOARD_CACHE_KEY);\n        if (!cacheRaw) return;\n        const cache = JSON.parse(cacheRaw);\n        Object.keys(cache).forEach((key: string) => {\n            if (prefixes.some((prefix) => key.startsWith(prefix))) {\n                delete cache[key];\n            }\n        });\n        localStorage.setItem(DASHBOARD_CACHE_KEY, JSON.stringify(cache));\n    } catch {\n        clearDashboardCache();\n    }\n};\n\nexport { DASHBOARD_CACHE_KEY };\n"
  },
  {
    "path": "frontend/src/utils/docker.ts",
    "content": "import { isJson } from './util';\n\nexport function formatImageStdout(stdout: string) {\n    let lines = stdout.split('\\r\\n');\n    for (let i = 0; i < lines.length; i++) {\n        if (isJson(lines[i])) {\n            const data = JSON.parse(lines[i]);\n            if (data.errorDetail || data.error) {\n                lines[i] = data.errorDetail || data.errorDetail;\n                lines[i] = data.error || data.error;\n                continue;\n            }\n            if (data.stream) {\n                lines[i] = data.stream;\n                continue;\n            }\n            if (data.id) {\n                lines[i] = data.id + ': ' + data.status;\n            } else {\n                lines[i] = data.status;\n            }\n            if (data.progress) {\n                lines[i] = lines[i] + data.progress;\n            }\n        }\n    }\n    return lines.join('\\r\\n');\n}\n"
  },
  {
    "path": "frontend/src/utils/echarts.ts",
    "content": "import * as echarts from 'echarts/core';\n\nimport { LineChart, BarChart, PieChart, MapChart } from 'echarts/charts';\n\nimport {\n    TitleComponent,\n    TooltipComponent,\n    GridComponent,\n    LegendComponent,\n    VisualMapComponent,\n    GeoComponent,\n    PolarComponent,\n    DataZoomComponent,\n} from 'echarts/components';\n\nimport { CanvasRenderer } from 'echarts/renderers';\n\necharts.use([\n    LineChart,\n    BarChart,\n    PieChart,\n    MapChart,\n\n    TitleComponent,\n    TooltipComponent,\n    GridComponent,\n    LegendComponent,\n    VisualMapComponent,\n    GeoComponent,\n    PolarComponent,\n    DataZoomComponent,\n\n    CanvasRenderer,\n]);\n\nexport default echarts;\n"
  },
  {
    "path": "frontend/src/utils/get-env.ts",
    "content": "import fs from 'fs';\nimport path from 'path';\nimport dotenv from 'dotenv';\n\nexport function isDevFn(mode: string): boolean {\n    return mode === 'development';\n}\n\nexport function isProdFn(mode: string): boolean {\n    return mode === 'production';\n}\n\n/**\n * Whether to generate package preview\n */\nexport function isReportMode(): boolean {\n    return process.env.VITE_REPORT === 'true';\n}\n\n// Read all environment variable configuration files to process.env\nexport function wrapperEnv(envConf: Recordable): ViteEnv {\n    const ret: any = {};\n\n    for (const envName of Object.keys(envConf)) {\n        let realName = envConf[envName].replace(/\\\\n/g, '\\n');\n        realName = realName === 'true' ? true : realName === 'false' ? false : realName;\n\n        if (envName === 'VITE_PORT') {\n            realName = Number(realName);\n        }\n        if (envName === 'VITE_PROXY') {\n            try {\n                realName = JSON.parse(realName);\n            } catch (error) {}\n        }\n        ret[envName] = realName;\n        process.env[envName] = realName;\n    }\n    return ret;\n}\n\n/**\n * Get the environment variables starting with the specified prefix\n * @param match prefix\n * @param confFiles ext\n */\nexport function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.production']) {\n    let envConfig = {};\n    confFiles.forEach((item) => {\n        try {\n            const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));\n            envConfig = { ...envConfig, ...env };\n        } catch (error) {\n            console.error(`Error in parsing ${item}`, error);\n        }\n    });\n\n    Object.keys(envConfig).forEach((key) => {\n        const reg = new RegExp(`^(${match})`);\n        if (!reg.test(key)) {\n            Reflect.deleteProperty(envConfig, key);\n        }\n    });\n    return envConfig;\n}\n\n/**\n * Get user root directory\n * @param dir file path\n */\nexport function getRootPath(...dir: string[]) {\n    return path.resolve(process.cwd(), ...dir);\n}\n"
  },
  {
    "path": "frontend/src/utils/message.ts",
    "content": "import { ElMessage } from 'element-plus';\n\nlet messageDom: any = null;\nconst messageTypes: Array<string> = ['success', 'error', 'warning', 'info'];\nconst Message: any = (options) => {\n    if (messageDom) messageDom.close();\n    messageDom = ElMessage(options);\n};\nmessageTypes.forEach((type) => {\n    Message[type] = (options) => {\n        if (typeof options === 'string') options = { message: options };\n        options.type = type;\n        return Message(options);\n    };\n});\n\nexport const MsgSuccess = (message) => {\n    Message.success({\n        message: message,\n        type: 'success',\n        showClose: true,\n        duration: 3000,\n    });\n};\n\nexport const MsgInfo = (message) => {\n    Message.info({\n        message: message,\n        type: 'info',\n        showClose: true,\n        duration: 3000,\n    });\n};\n\nexport const MsgWarning = (message) => {\n    Message.warning({\n        message: message,\n        type: 'warning',\n        showClose: true,\n        duration: 3000,\n    });\n};\n\nexport const MsgError = (message) => {\n    Message.error({\n        message: message,\n        type: 'error',\n        showClose: true,\n        duration: 3000,\n    });\n};\n"
  },
  {
    "path": "frontend/src/utils/node.ts",
    "content": "import { listNodeOptions } from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\n\nconst globalStore = GlobalStore();\n\nexport const changeToLocal = async () => {\n    if (!globalStore.isMasterPro) {\n        setDefaultNodeInfo();\n        return;\n    }\n    await listNodeOptions('all')\n        .then((res) => {\n            if (!res) {\n                setDefaultNodeInfo();\n                return;\n            }\n            let nodes = res.data || [];\n            if (nodes.length === 0) {\n                setDefaultNodeInfo();\n                return;\n            }\n            for (const item of nodes) {\n                if (item.name === 'local') {\n                    globalStore.currentNode = 'local';\n                    globalStore.currentNodeAddr = item.addr;\n                    return;\n                }\n            }\n        })\n        .catch(() => {\n            setDefaultNodeInfo();\n        });\n};\n\nexport const setDefaultNodeInfo = () => {\n    globalStore.currentNode = 'local';\n    globalStore.currentNodeAddr = '127.0.0.1';\n};\n"
  },
  {
    "path": "frontend/src/utils/router.ts",
    "content": "import router from '@/routers';\nimport { TabsStore } from '@/store';\n\nexport const routerToName = async (name: string) => {\n    await router.push({ name: name });\n    tabStoreMiddleWare();\n};\n\nexport const routerToPath = async (path: string) => {\n    await router.push({ path: path });\n    tabStoreMiddleWare();\n};\n\nexport const routerToFileWithPath = async (pathItem: string) => {\n    await router.push({ name: 'File', query: { path: pathItem, uncached: 'true' } });\n    tabStoreMiddleWare();\n};\n\nexport const routerToNameWithQuery = async (name: string, query: any) => {\n    await router.push({ name: name, query: query });\n    tabStoreMiddleWare();\n};\n\nexport const routerToPathWithQuery = async (path: string, query: any) => {\n    await router.push({ path: path, query: query });\n    tabStoreMiddleWare();\n};\n\nexport const routerToNameWithParams = async (name: string, params: any) => {\n    await router.push({ name: name, params: params });\n    tabStoreMiddleWare();\n};\n\nconst tabStoreMiddleWare = () => {\n    try {\n        let route = router.currentRoute;\n        if (route.value.meta.ignoreTab) {\n            return;\n        }\n        const tabsStore = TabsStore();\n        tabsStore.addTab(route.value);\n        tabsStore.activeTabPath = route.value?.path;\n    } catch (error) {}\n};\n"
  },
  {
    "path": "frontend/src/utils/runtime.ts",
    "content": "import { Runtime } from '@/api/interface/runtime';\n\nexport function disabledButton(row: Runtime.Runtime, type: string): boolean {\n    switch (type) {\n        case 'stop':\n            return (\n                row.status === 'Recreating' ||\n                row.status === 'Stopped' ||\n                row.status === 'Building' ||\n                row.resource == 'local'\n            );\n        case 'start':\n            return (\n                row.status === 'Starting' ||\n                row.status === 'Recreating' ||\n                row.status === 'Running' ||\n                row.status === 'Building' ||\n                row.resource == 'local'\n            );\n        case 'restart':\n            return row.status === 'Recreating' || row.status === 'Building' || row.resource == 'local';\n        case 'edit':\n            return row.status === 'Recreating' || row.status === 'Building';\n        case 'extension':\n        case 'config':\n            return row.status != 'Running';\n        default:\n            return false;\n    }\n}\n"
  },
  {
    "path": "frontend/src/utils/shortcuts.ts",
    "content": "import i18n from '@/lang';\n\nexport const shortcuts = [\n    {\n        text: i18n.global.t('monitor.today'),\n        value: () => {\n            const end = new Date();\n            const start = new Date(new Date().setHours(0, 0, 0, 0));\n            return [start, end];\n        },\n    },\n    {\n        text: i18n.global.t('monitor.yesterday'),\n        value: () => {\n            const yesterday = new Date(new Date().getTime() - 3600 * 1000 * 24 * 1);\n            const end = new Date(yesterday.setHours(23, 59, 59, 999));\n            const start = new Date(yesterday.setHours(0, 0, 0, 0));\n            return [start, end];\n        },\n    },\n    {\n        text: i18n.global.t('monitor.lastNDay', [3]),\n        value: () => {\n            const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 3);\n            const end = new Date();\n            return [start, end];\n        },\n    },\n    {\n        text: i18n.global.t('monitor.lastNDay', [7]),\n        value: () => {\n            const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 7);\n            const end = new Date();\n            return [start, end];\n        },\n    },\n    {\n        text: i18n.global.t('monitor.lastNDay', [30]),\n        value: () => {\n            const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 30);\n            const end = new Date();\n            return [start, end];\n        },\n    },\n    {\n        text: i18n.global.t('monitor.lastNMonth', [3]),\n        value: () => {\n            const end = new Date();\n            const start = new Date(end);\n            start.setMonth(end.getMonth() - 3);\n            return [start, end];\n        },\n    },\n    {\n        text: i18n.global.t('monitor.lastHalfYear', [30]),\n        value: () => {\n            const end = new Date();\n            const start = new Date(end);\n            start.setMonth(end.getMonth() - 6);\n            return [start, end];\n        },\n    },\n];\n"
  },
  {
    "path": "frontend/src/utils/svg.ts",
    "content": "/**\n * @description 自定义 loading svg\n */\nexport const loadingSvg = `\n<path class=\"path\" d=\"\n\tM 30 15\n\tL 28 17\n\tM 25.61 25.61\n\tA 15 15, 0, 0, 1, 15 30\n\tA 15 15, 0, 1, 1, 27.99 7.5\n\tL 15 15\n\" style=\"stroke-width: 4px; fill: rgba(0, 0, 0, 0)\"/>\n`;\n"
  },
  {
    "path": "frontend/src/utils/theme.ts",
    "content": "export function setPrimaryColor(color: string) {\n    let setPrimaryColor: (arg0: string) => any;\n    const xpackModules = import.meta.glob('../xpack/utils/theme/tool.ts', { eager: true });\n    if (xpackModules['../xpack/utils/theme/tool.ts']) {\n        setPrimaryColor = xpackModules['../xpack/utils/theme/tool.ts']['setPrimaryColor'] || {};\n        return setPrimaryColor(color);\n    }\n}\n"
  },
  {
    "path": "frontend/src/utils/util.ts",
    "content": "import { AcmeAccountTypes, DNSTypes, KeyTypes } from '@/global/mimetype';\nimport i18n from '@/lang';\nimport useClipboard from 'vue-clipboard3';\nconst { toClipboard } = useClipboard();\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { v4 as uuidv4 } from 'uuid';\nimport JSEncrypt from 'jsencrypt';\nimport CryptoJS from 'crypto-js';\nimport { routerToPathWithQuery } from './router';\nimport { toUnicode } from 'punycode';\n\nexport function deepCopy<T>(obj: any): T {\n    let newObj: any;\n    try {\n        newObj = obj.push ? [] : {};\n    } catch (error) {\n        newObj = {};\n    }\n    for (let attr in obj) {\n        if (typeof obj[attr] === 'object') {\n            newObj[attr] = deepCopy(obj[attr]);\n        } else {\n            newObj[attr] = obj[attr];\n        }\n    }\n    return newObj;\n}\nexport function randomNum(min: number, max: number): number {\n    let num = Math.floor(Math.random() * (min - max) + max);\n    return num;\n}\n\nexport function debounce(func: Function, wait: number) {\n    let timeout: NodeJS.Timeout | null = null;\n    return function executedFunction(...args: any[]) {\n        const later = () => {\n            if (timeout) clearTimeout(timeout);\n            func(...args);\n        };\n        if (timeout) clearTimeout(timeout);\n        timeout = setTimeout(later, wait);\n    };\n}\n\nexport function getBrowserLang() {\n    let browserLang = navigator.language ? navigator.language : navigator.browserLanguage;\n    let defaultBrowserLang = '';\n    if (\n        browserLang.toLowerCase() === 'cn' ||\n        browserLang.toLowerCase() === 'zh' ||\n        browserLang.toLowerCase() === 'zh-cn'\n    ) {\n        defaultBrowserLang = 'zh';\n    } else {\n        defaultBrowserLang = 'en';\n    }\n    return defaultBrowserLang;\n}\n\nexport function loadUpTime(timeSince: string) {\n    if (!timeSince) {\n        return '';\n    }\n    const targetTime = new Date(timeSince);\n    const currentTime = new Date();\n    const uptime = (currentTime.getTime() - targetTime.getTime()) / 1000;\n    if (uptime <= 0) {\n        return '';\n    }\n    let days = Math.floor(uptime / 86400);\n    let hours = Math.floor((uptime % 86400) / 3600);\n    let minutes = Math.floor((uptime % 3600) / 60);\n    let seconds = Math.floor(uptime % 60);\n    if (days !== 0) {\n        return (\n            days +\n            i18n.global.t('commons.units.day') +\n            ' ' +\n            hours +\n            i18n.global.t('commons.units.hour') +\n            ' ' +\n            minutes +\n            i18n.global.t('commons.units.minute') +\n            ' ' +\n            seconds +\n            i18n.global.t('commons.units.second')\n        );\n    }\n    if (hours !== 0) {\n        return (\n            hours +\n            i18n.global.t('commons.units.hour') +\n            ' ' +\n            minutes +\n            i18n.global.t('commons.units.minute') +\n            ' ' +\n            seconds +\n            i18n.global.t('commons.units.second')\n        );\n    }\n    if (minutes !== 0) {\n        return minutes + i18n.global.t('commons.units.minute') + ' ' + seconds + i18n.global.t('commons.units.second');\n    }\n    return seconds + i18n.global.t('commons.units.second');\n}\n\n// 20250310\nexport function getCurrentDateFormatted() {\n    const now = new Date();\n    const year = now.getFullYear();\n    const month = String(now.getMonth() + 1).padStart(2, '0');\n    const day = String(now.getDate()).padStart(2, '0');\n    const hours = String(now.getHours()).padStart(2, '0');\n    const minutes = String(now.getMinutes()).padStart(2, '0');\n    const seconds = String(now.getSeconds()).padStart(2, '0');\n\n    return `${year}${month}${day}${hours}${minutes}${seconds}`;\n}\n\nexport function dateFormat(row: any, col: any, dataStr: any) {\n    const date = new Date(dataStr);\n    const y = date.getFullYear();\n    let m: string | number = date.getMonth() + 1;\n    m = m < 10 ? `0${String(m)}` : m;\n    let d: string | number = date.getDate();\n    d = d < 10 ? `0${String(d)}` : d;\n    let h: string | number = date.getHours();\n    h = h < 10 ? `0${String(h)}` : h;\n    let minute: string | number = date.getMinutes();\n    minute = minute < 10 ? `0${String(minute)}` : minute;\n    let second: string | number = date.getSeconds();\n    second = second < 10 ? `0${String(second)}` : second;\n    return `${String(y)}-${String(m)}-${String(d)}   ${String(h)}:${String(minute)}:${String(second)}`;\n}\n\n//2016-01-12\nexport function dateFormatSimple(dataStr: any) {\n    const date = new Date(dataStr);\n    const y = date.getFullYear();\n    let m: string | number = date.getMonth() + 1;\n    m = m < 10 ? `0${String(m)}` : m;\n    let d: string | number = date.getDate();\n    d = d < 10 ? `0${String(d)}` : d;\n    return `${String(y)}-${String(m)}-${String(d)}`;\n}\n\n// 20221013151302\nexport function dateFormatForName(dataStr: any) {\n    const date = new Date(dataStr);\n    const y = date.getFullYear();\n    let m: string | number = date.getMonth() + 1;\n    m = m < 10 ? `0${String(m)}` : m;\n    let d: string | number = date.getDate();\n    d = d < 10 ? `0${String(d)}` : d;\n    let h: string | number = date.getHours();\n    h = h < 10 ? `0${String(h)}` : h;\n    let minute: string | number = date.getMinutes();\n    minute = minute < 10 ? `0${String(minute)}` : minute;\n    let second: string | number = date.getSeconds();\n    second = second < 10 ? `0${String(second)}` : second;\n    return `${String(y)}${String(m)}${String(d)}${String(h)}${String(minute)}${String(second)}`;\n}\n\n// 10-13 \\n 15:13\nexport function dateFormatWithoutYear(dataStr: any) {\n    const date = new Date(dataStr);\n    let m: string | number = date.getMonth() + 1;\n    m = m < 10 ? `0${String(m)}` : m;\n    let d: string | number = date.getDate();\n    d = d < 10 ? `0${String(d)}` : d;\n    let h: string | number = date.getHours();\n    h = h < 10 ? `0${String(h)}` : h;\n    let minute: string | number = date.getMinutes();\n    minute = minute < 10 ? `0${String(minute)}` : minute;\n    let s: string | number = date.getSeconds();\n    s = s < 10 ? `0${String(s)}` : s;\n    return `${String(m)}-${String(d)}\\n${String(h)}:${String(minute)}:${String(s)}`;\n}\n\n// 20221013151302\nexport function dateFormatForSecond(dataStr: any) {\n    const date = new Date(dataStr);\n    let h: string | number = date.getHours();\n    h = h < 10 ? `0${String(h)}` : h;\n    let minute: string | number = date.getMinutes();\n    minute = minute < 10 ? `0${String(minute)}` : minute;\n    let second: string | number = date.getSeconds();\n    second = second < 10 ? `0${String(second)}` : second;\n    return `${String(h)}:${String(minute)}:${String(second)}`;\n}\n\nexport function getRandomStr(e: number): string {\n    const t = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';\n    const a = t.length;\n    let n = '';\n\n    for (let i = 0; i < e; i++) {\n        n += t.charAt(Math.floor(Math.random() * a));\n    }\n    return n;\n}\n\nexport function getDBName(e: number): string {\n    const t = 'abcdefhijkmnprstwxyz2345678';\n    const a = t.length;\n    let n = '';\n\n    for (let i = 0; i < e; i++) {\n        n += t.charAt(Math.floor(Math.random() * a));\n    }\n    return n;\n}\n\nexport function loadZero(i: number) {\n    return i < 10 ? '0' + i : '' + i;\n}\n\nexport function computeSize(size: number): string {\n    const num = 1024.0;\n    if (size < num) return size + ' B';\n    if (size < Math.pow(num, 2)) return formattedNumber((size / num).toFixed(2)) + ' KB';\n    if (size < Math.pow(num, 3)) return formattedNumber((size / Math.pow(num, 2)).toFixed(2)) + ' MB';\n    if (size < Math.pow(num, 4)) return formattedNumber((size / Math.pow(num, 3)).toFixed(2)) + ' GB';\n    return formattedNumber((size / Math.pow(num, 4)).toFixed(2)) + ' TB';\n}\n\nexport function computeSizeForDocker(size: number): string {\n    const num = 1024.0;\n    if (size < num) return size + ' B';\n    if (size < Math.pow(num, 2)) return formattedNumber((size / num).toFixed(2)) + ' KiB';\n    if (size < Math.pow(num, 3)) return formattedNumber((size / Math.pow(num, 2)).toFixed(2)) + ' MiB';\n    if (size < Math.pow(num, 4)) return formattedNumber((size / Math.pow(num, 3)).toFixed(2)) + ' GiB';\n    return formattedNumber((size / Math.pow(num, 4)).toFixed(2)) + ' TiB';\n}\n\nexport function computeSize2(size: number): string {\n    const num = 1000.0;\n    if (size < num) return size + ' B';\n    if (size < Math.pow(num, 2)) return formattedNumber((size / num).toFixed(2)) + ' KB';\n    if (size < Math.pow(num, 3)) return formattedNumber((size / Math.pow(num, 2)).toFixed(2)) + ' MB';\n    if (size < Math.pow(num, 4)) return formattedNumber((size / Math.pow(num, 3)).toFixed(2)) + ' GB';\n    return formattedNumber((size / Math.pow(num, 4)).toFixed(2)) + ' TB';\n}\n\nexport function computeCPU(size: number): string {\n    const num = 1000;\n    if (size < num) return size + ' ns';\n    if (size < Math.pow(num, 2)) return formattedNumber((size / num).toFixed(2)) + ' μs';\n    if (size < Math.pow(num, 3)) return formattedNumber((size / Math.pow(num, 2)).toFixed(2)) + ' ms';\n    return formattedNumber((size / Math.pow(num, 3)).toFixed(2)) + ' s';\n}\n\nexport function splitSize(size: number): any {\n    const num = 1024.0;\n    if (size < num) return { size: Number(size), unit: 'B' };\n    if (size < Math.pow(num, 2)) return { size: formattedNumber((size / num).toFixed(2)), unit: 'KB' };\n    if (size < Math.pow(num, 3))\n        return { size: formattedNumber((size / Number(Math.pow(num, 2).toFixed(2))).toFixed(2)), unit: 'MB' };\n    if (size < Math.pow(num, 4))\n        return { size: formattedNumber((size / Number(Math.pow(num, 3))).toFixed(2)), unit: 'GB' };\n    return { size: formattedNumber((size / Number(Math.pow(num, 4))).toFixed(2)), unit: 'TB' };\n}\n\nexport function formattedNumber(num: string) {\n    return num.endsWith('.00') ? Number(num.slice(0, -3)) : Number(num);\n}\n\nexport function computeSizeFromMB(size: number): string {\n    const num = 1024.0;\n    if (size < num) return size + ' MB';\n    if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + ' GB';\n    return (size / Math.pow(num, 3)).toFixed(2) + ' TB';\n}\n\nexport function computeSizeFromKB(size: number): string {\n    const num = 1024.0;\n    if (size < num) return size + ' KB';\n    if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + ' MB';\n    if (size < Math.pow(num, 3)) return (size / Math.pow(num, 2)).toFixed(2) + ' GB';\n    return (size / Math.pow(num, 3)).toFixed(2) + ' TB';\n}\nexport function computeSizeFromByte(size: number): string {\n    const num = 1024.0;\n    if (size < num) return size + ' B';\n    if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + ' KB';\n    if (size < Math.pow(num, 3)) return (size / Math.pow(num, 2)).toFixed(2) + ' MB';\n    if (size < Math.pow(num, 4)) return (size / Math.pow(num, 2)).toFixed(2) + ' GB';\n    return (size / Math.pow(num, 5)).toFixed(2) + ' TB';\n}\n\nexport function computeSizeFromKBs(size: number): string {\n    const num = 1024.0;\n    if (size < num) return size + ' KB/s';\n    if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + ' MB/s';\n    if (size < Math.pow(num, 3)) return (size / Math.pow(num, 2)).toFixed(2) + ' GB/s';\n    return (size / Math.pow(num, 3)).toFixed(2) + ' TB/s';\n}\n\nlet icons = new Map([\n    ['.zip', 'p-file-zip'],\n    ['.gz', 'p-file-zip'],\n    ['.tar.bz2', 'p-file-zip'],\n    ['.bz2', 'p-file-zip'],\n    ['.xz', 'p-file-zip'],\n    ['.tar.xz', 'p-file-zip'],\n    ['.tar', 'p-file-zip'],\n    ['.tar.gz', 'p-file-zip'],\n    ['.war', 'p-file-zip'],\n    ['.tgz', 'p-file-zip'],\n    ['.7z', 'p-file-zip'],\n    ['.rar', 'p-file-zip'],\n    ['.mp3', 'p-file-mp3'],\n    ['.svg', 'p-file-svg'],\n    ['.txt', 'p-file-txt'],\n    ['.html', 'p-file-html'],\n    ['.word', 'p-file-word'],\n    ['.ppt', 'p-file-ppt'],\n    ['.jpg', 'p-file-jpg'],\n    ['.jpeg', 'p-file-jpg'],\n    ['.png', 'p-file-png'],\n    ['.xlsx', 'p-file-excel'],\n    ['.doc', 'p-file-word'],\n    ['.xls', 'p-file-excel'],\n    ['.docx', 'p-file-word'],\n    ['.pdf', 'p-file-pdf'],\n    ['.bmp', 'p-file-png'],\n    ['.gif', 'p-file-png'],\n    ['.tiff', 'p-file-png'],\n    ['.ico', 'p-file-png'],\n    ['.webp', 'p-file-png'],\n    ['.mp4', 'p-file-video'],\n    ['.webm', 'p-file-video'],\n    ['.mov', 'p-file-video'],\n    ['.wmv', 'p-file-video'],\n    ['.mkv', 'p-file-video'],\n    ['.avi', 'p-file-video'],\n    ['.wma', 'p-file-video'],\n    ['.flv', 'p-file-video'],\n    ['.wav', 'p-file-mp3'],\n    ['.wma', 'p-file-mp3'],\n    ['.ape', 'p-file-mp3'],\n    ['.acc', 'p-file-mp3'],\n    ['.ogg', 'p-file-mp3'],\n    ['.flac', 'p-file-mp3'],\n]);\n\nexport function getIcon(extension: string): string {\n    if (icons.get(extension) != undefined) {\n        const icon = icons.get(extension);\n        return String(icon);\n    } else {\n        return 'p-file-normal';\n    }\n}\n\nexport function checkIp(value: string): boolean {\n    if (value === '') {\n        return true;\n    }\n    const reg =\n        /^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$/;\n    if (!reg.test(value) && value !== '') {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nexport function checkDomain(value: string): boolean {\n    if (value === '') {\n        return true;\n    }\n    const reg = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;\n    if (!reg.test(value) && value !== '') {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nexport function isDomain(value: string): boolean {\n    if (value === '') {\n        return true;\n    }\n    const reg = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i;\n    if (value !== '' && reg.test(value)) {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nexport function checkIpV4V6(value: string): boolean {\n    if (value === '') {\n        return true;\n    }\n    const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';\n    const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`;\n    const IPv4AddressRegExp = new RegExp(`^${IPv4AddressFormat}$`);\n    const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';\n    const IPv6AddressRegExp = new RegExp(\n        '^(' +\n            `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` +\n            `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` +\n            `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` +\n            `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` +\n            `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` +\n            `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` +\n            `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` +\n            `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` +\n            ')(%[0-9a-zA-Z-.:]{1,})?$',\n    );\n    if (!IPv4AddressRegExp.test(value) && !IPv6AddressRegExp.test(value) && value !== '') {\n        return true;\n    } else {\n        return false;\n    }\n}\n\nexport function checkIpV6(value: string): boolean {\n    if (value === '' || typeof value === 'undefined' || value == null) {\n        return true;\n    } else {\n        const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';\n        const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`;\n        const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';\n        const IPv6AddressRegExp = new RegExp(\n            '^(' +\n                `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` +\n                `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` +\n                `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` +\n                `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` +\n                `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` +\n                `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` +\n                `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` +\n                `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` +\n                ')(%[0-9a-zA-Z-.:]{1,})?$',\n        );\n        if (!IPv6AddressRegExp.test(value) && value !== '') {\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n\nexport function checkCidr(value: string): boolean {\n    if (value === '') {\n        return true;\n    }\n    const reg =\n        /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\\/([0-9]|[1-2][0-9]|3[0-2]))?$/;\n    if (!reg.test(value) && value !== '') {\n        return true;\n    } else {\n        return false;\n    }\n}\nexport function checkCidrV6(value: string): boolean {\n    if (value === '') {\n        return true;\n    }\n    if (checkIpV6(value.split('/')[0])) {\n        return true;\n    }\n    const reg = /^(?:[0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/;\n    if (!reg.test(value.split('/')[1])) {\n        return true;\n    }\n    return false;\n}\n\nexport function checkPort(value: string): boolean {\n    if (Number(value) <= 0) {\n        return true;\n    }\n    const reg = /^([1-9](\\d{0,3}))$|^([1-5]\\d{4})$|^(6[0-4]\\d{3})$|^(65[0-4]\\d{2})$|^(655[0-2]\\d)$|^(6553[0-5])$/;\n    return !reg.test(value) && value !== '';\n}\n\nexport function getProvider(provider: string): string {\n    switch (provider) {\n        case 'dnsAccount':\n            return i18n.global.t('website.dnsAccount');\n        case 'dnsManual':\n            return i18n.global.t('website.dnsManual');\n        case 'http':\n            return 'HTTP';\n        case 'selfSigned':\n            return i18n.global.t('ssl.selfSigned');\n        case 'fromMaster':\n            return i18n.global.t('ssl.fromMaster');\n        default:\n            return i18n.global.t('ssl.manualCreate');\n    }\n}\n\nexport function transferTimeToSecond(item: string): any {\n    if (item.indexOf('s') !== -1) {\n        return Number(item.replaceAll('s', ''));\n    }\n    if (item.indexOf('m') !== -1) {\n        return Number(item.replaceAll('m', '')) * 60;\n    }\n    if (item.indexOf('h') !== -1) {\n        return Number(item.replaceAll('h', '')) * 60 * 60;\n    }\n    if (item.indexOf('d') !== -1) {\n        return Number(item.replaceAll('d', '')) * 60 * 60 * 24;\n    }\n    return Number(item);\n}\nexport function splitTime(item: string): any {\n    if (item.indexOf('s') !== -1) {\n        return { time: Number(item.replaceAll('s', '')), unit: 's' };\n    }\n    if (item.indexOf('m') !== -1) {\n        return { time: Number(item.replaceAll('m', '')), unit: 'm' };\n    }\n    if (item.indexOf('h') !== -1) {\n        return { time: Number(item.replaceAll('h', '')), unit: 'h' };\n    }\n    if (item.indexOf('d') !== -1) {\n        return { time: Number(item.replaceAll('d', '')), unit: 'd' };\n    }\n    if (item.indexOf('y') !== -1) {\n        return { time: Number(item.replaceAll('y', '')), unit: 'y' };\n    }\n    return { time: Number(item), unit: 's' };\n}\nexport function transTimeUnit(val: string): any {\n    if (val.indexOf('s') !== -1) {\n        return val.replaceAll('s', i18n.global.t('commons.units.second'));\n    }\n    if (val.indexOf('m') !== -1) {\n        return val.replaceAll('m', i18n.global.t('commons.units.minute'));\n    }\n    if (val.indexOf('h') !== -1) {\n        return val.replaceAll('h', i18n.global.t('commons.units.hour'));\n    }\n    if (val.indexOf('d') !== -1) {\n        return val.replaceAll('d', i18n.global.t('commons.units.day'));\n    }\n    if (val.indexOf('y') !== -1) {\n        return val.replaceAll('y', i18n.global.t('commons.units.year'));\n    }\n    return val + i18n.global.t('commons.units.second');\n}\nexport function splitTimeFromSecond(item: number): any {\n    if (item < 60) return { timeItem: item, timeUnit: 's' };\n    if (item < 3600) return { timeItem: item / 60, timeUnit: 'm' };\n    return { timeItem: item / 3600, timeUnit: 'h' };\n}\n\nexport function splitHttp(url: string) {\n    if (url.indexOf('https://') != -1) {\n        return { proto: 'https', url: url.replaceAll('https://', '') };\n    }\n    if (url.indexOf('http://') != -1) {\n        return { proto: 'http', url: url.replaceAll('http://', '') };\n    }\n    return { proto: '', url: url };\n}\nexport function spliceHttp(proto: string, url: string) {\n    return proto + '://' + url.replaceAll('https://', '').replaceAll('http://', '');\n}\n\nexport function getAge(d1: string): string {\n    const dateBegin = new Date(d1);\n    const dateEnd = new Date();\n    const dateDiff = dateEnd.getTime() - dateBegin.getTime();\n    const dayDiff = Math.floor(dateDiff / (24 * 3600 * 1000));\n    const leave1 = dateDiff % (24 * 3600 * 1000);\n    const hours = Math.floor(leave1 / (3600 * 1000));\n    const leave2 = leave1 % (3600 * 1000);\n    const minutes = Math.floor(leave2 / (60 * 1000));\n\n    let res = '';\n    if (dayDiff > 0) {\n        res += String(dayDiff) + ' ' + i18n.global.t('commons.units.day', dayDiff) + ' ';\n        if (hours <= 0) {\n            return res;\n        }\n    }\n    if (hours > 0) {\n        res += String(hours) + ' ' + i18n.global.t('commons.units.hour', hours);\n        return res;\n    }\n    if (minutes > 0) {\n        res += String(minutes) + ' ' + i18n.global.t('commons.units.minute', minutes);\n        return res;\n    }\n    return i18n.global.t('app.less1Minute');\n}\n\nexport function isJson(str: string) {\n    try {\n        if (typeof JSON.parse(str) === 'object') {\n            return true;\n        }\n    } catch {\n        return false;\n    }\n}\n\nexport function toLowerCase(str: string) {\n    return str.toLowerCase();\n}\n\nexport function downloadFile(filePath: string, currentNode: string) {\n    let url = `${import.meta.env.VITE_API_URL as string}/files/download?operateNode=${currentNode}&`;\n    let path = encodeURIComponent(filePath);\n    window.open(url + 'path=' + path, '_blank');\n}\n\nexport function downloadWithContent(content: string, fileName: string) {\n    const downloadUrl = window.URL.createObjectURL(new Blob([content]));\n    const a = document.createElement('a');\n    a.style.display = 'none';\n    a.href = downloadUrl;\n    a.download = fileName;\n    const event = new MouseEvent('click');\n    a.dispatchEvent(event);\n}\nexport function getDateStr() {\n    let now: Date = new Date();\n\n    let year: number = now.getFullYear();\n    let month: number = now.getMonth() + 1;\n    let date: number = now.getDate();\n    let hours: number = now.getHours();\n    let minutes: number = now.getMinutes();\n    let seconds: number = now.getSeconds();\n\n    let timestamp: string = `${year}-${month < 10 ? '0' + month : month}-${date < 10 ? '0' + date : date}-${\n        hours < 10 ? '0' + hours : hours\n    }-${minutes < 10 ? '0' + minutes : minutes}-${seconds < 10 ? '0' + seconds : seconds}`;\n\n    return timestamp;\n}\n\nexport function getAccountName(type: string) {\n    for (const i of AcmeAccountTypes) {\n        if (i.value === type) {\n            return i.label;\n        }\n    }\n    return '';\n}\n\nexport function getKeyName(type: string) {\n    for (const i of KeyTypes) {\n        if (i.value === type) {\n            return i.label;\n        }\n    }\n    return '';\n}\n\nexport function getDNSName(type: string) {\n    for (const i of DNSTypes) {\n        if (i.value === type) {\n            return i.label;\n        }\n    }\n    return '';\n}\n\nexport async function copyText(content: string) {\n    try {\n        await toClipboard(content);\n        MsgSuccess(i18n.global.t('commons.msg.copySuccess'));\n    } catch (e) {\n        MsgError(i18n.global.t('commons.msg.copyFailed'));\n    }\n}\n\nexport function getAction(action: string) {\n    if (action == '') {\n        return '';\n    }\n    return i18n.global.t(`xpack.waf.${action}`);\n}\n\nexport function getLanguage() {\n    return localStorage.getItem('lang') || 'zh';\n}\n\nfunction normalizeAppLocaleKey(language: string) {\n    const localeMap: Record<string, string> = {\n        tw: 'zh-hant',\n        'zh-Hant': 'zh-hant',\n        'pt-BR': 'pt-br',\n        'es-ES': 'es-es',\n    };\n    return localeMap[language] || language.toLowerCase();\n}\n\nexport function getLabel(row: any) {\n    const language = localStorage.getItem('lang') || 'zh';\n    const lang = normalizeAppLocaleKey(language);\n    if (row.label && typeof row.label[lang] === 'string' && row.label[lang] !== '') {\n        return row.label[lang];\n    }\n    if (language == 'zh' || language == 'tw') {\n        return row.labelZh;\n    } else {\n        return row.labelEn;\n    }\n}\n\nexport function getDescription(row: any) {\n    const language = localStorage.getItem('lang') || 'zh';\n    const lang = normalizeAppLocaleKey(language);\n    if (row.description && typeof row.description[lang] === 'string' && row.description[lang] !== '') {\n        return row.description[lang];\n    }\n    return '';\n}\n\nexport function emptyLineFilter(str: string, spilt: string) {\n    let list = str.split(spilt);\n    let results = [];\n    for (let i = 0; i < list.length; i++) {\n        if (list[i].trim() !== '') {\n            results.push(list[i]);\n        }\n    }\n    return results.join(spilt);\n}\n\nlet fileTypes = {\n    image: ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.ico', '.svg', '.webp'],\n    compress: ['.zip', '.rar', '.gz', '.war', '.tgz', '.7z', '.tar.gz', '.tar', '.bz2', '.xz', '.tar.bz2', '.tar.xz'],\n    video: ['.mp4', '.webm', '.mov', '.wmv', '.mkv', '.avi', '.wma', '.flv'],\n    audio: ['.mp3', '.wav', '.wma', '.ape', '.acc', '.ogg', '.flac'],\n    pdf: ['.pdf'],\n    word: ['.doc', '.docx'],\n    excel: ['.xls', '.xlsx'],\n    text: ['.iso', '.tiff', '.exe', '.so', '.bz', '.dmg', '.apk', '.pptx', '.ppt', '.xlsb'],\n};\n\nexport const getFileType = (extension: string) => {\n    let type = 'text';\n    Object.entries(fileTypes).forEach(([key, extensions]) => {\n        if (extensions.includes(extension.toLowerCase())) {\n            type = key;\n        }\n    });\n    return type;\n};\n\nexport const newUUID = () => {\n    return uuidv4();\n};\n\nexport const escapeProxyURL = (url: string): string => {\n    const encodeMap: { [key: string]: string } = {\n        ':': '%%3A',\n        '/': '%%2F',\n        '?': '%%3F',\n        '#': '%%23',\n        '[': '%%5B',\n        ']': '%%5D',\n        '@': '%%40',\n        '!': '%%21',\n        $: '%%24',\n        '&': '%%26',\n        \"'\": '%%27',\n        '(': '%%28',\n        ')': '%%29',\n        '*': '%%2A',\n        '+': '%%2B',\n        ',': '%%2C',\n        ';': '%%3B',\n        '=': '%%3D',\n        '%': '%%25',\n    };\n\n    return url.replace(/[\\/:?#[\\]@!$&'()*+,;=%~]/g, (match) => encodeMap[match] || match);\n};\n\nexport function getRuntimeLabel(type: string) {\n    if (type == 'appstore') {\n        return i18n.global.t('menu.apps');\n    }\n    if (type == 'local') {\n        return i18n.global.t('commons.table.local');\n    }\n    return '';\n}\n\nfunction getCookie(name: string) {\n    const value = `; ${document.cookie}`;\n    const parts = value.split(`; ${name}=`);\n    if (parts.length === 2) return parts.pop().split(';').shift();\n}\n\nfunction rsaEncrypt(data: string, publicKey: string) {\n    if (!data) {\n        return data;\n    }\n    const jsEncrypt = new JSEncrypt();\n    jsEncrypt.setPublicKey(publicKey);\n    return jsEncrypt.encrypt(data);\n}\n\nfunction aesEncrypt(data: string, key: string) {\n    const keyBytes = CryptoJS.enc.Utf8.parse(key);\n    const iv = CryptoJS.lib.WordArray.random(16);\n    const encrypted = CryptoJS.AES.encrypt(data, keyBytes, {\n        iv: iv,\n        mode: CryptoJS.mode.CBC,\n        padding: CryptoJS.pad.Pkcs7,\n    });\n    return iv.toString(CryptoJS.enc.Base64) + ':' + encrypted.toString();\n}\n\nfunction urlDecode(value: string): string {\n    return decodeURIComponent(value.replace(/\\+/g, ' '));\n}\n\nfunction generateAESKey(): string {\n    const keyLength = 16;\n    const randomBytes = new Uint8Array(keyLength);\n    crypto.getRandomValues(randomBytes);\n    return Array.from(randomBytes)\n        .map((b) => b.toString(16).padStart(2, '0'))\n        .join('');\n}\n\nexport const encryptPassword = (password: string) => {\n    if (!password) {\n        return '';\n    }\n    let rsaPublicKeyText = getCookie('panel_public_key');\n    if (!rsaPublicKeyText) {\n        return password;\n    }\n    rsaPublicKeyText = urlDecode(rsaPublicKeyText);\n\n    const aesKey = generateAESKey();\n    rsaPublicKeyText = rsaPublicKeyText.replaceAll('\"', '');\n    const rsaPublicKey = atob(rsaPublicKeyText);\n    const keyCipher = rsaEncrypt(aesKey, rsaPublicKey);\n    const passwordCipher = aesEncrypt(password, aesKey);\n    return `${keyCipher}:${passwordCipher}`;\n};\n\nexport async function loadJson(lang: string): Promise<Object> {\n    try {\n        lang = lang == 'zh' ? 'zh' : 'en';\n        const jsonModule = await import(`@/assets/json/china/${lang}.json`);\n        return jsonModule.default;\n    } catch (error) {\n        throw new Error(`Language file not found: ${lang}`);\n    }\n}\n\nexport const jumpToPath = (router: any, path: string) => {\n    routerToPathWithQuery(path, { uncached: 'true' });\n};\n\nexport const toLink = (link: string) => {\n    const ipv6Regex = /^https?:\\/\\/([a-f0-9:]+):(\\d+)(\\/?.*)?$/i;\n    try {\n        if (ipv6Regex.test(link)) {\n            const match = link.match(ipv6Regex);\n            if (match) {\n                const ipv6 = match[1];\n                const port = match[2];\n                const path = match[3] || '';\n                link = `${link.startsWith('https') ? 'https' : 'http'}://[${ipv6}]:${port}${path}`;\n            }\n        }\n        window.open(link, '_blank');\n    } catch (e) {}\n};\n\nexport const preloadImage = (url: string): Promise<string> => {\n    return new Promise((resolve) => {\n        const img = new Image();\n        img.onload = () => resolve(url);\n        img.onerror = () => resolve(url);\n        img.src = url;\n    });\n};\n\nexport const isSensitiveLinuxPath = (path) => {\n    let sensitivePath = [\n        '/',\n        '/bin',\n        '/sbin',\n        '/usr/bin',\n        '/usr/sbin',\n        '/usr/local/bin',\n        '/etc',\n        '/lib',\n        '/lib64',\n        '/usr/lib',\n        '/home',\n        '/tmp',\n        '/var',\n        '/dev',\n        '/proc',\n        '/sys',\n    ];\n    return sensitivePath.indexOf(path) !== -1;\n};\n\nconst convertTypes = ['image', 'video', 'audio'] as const;\ntype ConvertType = (typeof convertTypes)[number];\n\nexport function isConvertible(extension: string, mimeType: string): boolean {\n    return convertTypes.includes(getFileType(extension) as ConvertType) && /^(image|audio|video)\\//.test(mimeType);\n}\n\nfunction compareById(a, b) {\n    return a.sort - b.sort;\n}\n\nexport function sortMenu(arr) {\n    if (!arr || arr.length === 0) {\n        return;\n    }\n    arr.forEach((item) => {\n        if (item.children && Array.isArray(item.children)) {\n            item.children.sort(compareById);\n        }\n    });\n\n    arr.sort(compareById);\n}\n\nexport function isPunycoded(domain: string): boolean {\n    return domain.includes('xn--');\n}\n\nexport function GetPunyCodeDomain(domain: string): string {\n    if (!domain || typeof domain !== 'string') {\n        return '';\n    }\n\n    const lowerDomain = domain.toLowerCase();\n    if (!lowerDomain.includes('xn--')) {\n        return '';\n    }\n\n    try {\n        const decoded = toUnicode(domain);\n\n        if (decoded === domain) {\n            return '';\n        }\n\n        return decoded;\n    } catch (error) {\n        return '';\n    }\n}\n\nexport function base64UrlToBuffer(base64url: string): ArrayBuffer {\n    let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');\n    const pad = base64.length % 4;\n    if (pad) {\n        base64 += '='.repeat(4 - pad);\n    }\n    const binary = window.atob(base64);\n    const bytes = new Uint8Array(binary.length);\n    for (let i = 0; i < binary.length; i++) {\n        bytes[i] = binary.charCodeAt(i);\n    }\n    return bytes.buffer;\n}\n\nexport function bufferToBase64Url(buffer: ArrayBuffer): string {\n    const bytes = new Uint8Array(buffer);\n    let binary = '';\n    bytes.forEach((byte) => {\n        binary += String.fromCharCode(byte);\n    });\n    const base64 = window.btoa(binary);\n    return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n"
  },
  {
    "path": "frontend/src/utils/version.ts",
    "content": "export function compareVersion(version1: string, version2: string): boolean {\n    const v1s = extractNumbers(version1);\n    const v2s = extractNumbers(version2);\n\n    const maxLen = Math.max(v1s.length, v2s.length);\n    v1s.push(...new Array(maxLen - v1s.length).fill('0'));\n    v2s.push(...new Array(maxLen - v2s.length).fill('0'));\n\n    for (let i = 0; i < maxLen; i++) {\n        const v1 = parseInt(v1s[i], 10);\n        const v2 = parseInt(v2s[i], 10);\n        if (v1 !== v2) {\n            return v1 > v2;\n        }\n    }\n    return true;\n}\n\nfunction extractNumbers(version: string): string[] {\n    const numbers: string[] = [];\n    let start = -1;\n    for (let i = 0; i < version.length; i++) {\n        if (isDigit(version[i])) {\n            if (start === -1) {\n                start = i;\n            }\n        } else {\n            if (start !== -1) {\n                numbers.push(version.slice(start, i));\n                start = -1;\n            }\n        }\n    }\n    if (start !== -1) {\n        numbers.push(version.slice(start));\n    }\n    return numbers;\n}\n\nfunction isDigit(char: string): boolean {\n    return /^\\d$/.test(char);\n}\n"
  },
  {
    "path": "frontend/src/utils/xpack.ts",
    "content": "import { getLicenseStatus, getMasterLicenseStatus, getSettingInfo } from '@/api/modules/setting';\nimport { useTheme } from '@/global/use-theme';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\nconst { switchTheme } = useTheme();\nimport faviconUrl from '@/assets/images/favicon.svg';\n\nexport function resetXSetting() {\n    globalStore.themeConfig.title = '';\n    globalStore.themeConfig.logo = '';\n    globalStore.themeConfig.logoWithText = '';\n    globalStore.themeConfig.favicon = '';\n    globalStore.watermark = null;\n    globalStore.watermarkShow = false;\n    globalStore.masterAlias = '';\n}\n\nasync function getColoredFavicon(url: string, color: string) {\n    const res = await fetch(url);\n    let svgText = await res.text();\n    svgText = svgText.replace(/fill=([\"'])(.*?)\\1/g, `fill=\"${color}\"`);\n    return `data:image/svg+xml,${encodeURIComponent(svgText)}`;\n}\n\nexport async function initFavicon() {\n    document.title = globalStore.themeConfig.panelName;\n    const favicon = globalStore.themeConfig.favicon;\n    const isPro = globalStore.isMasterProductPro;\n    const themeColor = globalStore.themeConfig.primary;\n    const customFaviconUrl = `/api/v2/images/favicon?t=${Date.now()}`;\n    const fallbackSvg = isPro ? await getColoredFavicon(faviconUrl, themeColor) : '/public/favicon.png';\n    const setLink = (href: string) => {\n        let link = document.querySelector(\"link[rel*='icon']\") as HTMLLinkElement;\n        if (!link) {\n            link = document.createElement('link');\n            link.rel = 'shortcut icon';\n            link.type = 'image/x-icon';\n            document.head.appendChild(link);\n        }\n        link.href = href;\n    };\n\n    if (favicon) {\n        const testImg = new Image();\n        testImg.onload = () => setLink(customFaviconUrl);\n        testImg.onerror = () => setLink(fallbackSvg);\n        testImg.src = customFaviconUrl;\n    } else {\n        setLink(fallbackSvg);\n    }\n}\n\nexport async function getXpackSetting() {\n    let searchXSetting;\n    const xpackModules = import.meta.glob('../xpack/api/modules/setting.ts', { eager: true });\n    if (xpackModules['../xpack/api/modules/setting.ts']) {\n        searchXSetting = xpackModules['../xpack/api/modules/setting.ts']['searchXSetting'] || {};\n        const res = await searchXSetting();\n        if (!res) {\n            initFavicon();\n            resetXSetting();\n            return;\n        }\n        initFavicon();\n        return res;\n    }\n}\n\nconst loadDataFromDB = async () => {\n    const res = await getSettingInfo();\n    document.title = res.data.panelName;\n    globalStore.entrance = res.data.securityEntrance;\n    globalStore.setOpenMenuTabs(res.data.menuTabs === 'Enable');\n};\n\nexport async function loadProductProFromDB() {\n    const res = await getLicenseStatus();\n    if (!res || !res.data) {\n        globalStore.isProductPro = false;\n    } else {\n        globalStore.isProductPro = res.data.status === 'Bound';\n        if (globalStore.isProductPro) {\n            globalStore.productProExpires = Number(res.data.productPro);\n        }\n    }\n}\n\nexport async function loadMasterProductProFromDB() {\n    const res = await getMasterLicenseStatus();\n    if (!res || !res.data) {\n        globalStore.isMasterProductPro = false;\n    } else {\n        globalStore.isMasterProductPro = res.data.status === 'Bound';\n    }\n    switchTheme();\n    initFavicon();\n    loadDataFromDB();\n}\n\nexport async function getXpackSettingForTheme() {\n    let searchXSetting;\n    const xpackModules = import.meta.glob('../xpack/api/modules/setting.ts', { eager: true });\n    if (xpackModules['../xpack/api/modules/setting.ts']) {\n        searchXSetting = xpackModules['../xpack/api/modules/setting.ts']['searchXSetting'] || {};\n        const res2 = await searchXSetting();\n        if (res2) {\n            globalStore.themeConfig.title = res2.data?.title;\n            globalStore.themeConfig.logo = res2.data?.logo;\n            globalStore.themeConfig.logoWithText = res2.data?.logoWithText;\n            globalStore.themeConfig.favicon = res2.data?.favicon;\n            globalStore.themeConfig.loginImage = res2.data?.loginImage;\n            globalStore.themeConfig.loginBgType = res2.data?.loginBgType;\n            globalStore.themeConfig.loginBackground = res2.data?.loginBackground;\n            globalStore.themeConfig.loginBtnLinkColor = res2.data?.loginBtnLinkColor;\n            globalStore.themeConfig.themeColor = res2.data?.themeColor;\n            globalStore.masterAlias = res2.data.masterAlias;\n            if (res2.data?.theme) {\n                globalStore.themeConfig.theme = res2.data.theme;\n            }\n            globalStore.watermarkShow = res2.data.watermarkShow === 'Enable';\n            try {\n                globalStore.watermark = JSON.parse(res2.data.watermark);\n            } catch {\n                globalStore.watermark = null;\n            }\n        } else {\n            resetXSetting();\n        }\n    }\n    switchTheme();\n    initFavicon();\n}\n\nexport async function updateXpackSettingByKey(key: string, value: string) {\n    let updateXSettingByKey;\n    const xpackModules = import.meta.glob('../xpack/api/modules/setting.ts', { eager: true });\n    if (xpackModules['../xpack/api/modules/setting.ts']) {\n        updateXSettingByKey = xpackModules['../xpack/api/modules/setting.ts']['updateXSettingByKey'] || {};\n        return updateXSettingByKey(key, value);\n    }\n}\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/add/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.create')\" size=\"large\" @close=\"handleClose\">\n        <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n            <el-card class=\"form-card\">\n                <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                    <el-input v-model=\"form.name\" />\n                </el-form-item>\n                <el-form-item :label=\"`${$t('aiTools.agents.agents')}${$t('commons.table.type')}`\" prop=\"agentType\">\n                    <el-select v-model=\"form.agentType\" @change=\"handleAgentTypeChange\">\n                        <el-option :label=\"$t('aiTools.agents.openclawType')\" value=\"openclaw\" />\n                        <el-option :label=\"$t('aiTools.agents.copawType')\" value=\"copaw\" />\n                    </el-select>\n                </el-form-item>\n                <el-form-item :label=\"$t('aiTools.agents.appVersion')\" prop=\"appVersion\">\n                    <el-select v-model=\"form.appVersion\" filterable>\n                        <el-option v-for=\"item in versions\" :key=\"item\" :label=\"item\" :value=\"item\" />\n                    </el-select>\n                </el-form-item>\n                <el-form-item :label=\"$t('aiTools.agents.webuiPort')\" prop=\"webUIPort\">\n                    <el-input-number v-model=\"form.webUIPort\" :min=\"1\" :max=\"65535\" />\n                </el-form-item>\n                <el-form-item\n                    v-if=\"form.agentType === 'openclaw'\"\n                    :label=\"$t('aiTools.agents.allowedOrigins')\"\n                    prop=\"allowedOrigins\"\n                >\n                    <el-input\n                        v-model=\"form.allowedOrigins\"\n                        type=\"textarea\"\n                        :rows=\"3\"\n                        :placeholder=\"$t('aiTools.agents.allowedOriginsPlaceholder')\"\n                        @input=\"handleAllowedOriginsInput\"\n                    />\n                    <span class=\"input-help\">\n                        {{ $t('aiTools.agents.allowedOriginsHelper') }}\n                    </span>\n                </el-form-item>\n            </el-card>\n            <el-card class=\"form-card\" v-if=\"form.agentType === 'openclaw'\">\n                <el-form-item :label=\"$t('aiTools.agents.provider')\" prop=\"provider\">\n                    <el-select v-model=\"form.provider\" @change=\"handleProviderChange\">\n                        <el-option\n                            v-for=\"item in providerOptions\"\n                            :key=\"item.value\"\n                            :label=\"item.label\"\n                            :value=\"item.value\"\n                        >\n                            <div class=\"option-row\">\n                                <span class=\"option-label\">{{ item.label }}</span>\n                                <el-tag\n                                    size=\"small\"\n                                    :type=\"providerAccountCount[item.value] > 0 ? 'success' : 'info'\"\n                                    class=\"option-tag\"\n                                >\n                                    {{ $t('aiTools.agents.accountCount', [providerAccountCount[item.value] || 0]) }}\n                                </el-tag>\n                            </div>\n                        </el-option>\n                    </el-select>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('aiTools.agents.account')\" prop=\"accountId\">\n                    <el-select v-model=\"form.accountId\" @change=\"handleAccountChange\">\n                        <el-option v-for=\"item in accountOptions\" :key=\"item.id\" :label=\"item.name\" :value=\"item.id\" />\n                    </el-select>\n                    <span class=\"input-help\">\n                        {{ $t('aiTools.agents.noAccountHint') }}\n                        <el-button type=\"primary\" link class=\"inline-link\" @click=\"openAccountCreate\">\n                            {{ $t('commons.button.create') }}\n                        </el-button>\n                    </span>\n                </el-form-item>\n                <el-form-item :label=\"$t('aiTools.model.model')\" prop=\"model\">\n                    <el-select v-model=\"form.model\" filterable>\n                        <el-option v-for=\"item in filteredModels\" :key=\"item.id\" :label=\"item.name\" :value=\"item.id\" />\n                    </el-select>\n                    <span class=\"input-help\">{{ $t('aiTools.agents.accountModelsHelper') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('aiTools.agents.baseUrl')\" v-if=\"form.accountId\" prop=\"baseURL\">\n                    <el-input v-model=\"form.baseURL\" disabled />\n                </el-form-item>\n                <el-form-item :label=\"$t('aiTools.agents.token')\">\n                    <el-input v-model=\"form.token\" disabled>\n                        <template #append>\n                            <CopyButton :content=\"form.token\" />\n                        </template>\n                    </el-input>\n                </el-form-item>\n            </el-card>\n            <el-card class=\"form-card\">\n                <AdvancedSetting :form=\"form\" />\n            </el-card>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <AccountAddDialog ref=\"accountAddRef\" @search=\"handleAccountCreated\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, reactive, ref, watch } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport { createAgent, getAgentProviders, pageAgentAccounts } from '@/api/modules/ai';\nimport { AI } from '@/api/interface/ai';\nimport { getAppByKey, getAppDetail } from '@/api/modules/app';\nimport { getAgentSettingByKey } from '@/api/modules/setting';\nimport { getRandomStr, newUUID } from '@/utils/util';\nimport {\n    buildDefaultAllowedOrigin,\n    getAgentProviderDisplayName,\n    parseAllowedOriginsInput,\n    validateAllowedOriginsInput,\n} from '@/utils/agent';\nimport { App } from '@/api/interface/app';\nimport AdvancedSetting from '@/components/advanced-setting/index.vue';\nimport AccountAddDialog from '@/views/ai/agents/model/add/index.vue';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\n\nconst emit = defineEmits(['search', 'task']);\n\nconst open = ref(false);\nconst formRef = ref<FormInstance>();\nconst versions = ref<string[]>([]);\nconst accountOptions = ref<AI.AgentAccountItem[]>([]);\nconst providerOptions = ref<Array<{ label: string; value: string }>>([]);\nconst providerModels = ref<Record<string, AI.ProviderModelInfo[]>>({});\nconst providerAccountCount = ref<Record<string, number>>({});\nconst appInfo = ref<App.AppDTO>();\nconst accountAddRef = ref();\nconst systemIP = ref('');\nconst lastAutoAllowedOrigins = ref('');\nconst allowedOriginsAutoFilled = ref(true);\nconst { isIntl } = useGlobalStore();\n\nconst form = reactive({\n    name: '',\n    agentType: 'openclaw' as 'openclaw' | 'copaw',\n    appVersion: '',\n    webUIPort: 18789,\n    allowedOrigins: '',\n    provider: 'deepseek',\n    accountId: undefined as unknown as number,\n    model: '',\n    baseURL: '',\n    token: '',\n    advanced: true,\n    containerName: '',\n    allowPort: true,\n    specifyIP: '',\n    restartPolicy: 'unless-stopped',\n    cpuQuota: 0,\n    memoryLimit: 0,\n    memoryUnit: 'M',\n    pullImage: true,\n    editCompose: false,\n    dockerCompose: '',\n});\n\nconst rules = reactive({\n    name: [Rules.requiredInput],\n    agentType: [Rules.requiredSelect],\n    appVersion: [Rules.requiredSelect],\n    webUIPort: [Rules.requiredInput],\n    allowedOrigins: [\n        Rules.requiredInput,\n        {\n            validator: (_rule: any, value: any, callback: (error?: Error) => void) => {\n                const message = validateAllowedOriginsInput(String(value || ''));\n                if (message) {\n                    callback(new Error(message));\n                    return;\n                }\n                callback();\n            },\n            trigger: 'blur',\n        },\n    ],\n    provider: [Rules.requiredSelect],\n    accountId: [Rules.requiredSelect],\n    model: [Rules.requiredInput],\n    containerName: [Rules.containerName],\n    restartPolicy: [Rules.requiredSelect],\n    cpuQuota: [checkNumberRange(0, 99999)],\n    memoryLimit: [checkNumberRange(0, 9999999999)],\n    specifyIP: [Rules.ipv4orV6],\n});\n\nconst filteredModels = computed(() => {\n    const selected = accountOptions.value.find((item) => item.id === form.accountId);\n    return selected?.models || [];\n});\n\nconst syncAllowedOriginsWithDefault = (force = false) => {\n    if (form.agentType !== 'openclaw') {\n        return;\n    }\n    const defaultOrigin = buildDefaultAllowedOrigin(systemIP.value, form.webUIPort);\n    if (!force && !allowedOriginsAutoFilled.value && form.allowedOrigins !== lastAutoAllowedOrigins.value) {\n        return;\n    }\n    form.allowedOrigins = defaultOrigin;\n    lastAutoAllowedOrigins.value = defaultOrigin;\n    allowedOriginsAutoFilled.value = true;\n};\n\nconst loadSystemIP = async () => {\n    try {\n        const res = await getAgentSettingByKey('SystemIP');\n        systemIP.value = String(res.data || '').trim();\n    } catch (error) {\n        systemIP.value = '';\n    }\n};\n\nconst loadVersions = async (appKey: 'openclaw' | 'copaw') => {\n    const res = await getAppByKey(appKey);\n    appInfo.value = res.data;\n    versions.value = res.data.versions || [];\n    if (!form.appVersion && versions.value.length > 0) {\n        form.appVersion = versions.value[0];\n    }\n};\n\nconst loadCompose = async () => {\n    if (!appInfo.value || !form.appVersion) {\n        return;\n    }\n    const res = await getAppDetail(appInfo.value.id, form.appVersion, appInfo.value.type);\n    form.dockerCompose = res.data.dockerCompose || '';\n};\n\nconst loadProviders = async () => {\n    if (form.agentType !== 'openclaw') {\n        providerOptions.value = [];\n        providerModels.value = {};\n        return;\n    }\n    const res = await getAgentProviders();\n    const data = res.data || [];\n    const blockedProviders = new Set(['ark-coding-plan', 'bailian-coding-plan']);\n    const filteredData = isIntl.value ? data.filter((item) => !blockedProviders.has(item.provider)) : data;\n    providerOptions.value = filteredData.map((item) => ({\n        value: item.provider,\n        label: getAgentProviderDisplayName(item.provider, item.displayName),\n    }));\n    providerModels.value = filteredData.reduce((acc, item) => {\n        acc[item.provider] = item.models || [];\n        return acc;\n    }, {} as Record<string, AI.ProviderModelInfo[]>);\n    if (!providerOptions.value.find((item) => item.value === form.provider) && providerOptions.value.length > 0) {\n        form.provider = providerOptions.value[0].value;\n    }\n    await loadProviderAccountCounts(providerOptions.value.map((item) => item.value));\n    setDefaultModel();\n};\n\nconst loadProviderAccountCounts = async (providers: string[]) => {\n    const tasks = providers.map(async (provider) => {\n        const req: AI.AgentAccountSearch = {\n            page: 1,\n            pageSize: 1,\n            provider: provider,\n            name: '',\n        };\n        const res = await pageAgentAccounts(req);\n        providerAccountCount.value[provider] = res.data.total || 0;\n    });\n    await Promise.all(tasks);\n};\n\nconst loadAccounts = async () => {\n    if (form.agentType !== 'openclaw') {\n        accountOptions.value = [];\n        return;\n    }\n    if (!form.provider) {\n        accountOptions.value = [];\n        return;\n    }\n    const req: AI.AgentAccountSearch = {\n        page: 1,\n        pageSize: 200,\n        provider: form.provider,\n        name: '',\n    };\n    const res = await pageAgentAccounts(req);\n    accountOptions.value = res.data.items || [];\n    providerAccountCount.value[form.provider] = res.data.total || accountOptions.value.length;\n    if (accountOptions.value.length > 0) {\n        form.accountId = accountOptions.value[0].id;\n        handleAccountChange();\n    } else {\n        form.accountId = undefined as unknown as number;\n        form.baseURL = '';\n    }\n};\n\nconst handleProviderChange = () => {\n    if (form.agentType !== 'openclaw') {\n        return;\n    }\n    form.model = '';\n    form.baseURL = '';\n    form.accountId = undefined as unknown as number;\n    loadAccounts();\n};\n\nconst handleAgentTypeChange = async () => {\n    if (form.name === '' || form.name === 'OpenClaw' || form.name === 'CoPaw') {\n        form.name = form.agentType === 'copaw' ? 'CoPaw' : 'OpenClaw';\n    }\n    form.appVersion = '';\n    form.model = '';\n    form.provider = 'deepseek';\n    form.accountId = undefined as unknown as number;\n    form.baseURL = '';\n    if (form.agentType === 'openclaw') {\n        await loadSystemIP();\n        allowedOriginsAutoFilled.value = true;\n        syncAllowedOriginsWithDefault(true);\n        await loadVersions('openclaw');\n        await loadProviders();\n        await loadAccounts();\n        return;\n    }\n    form.allowedOrigins = '';\n    lastAutoAllowedOrigins.value = '';\n    allowedOriginsAutoFilled.value = true;\n    await loadVersions('copaw');\n};\n\nconst handleAccountChange = () => {\n    if (form.agentType !== 'openclaw') {\n        return;\n    }\n    const selected = accountOptions.value.find((item) => item.id === form.accountId);\n    if (selected) {\n        form.baseURL = selected.baseUrl || '';\n        if (!selected.models?.some((item) => item.id === form.model)) {\n            form.model = selected.models?.[0]?.id || '';\n        }\n    }\n    setDefaultModel();\n};\n\nconst setDefaultModel = () => {\n    if (form.agentType !== 'openclaw') {\n        return;\n    }\n    const models = filteredModels.value;\n    if (models.length > 0 && !form.model) {\n        form.model = models[0].id;\n        return;\n    }\n    if (models.length === 0) {\n        form.model = '';\n    }\n};\n\nconst handleAllowedOriginsInput = () => {\n    allowedOriginsAutoFilled.value = form.allowedOrigins === lastAutoAllowedOrigins.value;\n};\n\nconst submit = async () => {\n    if (!formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    const taskID = newUUID();\n    if (form.agentType === 'openclaw' && !form.token) {\n        form.token = getRandomStr(32).toLowerCase();\n    }\n    try {\n        const res = await createAgent({\n            name: form.name,\n            appVersion: form.appVersion,\n            webUIPort: form.webUIPort,\n            allowedOrigins: form.agentType === 'openclaw' ? parseAllowedOriginsInput(form.allowedOrigins) : undefined,\n            agentType: form.agentType,\n            model: form.agentType === 'openclaw' ? form.model : undefined,\n            accountId: form.agentType === 'openclaw' ? form.accountId : undefined,\n            token: form.agentType === 'openclaw' ? form.token : undefined,\n            taskID: taskID,\n            advanced: form.advanced,\n            containerName: form.containerName,\n            allowPort: form.allowPort,\n            specifyIP: form.specifyIP,\n            restartPolicy: form.restartPolicy,\n            cpuQuota: form.cpuQuota,\n            memoryLimit: form.memoryLimit,\n            memoryUnit: form.memoryUnit,\n            pullImage: form.pullImage,\n            editCompose: form.editCompose,\n            dockerCompose: form.dockerCompose,\n        });\n        form.token = res.data.token || form.token;\n        emit('search');\n        emit('task', taskID);\n        open.value = false;\n    } catch (error: any) {\n        const message = String(error?.message || '').toLowerCase();\n        const isTimeout = message.includes('timeout') || error?.code === 'ECONNABORTED';\n        if (isTimeout) {\n            emit('task', taskID);\n            open.value = false;\n        }\n    }\n};\n\nconst handleClose = () => {\n    formRef.value?.resetFields();\n    form.token = '';\n    form.allowedOrigins = '';\n    form.dockerCompose = '';\n    lastAutoAllowedOrigins.value = '';\n    allowedOriginsAutoFilled.value = true;\n};\n\nconst openDrawer = async (agentType?: 'openclaw' | 'copaw') => {\n    const targetType = agentType === 'copaw' ? 'copaw' : 'openclaw';\n    form.name = targetType === 'copaw' ? 'CoPaw' : 'OpenClaw';\n    open.value = true;\n    form.agentType = targetType;\n    form.token = getRandomStr(32).toLowerCase();\n    if (form.agentType === 'copaw') {\n        form.allowedOrigins = '';\n        lastAutoAllowedOrigins.value = '';\n        allowedOriginsAutoFilled.value = true;\n        await loadVersions('copaw');\n        providerOptions.value = [];\n        providerModels.value = {};\n        accountOptions.value = [];\n        return;\n    }\n    await loadSystemIP();\n    allowedOriginsAutoFilled.value = true;\n    syncAllowedOriginsWithDefault(true);\n    await loadVersions('openclaw');\n    await loadProviders();\n    await loadAccounts();\n};\n\nconst openAccountCreate = () => {\n    if (form.agentType !== 'openclaw') {\n        return;\n    }\n    if (accountAddRef.value?.open) {\n        accountAddRef.value.open({ provider: form.provider });\n    }\n};\n\nconst handleAccountCreated = async () => {\n    await loadAccounts();\n};\n\nwatch(\n    () => form.editCompose,\n    async (value) => {\n        if (value && !form.dockerCompose) {\n            await loadCompose();\n        }\n    },\n);\n\nwatch(\n    () => form.appVersion,\n    async (value, oldValue) => {\n        if (!value || value === oldValue) {\n            return;\n        }\n        if (form.editCompose) {\n            await loadCompose();\n        }\n    },\n);\n\nwatch(\n    () => form.webUIPort,\n    () => {\n        syncAllowedOriginsWithDefault();\n    },\n);\n\ndefineExpose({\n    open: openDrawer,\n});\n</script>\n\n<style scoped>\n.form-card {\n    margin-bottom: 16px;\n}\n\n.inline-link {\n    padding: 0;\n    margin-top: -1px;\n    margin-left: 5px;\n    height: auto;\n    line-height: inherit;\n    font-size: inherit;\n}\n\n.option-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 8px;\n}\n\n.option-label {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n\n.option-tag {\n    flex-shrink: 0;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/components/terminal.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"terminalVisible\"\n        :header=\"$t('menu.terminal')\"\n        @close=\"handleClose\"\n        :resource=\"title\"\n        fullScreen\n        :size=\"globalStore.isFullScreen ? 'full' : 'large'\"\n        :autoClose=\"false\"\n    >\n        <template #content>\n            <el-form label-position=\"top\" @submit.prevent>\n                <el-form-item :label=\"$t('commons.table.user')\" prop=\"user\">\n                    <el-select v-model=\"form.user\" :disabled=\"form.users.length <= 1\" @change=\"reConnect\">\n                        <el-option v-for=\"item in form.users\" :key=\"item\" :label=\"item\" :value=\"item\" />\n                    </el-select>\n                </el-form-item>\n            </el-form>\n            <Terminal class=\"terminal\" ref=\"terminalRef\" v-if=\"terminalOpen\"></Terminal>\n        </template>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button type=\"primary\" @click=\"handleClose\">\n                    {{ $t('commons.button.disConn') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { nextTick, reactive, ref } from 'vue';\nimport Terminal from '@/components/terminal/index.vue';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\n\nconst { globalStore, currentNode } = useGlobalStore();\n\nconst title = ref('');\nconst terminalVisible = ref(false);\nconst terminalOpen = ref(false);\nconst terminalRef = ref<InstanceType<typeof Terminal> | null>(null);\nconst form = reactive({\n    containerID: '',\n    users: [] as string[],\n    user: '',\n    shell: '',\n    node: '',\n});\n\ninterface DialogProps {\n    containerID: string;\n    title: string;\n    users: string[];\n    shell: string;\n    node?: string;\n}\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    terminalVisible.value = true;\n    form.containerID = params.containerID;\n    form.users = [...params.users];\n    form.user = form.users[0];\n    form.shell = params.shell;\n    form.node = params.node || currentNode.value;\n    title.value = params.title;\n    await reConnect();\n};\n\nconst initTerm = async () => {\n    terminalOpen.value = true;\n    await nextTick();\n    let args = `source=container&containerid=${form.containerID}&user=${form.user}&command=${form.shell}`;\n    if (form.node) {\n        args += `&operateNode=${form.node}`;\n    }\n    terminalRef.value?.acceptParams({\n        endpoint: '/api/v2/containers/exec',\n        args,\n        error: '',\n        initCmd: '',\n    });\n};\n\nconst reConnect = async () => {\n    terminalRef.value?.onClose();\n    terminalOpen.value = false;\n    await nextTick();\n    await initTerm();\n};\n\nconst onClose = () => {\n    terminalRef.value?.onClose();\n    terminalOpen.value = false;\n};\n\nconst handleClose = () => {\n    onClose();\n    terminalVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.terminal {\n    height: calc(100vh - 180px);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"header\" size=\"large\" @close=\"handleClose\">\n        <template #content>\n            <el-tabs v-model=\"activeTab\" tab-position=\"left\" class=\"config-tabs\" @tab-click=\"handleTabClick\">\n                <el-tab-pane :label=\"t('aiTools.agents.channelsTab')\" name=\"channels\">\n                    <ChannelsTab ref=\"channelsRef\" />\n                </el-tab-pane>\n                <el-tab-pane :label=\"t('aiTools.model.model')\" name=\"model\">\n                    <ModelTab ref=\"modelRef\" @updated=\"handleModelUpdated\" />\n                </el-tab-pane>\n                <el-tab-pane :label=\"t('aiTools.agents.settingsTab')\" name=\"settings\">\n                    <SettingsTab ref=\"settingsRef\" />\n                </el-tab-pane>\n            </el-tabs>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { nextTick, ref } from 'vue';\nimport type { TabsPaneContext } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport ChannelsTab from './tabs/channels.vue';\nimport ModelTab from './tabs/model.vue';\nimport SettingsTab from './tabs/settings.vue';\n\nconst { t } = useI18n();\nconst emit = defineEmits(['updated']);\nconst open = ref(false);\nconst activeTab = ref('channels');\nconst header = ref('');\nconst agentId = ref(0);\nconst currentAgent = ref<AI.AgentItem>();\nconst channelsRef = ref();\nconst modelRef = ref();\nconst settingsRef = ref();\n\nconst loadSettings = async () => {\n    if (!currentAgent.value) {\n        return;\n    }\n    await nextTick();\n    await settingsRef.value?.load(currentAgent.value);\n};\n\nconst loadModel = async () => {\n    if (!currentAgent.value) {\n        return;\n    }\n    await nextTick();\n    await modelRef.value?.load(currentAgent.value);\n};\n\nconst loadChannels = async () => {\n    if (agentId.value <= 0) {\n        return;\n    }\n    await nextTick();\n    await channelsRef.value?.load(agentId.value);\n};\n\nconst handleClose = () => {\n    activeTab.value = 'channels';\n};\n\nconst handleTabClick = async (pane: TabsPaneContext) => {\n    if (pane.paneName === 'settings' && agentId.value > 0) {\n        await loadSettings();\n    }\n    if (pane.paneName === 'model' && currentAgent.value) {\n        await loadModel();\n    }\n    if (pane.paneName === 'channels' && agentId.value > 0) {\n        await loadChannels();\n    }\n};\n\nconst handleModelUpdated = () => {\n    emit('updated');\n};\n\nconst openDrawer = async (agent: AI.AgentItem) => {\n    agentId.value = agent.id;\n    currentAgent.value = agent;\n    header.value = `${agent.name} - ${t('menu.config')}`;\n    activeTab.value = 'channels';\n    open.value = true;\n    await loadChannels();\n};\n\ndefineExpose({\n    open: openDrawer,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.config-tabs {\n    min-height: 440px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/channels/dingtalk.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n        <el-alert\n            v-if=\"!form.installed\"\n            type=\"warning\"\n            :closable=\"false\"\n            :title=\"t('aiTools.agents.pluginNotInstalled')\"\n            class=\"mb-4\"\n        />\n        <el-form-item>\n            <el-button v-if=\"!form.installed\" type=\"primary\" :loading=\"installing\" @click=\"installPlugin\">\n                {{ t('commons.button.install') }}\n            </el-button>\n        </el-form-item>\n        <el-form-item :label=\"t('commons.table.status')\">\n            <el-switch v-model=\"form.enabled\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.clientId')\" prop=\"clientId\">\n            <el-input v-model=\"form.clientId\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.clientSecret')\" prop=\"clientSecret\">\n            <el-input v-model=\"form.clientSecret\" type=\"password\" show-password />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.dmPolicy')\" prop=\"dmPolicy\">\n            <el-select v-model=\"form.dmPolicy\">\n                <el-option :label=\"t('aiTools.agents.policyPairing')\" value=\"pairing\" />\n                <el-option :label=\"t('aiTools.agents.policyAllowlist')\" value=\"allowlist\" />\n                <el-option :label=\"t('aiTools.agents.policyOpen')\" value=\"open\" />\n                <el-option :label=\"t('aiTools.agents.policyDisabled')\" value=\"disabled\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item v-if=\"form.dmPolicy === 'allowlist'\" :label=\"t('aiTools.agents.allowFrom')\" prop=\"allowFromText\">\n            <el-input\n                v-model=\"form.allowFromText\"\n                type=\"textarea\"\n                :rows=\"3\"\n                :placeholder=\"t('aiTools.agents.allowFromPlaceholder')\"\n            />\n            <span class=\"input-help\">{{ t('aiTools.agents.allowFromHelper') }}</span>\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.groupPolicy')\" prop=\"groupPolicy\">\n            <el-select v-model=\"form.groupPolicy\">\n                <el-option :label=\"t('aiTools.agents.policyOpen')\" value=\"open\" />\n                <el-option :label=\"t('aiTools.agents.policyAllowlist')\" value=\"allowlist\" />\n                <el-option :label=\"t('aiTools.agents.policyDisabled')\" value=\"disabled\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item\n            v-if=\"form.groupPolicy === 'allowlist'\"\n            :label=\"t('aiTools.agents.groupAllowFrom')\"\n            prop=\"groupAllowFromText\"\n        >\n            <el-input\n                v-model=\"form.groupAllowFromText\"\n                type=\"textarea\"\n                :rows=\"3\"\n                :placeholder=\"t('aiTools.agents.groupAllowFromPlaceholder')\"\n            />\n            <span class=\"input-help\">{{ t('aiTools.agents.groupAllowFromHelper') }}</span>\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" :disabled=\"!form.installed\" @click=\"saveChannel\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n\n        <el-divider />\n\n        <el-form-item :label=\"t('aiTools.agents.pairingCode')\">\n            <el-input v-model=\"pairingCode\" :placeholder=\"t('aiTools.agents.pairingCodePlaceholder')\" />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"approving\" :disabled=\"!form.installed\" @click=\"approvePairing\">\n                {{ t('aiTools.agents.approvePairing') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n    <TaskLog ref=\"taskLogRef\" @close=\"checkPluginStatus\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance, FormRules } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport {\n    approveAgentChannelPairing,\n    checkAgentPlugin,\n    getAgentDingTalkConfig,\n    installAgentPlugin,\n    updateAgentDingTalkConfig,\n} from '@/api/modules/ai';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport { newUUID } from '@/utils/util';\nimport TaskLog from '@/components/log/task/index.vue';\n\ninterface DingTalkForm extends AI.AgentDingTalkConfig {\n    allowFromText: string;\n    groupAllowFromText: string;\n}\n\nconst { t } = useI18n();\nconst saving = ref(false);\nconst approving = ref(false);\nconst installing = ref(false);\nconst agentId = ref(0);\nconst pairingCode = ref('');\nconst formRef = ref<FormInstance>();\nconst taskLogRef = ref();\n\nconst form = reactive<DingTalkForm>({\n    enabled: true,\n    clientId: '',\n    clientSecret: '',\n    dmPolicy: 'pairing',\n    allowFrom: [],\n    groupPolicy: 'disabled',\n    groupAllowFrom: [],\n    installed: false,\n    allowFromText: '',\n    groupAllowFromText: '',\n});\n\nconst parseTextList = (value: string): string[] => {\n    return Array.from(\n        new Set(\n            String(value || '')\n                .split(/\\r?\\n/)\n                .map((item) => item.trim())\n                .filter(Boolean),\n        ),\n    );\n};\n\nconst validateAllowFrom = (_rule: any, value: string, callback: (error?: Error) => void) => {\n    if (form.dmPolicy !== 'allowlist') {\n        callback();\n        return;\n    }\n    if (parseTextList(value).length === 0) {\n        callback(new Error(t('aiTools.agents.allowFromRequired')));\n        return;\n    }\n    callback();\n};\n\nconst validateGroupAllowFrom = (_rule: any, value: string, callback: (error?: Error) => void) => {\n    if (form.groupPolicy !== 'allowlist') {\n        callback();\n        return;\n    }\n    if (parseTextList(value).length === 0) {\n        callback(new Error(t('aiTools.agents.allowFromRequired')));\n        return;\n    }\n    callback();\n};\n\nconst rules = reactive<FormRules>({\n    clientId: [Rules.requiredInput],\n    clientSecret: [Rules.requiredInput],\n    dmPolicy: [Rules.requiredSelect],\n    groupPolicy: [Rules.requiredSelect],\n    allowFromText: [{ validator: validateAllowFrom, trigger: 'blur' }],\n    groupAllowFromText: [{ validator: validateGroupAllowFrom, trigger: 'blur' }],\n});\n\nconst checkPluginStatus = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    const res = await checkAgentPlugin({\n        agentId: agentId.value,\n        type: 'dingtalk',\n    });\n    form.installed = Boolean(res.data?.installed);\n};\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    pairingCode.value = '';\n    const res = await getAgentDingTalkConfig({ agentId: id });\n    Object.assign(form, res.data || {});\n    form.dmPolicy = form.dmPolicy || 'pairing';\n    form.groupPolicy = form.groupPolicy || 'disabled';\n    form.allowFromText = (res.data?.allowFrom || []).join('\\n');\n    form.groupAllowFromText = (res.data?.groupAllowFrom || []).join('\\n');\n    await checkPluginStatus();\n};\n\nconst saveChannel = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentDingTalkConfig({\n            agentId: agentId.value,\n            enabled: form.enabled,\n            clientId: form.clientId,\n            clientSecret: form.clientSecret,\n            dmPolicy: form.dmPolicy,\n            allowFrom: parseTextList(form.allowFromText),\n            groupPolicy: form.groupPolicy,\n            groupAllowFrom: parseTextList(form.groupAllowFromText),\n        });\n        MsgSuccess(t('aiTools.agents.saveSuccess'));\n    } finally {\n        saving.value = false;\n    }\n};\n\nconst approvePairing = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    if (!pairingCode.value) {\n        MsgWarning(t('aiTools.agents.pairingCodeRequired'));\n        return;\n    }\n    approving.value = true;\n    try {\n        await approveAgentChannelPairing({\n            agentId: agentId.value,\n            type: 'dingtalk-connector',\n            pairingCode: pairingCode.value,\n        });\n        MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));\n        pairingCode.value = '';\n    } finally {\n        approving.value = false;\n    }\n};\n\nconst installPlugin = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    const taskID = newUUID();\n    installing.value = true;\n    try {\n        await installAgentPlugin({\n            agentId: agentId.value,\n            type: 'dingtalk',\n            taskID,\n        });\n        taskLogRef.value?.openWithTaskID(taskID);\n    } finally {\n        installing.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.input-help {\n    font-size: 12px;\n    color: var(--el-text-color-secondary);\n    line-height: 1.5;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/channels/discord.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n        <el-form-item :label=\"t('commons.table.status')\">\n            <el-switch v-model=\"form.enabled\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.dmPolicy')\" prop=\"dmPolicy\">\n            <el-select v-model=\"form.dmPolicy\">\n                <el-option :label=\"t('aiTools.agents.policyPairing')\" value=\"pairing\" />\n                <el-option :label=\"t('aiTools.agents.policyOpen')\" value=\"open\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.groupPolicy')\" prop=\"groupPolicy\">\n            <el-select v-model=\"form.groupPolicy\">\n                <el-option :label=\"t('aiTools.agents.policyOpen')\" value=\"open\" />\n                <el-option :label=\"t('aiTools.agents.policyDisabled')\" value=\"disabled\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item label=\"Token\" prop=\"token\">\n            <el-input v-model=\"form.token\" type=\"password\" show-password />\n        </el-form-item>\n        <el-form-item :label=\"t('setting.proxy')\">\n            <el-input v-model=\"form.proxy\" placeholder=\"http://127.0.0.1:7890\" />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" @click=\"saveChannel\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n\n        <el-divider />\n\n        <el-form-item :label=\"t('aiTools.agents.pairingCode')\">\n            <el-input v-model=\"pairingCode\" :placeholder=\"t('aiTools.agents.pairingCodePlaceholder')\" />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"approving\" @click=\"approvePairing\">\n                {{ t('aiTools.agents.approvePairing') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport { approveAgentChannelPairing, getAgentDiscordConfig, updateAgentDiscordConfig } from '@/api/modules/ai';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\n\nconst { t } = useI18n();\nconst saving = ref(false);\nconst approving = ref(false);\nconst agentId = ref(0);\nconst pairingCode = ref('');\nconst formRef = ref<FormInstance>();\n\nconst form = reactive<AI.AgentDiscordConfig>({\n    enabled: true,\n    dmPolicy: 'pairing',\n    groupPolicy: 'open',\n    token: '',\n    proxy: '',\n});\n\nconst rules = reactive({\n    dmPolicy: [Rules.requiredSelect],\n    groupPolicy: [Rules.requiredSelect],\n    token: [Rules.requiredInput],\n});\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    pairingCode.value = '';\n    const res = await getAgentDiscordConfig({ agentId: id });\n    Object.assign(form, res.data || {});\n    if (!form.dmPolicy) {\n        form.dmPolicy = 'pairing';\n    }\n    if (!form.groupPolicy) {\n        form.groupPolicy = 'open';\n    }\n};\n\nconst saveChannel = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentDiscordConfig({\n            agentId: agentId.value,\n            enabled: form.enabled,\n            dmPolicy: form.dmPolicy || 'pairing',\n            groupPolicy: form.groupPolicy || 'open',\n            token: form.token,\n            proxy: form.proxy,\n        });\n        MsgSuccess(t('aiTools.agents.saveSuccess'));\n    } finally {\n        saving.value = false;\n    }\n};\n\nconst approvePairing = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    if (!pairingCode.value) {\n        MsgWarning(t('aiTools.agents.pairingCodeRequired'));\n        return;\n    }\n    approving.value = true;\n    try {\n        await approveAgentChannelPairing({\n            agentId: agentId.value,\n            type: 'discord',\n            pairingCode: pairingCode.value,\n        });\n        MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));\n        pairingCode.value = '';\n    } finally {\n        approving.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/channels/feishu.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n        <el-form-item :label=\"t('commons.table.status')\">\n            <el-switch v-model=\"form.enabled\" />\n        </el-form-item>\n        <el-form-item>\n            <el-link type=\"primary\" icon=\"Position\" @click=\"toFeishuDoc\">\n                {{ t('container.mirrorsHelper2') }}\n            </el-link>\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.dmPolicy')\" prop=\"dmPolicy\">\n            <el-select v-model=\"form.dmPolicy\">\n                <el-option :label=\"t('aiTools.agents.policyPairing')\" value=\"pairing\" />\n                <el-option :label=\"t('aiTools.agents.policyOpen')\" value=\"open\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.botName')\" prop=\"botName\">\n            <el-input v-model=\"form.botName\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.appId')\" prop=\"appId\">\n            <el-input v-model=\"form.appId\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.appSecret')\" prop=\"appSecret\">\n            <el-input v-model=\"form.appSecret\" type=\"password\" show-password />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" @click=\"saveChannel\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n\n        <el-divider />\n\n        <el-form-item :label=\"t('aiTools.agents.pairingCode')\">\n            <el-input v-model=\"pairingCode\" :placeholder=\"t('aiTools.agents.pairingCodePlaceholder')\" />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"approving\" @click=\"approvePairing\">\n                {{ t('aiTools.agents.approvePairing') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport { approveAgentChannelPairing, getAgentFeishuConfig, updateAgentFeishuConfig } from '@/api/modules/ai';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\n\nconst { t } = useI18n();\nconst saving = ref(false);\nconst approving = ref(false);\nconst agentId = ref(0);\nconst pairingCode = ref('');\nconst formRef = ref<FormInstance>();\n\nconst form = reactive<AI.AgentFeishuConfig>({\n    enabled: true,\n    dmPolicy: 'pairing',\n    botName: '',\n    appId: '',\n    appSecret: '',\n});\n\nconst rules = reactive({\n    dmPolicy: [Rules.requiredSelect],\n    botName: [Rules.requiredInput],\n    appId: [Rules.requiredInput],\n    appSecret: [Rules.requiredInput],\n});\n\nconst toFeishuDoc = () => {\n    window.open('https://openclaw.club/guides/feishu-platform', '_blank');\n};\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    pairingCode.value = '';\n    const res = await getAgentFeishuConfig({ agentId: id });\n    Object.assign(form, res.data || {});\n    if (!form.dmPolicy) {\n        form.dmPolicy = 'pairing';\n    }\n};\n\nconst saveChannel = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentFeishuConfig({\n            agentId: agentId.value,\n            enabled: form.enabled,\n            dmPolicy: form.dmPolicy || 'pairing',\n            botName: form.botName,\n            appId: form.appId,\n            appSecret: form.appSecret,\n        });\n        MsgSuccess(t('aiTools.agents.saveSuccess'));\n    } finally {\n        saving.value = false;\n    }\n};\n\nconst approvePairing = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    if (!pairingCode.value) {\n        MsgWarning(t('aiTools.agents.pairingCodeRequired'));\n        return;\n    }\n    approving.value = true;\n    try {\n        await approveAgentChannelPairing({\n            agentId: agentId.value,\n            type: 'feishu',\n            pairingCode: pairingCode.value,\n        });\n        MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));\n        pairingCode.value = '';\n    } finally {\n        approving.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/channels/qq.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n        <el-alert\n            v-if=\"!form.installed\"\n            type=\"warning\"\n            :closable=\"false\"\n            :title=\"t('aiTools.agents.pluginNotInstalled')\"\n            class=\"mb-4\"\n        />\n        <el-form-item>\n            <el-button v-if=\"!form.installed\" type=\"primary\" :loading=\"installing\" @click=\"installPlugin\">\n                {{ t('commons.button.install') }}\n            </el-button>\n        </el-form-item>\n        <el-form-item :label=\"t('commons.table.status')\">\n            <el-switch v-model=\"form.enabled\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.appId')\" prop=\"appId\">\n            <el-input v-model=\"form.appId\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.appSecret')\" prop=\"clientSecret\">\n            <el-input v-model=\"form.clientSecret\" type=\"password\" show-password />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" :disabled=\"!form.installed\" @click=\"saveChannel\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n    <TaskLog ref=\"taskLogRef\" @close=\"checkPluginStatus\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport { checkAgentPlugin, getAgentQQBotConfig, installAgentPlugin, updateAgentQQBotConfig } from '@/api/modules/ai';\nimport { MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport { newUUID } from '@/utils/util';\nimport TaskLog from '@/components/log/task/index.vue';\n\nconst { t } = useI18n();\nconst saving = ref(false);\nconst installing = ref(false);\nconst agentId = ref(0);\nconst formRef = ref<FormInstance>();\nconst taskLogRef = ref();\n\nconst form = reactive<AI.AgentQQBotConfig>({\n    enabled: true,\n    appId: '',\n    clientSecret: '',\n    installed: false,\n});\n\nconst rules = reactive({\n    appId: [Rules.requiredInput],\n    clientSecret: [Rules.requiredInput],\n});\n\nconst checkPluginStatus = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    const res = await checkAgentPlugin({\n        agentId: agentId.value,\n        type: 'qqbot',\n    });\n    form.installed = Boolean(res.data?.installed);\n};\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    const res = await getAgentQQBotConfig({ agentId: id });\n    Object.assign(form, res.data || {});\n    await checkPluginStatus();\n};\n\nconst saveChannel = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentQQBotConfig({\n            agentId: agentId.value,\n            enabled: form.enabled,\n            appId: form.appId,\n            clientSecret: form.clientSecret,\n        });\n        MsgSuccess(t('aiTools.agents.saveSuccess'));\n    } finally {\n        saving.value = false;\n    }\n};\n\nconst installPlugin = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    const taskID = newUUID();\n    installing.value = true;\n    try {\n        await installAgentPlugin({\n            agentId: agentId.value,\n            type: 'qqbot',\n            taskID,\n        });\n        taskLogRef.value?.openWithTaskID(taskID);\n    } finally {\n        installing.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/channels/telegram.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n        <el-form-item :label=\"t('commons.table.status')\">\n            <el-switch v-model=\"form.enabled\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.dmPolicy')\" prop=\"dmPolicy\">\n            <el-select v-model=\"form.dmPolicy\">\n                <el-option :label=\"t('aiTools.agents.policyPairing')\" value=\"pairing\" />\n                <el-option :label=\"t('aiTools.agents.policyOpen')\" value=\"open\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item label=\"Bot Token\" prop=\"botToken\">\n            <el-input v-model=\"form.botToken\" type=\"password\" show-password />\n        </el-form-item>\n        <el-form-item :label=\"t('setting.proxy')\">\n            <el-input v-model=\"form.proxy\" placeholder=\"http://127.0.0.1:7890\" />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" @click=\"saveChannel\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n\n        <el-divider />\n\n        <el-form-item :label=\"t('aiTools.agents.pairingCode')\">\n            <el-input v-model=\"pairingCode\" :placeholder=\"t('aiTools.agents.pairingCodePlaceholder')\" />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"approving\" @click=\"approvePairing\">\n                {{ t('aiTools.agents.approvePairing') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport { approveAgentChannelPairing, getAgentTelegramConfig, updateAgentTelegramConfig } from '@/api/modules/ai';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\n\nconst { t } = useI18n();\nconst saving = ref(false);\nconst approving = ref(false);\nconst agentId = ref(0);\nconst pairingCode = ref('');\nconst formRef = ref<FormInstance>();\n\nconst form = reactive<AI.AgentTelegramConfig>({\n    enabled: true,\n    dmPolicy: 'pairing',\n    botToken: '',\n    proxy: '',\n});\n\nconst rules = reactive({\n    dmPolicy: [Rules.requiredSelect],\n    botToken: [Rules.requiredInput],\n});\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    pairingCode.value = '';\n    const res = await getAgentTelegramConfig({ agentId: id });\n    Object.assign(form, res.data || {});\n    if (!form.dmPolicy) {\n        form.dmPolicy = 'pairing';\n    }\n};\n\nconst saveChannel = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentTelegramConfig({\n            agentId: agentId.value,\n            enabled: form.enabled,\n            dmPolicy: form.dmPolicy || 'pairing',\n            botToken: form.botToken,\n            proxy: form.proxy,\n        });\n        MsgSuccess(t('aiTools.agents.saveSuccess'));\n    } finally {\n        saving.value = false;\n    }\n};\n\nconst approvePairing = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    if (!pairingCode.value) {\n        MsgWarning(t('aiTools.agents.pairingCodeRequired'));\n        return;\n    }\n    approving.value = true;\n    try {\n        await approveAgentChannelPairing({\n            agentId: agentId.value,\n            type: 'telegram',\n            pairingCode: pairingCode.value,\n        });\n        MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));\n        pairingCode.value = '';\n    } finally {\n        approving.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/channels/wecom.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n        <el-alert\n            v-if=\"!form.installed\"\n            type=\"warning\"\n            :closable=\"false\"\n            :title=\"t('aiTools.agents.pluginNotInstalled')\"\n            class=\"mb-4\"\n        />\n        <el-form-item>\n            <el-button v-if=\"!form.installed\" type=\"primary\" :loading=\"installing\" @click=\"installPlugin\">\n                {{ t('commons.button.install') }}\n            </el-button>\n        </el-form-item>\n        <el-form-item :label=\"t('commons.table.status')\">\n            <el-switch v-model=\"form.enabled\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.dmPolicy')\" prop=\"dmPolicy\">\n            <el-select v-model=\"form.dmPolicy\">\n                <el-option :label=\"t('aiTools.agents.policyPairing')\" value=\"pairing\" />\n                <el-option :label=\"t('aiTools.agents.policyOpen')\" value=\"open\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.botId')\" prop=\"botId\">\n            <el-input v-model=\"form.botId\" />\n        </el-form-item>\n        <el-form-item :label=\"t('setting.secret')\" prop=\"secret\">\n            <el-input v-model=\"form.secret\" type=\"password\" show-password />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" :disabled=\"!form.installed\" @click=\"saveChannel\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n\n        <el-divider />\n\n        <el-form-item :label=\"t('aiTools.agents.pairingCode')\">\n            <el-input v-model=\"pairingCode\" :placeholder=\"t('aiTools.agents.pairingCodePlaceholder')\" />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"approving\" :disabled=\"!form.installed\" @click=\"approvePairing\">\n                {{ t('aiTools.agents.approvePairing') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n    <TaskLog ref=\"taskLogRef\" @close=\"checkPluginStatus\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport {\n    approveAgentChannelPairing,\n    checkAgentPlugin,\n    getAgentWecomConfig,\n    installAgentPlugin,\n    updateAgentWecomConfig,\n} from '@/api/modules/ai';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport { newUUID } from '@/utils/util';\nimport TaskLog from '@/components/log/task/index.vue';\n\nconst { t } = useI18n();\nconst saving = ref(false);\nconst approving = ref(false);\nconst installing = ref(false);\nconst agentId = ref(0);\nconst pairingCode = ref('');\nconst formRef = ref<FormInstance>();\nconst taskLogRef = ref();\n\nconst form = reactive<AI.AgentWecomConfig>({\n    enabled: true,\n    dmPolicy: 'pairing',\n    botId: '',\n    secret: '',\n    installed: false,\n});\n\nconst rules = reactive({\n    dmPolicy: [Rules.requiredSelect],\n    botId: [Rules.requiredInput],\n    secret: [Rules.requiredInput],\n});\n\nconst checkPluginStatus = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    const res = await checkAgentPlugin({\n        agentId: agentId.value,\n        type: 'wecom',\n    });\n    form.installed = Boolean(res.data?.installed);\n};\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    pairingCode.value = '';\n    const res = await getAgentWecomConfig({ agentId: id });\n    Object.assign(form, res.data || {});\n    if (!form.dmPolicy) {\n        form.dmPolicy = 'pairing';\n    }\n    await checkPluginStatus();\n};\n\nconst saveChannel = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentWecomConfig({\n            agentId: agentId.value,\n            enabled: form.enabled,\n            dmPolicy: form.dmPolicy,\n            botId: form.botId,\n            secret: form.secret,\n        });\n        MsgSuccess(t('aiTools.agents.saveSuccess'));\n    } finally {\n        saving.value = false;\n    }\n};\n\nconst approvePairing = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    if (!pairingCode.value) {\n        MsgWarning(t('aiTools.agents.pairingCodeRequired'));\n        return;\n    }\n    approving.value = true;\n    try {\n        await approveAgentChannelPairing({\n            agentId: agentId.value,\n            type: 'wecom',\n            pairingCode: pairingCode.value,\n        });\n        MsgSuccess(t('aiTools.agents.pairingApproveSuccess'));\n        pairingCode.value = '';\n    } finally {\n        approving.value = false;\n    }\n};\n\nconst installPlugin = async () => {\n    if (!agentId.value) {\n        return;\n    }\n    const taskID = newUUID();\n    installing.value = true;\n    try {\n        await installAgentPlugin({\n            agentId: agentId.value,\n            type: 'wecom',\n            taskID,\n        });\n        taskLogRef.value?.openWithTaskID(taskID);\n    } finally {\n        installing.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/channels.vue",
    "content": "<template>\n    <el-tabs v-model=\"activeTab\" @tab-click=\"handleTabClick\">\n        <el-tab-pane label=\"QQ\" name=\"qqbot\">\n            <QQBotTab ref=\"qqbotRef\" />\n        </el-tab-pane>\n        <el-tab-pane :label=\"t('aiTools.agents.wecom')\" name=\"wecom\">\n            <WecomTab ref=\"wecomRef\" />\n        </el-tab-pane>\n        <el-tab-pane :label=\"t('aiTools.agents.dingtalk')\" name=\"dingtalk\">\n            <DingTalkTab ref=\"dingtalkRef\" />\n        </el-tab-pane>\n        <el-tab-pane :label=\"t('aiTools.agents.feishu')\" name=\"feishu\">\n            <FeishuTab ref=\"feishuRef\" />\n        </el-tab-pane>\n        <el-tab-pane label=\"Telegram\" name=\"telegram\">\n            <TelegramTab ref=\"telegramRef\" />\n        </el-tab-pane>\n        <el-tab-pane label=\"Discord\" name=\"discord\">\n            <DiscordTab ref=\"discordRef\" />\n        </el-tab-pane>\n    </el-tabs>\n</template>\n\n<script setup lang=\"ts\">\nimport { nextTick, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport FeishuTab from './channels/feishu.vue';\nimport TelegramTab from './channels/telegram.vue';\nimport DiscordTab from './channels/discord.vue';\nimport QQBotTab from './channels/qq.vue';\nimport WecomTab from './channels/wecom.vue';\nimport DingTalkTab from './channels/dingtalk.vue';\n\nconst { t } = useI18n();\nconst activeTab = ref('qqbot');\nconst agentId = ref(0);\nconst feishuRef = ref();\nconst telegramRef = ref();\nconst discordRef = ref();\nconst qqbotRef = ref();\nconst wecomRef = ref();\nconst dingtalkRef = ref();\n\nconst loadCurrentTab = async () => {\n    if (agentId.value <= 0) {\n        return;\n    }\n    await nextTick();\n    if (activeTab.value === 'discord') {\n        await discordRef.value?.load(agentId.value);\n        return;\n    }\n    if (activeTab.value === 'telegram') {\n        await telegramRef.value?.load(agentId.value);\n        return;\n    }\n    if (activeTab.value === 'wecom') {\n        await wecomRef.value?.load(agentId.value);\n        return;\n    }\n    if (activeTab.value === 'dingtalk') {\n        await dingtalkRef.value?.load(agentId.value);\n        return;\n    }\n    if (activeTab.value === 'qqbot') {\n        await qqbotRef.value?.load(agentId.value);\n        return;\n    }\n    await feishuRef.value?.load(agentId.value);\n};\n\nconst handleTabClick = async () => {\n    await loadCurrentTab();\n};\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    await loadCurrentTab();\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/model.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\" v-loading=\"loading\">\n        <el-form-item :label=\"t('aiTools.agents.account')\" prop=\"accountId\">\n            <el-select v-model=\"form.accountId\" @change=\"handleAccountChange\">\n                <el-option v-for=\"item in accountOptions\" :key=\"item.id\" :label=\"item.name\" :value=\"item.id\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.model.model')\" prop=\"model\">\n            <el-select v-model=\"form.model\" filterable>\n                <el-option v-for=\"item in modelOptions\" :key=\"item.id\" :label=\"item.name\" :value=\"item.id\" />\n            </el-select>\n            <span class=\"input-help\">{{ t('aiTools.agents.accountModelsHelper') }}</span>\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" @click=\"saveModel\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport { pageAgentAccounts, updateAgentModelConfig } from '@/api/modules/ai';\nimport { Rules } from '@/global/form-rules';\nimport { MsgSuccess } from '@/utils/message';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\n\nconst emit = defineEmits(['updated']);\nconst { t } = useI18n();\n\nconst loading = ref(false);\nconst saving = ref(false);\nconst formRef = ref<FormInstance>();\nconst { isIntl } = useGlobalStore();\nconst blockedProviders = new Set(['ark-coding-plan', 'bailian-coding-plan']);\n\nconst agentId = ref(0);\nconst accountOptions = ref<AI.AgentAccountItem[]>([]);\nconst modelOptions = ref<AI.AgentAccountModel[]>([]);\n\nconst form = reactive({\n    accountId: undefined as unknown as number,\n    model: '',\n});\n\nconst rules = reactive({\n    accountId: [Rules.requiredSelect],\n    model: [Rules.requiredSelect],\n});\n\nconst loadAccounts = async () => {\n    const res = await pageAgentAccounts({\n        page: 1,\n        pageSize: 200,\n        provider: '',\n        name: '',\n    });\n    const items = res.data.items || [];\n    accountOptions.value = isIntl.value ? items.filter((item) => !blockedProviders.has(item.provider)) : items;\n};\n\nconst setModelOptionsByAccount = (accountId: number) => {\n    const selected = accountOptions.value.find((item) => item.id === accountId);\n    modelOptions.value = selected?.models || [];\n    if (!modelOptions.value.some((item) => item.id === form.model)) {\n        form.model = modelOptions.value[0]?.id || '';\n    }\n};\n\nconst handleAccountChange = () => {\n    setModelOptionsByAccount(form.accountId);\n};\n\nconst load = async (agent: AI.AgentItem) => {\n    loading.value = true;\n    try {\n        agentId.value = agent.id;\n        await loadAccounts();\n        if (accountOptions.value.length === 0) {\n            form.accountId = undefined as unknown as number;\n            form.model = '';\n            modelOptions.value = [];\n            return;\n        }\n        const currentAccount =\n            accountOptions.value.find((item) => item.id === agent.accountId) || accountOptions.value[0];\n        form.accountId = currentAccount.id;\n        form.model = agent.model || currentAccount.models?.[0]?.id || '';\n        setModelOptionsByAccount(currentAccount.id);\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst saveModel = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentModelConfig({\n            agentId: agentId.value,\n            accountId: form.accountId,\n            model: form.model,\n        });\n        MsgSuccess(t('aiTools.agents.switchModelSuccess'));\n        emit('updated');\n    } finally {\n        saving.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/settings/other.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\" v-loading=\"loading\">\n        <el-form-item :label=\"t('aiTools.agents.browserEnabled')\">\n            <el-switch v-model=\"form.browserEnabled\" />\n        </el-form-item>\n        <el-form-item :label=\"t('aiTools.agents.timeZone')\" prop=\"userTimezone\">\n            <el-input v-model=\"form.userTimezone\" />\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" @click=\"saveConfig\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { Rules } from '@/global/form-rules';\nimport { AI } from '@/api/interface/ai';\nimport { getAgentOtherConfig, updateAgentOtherConfig } from '@/api/modules/ai';\nimport { MsgSuccess } from '@/utils/message';\n\nconst { t } = useI18n();\nconst loading = ref(false);\nconst saving = ref(false);\nconst agentId = ref(0);\nconst formRef = ref<FormInstance>();\n\nconst form = reactive<AI.AgentOtherConfig>({\n    userTimezone: '',\n    browserEnabled: true,\n});\n\nconst rules = reactive({\n    userTimezone: [Rules.requiredInput],\n});\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    loading.value = true;\n    try {\n        const res = await getAgentOtherConfig({ agentId: id });\n        Object.assign(form, res.data || {});\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst saveConfig = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentOtherConfig({\n            agentId: agentId.value,\n            userTimezone: form.userTimezone,\n            browserEnabled: form.browserEnabled,\n        });\n        MsgSuccess(t('aiTools.agents.saveSuccess'));\n    } finally {\n        saving.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/settings/security.vue",
    "content": "<template>\n    <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\" v-loading=\"loading\">\n        <el-form-item :label=\"t('aiTools.agents.allowedOrigins')\" prop=\"allowedOrigins\">\n            <el-input\n                v-model=\"form.allowedOrigins\"\n                type=\"textarea\"\n                :rows=\"4\"\n                :placeholder=\"t('aiTools.agents.allowedOriginsPlaceholder')\"\n            />\n            <span class=\"input-help\">\n                {{ t('aiTools.agents.allowedOriginsHelper') }}\n            </span>\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" :loading=\"saving\" @click=\"saveConfig\">\n                {{ t('commons.button.save') }}\n            </el-button>\n        </el-form-item>\n    </el-form>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { useI18n } from 'vue-i18n';\nimport { getAgentSecurityConfig, updateAgentSecurityConfig } from '@/api/modules/ai';\nimport { MsgSuccess } from '@/utils/message';\nimport { parseAllowedOriginsInput, validateAllowedOriginsInput } from '@/utils/agent';\n\nconst { t } = useI18n();\nconst loading = ref(false);\nconst saving = ref(false);\nconst agentId = ref(0);\nconst formRef = ref<FormInstance>();\n\nconst form = reactive({\n    allowedOrigins: '',\n});\n\nconst rules = reactive({\n    allowedOrigins: [\n        {\n            validator: (_rule: any, value: any, callback: (error?: Error) => void) => {\n                const message = validateAllowedOriginsInput(String(value || ''));\n                if (message) {\n                    callback(new Error(message));\n                    return;\n                }\n                callback();\n            },\n            trigger: 'blur',\n        },\n    ],\n});\n\nconst load = async (id: number) => {\n    agentId.value = id;\n    loading.value = true;\n    try {\n        const res = await getAgentSecurityConfig({ agentId: id });\n        form.allowedOrigins = (res.data?.allowedOrigins || []).join('\\n');\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst saveConfig = async () => {\n    if (!agentId.value || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    saving.value = true;\n    try {\n        await updateAgentSecurityConfig({\n            agentId: agentId.value,\n            allowedOrigins: parseAllowedOriginsInput(form.allowedOrigins),\n        });\n        MsgSuccess(t('aiTools.agents.saveSuccess'));\n    } finally {\n        saving.value = false;\n    }\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/config/tabs/settings.vue",
    "content": "<template>\n    <el-tabs v-model=\"activeTab\" @tab-click=\"handleTabClick\">\n        <el-tab-pane v-if=\"agentType === 'openclaw'\" :label=\"t('aiTools.agents.securityTab')\" name=\"security\">\n            <SecurityTab ref=\"securityRef\" />\n        </el-tab-pane>\n        <el-tab-pane :label=\"t('aiTools.agents.otherTab')\" name=\"other\">\n            <OtherTab ref=\"otherRef\" />\n        </el-tab-pane>\n    </el-tabs>\n</template>\n\n<script setup lang=\"ts\">\nimport { nextTick, ref } from 'vue';\nimport { useI18n } from 'vue-i18n';\nimport { AI } from '@/api/interface/ai';\nimport SecurityTab from './settings/security.vue';\nimport OtherTab from './settings/other.vue';\n\nconst { t } = useI18n();\nconst activeTab = ref('security');\nconst agentId = ref(0);\nconst agentType = ref<AI.AgentItem['agentType']>('openclaw');\nconst securityRef = ref();\nconst otherRef = ref();\n\nconst loadCurrentTab = async () => {\n    if (agentId.value <= 0) {\n        return;\n    }\n    await nextTick();\n    if (activeTab.value === 'security' && agentType.value === 'openclaw') {\n        await securityRef.value?.load(agentId.value);\n        return;\n    }\n    if (activeTab.value === 'other') {\n        await otherRef.value?.load(agentId.value);\n    }\n};\n\nconst handleTabClick = async () => {\n    await loadCurrentTab();\n};\n\nconst load = async (agent: AI.AgentItem) => {\n    agentId.value = agent.id;\n    agentType.value = agent.agentType;\n    activeTab.value = agent.agentType === 'openclaw' ? 'security' : 'other';\n    await loadCurrentTab();\n};\n\ndefineExpose({\n    load,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/delete/index.vue",
    "content": "<template>\n    <DialogPro\n        v-model=\"open\"\n        :title=\"$t('commons.button.delete') + ' - ' + agentName\"\n        size=\"small\"\n        @close=\"handleClose\"\n    >\n        <el-form ref=\"deleteForm\" label-position=\"left\" v-loading=\"loading\">\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.forceDelete\" :label=\"$t('website.forceDelete')\" />\n                <span class=\"input-help\">\n                    {{ $t('website.forceDeleteHelper') }}\n                </span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit\" :loading=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n    <TaskLog ref=\"taskLogRef\" @close=\"handleClose\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { deleteAgent } from '@/api/modules/ai';\nimport { newUUID } from '@/utils/util';\nimport TaskLog from '@/components/log/task/index.vue';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst taskLogRef = ref();\nconst agentName = ref('');\nconst deleteForm = ref<FormInstance>();\nconst deleteReq = ref({\n    id: 0,\n    taskID: '',\n    forceDelete: false,\n});\n\nconst emit = defineEmits(['close']);\n\nconst acceptParams = (id: number, name: string) => {\n    deleteReq.value = {\n        id: id,\n        taskID: newUUID(),\n        forceDelete: false,\n    };\n    agentName.value = name;\n    open.value = true;\n};\n\nconst handleClose = () => {\n    open.value = false;\n    emit('close');\n};\n\nconst submit = async () => {\n    loading.value = true;\n    try {\n        await deleteAgent(deleteReq.value);\n        handleClose();\n        taskLogRef.value?.openWithTaskID(deleteReq.value.taskID);\n    } finally {\n        loading.value = false;\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/agent/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <DockerStatus v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" />\n        <LayoutContent v-loading=\"loading\" v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\" :disabled=\"noApp\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch v-model:searchName=\"searchName\" @search=\"search\" />\n                <TableRefresh @search=\"search\" />\n            </template>\n            <template #main>\n                <NoApp v-if=\"noApp\" />\n                <ComplexTable :data=\"items\" :pagination-config=\"paginationConfig\" @search=\"search\" v-if=\"!noApp\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        show-overflow-tooltip\n                        prop=\"name\"\n                        min-width=\"120\"\n                    />\n                    <el-table-column :label=\"$t('commons.table.type')\" prop=\"agentType\" min-width=\"120\">\n                        <template #default=\"{ row }\">\n                            <div class=\"agent-type-cell\">\n                                <img\n                                    class=\"agent-type-icon\"\n                                    :src=\"row.agentType === 'copaw' ? copawIcon : openclawIcon\"\n                                    :alt=\"row.agentType === 'copaw' ? 'CoPaw' : 'OpenClaw'\"\n                                />\n                                <span>\n                                    {{\n                                        row.agentType === 'copaw'\n                                            ? $t('aiTools.agents.copawType')\n                                            : $t('aiTools.agents.openclawType')\n                                    }}\n                                </span>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\" width=\"120\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('aiTools.agents.appVersion')\" prop=\"appVersion\" min-width=\"140\">\n                        <template #default=\"{ row }\">\n                            <div class=\"version-cell\">\n                                <span>{{ row.appVersion }}</span>\n                                <el-button v-if=\"row.upgradable\" link type=\"primary\" @click=\"openUpgrade(row)\">\n                                    {{ $t('commons.button.upgrade') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('aiTools.model.model')\"\n                        show-overflow-tooltip\n                        prop=\"provider\"\n                        min-width=\"120\"\n                    >\n                        <template #default=\"{ row }\">\n                            <template v-if=\"row.agentType !== 'copaw'\">\n                                <span>{{ getAgentProviderDisplayName(row.provider, row.providerName) }}</span>\n                                <div>\n                                    <span>{{ row.model }}</span>\n                                </div>\n                            </template>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.port')\" prop=\"webUIPort\" min-width=\"150\">\n                        <template #default=\"{ row }\">\n                            <el-button icon=\"Position\" plain size=\"small\" @click=\"jumpWebUI(row)\">\n                                {{ $t('aiTools.agents.webuiPort') }}: {{ row.webUIPort }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('runtime.workDir')\" min-width=\"90\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"openWorkDir(row)\">\n                                <el-icon>\n                                    <FolderOpened />\n                                </el-icon>\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('aiTools.agents.token')\" min-width=\"80\">\n                        <template #default=\"{ row }\">\n                            <el-space v-if=\"row.agentType !== 'copaw'\">\n                                <CopyButton :content=\"row.token\" />\n                                <el-button link type=\"primary\" @click=\"onResetToken(row)\">\n                                    {{ $t('commons.button.reset') }}\n                                </el-button>\n                            </el-space>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('commons.table.date')\"\n                        prop=\"createdAt\"\n                        width=\"180\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        :buttons=\"buttons\"\n                        min-width=\"220\"\n                        :label=\"$t('commons.table.operate')\"\n                        fixed=\"right\"\n                        :ellipsis=\"3\"\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <AddDialog ref=\"addRef\" @search=\"search\" @task=\"openTaskLog\" />\n        <TaskLog ref=\"taskLogRef\" @close=\"search\" />\n        <DeleteDialog ref=\"deleteRef\" @close=\"search\" />\n        <ConfigDrawer ref=\"configRef\" @updated=\"search\" />\n        <AppUpgrade ref=\"upgradeRef\" @close=\"search\" />\n        <ComposeLogs ref=\"composeLogRef\" />\n        <AgentTerminalDialog ref=\"dialogTerminalRef\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, reactive, ref } from 'vue';\nimport { useRoute, useRouter } from 'vue-router';\nimport { pageAgents, resetAgentToken } from '@/api/modules/ai';\nimport { installedOp, searchApp, searchAppInstalled } from '@/api/modules/app';\nimport { AI } from '@/api/interface/ai';\nimport { App } from '@/api/interface/app';\nimport { SearchWithPage } from '@/api/interface';\nimport { dateFormat, newUUID } from '@/utils/util';\nimport { MsgSuccess } from '@/utils/message';\n\nimport RouterMenu from '@/views/ai/agents/index.vue';\nimport AddDialog from '@/views/ai/agents/agent/add/index.vue';\nimport DeleteDialog from '@/views/ai/agents/agent/delete/index.vue';\nimport ConfigDrawer from '@/views/ai/agents/agent/config/index.vue';\nimport AppUpgrade from '@/views/app-store/installed/upgrade/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport AgentTerminalDialog from '@/views/ai/agents/agent/components/terminal.vue';\nimport i18n from '@/lang';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { getAgentProviderDisplayName } from '@/utils/agent';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { compareVersion } from '@/utils/version';\nimport NoApp from '@/views/app-store/apps/no-app/index.vue';\nimport openclawIcon from '@/assets/images/ai-agent-openclaw.svg';\nimport copawIcon from '@/assets/images/ai-agent-copaw.svg';\n\nconst items = ref<AI.AgentItem[]>([]);\nconst loading = ref(false);\nconst addRef = ref();\nconst taskLogRef = ref();\nconst deleteRef = ref();\nconst configRef = ref();\nconst upgradeRef = ref();\nconst composeLogRef = ref();\nconst dialogTerminalRef = ref();\nconst dialogPortJumpRef = ref();\nconst route = useRoute();\nconst router = useRouter();\nconst isActive = ref(false);\nconst isExist = ref(false);\nconst noApp = ref(false);\nconst searchName = ref('');\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.config'),\n        click: (row: AI.AgentItem) => openConfig(row),\n        show: (row: AI.AgentItem) => row.agentType !== 'copaw',\n        disabled: (row: AI.AgentItem) => row.status !== 'Running',\n    },\n    {\n        label: i18n.global.t('menu.terminal'),\n        click: (row: AI.AgentItem) => openTerminal(row),\n    },\n    {\n        label: i18n.global.t('commons.button.log'),\n        click: (row: AI.AgentItem) => openLog(row),\n    },\n    {\n        label: i18n.global.t('commons.operate.start'),\n        click: (row: AI.AgentItem) => onOperate(row, 'start'),\n        disabled: (row: AI.AgentItem) => row.status === 'Running',\n    },\n    {\n        label: i18n.global.t('commons.operate.stop'),\n        click: (row: AI.AgentItem) => onOperate(row, 'stop'),\n        disabled: (row: AI.AgentItem) => row.status !== 'Running',\n    },\n    {\n        label: i18n.global.t('commons.operate.restart'),\n        click: (row: AI.AgentItem) => onOperate(row, 'restart'),\n    },\n    {\n        label: i18n.global.t('commons.button.upgrade'),\n        click: (row: AI.AgentItem) => openUpgrade(row),\n        disabled: (row: AI.AgentItem) => !row.upgradable,\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: AI.AgentItem) => onDelete(row),\n    },\n];\n\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst checkNoApp = async () => {\n    try {\n        const appRes = await searchApp({\n            page: 1,\n            pageSize: 1,\n            name: '',\n            tags: [],\n            resource: 'all',\n            showCurrentArch: false,\n        });\n        noApp.value = (appRes.data.total || 0) === 0;\n    } catch {\n        noApp.value = false;\n    }\n};\n\nconst search = async () => {\n    loading.value = true;\n    try {\n        const req: SearchWithPage = {\n            page: paginationConfig.currentPage,\n            pageSize: paginationConfig.pageSize,\n            info: searchName.value || '',\n        };\n        const [res] = await Promise.all([pageAgents(req), checkNoApp()]);\n        items.value = res.data.items || [];\n        paginationConfig.total = res.data.total || 0;\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst openCreate = (agentType?: 'openclaw' | 'copaw') => {\n    if (noApp.value) {\n        return;\n    }\n    const targetType = agentType === 'copaw' ? 'copaw' : 'openclaw';\n    if (addRef.value?.open) {\n        addRef.value.open(targetType);\n    }\n};\n\nconst openCreateFromQuery = async () => {\n    const shouldOpen = route.query.open === 'create';\n    if (!shouldOpen) {\n        return;\n    }\n    const agentType = route.query.agentType === 'copaw' ? 'copaw' : 'openclaw';\n    openCreate(agentType);\n    const nextQuery = { ...route.query };\n    delete nextQuery.open;\n    delete nextQuery.agentType;\n    await router.replace({ path: route.path, query: nextQuery });\n};\n\nconst openTaskLog = (taskID: string) => {\n    if (taskLogRef.value?.openWithTaskID) {\n        taskLogRef.value.openWithTaskID(taskID);\n    }\n};\n\nconst onOperate = async (row: AI.AgentItem, operate: string) => {\n    await ElMessageBox.confirm(\n        i18n.global.t('app.operatorHelper', [i18n.global.t('commons.operate.' + operate)]),\n        i18n.global.t('commons.operate.' + operate),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    );\n    const taskID = newUUID();\n    await installedOp({ installId: row.appInstallId, operate, taskID });\n    await search();\n};\n\nconst openLog = (row: AI.AgentItem) => {\n    if (row.status === 'Installing') {\n        taskLogRef.value?.openWithResourceID('App', 'TaskInstall', row.appInstallId);\n        return;\n    }\n    composeLogRef.value?.acceptParams({\n        compose: `${row.path}/docker-compose.yml`,\n        resource: row.name,\n        container: row.containerName,\n    });\n};\n\nconst openTerminal = (row: AI.AgentItem) => {\n    const title = i18n.global.t('aiTools.agents.agent') + ' ' + row.name;\n    dialogTerminalRef.value?.acceptParams({\n        containerID: row.containerName,\n        title,\n        users: ['node', 'root'],\n        shell: '/bin/bash',\n    });\n};\n\nconst openWorkDir = (row: AI.AgentItem) => {\n    if (!row.path) {\n        return;\n    }\n    routerToFileWithPath(`${row.path}/data`);\n};\n\nconst isOpenClawHttpsVersion = (version: string) => {\n    const target = String(version || '')\n        .trim()\n        .toLowerCase();\n    if (!target || target === 'latest') {\n        return true;\n    }\n    if (!/\\d/.test(target)) {\n        return true;\n    }\n    return compareVersion(target, '2026.3.13');\n};\n\nconst jumpWebUI = (row: AI.AgentItem) => {\n    if (dialogPortJumpRef.value?.acceptParams) {\n        dialogPortJumpRef.value.acceptParams({\n            port: row.webUIPort,\n            protocol: row.agentType === 'openclaw' && isOpenClawHttpsVersion(row.appVersion) ? 'https' : 'http',\n            path: row.agentType === 'copaw' ? undefined : '/',\n            hash: row.agentType === 'copaw' ? undefined : `token=${row.token}`,\n        });\n    }\n};\n\nconst onDelete = (row: AI.AgentItem) => {\n    deleteRef.value?.acceptParams(row.id, row.name);\n};\n\nconst onResetToken = async (row: AI.AgentItem) => {\n    await ElMessageBox.confirm(\n        i18n.global.t('aiTools.mcp.operatorHelper', ['token', i18n.global.t('commons.button.reset')]),\n        i18n.global.t('commons.button.reset'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    );\n    await resetAgentToken({ id: row.id });\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    await search();\n};\n\nconst openConfig = (row: AI.AgentItem) => {\n    if (row.agentType === 'copaw') {\n        return;\n    }\n    configRef.value?.open(row);\n};\n\nconst openUpgrade = async (row: AI.AgentItem) => {\n    const res = await searchAppInstalled({ page: 1, pageSize: 200, name: row.name, update: true });\n    const appInstall = (res.data.items || []).find((item: App.AppInstallDto) => item.id === row.appInstallId);\n    if (!appInstall) {\n        return;\n    }\n    upgradeRef.value?.acceptParams(appInstall, 'upgrade');\n};\n\nonMounted(async () => {\n    await search();\n    await openCreateFromQuery();\n});\n</script>\n\n<style scoped>\n.version-cell {\n    display: inline-flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.agent-type-cell {\n    display: inline-flex;\n    align-items: center;\n    gap: 6px;\n}\n\n.agent-type-icon {\n    width: 16px;\n    height: 16px;\n    flex: 0 0 16px;\n    object-fit: contain;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('aiTools.agents.agent'),\n        path: '/ai/agents/agent',\n    },\n    {\n        label: i18n.global.t('aiTools.agents.account'),\n        path: '/ai/agents/model',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/model/add/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"headerTitle\" size=\"large\" @close=\"handleClose\">\n        <el-form ref=\"formRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model=\"form.name\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('aiTools.agents.provider')\" prop=\"provider\">\n                <el-select v-model=\"form.provider\" :disabled=\"form.id > 0\" @change=\"handleProviderChange\">\n                    <el-option\n                        v-for=\"item in providerOptions\"\n                        :key=\"item.value\"\n                        :label=\"item.label\"\n                        :value=\"item.value\"\n                    />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('aiTools.agents.apiKey')\" prop=\"apiKey\">\n                <el-input v-model=\"form.apiKey\" type=\"password\" show-password />\n                <span class=\"input-help\" v-if=\"form.provider === 'custom' || form.provider === 'vllm'\">\n                    {{ $t('aiTools.agents.customProviderHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"form.rememberApiKey\">{{ $t('terminal.rememberPassword') }}</el-checkbox>\n            </el-form-item>\n            <el-form-item :label=\"$t('aiTools.agents.baseUrl')\" prop=\"baseURL\">\n                <el-input v-model=\"form.baseURL\" :disabled=\"!editableBaseURLProviders.includes(form.provider)\" />\n            </el-form-item>\n            <el-form-item :label=\"'API ' + $t('commons.table.type')\" prop=\"apiType\">\n                <el-select v-model=\"form.apiType\">\n                    <el-option label=\"openai-completions\" value=\"openai-completions\" />\n                    <el-option label=\"openai-responses\" value=\"openai-responses\" />\n                    <el-option\n                        v-if=\"form.provider === 'custom' || form.provider === 'vllm'\"\n                        label=\"anthropic-messages\"\n                        value=\"anthropic-messages\"\n                    />\n                </el-select>\n            </el-form-item>\n            <template v-if=\"showInitialModel\">\n                <el-divider content-position=\"left\">{{ $t('aiTools.agents.accountModels') }}</el-divider>\n                <el-form-item :label=\"$t('aiTools.model.model')\" prop=\"initialModel.id\" :rules=\"[Rules.requiredInput]\">\n                    <el-input v-model=\"form.initialModel.id\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.table.name')\" prop=\"initialModel.name\">\n                    <el-input v-model=\"form.initialModel.name\" />\n                </el-form-item>\n                <el-form-item label=\"Context Window\" prop=\"initialModel.contextWindow\">\n                    <el-input-number v-model=\"form.initialModel.contextWindow\" :min=\"1\" :max=\"2000000\" />\n                </el-form-item>\n                <el-form-item label=\"Max Tokens\" prop=\"initialModel.maxTokens\">\n                    <el-input-number v-model=\"form.initialModel.maxTokens\" :min=\"1\" :max=\"2000000\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('aiTools.agents.modelInputTypes')\" prop=\"initialModel.input\">\n                    <el-checkbox-group v-model=\"form.initialModel.input\">\n                        <el-checkbox label=\"text\">Text</el-checkbox>\n                        <el-checkbox label=\"image\">Image</el-checkbox>\n                    </el-checkbox-group>\n                </el-form-item>\n                <el-form-item :label=\"$t('aiTools.agents.reasoning')\" prop=\"initialModel.reasoning\">\n                    <el-switch v-model=\"form.initialModel.reasoning\" />\n                </el-form-item>\n            </template>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                <el-input v-model=\"form.remark\" />\n            </el-form-item>\n            <el-form-item v-if=\"form.id\" prop=\"syncAgents\">\n                <el-checkbox v-model=\"form.syncAgents\" :label=\"$t('aiTools.agents.syncAgents')\" />\n                <span class=\"input-help\">{{ $t('aiTools.agents.syncAgentsHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"submit\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport { AI } from '@/api/interface/ai';\nimport { Rules } from '@/global/form-rules';\nimport { createAgentAccount, getAgentProviders, updateAgentAccount } from '@/api/modules/ai';\nimport i18n from '@/lang';\nimport { getAgentProviderDisplayName } from '@/utils/agent';\nimport { MsgError } from '@/utils/message';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\n\nconst emit = defineEmits(['search']);\n\nconst open = ref(false);\nconst formRef = ref<FormInstance>();\nconst providerOptions = ref<Array<{ label: string; value: string }>>([]);\nconst providerBaseURL = ref<Record<string, string>>({});\nconst loading = ref(false);\nconst editableBaseURLProviders = ['ollama', 'custom', 'vllm', 'zai'];\nconst initialModelProviders = ['custom', 'vllm', 'ollama'];\nconst { isIntl } = useGlobalStore();\n\nconst form = reactive({\n    id: 0,\n    provider: '',\n    name: '',\n    baseURL: '',\n    apiType: 'openai-completions',\n    apiKey: '',\n    rememberApiKey: false,\n    initialModel: {} as AI.AgentAccountModel,\n    remark: '',\n    syncAgents: false,\n});\n\nconst headerTitle = computed(() =>\n    form.id ? i18n.global.t('commons.button.edit') : i18n.global.t('commons.button.create'),\n);\n\nconst showInitialModel = computed(() => !form.id && initialModelProviders.includes(form.provider));\n\nconst rules = reactive({\n    provider: [Rules.requiredSelect],\n    name: [Rules.requiredInput],\n    apiKey: [Rules.requiredInput],\n    baseURL: [Rules.requiredInput],\n    apiType: [Rules.requiredSelect],\n});\n\nconst isInitialModelProvider = (provider: string) => initialModelProviders.includes(provider);\n\nconst defaultContextWindowForProvider = (provider: string) => {\n    if (provider === 'custom' || provider === 'vllm') {\n        return 128000;\n    }\n    return 256000;\n};\n\nconst buildInitialModel = (provider: string): AI.AgentAccountModel => ({\n    recordId: 0,\n    id: '',\n    name: '',\n    contextWindow: defaultContextWindowForProvider(provider),\n    maxTokens: 8192,\n    reasoning: false,\n    input: ['text'],\n});\n\nconst normalizeInitialModel = () => {\n    const item = {\n        recordId: 0,\n        id: String(form.initialModel.id || '').trim(),\n        name: String(form.initialModel.name || form.initialModel.id || '').trim(),\n        contextWindow: Number(form.initialModel.contextWindow || 0) || defaultContextWindowForProvider(form.provider),\n        maxTokens: Number(form.initialModel.maxTokens || 0) || 8192,\n        reasoning: Boolean(form.initialModel.reasoning),\n        input: Array.from(\n            new Set((form.initialModel.input || []).filter((value) => value === 'text' || value === 'image')),\n        ),\n    };\n    if (item.id === '') {\n        return null;\n    }\n    return item;\n};\n\nconst resetInitialModel = () => {\n    form.initialModel = isInitialModelProvider(form.provider)\n        ? buildInitialModel(form.provider)\n        : ({} as AI.AgentAccountModel);\n};\n\nconst submit = async () => {\n    if (!formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    const initialModel = normalizeInitialModel();\n    if (showInitialModel.value && !initialModel) {\n        MsgError(i18n.global.t('aiTools.agents.accountModelsRequired'));\n        return;\n    }\n    loading.value = true;\n    try {\n        if (form.id) {\n            await updateAgentAccount({\n                id: form.id,\n                name: form.name,\n                baseURL: form.baseURL,\n                apiKey: form.apiKey,\n                rememberApiKey: form.rememberApiKey,\n                apiType: form.apiType,\n                remark: form.remark,\n                syncAgents: form.syncAgents,\n            });\n        } else {\n            await createAgentAccount({\n                provider: form.provider,\n                name: form.name,\n                baseURL: form.baseURL,\n                apiKey: form.apiKey,\n                rememberApiKey: form.rememberApiKey,\n                apiType: form.apiType,\n                models: initialModel ? [initialModel] : [],\n                remark: form.remark,\n            });\n        }\n        emit('search');\n        open.value = false;\n    } catch (error: any) {\n        MsgError(String(error?.message || i18n.global.t('commons.res.commonError')));\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst handleClose = () => {\n    formRef.value?.resetFields();\n    loading.value = false;\n    form.id = 0;\n    form.provider = '';\n    form.name = '';\n    form.baseURL = '';\n    form.apiType = 'openai-completions';\n    form.apiKey = '';\n    form.rememberApiKey = false;\n    form.initialModel = {} as AI.AgentAccountModel;\n    form.remark = '';\n    form.syncAgents = false;\n};\n\ninterface OpenParams {\n    id?: number;\n    provider?: string;\n    name?: string;\n    baseURL?: string;\n    apiKey?: string;\n    rememberApiKey?: boolean;\n    apiType?: string;\n    remark?: string;\n}\n\nconst openDrawer = async (params?: OpenParams) => {\n    open.value = true;\n    loading.value = false;\n    if (providerOptions.value.length === 0) {\n        await loadProviders();\n    }\n    if (params?.id) {\n        form.id = params.id;\n        form.provider = params.provider || '';\n        form.name = params.name || '';\n        form.baseURL = params.baseURL || '';\n        form.apiKey = params.apiKey || '';\n        form.rememberApiKey = params.rememberApiKey || false;\n        form.apiType = params.apiType || 'openai-completions';\n        form.remark = params.remark || '';\n        form.syncAgents = false;\n        return;\n    }\n    form.id = 0;\n    form.name = '';\n    form.baseURL = '';\n    form.apiKey = '';\n    form.rememberApiKey = false;\n    form.apiType = 'openai-completions';\n    form.initialModel = {} as AI.AgentAccountModel;\n    form.remark = '';\n    form.syncAgents = false;\n    if (params?.provider) {\n        form.provider = params.provider;\n    } else if (providerOptions.value.length > 0) {\n        form.provider = providerOptions.value[0].value;\n    }\n    handleProviderChange();\n};\n\nconst loadProviders = async () => {\n    const res = await getAgentProviders();\n    const data = res.data || [];\n    const blockedProviders = new Set(['ark-coding-plan', 'bailian-coding-plan']);\n    const filteredData = isIntl.value ? data.filter((item) => !blockedProviders.has(item.provider)) : data;\n    providerOptions.value = filteredData.map((item) => ({\n        value: item.provider,\n        label: getAgentProviderDisplayName(item.provider, item.displayName),\n    }));\n    providerBaseURL.value = filteredData.reduce((acc, item) => {\n        acc[item.provider] = item.baseUrl || '';\n        return acc;\n    }, {} as Record<string, string>);\n    if (!form.provider && providerOptions.value.length > 0) {\n        form.provider = providerOptions.value[0].value;\n        handleProviderChange();\n    }\n};\n\nconst handleProviderChange = () => {\n    if (form.provider === 'custom' || form.provider === 'vllm') {\n        form.baseURL = '';\n        form.apiType = form.apiType || 'openai-completions';\n    } else if (form.provider === 'ollama') {\n        form.baseURL = '';\n        form.apiType = 'openai-responses';\n    } else {\n        form.baseURL = providerBaseURL.value[form.provider] || '';\n        if (!form.apiType) {\n            form.apiType = 'openai-completions';\n        }\n    }\n    if (!form.id) {\n        resetInitialModel();\n    }\n};\n\nonMounted(async () => {\n    await loadProviders();\n});\n\ndefineExpose({\n    open: openDrawer,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/model/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <LayoutContent>\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\">{{ $t('commons.button.create') }}</el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch v-model:searchName=\"searchName\" @search=\"search\" />\n                <TableRefresh @search=\"search\" />\n            </template>\n            <template #main>\n                <ComplexTable :data=\"items\" :pagination-config=\"paginationConfig\" @search=\"search\">\n                    <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\" min-width=\"200\" />\n                    <el-table-column :label=\"$t('aiTools.agents.provider')\" prop=\"provider\" width=\"200\">\n                        <template #default=\"{ row }\">\n                            {{ getAgentProviderDisplayName(row.provider, row.providerName) }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('aiTools.agents.baseUrl')\" prop=\"baseUrl\" min-width=\"200\" />\n                    <el-table-column :label=\"$t('aiTools.agents.apiKey')\" prop=\"apiKey\" min-width=\"160\">\n                        <template #default=\"{ row }\">\n                            {{ maskKey(row.apiKey) }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('aiTools.agents.verified')\" prop=\"verified\" width=\"120\">\n                        <template #default=\"{ row }\">\n                            <el-tag :type=\"verificationTagType(row)\">\n                                {{ verificationLabel(row) }}\n                            </el-tag>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('commons.table.date')\"\n                        prop=\"createdAt\"\n                        width=\"180\"\n                        :formatter=\"dateFormat\"\n                    />\n                    <fu-table-operations\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        fixed=\"right\"\n                        width=\"180\"\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <AddDialog ref=\"addRef\" @search=\"search\" />\n        <ModelPoolDialog ref=\"modelPoolRef\" @updated=\"search\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, reactive, ref } from 'vue';\nimport { deleteAgentAccount, pageAgentAccounts } from '@/api/modules/ai';\nimport { AI } from '@/api/interface/ai';\nimport RouterMenu from '@/views/ai/agents/index.vue';\nimport AddDialog from '@/views/ai/agents/model/add/index.vue';\nimport ModelPoolDialog from '@/views/ai/agents/model/pool/index.vue';\nimport { ElMessageBox } from 'element-plus';\nimport i18n from '@/lang';\nimport { dateFormat } from '@/utils/util';\nimport { getAgentProviderDisplayName } from '@/utils/agent';\n\nconst items = ref<AI.AgentAccountItem[]>([]);\nconst addRef = ref();\nconst modelPoolRef = ref();\nconst searchName = ref('');\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: AI.AgentAccountItem) => onEdit(row),\n    },\n    {\n        label: i18n.global.t('aiTools.agents.modelPool'),\n        click: (row: AI.AgentAccountItem) => onManageModelPool(row),\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: AI.AgentAccountItem) => onDelete(row),\n    },\n];\n\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst search = async () => {\n    const req: AI.AgentAccountSearch = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        provider: '',\n        name: searchName.value || '',\n    };\n    const res = await pageAgentAccounts(req);\n    items.value = res.data.items || [];\n    paginationConfig.total = res.data.total || 0;\n};\n\nconst openCreate = () => {\n    if (addRef.value?.open) {\n        addRef.value.open({\n            provider: 'deepseek',\n        });\n    }\n};\n\nconst onEdit = (row: AI.AgentAccountItem) => {\n    if (addRef.value?.open) {\n        addRef.value.open({\n            id: row.id,\n            provider: row.provider,\n            name: row.name,\n            baseURL: row.baseUrl,\n            apiKey: row.apiKey,\n            rememberApiKey: row.rememberApiKey,\n            apiType: row.apiType,\n            remark: row.remark,\n        });\n    }\n};\n\nconst onManageModelPool = (row: AI.AgentAccountItem) => {\n    if (modelPoolRef.value?.open) {\n        modelPoolRef.value.open(row);\n    }\n};\n\nconst isVerificationSkipped = (provider: string) => {\n    const key = (provider || '').toLowerCase();\n    return key === 'custom' || key === 'vllm' || key === 'ollama' || key === 'kimi-coding';\n};\n\nconst verificationLabel = (row: AI.AgentAccountItem) => {\n    if (isVerificationSkipped(row.provider)) {\n        return i18n.global.t('aiTools.agents.verifySkipped');\n    }\n    return row.verified ? 'OK' : 'N/A';\n};\n\nconst verificationTagType = (row: AI.AgentAccountItem) => {\n    if (isVerificationSkipped(row.provider)) {\n        return 'info';\n    }\n    return row.verified ? 'success' : 'info';\n};\n\nconst maskKey = (value: string) => {\n    if (!value) {\n        return '';\n    }\n    if (value.length <= 6) {\n        return value;\n    }\n    return `${value.slice(0, 3)}****${value.slice(-3)}`;\n};\n\nconst onDelete = async (row: AI.AgentAccountItem) => {\n    await ElMessageBox.confirm(\n        i18n.global.t('commons.msg.delete', [row.name]),\n        i18n.global.t('commons.button.delete'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    );\n    await deleteAgentAccount({ id: row.id });\n    await search();\n};\n\nonMounted(async () => {\n    await search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/agents/model/pool/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"headerTitle\" size=\"large\" @close=\"handleClose\">\n        <template #content>\n            <div v-loading=\"loading\">\n                <el-alert\n                    :title=\"$t('aiTools.agents.modelPoolHelper')\"\n                    type=\"info\"\n                    :closable=\"false\"\n                    show-icon\n                    class=\"mb-4\"\n                />\n                <div class=\"toolbar\">\n                    <el-button type=\"primary\" @click=\"openCreate\">\n                        {{ $t('commons.button.add') }}\n                    </el-button>\n                </div>\n                <ComplexTable :data=\"items\">\n                    <el-table-column :label=\"$t('aiTools.model.model')\" prop=\"id\" min-width=\"220\" />\n                    <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\" min-width=\"180\" />\n                    <el-table-column label=\"Context Window\" prop=\"contextWindow\" min-width=\"140\" />\n                    <el-table-column label=\"Max Tokens\" prop=\"maxTokens\" min-width=\"120\" />\n                    <el-table-column :label=\"$t('aiTools.agents.modelInputTypes')\" min-width=\"140\">\n                        <template #default=\"{ row }\">\n                            {{ (row.input || []).join(', ') || '-' }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('aiTools.agents.reasoning')\" min-width=\"120\">\n                        <template #default=\"{ row }\">\n                            <el-tag :type=\"row.reasoning ? 'success' : 'info'\">\n                                {{ row.reasoning ? $t('commons.true') : $t('commons.false') }}\n                            </el-tag>\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fixed=\"right\" />\n                </ComplexTable>\n            </div>\n        </template>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n\n    <DrawerPro v-model=\"editorOpen\" :header=\"editorTitle\" size=\"normal\" @close=\"handleEditorClose\">\n        <el-form ref=\"formRef\" :model=\"form\" label-position=\"top\" :rules=\"rules\" v-loading=\"saving\">\n            <el-form-item :label=\"$t('aiTools.model.model')\" prop=\"id\">\n                <el-select\n                    v-model=\"form.id\"\n                    filterable\n                    allow-create\n                    default-first-option\n                    :reserve-keyword=\"false\"\n                    @change=\"handleModelIDChange\"\n                >\n                    <el-option\n                        v-for=\"model in providerModelOptions\"\n                        :key=\"model.id\"\n                        :label=\"model.name\"\n                        :value=\"model.id\"\n                    />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model=\"form.name\" />\n            </el-form-item>\n            <el-form-item label=\"Context Window\" prop=\"contextWindow\">\n                <el-input-number v-model=\"form.contextWindow\" :min=\"1\" :max=\"2000000\" />\n            </el-form-item>\n            <el-form-item label=\"Max Tokens\" prop=\"maxTokens\">\n                <el-input-number v-model=\"form.maxTokens\" :min=\"1\" :max=\"2000000\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('aiTools.agents.modelInputTypes')\" prop=\"input\">\n                <el-checkbox-group v-model=\"form.input\">\n                    <el-checkbox label=\"text\">Text</el-checkbox>\n                    <el-checkbox label=\"image\">Image</el-checkbox>\n                </el-checkbox-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('aiTools.agents.reasoning')\" prop=\"reasoning\">\n                <el-switch v-model=\"form.reasoning\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"saving\" @click=\"editorOpen = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"saving\" type=\"primary\" @click=\"submit\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, reactive, ref } from 'vue';\nimport type { FormInstance } from 'element-plus';\nimport { ElMessageBox } from 'element-plus';\nimport {\n    createAgentAccountModel,\n    deleteAgentAccountModel,\n    getAgentAccountModels,\n    getAgentProviders,\n    updateAgentAccountModel,\n} from '@/api/modules/ai';\nimport { AI } from '@/api/interface/ai';\nimport i18n from '@/lang';\nimport { MsgError } from '@/utils/message';\nimport { getAgentProviderDisplayName } from '@/utils/agent';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nimport { Rules } from '@/global/form-rules';\n\nconst emit = defineEmits(['updated']);\n\nconst open = ref(false);\nconst editorOpen = ref(false);\nconst loading = ref(false);\nconst saving = ref(false);\nconst formRef = ref<FormInstance>();\nconst providerModels = ref<Record<string, AI.ProviderModelInfo[]>>({});\nconst items = ref<AI.AgentAccountModel[]>([]);\nconst { isIntl } = useGlobalStore();\n\nconst account = reactive({\n    id: 0,\n    name: '',\n    provider: '',\n});\n\nconst form = reactive<AI.AgentAccountModel>({\n    recordId: 0,\n    id: '',\n    name: '',\n    contextWindow: 128000,\n    maxTokens: 8192,\n    reasoning: false,\n    input: ['text'],\n});\n\nconst headerTitle = computed(() => {\n    const accountName = account.name || getAgentProviderDisplayName(account.provider, account.provider);\n    return `${accountName} · ${i18n.global.t('aiTools.agents.modelPool')}`;\n});\n\nconst editorTitle = computed(() =>\n    form.recordId ? i18n.global.t('commons.button.edit') : i18n.global.t('commons.button.add'),\n);\n\nconst providerModelOptions = computed(() => providerModels.value[account.provider] || []);\n\nconst rules = reactive({\n    id: [Rules.requiredInput],\n    name: [Rules.requiredInput],\n});\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: AI.AgentAccountModel) => openEdit(row),\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: AI.AgentAccountModel) => onDelete(row),\n    },\n];\n\nconst buildModelItem = (model?: Partial<AI.AgentAccountModel>): AI.AgentAccountModel => ({\n    recordId: Number(model?.recordId || 0),\n    id: String(model?.id || '').trim(),\n    name: String(model?.name || '').trim(),\n    contextWindow: Number(model?.contextWindow || 0) || 128000,\n    maxTokens: Number(model?.maxTokens || 0) || 8192,\n    reasoning: Boolean(model?.reasoning),\n    input: Array.from(new Set((model?.input || []).filter((value) => value === 'text' || value === 'image'))),\n});\n\nconst loadProviders = async () => {\n    if (Object.keys(providerModels.value).length > 0) {\n        return;\n    }\n    const res = await getAgentProviders();\n    const data = res.data || [];\n    const blockedProviders = new Set(['ark-coding-plan', 'bailian-coding-plan']);\n    const filteredData = isIntl.value ? data.filter((item) => !blockedProviders.has(item.provider)) : data;\n    providerModels.value = filteredData.reduce((acc, item) => {\n        acc[item.provider] = item.models || [];\n        return acc;\n    }, {} as Record<string, AI.ProviderModelInfo[]>);\n};\n\nconst search = async () => {\n    if (!account.id) {\n        items.value = [];\n        return;\n    }\n    loading.value = true;\n    try {\n        const res = await getAgentAccountModels({ accountId: account.id });\n        items.value = (res.data || []).map((item) => buildModelItem(item));\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst resetForm = () => {\n    form.recordId = 0;\n    form.id = '';\n    form.name = '';\n    form.contextWindow = 128000;\n    form.maxTokens = 8192;\n    form.reasoning = false;\n    form.input = ['text'];\n};\n\nconst openCreate = () => {\n    resetForm();\n    const firstModel = providerModelOptions.value[0];\n    if (firstModel) {\n        form.id = firstModel.id;\n        form.name = firstModel.name || firstModel.id;\n        form.contextWindow = firstModel.contextWindow || 128000;\n        form.maxTokens = firstModel.maxTokens || 8192;\n        form.reasoning = firstModel.reasoning;\n        form.input = firstModel.input?.length ? [...firstModel.input] : ['text'];\n    }\n    editorOpen.value = true;\n};\n\nconst openEdit = (row: AI.AgentAccountModel) => {\n    Object.assign(form, buildModelItem(row));\n    editorOpen.value = true;\n};\n\nconst handleModelIDChange = (value: string) => {\n    form.id = String(value || '').trim();\n    const matched = providerModelOptions.value.find((item) => item.id === form.id);\n    if (!matched) {\n        return;\n    }\n    if (!form.name) {\n        form.name = matched.name || matched.id;\n    }\n    form.contextWindow = matched.contextWindow || form.contextWindow || 128000;\n    form.maxTokens = matched.maxTokens || form.maxTokens || 8192;\n    form.reasoning = matched.reasoning;\n    form.input = matched.input?.length ? [...matched.input] : ['text'];\n};\n\nconst submit = async () => {\n    if (!account.id || !formRef.value) {\n        return;\n    }\n    await formRef.value.validate();\n    const duplicate = items.value.some((item) => item.id === form.id && item.recordId !== form.recordId);\n    if (duplicate) {\n        MsgError(i18n.global.t('aiTools.agents.accountModelsDuplicate'));\n        return;\n    }\n    if (!form.input?.length) {\n        form.input = ['text'];\n    }\n    saving.value = true;\n    try {\n        if (form.recordId) {\n            await updateAgentAccountModel({\n                accountId: account.id,\n                model: buildModelItem(form),\n            });\n        } else {\n            await createAgentAccountModel({\n                accountId: account.id,\n                model: buildModelItem(form),\n            });\n        }\n        editorOpen.value = false;\n        await search();\n        emit('updated');\n    } catch (error: any) {\n        MsgError(String(error?.message || i18n.global.t('commons.res.commonError')));\n    } finally {\n        saving.value = false;\n    }\n};\n\nconst onDelete = async (row: AI.AgentAccountModel) => {\n    await ElMessageBox.confirm(\n        i18n.global.t('commons.msg.delete', [row.name || row.id]),\n        i18n.global.t('commons.button.delete'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    );\n    await deleteAgentAccountModel({\n        accountId: account.id,\n        recordId: row.recordId,\n    });\n    await search();\n    emit('updated');\n};\n\nconst handleEditorClose = () => {\n    formRef.value?.resetFields();\n    resetForm();\n    saving.value = false;\n};\n\nconst handleClose = () => {\n    loading.value = false;\n    account.id = 0;\n    account.name = '';\n    account.provider = '';\n    items.value = [];\n    editorOpen.value = false;\n    handleEditorClose();\n};\n\nconst openDrawer = async (row: AI.AgentAccountItem) => {\n    account.id = row.id;\n    account.name = row.name;\n    account.provider = row.provider;\n    open.value = true;\n    await loadProviders();\n    await search();\n};\n\ndefineExpose({\n    open: openDrawer,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.toolbar {\n    display: flex;\n    justify-content: flex-start;\n    margin-bottom: 16px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/gpu/current/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <div v-if=\"gpuType == 'nvidia'\">\n            <LayoutContent\n                v-loading=\"loading\"\n                :title=\"$t('aiTools.gpu.gpu')\"\n                :divider=\"true\"\n                v-if=\"gpuInfo.driverVersion.length !== 0 && !loading\"\n            >\n                <template #rightToolBar>\n                    <TableSetting title=\"gpu-refresh\" @search=\"refresh()\" />\n                    <TableRefresh @search=\"refresh()\" />\n                </template>\n                <template #main>\n                    <el-descriptions direction=\"vertical\" :column=\"14\" border>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.driverVersion')\" width=\"50%\" :span=\"7\">\n                            {{ gpuInfo.driverVersion }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.cudaVersion')\" :span=\"7\">\n                            {{ gpuInfo.cudaVersion }}\n                        </el-descriptions-item>\n                    </el-descriptions>\n                    <el-collapse v-model=\"activeNames\" class=\"card-interval\">\n                        <el-collapse-item v-for=\"item in gpuInfo.gpu\" :key=\"item.index\" :name=\"item.index\">\n                            <template #title>\n                                <span class=\"name-class\">{{ item.index + '. ' + item.productName }}</span>\n                            </template>\n                            <span class=\"title-class\">{{ $t('aiTools.gpu.base') }}</span>\n                            <el-descriptions direction=\"vertical\" :column=\"6\" border size=\"small\" class=\"mt-2\">\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.gpuUtil')\">\n                                    {{ item.gpuUtil }}\n                                </el-descriptions-item>\n                                <el-descriptions-item>\n                                    <template #label>\n                                        <div class=\"cell-item\">\n                                            {{ $t('aiTools.gpu.temperature') }}\n                                            <el-tooltip placement=\"top\" :content=\"$t('aiTools.gpu.temperatureHelper')\">\n                                                <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                            </el-tooltip>\n                                        </div>\n                                    </template>\n                                    {{ item.temperature.replaceAll('C', '°C') }}\n                                </el-descriptions-item>\n                                <el-descriptions-item>\n                                    <template #label>\n                                        <div class=\"cell-item\">\n                                            {{ $t('aiTools.gpu.performanceState') }}\n                                            <el-tooltip\n                                                placement=\"top\"\n                                                :content=\"$t('aiTools.gpu.performanceStateHelper')\"\n                                            >\n                                                <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                            </el-tooltip>\n                                        </div>\n                                    </template>\n                                    {{ item.performanceState }}\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.powerUsage')\">\n                                    {{ item.powerDraw }} / {{ item.maxPowerLimit }}\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.memoryUsage')\">\n                                    {{ item.memUsed }} / {{ item.memTotal }}\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.fanSpeed')\">\n                                    {{ item.fanSpeed }}\n                                </el-descriptions-item>\n\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.busID')\">\n                                    {{ item.busID }}\n                                </el-descriptions-item>\n                                <el-descriptions-item>\n                                    <template #label>\n                                        <div class=\"cell-item\">\n                                            {{ $t('aiTools.gpu.persistenceMode') }}\n                                            <el-tooltip\n                                                placement=\"top\"\n                                                :content=\"$t('aiTools.gpu.persistenceModeHelper')\"\n                                            >\n                                                <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                            </el-tooltip>\n                                        </div>\n                                    </template>\n                                    {{ $t('aiTools.gpu.' + item.persistenceMode.toLowerCase()) }}\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.displayActive')\">\n                                    {{\n                                        lowerCase(item.displayActive) === 'disabled'\n                                            ? $t('aiTools.gpu.displayActiveF')\n                                            : $t('aiTools.gpu.displayActiveT')\n                                    }}\n                                </el-descriptions-item>\n                                <el-descriptions-item>\n                                    <template #label>\n                                        <div class=\"cell-item\">\n                                            Uncorr. ECC\n                                            <el-tooltip placement=\"top\" :content=\"$t('aiTools.gpu.ecc')\">\n                                                <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                            </el-tooltip>\n                                        </div>\n                                    </template>\n                                    {{ loadEcc(item.ecc) }}\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.computeMode')\">\n                                    <template #label>\n                                        <div class=\"cell-item\">\n                                            {{ $t('aiTools.gpu.computeMode') }}\n                                            <el-tooltip placement=\"top\">\n                                                <template #content>\n                                                    {{ $t('aiTools.gpu.defaultHelper') }}\n                                                    <br />\n                                                    {{ $t('aiTools.gpu.exclusiveProcessHelper') }}\n                                                    <br />\n                                                    {{ $t('aiTools.gpu.exclusiveThreadHelper') }}\n                                                    <br />\n                                                    {{ $t('aiTools.gpu.prohibitedHelper') }}\n                                                </template>\n                                                <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                            </el-tooltip>\n                                        </div>\n                                    </template>\n                                    {{ loadComputeMode(item.computeMode) }}\n                                </el-descriptions-item>\n                                <el-descriptions-item label=\"MIG.M\">\n                                    <template #label>\n                                        <div class=\"cell-item\">\n                                            MIG M.\n                                            <el-tooltip placement=\"top\">\n                                                <template #content>\n                                                    {{ $t('aiTools.gpu.migModeHelper') }}\n                                                </template>\n                                                <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                            </el-tooltip>\n                                        </div>\n                                    </template>\n                                    {{\n                                        item.migMode === 'N/A'\n                                            ? $t('aiTools.gpu.migModeNA')\n                                            : $t('aiTools.gpu.' + lowerCase(item.migMode))\n                                    }}\n                                </el-descriptions-item>\n                            </el-descriptions>\n                            <div class=\"card-interval\">\n                                <span class=\"title-class\">{{ $t('aiTools.gpu.process') }}</span>\n                            </div>\n                            <el-table :data=\"item.processes\" v-if=\"item.processes?.length !== 0\">\n                                <el-table-column label=\"PID\" prop=\"pid\" />\n                                <el-table-column :label=\"$t('aiTools.gpu.type')\" prop=\"type\">\n                                    <template #default=\"{ row }\">\n                                        {{ loadProcessType(row.type) }}\n                                    </template>\n                                </el-table-column>\n                                <el-table-column :label=\"$t('aiTools.gpu.processName')\" prop=\"processName\" />\n                                <el-table-column :label=\"$t('aiTools.gpu.processMemoryUsage')\" prop=\"usedMemory\" />\n                            </el-table>\n                        </el-collapse-item>\n                    </el-collapse>\n                </template>\n            </LayoutContent>\n        </div>\n        <div v-else>\n            <LayoutContent\n                v-loading=\"loading\"\n                :title=\"$t('aiTools.gpu.gpu')\"\n                :divider=\"true\"\n                v-if=\"xpuInfo.driverVersion.length !== 0 && !loading\"\n            >\n                <template #rightToolBar>\n                    <TableSetting title=\"gpu-refresh\" @search=\"refresh()\" />\n                    <TableRefresh @search=\"refresh()\" />\n                </template>\n                <template #main>\n                    <el-descriptions direction=\"vertical\" :column=\"14\" border>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.driverVersion')\" width=\"50%\" :span=\"7\">\n                            {{ xpuInfo.driverVersion }}\n                        </el-descriptions-item>\n                    </el-descriptions>\n                    <el-collapse v-model=\"activeNames\" class=\"card-interval\">\n                        <el-collapse-item\n                            v-for=\"item in xpuInfo.xpu\"\n                            :key=\"item.basic.deviceID\"\n                            :name=\"item.basic.deviceID\"\n                        >\n                            <template #title>\n                                <span class=\"name-class\">{{ item.basic.deviceID + '. ' + item.basic.deviceName }}</span>\n                            </template>\n                            <span class=\"title-class\">{{ $t('aiTools.gpu.base') }}</span>\n                            <el-descriptions direction=\"vertical\" :column=\"6\" border size=\"small\" class=\"mt-2\">\n                                <el-descriptions-item>\n                                    <template #label>\n                                        <div class=\"cell-item\">\n                                            {{ $t('aiTools.gpu.temperature') }}\n                                            <el-tooltip placement=\"top\" :content=\"$t('aiTools.gpu.temperatureHelper')\">\n                                                <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                            </el-tooltip>\n                                        </div>\n                                    </template>\n                                    {{ item.stats.temperature }}\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.powerUsage')\">\n                                    {{ item.stats.power }}\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.memoryUsage')\">\n                                    {{ item.stats.memoryUsed }} / {{ item.basic.memory }}\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('aiTools.gpu.busID')\">\n                                    {{ item.basic.pciBdfAddress }}\n                                </el-descriptions-item>\n                            </el-descriptions>\n                            <div class=\"card-interval\">\n                                <span class=\"title-class\">{{ $t('aiTools.gpu.process') }}</span>\n                            </div>\n                            <el-table :data=\"item.processes\" v-if=\"item.processes?.length !== 0\">\n                                <el-table-column label=\"PID\" prop=\"pid\" />\n                                <el-table-column :label=\"$t('aiTools.gpu.processName')\" prop=\"command\" />\n                                <el-table-column :label=\"$t('aiTools.gpu.shr')\" prop=\"shr\" />\n                                <el-table-column :label=\"$t('aiTools.gpu.processMemoryUsage')\" prop=\"memory\" />\n                            </el-table>\n                        </el-collapse-item>\n                    </el-collapse>\n                </template>\n            </LayoutContent>\n        </div>\n        <LayoutContent\n            :title=\"$t('aiTools.gpu.gpu')\"\n            :divider=\"true\"\n            v-if=\"gpuInfo.driverVersion.length === 0 && xpuInfo.driverVersion.length == 0 && !loading\"\n        >\n            <template #main>\n                <div class=\"app-warn\">\n                    <div class=\"flx-center\">\n                        <span>{{ $t('aiTools.gpu.gpuHelper') }}</span>\n                    </div>\n                    <div>\n                        <img src=\"@/assets/images/no_app.svg\" />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, ref } from 'vue';\nimport { loadGPUInfo } from '@/api/modules/ai';\nimport RouterMenu from '@/views/ai/gpu/index.vue';\nimport { AI } from '@/api/interface/ai';\nimport i18n from '@/lang';\n\nconst loading = ref();\nconst activeNames = ref(0);\nconst gpuInfo = ref<AI.Info>({\n    cudaVersion: '',\n    driverVersion: '',\n    type: 'nvidia',\n    gpu: [],\n});\nconst xpuInfo = ref<AI.XpuInfo>({\n    driverVersion: '',\n    type: 'xpu',\n    xpu: [],\n});\nconst gpuType = ref('nvidia');\n\nconst search = async () => {\n    loading.value = true;\n    await loadGPUInfo()\n        .then((res) => {\n            loading.value = false;\n            gpuType.value = res.data.type;\n            if (res.data.type == 'nvidia') {\n                gpuInfo.value = res.data;\n            } else {\n                xpuInfo.value = res.data;\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst refresh = async () => {\n    const res = await loadGPUInfo();\n    gpuInfo.value = res.data;\n};\n\nconst lowerCase = (val: string) => {\n    return val.toLowerCase();\n};\n\nconst loadComputeMode = (val: string) => {\n    switch (val) {\n        case 'Default':\n            return i18n.global.t('aiTools.gpu.default');\n        case 'Exclusive Process':\n            return i18n.global.t('aiTools.gpu.exclusiveProcess');\n        case 'Exclusive Thread':\n            return i18n.global.t('aiTools.gpu.exclusiveThread');\n        case 'Prohibited':\n            return i18n.global.t('aiTools.gpu.prohibited');\n    }\n};\n\nconst loadEcc = (val: string) => {\n    if (val === 'N/A') {\n        return i18n.global.t('aiTools.gpu.migModeNA');\n    }\n    if (val === 'Disabled') {\n        return i18n.global.t('aiTools.gpu.disabled');\n    }\n    if (val === 'Enabled') {\n        return i18n.global.t('aiTools.gpu.enabled');\n    }\n    return val || 0;\n};\n\nconst loadProcessType = (val: string) => {\n    if (val === 'C' || val === 'G') {\n        return i18n.global.t('aiTools.gpu.type' + val);\n    }\n    if (val === 'C+G') {\n        return i18n.global.t('aiTools.gpu.typeCG');\n    }\n    return val;\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.name-class {\n    font-size: 18px;\n    font-weight: 500;\n}\n.title-class {\n    font-size: 14px;\n    font-weight: 500;\n}\n.cell-item {\n    display: flex;\n    align-items: center;\n    .icon-item {\n        margin-left: 4px;\n        margin-top: -1px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/gpu/history/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <RouterMenu />\n\n        <div class=\"content-container__search\" v-if=\"options.length !== 0\">\n            <el-card>\n                <div>\n                    <el-date-picker\n                        @change=\"search()\"\n                        v-model=\"timeRangeGlobal\"\n                        type=\"datetimerange\"\n                        range-separator=\"-\"\n                        :start-placeholder=\"$t('commons.search.timeStart')\"\n                        :end-placeholder=\"$t('commons.search.timeEnd')\"\n                        :shortcuts=\"shortcuts\"\n                        style=\"max-width: 360px; width: 100%\"\n                        :size=\"mobile ? 'small' : 'default'\"\n                    ></el-date-picker>\n                    <el-select class=\"p-w-300 ml-2\" v-model=\"searchInfo.productName\" @change=\"search()\">\n                        <el-option v-for=\"item in options\" :key=\"item\" :label=\"item\" :value=\"item\" />\n                    </el-select>\n                    <TableRefresh class=\"float-right\" @search=\"search()\" />\n                    <el-button icon=\"Setting\" class=\"float-right mr-2\" @click=\"quickJump()\" />\n                </div>\n            </el-card>\n        </div>\n        <el-row :gutter=\"7\" v-if=\"options.length !== 0\">\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card class=\"card-interval\" style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <span class=\"title\">{{ $t('aiTools.gpu.memoryUsage') }}</span>\n                        </div>\n                    </template>\n                    <div class=\"chart\" v-if=\"!currentHide?.memory\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadMemoryChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadMemoryChart']\"\n                            v-if=\"chartsOption['loadMemoryChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                    <el-empty class=\"chart\" v-else :description=\"$t('aiTools.gpu.notSupport')\" />\n                </el-card>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card class=\"card-interval\" style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <span class=\"title\">{{ $t('aiTools.gpu.gpuUtil') }}</span>\n                        </div>\n                    </template>\n                    <div class=\"chart\" v-if=\"!currentHide?.gpu\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadGPUChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadGPUChart']\"\n                            v-if=\"chartsOption['loadGPUChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                    <el-empty class=\"chart\" v-else :description=\"$t('aiTools.gpu.notSupport')\" />\n                </el-card>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card class=\"card-interval\" style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <span class=\"title\">{{ $t('aiTools.gpu.process') }}</span>\n                        </div>\n                    </template>\n                    <div class=\"chart\" v-if=\"!currentHide?.process\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadProcessChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadProcessChart']\"\n                            v-if=\"chartsOption['loadProcessChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                    <el-empty class=\"chart\" v-else :description=\"$t('aiTools.gpu.notSupport')\" />\n                </el-card>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card class=\"card-interval\" style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <span class=\"title\">{{ $t('aiTools.gpu.powerUsage') }}</span>\n                        </div>\n                    </template>\n                    <div class=\"chart\" v-if=\"!currentHide?.power\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadPowerChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadPowerChart']\"\n                            v-if=\"chartsOption['loadPowerChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                    <el-empty class=\"chart\" v-else :description=\"$t('aiTools.gpu.notSupport')\" />\n                </el-card>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card class=\"card-interval\" style=\"overflow: inherit\">\n                    <template #header>\n                        <div>\n                            {{ $t('aiTools.gpu.temperature') }}\n                            <el-tooltip placement=\"top\" :content=\"$t('aiTools.gpu.temperatureHelper')\">\n                                <el-icon size=\"15\"><InfoFilled /></el-icon>\n                            </el-tooltip>\n                        </div>\n                    </template>\n                    <div class=\"chart\" v-if=\"!currentHide?.temperature\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadTemperatureChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadTemperatureChart']\"\n                            v-if=\"chartsOption['loadTemperatureChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                    <el-empty class=\"chart\" v-else :description=\"$t('aiTools.gpu.notSupport')\" />\n                </el-card>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card class=\"card-interval\" style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <span class=\"title\">{{ $t('aiTools.gpu.fanSpeed') }}</span>\n                        </div>\n                    </template>\n                    <div class=\"chart\" v-if=\"!currentHide?.speed\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadSpeedChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadSpeedChart']\"\n                            v-if=\"chartsOption['loadSpeedChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                    <el-empty class=\"chart\" v-else :description=\"$t('aiTools.gpu.notSupport')\" />\n                </el-card>\n            </el-col>\n        </el-row>\n\n        <LayoutContent :title=\"$t('aiTools.gpu.gpu')\" :divider=\"true\" v-else>\n            <template #main>\n                <div class=\"app-warn\">\n                    <div class=\"flx-center\">\n                        <span>{{ $t('aiTools.gpu.gpuHelper') }}</span>\n                    </div>\n                    <div>\n                        <img src=\"@/assets/images/no_app.svg\" />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, reactive, onMounted, computed } from 'vue';\nimport { loadGPUMonitor, getGPUOptions } from '@/api/modules/host';\nimport { dateFormatWithoutYear } from '@/utils/util';\nimport RouterMenu from '@/views/ai/gpu/index.vue';\nimport { GlobalStore } from '@/store';\nimport { shortcuts } from '@/utils/shortcuts';\nimport { Host } from '@/api/interface/host';\nimport i18n from '@/lang';\nimport { routerToName } from '@/utils/router';\n\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref(false);\nconst options = ref([]);\nconst gpuType = ref('gpu');\nconst timeRangeGlobal = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);\nconst chartsOption = ref({\n    loadPowerChart: null,\n    loadGPUChart: null,\n    loadMemoryChart: null,\n    loadTemperatureChart: null,\n    loadSpeedChart: null,\n});\nconst chartHide = ref([]);\nconst currentHide = ref();\n\nconst searchInfo = reactive<Host.MonitorGPUSearch>({\n    productName: '',\n    startTime: new Date(new Date().setHours(0, 0, 0, 0)),\n    endTime: new Date(),\n});\n\nconst loadOptions = async () => {\n    loading.value = true;\n    await getGPUOptions()\n        .then((res) => {\n            gpuType.value = res.data.gpuType || 'gpu';\n            options.value = res.data.options || [];\n            chartHide.value = res.data.chartHide || [];\n            searchInfo.productName = options.value.length > 0 ? options.value[0] : '';\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n            options.value = [];\n        });\n};\n\nconst loadCurrentHide = () => {\n    for (const item of chartHide.value) {\n        if (item.productName === searchInfo.productName) {\n            currentHide.value = item;\n            break;\n        }\n    }\n};\n\nconst search = async () => {\n    if (timeRangeGlobal.value && timeRangeGlobal.value.length === 2) {\n        searchInfo.startTime = timeRangeGlobal.value[0];\n        searchInfo.endTime = timeRangeGlobal.value[1];\n    }\n    loading.value = true;\n    loadCurrentHide();\n    await loadGPUMonitor(searchInfo)\n        .then((res) => {\n            loading.value = false;\n            let baseDate = res.data.date || [];\n            if (baseDate.length === 0) {\n                baseDate = loadEmptyDate(timeRangeGlobal.value);\n            }\n            let date = baseDate.map(function (item: any) {\n                return dateFormatWithoutYear(item);\n            });\n            if (!currentHide.value || !currentHide.value.gpu) {\n                initCPUCharts(date, res.data.gpuValue || []);\n            }\n            if (!currentHide.value || !currentHide.value.memory) {\n                initMemoryCharts(date, res.data);\n            }\n            if (!currentHide.value || !currentHide.value.power) {\n                initPowerCharts(date, res.data);\n            }\n            if (!currentHide.value || !currentHide.value.speed) {\n                initSpeedCharts(date, res.data.speedValue || []);\n            }\n            if (!currentHide.value || !currentHide.value.temperature) {\n                initTemperatureCharts(date, res.data.temperatureValue || []);\n            }\n            if (!currentHide.value || !currentHide.value.process) {\n                initProcessCharts(date, res.data);\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nfunction initCPUCharts(baseDate: any, items: any) {\n    let percents = items.map(function (item: any) {\n        return Number(item.toFixed(2));\n    });\n    let data = percents.length === 0 ? loadEmptyData() : percents;\n    chartsOption.value['loadGPUChart'] = {\n        xData: baseDate,\n        yData: [\n            {\n                name: i18n.global.t('aiTools.gpu.gpuUtil'),\n                data: data,\n            },\n        ],\n        formatStr: '%',\n    };\n}\nfunction initMemoryCharts(baseDate: any, data: any) {\n    chartsOption.value['loadMemoryChart'] = {\n        xData: baseDate,\n        yData: [\n            {\n                name: i18n.global.t('aiTools.gpu.memoryUsed'),\n                data: data.memoryUsed,\n            },\n            {\n                name: i18n.global.t('aiTools.gpu.memoryTotal'),\n                data: data.memoryTotal,\n            },\n            {\n                name: i18n.global.t('aiTools.gpu.percent'),\n                data: data.memoryPercent,\n                yAxisIndex: 1,\n            },\n        ],\n        yAxis: [\n            { type: 'value', name: i18n.global.t('aiTools.gpu.memory') },\n            {\n                type: 'value',\n                name: i18n.global.t('aiTools.gpu.percent') + ' ( % )',\n                position: 'right',\n                alignTicks: true,\n            },\n        ],\n        grid: mobile.value ? { left: '15%', right: '15%', bottom: '20%' } : null,\n        tooltip: {\n            trigger: 'axis',\n            formatter: function (list: any) {\n                return withMemoryProcess(list);\n            },\n        },\n        formatStr: '%',\n    };\n}\n\nfunction initPowerCharts(baseDate: any, data: any) {\n    chartsOption.value['loadPowerChart'] = {\n        xData: baseDate,\n        yData: [\n            {\n                name: i18n.global.t('aiTools.gpu.powerCurrent'),\n                data: data.powerUsed,\n            },\n            {\n                name: i18n.global.t('aiTools.gpu.powerLimit'),\n                data: data.powerTotal,\n            },\n            {\n                name: i18n.global.t('aiTools.gpu.percent'),\n                data: data.powerPercent,\n                yAxisIndex: 1,\n            },\n        ],\n        yAxis: [\n            { type: 'value', name: i18n.global.t('aiTools.gpu.power') },\n            {\n                type: 'value',\n                name: i18n.global.t('aiTools.gpu.percent') + ' ( % )',\n                position: 'right',\n                alignTicks: true,\n            },\n        ],\n        grid: mobile.value ? { left: '15%', right: '15%', bottom: '20%' } : null,\n        tooltip: {\n            trigger: 'axis',\n            formatter: function (list: any) {\n                let res = loadDate(list[0].name);\n                for (const item of list) {\n                    if (\n                        item.seriesName === i18n.global.t('aiTools.gpu.powerCurrent') ||\n                        item.seriesName === i18n.global.t('aiTools.gpu.powerLimit')\n                    ) {\n                        res += loadSeries(item, item.data, 'W');\n                    } else {\n                        res += loadSeries(item, Number(item.data.toFixed(2)), '%');\n                    }\n                }\n                return res;\n            },\n        },\n        formatStr: '%',\n    };\n}\n\nconst quickJump = () => {\n    routerToName('HostMonitorSetting');\n};\n\nfunction initTemperatureCharts(baseDate: any, items: any) {\n    let temperatures = items.map(function (item: any) {\n        return Number(item);\n    });\n    temperatures = temperatures.length === 0 ? loadEmptyData() : temperatures;\n    chartsOption.value['loadTemperatureChart'] = {\n        xData: baseDate,\n        yData: [\n            {\n                name: i18n.global.t('aiTools.gpu.temperature'),\n                data: temperatures,\n            },\n        ],\n        formatStr: '°C',\n    };\n}\nfunction initProcessCharts(baseDate: any, data: any) {\n    let process = data.processCount.map(function (item: any) {\n        return Number(item);\n    });\n    process = process.length === 0 ? loadEmptyData() : process;\n    chartsOption.value['loadProcessChart'] = {\n        xData: baseDate,\n        yData: [\n            {\n                name: i18n.global.t('aiTools.gpu.processCount'),\n                data: process,\n            },\n        ],\n        tooltip: {\n            trigger: 'axis',\n            formatter: function (list: any) {\n                const param = list[0];\n                const index = param.dataIndex;\n                let process = data.gpuProcesses?.length > index ? data.gpuProcesses[index] : [];\n                return withProcess(list, process);\n            },\n        },\n        formatStr: '-',\n    };\n}\nfunction initSpeedCharts(baseDate: any, items: any) {\n    let speeds = items.map(function (item: any) {\n        return Number(item);\n    });\n    speeds = speeds.length === 0 ? loadEmptyData() : speeds;\n    chartsOption.value['loadSpeedChart'] = {\n        xData: baseDate,\n        yData: [\n            {\n                name: i18n.global.t('aiTools.gpu.fanSpeed'),\n                data: speeds,\n            },\n        ],\n        formatStr: '%',\n    };\n}\n\nfunction loadEmptyDate(timeRange: any) {\n    if (timeRange.length != 2) {\n        return;\n    }\n    let date1 = new Date(timeRange[0]);\n    let date2 = new Date(timeRange[1]);\n    return [date1, date2];\n}\nfunction loadEmptyData() {\n    return [0, 0];\n}\n\nfunction withMemoryProcess(list: any) {\n    let res = loadDate(list[0].name);\n    for (const item of list) {\n        if (\n            item.seriesName === i18n.global.t('aiTools.gpu.memoryUsed') ||\n            item.seriesName === i18n.global.t('aiTools.gpu.memoryTotal')\n        ) {\n            res += loadSeries(item, item.data, 'MiB');\n        } else {\n            res += loadSeries(item, Number(item.data.toFixed(2)), '%');\n        }\n    }\n    return res;\n}\n\nfunction withProcess(list: any, process: any) {\n    let res = loadDate(list[0].name);\n    for (const item of list) {\n        res += loadSeries(item, item.data, '');\n    }\n    let title = gpuType.value === 'gpu' ? i18n.global.t('aiTools.gpu.type') : i18n.global.t('aiTools.gpu.shr');\n    res += `\n        <div style=\"margin-top: 10px; border-bottom: 1px dashed black;\"></div>\n        <table style=\"border-collapse: collapse; margin-top: 20px; font-size: 12px;\">\n        <thead>\n            <tr>\n            <th style=\"padding: 6px 8px;\">PID</th>\n            <th style=\"padding: 6px 8px;\">${i18n.global.t('aiTools.gpu.processName')}</th>\n            <th style=\"padding: 6px 8px;\">${title}</th>\n            <th style=\"padding: 6px 8px;\">${i18n.global.t('aiTools.gpu.memoryUsed')}</th>\n            </tr>\n        </thead>\n        <tbody>\n    `;\n    if (!process) {\n        return res;\n    }\n    for (const row of process) {\n        res += `\n            <tr>\n                <td style=\"padding: 6px 8px; text-align: center;\">\n                    ${row.pid}\n                </td>\n                <td style=\"padding: 6px 8px; text-align: center;\">\n                    ${row.processName}\n                </td>\n                <td style=\"padding: 6px 8px; text-align: center;\">\n                    ${loadProcessType(row.type)}\n                </td>\n                <td style=\"padding: 6px 8px; text-align: center;\">\n                    ${row.usedMemory.replaceAll('MB', 'MiB')}\n                </td>\n            </tr>\n        `;\n    }\n    return res;\n}\nfunction loadDate(name: any) {\n    return ` <div style=\"display: inline-block; width: 100%; padding-bottom: 10px;\">\n                ${i18n.global.t('commons.search.date')}: ${name.replaceAll('\\n', ' ')}\n            </div>`;\n}\nfunction loadSeries(item: any, data: any, unit: any) {\n    return `<div style=\"width: 100%;\">\n                ${item.marker} ${item.seriesName}: ${data} ${unit}\n            </div>`;\n}\nconst loadProcessType = (val: string) => {\n    if (val === 'C' || val === 'G') {\n        return i18n.global.t('aiTools.gpu.type' + val);\n    }\n    if (val === 'C+G') {\n        return i18n.global.t('aiTools.gpu.typeCG');\n    }\n    return val;\n};\n\nonMounted(() => {\n    loadOptions();\n});\n</script>\n\n<style scoped lang=\"scss\">\n.content-container__search {\n    margin-top: 7px;\n    .el-card {\n        --el-card-padding: 12px;\n    }\n}\n.title {\n    font-size: 16px;\n    font-weight: 500;\n}\n.chart {\n    width: 100%;\n    height: 400px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/gpu/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('aiTools.gpu.current'),\n        path: '/ai/gpu/current',\n    },\n    {\n        label: i18n.global.t('aiTools.gpu.history'),\n        path: '/ai/gpu/history',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/mcp/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nconst buttons = [\n    {\n        label: 'Servers',\n        path: '/ai/mcp/servers',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/mcp/server/bind/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('aiTools.mcp.bindDomain')\" @close=\"handleClose\" size=\"normal\">\n        <div v-loading=\"loading\">\n            <el-form ref=\"formRef\" label-position=\"top\" @submit.prevent :model=\"req\" :rules=\"rules\">\n                <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\">\n                    <template #default>\n                        <ul>\n                            <li>{{ $t('aiTools.proxy.proxyHelper1') }}</li>\n                            <li>{{ $t('aiTools.proxy.proxyHelper2') }}</li>\n                            <li>{{ $t('aiTools.proxy.proxyHelper3') }}</li>\n                        </ul>\n                    </template>\n                </el-alert>\n                <el-form-item :label=\"$t('website.domain')\" prop=\"domain\">\n                    <el-input v-model.trim=\"req.domain\" :disabled=\"operate === 'update'\" />\n                    <span class=\"input-help\">\n                        {{ $t('aiTools.proxy.proxyHelper4') }}\n                    </span>\n                    <span class=\"input-help\">\n                        {{ $t('aiTools.proxy.proxyHelper6') }}\n                        <el-link class=\"pageRoute\" icon=\"Position\" @click=\"toWebsite(req.websiteID)\" type=\"primary\">\n                            {{ $t('firewall.quickJump') }}\n                        </el-link>\n                    </span>\n                    <el-text type=\"danger\">{{ $t('aiTools.mcp.bindDomainHelper') }}</el-text>\n                </el-form-item>\n                <el-form-item :label=\"$t('xpack.waf.whiteList') + ' IP'\" prop=\"ipList\">\n                    <el-input\n                        :rows=\"3\"\n                        type=\"textarea\"\n                        clearable\n                        v-model=\"req.ipList\"\n                        :placeholder=\"$t('xpack.waf.ipGroupHelper')\"\n                    />\n                    <span class=\"input-help\">\n                        {{ $t('aiTools.proxy.whiteListHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item>\n                    <el-checkbox v-model=\"req.enableSSL\" @change=\"changeSSL\">\n                        {{ $t('website.enable') + ' ' + 'HTTPS' }}\n                    </el-checkbox>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.acmeAccountManage')\" prop=\"acmeAccountID\" v-if=\"req.enableSSL\">\n                    <el-select v-model=\"req.acmeAccountID\" :placeholder=\"$t('website.selectAcme')\" @change=\"listSSL\">\n                        <el-option :key=\"0\" :label=\"$t('website.imported')\" :value=\"0\"></el-option>\n                        <el-option\n                            v-for=\"(acme, index) in acmeAccounts\"\n                            :key=\"index\"\n                            :label=\"acme.email\"\n                            :value=\"acme.id\"\n                        >\n                            <span>\n                                {{ acme.email }}\n                                <el-tag class=\"ml-5\">{{ getAccountName(acme.type) }}</el-tag>\n                            </span>\n                        </el-option>\n                    </el-select>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.ssl')\" prop=\"sslID\" v-if=\"req.enableSSL\">\n                    <el-select\n                        v-model=\"req.sslID\"\n                        :placeholder=\"$t('website.selectSSL')\"\n                        @change=\"changeSSl(req.sslID)\"\n                    >\n                        <el-option v-for=\"(ssl, index) in ssls\" :key=\"index\" :label=\"ssl.primaryDomain\" :value=\"ssl.id\">\n                            <span>{{ ssl.primaryDomain }}</span>\n                            <el-tag class=\"tagClass\" v-if=\"ssl.expireDate\">\n                                {{ dateFormatSimple(ssl.expireDate) }}\n                            </el-tag>\n                            <el-tag class=\"tagClass\" v-if=\"ssl.organization\">\n                                {{ ssl.organization }}\n                            </el-tag>\n                        </el-option>\n                    </el-select>\n                </el-form-item>\n            </el-form>\n        </div>\n        <template #footer>\n            <el-button @click=\"handleClose\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                {{ $t('commons.button.add') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { listSSL, searchAcmeAccount } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport { getAccountName, dateFormatSimple } from '@/utils/util';\nimport { bindMcpDomain, getMcpDomain, updateMcpDomain } from '@/api/modules/ai';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\n\ntype SSLItem = Website.SSL & { organization?: string };\n\nconst open = ref(false);\nconst operate = ref('create');\nconst loading = ref(false);\nconst ssls = ref<SSLItem[]>([]);\nconst websiteSSL = ref<Website.SSL>();\nconst acmeAccounts = ref();\nconst formRef = ref();\nconst req = ref({\n    domain: '',\n    sslID: undefined,\n    ipList: '',\n    acmeAccountID: 0,\n    enableSSL: false,\n    allowIPs: [],\n    websiteID: 0,\n});\nconst rules = reactive<FormRules>({\n    domain: [Rules.domainWithPort],\n    sslID: [Rules.requiredSelectBusiness],\n});\nconst emit = defineEmits(['close']);\n\nconst handleClose = () => {\n    emit('close');\n    open.value = false;\n};\n\nconst acceptParams = () => {\n    search();\n    open.value = true;\n};\n\nconst changeSSl = (sslid: number) => {\n    const res = ssls.value.filter((element: Website.SSL) => {\n        return element.id == sslid;\n    });\n    websiteSSL.value = res[0];\n};\n\nconst changeSSL = () => {\n    if (!req.value.enableSSL) {\n        req.value.sslID = undefined;\n    } else {\n        listAcmeAccount();\n    }\n};\n\nconst listSSLs = () => {\n    const sslReq = {\n        acmeAccountID: String(req.value.acmeAccountID),\n    };\n    listSSL(sslReq).then((res) => {\n        ssls.value = res.data || [];\n        if (ssls.value.length > 0) {\n            let exist = false;\n            for (const ssl of ssls.value) {\n                if (ssl.id === req.value.sslID) {\n                    exist = true;\n                    break;\n                }\n            }\n            if (!exist) {\n                req.value.sslID = ssls.value[0].id;\n            }\n            changeSSl(req.value.sslID);\n        } else {\n            req.value.sslID = undefined;\n        }\n    });\n};\n\nconst listAcmeAccount = () => {\n    searchAcmeAccount({ page: 1, pageSize: 100 }).then((res) => {\n        acmeAccounts.value = res.data.items || [];\n        listSSLs();\n    });\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (operate.value === 'update') {\n            await updateMcpDomain(req.value);\n        } else {\n            await bindMcpDomain(req.value);\n        }\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        handleClose();\n    });\n};\n\nconst search = async () => {\n    try {\n        const res = await getMcpDomain();\n        if (res.data.websiteID > 0) {\n            operate.value = 'update';\n            req.value.domain = res.data.domain;\n            req.value.websiteID = res.data.websiteID;\n            if (res.data.allowIPs && res.data.allowIPs.length > 0) {\n                req.value.ipList = res.data.allowIPs.join('\\n');\n            }\n            if (res.data.sslID > 0) {\n                req.value.enableSSL = true;\n                req.value.sslID = res.data.sslID;\n                req.value.acmeAccountID = res.data.acmeAccountID;\n                listAcmeAccount();\n            }\n        }\n    } catch (e) {}\n};\n\nconst toWebsite = (websiteID: number) => {\n    if (websiteID != undefined && websiteID > 0) {\n        window.location.href = `/websites/${websiteID}/config/basic`;\n    } else {\n        window.location.href = '/websites';\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.pageRoute {\n    font-size: 12px;\n    margin-left: 5px;\n}\n.tagClass {\n    float: right;\n    margin-right: 10px;\n    font-size: 12px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/mcp/server/config/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('menu.config')\" @close=\"handleClose\" size=\"normal\">\n        <codemirror\n            :autofocus=\"true\"\n            :placeholder=\"$t('commons.msg.noneData')\"\n            :indent-with-tab=\"true\"\n            :tabSize=\"4\"\n            style=\"height: 300px\"\n            :lineWrapping=\"true\"\n            :matchBrackets=\"true\"\n            theme=\"cobalt\"\n            :styleActiveLine=\"true\"\n            :extensions=\"extensions\"\n            v-model=\"prettyJson\"\n            :disabled=\"true\"\n        />\n        <CopyButton :content=\"prettyJson\" class=\"mt-2\" />\n        <template #footer>\n            <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { AI } from '@/api/interface/ai';\nimport { ref } from 'vue';\nimport { Codemirror } from 'vue-codemirror';\nimport { javascript } from '@codemirror/lang-javascript';\nimport { oneDark } from '@codemirror/theme-one-dark';\n\nconst extensions = [javascript(), oneDark];\n\nconst open = ref(false);\nconst jsonObj = ref({\n    mcpServers: {},\n});\nconst prettyJson = ref('');\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = (mcpServer: AI.McpServer) => {\n    jsonObj.value.mcpServers = {};\n    jsonObj.value.mcpServers[mcpServer.name] = {\n        url: mcpServer.baseUrl + mcpServer.ssePath,\n    };\n    prettyJson.value = JSON.stringify(jsonObj.value, null, 2);\n    open.value = true;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/mcp/server/import/index.vue",
    "content": "<template>\n    <el-dialog v-model=\"submitVisible\" :destroy-on-close=\"true\" :close-on-click-modal=\"false\" width=\"40%\">\n        <template #header>\n            {{ $t('aiTools.mcp.importMcpJson') }}\n        </template>\n        <div>\n            <el-input\n                v-model=\"mcpServerJson\"\n                type=\"textarea\"\n                :rows=\"15\"\n                placeholder='{\n  \"mcpServers\": {\n    \"postgres\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"@modelcontextprotocol/server-postgres\",\n        \"postgresql://localhost/mydb\"\n      ]\n    }\n  }\n}'\n            ></el-input>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"onCancel\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </el-dialog>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { ref } from 'vue';\n\nconst submitVisible = ref(false);\nconst mcpServerJson = ref();\nconst mcpServerConfig = ref();\n\nconst acceptParams = (): void => {\n    mcpServerJson.value = '';\n    submitVisible.value = true;\n};\nconst emit = defineEmits(['confirm', 'cancel']);\n\nconst onConfirm = async () => {\n    try {\n        const data = JSON.parse(mcpServerJson.value);\n        if (!data.mcpServers || typeof data.mcpServers !== 'object') {\n            throw new Error(i18n.global.t('mcp.importMcpJsonError'));\n        }\n        mcpServerConfig.value = Object.entries(data.mcpServers).map(([name, config]: any) => ({\n            name,\n            command: [config.command, ...config.args].join(' '),\n            environments: config.env ? Object.entries(config.env).map(([key, value]) => ({ key, value })) : [],\n            ssePath: '/' + name,\n            containerName: name,\n        }));\n    } catch (error) {\n        return;\n    }\n    emit('confirm', mcpServerConfig.value);\n    submitVisible.value = false;\n};\n\nconst onCancel = async () => {\n    emit('cancel');\n    submitVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/mcp/server/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <LayoutContent :title=\"'Servers'\" v-loading=\"loading\">\n            <template #leftToolBar>\n                <div class=\"flex flex-wrap gap-3\">\n                    <el-button type=\"primary\" @click=\"openCreate\">\n                        {{ $t('commons.button.create') }}\n                    </el-button>\n                    <el-button type=\"primary\" plain @click=\"openDomain\">\n                        {{ $t('aiTools.mcp.bindDomain') }}\n                    </el-button>\n                </div>\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"items\" @search=\"search()\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"name\"\n                        width=\"200px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"openDetail(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('aiTools.mcp.externalUrl')\" prop=\"baseUrl\" min-width=\"200px\">\n                        <template #default=\"{ row }\">\n                            {{ getUrl(row) }}\n                            <CopyButton :content=\"getUrl(row)\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\" width=\"120px\">\n                        <template #default=\"{ row }\">\n                            <el-popover\n                                v-if=\"row.status === 'error'\"\n                                placement=\"bottom\"\n                                :width=\"400\"\n                                trigger=\"hover\"\n                                :content=\"row.message\"\n                                popper-class=\"max-h-[300px] overflow-auto\"\n                            >\n                                <template #reference>\n                                    <Status :key=\"row.status\" :status=\"row.status\"></Status>\n                                </template>\n                            </el-popover>\n                            <div v-else>\n                                <Status :key=\"row.status\" :status=\"row.status\"></Status>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" prop=\"path\" width=\"120px\">\n                        <template #default=\"{ row }\">\n                            <el-button\n                                @click=\"openLog(row)\"\n                                link\n                                type=\"primary\"\n                                :disabled=\"\n                                    row.status !== 'Running' && row.status !== 'Rrror' && row.status !== 'Restarting'\n                                \"\n                            >\n                                {{ $t('website.check') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        width=\"180\"\n                        fix\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 2\"\n                        :min-width=\"mobile ? 'auto' : 200\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        fixed=\"right\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <McpServerOperate ref=\"createRef\" @close=\"searchWithTimeOut\" />\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <ComposeLogs ref=\"composeLogRef\" />\n        <BindDomain ref=\"bindDomainRef\" @close=\"searchWithTimeOut\" />\n        <Config ref=\"configRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { AI } from '@/api/interface/ai';\nimport { deleteMcpServer, operateMcpServer, pageMcpServer } from '@/api/modules/ai';\nimport RouterMenu from '@/views/ai/mcp/index.vue';\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport { dateFormat } from '@/utils/util';\nimport McpServerOperate from './operate/index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport { GlobalStore } from '@/store';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport BindDomain from './bind/index.vue';\nimport Config from './config/index.vue';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\nconst createRef = ref();\nconst opRef = ref();\nconst composeLogRef = ref();\nconst bindDomainRef = ref();\nconst configRef = ref();\nconst items = ref<AI.McpServer[]>([]);\nconst paginationConfig = reactive({\n    cacheSizeKey: 'mcp-server-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('mcp-server-page-size')) || 20,\n    total: 0,\n});\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst getUrl = (row: AI.McpServer) => {\n    if (row.outputTransport == 'sse') {\n        return row.baseUrl + row.ssePath;\n    } else {\n        return row.baseUrl + row.streamableHttpPath;\n    }\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.config'),\n        click: (row: AI.McpServer) => {\n            openConfig(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: AI.McpServer) => {\n            openDetail(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.start'),\n        click: (row: AI.McpServer) => {\n            opServer(row, 'start');\n        },\n        disabled: (row: AI.McpServer) => {\n            return row.status === 'Running';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.stop'),\n        click: (row: AI.McpServer) => {\n            opServer(row, 'stop');\n        },\n        disabled: (row: AI.McpServer) => {\n            return row.status === 'Stopped';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: (row: AI.McpServer) => {\n            opServer(row, 'restart');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: AI.McpServer) => {\n            deleteServer(row);\n        },\n    },\n];\n\nconst searchWithTimeOut = () => {\n    search();\n    setTimeout(() => {\n        search();\n    }, 1000);\n};\n\nconst search = () => {\n    loading.value = true;\n    pageMcpServer({\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        name: '',\n    }).then((res) => {\n        items.value = res.data.items;\n        paginationConfig.total = res.data.total;\n        loading.value = false;\n    });\n};\n\nconst openDetail = (row: AI.McpServer) => {\n    createRef.value.acceptParams(row);\n};\n\nconst openCreate = () => {\n    let maxPort = 7999;\n    if (items.value && items.value.length > 0) {\n        maxPort = Math.max(...items.value.map((item) => item.port));\n    }\n    createRef.value.acceptParams({ port: maxPort + 1 });\n};\n\nconst openLog = (row: AI.McpServer) => {\n    composeLogRef.value.acceptParams({\n        compose: row.dir + '/docker-compose.yml',\n        resource: row.name,\n        container: row.containerName,\n    });\n};\n\nconst deleteServer = async (row: AI.McpServer) => {\n    try {\n        opRef.value.acceptParams({\n            title: i18n.global.t('commons.button.delete'),\n            names: [row.name],\n            msg: i18n.global.t('commons.msg.operatorHelper', [\n                i18n.global.t('aiTools.mcp.server'),\n                i18n.global.t('commons.button.delete'),\n            ]),\n            api: deleteMcpServer,\n            params: { id: row.id },\n        });\n    } catch (error) {}\n};\n\nconst opServer = async (row: AI.McpServer, operate: string) => {\n    ElMessageBox.confirm(\n        i18n.global.t('aiTools.mcp.operatorHelper', [\n            i18n.global.t('aiTools.mcp.server'),\n            i18n.global.t('commons.button.' + operate),\n        ]),\n        i18n.global.t('commons.button.' + operate),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(async () => {\n        try {\n            await operateMcpServer({ id: row.id, operate: operate });\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search();\n        } catch (error) {}\n    });\n};\n\nconst openDomain = () => {\n    bindDomainRef.value.acceptParams();\n};\n\nconst openConfig = (row: AI.McpServer) => {\n    configRef.value.acceptParams(row);\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/mcp/server/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.' + mode)\"\n        :resource=\"mcpServer.name\"\n        @close=\"handleClose\"\n        size=\"large\"\n    >\n        <el-form\n            ref=\"mcpServerForm\"\n            label-position=\"top\"\n            :model=\"mcpServer\"\n            label-width=\"125px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item>\n                <el-button @click=\"importRef.acceptParams()\" type=\"primary\" plain>\n                    {{ $t('aiTools.mcp.importMcpJson') }}\n                </el-button>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model=\"mcpServer.name\" :disabled=\"mode == 'edit'\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.type')\" prop=\"type\">\n                <el-select v-model=\"mcpServer.type\">\n                    <el-option label=\"npx\" value=\"npx\" />\n                    <el-option label=\"uvx\" value=\"uvx\" />\n                </el-select>\n                <span class=\"input-help\">\n                    {{ $t('aiTools.mcp.' + mcpServer.type + 'Helper') }}\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('runtime.runScript')\" prop=\"command\">\n                <el-input\n                    v-model=\"mcpServer.command\"\n                    type=\"textarea\"\n                    :rows=\"3\"\n                    :placeholder=\"\n                        $t('ssl.commonNameHelper') +\n                        (mcpServer.type == 'npx'\n                            ? ' npx -y @modelcontextprotocol/server-github'\n                            : ' uvx mcp-server-fetch')\n                    \"\n                ></el-input>\n            </el-form-item>\n            <div>\n                <el-text>{{ $t('aiTools.mcp.environment') }}</el-text>\n                <div class=\"mt-1\">\n                    <el-row :gutter=\"20\" v-for=\"(env, index) in mcpServer.environments\" :key=\"index\">\n                        <el-col :span=\"8\">\n                            <el-form-item :prop=\"`environments.${index}.key`\" :rules=\"rules.key\">\n                                <el-input v-model=\"env.key\" :placeholder=\"$t('mcp.envKey')\" />\n                            </el-form-item>\n                        </el-col>\n                        <el-col :span=\"8\">\n                            <el-form-item :prop=\"`environments.${index}.value`\" :rules=\"rules.value\">\n                                <el-input v-model=\"env.value\" :placeholder=\"$t('mcp.envValue')\" />\n                            </el-form-item>\n                        </el-col>\n                        <el-col :span=\"4\">\n                            <el-form-item>\n                                <el-button type=\"primary\" @click=\"removeEnv(index)\" link class=\"mt-1\">\n                                    {{ $t('commons.button.delete') }}\n                                </el-button>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                    <el-row :gutter=\"20\">\n                        <el-col :span=\"4\">\n                            <el-button class=\"mb-2\" @click=\"addEnv\">{{ $t('commons.button.add') }}</el-button>\n                        </el-col>\n                    </el-row>\n                </div>\n            </div>\n            <Volumes :volumes=\"mcpServer.volumes\" class=\"mb-2\" />\n            <el-row :gutter=\"20\">\n                <el-col :span=\"8\">\n                    <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\">\n                        <el-input v-model.number=\"mcpServer.port\" />\n                    </el-form-item>\n                </el-col>\n                <el-col :span=\"6\">\n                    <el-form-item :label=\"$t('app.allowPort')\" prop=\"hostIP\">\n                        <el-switch v-model=\"mcpServer.hostIP\" :active-value=\"'0.0.0.0'\" :inactive-value=\"'127.0.0.1'\" />\n                    </el-form-item>\n                </el-col>\n            </el-row>\n            <el-form-item :label=\"$t('aiTools.mcp.baseUrl')\" prop=\"url\">\n                <el-input v-model.trim=\"mcpServer.url\">\n                    <template #prepend>\n                        <el-select v-model=\"mcpServer.protocol\" class=\"pre-select\">\n                            <el-option label=\"http\" value=\"http://\" />\n                            <el-option label=\"https\" value=\"https://\" />\n                        </el-select>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">\n                    {{ $t('aiTools.mcp.baseUrlHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('app.containerName')\" prop=\"containerName\">\n                <el-input v-model.trim=\"mcpServer.containerName\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('aiTools.mcp.outputTransport')\" prop=\"outputTransport\">\n                <el-select v-model=\"mcpServer.outputTransport\">\n                    <el-option label=\"sse\" value=\"sse\" />\n                    <el-option label=\"streamableHttp\" value=\"streamableHttp\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('aiTools.mcp.ssePath')\" prop=\"ssePath\" v-if=\"mcpServer.outputTransport === 'sse'\">\n                <el-input v-model.trim=\"mcpServer.ssePath\"></el-input>\n                <span class=\"input-help\">\n                    {{ $t('aiTools.mcp.ssePathHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item\n                :label=\"$t('aiTools.mcp.streamableHttpPath')\"\n                prop=\"streamableHttpPath\"\n                v-if=\"mcpServer.outputTransport === 'streamableHttp'\"\n            >\n                <el-input v-model.trim=\"mcpServer.streamableHttpPath\"></el-input>\n                <span class=\"input-help\">\n                    {{ $t('aiTools.mcp.streamableHttpPathHelper') }}\n                </span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span>\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(mcpServerForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <Import ref=\"importRef\" @confirm=\"getImport\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { AI } from '@/api/interface/ai';\nimport { createMcpServer, getMcpDomain, updateMcpServer } from '@/api/modules/ai';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { ref, watch } from 'vue';\nimport Volumes from '../volume/index.vue';\nimport Import from '../import/index.vue';\n\nconst open = ref(false);\nconst mode = ref('create');\nconst loading = ref(false);\nconst mcpServerForm = ref();\nconst importRef = ref();\nconst newMcpServer = () => {\n    return {\n        id: 0,\n        name: '',\n        port: 8000,\n        status: '',\n        message: '',\n        baseUrl: '',\n        ssePath: '',\n        command: '',\n        containerName: '',\n        environments: [],\n        volumes: [],\n        hostIP: '127.0.0.1',\n        protocol: 'http://',\n        url: '',\n        outputTransport: 'sse',\n        streamableHttpPath: '',\n        type: 'npx',\n    };\n};\nconst em = defineEmits(['close']);\nconst mcpServer = ref(newMcpServer());\nconst rules = ref({\n    name: [Rules.requiredInput, Rules.appName],\n    command: [Rules.requiredInput],\n    port: [Rules.requiredInput, Rules.port],\n    containerName: [Rules.requiredInput],\n    url: [Rules.requiredInput],\n    ssePath: [Rules.requiredInput],\n    key: [Rules.requiredInput],\n    value: [Rules.requiredInput],\n    outputTransport: [Rules.requiredSelect],\n    streamableHttpPath: [Rules.requiredInput],\n    type: [Rules.requiredSelect],\n});\nconst hasWebsite = ref(false);\n\nconst acceptParams = async (params: AI.McpServer) => {\n    hasWebsite.value = false;\n    mode.value = params.id ? 'edit' : 'create';\n    let mcpDomainRes;\n    try {\n        mcpDomainRes = await getMcpDomain();\n        if (mcpDomainRes.data.connUrl != '') {\n            hasWebsite.value = true;\n        }\n    } catch (error) {}\n\n    if (mode.value == 'edit') {\n        mcpServer.value = params;\n        if (!mcpServer.value.environments) {\n            mcpServer.value.environments = [];\n        }\n        if (!mcpServer.value.volumes) {\n            mcpServer.value.volumes = [];\n        }\n        const parts = mcpServer.value.baseUrl.split(/(https?:\\/\\/)/).filter(Boolean);\n        mcpServer.value.protocol = parts[0];\n        mcpServer.value.url = parts[1];\n        mcpServer.value.outputTransport = mcpServer.value.outputTransport || 'sse';\n        mcpServer.value.type = mcpServer.value.type || 'npx';\n    } else {\n        mcpServer.value = newMcpServer();\n        if (params.port) {\n            mcpServer.value.port = params.port;\n        }\n        if (mcpDomainRes.data && mcpDomainRes.data.connUrl != '') {\n            const parts = mcpDomainRes.data.connUrl.split(/(https?:\\/\\/)/).filter(Boolean);\n            mcpServer.value.protocol = parts[0];\n            mcpServer.value.url = parts[1];\n            mcpServer.value.baseUrl = mcpDomainRes.data.connUrl;\n        }\n    }\n    open.value = true;\n};\n\nwatch(\n    () => mcpServer.value.name,\n    (newVal) => {\n        if (newVal && mode.value == 'create') {\n            mcpServer.value.containerName = newVal;\n            mcpServer.value.ssePath = '/' + newVal;\n        }\n    },\n    { deep: true },\n);\n\nconst addEnv = () => {\n    mcpServer.value.environments.push({\n        key: '',\n        value: '',\n    });\n};\n\nconst removeEnv = (index: number) => {\n    mcpServer.value.environments.splice(index, 1);\n};\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst getImport = async (data: AI.ImportMcpServer[]) => {\n    if (!data) {\n        return;\n    }\n    const importServer = data[0];\n    mcpServer.value.name = importServer.name;\n    mcpServer.value.containerName = importServer.containerName;\n    mcpServer.value.ssePath = importServer.ssePath;\n    mcpServer.value.command = importServer.command;\n    mcpServer.value.environments = importServer.environments || [];\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) {\n            return;\n        }\n        let request = true;\n        if (mcpServer.value.hostIP != '0.0.0.0' && !hasWebsite.value) {\n            await ElMessageBox.confirm(i18n.global.t('app.installWarn'), i18n.global.t('app.checkTitle'), {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n            }).catch(() => {\n                request = false;\n            });\n        }\n        if (!request) {\n            return;\n        }\n        try {\n            loading.value = true;\n            mcpServer.value.baseUrl = mcpServer.value.protocol + mcpServer.value.url;\n            if (mode.value == 'create') {\n                await createMcpServer(mcpServer.value);\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n            } else {\n                await updateMcpServer(mcpServer.value);\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            }\n            handleClose();\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/mcp/server/volume/index.vue",
    "content": "<template>\n    <div class=\"mt-2\">\n        <el-text>{{ $t('container.mount') }}</el-text>\n        <div class=\"mt-2\">\n            <el-row :gutter=\"20\" v-for=\"(volume, index) in volumes\" :key=\"index\">\n                <el-col :span=\"8\">\n                    <el-form-item :prop=\"`volumes.${index}.source`\" :rules=\"rules.value\">\n                        <el-input v-model=\"volume.source\" :placeholder=\"$t('container.hostOption')\" />\n                    </el-form-item>\n                </el-col>\n                <el-col :span=\"8\">\n                    <el-form-item :prop=\"`volumes.${index}.target`\" :rules=\"rules.value\">\n                        <el-input v-model=\"volume.target\" :placeholder=\"$t('container.containerDir')\" />\n                    </el-form-item>\n                </el-col>\n                <el-col :span=\"4\">\n                    <el-form-item>\n                        <el-button type=\"primary\" @click=\"removeEnv(index)\" link class=\"mt-1\">\n                            {{ $t('commons.button.delete') }}\n                        </el-button>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n            <el-row :gutter=\"20\">\n                <el-col :span=\"4\">\n                    <el-button @click=\"addEnv\">{{ $t('commons.button.add') }}</el-button>\n                </el-col>\n            </el-row>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { defineProps, reactive } from 'vue';\nimport { FormRules } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { AI } from '@/api/interface/ai';\n\nconst props = defineProps({\n    volumes: {\n        type: Array<AI.Volume>,\n        required: true,\n    },\n});\n\nconst rules = reactive<FormRules>({\n    value: [Rules.requiredInput],\n});\n\nconst addEnv = () => {\n    props.volumes.push({\n        source: '',\n        target: '',\n    });\n};\n\nconst removeEnv = (index: number) => {\n    props.volumes.splice(index, 1);\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/model/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nconst buttons = [\n    {\n        label: 'Ollama',\n        path: '/ai/model/ollama',\n    },\n    {\n        label: 'vLLM',\n        path: '/ai/model/vllm',\n    },\n    {\n        label: 'TensorRT LLM',\n        path: '/ai/model/tensorrt',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/model/ollama/add/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('commons.button.add')\" @close=\"handleClose\">\n        <el-alert type=\"info\" :closable=\"false\">\n            <template #title>\n                <span class=\"flx-align-center\">\n                    {{ $t('aiTools.model.ollama_doc') }}\n                    <el-button link class=\"ml-5\" icon=\"Position\" @click=\"goSearch()\" type=\"primary\">\n                        {{ $t('firewall.quickJump') }}\n                    </el-button>\n                </span>\n            </template>\n        </el-alert>\n        <el-form ref=\"formRef\" label-position=\"top\" class=\"mt-5\" :model=\"form\">\n            <el-form-item :label=\"$t('commons.table.name')\" :rules=\"Rules.requiredInput\" prop=\"name\">\n                <el-input v-model.trim=\"form.name\" />\n                <span class=\"input-help\" v-if=\"form.name\">\n                    {{\n                        $t('aiTools.model.create_helper', [\n                            form.name.replaceAll('ollama run ', '').replaceAll('ollama pull ', ''),\n                        ])\n                    }}\n                </span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.add') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { createOllamaModel } from '@/api/modules/ai';\nimport { newUUID } from '@/utils/util';\n\nconst drawerVisible = ref(false);\nconst form = reactive({\n    name: '',\n});\n\nconst acceptParams = async (): Promise<void> => {\n    form.name = '';\n    drawerVisible.value = true;\n};\nconst emit = defineEmits(['search', 'log']);\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    let taskID = newUUID();\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let itemName = form.name.replaceAll('ollama run ', '').replaceAll('ollama pull ', '');\n        await createOllamaModel(itemName, taskID).then(() => {\n            drawerVisible.value = false;\n            emit('search');\n            emit('log', { logFileExist: true, name: itemName, from: 'local', taskID: taskID });\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        });\n    });\n};\n\nconst goSearch = () => {\n    window.open('https://ollama.com/search', '_blank', 'noopener,noreferrer');\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/model/ollama/conn/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('database.databaseConnInfo')\" @close=\"handleClose\" size=\"small\">\n        <el-form @submit.prevent v-loading=\"loading\" :model=\"form\" label-position=\"top\">\n            <el-row type=\"flex\" justify=\"center\">\n                <el-col :span=\"22\">\n                    <el-form-item :label=\"$t('database.containerConn')\">\n                        <el-card class=\"mini-border-card\">\n                            <el-descriptions :column=\"1\">\n                                <el-descriptions-item :label=\"$t('database.connAddress')\">\n                                    {{ form.containerName }}\n                                    <CopyButton :content=\"form.containerName\" />\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('commons.table.port')\">\n                                    11434\n                                    <CopyButton content=\"11434\" />\n                                </el-descriptions-item>\n                            </el-descriptions>\n                        </el-card>\n                        <span class=\"input-help\">\n                            {{ $t('aiTools.model.container_conn_helper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('setting.proxyUrl')\" v-if=\"bindDomain.connUrl != ''\">\n                        <el-card class=\"mini-border-card\">\n                            <el-descriptions :column=\"1\">\n                                <el-descriptions-item :label=\"$t('database.connAddress')\">\n                                    {{ bindDomain.connUrl }}\n                                    <CopyButton :content=\"bindDomain.connUrl\" />\n                                </el-descriptions-item>\n                            </el-descriptions>\n                        </el-card>\n                        <span class=\"input-help\">\n                            {{ $t('database.remoteConnHelper2') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('database.remoteConn')\" v-else>\n                        <el-card class=\"mini-border-card\">\n                            <el-descriptions :column=\"1\">\n                                <el-descriptions-item :label=\"$t('database.connAddress')\">\n                                    {{ form.systemIP }}\n                                    <CopyButton :content=\"form.systemIP\" />\n                                </el-descriptions-item>\n                                <el-descriptions-item :label=\"$t('commons.table.port')\">\n                                    {{ form.port }}\n                                    <CopyButton :content=\"form.port + ''\" />\n                                </el-descriptions-item>\n                            </el-descriptions>\n                        </el-card>\n                        <span class=\"input-help\">\n                            {{ $t('database.remoteConnHelper2') }}\n                        </span>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { getSettingInfo } from '@/api/modules/setting';\nimport { getBindDomain } from '@/api/modules/ai';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\n\nconst drawerVisible = ref(false);\nconst form = reactive({\n    systemIP: '',\n    containerName: '',\n    port: 0,\n\n    remoteIP: '',\n});\nconst bindDomain = ref({\n    connUrl: '',\n});\n\ninterface DialogProps {\n    port: number;\n    containerName: string;\n    appinstallID: number;\n}\n\nconst acceptParams = (param: DialogProps): void => {\n    form.containerName = param.containerName;\n    form.port = param.port;\n    loadSystemIP();\n    loadBindDomain(param.appinstallID);\n    drawerVisible.value = true;\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst loadSystemIP = async () => {\n    if (globalStore.currentNode !== 'local') {\n        form.systemIP = globalStore.currentNode || i18n.global.t('database.localIP');\n        return;\n    }\n    const res = await getSettingInfo();\n    form.systemIP = res.data.systemIP || i18n.global.t('database.localIP');\n};\n\nconst loadBindDomain = async (appInstallID: number) => {\n    if (appInstallID == undefined || appInstallID <= 0) {\n        return;\n    }\n    try {\n        const res = await getBindDomain({\n            appInstallID: appInstallID,\n        });\n        if (res.data.websiteID > 0) {\n            bindDomain.value.connUrl = res.data.connUrl;\n        }\n    } catch (e) {}\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.copy_button {\n    border-radius: 0px;\n    border-left-width: 0px;\n}\n:deep(.el-input__wrapper) {\n    border-top-right-radius: 0px;\n    border-bottom-right-radius: 0px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/model/ollama/del/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.sync')\" size=\"small\" @close=\"handleClose\">\n        <div v-loading=\"loading\">\n            <el-row type=\"flex\" justify=\"center\">\n                <el-col :span=\"22\">\n                    <el-alert class=\"mt-2\" :show-icon=\"true\" type=\"warning\" :closable=\"false\">\n                        {{ $t('aiTools.model.ollama_sync') }}\n                    </el-alert>\n                    <el-checkbox\n                        class=\"mt-2\"\n                        v-model=\"checkAll\"\n                        :indeterminate=\"isIndeterminate\"\n                        @change=\"handleCheckAllChange\"\n                    >\n                        {{ $t('commons.table.all') }}\n                    </el-checkbox>\n                    <el-checkbox-group v-model=\"checkedItems\" @change=\"handleCheckedChange\">\n                        <el-checkbox v-for=\"(item, index) in list\" :key=\"index\" :label=\"item.name\" :value=\"item.id\" />\n                    </el-checkbox-group>\n                </el-col>\n            </el-row>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose()\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\" :disabled=\"loading || checkedItems.length === 0\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { AI } from '@/api/interface/ai';\nimport { deleteOllamaModel } from '@/api/modules/ai';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { CheckboxValueType } from 'element-plus';\nimport { ref } from 'vue';\n\ndefineOptions({ name: 'OpDialog' });\n\nconst checkAll = ref();\nconst isIndeterminate = ref(false);\nconst checkedItems = ref([]);\nconst list = ref([]);\n\nconst loading = ref();\nconst open = ref();\n\ninterface DialogProps {\n    list: Array<AI.OllamaModelDropInfo>;\n}\nconst acceptParams = (props: DialogProps): void => {\n    checkAll.value = false;\n    checkedItems.value = [];\n    list.value = props.list;\n    open.value = true;\n};\n\nconst emit = defineEmits(['search']);\n\nconst handleCheckAllChange = (val: CheckboxValueType) => {\n    checkedItems.value = [];\n    if (val) {\n        for (const item of list.value) {\n            checkedItems.value.push(item.id);\n        }\n    }\n    isIndeterminate.value = false;\n};\nconst handleCheckedChange = (value: CheckboxValueType[]) => {\n    const checkedCount = value.length;\n    checkAll.value = checkedCount === list.value.length;\n    isIndeterminate.value = checkedCount > 0 && checkedCount < list.value.length;\n};\n\nconst onConfirm = async () => {\n    loading.value = true;\n    await deleteOllamaModel(checkedItems.value, true)\n        .then(() => {\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            open.value = false;\n            loading.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    emit('search');\n    open.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/model/ollama/domain/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('aiTools.proxy.proxy')\" @close=\"handleClose\" size=\"large\">\n        <div v-loading=\"loading\">\n            <el-form ref=\"formRef\" label-position=\"top\" @submit.prevent :model=\"req\" :rules=\"rules\">\n                <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\">\n                    <template #default>\n                        <ul>\n                            <li>{{ $t('aiTools.proxy.proxyHelper1') }}</li>\n                            <li>{{ $t('aiTools.proxy.proxyHelper2') }}</li>\n                            <li>{{ $t('aiTools.proxy.proxyHelper3') }}</li>\n                        </ul>\n                    </template>\n                </el-alert>\n                <el-form-item :label=\"$t('website.domain')\" prop=\"domain\">\n                    <el-input v-model.trim=\"req.domain\" :disabled=\"operate === 'update'\" />\n                    <span class=\"input-help\">\n                        {{ $t('aiTools.proxy.proxyHelper4') }}\n                    </span>\n                    <span class=\"input-help\">\n                        {{ $t('aiTools.proxy.proxyHelper6') }}\n                        <el-link class=\"pageRoute\" icon=\"Position\" @click=\"toWebsite(req.websiteID)\" type=\"primary\">\n                            {{ $t('firewall.quickJump') }}\n                        </el-link>\n                    </span>\n                </el-form-item>\n                <el-form-item :label=\"$t('xpack.waf.whiteList') + ' IP'\" prop=\"ipList\">\n                    <el-input\n                        :rows=\"3\"\n                        type=\"textarea\"\n                        clearable\n                        v-model=\"req.ipList\"\n                        :placeholder=\"$t('xpack.waf.ipGroupHelper')\"\n                    />\n                    <span class=\"input-help\">\n                        {{ $t('aiTools.proxy.whiteListHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item>\n                    <el-checkbox v-model=\"req.enableSSL\" @change=\"changeSSL\">\n                        {{ $t('website.enable') + ' ' + 'HTTPS' }}\n                    </el-checkbox>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.acmeAccountManage')\" prop=\"acmeAccountID\" v-if=\"req.enableSSL\">\n                    <el-select v-model=\"req.acmeAccountID\" :placeholder=\"$t('website.selectAcme')\" @change=\"loadSSL\">\n                        <el-option :key=\"0\" :label=\"$t('website.imported')\" :value=\"0\"></el-option>\n                        <el-option\n                            v-for=\"(acme, index) in acmeAccounts\"\n                            :key=\"index\"\n                            :label=\"acme.email\"\n                            :value=\"acme.id\"\n                        >\n                            <span>\n                                {{ acme.email }}\n                                <el-tag class=\"ml-5\">{{ getAccountName(acme.type) }}</el-tag>\n                            </span>\n                        </el-option>\n                    </el-select>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.ssl')\" prop=\"sslID\" v-if=\"req.enableSSL\">\n                    <el-select\n                        v-model=\"req.sslID\"\n                        :placeholder=\"$t('website.selectSSL')\"\n                        @change=\"changeSSl(req.sslID)\"\n                    >\n                        <el-option v-for=\"(ssl, index) in ssls\" :key=\"index\" :label=\"ssl.primaryDomain\" :value=\"ssl.id\">\n                            <span>{{ ssl.primaryDomain }}</span>\n                            <el-tag class=\"tagClass\" v-if=\"ssl.expireDate\">\n                                {{ dateFormatSimple(ssl.expireDate) }}\n                            </el-tag>\n                            <el-tag class=\"tagClass\" v-if=\"ssl.organization\">\n                                {{ ssl.organization }}\n                            </el-tag>\n                        </el-option>\n                    </el-select>\n                </el-form-item>\n                <el-alert :closable=\"false\">\n                    {{ $t('aiTools.proxy.proxyHelper5') }}\n                    <el-link class=\"pageRoute\" icon=\"Position\" @click=\"toInstalled()\" type=\"primary\">\n                        {{ $t('firewall.quickJump') }}\n                    </el-link>\n                </el-alert>\n            </el-form>\n        </div>\n        <template #footer>\n            <el-button @click=\"handleClose\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                {{ $t('commons.button.add') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { listSSL, searchAcmeAccount } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport { getAccountName, dateFormatSimple } from '@/utils/util';\nimport { bindDomain, getBindDomain, updateBindDomain } from '@/api/modules/ai';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\n\ntype SSLItem = Website.SSL & { organization?: string };\n\nconst open = ref(false);\nconst operate = ref('create');\nconst loading = ref(false);\nconst ssls = ref<SSLItem[]>([]);\nconst websiteSSL = ref<Website.SSL>();\nconst acmeAccounts = ref();\nconst formRef = ref();\nconst req = ref({\n    domain: '',\n    sslID: undefined,\n    ipList: '',\n    acmeAccountID: 0,\n    enableSSL: false,\n    allowIPs: [],\n    appInstallID: 0,\n    websiteID: 0,\n});\nconst sslReq = reactive({\n    acmeAccountID: '',\n});\nconst rules = reactive<FormRules>({\n    domain: [Rules.domainWithPort],\n    sslID: [Rules.requiredSelectBusiness],\n});\nconst emit = defineEmits(['search']);\n\nconst handleClose = () => {\n    emit('search');\n    open.value = false;\n};\n\nconst acceptParams = (installID: number) => {\n    req.value.appInstallID = installID;\n    search(installID);\n    open.value = true;\n};\n\nconst changeSSl = (sslid: number) => {\n    const res = ssls.value.filter((element: Website.SSL) => {\n        return element.id == sslid;\n    });\n    websiteSSL.value = res[0];\n};\n\nconst changeSSL = () => {\n    if (!req.value.enableSSL) {\n        req.value.sslID = undefined;\n    } else {\n        listAcmeAccount();\n    }\n};\n\nconst loadSSL = () => {\n    sslReq.acmeAccountID = String(req.value.acmeAccountID);\n    listSSL(sslReq).then((res) => {\n        ssls.value = res.data || [];\n        if (ssls.value.length > 0) {\n            let exist = false;\n            for (const ssl of ssls.value) {\n                if (ssl.id === req.value.sslID) {\n                    exist = true;\n                    break;\n                }\n            }\n            if (!exist) {\n                req.value.sslID = ssls.value[0].id;\n            }\n            changeSSl(req.value.sslID);\n        } else {\n            req.value.sslID = undefined;\n        }\n    });\n};\n\nconst listAcmeAccount = () => {\n    searchAcmeAccount({ page: 1, pageSize: 100 }).then((res) => {\n        acmeAccounts.value = res.data.items || [];\n        loadSSL();\n    });\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (operate.value === 'update') {\n            await updateBindDomain(req.value);\n        } else {\n            await bindDomain(req.value);\n        }\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        handleClose();\n    });\n};\n\nconst search = async (appInstallID: number) => {\n    try {\n        const res = await getBindDomain({\n            appInstallID: appInstallID,\n        });\n        if (res.data.websiteID > 0) {\n            operate.value = 'update';\n            req.value.domain = res.data.domain;\n            req.value.websiteID = res.data.websiteID;\n            if (res.data.allowIPs && res.data.allowIPs.length > 0) {\n                req.value.ipList = res.data.allowIPs.join('\\n');\n            }\n            if (res.data.sslID > 0) {\n                req.value.enableSSL = true;\n                req.value.sslID = res.data.sslID;\n                req.value.acmeAccountID = res.data.acmeAccountID;\n                listAcmeAccount();\n            }\n        }\n    } catch (e) {}\n};\n\nconst toWebsite = (websiteID: number) => {\n    if (websiteID != undefined && websiteID > 0) {\n        window.location.href = `/websites/${websiteID}/config/basic`;\n    } else {\n        window.location.href = '/websites';\n    }\n};\n\nconst toInstalled = () => {\n    window.location.href = '/apps/installed';\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.pageRoute {\n    font-size: 12px;\n    margin-left: 5px;\n}\n.tagClass {\n    float: right;\n    margin-right: 10px;\n    font-size: 12px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/model/ollama/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <RouterMenu />\n        <LayoutContent title=\"Ollama\">\n            <template #app>\n                <AppStatus\n                    app-key=\"ollama\"\n                    v-model:loading=\"loading\"\n                    :hide-setting=\"true\"\n                    v-model:mask-show=\"maskShow\"\n                    v-model:appInstallID=\"appInstallID\"\n                    @is-exist=\"checkExist\"\n                    ref=\"appStatusRef\"\n                ></AppStatus>\n            </template>\n            <template #prompt>\n                <el-alert type=\"info\" :closable=\"false\">\n                    <template #title>\n                        <span>{{ $t('runtime.systemRestartHelper') }}</span>\n                    </template>\n                </el-alert>\n            </template>\n            <template #leftToolBar>\n                <el-button :disabled=\"modelInfo.status !== 'Running'\" type=\"primary\" @click=\"onCreate()\">\n                    {{ $t('commons.button.add') }}\n                </el-button>\n                <el-button plain type=\"primary\" :disabled=\"modelInfo.status !== 'Running'\" @click=\"bindDomain\">\n                    {{ $t('aiTools.proxy.proxy') }}\n                </el-button>\n                <el-button :disabled=\"modelInfo.status !== 'Running'\" @click=\"onLoadConn\" type=\"primary\" plain>\n                    {{ $t('database.databaseConnInfo') }}\n                </el-button>\n                <el-button :disabled=\"modelInfo.status !== 'Running'\" type=\"primary\" plain @click=\"onSync()\">\n                    {{ $t('database.loadFromRemote') }}\n                </el-button>\n                <el-button\n                    :disabled=\"modelInfo.status !== 'Running'\"\n                    icon=\"Position\"\n                    @click=\"goDashboard()\"\n                    type=\"primary\"\n                    plain\n                >\n                    OpenWebUI\n                </el-button>\n\n                <el-button plain :disabled=\"selects.length === 0\" type=\"primary\" @click=\"onDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"model-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :class=\"{ mask: maskShow }\"\n                    @sort-change=\"search\"\n                    @search=\"search\"\n                    :data=\"data\"\n                >\n                    <el-table-column type=\"selection\" :selectable=\"selectable\" fix />\n                    <el-table-column :label=\"$t('aiTools.model.model')\" prop=\"name\" min-width=\"90\">\n                        <template #default=\"{ row }\">\n                            <el-text v-if=\"row.size\" type=\"primary\" class=\"cursor-pointer\" @click=\"onLoad(row.name)\">\n                                {{ row.name }}\n                            </el-text>\n                            <span v-else>{{ row.name }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('file.size')\" prop=\"size\">\n                        <template #default=\"{ row }\">\n                            <span>{{ row.size || '-' }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <el-tag v-if=\"row.status === 'Success'\" type=\"success\">\n                                {{ $t('commons.status.success') }}\n                            </el-tag>\n                            <el-tag v-if=\"row.status === 'Deleted'\" type=\"info\">\n                                {{ $t('database.isDelete') }}\n                            </el-tag>\n                            <el-tag v-if=\"row.status === 'Canceled'\" type=\"danger\">\n                                {{ $t('commons.status.systemrestart') }}\n                            </el-tag>\n                            <el-tag v-if=\"row.status === 'Failed'\" type=\"danger\">\n                                {{ $t('commons.status.failed') }}\n                            </el-tag>\n                            <el-tag v-if=\"row.status === 'Waiting'\">\n                                <el-icon v-if=\"row.status === 'Waiting'\" class=\"is-loading\">\n                                    <Loading />\n                                </el-icon>\n                                {{ $t('commons.status.waiting') }}\n                            </el-tag>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\">\n                        <template #default=\"{ row }\">\n                            <el-button @click=\"onLoadLog(row)\" link type=\"primary\">\n                                {{ $t('website.check') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        min-width=\"80\"\n                        :label=\"$t('commons.table.date')\"\n                        prop=\"createdAt\"\n                        :formatter=\"dateFormat\"\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 10\"\n                        :min-width=\"mobile ? 'auto' : 200\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        fixed=\"right\"\n                        fix\n                    />\n                </ComplexTable>\n\n                <el-card v-if=\"modelInfo.status != 'Running' && !loading && maskShow\" class=\"mask-prompt\">\n                    <span v-if=\"modelInfo.isExist\">\n                        {{ $t('commons.service.serviceNotStarted', ['Ollama']) }}\n                    </span>\n                    <span v-else>\n                        {{ $t('app.checkInstalledWarn', ['Ollama']) }}\n                        <el-button @click=\"goInstall('ollama')\" link icon=\"Position\" type=\"primary\">\n                            {{ $t('database.goInstall') }}\n                        </el-button>\n                    </span>\n                </el-card>\n            </template>\n        </LayoutContent>\n\n        <DialogPro v-model=\"dashboardVisible\" :title=\"$t('app.checkTitle')\" size=\"mini\">\n            <div class=\"flex justify-center items-center gap-2 flex-wrap\">\n                {{ $t('app.checkInstalledWarn', ['OpenWebUI']) }}\n                <el-link icon=\"Position\" @click=\"goInstall('ollama-webui')\" type=\"primary\">\n                    {{ $t('database.goInstall') }}\n                </el-link>\n            </div>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"dashboardVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"onSubmitDelete()\">\n            <template #content>\n                <el-form class=\"mt-4 mb-1\" ref=\"deleteForm\" label-position=\"left\">\n                    <el-form-item>\n                        <el-checkbox v-model=\"forceDelete\" :label=\"$t('website.forceDelete')\" />\n                        <span class=\"input-help\">\n                            {{ $t('website.forceDeleteHelper') }}\n                        </span>\n                    </el-form-item>\n                </el-form>\n            </template>\n        </OpDialog>\n        <AddDialog ref=\"addRef\" @search=\"search\" @log=\"onLoadLog\" />\n        <Del ref=\"delRef\" @search=\"search\" />\n        <Terminal ref=\"terminalRef\" />\n        <Conn ref=\"connRef\" />\n        <CodemirrorDrawer ref=\"detailRef\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <BindDomain ref=\"bindDomainRef\" />\n\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport AppStatus from '@/components/app-status/index.vue';\nimport AddDialog from '@/views/ai/model/ollama/add/index.vue';\nimport Conn from '@/views/ai/model/ollama/conn/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport Terminal from '@/views/ai/model/ollama/terminal/index.vue';\nimport Del from '@/views/ai/model/ollama/del/index.vue';\nimport RouterMenu from '@/views/ai/model/router-menu.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { App } from '@/api/interface/app';\nimport { GlobalStore } from '@/store';\nimport {\n    deleteOllamaModel,\n    loadOllamaModel,\n    recreateOllamaModel,\n    searchOllamaModel,\n    syncOllamaModel,\n} from '@/api/modules/ai';\nimport { AI } from '@/api/interface/ai';\nimport { getAppPort } from '@/api/modules/app';\nimport { dateFormat, newUUID } from '@/utils/util';\nimport { MsgInfo, MsgSuccess } from '@/utils/message';\nimport BindDomain from '@/views/ai/model/ollama/domain/index.vue';\nimport { routerToNameWithQuery } from '@/utils/router';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\nconst selects = ref<any>([]);\nconst maskShow = ref(false);\nconst addRef = ref();\nconst detailRef = ref();\nconst delRef = ref();\nconst connRef = ref();\nconst terminalRef = ref();\nconst openWebUIPort = ref();\nconst dashboardVisible = ref(false);\nconst dialogPortJumpRef = ref();\nconst appStatusRef = ref();\nconst bindDomainRef = ref();\nconst taskLogRef = ref();\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'model-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('page-size')) || 20,\n    total: 0,\n});\nconst searchName = ref();\nconst appInstallID = ref(0);\n\nconst opRef = ref();\nconst operateIDs = ref();\nconst forceDelete = ref();\n\nconst modelInfo = reactive({\n    status: '',\n    container: '',\n    isExist: null,\n    version: '',\n    port: 11434,\n});\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nfunction selectable(row) {\n    return row.status !== 'Waiting';\n}\n\nconst search = async () => {\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        info: searchName.value,\n    };\n    loading.value = true;\n    await searchOllamaModel(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onCreate = async () => {\n    addRef.value.acceptParams();\n};\n\nconst onSync = async () => {\n    loading.value = true;\n    await syncOllamaModel()\n        .then((res) => {\n            loading.value = false;\n            if (res.data) {\n                delRef.value.acceptParams({ list: res.data });\n            } else {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onLoadConn = async () => {\n    connRef.value.acceptParams({\n        port: modelInfo.port,\n        containerName: modelInfo.container,\n        appinstallID: appInstallID.value,\n    });\n};\n\nconst onLoad = async (name: string) => {\n    const res = await loadOllamaModel(name);\n    let detailInfo = res.data;\n    let param = {\n        header: i18n.global.t('commons.button.view'),\n        detailInfo: detailInfo,\n        mode: 'json',\n    };\n    detailRef.value!.acceptParams(param);\n};\n\nconst goDashboard = async () => {\n    if (openWebUIPort.value === 0) {\n        dashboardVisible.value = true;\n        return;\n    }\n    dialogPortJumpRef.value.acceptParams({ port: openWebUIPort.value });\n};\n\nconst bindDomain = () => {\n    bindDomainRef.value.acceptParams(appInstallID.value);\n};\n\nconst goInstall = (name: string) => {\n    routerToNameWithQuery('AppAll', { install: name });\n};\n\nconst loadWebUIPort = async () => {\n    const res = await getAppPort('ollama-webui', '');\n    openWebUIPort.value = res.data;\n};\n\nconst checkExist = (data: App.CheckInstalled) => {\n    modelInfo.isExist = data.isExist;\n    modelInfo.status = data.status;\n    modelInfo.version = data.version;\n    modelInfo.container = data.containerName;\n    modelInfo.port = data.httpPort;\n\n    if (modelInfo.isExist && modelInfo.status === 'Running') {\n        search();\n    }\n};\n\nconst onSubmitDelete = async () => {\n    loading.value = true;\n    await deleteOllamaModel(operateIDs.value, forceDelete.value)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onReCreate = async (name: string) => {\n    loading.value = true;\n    let taskID = newUUID();\n    await recreateOllamaModel(name, taskID)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            openTaskLog(taskID);\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst onDelete = async (row: AI.OllamaModelInfo) => {\n    let names = [];\n    let ids = [];\n    if (row) {\n        ids = [row.id];\n        names = [row.name];\n    } else {\n        for (const item of selects.value) {\n            names.push(item.name);\n            ids.push(item.id);\n        }\n    }\n    operateIDs.value = ids;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('aiTools.model.model'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\n\nconst onLoadLog = (row: any) => {\n    if (row.taskID) {\n        openTaskLog(row.taskID);\n    }\n    if (row.from === 'remote') {\n        MsgInfo(i18n.global.t('aiTools.model.from_remote'));\n        return;\n    }\n    if (!row.logFileExist) {\n        MsgInfo(i18n.global.t('aiTools.model.no_logs'));\n        return;\n    }\n    taskLogRef.value.openWithResourceID('AI', 'TaskPull', row.id);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.run'),\n        click: (row: AI.OllamaModelInfo) => {\n            terminalRef.value.acceptParams({ name: row.name });\n        },\n        disabled: (row: any) => {\n            return row.status !== 'Success';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.retry'),\n        click: (row: AI.OllamaModelInfo) => {\n            onReCreate(row.name);\n        },\n        disabled: (row: any) => {\n            return row.status === 'Success' || row.status === 'Waiting';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: AI.OllamaModelInfo) => {\n            onDelete(row);\n        },\n        disabled: (row: any) => {\n            return row.status === 'Waiting';\n        },\n    },\n];\n\nonMounted(() => {\n    loadWebUIPort();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.iconInTable {\n    margin-left: 5px;\n    margin-top: 3px;\n}\n.jumpAdd {\n    margin-top: 10px;\n    margin-left: 15px;\n    margin-bottom: 5px;\n    font-size: 12px;\n}\n.tagClass {\n    float: right;\n    font-size: 12px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/ai/model/ollama/terminal/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :title=\"$t('menu.terminal')\"\n        @close=\"handleClose\"\n        :resource=\"title\"\n        :autoClose=\"false\"\n        :fullScreen=\"true\"\n    >\n        <Terminal class=\"mt-2\" style=\"height: calc(100vh - 175px)\" ref=\"terminalRef\"></Terminal>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button type=\"primary\" @click=\"handleClose\">\n                    {{ $t('commons.button.disConn') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { nextTick, ref } from 'vue';\nimport Terminal from '@/components/terminal/index.vue';\nimport { closeOllamaModel } from '@/api/modules/ai';\n\nconst title = ref();\nconst open = ref(false);\nconst itemName = ref();\nconst terminalRef = ref();\n\ninterface DialogProps {\n    name: string;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    itemName.value = params.name;\n    open.value = true;\n    initTerm();\n};\n\nconst initTerm = () => {\n    nextTick(() => {\n        terminalRef.value.acceptParams({\n            endpoint: '/api/v2/containers/exec',\n            args: `source=ollama&name=${itemName.value}`,\n            error: '',\n            initCmd: '',\n        });\n    });\n};\n\nconst onClose = async () => {\n    await closeOllamaModel(itemName.value)\n        .then(() => {\n            terminalRef.value?.onClose();\n        })\n        .catch(() => {\n            terminalRef.value?.onClose();\n        });\n};\n\nfunction handleClose() {\n    onClose();\n    open.value = false;\n}\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/model/router-menu.vue",
    "content": "<template>\n    <RouterButton :buttons=\"buttons\" />\n</template>\n\n<script lang=\"ts\" setup>\nconst buttons = [\n    {\n        label: 'Ollama',\n        path: '/ai/model/ollama',\n    },\n    {\n        label: 'vLLM',\n        path: '/ai/model/vllm',\n    },\n    {\n        label: 'TensorRT LLM',\n        path: '/ai/model/tensorrt',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/model/tensorrt/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <LayoutContent>\n            <template #leftToolBar>\n                <div class=\"flex flex-wrap gap-3\">\n                    <el-button type=\"primary\" @click=\"openCreate\">\n                        {{ $t('commons.button.create') }}\n                    </el-button>\n                </div>\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @search=\"search\"\n                    v-loading=\"loading\"\n                >\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        min-width=\"120\"\n                        prop=\"name\"\n                        show-overflow-tooltip\n                    />\n                    <el-table-column :label=\"$t('app.version')\" min-width=\"100\" prop=\"version\" show-overflow-tooltip />\n                    <el-table-column :label=\"$t('commons.table.port')\" min-width=\"80\" prop=\"port\">\n                        <template #default=\"{ row }\">\n                            <PortJump :row=\"row\" :jump=\"goDashboard\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" min-width=\"100\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <Status :key=\"row.status\" :status=\"row.status\"></Status>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" width=\"120px\">\n                        <template #default=\"{ row }\">\n                            <el-button\n                                @click=\"openLog(row)\"\n                                link\n                                type=\"primary\"\n                                :disabled=\"\n                                    row.status !== 'Running' && row.status !== 'Rrror' && row.status !== 'Restarting'\n                                \"\n                            >\n                                {{ $t('website.check') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        width=\"180\"\n                        fix\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 5\"\n                        :min-width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        fixed=\"right\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <ComposeLogs ref=\"composeLogRef\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport OperateDialog from './operate/index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport RouterMenu from '@/views/ai/model/router-menu.vue';\nimport PortJump from '@/views/website/runtime/components/port-jump.vue';\n\nimport { reactive, onMounted, ref } from 'vue';\nimport { dateFormat } from '@/utils/util';\nimport { AI } from '@/api/interface/ai';\nimport { deleteTensorRTLLM, operateTensorRTLLM, pageTensorRTLLM } from '@/api/modules/ai';\nimport { ElMessageBox } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref();\nconst data = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\nconst searchName = ref();\nconst opRef = ref();\nconst dialogRef = ref();\nconst composeLogRef = ref();\nconst dialogPortJumpRef = ref();\n\nconst search = async () => {\n    const params = {\n        name: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await pageTensorRTLLM(params)\n        .then((res) => {\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total || 0;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst openCreate = () => {\n    dialogRef.value.openCreate();\n};\n\nconst openEdit = (row: AI.TensorRTLLM) => {\n    dialogRef.value.openEdit(row);\n};\n\nconst goDashboard = async (port: any, protocol: string) => {\n    dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });\n};\n\nconst openLog = (row: AI.McpServer) => {\n    composeLogRef.value.acceptParams({\n        compose: row.dir + '/docker-compose.yml',\n        resource: row.name,\n        container: row.containerName,\n    });\n};\n\nconst operate = async (row: AI.TensorRTLLM, operation: string) => {\n    ElMessageBox.confirm(\n        i18n.global.t('commons.msg.operatorHelper', ['LLM', i18n.global.t('commons.operate.' + operation)]),\n        i18n.global.t('commons.operate.' + operation),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(async () => {\n        loading.value = true;\n        await operateTensorRTLLM({ id: row.id, operate: operation })\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\nconst deleteLLM = async (row: AI.TensorRTLLM) => {\n    try {\n        opRef.value.acceptParams({\n            title: i18n.global.t('commons.button.delete'),\n            names: [row.name],\n            msg: i18n.global.t('commons.msg.operatorHelper', ['LLM', i18n.global.t('commons.button.delete')]),\n            api: deleteTensorRTLLM,\n            params: { id: row.id },\n        });\n    } catch (error) {}\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: AI.TensorRTLLM) => {\n            openEdit(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.start'),\n        disabled: (row: AI.TensorRTLLM) => {\n            return row.status === 'Running';\n        },\n        click: (row: AI.TensorRTLLM) => {\n            operate(row, 'start');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.stop'),\n        disabled: (row: AI.TensorRTLLM) => {\n            return row.status !== 'Running';\n        },\n        click: (row: AI.TensorRTLLM) => {\n            operate(row, 'stop');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: (row: AI.TensorRTLLM) => {\n            operate(row, 'restart');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: AI.TensorRTLLM) => {\n            deleteLLM(row);\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/model/tensorrt/operate/index.vue",
    "content": "<template>\n    <DrawerPro :header=\"$t('commons.button.' + mode)\" v-model=\"drawerVisiable\" size=\"large\" @close=\"handleClose\">\n        <el-alert :title=\"$t('aiTools.tensorRT.imageAlert')\" class=\"common-prompt\" :closable=\"false\" type=\"warning\" />\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"tensorRTLLM\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input clearable v-model.trim=\"tensorRTLLM.name\" :disabled=\"mode == 'edit'\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('app.containerName')\" prop=\"containerName\">\n                <el-input v-model.trim=\"tensorRTLLM.containerName\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.image')\" prop=\"image\">\n                <el-input v-model.trim=\"tensorRTLLM.image\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('app.version')\" prop=\"version\">\n                <el-input v-model.trim=\"tensorRTLLM.version\" />\n            </el-form-item>\n            <div v-if=\"isFxplay\">\n                <el-form-item :label=\"$t('aiTools.tensorRT.modelSpeedup')\" prop=\"modelSpeedup\">\n                    <el-switch v-model=\"tensorRTLLM.modelSpeedup\" @change=\"changeModelSpeedup\"></el-switch>\n                </el-form-item>\n                <el-form-item\n                    :label=\"$t('aiTools.tensorRT.modelType')\"\n                    prop=\"modelType\"\n                    v-if=\"tensorRTLLM.modelSpeedup\"\n                >\n                    <el-select v-model=\"tensorRTLLM.modelType\">\n                        <el-option label=\"Qwen3\" value=\"Qwen3\" />\n                    </el-select>\n                </el-form-item>\n            </div>\n            <el-form-item :label=\"$t('aiTools.tensorRT.modelDir')\" prop=\"modelDir\">\n                <el-input v-model=\"tensorRTLLM.modelDir\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"modelDirRef.acceptParams({ dir: true })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('runtime.runScript')\" prop=\"command\">\n                <el-input v-model=\"tensorRTLLM.command\"></el-input>\n                <span class=\"input-help\">\n                    {{ $t('aiTools.tensorRT.commandHelper') }}\n                </span>\n            </el-form-item>\n            <NodeConfig v-model=\"tensorRTLLM\" />\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisiable = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n        <FileList ref=\"modelDirRef\" @choose=\"getModelDir\" />\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport NodeConfig from '@/views/website/runtime//components/node-config.vue';\nimport DrawerPro from '@/components/drawer-pro/index.vue';\nimport FileList from '@/components/file-list/index.vue';\n\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm, FormInstance } from 'element-plus';\nimport { createTensorRTLLM, updateTensorRTLLM } from '@/api/modules/ai';\nimport { MsgSuccess } from '@/utils/message';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { isFxplay } = useGlobalStore();\n\nconst loading = ref(false);\nconst mode = ref('create');\nconst drawerVisiable = ref(false);\nconst newTensorRTLLM = () => {\n    return {\n        name: '',\n        containerName: '',\n        version: '1.2.0rc0',\n        modelDir: '',\n        image: 'nvcr.io/nvidia/tensorrt-llm/release',\n        command: 'bash -c \"trtllm-serve ${MODEL_PATH} --host 0.0.0.0 --port 8000\"',\n        exposedPorts: [],\n        environments: [],\n        extraHosts: [],\n        volumes: [],\n        modelSpeedup: false,\n        modelType: 'Qwen3',\n    };\n};\nconst modelDirRef = ref();\nconst tensorRTLLM = ref(newTensorRTLLM());\nconst emit = defineEmits(['search']);\n\nconst openCreate = (): void => {\n    mode.value = 'create';\n    drawerVisiable.value = true;\n    tensorRTLLM.value = newTensorRTLLM();\n};\n\nconst openEdit = (rowData: any): void => {\n    mode.value = 'edit';\n    tensorRTLLM.value = { ...rowData };\n    if (tensorRTLLM.value.environments == null) {\n        tensorRTLLM.value.environments = [];\n    }\n    if (tensorRTLLM.value.volumes == null) {\n        tensorRTLLM.value.volumes = [];\n    }\n    if (tensorRTLLM.value.exposedPorts == null) {\n        tensorRTLLM.value.exposedPorts = [];\n    }\n    tensorRTLLM.value.command = rowData.command.slice(1, -1).replace(/^'|'$/g, '').replace(/\\\\\"/g, '\"');\n    if (tensorRTLLM.value.extraHosts == null) {\n        tensorRTLLM.value.extraHosts = [];\n    }\n    drawerVisiable.value = true;\n};\n\nconst handleClose = () => {\n    drawerVisiable.value = false;\n};\n\nconst getModelDir = (path: string) => {\n    tensorRTLLM.value.modelDir = path;\n};\n\nconst changeModelSpeedup = () => {\n    if (tensorRTLLM.value.modelSpeedup) {\n        tensorRTLLM.value.command =\n            'bash -c \"${MODEL_PATH}/fusionxpark_accelerator --model_path ${MODEL_PATH} --host 0.0.0.0 --port 8000\"';\n    } else {\n        tensorRTLLM.value.command = 'bash -c \"trtllm-serve ${MODEL_PATH} --host 0.0.0.0 --port 8000\"';\n    }\n};\n\nconst rules = reactive({\n    name: [Rules.requiredInput],\n    version: [Rules.requiredInput],\n    modelDir: [Rules.requiredInput],\n    containerName: [Rules.requiredInput],\n    image: [Rules.requiredInput],\n    command: [Rules.requiredInput],\n    modelType: [Rules.requiredSelect],\n});\n\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async () => {\n    formRef.value?.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        if (mode.value === 'edit') {\n            await updateTensorRTLLM(tensorRTLLM.value)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisiable.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n            return;\n        }\n        await createTensorRTLLM(tensorRTLLM.value)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisiable.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nwatch(\n    () => tensorRTLLM.value.name,\n    (newVal) => {\n        if (newVal && mode.value == 'create') {\n            tensorRTLLM.value.containerName = newVal;\n        }\n    },\n    { deep: true },\n);\n\ndefineExpose({\n    openCreate,\n    openEdit,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/ai/model/vllm/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <LayoutContent title=\"vLLM\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"goLicense\">\n                    {{ $t('license.levelUpPro') }}\n                </el-button>\n            </template>\n            <template #prompt>\n                <el-alert type=\"info\" :closable=\"false\">\n                    <template #title>\n                        <span>{{ $t('license.vllm') }}</span>\n                    </template>\n                </el-alert>\n            </template>\n            <template #main>\n                <div class=\"vllm-placeholder\">\n                    <div class=\"vllm-placeholder__intro\">\n                        <div class=\"vllm-placeholder__title\">{{ $t('license.introduce') }}</div>\n                        <div class=\"vllm-placeholder__desc\">{{ $t('license.vllm') }}</div>\n                    </div>\n                    <el-row :gutter=\"16\">\n                        <el-col v-for=\"item in features\" :key=\"item.title\" :xs=\"24\" :sm=\"12\" :xl=\"6\">\n                            <el-card class=\"feature-card\" shadow=\"hover\">\n                                <template #header>\n                                    <div class=\"feature-card__header\">\n                                        <svg-icon class=\"feature-card__icon\" :iconName=\"item.icon\" />\n                                        <span>{{ $t(item.title) }}</span>\n                                    </div>\n                                </template>\n                                <div class=\"feature-card__content\">{{ $t(item.content) }}</div>\n                            </el-card>\n                        </el-col>\n                    </el-row>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport RouterMenu from '@/views/ai/model/router-menu.vue';\nimport { routerToName } from '@/utils/router';\n\nconst features = [\n    { icon: 'p-ai-1', title: 'xpack.helper.vllmTitle1', content: 'xpack.helper.vllmContent1' },\n    { icon: 'p-ai-2', title: 'xpack.helper.vllmTitle2', content: 'xpack.helper.vllmContent2' },\n    { icon: 'p-ai-3', title: 'xpack.helper.vllmTitle3', content: 'xpack.helper.vllmContent3' },\n    { icon: 'p-ai-4', title: 'xpack.helper.vllmTitle4', content: 'xpack.helper.vllmContent4' },\n];\n\nconst goLicense = () => {\n    routerToName('License');\n};\n</script>\n\n<style scoped lang=\"scss\">\n.vllm-placeholder {\n    padding-bottom: 16px;\n}\n\n.vllm-placeholder__intro {\n    max-width: 760px;\n    margin-bottom: 20px;\n}\n\n.vllm-placeholder__title {\n    font-size: 28px;\n    font-weight: 500;\n    line-height: 1.4;\n}\n\n.vllm-placeholder__desc {\n    margin-top: 12px;\n    color: var(--el-text-color-regular);\n    line-height: 1.7;\n}\n\n.feature-card {\n    height: 100%;\n    border: var(--panel-border);\n}\n\n.feature-card__header {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    font-size: 16px;\n    font-weight: 500;\n}\n\n.feature-card__icon {\n    font-size: 18px;\n}\n\n.feature-card__content {\n    color: var(--el-text-color-regular);\n    line-height: 1.7;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/apps/app/index.vue",
    "content": "<template>\n    <div class=\"app\">\n        <el-card>\n            <div class=\"app-wrapper\" @click=\"openDetail(app.key)\">\n                <div class=\"app-image\">\n                    <el-avatar shape=\"square\" :size=\"60\" :src=\"getAppIconUrl(app.key, currentNode)\" />\n                </div>\n                <div class=\"app-content\">\n                    <div class=\"content-top\">\n                        <el-space wrap :size=\"1\">\n                            <span class=\"app-title\">{{ app.name }}</span>\n                            <el-tag type=\"success\" v-if=\"app.installed\" round size=\"small\" class=\"!ml-2\">\n                                {{ $t('app.allReadyInstalled') }}\n                            </el-tag>\n                        </el-space>\n                    </div>\n                    <div class=\"content-middle\">\n                        <span class=\"app-description\">\n                            {{ app.description }}\n                        </span>\n                    </div>\n                    <div class=\"content-bottom\">\n                        <div class=\"app-tags\">\n                            <el-tag v-for=\"(tag, ind) in app.tags\" :key=\"ind\" type=\"info\">\n                                <span>\n                                    {{ tag }}\n                                </span>\n                            </el-tag>\n                            <el-tag v-if=\"app.status === 'TakeDown'\" class=\"p-mr-5\">\n                                <span class=\"text-red-500\">{{ $t('app.takeDown') }}</span>\n                            </el-tag>\n                        </div>\n                        <el-button\n                            type=\"primary\"\n                            size=\"small\"\n                            plain\n                            round\n                            :disabled=\"(app.installed && app.limit == 1) || app.status === 'TakeDown'\"\n                            @click.stop=\"openInstall(app)\"\n                        >\n                            {{ $t('commons.button.install') }}\n                        </el-button>\n                    </div>\n                </div>\n            </div>\n        </el-card>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getAppIconUrl } from '@/api/modules/app';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { currentNode } = useGlobalStore();\n\ndefineProps({\n    app: {\n        type: Object,\n        default: () => ({}),\n    },\n});\n\nconst em = defineEmits(['openDetail', 'openInstall']);\n\nconst openDetail = (key: string) => {\n    em('openDetail', key);\n};\n\nconst openInstall = (app: any) => {\n    em('openInstall', app);\n};\n</script>\n\n<style lang=\"scss\" scoped>\n.app {\n    margin: 10px;\n    .el-card {\n        padding: 0 !important;\n        border: var(--panel-border) !important;\n        &:hover {\n            border: 1px solid var(--el-color-primary) !important;\n        }\n    }\n    .el-card__body {\n        padding: 8px 8px 2px 8px !important;\n    }\n    .app-wrapper {\n        display: flex;\n        height: 100%;\n        cursor: pointer;\n    }\n    .app-image {\n        flex: 0 0 100px;\n        display: flex;\n        justify-content: center;\n        margin-top: 14px;\n        transition: transform 0.1s;\n    }\n\n    &:hover .app-image {\n        transform: scale(1.2);\n    }\n\n    .el-avatar {\n        width: 65px !important;\n        height: 65px !important;\n        max-width: 65px;\n        max-height: 65px;\n        object-fit: cover;\n    }\n    .app-content {\n        flex: 1;\n        display: flex;\n        flex-direction: column;\n        padding: 10px;\n    }\n    .content-top,\n    .content-bottom {\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n    }\n    .content-middle {\n        flex: 1;\n        margin: 10px 0;\n        overflow: hidden; /* 防止内容溢出 */\n    }\n    .app-name {\n        margin: 0;\n        line-height: 1.5;\n        font-weight: 500;\n        font-size: 16px;\n        color: var(--el-text-color-regular);\n    }\n    .app-description {\n        margin: 0;\n        overflow: hidden;\n        display: -webkit-box;\n        -webkit-line-clamp: 2;\n        -webkit-box-orient: vertical;\n        text-overflow: ellipsis;\n        font-size: 14px;\n        color: var(--el-text-color-regular);\n\n        line-height: 1.2;\n        height: calc(1.2em * 2);\n        min-height: calc(1.2em * 2);\n    }\n    .app-tags {\n        display: flex;\n        gap: 5px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/apps/index.vue",
    "content": "<template>\n    <div>\n        <docker-status v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" />\n        <LayoutContent v-loading=\"loading\" v-if=\"isExist\" :class=\"{ mask: !isActive }\" :title=\"$t('app.app', 2)\">\n            <template #search>\n                <Tags @change=\"changeTag\" />\n            </template>\n            <template #leftToolBar>\n                <el-button @click=\"sync\" type=\"primary\" plain :disabled=\"syncing\">\n                    <span>{{ syncCustomAppstore || isOffLine ? $t('app.syncCustomApp') : $t('app.syncAppList') }}</span>\n                </el-button>\n                <el-button @click=\"syncLocal\" type=\"primary\" plain :disabled=\"syncing\" class=\"ml-2\">\n                    {{ $t('app.syncLocalApp') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <el-checkbox class=\"!mr-2.5\" v-model=\"req.showCurrentArch\" @change=\"search(req)\">\n                    {{ $t('app.showCurrentArch') }}\n                </el-checkbox>\n                <el-checkbox\n                    class=\"!mr-2.5\"\n                    v-model=\"req.resource\"\n                    true-value=\"all\"\n                    false-value=\"remote\"\n                    @change=\"search(req)\"\n                >\n                    {{ $t('app.showLocal') }}\n                </el-checkbox>\n                <TableSearch @search=\"searchByName()\" v-model:searchName=\"req.name\" />\n            </template>\n            <template #main>\n                <div>\n                    <MainDiv :heightDiff=\"300\">\n                        <el-alert type=\"info\" :title=\"$t('app.appHelper')\" :closable=\"false\" />\n                        <el-row :gutter=\"5\" v-if=\"apps.length > 0\">\n                            <el-col\n                                class=\"app-col-12\"\n                                v-for=\"(app, index) in apps\"\n                                :key=\"index\"\n                                :xs=\"24\"\n                                :sm=\"12\"\n                                :md=\"8\"\n                                :lg=\"8\"\n                                :xl=\"6\"\n                            >\n                                <AppCard :app=\"app\" @open-install=\"openInstall\" @open-detail=\"openDetail\" />\n                            </el-col>\n                        </el-row>\n                        <NoApp v-if=\"noApp\" />\n                    </MainDiv>\n                    <div class=\"page-button\">\n                        <fu-table-pagination\n                            v-model:current-page=\"paginationConfig.currentPage\"\n                            v-model:page-size=\"paginationConfig.pageSize\"\n                            v-bind=\"paginationConfig\"\n                            @change=\"search(req)\"\n                            :page-sizes=\"[30, 60, 90]\"\n                            :layout=\"mobile ? 'total, prev, pager, next' : 'total, sizes, prev, pager, next, jumper'\"\n                        />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n    <Install ref=\"installRef\" />\n    <Detail ref=\"detailRef\" />\n    <TaskLog ref=\"taskLogRef\" @close=\"refresh\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { App } from '@/api/interface/app';\nimport { onMounted, reactive, ref, computed } from 'vue';\nimport { searchApp, syncApp, syncCutomAppStore, syncLocalApp, getCurrentNodeCustomAppConfig } from '@/api/modules/app';\nimport Install from '../detail/install/index.vue';\nimport router from '@/routers';\nimport { MsgSuccess } from '@/utils/message';\nimport { newUUID } from '@/utils/util';\nimport Detail from '../detail/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport bus from '@/global/bus';\nimport Tags from '@/views/app-store/components/tag.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport NoApp from '@/views/app-store/apps/no-app/index.vue';\nimport AppCard from '@/views/app-store/apps/app/index.vue';\nimport MainDiv from '@/components/main-div/index.vue';\nimport { jumpToInstall } from '@/utils/app';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { globalStore, isProductPro, isOffLine } = useGlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'app-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('app-page-size')) || 60,\n    total: 0,\n});\n\nconst req = reactive({\n    name: '',\n    tags: [],\n    page: 1,\n    pageSize: 60,\n    resource: 'all',\n    showCurrentArch: false,\n});\n\nconst apps = ref<App.AppItem[]>([]);\nconst loading = ref(false);\nconst canUpdate = ref(false);\nconst syncing = ref(false);\nconst installRef = ref();\nconst installKey = ref('');\nconst mainHeight = ref(0);\nconst detailRef = ref();\nconst taskLogRef = ref();\nconst syncCustomAppstore = ref(false);\nconst isActive = ref(false);\nconst isExist = ref(false);\nconst noApp = ref(false);\n\nconst refresh = () => {\n    search(req);\n};\n\nconst search = async (req: App.AppReq) => {\n    loading.value = true;\n    req.pageSize = paginationConfig.pageSize;\n    req.page = paginationConfig.currentPage;\n    localStorage.setItem('app-page-size', req.pageSize + '');\n\n    const customReq = {\n        page: req.page,\n        pageSize: req.pageSize,\n        tags: req.tags,\n        name: req.name,\n        resource: req.resource,\n        showCurrentArch: req.showCurrentArch,\n    };\n    if (syncCustomAppstore.value && req.resource === 'remote') {\n        customReq.resource = 'custom';\n    }\n    await searchApp(customReq)\n        .then((res) => {\n            apps.value = res.data.items;\n            paginationConfig.total = res.data.total;\n            if (noApp.value && apps.value.length > 0) {\n                noApp.value = false;\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst openInstall = (app: App.App) => {\n    if (!jumpToInstall(app.type, app.key)) {\n        const params = {\n            app: app,\n        };\n        installRef.value.acceptParams(params);\n    }\n};\n\nconst openDetail = (key: string) => {\n    detailRef.value.acceptParams(key, 'install');\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst sync = async () => {\n    syncing.value = true;\n    const taskID = newUUID();\n    const syncReq = {\n        taskID: taskID,\n    };\n    try {\n        let res;\n        if (isOffLine.value || (isProductPro.value && syncCustomAppstore.value)) {\n            res = await syncCutomAppStore(syncReq);\n        } else {\n            res = await syncApp(syncReq);\n        }\n        if (res.message != '' && res.message != 'success') {\n            MsgSuccess(res.message);\n        } else {\n            openTaskLog(taskID);\n        }\n        canUpdate.value = false;\n        search(req);\n    } finally {\n        syncing.value = false;\n    }\n};\n\nconst syncLocal = () => {\n    const taskID = newUUID();\n    const syncReq = {\n        taskID: taskID,\n    };\n    syncing.value = true;\n    syncLocalApp(syncReq)\n        .then(() => {\n            openTaskLog(taskID);\n            canUpdate.value = false;\n            search(req);\n        })\n        .finally(() => {\n            syncing.value = false;\n        });\n};\n\nconst changeTag = (key: string) => {\n    req.tags = [];\n    if (key !== 'all') {\n        req.tags = [key];\n    }\n    search(req);\n};\n\nconst searchByName = () => {\n    search(req);\n};\n\nonMounted(async () => {\n    bus.on('refreshApp', () => {\n        search(req);\n    });\n    if (router.currentRoute.value.query.install) {\n        installKey.value = String(router.currentRoute.value.query.install);\n        const params = {\n            app: {\n                key: installKey.value,\n            },\n        };\n        installRef.value.acceptParams(params);\n    }\n    search(req);\n    if (isProductPro.value) {\n        const res = await getCurrentNodeCustomAppConfig();\n        if (res && res.data) {\n            syncCustomAppstore.value = res.data.status === 'Enable';\n        }\n    }\n    if (isOffLine.value) {\n        syncCustomAppstore.value = true;\n    }\n    mainHeight.value = window.innerHeight - 380;\n    window.onresize = () => {\n        return (() => {\n            mainHeight.value = window.innerHeight - 380;\n        })();\n    };\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.header {\n    padding-bottom: 10px;\n}\n\n@media only screen and (min-width: 768px) and (max-width: 1200px) {\n    .app-col-12 {\n        max-width: 50%;\n        flex: 0 0 50%;\n    }\n}\n\n.page-button {\n    float: right;\n    margin-bottom: 10px;\n    margin-top: 10px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/apps/no-app/index.vue",
    "content": "<template>\n    <div class=\"app-warn\">\n        <div class=\"flex flex-col gap-2 items-center justify-center w-full sm:flex-row\">\n            <span>{{ $t('app.noAppHelper') }}</span>\n            <span class=\"flex items-center justify-center gap-0.5\" @click=\"openTask\">\n                <el-icon><Position /></el-icon>\n                {{ $t('website.check') }}\n            </span>\n        </div>\n        <div>\n            <img src=\"@/assets/images/no_app.svg\" />\n        </div>\n    </div>\n    <TaskList ref=\"taskListRef\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport TaskList from '@/components/task-list/index.vue';\nconst taskListRef = ref();\nconst openTask = () => {\n    taskListRef.value.acceptParams();\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/app-store/components/tag.vue",
    "content": "<template>\n    <div ref=\"containerRef\" class=\"flex gap-2\">\n        <el-check-tag :checked=\"activeTag == 'all'\" @click=\"changeTag('all')\">\n            {{ $t('app.all') }}\n        </el-check-tag>\n        <el-check-tag\n            :checked=\"activeTag == item.key\"\n            v-for=\"item in visibleTags\"\n            :key=\"item.key\"\n            @click=\"changeTag(item.key)\"\n        >\n            {{ item.name }}\n        </el-check-tag>\n        <div class=\"inline\" v-if=\"hiddenTags.length > 0\">\n            <el-dropdown>\n                <el-check-tag :checked=\"moreTag !== ''\">\n                    {{ moreTag == '' ? $t('tabs.more') : getTagValue(moreTag) }}\n                    <el-icon :size=\"10\">\n                        <arrow-down />\n                    </el-icon>\n                </el-check-tag>\n                <template #dropdown>\n                    <el-dropdown-menu>\n                        <el-dropdown-item v-for=\"item in hiddenTags\" @click=\"changeTag(item.key)\" :key=\"item.key\">\n                            {{ item.name }}\n                        </el-dropdown-item>\n                    </el-dropdown-menu>\n                </template>\n            </el-dropdown>\n        </div>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getAppTags } from '@/api/modules/app';\nimport { App } from '@/api/interface/app';\n\nconst props = defineProps({\n    hideKey: {\n        type: String,\n        default: '',\n    },\n});\n\nconst containerRef = ref<HTMLElement>();\nconst activeTag = ref('all');\nconst tags = ref<App.Tag[]>([]);\nconst moreTag = ref('');\nconst visibleTagCount = ref(7);\nconst emit = defineEmits(['change']);\n\nconst visibleTags = computed(() => tags.value.slice(0, visibleTagCount.value));\nconst hiddenTags = computed(() => tags.value.slice(visibleTagCount.value));\n\nconst getTagValue = (key: string) => {\n    const tag = tags.value.find((tag) => tag.key === key);\n    if (tag) {\n        return tag.name;\n    }\n};\n\nconst changeTag = (key: string) => {\n    activeTag.value = key;\n    emit('change', key);\n    const index = tags.value.findIndex((tag) => tag.key === key);\n    if (index >= visibleTagCount.value) {\n        moreTag.value = key;\n    } else {\n        moreTag.value = '';\n    }\n};\n\nconst calculateVisibleTagCount = () => {\n    if (!containerRef.value) return;\n\n    const containerWidth = containerRef.value.offsetWidth;\n\n    if (containerWidth >= 1800) {\n        visibleTagCount.value = 18;\n    } else if (containerWidth >= 1400) {\n        visibleTagCount.value = 15;\n    } else if (containerWidth >= 1200) {\n        visibleTagCount.value = 12;\n    } else if (containerWidth >= 992) {\n        visibleTagCount.value = 8;\n    } else if (containerWidth >= 768) {\n        visibleTagCount.value = 6;\n    } else if (containerWidth >= 576) {\n        visibleTagCount.value = 4;\n    } else {\n        visibleTagCount.value = 2;\n    }\n};\n\nlet resizeObserver: ResizeObserver | null = null;\n\nconst initResizeObserver = () => {\n    if (!containerRef.value) return;\n\n    resizeObserver = new ResizeObserver(() => {\n        calculateVisibleTagCount();\n    });\n\n    resizeObserver.observe(containerRef.value);\n};\n\nconst getTags = async () => {\n    await getAppTags().then((res) => {\n        for (let i = 0; i < res.data.length; i++) {\n            if (res.data[i].key === props.hideKey) {\n                res.data.splice(i, 1);\n                break;\n            }\n        }\n        tags.value = res.data;\n        nextTick(() => {\n            calculateVisibleTagCount();\n        });\n    });\n};\n\nonMounted(() => {\n    getTags();\n    nextTick(() => {\n        initResizeObserver();\n    });\n});\n\nonBeforeUnmount(() => {\n    if (resizeObserver) {\n        resizeObserver.disconnect();\n    }\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.el-check-tag.el-check-tag--primary.is-checked {\n    background-color: var(--el-color-info-light-9) !important;\n}\n\n.el-check-tag:hover {\n    background-color: var(--el-color-info-light-9) !important;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/detail/form/index.vue",
    "content": "<template>\n    <div>\n        <el-alert\n            :title=\"$t('app.hostModeHelper')\"\n            class=\"common-prompt\"\n            :closable=\"false\"\n            type=\"warning\"\n            v-if=\"isHostMode\"\n        />\n        <el-alert\n            :title=\"$t('app.memoryRequiredHelper', [computeSizeFromMB(memoryRequired)])\"\n            class=\"common-prompt\"\n            :closable=\"false\"\n            type=\"warning\"\n            v-if=\"memoryRequired > 0\"\n        />\n\n        <el-form\n            v-loading=\"loading\"\n            @submit.prevent\n            ref=\"formRef\"\n            label-position=\"top\"\n            :model=\"formData\"\n            label-width=\"150px\"\n            :rules=\"formRules\"\n            :validate-on-rule-change=\"false\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model.trim=\"formData.name\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('app.version')\" prop=\"version\">\n                <el-select v-model=\"formData.version\" @change=\"handleVersionChange\">\n                    <el-option\n                        v-for=\"(version, index) in appVersions\"\n                        :key=\"index\"\n                        :label=\"version\"\n                        :value=\"version\"\n                    ></el-option>\n                </el-select>\n            </el-form-item>\n\n            <Params\n                :key=\"paramKey\"\n                v-if=\"showParams\"\n                v-model:form=\"formData.params\"\n                v-model:params=\"installParams\"\n                v-model:rules=\"formRules.params\"\n                :propStart=\"'params.'\"\n            />\n\n            <el-form-item prop=\"advanced\">\n                <el-checkbox v-model=\"formData.advanced\" :label=\"$t('app.advanced')\" size=\"large\" />\n            </el-form-item>\n\n            <div v-if=\"formData.advanced\">\n                <el-form-item :label=\"$t('app.containerName')\" prop=\"containerName\">\n                    <el-input v-model.trim=\"formData.containerName\" :placeholder=\"$t('app.containerNameHelper')\" />\n                </el-form-item>\n\n                <el-form-item prop=\"allowPort\" v-if=\"!isHostMode\">\n                    <el-checkbox v-model=\"formData.allowPort\" :label=\"$t('app.allowPort')\" size=\"large\" />\n                    <span class=\"input-help\">{{ $t('app.allowPortHelper') }}</span>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('app.specifyIP')\" v-if=\"formData.allowPort\" prop=\"specifyIP\">\n                    <el-input v-model=\"formData.specifyIP\" />\n                    <span class=\"input-help\">{{ $t('app.specifyIPHelper') }}</span>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('container.restartPolicy')\" prop=\"restartPolicy\">\n                    <el-select v-model=\"formData.restartPolicy\" class=\"p-w-300\">\n                        <el-option :label=\"$t('container.no')\" value=\"no\"></el-option>\n                        <el-option :label=\"$t('container.always')\" value=\"always\"></el-option>\n                        <el-option :label=\"$t('container.onFailure')\" value=\"on-failure\"></el-option>\n                        <el-option :label=\"$t('container.unlessStopped')\" value=\"unless-stopped\"></el-option>\n                    </el-select>\n                </el-form-item>\n\n                <el-form-item\n                    :label=\"$t('container.cpuQuota')\"\n                    prop=\"cpuQuota\"\n                    :rules=\"checkNumberRange(0, limits.cpu)\"\n                >\n                    <el-input type=\"number\" class=\"!w-2/5\" v-model.number=\"formData.cpuQuota\" maxlength=\"5\">\n                        <template #append>{{ $t('app.cpuCore') }}</template>\n                    </el-input>\n                    <span class=\"input-help\">\n                        {{ $t('container.limitHelper', [limits.cpu]) }}{{ $t('commons.units.core') }}\n                    </span>\n                </el-form-item>\n\n                <el-form-item\n                    :label=\"$t('container.memoryLimit')\"\n                    prop=\"memoryLimit\"\n                    :rules=\"checkNumberRange(0, limits.memory)\"\n                >\n                    <el-input class=\"!w-2/5\" v-model.number=\"formData.memoryLimit\" maxlength=\"10\">\n                        <template #append>\n                            <el-select\n                                v-model=\"formData.memoryUnit\"\n                                placeholder=\"Select\"\n                                class=\"p-w-100\"\n                                @change=\"changeUnit\"\n                            >\n                                <el-option label=\"MB\" value=\"M\" />\n                                <el-option label=\"GB\" value=\"G\" />\n                            </el-select>\n                        </template>\n                    </el-input>\n                    <span class=\"input-help\">\n                        {{ $t('container.limitHelper', [limits.memory]) }}{{ formData.memoryUnit }}B\n                    </span>\n                </el-form-item>\n\n                <el-form-item pro=\"gpuConfig\" v-if=\"gpuSupport\">\n                    <el-checkbox v-model=\"formData.gpuConfig\" :label=\"$t('app.gpuConfig')\" size=\"large\" />\n                    <span class=\"input-help\">{{ $t('app.gpuConfigHelper') }}</span>\n                </el-form-item>\n\n                <el-form-item pro=\"pullImage\">\n                    <el-checkbox v-model=\"formData.pullImage\" :label=\"$t('app.pullImage')\" size=\"large\" />\n                    <span class=\"input-help\">{{ $t('app.pullImageHelper') }}</span>\n                </el-form-item>\n\n                <PushtoNode\n                    v-if=\"isMaster && isMasterProductPro && batchInstallSupport\"\n                    :push-node=\"formData.pushNode\"\n                    :nodes=\"formData.nodes\"\n                    type=\"app\"\n                    @update:push-node=\"formData.pushNode = $event\"\n                    @update:nodes=\"formData.nodes = $event\"\n                />\n\n                <el-form-item prop=\"editCompose\">\n                    <el-checkbox v-model=\"formData.editCompose\" :label=\"$t('app.editCompose')\" size=\"large\" />\n                    <span class=\"input-help\">{{ $t('app.editComposeHelper') }}</span>\n                </el-form-item>\n\n                <div v-if=\"formData.editCompose\">\n                    <CodemirrorPro v-model=\"formData.dockerCompose\" mode=\"yaml\" />\n                </div>\n            </div>\n        </el-form>\n    </div>\n</template>\n\n<script lang=\"ts\" setup name=\"AppInstallForm\">\nimport { App } from '@/api/interface/app';\nimport { getAppByKey, getAppDetail, getAppInstalledByID } from '@/api/modules/app';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { ref, watch } from 'vue';\nimport Params from '../params/index.vue';\nimport { Container } from '@/api/interface/container';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport { computeSizeFromMB } from '@/utils/util';\nimport { loadResourceLimit } from '@/api/modules/container';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { isOffLine, isMasterProductPro, isMaster } = useGlobalStore();\n\nconst PushtoNode = defineAsyncComponent(async () => {\n    const modules = import.meta.glob('@/xpack/views/ssl/index.vue');\n    const loader = modules['/src/xpack/views/ssl/index.vue'];\n    if (loader) {\n        return ((await loader()) as any).default;\n    }\n    return { template: '<div></div>' };\n});\n\ninterface ClusterProps {\n    key: string;\n    node: string;\n    masterNode: string;\n    appInstallID: number;\n    masterVersion: string;\n    masterNodeAddr: string;\n    role: string;\n}\ninterface Props {\n    loading?: boolean;\n    modelValue?: any;\n    batchInstallSupport?: boolean;\n}\n\nconst limits = ref<Container.ResourceLimit>({\n    cpu: null as number,\n    memory: null as number,\n});\n\nconst props = withDefaults(defineProps<Props>(), {\n    loading: false,\n    batchInstallSupport: false,\n});\n\ninterface Emits {\n    (e: 'update:modelValue', value: any): void;\n}\n\nconst emit = defineEmits<Emits>();\n\nconst formRef = ref<FormInstance>();\nconst paramKey = ref(1);\nconst isHostMode = ref(false);\nconst memoryRequired = ref(0);\nconst gpuSupport = ref(false);\nconst installParams = ref<App.AppParams>();\nconst oldMemory = ref<number>(0);\nconst showParams = ref(false);\nconst currentApp = ref<any>({});\nconst appVersions = ref<string[]>([]);\nconst operateNode = ref();\nconst env = ref();\nconst masterNodeAddr = ref();\n\nconst formRules = ref<FormRules>({\n    name: [Rules.appName],\n    params: [],\n    version: [Rules.requiredSelect],\n    containerName: [Rules.containerName],\n    cpuQuota: [Rules.requiredInput, checkNumberRange(0, 99999)],\n    memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],\n    specifyIP: [Rules.ipv4orV6],\n    restartPolicy: [Rules.requiredSelect],\n    format: [Rules.requiredInput],\n    nodes: [Rules.requiredSelect],\n});\n\nconst initFormData = () => ({\n    appDetailId: 0,\n    params: {},\n    name: '',\n    advanced: true,\n    cpuQuota: 0,\n    memoryLimit: 0,\n    memoryUnit: 'M',\n    containerName: '',\n    allowPort: false,\n    editCompose: false,\n    dockerCompose: '',\n    version: '',\n    appID: '',\n    pullImage: true,\n    taskID: '',\n    gpuConfig: false,\n    specifyIP: '',\n    restartPolicy: 'always',\n    pushNode: false,\n    nodes: [],\n});\n\nconst formData = ref(props.modelValue || initFormData());\n\nwatch(\n    formData.value,\n    (newVal) => {\n        emit('update:modelValue', newVal);\n    },\n    { deep: true },\n);\n\nwatch(\n    () => props.modelValue,\n    (newVal) => {\n        if (newVal) {\n            Object.assign(formData.value, newVal);\n        }\n    },\n    { immediate: true, deep: true },\n);\n\nconst changeUnit = () => {\n    if (formData.value.memoryUnit == 'M') {\n        limits.value.memory = oldMemory.value;\n    } else {\n        limits.value.memory = Number((oldMemory.value / 1024).toFixed(2));\n    }\n};\n\nconst handleVersionChange = async (version: string) => {\n    await getVersionDetail(version);\n};\n\nconst getVersionDetail = async (version: string) => {\n    try {\n        const res = await getAppDetail(currentApp.value.id, version, 'app', operateNode.value);\n        formData.value.appDetailId = res.data.id;\n        formData.value.dockerCompose = res.data.dockerCompose;\n        isHostMode.value = res.data.hostMode;\n        if (env.value) {\n            installParams.value = addMasterParams(res.data.params);\n        } else {\n            installParams.value = res.data.params;\n        }\n        paramKey.value++;\n        memoryRequired.value = res.data.memoryRequired;\n        gpuSupport.value = res.data.gpuSupport;\n        showParams.value = true;\n    } catch (error) {}\n};\n\nconst initForm = async (appKey: string) => {\n    formData.value.name = appKey.replace(/^local/, '');\n    const res = await getAppByKey(appKey);\n    currentApp.value = res.data;\n    appVersions.value = currentApp.value.versions;\n    if (appVersions.value.length > 0) {\n        const defaultVersion = appVersions.value[0];\n        formData.value.version = defaultVersion;\n        getVersionDetail(defaultVersion);\n    }\n    if (isOffLine.value) {\n        formData.value.pullImage = false;\n    }\n};\n\nconst getMasterAppInstall = async (appInstallID: number, masterNode: string) => {\n    try {\n        const res = await getAppInstalledByID(appInstallID, masterNode);\n        env.value = res.data.env;\n    } catch (error) {}\n};\n\nconst addMasterParams = (appParams: App.AppParams) => {\n    for (const key in appParams.formFields) {\n        const field = appParams.formFields[key];\n        if (field?.envKey === 'MASTER_ROOT_PASSWORD') {\n            field.default = env.value['PANEL_DB_ROOT_PASSWORD']\n                ? env.value['PANEL_DB_ROOT_PASSWORD']\n                : env.value['PANEL_REDIS_ROOT_PASSWORD'];\n        }\n        if (field?.envKey === 'PANEL_DB_ROOT_PASSWORD') {\n            field.default = env.value['PANEL_DB_ROOT_PASSWORD'];\n        }\n        if (field?.envKey === 'REPLICATION_USER') {\n            field.default = env.value['REPLICATION_USER'];\n        }\n        if (field?.envKey === 'REPLICATION_PASSWORD') {\n            field.default = env.value['REPLICATION_PASSWORD'];\n        }\n        if (field?.envKey === 'MASTER_PORT') {\n            field.default = env.value['PANEL_APP_PORT_HTTP'];\n        }\n        if (field?.envKey === 'MASTER_HOST' && masterNodeAddr.value != '127.0.0.1') {\n            field.default = masterNodeAddr.value;\n        }\n    }\n    return appParams;\n};\n\nconst initClusterForm = async (props: ClusterProps) => {\n    if (props.appInstallID && props.masterNode) {\n        getMasterAppInstall(props.appInstallID, props.masterNode);\n    }\n    masterNodeAddr.value = props.masterNodeAddr;\n    operateNode.value = props.node;\n    const res = await getAppByKey(props.key, props.node);\n    currentApp.value = res.data;\n    appVersions.value = currentApp.value.versions;\n    if (appVersions.value.length > 0) {\n        appVersions.value = appVersions.value.filter((v: string) => {\n            return v.includes(props.role) && v.includes(props.masterVersion);\n        });\n        const defaultVersion = appVersions.value[0];\n        formData.value.version = defaultVersion;\n        getVersionDetail(defaultVersion);\n    }\n    formData.value.name = props.key + '-' + props.role;\n};\n\nconst resetForm = () => {\n    if (formRef.value) {\n        formRef.value.clearValidate();\n        formRef.value.resetFields();\n    }\n    Object.assign(formData.value, initFormData());\n    isHostMode.value = false;\n    memoryRequired.value = 0;\n    gpuSupport.value = false;\n    showParams.value = false;\n};\n\nconst validate = async (): Promise<boolean> => {\n    if (!formRef.value) return false;\n    try {\n        const isValid = await formRef.value.validate();\n        return isValid;\n    } catch (error) {\n        return false;\n    }\n};\n\nconst clearValidate = () => {\n    if (formRef.value) {\n        formRef.value.clearValidate();\n    }\n};\n\nconst getFormData = () => {\n    return { ...formData.value };\n};\n\nconst setFormData = (data: any) => {\n    Object.assign(formData.value, data);\n};\n\nconst loadLimit = async () => {\n    const res = await loadResourceLimit();\n    limits.value = res.data;\n    limits.value.memory = Number((limits.value.memory / 1024 / 1024).toFixed(2));\n    oldMemory.value = limits.value.memory;\n};\n\nonMounted(() => {\n    loadLimit();\n});\n\ndefineExpose({\n    formRef,\n    formData,\n    initForm,\n    resetForm,\n    validate,\n    clearValidate,\n    getFormData,\n    setFormData,\n    isHostMode: () => isHostMode.value,\n    initClusterForm,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/app-store/detail/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('app.detail')\" @close=\"handleClose\" size=\"large\">\n        <template #content>\n            <div class=\"brief\" v-loading=\"loadingApp\">\n                <div class=\"detail flex\">\n                    <div class=\"w-12 h-12 rounded p-1 shadow-md icon\">\n                        <img\n                            :src=\"getAppIconUrl(app.key, currentNode)\"\n                            alt=\"App Icon\"\n                            class=\"w-full h-full rounded\"\n                            style=\"object-fit: contain\"\n                        />\n                    </div>\n                    <div class=\"ml-4\">\n                        <div class=\"name mb-2\">\n                            <span>{{ app.name }}</span>\n                        </div>\n                        <div class=\"description mb-4\">\n                            <span>\n                                {{ app.description }}\n                            </span>\n                        </div>\n                        <br />\n                        <div v-if=\"!loadingDetail\" class=\"mb-2\">\n                            <el-alert\n                                v-if=\"!appDetail.enable\"\n                                :title=\"$t('app.limitHelper')\"\n                                type=\"warning\"\n                                show-icon\n                                :closable=\"false\"\n                            />\n                        </div>\n                        <el-button\n                            round\n                            v-if=\"appDetail.enable && operate === 'install'\"\n                            @click=\"openInstall\"\n                            type=\"primary\"\n                            class=\"brief-button\"\n                        >\n                            {{ $t('commons.button.install') }}\n                        </el-button>\n                    </div>\n                </div>\n                <div class=\"descriptions\">\n                    <el-descriptions border size=\"large\" direction=\"vertical\">\n                        <el-descriptions-item :label=\"$t('app.appOfficeWebsite')\">\n                            <el-link @click=\"toLink(app.website)\">\n                                {{ $t('app.link') }}\n                                <el-icon class=\"ml-1.5\"><Promotion /></el-icon>\n                            </el-link>\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('app.github')\">\n                            <el-link @click=\"toLink(app.github)\">\n                                {{ $t('app.link') }}\n                                <el-icon class=\"ml-1.5\"><Promotion /></el-icon>\n                            </el-link>\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('app.requireMemory')\" v-if=\"appDetail.memoryRequired > 0\">\n                            <span>{{ computeSizeFromMB(appDetail.memoryRequired) }}</span>\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('app.supportedArchitectures')\" v-if=\"architectures.length > 0\">\n                            <el-tag v-for=\"(arch, index) in architectures\" :key=\"index\" class=\"mx-1\">\n                                {{ arch }}\n                            </el-tag>\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </div>\n            </div>\n            <MarkDownEditor :content=\"app.readMe\" />\n        </template>\n    </DrawerPro>\n    <Install ref=\"installRef\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport MarkDownEditor from '@/components/mkdown-editor/index.vue';\n\nimport { getAppByKey, getAppDetail, getAppIconUrl } from '@/api/modules/app';\nimport { ref } from 'vue';\nimport Install from './install/index.vue';\nimport { computeSizeFromMB } from '@/utils/util';\nimport { jumpToInstall } from '@/utils/app';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { currentNode } = useGlobalStore();\n\nconst app = ref<any>({});\nconst appDetail = ref<any>({});\nconst version = ref('');\nconst loadingDetail = ref(false);\nconst loadingApp = ref(false);\nconst installRef = ref();\nconst open = ref(false);\nconst appKey = ref();\nconst operate = ref();\nconst architectures = ref([]);\n\nconst acceptParams = async (key: string, op: string) => {\n    appKey.value = key;\n    operate.value = op;\n    open.value = true;\n    getApp();\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst getApp = async () => {\n    loadingApp.value = true;\n    try {\n        const res = await getAppByKey(appKey.value);\n        app.value = res.data;\n        version.value = app.value.versions[0];\n        getDetail(app.value.id, version.value);\n    } finally {\n        loadingApp.value = false;\n    }\n};\n\nconst getDetail = async (id: number, version: string) => {\n    loadingDetail.value = true;\n    try {\n        const res = await getAppDetail(id, version, 'app');\n        appDetail.value = res.data;\n        if (appDetail.value.architectures != '') {\n            architectures.value = appDetail.value.architectures.split(',');\n        }\n    } finally {\n        loadingDetail.value = false;\n    }\n};\n\nconst toLink = (link: string) => {\n    window.open(link, '_blank');\n};\n\nconst openInstall = () => {\n    if (!jumpToInstall(app.value.type, app.value.key)) {\n        const params = {\n            app: app.value,\n        };\n        installRef.value.acceptParams(params);\n        open.value = false;\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.brief {\n    .name {\n        span {\n            font-weight: 500;\n            font-size: 18px;\n            color: var(--el-text-color-regular);\n        }\n    }\n\n    .description {\n        margin-top: 10px;\n        span {\n            font-size: 14px;\n            color: var(--el-text-color-regular);\n        }\n    }\n\n    .icon {\n        flex-shrink: 0;\n        width: 180px;\n        height: 180px;\n        background-color: #ffffff;\n    }\n\n    .version {\n        margin-top: 10px;\n    }\n\n    .descriptions {\n        margin-top: 20px;\n    }\n}\n:deep(.md-editor-dark) {\n    background-color: var(--panel-main-bg-color-9);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/detail/install/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.install')\" @close=\"handleClose\" size=\"large\">\n        <AppInstallForm\n            ref=\"installFormRef\"\n            v-model=\"formData\"\n            :loading=\"loading\"\n            :batch-install-support=\"batchInstallSupport\"\n        />\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"handleSubmit\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" />\n</template>\n\n<script lang=\"ts\" setup name=\"AppInstallPage\">\nimport { useRouter } from 'vue-router';\nimport AppInstallForm from '@/views/app-store/detail/form/index.vue';\nimport { installApp } from '@/api/modules/app';\nimport { MsgError } from '@/utils/message';\nimport { newUUID } from '@/utils/util';\nimport { routerToName } from '@/utils/router';\nimport TaskLog from '@/components/log/task/index.vue';\nimport i18n from '@/lang';\nimport { installAppToNodes } from '@/api/modules/app';\n\nconst router = useRouter();\nconst open = ref(false);\nconst loading = ref(false);\nconst installFormRef = ref<InstanceType<typeof AppInstallForm>>();\nconst taskLogRef = ref();\nconst appKey = ref('');\nconst batchInstallSupport = ref(false);\n\nconst formData = ref({\n    appDetailId: 0,\n    params: {},\n    name: '',\n    advanced: true,\n    cpuQuota: 0,\n    memoryLimit: 0,\n    memoryUnit: 'M',\n    containerName: '',\n    allowPort: false,\n    editCompose: false,\n    dockerCompose: '',\n    version: '',\n    appID: '',\n    pullImage: true,\n    taskID: '',\n    gpuConfig: false,\n    specifyIP: '',\n    format: 'utf8mb4',\n    collation: '',\n});\n\nconst handleClose = () => {\n    open.value = false;\n    installFormRef.value?.resetForm();\n    if (router.currentRoute.value.query.install) {\n        routerToName('AppAll');\n    }\n};\n\nconst handleSubmit = async () => {\n    const isValid = await installFormRef.value?.validate();\n    if (!isValid) return;\n\n    const submitData = installFormRef.value?.getFormData();\n\n    if (submitData.editCompose && submitData.dockerCompose === '') {\n        MsgError(i18n.global.t('app.composeNullErr'));\n        return;\n    }\n\n    if (submitData.cpuQuota < 0) {\n        submitData.cpuQuota = 0;\n    }\n    if (submitData.memoryLimit < 0) {\n        submitData.memoryLimit = 0;\n    }\n\n    const isHostMode = installFormRef.value?.isHostMode();\n    if (!isHostMode && !submitData.allowPort) {\n        ElMessageBox.confirm(i18n.global.t('app.installWarn'), i18n.global.t('app.checkTitle'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        }).then(async () => {\n            await install(submitData);\n        });\n    } else {\n        await install(submitData);\n    }\n};\n\nconst install = async (submitData: any) => {\n    loading.value = true;\n    const taskID = newUUID();\n    submitData.taskID = taskID;\n\n    try {\n        if (submitData.pushNode) {\n            submitData.appKey = appKey.value;\n            await installAppToNodes(submitData);\n        } else {\n            await installApp(submitData);\n        }\n        handleClose();\n        openTaskLog(taskID);\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst acceptParams = async (props: { app: any; params?: any }) => {\n    appKey.value = props.app.key;\n    batchInstallSupport.value = props.app.batchInstallSupport;\n    open.value = true;\n    await nextTick();\n    installFormRef.value?.resetForm();\n    installFormRef.value?.initForm(props.app.key);\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/app-store/detail/params/index.vue",
    "content": "<template>\n    <div v-for=\"(p, index) in paramObjs\" :key=\"index\">\n        <el-form-item :label=\"getLabel(p)\" :prop=\"p.prop\">\n            <el-input\n                v-model.trim=\"form[p.envKey]\"\n                v-if=\"p.type == 'text'\"\n                :type=\"p.type\"\n                @change=\"updateParam\"\n                :disabled=\"p.disabled\"\n            ></el-input>\n            <el-input\n                v-model.number=\"form[p.envKey]\"\n                @blur=\"form[p.envKey] = Number(form[p.envKey])\"\n                v-if=\"p.type == 'number'\"\n                maxlength=\"15\"\n                @change=\"updateParam\"\n                :disabled=\"p.disabled\"\n            ></el-input>\n            <el-input\n                v-model.trim=\"form[p.envKey]\"\n                v-if=\"p.type == 'password'\"\n                :type=\"p.type\"\n                show-password\n                clearable\n                @change=\"updateParam\"\n            ></el-input>\n            <el-select\n                class=\"p-w-200\"\n                v-model=\"form[p.envKey]\"\n                v-if=\"p.type == 'service'\"\n                @change=\"changeService(form[p.envKey], p.services)\"\n            >\n                <el-option\n                    v-for=\"service in p.services\"\n                    :key=\"service.label\"\n                    :value=\"service.value\"\n                    :label=\"service.label\"\n                ></el-option>\n            </el-select>\n            <span v-if=\"p.type === 'service' && p.services.length === 0\" class=\"ml-1.5\">\n                <el-link type=\"primary\" underline=\"never\" @click=\"toPage(p.key)\">\n                    {{ $t('app.toInstall') }}\n                </el-link>\n            </span>\n            <el-select\n                v-model=\"form[p.envKey]\"\n                v-if=\"p.type == 'select'\"\n                :multiple=\"p.multiple\"\n                :allowCreate=\"p.allowCreate\"\n                filterable\n            >\n                <el-option\n                    v-for=\"service in p.values\"\n                    :key=\"service.label\"\n                    :value=\"service.value\"\n                    :label=\"service.label\"\n                ></el-option>\n            </el-select>\n            <div v-if=\"p.type == 'apps'\" class=\"flex space-x-4\">\n                <div class=\"flex-1\">\n                    <el-form-item :prop=\"p.prop\">\n                        <el-select\n                            v-model=\"form[p.envKey]\"\n                            @change=\"getServices(p.child.envKey, form[p.envKey], p)\"\n                            class=\"p-w-200\"\n                        >\n                            <el-option\n                                v-for=\"service in p.values\"\n                                :label=\"service.label\"\n                                :key=\"service.value\"\n                                :value=\"service.value\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                </div>\n                <div class=\"flex-2\">\n                    <el-form-item :prop=\"p.childProp\">\n                        <el-select\n                            v-model=\"form[p.child.envKey]\"\n                            v-if=\"p.child.type == 'service'\"\n                            @change=\"changeService(form[p.child.envKey], p.services)\"\n                            class=\"p-w-300\"\n                        >\n                            <el-option\n                                v-for=\"service in p.services\"\n                                :key=\"service.label\"\n                                :value=\"service.value\"\n                                :label=\"service.label\"\n                                :disabled=\"service.status != 'Running'\"\n                            >\n                                <el-row :gutter=\"5\">\n                                    <el-col :span=\"14\">\n                                        <span>{{ service.label }}</span>\n                                    </el-col>\n                                    <el-col :span=\"6\">\n                                        <span v-if=\"service.from != ''\">\n                                            <el-tag v-if=\"service.from === 'local'\">\n                                                {{ $t('commons.table.local') }}\n                                            </el-tag>\n                                            <el-tag v-else type=\"success\">{{ $t('database.remote') }}</el-tag>\n                                            <Status\n                                                class=\"ml-2\"\n                                                :key=\"service.status\"\n                                                :status=\"service.status\"\n                                            ></Status>\n                                        </span>\n                                    </el-col>\n                                </el-row>\n                            </el-option>\n                        </el-select>\n                    </el-form-item>\n                </div>\n                <span v-if=\"p.child.type === 'service' && p.services.length === 0\">\n                    <el-link type=\"primary\" underline=\"never\" @click=\"toPage(form[p.envKey])\">\n                        {{ $t('app.toInstall') }}\n                    </el-link>\n                </span>\n            </div>\n            <span class=\"input-help\" v-if=\"p.description\">{{ getDescription(p) }}</span>\n        </el-form-item>\n        <el-form-item v-if=\"isMysql(form, p.envKey)\" :label=\"$t('database.format')\" prop=\"format\">\n            <el-select filterable v-model=\"form.format\" @change=\"loadCollations()\">\n                <el-option v-for=\"item of formatOptions\" :key=\"item.format\" :label=\"item.format\" :value=\"item.format\" />\n            </el-select>\n        </el-form-item>\n        <el-form-item v-if=\"isMysql(form, p.envKey)\" :label=\"$t('database.collation')\" prop=\"collation\">\n            <el-select filterable v-model=\"form.collation\">\n                <el-option v-for=\"item of collationOptions\" :key=\"item\" :label=\"item\" :value=\"item\" />\n            </el-select>\n            <span class=\"input-help\">{{ $t('database.collationHelper', [form.format]) }}</span>\n        </el-form-item>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport { getRandomStr } from '@/utils/util';\nimport { getAppService } from '@/api/modules/app';\nimport { Rules } from '@/global/form-rules';\nimport { App } from '@/api/interface/app';\nimport { getDBName, getLabel, getDescription } from '@/utils/util';\nimport { getPathByType } from '@/api/modules/files';\nimport { loadFormatCollations } from '@/api/modules/database';\n\ninterface ParamObj extends App.FromField {\n    services: App.AppService[];\n    prop: string;\n    disabled: false;\n    childProp: string;\n}\n\nconst emit = defineEmits(['update:form', 'update:rules']);\n\nconst props = defineProps({\n    form: {\n        type: Object,\n        default: function () {\n            return {};\n        },\n    },\n    params: {\n        type: Object,\n        default: function () {\n            return {};\n        },\n    },\n    rules: {\n        type: Object,\n        default: function () {\n            return {};\n        },\n    },\n    propStart: {\n        type: String,\n        default: '',\n    },\n});\n\nconst form = reactive({\n    format: '',\n    collation: '',\n});\nlet rules = reactive({});\nconst params = computed({\n    get() {\n        return props.params;\n    },\n    set() {},\n});\nconst propStart = computed({\n    get() {\n        return props.propStart;\n    },\n    set() {},\n});\nconst paramObjs = ref<ParamObj[]>([]);\n\nconst updateParam = () => {\n    emit('update:form', form);\n};\n\nconst isMysql = (form: Object, envKey: string) => {\n    return form['PANEL_DB_HOST'] != undefined && (form[envKey] == 'mysql' || form[envKey] == 'mariadb');\n};\n\nconst handleParams = () => {\n    rules = props.rules;\n    if (params.value != undefined && params.value.formFields != undefined) {\n        for (const p of params.value.formFields) {\n            const pObj = p;\n            pObj.prop = propStart.value + p.envKey;\n            pObj.disabled = p.disabled;\n            paramObjs.value.push(pObj);\n            if (p.random) {\n                if (p.envKey === 'PANEL_DB_NAME') {\n                    form[p.envKey] = p.default + '_' + getDBName(6);\n                } else {\n                    form[p.envKey] = p.default + '_' + getRandomStr(6);\n                }\n            } else {\n                form[p.envKey] = p.default;\n            }\n            if (p.type == 'text' && p.envKey == 'WEBSITE_DIR') {\n                getPathByType('websiteDir').then((res) => {\n                    form[p.envKey] = res.data;\n                });\n            }\n            if (p.required) {\n                if (p.type === 'service' || p.type === 'apps') {\n                    rules[p.envKey] = [Rules.requiredSelect];\n                    if (p.child) {\n                        p.childProp = propStart.value + p.child.envKey;\n                        if (p.child.type === 'service') {\n                            rules[p.child.envKey] = [Rules.requiredSelect];\n                        }\n                    }\n                } else {\n                    rules[p.envKey] = [Rules.requiredInput];\n                }\n                if (p.rule && p.rule != '') {\n                    rules[p.envKey].push(Rules[p.rule]);\n                }\n            } else {\n                delete rules[p.envKey];\n            }\n            if (p.type === 'apps') {\n                getServices(p.child.envKey, p.default, p);\n                p.child.services = [];\n                form[p.child.envKey] = '';\n            }\n            if (p.type === 'service') {\n                getServices(p.envKey, p.key, p);\n                p.services = [];\n                form[p.envKey] = '';\n            }\n            emit('update:rules', rules);\n            updateParam();\n        }\n    }\n};\n\nconst getServices = async (childKey: string, key: string | undefined, pObj: ParamObj | undefined) => {\n    pObj.services = [];\n    appKey.value = key || '';\n    if (appKey.value == 'mysql' || appKey.value == 'mariadb') {\n        form.format = 'utf8mb4';\n    }\n    await getAppService(key).then((res) => {\n        pObj.services = res.data || [];\n        form[childKey] = '';\n        if (res.data && res.data.length > 0) {\n            form[childKey] = res.data[0].value;\n            if (pObj.params) {\n                pObj.params.forEach((param: App.FromParam) => {\n                    if (param.key === key) {\n                        form[param.envKey] = param.value;\n                    }\n                });\n            }\n            changeService(form[childKey], pObj.services);\n        }\n    });\n};\n\nconst changeService = (value: string, services: App.AppService[]) => {\n    services.forEach((item) => {\n        if (item.value === value && item.config) {\n            Object.entries(item.config).forEach(([k, v]) => {\n                if (form.hasOwnProperty(k)) {\n                    form[k] = v;\n                }\n            });\n        }\n    });\n    if (appKey.value == 'mysql' || appKey.value == 'mariadb') {\n        loadOptions(value);\n    }\n    updateParam();\n};\n\nconst toPage = (key: string) => {\n    window.location.href = '/apps/all?install=' + key;\n};\n\nconst formatOptions = ref();\nconst collationOptions = ref();\nconst appKey = ref('');\n\nconst loadOptions = async (database: string) => {\n    const defaultOptions = [{ format: 'utf8mb4' }, { format: 'utf8mb3' }, { format: 'gbk' }, { format: 'big5' }];\n    await loadFormatCollations(database).then((res) => {\n        formatOptions.value = res.data || defaultOptions;\n        loadCollations();\n    });\n};\n\nconst loadCollations = async () => {\n    collationOptions.value = formatOptions.value?.find((item) => item.format === form.format)?.collations || [];\n};\n\nonMounted(() => {\n    handleParams();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/app-store/index.scss",
    "content": ".sortable-ghost {\n    opacity: 0.4;\n}\n\n.install-card {\n    margin-top: 10px;\n    cursor: pointer;\n    padding: 10px;\n\n    .icon {\n        text-align: center;\n    }\n\n    .a-detail {\n        .d-name {\n            .d-name-row {\n                gap: 8px;\n                min-width: 0;\n            }\n\n            .name-actions {\n                flex: 1;\n                min-width: 0;\n                flex-wrap: nowrap;\n                overflow: hidden;\n            }\n\n            .operate-actions {\n                flex-shrink: 0;\n            }\n\n            .name-wrap {\n                min-width: 0;\n                max-width: clamp(140px, 22vw, 320px);\n                flex-shrink: 1;\n                margin-right: 4px;\n                overflow: hidden;\n            }\n\n            .name {\n                display: block;\n                white-space: nowrap;\n                overflow: hidden;\n                text-overflow: ellipsis;\n                font-weight: 500;\n                font-size: 18px;\n                color: var(--panel-text-color);\n                max-width: 100%;\n            }\n\n            .status {\n                margin-left: 5px;\n            }\n\n            .h-button {\n                float: right;\n            }\n\n            .msg {\n                margin-left: 5px;\n            }\n        }\n\n        .d-description {\n            margin-top: 10px;\n            overflow: hidden;\n\n            .el-tag {\n                margin-right: 5px;\n            }\n        }\n\n        .description {\n            margin-top: 10px;\n            font-size: 14px;\n            color: var(--el-text-color-regular);\n        }\n\n        .d-button {\n            margin-top: 10px;\n            min-width: 330px;\n        }\n    }\n\n    .e-card {\n        border: var(--panel-border) !important;\n\n        &:hover {\n            cursor: pointer;\n            border: 1px solid var(--el-color-primary) !important;\n        }\n    }\n}\n\n.table-button {\n    display: inline;\n    margin-right: 5px;\n}\n\n.app-divider {\n    margin-top: 5px;\n    border: 0;\n    border-top: var(--panel-border);\n}\n\n.update-prompt {\n    text-align: center;\n    margin-top: 100px;\n\n    span {\n        color: #bbbfc4;\n    }\n\n    img {\n        width: 300px;\n        height: 300px;\n    }\n}\n\n.tag-button {\n    &.no-active {\n        background: none;\n        border: none;\n    }\n}\n\n.page-button {\n    float: right;\n    margin-bottom: 10px;\n    margin-top: 10px;\n}\n\n@media (max-width: 575px) {\n    .install-card {\n        .a-detail {\n            .d-name {\n                .h-button {\n                    float: none;\n                    display: inline-block;\n                    margin-left: 5px;\n                }\n            }\n\n            .d-button {\n                min-width: auto;\n\n                .app-button {\n                    font-size: 12px;\n                    padding: 5px 10px;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "frontend/src/views/app-store/index.vue",
    "content": "<template>\n    <div>\n        <div v-if=\"showButton\">\n            <RouterButton :buttons=\"buttons\"></RouterButton>\n        </div>\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { onMounted, ref } from 'vue';\nimport { searchAppInstalled } from '@/api/modules/app';\nimport bus from '@/global/bus';\nlet showButton = ref(false);\n\nlet buttons = [\n    {\n        label: i18n.global.t('app.all'),\n        path: '/apps/all',\n    },\n    {\n        label: i18n.global.t('app.installed'),\n        path: '/apps/installed',\n    },\n    {\n        label: i18n.global.t('app.canUpgrade'),\n        path: '/apps/upgrade',\n        count: 0,\n    },\n    {\n        label: i18n.global.t('commons.button.set'),\n        path: '/apps/setting',\n    },\n];\n\nconst search = () => {\n    searchAppInstalled({ update: true, page: 1, pageSize: 100 })\n        .then((res) => {\n            if (res.data.items) {\n                buttons[2].count = res.data.items.length;\n            } else {\n                buttons[2].count = 0;\n            }\n        })\n        .finally(() => {\n            showButton.value = true;\n        });\n};\n\nonMounted(() => {\n    search();\n    bus.on('upgrade', () => {\n        showButton.value = false;\n        search();\n    });\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/app/card.vue",
    "content": "<template>\n    <div class=\"install-card\">\n        <el-card class=\"e-card\">\n            <el-row :gutter=\"10\">\n                <el-col :xs=\"3\" :sm=\"3\" :md=\"3\" :lg=\"4\" :xl=\"3\">\n                    <AppIcon\n                        @open-detail=\"$emit('openDetail')\"\n                        :appKey=\"installed.appKey\"\n                        :currentNode=\"currentNode\"\n                    ></AppIcon>\n                </el-col>\n                <el-col :xs=\"21\" :sm=\"21\" :md=\"21\" :lg=\"20\" :xl=\"21\">\n                    <div class=\"a-detail\">\n                        <AppHeader\n                            :installed=\"installed\"\n                            :mode=\"mode\"\n                            :defaultLink=\"defaultLink\"\n                            :sortMode=\"sortMode\"\n                            @open-backups=\"$emit('openBackups')\"\n                            @open-log=\"$emit('openLog')\"\n                            @open-terminal=\"$emit('openTerminal')\"\n                            @open-operate=\"$emit('openOperate')\"\n                            @favorite-install=\"$emit('favoriteInstall')\"\n                            @to-folder=\"$emit('toFolder')\"\n                            @open-uploads=\"$emit('openUploads')\"\n                            @ignore-app=\"$emit('ignoreApp')\"\n                            @to-container=\"$emit('toContainer')\"\n                        ></AppHeader>\n                        <AppInfo\n                            :installed=\"installed\"\n                            :defaultLink=\"defaultLink\"\n                            @jump-to-path=\"$emit('jumpToPath', '/settings/panel')\"\n                        ></AppInfo>\n                        <div class=\"app-divider\" />\n                        <slot name=\"buttons\"></slot>\n                    </div>\n                </el-col>\n            </el-row>\n        </el-card>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport AppIcon from '@/views/app-store/installed/app/icon.vue';\nimport AppHeader from '@/views/app-store/installed/app/header.vue';\nimport AppInfo from '@/views/app-store/installed/app/info.vue';\n\nimport { App } from '@/api/interface/app';\n\ninterface Props {\n    installed: App.AppInstalled;\n    mode: string;\n    defaultLink: string;\n    currentNode: string;\n    sortMode?: boolean;\n}\ndefineProps<Props>();\n\ndefineEmits([\n    'toFolder',\n    'openUploads',\n    'openDetail',\n    'openBackups',\n    'openLog',\n    'openTerminal',\n    'openOperate',\n    'favoriteInstall',\n    'jumpToPath',\n    'ignoreApp',\n    'toContainer',\n]);\n</script>\n\n<style scoped lang=\"scss\">\n@use '@/views/app-store/index.scss';\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/app/header.vue",
    "content": "<template>\n    <div class=\"d-name\">\n        <div class=\"d-name-row flex items-center justify-between\">\n            <div class=\"name-actions flex items-center justify-start gap-1\">\n                <div class=\"name-wrap\">\n                    <el-tooltip :content=\"installed.name\" placement=\"top\">\n                        <span class=\"name\">{{ installed.name }}</span>\n                    </el-tooltip>\n                </div>\n                <span class=\"status\">\n                    <Status :key=\"installed.status\" :status=\"installed.status\"></Status>\n                </span>\n                <span class=\"msg\">\n                    <el-popover\n                        v-if=\"isAppErr(installed)\"\n                        placement=\"bottom\"\n                        :width=\"400\"\n                        trigger=\"hover\"\n                        :content=\"installed.message\"\n                        :popper-options=\"options\"\n                    >\n                        <template #reference>\n                            <el-button link type=\"danger\">\n                                <el-icon><Warning /></el-icon>\n                            </el-button>\n                        </template>\n                        <div class=\"app-error\">\n                            {{ installed.message }}\n                        </div>\n                    </el-popover>\n                </span>\n                <span class=\"ml-1\">\n                    <el-tooltip effect=\"dark\" :content=\"$t('app.toFolder')\" placement=\"top\">\n                        <el-button type=\"primary\" link @click=\"$emit('toFolder')\" icon=\"FolderOpened\"></el-button>\n                    </el-tooltip>\n                </span>\n                <span class=\"ml-1\">\n                    <el-tooltip\n                        v-if=\"mode !== 'upgrade'\"\n                        effect=\"dark\"\n                        :content=\"$t('commons.button.log')\"\n                        placement=\"top\"\n                    >\n                        <el-button\n                            type=\"primary\"\n                            link\n                            @click=\"$emit('openLog')\"\n                            :disabled=\"installed.status === 'DownloadErr'\"\n                        >\n                            <el-icon><Tickets /></el-icon>\n                        </el-button>\n                    </el-tooltip>\n                </span>\n                <span class=\"ml-1\">\n                    <el-tooltip v-if=\"mode !== 'upgrade'\" effect=\"dark\" :content=\"$t('menu.terminal')\" placement=\"top\">\n                        <el-button\n                            type=\"primary\"\n                            link\n                            @click=\"$emit('openTerminal')\"\n                            :disabled=\"installed.status !== 'Running'\"\n                        >\n                            <el-icon>\n                                <SvgIcon iconName=\"p-terminal2\" />\n                            </el-icon>\n                        </el-button>\n                    </el-tooltip>\n                </span>\n                <span class=\"ml-1\">\n                    <el-tooltip v-if=\"mode !== 'upgrade'\" effect=\"dark\" :content=\"$t('menu.container')\" placement=\"top\">\n                        <el-button type=\"primary\" link @click=\"$emit('toContainer')\">\n                            <el-icon>\n                                <SvgIcon iconName=\"p-docker\" />\n                            </el-icon>\n                        </el-button>\n                    </el-tooltip>\n                </span>\n                <span class=\"ml-1\" v-if=\"mode === 'installed'\">\n                    <el-tooltip\n                        effect=\"dark\"\n                        :content=\"$t('website.cancelFavorite')\"\n                        placement=\"top-start\"\n                        v-if=\"installed.favorite\"\n                    >\n                        <el-button\n                            link\n                            size=\"large\"\n                            icon=\"StarFilled\"\n                            type=\"warning\"\n                            :disabled=\"sortMode\"\n                            @click=\"$emit('favoriteInstall')\"\n                        ></el-button>\n                    </el-tooltip>\n                    <el-tooltip effect=\"dark\" :content=\"$t('website.favorite')\" placement=\"top-start\" v-else>\n                        <el-button\n                            link\n                            icon=\"Star\"\n                            type=\"info\"\n                            :disabled=\"sortMode\"\n                            @click=\"$emit('favoriteInstall')\"\n                        ></el-button>\n                    </el-tooltip>\n                </span>\n            </div>\n            <div class=\"operate-actions flex flex-wrap items-center justify-end gap-1\">\n                <el-button\n                    class=\"h-button\"\n                    plain\n                    round\n                    size=\"small\"\n                    @click=\"$emit('openUploads')\"\n                    v-if=\"mode === 'installed'\"\n                >\n                    {{ $t('database.loadBackup') }}\n                </el-button>\n                <el-button\n                    class=\"h-button\"\n                    plain\n                    round\n                    size=\"small\"\n                    @click=\"$emit('openBackups')\"\n                    v-if=\"mode === 'installed'\"\n                >\n                    {{ $t('commons.button.backup') }}\n                </el-button>\n                <el-button\n                    class=\"h-button\"\n                    plain\n                    round\n                    size=\"small\"\n                    :disabled=\"installed.status === 'Upgrading'\"\n                    @click=\"$emit('ignoreApp')\"\n                    v-if=\"mode === 'upgrade'\"\n                >\n                    {{ $t('commons.button.ignore') }}\n                </el-button>\n                <el-button\n                    class=\"h-button\"\n                    plain\n                    round\n                    size=\"small\"\n                    :disabled=\"\n                        (installed.status !== 'Running' && installed.status !== 'UpgradeErr') ||\n                        installed.appStatus === 'TakeDown'\n                    \"\n                    @click=\"$emit('openOperate')\"\n                    v-if=\"mode === 'upgrade'\"\n                >\n                    {{ $t('commons.button.upgrade') }}\n                </el-button>\n            </div>\n        </div>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { App } from '@/api/interface/app';\n\ninterface Props {\n    installed: App.AppInstalled;\n    mode: string;\n    sortMode?: boolean;\n}\ndefineProps<Props>();\n\ndefineEmits([\n    'toFolder',\n    'openLog',\n    'openTerminal',\n    'toContainer',\n    'openBackups',\n    'openOperate',\n    'ignoreApp',\n    'openUploads',\n    'favoriteInstall',\n]);\n\nconst options = {\n    modifiers: [\n        {\n            name: 'flip',\n            options: {\n                padding: 5,\n                fallbackPlacements: ['bottom-start', 'top-start', 'right', 'left'],\n            },\n        },\n    ],\n};\n\nconst isAppErr = (row: any) => {\n    return row.status.includes('Err') || row.status.includes('Error') || row.status.includes('UnHealthy');\n};\n</script>\n<style scoped lang=\"scss\">\n@use '@/views/app-store/index.scss';\n\n.svg-icon {\n    width: 100%;\n    height: 100%;\n    padding: 0;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/app/icon.vue",
    "content": "<template>\n    <div class=\"icon\">\n        <el-avatar @click=\"$emit('openDetail')\" shape=\"square\" :size=\"77\" :src=\"getAppIconUrl(appKey, currentNode)\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getAppIconUrl } from '@/api/modules/app';\n\ninterface Props {\n    currentNode: string;\n    appKey: string;\n}\ndefineProps<Props>();\n\ndefineEmits(['openDetail']);\n</script>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/app/info.vue",
    "content": "<template>\n    <div>\n        <div class=\"d-description flex flex-wrap items-center justify-start gap-1.5\">\n            <el-button class=\"mr-1\" plain size=\"small\">\n                {{ $t('app.version') }}{{ $t('commons.colon') }}{{ installed.version }}\n            </el-button>\n            <el-button v-if=\"installed.httpPort > 0\" class=\"mr-1\" plain size=\"small\">\n                {{ $t('commons.table.port') }}{{ $t('commons.colon') }}{{ installed.httpPort }}\n            </el-button>\n            <el-button v-if=\"installed.httpsPort > 0\" plain size=\"small\">\n                {{ $t('commons.table.port') }}：{{ installed.httpsPort }}\n            </el-button>\n\n            <el-popover\n                placement=\"right\"\n                trigger=\"hover\"\n                v-if=\"hasLinkButton(installed)\"\n                popper-class=\"app-link-popover\"\n                :popper-style=\"linkPopoverStyle\"\n            >\n                <template #reference>\n                    <el-button plain icon=\"Promotion\" size=\"small\">{{ $t('app.toLink') }}</el-button>\n                </template>\n                <table>\n                    <tbody>\n                        <tr v-if=\"defaultLink != ''\">\n                            <td v-if=\"installed.httpPort > 0\">\n                                <el-button\n                                    type=\"primary\"\n                                    link\n                                    @click=\"toLink('http://' + defaultLink + ':' + installed.httpPort)\"\n                                >\n                                    {{ 'http://' + defaultLink + ':' + installed.httpPort }}\n                                </el-button>\n                            </td>\n                        </tr>\n                        <tr v-if=\"defaultLink != ''\">\n                            <td v-if=\"installed.httpsPort > 0\">\n                                <el-button\n                                    type=\"primary\"\n                                    link\n                                    @click=\"toLink('https://' + defaultLink + ':' + installed.httpsPort)\"\n                                >\n                                    {{ 'https://' + defaultLink + ':' + installed.httpsPort }}\n                                </el-button>\n                            </td>\n                        </tr>\n                        <tr v-if=\"installed.webUI != ''\">\n                            <td>\n                                <el-button type=\"primary\" link @click=\"toLink(installed.webUI)\">\n                                    {{ installed.webUI }}\n                                </el-button>\n                            </td>\n                        </tr>\n                    </tbody>\n                </table>\n                <span v-if=\"defaultLink == '' && installed.webUI == ''\">\n                    {{ $t('app.webUIConfig') }}\n                    <el-link icon=\"Position\" @click=\"$emit('jumpToPath')\" type=\"primary\">\n                        {{ $t('firewall.quickJump') }}\n                    </el-link>\n                </span>\n            </el-popover>\n        </div>\n        <div class=\"description\">\n            <span>\n                {{ $t('app.alreadyRun') }}{{ $t('commons.colon') }}\n                {{ getAge(installed.createdAt) }}\n            </span>\n        </div>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { App } from '@/api/interface/app';\nimport { getAge, toLink } from '@/utils/util';\n\ninterface Props {\n    installed: App.AppInstalled;\n    defaultLink: string;\n}\ndefineProps<Props>();\n\ndefineEmits(['jumpToPath']);\n\nconst hasLinkButton = (installed: any) => {\n    return (\n        (installed.appType == 'website' || installed.appKey?.startsWith('local')) &&\n        (installed.httpPort > 0 || installed.httpsPort > 0 || installed.webUI != '')\n    );\n};\n\nconst linkPopoverStyle = {\n    width: 'fit-content',\n    maxWidth: 'min(92vw, 560px)',\n};\n</script>\n\n<style scoped lang=\"scss\">\n@use '@/views/app-store/index.scss';\n\n.d-description {\n    .el-button + .el-button {\n        margin-left: 0;\n    }\n}\n\n:deep(.app-link-popover) {\n    max-height: 65vh;\n    overflow-y: auto;\n    overflow-x: hidden;\n\n    table {\n        width: 100%;\n        table-layout: fixed;\n    }\n\n    td {\n        max-width: 100%;\n    }\n\n    .el-button.is-link {\n        white-space: normal;\n        text-align: left;\n        height: auto;\n        line-height: 1.4;\n        word-break: break-all;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/check/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"large\">\n        <el-row>\n            <el-col :span=\"20\" :offset=\"2\" v-if=\"open\">\n                <el-alert\n                    type=\"error\"\n                    :description=\"$t('app.deleteHelper', [$t('app.app')])\"\n                    center\n                    show-icon\n                    :closable=\"false\"\n                />\n                <el-descriptions border :column=\"1\" class=\"mt-5\">\n                    <el-descriptions-item\n                        v-for=\"(item, key) in map\"\n                        :key=\"key\"\n                        label-class-name=\"check-label\"\n                        class-name=\"check-content\"\n                        min-width=\"60px\"\n                    >\n                        <template #label>\n                            <a href=\"javascript:void(0);\" class=\"check-label-a\" @click=\"toPage(item[0])\">\n                                {{ $t('menu.' + item[0]) }}\n                            </a>\n                        </template>\n                        <span class=\"resources\">\n                            {{ map.get(item[0]).toString() }}\n                        </span>\n                    </el-descriptions-item>\n                </el-descriptions>\n                <div v-if=\"installData.key === 'openresty'\" class=\"mt-5\">\n                    <el-checkbox v-model=\"forceDelete\" label=\"true\">{{ $t('app.forceDelete') }}</el-checkbox>\n                    <ErrPrompt :title=\"$t('app.openrestyDeleteHelper')\" />\n                </div>\n            </el-col>\n        </el-row>\n\n        <template #footer v-if=\"forceDelete\">\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { App } from '@/api/interface/app';\nimport { installedOp } from '@/api/modules/app';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { routerToName } from '@/utils/router';\nimport { ref } from 'vue';\nimport ErrPrompt from '@/components/error-prompt/index.vue';\n\ninterface CheckRrops {\n    items: App.AppInstallResource[];\n    installID: Number;\n    key: string;\n}\nconst installData = ref<CheckRrops>({\n    items: [],\n    installID: 0,\n    key: '',\n});\nconst open = ref(false);\nconst map = new Map();\nconst forceDelete = ref(false);\nconst em = defineEmits(['close']);\n\nconst acceptParams = (props: CheckRrops) => {\n    map.clear();\n    forceDelete.value = false;\n    installData.value.installID = props.installID;\n    installData.value.key = props.key;\n    installData.value.items = [];\n    installData.value.items = props.items;\n    installData.value.items.forEach((item) => {\n        if (map.has(item.type)) {\n            const array = map.get(item.type);\n            array.push(item.name);\n            map.set(item.type, array);\n        } else {\n            map.set(item.type, [item.name]);\n        }\n    });\n    open.value = true;\n};\n\nconst toPage = (key: string) => {\n    if (key === 'app') {\n        open.value = false;\n    }\n    if (key === 'website') {\n        routerToName('Website');\n    }\n    if (key === 'database') {\n        routerToName('MySQL');\n    }\n};\n\nconst onConfirm = () => {\n    ElMessageBox.confirm(\n        i18n.global.t('app.operatorHelper', [i18n.global.t('commons.button.delete')]),\n        i18n.global.t('commons.button.delete'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(() => {\n        const deleteReq = {\n            operate: 'delete',\n            installId: Number(installData.value.installID),\n            deleteBackup: true,\n            forceDelete: true,\n            deleteDB: true,\n        };\n        installedOp(deleteReq).then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            open.value = false;\n            em('close', open);\n        });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped>\n.resources {\n    word-break: break-all;\n}\n\n.center {\n    text-align: center;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.uninstall') + ' - ' + appInstallName\" @close=\"handleClose\">\n        <el-form\n            ref=\"deleteForm\"\n            label-position=\"left\"\n            v-loading=\"loading\"\n            @submit.prevent=\"handleFormSubmit\"\n            @keyup.enter=\"handleFormSubmit\"\n        >\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.forceDelete\" :label=\"$t('app.forceUninstall')\" />\n                <span class=\"input-help\">\n                    {{ $t('app.forceDeleteHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.deleteBackup\" :label=\"$t('app.deleteBackup')\" />\n                <span class=\"input-help\">\n                    {{ $t('app.deleteBackupHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.deleteImage\" :label=\"$t('app.deleteImage')\" />\n                <span class=\"input-help\">\n                    {{ $t('app.deleteImageHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item v-if=\"appType === 'website' && linkDB\">\n                <el-checkbox v-model=\"deleteReq.deleteDB\" :label=\"$t('app.deleteDB')\" />\n                <span class=\"input-help\">\n                    {{ $t('app.deleteDBHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <span v-html=\"deleteHelper\"></span>\n                <el-input v-model=\"deleteInfo\" :placeholder=\"appInstallName\" @keyup.enter=\"handleFormSubmit\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"loading || deleteInfo !== appInstallName\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n    <TaskLog ref=\"taskLogRef\" @close=\"handleClose\" />\n</template>\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { onBeforeUnmount, ref } from 'vue';\nimport { App } from '@/api/interface/app';\nimport { installedOp } from '@/api/modules/app';\nimport { getAppStoreConfig } from '@/api/modules/setting';\nimport i18n from '@/lang';\nimport bus from '@/global/bus';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { v4 as uuidv4 } from 'uuid';\n\nconst deleteReq = ref({\n    operate: 'delete',\n    installId: 0,\n    deleteBackup: false,\n    forceDelete: false,\n    deleteDB: true,\n    deleteImage: false,\n    taskID: '',\n});\nconst open = ref(false);\nconst loading = ref(false);\nconst deleteHelper = ref('');\nconst deleteInfo = ref('');\nconst appInstallName = ref('');\nconst appType = ref('');\nconst taskLogRef = ref();\nconst linkDB = ref(false);\n\nconst deleteForm = ref<FormInstance>();\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', open);\n};\n\nconst handleFormSubmit = () => {\n    if (!loading.value && deleteInfo.value === appInstallName.value) {\n        submit();\n    }\n};\n\nconst acceptParams = async (app: App.AppInstallDto) => {\n    const config = await getAppStoreConfig();\n    deleteReq.value = {\n        operate: 'delete',\n        installId: 0,\n        deleteBackup: config.data.uninstallDeleteBackup === 'Enable',\n        forceDelete: false,\n        deleteDB: true,\n        deleteImage: config.data.uninstallDeleteImage === 'Enable',\n        taskID: uuidv4(),\n    };\n    deleteInfo.value = '';\n    deleteReq.value.installId = app.id;\n    appType.value = app.appType;\n    deleteHelper.value = i18n.global.t('website.deleteConfirmHelper', [app.name]);\n    appInstallName.value = app.name;\n    linkDB.value = app.linkDB;\n    open.value = true;\n};\n\nconst submit = async () => {\n    installedOp(deleteReq.value).then(() => {\n        handleClose();\n        taskLogRef.value.openWithTaskID(deleteReq.value.taskID);\n        bus.emit('update', true);\n    });\n};\n\nonBeforeUnmount(() => {\n    bus.off('update');\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/detail/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('app.param')\" @close=\"handleClose\" size=\"normal\">\n        <template #buttons>\n            <el-button type=\"primary\" plain @click=\"editParam\" :disabled=\"loading\" :icon=\"edit ? '' : 'Edit'\">\n                {{ edit ? $t('app.detail') : $t('commons.button.edit') }}\n            </el-button>\n        </template>\n        <div v-if=\"!edit\">\n            <el-descriptions border :column=\"1\">\n                <el-descriptions-item :label=\"$t('app.webUI')\">\n                    <span v-if=\"!openConfig\">\n                        {{ appConfigUpdate.webUI }}\n                        <el-button size=\"small\" @click=\"openConfig = true\">\n                            {{ $t('container.custom') }}\n                        </el-button>\n                    </span>\n                    <span class=\"flex\" v-else>\n                        <el-input v-model=\"webUI.domain\" :placeholder=\"$t('app.webUIPlaceholder')\">\n                            <template #prepend>\n                                <el-select v-model=\"webUI.protocol\" class=\"pre-select\">\n                                    <el-option label=\"http\" value=\"http://\" />\n                                    <el-option label=\"https\" value=\"https://\" />\n                                </el-select>\n                            </template>\n                        </el-input>\n                        <el-button type=\"primary\" @click=\"updateAppConfig\" class=\"ml-2\">\n                            {{ $t('commons.button.confirm') }}\n                        </el-button>\n                    </span>\n                </el-descriptions-item>\n                <el-descriptions-item\n                    label-class-name=\"system-label\"\n                    v-for=\"(param, key) in params\"\n                    :label=\"getLabel(param)\"\n                    :key=\"key\"\n                >\n                    <span v-if=\"param.type === 'password' && !param.showPassword\">********</span>\n                    <span v-else class=\"break-all whitespace-normal\">\n                        {{ param.showValue && param.showValue != '' ? param.showValue : param.value }}\n                    </span>\n                    <template v-if=\"param.type === 'password'\">\n                        <el-button\n                            v-if=\"!param.showPassword\"\n                            link\n                            @click=\"param.showPassword = true\"\n                            icon=\"View\"\n                            class=\"-mt-1 ml-3\"\n                        ></el-button>\n                        <el-button\n                            v-if=\"param.showPassword\"\n                            link\n                            @click=\"param.showPassword = false\"\n                            icon=\"Hide\"\n                            class=\"-mt-1 ml-3\"\n                        ></el-button>\n                    </template>\n                    <CopyButton class=\"-mt-1\" v-if=\"showCopyButton(param.key)\" :content=\"param.value\" />\n                </el-descriptions-item>\n            </el-descriptions>\n        </div>\n        <div v-else v-loading=\"loading\">\n            <el-alert :title=\"$t('app.updateHelper')\" type=\"warning\" :closable=\"false\" class=\"common-prompt\" />\n            <el-form @submit.prevent ref=\"paramForm\" :model=\"paramModel\" label-position=\"top\" :rules=\"rules\">\n                <div v-for=\"(p, index) in params\" :key=\"index\">\n                    <el-form-item :prop=\"'params.' + p.key\" :label=\"getLabel(p)\">\n                        <el-input\n                            v-if=\"p.type == 'number'\"\n                            type=\"number\"\n                            v-model.number=\"paramModel.params[p.key]\"\n                            :disabled=\"!p.edit\"\n                        ></el-input>\n                        <el-input\n                            v-else-if=\"p.type == 'password'\"\n                            :type=\"p.showPassword ? 'text' : 'password'\"\n                            v-model=\"paramModel.params[p.key]\"\n                            :disabled=\"!p.edit\"\n                        >\n                            <template #append>\n                                <el-button\n                                    :icon=\"p.showPassword ? 'Hide' : 'View'\"\n                                    @click=\"p.showPassword = !p.showPassword\"\n                                />\n                            </template>\n                        </el-input>\n                        <el-select\n                            v-model=\"paramModel.params[p.key]\"\n                            v-else-if=\"p.type == 'select'\"\n                            :multiple=\"p.multiple\"\n                        >\n                            <el-option\n                                v-for=\"value in p.values\"\n                                :key=\"value.label\"\n                                :value=\"value.value\"\n                                :label=\"value.label\"\n                                :disabled=\"!p.edit\"\n                            ></el-option>\n                        </el-select>\n                        <el-input v-else v-model.trim=\"paramModel.params[p.key]\" :disabled=\"!p.edit\"></el-input>\n                    </el-form-item>\n                </div>\n                <el-form-item prop=\"advanced\">\n                    <el-checkbox v-model=\"paramModel.advanced\" :label=\"$t('app.advanced')\" size=\"large\" />\n                </el-form-item>\n                <div v-if=\"paramModel.advanced\">\n                    <el-form-item :label=\"$t('app.containerName')\" prop=\"containerName\">\n                        <el-input\n                            v-model.trim=\"paramModel.containerName\"\n                            :placeholder=\"$t('app.containerNameHelper')\"\n                        ></el-input>\n                    </el-form-item>\n                    <el-form-item prop=\"allowPort\" v-if=\"!paramModel.isHostMode\">\n                        <el-checkbox\n                            v-model=\"paramModel.allowPort\"\n                            :label=\"$t('app.allowPort')\"\n                            size=\"large\"\n                            @change=\"changeAllowPort\"\n                        />\n                        <span class=\"input-help\">{{ $t('app.allowPortHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('app.specifyIP')\" v-if=\"paramModel.allowPort\" prop=\"specifyIP\">\n                        <el-input v-model=\"paramModel.specifyIP\"></el-input>\n                        <span class=\"input-help\">{{ $t('app.specifyIPHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('container.restartPolicy')\" prop=\"restartPolicy\">\n                        <el-select v-model=\"paramModel.restartPolicy\" class=\"p-w-300\">\n                            <el-option :label=\"$t('container.no')\" value=\"no\"></el-option>\n                            <el-option :label=\"$t('container.always')\" value=\"always\"></el-option>\n                            <el-option :label=\"$t('container.onFailure')\" value=\"on-failure\"></el-option>\n                            <el-option :label=\"$t('container.unlessStopped')\" value=\"unless-stopped\"></el-option>\n                        </el-select>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('container.cpuQuota')\" prop=\"cpuQuota\">\n                        <el-input type=\"number\" class=\"!w-2/5\" v-model.number=\"paramModel.cpuQuota\" maxlength=\"5\">\n                            <template #append>{{ $t('app.cpuCore') }}</template>\n                        </el-input>\n                        <span class=\"input-help\">\n                            {{ $t('container.limitHelper', [limits.cpu]) }}{{ $t('commons.units.core') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('container.memoryLimit')\" prop=\"memoryLimit\">\n                        <el-input class=\"!w-2/5\" v-model.number=\"paramModel.memoryLimit\" maxlength=\"10\">\n                            <template #append>\n                                <el-select\n                                    v-model=\"paramModel.memoryUnit\"\n                                    placeholder=\"Select\"\n                                    class=\"!w-[85px]\"\n                                    @change=\"changeUnit\"\n                                >\n                                    <el-option label=\"MB\" value=\"M\" />\n                                    <el-option label=\"GB\" value=\"G\" />\n                                </el-select>\n                            </template>\n                        </el-input>\n                        <span class=\"input-help\">\n                            {{ $t('container.limitHelper', [limits.memory]) }}{{ paramModel.memoryUnit }}B\n                        </span>\n                    </el-form-item>\n\n                    <el-form-item prop=\"editCompose\">\n                        <el-checkbox v-model=\"paramModel.editCompose\" :label=\"$t('app.editCompose')\" size=\"large\" />\n                        <span class=\"input-help\">{{ $t('app.editComposeHelper') }}</span>\n                    </el-form-item>\n                    <div v-if=\"paramModel.editCompose\">\n                        <el-button @click=\"openDiff()\" type=\"primary\" class=\"!mb-2\">\n                            {{ $t('app.showDiff') }}\n                        </el-button>\n                        <CodemirrorPro v-model=\"paramModel.dockerCompose\" mode=\"yaml\"></CodemirrorPro>\n                    </div>\n                </div>\n            </el-form>\n        </div>\n        <template #footer v-if=\"edit\">\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" :disabled=\"loading\" @click=\"submit(paramForm)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n        <Diff ref=\"composeDiffRef\" @confirm=\"getNewCompose\" />\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport Diff from '@/views/app-store/installed/upgrade/diff/index.vue';\n\nimport { App } from '@/api/interface/app';\nimport { getAppInstallParams, updateAppInstallParams, updateInstallConfig } from '@/api/modules/app';\nimport { reactive, ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { getLabel, splitHttp, checkIpV4V6, checkDomain } from '@/utils/util';\nimport i18n from '@/lang';\nimport { loadResourceLimit } from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\n\ninterface ParamProps {\n    id: Number;\n    app: any;\n}\nconst paramData = ref<ParamProps>({\n    id: 0,\n    app: {},\n});\n\ninterface EditForm extends App.InstallParams {\n    default: any;\n}\n\nconst emit = defineEmits(['close']);\nconst open = ref(false);\nconst loading = ref(false);\nconst params = ref<EditForm[]>();\nconst edit = ref(false);\nconst paramForm = ref<FormInstance>();\nconst paramModel = reactive<any>({\n    params: {},\n});\nconst rules = reactive({\n    params: {},\n    cpuQuota: [Rules.requiredInput, checkNumberRange(0, 999)],\n    memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],\n    containerName: [Rules.containerName],\n    restartPolicy: [Rules.requiredSelect],\n});\nconst submitModel = reactive<any>({\n    webUI: '',\n});\nconst appType = ref('');\nconst appConfigUpdate = ref<App.AppConfigUpdate>({\n    installID: 0,\n    webUI: '',\n});\nconst openConfig = ref(false);\nconst webUI = reactive({\n    protocol: 'http://',\n    domain: '',\n});\nconst limits = ref<Container.ResourceLimit>({\n    cpu: null as number,\n    memory: null as number,\n});\nconst oldMemory = ref<number>(0);\nconst composeDiffRef = ref();\nconst rawCompose = ref('');\n\nconst getNewCompose = (compose: string) => {\n    if (compose != '') {\n        paramModel.dockerCompose = compose;\n    }\n};\n\nconst openDiff = () => {\n    composeDiffRef.value.acceptParams(rawCompose.value, paramModel.dockerCompose);\n};\n\nfunction checkWebUI() {\n    if (webUI.domain !== '') {\n        let domain = webUI.domain;\n        let port = null;\n\n        if (domain.includes('/')) {\n            domain = domain.split('/')[0];\n        }\n\n        if (domain.includes(':')) {\n            const parts = domain.split(':');\n            domain = parts[0];\n            port = parts[1];\n\n            if (!checkPort(port)) {\n                return false;\n            }\n        }\n\n        if (checkIpV4V6(domain) && checkDomain(domain)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nfunction checkPort(port: string) {\n    const portNum = parseInt(port, 10);\n    return !isNaN(portNum) && portNum > 0 && portNum <= 65535;\n}\n\nconst acceptParams = async (props: ParamProps) => {\n    submitModel.installId = props.id;\n    params.value = [];\n    paramData.value.id = props.id;\n    paramModel.params = {};\n    edit.value = false;\n    rules.params = {};\n    await get();\n    open.value = true;\n    openConfig.value = false;\n};\n\nconst handleClose = () => {\n    emit('close');\n    open.value = false;\n};\nconst editParam = () => {\n    params.value.forEach((param: EditForm) => {\n        paramModel.params[param.key] = param.value;\n    });\n    edit.value = !edit.value;\n    loadLimit();\n};\n\nconst changeAllowPort = () => {\n    if (paramModel.allowPort) {\n        paramModel.specifyIP = '';\n    }\n};\n\nconst get = async () => {\n    try {\n        loading.value = true;\n        const res = await getAppInstallParams(Number(paramData.value.id));\n        rawCompose.value = res.data.rawCompose;\n        const configParams = res.data.params || [];\n        if (configParams && configParams.length > 0) {\n            configParams.forEach((d) => {\n                let value = d.value;\n                if (d.type === 'number') {\n                    value = Number(value);\n                }\n                params.value.push({\n                    default: value,\n                    labelEn: d.labelEn,\n                    labelZh: d.labelZh,\n                    rule: d.rule,\n                    value: value,\n                    edit: d.edit,\n                    key: d.key,\n                    type: d.type,\n                    values: d.values,\n                    showValue: d.showValue,\n                    multiple: d.multiple,\n                    label: d.label,\n                    required: d.required,\n                    showPassword: false,\n                });\n                if (d.required) {\n                    rules.params[d.key] = [Rules.requiredInput];\n                    if (d.rule) {\n                        rules.params[d.key].push(Rules[d.rule]);\n                    }\n                } else {\n                    rules.params[d.key] = [];\n                }\n            });\n        }\n        paramModel.memoryLimit = res.data.memoryLimit;\n        paramModel.cpuQuota = res.data.cpuQuota;\n        paramModel.memoryUnit = res.data.memoryUnit !== '' ? res.data.memoryUnit : 'MB';\n        paramModel.allowPort = res.data.allowPort;\n        paramModel.containerName = res.data.containerName;\n        paramModel.advanced = false;\n        paramModel.dockerCompose = res.data.dockerCompose;\n        paramModel.isHostMode = res.data.hostMode;\n        paramModel.specifyIP = res.data.specifyIP;\n        paramModel.restartPolicy = res.data.restartPolicy || 'no';\n        if (paramModel.restartPolicy === 'on-failure:5') {\n            paramModel.restartPolicy = 'on-failure';\n        }\n        appConfigUpdate.value.webUI = res.data.webUI;\n        if (res.data.webUI != '') {\n            const httpConfig = splitHttp(res.data.webUI);\n            webUI.domain = httpConfig.url;\n            webUI.protocol = httpConfig.proto + '://';\n        }\n        appType.value = res.data.type;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst submit = async (formEl: FormInstance) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        ElMessageBox.confirm(i18n.global.t('app.updateWarn'), i18n.global.t('commons.button.update'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        }).then(async () => {\n            submitModel.params = paramModel.params;\n            if (paramModel.advanced) {\n                submitModel.advanced = paramModel.advanced;\n                submitModel.memoryLimit = paramModel.memoryLimit;\n                submitModel.cpuQuota = paramModel.cpuQuota;\n                submitModel.memoryUnit = paramModel.memoryUnit;\n                submitModel.allowPort = paramModel.allowPort;\n                submitModel.containerName = paramModel.containerName;\n                if (paramModel.editCompose) {\n                    submitModel.editCompose = paramModel.editCompose;\n                    submitModel.dockerCompose = paramModel.dockerCompose;\n                }\n                submitModel.restartPolicy = paramModel.restartPolicy;\n            }\n            try {\n                loading.value = true;\n                await updateAppInstallParams(submitModel);\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                handleClose();\n            } catch (error) {\n                loading.value = false;\n            }\n        });\n    });\n};\n\nconst updateAppConfig = async () => {\n    try {\n        let req = {\n            installID: Number(paramData.value.id),\n            webUI: webUI.protocol + webUI.domain,\n        };\n        if (!webUI.domain || webUI.domain === '') {\n            req.webUI = '';\n        }\n        if (!checkWebUI()) {\n            MsgError(i18n.global.t('commons.rule.host'));\n            return;\n        }\n        await updateInstallConfig(req);\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        handleClose();\n    } catch (error) {}\n};\n\nconst showCopyButton = (key: string) => {\n    const keys = [\n        'PANEL_DB_ROOT_PASSWORD',\n        'PANEL_DB_NAME',\n        'PANEL_DB_USER',\n        'PANEL_DB_USER_PASSWORD',\n        'PANEL_REDIS_ROOT_PASSWORD',\n        'PANEL_DB_ROOT_USER',\n    ];\n    for (let i = 0; i < keys.length; i++) {\n        if (key === keys[i]) {\n            return true;\n        }\n    }\n    return false;\n};\n\nconst loadLimit = async () => {\n    const res = await loadResourceLimit();\n    limits.value = res.data;\n    limits.value.memory = Number((limits.value.memory / 1024 / 1024).toFixed(2));\n    oldMemory.value = limits.value.memory;\n};\n\nconst changeUnit = () => {\n    if (paramModel.memoryUnit == 'M') {\n        limits.value.memory = oldMemory.value;\n    } else {\n        limits.value.memory = Number((oldMemory.value / 1024).toFixed(2));\n    }\n};\n\ndefineExpose({ acceptParams });\n</script>\n\n<style lang=\"scss\">\n.change-button {\n    margin-top: 5px;\n}\n.system-label {\n    width: 40% !important;\n    white-space: nowrap !important;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/ignore/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.ignore')\" :resource=\"resourceName\" @close=\"handleClose\">\n        <el-form @submit.prevent ref=\"updateRef\" :rules=\"rules\" label-position=\"top\" :model=\"req\">\n            <el-form-item>\n                <el-radio-group v-model=\"req.scope\">\n                    <el-radio-button :label=\"$t('app.ignoreAll')\" value=\"all\" />\n                    <el-radio-button :label=\"$t('app.ignoreVersion')\" value=\"version\" />\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('app.versionSelect')\" prop=\"appDetailID\" v-if=\"req.scope === 'version'\">\n                <el-select v-model=\"req.appDetailID\">\n                    <el-option\n                        v-for=\"(version, index) in versions\"\n                        :key=\"index\"\n                        :value=\"version.detailId\"\n                        :label=\"version.version\"\n                    ></el-option>\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { App } from '@/api/interface/app';\nimport { getAppUpdateVersions, ignoreUpgrade } from '@/api/modules/app';\nimport bus from '@/global/bus';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst open = ref(false);\nconst resourceName = ref('');\nconst req = reactive({\n    appID: 0,\n    appDetailID: 0,\n    scope: 'all',\n});\nconst versions = ref();\nconst appInstallID = ref(0);\nconst rules = ref<any>({\n    appDetailID: [Rules.requiredSelect],\n});\nconst loading = ref(false);\nconst emit = defineEmits(['close']);\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = (apppInstall: App.AppInstalled) => {\n    appInstallID.value = apppInstall.id;\n    req.appID = apppInstall.appID;\n    getVersions();\n    open.value = true;\n};\n\nconst getVersions = async () => {\n    try {\n        const res = await getAppUpdateVersions({ appInstallID: appInstallID.value });\n        versions.value = res.data || [];\n        if (versions.value.length > 0) {\n            req.appDetailID = versions.value[0].detailId;\n        }\n    } catch (error) {}\n};\n\nconst submit = async () => {\n    loading.value = true;\n    try {\n        await ignoreUpgrade(req);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        handleClose();\n        bus.emit('upgrade', true);\n        emit('close');\n    } catch (error) {\n        return;\n    } finally {\n        loading.value = false;\n    }\n};\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/ignore/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('app.ignoreList')\" @close=\"handleClose\" size=\"small\">\n        <template #content>\n            <el-table :data=\"apps\">\n                <el-table-column prop=\"name\" :label=\"$t('app.app')\" />\n                <el-table-column prop=\"scope\" :label=\"$t('license.trialInfo')\">\n                    <template #default=\"{ row }\">\n                        <el-tag v-if=\"row.version != ''\">{{ row.version }}</el-tag>\n                        <el-tag v-else>{{ $t('commons.table.all') + $t('app.version') }}</el-tag>\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"scope\" :label=\"$t('commons.table.operate')\">\n                    <template #default=\"{ row }\">\n                        <el-button type=\"primary\" link @click=\"cancelIgnore(row.ID)\">\n                            {{ $t('app.cancelIgnore') }}\n                        </el-button>\n                    </template>\n                </el-table-column>\n            </el-table>\n        </template>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { cancelAppIgnore, getIgnoredApp } from '@/api/modules/app';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport bus from '@/global/bus';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst apps = ref();\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', open);\n};\n\nconst acceptParams = () => {\n    open.value = true;\n    getApps();\n};\n\nconst getApps = async () => {\n    try {\n        const res = await getIgnoredApp();\n        apps.value = res.data;\n    } catch (error) {}\n};\n\nconst cancelIgnore = async (id: number) => {\n    loading.value = true;\n    await cancelAppIgnore({ id: id })\n        .then(() => {\n            bus.emit('upgrade', true);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .finally(() => {\n            getApps();\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped>\n.app-margin {\n    margin-top: 10px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/index.vue",
    "content": "<template>\n    <LayoutContent v-loading=\"loading || syncLoading\" :title=\"activeName\">\n        <template #search>\n            <Tags @change=\"changeTag\" hideKey=\"Runtime\" />\n        </template>\n        <template #leftToolBar>\n            <el-button @click=\"sync\" type=\"primary\" plain v-if=\"mode === 'installed' && !sortMode && data != null\">\n                {{ $t('commons.button.refresh') }}\n            </el-button>\n            <el-button\n                @click=\"enterSortMode\"\n                type=\"primary\"\n                plain\n                v-if=\"mode === 'installed' && !sortMode && data != null\"\n            >\n                {{ $t('app.sortMode') }}\n            </el-button>\n            <el-button @click=\"saveSortOrder\" type=\"primary\" v-if=\"sortMode\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n            <el-button @click=\"exitSortMode\" plain v-if=\"sortMode\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button @click=\"openIgnore\" type=\"primary\" plain v-if=\"mode === 'upgrade'\">\n                {{ $t('app.showIgnore') }}\n            </el-button>\n        </template>\n        <template #rightToolBar>\n            <TableSearch @search=\"search()\" v-model:searchName=\"searchReq.name\" />\n        </template>\n        <template #main>\n            <div>\n                <MainDiv :heightDiff=\"mode === 'upgrade' ? 280 : 300\">\n                    <el-alert type=\"info\" :closable=\"false\" v-if=\"mode === 'installed' && !isIntl\">\n                        <template #title>\n                            <span class=\"flx-align-center\">\n                                {{ $t('app.installHelper') }}\n                                <el-link\n                                    class=\"ml-5\"\n                                    icon=\"Position\"\n                                    @click=\"jumpToPath(router, '/containers/setting')\"\n                                    type=\"primary\"\n                                >\n                                    {{ $t('firewall.quickJump') }}\n                                </el-link>\n                            </span>\n                        </template>\n                    </el-alert>\n                    <div class=\"update-prompt\" v-if=\"data === null\">\n                        <span>{{ mode === 'upgrade' ? $t('app.updatePrompt') : $t('app.installPrompt') }}</span>\n                        <div>\n                            <img src=\"@/assets/images/no_update_app.svg\" />\n                        </div>\n                    </div>\n                    <el-row :gutter=\"5\" ref=\"sortContainer\">\n                        <el-col\n                            v-for=\"(installed, index) in data\"\n                            :key=\"installed.id\"\n                            :data-sort-index=\"index\"\n                            :xs=\"24\"\n                            :sm=\"24\"\n                            :md=\"24\"\n                            :lg=\"12\"\n                            :xl=\"12\"\n                        >\n                            <AppCard\n                                :installed=\"installed\"\n                                :mode=\"mode\"\n                                :defaultLink=\"defaultLink\"\n                                :currentNode=\"currentNode\"\n                                :sortMode=\"sortMode\"\n                                @open-detail=\"openDetail(installed.appKey)\"\n                                @open-backups=\"openBackups(installed)\"\n                                @open-log=\"openLog(installed)\"\n                                @open-terminal=\"openTerminal(installed)\"\n                                @open-operate=\"openOperate(installed, 'upgrade')\"\n                                @favorite-install=\"favoriteInstall(installed)\"\n                                @to-folder=\"routerToFileWithPath(installed.path)\"\n                                @open-uploads=\"openUploads(installed.appKey, installed.name)\"\n                                @jump-to-path=\"jumpToPath(router, '/settings/panel')\"\n                                @to-container=\"toContainer(installed)\"\n                                @ignore-app=\"ignoreApp(installed)\"\n                            >\n                                <template #buttons>\n                                    <div\n                                        class=\"d-button flex flex-wrap items-center justify-start gap-1.5\"\n                                        v-if=\"mode === 'installed' && installed.status != 'Installing'\"\n                                    >\n                                        <el-button\n                                            class=\"app-button\"\n                                            v-for=\"(button, key) in buttons\"\n                                            :key=\"key\"\n                                            :type=\"button.disabled && button.disabled(installed) ? 'info' : ''\"\n                                            plain\n                                            round\n                                            size=\"small\"\n                                            @click=\"button.click(installed)\"\n                                            :disabled=\"button.disabled && button.disabled(installed)\"\n                                        >\n                                            {{ button.label }}\n                                        </el-button>\n                                    </div>\n                                </template>\n                            </AppCard>\n                        </el-col>\n                    </el-row>\n                </MainDiv>\n            </div>\n            <div class=\"page-button\" v-if=\"mode === 'installed' && !sortMode\">\n                <fu-table-pagination\n                    v-model:current-page=\"paginationConfig.currentPage\"\n                    v-model:page-size=\"paginationConfig.pageSize\"\n                    v-bind=\"paginationConfig\"\n                    @change=\"search\"\n                    :layout=\"'total, sizes, prev, pager, next, jumper'\"\n                />\n            </div>\n        </template>\n    </LayoutContent>\n    <Backups ref=\"backupRef\" />\n    <Uploads ref=\"uploadRef\" />\n    <AppResources ref=\"checkRef\" @close=\"search\" />\n    <AppDelete ref=\"deleteRef\" @close=\"search\" />\n    <AppParams ref=\"appParamRef\" @close=\"search\" />\n    <AppUpgrade ref=\"upgradeRef\" @close=\"search\" />\n    <AppIgnore ref=\"ignoreRef\" @close=\"search\" />\n    <ComposeLogs ref=\"composeLogRef\" />\n    <TerminalDialog ref=\"dialogTerminalRef\" />\n    <TaskLog ref=\"taskLogRef\" @close=\"search\" />\n    <Detail ref=\"detailRef\" />\n    <IgnoreApp ref=\"ignoreAppRef\" @close=\"search\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport AppCard from '@/views/app-store/installed/app/card.vue';\nimport Backups from '@/components/backup/index.vue';\nimport Uploads from '@/components/upload/index.vue';\nimport AppResources from './check/index.vue';\nimport AppDelete from './delete/index.vue';\nimport AppParams from './detail/index.vue';\nimport AppUpgrade from './upgrade/index.vue';\nimport AppIgnore from './ignore/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport Detail from '@/views/app-store/detail/index.vue';\nimport Tags from '@/views/app-store/components/tag.vue';\nimport MainDiv from '@/components/main-div/index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport IgnoreApp from '@/views/app-store/installed/ignore/create/index.vue';\nimport TerminalDialog from '@/views/container/container/terminal/index.vue';\n\nimport { searchAppInstalled, installedOp, appInstalledDeleteCheck, updateAppInstallSort } from '@/api/modules/app';\nimport { onMounted, onUnmounted, reactive, ref, nextTick } from 'vue';\nimport Sortable from 'sortablejs';\nimport i18n from '@/lang';\nimport { ElMessageBox } from 'element-plus';\nimport { App } from '@/api/interface/app';\nimport { jumpToPath } from '@/utils/util';\nimport { useRouter } from 'vue-router';\nimport { MsgSuccess } from '@/utils/message';\nimport { getAgentSettingByKey } from '@/api/modules/setting';\nimport { routerToFileWithPath, routerToNameWithQuery } from '@/utils/router';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { currentNode, isMaster, currentNodeAddr, isIntl } = useGlobalStore();\n\nconst data = ref<any>();\nconst loading = ref(false);\nconst syncLoading = ref(false);\nconst sortMode = ref(false);\nconst sortContainer = ref();\nlet sortableInstance: Sortable | null = null;\nlet timer: NodeJS.Timer | null = null;\nconst paginationConfig = reactive({\n    cacheSizeKey: 'app-installed-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('app-installed-page-size')) || 20,\n    total: 0,\n});\nconst open = ref(false);\nconst operateReq = reactive({\n    installId: 0,\n    operate: '',\n    detailId: 0,\n    favorite: false,\n});\nconst backupRef = ref();\nconst uploadRef = ref();\nconst checkRef = ref();\nconst deleteRef = ref();\nconst appParamRef = ref();\nconst upgradeRef = ref();\nconst ignoreRef = ref();\nconst composeLogRef = ref();\nconst dialogTerminalRef = ref();\nconst taskLogRef = ref();\nconst searchReq = reactive({\n    page: 1,\n    pageSize: 20,\n    name: '',\n    tags: [],\n    update: false,\n    sync: false,\n});\nconst router = useRouter();\nconst activeName = ref(i18n.global.t('app.installed'));\nconst mode = ref('installed');\nconst defaultLink = ref('');\nconst detailRef = ref();\nconst ignoreAppRef = ref();\n\nconst openDetail = (key: string) => {\n    detailRef.value.acceptParams(key, 'install');\n};\n\nconst changeTag = (key: string) => {\n    searchReq.tags = [];\n    if (key !== 'all') {\n        searchReq.tags = [key];\n    }\n    search();\n};\n\nconst search = async () => {\n    searchReq.page = paginationConfig.currentPage;\n    searchReq.pageSize = paginationConfig.pageSize;\n\n    localStorage.setItem('app-installed-page-size', String(searchReq.pageSize));\n\n    const res = await searchAppInstalled(searchReq);\n    data.value = res.data.items;\n    paginationConfig.total = res.data.total;\n};\n\nconst sync = async () => {\n    loading.value = true;\n    const searchItem = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        name: searchReq.name,\n        tags: searchReq.tags,\n        update: false,\n        sync: true,\n    };\n    const res = await searchAppInstalled(searchItem);\n    loading.value = false;\n    data.value = res.data.items;\n    paginationConfig.total = res.data.total;\n};\n\nconst openOperate = (row: any, op: string) => {\n    operateReq.installId = row.id;\n    operateReq.operate = op;\n    if (op == 'upgrade') {\n        upgradeRef.value.acceptParams(row, op);\n    } else if (op == 'delete') {\n        appInstalledDeleteCheck(row.id).then(async (res) => {\n            const items = res.data;\n            if (res.data && res.data.length > 0) {\n                checkRef.value.acceptParams({ items: items, key: row.appKey, installID: row.id });\n            } else {\n                deleteRef.value.acceptParams(row);\n            }\n        });\n    } else {\n        onOperate(op);\n    }\n};\n\nconst favoriteInstall = (row: App.AppInstalled) => {\n    operateReq.installId = row.id;\n    operateReq.operate = 'favorite';\n    operateReq.favorite = !row.favorite;\n    operate();\n};\n\nconst openIgnore = () => {\n    ignoreRef.value.acceptParams();\n};\n\nconst operate = async () => {\n    open.value = false;\n    loading.value = true;\n    await installedOp(operateReq)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            searchReq.sync = true;\n            search();\n            setTimeout(() => {\n                search();\n            }, 3000);\n            setTimeout(() => {\n                search();\n            }, 15000);\n        })\n        .catch(() => {\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst onOperate = async (operation: string) => {\n    ElMessageBox.confirm(\n        i18n.global.t('app.operatorHelper', [i18n.global.t('commons.operate.' + operation)]),\n        i18n.global.t('commons.operate.' + operation),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(() => {\n        operate();\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.operate.rebuild'),\n        click: (row: any) => {\n            openOperate(row, 'rebuild');\n        },\n        disabled: (row: any) => {\n            return (\n                row.status === 'DownloadErr' ||\n                row.status === 'Upgrading' ||\n                row.status === 'Rebuilding' ||\n                row.status === 'Uninstalling'\n            );\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.restart'),\n        click: (row: any) => {\n            openOperate(row, 'restart');\n        },\n        disabled: (row: any) => {\n            return (\n                row.status === 'DownloadErr' ||\n                row.status === 'Upgrading' ||\n                row.status === 'Rebuilding' ||\n                row.status === 'Uninstalling'\n            );\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.start'),\n        click: (row: any) => {\n            openOperate(row, 'start');\n        },\n        disabled: (row: any) => {\n            return (\n                row.status === 'Running' ||\n                row.status === 'Error' ||\n                row.status === 'DownloadErr' ||\n                row.status === 'Upgrading' ||\n                row.status === 'Rebuilding' ||\n                row.status === 'Uninstalling'\n            );\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.stop'),\n        click: (row: any) => {\n            openOperate(row, 'stop');\n        },\n        disabled: (row: any) => {\n            return (\n                row.status !== 'Running' ||\n                row.status === 'DownloadErr' ||\n                row.status === 'Upgrading' ||\n                row.status === 'Rebuilding' ||\n                row.status === 'Uninstalling'\n            );\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.uninstall'),\n        click: (row: any) => {\n            openOperate(row, 'delete');\n        },\n    },\n    {\n        label: i18n.global.t('app.params'),\n        click: (row: any) => {\n            openParam(row);\n        },\n        disabled: (row: any) => {\n            return (\n                row.status === 'DownloadErr' ||\n                row.status === 'Upgrading' ||\n                row.status === 'Rebuilding' ||\n                row.status === 'Uninstalling'\n            );\n        },\n    },\n];\n\nconst ignoreApp = (row: App.AppInstalled) => {\n    ignoreAppRef.value.acceptParams(row);\n};\n\nconst toContainer = async (row: App.AppInstalled) => {\n    routerToNameWithQuery('ContainerItem', {\n        filters: 'com.docker.compose.project=' + row.serviceName,\n        uncached: true,\n    });\n};\n\nconst openBackups = (row: App.AppInstalled) => {\n    let params = {\n        type: 'app',\n        name: row.appKey,\n        detailName: row.name,\n        status: row.status,\n        appInstallID: row.id,\n    };\n    backupRef.value.acceptParams(params);\n};\n\nconst openUploads = (key: string, name: string) => {\n    let params = {\n        type: 'app',\n        name: key,\n        detailName: name,\n    };\n    uploadRef.value.acceptParams(params);\n};\n\nconst openParam = (row: any) => {\n    appParamRef.value.acceptParams({ id: row.id });\n};\n\nconst openLog = (row: any) => {\n    switch (row.status) {\n        case 'Installing':\n            taskLogRef.value.openWithResourceID('App', 'TaskInstall', row.id);\n            break;\n        default:\n            composeLogRef.value.acceptParams({\n                compose: row.path + '/docker-compose.yml',\n                resource: row.name,\n                container: row.container,\n            });\n    }\n};\n\nconst openTerminal = (row: any) => {\n    const title = i18n.global.t('app.app') + ' ' + row.name;\n    dialogTerminalRef.value!.acceptParams({ containerID: row.container, title: title });\n};\n\nconst enterSortMode = async () => {\n    sortMode.value = true;\n    clearInterval(Number(timer));\n    timer = null;\n    const res = await searchAppInstalled({ page: 1, pageSize: 10000, name: '', tags: [], update: false, sync: false });\n    data.value = res.data.items;\n    await nextTick();\n    const el = sortContainer.value?.$el;\n    if (el) {\n        const favCount = data.value.filter((i: any) => i.favorite).length;\n        sortableInstance = Sortable.create(el, {\n            animation: 150,\n            ghostClass: 'sortable-ghost',\n            onMove: (evt: any) => {\n                const from = evt.dragged.dataset.sortIndex;\n                const to = evt.related.dataset.sortIndex;\n                const fromFav = Number(from) < favCount;\n                const toFav = Number(to) < favCount;\n                return fromFav === toFav;\n            },\n            onEnd: (evt: any) => {\n                const el = evt.from;\n                el.removeChild(evt.item);\n                el.insertBefore(evt.item, el.children[evt.oldIndex] || null);\n\n                const list = [...data.value];\n                const [moved] = list.splice(evt.oldIndex, 1);\n                list.splice(evt.newIndex, 0, moved);\n                data.value = list;\n            },\n        });\n    }\n};\n\nconst saveSortOrder = async () => {\n    if (!data.value) return;\n    let favIdx = 0;\n    let normalIdx = 0;\n    const items = data.value.map((item: any) => ({\n        installID: item.id,\n        sortOrder: item.favorite ? favIdx++ : normalIdx++,\n    }));\n    await updateAppInstallSort(items);\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    exitSortMode();\n};\n\nconst exitSortMode = () => {\n    if (sortableInstance) {\n        sortableInstance.destroy();\n        sortableInstance = null;\n    }\n    sortMode.value = false;\n    search();\n    timer = setInterval(() => {\n        search();\n    }, 1000 * 30);\n};\n\nconst getConfig = async () => {\n    try {\n        const res = await getAgentSettingByKey('SystemIP');\n        if (res.data != '') {\n            defaultLink.value = res.data;\n            return;\n        }\n        if (!isMaster.value || currentNodeAddr.value != '127.0.0.1') {\n            defaultLink.value = currentNodeAddr.value;\n        }\n    } catch (error) {}\n};\n\nonMounted(() => {\n    getConfig();\n    const path = router.currentRoute.value.path;\n    if (path == '/apps/upgrade') {\n        activeName.value = i18n.global.t('app.canUpgrade');\n        mode.value = 'upgrade';\n        searchReq.update = true;\n    }\n    loading.value = true;\n    search();\n    loading.value = false;\n    setTimeout(() => {\n        searchReq.sync = true;\n        search();\n    }, 1000);\n    timer = setInterval(() => {\n        search();\n    }, 1000 * 30);\n});\n\nonUnmounted(() => {\n    clearInterval(Number(timer));\n    timer = null;\n});\n</script>\n\n<style scoped lang=\"scss\">\n@use '../index';\n\n.d-button {\n    .el-button + .el-button {\n        margin-left: 0;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/upgrade/diff/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('app.composeDiff')\" @close=\"handleClose\" size=\"w-90\">\n        <div>\n            <el-text type=\"warning\">{{ $t('app.diffHelper') }}</el-text>\n            <div ref=\"container\" class=\"compose-diff\"></div>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"confirm(true)\">\n                    {{ $t('app.useNew') }}\n                </el-button>\n                <el-button @click=\"confirm(false)\">\n                    {{ $t('app.useDefault') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { nextTick, ref } from 'vue';\nimport * as monaco from 'monaco-editor';\n\nconst open = ref(false);\nconst newContent = ref('');\nconst oldContent = ref('');\nconst em = defineEmits(['confirm']);\n\nlet originalModel = null;\nlet modifiedModel = null;\nlet editor: monaco.editor.IStandaloneDiffEditor = null;\n\nconst container = ref();\n\nconst handleClose = () => {\n    open.value = false;\n    if (editor) {\n        editor.dispose();\n    }\n};\n\nconst acceptParams = (oldCompose: string, newCompose: string) => {\n    oldContent.value = oldCompose;\n    newContent.value = newCompose;\n    open.value = true;\n    initEditor();\n};\n\nconst confirm = (useEditor: boolean) => {\n    let content = '';\n    if (useEditor) {\n        content = editor.getModifiedEditor().getValue();\n    } else {\n        content = '';\n    }\n    em('confirm', content);\n    handleClose();\n};\n\nconst initEditor = () => {\n    nextTick(() => {\n        originalModel = monaco.editor.createModel(oldContent.value, 'yaml');\n        modifiedModel = monaco.editor.createModel(newContent.value, 'yaml');\n\n        editor = monaco.editor.createDiffEditor(container.value, {\n            theme: 'vs-dark',\n            readOnly: false,\n            automaticLayout: true,\n            folding: true,\n            roundedSelection: false,\n            overviewRulerBorder: false,\n        });\n\n        editor.setModel({\n            original: originalModel,\n            modified: modifiedModel,\n        });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped>\n.compose-diff {\n    width: 100%;\n    height: calc(100vh - 350px);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/installed/upgrade/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.' + operateReq.operate)\"\n        :resource=\"resourceName\"\n        @close=\"handleClose\"\n    >\n        <div v-loading=\"loading\">\n            <el-descriptions direction=\"vertical\">\n                <el-descriptions-item>\n                    <el-link @click=\"toLink(app.website)\">\n                        <el-icon><OfficeBuilding /></el-icon>\n                        <span>{{ $t('app.appOfficeWebsite') }}</span>\n                    </el-link>\n                </el-descriptions-item>\n                <el-descriptions-item>\n                    <el-link @click=\"toLink(app.document)\">\n                        <el-icon><Document /></el-icon>\n                        <span>{{ $t('app.document') }}</span>\n                    </el-link>\n                </el-descriptions-item>\n                <el-descriptions-item>\n                    <el-link @click=\"toLink(app.github)\">\n                        <el-icon><Link /></el-icon>\n                        <span>{{ $t('app.github') }}</span>\n                    </el-link>\n                </el-descriptions-item>\n            </el-descriptions>\n            <el-form @submit.prevent ref=\"updateRef\" :rules=\"rules\" label-position=\"top\" :model=\"operateReq\">\n                <el-form-item :label=\"$t('app.versionSelect')\" prop=\"detailId\">\n                    <el-select v-model=\"operateReq.version\" @change=\"getVersions(operateReq.version)\">\n                        <el-option\n                            v-if=\"operateReq.operate == 'ignore'\"\n                            :value=\"'all'\"\n                            :label=\"$t('commons.table.all') + $t('app.version')\"\n                        ></el-option>\n                        <el-option\n                            v-for=\"(version, index) in versions\"\n                            :key=\"index\"\n                            :value=\"version.version\"\n                            :label=\"version.version\"\n                        ></el-option>\n                    </el-select>\n                </el-form-item>\n                <el-alert\n                    v-if=\"showOpenclawHttpsUpgradeNotice\"\n                    type=\"warning\"\n                    :closable=\"false\"\n                    show-icon\n                    class=\"upgrade-notice\"\n                >\n                    <template #title>{{ $t('app.openclawHttpsUpgradeNoticeTitle') }}</template>\n                    <div class=\"upgrade-notice-content\">\n                        <div>1. {{ $t('app.openclawHttpsUpgradeNoticeItem1') }}</div>\n                        <div>2. {{ $t('app.openclawHttpsUpgradeNoticeItem2') }}</div>\n                    </div>\n                </el-alert>\n                <el-form-item prop=\"backup\" v-if=\"operateReq.operate === 'upgrade'\">\n                    <el-checkbox v-model=\"operateReq.backup\" :label=\"$t('app.backupApp')\" />\n                    <span class=\"input-help\">\n                        <el-text type=\"warning\">{{ $t('app.backupAppHelper') }}</el-text>\n                    </span>\n                </el-form-item>\n                <el-form-item prop=\"pullImage\" v-if=\"operateReq.operate === 'upgrade'\">\n                    <el-checkbox v-model=\"operateReq.pullImage\" :label=\"$t('app.pullImage')\" size=\"large\" />\n                    <span class=\"input-help\">{{ $t('app.pullImageHelper') }}</span>\n                </el-form-item>\n            </el-form>\n            <div v-if=\"operateReq.operate === 'upgrade'\">\n                <el-text type=\"danger\" v-if=\"isEdit\">{{ $t('app.isEdirWarn') }}</el-text>\n                <el-text type=\"warning\" v-else>{{ $t('app.upgradeWarn') }}</el-text>\n                <div>\n                    <el-button @click=\"openDiff()\" :type=\"isEdit ? 'warning' : 'primary'\">\n                        {{ $t('app.showDiff') }}\n                    </el-button>\n                </div>\n                <el-checkbox v-model=\"useNewCompose\" :label=\"$t('app.useCustom')\" size=\"large\" />\n                <div v-if=\"useNewCompose\">\n                    <el-text type=\"danger\" v-if=\"!isEdit\">{{ $t('app.useCustomHelper') }}</el-text>\n                    <CodemirrorPro v-model=\"newCompose\" mode=\"yaml\"></CodemirrorPro>\n                </div>\n            </div>\n        </div>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"onOperate\" :disabled=\"versions == null || loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n        <Diff ref=\"composeDiffRef\" @confirm=\"getNewCompose\" />\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" />\n</template>\n<script lang=\"ts\" setup>\nimport Diff from './diff/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\n\nimport { App } from '@/api/interface/app';\nimport { getAppUpdateVersions, ignoreUpgrade, installedOp } from '@/api/modules/app';\nimport { getAppStoreConfig } from '@/api/modules/setting';\nimport { compareVersion } from '@/utils/version';\nimport i18n from '@/lang';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { computed, onBeforeUnmount, reactive, ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport bus from '@/global/bus';\nimport { v4 as uuidv4 } from 'uuid';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { currentNode } = useGlobalStore();\nconst openclawHttpsVersion = '2026.3.13';\n\nconst composeDiffRef = ref();\nconst updateRef = ref<FormInstance>();\nconst open = ref(false);\nconst loading = ref(false);\nconst versions = ref<App.VersionDetail[]>();\nconst operateReq = reactive({\n    detailId: 0,\n    operate: 'upgrade',\n    installId: 0,\n    backup: true,\n    pullImage: true,\n    version: '',\n    dockerCompose: '',\n    taskID: '',\n});\nconst resourceName = ref('');\nconst rules = ref<any>({\n    detailId: [Rules.requiredSelect],\n});\nconst app = ref();\nconst oldContent = ref('');\nconst newContent = ref('');\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    em('close', open);\n};\n\nconst newCompose = ref('');\nconst useNewCompose = ref(false);\nconst appInstallID = ref(0);\nconst taskLogRef = ref();\nconst ignoreAppReq = reactive({\n    appID: 0,\n    appDetailID: 0,\n    scope: 'app',\n});\nconst isEdit = ref(false);\nconst node = ref('');\nconst currentVersion = ref('');\nconst currentAppKey = ref('');\n\nconst isOpenclawHttpsVersion = (version: string) => {\n    const target = String(version || '')\n        .trim()\n        .toLowerCase();\n    if (!target || target === 'latest') {\n        return true;\n    }\n    if (!/\\d/.test(target)) {\n        return false;\n    }\n    return compareVersion(target, openclawHttpsVersion);\n};\n\nconst isLegacyOpenclawVersion = (version: string) => {\n    const target = String(version || '')\n        .trim()\n        .toLowerCase();\n    if (!target || target === 'latest' || !/\\d/.test(target)) {\n        return false;\n    }\n    return !compareVersion(target, openclawHttpsVersion);\n};\n\nconst showOpenclawHttpsUpgradeNotice = computed(() => {\n    return (\n        operateReq.operate === 'upgrade' &&\n        currentAppKey.value === 'openclaw' &&\n        isLegacyOpenclawVersion(currentVersion.value) &&\n        isOpenclawHttpsVersion(operateReq.version)\n    );\n});\n\nconst toLink = (link: string) => {\n    window.open(link, '_blank');\n};\n\nconst openDiff = async () => {\n    if (newContent.value === '') {\n        await getVersions(operateReq.version);\n    }\n    composeDiffRef.value.acceptParams(oldContent.value, newContent.value);\n};\n\nconst getNewCompose = (compose: string) => {\n    if (compose !== '') {\n        newCompose.value = compose;\n        useNewCompose.value = true;\n    } else {\n        newCompose.value = newContent.value;\n        useNewCompose.value = false;\n    }\n};\n\nconst initData = async () => {\n    const config = await getAppStoreConfig(node.value);\n    newCompose.value = '';\n    useNewCompose.value = false;\n    operateReq.backup = config.data.upgradeBackup == 'Enable';\n    operateReq.pullImage = true;\n    operateReq.dockerCompose = '';\n};\n\nconst acceptParams = (appInstall: App.AppInstallDto, op: string, opNode?: string) => {\n    initData();\n    if (opNode) {\n        node.value = opNode;\n    } else {\n        node.value = currentNode.value;\n    }\n    isEdit.value = appInstall.isEdit;\n    currentVersion.value = appInstall.version;\n    currentAppKey.value = appInstall.appKey;\n    operateReq.installId = appInstall.id;\n    operateReq.operate = op;\n    resourceName.value = appInstall.name;\n    app.value = appInstall.app;\n    oldContent.value = appInstall.dockerCompose;\n    appInstallID.value = appInstall.id;\n    getVersions('');\n    ignoreAppReq.appID = appInstall.app.id;\n    if (op === 'ignore') {\n        operateReq.detailId = -1;\n    }\n    open.value = true;\n};\n\nconst getVersions = async (version: string) => {\n    const req = {\n        appInstallID: appInstallID.value,\n    };\n    if (version !== '') {\n        req['updateVersion'] = version;\n    }\n    try {\n        const res = await getAppUpdateVersions(req, node.value);\n        versions.value = res.data || [];\n        if (res.data != null && res.data.length > 0) {\n            let item = res.data[0];\n            if (version != '') {\n                item = res.data.find((v) => v.version === version);\n            }\n            operateReq.detailId = item.detailId;\n            operateReq.version = item.version;\n            newContent.value = item.dockerCompose;\n            newCompose.value = item.dockerCompose;\n            useNewCompose.value = false;\n        }\n    } catch (error) {}\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID, true, node.value);\n};\n\nconst operate = async () => {\n    loading.value = true;\n    if (operateReq.operate === 'upgrade') {\n        if (useNewCompose.value) {\n            operateReq.dockerCompose = newCompose.value;\n        }\n        const taskID = uuidv4();\n        operateReq.taskID = taskID;\n        await installedOp(operateReq, node.value)\n            .then(() => {\n                bus.emit('upgrade', true);\n                handleClose();\n                openTaskLog(taskID);\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    } else {\n        await ignoreUpgrade(ignoreAppReq)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                bus.emit('upgrade', true);\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    }\n};\n\nconst onOperate = async () => {\n    ElMessageBox.confirm(\n        i18n.global.t('app.operatorHelper', [i18n.global.t('commons.button.' + operateReq.operate)]),\n        i18n.global.t('commons.button.upgrade'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(() => {\n        operate();\n    });\n};\n\nonBeforeUnmount(() => {\n    bus.off('upgrade');\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.upgrade-notice {\n    margin-bottom: 16px;\n}\n\n.upgrade-notice-content {\n    line-height: 1.8;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/app-store/setting/index.vue",
    "content": "<template>\n    <LayoutContent :title=\"$t('commons.button.set')\">\n        <template #main>\n            <el-form\n                :model=\"config\"\n                label-position=\"left\"\n                label-width=\"260px\"\n                class=\"ml-2.5 app-setting-form\"\n                v-loading=\"loading\"\n                :rules=\"rules\"\n                ref=\"configForm\"\n            >\n                <el-row>\n                    <el-col :xs=\"24\" :sm=\"20\" :md=\"15\" :lg=\"12\" :xl=\"12\">\n                        <el-form-item :label=\"$t('app.uninstallDeleteBackup')\" prop=\"uninstallDeleteBackup\">\n                            <el-switch\n                                v-model=\"config.uninstallDeleteBackup\"\n                                active-value=\"Enable\"\n                                inactive-value=\"Disable\"\n                                :loading=\"loading\"\n                                @change=\"updateConfig('UninstallDeleteBackup', config.uninstallDeleteBackup)\"\n                            />\n                        </el-form-item>\n                        <el-form-item :label=\"$t('app.uninstallDeleteImage')\" prop=\"uninstallDeleteImage\">\n                            <el-switch\n                                v-model=\"config.uninstallDeleteImage\"\n                                active-value=\"Enable\"\n                                inactive-value=\"Disable\"\n                                :loading=\"loading\"\n                                @change=\"updateConfig('UninstallDeleteImage', config.uninstallDeleteImage)\"\n                            />\n                        </el-form-item>\n                        <el-form-item :label=\"$t('app.upgradeBackup')\" prop=\"upgradeBackup\">\n                            <el-switch\n                                v-model=\"config.upgradeBackup\"\n                                active-value=\"Enable\"\n                                inactive-value=\"Disable\"\n                                :loading=\"loading\"\n                                @change=\"updateConfig('UpgradeBackup', config.upgradeBackup)\"\n                            />\n                        </el-form-item>\n                        <CustomSetting v-if=\"isProductPro\" />\n                        <span class=\"input-help logText\" v-else>\n                            {{ $t('xpack.customApp.licenseHelper') }}\n                            <el-link class=\"link\" @click=\"toUpload\" type=\"primary\">\n                                {{ $t('license.levelUpPro') }}\n                            </el-link>\n                        </span>\n                    </el-col>\n                </el-row>\n            </el-form>\n        </template>\n    </LayoutContent>\n    <LicenseImport ref=\"licenseRef\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { getCurrentNodeCustomAppConfig } from '@/api/modules/app';\nimport { getAppStoreConfig, updateAppStoreConfig } from '@/api/modules/setting';\nimport { FormRules } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { defineAsyncComponent } from 'vue';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { isProductPro, isMasterProductPro } = useGlobalStore();\n\nconst CustomSetting = defineAsyncComponent(async () => {\n    const modules = import.meta.glob('@/xpack/views/appstore/index.vue');\n    const loader = modules['/src/xpack/views/appstore/index.vue'];\n    if (loader) {\n        return ((await loader()) as any).default;\n    }\n    return { template: '<div></div>' };\n});\n\nconst rules = ref<FormRules>({});\nconst config = ref({\n    uninstallDeleteImage: '',\n    uninstallDeleteBackup: '',\n    upgradeBackup: '',\n});\nconst loading = ref(false);\nconst configForm = ref();\nconst useCustomApp = ref(false);\nconst isInitializing = ref(true);\nconst licenseRef = ref();\n\nconst search = async () => {\n    loading.value = true;\n    try {\n        const res = await getAppStoreConfig();\n        if (res && res.data) {\n            isInitializing.value = true;\n            config.value = res.data;\n            setTimeout(() => {\n                isInitializing.value = false;\n            }, 0);\n        }\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst toUpload = () => {\n    licenseRef.value.acceptParams();\n};\n\nconst getNodeConfig = async () => {\n    if (isMasterProductPro.value) {\n        return;\n    }\n    const res = await getCurrentNodeCustomAppConfig();\n    if (res && res.data) {\n        useCustomApp.value = res.data.status === 'enable';\n    }\n};\n\nconst updateConfig = async (scope: string, value: string) => {\n    if (isInitializing.value) {\n        return;\n    }\n    loading.value = true;\n    try {\n        const req = {\n            scope: scope,\n            status: value,\n        };\n        await updateAppStoreConfig(req);\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        search();\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nonMounted(() => {\n    search();\n    getNodeConfig();\n});\n</script>\n\n<style lang=\"css\" scoped>\n.app-setting-form :deep(.el-form-item__label) {\n    white-space: nowrap;\n}\n\n.logText {\n    line-height: 22px;\n    font-size: 12px;\n    .link {\n        font-size: 12px !important;\n        margin-top: -3px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/compose/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.delete') + ' - ' + composeName\" size=\"small\">\n        <el-form ref=\"deleteForm\" v-loading=\"loading\">\n            <el-form-item>\n                <el-checkbox v-model=\"deleteFile\" :label=\"$t('container.deleteFile')\" />\n                <span class=\"input-help whitespace-break-spaces\">\n                    {{ $t('container.deleteComposeHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"force\" :label=\"$t('website.forceDelete')\" />\n                <span class=\"input-help\">\n                    {{ $t('website.forceDeleteHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <div class=\"font\">\n                    <span>{{ $t('database.delete') }}</span>\n                    <span class=\"warning\">{{ composeName }}</span>\n                    <span>{{ $t('container.deleteCompose') }}</span>\n                </div>\n                <el-input v-model=\"deleteInfo\" :placeholder=\"composeName\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"deleteInfo != composeName || loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { composeOperate } from '@/api/modules/container';\n\nlet open = ref(false);\nlet loading = ref(false);\nlet deleteInfo = ref('');\n\nconst deleteFile = ref();\nconst force = ref();\nconst composeName = ref();\nconst composePath = ref();\n\nconst deleteForm = ref<FormInstance>();\n\ninterface DialogProps {\n    name: string;\n    path: string;\n}\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst acceptParams = async (prop: DialogProps) => {\n    deleteFile.value = false;\n    force.value = false;\n    composeName.value = prop.name;\n    composePath.value = prop.path;\n    deleteInfo.value = '';\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    let params = {\n        name: composeName.value,\n        path: composePath.value,\n        operation: 'delete',\n        withFile: deleteFile.value,\n        force: force.value,\n    };\n    await composeOperate(params)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            open.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.font {\n    font-size: 12px;\n    .warning {\n        color: red;\n        font-weight: 500;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/compose/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status\n            v-model:isActive=\"isActive\"\n            v-model:isExist=\"isExist\"\n            v-model:loading=\"loading\"\n            @search=\"search(true)\"\n        />\n\n        <LayoutContent v-if=\"isExist\" :title=\"$t('container.compose', 2)\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog()\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onImportCompose()\">\n                    {{ $t('commons.button.import') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"container-refresh\" @search=\"refresh()\" />\n            </template>\n            <template #main>\n                <el-row v-if=\"data.length > 0 || isOnCreate\" :gutter=\"20\" class=\"row-box\">\n                    <el-col :xs=\"24\" :sm=\"24\" :md=\"8\" :lg=\"8\" :xl=\"6\">\n                        <el-card>\n                            <el-table\n                                :max-height=\"loadTableHeight()\"\n                                :show-header=\"false\"\n                                @row-click=\"(row, column, event) => loadDetail(row, true)\"\n                                :data=\"data\"\n                            >\n                                <el-table-column prop=\"name\">\n                                    <template #default=\"{ row }\">\n                                        <div\n                                            class=\"compose-item\"\n                                            :class=\"{ 'is-active': currentCompose?.name === row.name && !isOnCreate }\"\n                                        >\n                                            <div class=\"font-medium text-base compose-title\">\n                                                {{ row.name }}\n                                            </div>\n                                            <div class=\"mb-1\">\n                                                <el-text class=\"w-12\" link size=\"small\" type=\"info\">\n                                                    {{ loadFrom(row) }}\n                                                </el-text>\n                                                <el-divider direction=\"vertical\" />\n                                                <el-text link size=\"small\" type=\"info\" class=\"ml-2\">\n                                                    {{ row.createdAt }}\n                                                </el-text>\n                                                <el-divider direction=\"vertical\" />\n                                                <el-text\n                                                    link\n                                                    v-if=\"row.containerCount === 0\"\n                                                    type=\"danger\"\n                                                    size=\"small\"\n                                                >\n                                                    {{ $t('container.exited') }}\n                                                </el-text>\n                                                <el-text\n                                                    link\n                                                    v-else\n                                                    :type=\"\n                                                        row.containerCount === row.runningCount ? 'success' : 'warning'\n                                                    \"\n                                                    size=\"small\"\n                                                >\n                                                    {{\n                                                        $t('container.running', [row.runningCount, row.containerCount])\n                                                    }}\n                                                </el-text>\n                                            </div>\n                                            <div class=\"compose-actions\">\n                                                <el-dropdown placement=\"bottom\">\n                                                    <Status\n                                                        v-if=\"row.containerCount === row.runningCount\"\n                                                        status=\"running\"\n                                                        :operate=\"true\"\n                                                    />\n                                                    <Status\n                                                        v-if=\"row.runningCount === 0\"\n                                                        status=\"exited\"\n                                                        :operate=\"true\"\n                                                    />\n                                                    <template #dropdown>\n                                                        <el-dropdown-menu>\n                                                            <el-dropdown-item\n                                                                :disabled=\"row.containerCount === row.runningCount\"\n                                                                @click=\"handleComposeOperate('up', row)\"\n                                                            >\n                                                                {{ $t('commons.operate.start') }}\n                                                            </el-dropdown-item>\n                                                            <el-dropdown-item\n                                                                :disabled=\"row.runningCount === 0\"\n                                                                @click=\"handleComposeOperate('stop', row)\"\n                                                            >\n                                                                {{ $t('commons.operate.stop') }}\n                                                            </el-dropdown-item>\n                                                            <el-dropdown-item\n                                                                @click=\"handleComposeOperate('restart', row)\"\n                                                            >\n                                                                {{ $t('commons.button.restart') }}\n                                                            </el-dropdown-item>\n                                                        </el-dropdown-menu>\n                                                    </template>\n                                                </el-dropdown>\n                                                <el-button\n                                                    plain\n                                                    round\n                                                    size=\"small\"\n                                                    class=\"round-btn ml-3\"\n                                                    :disabled=\"!row?.workdir\"\n                                                    @click=\"openComposeFolder(row)\"\n                                                >\n                                                    {{ $t('home.dir') }}\n                                                </el-button>\n                                                <el-button\n                                                    plain\n                                                    round\n                                                    size=\"small\"\n                                                    class=\"round-btn\"\n                                                    @click=\"onBackupList(row)\"\n                                                >\n                                                    {{ $t('commons.button.backup') }}\n                                                </el-button>\n                                                <el-button\n                                                    plain\n                                                    round\n                                                    size=\"small\"\n                                                    class=\"round-btn\"\n                                                    @click=\"onDelete(row)\"\n                                                >\n                                                    {{ $t('commons.operate.delete') }}\n                                                </el-button>\n                                            </div>\n                                        </div>\n                                    </template>\n                                </el-table-column>\n                            </el-table>\n                        </el-card>\n                    </el-col>\n                    <el-col :xs=\"24\" :sm=\"24\" :md=\"16\" :lg=\"16\" :xl=\"18\">\n                        <el-card v-if=\"currentCompose && !isOnCreate\" v-loading=\"detailLoading\">\n                            <div class=\"flex items-center justify-between\">\n                                <div class=\"flex items-center gap-2.5\">\n                                    <span class=\"text-base font-medium\">{{ currentCompose.name }}</span>\n                                    <el-tag size=\"small\" effect=\"plain\" round>\n                                        {{ loadFrom(currentCompose) }}\n                                    </el-tag>\n                                    <el-divider direction=\"vertical\" />\n                                    <el-text size=\"small\" type=\"info\">{{ currentCompose.createdAt }}</el-text>\n                                </div>\n                                <el-tag v-if=\"currentCompose.containerCount === 0\" type=\"danger\" effect=\"light\" round>\n                                    {{ $t('container.exited') }}\n                                </el-tag>\n                                <el-tag\n                                    v-else\n                                    :type=\"\n                                        currentCompose.containerCount === currentCompose.runningCount\n                                            ? 'success'\n                                            : 'warning'\n                                    \"\n                                    effect=\"light\"\n                                    round\n                                >\n                                    {{\n                                        $t('container.running', [\n                                            currentCompose.runningCount,\n                                            currentCompose.containerCount,\n                                        ])\n                                    }}\n                                </el-tag>\n                            </div>\n\n                            <el-divider class=\"!my-3\" />\n\n                            <el-table\n                                v-if=\"composeContainers.length > 0\"\n                                :data=\"tableData\"\n                                size=\"small\"\n                                max-height=\"230\"\n                                stripe\n                                class=\"compose-container-table\"\n                            >\n                                <el-table-column\n                                    :label=\"$t('commons.table.name')\"\n                                    prop=\"name\"\n                                    show-overflow-tooltip\n                                    fixed=\"left\"\n                                >\n                                    <template #default=\"{ row }\">\n                                        <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"onInspectContainer(row)\">\n                                            {{ row.name }}\n                                        </el-text>\n                                    </template>\n                                </el-table-column>\n                                <el-table-column :label=\"$t('commons.table.status')\" prop=\"state\" width=\"120\">\n                                    <template #default=\"{ row }\">\n                                        <Status :key=\"row.state\" :status=\"row.state\"></Status>\n                                    </template>\n                                </el-table-column>\n                                <el-table-column :label=\"$t('container.source')\" show-overflow-tooltip prop=\"resource\">\n                                    <template #default=\"{ row }\">\n                                        <div v-if=\"row.hasLoad\">\n                                            <div class=\"source-font\">CPU: {{ row.cpuPercent.toFixed(2) }}%</div>\n                                            <div class=\"float-left source-font\">\n                                                {{ $t('monitor.memory') }}: {{ row.memoryPercent.toFixed(2) }}%\n                                            </div>\n                                            <el-popover placement=\"right\" width=\"500px\" class=\"float-right\">\n                                                <template #reference>\n                                                    <svg-icon iconName=\"p-xiangqing\" class=\"svg-icon\"></svg-icon>\n                                                </template>\n                                                <template #default>\n                                                    <el-descriptions\n                                                        direction=\"vertical\"\n                                                        border\n                                                        :column=\"3\"\n                                                        size=\"small\"\n                                                    >\n                                                        <el-descriptions-item :label=\"$t('container.cpuUsage')\">\n                                                            {{ computeCPU(row.cpuTotalUsage) }}\n                                                        </el-descriptions-item>\n                                                        <el-descriptions-item :label=\"$t('container.cpuTotal')\">\n                                                            {{ computeCPU(row.systemUsage) }}\n                                                        </el-descriptions-item>\n                                                        <el-descriptions-item :label=\"$t('container.core')\">\n                                                            {{ row.percpuUsage }}\n                                                        </el-descriptions-item>\n\n                                                        <el-descriptions-item :label=\"$t('container.memUsage')\">\n                                                            {{ computeSizeForDocker(row.memoryUsage) }}\n                                                        </el-descriptions-item>\n                                                        <el-descriptions-item :label=\"$t('container.memCache')\">\n                                                            {{ computeSizeForDocker(row.memoryCache) }}\n                                                        </el-descriptions-item>\n                                                        <el-descriptions-item :label=\"$t('container.memTotal')\">\n                                                            {{ computeSizeForDocker(row.memoryLimit) }}\n                                                        </el-descriptions-item>\n                                                        <el-descriptions-item v-if=\"row.hasLoadSize\">\n                                                            <template #label>\n                                                                {{ $t('container.sizeRw') }}\n                                                                <el-tooltip :content=\"$t('container.sizeRwHelper')\">\n                                                                    <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                                                </el-tooltip>\n                                                            </template>\n                                                            {{ computeSize2(row.sizeRw) }}\n                                                        </el-descriptions-item>\n                                                        <el-descriptions-item\n                                                            :label=\"$t('container.sizeRootFs')\"\n                                                            v-if=\"row.hasLoadSize\"\n                                                        >\n                                                            <template #label>\n                                                                {{ $t('container.sizeRootFs') }}\n                                                                <el-tooltip :content=\"$t('container.sizeRootFsHelper')\">\n                                                                    <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                                                </el-tooltip>\n                                                            </template>\n                                                            {{ computeSize2(row.sizeRootFs) }}\n                                                        </el-descriptions-item>\n                                                    </el-descriptions>\n\n                                                    <el-button\n                                                        class=\"mt-2\"\n                                                        v-if=\"!row.hasLoadSize\"\n                                                        size=\"small\"\n                                                        link\n                                                        type=\"primary\"\n                                                        @click=\"loadSize(row)\"\n                                                    >\n                                                        {{ $t('container.loadSize') }}\n                                                    </el-button>\n                                                </template>\n                                            </el-popover>\n                                        </div>\n                                        <div v-if=\"!row.hasLoad\">\n                                            <el-button link loading></el-button>\n                                        </div>\n                                    </template>\n                                </el-table-column>\n                                <el-table-column :label=\"$t('commons.table.operate')\" width=\"160\">\n                                    <template #default=\"{ row }\">\n                                        <el-button type=\"primary\" link @click=\"onOpenTerminal(row)\">\n                                            {{ $t('menu.terminal') }}\n                                        </el-button>\n                                        <el-button type=\"primary\" link @click=\"onOpenLog(row)\">\n                                            {{ $t('commons.button.log') }}\n                                        </el-button>\n                                    </template>\n                                </el-table-column>\n                            </el-table>\n\n                            <el-divider v-if=\"composeContainers.length > 0\" class=\"!my-2\" />\n\n                            <div class=\"flex items-center justify-between mb-2\">\n                                <el-radio-group v-model=\"showType\">\n                                    <el-radio-button value=\"compose\">{{ $t('container.compose') }}</el-radio-button>\n                                    <el-radio-button value=\"log\">{{ $t('commons.button.log') }}</el-radio-button>\n                                    <el-radio-button value=\"env\">{{ $t('container.env') }}</el-radio-button>\n                                </el-radio-group>\n                                <el-button\n                                    v-if=\"showType !== 'log' && !(showType === 'env' && isAppStoreCompose)\"\n                                    type=\"primary\"\n                                    @click=\"onSubmitEdit\"\n                                >\n                                    {{ $t('commons.button.save') }}\n                                </el-button>\n                            </div>\n                            <el-select\n                                class=\"p-w-300 mb-2\"\n                                v-model=\"currentYamlPath\"\n                                @change=\"inspectCompose(currentCompose.name, currentYamlPath)\"\n                                v-if=\"currentCompose.path.indexOf(',') !== -1\"\n                            >\n                                <template #prefix>{{ $t('container.composeFile') }}</template>\n                                <el-option\n                                    v-for=\"item in currentCompose.path.split(',')\"\n                                    :key=\"item\"\n                                    :value=\"item\"\n                                    :label=\"item.split('/').pop()\"\n                                />\n                            </el-select>\n                            <div v-show=\"showType === 'compose'\">\n                                <CodemirrorPro\n                                    v-model=\"composeContent\"\n                                    mode=\"yaml\"\n                                    :heightDiff=\"475\"\n                                    :placeholder=\"$t('container.composeContentPlaceholder')\"\n                                />\n                            </div>\n\n                            <div v-show=\"showType === 'log'\">\n                                <ContainerLog\n                                    :key=\"currentCompose.path\"\n                                    :compose=\"currentCompose.path\"\n                                    :resource=\"currentCompose.name\"\n                                    :highlightDiff=\"450\"\n                                    :defaultFollow=\"true\"\n                                />\n                            </div>\n\n                            <div v-show=\"showType === 'env'\">\n                                <el-input\n                                    placeholder=\"key=value\"\n                                    type=\"textarea\"\n                                    :rows=\"18\"\n                                    :readonly=\"isAppStoreCompose\"\n                                    v-model=\"env\"\n                                />\n                                <span v-if=\"isAppStoreCompose\" class=\"input-help\">\n                                    {{ $t('container.composeEnvHelper2') }}\n                                </span>\n                            </div>\n                        </el-card>\n                        <el-card v-else>\n                            <el-form\n                                ref=\"formRef\"\n                                @submit.prevent\n                                label-position=\"top\"\n                                :model=\"form\"\n                                :rules=\"rules\"\n                                v-loading=\"detailLoading\"\n                            >\n                                <el-form-item :label=\"$t('app.source')\">\n                                    <el-radio-group v-model=\"form.from\" @change=\"onEdit('form')\">\n                                        <el-radio value=\"edit\">{{ $t('commons.button.edit') }}</el-radio>\n                                        <el-radio value=\"path\">{{ $t('container.pathSelect') }}</el-radio>\n                                        <el-radio value=\"template\">{{ $t('container.composeTemplate') }}</el-radio>\n                                    </el-radio-group>\n                                </el-form-item>\n                                <el-form-item v-if=\"form.from === 'path'\" prop=\"path\">\n                                    <el-input\n                                        @change=\"loadDir(form.path)\"\n                                        :placeholder=\"$t('commons.example') + '/tmp/docker-compose.yml'\"\n                                        v-model=\"form.path\"\n                                    >\n                                        <template #prepend>\n                                            <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: false })\" />\n                                        </template>\n                                    </el-input>\n                                </el-form-item>\n                                <el-form-item v-if=\"form.from === 'template'\" prop=\"template\">\n                                    <el-select v-model=\"form.template\" @change=\"onEdit('template')\">\n                                        <template #prefix>{{ $t('container.template') }}</template>\n                                        <el-option\n                                            v-for=\"item in templateOptions\"\n                                            :key=\"item.id\"\n                                            :value=\"item.id\"\n                                            :label=\"item.name\"\n                                        />\n                                    </el-select>\n                                </el-form-item>\n                                <el-form-item v-if=\"form.from === 'edit' || form.from === 'template'\" prop=\"name\">\n                                    <el-input @input=\"changePath\" @change=\"onEdit('')\" v-model.trim=\"form.name\">\n                                        <template #prefix>\n                                            <span style=\"margin-right: 8px\">{{ $t('file.dir') }}</span>\n                                        </template>\n                                    </el-input>\n                                    <span class=\"input-help\">\n                                        {{ $t('container.composePathHelper', [composeFile]) }}\n                                    </span>\n                                </el-form-item>\n                                <el-form-item>\n                                    <div v-if=\"form.from === 'edit' || form.from === 'template'\" class=\"w-full\">\n                                        <CodemirrorPro\n                                            v-model=\"form.file\"\n                                            :placeholder=\"$t('container.composeContentPlaceholder')\"\n                                            mode=\"yaml\"\n                                            :heightDiff=\"400\"\n                                        ></CodemirrorPro>\n                                    </div>\n                                </el-form-item>\n                                <span class=\"envTitle\">{{ $t('container.env') }}</span>\n                                <el-input placeholder=\"key=value\" type=\"textarea\" :rows=\"3\" v-model=\"form.env\" />\n                                <span class=\"envTitle\">{{ $t('commons.button.set') }}</span>\n                                <el-form-item>\n                                    <el-checkbox v-model=\"form.forcePull\" :label=\"$t('container.forcePull')\" />\n                                    <span class=\"input-help\">{{ $t('container.forcePullHelper') }}</span>\n                                </el-form-item>\n                            </el-form>\n\n                            <el-button type=\"primary\" class=\"mt-2\" @click=\"onSubmit(formRef)\">\n                                {{ $t('commons.button.save') }}\n                            </el-button>\n                        </el-card>\n                    </el-col>\n                </el-row>\n                <el-empty v-else :description=\"$t('commons.msg.noneData')\" />\n            </template>\n        </LayoutContent>\n\n        <TaskLog ref=\"taskLogRef\" width=\"70%\">\n            <template #task-footer>\n                <el-button @click=\"handleClose\">{{ $t('commons.table.backToList') }}</el-button>\n                <el-button type=\"primary\" @click=\"closeTask\">{{ $t('commons.table.keepEdit') }}</el-button>\n            </template>\n        </TaskLog>\n        <FileList ref=\"fileRef\" @choose=\"loadDir\" />\n        <DeleteDialog @search=\"search(true)\" ref=\"dialogDelRef\" />\n        <ContainerInspectDialog ref=\"containerInspectRef\" />\n        <TerminalDialog ref=\"terminalDialogRef\" />\n        <ContainerLogDialog ref=\"containerLogDialogRef\" :highlightDiff=\"210\" />\n        <Backups ref=\"dialogBackupRef\" @close=\"search(true)\" />\n        <Uploads ref=\"uploadRef\" @close=\"search(true)\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, h, ref } from 'vue';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport ContainerLog from '@/components/log/container/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport FileList from '@/components/file-list/index.vue';\nimport ContainerInspectDialog from '@/views/container/container/inspect/index.vue';\nimport TerminalDialog from '@/views/container/container/terminal/index.vue';\nimport ContainerLogDialog from '@/components/log/container-drawer/index.vue';\nimport DeleteDialog from '@/views/container/compose/delete/index.vue';\nimport Backups from '@/components/backup/index.vue';\nimport Uploads from '@/components/upload/index.vue';\nimport {\n    composeOperate,\n    composeUpdate,\n    containerItemStats,\n    containerListStats,\n    inspect,\n    listComposeTemplate,\n    loadComposeEnv,\n    searchCompose,\n    testCompose,\n    upCompose,\n} from '@/api/modules/container';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport i18n from '@/lang';\nimport { Container } from '@/api/interface/container';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { computeCPU, computeSize2, computeSizeForDocker, newUUID } from '@/utils/util';\nimport { Rules } from '@/global/form-rules';\nimport { loadBaseDir } from '@/api/modules/setting';\nimport { ElCheckbox, ElForm } from 'element-plus';\nimport { GlobalStore } from '@/store';\n\nconst globalStore = GlobalStore();\n\nconst data = ref<any[]>([]);\nconst loading = ref(false);\nconst detailLoading = ref(false);\nconst currentCompose = ref<Container.ComposeInfo | null>(null);\nconst currentYamlPath = ref('');\nconst composeContainers = ref([]);\nconst composeContent = ref('');\n\nconst dialogDelRef = ref();\nconst containerInspectRef = ref();\nconst terminalDialogRef = ref();\nconst containerLogDialogRef = ref();\nconst dialogBackupRef = ref();\nconst uploadRef = ref();\n\nconst searchName = ref('');\nconst showType = ref('compose');\nconst containerStats = ref<any[]>([]);\nconst env = ref();\n\nconst isOnCreate = ref();\nconst oldFrom = ref('edit');\nconst templateOptions = ref();\nconst baseDir = ref();\nconst composeFile = ref();\nconst taskLogRef = ref();\nconst fileRef = ref();\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\nconst form = reactive({\n    taskID: '',\n    name: '',\n    from: 'edit',\n    path: '',\n    file: '',\n    template: null as number,\n    env: '',\n    forcePull: false,\n});\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.composeName],\n    path: [Rules.requiredInput],\n    template: [Rules.requiredSelect],\n});\n\nconst isActive = ref(false);\nconst isExist = ref(false);\nconst isAppStoreCompose = computed(() => currentCompose.value?.createdBy === 'Apps');\n\nconst tableData = computed(() => {\n    return composeContainers.value.map((container) => {\n        const stats = containerStats.value.find((s) => s.containerID === container.containerID);\n        return {\n            ...container,\n            hasLoad: !!stats,\n            cpuPercent: stats?.cpuPercent || 0,\n            memoryPercent: stats?.memoryPercent || 0,\n            cpuTotalUsage: stats?.cpuTotalUsage || 0,\n            systemUsage: stats?.systemUsage || 0,\n            percpuUsage: stats?.percpuUsage || 0,\n            memoryCache: stats?.memoryCache || 0,\n            memoryUsage: stats?.memoryUsage || 0,\n            memoryLimit: stats?.memoryLimit || 0,\n            sizeRw: stats?.sizeRw || 0,\n            sizeRootFs: stats?.sizeRootFs || 0,\n        };\n    });\n});\n\nconst loadFrom = (row: any) => {\n    switch (row.createdBy) {\n        case '1Panel':\n            return '1Panel';\n        case 'Apps':\n            return i18n.global.t('menu.apps');\n        default:\n            return i18n.global.t('commons.table.local');\n    }\n};\n\nconst loadTableHeight = () => {\n    if (currentCompose.value?.createdBy === '1Panel') {\n        return `calc(100vh - 120px)`;\n    } else {\n        return `calc(100vh - 240px)`;\n    }\n};\n\nconst refresh = async () => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    let params = {\n        info: searchName.value,\n        page: 1,\n        pageSize: 100,\n    };\n    await searchCompose(params).then((res) => {\n        data.value = res.data.items || [];\n    });\n};\n\nconst search = async (withRefreshDetail?: boolean) => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    let params = {\n        info: searchName.value,\n        page: 1,\n        pageSize: 100,\n    };\n    loading.value = true;\n    await searchCompose(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            if (data.value.length > 0 && withRefreshDetail) {\n                loadDetail(data.value[0], true);\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst loadDetail = async (row: Container.ComposeInfo, withRefresh: boolean) => {\n    if (currentCompose.value?.name === row.name && withRefresh !== true) {\n        return;\n    }\n    form.forcePull = false;\n    isOnCreate.value = false;\n    detailLoading.value = true;\n    currentCompose.value = row;\n    currentYamlPath.value = row.path.indexOf(',') !== -1 ? row.path.split(',')[0] : row.path;\n    env.value = row.env || '';\n    composeContainers.value = row.containers || [];\n    inspectCompose(row.name, currentYamlPath.value);\n};\n\nconst inspectCompose = async (name: string, detailPath: string) => {\n    await inspect({ id: name, type: 'compose', detail: detailPath })\n        .then((res) => {\n            composeContent.value = res.data;\n            detailLoading.value = false;\n        })\n        .finally(() => {\n            loadContainerStats();\n            detailLoading.value = false;\n        });\n};\n\nconst loadContainerStats = async () => {\n    try {\n        const res = await containerListStats();\n        containerStats.value = res.data || [];\n    } catch (error) {\n        containerStats.value = [];\n    }\n};\n\nconst onOpenDialog = async () => {\n    isOnCreate.value = true;\n    loadTemplates();\n    form.name = '';\n    form.from = 'edit';\n    form.path = '';\n    form.file = '';\n    form.template = null;\n    form.env = '';\n    form.forcePull = false;\n    loadPath();\n    loadTemplates();\n};\nconst onEdit = (item: string) => {\n    if (item === 'template') {\n        changeTemplate();\n    }\n    if (item === 'form') {\n        changeFrom();\n    }\n};\nconst changeTemplate = () => {\n    for (const item of templateOptions.value) {\n        if (form.template === item.id) {\n            form.file = item.content;\n            break;\n        }\n    }\n};\nconst changeFrom = () => {\n    if ((oldFrom.value === 'edit' || oldFrom.value === 'template') && form.file) {\n        ElMessageBox.confirm(i18n.global.t('container.fromChangeHelper'), i18n.global.t('app.source'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        })\n            .then(() => {\n                if (oldFrom.value === 'template') {\n                    form.template = null;\n                    form.file = '';\n                }\n                if (oldFrom.value === 'edit') {\n                    form.file = '';\n                }\n                oldFrom.value = form.from;\n            })\n            .catch(() => {\n                form.from = oldFrom.value;\n            });\n    } else {\n        oldFrom.value = form.from;\n    }\n};\nconst loadTemplates = async () => {\n    const res = await listComposeTemplate();\n    templateOptions.value = res.data;\n};\nconst loadPath = async () => {\n    const pathRes = await loadBaseDir();\n    baseDir.value = pathRes.data;\n    changePath();\n};\nconst changePath = async () => {\n    composeFile.value = baseDir.value + '/docker/compose/' + form.name;\n};\nconst loadDir = async (path: string) => {\n    form.path = path;\n    await loadComposeEnv(path).then((res) => {\n        form.env = res.data || '';\n    });\n};\nconst handleClose = () => {\n    search(true);\n    taskLogRef.value?.handleClose();\n};\nconst closeTask = () => {\n    taskLogRef.value?.handleClose();\n};\n\nconst onDelete = (row: any) => {\n    dialogDelRef.value.acceptParams({\n        name: row.name,\n        path: row.path,\n    });\n};\n\nconst onBackupList = (row: Container.ComposeInfo) => {\n    dialogBackupRef.value?.acceptParams({\n        type: 'compose',\n        name: row.name,\n        detailName: '',\n        node: globalStore.currentNode,\n    });\n};\n\nconst onImportCompose = () => {\n    uploadRef.value?.acceptParams({\n        type: 'compose',\n        name: '',\n        detailName: '',\n        remark: '.tar.gz',\n        node: globalStore.currentNode,\n    });\n};\n\nconst handleComposeOperate = async (operation: 'up' | 'stop' | 'restart', row: any) => {\n    const mes = i18n.global.t('container.composeOperatorHelper', [\n        row.name,\n        i18n.global.t('commons.operate.' + operation),\n    ]);\n    ElMessageBox.confirm(mes, i18n.global.t('commons.operate.' + operation), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        const params = {\n            name: row.name,\n            path: row.path,\n            operation: operation,\n            withFile: false,\n            force: false,\n        };\n        await composeOperate(params)\n            .then(async () => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                await search();\n                if (currentCompose.value) {\n                    const updated = data.value.find((item) => item.name === currentCompose.value.name);\n                    if (updated) {\n                        await loadDetail(updated, true);\n                    }\n                }\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst loadSize = async (row: any) => {\n    containerItemStats(row.containerID).then((res) => {\n        row.sizeRw = res.data.sizeRw || 0;\n        row.sizeRootFs = res.data.sizeRootFs || 0;\n        row.hasLoadSize = true;\n    });\n};\n\nconst onSubmitEdit = async () => {\n    const forcePull = ref(false);\n    try {\n        await ElMessageBox({\n            title: i18n.global.t('commons.button.save'),\n            message: h('div', { class: 'w-full' }, [\n                h(\n                    ElCheckbox,\n                    {\n                        onChange: (value: string | number | boolean) => {\n                            forcePull.value = Boolean(value);\n                        },\n                    },\n                    {\n                        default: () => i18n.global.t('container.forcePull'),\n                    },\n                ),\n                h('div', { class: 'input-help mt-1' }, i18n.global.t('container.forcePullHelper')),\n                h('div', { class: 'mt-2 leading-6' }, i18n.global.t('container.updateHelper4')),\n            ]),\n            showCancelButton: true,\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            closeOnClickModal: false,\n        });\n    } catch {\n        return;\n    }\n\n    const taskID = newUUID();\n    const param = {\n        taskID: taskID,\n        name: currentCompose.value.name,\n        path: currentCompose.value.path,\n        detailPath: currentYamlPath.value,\n        content: composeContent.value,\n        createdBy: currentCompose.value.createdBy,\n        env: env.value || '',\n        forcePull: forcePull.value,\n    };\n    loading.value = true;\n    await composeUpdate(param)\n        .then(async () => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            openTaskLog(taskID);\n            await search();\n            if (currentCompose.value) {\n                const updated = data.value.find((item) => item.name === currentCompose.value.name);\n                if (updated) {\n                    await loadDetail(updated, true);\n                }\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if ((form.from === 'edit' || form.from === 'template') && form.file.length === 0) {\n            MsgError(i18n.global.t('container.contentEmpty'));\n            return;\n        }\n        loading.value = true;\n        await testCompose(form)\n            .then(async (res) => {\n                loading.value = false;\n                if (res.data) {\n                    form.taskID = newUUID();\n                    await upCompose(form);\n                    openTaskLog(form.taskID);\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                }\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst openComposeFolder = (row: any) => {\n    if (row?.workdir) {\n        routerToFileWithPath(row.workdir);\n    }\n};\nconst onInspectContainer = async (item: any) => {\n    if (!item.containerID) {\n        return;\n    }\n    const res = await inspect({ id: item.containerID, type: 'container', detail: '' });\n    containerInspectRef.value!.acceptParams({ data: res.data, ports: item.ports || [] });\n};\nconst onOpenTerminal = (row: any) => {\n    if (!row.containerID) {\n        return;\n    }\n    const title = i18n.global.t('menu.container') + ' ' + row.name;\n    terminalDialogRef.value?.acceptParams({ containerID: row.containerID, title });\n};\nconst onOpenLog = (row: any) => {\n    containerLogDialogRef.value?.acceptParams({ container: row.name });\n};\n</script>\n\n<style scoped lang=\"scss\">\n.compose-item {\n    cursor: pointer;\n}\n\n.compose-title {\n    transition: color 0.15s;\n}\n\n.compose-item.is-active .compose-title {\n    color: var(--el-color-primary);\n}\n\n.compose-actions {\n    opacity: 0;\n    max-height: 0;\n    overflow: hidden;\n    transition: opacity 0.2s, max-height 0.2s;\n}\n\n.compose-item:hover .compose-actions,\n.compose-item.is-active .compose-actions {\n    opacity: 1;\n    max-height: 40px;\n}\n\n.svg-icon {\n    margin-top: -3px;\n    font-size: 6px;\n    cursor: pointer;\n}\n.envTitle {\n    font-size: 14px;\n    font-weight: 500;\n    color: var(--el-text-color-primary);\n    margin-top: 12px;\n    margin-bottom: 4px;\n    display: block;\n}\n.round-btn {\n    padding: 4px 8px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/container/commit/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"$t('container.makeImage')\"\n        @close=\"handleClose\"\n        :resource=\"form.containerName\"\n        size=\"large\"\n    >\n        <el-form @submit.prevent ref=\"formRef\" :model=\"form\" label-position=\"top\" v-loading=\"loading\">\n            <el-form-item prop=\"newImageName\" :rules=\"Rules.imageName\">\n                <template #label>\n                    {{ $t('container.newImageName') }}\n                </template>\n                <el-input v-model=\"form.newImageName\" />\n            </el-form-item>\n            <el-form-item prop=\"comment\">\n                <template #label>\n                    {{ $t('container.commitMessage') }}\n                </template>\n                <el-input v-model=\"form.comment\" />\n            </el-form-item>\n            <el-form-item prop=\"author\">\n                <template #label>\n                    {{ $t('container.author') }}\n                </template>\n                <el-input v-model=\"form.author\" />\n            </el-form-item>\n            <el-form-item prop=\"pause\">\n                <el-checkbox v-model=\"form.pause\">\n                    {{ $t('container.ifPause') }}\n                </el-checkbox>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport { ElForm } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { commitContainer } from '@/api/modules/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { newUUID } from '@/utils/util';\n\nconst drawerVisible = ref<boolean>(false);\nconst emit = defineEmits<{ (e: 'search'): void }>();\nconst loading = ref(false);\nconst form = reactive({\n    containerID: '',\n    containerName: '',\n    newImageName: '',\n    comment: '',\n    author: '',\n    pause: false,\n    taskID: '',\n});\n\nconst taskLogRef = ref();\n\ninterface DialogProps {\n    containerID: string;\n    containerName: string;\n}\nconst acceptParams = (props: DialogProps): void => {\n    form.newImageName = '';\n    form.comment = '';\n    form.author = '';\n    form.containerID = props.containerID;\n    form.containerName = props.containerName;\n    drawerVisible.value = true;\n};\n\nconst formRef = ref<FormInstance>();\ntype FormInstance = InstanceType<typeof ElForm>;\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(i18n.global.t('container.ifMakeImageWithContainer'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        }).then(async () => {\n            loading.value = true;\n            form.taskID = newUUID();\n            await commitContainer(form)\n                .then(() => {\n                    loading.value = false;\n                    emit('search');\n                    drawerVisible.value = false;\n                    openTaskLog(form.taskID);\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst handleClose = async () => {\n    drawerVisible.value = false;\n    emit('search');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/container/file-browser/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"visible\"\n        :header=\"$t('home.dir')\"\n        :resource=\"title\"\n        size=\"large\"\n        :fullScreen=\"true\"\n        @close=\"visible = false\"\n    >\n        <template #content>\n            <el-form label-position=\"top\">\n                <el-form-item>\n                    <div class=\"path-breadcrumb\">\n                        <el-breadcrumb separator=\"/\">\n                            <el-breadcrumb-item>\n                                <el-link type=\"primary\" @click=\"navigateToPath('/')\">\n                                    <el-icon><HomeFilled /></el-icon>\n                                </el-link>\n                            </el-breadcrumb-item>\n                            <el-breadcrumb-item v-for=\"item in pathSegments\" :key=\"item.path\">\n                                <el-link type=\"primary\" @click=\"navigateToPath(item.path)\">\n                                    {{ item.name }}\n                                </el-link>\n                            </el-breadcrumb-item>\n                        </el-breadcrumb>\n                    </div>\n                    <el-upload ref=\"uploadRef\" :auto-upload=\"false\" :show-file-list=\"false\" :on-change=\"onUploadChange\">\n                        <el-button class=\"mt-2\" :loading=\"uploading\" type=\"primary\" plain>\n                            {{ $t('commons.button.upload') }}\n                        </el-button>\n                    </el-upload>\n                    <el-button class=\"mt-2 ml-3\" plain :disabled=\"selectedRows.length === 0\" @click=\"onBatchDelete\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                    <el-button class=\"mt-2\" plain @click=\"loadContainerFiles\">\n                        {{ $t('commons.button.refresh') }}\n                    </el-button>\n                </el-form-item>\n                <ComplexTable\n                    :data=\"containerFiles\"\n                    size=\"small\"\n                    max-height=\"calc(100vh - 240px)\"\n                    v-model:selects=\"selectedRows\"\n                >\n                    <el-table-column type=\"selection\" width=\"48\" />\n                    <el-table-column :label=\"$t('commons.table.name')\" min-width=\"260\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            <div class=\"name-cell\">\n                                <el-icon>\n                                    <FolderOpened v-if=\"row.isDir\" />\n                                    <Document v-else />\n                                </el-icon>\n                                <el-link v-if=\"row.isDir\" type=\"primary\" @click=\"enterDir(row.path)\">\n                                    {{ row.name }}\n                                </el-link>\n                                <el-link v-else type=\"primary\" @click=\"onOpenFile(row.path)\">\n                                    {{ row.name }}\n                                </el-link>\n                                <span v-if=\"row.isLink && row.linkTo\" class=\"symlink-target\">-> {{ row.linkTo }}</span>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('file.size')\" width=\"130\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"!row.isDir\">{{ formatSize(row.size) }}</span>\n                            <el-button\n                                v-else-if=\"!row.sizeLoaded\"\n                                link\n                                type=\"primary\"\n                                :loading=\"row.sizeLoading\"\n                                @click=\"onCalcDirSize(row)\"\n                            >\n                                {{ $t('file.calculate') }}\n                            </el-button>\n                            <span v-else>{{ formatSize(row.size) }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.operate')\" width=\"220\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"onDownloadFile(row)\">\n                                {{ $t('commons.button.download') }}\n                            </el-button>\n                            <el-button type=\"primary\" link @click=\"onDeleteFile(row)\">\n                                {{ $t('commons.button.delete') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                </ComplexTable>\n            </el-form>\n        </template>\n    </DrawerPro>\n    <el-dialog v-model=\"previewVisible\" :title=\"previewTitle\" width=\"70%\" destroy-on-close>\n        <el-alert\n            v-if=\"previewTruncated\"\n            :title=\"$t('file.previewTruncated')\"\n            type=\"warning\"\n            :closable=\"false\"\n            show-icon\n            class=\"mb-2\"\n        />\n        <pre class=\"preview-content\">{{ previewContent }}</pre>\n    </el-dialog>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, ref } from 'vue';\nimport {\n    deleteContainerFile,\n    downloadContainerFile,\n    getContainerFileContent,\n    getContainerFileSize,\n    listContainerFiles,\n    uploadContainerFile,\n} from '@/api/modules/container';\nimport { MsgError, MsgSuccess, MsgWarning } from '@/utils/message';\nimport i18n from '@/lang';\nimport { ElMessageBox, UploadFile, UploadInstance } from 'element-plus';\nimport { Document, FolderOpened, HomeFilled } from '@element-plus/icons-vue';\nimport { computeSize2 } from '@/utils/util';\n\nconst visible = ref(false);\nconst title = ref('');\nconst containerID = ref('');\nconst filePath = ref('/');\nconst containerFiles = ref<any[]>([]);\nconst uploadRef = ref<UploadInstance>();\nconst uploading = ref(false);\nconst previewVisible = ref(false);\nconst previewTitle = ref('');\nconst previewContent = ref('');\nconst previewTruncated = ref(false);\nconst selectedRows = ref<any[]>([]);\nconst pathSegments = computed(() => {\n    const parts = filePath.value.split('/').filter((item) => item);\n    return parts.map((name, index) => ({\n        name,\n        path: '/' + parts.slice(0, index + 1).join('/'),\n    }));\n});\n\ninterface DrawerProps {\n    containerID: string;\n    title: string;\n}\n\nconst acceptParams = async (params: DrawerProps): Promise<void> => {\n    visible.value = true;\n    containerID.value = params.containerID;\n    title.value = params.title;\n    filePath.value = '/';\n    await loadContainerFiles();\n};\n\nconst loadContainerFiles = async () => {\n    if (!containerID.value || !filePath.value) {\n        return;\n    }\n    await listContainerFiles({\n        containerID: containerID.value,\n        path: filePath.value,\n    })\n        .then((res) => {\n            containerFiles.value = (res.data || []).map((item) => ({\n                ...item,\n                sizeLoaded: !item.isDir,\n                sizeLoading: false,\n            }));\n            selectedRows.value = [];\n        })\n        .catch(() => {\n            containerFiles.value = [];\n        });\n};\n\nconst enterDir = async (path: string) => {\n    filePath.value = path;\n    await loadContainerFiles();\n};\n\nconst navigateToPath = async (path: string) => {\n    filePath.value = path || '/';\n    await loadContainerFiles();\n};\n\nconst onUploadChange = async (uploadFile: UploadFile) => {\n    if (!uploadFile.raw) {\n        return;\n    }\n    const formData = new FormData();\n    formData.append('containerID', containerID.value);\n    formData.append('path', filePath.value);\n    formData.append('file', uploadFile.raw);\n    uploading.value = true;\n    await uploadContainerFile(formData)\n        .then(async () => {\n            MsgSuccess(i18n.global.t('file.uploadSuccess'));\n            await loadContainerFiles();\n        })\n        .catch((err) => {\n            MsgError(err?.message || i18n.global.t('commons.msg.operationFailed'));\n        })\n        .finally(() => {\n            uploading.value = false;\n            uploadRef.value?.clearFiles();\n        });\n};\n\nconst onOpenFile = async (path: string) => {\n    await getContainerFileContent({\n        containerID: containerID.value,\n        path,\n    })\n        .then((res) => {\n            if (res.data?.isBinary) {\n                MsgWarning(i18n.global.t('file.fileCanNotRead'));\n                return;\n            }\n            previewTitle.value = path;\n            previewContent.value = res.data?.content || '';\n            previewTruncated.value = !!res.data?.truncated;\n            previewVisible.value = true;\n        })\n        .catch((err) => {\n            MsgError(err?.message || i18n.global.t('commons.msg.operationFailed'));\n        });\n};\n\nconst onCalcDirSize = async (row: any) => {\n    row.sizeLoading = true;\n    await getContainerFileSize({\n        containerID: containerID.value,\n        path: row.path,\n    })\n        .then((res) => {\n            row.size = res.data || 0;\n            row.sizeLoaded = true;\n        })\n        .catch((err) => {\n            MsgError(err?.message || i18n.global.t('commons.msg.operationFailed'));\n        })\n        .finally(() => {\n            row.sizeLoading = false;\n        });\n};\n\nconst formatSize = (size: number) => {\n    return computeSize2(size || 0);\n};\n\nconst onDownloadFile = async (row: any) => {\n    const blob = await downloadContainerFile({\n        containerID: containerID.value,\n        path: row.path,\n    });\n    const downloadUrl = window.URL.createObjectURL(new Blob([blob]));\n    const a = document.createElement('a');\n    a.style.display = 'none';\n    a.href = downloadUrl;\n    const fileName = row.path.split('/').pop() || 'container-file';\n    a.download = row.isDir ? `${fileName}.tar.gz` : fileName;\n    a.dispatchEvent(new MouseEvent('click'));\n};\n\nconst onDeleteFile = async (row: any) => {\n    try {\n        await ElMessageBox.confirm(i18n.global.t('file.deleteHelper2'), i18n.global.t('commons.button.delete'), {\n            type: 'warning',\n        });\n    } catch {\n        return;\n    }\n    await deleteContainerFile({\n        containerID: containerID.value,\n        paths: [row.path],\n    })\n        .then(async () => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            await loadContainerFiles();\n        })\n        .catch((err) => {\n            MsgError(err?.message || i18n.global.t('commons.msg.operationFailed'));\n        });\n};\n\nconst onBatchDelete = async () => {\n    if (selectedRows.value.length === 0) {\n        return;\n    }\n    try {\n        await ElMessageBox.confirm(i18n.global.t('file.deleteHelper2'), i18n.global.t('commons.button.delete'), {\n            type: 'warning',\n        });\n    } catch {\n        return;\n    }\n    await deleteContainerFile({\n        containerID: containerID.value,\n        paths: selectedRows.value.map((item) => item.path),\n    })\n        .then(async () => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            selectedRows.value = [];\n            await loadContainerFiles();\n        })\n        .catch((err) => {\n            MsgError(err?.message || i18n.global.t('commons.msg.operationFailed'));\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.name-cell {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n}\n\n.symlink-target {\n    color: var(--el-text-color-secondary);\n    font-size: 12px;\n}\n\n.path-breadcrumb {\n    width: 100%;\n    padding: 6px 10px;\n    border: 1px solid var(--el-border-color);\n    border-radius: 4px;\n    overflow-x: auto;\n}\n\n.preview-content {\n    margin: 0;\n    max-height: 70vh;\n    overflow: auto;\n    white-space: pre-wrap;\n    word-break: break-word;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/container/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" @search=\"search\" />\n\n        <LayoutContent :title=\"$t('menu.container', 2)\" v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <template #search v-if=\"tags.length !== 0\">\n                <div class=\"card-interval\" v-if=\"isExist && isActive\">\n                    <div v-for=\"item in tags\" :key=\"item.key\" class=\"inline\">\n                        <el-button\n                            v-if=\"item.count\"\n                            class=\"tag-button\"\n                            :class=\"activeTag === item.key ? '' : 'no-active'\"\n                            @click=\"searchWithStatus(item.key)\"\n                            :type=\"activeTag === item.key ? 'primary' : ''\"\n                            :plain=\"activeTag !== item.key\"\n                        >\n                            {{ item.key === 'all' ? $t('commons.table.all') : $t('commons.status.' + item.key) }} *\n                            {{ item.count }}\n                        </el-button>\n                    </div>\n                </div>\n            </template>\n\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onContainerOperate('')\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onImportCreate()\">\n                    {{ $t('commons.button.import') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onClean()\">\n                    {{ $t('container.containerPrune') }}\n                </el-button>\n                <el-button-group class=\"button-group\">\n                    <el-button :disabled=\"checkStatus('start', null)\" @click=\"onOperate('start', null)\">\n                        {{ $t('commons.operate.start') }}\n                    </el-button>\n                    <el-button :disabled=\"checkStatus('stop', null)\" @click=\"onOperate('stop', null)\">\n                        {{ $t('commons.operate.stop') }}\n                    </el-button>\n                    <el-button :disabled=\"checkStatus('restart', null)\" @click=\"onOperate('restart', null)\">\n                        {{ $t('commons.button.restart') }}\n                    </el-button>\n                    <el-button :disabled=\"checkStatus('kill', null)\" @click=\"onOperate('kill', null)\">\n                        {{ $t('container.kill') }}\n                    </el-button>\n                    <el-button :disabled=\"checkStatus('pause', null)\" @click=\"onOperate('pause', null)\">\n                        {{ $t('container.pause') }}\n                    </el-button>\n                    <el-button :disabled=\"checkStatus('unpause', null)\" @click=\"onOperate('unpause', null)\">\n                        {{ $t('container.unpause') }}\n                    </el-button>\n                    <el-button :disabled=\"checkStatus('remove', null)\" @click=\"onOperate('remove', null)\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </el-button-group>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <el-tooltip\n                    :content=\"includeAppStore ? $t('container.includeAppstore') : $t('container.excludeAppstore')\"\n                >\n                    <el-button\n                        :type=\"includeAppStore ? '' : 'primary'\"\n                        @click=\"searchWithAppShow(!includeAppStore)\"\n                        :icon=\"includeAppStore ? 'View' : 'Hide'\"\n                    />\n                </el-tooltip>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"container-refresh\" @search=\"refresh()\" />\n                <fu-table-column-select\n                    :columns=\"columns\"\n                    trigger=\"hover\"\n                    :title=\"$t('commons.table.selectColumn')\"\n                    popper-class=\"popper-class\"\n                    :only-icon=\"true\"\n                />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @sort-change=\"search\"\n                    @search=\"search\"\n                    @cell-mouse-enter=\"showFavorite\"\n                    @cell-mouse-leave=\"hideFavorite\"\n                    :row-style=\"{ height: '65px' }\"\n                    style=\"width: 100%\"\n                    :columns=\"columns\"\n                    localKey=\"containerColumn\"\n                    :heightDiff=\"300\"\n                >\n                    <el-table-column type=\"selection\" />\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        min-width=\"250\"\n                        prop=\"name\"\n                        sortable=\"custom\"\n                        fix\n                        :fixed=\"mobile ? false : 'left'\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row, $index }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"onInspect(row)\">\n                                {{ row.name }}\n                            </el-text>\n\n                            <div class=\"float-right\">\n                                <el-tooltip\n                                    :content=\"row.isPinned ? $t('website.cancelFavorite') : $t('website.favorite')\"\n                                    v-if=\"row.isPinned || hoveredRowIndex === $index\"\n                                >\n                                    <el-button\n                                        link\n                                        size=\"large\"\n                                        :icon=\"row.isPinned ? 'StarFilled' : 'Star'\"\n                                        type=\"warning\"\n                                        @click=\"changePinned(row, true)\"\n                                    />\n                                </el-tooltip>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.image')\"\n                        show-overflow-tooltip\n                        min-width=\"180\"\n                        prop=\"imageName\"\n                    />\n                    <el-table-column :label=\"$t('commons.table.status')\" min-width=\"150\" prop=\"state\">\n                        <template #default=\"{ row }\">\n                            <el-dropdown placement=\"bottom\">\n                                <Status :key=\"row.state\" :status=\"row.state\" :operate=\"true\"></Status>\n                                <template #dropdown>\n                                    <el-dropdown-menu>\n                                        <el-dropdown-item\n                                            :disabled=\"checkStatus('start', row)\"\n                                            @click=\"onOperate('start', row)\"\n                                        >\n                                            {{ $t('commons.operate.start') }}\n                                        </el-dropdown-item>\n                                        <el-dropdown-item\n                                            :disabled=\"checkStatus('stop', row)\"\n                                            @click=\"onOperate('stop', row)\"\n                                        >\n                                            {{ $t('commons.operate.stop') }}\n                                        </el-dropdown-item>\n                                        <el-dropdown-item\n                                            :disabled=\"checkStatus('restart', row)\"\n                                            @click=\"onOperate('restart', row)\"\n                                        >\n                                            {{ $t('commons.button.restart') }}\n                                        </el-dropdown-item>\n                                        <el-dropdown-item\n                                            :disabled=\"checkStatus('kill', row)\"\n                                            @click=\"onOperate('kill', row)\"\n                                        >\n                                            {{ $t('container.kill') }}\n                                        </el-dropdown-item>\n                                        <el-dropdown-item\n                                            :disabled=\"checkStatus('pause', row)\"\n                                            @click=\"onOperate('pause', row)\"\n                                        >\n                                            {{ $t('container.pause') }}\n                                        </el-dropdown-item>\n                                        <el-dropdown-item\n                                            :disabled=\"checkStatus('unpause', row)\"\n                                            @click=\"onOperate('unpause', row)\"\n                                        >\n                                            {{ $t('container.unpause') }}\n                                        </el-dropdown-item>\n                                    </el-dropdown-menu>\n                                </template>\n                            </el-dropdown>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.source')\"\n                        show-overflow-tooltip\n                        prop=\"resource\"\n                        min-width=\"120\"\n                    >\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.hasLoad\">\n                                <div class=\"source-font\">CPU: {{ row.cpuPercent.toFixed(2) }}%</div>\n                                <div class=\"float-left source-font\">\n                                    {{ $t('monitor.memory') }}: {{ row.memoryPercent.toFixed(2) }}%\n                                </div>\n                                <el-popover placement=\"right\" width=\"500px\" class=\"float-right\">\n                                    <template #reference>\n                                        <svg-icon iconName=\"p-xiangqing\" class=\"svg-icon\"></svg-icon>\n                                    </template>\n                                    <template #default>\n                                        <el-descriptions direction=\"vertical\" border :column=\"3\" size=\"small\">\n                                            <el-descriptions-item :label=\"$t('container.cpuUsage')\">\n                                                {{ computeCPU(row.cpuTotalUsage) }}\n                                            </el-descriptions-item>\n                                            <el-descriptions-item :label=\"$t('container.cpuTotal')\">\n                                                {{ computeCPU(row.systemUsage) }}\n                                            </el-descriptions-item>\n                                            <el-descriptions-item :label=\"$t('container.core')\">\n                                                {{ row.percpuUsage }}\n                                            </el-descriptions-item>\n\n                                            <el-descriptions-item :label=\"$t('container.memUsage')\">\n                                                {{ computeSizeForDocker(row.memoryUsage) }}\n                                            </el-descriptions-item>\n                                            <el-descriptions-item :label=\"$t('container.memCache')\">\n                                                {{ computeSizeForDocker(row.memoryCache) }}\n                                            </el-descriptions-item>\n                                            <el-descriptions-item :label=\"$t('container.memTotal')\">\n                                                {{ computeSizeForDocker(row.memoryLimit) }}\n                                            </el-descriptions-item>\n                                            <el-descriptions-item v-if=\"row.hasLoadSize\">\n                                                <template #label>\n                                                    {{ $t('container.sizeRw') }}\n                                                    <el-tooltip :content=\"$t('container.sizeRwHelper')\">\n                                                        <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                                    </el-tooltip>\n                                                </template>\n                                                {{ computeSize2(row.sizeRw) }}\n                                            </el-descriptions-item>\n                                            <el-descriptions-item\n                                                :label=\"$t('container.sizeRootFs')\"\n                                                v-if=\"row.hasLoadSize\"\n                                            >\n                                                <template #label>\n                                                    {{ $t('container.sizeRootFs') }}\n                                                    <el-tooltip :content=\"$t('container.sizeRootFsHelper')\">\n                                                        <el-icon class=\"icon-item\"><InfoFilled /></el-icon>\n                                                    </el-tooltip>\n                                                </template>\n                                                {{ computeSize2(row.sizeRootFs) }}\n                                            </el-descriptions-item>\n                                        </el-descriptions>\n\n                                        <el-button\n                                            class=\"mt-2\"\n                                            v-if=\"!row.hasLoadSize\"\n                                            size=\"small\"\n                                            link\n                                            type=\"primary\"\n                                            @click=\"loadSize(row)\"\n                                        >\n                                            {{ $t('container.loadSize') }}\n                                        </el-button>\n                                    </template>\n                                </el-popover>\n                            </div>\n                            <div v-if=\"!row.hasLoad\">\n                                <el-button link loading></el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.ip')\"\n                        :width=\"mobile ? 120 : 'auto'\"\n                        min-width=\"120\"\n                        prop=\"network\"\n                    >\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.network\">\n                                <div v-for=\"(item, index) in row.network\" :key=\"index\">{{ item }}</div>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.related')\"\n                        show-overflow-tooltip\n                        min-width=\"210\"\n                        prop=\"appName\"\n                    >\n                        <template #default=\"{ row }\">\n                            <el-button v-if=\"row.appName != '' || row.websites != null\" link icon=\"Position\" />\n                            <el-text\n                                v-if=\"row.appName != ''\"\n                                link\n                                class=\"cursor-pointer\"\n                                size=\"small\"\n                                @click=\"routerToName('AppInstalled')\"\n                            >\n                                {{ $t('app.app') }}: {{ row.appName }} [{{ row.appInstallName }}]\n                            </el-text>\n                            <el-text\n                                v-if=\"row.websites != null\"\n                                link\n                                class=\"cursor-pointer\"\n                                size=\"small\"\n                                @click=\"routerToName('Website')\"\n                            >\n                                {{ $t('menu.website') }}:\n                                {{ row.websites.join(',') }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('commons.table.port')\"\n                        :width=\"mobile ? 260 : 'auto'\"\n                        min-width=\"200\"\n                        prop=\"ports\"\n                    >\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.ports\">\n                                <div v-for=\"(item, index) in row.ports\" :key=\"index\">\n                                    <div v-if=\"row.expand || (!row.expand && index < 3)\">\n                                        <el-tooltip :hide-after=\"20\" :content=\"item\" placement=\"top\">\n                                            <el-button\n                                                v-if=\"item.indexOf('->') !== -1\"\n                                                @click=\"goDashboard(item)\"\n                                                class=\"tagMargin\"\n                                                icon=\"Position\"\n                                                plain\n                                                size=\"small\"\n                                            >\n                                                {{ item.length > 25 ? item.substring(0, 25) + '...' : item }}\n                                            </el-button>\n                                            <el-button v-else class=\"tagMargin\" plain size=\"small\">\n                                                {{ item }}\n                                            </el-button>\n                                        </el-tooltip>\n                                    </div>\n                                </div>\n                                <div v-if=\"!row.expand && row.ports.length > 3\">\n                                    <el-button type=\"primary\" link @click=\"row.expand = true\">\n                                        {{ $t('commons.button.expand') }}...\n                                    </el-button>\n                                </div>\n                                <div v-if=\"row.expand && row.ports.length > 3\">\n                                    <el-button type=\"primary\" link @click=\"row.expand = false\">\n                                        {{ $t('commons.button.collapse') }}\n                                    </el-button>\n                                </div>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        min-width=\"200\"\n                        :label=\"$t('commons.table.description')\"\n                        prop=\"description\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <fu-input-rw-switch\n                                v-model=\"row.description\"\n                                @enter=\"changePinned(row, false)\"\n                                @blur=\"changePinned(row, false)\"\n                            />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.upTime')\"\n                        min-width=\"200\"\n                        show-overflow-tooltip\n                        prop=\"runTime\"\n                    />\n                    <fu-table-operations\n                        fix\n                        width=\"220px\"\n                        :ellipsis=\"2\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        :fixed=\"mobile ? false : 'right'\"\n                        prop=\"operate\"\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"onSubmitOperate\" />\n\n        <ContainerInspectDialog ref=\"containerInspectRef\" />\n        <PruneDialog @search=\"search\" ref=\"dialogPruneRef\" />\n\n        <RenameDialog @search=\"search\" ref=\"dialogRenameRef\" />\n        <ContainerLogDialog ref=\"dialogContainerLogRef\" :highlightDiff=\"210\" />\n        <UpgradeDialog @search=\"search\" ref=\"dialogUpgradeRef\" />\n        <CommitDialog @search=\"search\" ref=\"dialogCommitRef\" />\n        <MonitorDialog ref=\"dialogMonitorRef\" />\n        <TerminalDialog ref=\"dialogTerminalRef\" />\n        <ContainerFileDrawer ref=\"dialogFileBrowserRef\" />\n\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <Backups ref=\"dialogBackupRef\" />\n        <Uploads ref=\"uploadRef\" @close=\"search\" />\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport PruneDialog from '@/views/container/container/prune/index.vue';\nimport RenameDialog from '@/views/container/container/rename/index.vue';\nimport UpgradeDialog from '@/views/container/container/upgrade/index.vue';\nimport CommitDialog from '@/views/container/container/commit/index.vue';\nimport MonitorDialog from '@/views/container/container/monitor/index.vue';\nimport TerminalDialog from '@/views/container/container/terminal/index.vue';\nimport ContainerFileDrawer from '@/views/container/container/file-browser/index.vue';\nimport ContainerInspectDialog from '@/views/container/container/inspect/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport Backups from '@/components/backup/index.vue';\nimport Uploads from '@/components/upload/index.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport ContainerLogDialog from '@/components/log/container-drawer/index.vue';\nimport Status from '@/components/status/index.vue';\nimport { reactive, onMounted, ref, computed } from 'vue';\nimport {\n    containerItemStats,\n    containerListStats,\n    containerOperator,\n    inspect,\n    loadContainerStatus,\n    searchContainer,\n} from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\nimport i18n from '@/lang';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nimport { routerToName, routerToNameWithQuery } from '@/utils/router';\nimport router from '@/routers';\nimport { computeSize2, computeSizeForDocker, computeCPU, newUUID } from '@/utils/util';\nimport { updateCommonDescription } from '@/api/modules/setting';\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst loading = ref(false);\nconst data = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    cacheSizeKey: 'container-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('container-page-size')) || 20,\n    total: 0,\n    state: 'all',\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\nconst dialogUpgradeRef = ref();\nconst dialogCommitRef = ref();\nconst dialogPortJumpRef = ref();\nconst dialogBackupRef = ref();\nconst opRef = ref();\nconst includeAppStore = ref(true);\nconst columns = ref([]);\n\nconst batchNames = ref();\nconst batchOp = ref();\nconst taskLogRef = ref();\n\nconst tags = ref([]);\nconst activeTag = ref('all');\n\nconst hoveredRowIndex = ref(-1);\n\nconst goDashboard = async (port: any) => {\n    if (port.indexOf('127.0.0.1') !== -1) {\n        MsgWarning(i18n.global.t('container.unExposedPort'));\n        return;\n    }\n    if (port.indexOf(':') === -1) {\n        MsgWarning(i18n.global.t('commons.msg.errPort'));\n        return;\n    }\n    let portEx = port.match(/:(\\d+)/)[1];\n\n    let matches = port.match(new RegExp(':', 'g'));\n    let ip = matches && matches.length > 1 ? 'ipv6' : 'ipv4';\n    dialogPortJumpRef.value.acceptParams({ port: portEx, ip: ip });\n};\n\ninterface Filters {\n    filters?: string;\n}\nconst props = withDefaults(defineProps<Filters>(), {\n    filters: '',\n});\n\nconst containerInspectRef = ref();\n\nconst dialogContainerLogRef = ref();\nconst dialogRenameRef = ref();\nconst dialogPruneRef = ref();\n\nconst search = async (column?: any) => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    localStorage.setItem('includeAppStore', includeAppStore.value ? 'true' : 'false');\n    let filterItem = (router.currentRoute.value.query?.filters as string) || '';\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        name: searchName.value,\n        state: paginationConfig.state || 'all',\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        filters: filterItem,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n        excludeAppStore: !includeAppStore.value,\n    };\n    loading.value = true;\n    loadStats();\n    loadContainerCount();\n    await searchContainer(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst searchWithStatus = (item: string) => {\n    activeTag.value = item;\n    paginationConfig.state = activeTag.value;\n    search();\n};\n\nconst searchWithAppShow = (item: any) => {\n    includeAppStore.value = item;\n    search();\n};\n\nconst showFavorite = (row: any) => {\n    hoveredRowIndex.value = data.value.findIndex((item) => item === row);\n};\nconst hideFavorite = () => {\n    hoveredRowIndex.value = -1;\n};\nconst changePinned = (row: any, isPinned: boolean) => {\n    let params = {\n        id: row.containerID,\n        type: 'container',\n        detailType: '',\n        isPinned: row.isPinned,\n        description: row.description || '',\n    };\n    if (isPinned) {\n        params.isPinned = !row.isPinned;\n    }\n    updateCommonDescription(params).then(() => {\n        search();\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\n\nconst loadContainerCount = async () => {\n    await loadContainerStatus().then((res) => {\n        tags.value = [];\n        if (res.data.containerCount) {\n            tags.value.push({ key: 'all', count: res.data.containerCount });\n        }\n        if (res.data.running) {\n            tags.value.push({ key: 'running', count: res.data.running });\n        }\n        if (res.data.paused) {\n            tags.value.push({ key: 'paused', count: res.data.paused });\n        }\n        if (res.data.restarting) {\n            tags.value.push({ key: 'restarting', count: res.data.restarting });\n        }\n        if (res.data.removing) {\n            tags.value.push({ key: 'removing', count: res.data.removing });\n        }\n        if (res.data.created) {\n            tags.value.push({ key: 'created', count: res.data.created });\n        }\n        if (res.data.dead) {\n            tags.value.push({ key: 'dead', count: res.data.dead });\n        }\n        if (res.data.exited) {\n            tags.value.push({ key: 'exited', count: res.data.exited });\n        }\n    });\n};\n\nconst refresh = async () => {\n    let filterItem = props.filters ? props.filters : '';\n    let params = {\n        name: searchName.value,\n        state: paginationConfig.state || 'all',\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        filters: filterItem,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    loadStats();\n    const res = await searchContainer(params);\n    let containers = res.data.items || [];\n    for (const container of containers) {\n        for (const c of data.value) {\n            c.hasLoad = true;\n            if (container.containerID == c.containerID) {\n                for (let key in container) {\n                    if (key !== 'cpuPercent' && key !== 'memoryPercent') {\n                        c[key] = container[key];\n                    }\n                }\n            }\n        }\n    }\n};\n\nconst loadSize = async (row: any) => {\n    containerItemStats(row.containerID).then((res) => {\n        row.sizeRw = res.data.sizeRw || 0;\n        row.sizeRootFs = res.data.sizeRootFs || 0;\n        row.hasLoadSize = true;\n    });\n};\n\nconst loadStats = async () => {\n    const res = await containerListStats();\n    let stats = res.data || [];\n    if (stats.length === 0) {\n        return;\n    }\n    for (const container of data.value) {\n        for (const item of stats) {\n            if (container.containerID === item.containerID) {\n                container.hasLoad = true;\n                container.cpuTotalUsage = item.cpuTotalUsage;\n                container.systemUsage = item.systemUsage;\n                container.cpuPercent = item.cpuPercent;\n                container.percpuUsage = item.percpuUsage;\n                container.memoryCache = item.memoryCache;\n                container.memoryUsage = item.memoryUsage;\n                container.memoryLimit = item.memoryLimit;\n                container.memoryPercent = item.memoryPercent;\n                break;\n            }\n        }\n    }\n};\n\nconst onContainerOperate = async (container: string) => {\n    routerToNameWithQuery('ContainerCreate', { name: container });\n};\n\nconst onBackup = (row: Container.ContainerInfo) => {\n    dialogBackupRef.value!.acceptParams({\n        type: 'container',\n        name: row.name,\n        detailName: '',\n    });\n};\n\nconst uploadRef = ref();\nconst onImportCreate = () => {\n    uploadRef.value!.acceptParams({\n        type: 'container',\n        name: '',\n        detailName: '',\n        remark: '.tar.gz',\n        node: globalStore.currentNode,\n    });\n};\n\nconst dialogMonitorRef = ref();\nconst onMonitor = (row: any) => {\n    dialogMonitorRef.value!.acceptParams({ containerID: row.containerID, container: row.name });\n};\n\nconst dialogTerminalRef = ref();\nconst onTerminal = (row: any) => {\n    const title = i18n.global.t('menu.container') + ' ' + row.name;\n    dialogTerminalRef.value!.acceptParams({ containerID: row.containerID, title: title });\n};\nconst dialogFileBrowserRef = ref();\nconst onOpenFileBrowser = (row: any) => {\n    const title = i18n.global.t('menu.container') + ' ' + row.name;\n    dialogFileBrowserRef.value!.acceptParams({ containerID: row.containerID, title: title });\n};\n\nconst onInspect = async (row: any) => {\n    const res = await inspect({ id: row.containerID, type: 'container', detail: '' });\n    containerInspectRef.value!.acceptParams({ data: res.data, ports: row.ports });\n};\n\nconst onClean = () => {\n    dialogPruneRef.value!.acceptParams();\n};\n\nconst checkStatus = (operation: string, row: Container.ContainerInfo | null) => {\n    let opList = row ? [row] : selects.value;\n    if (opList.length < 1) {\n        return true;\n    }\n    switch (operation) {\n        case 'start':\n            for (const item of opList) {\n                if (item.state === 'running') {\n                    return true;\n                }\n            }\n            return false;\n        case 'stop':\n            for (const item of opList) {\n                if (item.state === 'stopped' || item.state === 'exited') {\n                    return true;\n                }\n            }\n            return false;\n        case 'pause':\n            for (const item of opList) {\n                if (item.state === 'paused' || item.state === 'exited') {\n                    return true;\n                }\n            }\n            return false;\n        case 'unpause':\n            for (const item of opList) {\n                if (item.state !== 'paused') {\n                    return true;\n                }\n            }\n            return false;\n    }\n};\n\nconst onOperate = async (op: string, row: Container.ContainerInfo | null) => {\n    let opList = row ? [row] : selects.value;\n    let msg = i18n.global.t('container.operatorHelper', [i18n.global.t('container.' + op)]);\n    batchNames.value = [];\n    batchOp.value = op;\n    for (const item of opList) {\n        batchNames.value.push(item.name);\n        if (item.isFromApp) {\n            msg =\n                op == 'remove'\n                    ? i18n.global.t('container.containerDeleteHelper', [i18n.global.t('container.' + op)])\n                    : i18n.global.t('container.operatorAppHelper', [i18n.global.t('container.' + op)]);\n        }\n    }\n    const successMsg = `${i18n.global.t('container.' + op)}${i18n.global.t('commons.status.success')}`;\n    opRef.value.acceptParams({\n        title: i18n.global.t('container.' + op),\n        names: batchNames.value,\n        msg: msg,\n        api: null,\n        params: null,\n        successMsg,\n    });\n};\n\nconst onSubmitOperate = async () => {\n    loading.value = true;\n    let taskID = newUUID();\n    await containerOperator({ names: batchNames.value, operation: batchOp.value, taskID: taskID })\n        .then(() => {\n            loading.value = false;\n            search();\n            openTaskLog(taskID);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.terminal'),\n        disabled: (row: Container.ContainerInfo) => {\n            return row.state !== 'running';\n        },\n        click: (row: Container.ContainerInfo) => {\n            onTerminal(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.log'),\n        click: (row: Container.ContainerInfo) => {\n            dialogContainerLogRef.value!.acceptParams({ containerID: row.containerID, container: row.name });\n        },\n    },\n    {\n        label: i18n.global.t('home.dir'),\n        disabled: (row: Container.ContainerInfo) => {\n            return row.state !== 'running';\n        },\n        click: (row: Container.ContainerInfo) => {\n            onOpenFileBrowser(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Container.ContainerInfo) => {\n            onContainerOperate(row.name);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.upgrade'),\n        click: (row: Container.ContainerInfo) => {\n            dialogUpgradeRef.value!.acceptParams({ container: row.name, image: row.imageName, fromApp: row.isFromApp });\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.backup'),\n        click: (row: Container.ContainerInfo) => {\n            onBackup(row);\n        },\n    },\n    {\n        label: i18n.global.t('menu.monitor'),\n        disabled: (row: Container.ContainerInfo) => {\n            return row.state !== 'running';\n        },\n        click: (row: Container.ContainerInfo) => {\n            onMonitor(row);\n        },\n    },\n    {\n        label: i18n.global.t('container.rename'),\n        click: (row: Container.ContainerInfo) => {\n            dialogRenameRef.value!.acceptParams({ container: row.name });\n        },\n        disabled: (row: any) => {\n            return row.isFromCompose;\n        },\n    },\n    {\n        label: i18n.global.t('container.makeImage'),\n        click: (row: Container.ContainerInfo) => {\n            dialogCommitRef.value!.acceptParams({ containerID: row.containerID, containerName: row.name });\n        },\n        disabled: (row: any) => {\n            return checkStatus('commit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Container.ContainerInfo) => {\n            onOperate('remove', row);\n        },\n        disabled: (row: any) => {\n            return checkStatus('remove', row);\n        },\n    },\n];\n\nonMounted(() => {\n    let includeItem = localStorage.getItem('includeAppStore');\n    includeAppStore.value = !includeItem || includeItem === 'true';\n});\n</script>\n\n<style scoped lang=\"scss\">\n.tagMargin {\n    margin-top: 2px;\n}\n.source-font {\n    font-size: 12px;\n}\n.svg-icon {\n    margin-top: -3px;\n    font-size: 6px;\n    cursor: pointer;\n}\n.button-group .el-button {\n    margin-left: -1px !important;\n    position: relative !important;\n    z-index: 1 !important;\n}\n.tag-button {\n    margin-top: -5px;\n    margin-right: 10px;\n    &.no-active {\n        background: none;\n        border: none;\n    }\n}\n.button-cell {\n    width: 100%;\n    max-width: 150px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/container/inspect/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"visible\" :header=\"$t('commons.button.view')\" size=\"large\" @close=\"handleClose\">\n        <el-tabs v-model=\"activeTab\" type=\"border-card\">\n            <el-tab-pane :label=\"$t('menu.container')\" name=\"overview\">\n                <el-descriptions :column=\"1\" border :title=\"$t('home.baseInfo')\">\n                    <el-descriptions-item :label=\"$t('commons.table.name')\">\n                        <el-text>{{ inspectData?.Name?.substring(1) || '-' }}</el-text>\n                        <CopyButton :content=\"inspectData?.Name || ''\" />\n                    </el-descriptions-item>\n                    <el-descriptions-item label=\"ID\">\n                        <el-text class=\"text-xs\">{{ inspectData?.Id?.substring(0, 12) }}</el-text>\n                        <CopyButton :content=\"inspectData?.Id || ''\" />\n                    </el-descriptions-item>\n                    <el-descriptions-item label=\"PID\">\n                        {{ inspectData?.State?.Pid }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('container.image')\">\n                        {{ inspectData?.Config?.Image }}\n                        <CopyButton :content=\"inspectData?.Config?.Image || ''\" />\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('commons.table.createdAt')\">\n                        {{ formatDate(inspectData?.Created) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('process.startTime')\">\n                        {{ formatDate(inspectData?.State?.StartedAt) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('container.finishTime')\">\n                        {{ formatDate(inspectData?.State?.FinishedAt) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('container.restartPolicy')\">\n                        {{ getRestartPolicy() }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('commons.table.status')\">\n                        <el-tag :type=\"getStatusType(inspectData?.State?.Status)\">\n                            {{ inspectData?.State?.Status }}\n                            {{\n                                inspectData?.State?.Health?.Status ? '(' + inspectData?.State?.Health?.Status + ')' : ''\n                            }}\n                        </el-tag>\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <el-descriptions class=\"mt-4\" :column=\"1\" border :title=\"$t('container.command')\">\n                    <el-descriptions-item :label=\"$t('container.command')\">\n                        <div v-if=\"inspectData?.Config?.Cmd?.length\" class=\"tag-list\">\n                            <el-tag type=\"info\" v-for=\"(entry, index) in inspectData?.Config?.Cmd\" :key=\"index\">\n                                {{ entry }}\n                            </el-tag>\n                        </div>\n                        <span v-else>-</span>\n                    </el-descriptions-item>\n                    <el-descriptions-item label=\"ENTRYPONT\">\n                        <div v-if=\"inspectData?.Config?.Entrypoint?.length\" class=\"tag-list\">\n                            <el-tag type=\"info\" v-for=\"(entry, index) in inspectData?.Config?.Entrypoint\" :key=\"index\">\n                                {{ entry }}\n                            </el-tag>\n                        </div>\n                        <span v-else>-</span>\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('container.workingDir')\">\n                        {{ inspectData?.Config?.WorkingDir || '-' }}\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <span class=\"envTitle\">{{ $t('container.env') }}</span>\n                <el-collapse accordion :title=\"$t('container.env')\">\n                    <el-collapse-item v-for=\"(env, index) in inspectData?.Config?.Env\" :key=\"index\" :name=\"index\">\n                        <template #title>\n                            <el-text class=\"text-sm\">{{ getEnvKey(env) }}</el-text>\n                        </template>\n                        <el-text class=\"text-xs break-all\">{{ getEnvValue(env) }}</el-text>\n                        <CopyButton :content=\"getEnvValue(env)\" />\n                    </el-collapse-item>\n                </el-collapse>\n                <el-empty v-if=\"!inspectData?.Config?.Env?.length\" :description=\"$t('commons.msg.noData')\" />\n            </el-tab-pane>\n\n            <el-tab-pane :label=\"$t('container.network')\" name=\"network\">\n                <el-descriptions :column=\"1\" border :title=\"$t('home.baseInfo')\">\n                    <el-descriptions-item :label=\"$t('container.networkName')\">\n                        {{ inspectData?.HostConfig?.NetworkMode }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.hostname')\">\n                        {{ inspectData?.Config?.Hostname }}\n                    </el-descriptions-item>\n                    <el-descriptions-item label=\"Domain\">\n                        {{ inspectData?.Config?.Domainname || '-' }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('commons.table.port')\">\n                        <div v-for=\"item of ports\" :key=\"item\">\n                            <span>{{ item }}</span>\n                        </div>\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <div v-for=\"(network, name) in inspectData?.NetworkSettings?.Networks\" :key=\"name\" class=\"mb-4 mt-4\">\n                    <el-descriptions :column=\"2\" border :title=\"name\">\n                        <el-descriptions-item label=\"Network ID\">\n                            <el-text class=\"text-xs\">{{ network?.NetworkID?.substring(0, 12) }}</el-text>\n                        </el-descriptions-item>\n                        <el-descriptions-item label=\"Endpoint ID\">\n                            <el-text class=\"text-xs\">{{ network?.EndpointID?.substring(0, 12) }}</el-text>\n                        </el-descriptions-item>\n                        <el-descriptions-item label=\"IPv4\">\n                            {{ network?.IPAddress || '-' }}\n                        </el-descriptions-item>\n                        <el-descriptions-item label=\"IPv4 Gateway\">\n                            {{ network?.Gateway || '-' }}\n                        </el-descriptions-item>\n                        <el-descriptions-item label=\"MAC\">\n                            {{ network?.MacAddress || '-' }}\n                        </el-descriptions-item>\n                        <el-descriptions-item label=\"IPv6 Gateway\">\n                            {{ network?.IPv6Gateway || '-' }}\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </div>\n            </el-tab-pane>\n\n            <el-tab-pane :label=\"$t('container.volume')\" name=\"storage\">\n                <el-table :data=\"inspectData?.Mounts\" border>\n                    <el-table-column :label=\"$t('commons.table.type')\" width=\"100\">\n                        <template #default=\"{ row }\">\n                            <el-tag size=\"small\">\n                                {{ row.Type === 'bind' ? $t('container.volumeOption') : $t('container.hostOption') }}\n                            </el-tag>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('container.hostOption')\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            <el-text\n                                v-if=\"row.Source\"\n                                type=\"primary\"\n                                class=\"cursor-pointer\"\n                                @click=\"handleJumpToFile(row.Source)\"\n                            >\n                                {{ row.Source }}\n                            </el-text>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('container.volumeOption')\" prop=\"Destination\" show-overflow-tooltip />\n                    <el-table-column :label=\"$t('container.tag')\" width=\"80\" align=\"center\">\n                        <template #default=\"{ row }\">\n                            {{ row.RW ? $t('container.modeRW') : $t('container.modeR') }}\n                        </template>\n                    </el-table-column>\n                </el-table>\n            </el-tab-pane>\n\n            <el-tab-pane :label=\"$t('commons.button.view')\" name=\"view\">\n                <div class=\"mb-2 flex justify-start\">\n                    <el-button type=\"primary\" @click=\"handleCopyRawJson\" icon=\"DocumentCopy\" size=\"default\">\n                        {{ $t('commons.button.copy') }}\n                    </el-button>\n                </div>\n                <CodemirrorPro v-model=\"rawJson\" :height-diff=\"270\" :readonly=\"true\" mode=\"json\" />\n            </el-tab-pane>\n        </el-tabs>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { copyText } from '@/utils/util';\nimport i18n from '@/lang';\n\nconst visible = ref(false);\nconst activeTab = ref('overview');\nconst inspectData = ref<any>(null);\nconst rawJson = ref('');\nconst showRawJson = ref(false);\n\nconst ports = ref([]);\ninterface DialogProps {\n    data: any;\n    ports: string[];\n}\n\nconst acceptParams = (props: DialogProps): void => {\n    visible.value = true;\n    activeTab.value = 'overview';\n    showRawJson.value = false;\n    ports.value = props.ports || [];\n\n    try {\n        if (typeof props.data === 'string') {\n            inspectData.value = JSON.parse(props.data);\n        } else {\n            inspectData.value = props.data;\n        }\n        rawJson.value = JSON.stringify(inspectData.value, null, 2);\n    } catch (e) {\n        console.error('Failed to parse inspect data:', e);\n    }\n};\n\nconst handleClose = () => {\n    visible.value = false;\n    inspectData.value = null;\n    rawJson.value = '';\n};\n\nconst getStatusType = (status: string): string => {\n    const statusMap: Record<string, string> = {\n        running: 'success',\n        paused: 'warning',\n        restarting: 'warning',\n        exited: 'info',\n        dead: 'danger',\n    };\n    return statusMap[status?.toLowerCase()] || 'info';\n};\n\nconst formatDate = (dateStr: string): string => {\n    if (!dateStr || dateStr === '0001-01-01T00:00:00Z') {\n        return '-';\n    }\n    try {\n        const date = new Date(dateStr);\n        const y = date.getFullYear();\n        const m = String(date.getMonth() + 1).padStart(2, '0');\n        const d = String(date.getDate()).padStart(2, '0');\n        const h = String(date.getHours()).padStart(2, '0');\n        const minute = String(date.getMinutes()).padStart(2, '0');\n        const second = String(date.getSeconds()).padStart(2, '0');\n        return `${y}-${m}-${d} ${h}:${minute}:${second}`;\n    } catch {\n        return dateStr;\n    }\n};\n\nconst getEnvKey = (env: string): string => {\n    const index = env.indexOf('=');\n    return index > 0 ? env.substring(0, index) : env;\n};\n\nconst getEnvValue = (env: string): string => {\n    const index = env.indexOf('=');\n    return index > 0 ? env.substring(index + 1) : '';\n};\n\nconst getRestartPolicy = () => {\n    switch (inspectData.value?.HostConfig?.RestartPolicy?.Name) {\n        case 'no':\n            return i18n.global.t('container.no');\n        case 'always':\n            return i18n.global.t('container.always');\n        case 'on-failure':\n            return i18n.global.t('container.onFailure');\n        case 'unless-stopped':\n            return i18n.global.t('container.unlessStopped');\n        default:\n            return '-';\n    }\n};\n\nconst handleJumpToFile = (path: string) => {\n    if (path) {\n        routerToFileWithPath(path);\n    }\n};\n\nconst handleCopyRawJson = () => {\n    copyText(rawJson.value);\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.break-all {\n    word-break: break-all;\n}\n\n:deep(.el-descriptions__label) {\n    width: 180px;\n    background-color: transparent !important;\n}\n\n.envTitle {\n    font-size: 16px;\n    color: var(--el-text-color-primary);\n    margin-top: 20px;\n    margin-bottom: 16px;\n    display: block;\n}\n\n.tag-list {\n    .el-tag {\n        & ~ .el-tag {\n            margin-left: 5px;\n        }\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/container/monitor/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"monitorVisible\"\n        :header=\"$t('menu.monitor')\"\n        @close=\"handleClose\"\n        :resource=\"title\"\n        size=\"large\"\n    >\n        <el-form label-position=\"top\" @submit.prevent>\n            <el-form-item :label=\"$t('container.refreshTime')\">\n                <el-select v-model=\"timeInterval\" @change=\"changeTimer\">\n                    <el-option label=\"3s\" :value=\"3\" />\n                    <el-option label=\"5s\" :value=\"5\" />\n                    <el-option label=\"10s\" :value=\"10\" />\n                    <el-option label=\"30s\" :value=\"30\" />\n                    <el-option label=\"60s\" :value=\"60\" />\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <el-card>\n            <v-charts\n                height=\"200px\"\n                id=\"cpuChart\"\n                type=\"line\"\n                :option=\"chartsOption['cpuChart']\"\n                v-if=\"chartsOption['cpuChart']\"\n            />\n        </el-card>\n        <el-card class=\"mt-2.5\">\n            <v-charts\n                height=\"200px\"\n                id=\"memoryChart\"\n                type=\"line\"\n                :option=\"chartsOption['memoryChart']\"\n                v-if=\"chartsOption['memoryChart']\"\n            />\n        </el-card>\n        <el-card class=\"mt-2.5\">\n            <v-charts\n                height=\"200px\"\n                id=\"ioChart\"\n                type=\"line\"\n                :option=\"chartsOption['ioChart']\"\n                v-if=\"chartsOption['ioChart']\"\n            />\n        </el-card>\n        <el-card class=\"mt-2.5\">\n            <v-charts\n                height=\"200px\"\n                id=\"networkChart\"\n                type=\"line\"\n                :option=\"chartsOption['networkChart']\"\n                v-if=\"chartsOption['networkChart']\"\n            />\n        </el-card>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onBeforeUnmount, ref } from 'vue';\nimport { containerStats } from '@/api/modules/container';\nimport { dateFormatForSecond } from '@/utils/util';\nimport VCharts from '@/components/v-charts/index.vue';\nimport i18n from '@/lang';\n\nconst title = ref();\nconst monitorVisible = ref(false);\nconst timeInterval = ref();\nlet timer: NodeJS.Timer | null = null;\nlet isInit = ref<boolean>(true);\ninterface DialogProps {\n    containerID: string;\n    container: string;\n}\nconst dialogData = ref<DialogProps>({\n    containerID: '',\n    container: '',\n});\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    monitorVisible.value = true;\n    dialogData.value.containerID = params.containerID;\n    title.value = params.container;\n    cpuDatas.value = [];\n    memDatas.value = [];\n    cacheDatas.value = [];\n    ioReadDatas.value = [];\n    ioWriteDatas.value = [];\n    netTxDatas.value = [];\n    netRxDatas.value = [];\n    timeDatas.value = [];\n    timeInterval.value = 5;\n    isInit.value = true;\n    loadData();\n    timer = setInterval(async () => {\n        if (monitorVisible.value) {\n            isInit.value = false;\n            loadData();\n        }\n    }, 1000 * timeInterval.value);\n};\n\nconst cpuDatas = ref<Array<string>>([]);\nconst memDatas = ref<Array<string>>([]);\nconst cacheDatas = ref<Array<string>>([]);\nconst ioReadDatas = ref<Array<string>>([]);\nconst ioWriteDatas = ref<Array<string>>([]);\nconst netTxDatas = ref<Array<string>>([]);\nconst netRxDatas = ref<Array<string>>([]);\nconst timeDatas = ref<Array<string>>([]);\nconst chartsOption = ref({ cpuChart: null, memoryChart: null, ioChart: null, networkChart: null });\n\nconst changeTimer = () => {\n    clearInterval(Number(timer));\n    timer = setInterval(async () => {\n        if (monitorVisible.value) {\n            loadData();\n        }\n    }, 1000 * timeInterval.value);\n};\n\nconst loadData = async () => {\n    const res = await containerStats(dialogData.value.containerID);\n    cpuDatas.value.push(res.data.cpuPercent.toFixed(2));\n    if (cpuDatas.value.length > 20) {\n        cpuDatas.value.splice(0, 1);\n    }\n    memDatas.value.push(res.data.memory.toFixed(2));\n    if (memDatas.value.length > 20) {\n        memDatas.value.splice(0, 1);\n    }\n    cacheDatas.value.push(res.data.cache.toFixed(2));\n    if (cacheDatas.value.length > 20) {\n        cacheDatas.value.splice(0, 1);\n    }\n    ioReadDatas.value.push(res.data.ioRead.toFixed(2));\n    if (ioReadDatas.value.length > 20) {\n        ioReadDatas.value.splice(0, 1);\n    }\n    ioWriteDatas.value.push(res.data.ioWrite.toFixed(2));\n    if (ioWriteDatas.value.length > 20) {\n        ioWriteDatas.value.splice(0, 1);\n    }\n    netTxDatas.value.push(res.data.networkTX.toFixed(2));\n    if (netTxDatas.value.length > 20) {\n        netTxDatas.value.splice(0, 1);\n    }\n    netRxDatas.value.push(res.data.networkRX.toFixed(2));\n    if (netRxDatas.value.length > 20) {\n        netRxDatas.value.splice(0, 1);\n    }\n    timeDatas.value.push(dateFormatForSecond(res.data.shotTime));\n    if (timeDatas.value.length > 20) {\n        timeDatas.value.splice(0, 1);\n    }\n\n    chartsOption.value['cpuChart'] = {\n        title: 'CPU',\n        xData: timeDatas.value,\n        yData: [\n            {\n                name: 'CPU',\n                data: cpuDatas.value,\n            },\n        ],\n        formatStr: '%',\n    };\n\n    chartsOption.value['memoryChart'] = {\n        title: i18n.global.t('monitor.memory'),\n        xData: timeDatas.value,\n        yData: [\n            {\n                name: i18n.global.t('monitor.memory'),\n                data: memDatas.value,\n            },\n            {\n                name: i18n.global.t('container.cache'),\n                data: cacheDatas.value,\n            },\n        ],\n        formatStr: 'MB',\n    };\n\n    chartsOption.value['ioChart'] = {\n        title: i18n.global.t('monitor.disk') + ' IO',\n        xData: timeDatas.value,\n        yData: [\n            {\n                name: i18n.global.t('monitor.read'),\n                data: ioReadDatas.value,\n            },\n            {\n                name: i18n.global.t('monitor.write'),\n                data: ioWriteDatas.value,\n            },\n        ],\n        formatStr: 'MB',\n    };\n\n    chartsOption.value['networkChart'] = {\n        title: i18n.global.t('monitor.network'),\n        xData: timeDatas.value,\n        yData: [\n            {\n                name: i18n.global.t('monitor.up'),\n                data: netTxDatas.value,\n            },\n            {\n                name: i18n.global.t('monitor.down'),\n                data: netRxDatas.value,\n            },\n        ],\n        formatStr: 'KB',\n    };\n};\nconst handleClose = async () => {\n    monitorVisible.value = false;\n    clearInterval(Number(timer));\n    timer = null;\n    chartsOption.value = { cpuChart: null, memoryChart: null, ioChart: null, networkChart: null };\n};\n\nonBeforeUnmount(() => {\n    handleClose;\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/container/operate/confirm.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.edit')\" size=\"small\">\n        <div v-if=\"isFromApp\" class=\"leading-6\">\n            <div>\n                <span>{{ $t('container.updateHelper1') }}</span>\n            </div>\n            <br />\n            <div>\n                <span>{{ $t('container.updateHelper2') }}</span>\n            </div>\n            <div>\n                <span>{{ $t('container.updateHelper3') }}</span>\n            </div>\n            <br />\n        </div>\n        <div>\n            <span>{{ $t('container.updateHelper4') }}</span>\n        </div>\n        <template #footer>\n            <el-button :disabled=\"loading\" @click=\"open = false\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit()\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\n\nconst loading = ref();\nconst open = ref(false);\nconst isFromApp = ref();\n\ninterface DialogProps {\n    isFromApp: boolean;\n}\n\nconst acceptParams = (props: DialogProps): void => {\n    isFromApp.value = props.isFromApp;\n    open.value = true;\n};\nconst emit = defineEmits(['submit']);\n\nconst onSubmit = async () => {\n    emit('submit');\n    open.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/container/operate/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent\n            back-name=\"ContainerItem\"\n            :title=\"isCreate ? $t('commons.button.create') : $t('commons.button.edit') + ' - ' + form.name\"\n        >\n            <template #main>\n                <el-form\n                    ref=\"formRef\"\n                    label-position=\"top\"\n                    v-loading=\"loading\"\n                    :model=\"form\"\n                    :rules=\"rules\"\n                    label-width=\"80px\"\n                >\n                    <el-row type=\"flex\" justify=\"center\" :gutter=\"20\">\n                        <el-col :span=\"20\">\n                            <el-card>\n                                <el-alert\n                                    v-if=\"!isCreate && isFromApp(form)\"\n                                    :title=\"$t('container.containerFromAppHelper')\"\n                                    :closable=\"false\"\n                                    type=\"error\"\n                                />\n\n                                <el-button v-if=\"isCreate\" type=\"primary\" icon=\"EditPen\" plain @click=\"toTerminal()\">\n                                    {{ $t('container.commandInput') }}\n                                </el-button>\n                                <el-form-item class=\"mt-5\" :label=\"$t('commons.table.name')\" prop=\"name\">\n                                    <el-input\n                                        :disabled=\"!isCreate\"\n                                        class=\"mini-form-item\"\n                                        clearable\n                                        v-model.trim=\"form.name\"\n                                    />\n                                    <span class=\"input-help\" v-if=\"!isCreate && isFromApp(form)\">\n                                        {{ $t('container.containerFromAppHelper1') }}\n                                        <el-button\n                                            style=\"margin-left: -5px\"\n                                            size=\"small\"\n                                            text\n                                            type=\"primary\"\n                                            @click=\"goRouter()\"\n                                        >\n                                            <el-icon><Position /></el-icon>\n                                            {{ $t('firewall.quickJump') }}\n                                        </el-button>\n                                    </span>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('container.image')\" prop=\"image\">\n                                    <el-checkbox v-model=\"form.imageInput\" :label=\"$t('container.input')\" />\n                                </el-form-item>\n                                <el-form-item>\n                                    <el-select\n                                        class=\"mini-form-item\"\n                                        v-if=\"!form.imageInput\"\n                                        filterable\n                                        v-model=\"form.image\"\n                                    >\n                                        <el-option\n                                            v-for=\"(item, index) of images\"\n                                            :key=\"index\"\n                                            :value=\"item.option\"\n                                            :label=\"item.option\"\n                                        />\n                                    </el-select>\n                                    <el-input class=\"mini-form-item\" v-else v-model=\"form.image\" />\n                                </el-form-item>\n                                <el-form-item prop=\"forcePull\">\n                                    <el-checkbox v-model=\"form.forcePull\">\n                                        {{ $t('container.forcePull') }}\n                                    </el-checkbox>\n                                    <span class=\"input-help\">{{ $t('container.forcePullHelper') }}</span>\n                                </el-form-item>\n\n                                <el-form-item prop=\"autoRemove\">\n                                    <el-checkbox v-model=\"form.autoRemove\">\n                                        {{ $t('container.autoRemove') }}\n                                    </el-checkbox>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('commons.table.port')\">\n                                    <el-radio-group v-model=\"form.publishAllPorts\" class=\"ml-4\">\n                                        <el-radio :value=\"false\">{{ $t('container.exposePort') }}</el-radio>\n                                        <el-radio :value=\"true\">{{ $t('container.exposeAll') }}</el-radio>\n                                    </el-radio-group>\n                                </el-form-item>\n                                <el-form-item v-if=\"!form.publishAllPorts\">\n                                    <el-table v-if=\"form.exposedPorts.length !== 0\" :data=\"form.exposedPorts\">\n                                        <el-table-column :label=\"$t('container.server')\" min-width=\"200\">\n                                            <template #default=\"{ row }\">\n                                                <el-input\n                                                    :placeholder=\"$t('container.serverExample')\"\n                                                    v-model=\"row.host\"\n                                                />\n                                            </template>\n                                        </el-table-column>\n                                        <el-table-column :label=\"$t('menu.container')\" min-width=\"120\">\n                                            <template #default=\"{ row }\">\n                                                <el-input\n                                                    :placeholder=\"$t('container.containerExample')\"\n                                                    v-model=\"row.containerPort\"\n                                                />\n                                            </template>\n                                        </el-table-column>\n                                        <el-table-column :label=\"$t('commons.table.protocol')\" min-width=\"100\">\n                                            <template #default=\"{ row }\">\n                                                <el-radio-group v-model=\"row.protocol\">\n                                                    <el-radio value=\"tcp\">tcp</el-radio>\n                                                    <el-radio value=\"udp\">udp</el-radio>\n                                                </el-radio-group>\n                                            </template>\n                                        </el-table-column>\n                                        <el-table-column min-width=\"80\">\n                                            <template #default=\"scope\">\n                                                <el-button link type=\"primary\" @click=\"handlePortsDelete(scope.$index)\">\n                                                    {{ $t('commons.button.delete') }}\n                                                </el-button>\n                                            </template>\n                                        </el-table-column>\n                                    </el-table>\n\n                                    <el-button class=\"ml-3 mt-2\" @click=\"handlePortsAdd()\">\n                                        {{ $t('commons.button.add') }}\n                                    </el-button>\n                                </el-form-item>\n                            </el-card>\n\n                            <el-tabs type=\"border-card\" class=\"mt-5\">\n                                <el-tab-pane :label=\"$t('container.network')\">\n                                    <Network ref=\"networkRef\" :networks=\"form.networks\" />\n                                    <el-divider border-style=\"dashed\" />\n                                    <el-row :gutter=\"20\">\n                                        <el-col :xs=\"24\" :sm=\"10\" :md=\"10\" :lg=\"10\" :xl=\"10\">\n                                            <el-form-item :label=\"$t('toolbox.device.hostname')\" prop=\"hostname\">\n                                                <el-input v-model=\"form.hostname\" />\n                                            </el-form-item>\n                                        </el-col>\n                                        <el-col :xs=\"24\" :sm=\"10\" :md=\"10\" :lg=\"10\" :xl=\"10\">\n                                            <el-form-item label=\"Domain\" prop=\"domainName\">\n                                                <el-input v-model=\"form.domainName\" />\n                                            </el-form-item>\n                                        </el-col>\n                                        <el-col :xs=\"24\" :sm=\"10\" :md=\"10\" :lg=\"10\" :xl=\"10\">\n                                            <el-form-item label=\"DNS\" prop=\"dns\">\n                                                <div v-for=\"(_, index) of form.dns\" :key=\"index\" class=\"w-full\">\n                                                    <el-input class=\"mt-2\" v-model=\"form.dns[index]\">\n                                                        <template #append>\n                                                            <el-button\n                                                                link\n                                                                icon=\"Delete\"\n                                                                @click=\"form.dns.splice(index, 1)\"\n                                                            />\n                                                        </template>\n                                                    </el-input>\n                                                </div>\n                                                <el-button class=\"mt-2\" @click=\"form.dns.push('')\">\n                                                    {{ $t('commons.button.add') }}\n                                                </el-button>\n                                            </el-form-item>\n                                        </el-col>\n                                    </el-row>\n                                </el-tab-pane>\n\n                                <el-tab-pane :label=\"$t('container.mount')\">\n                                    <Volume ref=\"volumeRef\" :volumes=\"form.volumes\" />\n                                </el-tab-pane>\n                                <el-tab-pane :label=\"$t('runtime.extraHosts')\">\n                                    <ExtraHosts :extraHosts=\"form.extraHosts\" />\n                                </el-tab-pane>\n\n                                <el-tab-pane :label=\"$t('terminal.command')\">\n                                    <el-row :gutter=\"20\">\n                                        <el-col :xs=\"24\" :sm=\"20\" :md=\"20\" :lg=\"20\" :xl=\"20\">\n                                            <el-form-item label=\"Command\" prop=\"cmdStr\">\n                                                <el-input\n                                                    v-model=\"form.cmdStr\"\n                                                    :placeholder=\"$t('container.cmdHelper')\"\n                                                />\n                                            </el-form-item>\n                                        </el-col>\n                                    </el-row>\n                                    <el-row :gutter=\"20\">\n                                        <el-col :xs=\"24\" :sm=\"20\" :md=\"20\" :lg=\"20\" :xl=\"20\">\n                                            <el-form-item label=\"Entrypoint\" prop=\"entrypointStr\">\n                                                <el-input\n                                                    v-model=\"form.entrypointStr\"\n                                                    :placeholder=\"$t('container.entrypointHelper')\"\n                                                />\n                                            </el-form-item>\n                                        </el-col>\n                                    </el-row>\n\n                                    <el-row :gutter=\"20\">\n                                        <el-col :xs=\"24\" :sm=\"10\" :md=\"10\" :lg=\"10\" :xl=\"10\">\n                                            <el-form-item :label=\"$t('container.workingDir')\" prop=\"workingDir\">\n                                                <el-input v-model=\"form.workingDir\" />\n                                            </el-form-item>\n                                        </el-col>\n                                        <el-col :xs=\"24\" :sm=\"10\" :md=\"10\" :lg=\"10\" :xl=\"10\">\n                                            <el-form-item :label=\"$t('commons.table.user')\" prop=\"user\">\n                                                <el-input v-model=\"form.user\" />\n                                            </el-form-item>\n                                        </el-col>\n                                    </el-row>\n                                    <el-form-item :label=\"$t('container.console')\">\n                                        <el-checkbox v-model=\"form.tty\">{{ $t('container.tty') }}</el-checkbox>\n                                        <el-checkbox v-model=\"form.openStdin\">\n                                            {{ $t('container.openStdin') }}\n                                        </el-checkbox>\n                                    </el-form-item>\n                                </el-tab-pane>\n\n                                <el-tab-pane :label=\"$t('container.resource')\">\n                                    <el-form-item :label=\"$t('container.cpuShare')\" prop=\"cpuShares\">\n                                        <el-input class=\"mini-form-item\" v-model.number=\"form.cpuShares\" />\n                                        <span class=\"input-help\">{{ $t('container.cpuShareHelper') }}</span>\n                                    </el-form-item>\n                                    <el-form-item\n                                        :label=\"$t('container.cpuQuota')\"\n                                        prop=\"nanoCPUs\"\n                                        :rules=\"checkFloatNumberRange(0, Number(limits.cpu))\"\n                                    >\n                                        <el-input class=\"mini-form-item\" v-model=\"form.nanoCPUs\">\n                                            <template #append>\n                                                <div style=\"width: 35px\">{{ $t('commons.units.core') }}</div>\n                                            </template>\n                                        </el-input>\n                                        <span class=\"input-help\">\n                                            {{ $t('container.limitHelper', [limits.cpu])\n                                            }}{{ $t('commons.units.core') }}\n                                        </span>\n                                    </el-form-item>\n                                    <el-form-item\n                                        :label=\"$t('container.memoryLimit')\"\n                                        prop=\"memory\"\n                                        :rules=\"checkFloatNumberRange(0, Number(limits.memory))\"\n                                    >\n                                        <el-input class=\"mini-form-item\" v-model=\"form.memory\">\n                                            <template #append><div style=\"width: 35px\">MB</div></template>\n                                        </el-input>\n                                        <span class=\"input-help\">\n                                            {{ $t('container.limitHelper', [limits.memory]) }}MB\n                                        </span>\n                                    </el-form-item>\n                                    <el-form-item>\n                                        <el-checkbox v-model=\"form.privileged\">\n                                            {{ $t('container.privileged') }}\n                                        </el-checkbox>\n                                        <span class=\"input-help\">{{ $t('container.privilegedHelper') }}</span>\n                                    </el-form-item>\n                                </el-tab-pane>\n\n                                <el-tab-pane :label=\"$t('container.tag') + ' & ' + $t('container.env')\">\n                                    <el-row :gutter=\"20\">\n                                        <el-col :xs=\"24\" :sm=\"20\" :md=\"20\" :lg=\"20\" :xl=\"20\">\n                                            <el-form-item :label=\"$t('container.tag')\" prop=\"labels\">\n                                                <div v-for=\"(_, index) of form.labels\" :key=\"index\" class=\"w-full\">\n                                                    <el-input\n                                                        class=\"mt-2\"\n                                                        placeholder=\"e.g. key=val\"\n                                                        v-model=\"form.labels[index]\"\n                                                    >\n                                                        <template #append>\n                                                            <el-button\n                                                                link\n                                                                icon=\"Delete\"\n                                                                @click=\"form.labels.splice(index, 1)\"\n                                                            />\n                                                        </template>\n                                                    </el-input>\n                                                </div>\n                                                <el-button class=\"mt-2\" @click=\"form.labels.push('')\">\n                                                    {{ $t('commons.button.add') }}\n                                                </el-button>\n                                            </el-form-item>\n                                        </el-col>\n                                        <el-col :xs=\"24\" :sm=\"20\" :md=\"20\" :lg=\"20\" :xl=\"20\">\n                                            <el-form-item :label=\"$t('container.env')\" prop=\"envStr\">\n                                                <div v-for=\"(_, index) of form.env\" :key=\"index\" class=\"w-full\">\n                                                    <el-input\n                                                        class=\"mt-2\"\n                                                        placeholder=\"e.g. key=val\"\n                                                        v-model=\"form.env[index]\"\n                                                    >\n                                                        <template #append>\n                                                            <el-button\n                                                                link\n                                                                icon=\"Delete\"\n                                                                @click=\"form.env.splice(index, 1)\"\n                                                            />\n                                                        </template>\n                                                    </el-input>\n                                                </div>\n                                                <el-button class=\"mt-2\" @click=\"form.env.push('')\">\n                                                    {{ $t('commons.button.add') }}\n                                                </el-button>\n                                            </el-form-item>\n                                        </el-col>\n                                    </el-row>\n                                </el-tab-pane>\n\n                                <el-tab-pane :label=\"$t('container.restartPolicy')\">\n                                    <el-form-item prop=\"restartPolicy\">\n                                        <el-radio-group v-model=\"form.restartPolicy\">\n                                            <el-radio value=\"no\">{{ $t('container.no') }}</el-radio>\n                                            <el-radio value=\"always\">{{ $t('container.always') }}</el-radio>\n                                            <el-radio value=\"on-failure\">{{ $t('container.onFailure') }}</el-radio>\n                                            <el-radio value=\"unless-stopped\">\n                                                {{ $t('container.unlessStopped') }}\n                                            </el-radio>\n                                        </el-radio-group>\n                                    </el-form-item>\n                                </el-tab-pane>\n                            </el-tabs>\n\n                            <el-form-item class=\"mt-5\">\n                                <el-button :disabled=\"loading\" @click=\"goBack\">\n                                    {{ $t('commons.button.back') }}\n                                </el-button>\n                                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                                    {{ $t('commons.button.confirm') }}\n                                </el-button>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                </el-form>\n            </template>\n        </LayoutContent>\n        <Confirm ref=\"confirmRef\" @submit=\"submit\" />\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" :show-close=\"false\">\n            <template #task-footer>\n                <el-button @click=\"goBack\">{{ $t('commons.table.backToList') }}</el-button>\n                <el-button type=\"primary\" @click=\"closeTask\">{{ $t('commons.table.keepEdit') }}</el-button>\n            </template>\n        </TaskLog>\n        <TerminalDialog ref=\"dialogTerminalRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules, checkFloatNumberRange, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport Confirm from '@/views/container/container/operate/confirm.vue';\nimport Volume from '@/views/container/container/operate/volume.vue';\nimport Network from '@/views/container/container/operate/network.vue';\nimport ExtraHosts from '@/views/website/runtime/components/extra_hosts/index.vue';\nimport {\n    listImage,\n    createContainer,\n    updateContainer,\n    loadResourceLimit,\n    loadContainerInfo,\n    searchContainer,\n} from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\nimport { MsgError } from '@/utils/message';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { checkIpV4V6, checkPort, newUUID } from '@/utils/util';\nimport router from '@/routers';\nimport TerminalDialog from '@/views/host/file-management/terminal/index.vue';\nimport { routerToName, routerToNameWithQuery } from '@/utils/router';\n\nconst loading = ref(false);\nconst isCreate = ref();\nconst confirmRef = ref();\nconst volumeRef = ref();\nconst networkRef = ref();\nconst form = reactive<Container.ContainerHelper>({\n    taskID: '',\n    name: '',\n    image: '',\n    imageInput: false,\n    forcePull: false,\n    networks: [{ network: 'bridge', ipv4: '', ipv6: '', macAddr: '' }],\n    hostname: '',\n    domainName: '',\n    dns: [],\n    cmdStr: '',\n    entrypointStr: '',\n    memoryItem: 0,\n    cmd: [],\n    workingDir: '',\n    user: '',\n    openStdin: false,\n    tty: false,\n    entrypoint: [],\n    publishAllPorts: false,\n    exposedPorts: [],\n    nanoCPUs: 0,\n    cpuShares: 1024,\n    memory: 0,\n    volumes: [],\n    extraHosts: [],\n    privileged: false,\n    autoRemove: false,\n    labels: [],\n    env: [],\n    restartPolicy: 'no',\n});\nconst search = async () => {\n    if (!isCreate.value) {\n        loading.value = true;\n        await loadContainerInfo(form.name)\n            .then((res) => {\n                loading.value = false;\n                form.name = res.data.name;\n                form.image = res.data.image;\n                form.networks = res.data.networks;\n                form.hostname = res.data.hostname;\n                form.domainName = res.data.domainName;\n                form.dns = res.data.dns || [];\n                form.openStdin = res.data.openStdin;\n                form.tty = res.data.tty;\n                form.publishAllPorts = res.data.publishAllPorts;\n                form.nanoCPUs = res.data.nanoCPUs;\n                form.cpuShares = res.data.cpuShares;\n                form.privileged = res.data.privileged;\n                form.autoRemove = res.data.autoRemove;\n                form.restartPolicy = res.data.restartPolicy;\n                form.memory = Number(res.data.memory.toFixed(2));\n                form.user = res.data.user;\n                form.workingDir = res.data.workingDir;\n\n                let itemCmd = '';\n                form.cmd = res.data.cmd || [];\n                for (const item of form.cmd) {\n                    if (item.indexOf(' ') !== -1) {\n                        itemCmd += `\"${escapeQuotes(item)}\" `;\n                    } else {\n                        itemCmd += item + ' ';\n                    }\n                }\n                form.cmdStr = itemCmd.trimEnd();\n                let itemEntrypoint = '';\n                form.entrypoint = res.data.entrypoint || [];\n                for (const item of form.entrypoint) {\n                    if (item.indexOf(' ') !== -1) {\n                        itemEntrypoint += `\"${escapeQuotes(item)}\" `;\n                    } else {\n                        itemEntrypoint += item + ' ';\n                    }\n                }\n                form.entrypointStr = itemEntrypoint.trimEnd();\n\n                form.labels = res.data.labels || [];\n                form.env = res.data.env || [];\n                form.exposedPorts = res.data.exposedPorts || [];\n                for (const item of form.exposedPorts) {\n                    if (item.hostIP) {\n                        item.host = item.hostIP + ':' + item.hostPort;\n                    } else {\n                        item.host = item.hostPort;\n                    }\n                }\n                form.volumes = res.data.volumes || [];\n                form.extraHosts = res.data.extraHosts || [];\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    }\n    loadLimit();\n    loadImageOptions();\n};\n\nconst taskLogRef = ref();\nconst images = ref();\nconst limits = ref<Container.ResourceLimit>({\n    cpu: null as number,\n    memory: null as number,\n});\n\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.containerName],\n    image: [Rules.imageName],\n    cpuShares: [Rules.integerNumberWith0, checkNumberRange(0, 262144)],\n    nanoCPUs: [Rules.floatNumber],\n    memory: [Rules.floatNumber],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst goBack = () => {\n    routerToName('ContainerItem');\n};\nconst closeTask = () => {\n    taskLogRef.value.handleClose();\n    checkExist();\n};\nconst dialogTerminalRef = ref();\nconst toTerminal = () => {\n    dialogTerminalRef.value!.acceptParams({ cwd: '$HOME', command: '/bin/sh' });\n};\n\nconst handlePortsAdd = () => {\n    let item = {\n        host: '',\n        hostIP: '',\n        containerPort: '',\n        hostPort: '',\n        protocol: 'tcp',\n    };\n    form.exposedPorts.push(item);\n};\nconst handlePortsDelete = (index: number) => {\n    form.exposedPorts.splice(index, 1);\n};\n\nconst goRouter = async () => {\n    routerToName('AppInstalled');\n};\n\nconst loadLimit = async () => {\n    const res = await loadResourceLimit();\n    limits.value = res.data;\n    limits.value.memory = Number((limits.value.memory / 1024 / 1024).toFixed(2));\n};\n\nconst loadImageOptions = async () => {\n    const res = await listImage();\n    images.value = res.data;\n};\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    form.volumes = volumeRef.value.loadVolumes();\n    if (form.volumes.length !== 0) {\n        for (const item of form.volumes) {\n            if (!item.containerDir || !item.sourceDir) {\n                MsgError(i18n.global.t('container.volumeHelper'));\n                return;\n            }\n        }\n    }\n    form.networks = networkRef.value.loadNetworks();\n    if (form.volumes.length !== 0) {\n        for (const item of form.networks) {\n            if (!item.network) {\n                MsgError(i18n.global.t('container.volumeHelper'));\n                return;\n            }\n        }\n    }\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (isCreate.value) {\n            submit();\n        } else {\n            confirmRef.value.acceptParams({ isFromApp: isFromApp(form) });\n        }\n    });\n};\nconst submit = async () => {\n    form.cmd = [];\n    form.taskID = newUUID();\n    if (form.cmdStr) {\n        let itemCmd = splitStringIgnoringQuotes(form.cmdStr);\n        for (const item of itemCmd) {\n            form.cmd.push(item.replace(/(?<!\\\\)\"/g, '').replaceAll('\\\\\"', '\"'));\n        }\n    }\n    form.entrypoint = [];\n    if (form.entrypointStr) {\n        let itemEntrypoint = splitStringIgnoringQuotes(form.entrypointStr);\n        for (const item of itemEntrypoint) {\n            form.entrypoint.push(item.replace(/(?<!\\\\)\"/g, '').replaceAll('\\\\\"', '\"'));\n        }\n    }\n    if (form.publishAllPorts) {\n        form.exposedPorts = [];\n    } else {\n        if (!checkPortValid()) {\n            return;\n        }\n    }\n    form.memory = Number(form.memory);\n    form.nanoCPUs = Number(form.nanoCPUs);\n\n    loading.value = true;\n    if (isCreate.value) {\n        await createContainer(form)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(form.taskID);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    } else {\n        await updateContainer(form)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(form.taskID);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    }\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst checkPortValid = () => {\n    if (form.exposedPorts.length === 0) {\n        return true;\n    }\n    for (const port of form.exposedPorts) {\n        if (port.host.indexOf(':') !== -1) {\n            port.hostIP = port.host.substring(0, port.host.lastIndexOf(':'));\n            if (checkIpV4V6(port.hostIP)) {\n                MsgError(i18n.global.t('firewall.addressFormatError'));\n                return false;\n            }\n            port.hostPort = port.host.substring(port.host.lastIndexOf(':') + 1);\n        } else {\n            port.hostIP = '';\n            port.hostPort = port.host;\n        }\n        if (port.hostPort.indexOf('-') !== -1) {\n            if (checkPort(port.hostPort.split('-')[0])) {\n                MsgError(i18n.global.t('firewall.portFormatError'));\n                return false;\n            }\n            if (checkPort(port.hostPort.split('-')[1])) {\n                MsgError(i18n.global.t('firewall.portFormatError'));\n                return false;\n            }\n        } else {\n            if (checkPort(port.hostPort)) {\n                MsgError(i18n.global.t('firewall.portFormatError'));\n                return false;\n            }\n        }\n        if (port.containerPort.indexOf('-') !== -1) {\n            if (checkPort(port.containerPort.split('-')[0])) {\n                MsgError(i18n.global.t('firewall.portFormatError'));\n                return false;\n            }\n            if (checkPort(port.containerPort.split('-')[1])) {\n                MsgError(i18n.global.t('firewall.portFormatError'));\n                return false;\n            }\n        } else {\n            if (checkPort(port.containerPort)) {\n                MsgError(i18n.global.t('firewall.portFormatError'));\n                return false;\n            }\n        }\n    }\n    return true;\n};\n\nconst isFromApp = (rowData: Container.ContainerHelper) => {\n    if (rowData && rowData.labels) {\n        return rowData.labels.indexOf('createdBy=Apps') > -1;\n    }\n    return false;\n};\n\nconst checkExist = async () => {\n    let params = {\n        page: 1,\n        pageSize: 1,\n        state: 'all',\n        name: form.name,\n        filters: '',\n        orderBy: 'createdAt',\n        order: 'null',\n    };\n    await searchContainer(params).then((res) => {\n        if (res.data.items?.length === 1) {\n            isCreate.value = false;\n            routerToNameWithQuery('ContainerCreate', { name: form.name, t: Date.now() });\n            return;\n        } else {\n            isCreate.value = true;\n        }\n    });\n};\n\nconst escapeQuotes = (input) => {\n    return input.replace(/(?<!\\\\)\"/g, '\\\\\"');\n};\n\nconst splitStringIgnoringQuotes = (input) => {\n    input = input.replace(/\\\\\"/g, '<quota>');\n    const regex = /\"([^\"]*)\"|(\\S+)/g;\n    const result = [];\n    let match;\n\n    while ((match = regex.exec(input)) !== null) {\n        if (match[1]) {\n            result.push(match[1].replaceAll('<quota>', '\\\\\"'));\n        } else if (match[2]) {\n            result.push(match[2].replaceAll('<quota>', '\\\\\"'));\n        }\n    }\n\n    return result;\n};\n\nonMounted(() => {\n    if (router.currentRoute.value.query.name) {\n        isCreate.value = false;\n        form.name = String(router.currentRoute.value.query.name);\n    } else {\n        isCreate.value = true;\n    }\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.el-card {\n    border: 1px solid var(--el-border-color-light) !important;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/container/operate/network.vue",
    "content": "<template>\n    <div>\n        <ComplexTable v-if=\"tmpNetworks\" style=\"width: 100%\" :data=\"tmpNetworks\">\n            <el-table-column :label=\"$t('container.network')\" min-width=\"200\">\n                <template #default=\"{ row }\">\n                    <el-select v-model=\"row.network\" @change=\"changeNetwork(row)\">\n                        <el-option\n                            v-for=\"(item, indexV) of networkOptions\"\n                            :key=\"indexV\"\n                            :value=\"item.option\"\n                            :label=\"item.option\"\n                        />\n                    </el-select>\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('container.macAddr')\" min-width=\"200\">\n                <template #default=\"{ row }\">\n                    <el-input v-model=\"row.macAddr\" />\n                </template>\n            </el-table-column>\n            <el-table-column label=\"IPv4\" min-width=\"200\">\n                <template #default=\"{ row }\">\n                    <el-input\n                        v-model=\"row.ipv4\"\n                        :disabled=\"isIpDisabled(row)\"\n                        :placeholder=\"$t('container.inputIpv4')\"\n                    />\n                </template>\n            </el-table-column>\n            <el-table-column label=\"IPv6\" min-width=\"200\">\n                <template #default=\"{ row }\">\n                    <el-input\n                        v-model=\"row.ipv6\"\n                        :disabled=\"isIpDisabled(row)\"\n                        :placeholder=\"$t('container.inputIpv6')\"\n                    />\n                </template>\n            </el-table-column>\n            <el-table-column min-width=\"80\" :fixed=\"'right'\">\n                <template #default=\"scope\">\n                    <el-button link type=\"primary\" @click=\"handleNetworksDelete(scope.$index)\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </template>\n            </el-table-column>\n        </ComplexTable>\n        <el-button class=\"mt-2\" @click=\"handleNetworksAdd()\">\n            {{ $t('commons.button.add') }}\n        </el-button>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Container } from '@/api/interface/container';\nimport { listNetwork } from '@/api/modules/container';\nimport { ref, onMounted } from 'vue';\nconst tmpNetworks = ref([]);\nconst networkOptions = ref();\nconst props = defineProps({\n    networks: { type: Array<Container.ContainerNetwork>, default: [] },\n});\nwatch(\n    () => props.networks,\n    (newVal) => {\n        tmpNetworks.value = newVal || [];\n        for (const item of tmpNetworks.value) {\n            if (item.network === 'none' || item.network === 'host') {\n                item.ipv4 = '';\n                item.ipv6 = '';\n            }\n        }\n    },\n);\n\nconst loadNetworkOptions = async () => {\n    const res = await listNetwork();\n    networkOptions.value = res.data;\n};\nconst handleNetworksAdd = () => {\n    let item = {\n        network: 'bridge',\n        ipv4: '',\n        ipv6: '',\n        macAddr: '',\n    };\n    tmpNetworks.value.push(item);\n};\nconst handleNetworksDelete = (index: number) => {\n    tmpNetworks.value.splice(index, 1);\n};\nconst changeNetwork = (row: any) => {\n    if (row.network === 'none' || row.network === 'host') {\n        row.ipv4 = '';\n        row.ipv6 = '';\n    }\n};\nconst isIpDisabled = (row: any) => {\n    return row.network === 'none' || row.network === 'host' || row.network === 'bridge';\n};\n\nconst loadNetworks = () => {\n    return tmpNetworks.value;\n};\n\nonMounted(() => {\n    tmpNetworks.value = props.networks || [];\n    loadNetworkOptions();\n});\n\ndefineExpose({\n    loadNetworks,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/container/operate/volume.vue",
    "content": "<template>\n    <div>\n        <ComplexTable v-if=\"tmpVolumes\" style=\"width: 100%\" :data=\"tmpVolumes\">\n            <el-table-column :label=\"$t('container.server')\" min-width=\"200\">\n                <template #default=\"{ row }\">\n                    <el-radio-group v-model=\"row.type\">\n                        <el-radio-button value=\"volume\">\n                            {{ $t('container.volumeOption') }}\n                        </el-radio-button>\n                        <el-radio-button value=\"bind\">\n                            {{ $t('container.hostOption') }}\n                        </el-radio-button>\n                    </el-radio-group>\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('container.server') + ' -> ' + $t('container.containerDir')\" min-width=\"400\">\n                <template #default=\"{ row }\">\n                    <el-select v-if=\"row.type === 'volume'\" filterable v-model=\"row.sourceDir\">\n                        <div v-for=\"(item, indexV) of volumeOptions\" :key=\"indexV\">\n                            <el-tooltip :hide-after=\"20\" :content=\"item.option\" placement=\"top\">\n                                <el-option :value=\"item.option\" :label=\"item.option.substring(0, 30)\" />\n                            </el-tooltip>\n                        </div>\n                    </el-select>\n                    <el-input v-else v-model=\"row.sourceDir\" />\n                    <el-icon><Bottom /></el-icon>\n                    <el-input v-model=\"row.containerDir\" />\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('container.mode')\" min-width=\"180\">\n                <template #default=\"{ row }\">\n                    <el-radio-group v-model=\"row.mode\">\n                        <el-radio value=\"rw\">{{ $t('container.modeRW') }}</el-radio>\n                        <el-radio value=\"ro\">{{ $t('container.modeR') }}</el-radio>\n                    </el-radio-group>\n                </template>\n            </el-table-column>\n\n            <el-table-column :label=\"$t('container.sharedLabel')\" min-width=\"180\">\n                <template #default=\"{ row }\">\n                    <el-select v-if=\"row.type !== 'volume'\" popper-class=\"tall-options\" v-model=\"row.shared\">\n                        <el-option value=\"private\" :label=\"$t('container.private')\">\n                            <div class=\"title\">{{ $t('container.private') }}</div>\n                            <div class=\"description\">{{ $t('container.privateHelper') }}</div>\n                        </el-option>\n                        <el-option value=\"rprivate\" :label=\"$t('container.rprivate')\">\n                            <div class=\"title\">{{ $t('container.rprivate') }}</div>\n                            <div class=\"description\">{{ $t('container.rprivateHelper') }}</div>\n                        </el-option>\n                        <el-option value=\"shared\" :label=\"$t('container.shared')\">\n                            <div class=\"title\">{{ $t('container.shared') }}</div>\n                            <div class=\"description\">{{ $t('container.sharedHelper') }}</div>\n                        </el-option>\n                        <el-option value=\"rshared\" :label=\"$t('container.rshared')\">\n                            <div class=\"title\">{{ $t('container.rshared') }}</div>\n                            <div class=\"description\">{{ $t('container.rsharedHelper') }}</div>\n                        </el-option>\n                        <el-option value=\"slave\" :label=\"$t('container.slave')\">\n                            <div class=\"title\">{{ $t('container.slave') }}</div>\n                            <div class=\"description\">{{ $t('container.slaveHelper') }}</div>\n                        </el-option>\n                        <el-option value=\"rslave\" :label=\"$t('container.rslave')\">\n                            <div class=\"title\">{{ $t('container.rslave') }}</div>\n                            <div class=\"description\">{{ $t('container.rslaveHelper') }}</div>\n                        </el-option>\n                    </el-select>\n                    <span v-else>-</span>\n                </template>\n            </el-table-column>\n            <el-table-column min-width=\"80\" :fixed=\"'right'\">\n                <template #default=\"scope\">\n                    <el-button link type=\"primary\" @click=\"handleVolumesDelete(scope.$index)\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </template>\n            </el-table-column>\n        </ComplexTable>\n        <el-button class=\"mt-2\" @click=\"handleVolumesAdd()\">\n            {{ $t('commons.button.add') }}\n        </el-button>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Container } from '@/api/interface/container';\nimport { listVolume } from '@/api/modules/container';\nimport { ref, onMounted } from 'vue';\nconst tmpVolumes = ref([]);\nconst volumeOptions = ref();\nconst props = defineProps({\n    volumes: { type: Array<Container.Volume>, default: [] },\n});\nwatch(\n    () => props.volumes,\n    (newVal) => {\n        tmpVolumes.value = newVal || [];\n    },\n);\n\nconst loadVolumeOptions = async () => {\n    const res = await listVolume();\n    volumeOptions.value = res.data;\n};\nconst handleVolumesAdd = () => {\n    let item = {\n        type: 'volume',\n        sourceDir: '',\n        containerDir: '',\n        mode: 'rw',\n        shared: 'private',\n    };\n    tmpVolumes.value.push(item);\n};\nconst handleVolumesDelete = (index: number) => {\n    tmpVolumes.value.splice(index, 1);\n};\nconst loadVolumes = () => {\n    return tmpVolumes.value;\n};\n\nonMounted(() => {\n    tmpVolumes.value = props.volumes || [];\n    loadVolumeOptions();\n});\n\ndefineExpose({\n    loadVolumes,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.tall-options {\n    .el-select-dropdown__item {\n        height: 50px !important;\n        padding: 5px 15px !important;\n        line-height: 1.5 !important;\n        font-size: 14px;\n\n        &.selected {\n            font-weight: bold;\n        }\n\n        &:hover {\n            background-color: #f0f9ff;\n        }\n    }\n    .title {\n        font-size: 14px;\n        font-weight: 500;\n    }\n    .description {\n        font-size: 12px;\n        color: #adb0bc;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/container/prune/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('container.containerPrune')\" size=\"small\">\n        <div>\n            <ul class=\"help-ul\">\n                <li class=\"lineClass\" style=\"color: red\">{{ $t('container.containerPruneHelper1') }}</li>\n                <li class=\"lineClass\">{{ $t('container.containerPruneHelper2') }}</li>\n                <li class=\"lineClass\">{{ $t('container.containerPruneHelper3') }}</li>\n            </ul>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"open = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onClean()\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"onSearch\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { containerPrune } from '@/api/modules/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { ref } from 'vue';\nimport { newUUID } from '@/utils/util';\n\nconst loading = ref(false);\nconst open = ref<boolean>(false);\nconst taskLogRef = ref();\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst onClean = async () => {\n    loading.value = true;\n    let params = {\n        taskID: newUUID(),\n        pruneType: 'container',\n        withTagAll: false,\n    };\n    await containerPrune(params)\n        .then(() => {\n            loading.value = false;\n            open.value = false;\n            openTaskLog(params.taskID);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst onSearch = () => {\n    emit('search');\n};\n\nconst acceptParams = (): void => {\n    open.value = true;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.lineClass {\n    line-height: 30px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/container/rename/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"newNameVisible\"\n        :header=\"$t('container.rename')\"\n        @close=\"handleClose\"\n        :resource=\"renameForm.name\"\n        size=\"small\"\n    >\n        <el-form @submit.prevent ref=\"newNameRef\" v-loading=\"loading\" :model=\"renameForm\" label-position=\"top\">\n            <el-form-item\n                :label=\"$t('container.newName')\"\n                :rules=\"[Rules.containerName, Rules.requiredInput]\"\n                prop=\"newName\"\n            >\n                <el-input v-model=\"renameForm.newName\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"newNameVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmitName(newNameRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { containerRename } from '@/api/modules/container';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElForm } from 'element-plus';\nimport { reactive, ref } from 'vue';\n\nconst loading = ref(false);\n\nconst renameForm = reactive({\n    name: '',\n    newName: '',\n});\n\nconst newNameRef = ref<FormInstance>();\n\nconst newNameVisible = ref<boolean>(false);\ntype FormInstance = InstanceType<typeof ElForm>;\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst onSubmitName = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await containerRename(renameForm)\n            .then(() => {\n                loading.value = false;\n                emit('search');\n                newNameVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ninterface DialogProps {\n    container: string;\n}\n\nconst acceptParams = (props: DialogProps): void => {\n    renameForm.name = props.container;\n    renameForm.newName = '';\n    newNameVisible.value = true;\n};\n\nconst handleClose = async () => {\n    newNameVisible.value = false;\n    emit('search');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/container/terminal/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"terminalVisible\"\n        :header=\"$t('menu.terminal')\"\n        @close=\"handleClose\"\n        :resource=\"title\"\n        :autoClose=\"!terminalOpen\"\n        size=\"large\"\n        :fullScreen=\"true\"\n    >\n        <template #content>\n            <el-form ref=\"formRef\" :model=\"form\" label-position=\"top\">\n                <el-form-item :label=\"$t('menu.container')\" prop=\"containerID\" v-if=\"form.containerIDList.length > 1\">\n                    <el-select placeholder=\"container\" clearable v-model=\"form.containerID\">\n                        <el-option v-for=\"item in form.containerIDList\" :key=\"item\" :label=\"item\" :value=\"item\" />\n                    </el-select>\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.table.user')\" prop=\"user\">\n                    <el-input placeholder=\"root\" clearable v-model=\"form.user\" />\n                </el-form-item>\n                <el-form-item\n                    v-if=\"form.isCustom\"\n                    :label=\"$t('container.command')\"\n                    prop=\"command\"\n                    :rules=\"Rules.requiredInput\"\n                >\n                    <el-checkbox class=\"p-w-100\" border v-model=\"form.isCustom\" @change=\"onChangeCommand\">\n                        {{ $t('container.custom') }}\n                    </el-checkbox>\n                    <el-input style=\"width: calc(100% - 100px)\" clearable v-model=\"form.command\" />\n                </el-form-item>\n                <el-form-item\n                    v-if=\"!form.isCustom\"\n                    :label=\"$t('container.command')\"\n                    prop=\"command\"\n                    :rules=\"Rules.requiredSelect\"\n                >\n                    <el-checkbox class=\"p-w-100\" border v-model=\"form.isCustom\" @change=\"onChangeCommand\">\n                        {{ $t('container.custom') }}\n                    </el-checkbox>\n                    <el-select style=\"width: calc(100% - 100px)\" filterable clearable v-model=\"form.command\">\n                        <el-option value=\"/bin/ash\" label=\"/bin/ash\" />\n                        <el-option value=\"/bin/bash\" label=\"/bin/bash\" />\n                        <el-option value=\"/bin/sh\" label=\"/bin/sh\" />\n                    </el-select>\n                </el-form-item>\n\n                <el-button v-if=\"!terminalOpen\" @click=\"initTerm(formRef)\">\n                    {{ $t('commons.button.conn') }}\n                </el-button>\n                <el-button v-else @click=\"onClose()\">{{ $t('commons.button.disConn') }}</el-button>\n                <Terminal\n                    style=\"height: calc(100vh - 312px); margin-top: 18px\"\n                    ref=\"terminalRef\"\n                    v-if=\"terminalOpen\"\n                ></Terminal>\n            </el-form>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref, nextTick } from 'vue';\nimport { ElForm, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport Terminal from '@/components/terminal/index.vue';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { currentNode } = useGlobalStore();\n\nconst title = ref();\nconst terminalVisible = ref(false);\nconst terminalOpen = ref(false);\nconst form = reactive({\n    isCustom: false,\n    command: '',\n    user: '',\n    containerID: '',\n    containerIDList: [],\n    node: '',\n});\nconst formRef = ref();\nconst terminalRef = ref<InstanceType<typeof Terminal> | null>(null);\n\ninterface DialogProps {\n    containerID: string;\n    title: string;\n    node?: string;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    terminalVisible.value = true;\n    form.containerIDList = params.containerID.split(',');\n    form.containerID = form.containerIDList[0];\n    title.value = params.title;\n    form.isCustom = false;\n    form.user = '';\n    form.command = '/bin/sh';\n    form.node = params.node || currentNode.value;\n    terminalOpen.value = false;\n};\n\nconst onChangeCommand = async () => {\n    form.command = '';\n};\n\nconst initTerm = (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        terminalOpen.value = true;\n        await nextTick();\n        let args = `source=container&containerid=${form.containerID}&user=${form.user}&command=${form.command}`;\n        if (form.node) {\n            args += `&operateNode=${form.node}`;\n        }\n        terminalRef.value!.acceptParams({\n            endpoint: '/api/v2/containers/exec',\n            args: args,\n            error: '',\n            initCmd: '',\n        });\n    });\n};\n\nconst onClose = () => {\n    terminalRef.value?.onClose();\n    terminalOpen.value = false;\n};\n\nfunction handleClose() {\n    onClose();\n    terminalVisible.value = false;\n}\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/container/upgrade/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('commons.button.upgrade')\" @close=\"handleClose\" size=\"large\">\n        <el-alert\n            :title=\"$t('container.appHelper')\"\n            v-if=\"form.fromApp\"\n            class=\"common-prompt\"\n            :closable=\"false\"\n            type=\"warning\"\n        />\n        <el-form @submit.prevent ref=\"formRef\" :model=\"form\" label-position=\"top\">\n            <el-form-item :label=\"$t('container.oldImage')\" prop=\"oldImage\">\n                <el-tooltip placement=\"top-start\" :content=\"form.oldImageName\" v-if=\"form.oldImageName.length > 50\">\n                    <el-tag>{{ form.oldImageName.substring(0, 50) }}...</el-tag>\n                </el-tooltip>\n                <el-tag v-else>{{ form.oldImageName }}</el-tag>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.sameImageContainer')\" v-if=\"containerOptions.length > 1\">\n                <div class=\"w-full\">\n                    <el-checkbox v-model=\"checkAll\" :indeterminate=\"isIndeterminate\" @change=\"handleCheckAllChange\">\n                        {{ $t('commons.table.all') }}\n                    </el-checkbox>\n                </div>\n                <el-checkbox-group v-model=\"form.names\" @change=\"handleCheckedChange\">\n                    <el-checkbox\n                        v-for=\"item in containerOptions\"\n                        :key=\"item.name\"\n                        :label=\"item.name\"\n                        :value=\"item.name\"\n                    >\n                        {{ item.name }}\n                    </el-checkbox>\n                </el-checkbox-group>\n                <span class=\"input-help\">{{ $t('container.sameImageHelper') }}</span>\n            </el-form-item>\n            <el-form-item prop=\"newImageName\" :rules=\"Rules.imageName\">\n                <template #label>\n                    {{ $t('container.targetImage') }}\n                    <span v-if=\"!form.hasName\">\n                        {{ ' (' + $t('container.imageLoadErr') + ')' }}\n                    </span>\n                </template>\n                <el-input v-model=\"form.newImageName\" />\n                <span class=\"input-help\">{{ $t('container.upgradeHelper') }}</span>\n            </el-form-item>\n            <el-form-item prop=\"forcePull\">\n                <el-checkbox v-model=\"form.forcePull\">\n                    {{ $t('container.forcePull') }}\n                </el-checkbox>\n                <span class=\"input-help\">{{ $t('container.forcePullHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading || form.names.length === 0\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"closeTaskLog()\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { listContainerByImage, upgradeContainer } from '@/api/modules/container';\nimport { Rules } from '@/global/form-rules';\nimport TaskLog from '@/components/log/task/index.vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { newUUID } from '@/utils/util';\nimport { CheckboxValueType, ElForm } from 'element-plus';\nimport { reactive, ref } from 'vue';\n\nconst loading = ref(false);\nconst taskLogRef = ref();\n\nconst form = reactive({\n    names: [],\n    oldImageName: '',\n    newImageName: '',\n    hasName: true,\n\n    fromApp: false,\n    forcePull: false,\n});\n\nconst formRef = ref<FormInstance>();\n\nconst drawerVisible = ref<boolean>(false);\ntype FormInstance = InstanceType<typeof ElForm>;\n\nconst containerOptions = ref([]);\nconst isIndeterminate = ref();\nconst checkAll = ref();\n\ninterface DialogProps {\n    container: string;\n    image: string;\n    fromApp: boolean;\n}\nconst acceptParams = (props: DialogProps): void => {\n    form.names = [props.container];\n    isIndeterminate.value = true;\n    form.oldImageName = props.image;\n    form.fromApp = props.fromApp;\n    form.hasName = props.image.indexOf('sha256:') === -1;\n    if (form.hasName) {\n        form.newImageName = props.image;\n    } else {\n        form.newImageName = '';\n    }\n    loadContainers();\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst loadContainers = async () => {\n    const res = await listContainerByImage(form.oldImageName);\n    containerOptions.value = res.data || [];\n};\n\nconst handleCheckAllChange = (val: CheckboxValueType) => {\n    form.names = [];\n    if (!val) {\n        isIndeterminate.value = false;\n        return;\n    }\n    for (const item of containerOptions.value) {\n        form.names.push(item.name);\n    }\n};\nconst handleCheckedChange = (value: CheckboxValueType[]) => {\n    const checkedCount = value.length;\n    checkAll.value = checkedCount === containerOptions.value.length;\n    isIndeterminate.value = checkedCount > 0 && checkedCount < containerOptions.value.length;\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(i18n.global.t('container.upgradeWarning2'), i18n.global.t('commons.button.upgrade'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        }).then(async () => {\n            let taskID = newUUID();\n            let param = {\n                taskID: taskID,\n                names: form.names,\n                image: form.newImageName,\n                forcePull: form.forcePull,\n            };\n            loading.value = true;\n            await upgradeContainer(param)\n                .then(() => {\n                    loading.value = false;\n                    openTaskLog(taskID);\n                    drawerVisible.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst handleClose = async () => {\n    drawerVisible.value = false;\n    emit('search');\n};\n\nconst closeTaskLog = () => {\n    emit('search');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/dashboard/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status\n            v-model:isActive=\"isActive\"\n            v-model:isExist=\"isExist\"\n            v-model:loading=\"loading\"\n            @search=\"search\"\n        />\n\n        <div v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <CardWithHeader :header=\"$t('menu.container')\" class=\"card-interval\">\n                <template #body>\n                    <span class=\"count\" @click=\"routerToName('ContainerItem')\">{{ countItem.containerCount }}</span>\n                </template>\n                <template #header-l>\n                    <el-tag size=\"small\" class=\"ml-5\" v-if=\"countItem.containerCount\" effect=\"plain\">\n                        {{ $t('commons.table.all') }} * {{ countItem.containerCount }}\n                    </el-tag>\n                    <el-tag size=\"small\" v-if=\"countItem.running\" effect=\"plain\" class=\"ml-2\">\n                        {{ $t('commons.status.running') }} * {{ countItem.running }}\n                    </el-tag>\n                    <el-tag size=\"small\" v-if=\"countItem.created\" effect=\"plain\" class=\"ml-2\">\n                        {{ $t('commons.status.created') }} * {{ countItem.created }}\n                    </el-tag>\n                    <el-tag size=\"small\" v-if=\"countItem.paused\" effect=\"plain\" class=\"ml-2\">\n                        {{ $t('commons.status.paused') }} * {{ countItem.paused }}\n                    </el-tag>\n                    <el-tag size=\"small\" v-if=\"countItem.restarting\" effect=\"plain\" class=\"ml-2\">\n                        {{ $t('commons.status.restarting') }} * {{ countItem.restarting }}\n                    </el-tag>\n                    <el-tag size=\"small\" v-if=\"countItem.removing\" effect=\"plain\" class=\"ml-2\">\n                        {{ $t('commons.status.removing') }} * {{ countItem.removing }}\n                    </el-tag>\n                    <el-tag size=\"small\" v-if=\"countItem.exited\" effect=\"plain\" class=\"ml-2\">\n                        {{ $t('commons.status.exited') }} * {{ countItem.exited }}\n                    </el-tag>\n                    <el-tag size=\"small\" v-if=\"countItem.dead\" effect=\"plain\" class=\"ml-2\">\n                        {{ $t('commons.status.dead') }} * {{ countItem.dead }}\n                    </el-tag>\n                </template>\n            </CardWithHeader>\n            <el-row :gutter=\"7\" class=\"card-interval\">\n                <el-col :span=\"8\">\n                    <CardWithHeader :header=\"$t('container.compose')\">\n                        <template #body>\n                            <span class=\"count\" @click=\"routerToName('Compose')\">{{ countItem.composeCount }}</span>\n                        </template>\n                    </CardWithHeader>\n                </el-col>\n                <el-col :span=\"8\">\n                    <CardWithHeader :header=\"$t('container.composeTemplate')\">\n                        <template #body>\n                            <span class=\"count\" @click=\"routerToName('ComposeTemplate')\">\n                                {{ countItem.composeTemplateCount }}\n                            </span>\n                        </template>\n                    </CardWithHeader>\n                </el-col>\n                <el-col :span=\"8\">\n                    <CardWithHeader :header=\"$t('container.image')\">\n                        <template #body>\n                            <span class=\"count\" @click=\"routerToName('Image')\">{{ countItem.imageCount }}</span>\n                        </template>\n                    </CardWithHeader>\n                </el-col>\n            </el-row>\n            <el-row :gutter=\"7\" class=\"card-interval\">\n                <el-col :span=\"8\">\n                    <CardWithHeader :header=\"$t('container.imageRepo')\">\n                        <template #body>\n                            <span class=\"count\" @click=\"routerToName('Repo')\">{{ countItem.repoCount }}</span>\n                        </template>\n                    </CardWithHeader>\n                </el-col>\n                <el-col :span=\"8\">\n                    <CardWithHeader :header=\"$t('container.network')\">\n                        <template #body>\n                            <span class=\"count\" @click=\"routerToName('Network')\">{{ countItem.networkCount }}</span>\n                        </template>\n                    </CardWithHeader>\n                </el-col>\n                <el-col :span=\"8\">\n                    <CardWithHeader :header=\"$t('container.volume')\">\n                        <template #body>\n                            <span class=\"count\" @click=\"routerToName('Volume')\">{{ countItem.volumeCount }}</span>\n                        </template>\n                    </CardWithHeader>\n                </el-col>\n            </el-row>\n\n            <CardWithHeader :header=\"$t('container.diskUsage')\" class=\"card-interval\">\n                <template #body>\n                    <el-descriptions\n                        direction=\"vertical\"\n                        align=\"center\"\n                        v-loading=\"usageLoading\"\n                        :column=\"4\"\n                        class=\"mt-2\"\n                    >\n                        <el-descriptions-item label-width=\"25%\" align=\"center\" :label=\"$t('container.image')\">\n                            {{\n                                $t('container.usage', [\n                                    computeSize2(countItem.imageUsage),\n                                    computeSize2(countItem.imageReclaimable),\n                                ])\n                            }}\n                            <el-button\n                                v-if=\"countItem.imageReclaimable\"\n                                class=\"-mt-0.5\"\n                                @click=\"onClean('image', true)\"\n                                link\n                                type=\"primary\"\n                            >\n                                {{ $t('container.clean') }}\n                            </el-button>\n                        </el-descriptions-item>\n                        <el-descriptions-item label-width=\"25%\" align=\"center\" :label=\"$t('menu.container')\">\n                            {{\n                                $t('container.usage', [\n                                    computeSize2(countItem.containerUsage),\n                                    computeSize2(countItem.containerReclaimable),\n                                ])\n                            }}\n                            <el-button\n                                v-if=\"countItem.containerReclaimable\"\n                                class=\"-mt-0.5\"\n                                @click=\"onClean('container', false)\"\n                                link\n                                type=\"primary\"\n                            >\n                                {{ $t('container.clean') }}\n                            </el-button>\n                        </el-descriptions-item>\n                        <el-descriptions-item label-width=\"25%\" align=\"center\" :label=\"$t('container.localVolume')\">\n                            {{\n                                $t('container.usage', [\n                                    computeSize2(countItem.volumeUsage),\n                                    computeSize2(countItem.volumeReclaimable),\n                                ])\n                            }}\n                            <el-button\n                                v-if=\"countItem.volumeReclaimable\"\n                                class=\"-mt-0.5\"\n                                @click=\"onClean('volume', false)\"\n                                link\n                                type=\"primary\"\n                            >\n                                {{ $t('container.clean') }}\n                            </el-button>\n                        </el-descriptions-item>\n                        <el-descriptions-item label-width=\"25%\" align=\"center\" :label=\"$t('container.buildCache')\">\n                            {{\n                                $t('container.usage', [\n                                    computeSize2(countItem.buildCacheUsage),\n                                    computeSize2(countItem.buildCacheUsage),\n                                ])\n                            }}\n                            <el-button\n                                v-if=\"countItem.buildCacheUsage\"\n                                class=\"-mt-0.5\"\n                                @click=\"onClean('buildcache', false)\"\n                                link\n                                type=\"primary\"\n                            >\n                                {{ $t('container.clean') }}\n                            </el-button>\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </template>\n            </CardWithHeader>\n            <CardWithHeader :header=\"$t('container.setting')\" class=\"card-interval\">\n                <template #body>\n                    <el-descriptions :column=\"1\" border>\n                        <el-descriptions-item label-width=\"200\" :label=\"$t('container.sockPath')\">\n                            {{ countItem.sockPath }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('container.mirrors')\">\n                            <div v-for=\"item in countItem.mirrors\" :key=\"item\" :value=\"item\" :label=\"item\">\n                                <div class=\"mt-1\">\n                                    <el-tag>{{ item }}</el-tag>\n                                </div>\n                            </div>\n                        </el-descriptions-item>\n                    </el-descriptions>\n                    <el-button class=\"mt-2\" type=\"primary\" link @click=\"routerToName('ContainerSetting')\">\n                        <el-icon class=\"mr-1\"><Position /></el-icon>\n                        {{ $t('container.goSetting') }}\n                    </el-button>\n                </template>\n            </CardWithHeader>\n        </div>\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"loadUsage\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { containerItemStats, containerPrune, loadContainerStatus, loadDaemonJson } from '@/api/modules/container';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { getSettingInfo } from '@/api/modules/setting';\nimport { computeSize2, newUUID } from '@/utils/util';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { routerToName } from '@/utils/router';\nimport { onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\n\nconst taskLogRef = ref();\n\nconst loading = ref();\nconst usageLoading = ref(false);\nconst isActive = ref(false);\nconst isExist = ref(false);\nconst countItem = reactive({\n    created: 0,\n    running: 0,\n    paused: 0,\n    restarting: 0,\n    removing: 0,\n    exited: 0,\n    dead: 0,\n\n    containerCount: 0,\n    composeCount: 0,\n    composeTemplateCount: 0,\n    imageCount: 0,\n    networkCount: 0,\n    volumeCount: 0,\n    repoCount: 0,\n\n    containerUsage: 0,\n    containerReclaimable: 0,\n    imageUsage: 0,\n    imageReclaimable: 0,\n    volumeUsage: 0,\n    volumeReclaimable: 0,\n    buildCacheUsage: 0,\n    buildCacheReclaimable: 0,\n\n    mirrors: [],\n    sockPath: '',\n});\n\nconst search = () => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    loadContainerCount();\n    loadUsage();\n    loadContainerSetting();\n};\n\nconst loadContainerCount = async () => {\n    await loadContainerStatus().then((res) => {\n        countItem.running = res.data.running;\n        countItem.paused = res.data.paused;\n        countItem.restarting = res.data.restarting;\n        countItem.removing = res.data.removing;\n        countItem.created = res.data.created;\n        countItem.dead = res.data.dead;\n        countItem.exited = res.data.exited;\n\n        countItem.containerCount = res.data.containerCount;\n        countItem.composeCount = res.data.composeCount;\n        countItem.composeTemplateCount = res.data.composeTemplateCount;\n        countItem.imageCount = res.data.imageCount;\n        countItem.networkCount = res.data.networkCount;\n        countItem.volumeCount = res.data.volumeCount;\n        countItem.repoCount = res.data.repoCount;\n    });\n};\n\nconst loadUsage = async () => {\n    usageLoading.value = true;\n    await containerItemStats('system')\n        .then((res) => {\n            countItem.containerUsage = res.data.containerUsage;\n            countItem.containerReclaimable = res.data.containerReclaimable;\n            countItem.imageUsage = res.data.imageUsage;\n            countItem.imageReclaimable = res.data.imageReclaimable;\n            countItem.volumeUsage = res.data.volumeUsage;\n            countItem.volumeReclaimable = res.data.volumeReclaimable;\n            countItem.buildCacheUsage = res.data.buildCacheUsage;\n            countItem.buildCacheReclaimable = res.data.buildCacheReclaimable;\n        })\n        .finally(() => {\n            usageLoading.value = false;\n        });\n};\n\nconst loadContainerSetting = async () => {\n    const res = await loadDaemonJson();\n    countItem.mirrors = res.data.registryMirrors || [];\n\n    const settingRes = await getSettingInfo();\n    countItem.sockPath = settingRes.data.dockerSockPath || 'unix:///var/run/docker.sock';\n};\n\nconst onClean = async (type: string, withAll: boolean) => {\n    let msg = '';\n    switch (type) {\n        case 'image':\n            msg = i18n.global.t('container.imageClean');\n            break;\n        case 'container':\n            msg = i18n.global.t('container.containerClean');\n            break;\n        case 'volume':\n            msg = i18n.global.t('container.volumePruneHelper');\n            break;\n        case 'buildcache':\n            msg = i18n.global.t('container.delBuildCacheHelper');\n            break;\n    }\n    ElMessageBox.confirm(msg, i18n.global.t('container.clean'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        loading.value = true;\n        let params = {\n            taskID: newUUID(),\n            pruneType: type,\n            withTagAll: withAll,\n        };\n        await containerPrune(params)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(params.taskID);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n\n<style scoped lang=\"scss\">\n.count {\n    margin-left: 20px;\n    font-size: 25px;\n    color: $primary-color;\n    font-weight: 500;\n    line-height: 32px;\n    cursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/docker-status/index.vue",
    "content": "<template>\n    <div>\n        <el-card v-if=\"isExist && !isActive && !prop.isHide\" class=\"mask-prompt\">\n            <span>{{ $t('container.serviceUnavailable') }}</span>\n            <el-button type=\"primary\" link class=\"bt\" @click=\"goSetting\">【 {{ $t('container.setting') }} 】</el-button>\n            <span>{{ $t('container.startIn') }}</span>\n        </el-card>\n        <NoSuchService v-if=\"!isExist\" name=\"Docker\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, ref } from 'vue';\nimport { loadDockerStatus } from '@/api/modules/container';\nimport NoSuchService from '@/components/layout-content/no-such-service.vue';\nimport { routerToName } from '@/utils/router';\n\nconst prop = defineProps({\n    isHide: Boolean,\n});\n\nconst em = defineEmits(['search', 'mounted', 'update:is-active', 'update:is-exist', 'update:loading']);\nconst isActive = ref(true);\nconst isExist = ref(true);\nconst loadStatus = async () => {\n    em('update:loading', true);\n    await loadDockerStatus()\n        .then((res) => {\n            isActive.value = res.data.isActive;\n            isExist.value = res.data.isExist;\n            em('update:loading', false);\n            em('update:is-active', isActive.value);\n            em('update:is-exist', isExist.value);\n            em('search');\n            em('mounted');\n        })\n        .catch(() => {\n            em('update:loading', false);\n            em('update:is-active', false);\n            em('update:is-exist', false);\n        });\n};\n\nconst goSetting = async () => {\n    routerToName('ContainerSetting');\n};\n\nonMounted(() => {\n    loadStatus();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/build/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"$t('container.imageBuild')\"\n        @close=\"handleClose\"\n        size=\"large\"\n        :autoClose=\"false\"\n        :fullScreen=\"true\"\n    >\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" label-width=\"80px\" :rules=\"rules\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input :placeholder=\"$t('container.imageNameHelper')\" v-model.trim=\"form.name\" clearable />\n            </el-form-item>\n            <el-form-item label=\"Dockerfile\" prop=\"from\">\n                <el-radio-group v-model=\"form.from\">\n                    <el-radio value=\"edit\">{{ $t('commons.button.edit') }}</el-radio>\n                    <el-radio value=\"path\">{{ $t('container.pathSelect') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item v-if=\"form.from === 'edit'\" :rules=\"Rules.requiredInput\">\n                <CodemirrorPro\n                    v-model=\"form.dockerfile\"\n                    mode=\"dockerfile\"\n                    placeholder=\"#Define or paste the content of your Dockerfile here\"\n                ></CodemirrorPro>\n            </el-form-item>\n            <el-form-item v-else :rules=\"Rules.requiredSelect\" prop=\"dockerfile\">\n                <el-input clearable v-model=\"form.dockerfile\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({})\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.buildArgs')\">\n                <el-input :placeholder=\"$t('container.tagHelper')\" type=\"textarea\" :rows=\"3\" v-model=\"form.argStr\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('container.tag')\">\n                <el-input :placeholder=\"$t('container.tagHelper')\" type=\"textarea\" :rows=\"3\" v-model=\"form.tagStr\" />\n            </el-form-item>\n        </el-form>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n    <FileList ref=\"fileRef\" @choose=\"loadBuildDir\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport FileList from '@/components/file-list/index.vue';\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { imageBuild } from '@/api/modules/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { newUUID } from '@/utils/util';\nimport { MsgSuccess } from '@/utils/message';\n\nconst drawerVisible = ref(false);\nconst taskLogRef = ref();\nconst fileRef = ref();\n\nconst form = reactive({\n    taskID: '',\n    from: 'path',\n    dockerfile: '',\n    name: '',\n    tagStr: '',\n    tags: [] as Array<string>,\n    argStr: '',\n    args: [] as Array<string>,\n});\n\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.imageName],\n    from: [Rules.requiredSelect],\n    dockerfile: [Rules.requiredInput],\n});\nconst acceptParams = async () => {\n    drawerVisible.value = true;\n    form.from = 'path';\n    form.dockerfile = '';\n    form.tagStr = '';\n    form.argStr = '';\n    form.name = '';\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n    emit('search');\n};\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (form.tagStr !== '') {\n            form.tags = form.tagStr.split('\\n');\n        }\n        if (form.argStr !== '') {\n            form.args = form.argStr.split('\\n');\n        }\n        form.taskID = newUUID();\n        await imageBuild(form);\n        openTaskLog(form.taskID);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst loadBuildDir = async (path: string) => {\n    form.dockerfile = path;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/delete/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"deleteVisible\" :header=\"$t('container.imageDelete')\" @close=\"handleClose\" size=\"small\">\n            <el-form @submit.prevent :model=\"form\" label-position=\"top\">\n                <el-form-item :label=\"$t('container.tag')\" prop=\"tagName\">\n                    <div style=\"width: 100%\">\n                        <el-checkbox\n                            v-model=\"deleteAll\"\n                            :indeterminate=\"isIndeterminate\"\n                            @change=\"handleCheckAllChange\"\n                        >\n                            {{ $t('container.removeAll') }}\n                        </el-checkbox>\n                    </div>\n                    <el-checkbox-group v-model=\"form.deleteTags\" @change=\"handleCheckedChange\">\n                        <div>\n                            <el-checkbox\n                                style=\"width: 100%\"\n                                v-for=\"item in form.tags\"\n                                :key=\"item\"\n                                :value=\"item\"\n                                :label=\"item\"\n                            />\n                        </div>\n                    </el-checkbox-group>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <el-button @click=\"deleteVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" :disabled=\"form.deleteTags.length === 0\" @click=\"batchDelete()\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n        </DrawerPro>\n\n        <OpDialog ref=\"opRef\" @search=\"onSearch\" @cancel=\"handleClose\" />\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { ElForm } from 'element-plus';\nimport { imageRemove } from '@/api/modules/container';\nimport i18n from '@/lang';\n\nconst deleteVisible = ref(false);\nconst form = reactive({\n    id: '',\n    force: false,\n    tags: [] as Array<string>,\n    deleteTags: [] as Array<string>,\n});\n\nconst deleteAll = ref();\nconst isIndeterminate = ref(true);\nconst opRef = ref();\n\ninterface DialogProps {\n    id: string;\n    isUsed: boolean;\n    tags: Array<string>;\n}\nconst acceptParams = (params: DialogProps) => {\n    deleteAll.value = false;\n    deleteVisible.value = true;\n    form.deleteTags = [];\n    form.id = params.id;\n    form.tags = params.tags;\n    form.force = !params.isUsed;\n};\nconst handleClose = () => {\n    deleteVisible.value = false;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst onSearch = () => {\n    emit('search');\n};\n\nconst handleCheckAllChange = (val: boolean) => {\n    form.deleteTags = val ? form.tags : [];\n    isIndeterminate.value = false;\n};\nconst handleCheckedChange = (value: string[]) => {\n    const checkedCount = value.length;\n    deleteAll.value = checkedCount === form.tags.length;\n    isIndeterminate.value = checkedCount > 0 && checkedCount < form.tags.length;\n};\n\nconst batchDelete = async () => {\n    let names = [];\n    let showNames = [];\n    if (deleteAll.value) {\n        names.push(form.id);\n        for (const item of form.deleteTags) {\n            showNames.push(item);\n        }\n    } else {\n        for (const item of form.deleteTags) {\n            names.push(item);\n            showNames.push(item);\n        }\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: showNames,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('container.image'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: imageRemove,\n        params: { names: names, force: form.force },\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status\n            v-model:isActive=\"isActive\"\n            v-model:isExist=\"isExist\"\n            v-model:loading=\"loading\"\n            @search=\"search\"\n            @mounted=\"loadRepos\"\n        />\n\n        <LayoutContent v-if=\"isExist\" :title=\"$t('container.image', 2)\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" plain @click=\"onOpenPull\">\n                    {{ $t('container.imagePull') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onOpenLoad\">\n                    {{ $t('container.importImage') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onOpenBuild\">\n                    {{ $t('container.imageBuild') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onOpenBuildCache()\">\n                    {{ $t('container.cleanBuildCache') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onOpenPrune()\">\n                    {{ $t('container.imagePrune') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"paginationConfig.name\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"image-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    :data=\"data\"\n                    @sort-change=\"search\"\n                    @cell-mouse-enter=\"showFavorite\"\n                    @cell-mouse-leave=\"hideFavorite\"\n                    :columns=\"columns\"\n                    @search=\"search\"\n                    :heightDiff=\"300\"\n                >\n                    <el-table-column label=\"ID\" prop=\"id\" width=\"180\">\n                        <template #default=\"{ row, $index }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"onInspect(row.id)\">\n                                {{ row.id.replaceAll('sha256:', '').substring(0, 12) }}\n                            </el-text>\n                            <div class=\"float-right\">\n                                <el-tooltip\n                                    :content=\"row.isPinned ? $t('website.cancelFavorite') : $t('website.favorite')\"\n                                    v-if=\"row.isPinned || hoveredRowIndex === $index\"\n                                >\n                                    <el-button\n                                        link\n                                        size=\"large\"\n                                        :icon=\"row.isPinned ? 'StarFilled' : 'Star'\"\n                                        type=\"warning\"\n                                        @click=\"changePinned(row, true)\"\n                                    />\n                                </el-tooltip>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"isUsed\" width=\"100\" sortable=\"custom\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.isUsed ? 'used' : 'unused'\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.tag')\"\n                        prop=\"tags\"\n                        sortable=\"custom\"\n                        min-width=\"160\"\n                        :width=\"mobile ? 400 : 'auto'\"\n                        fix\n                    >\n                        <template #default=\"{ row }\">\n                            <el-tag\n                                class=\"ml-2.5\"\n                                v-for=\"(item, index) of row.tags\"\n                                :key=\"index\"\n                                :title=\"item\"\n                                type=\"info\"\n                            >\n                                {{ item }}\n                            </el-tag>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('container.size')\" prop=\"size\" min-width=\"60\" fix sortable=\"custom\">\n                        <template #default=\"{ row }\">\n                            {{ computeSize2(row.size) }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        sortable=\"custom\"\n                        prop=\"createdAt\"\n                        min-width=\"80\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                    />\n                    <fu-table-operations\n                        width=\"250px\"\n                        :ellipsis=\"2\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <CodemirrorDrawer ref=\"myDetail\" />\n        <DialogPro v-model=\"updateDialogVisible\" :title=\"$t('commons.button.update')\" @close=\"handleUpdateDialogClose\">\n            <el-form label-position=\"top\">\n                <el-form-item :label=\"$t('container.tag')\">\n                    <el-checkbox\n                        class=\"w-full\"\n                        :model-value=\"updateCheckAll\"\n                        :indeterminate=\"updateIndeterminate\"\n                        @change=\"onUpdateCheckAllChange\"\n                    >\n                        {{ $t('commons.table.all') }}\n                    </el-checkbox>\n                    <el-checkbox-group v-model=\"updateSelectedTags\">\n                        <el-checkbox v-for=\"tag in updateTagOptions\" :key=\"tag\" :label=\"tag\">\n                            {{ tag }}\n                        </el-checkbox>\n                    </el-checkbox-group>\n                    <span class=\"input-help\">{{ $t('container.imageUpdateHelper') }}</span>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"handleUpdateDialogClose\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button type=\"primary\" @click=\"submitUpdateSelection\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <Pull ref=\"dialogPullRef\" @search=\"search\" />\n        <Tag ref=\"dialogTagRef\" @search=\"search\" />\n        <Push ref=\"dialogPushRef\" @search=\"search\" />\n        <Save ref=\"dialogSaveRef\" @search=\"search\" />\n        <Load ref=\"dialogLoadRef\" @search=\"search\" />\n        <Build ref=\"dialogBuildRef\" @search=\"search\" />\n        <Delete ref=\"dialogDeleteRef\" @search=\"search\" />\n        <Prune ref=\"dialogPruneRef\" @search=\"search\" />\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref, computed } from 'vue';\nimport { dateFormat, newUUID, computeSize2 } from '@/utils/util';\nimport { Container } from '@/api/interface/container';\nimport Pull from '@/views/container/image/pull/index.vue';\nimport Tag from '@/views/container/image/tag/index.vue';\nimport Push from '@/views/container/image/push/index.vue';\nimport Save from '@/views/container/image/save/index.vue';\nimport Load from '@/views/container/image/load/index.vue';\nimport Build from '@/views/container/image/build/index.vue';\nimport Delete from '@/views/container/image/delete/index.vue';\nimport Prune from '@/views/container/image/prune/index.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { searchImage, listImageRepo, imageRemove, inspect, containerPrune, imagePull } from '@/api/modules/container';\nimport i18n from '@/lang';\nimport { GlobalStore } from '@/store';\nimport { ElMessageBox } from 'element-plus';\nimport { updateCommonDescription } from '@/api/modules/setting';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nconst globalStore = GlobalStore();\n\nconst taskLogRef = ref();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref(false);\n\nconst opRef = ref();\n\nconst data = ref();\nconst repos = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'container-image-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('container-image-page-size')) || 20,\n    total: 0,\n    name: '',\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst columns = ref([]);\n\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst hoveredRowIndex = ref(-1);\n\nconst myDetail = ref();\nconst dialogPullRef = ref();\nconst dialogTagRef = ref();\nconst dialogPushRef = ref();\nconst dialogLoadRef = ref();\nconst dialogSaveRef = ref();\nconst dialogBuildRef = ref();\nconst dialogDeleteRef = ref();\nconst dialogPruneRef = ref();\nconst updateDialogVisible = ref(false);\nconst updateTagOptions = ref<Array<string>>([]);\nconst updateSelectedTags = ref<Array<string>>([]);\nconst updateCheckAll = computed(\n    () => updateTagOptions.value.length > 0 && updateSelectedTags.value.length === updateTagOptions.value.length,\n);\nconst updateIndeterminate = computed(\n    () => updateSelectedTags.value.length > 0 && updateSelectedTags.value.length < updateTagOptions.value.length,\n);\n\nconst search = async (column?: any) => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    const params = {\n        name: paginationConfig.name,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    loading.value = true;\n    await searchImage(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst loadRepos = async () => {\n    const res = await listImageRepo();\n    repos.value = res.data || [];\n};\n\nconst onDelete = (row: Container.ImageInfo) => {\n    let names = [row.id.replaceAll('sha256:', '').substring(0, 12)];\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('container.image'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: imageRemove,\n        params: { names: names },\n    });\n};\n\nconst showFavorite = (row: any) => {\n    hoveredRowIndex.value = data.value.findIndex((item) => item === row);\n};\nconst hideFavorite = () => {\n    hoveredRowIndex.value = -1;\n};\nconst changePinned = (row: any, isPinned: boolean) => {\n    let params = {\n        id: row.id.replaceAll('sha256:', ''),\n        type: 'image',\n        detailType: '',\n        isPinned: !row.isPinned,\n        description: row.description || '',\n    };\n    if (isPinned) {\n        params.isPinned = !row.isPinned;\n    }\n    updateCommonDescription(params).then(() => {\n        search();\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\n\nconst onInspect = async (id: string) => {\n    const res = await inspect({ id: id, type: 'image', detail: '' });\n    let detailInfo = JSON.stringify(JSON.parse(res.data), null, 2);\n    let param = {\n        header: i18n.global.t('commons.button.view'),\n        detailInfo: detailInfo,\n        mode: 'json',\n    };\n    myDetail.value!.acceptParams(param);\n};\n\nconst onOpenPull = () => {\n    let params = {\n        repos: repos.value,\n    };\n    dialogPullRef.value!.acceptParams(params);\n};\n\nconst onOpenBuild = () => {\n    dialogBuildRef.value!.acceptParams();\n};\n\nconst onOpenPrune = () => {\n    dialogPruneRef.value!.acceptParams();\n};\n\nconst onOpenBuildCache = () => {\n    ElMessageBox.confirm(i18n.global.t('container.delBuildCacheHelper'), i18n.global.t('container.cleanBuildCache'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        let params = {\n            taskID: newUUID(),\n            pruneType: 'buildcache',\n            withTagAll: false,\n        };\n        await containerPrune(params)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(params.taskID);\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst onOpenLoad = () => {\n    dialogLoadRef.value!.acceptParams();\n};\n\nconst normalizeImageTags = (tags: string[]) => {\n    return (tags || []).filter((tag) => tag && !tag.includes('<none>'));\n};\n\nconst pullImageTags = async (tags: string[]) => {\n    const taskID = newUUID();\n    await imagePull({\n        taskID: taskID,\n        repoID: 0,\n        imageName: tags,\n    });\n    openTaskLog(taskID);\n};\n\nconst runUpdate = async (tags: string[]) => {\n    const validTags = normalizeImageTags(tags);\n    if (validTags.length === 0) {\n        MsgError(i18n.global.t('container.imageUpdateTagEmpty'));\n        return;\n    }\n    try {\n        await ElMessageBox.confirm(\n            i18n.global.t('container.imageUpdateHelper'),\n            i18n.global.t('commons.button.update'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        );\n    } catch {\n        return;\n    }\n    await pullImageTags(validTags);\n};\n\nconst onUpdate = async (row: Container.ImageInfo) => {\n    const tags = normalizeImageTags(row.tags || []);\n    if (tags.length === 0) {\n        MsgError(i18n.global.t('container.imageUpdateTagEmpty'));\n        return;\n    }\n    if (tags.length === 1) {\n        await runUpdate(tags);\n        return;\n    }\n    updateTagOptions.value = tags;\n    updateSelectedTags.value = [...tags];\n    updateDialogVisible.value = true;\n};\n\nconst onUpdateCheckAllChange = (checked: boolean) => {\n    updateSelectedTags.value = checked ? [...updateTagOptions.value] : [];\n};\n\nconst handleUpdateDialogClose = () => {\n    updateDialogVisible.value = false;\n    updateTagOptions.value = [];\n    updateSelectedTags.value = [];\n};\n\nconst submitUpdateSelection = async () => {\n    if (updateSelectedTags.value.length === 0) {\n        MsgError(i18n.global.t('commons.msg.confirmNoNull', [i18n.global.t('container.tag')]));\n        return;\n    }\n    const selected = [...updateSelectedTags.value];\n    handleUpdateDialogClose();\n    await runUpdate(selected);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('container.push'),\n        click: (row: Container.ImageInfo) => {\n            let params = {\n                repos: repos.value,\n                tags: row.tags,\n            };\n            dialogPushRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('container.export'),\n        click: (row: Container.ImageInfo) => {\n            let params = {\n                repos: repos.value,\n                tags: row.tags,\n            };\n            dialogSaveRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.update'),\n        click: (row: Container.ImageInfo) => {\n            onUpdate(row);\n        },\n    },\n    {\n        label: i18n.global.t('container.tag'),\n        click: (row: Container.ImageInfo) => {\n            let params = {\n                repos: repos.value,\n                imageID: row.id,\n                tags: row.tags,\n            };\n            dialogTagRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: async (row: Container.ImageInfo) => {\n            if (row.tags && row.tags.length > 1) {\n                let params = {\n                    id: row.id,\n                    isUsed: row.isUsed,\n                    tags: row.tags,\n                };\n                dialogDeleteRef.value!.acceptParams(params);\n            } else {\n                onDelete(row);\n            }\n        },\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/load/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"loadVisible\" :header=\"$t('container.importImage')\" @close=\"handleClose\" size=\"small\">\n        <el-form @submit.prevent v-loading=\"loading\" ref=\"formRef\" :model=\"form\" label-position=\"top\">\n            <el-form-item :label=\"$t('container.path')\" :rules=\"Rules.requiredInput\" prop=\"path\">\n                <el-input v-model=\"form.path\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: false, multiple: true })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"loadVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.import') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"loadLoadDir\" />\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport FileList from '@/components/file-list/index.vue';\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport TaskLog from '@/components/log/task/index.vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { imageLoad } from '@/api/modules/container';\nimport { MsgSuccess } from '@/utils/message';\nimport { newUUID } from '@/utils/util';\n\nconst loading = ref(false);\nconst fileRef = ref();\nconst taskLogRef = ref();\n\nconst loadVisible = ref(false);\nconst form = reactive({\n    path: '',\n    paths: [] as string[],\n    taskID: '',\n});\n\nconst acceptParams = () => {\n    loadVisible.value = true;\n    form.path = '';\n    form.paths = [];\n};\nconst handleClose = () => {\n    loadVisible.value = false;\n};\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        form.taskID = newUUID();\n        await imageLoad({ paths: form.paths, taskID: form.taskID })\n            .then(() => {\n                loading.value = false;\n                loadVisible.value = false;\n                openTaskLog(form.taskID);\n                emit('search');\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst loadLoadDir = async (paths: string | string[]) => {\n    const newPaths = Array.isArray(paths) ? paths : [paths];\n    form.paths = [...new Set([...form.paths, ...newPaths])];\n    form.path = form.paths.join('; ');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/prune/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"dialogVisible\" :header=\"$t('container.imagePrune')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"deleteForm\" v-loading=\"loading\">\n            <el-form-item>\n                <el-radio-group class=\"w-full\" v-model=\"scope\" @change=\"changeScope\">\n                    <el-radio value=\"untag\">{{ $t('container.imagePruneSome') }}</el-radio>\n                    <el-radio value=\"unused\">{{ $t('container.imagePruneAll') }}</el-radio>\n                </el-radio-group>\n                <span class=\"input-help\">{{ showMsg }}</span>\n                <el-checkbox\n                    class=\"w-full\"\n                    v-if=\"data.length !== 0\"\n                    v-model=\"checkAll\"\n                    :indeterminate=\"isIndeterminate\"\n                    @change=\"handleCheckAllChange\"\n                >\n                    {{ $t('commons.table.all') }}\n                </el-checkbox>\n                <el-checkbox-group v-model=\"checkedLists\" @change=\"handleCheckedChange\">\n                    <el-checkbox class=\"w-full\" v-for=\"(item, index) in data\" :key=\"index\" :value=\"item.id\">\n                        {{\n                            item.tags && item.tags[0]\n                                ? item.tags[0]\n                                : item.id.replaceAll('sha256:', '').substring(0, 12)\n                        }}\n                    </el-checkbox>\n                </el-checkbox-group>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"dialogVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"data.length === 0 || loading\" @click=\"onClean\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"onSearch\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { containerPrune, imageRemove, listAllImage } from '@/api/modules/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport i18n from '@/lang';\nimport { newUUID } from '@/utils/util';\nimport { ref } from 'vue';\n\nconst dialogVisible = ref(false);\nconst scope = ref('untag');\nconst showMsg = ref();\nconst loading = ref();\nconst unTagList = ref([]);\nconst unUsedList = ref([]);\nconst data = ref([]);\nconst taskLogRef = ref();\n\nconst checkAll = ref(false);\nconst isIndeterminate = ref(false);\nconst checkedLists = ref([]);\n\nconst acceptParams = async (): Promise<void> => {\n    const res = await listAllImage();\n    let list = res.data || [];\n    unTagList.value = [];\n    unUsedList.value = [];\n    for (const item of list) {\n        if (\n            !item.tags ||\n            item.tags.length === 0 ||\n            (item.tags.length === 1 && item.tags[0].indexOf('<none>') !== -1 && !item.isUsed)\n        ) {\n            unTagList.value.push(item);\n        }\n        if (!item.isUsed) {\n            unUsedList.value.push(item);\n        }\n    }\n    checkedLists.value = [];\n    checkAll.value = false;\n    isIndeterminate.value = false;\n    dialogVisible.value = true;\n    scope.value = 'untag';\n    changeScope();\n};\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst changeScope = () => {\n    if (scope.value === 'untag') {\n        data.value = unTagList.value || [];\n        showMsg.value =\n            data.value.length !== 0\n                ? i18n.global.t('container.imagePruneSomeHelper')\n                : i18n.global.t('container.imagePruneSomeEmpty');\n        return;\n    }\n    data.value = unUsedList.value || [];\n    showMsg.value =\n        data.value.length !== 0\n            ? i18n.global.t('container.imagePruneAllHelper')\n            : i18n.global.t('container.imagePruneAllEmpty');\n    return;\n};\n\nconst handleCheckAllChange = (val: boolean) => {\n    checkedLists.value = [];\n    if (!val) {\n        isIndeterminate.value = false;\n        return;\n    }\n    for (const item of data.value) {\n        checkedLists.value.push(item.id);\n    }\n};\nconst handleCheckedChange = (value: string[]) => {\n    const checkedCount = value.length;\n    checkAll.value = checkedCount === unUsedList.value.length;\n    isIndeterminate.value = checkedCount > 0 && checkedCount < unUsedList.value.length;\n};\n\nconst handleClose = () => {\n    dialogVisible.value = false;\n};\n\nconst onSearch = () => {\n    emit('search');\n};\n\nconst onClean = async () => {\n    loading.value = true;\n    if (checkAll.value) {\n        prune();\n        return;\n    }\n    removeImage();\n};\n\nconst prune = async () => {\n    let params = {\n        taskID: newUUID(),\n        pruneType: 'image',\n        withTagAll: scope.value === 'unused',\n    };\n    await containerPrune(params)\n        .then(() => {\n            loading.value = false;\n            dialogVisible.value = false;\n            openTaskLog(params.taskID);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst removeImage = async () => {\n    let params = {\n        taskID: newUUID(),\n        names: checkedLists.value,\n    };\n    await imageRemove(params)\n        .then(() => {\n            loading.value = false;\n            dialogVisible.value = false;\n            openTaskLog(params.taskID);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/pull/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('container.imagePull')\" @close=\"onCloseLog\" size=\"large\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\">\n            <el-form-item :label=\"$t('app.source')\">\n                <el-checkbox v-model=\"form.fromRepo\">\n                    {{ $t('container.imageRepo') }}\n                </el-checkbox>\n            </el-form-item>\n            <el-form-item v-if=\"form.fromRepo\" :label=\"$t('container.repoName')\" prop=\"repoID\">\n                <el-select clearable style=\"width: 100%\" filterable v-model=\"form.repoID\">\n                    <el-option v-for=\"item in repos\" :key=\"item.id\" :value=\"item.id\" :label=\"item.name\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.imageName')\" prop=\"imageName\">\n                <el-input-tag v-if=\"form.fromRepo\" v-model=\"form.imageName\">\n                    <template #prefix>{{ loadDetailInfo(form.repoID) }}/</template>\n                </el-input-tag>\n                <el-input-tag v-else v-model=\"form.imageName\" />\n                <span class=\"input-help\">{{ $t('container.imagePullHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('container.pull') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { imagePull } from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { newUUID } from '@/utils/util';\n\nconst drawerVisible = ref(false);\nconst form = reactive({\n    taskID: '',\n    fromRepo: true,\n    repoID: null as number,\n    imageName: [],\n});\nconst verifyImage = (rule: any, value: any, callback: any) => {\n    if (!value || value.length === 0) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n        return;\n    }\n    for (const item of value) {\n        if (item === '' || typeof item === 'undefined' || item == null) {\n            callback(new Error(i18n.global.t('commons.rule.imageName')));\n        } else {\n            const reg = /^[a-zA-Z0-9]{1}[a-z:@A-Z0-9_/.-]{0,255}$/;\n            if (!reg.test(item) && item !== '') {\n                callback(new Error(i18n.global.t('commons.rule.imageName')));\n                return;\n            }\n        }\n    }\n    callback();\n};\nconst rules = reactive({\n    imageName: [{ validator: verifyImage, trigger: 'blur', required: true }],\n    repoID: Rules.requiredSelect,\n});\n\nconst taskLogRef = ref();\n\ninterface DialogProps {\n    repos: Array<Container.RepoOptions>;\n}\nconst repos = ref();\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    drawerVisible.value = true;\n    form.fromRepo = true;\n    form.imageName = [];\n    repos.value = params.repos;\n    form.repoID = 1;\n    for (const item of repos.value) {\n        if (item.name === 'Docker Hub' && item.downloadUrl === 'docker.io') {\n            form.repoID = item.id;\n            break;\n        }\n    }\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (!form.fromRepo) {\n            form.repoID = 0;\n        }\n        form.taskID = newUUID();\n        await imagePull(form);\n        openTaskLog(form.taskID);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst onCloseLog = async () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\nfunction loadDetailInfo(id: number) {\n    for (const item of repos.value) {\n        if (item.id === id) {\n            return item.downloadUrl;\n        }\n    }\n    return '';\n}\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/push/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('container.imagePush')\" @close=\"onCloseLog\" size=\"large\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" label-width=\"80px\">\n            <el-form-item :label=\"$t('container.tag')\" :rules=\"Rules.requiredSelect\" prop=\"tagName\">\n                <el-select filterable v-model=\"form.tagName\" @change=\"handleChange\">\n                    <el-option v-for=\"item in form.tags\" :key=\"item\" :value=\"item\" :label=\"item\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.repoName')\" :rules=\"Rules.requiredSelect\" prop=\"repoID\">\n                <el-select clearable class=\"w-full\" filterable v-model=\"form.repoID\" @change=\"handleChange\">\n                    <el-option v-for=\"item in dialogData.repos\" :key=\"item.id\" :value=\"item.id\" :label=\"item.name\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.image')\" :rules=\"Rules.imageName\" prop=\"name\">\n                <el-input v-model.trim=\"form.name\">\n                    <template #prepend>{{ loadDetailInfo(form.repoID) }}/</template>\n                </el-input>\n                <span v-if=\"form.tags?.length !== 1\" class=\"input-help\">\n                    {{ $t('container.imagePushHelper', [loadDetailInfo(form.repoID) + '/' + form.name]) }}\n                </span>\n            </el-form-item>\n        </el-form>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('container.push') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { imagePush } from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\nimport { MsgSuccess } from '@/utils/message';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { newUUID } from '@/utils/util';\n\nconst drawerVisible = ref(false);\nconst taskLogRef = ref();\nconst form = reactive({\n    taskID: '',\n    tags: [] as Array<string>,\n    tagName: '',\n    repoID: 1,\n    name: '',\n});\n\ninterface DialogProps {\n    repos: Array<Container.RepoOptions>;\n    tags: Array<string>;\n}\nconst dialogData = ref<DialogProps>({\n    repos: [] as Array<Container.RepoOptions>,\n    tags: [] as Array<string>,\n});\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    drawerVisible.value = true;\n    form.tags = params.tags;\n    form.repoID = 1;\n    form.tagName = form.tags.length !== 0 ? form.tags[0] : '';\n    form.name = form.tags.length !== 0 ? form.tags[0] : '';\n    dialogData.value.repos = params.repos;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst handleChange = () => {\n    let repoURL = loadDetailInfo(form.repoID);\n    form.name = form.tagName.indexOf(repoURL) !== -1 ? form.tagName.replaceAll(repoURL + '/', '') : form.tagName;\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        form.taskID = newUUID();\n        await imagePush(form);\n        openTaskLog(form.taskID);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst onCloseLog = async () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\nfunction loadDetailInfo(id: number) {\n    for (const item of dialogData.value.repos) {\n        if (item.id === id) {\n            return item.downloadUrl;\n        }\n    }\n    return '';\n}\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/save/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('container.exportImage')\" @close=\"handleClose\" size=\"small\">\n        <el-form v-loading=\"loading\" label-position=\"top\" ref=\"formRef\" :model=\"form\" label-width=\"80px\">\n            <el-form-item :label=\"$t('container.tag')\" :rules=\"Rules.requiredSelect\" prop=\"tagName\">\n                <el-select filterable v-model=\"form.tagName\">\n                    <el-option\n                        :disabled=\"item.indexOf(':<none>') !== -1\"\n                        v-for=\"item in form.tags\"\n                        :key=\"item\"\n                        :value=\"item\"\n                        :label=\"item\"\n                    />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.path')\" :rules=\"Rules.requiredInput\" prop=\"path\">\n                <el-input v-model=\"form.path\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: true })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.fileName')\" :rules=\"Rules.requiredInput\" prop=\"name\">\n                <el-input v-model=\"form.name\">\n                    <template #append>.tar</template>\n                </el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                {{ $t('container.export') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"loadSaveDir\" />\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport FileList from '@/components/file-list/index.vue';\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { imageSave } from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { newUUID } from '@/utils/util';\n\nconst loading = ref(false);\nconst fileRef = ref();\nconst taskLogRef = ref();\n\nconst drawerVisible = ref(false);\nconst form = reactive({\n    taskID: '',\n    tags: [] as Array<string>,\n    tagName: '',\n    path: '',\n    name: '',\n});\n\ninterface DialogProps {\n    repos: Array<Container.RepoOptions>;\n    tags: Array<string>;\n}\nconst dialogData = ref<DialogProps>({\n    repos: [] as Array<Container.RepoOptions>,\n    tags: [] as Array<string>,\n});\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    drawerVisible.value = true;\n    form.tags = params.tags;\n    form.tagName = form.tags.length !== 0 ? form.tags[0] : '';\n    form.path = '';\n    form.name = '';\n    dialogData.value.repos = params.repos;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        form.taskID = newUUID();\n        await imageSave(form)\n            .then(() => {\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                openTaskLog(form.taskID);\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst loadSaveDir = async (path: string) => {\n    form.path = path;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/image/tag/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('container.imageTag')\" @close=\"handleClose\" size=\"large\">\n        <el-form\n            v-loading=\"loading\"\n            label-position=\"top\"\n            ref=\"formRef\"\n            :model=\"form\"\n            :rules=\"rules\"\n            label-width=\"80px\"\n            @submit.prevent\n        >\n            <el-form-item :label=\"$t('app.source')\">\n                <el-checkbox v-model=\"form.fromRepo\">{{ $t('container.imageRepo') }}</el-checkbox>\n            </el-form-item>\n            <el-form-item\n                v-if=\"form.fromRepo\"\n                :label=\"$t('container.repoName')\"\n                :rules=\"Rules.requiredSelect\"\n                prop=\"repo\"\n            >\n                <el-select clearable filterable v-model=\"form.repo\" @change=\"changeRepo\">\n                    <el-option v-for=\"item in repos\" :key=\"item.id\" :value=\"item.name\" :label=\"item.name\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.imageTag')\" prop=\"tags\">\n                <el-input-tag ref=\"inputTagRef\" @add-tag=\"handleAdd\" v-model=\"form.tags\">\n                    <template #tag=\"{ value }\">\n                        <el-button @click=\"setInputValue(value)\" size=\"small\" link type=\"info\">\n                            {{ value }}\n                        </el-button>\n                    </template>\n                </el-input-tag>\n                <span class=\"input-help\">{{ $t('container.imageTagHelper') }}</span>\n            </el-form-item>\n        </el-form>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.save') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { imageTag } from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref(false);\nconst inputTagRef = ref();\n\nconst drawerVisible = ref(false);\nconst repos = ref();\nconst form = reactive({\n    imageID: '',\n    fromRepo: false,\n    repo: '',\n    originName: '',\n\n    tags: [],\n});\nconst rules = reactive({\n    tags: [{ validator: checkTags, trigger: 'blur', required: true }],\n});\nfunction checkTags(rule: any, value: any, callback: any) {\n    if (value.length === 0) {\n        return callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n    }\n    for (const item of value) {\n        if (item === '' || typeof item === 'undefined' || item == null) {\n            return callback(new Error(i18n.global.t('commons.rule.imageName')));\n        } else {\n            const reg = /^[a-zA-Z0-9]{1}[a-z:@A-Z0-9_/.-]{0,255}$/;\n            if (!reg.test(item) && item !== '') {\n                return callback(new Error(i18n.global.t('commons.rule.imageName')));\n            } else {\n                return callback();\n            }\n        }\n    }\n    callback();\n}\n\ninterface DialogProps {\n    repos: Array<Container.RepoOptions>;\n    imageID: string;\n    tags: Array<string>;\n}\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    drawerVisible.value = true;\n    form.imageID = params.imageID;\n    form.originName = params.tags?.length !== 0 ? params.tags[0] : '';\n    form.tags = params.tags || [];\n    form.fromRepo = false;\n    form.repo = '';\n    repos.value = params.repos;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst handleAdd = (val: string) => {\n    form.tags = form.tags?.filter((item) => item !== val);\n    form.tags.push(val);\n};\nconst setInputValue = async (text) => {\n    await nextTick();\n    const inputEl = inputTagRef.value?.$el?.querySelector('input');\n    if (!inputEl) return;\n\n    inputEl.value = text;\n    inputEl.dispatchEvent(new Event('input', { bubbles: true }));\n    inputEl.dispatchEvent(new Event('change', { bubbles: true }));\n    inputEl.setSelectionRange(text.length, text.length);\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let params = {\n            sourceID: form.imageID,\n            tags: form.tags,\n        };\n        loading.value = true;\n        await imageTag(params)\n            .then(async () => {\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst changeRepo = (val) => {\n    if (val === 'Docker Hub') {\n        return;\n    }\n    for (const item of repos.value) {\n        if (item.name == val) {\n            form.tags.push(item.downloadUrl + '/' + form.originName);\n            return;\n        }\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.home'),\n        path: '/containers/dashboard',\n    },\n    {\n        label: i18n.global.t('menu.container', 2),\n        path: '/containers/container',\n    },\n    {\n        label: i18n.global.t('container.compose', 2),\n        path: '/containers/compose',\n    },\n    {\n        label: i18n.global.t('container.image', 2),\n        path: '/containers/image',\n    },\n    {\n        label: i18n.global.t('container.network', 2),\n        path: '/containers/network',\n    },\n    {\n        label: i18n.global.t('container.volume', 2),\n        path: '/containers/volume',\n    },\n    {\n        label: i18n.global.t('container.repo', 2),\n        path: '/containers/repo',\n    },\n    {\n        label: i18n.global.t('container.composeTemplate', 2),\n        path: '/containers/template',\n    },\n    {\n        label: i18n.global.t('container.setting', 2),\n        path: '/containers/setting',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/network/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('commons.button.create')\" @close=\"handleClose\" size=\"large\">\n        <el-form ref=\"formRef\" label-position=\"top\" v-loading=\"loading\" :model=\"form\" :rules=\"rules\" label-width=\"80px\">\n            <el-form-item :label=\"$t('container.networkName')\" prop=\"name\">\n                <el-input clearable v-model.trim=\"form.name\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('container.driver')\" prop=\"driver\">\n                <el-select v-model=\"form.driver\">\n                    <el-option label=\"bridge\" value=\"bridge\" />\n                    <el-option label=\"ipvlan\" value=\"ipvlan\" />\n                    <el-option label=\"macvlan\" value=\"macvlan\" />\n                    <el-option label=\"overlay\" value=\"overlay\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item\n                v-if=\"form.driver === 'macvlan'\"\n                :label=\"$t('container.parentNetworkCard')\"\n                prop=\"parentNetworkCard\"\n            >\n                <el-select v-model=\"form.parentNetworkCard\">\n                    <span v-for=\"item in netOptions\" :key=\"item\">\n                        <el-option v-if=\"item !== 'all'\" :label=\"item\" :value=\"item\" />\n                    </span>\n                </el-select>\n            </el-form-item>\n\n            <el-checkbox v-model=\"form.ipv4\">IPv4</el-checkbox>\n            <div v-if=\"form.ipv4\">\n                <el-row type=\"flex\" justify=\"center\" :gutter=\"20\">\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('container.subnet')\" prop=\"subnet\">\n                            <el-input placeholder=\"172.13.0.0/16\" clearable v-model.trim=\"form.subnet\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('container.gateway')\" prop=\"gateway\">\n                            <el-input placeholder=\"172.13.10.254\" clearable v-model.trim=\"form.gateway\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('container.scope')\" prop=\"ipRange\">\n                            <el-input placeholder=\"172.13.1.0/24\" clearable v-model.trim=\"form.ipRange\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\"></el-col>\n                </el-row>\n                <el-form-item :label=\"$t('container.auxAddress')\" prop=\"auxAddress\">\n                    <el-table :data=\"form.auxAddress\" v-if=\"form.auxAddress.length !== 0\">\n                        <el-table-column :label=\"$t('container.label')\" min-width=\"100\">\n                            <template #default=\"{ row }\">\n                                <el-input placeholder=\"my-router\" v-model=\"row.key\" />\n                            </template>\n                        </el-table-column>\n                        <el-table-column label=\"IP\" min-width=\"150\">\n                            <template #default=\"{ row }\">\n                                <el-input placeholder=\"172.13.1.13\" v-model=\"row.value\" />\n                            </template>\n                        </el-table-column>\n                        <el-table-column min-width=\"40\">\n                            <template #default=\"scope\">\n                                <el-button link type=\"primary\" @click=\"handleV4Delete(scope.$index)\">\n                                    {{ $t('commons.button.delete') }}\n                                </el-button>\n                            </template>\n                        </el-table-column>\n                    </el-table>\n                    <el-button class=\"mt-2\" @click=\"handleV4Add()\">\n                        {{ $t('commons.button.add') }}\n                    </el-button>\n                </el-form-item>\n            </div>\n\n            <el-checkbox class=\"mb-4\" v-model=\"form.ipv6\">IPv6</el-checkbox>\n            <div v-if=\"form.ipv6\">\n                <el-row type=\"flex\" justify=\"center\" :gutter=\"20\">\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('container.subnet')\" prop=\"subnetV6\">\n                            <el-input placeholder=\"2408:400e::/48\" clearable v-model.trim=\"form.subnetV6\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('container.gateway')\" prop=\"gatewayV6\">\n                            <el-input placeholder=\"2408:400e::1\" clearable v-model.trim=\"form.gatewayV6\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('container.scope')\" prop=\"ipRangeV6\">\n                            <el-input placeholder=\"2408:400e::/64\" clearable v-model.trim=\"form.ipRangeV6\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\"></el-col>\n                </el-row>\n                <el-form-item :label=\"$t('container.auxAddress')\" prop=\"auxAddressV6\">\n                    <el-table :data=\"form.auxAddressV6\" v-if=\"form.auxAddressV6.length !== 0\">\n                        <el-table-column :label=\"$t('container.label')\" min-width=\"100\">\n                            <template #default=\"{ row }\">\n                                <el-input placeholder=\"my-router\" v-model=\"row.key\" />\n                            </template>\n                        </el-table-column>\n                        <el-table-column label=\"IP\" min-width=\"150\">\n                            <template #default=\"{ row }\">\n                                <el-input placeholder=\"2408:400e::3\" v-model=\"row.value\" />\n                            </template>\n                        </el-table-column>\n                        <el-table-column min-width=\"40\">\n                            <template #default=\"scope\">\n                                <el-button link type=\"primary\" @click=\"handleV6Delete(scope.$index)\">\n                                    {{ $t('commons.button.delete') }}\n                                </el-button>\n                            </template>\n                        </el-table-column>\n                    </el-table>\n                    <el-button class=\"mt-2\" @click=\"handleV6Add()\">\n                        {{ $t('commons.button.add') }}\n                    </el-button>\n                </el-form-item>\n            </div>\n\n            <el-form-item :label=\"$t('container.option')\" prop=\"optionStr\">\n                <el-input type=\"textarea\" :placeholder=\"$t('container.tagHelper')\" :rows=\"3\" v-model=\"form.optionStr\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('container.tag')\" prop=\"labelStr\">\n                <el-input type=\"textarea\" :placeholder=\"$t('container.tagHelper')\" :rows=\"3\" v-model=\"form.labelStr\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { createNetwork } from '@/api/modules/container';\nimport { MsgSuccess } from '@/utils/message';\nimport { checkIp, checkIpV6 } from '@/utils/util';\nimport { getNetworkOptions } from '@/api/modules/host';\n\nconst loading = ref(false);\n\nconst drawerVisible = ref(false);\nconst form = reactive({\n    name: '',\n    parentNetworkCard: '',\n    labelStr: '',\n    labels: [] as Array<string>,\n    optionStr: '',\n    options: [] as Array<string>,\n    driver: '',\n    ipv4: true,\n    subnet: '',\n    gateway: '',\n    ipRange: '',\n    auxAddress: [],\n    ipv6: false,\n    subnetV6: '',\n    gatewayV6: '',\n    ipRangeV6: '',\n    auxAddressV6: [],\n});\n\nconst acceptParams = (): void => {\n    form.name = '';\n    form.labelStr = '';\n    form.labels = [];\n    form.optionStr = '';\n    form.options = [];\n    form.driver = 'bridge';\n    form.parentNetworkCard = '';\n    form.ipv4 = true;\n    form.subnet = '';\n    form.gateway = '';\n    form.ipRange = '';\n    form.auxAddress = [];\n    form.ipv6 = false;\n    form.subnetV6 = '';\n    form.gatewayV6 = '';\n    form.ipRangeV6 = '';\n    form.auxAddressV6 = [];\n    drawerVisible.value = true;\n    loadNetworkOptions();\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst netOptions = ref();\n\nconst rules = reactive({\n    name: [Rules.requiredInput],\n    driver: [Rules.requiredSelect],\n    parentNetworkCard: [Rules.requiredSelect],\n    subnet: [{ validator: checkCidr, trigger: 'blur' }, Rules.requiredInput],\n    gateway: [{ validator: checkGateway, trigger: 'blur' }],\n    ipRange: [{ validator: checkCidr, trigger: 'blur' }],\n    subnetV6: [{ validator: checkFixedCidrV6, trigger: 'blur' }, Rules.requiredInput],\n    gatewayV6: [{ validator: checkGatewayV6, trigger: 'blur' }],\n    ipRangeV6: [{ validator: checkFixedCidrV6, trigger: 'blur' }],\n});\n\nfunction checkGateway(rule: any, value: any, callback: any) {\n    if (value === '') {\n        callback();\n    }\n    if (checkIp(value)) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    callback();\n}\n\nfunction checkGatewayV6(rule: any, value: any, callback: any) {\n    if (value === '') {\n        callback();\n    }\n    if (checkIpV6(value)) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    callback();\n}\n\nfunction checkCidr(rule: any, value: any, callback: any) {\n    if (value === '') {\n        callback();\n    }\n    const reg =\n        /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\\/([0-9]|[1-2][0-9]|3[0-2]))$/;\n    if (!reg.test(value)) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    callback();\n}\n\nfunction checkFixedCidrV6(rule: any, value: any, callback: any) {\n    if (value === '') {\n        callback();\n    }\n    if (!form.subnetV6 || form.subnetV6.indexOf('/') === -1) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    if (checkIpV6(form.subnetV6.split('/')[0])) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    const reg = /^(?:[1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/;\n    if (!reg.test(form.subnetV6.split('/')[1])) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    callback();\n}\n\nconst handleV4Add = () => {\n    let item = {\n        key: '',\n        value: '',\n    };\n    form.auxAddress.push(item);\n};\nconst handleV4Delete = (index: number) => {\n    form.auxAddress.splice(index, 1);\n};\n\nconst handleV6Add = () => {\n    let item = {\n        key: '',\n        value: '',\n    };\n    form.auxAddressV6.push(item);\n};\nconst handleV6Delete = (index: number) => {\n    form.auxAddressV6.splice(index, 1);\n};\n\nconst loadNetworkOptions = async () => {\n    const res = await getNetworkOptions();\n    netOptions.value = res.data;\n};\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (form.labelStr !== '') {\n            form.labels = form.labelStr.split('\\n');\n        }\n        if (form.optionStr !== '') {\n            form.options = form.optionStr.split('\\n');\n        }\n        loading.value = true;\n        if (form.driver === 'macvlan') {\n            form.options.push('parent=' + form.parentNetworkCard);\n        }\n        await createNetwork(form)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/network/detail/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"visible\" :header=\"$t('commons.button.view')\" size=\"large\" @close=\"handleClose\">\n        <el-tabs v-model=\"activeTab\" type=\"border-card\">\n            <el-tab-pane :label=\"$t('container.network')\" name=\"overview\">\n                <el-descriptions :column=\"1\" border :title=\"$t('home.baseInfo')\">\n                    <el-descriptions-item :label=\"$t('commons.table.name')\">\n                        <el-text>{{ networkData?.Name || '-' }}</el-text>\n                        <CopyButton :content=\"networkData?.Name || ''\" />\n                    </el-descriptions-item>\n                    <el-descriptions-item label=\"ID\">\n                        <el-text class=\"text-xs\">{{ networkData?.Id?.substring(0, 12) }}</el-text>\n                        <CopyButton :content=\"networkData?.Id || ''\" />\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('container.driver')\">\n                        {{ networkData?.Driver || '-' }}\n                    </el-descriptions-item>\n                    <el-descriptions-item\n                        v-if=\"networkData?.Options?.parent\"\n                        :label=\"$t('container.parentNetworkCard')\"\n                    >\n                        {{ networkData?.Options?.parent }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('commons.table.createdAt')\">\n                        {{ formatDate(networkData?.Created) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item label=\"IPv4\">\n                        <el-tag :type=\"networkData?.EnableIPv4 ? 'success' : 'info'\">\n                            {{ networkData?.EnableIPv4 ? $t('commons.status.enable') : $t('commons.status.disable') }}\n                        </el-tag>\n                    </el-descriptions-item>\n                    <el-descriptions-item label=\"IPv6\">\n                        <el-tag :type=\"networkData?.EnableIPv6 ? 'success' : 'info'\">\n                            {{ networkData?.EnableIPv6 ? $t('commons.status.enable') : $t('commons.status.disable') }}\n                        </el-tag>\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <el-descriptions class=\"mt-4\" :column=\"1\" border title=\"IPAM\">\n                    <el-descriptions-item :label=\"$t('container.driver')\">\n                        {{ networkData?.IPAM?.Driver || '-' }}\n                    </el-descriptions-item>\n                    <el-descriptions-item\n                        v-for=\"(config, index) in networkData?.IPAM?.Config\"\n                        :key=\"index\"\n                        :label=\"$t('container.subnet') + (index > 0 ? ' ' + (index + 1) : '')\"\n                    >\n                        <div v-if=\"config\">\n                            <el-tag v-if=\"config.Subnet\" type=\"info\" :label=\"$t('container.subnet')\">\n                                {{ $t('container.subnet') }}: {{ config.Subnet }}\n                            </el-tag>\n                            <el-tag class=\"ml-2\" v-if=\"config.Gateway\" type=\"info\" :label=\"$t('container.gateway')\">\n                                {{ $t('container.gateway') }}: {{ config.Gateway }}\n                            </el-tag>\n                            <el-tag class=\"ml-2\" v-if=\"config.IPRange\" type=\"info\" :label=\"$t('container.scope')\">\n                                {{ $t('container.scope') }}: {{ config.IPRange }}\n                            </el-tag>\n                        </div>\n                        <span v-else>-</span>\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <div class=\"mt-4\">\n                    <span class=\"block text-base font-medium text-el-color-primary\">{{ $t('menu.container') }}</span>\n                    <el-table :data=\"containerList\" border class=\"mt-2\">\n                        <el-table-column :label=\"$t('commons.table.name')\" min-width=\"120\">\n                            <template #default=\"{ row }\">\n                                <el-text>{{ row.name }}</el-text>\n                                <CopyButton :content=\"row.name\" />\n                            </template>\n                        </el-table-column>\n                        <el-table-column label=\"IPv4\" min-width=\"100\">\n                            <template #default=\"{ row }\">\n                                {{ row.ipv4 || '-' }}\n                            </template>\n                        </el-table-column>\n                        <el-table-column label=\"IPv6\" min-width=\"100\">\n                            <template #default=\"{ row }\">\n                                {{ row.ipv6 || '-' }}\n                            </template>\n                        </el-table-column>\n                        <el-table-column label=\"MAC\" min-width=\"120\">\n                            <template #default=\"{ row }\">\n                                {{ row.mac || '-' }}\n                            </template>\n                        </el-table-column>\n                        <el-table-column label=\"Endpoint ID\" width=\"120\">\n                            <template #default=\"{ row }\">\n                                <el-text class=\"text-xs\">{{ row.endpointId?.substring(0, 12) || '-' }}</el-text>\n                            </template>\n                        </el-table-column>\n                    </el-table>\n                </div>\n            </el-tab-pane>\n\n            <el-tab-pane :label=\"$t('commons.button.view')\" name=\"raw\">\n                <div class=\"mb-2 flex justify-start\">\n                    <el-button type=\"primary\" @click=\"handleCopyRawJson\" icon=\"DocumentCopy\" size=\"default\">\n                        {{ $t('commons.button.copy') }}\n                    </el-button>\n                </div>\n                <CodemirrorPro v-model=\"rawJson\" :height-diff=\"270\" :readonly=\"true\" mode=\"json\" />\n            </el-tab-pane>\n        </el-tabs>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, computed } from 'vue';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport { copyText } from '@/utils/util';\n\nconst visible = ref(false);\nconst activeTab = ref('overview');\nconst networkData = ref<any>(null);\nconst rawJson = ref('');\n\ninterface DialogProps {\n    data: any;\n}\n\nconst acceptParams = (props: DialogProps): void => {\n    visible.value = true;\n    activeTab.value = 'overview';\n\n    try {\n        if (typeof props.data === 'string') {\n            networkData.value = JSON.parse(props.data);\n        } else {\n            networkData.value = props.data;\n        }\n        rawJson.value = JSON.stringify(networkData.value, null, 2);\n    } catch (e) {\n        console.error('Failed to parse network data:', e);\n    }\n};\n\nconst handleClose = () => {\n    visible.value = false;\n    networkData.value = null;\n    rawJson.value = '';\n};\n\nconst formatDate = (dateStr: string): string => {\n    if (!dateStr || dateStr === '0001-01-01T00:00:00Z') {\n        return '-';\n    }\n    try {\n        const date = new Date(dateStr);\n        const y = date.getFullYear();\n        const m = String(date.getMonth() + 1).padStart(2, '0');\n        const d = String(date.getDate()).padStart(2, '0');\n        const h = String(date.getHours()).padStart(2, '0');\n        const minute = String(date.getMinutes()).padStart(2, '0');\n        const second = String(date.getSeconds()).padStart(2, '0');\n        return `${y}-${m}-${d} ${h}:${minute}:${second}`;\n    } catch {\n        return dateStr;\n    }\n};\n\nconst containerList = computed(() => {\n    if (!networkData.value?.Containers) {\n        return [];\n    }\n\n    return Object.entries(networkData.value.Containers).map(([id, container]: [string, any]) => ({\n        id,\n        name: container.Name || '-',\n        ipv4: container.IPv4Address || '',\n        ipv6: container.IPv6Address || '',\n        mac: container.MacAddress || '',\n        endpointId: container.EndpointID || '',\n    }));\n});\n\nconst handleCopyRawJson = () => {\n    copyText(rawJson.value);\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n:deep(.el-descriptions__label) {\n    width: 180px;\n    background-color: transparent !important;\n}\n.text-el-color-primary {\n    color: var(--el-text-color-primary);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/container/network/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status\n            v-model:isActive=\"isActive\"\n            v-model:isExist=\"isExist\"\n            v-model:loading=\"loading\"\n            @search=\"search\"\n        />\n\n        <LayoutContent v-if=\"isExist\" :title=\"$t('container.network', 2)\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onCreate()\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onClean()\">\n                    {{ $t('container.networkPrune') }}\n                </el-button>\n                <el-button :disabled=\"selects.length === 0\" @click=\"batchDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"network-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @search=\"search\"\n                    :heightDiff=\"300\"\n                >\n                    <el-table-column type=\"selection\" :selectable=\"selectable\" fix />\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        width=\"130\"\n                        prop=\"name\"\n                        fix\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"onInspect(row.id)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column width=\"90\">\n                        <template #default=\"{ row }\">\n                            <el-tag round v-if=\"row.isSystem || row.name === '1panel-network'\">system</el-tag>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.driver')\"\n                        show-overflow-tooltip\n                        min-width=\"60\"\n                        prop=\"driver\"\n                    />\n                    <el-table-column :label=\"$t('container.subnet')\" min-width=\"80\" prop=\"subnet\" fix />\n                    <el-table-column :label=\"$t('container.gateway')\" min-width=\"80\" prop=\"gateway\" fix />\n                    <el-table-column :label=\"$t('container.tag')\" min-width=\"140\" fix>\n                        <template #default=\"{ row }\">\n                            <div v-for=\"(item, index) in row.labels\" :key=\"index\">\n                                <div v-if=\"row.expand || (!row.expand && index < 3)\">\n                                    <el-button class=\"mt-0.5\" plain size=\"small\">{{ item }}</el-button>\n                                </div>\n                            </div>\n                            <div v-if=\"!row.expand && row.labels.length > 3\">\n                                <el-button link @click=\"row.expand = true\">\n                                    {{ $t('commons.button.expand') }}...\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        show-overflow-tooltip\n                        min-width=\"90\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                    />\n                    <fu-table-operations width=\"100\" :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <DetailDrawer ref=\"detailDrawerRef\" />\n        <CreateDialog @search=\"search\" ref=\"dialogCreateRef\" />\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport CreateDialog from '@/views/container/network/create/index.vue';\nimport DetailDrawer from '@/views/container/network/detail/index.vue';\nimport { reactive, ref } from 'vue';\nimport { dateFormat, newUUID } from '@/utils/util';\nimport { deleteNetwork, searchNetwork, inspect, containerPrune } from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport i18n from '@/lang';\nimport { ElMessageBox } from 'element-plus';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\n\nconst loading = ref();\nconst detailDrawerRef = ref();\nconst taskLogRef = ref();\n\nconst data = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    cacheSizeKey: 'container-network-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('container-network-page-size')) || 20,\n    total: 0,\n});\nconst searchName = ref();\n\nconst opRef = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\nconst dialogCreateRef = ref<DialogExpose>();\n\ninterface DialogExpose {\n    acceptParams: () => void;\n}\nconst onCreate = async () => {\n    dialogCreateRef.value!.acceptParams();\n};\n\nconst onClean = () => {\n    ElMessageBox.confirm(i18n.global.t('container.networkPruneHelper'), i18n.global.t('container.networkPrune'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        let params = {\n            taskID: newUUID(),\n            pruneType: 'network',\n            withTagAll: false,\n        };\n        await containerPrune(params)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(params.taskID);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nfunction selectable(row) {\n    return !row.isSystem;\n}\n\nconst search = async () => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    const params = {\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchNetwork(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            for (const item of data.value) {\n                item.isSystem = isSystem(item.name);\n            }\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst batchDelete = async (row: Container.NetworkInfo | null) => {\n    let names: Array<string> = [];\n    let hasPanelNetwork;\n    if (row === null) {\n        selects.value.forEach((item: Container.NetworkInfo) => {\n            if (item.name === '1panel-network') {\n                hasPanelNetwork = true;\n            }\n            names.push(item.name);\n        });\n    } else {\n        if (row.name === '1panel-network') {\n            hasPanelNetwork = true;\n        }\n        names.push(row.name);\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: hasPanelNetwork\n            ? i18n.global.t('container.networkHelper')\n            : i18n.global.t('commons.msg.operatorHelper', [\n                  i18n.global.t('container.network'),\n                  i18n.global.t('commons.button.delete'),\n              ]),\n        api: deleteNetwork,\n        params: { names: names },\n    });\n};\n\nconst onInspect = async (id: string) => {\n    const res = await inspect({ id: id, type: 'network', detail: '' });\n    let networkData = JSON.parse(res.data);\n    detailDrawerRef.value!.acceptParams({ data: networkData });\n};\n\nfunction isSystem(val: string) {\n    return val === 'bridge' || val === 'none' || val === 'host';\n}\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Container.NetworkInfo) => {\n            batchDelete(row);\n        },\n        disabled: (row: any) => {\n            return row.isSystem;\n        },\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/repo/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status\n            v-model:isActive=\"isActive\"\n            v-model:isExist=\"isExist\"\n            v-model:loading=\"loading\"\n            @search=\"search\"\n        />\n\n        <LayoutContent v-if=\"isExist\" :title=\"$t('container.repo', 2)\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('add')\">\n                    {{ $t('commons.button.add') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"repo-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @search=\"search\"\n                    :heightDiff=\"300\"\n                >\n                    <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\" min-width=\"60\" />\n                    <el-table-column\n                        :label=\"$t('container.downloadUrl')\"\n                        show-overflow-tooltip\n                        prop=\"downloadUrl\"\n                        min-width=\"100\"\n                        fix\n                    />\n                    <el-table-column :label=\"$t('commons.table.protocol')\" prop=\"protocol\" min-width=\"60\" fix />\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\" min-width=\"60\" fix>\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" :msg=\"row.message\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.createdAt')\"\n                        min-width=\"80\"\n                        fix\n                        :formatter=\"dateFormat\"\n                    />\n                    <fu-table-operations width=\"200px\" :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"submitDelete\" />\n        <OperatorDialog @search=\"search\" ref=\"dialogRef\" />\n        <ConfirmDialog ref=\"confirmDialog\" @confirm=\"submitDelete\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport OperatorDialog from '@/views/container/repo/operator/index.vue';\nimport { reactive, ref } from 'vue';\nimport { dateFormat } from '@/utils/util';\nimport { Container } from '@/api/interface/container';\nimport { checkRepoStatus, deleteImageRepo, searchImageRepo } from '@/api/modules/container';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport i18n from '@/lang';\n\nconst loading = ref();\nconst data = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    cacheSizeKey: 'image-repo-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('image-repo-page-size')) || 20,\n    total: 0,\n});\nconst searchName = ref();\n\nconst opRef = ref();\nconst confirmDialog = ref();\nconst currentRepo = ref();\n\nconst isActive = ref();\nconst isExist = ref();\n\nconst search = async () => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    let params = {\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchImageRepo(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst dialogRef = ref();\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Container.RepoInfo> = {\n        auth: true,\n        protocol: 'http',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onDelete = async (row: Container.RepoInfo) => {\n    currentRepo.value = row.id;\n    if (row.protocol === 'http') {\n        let params = {\n            header: i18n.global.t('container.repo'),\n            operationInfo: i18n.global.t('container.httpRepoHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialog.value!.acceptParams(params);\n        return;\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [row.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('container.repo'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\n\nconst submitDelete = async () => {\n    loading.value = true;\n    await deleteImageRepo(currentRepo.value)\n        .then(() => {\n            loading.value = false;\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onCheckConn = async (row: Container.RepoInfo) => {\n    loading.value = true;\n    await checkRepoStatus(row.id)\n        .then(() => {\n            loading.value = false;\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.sync'),\n        disabled: (row: Container.RepoInfo) => {\n            return row.id === 1;\n        },\n        click: (row: Container.RepoInfo) => {\n            onCheckConn(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        disabled: (row: Container.RepoInfo) => {\n            return row.id === 1;\n        },\n        click: (row: Container.RepoInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        disabled: (row: Container.RepoInfo) => {\n            return row.id === 1;\n        },\n        click: (row: Container.RepoInfo) => {\n            onDelete(row);\n        },\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/repo/operator/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"title\"\n        :resource=\"dialogData.rowData?.name\"\n        @close=\"handleClose\"\n        size=\"small\"\n    >\n        <el-form\n            ref=\"formRef\"\n            label-position=\"top\"\n            v-loading=\"loading\"\n            :model=\"dialogData.rowData\"\n            :rules=\"rules\"\n            label-width=\"120px\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input\n                    clearable\n                    :disabled=\"dialogData.title === 'edit'\"\n                    v-model.trim=\"dialogData.rowData!.name\"\n                ></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.auth')\" prop=\"auth\">\n                <el-radio-group v-model=\"dialogData.rowData!.auth\">\n                    <el-radio :value=\"true\">{{ $t('commons.true') }}</el-radio>\n                    <el-radio :value=\"false\">{{ $t('commons.false') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item v-if=\"dialogData.rowData!.auth\" :label=\"$t('commons.login.username')\" prop=\"username\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.username\"></el-input>\n            </el-form-item>\n            <el-form-item v-if=\"dialogData.rowData!.auth\" :label=\"$t('commons.login.password')\" prop=\"password\">\n                <el-input\n                    clearable\n                    type=\"password\"\n                    show-password\n                    v-model.trim=\"dialogData.rowData!.password\"\n                ></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.downloadUrl')\" prop=\"downloadUrl\">\n                <el-input\n                    clearable\n                    v-model.trim=\"dialogData.rowData!.downloadUrl\"\n                    :placeholder=\"'172.16.10.10:8081'\"\n                ></el-input>\n                <span v-if=\"dialogData.rowData!.downloadUrl\" class=\"input-help\">\n                    Pull example: docker pull {{ dialogData.rowData!.downloadUrl }}/nginx\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.protocol')\" prop=\"protocol\">\n                <el-radio-group v-model=\"dialogData.rowData!.protocol\">\n                    <el-radio label=\"http\">http</el-radio>\n                    <el-radio label=\"https\">https</el-radio>\n                </el-radio-group>\n                <span v-if=\"dialogData.rowData!.protocol === 'http'\" class=\"input-help\">\n                    {{ $t('container.httpRepo') }}\n                </span>\n            </el-form-item>\n        </el-form>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n\n    <ConfirmDialog ref=\"confirmDialog\" @confirm=\"submit\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { Container } from '@/api/interface/container';\nimport { createImageRepo, updateImageRepo } from '@/api/modules/container';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref(false);\n\ninterface DialogProps {\n    title: string;\n    rowData?: Container.RepoInfo;\n    getTableList?: () => Promise<any>;\n}\nconst confirmDialog = ref();\nconst oldUrl = ref();\nconst oldProto = ref();\n\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    oldUrl.value = params.rowData.downloadUrl;\n    oldProto.value = params.rowData.protocol;\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.name],\n    downloadUrl: [{ validator: validateDownloadUrl, trigger: 'blur' }, Rules.illegal],\n    protocol: [Rules.requiredSelect],\n    username: [Rules.illegal],\n    password: [Rules.illegal],\n    auth: [Rules.requiredSelect],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nfunction validateDownloadUrl(rule: any, value: any, callback: any) {\n    if (value === '') {\n        callback();\n    }\n    const pattern = /^(http:\\/\\/|https:\\/\\/)/i;\n    if (pattern.test(value)) {\n        return callback(new Error(i18n.global.t('container.urlWarning')));\n    }\n    callback();\n}\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let newProto = dialogData.value.rowData.protocol;\n        if (newProto === 'https' && dialogData.value.title === 'add') {\n            submit();\n            return;\n        }\n        if (newProto === oldProto.value) {\n            if (\n                (oldProto.value === 'http' && dialogData.value.rowData.downloadUrl === oldUrl.value) ||\n                oldProto.value === 'https'\n            ) {\n                submit();\n                return;\n            }\n        }\n        let params = {\n            header: i18n.global.t('container.repo'),\n            operationInfo: i18n.global.t('container.httpRepoHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialog.value!.acceptParams(params);\n    });\n};\n\nconst submit = async () => {\n    loading.value = true;\n    if (dialogData.value.title === 'add') {\n        await createImageRepo(dialogData.value.rowData!)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n        return;\n    }\n    await updateImageRepo(dialogData.value.rowData!)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            emit('search');\n            drawerVisible.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/setting/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status\n            v-model:isActive=\"isActive\"\n            v-model:isExist=\"isExist\"\n            :is-hide=\"true\"\n            v-model:loading=\"loading\"\n            @search=\"search\"\n        />\n\n        <div v-if=\"isExist\" class=\"app-status card-interval\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag class=\"float-left\" effect=\"dark\" type=\"success\">Docker</el-tag>\n                        <Status class=\"mt-0.5\" :status=\"isActive ? 'enable' : 'disable'\" />\n                        <el-tag>{{ $t('app.version') }}: {{ form.version }}</el-tag>\n                    </div>\n                    <div class=\"mt-0.5\">\n                        <el-button v-if=\"isActive\" type=\"primary\" @click=\"onOperator('stop')\" link>\n                            {{ $t('commons.operate.stop') }}\n                        </el-button>\n                        <el-button v-if=\"!isActive\" type=\"primary\" @click=\"onOperator('start')\" link>\n                            {{ $t('commons.operate.start') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" @click=\"onOperator('restart')\" link>\n                            {{ $t('commons.button.restart') }}\n                        </el-button>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n\n        <LayoutContent v-if=\"isExist\" class=\"card-interval\" :title=\"$t('container.setting', 2)\">\n            <template #main>\n                <el-radio-group v-model=\"confShowType\" @change=\"changeMode\">\n                    <el-radio-button value=\"base\">{{ $t('database.baseConf') }}</el-radio-button>\n                    <el-radio-button value=\"all\">{{ $t('database.allConf') }}</el-radio-button>\n                </el-radio-group>\n                <el-row class=\"p-mt-20\" v-if=\"confShowType === 'base'\">\n                    <el-col :span=\"1\"><br /></el-col>\n                    <el-col :xs=\"24\" :sm=\"24\" :md=\"15\" :lg=\"12\" :xl=\"10\">\n                        <el-form\n                            :model=\"form\"\n                            :label-position=\"mobile ? 'top' : 'left'\"\n                            :rules=\"rules\"\n                            ref=\"formRef\"\n                            label-width=\"auto\"\n                        >\n                            <el-form-item :label=\"$t('container.mirrors')\" prop=\"mirrors\">\n                                <div class=\"w-full\" v-if=\"form.mirrors\">\n                                    <el-input\n                                        type=\"textarea\"\n                                        :rows=\"5\"\n                                        disabled\n                                        v-model=\"form.mirrors\"\n                                        style=\"width: calc(100% - 80px)\"\n                                    />\n                                    <el-button @click=\"onChangeMirrors\" icon=\"Setting\" class=\"custom-input-textarea\">\n                                        {{ $t('commons.button.set') }}\n                                    </el-button>\n                                </div>\n                                <el-input disabled v-if=\"!form.mirrors\" v-model=\"unset\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeMirrors\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('container.mirrorsHelper') }}</span>\n                                <span class=\"input-help flex flx-align-center\" v-if=\"!globalStore.isFxplay\">\n                                    {{ $t('container.mirrorsHelper2') }}\n                                    <el-link class=\"p-ml-5 text-xs\" icon=\"Position\" @click=\"toDoc()\" type=\"primary\">\n                                        {{ $t('firewall.quickJump') }}\n                                    </el-link>\n                                </span>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('container.registries')\" prop=\"registries\">\n                                <div class=\"w-full\" v-if=\"form.registries\">\n                                    <el-input\n                                        type=\"textarea\"\n                                        :rows=\"5\"\n                                        disabled\n                                        v-model=\"form.registries\"\n                                        style=\"width: calc(100% - 80px)\"\n                                    />\n                                    <el-button @click=\"onChangeRegistries\" icon=\"Setting\">\n                                        {{ $t('commons.button.set') }}\n                                    </el-button>\n                                </div>\n                                <el-input disabled v-if=\"!form.registries\" v-model=\"unset\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeRegistries\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n\n                            <el-form-item label=\"IPv6\" prop=\"ipv6\">\n                                <el-switch v-model=\"form.ipv6\" @change=\"handleIPv6\"></el-switch>\n                                <span class=\"input-help\"></span>\n                                <div v-if=\"ipv6OptionShow\">\n                                    <el-tag>{{ $t('container.subnet') }}: {{ form.fixedCidrV6 }}</el-tag>\n                                    <div>\n                                        <el-button @click=\"handleIPv6\" type=\"primary\" link>\n                                            {{ $t('commons.button.view') }}\n                                        </el-button>\n                                    </div>\n                                </div>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('container.cutLog')\" prop=\"hasLogOption\">\n                                <el-switch v-model=\"form.logOptionShow\" @change=\"handleLogOption\"></el-switch>\n                                <span class=\"input-help\"></span>\n                                <div v-if=\"logOptionShow\">\n                                    <el-tag>{{ $t('container.maxSize') }}: {{ form.logMaxSize }}</el-tag>\n                                    <el-tag class=\"p-ml-5\">{{ $t('container.maxFile') }}: {{ form.logMaxFile }}</el-tag>\n                                    <div>\n                                        <el-button @click=\"handleLogOption\" type=\"primary\" link>\n                                            {{ $t('commons.button.view') }}\n                                        </el-button>\n                                    </div>\n                                </div>\n                            </el-form-item>\n\n                            <el-form-item label=\"iptables\" prop=\"iptables\">\n                                <el-switch v-model=\"form.iptables\" @change=\"handleIptables\"></el-switch>\n                                <span class=\"input-help\">{{ $t('container.iptablesHelper1') }}</span>\n                            </el-form-item>\n                            <el-form-item label=\"Live restore\" prop=\"liveRestore\">\n                                <el-switch\n                                    :disabled=\"form.isSwarm\"\n                                    v-model=\"form.liveRestore\"\n                                    @change=\"handleLive\"\n                                ></el-switch>\n                                <span class=\"input-help\">{{ $t('container.liveHelper') }}</span>\n                                <span v-if=\"form.isSwarm\" class=\"input-help\">\n                                    {{ $t('container.liveWithSwarmHelper') }}\n                                </span>\n                            </el-form-item>\n                            <el-form-item label=\"Cgroup Driver\" prop=\"cgroupDriver\">\n                                <el-radio-group v-model=\"form.cgroupDriver\" @change=\"handleCgroup\">\n                                    <el-radio value=\"cgroupfs\">cgroupfs</el-radio>\n                                    <el-radio value=\"systemd\">systemd</el-radio>\n                                </el-radio-group>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('container.sockPath')\" prop=\"dockerSockPath\">\n                                <el-input disabled v-model=\"form.dockerSockPath\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeSockPath\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('container.sockPathHelper') }}</span>\n                            </el-form-item>\n                        </el-form>\n                    </el-col>\n                </el-row>\n\n                <div v-if=\"confShowType === 'all'\">\n                    <CodemirrorPro\n                        class=\"mt-5\"\n                        :heightDiff=\"loadHeight()\"\n                        :minHeight=\"350\"\n                        v-model=\"dockerConf\"\n                        mode=\"json\"\n                        placeholder=\"# The Docker configuration file does not exist or is empty\"\n                    ></CodemirrorPro>\n                    <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveFile\" class=\"mt-2.5\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <DialogPro v-model=\"open\" :title=\"$t('container.iptablesDisable')\" size=\"small\">\n            <div class=\"mt-2.5\">\n                <span class=\"text-rose-500\">{{ $t('container.iptablesHelper2') }}</span>\n                <div class=\"mt-2.5\">\n                    <span class=\"text-xs\">{{ $t('container.restartHelper') }}</span>\n                </div>\n                <div class=\"mt-2.5\">\n                    <span class=\"text-xs\">{{ $t('commons.msg.operateConfirm') }}</span>\n                    <span class=\"text-xs text-rose-500 font-medium\">'{{ $t('database.restartNow') }}'</span>\n                </div>\n                <el-input class=\"mt-2.5\" v-model=\"submitInput\"></el-input>\n            </div>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button\n                        @click=\"\n                            open = false;\n                            search();\n                        \"\n                    >\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button\n                        :disabled=\"submitInput !== $t('database.restartNow')\"\n                        type=\"primary\"\n                        @click=\"onSubmitCloseIPtable\"\n                    >\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <Mirror ref=\"mirrorRef\" @search=\"search\" />\n        <Registry ref=\"registriesRef\" @search=\"search\" />\n        <LogOption ref=\"logOptionRef\" @search=\"search\" />\n        <Ipv6Option ref=\"ipv6OptionRef\" @search=\"search\" />\n        <SockPath ref=\"sockPathRef\" @search=\"search\" />\n        <ConfirmDialog ref=\"confirmDialogRefIpv6\" @confirm=\"onSaveIPv6\" @cancel=\"search\" />\n        <ConfirmDialog ref=\"confirmDialogRefIptable\" @confirm=\"onSubmitOpenIPtable\" @cancel=\"search\" />\n        <ConfirmDialog ref=\"confirmDialogRefLog\" @confirm=\"onSubmitSaveLog\" @cancel=\"search\" />\n        <ConfirmDialog ref=\"confirmDialogRefLive\" @confirm=\"onSubmitSaveLive\" @cancel=\"search\" />\n        <ConfirmDialog ref=\"confirmDialogRefCgroup\" @confirm=\"onSubmitSaveCgroup\" @cancel=\"search\" />\n\n        <ConfirmDialog ref=\"confirmDialogRefFile\" @confirm=\"onSubmitSaveFile\" @cancel=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { onMounted, reactive, ref, computed } from 'vue';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport Mirror from '@/views/container/setting/mirror/index.vue';\nimport Registry from '@/views/container/setting/registry/index.vue';\nimport LogOption from '@/views/container/setting/log/index.vue';\nimport Ipv6Option from '@/views/container/setting/ipv6/index.vue';\nimport SockPath from '@/views/container/setting/sock-path/index.vue';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport i18n from '@/lang';\nimport {\n    dockerOperate,\n    loadDaemonJson,\n    loadDaemonJsonFile,\n    updateDaemonJson,\n    updateDaemonJsonByfile,\n} from '@/api/modules/container';\nimport { getAgentSettingInfo } from '@/api/modules/setting';\nimport { MsgSuccess } from '@/utils/message';\nimport { checkNumberRange } from '@/global/form-rules';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst unset = ref(i18n.global.t('setting.unSetting'));\nconst submitInput = ref();\n\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst loading = ref(false);\nconst showDaemonJsonAlert = ref(false);\nconst confShowType = ref('base');\n\nconst logOptionRef = ref();\nconst ipv6OptionRef = ref();\nconst confirmDialogRefLog = ref();\nconst mirrorRef = ref();\nconst registriesRef = ref();\nconst confirmDialogRefLive = ref();\nconst confirmDialogRefCgroup = ref();\nconst confirmDialogRefIptable = ref();\nconst confirmDialogRefIpv6 = ref();\nconst logOptionShow = ref();\nconst ipv6OptionShow = ref();\nconst sockPathRef = ref();\n\nconst form = reactive({\n    isSwarm: false,\n    isActive: false,\n    version: '',\n    mirrors: '',\n    registries: '',\n    liveRestore: false,\n    iptables: true,\n    cgroupDriver: '',\n\n    ipv6: false,\n    fixedCidrV6: '',\n    ip6Tables: false,\n    experimental: false,\n\n    logOptionShow: false,\n    logMaxSize: '',\n    logMaxFile: 3,\n\n    dockerSockPath: '',\n});\nconst rules = reactive({\n    logMaxSize: [checkNumberRange(1, 1024000)],\n    logMaxFile: [checkNumberRange(1, 100)],\n});\nconst formRef = ref<FormInstance>();\nconst dockerConf = ref();\nconst confirmDialogRefFile = ref();\n\nconst open = ref();\n\nconst onSaveFile = async () => {\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('container.restartHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRefFile.value!.acceptParams(params);\n};\n\nconst loadHeight = () => {\n    return globalStore.openMenuTabs ? 450 : 430;\n};\n\nconst onChangeMirrors = () => {\n    mirrorRef.value.acceptParams({ mirrors: form.mirrors });\n};\nconst onChangeRegistries = () => {\n    registriesRef.value.acceptParams({ registries: form.registries });\n};\n\nconst onChangeSockPath = () => {\n    sockPathRef.value.acceptParams({ dockerSockPath: form.dockerSockPath });\n};\n\nconst handleIPv6 = async () => {\n    if (form.ipv6) {\n        ipv6OptionRef.value.acceptParams({\n            fixedCidrV6: form.fixedCidrV6,\n            ip6Tables: form.ip6Tables,\n            experimental: form.experimental,\n        });\n        return;\n    }\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('container.restartHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRefIpv6.value!.acceptParams(params);\n};\nconst onSaveIPv6 = () => {\n    save('Ipv6', 'disable');\n};\n\nconst handleLogOption = async () => {\n    if (form.logOptionShow) {\n        logOptionRef.value.acceptParams({ logMaxSize: form.logMaxSize, logMaxFile: form.logMaxFile });\n        return;\n    }\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('container.restartHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRefLog.value!.acceptParams(params);\n};\nconst onSubmitSaveLog = async () => {\n    save('LogOption', 'disable');\n};\n\nconst handleIptables = () => {\n    if (form.iptables) {\n        let params = {\n            header: i18n.global.t('database.confChange'),\n            operationInfo: i18n.global.t('container.restartHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialogRefIptable.value!.acceptParams(params);\n        return;\n    } else {\n        open.value = true;\n    }\n};\nconst onSubmitCloseIPtable = () => {\n    save('IPtables', 'disable');\n    open.value = false;\n};\nconst onSubmitOpenIPtable = () => {\n    save('IPtables', 'enable');\n};\n\nconst handleLive = async () => {\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('container.restartHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRefLive.value!.acceptParams(params);\n};\nconst onSubmitSaveLive = () => {\n    save('LiveRestore', form.liveRestore ? 'enable' : 'disable');\n};\nconst handleCgroup = async () => {\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('container.restartHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRefCgroup.value!.acceptParams(params);\n};\nconst onSubmitSaveCgroup = () => {\n    save('Driver', form.cgroupDriver);\n};\n\nconst save = async (key: string, value: string) => {\n    loading.value = true;\n    await updateDaemonJson(key, value)\n        .then(() => {\n            loading.value = false;\n            search();\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            search();\n            loading.value = false;\n        });\n};\n\nconst toDoc = () => {\n    window.open(globalStore.docsUrl + '/user_manual/containers/setting/', '_blank', 'noopener,noreferrer');\n};\n\nconst onOperator = async (operation: string) => {\n    ElMessageBox.confirm(\n        i18n.global.t('container.operatorStatusHelper', [i18n.global.t('commons.button.' + operation)]),\n        i18n.global.t('commons.table.operate'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(async () => {\n        loading.value = true;\n        await dockerOperate(operation)\n            .then(() => {\n                loading.value = false;\n                window.location.reload();\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onSubmitSaveFile = async () => {\n    let param = { file: dockerConf.value };\n    loading.value = true;\n    await updateDaemonJsonByfile(param)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n    return;\n};\n\nconst loadDockerConf = async () => {\n    const res = await loadDaemonJsonFile();\n    if (res.data === 'daemon.json is not find in path') {\n        showDaemonJsonAlert.value = true;\n    } else {\n        dockerConf.value = res.data;\n    }\n};\n\nconst changeMode = async () => {\n    if (confShowType.value === 'all') {\n        loadDockerConf();\n    } else {\n        showDaemonJsonAlert.value = false;\n        search();\n    }\n};\n\nconst search = async () => {\n    const res = await loadDaemonJson();\n    form.isSwarm = res.data.isSwarm;\n    form.version = res.data.version;\n    form.cgroupDriver = res.data.cgroupDriver || 'cgroupfs';\n    form.liveRestore = res.data.liveRestore;\n    form.iptables = res.data.iptables;\n    form.mirrors = res.data.registryMirrors ? res.data.registryMirrors.join('\\n') : '';\n    form.registries = res.data.insecureRegistries ? res.data.insecureRegistries.join('\\n') : '';\n    if (res.data.logMaxFile || res.data.logMaxSize) {\n        form.logOptionShow = true;\n        logOptionShow.value = true;\n        form.logMaxFile = Number(res.data.logMaxFile);\n        form.logMaxSize = res.data.logMaxSize;\n    } else {\n        form.logOptionShow = false;\n        logOptionShow.value = false;\n    }\n    form.ipv6 = res.data.ipv6;\n    ipv6OptionShow.value = form.ipv6;\n    form.fixedCidrV6 = res.data.fixedCidrV6;\n    form.ip6Tables = res.data.ip6Tables;\n    form.experimental = res.data.experimental;\n\n    const settingRes = await getAgentSettingInfo();\n    form.dockerSockPath = settingRes.data.dockerSockPath || 'unix:///var/run/docker.sock';\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/setting/ipv6/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"drawerVisible\" header=\"IPv6\" @close=\"handleClose\" size=\"small\">\n            <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\" v-if=\"!globalStore.isFxplay\">\n                <template #default>\n                    <span class=\"input-help\">\n                        {{ $t('container.ipv6Helper') }}\n                        <el-link\n                            style=\"font-size: 12px; margin-left: 5px\"\n                            icon=\"Position\"\n                            @click=\"toDoc()\"\n                            type=\"primary\"\n                        >\n                            {{ $t('firewall.quickJump') }}\n                        </el-link>\n                    </span>\n                </template>\n            </el-alert>\n\n            <el-form :model=\"form\" ref=\"formRef\" :rules=\"rules\" v-loading=\"loading\" label-position=\"top\">\n                <el-form-item prop=\"fixedCidrV6\" :label=\"$t('container.subnet')\">\n                    <el-input v-model=\"form.fixedCidrV6\" />\n                    <span class=\"input-help\">{{ $t('container.ipv6CidrHelper') }}</span>\n                </el-form-item>\n                <el-form-item>\n                    <el-checkbox v-model=\"showMore\" :label=\"$t('app.advanced')\" />\n                </el-form-item>\n                <div v-if=\"showMore\">\n                    <el-form-item prop=\"ip6Tables\" label=\"ip6tables\">\n                        <el-switch v-model=\"form.ip6Tables\"></el-switch>\n                        <span class=\"input-help\">{{ $t('container.ipv6TablesHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item prop=\"experimental\" label=\"experimental\">\n                        <el-switch v-model=\"form.experimental\"></el-switch>\n                        <span class=\"input-help\">{{ $t('container.experimentalHelper') }}</span>\n                    </el-form-item>\n                </div>\n            </el-form>\n            <template #footer>\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </template>\n        </DrawerPro>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmitSave\"></ConfirmDialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { updateIpv6Option } from '@/api/modules/container';\nimport { checkIpV6 } from '@/utils/util';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref();\nconst drawerVisible = ref();\nconst confirmDialogRef = ref();\nconst formRef = ref();\nconst showMore = ref(true);\n\ninterface DialogProps {\n    fixedCidrV6: string;\n    ip6Tables: boolean;\n    experimental: boolean;\n}\n\nconst form = reactive({\n    fixedCidrV6: '',\n    ip6Tables: false,\n    experimental: false,\n});\nconst rules = reactive({\n    fixedCidrV6: [{ validator: checkFixedCidrV6, trigger: 'blur', required: true }],\n});\n\nfunction checkFixedCidrV6(rule: any, value: any, callback: any) {\n    if (!form.fixedCidrV6 || form.fixedCidrV6.indexOf('/') === -1) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    if (checkIpV6(form.fixedCidrV6.split('/')[0])) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    const reg = /^(?:[1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/;\n    if (!reg.test(form.fixedCidrV6.split('/')[1])) {\n        return callback(new Error(i18n.global.t('commons.rule.formatErr')));\n    }\n    callback();\n}\n\nconst toDoc = () => {\n    window.open(globalStore.docsUrl + '/user_manual/containers/setting/', '_blank', 'noopener,noreferrer');\n};\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.fixedCidrV6 = params.fixedCidrV6;\n    form.ip6Tables = params.ip6Tables;\n    form.experimental = params.experimental;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let params = {\n            header: i18n.global.t('database.confChange'),\n            operationInfo: i18n.global.t('container.restartHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialogRef.value!.acceptParams(params);\n    });\n};\n\nconst onSubmitSave = async () => {\n    loading.value = true;\n    await updateIpv6Option(form.fixedCidrV6, form.ip6Tables, form.experimental)\n        .then(() => {\n            loading.value = false;\n            drawerVisible.value = false;\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/setting/log/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"drawerVisible\" :header=\"$t('container.cutLog')\" @close=\"handleClose\" size=\"small\">\n            <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\">\n                <template #default>\n                    <ul style=\"margin-left: -20px\">\n                        <li>{{ $t('container.cutLogHelper1') }}</li>\n                        <li>{{ $t('container.cutLogHelper2') }}</li>\n                        <li>{{ $t('container.cutLogHelper3') }}</li>\n                    </ul>\n                </template>\n            </el-alert>\n            <el-form :model=\"form\" ref=\"formRef\" :rules=\"rules\" v-loading=\"loading\" label-position=\"top\">\n                <el-form-item prop=\"logMaxSize\" :label=\"$t('container.maxSize')\">\n                    <el-input v-model.number=\"form.logMaxSize\">\n                        <template #append>\n                            <el-select v-model=\"form.sizeUnit\" style=\"width: 70px\">\n                                <el-option label=\"Byte\" value=\"b\"></el-option>\n                                <el-option label=\"KB\" value=\"k\"></el-option>\n                                <el-option label=\"MB\" value=\"m\"></el-option>\n                                <el-option label=\"GB\" value=\"g\"></el-option>\n                            </el-select>\n                        </template>\n                    </el-input>\n                </el-form-item>\n                <el-form-item prop=\"logMaxFile\" :label=\"$t('container.maxFile')\">\n                    <el-input v-model.number=\"form.logMaxFile\" />\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </template>\n        </DrawerPro>\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmitSave\"></ConfirmDialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { updateLogOption } from '@/api/modules/container';\n\nconst loading = ref();\nconst drawerVisible = ref();\nconst confirmDialogRef = ref();\nconst formRef = ref();\n\ninterface DialogProps {\n    logMaxSize: string;\n    logMaxFile: number;\n}\n\nconst form = reactive({\n    logMaxSize: 10,\n    logMaxFile: 3,\n    sizeUnit: 'm',\n});\nconst rules = reactive({\n    logMaxSize: [checkNumberRange(1, 1024000), Rules.number],\n    logMaxFile: [checkNumberRange(1, 100), Rules.number],\n});\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.logMaxFile = params.logMaxFile || 3;\n    if (params.logMaxSize) {\n        form.logMaxSize = loadSize(params.logMaxSize);\n    } else {\n        form.logMaxSize = 10;\n        form.sizeUnit = 'm';\n    }\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let params = {\n            header: i18n.global.t('database.confChange'),\n            operationInfo: i18n.global.t('container.restartHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialogRef.value!.acceptParams(params);\n    });\n};\n\nconst onSubmitSave = async () => {\n    loading.value = true;\n    await updateLogOption(form.logMaxSize + form.sizeUnit, form.logMaxFile + '')\n        .then(() => {\n            loading.value = false;\n            drawerVisible.value = false;\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadSize = (value: string) => {\n    if (value.indexOf('k') !== -1 || value.indexOf('KB') !== -1) {\n        form.sizeUnit = 'k';\n        return Number(value.replaceAll('k', '').replaceAll('KB', ''));\n    }\n    if (value.indexOf('m') !== -1 || value.indexOf('MB') !== -1) {\n        form.sizeUnit = 'm';\n        return Number(value.replaceAll('m', '').replaceAll('MB', ''));\n    }\n    if (value.indexOf('g') !== -1 || value.indexOf('GB') !== -1) {\n        form.sizeUnit = 'g';\n        return Number(value.replaceAll('g', '').replaceAll('GB', ''));\n    }\n    if (value.indexOf('b') !== -1 || value.indexOf('B') !== -1) {\n        form.sizeUnit = 'b';\n        return Number(value.replaceAll('b', '').replaceAll('B', ''));\n    }\n};\n\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/setting/mirror/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"drawerVisible\" :header=\"$t('container.mirrors')\" @close=\"handleClose\" size=\"small\">\n            <el-form\n                ref=\"formRef\"\n                label-position=\"top\"\n                :model=\"form\"\n                @submit.prevent\n                :rules=\"rules\"\n                v-loading=\"loading\"\n            >\n                <el-form-item :label=\"$t('container.mirrors')\" prop=\"mirrors\">\n                    <el-input\n                        type=\"textarea\"\n                        :placeholder=\"$t('container.mirrorHelper')\"\n                        :rows=\"5\"\n                        v-model=\"form.mirrors\"\n                    />\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </template>\n        </DrawerPro>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmit\" />\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { updateDaemonJson } from '@/api/modules/container';\nimport { FormInstance } from 'element-plus';\nimport { emptyLineFilter } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst confirmDialogRef = ref();\n\ninterface DialogProps {\n    mirrors: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    mirrors: '',\n});\nconst formRef = ref<FormInstance>();\nconst rules = reactive({\n    mirrors: [{ validator: checkMirrors, trigger: 'blur' }],\n});\n\nfunction checkMirrors(rule: any, value: any, callback: any) {\n    if (form.mirrors !== '') {\n        const reg = /^https?:\\/\\/[a-zA-Z0-9.-]+(:[0-9]{1,5})?(\\/[a-zA-Z0-9./-]*)?$/;\n        let mirrors = form.mirrors.split('\\n');\n        for (const item of mirrors) {\n            if (item === '') {\n                continue;\n            }\n            if (!reg.test(item)) {\n                return callback(new Error(i18n.global.t('commons.rule.mirror')));\n            }\n        }\n    }\n    callback();\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    form.mirrors = params.mirrors || params.mirrors.replaceAll(',', '\\n');\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let params = {\n            header: i18n.global.t('database.confChange'),\n            operationInfo: i18n.global.t('container.restartHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialogRef.value!.acceptParams(params);\n    });\n};\n\nconst onSubmit = async () => {\n    loading.value = true;\n    await updateDaemonJson('Mirrors', emptyLineFilter(form.mirrors, '\\n').replaceAll('\\n', ','))\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            handleClose();\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/setting/registry/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"drawerVisible\" :header=\"$t('container.registries')\" @close=\"handleClose\" size=\"small\">\n            <el-form\n                ref=\"formRef\"\n                label-position=\"top\"\n                :model=\"form\"\n                :rules=\"rules\"\n                @submit.prevent\n                v-loading=\"loading\"\n            >\n                <el-form-item :label=\"$t('container.registries')\" prop=\"registries\">\n                    <el-input\n                        type=\"textarea\"\n                        :placeholder=\"$t('container.registrieHelper')\"\n                        :rows=\"5\"\n                        v-model=\"form.registries\"\n                    />\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </template>\n        </DrawerPro>\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmit\" />\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { updateDaemonJson } from '@/api/modules/container';\nimport { FormInstance } from 'element-plus';\nimport { emptyLineFilter } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst confirmDialogRef = ref();\n\ninterface DialogProps {\n    registries: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    registries: '',\n});\nconst formRef = ref<FormInstance>();\nconst rules = reactive({\n    registries: [{ validator: checkRegistries, trigger: 'blur' }],\n});\n\nfunction checkRegistries(rule: any, value: any, callback: any) {\n    if (form.registries !== '') {\n        const reg = /^[a-zA-Z0-9]{1}[a-z:A-Z0-9_/.-]{0,150}$/;\n        let regis = form.registries.split('\\n');\n        for (const item of regis) {\n            if (item === '') {\n                continue;\n            }\n            if (!reg.test(item)) {\n                return callback(new Error(i18n.global.t('commons.rule.imageName')));\n            }\n        }\n    }\n    callback();\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    form.registries = params.registries || params.registries.replaceAll(',', '\\n');\n    drawerVisible.value = true;\n};\n\nconst onSave = async () => {\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('container.restartHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRef.value!.acceptParams(params);\n};\n\nconst onSubmit = async () => {\n    loading.value = true;\n    await updateDaemonJson('Registries', emptyLineFilter(form.registries, '\\n').replaceAll('\\n', ','))\n        .then(() => {\n            loading.value = false;\n            handleClose();\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/setting/sock-path/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"drawerVisible\" :header=\"$t('container.sockPath')\" @close=\"handleClose\" size=\"small\">\n            <el-form\n                ref=\"formRef\"\n                label-position=\"top\"\n                :model=\"form\"\n                :rules=\"rules\"\n                @submit.prevent\n                v-loading=\"loading\"\n            >\n                <el-form-item :label=\"$t('container.sockPath')\" prop=\"dockerSockPath\">\n                    <el-input v-model=\"form.dockerSockPath\">\n                        <template #prepend>unix://</template>\n                        <template #append>\n                            <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({})\" />\n                        </template>\n                    </el-input>\n                    <span class=\"input-help\">{{ $t('container.sockPathHelper1') }}</span>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </template>\n        </DrawerPro>\n        <FileList ref=\"fileRef\" @choose=\"loadBuildDir\" />\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport FileList from '@/components/file-list/index.vue';\nimport { updateAgentSetting } from '@/api/modules/setting';\nimport { ElMessageBox, FormInstance } from 'element-plus';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    dockerSockPath: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst fileRef = ref();\n\nconst form = reactive({\n    dockerSockPath: '',\n    currentPath: '',\n});\nconst formRef = ref<FormInstance>();\nconst rules = reactive({\n    dockerSockPath: [{ required: true, validator: checkSockPath, trigger: 'blur' }],\n});\n\nfunction checkSockPath(rule: any, value: any, callback: any) {\n    if (!value.endsWith('.sock')) {\n        return callback(new Error(i18n.global.t('container.sockPathErr')));\n    }\n    callback();\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    form.dockerSockPath = params.dockerSockPath.replaceAll('unix://', '');\n    form.currentPath = params.dockerSockPath.replaceAll('unix://', '');\n    drawerVisible.value = true;\n};\n\nconst loadBuildDir = async (path: string) => {\n    form.dockerSockPath = path;\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            `\n            <div style=\"line-height: 1.6;\">\n                <ul style=\"padding-left: 18px; margin: 0;\">\n                    <li>${i18n.global.t('container.restartHelper')}</li>\n                    <li>${i18n.global.t('container.sockPathMsg')}</li>\n                </ul>\n            </div>\n            `,\n            i18n.global.t('container.sockPath'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                dangerouslyUseHTMLString: true,\n            },\n        ).then(async () => {\n            loading.value = true;\n            let params = {\n                key: 'DockerSockPath',\n                value: form.dockerSockPath.startsWith('unix://')\n                    ? form.dockerSockPath\n                    : 'unix://' + form.dockerSockPath,\n            };\n            await updateAgentSetting(params)\n                .then(() => {\n                    loading.value = false;\n                    handleClose();\n                    emit('search');\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/template/detail/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"detailVisible\" :header=\"$t('commons.button.view')\" @close=\"handleClose\" size=\"large\">\n        <CodemirrorPro\n            :placeholder=\"$t('commons.msg.noneData')\"\n            v-model=\"detailInfo\"\n            mode=\"yaml\"\n            :heightDiff=\"160\"\n            :readonly=\"true\"\n        ></CodemirrorPro>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"detailVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\n\nconst detailVisible = ref(false);\nconst detailInfo = ref();\n\ninterface DialogProps {\n    content: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    detailInfo.value = params.content;\n    detailVisible.value = true;\n};\n\nconst handleClose = () => {\n    detailVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/template/import/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"visible\" :title=\"$t('commons.button.import')\" size=\"large\">\n        <div>\n            <el-alert :closable=\"false\" show-icon type=\"info\">\n                <template #default>\n                    <div>{{ $t('commons.msg.importHelper') }}</div>\n                </template>\n            </el-alert>\n            <el-upload\n                action=\"#\"\n                :auto-upload=\"false\"\n                ref=\"uploadRef\"\n                class=\"float-left mt-2\"\n                :show-file-list=\"false\"\n                :limit=\"1\"\n                accept=\".json\"\n                :on-change=\"fileOnChange\"\n                :on-exceed=\"handleExceed\"\n                v-model:file-list=\"uploaderFiles\"\n            >\n                <el-button class=\"float-left\" type=\"primary\">{{ $t('commons.button.upload') }}</el-button>\n            </el-upload>\n\n            <el-card class=\"mt-2 w-full\" v-loading=\"loading\">\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    @search=\"search\"\n                    v-model:selects=\"selects\"\n                    :data=\"pageData\"\n                    :height=\"440\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column :label=\"$t('commons.table.status')\" :min-width=\"80\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        :min-width=\"70\"\n                        show-overflow-tooltip\n                        prop=\"name\"\n                    />\n                    <el-table-column\n                        :label=\"$t('commons.table.description')\"\n                        show-overflow-tooltip\n                        :min-width=\"120\"\n                        prop=\"description\"\n                    />\n                    <el-table-column :label=\"$t('container.content')\" :min-width=\"70\" prop=\"content\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"onOpenDetail(row)\">\n                                {{ $t('commons.button.view') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                </ComplexTable>\n            </el-card>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"selects.length === 0\" @click=\"onImport\">\n                    {{ $t('commons.button.import') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n    <DetailDialog ref=\"detailRef\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { genFileId, UploadFile, UploadFiles, UploadProps, UploadRawFile } from 'element-plus';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport DetailDialog from '@/views/container/template/detail/index.vue';\nimport i18n from '@/lang';\nimport { Container } from '@/api/interface/container';\nimport { batchComposeTemplate, searchComposeTemplate } from '@/api/modules/container';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst visible = ref(false);\nconst loading = ref(false);\nconst selects = ref<any>([]);\nconst displayData = ref<any>([]);\nconst currentData = ref<Container.TemplateInfo[]>([]);\n\nconst uploadRef = ref();\nconst uploaderFiles = ref();\nconst detailRef = ref();\nconst pageData = ref([]);\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst acceptParams = async (): Promise<void> => {\n    visible.value = true;\n    displayData.value = [];\n    selects.value = [];\n    loadTemplates();\n};\n\nconst loadTemplates = async () => {\n    const res = await searchComposeTemplate({\n        info: '',\n        page: 1,\n        pageSize: 10000,\n    });\n    currentData.value = res.data.items || [];\n};\n\nconst search = () => {\n    const startIndex = (paginationConfig.currentPage - 1) * paginationConfig.pageSize;\n    const endIndex = startIndex + paginationConfig.pageSize;\n    pageData.value = displayData.value.slice(startIndex, endIndex);\n};\n\nconst onOpenDetail = async (row: Container.TemplateInfo) => {\n    detailRef.value.acceptParams({ content: row.content });\n};\n\nconst fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    loading.value = true;\n    displayData.value = [];\n    uploaderFiles.value = uploadFiles;\n\n    const reader = new FileReader();\n    reader.onload = (e) => {\n        try {\n            const content = e.target.result as string;\n            const parsed = JSON.parse(content);\n\n            if (!Array.isArray(parsed)) {\n                MsgError(i18n.global.t('commons.msg.errJsonImportFormat'));\n                loading.value = false;\n                return;\n            }\n\n            for (const item of parsed) {\n                if (!checkDataFormat(item)) {\n                    MsgError(i18n.global.t('commons.msg.errJsonImportFormat'));\n                    loading.value = false;\n                    return;\n                }\n            }\n\n            compareData(parsed);\n            loading.value = false;\n        } catch (error) {\n            loading.value = false;\n        }\n    };\n    reader.readAsText(_uploadFile.raw);\n};\n\nconst handleExceed: UploadProps['onExceed'] = (files) => {\n    uploadRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadRef.value!.handleStart(file);\n};\n\nconst checkDataFormat = (item: any): boolean => {\n    return item.name && item.content;\n};\n\nconst compareData = (importLists: any[]) => {\n    const news: any[] = [];\n    const conflicts: any[] = [];\n    const duplicates: any[] = [];\n\n    for (const importItem of importLists) {\n        const key = `${importItem.name}:${importItem.content}`;\n\n        const existing = currentData.value.find((item) => {\n            const existingKey = `${item.name}:${item.content}`;\n            return existingKey === key;\n        });\n        const conflict = currentData.value.find((item) => {\n            if (existing) {\n                return false;\n            }\n            return item.name === importItem.name && item.content !== importItem.content;\n        });\n\n        if (existing) {\n            duplicates.push({ ...importItem, status: 'duplicate' });\n            continue;\n        }\n        if (conflict) {\n            conflicts.push({ ...importItem, status: 'conflict' });\n            continue;\n        }\n        news.push({ ...importItem, status: 'new' });\n    }\n\n    displayData.value = [...news, ...conflicts, ...duplicates];\n    paginationConfig.total = displayData.value.length;\n    search();\n};\n\nconst onImport = async () => {\n    loading.value = true;\n    await batchComposeTemplate(selects.value)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            emit('search');\n            visible.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/template/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status\n            v-model:isActive=\"isActive\"\n            v-model:isExist=\"isExist\"\n            v-model:loading=\"loading\"\n            @search=\"search\"\n        />\n\n        <LayoutContent v-if=\"isExist\" :title=\"$t('container.composeTemplate', 2)\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button type=\"primary\" plain :disabled=\"selects.length === 0\" @click=\"onBatchDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n\n                <el-button-group>\n                    <el-button @click=\"onImport\">\n                        {{ $t('commons.button.import') }}\n                    </el-button>\n                    <el-button :disabled=\"selects.length === 0\" @click=\"onExport\">\n                        {{ $t('commons.button.export') }}\n                    </el-button>\n                </el-button-group>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"template-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @search=\"search\"\n                    :heightDiff=\"300\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        min-width=\"100\"\n                        prop=\"name\"\n                        sortable\n                        fix\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"onOpenDetail(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.description')\" prop=\"description\" min-width=\"200\" fix />\n                    <el-table-column :label=\"$t('commons.table.createdAt')\" min-width=\"80\" fix>\n                        <template #default=\"{ row }\">\n                            {{ dateFormat(0, 0, row.createdAt) }}\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <ImportDialog @search=\"search\" ref=\"dialogImportRef\" />\n        <DetailDialog ref=\"detailRef\" />\n        <OperatorDialog @search=\"search\" ref=\"dialogRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { dateFormat, downloadWithContent, getCurrentDateFormatted } from '@/utils/util';\nimport { Container } from '@/api/interface/container';\nimport DetailDialog from '@/views/container/template/detail/index.vue';\nimport ImportDialog from '@/views/container/template/import/index.vue';\nimport OperatorDialog from '@/views/container/template/operator/index.vue';\nimport { deleteComposeTemplate, searchComposeTemplate } from '@/api/modules/container';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport i18n from '@/lang';\n\nconst loading = ref();\nconst data = ref();\nconst selects = ref<any>([]);\n\nconst dialogImportRef = ref();\nconst detailRef = ref();\nconst dialogRef = ref();\nconst opRef = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'compose-template-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('compose-template-page-size')) || 20,\n    total: 0,\n});\nconst searchName = ref();\n\nconst search = async () => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    let params = {\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchComposeTemplate(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onOpenDetail = async (row: Container.TemplateInfo) => {\n    detailRef.value.acceptParams({ content: row.content });\n};\n\nconst onImport = () => {\n    dialogImportRef.value.acceptParams();\n};\n\nconst onExport = () => {\n    ElMessageBox.confirm(\n        i18n.global.t('container.exportHelper', [selects.value.length]),\n        i18n.global.t('commons.button.export'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    ).then(async () => {\n        const exportData = selects.value.map((item: Container.TemplateInfo) => ({\n            name: item.name,\n            description: item.description,\n            content: item.content,\n        }));\n        const content = JSON.stringify(exportData, null, 2);\n        const fileName = `1panel-docker-compose-template-${getCurrentDateFormatted()}.json`;\n        downloadWithContent(content, fileName);\n    });\n};\n\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Container.TemplateInfo> = {\n        name: '',\n        content: '',\n        description: '',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onBatchDelete = async (row: Container.RepoInfo | null) => {\n    let ids = [];\n    let names = [];\n    if (row) {\n        names.push(row.name);\n        ids.push(row.id);\n    } else {\n        selects.value.forEach((item: Container.RepoInfo) => {\n            names.push(item.name);\n            ids.push(item.id);\n        });\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('container.composeTemplate'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteComposeTemplate,\n        params: { ids: ids },\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        disabled: (row: Container.RepoInfo) => {\n            return row.downloadUrl === 'docker.io';\n        },\n        click: (row: Container.RepoInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        disabled: (row: Container.RepoInfo) => {\n            return row.downloadUrl === 'docker.io';\n        },\n        click: (row: Container.RepoInfo) => {\n            onBatchDelete(row);\n        },\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/template/operator/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"$t('container.composeTemplate')\"\n        :resource=\"dialogData.title === 'create' ? '' : dialogData.rowData?.name\"\n        @close=\"handleClose\"\n        size=\"large\"\n        :autoClose=\"false\"\n        :fullScreen=\"true\"\n    >\n        <el-form\n            v-loading=\"loading\"\n            label-position=\"top\"\n            ref=\"formRef\"\n            :model=\"dialogData.rowData\"\n            :rules=\"rules\"\n            label-width=\"80px\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input :disabled=\"dialogData.title === 'edit'\" v-model.trim=\"dialogData.rowData!.name\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\">\n                <el-input v-model=\"dialogData.rowData!.description\"></el-input>\n            </el-form-item>\n            <el-form-item>\n                <CodemirrorPro\n                    placeholder=\"#Define or paste the content of your docker-compose file here\"\n                    v-model=\"dialogData.rowData!.content\"\n                    mode=\"yaml\"\n                    :heightDiff=\"400\"\n                ></CodemirrorPro>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { Container } from '@/api/interface/container';\nimport { createComposeTemplate, updateComposeTemplate } from '@/api/modules/container';\nimport { MsgSuccess } from '@/utils/message';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\n\nconst loading = ref(false);\n\ninterface DialogProps {\n    title: string;\n    rowData?: Container.TemplateInfo;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.name],\n    content: [Rules.requiredInput],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        if (dialogData.value.title === 'create') {\n            await createComposeTemplate(dialogData.value.rowData!)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n            return;\n        }\n        await updateComposeTemplate(dialogData.value.rowData!)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/volume/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('commons.button.create')\" @close=\"handleClose\" size=\"small\">\n        <el-form\n            ref=\"formRef\"\n            v-loading=\"loading\"\n            label-position=\"top\"\n            :model=\"form\"\n            :rules=\"rules\"\n            label-width=\"80px\"\n            @submit.prevent\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input clearable v-model.trim=\"form.name\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('container.driver')\" prop=\"driver\">\n                <el-tag type=\"success\">local</el-tag>\n            </el-form-item>\n            <el-form-item :label=\"$t('container.nfsEnable')\" prop=\"nfsStatus\">\n                <el-switch v-model=\"form.nfsStatus\" active-value=\"enable\" inactive-value=\"disable\" />\n            </el-form-item>\n            <div v-if=\"form.nfsStatus === 'enable'\">\n                <el-form-item :label=\"$t('container.nfsAddress')\" prop=\"nfsAddress\">\n                    <el-input clearable v-model.trim=\"form.nfsAddress\" :placeholder=\"$t('commons.rule.hostHelper')\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('app.version')\" prop=\"nfsVersion\">\n                    <el-radio-group v-model=\"form.nfsVersion\">\n                        <el-radio value=\"v3\">NFS</el-radio>\n                        <el-radio value=\"v4\">NFS4</el-radio>\n                    </el-radio-group>\n                </el-form-item>\n                <el-form-item :label=\"$t('container.mountpoint')\" prop=\"nfsMount\">\n                    <el-input\n                        clearable\n                        v-model.trim=\"form.nfsMount\"\n                        :placeholder=\"$t('container.mountpointNFSHelper')\"\n                    />\n                </el-form-item>\n                <el-form-item :label=\"$t('container.options')\" prop=\"nfsOption\">\n                    <el-input clearable v-model.trim=\"form.nfsOption\" />\n                </el-form-item>\n            </div>\n            <el-form-item :label=\"$t('container.option')\" prop=\"optionStr\">\n                <el-input type=\"textarea\" :placeholder=\"$t('container.tagHelper')\" :rows=\"3\" v-model=\"form.optionStr\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('container.tag')\" prop=\"labelStr\">\n                <el-input type=\"textarea\" :placeholder=\"$t('container.tagHelper')\" :rows=\"3\" v-model=\"form.labelStr\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { createVolume } from '@/api/modules/container';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref(false);\n\nconst drawerVisible = ref(false);\nconst form = reactive({\n    name: '',\n    driver: 'local',\n    labelStr: '',\n    labels: [] as Array<string>,\n    nfsStatus: 'disable',\n    nfsAddress: '',\n    nfsVersion: 'v4',\n    nfsMount: '',\n    nfsOption: 'rw,noatime,rsize=8192,wsize=8192,tcp,timeo=14',\n    optionStr: '',\n    options: [] as Array<string>,\n});\n\nconst acceptParams = (): void => {\n    form.name = '';\n    form.labels = [];\n    form.labelStr = '';\n    form.options = [];\n    form.optionStr = '';\n    form.nfsStatus = 'disable';\n    form.nfsAddress = '';\n    form.nfsVersion = 'v4';\n    form.nfsMount = '';\n    form.nfsOption = 'rw,noatime,rsize=8192,wsize=8192,tcp,timeo=14';\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.volumeName],\n    driver: [Rules.requiredSelect],\n    nfsAddress: [Rules.host],\n    nfsVersion: [Rules.requiredSelect],\n    nfsMount: [Rules.requiredInput],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (form.labelStr !== '') {\n            form.labels = form.labelStr.split('\\n');\n        }\n        if (form.optionStr !== '') {\n            form.options = form.optionStr.split('\\n');\n        }\n        if (form.nfsStatus === 'enable') {\n            let typeOption = form.nfsVersion === 'v4' ? 'nfs4' : 'nfs';\n            form.options.push('type=' + typeOption);\n            form.options.push('o=addr=' + form.nfsAddress + ',' + form.nfsOption);\n            let mount = form.nfsMount.startsWith(':') ? form.nfsMount : ':' + form.nfsMount;\n            form.options.push('device=' + mount);\n        }\n        loading.value = true;\n        await createVolume(form)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/container/volume/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <docker-status\n            v-model:isActive=\"isActive\"\n            v-model:isExist=\"isExist\"\n            v-model:loading=\"loading\"\n            @search=\"search\"\n        />\n\n        <LayoutContent v-if=\"isExist\" :title=\"$t('container.volume', 2)\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onCreate()\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onClean()\">\n                    {{ $t('container.volumePrune') }}\n                </el-button>\n                <el-button :disabled=\"selects.length === 0\" @click=\"batchDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"volume-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @search=\"search\"\n                    :heightDiff=\"300\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        min-width=\"100\"\n                        :width=\"mobile ? 220 : 'auto'\"\n                        prop=\"name\"\n                        fix\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"onInspect(row.name)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column label=\"Options\" min-width=\"160\">\n                        <template #default=\"{ row }\">\n                            <div v-for=\"(item, index) in row.options\" :key=\"index\">\n                                <div v-if=\"row.expand || (!row.expand && index < 3)\">\n                                    <el-button\n                                        v-if=\"item.key === 'device'\"\n                                        @click=\"jumpTo(item.value)\"\n                                        class=\"mt-0.5\"\n                                        icon=\"Position\"\n                                        plain\n                                        size=\"small\"\n                                    >\n                                        {{ item.key + ': ' + item.value }}\n                                    </el-button>\n                                    <el-button v-else class=\"mt-0.5\" plain size=\"small\">\n                                        {{ item.key + ': ' + item.value }}\n                                    </el-button>\n                                </div>\n                            </div>\n                            <div v-if=\"!row.expand && row.options?.length > 3\">\n                                <el-button type=\"primary\" link @click=\"row.expand = true\">\n                                    {{ $t('commons.button.expand') }}...\n                                </el-button>\n                            </div>\n                            <div v-if=\"row.expand && row.options?.length > 3\">\n                                <el-button type=\"primary\" link @click=\"row.expand = false\">\n                                    {{ $t('commons.button.collapse') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.mountpoint')\"\n                        show-overflow-tooltip\n                        min-width=\"120\"\n                        prop=\"mountpoint\"\n                    >\n                        <template #default=\"{ row }\">\n                            <el-tooltip :content=\"row.mountpoint\">\n                                <el-button\n                                    type=\"primary\"\n                                    icon=\"FolderOpened\"\n                                    link\n                                    @click=\"routerToFileWithPath(row.mountpoint)\"\n                                />\n                            </el-tooltip>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.driver')\"\n                        show-overflow-tooltip\n                        min-width=\"80\"\n                        prop=\"driver\"\n                    />\n                    <el-table-column\n                        prop=\"createdAt\"\n                        min-width=\"90\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                    />\n                    <fu-table-operations :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n\n        <CodemirrorDrawer ref=\"myDetail\" />\n        <CreateDialog @search=\"search\" ref=\"dialogCreateRef\" />\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport CreateDialog from '@/views/container/volume/create/index.vue';\nimport CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { reactive, ref, computed } from 'vue';\nimport { dateFormat, newUUID } from '@/utils/util';\nimport { deleteVolume, searchVolume, inspect, containerPrune } from '@/api/modules/container';\nimport { Container } from '@/api/interface/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport i18n from '@/lang';\nimport { ElMessageBox } from 'element-plus';\nimport { GlobalStore } from '@/store';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { checkFile } from '@/api/modules/files';\nimport { MsgError } from '@/utils/message';\nconst globalStore = GlobalStore();\n\nconst taskLogRef = ref();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref();\nconst myDetail = ref();\n\nconst opRef = ref();\n\nconst data = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    cacheSizeKey: 'container-volume-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('container-volume-page-size')) || 20,\n    total: 0,\n});\nconst searchName = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst dialogCreateRef = ref<DialogExpose>();\n\ninterface DialogExpose {\n    acceptParams: () => void;\n}\nconst onCreate = async () => {\n    dialogCreateRef.value!.acceptParams();\n};\n\nconst search = async () => {\n    if (!isActive.value || !isExist.value) {\n        return;\n    }\n    const params = {\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchVolume(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onInspect = async (id: string) => {\n    const res = await inspect({ id: id, type: 'volume', detail: '' });\n    let detailInfo = JSON.stringify(JSON.parse(res.data), null, 2);\n    let param = {\n        header: i18n.global.t('commons.button.view'),\n        detailInfo: detailInfo,\n        mode: 'json',\n    };\n    myDetail.value!.acceptParams(param);\n};\n\nconst onClean = () => {\n    ElMessageBox.confirm(i18n.global.t('container.volumePruneHelper'), i18n.global.t('container.volumePrune'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        let params = {\n            taskID: newUUID(),\n            pruneType: 'volume',\n            withTagAll: false,\n        };\n        await containerPrune(params)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(params.taskID);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst jumpTo = async (path: any) => {\n    await checkFile(path, false).then((res) => {\n        if (res.data) {\n            routerToFileWithPath(path);\n        } else {\n            MsgError(i18n.global.t('file.noSuchFile'));\n        }\n    });\n};\n\nconst batchDelete = async (row: Container.VolumeInfo | null) => {\n    let names = [];\n    if (row) {\n        names.push(row.name);\n    } else {\n        selects.value.forEach((item: Container.VolumeInfo) => {\n            names.push(item.name);\n        });\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('container.volume'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteVolume,\n        params: { names: names },\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Container.VolumeInfo) => {\n            batchDelete(row);\n        },\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/cronjob/cronjob/backup/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"backupVisible\"\n        :header=\"$t('commons.button.backup')\"\n        :resource=\"cronjob\"\n        @close=\"handleClose\"\n        size=\"large\"\n    >\n        <template #content>\n            <ComplexTable\n                v-loading=\"loading\"\n                :pagination-config=\"paginationConfig\"\n                v-model:selects=\"selects\"\n                @search=\"search\"\n                :data=\"data\"\n            >\n                <el-table-column :label=\"$t('commons.table.name')\" prop=\"fileName\" show-overflow-tooltip />\n                <el-table-column :label=\"$t('file.size')\" prop=\"size\" show-overflow-tooltip>\n                    <template #default=\"{ row }\">\n                        <div v-if=\"row.hasLoad\">\n                            <span v-if=\"row.size\">\n                                {{ computeSize(row.size) }}\n                            </span>\n                            <span v-else>-</span>\n                        </div>\n                        <div v-if=\"!row.hasLoad\">\n                            <el-button link loading></el-button>\n                        </div>\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('app.source')\" prop=\"accountType\" show-overflow-tooltip>\n                    <template #default=\"{ row }\">\n                        <span v-if=\"row.accountType === 'LOCAL'\">\n                            {{ $t('setting.LOCAL') }}\n                        </span>\n                        <span v-if=\"row.accountType && row.accountType !== 'LOCAL'\">\n                            {{ $t('setting.' + row.accountType) + ' - ' + row.accountName }}\n                        </span>\n                        <span v-if=\"!row.accountType\">-</span>\n                    </template>\n                </el-table-column>\n                <el-table-column\n                    prop=\"createdAt\"\n                    :label=\"$t('commons.table.date')\"\n                    :formatter=\"dateFormat\"\n                    show-overflow-tooltip\n                />\n\n                <fu-table-operations width=\"130px\" :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n            </ComplexTable>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { computeSize, dateFormat, downloadFile } from '@/utils/util';\nimport i18n from '@/lang';\nimport { downloadBackupRecord, loadRecordSize, searchBackupRecordsByCronjob } from '@/api/modules/backup';\nimport { Backup } from '@/api/interface/backup';\nimport { MsgError } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst selects = ref<any>([]);\nconst loading = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst backupVisible = ref(false);\nconst cronjob = ref();\nconst cronjobID = ref();\n\ninterface DialogProps {\n    cronjob: string;\n    cronjobID: number;\n}\nconst acceptParams = (params: DialogProps): void => {\n    cronjob.value = params.cronjob;\n    cronjobID.value = params.cronjobID;\n    backupVisible.value = true;\n    search();\n};\nconst handleClose = () => {\n    backupVisible.value = false;\n};\n\nconst search = async () => {\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        cronjobID: cronjobID.value,\n    };\n    loading.value = true;\n    await searchBackupRecordsByCronjob(params)\n        .then((res) => {\n            loading.value = false;\n            loadSize(params);\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadSize = async (params: any) => {\n    params.type = 'cronjob';\n    await loadRecordSize(params)\n        .then((res) => {\n            let stats = res.data || [];\n            if (stats.length === 0) {\n                return;\n            }\n            for (const backup of data.value) {\n                for (const item of stats) {\n                    if (backup.id === item.id) {\n                        backup.hasLoad = true;\n                        backup.size = item.size;\n                        break;\n                    }\n                }\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onDownload = async (row: Backup.RecordInfo) => {\n    if (row.accountType === 'ALIYUN' && row.size < 100 * 1024 * 1024) {\n        MsgError(i18n.global.t('setting.ALIYUNHelper'));\n        return;\n    }\n    let params = {\n        downloadAccountID: row.downloadAccountID,\n        fileDir: row.fileDir,\n        fileName: row.fileName,\n    };\n    loading.value = true;\n    await downloadBackupRecord(params)\n        .then(async (res) => {\n            loading.value = false;\n            downloadFile(res.data, globalStore.currentNode);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.download'),\n        disabled: (row: any) => {\n            return row.size === 0;\n        },\n        click: (row: Backup.RecordInfo) => {\n            onDownload(row);\n        },\n    },\n];\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/cronjob/cronjob/config/clean-log.vue",
    "content": "<template>\n    <el-card>\n        <el-form-item :label=\"$t('cronjob.cleanLogscope')\" prop=\"scopes\">\n            <el-select :model-value=\"modelValue\" multiple @change=\"handleChange\" clearable>\n                <el-option v-for=\"item in scopes\" :key=\"item.value\" :value=\"item.value\" :label=\"item.label\" />\n            </el-select>\n        </el-form-item>\n    </el-card>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\ninterface Props {\n    modelValue?: string[];\n}\n\nwithDefaults(defineProps<Props>(), {\n    modelValue: () => [],\n});\n\nconst emit = defineEmits<{\n    (e: 'update:modelValue', value: string[]): void;\n}>();\n\nconst handleChange = (value: string[]) => {\n    emit('update:modelValue', value);\n};\n\nconst scopes = [{ value: 'website', label: i18n.global.t('logs.websiteLog') }];\n</script>\n"
  },
  {
    "path": "frontend/src/views/cronjob/cronjob/helper.ts",
    "content": "import { Cronjob } from '@/api/interface/cronjob';\nimport i18n from '@/lang';\nimport { loadZero } from '@/utils/util';\n\nexport const specOptions = [\n    { label: i18n.global.t('cronjob.perMonth'), value: 'perMonth' },\n    { label: i18n.global.t('cronjob.perWeek'), value: 'perWeek' },\n    { label: i18n.global.t('cronjob.perDay'), value: 'perDay' },\n    { label: i18n.global.t('cronjob.perHour'), value: 'perHour' },\n    { label: i18n.global.t('cronjob.perNDay'), value: 'perNDay' },\n    { label: i18n.global.t('cronjob.perNHour'), value: 'perNHour' },\n    { label: i18n.global.t('cronjob.perNMinute'), value: 'perNMinute' },\n    { label: i18n.global.t('cronjob.perNSecond'), value: 'perNSecond' },\n];\nexport const weekOptions = [\n    { label: i18n.global.t('cronjob.monday'), value: 1 },\n    { label: i18n.global.t('cronjob.tuesday'), value: 2 },\n    { label: i18n.global.t('cronjob.wednesday'), value: 3 },\n    { label: i18n.global.t('cronjob.thursday'), value: 4 },\n    { label: i18n.global.t('cronjob.friday'), value: 5 },\n    { label: i18n.global.t('cronjob.saturday'), value: 6 },\n    { label: i18n.global.t('cronjob.sunday'), value: 0 },\n];\nexport const mysqlArgs = [\n    { arg: '--single-transaction', description: i18n.global.t('cronjob.singleTransaction') },\n    { arg: '--quick', description: i18n.global.t('cronjob.quick') },\n    { arg: '--skip-lock-tables', description: i18n.global.t('cronjob.skipLockTables') },\n];\nfunction loadWeek(i: number) {\n    for (const week of weekOptions) {\n        if (week.value === i) {\n            return week.label;\n        }\n    }\n    return '';\n}\n\nexport function loadDefaultSpec(type: string) {\n    let item = {} as Cronjob.SpecObj;\n    item.week = 0;\n    item.day = 0;\n    item.hour = 0;\n    item.minute = 0;\n    item.second = 0;\n    switch (type) {\n        case 'shell':\n        case 'clean':\n        case 'website':\n        case 'log':\n        case 'snapshot':\n        case 'curl':\n            item.specType = 'perWeek';\n            item.week = 1;\n            item.hour = 1;\n            item.minute = 30;\n            break;\n        case 'app':\n        case 'database':\n            item.specType = 'perDay';\n            item.hour = 2;\n            item.minute = 30;\n            break;\n        case 'directory':\n        case 'cutWebsiteLog':\n        case 'cleanLog':\n        case 'syncIpGroup':\n        case 'ntp':\n            item.specType = 'perDay';\n            item.hour = 1;\n            item.minute = 30;\n            break;\n    }\n    return item;\n}\n\nexport function loadDefaultSpecCustom(type: string) {\n    switch (type) {\n        case 'shell':\n        case 'clean':\n        case 'website':\n        case 'log':\n        case 'snapshot':\n        case 'curl':\n            return '30 1 * * 1';\n        case 'app':\n        case 'database':\n            return '30 2 * * *';\n        case 'directory':\n        case 'cutWebsiteLog':\n        case 'ntp':\n        case 'syncIpGroup':\n            return '30 1 * * *';\n        default:\n            return '30 1 * * 1';\n    }\n}\n\nexport function transObjToSpec(specType: string, week, day, hour, minute, second): string {\n    switch (specType) {\n        case 'perMonth':\n            return `${minute} ${hour} ${day} * *`;\n        case 'perWeek':\n            return `${minute} ${hour} * * ${week}`;\n        case 'perNDay':\n            return `${minute} ${hour} */${day} * *`;\n        case 'perDay':\n            return `${minute} ${hour} * * *`;\n        case 'perNHour':\n            return `${minute} */${hour} * * *`;\n        case 'perHour':\n            return `${minute} * * * *`;\n        case 'perNMinute':\n            return `@every ${minute}m`;\n        case 'perNSecond':\n            return `@every ${second}s`;\n        default:\n            return '';\n    }\n}\n\nexport function transSpecToObj(spec: string) {\n    let specs = spec.split(' ');\n    let specItem = {\n        specType: 'perNMinute',\n        week: 0,\n        day: 0,\n        hour: 0,\n        minute: 0,\n        second: 0,\n    };\n    if (specs.length === 2) {\n        if (specs[1].indexOf('m') !== -1) {\n            specItem.specType = 'perNMinute';\n            specItem.minute = Number(specs[1].replaceAll('m', ''));\n            return specItem;\n        } else {\n            specItem.specType = 'perNSecond';\n            specItem.second = Number(specs[1].replaceAll('s', ''));\n            return specItem;\n        }\n    }\n    if (specs.length !== 5 || specs[0] === '*') {\n        return null;\n    }\n    specItem.minute = Number(specs[0]);\n    if (specs[1] === '*') {\n        specItem.specType = 'perHour';\n        return specItem;\n    }\n    if (specs[1].indexOf('*/') !== -1) {\n        specItem.specType = 'perNHour';\n        specItem.hour = Number(specs[1].replaceAll('*/', ''));\n        return specItem;\n    }\n    specItem.hour = Number(specs[1]);\n    if (specs[2].indexOf('*/') !== -1) {\n        specItem.specType = 'perNDay';\n        specItem.day = Number(specs[2].replaceAll('*/', ''));\n        return specItem;\n    }\n    if (specs[2] !== '*') {\n        specItem.specType = 'perMonth';\n        specItem.day = Number(specs[2]);\n        return specItem;\n    }\n    if (specs[4] !== '*') {\n        specItem.specType = 'perWeek';\n        specItem.week = Number(specs[4]);\n        return specItem;\n    }\n    specItem.specType = 'perDay';\n    return specItem;\n}\n\nexport function transSpecToStr(spec: string): string {\n    const specObj = transSpecToObj(spec);\n    switch (specObj.specType) {\n        case 'perMonth':\n            return i18n.global.t('cronjob.perMonthHelper', [specObj.day, specObj.hour, loadZero(specObj.minute)]);\n        case 'perWeek':\n            return i18n.global.t('cronjob.perWeekHelper', [\n                loadWeek(specObj.week),\n                specObj.hour,\n                loadZero(specObj.minute),\n            ]);\n        case 'perDay':\n            return i18n.global.t('cronjob.perDayHelper', [specObj.hour, loadZero(specObj.minute)]);\n        case 'perHour':\n            return i18n.global.t('cronjob.perHourHelper', [loadZero(specObj.minute)]);\n        case 'perNDay':\n            return i18n.global.t('cronjob.perNDayHelper', [specObj.day, specObj.hour, loadZero(specObj.minute)]);\n        case 'perNHour':\n            return i18n.global.t('cronjob.perNHourHelper', [specObj.hour, loadZero(specObj.minute)]);\n        case 'perNMinute':\n            return i18n.global.t('cronjob.perNMinuteHelper', [loadZero(specObj.minute)]);\n        case 'perNSecond':\n            return i18n.global.t('cronjob.perNSecondHelper', [loadZero(specObj.second)]);\n    }\n}\n\nexport function hasBackup(type: string) {\n    return (\n        type === 'app' ||\n        type === 'website' ||\n        type === 'database' ||\n        type === 'directory' ||\n        type === 'snapshot' ||\n        type === 'log' ||\n        type === 'cutWebsiteLog'\n    );\n}\n\nexport const cronjobTypes = [\n    { value: 'shell', label: i18n.global.t('cronjob.shell') },\n    { value: 'app', label: i18n.global.t('cronjob.app') },\n    { value: 'website', label: i18n.global.t('cronjob.website') },\n    { value: 'database', label: i18n.global.t('cronjob.database') },\n    { value: 'directory', label: i18n.global.t('cronjob.directory') },\n    { value: 'log', label: i18n.global.t('cronjob.log') },\n    { value: 'curl', label: i18n.global.t('cronjob.curl') },\n    { value: 'cutWebsiteLog', label: i18n.global.t('cronjob.cutWebsiteLog') },\n    { value: 'clean', label: i18n.global.t('setting.diskClean') },\n    { value: 'snapshot', label: i18n.global.t('cronjob.snapshot') },\n    { value: 'ntp', label: i18n.global.t('cronjob.ntp') },\n    { value: 'syncIpGroup', label: i18n.global.t('cronjob.syncIpGroup') },\n    { value: 'cleanLog', label: i18n.global.t('cronjob.cleanLog') },\n];\n"
  },
  {
    "path": "frontend/src/views/cronjob/cronjob/import/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"visible\" :title=\"$t('commons.button.import')\" size=\"large\">\n        <div>\n            <el-alert :closable=\"false\" show-icon type=\"info\" :title=\"$t('cronjob.importHelper')\" />\n            <el-upload\n                action=\"#\"\n                :auto-upload=\"false\"\n                ref=\"uploadRef\"\n                class=\"float-left mt-2\"\n                :show-file-list=\"false\"\n                :limit=\"1\"\n                accept=\".json\"\n                :on-change=\"fileOnChange\"\n                :on-exceed=\"handleExceed\"\n                v-model:file-list=\"uploaderFiles\"\n            >\n                <el-button class=\"float-left\" type=\"primary\">{{ $t('commons.button.upload') }}</el-button>\n            </el-upload>\n\n            <el-button :disabled=\"selects.length === 0\" @click=\"onImport\" class=\"ml-2 mt-2\">\n                {{ $t('commons.button.import') }}\n            </el-button>\n\n            <el-card class=\"mt-2 w-full\" v-loading=\"loading\">\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    @search=\"search\"\n                    v-model:selects=\"selects\"\n                    :data=\"pageData\"\n                    :height=\"440\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column\n                        :label=\"$t('cronjob.taskName')\"\n                        :min-width=\"120\"\n                        prop=\"name\"\n                        show-overflow-tooltip\n                    />\n                    <el-table-column\n                        :label=\"$t('commons.table.type')\"\n                        :min-width=\"120\"\n                        prop=\"type\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-tag>{{ $t('cronjob.' + row.type) }}</el-tag>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('cronjob.cronSpec')\" show-overflow-tooltip :min-width=\"120\">\n                        <template #default=\"{ row }\">\n                            <div v-for=\"(item, index) of row.spec.split(',')\" :key=\"index\">\n                                <div v-if=\"row.expand || (!row.expand && index < 3)\">\n                                    <span>\n                                        {{ row.specCustom ? item : transSpecToStr(item) }}\n                                    </span>\n                                </div>\n                            </div>\n                            <div v-if=\"!row.expand && row.spec.split(',').length > 3\">\n                                <el-button type=\"primary\" link @click=\"row.expand = true\">\n                                    {{ $t('commons.button.expand') }}...\n                                </el-button>\n                            </div>\n                            <div v-if=\"row.expand && row.spec.split(',').length > 3\">\n                                <el-button type=\"primary\" link @click=\"row.expand = false\">\n                                    {{ $t('commons.button.collapse') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('cronjob.retainCopies')\" :min-width=\"120\" prop=\"retainCopies\" />\n                </ComplexTable>\n            </el-card>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { transSpecToStr } from './../helper';\nimport { genFileId, UploadFile, UploadFiles, UploadProps, UploadRawFile } from 'element-plus';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { importCronjob } from '@/api/modules/cronjob';\nimport { Cronjob } from '@/api/interface/cronjob';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst visible = ref(false);\nconst loading = ref();\nconst selects = ref<any>([]);\nconst data = ref();\n\nconst uploadRef = ref();\nconst uploaderFiles = ref();\nconst pageData = ref([]);\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst acceptParams = (): void => {\n    visible.value = true;\n    data.value = [];\n};\n\nconst search = () => {\n    const startIndex = (paginationConfig.currentPage - 1) * paginationConfig.pageSize;\n    const endIndex = startIndex + paginationConfig.pageSize;\n    pageData.value = data.value.slice(startIndex, endIndex);\n};\n\nconst fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    loading.value = true;\n    data.value = [];\n    uploaderFiles.value = uploadFiles;\n    const reader = new FileReader();\n    reader.onload = (e) => {\n        try {\n            const content = e.target.result as string;\n            const parsed = JSON.parse(content) as Cronjob.CronjobTrans;\n            if (!Array.isArray(parsed)) {\n                MsgError(i18n.global.t('commons.msg.errImportFormat'));\n                loading.value = false;\n                return;\n            }\n            for (const item of parsed) {\n                if (!checkDataFormat(item)) {\n                    MsgError(i18n.global.t('commons.msg.errImportFormat'));\n                    loading.value = false;\n                    return;\n                }\n            }\n            data.value = parsed;\n            paginationConfig.total = data.value.length;\n            search();\n            loading.value = false;\n        } catch (error) {\n            MsgError(i18n.global.t('commons.msg.errImport') + error.message);\n            loading.value = false;\n        }\n    };\n    reader.readAsText(_uploadFile.raw);\n};\n\nconst handleExceed: UploadProps['onExceed'] = (files) => {\n    uploadRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadRef.value!.handleStart(file);\n};\n\nconst checkDataFormat = (item: any) => {\n    if (!item.name) {\n        return false;\n    }\n    const cronjobTypes = [\n        'shell',\n        'app',\n        'website',\n        'database',\n        'directory',\n        'log',\n        'curl',\n        'cutWebsiteLog',\n        'clean',\n        'snapshot',\n        'ntp',\n    ];\n    if (!item.type || cronjobTypes.indexOf(item.type) === -1) {\n        return false;\n    }\n    return true;\n};\n\nconst onImport = async () => {\n    await importCronjob(selects.value).then(() => {\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        visible.value = false;\n        emit('search');\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/cronjob/cronjob/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" v-if=\"!isRecordShow\" :title=\"$t('menu.cronjob')\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('')\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button @click=\"onOpenGroupDialog()\">\n                    {{ $t('commons.table.group') }}\n                </el-button>\n                <el-button-group>\n                    <el-button plain :disabled=\"selects.length === 0\" @click=\"onBatchChangeStatus('enable')\">\n                        {{ $t('commons.button.enable') }}\n                    </el-button>\n                    <el-button plain :disabled=\"selects.length === 0\" @click=\"onBatchChangeStatus('disable')\">\n                        {{ $t('commons.button.disable') }}\n                    </el-button>\n                    <el-button plain :disabled=\"selects.length === 0\" @click=\"onDelete(null)\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </el-button-group>\n\n                <el-button-group>\n                    <el-button @click=\"onImport\">\n                        {{ $t('commons.button.import') }}\n                    </el-button>\n                    <el-button :disabled=\"selects.length === 0\" @click=\"onExport\">\n                        {{ $t('commons.button.export') }}\n                    </el-button>\n                </el-button-group>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"searchGroupID\" @change=\"search()\" clearable class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.group') }}</template>\n                    <div v-for=\"item in groupOptions\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"cronjob-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    @sort-change=\"search\"\n                    @search=\"search\"\n                    :data=\"data\"\n                    :heightDiff=\"300\"\n                >\n                    <el-table-column type=\"selection\" :selectable=\"selectable\" fix />\n                    <el-table-column\n                        :label=\"$t('cronjob.taskName')\"\n                        :min-width=\"120\"\n                        prop=\"name\"\n                        sortable\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"loadDetail(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.group')\" min-width=\"120\" prop=\"group\">\n                        <template #default=\"{ row }\">\n                            <fu-select-rw-switch v-model=\"row.groupID\" @change=\"updateGroup(row)\">\n                                <template #read>\n                                    {{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}\n                                </template>\n                                <div v-for=\"item in groupOptions\" :key=\"item.id\">\n                                    <el-option\n                                        v-if=\"item.name === 'Default'\"\n                                        :label=\"$t('commons.table.default')\"\n                                        :value=\"item.id\"\n                                    />\n                                    <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                                </div>\n                            </fu-select-rw-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" :min-width=\"90\" prop=\"status\" sortable>\n                        <template #default=\"{ row }\">\n                            <Status\n                                v-if=\"row.status === 'Enable'\"\n                                @click=\"onChangeStatus(row.id, 'disable')\"\n                                :status=\"row.status\"\n                                :operate=\"true\"\n                            />\n                            <Status\n                                v-if=\"row.status === 'Disable'\"\n                                @click=\"onChangeStatus(row.id, 'enable')\"\n                                :status=\"row.status\"\n                                :operate=\"true\"\n                            />\n                            <Status v-if=\"row.status === 'Pending'\" :status=\"row.status\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('cronjob.cronSpec')\" show-overflow-tooltip :min-width=\"120\">\n                        <template #default=\"{ row }\">\n                            <div v-for=\"(item, index) of row.spec.split('&&')\" :key=\"index\">\n                                <div v-if=\"row.expand || (!row.expand && index < 3)\">\n                                    <span>\n                                        {{ row.specCustom ? item : transSpecToStr(item) }}\n                                    </span>\n                                </div>\n                            </div>\n                            <div v-if=\"!row.expand && row.spec.split('&&').length > 3\">\n                                <el-button type=\"primary\" link @click=\"row.expand = true\">\n                                    {{ $t('commons.button.expand') }}...\n                                </el-button>\n                            </div>\n                            <div v-if=\"row.expand && row.spec.split('&&').length > 3\">\n                                <el-button type=\"primary\" link @click=\"row.expand = false\">\n                                    {{ $t('commons.button.collapse') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('cronjob.retainCopies')\" :min-width=\"120\" prop=\"retainCopies\">\n                        <template #default=\"{ row }\">\n                            <el-button v-if=\"hasBackup(row.type)\" @click=\"loadBackups(row)\" plain size=\"small\">\n                                {{ row.retainCopies }}{{ $t('cronjob.retainCopiesUnit') }}\n                            </el-button>\n                            <span v-else>{{ row.retainCopies }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('cronjob.lastRecordTime')\"\n                        :min-width=\"120\"\n                        show-overflow-tooltip\n                        prop=\"lastRecordTime\"\n                    >\n                        <template #default=\"{ row }\">\n                            <el-button v-if=\"row.lastRecordStatus === 'Success'\" icon=\"Select\" link type=\"success\" />\n                            <el-button v-if=\"row.lastRecordStatus === 'Failed'\" icon=\"CloseBold\" link type=\"danger\" />\n                            <el-button v-if=\"row.lastRecordStatus === 'Waiting'\" :loading=\"true\" link type=\"info\" />\n                            <el-button v-if=\"row.lastRecordStatus === 'Unexecuted'\" icon=\"Minus\" link type=\"info\" />\n                            {{ row.lastRecordTime }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :min-width=\"120\" :label=\"$t('setting.backupAccount')\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"!hasBackup(row.type)\">-</span>\n                            <div v-else>\n                                <div v-for=\"(item, index) of row.sourceAccounts\" :key=\"index\">\n                                    <div v-if=\"row.accountExpand || (!row.accountExpand && index < 3)\">\n                                        <div v-if=\"row.expand || (!row.expand && index < 3)\">\n                                            <span type=\"info\">\n                                                {{ item === 'localhost' ? $t('setting.LOCAL') : item }}\n                                                <el-icon\n                                                    v-if=\"item === row.downloadAccount\"\n                                                    size=\"12\"\n                                                    class=\"relative top-px left-1\"\n                                                >\n                                                    <Star />\n                                                </el-icon>\n                                            </span>\n                                        </div>\n                                    </div>\n                                </div>\n                                <div v-if=\"!row.accountExpand && row.sourceAccounts?.length > 3\">\n                                    <el-button type=\"primary\" link @click=\"row.accountExpand = true\">\n                                        {{ $t('commons.button.expand') }}...\n                                    </el-button>\n                                </div>\n                                <div v-if=\"row.accountExpand && row.sourceAccounts?.length > 3\">\n                                    <el-button type=\"primary\" link @click=\"row.accountExpand = false\">\n                                        {{ $t('commons.button.collapse') }}\n                                    </el-button>\n                                </div>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations\n                        width=\"200px\"\n                        :buttons=\"buttons\"\n                        :ellipsis=\"2\"\n                        :label=\"$t('commons.table.operate')\"\n                        min-width=\"mobile ? 'auto' : 200\"\n                        :fixed=\"mobile ? false : 'right'\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"onSubmitDelete()\">\n            <template #content>\n                <el-form class=\"mt-4 mb-1\" v-if=\"showClean\" ref=\"deleteForm\" label-position=\"left\">\n                    <el-form-item>\n                        <el-checkbox v-model=\"cleanData\" :label=\"$t('cronjob.cleanData')\" />\n                        <el-checkbox\n                            v-if=\"cleanData\"\n                            v-model=\"cleanRemoteData\"\n                            :label=\"$t('cronjob.cleanRemoteData')\"\n                        />\n                        <span class=\"input-help\">\n                            {{ $t('cronjob.cleanDataHelper') }}\n                        </span>\n                    </el-form-item>\n                </el-form>\n            </template>\n        </OpDialog>\n        <OpDialog ref=\"opExportRef\" @search=\"search\" @submit=\"onSubmitExport()\" />\n        <GroupDialog @search=\"loadGroups\" ref=\"dialogGroupRef\" />\n        <Records @search=\"search\" ref=\"dialogRecordRef\" />\n        <Import @search=\"search\" ref=\"dialogImportRef\" />\n        <Backups @search=\"search\" ref=\"dialogBackupRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport Records from '@/views/cronjob/cronjob/record/index.vue';\nimport Backups from '@/views/cronjob/cronjob/backup/index.vue';\nimport Import from '@/views/cronjob/cronjob/import/index.vue';\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport {\n    deleteCronjob,\n    editCronjobGroup,\n    exportCronjob,\n    searchCronjobPage,\n    handleOnce,\n    updateStatus,\n} from '@/api/modules/cronjob';\nimport i18n from '@/lang';\nimport { Cronjob } from '@/api/interface/cronjob';\nimport GroupDialog from '@/components/group/index.vue';\nimport { ElMessageBox } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { hasBackup, transSpecToStr } from './helper';\nimport { GlobalStore } from '@/store';\nimport { getCurrentDateFormatted } from '@/utils/util';\nimport { getGroupList } from '@/api/modules/group';\nimport { routerToNameWithQuery } from '@/utils/router';\n\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref();\nconst selects = ref<any>([]);\nconst isRecordShow = ref();\nconst operateIDs = ref();\n\nconst opRef = ref();\nconst showClean = ref();\nconst cleanData = ref();\nconst cleanRemoteData = ref();\nconst opExportRef = ref();\nconst dialogImportRef = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'cronjob-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('cronjob-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst defaultGroupID = ref<number>();\nconst searchGroupID = ref<number>();\nconst groupOptions = ref();\nconst dialogGroupRef = ref();\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let groupIDs;\n    if (searchGroupID.value) {\n        groupIDs = searchGroupID.value === defaultGroupID.value ? [searchGroupID.value, 0] : [searchGroupID.value];\n    }\n    let params = {\n        info: searchName.value,\n        groupIDs: groupIDs,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    loading.value = true;\n    await searchCronjobPage(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            loadGroups();\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst dialogRecordRef = ref();\nconst dialogBackupRef = ref();\n\nconst onOpenDialog = async (id: string) => {\n    routerToNameWithQuery('CronjobOperate', { id: id });\n};\n\nfunction selectable(row) {\n    return row.status !== 'Pending';\n}\n\nconst onDelete = async (row: Cronjob.CronjobInfo | null) => {\n    let names = [];\n    let ids = [];\n    showClean.value = false;\n    cleanData.value = false;\n    cleanRemoteData.value = true;\n    if (row) {\n        ids = [row.id];\n        names = [row.name];\n        if (hasBackup(row.type)) {\n            showClean.value = true;\n        }\n    } else {\n        for (const item of selects.value) {\n            names.push(item.name);\n            ids.push(item.id);\n            if (hasBackup(item.type)) {\n                showClean.value = true;\n            }\n        }\n    }\n    operateIDs.value = ids;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('menu.cronjob'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\n\nconst onSubmitDelete = async () => {\n    loading.value = true;\n    await deleteCronjob({ ids: operateIDs.value, cleanData: cleanData.value, cleanRemoteData: cleanRemoteData.value })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onImport = () => {\n    dialogImportRef.value.acceptParams();\n};\n\nconst onExport = async () => {\n    let names = [];\n    let ids = [];\n    for (const item of selects.value) {\n        names.push(item.name);\n        ids.push(item.id);\n    }\n    operateIDs.value = ids;\n    opExportRef.value.acceptParams({\n        title: i18n.global.t('commons.button.export'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('menu.cronjob'),\n            i18n.global.t('commons.button.export'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\n\nconst onSubmitExport = async () => {\n    loading.value = true;\n    await exportCronjob({ ids: operateIDs.value })\n        .then((res) => {\n            const downloadUrl = window.URL.createObjectURL(new Blob([res]));\n            const a = document.createElement('a');\n            a.style.display = 'none';\n            a.href = downloadUrl;\n            a.download = '1panel-cronjob-' + getCurrentDateFormatted() + '.json';\n            const event = new MouseEvent('click');\n            a.dispatchEvent(event);\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst loadGroups = async () => {\n    const res = await getGroupList('cronjob');\n    groupOptions.value = res.data || [];\n    for (const group of groupOptions.value) {\n        if (group.name === 'Default') {\n            defaultGroupID.value = group.id;\n            break;\n        }\n    }\n    for (const item of data.value) {\n        if (item.groupID === 0) {\n            item.groupBelong = 'Default';\n            item.groupID = defaultGroupID.value;\n            continue;\n        }\n        let hasGroup = false;\n        for (const group of groupOptions.value) {\n            if (item.groupID === group.id) {\n                hasGroup = true;\n                item.groupBelong = group.name;\n            }\n        }\n        if (!hasGroup) {\n            item.groupID = null;\n            item.groupBelong = '-';\n        }\n    }\n};\n\nconst updateGroup = async (row: any) => {\n    await editCronjobGroup(row.id, row.groupID);\n    search();\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst onOpenGroupDialog = () => {\n    dialogGroupRef.value!.acceptParams({ type: 'cronjob', hideDefaultButton: true });\n};\n\nconst onChangeStatus = async (id: number, status: string) => {\n    ElMessageBox.confirm(i18n.global.t('cronjob.' + status + 'Msg'), i18n.global.t('cronjob.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        let itemStatus = status === 'enable' ? 'Enable' : 'Disable';\n        await updateStatus({ id: id, status: itemStatus });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        search();\n    });\n};\n\nconst onBatchChangeStatus = async (status: string) => {\n    ElMessageBox.confirm(i18n.global.t('cronjob.' + status + 'Msg'), i18n.global.t('cronjob.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        let itemStatus = status === 'enable' ? 'Enable' : 'Disable';\n        for (const item of selects.value) {\n            await updateStatus({ id: item.id, status: itemStatus });\n        }\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        search();\n    });\n};\n\nconst loadBackups = async (row: any) => {\n    dialogBackupRef.value!.acceptParams({ cronjobID: row.id, cronjob: row.name });\n};\n\nconst onHandle = async (row: Cronjob.CronjobInfo) => {\n    loading.value = true;\n    await handleOnce(row.id)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadDetail = (row: any) => {\n    isRecordShow.value = true;\n    let params = {\n        rowData: { ...row },\n    };\n    dialogRecordRef.value!.acceptParams(params);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.handle'),\n        click: (row: Cronjob.CronjobInfo) => {\n            onHandle(row);\n        },\n        disabled: (row: any) => {\n            return row.status === 'Pending';\n        },\n    },\n    {\n        label: i18n.global.t('cronjob.record'),\n        click: (row: Cronjob.CronjobInfo) => {\n            loadDetail(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Cronjob.CronjobInfo) => {\n            onOpenDialog(row.id + '');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Cronjob.CronjobInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/cronjob/cronjob/operate/index.vue",
    "content": "<template>\n    <LayoutContent\n        back-name=\"CronjobItem\"\n        :title=\"isCreate ? $t('commons.button.create') : $t('commons.button.edit') + ' - ' + form.name\"\n    >\n        <template #main>\n            <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\">\n                <el-row type=\"flex\" justify=\"center\" :gutter=\"20\">\n                    <el-col :span=\"20\">\n                        <el-card>\n                            <el-form-item :label=\"$t('cronjob.taskType')\" prop=\"type\">\n                                <el-select\n                                    v-if=\"isCreate\"\n                                    class=\"mini-form-item\"\n                                    @change=\"changeType\"\n                                    v-model=\"form.type\"\n                                >\n                                    <el-option\n                                        v-for=\"item in cronjobTypes\"\n                                        :key=\"item.value\"\n                                        :value=\"item.value\"\n                                        :label=\"item.label\"\n                                    />\n                                </el-select>\n                                <div v-else class=\"w-full\">\n                                    <el-tag>{{ $t('cronjob.' + form.type) }}</el-tag>\n                                </div>\n                                <span class=\"input-help logText\" v-if=\"form.type === 'log'\">\n                                    {{ $t('cronjob.logHelper1') }}\n                                    <el-link\n                                        class=\"link\"\n                                        icon=\"Position\"\n                                        @click=\"goRouter('/logs/system')\"\n                                        type=\"primary\"\n                                    >\n                                        {{ $t('firewall.quickJump') }}\n                                    </el-link>\n                                </span>\n                                <span class=\"input-help logText\" v-if=\"form.type === 'log'\">\n                                    {{ $t('cronjob.logHelper2') }}\n                                    <el-link class=\"link\" icon=\"Position\" @click=\"goRouter('/logs/ssh')\" type=\"primary\">\n                                        {{ $t('firewall.quickJump') }}\n                                    </el-link>\n                                </span>\n                                <span class=\"input-help logText\" v-if=\"form.type === 'log'\">\n                                    {{ $t('cronjob.logHelper3') }}\n                                    <el-link\n                                        class=\"link\"\n                                        icon=\"Position\"\n                                        @click=\"goRouter('/logs/website')\"\n                                        type=\"primary\"\n                                    >\n                                        {{ $t('firewall.quickJump') }}\n                                    </el-link>\n                                </span>\n                                <span class=\"input-help logText\" v-if=\"form.type === 'ntp'\">\n                                    {{ $t('cronjob.ntp_helper') }}\n                                    <el-link\n                                        class=\"link\"\n                                        icon=\"Position\"\n                                        @click=\"goRouter('/toolbox/device')\"\n                                        type=\"primary\"\n                                    >\n                                        {{ $t('firewall.quickJump') }}\n                                    </el-link>\n                                </span>\n                            </el-form-item>\n                            <el-row :gutter=\"20\">\n                                <LayoutCol>\n                                    <el-form-item :label=\"$t('cronjob.taskName')\" prop=\"name\">\n                                        <el-input :disabled=\"!isCreate\" clearable v-model.trim=\"form.name\" />\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol>\n                                    <el-form-item :label=\"$t('commons.table.group')\" prop=\"groupID\">\n                                        <el-select filterable v-model=\"form.groupID\" clearable>\n                                            <div v-for=\"item in groupOptions\" :key=\"item.id\">\n                                                <el-option\n                                                    v-if=\"item.name === 'Default'\"\n                                                    :label=\"$t('commons.table.default')\"\n                                                    :value=\"item.id\"\n                                                />\n                                                <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                                            </div>\n                                        </el-select>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n                        </el-card>\n\n                        <CleanLogConfig\n                            v-if=\"form.type == 'cleanLog'\"\n                            class=\"mt-5\"\n                            v-model=\"form.scopes\"\n                        ></CleanLogConfig>\n\n                        <el-card class=\"mt-5\">\n                            <el-form-item :label=\"$t('cronjob.cronSpec')\" prop=\"specCustom\">\n                                <el-checkbox :label=\"$t('container.custom')\" v-model=\"form.specCustom\" />\n                            </el-form-item>\n                            <div v-if=\"!form.specCustom\">\n                                <el-form-item prop=\"spec\">\n                                    <div v-for=\"(specObj, index) of form.specObjs\" :key=\"index\" style=\"width: 100%\">\n                                        <el-select\n                                            class=\"specTypeClass\"\n                                            v-model=\"specObj.specType\"\n                                            @change=\"changeSpecType(index)\"\n                                        >\n                                            <el-option\n                                                v-for=\"item in specOptions\"\n                                                :key=\"item.label\"\n                                                :value=\"item.value\"\n                                                :label=\"item.label\"\n                                            />\n                                        </el-select>\n                                        <el-select\n                                            v-if=\"specObj.specType === 'perWeek'\"\n                                            class=\"specClass\"\n                                            v-model=\"specObj.week\"\n                                        >\n                                            <el-option\n                                                v-for=\"item in weekOptions\"\n                                                :key=\"item.label\"\n                                                :value=\"item.value\"\n                                                :label=\"item.label\"\n                                            />\n                                        </el-select>\n                                        <el-input v-if=\"hasDay(specObj)\" class=\"specClass\" v-model.number=\"specObj.day\">\n                                            <template #append>\n                                                <div class=\"append\">{{ $t('cronjob.day') }}</div>\n                                            </template>\n                                        </el-input>\n                                        <el-input\n                                            v-if=\"hasHour(specObj)\"\n                                            class=\"specClass\"\n                                            v-model.number=\"specObj.hour\"\n                                        >\n                                            <template #append>\n                                                <div class=\"append\">{{ $t('commons.units.hour') }}</div>\n                                            </template>\n                                        </el-input>\n                                        <el-input\n                                            v-if=\"specObj.specType !== 'perNSecond'\"\n                                            class=\"specClass\"\n                                            v-model.number=\"specObj.minute\"\n                                        >\n                                            <template #append>\n                                                <div class=\"append\">{{ $t('commons.units.minute') }}</div>\n                                            </template>\n                                        </el-input>\n                                        <el-input\n                                            v-if=\"specObj.specType === 'perNSecond'\"\n                                            class=\"specClass\"\n                                            v-model.number=\"specObj.second\"\n                                        >\n                                            <template #append>\n                                                <div class=\"append\">{{ $t('commons.units.second') }}</div>\n                                            </template>\n                                        </el-input>\n                                        <el-popover\n                                            placement=\"top-start\"\n                                            :title=\"$t('cronjob.nextTime')\"\n                                            width=\"200\"\n                                            trigger=\"click\"\n                                        >\n                                            <div v-for=\"(time, index_t) of nextTimes\" :key=\"index_t\">\n                                                <el-tag class=\"mt-2\">{{ time }}</el-tag>\n                                            </div>\n                                            <template #reference>\n                                                <el-button class=\"ml-5\" @click=\"loadNext(specObj)\" link type=\"primary\">\n                                                    {{ $t('commons.button.preview') }}\n                                                </el-button>\n                                            </template>\n                                        </el-popover>\n                                        <el-button\n                                            class=\"ml-2.5\"\n                                            link\n                                            type=\"primary\"\n                                            @click=\"handleSpecDelete(index)\"\n                                            v-if=\"form.specObjs.length > 1\"\n                                        >\n                                            {{ $t('commons.button.delete') }}\n                                        </el-button>\n                                        <el-divider v-if=\"form.specObjs.length > 1\" class=\"divider\" />\n                                    </div>\n                                </el-form-item>\n                                <el-button class=\"mb-3\" @click=\"handleSpecAdd()\">\n                                    {{ $t('commons.button.add') }}\n                                </el-button>\n                            </div>\n\n                            <div v-if=\"form.specCustom\">\n                                <el-form-item prop=\"spec\">\n                                    <div v-for=\"(spec, index) of form.specs\" :key=\"index\" class=\"w-full\">\n                                        <el-input class=\"specCustom\" v-model=\"form.specs[index]\" />\n                                        <el-popover\n                                            placement=\"top-start\"\n                                            :title=\"$t('cronjob.nextTime')\"\n                                            width=\"200\"\n                                            trigger=\"click\"\n                                        >\n                                            <div v-for=\"(time, index_t) of nextTimes\" :key=\"index_t\">\n                                                <el-tag class=\"mt-2\">{{ time }}</el-tag>\n                                            </div>\n                                            <template #reference>\n                                                <el-button class=\"ml-2.5\" @click=\"loadNext(spec)\" link type=\"primary\">\n                                                    {{ $t('commons.button.preview') }}\n                                                </el-button>\n                                            </template>\n                                        </el-popover>\n                                        <el-button\n                                            class=\"ml-2.5\"\n                                            link\n                                            type=\"primary\"\n                                            @click=\"handleSpecCustomDelete(index)\"\n                                            v-if=\"form.specs.length > 1\"\n                                        >\n                                            {{ $t('commons.button.delete') }}\n                                        </el-button>\n                                        <el-divider v-if=\"form.specs.length > 1\" class=\"divider\" />\n                                    </div>\n                                    <span class=\"input-help logText\" v-if=\"!isFxplay\">\n                                        {{ $t('cronjob.cronSpecDoc') }}\n                                        <el-link class=\"link\" icon=\"Position\" @click=\"toDoc\" type=\"primary\">\n                                            {{ $t('firewall.quickJump') }}\n                                        </el-link>\n                                    </span>\n                                </el-form-item>\n                                <el-button class=\"mb-3\" @click=\"handleSpecCustomAdd()\">\n                                    {{ $t('commons.button.add') }}\n                                </el-button>\n                            </div>\n                        </el-card>\n\n                        <el-card class=\"mt-5\">\n                            <el-row :gutter=\"20\">\n                                <LayoutCol v-if=\"isWebsite()\">\n                                    <el-form-item\n                                        :label=\"form.type === 'website' ? $t('cronjob.website') : $t('menu.website')\"\n                                        prop=\"websiteList\"\n                                    >\n                                        <el-select\n                                            v-model=\"form.websiteList\"\n                                            multiple\n                                            @change=\"\n                                                form.websiteList = form.websiteList.includes('all')\n                                                    ? ['all']\n                                                    : form.websiteList\n                                            \"\n                                        >\n                                            <el-option :label=\"$t('commons.table.all')\" value=\"all\" />\n                                            <el-option\n                                                v-for=\"(item, index) in websiteOptions\"\n                                                :key=\"index\"\n                                                :value=\"item.id + ''\"\n                                                :label=\"item.primaryDomain\"\n                                            >\n                                                <div class=\"option-content\">\n                                                    <el-tooltip\n                                                        :content=\"item.primaryDomain\"\n                                                        placement=\"top\"\n                                                        :open-delay=\"500\"\n                                                        effect=\"dark\"\n                                                    >\n                                                        <span class=\"domain-text\">{{ item.primaryDomain }}</span>\n                                                    </el-tooltip>\n\n                                                    <el-tag class=\"tagClass\">\n                                                        {{ item.alias }}\n                                                    </el-tag>\n                                                </div>\n                                            </el-option>\n                                        </el-select>\n                                        <span class=\"input-help\" v-if=\"form.type === 'cutWebsiteLog'\">\n                                            {{ $t('cronjob.cutWebsiteLogHelper') }}\n                                        </span>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol v-if=\"form.type === 'app'\">\n                                    <el-form-item :label=\"$t('cronjob.app')\" prop=\"appIdList\">\n                                        <el-select\n                                            clearable\n                                            v-model=\"form.appIdList\"\n                                            multiple\n                                            @change=\"\n                                                form.appIdList = form.appIdList.includes('all')\n                                                    ? ['all']\n                                                    : form.appIdList\n                                            \"\n                                        >\n                                            <el-option :label=\"$t('commons.table.all')\" value=\"all\" />\n                                            <div v-for=\"item in appOptions\" :key=\"item.id\">\n                                                <el-option :value=\"item.id + ''\" :label=\"item.name\">\n                                                    <span>{{ item.name }}</span>\n                                                    <el-tag class=\"tagClass\">\n                                                        {{ item.key }}\n                                                    </el-tag>\n                                                </el-option>\n                                            </div>\n                                        </el-select>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol v-if=\"form.type === 'database'\">\n                                    <el-form-item :label=\"$t('cronjob.database')\">\n                                        <el-select v-model=\"form.dbType\" @change=\"loadDatabases\">\n                                            <el-option label=\"MySQL\" value=\"mysql\" />\n                                            <el-option label=\"MySQL-Cluster\" value=\"mysql-cluster\" />\n                                            <el-option label=\"Mariadb\" value=\"mariadb\" />\n                                            <el-option label=\"PostgreSQL\" value=\"postgresql\" />\n                                            <el-option label=\"PostgreSQL-Cluster\" value=\"postgresql-cluster\" />\n                                        </el-select>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol v-if=\"form.type === 'database'\">\n                                    <el-form-item :label=\"$t('cronjob.database')\" prop=\"dbNameList\">\n                                        <el-select\n                                            clearable\n                                            v-model=\"form.dbNameList\"\n                                            multiple\n                                            @change=\"\n                                                form.dbNameList = form.dbNameList.includes('all')\n                                                    ? ['all']\n                                                    : form.dbNameList\n                                            \"\n                                        >\n                                            <el-option :label=\"$t('commons.table.all')\" value=\"all\" />\n                                            <el-option\n                                                v-for=\"item in dbInfo.dbs\"\n                                                :key=\"item.id\"\n                                                :value=\"item.id + ''\"\n                                                :label=\"item.name\"\n                                            >\n                                                <span>{{ item.name }}</span>\n                                                <el-tag class=\"tagClass\">\n                                                    {{\n                                                        item.from === 'local'\n                                                            ? $t('commons.table.local')\n                                                            : $t('database.remote')\n                                                    }}\n                                                </el-tag>\n                                                <el-tag class=\"tagClass\">\n                                                    {{ item.database }}\n                                                </el-tag>\n                                            </el-option>\n                                        </el-select>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol\n                                    :span=\"20\"\n                                    v-if=\"\n                                        form.type === 'database' &&\n                                        (form.dbType === 'mysql' || form.dbType === 'mysql-cluster')\n                                    \"\n                                >\n                                    <el-form-item :label=\"$t('cronjob.backupArgs')\">\n                                        <el-select v-model=\"form.argItems\" filterable allow-create multiple>\n                                            <el-option\n                                                v-for=\"item in mysqlArgs\"\n                                                :key=\"item.arg\"\n                                                :value=\"item.arg\"\n                                                :label=\"item.arg\"\n                                            >\n                                                {{ item.arg }}\n                                                <span class=\"ml-2\">{{ item.description }}</span>\n                                            </el-option>\n                                        </el-select>\n                                        <span class=\"input-help\">\n                                            {{ $t('cronjob.backupArgsHelper') }}\n                                        </span>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol v-if=\"form.type === 'directory'\">\n                                    <el-form-item :label=\"$t('commons.button.backup')\">\n                                        <el-radio-group v-model=\"form.isDir\" class=\"w-full\">\n                                            <el-radio :value=\"true\">{{ $t('file.dir') }}</el-radio>\n                                            <el-radio :value=\"false\">{{ $t('menu.files') }}</el-radio>\n                                        </el-radio-group>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol v-if=\"form.type === 'curl'\">\n                                    <el-form-item :label=\"$t('cronjob.url')\" prop=\"urlItems\">\n                                        <div v-for=\"(_, index) of form.urlItems\" :key=\"index\" class=\"w-full\">\n                                            <el-input class=\"mt-2\" v-model=\"form.urlItems[index]\">\n                                                <template #append>\n                                                    <el-button\n                                                        link\n                                                        icon=\"Delete\"\n                                                        @click=\"form.urlItems.splice(index, 1)\"\n                                                    />\n                                                </template>\n                                            </el-input>\n                                        </div>\n                                        <el-button class=\"mt-2\" @click=\"form.urlItems.push('')\">\n                                            {{ $t('commons.button.add') }}\n                                        </el-button>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n\n                            <el-row :gutter=\"20\">\n                                <el-col :span=\"24\" v-if=\"hasScript()\">\n                                    <el-form-item>\n                                        <el-checkbox @change=\"loadUserOptions(false)\" v-model=\"form.inContainer\">\n                                            {{ $t('cronjob.containerCheckBox') }}\n                                        </el-checkbox>\n                                    </el-form-item>\n\n                                    <el-row :gutter=\"20\" v-if=\"form.inContainer\">\n                                        <LayoutCol>\n                                            <el-form-item :label=\"$t('cronjob.containerName')\" prop=\"containerName\">\n                                                <el-select\n                                                    v-model=\"form.containerName\"\n                                                    @change=\"loadUserOptions(false)\"\n                                                >\n                                                    <div v-for=\"item in containerOptions\" :key=\"item.name\">\n                                                        <el-option :value=\"item.name\" :label=\"item.name\">\n                                                            {{ item.name }}\n                                                            <Status\n                                                                class=\"float-right\"\n                                                                :key=\"item.state\"\n                                                                :status=\"item.state\"\n                                                            />\n                                                        </el-option>\n                                                    </div>\n                                                </el-select>\n                                            </el-form-item>\n                                        </LayoutCol>\n                                        <LayoutCol>\n                                            <el-form-item\n                                                :label=\"$t('container.command')\"\n                                                prop=\"command\"\n                                                :rules=\"Rules.requiredInput\"\n                                            >\n                                                <el-checkbox border v-model=\"form.isCustom\">\n                                                    {{ $t('container.custom') }}\n                                                </el-checkbox>\n                                                <el-select\n                                                    v-if=\"!form.isCustom\"\n                                                    style=\"width: calc(100% - 100px)\"\n                                                    filterable\n                                                    clearable\n                                                    v-model=\"form.command\"\n                                                >\n                                                    <el-option value=\"ash\" label=\"/bin/ash\" />\n                                                    <el-option value=\"bash\" label=\"/bin/bash\" />\n                                                    <el-option value=\"sh\" label=\"/bin/sh\" />\n                                                </el-select>\n                                                <el-input\n                                                    clearable\n                                                    v-else\n                                                    style=\"width: calc(100% - 100px)\"\n                                                    v-model=\"form.command\"\n                                                />\n                                            </el-form-item>\n                                        </LayoutCol>\n                                    </el-row>\n                                    <el-row :gutter=\"20\">\n                                        <LayoutCol>\n                                            <el-form-item :label=\"$t('commons.table.user')\" prop=\"user\">\n                                                <el-select filterable v-model=\"form.user\">\n                                                    <div v-for=\"item in userOptions\" :key=\"item\">\n                                                        <el-option :value=\"item\" :label=\"item\" />\n                                                    </div>\n                                                </el-select>\n                                            </el-form-item>\n                                        </LayoutCol>\n                                        <LayoutCol v-if=\"!form.inContainer\">\n                                            <el-form-item :label=\"$t('cronjob.executor')\" prop=\"executor\">\n                                                <el-checkbox border v-model=\"form.isCustom\">\n                                                    {{ $t('container.custom') }}\n                                                </el-checkbox>\n                                                <el-select\n                                                    v-if=\"!form.isCustom\"\n                                                    style=\"width: calc(100% - 100px)\"\n                                                    v-model=\"form.executor\"\n                                                >\n                                                    <el-option value=\"bash\" label=\"bash\" />\n                                                    <el-option value=\"sh\" label=\"sh\" />\n                                                    <el-option value=\"python\" label=\"python\" />\n                                                    <el-option value=\"python2\" label=\"python2\" />\n                                                    <el-option value=\"python3\" label=\"python3\" />\n                                                </el-select>\n                                                <el-input\n                                                    clearable\n                                                    v-else\n                                                    style=\"width: calc(100% - 100px)\"\n                                                    v-model=\"form.executor\"\n                                                />\n                                            </el-form-item>\n                                        </LayoutCol>\n                                    </el-row>\n\n                                    <el-form-item :label=\"$t('cronjob.shellContent')\" prop=\"scriptMode\">\n                                        <el-radio-group @change=\"form.script = ''\" v-model=\"form.scriptMode\">\n                                            <el-radio value=\"input\">{{ $t('commons.button.edit') }}</el-radio>\n                                            <el-radio value=\"library\">{{ $t('cronjob.library.library') }}</el-radio>\n                                            <el-radio value=\"select\">{{ $t('container.pathSelect') }}</el-radio>\n                                        </el-radio-group>\n                                    </el-form-item>\n                                    <el-form-item class=\"-mt-4\" v-if=\"form.scriptMode === 'input'\" prop=\"script\">\n                                        <CodemirrorPro\n                                            v-model=\"form.script\"\n                                            placeholder=\"#Define or paste the content of your shell file here\"\n                                            mode=\"javascript\"\n                                            :heightDiff=\"400\"\n                                        />\n                                    </el-form-item>\n                                    <el-row :gutter=\"20\" class=\"-mt-4\">\n                                        <LayoutCol>\n                                            <el-form-item prop=\"scriptID\" v-if=\"form.scriptMode === 'library'\">\n                                                <el-select filterable v-model=\"form.scriptID\">\n                                                    <el-option\n                                                        v-for=\"item in scriptOptions\"\n                                                        :key=\"item.id\"\n                                                        :value=\"item.id\"\n                                                        :label=\"item.name\"\n                                                    />\n                                                </el-select>\n                                            </el-form-item>\n                                            <el-form-item prop=\"script\" v-if=\"form.scriptMode === 'select'\">\n                                                <el-input\n                                                    :placeholder=\"$t('commons.example') + '/tmp/test.sh'\"\n                                                    v-model=\"form.script\"\n                                                >\n                                                    <template #prepend>\n                                                        <el-button\n                                                            icon=\"Folder\"\n                                                            @click=\"scriptFileRef.acceptParams({ dir: false })\"\n                                                        />\n                                                    </template>\n                                                </el-input>\n                                            </el-form-item>\n                                        </LayoutCol>\n                                    </el-row>\n                                </el-col>\n\n                                <LayoutCol v-if=\"isDir() && form.isDir\">\n                                    <el-form-item :label=\"$t('cronjob.backupContent')\" prop=\"sourceDir\">\n                                        <el-input v-model=\"form.sourceDir\">\n                                            <template #prepend>\n                                                <el-button\n                                                    icon=\"Folder\"\n                                                    @click=\"dirRef.acceptParams({ dir: true, path: form.sourceDir })\"\n                                                />\n                                            </template>\n                                        </el-input>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol v-if=\"isDir() && !form.isDir\">\n                                    <el-form-item :label=\"$t('cronjob.backupContent')\" prop=\"files\">\n                                        <el-input>\n                                            <template #prepend>\n                                                <el-button\n                                                    icon=\"Folder\"\n                                                    @click=\"fileRef.acceptParams({ dir: false })\"\n                                                />\n                                            </template>\n                                        </el-input>\n                                        <div class=\"w-full\">\n                                            <ComplexTable :show-header=\"false\" :data=\"form.files\" v-if=\"form.files\">\n                                                <el-table-column prop=\"val\" />\n                                                <el-table-column width=\"60\">\n                                                    <template #default=\"scope\">\n                                                        <el-button\n                                                            link\n                                                            type=\"primary\"\n                                                            @click=\"handleFileDelete(scope.$index)\"\n                                                        >\n                                                            {{ $t('commons.button.delete') }}\n                                                        </el-button>\n                                                    </template>\n                                                </el-table-column>\n                                            </ComplexTable>\n                                        </div>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n\n                            <el-row :gutter=\"20\" v-if=\"form.type === 'snapshot'\">\n                                <LayoutCol>\n                                    <el-form-item prop=\"withImage\">\n                                        <el-checkbox v-model=\"form.withImage\" :label=\"$t('cronjob.withImage')\" />\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n                            <el-row :gutter=\"20\" v-if=\"form.type === 'snapshot'\">\n                                <LayoutCol>\n                                    <el-form-item :label=\"$t('cronjob.ignoreApp')\" prop=\"ignoreAppIDs\">\n                                        <el-select v-model=\"form.ignoreAppIDs\" multiple cleanable>\n                                            <div v-for=\"item in appOptions\" :key=\"item.id\">\n                                                <el-option :value=\"item.id\" :label=\"item.name\">\n                                                    <span>{{ item.name }}</span>\n                                                    <el-tag class=\"tagClass\">\n                                                        {{ item.key }}\n                                                    </el-tag>\n                                                </el-option>\n                                            </div>\n                                        </el-select>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n\n                            <el-row :gutter=\"20\">\n                                <LayoutCol v-if=\"isBackup()\">\n                                    <el-form-item :label=\"$t('setting.backupAccount')\" prop=\"sourceAccountItems\">\n                                        <el-select multiple v-model=\"form.sourceAccountItems\" @change=\"changeAccount\">\n                                            <div v-for=\"item in backupOptions\" :key=\"item.id\">\n                                                <el-option\n                                                    v-if=\"item.type !== $t('setting.LOCAL')\"\n                                                    :value=\"item.id\"\n                                                    :label=\"item.name\"\n                                                >\n                                                    {{ item.name }}\n                                                    <el-tag class=\"tagClass\" type=\"primary\">{{ item.type }}</el-tag>\n                                                </el-option>\n                                                <el-option v-else :value=\"item.id\" :label=\"item.type\" />\n                                            </div>\n                                        </el-select>\n                                        <span class=\"input-help logText\">\n                                            {{ $t('cronjob.targetHelper') }}\n                                            <el-link\n                                                class=\"link\"\n                                                icon=\"Position\"\n                                                @click=\"goRouter('/settings/backupaccount')\"\n                                                type=\"primary\"\n                                            >\n                                                {{ $t('firewall.quickJump') }}\n                                            </el-link>\n                                        </span>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol v-if=\"isBackup()\">\n                                    <el-form-item :label=\"$t('cronjob.default_download_path')\" prop=\"downloadAccountID\">\n                                        <el-select v-model=\"form.downloadAccountID\">\n                                            <div v-for=\"item in accountOptions\" :key=\"item.id\">\n                                                <el-option\n                                                    v-if=\"item.type !== $t('setting.LOCAL')\"\n                                                    :value=\"item.id\"\n                                                    :label=\"item.name\"\n                                                >\n                                                    {{ item.name }}\n                                                    <el-tag class=\"tagClass\" type=\"primary\">{{ item.type }}</el-tag>\n                                                </el-option>\n                                                <el-option v-else :value=\"item.id\" :label=\"item.type\" />\n                                            </div>\n                                        </el-select>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n\n                            <el-row :gutter=\"20\">\n                                <LayoutCol v-if=\"isBackup() && !isDatabase()\">\n                                    <el-form-item :label=\"$t('setting.compressPassword')\" prop=\"secret\">\n                                        <el-input type=\"password\" show-password v-model=\"form.secret\" />\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol>\n                                    <el-form-item :label=\"$t('cronjob.retainCopies')\" prop=\"retainCopies\">\n                                        <el-input-number\n                                            class=\"selectClass\"\n                                            :min=\"1\"\n                                            :precision=\"0\"\n                                            step-strictly\n                                            :step=\"1\"\n                                            v-model.number=\"form.retainCopies\"\n                                        />\n                                        <span v-if=\"isBackup()\" class=\"input-help\">\n                                            {{ $t('cronjob.retainCopiesHelper1') }}\n                                        </span>\n                                        <span v-else class=\"input-help\">{{ $t('cronjob.retainCopiesHelper') }}</span>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n                            <el-row :gutter=\"20\">\n                                <LayoutCol :span=\"20\" v-if=\"hasExclusionRules()\">\n                                    <el-form-item :label=\"$t('cronjob.exclusionRules')\" prop=\"exclusionRules\">\n                                        <InputTag\n                                            class=\"w-full\"\n                                            v-model:tags=\"form.ignoreFiles\"\n                                            :withFile=\"true\"\n                                            :baseDir=\"loadItemDir()\"\n                                            :egHelp=\"$t('cronjob.exclusionRulesHelper')\"\n                                        />\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n                        </el-card>\n\n                        <el-card class=\"mt-5\">\n                            <el-row :gutter=\"20\">\n                                <LayoutCol :span=\"8\">\n                                    <el-form-item prop=\"hasAlert\">\n                                        <el-checkbox v-model=\"form.hasAlert\" :label=\"$t('xpack.alert.isAlert')\" />\n                                        <span class=\"input-help\">{{ $t('xpack.alert.cronJobHelper') }}</span>\n\n                                        <span class=\"input-help logText\" v-if=\"form.hasAlert && !isProductPro\">\n                                            {{ $t('xpack.alert.licenseHelper') }}\n                                            <el-link class=\"link\" @click=\"toUpload\" type=\"primary\">\n                                                {{ $t('license.levelUpPro') }}\n                                            </el-link>\n                                        </span>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n                            <el-row :gutter=\"20\">\n                                <LayoutCol>\n                                    <el-form-item\n                                        :label=\"$t('xpack.alert.alertMethod')\"\n                                        v-if=\"form.hasAlert\"\n                                        prop=\"alertMethodItems\"\n                                    >\n                                        <el-select\n                                            class=\"selectClass\"\n                                            v-model=\"form.alertMethodItems\"\n                                            multiple\n                                            cleanable\n                                        >\n                                            <el-option value=\"mail\" :label=\"$t('xpack.alert.mail')\" />\n                                            <el-option\n                                                value=\"weCom\"\n                                                v-if=\"!globalStore.isIntl\"\n                                                :disabled=\"!form.hasAlert || !isProductPro\"\n                                                :label=\"$t('xpack.alert.weCom')\"\n                                            />\n                                            <el-option\n                                                value=\"dingTalk\"\n                                                v-if=\"!globalStore.isIntl\"\n                                                :disabled=\"!form.hasAlert || !isProductPro\"\n                                                :label=\"$t('xpack.alert.dingTalk')\"\n                                            />\n                                            <el-option\n                                                value=\"feiShu\"\n                                                v-if=\"!globalStore.isIntl\"\n                                                :disabled=\"!form.hasAlert || !isProductPro\"\n                                                :label=\"$t('xpack.alert.feiShu')\"\n                                            />\n                                            <el-option\n                                                value=\"sms\"\n                                                v-if=\"!globalStore.isIntl\"\n                                                :disabled=\"!form.hasAlert || !isProductPro\"\n                                                :label=\"$t('xpack.alert.sms')\"\n                                            />\n                                        </el-select>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol>\n                                    <el-form-item\n                                        prop=\"alertCount\"\n                                        v-if=\"form.hasAlert\"\n                                        :label=\"$t('xpack.alert.alertCount')\"\n                                    >\n                                        <el-input-number\n                                            class=\"selectClass\"\n                                            :min=\"1\"\n                                            :precision=\"0\"\n                                            step-strictly\n                                            :step=\"1\"\n                                            v-model.number=\"form.alertCount\"\n                                        />\n                                        <span class=\"input-help\">{{ $t('xpack.alert.alertCountHelper') }}</span>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n                        </el-card>\n\n                        <el-card class=\"mt-5\">\n                            <el-row :gutter=\"20\">\n                                <LayoutCol :span=\"20\" v-if=\"hasIgnore()\">\n                                    <el-form-item>\n                                        <el-checkbox v-model=\"form.ignoreErr\" :label=\"$t('cronjob.ignoreErr')\" />\n                                        <span class=\"input-help\">{{ $t('cronjob.ignoreErrHelper') }}</span>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol>\n                                    <el-form-item :label=\"$t('cronjob.timeout')\" prop=\"timeoutItem\">\n                                        <el-input type=\"number\" class=\"selectClass\" v-model.number=\"form.timeoutItem\">\n                                            <template #append>\n                                                <el-select v-model=\"form.timeoutUnit\" style=\"width: 80px\">\n                                                    <el-option :label=\"$t('commons.units.second')\" value=\"s\" />\n                                                    <el-option :label=\"$t('commons.units.minute')\" value=\"m\" />\n                                                    <el-option :label=\"$t('commons.units.hour')\" value=\"h\" />\n                                                </el-select>\n                                            </template>\n                                        </el-input>\n                                    </el-form-item>\n                                </LayoutCol>\n                                <LayoutCol>\n                                    <el-form-item :label=\"$t('cronjob.retryTimes')\" prop=\"retryTimes\">\n                                        <el-input-number\n                                            class=\"selectClass\"\n                                            :min=\"0\"\n                                            :precision=\"0\"\n                                            step-strictly\n                                            :step=\"1\"\n                                            v-model.number=\"form.retryTimes\"\n                                        />\n                                        <span class=\"input-help\">{{ $t('cronjob.retryTimesHelper') }}</span>\n                                    </el-form-item>\n                                </LayoutCol>\n                            </el-row>\n                        </el-card>\n\n                        <el-form-item class=\"mt-5\">\n                            <el-button :disabled=\"loading\" @click=\"goBack\">\n                                {{ $t('commons.button.back') }}\n                            </el-button>\n                            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                                {{ $t('commons.button.confirm') }}\n                            </el-button>\n                        </el-form-item>\n                    </el-col>\n                </el-row>\n            </el-form>\n        </template>\n    </LayoutContent>\n\n    <FileList ref=\"scriptFileRef\" @choose=\"loadScriptDir\" />\n    <FileList ref=\"dirRef\" @choose=\"loadDir\" />\n    <FileList ref=\"fileRef\" @choose=\"loadFile\" />\n    <LicenseImport ref=\"licenseRef\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport FileList from '@/components/file-list/index.vue';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport InputTag from '@/components/input-tag/index.vue';\nimport LayoutCol from '@/components/layout-col/form.vue';\nimport CleanLogConfig from '@/views/cronjob/cronjob/config/clean-log.vue';\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport { listBackupOptions } from '@/api/modules/backup';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { Cronjob } from '@/api/interface/cronjob';\nimport { addCronjob, editCronjob, loadCronjobInfo, loadNextHandle, loadScriptOptions } from '@/api/modules/cronjob';\nimport { listDbItems } from '@/api/modules/database';\nimport { getWebsiteOptions } from '@/api/modules/website';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { useRouter } from 'vue-router';\nimport { listContainer } from '@/api/modules/container';\nimport { Database } from '@/api/interface/database';\nimport { listAppInstalled } from '@/api/modules/app';\nimport {\n    loadDefaultSpec,\n    loadDefaultSpecCustom,\n    specOptions,\n    transObjToSpec,\n    transSpecToObj,\n    weekOptions,\n    cronjobTypes,\n    mysqlArgs,\n} from '../helper';\nimport { loadUsers } from '@/api/modules/toolbox';\nimport { loadContainerUsers } from '@/api/modules/container';\nimport { storeToRefs } from 'pinia';\nimport { GlobalStore } from '@/store';\nimport LicenseImport from '@/components/license-import/index.vue';\nimport { splitTimeFromSecond, transferTimeToSecond } from '@/utils/util';\nimport { getGroupList } from '@/api/modules/group';\nimport { routerToName, routerToPath } from '@/utils/router';\nimport { loadBaseDir } from '@/api/modules/setting';\nconst router = useRouter();\n\nconst globalStore = GlobalStore();\nconst licenseRef = ref();\nconst scriptFileRef = ref();\nconst dirRef = ref();\nconst fileRef = ref();\nconst { isProductPro, isFxplay } = storeToRefs(globalStore);\nconst loading = ref();\nconst nextTimes = ref([]);\n\nconst baseDir = ref();\n\nconst isCreate = ref();\nconst defaultGroupID = ref();\nconst form = reactive<Cronjob.CronjobInfo>({\n    id: 0,\n    name: '',\n    type: 'shell',\n    groupID: null,\n    specCustom: false,\n    spec: '',\n    specs: ['0 0 * * *'],\n    specObjs: [{ specType: 'perMonth', week: 1, day: 3, hour: 1, minute: 30, second: 30 }],\n\n    executor: '',\n    isExecutorCustom: false,\n    script: '',\n    scriptMode: 'input',\n    isCustom: false,\n    command: '',\n    inContainer: false,\n    containerName: '',\n    user: '',\n\n    scriptID: null,\n    appID: '',\n    website: '',\n    ignoreFiles: [],\n    exclusionRules: '',\n    dbType: 'mysql',\n    dbName: '',\n    url: '',\n    urlItems: [''],\n    isDir: true,\n    files: [],\n    sourceDir: '',\n    snapshotRule: { withImage: false, ignoreAppIDs: [] },\n    ignoreAppIDs: [],\n    withImage: false,\n\n    sourceAccounts: [],\n    downloadAccount: '',\n    sourceAccountIDs: '',\n    downloadAccountID: 0,\n    sourceAccountItems: [],\n\n    websiteList: [],\n    appIdList: [],\n    dbNameList: [],\n\n    retainCopies: 7,\n    ignoreErr: false,\n    retryTimes: 3,\n    timeout: 3600,\n    timeoutItem: 3600,\n    timeoutUnit: 's',\n    status: '',\n    secret: '',\n    hasAlert: false,\n    alertCount: 3,\n    alertTitle: '',\n    alertMethod: '',\n    alertMethodItems: [],\n\n    scopes: [],\n    args: '',\n    argItems: [],\n});\n\nconst search = async () => {\n    if (!isCreate.value) {\n        loading.value = true;\n        await loadCronjobInfo(form.id)\n            .then((res) => {\n                loading.value = false;\n                form.name = res.data.name;\n                form.groupID = res.data.groupID || defaultGroupID.value;\n                form.type = res.data.type;\n                form.specCustom = res.data.specCustom;\n                form.spec = res.data.spec;\n                form.specs = res.data.specs || [];\n                if (!form.specCustom && form.spec) {\n                    let objs = [];\n                    for (const item of res.data.spec.split('&&')) {\n                        objs.push(transSpecToObj(item));\n                    }\n                    form.specObjs = objs || [];\n                }\n                if (form.specCustom && form.spec) {\n                    form.specs = form.spec.split('&&') || [];\n                }\n\n                form.script = res.data.script;\n                form.scriptMode = res.data.scriptMode;\n                form.urlItems = res.data.url.split(',') || [];\n\n                form.containerName = res.data.containerName;\n                form.user = res.data.user;\n                if (form.containerName.length !== 0) {\n                    form.inContainer = true;\n                    form.command = res.data.command || 'sh';\n                    form.isCustom = form.command !== 'sh' && form.command !== 'bash' && form.command !== 'ash';\n                } else {\n                    form.executor = res.data.executor || 'bash';\n                    form.isCustom =\n                        form.executor !== 'sh' &&\n                        form.executor !== 'bash' &&\n                        form.executor !== 'python' &&\n                        form.executor !== 'python3';\n                }\n\n                form.scriptID = res.data.scriptID || null;\n                form.appID = res.data.appID;\n                form.appIdList = res.data.appID ? res.data.appID.split(',') : [];\n                form.website = res.data.website;\n                form.websiteList = res.data.website ? res.data.website.split(',') : [];\n                form.exclusionRules = res.data.exclusionRules;\n                form.ignoreFiles = res.data.exclusionRules ? res.data.exclusionRules.split(',') : [];\n                form.dbType = res.data.dbType;\n                form.dbName = res.data.dbName;\n                form.dbNameList = res.data.dbName ? res.data.dbName.split(',') : [];\n                form.url = res.data.url;\n                form.withImage = res.data.snapshotRule.withImage;\n                form.ignoreAppIDs = res.data.snapshotRule.ignoreAppIDs;\n\n                form.isDir = res.data.isDir;\n                form.sourceDir = res.data.sourceDir;\n                if (!form.isDir) {\n                    let files = form.sourceDir?.split(',') || [];\n                    for (const item of files) {\n                        form.files.push({ val: item });\n                    }\n                }\n\n                form.sourceAccountIDs = res.data.sourceAccountIDs;\n                form.downloadAccountID = res.data.downloadAccountID;\n                if (form.sourceAccountIDs) {\n                    let list = [];\n                    form.sourceAccountItems = [];\n                    list = form.sourceAccountIDs.split(',');\n                    for (const item of list) {\n                        if (item) {\n                            form.sourceAccountItems.push(Number(item));\n                        }\n                    }\n                }\n\n                form.ignoreErr = res.data.ignoreErr;\n                form.retainCopies = res.data.retainCopies;\n                form.retryTimes = res.data.retryTimes;\n\n                form.timeout = res.data.timeout || 3600;\n                let item = splitTimeFromSecond(form.timeout);\n                form.timeoutItem = item.timeItem;\n                form.timeoutUnit = item.timeUnit;\n\n                form.secret = res.data.secret;\n                form.hasAlert = res.data.alertCount > 0;\n                form.alertCount = res.data.alertCount || 3;\n                form.alertTitle = res.data.alertTitle;\n                if (res.data.alertMethod) {\n                    form.alertMethodItems = res.data.alertMethod.split(',') || [];\n                } else {\n                    form.alertMethodItems = [];\n                }\n                form.scopes = res.data.scopes;\n                form.argItems = res.data.args ? res.data.args.split(',') : [];\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    }\n    loadBackups();\n    loadAppInstalls();\n    loadUserOptions(true);\n    loadWebsites();\n    loadContainers();\n    loadScripts();\n    if (form.dbType) {\n        loadDatabases(form.dbType);\n    } else {\n        loadDatabases('mysql');\n    }\n};\n\nconst goRouter = async (path: string) => {\n    routerToPath(path);\n};\n\nconst containerOptions = ref([]);\nconst websiteOptions = ref([]);\nconst backupOptions = ref([]);\nconst accountOptions = ref([]);\nconst appOptions = ref([]);\nconst userOptions = ref([]);\nconst scriptOptions = ref([]);\nconst groupOptions = ref([]);\n\nconst dbInfo = reactive({\n    isExist: false,\n    name: '',\n    version: '',\n    dbs: [] as Array<Database.DbItem>,\n});\n\nconst verifyScript = (rule: any, value: any, callback: any) => {\n    if (!form.script || form.script.length === 0) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n        return;\n    }\n    callback();\n};\nconst verifyUrlItems = () => {\n    if (!form.urlItems || form.urlItems.length === 0) {\n        return false;\n    }\n    for (const item of form.urlItems) {\n        if (!item) {\n            return false;\n        }\n    }\n    return true;\n};\n\nconst verifySpec = (rule: any, value: any, callback: any) => {\n    if (form.specCustom) {\n        if (form.specs.length === 0) {\n            callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n            return;\n        }\n        for (let i = 0; i < form.specs.length; i++) {\n            if (form.specs[i] && form.specs[i].split(' ').length === 5) {\n                continue;\n            }\n            callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n            return;\n        }\n        callback();\n        return;\n    }\n    if (form.specObjs.length === 0) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n    }\n    for (let i = 0; i < form.specObjs.length; i++) {\n        let item = form.specObjs[i];\n        if (\n            !Number.isInteger(item.day) ||\n            !Number.isInteger(item.hour) ||\n            !Number.isInteger(item.minute) ||\n            !Number.isInteger(item.second) ||\n            !Number.isInteger(item.week)\n        ) {\n            callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n            return;\n        }\n        switch (item.specType) {\n            case 'perMonth':\n                if (\n                    item.day < 0 ||\n                    item.day > 31 ||\n                    item.hour < 0 ||\n                    item.hour > 23 ||\n                    item.minute < 0 ||\n                    item.minute > 59\n                ) {\n                    callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n                    return;\n                }\n                break;\n            case 'perNDay':\n                if (\n                    item.day < 0 ||\n                    item.day > 366 ||\n                    item.hour < 0 ||\n                    item.hour > 23 ||\n                    item.minute < 0 ||\n                    item.minute > 59\n                ) {\n                    callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n                    return;\n                }\n                break;\n            case 'perWeek':\n                if (\n                    item.week < 0 ||\n                    item.week > 6 ||\n                    item.hour < 0 ||\n                    item.hour > 23 ||\n                    item.minute < 0 ||\n                    item.minute > 59\n                ) {\n                    callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n                    return;\n                }\n                break;\n            case 'perDay':\n                if (item.hour < 0 || item.hour > 23 || item.minute < 0 || item.minute > 59) {\n                    callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n                    return;\n                }\n                break;\n            case 'perNHour':\n                if (item.hour < 0 || item.hour > 8784 || item.minute < 0 || item.minute > 59) {\n                    callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n                    return;\n                }\n                break;\n            case 'perHour':\n                if (item.minute < 0 || item.minute > 59) {\n                    callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n                    return;\n                }\n            case 'perNMinute':\n                if (item.minute < 0 || item.minute > 527040) {\n                    callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n                    return;\n                }\n                break;\n            case 'perNSecond':\n                if (item.second < 0 || item.second > 31622400) {\n                    callback(new Error(i18n.global.t('cronjob.cronSpecRule', [i + 1])));\n                    return;\n                }\n                break;\n        }\n    }\n    callback();\n};\n\nconst verifyFiles = (rule: any, value: any, callback: any) => {\n    if (!form.files || form.files.length === 0) {\n        callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n        return;\n    }\n    callback();\n};\n\nconst checkSendCount = (rule: any, value: any, callback: any) => {\n    if (value === '') {\n        callback();\n    }\n    const regex = /^(?:[1-9]|[12][0-9]|30)$/;\n    if (!regex.test(value)) {\n        return callback(new Error(i18n.global.t('commons.rule.numberRange', [1, 30])));\n    }\n    callback();\n};\n\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.noSpace, Rules.linuxName],\n    type: [Rules.requiredSelect],\n    spec: [\n        { validator: verifySpec, trigger: 'blur', required: true },\n        { validator: verifySpec, trigger: 'change', required: true },\n    ],\n\n    script: [{ validator: verifyScript, trigger: 'blur', required: true }],\n    scriptID: [Rules.requiredSelect],\n    containerName: [Rules.requiredSelect],\n    websiteList: [Rules.requiredSelect],\n    appIdList: [Rules.requiredSelect],\n    dbNameList: [Rules.requiredSelect],\n    files: [{ validator: verifyFiles, trigger: 'blur', required: true }],\n    sourceDir: [Rules.requiredInput],\n    sourceAccountItems: [Rules.requiredSelect],\n    downloadAccountID: [Rules.requiredSelect],\n    retainCopies: [Rules.number],\n    retryTimes: [Rules.number],\n    timeoutItem: [Rules.number],\n    timeoutUnit: [Rules.requiredSelect],\n    alertCount: [Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }],\n    alertMethodItems: [Rules.requiredSelect],\n    scopes: [Rules.requiredSelect],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst toDoc = () => {\n    window.open(globalStore.docsUrl + '/user_manual/cronjobs/', '_blank', 'noopener,noreferrer');\n};\n\nconst loadDir = async (path: string) => {\n    form.sourceDir = path;\n};\n\nconst loadScriptDir = async (path: string) => {\n    form.script = path;\n};\n\nconst loadItemDir = () => {\n    if (form.type === 'directory' && form.isDir) {\n        return form.sourceDir;\n    }\n    return baseDir.value;\n};\n\nconst loadInstallDir = async () => {\n    const pathRes = await loadBaseDir();\n    baseDir.value = pathRes.data;\n};\n\nconst loadGroups = async () => {\n    const res = await getGroupList('cronjob');\n    groupOptions.value = res.data || [];\n    for (const item of groupOptions.value) {\n        if (item.isDefault) {\n            defaultGroupID.value = item.id;\n            break;\n        }\n    }\n    if (isCreate.value) {\n        form.groupID = defaultGroupID.value;\n    }\n};\n\nconst goBack = () => {\n    routerToName('CronjobItem');\n};\n\nconst loadFile = async (path: string) => {\n    for (const item of form.files) {\n        if (item.val === path) {\n            return;\n        }\n    }\n    form.files.push({ val: path });\n};\n\nconst hasDay = (item: any) => {\n    return item.specType === 'perMonth' || item.specType === 'perNDay';\n};\nconst hasHour = (item: any) => {\n    return item.specType !== 'perHour' && item.specType !== 'perNMinute' && item.specType !== 'perNSecond';\n};\nconst isWebsite = () => {\n    return form.type === 'website' || form.type === 'cutWebsiteLog';\n};\nconst isDir = () => {\n    return form.type === 'directory';\n};\nconst isDatabase = () => {\n    return form.type === 'database';\n};\n\nconst loadNext = async (spec: any) => {\n    nextTimes.value = [];\n    let specItem = '';\n    if (!form.specCustom) {\n        specItem = transObjToSpec(spec.specType, spec.week, spec.day, spec.hour, spec.minute, spec.second);\n    } else {\n        specItem = spec;\n    }\n    if (!specItem) {\n        MsgError(i18n.global.t('cronjob.cronSpecRule2'));\n        return;\n    }\n    const data = await loadNextHandle(specItem);\n    nextTimes.value = data.data || [];\n};\n\nconst loadScripts = async () => {\n    const res = await loadScriptOptions();\n    scriptOptions.value = res.data || [];\n};\n\nconst loadDatabases = async (dbType: string) => {\n    const data = await listDbItems(dbType);\n    dbInfo.dbs = data.data || [];\n};\n\nconst changeType = () => {\n    form.specObjs = [loadDefaultSpec(form.type)];\n    form.specs = [loadDefaultSpecCustom(form.type)];\n    if (form.type === 'cleanLog') {\n        form.name = i18n.global.t('cronjob.cleanLog');\n    }\n};\n\nconst changeSpecType = (index: number) => {\n    let item = form.specObjs[index];\n    switch (item.specType) {\n        case 'perMonth':\n        case 'perNDay':\n            item.day = 3;\n            item.hour = 1;\n            item.minute = 30;\n            break;\n        case 'perWeek':\n            item.week = 1;\n            item.hour = 1;\n            item.minute = 30;\n            break;\n        case 'perDay':\n        case 'perNHour':\n            item.hour = 2;\n            item.minute = 30;\n            break;\n        case 'perHour':\n        case 'perNMinute':\n            item.minute = 30;\n            break;\n        case 'perNSecond':\n            item.second = 30;\n            break;\n    }\n};\n\nconst handleSpecAdd = () => {\n    let item = {\n        specType: 'perWeek',\n        week: 1,\n        day: 0,\n        hour: 1,\n        minute: 30,\n        second: 0,\n    };\n    form.specObjs.push(item);\n};\n\nconst handleSpecCustomAdd = () => {\n    form.specs.push('');\n};\n\nconst handleSpecDelete = (index: number) => {\n    form.specObjs.splice(index, 1);\n};\n\nconst handleSpecCustomDelete = (index: number) => {\n    form.specs.splice(index, 1);\n};\n\nconst handleFileDelete = (index: number) => {\n    form.files.splice(index, 1);\n};\n\nconst loadBackups = async () => {\n    const res = await listBackupOptions();\n    let options = res.data || [];\n    backupOptions.value = [];\n    let local = 0;\n    for (const item of options) {\n        if (item.id === 0) {\n            continue;\n        }\n        if (item.type == 'LOCAL') {\n            local = item.id;\n        }\n        backupOptions.value.push({ id: item.id, type: i18n.global.t('setting.' + item.type), name: item.name });\n    }\n    if (!form.sourceAccountItems) {\n        form.sourceAccountItems = local === 0 ? [local] : [];\n    }\n    changeAccount();\n};\n\nconst changeAccount = async () => {\n    accountOptions.value = [];\n    let isInAccounts = false;\n    for (const item of backupOptions.value) {\n        let exist = false;\n        for (const ac of form.sourceAccountItems) {\n            if (item.id == ac) {\n                exist = true;\n                break;\n            }\n        }\n        if (exist) {\n            if (item.id === form.downloadAccountID) {\n                isInAccounts = true;\n            }\n            accountOptions.value.push(item);\n        }\n    }\n    if (!isInAccounts) {\n        form.downloadAccountID = form.sourceAccountItems.length === 0 ? undefined : form.sourceAccountItems[0];\n    }\n};\n\nconst loadUserOptions = async (isInit: boolean) => {\n    userOptions.value = [];\n    if (!form.inContainer) {\n        const res = await loadUsers();\n        userOptions.value = res.data || [];\n    } else {\n        if (!isInit) {\n            form.user = '';\n        }\n        if (form.containerName) {\n            const res = await loadContainerUsers(form.containerName);\n            userOptions.value = res.data || [];\n        }\n    }\n};\n\nconst loadAppInstalls = async () => {\n    const res = await listAppInstalled();\n    appOptions.value = res.data || [];\n};\n\nconst loadWebsites = async () => {\n    const res = await getWebsiteOptions({});\n    websiteOptions.value = res.data || [];\n};\n\nconst loadContainers = async () => {\n    const res = await listContainer();\n    containerOptions.value = res.data || [];\n};\n\nfunction isBackup() {\n    return (\n        form.type === 'app' ||\n        form.type === 'website' ||\n        form.type === 'database' ||\n        form.type === 'directory' ||\n        form.type === 'snapshot' ||\n        form.type === 'log'\n    );\n}\n\nfunction hasExclusionRules() {\n    return (\n        form.type === 'app' ||\n        form.type === 'website' ||\n        form.type === 'snapshot' ||\n        (form.type === 'directory' && form.isDir)\n    );\n}\n\nfunction hasIgnore() {\n    return form.type === 'app' || form.type === 'website' || form.type === 'database';\n}\n\nfunction hasScript() {\n    return form.type === 'shell';\n}\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    let specs = [];\n    if (form.type === 'curl' && (form.urlItems.length === 0 || !verifyUrlItems())) {\n        MsgError(i18n.global.t('cronjob.urlHelper'));\n        return;\n    }\n    if (!form.specCustom) {\n        for (const item of form.specObjs) {\n            const itemSpec = transObjToSpec(item.specType, item.week, item.day, item.hour, item.minute, item.second);\n            if (itemSpec === '') {\n                MsgError(i18n.global.t('cronjob.cronSpecHelper'));\n                return;\n            }\n            specs.push(itemSpec);\n        }\n    } else {\n        specs = form.specs;\n    }\n    if (!form.isDir) {\n        let files = [];\n        for (const item of form.files) {\n            files.push(item.val);\n        }\n        form.sourceDir = files.join(',');\n    }\n    form.url = form.urlItems.join(',');\n    form.sourceAccountIDs = form.sourceAccountItems.join(',');\n    form.spec = specs.join('&&');\n    form.args = form.argItems.join(',');\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (!form.inContainer) {\n            form.containerName = '';\n        }\n        form.timeout = transferTimeToSecond(form.timeoutItem + form.timeoutUnit);\n        if (form.appIdList) {\n            form.appID = form.appIdList.join(',');\n        }\n        if (form.websiteList) {\n            form.website = form.websiteList.join(',');\n        }\n        if (form.dbNameList) {\n            form.dbName = form.dbNameList.join(',');\n        }\n\n        form.exclusionRules = form.ignoreFiles.join(',');\n        form.snapshotRule = { withImage: form.withImage, ignoreAppIDs: form.ignoreAppIDs };\n        form.alertCount = form.hasAlert ? form.alertCount : 0;\n        form.alertMethod = form.alertMethodItems.join(',');\n        form.alertTitle = form.hasAlert\n            ? i18n.global.t('cronjob.alertTitle', [i18n.global.t('cronjob.' + form.type), form.name])\n            : '';\n        if (!form) return;\n\n        if (isCreate.value) {\n            await addCronjob(form);\n        } else {\n            await editCronjob(form);\n        }\n\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        goBack();\n    });\n};\n\nconst toUpload = () => {\n    licenseRef.value.acceptParams();\n};\n\nonMounted(() => {\n    if (router.currentRoute.value.query.id) {\n        isCreate.value = false;\n        form.id = Number(router.currentRoute.value.query.id);\n    } else {\n        isCreate.value = true;\n    }\n    loadGroups();\n    search();\n    loadInstallDir();\n});\n</script>\n<style scoped lang=\"scss\">\n.specClass {\n    width: 17% !important;\n    margin-left: 20px;\n    .append {\n        margin-left: -10px;\n        width: 30px;\n    }\n}\n@media only screen and (max-width: 1000px) {\n    .specClass {\n        width: 100% !important;\n        margin-top: 20px;\n        margin-left: 0;\n        .append {\n            width: 43px;\n        }\n    }\n}\n.specTypeClass {\n    width: 22% !important;\n}\n@media only screen and (max-width: 1000px) {\n    .specTypeClass {\n        width: 100% !important;\n    }\n}\n.specCustom {\n    width: 80%;\n}\n@media only screen and (max-width: 1000px) {\n    .specCustom {\n        width: 100% !important;\n    }\n}\n.selectClass {\n    width: 100%;\n    padding-left: 0px;\n}\n.tagClass {\n    float: right;\n    margin-right: 10px;\n    font-size: 12px;\n    margin-top: 5px;\n}\n.logText {\n    line-height: 22px;\n    font-size: 12px;\n    .link {\n        font-size: 12px !important;\n        margin-top: -3px;\n    }\n}\n.el-card {\n    border: 1px solid var(--el-border-color-light) !important;\n}\n.divider {\n    display: block;\n    height: 1px;\n    width: 100%;\n    margin: 3px 0;\n    border-top: 1px var(--el-border-color) var(--el-border-style);\n}\n\n.option-wrapper {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n}\n\n.domain-text {\n    max-width: 200px;\n\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n\n    display: inline-block;\n    vertical-align: middle;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/cronjob/cronjob/record/index.vue",
    "content": "<template>\n    <div v-if=\"recordShow\" v-loading=\"loading\">\n        <div class=\"app-status card-interval\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-popover\n                            v-if=\"dialogData.rowData.name.length >= 15\"\n                            placement=\"top-start\"\n                            trigger=\"hover\"\n                            width=\"250\"\n                            :content=\"$t('cronjob.' + dialogData.rowData.type) + ' - ' + dialogData.rowData.name\"\n                        >\n                            <template #reference>\n                                <el-tag style=\"float: left\" effect=\"dark\" type=\"success\">\n                                    {{ $t('cronjob.' + dialogData.rowData.type) }} -\n                                    {{ dialogData.rowData.name.substring(0, 12) }}...\n                                </el-tag>\n                            </template>\n                        </el-popover>\n                        <el-tag\n                            v-if=\"dialogData.rowData.name.length < 15\"\n                            class=\"float-left\"\n                            effect=\"dark\"\n                            type=\"success\"\n                        >\n                            {{ $t('cronjob.' + dialogData.rowData.type) }} - {{ dialogData.rowData.name }}\n                        </el-tag>\n\n                        <Status class=\"mt-0.5\" :status=\"dialogData.rowData.status\" />\n                    </div>\n                    <div class=\"mt-0.5\">\n                        <el-button\n                            type=\"primary\"\n                            :disabled=\"dialogData.rowData.status === 'Pending'\"\n                            @click=\"onHandle(dialogData.rowData)\"\n                            link\n                        >\n                            {{ $t('commons.button.handle') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" v-if=\"dialogData.rowData.status !== 'Pending'\" />\n                        <el-button\n                            type=\"primary\"\n                            v-if=\"dialogData.rowData.status === 'Enable'\"\n                            @click=\"onChangeStatus(dialogData.rowData.id, 'disable')\"\n                            link\n                        >\n                            {{ $t('commons.button.disable') }}\n                        </el-button>\n                        <el-button\n                            type=\"primary\"\n                            v-if=\"dialogData.rowData.status === 'Disable'\"\n                            @click=\"onChangeStatus(dialogData.rowData.id, 'enable')\"\n                            link\n                        >\n                            {{ $t('commons.button.enable') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button :disabled=\"!hasRecords\" type=\"primary\" @click=\"onClean\" link>\n                            {{ $t('commons.button.clean') }}\n                        </el-button>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n\n        <LayoutContent :title=\"$t('cronjob.record')\" :reload=\"true\">\n            <template #rightToolBar>\n                <el-date-picker\n                    class=\"mr-2.5\"\n                    @change=\"search(true)\"\n                    v-model=\"timeRangeLoad\"\n                    type=\"datetimerange\"\n                    range-separator=\"-\"\n                    :start-placeholder=\"$t('commons.search.timeStart')\"\n                    :end-placeholder=\"$t('commons.search.timeEnd')\"\n                    :shortcuts=\"shortcuts\"\n                ></el-date-picker>\n                <el-select @change=\"search(true)\" v-model=\"searchInfo.status\" class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.status') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"\" />\n                    <el-option :label=\"$t('commons.status.success')\" value=\"Success\" />\n                    <el-option :label=\"$t('commons.status.waiting')\" value=\"Waiting\" />\n                    <el-option :label=\"$t('commons.status.unexecuted')\" value=\"Unexecuted\" />\n                    <el-option :label=\"$t('commons.status.failed')\" value=\"Failed\" />\n                </el-select>\n                <TableRefresh @search=\"search(false)\" />\n            </template>\n            <template #main>\n                <div class=\"mainClass\">\n                    <el-row :gutter=\"20\" v-show=\"hasRecords\" class=\"mainRowClass row-box\">\n                        <el-col :span=\"7\">\n                            <el-card class=\"el-card\">\n                                <div class=\"infinite-list\" style=\"overflow: auto\">\n                                    <ComplexTable\n                                        style=\"cursor: pointer\"\n                                        :data=\"records\"\n                                        :show-header=\"false\"\n                                        @row-click=\"forDetail\"\n                                    >\n                                        <el-table-column min-width=\"230px\">\n                                            <template #default=\"{ row }\">\n                                                <span v-if=\"row.id === currentRecord.id\" class=\"select-sign\"></span>\n                                                <Status class=\"mr-2 ml-1 float-left w-20\" :status=\"row.status\" />\n                                                <div class=\"mt-0.5\">\n                                                    <span>\n                                                        {{ row.startTime }}\n                                                    </span>\n                                                </div>\n                                            </template>\n                                        </el-table-column>\n                                        <el-table-column min-width=\"30px\">\n                                            <template #default=\"{ row }\">\n                                                <el-tooltip :content=\"$t('cronjob.stop')\">\n                                                    <el-button\n                                                        v-if=\"\n                                                            dialogData.rowData.type === 'shell' &&\n                                                            row.status === 'Waiting'\n                                                        \"\n                                                        class=\"float-right\"\n                                                        link\n                                                        type=\"primary\"\n                                                        @click=\"onStop(row.id)\"\n                                                        icon=\"SwitchButton\"\n                                                    />\n                                                </el-tooltip>\n                                            </template>\n                                        </el-table-column>\n                                    </ComplexTable>\n                                </div>\n                                <div class=\"page-item\">\n                                    <el-pagination\n                                        :page-size=\"searchInfo.pageSize\"\n                                        :current-page=\"searchInfo.page\"\n                                        @current-change=\"handleCurrentChange\"\n                                        @size-change=\"handleSizeChange\"\n                                        :pager-count=\"5\"\n                                        :page-sizes=\"[5, 10, 20, 50, 100, 200, 500, 1000]\"\n                                        small\n                                        layout=\"total, sizes, prev, pager, next\"\n                                        :total=\"searchInfo.recordTotal\"\n                                    />\n                                </div>\n                            </el-card>\n                        </el-col>\n                        <el-col :span=\"17\">\n                            <el-card class=\"el-card\">\n                                <el-form label-position=\"top\">\n                                    <el-row type=\"flex\" justify=\"center\">\n                                        <el-form-item class=\"descriptionWide\">\n                                            <template #label>\n                                                <span class=\"status-label\">{{ $t('commons.search.timeStart') }}</span>\n                                            </template>\n                                            <span class=\"status-count\">\n                                                {{ dateFormat(0, 0, currentRecord?.startTime) }}\n                                            </span>\n                                        </el-form-item>\n                                        <el-form-item class=\"description\">\n                                            <template #label>\n                                                <span class=\"status-label\">{{ $t('commons.table.interval') }}</span>\n                                            </template>\n                                            <el-button\n                                                link\n                                                v-if=\"currentRecord?.status === 'Waiting' && !currentRecord?.interval\"\n                                                :loading=\"true\"\n                                            />\n                                            <span v-else>\n                                                <span class=\"status-count\" v-if=\"currentRecord?.interval! <= 1000\">\n                                                    {{ currentRecord?.interval === 0 ? '-' : currentRecord?.interval }}\n                                                    ms\n                                                </span>\n                                                <span class=\"status-count\" v-if=\"currentRecord?.interval! > 1000\">\n                                                    {{ currentRecord?.interval! / 1000 }} s\n                                                </span>\n                                            </span>\n                                        </el-form-item>\n                                        <el-form-item class=\"description\">\n                                            <template #label>\n                                                <span class=\"status-label\">{{ $t('commons.table.status') }}</span>\n                                            </template>\n                                            <Status :status=\"currentRecord?.status\" />\n                                            <el-tooltip :content=\"currentRecord?.message\">\n                                                <el-button\n                                                    class=\"mt-0.5\"\n                                                    type=\"danger\"\n                                                    v-if=\"\n                                                        currentRecord?.status === 'Failed' ||\n                                                        currentRecord?.status === 'Unexecuted'\n                                                    \"\n                                                    icon=\"Warning\"\n                                                    link\n                                                />\n                                            </el-tooltip>\n                                        </el-form-item>\n                                    </el-row>\n                                    <el-row v-if=\"currentRecord?.taskID && currentRecord?.taskID != ''\">\n                                        <LogFile\n                                            :defaultButton=\"true\"\n                                            class=\"w-full\"\n                                            :key=\"currentRecord?.taskID\"\n                                            @stop-reading=\"search(false)\"\n                                            :heightDiff=\"420\"\n                                            :config=\"{\n                                                type: 'task',\n                                                colorMode: 'task',\n                                                taskID: currentRecord?.taskID,\n                                                tail: true,\n                                            }\"\n                                        />\n                                    </el-row>\n                                </el-form>\n                            </el-card>\n                        </el-col>\n                    </el-row>\n                </div>\n                <div class=\"app-warn\" v-show=\"!hasRecords\">\n                    <div>\n                        <span>{{ $t('cronjob.noRecord') }}</span>\n                        <div>\n                            <img src=\"@/assets/images/no_app.svg\" />\n                        </div>\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <DialogPro v-model=\"open\" :title=\"$t('commons.button.clean')\" size=\"small\">\n            <el-form ref=\"deleteForm\" label-position=\"left\" v-loading=\"delLoading\">\n                <el-form-item>\n                    <el-checkbox v-model=\"cleanData\" :label=\"$t('cronjob.cleanData')\" />\n                    <el-checkbox v-if=\"cleanData\" v-model=\"cleanRemoteData\" :label=\"$t('cronjob.cleanRemoteData')\" />\n                    <span class=\"input-help\">\n                        {{ $t('cronjob.cleanDataHelper') }}\n                    </span>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"open = false\" :disabled=\"delLoading\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button type=\"primary\" @click=\"cleanRecord\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DialogPro>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Cronjob } from '@/api/interface/cronjob';\nimport { searchRecords, handleOnce, updateStatus, cleanRecords, stopCronjob } from '@/api/modules/cronjob';\nimport { dateFormat } from '@/utils/util';\nimport LogFile from '@/components/log/file/index.vue';\nimport i18n from '@/lang';\nimport { ElMessageBox } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { listDbItems } from '@/api/modules/database';\nimport { listAppInstalled } from '@/api/modules/app';\nimport { shortcuts } from '@/utils/shortcuts';\nimport { hasBackup } from '../helper';\n\nconst loading = ref();\nconst hasRecords = ref();\n\ninterface DialogProps {\n    rowData: Cronjob.CronjobInfo;\n}\nconst recordShow = ref(false);\nconst dialogData = ref();\nconst records = ref<Array<Cronjob.Record>>([]);\nconst currentRecord = ref<Cronjob.Record>();\n\nconst open = ref();\nconst delLoading = ref();\nconst cleanData = ref();\nconst cleanRemoteData = ref();\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    let itemSize = Number(localStorage.getItem(searchInfo.cacheSizeKey));\n    if (itemSize) {\n        searchInfo.pageSize = itemSize;\n    }\n\n    recordShow.value = true;\n    dialogData.value = params;\n    if (dialogData.value.rowData.type === 'database') {\n        const data = await listDbItems('mysql,mariadb,postgresql');\n        let itemDBs = data.data || [];\n        for (const item of itemDBs) {\n            if (item.id == dialogData.value.rowData.dbName) {\n                dialogData.value.rowData.dbName = item.database + ' [' + item.name + ']';\n                break;\n            }\n        }\n    }\n    if (dialogData.value.rowData.type === 'app') {\n        const res = await listAppInstalled();\n        let itemApps = res.data || [];\n        for (const item of itemApps) {\n            if (item.id == dialogData.value.rowData.appID) {\n                dialogData.value.rowData.appID = item.key + ' [' + item.name + ']';\n                break;\n            }\n        }\n    }\n    search(true);\n};\n\nconst handleSizeChange = (val: number) => {\n    searchInfo.pageSize = val;\n    localStorage.setItem(searchInfo.cacheSizeKey, val + '');\n    search(true);\n};\nconst handleCurrentChange = (val: number) => {\n    searchInfo.page = val;\n    search(false);\n};\n\nconst timeRangeLoad = ref<[Date, Date]>([\n    new Date(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7).setHours(0, 0, 0, 0)),\n    new Date(new Date().setHours(23, 59, 59, 999)),\n]);\nconst searchInfo = reactive({\n    cacheSizeKey: 'cronjob-record-page-size',\n    page: 1,\n    pageSize: 10,\n    recordTotal: 0,\n    cronjobID: 0,\n    startTime: new Date(),\n    endTime: new Date(),\n    status: '',\n});\n\nconst onHandle = async (row: Cronjob.CronjobInfo) => {\n    loading.value = true;\n    await handleOnce(row.id)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search(true);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onChangeStatus = async (id: number, status: string) => {\n    ElMessageBox.confirm(i18n.global.t('cronjob.' + status + 'Msg'), i18n.global.t('cronjob.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        let itemStatus = status === 'enable' ? 'Enable' : 'Disable';\n        await updateStatus({ id: id, status: itemStatus });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        dialogData.value.rowData.status = itemStatus;\n    });\n};\n\nconst search = async (changeToLatest: boolean) => {\n    if (timeRangeLoad.value && timeRangeLoad.value.length === 2) {\n        searchInfo.startTime = timeRangeLoad.value[0];\n        searchInfo.endTime = timeRangeLoad.value[1];\n    } else {\n        searchInfo.startTime = new Date(new Date().setHours(0, 0, 0, 0));\n        searchInfo.endTime = new Date();\n    }\n    let params = {\n        page: searchInfo.page,\n        pageSize: searchInfo.pageSize,\n        cronjobID: dialogData.value.rowData!.id,\n        startTime: searchInfo.startTime,\n        endTime: searchInfo.endTime,\n        status: searchInfo.status,\n    };\n    const res = await searchRecords(params);\n    records.value = res.data.items;\n    searchInfo.recordTotal = res.data.total;\n    hasRecords.value = searchInfo.recordTotal !== 0;\n    if (!hasRecords.value) {\n        return;\n    }\n    if (changeToLatest || !currentRecord.value) {\n        currentRecord.value = records.value[0];\n        return;\n    }\n    for (const item of records.value) {\n        if (item.id === currentRecord.value.id) {\n            currentRecord.value = item;\n            break;\n        }\n    }\n};\n\nconst forDetail = (row: Cronjob.Record) => {\n    currentRecord.value = row;\n};\n\nconst onClean = async () => {\n    if (!hasBackup(dialogData.value.rowData.type)) {\n        ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('commons.button.clean'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'warning',\n        }).then(async () => {\n            await cleanRecords(dialogData.value.rowData.id, cleanData.value, cleanRemoteData.value)\n                .then(() => {\n                    delLoading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    search(true);\n                })\n                .catch(() => {\n                    delLoading.value = false;\n                });\n        });\n    } else {\n        open.value = true;\n    }\n};\n\nconst onStop = async (id: number) => {\n    ElMessageBox.confirm(i18n.global.t('cronjob.stopHelper'), i18n.global.t('cronjob.stop'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'warning',\n    }).then(async () => {\n        await stopCronjob(id);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\n\nconst cleanRecord = async () => {\n    delLoading.value = true;\n    await cleanRecords(dialogData.value.rowData.id, cleanData.value, cleanRemoteData.value)\n        .then(() => {\n            delLoading.value = false;\n            open.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search(true);\n        })\n        .catch(() => {\n            delLoading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.infinite-list {\n    height: calc(100vh - 320px);\n    .select-sign {\n        &::before {\n            float: left;\n            margin-left: -3px;\n            position: relative;\n            width: 3px;\n            height: 24px;\n            content: '';\n            background: $primary-color;\n            border-radius: 20px;\n        }\n    }\n    .el-tag {\n        margin-left: 20px;\n        margin-right: 20px;\n    }\n}\n\n.descriptionWide {\n    width: 40%;\n}\n.description {\n    width: 30%;\n}\n.page-item {\n    margin-top: 10px;\n    font-size: 12px;\n    float: right;\n}\n\n@media only screen and (max-width: 1400px) {\n    .mainClass {\n        overflow: auto;\n    }\n    .mainRowClass {\n        min-width: 1200px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/cronjob/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.cronjob'),\n        path: '/cronjobs/cronjob',\n    },\n    {\n        label: i18n.global.t('cronjob.library.library'),\n        path: '/cronjobs/library',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/cronjob/library/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('logs.login')\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-dropdown @command=\"handleSyncOp\" class=\"mr-2.5\">\n                    <el-button type=\"primary\" plain>\n                        {{ $t('commons.button.sync') }}\n                        <el-icon><arrow-down /></el-icon>\n                    </el-button>\n                    <template #dropdown>\n                        <el-dropdown-menu>\n                            <el-dropdown-item command=\"sync\">\n                                {{ $t('cronjob.library.syncNow') }}\n                            </el-dropdown-item>\n                            <el-dropdown-item v-if=\"scriptSync === 'Disable'\" command=\"turnOnSync\">\n                                {{ $t('cronjob.library.turnOnSync') }}\n                            </el-dropdown-item>\n                            <el-dropdown-item v-if=\"scriptSync === 'Enable'\" command=\"turnOffSync\">\n                                {{ $t('cronjob.library.turnOffSync') }}\n                            </el-dropdown-item>\n                        </el-dropdown-menu>\n                    </template>\n                </el-dropdown>\n\n                <el-button type=\"primary\" plain @click=\"onOpenGroupDialog()\">\n                    {{ $t('commons.table.group') }}\n                </el-button>\n                <el-button plain :disabled=\"selects.length === 0\" @click=\"onDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"group\" @change=\"search()\" clearable class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.group') }}</template>\n                    <div v-for=\"item in groupOptions\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchInfo\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"script-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    v-model:selects=\"selects\"\n                    :pagination-config=\"paginationConfig\"\n                    :data=\"data\"\n                    @search=\"search\"\n                    :heightDiff=\"300\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column :label=\"$t('commons.table.name')\" show-overflow-tooltip prop=\"name\" min-width=\"60\">\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"showScript(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('cronjob.library.isInteractive')\" prop=\"isInteractive\" min-width=\"60\">\n                        <template #default=\"{ row }\">\n                            <div class=\"-mb-1\">\n                                <el-icon v-if=\"row.isInteractive\"><Check /></el-icon>\n                                <el-icon v-else><Minus /></el-icon>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.group')\" min-width=\"120\" prop=\"group\">\n                        <template #default=\"{ row }\">\n                            <el-button class=\"mr-3\" size=\"small\" v-if=\"row.isSystem\">{{ $t('menu.system') }}</el-button>\n                            <span v-if=\"row.groupBelong\">\n                                <el-button size=\"small\" v-for=\"(item, index) in row.groupBelong\" :key=\"index\">\n                                    <span v-if=\"item === 'Default'\">\n                                        {{ $t('commons.table.default') }}\n                                    </span>\n                                    <span v-else>{{ item }}</span>\n                                </el-button>\n                            </span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        min-width=\"120\"\n                        :label=\"$t('commons.table.description')\"\n                        show-overflow-tooltip\n                        prop=\"description\"\n                    />\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        width=\"300px\"\n                        :buttons=\"buttons\"\n                        :ellipsis=\"10\"\n                        :label=\"$t('commons.table.operate')\"\n                        min-width=\"mobile ? 'auto' : 200\"\n                        :fixed=\"mobile ? false : 'right'\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\"></OpDialog>\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <GroupDialog @search=\"loadGroupOptions\" ref=\"dialogGroupRef\" />\n        <CodemirrorDrawer ref=\"myDetail\" />\n        <TerminalDialog ref=\"runRef\" />\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"search\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { dateFormat, deepCopy, getCurrentDateFormatted, newUUID } from '@/utils/util';\nimport GroupDialog from '@/components/group/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport OperateDialog from '@/views/cronjob/library/operate/index.vue';\nimport TerminalDialog from '@/views/cronjob/library/run/index.vue';\nimport { deleteScript, searchScript, syncScript } from '@/api/modules/cronjob';\nimport { onMounted, reactive, ref } from 'vue';\nimport { Cronjob } from '@/api/interface/cronjob';\nimport i18n from '@/lang';\nimport { GlobalStore } from '@/store';\nimport { getGroupList } from '@/api/modules/group';\nimport CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { getSettingBy, updateSetting } from '@/api/modules/setting';\n\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst myDetail = ref();\n\nconst loading = ref();\nconst selects = ref<any>([]);\nconst opRef = ref();\n\nconst runRef = ref();\nconst taskLogRef = ref();\nconst scriptSync = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'script-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('script-page-size')) || 20,\n    total: 0,\n});\nconst searchInfo = ref<string>('');\nconst group = ref<string>('');\nconst groupOptions = ref();\n\nconst dialogGroupRef = ref();\nconst onOpenGroupDialog = () => {\n    dialogGroupRef.value!.acceptParams({ type: 'script' });\n};\n\nconst dialogRef = ref();\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Cronjob.ScriptOperate> = {\n        name: '',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst showScript = async (row: any) => {\n    let param = {\n        header: i18n.global.t('commons.button.view') + ' - ' + row.name,\n        detailInfo: row.script,\n        mode: 'shell',\n    };\n    myDetail.value!.acceptParams(param);\n};\n\nconst onDelete = async (row: Cronjob.ScriptInfo | null) => {\n    let names = [];\n    let ids = [];\n    if (row) {\n        ids = [row.id];\n        names = [row.name];\n    } else {\n        for (const item of selects.value) {\n            names.push(item.name);\n            ids.push(item.id);\n        }\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('cronjob.library.script'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteScript,\n        params: ids,\n    });\n};\n\nconst onSync = async () => {\n    ElMessageBox.confirm(i18n.global.t('cronjob.library.syncHelper'), i18n.global.t('cronjob.library.syncNow'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        let taskID = newUUID();\n        await syncScript(taskID)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                openTaskLog(taskID);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID, true, 'local');\n};\n\nconst loadSyncStatus = async () => {\n    const res = await getSettingBy('ScriptSync');\n    scriptSync.value = res.data;\n};\nconst handleSyncOp = async (command: string) => {\n    let val = 'Enable';\n    switch (command) {\n        case 'sync':\n            onSync();\n            return;\n        case 'turnOnSync':\n            val = 'Enable';\n            break;\n        case 'turnOffSync':\n            val = 'Disable';\n            break;\n        default:\n            return;\n    }\n    ElMessageBox.confirm(\n        i18n.global.t('cronjob.library.' + command + 'Helper'),\n        i18n.global.t('cronjob.library.' + command),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(async () => {\n        loading.value = true;\n        await updateSetting({ key: 'ScriptSync', value: val })\n            .then(() => {\n                loadSyncStatus();\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst search = async () => {\n    let params = {\n        info: searchInfo.value,\n        groupID: Number(group.value),\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchScript(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items;\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadGroupOptions = async () => {\n    const res = await getGroupList('script');\n    groupOptions.value = res.data || [];\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.handle'),\n        click: (row: Cronjob.ScriptInfo) => {\n            ElMessageBox.confirm(\n                i18n.global.t('cronjob.library.handleHelper', [\n                    globalStore.currentNode === 'local' ? globalStore.getMasterAlias() : globalStore.currentNode,\n                    row.name,\n                ]),\n                i18n.global.t('commons.button.handle'),\n                {\n                    confirmButtonText: i18n.global.t('commons.button.confirm'),\n                    cancelButtonText: i18n.global.t('commons.button.cancel'),\n                    type: 'info',\n                },\n            ).then(() => {\n                runRef.value!.acceptParams({ scriptID: row.id, scriptName: row.name });\n            });\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.clone'),\n        disabled: (row: any) => {\n            return !row.isSystem;\n        },\n        click: (row: Cronjob.ScriptInfo) => {\n            let item = deepCopy(row) as Cronjob.ScriptInfo;\n            item.id = 0;\n            item.name += '-' + getCurrentDateFormatted();\n            item.groupList = row.groupList || [];\n            item.groupBelong = row.groupBelong || [];\n            onOpenDialog('clone', item);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        disabled: (row: any) => {\n            return row.isSystem;\n        },\n        click: (row: Cronjob.ScriptInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        disabled: (row: any) => {\n            return row.isSystem;\n        },\n        click: (row: Cronjob.ScriptInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n    loadSyncStatus();\n    loadGroupOptions();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/cronjob/library/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"title\"\n        @close=\"handleClose\"\n        :resource=\"dialogData.title !== 'edit' ? '' : dialogData.rowData?.name\"\n        size=\"large\"\n        :autoClose=\"false\"\n        :fullScreen=\"true\"\n    >\n        <el-form ref=\"formRef\" v-loading=\"loading\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-tag v-if=\"dialogData.title === 'edit'\">{{ dialogData.rowData!.name }}</el-tag>\n                <el-input v-else v-model=\"dialogData.rowData!.name\" />\n            </el-form-item>\n            <el-form-item prop=\"isInteractive\">\n                <el-checkbox v-model=\"dialogData.rowData!.isInteractive\">\n                    {{ $t('cronjob.library.interactive') }}\n                </el-checkbox>\n                <span class=\"input-help\">{{ $t('cronjob.library.interactiveHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.group')\" prop=\"groupList\">\n                <el-select filterable v-model=\"dialogData.rowData!.groupList\" multiple>\n                    <div v-for=\"item in groupOptions\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n                <span class=\"input-help\">{{ $t('cronjob.library.groupHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('cronjob.shellContent')\" prop=\"script\" class=\"mt-5\">\n                <CodemirrorPro\n                    v-model=\"dialogData.rowData!.script\"\n                    placeholder=\"#Define or paste the content of your script file here\"\n                    mode=\"javascript\"\n                    :heightDiff=\"400\"\n                />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable v-model=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { Cronjob } from '@/api/interface/cronjob';\nimport { MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport { addScript, editScript } from '@/api/modules/cronjob';\nimport { getGroupList } from '@/api/modules/group';\n\ninterface DialogProps {\n    title: string;\n    rowData?: Cronjob.ScriptInfo;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst loading = ref();\nconst groupOptions = ref();\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title);\n    loadGroupOptions();\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    name: [Rules.requiredInput],\n    script: [Rules.requiredInput],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        if (dialogData.value.rowData.groupList?.length !== 0) {\n            dialogData.value.rowData.groups = dialogData.value.rowData.groupList?.join(',');\n        }\n        if (dialogData.value.title === 'create' || dialogData.value.title === 'clone') {\n            await addScript(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n            return;\n        }\n\n        await editScript(dialogData.value.rowData)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst loadGroupOptions = async () => {\n    const res = await getGroupList('script');\n    groupOptions.value = res.data || [];\n    if (dialogData.value.title !== 'create') {\n        return;\n    }\n    for (const group of groupOptions.value) {\n        if (group.isDefault) {\n            dialogData.value.rowData.groupList = [group.id];\n            break;\n        }\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/cronjob/library/run/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"terminalVisible\"\n        :header=\"$t('menu.terminal')\"\n        @close=\"handleClose\"\n        :resource=\"scriptName\"\n        size=\"large\"\n        :autoClose=\"false\"\n        :fullScreen=\"true\"\n    >\n        <template #content>\n            <Terminal style=\"height: calc(100vh - 175px); margin-top: 18px\" ref=\"terminalRef\"></Terminal>\n        </template>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"onClose()\">{{ $t('commons.button.disConn') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, nextTick } from 'vue';\nimport Terminal from '@/components/terminal/index.vue';\nimport { GlobalStore } from '@/store';\n\nconst globalStore = GlobalStore();\nconst terminalVisible = ref(false);\nconst terminalRef = ref<InstanceType<typeof Terminal> | null>(null);\nconst scriptID = ref();\nconst scriptName = ref();\n\ninterface DialogProps {\n    scriptID: number;\n    scriptName: string;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    terminalVisible.value = true;\n    scriptID.value = params.scriptID;\n    scriptName.value = params.scriptName;\n    initTerm();\n};\n\nconst initTerm = async () => {\n    await nextTick();\n    terminalRef.value!.acceptParams({\n        endpoint: '/api/v2/core/script/run',\n        args: `script_id=${scriptID.value}&current_node=${globalStore.currentNode}`,\n        error: '',\n        initCmd: '',\n    });\n};\n\nconst onClose = () => {\n    terminalRef.value?.onClose();\n    terminalVisible.value = false;\n};\n\nfunction handleClose() {\n    onClose();\n    terminalVisible.value = false;\n}\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nconst buttons = [\n    {\n        label: 'MySQL',\n        path: '/databases/mysql',\n    },\n    {\n        label: 'PostgreSQL',\n        path: '/databases/postgresql',\n    },\n    {\n        label: 'Redis',\n        path: '/databases/redis',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/bind/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro\n            v-model=\"bindVisible\"\n            :header=\"$t('database.userBind')\"\n            :resource=\"form.mysqlName\"\n            @close=\"handleClose\"\n            size=\"small\"\n        >\n            <el-form v-loading=\"loading\" ref=\"changeFormRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n                <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                    <el-input v-model=\"form.username\"></el-input>\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                    <el-input type=\"password\" clearable show-password v-model=\"form.password\"></el-input>\n                    <span class=\"input-help\">{{ $t('commons.rule.illegalChar') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('database.permission')\" prop=\"permission\">\n                    <el-select v-model=\"form.permission\">\n                        <el-option value=\"%\" :label=\"$t('database.permissionAll')\" />\n                        <el-option\n                            v-if=\"form.from !== 'local'\"\n                            value=\"localhost\"\n                            :label=\"$t('terminal.localhost') + '(localhost)'\"\n                        />\n                        <el-option value=\"ip\" :label=\"$t('database.permissionForIP')\" />\n                    </el-select>\n                    <span v-if=\"form.from !== 'local'\" class=\"input-help\">\n                        {{ $t('database.localhostHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item v-if=\"form.permission === 'ip'\" prop=\"permissionIPs\">\n                    <el-input clearable :rows=\"3\" type=\"textarea\" v-model=\"form.permissionIPs\" />\n                    <span class=\"input-help\">{{ $t('database.remoteHelper') }}</span>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button :disabled=\"loading\" @click=\"bindVisible = false\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(changeFormRef)\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DrawerPro>\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmit\"></ConfirmDialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { bindUser } from '@/api/modules/database';\nimport { Rules } from '@/global/form-rules';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst bindVisible = ref(false);\ntype FormInstance = InstanceType<typeof ElForm>;\nconst changeFormRef = ref<FormInstance>();\nconst form = reactive({\n    from: '',\n    database: '',\n    mysqlName: '',\n    username: '',\n    password: '',\n    permission: '',\n    permissionIPs: '',\n});\nconst confirmDialogRef = ref();\n\nconst rules = reactive({\n    username: [Rules.requiredInput, Rules.name],\n    password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n    permission: [Rules.requiredSelect],\n    permissionIPs: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n});\n\ninterface DialogProps {\n    from: string;\n    database: string;\n    mysqlName: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    form.database = params.database;\n    form.mysqlName = params.mysqlName;\n    form.username = '';\n    form.password = '';\n    form.permission = '%';\n    form.from = params.from;\n    bindVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    bindVisible.value = false;\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let param = {\n            database: form.database,\n            db: form.mysqlName,\n            username: form.username,\n            password: form.password,\n            permission: form.permission === 'ip' ? form.permissionIPs : form.permission,\n        };\n        loading.value = true;\n        await bindUser(param)\n            .then(() => {\n                loading.value = false;\n                emit('search');\n                bindVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/check/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"large\">\n        <el-row>\n            <el-col :span=\"20\" :offset=\"2\" v-if=\"open\">\n                <el-alert\n                    type=\"error\"\n                    :title=\"$t('app.deleteHelper', [$t('menu.database')])\"\n                    center\n                    show-icon\n                    :closable=\"false\"\n                />\n                <br />\n                <el-descriptions :column=\"1\" border>\n                    <el-descriptions-item\n                        v-for=\"(item, key) in installData\"\n                        :key=\"key\"\n                        label-class-name=\"check-label\"\n                        class-name=\"check-content\"\n                        min-width=\"60px\"\n                    >\n                        <template #label>\n                            <a href=\"javascript:void(0);\" class=\"check-label-a\" @click=\"toPage(item.type)\">\n                                {{ $t('menu.' + item.type) }}\n                            </a>\n                        </template>\n                        <span class=\"resources\">\n                            {{ item.name }}\n                        </span>\n                    </el-descriptions-item>\n                </el-descriptions>\n            </el-col>\n        </el-row>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { Database } from '@/api/interface/database';\nimport { routerToName } from '@/utils/router';\nimport { ref } from 'vue';\n\ninterface InstallProps {\n    items: Array<Database.DBResource>;\n}\nconst installData = ref();\nlet open = ref(false);\n\nconst acceptParams = (props: InstallProps) => {\n    installData.value = props.items;\n    open.value = true;\n};\n\nconst toPage = (key: string) => {\n    if (key === 'app') {\n        routerToName('App');\n    }\n    if (key === 'website') {\n        routerToName('Website');\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/conn/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"dialogVisible\" :header=\"$t('database.databaseConnInfo')\" @close=\"handleClose\" size=\"small\">\n        <el-form @submit.prevent v-loading=\"loading\" ref=\"formRef\" :rules=\"rules\" :model=\"form\" label-position=\"top\">\n            <el-form-item v-if=\"form.from === 'local'\">\n                <template #label>\n                    <div class=\"flex items-center justify-between\">\n                        <span>{{ $t('database.containerConn') }}</span>\n                        <el-button link @click=\"copyConnURL(true)\" icon=\"DocumentCopy\">\n                            {{ $t('database.copyConnURL') }}\n                        </el-button>\n                    </div>\n                </template>\n                <el-card class=\"mini-border-card\">\n                    <el-descriptions :column=\"1\">\n                        <el-descriptions-item :label=\"$t('database.connAddress')\">\n                            <el-tooltip\n                                v-if=\"loadMysqlInfo(true).length > 48\"\n                                :content=\"loadMysqlInfo(true)\"\n                                placement=\"top\"\n                            >\n                                {{ loadMysqlInfo(true).substring(0, 48) }}...\n                            </el-tooltip>\n                            <span else>\n                                {{ loadMysqlInfo(true) }}\n                            </span>\n                            <CopyButton :content=\"loadMysqlInfo(true)\" />\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.port')\">\n                            3306\n                            <CopyButton content=\"3306\" />\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </el-card>\n                <span class=\"input-help\">\n                    {{ $t('database.containerConnHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <template #label>\n                    <div class=\"flex items-center justify-between\">\n                        <span>{{ $t('database.remoteConn') }}</span>\n                        <el-button link @click=\"copyConnURL(false)\" icon=\"DocumentCopy\">\n                            {{ $t('database.copyConnURL') }}\n                        </el-button>\n                    </div>\n                </template>\n                <el-card class=\"mini-border-card\">\n                    <el-descriptions :column=\"1\">\n                        <el-descriptions-item :label=\"$t('database.connAddress')\">\n                            <el-tooltip\n                                v-if=\"loadMysqlInfo(false).length > 48\"\n                                :content=\"loadMysqlInfo(false)\"\n                                placement=\"top\"\n                            >\n                                {{ loadMysqlInfo(false).substring(0, 48) }}...\n                            </el-tooltip>\n                            <span else>\n                                {{ loadMysqlInfo(false) }}\n                            </span>\n                            <CopyButton :content=\"loadMysqlInfo(false)\" />\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.port')\">\n                            {{ form.port }}\n                            <CopyButton :content=\"form.port + ''\" />\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </el-card>\n                <span v-if=\"form.from === 'local'\" class=\"input-help\">\n                    {{ $t('database.remoteConnHelper2') }}\n                </span>\n            </el-form-item>\n            <el-divider border-style=\"dashed\" />\n\n            <div v-if=\"form.from === 'local'\">\n                <el-form-item :label=\"$t('database.remoteAccess')\" prop=\"privilege\">\n                    <el-switch v-model=\"form.privilege\" :disabled=\"form.status !== 'Running'\" @change=\"onSaveAccess\" />\n                    <span class=\"input-help\">{{ $t('database.remoteConnHelper') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('database.rootPassword')\" prop=\"password\">\n                    <el-input\n                        style=\"width: calc(100% - 205px)\"\n                        type=\"password\"\n                        show-password\n                        clearable\n                        v-model=\"form.password\"\n                    />\n                    <el-button-group>\n                        <CopyButton class=\"copy_button\" :isIcon=\"false\" :content=\"form.password\" />\n                        <el-button @click=\"random\">\n                            {{ $t('commons.button.random') }}\n                        </el-button>\n                    </el-button-group>\n                    <span class=\"input-help\">{{ $t('commons.rule.illegalChar') }}</span>\n                </el-form-item>\n            </div>\n            <div v-if=\"form.from !== 'local'\">\n                <el-form-item :label=\"$t('commons.login.username')\">\n                    <el-tag>{{ form.username }}</el-tag>\n                    <CopyButton :content=\"form.username\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.password')\">\n                    <el-tag>{{ form.password }}</el-tag>\n                    <CopyButton :content=\"form.password\" />\n                </el-form-item>\n            </div>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"dialogVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading || form.status !== 'Running'\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { getDatabase, loadRemoteAccess, updateMysqlAccess, updateMysqlPassword } from '@/api/modules/database';\nimport { getAppConnInfo } from '@/api/modules/app';\nimport { MsgSuccess } from '@/utils/message';\nimport { getRandomStr, copyText } from '@/utils/util';\nimport { getAgentSettingInfo } from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\n\nconst dialogVisible = ref(false);\nconst form = reactive({\n    status: '',\n    systemIP: '',\n    password: '',\n    serviceName: '',\n    containerName: '',\n    oldPrivilege: false,\n    privilege: false,\n    port: 0,\n\n    from: '',\n    type: '',\n    database: '',\n    username: '',\n    remoteIP: '',\n});\nconst rules = reactive({\n    password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\ninterface DialogProps {\n    from: string;\n    type: string;\n    database: string;\n}\n\nconst acceptParams = (param: DialogProps): void => {\n    form.password = '';\n    form.from = param.from;\n    form.type = param.type;\n    form.database = param.database;\n    loadAccess();\n    loadPassword();\n    dialogVisible.value = true;\n};\n\nfunction loadMysqlInfo(isContainer: boolean) {\n    if (isContainer) {\n        return form.from === 'local' ? form.containerName : form.systemIP;\n    } else {\n        return form.from === 'local' ? form.systemIP : form.remoteIP;\n    }\n}\n\nconst copyConnURL = (isContainer: boolean) => {\n    const host = loadMysqlInfo(isContainer);\n    const port = isContainer && form.from === 'local' ? 3306 : form.port;\n    const user = form.from === 'local' ? 'root' : form.username;\n    const encodedPassword = encodeURIComponent(form.password);\n    copyText(`mysql://${user}:${encodedPassword}@${host}:${port}`);\n};\n\nconst random = async () => {\n    form.password = getRandomStr(16);\n};\n\nconst handleClose = () => {\n    dialogVisible.value = false;\n};\n\nconst loadAccess = async () => {\n    if (form.from === 'local') {\n        const res = await loadRemoteAccess(form.type, form.database);\n        form.privilege = res.data;\n        form.oldPrivilege = res.data;\n    }\n};\n\nconst loadSystemIP = async () => {\n    const res = await getAgentSettingInfo();\n    form.systemIP = res.data.systemIP || globalStore.currentNodeAddr || i18n.global.t('database.localIP');\n};\n\nconst loadPassword = async () => {\n    if (form.from === 'local') {\n        const res = await getAppConnInfo(form.type, form.database);\n        form.status = res.data.status;\n        form.password = res.data.password || '';\n        form.port = res.data.port || 3306;\n        form.serviceName = res.data.serviceName || '';\n        form.containerName = res.data.containerName || '';\n        loadSystemIP();\n        return;\n    }\n    const res = await getDatabase(form.database);\n    form.password = res.data.password || '';\n    form.port = res.data.port || 3306;\n    form.username = res.data.username;\n    form.password = res.data.password;\n    form.remoteIP = res.data.address;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('database.changeConnHelper', [i18n.global.t('commons.login.password')]),\n            i18n.global.t('commons.msg.infoTitle'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n            },\n        ).then(async () => {\n            let param = {\n                id: 0,\n                from: form.from,\n                type: form.type,\n                database: form.database,\n                value: form.password,\n            };\n            loading.value = true;\n            await updateMysqlPassword(param)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    dialogVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst onSaveAccess = async () => {\n    ElMessageBox.confirm(\n        i18n.global.t('database.changeConnHelper', [i18n.global.t('database.remoteAccess')]),\n        i18n.global.t('commons.msg.infoTitle'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    )\n        .then(async () => {\n            let param = {\n                id: 0,\n                from: form.from,\n                type: form.type,\n                database: form.database,\n                value: form.privilege ? '%' : 'localhost',\n            };\n            loading.value = true;\n            await updateMysqlAccess(param)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    dialogVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            form.privilege = form.oldPrivilege;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.copy_button {\n    border-radius: 0px;\n    border-left-width: 0px;\n}\n:deep(.el-input__wrapper) {\n    border-top-right-radius: 0px;\n    border-bottom-right-radius: 0px;\n}\n:deep(.el-form-item__label) {\n    width: 100%;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"createVisible\" :header=\"$t('commons.button.create')\" @close=\"handleClose\" size=\"normal\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input clearable v-model.trim=\"form.name\" @input=\"form.username = form.name\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('database.format')\" prop=\"format\">\n                <el-select filterable v-model=\"form.format\" @change=\"loadCollations()\">\n                    <el-option\n                        v-for=\"item of formatOptions\"\n                        :key=\"item.format\"\n                        :label=\"item.format\"\n                        :value=\"item.format\"\n                    />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('database.collation')\" prop=\"collation\">\n                <el-select filterable v-model=\"form.collation\" clearable>\n                    <el-option v-for=\"item of collationOptions\" :key=\"item\" :label=\"item\" :value=\"item\" />\n                </el-select>\n                <span class=\"input-help\">{{ $t('database.collationHelper', [form.format]) }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                <el-input clearable v-model.trim=\"form.username\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                <el-input type=\"password\" clearable show-password v-model.trim=\"form.password\">\n                    <template #append>\n                        <el-button @click=\"random\">{{ $t('commons.button.random') }}</el-button>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('commons.rule.illegalChar') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('database.permission')\" prop=\"permission\">\n                <el-select v-model=\"form.permission\">\n                    <el-option value=\"%\" :label=\"$t('database.permissionAll')\" />\n                    <el-option\n                        v-if=\"form.from !== 'local'\"\n                        value=\"localhost\"\n                        :label=\"$t('terminal.localhost') + '(localhost)'\"\n                    />\n                    <el-option value=\"ip\" :label=\"$t('database.permissionForIP')\" />\n                </el-select>\n                <span v-if=\"form.from !== 'local'\" class=\"input-help\">\n                    {{ $t('database.localhostHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item v-if=\"form.permission === 'ip'\" prop=\"permissionIPs\">\n                <el-input clearable :rows=\"3\" type=\"textarea\" v-model=\"form.permissionIPs\" />\n                <span class=\"input-help\">{{ $t('database.remoteHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.type')\" prop=\"database\">\n                <el-tag>{{ form.database + ' [' + form.type + ']' }}</el-tag>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input type=\"textarea\" clearable v-model=\"form.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"createVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { addMysqlDB, loadFormatCollations } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\nimport { getRandomStr } from '@/utils/util';\n\nconst loading = ref();\nconst createVisible = ref(false);\nconst formatOptions = ref();\nconst collationOptions = ref();\nconst form = reactive({\n    name: '',\n    from: 'local',\n    type: '',\n    database: '',\n    format: '',\n    collation: '',\n    username: '',\n    password: '',\n    permission: '',\n    permissionIPs: '',\n    description: '',\n});\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.dbName],\n    format: [Rules.requiredSelect],\n    username: [Rules.requiredInput, Rules.name],\n    password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n    permission: [Rules.requiredSelect],\n    permissionIPs: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\ninterface DialogProps {\n    from: string;\n    type: string;\n    database: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    form.name = '';\n    form.from = params.from;\n    form.type = params.type;\n    form.database = params.database;\n    form.format = 'utf8mb4';\n    form.collation = '';\n    form.username = '';\n    form.permission = '%';\n    form.permissionIPs = '';\n    form.description = '';\n    random();\n    loadOptions();\n    createVisible.value = true;\n};\nconst handleClose = () => {\n    createVisible.value = false;\n};\n\nconst loadOptions = async () => {\n    const defaultOptions = [{ format: 'utf8mb4' }, { format: 'utf8mb3' }, { format: 'gbk' }, { format: 'big5' }];\n    await loadFormatCollations(form.database).then((res) => {\n        formatOptions.value = res.data || defaultOptions;\n        loadCollations();\n    });\n};\n\nconst loadCollations = async () => {\n    collationOptions.value = formatOptions.value?.find((item) => item.format === form.format)?.collations || [];\n};\n\nconst random = async () => {\n    form.password = getRandomStr(16);\n};\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (form.permission === 'ip') {\n            form.permission = form.permissionIPs;\n        }\n        loading.value = true;\n        await addMysqlDB(form)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                createVisible.value = false;\n            })\n            .catch(() => {\n                if (form.permission != '%') {\n                    form.permissionIPs = form.permission;\n                    form.permission = 'ip';\n                }\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.delete') + ' - ' + dbName\" size=\"small\">\n        <el-form ref=\"deleteForm\" v-loading=\"loading\" @submit.prevent>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.forceDelete\" :label=\"$t('app.forceDelete')\" />\n                <span class=\"input-help\">\n                    {{ $t('app.forceDeleteHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.deleteBackup\" :label=\"$t('app.deleteBackup')\" />\n                <span class=\"input-help\">\n                    {{ $t('database.deleteBackupHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <div>\n                    <span style=\"font-size: 12px\">{{ $t('database.delete') }}</span>\n                    <span style=\"font-size: 12px; color: red; font-weight: 500\">{{ dbName }}</span>\n                    <span style=\"font-size: 12px\">{{ $t('database.deleteHelper') }}</span>\n                </div>\n                <el-input v-model=\"deleteInfo\" :placeholder=\"dbName\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"deleteInfo != dbName || loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { deleteMysqlDB } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\n\nlet deleteReq = ref({\n    id: 0,\n    type: '',\n    database: '',\n    deleteBackup: false,\n    forceDelete: false,\n});\nlet open = ref(false);\nlet loading = ref(false);\nlet deleteInfo = ref('');\nlet dbName = ref('');\n\nconst deleteForm = ref<FormInstance>();\n\ninterface DialogProps {\n    id: number;\n    type: string;\n    name: string;\n    database: string;\n}\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst acceptParams = async (prop: DialogProps) => {\n    deleteReq.value = {\n        id: prop.id,\n        type: prop.type,\n        database: prop.database,\n        deleteBackup: false,\n        forceDelete: false,\n    };\n    dbName.value = prop.name;\n    deleteInfo.value = '';\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    deleteMysqlDB(deleteReq.value)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            open.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <div class=\"app-status mt-5\" v-if=\"currentDB?.from === 'remote'\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag class=\"float-left\" effect=\"dark\" type=\"success\">\n                            {{ currentDB?.type === 'mysql' ? 'Mysql' : 'MariaDB' }}\n                        </el-tag>\n                        <el-tag>{{ $t('app.version') }}: {{ currentDB?.version }}</el-tag>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n        <LayoutContent>\n            <template #app v-if=\"currentDB?.from === 'local'\">\n                <AppStatus\n                    :app-key=\"appKey\"\n                    :app-name=\"appName\"\n                    v-model:loading=\"loading\"\n                    v-model:mask-show=\"maskShow\"\n                    @setting=\"onSetting()\"\n                    @is-exist=\"checkExist\"\n                    ref=\"appStatusRef\"\n                ></AppStatus>\n            </template>\n            <template #leftToolBar>\n                <el-button\n                    v-if=\"currentDB && (currentDB.from !== 'local' || mysqlStatus === 'Running')\"\n                    type=\"primary\"\n                    @click=\"onOpenDialog()\"\n                >\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button v-if=\"currentDB\" @click=\"onChangeConn()\" type=\"primary\" plain>\n                    {{ $t('database.databaseConnInfo') }}\n                </el-button>\n                <el-button\n                    v-if=\"currentDB && (currentDB.from !== 'local' || mysqlStatus === 'Running')\"\n                    @click=\"loadDB\"\n                    type=\"primary\"\n                    plain\n                >\n                    {{ $t('database.loadFromRemote') }}\n                </el-button>\n                <el-button @click=\"goRemoteDB()\" type=\"primary\" plain>\n                    {{ $t('database.remoteDB') }}\n                </el-button>\n                <el-button @click=\"goTerminal()\" :disabled=\"currentDB?.from !== 'local'\" type=\"primary\" plain>\n                    {{ $t('menu.terminal') }}\n                </el-button>\n                <el-dropdown>\n                    <el-button type=\"primary\" plain>\n                        {{ $t('database.manage') }}\n                        <el-icon class=\"el-icon--right\"><arrow-down /></el-icon>\n                    </el-button>\n                    <template #dropdown>\n                        <el-dropdown-menu>\n                            <el-dropdown-item icon=\"Position\" @click=\"goDashboard('phpMyAdmin')\">\n                                phpMyAdmin\n                            </el-dropdown-item>\n                            <el-dropdown-item icon=\"Position\" @click=\"goDashboard('Adminer')\" divided>\n                                Adminer\n                            </el-dropdown-item>\n                        </el-dropdown-menu>\n                    </template>\n                </el-dropdown>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"currentDBName\" @change=\"changeDatabase()\" class=\"p-w-200\" v-if=\"currentDB\">\n                    <template #prefix>{{ $t('commons.table.type') }}</template>\n                    <el-option-group :label=\"$t('commons.table.local')\">\n                        <div v-for=\"(item, index) in dbOptionsLocal\" :key=\"index\">\n                            <el-option v-if=\"item.from === 'local'\" :value=\"item.database\" class=\"optionClass\">\n                                <span v-if=\"item.database.length < 25\">{{ item.database }}</span>\n                                <el-tooltip v-else :content=\"item.database\" placement=\"top\">\n                                    <span>{{ item.database.substring(0, 25) }}...</span>\n                                </el-tooltip>\n                                <el-tag class=\"tagClass\">\n                                    {{ mysqlName(item.type) }}\n                                </el-tag>\n                            </el-option>\n                        </div>\n                        <el-button link type=\"primary\" class=\"jumpAdd\" @click=\"goRouter('app')\" icon=\"Position\">\n                            {{ $t('database.goInstall') }}\n                        </el-button>\n                    </el-option-group>\n                    <el-option-group :label=\"$t('database.remote')\">\n                        <div v-for=\"(item, index) in dbOptionsRemote\" :key=\"index\">\n                            <el-option v-if=\"item.from === 'remote'\" :value=\"item.database\" class=\"optionClass\">\n                                <span v-if=\"item.database.length < 25\">{{ item.database }}</span>\n                                <el-tooltip v-else :content=\"item.database\" placement=\"top\">\n                                    <span>{{ item.database.substring(0, 25) }}...</span>\n                                </el-tooltip>\n                                <el-tag class=\"tagClass\">\n                                    {{ mysqlName(item.type) }}\n                                </el-tag>\n                            </el-option>\n                        </div>\n                        <el-button link type=\"primary\" class=\"jumpAdd\" @click=\"goRouter('remote')\" icon=\"Position\">\n                            {{ $t('database.createRemoteDB') }}\n                        </el-button>\n                    </el-option-group>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    v-if=\"currentDB\"\n                    :pagination-config=\"paginationConfig\"\n                    :class=\"{ mask: maskShow }\"\n                    @sort-change=\"search\"\n                    @search=\"search\"\n                    :data=\"data\"\n                    :heightDiff=\"370\"\n                >\n                    <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\" sortable min-width=\"90\">\n                        <template #default=\"{ row }\">\n                            <Tooltip v-if=\"!row.isDelete\" :islink=\"false\" :text=\"row.name\" />\n                            <div v-else>\n                                <span v-if=\"row.name.length < 15\">{{ row.name }}</span>\n                                <el-tooltip v-else :content=\"row.name\">{{ row.name.substring(0, 10) }}...</el-tooltip>\n                                <el-tag round type=\"info\" class=\"ml-1\" size=\"small\">\n                                    {{ $t('database.isDelete') }}\n                                </el-tag>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.login.username')\" show-overflow-tooltip prop=\"username\">\n                        <template #default=\"{ row }\">\n                            <div class=\"flex items-center\" v-if=\"row.username\">\n                                <span>\n                                    {{ row.username }}\n                                </span>\n                            </div>\n                            <div v-else>\n                                <el-button\n                                    :disabled=\"row.isDelete\"\n                                    style=\"margin-left: -3px\"\n                                    type=\"primary\"\n                                    link\n                                    @click=\"onBind(row)\"\n                                >\n                                    {{ $t('database.userBind') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.login.password')\" prop=\"password\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.username === ''\">-</span>\n                            <div class=\"flex items-center flex-wrap\" v-if=\"row.password && row.username\">\n                                <div class=\"star-center\" v-if=\"!row.showPassword\">\n                                    <span>**********</span>\n                                </div>\n                                <div>\n                                    <span v-if=\"row.showPassword\">\n                                        {{ row.password }}\n                                    </span>\n                                </div>\n                                <el-button\n                                    v-if=\"!row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = true\"\n                                    icon=\"View\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <el-button\n                                    v-if=\"row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = false\"\n                                    icon=\"Hide\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <div>\n                                    <CopyButton :content=\"row.password\" />\n                                </div>\n                            </div>\n                            <div v-if=\"row.password === '' && row.username\">\n                                <el-button\n                                    :disabled=\"row.isDelete\"\n                                    style=\"margin-left: -3px\"\n                                    link\n                                    type=\"primary\"\n                                    @click=\"onChangePassword(row)\"\n                                >\n                                    {{ $t('database.passwordHelper') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.description')\" prop=\"description\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            <fu-input-rw-switch\n                                v-model=\"row.description\"\n                                @enter=\"onChange(row)\"\n                                @blur=\"onChange(row)\"\n                            />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 10\"\n                        :min-width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        fixed=\"right\"\n                        fix\n                    />\n                </ComplexTable>\n                <div v-if=\"isLoaded && dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0\" class=\"app-warn\">\n                    <div class=\"flex flex-col gap-2 items-center justify-center w-full sm:flex-row\">\n                        <span>{{ $t('app.checkInstalledWarn', [$t('database.noMysql')]) }}</span>\n                        <span @click=\"goRouter('app')\" class=\"flex items-center justify-center gap-0.5\">\n                            <el-icon><Position /></el-icon>\n                            {{ $t('database.goInstall') }}\n                        </span>\n                    </div>\n                    <div>\n                        <img src=\"@/assets/images/no_app.svg\" />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <el-card\n            v-if=\"mysqlStatus != 'Running' && currentDB && !loading && maskShow && currentDB?.from === 'local'\"\n            class=\"mask-prompt\"\n        >\n            <span>\n                {{ $t('commons.service.serviceNotStarted', [mysqlName(currentDB.type)]) }}\n            </span>\n        </el-card>\n\n        <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"small\">\n            <div class=\"flex justify-center items-center gap-2 flex-wrap\">\n                {{ $t('app.checkInstalledWarn', [dashboardName]) }}\n                <el-link icon=\"Position\" @click=\"getAppDetail\" type=\"primary\">\n                    {{ $t('database.goInstall') }}\n                </el-link>\n            </div>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <BindDialog ref=\"bindRef\" @search=\"search\" />\n        <PasswordDialog ref=\"passwordRef\" @search=\"search\" />\n        <RootPasswordDialog ref=\"connRef\" />\n        <UploadDialog ref=\"uploadRef\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <Backups ref=\"dialogBackupRef\" />\n        <AppResources ref=\"checkRef\"></AppResources>\n        <DeleteDialog ref=\"deleteRef\" @search=\"search\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <TerminalDialog ref=\"dialogTerminalRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport BindDialog from '@/views/database/mysql/bind/index.vue';\nimport OperateDialog from '@/views/database/mysql/create/index.vue';\nimport DeleteDialog from '@/views/database/mysql/delete/index.vue';\nimport PasswordDialog from '@/views/database/mysql/password/index.vue';\nimport RootPasswordDialog from '@/views/database/mysql/conn/index.vue';\nimport TerminalDialog from '@/components/terminal/database.vue';\nimport AppResources from '@/views/database/mysql/check/index.vue';\nimport AppStatus from '@/components/app-status/index.vue';\nimport Backups from '@/components/backup/index.vue';\nimport UploadDialog from '@/components/upload/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport Tooltip from '@/components/tooltip/index.vue';\nimport { dateFormat } from '@/utils/util';\nimport { ElMessageBox } from 'element-plus';\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport {\n    deleteCheckMysqlDB,\n    listDatabases,\n    loadDBFromRemote,\n    searchMysqlDBs,\n    updateMysqlDescription,\n} from '@/api/modules/database';\nimport i18n from '@/lang';\nimport { Database } from '@/api/interface/database';\nimport { App } from '@/api/interface/app';\nimport { getAppPort } from '@/api/modules/app';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nimport { routerToName, routerToNameWithParams, routerToNameWithQuery } from '@/utils/router';\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref(false);\nconst maskShow = ref(true);\n\nconst appKey = ref('mysql');\nconst appName = ref();\n\nconst isLoaded = ref(false);\nconst dbOptionsLocal = ref<Array<Database.DatabaseOption>>([]);\nconst dbOptionsRemote = ref<Array<Database.DatabaseOption>>([]);\nconst currentDB = ref<Database.DatabaseOption>();\nconst currentDBName = ref();\n\nconst bindRef = ref();\nconst checkRef = ref();\nconst deleteRef = ref();\nconst dialogTerminalRef = ref();\n\nconst phpadminPort = ref();\nconst adminerPort = ref();\nconst dashboardName = ref();\nconst dashboardKey = ref();\nconst open = ref(false);\n\nconst appStatusRef = ref();\n\nconst dialogPortJumpRef = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'mysql-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('mysql-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst mysqlContainer = ref();\nconst mysqlStatus = ref();\nconst mysqlVersion = ref();\n\nconst dialogRef = ref();\nconst onOpenDialog = async () => {\n    let params = {\n        from: currentDB.value.from,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst dialogBackupRef = ref();\n\nconst uploadRef = ref();\n\nconst connRef = ref();\nconst onChangeConn = async () => {\n    connRef.value!.acceptParams({\n        from: currentDB.value.from,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n    });\n};\n\nconst mysqlName = (appType: string) => {\n    if (appType === 'mysql' || appType === 'mysql-cluster') {\n        return 'MySQL';\n    } else {\n        return 'MariaDB';\n    }\n};\n\nconst goRemoteDB = async () => {\n    if (currentDB.value) {\n        globalStore.setCurrentDB(currentDB.value.database);\n    }\n    routerToName('MySQL-Remote');\n};\n\nconst goTerminal = () => {\n    dialogTerminalRef.value.acceptParams({ databaseType: currentDB.value.type, database: currentDB.value.database });\n};\n\nconst passwordRef = ref();\n\nconst onSetting = async () => {\n    if (currentDB.value) {\n        globalStore.setCurrentDB(currentDB.value.database);\n    }\n    routerToNameWithParams('MySQL-Setting', { type: currentDB.value.type, database: currentDB.value.database });\n};\n\nconst changeDatabase = async () => {\n    for (const item of dbOptionsLocal.value) {\n        if (item.database == currentDBName.value) {\n            currentDB.value = item;\n            globalStore.setCurrentDB(currentDB.value.database);\n            appKey.value = item.type;\n            appName.value = item.database;\n            search();\n            appStatusRef.value?.onCheck(appKey.value, appName.value);\n            return;\n        }\n    }\n    for (const item of dbOptionsRemote.value) {\n        if (item.database == currentDBName.value) {\n            maskShow.value = false;\n            currentDB.value = item;\n            globalStore.setCurrentDB(currentDB.value.database);\n            break;\n        }\n    }\n    search();\n};\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        info: searchName.value,\n        database: currentDB.value.database,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    const res = await searchMysqlDBs(params);\n    data.value = res.data.items || [];\n    paginationConfig.total = res.data.total;\n};\n\nconst loadDB = async () => {\n    ElMessageBox.confirm(i18n.global.t('database.loadFromRemoteHelper'), i18n.global.t('commons.msg.infoTitle'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        let params = {\n            from: currentDB.value.from,\n            type: currentDB.value.type,\n            database: currentDBName.value,\n        };\n        await loadDBFromRemote(params)\n            .then(() => {\n                loading.value = false;\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst goRouter = async (target: string) => {\n    if (target === 'app') {\n        routerToNameWithQuery('AppAll', { install: 'mysql' });\n        return;\n    }\n    routerToName('MySQL-Remote');\n};\n\nconst onChange = async (info: any) => {\n    await updateMysqlDescription({ id: info.id, description: info.description });\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst goDashboard = async (name: string) => {\n    if (name === 'phpMyAdmin') {\n        if (phpadminPort.value === 0) {\n            dashboardName.value = 'phpMyAdmin';\n            dashboardKey.value = 'phpmyadmin';\n            open.value = true;\n            return;\n        }\n        dialogPortJumpRef.value.acceptParams({ port: phpadminPort.value });\n        return;\n    }\n    if (adminerPort.value === 0) {\n        dashboardName.value = 'Adminer';\n        dashboardKey.value = 'adminer';\n        open.value = true;\n        return;\n    }\n    dialogPortJumpRef.value.acceptParams({ port: adminerPort.value });\n};\n\nconst getAppDetail = () => {\n    routerToNameWithQuery('AppAll', { install: dashboardKey.value });\n};\n\nconst loadPhpMyAdminPort = async () => {\n    const res = await getAppPort('phpmyadmin', '');\n    phpadminPort.value = res.data;\n};\n\nconst loadAdminerPort = async () => {\n    const res = await getAppPort('adminer', '');\n    adminerPort.value = res.data;\n};\n\nconst checkExist = (data: App.CheckInstalled) => {\n    mysqlStatus.value = data.status;\n    mysqlVersion.value = data.version;\n    mysqlContainer.value = data.containerName;\n};\n\nconst loadDBOptions = async () => {\n    try {\n        const res = await listDatabases('mysql,mariadb,mysql-cluster');\n        let datas = res.data || [];\n        dbOptionsLocal.value = [];\n        dbOptionsRemote.value = [];\n        currentDBName.value = globalStore.currentDB;\n        for (const item of datas) {\n            if (currentDBName.value && item.database === currentDBName.value) {\n                currentDB.value = item;\n                if (item.from === 'local') {\n                    appKey.value = item.type;\n                    appName.value = item.database;\n                }\n            }\n            if (item.from === 'local') {\n                dbOptionsLocal.value.push(item);\n            } else {\n                dbOptionsRemote.value.push(item);\n            }\n        }\n        if (currentDB.value) {\n            if (currentDB.value?.from === 'remote') {\n                maskShow.value = false;\n            }\n            search();\n            return;\n        }\n        if (dbOptionsLocal.value.length !== 0) {\n            currentDB.value = dbOptionsLocal.value[0];\n            currentDBName.value = dbOptionsLocal.value[0].database;\n            appKey.value = dbOptionsLocal.value[0].type;\n            appName.value = dbOptionsLocal.value[0].database;\n        }\n        if (!currentDB.value && dbOptionsRemote.value.length !== 0) {\n            currentDB.value = dbOptionsRemote.value[0];\n            currentDBName.value = dbOptionsRemote.value[0].database;\n        }\n        if (currentDB.value) {\n            search();\n        }\n        if (currentDB.value?.from === 'remote') {\n            maskShow.value = false;\n        }\n    } finally {\n        isLoaded.value = true;\n    }\n};\nconst onDelete = async (row: Database.MysqlDBInfo) => {\n    let param = {\n        id: row.id,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n    };\n    const res = await deleteCheckMysqlDB(param);\n    if (res.data && res.data.length > 0) {\n        checkRef.value.acceptParams({ items: res.data });\n    } else {\n        deleteRef.value.acceptParams({\n            id: row.id,\n            type: currentDB.value.type,\n            database: currentDBName.value,\n            name: row.name,\n        });\n    }\n};\n\nconst onBind = async (row: Database.MysqlDBInfo) => {\n    let param = {\n        database: currentDBName.value,\n        mysqlName: row.name,\n        from: row.from,\n    };\n    bindRef.value.acceptParams(param);\n};\n\nconst onChangePassword = async (row: Database.MysqlDBInfo) => {\n    let param = {\n        id: row.id,\n        from: row.from,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n        mysqlName: row.name,\n        operation: 'password',\n        username: row.username,\n        password: row.password,\n    };\n    passwordRef.value.acceptParams(param);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('database.changePassword'),\n        disabled: (row: Database.MysqlDBInfo) => {\n            return !row.username || row.isDelete;\n        },\n        click: (row: Database.MysqlDBInfo) => {\n            onChangePassword(row);\n        },\n    },\n    {\n        label: i18n.global.t('database.permission'),\n        disabled: (row: Database.MysqlDBInfo) => {\n            return !row.password || row.isDelete;\n        },\n        click: (row: Database.MysqlDBInfo) => {\n            let param = {\n                id: row.id,\n                from: row.from,\n                type: currentDB.value.type,\n                database: currentDBName.value,\n                mysqlName: row.name,\n                operation: 'privilege',\n                privilege: '',\n                privilegeIPs: '',\n                password: '',\n            };\n            if (row.permission === '%' || row.permission === 'localhost') {\n                param.privilege = row.permission;\n            } else {\n                param.privilegeIPs = row.permission;\n                param.privilege = 'ip';\n            }\n            passwordRef.value.acceptParams(param);\n        },\n    },\n    {\n        label: i18n.global.t('database.backupList'),\n        disabled: (row: Database.MysqlDBInfo) => {\n            return row.isDelete;\n        },\n        click: (row: Database.MysqlDBInfo) => {\n            let params = {\n                type: currentDB.value.type,\n                name: currentDBName.value,\n                detailName: row.name,\n            };\n            dialogBackupRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('database.loadBackup'),\n        disabled: (row: Database.MysqlDBInfo) => {\n            return row.isDelete;\n        },\n        click: (row: Database.MysqlDBInfo) => {\n            let params = {\n                type: currentDB.value.type,\n                name: currentDBName.value,\n                detailName: row.name,\n                remark: row.format,\n            };\n            uploadRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Database.MysqlDBInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    loadDBOptions();\n    loadPhpMyAdminPort();\n    loadAdminerPort();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.iconInTable {\n    margin-left: 5px;\n    margin-top: 3px;\n}\n.jumpAdd {\n    margin-top: 10px;\n    margin-left: 15px;\n    margin-bottom: 5px;\n    font-size: 12px;\n}\n.tagClass {\n    float: right;\n    font-size: 12px;\n    margin-top: 5px;\n}\n.optionClass {\n    min-width: 350px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/password/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro\n            v-model=\"changeVisible\"\n            :header=\"title\"\n            :resource=\"changeForm.mysqlName\"\n            @close=\"handleClose\"\n            size=\"small\"\n        >\n            <el-form v-loading=\"loading\" ref=\"changeFormRef\" :model=\"changeForm\" :rules=\"rules\" label-position=\"top\">\n                <div v-if=\"changeForm.operation === 'password'\">\n                    <el-form-item :label=\"$t('commons.login.username')\" prop=\"userName\">\n                        <el-input disabled v-model=\"changeForm.userName\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                        <el-input type=\"password\" clearable show-password v-model=\"changeForm.password\" />\n                    </el-form-item>\n                    <span class=\"input-help\">{{ $t('commons.rule.illegalChar') }}</span>\n                </div>\n                <div v-if=\"changeForm.operation === 'privilege'\">\n                    <el-form-item :label=\"$t('database.permission')\" prop=\"privilege\">\n                        <el-select style=\"width: 100%\" v-model=\"changeForm.privilege\">\n                            <el-option value=\"%\" :label=\"$t('database.permissionAll')\" />\n                            <el-option\n                                v-if=\"changeForm.from !== 'local'\"\n                                value=\"localhost\"\n                                :label=\"$t('terminal.localhost') + '(localhost)'\"\n                            />\n                            <el-option value=\"ip\" :label=\"$t('database.permissionForIP')\" />\n                        </el-select>\n                        <span v-if=\"changeForm.from !== 'local'\" class=\"input-help\">\n                            {{ $t('database.localhostHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item v-if=\"changeForm.privilege === 'ip'\" prop=\"privilegeIPs\">\n                        <el-input clearable :rows=\"3\" type=\"textarea\" v-model=\"changeForm.privilegeIPs\" />\n                        <span class=\"input-help\">{{ $t('database.remoteHelper') }}</span>\n                    </el-form-item>\n                </div>\n            </el-form>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button :disabled=\"loading\" @click=\"changeVisible = false\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button :disabled=\"loading\" type=\"primary\" @click=\"submitChangeInfo(changeFormRef)\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DrawerPro>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmit\"></ConfirmDialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { deleteCheckMysqlDB, updateMysqlAccess, updateMysqlPassword } from '@/api/modules/database';\nimport { Rules } from '@/global/form-rules';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst changeVisible = ref(false);\ntype FormInstance = InstanceType<typeof ElForm>;\nconst changeFormRef = ref<FormInstance>();\nconst title = ref();\nconst oldPrivilege = ref();\nconst oldPrivilegeIPs = ref();\nconst changeForm = reactive({\n    id: 0,\n    from: '',\n    type: '',\n    database: '',\n    mysqlName: '',\n    userName: '',\n    password: '',\n    operation: '',\n    privilege: '',\n    privilegeIPs: '',\n    value: '',\n});\nconst confirmDialogRef = ref();\n\nconst rules = reactive({\n    password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n    privilegeIPs: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n});\n\ninterface DialogProps {\n    id: number;\n    from: string;\n    type: string;\n    database: string;\n    mysqlName: string;\n    username: string;\n    password: string;\n    operation: string;\n    privilege: string;\n    privilegeIPs: string;\n    value: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    title.value =\n        params.operation === 'password'\n            ? i18n.global.t('database.changePassword')\n            : i18n.global.t('database.permission');\n    changeForm.id = params.id;\n    changeForm.from = params.from;\n    changeForm.type = params.type;\n    changeForm.database = params.database;\n    changeForm.mysqlName = params.mysqlName;\n    changeForm.userName = params.username;\n    changeForm.password = params.password;\n    changeForm.operation = params.operation;\n    changeForm.privilege = params.privilege;\n    changeForm.privilegeIPs = params.privilegeIPs;\n    changeForm.value = params.value;\n    changeVisible.value = true;\n    oldPrivilege.value = params.privilege;\n    oldPrivilegeIPs.value = params.privilegeIPs;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    changeVisible.value = false;\n};\n\nconst submitChangeInfo = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let param = {\n            id: changeForm.id,\n            from: changeForm.from,\n            type: changeForm.type,\n            database: changeForm.database,\n            value: '',\n        };\n        if (changeForm.operation === 'password') {\n            const res = await deleteCheckMysqlDB(param);\n            if (res.data && res.data.length > 0) {\n                let params = {\n                    header: i18n.global.t('database.changePassword'),\n                    operationInfo: i18n.global.t('database.changePasswordHelper'),\n                    submitInputInfo: i18n.global.t('database.restartNow'),\n                };\n                confirmDialogRef.value!.acceptParams(params);\n            } else {\n                param.value = changeForm.password;\n                loading.value = true;\n                await updateMysqlPassword(param)\n                    .then(() => {\n                        loading.value = false;\n                        emit('search');\n                        changeVisible.value = false;\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    })\n                    .catch(() => {\n                        loading.value = false;\n                    });\n            }\n            return;\n        }\n        if (changeForm.privilege === oldPrivilege.value && changeForm.privilegeIPs === oldPrivilegeIPs.value) {\n            changeVisible.value = false;\n            return;\n        }\n        if (changeForm.privilege !== 'ip') {\n            param.value = changeForm.privilege;\n        } else {\n            param.value = changeForm.privilegeIPs;\n        }\n        loading.value = true;\n        await updateMysqlAccess(param)\n            .then(() => {\n                loading.value = false;\n                emit('search');\n                changeVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onSubmit = async () => {\n    let param = {\n        id: changeForm.id,\n        from: changeForm.from,\n        type: changeForm.type,\n        database: changeForm.database,\n        value: changeForm.password,\n    };\n    loading.value = true;\n    await updateMysqlPassword(param)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            changeVisible.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/remote/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('database.unBindRemoteDB') + ' - ' + deleteMysqlReq.database\" size=\"small\">\n        <el-form ref=\"deleteForm\" v-loading=\"loading\" @submit.prevent>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteMysqlReq.forceDelete\" :label=\"$t('database.unBindForce')\" />\n                <span class=\"input-help\">\n                    {{ $t('database.unBindForceHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteMysqlReq.deleteBackup\" :label=\"$t('app.deleteBackup')\" />\n                <span class=\"input-help\">\n                    {{ $t('database.deleteBackupHelper') }}\n                </span>\n            </el-form-item>\n\n            <span style=\"font-size: 12px\">{{ $t('database.unBindRemoteHelper') }}</span>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { deleteDatabase } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\n\nlet deleteMysqlReq = ref({\n    id: 0,\n    database: '',\n    deleteBackup: false,\n    forceDelete: false,\n});\nlet open = ref(false);\nlet loading = ref(false);\n\nconst deleteForm = ref<FormInstance>();\n\ninterface DialogProps {\n    id: number;\n    name: string;\n    database: string;\n}\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst acceptParams = async (prop: DialogProps) => {\n    deleteMysqlReq.value = {\n        id: prop.id,\n        database: prop.database,\n        deleteBackup: false,\n        forceDelete: false,\n    };\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    deleteDatabase(deleteMysqlReq.value)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            open.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/remote/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent backName=\"MySQL\" :title=\"$t('database.remoteDB')\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                    {{ $t('database.createRemoteDB') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" @sort-change=\"search\" @search=\"search\" :data=\"data\">\n                    <el-table-column show-overflow-tooltip :label=\"$t('commons.table.name')\" prop=\"name\" sortable />\n                    <el-table-column show-overflow-tooltip :label=\"$t('database.address')\" prop=\"address\" />\n                    <el-table-column :label=\"$t('commons.login.username')\" prop=\"username\" />\n                    <el-table-column :label=\"$t('commons.login.password')\" prop=\"password\">\n                        <template #default=\"{ row }\">\n                            <div class=\"flex items-center flex-wrap\">\n                                <div class=\"star-center\">\n                                    <span v-if=\"!row.showPassword\">**********</span>\n                                </div>\n                                <div>\n                                    <span v-if=\"row.showPassword\">\n                                        {{ row.password }}\n                                    </span>\n                                </div>\n                                <el-button\n                                    v-if=\"!row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = true\"\n                                    icon=\"View\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <el-button\n                                    v-if=\"row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = false\"\n                                    icon=\"Hide\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <div>\n                                    <CopyButton :content=\"row.password\" />\n                                </div>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"description\"\n                        :label=\"$t('commons.table.description')\"\n                        show-overflow-tooltip\n                    />\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        width=\"170px\"\n                        :buttons=\"buttons\"\n                        :ellipsis=\"10\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <AppResources ref=\"checkRef\"></AppResources>\n        <OperateDialog ref=\"dialogRef\" @search=\"search\" />\n        <DeleteDialog ref=\"deleteRef\" @search=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { dateFormat } from '@/utils/util';\nimport { onMounted, reactive, ref } from 'vue';\nimport { deleteCheckDatabase, searchDatabases } from '@/api/modules/database';\nimport AppResources from '@/views/database/mysql/check/index.vue';\nimport OperateDialog from '@/views/database/mysql/remote/operate/index.vue';\nimport DeleteDialog from '@/views/database/mysql/remote/delete/index.vue';\nimport i18n from '@/lang';\nimport { Database } from '@/api/interface/database';\n\nconst loading = ref(false);\n\nconst dialogRef = ref();\nconst checkRef = ref();\nconst deleteRef = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'mysql-remote-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('mysql-remote-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        info: searchName.value,\n        type: 'mysql,mariadb',\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    const res = await searchDatabases(params);\n    data.value = res.data.items || [];\n    paginationConfig.total = res.data.total;\n};\n\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Database.DatabaseInfo> = {\n        name: '',\n        type: 'mysql',\n        version: '8.x',\n        address: '',\n        port: 3306,\n        username: 'root',\n        password: '',\n        timeout: 30,\n        description: '',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onDelete = async (row: Database.DatabaseInfo) => {\n    const res = await deleteCheckDatabase(row.id);\n    if (res.data && res.data.length > 0) {\n        checkRef.value.acceptParams({ items: res.data });\n    } else {\n        deleteRef.value.acceptParams({\n            id: row.id,\n            database: row.name,\n        });\n    }\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Database.DatabaseInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.unbind'),\n        click: (row: Database.DatabaseInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/remote/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"title\"\n        @close=\"handleClose\"\n        :resource=\"dialogData.title === 'create' ? '' : dialogData.rowData?.name\"\n        size=\"large\"\n    >\n        <el-form ref=\"formRef\" v-loading=\"loading\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-if=\"dialogData.title === 'create'\" clearable v-model.trim=\"dialogData.rowData!.name\" />\n                <el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.type')\" prop=\"type\">\n                <el-radio-group v-model=\"dialogData.rowData!.type\" @change=\"changeType\">\n                    <el-radio-button value=\"mysql\">MySQL</el-radio-button>\n                    <el-radio-button value=\"mariadb\">MariaDB</el-radio-button>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('database.version')\" prop=\"version\">\n                <el-radio-group v-model=\"dialogData.rowData!.version\" @change=\"isOK = false\">\n                    <div v-if=\"dialogData.rowData!.type === 'mysql'\">\n                        <el-radio label=\"8.x\" value=\"8.x\" />\n                        <el-radio label=\"5.7\" value=\"5.7\" />\n                        <el-radio label=\"5.6\" value=\"5.6\" />\n                    </div>\n                    <div v-else>\n                        <el-radio label=\"10.x\" value=\"10.x\" />\n                        <el-radio label=\"11.x\" value=\"11.x\" />\n                    </div>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('database.address')\" prop=\"address\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"dialogData.rowData!.address\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\">\n                <el-input @change=\"isOK = false\" clearable v-model.number=\"dialogData.rowData!.port\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"dialogData.rowData!.username\" />\n                <span class=\"input-help\">{{ $t('database.userHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                <el-input\n                    @change=\"isOK = false\"\n                    type=\"password\"\n                    clearable\n                    show-password\n                    v-model.trim=\"dialogData.rowData!.password\"\n                />\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox @change=\"isOK = false\" v-model=\"dialogData.rowData!.ssl\" :label=\"$t('database.ssl')\" />\n            </el-form-item>\n            <div v-if=\"dialogData.rowData!.ssl\">\n                <el-form-item>\n                    <el-checkbox\n                        @change=\"isOK = false\"\n                        v-model=\"dialogData.rowData!.hasCA\"\n                        :label=\"$t('database.hasCA')\"\n                    />\n                </el-form-item>\n                <el-form-item>\n                    <el-checkbox\n                        @change=\"isOK = false\"\n                        v-model=\"dialogData.rowData!.skipVerify\"\n                        :label=\"$t('database.skipVerify')\"\n                    />\n                </el-form-item>\n                <el-form-item :label=\"$t('database.clientKey')\" prop=\"clientKey\">\n                    <el-input\n                        type=\"textarea\"\n                        @change=\"isOK = false\"\n                        clearable\n                        v-model=\"dialogData.rowData!.clientKey\"\n                    />\n                </el-form-item>\n                <el-form-item :label=\"$t('database.clientCert')\" prop=\"clientCert\">\n                    <el-input\n                        type=\"textarea\"\n                        @change=\"isOK = false\"\n                        clearable\n                        v-model=\"dialogData.rowData!.clientCert\"\n                    />\n                </el-form-item>\n                <el-form-item v-if=\"dialogData.rowData!.hasCA\" :label=\"$t('database.caCert')\" prop=\"rootCert\">\n                    <el-input type=\"textarea\" @change=\"isOK = false\" clearable v-model=\"dialogData.rowData!.rootCert\" />\n                </el-form-item>\n            </div>\n            <el-form-item :label=\"$t('database.timeout')\" prop=\"timeout\">\n                <el-input-number\n                    class=\"p-w-200\"\n                    :min=\"1\"\n                    :precision=\"0\"\n                    step-strictly\n                    :step=\"1\"\n                    v-model.number=\"dialogData.rowData!.timeout\"\n                />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button @click=\"onSubmit(formRef, 'check')\">\n                    {{ $t('terminal.testConn') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"!isOK\" @click=\"onSubmit(formRef, dialogData.title)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { Database } from '@/api/interface/database';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport { addDatabase, checkDatabase, editDatabase } from '@/api/modules/database';\n\ninterface DialogProps {\n    title: string;\n    rowData?: Database.DatabaseInfo;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst isOK = ref(false);\nconst loading = ref();\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    if (dialogData.value.rowData.version.startsWith('5.6')) {\n        dialogData.value.rowData.version = '5.6';\n    }\n    if (dialogData.value.rowData.version.startsWith('5.7')) {\n        dialogData.value.rowData.version = '5.7';\n    }\n    if (dialogData.value.rowData.version.startsWith('8.')) {\n        dialogData.value.rowData.version = '8.x';\n    }\n    if (dialogData.value.rowData.version.startsWith('10.')) {\n        dialogData.value.rowData.version = '10.x';\n    }\n    dialogData.value.rowData.hasCA = dialogData.value.rowData.rootCert?.length !== 0;\n    title.value = i18n.global.t('database.' + dialogData.value.title + 'RemoteDB');\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    name: [Rules.simpleName, Rules.noSpace],\n    type: [Rules.requiredSelect],\n    version: [Rules.requiredSelect],\n    address: [Rules.ipV4V6OrDomain],\n    port: [Rules.port],\n    username: [Rules.requiredInput],\n    password: [Rules.requiredInput],\n    timeout: [Rules.number, checkNumberRange(1, 600)],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst changeType = () => {\n    dialogData.value.rowData.version = dialogData.value.rowData.type === 'mysql' ? '5.6' : '10.x';\n    isOK.value = false;\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined, operation: string) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        dialogData.value.rowData.from = 'remote';\n        loading.value = true;\n        dialogData.value.rowData.rootCert = dialogData.value.rowData.hasCA ? dialogData.value.rowData.rootCert : '';\n        if (operation === 'check') {\n            await checkDatabase(dialogData.value.rowData)\n                .then((res) => {\n                    loading.value = false;\n                    if (res.data) {\n                        isOK.value = true;\n                        MsgSuccess(i18n.global.t('terminal.connTestOk'));\n                    } else {\n                        MsgError(i18n.global.t('terminal.connTestFailed'));\n                    }\n                })\n                .catch(() => {\n                    loading.value = false;\n                    MsgError(i18n.global.t('terminal.connTestFailed'));\n                });\n        }\n\n        if (operation === 'create') {\n            await addDatabase(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n        if (operation === 'edit') {\n            await editDatabase(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/setting/helper.ts",
    "content": "export const planOptions = [\n    {\n        id: 1,\n        title: '1-2GB',\n        data: {\n            version: '',\n            key_buffer_size: 32,\n            query_cache_size: 32,\n            tmp_table_size: 32,\n            innodb_buffer_pool_size: 64,\n            innodb_log_buffer_size: 64,\n            sort_buffer_size: 256,\n            read_buffer_size: 256,\n            read_rnd_buffer_size: 256,\n            join_buffer_size: 512,\n            thread_stack: 256,\n            binlog_cache_size: 64,\n            thread_cache_size: 64,\n            table_open_cache: 128,\n            max_connections: 100,\n        },\n    },\n    {\n        id: 2,\n        title: '2-4GB',\n        data: {\n            version: '',\n            key_buffer_size: 64,\n            query_cache_size: 64,\n            tmp_table_size: 64,\n            innodb_buffer_pool_size: 128,\n            innodb_log_buffer_size: 64,\n            sort_buffer_size: 512,\n            read_buffer_size: 512,\n            read_rnd_buffer_size: 512,\n            join_buffer_size: 1024,\n            thread_stack: 256,\n            binlog_cache_size: 64,\n            thread_cache_size: 96,\n            table_open_cache: 192,\n            max_connections: 200,\n        },\n    },\n    {\n        id: 3,\n        title: '4-8GB',\n        data: {\n            version: '',\n            key_buffer_size: 128,\n            query_cache_size: 128,\n            tmp_table_size: 128,\n            innodb_buffer_pool_size: 256,\n            innodb_log_buffer_size: 64,\n            sort_buffer_size: 1024,\n            read_buffer_size: 1024,\n            read_rnd_buffer_size: 768,\n            join_buffer_size: 2048,\n            thread_stack: 256,\n            binlog_cache_size: 128,\n            thread_cache_size: 128,\n            table_open_cache: 384,\n            max_connections: 300,\n        },\n    },\n    {\n        id: 4,\n        title: '8-16GB',\n        data: {\n            version: '',\n            key_buffer_size: 256,\n            query_cache_size: 256,\n            tmp_table_size: 256,\n            innodb_buffer_pool_size: 512,\n            innodb_log_buffer_size: 64,\n            sort_buffer_size: 1024,\n            read_buffer_size: 2048,\n            read_rnd_buffer_size: 1024,\n            join_buffer_size: 2048,\n            thread_stack: 384,\n            binlog_cache_size: 192,\n            thread_cache_size: 192,\n            table_open_cache: 1024,\n            max_connections: 400,\n        },\n    },\n    {\n        id: 5,\n        title: '16-32GB',\n        data: {\n            version: '',\n            key_buffer_size: 1024,\n            query_cache_size: 384,\n            tmp_table_size: 1024,\n            innodb_buffer_pool_size: 1024,\n            innodb_log_buffer_size: 64,\n            sort_buffer_size: 4096,\n            read_buffer_size: 4096,\n            read_rnd_buffer_size: 2048,\n            join_buffer_size: 4096,\n            thread_stack: 512,\n            binlog_cache_size: 256,\n            thread_cache_size: 256,\n            table_open_cache: 2048,\n            max_connections: 500,\n        },\n    },\n];\n"
  },
  {
    "path": "frontend/src/views/database/mysql/setting/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent backName=\"MySQL\">\n            <template #leftToolBar>\n                <el-text class=\"mx-1\">\n                    {{ props.database }}\n                </el-text>\n                <el-divider direction=\"vertical\" />\n                <el-button\n                    type=\"primary\"\n                    :disabled=\"mysqlStatus !== 'Running'\"\n                    :plain=\"activeName !== 'status'\"\n                    @click=\"changeTab('status')\"\n                >\n                    {{ $t('database.currentStatus') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"activeName !== 'conf'\" @click=\"jumpToConf\">\n                    {{ $t('database.confChange') }}\n                </el-button>\n                <el-button\n                    type=\"primary\"\n                    :disabled=\"mysqlStatus !== 'Running'\"\n                    :plain=\"activeName !== 'tuning'\"\n                    @click=\"changeTab('tuning')\"\n                >\n                    {{ $t('database.performanceTuning') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"activeName !== 'port'\" @click=\"activeName = 'port'\">\n                    {{ $t('commons.table.port') }}\n                </el-button>\n                <el-button\n                    type=\"primary\"\n                    :disabled=\"mysqlStatus !== 'Running'\"\n                    :plain=\"activeName !== 'log'\"\n                    @click=\"changeTab('log')\"\n                >\n                    {{ $t('commons.button.log') }}\n                </el-button>\n                <el-button\n                    type=\"primary\"\n                    :disabled=\"mysqlStatus !== 'Running'\"\n                    @click=\"changeTab('slowLog')\"\n                    v-if=\"type != 'mysql-cluster'\"\n                    :plain=\"activeName !== 'slowLog'\"\n                >\n                    {{ $t('database.slowLog') }}\n                </el-button>\n            </template>\n\n            <template #app>\n                <AppStatus :app-key=\"props.type\" :app-name=\"props.database\" v-model:loading=\"loading\" />\n            </template>\n\n            <template #main>\n                <div v-if=\"activeName === 'conf'\">\n                    <CodemirrorPro v-model=\"mysqlConf\" :heightDiff=\"400\"></CodemirrorPro>\n                    <el-button class=\"mt-2.5\" @click=\"getDefaultConfig()\">\n                        {{ $t('app.defaultConfig') }}\n                    </el-button>\n                    <el-button type=\"primary\" class=\"mt-2.5\" @click=\"onSaveConf\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                    <el-row>\n                        <el-col :span=\"8\">\n                            <el-alert\n                                v-if=\"useOld\"\n                                class=\"mt-2.5\"\n                                :title=\"$t('app.defaultConfigHelper')\"\n                                type=\"info\"\n                                :closable=\"false\"\n                            ></el-alert>\n                        </el-col>\n                    </el-row>\n                </div>\n                <Status v-if=\"activeName === 'status'\" ref=\"statusRef\" />\n                <Variables @loading=\"changeLoading\" v-if=\"activeName === 'tuning'\" ref=\"variablesRef\" />\n                <div v-if=\"activeName === 'port'\">\n                    <el-form :model=\"baseInfo\" ref=\"panelFormRef\" label-position=\"top\">\n                        <el-row>\n                            <el-col :span=\"1\"><br /></el-col>\n                            <el-col :span=\"10\">\n                                <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\" :rules=\"Rules.port\">\n                                    <el-input clearable type=\"number\" v-model.number=\"baseInfo.port\" />\n                                </el-form-item>\n                                <el-form-item>\n                                    <el-button type=\"primary\" @click=\"onSavePort(panelFormRef)\" icon=\"Collection\">\n                                        {{ $t('commons.button.save') }}\n                                    </el-button>\n                                </el-form-item>\n                            </el-col>\n                        </el-row>\n                    </el-form>\n                </div>\n                <ContainerLog v-if=\"activeName === 'log'\" :container=\"baseInfo.containerID\" :highlightDiff=\"350\" />\n                <SlowLog\n                    @loading=\"changeLoading\"\n                    @refresh=\"loadBaseInfo\"\n                    v-if=\"activeName === 'slowLog' && type != 'mysql-cluster'\"\n                    ref=\"slowLogRef\"\n                />\n            </template>\n        </LayoutContent>\n\n        <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"small\">\n            <el-alert :closable=\"false\" :title=\"$t('database.confNotFound')\" type=\"info\">\n                <el-link icon=\"Position\" @click=\"goUpgrade()\" type=\"primary\">\n                    {{ $t('database.goUpgrade') }}\n                </el-link>\n            </el-alert>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <ConfirmDialog ref=\"confirmPortRef\" @confirm=\"onSubmitChangePort\"></ConfirmDialog>\n        <ConfirmDialog ref=\"confirmConfRef\" @confirm=\"onSubmitChangeConf\"></ConfirmDialog>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport ContainerLog from '@/components/log/container/index.vue';\nimport Status from '@/views/database/mysql/setting/status/index.vue';\nimport Variables from '@/views/database/mysql/setting/variables/index.vue';\nimport SlowLog from '@/views/database/mysql/setting/slow-log/index.vue';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { onMounted, reactive, ref } from 'vue';\nimport { loadDBFile, loadDBBaseInfo, loadMysqlVariables, updateDBFile } from '@/api/modules/database';\nimport { changePort, checkAppInstalled, getAppDefaultConfig } from '@/api/modules/app';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport { routerToName } from '@/utils/router';\n\nconst loading = ref(false);\n\nconst activeName = ref('status');\n\nconst baseInfo = reactive({\n    name: '',\n    port: 3306,\n    password: '',\n    remoteConn: false,\n    containerID: '',\n});\nconst panelFormRef = ref<FormInstance>();\nconst mysqlConf = ref();\nconst open = ref();\n\nconst useOld = ref(false);\n\nconst statusRef = ref();\nconst variablesRef = ref();\nconst slowLogRef = ref();\n\nconst mysqlName = ref();\nconst mysqlStatus = ref();\nconst mysqlVersion = ref();\nconst variables = ref();\n\ninterface DBProps {\n    type: string;\n    database: string;\n}\nconst props = withDefaults(defineProps<DBProps>(), {\n    type: '',\n    database: '',\n});\n\nconst jumpToConf = async () => {\n    activeName.value = 'conf';\n    loadMysqlConf();\n};\n\nconst changeTab = (tab: string) => {\n    activeName.value = tab;\n\n    switch (tab) {\n        case 'slowLog':\n            nextTick(() => {\n                loadSlowLogs();\n            });\n            break;\n        case 'status':\n            nextTick(() => {\n                statusRef.value!.acceptParams({ type: props.type, database: props.database });\n            });\n            break;\n        case 'tuning':\n            loadVariables();\n            break;\n    }\n};\n\nconst onSubmitChangePort = async () => {\n    let params = {\n        key: props.type,\n        name: props.database,\n        port: baseInfo.port,\n    };\n    loading.value = true;\n    await changePort(params)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst confirmPortRef = ref();\nconst onSavePort = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    const result = await formEl.validateField('port', callback);\n    if (!result) {\n        return;\n    }\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmPortRef.value!.acceptParams(params);\n};\nfunction callback(error: any) {\n    if (error) {\n        return error.message;\n    } else {\n        return;\n    }\n}\n\nconst getDefaultConfig = async () => {\n    loading.value = true;\n    await getAppDefaultConfig(props.type, props.database)\n        .then((res) => {\n            mysqlConf.value = res.data;\n            useOld.value = true;\n            loading.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onSubmitChangeConf = async () => {\n    let param = {\n        type: props.type,\n        database: props.database,\n        file: mysqlConf.value,\n    };\n    loading.value = true;\n    await updateDBFile(param)\n        .then(() => {\n            useOld.value = false;\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst confirmConfRef = ref();\nconst onSaveConf = async () => {\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmConfRef.value!.acceptParams(params);\n    return;\n};\n\nconst loadBaseInfo = async () => {\n    const res = await loadDBBaseInfo(props.type, props.database);\n    mysqlName.value = res.data?.name;\n    baseInfo.port = res.data?.port;\n    baseInfo.containerID = res.data?.containerName;\n    loadMysqlConf();\n};\n\nconst changeLoading = (status: boolean) => {\n    loading.value = status;\n};\n\nconst loadVariables = async () => {\n    const res = await loadMysqlVariables(props.type, props.database);\n    variables.value = res.data;\n    variablesRef.value!.acceptParams({\n        type: props.type,\n        database: props.database,\n        version: mysqlVersion.value,\n        variables: res.data,\n    });\n};\n\nconst loadSlowLogs = async () => {\n    const res = await loadMysqlVariables(props.type, props.database);\n    variables.value = res.data;\n\n    let param = {\n        type: props.type,\n        database: props.database,\n        variables: variables.value,\n    };\n    slowLogRef.value!.acceptParams(param);\n};\n\nconst loadMysqlConf = async () => {\n    useOld.value = false;\n    await loadDBFile(props.type + '-conf', props.database)\n        .then((res) => {\n            loading.value = false;\n            mysqlConf.value = res.data;\n        })\n        .catch(() => {\n            open.value = true;\n            loading.value = false;\n        });\n};\n\nconst goUpgrade = () => {\n    routerToName('AppUpgrade');\n};\n\nconst onLoadInfo = async () => {\n    await checkAppInstalled(props.type, props.database).then((res) => {\n        mysqlName.value = res.data.name;\n        mysqlStatus.value = res.data.status;\n        mysqlVersion.value = res.data.version;\n        loadBaseInfo();\n    });\n};\n\nonMounted(() => {\n    changeTab('status');\n    onLoadInfo();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/setting/slow-log/index.vue",
    "content": "<template>\n    <div>\n        <el-form label-position=\"left\" label-width=\"80px\" @submit.prevent>\n            <el-form-item :label=\"$t('database.isOn')\">\n                <el-switch\n                    v-model=\"variables.slow_query_log\"\n                    active-value=\"ON\"\n                    inactive-value=\"OFF\"\n                    @change=\"handleSlowLogs\"\n                />\n            </el-form-item>\n            <el-form-item :label=\"$t('database.longQueryTime')\" v-if=\"detailShow\">\n                <div class=\"float-left\">\n                    <el-input type=\"number\" v-model.number=\"variables.long_query_time\" />\n                </div>\n                <el-button class=\"float-left ml-2\" @click=\"changeSlowLogs\">\n                    {{ $t('commons.button.save') }}\n                </el-button>\n            </el-form-item>\n        </el-form>\n        <LogFile v-if=\"currentStatus\" :config=\"config\" />\n        <ConfirmDialog @cancel=\"onCancel\" ref=\"confirmDialogRef\" @confirm=\"onSave\"></ConfirmDialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Database } from '@/api/interface/database';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { updateMysqlVariables } from '@/api/modules/database';\nimport LogFile from '@/components/log/file/index.vue';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\n\nconst detailShow = ref();\nconst currentStatus = ref();\nconst config = ref();\nconst confirmDialogRef = ref();\n\nconst variables = reactive({\n    slow_query_log: 'OFF',\n    long_query_time: 10,\n});\n\nconst currentDB = reactive({\n    type: '',\n    database: '',\n});\ninterface DialogProps {\n    type: string;\n    database: string;\n    variables: Database.MysqlVariables;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    currentDB.type = params.type;\n    currentDB.database = params.database;\n    variables.slow_query_log = params.variables.slow_query_log;\n    variables.long_query_time = Number(params.variables.long_query_time);\n\n    if (variables.slow_query_log === 'ON') {\n        currentStatus.value = true;\n        detailShow.value = true;\n        config.value = {\n            type: params.type + '-slow-logs',\n            name: params.database,\n            tail: true,\n        };\n    } else {\n        detailShow.value = false;\n    }\n};\nconst emit = defineEmits(['loading']);\n\nconst handleSlowLogs = async () => {\n    if (variables.slow_query_log === 'ON') {\n        config.value = {\n            type: currentDB.type + '-slow-logs',\n            name: currentDB.database,\n            tail: true,\n        };\n        detailShow.value = true;\n        return;\n    }\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRef.value!.acceptParams(params);\n};\n\nconst changeSlowLogs = () => {\n    if (!(variables.long_query_time > 0 && variables.long_query_time <= 600)) {\n        MsgError(i18n.global.t('database.thresholdRangeHelper'));\n        return;\n    }\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRef.value!.acceptParams(params);\n};\n\nconst onCancel = async () => {\n    variables.slow_query_log = currentStatus.value ? 'ON' : 'OFF';\n    detailShow.value = currentStatus.value;\n};\n\nconst onSave = async () => {\n    let param = [] as Array<Database.VariablesUpdateHelper>;\n    param.push({ param: 'slow_query_log', value: variables.slow_query_log });\n    if (variables.slow_query_log === 'ON') {\n        param.push({ param: 'long_query_time', value: variables.long_query_time + '' });\n        param.push({ param: 'slow_query_log_file', value: '/var/lib/mysql/1Panel-slow.log' });\n    }\n    let params = {\n        type: currentDB.type,\n        database: currentDB.database,\n        variables: param,\n    };\n    emit('loading', true);\n    await updateMysqlVariables(params)\n        .then(() => {\n            emit('loading', false);\n            currentStatus.value = variables.slow_query_log === 'ON';\n            detailShow.value = variables.slow_query_log === 'ON';\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            emit('loading', false);\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/setting/status/index.vue",
    "content": "<template>\n    <div>\n        <el-form label-position=\"top\">\n            <span class=\"title\">{{ $t('database.baseParam') }}</span>\n            <el-divider class=\"divider\" />\n            <el-row type=\"flex\" justify=\"center\" style=\"margin-left: 50px\" :gutter=\"20\">\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.runTime') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.run }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.connections') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.connections }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.bytesSent') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.bytesSent }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.bytesReceived') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.bytesReceived }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.queryPerSecond') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.queryPerSecond }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.txPerSecond') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.txPerSecond }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">File</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.file }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">Position</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.position }}</span>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n\n            <span class=\"title\">{{ $t('database.performanceParam') }}</span>\n            <el-divider class=\"divider\" />\n            <el-row type=\"flex\" style=\"margin-left: 50px\" justify=\"center\" :gutter=\"20\">\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.queryPerSecond') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.queryPerSecond }}</span>\n                        <span class=\"input-help\">{{ $t('database.connInfoHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.threadCacheHit') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.threadCacheHit }}</span>\n                        <span class=\"input-help\">{{ $t('database.threadCacheHitHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.indexHit') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.indexHit }}</span>\n                        <span class=\"input-help\">{{ $t('database.indexHitHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.innodbIndexHit') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.innodbIndexHit }}</span>\n                        <span class=\"input-help\">{{ $t('database.innodbIndexHitHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.cacheHit') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.cacheHit }}</span>\n                        <span class=\"input-help\">{{ $t('database.cacheHitHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.tmpTableToDB') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.tmpTableToDB }}</span>\n                        <span class=\"input-help\">{{ $t('database.tmpTableToDBHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.openTables') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.openTables }}</span>\n                        <span class=\"input-help\">{{ $t('database.openTablesHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.selectFullJoin') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.selectFullJoin }}</span>\n                        <span class=\"input-help\">{{ $t('database.selectFullJoinHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.selectRangeCheck') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.selectRangeCheck }}</span>\n                        <span class=\"input-help\">{{ $t('database.selectRangeCheckHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.sortMergePasses') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.sortMergePasses }}</span>\n                        <span class=\"input-help\">{{ $t('database.sortMergePassesHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">{{ $t('database.tableLocksWaited') }}</span>\n                        </template>\n                        <span class=\"status-count\">{{ mysqlStatus.tableLocksWaited }}</span>\n                        <span class=\"input-help\">{{ $t('database.tableLocksWaitedHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item style=\"width: 25%\"></el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { loadMysqlStatus } from '@/api/modules/database';\nimport { computeSize } from '@/utils/util';\nimport { reactive } from 'vue';\n\nlet mysqlStatus = reactive({\n    run: 0,\n    connections: 0,\n    bytesSent: '',\n    bytesReceived: '',\n\n    queryPerSecond: '',\n    txPerSecond: '',\n    file: '',\n    position: 0,\n\n    connInfo: '',\n    threadCacheHit: '',\n    indexHit: '',\n    innodbIndexHit: '',\n    cacheHit: '',\n    tmpTableToDB: '',\n    openTables: 0,\n    selectFullJoin: 0,\n    selectRangeCheck: 0,\n    sortMergePasses: 0,\n    tableLocksWaited: 0,\n});\n\nconst currentDB = reactive({\n    type: '',\n    database: '',\n});\n\ninterface DialogProps {\n    type: string;\n    database: string;\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    currentDB.type = params.type;\n    currentDB.database = params.database;\n    loadStatus();\n};\n\nconst loadStatus = async () => {\n    const res = await loadMysqlStatus(currentDB.type, currentDB.database);\n    let queryPerSecond = res.data.Questions / res.data.Uptime;\n    let txPerSecond = (res.data!.Com_commit + res.data.Com_rollback) / res.data.Uptime;\n\n    let threadCacheHit = (1 - res.data.Threads_created / res.data.Connections) * 100;\n    let cacheHit = (res.data.Qcache_hits / (res.data.Qcache_hits + res.data.Qcache_inserts)) * 100;\n    let indexHit = (1 - res.data.Key_reads / res.data.Key_read_requests) * 100;\n    let innodbIndexHit = (1 - res.data.Innodb_buffer_pool_reads / res.data.Innodb_buffer_pool_read_requests) * 100;\n    let tmpTableToDB = (res.data.Created_tmp_disk_tables / res.data.Created_tmp_tables) * 100;\n\n    mysqlStatus.run = res.data.Run;\n    mysqlStatus.connections = res.data.Connections;\n    mysqlStatus.bytesSent = res.data.Bytes_sent ? computeSize(res.data.Bytes_sent) : '0';\n    mysqlStatus.bytesReceived = res.data.Bytes_received ? computeSize(res.data.Bytes_received) : '0';\n\n    mysqlStatus.queryPerSecond = isNaN(queryPerSecond) || queryPerSecond === 0 ? '0' : queryPerSecond.toFixed(2);\n    mysqlStatus.txPerSecond = isNaN(txPerSecond) || txPerSecond === 0 ? '0' : txPerSecond.toFixed(2);\n    mysqlStatus.file = res.data.File;\n    mysqlStatus.position = res.data.Position;\n\n    mysqlStatus.connInfo = res.data.Threads_running + '/' + res.data.Max_used_connections;\n    mysqlStatus.threadCacheHit = isNaN(threadCacheHit) || threadCacheHit === 0 ? '0' : threadCacheHit.toFixed(2) + '%';\n    mysqlStatus.indexHit = isNaN(indexHit) || indexHit === 0 ? '0' : indexHit.toFixed(2) + '%';\n    mysqlStatus.innodbIndexHit = isNaN(innodbIndexHit) || innodbIndexHit === 0 ? '0' : innodbIndexHit.toFixed(2) + '%';\n    mysqlStatus.cacheHit = isNaN(cacheHit) || cacheHit === 0 ? 'OFF' : cacheHit.toFixed(2) + '%';\n    mysqlStatus.tmpTableToDB = isNaN(tmpTableToDB) || tmpTableToDB === 0 ? '0' : tmpTableToDB.toFixed(2) + '%';\n    mysqlStatus.openTables = res.data.Open_tables;\n    mysqlStatus.selectFullJoin = res.data.Select_full_join;\n    mysqlStatus.selectRangeCheck = res.data.Select_range_check;\n    mysqlStatus.sortMergePasses = res.data.Sort_merge_passes;\n    mysqlStatus.tableLocksWaited = res.data.Table_locks_waited;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.divider {\n    display: block;\n    height: 1px;\n    width: 100%;\n    margin: 12px 0;\n    border-top: 1px var(--el-border-color) var(--el-border-style);\n}\n.title {\n    font-size: 20px;\n    font-weight: 500;\n    margin-left: 50px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/database/mysql/setting/variables/index.vue",
    "content": "<template>\n    <div>\n        <el-form :model=\"mysqlVariables\" :rules=\"variablesRules\" ref=\"variableFormRef\" label-position=\"top\">\n            <el-row>\n                <el-col :span=\"1\"><br /></el-col>\n                <el-col :span=\"9\">\n                    <el-form-item :label=\"$t('database.optimizationScheme')\">\n                        <el-select @change=\"changePlan\" clearable v-model=\"plan\">\n                            <el-option\n                                v-for=\"item in planOptions\"\n                                :key=\"item.id\"\n                                :label=\"item.title\"\n                                :value=\"item.id\"\n                            />\n                        </el-select>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n            <el-row>\n                <el-col :span=\"1\"><br /></el-col>\n                <el-col :span=\"9\">\n                    <el-form-item label=\"key_buffer_size\" prop=\"key_buffer_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.key_buffer_size\">\n                            <template #append>MB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.keyBufferSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"join_buffer_size\" prop=\"join_buffer_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.join_buffer_size\">\n                            <template #append>KB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.joinBufferSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"tmp_table_size\" prop=\"tmp_table_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.tmp_table_size\">\n                            <template #append>MB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.tmpTableSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"innodb_buffer_pool_size\" prop=\"innodb_buffer_pool_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.innodb_buffer_pool_size\">\n                            <template #append>MB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.innodbBufferPoolSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"innodb_log_buffer_size\" prop=\"innodb_log_buffer_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.innodb_log_buffer_size\">\n                            <template #append>MB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.innodbLogBufferSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"sort_buffer_size\" prop=\"sort_buffer_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.sort_buffer_size\">\n                            <template #append>KB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.sortBufferSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"read_buffer_size\" prop=\"read_buffer_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.read_buffer_size\">\n                            <template #append>KB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.readBufferSizeHelper') }}</span>\n                    </el-form-item>\n\n                    <el-form-item>\n                        <el-button @click=\"onSaveStart(variableFormRef)\" type=\"primary\">\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                    </el-form-item>\n                </el-col>\n                <el-col :span=\"2\"><br /></el-col>\n                <el-col :span=\"9\">\n                    <el-form-item label=\"read_rnd_buffer_size\" prop=\"read_rnd_buffer_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.read_rnd_buffer_size\">\n                            <template #append>KB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.readRndBufferSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item v-if=\"showCacheSize()\" label=\"query_cache_size\" prop=\"query_cache_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.query_cache_size\">\n                            <template #append>MB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.queryCacheSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"thread_stack\" prop=\"thread_stack\">\n                        <el-input clearable v-model.number=\"mysqlVariables.thread_stack\">\n                            <template #append>KB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.threadStackelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"binlog_cache_size\" prop=\"binlog_cache_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.binlog_cache_size\">\n                            <template #append>KB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('database.binlogCacheSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"thread_cache_size\" prop=\"thread_cache_size\">\n                        <el-input clearable v-model.number=\"mysqlVariables.thread_cache_size\" />\n                        <span class=\"input-help\">{{ $t('database.threadCacheSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"table_open_cache\" prop=\"table_open_cache\">\n                        <el-input clearable v-model.number=\"mysqlVariables.table_open_cache\" />\n                        <span class=\"input-help\">{{ $t('database.tableOpenCacheHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"max_connections\" prop=\"max_connections\">\n                        <el-input clearable v-model.number=\"mysqlVariables.max_connections\" />\n                        <span class=\"input-help\">{{ $t('database.maxConnectionsHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSaveVariables\"></ConfirmDialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport { FormInstance } from 'element-plus';\nimport { Database } from '@/api/interface/database';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { updateMysqlVariables } from '@/api/modules/database';\nimport i18n from '@/lang';\nimport { planOptions } from './../helper';\nimport { MsgSuccess } from '@/utils/message';\n\nconst plan = ref();\nconst confirmDialogRef = ref();\n\nconst variableFormRef = ref<FormInstance>();\nconst oldVariables = ref<Database.MysqlVariables>();\nlet mysqlVariables = reactive({\n    mysqlName: '',\n    key_buffer_size: 0,\n    query_cache_size: 0,\n    tmp_table_size: 0,\n    innodb_buffer_pool_size: 0,\n    innodb_log_buffer_size: 0,\n    sort_buffer_size: 0,\n    read_buffer_size: 0,\n    read_rnd_buffer_size: 0,\n    join_buffer_size: 0,\n    thread_stack: 0,\n    binlog_cache_size: 0,\n    thread_cache_size: 0,\n    table_open_cache: 0,\n    max_connections: 0,\n\n    slow_query_log: '',\n    long_query_time: 0,\n});\nconst variablesRules = reactive({\n    key_buffer_size: [Rules.number, checkNumberRange(1, 102400)],\n    query_cache_size: [Rules.number, checkNumberRange(0, 102400)],\n    tmp_table_size: [Rules.number, checkNumberRange(1, 102400)],\n    innodb_buffer_pool_size: [Rules.number, checkNumberRange(1, 102400)],\n    innodb_log_buffer_size: [Rules.number, checkNumberRange(1, 102400)],\n    sort_buffer_size: [Rules.number, checkNumberRange(1, 102400)],\n    read_buffer_size: [Rules.number, checkNumberRange(1, 102400)],\n    read_rnd_buffer_size: [Rules.number, checkNumberRange(1, 102400)],\n    join_buffer_size: [Rules.number, checkNumberRange(1, 102400)],\n    thread_stack: [Rules.number, checkNumberRange(1, 102400)],\n    binlog_cache_size: [Rules.number, checkNumberRange(1, 102400)],\n    thread_cache_size: [Rules.number, checkNumberRange(1, 10000)],\n    table_open_cache: [Rules.number, checkNumberRange(1, 10000)],\n    max_connections: [Rules.number, checkNumberRange(1, 10000)],\n\n    slow_query_log: [Rules.requiredSelect],\n    long_query_time: [Rules.number, checkNumberRange(1, 102400)],\n});\n\nconst currentDB = reactive({\n    type: '',\n    database: '',\n    version: '',\n});\ninterface DialogProps {\n    type: string;\n    database: string;\n    version: string;\n    variables: Database.MysqlVariables;\n}\nconst acceptParams = (params: DialogProps): void => {\n    currentDB.type = params.type;\n    currentDB.database = params.database;\n    currentDB.version = params.version;\n    mysqlVariables.key_buffer_size = Number(params.variables.key_buffer_size) / 1024 / 1024;\n    mysqlVariables.query_cache_size = Number(params.variables.query_cache_size) / 1024 / 1024;\n    mysqlVariables.tmp_table_size = Number(params.variables.tmp_table_size) / 1024 / 1024;\n    mysqlVariables.innodb_buffer_pool_size = Number(params.variables.innodb_buffer_pool_size) / 1024 / 1024;\n    mysqlVariables.innodb_log_buffer_size = Number(params.variables.innodb_log_buffer_size) / 1024 / 1024;\n\n    mysqlVariables.sort_buffer_size = Number(params.variables.sort_buffer_size) / 1024;\n    mysqlVariables.read_buffer_size = Number(params.variables.read_buffer_size) / 1024;\n    mysqlVariables.read_rnd_buffer_size = Number(params.variables.read_rnd_buffer_size) / 1024;\n    mysqlVariables.join_buffer_size = Number(params.variables.join_buffer_size) / 1024;\n    mysqlVariables.thread_stack = Number(params.variables.thread_stack) / 1024;\n    mysqlVariables.binlog_cache_size = Number(params.variables.binlog_cache_size) / 1024;\n    mysqlVariables.thread_cache_size = Number(params.variables.thread_cache_size);\n    mysqlVariables.table_open_cache = Number(params.variables.table_open_cache);\n    mysqlVariables.max_connections = Number(params.variables.max_connections);\n    oldVariables.value = { ...mysqlVariables };\n};\nconst emit = defineEmits(['loading']);\n\nconst changePlan = async () => {\n    for (const item of planOptions) {\n        if (item.id === plan.value) {\n            variableFormRef.value.resetFields();\n            mysqlVariables.key_buffer_size = item.data.key_buffer_size;\n            mysqlVariables.query_cache_size = item.data.query_cache_size;\n            mysqlVariables.tmp_table_size = item.data.tmp_table_size;\n            mysqlVariables.innodb_buffer_pool_size = item.data.innodb_buffer_pool_size;\n            mysqlVariables.innodb_log_buffer_size = item.data.innodb_log_buffer_size;\n\n            mysqlVariables.sort_buffer_size = item.data.sort_buffer_size;\n            mysqlVariables.read_buffer_size = item.data.read_buffer_size;\n            mysqlVariables.read_rnd_buffer_size = item.data.read_rnd_buffer_size;\n            mysqlVariables.join_buffer_size = item.data.join_buffer_size;\n            mysqlVariables.thread_stack = item.data.thread_stack;\n            mysqlVariables.binlog_cache_size = item.data.binlog_cache_size;\n            mysqlVariables.thread_cache_size = item.data.thread_cache_size;\n            mysqlVariables.table_open_cache = item.data.table_open_cache;\n            mysqlVariables.max_connections = item.data.max_connections;\n            break;\n        }\n    }\n};\n\nconst onSaveStart = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let params = {\n            header: i18n.global.t('database.confChange'),\n            operationInfo: i18n.global.t('database.restartNowHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialogRef.value!.acceptParams(params);\n    });\n};\n\nconst onSaveVariables = async () => {\n    let param = [] as Array<Database.VariablesUpdateHelper>;\n    if (oldVariables.value?.key_buffer_size !== mysqlVariables.key_buffer_size) {\n        param.push({ param: 'key_buffer_size', value: mysqlVariables.key_buffer_size * 1024 * 1024 });\n    }\n    if (oldVariables.value?.query_cache_size !== mysqlVariables.query_cache_size) {\n        param.push({ param: 'query_cache_size', value: mysqlVariables.query_cache_size * 1024 * 1024 });\n    }\n    if (oldVariables.value?.tmp_table_size !== mysqlVariables.tmp_table_size) {\n        param.push({ param: 'tmp_table_size', value: mysqlVariables.tmp_table_size * 1024 * 1024 });\n    }\n    if (oldVariables.value?.innodb_buffer_pool_size !== mysqlVariables.innodb_buffer_pool_size) {\n        param.push({\n            param: 'innodb_buffer_pool_size',\n            value: mysqlVariables.innodb_buffer_pool_size * 1024 * 1024,\n        });\n    }\n    if (oldVariables.value?.innodb_log_buffer_size !== mysqlVariables.innodb_log_buffer_size) {\n        param.push({ param: 'innodb_log_buffer_size', value: mysqlVariables.innodb_log_buffer_size * 1024 * 1024 });\n    }\n\n    if (oldVariables.value?.sort_buffer_size !== mysqlVariables.sort_buffer_size) {\n        param.push({ param: 'sort_buffer_size', value: mysqlVariables.sort_buffer_size * 1024 });\n    }\n    if (oldVariables.value?.read_buffer_size !== mysqlVariables.read_buffer_size) {\n        param.push({ param: 'read_buffer_size', value: mysqlVariables.read_buffer_size * 1024 });\n    }\n    if (oldVariables.value?.read_rnd_buffer_size !== mysqlVariables.read_rnd_buffer_size) {\n        param.push({ param: 'read_rnd_buffer_size', value: mysqlVariables.read_rnd_buffer_size * 1024 });\n    }\n    if (oldVariables.value?.join_buffer_size !== mysqlVariables.join_buffer_size) {\n        param.push({ param: 'join_buffer_size', value: mysqlVariables.join_buffer_size * 1024 });\n    }\n    if (oldVariables.value?.thread_stack !== mysqlVariables.thread_stack) {\n        param.push({ param: 'thread_stack', value: mysqlVariables.thread_stack * 1024 });\n    }\n    if (oldVariables.value?.binlog_cache_size !== mysqlVariables.binlog_cache_size) {\n        param.push({ param: 'binlog_cache_size', value: mysqlVariables.binlog_cache_size * 1024 });\n    }\n    if (oldVariables.value?.thread_cache_size !== mysqlVariables.thread_cache_size) {\n        param.push({ param: 'thread_cache_size', value: mysqlVariables.thread_cache_size });\n    }\n    if (oldVariables.value?.table_open_cache !== mysqlVariables.table_open_cache) {\n        param.push({ param: 'table_open_cache', value: mysqlVariables.table_open_cache });\n    }\n    if (oldVariables.value?.max_connections !== mysqlVariables.max_connections) {\n        param.push({ param: 'max_connections', value: mysqlVariables.max_connections });\n    }\n    emit('loading', true);\n    let params = {\n        type: currentDB.type,\n        database: currentDB.database,\n        variables: param,\n    };\n    await updateMysqlVariables(params)\n        .then(() => {\n            emit('loading', false);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            emit('loading', false);\n        });\n};\n\nconst showCacheSize = () => {\n    return currentDB.version?.startsWith('5.7');\n};\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/bind/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro\n            v-model=\"bindVisible\"\n            :header=\"$t('database.userBind')\"\n            :resource=\"form.name\"\n            @close=\"handleClose\"\n            size=\"small\"\n        >\n            <el-form v-loading=\"loading\" ref=\"changeFormRef\" :model=\"form\" :rules=\"rules\" label-position=\"top\">\n                <el-alert type=\"warning\" :title=\"$t('database.pgBindHelper')\" :closable=\"false\" />\n                <el-form-item class=\"mt-5\" :label=\"$t('database.userBind')\" prop=\"username\">\n                    <el-input v-model=\"form.username\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                    <el-input type=\"password\" clearable show-password v-model=\"form.password\" />\n                    <span class=\"input-help\">{{ $t('commons.rule.illegalChar') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('database.permission')\" prop=\"superUser\">\n                    <el-checkbox v-model=\"form.superUser\">{{ $t('database.pgSuperUser') }}</el-checkbox>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button :disabled=\"loading\" @click=\"bindVisible = false\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(changeFormRef)\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DrawerPro>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmit\"></ConfirmDialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { bindPostgresqlUser } from '@/api/modules/database';\nimport { Rules } from '@/global/form-rules';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst bindVisible = ref(false);\ntype FormInstance = InstanceType<typeof ElForm>;\nconst changeFormRef = ref<FormInstance>();\nconst form = reactive({\n    database: '',\n    name: '',\n    username: '',\n    password: '',\n    superUser: true,\n});\nconst confirmDialogRef = ref();\n\nconst rules = reactive({\n    username: [Rules.requiredInput, Rules.name],\n    password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n});\n\ninterface DialogProps {\n    database: string;\n    name: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    form.database = params.database;\n    form.name = params.name;\n    form.username = '';\n    form.password = '';\n    form.superUser = true;\n    bindVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    bindVisible.value = false;\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let param = {\n            name: form.name,\n            database: form.database,\n            username: form.username,\n            password: form.password,\n            superUser: form.superUser,\n        };\n        loading.value = true;\n        await bindPostgresqlUser(param)\n            .then(() => {\n                loading.value = false;\n                emit('search');\n                bindVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/check/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"large\">\n        <el-row>\n            <el-col :span=\"20\" :offset=\"2\" v-if=\"open\">\n                <el-alert\n                    type=\"error\"\n                    :description=\"$t('app.deleteHelper', [$t('menu.database')])\"\n                    center\n                    show-icon\n                    :closable=\"false\"\n                />\n                <br />\n                <el-descriptions border :column=\"1\">\n                    <el-descriptions-item\n                        v-for=\"(item, key) in installData\"\n                        :key=\"key\"\n                        label-class-name=\"check-label\"\n                        class-name=\"check-content\"\n                        min-width=\"60px\"\n                    >\n                        <template #label>\n                            <a href=\"javascript:void(0);\" class=\"check-label-a\" @click=\"toPage(item.type)\">\n                                {{ $t('menu.' + item.type) }}\n                            </a>\n                        </template>\n                        <span class=\"resources\">\n                            {{ item.name }}\n                        </span>\n                    </el-descriptions-item>\n                </el-descriptions>\n            </el-col>\n        </el-row>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { routerToName } from '@/utils/router';\nimport { ref } from 'vue';\n\ninterface InstallProps {\n    items: Array<{ type: string; name: string }>;\n}\nconst installData = ref();\nlet open = ref(false);\n\nconst acceptParams = (props: InstallProps) => {\n    installData.value = props.items;\n    open.value = true;\n};\n\nconst toPage = (key: string) => {\n    if (key === 'app') {\n        routerToName('App');\n    }\n    if (key === 'website') {\n        routerToName('Website');\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/conn/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"dialogVisible\" :header=\"$t('database.databaseConnInfo')\" @close=\"handleClose\" size=\"small\">\n        <el-form @submit.prevent v-loading=\"loading\" ref=\"formRef\" :rules=\"rules\" :model=\"form\" label-position=\"top\">\n            <el-form-item v-if=\"form.from === 'local'\">\n                <template #label>\n                    <div class=\"flex items-center justify-between\">\n                        <span>{{ $t('database.containerConn') }}</span>\n                        <el-button link @click=\"copyConnURL(true)\" icon=\"DocumentCopy\">\n                            {{ $t('database.copyConnURL') }}\n                        </el-button>\n                    </div>\n                </template>\n                <el-card class=\"mini-border-card\">\n                    <el-descriptions :column=\"1\">\n                        <el-descriptions-item :label=\"$t('database.connAddress')\">\n                            <el-tooltip v-if=\"loadPgInfo(true).length > 48\" :content=\"loadPgInfo(true)\" placement=\"top\">\n                                {{ loadPgInfo(true).substring(0, 48) }}...\n                            </el-tooltip>\n                            <span else>\n                                {{ loadPgInfo(true) }}\n                            </span>\n                            <CopyButton :content=\"loadPgInfo(true)\" />\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.port')\">\n                            5432\n                            <CopyButton content=\"5432\" />\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </el-card>\n                <span class=\"input-help\">\n                    {{ $t('database.containerConnHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <template #label>\n                    <div class=\"flex items-center justify-between\">\n                        <span>{{ $t('database.remoteConn') }}</span>\n                        <el-button link @click=\"copyConnURL(false)\" icon=\"DocumentCopy\">\n                            {{ $t('database.copyConnURL') }}\n                        </el-button>\n                    </div>\n                </template>\n                <el-card class=\"mini-border-card\">\n                    <el-descriptions :column=\"1\">\n                        <el-descriptions-item :label=\"$t('database.connAddress')\">\n                            <el-tooltip\n                                v-if=\"loadPgInfo(false).length > 48\"\n                                :content=\"loadPgInfo(false)\"\n                                placement=\"top\"\n                            >\n                                {{ loadPgInfo(false).substring(0, 48) }}...\n                            </el-tooltip>\n                            <span else>\n                                {{ loadPgInfo(false) }}\n                            </span>\n                            <CopyButton :content=\"loadPgInfo(false)\" />\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.port')\">\n                            {{ form.port }}\n                            <CopyButton :content=\"form.port + ''\" />\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </el-card>\n                <span v-if=\"form.from === 'local'\" class=\"input-help\">\n                    {{ $t('database.remoteConnHelper2') }}\n                </span>\n            </el-form-item>\n\n            <el-divider border-style=\"dashed\" />\n            <div v-if=\"form.from === 'local'\">\n                <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                    <el-input type=\"text\" style=\"width: calc(100% - 60px)\" readonly disabled v-model=\"form.username\">\n                        <template #append>\n                            <el-button-group>\n                                <CopyButton :content=\"form.username\" :isIcon=\"false\" />\n                            </el-button-group>\n                        </template>\n                    </el-input>\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                    <el-input\n                        style=\"width: calc(100% - 205px)\"\n                        type=\"password\"\n                        show-password\n                        clearable\n                        v-model=\"form.password\"\n                    />\n                    <el-button-group>\n                        <CopyButton :isIcon=\"false\" class=\"copy_button\" :content=\"form.password\" />\n                        <el-button @click=\"random\">\n                            {{ $t('commons.button.random') }}\n                        </el-button>\n                    </el-button-group>\n                    <span class=\"input-help\">{{ $t('commons.rule.illegalChar') }}</span>\n                </el-form-item>\n            </div>\n            <div v-if=\"form.from !== 'local'\">\n                <el-form-item :label=\"$t('commons.login.username')\">\n                    <el-tag>{{ form.username }}</el-tag>\n                    <CopyButton :content=\"form.username\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.password')\">\n                    <el-tag>{{ form.password }}</el-tag>\n                    <CopyButton :content=\"form.password\" />\n                </el-form-item>\n            </div>\n        </el-form>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"dialogVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading || form.status !== 'Running'\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { getDatabase, updatePostgresqlPassword } from '@/api/modules/database';\nimport { getAppConnInfo } from '@/api/modules/app';\nimport { MsgSuccess } from '@/utils/message';\nimport { getRandomStr, copyText } from '@/utils/util';\nimport { getAgentSettingInfo } from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\n\nconst dialogVisible = ref(false);\nconst form = reactive({\n    status: '',\n    systemIP: '',\n    password: '',\n    containerName: '',\n    serviceName: '',\n    privilege: false,\n    port: 0,\n\n    from: '',\n    type: '',\n    database: '',\n    username: '',\n    remoteIP: '',\n});\nconst rules = reactive({\n    password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\ninterface DialogProps {\n    from: string;\n    type: string;\n    database: string;\n}\n\nconst acceptParams = (param: DialogProps): void => {\n    form.password = '';\n    form.from = param.from;\n    form.type = param.type;\n    form.database = param.database;\n    loadAccess();\n    loadPassword();\n    dialogVisible.value = true;\n};\n\nfunction loadPgInfo(isContainer: boolean) {\n    if (isContainer) {\n        return form.from === 'local' ? form.containerName : form.systemIP;\n    } else {\n        return form.from === 'local' ? form.systemIP : form.remoteIP;\n    }\n}\n\nconst copyConnURL = (isContainer: boolean) => {\n    const host = loadPgInfo(isContainer);\n    const port = isContainer && form.from === 'local' ? 5432 : form.port;\n    const encodedPassword = encodeURIComponent(form.password);\n    copyText(`postgresql://${form.username}:${encodedPassword}@${host}:${port}`);\n};\n\nconst random = async () => {\n    form.password = getRandomStr(16);\n};\n\nconst handleClose = () => {\n    dialogVisible.value = false;\n};\n\nconst loadAccess = async () => {\n    if (form.from === 'local') {\n        form.privilege = false;\n    }\n};\n\nconst loadSystemIP = async () => {\n    const res = await getAgentSettingInfo();\n    form.systemIP = res.data.systemIP || globalStore.currentNodeAddr || i18n.global.t('database.localIP');\n};\n\nconst loadPassword = async () => {\n    if (form.from === 'local') {\n        const res = await getAppConnInfo(form.type, form.database);\n        form.status = res.data.status;\n        form.username = res.data.username || '';\n        form.password = res.data.password || '';\n        form.port = res.data.port || 5432;\n        form.containerName = res.data.containerName || '';\n        form.serviceName = res.data.serviceName || '';\n        loadSystemIP();\n        return;\n    }\n    const res = await getDatabase(form.database);\n    form.password = res.data.password || '';\n    form.port = res.data.port || 5432;\n    form.username = res.data.username;\n    form.password = res.data.password;\n    form.remoteIP = res.data.address;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('database.changeConnHelper', [i18n.global.t('commons.login.password')]),\n            i18n.global.t('commons.msg.infoTitle'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n            },\n        ).then(async () => {\n            let param = {\n                id: 0,\n                from: form.from,\n                type: form.type,\n                database: form.database,\n                value: form.password,\n            };\n            loading.value = true;\n            await updatePostgresqlPassword(param)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    dialogVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.copy_button {\n    border-radius: 0px;\n    border-left-width: 0px;\n}\n:deep(.el-input__wrapper) {\n    border-top-right-radius: 0px;\n    border-bottom-right-radius: 0px;\n}\n:deep(.el-form-item__label) {\n    width: 100%;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"createVisible\" :header=\"$t('commons.button.create')\" @close=\"handleClose\" size=\"small\">\n        <div v-loading=\"loading\">\n            <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\">\n                <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                    <el-input clearable v-model.trim=\"form.name\" @input=\"form.username = form.name\"></el-input>\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                    <el-input clearable v-model.trim=\"form.username\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                    <el-input type=\"password\" clearable show-password v-model.trim=\"form.password\">\n                        <template #append>\n                            <el-button @click=\"random\">{{ $t('commons.button.random') }}</el-button>\n                        </template>\n                    </el-input>\n                    <span class=\"input-help\">{{ $t('commons.rule.illegalChar') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('database.permission')\" prop=\"superUser\">\n                    <el-checkbox v-model=\"form.superUser\">{{ $t('database.pgSuperUser') }}</el-checkbox>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('commons.table.type')\" prop=\"database\">\n                    <el-tag>{{ form.database + ' [' + form.type + ']' }}</el-tag>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                    <el-input type=\"textarea\" clearable v-model=\"form.description\" />\n                </el-form-item>\n            </el-form>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"createVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { addPostgresqlDB } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\nimport { getRandomStr } from '@/utils/util';\n\nconst loading = ref();\nconst createVisible = ref(false);\nconst form = reactive({\n    name: '',\n    from: 'local',\n    type: '',\n    database: '',\n    format: '',\n    username: '',\n    password: '',\n    superUser: true,\n    description: '',\n});\nconst rules = reactive({\n    name: [Rules.requiredInput, Rules.dbName],\n    username: [Rules.requiredInput, Rules.name],\n    password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\ninterface DialogProps {\n    from: string;\n    type: string;\n    database: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    form.name = '';\n    form.from = params.from;\n    form.type = params.type;\n    form.database = params.database;\n    form.format = 'UTF8';\n    form.username = '';\n    form.superUser = true;\n    form.description = '';\n    random();\n    createVisible.value = true;\n};\nconst handleClose = () => {\n    createVisible.value = false;\n};\n\nconst random = async () => {\n    form.password = getRandomStr(16);\n};\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n\n        loading.value = true;\n        await addPostgresqlDB(form)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                createVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.delete') + ' - ' + dbName\" size=\"small\">\n        <el-form ref=\"deleteForm\" v-loading=\"loading\" @submit.prevent>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.forceDelete\" :label=\"$t('app.forceDelete')\" />\n                <span class=\"input-help\">\n                    {{ $t('app.forceDeleteHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.deleteBackup\" :label=\"$t('app.deleteBackup')\" />\n                <span class=\"input-help\">\n                    {{ $t('database.deleteBackupHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <div>\n                    <span style=\"font-size: 12px\">{{ $t('database.delete') }}</span>\n                    <span style=\"font-size: 12px; color: red; font-weight: 500\">{{ dbName }}</span>\n                    <span style=\"font-size: 12px\">{{ $t('database.deleteHelper') }}</span>\n                </div>\n                <el-input v-model=\"deleteInfo\" :placeholder=\"dbName\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"deleteInfo != dbName || loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { deletePostgresqlDB } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\n\nlet deleteReq = ref({\n    id: 0,\n    type: '',\n    database: '',\n    deleteBackup: false,\n    forceDelete: false,\n});\nlet open = ref(false);\nlet loading = ref(false);\nlet deleteInfo = ref('');\nlet dbName = ref('');\n\nconst deleteForm = ref<FormInstance>();\n\ninterface DialogProps {\n    id: number;\n    type: string;\n    name: string;\n    database: string;\n}\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst acceptParams = async (prop: DialogProps) => {\n    deleteReq.value = {\n        id: prop.id,\n        type: prop.type,\n        database: prop.database,\n        deleteBackup: false,\n        forceDelete: false,\n    };\n    dbName.value = prop.name;\n    deleteInfo.value = '';\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    deletePostgresqlDB(deleteReq.value)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            open.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <div class=\"app-status mt-5\" v-if=\"currentDB?.from === 'remote'\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag class=\"float-left\" effect=\"dark\" type=\"success\">PostgreSQL</el-tag>\n                        <el-tag>{{ $t('app.version') }}: {{ currentDB?.version }}</el-tag>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n        <LayoutContent :title=\"'PostgreSQL'\">\n            <template #app v-if=\"currentDB?.from === 'local'\">\n                <AppStatus\n                    :app-key=\"appKey\"\n                    :app-name=\"appName\"\n                    v-model:loading=\"loading\"\n                    v-model:mask-show=\"maskShow\"\n                    @setting=\"onSetting\"\n                    @is-exist=\"checkExist\"\n                    ref=\"appStatusRef\"\n                ></AppStatus>\n            </template>\n            <template #leftToolBar>\n                <el-button\n                    v-if=\"currentDB && (currentDB.from !== 'local' || postgresqlStatus === 'Running')\"\n                    type=\"primary\"\n                    @click=\"onOpenDialog()\"\n                >\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button v-if=\"currentDB\" @click=\"onChangeConn\" type=\"primary\" plain>\n                    {{ $t('database.databaseConnInfo') }}\n                </el-button>\n                <el-button\n                    v-if=\"currentDB && (currentDB.from !== 'local' || postgresqlStatus === 'Running')\"\n                    @click=\"loadDB\"\n                    type=\"primary\"\n                    plain\n                >\n                    {{ $t('database.loadFromRemote') }}\n                </el-button>\n                <el-button @click=\"goRemoteDB\" type=\"primary\" plain>\n                    {{ $t('database.remoteDB') }}\n                </el-button>\n                <el-button @click=\"goTerminal()\" :disabled=\"currentDB?.from !== 'local'\" type=\"primary\" plain>\n                    {{ $t('menu.terminal') }}\n                </el-button>\n                <el-button @click=\"goDashboard()\" type=\"primary\" plain>PGAdmin4</el-button>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"currentDBName\" @change=\"changeDatabase()\" class=\"p-w-200\" v-if=\"currentDB\">\n                    <template #prefix>{{ $t('commons.table.type') }}</template>\n                    <el-option-group :label=\"$t('commons.table.local')\">\n                        <div v-for=\"(item, index) in dbOptionsLocal\" :key=\"index\">\n                            <el-option v-if=\"item.from === 'local'\" :value=\"item.database\" class=\"optionClass\">\n                                <span v-if=\"item.database.length < 25\">{{ item.database }}</span>\n                                <el-tooltip v-else :content=\"item.database\" placement=\"top\">\n                                    <span>{{ item.database.substring(0, 25) }}...</span>\n                                </el-tooltip>\n                            </el-option>\n                        </div>\n                        <el-button link type=\"primary\" class=\"jumpAdd\" @click=\"goRouter('app')\" icon=\"Position\">\n                            {{ $t('database.goInstall') }}\n                        </el-button>\n                    </el-option-group>\n                    <el-option-group :label=\"$t('database.remote')\">\n                        <div v-for=\"(item, index) in dbOptionsRemote\" :key=\"index\">\n                            <el-option v-if=\"item.from === 'remote'\" :value=\"item.database\" class=\"optionClass\">\n                                <span v-if=\"item.database.length < 25\">{{ item.database }}</span>\n                                <el-tooltip v-else :content=\"item.database\" placement=\"top\">\n                                    <span>{{ item.database.substring(0, 25) }}...</span>\n                                </el-tooltip>\n                            </el-option>\n                        </div>\n                        <el-button link type=\"primary\" class=\"jumpAdd\" @click=\"goRouter('remote')\" icon=\"Position\">\n                            {{ $t('database.createRemoteDB') }}\n                        </el-button>\n                    </el-option-group>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    v-if=\"currentDB\"\n                    :class=\"{ mask: maskShow }\"\n                    :pagination-config=\"paginationConfig\"\n                    @sort-change=\"search\"\n                    @search=\"search\"\n                    :data=\"data\"\n                    :heightDiff=\"370\"\n                >\n                    <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\" sortable>\n                        <template #default=\"{ row }\">\n                            <Tooltip v-if=\"!row.isDelete\" :islink=\"false\" :text=\"row.name\" />\n                            <div v-else>\n                                <span v-if=\"row.name.length < 15\">{{ row.name }}</span>\n                                <el-tooltip v-else :content=\"row.name\">{{ row.name.substring(0, 10) }}...</el-tooltip>\n                                <el-tag round type=\"info\" class=\"ml-1\" size=\"small\">\n                                    {{ $t('database.isDelete') }}\n                                </el-tag>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.login.username')\" prop=\"username\">\n                        <template #default=\"{ row }\">\n                            <div class=\"flex items-center\" v-if=\"row.username\">\n                                <span>\n                                    {{ row.username }}\n                                </span>\n                            </div>\n                            <div v-else>\n                                <el-button\n                                    :disabled=\"row.isDelete\"\n                                    style=\"margin-left: -3px\"\n                                    type=\"primary\"\n                                    link\n                                    @click=\"onBind(row)\"\n                                >\n                                    {{ $t('database.userBind') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.login.password')\" prop=\"password\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.username === '' || row.password === ''\">-</span>\n                            <div class=\"flex items-center flex-wrap\" v-else>\n                                <div class=\"star-center\" v-if=\"!row.showPassword\">\n                                    <span>**********</span>\n                                </div>\n                                <div>\n                                    <span v-if=\"row.showPassword\">\n                                        {{ row.password }}\n                                    </span>\n                                </div>\n                                <el-button\n                                    v-if=\"!row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = true\"\n                                    icon=\"View\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <el-button\n                                    v-if=\"row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = false\"\n                                    icon=\"Hide\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <div>\n                                    <CopyButton :content=\"row.password\" />\n                                </div>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.description')\" prop=\"description\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            <fu-input-rw-switch\n                                v-model=\"row.description\"\n                                @enter=\"onChange(row)\"\n                                @blur=\"onChange(row)\"\n                            />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 10\"\n                        :min-width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        fixed=\"right\"\n                        fix\n                    />\n                </ComplexTable>\n                <div class=\"app-warn\" v-if=\"isLoaded && dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0\">\n                    <div class=\"flex flex-col gap-2 items-center justify-center w-full sm:flex-row\">\n                        <span>{{ $t('app.checkInstalledWarn', [$t('database.noPostgresql')]) }}</span>\n                        <span @click=\"goRouter('app')\" class=\"flex items-center justify-center gap-0.5\">\n                            <el-icon><Position /></el-icon>\n                            {{ $t('database.goInstall') }}\n                        </span>\n                    </div>\n                    <div>\n                        <img src=\"@/assets/images/no_app.svg\" />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <el-card\n            v-if=\"postgresqlStatus != 'Running' && currentDB && !loading && maskShow && currentDB?.from === 'local'\"\n            class=\"mask-prompt\"\n        >\n            <span>{{ $t('commons.service.serviceNotStarted', ['PostgreSQL']) }}</span>\n        </el-card>\n\n        <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"small\">\n            <div class=\"flex justify-center items-center gap-2 flex-wrap\">\n                {{ $t('app.checkInstalledWarn', [dashboardName]) }}\n                <el-link icon=\"Position\" @click=\"getAppDetail\" type=\"primary\">\n                    {{ $t('database.goInstall') }}\n                </el-link>\n            </div>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <PrivilegesDialog ref=\"privilegesRef\" @search=\"search\" />\n        <BindDialog ref=\"bindRef\" @search=\"search\" />\n        <PasswordDialog ref=\"passwordRef\" @search=\"search\" />\n        <RootPasswordDialog ref=\"connRef\" />\n        <UploadDialog ref=\"uploadRef\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <Backups ref=\"dialogBackupRef\" />\n\n        <AppResources ref=\"checkRef\"></AppResources>\n        <DeleteDialog ref=\"deleteRef\" @search=\"search\" />\n\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <TerminalDialog ref=\"dialogTerminalRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport BindDialog from '@/views/database/postgresql/bind/index.vue';\nimport OperateDialog from '@/views/database/postgresql/create/index.vue';\nimport DeleteDialog from '@/views/database/postgresql/delete/index.vue';\nimport PasswordDialog from '@/views/database/postgresql/password/index.vue';\nimport TerminalDialog from '@/components/terminal/database.vue';\nimport PrivilegesDialog from '@/views/database/postgresql/privileges/index.vue';\nimport RootPasswordDialog from '@/views/database/postgresql/conn/index.vue';\nimport AppResources from '@/views/database/postgresql/check/index.vue';\nimport AppStatus from '@/components/app-status/index.vue';\nimport Backups from '@/components/backup/index.vue';\nimport UploadDialog from '@/components/upload/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport Tooltip from '@/components/tooltip/index.vue';\nimport { dateFormat } from '@/utils/util';\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport {\n    deleteCheckPostgresqlDB,\n    listDatabases,\n    loadPgFromRemote,\n    searchPostgresqlDBs,\n    updatePostgresqlDescription,\n} from '@/api/modules/database';\nimport i18n from '@/lang';\nimport { Database } from '@/api/interface/database';\nimport { App } from '@/api/interface/app';\nimport { getAppPort } from '@/api/modules/app';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nimport { routerToName, routerToNameWithParams, routerToNameWithQuery } from '@/utils/router';\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref(false);\nconst maskShow = ref(true);\n\nconst appKey = ref('postgresql');\nconst appName = ref();\n\nconst isLoaded = ref(false);\nconst dbOptionsLocal = ref<Array<Database.DatabaseOption>>([]);\nconst dbOptionsRemote = ref<Array<Database.DatabaseOption>>([]);\nconst currentDB = ref<Database.DatabaseOption>();\nconst currentDBName = ref();\n\nconst checkRef = ref();\nconst deleteRef = ref();\nconst bindRef = ref();\nconst privilegesRef = ref();\nconst dialogTerminalRef = ref();\n\nconst pgadminPort = ref();\nconst dashboardName = ref();\nconst dashboardKey = ref();\nconst open = ref(false);\n\nconst appStatusRef = ref();\n\nconst dialogPortJumpRef = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'postgresql-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('postgresql-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst postgresqlContainer = ref();\nconst postgresqlStatus = ref();\nconst postgresqlVersion = ref();\n\nconst dialogRef = ref();\nconst onOpenDialog = async () => {\n    let params = {\n        from: currentDB.value.from,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst dialogBackupRef = ref();\n\nconst uploadRef = ref();\n\nconst connRef = ref();\nconst onChangeConn = async () => {\n    connRef.value!.acceptParams({\n        from: currentDB.value.from,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n    });\n};\n\nconst goRemoteDB = async () => {\n    if (currentDB.value) {\n        globalStore.setCurrentPgDB(currentDB.value.database);\n    }\n    routerToName('PostgreSQL-Remote');\n};\n\nconst goTerminal = () => {\n    dialogTerminalRef.value.acceptParams({ databaseType: currentDB.value.type, database: currentDB.value.database });\n};\n\nconst passwordRef = ref();\n\nconst onSetting = async () => {\n    if (currentDB.value) {\n        globalStore.setCurrentPgDB(currentDB.value.database);\n    }\n    routerToNameWithParams('PostgreSQL-Setting', { type: currentDB.value.type, database: currentDB.value.database });\n};\n\nconst changeDatabase = async () => {\n    for (const item of dbOptionsLocal.value) {\n        if (item.database == currentDBName.value) {\n            currentDB.value = item;\n            globalStore.setCurrentPgDB(currentDB.value.database);\n            appKey.value = item.type;\n            appName.value = item.database;\n            search();\n            appStatusRef.value?.onCheck(appKey.value, appName.value);\n            return;\n        }\n    }\n    for (const item of dbOptionsRemote.value) {\n        if (item.database == currentDBName.value) {\n            maskShow.value = false;\n            currentDB.value = item;\n            globalStore.setCurrentPgDB(currentDB.value.database);\n            break;\n        }\n    }\n    search();\n};\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        info: searchName.value,\n        database: currentDB.value.database,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    const res = await searchPostgresqlDBs(params);\n    data.value = res.data.items || [];\n    paginationConfig.total = res.data.total;\n};\n\nconst onBind = async (row: Database.PostgresqlDBInfo) => {\n    let param = {\n        name: row.name,\n        database: currentDBName.value,\n    };\n    bindRef.value.acceptParams(param);\n};\n\nconst loadDB = async () => {\n    ElMessageBox.confirm(i18n.global.t('database.loadFromRemoteHelper'), i18n.global.t('commons.msg.infoTitle'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        await loadPgFromRemote(currentDBName.value)\n            .then(() => {\n                loading.value = false;\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst goRouter = async (target: string) => {\n    if (target === 'app') {\n        routerToNameWithQuery('AppAll', { install: 'postgresql' });\n        return;\n    }\n    routerToName('PostgreSQL-Remote');\n};\n\nconst onChange = async (info: any) => {\n    await updatePostgresqlDescription({ id: info.id, description: info.description });\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst goDashboard = async () => {\n    if (pgadminPort.value === 0) {\n        dashboardName.value = 'PGAdmin4';\n        dashboardKey.value = 'pgadmin4';\n        open.value = true;\n        return;\n    }\n    dialogPortJumpRef.value.acceptParams({ port: pgadminPort.value });\n    return;\n};\n\nconst getAppDetail = () => {\n    routerToNameWithQuery('AppAll', { install: dashboardKey.value });\n};\n\nconst loadPGAdminPort = async () => {\n    const res = await getAppPort('pgadmin4', '');\n    pgadminPort.value = res.data;\n};\n\nconst checkExist = (data: App.CheckInstalled) => {\n    postgresqlStatus.value = data.status;\n    postgresqlVersion.value = data.version;\n    postgresqlContainer.value = data.containerName;\n};\n\nconst loadDBOptions = async () => {\n    try {\n        const res = await listDatabases('postgresql,postgresql-cluster');\n        let datas = res.data || [];\n        dbOptionsLocal.value = [];\n        dbOptionsRemote.value = [];\n        currentDBName.value = globalStore.currentPgDB;\n        for (const item of datas) {\n            if (currentDBName.value && item.database === currentDBName.value) {\n                currentDB.value = item;\n                if (item.from === 'local') {\n                    appKey.value = item.type;\n                    appName.value = item.database;\n                }\n            }\n            if (item.from === 'local') {\n                dbOptionsLocal.value.push(item);\n            } else {\n                dbOptionsRemote.value.push(item);\n            }\n        }\n        if (currentDB.value) {\n            if (currentDB.value?.from === 'remote') {\n                maskShow.value = false;\n            }\n            search();\n            return;\n        }\n        if (dbOptionsLocal.value.length !== 0) {\n            currentDB.value = dbOptionsLocal.value[0];\n            currentDBName.value = dbOptionsLocal.value[0].database;\n            appKey.value = dbOptionsLocal.value[0].type;\n            appName.value = dbOptionsLocal.value[0].database;\n        }\n        if (!currentDB.value && dbOptionsRemote.value.length !== 0) {\n            currentDB.value = dbOptionsRemote.value[0];\n            currentDBName.value = dbOptionsRemote.value[0].database;\n        }\n        if (currentDB.value) {\n            await search();\n        }\n        if (currentDB.value?.from === 'remote') {\n            maskShow.value = false;\n        }\n    } finally {\n        isLoaded.value = true;\n    }\n};\nconst onDelete = async (row: Database.PostgresqlDBInfo) => {\n    let param = {\n        id: row.id,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n    };\n    const res = await deleteCheckPostgresqlDB(param);\n    if (res.data && res.data.length > 0) {\n        checkRef.value.acceptParams({ items: res.data });\n    } else {\n        deleteRef.value.acceptParams({\n            id: row.id,\n            type: currentDB.value.type,\n            database: currentDBName.value,\n            name: row.name,\n        });\n    }\n};\n\nconst onChangePassword = async (row: Database.PostgresqlDBInfo) => {\n    let param = {\n        id: row.id,\n        from: row.from,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n        postgresqlName: row.name,\n        operation: 'password',\n        username: row.username,\n        password: row.password,\n    };\n    passwordRef.value.acceptParams(param);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('database.changePassword'),\n        disabled: (row: Database.PostgresqlDBInfo) => {\n            return !row.username || row.isDelete;\n        },\n        click: (row: Database.PostgresqlDBInfo) => {\n            onChangePassword(row);\n        },\n    },\n    {\n        label: i18n.global.t('database.permission'),\n        disabled: (row: Database.PostgresqlDBInfo) => {\n            return !row.username || row.isDelete;\n        },\n        click: (row: Database.PostgresqlDBInfo) => {\n            let param = {\n                database: currentDBName.value,\n                name: row.name,\n                username: row.username,\n                superUser: row.superUser,\n            };\n            privilegesRef.value.acceptParams(param);\n        },\n    },\n    {\n        label: i18n.global.t('database.backupList'),\n        disabled: (row: Database.PostgresqlDBInfo) => {\n            return row.isDelete;\n        },\n        click: (row: Database.PostgresqlDBInfo) => {\n            let params = {\n                type: currentDB.value.type,\n                name: currentDBName.value,\n                detailName: row.name,\n            };\n            dialogBackupRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('database.loadBackup'),\n        disabled: (row: Database.PostgresqlDBInfo) => {\n            return row.isDelete;\n        },\n        click: (row: Database.PostgresqlDBInfo) => {\n            let params = {\n                type: currentDB.value.type,\n                name: currentDBName.value,\n                detailName: row.name,\n                remark: row.format,\n            };\n            uploadRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Database.PostgresqlDBInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    loadDBOptions();\n    loadPGAdminPort();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.iconInTable {\n    margin-left: 5px;\n    margin-top: 3px;\n}\n.jumpAdd {\n    margin-top: 10px;\n    margin-left: 15px;\n    margin-bottom: 5px;\n    font-size: 12px;\n}\n.tagClass {\n    float: right;\n    font-size: 12px;\n    margin-top: 5px;\n}\n.optionClass {\n    min-width: 350px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/password/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro\n            v-model=\"changeVisible\"\n            :header=\"title\"\n            :resource=\"changeForm.postgresqlName\"\n            @close=\"handleClose\"\n            size=\"small\"\n        >\n            <el-form v-loading=\"loading\" ref=\"changeFormRef\" :rules=\"rules\" :model=\"changeForm\" label-position=\"top\">\n                <div v-if=\"changeForm.operation === 'password'\">\n                    <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                        <el-input disabled v-model=\"changeForm.username\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                        <el-input type=\"password\" clearable show-password v-model=\"changeForm.password\"></el-input>\n                        <span class=\"input-help\">{{ $t('commons.rule.illegalChar') }}</span>\n                    </el-form-item>\n                </div>\n            </el-form>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button :disabled=\"loading\" @click=\"changeVisible = false\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button :disabled=\"loading\" type=\"primary\" @click=\"submitChangeInfo(changeFormRef)\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DrawerPro>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmit\"></ConfirmDialog>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { deleteCheckPostgresqlDB, updatePostgresqlPassword } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\n\nconst loading = ref();\nconst changeVisible = ref(false);\ntype FormInstance = InstanceType<typeof ElForm>;\nconst changeFormRef = ref<FormInstance>();\nconst title = ref();\nconst changeForm = reactive({\n    id: 0,\n    from: '',\n    type: '',\n    database: '',\n    postgresqlName: '',\n    username: '',\n    password: '',\n    operation: '',\n    value: '',\n});\nconst confirmDialogRef = ref();\nconst rules = reactive({\n    password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],\n});\n\ninterface DialogProps {\n    id: number;\n    from: string;\n    type: string;\n    database: string;\n    postgresqlName: string;\n    username: string;\n    password: string;\n    operation: string;\n    privilege: string;\n    privilegeIPs: string;\n    value: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    title.value = i18n.global.t('database.changePassword');\n    changeForm.id = params.id;\n    changeForm.from = params.from;\n    changeForm.type = params.type;\n    changeForm.database = params.database;\n    changeForm.postgresqlName = params.postgresqlName;\n    changeForm.username = params.username;\n    changeForm.password = params.password;\n    changeForm.operation = params.operation;\n    changeForm.value = params.value;\n    changeVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    changeVisible.value = false;\n};\n\nconst submitChangeInfo = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let param = {\n            id: changeForm.id,\n            from: changeForm.from,\n            type: changeForm.type,\n            database: changeForm.database,\n            value: '',\n        };\n        if (changeForm.operation === 'password') {\n            const res = await deleteCheckPostgresqlDB(param);\n            if (res.data && res.data.length > 0) {\n                let params = {\n                    header: i18n.global.t('database.changePassword'),\n                    operationInfo: i18n.global.t('database.changePasswordHelper'),\n                    submitInputInfo: i18n.global.t('database.restartNow'),\n                };\n                confirmDialogRef.value!.acceptParams(params);\n            } else {\n                param.value = changeForm.password;\n                loading.value = true;\n                await updatePostgresqlPassword(param)\n                    .then(() => {\n                        loading.value = false;\n                        emit('search');\n                        changeVisible.value = false;\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    })\n                    .catch(() => {\n                        loading.value = false;\n                    });\n            }\n            return;\n        }\n\n        loading.value = true;\n    });\n};\n\nconst onSubmit = async () => {\n    let param = {\n        id: changeForm.id,\n        from: changeForm.from,\n        type: changeForm.type,\n        database: changeForm.database,\n        value: changeForm.password,\n    };\n    loading.value = true;\n    await updatePostgresqlPassword(param)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            changeVisible.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/privileges/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro\n            v-model=\"changeVisible\"\n            :header=\"$t('database.permission')\"\n            :resource=\"form.name\"\n            @close=\"handleClose\"\n            size=\"small\"\n        >\n            <el-form v-loading=\"loading\" :model=\"form\" label-position=\"top\">\n                <el-form-item :label=\"$t('database.userBind')\">\n                    <el-tag>\n                        {{ form.username }}\n                    </el-tag>\n                </el-form-item>\n                <el-form-item :label=\"$t('database.permission')\" prop=\"superUser\">\n                    <el-checkbox v-model=\"form.superUser\">{{ $t('database.pgSuperUser') }}</el-checkbox>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <el-button :disabled=\"loading\" @click=\"changeVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit()\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </template>\n        </DrawerPro>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { changePrivileges } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst changeVisible = ref(false);\nconst form = reactive({\n    database: '',\n    name: '',\n    username: '',\n    superUser: true,\n});\n\ninterface DialogProps {\n    database: string;\n    name: string;\n    username: string;\n    superUser: boolean;\n}\nconst acceptParams = (params: DialogProps): void => {\n    form.database = params.database;\n    form.name = params.name;\n    form.username = params.username;\n    form.superUser = params.superUser;\n    changeVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    changeVisible.value = false;\n};\n\nconst onSubmit = async () => {\n    let param = {\n        name: form.name,\n        database: form.database,\n        username: form.username,\n        superUser: form.superUser,\n    };\n    loading.value = true;\n    await changePrivileges(param)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            changeVisible.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/remote/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('database.unBindRemoteDB') + ' - ' + deleteReq.database\" size=\"small\">\n        <el-form ref=\"deleteForm\" v-loading=\"loading\" @submit.prevent>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.forceDelete\" :label=\"$t('database.unBindForce')\" />\n                <span class=\"input-help\">\n                    {{ $t('database.unBindForceHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteReq.deleteBackup\" :label=\"$t('app.deleteBackup')\" />\n                <span class=\"input-help\">\n                    {{ $t('database.deleteBackupHelper') }}\n                </span>\n            </el-form-item>\n\n            <span style=\"font-size: 12px\">{{ $t('database.unBindRemoteHelper') }}</span>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { deleteDatabase } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\n\nlet deleteReq = ref({\n    id: 0,\n    database: '',\n    deleteBackup: false,\n    forceDelete: false,\n});\nlet open = ref(false);\nlet loading = ref(false);\n\nconst deleteForm = ref<FormInstance>();\n\ninterface DialogProps {\n    id: number;\n    name: string;\n    database: string;\n}\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst acceptParams = async (prop: DialogProps) => {\n    deleteReq.value = {\n        id: prop.id,\n        database: prop.database,\n        deleteBackup: false,\n        forceDelete: false,\n    };\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    deleteDatabase(deleteReq.value)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            open.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/remote/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent :title=\"$t('database.remoteDB', 2)\" backName=\"PostgreSQL\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                    {{ $t('database.createRemoteDB') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" @sort-change=\"search\" @search=\"search\" :data=\"data\">\n                    <el-table-column show-overflow-tooltip :label=\"$t('commons.table.name')\" prop=\"name\" sortable />\n                    <el-table-column show-overflow-tooltip :label=\"$t('database.address')\" prop=\"address\" />\n                    <el-table-column :label=\"$t('commons.login.username')\" prop=\"username\" />\n                    <el-table-column :label=\"$t('commons.login.password')\" prop=\"password\">\n                        <template #default=\"{ row }\">\n                            <div class=\"flex items-center flex-wrap\">\n                                <div class=\"star-center\">\n                                    <span v-if=\"!row.showPassword\">**********</span>\n                                </div>\n                                <div>\n                                    <span v-if=\"row.showPassword\">\n                                        {{ row.password }}\n                                    </span>\n                                </div>\n                                <el-button\n                                    v-if=\"!row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = true\"\n                                    icon=\"View\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <el-button\n                                    v-if=\"row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = false\"\n                                    icon=\"Hide\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <div>\n                                    <CopyButton :content=\"row.password\" />\n                                </div>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"description\"\n                        :label=\"$t('commons.table.description')\"\n                        show-overflow-tooltip\n                    />\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        width=\"170px\"\n                        :buttons=\"buttons\"\n                        :ellipsis=\"10\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <AppResources ref=\"checkRef\"></AppResources>\n        <OperateDialog ref=\"dialogRef\" @search=\"search\" />\n        <DeleteDialog ref=\"deleteRef\" @search=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { dateFormat } from '@/utils/util';\nimport { onMounted, reactive, ref } from 'vue';\nimport { deleteCheckDatabase, searchDatabases } from '@/api/modules/database';\nimport AppResources from '@/views/database/postgresql/check/index.vue';\nimport OperateDialog from '@/views/database/postgresql/remote/operate/index.vue';\nimport DeleteDialog from '@/views/database/postgresql/remote/delete/index.vue';\nimport i18n from '@/lang';\nimport { Database } from '@/api/interface/database';\n\nconst loading = ref(false);\n\nconst dialogRef = ref();\nconst checkRef = ref();\nconst deleteRef = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'postgresql-remote-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('postgresql-remote-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        info: searchName.value,\n        type: 'postgresql',\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    const res = await searchDatabases(params);\n    data.value = res.data.items || [];\n    paginationConfig.total = res.data.total;\n};\n\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Database.DatabaseInfo> = {\n        name: '',\n        type: 'postgresql',\n        version: '18.x',\n        address: '',\n        port: 5432,\n        username: '',\n        password: '',\n        description: '',\n        timeout: 30,\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onDelete = async (row: Database.DatabaseInfo) => {\n    const res = await deleteCheckDatabase(row.id);\n    if (res.data && res.data.length > 0) {\n        checkRef.value.acceptParams({ items: res.data });\n    } else {\n        deleteRef.value.acceptParams({\n            id: row.id,\n            database: row.name,\n        });\n    }\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Database.DatabaseInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.unbind'),\n        click: (row: Database.DatabaseInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/remote/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"title\"\n        @close=\"handleClose\"\n        :resource=\"dialogData.title === 'create' ? '' : dialogData.rowData?.name\"\n        size=\"large\"\n    >\n        <el-form ref=\"formRef\" v-loading=\"loading\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-if=\"dialogData.title === 'create'\" clearable v-model.trim=\"dialogData.rowData!.name\" />\n                <el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>\n            </el-form-item>\n            <el-form-item :label=\"$t('database.version')\" prop=\"version\">\n                <el-radio-group v-model=\"dialogData.rowData!.version\" @change=\"isOK = false\">\n                    <el-radio label=\"18.x\" value=\"18.x\" />\n                    <el-radio label=\"17.x\" value=\"17.x\" />\n                    <el-radio label=\"16.x\" value=\"16.x\" />\n                    <el-radio label=\"15.x\" value=\"15.x\" />\n                    <el-radio label=\"14.x\" value=\"14.x\" />\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('database.address')\" prop=\"address\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"dialogData.rowData!.address\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\">\n                <el-input @change=\"isOK = false\" clearable v-model.number=\"dialogData.rowData!.port\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('database.initialDB')\" prop=\"initialDB\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"dialogData.rowData!.initialDB\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"dialogData.rowData!.username\" />\n                <span class=\"input-help\">{{ $t('database.pgUserHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                <el-input\n                    @change=\"isOK = false\"\n                    type=\"password\"\n                    clearable\n                    show-password\n                    v-model.trim=\"dialogData.rowData!.password\"\n                />\n            </el-form-item>\n\n            <el-form-item :label=\"$t('database.timeout')\" prop=\"timeout\">\n                <el-input-number\n                    class=\"p-w-200\"\n                    :min=\"1\"\n                    :precision=\"0\"\n                    step-strictly\n                    :step=\"1\"\n                    v-model.number=\"dialogData.rowData!.timeout\"\n                />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button @click=\"onSubmit(formRef, 'check')\">\n                    {{ $t('terminal.testConn') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"!isOK\" @click=\"onSubmit(formRef, dialogData.title)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { Database } from '@/api/interface/database';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport { addDatabase, checkDatabase, editDatabase } from '@/api/modules/database';\n\ninterface DialogProps {\n    title: string;\n    rowData?: Database.DatabaseInfo;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst isOK = ref(false);\nconst loading = ref();\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    dialogData.value.rowData.hasCA = dialogData.value.rowData.rootCert?.length !== 0;\n    title.value = i18n.global.t('database.' + dialogData.value.title + 'RemoteDB');\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    name: [Rules.simpleName, Rules.noSpace],\n    type: [Rules.requiredSelect],\n    version: [Rules.requiredSelect],\n    address: [Rules.ipV4V6OrDomain],\n    port: [Rules.port],\n    username: [Rules.requiredInput],\n    password: [Rules.requiredInput],\n\n    clientKey: [Rules.requiredInput],\n    clientCert: [Rules.requiredInput],\n    rootCert: [Rules.requiredInput],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined, operation: string) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        dialogData.value.rowData.from = 'remote';\n        loading.value = true;\n        dialogData.value.rowData.rootCert = dialogData.value.rowData.hasCA ? dialogData.value.rowData.rootCert : '';\n        if (operation === 'check') {\n            await checkDatabase(dialogData.value.rowData)\n                .then((res) => {\n                    loading.value = false;\n                    if (res.data) {\n                        isOK.value = true;\n                        MsgSuccess(i18n.global.t('terminal.connTestOk'));\n                    } else {\n                        MsgError(i18n.global.t('terminal.connTestFailed'));\n                    }\n                })\n                .catch(() => {\n                    loading.value = false;\n                    MsgError(i18n.global.t('terminal.connTestFailed'));\n                });\n        }\n\n        if (operation === 'create') {\n            await addDatabase(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n        if (operation === 'edit') {\n            await editDatabase(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/postgresql/setting/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent backName=\"PostgreSQL\">\n            <template #leftToolBar>\n                <el-text class=\"mx-1\">\n                    {{ props.database }}\n                </el-text>\n                <el-divider direction=\"vertical\" />\n                <el-button type=\"primary\" :plain=\"activeName !== 'conf'\" @click=\"jumpToConf\">\n                    {{ $t('database.confChange') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"activeName !== 'port'\" @click=\"activeName = 'port'\">\n                    {{ $t('commons.table.port') }}\n                </el-button>\n                <el-button\n                    type=\"primary\"\n                    :disabled=\"postgresqlStatus !== 'Running'\"\n                    :plain=\"activeName !== 'log'\"\n                    @click=\"activeName = 'log'\"\n                >\n                    {{ $t('commons.button.log') }}\n                </el-button>\n            </template>\n\n            <template #app>\n                <AppStatus :app-key=\"props.type\" :app-name=\"props.database\" v-model:loading=\"loading\" />\n            </template>\n\n            <template #main>\n                <div v-if=\"activeName === 'conf'\">\n                    <CodemirrorPro :heightDiff=\"320\" v-model=\"postgresqlConf\"></CodemirrorPro>\n                    <el-button type=\"primary\" class=\"mt-5\" @click=\"onSaveConf\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </div>\n                <div v-show=\"activeName === 'port'\">\n                    <el-form :model=\"baseInfo\" ref=\"panelFormRef\" label-position=\"top\">\n                        <el-row>\n                            <el-col :span=\"1\"><br /></el-col>\n                            <el-col :span=\"10\">\n                                <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\" :rules=\"Rules.port\">\n                                    <el-input clearable type=\"number\" v-model.number=\"baseInfo.port\" />\n                                </el-form-item>\n                                <el-form-item>\n                                    <el-button type=\"primary\" @click=\"onSavePort(panelFormRef)\" icon=\"Collection\">\n                                        {{ $t('commons.button.save') }}\n                                    </el-button>\n                                </el-form-item>\n                            </el-col>\n                        </el-row>\n                    </el-form>\n                </div>\n                <ContainerLog v-if=\"activeName === 'log'\" :container=\"baseInfo.containerID\" :highlightDiff=\"350\" />\n            </template>\n        </LayoutContent>\n\n        <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"small\">\n            <el-alert :closable=\"false\" :title=\"$t('database.confNotFound')\" type=\"info\">\n                <el-link icon=\"Position\" @click=\"goUpgrade()\" type=\"primary\">\n                    {{ $t('database.goUpgrade') }}\n                </el-link>\n            </el-alert>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <ConfirmDialog ref=\"confirmPortRef\" @confirm=\"onSubmitChangePort\"></ConfirmDialog>\n        <ConfirmDialog ref=\"confirmConfRef\" @confirm=\"onSubmitChangeConf\"></ConfirmDialog>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport ContainerLog from '@/components/log/container/index.vue';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { onMounted, reactive, ref } from 'vue';\nimport { loadDBFile, loadDBBaseInfo, updateDBFile } from '@/api/modules/database';\nimport { changePort, checkAppInstalled } from '@/api/modules/app';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport { routerToName } from '@/utils/router';\n\nconst loading = ref(false);\n\nconst activeName = ref('conf');\n\nconst baseInfo = reactive({\n    name: '',\n    port: 5432,\n    password: '',\n    remoteConn: false,\n    containerID: '',\n});\nconst panelFormRef = ref<FormInstance>();\nconst postgresqlConf = ref();\nconst open = ref();\n\nconst postgresqlName = ref();\nconst postgresqlStatus = ref();\nconst postgresqlVersion = ref();\n\ninterface DBProps {\n    type: string;\n    database: string;\n}\nconst props = withDefaults(defineProps<DBProps>(), {\n    type: '',\n    database: '',\n});\n\nconst jumpToConf = async () => {\n    activeName.value = 'conf';\n    loadPostgresqlConf();\n};\n\nconst onSubmitChangePort = async () => {\n    let params = {\n        key: props.type,\n        name: props.database,\n        port: baseInfo.port,\n    };\n    loading.value = true;\n    await changePort(params)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst confirmPortRef = ref();\nconst onSavePort = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    const result = await formEl.validateField('port', callback);\n    if (!result) {\n        return;\n    }\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmPortRef.value!.acceptParams(params);\n    return;\n};\nfunction callback(error: any) {\n    if (error) {\n        return error.message;\n    } else {\n        return;\n    }\n}\n\nconst onSubmitChangeConf = async () => {\n    let param = {\n        type: props.type,\n        database: props.database,\n        file: postgresqlConf.value,\n    };\n    loading.value = true;\n    await updateDBFile(param)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst confirmConfRef = ref();\nconst onSaveConf = async () => {\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmConfRef.value!.acceptParams(params);\n    return;\n};\n\nconst loadBaseInfo = async () => {\n    const res = await loadDBBaseInfo(props.type, props.database);\n    postgresqlName.value = res.data?.name;\n    baseInfo.port = res.data?.port;\n    baseInfo.containerID = res.data?.containerName;\n    loadPostgresqlConf();\n};\n\nconst loadPostgresqlConf = async () => {\n    await loadDBFile(props.type + '-conf', props.database)\n        .then((res) => {\n            loading.value = false;\n            postgresqlConf.value = res.data;\n        })\n        .catch(() => {\n            open.value = true;\n            loading.value = false;\n        });\n};\n\nconst goUpgrade = () => {\n    routerToName('AppUpgrade');\n};\n\nconst onLoadInfo = async () => {\n    await checkAppInstalled(props.type, props.database).then((res) => {\n        postgresqlName.value = res.data.name;\n        postgresqlStatus.value = res.data.status;\n        postgresqlVersion.value = res.data.version;\n        loadBaseInfo();\n    });\n};\n\nonMounted(() => {\n    onLoadInfo();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/redis/check/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"large\">\n        <el-row>\n            <el-col :span=\"20\" :offset=\"2\" v-if=\"open\">\n                <el-alert\n                    type=\"error\"\n                    :description=\"$t('app.deleteHelper', [$t('menu.database')])\"\n                    center\n                    show-icon\n                    :closable=\"false\"\n                />\n                <br />\n                <el-descriptions border :column=\"1\" class=\"mt-5\">\n                    <el-descriptions-item label-class-name=\"check-label\" class-name=\"check-content\" min-width=\"60px\">\n                        <template #label>\n                            <a href=\"javascript:void(0);\" class=\"check-label-a\" @click=\"toApp()\">\n                                {{ $t('app.app') }}\n                            </a>\n                        </template>\n                        <pre>{{ installData.join('\\n') }}</pre>\n                    </el-descriptions-item>\n                </el-descriptions>\n            </el-col>\n        </el-row>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { routerToName } from '@/utils/router';\nimport { ref } from 'vue';\n\ninterface InstallProps {\n    items: Array<string>;\n}\nconst installData = ref();\nlet open = ref(false);\n\nconst acceptParams = (props: InstallProps) => {\n    installData.value = props.items;\n    open.value = true;\n};\n\nconst toApp = () => {\n    routerToName('AppInstalled');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/redis/command/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('database.redisQuickCmd')\" @close=\"handleClose\" size=\"large\">\n        <template #content>\n            <el-button type=\"primary\" @click=\"handleCmdAdd()\">\n                {{ $t('commons.button.add') }}\n            </el-button>\n            <el-button @click=\"batchDelete(null)\">\n                {{ $t('commons.button.delete') }}\n            </el-button>\n            <el-table :data=\"data\" class=\"mt-5\" @selection-change=\"handleSelectionChange\">\n                <el-table-column type=\"selection\" fix />\n                <el-table-column :label=\"$t('commons.table.name')\" min-width=\"50\" show-overflow-tooltip>\n                    <template #default=\"{ row }\">\n                        <el-input v-if=\"row.lineStatus === 'create' || row.lineStatus === 'edit'\" v-model=\"row.name\" />\n                        <span v-else>{{ row.name }}</span>\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('terminal.quickCommand')\" min-width=\"120\" show-overflow-tooltip>\n                    <template #default=\"{ row }\">\n                        <el-input\n                            v-if=\"row.lineStatus === 'create' || row.lineStatus === 'edit'\"\n                            v-model=\"row.command\"\n                        />\n                        <span v-else>{{ row.command }}</span>\n                    </template>\n                </el-table-column>\n                <el-table-column min-width=\"40\">\n                    <template #default=\"scope\">\n                        <el-button\n                            v-if=\"scope.row.lineStatus === 'create' || scope.row.lineStatus === 'edit'\"\n                            link\n                            type=\"primary\"\n                            @click=\"handleCmdSave(scope.row)\"\n                        >\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                        <el-button\n                            v-if=\"!scope.row.lineStatus || scope.row.lineStatus === 'saved'\"\n                            link\n                            type=\"primary\"\n                            @click=\"scope.row.lineStatus = 'edit'\"\n                        >\n                            {{ $t('commons.button.edit') }}\n                        </el-button>\n                        <el-button\n                            v-if=\"scope.row.lineStatus === 'create' || scope.row.lineStatus === 'edit'\"\n                            link\n                            type=\"primary\"\n                            @click=\"search()\"\n                        >\n                            {{ $t('commons.button.cancel') }}\n                        </el-button>\n                        <el-button\n                            v-if=\"scope.row.lineStatus !== 'create' && scope.row.lineStatus !== 'edit'\"\n                            link\n                            type=\"primary\"\n                            @click=\"handleCmdDelete(scope.$index)\"\n                        >\n                            {{ $t('commons.button.delete') }}\n                        </el-button>\n                    </template>\n                </el-table-column>\n            </el-table>\n        </template>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <OpDialog ref=\"opRef\" @search=\"search\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { Command } from '@/api/interface/command';\nimport { deleteCommand, getCommandPage, addCommand, editCommand } from '@/api/modules/command';\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\n\nconst drawerVisible = ref();\nconst loading = ref();\nconst data = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n    orderBy: 'name',\n    order: 'ascending',\n});\nconst opRef = ref();\n\nconst emit = defineEmits(['reload']);\n\nconst acceptParams = () => {\n    drawerVisible.value = true;\n    search();\n};\n\nconst handleSelectionChange = (val: any[]) => {\n    selects.value = val;\n};\n\nconst handleCmdAdd = () => {\n    let item = {\n        name: '',\n        command: '',\n        type: 'redis',\n        lineStatus: 'create',\n    };\n    data.value.push(item);\n};\nconst handleCmdDelete = (index: number) => {\n    batchDelete(data.value[index]);\n};\n\nconst handleCmdSave = async (row: any) => {\n    if (row.name === '' || row.command === '') {\n        MsgError(i18n.global.t('toolbox.device.hostHelper'));\n        return;\n    }\n    loading.value = true;\n    if (row.lineStatus === 'create') {\n        await addCommand(row)\n            .then(() => {\n                loading.value = false;\n                row.lineStatus = 'saved';\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n        return;\n    }\n    await editCommand(row)\n        .then(() => {\n            loading.value = false;\n            row.lineStatus = 'saved';\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst batchDelete = async (row: Command.CommandInfo | null) => {\n    let names = [];\n    let ids = [];\n    if (row) {\n        ids = [row.id];\n        names = [row.name];\n    } else {\n        selects.value.forEach((item: Command.CommandInfo) => {\n            ids.push(item.id);\n            names.push(item.name);\n        });\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('terminal.quickCommand'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteCommand,\n        params: { ids: ids },\n    });\n};\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n        info: '',\n        type: 'redis',\n    };\n    loading.value = true;\n    await getCommandPage(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n    emit('reload');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/redis/conn/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"dialogVisible\" :header=\"$t('database.databaseConnInfo')\" @close=\"handleClose\" size=\"small\">\n        <el-form @submit.prevent v-loading=\"loading\" ref=\"formRef\" :model=\"form\" label-position=\"top\">\n            <el-form-item v-if=\"form.from === 'local'\">\n                <template #label>\n                    <div class=\"flex items-center justify-between\">\n                        <span>{{ $t('database.containerConn') }}</span>\n                        <el-button link @click=\"copyConnURL(true)\" icon=\"DocumentCopy\">\n                            {{ $t('database.copyConnURL') }}\n                        </el-button>\n                    </div>\n                </template>\n                <el-card class=\"mini-border-card\">\n                    <el-descriptions :column=\"1\">\n                        <el-descriptions-item :label=\"$t('database.connAddress')\">\n                            <el-tooltip\n                                v-if=\"loadRedisInfo(true).length > 48\"\n                                :content=\"loadRedisInfo(true)\"\n                                placement=\"top\"\n                            >\n                                {{ loadRedisInfo(true).substring(0, 48) }}...\n                            </el-tooltip>\n                            <span else>\n                                {{ loadRedisInfo(true) }}\n                            </span>\n                            <CopyButton :content=\"loadRedisInfo(true)\" />\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.port')\">\n                            6379\n                            <CopyButton content=\"6379\" />\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </el-card>\n                <span class=\"input-help\">\n                    {{ $t('database.containerConnHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <template #label>\n                    <div class=\"flex items-center justify-between\">\n                        <span>{{ $t('database.remoteConn') }}</span>\n                        <el-button link @click=\"copyConnURL(false)\" icon=\"DocumentCopy\">\n                            {{ $t('database.copyConnURL') }}\n                        </el-button>\n                    </div>\n                </template>\n                <el-card class=\"mini-border-card\">\n                    <el-descriptions :column=\"1\">\n                        <el-descriptions-item :label=\"$t('database.connAddress')\">\n                            <el-tooltip\n                                v-if=\"loadRedisInfo(false).length > 48\"\n                                :content=\"loadRedisInfo(false)\"\n                                placement=\"top\"\n                            >\n                                {{ loadRedisInfo(false).substring(0, 48) }}...\n                            </el-tooltip>\n                            <span else>\n                                {{ loadRedisInfo(false) }}\n                            </span>\n                            <CopyButton :content=\"loadRedisInfo(false)\" />\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.port')\">\n                            {{ form.port }}\n                            <CopyButton :content=\"form.port + ''\" />\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </el-card>\n                <span class=\"input-help\">\n                    {{ $t('database.remoteConnHelper2') }}\n                </span>\n            </el-form-item>\n\n            <el-divider border-style=\"dashed\" />\n            <el-form-item :label=\"$t('commons.login.password')\" v-if=\"form.from === 'local'\" prop=\"password\">\n                <el-input\n                    style=\"width: calc(100% - 205px)\"\n                    type=\"password\"\n                    show-password\n                    clearable\n                    v-model.trim=\"form.password\"\n                />\n                <el-button-group>\n                    <CopyButton class=\"copy_button\" :isIcon=\"false\" :content=\"form.password\" />\n                    <el-button @click=\"random\">\n                        {{ $t('commons.button.random') }}\n                    </el-button>\n                </el-button-group>\n            </el-form-item>\n\n            <div v-if=\"form.from !== 'local'\">\n                <el-form-item :label=\"$t('commons.login.password')\">\n                    <el-tag>{{ form.password }}</el-tag>\n                    <CopyButton :content=\"form.password\" />\n                </el-form-item>\n            </div>\n        </el-form>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmit\"></ConfirmDialog>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"dialogVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading || form.status !== 'Running'\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { changeRedisPassword, getDatabase } from '@/api/modules/database';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { getAppConnInfo } from '@/api/modules/app';\nimport { MsgSuccess } from '@/utils/message';\nimport { getRandomStr, copyText } from '@/utils/util';\nimport { getAgentSettingInfo } from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\n\nconst dialogVisible = ref(false);\nconst form = reactive({\n    status: '',\n    type: '',\n    systemIP: '',\n    password: '',\n    serviceName: '',\n    containerName: '',\n    port: 0,\n\n    from: '',\n    database: '',\n    remoteIP: '',\n});\n\nconst confirmDialogRef = ref();\n\nconst emit = defineEmits(['checkExist', 'closeTerminal']);\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\ninterface DialogProps {\n    from: string;\n    type: string;\n    database: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    form.password = '';\n    form.from = params.from;\n    form.type = params.type;\n    form.database = params.database;\n    loadPassword();\n    dialogVisible.value = true;\n};\nconst handleClose = () => {\n    dialogVisible.value = false;\n};\n\nconst random = async () => {\n    form.password = getRandomStr(16);\n};\n\nconst loadPassword = async () => {\n    if (form.from === 'local') {\n        const res = await getAppConnInfo(form.type, form.database);\n        form.status = res.data.status;\n        form.password = res.data.password || '';\n        form.port = res.data.port || 3306;\n        form.serviceName = res.data.serviceName || '';\n        form.containerName = res.data.containerName || '';\n        loadSystemIP();\n        return;\n    }\n    const res = await getDatabase(form.database);\n    form.password = res.data.password || '';\n    form.port = res.data.port || 3306;\n    form.password = res.data.password;\n    form.remoteIP = res.data.address;\n};\n\nconst loadSystemIP = async () => {\n    const res = await getAgentSettingInfo();\n    form.systemIP = res.data.systemIP || globalStore.currentNodeAddr || i18n.global.t('database.localIP');\n};\n\nfunction loadRedisInfo(isContainer: boolean) {\n    if (isContainer) {\n        return form.from === 'local' ? form.containerName : form.systemIP;\n    } else {\n        return form.from === 'local' ? form.systemIP : form.remoteIP;\n    }\n}\n\nconst copyConnURL = (isContainer: boolean) => {\n    const host = loadRedisInfo(isContainer);\n    const port = isContainer && form.from === 'local' ? 6379 : form.port;\n    const encodedPassword = encodeURIComponent(form.password);\n    copyText(`redis://:${encodedPassword}@${host}:${port}`);\n};\n\nconst onSubmit = async () => {\n    loading.value = true;\n    emit('closeTerminal');\n    await changeRedisPassword(form.database, form.password)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            dialogVisible.value = false;\n            emit('checkExist');\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let params = {\n            header: i18n.global.t('database.confChange'),\n            operationInfo: i18n.global.t('database.restartNowHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialogRef.value!.acceptParams(params);\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.copy_button {\n    border-radius: 0px;\n    border-left-width: 0px;\n}\n:deep(.el-input__wrapper) {\n    border-top-right-radius: 0px;\n    border-bottom-right-radius: 0px;\n}\n:deep(.el-form-item__label) {\n    width: 100%;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/database/redis/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <div class=\"app-status mt-5\" v-if=\"currentDB && currentDB.from === 'remote'\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag class=\"float-left\" effect=\"dark\" type=\"success\">Redis</el-tag>\n                        <el-tag>{{ $t('app.version') }}: {{ currentDB?.version }}</el-tag>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n        <LayoutContent title=\"Redis\">\n            <template #app v-if=\"currentDB?.from === 'local'\">\n                <AppStatus\n                    :app-key=\"currentDB.type\"\n                    :app-name=\"appName\"\n                    v-model:loading=\"loading\"\n                    @before=\"onBefore\"\n                    @after=\"onAfter\"\n                    @setting=\"onSetting\"\n                    ref=\"appStatusRef\"\n                ></AppStatus>\n            </template>\n            <template #leftToolBar v-if=\"!isOnSetting\">\n                <el-button v-if=\"currentDB\" type=\"primary\" plain @click=\"onLoadConn\">\n                    {{ $t('database.databaseConnInfo') }}\n                </el-button>\n                <el-button @click=\"goRemoteDB()\" type=\"primary\" plain>\n                    {{ $t('database.remoteDB') }}\n                </el-button>\n            </template>\n            <template #rightToolBar v-if=\"!isOnSetting\">\n                <el-select\n                    v-model=\"currentDBName\"\n                    @change=\"changeDatabase()\"\n                    class=\"p-w-200 ml-5\"\n                    v-if=\"currentDB\"\n                    placement=\"bottom-end\"\n                >\n                    <template #prefix>{{ $t('commons.table.type') }}</template>\n                    <el-option-group :label=\"$t('commons.table.local')\">\n                        <div v-for=\"(item, index) in dbOptionsLocal\" :key=\"index\">\n                            <el-option v-if=\"item.from === 'local'\" :value=\"item.database\" class=\"optionClass\">\n                                <span v-if=\"item.database.length < 25\">{{ item.database }}</span>\n                                <el-tooltip v-else :content=\"item.database\" placement=\"top\">\n                                    <span>{{ item.database.substring(0, 25) }}...</span>\n                                </el-tooltip>\n                            </el-option>\n                        </div>\n                        <el-button link type=\"primary\" class=\"jumpAdd\" @click=\"goRouter('app')\" icon=\"Position\">\n                            {{ $t('database.goInstall') }}\n                        </el-button>\n                    </el-option-group>\n                    <el-option-group :label=\"$t('database.remote')\">\n                        <div v-for=\"(item, index) in dbOptionsRemote\" :key=\"index\">\n                            <el-option v-if=\"item.from === 'remote'\" :value=\"item.database\" class=\"optionClass\">\n                                <span v-if=\"item.database.length < 25\">{{ item.database }}</span>\n                                <el-tooltip v-else :content=\"item.database\" placement=\"top\">\n                                    <span>{{ item.database.substring(0, 25) }}...</span>\n                                </el-tooltip>\n                            </el-option>\n                        </div>\n                        <el-button link type=\"primary\" class=\"jumpAdd\" @click=\"goRouter('remote')\" icon=\"Position\">\n                            {{ $t('database.createRemoteDB') }}\n                        </el-button>\n                    </el-option-group>\n                </el-select>\n            </template>\n            <template #main v-if=\"!isOnSetting\">\n                <div v-if=\"currentDB && !isOnSetting\" class=\"mt-5\">\n                    <Terminal\n                        :style=\"{ height: `calc(100vh - ${loadHeight()})` }\"\n                        :key=\"isRefresh\"\n                        ref=\"terminalRef\"\n                        v-show=\"redisStatus === 'Running' && terminalShow\"\n                    />\n                    <el-empty\n                        v-if=\"redisStatus !== 'Running' || (currentDB.from === 'remote' && !redisCliExist)\"\n                        :image-size=\"80\"\n                        :style=\"{ height: `calc(100vh - ${loadHeight()})`, 'background-color': '#000' }\"\n                        :description=\"loadErrMsg()\"\n                    >\n                        <el-button v-if=\"currentDB.from === 'remote'\" type=\"primary\" @click=\"installCli\">\n                            {{ $t('commons.button.enable') }}\n                        </el-button>\n                    </el-empty>\n                    <div>\n                        <el-select v-model=\"quickCmd\" clearable filterable @change=\"quickInput\" style=\"width: 90%\">\n                            <template #prefix>{{ $t('terminal.quickCommand') }}</template>\n                            <el-option\n                                v-for=\"cmd in quickCmdList\"\n                                :key=\"cmd.id\"\n                                :label=\"cmd.name\"\n                                :value=\"cmd.command\"\n                            />\n                        </el-select>\n                        <el-button @click=\"onSetQuickCmd\" icon=\"Setting\" style=\"width: 10%\">\n                            {{ $t('commons.button.set') }}\n                        </el-button>\n                    </div>\n                </div>\n\n                <div class=\"app-warn\" v-if=\"isLoaded && dbOptionsLocal.length === 0 && dbOptionsRemote.length === 0\">\n                    <div class=\"flex flex-col gap-2 items-center justify-center w-full sm:flex-row\">\n                        <span>{{ $t('app.checkInstalledWarn', ['Redis']) }}</span>\n                        <span @click=\"goRouter('app')\" class=\"flex items-center justify-center gap-0.5\">\n                            <el-icon><Position /></el-icon>\n                            {{ $t('database.goInstall') }}\n                        </span>\n                    </div>\n                    <div>\n                        <img src=\"@/assets/images/no_app.svg\" />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <Setting ref=\"settingRef\" style=\"margin-top: 30px\" />\n        <Conn ref=\"connRef\" @check-exist=\"reOpenTerminal\" @close-terminal=\"closeTerminal(true)\" />\n\n        <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"small\">\n            <div class=\"flex justify-center items-center gap-2 flex-wrap\">\n                {{ $t('app.checkInstalledWarn', ['Redis-Commander']) }}\n                <el-link icon=\"Position\" @click=\"getAppDetail('redis-commander')\" type=\"primary\">\n                    {{ $t('database.goInstall') }}\n                </el-link>\n            </div>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                </span>\n            </template>\n        </DialogPro>\n\n        <QuickCmd ref=\"dialogQuickCmdRef\" @reload=\"loadQuickCmd\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport Setting from '@/views/database/redis/setting/index.vue';\nimport Conn from '@/views/database/redis/conn/index.vue';\nimport Terminal from '@/components/terminal/index.vue';\nimport AppStatus from '@/components/app-status/index.vue';\nimport QuickCmd from '@/views/database/redis/command/index.vue';\nimport { nextTick, onBeforeUnmount, onMounted, ref } from 'vue';\nimport { checkAppInstalled } from '@/api/modules/app';\nimport { GlobalStore } from '@/store';\nimport { listDatabases, checkRedisCli, installRedisCli } from '@/api/modules/database';\nimport { Database } from '@/api/interface/database';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { getCommandList } from '@/api/modules/command';\nimport { routerToName, routerToNameWithQuery } from '@/utils/router';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\n\nconst terminalRef = ref<InstanceType<typeof Terminal> | null>(null);\nconst settingRef = ref();\nconst isOnSetting = ref(false);\nconst redisIsExist = ref(false);\nconst redisStatus = ref();\nconst terminalShow = ref(false);\n\nconst appStatusRef = ref();\n\nconst open = ref(false);\n\nconst redisCliExist = ref();\n\nconst appKey = ref('redis');\nconst appName = ref();\n\nconst isLoaded = ref(false);\nconst dbOptionsLocal = ref<Array<Database.DatabaseOption>>([]);\nconst dbOptionsRemote = ref<Array<Database.DatabaseOption>>([]);\nconst currentDB = ref<Database.DatabaseOption>();\nconst currentDBName = ref();\n\nconst quickCmd = ref();\nconst quickCmdList = ref([]);\nconst dialogQuickCmdRef = ref();\n\nconst isRefresh = ref();\n\nconst onSetting = async () => {\n    isOnSetting.value = true;\n    terminalRef.value?.onClose(false);\n    terminalShow.value = false;\n    settingRef.value!.acceptParams({ status: redisStatus.value, database: currentDBName.value, type: appKey.value });\n};\n\nconst loadHeight = () => {\n    return globalStore.openMenuTabs ? '470px' : '380px';\n};\n\nconst getAppDetail = (key: string) => {\n    routerToNameWithQuery('AppAll', { install: key });\n};\nconst goRemoteDB = async () => {\n    if (currentDB.value) {\n        globalStore.setCurrentRedisDB(currentDBName.value);\n    }\n    routerToName('Redis-Remote');\n};\n\nconst connRef = ref();\nconst onLoadConn = async () => {\n    connRef.value!.acceptParams({\n        from: currentDB.value.from,\n        type: currentDB.value.type,\n        database: currentDBName.value,\n    });\n};\n\nconst goRouter = async (target: string) => {\n    if (target === 'app') {\n        routerToNameWithQuery('AppAll', { install: 'redis' });\n        return;\n    }\n    routerToName('Redis-Remote');\n};\n\nconst changeDatabase = async () => {\n    for (const item of dbOptionsLocal.value) {\n        if (item.database == currentDBName.value) {\n            currentDB.value = item;\n            globalStore.setCurrentRedisDB(currentDB.value.database);\n            appKey.value = item.type;\n            appName.value = item.database;\n            appStatusRef.value?.onCheck(appKey.value, appName.value);\n            reOpenTerminal();\n            return;\n        }\n    }\n    for (const item of dbOptionsRemote.value) {\n        if (item.database == currentDBName.value) {\n            currentDB.value = item;\n            globalStore.setCurrentRedisDB(currentDB.value.database);\n            break;\n        }\n    }\n    reOpenTerminal();\n};\n\nconst loadDBOptions = async () => {\n    try {\n        const res = await listDatabases('redis,redis-cluster');\n        let datas = res.data || [];\n        dbOptionsLocal.value = [];\n        dbOptionsRemote.value = [];\n        currentDBName.value = globalStore.currentRedisDB;\n        for (const item of datas) {\n            if (currentDBName.value && item.database === currentDBName.value) {\n                currentDB.value = item;\n                if (item.from === 'local') {\n                    appKey.value = item.type;\n                    appName.value = item.database;\n                }\n            }\n            if (item.from === 'local') {\n                dbOptionsLocal.value.push(item);\n            } else {\n                dbOptionsRemote.value.push(item);\n            }\n        }\n        if (currentDB.value) {\n            reOpenTerminal();\n            return;\n        }\n        if (dbOptionsLocal.value.length !== 0) {\n            currentDB.value = dbOptionsLocal.value[0];\n            currentDBName.value = dbOptionsLocal.value[0].database;\n            appKey.value = dbOptionsLocal.value[0].type;\n            appName.value = dbOptionsLocal.value[0].database;\n        }\n        if (!currentDB.value && dbOptionsRemote.value.length !== 0) {\n            currentDB.value = dbOptionsRemote.value[0];\n            currentDBName.value = dbOptionsRemote.value[0].database;\n        }\n        if (currentDB.value) {\n            reOpenTerminal();\n        }\n    } finally {\n        isLoaded.value = true;\n    }\n};\n\nconst loadErrMsg = () => {\n    return currentDB.value.from === 'local'\n        ? i18n.global.t('commons.service.serviceNotStarted', ['Redis'])\n        : i18n.global.t('database.redisCliHelper');\n};\nconst reOpenTerminal = async () => {\n    closeTerminal(false);\n    initTerminal();\n};\n\nconst initTerminal = async () => {\n    loading.value = true;\n    if (currentDB.value.from === 'remote') {\n        if (!redisCliExist.value) {\n            loading.value = false;\n            return;\n        }\n        isRefresh.value = !isRefresh.value;\n        loading.value = false;\n        redisIsExist.value = true;\n        nextTick(() => {\n            terminalShow.value = true;\n            redisStatus.value = 'Running';\n            terminalRef.value.acceptParams({\n                endpoint: '/api/v2/containers/exec',\n                args: `source=redis&name=${currentDBName.value}&from=${currentDB.value.from}`,\n                error: '',\n                initCmd: '',\n            });\n        });\n        isRefresh.value = !isRefresh.value;\n        return;\n    }\n    await checkAppInstalled(currentDB.value.type, currentDBName.value)\n        .then((res) => {\n            redisIsExist.value = res.data.isExist;\n            redisStatus.value = res.data.status;\n            loading.value = false;\n            nextTick(() => {\n                if (res.data.status === 'Running') {\n                    terminalShow.value = true;\n                    terminalRef.value.acceptParams({\n                        endpoint: '/api/v2/containers/exec',\n                        args: `source=${currentDB.value.type}&name=${currentDBName.value}&from=${currentDB.value.from}`,\n                        error: '',\n                        initCmd: '',\n                    });\n                }\n            });\n            isRefresh.value = !isRefresh.value;\n        })\n        .catch(() => {\n            closeTerminal(false);\n            loading.value = false;\n        });\n};\nconst closeTerminal = async (isKeepShow: boolean) => {\n    isRefresh.value = !isRefresh.value;\n    terminalRef.value?.onClose(isKeepShow);\n    terminalShow.value = isKeepShow;\n};\n\nconst checkCliValid = async () => {\n    await checkRedisCli()\n        .then((res) => {\n            redisCliExist.value = res.data;\n            loadDBOptions();\n        })\n        .catch(() => {\n            loadDBOptions();\n        });\n};\nconst installCli = async () => {\n    loading.value = true;\n    await installRedisCli()\n        .then(() => {\n            loading.value = false;\n            redisCliExist.value = true;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            reOpenTerminal();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadQuickCmd = async () => {\n    const res = await getCommandList('redis');\n    quickCmdList.value = res.data || [];\n};\n\nconst quickInput = (val: any) => {\n    if (val) {\n        terminalRef.value?.sendMsg(val);\n        quickCmd.value = '';\n    }\n};\n\nconst onSetQuickCmd = () => {\n    dialogQuickCmdRef.value.acceptParams();\n};\n\nonMounted(() => {\n    loadQuickCmd();\n    checkCliValid();\n});\nconst onBefore = () => {\n    closeTerminal(false);\n};\nconst onAfter = () => {\n    initTerminal();\n};\nonBeforeUnmount(() => {\n    closeTerminal(false);\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.jumpAdd {\n    margin-top: 10px;\n    margin-left: 15px;\n    margin-bottom: 5px;\n    font-size: 12px;\n}\n.tagClass {\n    float: right;\n    font-size: 12px;\n    margin-top: 5px;\n}\n.optionClass {\n    min-width: 350px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/database/redis/remote/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('database.unBindRemoteDB') + ' - ' + deleteMysqlReq.database\" size=\"small\">\n        <el-form ref=\"deleteForm\" v-loading=\"loading\" @submit.prevent>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteMysqlReq.forceDelete\" :label=\"$t('database.unBindForce')\" />\n                <span class=\"input-help\">\n                    {{ $t('database.unBindForceHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"deleteMysqlReq.deleteBackup\" :label=\"$t('app.deleteBackup')\" />\n                <span class=\"input-help\">\n                    {{ $t('database.deleteBackupHelper') }}\n                </span>\n            </el-form-item>\n\n            <span style=\"font-size: 12px\">{{ $t('database.unBindRemoteHelper') }}</span>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { deleteDatabase } from '@/api/modules/database';\nimport { MsgSuccess } from '@/utils/message';\n\nlet deleteMysqlReq = ref({\n    id: 0,\n    database: '',\n    deleteBackup: false,\n    forceDelete: false,\n});\nlet open = ref(false);\nlet loading = ref(false);\n\nconst deleteForm = ref<FormInstance>();\n\ninterface DialogProps {\n    id: number;\n    name: string;\n    database: string;\n}\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst acceptParams = async (prop: DialogProps) => {\n    deleteMysqlReq.value = {\n        id: prop.id,\n        database: prop.database,\n        deleteBackup: false,\n        forceDelete: false,\n    };\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    deleteDatabase(deleteMysqlReq.value)\n        .then(() => {\n            loading.value = false;\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            open.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/redis/remote/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent :title=\"$t('database.remoteDB', 2)\" backName=\"Redis\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                    {{ $t('database.createRemoteDB') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" @sort-change=\"search\" @search=\"search\" :data=\"data\">\n                    <el-table-column show-overflow-tooltip :label=\"$t('commons.table.name')\" prop=\"name\" sortable />\n                    <el-table-column show-overflow-tooltip :label=\"$t('database.address')\" prop=\"address\" />\n                    <el-table-column :label=\"$t('commons.login.password')\" prop=\"password\">\n                        <template #default=\"{ row }\">\n                            <div class=\"flex items-center flex-wrap\">\n                                <div class=\"star-center\">\n                                    <span v-if=\"!row.showPassword\">**********</span>\n                                </div>\n                                <div>\n                                    <span v-if=\"row.showPassword\">\n                                        {{ row.password }}\n                                    </span>\n                                </div>\n                                <el-button\n                                    v-if=\"!row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = true\"\n                                    icon=\"View\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <el-button\n                                    v-if=\"row.showPassword\"\n                                    link\n                                    @click=\"row.showPassword = false\"\n                                    icon=\"Hide\"\n                                    class=\"ml-1.5\"\n                                ></el-button>\n                                <div>\n                                    <CopyButton :content=\"row.password\" />\n                                </div>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"description\"\n                        :label=\"$t('commons.table.description')\"\n                        show-overflow-tooltip\n                    />\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        width=\"170px\"\n                        :buttons=\"buttons\"\n                        :ellipsis=\"10\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <AppResources ref=\"checkRef\"></AppResources>\n        <OperateDialog ref=\"dialogRef\" @search=\"search\" />\n        <DeleteDialog ref=\"deleteRef\" @search=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { dateFormat } from '@/utils/util';\nimport { onMounted, reactive, ref } from 'vue';\nimport { deleteCheckDatabase, searchDatabases } from '@/api/modules/database';\nimport AppResources from '@/views/database/redis/check/index.vue';\nimport OperateDialog from '@/views/database/redis/remote/operate/index.vue';\nimport DeleteDialog from '@/views/database/redis/remote/delete/index.vue';\nimport i18n from '@/lang';\nimport { Database } from '@/api/interface/database';\n\nconst loading = ref(false);\n\nconst dialogRef = ref();\nconst checkRef = ref();\nconst deleteRef = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'redis-remote-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('redis-remote-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        info: searchName.value,\n        type: 'redis',\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    const res = await searchDatabases(params);\n    data.value = res.data.items || [];\n    paginationConfig.total = res.data.total;\n};\n\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Database.DatabaseInfo> = {\n        name: '',\n        type: 'redis',\n        from: 'remote',\n        version: '7.2.x',\n        address: '',\n        port: 6379,\n        username: '-',\n        password: '',\n        description: '',\n        timeout: 30,\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onDelete = async (row: Database.DatabaseInfo) => {\n    const res = await deleteCheckDatabase(row.id);\n    if (res.data && res.data.length > 0) {\n        checkRef.value.acceptParams({ items: res.data });\n    } else {\n        deleteRef.value.acceptParams({\n            id: row.id,\n            database: row.name,\n        });\n    }\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Database.DatabaseInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.unbind'),\n        click: (row: Database.DatabaseInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/redis/remote/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"title\"\n        @close=\"handleClose\"\n        :resource=\"dialogData.title === 'create' ? '' : dialogData.rowData?.name\"\n        size=\"large\"\n    >\n        <el-form ref=\"formRef\" v-loading=\"loading\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-if=\"dialogData.title === 'create'\" clearable v-model.trim=\"dialogData.rowData!.name\" />\n                <el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>\n            </el-form-item>\n            <el-form-item :label=\"$t('database.version')\" prop=\"version\">\n                <el-radio-group v-model=\"dialogData.rowData!.version\" @change=\"isOK = false\">\n                    <el-radio label=\"6.x\" value=\"6.x\" />\n                    <el-radio label=\"7.x\" value=\"7.x\" />\n                    <el-radio label=\"8.x\" value=\"8.x\" />\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('database.address')\" prop=\"address\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"dialogData.rowData!.address\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\">\n                <el-input @change=\"isOK = false\" clearable v-model.number=\"dialogData.rowData!.port\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                <el-input\n                    @change=\"isOK = false\"\n                    type=\"password\"\n                    clearable\n                    show-password\n                    v-model.trim=\"dialogData.rowData!.password\"\n                />\n            </el-form-item>\n            <el-form-item :label=\"$t('database.timeout')\" prop=\"timeout\">\n                <el-input-number\n                    class=\"p-w-200\"\n                    :min=\"1\"\n                    :precision=\"0\"\n                    step-strictly\n                    :step=\"1\"\n                    v-model.number=\"dialogData.rowData!.timeout\"\n                />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button @click=\"onSubmit(formRef, 'check')\">\n                    {{ $t('terminal.testConn') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"!isOK\" @click=\"onSubmit(formRef, dialogData.title)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { Database } from '@/api/interface/database';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport { addDatabase, checkDatabase, editDatabase } from '@/api/modules/database';\n\ninterface DialogProps {\n    title: string;\n    rowData?: Database.DatabaseInfo;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst isOK = ref(false);\nconst loading = ref();\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    if (dialogData.value.rowData.version.startsWith('6.')) {\n        dialogData.value.rowData.version = '6.x';\n    }\n    if (dialogData.value.rowData.version.startsWith('7.')) {\n        dialogData.value.rowData.version = '7.x';\n    }\n    if (dialogData.value.rowData.version.startsWith('8.')) {\n        dialogData.value.rowData.version = '8.x';\n    }\n    title.value = i18n.global.t('database.' + dialogData.value.title + 'RemoteDB');\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    name: [Rules.simpleName, Rules.noSpace],\n    type: [Rules.requiredSelect],\n    version: [Rules.requiredSelect],\n    address: [Rules.ipV4V6OrDomain],\n    port: [Rules.port],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined, operation: string) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        dialogData.value.rowData.from = 'remote';\n        loading.value = true;\n        dialogData.value.rowData.rootCert = dialogData.value.rowData.hasCA ? dialogData.value.rowData.rootCert : '';\n        if (operation === 'check') {\n            await checkDatabase(dialogData.value.rowData)\n                .then((res) => {\n                    loading.value = false;\n                    if (res.data) {\n                        isOK.value = true;\n                        MsgSuccess(i18n.global.t('terminal.connTestOk'));\n                    } else {\n                        MsgError(i18n.global.t('terminal.connTestFailed'));\n                    }\n                })\n                .catch(() => {\n                    loading.value = false;\n                    MsgError(i18n.global.t('terminal.connTestFailed'));\n                });\n        }\n\n        if (operation === 'create') {\n            await addDatabase(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n        if (operation === 'edit') {\n            await editDatabase(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/redis/setting/index.vue",
    "content": "<template>\n    <div v-show=\"settingShow\" v-loading=\"loading\">\n        <LayoutContent :reload=\"true\">\n            <template #leftToolBar>\n                <el-text class=\"mx-1\">\n                    {{ database }}\n                </el-text>\n                <el-divider direction=\"vertical\" />\n                <el-button\n                    type=\"primary\"\n                    :disabled=\"redisStatus !== 'Running'\"\n                    :plain=\"activeName !== 'status'\"\n                    @click=\"changeTab('status')\"\n                >\n                    {{ $t('database.currentStatus') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"activeName !== 'conf'\" @click=\"changeTab('conf')\">\n                    {{ $t('database.confChange') }}\n                </el-button>\n                <el-button\n                    type=\"primary\"\n                    :disabled=\"redisStatus !== 'Running'\"\n                    :plain=\"activeName !== 'tuning'\"\n                    @click=\"changeTab('tuning')\"\n                >\n                    {{ $t('database.performanceTuning') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"activeName !== 'port'\" @click=\"changeTab('port')\">\n                    {{ $t('commons.table.port') }}\n                </el-button>\n                <el-button\n                    type=\"primary\"\n                    :disabled=\"redisStatus !== 'Running'\"\n                    :plain=\"activeName !== 'persistence'\"\n                    @click=\"changeTab('persistence')\"\n                >\n                    {{ $t('database.persistence') }}\n                </el-button>\n            </template>\n            <template #main>\n                <div v-if=\"activeName === 'conf'\">\n                    <CodemirrorPro\n                        :heightDiff=\"340\"\n                        v-model=\"redisConf\"\n                        :placeholder=\"$t('commons.msg.noneData')\"\n                    ></CodemirrorPro>\n                    <el-button class=\"mt-5\" @click=\"getDefaultConfig()\">\n                        {{ $t('app.defaultConfig') }}\n                    </el-button>\n                    <el-button type=\"primary\" @click=\"onSaveFile\" class=\"mt-5\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                    <el-row>\n                        <el-col :span=\"8\">\n                            <el-alert\n                                v-if=\"useOld\"\n                                class=\"mt-5\"\n                                :title=\"$t('app.defaultConfigHelper')\"\n                                type=\"info\"\n                                :closable=\"false\"\n                            ></el-alert>\n                        </el-col>\n                    </el-row>\n                </div>\n                <Status v-show=\"activeName === 'status'\" ref=\"statusRef\" />\n                <div v-if=\"activeName === 'tuning'\">\n                    <el-form :model=\"form\" ref=\"formRef\" :rules=\"rules\" label-position=\"top\">\n                        <el-row class=\"mt-10\">\n                            <el-col :span=\"1\"><br /></el-col>\n                            <el-col :span=\"10\">\n                                <el-form-item :label=\"$t('database.timeout')\" prop=\"timeout\">\n                                    <el-input clearable type=\"number\" v-model.number=\"form.timeout\">\n                                        <template #append>{{ $t('commons.units.second') }}</template>\n                                    </el-input>\n                                    <span class=\"input-help\">{{ $t('database.timeoutHelper') }}</span>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('database.maxclients')\" prop=\"maxclients\">\n                                    <el-input clearable type=\"number\" v-model.number=\"form.maxclients\" />\n                                </el-form-item>\n                                <el-form-item :label=\"$t('database.maxmemory')\" prop=\"maxmemory\">\n                                    <el-input clearable type=\"number\" v-model.number=\"form.maxmemory\">\n                                        <template #append>mb</template>\n                                    </el-input>\n\n                                    <span class=\"input-help\">{{ $t('database.maxmemoryHelper') }}</span>\n                                </el-form-item>\n                                <el-form-item>\n                                    <el-button type=\"primary\" @click=\"onSubmitForm(formRef)\">\n                                        {{ $t('commons.button.save') }}\n                                    </el-button>\n                                </el-form-item>\n                            </el-col>\n                        </el-row>\n                    </el-form>\n                </div>\n                <div v-if=\"activeName === 'port'\">\n                    <el-form :model=\"form\" ref=\"portRef\" label-position=\"top\">\n                        <el-row>\n                            <el-col :span=\"1\"><br /></el-col>\n                            <el-col :span=\"10\">\n                                <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\" :rules=\"Rules.port\">\n                                    <el-input clearable type=\"number\" v-model.number=\"form.port\" />\n                                </el-form-item>\n                                <el-form-item>\n                                    <el-button @click=\"onSavePort(portRef)\" icon=\"Collection\">\n                                        {{ $t('commons.button.save') }}\n                                    </el-button>\n                                </el-form-item>\n                            </el-col>\n                        </el-row>\n                    </el-form>\n                </div>\n                <Persistence @loading=\"changeLoading\" v-show=\"activeName === 'persistence'\" ref=\"persistenceRef\" />\n            </template>\n        </LayoutContent>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"submitFile\"></ConfirmDialog>\n        <ConfirmDialog ref=\"confirmFileRef\" @confirm=\"submitFile\"></ConfirmDialog>\n        <ConfirmDialog ref=\"confirmFormRef\" @confirm=\"submitForm\"></ConfirmDialog>\n        <ConfirmDialog ref=\"confirmPortRef\" @confirm=\"onChangePort(portRef)\"></ConfirmDialog>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport Status from '@/views/database/redis/setting/status/index.vue';\nimport Persistence from '@/views/database/redis/setting/persistence/index.vue';\nimport { loadDBFile, loadRedisConf, updateRedisConf, updateDBFile } from '@/api/modules/database';\nimport i18n from '@/lang';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport { changePort, getAppDefaultConfig } from '@/api/modules/app';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref(false);\n\nconst form = reactive({\n    name: '',\n    port: 6379,\n    timeout: 0,\n    maxclients: 0,\n    maxmemory: 0,\n});\nconst rules = reactive({\n    port: [Rules.port],\n    timeout: [Rules.number, checkNumberRange(0, 9999999)],\n    maxclients: [Rules.number, checkNumberRange(1, 65504)],\n    maxmemory: [Rules.number, checkNumberRange(0, 999999)],\n});\n\nconst activeName = ref('status');\nconst statusRef = ref();\nconst persistenceRef = ref();\n\nconst useOld = ref(false);\n\nconst redisStatus = ref();\nconst database = ref();\nconst dbType = ref('redis');\n\nconst formRef = ref<FormInstance>();\nconst redisConf = ref();\nconst confirmDialogRef = ref();\n\nconst settingShow = ref<boolean>(false);\n\ninterface DialogProps {\n    database: string;\n    status: string;\n    type: string;\n}\n\nconst changeTab = (val: string) => {\n    activeName.value = val;\n    switch (val) {\n        case 'conf':\n            loadConfFile();\n            break;\n        case 'persistence':\n            persistenceRef.value!.acceptParams({\n                status: redisStatus.value,\n                database: database.value,\n                type: dbType.value,\n            });\n            break;\n        case 'tuning':\n        case 'port':\n            loadForm();\n            break;\n        case 'status':\n            statusRef.value!.acceptParams({ status: redisStatus.value, database: database.value, type: dbType.value });\n            break;\n    }\n};\n\nconst changeLoading = (status: boolean) => {\n    loading.value = status;\n};\n\nconst acceptParams = (prop: DialogProps): void => {\n    redisStatus.value = prop.status;\n    database.value = prop.database;\n    dbType.value = prop.type;\n    settingShow.value = true;\n    changeTab('status');\n};\n\nconst portRef = ref();\nconst confirmPortRef = ref();\nconst onSavePort = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    const result = await formEl.validateField('port', callback);\n    if (!result) {\n        return;\n    }\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmPortRef.value!.acceptParams(params);\n    return;\n};\nfunction callback(error: any) {\n    if (error) {\n        return error.message;\n    } else {\n        return;\n    }\n}\nconst onChangePort = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    const result = await formEl.validateField('port', callback);\n    if (!result) {\n        return;\n    }\n    let params = {\n        type: dbType.value,\n        key: dbType.value,\n        name: form.name,\n        port: form.port,\n    };\n    loading.value = true;\n    await changePort(params)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst confirmFormRef = ref();\nconst onSubmitForm = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let params = {\n            header: i18n.global.t('database.confChange'),\n            operationInfo: i18n.global.t('database.restartNowHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmFormRef.value!.acceptParams(params);\n    });\n};\nconst submitForm = async () => {\n    let param = {\n        dbType: dbType.value,\n        database: database.value,\n        timeout: form.timeout + '',\n        maxclients: form.maxclients + '',\n        maxmemory: form.maxmemory + 'mb',\n    };\n    loading.value = true;\n    await updateRedisConf(param)\n        .then(() => {\n            loadForm();\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst getDefaultConfig = async () => {\n    loading.value = true;\n    await getAppDefaultConfig(dbType.value, '')\n        .then((res) => {\n            redisConf.value = res.data;\n            useOld.value = true;\n            loading.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onSaveFile = async () => {\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper1'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRef.value!.acceptParams(params);\n};\nconst submitFile = async () => {\n    let param = {\n        type: dbType.value,\n        database: database.value,\n        file: redisConf.value,\n    };\n    loading.value = true;\n    await updateDBFile(param)\n        .then(() => {\n            useOld.value = false;\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadForm = async () => {\n    const res = await loadRedisConf(dbType.value, database.value);\n    form.name = res.data?.name;\n    form.timeout = Number(res.data?.timeout);\n    form.maxclients = Number(res.data?.maxclients);\n    form.maxmemory = Number(res.data?.maxmemory.replaceAll('mb', '')) / 1048576;\n    form.port = Number(res.data?.port);\n};\n\nconst loadConfFile = async () => {\n    useOld.value = false;\n    loading.value = true;\n    await loadDBFile(dbType.value + '-conf', database.value)\n        .then((res) => {\n            loading.value = false;\n            redisConf.value = res.data;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/redis/setting/persistence/index.vue",
    "content": "<template>\n    <div v-if=\"persistenceShow\">\n        <el-row :gutter=\"20\" style=\"margin-top: 5px\" class=\"row-box\">\n            <el-col :xs=\"24\" :sm=\"12\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card class=\"el-card\">\n                    <template #header>\n                        <div class=\"card-header\">\n                            <span>AOF {{ $t('database.persistence') }}</span>\n                        </div>\n                    </template>\n                    <el-form :model=\"form\" ref=\"formRef\" :rules=\"rules\" label-width=\"120px\">\n                        <el-row>\n                            <el-col :span=\"1\"><br /></el-col>\n                            <el-form>\n                                <el-form-item label=\"appendonly\" prop=\"appendonly\">\n                                    <el-switch\n                                        active-value=\"yes\"\n                                        inactive-value=\"no\"\n                                        v-model=\"form.appendonly\"\n                                    ></el-switch>\n                                </el-form-item>\n                                <el-form-item label=\"appendfsync\" prop=\"appendfsync\">\n                                    <el-radio-group style=\"width: 100%\" v-model=\"form.appendfsync\">\n                                        <el-radio value=\"always\">always</el-radio>\n                                        <el-radio value=\"everysec\">everysec</el-radio>\n                                        <el-radio value=\"no\">no</el-radio>\n                                    </el-radio-group>\n                                </el-form-item>\n                                <el-form-item>\n                                    <el-button type=\"primary\" @click=\"onSave(formRef, 'aof')\">\n                                        {{ $t('commons.button.save') }}\n                                    </el-button>\n                                </el-form-item>\n                            </el-form>\n                        </el-row>\n                    </el-form>\n                </el-card>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"12\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card class=\"el-card\">\n                    <template #header>\n                        <div class=\"card-header\">\n                            <span>RDB {{ $t('database.persistence') }}</span>\n                        </div>\n                    </template>\n                    <table style=\"width: 100%\" class=\"tab-table\">\n                        <tr v-for=\"(row, index) in form.saves\" :key=\"index\">\n                            <td width=\"32%\">\n                                <el-input type=\"number\" v-model=\"row.second\"></el-input>\n                            </td>\n                            <td width=\"80px\">\n                                {{ $t('database.rdbHelper1') }}\n                            </td>\n                            <td width=\"32%\">\n                                <el-input type=\"number\" v-model=\"row.count\"></el-input>\n                            </td>\n                            <td width=\"10%\">\n                                {{ $t('database.rdbHelper2') }}\n                            </td>\n                            <td>\n                                <el-button link type=\"primary\" style=\"font-size: 10px\" @click=\"handleDelete(index)\">\n                                    {{ $t('commons.button.delete') }}\n                                </el-button>\n                            </td>\n                        </tr>\n                        <tr>\n                            <td align=\"left\">\n                                <el-button @click=\"handleAdd()\">{{ $t('commons.button.add') }}</el-button>\n                            </td>\n                        </tr>\n                    </table>\n                    <div>\n                        <span style=\"margin-left: 2px; margin-top: 5px\">{{ $t('database.rdbHelper3') }}</span>\n                    </div>\n                    <el-button type=\"primary\" @click=\"onSave(undefined, 'rbd')\" style=\"margin-top: 10px\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </el-card>\n            </el-col>\n        </el-row>\n        <el-card style=\"margin-top: 20px\">\n            <ComplexTable :pagination-config=\"paginationConfig\" v-model:selects=\"selects\" @search=\"search\" :data=\"data\">\n                <template #toolbar>\n                    <el-button type=\"primary\" @click=\"onBackup\">{{ $t('commons.button.backup') }}</el-button>\n                    <el-button type=\"primary\" plain :disabled=\"selects.length === 0\" @click=\"onBatchDelete(null)\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </template>\n                <el-table-column type=\"selection\" fix />\n                <el-table-column :label=\"$t('commons.table.name')\" show-overflow-tooltip prop=\"fileName\" />\n                <el-table-column :label=\"$t('app.source')\" prop=\"backupType\">\n                    <template #default=\"{ row }\">\n                        <span v-if=\"row.accountType === 'LOCAL'\">\n                            {{ $t('setting.LOCAL') }}\n                        </span>\n                        <span v-if=\"row.accountType && row.accountType !== 'LOCAL'\">\n                            {{ $t('setting.' + row.accountType) + ' - ' + row.accountName }}\n                        </span>\n                        <span v-if=\"!row.accountType\">-</span>\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('file.dir')\" show-overflow-tooltip prop=\"fileDir\" />\n                <el-table-column :label=\"$t('commons.table.createdAt')\" :formatter=\"dateFormat\" prop=\"createdAt\" />\n                <fu-table-operations\n                    width=\"300px\"\n                    :buttons=\"buttons\"\n                    :ellipsis=\"10\"\n                    :label=\"$t('commons.table.operate')\"\n                    fix\n                />\n            </ComplexTable>\n        </el-card>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onRecover\"></ConfirmDialog>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { Database } from '@/api/interface/database';\nimport { redisPersistenceConf, updateRedisPersistenceConf } from '@/api/modules/database';\nimport { deleteBackupRecord, handleBackup, handleRecover, searchBackupRecords } from '@/api/modules/backup';\nimport { Rules } from '@/global/form-rules';\nimport { dateFormat, newUUID } from '@/utils/util';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport { MsgInfo, MsgSuccess } from '@/utils/message';\nimport { Backup } from '@/api/interface/backup';\n\ninterface saveStruct {\n    second: number;\n    count: number;\n}\nconst form = reactive({\n    appendonly: '',\n    appendfsync: 'no',\n    saves: [] as Array<saveStruct>,\n});\nconst rules = reactive({\n    appendonly: [Rules.requiredSelect],\n    appendfsync: [Rules.requiredSelect],\n});\nconst formRef = ref<FormInstance>();\nconst database = ref();\nconst opRef = ref();\nconst dbType = ref('redis');\n\ninterface DialogProps {\n    database: string;\n    status: string;\n    type: string;\n}\nconst persistenceShow = ref(false);\nconst acceptParams = (prop: DialogProps): void => {\n    dbType.value = prop.type;\n    persistenceShow.value = true;\n    database.value = prop.database;\n    if (prop.status === 'Running') {\n        loadform();\n        search();\n    }\n};\nconst emit = defineEmits(['loading']);\n\nconst data = ref();\nconst selects = ref<any>([]);\nconst currentRow = ref();\nconst confirmDialogRef = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'redis-backup-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('redis-backup-page-size')) || 20,\n    total: 0,\n});\n\nconst handleAdd = () => {\n    let item = {\n        second: 0,\n        count: 0,\n    };\n    form.saves.push(item);\n};\nconst handleDelete = (index: number) => {\n    form.saves.splice(index, 1);\n};\n\nconst search = async () => {\n    let params = {\n        type: 'redis',\n        name: database.value,\n        detailName: '',\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    const res = await searchBackupRecords(params);\n    data.value = res.data.items || [];\n    paginationConfig.total = res.data.total;\n};\nconst onBackup = async () => {\n    emit('loading', true);\n    await handleBackup({ name: database.value, detailName: '', type: dbType.value, secret: '', taskID: '' })\n        .then(() => {\n            emit('loading', false);\n            search();\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            emit('loading', false);\n        });\n};\nconst onRecover = async () => {\n    let param = {\n        downloadAccountID: currentRow.value.downloadAccountID,\n        type: dbType.value,\n        name: database.value,\n        detailName: '',\n        file: currentRow.value.fileDir + '/' + currentRow.value.fileName,\n        secret: '',\n        taskID: newUUID(),\n    };\n    emit('loading', true);\n    await handleRecover(param)\n        .then(() => {\n            emit('loading', false);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            emit('loading', false);\n        });\n};\n\nconst onBatchDelete = async (row: Backup.RecordInfo | null) => {\n    let ids: Array<number> = [];\n    let names: Array<string> = [];\n    if (row) {\n        ids.push(row.id);\n        names.push(row.fileName);\n    } else {\n        selects.value.forEach((item: Backup.RecordInfo) => {\n            ids.push(item.id);\n            names.push(item.fileName);\n        });\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('commons.button.backup'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteBackupRecord,\n        params: { ids: ids },\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.recover'),\n        click: (row: Backup.RecordInfo) => {\n            currentRow.value = row;\n            let params = {\n                header: i18n.global.t('commons.button.recover'),\n                operationInfo: i18n.global.t('database.recoverHelper', [row.fileName]),\n                submitInputInfo: i18n.global.t('database.submitIt'),\n            };\n            confirmDialogRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Backup.RecordInfo) => {\n            onBatchDelete(row);\n        },\n    },\n];\n\nconst onSave = async (formEl: FormInstance | undefined, type: string) => {\n    let param = {} as Database.RedisConfPersistenceUpdate;\n    param.database = database.value;\n    if (type == 'aof') {\n        if (!formEl) return;\n        formEl.validate(async (valid) => {\n            if (!valid) return;\n            param.type = type;\n            param.appendfsync = form.appendfsync;\n            param.appendonly = form.appendonly;\n            param.dbType = dbType.value;\n            emit('loading', true);\n            await updateRedisPersistenceConf(param)\n                .then(() => {\n                    emit('loading', false);\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    emit('loading', false);\n                });\n        });\n        return;\n    }\n    let itemSaves = [] as Array<string>;\n    for (const item of form.saves) {\n        if (item.count < 0 || item.count > 100000 || item.second < 0 || item.second > 100000) {\n            MsgInfo(i18n.global.t('database.rdbInfo'));\n            return;\n        }\n        itemSaves.push(item.second + ' ' + item.count);\n    }\n    param.type = type;\n    param.save = itemSaves.join(',');\n    param.dbType = dbType.value;\n    emit('loading', true);\n    await updateRedisPersistenceConf(param)\n        .then(() => {\n            emit('loading', false);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            emit('loading', false);\n        });\n};\n\nconst loadform = async () => {\n    form.saves = [];\n    const res = await redisPersistenceConf(dbType.value, database.value);\n    form.appendonly = res.data?.appendonly;\n    form.appendfsync = res.data?.appendfsync;\n    let itemSaves = res.data?.save.split(' ');\n    for (let i = 0; i < itemSaves.length; i++) {\n        if (i % 2 === 1) {\n            form.saves.push({ second: Number(itemSaves[i - 1]), count: Number(itemSaves[i]) });\n        }\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/database/redis/setting/status/index.vue",
    "content": "<template>\n    <div v-if=\"statusShow\" class=\"database-status\">\n        <el-form label-position=\"top\">\n            <span class=\"title\">{{ $t('database.baseParam') }}</span>\n            <el-divider class=\"divider\" />\n            <el-row class=\"content\">\n                <el-col :xs=\"8\" :sm=\"6\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">uptime_in_days</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.uptime_in_days }}</span>\n                        <span class=\"input-help\">{{ $t('database.uptimeInDays') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"8\" :sm=\"6\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">tcp_port</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.tcp_port }}</span>\n                        <span class=\"input-help\">{{ $t('database.tcpPort') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"8\" :sm=\"6\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">connected_clients</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.connected_clients }}</span>\n                        <span class=\"input-help\">{{ $t('database.connectedClients') }}</span>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n\n            <span class=\"title\">{{ $t('database.performanceParam') }}</span>\n            <el-divider class=\"divider\" />\n            <el-row class=\"content\">\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">used_memory_rss</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.used_memory_rss }}</span>\n                        <span class=\"input-help\">{{ $t('database.usedMemoryRss') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">used_memory</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.used_memory }}</span>\n                        <span class=\"input-help\">{{ $t('database.usedMemory') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">used_memory_peak</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.used_memory_peak }}</span>\n                        <span class=\"input-help\">{{ $t('database.usedMemoryPeak') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">mem_fragmentation_ratio</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.mem_fragmentation_ratio }}</span>\n                        <span class=\"input-help\">{{ $t('database.tmpTableToDBHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"8\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">total_connections_received</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.total_connections_received }}</span>\n                        <span class=\"input-help\">{{ $t('database.totalConnectionsReceived') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">total_commands_processed</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.total_commands_processed }}</span>\n                        <span class=\"input-help\">{{ $t('database.totalCommandsProcessed') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">instantaneous_ops_per_sec</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.instantaneous_ops_per_sec }}</span>\n                        <span class=\"input-help\">{{ $t('database.instantaneousOpsPerSec') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">keyspace_hits</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.keyspace_hits }}</span>\n                        <span class=\"input-help\">{{ $t('database.keyspaceHits') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">keyspace_misses</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.keyspace_misses }}</span>\n                        <span class=\"input-help\">{{ $t('database.keyspaceMisses') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">hit</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.hit }}</span>\n                        <span class=\"input-help\">{{ $t('database.hit') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"12\" :sm=\"12\" :md=\"6\" :lg=\"6\" :xl=\"6\">\n                    <el-form-item>\n                        <template #label>\n                            <span class=\"status-label\">latest_fork_usec</span>\n                        </template>\n                        <span class=\"status-count\">{{ redisStatus.latest_fork_usec }}</span>\n                        <span class=\"input-help\">{{ $t('database.latestForkUsec') }}</span>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { loadRedisStatus } from '@/api/modules/database';\nimport { reactive, ref } from 'vue';\n\nconst redisStatus = reactive({\n    tcp_port: '',\n    uptime_in_days: '',\n    connected_clients: '',\n    used_memory: '',\n    used_memory_rss: '',\n    used_memory_peak: '',\n    mem_fragmentation_ratio: '',\n    total_connections_received: '',\n    total_commands_processed: '',\n    instantaneous_ops_per_sec: '',\n    keyspace_hits: '',\n    keyspace_misses: '',\n    hit: '',\n    latest_fork_usec: '',\n});\n\nconst database = ref();\nconst statusShow = ref(false);\nconst dbType = ref('redis');\n\ninterface DialogProps {\n    database: string;\n    status: string;\n    type: string;\n}\nconst acceptParams = (prop: DialogProps): void => {\n    statusShow.value = true;\n    database.value = prop.database;\n    dbType.value = prop.type;\n    if (prop.status === 'Running') {\n        loadStatus();\n    }\n};\n\nconst loadStatus = async () => {\n    const res = await loadRedisStatus(dbType.value, database.value);\n    let hit = (\n        (Number(res.data.keyspace_hits) / (Number(res.data.keyspace_hits) + Number(res.data.keyspace_misses))) *\n        100\n    ).toFixed(2);\n\n    redisStatus.uptime_in_days = res.data.uptime_in_days;\n    redisStatus.tcp_port = res.data.tcp_port;\n    redisStatus.connected_clients = res.data.connected_clients;\n    redisStatus.used_memory_rss = (Number(res.data.used_memory_rss) / 1024 / 1024).toFixed(2) + ' MB';\n    redisStatus.used_memory_peak = (Number(res.data.used_memory_peak) / 1024 / 1024).toFixed(2) + ' MB';\n    redisStatus.used_memory = (Number(res.data.used_memory) / 1024 / 1024).toFixed(2) + ' MB';\n    redisStatus.mem_fragmentation_ratio = res.data.mem_fragmentation_ratio;\n    redisStatus.total_connections_received = res.data.total_connections_received;\n    redisStatus.total_commands_processed = res.data.total_commands_processed;\n    redisStatus.instantaneous_ops_per_sec = res.data.instantaneous_ops_per_sec;\n    redisStatus.keyspace_hits = res.data.keyspace_hits;\n    redisStatus.keyspace_misses = res.data.keyspace_misses;\n    redisStatus.hit = hit;\n    redisStatus.latest_fork_usec = res.data.latest_fork_usec;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.database-status {\n    .divider {\n        display: block;\n        height: 1px;\n        width: 100%;\n        margin: 12px 0;\n        border-top: 1px var(--el-border-color) var(--el-border-style);\n    }\n    .title {\n        font-size: 20px;\n        font-weight: 500;\n        margin-left: 50px;\n    }\n    .content {\n        margin-left: 50px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/home/app/index.vue",
    "content": "<template>\n    <div>\n        <CardWithHeader :header=\"$t('app.app')\" class=\"card-interval\" v-loading=\"loading\">\n            <template #header-r>\n                <el-popover placement=\"left\" :width=\"226\" trigger=\"click\">\n                    <el-input size=\"small\" v-model=\"filter\" clearable @input=\"loadOption()\" />\n                    <el-table :show-header=\"false\" :data=\"options\" max-height=\"150px\">\n                        <el-table-column prop=\"key\" width=\"120\" show-overflow-tooltip />\n                        <el-table-column prop=\"name\">\n                            <template #default=\"{ row }\">\n                                <el-switch\n                                    @change=\"onChangeStatus(row)\"\n                                    class=\"float-right\"\n                                    size=\"small\"\n                                    v-model=\"row.isShow\"\n                                />\n                            </template>\n                        </el-table-column>\n                    </el-table>\n                    <template #reference>\n                        <el-button class=\"h-button-setting\" link icon=\"Setting\"></el-button>\n                    </template>\n                </el-popover>\n            </template>\n            <template #body>\n                <el-scrollbar height=\"531px\" class=\"moz-height\">\n                    <div class=\"h-app-card\" v-for=\"(app, index) in apps\" :key=\"index\">\n                        <el-row :gutter=\"5\">\n                            <el-col :span=\"5\">\n                                <el-avatar shape=\"square\" :size=\"55\" :src=\"getAppIconSrc(app)\" />\n                            </el-col>\n                            <el-col :span=\"16\">\n                                <div class=\"h-app-content\" v-if=\"!app.currentRow\">\n                                    <div>\n                                        <span class=\"h-app-title\">{{ app.name }}</span>\n                                        <svg-icon class=\"svg-icon\" iconName=\"p-huobao1\"></svg-icon>\n                                    </div>\n                                    <div class=\"h-app-desc\">\n                                        <span>\n                                            {{ app.description }}\n                                        </span>\n                                    </div>\n                                </div>\n                                <div class=\"h-app-content\" v-else>\n                                    <div>\n                                        <el-dropdown trigger=\"hover\">\n                                            <el-button type=\"primary\" plain size=\"small\" link class=\"h-app-dropdown\">\n                                                {{ app.currentRow.name }}\n                                                <el-icon class=\"el-icon--right\"><ArrowDown /></el-icon>\n                                            </el-button>\n                                            <template #dropdown>\n                                                <el-dropdown-menu>\n                                                    <el-dropdown-item\n                                                        v-for=\"(detailItem, index2) in app.detail\"\n                                                        :key=\"index2\"\n                                                        @click=\"app.currentRow = detailItem\"\n                                                    >\n                                                        {{ detailItem.name + ' - ' + detailItem.version }}\n                                                    </el-dropdown-item>\n                                                </el-dropdown-menu>\n                                            </template>\n                                        </el-dropdown>\n                                    </div>\n                                    <div class=\"h-app-margin\">\n                                        <el-button plain size=\"small\" link class=\"h-app-desc\">\n                                            {{ $t('app.version') + ': ' + app.currentRow.version }}\n                                        </el-button>\n                                    </div>\n                                    <div class=\"h-app-margin\">\n                                        <el-button\n                                            size=\"small\"\n                                            type=\"primary\"\n                                            link\n                                            v-if=\"app.currentRow.status !== 'Running'\"\n                                            @click=\"onOperate('start', app.currentRow)\"\n                                        >\n                                            {{ $t('commons.button.start') }}\n                                        </el-button>\n                                        <el-button\n                                            :style=\"mobile ? 'margin-left: -1px' : ''\"\n                                            size=\"small\"\n                                            type=\"primary\"\n                                            link\n                                            v-else\n                                            @click=\"onOperate('stop', app.currentRow)\"\n                                        >\n                                            {{ $t('commons.button.stop') }}\n                                        </el-button>\n                                        <el-button\n                                            :style=\"mobile ? 'margin-left: -1px' : ''\"\n                                            size=\"small\"\n                                            type=\"primary\"\n                                            link\n                                            @click=\"onOperate('restart', app.currentRow)\"\n                                        >\n                                            {{ $t('commons.button.restart') }}\n                                        </el-button>\n                                        <el-button\n                                            :style=\"mobile ? 'margin-left: -1px' : ''\"\n                                            size=\"small\"\n                                            type=\"primary\"\n                                            link\n                                            @click=\"routerToFileWithPath(app.currentRow.path)\"\n                                        >\n                                            {{ $t('home.dir') }}\n                                        </el-button>\n                                        <el-popover\n                                            placement=\"left\"\n                                            trigger=\"hover\"\n                                            v-if=\"app.currentRow.appType == 'website'\"\n                                            :width=\"260\"\n                                        >\n                                            <template #reference>\n                                                <el-button\n                                                    link\n                                                    size=\"small\"\n                                                    type=\"primary\"\n                                                    :style=\"mobile ? 'margin-left: -1px' : ''\"\n                                                >\n                                                    {{ $t('app.toLink') }}\n                                                </el-button>\n                                            </template>\n                                            <span v-if=\"defaultLink == '' && app.currentRow.webUI == ''\">\n                                                {{ $t('app.webUIConfig') }}\n                                                <el-link\n                                                    icon=\"Position\"\n                                                    @click=\"jumpToPath(router, '/settings/panel')\"\n                                                    type=\"primary\"\n                                                >\n                                                    {{ $t('firewall.quickJump') }}\n                                                </el-link>\n                                            </span>\n                                            <div v-else>\n                                                <div>\n                                                    <el-button\n                                                        v-if=\"defaultLink != ''\"\n                                                        type=\"primary\"\n                                                        link\n                                                        @click=\"toLink(defaultLink + ':' + app.currentRow.httpPort)\"\n                                                    >\n                                                        {{ defaultLink + ':' + app.currentRow.httpPort }}\n                                                    </el-button>\n                                                </div>\n                                                <div>\n                                                    <el-button\n                                                        v-if=\"app.currentRow.webUI != ''\"\n                                                        type=\"primary\"\n                                                        link\n                                                        @click=\"toLink(app.currentRow.webUI)\"\n                                                    >\n                                                        {{ app.currentRow.webUI }}\n                                                    </el-button>\n                                                </div>\n                                            </div>\n                                        </el-popover>\n                                    </div>\n                                </div>\n                            </el-col>\n                            <el-col :span=\"1\">\n                                <el-button\n                                    class=\"h-app-button\"\n                                    type=\"primary\"\n                                    plain\n                                    round\n                                    size=\"small\"\n                                    :disabled=\"app.limit == 1 && app.detail && app.detail.length !== 0\"\n                                    @click=\"goInstall(app.key, app.appType)\"\n                                >\n                                    {{ $t('commons.button.install') }}\n                                </el-button>\n                            </el-col>\n                        </el-row>\n                        <div class=\"h-app-divider\" />\n                    </div>\n                </el-scrollbar>\n            </template>\n        </CardWithHeader>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getAppIconUrl, installedOp } from '@/api/modules/app';\nimport { getAgentSettingByKey } from '@/api/modules/setting';\nimport { changeLauncherStatus, loadAppLauncher, loadAppLauncherOption } from '@/api/modules/dashboard';\nimport i18n from '@/lang';\nimport { GlobalStore } from '@/store';\nimport { MsgSuccess } from '@/utils/message';\nimport { ref } from 'vue';\nimport { useRouter } from 'vue-router';\nimport { jumpToPath } from '@/utils/util';\nimport { jumpToInstall } from '@/utils/app';\nimport { routerToFileWithPath, routerToNameWithQuery } from '@/utils/router';\n\nconst router = useRouter();\nconst globalStore = GlobalStore();\n\nlet loading = ref(false);\nlet apps = ref([]);\nconst options = ref([]);\nconst filter = ref();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst defaultLink = ref('');\n\nconst acceptParams = (): void => {\n    search();\n    loadOption();\n    getConfig();\n};\n\nconst goInstall = (key: string, type: string) => {\n    if (!jumpToInstall(type, key)) {\n        routerToNameWithQuery('AppAll', { install: key });\n    }\n};\n\nconst search = async () => {\n    loading.value = true;\n    await loadAppLauncher()\n        .then((res) => {\n            loading.value = false;\n            apps.value = res.data;\n            for (const item of apps.value) {\n                if (item.detail && item.detail.length !== 0) {\n                    item.currentRow = item.detail[0];\n                }\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst onChangeStatus = async (row: any) => {\n    loading.value = true;\n    await changeLauncherStatus(row.key, row.isShow ? 'Enable' : 'Disable')\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst toLink = (link: string) => {\n    window.open(link, '_blank');\n};\n\nconst getConfig = async () => {\n    try {\n        const res = await getAgentSettingByKey('SystemIP');\n        if (res.data != '') {\n            defaultLink.value = res.data;\n        }\n    } catch (error) {}\n};\n\nconst onOperate = async (operation: string, row: any) => {\n    ElMessageBox.confirm(\n        i18n.global.t('app.operatorHelper', [i18n.global.t('commons.button.' + operation)]),\n        i18n.global.t('commons.button.' + operation),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(async () => {\n        loading.value = true;\n        let params = {\n            installId: row.installID,\n            operate: operation,\n            detailId: row.detailID,\n        };\n        await installedOp(params)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst loadOption = async () => {\n    const res = await loadAppLauncherOption(filter.value || '');\n    options.value = res.data || [];\n};\n\nconst getAppIconSrc = (app: any) => {\n    const icon = app?.icon || '';\n    if (icon.startsWith('app_') || icon === '') {\n        return getAppIconUrl(app.key);\n    }\n    return `data:image/png;base64,${icon}`;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.h-app-card {\n    cursor: pointer;\n    padding: 10px 15px;\n    margin-right: 10px;\n    line-height: 18px;\n\n    .h-app-content {\n        padding-left: 15px;\n        .h-app-title {\n            font-weight: 500;\n            font-size: 15px;\n            color: var(--panel-text-color);\n        }\n\n        .h-app-desc {\n            margin-top: 2px;\n            span {\n                font-weight: 400;\n                font-size: 12px;\n                color: var(--el-text-color-regular);\n            }\n        }\n    }\n    .h-app-button {\n        margin-top: 10px;\n    }\n    &:hover {\n        background-color: rgba(0, 94, 235, 0.03);\n    }\n}\n\n.h-app-divider {\n    margin-top: 10px;\n    border: 0;\n    border-top: var(--panel-border);\n}\n\n.h-app-desc {\n    font-weight: 400;\n    font-size: 12px;\n    color: var(--el-text-color-regular);\n}\n\n.svg-icon {\n    font-size: 5px !important;\n    margin-left: 5px;\n    margin-bottom: 2px;\n}\n\n.h-button-setting {\n    float: right;\n    margin-left: 5px;\n}\n\n.h-app-dropdown {\n    font-weight: 600;\n    font-size: 16px;\n    color: var(--panel-text-color);\n}\n\n.h-app-margin {\n    margin-top: 2px;\n}\n\n.h-app-option {\n    font-weight: 500;\n    font-size: 14px;\n    line-height: 20px;\n    color: var(--el-text-color-regular);\n}\n\n/* FOR MOZILLA */\n@-moz-document url-prefix() {\n    .moz-height {\n        height: 524px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/home/index.vue",
    "content": "<template>\n    <div :key=\"$route.fullPath\" id=\"dashboard\">\n        <RouterButton\n            :buttons=\"[\n                {\n                    label: i18n.global.t('menu.home'),\n                    path: '/',\n                },\n            ]\"\n        >\n            <template #route-button>\n                <div class=\"router-button\" v-if=\"!isOffLine\">\n                    <template v-if=\"!isProductPro\">\n                        <el-button link type=\"primary\" @click=\"toUpload\">\n                            {{ $t('license.levelUpPro') }}\n                        </el-button>\n                    </template>\n                </div>\n            </template>\n        </RouterButton>\n\n        <el-alert\n            v-if=\"!isSafety && globalStore.showEntranceWarn\"\n            class=\"card-interval\"\n            type=\"warning\"\n            @close=\"hideEntrance\"\n        >\n            <template #title>\n                <span class=\"flx-align-center\">\n                    <span>{{ $t('home.entranceHelper') }}</span>\n                    <el-link\n                        style=\"font-size: 12px; margin-left: 5px\"\n                        icon=\"Position\"\n                        @click=\"jumpToPath(router, '/settings/safe')\"\n                        type=\"primary\"\n                    >\n                        {{ $t('firewall.quickJump') }}\n                    </el-link>\n                </span>\n            </template>\n        </el-alert>\n\n        <el-row :gutter=\"7\" class=\"card-interval\">\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"16\" :lg=\"16\" :xl=\"16\">\n                <CardWithHeader :header=\"$t('menu.home')\" height=\"166px\">\n                    <template #header-r>\n                        <el-button class=\"h-button-setting\" @click=\"quickJumpRef.acceptParams()\" link icon=\"Setting\" />\n                    </template>\n                    <template #body>\n                        <div class=\"h-overview\">\n                            <el-row>\n                                <el-col :span=\"6\" v-for=\"item in baseInfo.quickJump\" :key=\"item.name\">\n                                    <span>{{ $t(item.title, 2) }}</span>\n                                    <div class=\"count\">\n                                        <el-tooltip\n                                            v-if=\"item.alias || item.detail.length > 20\"\n                                            :content=\"item.detail\"\n                                            placement=\"bottom\"\n                                        >\n                                            <span @click=\"quickJump(item)\">\n                                                {{ item.alias || item.detail.substring(0, 18) + '...' }}\n                                            </span>\n                                        </el-tooltip>\n                                        <span @click=\"quickJump(item)\" v-else>{{ item.detail }}</span>\n                                    </div>\n                                </el-col>\n                            </el-row>\n                        </div>\n                    </template>\n                </CardWithHeader>\n                <CardWithHeader :header=\"$t('commons.table.status')\" class=\"card-interval\">\n                    <template #body>\n                        <SystemStatus ref=\"statusRef\" style=\"margin-bottom: 33px\" />\n                    </template>\n                </CardWithHeader>\n                <CardWithHeader\n                    :header=\"$t('menu.monitor')\"\n                    class=\"card-interval chart-card\"\n                    v-loading=\"!chartsOption['networkChart']\"\n                    @mouseenter=\"refreshOptionsOnHover\"\n                >\n                    <template #header-r>\n                        <el-radio-group\n                            style=\"float: right; margin-left: 5px\"\n                            v-model=\"chartOption\"\n                            @change=\"changeOption\"\n                        >\n                            <el-radio-button value=\"network\">{{ $t('home.network') }}</el-radio-button>\n                            <el-radio-button value=\"io\">{{ $t('home.io') }}</el-radio-button>\n                        </el-radio-group>\n                        <el-select\n                            v-if=\"chartOption === 'network'\"\n                            @change=\"onLoadBaseInfo(false, 'network')\"\n                            v-model=\"searchInfo.netOption\"\n                            class=\"p-w-200 float-right\"\n                        >\n                            <template #prefix>{{ $t('home.networkCard') }}</template>\n                            <el-option\n                                v-for=\"item in netOptions\"\n                                :key=\"item\"\n                                :label=\"item == 'all' ? $t('commons.table.all') : item\"\n                                :value=\"item\"\n                            />\n                        </el-select>\n                        <el-select\n                            v-if=\"chartOption === 'io'\"\n                            v-model=\"searchInfo.ioOption\"\n                            @change=\"onLoadBaseInfo(false, 'io')\"\n                            class=\"p-w-200 float-right\"\n                        >\n                            <template #prefix>{{ $t('home.disk') }}</template>\n                            <el-option\n                                v-for=\"item in ioOptions\"\n                                :key=\"item\"\n                                :label=\"item == 'all' ? $t('commons.table.all') : item\"\n                                :value=\"item\"\n                            />\n                        </el-select>\n                    </template>\n                    <template #body>\n                        <div style=\"position: relative; margin-top: 60px\">\n                            <div class=\"monitor-tags\" v-if=\"chartOption === 'network'\">\n                                <el-tag>\n                                    {{ $t('monitor.up') }}: {{ computeSizeFromKBs(currentChartInfo.netBytesSent) }}\n                                </el-tag>\n                                <el-tag>\n                                    {{ $t('monitor.down') }}: {{ computeSizeFromKBs(currentChartInfo.netBytesRecv) }}\n                                </el-tag>\n                                <el-tag>{{ $t('home.totalSend') }}: {{ computeSize(currentInfo.netBytesSent) }}</el-tag>\n                                <el-tag>{{ $t('home.totalRecv') }}: {{ computeSize(currentInfo.netBytesRecv) }}</el-tag>\n                            </div>\n                            <div class=\"monitor-tags\" v-if=\"chartOption === 'io'\">\n                                <el-tag>{{ $t('monitor.read') }}: {{ currentChartInfo.ioReadBytes }} MB</el-tag>\n                                <el-tag>{{ $t('monitor.write') }}: {{ currentChartInfo.ioWriteBytes }} MB</el-tag>\n                                <el-tag>\n                                    {{ $t('home.rwPerSecond') }}: {{ currentChartInfo.ioCount }}\n                                    {{ $t('commons.units.time') }}/s\n                                </el-tag>\n                                <el-tag>{{ $t('home.ioDelay') }}: {{ currentChartInfo.ioTime }} ms</el-tag>\n                            </div>\n\n                            <div v-if=\"chartOption === 'io'\" style=\"margin-top: 40px\" class=\"mobile-monitor-chart\">\n                                <v-charts\n                                    height=\"383px\"\n                                    id=\"ioChart\"\n                                    type=\"line\"\n                                    :option=\"chartsOption['ioChart']\"\n                                    v-if=\"chartsOption['ioChart']\"\n                                    :dataZoom=\"true\"\n                                />\n                            </div>\n                            <div v-if=\"chartOption === 'network'\" style=\"margin-top: 40px\" class=\"mobile-monitor-chart\">\n                                <v-charts\n                                    height=\"383px\"\n                                    id=\"networkChart\"\n                                    type=\"line\"\n                                    :option=\"chartsOption['networkChart']\"\n                                    v-if=\"chartsOption['networkChart']\"\n                                    :dataZoom=\"true\"\n                                />\n                            </div>\n                        </div>\n                    </template>\n                </CardWithHeader>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"8\" :lg=\"8\" :xl=\"8\">\n                <el-carousel\n                    class=\"my-carousel\"\n                    :class=\"{ 'no-indicator': carouselItemCount <= 1 }\"\n                    :key=\"simpleNodes.length + carouselItemCount\"\n                    height=\"368px\"\n                    indicator-position=\"\"\n                    arrow=\"never\"\n                >\n                    <el-carousel-item key=\"systemInfo\">\n                        <CardWithHeader :header=\"$t('home.systemInfo')\">\n                            <template #header-r>\n                                <el-popover\n                                    popper-class=\"dashboard-carousel-popover\"\n                                    placement=\"bottom\"\n                                    :title=\"$t('home.carouselSetting')\"\n                                    width=\"220\"\n                                    trigger=\"click\"\n                                >\n                                    <div class=\"dashboard-carousel-setting\">\n                                        <div class=\"setting-item mt-2\">\n                                            <span>{{ $t('home.systemInfo') }}</span>\n                                            <div class=\"mr-4\">-</div>\n                                        </div>\n                                        <div class=\"setting-item mt-2\">\n                                            <span>{{ $t('home.memo') }}</span>\n                                            <el-switch\n                                                v-model=\"memoCarouselSetting\"\n                                                active-value=\"Enable\"\n                                                inactive-value=\"Disable\"\n                                                @change=\"\n                                                    (val) => updateDashboardCarouselSetting('DashboardMemoVisible', val)\n                                                \"\n                                            />\n                                        </div>\n                                        <div class=\"setting-item\">\n                                            <span>{{ $t('setting.panel') }}</span>\n                                            <el-switch\n                                                v-model=\"simpleNodeCarouselSetting\"\n                                                active-value=\"Enable\"\n                                                inactive-value=\"Disable\"\n                                                @change=\"\n                                                    (val) =>\n                                                        updateDashboardCarouselSetting(\n                                                            'DashboardSimpleNodeVisible',\n                                                            val,\n                                                        )\n                                                \"\n                                            />\n                                        </div>\n                                    </div>\n                                    <template #reference>\n                                        <el-button class=\"h-button-setting\" link icon=\"Setting\" />\n                                    </template>\n                                </el-popover>\n                                <el-tooltip :content=\"$t('commons.button.refresh')\" placement=\"top\">\n                                    <el-button class=\"h-button-setting\" @click=\"refreshDashboard\" link icon=\"Refresh\" />\n                                </el-tooltip>\n                                <el-tooltip :content=\"$t('home.tooltipSensitiveInfo')\" placement=\"top\">\n                                    <el-button\n                                        class=\"h-button-setting\"\n                                        @click=\"toggleSensitiveInfo\"\n                                        link\n                                        :icon=\"showSensitiveInfo ? 'View' : 'Hide'\"\n                                    />\n                                </el-tooltip>\n                                <el-tooltip :content=\"$t('commons.button.copy')\" placement=\"top\">\n                                    <el-button class=\"h-button-setting\" @click=\"handleCopy\" link icon=\"CopyDocument\" />\n                                </el-tooltip>\n                            </template>\n                            <template #body>\n                                <el-scrollbar>\n                                    <el-descriptions :column=\"1\" class=\"ml-5 -mt-2 h-systemInfo\" border>\n                                        <el-descriptions-item\n                                            class-name=\"system-content\"\n                                            label-class-name=\"system-label\"\n                                        >\n                                            <template #label>\n                                                <span class=\"system-label\">{{ $t('home.hostname') }}</span>\n                                            </template>\n                                            {{ showSensitiveInfo ? baseInfo.hostname : '****' }}\n                                        </el-descriptions-item>\n                                        <el-descriptions-item\n                                            class-name=\"system-content\"\n                                            label-class-name=\"system-label\"\n                                        >\n                                            <template #label>\n                                                <span class=\"system-label\">{{ $t('home.platformVersion') }}</span>\n                                            </template>\n                                            {{\n                                                baseInfo.prettyDistro\n                                                    ? baseInfo.prettyDistro\n                                                    : baseInfo.platformVersion\n                                                    ? baseInfo.platform + '-' + baseInfo.platformVersion\n                                                    : baseInfo.platform\n                                            }}\n                                        </el-descriptions-item>\n                                        <el-descriptions-item\n                                            class-name=\"system-content\"\n                                            label-class-name=\"system-label\"\n                                        >\n                                            <template #label>\n                                                <span class=\"system-label\">{{ $t('home.kernelVersion') }}</span>\n                                            </template>\n                                            {{ baseInfo.kernelVersion }}\n                                        </el-descriptions-item>\n                                        <el-descriptions-item\n                                            class-name=\"system-content\"\n                                            label-class-name=\"system-label\"\n                                        >\n                                            <template #label>\n                                                <span class=\"system-label\">{{ $t('home.kernelArch') }}</span>\n                                            </template>\n                                            {{ baseInfo.kernelArch }}\n                                        </el-descriptions-item>\n                                        <el-descriptions-item\n                                            class-name=\"system-content\"\n                                            label-class-name=\"system-label\"\n                                        >\n                                            <template #label>\n                                                <span class=\"system-label\">{{ $t('home.ip') }}</span>\n                                            </template>\n                                            {{ showSensitiveInfo ? baseInfo.ipV4Addr : '****' }}\n                                        </el-descriptions-item>\n                                        <el-descriptions-item\n                                            v-if=\"baseInfo.httpProxy && baseInfo.httpProxy !== 'noProxy'\"\n                                            class-name=\"system-content\"\n                                            label-class-name=\"system-label\"\n                                        >\n                                            <template #label>\n                                                <span class=\"system-label\">{{ $t('home.proxy') }}</span>\n                                                {{ baseInfo.httpProxy }}\n                                            </template>\n                                        </el-descriptions-item>\n                                        <el-descriptions-item\n                                            class-name=\"system-content\"\n                                            label-class-name=\"system-label\"\n                                        >\n                                            <template #label>\n                                                <span class=\"system-label\">{{ $t('home.uptime') }}</span>\n                                            </template>\n                                            {{ currentInfo.timeSinceUptime }}\n                                        </el-descriptions-item>\n                                        <el-descriptions-item\n                                            class-name=\"system-content\"\n                                            label-class-name=\"system-label\"\n                                        >\n                                            <template #label>\n                                                <span class=\"system-label\">{{ $t('home.runningTime') }}</span>\n                                            </template>\n                                            {{ loadUpTime(currentInfo.timeSinceUptime) }}\n                                        </el-descriptions-item>\n                                    </el-descriptions>\n                                </el-scrollbar>\n                            </template>\n                        </CardWithHeader>\n                    </el-carousel-item>\n                    <el-carousel-item key=\"memoInfo\" v-if=\"showMemoCarousel\">\n                        <CardWithHeader :header=\"$t('home.memo')\" class=\"memo-card\">\n                            <template #header-r>\n                                <el-tooltip v-if=\"!memoEditing\" :content=\"$t('commons.button.edit')\" placement=\"top\">\n                                    <el-button class=\"h-button-setting\" @click=\"startMemoEdit\" link icon=\"Edit\" />\n                                </el-tooltip>\n                                <el-tooltip v-if=\"memoEditing\" :content=\"$t('commons.button.save')\" placement=\"top\">\n                                    <el-button\n                                        class=\"h-button-setting\"\n                                        @click=\"saveMemo\"\n                                        link\n                                        icon=\"Check\"\n                                        :loading=\"memoSaving\"\n                                    />\n                                </el-tooltip>\n                                <el-tooltip v-if=\"memoEditing\" :content=\"$t('commons.button.cancel')\" placement=\"top\">\n                                    <el-button class=\"h-button-setting\" @click=\"cancelMemoEdit\" link icon=\"Close\" />\n                                </el-tooltip>\n                            </template>\n                            <template #body>\n                                <el-scrollbar height=\"286px\">\n                                    <div class=\"memo-container ml-5 mr-5\">\n                                        <el-input\n                                            v-if=\"memoEditing\"\n                                            v-model=\"memoEditContent\"\n                                            type=\"textarea\"\n                                            :rows=\"10\"\n                                            :maxlength=\"500\"\n                                            :placeholder=\"$t('home.memoPlaceholder')\"\n                                            show-word-limit\n                                        />\n                                        <div v-else class=\"memo-content\">\n                                            <MarkDownEditor v-if=\"memoContent\" :content=\"memoContent\" />\n                                            <span v-else class=\"memo-placeholder\">\n                                                {{ $t('home.memoPlaceholder') }}\n                                            </span>\n                                        </div>\n                                    </div>\n                                </el-scrollbar>\n                            </template>\n                        </CardWithHeader>\n                    </el-carousel-item>\n                    <el-carousel-item key=\"simpleNode\" v-if=\"showSimpleNode()\">\n                        <CardWithHeader :header=\"$t('setting.panel')\">\n                            <template #header-r>\n                                <el-tooltip :content=\"$t('xpack.node.panelItem')\" placement=\"top\">\n                                    <el-button\n                                        class=\"h-button-setting\"\n                                        @click=\"routerToNameWithQuery('SimpleNode', { uncached: 'true' })\"\n                                        link\n                                        icon=\"Setting\"\n                                    />\n                                </el-tooltip>\n                            </template>\n                            <template #body>\n                                <el-scrollbar height=\"286px\">\n                                    <div class=\"simple-node cursor-pointer\" v-for=\"row in simpleNodes\" :key=\"row.id\">\n                                        <el-row :gutter=\"5\">\n                                            <el-col :span=\"21\">\n                                                <div class=\"name\">\n                                                    {{ row.name }}\n                                                    <Status :status=\"row.status\" :msg=\"row.message\" />\n                                                </div>\n                                                <div class=\"detail\">\n                                                    {{ loadSource(row) }}\n                                                </div>\n                                            </el-col>\n\n                                            <el-col :span=\"1\">\n                                                <el-button\n                                                    @click=\"jumpPanel(row)\"\n                                                    size=\"small\"\n                                                    :disabled=\"row.status !== 'Healthy'\"\n                                                    class=\"visit\"\n                                                    round\n                                                    plain\n                                                    type=\"primary\"\n                                                >\n                                                    {{ $t('commons.button.visit') }}\n                                                </el-button>\n                                            </el-col>\n                                        </el-row>\n                                        <div class=\"h-app-divider\" />\n                                    </div>\n                                </el-scrollbar>\n                            </template>\n                        </CardWithHeader>\n                    </el-carousel-item>\n                </el-carousel>\n\n                <AppLauncher ref=\"appRef\" class=\"card-interval\" />\n            </el-col>\n        </el-row>\n\n        <LicenseImport ref=\"licenseRef\" />\n        <QuickJump @search=\"onLoadBaseInfo(false, 'all')\" ref=\"quickJumpRef\" />\n\n        <DialogPro v-model=\"welcomeOpen\" size=\"w-70\" id=\"welcomeDialog\">\n            <div ref=\"shadowContainer\" />\n        </DialogPro>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, onBeforeUnmount, ref, reactive, computed, nextTick } from 'vue';\nimport SystemStatus from '@/views/home/status/index.vue';\nimport AppLauncher from '@/views/home/app/index.vue';\nimport VCharts from '@/components/v-charts/index.vue';\nimport LicenseImport from '@/components/license-import/index.vue';\nimport QuickJump from '@/views/home/quick/index.vue';\nimport CardWithHeader from '@/components/card-with-header/index.vue';\nimport MarkDownEditor from '@/components/mkdown-editor/index.vue';\nimport i18n from '@/lang';\nimport { Dashboard } from '@/api/interface/dashboard';\nimport { dateFormatForSecond, computeSize, computeSizeFromKBs, loadUpTime, jumpToPath, copyText } from '@/utils/util';\nimport { useRouter } from 'vue-router';\nimport { loadBaseInfo, loadCurrentInfo } from '@/api/modules/dashboard';\nimport { getIOOptions, getNetworkOptions } from '@/api/modules/host';\nimport {\n    getSettingInfo,\n    getAgentSettingInfo,\n    listAllSimpleNodes,\n    loadUpgradeInfo,\n    getMemo,\n    updateMemo,\n    updateSetting,\n} from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nimport { storeToRefs } from 'pinia';\nimport { routerToFileWithPath, routerToNameWithQuery, routerToPath } from '@/utils/router';\nimport { getWelcomePage } from '@/api/modules/auth';\nimport {\n    clearDashboardCache,\n    clearDashboardCacheByPrefix,\n    getDashboardCache,\n    setDashboardCache,\n} from '@/utils/dashboardCache';\nimport { MsgSuccess } from '@/utils/message';\nconst router = useRouter();\nconst globalStore = GlobalStore();\n\nconst DASHBOARD_CACHE_TTL = {\n    safeStatus: 10 * 60 * 1000,\n    netOptions: 60 * 60 * 1000,\n    ioOptions: 60 * 60 * 1000,\n};\n\nconst statusRef = ref();\nconst appRef = ref();\n\nconst isSafety = ref();\n\nconst welcomeOpen = ref();\nconst shadowContainer = ref();\n\nconst chartOption = ref('network');\nlet timer: NodeJS.Timer | null = null;\nlet isInit = ref<boolean>(true);\nlet isStatusInit = ref<boolean>(true);\nlet isActive = ref(true);\nlet isCurrentActive = ref(true);\n\nconst showSensitiveInfo = ref(true);\n\nconst ioReadBytes = ref<Array<number>>([]);\nconst ioWriteBytes = ref<Array<number>>([]);\nconst netBytesSents = ref<Array<number>>([]);\nconst netBytesRecvs = ref<Array<number>>([]);\nconst timeIODatas = ref<Array<string>>([]);\nconst timeNetDatas = ref<Array<string>>([]);\n\nconst simpleNodes = ref([]);\nconst ioOptions = ref();\nconst netOptions = ref();\nconst netOptionsFromCache = ref(false);\nconst ioOptionsFromCache = ref(false);\nconst hasRefreshedOptionsOnHover = ref(false);\n\nconst licenseRef = ref();\nconst quickJumpRef = ref();\nconst { isProductPro, isOffLine } = storeToRefs(globalStore);\n\nconst searchInfo = reactive({\n    ioOption: 'all',\n    netOption: 'all',\n});\n\nconst memoContent = ref('');\nconst memoEditContent = ref('');\nconst memoEditing = ref(false);\nconst memoSaving = ref(false);\nconst memoCarouselSetting = ref();\nconst simpleNodeCarouselSetting = ref();\nconst carouselSettingReady = ref(false);\n\nconst showMemoCarousel = computed(() => memoCarouselSetting.value === 'Enable');\nconst carouselItemCount = computed(() => {\n    let count = 1;\n    if (showMemoCarousel.value) count += 1;\n    if (showSimpleNode()) count += 1;\n    return count;\n});\n\nconst baseInfo = ref<Dashboard.BaseInfo>({\n    hostname: '',\n    os: '',\n    platform: '',\n    platformFamily: '',\n    platformVersion: '',\n    prettyDistro: '',\n    kernelArch: '',\n    kernelVersion: '',\n    virtualizationSystem: '',\n    ipV4Addr: '',\n    httpProxy: '',\n\n    cpuCores: 0,\n    cpuLogicalCores: 0,\n    cpuModelName: '',\n    cpuMhz: 0,\n    currentInfo: null,\n\n    quickJump: [],\n});\nconst currentInfo = ref<Dashboard.CurrentInfo>({\n    uptime: 0,\n    timeSinceUptime: '',\n    procs: 0,\n\n    load1: 0,\n    load5: 0,\n    load15: 0,\n    loadUsagePercent: 0,\n\n    cpuPercent: [] as Array<number>,\n    cpuUsedPercent: 0,\n    cpuUsed: 0,\n    cpuTotal: 0,\n    cpuDetailedPercent: [] as Array<number>,\n\n    memoryTotal: 0,\n    memoryAvailable: 0,\n    memoryUsed: 0,\n    memoryFree: 0,\n    memoryShard: 0,\n    memoryCache: 0,\n    memoryUsedPercent: 0,\n    swapMemoryTotal: 0,\n    swapMemoryAvailable: 0,\n    swapMemoryUsed: 0,\n    swapMemoryUsedPercent: 0,\n\n    ioReadBytes: 0,\n    ioWriteBytes: 0,\n    ioCount: 0,\n    ioReadTime: 0,\n    ioWriteTime: 0,\n\n    diskData: [],\n    gpuData: [],\n    xpuData: [],\n\n    netBytesSent: 0,\n    netBytesRecv: 0,\n\n    topCPUItems: [],\n    topMemItems: [],\n\n    shotTime: new Date(),\n});\nconst currentChartInfo = reactive({\n    ioReadBytes: 0,\n    ioWriteBytes: 0,\n    ioCount: 0,\n    ioTime: 0,\n\n    netBytesSent: 0,\n    netBytesRecv: 0,\n});\n\nconst chartsOption = ref({ ioChart1: null, networkChart: null });\n\nconst updateCurrentInfo = (data: Dashboard.CurrentInfo) => {\n    currentInfo.value = {\n        ...data,\n        topCPUItems: currentInfo.value.topCPUItems || [],\n        topMemItems: currentInfo.value.topMemItems || [],\n    };\n};\n\nconst changeOption = async () => {\n    isInit.value = true;\n    loadData();\n};\n\nconst applyDefaultNetOption = () => {\n    if (!netOptions.value || netOptions.value.length === 0) return;\n    const defaultNet = globalStore.defaultNetwork || netOptions.value[0];\n    if (defaultNet && searchInfo.netOption !== defaultNet) {\n        searchInfo.netOption = defaultNet;\n    }\n};\n\nconst onLoadAgentSettingInfo = async () => {\n    await getAgentSettingInfo().then((res) => {\n        globalStore.defaultIO = res.data.defaultIO;\n        globalStore.defaultNetwork = res.data.defaultNetwork;\n    });\n};\n\nconst onLoadNetworkOptions = async (force?: boolean) => {\n    const cache = force ? null : getDashboardCache('netOptions');\n    if (cache !== null) {\n        netOptions.value = cache;\n        netOptionsFromCache.value = true;\n        applyDefaultNetOption();\n        return;\n    }\n    const res = await getNetworkOptions();\n    netOptions.value = res.data;\n    netOptionsFromCache.value = false;\n    setDashboardCache('netOptions', res.data, DASHBOARD_CACHE_TTL.netOptions);\n    applyDefaultNetOption();\n};\n\nconst onLoadSimpleNode = async () => {\n    const res = await listAllSimpleNodes();\n    simpleNodes.value = res.data || [];\n};\n\nconst applyDefaultIOOption = async () => {\n    if (!ioOptions.value || ioOptions.value.length === 0) return;\n    const defaultIO = globalStore.defaultIO || ioOptions.value[0];\n    if (defaultIO && searchInfo.ioOption !== defaultIO) {\n        searchInfo.ioOption = defaultIO;\n    }\n};\n\nconst onLoadIOOptions = async (force?: boolean) => {\n    const cache = force ? null : getDashboardCache('ioOptions');\n    if (cache !== null) {\n        ioOptions.value = cache;\n        ioOptionsFromCache.value = true;\n        applyDefaultIOOption();\n        return;\n    }\n    const res = await getIOOptions();\n    ioOptions.value = res.data;\n    ioOptionsFromCache.value = false;\n    setDashboardCache('ioOptions', ioOptions.value, DASHBOARD_CACHE_TTL.ioOptions);\n    applyDefaultIOOption();\n};\n\nconst onLoadBaseInfo = async (isInit: boolean, range: string) => {\n    if (range === 'all' || range === 'io') {\n        ioReadBytes.value = [];\n        ioWriteBytes.value = [];\n        timeIODatas.value = [];\n    } else if (range === 'all' || range === 'network') {\n        netBytesSents.value = [];\n        netBytesRecvs.value = [];\n        timeNetDatas.value = [];\n    }\n    const res = await loadBaseInfo(searchInfo.ioOption, searchInfo.netOption);\n    baseInfo.value = res.data;\n    updateCurrentInfo(baseInfo.value.currentInfo);\n    onLoadCurrentInfo();\n    isStatusInit.value = false;\n    statusRef.value?.acceptParams(currentInfo.value, baseInfo.value);\n    appRef.value?.acceptParams();\n    if (isInit) {\n        clearTimer();\n        timer = setInterval(async () => {\n            try {\n                if (!isCurrentActive.value) {\n                    throw new Error('jump out');\n                }\n                if (isActive.value && !globalStore.isOnRestart) {\n                    await onLoadCurrentInfo();\n                    await onLoadSimpleNode();\n                }\n            } catch {\n                clearTimer();\n            }\n        }, 3000);\n    }\n};\n\nconst quickJump = (item: any) => {\n    if (item.name === 'File') {\n        return routerToFileWithPath(item.detail);\n    }\n    return routerToPath(item.router);\n};\n\nconst showSimpleNode = () => {\n    return (\n        simpleNodeCarouselSetting.value === 'Enable' &&\n        globalStore.isMasterProductPro &&\n        simpleNodes.value?.length !== 0\n    );\n};\n\nconst toggleSensitiveInfo = () => {\n    showSensitiveInfo.value = !showSensitiveInfo.value;\n};\n\nconst refreshDashboard = async () => {\n    clearDashboardCache();\n    onLoadBaseInfo(false, '');\n    hasRefreshedOptionsOnHover.value = false;\n    await Promise.allSettled([onLoadNetworkOptions(true), onLoadIOOptions(true), loadSettingInfo()]);\n    MsgSuccess(i18n.global.t('commons.msg.refreshSuccess'));\n};\n\nconst jumpPanel = (row: any) => {\n    let entrance = row.securityEntrance.startsWith('/') ? row.securityEntrance.slice(1) : row.securityEntrance;\n    entrance = entrance ? '/' + entrance : '';\n    let addr = row.addr.endsWith('/') ? row.addr.slice(0, -1) : row.addr;\n    window.open(addr + entrance, '_blank', 'noopener,noreferrer');\n};\n\nconst onLoadCurrentInfo = async () => {\n    const res = await loadCurrentInfo(searchInfo.ioOption, searchInfo.netOption);\n    currentInfo.value.timeSinceUptime = res.data.timeSinceUptime;\n\n    let timeInterval = Number(res.data.uptime - currentInfo.value.uptime) || 3;\n    currentChartInfo.netBytesSent =\n        res.data.netBytesSent - currentInfo.value.netBytesSent > 0\n            ? Number(((res.data.netBytesSent - currentInfo.value.netBytesSent) / 1024 / timeInterval).toFixed(2))\n            : 0;\n    netBytesSents.value.push(currentChartInfo.netBytesSent);\n    if (netBytesSents.value.length > 20) {\n        netBytesSents.value.splice(0, 1);\n    }\n\n    currentChartInfo.netBytesRecv =\n        res.data.netBytesRecv - currentInfo.value.netBytesRecv > 0\n            ? Number(((res.data.netBytesRecv - currentInfo.value.netBytesRecv) / 1024 / timeInterval).toFixed(2))\n            : 0;\n    netBytesRecvs.value.push(currentChartInfo.netBytesRecv);\n    if (netBytesRecvs.value.length > 20) {\n        netBytesRecvs.value.splice(0, 1);\n    }\n\n    currentChartInfo.ioReadBytes =\n        res.data.ioReadBytes - currentInfo.value.ioReadBytes > 0\n            ? Number(((res.data.ioReadBytes - currentInfo.value.ioReadBytes) / 1024 / 1024 / timeInterval).toFixed(2))\n            : 0;\n    ioReadBytes.value.push(currentChartInfo.ioReadBytes);\n    if (ioReadBytes.value.length > 20) {\n        ioReadBytes.value.splice(0, 1);\n    }\n\n    currentChartInfo.ioWriteBytes =\n        res.data.ioWriteBytes - currentInfo.value.ioWriteBytes > 0\n            ? Number(((res.data.ioWriteBytes - currentInfo.value.ioWriteBytes) / 1024 / 1024 / timeInterval).toFixed(2))\n            : 0;\n    ioWriteBytes.value.push(currentChartInfo.ioWriteBytes);\n    if (ioWriteBytes.value.length > 20) {\n        ioWriteBytes.value.splice(0, 1);\n    }\n    currentChartInfo.ioCount = Math.round(Number((res.data.ioCount - currentInfo.value.ioCount) / timeInterval));\n    let ioReadTime = res.data.ioReadTime - currentInfo.value.ioReadTime;\n    let ioWriteTime = res.data.ioWriteTime - currentInfo.value.ioWriteTime;\n    let ioChoose = ioReadTime > ioWriteTime ? ioReadTime : ioWriteTime;\n    currentChartInfo.ioTime = Math.round(Number(ioChoose / timeInterval));\n\n    timeIODatas.value.push(dateFormatForSecond(res.data.shotTime));\n    if (timeIODatas.value.length > 20) {\n        timeIODatas.value.splice(0, 1);\n    }\n    timeNetDatas.value.push(dateFormatForSecond(res.data.shotTime));\n    if (timeNetDatas.value.length > 20) {\n        timeNetDatas.value.splice(0, 1);\n    }\n    loadData();\n    updateCurrentInfo(res.data);\n    statusRef.value?.acceptParams(currentInfo.value, baseInfo.value);\n};\n\nconst handleCopy = () => {\n    let content =\n        i18n.global.t('home.hostname') +\n        ': ' +\n        baseInfo.value.hostname +\n        '\\n' +\n        i18n.global.t('home.platformVersion') +\n        ': ' +\n        (baseInfo.value.prettyDistro\n            ? baseInfo.value.prettyDistro\n            : baseInfo.value.platformVersion\n            ? baseInfo.value.platform + '-' + baseInfo.value.platformVersion\n            : baseInfo.value.platform) +\n        '\\n' +\n        i18n.global.t('home.kernelVersion') +\n        ': ' +\n        baseInfo.value.kernelVersion +\n        '\\n' +\n        i18n.global.t('home.kernelArch') +\n        ': ' +\n        baseInfo.value.kernelArch +\n        '\\n' +\n        i18n.global.t('home.ip') +\n        ': ' +\n        baseInfo.value.ipV4Addr +\n        '\\n' +\n        i18n.global.t('home.uptime') +\n        ': ' +\n        currentInfo.value.timeSinceUptime +\n        '\\n' +\n        i18n.global.t('home.runningTime') +\n        ': ' +\n        loadUpTime(currentInfo.value.timeSinceUptime) +\n        '\\n';\n    copyText(content);\n};\n\nconst loadMemo = async () => {\n    try {\n        const res = await getMemo();\n        memoContent.value = res.data || '';\n    } catch (error) {\n        memoContent.value = '';\n    }\n};\n\nconst updateDashboardCarouselSetting = async (key: string, value: 'Enable' | 'Disable') => {\n    if (!carouselSettingReady.value) {\n        return;\n    }\n    let target;\n    if (key === 'DashboardMemoVisible') {\n        target = memoCarouselSetting.value;\n        clearDashboardCacheByPrefix(['memoCarouselSetting']);\n    } else {\n        target = simpleNodeCarouselSetting.value;\n        clearDashboardCacheByPrefix(['simpleNodeCarouselSetting']);\n    }\n    const previous = value === 'Enable' ? 'Disable' : 'Enable';\n    try {\n        await updateSetting({ key, value });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    } catch (error) {\n        target.value = previous;\n    }\n};\n\nconst startMemoEdit = () => {\n    memoEditContent.value = memoContent.value;\n    memoEditing.value = true;\n};\n\nconst cancelMemoEdit = () => {\n    memoEditing.value = false;\n    memoEditContent.value = '';\n};\n\nconst saveMemo = async () => {\n    memoSaving.value = true;\n    try {\n        await updateMemo(memoEditContent.value);\n        memoContent.value = memoEditContent.value;\n        memoEditing.value = false;\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    } finally {\n        memoSaving.value = false;\n    }\n};\n\nconst loadData = async () => {\n    if (chartOption.value === 'io') {\n        chartsOption.value['ioChart'] = {\n            xData: timeIODatas.value,\n            yData: [\n                {\n                    name: i18n.global.t('monitor.read'),\n                    data: ioReadBytes.value,\n                },\n                {\n                    name: i18n.global.t('monitor.write'),\n                    data: ioWriteBytes.value,\n                },\n            ],\n            formatStr: 'MB',\n        };\n    } else {\n        chartsOption.value['networkChart'] = {\n            xData: timeNetDatas.value,\n            yData: [\n                {\n                    name: i18n.global.t('monitor.up'),\n                    data: netBytesSents.value,\n                },\n                {\n                    name: i18n.global.t('monitor.down'),\n                    data: netBytesRecvs.value,\n                },\n            ],\n            formatStr: 'KB/s',\n        };\n    }\n};\n\nconst hideEntrance = () => {\n    globalStore.setShowEntranceWarn(false);\n};\n\nconst loadUpgradeStatus = async () => {\n    const res = await loadUpgradeInfo();\n    if (res && (res.data.testVersion || res.data.newVersion || res.data.latestVersion)) {\n        globalStore.hasNewVersion = true;\n    } else {\n        globalStore.hasNewVersion = false;\n    }\n};\n\nconst loadSettingInfo = async () => {\n    const safeCache = getDashboardCache('safeStatus');\n    const memoCache = getDashboardCache('memoCarouselSetting');\n    const simpleNodeCache = getDashboardCache('simpleNodeCarouselSetting');\n    if (safeCache === null || memoCache === null || simpleNodeCache === null) {\n        const res = await getSettingInfo();\n        isSafety.value = res.data.securityEntrance;\n        memoCarouselSetting.value = res.data.dashboardMemoVisible;\n        simpleNodeCarouselSetting.value = res.data.dashboardSimpleNodeVisible;\n        setDashboardCache('safeStatus', isSafety.value, DASHBOARD_CACHE_TTL.safeStatus);\n        setDashboardCache('memoCarouselSetting', memoCarouselSetting.value, DASHBOARD_CACHE_TTL.safeStatus);\n        setDashboardCache('simpleNodeCarouselSetting', simpleNodeCarouselSetting.value, DASHBOARD_CACHE_TTL.safeStatus);\n        if (!carouselSettingReady.value) {\n            await nextTick();\n            carouselSettingReady.value = true;\n        }\n        return;\n    }\n    isSafety.value = safeCache;\n    memoCarouselSetting.value = memoCache;\n    simpleNodeCarouselSetting.value = simpleNodeCache;\n    if (!carouselSettingReady.value) {\n        await nextTick();\n        carouselSettingReady.value = true;\n    }\n};\n\nconst loadSource = (row: any) => {\n    if (row.status !== 'Healthy') {\n        return `- ${i18n.global.t('commons.units.core')} (-%) / - GB (-%)`;\n    }\n    return (\n        row.cpuTotal +\n        ' ' +\n        i18n.global.t('commons.units.core') +\n        ' (' +\n        row.cpuUsedPercent?.toFixed(2) +\n        '%) / ' +\n        computeSize(row.memoryTotal) +\n        ' (' +\n        row.memoryUsedPercent?.toFixed(2) +\n        '%)'\n    );\n};\n\nconst onFocus = () => {\n    isActive.value = true;\n};\nconst onBlur = () => {\n    isActive.value = false;\n};\n\nconst toUpload = () => {\n    licenseRef.value.acceptParams();\n};\n\nconst refreshOptionsOnHover = async () => {\n    if (hasRefreshedOptionsOnHover.value) return;\n    if (!netOptionsFromCache.value && !ioOptionsFromCache.value) return;\n    hasRefreshedOptionsOnHover.value = true;\n    if (netOptionsFromCache.value) {\n        await onLoadNetworkOptions(true);\n    }\n    if (ioOptionsFromCache.value) {\n        await onLoadIOOptions(true);\n    }\n};\n\nconst scheduleDeferredFetch = () => {\n    setTimeout(() => {\n        onLoadSimpleNode();\n        onLoadNetworkOptions();\n        onLoadIOOptions();\n    }, 600);\n};\n\nconst fetchData = async () => {\n    window.addEventListener('focus', onFocus);\n    window.addEventListener('blur', onBlur);\n    hasRefreshedOptionsOnHover.value = false;\n    loadSettingInfo();\n    onLoadAgentSettingInfo();\n    onLoadBaseInfo(true, 'all');\n    scheduleDeferredFetch();\n    setTimeout(() => {\n        loadUpgradeStatus();\n    }, 2000);\n};\n\nconst loadWelcome = async () => {\n    await getWelcomePage().then((res) => {\n        if (res.data) {\n            welcomeOpen.value = true;\n            nextTick(() => {\n                const shadowRoot = shadowContainer.value.attachShadow({ mode: 'open' });\n                shadowRoot.innerHTML = res.data;\n            });\n            localStorage.setItem('welcomeShow', 'false');\n        } else {\n            localStorage.setItem('welcomeShow', 'false');\n        }\n    });\n};\n\nonBeforeRouteUpdate((to, from, next) => {\n    if (to.name === 'home') {\n        clearTimer();\n        fetchData();\n    }\n    next();\n});\n\nconst clearTimer = () => {\n    clearInterval(Number(timer));\n    timer = null;\n};\n\nonMounted(() => {\n    fetchData();\n    loadMemo();\n    if (localStorage.getItem('welcomeShow') !== 'false') {\n        loadWelcome();\n    }\n});\n\nonBeforeUnmount(() => {\n    window.removeEventListener('focus', onFocus);\n    window.removeEventListener('blur', onBlur);\n    isCurrentActive.value = false;\n    clearTimer();\n});\n</script>\n\n<style lang=\"scss\">\n.h-overview {\n    text-align: center;\n\n    span:first-child {\n        font-size: 14px;\n        color: var(--el-text-color-regular);\n    }\n\n    @media only screen and (max-width: 1300px) {\n        span:first-child {\n            font-size: 12px;\n            color: var(--el-text-color-regular);\n        }\n    }\n\n    .count {\n        margin-top: 10px;\n\n        span {\n            font-size: 18px;\n            color: $primary-color;\n            line-height: 32px;\n            cursor: pointer;\n        }\n    }\n}\n\n.h-systemInfo {\n    margin-left: 18px;\n    height: 306px;\n}\n\n@-moz-document url-prefix() {\n    .h-systemInfo {\n        height: auto;\n    }\n}\n\n.system-label {\n    font-weight: 400 !important;\n    font-size: 14px !important;\n    color: var(--panel-text-color);\n    border: none !important;\n    background: none !important;\n    width: fit-content !important;\n    white-space: nowrap !important;\n}\n\n.system-content {\n    font-size: 13px !important;\n    border: none !important;\n    width: 100% !important;\n}\n\n.my-carousel {\n    &.no-indicator {\n        .el-carousel__indicators {\n            display: none;\n        }\n    }\n\n    .el-carousel__button {\n        margin-bottom: -4px;\n        background-color: var(--el-text-color-regular);\n    }\n\n    .el-carousel__indicator.is-active .el-carousel__button {\n        background-color: var(--panel-color-primary);\n    }\n\n    .el-descriptions .el-descriptions__body .el-descriptions__table {\n        border-spacing: 0 5px !important;\n    }\n}\n\n.simple-node {\n    padding: 10px 15px 10px 0px;\n    margin: -8px 10px 3px 20px;\n\n    &:hover {\n        background-color: rgba(0, 94, 235, 0.03);\n    }\n\n    .name {\n        font-weight: 500 !important;\n        font-size: 18px !important;\n        line-height: 30px;\n        color: var(--panel-text-color);\n    }\n\n    .detail {\n        font-size: 12px !important;\n    }\n\n    .visit {\n        margin-bottom: -25px;\n    }\n}\n\n.h-app-divider {\n    margin-top: 3px;\n    border: 0;\n    border-top: var(--panel-border);\n}\n\n.monitor-tags {\n    position: absolute;\n    top: -10px;\n    left: 20px;\n\n    .el-tag {\n        margin-right: 10px;\n        margin-bottom: 10px;\n    }\n}\n\n.version {\n    font-size: 14px;\n    color: #858585;\n    text-decoration: none;\n    letter-spacing: 0.5px;\n}\n\n.system-link {\n    margin-left: 15px;\n\n    .svg-icon {\n        font-size: 7px;\n    }\n\n    span {\n        line-height: 20px;\n    }\n}\n\n.chart-card {\n    min-height: 383px;\n}\n\n.memo-container {\n    height: 270px;\n}\n\n.memo-card {\n    height: 368px;\n}\n\n.memo-content {\n    min-height: 218px;\n    border-radius: 4px;\n    border: 1px solid var(--el-border-color);\n    background-color: var(--el-fill-color-light);\n    word-wrap: break-word;\n    white-space: pre-wrap;\n\n    .md-editor {\n        background-color: transparent;\n    }\n}\n\n.memo-placeholder {\n    color: var(--el-text-color-placeholder);\n    display: inline-block;\n    font-size: 14px;\n}\n\n.dashboard-carousel-setting {\n    display: flex;\n    flex-direction: column;\n\n    .setting-item {\n        display: flex;\n        align-items: center;\n        justify-content: space-between;\n    }\n}\n\n.dashboard-carousel-popover {\n    .el-popover__title {\n        padding-bottom: 10px;\n        margin-bottom: 8px;\n        border-bottom: 1px solid var(--el-border-color);\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/home/quick/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('menu.home')\" @close=\"handleClose\">\n        <div>\n            <ComplexTable :heightDiff=\"1\" :data=\"quickOptions\" :show-header=\"false\" row-key=\"id\">\n                <el-table-column prop=\"title\" :label=\"$t('setting.menu')\">\n                    <template #default=\"{ row }\">\n                        {{ i18n.global.t(row.title) }}\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"isShow\" :label=\"$t('setting.ifShow')\">\n                    <template #default=\"{ row }\">\n                        <el-switch v-model=\"row.isShow\" />\n                        <div v-if=\"row.name === 'File' && row.isShow\">\n                            <el-input v-model=\"row.detail\" class=\"w-full\">\n                                <template #prepend>\n                                    <el-button\n                                        class=\"w-16\"\n                                        v-if=\"row.name === 'File' && row.isShow\"\n                                        icon=\"Folder\"\n                                        @click=\"fileRef.acceptParams({ path: row.detail, isAll: true })\"\n                                    />\n                                </template>\n                            </el-input>\n                            <el-input v-model=\"row.alias\" class=\"mt-1\">\n                                <template #prepend>\n                                    <el-button class=\"w-16\">\n                                        {{ $t('home.alias') }}\n                                    </el-button>\n                                </template>\n                            </el-input>\n                        </div>\n                    </template>\n                </el-table-column>\n            </ComplexTable>\n        </div>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onChangeShow\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n        <FileList ref=\"fileRef\" @choose=\"loadDir\" />\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgInfo, MsgSuccess } from '@/utils/message';\nimport FileList from '@/components/file-list/index.vue';\nimport { changeQuick, loadQuickOption } from '@/api/modules/dashboard';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\nconst drawerVisible = ref();\nconst loading = ref();\nconst acceptParams = (): void => {\n    search();\n    drawerVisible.value = true;\n};\nconst quickOptions = ref([]);\nconst fileRef = ref();\n\nconst search = async () => {\n    loading.value = true;\n    await loadQuickOption()\n        .then((res) => {\n            loading.value = false;\n            quickOptions.value = res.data || [];\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst onChangeShow = async () => {\n    let i = 0;\n    for (const item of quickOptions.value) {\n        if (item.isShow) {\n            i++;\n        }\n    }\n    if (i === 0) {\n        MsgInfo(i18n.global.t('home.minQuickJump'));\n        return;\n    }\n    if (i > 4) {\n        MsgInfo(i18n.global.t('home.maxQuickJump'));\n        return;\n    }\n    loading.value = true;\n    await changeQuick(quickOptions.value)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            drawerVisible.value = false;\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadDir = async (path: string) => {\n    for (const item of quickOptions.value) {\n        if (item.name === 'File') {\n            item.detail = path;\n        }\n    }\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n    emit('search');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/home/status/index.vue",
    "content": "<template>\n    <div class=\"custom-row\">\n        <el-col :xs=\"6\" :sm=\"6\" :md=\"3\" :lg=\"3\" :xl=\"3\" align=\"center\">\n            <el-popover\n                :hide-after=\"20\"\n                :teleported=\"false\"\n                :width=\"320\"\n                v-if=\"chartsOption['load']\"\n                @hide=\"onCpuPopoverHide\"\n            >\n                <el-descriptions :column=\"1\" size=\"small\">\n                    <el-descriptions-item :label=\"$t('home.loadAverage', [1])\">\n                        {{ formatNumber(currentInfo.load1) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.loadAverage', [5])\">\n                        {{ formatNumber(currentInfo.load5) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.loadAverage', [15])\">\n                        {{ formatNumber(currentInfo.load15) }}\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <el-button link size=\"small\" type=\"primary\" class=\"float-left mb-2\" @click=\"toggleCpuTop\">\n                    {{ $t('home.cpuTop') }}\n                    <el-icon v-if=\"!showCpuTop\"><ArrowRight /></el-icon>\n                    <el-icon v-if=\"showCpuTop\"><ArrowDown /></el-icon>\n                </el-button>\n                <ComplexTable v-if=\"showCpuTop\" :data=\"currentInfo.topCPUItems\">\n                    <el-table-column :min-width=\"120\" show-overflow-tooltip :label=\"$t('menu.process')\" prop=\"name\" />\n                    <el-table-column :min-width=\"60\" :label=\"$t('monitor.percent')\" prop=\"percent\">\n                        <template #default=\"{ row }\">{{ row.percent.toFixed(2) }}%</template>\n                    </el-table-column>\n                    <el-table-column :width=\"80\" :label=\"$t('commons.table.operate')\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"onKill(row)\">\n                                {{ $t('process.stopProcess') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                </ComplexTable>\n                <template #reference>\n                    <v-charts\n                        height=\"160px\"\n                        id=\"load\"\n                        type=\"pie\"\n                        :option=\"chartsOption['load']\"\n                        v-if=\"chartsOption['load']\"\n                    />\n                </template>\n            </el-popover>\n            <span class=\"input-help\">{{ loadStatus(currentInfo.loadUsagePercent) }}</span>\n        </el-col>\n        <el-col :xs=\"6\" :sm=\"6\" :md=\"3\" :lg=\"3\" :xl=\"3\">\n            <el-popover\n                :hide-after=\"20\"\n                :teleported=\"false\"\n                :width=\"430\"\n                v-if=\"chartsOption['cpu']\"\n                @hide=\"onCpuPopoverHide\"\n            >\n                <el-descriptions :title=\"baseInfo.cpuModelName\" class=\"ml-1\" :column=\"3\" size=\"small\">\n                    <el-descriptions-item :label=\"$t('home.core')\">\n                        {{ baseInfo.cpuCores }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.logicCore')\">\n                        {{ baseInfo.cpuLogicalCores }}\n                    </el-descriptions-item>\n                    <el-descriptions-item v-if=\"baseInfo.cpuMhz > 0\" :label=\"$t('home.cpuFrequency')\">\n                        {{ formatNumber(baseInfo.cpuMhz) }} MHz\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <el-button size=\"small\" link type=\"primary\" class=\"mb-2\">\n                    {{ $t('home.corePercent') }}\n                </el-button>\n                <el-space wrap :size=\"5\" class=\"ml-1\">\n                    <template v-for=\"(item, index) of currentInfo.cpuPercent\" :key=\"index\">\n                        <div class=\"cpu-detail\" v-if=\"cpuShowAll || (!cpuShowAll && index < 8)\">\n                            CPU-{{ index }}: {{ formatNumber(item) }}%\n                        </div>\n                    </template>\n                </el-space>\n                <div v-if=\"currentInfo.cpuPercent.length > 8\">\n                    <el-button v-if=\"!cpuShowAll\" @click=\"cpuShowAll = true\" icon=\"More\" link size=\"small\" />\n                    <el-button v-if=\"cpuShowAll\" @click=\"cpuShowAll = false\" icon=\"ArrowUp\" link size=\"small\" />\n                </div>\n\n                <div v-if=\"currentInfo.cpuDetailedPercent?.length >= 8\" class=\"mt-2\">\n                    <el-button\n                        link\n                        size=\"small\"\n                        type=\"primary\"\n                        class=\"mb-2\"\n                        @click=\"showCpuDetailedPercent = !showCpuDetailedPercent\"\n                    >\n                        {{ $t('home.cpuDetailedPercent') }}\n                        <el-icon v-if=\"!showCpuDetailedPercent\"><ArrowRight /></el-icon>\n                        <el-icon v-if=\"showCpuDetailedPercent\"><ArrowDown /></el-icon>\n                    </el-button>\n                    <el-space wrap :size=\"5\" class=\"ml-1 mb-2\" v-if=\"showCpuDetailedPercent\">\n                        <div class=\"cpu-detail\">\n                            {{ $t('home.cpuUser') }}: {{ formatNumber(currentInfo.cpuDetailedPercent[0]) }}%\n                        </div>\n                        <div class=\"cpu-detail\">\n                            {{ $t('home.cpuSystem') }}: {{ formatNumber(currentInfo.cpuDetailedPercent[1]) }}%\n                        </div>\n                        <div class=\"cpu-detail\">Nice: {{ formatNumber(currentInfo.cpuDetailedPercent[2]) }}%</div>\n                        <div class=\"cpu-detail\">\n                            {{ $t('home.cpuIdle') }}: {{ formatNumber(currentInfo.cpuDetailedPercent[3]) }}%\n                        </div>\n                        <div class=\"cpu-detail\">I/O: {{ formatNumber(currentInfo.cpuDetailedPercent[4]) }}%</div>\n                        <div class=\"cpu-detail\">\n                            {{ $t('home.cpuIrq') }}: {{ formatNumber(currentInfo.cpuDetailedPercent[5]) }}%\n                        </div>\n                        <div class=\"cpu-detail\">\n                            {{ $t('home.cpuSoftirq') }}: {{ formatNumber(currentInfo.cpuDetailedPercent[6]) }}%\n                        </div>\n                        <div class=\"cpu-detail\">\n                            {{ $t('home.cpuSteal') }}: {{ formatNumber(currentInfo.cpuDetailedPercent[7]) }}%\n                        </div>\n                    </el-space>\n                </div>\n\n                <el-button link size=\"small\" type=\"primary\" class=\"mt-2 mb-2\" @click=\"toggleCpuTop\">\n                    {{ $t('home.cpuTop') }}\n                    <el-icon v-if=\"!showCpuTop\"><ArrowRight /></el-icon>\n                    <el-icon v-if=\"showCpuTop\"><ArrowDown /></el-icon>\n                </el-button>\n                <ComplexTable v-if=\"showCpuTop\" :data=\"currentInfo.topCPUItems\">\n                    <el-table-column :min-width=\"120\" show-overflow-tooltip :label=\"$t('menu.process')\" prop=\"name\" />\n                    <el-table-column :min-width=\"60\" :label=\"$t('monitor.percent')\" prop=\"percent\">\n                        <template #default=\"{ row }\">{{ row.percent.toFixed(2) }}%</template>\n                    </el-table-column>\n                    <el-table-column :width=\"80\" :label=\"$t('commons.table.operate')\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"onKill(row)\">\n                                {{ $t('process.stopProcess') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                </ComplexTable>\n                <template #reference>\n                    <v-charts\n                        height=\"160px\"\n                        id=\"cpu\"\n                        type=\"pie\"\n                        :option=\"chartsOption['cpu']\"\n                        v-if=\"chartsOption['cpu']\"\n                    />\n                </template>\n            </el-popover>\n            <div class=\"text-center\">\n                <span class=\"input-help\">\n                    ( {{ formatNumber(currentInfo.cpuUsed) }} / {{ currentInfo.cpuTotal }} )\n                    {{ $t('commons.units.core', currentInfo.cpuTotal) }}\n                </span>\n            </div>\n        </el-col>\n        <el-col :xs=\"6\" :sm=\"6\" :md=\"3\" :lg=\"3\" :xl=\"3\" align=\"center\">\n            <el-popover\n                :hide-after=\"20\"\n                :teleported=\"false\"\n                :width=\"480\"\n                v-if=\"chartsOption['memory']\"\n                @hide=\"onMemPopoverHide\"\n            >\n                <el-descriptions direction=\"vertical\" :title=\"$t('home.mem')\" class=\"ml-1\" :column=\"4\" size=\"small\">\n                    <el-descriptions-item :label-width=\"60\" :label=\"$t('home.total')\">\n                        {{ computeSize(currentInfo.memoryTotal) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.used')\">\n                        {{ computeSize(currentInfo.memoryUsed) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.free')\">\n                        {{ computeSize(currentInfo.memoryFree) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.available')\">\n                        {{ computeSize(currentInfo.memoryAvailable) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.shard')\">\n                        {{ computeSize(currentInfo.memoryShard) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.cache')\">\n                        {{ computeSize(currentInfo.memoryCache) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('home.percent')\">\n                        {{ formatNumber(currentInfo.memoryUsedPercent) }}%\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <el-descriptions\n                    v-if=\"currentInfo.swapMemoryTotal\"\n                    direction=\"vertical\"\n                    :title=\"$t('home.swapMem')\"\n                    :column=\"4\"\n                    size=\"small\"\n                    class=\"ml-1\"\n                >\n                    <el-descriptions-item :label-width=\"60\" :label=\"$t('home.total')\">\n                        {{ computeSize(currentInfo.swapMemoryTotal) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label-width=\"60\" :label=\"$t('home.used')\">\n                        {{ computeSize(currentInfo.swapMemoryUsed) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label-width=\"60\" :label=\"$t('home.free')\">\n                        {{ computeSize(currentInfo.swapMemoryAvailable) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label-width=\"60\" :label=\"$t('home.percent')\">\n                        {{ formatNumber(currentInfo.swapMemoryUsedPercent) }}%\n                    </el-descriptions-item>\n                </el-descriptions>\n\n                <el-button link size=\"small\" type=\"primary\" class=\"float-left mb-2\" @click=\"toggleMemTop\">\n                    {{ $t('home.memTop') }}\n                    <el-icon v-if=\"!showMemTop\"><ArrowRight /></el-icon>\n                    <el-icon v-if=\"showMemTop\"><ArrowDown /></el-icon>\n                </el-button>\n                <ComplexTable v-if=\"showMemTop\" :data=\"currentInfo.topMemItems\">\n                    <el-table-column :min-width=\"120\" show-overflow-tooltip :label=\"$t('menu.process')\" prop=\"name\" />\n                    <el-table-column :min-width=\"100\" :label=\"$t('monitor.memory')\" prop=\"memory\">\n                        <template #default=\"{ row }\">\n                            {{ computeSize(row.memory) }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :min-width=\"80\" :label=\"$t('monitor.percent')\" prop=\"percent\">\n                        <template #default=\"{ row }\">{{ row.percent.toFixed(2) }}%</template>\n                    </el-table-column>\n                    <el-table-column :width=\"80\" :label=\"$t('commons.table.operate')\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"onKill(row)\">\n                                {{ $t('process.stopProcess') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                </ComplexTable>\n                <template #reference>\n                    <v-charts\n                        height=\"160px\"\n                        id=\"memory\"\n                        type=\"pie\"\n                        :option=\"chartsOption['memory']\"\n                        v-if=\"chartsOption['memory']\"\n                    />\n                </template>\n            </el-popover>\n            <span class=\"input-help\">\n                {{ computeSize(currentInfo.memoryUsed) }} / {{ computeSize(currentInfo.memoryTotal) }}\n            </span>\n        </el-col>\n        <template v-for=\"(item, index) of currentInfo.diskData\" :key=\"index\">\n            <el-col :xs=\"6\" :sm=\"6\" :md=\"3\" :lg=\"3\" :xl=\"3\" align=\"center\" v-if=\"isShow('disk', index)\">\n                <el-popover :hide-after=\"20\" :teleported=\"false\" :width=\"450\" v-if=\"chartsOption[`disk${index}`]\">\n                    <el-descriptions :column=\"1\" size=\"small\">\n                        <el-descriptions-item :label=\"$t('home.mount')\">\n                            {{ item.path }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.type')\">\n                            {{ item.type }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('home.fileSystem')\">\n                            {{ item.device }}\n                        </el-descriptions-item>\n                    </el-descriptions>\n                    <el-descriptions title=\"Inode\" direction=\"vertical\" :column=\"4\" size=\"small\">\n                        <el-descriptions-item :label=\"$t('home.total')\">{{ item.inodesTotal }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('home.used')\">{{ item.inodesUsed }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('home.free')\">{{ item.inodesFree }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('home.percent')\">\n                            {{ formatNumber(item.inodesUsedPercent) }}%\n                        </el-descriptions-item>\n                    </el-descriptions>\n\n                    <el-descriptions :title=\"$t('monitor.disk')\" direction=\"vertical\" :column=\"4\" size=\"small\">\n                        <el-descriptions-item :label=\"$t('home.total')\">\n                            {{ computeSize(item.total) }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('home.used')\">\n                            {{ computeSize(item.used) }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('home.free')\">\n                            {{ computeSize(item.free) }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('home.percent')\">\n                            {{ formatNumber(item.usedPercent) }}%\n                        </el-descriptions-item>\n                    </el-descriptions>\n                    <template #reference>\n                        <v-charts\n                            @click=\"routerToFileWithPath(item.path)\"\n                            height=\"160px\"\n                            :id=\"`disk${index}`\"\n                            type=\"pie\"\n                            :option=\"chartsOption[`disk${index}`]\"\n                            v-if=\"chartsOption[`disk${index}`]\"\n                        />\n                    </template>\n                </el-popover>\n                <span class=\"input-help\">{{ computeSize(item.used) }} / {{ computeSize(item.total) }}</span>\n            </el-col>\n        </template>\n        <template v-for=\"(item, index) of currentInfo.gpuData\" :key=\"index\">\n            <el-col :xs=\"6\" :sm=\"6\" :md=\"3\" :lg=\"3\" :xl=\"3\" align=\"center\" v-if=\"isShow('gpu', index)\">\n                <el-popover :hide-after=\"20\" :teleported=\"false\" :width=\"450\" v-if=\"chartsOption[`gpu${index}`]\">\n                    <el-descriptions :title=\"item.productName\" direction=\"vertical\" :column=\"3\" size=\"small\">\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.gpuUtil')\">\n                            {{ item.gpuUtil }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.temperature')\">\n                            {{ item.temperature.replaceAll('C', '°C') }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.performanceState')\">\n                            {{ item.performanceState }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.powerUsage')\">\n                            {{ item.powerUsage }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.memoryUsage')\">\n                            {{ item.memoryUsage }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.fanSpeed')\">\n                            {{ item.fanSpeed }}\n                        </el-descriptions-item>\n                    </el-descriptions>\n                    <template #reference>\n                        <v-charts\n                            @click=\"goGPU()\"\n                            height=\"160px\"\n                            :id=\"`gpu${index}`\"\n                            type=\"pie\"\n                            :option=\"chartsOption[`gpu${index}`]\"\n                            v-if=\"chartsOption[`gpu${index}`]\"\n                        />\n                    </template>\n                </el-popover>\n                <el-tooltip :content=\"item.productName\" v-if=\"item.productName.length > 25\">\n                    <span class=\"input-help\">{{ item.productName.substring(0, 22) }}...</span>\n                </el-tooltip>\n                <span class=\"input-help\" v-else>{{ item.productName }}</span>\n            </el-col>\n        </template>\n        <template v-for=\"(item, index) of currentInfo.xpuData\" :key=\"index\">\n            <el-col :xs=\"6\" :sm=\"6\" :md=\"3\" :lg=\"3\" :xl=\"3\" align=\"center\" v-if=\"isShow('xpu', index)\">\n                <el-popover :hide-after=\"20\" :teleported=\"false\" :width=\"400\" v-if=\"chartsOption[`xpu${index}`]\">\n                    <el-descriptions :title=\"item.deviceName\" direction=\"vertical\" :column=\"3\" size=\"small\">\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.temperature')\">\n                            {{ item.temperature }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.powerUsage')\">\n                            {{ item.power }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('aiTools.gpu.memoryUsage')\">\n                            {{ item.memoryUsed }}/{{ item.memory }}\n                        </el-descriptions-item>\n                    </el-descriptions>\n                    <template #reference>\n                        <v-charts\n                            @click=\"goGPU()\"\n                            height=\"160px\"\n                            :id=\"`xpu${index}`\"\n                            type=\"pie\"\n                            :option=\"chartsOption[`xpu${index}`]\"\n                            v-if=\"chartsOption[`xpu${index}`]\"\n                        />\n                    </template>\n                </el-popover>\n                <el-tooltip :content=\"item.deviceName\" v-if=\"item.deviceName.length > 25\">\n                    <span class=\"input-help\">{{ item.deviceName.substring(0, 22) }}...</span>\n                </el-tooltip>\n                <span class=\"input-help\" v-else>{{ item.deviceName }}</span>\n            </el-col>\n        </template>\n        <el-col :xs=\"6\" :sm=\"6\" :md=\"3\" :lg=\"3\" :xl=\"3\" align=\"center\" v-if=\"totalCount > 5\">\n            <el-button v-if=\"!showMore\" link type=\"primary\" @click=\"changeShowMore(true)\" class=\"buttonClass\">\n                {{ $t('tabs.more') }}\n                <el-icon><Bottom /></el-icon>\n            </el-button>\n            <el-button v-if=\"showMore\" type=\"primary\" link @click=\"changeShowMore(false)\" class=\"buttonClass\">\n                {{ $t('tabs.hide') }}\n                <el-icon><Top /></el-icon>\n            </el-button>\n        </el-col>\n        <ConfirmDialog ref=\"confirmConfRef\" @confirm=\"submitKill\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { Dashboard } from '@/api/interface/dashboard';\nimport { computeSize } from '@/utils/util';\nimport i18n from '@/lang';\nimport { nextTick, onBeforeUnmount, ref } from 'vue';\nimport { routerToFileWithPath, routerToName } from '@/utils/router';\nimport { stopProcess } from '@/api/modules/process';\nimport { loadTopCPU, loadTopMem } from '@/api/modules/dashboard';\nimport { MsgSuccess } from '@/utils/message';\nconst showMore = ref(false);\nconst totalCount = ref();\n\nlet cpuPopoverTimer: ReturnType<typeof setTimeout> | null = null;\nlet memPopoverTimer: ReturnType<typeof setTimeout> | null = null;\nlet cpuLoading = false;\nlet memLoading = false;\n\nconst baseInfo = ref<Dashboard.BaseInfo>({\n    hostname: '',\n    os: '',\n    platform: '',\n    platformFamily: '',\n    platformVersion: '',\n    kernelArch: '',\n    kernelVersion: '',\n    virtualizationSystem: '',\n    ipV4Addr: '',\n    httpProxy: '',\n\n    cpuCores: 0,\n    cpuLogicalCores: 0,\n    cpuModelName: '',\n    cpuMhz: 0,\n    currentInfo: null,\n    prettyDistro: '',\n    quickJump: [],\n});\nconst currentInfo = ref<Dashboard.CurrentInfo>({\n    uptime: 0,\n    timeSinceUptime: '',\n    procs: 0,\n\n    load1: 0,\n    load5: 0,\n    load15: 0,\n    loadUsagePercent: 0,\n\n    cpuPercent: [] as Array<number>,\n    cpuUsedPercent: 0,\n    cpuUsed: 0,\n    cpuTotal: 0,\n    cpuDetailedPercent: [] as Array<number>,\n\n    memoryTotal: 0,\n    memoryAvailable: 0,\n    memoryCache: 0,\n    memoryFree: 0,\n    memoryShard: 0,\n    memoryUsed: 0,\n    memoryUsedPercent: 0,\n    swapMemoryTotal: 0,\n    swapMemoryAvailable: 0,\n    swapMemoryUsed: 0,\n    swapMemoryUsedPercent: 0,\n\n    ioReadBytes: 0,\n    ioWriteBytes: 0,\n    ioCount: 0,\n    ioReadTime: 0,\n    ioWriteTime: 0,\n\n    diskData: [],\n    gpuData: [],\n    xpuData: [],\n\n    topCPUItems: [],\n    topMemItems: [],\n\n    netBytesSent: 0,\n    netBytesRecv: 0,\n    shotTime: new Date(),\n});\n\nconst cpuShowAll = ref();\nconst showCpuDetailedPercent = ref(false);\nconst showCpuTop = ref(false);\nconst showMemTop = ref(false);\nconst killProcessID = ref();\nconst confirmConfRef = ref();\n\nconst chartsOption = ref({\n    cpu: { title: 'CPU', data: 0 },\n    memory: { title: i18n.global.t('monitor.memory'), data: 0 },\n    load: { title: i18n.global.t('home.load'), data: 0 },\n});\n\nconst acceptParams = (current: Dashboard.CurrentInfo, base: Dashboard.BaseInfo): void => {\n    currentInfo.value = current;\n    baseInfo.value = base;\n    chartsOption.value['cpu'] = {\n        title: 'CPU',\n        data: formatNumber(currentInfo.value.cpuUsedPercent),\n    };\n    chartsOption.value['memory'] = {\n        title: i18n.global.t('monitor.memory'),\n        data: formatNumber(currentInfo.value.memoryUsedPercent),\n    };\n    chartsOption.value['load'] = {\n        title: i18n.global.t('home.load'),\n        data: formatNumber(currentInfo.value.loadUsagePercent),\n    };\n    currentInfo.value.diskData = currentInfo.value.diskData || [];\n    nextTick(() => {\n        for (let i = 0; i < currentInfo.value.diskData.length; i++) {\n            let itemPath = currentInfo.value.diskData[i].path;\n            itemPath = itemPath.length > 12 ? itemPath.substring(0, 9) + '...' : itemPath;\n            chartsOption.value['disk' + i] = {\n                title: itemPath,\n                data: formatNumber(currentInfo.value.diskData[i].usedPercent),\n            };\n        }\n        currentInfo.value.gpuData = currentInfo.value.gpuData || [];\n        for (let i = 0; i < currentInfo.value.gpuData.length; i++) {\n            chartsOption.value['gpu' + i] = {\n                title: 'GPU-' + currentInfo.value.gpuData[i].index,\n                data: formatNumber(Number(currentInfo.value.gpuData[i].gpuUtil.replaceAll(' %', ''))),\n            };\n        }\n        currentInfo.value.xpuData = currentInfo.value.xpuData || [];\n        for (let i = 0; i < currentInfo.value.xpuData.length; i++) {\n            chartsOption.value['xpu' + i] = {\n                title: 'XPU-' + currentInfo.value.xpuData[i].deviceID,\n                data: formatNumber(Number(currentInfo.value.xpuData[i].memoryUtil.replaceAll('%', ''))),\n            };\n        }\n        totalCount.value =\n            currentInfo.value.diskData.length + currentInfo.value.gpuData.length + currentInfo.value.xpuData.length;\n        showMore.value = localStorage.getItem('dashboard_show') === 'more';\n    });\n};\n\nconst isShow = (val: string, index: number) => {\n    let showCount = totalCount.value < 6 ? 5 : 4;\n    switch (val) {\n        case 'disk':\n            return showMore.value || index < showCount;\n        case 'gpu':\n            let gpuCount = showCount - currentInfo.value.diskData.length;\n            return showMore.value || index < gpuCount;\n        case 'xpu':\n            let xpuCount = showCount - currentInfo.value.diskData.length - currentInfo.value.gpuData.length;\n            return showMore.value || index < xpuCount;\n    }\n};\n\nconst changeShowMore = (show: boolean) => {\n    showMore.value = show;\n    localStorage.setItem('dashboard_show', show ? 'more' : 'hide');\n};\n\nconst onKill = async (row: any) => {\n    let params = {\n        header: i18n.global.t('process.kill'),\n        operationInfo: i18n.global.t('process.killHelper'),\n        submitInputInfo: i18n.global.t('process.killNow'),\n    };\n    killProcessID.value = row.pid;\n    confirmConfRef.value!.acceptParams(params);\n};\nconst submitKill = async () => {\n    await stopProcess({ PID: killProcessID.value }).then(() => {\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\n\nfunction loadStatus(val: number) {\n    if (val < 30) {\n        return i18n.global.t('home.runSmoothly');\n    }\n    if (val < 70) {\n        return i18n.global.t('home.runNormal');\n    }\n    if (val < 80) {\n        return i18n.global.t('home.runSlowly');\n    }\n    return i18n.global.t('home.runJam');\n}\n\nconst goGPU = () => {\n    routerToName('GPU');\n};\n\nfunction formatNumber(val: number) {\n    return Number(val.toFixed(2));\n}\n\nconst toggleCpuTop = async () => {\n    showCpuTop.value = !showCpuTop.value;\n    if (showCpuTop.value) {\n        await loadTopCPUData();\n        if (cpuPopoverTimer) {\n            clearInterval(Number(cpuPopoverTimer));\n        }\n        cpuPopoverTimer = setInterval(loadTopCPUData, 5000);\n    } else {\n        if (cpuPopoverTimer) {\n            clearInterval(Number(cpuPopoverTimer));\n            cpuPopoverTimer = null;\n        }\n    }\n};\n\nconst onCpuPopoverHide = () => {\n    showCpuTop.value = false;\n    if (cpuPopoverTimer) {\n        clearInterval(Number(cpuPopoverTimer));\n        cpuPopoverTimer = null;\n    }\n};\n\nconst toggleMemTop = async () => {\n    showMemTop.value = !showMemTop.value;\n    if (showMemTop.value) {\n        await loadTopMemData();\n        if (memPopoverTimer) {\n            clearInterval(Number(memPopoverTimer));\n        }\n        memPopoverTimer = setInterval(loadTopMemData, 5000);\n    } else {\n        if (memPopoverTimer) {\n            clearInterval(Number(memPopoverTimer));\n            memPopoverTimer = null;\n        }\n    }\n};\n\nconst onMemPopoverHide = () => {\n    showMemTop.value = false;\n    if (memPopoverTimer) {\n        clearInterval(Number(memPopoverTimer));\n        memPopoverTimer = null;\n    }\n};\n\nconst loadTopCPUData = async () => {\n    if (cpuLoading) return;\n    cpuLoading = true;\n    try {\n        const res = await loadTopCPU();\n        currentInfo.value.topCPUItems = res.data || [];\n    } catch (_error) {\n        // ignore load errors\n    } finally {\n        cpuLoading = false;\n    }\n};\n\nconst loadTopMemData = async () => {\n    if (memLoading) return;\n    memLoading = true;\n    try {\n        const res = await loadTopMem();\n        currentInfo.value.topMemItems = res.data || [];\n    } catch (_error) {\n        // ignore load errors\n    } finally {\n        memLoading = false;\n    }\n};\n\nonBeforeUnmount(() => {\n    if (cpuPopoverTimer) {\n        clearInterval(Number(cpuPopoverTimer));\n        cpuPopoverTimer = null;\n    }\n    if (memPopoverTimer) {\n        clearInterval(Number(memPopoverTimer));\n        memPopoverTimer = null;\n    }\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.buttonClass {\n    margin-top: 28%;\n    font-size: 14px;\n}\n.cpu-detail {\n    font-size: 12px;\n    width: 95px;\n}\n:deep(.el-descriptions__label) {\n    width: 80px;\n    background-color: transparent !important;\n}\n.custom-row {\n    display: grid;\n    grid-template-columns: repeat(12, 1fr);\n    gap: 10px;\n    width: 100%;\n}\n.custom-row .el-col {\n    width: 100% !important;\n    max-width: 100% !important;\n    flex: none !important;\n    float: none !important;\n    display: block !important;\n}\n.custom-row .el-col.el-col-xs-6 {\n    grid-column: span 6;\n}\n@media (min-width: 768px) {\n    .custom-row .el-col.el-col-sm-6 {\n        grid-column: span 6;\n    }\n}\n@media (min-width: 992px) {\n    .custom-row .el-col.el-col-md-3 {\n        grid-column: span 3;\n    }\n}\n@media (min-width: 1200px) {\n    .custom-row .el-col.el-col-lg-3 {\n        grid-column: span 3;\n    }\n}\n@media (min-width: 1920px) {\n    .custom-row .el-col.el-col-xl-3 {\n        grid-column: span 3;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/disk-management/components/disk-card.vue",
    "content": "<template>\n    <el-card class=\"shadow-sm\">\n        <div class=\"border-b pb-4\">\n            <div class=\"flex items-center space-x-4\">\n                <div>\n                    <h3 class=\"text-lg\">\n                        {{ $t('home.disk') }}{{ $t('commons.table.name') }}: {{ diskInfo.device }}\n                        <el-tag size=\"small\" type=\"warning\" v-if=\"scope === 'system'\">\n                            {{ $t('disk.systemDisk') }}\n                        </el-tag>\n                        <el-tag\n                            size=\"small\"\n                            type=\"warning\"\n                            v-if=\"\n                                scope == 'unpartitioned' &&\n                                diskInfo.partitions == undefined &&\n                                diskInfo.mountPoint == ''\n                            \"\n                        >\n                            {{ $t('disk.unpartitionedDisk') }}\n                        </el-tag>\n                    </h3>\n                    <div class=\"flex items-center space-x-6 text-sm\">\n                        <el-text type=\"info\">{{ $t('container.size') }}: {{ diskInfo.size }}</el-text>\n                        <el-text type=\"info\">\n                            {{ $t('disk.partition') }}:\n                            <span v-if=\"diskInfo.partitions\">\n                                {{ diskInfo.partitions?.length }}\n                            </span>\n                            <span v-else>0</span>\n                        </el-text>\n                        <el-text type=\"info\" v-if=\"diskInfo.diskType\" class=\"flex items-center\">\n                            {{ $t('disk.diskType') }}:\n                            <el-tag class=\"ml-2\" size=\"small\" type=\"info\">{{ diskInfo.diskType }}</el-tag>\n                        </el-text>\n                        <el-text type=\"info\" v-if=\"diskInfo.model\" class=\"flex items-center\">\n                            {{ $t('disk.model') }}:\n                            <span class=\"ml-2\">{{ diskInfo.model }}</span>\n                        </el-text>\n                        <el-text type=\"info\" v-if=\"diskInfo.serial\" class=\"flex items-center\">\n                            {{ $t('disk.serial') }}:\n                            <span class=\"ml-2\">{{ diskInfo.serial }}</span>\n                        </el-text>\n                        <div\n                            v-if=\"\n                                scope == 'unpartitioned' &&\n                                diskInfo.partitions == undefined &&\n                                diskInfo.mountPoint == ''\n                            \"\n                        >\n                            <el-button type=\"primary\" size=\"small\" @click=\"handlePartition(diskInfo)\">\n                                {{ $t('disk.handlePartition') }}\n                            </el-button>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n        <div v-if=\"diskInfo.partitions && diskInfo.partitions.length > 0\">\n            <el-table :data=\"diskInfo.partitions\" class=\"w-full\">\n                <el-table-column prop=\"device\" :label=\"$t('disk.partition') + $t('commons.table.name')\" min-width=\"100\">\n                    <template #default=\"{ row }\">\n                        <span class=\"font-medium\">{{ row.device.split('/').pop() }}</span>\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"size\" :label=\"$t('container.size')\" min-width=\"40\" />\n                <el-table-column prop=\"used\" :label=\"$t('home.used')\" min-width=\"40\" />\n                <el-table-column prop=\"avail\" :label=\"$t('home.available')\" min-width=\"40\" />\n                <el-table-column prop=\"usePercent\" :label=\"$t('home.percent')\" min-width=\"60\">\n                    <template #default=\"{ row }\">\n                        <el-progress\n                            :percentage=\"row.usePercent\"\n                            :status=\"row.usePercent >= 90 ? 'exception' : 'success'\"\n                            :text-inside=\"true\"\n                            :stroke-width=\"14\"\n                        />\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"mountPoint\" :label=\"$t('disk.mountPoint')\" min-width=\"120\">\n                    <template #default=\"{ row }\">\n                        <span v-if=\"row.mountPoint != ''\">\n                            {{ row.mountPoint }}\n                        </span>\n                        <el-tag v-else size=\"small\" type=\"warning\">{{ $t('disk.unmounted') }}</el-tag>\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"filesystem\" :label=\"$t('disk.filesystem')\" min-width=\"80\">\n                    <template #default=\"{ row }\">\n                        <el-tag size=\"small\" type=\"info\" v-if=\"row.filesystem != ''\">{{ row.filesystem }}</el-tag>\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('commons.table.operate')\" width=\"150\">\n                    <template #default=\"{ row }\">\n                        <el-text type=\"info\" v-if=\"scope === 'system'\">{{ $t('disk.cannotOperate') }}</el-text>\n                        <el-button type=\"primary\" link v-else-if=\"row.mountPoint != ''\" @click=\"unmount(row)\">\n                            {{ $t('disk.unmount') }}\n                        </el-button>\n                        <el-button type=\"primary\" link v-else @click=\"mount(row)\">{{ $t('disk.mount') }}</el-button>\n                    </template>\n                </el-table-column>\n            </el-table>\n            <el-text v-if=\"scope === 'system'\">{{ $t('disk.systemDiskHelper') }}</el-text>\n        </div>\n        <div v-if=\"diskInfo.partitions == undefined && diskInfo.mountPoint != ''\">\n            <el-table :data=\"[diskInfo]\" class=\"w-full\">\n                <el-table-column prop=\"device\" :label=\"$t('disk.partition') + $t('commons.table.name')\" min-width=\"100\">\n                    <template #default=\"{ row }\">\n                        <span class=\"font-medium\">{{ row.device.split('/').pop() }}</span>\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"size\" :label=\"$t('container.size')\" min-width=\"40\" />\n                <el-table-column prop=\"used\" :label=\"$t('home.used')\" min-width=\"40\" />\n                <el-table-column prop=\"avail\" :label=\"$t('home.available')\" min-width=\"40\" />\n                <el-table-column prop=\"usePercent\" :label=\"$t('home.percent')\" min-width=\"60\">\n                    <template #default=\"{ row }\">\n                        <el-progress\n                            :percentage=\"row.usePercent\"\n                            :status=\"row.usePercent >= 90 ? 'exception' : 'success'\"\n                            :text-inside=\"true\"\n                            :stroke-width=\"14\"\n                        />\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"mountPoint\" :label=\"$t('disk.mountPoint')\" min-width=\"120\">\n                    <template #default=\"{ row }\">\n                        <span v-if=\"row.mountPoint != ''\">\n                            {{ row.mountPoint }}\n                        </span>\n                        <el-tag v-else size=\"small\" type=\"warning\">{{ $t('disk.unmounted') }}</el-tag>\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"filesystem\" :label=\"$t('disk.filesystem')\" min-width=\"80\">\n                    <template #default=\"{ row }\">\n                        <el-tag size=\"small\" type=\"info\" v-if=\"row.filesystem != ''\">{{ row.filesystem }}</el-tag>\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('commons.table.operate')\" width=\"150\">\n                    <template #default=\"{ row }\">\n                        <el-text type=\"info\" v-if=\"scope === 'system'\">{{ $t('disk.cannotOperate') }}</el-text>\n                        <el-button type=\"primary\" link v-else-if=\"row.mountPoint != ''\" @click=\"unmount(row)\">\n                            {{ $t('disk.unmount') }}\n                        </el-button>\n                        <el-button type=\"primary\" link v-else @click=\"mount(row)\">{{ $t('disk.mount') }}</el-button>\n                    </template>\n                </el-table-column>\n            </el-table>\n        </div>\n    </el-card>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Host } from '@/api/interface/host';\nimport i18n from '@/lang';\nimport { unmountDisk } from '@/api/modules/host';\nimport { MsgSuccess } from '@/utils/message';\n\nconst emit = defineEmits(['partition', 'search', 'mount']);\n\ndefineProps({\n    diskInfo: {\n        type: Object as () => Host.DiskInfo,\n        required: true,\n    },\n    scope: {\n        type: String,\n        required: false,\n    },\n});\n\nconst handlePartition = (diskInfo: Host.DiskInfo) => {\n    emit('partition', diskInfo);\n};\n\nconst mount = (diskInfo: Host.DiskInfo) => {\n    emit('mount', diskInfo);\n};\n\nconst unmount = (diskInfo: Host.DiskInfo) => {\n    ElMessageBox.confirm(\n        i18n.global.t('disk.unmountHelper', [diskInfo.device.split('/').pop()]),\n        i18n.global.t('disk.unmount'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    ).then(async () => {\n        unmountDisk({\n            mountPoint: diskInfo.mountPoint,\n        }).then(() => {\n            MsgSuccess(i18n.global.t('disk.unmount') + i18n.global.t('commons.status.success'));\n            emit('search');\n        });\n    });\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/disk-management/disk/index.vue",
    "content": "<template>\n    <div>\n        <DiskRouter />\n        <MainDiv class=\"mt-2\" :height-diff=\"140\" v-loading=\"loading\">\n            <div v-if=\"diskInfo?.systemDisks\">\n                <DiskCard\n                    class=\"mt-2\"\n                    v-for=\"(disk, index) in diskInfo.systemDisks\"\n                    :diskInfo=\"disk\"\n                    scope=\"system\"\n                    :key=\"index\"\n                />\n            </div>\n            <div v-if=\"diskInfo?.unpartitionedDisks\">\n                <DiskCard\n                    class=\"mt-2\"\n                    v-for=\"(disk, index) in diskInfo.unpartitionedDisks\"\n                    :key=\"index\"\n                    :diskInfo=\"disk\"\n                    scope=\"unpartitioned\"\n                    @partition=\"(diskInfo) => partitionRef.acceptParams(diskInfo, 'partition')\"\n                    @search=\"() => getDisk()\"\n                />\n            </div>\n            <div v-if=\"diskInfo?.disks\">\n                <DiskCard\n                    class=\"mt-2\"\n                    v-for=\"(disk, index) in diskInfo.disks\"\n                    :key=\"index\"\n                    :diskInfo=\"disk\"\n                    @mount=\"(diskInfo) => partitionRef.acceptParams(diskInfo, 'mount')\"\n                    scope=\"normal\"\n                    @search=\"() => getDisk()\"\n                />\n            </div>\n        </MainDiv>\n        <Partition ref=\"partitionRef\" @search=\"getDisk\" />\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { Host } from '@/api/interface/host';\nimport { listDisks } from '@/api/modules/host';\nimport DiskRouter from '@/views/host/disk-management/index.vue';\nimport DiskCard from '@/views/host/disk-management/components/disk-card.vue';\nimport Partition from '@/views/host/disk-management/partition/index.vue';\n\nconst loading = ref(false);\nconst partitionRef = ref();\nconst diskInfo = ref<Host.CompleteDiskInfo>();\n\nconst getDisk = async () => {\n    try {\n        loading.value = true;\n        const res = await listDisks();\n        diskInfo.value = res.data;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nonMounted(() => {\n    getDisk();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/disk-management/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.disk'),\n        path: '/hosts/disk',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/disk-management/partition/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('disk.' + operate)\"\n        :resource=\"form.device.split('/').pop()\"\n        @close=\"handleClose\"\n        v-loading=\"loading\"\n    >\n        <el-alert\n            v-if=\"operate == 'partition' || (operate == 'mount' && filesystem == '')\"\n            :title=\"$t('disk.partitionAlert')\"\n            type=\"warning\"\n            :closable=\"false\"\n        />\n        <el-form\n            @submit.prevent\n            ref=\"formRef\"\n            :rules=\"rules\"\n            label-position=\"top\"\n            :model=\"form\"\n            class=\"mt-2\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('disk.filesystem')\" prop=\"filesystem\">\n                <el-radio-group v-model=\"form.filesystem\" :disabled=\"operate == 'mount' && filesystem != ''\">\n                    <el-radio-button label=\"ext4\" value=\"ext4\" />\n                    <el-radio-button label=\"xfs\" value=\"xfs\" />\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('disk.mountPoint')\" prop=\"mountPoint\">\n                <el-input v-model=\"form.mountPoint\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: true })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('disk.autoMount')\" prop=\"autoMount\">\n                <el-switch v-model=\"form.autoMount\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('disk.noFail')\" prop=\"noFail\" v-if=\"form.autoMount\">\n                <el-switch v-model=\"form.noFail\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n        <FileList ref=\"fileRef\" @choose=\"loadBuildDir\" />\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Host } from '@/api/interface/host';\nimport { mountDisk, partitionDisk } from '@/api/modules/host';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst rules = ref<any>({\n    filesystem: [Rules.requiredInput],\n    autoMount: [Rules.requiredInput],\n    mountPoint: [Rules.requiredInput],\n});\nconst form = reactive({\n    device: '',\n    filesystem: 'ext4',\n    autoMount: true,\n    mountPoint: '',\n    label: '',\n    noFail: true,\n});\nconst open = ref(false);\nconst loading = ref(false);\nconst operate = ref('mount');\nconst fileRef = ref();\nconst emit = defineEmits(['search']);\nconst filesystem = ref('');\nconst formRef = ref();\n\nconst loadBuildDir = async (path: string) => {\n    form.mountPoint = path;\n};\n\nconst acceptParams = (diskInfo: Host.DiskInfo, operateType: string) => {\n    operate.value = operateType;\n    form.device = diskInfo.device;\n    form.mountPoint = '';\n    if (operateType == 'mount' && diskInfo.filesystem) {\n        filesystem.value = diskInfo.filesystem;\n        form.filesystem = diskInfo.filesystem;\n    }\n    open.value = true;\n};\n\nconst submit = async () => {\n    try {\n        const isValid = await formRef.value.validate();\n        if (!isValid) {\n            loading.value = false;\n            return;\n        }\n        loading.value = true;\n        if (operate.value == 'mount') {\n            await mountDisk(form);\n            MsgSuccess(i18n.global.t('disk.mount') + i18n.global.t('commons.status.success'));\n            handleClose();\n        } else {\n            await partitionDisk(form);\n            MsgSuccess(i18n.global.t('disk.partition') + i18n.global.t('commons.status.success'));\n            handleClose();\n        }\n    } catch (error) {\n        loading.value = false;\n        return;\n    }\n};\n\nconst handleClose = () => {\n    open.value = false;\n    loading.value = false;\n    emit('search');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/batch-role/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('file.editPermissions')\" @close=\"handleClose\" size=\"large\">\n        <div v-loading=\"loading\">\n            <FileRole :mode=\"mode\" @get-mode=\"getMode\" :key=\"open.toString()\"></FileRole>\n            <el-form ref=\"fileForm\" label-position=\"left\" :model=\"addForm\" label-width=\"100px\" :rules=\"rules\">\n                <el-form-item :label=\"$t('commons.table.user')\" prop=\"user\">\n                    <el-select v-model=\"addForm.user\" @change=\"handleUserChange\" filterable allow-create>\n                        <el-option\n                            v-for=\"item in users\"\n                            :key=\"item.username\"\n                            :label=\"item.username\"\n                            :value=\"item.username\"\n                        />\n                    </el-select>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('file.group')\" prop=\"group\">\n                    <el-select v-model=\"addForm.group\" filterable allow-create>\n                        <el-option v-for=\"group in groups\" :key=\"group\" :label=\"group\" :value=\"group\" />\n                    </el-select>\n                </el-form-item>\n                <el-form-item>\n                    <el-checkbox v-model=\"addForm.sub\">{{ $t('file.containSub') }}</el-checkbox>\n                </el-form-item>\n            </el-form>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport { batchChangeRole, searchUserGroup } from '@/api/modules/files';\nimport i18n from '@/lang';\nimport FileRole from '@/components/file-role/index.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormRules } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\n\ninterface BatchRoleProps {\n    files: File.File[];\n}\n\nconst open = ref(false);\nconst loading = ref(false);\nconst mode = ref('0755');\nconst files = ref<File.File[]>([]);\nconst users = ref<File.UserInfo[]>([]);\nconst groups = ref<string[]>([]);\n\nconst rules = reactive<FormRules>({\n    user: [Rules.requiredInput],\n    group: [Rules.requiredInput],\n});\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst addForm = reactive({\n    paths: [],\n    mode: 755,\n    user: '',\n    group: '',\n    sub: false,\n});\n\nconst acceptParams = (props: BatchRoleProps) => {\n    addForm.paths = [];\n    files.value = props.files;\n    files.value.forEach((file) => {\n        addForm.paths.push(file.path);\n    });\n    addForm.mode = Number.parseInt(String(props.files[0].mode), 8);\n    addForm.group = props.files[0].group || props.files[0].gid + '';\n    addForm.user = props.files[0].user || props.files[0].uid + '';\n    addForm.sub = true;\n\n    mode.value = String(props.files[0].mode);\n    open.value = true;\n};\n\nconst getUserAndGroup = async () => {\n    try {\n        const res = await searchUserGroup();\n        users.value = res.data.users;\n        groups.value = res.data.groups;\n    } catch (error) {\n        console.error('Failed to fetch user and group:', error);\n    }\n};\n\nconst handleUserChange = (val: string) => {\n    const found = users.value.find((u) => u.username === val);\n    if (found) {\n        addForm.group = found.group;\n    }\n};\n\nconst getMode = (val: number) => {\n    addForm.mode = val;\n};\n\nconst submit = async () => {\n    const regFilePermission = /^[0-7]{3,4}$/;\n    if (!regFilePermission.test(addForm.mode.toString(8))) {\n        return;\n    }\n    loading.value = true;\n\n    batchChangeRole(addForm)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            handleClose();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\nonMounted(() => {\n    getUserAndGroup();\n});\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/change-role/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('file.editPermissions')\" @close=\"handleClose\" :resource=\"name\" size=\"large\">\n        <FileRole v-loading=\"loading\" :mode=\"mode\" @get-mode=\"getMode\" :key=\"open.toString()\"></FileRole>\n        <el-form-item v-if=\"form.isDir\">\n            <el-checkbox v-model=\"form.sub\">{{ $t('file.containSub') }}</el-checkbox>\n        </el-form-item>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport { changeFileMode } from '@/api/modules/files';\nimport i18n from '@/lang';\nimport FileRole from '@/components/file-role/index.vue';\nimport { MsgSuccess } from '@/utils/message';\n\nconst open = ref(false);\nconst form = ref<File.FileCreate>({ path: '', isDir: false, mode: 0o755 });\nconst loading = ref(false);\nconst mode = ref('0755');\nconst name = ref('');\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = (create: File.FileCreate) => {\n    open.value = true;\n    form.value.isDir = create.isDir;\n    form.value.path = create.path;\n    form.value.isLink = false;\n    form.value.sub = false;\n    name.value = create.name;\n\n    mode.value = String(create.mode);\n};\n\nconst getMode = (val: number) => {\n    form.value.mode = val;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    changeFileMode(form.value)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            handleClose();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/chown/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('file.setRole')\" @close=\"handleClose\" :resource=\"name\" size=\"normal\">\n        <el-alert :title=\"$t('file.ownerHelper')\" type=\"info\" :closable=\"false\" class=\"common-prompt\" />\n        <el-form\n            ref=\"fileForm\"\n            label-position=\"top\"\n            :model=\"addForm\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('commons.table.user')\" prop=\"user\">\n                <el-input v-model.trim=\"addForm.user\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('file.group')\" prop=\"group\">\n                <el-input v-model.trim=\"addForm.group\" />\n            </el-form-item>\n            <el-form-item v-if=\"isDir\">\n                <el-checkbox v-model=\"addForm.sub\">{{ $t('file.containSub') }}</el-checkbox>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(fileForm)\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { changeOwner } from '@/api/modules/files';\nimport { Rules } from '@/global/form-rules';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\ninterface OwnerProps {\n    path: string;\n    user: string;\n    group: string;\n    isDir: boolean;\n    name: string;\n}\n\nconst fileForm = ref<FormInstance>();\nconst loading = ref(false);\nconst open = ref(false);\nconst isDir = ref(false);\nconst name = ref('');\n\nconst addForm = reactive({\n    path: '',\n    user: '',\n    group: '',\n    sub: false,\n});\n\nconst rules = reactive<FormRules>({\n    user: [Rules.requiredInput],\n    group: [Rules.requiredInput],\n});\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    if (fileForm.value) {\n        fileForm.value.resetFields();\n    }\n    em('close', false);\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        changeOwner(addForm)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst acceptParams = (props: OwnerProps) => {\n    addForm.user = props.user;\n    addForm.path = props.path;\n    addForm.group = props.group;\n    isDir.value = props.isDir;\n    name.value = props.name;\n    open.value = true;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/code-editor/index.vue",
    "content": "<template>\n    <DialogPro\n        v-model=\"open\"\n        size=\"w-70\"\n        class=\"code-dialog !p-0\"\n        @opened=\"onOpen\"\n        :show-close=\"false\"\n        :fullscreen=\"isFullscreen\"\n    >\n        <template #header>\n            <div ref=\"dialogHeader\" class=\"flex items-center justify-between code-header px-4 rounded-t\">\n                <span class=\"truncate-text\">{{ $t('home.dir') + ' - ' + form.path }}</span>\n                <el-space alignment=\"center\" :size=\"1\" class=\"dialog-header-icon\">\n                    <el-tooltip :content=\"loadTooltip()\" placement=\"top\">\n                        <el-button\n                            @click=\"toggleFullscreen\"\n                            v-if=\"!mobile\"\n                            class=\"!border-none !bg-transparent !text-base !font-semibold !py-2 !px-1 mr-2.5\"\n                            icon=\"FullScreen\"\n                        ></el-button>\n                    </el-tooltip>\n                    <el-button\n                        @click=\"handleClose\"\n                        class=\"!border-none !bg-transparent !text-xl !py-2 !px-1\"\n                        icon=\"Close\"\n                    ></el-button>\n                </el-space>\n            </div>\n        </template>\n        <template #content>\n            <div ref=\"dialogForm\" class=\"px-4 py-2 code-action\">\n                <div class=\"flex justify-start items-center gap-x-4 card-action\">\n                    <el-text class=\"cursor-pointer\" @click=\"handleReset\">{{ $t('commons.button.reset') }}</el-text>\n                    <el-divider direction=\"vertical\" class=\"!mx-0\" />\n                    <el-text class=\"cursor-pointer ml-0\" @click=\"saveContent()\">\n                        {{ $t('commons.button.save') }}\n                    </el-text>\n                    <el-divider direction=\"vertical\" class=\"!mx-0\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"bottom-start\" @command=\"changeTheme\">\n                        <span class=\"el-dropdown-link cursor-pointer\">{{ $t('file.theme') }}</span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item v-for=\"item in themes\" :key=\"item.label\" :command=\"item.value\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ item.label }}\n                                        <el-icon v-if=\"config.theme == item.value\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                    <el-divider direction=\"vertical\" class=\"!mx-0\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"bottom-start\" @command=\"changeLanguage\">\n                        <span class=\"el-dropdown-link cursor-pointer\">{{ $t('file.language') }}</span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item v-for=\"item in Languages\" :key=\"item.label\" :command=\"item.label\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ item.label }}\n                                        <el-icon v-if=\"config.language == item.label\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                    <el-divider direction=\"vertical\" class=\"!mx-0\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"bottom-start\" @command=\"changeEOL\">\n                        <span class=\"el-dropdown-link cursor-pointer\">{{ $t('file.eol') }}</span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item v-for=\"item in eols\" :key=\"item.label\" :command=\"item.value\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ item.label }}\n                                        <el-icon v-if=\"config.eol == item.value\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                    <el-divider direction=\"vertical\" class=\"!mx-0\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"bottom-start\">\n                        <span class=\"el-dropdown-link cursor-pointer\">{{ $t('file.setting') }}</span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item @click=\"changeMinimap(!config.minimap)\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ $t('file.minimap') }}\n                                        <el-icon v-if=\"config.minimap\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                                <el-dropdown-item @click=\"changeWarp(config.wordWrap)\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ $t('file.wordWrap') }}\n                                        <el-icon v-if=\"config.wordWrap == 'on'\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                </div>\n            </div>\n            <div v-loading=\"loading\">\n                <div class=\"flex\">\n                    <div\n                        class=\"monaco-editor sm:w-48 w-1/3 monaco-editor-background border-0 tree-container\"\n                        v-if=\"isShow\"\n                    >\n                        <div class=\"flex items-center justify-between pl-1 pr-1 py-0.5 h-6\">\n                            <el-text size=\"small\" @click=\"getUpData()\" class=\"cursor-pointer\">\n                                <el-icon>\n                                    <Top />\n                                </el-icon>\n                                <span class=\"sm:inline hidden pl-1\">{{ $t('file.up') }}</span>\n                            </el-text>\n                            <el-divider direction=\"vertical\" class=\"!mx-0\" />\n                            <el-text size=\"small\" @click=\"getRefresh(directoryPath)\" class=\"cursor-pointer\">\n                                <el-icon>\n                                    <Refresh />\n                                </el-icon>\n                                <span class=\"sm:inline hidden pl-1\">{{ $t('commons.button.refresh') }}</span>\n                            </el-text>\n                            <el-divider direction=\"vertical\" v-if=\"!mobile\" class=\"!mx-0\" />\n                            <el-dropdown @command=\"handleCreate\" v-if=\"!mobile\" trigger=\"click\">\n                                <el-text size=\"small\">\n                                    {{ $t('commons.button.create') }}\n                                    <el-icon><arrow-down /></el-icon>\n                                </el-text>\n                                <template #dropdown>\n                                    <el-dropdown-menu>\n                                        <el-dropdown-item command=\"dir\" class=\"!px-2\">\n                                            <svg-icon class=\"!w-5 !h-5\" iconName=\"p-file-folder\"></svg-icon>\n                                            {{ $t('file.dir') }}\n                                        </el-dropdown-item>\n                                        <el-dropdown-item command=\"file\" class=\"!px-2\">\n                                            <svg-icon class=\"!w-5 !h-5\" iconName=\"p-file-normal\"></svg-icon>\n                                            {{ $t('menu.files') }}\n                                        </el-dropdown-item>\n                                    </el-dropdown-menu>\n                                </template>\n                            </el-dropdown>\n                        </div>\n                        <el-divider class=\"!my-0\" />\n                        <el-tree-v2\n                            ref=\"treeRef\"\n                            :data=\"treeData\"\n                            :props=\"treeProps\"\n                            @node-expand=\"handleNodeExpand\"\n                            @node-collapse=\"handleNodeCollapse\"\n                            class=\"monaco-editor-tree monaco-editor-background pt-2\"\n                            :height=\"treeHeight\"\n                            :indent=\"6\"\n                            :item-size=\"26\"\n                            highlight-current\n                        >\n                            <template #default=\"{ node, data }\">\n                                <span v-if=\"data.isDir\" style=\"align-items: center\">\n                                    <template v-if=\"isCreate == 'dir' && data.id == 'new-dir'\">\n                                        <div class=\"flex justify-between items-center gap-0.5 pr-2\">\n                                            <svg-icon class=\"table-icon\" iconName=\"p-file-folder\"></svg-icon>\n                                            <el-input\n                                                size=\"small\"\n                                                class=\"!flex-1 !min-w-16\"\n                                                ref=\"rowRefs\"\n                                                v-model=\"newFolder\"\n                                            ></el-input>\n                                            <el-icon\n                                                class=\"cursor-pointer w-4 pl-1\"\n                                                size=\"small\"\n                                                @click.stop=\"createFolder(true)\"\n                                            >\n                                                <Check />\n                                            </el-icon>\n                                            <el-icon\n                                                class=\"cursor-pointer w-4\"\n                                                size=\"small\"\n                                                @click.stop=\"cancelFolder()\"\n                                            >\n                                                <Close />\n                                            </el-icon>\n                                        </div>\n                                    </template>\n                                    <template v-else>\n                                        <svg-icon class=\"table-icon\" iconName=\"p-file-folder\"></svg-icon>\n                                        <small :title=\"node.label\" class=\"min-w-32\">{{ node.label }}</small>\n                                    </template>\n                                </span>\n                                <span\n                                    v-else\n                                    style=\"display: inline-flex; align-items: center\"\n                                    @click=\"getContent(data.path, data.extension)\"\n                                >\n                                    <template v-if=\"isCreate == 'file' && data.id == 'new-file'\">\n                                        <div class=\"flex justify-between items-center gap-0.5 pr-2\">\n                                            <svg-icon\n                                                class=\"table-icon w-4\"\n                                                :iconName=\"getIconName(data.extension)\"\n                                            ></svg-icon>\n                                            <el-input\n                                                size=\"small\"\n                                                ref=\"rowRefs\"\n                                                class=\"!flex-1 !min-w-16\"\n                                                v-model=\"newFolder\"\n                                            ></el-input>\n                                            <el-icon\n                                                class=\"cursor-pointer w-4 pl-1\"\n                                                size=\"small\"\n                                                @click.stop=\"createFolder(false)\"\n                                            >\n                                                <Check />\n                                            </el-icon>\n                                            <el-icon\n                                                class=\"cursor-pointer w-4\"\n                                                size=\"small\"\n                                                @click.stop=\"cancelFolder()\"\n                                            >\n                                                <Close />\n                                            </el-icon>\n                                        </div>\n                                    </template>\n                                    <template v-else>\n                                        <svg-icon class=\"table-icon\" :iconName=\"getIconName(data.extension)\"></svg-icon>\n                                        <small :title=\"node.label\" class=\"min-w-32\">{{ node.label }}</small>\n                                    </template>\n                                </span>\n                            </template>\n                        </el-tree-v2>\n                    </div>\n                    <div class=\"relative\">\n                        <el-divider\n                            v-if=\"isShow\"\n                            direction=\"vertical\"\n                            style=\"height: 100%; width: 0\"\n                            class=\"!m-0 p-0\"\n                            :class=\"isShow ? 'opacity-100' : 'opacity-0'\"\n                        ></el-divider>\n                    </div>\n                    <div class=\"flex-1 sm:w-4/5 w-2/3 relative\">\n                        <CodeTabs\n                            class=\"monaco-editor monaco-editor-background\"\n                            :select-tab=\"selectTab\"\n                            :file-tabs=\"fileTabs\"\n                            :on-remove-tab=\"removeTab\"\n                            :on-change-tab=\"changeTab\"\n                            :on-remove-all-tab=\"removeAllTab\"\n                            :on-remove-other-tab=\"removeOtherTab\"\n                        ></CodeTabs>\n                        <div ref=\"codeBox\" class=\"relative\" :style=\"{ height: codeHeight }\">\n                            <el-icon\n                                v-if=\"isShow\"\n                                class=\"cursor-pointer absolute bg-gray-100 py-2 rounded-l-sm block top-1/3 -left-[9px]\"\n                                size=\"9\"\n                                @click=\"toggleShow\"\n                            >\n                                <DArrowLeft />\n                            </el-icon>\n                            <el-icon\n                                v-else\n                                class=\"cursor-pointer absolute bg-gray-100 py-2 rounded-r-sm block top-1/3 z-50\"\n                                size=\"9\"\n                                @click=\"toggleShow\"\n                            >\n                                <DArrowRight />\n                            </el-icon>\n                            <div class=\"flex justify-center items-center h-full\" v-if=\"fileTabs.length === 0\">\n                                <el-empty :image=\"noUpdateImage\" />\n                            </div>\n                        </div>\n                    </div>\n                </div>\n                <div\n                    class=\"hidden code-footer pl-4 h-6 sm:flex justify-end items-center gap-4 rounded-b\"\n                    ref=\"dialogFooter\"\n                >\n                    <el-divider direction=\"vertical\" class=\"!h-6\" v-if=\"config.theme\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"top\" @command=\"changeTheme\">\n                        <span class=\"el-dropdown-link\">\n                            {{ themes.find((item) => item.value === config.theme)?.label || $t('file.theme') }}\n                        </span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item v-for=\"item in themes\" :key=\"item.label\" :command=\"item.value\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ item.label }}\n                                        <el-icon v-if=\"config.theme == item.value\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                    <el-divider direction=\"vertical\" class=\"!h-6\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"top\" @command=\"changeEOL\">\n                        <span class=\"el-dropdown-link\">\n                            {{ eols.find((item) => item.value === config.eol)?.label || $t('file.eol') }}\n                        </span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item v-for=\"item in eols\" :key=\"item.label\" :command=\"item.value\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ item.label }}\n                                        <el-icon v-if=\"config.eol == item.value\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                    <el-divider direction=\"vertical\" class=\"!h-6\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"top\" @command=\"changeLanguage\">\n                        <span class=\"el-dropdown-link\">\n                            {{\n                                config.language\n                                    ? `${$t('file.language')}: ${\n                                          Languages.find((item) => item.label === config.language)?.label ||\n                                          config.language\n                                      }`\n                                    : $t('file.language')\n                            }}\n                        </span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item v-for=\"item in Languages\" :key=\"item.label\" :command=\"item.label\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ item.label }}\n                                        <el-icon v-if=\"config.language == item.label\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                    <el-divider direction=\"vertical\" class=\"!h-6\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"top\">\n                        <span class=\"el-dropdown-link\">\n                            {{\n                                $t('file.wordWrap') +\n                                ': ' +\n                                $t(config.wordWrap === 'on' ? 'commons.button.enable' : 'commons.button.disable')\n                            }}\n                        </span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item @click=\"changeWarp('off')\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ $t('commons.button.enable') }}\n                                        <el-icon v-if=\"config.wordWrap == 'on'\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                                <el-dropdown-item @click=\"changeWarp('on')\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ $t('commons.button.disable') }}\n                                        <el-icon v-if=\"config.wordWrap == 'off'\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                    <el-divider direction=\"vertical\" class=\"!h-6\" />\n                    <el-dropdown trigger=\"click\" max-height=\"300\" placement=\"top\">\n                        <span class=\"el-dropdown-link\">\n                            {{\n                                $t('file.minimap') +\n                                ': ' +\n                                $t(config.minimap ? 'commons.button.enable' : 'commons.button.disable')\n                            }}\n                        </span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item @click=\"changeMinimap(true)\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ $t('commons.button.enable') }}\n                                        <el-icon v-if=\"config.minimap\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                                <el-dropdown-item @click=\"changeMinimap(false)\">\n                                    <div class=\"flex items-center justify-between gap-4 w-full\">\n                                        {{ $t('commons.button.disable') }}\n                                        <el-icon v-if=\"!config.minimap\"><Check /></el-icon>\n                                    </div>\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                    <el-divider direction=\"vertical\" class=\"!h-6 !mr-3.5\" />\n                </div>\n            </div>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { createFile, getFileContent, getFilesTree, saveFileContent } from '@/api/modules/files';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess, MsgWarning } from '@/utils/message';\nimport * as monaco from 'monaco-editor';\nimport { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue';\nimport { Languages } from '@/global/mimetype';\nimport jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';\nimport cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';\nimport htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';\nimport tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';\nimport EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';\n\nimport type { TabPaneName } from 'element-plus';\nimport { ElMessageBox, ElTreeV2 } from 'element-plus';\nimport { ResultData } from '@/api/interface';\nimport { File } from '@/api/interface/file';\nimport { getIcon, newUUID } from '@/utils/util';\nimport { TreeKey, TreeNodeData } from 'element-plus/es/components/tree-v2/src/types';\nimport { DArrowLeft, DArrowRight, Refresh, Top } from '@element-plus/icons-vue';\nimport { loadBaseDir } from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nimport CodeTabs from './tabs/index.vue';\nimport noUpdateImage from '@/assets/images/no_update_app.svg';\n\nlet editor: monaco.editor.IStandaloneCodeEditor | undefined;\n\nself.MonacoEnvironment = {\n    getWorker(workerId, label) {\n        if (label === 'json') {\n            return new jsonWorker();\n        }\n        if (label === 'css' || label === 'scss' || label === 'less') {\n            return new cssWorker();\n        }\n        if (label === 'html' || label === 'handlebars' || label === 'razor') {\n            return new htmlWorker();\n        }\n        if (['typescript', 'javascript'].includes(label)) {\n            return new tsWorker();\n        }\n        return new EditorWorker();\n    },\n};\n\ninterface EditProps {\n    language: string;\n    content: string;\n    path: string;\n    name: string;\n    extension: string;\n}\n\ninterface EditorConfig {\n    theme: string;\n    language: string;\n    eol: number;\n    wordWrap: WordWrapOptions;\n    minimap: boolean;\n}\n\ninterface TreeNode {\n    key: TreeKey;\n    level: number;\n    parent?: TreeNode;\n    children?: File.FileTree[];\n    data: TreeNodeData;\n    disabled?: boolean;\n    name?: string;\n    isLeaf?: boolean;\n}\n\nconst open = ref(false);\nconst loading = ref(false);\nconst fileName = ref('');\nconst codeThemeKey = 'code-theme';\nconst warpKey = 'code-warp';\nconst minimapKey = 'code-minimap';\nconst directoryPath = ref('');\nconst fileExtension = ref('');\nconst baseDir = ref();\nconst treeData = ref([]);\nconst codeBox = ref();\nconst defaultHeight = ref(56);\nconst treeHeight = ref(0);\nconst codeHeight = ref('56vh');\nconst codeReq = reactive({ path: '', expand: false, page: 1, pageSize: 100 });\nconst isShow = ref(true);\nconst isEdit = ref(false);\nconst oldFileContent = ref('');\nconst dialogHeader = ref(null);\nconst dialogForm = ref(null);\nconst dialogFooter = ref(null);\nconst currentPath = ref();\nconst rowRefs = ref();\nconst isCreate = ref('none');\nconst newFolder = ref();\nconst selectedParentNode = ref(null);\nconst expandedNodeIds = ref(new Set());\n\nconst toggleShow = () => {\n    isShow.value = !isShow.value;\n};\n\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\ntype WordWrapOptions = 'off' | 'on' | 'wordWrapColumn' | 'bounded';\n\nconst isFullscreen = ref(false);\n\nconst config = reactive<EditorConfig>({\n    theme: 'vs-dark',\n    language: 'plaintext',\n    eol: monaco.editor.EndOfLineSequence.LF,\n    wordWrap: 'on',\n    minimap: false,\n});\n\nmonaco.editor.defineTheme('vs', {\n    base: 'vs',\n    inherit: true,\n    rules: [{ token: '' }],\n    colors: {\n        'editor.background': '#f8f6f6',\n        'minimap.background': '#f4f4f4',\n        'scrollbar.shadow': '#e1e1e1',\n        'scrollbarSlider.background': '#e1e1e1',\n        'scrollbarSlider.hoverBackground': '#cccccc',\n        'scrollbarSlider.activeBackground': '#bfbfbf',\n    },\n});\n\nconst selectTab = ref();\nconst fileTabs = ref([]);\nconst maxTabs = 10;\nconst codeTabsStorageKey = 'code-editor-tabs';\n\nconst saveTabsToStorage = () => {\n    const simpleTabs = fileTabs.value.map((tab: any) => ({\n        path: tab.path,\n        name: tab.name,\n    }));\n    localStorage.setItem(codeTabsStorageKey, JSON.stringify(simpleTabs.slice(0, maxTabs)));\n};\n\nconst loadTabsFromStorage = (): { path: string; name: string }[] => {\n    const raw = localStorage.getItem(codeTabsStorageKey);\n    if (!raw) return [];\n    const parsed = JSON.parse(raw);\n    if (Array.isArray(parsed)) {\n        return parsed.filter((item) => item && item.path && item.name);\n    }\n    return [];\n};\nconst removeTab = (targetPath: TabPaneName) => {\n    const tabs = fileTabs.value;\n    let activeName = selectTab.value;\n\n    const updateTabs = () => {\n        if (activeName === targetPath) {\n            const index = tabs.findIndex((tab) => tab.path === targetPath);\n            const nextTab = tabs[index + 1] || tabs[index - 1];\n            if (nextTab) {\n                activeName = nextTab.path;\n            }\n        }\n        selectTab.value = activeName;\n        fileTabs.value = tabs.filter((tab) => tab.path !== targetPath);\n    };\n\n    if (isEdit.value) {\n        ElMessageBox.confirm(i18n.global.t('file.saveContentAndClose'), {\n            confirmButtonText: i18n.global.t('commons.button.save'),\n            cancelButtonText: i18n.global.t('commons.button.notSave'),\n            type: 'info',\n            distinguishCancelAndClose: true,\n        })\n            .then(() => {\n                updateTabs();\n                saveContent();\n                getContent(selectTab.value, '');\n            })\n            .catch(() => {\n                updateTabs();\n                isEdit.value = false;\n                if (fileTabs.value.length > 0) {\n                    getContent(selectTab.value, '');\n                }\n            });\n    } else {\n        updateTabs();\n        getContent(selectTab.value, '');\n    }\n};\n\nconst removeAllTab = (targetPath: string, type: 'left' | 'right' | 'all') => {\n    const tabs = fileTabs.value;\n    const targetIndex = tabs.findIndex((tab) => tab.path === targetPath);\n    let activeName = selectTab.value;\n\n    const filterTabs = (): typeof fileTabs.value => {\n        if (type === 'left') return tabs.slice(targetIndex);\n        if (type === 'right') return tabs.slice(0, targetIndex + 1);\n        return [];\n    };\n\n    const updateTabs = () => {\n        if (activeName !== targetPath && type !== 'all') {\n            activeName = tabs[targetIndex]?.path || '';\n        }\n        const newTabs = type === 'all' ? [] : filterTabs();\n        fileTabs.value = newTabs;\n        selectTab.value = activeName;\n\n        if (type === 'all') {\n            selectTab.value = '';\n            editor.dispose();\n        } else if (newTabs.length > 0) {\n            getContent(activeName, '');\n        }\n    };\n\n    const onConfirm = () => {\n        updateTabs();\n        saveContent();\n    };\n\n    const onCancel = () => {\n        if (type === 'left' || type === 'right') {\n            editor.setValue(oldFileContent.value);\n        }\n        isEdit.value = false;\n        isCreate.value = 'none';\n        updateTabs();\n    };\n\n    if (isEdit.value) {\n        ElMessageBox.confirm(i18n.global.t('file.saveContentAndClose'), {\n            confirmButtonText: i18n.global.t('commons.button.save'),\n            cancelButtonText: i18n.global.t('commons.button.notSave'),\n            type: 'info',\n            distinguishCancelAndClose: true,\n        })\n            .then(onConfirm)\n            .catch(onCancel);\n    } else {\n        updateTabs();\n        if (type === 'all') editor.dispose();\n        else getContent(activeName, '');\n    }\n};\n\nconst removeOtherTab = (targetPath: string) => {\n    const tabs = fileTabs.value;\n    const targetTab = tabs.find((tab) => tab.path === targetPath);\n    if (!targetTab) return;\n\n    const updateTabs = () => {\n        fileTabs.value = [targetTab];\n        selectTab.value = targetTab.path;\n        getContent(targetTab.path, '');\n    };\n\n    const onConfirm = () => {\n        updateTabs();\n        saveContent();\n    };\n\n    const onCancel = () => {\n        editor.setValue(oldFileContent.value);\n        isEdit.value = false;\n        updateTabs();\n    };\n\n    if (isEdit.value) {\n        ElMessageBox.confirm(i18n.global.t('file.saveContentAndClose'), {\n            confirmButtonText: i18n.global.t('commons.button.save'),\n            cancelButtonText: i18n.global.t('commons.button.notSave'),\n            type: 'info',\n            distinguishCancelAndClose: true,\n        })\n            .then(onConfirm)\n            .catch(onCancel);\n    } else {\n        updateTabs();\n    }\n};\n\nconst changeTab = (targetPath: TabPaneName) => {\n    selectTab.value = targetPath.toString();\n    getContent(targetPath.toString(), '');\n};\n\nconst eols = [\n    {\n        label: 'LF (Linux)',\n        value: monaco.editor.EndOfLineSequence.LF,\n    },\n    {\n        label: 'CRLF (Windows)',\n        value: monaco.editor.EndOfLineSequence.CRLF,\n    },\n];\n\nconst themes = [\n    {\n        label: 'Visual Studio',\n        value: 'vs',\n    },\n    {\n        label: 'Visual Studio Dark',\n        value: 'vs-dark',\n    },\n    {\n        label: 'High Contrast Dark',\n        value: 'hc-black',\n    },\n];\n\nlet form = ref({\n    content: '',\n    path: '',\n});\n\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    const closeEditor = () => {\n        open.value = false;\n        selectTab.value = '';\n        fileTabs.value = [];\n        isEdit.value = false;\n        if (editor) {\n            editor.dispose();\n        }\n        em('close', open.value);\n    };\n\n    if (isEdit.value) {\n        ElMessageBox.confirm(i18n.global.t('file.saveContentAndClose'), {\n            confirmButtonText: i18n.global.t('commons.button.save'),\n            cancelButtonText: i18n.global.t('commons.button.notSave'),\n            type: 'info',\n            distinguishCancelAndClose: true,\n        })\n            .then(() => {\n                saveContent();\n            })\n            .finally(() => {\n                closeEditor();\n            });\n    } else {\n        closeEditor();\n    }\n};\n\nconst handleReset = () => {\n    if (isEdit.value) {\n        loading.value = true;\n        form.value.content = oldFileContent.value;\n        editor.setValue(oldFileContent.value);\n        isEdit.value = false;\n        MsgSuccess(i18n.global.t('commons.msg.resetSuccess'));\n        loading.value = false;\n    } else {\n        MsgWarning(i18n.global.t('file.noEdit'));\n    }\n};\n\nconst loadTooltip = () => {\n    return i18n.global.t('commons.button.' + (isFullscreen.value ? 'quitFullscreen' : 'fullscreen'));\n};\n\nonMounted(() => {\n    isCreate.value = 'none';\n    loadPath();\n    updateHeights();\n    window.addEventListener('resize', updateHeights);\n});\n\nconst updateHeights = () => {\n    const vh = window.innerHeight / 100;\n    if (isFullscreen.value) {\n        let paddingHeight = 30;\n        const headerHeight = dialogHeader.value.offsetHeight;\n        const formHeight = dialogForm.value.offsetHeight;\n        const footerHeight = dialogFooter.value.offsetHeight;\n        treeHeight.value = window.innerHeight - headerHeight - formHeight - footerHeight - paddingHeight - 31;\n        codeHeight.value = `${\n            ((window.innerHeight - headerHeight - formHeight - footerHeight - paddingHeight) / window.innerHeight) * 100\n        }vh`;\n    } else {\n        treeHeight.value = defaultHeight.value * vh - 31;\n        codeHeight.value = `${defaultHeight.value}vh`;\n    }\n};\n\nconst toggleFullscreen = () => {\n    isFullscreen.value = !isFullscreen.value;\n    updateHeights();\n};\n\nconst changeLanguage = (command: string) => {\n    config.language = command;\n    monaco.editor.setModelLanguage(editor.getModel(), config.language);\n};\n\nconst changeTheme = (command: string) => {\n    config.theme = command;\n    monaco.editor.setTheme(config.theme);\n    const themes = {\n        vs: 'monaco-editor-tree-light',\n        'vs-dark': 'monaco-editor-tree-dark',\n        'hc-black': 'monaco-editor-tree-dark',\n    };\n\n    if (treeRef.value) {\n        Object.values(themes).forEach((themeClass) => {\n            treeRef.value.$el.classList.remove(themeClass);\n        });\n        if (themes[config.theme]) {\n            treeRef.value.$el.classList.add(themes[config.theme]);\n        }\n    }\n\n    localStorage.setItem(codeThemeKey, config.theme);\n};\n\nconst changeEOL = (command: number) => {\n    config.eol = command;\n    editor.getModel().pushEOL(config.eol);\n};\n\nconst changeWarp = (command: string) => {\n    config.wordWrap = command === 'on' ? 'off' : 'on';\n    localStorage.setItem(warpKey, config.wordWrap);\n    editor.updateOptions({\n        wordWrap: config.wordWrap,\n    });\n};\n\nconst changeMinimap = (command: boolean) => {\n    config.minimap = command;\n    localStorage.setItem(minimapKey, JSON.stringify(config.minimap));\n    editor.updateOptions({\n        minimap: {\n            enabled: config.minimap,\n        },\n    });\n};\n\nconst initEditor = () => {\n    if (editor) {\n        editor.dispose();\n    }\n    nextTick(() => {\n        editor = monaco.editor.create(codeBox.value as HTMLElement, {\n            theme: config.theme,\n            value: form.value.content,\n            readOnly: false,\n            automaticLayout: true,\n            language: config.language,\n            folding: true,\n            roundedSelection: false,\n            overviewRulerBorder: false,\n            wordWrap: config.wordWrap,\n            minimap: {\n                enabled: config.minimap,\n            },\n            lineNumbersMinChars: 6,\n        });\n        if (editor.getModel().getValue() === '') {\n            let defaultContent = '';\n            editor.getModel().setValue(defaultContent);\n        }\n\n        editor.getModel().pushEOL(config.eol);\n\n        editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, quickSave);\n\n        editor.onDidChangeModelContent(() => {\n            if (editor) {\n                form.value.content = editor.getValue();\n                isEdit.value = true;\n            }\n        });\n    });\n};\n\nconst quickSave = () => {\n    saveContent();\n};\n\nconst saveContent = async () => {\n    if (isEdit.value) {\n        loading.value = true;\n        try {\n            const res = await saveFileContent(form.value);\n            if (res) {\n                isEdit.value = false;\n                oldFileContent.value = form.value.content;\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            } else {\n                MsgError(i18n.global.t('commons.status.failed'));\n                isEdit.value = false;\n            }\n        } finally {\n            loading.value = false;\n        }\n    } else {\n        MsgWarning(i18n.global.t('file.noEdit'));\n    }\n};\n\nconst acceptParams = (props: EditProps) => {\n    form.value.content = props.content;\n    oldFileContent.value = props.content;\n    form.value.path = props.path;\n    currentPath.value = getDirectoryPath(props.path);\n    directoryPath.value = getDirectoryPath(props.path);\n    fileExtension.value = props.extension;\n    fileName.value = props.name;\n    const savedTabs = loadTabsFromStorage();\n    const withoutCurrent = savedTabs.filter((tab) => tab.path !== props.path);\n    const merged = [...withoutCurrent, { path: props.path, name: props.name }];\n    fileTabs.value = merged.slice(-maxTabs);\n    selectTab.value = props.path;\n\n    config.language = props.language;\n    config.eol = monaco.editor.EndOfLineSequence.LF;\n    config.theme = localStorage.getItem(codeThemeKey) || 'vs-dark';\n    config.wordWrap = (localStorage.getItem(warpKey) as WordWrapOptions) || 'on';\n    config.minimap = localStorage.getItem(minimapKey) !== null ? localStorage.getItem(minimapKey) === 'true' : true;\n    open.value = true;\n    saveTabsToStorage();\n};\n\nconst getIconName = (extension: string) => getIcon(extension);\n\nconst loadPath = async () => {\n    const pathRes = await loadBaseDir();\n    baseDir.value = pathRes.data;\n};\n\nconst getDirectoryPath = (filePath: string) => {\n    if (!filePath) {\n        return baseDir.value;\n    }\n\n    const lastSlashIndex = filePath.lastIndexOf('/');\n\n    if (lastSlashIndex === -1) {\n        return baseDir.value;\n    }\n\n    const directoryPath = filePath.substring(0, lastSlashIndex);\n    if (directoryPath === '' || directoryPath === '.' || directoryPath === '/') {\n        return baseDir.value;\n    }\n    return directoryPath;\n};\n\nconst onOpen = () => {\n    initEditor();\n    changeTheme(config.theme);\n    search(directoryPath.value).then((res) => {\n        handleSearchResult(res);\n    });\n};\n\nconst handleSearchResult = (res: ResultData<File.FileTree[]>) => {\n    if (res.data.length > 0 && res.data[0].children) {\n        treeData.value = res.data[0].children.map((item) => ({\n            ...item,\n            children: item.isDir ? item.children || [] : undefined,\n        }));\n    } else {\n        treeData.value = [];\n    }\n};\n\nconst getRefresh = (path: string) => {\n    loading.value = true;\n    try {\n        search(path).then((res) => {\n            treeData.value = res.data[0].children;\n            loadedNodes.value = new Set();\n            isCreate.value = 'none';\n            currentPath.value = path;\n            selectedParentNode.value = null;\n        });\n    } finally {\n        loading.value = false;\n        MsgSuccess(i18n.global.t('commons.msg.refreshSuccess'));\n    }\n};\n\nconst getContent = (path: string, extension: string) => {\n    if (form.value.path === path || isCreate.value == 'file') {\n        return;\n    }\n    const existsInTabs = fileTabs.value.some((tab) => tab.path === path);\n    if (!existsInTabs && fileTabs.value.length >= maxTabs) {\n        fileTabs.value.shift();\n    }\n    const fetchFileContent = () => {\n        codeReq.path = path;\n        codeReq.expand = true;\n\n        if (extension !== '') {\n            Languages.forEach((language) => {\n                const ext = extension.substring(1);\n                if (language.value.indexOf(ext) > -1) {\n                    config.language = language.label;\n                }\n            });\n        }\n\n        getFileContent(codeReq)\n            .then((res) => {\n                form.value.content = res.data.content;\n                oldFileContent.value = res.data.content;\n                form.value.path = res.data.path;\n                fileExtension.value = res.data.extension;\n                fileName.value = res.data.name;\n                initEditor();\n                if (extension == '') {\n                    Languages.forEach((language) => {\n                        const ext = fileExtension.value.substring(1);\n                        if (language.value.indexOf(ext) > -1) {\n                            config.language = language.label;\n                        }\n                    });\n                }\n                const exists = fileTabs.value.some((tab) => tab.path === path);\n                if (exists) {\n                    fileTabs.value = fileTabs.value\n                        .filter((t) => t.path !== path)\n                        .concat([{ name: res.data.name, path: res.data.path }]);\n                } else {\n                    fileTabs.value.push({\n                        name: res.data.name,\n                        path: res.data.path,\n                    });\n                }\n                saveTabsToStorage();\n                selectTab.value = res.data.path;\n            })\n            .catch(() => {});\n    };\n\n    if (isEdit.value) {\n        ElMessageBox.confirm(i18n.global.t('file.saveAndOpenNewFile'), {\n            confirmButtonText: i18n.global.t('commons.button.sure'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        })\n            .then(() => {\n                saveContent();\n                fetchFileContent();\n            })\n            .catch(() => {\n                selectTab.value = form.value.path;\n            })\n            .finally(() => {});\n    } else {\n        fetchFileContent();\n    }\n};\n\nconst initTreeData = () => ({\n    path: '/',\n    expand: true,\n    showHidden: true,\n    page: 1,\n    pageSize: 1000,\n    search: '',\n    containSub: true,\n    dir: false,\n    sortBy: 'name',\n    sortOrder: 'ascending',\n});\n\nlet req = reactive(initTreeData());\n\nconst loadedNodes = ref(new Set());\n\nconst search = async (path: string) => {\n    req.path = path;\n    if (req.search != '') {\n        req.sortBy = 'name';\n        req.sortOrder = 'ascending';\n    }\n    return await getFilesTree(req);\n};\n\nconst getUpData = async () => {\n    if ('/' === directoryPath.value) {\n        MsgWarning(i18n.global.t('commons.msg.rootInfoErr'));\n        return;\n    }\n    let pathParts = directoryPath.value.split('/');\n    pathParts.pop();\n    let newPath = pathParts.join('/') || '/';\n\n    try {\n        const response = await search(newPath);\n        treeData.value = response.data[0]?.children || [];\n        loadedNodes.value = new Set();\n        isCreate.value = 'none';\n        currentPath.value = newPath;\n        selectedParentNode.value = null;\n    } catch (error) {\n    } finally {\n        directoryPath.value = newPath;\n    }\n};\n\nconst treeRef = ref<InstanceType<typeof ElTreeV2>>();\n\nconst treeProps = {\n    value: 'id',\n    label: 'name',\n    children: 'children',\n};\n\nconst handleNodeCollapse = (data: TreeNodeData, node: TreeNode) => {\n    isCreate.value = 'none';\n    expandedNodeIds.value.delete(data.id);\n\n    const parentNode = node.parent;\n    if (!parentNode) {\n        selectedParentNode.value = null;\n        return;\n    }\n\n    const hasExpandedChildren = parentNode.data.children?.some((child) => expandedNodeIds.value.has(child.id));\n\n    if (hasExpandedChildren) {\n        selectedParentNode.value = parentNode;\n    } else {\n        selectedParentNode.value = null;\n    }\n};\n\nconst handleNodeExpand = async (data: TreeNodeData, node: TreeNode) => {\n    if (node.data.id == 'new-dir' || node.data.id == 'new-file') {\n        return;\n    }\n    if (!node.data.isDir || loadedNodes.value.has(node.data.path)) {\n        return;\n    }\n    if (node.data.isDir && isCreate.value == 'none') {\n        currentPath.value = node.data.path;\n        selectedParentNode.value = node;\n        expandedNodeIds.value.add(node.data.id);\n    }\n    try {\n        const response = await search(data.path);\n        const newTreeData = JSON.parse(JSON.stringify(treeData.value));\n        if (response.data.length > 0 && response.data[0].children) {\n            node.children = response.data[0].children;\n            loadedNodes.value.add(node.data.path);\n            updateNodeChildren(newTreeData, node.data.path, response.data[0].children);\n        } else {\n            node.children = [];\n        }\n        treeData.value = newTreeData;\n    } catch (error) {}\n};\n\nconst updateNodeChildren = (nodes: any[], path: any, newChildren: File.FileTree[]) => {\n    const updateNode = (nodes: string | any[]) => {\n        for (const element of nodes) {\n            if (element.path === path) {\n                element.children = newChildren;\n                break;\n            }\n            if (element.children && element.children.length) {\n                updateNode(element.children);\n            }\n        }\n    };\n    updateNode(nodes);\n};\n\nconst currentEditingNode = ref<any>(null);\n\nconst createNewNode = (command: string) => {\n    const isDir = command === 'dir';\n    const fileName = isDir ? 'dir' : 'file';\n    return {\n        id: isDir ? 'new-dir' : 'new-file',\n        name: fileName,\n        path: `${currentPath.value}/${fileName}`,\n        isDir,\n        extension: '',\n        children: [],\n    };\n};\n\nconst removeExistingNode = (data: any[], command: string) => {\n    const targetId = command == 'dir' ? 'new-dir' : 'new-file';\n    data = filterNodes(data, targetId);\n    treeData.value = [...data];\n    return data;\n};\n\nconst handleCreate = (command: string) => {\n    removeExistingNode(treeData.value, command);\n    if ((command === 'dir' && isCreate.value === 'file') || (command === 'file' && isCreate.value === 'dir')) {\n        cancelFolder();\n        isCreate.value = 'none';\n    }\n\n    if (isCreate.value !== 'none') return;\n\n    const newFileNode = createNewNode(command);\n    newFolder.value = newFileNode.name;\n    currentEditingNode.value = newFileNode;\n    if (selectedParentNode.value) {\n        if (!selectedParentNode.value.children) {\n            selectedParentNode.value.children = [];\n        }\n        selectedParentNode.value.children.unshift(newFileNode);\n        updateNodeChildren(treeData.value, selectedParentNode.value.data.path, selectedParentNode.value.children);\n        treeData.value = [...treeData.value];\n    } else {\n        treeData.value = [newFileNode, ...treeData.value];\n    }\n\n    nextTick(() => {\n        rowRefs.value?.focus();\n    });\n\n    isCreate.value = command;\n};\n\nconst filterNodes = (nodes, targetId) => {\n    const filtered = nodes.filter((node) => node.id !== targetId);\n\n    filtered.forEach((node) => {\n        if (node.children && node.children.length > 0) {\n            node.children = filterNodes(node.children, targetId);\n        }\n        if (node.data && node.data.children && node.data.children.length > 0) {\n            node.data.children = filterNodes(node.data.children, targetId);\n        }\n    });\n\n    return filtered;\n};\n\nconst cancelFolder = () => {\n    const targetId = isCreate.value == 'dir' ? 'new-dir' : 'new-file';\n    isCreate.value = 'none';\n    newFolder.value = '';\n    if (selectedParentNode.value && selectedParentNode.value.children.length > 0) {\n        selectedParentNode.value.children = selectedParentNode.value.children.filter((node) => node.id !== targetId);\n        if (selectedParentNode.value.data?.children?.length > 0) {\n            selectedParentNode.value.data.children = selectedParentNode.value.data.children.filter(\n                (node) => node.id !== targetId,\n            );\n        }\n    }\n    treeData.value = filterNodes(treeData.value, targetId);\n    loadedNodes.value.delete(currentPath.value);\n};\n\nlet addForm = reactive({ path: '', name: '', isDir: true, mode: 0o755, isLink: false, isSymlink: true, linkPath: '' });\n\nconst createFolder = async (isDir: boolean) => {\n    addForm.path = `${currentPath.value}/${newFolder.value}`;\n    const editingNode = currentEditingNode.value;\n    if (!editingNode) return;\n    if (addForm.path.indexOf('.1panel_clash') > -1) {\n        MsgWarning(i18n.global.t('file.clashDidNotSupport'));\n        return;\n    }\n    addForm.isDir = isDir;\n    addForm.name = newFolder.value;\n    let addItem = {};\n    Object.assign(addItem, addForm);\n    loading.value = true;\n    createFile(addItem as File.FileCreate)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n            editingNode.id = newUUID();\n            editingNode.name = addForm.name;\n            editingNode.path = addForm.path;\n            treeData.value = [...treeData.value];\n            isCreate.value = 'none';\n            currentEditingNode.value = null;\n            newFolder.value = '';\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonBeforeUnmount(() => {\n    if (editor) {\n        editor.dispose();\n    }\n    isCreate.value = 'none';\n    currentPath.value = '';\n    selectedParentNode.value = null;\n    window.removeEventListener('resize', updateHeights);\n});\n\ndefineExpose({ acceptParams });\n</script>\n\n<style scoped lang=\"scss\">\n.dialog-top {\n    top: 0;\n}\n\n.dialog-header-icon {\n    color: var(--el-color-info);\n}\n\n.monaco-editor-tree {\n    color: var(--el-color-primary) !important;\n}\n\n.monaco-editor-background {\n    outline-style: none;\n    background-color: var(--vscode-editor-background) !important;\n}\n\n.tree-widget {\n    background-color: var(--el-button--primary);\n}\n\n.truncate-text {\n    display: inline-block;\n    max-width: 800px;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n.code-header {\n    background-color: var(--panel-code-header-footer-color);\n}\n.code-footer {\n    background-color: var(--panel-code-header-footer-color);\n}\n.card-action {\n    .el-button + .el-button {\n        margin-left: 0 !important;\n    }\n    .el-button.is-link {\n        padding: 0;\n    }\n}\n.code-dialog {\n    .el-dialog__footer {\n        padding-top: 0 !important;\n    }\n}\n.code-action {\n    border-bottom: 1px solid var(--el-border-color-light) !important;\n}\n\n.table-icon {\n    width: 1.35em;\n    height: 1.35em;\n    position: relative;\n    fill: currentColor;\n    vertical-align: middle;\n}\n\n:deep(.el-tabs) {\n    --el-tabs-header-height: 29px;\n    .el-tabs__header {\n        height: 29px;\n        margin: 0;\n    }\n    .el-tabs__nav-wrap {\n        height: 28px;\n        line-height: 28px;\n    }\n    .el-tabs__nav {\n        border-right: 1px solid var(--el-border-color-light) !important;\n        border-top: none !important;\n        border-left: none !important;\n        border-bottom: none !important;\n        border-radius: 0 !important;\n        box-sizing: border-box !important;\n    }\n    .el-tabs__nav,\n    .el-tabs__nav-next,\n    .el-tabs__nav-prev {\n        height: 28px;\n        line-height: 28px;\n    }\n    .el-tabs__item:hover {\n        color: var(--el-color-primary) !important;\n        .el-dropdown {\n            color: var(--el-color-primary) !important;\n        }\n    }\n    .el-tabs__item.is-active {\n        color: var(--el-color-primary) !important;\n        .el-dropdown {\n            color: var(--el-color-primary) !important;\n        }\n    }\n}\n\n:deep(.el-dropdown .el-text:focus) {\n    outline: none !important;\n}\n\n:deep(.el-input__inner:focus) {\n    outline: none !important;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/code-editor/tabs/index.vue",
    "content": "<template>\n    <el-tabs\n        v-model=\"currentTab\"\n        type=\"card\"\n        :closable=\"props.fileTabs.length > 1\"\n        class=\"monaco-editor monaco-editor-background\"\n        @tab-remove=\"props.onRemoveTab\"\n        @tab-change=\"props.onChangeTab\"\n    >\n        <el-tab-pane v-for=\"item in props.fileTabs\" :key=\"item.path\" :name=\"item.path\">\n            <template #label>\n                <el-tooltip v-if=\"props.fileTabs.length == 1\" :content=\"item.path\" placement=\"bottom-start\">\n                    <span>{{ item.name }}</span>\n                </el-tooltip>\n                <template v-if=\"props.fileTabs.length > 1\">\n                    <el-dropdown\n                        size=\"small\"\n                        :id=\"item.path\"\n                        :ref=\"(el) => setDropdownRef(item.path, el)\"\n                        trigger=\"contextmenu\"\n                        placement=\"bottom\"\n                        @visible-change=\"(visible) => onDropdownVisibleChange(visible, item.path)\"\n                    >\n                        <span class=\"el-dropdown-link\">\n                            <el-tooltip :content=\"item.path\" placement=\"bottom-start\">\n                                {{ item.name }}\n                            </el-tooltip>\n                        </span>\n                        <template #dropdown>\n                            <el-dropdown-menu>\n                                <el-dropdown-item @click=\"props.onRemoveTab(item.path)\">\n                                    <el-icon><Close /></el-icon>\n                                    {{ $t('commons.button.close') }}\n                                </el-dropdown-item>\n                                <el-dropdown-item @click=\"props.onRemoveAllTab(item.path, 'left')\">\n                                    <el-icon><DArrowLeft /></el-icon>\n                                    {{ $t('tabs.closeLeft') }}\n                                </el-dropdown-item>\n                                <el-dropdown-item @click=\"props.onRemoveAllTab(item.path, 'right')\">\n                                    <el-icon><DArrowRight /></el-icon>\n                                    {{ $t('tabs.closeRight') }}\n                                </el-dropdown-item>\n                                <el-dropdown-item @click=\"props.onRemoveOtherTab(item.path)\">\n                                    <el-icon><More /></el-icon>\n                                    {{ $t('tabs.closeOther') }}\n                                </el-dropdown-item>\n                                <el-dropdown-item @click=\"props.onRemoveAllTab(item.path, 'all')\">\n                                    <el-icon><Operation /></el-icon>\n                                    {{ $t('tabs.closeAll') }}\n                                </el-dropdown-item>\n                            </el-dropdown-menu>\n                        </template>\n                    </el-dropdown>\n                </template>\n            </template>\n        </el-tab-pane>\n    </el-tabs>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\n\ninterface FileTabs {\n    path: string;\n    name: string;\n}\n\nconst props = defineProps({\n    selectTab: String,\n    fileTabs: Array<FileTabs>,\n    onRemoveTab: Function,\n    onChangeTab: Function,\n    onRemoveAllTab: Function,\n    onRemoveOtherTab: Function,\n});\nconst dropdownRefs = ref<Record<string, any>>({});\n\nconst setDropdownRef = (path: string, el: any) => {\n    if (el) {\n        dropdownRefs.value[path] = el;\n    }\n};\n\nconst currentTab = ref(props.selectTab);\n\nwatch(\n    () => props.selectTab,\n    (val) => {\n        currentTab.value = val;\n    },\n);\n\nconst onDropdownVisibleChange = (visible: boolean, currentPath: string) => {\n    if (visible) {\n        for (const path in dropdownRefs.value) {\n            if (path !== currentPath) {\n                dropdownRefs.value[path]?.handleClose?.();\n            }\n        }\n    }\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/compress/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"title\" @close=\"handleClose\" size=\"large\">\n        <el-form\n            ref=\"fileForm\"\n            label-position=\"top\"\n            :model=\"form\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('file.compressType')\" prop=\"type\">\n                <el-select v-model=\"form.type\">\n                    <el-option v-for=\"item in options\" :key=\"item\" :label=\"item\" :value=\"item\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model=\"form.name\">\n                    <template #append>{{ extension }}</template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('file.compressDst')\" prop=\"dst\">\n                <el-input v-model=\"form.dst\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: true, path: form.dst })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.compressPassword')\" prop=\"secret\" v-if=\"form.type === 'tar.gz'\">\n                <el-input v-model=\"form.secret\"></el-input>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"form.replace\" :label=\"$t('file.replace')\"></el-checkbox>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(fileForm)\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"getLinkPath\" />\n</template>\n\n<script setup lang=\"ts\">\nimport i18n from '@/lang';\nimport { computed, reactive, ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { CompressExtension, CompressType } from '@/enums/files';\nimport { compressFile } from '@/api/modules/files';\nimport FileList from '@/components/file-list/index.vue';\nimport { MsgSuccess } from '@/utils/message';\n\ninterface CompressProps {\n    files: Array<any>;\n    dst: string;\n    name: string;\n    operate: string;\n}\n\nconst rules = reactive<FormRules>({\n    type: [Rules.requiredSelect],\n    dst: [Rules.requiredInput],\n    name: [Rules.requiredInput],\n});\n\nconst fileForm = ref<FormInstance>();\nconst loading = ref(false);\nconst form = ref<File.FileCompress>({ files: [], type: 'zip', dst: '', name: '', replace: false, secret: '' });\nconst options = ref<string[]>([]);\nconst open = ref(false);\nconst title = ref('');\nconst operate = ref('compress');\nconst fileRef = ref();\n\nconst em = defineEmits(['close']);\n\nconst extension = computed(() => {\n    return CompressExtension[form.value.type];\n});\n\nconst handleClose = () => {\n    if (fileForm.value) {\n        fileForm.value.resetFields();\n    }\n    em('close', open);\n    open.value = false;\n};\n\nconst getLinkPath = (path: string) => {\n    form.value.dst = path;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        let addItem = {};\n        Object.assign(addItem, form.value);\n        addItem['name'] = form.value.name + extension.value;\n        loading.value = true;\n        compressFile(addItem as File.FileCompress)\n            .then(() => {\n                MsgSuccess(i18n.global.t('file.compressSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst acceptParams = (props: CompressProps) => {\n    form.value.files = props.files;\n    form.value.dst = props.dst;\n    form.value.name = props.name;\n\n    operate.value = props.operate;\n    options.value = [];\n    for (const t in CompressType) {\n        options.value.push(CompressType[t]);\n    }\n    open.value = true;\n\n    title.value = i18n.global.t('file.' + props.operate);\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/convert/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"open\" :header=\"$t('file.formatConvert')\" @close=\"handleClose\" size=\"60%\">\n            <template #content>\n                <el-tabs v-model=\"activeName\" type=\"card\" @tab-click=\"handleClick\">\n                    <el-tab-pane :label=\"$t('file.formatConvert')\" name=\"convert\">\n                        <el-form v-loading=\"loading\">\n                            <div class=\"flex-row justify-start items-center\">\n                                <el-button type=\"primary\" plain :icon=\"Plus\" @click=\"addFile\" />\n                            </div>\n                            <ComplexTable v-model:selects=\"selects\" :data=\"data\">\n                                <el-table-column fixed type=\"selection\" width=\"30\" />\n                                <el-table-column\n                                    :label=\"$t('file.sourceFile')\"\n                                    show-overflow-tooltip\n                                    prop=\"inputFile\"\n                                    min-width=\"150\"\n                                ></el-table-column>\n                                <el-table-column\n                                    :label=\"$t('file.sourceFormat')\"\n                                    show-overflow-tooltip\n                                    prop=\"extension\"\n                                    width=\"90\"\n                                ></el-table-column>\n                                <el-table-column prop=\"outputFile\" width=\"160\">\n                                    <template #header>\n                                        <div class=\"flex items-center justify-start gap-x-1\">\n                                            <span>{{ $t('file.converting') }}</span>\n                                            <el-select\n                                                v-model=\"handleOutputFile\"\n                                                placeholder=\"Select\"\n                                                size=\"small\"\n                                                style=\"width: 90px\"\n                                                class=\"font-normal\"\n                                                @change=\"handleHeaderSelectChange\"\n                                            >\n                                                <el-option\n                                                    v-for=\"item in options\"\n                                                    :key=\"item.value\"\n                                                    :label=\"item.label\"\n                                                    :value=\"item.value\"\n                                                ></el-option>\n                                            </el-select>\n                                        </div>\n                                    </template>\n\n                                    <template #default=\"{ row }\">\n                                        <el-select\n                                            v-model=\"row.outputFormat\"\n                                            style=\"width: 135px\"\n                                            :value=\"row.outputFormat || getOptionsByType(row.type)[0]?.value\"\n                                        >\n                                            <el-option\n                                                v-for=\"item in getOptionsByType(row.type)\"\n                                                :key=\"item.value\"\n                                                :label=\"item.label\"\n                                                :value=\"item.value\"\n                                            ></el-option>\n                                        </el-select>\n                                    </template>\n                                </el-table-column>\n                                <fu-table-operations\n                                    :buttons=\"buttons\"\n                                    :label=\"$t('commons.table.operate')\"\n                                    fix\n                                    width=\"100\"\n                                />\n                            </ComplexTable>\n                            <div class=\"flex-row justify-start items-center pt-6\">\n                                <div class=\"flex justify-start items-center\">\n                                    <el-breadcrumb-item>{{ $t('file.saveDir') + ':' }}</el-breadcrumb-item>\n                                    <el-breadcrumb separator=\"/\">\n                                        <el-breadcrumb-item>{{ outputPath }}</el-breadcrumb-item>\n                                    </el-breadcrumb>\n                                </div>\n                                <el-checkbox v-model=\"deleteSource\" :label=\"$t('file.deleteSourceFile')\" size=\"large\" />\n                            </div>\n                        </el-form>\n                    </el-tab-pane>\n                    <el-tab-pane :label=\"$t('file.convertLogs')\" name=\"log\">\n                        <ComplexTable :pagination-config=\"paginationConfig\" @search=\"search()\" :data=\"logs\">\n                            <el-table-column\n                                :label=\"$t('commons.table.date')\"\n                                show-overflow-tooltip\n                                prop=\"date\"\n                                width=\"160\"\n                            ></el-table-column>\n                            <el-table-column\n                                :label=\"$t('commons.table.type')\"\n                                show-overflow-tooltip\n                                prop=\"type\"\n                                width=\"70\"\n                            ></el-table-column>\n                            <el-table-column :label=\"$t('commons.button.log')\" prop=\"log\" min-width=\"160\">\n                                <template #default=\"{ row }\">\n                                    <el-tooltip :content=\"row.log\" placement=\"top\">\n                                        {{ row.log }}\n                                    </el-tooltip>\n                                </template>\n                            </el-table-column>\n                            <el-table-column :label=\"$t('commons.table.message')\" prop=\"status\" width=\"100\">\n                                <template #default=\"{ row }\">\n                                    <el-tooltip v-if=\"row.status === 'FAILED'\" :content=\"row.message\" placement=\"top\">\n                                        <el-button type=\"danger\" link>\n                                            {{ $t('commons.status.' + row.status.toLowerCase()) }}\n                                        </el-button>\n                                    </el-tooltip>\n\n                                    <el-button v-else :type=\"row.status === 'SUCCESS' ? 'success' : 'default'\" link>\n                                        {{ $t('commons.status.' + row.status.toLowerCase()) }}\n                                    </el-button>\n                                </template>\n                            </el-table-column>\n                        </ComplexTable>\n                    </el-tab-pane>\n                </el-tabs>\n            </template>\n            <template #footer v-if=\"activeName === 'convert'\">\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"removeSelected\" :disabled=\"selects.length === 0\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                    <el-button type=\"primary\" @click=\"convert\" :disabled=\"selects.length === 0\">\n                        {{ $t('commons.button.handle') }}\n                    </el-button>\n                </span>\n            </template>\n        </DrawerPro>\n        <FileList ref=\"chooseFileRef\" @choose=\"chooseFile\" />\n        <TaskLog ref=\"taskLogRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport i18n from '@/lang';\nimport { reactive, ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport FileList from '@/components/file-list/index.vue';\nimport { Plus } from '@element-plus/icons-vue';\nimport { MsgWarning, MsgSuccess } from '@/utils/message';\nimport { convertFiles, convertLogs, getFileContent } from '@/api/modules/files';\nimport { getFileType, isConvertible } from '@/utils/util';\nimport { v4 as uuidv4 } from 'uuid';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { TabsPaneContext } from 'element-plus';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst activeName = ref('convert');\nconst open = ref(false);\nconst loading = ref(false);\nconst deleteSource = ref(false);\nconst handleOutputFile = ref('png');\nconst chooseFileRef = ref();\nconst outputPath = ref('');\nconst data = ref<File.ConvertFile[]>([]);\nconst em = defineEmits(['close']);\nlet selects = ref<any>([]);\nconst taskLogRef = ref();\nconst logs = ref([]);\n\nconst CONVERT_KEY = 'convert-files:' + globalStore.currentNode;\nconst handleClose = () => {\n    open.value = false;\n    handleOutputFile.value = 'png';\n    activeName.value = 'convert';\n    em('close', false);\n};\ninterface DialogProps {\n    files: File.ConvertFile[];\n    outputPath: string;\n}\n\nconst options = [\n    { value: 'png', label: 'png', type: 'image' },\n    { value: 'jpg', label: 'jpg', type: 'image' },\n    { value: 'webp', label: 'webp', type: 'image' },\n    { value: 'gif', label: 'gif', type: 'image' },\n    { value: 'jpeg', label: 'jpeg', type: 'image' },\n    { value: 'bmp', label: 'bmp', type: 'image' },\n    { value: 'tiff', label: 'tiff', type: 'image' },\n\n    { value: 'mp4', label: 'mp4', type: 'video' },\n    { value: 'avi', label: 'avi', type: 'video' },\n    { value: 'mov', label: 'mov', type: 'video' },\n    { value: 'mkv', label: 'mkv', type: 'video' },\n\n    { value: 'mp3', label: 'mp3', type: 'audio' },\n    { value: 'wav', label: 'wav', type: 'audio' },\n    { value: 'flac', label: 'flac', type: 'audio' },\n    { value: 'aac', label: 'aac', type: 'audio' },\n];\n\nlet req = reactive({\n    name: '',\n    page: 1,\n    pageSize: 10,\n    orderBy: 'favorite',\n    order: 'descending',\n});\n\nconst getOptionsByType = (type: string) => {\n    return options.filter((opt) => opt.type === type);\n};\n\nconst initTableData = () => {\n    data.value.map((row) => {\n        const matchedOption = getOptionsByType(row.type).find((item) => row.extension.concat(item.value));\n        const defaultOption = getOptionsByType(row.type)[0]?.value;\n        row.outputFormat = matchedOption?.value || defaultOption;\n    });\n};\n\nconst handleHeaderSelectChange = (value: string) => {\n    if (value && data.value.length) {\n        data.value.forEach((row) => {\n            const isValid = getOptionsByType(row.type).some((item) => item.value === value);\n            row.outputFormat = isValid ? value : row.outputFormat;\n        });\n    }\n};\nconst addFile = () => {\n    chooseFileRef.value?.acceptParams({ dir: false });\n};\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'file-convert-log-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('file-convert-log-page-size')) || 20,\n    total: 0,\n});\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    data.value = [];\n    await convertLogs(req)\n        .then((res) => {\n            logs.value = res.data.items;\n            paginationConfig.total = res.data.total;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst chooseFile = async (path: string) => {\n    await getFileContent({ path: path, expand: false, page: 1, pageSize: 1, isDetail: true }).then((res) => {\n        if (!isConvertible(res.data.extension, res.data.mimeType)) {\n            MsgWarning(i18n.global.t('file.fileCanNotConvert'));\n            return;\n        }\n        updateFiles([\n            {\n                type: getFileType(res.data.extension),\n                inputFile: res.data.name,\n                extension: res.data.extension,\n                path: getDirPath(res.data.path, res.data.name),\n                outputFormat: res.data.extension.slice(1),\n            },\n        ]);\n    });\n};\n\nconst getDirPath = (fullPath: string, fileName: string): string => {\n    if (!fullPath || !fileName) return fullPath;\n    if (fullPath.endsWith(fileName)) {\n        const dir = fullPath.slice(0, fullPath.length - fileName.length);\n        return dir.replace(/\\/$/, '');\n    }\n    return fullPath;\n};\n\nconst delFile = async (path: string, name: string) => {\n    ElMessageBox.confirm(i18n.global.t('file.deleteHelper2'), i18n.global.t('commons.msg.remove'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        try {\n            loading.value = true;\n            removeByPathAndName(path, name);\n        } catch (error) {\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\nconst removeByPathAndName = (path: string, name: string) => {\n    const fullPath = `${path}/${name}`;\n    data.value = data.value.filter((item) => `${item.path}/${item.inputFile}` !== fullPath);\n    saveFiles();\n};\n\nconst convert = async () => {\n    if (selects.value.length > 0) {\n        ElMessageBox.confirm(i18n.global.t('file.convertHelper'), i18n.global.t('file.convert'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        }).then(async () => {\n            loading.value = true;\n            const reqFiles = selects.value;\n            const output = outputPath.value;\n            const deleteSrc = deleteSource.value;\n            const taskID = uuidv4();\n            const singleReq = {\n                files: reqFiles,\n                outputPath: output,\n                deleteSource: deleteSrc,\n                taskID: taskID,\n            };\n            await convertFiles(singleReq)\n                .then(() => {\n                    removeSelected();\n                    deleteSource.value = false;\n                    MsgSuccess(i18n.global.t('file.execConvert'));\n                    openTaskLog(taskID);\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        });\n    } else {\n        MsgWarning(i18n.global.t('file.convertHelper1'));\n    }\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: any) => {\n            delFile(row.path, row.inputFile);\n        },\n    },\n];\n\nconst loadFiles = () => {\n    const stored = localStorage.getItem(CONVERT_KEY);\n    if (stored) {\n        try {\n            data.value = JSON.parse(stored);\n        } catch {\n            data.value = [];\n        }\n    } else {\n        data.value = [];\n    }\n};\n\nconst saveFiles = () => {\n    localStorage.setItem(CONVERT_KEY, JSON.stringify(data.value));\n};\n\nconst updateFiles = (newFiles: File.ConvertFile[]) => {\n    const stored = localStorage.getItem(CONVERT_KEY);\n    if (stored) {\n        try {\n            const oldFiles: File.ConvertFile[] = JSON.parse(stored);\n            const merged = [...oldFiles];\n            for (const file of newFiles) {\n                if (!merged.some((item) => item.path === file.path && item.inputFile === file.inputFile)) {\n                    merged.push(file);\n                }\n            }\n            data.value = merged;\n        } catch {\n            data.value = [...newFiles];\n        }\n    } else {\n        data.value = [...newFiles];\n    }\n    saveFiles();\n};\n\nconst handleClick = (tab: TabsPaneContext) => {\n    if (tab.paneName == 'log') {\n        search();\n    } else {\n        loadFiles();\n    }\n};\n\nconst removeSelected = () => {\n    const selectedKeys = new Set(selects.value.map((s) => `${s.path}/${s.inputFile}`));\n    data.value = data.value.filter((d) => !selectedKeys.has(`${d.path}/${d.inputFile}`));\n    saveFiles();\n};\n\nconst acceptParams = (param: DialogProps): void => {\n    open.value = true;\n    outputPath.value = param.outputPath;\n    loadFiles();\n    updateFiles(param.files);\n    initTableData();\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.create')\" @close=\"handleClose\" size=\"normal\">\n        <el-form\n            ref=\"fileForm\"\n            label-position=\"top\"\n            :model=\"addForm\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n            @submit.enter.prevent\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model=\"addForm.name\" />\n            </el-form-item>\n            <el-form-item v-if=\"!addForm.isDir\">\n                <el-checkbox v-model=\"addForm.isLink\" :label=\"$t('file.link')\"></el-checkbox>\n            </el-form-item>\n            <el-form-item :label=\"$t('file.linkType')\" v-if=\"addForm.isLink\" prop=\"linkType\">\n                <el-radio-group v-model=\"addForm.isSymlink\">\n                    <el-radio :value=\"true\">{{ $t('file.softLink') }}</el-radio>\n                    <el-radio :value=\"false\">{{ $t('file.hardLink') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item v-if=\"addForm.isLink\" :label=\"$t('file.linkPath')\" prop=\"linkPath\">\n                <el-input v-model=\"addForm.linkPath\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({})\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-if=\"addForm.isDir\" v-model=\"setRole\" :label=\"$t('file.editPermissions')\"></el-checkbox>\n            </el-form-item>\n        </el-form>\n        <FileRole v-if=\"setRole\" :mode=\"'0755'\" @get-mode=\"getMode\" :key=\"open.toString()\"></FileRole>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(fileForm)\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"getLinkPath\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, reactive, computed } from 'vue';\nimport { File } from '@/api/interface/file';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { createFile } from '@/api/modules/files';\nimport i18n from '@/lang';\nimport FileRole from '@/components/file-role/index.vue';\nimport { Rules } from '@/global/form-rules';\nimport FileList from '@/components/file-list/index.vue';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\n\nconst fileForm = ref<FormInstance>();\nlet loading = ref(false);\nlet setRole = ref(false);\nconst fileRef = ref();\n\ninterface CreateProps {\n    file: Object;\n}\nconst propData = ref<CreateProps>({\n    file: {},\n});\n\nlet addForm = reactive({ path: '', name: '', isDir: false, mode: 0o755, isLink: false, isSymlink: true, linkPath: '' });\nlet open = ref(false);\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    if (fileForm.value) {\n        fileForm.value.resetFields();\n    }\n    em('close', open);\n};\n\nconst rules = reactive<FormRules>({\n    name: [Rules.requiredInput, Rules.linuxName],\n    path: [Rules.requiredInput],\n    isSymlink: [Rules.requiredInput],\n    linkPath: [Rules.requiredInput],\n});\n\nconst getMode = (val: number) => {\n    addForm.mode = val;\n};\n\nlet getPath = computed(() => {\n    if (addForm.path.endsWith('/')) {\n        return addForm.path + addForm.name.trim();\n    } else {\n        return addForm.path + '/' + addForm.name.trim();\n    }\n});\n\nconst getLinkPath = (path: string) => {\n    addForm.linkPath = path;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        if (getPath.value.indexOf('.1panel_clash') > -1) {\n            MsgWarning(i18n.global.t('file.clashDidNotSupport'));\n            return;\n        }\n\n        let addItem = {};\n        Object.assign(addItem, addForm);\n        addItem['path'] = getPath.value;\n        loading.value = true;\n        if (!setRole.value) {\n            addItem['mode'] = undefined;\n        }\n        addItem['name'] = addForm.name.trim();\n        createFile(addItem as File.FileCreate)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst acceptParams = (create: File.FileCreate) => {\n    propData.value.file = create;\n    open.value = true;\n    addForm.isDir = create.isDir;\n    addForm.path = create.path;\n    addForm.name = '';\n    addForm.isLink = false;\n\n    init();\n};\n\nconst init = () => {\n    setRole.value = false;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/decompress/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('file.deCompress')\" :resource=\"name\" @close=\"handleClose\" size=\"normal\">\n        <el-form\n            ref=\"fileForm\"\n            label-position=\"top\"\n            :model=\"form\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\">\n                <el-input v-model=\"name\" disabled></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('file.deCompressDst')\" prop=\"dst\">\n                <el-input v-model=\"form.dst\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ path: form.dst, dir: true })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.compressPassword')\" prop=\"secret\" v-if=\"name.includes('tar.gz')\">\n                <el-input v-model=\"form.secret\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(fileForm)\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"getLinkPath\" />\n</template>\n\n<script setup lang=\"ts\">\nimport i18n from '@/lang';\nimport { reactive, ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { deCompressFile } from '@/api/modules/files';\nimport FileList from '@/components/file-list/index.vue';\nimport { MsgSuccess } from '@/utils/message';\n\ninterface CompressProps {\n    files: Array<any>;\n    dst: string;\n    name: string;\n    path: string;\n    type: string;\n}\n\nconst rules = reactive<FormRules>({\n    dst: [Rules.requiredInput],\n});\n\nconst fileForm = ref<FormInstance>();\nlet loading = ref(false);\nlet form = ref<File.FileDeCompress>({ type: 'zip', dst: '', path: '', secret: '' });\nlet open = ref(false);\nlet name = ref('');\nconst fileRef = ref();\n\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    if (fileForm.value) {\n        fileForm.value.resetFields();\n    }\n    open.value = false;\n    em('close', open);\n};\n\nconst getLinkPath = (path: string) => {\n    form.value.dst = path;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        deCompressFile(form.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('file.deCompressSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst acceptParams = (props: CompressProps) => {\n    form.value.type = props.type;\n    form.value.dst = props.dst;\n    form.value.path = props.path;\n    name.value = props.name;\n    open.value = true;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.delete')\" size=\"small\">\n        <div v-loading=\"loading\">\n            <el-row>\n                <el-col :span=\"22\" :offset=\"1\">\n                    <el-alert\n                        class=\"mt-2\"\n                        :show-icon=\"true\"\n                        :type=\"recycleStatus === 'Enable' ? 'warning' : 'error'\"\n                        :closable=\"false\"\n                    >\n                        <div class=\"delete-warn\">\n                            <span v-if=\"recycleStatus === 'Enable'\">{{ $t('file.deleteHelper') }}</span>\n                            <span v-else>{{ $t('file.deleteHelper2') }}</span>\n                        </div>\n                    </el-alert>\n                    <div class=\"mt-4\" v-if=\"recycleStatus === 'Enable'\">\n                        <el-checkbox v-model=\"forceDelete\" class=\"force-delete\">\n                            <span>{{ $t('file.forceDeleteHelper') }}</span>\n                        </el-checkbox>\n                    </div>\n\n                    <div class=\"file-list\">\n                        <div class=\"flx-align-center mb-1\" v-for=\"(row, index) in files\" :key=\"index\">\n                            <div>\n                                <svg-icon\n                                    v-if=\"row.isDir\"\n                                    className=\"table-icon mr-1 \"\n                                    iconName=\"p-file-folder\"\n                                ></svg-icon>\n                                <svg-icon\n                                    v-else\n                                    className=\"table-icon mr-1\"\n                                    :iconName=\"getIconName(row.extension)\"\n                                ></svg-icon>\n                            </div>\n                            <span class=\"sle\">{{ row.name }}</span>\n                        </div>\n                    </div>\n                </el-col>\n            </el-row>\n        </div>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\" :loading=\"loading\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport { getIcon } from '@/utils/util';\nimport { deleteFile, deleteFileByNode, getRecycleStatus, getRecycleStatusByNode } from '@/api/modules/files';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { loadBaseDir } from '@/api/modules/setting';\n\nconst open = ref(false);\nconst files = ref();\nconst loading = ref(false);\nconst em = defineEmits(['close']);\nconst forceDelete = ref(false);\nconst recycleStatus = ref('Enable');\nconst reqNode = ref('');\n\nconst acceptParams = (props: File.File[], node: string) => {\n    reqNode.value = '';\n    if (node != '') {\n        reqNode.value = node;\n    }\n    getStatus();\n    files.value = props;\n    open.value = true;\n    forceDelete.value = false;\n};\n\nconst getStatus = async () => {\n    loading.value = true;\n    try {\n        let res;\n        if (reqNode.value != '') {\n            res = await getRecycleStatusByNode(reqNode.value);\n        } else {\n            res = await getRecycleStatus();\n        }\n        recycleStatus.value = res.data;\n        if (recycleStatus.value === 'Disable') {\n            forceDelete.value = true;\n        }\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst onConfirm = async () => {\n    loading.value = true;\n    try {\n        const pros = [];\n        let baseDir = '';\n        if (files.value.some((item) => item['isDir'])) {\n            const pathRes = await loadBaseDir();\n            baseDir = pathRes.data;\n        }\n        for (const s of files.value) {\n            if (s['isDir']) {\n                if (s['path'].indexOf('.1panel_clash') > -1) {\n                    MsgWarning(i18n.global.t('file.clashDeleteAlert'));\n                    return;\n                }\n                if (s['path'] === baseDir) {\n                    MsgWarning(i18n.global.t('file.panelInstallDir'));\n                    return;\n                }\n            }\n            if (reqNode.value != '') {\n                pros.push(\n                    deleteFileByNode(\n                        { path: s['path'], isDir: s['isDir'], forceDelete: forceDelete.value },\n                        reqNode.value,\n                    ),\n                );\n            } else {\n                pros.push(deleteFile({ path: s['path'], isDir: s['isDir'], forceDelete: forceDelete.value }));\n            }\n        }\n        await Promise.all(pros);\n        MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n        open.value = false;\n        em('close');\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst getIconName = (extension: string) => {\n    return getIcon(extension);\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped>\n.resource {\n    margin-top: 10px;\n    max-height: 400px;\n    overflow: auto;\n}\n\n.file-list {\n    max-height: 400px;\n    overflow-y: auto;\n    margin-top: 15px;\n}\n\n.delete-warn {\n    line-height: 20px;\n    word-wrap: break-word;\n}\n\n.force-delete {\n    white-space: pre-line;\n    word-wrap: break-word;\n    line-height: 50px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/detail/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('file.info')\" size=\"small\" @close=\"handleClose\">\n        <el-descriptions :column=\"1\" border>\n            <el-descriptions-item label-class-name=\"detail-label\" :label=\"$t('file.fileName')\">\n                {{ data.name }}\n            </el-descriptions-item>\n            <el-descriptions-item\n                label-class-name=\"detail-label\"\n                :label=\"$t('commons.table.type')\"\n                v-if=\"data.type != ''\"\n            >\n                {{ data.type }}\n            </el-descriptions-item>\n            <el-descriptions-item class-name=\"detail-content\" label-class-name=\"detail-label\" :label=\"$t('file.path')\">\n                {{ data.path }}\n            </el-descriptions-item>\n            <el-descriptions-item label-class-name=\"detail-label\" :label=\"$t('file.size')\">\n                <span v-if=\"data.isDir\">\n                    <el-button type=\"primary\" link small @click=\"getDirSize(data)\" :loading=\"loading\">\n                        <span v-if=\"data.dirSize == undefined\">\n                            {{ $t('file.calculate') }}\n                        </span>\n                        <span v-else>{{ computeSize(data.dirSize) }}</span>\n                    </el-button>\n                </span>\n                <span v-else>{{ computeSize(data.size) }}</span>\n            </el-descriptions-item>\n            <el-descriptions-item label-class-name=\"detail-label\" :label=\"$t('file.role')\">\n                {{ data.mode }}\n            </el-descriptions-item>\n            <el-descriptions-item label-class-name=\"detail-label\" :label=\"$t('commons.table.user')\">\n                {{ data.user ? data.user : '-' }} ({{ data.uid }})\n            </el-descriptions-item>\n            <el-descriptions-item label-class-name=\"detail-label\" :label=\"$t('file.group')\">\n                {{ data.group ? data.group : '-' }} ({{ data.gid }})\n            </el-descriptions-item>\n            <el-descriptions-item label-class-name=\"detail-label\" :label=\"$t('commons.table.updatedAt')\">\n                {{ dateFormatSimple(data.modTime) }}\n            </el-descriptions-item>\n        </el-descriptions>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computeDirSize, getFileContent } from '@/api/modules/files';\nimport { computeSize } from '@/utils/util';\nimport { ref } from 'vue';\nimport { dateFormatSimple } from '@/utils/util';\n\ninterface InfoProps {\n    path: string;\n}\nconst props = ref<InfoProps>({\n    path: '',\n});\n\nlet open = ref(false);\nlet data = ref();\nlet loading = ref(false);\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = async (params: InfoProps): Promise<void> => {\n    props.value = params;\n    getFileContent({ path: params.path, expand: false, page: 1, pageSize: 1, isDetail: true }).then((res) => {\n        data.value = res.data;\n        open.value = true;\n    });\n};\n\nconst getDirSize = async (row: any) => {\n    const req = {\n        path: row.path,\n    };\n    loading.value = true;\n    await computeDirSize(req)\n        .then(async (res) => {\n            data.value.dirSize = res.data.size;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style scoped>\n:deep(.detail-label) {\n    min-width: 100px !important;\n}\n\n:deep(.detail-content) {\n    max-width: 295px;\n    word-break: break-all;\n    word-wrap: break-word;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/download/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.download')\" @close=\"handleClose\" size=\"normal\">\n        <el-form ref=\"fileForm\" label-position=\"top\" :model=\"addForm\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('file.compressType')\" prop=\"type\">\n                <el-select v-model=\"addForm.type\">\n                    <el-option v-for=\"item in options\" :key=\"item\" :label=\"item\" :value=\"item\" />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model=\"addForm.name\">\n                    <template #append>{{ extension }}</template>\n                </el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(fileForm)\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { FormInstance, FormRules } from 'element-plus';\nimport { CompressExtension, CompressType } from '@/enums/files';\nimport { computed, reactive, ref } from 'vue';\nimport { downloadFile } from '@/api/modules/files';\nimport { File } from '@/api/interface/file';\nimport { Rules } from '@/global/form-rules';\n\ninterface DownloadProps {\n    paths: Array<string>;\n    name: string;\n}\n\nconst rules = reactive<FormRules>({\n    name: [Rules.requiredInput],\n    type: [Rules.requiredInput],\n});\n\nconst fileForm = ref<FormInstance>();\nconst options = ref<string[]>([]);\nlet loading = ref(false);\nlet open = ref(false);\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    if (fileForm.value) {\n        fileForm.value.resetFields();\n    }\n    em('close', open);\n};\n\nlet addForm = ref({\n    paths: [] as string[],\n    type: '',\n    name: '',\n    compress: true,\n});\n\nconst extension = computed(() => {\n    return CompressExtension[addForm.value.type];\n});\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        let addItem = {};\n        Object.assign(addItem, addForm.value);\n        addItem['name'] = addForm.value.name + extension.value;\n        loading.value = true;\n        downloadFile(addItem as File.FileDownload)\n            .then((res) => {\n                const downloadUrl = window.URL.createObjectURL(new Blob([res]));\n                const a = document.createElement('a');\n                a.style.display = 'none';\n                a.href = downloadUrl;\n                a.download = addItem['name'];\n                const event = new MouseEvent('click');\n                a.dispatchEvent(event);\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\nconst acceptParams = (props: DownloadProps) => {\n    addForm.value.paths = props.paths;\n    addForm.value.name = props.name;\n    addForm.value.type = 'zip';\n    addForm.value.compress = true;\n    options.value = [];\n    for (const t in CompressType) {\n        options.value.push(CompressType[t]);\n    }\n    open.value = true;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/favorite/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('file.favorite')\" @close=\"handleClose\" size=\"large\">\n        <template #content>\n            <ComplexTable :pagination-config=\"paginationConfig\" :data=\"data\" @search=\"search\">\n                <el-table-column :label=\"$t('file.path')\" show-overflow-tooltip prop=\"path\">\n                    <template #default=\"{ row }\">\n                        <el-tooltip class=\"box-item\" effect=\"dark\" :content=\"row.path\" placement=\"top\">\n                            <span class=\"table-link text-ellipsis\" @click=\"toFavorite(row)\" type=\"primary\">\n                                <svg-icon v-if=\"row.isDir\" className=\"table-icon\" iconName=\"p-file-folder\"></svg-icon>\n                                <svg-icon v-else className=\"table-icon\" iconName=\"p-file-normal\"></svg-icon>\n                                {{ row.name }}\n                            </span>\n                        </el-tooltip>\n                    </template>\n                </el-table-column>\n                <fu-table-operations :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n            </ComplexTable>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { searchFavorite, removeFavorite } from '@/api/modules/files';\nimport i18n from '@/lang';\nimport { reactive, ref } from 'vue';\nimport { File } from '@/api/interface/file';\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'favorite-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('favorite-page-size')) || 20,\n    total: 0,\n});\nconst req = reactive({\n    page: 1,\n    pageSize: 20,\n});\nconst open = ref(false);\nconst data = ref([]);\nconst em = defineEmits(['close', 'jump', 'to-favorite']);\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst toFavorite = (row: File.Favorite) => {\n    open.value = false;\n    em('to-favorite', row);\n};\nconst openDir = (url: string) => {\n    open.value = false;\n    em('jump', url);\n};\n\nconst acceptParams = () => {\n    search();\n};\n\nconst search = async () => {\n    try {\n        req.page = paginationConfig.currentPage;\n        req.pageSize = paginationConfig.pageSize;\n        const res = await searchFavorite(req);\n        data.value = res.data.items;\n        paginationConfig.total = res.data.total;\n        open.value = true;\n    } catch (error) {}\n};\n\nconst singleDel = async (id: number) => {\n    ElMessageBox.confirm(i18n.global.t('file.removeFavorite'), i18n.global.t('commons.msg.remove'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        try {\n            await removeFavorite(id);\n            await search();\n        } catch (error) {}\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.open'),\n        click: (row: any) => {\n            openDir(row.path);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: any) => {\n            singleDel(row.id);\n        },\n    },\n];\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/hooks/searchable.ts",
    "content": "import { nextTick, ref, watch } from 'vue';\n\nexport function useSearchable(paths) {\n    const searchableStatus = ref(false);\n    const searchablePath = ref('');\n    const searchableInputRef = ref();\n\n    watch(searchableStatus, (val) => {\n        if (val) {\n            searchablePath.value = paths.value.at(-1)?.url;\n            nextTick(() => {\n                searchableInputRef.value?.focus();\n            });\n        }\n    });\n    const searchableInputBlur = () => {\n        searchableStatus.value = false;\n    };\n\n    return {\n        searchableStatus,\n        searchablePath,\n        searchableInputRef,\n        searchableInputBlur,\n    };\n}\n\nexport function useSearchableForSelect(paths) {\n    const searchableStatus = ref(false);\n    const searchablePath = ref('');\n    const searchableInputRef = ref();\n\n    watch(searchableStatus, (val) => {\n        if (val) {\n            if (paths.value.length === 0) {\n                searchablePath.value = '/';\n            } else {\n                searchablePath.value = '/' + paths.value.join('/');\n            }\n            nextTick(() => {\n                searchableInputRef.value?.focus();\n            });\n        }\n    });\n    const searchableInputBlur = () => {\n        searchableStatus.value = false;\n    };\n\n    return {\n        searchableStatus,\n        searchablePath,\n        searchableInputRef,\n        searchableInputBlur,\n    };\n}\n\nexport function useMultipleSearchable(paths) {\n    const searchableStatus = ref(false);\n    const searchablePath = ref('');\n    const searchableInputRefs = ref<Record<string, HTMLInputElement | null>>({});\n    const setSearchableInputRef = (id: string, el: HTMLInputElement | null) => {\n        if (el) {\n            searchableInputRefs.value[id] = el;\n            nextTick(() => {\n                searchableInputRefs.value[id]?.focus();\n            });\n        } else {\n            delete searchableInputRefs.value[id];\n        }\n    };\n\n    watch(searchableStatus, (val) => {\n        if (val) {\n            searchablePath.value = paths.value.at(-1)?.url || '';\n        }\n    });\n\n    const searchableInputBlur = () => {\n        searchableStatus.value = false;\n    };\n\n    return {\n        searchableStatus,\n        searchablePath,\n        searchableInputRefs,\n        setSearchableInputRef,\n        searchableInputBlur,\n    };\n}\n"
  },
  {
    "path": "frontend/src/views/host/file-management/index.vue",
    "content": "<template>\n    <div class=\"h-full\" ref=\"fileTableRef\" @dragover=\"handleDragover\" @drop=\"handleDrop\" @dragleave=\"handleDragleave\">\n        <el-tabs\n            type=\"card\"\n            class=\"file-tabs\"\n            v-model=\"editableTabsKey\"\n            @tab-change=\"changeTab\"\n            @tab-remove=\"removeTab\"\n        >\n            <el-tab-pane\n                closable\n                v-for=\"item in editableTabs\"\n                :key=\"item.id\"\n                :label=\"item.name == '' ? $t('file.root') : item.name\"\n                :name=\"item.id\"\n            >\n                <div class=\"flex sm:flex-row flex-col justify-start gap-y-2 items-center gap-x-4\" ref=\"toolRef\">\n                    <div class=\"flex-shrink-0 flex sm:w-min w-full items-center justify-start\">\n                        <el-tooltip :content=\"$t('file.back')\" placement=\"top\">\n                            <el-button icon=\"Back\" @click=\"back\" circle />\n                        </el-tooltip>\n                        <el-tooltip :content=\"$t('file.right')\" placement=\"top\">\n                            <el-button icon=\"Right\" @click=\"right\" circle />\n                        </el-tooltip>\n                        <el-tooltip :content=\"$t('file.top')\" placement=\"top\">\n                            <el-button icon=\"Top\" @click=\"top\" circle :disabled=\"paths.length == 0\" />\n                        </el-tooltip>\n                        <el-tooltip :content=\"$t('commons.button.refresh')\" placement=\"top\">\n                            <el-button icon=\"Refresh\" circle @click=\"search\" />\n                        </el-tooltip>\n                        <el-tooltip\n                            :content=\"req.showHidden ? $t('file.noShowHide') : $t('file.showHide')\"\n                            placement=\"top\"\n                        >\n                            <el-button\n                                class=\"btn\"\n                                circle\n                                :type=\"req.showHidden ? '' : 'primary'\"\n                                :icon=\"req.showHidden ? View : Hide\"\n                                @click=\"viewHideFile\"\n                            />\n                        </el-tooltip>\n                    </div>\n                    <div class=\"flex-1 sm:w-min w-full hidden sm:block\" :ref=\"(el) => setPathRef(item.id, el)\">\n                        <div\n                            v-show=\"!searchableStatus\"\n                            @click=\"searchableStatus = true\"\n                            class=\"address-bar shadow-md rounded-md px-4 py-2 flex items-center flex-grow\"\n                        >\n                            <div ref=\"breadCrumbRef\" class=\"flex items-center address-url\">\n                                <span class=\"root mr-2\">\n                                    <el-link @click.stop=\"jump('/')\">\n                                        <el-icon :size=\"20\"><HomeFilled /></el-icon>\n                                    </el-link>\n                                </span>\n                                <span v-for=\"(path, index) in paths\" :key=\"path.url\" class=\"inline-flex items-center\">\n                                    <span class=\"mr-2 arrow\">></span>\n                                    <template v-if=\"index === 0 && hidePaths.length > 0\">\n                                        <el-dropdown>\n                                            <span\n                                                class=\"path-segment cursor-pointer mr-2 pathname focus:outline-none focus-visible:outline-none\"\n                                            >\n                                                ..\n                                            </span>\n                                            <template #dropdown>\n                                                <el-dropdown-menu>\n                                                    <el-dropdown-item\n                                                        v-for=\"hidePath in hidePaths\"\n                                                        :key=\"hidePath.url\"\n                                                        @click.stop=\"jump(hidePath.url)\"\n                                                    >\n                                                        <el-tooltip\n                                                            class=\"box-item\"\n                                                            effect=\"dark\"\n                                                            :content=\"hidePath.name\"\n                                                            placement=\"bottom\"\n                                                        >\n                                                            {{\n                                                                hidePath.name.length > 25\n                                                                    ? hidePath.name.substring(0, 22) + '...'\n                                                                    : hidePath.name\n                                                            }}\n                                                        </el-tooltip>\n                                                    </el-dropdown-item>\n                                                </el-dropdown-menu>\n                                            </template>\n                                        </el-dropdown>\n                                        <span class=\"mr-2 arrow\">></span>\n                                        <el-tooltip\n                                            class=\"box-item\"\n                                            effect=\"dark\"\n                                            :content=\"path.name\"\n                                            placement=\"bottom\"\n                                        >\n                                            <el-link\n                                                class=\"path-segment cursor-pointer mr-2 pathname\"\n                                                @click.stop=\"jump(path.url)\"\n                                            >\n                                                {{\n                                                    path.name.length > 25\n                                                        ? path.name.substring(0, 22) + '...'\n                                                        : path.name\n                                                }}\n                                            </el-link>\n                                        </el-tooltip>\n                                    </template>\n                                    <template v-else>\n                                        <el-tooltip\n                                            class=\"box-item\"\n                                            effect=\"dark\"\n                                            :content=\"path.name\"\n                                            placement=\"bottom\"\n                                        >\n                                            <el-link\n                                                class=\"path-segment cursor-pointer mr-2 pathname\"\n                                                @click.stop=\"jump(path.url)\"\n                                            >\n                                                {{\n                                                    path.name.length > 25\n                                                        ? path.name.substring(0, 22) + '...'\n                                                        : path.name\n                                                }}\n                                            </el-link>\n                                        </el-tooltip>\n                                    </template>\n                                </span>\n                            </div>\n                        </div>\n                        <el-input\n                            :ref=\"(el) => setSearchableInputRef(item.id, el)\"\n                            v-show=\"searchableStatus\"\n                            v-model=\"searchablePath\"\n                            @blur=\"searchableInputBlur\"\n                            class=\"px-4 py-2 border rounded-md shadow-md\"\n                            @keyup.enter=\"\n                                jump(searchablePath);\n                                searchableStatus = false;\n                            \"\n                        />\n                    </div>\n                    <div class=\"flex-1 sm:w-min w-full sm:hidden block\">\n                        <div class=\"address-bar shadow-md rounded-md px-4 py-2 flex items-center flex-grow\">\n                            <div class=\"flex items-center address-url\">\n                                <span class=\"root mr-2\">\n                                    <el-link @click.stop=\"jump('/')\">\n                                        <el-icon :size=\"20\"><HomeFilled /></el-icon>\n                                    </el-link>\n                                </span>\n                                <span v-for=\"(path, index) in paths\" :key=\"path.url\" class=\"inline-flex items-center\">\n                                    <span class=\"mr-2 arrow\">></span>\n                                    <template v-if=\"index === 0 && hidePaths.length > 0\">\n                                        <el-dropdown>\n                                            <span\n                                                class=\"path-segment cursor-pointer mr-2 pathname focus:outline-none focus-visible:outline-none\"\n                                            >\n                                                ..\n                                            </span>\n                                            <template #dropdown>\n                                                <el-dropdown-menu>\n                                                    <el-dropdown-item\n                                                        v-for=\"hidePath in hidePaths\"\n                                                        :key=\"hidePath.url\"\n                                                        @click.stop=\"jump(hidePath.url)\"\n                                                    >\n                                                        <el-tooltip\n                                                            class=\"box-item\"\n                                                            effect=\"dark\"\n                                                            :content=\"hidePath.name\"\n                                                            placement=\"bottom\"\n                                                        >\n                                                            {{\n                                                                hidePath.name.length > 25\n                                                                    ? hidePath.name.substring(0, 22) + '...'\n                                                                    : hidePath.name\n                                                            }}\n                                                        </el-tooltip>\n                                                    </el-dropdown-item>\n                                                </el-dropdown-menu>\n                                            </template>\n                                        </el-dropdown>\n                                    </template>\n                                    <template v-else>\n                                        <span class=\"mr-2 arrow\">></span>\n                                        <el-tooltip\n                                            class=\"box-item\"\n                                            effect=\"dark\"\n                                            :content=\"path.name\"\n                                            placement=\"bottom\"\n                                        >\n                                            <el-link\n                                                class=\"path-segment cursor-pointer mr-2 pathname\"\n                                                @click.stop=\"jump(path.url)\"\n                                            >\n                                                {{\n                                                    path.name.length > 25\n                                                        ? path.name.substring(0, 22) + '...'\n                                                        : path.name\n                                                }}\n                                            </el-link>\n                                        </el-tooltip>\n                                    </template>\n                                </span>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n                <LayoutContent :title=\"$t('menu.files')\" v-loading=\"loading\">\n                    <template #prompt>\n                        <el-alert type=\"info\" :closable=\"false\">\n                            <template #title>\n                                <span class=\"input-help whitespace-break-spaces\">\n                                    {{ $t('file.fileHelper') }}\n                                </span>\n                            </template>\n                        </el-alert>\n                    </template>\n                    <template #leftToolBar>\n                        <div ref=\"leftWrapper\" class=\"flex items-center gap-2 flex-wrap\">\n                            <el-dropdown @command=\"handleCreate\" class=\"mr-2.5\">\n                                <el-button type=\"primary\">\n                                    {{ $t('commons.button.create') }}\n                                    <el-icon><arrow-down /></el-icon>\n                                </el-button>\n                                <template #dropdown>\n                                    <el-dropdown-menu>\n                                        <el-dropdown-item command=\"dir\">\n                                            <svg-icon iconName=\"p-file-folder\"></svg-icon>\n                                            {{ $t('file.dir') }}\n                                        </el-dropdown-item>\n                                        <el-dropdown-item command=\"file\">\n                                            <svg-icon iconName=\"p-file-normal\"></svg-icon>\n                                            {{ $t('menu.files') }}\n                                        </el-dropdown-item>\n                                    </el-dropdown-menu>\n                                </template>\n                            </el-dropdown>\n                            <el-dropdown class=\"mr-2.5\">\n                                <el-button>\n                                    {{ $t('commons.button.upload') }}/{{ $t('commons.button.download') }}\n                                    <el-icon><arrow-down /></el-icon>\n                                </el-button>\n                                <template #dropdown>\n                                    <el-dropdown-menu>\n                                        <el-dropdown-item @click=\"openUpload\">\n                                            <el-icon><ElUpload /></el-icon>\n                                            {{ $t('commons.button.upload') }}\n                                        </el-dropdown-item>\n                                        <el-dropdown-item @click=\"openWget\">\n                                            <el-icon><ElDownload /></el-icon>\n                                            {{ $t('file.remoteFile') }}\n                                        </el-dropdown-item>\n                                    </el-dropdown-menu>\n                                </template>\n                            </el-dropdown>\n                            <el-button-group class=\"sm:!inline-block !flex flex-wrap gap-y-2\">\n                                <el-button class=\"btn\" @click=\"openRecycleBin\">\n                                    {{ $t('file.recycleBin') }}\n                                </el-button>\n                                <el-button class=\"btn\" @click=\"toTerminal\">\n                                    {{ $t('menu.terminal') }}\n                                </el-button>\n                                <el-popover\n                                    placement=\"bottom\"\n                                    :width=\"250\"\n                                    trigger=\"hover\"\n                                    @before-enter=\"getFavorites\"\n                                >\n                                    <template #reference>\n                                        <el-button @click=\"openFavorite\">\n                                            {{ $t('file.favorite') }}\n                                        </el-button>\n                                    </template>\n                                    <div class=\"favorite-item\">\n                                        <el-table :data=\"favorites\">\n                                            <el-table-column prop=\"name\">\n                                                <template #default=\"{ row }\">\n                                                    <div class=\"flex justify-between items-center group\">\n                                                        <el-tooltip\n                                                            class=\"box-item\"\n                                                            effect=\"dark\"\n                                                            :content=\"row.path\"\n                                                            placement=\"top\"\n                                                        >\n                                                            <span\n                                                                class=\"table-link text-ellipsis\"\n                                                                @click=\"toFavorite(row)\"\n                                                                type=\"primary\"\n                                                            >\n                                                                <svg-icon\n                                                                    v-if=\"row.isDir\"\n                                                                    className=\"table-icon\"\n                                                                    iconName=\"p-file-folder\"\n                                                                ></svg-icon>\n                                                                <svg-icon\n                                                                    v-else\n                                                                    className=\"table-icon\"\n                                                                    iconName=\"p-file-normal\"\n                                                                ></svg-icon>\n                                                                {{ row.name }}\n                                                            </span>\n                                                        </el-tooltip>\n                                                        <el-icon\n                                                            class=\"hidden group-hover:block cursor-pointer\"\n                                                            v-if=\"!row.isDir\"\n                                                            @click=\"jump(row.path)\"\n                                                        >\n                                                            <FolderOpened />\n                                                        </el-icon>\n                                                    </div>\n                                                </template>\n                                            </el-table-column>\n                                        </el-table>\n                                    </div>\n                                </el-popover>\n                                <el-button class=\"btn\" @click=\"calculateSize(req.path)\" :loading=\"disableBtn\">\n                                    {{ $t('file.calculate') }}\n                                </el-button>\n                                <template v-if=\"hostMount.length == 1\">\n                                    <el-button class=\"btn\" @click.stop=\"jump(hostMount[0]?.path)\">\n                                        {{ hostMount[0]?.path }} ({{ $t('file.root') }})\n                                        {{ formatFileSize(hostMount[0]?.free) }}\n                                    </el-button>\n                                </template>\n                                <template v-else>\n                                    <el-dropdown class=\"mr-2.5\">\n                                        <el-button class=\"btn\">\n                                            {{ hostMount[0]?.path }} ({{ $t('file.root') }})\n                                            {{ formatFileSize(hostMount[0]?.free) }}\n                                        </el-button>\n                                        <template #dropdown>\n                                            <el-dropdown-menu>\n                                                <template v-for=\"(mount, index) in hostMount\" :key=\"mount.path\">\n                                                    <el-dropdown-item v-if=\"index == 0\" @click.stop=\"jump(mount.path)\">\n                                                        {{ mount.path }} ({{ $t('file.root') }})\n                                                        {{ formatFileSize(mount.free) }}\n                                                    </el-dropdown-item>\n                                                    <el-dropdown-item v-if=\"index != 0\" @click.stop=\"jump(mount.path)\">\n                                                        {{ mount.path }} ({{ $t('home.mount') }})\n                                                        {{ formatFileSize(mount.free) }}\n                                                    </el-dropdown-item>\n                                                </template>\n                                            </el-dropdown-menu>\n                                        </template>\n                                    </el-dropdown>\n                                </template>\n                            </el-button-group>\n\n                            <el-badge :value=\"processCount\" class=\"btn\" v-if=\"processCount > 0\">\n                                <el-button class=\"btn\" @click=\"openProcess\">\n                                    {{ $t('file.wgetTask') }}\n                                </el-button>\n                            </el-badge>\n                        </div>\n                    </template>\n                    <template #rightToolBar>\n                        <div :ref=\"(el) => setBtnWrapperRef(item.id, el)\" class=\"flex items-center gap-2 flex-wrap\">\n                            <div class=\"flex items-center gap-2 flex-wrap\">\n                                <template v-if=\"visibleButtons.length == 0\">\n                                    <el-dropdown v-if=\"moreButtons.length\">\n                                        <el-button>\n                                            {{ $t('tabs.more') }}\n                                            <i class=\"el-icon-arrow-down el-icon--right\" />\n                                        </el-button>\n                                        <template #dropdown>\n                                            <el-dropdown-menu>\n                                                <el-dropdown-item\n                                                    v-for=\"btn in moreButtons\"\n                                                    :key=\"btn.label\"\n                                                    @click=\"btn.action\"\n                                                    :disabled=\"selects.length === 0\"\n                                                >\n                                                    {{ $t(btn.label) }}\n                                                </el-dropdown-item>\n                                            </el-dropdown-menu>\n                                        </template>\n                                    </el-dropdown>\n                                </template>\n                                <template v-if=\"visibleButtons.length > 0\">\n                                    <el-button-group class=\"flex items-center\">\n                                        <template v-for=\"btn in visibleButtons\" :key=\"btn.label\">\n                                            <el-button plain @click=\"btn.action\" :disabled=\"selects.length === 0\">\n                                                {{ $t(btn.label) }}\n                                            </el-button>\n                                        </template>\n\n                                        <el-dropdown v-if=\"moreButtons.length\">\n                                            <el-button>\n                                                {{ $t('tabs.more') }}\n                                                <i class=\"el-icon-arrow-down el-icon--right\" />\n                                            </el-button>\n                                            <template #dropdown>\n                                                <el-dropdown-menu>\n                                                    <el-dropdown-item\n                                                        v-for=\"btn in moreButtons\"\n                                                        :key=\"btn.label\"\n                                                        @click=\"btn.action\"\n                                                        :disabled=\"selects.length === 0\"\n                                                    >\n                                                        {{ $t(btn.label) }}\n                                                    </el-dropdown-item>\n                                                </el-dropdown-menu>\n                                            </template>\n                                        </el-dropdown>\n                                    </el-button-group>\n                                </template>\n                            </div>\n                            <el-button-group class=\"copy-button\" v-if=\"moveOpen\">\n                                <el-tooltip\n                                    class=\"box-item\"\n                                    effect=\"dark\"\n                                    :content=\"$t('file.paste')\"\n                                    placement=\"bottom\"\n                                >\n                                    <el-button plain @click=\"openPaste\">\n                                        {{ $t('file.paste') }}({{ fileMove.count }})\n                                    </el-button>\n                                </el-tooltip>\n                                <el-tooltip\n                                    class=\"box-item\"\n                                    effect=\"dark\"\n                                    :content=\"$t('commons.button.cancel')\"\n                                    placement=\"bottom\"\n                                >\n                                    <el-button plain class=\"close\" icon=\"Close\" @click=\"closeMove\"></el-button>\n                                </el-tooltip>\n                            </el-button-group>\n                            <div class=\"flex items-center gap-2\">\n                                <fu-table-column-select\n                                    :columns=\"columns\"\n                                    trigger=\"hover\"\n                                    :title=\"$t('commons.table.selectColumn')\"\n                                    popper-class=\"popper-class\"\n                                    :only-icon=\"true\"\n                                />\n                                <div class=\"w-80\">\n                                    <el-input\n                                        v-model=\"req.search\"\n                                        clearable\n                                        @clear=\"search()\"\n                                        @keydown.enter=\"search()\"\n                                        :placeholder=\"$t('file.search')\"\n                                    >\n                                        <template #prepend>\n                                            <el-checkbox v-model=\"req.containSub\">\n                                                {{ $t('file.sub') }}\n                                            </el-checkbox>\n                                        </template>\n                                        <template #append>\n                                            <el-button icon=\"Search\" @click=\"search\" round />\n                                        </template>\n                                    </el-input>\n                                </div>\n                            </div>\n                        </div>\n                    </template>\n                    <template #main>\n                        <ComplexTable\n                            :pagination-config=\"paginationConfig\"\n                            v-model:selects=\"selects\"\n                            :ref=\"(el) => setTableRef(item.id, el)\"\n                            :data=\"data\"\n                            @search=\"search\"\n                            @sort-change=\"changeSort\"\n                            @cell-mouse-enter=\"showFavorite\"\n                            @cell-mouse-leave=\"hideFavorite\"\n                            :heightDiff=\"heightDiff\"\n                            :right-buttons=\"rightButtons\"\n                            :columns=\"columns\"\n                            localKey=\"fileManagementColumn\"\n                        >\n                            <el-table-column type=\"selection\" width=\"30\" />\n                            <el-table-column\n                                :label=\"$t('commons.table.name')\"\n                                min-width=\"250\"\n                                fix\n                                show-overflow-tooltip\n                                :sortable=\"'custom'\"\n                                prop=\"name\"\n                                :tooltip-options=\"{\n                                    placement: 'bottom-start',\n                                }\"\n                            >\n                                <template #default=\"{ row }\">\n                                    <div class=\"file-row\">\n                                        <div>\n                                            <svg-icon\n                                                v-if=\"row.isDir\"\n                                                className=\"table-icon\"\n                                                iconName=\"p-file-folder\"\n                                            ></svg-icon>\n                                            <svg-icon\n                                                v-else\n                                                className=\"table-icon\"\n                                                :iconName=\"getIconName(row.name, row.extension)\"\n                                            ></svg-icon>\n                                        </div>\n                                        <div class=\"file-name\">\n                                            <el-input\n                                                v-if=\"fileRename.oldName === row.name && isEdit\"\n                                                v-model.trim=\"fileRename.newName\"\n                                                :ref=\"(el) => setRenameRef(item.id, el)\"\n                                                :autofocus=\"isEdit\"\n                                                class=\"table-link table-input\"\n                                                placeholder=\"file name\"\n                                                @keydown.enter=\"handleRename(row)\"\n                                                @blur=\"onRenameBlur($event, row)\"\n                                            />\n                                            <span v-else class=\"table-link\" @click=\"open(row)\" type=\"primary\">\n                                                {{ row.name }}\n                                            </span>\n                                            <span v-if=\"row.isSymlink\">-> {{ row.linkPath }}</span>\n                                        </div>\n                                        <div>\n                                            <el-button\n                                                v-if=\"row.favoriteID > 0\"\n                                                link\n                                                type=\"warning\"\n                                                size=\"large\"\n                                                icon=\"StarFilled\"\n                                                @click=\"remove(row.favoriteID)\"\n                                            ></el-button>\n                                            <div v-else>\n                                                <el-button\n                                                    v-if=\"hoveredRowPath === row.path\"\n                                                    link\n                                                    icon=\"Star\"\n                                                    @click=\"addToFavorite(row)\"\n                                                ></el-button>\n                                            </div>\n                                        </div>\n                                    </div>\n                                </template>\n                            </el-table-column>\n                            <el-table-column :label=\"$t('file.mode')\" prop=\"mode\" width=\"80\">\n                                <template #default=\"{ row }\">\n                                    <el-link underline=\"never\" @click=\"openMode(row)\">{{ row.mode }}</el-link>\n                                </template>\n                            </el-table-column>\n                            <el-table-column\n                                :label=\"`${$t('commons.table.user')} / ${$t('file.group')}`\"\n                                prop=\"user\"\n                                show-overflow-tooltip\n                                width=\"200\"\n                            >\n                                <template #default=\"{ row }\">\n                                    <el-link underline=\"never\" @click=\"openChown(row)\">\n                                        {{ row.user ? row.user : '-' }} ({{ row.uid }}) /\n                                        {{ row.group ? row.group : '-' }} ({{ row.gid }})\n                                    </el-link>\n                                </template>\n                            </el-table-column>\n                            <el-table-column :label=\"$t('file.size')\" prop=\"size\" width=\"120\" :sortable=\"'custom'\">\n                                <template #default=\"{ row }\">\n                                    <el-button\n                                        type=\"primary\"\n                                        link\n                                        small\n                                        :loading=\"row.btnLoading\"\n                                        @click=\"row.isDir ? getDirSize(row.path) : getFileSize(row.path)\"\n                                    >\n                                        <span v-if=\"row.isDir\">\n                                            <span v-if=\"row.dirSize === undefined\">\n                                                {{ $t('file.calculate') }}\n                                            </span>\n                                            <span v-else>{{ formatFileSize(row.dirSize) }}</span>\n                                        </span>\n                                        <span v-else>\n                                            {{ formatFileSize(row.size) }}\n                                        </span>\n                                    </el-button>\n                                </template>\n                            </el-table-column>\n                            <el-table-column\n                                :label=\"$t('file.updateTime')\"\n                                prop=\"modTime\"\n                                width=\"180\"\n                                :formatter=\"dateFormat\"\n                                show-overflow-tooltip\n                                :sortable=\"'custom'\"\n                            ></el-table-column>\n                            <el-table-column :label=\"$t('file.remark')\" prop=\"remark\" width=\"180\" show-overflow-tooltip>\n                                <template #default=\"{ row }\">\n                                    <span>{{ row.remark ? row.remark : '-' }}</span>\n                                </template>\n                            </el-table-column>\n                            <fu-table-operations\n                                :max-height=\"dropdownMaxHeight\"\n                                :ellipsis=\"mobile ? 0 : 2\"\n                                :buttons=\"tableMoreButtons\"\n                                :label=\"$t('commons.table.operate')\"\n                                :min-width=\"mobile ? 'auto' : 200\"\n                                :fixed=\"mobile ? false : 'right'\"\n                                width=\"200\"\n                                fix\n                            />\n                            <template #paginationLeft>\n                                <div class=\"flex justify-start items-center\">\n                                    <el-text small>\n                                        {{ $t('file.fileDirNum', [dirNum, fileNum]) }}\n                                    </el-text>\n                                    <el-text small>\n                                        {{ $t('file.currentDir') + $t('file.size') + ' ' }}\n                                    </el-text>\n                                    <el-button type=\"primary\" link small :loading=\"calculateBtn\">\n                                        <span v-if=\"dirTotalSize == -1\" @click=\"getDirTotalSize(req.path)\">\n                                            {{ $t('file.calculate') }}\n                                        </span>\n                                        <span v-else>\n                                            {{ formatFileSize(dirTotalSize) }}\n                                        </span>\n                                    </el-button>\n                                </div>\n                            </template>\n                        </ComplexTable>\n                    </template>\n                </LayoutContent>\n            </el-tab-pane>\n            <el-tab-pane :name=\"editableTabsKey\" :closable=\"false\" :disabled=\"editableTabs.length > 6\">\n                <template #label>\n                    <el-icon @click=\"addTab()\"><Plus /></el-icon>\n                </template>\n            </el-tab-pane>\n        </el-tabs>\n\n        <CreateFile ref=\"createRef\" @close=\"search\" />\n        <ChangeRole ref=\"roleRef\" @close=\"search\" />\n        <Compress ref=\"compressRef\" @close=\"search\" />\n        <Decompress ref=\"deCompressRef\" @close=\"search\" />\n        <CodeEditor ref=\"codeEditorRef\" @close=\"search\" />\n        <FileRename ref=\"renameRef\" @close=\"search\" />\n        <Upload ref=\"uploadRef\" @close=\"search\" />\n        <Wget ref=\"wgetRef\" @close=\"closeWget\" />\n        <Move ref=\"moveRef\" @close=\"closeMovePage\" @loading=\"onLoading\" />\n        <Download ref=\"downloadRef\" @close=\"search\" />\n        <Process ref=\"processRef\" @close=\"closeProcess\" />\n        <Owner ref=\"chownRef\" @close=\"search\"></Owner>\n        <Detail ref=\"detailRef\" />\n        <DeleteFile ref=\"deleteRef\" @close=\"search\" />\n        <RecycleBin ref=\"recycleBinRef\" @close=\"search\" />\n        <Favorite ref=\"favoriteRef\" @close=\"search\" @jump=\"jump\" @to-favorite=\"toFavorite\" />\n        <BatchRole ref=\"batchRoleRef\" @close=\"search\" />\n        <VscodeOpenDialog ref=\"dialogVscodeOpenRef\" />\n        <Preview ref=\"previewRef\" />\n        <TextPreview ref=\"textPreviewRef\" />\n        <TerminalDialog ref=\"dialogTerminalRef\" />\n        <Convert ref=\"convertRef\" @close=\"search\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, reactive, ref } from 'vue';\nimport {\n    addFavorite,\n    batchGetFileRemarks,\n    computeDepthDirSize,\n    computeDirSize,\n    fileWgetKeys,\n    getFileContent,\n    getFilesList,\n    setFileRemark,\n    removeFavorite,\n    renameRile,\n    searchFavorite,\n    searchHostMount,\n} from '@/api/modules/files';\nimport {\n    computeSize,\n    copyText,\n    dateFormat,\n    downloadFile,\n    getFileType,\n    getIcon,\n    getRandomStr,\n    isConvertible,\n} from '@/utils/util';\nimport { File } from '@/api/interface/file';\nimport { Languages, Mimetypes } from '@/global/mimetype';\nimport { useRouter } from 'vue-router';\nimport { MsgSuccess, MsgWarning } from '@/utils/message';\nimport { useMultipleSearchable } from './hooks/searchable';\nimport { ResultData } from '@/api/interface';\nimport { GlobalStore } from '@/store';\nimport { Download as ElDownload, Upload as ElUpload, View, Hide } from '@element-plus/icons-vue';\n\nimport i18n from '@/lang';\nimport CreateFile from './create/index.vue';\nimport ChangeRole from './change-role/index.vue';\nimport Compress from './compress/index.vue';\nimport Decompress from './decompress/index.vue';\nimport Upload from './upload/index.vue';\nimport FileRename from './rename/index.vue';\nimport CodeEditor from './code-editor/index.vue';\nimport Wget from './wget/index.vue';\nimport Move from './move/index.vue';\nimport Download from './download/index.vue';\nimport Owner from './chown/index.vue';\nimport DeleteFile from './delete/index.vue';\nimport Process from './process/index.vue';\nimport Detail from './detail/index.vue';\nimport RecycleBin from './recycle-bin/index.vue';\nimport Favorite from './favorite/index.vue';\nimport BatchRole from './batch-role/index.vue';\nimport Preview from './preview/index.vue';\nimport TextPreview from './text-preview/index.vue';\nimport VscodeOpenDialog from '@/components/vscode-open/index.vue';\nimport Convert from './convert/index.vue';\nimport { debounce } from 'lodash-es';\nimport TerminalDialog from './terminal/index.vue';\nimport { Dashboard } from '@/api/interface/dashboard';\nimport { CompressExtension, MimetypeByExtensionObject } from '@/enums/files';\nimport type { TabPaneName } from 'element-plus';\nimport { getComponentInfo } from '@/api/modules/host';\nimport { routerToNameWithQuery } from '@/utils/router';\nimport { loadBaseDir } from '@/api/modules/setting';\n\nconst globalStore = GlobalStore();\n\ninterface FilePaths {\n    url: string;\n    name: string;\n}\n\nconst router = useRouter();\nconst data = ref();\nconst tableRefs = ref<Record<string, any>>({});\nconst heightDiff = ref(365);\nconst fileTableRef = ref<HTMLElement | null>(null);\nconst dropdownMaxHeight = ref(450);\nconst baseDir = ref();\nconst remarkRequestId = ref(0);\nconst remarkLoadTimer = ref<number | null>(null);\nconst editableTabsKey = ref('');\nconst editableTabs = ref([\n    { id: '1', name: getLastPath(baseDir.value), path: baseDir.value },\n    { id: '2', name: 'home', path: '/home' },\n]);\n\nconst setTableRef = (key: string, el: any) => {\n    if (el) {\n        tableRefs.value[key] = el;\n    }\n};\nconst getCurrentTable = () => tableRefs.value[editableTabsKey.value];\n\nlet selects = ref<any>([]);\n\nconst initData = () => ({\n    path: '/',\n    expand: true,\n    showHidden: localStorage.getItem('show-hidden') === 'true',\n    page: 1,\n    pageSize: 100,\n    search: '',\n    containSub: false,\n    sortBy: 'name',\n    sortOrder: 'ascending',\n});\nlet req = reactive(initData());\nlet loading = ref(false);\nconst paths = ref<FilePaths[]>([]);\nconst hidePaths = ref<FilePaths[]>([]);\nlet pathWidth = ref(0);\nconst history: string[] = [];\nlet pointer = -1;\n\nconst fileCreate = reactive({ path: '/', isDir: false, mode: 0o755 });\nconst fileCompress = reactive({ files: [''], name: '', dst: '', operate: 'compress' });\nconst fileDeCompress = reactive({ path: '', name: '', dst: '', type: '' });\nconst fileEdit = reactive({ content: '', path: '', name: '', language: 'plaintext', extension: '' });\nconst filePreview = reactive({ path: '', name: '', extension: '', fileType: '', imageFiles: [], currentNode: '' });\nconst codeReq = reactive({ path: '', expand: false, page: 1, pageSize: 100, isDetail: false });\nconst fileUpload = reactive({ path: '' });\nconst fileRename = reactive({ path: '', oldName: '', newName: '' });\nconst fileWget = reactive({ path: '' });\nconst fileMove = reactive({ oldPaths: [''], allNames: [''], type: '', path: '', name: '', count: 0, isDir: false });\nconst fileConvert = reactive<{\n    outputPath: string;\n    files: File.ConvertFile[];\n}>({\n    outputPath: '',\n    files: [\n        {\n            type: '',\n            inputFile: '',\n            extension: '',\n            path: '',\n            outputFormat: '',\n        },\n    ],\n});\nconst ffmpegExist = ref(false);\n\nconst createRef = ref();\nconst roleRef = ref();\nconst detailRef = ref();\nconst compressRef = ref();\nconst deCompressRef = ref();\nconst codeEditorRef = ref();\nconst renameRef = ref();\nconst uploadRef = ref();\nconst wgetRef = ref();\nconst moveRef = ref();\nconst downloadRef = ref();\nconst toolRef = ref();\nconst breadCrumbRef = ref();\nconst chownRef = ref();\nconst moveOpen = ref(false);\nconst deleteRef = ref();\nconst recycleBinRef = ref();\nconst favoriteRef = ref();\nconst hoveredRowPath = ref(null);\nconst favorites = ref([]);\nconst batchRoleRef = ref();\nconst dialogVscodeOpenRef = ref();\nconst previewRef = ref();\nconst textPreviewRef = ref();\nconst processRef = ref();\n\nconst MAX_OPEN_SIZE = 10 * 1024 * 1024;\nconst hostMount = ref<Dashboard.DiskInfo[]>([]);\nlet resizeObserver: ResizeObserver;\nconst dirTotalSize = ref(-1);\nconst disableBtn = ref(false);\nconst calculateBtn = ref(false);\nconst dirNum = ref(0);\nconst fileNum = ref(0);\nconst imageFiles = ref([]);\nconst isEdit = ref(false);\nconst convertRef = ref();\n\nconst renameRefs = ref<Record<string, any>>({});\n\nconst setRenameRef = (key: string, el: any) => {\n    if (el) {\n        renameRefs.value[key] = el;\n    }\n};\nconst getCurrentRename = () => renameRefs.value[editableTabsKey.value];\n\nconst pathRefs = ref<Record<string, any>>({});\nconst columns = ref([]);\n\nconst setPathRef = (key: string, el: any) => {\n    if (el) {\n        pathRefs.value[key] = el;\n    }\n};\nconst getCurrentPath = () => pathRefs.value[editableTabsKey.value];\n\nconst { searchableStatus, searchablePath, setSearchableInputRef, searchableInputBlur } = useMultipleSearchable(paths);\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'file-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('file-page-size')) || 100,\n    total: 0,\n});\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst search = async () => {\n    dirTotalSize.value = -1;\n    await getWgetProcess();\n    loading.value = true;\n    if (req.search != '') {\n        req.sortBy = 'name';\n        req.sortOrder = 'ascending';\n        getCurrentTable().clearSort();\n    }\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    await getFilesList(req)\n        .then((res) => {\n            handleSearchResult(res);\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst searchFile = async () => {\n    loading.value = true;\n    dirTotalSize.value = -1;\n    try {\n        return await getFilesList(req);\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst handleSearchResult = (res: ResultData<File.File>) => {\n    data.value = res.data.items || [];\n    paginationConfig.total = res.data.itemTotal;\n    dirNum.value = data.value.filter((item) => item.isDir).length;\n    fileNum.value = data.value.filter((item) => !item.isDir).length;\n    req.path = res.data.path;\n    scheduleRemarkLoad();\n};\n\nconst viewHideFile = async () => {\n    req.showHidden = !req.showHidden;\n    localStorage.setItem('show-hidden', req.showHidden ? 'true' : 'false');\n    let searchResult = await searchFile();\n    handleSearchResult(searchResult);\n};\n\nconst open = async (row: File.File) => {\n    hideRightMenu();\n    calculateBtn.value = false;\n    disableBtn.value = false;\n    if (row.isDir) {\n        if (row.name.indexOf('.1panel_clash') > -1) {\n            MsgWarning(i18n.global.t('file.clashOpenAlert'));\n            return;\n        }\n        const name = row.name;\n        if (req.path.endsWith('/')) {\n            req.path = req.path + name;\n        } else {\n            req.path = req.path + '/' + name;\n        }\n        paths.value.push({\n            url: req.path,\n            name: name,\n        });\n        await jump(req.path);\n    } else {\n        openView(row);\n    }\n};\n\nconst copyDir = (row: File.File) => {\n    if (row?.path) {\n        copyText(row?.path);\n    }\n};\n\nconst leftWrapper = ref<HTMLElement | null>(null);\nconst btnWrapperRefs = ref<Record<string, any>>({});\n\nconst setBtnWrapperRef = (key: string, el: any) => {\n    if (el) {\n        btnWrapperRefs.value[key] = el;\n    }\n};\nconst getCurrentBtnWrapper = () => btnWrapperRefs.value[editableTabsKey.value];\n\nconst toolButtons = ref([\n    {\n        label: 'commons.button.copy',\n        action: () => openMove('copy'),\n    },\n    {\n        label: 'file.move',\n        action: () => openMove('cut'),\n    },\n    {\n        label: 'file.compress',\n        action: () => openCompress(selects.value),\n    },\n    {\n        label: 'file.role',\n        action: () => openBatchRole(selects.value),\n    },\n    {\n        label: 'commons.button.delete',\n        action: () => batchDelFiles(),\n    },\n]);\n\nconst visibleButtons = ref([...toolButtons.value]);\nconst moreButtons = ref([]);\n\nconst updateButtons = async () => {\n    await nextTick();\n    if (!getCurrentBtnWrapper()) return;\n    const pathWidth = toolRef.value.offsetWidth;\n    const leftWidth = leftWrapper.value.offsetWidth;\n    let num = Math.floor((pathWidth - leftWidth - 450) / 100);\n    if (num < 0) {\n        visibleButtons.value = toolButtons.value;\n        moreButtons.value = [];\n    } else {\n        visibleButtons.value = toolButtons.value.slice(0, num);\n        moreButtons.value = toolButtons.value.slice(num);\n    }\n};\n\nconst handlePath = (depth = 0) => {\n    if (depth > 10) return;\n    nextTick(function () {\n        let breadCrumbWidth = breadCrumbRef.value?.offsetWidth;\n        let pathWidth = toolRef.value?.offsetWidth;\n        if (pathWidth - breadCrumbWidth < 50 && paths.value.length > 1) {\n            const removed = paths.value.shift();\n            if (removed) hidePaths.value.push(removed);\n            handlePath(depth + 1);\n        }\n    });\n};\n\nconst resizeHandler = debounce(() => {\n    resetPaths();\n    handlePath();\n}, 100);\n\nconst btnResizeHandler = debounce(() => {\n    updateButtons();\n}, 100);\n\nconst observeResize = () => {\n    const el = getCurrentPath();\n    const ele = getCurrentBtnWrapper();\n    if (!el || !ele) return;\n\n    const observe = new ResizeObserver((entries) => {\n        const isElChanged = entries.some((entry) => entry.target === el);\n        const isEleChanged = entries.some((entry) => entry.target === ele);\n\n        if (isElChanged) resizeHandler();\n        if (isEleChanged) btnResizeHandler();\n        updateHeight();\n    });\n\n    observe.observe(el);\n    observe.observe(ele);\n\n    resizeObserver = observe;\n};\n\nfunction watchTitleHeight() {\n    const el = document.querySelector<HTMLElement>('.content-container__title');\n    if (el) {\n        let titleHeight = el.offsetHeight < 40 ? 40 : 80;\n        heightDiff.value = 325 + titleHeight;\n    }\n}\n\nconst resetPaths = () => {\n    paths.value = [...hidePaths.value, ...paths.value];\n    hidePaths.value = [];\n};\n\nconst right = () => {\n    if (pointer < history.length - 1) {\n        pointer++;\n        let url = history[pointer];\n        backForwardJump(url);\n    }\n};\n\nconst back = () => {\n    if (pointer > 0) {\n        pointer--;\n        let url = history[pointer];\n        backForwardJump(url);\n    }\n};\n\nconst top = () => {\n    if (paths.value.length > 0) {\n        let url = '/';\n        if (paths.value.length >= 2) {\n            url = paths.value[paths.value.length - 2].url;\n        }\n        jump(url);\n    }\n};\n\nconst jump = async (url: string) => {\n    hideRightMenu();\n    history.splice(pointer + 1);\n    history.push(url);\n    pointer = history.length - 1;\n\n    const { path: oldUrl, pageSize: oldPageSize } = req;\n    Object.assign(req, initData(), { path: url, containSub: false, search: '', pageSize: oldPageSize });\n    let searchResult = await searchFile();\n    if (!searchResult.data.path) {\n        req.path = oldUrl;\n        globalStore.setLastFilePath(req.path);\n        MsgWarning(i18n.global.t('commons.res.notFound'));\n        return;\n    }\n    req.path = searchResult.data.path;\n    globalStore.setLastFilePath(req.path);\n    handleSearchResult(searchResult);\n    getPaths(req.path);\n    updateTab(req.path);\n    await nextTick(function () {\n        handlePath();\n    });\n};\n\nconst backForwardJump = async (url: string) => {\n    const oldPageSize = req.pageSize;\n    Object.assign(req, initData());\n    req.path = url;\n    req.containSub = false;\n    req.search = '';\n    req.pageSize = oldPageSize;\n    let searchResult = await searchFile();\n    handleSearchResult(searchResult);\n    getPaths(req.path);\n    updateTab(req.path);\n    await nextTick(function () {\n        handlePath();\n    });\n};\n\nconst getPaths = (reqPath: string | undefined | null) => {\n    const pathString = reqPath || '';\n    const pathArray = pathString.split('/').filter((p) => p !== '');\n\n    const breadcrumbs = [];\n    let base = '';\n\n    for (const p of pathArray) {\n        base = `${base}/${p}`;\n        breadcrumbs.push({\n            url: base,\n            name: p,\n        });\n    }\n\n    paths.value = breadcrumbs;\n    hidePaths.value = [];\n};\n\nconst handleCreate = (command: string) => {\n    fileCreate.path = req.path;\n    fileCreate.isDir = command === 'dir';\n    createRef.value.acceptParams(fileCreate);\n};\n\nconst delFile = async (row: File.File | null) => {\n    deleteRef.value.acceptParams([row]);\n};\n\nconst batchDelFiles = () => {\n    deleteRef.value.acceptParams(selects.value);\n};\n\nconst formatFileSize = (size: number) => {\n    return computeSize(size);\n};\n\nconst getFileSize = async (path: string) => {\n    codeReq.path = path;\n    codeReq.expand = true;\n    codeReq.isDetail = true;\n    updateByPath(path, { btnLoading: true });\n    try {\n        const res = await getFileContent(codeReq);\n        updateByPath(path, { dirSize: res.data.size });\n    } finally {\n        updateByPath(path, { btnLoading: false });\n    }\n};\n\nconst getDirSize = async (path: string) => {\n    const req = {\n        path: path,\n    };\n    updateByPath(path, { btnLoading: true });\n    try {\n        const res = await computeDirSize(req);\n        updateByPath(path, { dirSize: res.data.size });\n    } finally {\n        updateByPath(path, { btnLoading: false });\n    }\n};\n\nconst updateByPath = (path: string, patch: Partial<(typeof data.value)[0]>) => {\n    data.value = data.value.map((item) => (item.path === path ? { ...item, ...patch } : item));\n};\n\nconst getDirTotalSize = async (path: string) => {\n    const req = {\n        path: path,\n    };\n    calculateBtn.value = true;\n    const res = await computeDirSize(req);\n    dirTotalSize.value = res.data.size;\n    calculateBtn.value = false;\n};\n\nconst calculateSize = (path: string) => {\n    const req = { path };\n    disableBtn.value = true;\n    setTimeout(async () => {\n        try {\n            const res = await computeDepthDirSize(req);\n            const sizeMap = new Map(res.data.map((dir) => [dir.path, dir.size]));\n            data.value.forEach((item) => {\n                if (sizeMap.has(item.path)) {\n                    item.dirSize = sizeMap.get(item.path)!;\n                }\n            });\n        } catch (err) {\n            console.error('Error computing dir size:', err);\n        } finally {\n            disableBtn.value = false;\n        }\n    }, 0);\n};\n\nconst getIconName = (name: string, extension: string) => {\n    return getIcon(getFileExtension(name, extension));\n};\n\nconst openMode = (item: File.File) => {\n    roleRef.value.acceptParams(item);\n};\n\nconst openChown = (item: File.File) => {\n    chownRef.value.acceptParams(item);\n};\n\nconst openCompress = (items: File.File[]) => {\n    const paths = [];\n    for (const item of items) {\n        paths.push(item.path);\n    }\n    fileCompress.files = paths;\n    if (paths.length === 1) {\n        fileCompress.name = items[0].name;\n    } else {\n        fileCompress.name = getRandomStr(6);\n    }\n    fileCompress.dst = req.path;\n\n    compressRef.value.acceptParams(fileCompress);\n};\n\nconst openDeCompress = (item: File.File) => {\n    const extension = getFileExtension(item.name, item.extension);\n    const mimeType = item.mimeType || MimetypeByExtensionObject[extension];\n    const typeByMime = mimeType ? Mimetypes.get(mimeType) : undefined;\n    const typeByExtension = getEnumKeyByValue(extension);\n\n    if (typeByMime && (!typeByExtension || CompressExtension[typeByMime] === extension)) {\n        fileDeCompress.type = typeByMime;\n    } else if (typeByExtension) {\n        fileDeCompress.type = typeByExtension;\n    } else {\n        MsgWarning(i18n.global.t('file.canNotDeCompress'));\n        return;\n    }\n\n    fileDeCompress.name = item.name;\n    fileDeCompress.path = item.path;\n    fileDeCompress.dst = req.path;\n\n    deCompressRef.value.acceptParams(fileDeCompress);\n};\n\nfunction getEnumKeyByValue(value: string): keyof typeof CompressExtension | undefined {\n    const normalizedValue = value.toLowerCase();\n    return (Object.keys(CompressExtension) as Array<keyof typeof CompressExtension>).find(\n        (k) => CompressExtension[k] === normalizedValue,\n    );\n}\n\nconst sortedCompressExtensions = Object.values(CompressExtension).sort((a, b) => b.length - a.length);\n\nconst getFileExtension = (name: string, extension?: string): string => {\n    const lowerName = name?.toLowerCase().split('?')[0] ?? '';\n    if (lowerName.startsWith('.') && lowerName.indexOf('.', 1) === -1) {\n        return extension.toLowerCase();\n    }\n    const compoundMatch = sortedCompressExtensions.find((compressExtension) => lowerName.endsWith(compressExtension));\n    if (compoundMatch) {\n        return compoundMatch;\n    }\n\n    if (extension) {\n        const lowerExt = extension.toLowerCase();\n        return lowerExt.startsWith('.') ? lowerExt : `.${lowerExt}`;\n    }\n\n    const extensionIndex = lowerName.lastIndexOf('.');\n    return extensionIndex === -1 ? '' : lowerName.slice(extensionIndex);\n};\n\nconst openView = (item: File.File) => {\n    const fileType = getFileType(item.extension);\n    if (fileType === 'image') {\n        imageFiles.value = data.value\n            .filter((item) => !item.isDir)\n            .filter((item) => getFileType(item.extension) == 'image')\n            .map((item) => (item.isSymlink ? item.linkPath : item.path));\n    }\n\n    const previewTypes = ['image', 'video', 'audio', 'word', 'excel'];\n    if (previewTypes.includes(fileType)) {\n        return openPreview(item, fileType);\n    }\n\n    if (fileType === 'compress') {\n        return openDeCompress(item);\n    }\n\n    const path = item.isSymlink ? item.linkPath : item.path;\n    if (item.size > MAX_OPEN_SIZE) {\n        return openTextPreview(path, item.name);\n    }\n\n    const actionMap = {\n        text: () => openCodeEditor(path, item.extension),\n    };\n\n    return actionMap[fileType] ? actionMap[fileType](item) : openCodeEditor(path, item.extension);\n};\n\nconst openPreview = (item: File.File, fileType: string) => {\n    if (item.mode.toString() == '-' && item.user == '-' && item.group == '-') {\n        MsgWarning(i18n.global.t('file.fileCanNotRead'));\n        return;\n    }\n    filePreview.path = item.isSymlink ? item.linkPath : item.path;\n    filePreview.name = item.name;\n    filePreview.extension = item.extension;\n    filePreview.fileType = fileType;\n    filePreview.imageFiles = imageFiles.value;\n    filePreview.currentNode = globalStore.currentNode;\n\n    previewRef.value.acceptParams(filePreview);\n};\n\nconst openCodeEditor = (path: string, extension: string) => {\n    codeReq.path = path;\n    codeReq.expand = true;\n\n    if (extension != '') {\n        Languages.forEach((language) => {\n            const ext = extension.substring(1);\n            if (language.value.indexOf(ext) > -1) {\n                fileEdit.language = language.label;\n            }\n        });\n    }\n\n    getFileContent(codeReq)\n        .then((res) => {\n            fileEdit.content = res.data.content;\n            fileEdit.path = res.data.path;\n            fileEdit.name = res.data.name;\n            fileEdit.extension = res.data.extension;\n\n            codeEditorRef.value.acceptParams(fileEdit);\n        })\n        .catch(() => {});\n};\n\nconst openTextPreview = (path: string, name: string) => {\n    textPreviewRef.value.acceptParams({ path, name });\n};\n\nconst openUpload = () => {\n    fileUpload.path = req.path;\n    uploadRef.value.acceptParams(fileUpload);\n};\n\nconst openWget = () => {\n    fileWget.path = req.path;\n    wgetRef.value.acceptParams(fileWget);\n};\n\nconst openBatchRole = (items: File.File[]) => {\n    batchRoleRef.value.acceptParams({ files: items });\n};\n\nconst closeWget = (submit: Boolean) => {\n    search();\n    if (submit) {\n        openProcess();\n    }\n};\n\nconst closeMovePage = (submit: Boolean) => {\n    if (submit) {\n        search();\n        closeMove();\n    }\n};\n\nconst openProcess = () => {\n    processRef.value.acceptParams();\n};\n\nconst closeProcess = () => {\n    search();\n    getWgetProcess();\n    setTimeout(() => {\n        getWgetProcess();\n    }, 1000);\n};\n\nconst processCount = ref(0);\nconst getWgetProcess = async () => {\n    processCount.value = 0;\n    try {\n        const res = await fileWgetKeys();\n        if (res.data && res.data.keys.length > 0) {\n            processCount.value = res.data.keys.length;\n        }\n    } catch (error) {}\n};\n\nconst openRename = (item: File.File, source: String) => {\n    fileRename.path = req.path;\n    fileRename.oldName = item.name;\n    if (source === 'right') {\n        fileRename.newName = item.name;\n        isEdit.value = true;\n        nextTick(() => {\n            getCurrentRename().focus();\n        });\n        hideRightMenu();\n    } else {\n        renameRef.value.acceptParams(fileRename);\n    }\n};\n\nconst onRenameBlur = (e: FocusEvent, row: File.File) => {\n    const related = e.relatedTarget as HTMLElement | null;\n    if (\n        related &&\n        (related.closest('.fu-table-more-button') || related.closest('.fu-table-more-button .el-dropdown__item'))\n    ) {\n        setTimeout(() => {\n            getCurrentRename()?.focus();\n        }, 0);\n        return;\n    }\n    handleRename(row);\n};\n\nconst handleRename = async (row: File.File): Promise<void> => {\n    if (fileRename.newName === fileRename.oldName) {\n        isEdit.value = false;\n        fileRename.oldName = '';\n        return;\n    }\n    const addItem: File.FileRename = {\n        oldName: getPath(fileRename.path, fileRename.oldName),\n        newName: getPath(fileRename.path, fileRename.newName),\n    };\n    loading.value = true;\n    try {\n        await renameRile(addItem);\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        row.name = fileRename.newName;\n        row.path = getPath(req.path, fileRename.newName);\n    } catch (error) {\n        console.error(error);\n    } finally {\n        loading.value = false;\n        isEdit.value = false;\n        fileRename.oldName = '';\n    }\n};\n\nconst getPath = (path: string, name: string) => {\n    return path + '/' + name;\n};\n\nconst openMove = (type: string) => {\n    fileMove.type = type;\n    fileMove.name = '';\n    fileMove.allNames = [];\n    fileMove.isDir = false;\n    const oldPaths = [];\n    for (const s of selects.value) {\n        oldPaths.push(s['path']);\n    }\n    fileMove.count = selects.value.length;\n    fileMove.oldPaths = oldPaths;\n    if (selects.value.length == 1) {\n        fileMove.name = selects.value[0].name;\n        fileMove.isDir = selects.value[0].isDir;\n    } else {\n        const allNames = [];\n        for (const s of selects.value) {\n            allNames.push(s['name']);\n        }\n        fileMove.allNames = allNames;\n    }\n    moveOpen.value = true;\n    if (type === 'cut') {\n        MsgSuccess(i18n.global.t('file.moveSuccess') + '! ' + i18n.global.t('file.pasteMsg'));\n    } else {\n        MsgSuccess(i18n.global.t('file.copySuccess') + '! ' + i18n.global.t('file.pasteMsg'));\n    }\n};\n\nconst openMoveBtn = (type: string, item: File.File) => {\n    selects.value = [];\n    selects.value.push(item);\n    openMove(type);\n};\n\nconst closeMove = () => {\n    selects.value = [];\n    getCurrentTable().clearSelects();\n    hideRightMenu();\n    fileMove.oldPaths = [];\n    fileMove.name = '';\n    fileMove.count = 0;\n    fileMove.isDir = false;\n    moveOpen.value = false;\n};\n\nconst openPaste = () => {\n    fileMove.path = req.path;\n    moveRef.value.acceptParams(fileMove);\n};\n\nfunction onLoading(isLoading: boolean) {\n    loading.value = isLoading;\n}\n\nconst openDownload = (file: File.File) => {\n    downloadFile(file.path, globalStore.currentNode);\n};\n\nconst openDetail = (row: File.File) => {\n    detailRef.value.acceptParams({ path: row.path });\n};\n\nconst openRecycleBin = () => {\n    recycleBinRef.value.acceptParams();\n};\n\nconst openFavorite = () => {\n    favoriteRef.value.acceptParams();\n};\n\nconst changeSort = ({ prop, order }) => {\n    req.sortBy = prop;\n    req.sortOrder = order;\n    req.search = '';\n    req.page = 1;\n    req.pageSize = paginationConfig.pageSize;\n    req.containSub = false;\n    search();\n};\n\nconst showFavorite = (row: File.File) => {\n    hoveredRowPath.value = row.path;\n};\n\nconst hideFavorite = () => {\n    hoveredRowPath.value = null;\n};\n\nconst addToFavorite = async (row: File.File) => {\n    try {\n        await addFavorite(row.path);\n        await search();\n    } catch (error) {}\n};\n\nconst remove = async (id: number) => {\n    ElMessageBox.confirm(i18n.global.t('file.removeFavorite'), i18n.global.t('commons.msg.remove'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        try {\n            await removeFavorite(id);\n            await search();\n        } catch (error) {}\n    });\n};\n\nconst getFavorites = async () => {\n    try {\n        const res = await searchFavorite(req);\n        favorites.value = res.data.items;\n    } catch (error) {}\n};\n\nconst toFavorite = (row: File.Favorite) => {\n    if (row.isDir) {\n        jump(row.path);\n    } else {\n        let file = {} as File.File;\n        const extension = getFileExtension(row.name);\n        file.path = row.path;\n        file.name = row.name;\n        file.extension = extension;\n        file.mimeType = MimetypeByExtensionObject[extension] || '';\n        openView(file);\n    }\n};\n\nconst dialogTerminalRef = ref();\nconst toTerminal = () => {\n    dialogTerminalRef.value!.acceptParams({ cwd: req.path, command: '/bin/sh' });\n};\n\nconst openWithVSCode = (row: File.File) => {\n    dialogVscodeOpenRef.value.acceptParams({ path: row.path + (row.isDir ? '' : ':1:1') });\n};\n\nconst openRemark = async (row: File.File) => {\n    try {\n        const res = await ElMessageBox.prompt(i18n.global.t('file.remarkPrompt'), i18n.global.t('file.setRemark'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            inputValue: row.remark ?? '',\n            inputPlaceholder: i18n.global.t('file.remarkPlaceholder'),\n        });\n        const remark = res.value ?? '';\n        await setFileRemark({ path: row.path, remark: remark });\n        row.remark = remark;\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n    } catch (error) {\n        return;\n    }\n};\n\nconst beforeButtons = [\n    {\n        label: i18n.global.t('commons.button.open'),\n        click: open,\n        show: (row: File.File) => {\n            return row?.isDir || row?.size <= MAX_OPEN_SIZE || isDecompressFile(row);\n        },\n    },\n    {\n        label: i18n.global.t('file.previewLargeFile'),\n        click: (row: File.File) => {\n            const path = row.isSymlink ? row.linkPath : row.path;\n            openTextPreview(path, row.name);\n        },\n        show: (row: File.File) => {\n            return !row?.isDir && row?.size > MAX_OPEN_SIZE && !isDecompressFile(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.download'),\n        click: (row: File.File) => {\n            openDownload(row);\n        },\n        disabled: (row: File.File) => {\n            return row?.isDir;\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.copy'),\n        click: (row: File.File) => openMoveBtn('copy', row),\n    },\n    {\n        label: i18n.global.t('file.move'),\n        click: (row: File.File) => openMoveBtn('cut', row),\n    },\n    {\n        label: i18n.global.t('file.paste'),\n        click: openPaste,\n        disabled: () => {\n            return !moveOpen.value;\n        },\n    },\n    {\n        label: i18n.global.t('file.compress'),\n        click: (row: File.File) => {\n            openCompress([row]);\n        },\n    },\n    {\n        label: i18n.global.t('file.deCompress'),\n        click: openDeCompress,\n        disabled: (row: File.File) => {\n            return !isDecompressFile(row);\n        },\n    },\n    {\n        label: i18n.global.t('file.editPermissions'),\n        click: (row: File.File) => {\n            openBatchRole([row]);\n        },\n    },\n    {\n        label: i18n.global.t('file.setRemark'),\n        hideOnRemarkBlackList: true,\n        click: (row: File.File) => {\n            openRemark(row);\n        },\n    },\n];\nconst afterButtons = [\n    {\n        label: i18n.global.t('commons.button.delete'),\n        disabled: (row: File.File) => {\n            return row.name == '.1panel_clash';\n        },\n        click: delFile,\n        divided: true,\n    },\n    {\n        label: i18n.global.t('file.copyDir'),\n        click: copyDir,\n    },\n    {\n        label: i18n.global.t('file.addFavorite'),\n        click: (row: File.File) => {\n            if (row?.favoriteID > 0) {\n                remove(row?.favoriteID);\n            } else {\n                addToFavorite(row);\n            }\n        },\n    },\n    {\n        label: i18n.global.t('file.convert'),\n        click: (row: File.File) => {\n            openConvert(row);\n        },\n        disabled: (row: File.File) => {\n            return row?.isDir || !isConvertible(row?.extension, row?.mimeType);\n        },\n    },\n    {\n        label: i18n.global.t('file.openWithVscode'),\n        click: openWithVSCode,\n    },\n    {\n        label: i18n.global.t('file.info'),\n        click: openDetail,\n        divided: true,\n    },\n];\n\nconst rightBtnRename = [\n    {\n        label: i18n.global.t('file.rename'),\n        click: (row: File.File) => {\n            openRename(row, 'right');\n        },\n    },\n];\nconst moreBtnRename = [\n    {\n        label: i18n.global.t('file.rename'),\n        click: (row: File.File) => {\n            openRename(row, 'more');\n        },\n    },\n];\n\nconst filterRemarkButtons = (buttons: any[]) => {\n    if (!isInRemarkBlackList(req.path)) {\n        return buttons;\n    }\n    return buttons.filter((btn) => !btn.hideOnRemarkBlackList);\n};\n\nconst rightButtons = computed(() => filterRemarkButtons([...beforeButtons, ...rightBtnRename, ...afterButtons]));\nconst tableMoreButtons = computed(() => filterRemarkButtons([...beforeButtons, ...moreBtnRename, ...afterButtons]));\nconst openConvert = (item: File.File) => {\n    if (!ffmpegExist.value) {\n        ElMessageBox.confirm(i18n.global.t('cronjob.library.noSuchApp', ['FFmpeg']), i18n.global.t('file.convert'), {\n            confirmButtonText: i18n.global.t('app.toInstall'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        }).then(() => {\n            routerToNameWithQuery('Library', { t: Date.now(), uncached: 'true' });\n        });\n        return;\n    } else {\n        if (!isConvertible(item.extension, item.mimeType)) {\n            MsgWarning(i18n.global.t('file.fileCanNotConvert'));\n            return;\n        }\n        const fileType = getFileType(item.extension);\n        fileConvert.outputPath = req.path;\n        fileConvert.files = [\n            {\n                type: fileType,\n                path: req.path,\n                extension: item.extension,\n                inputFile: item.name,\n                outputFormat: item.extension.slice(1),\n            },\n        ];\n\n        convertRef.value.acceptParams(fileConvert);\n    }\n};\n\nconst isDecompressFile = (row: File.File) => {\n    if (row.isDir) {\n        return false;\n    }\n\n    const extension = getFileExtension(row.name, row.extension);\n    const mimeType = row.mimeType || MimetypeByExtensionObject[extension];\n\n    if (getFileType(extension) === 'compress') {\n        return true;\n    }\n\n    if (!mimeType || mimeType === 'application/octet-stream') {\n        return false;\n    }\n\n    return Mimetypes.get(mimeType) != undefined;\n};\n\nconst getHostMount = async () => {\n    try {\n        const res = await searchHostMount();\n        hostMount.value = res.data;\n    } catch (error) {\n        console.error('Error fetching host mount:', error);\n    }\n};\n\nconst handleDrop = async (event: DragEvent) => {\n    event.preventDefault();\n    fileUpload.path = req.path;\n    if (!uploadRef.value?.open) {\n        await uploadRef.value?.handleDrop(event);\n    }\n    uploadRef.value.acceptParams(fileUpload);\n};\n\nconst handleDragover = (event: DragEvent) => {\n    event.preventDefault();\n};\n\nconst handleDragleave = (event: { preventDefault: () => void }) => {\n    event.preventDefault();\n};\n\nfunction hideRightMenu() {\n    getCurrentTable().closeRightClick();\n}\n\nfunction initShowHidden() {\n    const showHidden = localStorage.getItem('show-hidden');\n    if (showHidden === null) {\n        localStorage.setItem('show-hidden', 'true');\n        req.showHidden = true;\n    } else {\n        req.showHidden = showHidden === 'true';\n    }\n}\n\nconst remarkBlackList = ['/proc', '/sys', '/dev', '/run'];\n\nconst scheduleRemarkLoad = () => {\n    if (remarkLoadTimer.value) {\n        window.clearTimeout(remarkLoadTimer.value);\n    }\n    if (isInRemarkBlackList(req.path)) {\n        return;\n    }\n    remarkLoadTimer.value = window.setTimeout(() => {\n        void loadRemarksForCurrentPage();\n    }, 1000);\n};\n\nconst isInRemarkBlackList = (path: string) => {\n    return remarkBlackList.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));\n};\n\nconst loadRemarksForCurrentPage = async () => {\n    if (!Array.isArray(data.value) || data.value.length === 0) return;\n    const paths = data.value.map((item) => item.path).filter(Boolean);\n    if (paths.length === 0) return;\n    const currentId = ++remarkRequestId.value;\n    try {\n        const res = await batchGetFileRemarks(paths);\n        if (currentId !== remarkRequestId.value) return;\n        const remarks = res.data?.remarks || {};\n        data.value.forEach((item) => {\n            const remark = remarks[item.path];\n            if (remark !== undefined && remark !== '') {\n                item.remark = remark;\n            }\n        });\n    } catch (error) {\n        if (currentId !== remarkRequestId.value) return;\n    }\n};\n\nfunction initTabsAndPaths() {\n    initTabs();\n    let path = getInitialPath();\n    req.path = path;\n    getPaths(path);\n    updateTab(path);\n    paths.value = buildPaths(path);\n    pathWidth.value = getCurrentPath()?.offsetWidth;\n}\n\nfunction buildPaths(path: string) {\n    return path\n        .split('/')\n        .filter(Boolean)\n        .reduce((accumulator, segment) => {\n            const lastPath = accumulator[accumulator.length - 1];\n            const currentUrl = lastPath ? `${lastPath.url}/${segment}` : `/${segment}`;\n            accumulator.push({\n                url: currentUrl,\n                name: segment,\n            });\n\n            return accumulator;\n        }, []);\n}\n\nfunction initHistory() {\n    search();\n    history.push(req.path);\n    pointer = history.length - 1;\n}\n\nfunction getInitialPath(): string {\n    const routePath = router.currentRoute.value.query.path;\n    if (routePath && typeof routePath === 'string') {\n        const p = routePath.trim();\n        if (p !== '') {\n            globalStore.setLastFilePath(p);\n            return p;\n        }\n    }\n    const tab = editableTabs.value.find((t) => t.id === editableTabsKey.value);\n    if (tab && typeof tab.path === 'string' && tab.path.trim() !== '') {\n        const p = tab.path.trim();\n        globalStore.setLastFilePath(p);\n        return p;\n    }\n    if (typeof globalStore.lastFilePath === 'string' && globalStore.lastFilePath.trim() !== '') {\n        return globalStore.lastFilePath;\n    }\n\n    return '/';\n}\n\nfunction initTabs() {\n    const savedTabs = localStorage.getItem('editableTabs');\n    if (savedTabs) {\n        editableTabs.value = JSON.parse(savedTabs);\n    }\n    const savedTabsKey = localStorage.getItem('editableTabsKey');\n    if (savedTabsKey) {\n        editableTabsKey.value = savedTabsKey;\n    } else {\n        setFirstTab();\n    }\n}\n\nfunction setFirstTab() {\n    if (editableTabs.value.length > 0) {\n        const first = editableTabs.value[0];\n        editableTabsKey.value = first.id;\n    } else {\n        initTabs();\n    }\n}\n\nfunction saveStorageTabs() {\n    localStorage.setItem('editableTabs', JSON.stringify(editableTabs.value));\n}\n\nfunction saveStorageTabsKey() {\n    localStorage.setItem('editableTabsKey', editableTabsKey.value);\n}\n\nfunction getLastPath(path: string): string {\n    if (!path) return '';\n    const parts = path.split('/').filter(Boolean);\n    return parts.length ? parts[parts.length - 1] : '';\n}\n\nfunction updateTab(newPath?: string) {\n    editableTabs.value = editableTabs.value.map((tab) => {\n        if (tab.id === editableTabsKey.value) {\n            return {\n                ...tab,\n                path: newPath,\n                name: getLastPath(newPath),\n            };\n        }\n        return tab;\n    });\n    saveStorageTabs();\n}\n\nconst loadPath = async () => {\n    const pathRes = await loadBaseDir();\n    baseDir.value = pathRes.data;\n};\n\nconst addTab = () => {\n    if (editableTabs.value.length >= 6) {\n        MsgWarning(i18n.global.t('file.notCanTab'));\n        return;\n    }\n    const usedIds = new Set(editableTabs.value.map((t) => Number(t.id)));\n    const newId = Array.from({ length: 6 }, (_, i) => i + 1).find((id) => !usedIds.has(id));\n\n    if (!newId) {\n        MsgWarning(i18n.global.t('file.notCanTab'));\n        return;\n    }\n    editableTabs.value.push({\n        id: String(newId),\n        name: getLastPath(baseDir.value),\n        path: baseDir.value,\n    });\n    editableTabsKey.value = String(newId);\n    changeTab(newId);\n};\n\nconst changeTab = (targetPath: TabPaneName) => {\n    if (targetPath === 99) {\n        return;\n    }\n    const current = editableTabs.value.find((tab) => tab.id === targetPath.toString());\n    editableTabsKey.value = current.id;\n    saveStorageTabs();\n    saveStorageTabsKey();\n    req.path = current ? current.path : '';\n    globalStore.setLastFilePath(req.path);\n    getPaths(req.path);\n    search();\n};\n\nconst removeTab = (targetId: TabPaneName) => {\n    const tabs = editableTabs.value;\n    if (tabs.length <= 1) {\n        MsgWarning(i18n.global.t('file.keepOneTab'));\n        return;\n    }\n    const target = String(targetId);\n    const current = String(editableTabsKey.value);\n    const idx = tabs.findIndex((t) => String(t.id) === target);\n    if (idx === -1) return;\n    let nextActive = current;\n    if (current === target) {\n        nextActive = tabs[idx + 1]?.id ?? tabs[idx - 1]?.id ?? current;\n    }\n    editableTabs.value = tabs.filter((t) => String(t.id) !== target);\n    editableTabsKey.value = String(nextActive);\n    changeTab(nextActive);\n};\n\nconst checkFFmpeg = () => {\n    getComponentInfo('ffmpeg', globalStore.currentNode).then((res) => {\n        ffmpegExist.value = res.data.exists ?? false;\n    });\n};\n\nconst updateHeight = () => {\n    const el = fileTableRef.value;\n    if (!el) return;\n    let tabHeight = globalStore.openMenuTabs ? 40 : -4;\n    const half = (el.offsetHeight + tabHeight) / 2;\n    dropdownMaxHeight.value = Math.max(half, 300);\n};\n\nonMounted(async () => {\n    await loadPath();\n    await nextTick();\n    watchTitleHeight();\n    window.addEventListener('resize', watchTitleHeight);\n    updateHeight();\n    window.addEventListener('resize', updateHeight);\n    initShowHidden();\n    initTabsAndPaths();\n    await getHostMount();\n    initHistory();\n    checkFFmpeg();\n    await nextTick(function () {\n        handlePath();\n        observeResize();\n    });\n});\n\nonBeforeUnmount(() => {\n    if (resizeObserver) resizeObserver.disconnect();\n    window.removeEventListener('resize', watchTitleHeight);\n    window.removeEventListener('resize', updateHeight);\n    if (remarkLoadTimer.value) {\n        window.clearTimeout(remarkLoadTimer.value);\n    }\n});\n</script>\n\n<style scoped lang=\"scss\">\n.path {\n    display: flex;\n    align-items: center;\n    border: 1px solid #ebeef5;\n    background-color: var(--panel-path-bg);\n    height: 30px;\n    border-radius: 2px !important;\n    &:hover {\n        cursor: text;\n        box-shadow: var(--el-box-shadow);\n    }\n\n    .root {\n        vertical-align: middle;\n        margin-left: 10px;\n    }\n    .other {\n        vertical-align: middle;\n    }\n    .split {\n        margin-left: 5px;\n        margin-right: 5px;\n    }\n}\n\n.copy-button {\n    .close {\n        width: 10px;\n        .close-icon {\n            color: red;\n        }\n    }\n}\n\n.btn-container {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    width: 100%;\n}\n\n.favorite-item {\n    height: 30vh;\n    overflow: auto;\n}\n\n.file-row {\n    display: flex;\n    align-items: center;\n    width: 100%;\n}\n\n.file-name {\n    flex-grow: 1;\n    margin-left: 1px;\n    width: 95%;\n    overflow: hidden;\n    white-space: nowrap;\n    text-overflow: ellipsis;\n}\n.address-bar {\n    border: var(--el-border);\n    .arrow {\n        color: #726e6e;\n    }\n}\n.search-button {\n    width: 20vw;\n}\n.el-button-group > .el-dropdown > .el-button {\n    border-left-color: var(--el-border-color);\n}\n.table-input {\n    --el-input-inner-height: 22px !important;\n}\n:deep(.file-tabs .el-tabs__nav .el-tabs__item:last-child) {\n    border-bottom: 1px solid var(--el-border-color-light) !important;\n}\n\n:deep(.file-tabs .el-tabs--card > .el-tabs__header .el-tabs__item.is-active) {\n    border-bottom-width: 1px !important;\n}\n:deep(.file-tabs .el-tabs--card .el-tabs__header .el-tabs__nav) {\n    border-bottom: none !important;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/move/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"title\" @close=\"handleClose\" size=\"690\">\n        <el-form\n            @submit.prevent\n            ref=\"fileForm\"\n            label-position=\"top\"\n            :model=\"addForm\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-alert\n                v-if=\"type == 'cut' && existFiles?.length == 0 && addForm.cover && changeName\"\n                show-icon\n                type=\"warning\"\n                :closable=\"false\"\n            >\n                <template #title>\n                    <span class=\"whitespace-break-spaces\">{{ $t('file.coverDirHelper') }}</span>\n                </template>\n            </el-alert>\n            <el-form-item :label=\"$t('file.path')\" prop=\"newPath\">\n                <el-input v-model=\"addForm.newPath\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: true })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <div v-if=\"changeName\">\n                <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                    <el-input v-model=\"addForm.name\" :disabled=\"addForm.cover\"></el-input>\n                </el-form-item>\n                <el-radio-group v-model=\"addForm.cover\" @change=\"changeType\">\n                    <el-radio :value=\"true\" size=\"large\">{{ $t('file.replace') }}</el-radio>\n                    <el-radio :value=\"false\" size=\"large\">{{ $t('file.rename') }}</el-radio>\n                </el-radio-group>\n            </div>\n            <div v-if=\"existFiles.length > 0 && !changeName\" class=\"text-center\">\n                <el-alert show-icon type=\"warning\" :closable=\"false\">\n                    <template #title>\n                        <span class=\"whitespace-break-spaces\">\n                            {{ $t('file.existFileDirHelper') + $t('file.coverDirHelper') }}\n                        </span>\n                    </template>\n                </el-alert>\n                <el-transfer\n                    v-model=\"skipFiles\"\n                    class=\"text-left inline-block mt-4\"\n                    :titles=\"[$t('commons.button.cover'), $t('commons.button.skip')]\"\n                    :format=\"{\n                        noChecked: '${total}',\n                        hasChecked: '${checked}/${total}',\n                    }\"\n                    :data=\"transferData\"\n                />\n            </div>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose(false)\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(fileForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"getPath\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { batchCheckFiles, checkFile, moveFile } from '@/api/modules/files';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { ref, reactive, computed, ComputedRef } from 'vue';\nimport FileList from '@/components/file-list/index.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { getDateStr } from '@/utils/util';\nimport { File } from '@/api/interface/file';\n\ninterface MoveProps {\n    oldPaths: Array<string>;\n    allNames: Array<string>;\n    type: string;\n    path: string;\n    name: string;\n    isDir: boolean;\n}\n\nconst fileForm = ref<FormInstance>();\nconst loading = ref(false);\nconst open = ref(false);\nconst type = ref('cut');\nconst changeName = ref(false);\nconst oldName = ref('');\nconst existFiles = ref<File.ExistFileInfo[]>([]);\nconst skipFiles = ref([]);\nconst transferData = ref([]);\nconst fileRef = ref();\n\nconst title = computed(() => {\n    if (type.value === 'cut') {\n        return i18n.global.t('file.move');\n    } else {\n        return i18n.global.t('commons.button.copy');\n    }\n});\n\nconst addForm = reactive({\n    oldPaths: [] as string[],\n    newPath: '',\n    type: '',\n    name: '',\n    allNames: [] as string[],\n    isDir: false,\n    cover: false,\n    coverPaths: [] as string[],\n});\n\nconst rules = reactive<FormRules>({\n    newPath: [Rules.requiredInput],\n    name: [Rules.requiredInput],\n});\n\nconst em = defineEmits(['close', 'loading']);\n\nconst handleClose = (search: boolean) => {\n    open.value = false;\n    if (fileForm.value) {\n        fileForm.value.resetFields();\n    }\n    em('close', search);\n};\n\nconst getFileName = (filePath: string) => {\n    if (filePath.endsWith('/')) {\n        filePath = filePath.slice(0, -1);\n    }\n\n    return filePath.split('/').pop();\n};\n\nconst coverFiles: ComputedRef<string[]> = computed(() => {\n    const existingNames = new Set(\n        existFiles.value.filter((item) => !skipFiles.value.includes(item.name) && item.isDir).map((item) => item.name),\n    );\n    return addForm.oldPaths.filter((path) => existingNames.has(getFileName(path)));\n});\n\nconst mvFiles: ComputedRef<string[]> = computed(() => {\n    const skipSet = new Set(skipFiles.value);\n    const coverSet = new Set(coverFiles.value.map(getFileName));\n\n    return addForm.oldPaths.filter((path) => {\n        const name = getFileName(path);\n        return !skipSet.has(name) && !coverSet.has(name);\n    });\n});\n\nconst getPath = (path: string) => {\n    addForm.newPath = path;\n};\n\nconst changeType = () => {\n    if (addForm.cover) {\n        addForm.name = oldName.value;\n    } else {\n        addForm.name = renameFileWithSuffix(oldName.value, addForm.isDir);\n    }\n};\n\nconst mvFile = () => {\n    loading.value = true;\n    em('loading', true);\n    moveFile(addForm)\n        .then(() => {\n            if (type.value === 'cut') {\n                MsgSuccess(i18n.global.t('file.moveSuccess'));\n            } else {\n                MsgSuccess(i18n.global.t('file.copySuccess'));\n            }\n            handleClose(true);\n        })\n        .finally(() => {\n            loading.value = false;\n            em('loading', false);\n        });\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        addForm.coverPaths = coverFiles.value;\n        addForm.oldPaths = mvFiles.value;\n        mvFile();\n    });\n};\n\nconst getCompleteExtension = (filename: string): string => {\n    const compoundExtensions = [\n        '.tar.gz',\n        '.tar.bz2',\n        '.tar.xz',\n        '.tar.lzma',\n        '.tar.Z',\n        '.tar.zst',\n        '.tar.lzo',\n        '.tar.sz',\n        '.tgz',\n        '.tbz2',\n        '.txz',\n        '.tzst',\n    ];\n    const foundExtension = compoundExtensions.find((ext) => filename.endsWith(ext));\n    if (foundExtension) {\n        return foundExtension;\n    }\n    const match = filename.match(/\\.[a-zA-Z0-9]+$/);\n    return match ? match[0] : '';\n};\n\nconst renameFileWithSuffix = (fileName: string, isDir: boolean): string => {\n    const insertStr = '-' + getDateStr();\n    const completeExt = isDir ? '' : getCompleteExtension(fileName);\n    if (!completeExt) {\n        return `${fileName}${insertStr}`;\n    } else {\n        const baseName = fileName.slice(0, fileName.length - completeExt.length);\n        return `${baseName}${insertStr}${completeExt}`;\n    }\n};\n\nconst handleFilePaths = async (fileNames: string[], newPath: string) => {\n    const uniqueFiles = [...new Set(fileNames)];\n    const fileNamesWithPath = uniqueFiles.map((file) => newPath + '/' + file);\n    const existData = await batchCheckFiles(fileNamesWithPath);\n    existFiles.value = existData.data;\n    transferData.value = existData.data.map((file) => ({\n        key: file.name,\n        label: file.name,\n    }));\n};\n\nconst acceptParams = async (props: MoveProps) => {\n    changeName.value = false;\n    addForm.oldPaths = props.oldPaths;\n    addForm.type = props.type;\n    addForm.newPath = props.path;\n    addForm.isDir = props.isDir;\n    addForm.name = '';\n    addForm.allNames = props.allNames;\n    type.value = props.type;\n    existFiles.value = [];\n    addForm.coverPaths = [];\n    if (props.name && props.name != '') {\n        oldName.value = props.name;\n        const res = await checkFile(props.path + '/' + props.name, false);\n        if (res.data) {\n            changeName.value = true;\n            addForm.cover = false;\n            addForm.name = renameFileWithSuffix(props.name, addForm.isDir);\n            open.value = true;\n        } else {\n            mvFile();\n        }\n    } else if (props.allNames && props.allNames.length > 0) {\n        await handleFilePaths(addForm.allNames, addForm.newPath);\n        if (existFiles.value.length > 0) {\n            changeName.value = false;\n            open.value = true;\n        } else {\n            mvFile();\n        }\n    } else {\n        mvFile();\n    }\n};\n\ndefineExpose({ acceptParams });\n</script>\n\n<style lang=\"scss\" scoped>\n:deep(.el-transfer) {\n    --el-transfer-panel-width: 260px;\n    .el-button {\n        padding: 4px 7px;\n    }\n}\n\n:deep(.el-transfer__buttons) {\n    padding: 5px 15px;\n    @media (max-width: 600px) {\n        width: 250px;\n        text-align: center;\n        padding: 10px 0;\n        .el-button [class*='el-icon'] svg {\n            transform: rotate(90deg);\n        }\n    }\n\n    @media (min-width: 601px) {\n        display: inline-flex;\n        flex-direction: column;\n        align-items: center;\n        gap: 10px;\n        width: 40px;\n        height: 40px;\n        justify-content: center;\n        .el-button + .el-button {\n            margin-left: 0;\n        }\n    }\n}\n\n:deep(.el-transfer-panel .el-transfer-panel__footer) {\n    height: 65px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/preview/index.vue",
    "content": "<template>\n    <el-dialog\n        v-model=\"open\"\n        :show-close=\"false\"\n        :before-close=\"handleClose\"\n        destroy-on-close\n        append-to-body\n        @opened=\"onOpen\"\n        :class=\"isFullscreen ? 'w-full' : '!w-3/4'\"\n        :top=\"'5vh'\"\n        :fullscreen=\"isFullscreen\"\n    >\n        <template #header>\n            <div class=\"flex items-center justify-between\">\n                <span>{{ $t('commons.button.preview') + ' - ' + filePath }}</span>\n                <el-space alignment=\"center\" :size=\"10\" class=\"dialog-header-icon\">\n                    <el-tooltip :content=\"loadTooltip()\" placement=\"top\" v-if=\"fileType !== 'excel'\">\n                        <el-icon @click=\"toggleFullscreen\" class=\"cursor-pointer hover:scale-110\">\n                            <FullScreen />\n                        </el-icon>\n                    </el-tooltip>\n                    <el-icon @click=\"handleClose\" size=\"20\" class=\"cursor-pointer hover:scale-110\"><Close /></el-icon>\n                </el-space>\n            </div>\n        </template>\n        <div\n            v-loading=\"loading\"\n            :style=\"isFullscreen ? 'height: 90vh' : 'height: 80vh'\"\n            :class=\"fileType === 'image' ? 'overflow-y-auto' : 'flex justify-center items-center'\"\n        >\n            <template v-if=\"fileType === 'image'\">\n                <div class=\"flex h-full\">\n                    <aside\n                        class=\"w-[200px] overflow-y-auto p-2 sm:block hidden left-aside rounded\"\n                        v-if=\"imageFiles.length > 1\"\n                    >\n                        <template v-for=\"(item, index) in imageFiles\" :key=\"index\">\n                            <el-tooltip :content=\"item.path\" placement=\"right\">\n                                <div\n                                    class=\"text-sm truncate mb-1 rounded p-1 left-item\"\n                                    @click=\"changeImg(item.path)\"\n                                    :class=\"item.path === filePath ? 'left-item-default' : ''\"\n                                >\n                                    {{ item.path }}\n                                </div>\n                            </el-tooltip>\n                        </template>\n                    </aside>\n                    <main class=\"flex-1 overflow-hidden\">\n                        <el-tooltip :content=\"filePath\" placement=\"bottom\">\n                            <el-image\n                                loading=\"lazy\"\n                                :src=\"fileUrl\"\n                                :alt=\"filePath\"\n                                fit=\"contain\"\n                                class=\"w-full h-full\"\n                            />\n                        </el-tooltip>\n                    </main>\n                </div>\n            </template>\n            <video v-else-if=\"fileType === 'video'\" :src=\"fileUrl\" controls autoplay class=\"size-3/4\"></video>\n\n            <audio v-else-if=\"fileType === 'audio'\" :src=\"fileUrl\" controls></audio>\n\n            <vue-office-docx\n                v-else-if=\"fileType === 'word'\"\n                :src=\"fileUrl\"\n                :style=\"isFullscreen ? 'height: 90vh' : 'height: 80vh'\"\n                class=\"w-full\"\n                @rendered=\"renderedHandler\"\n                @error=\"errorHandler\"\n            />\n\n            <vue-office-excel\n                v-else-if=\"fileType === 'excel'\"\n                :src=\"fileUrl\"\n                :style=\"isFullscreen ? 'height: 90vh;' : 'height: 80vh'\"\n                class=\"w-full\"\n                @rendered=\"renderedHandler\"\n                @error=\"errorHandler\"\n            />\n        </div>\n    </el-dialog>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { ref } from 'vue';\n\nimport { Close, FullScreen } from '@element-plus/icons-vue';\nimport VueOfficeDocx from '@vue-office/docx';\nimport VueOfficeExcel from '@vue-office/excel';\nimport '@vue-office/docx/lib/index.css';\nimport '@vue-office/excel/lib/index.css';\nimport { MsgError } from '@/utils/message';\n\ninterface EditProps {\n    fileType: string;\n    path: string;\n    name: string;\n    extension: string;\n    imageFiles: [];\n    currentNode: string;\n}\n\nconst open = ref(false);\nconst loading = ref(false);\nconst filePath = ref('');\nconst fileName = ref('');\nconst fileType = ref('');\nconst fileUrl = ref('');\nconst currentNode = ref('');\nconst imageFiles = ref([]);\n\nconst fileExtension = ref('');\nconst isFullscreen = ref(false);\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    open.value = false;\n\n    em('close', open.value);\n};\n\nconst renderedHandler = () => {\n    loading.value = false;\n};\nconst errorHandler = () => {\n    open.value = false;\n    MsgError(i18n.global.t('commons.msg.unSupportType'));\n};\nconst loadTooltip = () => {\n    return i18n.global.t('commons.button.' + (isFullscreen.value ? 'quitFullscreen' : 'fullscreen'));\n};\n\nconst toggleFullscreen = () => {\n    isFullscreen.value = !isFullscreen.value;\n};\n\nconst getDownloadUrl = (path: string) => {\n    const baseUrl = `${import.meta.env.VITE_API_URL as string}/files/download`;\n    const encodedPath = encodeURIComponent(path);\n    const timestamp = new Date().getTime();\n    return `${baseUrl}?operateNode=${currentNode.value}&path=${encodedPath}&timestamp=${timestamp}`;\n};\n\nconst acceptParams = (props: EditProps) => {\n    imageFiles.value = [];\n    fileExtension.value = props.extension;\n    fileName.value = props.name;\n    filePath.value = props.path;\n    fileType.value = props.fileType;\n    currentNode.value = props.currentNode;\n    isFullscreen.value = fileType.value === 'excel';\n\n    loading.value = true;\n    fileUrl.value = getDownloadUrl(props.path);\n    imageFiles.value = props.imageFiles.map((item) => ({\n        path: item,\n        url: getDownloadUrl(item),\n    }));\n    open.value = true;\n    loading.value = false;\n};\n\nconst onOpen = () => {};\n\nconst changeImg = (path: string) => {\n    filePath.value = path;\n    fileUrl.value = getDownloadUrl(path);\n};\n\ndefineExpose({ acceptParams });\n</script>\n\n<style scoped lang=\"scss\">\n.dialog-top {\n    top: 0;\n}\n\n.dialog-header-icon {\n    color: var(--el-color-info);\n}\n.left-aside {\n    background-color: var(--panel-menu-bg-color);\n    opacity: 85%;\n}\n.left-item {\n    &:hover {\n        background: var(--el-menu-item-bg-color-active) !important;\n    }\n}\n.left-item-default {\n    background: var(--el-menu-item-bg-color-active) !important;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/process/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('file.downloadProcess')\" size=\"small\" @close=\"handleClose\">\n        <template #content>\n            <div class=\"space-y-4 p-4\" :loading=\"loading\">\n                <div\n                    v-for=\"(value, index) in res\"\n                    :key=\"index\"\n                    class=\"rounded-lg p-4 shadow-sm border border-gray-100 transition-all duration-200 hover:shadow-md\"\n                    :class=\"{ completed: value.percent === 100 }\"\n                >\n                    <div class=\"flex items-center gap-3\">\n                        <div class=\"flex-1\">\n                            <MsgInfo :info=\"value.name\" class=\"text-gray-700\" />\n                            <div class=\"text-gray-500\">\n                                {{ value.percent === 100 ? $t('file.downloadSuccess') : $t('file.downloading') }}\n                            </div>\n                        </div>\n                    </div>\n\n                    <div class=\"space-y-2\">\n                        <div class=\"flex justify-end text-gray-500 mb-1\">\n                            <span>{{ getFileSize(value.written) }}</span>\n                            <span v-if=\"value.total > 0\" class=\"text-gray-400\">/{{ getFileSize(value.total) }}</span>\n                        </div>\n                        <div class=\"w-full\">\n                            <el-progress\n                                v-if=\"value.total === 0 && value.percent != 100\"\n                                :percentage=\"100\"\n                                :indeterminate=\"true\"\n                                :duration=\"1\"\n                                class=\"progress-bar\"\n                                :stroke-width=\"8\"\n                                :show-text=\"false\"\n                            />\n                            <el-progress\n                                v-else\n                                :percentage=\"value.percent\"\n                                :stroke-width=\"8\"\n                                class=\"progress-bar\"\n                                :status=\"value.percent === 100 ? 'success' : ''\"\n                            />\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { fileWgetKeys } from '@/api/modules/files';\nimport { computeSize } from '@/utils/util';\nimport { onBeforeUnmount, ref } from 'vue';\nimport MsgInfo from '@/components/msg-info/index.vue';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nlet processSocket = ref(null) as unknown as WebSocket;\nconst res = ref([]);\nconst keys = ref(['']);\nconst open = ref(false);\nconst loading = ref(false);\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    closeSocket();\n    open.value = false;\n    em('close', open);\n};\n\nconst isWsOpen = () => {\n    const readyState = processSocket && processSocket.readyState;\n    return readyState === 1;\n};\nconst closeSocket = () => {\n    if (isWsOpen()) {\n        processSocket && processSocket.close();\n    }\n};\n\nconst onOpenProcess = () => {};\nconst onMessage = (message: any) => {\n    res.value = JSON.parse(message.data);\n};\nconst onerror = () => {};\nconst onClose = () => {};\n\nconst initProcess = () => {\n    let href = window.location.href;\n    let protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss';\n    let ipLocal = href.split('//')[1].split('/')[0];\n    let currentNode = globalStore.currentNode;\n    processSocket = new WebSocket(`${protocol}://${ipLocal}/api/v2/files/wget/process?operateNode=${currentNode}`);\n    processSocket.onopen = onOpenProcess;\n    processSocket.onmessage = onMessage;\n    processSocket.onerror = onerror;\n    processSocket.onclose = onClose;\n    sendMsg();\n};\n\nconst getKeys = async () => {\n    keys.value = [];\n    res.value = [];\n    loading.value = true;\n    try {\n        const res = await fileWgetKeys();\n        if (res.data && res.data.keys.length > 0) {\n            keys.value = res.data.keys;\n            initProcess();\n        }\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst sendMsg = () => {\n    setInterval(() => {\n        if (isWsOpen()) {\n            processSocket.send(\n                JSON.stringify({\n                    type: 'wget',\n                    keys: keys.value,\n                }),\n            );\n        }\n    }, 1000);\n};\n\nconst getFileSize = (size: number) => {\n    return computeSize(size);\n};\n\nonBeforeUnmount(() => {\n    closeSocket();\n});\n\nconst acceptParams = () => {\n    open.value = true;\n    getKeys();\n};\n\ndefineExpose({ acceptParams });\n</script>\n\n<style type=\"scss\" scoped>\n.download-item.completed {\n    @apply bg-green-50/50;\n}\n\n.progress-bar {\n    :deep(.el-progress-bar__outer) {\n        @apply rounded-full bg-gray-100;\n    }\n\n    :deep(.el-progress-bar__inner) {\n        @apply rounded-full transition-all duration-300;\n    }\n}\n\n@keyframes bounce {\n    0%,\n    100% {\n        transform: translateY(-10%);\n        animation-timing-function: cubic-bezier(0.8, 0, 1, 1);\n    }\n    50% {\n        transform: translateY(0);\n        animation-timing-function: cubic-bezier(0, 0, 0.2, 1);\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/recycle-bin/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.delete')\" size=\"small\">\n        <el-row>\n            <el-col :span=\"20\" :offset=\"2\">\n                <el-alert :title=\"$t('file.deleteRecycleHelper')\" show-icon type=\"error\" :closable=\"false\"></el-alert>\n                <div class=\"flx-align-center mb-1 mt-1\" v-for=\"(row, index) in files\" :key=\"index\">\n                    <div>\n                        <svg-icon v-if=\"row.isDir\" className=\"table-icon mr-1 \" iconName=\"p-file-folder\"></svg-icon>\n                        <svg-icon v-else className=\"table-icon mr-1\" :iconName=\"getIconName(row.extension)\"></svg-icon>\n                    </div>\n                    <span class=\"sle\">{{ row.name }}</span>\n                </div>\n            </el-col>\n        </el-row>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\" v-loading=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport { getIcon } from '@/utils/util';\nimport { deleteFile } from '@/api/modules/files';\nimport { MsgSuccess } from '@/utils/message';\n\nconst open = ref(false);\nconst files = ref();\nconst loading = ref(false);\nconst em = defineEmits(['close']);\nconst forceDelete = ref(false);\n\nconst acceptParams = (props: File.RecycleBin[]) => {\n    files.value = props;\n    open.value = true;\n    forceDelete.value = false;\n};\n\nconst onConfirm = () => {\n    const pros = [];\n    for (const s of files.value) {\n        pros.push(deleteFile({ path: s.from + '/' + s.rName, isDir: s.isDir, forceDelete: true }));\n    }\n    loading.value = true;\n    Promise.all(pros)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            open.value = false;\n            em('close');\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst getIconName = (extension: string) => {\n    return getIcon(extension);\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped>\n.resource {\n    margin-top: 10px;\n    max-height: 400px;\n    overflow: auto;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/recycle-bin/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('file.recycleBin')\" @close=\"handleClose\" size=\"large\">\n        <template #content>\n            <div class=\"flex space-x-4\">\n                <el-button @click=\"clear\" type=\"primary\" :disabled=\"data == null || data.length == 0\">\n                    {{ $t('file.clearRecycleBin') }}\n                </el-button>\n                <el-button @click=\"patchDelete\" :disabled=\"data == null || selects.length == 0\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n                <el-button @click=\"patchReduce\" :disabled=\"data == null || selects.length == 0\">\n                    {{ $t('file.reduce') }}\n                </el-button>\n                <el-form-item :label=\"$t('file.fileRecycleBin')\">\n                    <el-switch v-model=\"status\" active-value=\"Enable\" inactive-value=\"Disable\" @change=\"changeStatus\" />\n                </el-form-item>\n            </div>\n            <ComplexTable\n                :pagination-config=\"paginationConfig\"\n                v-model:selects=\"selects\"\n                :data=\"data\"\n                @search=\"search\"\n                class=\"mt-5\"\n            >\n                <el-table-column type=\"selection\" fix />\n                <el-table-column prop=\"name\" :label=\"$t('commons.table.name')\" show-overflow-tooltip>\n                    <template #default=\"{ row }\">\n                        <span class=\"text-ellipsis\" type=\"primary\">\n                            <svg-icon v-if=\"row.isDir\" className=\"table-icon\" iconName=\"p-file-folder\"></svg-icon>\n                            <svg-icon v-else className=\"table-icon\" iconName=\"p-file-normal\"></svg-icon>\n                            {{ row.name }}\n                        </span>\n                    </template>\n                </el-table-column>\n\n                <el-table-column\n                    :label=\"$t('file.sourcePath')\"\n                    show-overflow-tooltip\n                    prop=\"sourcePath\"\n                ></el-table-column>\n                <el-table-column :label=\"$t('file.size')\" prop=\"size\" max-width=\"50\">\n                    <template #default=\"{ row }\">\n                        {{ getFileSize(row.size) }}\n                    </template>\n                </el-table-column>\n                <el-table-column\n                    :label=\"$t('file.deleteTime')\"\n                    prop=\"deleteTime\"\n                    :formatter=\"dateFormat\"\n                    show-overflow-tooltip\n                    sortable\n                ></el-table-column>\n                <fu-table-operations :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n            </ComplexTable>\n            <Delete ref=\"deleteRef\" @close=\"search\" />\n            <Reduce ref=\"reduceRef\" @close=\"search\" />\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getRecycleStatus, clearRecycle, getRecycleList, reduceFile } from '@/api/modules/files';\nimport { reactive, ref } from 'vue';\nimport { dateFormat, computeSize } from '@/utils/util';\nimport i18n from '@/lang';\nimport Delete from './delete/index.vue';\nimport Reduce from './reduce/index.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateAgentSetting } from '@/api/modules/setting';\n\nconst open = ref(false);\nconst req = reactive({\n    page: 1,\n    pageSize: 20,\n});\nconst data = ref([]);\nconst em = defineEmits(['close']);\nconst selects = ref([]);\nconst loading = ref(false);\nconst files = ref([]);\nconst status = ref('Enable');\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'recycle-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('recycle-page-size')) || 20,\n    total: 0,\n});\n\nconst deleteRef = ref();\nconst reduceRef = ref();\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst getFileSize = (size: number) => {\n    return computeSize(size);\n};\n\nconst acceptParams = () => {\n    search();\n    getStatus();\n};\n\nconst getStatus = async () => {\n    try {\n        const res = await getRecycleStatus();\n        status.value = res.data;\n    } catch (error) {}\n};\n\nconst changeStatus = async () => {\n    try {\n        loading.value = true;\n        await updateAgentSetting({ key: 'FileRecycleBin', value: status.value });\n        MsgSuccess(\n            i18n.global.t('file.fileRecycleBinMsg', [i18n.global.t('commons.button.' + status.value.toLowerCase())]),\n        );\n        loading.value = false;\n    } catch (error) {}\n};\n\nconst search = async () => {\n    try {\n        req.page = paginationConfig.currentPage;\n        req.pageSize = paginationConfig.pageSize;\n        const res = await getRecycleList(req);\n        data.value = res.data.items;\n        paginationConfig.total = res.data.total;\n        open.value = true;\n    } catch (error) {}\n};\n\nconst singleDel = (row: any) => {\n    files.value = [];\n    files.value.push(row);\n    deleteRef.value.acceptParams(files.value);\n};\n\nconst patchDelete = () => {\n    files.value = selects.value;\n    deleteRef.value.acceptParams(files.value);\n};\n\nconst patchReduce = () => {\n    files.value = selects.value;\n    reduceRef.value.acceptParams(files.value);\n};\n\nconst rdFile = async (row: any) => {\n    ElMessageBox.confirm(i18n.global.t('file.reduceHelper'), i18n.global.t('file.reduce'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        try {\n            loading.value = true;\n            await reduceFile({ from: row.from, rName: row.rName, name: row.name });\n            loading.value = false;\n            search();\n        } catch (error) {}\n    });\n};\n\nconst clear = async () => {\n    ElMessageBox.confirm(i18n.global.t('commons.msg.delete'), i18n.global.t('file.clearRecycleBinHelper'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        try {\n            loading.value = true;\n            await clearRecycle();\n            loading.value = false;\n            search();\n        } catch (error) {}\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('file.reduce'),\n        click: rdFile,\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: singleDel,\n    },\n];\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/recycle-bin/reduce/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('file.reduce')\" size=\"small\">\n        <el-row>\n            <el-col :span=\"20\" :offset=\"2\">\n                <el-alert :title=\"$t('file.confirmReduce')\" show-icon type=\"error\" :closable=\"false\"></el-alert>\n                <div class=\"flx-align-center mb-1 mt-1\" v-for=\"(row, index) in files\" :key=\"index\">\n                    <div>\n                        <svg-icon v-if=\"row.isDir\" className=\"table-icon mr-1 \" iconName=\"p-file-folder\"></svg-icon>\n                        <svg-icon v-else className=\"table-icon mr-1\" :iconName=\"getIconName(row.extension)\"></svg-icon>\n                    </div>\n                    <span class=\"sle\">{{ row.name }}</span>\n                </div>\n            </el-col>\n        </el-row>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\" v-loading=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { getIcon } from '@/utils/util';\nimport { reduceFile } from '@/api/modules/files';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { File } from '@/api/interface/file';\n\nconst open = ref(false);\nconst em = defineEmits(['close']);\n\nconst files = ref();\nconst loading = ref(false);\nconst forceDelete = ref(false);\n\nconst acceptParams = (props: File.RecycleBin[]) => {\n    files.value = props;\n    open.value = true;\n    forceDelete.value = false;\n};\n\nconst onConfirm = () => {\n    const pros = [];\n    for (const s of files.value) {\n        pros.push(reduceFile({ from: s.from, rName: s.rName, name: s.name }));\n    }\n    loading.value = true;\n    Promise.all(pros)\n        .then(() => {\n            MsgSuccess(i18n.global.t('file.reduceSuccess'));\n            open.value = false;\n            em('close');\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst getIconName = (extension: string) => {\n    return getIcon(extension);\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/rename/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('file.rename')\" :resource=\"oldName\" @close=\"handleClose\" size=\"normal\">\n        <el-form\n            ref=\"fileForm\"\n            label-position=\"top\"\n            :model=\"addForm\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('file.path')\" prop=\"path\">\n                <el-input v-model=\"addForm.path\" disabled />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"newName\">\n                <el-input v-model.trim=\"addForm.newName\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(fileForm)\">{{ $t('commons.button.confirm') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { renameRile } from '@/api/modules/files';\nimport { Rules } from '@/global/form-rules';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\ninterface RenameProps {\n    path: string;\n    oldName: string;\n}\n\nconst fileForm = ref<FormInstance>();\nconst loading = ref(false);\nconst open = ref(false);\nconst oldName = ref('');\n\nconst addForm = reactive({\n    newName: '',\n    path: '',\n});\n\nconst rules = reactive<FormRules>({\n    newName: [Rules.requiredInput],\n});\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    if (fileForm.value) {\n        fileForm.value.resetFields();\n    }\n    em('close', false);\n};\n\nconst getPath = (path: string, name: string) => {\n    return path + '/' + name;\n};\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n\n        let addItem = {};\n        Object.assign(addItem, addForm);\n        addItem['oldName'] = getPath(addForm.path, oldName.value);\n        addItem['newName'] = getPath(addForm.path, addForm.newName);\n        loading.value = true;\n        renameRile(addItem as File.FileRename)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst acceptParams = (props: RenameProps) => {\n    oldName.value = props.oldName;\n    addForm.newName = props.oldName;\n    addForm.path = props.path;\n    open.value = true;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/terminal/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"terminalVisible\"\n        :header=\"$t('menu.terminal')\"\n        @close=\"handleClose\"\n        size=\"large\"\n        :autoClose=\"false\"\n        :fullScreen=\"true\"\n    >\n        <template #content>\n            <div class=\"terminal-container\">\n                <el-alert :closable=\"false\" :title=\"$t('terminal.localConnJump')\" type=\"info\" />\n                <Terminal class=\"terminal-content\" ref=\"terminalRef\"></Terminal>\n                <div class=\"quick-command\">\n                    <el-cascader\n                        v-model=\"quickCmd\"\n                        :options=\"commandTree\"\n                        @change=\"quickInput\"\n                        :show-all-levels=\"false\"\n                        class=\"w-full\"\n                        placeholder=\" \"\n                        filterable\n                    >\n                        <template #prefix>{{ $t('terminal.quickCommand') }}</template>\n                    </el-cascader>\n                </div>\n            </div>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, nextTick } from 'vue';\nimport Terminal from '@/components/terminal/index.vue';\nimport { getCommandTree } from '@/api/modules/command';\nimport i18n from '@/lang';\n\nconst terminalVisible = ref(false);\nconst terminalRef = ref<InstanceType<typeof Terminal> | null>(null);\n\nlet quickCmd = ref();\nconst commandTree = ref();\n\ninterface DialogProps {\n    cwd: string;\n    command: string;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    terminalVisible.value = true;\n    loadCommandTree();\n    await initTerm(params.cwd);\n};\n\nconst initTerm = async (cwd: string) => {\n    await nextTick();\n    terminalRef.value!.acceptParams({\n        endpoint: '/api/v2/hosts/terminal',\n        args: `command=${encodeURIComponent(`clear && cd \"${cwd}\"`)}`,\n        error: '',\n        initCmd: '',\n    });\n};\n\nconst loadCommandTree = async () => {\n    const res = await getCommandTree('command');\n    commandTree.value = res.data || [];\n    for (const item of commandTree.value) {\n        if (item.label === 'Default') {\n            item.label = i18n.global.t('commons.table.default');\n        }\n    }\n};\n\nfunction quickInput(val: Array<string>) {\n    if (val.length < 1) {\n        return;\n    }\n    quickCmd.value = val[val.length - 1];\n    terminalRef.value?.sendMsg(quickCmd.value + '\\n');\n    quickCmd.value = '';\n}\n\nconst onClose = () => {\n    terminalRef.value?.onClose();\n};\n\nfunction handleClose() {\n    onClose();\n    terminalVisible.value = false;\n}\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped>\n.terminal-container {\n    display: flex;\n    flex-direction: column;\n    height: calc(100vh - 140px);\n}\n\n.terminal-content {\n    flex: 1;\n    overflow: hidden;\n    margin-top: 8px;\n}\n\n.quick-command {\n    flex-shrink: 0;\n    margin-top: 1px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/text-preview/index.vue",
    "content": "<template>\n    <el-drawer v-model=\"open\" :title=\"title\" size=\"50%\" :before-close=\"handleClose\" destroy-on-close>\n        <div v-loading=\"loading\">\n            <div class=\"mb-4 flex items-center justify-between\">\n                <el-tag v-if=\"isTruncated\" type=\"warning\">\n                    {{ $t('file.previewTruncated') }}\n                </el-tag>\n                <el-button @click=\"onDownload\" icon=\"Download\" :disabled=\"!hasContent\">\n                    {{ $t('commons.button.download') }}\n                </el-button>\n            </div>\n            <div class=\"log-container\" ref=\"logContainer\" @scroll=\"onScroll\">\n                <div class=\"log-spacer\" :style=\"{ height: `${totalHeight}px` }\"></div>\n                <div class=\"log-viewport\" :style=\"{ transform: `translateY(${offsetY}px)` }\">\n                    <div\n                        v-for=\"(line, index) in visibleLines\"\n                        :key=\"`${startIndex + index}-${line}`\"\n                        class=\"log-item\"\n                        :style=\"{ height: `${lineHeight}px` }\"\n                    >\n                        <span class=\"line-content\">{{ line }}</span>\n                    </div>\n                </div>\n                <div v-if=\"lines.length === 0 && !loading\" class=\"empty-content\">\n                    {{ $t('file.previewEmpty') }}\n                </div>\n            </div>\n        </div>\n    </el-drawer>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, computed, nextTick } from 'vue';\nimport { getPreviewContent } from '@/api/modules/files';\nimport { downloadFile } from '@/utils/util';\nimport { GlobalStore } from '@/store';\nimport i18n from '@/lang';\n\nconst globalStore = GlobalStore();\n\ninterface PreviewProps {\n    path: string;\n    name: string;\n}\n\nconst open = ref(false);\nconst loading = ref(false);\nconst title = ref('');\nconst filePath = ref('');\nconst lines = ref<string[]>([]);\nconst isTruncated = ref(false);\nconst hasContent = ref(false);\n\nconst logContainer = ref<HTMLElement | null>(null);\nconst lineHeight = 23;\nconst scrollTop = ref(0);\n\nconst totalHeight = computed(() => lines.value.length * lineHeight);\n\nconst visibleCount = computed(() => {\n    const buffer = 5;\n    return Math.ceil(600 / lineHeight) + buffer * 2;\n});\n\nconst startIndex = computed(() => {\n    const buffer = 5;\n    const index = Math.floor(scrollTop.value / lineHeight) - buffer;\n    return Math.max(0, index);\n});\n\nconst endIndex = computed(() => {\n    return Math.min(lines.value.length, startIndex.value + visibleCount.value);\n});\n\nconst visibleLines = computed(() => {\n    return lines.value.slice(startIndex.value, endIndex.value);\n});\n\nconst offsetY = computed(() => {\n    return startIndex.value * lineHeight;\n});\n\nconst onScroll = () => {\n    if (!logContainer.value) return;\n    scrollTop.value = logContainer.value.scrollTop;\n};\n\nconst handleClose = () => {\n    open.value = false;\n    lines.value = [];\n};\n\nconst onDownload = () => {\n    downloadFile(filePath.value, globalStore.currentNode);\n};\n\nconst acceptParams = async (props: PreviewProps) => {\n    title.value = i18n.global.t('commons.button.preview') + ' - ' + props.name;\n    filePath.value = props.path;\n    lines.value = [];\n    isTruncated.value = false;\n    hasContent.value = false;\n    open.value = true;\n    loading.value = true;\n\n    try {\n        const res = await getPreviewContent({ path: props.path });\n        if (res.data.content) {\n            lines.value = res.data.content.split('\\n');\n            hasContent.value = true;\n            if (res.data.size > 10 * 1024 * 1024) {\n                isTruncated.value = true;\n            }\n        }\n        nextTick(() => {\n            if (logContainer.value) {\n                logContainer.value.scrollTop = logContainer.value.scrollHeight;\n            }\n        });\n    } catch (error) {\n        console.error('Preview error:', error);\n    } finally {\n        loading.value = false;\n    }\n};\n\ndefineExpose({ acceptParams });\n</script>\n\n<style lang=\"scss\" scoped>\n.log-container {\n    height: calc(100vh - 200px);\n    overflow-y: auto;\n    overflow-x: auto;\n    position: relative;\n    background-color: var(--panel-logs-bg-color);\n    border-radius: 4px;\n}\n\n.log-spacer {\n    position: relative;\n    width: 100%;\n}\n\n.log-viewport {\n    position: absolute;\n    top: 0;\n    left: 0;\n    right: 0;\n    will-change: transform;\n    width: max-content;\n    min-width: 100%;\n}\n\n.log-item {\n    min-width: 100%;\n    padding: 2px 8px;\n    color: #f5f5f5;\n    box-sizing: border-box;\n    white-space: pre;\n}\n\n.line-content {\n    font-family: 'Courier New', Courier, monospace;\n    font-size: 13px;\n}\n\n.empty-content {\n    padding: 20px;\n    text-align: center;\n    color: #9ca3af;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/upload/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.upload')\"\n        @before-close=\"handleClose\"\n        size=\"normal\"\n        :confirmBeforeClose=\"true\"\n    >\n        <template #content>\n            <div class=\"upload-button flex flex-wrap justify-between items-center gap-4 pb-4 md:flex-nowrap\">\n                <div class=\"flex flex-wrap gap-4\">\n                    <el-button type=\"primary\" @click=\"upload('file')\">\n                        {{ $t('commons.button.upload') }}{{ $t('menu.files') }}\n                    </el-button>\n                    <el-button type=\"primary\" @click=\"upload('dir')\">\n                        {{ $t('commons.button.upload') }}{{ $t('file.dir') }}\n                    </el-button>\n                </div>\n                <el-button @click=\"clearFiles\">{{ $t('file.clearList') }}</el-button>\n            </div>\n\n            <div>\n                <div\n                    class=\"el-upload-dragger\"\n                    @dragover=\"handleDragover\"\n                    @drop=\"handleDrop\"\n                    @dragleave=\"handleDragleave\"\n                >\n                    <div class=\"flex items-center justify-center h-52\">\n                        <div>\n                            <el-icon class=\"el-icon--upload\"><upload-filled /></el-icon>\n                            <div class=\"el-upload__text\">\n                                {{ $t('file.dropHelper') }}\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <el-upload\n                action=\"#\"\n                :auto-upload=\"false\"\n                ref=\"uploadRef\"\n                :on-change=\"fileOnChange\"\n                :on-exceed=\"handleExceed\"\n                :on-success=\"handleSuccess\"\n                :show-file-list=\"false\"\n                multiple\n                v-model:file-list=\"uploaderFiles\"\n                :limit=\"1000\"\n            >\n                <template #tip>\n                    <el-text>{{ uploadHelper }}</el-text>\n                    <el-progress\n                        v-if=\"upLoading\"\n                        text-inside\n                        :stroke-width=\"20\"\n                        :percentage=\"uploadPercent\"\n                    ></el-progress>\n                </template>\n            </el-upload>\n\n            <div>\n                <p\n                    v-for=\"(item, index) in uploaderFiles\"\n                    :key=\"index\"\n                    class=\"file-item\"\n                    @mouseover=\"hoverIndex = index\"\n                    @mouseout=\"hoverIndex = null\"\n                >\n                    <el-icon class=\"file-icon\"><Document /></el-icon>\n                    <span v-if=\"item.raw.webkitRelativePath != ''\">{{ item.raw.webkitRelativePath }}</span>\n                    <span v-else>{{ item.name }}</span>\n                    <span v-if=\"item.status === 'success'\" class=\"success-icon\">\n                        <el-icon><Select /></el-icon>\n                    </span>\n                    <span v-else>\n                        <el-button\n                            class=\"delete-button\"\n                            type=\"primary\"\n                            link\n                            @click=\"removeFile(index)\"\n                            :disabled=\"loading\"\n                            :icon=\"Close\"\n                        ></el-button>\n                    </span>\n                </p>\n            </div>\n        </template>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"upLoading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"upLoading || uploaderFiles.length == 0\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <ExistFileDialog ref=\"dialogExistFileRef\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { nextTick, reactive, ref } from 'vue';\nimport { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';\nimport { batchCheckFiles, chunkUploadFileData, uploadFileData } from '@/api/modules/files';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess, MsgWarning } from '@/utils/message';\nimport { Close, Document, UploadFilled } from '@element-plus/icons-vue';\nimport { TimeoutEnum } from '@/enums/http-enum';\nimport ExistFileDialog from '@/components/exist-file/index.vue';\n\ninterface UploadFileProps {\n    path: string;\n}\n\nconst uploadRef = ref<UploadInstance>();\nconst loading = ref(false);\nlet uploadPercent = ref(0);\nconst open = ref(false);\nconst path = ref();\nlet uploadHelper = ref('');\nconst dialogExistFileRef = ref();\nconst upLoading = ref(false);\nconst abortController = ref<AbortController | null>(null);\n\nconst em = defineEmits(['close']);\nconst handleClose = (done) => {\n    if (upLoading.value) {\n        ElMessageBox.confirm(i18n.global.t('file.cancelUploadHelper'), i18n.global.t('file.cancelUpload'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        })\n            .then(() => {\n                abortController.value.abort();\n                abortController.value = null;\n                closePage();\n                done();\n            })\n            .catch(() => {});\n    } else {\n        closePage();\n        done();\n    }\n};\nconst closePage = () => {\n    open.value = false;\n    upLoading.value = false;\n    clearFiles();\n    em('close', false);\n};\nconst state = reactive({\n    uploadEle: null,\n});\nconst uploaderFiles = ref<UploadFiles>([]);\nconst hoverIndex = ref<number | null>(null);\nconst tmpFiles = ref<UploadFiles>([]);\nconst breakFlag = ref(false);\nconst CHUNK_SIZE = 1024 * 1024 * 5;\nconst MAX_SINGLE_FILE_SIZE = 1024 * 1024 * 10;\n\nconst upload = (command: string) => {\n    state.uploadEle.webkitdirectory = command == 'dir';\n    uploadRef.value.$el.querySelector('input').value = '';\n    uploadRef.value.$el.querySelector('input').click();\n};\n\nconst removeFile = (index: number) => {\n    uploaderFiles.value.splice(index, 1);\n};\n\nconst handleDragover = (event: DragEvent) => {\n    event.preventDefault();\n};\n\nconst initTempFiles = () => {\n    tmpFiles.value = [];\n    breakFlag.value = false;\n};\n\nconst handleDrop = async (event: DragEvent) => {\n    initTempFiles();\n    event.preventDefault();\n    const items = event.dataTransfer?.items;\n\n    if (items) {\n        const entries = Array.from(items).map((item) => item.webkitGetAsEntry());\n        await Promise.all(entries.map((entry) => traverseFileTree(entry)));\n        if (!breakFlag.value) {\n            uploaderFiles.value = uploaderFiles.value.concat(tmpFiles.value);\n        } else {\n            MsgWarning(i18n.global.t('file.uploadOverLimit'));\n        }\n        initTempFiles();\n    }\n};\n\nconst convertFileToUploadFile = (file: File, path: string): UploadFile => {\n    const uid = Date.now();\n\n    const uploadRawFile: UploadRawFile = new File([file], file.name, {\n        type: file.type,\n        lastModified: file.lastModified,\n    }) as UploadRawFile;\n    uploadRawFile.uid = uid;\n\n    let fileName = file.name;\n    if (path != '') {\n        fileName = path + file.name;\n    }\n    return {\n        name: fileName,\n        size: file.size,\n        status: 'ready',\n        uid: uid,\n        raw: uploadRawFile,\n    };\n};\n\nconst traverseFileTree = async (item: any, path = '') => {\n    path = path || '';\n    if (!item) {\n        return;\n    }\n    if (item.isFile) {\n        if (tmpFiles.value.length > 1000) {\n            breakFlag.value = true;\n            return;\n        }\n        await new Promise<void>((resolve) => {\n            item.file((file: File) => {\n                if (!breakFlag.value) {\n                    tmpFiles.value.push(convertFileToUploadFile(file, path));\n                }\n                resolve();\n            });\n        });\n    } else if (item.isDirectory) {\n        const dirReader = item.createReader();\n        const readEntries = async () => {\n            const entries = await new Promise<any[]>((resolve) => {\n                dirReader.readEntries((entries: any[] | PromiseLike<any[]>) => {\n                    resolve(entries);\n                });\n            });\n\n            if (entries.length === 0) {\n                return;\n            }\n\n            for (const element of entries) {\n                await traverseFileTree(element, path + item.name + '/');\n                if (breakFlag.value) {\n                    return;\n                }\n            }\n            await readEntries();\n        };\n        await readEntries();\n    }\n};\n\nconst handleDragleave = (event: { preventDefault: () => void }) => {\n    event.preventDefault();\n};\n\nconst fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    if (_uploadFile.size == 64 || _uploadFile.size == 0) {\n        uploaderFiles.value = uploadFiles;\n        const reader = new FileReader();\n        reader.readAsDataURL(_uploadFile.raw);\n        reader.onload = async () => {};\n        reader.onerror = () => {\n            uploaderFiles.value = uploaderFiles.value.filter((file) => file.uid !== _uploadFile.uid);\n            MsgError(i18n.global.t('file.typeErrOrEmpty', [_uploadFile.name]));\n        };\n    } else {\n        uploaderFiles.value = uploadFiles;\n    }\n};\n\nconst clearFiles = () => {\n    uploadRef.value!.clearFiles();\n    uploaderFiles.value = [];\n};\n\nconst handleExceed: UploadProps['onExceed'] = () => {\n    clearFiles();\n    MsgWarning(i18n.global.t('file.uploadOverLimit'));\n};\n\nconst handleSuccess: UploadProps['onSuccess'] = (res, file) => {\n    file.status = 'success';\n};\n\nconst submit = async () => {\n    const files = uploaderFiles.value.slice();\n    const fileNamesWithPath = Array.from(\n        new Set(files.map((file) => `${path.value}/${file.raw.webkitRelativePath || file.name}`)),\n    );\n    const existFiles = await batchCheckFiles(fileNamesWithPath);\n    if (existFiles.data.length > 0) {\n        const fileSizeMap = new Map(\n            files.map((file) => [`${path.value}/${file.raw.webkitRelativePath || file.name}`, file.size]),\n        );\n        existFiles.data.forEach((file) => {\n            if (fileSizeMap.has(file.path)) {\n                file.uploadSize = fileSizeMap.get(file.path);\n            }\n        });\n        dialogExistFileRef.value.acceptParams({\n            paths: existFiles.data,\n            onConfirm: handleFileUpload,\n        });\n    } else {\n        await uploadFile(files);\n    }\n};\n\nconst handleFileUpload = (action: 'skip' | 'overwrite', skippedPaths: string[] = []) => {\n    const files = uploaderFiles.value.slice();\n    if (action === 'skip') {\n        const filteredFiles = files.filter(\n            (file) => !skippedPaths.includes(`${path.value}/${file.raw.webkitRelativePath || file.name}`),\n        );\n        uploaderFiles.value = filteredFiles;\n        uploadFile(filteredFiles);\n    } else if (action === 'overwrite') {\n        uploadFile(files);\n    }\n};\n\nconst uploadFile = async (files: any[]) => {\n    if (files.length == 0) {\n        clearFiles();\n    } else {\n        loading.value = true;\n        upLoading.value = true;\n        abortController.value = new AbortController();\n        let successCount = 0;\n        for (let i = 0; i < files.length; i++) {\n            const file = files[i];\n            uploadHelper.value = i18n.global.t('file.fileUploadStart', [file.name]);\n\n            if (abortController.value.signal.aborted) {\n                break;\n            }\n\n            let isSuccess =\n                file.size <= MAX_SINGLE_FILE_SIZE ? await uploadSingleFile(file) : await uploadLargeFile(file);\n\n            if (isSuccess) {\n                successCount++;\n                uploaderFiles.value[i].status = 'success';\n            } else {\n                uploaderFiles.value[i].status = 'fail';\n            }\n        }\n\n        loading.value = false;\n        upLoading.value = false;\n        uploadHelper.value = '';\n\n        if (successCount === files.length && !abortController.value.signal.aborted) {\n            clearFiles();\n            MsgSuccess(i18n.global.t('file.uploadSuccess'));\n        }\n    }\n};\n\nconst uploadSingleFile = async (file: { raw: string | Blob }) => {\n    const formData = new FormData();\n    formData.append('file', file.raw);\n    formData.append('path', getUploadPath(file));\n    formData.append('overwrite', 'True');\n    uploadPercent.value = 0;\n    await uploadFileData(formData, {\n        onUploadProgress: (progressEvent) => {\n            uploadPercent.value = Math.round((progressEvent.loaded / progressEvent.total) * 100);\n        },\n        timeout: 40000,\n        signal: abortController.value?.signal,\n    });\n    return true;\n};\n\nconst uploadLargeFile = async (file: { size: any; raw: string | Blob; name: string }) => {\n    const fileSize = file.size;\n    const chunkCount = Math.ceil(fileSize / CHUNK_SIZE);\n    let uploadedChunkCount = 0;\n    for (let c = 0; c < chunkCount; c++) {\n        if (abortController.value?.signal.aborted) {\n            return false;\n        }\n        const start = c * CHUNK_SIZE;\n        const end = Math.min(start + CHUNK_SIZE, fileSize);\n        const chunk = file.raw.slice(start, end);\n        const formData = new FormData();\n        formData.append('filename', getFilenameFromPath(file.name));\n        formData.append('path', getUploadPath(file));\n        formData.append('chunk', chunk);\n        formData.append('chunkIndex', c.toString());\n        formData.append('chunkCount', chunkCount.toString());\n\n        try {\n            await chunkUploadFileData(formData, {\n                onUploadProgress: (progressEvent) => {\n                    uploadPercent.value = Math.round(\n                        ((uploadedChunkCount + progressEvent.loaded / progressEvent.total) * 100) / chunkCount,\n                    );\n                },\n                timeout: TimeoutEnum.T_60S,\n                signal: abortController.value?.signal,\n            });\n            uploadedChunkCount++;\n        } catch (error) {\n            if (abortController.value?.signal.aborted) {\n                return false;\n            }\n            return false;\n        }\n    }\n\n    return uploadedChunkCount === chunkCount;\n};\n\nconst getUploadPath = (file) => {\n    return `${path.value}/${getPathWithoutFilename(file.raw.webkitRelativePath || file.name)}`;\n};\n\nconst getPathWithoutFilename = (path: string) => {\n    return path ? path.split('/').slice(0, -1).join('/') : path;\n};\n\nconst getFilenameFromPath = (path: string) => {\n    return path ? path.split('/').pop() : path;\n};\n\nconst acceptParams = (props: UploadFileProps) => {\n    path.value = props.path;\n    open.value = true;\n    uploadPercent.value = 0;\n    uploadHelper.value = '';\n\n    nextTick(() => {\n        state.uploadEle = document.querySelector('.el-upload__input');\n    });\n};\n\nonMounted(() => {\n    window.addEventListener('beforeunload', (e) => {\n        if (upLoading.value) {\n            if (upLoading.value) {\n                e.preventDefault();\n                e.returnValue = i18n.global.t('file.cancelUploadHelper');\n                return i18n.global.t('file.cancelUploadHelper');\n            }\n        }\n    });\n});\n\ndefineExpose({ acceptParams, handleDrop, open });\n</script>\n\n<style lang=\"scss\" scoped>\n.upload-button {\n    .el-button + .el-button {\n        margin-left: 0;\n    }\n}\n\n.file-item {\n    font-size: 14px;\n    color: #888;\n    position: relative;\n    display: flex;\n    align-items: center;\n}\n\n.file-item:hover {\n    background-color: #f5f5f5;\n}\n\n.file-icon {\n    margin-right: 8px;\n}\n\n.delete-button {\n    position: absolute;\n    right: 0;\n    top: 50%;\n    transform: translateY(-50%);\n}\n\n.success-icon {\n    color: green;\n    position: absolute;\n    right: 0;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/file-management/wget/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.download')\" @close=\"handleClose\" size=\"large\">\n        <el-form\n            ref=\"fileForm\"\n            label-position=\"top\"\n            :model=\"addForm\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('file.downloadUrl')\" prop=\"url\">\n                <el-input v-model=\"addForm.url\" @input=\"getFileName\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('file.path')\" prop=\"path\">\n                <el-input v-model=\"addForm.path\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ path: addForm.path })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model=\"addForm.name\"></el-input>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"addForm.ignoreCertificate\">\n                    {{ $t('file.ignoreCertificate') }}\n                </el-checkbox>\n                <span class=\"input-help\">{{ $t('file.ignoreCertificateHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose()\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(fileForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"getPath\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { wgetFile } from '@/api/modules/files';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { FormInstance, FormRules } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport FileList from '@/components/file-list/index.vue';\nimport { MsgSuccess } from '@/utils/message';\n\ninterface WgetProps {\n    path: string;\n}\n\nconst fileForm = ref<FormInstance>();\nconst loading = ref(false);\nlet open = ref(false);\nlet submitData = ref(false);\nconst fileRef = ref();\n\nconst rules = reactive<FormRules>({\n    name: [Rules.requiredInput],\n    path: [Rules.requiredInput],\n    url: [Rules.requiredInput],\n});\n\nconst addForm = reactive({\n    url: '',\n    path: '',\n    name: '',\n    ignoreCertificate: false,\n});\n\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    if (fileForm.value) {\n        fileForm.value.resetFields();\n    }\n    open.value = false;\n    em('close', submitData.value);\n};\n\nconst getPath = (path: string) => {\n    addForm.path = path;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        wgetFile(addForm)\n            .then(() => {\n                MsgSuccess(i18n.global.t('file.downloadStart'));\n                submitData.value = true;\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst getFileName = (url: string) => {\n    const paths = url.split('/');\n    addForm.name = paths[paths.length - 1];\n};\n\nconst acceptParams = (props: WgetProps) => {\n    addForm.path = props.path;\n    open.value = true;\n    submitData.value = false;\n    addForm.ignoreCertificate = false;\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/advance/index.vue",
    "content": "<template>\n    <div>\n        <FireRouter />\n\n        <div v-loading=\"loading\">\n            <FireStatus\n                ref=\"fireStatusRef\"\n                @search=\"search\"\n                v-model:loading=\"loading\"\n                v-model:mask-show=\"maskShow\"\n                v-model:is-active=\"isActive\"\n                v-model:name=\"fireName\"\n                current-tab=\"advance\"\n            />\n            <div v-if=\"fireName !== '-' && fireName !== 'iptables'\">\n                <LayoutContent :divider=\"true\">\n                    <template #main>\n                        <div class=\"app-warn\">\n                            <div class=\"flex flex-col gap-2 items-center justify-center w-full sm:flex-row\">\n                                <span>{{ $t('firewall.advancedControlNotAvailable', [fireName]) }}</span>\n                            </div>\n                            <div>\n                                <img src=\"@/assets/images/no_app.svg\" />\n                            </div>\n                        </div>\n                    </template>\n                </LayoutContent>\n            </div>\n\n            <div v-if=\"fireName === 'iptables'\">\n                <el-card v-if=\"!isActive && maskShow\" class=\"mask-prompt\">\n                    <span>{{ $t('firewall.firewallNotStart') }}</span>\n                </el-card>\n                <LayoutContent :title=\"$t('firewall.filterRule')\" :class=\"{ mask: !isActive }\">\n                    <template #prompt>\n                        <el-alert type=\"info\" :closable=\"false\" :title=\"loadPrompt()\" />\n                    </template>\n                    <template #leftToolBar>\n                        <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                            {{ $t('firewall.create') }}\n                        </el-button>\n                        <el-button v-if=\"isBind\" plain @click=\"onUnBind\">\n                            {{ $t('commons.button.unbind') }}\n                        </el-button>\n                        <el-button v-if=\"!isBind\" plain @click=\"onBind\">\n                            {{ $t('commons.button.bind') }}\n                        </el-button>\n                        <el-button @click=\"onDelete(null)\" plain :disabled=\"selects.length === 0\">\n                            {{ $t('commons.button.delete') }}\n                        </el-button>\n                    </template>\n\n                    <template #rightToolBar>\n                        <el-select v-model=\"selectedChain\" @change=\"search()\" clearable class=\"p-w-200\">\n                            <template #prefix>{{ $t('firewall.chain') }}</template>\n                            <el-option :label=\"$t('firewall.inboundDirection')\" value=\"1PANEL_INPUT\"></el-option>\n                            <el-option :label=\"$t('firewall.outboundDirection')\" value=\"1PANEL_OUTPUT\"></el-option>\n                        </el-select>\n                        <TableRefresh @search=\"search()\" />\n                        <TableSetting title=\"firewall-filter-refresh\" @search=\"search()\" />\n                    </template>\n\n                    <template #main>\n                        <ComplexTable\n                            :pagination-config=\"paginationConfig\"\n                            v-model:selects=\"selects\"\n                            @search=\"search\"\n                            :data=\"data\"\n                            :heightDiff=\"220\"\n                        >\n                            <el-table-column type=\"selection\" fix />\n                            <el-table-column :label=\"$t('commons.table.protocol')\" :min-width=\"80\" prop=\"protocol\">\n                                <template #default=\"{ row }\">\n                                    {{ row.protocol === '' ? 'ALL' : row.protocol }}\n                                </template>\n                            </el-table-column>\n                            <el-table-column :label=\"$t('firewall.sourceIP')\" :min-width=\"120\" prop=\"srcIP\">\n                                <template #default=\"{ row }\">\n                                    {{ formatIP(row.srcIP) }}\n                                </template>\n                            </el-table-column>\n                            <el-table-column :label=\"$t('firewall.sourcePort')\" :min-width=\"100\" prop=\"sourcePort\">\n                                <template #default=\"{ row }\">\n                                    {{ formatPort(row.srcPort) }}\n                                </template>\n                            </el-table-column>\n                            <el-table-column :label=\"$t('firewall.destIP')\" :min-width=\"120\" prop=\"dstIP\">\n                                <template #default=\"{ row }\">\n                                    {{ formatIP(row.dstIP) }}\n                                </template>\n                            </el-table-column>\n                            <el-table-column :label=\"$t('firewall.destPort')\" :min-width=\"100\" prop=\"dstPort\">\n                                <template #default=\"{ row }\">\n                                    {{ formatPort(row.dstPort) }}\n                                </template>\n                            </el-table-column>\n                            <el-table-column :min-width=\"100\" :label=\"$t('firewall.action')\" prop=\"strategy\">\n                                <template #default=\"{ row }\">\n                                    <el-tag v-if=\"row.strategy === 'accept'\" type=\"success\">\n                                        {{ $t('firewall.accept') }}\n                                    </el-tag>\n                                    <el-tag v-else-if=\"row.strategy === 'drop'\" type=\"danger\">\n                                        {{ $t('firewall.drop') }}\n                                    </el-tag>\n                                    <el-tag v-else-if=\"row.strategy === 'reject'\" type=\"warning\">\n                                        {{ row.strategy }}\n                                    </el-tag>\n                                    <el-tag v-else type=\"info\">{{ row.strategy }}</el-tag>\n                                </template>\n                            </el-table-column>\n                            <el-table-column\n                                :min-width=\"150\"\n                                :label=\"$t('commons.table.description')\"\n                                prop=\"description\"\n                                show-overflow-tooltip\n                            >\n                                <template #default=\"{ row }\">\n                                    <fu-input-rw-switch\n                                        v-model=\"row.description\"\n                                        @enter=\"onChange(row)\"\n                                        @blur=\"onChange(row)\"\n                                    />\n                                </template>\n                            </el-table-column>\n                            <fu-table-operations\n                                width=\"120px\"\n                                :buttons=\"buttons\"\n                                :ellipsis=\"10\"\n                                :label=\"$t('commons.table.operate')\"\n                                fix\n                            />\n                        </ComplexTable>\n                    </template>\n                </LayoutContent>\n            </div>\n        </div>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport FireRouter from '@/views/host/firewall/index.vue';\nimport FireStatus from '@/views/host/firewall/status/index.vue';\nimport OperateDialog from '@/views/host/firewall/advance/operate/index.vue';\nimport { onMounted, reactive, ref } from 'vue';\nimport {\n    searchFilterRules,\n    batchOperateFilterRule,\n    loadChainStatus,\n    operateFilterChain,\n    updateFirewallDescription,\n} from '@/api/modules/host';\nimport { Host } from '@/api/interface/host';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst selects = ref<any>([]);\nconst selectedChain = ref('1PANEL_INPUT');\nconst defaultStrategy = ref('ACCEPT');\n\nconst maskShow = ref(true);\nconst isActive = ref(false);\nconst isBind = ref(false);\nconst fireName = ref();\nconst fireStatusRef = ref();\n\nconst opRef = ref();\n\nconst data = ref();\n\nconst formatPort = (port?: number | null | string) => {\n    if (port === '' || port === 0 || port === '0') {\n        return i18n.global.t('firewall.allPorts');\n    }\n    if (port === undefined || port === null) {\n        return '-';\n    }\n    return port;\n};\nconst formatIP = (ip?: null | string) => {\n    if (ip) {\n        return ip === 'anywhere' ? i18n.global.t('firewall.anyWhere') : ip;\n    }\n    return i18n.global.t('firewall.anyWhere');\n};\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'firewall-filter-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('firewall-filter-page-size')) || 20,\n    total: 0,\n});\n\nconst search = async () => {\n    if (!isActive.value) {\n        loading.value = false;\n        paginationConfig.total = 0;\n        return;\n    }\n    let params = {\n        type: selectedChain.value,\n        info: '',\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    loadStatus();\n    await searchFilterRules(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadPrompt = () => {\n    if (isBind.value) {\n        return i18n.global.t('firewall.defaultStrategy', [selectedChain.value, defaultStrategy.value]);\n    }\n    return i18n.global.t('firewall.defaultStrategy2', [selectedChain.value, defaultStrategy.value]);\n};\n\nconst loadStatus = async () => {\n    await loadChainStatus(selectedChain.value).then((res) => {\n        isBind.value = res.data.isBind;\n        defaultStrategy.value = res.data.defaultStrategy || 'ACCEPT';\n    });\n};\nconst onBind = async () => {\n    ElMessageBox.confirm(i18n.global.t('firewall.bindHelper'), i18n.global.t('commons.button.bind'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await operateFilterChain(selectedChain.value, 'bind').then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loadStatus();\n        });\n    });\n};\nconst onUnBind = async () => {\n    ElMessageBox.confirm(i18n.global.t('firewall.unbindHelper'), i18n.global.t('commons.button.unbind'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await operateFilterChain(selectedChain.value, 'unbind').then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loadStatus();\n        });\n    });\n};\n\nconst dialogRef = ref();\nconst onOpenDialog = async (title: string, rowData?: Host.IptablesFilterRuleOp) => {\n    const params = {\n        title,\n        rowData: rowData || {\n            chain: selectedChain.value,\n            protocol: 'tcp',\n            strategy: 'accept',\n            srcPort: 0,\n            dstPort: 0,\n        },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onDelete = async (row: Host.IptablesRules | null) => {\n    let names = [];\n    let rules = [];\n    if (row) {\n        rules.push({\n            operation: 'remove',\n            id: row.id,\n            chain: selectedChain.value,\n            srcPort: Number(row.srcPort),\n            dstPort: Number(row.dstPort),\n            srcIP: row.srcIP === 'anywhere' ? '' : row.srcIP,\n            dstIP: row.dstIP === 'anywhere' ? '' : row.dstIP,\n            protocol: row.protocol,\n            strategy: row.strategy,\n        });\n        names = [\n            `${row.protocol} ${row.srcIP || '*'}:${row.srcPort || '*'} -> ${row.dstIP || '*'}:${row.dstPort || '*'}`,\n        ];\n    } else {\n        for (const item of selects.value) {\n            names.push(\n                `${item.protocol} ${item.srcIP || '*'}:${item.srcPort || '*'} -> ${item.dstIP || '*'}:${\n                    item.dstPort || '*'\n                }`,\n            );\n            rules.push({\n                operation: 'remove',\n                id: item.id,\n                chain: selectedChain.value,\n                srcPort: Number(item.srcPort),\n                dstPort: Number(item.dstPort),\n                srcIP: item.srcIP === 'anywhere' ? '' : item.srcIP,\n                dstIP: item.dstIP === 'anywhere' ? '' : item.dstIP,\n                protocol: item.protocol,\n                strategy: item.strategy,\n            });\n        }\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('firewall.deleteRuleConfirm', [rules.length]),\n        api: batchOperateFilterRule,\n        params: { rules: rules },\n    });\n};\n\nconst onChange = async (row: any) => {\n    let params = {\n        type: 'advance',\n        chain: selectedChain.value,\n        srcIP: row.srcIP,\n        dstIP: row.dstIP,\n        srcPort: row.srcPort,\n        dstPort: row.dstPort,\n        protocol: row.protocol,\n        strategy: row.strategy,\n\n        description: row.description,\n    };\n    await updateFirewallDescription(params);\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Host.IptablesRules) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    if (fireName.value !== '-') {\n        loading.value = true;\n        fireStatusRef.value.acceptParams();\n    }\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.chain-card {\n    .chain-title {\n        font-size: 16px;\n        font-weight: 500;\n        margin-bottom: 8px;\n        display: flex;\n        align-items: center;\n        gap: 8px;\n    }\n    .chain-policy {\n        font-size: 14px;\n        color: var(--el-text-color-secondary);\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/advance/operate/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"title\" @close=\"handleClose\" size=\"large\">\n        <div class=\"mb-2\">\n            <el-alert :closable=\"false\" :title=\"$t('firewall.ipv4Limit')\" />\n        </div>\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('commons.table.protocol')\" prop=\"protocol\">\n                <el-select class=\"w-full\" v-model=\"dialogData.rowData!.protocol\" @change=\"changeProtocol\">\n                    <el-option value=\"all\" label=\"all\" />\n                    <el-option value=\"tcp\" label=\"tcp\" />\n                    <el-option value=\"udp\" label=\"udp\" />\n                    <el-option value=\"icmp\" label=\"icmp\" />\n                </el-select>\n            </el-form-item>\n\n            <el-form-item\n                v-if=\"dialogData.rowData?.chain === '1PANEL_INPUT'\"\n                :label=\"$t('firewall.sourceIP')\"\n                prop=\"srcIP\"\n            >\n                <el-input clearable v-model.trim=\"dialogData.rowData!.srcIP\" placeholder=\"0.0.0.0/0\" />\n                <span class=\"input-help\">{{ $t('firewall.sourceIPHelper') }}</span>\n            </el-form-item>\n\n            <el-form-item\n                v-if=\"dialogData.rowData?.chain === '1PANEL_OUTPUT'\"\n                :label=\"$t('firewall.destIP')\"\n                prop=\"dstIP\"\n            >\n                <el-input clearable v-model.trim=\"dialogData.rowData!.dstIP\" placeholder=\"0.0.0.0/0\" />\n                <span class=\"input-help\">{{ $t('firewall.destIPHelper') }}</span>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('firewall.destPort')\" prop=\"dstPort\">\n                <el-input-number\n                    class=\"w-full\"\n                    v-model=\"dialogData.rowData!.dstPort\"\n                    :min=\"0\"\n                    :max=\"65535\"\n                    :disabled=\"dialogData.rowData?.protocol === 'all'\"\n                />\n                <span class=\"input-help\">{{ $t('firewall.portHelper') }}</span>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('firewall.action')\" prop=\"strategy\">\n                <el-radio-group v-model=\"dialogData.rowData!.strategy\">\n                    <el-radio value=\"accept\">{{ $t('firewall.accept') }}</el-radio>\n                    <el-radio value=\"drop\">{{ $t('firewall.drop') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { Host } from '@/api/interface/host';\nimport { operateFilterRule } from '@/api/modules/host';\nimport { checkCidr, checkCidrV6, checkIpV4V6 } from '@/utils/util';\n\nconst loading = ref();\n\ninterface DialogProps {\n    title: string;\n    rowData?: Host.IptablesFilterRuleOp;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    title.value = i18n.global.t('firewall.' + dialogData.value.title);\n    if (dialogData.value.rowData.chain === '1PANEL_INPUT') {\n        dialogData.value.rowData.dstIP = '';\n    } else if (dialogData.value.rowData.chain === '1PANEL_OUTPUT') {\n        dialogData.value.rowData.srcIP = '';\n    }\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    chain: [Rules.requiredSelect],\n    protocol: [Rules.requiredSelect],\n    strategy: [Rules.requiredSelect],\n    srcIP: [{ validator: checkIPAddress, trigger: 'blur' }],\n    dstIP: [{ validator: checkIPAddress, trigger: 'blur' }],\n});\n\nfunction checkIPAddress(_rule: any, value: any, callback: any) {\n    if (!value) {\n        return callback();\n    }\n    if (value.indexOf('/') !== -1) {\n        if (value.indexOf(':') !== -1) {\n            if (checkCidrV6(value)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        } else {\n            if (checkCidr(value)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        }\n    } else {\n        if (checkIpV4V6(value)) {\n            return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n        }\n    }\n    callback();\n}\n\nconst changeProtocol = () => {\n    if (dialogData.value.rowData.protocol === 'all') {\n        dialogData.value.rowData.srcPort = 0;\n        dialogData.value.rowData.dstPort = 0;\n    }\n};\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        dialogData.value.rowData.operation = 'add';\n        if (!dialogData.value.rowData) return;\n\n        loading.value = true;\n        await operateFilterRule(dialogData.value.rowData)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/forward/import/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"visible\" :title=\"$t('commons.button.import')\" size=\"large\">\n        <div>\n            <el-alert :closable=\"false\" show-icon type=\"info\">\n                <template #default>\n                    <div>{{ $t('commons.msg.importHelper') }}</div>\n                </template>\n            </el-alert>\n            <el-upload\n                action=\"#\"\n                :auto-upload=\"false\"\n                ref=\"uploadRef\"\n                class=\"float-left mt-2\"\n                :show-file-list=\"false\"\n                :limit=\"1\"\n                accept=\".json\"\n                :on-change=\"fileOnChange\"\n                :on-exceed=\"handleExceed\"\n                v-model:file-list=\"uploaderFiles\"\n            >\n                <el-button class=\"float-left\" type=\"primary\">{{ $t('commons.button.upload') }}</el-button>\n            </el-upload>\n\n            <el-card class=\"mt-2 w-full\" v-loading=\"loading\">\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    @search=\"search\"\n                    v-model:selects=\"selects\"\n                    :data=\"pageData\"\n                    :height=\"440\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column :label=\"$t('commons.table.status')\" :min-width=\"80\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.protocol')\" :min-width=\"70\" prop=\"protocol\" />\n                    <el-table-column :label=\"$t('firewall.sourcePort')\" :min-width=\"70\" prop=\"port\" />\n                    <el-table-column :label=\"$t('firewall.targetIP')\" :min-width=\"100\" prop=\"targetIP\" />\n                    <el-table-column :label=\"$t('firewall.targetPort')\" :min-width=\"70\" prop=\"targetPort\" />\n                    <el-table-column\n                        v-if=\"currentFireName === 'ufw'\"\n                        :label=\"$t('firewall.forwardInboundInterface')\"\n                        :min-width=\"100\"\n                        prop=\"interface\"\n                    >\n                        <template #default=\"{ row }\">\n                            <span>\n                                {{\n                                    row.interface === '' || row.interface === 'all'\n                                        ? $t('commons.table.all')\n                                        : row.interface\n                                }}\n                            </span>\n                        </template>\n                    </el-table-column>\n                </ComplexTable>\n            </el-card>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"selects.length === 0\" @click=\"onImport\">\n                    {{ $t('commons.button.import') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { genFileId, UploadFile, UploadFiles, UploadProps, UploadRawFile } from 'element-plus';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { operateForwardRule, searchFireRule, getNetworkOptions } from '@/api/modules/host';\nimport { Host } from '@/api/interface/host';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst visible = ref(false);\nconst loading = ref(false);\nconst selects = ref<any>([]);\nconst displayData = ref<any>([]);\nconst currentRules = ref<Host.RuleInfo[]>([]);\nconst currentFireName = ref('');\nconst availableInterfaces = ref<string[]>([]);\n\nconst uploadRef = ref();\nconst uploaderFiles = ref();\nconst pageData = ref([]);\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst acceptParams = async (fireName: string): Promise<void> => {\n    visible.value = true;\n    displayData.value = [];\n    selects.value = [];\n    currentFireName.value = fireName;\n    loadCurrentData(fireName);\n};\n\nconst loadCurrentData = async (fireName: string) => {\n    const res = await searchFireRule({\n        type: 'forward',\n        strategy: '',\n        info: '',\n        page: 1,\n        pageSize: 10000,\n    });\n    currentRules.value = res.data.items || [];\n    if (fireName === 'ufw') {\n        const networkRes = await getNetworkOptions();\n        availableInterfaces.value = networkRes.data || [];\n    }\n};\n\nconst search = () => {\n    const startIndex = (paginationConfig.currentPage - 1) * paginationConfig.pageSize;\n    const endIndex = startIndex + paginationConfig.pageSize;\n    pageData.value = displayData.value.slice(startIndex, endIndex);\n};\n\nconst fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    loading.value = true;\n    displayData.value = [];\n    uploaderFiles.value = uploadFiles;\n\n    const reader = new FileReader();\n    reader.onload = (e) => {\n        try {\n            const content = e.target.result as string;\n            const parsed = JSON.parse(content);\n\n            if (!Array.isArray(parsed)) {\n                MsgError(i18n.global.t('commons.msg.errImportFormat'));\n                loading.value = false;\n                return;\n            }\n\n            for (const item of parsed) {\n                if (!checkDataFormat(item)) {\n                    MsgError(i18n.global.t('commons.msg.errImportFormat'));\n                    loading.value = false;\n                    return;\n                }\n            }\n\n            compareRules(parsed);\n            loading.value = false;\n        } catch (error) {\n            MsgError(i18n.global.t('commons.msg.errImport') + error.message);\n            loading.value = false;\n        }\n    };\n    reader.readAsText(_uploadFile.raw);\n};\n\nconst handleExceed: UploadProps['onExceed'] = (files) => {\n    uploadRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadRef.value!.handleStart(file);\n};\n\nconst checkDataFormat = (item: any): boolean => {\n    if (!item.protocol || !item.port || !item.targetIP || !item.targetPort) {\n        return false;\n    }\n    if (!['tcp', 'udp', 'tcp/udp'].includes(item.protocol)) {\n        return false;\n    }\n\n    if (currentFireName.value === 'ufw' && item.interface !== undefined && item.interface !== null) {\n        const interfaceValue = item.interface;\n        if (interfaceValue !== '' && interfaceValue !== 'all') {\n            if (!availableInterfaces.value.includes(interfaceValue)) {\n                return false;\n            }\n        }\n    }\n\n    return true;\n};\n\nconst compareRules = (importedRules: any[]) => {\n    const newRules: any[] = [];\n    const conflictRules: any[] = [];\n    const duplicateRules: any[] = [];\n\n    for (const importedRule of importedRules) {\n        const key = `${importedRule.protocol}:${importedRule.port}:${importedRule.targetIP}:${importedRule.targetPort}`;\n\n        const existingRule = currentRules.value.find((rule) => {\n            const existingKey = `${rule.protocol}:${rule.port}:${rule.targetIP}:${rule.targetPort}`;\n            return existingKey === key;\n        });\n\n        if (!existingRule) {\n            newRules.push({ ...importedRule, status: 'new' });\n        } else {\n            duplicateRules.push({ ...importedRule, status: 'duplicate' });\n        }\n    }\n\n    displayData.value = [...newRules, ...conflictRules, ...duplicateRules];\n    paginationConfig.total = displayData.value.length;\n    search();\n};\n\nconst onImport = async () => {\n    loading.value = true;\n    const rules: Host.RuleForward[] = [];\n    for (const rule of selects.value) {\n        rules.push({\n            operation: 'add',\n            protocol: rule.protocol,\n            port: rule.port,\n            targetIP: rule.targetIP,\n            targetPort: rule.targetPort,\n            interface: rule.interface || '',\n        });\n    }\n\n    await operateForwardRule({ rules })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            emit('search');\n            visible.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/forward/index.vue",
    "content": "<template>\n    <div>\n        <FireRouter />\n\n        <div v-loading=\"loading\">\n            <FireStatus\n                ref=\"fireStatusRef\"\n                @search=\"search\"\n                v-model:loading=\"loading\"\n                v-model:mask-show=\"maskShow\"\n                v-model:is-active=\"isActive\"\n                v-model:name=\"fireName\"\n                current-tab=\"forward\"\n            />\n            <div v-if=\"fireName !== '-'\">\n                <el-card v-if=\"!isActive && maskShow\" class=\"mask-prompt\">\n                    <span>{{ $t('firewall.firewallNotStart') }}</span>\n                </el-card>\n\n                <LayoutContent :title=\"$t('firewall.forwardRule', 2)\" :class=\"{ mask: !isActive }\">\n                    <template #leftToolBar>\n                        <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                            {{ $t('commons.button.create') }}\n                        </el-button>\n                        <el-button @click=\"onDelete(null)\" plain :disabled=\"selects.length === 0\">\n                            {{ $t('commons.button.delete') }}\n                        </el-button>\n                        <el-button-group>\n                            <el-button @click=\"onImport\">\n                                {{ $t('commons.button.import') }}\n                            </el-button>\n                            <el-button :disabled=\"selects.length === 0\" @click=\"onExport\">\n                                {{ $t('commons.button.export') }}\n                            </el-button>\n                        </el-button-group>\n                    </template>\n                    <template #rightToolBar>\n                        <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                        <TableRefresh @search=\"search()\" />\n                        <TableSetting title=\"firewall-forward-refresh\" @search=\"search()\" />\n                    </template>\n                    <template #main>\n                        <ComplexTable\n                            :pagination-config=\"paginationConfig\"\n                            v-model:selects=\"selects\"\n                            @search=\"search\"\n                            :data=\"data\"\n                            :heightDiff=\"370\"\n                        >\n                            <el-table-column type=\"selection\" fix />\n                            <el-table-column :label=\"$t('commons.table.protocol')\" :min-width=\"70\" prop=\"protocol\" />\n                            <el-table-column :label=\"$t('firewall.sourcePort')\" :min-width=\"70\" prop=\"port\" />\n                            <el-table-column :min-width=\"80\" :label=\"$t('firewall.targetIP')\" prop=\"targetIP\" />\n                            <el-table-column :label=\"$t('firewall.targetPort')\" :min-width=\"70\" prop=\"targetPort\" />\n                            <template v-if=\"fireName === 'ufw'\">\n                                <el-table-column\n                                    :label=\"$t('firewall.forwardInboundInterface')\"\n                                    :min-width=\"70\"\n                                    prop=\"interface\"\n                                >\n                                    <template #default=\"{ row }\">\n                                        <span>\n                                            {{ row.interface === '' ? $t('commons.table.all') : row.interface }}\n                                        </span>\n                                    </template>\n                                </el-table-column>\n                            </template>\n                            <fu-table-operations\n                                width=\"200px\"\n                                :buttons=\"buttons\"\n                                :ellipsis=\"10\"\n                                :label=\"$t('commons.table.operate')\"\n                                fix\n                            />\n                        </ComplexTable>\n                    </template>\n                </LayoutContent>\n            </div>\n        </div>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"onSubmitDelete()\">\n            <template #content>\n                <el-form class=\"mt-4 mb-1\" ref=\"deleteForm\" label-position=\"left\">\n                    <el-form-item>\n                        <el-checkbox v-model=\"forceDelete\" :label=\"$t('website.forceDelete')\" />\n                        <span class=\"input-help\">\n                            {{ $t('website.forceDeleteHelper') }}\n                        </span>\n                    </el-form-item>\n                </el-form>\n            </template>\n        </OpDialog>\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <ImportDialog @search=\"search\" ref=\"dialogImportRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport FireRouter from '@/views/host/firewall/index.vue';\nimport OperateDialog from './operate/index.vue';\nimport ImportDialog from './import/index.vue';\nimport FireStatus from '@/views/host/firewall/status/index.vue';\nimport { onMounted, reactive, ref } from 'vue';\nimport { operateForwardRule, searchFireRule } from '@/api/modules/host';\nimport { Host } from '@/api/interface/host';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { downloadWithContent, getCurrentDateFormatted } from '@/utils/util';\n\nconst loading = ref();\nconst activeTag = ref('forward');\nconst selects = ref<any>([]);\nconst searchName = ref();\nconst searchStrategy = ref('');\n\nconst maskShow = ref(true);\nconst isActive = ref(false);\nconst fireName = ref();\nconst fireStatusRef = ref();\n\nconst opRef = ref();\nconst dialogImportRef = ref();\nconst forceDelete = ref(false);\nconst operateRules = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'firewall-forward-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('firewall-forward-page-size')) || 20,\n    total: 0,\n});\n\nconst search = async () => {\n    if (!isActive.value) {\n        loading.value = false;\n        data.value = [];\n        paginationConfig.total = 0;\n        return;\n    }\n    let params = {\n        type: activeTag.value,\n        strategy: searchStrategy.value,\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchFireRule(params)\n        .then((res) => {\n            loading.value = false;\n            data.value =\n                res.data.items?.map((item) => {\n                    return {\n                        ...item,\n                        interface: item.interface === '*' ? '' : item.interface,\n                    };\n                }) || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst dialogRef = ref();\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Host.RuleForward> = {\n        protocol: 'tcp',\n        port: '8080',\n        targetIP: '',\n        targetPort: '',\n        interface: '',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n        fireName: fireName.value,\n    };\n    dialogRef.value!.acceptParams(params);\n};\nconst onDelete = async (row: Host.RuleForward | null) => {\n    let names = [];\n    let rules = [];\n    if (row) {\n        rules.push({\n            ...row,\n            operation: 'remove',\n        });\n        names = [row.port + ' (' + row.protocol + ')'];\n    } else {\n        for (const item of selects.value) {\n            names.push(item.port + ' (' + item.protocol + ')');\n            rules.push({\n                ...item,\n                operation: 'remove',\n            });\n        }\n    }\n    operateRules.value = rules;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('firewall.forwardRule'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\nconst onSubmitDelete = async () => {\n    loading.value = true;\n    await operateForwardRule({ rules: operateRules.value, forceDelete: forceDelete.value })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onImport = () => {\n    dialogImportRef.value.acceptParams(fireName.value);\n};\n\nconst onExport = () => {\n    ElMessageBox.confirm(\n        i18n.global.t('firewall.exportHelper', [selects.value.length]),\n        i18n.global.t('commons.button.export'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    ).then(async () => {\n        const exportData = selects.value.map((item: Host.RuleInfo) => ({\n            family: item.family,\n            protocol: item.protocol,\n            port: item.port,\n            targetIP: item.targetIP,\n            targetPort: item.targetPort,\n            interface: item.interface,\n        }));\n        const content = JSON.stringify(exportData, null, 2);\n        const fileName = `1panel-firewall-forward-${getCurrentDateFormatted()}.json`;\n        downloadWithContent(content, fileName);\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Host.RuleForward) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Host.RuleForward) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    forceDelete.value = false;\n    if (fireName.value !== '-') {\n        loading.value = true;\n        fireStatusRef.value.acceptParams();\n    }\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.svg-icon {\n    font-size: 8px;\n    margin-bottom: -4px;\n    cursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/forward/operate/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"title\" @close=\"handleClose\" size=\"large\">\n        <div class=\"mb-2\">\n            <el-alert :closable=\"false\" :title=\"$t('firewall.ipv4Limit')\" />\n        </div>\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('commons.table.protocol')\" prop=\"protocol\">\n                <el-select class=\"w-full\" v-model=\"dialogData.rowData!.protocol\">\n                    <el-option value=\"tcp\" label=\"tcp\" />\n                    <el-option value=\"udp\" label=\"udp\" />\n                    <el-option value=\"tcp/udp\" label=\"tcp/udp\" />\n                </el-select>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('firewall.sourcePort')\" prop=\"port\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.port\" />\n                <span class=\"input-help\">{{ $t('firewall.forwardPortHelper') }}</span>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('firewall.targetIP')\" prop=\"targetIP\">\n                <el-input v-model.trim=\"dialogData.rowData!.targetIP\" />\n                <span class=\"input-help\">{{ $t('firewall.forwardHelper1') }}</span>\n                <span class=\"input-help\">{{ $t('firewall.forwardHelper2') }}</span>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('firewall.targetPort')\" prop=\"targetPort\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.targetPort\" />\n                <span class=\"input-help\">{{ $t('firewall.forwardPortHelper') }}</span>\n            </el-form-item>\n\n            <el-form-item\n                v-if=\"dialogData.fireName !== 'firewalld'\"\n                :label=\"$t('firewall.forwardInboundInterface')\"\n                prop=\"interface\"\n            >\n                <el-select class=\"w-full\" v-model=\"dialogData.rowData!.interface\">\n                    <el-option\n                        v-for=\"item in interfaceOptions\"\n                        :key=\"item.value\"\n                        :label=\"item.label === 'all' ? $t('commons.table.all') : item.label\"\n                        :value=\"item.value\"\n                    />\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { Host } from '@/api/interface/host';\nimport { operateForwardRule, getNetworkOptions } from '@/api/modules/host';\nimport { checkCidr, checkCidrV6, checkIp, checkPort, deepCopy } from '@/utils/util';\n\nconst loading = ref();\nconst oldRule = ref<Host.RuleForward>();\n\nconst interfaceOptions = ref<Array<{ label: string; value: string }>>([]);\n\ninterface DialogProps {\n    title: string;\n    rowData?: Host.RuleForward;\n    fireName?: string;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    if (dialogData.value.title === 'edit') {\n        oldRule.value = deepCopy(params.rowData);\n    }\n    getNetworkOptions().then((res) => {\n        interfaceOptions.value = res.data.map((item) => ({ label: item, value: item }));\n        dialogData.value.rowData!.interface = dialogData.value.rowData!.interface || 'all';\n    });\n    title.value = i18n.global.t('firewall.' + dialogData.value.title);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    protocol: [Rules.requiredSelect],\n    port: [{ validator: checkPortRule, trigger: 'blur', required: true }],\n    targetPort: [{ validator: checkPortRule, trigger: 'blur', required: true }],\n    targetIP: [{ validator: checkAddress, trigger: 'blur' }],\n});\n\nfunction checkPortRule(rule: any, value: string, callback: any) {\n    if (!value) {\n        return callback(new Error(i18n.global.t('firewall.portFormatError')));\n    }\n    if (value.indexOf('-') !== -1) {\n        const ports = value.split('-');\n        if (ports.length !== 2) {\n            return callback(new Error(i18n.global.t('firewall.portFormatError')));\n        }\n        if (checkPort(ports[0]) || checkPort(ports[1])) {\n            return callback(new Error(i18n.global.t('firewall.portFormatError')));\n        }\n        if (Number(ports[0]) > Number(ports[1])) {\n            return callback(new Error(i18n.global.t('firewall.portFormatError')));\n        }\n    } else {\n        if (checkPort(value)) {\n            return callback(new Error(i18n.global.t('firewall.portFormatError')));\n        }\n    }\n    callback();\n}\nfunction checkAddress(rule: any, value: string, callback: any) {\n    if (!value) {\n        return callback();\n    }\n    let addrs = value.split(',');\n    for (const item of addrs) {\n        if (item.indexOf('/') !== -1) {\n            if (item.indexOf(':') !== -1) {\n                if (checkCidrV6(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            } else {\n                if (checkCidr(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            }\n        } else {\n            if (checkIp(item)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        }\n    }\n    callback();\n}\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        const { rowData } = dialogData.value;\n        let rules = [];\n        if (!rowData) return;\n        rowData.operation = 'add';\n        if (rowData.targetIP === '') {\n            rowData.targetIP = '127.0.0.1';\n        }\n        if (rowData.interface === 'all') {\n            rowData.interface = '';\n        }\n        rules.push(rowData);\n        loading.value = true;\n        if (dialogData.value.title === 'create') {\n            await operateForwardRule({ rules: rules })\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n            return;\n        }\n        rules = [];\n        oldRule.value.operation = 'remove';\n        dialogData.value.rowData.operation = 'add';\n        rules.push(oldRule.value);\n        rules.push(dialogData.value.rowData);\n        await operateForwardRule({ rules: rules })\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('firewall.portRule', 2),\n        path: '/hosts/firewall/port',\n    },\n    {\n        label: i18n.global.t('firewall.forwardRule', 2),\n        path: '/hosts/firewall/forward',\n    },\n    {\n        label: i18n.global.t('firewall.ipRule', 2),\n        path: '/hosts/firewall/ip',\n    },\n    {\n        label: 'iptables ' + i18n.global.t('firewall.advancedControl'),\n        path: '/hosts/firewall/advance',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/ip/import/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"visible\" :title=\"$t('commons.button.import')\" size=\"large\">\n        <div>\n            <el-alert :closable=\"false\" show-icon type=\"info\">\n                <template #default>\n                    <div>{{ $t('commons.msg.importHelper') }}</div>\n                </template>\n            </el-alert>\n            <el-upload\n                action=\"#\"\n                :auto-upload=\"false\"\n                ref=\"uploadRef\"\n                class=\"float-left mt-2\"\n                :show-file-list=\"false\"\n                :limit=\"1\"\n                accept=\".json\"\n                :on-change=\"fileOnChange\"\n                :on-exceed=\"handleExceed\"\n                v-model:file-list=\"uploaderFiles\"\n            >\n                <el-button class=\"float-left\" type=\"primary\">{{ $t('commons.button.upload') }}</el-button>\n            </el-upload>\n\n            <el-card class=\"mt-2 w-full\" v-loading=\"loading\">\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    @search=\"search\"\n                    v-model:selects=\"selects\"\n                    :data=\"pageData\"\n                    :height=\"440\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column :label=\"$t('commons.table.status')\" :min-width=\"80\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('firewall.address')\" :min-width=\"100\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.address && row.address !== 'Anywhere'\">{{ row.address }}</span>\n                            <span v-else>{{ $t('firewall.allIP') }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('firewall.strategy')\" :min-width=\"80\" prop=\"strategy\">\n                        <template #default=\"{ row }\">\n                            {{ row.strategy === 'accept' ? $t('firewall.allow') : $t('firewall.deny') }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('commons.table.description')\"\n                        :min-width=\"120\"\n                        prop=\"description\"\n                        show-overflow-tooltip\n                    />\n                </ComplexTable>\n            </el-card>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"selects.length === 0\" @click=\"onImport\">\n                    {{ $t('commons.button.import') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { genFileId, UploadFile, UploadFiles, UploadProps, UploadRawFile } from 'element-plus';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { operateIPRule, searchFireRule } from '@/api/modules/host';\nimport { Host } from '@/api/interface/host';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst visible = ref(false);\nconst loading = ref(false);\nconst selects = ref<any>([]);\nconst displayData = ref<any>([]);\nconst currentRules = ref<Host.RuleInfo[]>([]);\n\nconst uploadRef = ref();\nconst uploaderFiles = ref();\nconst pageData = ref([]);\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst acceptParams = async (): Promise<void> => {\n    visible.value = true;\n    displayData.value = [];\n    selects.value = [];\n    loadCurrentData();\n};\n\nconst loadCurrentData = async () => {\n    const res = await searchFireRule({\n        type: 'address',\n        strategy: '',\n        info: '',\n        page: 1,\n        pageSize: 10000,\n    });\n    currentRules.value = res.data.items || [];\n};\n\nconst search = () => {\n    const startIndex = (paginationConfig.currentPage - 1) * paginationConfig.pageSize;\n    const endIndex = startIndex + paginationConfig.pageSize;\n    pageData.value = displayData.value.slice(startIndex, endIndex);\n};\n\nconst fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    loading.value = true;\n    displayData.value = [];\n    uploaderFiles.value = uploadFiles;\n\n    const reader = new FileReader();\n    reader.onload = (e) => {\n        try {\n            const content = e.target.result as string;\n            const parsed = JSON.parse(content);\n\n            if (!Array.isArray(parsed)) {\n                MsgError(i18n.global.t('commons.msg.errImportFormat'));\n                loading.value = false;\n                return;\n            }\n\n            for (const item of parsed) {\n                if (!checkDataFormat(item)) {\n                    MsgError(i18n.global.t('commons.msg.errImportFormat'));\n                    loading.value = false;\n                    return;\n                }\n            }\n\n            compareRules(parsed);\n            loading.value = false;\n        } catch (error) {\n            MsgError(i18n.global.t('commons.msg.errImport') + error.message);\n            loading.value = false;\n        }\n    };\n    reader.readAsText(_uploadFile.raw);\n};\n\nconst handleExceed: UploadProps['onExceed'] = (files) => {\n    uploadRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadRef.value!.handleStart(file);\n};\n\nconst checkDataFormat = (item: any): boolean => {\n    if (!item.address || !item.strategy) {\n        return false;\n    }\n    if (!['accept', 'drop'].includes(item.strategy)) {\n        return false;\n    }\n    return true;\n};\n\nconst compareRules = (importedRules: any[]) => {\n    const newRules: any[] = [];\n    const conflictRules: any[] = [];\n    const duplicateRules: any[] = [];\n\n    for (const importedRule of importedRules) {\n        const key = `${importedRule.address || 'Anywhere'}`;\n\n        const existingRule = currentRules.value.find((rule) => {\n            const existingKey = `${rule.address || 'Anywhere'}`;\n            return existingKey === key;\n        });\n\n        if (!existingRule) {\n            newRules.push({ ...importedRule, status: 'new' });\n        } else if (existingRule.strategy !== importedRule.strategy) {\n            conflictRules.push({\n                ...importedRule,\n                status: 'conflict',\n                existingStrategy: existingRule.strategy,\n            });\n        } else {\n            duplicateRules.push({ ...importedRule, status: 'duplicate' });\n        }\n    }\n\n    displayData.value = [...newRules, ...conflictRules, ...duplicateRules];\n    paginationConfig.total = displayData.value.length;\n    search();\n};\n\nconst onImport = async () => {\n    loading.value = true;\n    let successCount = 0;\n    let errorCount = 0;\n\n    for (const rule of selects.value) {\n        try {\n            const params: Host.RuleIP = {\n                operation: 'add',\n                address: rule.address || 'Anywhere',\n                strategy: rule.strategy,\n                description: rule.description || '',\n            };\n\n            await operateIPRule(params);\n            successCount++;\n        } catch (error) {\n            errorCount++;\n            console.error('Failed to import rule:', rule, error);\n        }\n    }\n\n    loading.value = false;\n\n    if (errorCount === 0) {\n        MsgSuccess(i18n.global.t('firewall.importSuccess', [successCount]));\n        visible.value = false;\n        emit('search');\n    } else {\n        MsgError(i18n.global.t('firewall.importPartialSuccess', [successCount, errorCount]));\n        emit('search');\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/ip/index.vue",
    "content": "<template>\n    <div>\n        <FireRouter />\n\n        <div v-loading=\"loading\">\n            <FireStatus\n                ref=\"fireStatusRef\"\n                @search=\"search\"\n                v-model:loading=\"loading\"\n                v-model:name=\"fireName\"\n                v-model:mask-show=\"maskShow\"\n                v-model:is-active=\"isActive\"\n                v-model:is-bind=\"isBind\"\n                current-tab=\"base\"\n            />\n\n            <div v-if=\"fireName !== '-'\">\n                <el-card v-if=\"!isActive && maskShow\" class=\"mask-prompt\">\n                    <span>{{ $t('firewall.firewallNotStart') }}</span>\n                </el-card>\n                <el-card v-if=\"!isBind && maskShow\" class=\"mask-prompt\">\n                    <span>{{ $t('firewall.basicStatus', ['1PANEL_BASIC']) }}</span>\n                </el-card>\n\n                <LayoutContent :title=\"$t('firewall.ipRule', 2)\" :class=\"{ mask: !isActive || !isBind }\">\n                    <template #prompt>\n                        <div v-if=\"fireName !== 'iptables'\">\n                            <el-alert :closable=\"false\" :title=\"$t('firewall.iptablesHelper', [fireName])\" />\n                        </div>\n                    </template>\n                    <template #leftToolBar>\n                        <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                            {{ $t('commons.button.create') }}\n                        </el-button>\n                        <el-button @click=\"onDelete(null)\" plain :disabled=\"selects.length === 0\">\n                            {{ $t('commons.button.delete') }}\n                        </el-button>\n                        <el-button-group>\n                            <el-button @click=\"onImport\">\n                                {{ $t('commons.button.import') }}\n                            </el-button>\n                            <el-button :disabled=\"selects.length === 0\" @click=\"onExport\">\n                                {{ $t('commons.button.export') }}\n                            </el-button>\n                        </el-button-group>\n                    </template>\n                    <template #rightToolBar>\n                        <el-select v-model=\"searchStrategy\" @change=\"search()\" clearable class=\"p-w-200\">\n                            <template #prefix>{{ $t('firewall.strategy') }}</template>\n                            <el-option :label=\"$t('commons.table.all')\" value=\"\"></el-option>\n                            <el-option :label=\"$t('firewall.allow')\" value=\"accept\"></el-option>\n                            <el-option :label=\"$t('firewall.deny')\" value=\"drop\"></el-option>\n                        </el-select>\n                        <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                        <TableRefresh @search=\"search()\" />\n                        <TableSetting title=\"firewall-ip-refresh\" @search=\"search()\" />\n                    </template>\n                    <template #main>\n                        <ComplexTable\n                            :pagination-config=\"paginationConfig\"\n                            v-model:selects=\"selects\"\n                            @search=\"search\"\n                            :data=\"data\"\n                            :heightDiff=\"400\"\n                        >\n                            <el-table-column type=\"selection\" fix />\n                            <el-table-column :min-width=\"120\" :label=\"$t('firewall.address')\" prop=\"address\">\n                                <template #default=\"{ row }\">\n                                    <span v-if=\"row.address && row.address !== 'Anywhere'\">{{ row.address }}</span>\n                                    <span v-else>{{ $t('firewall.allIP') }}</span>\n                                </template>\n                            </el-table-column>\n                            <el-table-column :min-width=\"80\" :label=\"$t('firewall.strategy')\" prop=\"strategy\">\n                                <template #default=\"{ row }\">\n                                    <el-button\n                                        v-if=\"row.strategy === 'accept'\"\n                                        @click=\"onChangeStatus(row, 'drop')\"\n                                        link\n                                        type=\"success\"\n                                    >\n                                        {{ $t('firewall.allow') }}\n                                    </el-button>\n                                    <el-button v-else link type=\"danger\" @click=\"onChangeStatus(row, 'accept')\">\n                                        {{ $t('firewall.deny') }}\n                                    </el-button>\n                                </template>\n                            </el-table-column>\n                            <el-table-column\n                                :min-width=\"120\"\n                                :label=\"$t('commons.table.description')\"\n                                prop=\"description\"\n                                show-overflow-tooltip\n                            >\n                                <template #default=\"{ row }\">\n                                    <fu-input-rw-switch\n                                        v-model=\"row.description\"\n                                        @enter=\"onChange(row)\"\n                                        @blur=\"onChange(row)\"\n                                    />\n                                </template>\n                            </el-table-column>\n                            <fu-table-operations\n                                width=\"200px\"\n                                :buttons=\"buttons\"\n                                :ellipsis=\"10\"\n                                :label=\"$t('commons.table.operate')\"\n                                fix\n                            />\n                        </ComplexTable>\n                    </template>\n                </LayoutContent>\n            </div>\n        </div>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <ImportDialog @search=\"search\" ref=\"dialogImportRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport OperateDialog from '@/views/host/firewall/ip/operate/index.vue';\nimport ImportDialog from '@/views/host/firewall/ip/import/index.vue';\nimport FireRouter from '@/views/host/firewall/index.vue';\nimport FireStatus from '@/views/host/firewall/status/index.vue';\nimport { onMounted, reactive, ref } from 'vue';\nimport { batchOperateRule, searchFireRule, updateAddrRule, updateFirewallDescription } from '@/api/modules/host';\nimport { Host } from '@/api/interface/host';\nimport { ElMessageBox } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { downloadWithContent, getCurrentDateFormatted } from '@/utils/util';\n\nconst loading = ref();\nconst activeTag = ref('address');\nconst selects = ref<any>([]);\nconst searchName = ref();\nconst searchStrategy = ref('');\nconst fireName = ref();\n\nconst maskShow = ref(true);\nconst isActive = ref(false);\nconst isBind = ref(false);\nconst fireStatusRef = ref();\n\nconst opRef = ref();\nconst dialogImportRef = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'firewall-ip-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('firewall-ip-page-size')) || 20,\n    total: 0,\n});\n\nconst search = async () => {\n    if (!isActive.value) {\n        loading.value = false;\n        data.value = [];\n        paginationConfig.total = 0;\n        return;\n    }\n    let params = {\n        type: activeTag.value,\n        strategy: searchStrategy.value,\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchFireRule(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst dialogRef = ref();\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Host.RuleIP> = {\n        strategy: 'accept',\n    },\n) => {\n    let params = {\n        title,\n        fireName: fireName.value,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onChange = async (row: any) => {\n    let params = {\n        type: 'address',\n        chain: fireName.value === 'iptables' ? '1PANEL_BASIC' : '',\n        srcIP: row.address,\n        dstIP: '',\n        srcPort: '',\n        dstPort: '',\n        protocol: '',\n        strategy: row.strategy,\n\n        description: row.description,\n    };\n    await updateFirewallDescription(params);\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst onChangeStatus = async (row: Host.RuleInfo, status: string) => {\n    let operation =\n        status === 'accept'\n            ? i18n.global.t('firewall.changeStrategyIPHelper2')\n            : i18n.global.t('firewall.changeStrategyIPHelper1');\n    ElMessageBox.confirm(operation, i18n.global.t('firewall.changeStrategy', [' IP ']), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        let params = {\n            oldRule: {\n                operation: 'remove',\n                address: row.address,\n                strategy: row.strategy,\n                description: row.description,\n            },\n            newRule: {\n                operation: 'add',\n                address: row.address,\n                strategy: status,\n                description: row.description,\n            },\n        };\n        loading.value = true;\n        await updateAddrRule(params)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onDelete = async (row: Host.RuleIP | null) => {\n    let names = [];\n    let rules = [];\n    if (row) {\n        rules.push({\n            operation: 'remove',\n            address: row.address,\n            port: '',\n            source: '',\n            protocol: '',\n            strategy: row.strategy,\n        });\n        names = [row.address];\n    } else {\n        for (const item of selects.value) {\n            rules.push({\n                operation: 'remove',\n                address: item.address,\n                port: '',\n                source: '',\n                protocol: '',\n                strategy: item.strategy,\n            });\n            names.push(item.address);\n        }\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('firewall.ipRule'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: batchOperateRule,\n        params: { type: 'address', rules: rules },\n    });\n};\n\nconst onImport = () => {\n    dialogImportRef.value.acceptParams();\n};\n\nconst onExport = () => {\n    ElMessageBox.confirm(\n        i18n.global.t('firewall.exportHelper', [selects.value.length]),\n        i18n.global.t('commons.button.export'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    ).then(async () => {\n        const exportData = selects.value.map((item: Host.RuleInfo) => ({\n            family: item.family,\n            address: item.address,\n            strategy: item.strategy,\n            description: item.description,\n        }));\n        const content = JSON.stringify(exportData, null, 2);\n        const fileName = `1panel-firewall-ip-${getCurrentDateFormatted()}.json`;\n        downloadWithContent(content, fileName);\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Host.RuleIP) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Host.RuleIP) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    if (fireName.value !== '-') {\n        loading.value = true;\n        fireStatusRef.value.acceptParams();\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/ip/operate/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"title\" @close=\"handleClose\" size=\"large\">\n        <div v-if=\"dialogData.fireName === 'iptables'\" class=\"mb-2\">\n            <el-alert :closable=\"false\" :title=\"$t('firewall.ipv4Limit')\" />\n        </div>\n        <el-form\n            ref=\"formRef\"\n            label-position=\"top\"\n            @submit.prevent\n            :model=\"dialogData.rowData\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('firewall.address')\" prop=\"address\">\n                <el-input\n                    :disabled=\"dialogData.title === 'edit'\"\n                    :rows=\"3\"\n                    type=\"textarea\"\n                    clearable\n                    v-model.trim=\"dialogData.rowData!.address\"\n                />\n                <span class=\"input-help\">{{ $t('firewall.addressHelper1') }}</span>\n                <span class=\"input-help\">{{ $t('firewall.addressHelper2') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('firewall.strategy')\" prop=\"strategy\">\n                <el-radio-group v-model=\"dialogData.rowData!.strategy\">\n                    <el-radio value=\"accept\">{{ $t('firewall.allow') }}</el-radio>\n                    <el-radio value=\"drop\">{{ $t('firewall.deny') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { Host } from '@/api/interface/host';\nimport { operateIPRule, updateAddrRule } from '@/api/modules/host';\nimport { checkCidr, checkCidrV6, checkIpV4V6, deepCopy } from '@/utils/util';\n\nconst loading = ref();\nconst oldRule = ref<Host.RuleIP>();\n\ninterface DialogProps {\n    title: string;\n    fireName: string;\n    rowData?: Host.RuleIP;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n    fireName: '',\n});\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    if (dialogData.value.title === 'edit') {\n        oldRule.value = deepCopy(params.rowData);\n    }\n    title.value = i18n.global.t('firewall.' + dialogData.value.title);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    address: [{ validator: checkAddress, trigger: 'blur' }],\n});\nfunction checkAddress(rule: any, value: any, callback: any) {\n    if (!dialogData.value.rowData.address) {\n        return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n    }\n    let addrs = dialogData.value.rowData.address.split(',');\n    for (const item of addrs) {\n        if (item.indexOf('/') !== -1) {\n            if (item.indexOf(':') !== -1) {\n                if (checkCidrV6(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            } else {\n                if (checkCidr(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            }\n        } else {\n            if (checkIpV4V6(item)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        }\n    }\n    callback();\n}\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        dialogData.value.rowData.operation = 'add';\n        if (!dialogData.value.rowData) return;\n        loading.value = true;\n        if (dialogData.value.title === 'create') {\n            await operateIPRule(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n            return;\n        }\n        oldRule.value.operation = 'remove';\n        dialogData.value.rowData.operation = 'add';\n        await updateAddrRule({ oldRule: oldRule.value, newRule: dialogData.value.rowData })\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/port/import/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"visible\" :title=\"$t('commons.button.import')\" size=\"large\">\n        <div>\n            <el-alert :closable=\"false\" show-icon type=\"info\">\n                <template #default>\n                    <div>{{ $t('commons.msg.importHelper') }}</div>\n                </template>\n            </el-alert>\n            <el-upload\n                action=\"#\"\n                :auto-upload=\"false\"\n                ref=\"uploadRef\"\n                class=\"float-left mt-2\"\n                :show-file-list=\"false\"\n                :limit=\"1\"\n                accept=\".json\"\n                :on-change=\"fileOnChange\"\n                :on-exceed=\"handleExceed\"\n                v-model:file-list=\"uploaderFiles\"\n            >\n                <el-button class=\"float-left\" type=\"primary\">{{ $t('commons.button.upload') }}</el-button>\n            </el-upload>\n\n            <el-card class=\"mt-2 w-full\" v-loading=\"loading\">\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    @search=\"search\"\n                    v-model:selects=\"selects\"\n                    :data=\"pageData\"\n                    :height=\"440\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column :label=\"$t('commons.table.status')\" :min-width=\"80\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.protocol')\" :min-width=\"70\" prop=\"protocol\" />\n                    <el-table-column :label=\"$t('commons.table.port')\" :min-width=\"70\" prop=\"port\" />\n                    <el-table-column :label=\"$t('firewall.address')\" :min-width=\"80\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.address && row.address !== 'Anywhere'\">{{ row.address }}</span>\n                            <span v-else>{{ $t('firewall.allIP') }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('firewall.strategy')\" :min-width=\"80\" prop=\"strategy\">\n                        <template #default=\"{ row }\">\n                            {{ row.strategy === 'accept' ? $t('firewall.accept') : $t('firewall.drop') }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('commons.table.description')\"\n                        :min-width=\"120\"\n                        prop=\"description\"\n                        show-overflow-tooltip\n                    />\n                </ComplexTable>\n            </el-card>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"selects.length === 0\" @click=\"onImport\">\n                    {{ $t('commons.button.import') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { genFileId, UploadFile, UploadFiles, UploadProps, UploadRawFile } from 'element-plus';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { operatePortRule, searchFireRule } from '@/api/modules/host';\nimport { Host } from '@/api/interface/host';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst visible = ref(false);\nconst loading = ref(false);\nconst selects = ref<any>([]);\nconst displayData = ref<any>([]);\nconst currentRules = ref<Host.RuleInfo[]>([]);\n\nconst uploadRef = ref();\nconst uploaderFiles = ref();\nconst pageData = ref([]);\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst acceptParams = async (): Promise<void> => {\n    visible.value = true;\n    displayData.value = [];\n    selects.value = [];\n\n    loadCurrentData();\n};\n\nconst loadCurrentData = async () => {\n    const res = await searchFireRule({\n        type: 'port',\n        strategy: '',\n        info: '',\n        page: 1,\n        pageSize: 10000,\n    });\n    currentRules.value = res.data.items || [];\n};\n\nconst search = () => {\n    const startIndex = (paginationConfig.currentPage - 1) * paginationConfig.pageSize;\n    const endIndex = startIndex + paginationConfig.pageSize;\n    pageData.value = displayData.value.slice(startIndex, endIndex);\n};\n\nconst fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    loading.value = true;\n    displayData.value = [];\n    uploaderFiles.value = uploadFiles;\n\n    const reader = new FileReader();\n    reader.onload = (e) => {\n        try {\n            const content = e.target.result as string;\n            const parsed = JSON.parse(content);\n\n            if (!Array.isArray(parsed)) {\n                MsgError(i18n.global.t('commons.msg.errImportFormat'));\n                loading.value = false;\n                return;\n            }\n\n            for (const item of parsed) {\n                if (!checkDataFormat(item)) {\n                    MsgError(i18n.global.t('commons.msg.errImportFormat'));\n                    loading.value = false;\n                    return;\n                }\n            }\n\n            compareRules(parsed);\n            loading.value = false;\n        } catch (error) {\n            MsgError(i18n.global.t('commons.msg.errImport') + error.message);\n            loading.value = false;\n        }\n    };\n    reader.readAsText(_uploadFile.raw);\n};\n\nconst handleExceed: UploadProps['onExceed'] = (files) => {\n    uploadRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadRef.value!.handleStart(file);\n};\n\nconst checkDataFormat = (item: any): boolean => {\n    if (!item.port || !item.protocol || !item.strategy) {\n        return false;\n    }\n    if (!['tcp', 'udp', 'tcp/udp'].includes(item.protocol)) {\n        return false;\n    }\n    if (!['accept', 'drop'].includes(item.strategy)) {\n        return false;\n    }\n    return true;\n};\n\nconst compareRules = (importedRules: any[]) => {\n    const newRules: any[] = [];\n    const conflictRules: any[] = [];\n    const duplicateRules: any[] = [];\n\n    for (const importedRule of importedRules) {\n        const key = `${importedRule.address || 'Anywhere'}:${importedRule.port}:${importedRule.protocol}`;\n\n        const existingRule = currentRules.value.find((rule) => {\n            const existingKey = `${rule.address || 'Anywhere'}:${rule.port}:${rule.protocol}`;\n            return existingKey === key;\n        });\n\n        if (!existingRule) {\n            newRules.push({ ...importedRule, status: 'new' });\n        } else if (existingRule.strategy !== importedRule.strategy) {\n            conflictRules.push({\n                ...importedRule,\n                status: 'conflict',\n                existingStrategy: existingRule.strategy,\n            });\n        } else {\n            duplicateRules.push({ ...importedRule, status: 'duplicate' });\n        }\n    }\n\n    displayData.value = [...newRules, ...conflictRules, ...duplicateRules];\n    paginationConfig.total = displayData.value.length;\n    search();\n};\n\nconst onImport = async () => {\n    loading.value = true;\n    let successCount = 0;\n    let errorCount = 0;\n\n    for (const rule of selects.value) {\n        try {\n            const params: Host.RulePort = {\n                operation: 'add',\n                address: rule.address || 'Anywhere',\n                port: rule.port,\n                source: '',\n                protocol: rule.protocol,\n                strategy: rule.strategy,\n                description: rule.description || '',\n            };\n\n            await operatePortRule(params);\n            successCount++;\n        } catch (error) {\n            errorCount++;\n            console.error('Failed to import rule:', rule, error);\n        }\n    }\n\n    loading.value = false;\n\n    if (errorCount === 0) {\n        MsgSuccess(i18n.global.t('firewall.importSuccess', [successCount]));\n        visible.value = false;\n        emit('search');\n    } else {\n        MsgError(i18n.global.t('firewall.importPartialSuccess', [successCount, errorCount]));\n        emit('search');\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/port/index.vue",
    "content": "<template>\n    <div>\n        <FireRouter />\n\n        <div v-loading=\"loading\">\n            <FireStatus\n                ref=\"fireStatusRef\"\n                @search=\"search\"\n                v-model:loading=\"loading\"\n                v-model:mask-show=\"maskShow\"\n                v-model:is-active=\"isActive\"\n                v-model:is-bind=\"isBind\"\n                v-model:name=\"fireName\"\n                current-tab=\"base\"\n            />\n            <div v-if=\"fireName !== '-'\">\n                <el-card v-if=\"!isActive && maskShow\" class=\"mask-prompt\">\n                    <span>{{ $t('firewall.firewallNotStart') }}</span>\n                </el-card>\n                <el-card v-if=\"!isBind && maskShow\" class=\"mask-prompt\">\n                    <span>{{ $t('firewall.basicStatus', ['1PANEL_BASIC']) }}</span>\n                </el-card>\n\n                <LayoutContent :title=\"$t('firewall.portRule', 2)\" :class=\"{ mask: !isActive || !isBind }\">\n                    <template #prompt>\n                        <div class=\"mb-2\" v-if=\"fireName !== 'iptables'\">\n                            <el-alert :closable=\"false\" :title=\"$t('firewall.iptablesHelper', [fireName])\" />\n                        </div>\n                        <el-alert type=\"info\" :closable=\"false\">\n                            <template #default>\n                                <span class=\"flx-align-center\">\n                                    <span>{{ $t('firewall.dockerHelper', [fireName]) }}</span>\n                                    <el-link\n                                        style=\"font-size: 12px; margin-left: 5px\"\n                                        icon=\"Position\"\n                                        @click=\"quickJump()\"\n                                        type=\"primary\"\n                                    >\n                                        {{ $t('firewall.quickJump') }}\n                                    </el-link>\n                                </span>\n                            </template>\n                        </el-alert>\n                    </template>\n                    <template #leftToolBar>\n                        <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                            {{ $t('commons.button.create') }}\n                        </el-button>\n                        <el-button @click=\"onDelete(null)\" plain :disabled=\"selects.length === 0\">\n                            {{ $t('commons.button.delete') }}\n                        </el-button>\n                        <el-button-group>\n                            <el-button @click=\"onImport\">\n                                {{ $t('commons.button.import') }}\n                            </el-button>\n                            <el-button :disabled=\"selects.length === 0\" @click=\"onExport\">\n                                {{ $t('commons.button.export') }}\n                            </el-button>\n                        </el-button-group>\n                    </template>\n                    <template #rightToolBar>\n                        <el-select v-model=\"searchStrategy\" @change=\"search()\" clearable class=\"p-w-200\">\n                            <template #prefix>{{ $t('firewall.strategy') }}</template>\n                            <el-option :label=\"$t('commons.table.all')\" value=\"\"></el-option>\n                            <el-option :label=\"$t('firewall.accept')\" value=\"accept\"></el-option>\n                            <el-option :label=\"$t('firewall.drop')\" value=\"drop\"></el-option>\n                        </el-select>\n                        <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                        <TableRefresh @search=\"search()\" />\n                        <TableSetting title=\"firewall-port-refresh\" @search=\"search()\" />\n                    </template>\n                    <template #main>\n                        <ComplexTable\n                            :pagination-config=\"paginationConfig\"\n                            v-model:selects=\"selects\"\n                            @search=\"search\"\n                            :data=\"data\"\n                            :heightDiff=\"400\"\n                        >\n                            <el-table-column type=\"selection\" fix />\n                            <el-table-column :label=\"$t('commons.table.protocol')\" :min-width=\"70\" prop=\"protocol\" />\n                            <el-table-column :label=\"$t('commons.table.port')\" :min-width=\"70\" prop=\"port\" />\n                            <el-table-column :label=\"$t('commons.table.status')\" :min-width=\"120\">\n                                <template #default=\"{ row }\">\n                                    <div v-if=\"isSinglePort(row.port)\">\n                                        <el-tag type=\"success\" v-if=\"row.usedStatus\">\n                                            {{ $t('firewall.used') + ' (' + row.usedStatus + ')' }}\n                                            <el-icon\n                                                v-if=\"row.processInfo\"\n                                                @click=\"showProcessDetail(row.processInfo.PID)\"\n                                                style=\"margin-left: 4px; cursor: pointer; vertical-align: middle\"\n                                            >\n                                                <Expand />\n                                            </el-icon>\n                                        </el-tag>\n                                        <el-tag type=\"info\" v-else>{{ $t('firewall.unUsed') }}</el-tag>\n                                    </div>\n                                    <span v-else>-</span>\n                                </template>\n                            </el-table-column>\n                            <el-table-column :min-width=\"80\" :label=\"$t('firewall.strategy')\" prop=\"strategy\">\n                                <template #default=\"{ row }\">\n                                    <el-button\n                                        v-if=\"row.strategy === 'accept'\"\n                                        @click=\"onChangeStatus(row, 'drop')\"\n                                        link\n                                        type=\"success\"\n                                    >\n                                        {{ $t('firewall.accept') }}\n                                    </el-button>\n                                    <el-button v-else link type=\"danger\" @click=\"onChangeStatus(row, 'accept')\">\n                                        {{ $t('firewall.drop') }}\n                                    </el-button>\n                                </template>\n                            </el-table-column>\n                            <el-table-column :min-width=\"80\" :label=\"$t('firewall.address')\" prop=\"address\">\n                                <template #default=\"{ row }\">\n                                    <span v-if=\"row.address && row.address !== 'Anywhere'\">{{ row.address }}</span>\n                                    <span v-else>{{ $t('firewall.allIP') }}</span>\n                                </template>\n                            </el-table-column>\n                            <el-table-column\n                                :min-width=\"150\"\n                                :label=\"$t('commons.table.description')\"\n                                prop=\"description\"\n                                show-overflow-tooltip\n                            >\n                                <template #default=\"{ row }\">\n                                    <fu-input-rw-switch\n                                        v-model=\"row.description\"\n                                        @enter=\"onChange(row)\"\n                                        @blur=\"onChange(row)\"\n                                    />\n                                </template>\n                            </el-table-column>\n                            <fu-table-operations\n                                width=\"200px\"\n                                :buttons=\"buttons\"\n                                :ellipsis=\"10\"\n                                :label=\"$t('commons.table.operate')\"\n                                fix\n                            />\n                        </ComplexTable>\n                    </template>\n                </LayoutContent>\n            </div>\n        </div>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <ImportDialog @search=\"search\" ref=\"dialogImportRef\" />\n        <ProcessDetail ref=\"processDetailRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport FireRouter from '@/views/host/firewall/index.vue';\nimport OperateDialog from '@/views/host/firewall/port/operate/index.vue';\nimport ImportDialog from '@/views/host/firewall/port/import/index.vue';\nimport FireStatus from '@/views/host/firewall/status/index.vue';\nimport ProcessDetail from '@/views/host/process/process/detail/index.vue';\nimport { onMounted, reactive, ref } from 'vue';\nimport { batchOperateRule, searchFireRule, updateFirewallDescription, updatePortRule } from '@/api/modules/host';\nimport { getListeningProcess } from '@/api/modules/process';\nimport { Host } from '@/api/interface/host';\nimport { Process } from '@/api/interface/process';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox } from 'element-plus';\nimport { Expand } from '@element-plus/icons-vue';\nimport { routerToName } from '@/utils/router';\nimport { downloadWithContent, getCurrentDateFormatted } from '@/utils/util';\n\nconst loading = ref();\nconst activeTag = ref('port');\nconst selects = ref<any>([]);\nconst searchName = ref();\nconst searchStrategy = ref('');\n\nconst maskShow = ref(true);\nconst isActive = ref(false);\nconst isBind = ref(false);\nconst fireName = ref();\nconst fireStatusRef = ref();\n\nconst opRef = ref();\nconst dialogImportRef = ref();\nconst processDetailRef = ref();\n\nconst listeningProcesses = ref<Process.ListeningProcess[]>([]);\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'firewall-port-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('firewall-port-page-size')) || 20,\n    total: 0,\n});\n\nconst extractPortsFromObject = (portObj: { [key: string]: {} }): number[] => {\n    return Object.keys(portObj)\n        .map((portStr) => parseInt(portStr))\n        .filter((port) => !isNaN(port));\n};\n\nconst isSinglePort = (portStr: string): boolean => {\n    return portStr.indexOf('-') === -1 && portStr.indexOf(':') === -1 && portStr.indexOf(',') === -1;\n};\n\nconst loadListeningProcesses = async () => {\n    try {\n        const res = await getListeningProcess();\n        listeningProcesses.value = res.data || [];\n\n        for (const item of data.value) {\n            if (!item.usedStatus && isSinglePort(item.port)) {\n                const portNum = parseInt(item.port.trim());\n                if (!isNaN(portNum)) {\n                    const protocolNum =\n                        item.protocol.toLowerCase() === 'tcp' ? 1 : item.protocol.toLowerCase() === 'udp' ? 2 : 0;\n\n                    for (const proc of listeningProcesses.value) {\n                        if (proc.Protocol === protocolNum) {\n                            const procPorts = extractPortsFromObject(proc.Port);\n                            if (procPorts.includes(portNum)) {\n                                item.usedStatus = proc.Name;\n                                item.processInfo = proc;\n                                break;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    } catch (error) {\n        console.error('Failed to load listening processes:', error);\n    }\n};\n\nconst search = async () => {\n    if (!isActive.value) {\n        loading.value = false;\n        data.value = [];\n        paginationConfig.total = 0;\n        return;\n    }\n    let params = {\n        type: activeTag.value,\n        strategy: searchStrategy.value,\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchFireRule(params)\n        .then(async (res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n\n            await loadListeningProcesses();\n\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst dialogRef = ref();\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Host.RulePort> = {\n        protocol: 'tcp',\n        source: 'anyWhere',\n        strategy: 'accept',\n    },\n) => {\n    let params = {\n        title,\n        fireName: fireName.value,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst quickJump = () => {\n    routerToName('AppInstalled');\n};\n\nconst onChangeStatus = async (row: Host.RuleInfo, status: string) => {\n    let operation =\n        status === 'accept'\n            ? i18n.global.t('firewall.changeStrategyPortHelper2')\n            : i18n.global.t('firewall.changeStrategyPortHelper1');\n    ElMessageBox.confirm(operation, i18n.global.t('firewall.changeStrategy', [i18n.global.t('commons.table.port')]), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        let params = {\n            oldRule: {\n                operation: 'remove',\n                address: row.address,\n                port: row.port,\n                source: '',\n                protocol: row.protocol,\n                strategy: row.strategy,\n                description: row.description,\n            },\n            newRule: {\n                operation: 'add',\n                address: row.address,\n                port: row.port,\n                source: '',\n                protocol: row.protocol,\n                strategy: status,\n                description: row.description,\n            },\n        };\n        loading.value = true;\n        await updatePortRule(params)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onChange = async (row: any) => {\n    let params = {\n        type: 'port',\n        chain: fireName.value === 'iptables' ? '1PANEL_BASIC' : '',\n        srcIP: row.address,\n        dstIP: '',\n        srcPort: '',\n        dstPort: row.port,\n        protocol: row.protocol,\n        strategy: row.strategy,\n\n        description: row.description,\n    };\n    await updateFirewallDescription(params);\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst onDelete = async (row: Host.RuleInfo | null) => {\n    let names = [];\n    let rules = [];\n    if (row) {\n        rules.push({\n            operation: 'remove',\n            chain: row.chain,\n            address: row.address,\n            port: row.port,\n            source: '',\n            protocol: row.protocol,\n            strategy: row.strategy,\n        });\n        names = [row.port + ' (' + row.protocol + ')'];\n    } else {\n        for (const item of selects.value) {\n            names.push(item.port + ' (' + item.protocol + ')');\n            rules.push({\n                operation: 'remove',\n                chain: item.chain,\n                address: item.address,\n                port: item.port,\n                source: '',\n                protocol: item.protocol,\n                strategy: item.strategy,\n            });\n        }\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('firewall.portRule'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: batchOperateRule,\n        params: { type: 'port', rules: rules },\n    });\n};\n\nconst onImport = () => {\n    dialogImportRef.value.acceptParams();\n};\n\nconst onExport = () => {\n    ElMessageBox.confirm(\n        i18n.global.t('firewall.exportHelper', [selects.value.length]),\n        i18n.global.t('commons.button.export'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        },\n    ).then(async () => {\n        const exportData = selects.value.map((item: Host.RuleInfo) => ({\n            family: item.family,\n            address: item.address,\n            port: item.port,\n            protocol: item.protocol,\n            strategy: item.strategy,\n            description: item.description,\n        }));\n        const content = JSON.stringify(exportData, null, 2);\n        const fileName = `1panel-firewall-port-${getCurrentDateFormatted()}.json`;\n        downloadWithContent(content, fileName);\n    });\n};\n\nconst showProcessDetail = (pid: number) => {\n    processDetailRef.value?.acceptParams(pid);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Host.RulePort) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Host.RuleInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    if (fireName.value !== '-') {\n        loading.value = true;\n        fireStatusRef.value.acceptParams();\n    }\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.svg-icon {\n    font-size: 8px;\n    margin-bottom: -4px;\n    cursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/port/operate/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"title\" @close=\"handleClose\" size=\"large\">\n        <div v-if=\"dialogData.fireName === 'iptables'\" class=\"mb-2\">\n            <el-alert :closable=\"false\" :title=\"$t('firewall.ipv4Limit')\" />\n        </div>\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('commons.table.protocol')\" prop=\"protocol\">\n                <el-select class=\"w-full\" v-model=\"dialogData.rowData!.protocol\">\n                    <el-option value=\"tcp\" label=\"tcp\" />\n                    <el-option value=\"udp\" label=\"udp\" />\n                    <el-option value=\"tcp/udp\" label=\"tcp/udp\" />\n                </el-select>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('commons.table.port')\" prop=\"port\">\n                <el-input :disabled=\"dialogData.title === 'edit'\" clearable v-model.trim=\"dialogData.rowData!.port\" />\n                <span class=\"input-help\">{{ $t('firewall.portHelper1') }}</span>\n                <span class=\"input-help\">{{ $t('firewall.portHelper2') }}</span>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('app.source')\" prop=\"source\">\n                <el-radio-group v-model=\"dialogData.rowData!.source\">\n                    <el-radio value=\"anyWhere\">{{ $t('firewall.anyWhere') }}</el-radio>\n                    <el-radio value=\"address\">{{ $t('firewall.address') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n\n            <el-form-item\n                :label=\"$t('firewall.address')\"\n                v-if=\"dialogData.rowData!.source === 'address'\"\n                prop=\"address\"\n            >\n                <el-input v-model.trim=\"dialogData.rowData!.address\" />\n                <span class=\"input-help\">{{ $t('firewall.addressHelper1') }}</span>\n                <span class=\"input-help\">{{ $t('firewall.addressHelper2') }}</span>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('firewall.strategy')\" prop=\"strategy\">\n                <el-radio-group v-model=\"dialogData.rowData!.strategy\">\n                    <el-radio value=\"accept\">{{ $t('firewall.accept') }}</el-radio>\n                    <el-radio value=\"drop\">{{ $t('firewall.drop') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Host } from '@/api/interface/host';\nimport { operatePortRule, updatePortRule } from '@/api/modules/host';\nimport { checkCidr, checkCidrV6, checkIpV4V6, checkPort, deepCopy } from '@/utils/util';\n\nconst loading = ref();\nconst oldRule = ref<Host.RulePort>();\n\ninterface DialogProps {\n    title: string;\n    fireName: string;\n    rowData?: Host.RulePort;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n    fireName: '',\n});\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    if (dialogData.value.title === 'edit') {\n        if (params.rowData.address && params.rowData.address !== 'Anywhere') {\n            dialogData.value.rowData.source = 'address';\n        } else {\n            dialogData.value.rowData.source = 'anyWhere';\n            dialogData.value.rowData.address = '';\n        }\n        oldRule.value = deepCopy(params.rowData);\n    }\n    title.value = i18n.global.t('firewall.' + dialogData.value.title);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst rules = reactive({\n    protocol: [Rules.requiredSelect],\n    port: [Rules.requiredInput],\n    address: [{ validator: checkAddress, trigger: 'blur' }],\n});\n\nfunction checkAddress(rule: any, value: any, callback: any) {\n    if (!dialogData.value.rowData.address) {\n        return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n    }\n    let addrs = dialogData.value.rowData.address.split(',');\n    for (const item of addrs) {\n        if (item.indexOf('/') !== -1) {\n            if (item.indexOf(':') !== -1) {\n                if (checkCidrV6(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            } else {\n                if (checkCidr(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            }\n        } else {\n            if (checkIpV4V6(item)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        }\n    }\n    callback();\n}\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        dialogData.value.rowData.operation = 'add';\n        if (!dialogData.value.rowData) return;\n        if (dialogData.value.rowData.source === 'anyWhere') {\n            dialogData.value.rowData.address = '';\n        }\n        let ports = [];\n        if (dialogData.value.rowData.port.indexOf('-') !== -1 && !dialogData.value.rowData.port.startsWith('-')) {\n            ports = dialogData.value.rowData.port.split('-');\n        } else if (\n            dialogData.value.rowData.port.indexOf(',') !== -1 &&\n            !dialogData.value.rowData.port.startsWith(',')\n        ) {\n            ports = dialogData.value.rowData.port.split(',');\n        } else {\n            ports.push(dialogData.value.rowData.port);\n        }\n        for (const port of ports) {\n            if (checkPort(port)) {\n                MsgError(i18n.global.t('firewall.portFormatError'));\n                return;\n            }\n        }\n        loading.value = true;\n        if (dialogData.value.title === 'create') {\n            await operatePortRule(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n            return;\n        }\n        oldRule.value.operation = 'remove';\n        dialogData.value.rowData.operation = 'add';\n        await updatePortRule({ oldRule: oldRule.value, newRule: dialogData.value.rowData })\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/firewall/status/index.vue",
    "content": "<template>\n    <div>\n        <div class=\"app-status card-interval\" v-if=\"baseInfo.isExist\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag effect=\"dark\" type=\"success\">{{ baseInfo.name }}</el-tag>\n                        <Status class=\"mt-0.5\" :status=\"baseInfo.isActive ? 'enable' : 'disable'\" />\n                        <el-tag>{{ $t('app.version') }}: {{ baseInfo.version }}</el-tag>\n                    </div>\n                    <div class=\"mt-0.5\">\n                        <template v-if=\"baseInfo.name !== 'iptables'\">\n                            <el-button type=\"primary\" v-if=\"baseInfo.isActive\" @click=\"onOperate('stop')\" link>\n                                {{ $t('commons.button.stop') }}\n                            </el-button>\n                            <el-button type=\"primary\" v-if=\"!baseInfo.isActive\" @click=\"onOperate('start')\" link>\n                                {{ $t('commons.button.start') }}\n                            </el-button>\n                            <el-divider direction=\"vertical\" />\n                            <el-button type=\"primary\" @click=\"onOperate('restart')\" link>\n                                {{ $t('commons.button.restart') }}\n                            </el-button>\n                        </template>\n                        <template v-if=\"!baseInfo.isInit || (props.currentTab === 'forward' && !baseInfo.isBind)\">\n                            <el-divider direction=\"vertical\" />\n                            <el-button type=\"primary\" link @click=\"onInit\">\n                                {{ $t('commons.button.init') }}\n                            </el-button>\n                        </template>\n                        <template v-if=\"baseInfo.name === 'iptables' && baseInfo.isInit && props.currentTab == 'base'\">\n                            <el-divider direction=\"vertical\" />\n                            <el-button v-if=\"baseInfo.isBind\" type=\"primary\" link @click=\"onUnBind\">\n                                {{ $t('commons.button.unbind') }}\n                            </el-button>\n                            <el-button v-if=\"!baseInfo.isBind\" type=\"primary\" link @click=\"onBind\">\n                                {{ $t('commons.button.bind') }}\n                            </el-button>\n                        </template>\n                        <span v-if=\"onPing !== 'None'\">\n                            <el-divider direction=\"vertical\" />\n                            <el-button type=\"primary\" link>{{ $t('firewall.noPing') }}</el-button>\n                            <el-switch\n                                size=\"small\"\n                                class=\"ml-2\"\n                                inactive-value=\"Disable\"\n                                active-value=\"Enable\"\n                                @change=\"onPingOperate\"\n                                v-model=\"onPing\"\n                            />\n                        </span>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n        <NoSuchService v-else name=\"Firewalld / Ufw / iptables\" />\n\n        <LayoutContent :divider=\"true\" v-if=\"baseInfo.isExist && baseInfo.isActive && !baseInfo.isInit\">\n            <template #main>\n                <div class=\"app-warn\">\n                    <div class=\"flex flex-col gap-2 items-center justify-center w-full sm:flex-row\">\n                        <span>{{ loadInitMsg() }}</span>\n                    </div>\n                    <div>\n                        <img src=\"@/assets/images/no_app.svg\" />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <DockerRestart\n            ref=\"dockerRef\"\n            v-model:withDockerRestart=\"withDockerRestart\"\n            @submit=\"onSubmit\"\n            :title=\"$t('firewall.firewallHelper', [$t('commons.button.' + operation)])\"\n        >\n            <template #helper>\n                <span>{{ $t('firewall.' + operation + 'FirewallHelper') }}</span>\n            </template>\n        </DockerRestart>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Host } from '@/api/interface/host';\nimport { loadFireBaseInfo, operateFilterChain, operateFire } from '@/api/modules/host';\nimport i18n from '@/lang';\nimport NoSuchService from '@/components/layout-content/no-such-service.vue';\nimport DockerRestart from '@/components/docker-proxy/docker-restart.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox } from 'element-plus';\nimport { ref } from 'vue';\nimport { loadDockerStatus } from '@/api/modules/container';\n\nconst props = defineProps({\n    currentTab: String,\n});\n\nconst baseInfo = ref<Host.FirewallBase>({\n    isActive: false,\n    isExist: true,\n    isInit: false,\n    isBind: false,\n    name: '',\n    version: '',\n    pingStatus: '',\n});\nconst onPing = ref('Disable');\nconst oldStatus = ref();\nconst dockerRef = ref();\nconst operation = ref('restart');\nconst dockerStatus = ref();\nconst withDockerRestart = ref(false);\n\nconst acceptParams = (): void => {\n    loadBaseInfo(true);\n    loadDocker();\n};\nconst emit = defineEmits([\n    'search',\n    'update:is-active',\n    'update:is-bind',\n    'update:loading',\n    'update:maskShow',\n    'update:name',\n]);\n\nconst loadBaseInfo = async (search: boolean) => {\n    await loadFireBaseInfo(props.currentTab)\n        .then(async (res) => {\n            baseInfo.value = res.data;\n            onPing.value = baseInfo.value.pingStatus;\n            oldStatus.value = onPing.value;\n            if (baseInfo.value.isInit) {\n                emit('update:name', baseInfo.value.name);\n            } else {\n                emit('update:name', '-');\n            }\n            emit('update:is-active', baseInfo.value.isActive);\n            emit('update:is-bind', baseInfo.value.isBind);\n\n            if (search) {\n                emit('search');\n            } else {\n                emit('update:loading', false);\n            }\n        })\n        .catch(() => {\n            emit('update:loading', false);\n            emit('update:maskShow', true);\n            emit('update:name', '-');\n        });\n};\n\nconst loadDocker = async () => {\n    const res = await loadDockerStatus();\n    dockerStatus.value = res.data.isExist;\n};\n\nconst loadInitMsg = () => {\n    switch (props.currentTab) {\n        case 'base':\n            return i18n.global.t('firewall.initHelper', [i18n.global.t('firewall.baseIptables')]);\n        case 'forward':\n            return i18n.global.t('firewall.initHelper', [i18n.global.t('firewall.forwardIptables')]);\n        case 'advance':\n            return i18n.global.t('firewall.initHelper', [i18n.global.t('firewall.advanceIptables')]);\n    }\n};\n\nconst onInit = async () => {\n    let chainName = '';\n    let msg = '';\n    switch (props.currentTab) {\n        case 'base':\n            chainName = '1PANEL_BASIC';\n            msg = i18n.global.t('firewall.initMsg', [i18n.global.t('firewall.baseIptables')]);\n        case 'forward':\n            chainName = '1PANEL_FORWARD';\n            msg = i18n.global.t('firewall.initMsg', [i18n.global.t('firewall.forwardIptables')]);\n        case 'advance':\n            chainName = '1PANEL_INPUT';\n            msg = i18n.global.t('firewall.initMsg', [i18n.global.t('firewall.advanceIptables')]);\n    }\n    ElMessageBox.confirm(msg, i18n.global.t('commons.button.init'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await operateFilterChain(chainName, 'init-' + props.currentTab).then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loadBaseInfo(true);\n        });\n    });\n};\n\nconst onBind = async () => {\n    ElMessageBox.confirm(i18n.global.t('firewall.bindHelper'), i18n.global.t('commons.button.bind'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await operateFilterChain('1PANEL_BASIC', 'bind-base').then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loadBaseInfo(true);\n        });\n    });\n};\nconst onUnBind = async () => {\n    ElMessageBox.confirm(i18n.global.t('firewall.unbindHelper'), i18n.global.t('commons.button.unbind'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await operateFilterChain('1PANEL_BASIC', 'unbind-base').then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loadBaseInfo(true);\n        });\n    });\n};\n\nconst onOperate = async (op: string) => {\n    operation.value = op;\n    if (baseInfo.value.name === 'iptables' || !dockerStatus.value) {\n        emit('update:loading', true);\n        emit('update:maskShow', true);\n        await operateFire(operation.value, false)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loadBaseInfo(true);\n            })\n            .catch(() => {\n                loadBaseInfo(true);\n            });\n    } else {\n        dockerRef.value.acceptParams({ title: i18n.global.t('firewall.dockerRestart') });\n    }\n};\n\nconst onSubmit = async () => {\n    emit('update:loading', true);\n    emit('update:maskShow', true);\n    await operateFire(operation.value, withDockerRestart.value)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loadBaseInfo(true);\n        })\n        .catch(() => {\n            loadBaseInfo(true);\n        });\n};\n\nconst onPingOperate = async (operation: string) => {\n    emit('update:maskShow', false);\n    let operationHelper =\n        operation === 'Enable' ? i18n.global.t('firewall.noPingHelper') : i18n.global.t('firewall.onPingHelper');\n    ElMessageBox.confirm(operationHelper, i18n.global.t('firewall.noPingTitle'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    })\n        .then(async () => {\n            emit('update:loading', true);\n            operation = operation === 'Disable' ? 'disableBanPing' : 'enableBanPing';\n            emit('update:maskShow', true);\n            await operateFire(operation, false)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loadBaseInfo(false);\n                })\n                .catch(() => {\n                    loadBaseInfo(false);\n                });\n        })\n        .catch(() => {\n            emit('update:maskShow', true);\n            onPing.value = oldStatus.value;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/monitor/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.monitor'),\n        path: '/hosts/monitor/monitor',\n    },\n    {\n        label: i18n.global.t('file.setting'),\n        path: '/hosts/monitor/setting',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/monitor/monitor/index.vue",
    "content": "<template>\n    <div>\n        <MonitorRouter />\n\n        <div class=\"content-container__search\">\n            <el-card>\n                <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                    <el-date-picker\n                        @change=\"searchGlobal()\"\n                        v-model=\"timeRangeGlobal\"\n                        type=\"datetimerange\"\n                        range-separator=\"-\"\n                        :start-placeholder=\"$t('commons.search.timeStart')\"\n                        :end-placeholder=\"$t('commons.search.timeEnd')\"\n                        :shortcuts=\"shortcuts\"\n                        style=\"max-width: 360px; width: 100%\"\n                        :size=\"mobile ? 'small' : 'default'\"\n                    ></el-date-picker>\n                    <TableRefresh class=\"float-right\" @search=\"searchGlobal()\" />\n                </div>\n            </el-card>\n        </div>\n        <el-row :gutter=\"7\" class=\"card-interval\">\n            <el-col :span=\"24\">\n                <el-card style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <span class=\"title\">{{ $t('monitor.avgLoad') }}</span>\n                            <el-date-picker\n                                @change=\"search('load')\"\n                                v-model=\"timeRangeLoad\"\n                                type=\"datetimerange\"\n                                range-separator=\"-\"\n                                :start-placeholder=\"$t('commons.search.timeStart')\"\n                                :end-placeholder=\"$t('commons.search.timeEnd')\"\n                                :shortcuts=\"shortcuts\"\n                                style=\"max-width: 360px; width: 100%\"\n                                :size=\"mobile ? 'small' : 'default'\"\n                            ></el-date-picker>\n                        </div>\n                    </template>\n                    <div class=\"chart\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadLoadChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadLoadChart']\"\n                            v-if=\"chartsOption['loadLoadChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                </el-card>\n            </el-col>\n        </el-row>\n        <el-row :gutter=\"7\" class=\"card-interval\">\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <span class=\"title\">CPU</span>\n                            <el-date-picker\n                                @change=\"search('cpu')\"\n                                v-model=\"timeRangeCpu\"\n                                type=\"datetimerange\"\n                                range-separator=\"-\"\n                                :start-placeholder=\"$t('commons.search.timeStart')\"\n                                :end-placeholder=\"$t('commons.search.timeEnd')\"\n                                :shortcuts=\"shortcuts\"\n                                style=\"max-width: 360px; width: 100%\"\n                                :size=\"mobile ? 'small' : 'default'\"\n                            ></el-date-picker>\n                        </div>\n                    </template>\n                    <div class=\"chart\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadCPUChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadCPUChart']\"\n                            v-if=\"chartsOption['loadCPUChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                </el-card>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <span class=\"title\">{{ $t('monitor.memory') }}</span>\n                            <el-date-picker\n                                @change=\"search('memory')\"\n                                v-model=\"timeRangeMemory\"\n                                type=\"datetimerange\"\n                                range-separator=\"-\"\n                                :start-placeholder=\"$t('commons.search.timeStart')\"\n                                :end-placeholder=\"$t('commons.search.timeEnd')\"\n                                :shortcuts=\"shortcuts\"\n                                style=\"max-width: 360px; width: 100%\"\n                                :size=\"mobile ? 'small' : 'default'\"\n                            ></el-date-picker>\n                        </div>\n                    </template>\n                    <div class=\"chart\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadMemoryChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadMemoryChart']\"\n                            v-if=\"chartsOption['loadMemoryChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                </el-card>\n            </el-col>\n        </el-row>\n        <el-row :gutter=\"7\" class=\"card-interval\">\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <div>\n                                <span class=\"title\">{{ $t('monitor.disk') }} I/O{{ $t('commons.colon') }}</span>\n                                <el-dropdown max-height=\"300px\">\n                                    <span class=\"networkOption\">\n                                        {{ ioChoose === 'all' ? $t('commons.table.all') : ioChoose }}\n                                    </span>\n                                    <template #dropdown>\n                                        <el-dropdown-menu>\n                                            <div v-for=\"item in ioOptions\" :key=\"item\">\n                                                <el-dropdown-item v-if=\"item === 'all'\" @click=\"changeIO('all')\">\n                                                    {{ $t('commons.table.all') }}\n                                                </el-dropdown-item>\n                                                <el-dropdown-item v-else @click=\"changeIO(item)\">\n                                                    {{ item }}\n                                                </el-dropdown-item>\n                                            </div>\n                                        </el-dropdown-menu>\n                                    </template>\n                                </el-dropdown>\n                            </div>\n                            <el-date-picker\n                                @change=\"search('io')\"\n                                v-model=\"timeRangeIO\"\n                                type=\"datetimerange\"\n                                range-separator=\"-\"\n                                :start-placeholder=\"$t('commons.search.timeStart')\"\n                                :end-placeholder=\"$t('commons.search.timeEnd')\"\n                                :shortcuts=\"shortcuts\"\n                                style=\"max-width: 360px; width: 100%\"\n                                :size=\"mobile ? 'small' : 'default'\"\n                            ></el-date-picker>\n                        </div>\n                    </template>\n                    <div class=\"chart\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadIOChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadIOChart']\"\n                            v-if=\"chartsOption['loadIOChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                </el-card>\n            </el-col>\n            <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                <el-card style=\"overflow: inherit\">\n                    <template #header>\n                        <div :class=\"mobile ? 'flx-wrap' : 'flex justify-between'\">\n                            <div>\n                                <span class=\"title\">{{ $t('monitor.network') }}{{ $t('commons.colon') }}</span>\n                                <el-dropdown max-height=\"300px\">\n                                    <span class=\"networkOption\">\n                                        {{ networkChoose === 'all' ? $t('commons.table.all') : networkChoose }}\n                                    </span>\n                                    <template #dropdown>\n                                        <el-dropdown-menu>\n                                            <div v-for=\"item in netOptions\" :key=\"item\">\n                                                <el-dropdown-item v-if=\"item === 'all'\" @click=\"changeNetwork('all')\">\n                                                    {{ $t('commons.table.all') }}\n                                                </el-dropdown-item>\n                                                <el-dropdown-item v-else @click=\"changeNetwork(item)\">\n                                                    {{ item }}\n                                                </el-dropdown-item>\n                                            </div>\n                                        </el-dropdown-menu>\n                                    </template>\n                                </el-dropdown>\n                            </div>\n                            <el-date-picker\n                                @change=\"search('network')\"\n                                v-model=\"timeRangeNetwork\"\n                                type=\"datetimerange\"\n                                range-separator=\"-\"\n                                :start-placeholder=\"$t('commons.search.timeStart')\"\n                                :end-placeholder=\"$t('commons.search.timeEnd')\"\n                                :shortcuts=\"shortcuts\"\n                                style=\"max-width: 360px; width: 100%\"\n                                :size=\"mobile ? 'small' : 'default'\"\n                            ></el-date-picker>\n                        </div>\n                    </template>\n                    <div class=\"chart\">\n                        <v-charts\n                            height=\"400px\"\n                            id=\"loadNetworkChart\"\n                            type=\"line\"\n                            :option=\"chartsOption['loadNetworkChart']\"\n                            v-if=\"chartsOption['loadNetworkChart']\"\n                            :dataZoom=\"true\"\n                        />\n                    </div>\n                </el-card>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, reactive, onMounted, computed } from 'vue';\nimport { loadMonitor, getNetworkOptions, getIOOptions } from '@/api/modules/host';\nimport { computeSize, computeSizeFromKBs, dateFormatWithoutYear } from '@/utils/util';\nimport i18n from '@/lang';\nimport MonitorRouter from '@/views/host/monitor/index.vue';\nimport { GlobalStore } from '@/store';\nimport { shortcuts } from '@/utils/shortcuts';\nimport { Host } from '@/api/interface/host';\n\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst monitorBase = ref();\nconst timeRangeGlobal = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);\nconst timeRangeLoad = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);\nconst timeRangeCpu = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);\nconst timeRangeMemory = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);\nconst timeRangeIO = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);\nconst timeRangeNetwork = ref<[Date, Date]>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);\nconst networkChoose = ref();\nconst netOptions = ref();\nconst ioChoose = ref();\nconst ioOptions = ref();\nconst chartsOption = ref({ loadLoadChart: null, loadCPUChart: null, loadMemoryChart: null, loadNetworkChart: null });\n\nconst searchTime = ref();\nconst searchInfo = reactive<Host.MonitorSearch>({\n    param: '',\n    io: '',\n    network: '',\n    startTime: new Date(new Date().setHours(0, 0, 0, 0)),\n    endTime: new Date(),\n});\n\nconst searchGlobal = () => {\n    timeRangeLoad.value = timeRangeGlobal.value;\n    timeRangeCpu.value = timeRangeGlobal.value;\n    timeRangeMemory.value = timeRangeGlobal.value;\n    timeRangeIO.value = timeRangeGlobal.value;\n    timeRangeNetwork.value = timeRangeGlobal.value;\n    search('load');\n    search('cpu');\n    search('memory');\n    search('io');\n    search('network');\n};\n\nconst search = async (param: string) => {\n    searchInfo.param = param;\n    switch (param) {\n        case 'load':\n            searchTime.value = timeRangeLoad.value;\n            break;\n        case 'cpu':\n            searchTime.value = timeRangeCpu.value;\n            break;\n        case 'memory':\n            searchTime.value = timeRangeMemory.value;\n            break;\n        case 'io':\n            searchTime.value = timeRangeIO.value;\n            searchInfo.io = ioChoose.value || 'all';\n            break;\n        case 'network':\n            searchTime.value = timeRangeNetwork.value;\n            searchInfo.network = networkChoose.value || 'all';\n            break;\n        case 'all':\n            searchTime.value = timeRangeNetwork.value;\n            searchInfo.io = ioChoose.value || 'all';\n            searchInfo.network = networkChoose.value || 'all';\n            break;\n    }\n    if (searchTime.value && searchTime.value.length === 2) {\n        searchInfo.startTime = searchTime.value[0];\n        searchInfo.endTime = searchTime.value[1];\n    }\n    const res = await loadMonitor(searchInfo);\n    monitorBase.value = res.data;\n    for (const item of monitorBase.value) {\n        if (!item.value) {\n            item.value = [];\n            item.date = [];\n        }\n        switch (item.param) {\n            case 'base':\n                let baseDate = item.date.length === 0 ? loadEmptyDate(timeRangeCpu.value) : item.date;\n                baseDate = baseDate.map(function (item: any) {\n                    return dateFormatWithoutYear(item);\n                });\n                if (param === 'cpu' || param === 'all') {\n                    initCPUCharts(baseDate, item);\n                }\n                if (param === 'memory' || param === 'all') {\n                    initMemCharts(baseDate, item);\n                }\n                if (param === 'load' || param === 'all') {\n                    initLoadCharts(item);\n                }\n                break;\n            case 'io':\n                initIOCharts(item);\n                break;\n            case 'network':\n                initNetCharts(item);\n                break;\n        }\n    }\n};\n\nconst changeNetwork = (item: string) => {\n    networkChoose.value = item;\n    search('network');\n};\n\nconst changeIO = (item: string) => {\n    ioChoose.value = item;\n    search('io');\n};\n\nconst loadOptions = async () => {\n    const res = await getNetworkOptions();\n    netOptions.value = res.data;\n    searchInfo.network = globalStore.defaultNetwork || (netOptions.value && netOptions.value[0]);\n    networkChoose.value = searchInfo.network;\n\n    const res1 = await getIOOptions();\n    ioOptions.value = res1.data;\n    searchInfo.io = globalStore.defaultIO || (ioOptions.value && ioOptions.value[0]);\n    ioChoose.value = searchInfo.io;\n\n    search('all');\n};\n\nfunction initLoadCharts(item: Host.MonitorData) {\n    let itemLoadDate = item.date.length === 0 ? loadEmptyDate(timeRangeLoad.value) : item.date;\n    let loadDate = itemLoadDate.map(function (item: any) {\n        return dateFormatWithoutYear(item);\n    });\n    let load1Data = item.value.map(function (item: any) {\n        return item.cpuLoad1.toFixed(2);\n    });\n    load1Data = load1Data.length === 0 ? loadEmptyData() : load1Data;\n    let load5Data = item.value.map(function (item: any) {\n        return item.cpuLoad5.toFixed(2);\n    });\n    load5Data = load5Data.length === 0 ? loadEmptyData() : load5Data;\n    let load15Data = item.value.map(function (item: any) {\n        return item.cpuLoad15.toFixed(2);\n    });\n    load15Data = load15Data.length === 0 ? loadEmptyData() : load15Data;\n    let loadUsage = item.value.map(function (item: any) {\n        return { value: item.loadUsage.toFixed(2), top: item.topCPUItems, unit: '%' };\n    });\n    loadUsage = loadUsage.length === 0 ? loadTopEmptyData() : loadUsage;\n    chartsOption.value['loadLoadChart'] = {\n        xData: loadDate,\n        yData: [\n            {\n                name: '1 ' + i18n.global.t('commons.units.minute', 1),\n                data: load1Data,\n            },\n            {\n                name: '5 ' + i18n.global.t('commons.units.minute', 5),\n                data: load5Data,\n            },\n            {\n                name: '15 ' + i18n.global.t('commons.units.minute', 15),\n                data: load15Data,\n            },\n            {\n                name: i18n.global.t('monitor.resourceUsage'),\n                data: loadUsage,\n                yAxisIndex: 1,\n            },\n        ],\n        yAxis: [\n            { type: 'value', name: i18n.global.t('monitor.loadDetail') },\n            {\n                type: 'value',\n                name: i18n.global.t('monitor.resourceUsage') + ' ( % )',\n                position: 'right',\n                alignTicks: true,\n            },\n        ],\n        grid: mobile.value ? { left: '15%', right: '15%', bottom: '20%' } : null,\n        tooltip: {\n            trigger: 'axis',\n            formatter: function (datas: any) {\n                return withCPUProcess(datas);\n            },\n        },\n    };\n}\n\nfunction initCPUCharts(baseDate: any, items: Host.MonitorData) {\n    let data = items.value.map(function (item: any) {\n        return { value: item.cpu.toFixed(2), top: item.topCPUItems, unit: '%' };\n    });\n    data = data.length === 0 ? loadTopEmptyData() : data;\n    chartsOption.value['loadCPUChart'] = {\n        xData: baseDate,\n        yData: [\n            {\n                name: 'CPU',\n                data: data,\n            },\n        ],\n        tooltip: {\n            trigger: 'axis',\n            formatter: function (datas: any) {\n                return withCPUProcess(datas);\n            },\n        },\n\n        formatStr: '%',\n    };\n}\n\nfunction initMemCharts(baseDate: any, items: Host.MonitorData) {\n    let data = items.value.map(function (item: any) {\n        return { value: item.memory.toFixed(2), top: item.topMemItems };\n    });\n    data = data.length === 0 ? loadTopEmptyData() : data;\n    chartsOption.value['loadMemoryChart'] = {\n        xData: baseDate,\n        yData: [\n            {\n                name: i18n.global.t('monitor.memory'),\n                data: data,\n            },\n        ],\n        tooltip: {\n            trigger: 'axis',\n            formatter: function (datas: any) {\n                return withMemProcess(datas);\n            },\n        },\n\n        formatStr: '%',\n    };\n}\n\nfunction initNetCharts(item: Host.MonitorData) {\n    let networkDate = item.date.length === 0 ? loadEmptyDate(timeRangeNetwork.value) : item.date;\n    let date = networkDate.map(function (item: any) {\n        return dateFormatWithoutYear(item);\n    });\n    let networkUp = item.value.map(function (item: any) {\n        return item.up.toFixed(2);\n    });\n    networkUp = networkUp.length === 0 ? loadEmptyData() : networkUp;\n    let networkOut = item.value.map(function (item: any) {\n        return item.down.toFixed(2);\n    });\n    networkOut = networkOut.length === 0 ? loadEmptyData() : networkOut;\n\n    chartsOption.value['loadNetworkChart'] = {\n        xData: date,\n        yData: [\n            {\n                name: i18n.global.t('monitor.up'),\n                data: networkUp,\n            },\n            {\n                name: i18n.global.t('monitor.down'),\n                data: networkOut,\n            },\n        ],\n        tooltip: {\n            trigger: 'axis',\n            formatter: function (datas: any) {\n                let res = loadDate(datas[0].name);\n                for (const item of datas) {\n                    res += loadSeries(item, computeSizeFromKBs(item.data), '');\n                }\n                return res;\n            },\n        },\n        grid: {\n            left: getSideWidth(true),\n            right: getSideWidth(true),\n            bottom: '20%',\n        },\n        formatStr: 'KB/s',\n    };\n}\n\nfunction initIOCharts(item: Host.MonitorData) {\n    let itemIODate = item.date?.length === 0 ? loadEmptyDate(timeRangeIO.value) : item.date;\n    let ioDate = itemIODate.map(function (item: any) {\n        return dateFormatWithoutYear(item);\n    });\n    let ioRead = item.value.map(function (item: any) {\n        return Number((item.read / 1024).toFixed(2));\n    });\n    ioRead = ioRead.length === 0 ? loadEmptyData() : ioRead;\n    let ioWrite = item.value.map(function (item: any) {\n        return Number((item.write / 1024).toFixed(2));\n    });\n    ioWrite = ioWrite.length === 0 ? loadEmptyData() : ioWrite;\n    let ioCount = item.value.map(function (item: any) {\n        return item.count;\n    });\n    ioCount = ioCount.length === 0 ? loadEmptyData() : ioCount;\n    let ioTime = item.value.map(function (item: any) {\n        return item.time;\n    });\n    ioTime = ioTime.length === 0 ? loadEmptyData() : ioTime;\n    chartsOption.value['loadIOChart'] = {\n        xData: ioDate,\n        yData: [\n            {\n                name: i18n.global.t('monitor.read'),\n                data: ioRead,\n            },\n            {\n                name: i18n.global.t('monitor.write'),\n                data: ioWrite,\n            },\n            {\n                name: i18n.global.t('monitor.readWriteCount'),\n                data: ioCount,\n                yAxisIndex: 1,\n            },\n            {\n                name: i18n.global.t('monitor.readWriteTime'),\n                data: ioTime,\n                yAxisIndex: 1,\n            },\n        ],\n        tooltip: {\n            trigger: 'axis',\n            formatter: function (datas: any) {\n                let res = loadDate(datas[0].name);\n                for (const item of datas) {\n                    if (\n                        item.seriesName === i18n.global.t('monitor.read') ||\n                        item.seriesName === i18n.global.t('monitor.write')\n                    ) {\n                        res += loadSeries(item, computeSizeFromKBs(item.data), '');\n                    }\n                    if (item.seriesName === i18n.global.t('monitor.readWriteCount')) {\n                        res += loadSeries(item, item.data, i18n.global.t('commons.units.time') + '/s');\n                    }\n                    if (item.seriesName === i18n.global.t('monitor.readWriteTime')) {\n                        res += loadSeries(item, item.data, ' ms');\n                    }\n                }\n                return res;\n            },\n        },\n        grid: { left: getSideWidth(true), right: getSideWidth(true), bottom: '20%' },\n        yAxis: [\n            { type: 'value', name: '( KB/s )', axisLabel: { fontSize: 10 } },\n            {\n                type: 'value',\n                position: 'right',\n                alignTicks: true,\n                axisLabel: {\n                    fontSize: 10,\n                },\n            },\n        ],\n    };\n}\n\nfunction loadEmptyDate(timeRange: any) {\n    if (timeRange.length != 2) {\n        return;\n    }\n    let date1 = new Date(timeRange[0]);\n    let date2 = new Date(timeRange[1]);\n    return [date1, date2];\n}\nfunction loadEmptyData() {\n    return [0, 0];\n}\nfunction loadTopEmptyData() {\n    return [{ value: 0, top: 0, unit: '' }];\n}\n\nfunction withCPUProcess(datas: any) {\n    let tops;\n    let res = loadDate(datas[0].name);\n    for (const item of datas) {\n        if (item.data?.top) {\n            tops = item.data?.top;\n        }\n        res += loadSeries(item, item.data.value ? item.data.value : item.data, item.data.unit || '');\n    }\n    if (!tops) {\n        return res;\n    }\n    res += `\n        <div style=\"margin-top: 10px; border-bottom: 1px dashed black;\"></div>\n        <table style=\"border-collapse: collapse; margin-top: 20px; font-size: 12px;\">\n        <thead>\n            <tr>\n            <th style=\"padding: 6px 8px;\">PID</th>\n            <th style=\"padding: 6px 8px;\">${i18n.global.t('commons.table.user')}</th>\n            <th style=\"padding: 6px 8px;\">${i18n.global.t('menu.process')}</th>\n            <th style=\"padding: 6px 8px;\">${i18n.global.t('monitor.percent')}</th>\n            </tr>\n        </thead>\n        <tbody>\n    `;\n    for (const row of tops) {\n        res += `\n            <tr>\n                <td style=\"padding: 6px 8px; text-align: center;\">\n                    ${row.pid}\n                </td>\n                <td style=\"padding: 6px 8px; text-align: center;\">\n                    ${row.user}\n                </td>\n                <td style=\"padding: 6px 8px; text-align: center;\">\n                    ${row.name}\n                </td>\n                <td style=\"padding: 6px 8px; text-align: center;\">\n                    ${row.percent.toFixed(2)}%\n                </td>\n            </tr>\n        `;\n    }\n    return res;\n}\n\nfunction withMemProcess(datas: any) {\n    let res = loadDate(datas[0].name);\n    for (const item of datas) {\n        res += loadSeries(item, item.data.value ? item.data.value : item.data, ' %');\n    }\n    if (!datas[0].data.top) {\n        return res;\n    }\n    res += `\n        <div style=\"margin-top: 10px; border-bottom: 1px dashed black;\"></div>\n        <table style=\"border-collapse: collapse; margin-top: 20px; font-size: 12px;\">\n        <thead>\n            <tr>\n                <th style=\"padding: 6px 8px;\">PID</th>\n                <th style=\"padding: 6px 8px;\">${i18n.global.t('commons.table.user')}</th>\n                <th style=\"padding: 6px 8px;\">${i18n.global.t('menu.process')}</th>\n                <th style=\"padding: 6px 8px;\">${i18n.global.t('monitor.memory')}</th>\n                <th style=\"padding: 6px 8px;\">${i18n.global.t('monitor.percent')}</th>\n            </tr>\n        </thead>\n        <tbody>\n    `;\n    for (const item of datas) {\n        for (const row of item.data.top) {\n            res += `\n                  <tr>\n                    <td style=\"padding: 6px 8px; text-align: center;\">\n                      <span style=\"display: inline-block;\"></span>\n                      ${row.pid}\n                    </td>\n                    <td style=\"padding: 6px 8px; text-align: center;\">\n                      ${row.user}\n                    </td>\n                    <td style=\"padding: 6px 8px; text-align: center;\">\n                      ${row.name}\n                    </td>\n                    <td style=\"padding: 6px 8px; text-align: center;\">\n                      ${computeSize(row.memory)}\n                    </td>\n                    <td style=\"padding: 6px 8px; text-align: center;\">\n                      ${row.percent.toFixed(2)}%\n                    </td>\n                  </tr>\n                `;\n        }\n    }\n    return res;\n}\n\nfunction loadDate(name: any) {\n    return ` <div style=\"display: inline-block; width: 100%; padding-bottom: 10px;\">\n                ${i18n.global.t('commons.search.date')}: ${name.replaceAll('\\n', ' ')}\n            </div>`;\n}\n\nfunction loadSeries(item: any, data: any, unit: any) {\n    return `<div style=\"width: 100%;\">\n                ${item.marker} ${item.seriesName}: ${data} ${unit}\n            </div>`;\n}\n\nfunction getSideWidth(b: boolean) {\n    return !b || document.body.clientWidth > 1600 ? '7%' : '10%';\n}\n\nonMounted(() => {\n    loadOptions();\n});\n</script>\n\n<style scoped lang=\"scss\">\n.content-container__search {\n    margin-top: 7px;\n    .el-card {\n        --el-card-padding: 12px;\n    }\n}\n.networkOption {\n    font-size: 16px;\n    font-weight: 500;\n    margin-top: 3px;\n    cursor: pointer;\n    color: var(--el-color-primary);\n}\n.title {\n    font-size: 16px;\n    font-weight: 500;\n}\n.chart {\n    width: 100%;\n    height: 400px;\n}\n.el-dropdown {\n    vertical-align: baseline;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/monitor/setting/days/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('monitor.storeDays')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('monitor.storeDays')\" :rules=\"[Rules.integerNumber]\" prop=\"monitorStoreDays\">\n                <el-input clearable v-model.number=\"form.monitorStoreDays\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { updateMonitorSetting } from '@/api/modules/host';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    monitorStoreDays: number;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    monitorStoreDays: 30,\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.monitorStoreDays = params.monitorStoreDays;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await updateMonitorSetting('MonitorStoreDays', form.monitorStoreDays + '')\n            .then(() => {\n                loading.value = false;\n                handleClose();\n                emit('search');\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/monitor/setting/default-io/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('monitor.defaultIO')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('monitor.defaultIO')\" prop=\"defaultIO\" :rules=\"Rules.requiredSelect\">\n                <el-select v-model=\"form.defaultIO\" filterable>\n                    <el-option\n                        v-for=\"item in ioOptions\"\n                        :key=\"item\"\n                        :label=\"item == 'all' ? $t('commons.table.all') : item\"\n                        :value=\"item\"\n                    />\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { getIOOptions, updateMonitorSetting } from '@/api/modules/host';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    defaultIO: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst ioOptions = ref();\n\nconst form = reactive({\n    defaultIO: '',\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.defaultIO = params.defaultIO;\n    loadIOOptions();\n    drawerVisible.value = true;\n};\n\nconst loadIOOptions = async () => {\n    const res = await getIOOptions();\n    ioOptions.value = res.data;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        await updateMonitorSetting('DefaultIO', form.defaultIO)\n            .then(async () => {\n                globalStore.setDefaultIO(form.defaultIO);\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                return;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/monitor/setting/default-network/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('monitor.defaultNetwork')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('monitor.defaultNetwork')\" prop=\"defaultNetwork\" :rules=\"Rules.requiredSelect\">\n                <el-select v-model=\"form.defaultNetwork\" filterable>\n                    <el-option\n                        v-for=\"item in netOptions\"\n                        :key=\"item\"\n                        :label=\"item == 'all' ? $t('commons.table.all') : item\"\n                        :value=\"item\"\n                    />\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { getNetworkOptions, updateMonitorSetting } from '@/api/modules/host';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    defaultNetwork: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst netOptions = ref();\n\nconst form = reactive({\n    defaultNetwork: '',\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.defaultNetwork = params.defaultNetwork;\n    loadNetworkOptions();\n    drawerVisible.value = true;\n};\n\nconst loadNetworkOptions = async () => {\n    const res = await getNetworkOptions();\n    netOptions.value = res.data;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        await updateMonitorSetting('DefaultNetwork', form.defaultNetwork)\n            .then(async () => {\n                globalStore.setDefaultNetwork(form.defaultNetwork);\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                return;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/monitor/setting/index.vue",
    "content": "<template>\n    <div>\n        <MonitorRouter />\n\n        <LayoutContent v-loading=\"loading\" :title=\"$t('menu.settings', 2)\" :divider=\"true\">\n            <template #main>\n                <el-form :model=\"form\" @submit.prevent ref=\"panelFormRef\" label-position=\"left\" label-width=\"160px\">\n                    <el-row>\n                        <el-col :span=\"1\"><br /></el-col>\n                        <el-col :span=\"12\">\n                            <el-form-item :label=\"$t('monitor.enableMonitor')\" prop=\"monitorStatus\">\n                                <el-switch\n                                    @change=\"onSaveStatus\"\n                                    v-model=\"form.monitorStatus\"\n                                    active-value=\"Enable\"\n                                    inactive-value=\"Disable\"\n                                />\n                            </el-form-item>\n                            <el-form-item :label=\"$t('monitor.storeDays')\" prop=\"monitorStoreDays\">\n                                <el-input disabled v-model=\"form.monitorStoreDays\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeStoreDays\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('monitor.interval')\" prop=\"monitorIntervalItem\">\n                                <el-input disabled v-model=\"form.monitorIntervalItem\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeInterval\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('monitor.defaultNetwork')\">\n                                <el-input disabled v-model=\"form.defaultNetwork\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeNetwork\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('monitor.defaultNetworkHelper') }}</span>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('monitor.defaultIO')\">\n                                <el-input disabled v-model=\"form.defaultIO\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeIO\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('monitor.defaultIOHelper') }}</span>\n                            </el-form-item>\n                            <el-form-item>\n                                <el-button @click=\"onClean()\" icon=\"Delete\">{{ $t('monitor.cleanMonitor') }}</el-button>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                </el-form>\n            </template>\n        </LayoutContent>\n\n        <Interval ref=\"intervalRef\" @search=\"search\" />\n        <StoreDays ref=\"daysRef\" @search=\"search\" />\n        <Network ref=\"networkRef\" @search=\"search()\" />\n        <IO ref=\"ioRef\" @search=\"search()\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref } from 'vue';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { cleanMonitors, loadMonitorSetting, updateMonitorSetting } from '@/api/modules/host';\nimport MonitorRouter from '@/views/host/monitor/index.vue';\nimport Interval from '@/views/host/monitor/setting/interval/index.vue';\nimport StoreDays from '@/views/host/monitor/setting/days/index.vue';\nimport Network from '@/views/host/monitor/setting/default-network/index.vue';\nimport IO from '@/views/host/monitor/setting/default-io/index.vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { splitTimeFromSecond, transTimeUnit } from '@/utils/util';\n\nconst loading = ref();\nconst form = reactive({\n    monitorStatus: 'Disable',\n    monitorStoreDays: 30,\n    monitorInterval: 300,\n    timeItem: 5,\n    timeUnit: 'm',\n    monitorIntervalItem: '',\n    defaultNetwork: '',\n    defaultIO: '',\n});\nconst panelFormRef = ref<FormInstance>();\n\nconst intervalRef = ref();\nconst daysRef = ref();\nconst networkRef = ref();\nconst ioRef = ref();\n\nconst search = async () => {\n    const res = await loadMonitorSetting();\n    form.monitorStatus = res.data.monitorStatus;\n    form.monitorInterval = Number(res.data.monitorInterval);\n    let item = splitTimeFromSecond(form.monitorInterval);\n    form.timeItem = item.timeItem;\n    form.timeUnit = item.timeUnit;\n    form.monitorIntervalItem = transTimeUnit(form.timeItem + form.timeUnit);\n\n    form.monitorStoreDays = Number(res.data.monitorStoreDays);\n    form.defaultNetwork =\n        res.data.defaultNetwork === 'all' ? i18n.global.t('commons.table.all') : res.data.defaultNetwork;\n    form.defaultIO = res.data.defaultIO === 'all' ? i18n.global.t('commons.table.all') : res.data.defaultIO;\n};\n\nconst onSaveStatus = async () => {\n    loading.value = true;\n    await updateMonitorSetting('MonitorStatus', form.monitorStatus)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onChangeStoreDays = () => {\n    daysRef.value.acceptParams({ monitorStoreDays: form.monitorStoreDays });\n};\nconst onChangeInterval = () => {\n    intervalRef.value.acceptParams({ timeItem: form.timeItem, timeUnit: form.timeUnit });\n};\nconst onChangeNetwork = () => {\n    networkRef.value.acceptParams({ defaultNetwork: form.defaultNetwork });\n};\nconst onChangeIO = () => {\n    ioRef.value.acceptParams({ defaultIO: form.defaultIO });\n};\n\nconst onClean = async () => {\n    ElMessageBox.confirm(i18n.global.t('monitor.cleanHelper'), i18n.global.t('monitor.cleanMonitor'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        await cleanMonitors()\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/monitor/setting/interval/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('monitor.interval')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('monitor.interval')\" prop=\"monitorInterval\">\n                <el-input type=\"number\" @input=\"loadInterval()\" class=\"selectClass\" v-model.number=\"form.timeItem\">\n                    <template #append>\n                        <el-select v-model=\"form.timeUnit\" @change=\"loadInterval\" style=\"width: 80px\">\n                            <el-option :label=\"$t('commons.units.second')\" value=\"s\" />\n                            <el-option :label=\"$t('commons.units.minute')\" value=\"m\" />\n                            <el-option :label=\"$t('commons.units.hour')\" value=\"h\" />\n                        </el-select>\n                    </template>\n                </el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { updateMonitorSetting } from '@/api/modules/host';\nimport { transferTimeToSecond } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    timeItem: number;\n    timeUnit: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    timeItem: 5,\n    timeUnit: 'h',\n    monitorInterval: 300,\n});\n\nconst verifyInterval = (rule: any, value: any, callback: any) => {\n    if (value < 10 || value > 43200) {\n        callback(new Error(i18n.global.t('monitor.intervalHelper')));\n        return;\n    }\n    callback();\n};\nconst rules = reactive({\n    monitorInterval: [Rules.integerNumber, { validator: verifyInterval, trigger: 'blur', required: true }],\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.timeItem = params.timeItem;\n    form.timeUnit = params.timeUnit;\n    drawerVisible.value = true;\n};\n\nconst loadInterval = () => {\n    form.monitorInterval = transferTimeToSecond(form.timeItem + form.timeUnit);\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await updateMonitorSetting('MonitorInterval', form.monitorInterval + '')\n            .then(() => {\n                loading.value = false;\n                handleClose();\n                emit('search');\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/process/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.process', 2),\n        path: '/hosts/process/process',\n    },\n    {\n        label: i18n.global.t('menu.network', 2),\n        path: '/hosts/process/network',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/process/network/index.vue",
    "content": "<template>\n    <div>\n        <FireRouter />\n        <LayoutContent :title=\"$t('menu.network', 2)\" v-loading=\"processStore.netLoading\">\n            <template #rightToolBar>\n                <div class=\"w-full flex justify-end items-center gap-5\">\n                    <el-select\n                        v-model=\"filters\"\n                        :placeholder=\"$t('commons.table.status')\"\n                        clearable\n                        multiple\n                        collapse-tags\n                        collapse-tags-tooltip\n                        :max-collapse-tags=\"2\"\n                        @change=\"search()\"\n                        class=\"p-w-300\"\n                    >\n                        <el-option\n                            v-for=\"item in statusOptions\"\n                            :key=\"item.value\"\n                            :label=\"item.text\"\n                            :value=\"item.value\"\n                        />\n                    </el-select>\n                    <TableSearch\n                        @search=\"search()\"\n                        :placeholder=\"$t('process.pid')\"\n                        v-model:searchName=\"processStore.netSearch.processID\"\n                    />\n                    <TableSearch\n                        @search=\"search()\"\n                        :placeholder=\"$t('process.processName')\"\n                        v-model:searchName=\"processStore.netSearch.processName\"\n                    />\n                    <TableSearch\n                        @search=\"search()\"\n                        :placeholder=\"$t('commons.table.port')\"\n                        v-model:searchName=\"processStore.netSearch.port\"\n                    />\n                </div>\n            </template>\n\n            <template #main>\n                <div class=\"!h-[900px]\">\n                    <el-auto-resizer>\n                        <template #default=\"{ height, width }\">\n                            <el-table-v2\n                                :columns=\"columns\"\n                                :data=\"data\"\n                                :width=\"width\"\n                                :height=\"height\"\n                                :sort-by=\"sortState\"\n                                @column-sort=\"changeSort\"\n                            />\n                        </template>\n                    </el-auto-resizer>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport FireRouter from '@/views/host/process/index.vue';\nimport { ref, onMounted, onUnmounted, watch, h } from 'vue';\nimport { GlobalStore, ProcessStore } from '@/store';\nimport { SortBy, TableV2SortOrder, ElIcon } from 'element-plus';\nimport { Filter } from '@element-plus/icons-vue';\nimport i18n from '@/lang';\n\nconst statusOptions = [\n    { text: 'LISTEN', value: 'LISTEN' },\n    { text: 'ESTABLISHED', value: 'ESTABLISHED' },\n    { text: 'TIME_WAIT', value: 'TIME_WAIT' },\n    { text: 'CLOSE_WAIT', value: 'CLOSE_WAIT' },\n    { text: 'NONE', value: 'NONE' },\n];\n\nconst globalStore = GlobalStore();\nconst processStore = ProcessStore();\n\nconst quickSearchName = (name: string) => {\n    processStore.netSearch.processID = undefined;\n    processStore.netSearch.processName = name;\n    processStore.netSearch.port = undefined;\n    search();\n};\n\nconst quickSearchPort = (port: number) => {\n    processStore.netSearch.processID = undefined;\n    processStore.netSearch.processName = '';\n    processStore.netSearch.port = port;\n    search();\n};\n\nconst data = ref<any[]>([]);\n\nconst sortState = ref<SortBy>({\n    key: 'PID',\n    order: TableV2SortOrder.ASC,\n});\nconst filters = ref<string[]>(['LISTEN', 'ESTABLISHED']);\n\nconst sortByNum = (a: any, b: any, prop: string): number => {\n    const aVal = parseFloat(a[prop]) || 0;\n    const bVal = parseFloat(b[prop]) || 0;\n    return aVal - bVal;\n};\n\nconst columns = ref([\n    {\n        key: 'type',\n        title: i18n.global.t('commons.table.type'),\n        dataKey: 'type',\n        width: 220,\n    },\n    {\n        key: 'PID',\n        title: 'PID',\n        dataKey: 'PID',\n        width: 220,\n        sortable: true,\n        sortMethod: sortByNum,\n    },\n    {\n        key: 'name',\n        title: i18n.global.t('process.processName'),\n        dataKey: 'name',\n        width: 300,\n        cellRenderer: ({ rowData }) => {\n            return h('div', { class: 'flex items-center gap-1' }, [\n                h('span', { class: 'truncate', title: rowData.name }, rowData.name),\n                h(\n                    ElIcon,\n                    {\n                        class: 'cursor-pointer hover:text-primary ml-1 flex-shrink-0',\n                        size: 14,\n                        onClick: (e: Event) => {\n                            e.stopPropagation();\n                            quickSearchName(rowData.name);\n                        },\n                    },\n                    () => h(Filter),\n                ),\n            ]);\n        },\n    },\n    {\n        key: 'localaddr',\n        title: i18n.global.t('process.laddr'),\n        dataKey: 'localaddr',\n        width: 350,\n        cellRenderer: ({ rowData }) => {\n            const addr = rowData.localaddr;\n            const addrStr = addr?.ip ? `${addr.ip}${addr.port > 0 ? ':' + addr.port : ''}` : '';\n            const hasPort = addr?.port > 0;\n            return h('div', { class: 'flex items-center gap-1' }, [\n                h('span', {}, addrStr),\n                hasPort\n                    ? h(\n                          ElIcon,\n                          {\n                              class: 'cursor-pointer hover:text-primary ml-1',\n                              size: 12,\n                              onClick: (e: Event) => {\n                                  e.stopPropagation();\n                                  quickSearchPort(addr.port);\n                              },\n                          },\n                          () => h(Filter),\n                      )\n                    : null,\n            ]);\n        },\n    },\n    {\n        key: 'remoteaddr',\n        title: i18n.global.t('process.raddr'),\n        dataKey: 'remoteaddr',\n        width: 350,\n        cellRenderer: ({ rowData }) => {\n            const addr = rowData.remoteaddr;\n            return addr?.ip ? `${addr.ip}${addr.port > 0 ? ':' + addr.port : ''}` : '';\n        },\n    },\n    {\n        key: 'status',\n        title: i18n.global.t('commons.table.status'),\n        dataKey: 'status',\n        width: 380,\n        cellRenderer: ({ rowData }) => rowData.status,\n    },\n]);\n\nwatch(\n    [sortState, () => processStore.netData, filters],\n    ([newState, newData, newFilters]) => {\n        if (!newData?.length) {\n            data.value = [];\n            return;\n        }\n\n        let filtered = newData;\n        if (newFilters.length > 0) {\n            filtered = filtered.filter((row) => newFilters.includes(row.status));\n        }\n\n        const { key, order } = newState ?? {};\n        if (!key || !order) {\n            data.value = filtered;\n            return;\n        }\n\n        const currCol = columns.value.find((c) => c.key === key);\n        if (!currCol) {\n            data.value = filtered;\n            return;\n        }\n\n        const sortMethod = currCol.sortMethod ?? sortByNum;\n        data.value = filtered.slice().sort((a, b) => {\n            const res = (sortMethod as any)(a, b, currCol.dataKey);\n            return order === TableV2SortOrder.ASC ? res : -res;\n        });\n    },\n    { immediate: true },\n);\n\nconst changeSort = ({ key, order }) => {\n    if (!order) order = TableV2SortOrder.ASC;\n    sortState.value = { key, order };\n};\n\nconst search = () => {\n    processStore.sendNetMessage();\n};\n\nonMounted(() => {\n    processStore.connect(globalStore.currentNode);\n    const initialDelay = processStore.netData.length > 0 ? 500 : 0;\n    processStore.startPolling('net', 3000, initialDelay);\n});\n\nonUnmounted(() => {\n    processStore.stopPolling();\n    processStore.disconnect();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/process/process/detail/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('app.detail')\" :resource=\"resourceName\" @close=\"handleClose\" size=\"large\">\n        <template #content>\n            <el-tabs v-model=\"activeName\" type=\"card\" v-loading=\"loading\">\n                <el-tab-pane :label=\"$t('process.basic')\" name=\"basic\">\n                    <el-descriptions :column=\"2\" border>\n                        <el-descriptions-item :label=\"$t('commons.table.name')\" min-width=\"100px\">\n                            {{ data.name }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.status')\">\n                            {{ data.status }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('process.pid')\">{{ data.PID }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('process.ppid')\">{{ data.PPID }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('process.numThreads')\">\n                            {{ data.numThreads }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('process.numConnections')\">\n                            {{ data.numConnections }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('process.diskRead')\">\n                            {{ data.diskRead }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('process.diskWrite')\">\n                            {{ data.diskWrite }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('commons.table.user')\">\n                            {{ data.username }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('process.startTime')\">\n                            {{ data.startTime }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('process.cmdLine')\">\n                            {{ data.cmdLine }}\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('process.mem')\" name=\"mem\">\n                    <el-descriptions :column=\"2\" border>\n                        <el-descriptions-item :label=\"'rss'\">{{ data.rss }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'pss'\">{{ data.pss }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'uss'\">{{ data.uss }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'swap'\">{{ data.swap }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'shared'\">{{ data.shared }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'vms'\">{{ data.vms }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'hwm'\">{{ data.hwm }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'data'\">{{ data.data }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'stack'\">{{ data.stack }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'locked'\">{{ data.locked }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'text'\">{{ data.text }}</el-descriptions-item>\n                        <el-descriptions-item :label=\"'dirty'\">{{ data.dirty }}</el-descriptions-item>\n                    </el-descriptions>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('process.openFiles')\" name=\"openFiles\">\n                    <el-table max-height=\"800px\" :data=\"data.openFiles\" border class=\"w-full\">\n                        <el-table-column prop=\"path\" :label=\"$t('menu.files')\" />\n                        <el-table-column prop=\"fd\" label=\"fd\" width=\"100px\" />\n                    </el-table>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('process.env')\" name=\"env\">\n                    <CodemirrorPro\n                        :placeholder=\"$t('commons.msg.noneData')\"\n                        v-model=\"envStr\"\n                        :heightDiff=\"300\"\n                        :disabled=\"true\"\n                    ></CodemirrorPro>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('process.net')\" name=\"net\">\n                    <el-table max-height=\"800px\" :data=\"data.connects\" border class=\"w-full\">\n                        <el-table-column prop=\"localaddr\" :label=\"$t('process.laddr')\">\n                            <template #default=\"{ row }\">\n                                <span>{{ row.localaddr.ip }}</span>\n                                <span v-if=\"row.localaddr.port > 0\">:{{ row.localaddr.port }}</span>\n                            </template>\n                        </el-table-column>\n                        <el-table-column prop=\"remoteaddr\" :label=\"$t('process.raddr')\">\n                            <template #default=\"{ row }\">\n                                <span>{{ row.remoteaddr.ip }}</span>\n                                <span v-if=\"row.remoteaddr.port > 0\">:{{ row.remoteaddr.port }}</span>\n                            </template>\n                        </el-table-column>\n                        <el-table-column prop=\"status\" :label=\"$t('commons.table.status')\" />\n                    </el-table>\n                </el-tab-pane>\n            </el-tabs>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getProcessByID } from '@/api/modules/process';\nimport { ref } from 'vue';\n\nconst open = ref(false);\nconst data = ref({\n    name: '',\n    envs: [] as string[],\n    status: '',\n    PID: 0,\n    PPID: 0,\n    numThreads: 0,\n    numConnections: 0,\n    diskRead: '',\n    diskWrite: '',\n    username: '',\n    startTime: '',\n    cmdLine: '',\n    rss: '',\n    swap: '',\n    vms: '',\n    hwm: '',\n    data: '',\n    stack: '',\n    locked: '',\n    openFiles: [],\n    connects: [],\n});\nconst resourceName = ref('');\nconst activeName = ref('basic');\nconst envStr = ref('');\nconst loading = ref(false);\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = async (pid: number): Promise<void> => {\n    open.value = true;\n    activeName.value = 'basic';\n    loading.value = true;\n    try {\n        const res = await getProcessByID(pid);\n        data.value = res.data;\n        resourceName.value = data.value.name;\n        if (data.value.envs) {\n            envStr.value = data.value.envs.join('\\n');\n        }\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/process/process/index.vue",
    "content": "<template>\n    <div>\n        <FireRouter />\n        <LayoutContent :title=\"$t('menu.process', 2)\" v-loading=\"processStore.psLoading\">\n            <template #rightToolBar>\n                <div class=\"w-full flex justify-end items-center gap-5\">\n                    <el-select\n                        v-model=\"filters\"\n                        :placeholder=\"$t('commons.table.status')\"\n                        clearable\n                        @change=\"search()\"\n                        class=\"p-w-300\"\n                        multiple\n                        collapse-tags\n                        collapse-tags-tooltip\n                        :max-collapse-tags=\"4\"\n                    >\n                        <el-option\n                            v-for=\"item in statusOptions\"\n                            :key=\"item.value\"\n                            :label=\"item.text\"\n                            :value=\"item.value\"\n                        />\n                    </el-select>\n                    <TableSearch\n                        @search=\"search()\"\n                        :placeholder=\"$t('process.pid')\"\n                        v-model:searchName=\"processStore.psSearch.pid\"\n                    />\n                    <TableSearch\n                        @search=\"search()\"\n                        :placeholder=\"$t('commons.table.name')\"\n                        v-model:searchName=\"processStore.psSearch.name\"\n                    />\n                    <TableSearch\n                        @search=\"search()\"\n                        :placeholder=\"$t('commons.table.user')\"\n                        v-model:searchName=\"processStore.psSearch.username\"\n                    />\n                </div>\n            </template>\n            <template #main>\n                <div class=\"!h-[900px]\">\n                    <el-auto-resizer>\n                        <template #default=\"{ height, width }\">\n                            <el-table-v2\n                                @column-sort=\"changeSort\"\n                                :columns=\"columns\"\n                                :data=\"data\"\n                                :width=\"width\"\n                                :height=\"height\"\n                                :sort-by=\"sortState\"\n                            ></el-table-v2>\n                        </template>\n                    </el-auto-resizer>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <ProcessDetail ref=\"detailRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport FireRouter from '@/views/host/process/index.vue';\nimport { ref, onMounted, onUnmounted, computed, watch, h } from 'vue';\nimport ProcessDetail from './detail/index.vue';\nimport i18n from '@/lang';\nimport { stopProcess } from '@/api/modules/process';\nimport { GlobalStore, ProcessStore } from '@/store';\nimport { SortBy, TableV2SortOrder, ElButton } from 'element-plus';\n\nconst globalStore = GlobalStore();\nconst processStore = ProcessStore();\n\nconst statusOptions = computed(() => [\n    { text: i18n.global.t('process.running'), value: 'running' },\n    { text: i18n.global.t('process.sleep'), value: 'sleep' },\n    { text: i18n.global.t('process.stop'), value: 'stop' },\n    { text: i18n.global.t('process.idle'), value: 'idle' },\n    { text: i18n.global.t('process.wait'), value: 'wait' },\n    { text: i18n.global.t('process.lock'), value: 'lock' },\n    { text: i18n.global.t('process.zombie'), value: 'zombie' },\n]);\n\nconst opRef = ref();\nconst sortState = ref<SortBy>({\n    key: 'PID',\n    order: TableV2SortOrder.ASC,\n});\n\nconst data = ref<any[]>([]);\nconst detailRef = ref();\nconst filters = ref<string[]>([]);\n\nconst sortByNum = (a: any, b: any, prop: string): number => {\n    const aVal = parseFloat(a[prop]) || 0;\n    const bVal = parseFloat(b[prop]) || 0;\n    return aVal - bVal;\n};\n\nconst columns = ref([\n    {\n        key: 'PID',\n        title: 'PID',\n        dataKey: 'PID',\n        width: 120,\n        sortable: true,\n        sortMethod: sortByNum,\n    },\n    {\n        key: 'name',\n        title: i18n.global.t('commons.table.name'),\n        dataKey: 'name',\n        width: 400,\n    },\n    {\n        key: 'ppid',\n        title: i18n.global.t('process.ppid'),\n        dataKey: 'PPID',\n        width: 120,\n    },\n    {\n        key: 'numThreads',\n        title: i18n.global.t('process.numThreads'),\n        dataKey: 'numThreads',\n        width: 120,\n    },\n    {\n        key: 'username',\n        title: i18n.global.t('commons.table.user'),\n        dataKey: 'username',\n        width: 200,\n    },\n    {\n        key: 'cpuValue',\n        title: 'CPU',\n        dataKey: 'cpuValue',\n        width: 200,\n        sortable: true,\n        sortMethod: sortByNum,\n        cellRenderer: ({ rowData }) => {\n            return rowData.cpuPercent;\n        },\n    },\n    {\n        key: 'rssValue',\n        title: i18n.global.t('process.memory'),\n        dataKey: 'rssValue',\n        width: 200,\n        sortable: true,\n        sortMethod: sortByNum,\n        cellRenderer: ({ rowData }) => {\n            return rowData.rss;\n        },\n    },\n    {\n        key: 'numConnections',\n        title: i18n.global.t('process.numConnections'),\n        dataKey: 'numConnections',\n        width: 100,\n        sortable: true,\n    },\n    {\n        key: 'status',\n        title: i18n.global.t('commons.table.status'),\n        dataKey: 'status',\n        width: 100,\n        cellRenderer: ({ rowData }) => {\n            if (rowData.status) {\n                return i18n.global.t('process.' + rowData.status);\n            }\n            return '';\n        },\n    },\n    {\n        key: 'startTime',\n        title: i18n.global.t('process.startTime'),\n        dataKey: 'startTime',\n        width: 300,\n    },\n    {\n        key: 'actions',\n        title: i18n.global.t('commons.table.operate'),\n        dataKey: 'actions',\n        width: 300,\n        cellRenderer: ({ rowData }) => {\n            return h('div', { class: 'action-buttons' }, [\n                h(\n                    ElButton,\n                    {\n                        type: 'text',\n                        onClick: () => openDetail(rowData),\n                    },\n                    () => i18n.global.t('process.viewDetails'),\n                ),\n                h(\n                    ElButton,\n                    {\n                        type: 'text',\n                        onClick: () => stop(rowData),\n                    },\n                    () => i18n.global.t('process.stopProcess'),\n                ),\n            ]);\n        },\n    },\n]);\n\nwatch(\n    [sortState, () => processStore.psData, filters],\n    ([newState, newData, newFilters]) => {\n        if (!newData?.length) {\n            data.value = [];\n            return;\n        }\n\n        let filtered = newData;\n        if (newFilters.length > 0) {\n            filtered = filtered.filter((re: any) => newFilters.includes(re.status));\n        }\n\n        const { key, order } = newState ?? {};\n        if (!key || !order) {\n            data.value = filtered;\n            return;\n        }\n\n        const currCol = columns.value.find((c) => c.key === key);\n        if (!currCol) {\n            data.value = filtered;\n            return;\n        }\n\n        const currSortMethod = currCol.sortMethod ?? sortByNum;\n        data.value = filtered.slice(0).sort((a, b) => {\n            const res = (currSortMethod as any)(a, b, currCol.dataKey);\n            return order === TableV2SortOrder.ASC ? res : 0 - res;\n        });\n    },\n    { immediate: true },\n);\n\nconst openDetail = (row: any) => {\n    detailRef.value.acceptParams(row.PID);\n};\n\nconst changeSort = ({ key, order }) => {\n    if (!order) order = TableV2SortOrder.ASC;\n    sortState.value = { key, order };\n};\n\nconst search = () => {\n    processStore.sendPsMessage();\n};\n\nconst stop = async (row: any) => {\n    opRef.value.acceptParams({\n        title: i18n.global.t('process.stopProcess'),\n        names: [row.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('menu.process'),\n            i18n.global.t('process.stopProcess'),\n        ]),\n        api: stopProcess,\n        params: { PID: row.PID },\n        successMsg: i18n.global.t('commons.msg.operationSuccess'),\n    });\n};\n\nonMounted(() => {\n    processStore.connect(globalStore.currentNode);\n    const initialDelay = processStore.psData.length > 0 ? 500 : 0;\n    processStore.startPolling('ps', 3000, initialDelay);\n});\n\nonUnmounted(() => {\n    processStore.stopPolling();\n    processStore.disconnect();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.config', 2),\n        path: '/hosts/ssh/ssh',\n    },\n    {\n        label: i18n.global.t('ssh.session', 2),\n        path: '/hosts/ssh/session',\n    },\n    {\n        label: i18n.global.t('ssh.loginLogs', 2),\n        path: '/hosts/ssh/log',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/log/index.vue",
    "content": "<template>\n    <div>\n        <FireRouter />\n        <Log />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport FireRouter from '@/views/host/ssh/index.vue';\nimport Log from '@/views/host/ssh/log/log.vue';\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/log/log.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('ssh.loginLogs', 2)\">\n            <template #prompt>\n                <el-alert type=\"info\" :title=\"$t('ssh.sshAlert2')\" :closable=\"false\" />\n                <div class=\"mt-2\"><el-alert type=\"info\" :title=\"$t('ssh.sshAlert')\" :closable=\"false\" /></div>\n            </template>\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onExport\">\n                    {{ $t('commons.button.export') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"searchStatus\" @change=\"search()\" class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.status') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"All\"></el-option>\n                    <el-option :label=\"$t('commons.status.success')\" value=\"Success\"></el-option>\n                    <el-option :label=\"$t('commons.status.failed')\" value=\"Failed\"></el-option>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchInfo\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"ssh-log-refresh\" @search=\"search()\" />\n            </template>\n\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"data\" @search=\"search\" :heightDiff=\"400\">\n                    <el-table-column min-width=\"80\" :label=\"$t('logs.loginIP')\" prop=\"address\" />\n                    <el-table-column min-width=\"60\" :label=\"$t('ssh.belong')\" prop=\"area\" />\n                    <el-table-column min-width=\"60\" :label=\"$t('commons.table.port')\" prop=\"port\" />\n                    <el-table-column min-width=\"60\" :label=\"$t('ssh.loginMode')\" prop=\"authMode\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.authMode\">{{ $t('ssh.' + row.authMode) }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column min-width=\"60\" :label=\"$t('commons.table.user')\" prop=\"user\" />\n                    <el-table-column min-width=\"60\" :label=\"$t('logs.loginStatus')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.status === 'Success'\">\n                                <el-tag type=\"success\">{{ $t('commons.status.success') }}</el-tag>\n                            </div>\n                            <div v-else>\n                                <el-tooltip class=\"box-item\" effect=\"dark\" :content=\"row.message\" placement=\"top-start\">\n                                    <el-tag type=\"danger\">{{ $t('commons.status.failed') }}</el-tag>\n                                </el-tooltip>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"date\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <DialogPro v-model=\"open\" :title=\"$t('commons.button.export')\" size=\"mini\">\n            <el-form class=\"mt-5\" ref=\"backupForm\" @submit.prevent v-loading=\"loading\">\n                <el-form-item :label=\"$t('commons.table.status')\">\n                    <el-select v-model=\"exportConfig.status\" class=\"w-full\">\n                        <el-option :label=\"$t('commons.table.all')\" value=\"All\"></el-option>\n                        <el-option :label=\"$t('commons.status.success')\" value=\"Success\"></el-option>\n                        <el-option :label=\"$t('commons.status.failed')\" value=\"Failed\"></el-option>\n                    </el-select>\n                </el-form-item>\n                <el-form-item :label=\"$t('container.lines')\">\n                    <el-select class=\"tailClass\" v-model.number=\"exportConfig.count\">\n                        <el-option :value=\"-1\" :label=\"$t('commons.table.all')\" />\n                        <el-option :value=\"100\" :label=\"100\" />\n                        <el-option :value=\"200\" :label=\"200\" />\n                        <el-option :value=\"500\" :label=\"500\" />\n                        <el-option :value=\"1000\" :label=\"1000\" />\n                    </el-select>\n                </el-form-item>\n            </el-form>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"open = false\" :disabled=\"loading\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                    <el-button type=\"primary\" @click=\"onSubmitExport\" :disabled=\"loading\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </span>\n            </template>\n        </DialogPro>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { dateFormat, downloadFile } from '@/utils/util';\nimport { onMounted, reactive, ref } from 'vue';\nimport { exportSSHLogs, loadSSHLogs } from '@/api/modules/host';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref();\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'ssh-log-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('ssh-log-page-size')) || 20,\n    total: 0,\n});\n\nconst open = ref();\nconst exportConfig = reactive({\n    count: 100,\n    status: 'All',\n});\nconst searchInfo = ref();\nconst searchStatus = ref('All');\n\nconst search = async () => {\n    let params = {\n        info: searchInfo.value,\n        status: searchStatus.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await loadSSHLogs(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onExport = async () => {\n    open.value = true;\n    exportConfig.status = 'All';\n    exportConfig.count = -1;\n};\n\nconst onSubmitExport = async () => {\n    let params = {\n        info: '',\n        status: exportConfig.status,\n        page: 1,\n        pageSize: exportConfig.count,\n    };\n    await exportSSHLogs(params)\n        .then((res) => {\n            if (res.data) {\n                downloadFile(res.data, globalStore.currentNode);\n            }\n            open.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/session/index.vue",
    "content": "<template>\n    <div>\n        <FireRouter />\n        <LayoutContent :title=\"$t('ssh.session', 2)\">\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"sshSearch.loginUser\" />\n            </template>\n            <template #main>\n                <ComplexTable :data=\"data\" ref=\"tableRef\" v-loading=\"loading\" :heightDiff=\"260\">\n                    <el-table-column :label=\"$t('commons.table.user')\" fix prop=\"username\"></el-table-column>\n                    <el-table-column :label=\"'TTY'\" fix prop=\"terminal\"></el-table-column>\n                    <el-table-column :label=\"$t('ssh.loginIP')\" fix prop=\"host\"></el-table-column>\n                    <el-table-column\n                        :label=\"$t('ssh.loginTime')\"\n                        fix\n                        prop=\"loginTime\"\n                        min-width=\"120px\"\n                    ></el-table-column>\n                    <fu-table-operations :ellipsis=\"10\" :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport FireRouter from '@/views/host/ssh/index.vue';\nimport { ref, onMounted, onUnmounted, reactive } from 'vue';\nimport i18n from '@/lang';\nimport { stopProcess } from '@/api/modules/process';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst sshSearch = reactive({\n    type: 'ssh',\n    loginUser: '',\n});\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.disConn'),\n        click: function (row: any) {\n            stop(row.PID);\n        },\n    },\n];\n\nlet processSocket = ref(null) as unknown as WebSocket;\nconst data = ref([]);\nconst loading = ref(false);\nconst tableRef = ref();\n\nconst isWsOpen = () => {\n    const readyState = processSocket && processSocket.readyState;\n    return readyState === 1;\n};\nconst closeSocket = () => {\n    if (isWsOpen()) {\n        processSocket && processSocket.close();\n    }\n};\n\nconst onOpenProcess = () => {};\nconst onMessage = (message: any) => {\n    let result: any[] = JSON.parse(message.data);\n    data.value = result;\n    loading.value = false;\n};\n\nconst onerror = () => {};\nconst onClose = () => {\n    closeSocket();\n};\n\nconst initProcess = () => {\n    let href = window.location.href;\n    let protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss';\n    let ipLocal = href.split('//')[1].split('/')[0];\n    let currentNode = globalStore.currentNode;\n    processSocket = new WebSocket(`${protocol}://${ipLocal}/api/v2/process/ws?operateNode=${currentNode}`);\n    processSocket.onopen = onOpenProcess;\n    processSocket.onmessage = onMessage;\n    processSocket.onerror = onerror;\n    processSocket.onclose = onClose;\n    search();\n    sendMsg();\n};\n\nconst sendMsg = () => {\n    loading.value = true;\n    setInterval(() => {\n        search();\n    }, 3000);\n};\n\nconst search = () => {\n    if (isWsOpen()) {\n        processSocket.send(JSON.stringify(sshSearch));\n    }\n};\n\nconst stop = async (PID: number) => {\n    ElMessageBox.confirm(i18n.global.t('ssh.stopSSHWarn'), i18n.global.t('commons.button.disConn'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    })\n        .then(async () => {\n            try {\n                await stopProcess({ PID: PID });\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            } catch (error) {\n                MsgError(error);\n            }\n        })\n        .catch(() => {});\n};\n\nonMounted(() => {\n    initProcess();\n});\n\nonUnmounted(() => {\n    closeSocket();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/ssh/address/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('ssh.listenAddress')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :rules=\"rules\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\">\n                <template #default>\n                    <span>\n                        {{ $t('ssh.listenHelper', [form.port]) }}\n                    </span>\n                </template>\n            </el-alert>\n            <el-form-item label=\"IPv4\" prop=\"listenAddressV4\">\n                <el-checkbox\n                    v-model=\"form.ipv4All\"\n                    @change=\"form.listenAddressV4 = form.ipv4All ? '0.0.0.0' : form.listenAddressV4\"\n                >\n                    {{ $t('setting.bindAll') }}\n                </el-checkbox>\n                <el-input :disabled=\"form.ipv4All\" clearable v-model=\"form.listenAddressV4\"></el-input>\n            </el-form-item>\n            <el-form-item label=\"IPv6\" prop=\"listenAddressV6\">\n                <el-checkbox\n                    v-model=\"form.ipv6All\"\n                    @change=\"form.listenAddressV6 = form.ipv6All ? '::' : form.listenAddressV6\"\n                >\n                    {{ $t('setting.bindAll') }}\n                </el-checkbox>\n                <el-input :disabled=\"form.ipv6All\" clearable v-model=\"form.listenAddressV6\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { updateSSH } from '@/api/modules/host';\nimport { checkIp, checkIpV6 } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    port: number;\n    address: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    port: 22,\n    ipv4All: false,\n    ipv6All: false,\n    listenAddressV4: '',\n    listenAddressV6: '',\n});\n\nconst rules = reactive({\n    listenAddressV4: [{ validator: checkIPv4, trigger: 'blur' }],\n    listenAddressV6: [{ validator: checkIPv6, trigger: 'blur' }],\n});\n\nfunction checkIPv4(rule: any, value: any, callback: any) {\n    if (value === '') {\n        callback();\n    }\n    if (checkIp(value)) {\n        return callback(new Error(i18n.global.t('commons.rule.ip')));\n    }\n    callback();\n}\n\nfunction checkIPv6(rule: any, value: any, callback: any) {\n    if (value === '') {\n        callback();\n    }\n    if (checkIpV6(value)) {\n        return callback(new Error(i18n.global.t('commons.rule.ip')));\n    }\n    callback();\n}\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    let items = params.address.split(',');\n    for (const item of items) {\n        if (item.indexOf(':') !== -1) {\n            form.listenAddressV6 = item;\n            form.ipv6All = item === '::';\n            continue;\n        }\n        form.listenAddressV4 = item;\n        form.ipv4All = item === '0.0.0.0';\n    }\n    form.port = params.port;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let itemAddr = [];\n        if (form.listenAddressV4 !== '') {\n            itemAddr.push(form.listenAddressV4);\n        }\n        if (form.listenAddressV6 !== '') {\n            itemAddr.push(form.listenAddressV6);\n        }\n        let addr =\n            itemAddr.join(',') === '' || itemAddr.join(',') === '0.0.0.0,::'\n                ? i18n.global.t('ssh.allV4V6', [form.port])\n                : itemAddr.join(',');\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('ssh.listenAddress'), addr]),\n            i18n.global.t('ssh.sshChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        )\n            .then(async () => {\n                let params = {\n                    key: 'ListenAddress',\n                    oldValue: '',\n                    newValue: itemAddr.join(','),\n                };\n                loading.value = true;\n                await updateSSH(params)\n                    .then(() => {\n                        loading.value = false;\n                        handleClose();\n                        emit('search');\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    })\n                    .catch(() => {\n                        loading.value = false;\n                    });\n            })\n            .catch(() => {\n                emit('search');\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/ssh/auth-keys/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"open\" :header=\"$t('ssh.authKeys')\" size=\"large\">\n            <div v-loading=\"loading\">\n                <CodemirrorPro\n                    :heightDiff=\"160\"\n                    v-model=\"conf\"\n                    :lineWrapping=\"true\"\n                    placeholder=\"# The authorized_keys file does not exist or is empty (~/ssh/authorized_keys)\"\n                />\n            </div>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveFile\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </span>\n            </template>\n        </DrawerPro>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { loadSSHFile, updateSSHByFile } from '@/api/modules/host';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ref } from 'vue';\nconst conf = ref();\nconst loading = ref();\nconst open = ref();\n\nconst acceptParams = async (): Promise<void> => {\n    loadFile();\n    open.value = true;\n};\nconst loadFile = async () => {\n    const res = await loadSSHFile('authKeys');\n    conf.value = res.data || '';\n};\nconst onSaveFile = async () => {\n    ElMessageBox.confirm(i18n.global.t('ssh.authKeysHelper'), i18n.global.t('ssh.authKeys'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        await updateSSHByFile('authKeys', conf.value)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                open.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/ssh/certification/index.vue",
    "content": "<template>\n    <div>\n        <DrawerPro v-model=\"drawerVisible\" :header=\"$t('ssh.pubkey')\" @close=\"handleClose\" size=\"large\">\n            <div class=\"mb-4\">\n                <el-alert :closable=\"false\">{{ $t('ssh.pubKeyHelper', [currentUser]) }}</el-alert>\n            </div>\n            <el-button type=\"primary\" plain @click=\"onOpenDialog('create')\">\n                {{ $t('commons.button.create') }}\n            </el-button>\n            <el-button plain @click=\"onSync()\">\n                {{ $t('commons.button.sync') }}\n            </el-button>\n            <el-button plain :disabled=\"selects.length === 0\" @click=\"onDelete(null)\">\n                {{ $t('commons.button.delete') }}\n            </el-button>\n            <ComplexTable\n                :pagination-config=\"paginationConfig\"\n                v-model:selects=\"selects\"\n                :data=\"data\"\n                @search=\"search\"\n                :heightDiff=\"370\"\n            >\n                <el-table-column type=\"selection\" fix />\n                <el-table-column :label=\"$t('commons.table.name')\" show-overflow-tooltip prop=\"name\" />\n                <el-table-column :label=\"$t('ssh.encryptionMode')\" prop=\"encryptionMode\" />\n                <el-table-column :label=\"$t('commons.table.description')\" prop=\"description\" />\n                <fu-table-operations width=\"200px\" :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" />\n            </ComplexTable>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                </span>\n            </template>\n        </DrawerPro>\n\n        <DialogPro v-model=\"connOpen\" :title=\"$t('ssh.pubkey')\" size=\"small\" :showClose=\"false\">\n            <el-descriptions class=\"margin-top\" :column=\"1\" border>\n                <el-descriptions-item align=\"center\" :label=\"$t('ssh.password')\">\n                    <div>\n                        <span>{{ loadPassPhrase() }}</span>\n                    </div>\n                    <el-button\n                        v-if=\"currentRow.passPhrase && Base64.decode(currentRow.passPhrase) !== '<UN-SET>'\"\n                        size=\"small\"\n                        icon=\"CopyDocument\"\n                        @click=\"onCopy(currentRow.passPhrase)\"\n                    >\n                        {{ $t('commons.button.copy') }}\n                    </el-button>\n                </el-descriptions-item>\n                <el-descriptions-item align=\"center\" :label=\"$t('ssh.publicKey')\">\n                    <el-button-group size=\"small\">\n                        <el-button icon=\"CopyDocument\" @click=\"onCopy(currentRow.publicKey)\">\n                            {{ $t('commons.button.copy') }}\n                        </el-button>\n                        <el-button icon=\"Download\" @click=\"onDownload(currentRow, 'publicKey')\">\n                            {{ $t('commons.button.download') }}\n                        </el-button>\n                    </el-button-group>\n                </el-descriptions-item>\n                <el-descriptions-item align=\"center\" :label=\"$t('ssh.privateKey')\">\n                    <el-button-group size=\"small\">\n                        <el-button icon=\"CopyDocument\" @click=\"onCopy(currentRow.privateKey)\">\n                            {{ $t('commons.button.copy') }}\n                        </el-button>\n                        <el-button icon=\"Download\" @click=\"onDownload(currentRow, 'privateKey')\">\n                            {{ $t('commons.button.download') }}\n                        </el-button>\n                    </el-button-group>\n                </el-descriptions-item>\n            </el-descriptions>\n            <template #footer>\n                <span class=\"dialog-footer\">\n                    <el-button @click=\"connOpen = false\">\n                        {{ $t('commons.button.cancel') }}\n                    </el-button>\n                </span>\n            </template>\n        </DialogPro>\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"onSubmitDelete()\">\n            <template #content>\n                <el-form ref=\"deleteForm\" label-position=\"left\">\n                    <el-form-item>\n                        <el-checkbox v-model=\"forceDelete\" :label=\"$t('website.forceDelete')\" />\n                        <span class=\"input-help\">\n                            {{ $t('website.forceDeleteHelper') }}\n                        </span>\n                    </el-form-item>\n                </el-form>\n            </template>\n        </OpDialog>\n        <Operate ref=\"dialogRef\" @search=\"search\" />\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { Host } from '@/api/interface/host';\nimport { deleteCert, searchCert, syncCert } from '@/api/modules/host';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport Operate from '@/views/host/ssh/ssh/certification/operate/index.vue';\nimport { copyText } from '@/utils/util';\nimport { Base64 } from 'js-base64';\nimport { reactive, ref } from 'vue';\n\nconst loading = ref();\nconst drawerVisible = ref();\nconst data = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    cacheSizeKey: 'login-log-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('login-log-page-size')) || 20,\n    total: 0,\n    small: true,\n});\n\nconst forceDelete = ref();\nconst operateIDs = ref();\nconst opRef = ref();\n\nconst currentRow = ref();\nconst connOpen = ref();\nconst currentUser = ref();\n\nconst acceptParams = async (user: string): Promise<void> => {\n    search();\n    currentUser.value = user || 'root';\n    drawerVisible.value = true;\n};\n\nconst loadPassPhrase = () => {\n    if (currentRow.value.passPhrase === '') {\n        return '-';\n    }\n    let pass = Base64.decode(currentRow.value.passPhrase);\n    return pass === '<UN-SET>' ? i18n.global.t('ssh.unSyncPass') : '';\n};\n\nconst onCopy = async (content: string) => {\n    content = Base64.decode(content);\n    copyText(content);\n};\n\nconst dialogRef = ref();\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Host.RootCertInfo> = {\n        mode: 'generate',\n        encryptionMode: 'ed25519',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onDownload = async (row: Host.RootCertInfo, type: string) => {\n    let name = row.name;\n    let content;\n    if (type === 'publicKey') {\n        name = row.name + '.pub';\n        content = Base64.decode(row.publicKey);\n    } else {\n        content = Base64.decode(row.privateKey);\n    }\n    const downloadUrl = window.URL.createObjectURL(new Blob([content], { type: 'application/octet-stream' }));\n    const a = document.createElement('a');\n    a.style.display = 'none';\n    a.href = downloadUrl;\n    a.download = name;\n    const event = new MouseEvent('click');\n    a.dispatchEvent(event);\n\n    setTimeout(() => {\n        document.body.removeChild(a);\n        window.URL.revokeObjectURL(downloadUrl);\n    }, 100);\n};\n\nconst search = async () => {\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchCert(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items;\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onSync = async () => {\n    ElMessageBox.confirm(i18n.global.t('ssh.syncHelper'), i18n.global.t('commons.button.sync'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        await syncCert()\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onDelete = async (row: Host.RootCertInfo | null) => {\n    let names = [];\n    let ids = [];\n    forceDelete.value = false;\n    if (row) {\n        ids = [row.id];\n        names = [row.name + ' - ' + row.encryptionMode];\n    } else {\n        for (const item of selects.value) {\n            names.push(item.name + ' - ' + item.encryptionMode);\n            ids.push(item.id);\n        }\n    }\n    operateIDs.value = ids;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('menu.cronjob'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\n\nconst onSubmitDelete = async () => {\n    loading.value = true;\n    await deleteCert(operateIDs.value, forceDelete.value)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Host.RootCertInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.view'),\n        click: (row: Host.RootCertInfo) => {\n            currentRow.value = row;\n            connOpen.value = true;\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Host.RootCertInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.marginTop {\n    margin-top: 10px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/ssh/certification/operate/index.vue",
    "content": "<template>\n    <DialogPro\n        v-model=\"drawerVisible\"\n        :header=\"title\"\n        @close=\"handleClose\"\n        :resource=\"dialogData.title !== 'edit' ? '' : dialogData.rowData?.name\"\n        size=\"large\"\n        :autoClose=\"false\"\n        :fullScreen=\"true\"\n    >\n        <el-form ref=\"formRef\" label-position=\"top\" :rules=\"rules\" :model=\"dialogData.rowData\" v-loading=\"loading\">\n            <el-row :gutter=\"20\">\n                <el-col :span=\"12\">\n                    <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                        <el-input v-model=\"dialogData.rowData.name\" />\n                    </el-form-item>\n                </el-col>\n            </el-row>\n            <el-row :gutter=\"20\">\n                <el-col :span=\"12\">\n                    <el-form-item :label=\"$t('ssh.encryptionMode')\" prop=\"encryptionMode\">\n                        <el-select v-model=\"dialogData.rowData.encryptionMode\">\n                            <el-option label=\"ED25519\" value=\"ed25519\" />\n                            <el-option label=\"ECDSA\" value=\"ecdsa\" />\n                            <el-option label=\"RSA\" value=\"rsa\" />\n                            <el-option label=\"DSA\" value=\"dsa\" />\n                        </el-select>\n                    </el-form-item>\n                </el-col>\n                <el-col :span=\"12\">\n                    <el-form-item :label=\"$t('commons.login.password')\" prop=\"passPhrase\">\n                        <el-input v-model=\"dialogData.rowData.passPhrase\" type=\"password\" show-password>\n                            <template #append>\n                                <el-button @click=\"random\">\n                                    {{ $t('commons.button.random') }}\n                                </el-button>\n                            </template>\n                        </el-input>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n            <el-form-item :label=\"$t('ssh.createMode')\" prop=\"mode\" v-if=\"dialogData.title === 'create'\">\n                <el-radio-group v-model=\"dialogData.rowData.mode\">\n                    <el-radio value=\"generate\">{{ $t('ssh.generate') }}</el-radio>\n                    <el-radio value=\"input\">{{ $t('ssh.input') }}</el-radio>\n                    <el-radio value=\"import\">{{ $t('ssh.import') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <div v-if=\"dialogData.rowData.mode === 'input'\">\n                <el-row :gutter=\"20\">\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('ssh.privateKey')\" prop=\"privateKey\">\n                            <el-input type=\"textarea\" :rows=\"2\" v-model=\"dialogData.rowData.privateKey\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('ssh.publicKey')\" prop=\"publicKey\">\n                            <el-input type=\"textarea\" :rows=\"2\" v-model=\"dialogData.rowData.publicKey\" />\n                        </el-form-item>\n                    </el-col>\n                </el-row>\n            </div>\n            <div v-if=\"dialogData.rowData.mode === 'import'\">\n                <el-row :gutter=\"20\">\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('ssh.privateKey')\" prop=\"privateKey\">\n                            <el-upload\n                                action=\"#\"\n                                :auto-upload=\"false\"\n                                ref=\"uploadPrivateRef\"\n                                class=\"upload mt-2 w-full\"\n                                :limit=\"1\"\n                                :on-change=\"privateOnChange\"\n                                :on-exceed=\"privateExceed\"\n                            >\n                                <el-button size=\"small\" icon=\"Upload\">\n                                    {{ $t('commons.button.upload') }}\n                                </el-button>\n                            </el-upload>\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('ssh.publicKey')\" prop=\"publicKey\">\n                            <el-upload\n                                action=\"#\"\n                                :auto-upload=\"false\"\n                                ref=\"uploadPublicRef\"\n                                class=\"upload mt-2 w-full\"\n                                :limit=\"1\"\n                                :on-change=\"publicOnChange\"\n                                :on-exceed=\"publicExceed\"\n                            >\n                                <el-button size=\"small\" icon=\"Upload\">\n                                    {{ $t('commons.button.upload') }}\n                                </el-button>\n                            </el-upload>\n                        </el-form-item>\n                    </el-col>\n                </el-row>\n            </div>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input v-model=\"dialogData.rowData.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onConfirm(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElForm, genFileId, UploadFile, UploadProps, UploadRawFile } from 'element-plus';\nimport { Host } from '@/api/interface/host';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport { getRandomStr } from '@/utils/util';\nimport { createCert, editCert } from '@/api/modules/host';\nimport { Base64 } from 'js-base64';\n\ninterface DialogProps {\n    title: string;\n    rowData?: Host.RootCertInfo;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst loading = ref();\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref();\nconst uploadPrivateRef = ref();\nconst uploadPublicRef = ref();\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    if (params.title === 'edit') {\n        params.rowData.mode = 'input';\n        dialogData.value.rowData.publicKey = Base64.decode(params.rowData.publicKey);\n        dialogData.value.rowData.privateKey = Base64.decode(params.rowData.privateKey);\n        if (params.rowData.passPhrase) {\n            dialogData.value.rowData.passPhrase = Base64.decode(params.rowData.passPhrase);\n        }\n    }\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nfunction checkPassword(rule: any, value: any, callback: any) {\n    if (dialogData.value.rowData.passPhrase !== '') {\n        const reg = /^[A-Za-z0-9]{6,15}$/;\n        if (!reg.test(dialogData.value.rowData.passPhrase)) {\n            return callback(new Error(i18n.global.t('ssh.passwordHelper')));\n        }\n    }\n    callback();\n}\nconst rules = reactive({\n    name: Rules.simpleName,\n    encryptionMode: Rules.requiredSelect,\n    passPhrase: [{ validator: checkPassword, trigger: 'blur' }],\n    privateKey: [Rules.requiredInput],\n    publicKey: [Rules.requiredInput],\n});\n\nconst onConfirm = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        if (dialogData.value.title === 'create') {\n            await createCert(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        } else {\n            await editCert(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\nconst privateOnChange = (_uploadFile: UploadFile) => {\n    const reader = new FileReader();\n    reader.onload = (e) => {\n        try {\n            dialogData.value.rowData.privateKey = e.target.result as string;\n        } catch (error) {\n            MsgError(i18n.global.t('commons.msg.errImport') + error.message);\n        }\n    };\n    reader.readAsText(_uploadFile.raw);\n};\nconst privateExceed: UploadProps['onExceed'] = (files) => {\n    uploadPrivateRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadPrivateRef.value!.handleStart(file);\n};\nconst publicOnChange = (_uploadFile: UploadFile) => {\n    const reader = new FileReader();\n    reader.onload = (e) => {\n        try {\n            dialogData.value.rowData.publicKey = e.target.result as string;\n        } catch (error) {\n            MsgError(i18n.global.t('commons.msg.errImport') + error.message);\n        }\n    };\n    reader.readAsText(_uploadFile.raw);\n};\nconst publicExceed: UploadProps['onExceed'] = (files) => {\n    uploadPublicRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadPublicRef.value!.handleStart(file);\n};\n\nconst random = async () => {\n    dialogData.value.rowData.passPhrase = getRandomStr(10);\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/ssh/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <FireRouter />\n\n        <div class=\"app-status card-interval\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag class=\"float-left\" effect=\"dark\" type=\"success\">SSH</el-tag>\n                        <Status class=\"mt-0.5\" :status=\"form.isActive ? 'enable' : 'disable'\" :msg=\"form.message\" />\n                    </div>\n                    <div class=\"mt-0.5\">\n                        <el-button v-if=\"form.isActive\" type=\"primary\" @click=\"onOperate('stop')\" link>\n                            {{ $t('commons.button.stop') }}\n                        </el-button>\n                        <el-button v-if=\"!form.isActive\" type=\"primary\" @click=\"onOperate('start')\" link>\n                            {{ $t('commons.button.start') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" @click=\"onOperate('restart')\" link>\n                            {{ $t('commons.button.restart') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" link>\n                            {{ $t('ssh.autoStart') }}\n                        </el-button>\n                        <el-switch\n                            size=\"small\"\n                            class=\"ml-2\"\n                            inactive-value=\"disable\"\n                            active-value=\"enable\"\n                            @change=\"onOperate(autoStart)\"\n                            v-model=\"autoStart\"\n                        />\n                    </div>\n                </div>\n            </el-card>\n        </div>\n\n        <LayoutContent>\n            <template #leftToolBar>\n                <el-radio-group v-model=\"confShowType\" @change=\"changeMode\">\n                    <el-radio-button value=\"base\">{{ $t('database.baseConf') }}</el-radio-button>\n                    <el-radio-button value=\"all\">{{ $t('database.allConf') }}</el-radio-button>\n                </el-radio-group>\n\n                <el-button @click=\"onOpenDrawer\">{{ $t('ssh.pubkey') }}</el-button>\n                <el-button @click=\"onOpenAuthKeys\">{{ $t('ssh.authKeys') }}</el-button>\n            </template>\n            <template #main>\n                <el-row class=\"mt-10\" v-if=\"confShowType === 'base'\">\n                    <el-col :xs=\"24\" :sm=\"20\" :md=\"20\" :lg=\"10\" :xl=\"10\">\n                        <el-form :model=\"form\" label-position=\"right\" ref=\"formRef\" label-width=\"120px\">\n                            <el-form-item :label=\"$t('ssh.port')\" prop=\"port\">\n                                <el-input disabled v-model.number=\"form.port\">\n                                    <template #append>\n                                        <el-button @click=\"onChangePort\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('ssh.portHelper') }}</span>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('ssh.listenAddress')\" prop=\"listenAddress\">\n                                <el-input disabled v-model=\"form.listenAddressItem\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeAddress\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('ssh.addressHelper') }}</span>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('ssh.permitRootLogin')\" prop=\"permitRootLoginItem\">\n                                <el-input disabled v-model=\"form.permitRootLoginItem\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeRoot\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('ssh.rootSettingHelper') }}</span>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('ssh.passwordAuthentication')\" prop=\"passwordAuthentication\">\n                                <el-switch\n                                    active-value=\"yes\"\n                                    inactive-value=\"no\"\n                                    @change=\"onSave(formRef, 'PasswordAuthentication', form.passwordAuthentication)\"\n                                    v-model=\"form.passwordAuthentication\"\n                                ></el-switch>\n                                <span class=\"input-help\">{{ $t('ssh.pwdAuthHelper') }}</span>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('ssh.pubkeyAuthentication')\" prop=\"pubkeyAuthentication\">\n                                <el-switch\n                                    active-value=\"yes\"\n                                    inactive-value=\"no\"\n                                    @change=\"onSave(formRef, 'PubkeyAuthentication', form.pubkeyAuthentication)\"\n                                    v-model=\"form.pubkeyAuthentication\"\n                                ></el-switch>\n                                <span class=\"input-help\">{{ $t('ssh.keyAuthHelper') }}</span>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('ssh.useDNS')\" prop=\"useDNS\">\n                                <el-switch\n                                    active-value=\"yes\"\n                                    inactive-value=\"no\"\n                                    @change=\"onSave(formRef, 'UseDNS', form.useDNS)\"\n                                    v-model=\"form.useDNS\"\n                                ></el-switch>\n                                <span class=\"input-help\">{{ $t('ssh.dnsHelper') }}</span>\n                            </el-form-item>\n                        </el-form>\n                    </el-col>\n                </el-row>\n\n                <div v-if=\"confShowType === 'all'\">\n                    <CodemirrorPro\n                        :heightDiff=\"320\"\n                        :minHeight=\"350\"\n                        class=\"mt-5\"\n                        v-model=\"sshConf\"\n                        mode=\"nginx\"\n                        placeholder=\"# The SSH configuration file does not exist or is empty (/etc/ssh/sshd_config)\"\n                    ></CodemirrorPro>\n                    <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveFile\" class=\"mt-2.5\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <AuthKeys ref=\"authKeyRef\" />\n        <Cert ref=\"pubKeyRef\" @search=\"search\" />\n        <Port ref=\"portRef\" @search=\"search\" />\n        <Address ref=\"addressRef\" @search=\"search\" />\n        <Root ref=\"rootsRef\" @search=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref } from 'vue';\nimport FireRouter from '@/views/host/ssh/index.vue';\nimport AuthKeys from '@/views/host/ssh/ssh/auth-keys/index.vue';\nimport Cert from '@/views/host/ssh/ssh/certification/index.vue';\nimport Root from '@/views/host/ssh/ssh/root/index.vue';\nimport Port from '@/views/host/ssh/ssh/port/index.vue';\nimport Address from '@/views/host/ssh/ssh/address/index.vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { loadSSHFile, getSSHInfo, operateSSH, updateSSH, updateSSHByFile } from '@/api/modules/host';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\n\nconst loading = ref(false);\nconst formRef = ref();\nconst confShowType = ref('base');\nconst pubKeyRef = ref();\nconst portRef = ref();\nconst addressRef = ref();\nconst rootsRef = ref();\nconst authKeyRef = ref();\n\nconst autoStart = ref('enable');\n\nconst sshConf = ref();\nconst form = reactive({\n    isActive: false,\n    message: '',\n    port: 22,\n    listenAddress: '',\n    listenAddressItem: '',\n    passwordAuthentication: 'yes',\n    pubkeyAuthentication: 'yes',\n    encryptionMode: '',\n    primaryKey: '',\n    permitRootLogin: 'yes',\n    permitRootLoginItem: 'yes',\n    useDNS: 'no',\n    currentUser: 'root',\n});\n\nconst onSaveFile = async () => {\n    ElMessageBox.confirm(i18n.global.t('ssh.sshFileChangeHelper'), i18n.global.t('ssh.sshChange'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        await updateSSHByFile('sshdConf', sshConf.value)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onOpenDrawer = () => {\n    pubKeyRef.value.acceptParams(form.currentUser);\n};\nconst onOpenAuthKeys = () => {\n    authKeyRef.value.acceptParams();\n};\n\nconst onChangePort = () => {\n    portRef.value.acceptParams({ port: form.port });\n};\nconst onChangeRoot = () => {\n    rootsRef.value.acceptParams({ permitRootLogin: form.permitRootLogin });\n};\nconst onChangeAddress = () => {\n    addressRef.value.acceptParams({ address: form.listenAddress, port: form.port });\n};\n\nconst onOperate = async (operation: string) => {\n    let msg = operation === 'enable' || operation === 'disable' ? 'ssh.' : 'commons.button.';\n    ElMessageBox.confirm(i18n.global.t('ssh.sshOperate', [i18n.global.t(msg + operation)]), 'SSH', {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    })\n        .then(async () => {\n            loading.value = true;\n            await operateSSH(operation)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    search();\n                })\n                .catch(() => {\n                    autoStart.value = operation === 'enable' ? 'disable' : 'enable';\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            search();\n        });\n};\n\nconst onSave = async (formEl: FormInstance | undefined, key: string, value: string) => {\n    if (!formEl) return;\n    let itemKey = key.replace(key[0], key[0].toLowerCase());\n    const result = await formEl.validateField(itemKey, callback);\n    if (!result) {\n        return;\n    }\n\n    ElMessageBox.confirm(\n        i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('ssh.' + itemKey), changeI18n(value)]),\n        i18n.global.t('ssh.sshChange'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    )\n        .then(async () => {\n            let params = {\n                key: key,\n                oldValue: '',\n                newValue: value,\n            };\n            loading.value = true;\n            await updateSSH(params)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            search();\n        });\n};\nfunction callback(error: any) {\n    if (error) {\n        return error.message;\n    } else {\n        return;\n    }\n}\n\nconst changeI18n = (value: string) => {\n    switch (value) {\n        case 'yes':\n            return i18n.global.t('commons.button.enable');\n        case 'no':\n            return i18n.global.t('commons.button.disable');\n        default:\n            return value;\n    }\n};\n\nconst loadSSHConf = async () => {\n    const res = await loadSSHFile('sshdConf');\n    sshConf.value = res.data || '';\n};\n\nconst changeMode = async () => {\n    if (confShowType.value === 'all') {\n        loadSSHConf();\n    } else {\n        search();\n    }\n};\n\nconst search = async () => {\n    const res = await getSSHInfo();\n    form.isActive = res.data.isActive;\n    form.port = Number(res.data.port);\n    autoStart.value = res.data.autoStart ? 'enable' : 'disable';\n    form.listenAddress = res.data.listenAddress;\n    form.listenAddressItem =\n        form.listenAddress === '' || form.listenAddress === '0.0.0.0,::'\n            ? i18n.global.t('ssh.allV4V6', [form.port])\n            : form.listenAddress;\n    form.passwordAuthentication = res.data.passwordAuthentication;\n    form.pubkeyAuthentication = res.data.pubkeyAuthentication;\n    form.permitRootLogin = res.data.permitRootLogin;\n    form.permitRootLoginItem = loadPermitLabel(res.data.permitRootLogin);\n    form.useDNS = res.data.useDNS;\n    form.currentUser = res.data.currentUser;\n};\n\nconst loadPermitLabel = (value: string) => {\n    switch (value) {\n        case 'yes':\n            return i18n.global.t('ssh.rootHelper1');\n        case 'no':\n            return i18n.global.t('ssh.rootHelper2');\n        case 'without-password':\n            return i18n.global.t('ssh.rootHelper3');\n        case 'forced-commands-only':\n            return i18n.global.t('ssh.rootHelper4');\n    }\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/ssh/port/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('ssh.port')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('ssh.port')\" prop=\"port\" :rules=\"Rules.port\">\n                <el-input clearable v-model.number=\"form.port\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { updateSSH } from '@/api/modules/host';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    port: number;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst oldPort = ref();\n\nconst form = reactive({\n    port: 22,\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.port = params.port;\n    oldPort.value = params.port;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('commons.table.port'), form.port]),\n            i18n.global.t('ssh.sshChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        )\n            .then(async () => {\n                let params = {\n                    key: 'Port',\n                    oldValue: oldPort.value + '',\n                    newValue: form.port + '',\n                };\n                loading.value = true;\n                await updateSSH(params)\n                    .then(() => {\n                        loading.value = false;\n                        handleClose();\n                        emit('search');\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    })\n                    .catch(() => {\n                        loading.value = false;\n                    });\n            })\n            .catch(() => {\n                emit('search');\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/host/ssh/ssh/root/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('ssh.permitRootLogin')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('ssh.permitRootLogin')\" prop=\"permitRootLogin\">\n                <el-select v-model=\"form.permitRootLogin\" style=\"width: 100%\">\n                    <el-option :label=\"$t('ssh.rootHelper1')\" value=\"yes\" />\n                    <el-option :label=\"$t('ssh.rootHelper2')\" value=\"no\" />\n                    <el-option :label=\"$t('ssh.rootHelper3')\" value=\"without-password\" />\n                    <el-option :label=\"$t('ssh.rootHelper4')\" value=\"forced-commands-only\" />\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { updateSSH } from '@/api/modules/host';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    permitRootLogin: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    permitRootLogin: 'yes',\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.permitRootLogin = params.permitRootLogin;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [\n                i18n.global.t('ssh.permitRootLogin'),\n                loadPermitLabel(form.permitRootLogin),\n            ]),\n            i18n.global.t('ssh.sshChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        )\n            .then(async () => {\n                let params = {\n                    key: 'PermitRootLogin',\n                    oldValue: '',\n                    newValue: form.permitRootLogin,\n                };\n                loading.value = true;\n                await updateSSH(params)\n                    .then(() => {\n                        loading.value = false;\n                        handleClose();\n                        emit('search');\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    })\n                    .catch(() => {\n                        loading.value = false;\n                    });\n            })\n            .catch(() => {\n                emit('search');\n            });\n    });\n};\n\nconst loadPermitLabel = (value: string) => {\n    switch (value) {\n        case 'yes':\n            return i18n.global.t('ssh.rootHelper1');\n        case 'no':\n            return i18n.global.t('ssh.rootHelper2');\n        case 'without-password':\n            return i18n.global.t('ssh.rootHelper3');\n        case 'forced-commands-only':\n            return i18n.global.t('ssh.rootHelper4');\n    }\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/log/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\n\nconst buttons = [\n    {\n        label: i18n.global.t('logs.panelLog'),\n        path: '/logs/operation',\n    },\n    {\n        label: i18n.global.t('ssh.loginLogs'),\n        path: '/logs/ssh',\n    },\n    {\n        label: i18n.global.t('logs.websiteLog'),\n        path: '/logs/website',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/log/login/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('logs.login')\">\n            <template #search>\n                <LogRouter current=\"LoginLog\" />\n            </template>\n            <template #leftToolBar>\n                <el-button type=\"primary\" plain @click=\"onClean()\">\n                    {{ $t('logs.deleteLogs') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"searchStatus\" @change=\"search()\" clearable class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.status') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"\"></el-option>\n                    <el-option :label=\"$t('commons.status.success')\" value=\"Success\"></el-option>\n                    <el-option :label=\"$t('commons.status.failed')\" value=\"Failed\"></el-option>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchIP\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"login-log-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"data\" @search=\"search\" :heightDiff=\"370\">\n                    <el-table-column :label=\"$t('logs.loginIP')\" prop=\"ip\" />\n                    <el-table-column :label=\"$t('logs.loginAddress')\" prop=\"address\" />\n                    <el-table-column :label=\"$t('logs.loginAgent')\" show-overflow-tooltip prop=\"agent\" />\n                    <el-table-column :label=\"$t('logs.loginStatus')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" :msg=\"loadMsg(row.message)\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmitClean\"></ConfirmDialog>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport LogRouter from '@/views/log/router/index.vue';\nimport { dateFormat } from '@/utils/util';\nimport { cleanLogs, getLoginLogs } from '@/api/modules/log';\nimport { onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst data = ref();\nconst confirmDialogRef = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'login-log-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('login-log-page-size')) || 20,\n    total: 0,\n});\nconst searchIP = ref<string>('');\nconst searchStatus = ref<string>('');\n\nconst search = async () => {\n    let params = {\n        ip: searchIP.value,\n        status: searchStatus.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await getLoginLogs(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items;\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onClean = async () => {\n    let params = {\n        header: i18n.global.t('logs.deleteLogs'),\n        operationInfo: i18n.global.t('commons.msg.delete'),\n        submitInputInfo: i18n.global.t('logs.deleteLogs'),\n    };\n    confirmDialogRef.value!.acceptParams(params);\n};\n\nconst loadMsg = (msg: string) => {\n    if (msg === 'ErrAuth') {\n        return i18n.global.t('commons.login.errorAuthInfo');\n    }\n    if (msg === 'ErrMFA') {\n        return i18n.global.t('commons.login.errorMfaInfo');\n    }\n    return msg;\n};\n\nconst onSubmitClean = async () => {\n    await cleanLogs({ logType: 'login' });\n    search();\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/log/operation/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('logs.operation')\">\n            <template #search>\n                <LogRouter current=\"OperationLog\" />\n            </template>\n            <template #leftToolBar>\n                <el-button type=\"primary\" plain @click=\"onClean()\">\n                    {{ $t('logs.deleteLogs') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"searchGroup\" @change=\"search()\" clearable class=\"p-w-200\">\n                    <template #prefix>{{ $t('logs.resource') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"\" />\n                    <el-option :label=\"$t('logs.detail.apps')\" value=\"apps\" />\n                    <el-option :label=\"$t('logs.detail.openresty')\" value=\"openresty\" />\n                    <el-option :label=\"$t('logs.detail.websites')\" value=\"websites\" />\n                    <el-option :label=\"$t('logs.detail.runtimes')\" value=\"runtimes\" />\n                    <el-option :label=\"$t('logs.detail.ai')\" value=\"ai\" />\n                    <el-option :label=\"$t('logs.detail.databases')\" value=\"databases\" />\n                    <el-option :label=\"$t('logs.detail.containers')\" value=\"containers\" />\n                    <el-option :label=\"$t('menu.system')\" value=\"hosts\" />\n                    <el-option :label=\"$t('logs.detail.files')\" value=\"files\" />\n                    <el-option :label=\"$t('logs.detail.cronjobs')\" value=\"cronjobs\" />\n                    <el-option :label=\"$t('logs.detail.toolbox')\" value=\"toolbox\" />\n                    <el-option :label=\"$t('logs.detail.process')\" value=\"process\" />\n                    <el-option label=\"WAF\" value=\"waf\" />\n                    <el-option :label=\"$t('logs.detail.nodes')\" value=\"nodes\" />\n                    <el-option :label=\"$t('logs.detail.tampers')\" value=\"tampers\" />\n                    <el-option :label=\"$t('logs.detail.xsetting')\" value=\"xsetting\" />\n                    <el-option :label=\"$t('logs.detail.licenses')\" value=\"licenses\" />\n                    <el-option :label=\"$t('logs.detail.logs')\" value=\"logs\" />\n                    <el-option :label=\"$t('logs.detail.settings')\" value=\"settings\" />\n                    <el-option :label=\"$t('logs.detail.backups')\" value=\"backups\" />\n                    <el-option :label=\"$t('logs.detail.groups')\" value=\"groups\" />\n                    <el-option :label=\"$t('logs.detail.commands')\" value=\"commands\" />\n                </el-select>\n                <el-select v-model=\"searchStatus\" @change=\"search()\" clearable class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.status') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"\" />\n                    <el-option :label=\"$t('commons.status.success')\" value=\"Success\" />\n                    <el-option :label=\"$t('commons.status.failed')\" value=\"Failed\" />\n                </el-select>\n                <el-select v-model=\"searchNode\" @change=\"search()\" clearable class=\"p-w-200\">\n                    <template #prefix>{{ $t('xpack.node.node') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"\" />\n                    <el-option :label=\"globalStore.getMasterAlias()\" value=\"local\" />\n                    <el-option v-for=\"(node, index) in nodes\" :key=\"index\" :label=\"node.name\" :value=\"node.name\" />\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"operation-log-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"data\" @search=\"search\" :heightDiff=\"370\">\n                    <el-table-column :label=\"$t('logs.resource')\" prop=\"group\" fix>\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.source\">\n                                {{ $t('logs.detail.' + row.source) }}\n                            </span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.operate')\" min-width=\"150px\" prop=\"detailZH\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"globalStore.language === 'zh' || globalStore.language === 'zh-Hant'\">\n                                {{ row.detailZH }}\n                            </span>\n                            <span v-if=\"globalStore.language === 'en'\">{{ row.detailEN }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column v-if=\"globalStore.isMasterProductPro\" :label=\"$t('xpack.node.node')\" prop=\"node\">\n                        <template #default=\"{ row }\">\n                            <span>{{ row.node === 'local' ? globalStore.getMasterAlias() : row.node }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" :msg=\"row.message\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmitClean\"></ConfirmDialog>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport LogRouter from '@/views/log/router/index.vue';\nimport { dateFormat } from '@/utils/util';\nimport { cleanLogs, getOperationLogs } from '@/api/modules/log';\nimport { onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nimport { listNodeOptions } from '@/api/modules/setting';\n\nconst loading = ref();\nconst data = ref();\nconst confirmDialogRef = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'operation-log-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('operation-log-page-size')) || 20,\n    total: 0,\n});\nconst searchName = ref<string>('');\nconst searchGroup = ref<string>('');\nconst searchStatus = ref<string>('');\nconst searchNode = ref<string>('');\nconst nodes = ref();\n\nconst globalStore = GlobalStore();\n\nconst search = async () => {\n    let params = {\n        operation: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        status: searchStatus.value,\n        source: searchGroup.value,\n        node: searchNode.value,\n    };\n    loading.value = true;\n    await getOperationLogs(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            if (globalStore.language === 'zh' || globalStore.language === 'zh-Hant') {\n                for (const item of data.value) {\n                    item.detailZH = loadDetail(item.detailZH);\n                }\n            }\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onClean = async () => {\n    let params = {\n        header: i18n.global.t('logs.deleteLogs'),\n        operationInfo: i18n.global.t('commons.msg.delete'),\n        submitInputInfo: i18n.global.t('logs.deleteLogs'),\n    };\n    confirmDialogRef.value!.acceptParams(params);\n};\n\nconst loadDetail = (log: string) => {\n    return log.replace(/\\[([^\\]]+)\\]/g, (matched, token: string) => {\n        const transKey = resolveReplacementKey(token);\n        if (!transKey) {\n            return matched;\n        }\n        return '[' + i18n.global.t(transKey) + ']';\n    });\n};\n\nconst loadNodes = async () => {\n    await listNodeOptions('')\n        .then((res) => {\n            if (!res) {\n                nodes.value = [];\n                return;\n            }\n            nodes.value = res.data || [];\n        })\n        .catch(() => {\n            nodes.value = [];\n        });\n};\n\nconst normalizedReplacements: Record<string, string> = {\n    enable: 'commons.button.enable',\n    disable: 'commons.button.disable',\n    start: 'commons.button.start',\n    stop: 'commons.button.stop',\n    restart: 'commons.button.restart',\n    reload: 'commons.operate.reload',\n    sync: 'commons.button.sync',\n    update: 'commons.button.update',\n    upgrade: 'commons.button.upgrade',\n    open: 'commons.button.open',\n    close: 'commons.button.close',\n    up: 'commons.button.up',\n    down: 'commons.button.down',\n    login: 'commons.button.login',\n    delete: 'commons.button.delete',\n    create: 'commons.button.create',\n    add: 'commons.button.add',\n    edit: 'commons.button.edit',\n    save: 'commons.button.save',\n    clean: 'commons.button.clean',\n    clear: 'commons.button.clean',\n    get: 'commons.button.get',\n    install: 'commons.button.install',\n    uninstall: 'commons.button.uninstall',\n    backup: 'commons.button.backup',\n    recover: 'commons.button.recover',\n    upload: 'commons.button.upload',\n    download: 'commons.button.download',\n    bind: 'commons.button.bind',\n    unbind: 'commons.button.unbind',\n    verify: 'commons.button.verify',\n    rebuild: 'commons.operate.rebuild',\n    remove: 'commons.msg.remove',\n    kill: 'container.kill',\n    pause: 'container.pause',\n    unpause: 'container.unpause',\n    allow: 'firewall.allow',\n    deny: 'firewall.deny',\n    accept: 'firewall.accept',\n    drop: 'firewall.drop',\n    reject: 'firewall.stop',\n    running: 'commons.status.running',\n    stopped: 'commons.status.stopped',\n    success: 'commons.status.success',\n    failed: 'commons.status.failed',\n    created: 'commons.status.created',\n    restarting: 'commons.status.restarting',\n    paused: 'commons.status.paused',\n    exited: 'commons.status.exited',\n    dead: 'commons.status.dead',\n    light: 'setting.light',\n    dark: 'setting.dark',\n    darkgold: 'setting.darkGold',\n    auto: 'setting.auto',\n    cn: 'setting.cn',\n    intl: 'setting.intl',\n    status: 'commons.table.status',\n    all: 'commons.table.all',\n    operate: 'commons.table.operate',\n    true: 'commons.true',\n    false: 'commons.false',\n};\n\nconst exactReplacements: Record<string, string> = {\n    disableBanPing: 'firewall.disableBanPing',\n    enableBanPing: 'firewall.enableBanPing',\n    UserName: 'commons.login.username',\n    PanelName: 'setting.title',\n    Language: 'setting.language',\n    Theme: 'setting.theme',\n    MenuTabs: 'setting.menuTabs',\n    SessionTimeout: 'setting.sessionTimeout',\n    SecurityEntrance: 'setting.entrance',\n    ExpirationDays: 'setting.expirationTime',\n    ComplexityVerification: 'setting.complexity',\n    MFAStatus: 'setting.mfa',\n    MonitorStatus: 'setting.enableMonitor',\n    MonitorStoreDays: 'setting.monitor',\n    ApiInterfaceStatus: 'setting.apiInterface',\n    ComponentSize: 'setting.componentSize',\n    Region: 'setting.region',\n    SystemIP: 'setting.systemIP',\n    ProxyType: 'setting.proxyType',\n    ProxyUrl: 'setting.proxyUrl',\n    ProxyPort: 'setting.proxyPort',\n    ProxyPasswdKeep: 'setting.proxyPasswdKeep',\n    ProxyDocker: 'setting.proxyDocker',\n    SyncToNode: 'setting.syncToNode',\n    IPWhiteList: 'setting.ipWhiteList',\n    ApiKeyValidityTime: 'setting.apiKeyValidityTime',\n    DeveloperMode: 'setting.developerMode',\n};\n\nconst resolveReplacementKey = (token: string): string | undefined => {\n    if (exactReplacements[token]) {\n        return exactReplacements[token];\n    }\n    return normalizedReplacements[token.toLowerCase()];\n};\n\nconst onSubmitClean = async () => {\n    await cleanLogs({ logType: 'operation' });\n    search();\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nonMounted(() => {\n    if (globalStore.isMasterProductPro) {\n        loadNodes();\n    }\n    search();\n});\n</script>\n\n<style scoped lang=\"scss\">\n.tag-button {\n    &.no-active {\n        background: none;\n        border: none;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/log/router/index.vue",
    "content": "<template>\n    <div>\n        <el-button\n            class=\"tag-button\"\n            :class=\"current != 'OperationLog' ? 'no-active' : ''\"\n            :type=\"current === 'OperationLog' ? 'primary' : ''\"\n            @click=\"onChangeRoute('OperationLog')\"\n        >\n            {{ $t('logs.operation') }}\n        </el-button>\n        <el-button\n            class=\"tag-button\"\n            :class=\"current != 'LoginLog' ? 'no-active' : ''\"\n            :type=\"current === 'LoginLog' ? 'primary' : ''\"\n            @click=\"onChangeRoute('LoginLog')\"\n        >\n            {{ $t('logs.login') }}\n        </el-button>\n        <el-button\n            class=\"tag-button\"\n            :class=\"current != 'SystemLog' ? 'no-active' : ''\"\n            :type=\"current === 'SystemLog' ? 'primary' : ''\"\n            @click=\"onChangeRoute('SystemLog')\"\n        >\n            {{ $t('logs.system') }}\n        </el-button>\n        <el-button\n            class=\"tag-button\"\n            :class=\"current != 'Task' ? 'no-active' : ''\"\n            :type=\"current === 'Task' ? 'primary' : ''\"\n            @click=\"onChangeRoute('Task')\"\n        >\n            {{ $t('logs.task') }}\n        </el-button>\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { routerToName } from '@/utils/router';\n\ndefineProps({\n    current: {\n        type: String,\n        default: 'LoginLog',\n    },\n});\nconst onChangeRoute = async (addr: string) => {\n    routerToName(addr);\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/log/system/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('logs.system')\">\n            <template #search>\n                <LogRouter current=\"SystemLog\" />\n            </template>\n            <template #leftToolBar>\n                <el-select class=\"p-w-200\" v-model=\"itemName\" @change=\"search()\">\n                    <template #prefix>{{ $t('commons.table.date') }}</template>\n                    <el-option v-for=\"(item, index) in fileList\" :key=\"index\" :label=\"item\" :value=\"item\" />\n                </el-select>\n                <el-button>\n                    <el-checkbox @change=\"changeTail\" v-model=\"isWatch\">\n                        {{ $t('commons.button.watch') }}\n                    </el-checkbox>\n                </el-button>\n                <el-radio-group\n                    v-if=\"globalStore.currentNode === 'local'\"\n                    class=\"ml-2\"\n                    @change=\"search()\"\n                    v-model=\"itemType\"\n                >\n                    <el-radio-button :label=\"$t('logs.agent')\" value=\"Agent\" />\n                    <el-radio-button :label=\"$t('logs.core')\" value=\"Core\" />\n                </el-radio-group>\n            </template>\n            <template #main>\n                <LogFile\n                    ref=\"logRef\"\n                    :config=\"logConfig\"\n                    :default-button=\"false\"\n                    v-if=\"showLog\"\n                    v-model:loading=\"loading\"\n                    v-model:hasContent=\"hasContent\"\n                    :height-diff=\"330\"\n                />\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport LogFile from '@/components/log/file/index.vue';\nimport LogRouter from '@/views/log/router/index.vue';\nimport { nextTick, onMounted, reactive, ref } from 'vue';\nimport { getSystemFiles } from '@/api/modules/log';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref();\nconst isWatch = ref();\nconst fileList = ref();\nconst logRef = ref();\n\nconst hasContent = ref(false);\nconst logConfig = reactive({\n    type: 'system',\n    name: '',\n    colorMode: 'system',\n});\nconst showLog = ref(false);\nconst itemName = ref();\nconst itemType = ref('Agent');\n\nconst changeTail = () => {\n    logRef.value.changeTail(true);\n};\n\nconst loadFiles = async () => {\n    const res = await getSystemFiles();\n    fileList.value = res.data || [];\n    if (fileList.value) {\n        itemName.value = fileList.value[0];\n        search();\n    }\n};\n\nconst search = () => {\n    logConfig.name = itemType.value === 'Agent' ? itemName.value : 'Core-' + itemName.value;\n    showLog.value = false;\n    nextTick(() => {\n        showLog.value = true;\n    });\n};\n\nonMounted(() => {\n    loadFiles();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/log/task/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('logs.task')\">\n            <template #search>\n                <LogRouter current=\"Task\" />\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"req.status\" @change=\"search()\" clearable class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.status') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"\"></el-option>\n                    <el-option :label=\"$t('commons.status.success')\" value=\"Success\"></el-option>\n                    <el-option :label=\"$t('commons.status.failed')\" value=\"Failed\"></el-option>\n                    <el-option :label=\"$t('logs.taskRunning')\" value=\"Executing\"></el-option>\n                </el-select>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"task-log-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"data\" @search=\"search\" :heightDiff=\"370\">\n                    <el-table-column :label=\"$t('logs.taskName')\" prop=\"name\" min-width=\"180px\"></el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\" max-width=\"100px\">\n                        <template #default=\"{ row }\">\n                            <Status :status=\"row.status\" :msg=\"row.errorMsg\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" prop=\"log\" max-width=\"100px\">\n                        <template #default=\"{ row }\">\n                            <el-button @click=\"openTaskLog(row)\" link type=\"primary\">\n                                {{ $t('website.check') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport LogRouter from '@/views/log/router/index.vue';\nimport { dateFormat } from '@/utils/util';\nimport { searchTasks } from '@/api/modules/log';\nimport { onMounted, reactive, ref } from 'vue';\nimport { Log } from '@/api/interface/log';\nimport TaskLog from '@/components/log/task/index.vue';\nimport bus from '@/global/bus';\n\nconst loading = ref();\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'task-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('task-page-size')) || 20,\n    total: 0,\n});\nconst taskLogRef = ref();\nconst req = reactive({\n    type: '',\n    status: '',\n    page: 1,\n    pageSize: 10,\n});\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    try {\n        const res = await searchTasks(req);\n        loading.value = false;\n        data.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {\n    } finally {\n        bus.emit('refreshTask', true);\n        loading.value = false;\n    }\n};\n\nconst openTaskLog = (row: Log.Task) => {\n    taskLogRef.value.openWithTaskID(row.id, row.status == 'Executing');\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/log/website/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('logs.websiteLog')\">\n            <template #search>\n                <el-button\n                    class=\"tag-button\"\n                    :class=\"logConfig.name === 'access.log' ? '' : 'no-active'\"\n                    :type=\"logConfig.name === 'access.log' ? 'primary' : ''\"\n                    @click=\"changeType('access.log')\"\n                >\n                    {{ $t('logs.runLog') }}\n                </el-button>\n                <el-button\n                    class=\"tag-button\"\n                    :class=\"logConfig.name === 'error.log' ? '' : 'no-active'\"\n                    :type=\"logConfig.name === 'error.log' ? 'primary' : ''\"\n                    @click=\"changeType('error.log')\"\n                >\n                    {{ $t('logs.errLog') }}\n                </el-button>\n            </template>\n            <template #leftToolBar>\n                <el-select v-model=\"logConfig.id\" @change=\"changeWebsite()\" class=\"p-w-200\">\n                    <template #prefix>{{ $t('menu.website') }}</template>\n                    <el-option\n                        v-for=\"(website, index) in websites\"\n                        :key=\"index\"\n                        :label=\"website.primaryDomain\"\n                        :value=\"website.id\"\n                    ></el-option>\n                </el-select>\n                <el-button>\n                    <el-checkbox v-model=\"tailLog\" @change=\"changeTail\" :disabled=\"logConfig.id == undefined\">\n                        {{ $t('commons.button.watch') }}\n                    </el-checkbox>\n                </el-button>\n                <el-button @click=\"onDownload\" icon=\"Download\" :disabled=\"!hasContent\">\n                    {{ $t('commons.button.download') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onClean()\" :disabled=\"!hasContent\">\n                    {{ $t('logs.deleteLogs') }}\n                </el-button>\n            </template>\n            <template #main>\n                <MainDiv :heightDiff=\"370\">\n                    <LogFile\n                        ref=\"logRef\"\n                        :config=\"logConfig\"\n                        :default-button=\"false\"\n                        v-if=\"showLog\"\n                        v-model:loading=\"loading\"\n                        v-model:hasContent=\"hasContent\"\n                    />\n                </MainDiv>\n            </template>\n        </LayoutContent>\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmitClean\"></ConfirmDialog>\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { listWebsites, opWebsiteLog } from '@/api/modules/website';\nimport { reactive } from 'vue';\nimport { onMounted } from 'vue';\nimport { ref, nextTick } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport LogFile from '@/components/log/file/index.vue';\nimport MainDiv from '@/components/main-div/index.vue';\n\nconst logConfig = reactive({\n    type: 'website',\n    id: undefined,\n    name: 'access.log',\n    colorMode: 'nginx',\n});\nconst showLog = ref(false);\nconst loading = ref(false);\nconst websites = ref();\nconst confirmDialogRef = ref();\nconst tailLog = ref(false);\nconst logRef = ref();\nconst hasContent = ref(false);\n\nconst searchLog = () => {\n    showLog.value = false;\n    nextTick(() => {\n        showLog.value = true;\n    });\n};\n\nconst getWebsites = async () => {\n    loading.value = true;\n    await listWebsites()\n        .then((res) => {\n            websites.value = res.data || [];\n            if (websites.value.length > 0) {\n                logConfig.id = websites.value[0].id;\n                showLog.value = true;\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst changeType = (type: string) => {\n    logConfig.name = type;\n    if (logConfig.id != undefined) {\n        searchLog();\n    }\n};\n\nconst changeWebsite = () => {\n    searchLog();\n};\n\nconst onClean = async () => {\n    const params = {\n        header: i18n.global.t('logs.deleteLogs'),\n        operationInfo: i18n.global.t('commons.msg.delete'),\n        submitInputInfo: i18n.global.t('logs.deleteLogs'),\n    };\n    confirmDialogRef.value!.acceptParams(params);\n};\n\nconst onDownload = async () => {\n    logRef.value.onDownload();\n};\n\nconst changeTail = () => {\n    logRef.value.changeTail(true);\n};\n\nconst onSubmitClean = async () => {\n    const req = {\n        id: logConfig.id,\n        operate: 'delete',\n        logType: logConfig.name,\n    };\n    loading.value = true;\n    opWebsiteLog(req)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            searchLog();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    getWebsites();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/login/components/login-form.vue",
    "content": "<template>\n    <div v-loading=\"loading\" class=\"w-full h-full flex items-center justify-center px-8\">\n        <div class=\"w-full flex-grow flex flex-col login-form\">\n            <div v-if=\"mfaShow\">\n                <el-form @submit.prevent>\n                    <div class=\"flex flex-col justify-center items-center mb-6\">\n                        <div class=\"text-2xl font-medium text-gray-900 text-center\">\n                            {{ $t('commons.login.mfaTitle') }}\n                        </div>\n                    </div>\n\n                    <div class=\"space-y-6 flex-grow\">\n                        <el-form-item>\n                            <el-input\n                                ref=\"mfaLoginRef\"\n                                size=\"large\"\n                                :placeholder=\"$t('commons.login.mfaCode')\"\n                                v-model.trim=\"mfaLoginForm.code\"\n                                autocomplete=\"one-time-code\"\n                                @input=\"mfaLogin(true)\"\n                            ></el-input>\n                            <div class=\"h-1\">\n                                <span v-if=\"errMfaInfo\" class=\"input-error\">\n                                    {{ $t('commons.login.errorMfaInfo') }}\n                                </span>\n                            </div>\n                        </el-form-item>\n                        <el-form-item>\n                            <el-button\n                                @focus=\"mfaButtonFocused = true\"\n                                @blur=\"mfaButtonFocused = false\"\n                                class=\"w-full login-button\"\n                                type=\"primary\"\n                                @click=\"mfaLogin(false)\"\n                            >\n                                {{ $t('commons.button.verify') }}\n                            </el-button>\n                        </el-form-item>\n                    </div>\n                </el-form>\n            </div>\n            <div v-else-if=\"showPasskeyOnly\">\n                <div class=\"flex justify-between items-center mb-6\">\n                    <div class=\"text-2xl font-medium text-gray-900\">{{ $t('commons.button.login') }}</div>\n                    <div class=\"cursor-pointer\">\n                        <el-dropdown @command=\"handleCommand\">\n                            <span class=\"flex items-center space-x-1\">\n                                {{ dropdownText }}\n                                <el-icon>\n                                    <arrow-down />\n                                </el-icon>\n                            </span>\n                            <template #dropdown>\n                                <el-dropdown-menu>\n                                    <el-dropdown-item v-if=\"globalStore.isIntl\" command=\"en\">English</el-dropdown-item>\n                                    <el-dropdown-item command=\"zh\">中文(简体)</el-dropdown-item>\n                                    <el-dropdown-item command=\"zh-Hant\">中文(繁體)</el-dropdown-item>\n                                    <el-dropdown-item v-if=\"!globalStore.isIntl\" command=\"en\">English</el-dropdown-item>\n                                    <el-dropdown-item command=\"ja\">日本語</el-dropdown-item>\n                                    <el-dropdown-item command=\"pt-BR\">Português (Brasil)</el-dropdown-item>\n                                    <el-dropdown-item command=\"ko\">한국어</el-dropdown-item>\n                                    <el-dropdown-item command=\"ru\">Русский</el-dropdown-item>\n                                    <el-dropdown-item command=\"ms\">Bahasa Melayu</el-dropdown-item>\n                                    <el-dropdown-item command=\"tr\">Turkish</el-dropdown-item>\n                                </el-dropdown-menu>\n                            </template>\n                        </el-dropdown>\n                    </div>\n                </div>\n                <div class=\"space-y-6\">\n                    <el-form-item>\n                        <el-button class=\"w-full login-button\" type=\"primary\" size=\"default\" @click=\"passkeyLogin\">\n                            <el-icon class=\"mr-2\"><Key /></el-icon>\n                            {{ $t('commons.login.passkey') }}\n                        </el-button>\n                    </el-form-item>\n                    <el-form-item>\n                        <el-link type=\"primary\" :underline=\"false\" @click=\"switchToPasswordLogin\">\n                            {{ $t('commons.login.passkeyToPassword') }}\n                        </el-link>\n                    </el-form-item>\n                    <el-form-item v-if=\"!isIntl && !isFxplay\">\n                        <el-checkbox v-model=\"loginForm.agreeLicense\">\n                            <template #default>\n                                <span class=\"agree-title\">\n                                    {{ $t('commons.button.agree') }}\n                                    <a\n                                        class=\"agree\"\n                                        href=\"https://www.fit2cloud.com/legal/licenses.html\"\n                                        target=\"_blank\"\n                                    >\n                                        {{ $t('commons.login.licenseHelper') }}\n                                    </a>\n                                </span>\n                            </template>\n                        </el-checkbox>\n                    </el-form-item>\n                </div>\n            </div>\n            <div v-else>\n                <div class=\"flex justify-between items-center mb-6\">\n                    <div class=\"text-2xl font-medium text-gray-900\">{{ $t('commons.button.login') }}</div>\n                    <div class=\"cursor-pointer\">\n                        <el-dropdown @command=\"handleCommand\">\n                            <span class=\"flex items-center space-x-1\">\n                                {{ dropdownText }}\n                                <el-icon>\n                                    <arrow-down />\n                                </el-icon>\n                            </span>\n                            <template #dropdown>\n                                <el-dropdown-menu>\n                                    <el-dropdown-item v-if=\"globalStore.isIntl\" command=\"en\">English</el-dropdown-item>\n                                    <el-dropdown-item command=\"zh\">中文(简体)</el-dropdown-item>\n                                    <el-dropdown-item command=\"zh-Hant\">中文(繁體)</el-dropdown-item>\n                                    <el-dropdown-item v-if=\"!globalStore.isIntl\" command=\"en\">English</el-dropdown-item>\n                                    <el-dropdown-item command=\"ja\">日本語</el-dropdown-item>\n                                    <el-dropdown-item command=\"pt-BR\">Português (Brasil)</el-dropdown-item>\n                                    <el-dropdown-item command=\"ko\">한국어</el-dropdown-item>\n                                    <el-dropdown-item command=\"ru\">Русский</el-dropdown-item>\n                                    <el-dropdown-item command=\"ms\">Bahasa Melayu</el-dropdown-item>\n                                    <el-dropdown-item command=\"tr\">Turkish</el-dropdown-item>\n                                </el-dropdown-menu>\n                            </template>\n                        </el-dropdown>\n                    </div>\n                </div>\n                <el-form ref=\"loginFormRef\" :model=\"loginForm\" size=\"default\" :rules=\"loginRules\">\n                    <div class=\"space-y-6 flex-grow\">\n                        <el-form-item prop=\"name\" class=\"w-full\">\n                            <el-input\n                                v-model.trim=\"loginForm.name\"\n                                :placeholder=\"$t('commons.login.username')\"\n                                class=\"w-full\"\n                                size=\"large\"\n                                name=\"username\"\n                                autocomplete=\"username\"\n                                ref=\"userNameRef\"\n                            ></el-input>\n                        </el-form-item>\n                        <el-form-item prop=\"password\" class=\"w-full\">\n                            <el-input\n                                type=\"password\"\n                                show-password\n                                v-model.trim=\"loginForm.password\"\n                                class=\"w-full\"\n                                size=\"large\"\n                                :placeholder=\"$t('commons.login.password')\"\n                                name=\"password\"\n                                autocomplete=\"current-password\"\n                            ></el-input>\n                        </el-form-item>\n                        <el-row :gutter=\"10\">\n                            <el-col :span=\"12\" v-if=\"!globalStore.ignoreCaptcha\">\n                                <el-form-item prop=\"captcha\">\n                                    <el-input\n                                        v-model.trim=\"loginForm.captcha\"\n                                        size=\"large\"\n                                        :placeholder=\"$t('commons.login.captchaHelper')\"\n                                    ></el-input>\n                                </el-form-item>\n                            </el-col>\n                            <el-col :span=\"12\" v-if=\"!globalStore.ignoreCaptcha\">\n                                <img\n                                    class=\"w-full h-10\"\n                                    v-if=\"captcha.imagePath\"\n                                    :src=\"captcha.imagePath\"\n                                    :alt=\"$t('commons.login.captchaHelper')\"\n                                    @click=\"loginVerify()\"\n                                />\n                            </el-col>\n                            <el-col :span=\"24\" class=\"h-0.5\">\n                                <span v-show=\"errCaptcha\" class=\"input-error\">\n                                    {{ $t('commons.login.errorCaptcha') }}\n                                </span>\n                                <span v-show=\"errAuthInfo\" class=\"input-error\">\n                                    {{ $t('commons.login.errorAuthInfo') }}\n                                </span>\n                            </el-col>\n                        </el-row>\n                        <el-form-item>\n                            <el-button\n                                @click=\"login(loginFormRef)\"\n                                @focus=\"loginButtonFocused = true\"\n                                @blur=\"loginButtonFocused = false\"\n                                class=\"w-full login-button\"\n                                type=\"primary\"\n                                size=\"default\"\n                            >\n                                {{ $t('commons.button.login') }}\n                            </el-button>\n                        </el-form-item>\n                        <el-text v-if=\"isDemo\" type=\"danger\" class=\"demo\">\n                            {{ $t('commons.login.username') }}:demo {{ $t('commons.login.password') }}:1panel\n                        </el-text>\n                        <el-form-item prop=\"agreeLicense\" v-if=\"!isIntl && !isFxplay\">\n                            <el-checkbox v-model=\"loginForm.agreeLicense\">\n                                <template #default>\n                                    <span class=\"agree-title\">\n                                        {{ $t('commons.button.agree') }}\n                                        <a\n                                            class=\"agree\"\n                                            href=\"https://www.fit2cloud.com/legal/licenses.html\"\n                                            target=\"_blank\"\n                                        >\n                                            {{ $t('commons.login.licenseHelper') }}\n                                        </a>\n                                    </span>\n                                </template>\n                            </el-checkbox>\n                        </el-form-item>\n                    </div>\n                </el-form>\n            </div>\n\n            <DialogPro v-model=\"open\" center size=\"w-90\">\n                <el-row type=\"flex\" justify=\"center\">\n                    <span class=\"text-base mb-4\">\n                        {{ $t('commons.login.agreeTitle') }}\n                    </span>\n                </el-row>\n                <div>\n                    <span v-html=\"$t('commons.login.agreeContent')\"></span>\n                </div>\n                <template #footer>\n                    <span class=\"dialog-footer login-footer-btn\">\n                        <el-button @click=\"open = false\">\n                            {{ $t('commons.button.notAgree') }}\n                        </el-button>\n                        <el-button type=\"primary\" @click=\"agreeWithLogin()\">\n                            {{ $t('commons.button.agree') }}\n                        </el-button>\n                    </span>\n                </template>\n            </DialogPro>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, reactive, onMounted, computed, nextTick } from 'vue';\nimport type { ElForm } from 'element-plus';\nimport {\n    loginApi,\n    getCaptcha,\n    mfaLoginApi,\n    getLoginSetting,\n    passkeyBeginApi,\n    passkeyFinishApi,\n} from '@/api/modules/auth';\nimport { GlobalStore, MenuStore, TabsStore } from '@/store';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { useI18n } from 'vue-i18n';\nimport { encryptPassword, base64UrlToBuffer, bufferToBase64Url } from '@/utils/util';\nimport { getXpackSettingForTheme } from '@/utils/xpack';\nimport { routerToName } from '@/utils/router';\nimport { changeToLocal, setDefaultNodeInfo } from '@/utils/node';\nimport { Key } from '@element-plus/icons-vue';\n\nconst i18n = useI18n();\nconst themeConfig = computed(() => globalStore.themeConfig);\nconst globalStore = GlobalStore();\nconst menuStore = MenuStore();\nconst tabsStore = TabsStore();\n\nconst errAuthInfo = ref(false);\nconst errCaptcha = ref(false);\nconst errMfaInfo = ref(false);\nconst passkeySetting = ref(false);\nconst passkeySupported = ref(false);\nconst autoPasskeyEnabledKey = '1panel-passkey-auto-enabled';\nconst showPasswordLogin = ref(false);\nconst isDemo = ref(false);\nconst isIntl = ref(true);\nconst isFxplay = ref(false);\nconst open = ref(false);\nconst loginBtnLinkColor = ref<string | null>(null);\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst _isMobile = () => {\n    const rect = document.body.getBoundingClientRect();\n    return rect.width - 1 < 600;\n};\n\nconst loginButtonFocused = ref();\nconst loginFormRef = ref<FormInstance>();\nconst loginForm = reactive({\n    name: '',\n    password: '',\n    captcha: '',\n    captchaID: '',\n    authMethod: 'session',\n    agreeLicense: false,\n    language: 'zh',\n});\n\nconst loginRules = reactive({\n    name: [{ required: true, validator: checkUsername, trigger: 'blur' }],\n    password: [{ required: true, validator: checkPassword, trigger: 'blur' }],\n    agreeLicense: [{ required: true, validator: checkAgreeLicense, trigger: 'blur' }],\n});\n\nfunction checkUsername(rule: any, value: any, callback: any) {\n    if (value === '') {\n        return callback(new Error(i18n.t('commons.rule.username')));\n    }\n    callback();\n}\nfunction checkPassword(rule: any, value: any, callback: any) {\n    if (value === '') {\n        return callback(new Error(i18n.t('commons.rule.password')));\n    }\n    callback();\n}\nfunction checkAgreeLicense(rule: any, value: any, callback: any) {\n    if (!value && !_isMobile()) {\n        return callback(new Error(i18n.t('commons.login.errorAgree')));\n    }\n    callback();\n}\n\nlet isLoggingIn = false;\nconst userNameRef = ref();\nconst mfaLoginRef = ref();\nconst mfaButtonFocused = ref();\nconst pendingLoginMethod = ref<'password' | 'passkey'>('password');\nconst mfaLoginForm = reactive({\n    name: '',\n    password: '',\n    secret: '',\n    code: '',\n    authMethod: 'session',\n});\n\nconst captcha = reactive({\n    captchaID: '',\n    imagePath: '',\n    captchaLength: 0,\n});\n\nconst loading = ref<boolean>(false);\nconst mfaShow = ref<boolean>(false);\nconst dropdownText = ref('中文(简体)');\n\nconst isAutoPasskeyEnabled = () => {\n    try {\n        return localStorage.getItem(autoPasskeyEnabledKey) === '1';\n    } catch (error) {\n        return false;\n    }\n};\nconst enableAutoPasskey = () => {\n    try {\n        localStorage.setItem(autoPasskeyEnabledKey, '1');\n    } catch (error) {}\n};\nconst disableAutoPasskey = () => {\n    try {\n        localStorage.removeItem(autoPasskeyEnabledKey);\n    } catch (error) {}\n};\n\nconst languageLabelMap: Record<string, string> = {\n    zh: '中文(简体)',\n    en: 'English',\n    'pt-BR': 'Português (Brasil)',\n    'zh-Hant': '中文(繁體)',\n    ko: '한국어',\n    ja: '日本語',\n    ru: 'Русский',\n    ms: 'Bahasa Melayu',\n    tr: 'Turkish',\n    'es-ES': 'España - Español',\n};\n\nconst handleCommand = async (command: string) => {\n    const activeLocale = await globalStore.updateLanguage(command);\n    loginForm.language = activeLocale;\n    dropdownText.value = languageLabelMap[activeLocale] || languageLabelMap.zh;\n};\n\nconst agreeWithLogin = () => {\n    open.value = false;\n    loginForm.agreeLicense = true;\n    if (pendingLoginMethod.value === 'passkey') {\n        passkeyLogin();\n        return;\n    }\n    login(loginFormRef.value);\n};\n\nconst showPasskeyOnly = computed(() => {\n    return passkeySetting.value && passkeySupported.value && !showPasswordLogin.value;\n});\n\nconst switchToPasswordLogin = () => {\n    showPasswordLogin.value = true;\n    nextTick(() => {\n        userNameRef.value?.focus();\n    });\n};\n\nconst login = (formEl: FormInstance | undefined) => {\n    if (!formEl || isLoggingIn) return;\n    errAuthInfo.value = false;\n    errCaptcha.value = false;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (isIntl.value || isFxplay.value) {\n            loginForm.agreeLicense = true;\n        }\n        if (!loginForm.agreeLicense) {\n            if (_isMobile()) {\n                pendingLoginMethod.value = 'password';\n                open.value = true;\n            }\n            return;\n        }\n        let requestLoginForm = {\n            name: loginForm.name,\n            password: encryptPassword(loginForm.password),\n            captcha: loginForm.captcha,\n            captchaID: captcha.captchaID,\n            authMethod: 'session',\n            language: loginForm.language,\n        };\n        if (!globalStore.ignoreCaptcha && requestLoginForm.captcha == '') {\n            errCaptcha.value = true;\n            return;\n        }\n        try {\n            isLoggingIn = true;\n            loading.value = true;\n            const res = await loginApi(requestLoginForm);\n            globalStore.ignoreCaptcha = true;\n            if (res.data.mfaStatus === 'Enable') {\n                mfaShow.value = true;\n                errMfaInfo.value = false;\n                nextTick(() => {\n                    mfaLoginRef.value?.focus();\n                });\n                return;\n            }\n            globalStore.setLogStatus(true);\n            globalStore.setAgreeLicense(true);\n            menuStore.setMenuList([]);\n            tabsStore.removeAllTabs();\n            changeToLocal();\n            MsgSuccess(i18n.t('commons.msg.loginSuccess'));\n            setDefaultNodeInfo();\n            localStorage.removeItem('dashboardCache');\n            localStorage.removeItem('upgradeChecked');\n            routerToName('home');\n            document.onkeydown = null;\n        } catch (res) {\n            if (res.code === 401) {\n                if (res.message === 'ErrCaptchaCode') {\n                    globalStore.ignoreCaptcha = false;\n                    loginForm.captcha = '';\n                    errCaptcha.value = true;\n                    errAuthInfo.value = false;\n                    loginVerify();\n                    return;\n                }\n                if (res.message === 'ErrAuth') {\n                    globalStore.ignoreCaptcha = false;\n                    errCaptcha.value = false;\n                    errAuthInfo.value = true;\n                    loginVerify();\n                    return;\n                }\n                MsgError(res.message);\n            }\n            loginVerify();\n        } finally {\n            isLoggingIn = false;\n            loading.value = false;\n        }\n    });\n};\n\nconst mfaLogin = async (auto: boolean) => {\n    if (isLoggingIn) return;\n    if ((!auto && mfaLoginForm.code) || (auto && mfaLoginForm.code.length === 6)) {\n        isLoggingIn = true;\n        mfaLoginForm.name = loginForm.name;\n        mfaLoginForm.password = encryptPassword(loginForm.password);\n        try {\n            await mfaLoginApi(mfaLoginForm);\n            globalStore.setLogStatus(true);\n            menuStore.setMenuList([]);\n            tabsStore.removeAllTabs();\n            MsgSuccess(i18n.t('commons.msg.loginSuccess'));\n            changeToLocal();\n            setDefaultNodeInfo();\n            localStorage.removeItem('dashboardCache');\n            localStorage.removeItem('upgradeChecked');\n            routerToName('home');\n            document.onkeydown = null;\n        } catch (res) {\n            if (res.code === 401) {\n                errMfaInfo.value = true;\n                isLoggingIn = false;\n                return;\n            }\n        } finally {\n            isLoggingIn = false;\n        }\n    }\n};\n\nconst passkeyLogin = async () => {\n    if (isLoggingIn || !passkeySetting.value) return;\n    if (!passkeySupported.value) {\n        disableAutoPasskey();\n        MsgError(i18n.t('commons.login.passkeyNotSupported'));\n        return;\n    }\n    if (!isIntl.value && !isFxplay.value && !loginForm.agreeLicense) {\n        if (_isMobile() || showPasskeyOnly.value) {\n            pendingLoginMethod.value = 'passkey';\n            open.value = true;\n        } else {\n            MsgError(i18n.t('commons.login.errorAgree'));\n        }\n        return;\n    }\n    try {\n        isLoggingIn = true;\n        loading.value = true;\n        const res = await passkeyBeginApi();\n        const publicKey = normalizePasskeyRequest(res.data.publicKey);\n        const credential = (await navigator.credentials.get({ publicKey })) as PublicKeyCredential | null;\n        if (!credential) {\n            disableAutoPasskey();\n            MsgError(i18n.t('commons.login.passkeyFailed'));\n            return;\n        }\n        const payload = buildPasskeyAssertion(credential);\n        await passkeyFinishApi(payload, res.data.sessionId);\n        enableAutoPasskey();\n        globalStore.ignoreCaptcha = true;\n        globalStore.setLogStatus(true);\n        globalStore.setAgreeLicense(true);\n        menuStore.setMenuList([]);\n        tabsStore.removeAllTabs();\n        changeToLocal();\n        MsgSuccess(i18n.t('commons.msg.loginSuccess'));\n        setDefaultNodeInfo();\n        localStorage.removeItem('dashboardCache');\n        localStorage.removeItem('upgradeChecked');\n        routerToName('home');\n        document.onkeydown = null;\n    } catch (res: any) {\n        disableAutoPasskey();\n        if (res?.message) {\n            MsgError(i18n.t('commons.login.passkeyFailed'));\n        }\n    } finally {\n        isLoggingIn = false;\n        loading.value = false;\n    }\n};\n\nconst normalizePasskeyRequest = (publicKey: Record<string, any>): PublicKeyCredentialRequestOptions => {\n    const request = { ...publicKey };\n    request.challenge = base64UrlToBuffer(request.challenge);\n    if (request.allowCredentials && Array.isArray(request.allowCredentials)) {\n        request.allowCredentials = request.allowCredentials.map((item) => {\n            return { ...item, id: base64UrlToBuffer(item.id) };\n        });\n    }\n    return request as PublicKeyCredentialRequestOptions;\n};\n\nconst buildPasskeyAssertion = (credential: PublicKeyCredential) => {\n    const response = credential.response as AuthenticatorAssertionResponse;\n    const payload: Record<string, any> = {\n        id: credential.id,\n        rawId: bufferToBase64Url(credential.rawId),\n        type: credential.type,\n        response: {\n            clientDataJSON: bufferToBase64Url(response.clientDataJSON),\n            authenticatorData: bufferToBase64Url(response.authenticatorData),\n            signature: bufferToBase64Url(response.signature),\n        },\n        clientExtensionResults: credential.getClientExtensionResults(),\n        authenticatorAttachment: credential.authenticatorAttachment,\n    };\n    if (response.userHandle) {\n        payload.response.userHandle = bufferToBase64Url(response.userHandle);\n    }\n    return payload;\n};\n\nconst loginVerify = async () => {\n    const res = await getCaptcha();\n    captcha.imagePath = res.data.imagePath ? res.data.imagePath : '';\n    captcha.captchaID = res.data.captchaID ? res.data.captchaID : '';\n    captcha.captchaLength = res.data.captchaLength ? res.data.captchaLength : 0;\n};\n\nconst getSetting = async () => {\n    try {\n        const res = await getLoginSetting();\n        isDemo.value = res.data.isDemo;\n        const language = res.data.language || loginForm.language;\n        await handleCommand(language);\n        isIntl.value = res.data.isIntl;\n        isFxplay.value = res.data.isFxplay;\n        globalStore.isFxplay = isFxplay.value;\n        globalStore.isOffLine = res.data.isOffLine;\n        globalStore.ignoreCaptcha = !res.data.needCaptcha;\n        passkeySetting.value = res.data.passkeySetting;\n        if (!globalStore.ignoreCaptcha) {\n            loginVerify();\n        }\n\n        document.title = res.data.panelName;\n        i18n.warnHtmlMessage = false;\n        globalStore.setOpenMenuTabs(res.data.menuTabs === 'Enable');\n        globalStore.setThemeConfig({ ...themeConfig.value, theme: res.data.theme, panelName: res.data.panelName });\n\n        if (res.data.passkeySetting && !isIntl.value && !isFxplay.value) {\n            loginForm.agreeLicense = true;\n        }\n        if (passkeySetting.value && passkeySupported.value && isAutoPasskeyEnabled()) {\n            passkeyLogin();\n        }\n    } catch (error) {}\n};\n\nfunction adjustColorToRGBA(color: string, percent: number, opacity: number): string {\n    let r = 0,\n        g = 0,\n        b = 0,\n        a = opacity;\n\n    color = color.trim();\n\n    if (color.startsWith('#')) {\n        if (color.length === 4) {\n            r = parseInt(color[1] + color[1], 16);\n            g = parseInt(color[2] + color[2], 16);\n            b = parseInt(color[3] + color[3], 16);\n        } else if (color.length === 7) {\n            r = parseInt(color.slice(1, 3), 16);\n            g = parseInt(color.slice(3, 5), 16);\n            b = parseInt(color.slice(5, 7), 16);\n        } else {\n            return color;\n        }\n    } else if (color.startsWith('rgb')) {\n        const result = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*([0-9.]+))?\\)/);\n        if (!result) return color;\n        r = parseInt(result[1], 10);\n        g = parseInt(result[2], 10);\n        b = parseInt(result[3], 10);\n        if (result[4] !== undefined) {\n            a = parseFloat(result[4]);\n        }\n    } else {\n        return color;\n    }\n\n    r = Math.min(255, Math.max(0, Math.round(r * (1 + percent / 100))));\n    g = Math.min(255, Math.max(0, Math.round(g * (1 + percent / 100))));\n    b = Math.min(255, Math.max(0, Math.round(b * (1 + percent / 100))));\n    a = Math.min(1, Math.max(0, opacity / 100));\n\n    return `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n\nonMounted(() => {\n    globalStore.isOnRestart = false;\n    passkeySupported.value = !!window.PublicKeyCredential && window.isSecureContext;\n    getSetting();\n    getXpackSettingForTheme();\n    if (!globalStore.ignoreCaptcha) {\n        loginVerify();\n    }\n    document.title = globalStore.themeConfig.panelName;\n    loginBtnLinkColor.value = globalStore.themeConfig.loginBtnLinkColor || '#005eeb';\n    document.documentElement.style.setProperty('--login-btn-link-color', loginBtnLinkColor.value);\n    document.documentElement.style.setProperty(\n        '--login-btn-link-hover-color',\n        adjustColorToRGBA(loginBtnLinkColor.value, -10, 80),\n    );\n    document.documentElement.style.setProperty(\n        '--login-loading-mask-color',\n        adjustColorToRGBA(loginBtnLinkColor.value, 30, 15),\n    );\n    nextTick(() => {\n        userNameRef.value?.focus();\n    });\n    loginForm.agreeLicense = globalStore.agreeLicense;\n    document.onkeydown = (e: any) => {\n        e = window.event || e;\n        if (e.keyCode === 13) {\n            if (!mfaShow.value) {\n                if (!loginButtonFocused.value) {\n                    login(loginFormRef.value);\n                }\n            }\n            if (mfaShow.value && !mfaButtonFocused.value) {\n                mfaLogin(false);\n            }\n        }\n    };\n});\n</script>\n<style scoped lang=\"scss\">\n.agree {\n    text-decoration: none;\n}\n\n.agree:hover {\n    text-decoration: underline;\n}\n\n:deep(.el-button) {\n    height: 2.5rem;\n}\n\n:deep(.el-input__inner) {\n    -webkit-box-shadow: 0 0 0px 1000px transparent inset !important;\n    transition: background-color 50000s ease-in-out 0s;\n}\n\n:deep(.el-row) {\n    padding: 0 !important;\n}\n\n.login-form {\n    .login-button {\n        background-color: var(--login-btn-link-color);\n        border-color: var(--login-btn-link-color);\n        color: #ffffff;\n\n        &:hover {\n            background-color: var(--login-btn-link-hover-color) !important;\n            border-color: var(--login-btn-link-hover-color) !important;\n            outline: none !important;\n        }\n    }\n\n    :deep(.el-input) {\n        --el-input-border-color: #dcdfe6;\n        background: none !important;\n    }\n\n    :deep(.el-input__wrapper) {\n        background: none !important;\n    }\n\n    :deep(.el-input__wrapper.is-focus) {\n        box-shadow: 0 0 0 1px var(--login-btn-link-color) inset !important;\n    }\n\n    .demo {\n        text-align: center;\n\n        span {\n            color: red;\n        }\n    }\n\n    .agree-title {\n        color: var(--login-btn-link-color);\n    }\n\n    .agree {\n        white-space: pre-wrap;\n        color: var(--login-btn-link-color);\n    }\n\n    :deep(a) {\n        color: var(--login-btn-link-color);\n\n        &:hover {\n            opacity: 75%;\n        }\n    }\n\n    :deep(.el-checkbox__input .el-checkbox__inner) {\n        background-color: #fff !important;\n        border-color: var(--login-btn-link-color) !important;\n    }\n\n    :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {\n        background-color: var(--login-btn-link-color) !important;\n        border-color: var(--login-btn-link-color) !important;\n    }\n\n    :deep(.el-checkbox__input.is-checked .el-checkbox__inner::after) {\n        border-color: #ffffff !important;\n    }\n\n    :deep(.el-input__inner) {\n        color: #000 !important;\n    }\n}\n\n.cursor-pointer {\n    outline: none;\n}\n\n.el-dropdown:focus-visible {\n    outline: none;\n}\n\n.el-tooltip__trigger:focus-visible {\n    outline: none;\n}\n\n:deep(.el-dropdown-menu__item:not(.is-disabled):hover) {\n    background-color: var(--login-btn-link-color) !important;\n    color: #fff !important;\n}\n\n:deep(.el-dropdown-menu__item:not(.is-disabled):focus) {\n    background-color: var(--login-btn-link-color) !important;\n    color: #fff !important;\n}\n\n:deep(.el-loading-mask) {\n    background-color: var(--login-loading-mask-color) !important;\n\n    .el-loading-spinner .path {\n        stroke: var(--login-btn-link-color);\n    }\n}\n\n.login-footer-btn {\n    .el-button--primary {\n        border-color: var(--login-btn-link-color) !important;\n        background-color: var(--login-btn-link-color) !important;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/login/index.vue",
    "content": "<template>\n    <div class=\"flex items-center justify-center min-h-screen relative bg-gray-100\">\n        <div class=\"absolute inset-0 bg-cover bg-center bg-no-repeat\" :style=\"backgroundStyle\"></div>\n        <div\n            :style=\"{ opacity: backgroundOpacity, width: containerWidth, height: containerHeight }\"\n            class=\"bg-white shadow-lg relative z-10 border border-gray-200 flex overflow-hidden\"\n            id=\"login-container\"\n        >\n            <div class=\"grid grid-cols-1 md:grid-cols-2 items-stretch w-full\">\n                <div v-if=\"showLogo\" class=\"flex justify-center\" :style=\"{ height: containerHeight }\">\n                    <img\n                        v-show=\"imgLoaded\"\n                        :src=\"loadImage('loginImage')\"\n                        class=\"max-w-full max-h-full object-cover bg-cover bg-center\"\n                        alt=\"1panel\"\n                        @load=\"onImgLoad\"\n                        @error=\"onImgError\"\n                    />\n                </div>\n                <div :class=\"loginFormClass\">\n                    <LoginForm ref=\"loginRef\"></LoginForm>\n                </div>\n            </div>\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\" name=\"login\">\nimport LoginForm from './components/login-form.vue';\nimport { ref, onMounted } from 'vue';\nimport { GlobalStore } from '@/store';\nimport { preloadImage } from '@/utils/util';\n\nconst globalStore = GlobalStore();\nconst backgroundOpacity = ref(1);\nconst defaultLoginImage = new URL('@/assets/images/1panel-login.jpg', import.meta.url).href;\nconst defaultLoginBgImage = new URL('@/assets/images/1panel-login-bg.jpg', import.meta.url).href;\nconst loadedLoginImage = ref<string | null>(null);\nconst loadedBackgroundImage = ref<string | null>(null);\nconst backgroundStyle = ref<{ backgroundImage?: string; backgroundColor?: string }>({});\nconst imgLoaded = ref(false);\n\nfunction onImgLoad() {\n    imgLoaded.value = true;\n}\nconst mySafetyCode = defineProps({\n    code: {\n        type: String,\n        default: '',\n    },\n});\n\nconst getStatus = async () => {\n    let code = mySafetyCode.code;\n    if (code != '') {\n        globalStore.entrance = code;\n    }\n};\n\nconst loadImage = (name: string) => {\n    const { loginImage, loginBackground, loginBgType } = globalStore.themeConfig;\n    if (name === 'loginImage') {\n        return loginImage === 'loginImage' ? loadedLoginImage.value : defaultLoginImage;\n    }\n    if (name === 'loginBackground') {\n        if (loginBgType === 'image') {\n            return loginBackground === 'loginBackground' ? loadedBackgroundImage.value : defaultLoginBgImage;\n        }\n        if (loginBgType === 'color') {\n            return loginBackground;\n        }\n        return defaultLoginBgImage;\n    }\n    return '';\n};\n\nconst onImgError = (event: any) => {\n    event.target.src = defaultLoginImage;\n    imgLoaded.value = true;\n};\n\nonMounted(async () => {\n    await getStatus();\n    const loginImageUrl = `/api/v2/images/loginImage?t=${Date.now()}`;\n    const backgroundImageUrl = `/api/v2/images/loginBackground?t=${Date.now()}`;\n    loadedLoginImage.value = await preloadImage(loginImageUrl);\n    loadedBackgroundImage.value = await preloadImage(backgroundImageUrl);\n    if (globalStore.themeConfig.loginBgType === 'color') {\n        backgroundStyle.value = {\n            backgroundColor: globalStore.themeConfig.loginBackground,\n        };\n    } else {\n        const img = new Image();\n        const url = loadImage('loginBackground');\n        img.onload = () => {\n            backgroundStyle.value = {\n                backgroundImage: `url(${url})`,\n            };\n        };\n        img.onerror = () => {\n            backgroundStyle.value = {\n                backgroundImage: `url(${defaultLoginBgImage})`, // 你定义的默认图\n            };\n        };\n        img.src = url;\n    }\n});\n\nconst FIXED_WIDTH = 1000;\nconst FIXED_HEIGHT = 415;\nconst useWindowSize = () => {\n    const width = ref(window.innerWidth);\n    const height = ref(window.innerHeight);\n\n    const updateSize = () => {\n        width.value = window.innerWidth;\n        height.value = window.innerHeight;\n    };\n\n    onMounted(() => window.addEventListener('resize', updateSize));\n    onUnmounted(() => window.removeEventListener('resize', updateSize));\n\n    return { width, height };\n};\nconst { width } = useWindowSize();\nconst showLogo = computed(() => width.value >= FIXED_WIDTH);\nconst containerWidth = computed(() => `${FIXED_WIDTH}px`);\nconst containerHeight = computed(() => `${FIXED_HEIGHT}px`);\nconst loginFormClass = computed(() => {\n    return showLogo.value\n        ? 'hidden md:flex items-center justify-center p-4'\n        : 'flex items-center justify-center p-4 w-full';\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/about/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('setting.about')\" :divider=\"true\">\n            <template #main>\n                <div style=\"text-align: center; margin-top: 20px\">\n                    <div style=\"justify-self: center\" class=\"logo\">\n                        <img\n                            v-if=\"globalStore.themeConfig.logo && !logoLoadFailed\"\n                            style=\"width: 80px\"\n                            :src=\"`/api/v2/images/logo?t=${Date.now()}`\"\n                            @error=\"logoLoadFailed = true\"\n                            alt=\"\"\n                        />\n                        <PrimaryLogo v-else />\n                    </div>\n                    <h3 class=\"description\">{{ globalStore.themeConfig.title || $t('setting.description') }}</h3>\n                    <div class=\"flex justify-center\">\n                        <SystemUpgrade class=\"upgrade\" />\n                    </div>\n                    <div class=\"flex w-full justify-center my-5 flex-wrap md:flex-row gap-4\">\n                        <el-link @click=\"toDoc\" class=\"system-link\">\n                            <el-icon><Document /></el-icon>\n                            <span>{{ $t('setting.doc2') }}</span>\n                        </el-link>\n                        <el-link @click=\"toGithub\" class=\"system-link\">\n                            <svg-icon iconName=\"p-huaban88\"></svg-icon>\n                            <span>{{ $t('setting.project') }}</span>\n                        </el-link>\n                        <el-link @click=\"toIssue\" class=\"system-link\">\n                            <svg-icon iconName=\"p-bug\"></svg-icon>\n                            <span>{{ $t('setting.issue') }}</span>\n                        </el-link>\n                        <el-link @click=\"toGithubStar\" class=\"system-link\">\n                            <svg-icon iconName=\"p-star\"></svg-icon>\n                            <span>{{ $t('setting.star') }}</span>\n                        </el-link>\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getSystemAvailable } from '@/api/modules/setting';\nimport { onMounted, ref } from 'vue';\nimport SystemUpgrade from '@/components/system-upgrade/index.vue';\nimport { GlobalStore } from '@/store';\nimport PrimaryLogo from '@/assets/images/1panel-logo.svg?component';\nimport { storeToRefs } from 'pinia';\nconst globalStore = GlobalStore();\nconst { docsUrl } = storeToRefs(globalStore);\nconst loading = ref();\nconst logoLoadFailed = ref(false);\n\nconst toDoc = () => {\n    window.open(docsUrl.value, '_blank', 'noopener,noreferrer');\n};\nconst toGithub = () => {\n    window.open('https://github.com/1Panel-dev/1Panel', '_blank', 'noopener,noreferrer');\n};\nconst toIssue = () => {\n    window.open('https://github.com/1Panel-dev/1Panel/issues', '_blank', 'noopener,noreferrer');\n};\nconst toGithubStar = () => {\n    window.open('https://github.com/1Panel-dev/1Panel', '_blank', 'noopener,noreferrer');\n};\n\nonMounted(() => {\n    getSystemAvailable();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.system-link {\n    margin-left: 15px;\n\n    .svg-icon {\n        font-size: 7px;\n    }\n    span {\n        line-height: 20px;\n        font-weight: 400;\n    }\n}\n.description {\n    color: var(--el-text-color-regular);\n}\n.logo {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    height: 55px;\n    img {\n        object-fit: contain;\n        width: 95%;\n        height: 45px;\n    }\n}\n.upgrade {\n    all: initial;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/dash/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent :title=\"$t('xpack.alert.list')\" v-loading=\"loading\">\n            <template #prompt>\n                <el-alert type=\"info\" :closable=\"false\" class=\"!mt-2\">\n                    <template #title>\n                        {{ $t('xpack.alert.agentOfflineAlertHelper') }}\n                    </template>\n                </el-alert>\n            </template>\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openView('create')\">{{ $t('xpack.alert.addTask') }}</el-button>\n            </template>\n            <template #rightToolBar>\n                <div class=\"dropdowns\">\n                    <el-select filterable clearable v-model=\"req.type\" @change=\"search()\" class=\"!w-52 dropdown\">\n                        <template #prefix>{{ $t('commons.table.type') }}</template>\n                        <template v-if=\"isMaster\">\n                            <el-option value=\"panelPwdEndTime\" :label=\"$t('xpack.alert.panelPwdEndTime')\" />\n                            <el-option value=\"panelLogin\" :label=\"$t('xpack.alert.panelLogin')\" />\n                            <el-option\n                                v-if=\"isProductPro\"\n                                value=\"licenseException\"\n                                :label=\"$t('xpack.alert.licenseException')\"\n                            />\n                            <el-option\n                                v-if=\"isProductPro\"\n                                value=\"nodeException\"\n                                :label=\"$t('xpack.alert.nodeException')\"\n                            />\n                            <el-option value=\"panelUpdate\" :label=\"$t('xpack.alert.panelUpdate')\" />\n                        </template>\n                        <el-option value=\"sshLogin\" :label=\"$t('xpack.alert.sshLogin')\" />\n                        <el-option value=\"ssl\" :label=\"$t('xpack.alert.ssl')\" />\n                        <el-option value=\"siteEndTime\" :label=\"$t('xpack.alert.siteEndTime')\" />\n                        <el-option value=\"cpu\" :label=\"$t('xpack.alert.cpu')\" />\n                        <el-option value=\"memory\" :label=\"$t('xpack.alert.memory')\" />\n                        <el-option value=\"disk\" :label=\"$t('xpack.alert.disk')\" />\n                        <el-option value=\"load\" :label=\"$t('xpack.alert.load')\" />\n                        <el-option value=\"clams\" :label=\"$t('xpack.alert.clams')\" />\n                        <el-option value=\"shell\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.shell')])\" />\n                        <el-option value=\"app\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.app')])\" />\n                        <el-option value=\"website\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.website')])\" />\n                        <el-option value=\"database\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.database')])\" />\n                        <el-option value=\"directory\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.directory')])\" />\n                        <el-option value=\"log\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.log')])\" />\n                        <el-option value=\"snapshot\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.snapshot')])\" />\n                        <el-option value=\"curl\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.curl')])\" />\n                        <el-option\n                            value=\"cutWebsiteLog\"\n                            :label=\"$t('xpack.alert.cronjob', [$t('cronjob.cutWebsiteLog')])\"\n                        />\n                        <el-option value=\"clean\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.clean')])\" />\n                        <el-option value=\"ntp\" :label=\"$t('xpack.alert.cronjob', [$t('cronjob.ntp')])\" />\n                    </el-select>\n                    <el-select\n                        clearable\n                        filterable\n                        v-model=\"req.status\"\n                        @change=\"search()\"\n                        @clear=\"search\"\n                        class=\"!w-52 dropdown\"\n                    >\n                        <template #prefix>{{ $t('commons.table.status') }}</template>\n                        <el-option :label=\"$t('commons.button.enable')\" value=\"Enable\"></el-option>\n                        <el-option :label=\"$t('commons.button.disable')\" value=\"Disable\"></el-option>\n                    </el-select>\n                </div>\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    :data=\"data\"\n                    :height-diff=\"380\"\n                    @sort-change=\"changeSort\"\n                    @search=\"search()\"\n                >\n                    <el-table-column\n                        :label=\"$t('commons.table.title')\"\n                        prop=\"title\"\n                        min-width=\"300px\"\n                        show-overflow-tooltip\n                    ></el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\" width=\"110px\">\n                        <template #default=\"{ row }\">\n                            <el-button\n                                v-if=\"row.status === 'Enable'\"\n                                @click=\"updateAlertStatus('disable', row.id)\"\n                                link\n                                icon=\"VideoPlay\"\n                                type=\"success\"\n                            >\n                                {{ $t('commons.status.enabled') }}\n                            </el-button>\n                            <el-button\n                                v-else\n                                icon=\"VideoPause\"\n                                link\n                                type=\"danger\"\n                                @click=\"updateAlertStatus('enable', row.id)\"\n                            >\n                                {{ $t('commons.status.disabled') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('xpack.alert.alertMethod')\"\n                        prop=\"method\"\n                        width=\"200px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.method\">{{ formatMethod(row) }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('xpack.alert.alertRule')\"\n                        prop=\"rule\"\n                        min-width=\"300px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            {{ formatRule(row) }}\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations\n                        :ellipsis=\"2\"\n                        width=\"130px\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        :fixed=\"mobile ? false : 'right'\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <AddTask @search=\"search\" ref=\"addTaskRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport { GlobalStore } from '@/store';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { ElMessageBox } from 'element-plus';\nimport AddTask from '@/views/setting/alert/dash/task/index.vue';\nimport { Alert } from '@/api/interface/alert';\nimport { UpdateAlertStatus, SearchAlerts, DeleteAlert } from '@/api/modules/alert';\nimport { storeToRefs } from 'pinia';\n\nconst globalStore = GlobalStore();\nconst { isMaster, isProductPro } = storeToRefs(globalStore);\n\nconst { t } = i18n.global;\nconst loading = ref(false);\nconst addTaskRef = ref();\n\nconst req = reactive({\n    page: 1,\n    pageSize: 10,\n    total: 0,\n    type: '',\n    status: '',\n    method: '',\n});\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'alert-list-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('alert-list-page-size')) || 20,\n    total: 0,\n    orderBy: 'created_at',\n    order: 'null',\n});\nconst data = ref();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Alert.AlertInfo) {\n            openView('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Alert.AlertInfo) {\n            onDelete(row);\n        },\n    },\n];\n\nconst openView = async (\n    title: string,\n    rowData: Partial<Alert.AlertInfo> = {\n        type: isMaster.value ? 'panelPwdEndTime' : 'sshLogin',\n        cycle: 15,\n        count: 0,\n        sendCount: 3,\n        method: '',\n        project: '',\n        status: 'Enable',\n        title: '',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    addTaskRef.value.acceptParams(params);\n};\n\nconst changeSort = ({ prop, order }) => {\n    if (order) {\n        paginationConfig.orderBy = prop == 'status' ? 'status' : prop;\n        paginationConfig.order = order;\n    }\n    search();\n};\n\nconst formatRule = (row: Alert.AlertInfo) => {\n    const ruleTemplates = {\n        ssl: () => t('xpack.alert.timeRule', [row.cycle, row.sendCount]),\n        siteEndTime: () => t('xpack.alert.timeRule', [row.cycle, row.sendCount]),\n        panelPwdEndTime: () => t('xpack.alert.timeRule', [row.cycle, row.sendCount]),\n        panelUpdate: () => t('xpack.alert.panelUpdateRule', [row.cycle, row.sendCount]),\n        cpu: () => t('xpack.alert.avgRule', [row.cycle, t(`xpack.alert.${row.type}Name`), row.count, row.sendCount]),\n        memory: () => t('xpack.alert.avgRule', [row.cycle, t(`xpack.alert.${row.type}Name`), row.count, row.sendCount]),\n        load: () => t('xpack.alert.avgRule', [row.cycle, t(`xpack.alert.${row.type}Name`), row.count, row.sendCount]),\n        disk: () => {\n            return row.project === 'all'\n                ? t('xpack.alert.allDiskRule', [row.count, row.cycle === 1 ? 'G' : '%', row.sendCount])\n                : t('xpack.alert.diskRule', [row.project, row.count, row.cycle === 1 ? 'G' : '%', row.sendCount]);\n        },\n        clams: () => t('xpack.alert.clamsRule', [row.sendCount]),\n        app: () => t('xpack.alert.cronJobAppRule', [row.sendCount]),\n        website: () => t('xpack.alert.cronJobWebsiteRule', [row.sendCount]),\n        database: () => t('xpack.alert.cronJobDatabaseRule', [row.sendCount]),\n        directory: () => t('xpack.alert.cronJobDirectoryRule', [row.sendCount]),\n        log: () => t('xpack.alert.cronJobLogRule', [row.sendCount]),\n        snapshot: () => t('xpack.alert.cronJobSnapshotRule', [row.sendCount]),\n        shell: () => t('xpack.alert.cronJobShellRule', [row.sendCount]),\n        curl: () => t('xpack.alert.cronJobCurlRule', [row.sendCount]),\n        cutWebsiteLog: () => t('xpack.alert.cronJobCutWebsiteLogRule', [row.sendCount]),\n        clean: () => t('xpack.alert.cronJobCleanRule', [row.sendCount]),\n        ntp: () => t('xpack.alert.cronJobNtpRule', [row.sendCount]),\n        nodeException: () => t('xpack.alert.nodeExceptionRule', [row.sendCount]),\n        licenseException: () => t('xpack.alert.licenseExceptionRule', [row.sendCount]),\n        panelLogin: () => t('xpack.alert.panelLoginRule', [row.sendCount]),\n        sshLogin: () => t('xpack.alert.sshLoginRule', [row.sendCount]),\n    };\n\n    return ruleTemplates[row.type] ? ruleTemplates[row.type]() : '';\n};\n\nconst formatMethod = (row: Alert.AlertInfo) => {\n    if (!row.method) return '';\n\n    const sendMethod = row.method.split(',').filter(Boolean);\n    const methodStr = sendMethod.map((item) => t('xpack.alert.' + item)).join('｜');\n\n    return `「${methodStr}」`;\n};\n\nconst search = async () => {\n    if (req.status) {\n        req.status = req.status.toLowerCase() === 'enable' ? 'Enable' : 'Disable';\n    }\n    loading.value = true;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        type: req.type,\n        status: req.status,\n        method: req.method,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    try {\n        const res = await SearchAlerts(params);\n        data.value = res.data.items || [];\n        paginationConfig.total = res.data.total || 0;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst onDelete = (row: Alert.AlertInfo) => {\n    ElMessageBox.confirm(i18n.global.t('xpack.alert.deleteMsg'), i18n.global.t('xpack.alert.deleteTitle'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await DeleteAlert({ id: row.id });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        await search();\n    });\n};\n\nconst updateAlertStatus = (status: string, id: number) => {\n    ElMessageBox.confirm(i18n.global.t('xpack.alert.' + status + 'Msg'), i18n.global.t('xpack.alert.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        let itemStatus = status.toLowerCase() === 'enable' ? 'Enable' : 'Disable';\n        await UpdateAlertStatus({ id: id, status: itemStatus });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        await search();\n    });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.dropdowns {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    flex: 1 1 auto;\n    justify-content: start;\n}\n\n.search-fields {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px;\n    flex: 1 1 auto;\n    margin-top: 10px;\n}\n\n.el-tag {\n    cursor: default;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/dash/task/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"visible\"\n        :header=\"dialogData.title === 'create' ? $t('xpack.alert.addTask') : $t('xpack.alert.editTask')\"\n        :resource=\"dialogData.title === 'create' ? dialogData.rowData?.title : ''\"\n        @close=\"handleClose\"\n        size=\"large\"\n    >\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\">\n            <el-row type=\"flex\" justify=\"center\">\n                <el-col :span=\"22\">\n                    <el-form-item :label=\"$t('xpack.alert.taskType')\" prop=\"type\">\n                        <el-select\n                            class=\"selectClass\"\n                            @change=\"changeType\"\n                            v-model=\"dialogData.rowData!.type\"\n                            :disabled=\"dialogData.title === 'edit'\"\n                        >\n                            <template v-for=\"item in allTaskOptions\">\n                                <el-option\n                                    :key=\"item.value\"\n                                    v-if=\"item.show\"\n                                    :value=\"item.value\"\n                                    :label=\"$t(item.label)\"\n                                />\n                            </template>\n                        </el-select>\n                        <span\n                            class=\"input-help\"\n                            v-if=\"dialogData.rowData!.type === 'panelPwdEndTime' && expirationDays === 0\"\n                        >\n                            {{ $t('xpack.alert.panelPwdEndTimeRulesHelper') }}\n                            <el-link\n                                style=\"font-size: 12px; margin-left: 5px\"\n                                icon=\"Position\"\n                                @click=\"quickJump('Safe')\"\n                                type=\"primary\"\n                            >\n                                {{ $t('firewall.quickJump') }}\n                            </el-link>\n                        </span>\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"dialogData.rowData!.type === 'cronJob'\"\n                        :label=\"$t('xpack.alert.cronJobType')\"\n                        prop=\"subType\"\n                    >\n                        <el-select\n                            class=\"selectClass\"\n                            @change=\"changeType\"\n                            v-model=\"dialogData.rowData!.subType\"\n                            :disabled=\"dialogData.title === 'edit'\"\n                        >\n                            <el-option value=\"shell\" :label=\"$t('cronjob.shell')\" />\n                            <el-option value=\"app\" :label=\"$t('cronjob.app')\" />\n                            <el-option value=\"website\" :label=\"$t('cronjob.website')\" />\n                            <el-option value=\"database\" :label=\"$t('cronjob.database')\" />\n                            <el-option value=\"directory\" :label=\"$t('cronjob.directory')\" />\n                            <el-option value=\"log\" :label=\"$t('cronjob.log')\" />\n                            <el-option value=\"curl\" :label=\"$t('cronjob.curl')\" />\n                            <el-option value=\"cutWebsiteLog\" :label=\"$t('cronjob.cutWebsiteLog')\" />\n                            <el-option value=\"clean\" :label=\"$t('setting.diskClean')\" />\n                            <el-option value=\"snapshot\" :label=\"$t('cronjob.snapshot')\" />\n                            <el-option value=\"ntp\" :label=\"$t('cronjob.ntp')\" />\n                        </el-select>\n                    </el-form-item>\n\n                    <!--                        网站/证书或磁盘-->\n                    <el-form-item\n                        v-if=\"dialogData.rowData!.type === 'ssl'\"\n                        :label=\"$t('xpack.alert.certificate')\"\n                        prop=\"project\"\n                    >\n                        <el-select class=\"selectClass\" v-model=\"dialogData.rowData!.project\">\n                            <el-option\n                                :disabled=\"sslOptions.length === 0\"\n                                :label=\"$t('commons.table.all')\"\n                                value=\"all\"\n                            />\n                            <el-option\n                                v-for=\"(item, index) in sslOptions\"\n                                :key=\"index\"\n                                :value=\"item.id + ''\"\n                                :label=\"item.primaryDomain\"\n                            >\n                                <span>{{ item.primaryDomain }}</span>\n                                <el-tag class=\"tagClass\" v-if=\"item.autoRenew\">\n                                    {{ $t('xpack.alert.autoRenew') }}\n                                </el-tag>\n                            </el-option>\n                        </el-select>\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.autoRenewRulesHelper') }}\n                        </span>\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"dialogData.rowData!.type === 'siteEndTime'\"\n                        :label=\"$t('menu.website')\"\n                        prop=\"project\"\n                    >\n                        <el-select class=\"selectClass\" v-model=\"dialogData.rowData!.project\">\n                            <el-option\n                                :disabled=\"websiteOptions && websiteOptions.length === 0\"\n                                :label=\"$t('commons.table.all')\"\n                                value=\"all\"\n                            />\n                            <template v-for=\"(item, index) in websiteOptions\" :key=\"item\">\n                                <el-option\n                                    v-if=\"!isEver(item.expireDate)\"\n                                    :key=\"index\"\n                                    :value=\"item.id + ''\"\n                                    :label=\"item.primaryDomain\"\n                                >\n                                    <span>{{ item.primaryDomain }}</span>\n                                    <el-tag class=\"tagClass\">\n                                        {{ item.alias }}\n                                    </el-tag>\n                                </el-option>\n                            </template>\n                        </el-select>\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.siteEndTimeRulesHelper') }}\n                        </span>\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"diskTypes.includes(dialogData.rowData!.type)\"\n                        :label=\"$t('xpack.alert.diskInfo')\"\n                        prop=\"project\"\n                    >\n                        <el-select class=\"selectClass\" v-model=\"dialogData.rowData!.project\">\n                            <el-option\n                                :disabled=\"diskOptions.length === 0\"\n                                :label=\"$t('commons.table.all')\"\n                                value=\"all\"\n                            />\n                            <el-option\n                                v-for=\"(item, index) in diskOptions\"\n                                :key=\"index\"\n                                :value=\"item.path\"\n                                :label=\"item.path\"\n                            >\n                                <span>{{ item.path }}</span>\n                            </el-option>\n                        </el-select>\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"dialogData.rowData!.type === 'clams'\"\n                        :label=\"$t('xpack.alert.taskName')\"\n                        prop=\"project\"\n                    >\n                        <el-select class=\"selectClass\" default-first-option v-model=\"dialogData.rowData!.project\">\n                            <el-option\n                                v-for=\"(item, index) in clamsOptions\"\n                                :key=\"index\"\n                                :value=\"String(item.id)\"\n                                :label=\"item.name\"\n                            >\n                                <span>{{ item.name }}</span>\n                                <el-tag class=\"tagClass\">\n                                    {{ item.path }}\n                                </el-tag>\n                            </el-option>\n                        </el-select>\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"dialogData.rowData!.type === 'cronJob' && cronjobTypes.includes(dialogData.rowData!.subType)\"\n                        :label=\"$t('xpack.alert.taskName')\"\n                        prop=\"project\"\n                    >\n                        <el-select class=\"selectClass\" default-first-option v-model=\"dialogData.rowData!.project\">\n                            <el-option\n                                v-for=\"(item, index) in cronJobOptions\"\n                                :key=\"index\"\n                                :value=\"String(item.id)\"\n                                :label=\"item.name\"\n                            >\n                                <span>{{ item.name }}</span>\n                            </el-option>\n                        </el-select>\n                    </el-form-item>\n                    <span class=\"input-help\" v-if=\"dialogData.rowData!.type === 'clams' && clamsOptions.length === 0\">\n                        {{ $t('xpack.alert.clamsRulesHelper') }}\n                        <el-link\n                            style=\"font-size: 12px; margin-left: 5px\"\n                            icon=\"Position\"\n                            @click=\"quickJump('Clam')\"\n                            type=\"primary\"\n                        >\n                            {{ $t('firewall.quickJump') }}\n                        </el-link>\n                    </span>\n                    <span\n                        class=\"input-help\"\n                        v-if=\"cronjobTypes.includes(dialogData.rowData!.type) && cronJobOptions.length === 0\"\n                    >\n                        {{ $t('xpack.alert.cronJobRulesHelper') }}\n                        <el-link\n                            style=\"font-size: 12px; margin-left: 5px\"\n                            icon=\"Position\"\n                            @click=\"quickJump('Cronjob')\"\n                            type=\"primary\"\n                        >\n                            {{ $t('firewall.quickJump') }}\n                        </el-link>\n                    </span>\n                    <!--                        网站或磁盘-->\n\n                    <el-form-item\n                        v-if=\"timeTypes.includes(dialogData.rowData!.type)\"\n                        :label=\"$t('xpack.alert.remainingDays')\"\n                        prop=\"cycle\"\n                    >\n                        <el-input v-model.number=\"dialogData.rowData!.cycle\" />\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"diskTypes.includes(dialogData.rowData!.type)\"\n                        :label=\"$t('xpack.alert.monitoringType')\"\n                        prop=\"cycle\"\n                    >\n                        <el-radio-group @change=\"changeCycle\" v-model=\"dialogData.rowData!.cycle\">\n                            <el-radio-button :label=\"$t('xpack.alert.useDisk')\" :value=\"1\" />\n                            <el-radio-button :label=\"$t('xpack.alert.usePercentage')\" :value=\"2\" />\n                        </el-radio-group>\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"avgTypes.includes(dialogData.rowData!.type)\"\n                        :label=\"$t('xpack.alert.specifiedTime')\"\n                        prop=\"cycle\"\n                    >\n                        <el-select disabled class=\"selectClass\" v-model.number=\"dialogData.rowData!.cycle\">\n                            <el-option :value=\"1\" :label=\"1 + $t('commons.units.minute')\" />\n                            <el-option :value=\"5\" :label=\"5 + $t('commons.units.minute')\" />\n                            <el-option :value=\"15\" :label=\"15 + $t('commons.units.minute')\" />\n                        </el-select>\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"diskTypes.includes(dialogData.rowData!.type)\"\n                        :label=\"$t('xpack.alert.useExceed')\"\n                        prop=\"count\"\n                    >\n                        <el-input v-model.number=\"dialogData.rowData!.count\">\n                            <template #append>\n                                {{ dialogData.rowData!.cycle === 1 ? 'GB' : ' % ' }}\n                            </template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('xpack.alert.useExceedRulesHelper') }}</span>\n                    </el-form-item>\n\n                    <el-form-item\n                        v-if=\"avgTypes.includes(dialogData.rowData!.type)\"\n                        :label=\"$t('xpack.alert.' + dialogData.rowData!.type + 'UseExceedAvg')\"\n                        prop=\"count\"\n                    >\n                        <el-input v-model.number=\"dialogData.rowData!.count\">\n                            <template #append>%</template>\n                        </el-input>\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.' + dialogData.rowData!.type + 'UseExceedAvgHelper') }}\n                        </span>\n                    </el-form-item>\n\n                    <el-form-item\n                        :label=\"$t('xpack.alert.triggerCondition')\"\n                        v-if=\"ipTypes.includes(dialogData.rowData!.type)\"\n                        prop=\"count\"\n                    >\n                        <div\n                            class=\"flex items-center flex-row md:flex-nowrap sm:flex-nowrap flex-wrap justify-between gap-2 w-full\"\n                        >\n                            <el-form-item prop=\"cycle\" class=\"md:flex-1 sm:flex-1\">\n                                <el-input v-model.number=\"dialogData.rowData!.cycle\" :max=\"200\">\n                                    <template #append>{{ $t('commons.units.minute') }}</template>\n                                </el-input>\n                            </el-form-item>\n\n                            <span class=\"whitespace-nowrap input-help w-[4.5rem]\">\n                                {{ $t('xpack.alert.loginFail') }}\n                            </span>\n                            <el-form-item prop=\"count\" class=\"md:flex-1 sm:flex-1\">\n                                <el-input v-model.number=\"dialogData.rowData!.count\">\n                                    <template #append>{{ $t('commons.units.time') }}</template>\n                                </el-input>\n                            </el-form-item>\n                        </div>\n                    </el-form-item>\n\n                    <el-form-item\n                        :label=\"$t('setting.ipWhiteList')\"\n                        prop=\"advancedParams\"\n                        v-if=\"ipTypes.includes(dialogData.rowData!.type)\"\n                    >\n                        <el-input\n                            type=\"textarea\"\n                            :placeholder=\"$t('setting.ipWhiteListEgs')\"\n                            :rows=\"4\"\n                            v-model=\"dialogData.rowData!.advancedParams\"\n                        />\n                        <span class=\"input-help\">{{ $t('xpack.alert.ipWhiteListHelper') }}</span>\n                    </el-form-item>\n\n                    <el-form-item :label=\"$t('xpack.alert.sendCount')\" prop=\"sendCount\">\n                        <el-input v-model.number=\"dialogData.rowData!.sendCount\" />\n                        <span class=\"input-help\">\n                            {{\n                                timeTypes.includes(dialogData.rowData!.type)\n                                    ? $t('xpack.alert.sendCountRulesHelper')\n                                    : noParamTypes.includes(dialogData.rowData!.type)\n                                    ? $t('xpack.alert.panelUpdateRulesHelper')\n                                    : $t('xpack.alert.oneDaySendCountRulesHelper')\n                            }}\n                        </span>\n                    </el-form-item>\n\n                    <el-form-item :label=\"$t('xpack.alert.alertMethod')\" prop=\"sendMethod\">\n                        <el-select class=\"selectClass\" v-model=\"dialogData.rowData!.sendMethod\" multiple cleanable>\n                            <el-option value=\"mail\" :label=\"$t('xpack.alert.mail')\" />\n                            <el-option\n                                value=\"weCom\"\n                                v-if=\"!globalStore.isIntl\"\n                                :disabled=\"!globalStore.isProductPro\"\n                                :label=\"$t('xpack.alert.weCom')\"\n                            />\n                            <el-option\n                                value=\"dingTalk\"\n                                v-if=\"!globalStore.isIntl\"\n                                :disabled=\"!globalStore.isProductPro\"\n                                :label=\"$t('xpack.alert.dingTalk')\"\n                            />\n                            <el-option\n                                value=\"feiShu\"\n                                v-if=\"!globalStore.isIntl\"\n                                :disabled=\"!globalStore.isProductPro\"\n                                :label=\"$t('xpack.alert.feiShu')\"\n                            />\n                            <el-option\n                                value=\"sms\"\n                                v-if=\"!globalStore.isIntl\"\n                                :disabled=\"!globalStore.isProductPro\"\n                                :label=\"$t('xpack.alert.sms')\"\n                            />\n                        </el-select>\n                    </el-form-item>\n                    <span class=\"input-help\">\n                        {{\n                            intervalTypes.includes(dialogData.rowData!.type)\n                                ? $t('xpack.alert.resourceAlertRulesHelper')\n                                : ''\n                        }}\n                    </span>\n                </el-col>\n            </el-row>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"visible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button\n                    type=\"primary\"\n                    @click=\"onSubmit(formRef)\"\n                    :disabled=\"dialogData.rowData?.type === 'panelPwdEndTime' && expirationDays === 0 && loading\"\n                >\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { ElForm } from 'element-plus';\nimport { Alert } from '@/api/interface/alert';\nimport { listSSL, listWebsites } from '@/api/modules/website';\nimport { CreateAlert, ListDisks, UpdateAlert, ListClams, ListCronJob } from '@/api/modules/alert';\nimport { MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { getSettingInfo } from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nimport { storeToRefs } from 'pinia';\nimport { routerToName } from '@/utils/router';\nimport { checkCidr, checkCidrV6, checkIpV4V6 } from '@/utils/util';\n\nconst globalStore = GlobalStore();\nconst { isMaster, isProductPro } = storeToRefs(globalStore);\n\ninterface DialogProps {\n    title: string;\n    rowData?: Alert.AlertInfo;\n}\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\nconst { t } = i18n.global;\nconst loading = ref(false);\nconst visible = ref(false);\nconst websiteOptions = ref();\nconst expirationDays = ref(0);\nconst sslOptions = ref([]);\nconst diskOptions = ref([]);\nconst clamsOptions = ref([]);\nconst cronJobOptions = ref([]);\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\nconst timeTypes = ['ssl', 'siteEndTime', 'panelPwdEndTime'];\nconst avgTypes = ['cpu', 'memory', 'load'];\nconst ipTypes = ['sshLogin', 'panelLogin'];\nconst noParamTypes = ['panelUpdate'];\nconst intervalTypes = ['cpu', 'memory', 'load', 'disk', 'sshLogin', 'panelLogin', 'nodeException', 'licenseException'];\n\nconst diskTypes = ['disk'];\nconst cronjobTypes = [\n    'shell',\n    'app',\n    'website',\n    'database',\n    'directory',\n    'log',\n    'snapshot',\n    'curl',\n    'cutWebsiteLog',\n    'clean',\n    'ntp',\n];\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    dialogData.value.rowData.sendMethod = [];\n    if (dialogData.value.rowData.method != '') {\n        dialogData.value.rowData.sendMethod = dialogData.value.rowData.method.split(',');\n    }\n    if (cronjobTypes.includes(dialogData.value.rowData.type)) {\n        dialogData.value.rowData.subType = dialogData.value.rowData.type;\n        dialogData.value.rowData.type = 'cronJob';\n    }\n    initOptions(dialogData.value.rowData.type, dialogData.value.rowData.subType);\n    visible.value = true;\n};\n\nconst rules = reactive({\n    type: [Rules.requiredSelect],\n    subType: [Rules.requiredSelect],\n    project: [Rules.requiredSelect],\n    cycle: [Rules.requiredInput, Rules.integerNumber, { validator: checkCycle, trigger: 'blur' }],\n    count: [Rules.requiredInput, Rules.integerNumber, { validator: checkCount, trigger: 'blur' }],\n    sendCount: [Rules.requiredInput, Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }],\n    sendMethod: [Rules.requiredSelect],\n    advancedParams: [{ required: false, validator: checkIPs, trigger: 'blur' }],\n});\n\nconst allTaskOptions = [\n    { value: 'panelPwdEndTime', label: 'xpack.alert.panelPwdEndTime', show: isMaster.value },\n    { value: 'panelLogin', label: 'xpack.alert.panelLogin', show: isMaster.value },\n    { value: 'sshLogin', label: 'xpack.alert.sshLogin', show: true },\n    { value: 'licenseException', label: 'xpack.alert.licenseException', show: isMaster.value && isProductPro.value },\n    { value: 'ssl', label: 'xpack.alert.ssl', show: true },\n    { value: 'siteEndTime', label: 'xpack.alert.siteEndTime', show: true },\n    { value: 'nodeException', label: 'xpack.alert.nodeException', show: isMaster.value && isProductPro.value },\n    { value: 'cpu', label: 'xpack.alert.cpu', show: true },\n    { value: 'memory', label: 'xpack.alert.memory', show: true },\n    { value: 'disk', label: 'xpack.alert.disk', show: true },\n    { value: 'load', label: 'xpack.alert.load', show: true },\n    { value: 'cronJob', label: 'xpack.alert.cronjob', show: true },\n    { value: 'clams', label: 'xpack.alert.clams', show: true },\n    { value: 'panelUpdate', label: 'xpack.alert.panelUpdate', show: isMaster.value },\n];\n\nfunction checkRange(value: any, min: number, max: number, callback: any) {\n    const num = Number(value);\n    if (isNaN(num)) {\n        return callback(new Error(i18n.global.t('commons.rule.number')));\n    }\n    if (num < min || num > max) {\n        return callback(new Error(i18n.global.t('commons.rule.numberRange', [min, max])));\n    }\n    callback();\n}\n\nfunction checkCycle(rule: any, value: any, callback: any) {\n    if (value === '') return callback();\n\n    const type = dialogData.value.rowData.type;\n\n    if (type === 'ssl') {\n        return checkRange(value, 1, 60, callback);\n    } else if (ipTypes.includes(type)) {\n        return checkRange(value, 1, 200, callback);\n    } else {\n        return checkRange(value, 1, 30, callback);\n    }\n}\n\nfunction checkCount(rule: any, value: any, callback: any) {\n    if (value === '') return callback();\n\n    const type = dialogData.value.rowData.type;\n    const cycle = dialogData.value.rowData.cycle;\n\n    if (avgTypes.includes(type) || ipTypes.includes(type)) {\n        return checkRange(value, 1, 100, callback);\n    }\n    if (type === 'disk' && cycle === 2) {\n        return checkRange(value, 1, 100, callback);\n    }\n    callback();\n}\n\nfunction checkSendCount(rule: any, value: any, callback: any) {\n    if (value === '') return callback();\n\n    const type = dialogData.value.rowData.type;\n    const cycle = dialogData.value.rowData.cycle;\n\n    if (type === 'disk' || avgTypes.includes(type) || ipTypes.includes(type)) {\n        return checkRange(value, 1, 50, callback);\n    } else if (noParamTypes.includes(type)) {\n        return checkRange(value, 1, 30, callback);\n    } else {\n        if (cycle > 0) {\n            return checkRange(value, 1, cycle, callback);\n        }\n    }\n    callback();\n}\n\nfunction checkIPs(rule: any, value: any, callback: any) {\n    if (typeof value === 'string' && value.trim() !== '') {\n        let addr = value.split('\\n');\n        for (const item of addr) {\n            if (item === '') {\n                continue;\n            }\n            if (item.indexOf('/') !== -1) {\n                if (item.indexOf(':') !== -1) {\n                    if (checkCidrV6(item)) {\n                        return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                    }\n                } else if (checkCidr(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            } else if (checkIpV4V6(item)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        }\n    }\n    callback();\n}\n\nconst initOptions = (type: string, subType: string) => {\n    if (type === 'ssl') {\n        loadSSLs();\n    }\n    if (type === 'siteEndTime') {\n        loadWebsites();\n    }\n    if (type === 'panelPwdEndTime') {\n        loadSettings();\n    }\n    if (diskTypes.includes(type)) {\n        loadDisks();\n    }\n    if (type === 'clams') {\n        loadClams();\n    }\n    if (type === 'cronJob' && cronjobTypes.includes(subType)) {\n        loadCronJob(subType);\n    }\n};\n\nconst handleClose = () => {\n    visible.value = false;\n    cronJobOptions.value = [];\n};\n\nconst changeType = () => {\n    const typeToCycleMap = {\n        ssl: 15,\n        siteEndTime: 15,\n        panelPwdEndTime: 15,\n        panelUpdate: 0,\n        disk: 2,\n        cpu: 5,\n        load: 5,\n        memory: 5,\n        clams: 0,\n        app: 0,\n        website: 0,\n        database: 0,\n        directory: 0,\n        log: 0,\n        snapshot: 0,\n        shell: 0,\n        curl: 0,\n        cutWebsiteLog: 0,\n        clean: 0,\n        ntp: 0,\n        sshLogin: 30,\n        panelLogin: 30,\n        nodeException: 0,\n        licenseException: 0,\n    };\n\n    const typeToCountMap = {\n        ssl: 0,\n        siteEndTime: 0,\n        panelPwdEndTime: 0,\n        panelUpdate: 0,\n        disk: 80,\n        cpu: 80,\n        load: 80,\n        memory: 80,\n        clams: 0,\n        app: 0,\n        website: 0,\n        database: 0,\n        directory: 0,\n        log: 0,\n        snapshot: 0,\n        shell: 0,\n        curl: 0,\n        cutWebsiteLog: 0,\n        clean: 0,\n        ntp: 0,\n        sshLogin: 3,\n        panelLogin: 3,\n        nodeException: 0,\n        licenseException: 0,\n    };\n    const typeToProjectMap = {\n        ssl: 'all',\n        siteEndTime: 'all',\n        panelPwdEndTime: 'all',\n        panelUpdate: 'all',\n        disk: 'all',\n        cpu: 'all',\n        load: 'all',\n        memory: 'all',\n        clams: '',\n        app: '',\n        website: '',\n        database: '',\n        directory: '',\n        log: '',\n        snapshot: '',\n        shell: '',\n        curl: '',\n        cutWebsiteLog: '',\n        clean: '',\n        ntp: '',\n        sshLogin: 'all',\n        panelLogin: 'all',\n        nodeException: 'all',\n        licenseException: 'all',\n    };\n\n    const rowData = dialogData.value.rowData;\n    if (rowData) {\n        let type = rowData.type;\n        let subType = rowData.type;\n        if (dialogData.value.rowData.type === 'cronJob') {\n            subType = typeof rowData.subType === 'undefined' ? 'shell' : rowData.subType;\n            type = subType;\n            rowData.subType = subType;\n        }\n        rowData.project = typeof typeToProjectMap[type] !== 'undefined' ? typeToProjectMap[type] : rowData.project;\n        rowData.cycle = typeof typeToCycleMap[type] !== 'undefined' ? typeToCycleMap[type] : rowData.cycle;\n        rowData.count = typeof typeToCountMap[type] !== 'undefined' ? typeToCountMap[type] : rowData.count;\n\n        rowData.sendCount = 3;\n        formRef.value.validate();\n        initOptions(rowData.type, subType);\n    }\n};\n\nconst changeCycle = () => {\n    if (diskTypes.includes(dialogData.value.rowData.type)) {\n        dialogData.value.rowData.count = dialogData.value.rowData.cycle == 1 ? 30 : 80;\n        formRef.value.validate();\n    }\n};\n\nconst loadWebsites = async () => {\n    const res = await listWebsites();\n    websiteOptions.value = res.data || [];\n};\n\nconst loadSSLs = async () => {\n    const res = await listSSL({});\n    sslOptions.value = res.data || [];\n};\n\nconst loadDisks = async () => {\n    const res = await ListDisks();\n    diskOptions.value = res.data || [];\n};\n\nconst loadClams = async () => {\n    const res = await ListClams();\n    clamsOptions.value = res.data || [];\n    dialogData.value.rowData.project = dialogData.value.rowData.project || String(clamsOptions.value[0].id);\n};\n\nconst loadCronJob = async (jobType: string) => {\n    const res = await ListCronJob({\n        type: jobType,\n        name: '',\n        status: '',\n    });\n    cronJobOptions.value = res.data || [];\n    dialogData.value.rowData.project = dialogData.value.rowData.project || String(cronJobOptions.value[0].id);\n};\n\nconst loadSettings = async () => {\n    const res = await getSettingInfo();\n    expirationDays.value = Number(res.data.expirationDays);\n};\n\nconst formatTitle = (row: Alert.AlertInfo) => {\n    if (row.type === 'cronJob') {\n        row.type = row.subType;\n    }\n    const titleTemplates = {\n        ssl: () => {\n            return row.project === 'all'\n                ? t('xpack.alert.allSslTitle')\n                : t('xpack.alert.sslTitle', [formatSSLName(Number(row.project))]);\n        },\n        siteEndTime: () => {\n            return row.project === 'all'\n                ? t('xpack.alert.allSiteEndTimeTitle')\n                : t('xpack.alert.siteEndTimeTitle', [formatWebsiteName(Number(row.project))]);\n        },\n        panelPwdEndTime: () => t('xpack.alert.panelPwdEndTimeTitle'),\n        panelUpdate: () => t('xpack.alert.panelUpdateTitle'),\n        cpu: () => t('xpack.alert.cpuTitle'),\n        memory: () => t('xpack.alert.memoryTitle'),\n        load: () => t('xpack.alert.loadTitle'),\n        disk: () => {\n            return row.project === 'all' ? t('xpack.alert.allDiskTitle') : t('xpack.alert.diskTitle', [row.project]);\n        },\n        clams: () => t('xpack.alert.clamsTitle', [formatClamName(Number(row.project))]),\n        app: () => t('xpack.alert.cronJobAppTitle', [formatCronJobName(Number(row.project))]),\n        website: () => t('xpack.alert.cronJobWebsiteTitle', [formatCronJobName(Number(row.project))]),\n        database: () => t('xpack.alert.cronJobDatabaseTitle', [formatCronJobName(Number(row.project))]),\n        directory: () => t('xpack.alert.cronJobDirectoryTitle', [formatCronJobName(Number(row.project))]),\n        log: () => t('xpack.alert.cronJobLogTitle', [formatCronJobName(Number(row.project))]),\n        snapshot: () => t('xpack.alert.cronJobSnapshotTitle', [formatCronJobName(Number(row.project))]),\n        shell: () => t('xpack.alert.cronJobShellTitle', [formatCronJobName(Number(row.project))]),\n        curl: () => t('xpack.alert.cronJobCurlTitle', [formatCronJobName(Number(row.project))]),\n        cutWebsiteLog: () => t('xpack.alert.cronJobCutWebsiteLogTitle', [formatCronJobName(Number(row.project))]),\n        clean: () => t('xpack.alert.cronJobCleanTitle', [formatCronJobName(Number(row.project))]),\n        ntp: () => t('xpack.alert.cronJobNtpTitle', [formatCronJobName(Number(row.project))]),\n        nodeException: () => t('xpack.alert.nodeException'),\n        licenseException: () => t('xpack.alert.licenseException'),\n        panelLogin: () => t('xpack.alert.panelLogin'),\n        sshLogin: () => t('xpack.alert.sshLogin'),\n    };\n\n    return titleTemplates[row.type] ? titleTemplates[row.type]() : '';\n};\n\nconst formatSSLName = (id: number) => {\n    const sslObject = sslOptions.value.find((item) => item.id === id);\n    return sslObject ? sslObject.primaryDomain : undefined;\n};\n\nconst formatWebsiteName = (id: number) => {\n    const websiteOption = websiteOptions.value.find((item: { id: number }) => item.id === id);\n    return websiteOption ? websiteOption.primaryDomain : undefined;\n};\n\nconst formatClamName = (id: number) => {\n    const clamObject = clamsOptions.value.find((item) => item.id === id);\n    return clamObject ? clamObject.name : undefined;\n};\n\nconst formatCronJobName = (id: number) => {\n    const cronJobOption = cronJobOptions.value.find((item: { id: number }) => item.id === id);\n    return cronJobOption ? cronJobOption.name : undefined;\n};\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (loading.value) return;\n    loading.value = true;\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (!dialogData.value.rowData) return;\n        dialogData.value.rowData.method = dialogData.value.rowData.sendMethod.join(',');\n        dialogData.value.rowData.title = formatTitle(dialogData.value.rowData);\n        if (dialogData.value.rowData.type === 'cronJob') {\n            dialogData.value.rowData.type = dialogData.value.rowData.subType;\n        }\n        try {\n            if (dialogData.value.title === 'create') {\n                await CreateAlert(dialogData.value.rowData);\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n            } else if (dialogData.value.title === 'edit') {\n                await UpdateAlert(dialogData.value.rowData);\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            }\n            emit('search');\n            visible.value = false;\n        } catch (err) {\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\nconst isEver = (time: string) => {\n    const expireDate = new Date(time);\n    return expireDate < new Date('1970-01-02');\n};\n\nconst quickJump = (name: string) => {\n    handleClose();\n    routerToName(name);\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style scoped lang=\"scss\">\n.tagClass {\n    float: right;\n    margin-right: 10px;\n    font-size: 12px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/index.vue",
    "content": "<template>\n    <div>\n        <el-card class=\"router_card p-2 sm:p-3 mt-2\">\n            <div class=\"flex w-full justify-start items-center\">\n                <el-button type=\"primary\" :plain=\"index !== '0'\" @click=\"changeTab('0')\">\n                    {{ $t('xpack.alert.list') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"index !== '1'\" @click=\"changeTab('1')\">\n                    {{ $t('xpack.alert.logs') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"index !== '2'\" @click=\"changeTab('2')\">\n                    {{ $t('commons.button.set') }}\n                </el-button>\n            </div>\n        </el-card>\n        <AlertDash v-if=\"index == '0'\" />\n        <AlertLogs v-if=\"index == '1'\" />\n        <AlertSetting v-if=\"index == '2'\" />\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport AlertDash from '@/views/setting/alert/dash/index.vue';\nimport AlertLogs from '@/views/setting/alert/log/index.vue';\nimport AlertSetting from '@/views/setting/alert/setting/index.vue';\n\nconst index = ref('0');\n\nconst changeTab = (ind: string) => {\n    index.value = ind;\n    localStorage.setItem('alert-notice-tab', index.value);\n};\n\nonMounted(async () => {\n    const tab = localStorage.getItem('alert-notice-tab');\n    if (tab) {\n        index.value = tab;\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/log/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent :title=\"$t('xpack.alert.logs')\" v-loading=\"loading\">\n            <template #toolbar>\n                <div class=\"flex justify-between gap-2 flex-wrap sm:flex-row\">\n                    <div class=\"flex flex-wrap gap-3\">\n                        <el-button type=\"primary\" @click=\"syncAll\" v-if=\"isProductPro && !globalStore.isIntl\">\n                            {{ $t('commons.button.sync') }}\n                        </el-button>\n                        <el-button type=\"primary\" plain @click=\"onClean\">{{ $t('xpack.alert.cleanLog') }}</el-button>\n                    </div>\n                </div>\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"data\" @search=\"search()\">\n                    <el-table-column :label=\"$t('xpack.alert.alertMsg')\" prop=\"message\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            {{ formatMessage(row.alertDetail) }}\n                        </template>\n                    </el-table-column>\n\n                    <el-table-column :label=\"$t('xpack.alert.alertMethod')\" prop=\"method\" width=\"150px\">\n                        <template #default=\"{ row }\">\n                            {{ formatMethod(row) }}\n                        </template>\n                    </el-table-column>\n\n                    <el-table-column\n                        :label=\"$t('commons.table.status')\"\n                        fix\n                        show-overflow-tooltip\n                        prop=\"status\"\n                        width=\"150px\"\n                    >\n                        <template #default=\"{ row }\">\n                            <el-tag\n                                v-if=\"statusConfig(row)\"\n                                :type=\"statusConfig(row).type\"\n                                :link=\"statusConfig(row).link\"\n                            >\n                                <el-tooltip v-if=\"row.message\" :content=\"row.message\" placement=\"top\" trigger=\"click\">\n                                    {{ $t(statusConfig(row).text) }}\n                                </el-tooltip>\n                                <template v-else>\n                                    {{ $t(statusConfig(row).text) }}\n                                </template>\n                            </el-tag>\n                        </template>\n                    </el-table-column>\n\n                    <el-table-column\n                        :label=\"$t('commons.table.createdAt')\"\n                        :formatter=\"dateFormat\"\n                        prop=\"createdAt\"\n                        width=\"180px\"\n                    ></el-table-column>\n\n                    <el-table-column :label=\"$t('xpack.alert.sendCount')\" prop=\"count\" width=\"150px\">\n                        <template #default=\"{ row }\">\n                            {{ formatCount(row) }}\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations\n                        v-if=\"isProductPro && !globalStore.isIntl\"\n                        :ellipsis=\"2\"\n                        width=\"130px\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        :fixed=\"mobile ? false : 'right'\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref, computed } from 'vue';\nimport { dateFormat } from '@/utils/util';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { GlobalStore } from '@/store';\nimport { Alert } from '@/api/interface/alert';\nimport {\n    SearchAlertLogs,\n    SyncAlertInfo,\n    CleanAlertLogs,\n    SyncAlertAll,\n    SyncOfflineAlert,\n    ListAlertConfigs,\n} from '@/api/modules/alert';\nimport { ElMessageBox } from 'element-plus';\n\nconst globalStore = GlobalStore();\nconst { t } = i18n.global;\nconst isProductPro = ref(false);\nconst loading = ref(false);\nconst data = ref();\nconst isOffline = ref('Disable');\nconst resourceTypes = [\n    'cpu',\n    'memory',\n    'load',\n    'disk',\n    'nodeException',\n    'licenseException',\n    'panelLogin',\n    'sshLogin',\n    'panelIpLogin',\n    'sshIpLogin',\n];\nconst paginationConfig = reactive({\n    cacheSizeKey: 'alert-log-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('alert-log-page-size')) || 20,\n    total: 0,\n});\nconst req = reactive({\n    page: 1,\n    pageSize: 10,\n    total: 0,\n    count: null,\n    message: '',\n    CreatedAt: '',\n    status: '',\n});\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.sync'),\n        click: function (row: Alert.AlertLog) {\n            syncAlert(row);\n        },\n        disabled: (row: Alert.AlertLog) => {\n            return (\n                (row.method != 'sms' && row.status != 'PushSuccess' && row.status != 'SyncError') ||\n                row.status == 'Success'\n            );\n        },\n    },\n];\n\nconst statusMap = {\n    PushSuccess: { type: 'success', text: 'xpack.alert.pushSuccess' },\n    Pushing: { type: 'warning', text: 'xpack.alert.pushing' },\n    Success: { type: 'success', text: 'xpack.alert.success' },\n    Error: { type: 'danger', text: 'xpack.alert.error' },\n    SyncError: { type: 'danger', text: 'xpack.alert.syncError', link: true },\n    default: { type: 'danger', text: 'xpack.alert.pushError', link: true },\n};\n\nconst statusConfig = (row) => {\n    return statusMap[row.status] || statusMap['default'];\n};\n\nconst syncAlert = (row: Alert.AlertLog) => {\n    ElMessageBox.confirm(t('xpack.alert.syncAlertInfoMsg'), t('xpack.alert.syncAlertInfo'), {\n        confirmButtonText: t('commons.button.confirm'),\n        cancelButtonText: t('commons.button.cancel'),\n    }).then(async () => {\n        if (!globalStore.isMaster && isOffline.value == 'Enable') {\n            await SyncOfflineAlert();\n        } else {\n            await SyncAlertInfo({ id: row.id });\n        }\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        await search();\n    });\n};\n\nconst formatMessage = (row: Alert.AlertInfo) => {\n    const messageTemplates = {\n        ssl: () => {\n            return row.project === 'all' ? t('xpack.alert.allSslTitle') : t('xpack.alert.sslTitle', [row.project]);\n        },\n        siteEndTime: () => {\n            return row.project === 'all'\n                ? t('xpack.alert.allSiteEndTimeTitle')\n                : t('xpack.alert.siteEndTimeTitle', [row.project]);\n        },\n        panelPwdEndTime: () => t('xpack.alert.panelPwdEndTimeTitle'),\n        panelUpdate: () => t('xpack.alert.panelUpdateTitle'),\n        cpu: () => t('xpack.alert.cpuTitle'),\n        memory: () => t('xpack.alert.memoryTitle'),\n        load: () => t('xpack.alert.loadTitle'),\n        disk: () => {\n            return row.project === 'all' ? t('xpack.alert.allDiskTitle') : t('xpack.alert.diskTitle', [row.project]);\n        },\n        clams: () => t('xpack.alert.clamsTitle', [row.project]),\n        app: () => t('xpack.alert.cronJobAppTitle', [row.project]),\n        website: () => t('xpack.alert.cronJobWebsiteTitle', [row.project]),\n        database: () => t('xpack.alert.cronJobDatabaseTitle', [row.project]),\n        directory: () => t('xpack.alert.cronJobDirectoryTitle', [row.project]),\n        log: () => t('xpack.alert.cronJobLogTitle', [row.project]),\n        snapshot: () => t('xpack.alert.cronJobSnapshotTitle', [row.project]),\n        shell: () => t('xpack.alert.cronJobShellTitle', [row.project]),\n        curl: () => t('xpack.alert.cronJobCurlTitle', [row.project]),\n        cutWebsiteLog: () => t('xpack.alert.cronJobCutWebsiteLogTitle', [row.project]),\n        clean: () => t('xpack.alert.cronJobCleanTitle', [row.project]),\n        ntp: () => t('xpack.alert.cronJobNtpTitle', [row.project]),\n        nodeException: () => t('xpack.alert.nodeException'),\n        licenseException: () => t('xpack.alert.licenseException'),\n        panelLogin: () => t('xpack.alert.panelLogin'),\n        sshLogin: () => t('xpack.alert.sshLogin'),\n        panelIpLogin: () => t('xpack.alert.panelIpLogin'),\n        sshIpLogin: () => t('xpack.alert.sshIpLogin'),\n    };\n    let type = row.type === 'cronJob' ? row.subType : row.type;\n    return messageTemplates[type] ? messageTemplates[type]() : '';\n};\n\nconst formatMethod = (row: Alert.AlertLog) => {\n    switch (row.method) {\n        case 'mail':\n            return t('xpack.alert.mail');\n        case 'dingTalk':\n            return t('xpack.alert.dingTalk');\n        case 'weCom':\n            return t('xpack.alert.weCom');\n        case 'feiShu':\n            return t('xpack.alert.feiShu');\n        case 'wechat':\n            return t('xpack.alert.wechat');\n        case 'sms':\n            return t('xpack.alert.sms');\n        case 'webhook':\n            return t('xpack.alert.webhook');\n        default:\n            return t('xpack.alert.unknown');\n    }\n};\n\nconst formatCount = (row: Alert.AlertInfo) => {\n    return resourceTypes.includes(row.type) || row.type === 'cronJob' || row.type === 'clams'\n        ? t('xpack.alert.daily', [row.count])\n        : t('xpack.alert.cumulative', [row.count]);\n};\n\nconst search = async () => {\n    loading.value = true;\n    if (req.status) {\n        req.status = req.status.toLowerCase() === 'enable' ? 'Enable' : 'Disable';\n    }\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        count: req.count,\n        status: req.status,\n    };\n    try {\n        const res = await SearchAlertLogs(params);\n        data.value = res.data.items || [];\n        paginationConfig.total = res.data.total || 0;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst syncAll = async () => {\n    ElMessageBox.confirm(t('xpack.alert.syncAlertInfoMsg'), t('xpack.alert.syncAlertInfo'), {\n        confirmButtonText: t('commons.button.confirm'),\n        cancelButtonText: t('commons.button.cancel'),\n    }).then(async () => {\n        await syncAllAlert();\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\n\nconst syncAllAlert = async () => {\n    if (!globalStore.isMaster && isOffline.value == 'Enable') {\n        await SyncOfflineAlert();\n    } else {\n        await SyncAlertAll();\n    }\n    await search();\n};\n\nconst onClean = async () => {\n    ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('xpack.alert.cleanAlertLogs'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        await CleanAlertLogs()\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n        await search();\n    });\n};\n\nconst searchAlertInfo = async () => {\n    if (!globalStore.isMaster) {\n        loading.value = true;\n        try {\n            const res = await ListAlertConfigs();\n            const commonFound = res.data.find((s: any) => s.type === 'common');\n            const config: Alert.CommonConfig = JSON.parse(commonFound.config);\n            isOffline.value = config.isOffline;\n        } finally {\n            loading.value = false;\n        }\n    }\n    await search();\n};\n\nonMounted(async () => {\n    await searchAlertInfo();\n    isProductPro.value = globalStore.isProductPro;\n    if (globalStore.isProductPro && !globalStore.isIntl) {\n        await syncAllAlert();\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/setting/email/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('xpack.alert.emailConfig')\" @close=\"handleClose\" size=\"736\">\n        <el-form\n            ref=\"formRef\"\n            :rules=\"rules\"\n            label-position=\"top\"\n            :model=\"form.config\"\n            @submit.prevent\n            v-loading=\"loading\"\n        >\n            <el-row type=\"flex\" justify=\"center\">\n                <el-col :span=\"22\">\n                    <el-form-item :label=\"$t('xpack.alert.displayName')\" prop=\"displayName\">\n                        <el-input v-model=\"form.config.displayName\" />\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.displayNameHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('xpack.alert.sender')\" prop=\"sender\">\n                        <el-input v-model.trim=\"form.config.sender\" />\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.senderHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('commons.login.username')\" prop=\"userName\">\n                        <el-input v-model.trim=\"form.config.userName\" />\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.userNameHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('xpack.alert.password')\" prop=\"password\">\n                        <el-input v-model.trim=\"form.config.password\" type=\"password\" show-password />\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.passwordHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('xpack.alert.host')\" prop=\"host\">\n                        <el-input v-model.trim=\"form.config.host\" placeholder=\"smtp.qq.com\" />\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.hostHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('xpack.alert.port')\" prop=\"port\">\n                        <el-input v-model.number=\"form.config.port\" :min=\"1\" :max=\"65535\" />\n                    </el-form-item>\n                    <el-form-item :label=\"$t('xpack.alert.encryption')\" prop=\"encryption\">\n                        <div class=\"flex items-center gap-2\">\n                            <span class=\"el-form-item__label\">SSL</span>\n                            <el-switch\n                                v-model=\"form.config.encryption\"\n                                :active-value=\"'SSL'\"\n                                :inactive-value=\"form.config.encryption === 'SSL' ? 'NONE' : form.config.encryption\"\n                            />\n                        </div>\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.sslHelper') }}\n                        </span>\n                        <div class=\"flex items-center gap-2\">\n                            <span class=\"el-form-item__label\">TLS</span>\n                            <el-switch\n                                v-model=\"form.config.encryption\"\n                                :active-value=\"'TLS'\"\n                                :inactive-value=\"form.config.encryption === 'TLS' ? 'NONE' : form.config.encryption\"\n                            />\n                        </div>\n                        <span class=\"input-help\">\n                            {{ $t('xpack.alert.tlsHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('xpack.alert.recipient')\" prop=\"recipient\">\n                        <el-input v-model=\"form.config.recipient\" />\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n        <template #footer>\n            <div class=\"flex items-center justify-between\">\n                <el-button @click=\"onTest(formRef)\" plain type=\"primary\">\n                    {{ $t('xpack.alert.test') }}\n                </el-button>\n                <div>\n                    <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                    <el-button :disabled=\"loading || !isOK\" type=\"primary\" @click=\"onSave(formRef)\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </div>\n            </div>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { TestAlertConfig, UpdateAlertConfig } from '@/api/modules/alert';\nimport { Rules } from '@/global/form-rules';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst rules = {\n    displayName: [Rules.requiredInput],\n    sender: [Rules.requiredInput],\n    host: [Rules.requiredInput],\n    port: [Rules.requiredInput],\n    recipient: [Rules.requiredInput],\n};\ninterface Config {\n    status: string;\n    displayName: string;\n    sender: string;\n    userName: string;\n    password: string;\n    host: string;\n    port: number;\n    encryption: string;\n    recipient: string;\n}\ninterface DialogProps {\n    id: number;\n    config: Config;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    id: undefined,\n    config: {\n        displayName: '',\n        sender: '',\n        password: '',\n        userName: '',\n        host: '',\n        port: 465,\n        encryption: 'NONE',\n        status: 'Enable',\n        recipient: '',\n    },\n});\nconst isOK = ref(false);\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.id = params.id;\n    form.config = params.config;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        try {\n            form.config.status = 'Enable';\n            const configInfo = form.config;\n            await UpdateAlertConfig({\n                id: form.id,\n                type: 'email',\n                title: 'xpack.alert.emailConfig',\n                status: 'Enable',\n                config: JSON.stringify(configInfo),\n            });\n\n            loading.value = false;\n            handleClose();\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        } catch (error) {\n            loading.value = false;\n        }\n    });\n};\n\nconst onTest = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        try {\n            await TestAlertConfig(form.config)\n                .then((res) => {\n                    loading.value = false;\n                    if (res.data) {\n                        isOK.value = true;\n                        MsgSuccess(i18n.global.t('xpack.alert.alertTestOk'));\n                    } else {\n                        MsgError(i18n.global.t('xpack.alert.alertTestFailed'));\n                    }\n                })\n                .catch(() => {\n                    loading.value = false;\n                    MsgError(i18n.global.t('xpack.alert.alertTestFailed'));\n                });\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\nconst handleClose = () => {\n    isOK.value = false;\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/setting/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent :title=\"$t('commons.button.set')\" v-loading=\"loading\" :divider=\"true\">\n            <template #title>\n                <div class=\"flex items-center justify-between\">\n                    <span>{{ $t('xpack.alert.commonConfig') }}</span>\n                    <el-button plain round size=\"default\" @click=\"onChangeCommon(commonConfig.id)\">\n                        {{ $t('commons.button.edit') }}\n                    </el-button>\n                </div>\n            </template>\n            <template #main>\n                <el-form\n                    @submit.prevent\n                    ref=\"alertFormRef\"\n                    :label-position=\"mobile ? 'top' : 'left'\"\n                    label-width=\"120px\"\n                >\n                    <el-row>\n                        <el-col>\n                            <el-form-item :label=\"$t('xpack.alert.sendTimeRange')\" prop=\"sendTimeRange\">\n                                {{ sendTimeRange }}\n                            </el-form-item>\n                            <div v-if=\"!isMaster\">\n                                <el-form-item :label=\"$t('xpack.alert.offline')\" prop=\"isOffline\">\n                                    <el-switch\n                                        @change=\"onChangeOffline\"\n                                        v-model=\"commonConfig.config.isOffline\"\n                                        active-value=\"Enable\"\n                                        inactive-value=\"Disable\"\n                                    ></el-switch>\n                                    <span class=\"input-help\">{{ $t('xpack.alert.offlineHelper') }}</span>\n                                </el-form-item>\n                            </div>\n                        </el-col>\n                    </el-row>\n                </el-form>\n            </template>\n        </LayoutContent>\n        <LayoutContent :title=\"$t('commons.button.set')\" v-loading=\"loading\" :divider=\"true\">\n            <template #title>{{ $t('xpack.alert.methodConfig') }}</template>\n            <template #main>\n                <el-alert type=\"info\" :closable=\"false\">\n                    <template #title>\n                        <div class=\"flex items-center justify-start\">\n                            {{ $t('xpack.alert.alertConfigHelper') }}\n                            <span v-if=\"!globalStore.isProductPro\">\n                                {{ $t('commons.units.semicolon') }}{{ $t('xpack.alert.alertConfigProHelper') }}\n                            </span>\n                            <el-link\n                                class=\"ml-1 text-xs\"\n                                type=\"primary\"\n                                target=\"_blank\"\n                                :href=\"globalStore.docsUrl + '/user_manual/settings/#3'\"\n                            >\n                                {{ $t('commons.button.helpDoc') }}\n                            </el-link>\n                        </div>\n                    </template>\n                </el-alert>\n                <div class=\"grid gap-4 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 mt-3 app\">\n                    <el-card class=\"rounded-2xl shadow hover:shadow-md transition-all\">\n                        <div class=\"flex items-center justify-between mb-2\">\n                            <div class=\"text-lg font-semibold\">{{ $t('xpack.alert.emailConfig') }}</div>\n                            <div>\n                                <el-button\n                                    plain\n                                    round\n                                    size=\"default\"\n                                    :disabled=\"!emailConfig.id\"\n                                    @click=\"onChangeEmail(emailConfig.id)\"\n                                >\n                                    {{ $t('commons.button.edit') }}\n                                </el-button>\n                                <el-button\n                                    size=\"default\"\n                                    plain\n                                    round\n                                    :disabled=\"!emailConfig.id\"\n                                    @click=\"onDelete(emailConfig.id)\"\n                                >\n                                    {{ $t('commons.button.delete') }}\n                                </el-button>\n                            </div>\n                        </div>\n                        <div class=\"text-sm mb-2\">{{ $t('xpack.alert.emailConfigHelper') }}</div>\n                        <el-divider class=\"!mb-2 !mt-3\" />\n                        <div class=\"text-sm config-form\" v-if=\"emailConfig.id\">\n                            <el-form\n                                @submit.prevent\n                                ref=\"alertFormRef\"\n                                :label-position=\"mobile ? 'top' : 'left'\"\n                                label-width=\"110px\"\n                            >\n                                <el-form-item :label=\"$t('xpack.alert.displayName')\" prop=\"displayName\">\n                                    {{ emailConfig.config.displayName }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.sender')\" prop=\"sender\">\n                                    {{ emailConfig.config.sender }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('commons.login.username')\" prop=\"userName\">\n                                    {{ emailConfig.config.userName || emailConfig.config.sender }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.host')\" prop=\"host\">\n                                    {{ emailConfig.config.host }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.port')\" prop=\"port\">\n                                    {{ emailConfig.config.port }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.encryption')\" prop=\"encryption\">\n                                    {{ emailConfig.config.encryption }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.recipient')\" prop=\"recipient\">\n                                    {{ emailConfig.config.recipient }}\n                                </el-form-item>\n                            </el-form>\n                        </div>\n                        <div v-else class=\"flex items-center justify-center\" style=\"height: 257px\">\n                            <el-button size=\"large\" round plain type=\"primary\" @click=\"onChangeEmail(0)\">\n                                {{ $t('commons.button.create') }}\n                            </el-button>\n                        </div>\n                    </el-card>\n                    <el-card\n                        class=\"rounded-2xl shadow hover:shadow-md transition-all\"\n                        v-if=\"globalStore.isProductPro && !globalStore.isIntl\"\n                    >\n                        <div class=\"flex items-center justify-between mb-2\">\n                            <div class=\"text-lg font-semibold\">{{ $t('xpack.alert.weCom') }}</div>\n                            <div>\n                                <el-button\n                                    plain\n                                    round\n                                    size=\"default\"\n                                    :disabled=\"!weComConfig.id\"\n                                    @click=\"onChangeWeCom(weComConfig.id)\"\n                                >\n                                    {{ $t('commons.button.edit') }}\n                                </el-button>\n                                <el-button\n                                    size=\"default\"\n                                    plain\n                                    round\n                                    :disabled=\"!weComConfig.id\"\n                                    @click=\"onDelete(weComConfig.id)\"\n                                >\n                                    {{ $t('commons.button.delete') }}\n                                </el-button>\n                            </div>\n                        </div>\n                        <div class=\"text-sm mb-2\">{{ $t('xpack.alert.weComConfigHelper') }}</div>\n                        <el-divider class=\"!mb-2 !mt-3\" />\n                        <div class=\"text-sm config-form\" v-if=\"weComConfig.id\">\n                            <el-form\n                                @submit.prevent\n                                ref=\"alertFormRef\"\n                                :label-position=\"mobile ? 'top' : 'left'\"\n                                label-width=\"110px\"\n                            >\n                                <el-form-item :label=\"$t('xpack.alert.webhookName')\" prop=\"displayName\">\n                                    {{ weComConfig.config.displayName }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.webhookUrl')\" prop=\"url\">\n                                    <div class=\"webhook-field\">\n                                        <template v-if=\"weComUrlVisible\">\n                                            <el-tooltip :content=\"weComConfig.config.url\" placement=\"top\" effect=\"dark\">\n                                                <span class=\"webhook-text\">\n                                                    {{ weComConfig.config.url }}\n                                                </span>\n                                            </el-tooltip>\n                                        </template>\n                                        <template v-else>\n                                            <span class=\"webhook-text\">****************</span>\n                                        </template>\n                                        <el-icon class=\"webhook-icon\" @click=\"weComUrlVisible = !weComUrlVisible\">\n                                            <Hide v-if=\"!weComUrlVisible\" />\n                                            <View v-else />\n                                        </el-icon>\n                                    </div>\n                                </el-form-item>\n                            </el-form>\n                        </div>\n                        <div v-else class=\"flex items-center justify-center\" style=\"height: 257px\">\n                            <el-button size=\"large\" round plain type=\"primary\" @click=\"onChangeWeCom(0)\">\n                                {{ $t('commons.button.create') }}\n                            </el-button>\n                        </div>\n                    </el-card>\n                    <el-card\n                        class=\"rounded-2xl shadow hover:shadow-md transition-all\"\n                        v-if=\"globalStore.isProductPro && !globalStore.isIntl\"\n                    >\n                        <div class=\"flex items-center justify-between mb-2\">\n                            <div class=\"text-lg font-semibold\">{{ $t('xpack.alert.dingTalk') }}</div>\n                            <div>\n                                <el-button\n                                    plain\n                                    round\n                                    size=\"default\"\n                                    :disabled=\"!dingTalkConfig.id\"\n                                    @click=\"onChangeDingTalk(dingTalkConfig.id)\"\n                                >\n                                    {{ $t('commons.button.edit') }}\n                                </el-button>\n                                <el-button\n                                    size=\"default\"\n                                    plain\n                                    round\n                                    :disabled=\"!dingTalkConfig.id\"\n                                    @click=\"onDelete(dingTalkConfig.id)\"\n                                >\n                                    {{ $t('commons.button.delete') }}\n                                </el-button>\n                            </div>\n                        </div>\n                        <div class=\"text-sm mb-2\">{{ $t('xpack.alert.dingTalkConfigHelper') }}</div>\n                        <el-divider class=\"!mb-2 !mt-3\" />\n                        <div class=\"text-sm config-form\" v-if=\"dingTalkConfig.id\">\n                            <el-form\n                                @submit.prevent\n                                ref=\"alertFormRef\"\n                                :label-position=\"mobile ? 'top' : 'left'\"\n                                label-width=\"110px\"\n                            >\n                                <el-form-item :label=\"$t('xpack.alert.webhookName')\" prop=\"displayName\">\n                                    {{ dingTalkConfig.config.displayName }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.webhookUrl')\" prop=\"url\">\n                                    <div class=\"webhook-field\">\n                                        <template v-if=\"dingTalkUrlVisible\">\n                                            <el-tooltip\n                                                :content=\"dingTalkConfig.config.url\"\n                                                placement=\"top\"\n                                                effect=\"dark\"\n                                            >\n                                                <span class=\"webhook-text\">\n                                                    {{ dingTalkConfig.config.url }}\n                                                </span>\n                                            </el-tooltip>\n                                        </template>\n                                        <template v-else>\n                                            <span class=\"webhook-text\">****************</span>\n                                        </template>\n                                        <el-icon class=\"webhook-icon\" @click=\"dingTalkUrlVisible = !dingTalkUrlVisible\">\n                                            <Hide v-if=\"!dingTalkUrlVisible\" />\n                                            <View v-else />\n                                        </el-icon>\n                                    </div>\n                                </el-form-item>\n                            </el-form>\n                        </div>\n                        <div v-else class=\"flex items-center justify-center\" style=\"height: 257px\">\n                            <el-button size=\"large\" round plain type=\"primary\" @click=\"onChangeDingTalk(0)\">\n                                {{ $t('commons.button.create') }}\n                            </el-button>\n                        </div>\n                    </el-card>\n                    <el-card\n                        class=\"rounded-2xl shadow hover:shadow-md transition-all\"\n                        v-if=\"globalStore.isProductPro && !globalStore.isIntl\"\n                    >\n                        <div class=\"flex items-center justify-between mb-2\">\n                            <div class=\"text-lg font-semibold\">{{ $t('xpack.alert.feiShu') }}</div>\n                            <div>\n                                <el-button\n                                    plain\n                                    round\n                                    size=\"default\"\n                                    :disabled=\"!feiShuConfig.id\"\n                                    @click=\"onChangeFeiShu(feiShuConfig.id)\"\n                                >\n                                    {{ $t('commons.button.edit') }}\n                                </el-button>\n                                <el-button\n                                    size=\"default\"\n                                    plain\n                                    round\n                                    :disabled=\"!feiShuConfig.id\"\n                                    @click=\"onDelete(feiShuConfig.id)\"\n                                >\n                                    {{ $t('commons.button.delete') }}\n                                </el-button>\n                            </div>\n                        </div>\n                        <div class=\"text-sm mb-2\">{{ $t('xpack.alert.feiShuConfigHelper') }}</div>\n                        <el-divider class=\"!mb-2 !mt-3\" />\n                        <div class=\"text-sm config-form\" v-if=\"feiShuConfig.id\">\n                            <el-form\n                                @submit.prevent\n                                ref=\"alertFormRef\"\n                                :label-position=\"mobile ? 'top' : 'left'\"\n                                label-width=\"110px\"\n                            >\n                                <el-form-item :label=\"$t('xpack.alert.webhookName')\" prop=\"displayName\">\n                                    {{ feiShuConfig.config.displayName }}\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.webhookUrl')\" prop=\"url\">\n                                    <div class=\"webhook-field\">\n                                        <template v-if=\"feiShuUrlVisible\">\n                                            <el-tooltip\n                                                :content=\"feiShuConfig.config.url\"\n                                                placement=\"top\"\n                                                effect=\"dark\"\n                                            >\n                                                <span class=\"webhook-text\">\n                                                    {{ feiShuConfig.config.url }}\n                                                </span>\n                                            </el-tooltip>\n                                        </template>\n                                        <template v-else>\n                                            <span class=\"webhook-text\">****************</span>\n                                        </template>\n                                        <el-icon class=\"webhook-icon\" @click=\"feiShuUrlVisible = !feiShuUrlVisible\">\n                                            <Hide v-if=\"!feiShuUrlVisible\" />\n                                            <View v-else />\n                                        </el-icon>\n                                    </div>\n                                </el-form-item>\n                            </el-form>\n                        </div>\n                        <div v-else class=\"flex items-center justify-center\" style=\"height: 257px\">\n                            <el-button size=\"large\" round plain type=\"primary\" @click=\"onChangeFeiShu(0)\">\n                                {{ $t('commons.button.create') }}\n                            </el-button>\n                        </div>\n                    </el-card>\n                    <el-card\n                        class=\"rounded-2xl shadow hover:shadow-md transition-all\"\n                        v-if=\"globalStore.isProductPro && !globalStore.isIntl\"\n                    >\n                        <div class=\"flex items-center justify-between mb-2\">\n                            <div class=\"text-lg font-semibold\">\n                                {{ $t('xpack.alert.smsConfig') }}\n                            </div>\n                            <div>\n                                <el-button plain round @click=\"onChangePhone(smsConfig.id)\">\n                                    {{ $t('commons.button.edit') }}\n                                </el-button>\n                            </div>\n                        </div>\n                        <div class=\"text-sm mb-2 flex items-center justify-start\">\n                            {{ $t('xpack.alert.alertSmsHelper', [totalSms, usedSms]) }}\n                            <el-link class=\"ml-1 text-xs\" @click=\"goBuy\" type=\"primary\" icon=\"Position\">\n                                <span class=\"ml-0.5\">{{ $t('xpack.alert.goBuy') }}</span>\n                            </el-link>\n                        </div>\n                        <el-divider class=\"!mb-2 !mt-3\" />\n                        <div class=\"text-sm config-form\">\n                            <el-form\n                                @submit.prevent\n                                ref=\"alertFormRef\"\n                                :label-position=\"mobile ? 'top' : 'left'\"\n                                label-width=\"110px\"\n                            >\n                                <el-form-item :label=\"$t('xpack.alert.phone')\">\n                                    <span v-if=\"smsConfig.config.phone\">{{ smsConfig.config.phone }}</span>\n                                    <span v-else class=\"label\">{{ $t('xpack.alert.defaultPhone') }}</span>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('xpack.alert.dailyAlertNum')\" prop=\"dailyAlertNum\">\n                                    {{ smsConfig.config.alertDailyNum }}\n                                </el-form-item>\n                            </el-form>\n                        </div>\n                    </el-card>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <EmailDrawer ref=\"emailRef\" @search=\"search\" />\n        <Phone ref=\"phoneRef\" @search=\"search\" />\n        <SendTimeRange ref=\"sendTimeRangeRef\" @search=\"search\" />\n        <WebhookDrawer ref=\"webHookRef\" @search=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, onMounted, ref, Ref } from 'vue';\nimport { GlobalStore } from '@/store';\nimport { ListAlertConfigs, DeleteAlertConfig, UpdateAlertConfig } from '@/api/modules/alert';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { View, Hide } from '@element-plus/icons-vue';\nimport Phone from '@/views/setting/alert/setting/phone/index.vue';\nimport SendTimeRange from '@/views/setting/alert/setting/time-range/index.vue';\nimport i18n from '@/lang';\nimport { storeToRefs } from 'pinia';\nimport { MsgSuccess } from '@/utils/message';\nimport EmailDrawer from '@/views/setting/alert/setting/email/index.vue';\nimport WebhookDrawer from '@/views/setting/alert/setting/webhook/index.vue';\nimport { Alert } from '@/api/interface/alert';\nimport { getLicenseSmsInfo } from '@/api/modules/setting';\n\nconst globalStore = GlobalStore();\nconst { isMaster } = storeToRefs(globalStore);\nconst loading = ref(false);\n\nconst alertFormRef = ref<FormInstance>();\nconst phoneRef = ref();\nconst emailRef = ref();\nconst webHookRef = ref();\nconst sendTimeRangeRef = ref();\nconst sendTimeRangeValue = ref();\nconst sendTimeRange = ref();\n\nconst isInitialized = ref(false);\nconst defaultEmailConfig: Alert.EmailConfig = {\n    id: undefined,\n    type: 'email',\n    title: 'xpack.alert.emailConfig',\n    status: 'Enable',\n    config: {\n        displayName: '',\n        sender: '',\n        userName: '',\n        password: '',\n        host: '',\n        port: 25,\n        encryption: 'NONE',\n        status: '',\n        recipient: '',\n    },\n};\nconst emailConfig = ref<Alert.EmailConfig>({ ...defaultEmailConfig });\n\nconst defaultCommonConfig: Alert.CommonAlertConfig = {\n    id: undefined,\n    type: 'common',\n    title: 'xpack.alert.commonConfig',\n    status: 'Enable',\n    config: {\n        alertSendTimeRange:\n            i18n.global.t('xpack.alert.noticeAlert') +\n            ': ' +\n            '08:00:00 - 23:59:59' +\n            ' | ' +\n            i18n.global.t('xpack.alert.resourceAlert') +\n            ': ' +\n            '00:00:00 - 23:59:59',\n        isOffline: 'Disable',\n    },\n};\n\nconst commonConfig = ref<Alert.CommonAlertConfig>({ ...defaultCommonConfig });\n\nconst defaultSmsConfig: Alert.SmsConfig = {\n    id: undefined,\n    type: 'sms',\n    title: 'xpack.alert.smsConfig',\n    status: 'Enable',\n    config: {\n        phone: '',\n        alertDailyNum: 50,\n    },\n};\nconst smsConfig = ref<Alert.SmsConfig>({ ...defaultSmsConfig });\n\nconst defaultWeComConfig: Alert.WebhookConfig = {\n    id: undefined,\n    type: 'weCom',\n    title: 'xpack.alert.weCom',\n    status: 'Enable',\n    config: {\n        displayName: '',\n        url: '',\n    },\n};\nconst weComConfig = ref<Alert.WebhookConfig>({ ...defaultWeComConfig });\n\nconst defaultDingTalkConfig: Alert.WebhookConfig = {\n    id: undefined,\n    type: 'dingTalk',\n    title: 'xpack.alert.dingTalk',\n    status: 'Enable',\n    config: {\n        displayName: '',\n        url: '',\n    },\n};\nconst dingTalkConfig = ref<Alert.WebhookConfig>({ ...defaultDingTalkConfig });\n\nconst defaultFeiShuConfig: Alert.WebhookConfig = {\n    id: undefined,\n    type: 'feiShu',\n    title: 'xpack.alert.feiShu',\n    status: 'Enable',\n    config: {\n        displayName: '',\n        url: '',\n    },\n};\nconst feiShuConfig = ref<Alert.WebhookConfig>({ ...defaultFeiShuConfig });\n\nconst weComUrlVisible = ref(false);\nconst dingTalkUrlVisible = ref(false);\nconst feiShuUrlVisible = ref(false);\n\nconst config = ref<Alert.AlertConfigInfo>({\n    id: 0,\n    type: '',\n    title: '',\n    status: '',\n    config: '',\n});\nconst licenseName = ref('-');\nconst totalSms = ref(0);\nconst usedSms = ref(0);\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nfunction parseConfig<T extends object>(raw: any, fallback: T): T {\n    try {\n        const parsed = JSON.parse(raw.config || '{}');\n        return {\n            ...fallback,\n            ...parsed,\n        };\n    } catch (err) {\n        return { ...fallback };\n    }\n}\n\nfunction assignConfig<T extends { config: any }>(raw: any, target: Ref<T>, fallback: T) {\n    if (raw) {\n        target.value = {\n            ...(fallback as any),\n            id: raw.id,\n            type: raw.type,\n            title: raw.title,\n            status: raw.status,\n            config: parseConfig(raw, fallback.config),\n        };\n    } else {\n        target.value = { ...fallback };\n    }\n}\n\nconst search = async () => {\n    loading.value = true;\n    try {\n        const res = await ListAlertConfigs();\n        const emailFound = res.data.find((s: any) => s.type === 'email');\n        assignConfig(emailFound, emailConfig, defaultEmailConfig);\n\n        const commonFound = res.data.find((s: any) => s.type === 'common');\n        assignConfig(commonFound, commonConfig, defaultCommonConfig);\n\n        const smsFound = res.data.find((s: any) => s.type === 'sms');\n        assignConfig(smsFound, smsConfig, defaultSmsConfig);\n        sendTimeRangeValue.value = commonConfig.value.config.alertSendTimeRange;\n        const noticeTimeRange = sendTimeRangeValue.value.noticeAlert.sendTimeRange || '08:00:00 - 23:59:59';\n        const resourceTimeRange = sendTimeRangeValue.value.resourceAlert.sendTimeRange || '00:00:00 - 23:59:59';\n        sendTimeRange.value =\n            i18n.global.t('xpack.alert.noticeAlert') +\n            ': ' +\n            noticeTimeRange +\n            ' | ' +\n            i18n.global.t('xpack.alert.resourceAlert') +\n            ': ' +\n            resourceTimeRange;\n\n        const weComFound = res.data.find((s: any) => s.type === 'weCom');\n        assignConfig(weComFound, weComConfig, defaultWeComConfig);\n\n        const dingTalkFound = res.data.find((s: any) => s.type === 'dingTalk');\n        assignConfig(dingTalkFound, dingTalkConfig, defaultDingTalkConfig);\n\n        const feiShuFound = res.data.find((s: any) => s.type === 'feiShu');\n        assignConfig(feiShuFound, feiShuConfig, defaultFeiShuConfig);\n        isInitialized.value = true;\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst onChangePhone = (id: any) => {\n    phoneRef.value.acceptParams({\n        id: id,\n        phone: smsConfig.value.config.phone,\n        dailyAlertNum: smsConfig.value.config.alertDailyNum,\n    });\n};\n\nconst onChangeCommon = (id: any) => {\n    sendTimeRangeRef.value.acceptParams({\n        id: id,\n        sendTimeRange: sendTimeRangeValue.value,\n        isOffline: commonConfig.value.config.isOffline,\n    });\n};\n\nconst onChangeEmail = (id: number) => {\n    emailRef.value.acceptParams({ id: id, config: emailConfig.value.config });\n};\n\nconst onChangeOffline = async () => {\n    if (!isInitialized.value) return;\n    if (!isMaster.value && commonConfig.value.config.isOffline != '') {\n        let title =\n            commonConfig.value.config.isOffline == 'Enable'\n                ? i18n.global.t('xpack.alert.offlineOff')\n                : i18n.global.t('xpack.alert.offlineClose');\n        let content =\n            commonConfig.value.config.isOffline == 'Enable'\n                ? i18n.global.t('xpack.alert.offlineOffHelper')\n                : i18n.global.t('xpack.alert.offlineCloseHelper');\n        ElMessageBox.confirm(content, title, {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        })\n            .then(async () => {\n                loading.value = true;\n                try {\n                    config.value.id = commonConfig.value.id;\n                    config.value.type = 'common';\n                    config.value.title = 'xpack.alert.commonConfig';\n                    config.value.status = 'Enable';\n                    config.value.config = JSON.stringify(commonConfig.value.config);\n                    await UpdateAlertConfig(config.value);\n                    loading.value = false;\n                    await search();\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                } catch (error) {\n                    loading.value = false;\n                }\n            })\n            .catch(() => {\n                commonConfig.value.config.isOffline =\n                    commonConfig.value.config.isOffline == 'Enable' ? 'Disable' : 'Enable';\n            });\n    }\n};\n\nconst onDelete = (id: number) => {\n    ElMessageBox.confirm(i18n.global.t('xpack.alert.deleteConfigMsg'), i18n.global.t('xpack.alert.deleteConfigTitle'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await DeleteAlertConfig({ id: id });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        await search();\n    });\n};\n\nconst getSmsInfo = async () => {\n    const res = await getLicenseSmsInfo();\n    licenseName.value = res.data.licenseName;\n    usedSms.value = res.data.smsUsed;\n    totalSms.value = res.data.smsTotal;\n};\n\nconst goBuy = async () => {\n    const uri = licenseName.value === '-' ? '' : `${licenseName.value}/buy-sms`;\n    window.open('https://www.lxware.cn/uc/cloud/licenses/' + uri, '_blank', 'noopener,noreferrer');\n};\n\nconst onChangeWeCom = (id: number) => {\n    webHookRef.value.acceptParams({\n        id: id,\n        config: weComConfig.value.config,\n        type: 'weCom',\n        title: weComConfig.value.title,\n    });\n};\n\nconst onChangeDingTalk = (id: number) => {\n    webHookRef.value.acceptParams({\n        id: id,\n        config: dingTalkConfig.value.config,\n        type: 'dingTalk',\n        title: dingTalkConfig.value.title,\n    });\n};\n\nconst onChangeFeiShu = (id: number) => {\n    webHookRef.value.acceptParams({\n        id: id,\n        config: feiShuConfig.value.config,\n        type: 'feiShu',\n        title: feiShuConfig.value.title,\n    });\n};\n\nonMounted(async () => {\n    await search();\n    if (globalStore.isProductPro && !globalStore.isIntl) {\n        await getSmsInfo();\n    }\n});\n</script>\n<style scoped lang=\"scss\">\n.app {\n    .el-card {\n        padding: 0 !important;\n        border: var(--panel-border) !important;\n\n        &:hover {\n            border: 1px solid var(--el-color-primary) !important;\n        }\n    }\n}\n.label {\n    color: var(--el-text-color-placeholder);\n}\n.config-form {\n    .el-form-item {\n        margin-bottom: 0 !important;\n    }\n    height: 257px;\n}\n.webhook-field {\n    display: inline-flex;\n    align-items: center;\n    gap: 6px;\n    max-width: 100%;\n}\n.webhook-text {\n    max-width: 100%;\n    word-break: break-all;\n    white-space: normal;\n}\n.webhook-icon {\n    cursor: pointer;\n    color: var(--el-text-color-secondary);\n}\n.webhook-icon:hover {\n    color: var(--el-color-primary);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/setting/phone/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('xpack.alert.phone')\" @close=\"handleClose\" size=\"736\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-row type=\"flex\" justify=\"center\">\n                <el-col :span=\"22\">\n                    <el-form-item :label=\"$t('xpack.alert.phone')\" :rules=\"[Rules.phone]\" prop=\"phone\">\n                        <el-input clearable v-model=\"form.phone\" />\n                        <span class=\"input-help\">{{ $t('xpack.alert.phoneHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item\n                        :label=\"$t('xpack.alert.dailyAlertNum')\"\n                        :rules=\"[Rules.integerNumber, checkNumberRange(20, 100)]\"\n                        prop=\"dailyAlertNum\"\n                    >\n                        <el-input clearable v-model.number=\"form.dailyAlertNum\" min=\"20\" max=\"100\" />\n                        <span class=\"input-help\">{{ $t('xpack.alert.dailyAlertNumHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport { UpdateAlertConfig } from '@/api/modules/alert';\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    phone: string;\n    dailyAlertNum: number;\n    id: number;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    phone: '',\n    dailyAlertNum: 50,\n    id: undefined,\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.phone = params.phone;\n    form.id = params.id;\n    form.dailyAlertNum = Number(params.dailyAlertNum);\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        try {\n            const configInfo = { phone: form.phone, alertDailyNum: form.dailyAlertNum };\n            await UpdateAlertConfig({\n                id: form.id,\n                type: 'sms',\n                title: 'xpack.alert.smsConfig',\n                status: 'Enable',\n                config: JSON.stringify(configInfo),\n            });\n\n            loading.value = false;\n            handleClose();\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        } catch (error) {\n            loading.value = false;\n        }\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/setting/time-range/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('xpack.alert.sendTimeRange')\" @close=\"handleClose\" size=\"736\">\n        <el-form ref=\"formRef\" label-position=\"top\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('xpack.alert.sendTimeRange')\" prop=\"sendTimeRange\">\n                <div class=\"text-center\">\n                    <el-transfer\n                        v-model=\"resourceValue\"\n                        class=\"text-left inline-block\"\n                        :titles=\"[$t('xpack.alert.noticeAlert'), $t('xpack.alert.resourceAlert')]\"\n                        :format=\"{\n                            noChecked: '${total}',\n                            hasChecked: '${checked}/${total}',\n                        }\"\n                        :data=\"data\"\n                    >\n                        <template #left-footer>\n                            <el-form-item prop=\"noticeTimeRange\">\n                                <el-time-picker\n                                    v-model=\"noticeTimeRange\"\n                                    class=\"!w-[235px] mx-1 mt-1\"\n                                    is-range\n                                    :start-placeholder=\"$t('commons.search.timeStart')\"\n                                    :end-placeholder=\"$t('commons.search.timeEnd')\"\n                                />\n                                <span class=\"input-help ml-2\">\n                                    {{\n                                        $t('xpack.alert.sendTimeRangeHelper', [\n                                            $t('xpack.alert.noticeAlert').toLowerCase(),\n                                        ])\n                                    }}\n                                </span>\n                            </el-form-item>\n                        </template>\n                        <template #right-footer>\n                            <el-form-item prop=\"resourceTimeRange\">\n                                <el-time-picker\n                                    v-model=\"resourceTimeRange\"\n                                    class=\"!w-[235px] mx-1 mt-1\"\n                                    is-range\n                                    :start-placeholder=\"$t('commons.search.timeStart')\"\n                                    :end-placeholder=\"$t('commons.search.timeEnd')\"\n                                />\n                                <span class=\"input-help ml-2\">\n                                    {{\n                                        $t('xpack.alert.sendTimeRangeHelper', [\n                                            $t('xpack.alert.resourceAlert').toLowerCase(),\n                                        ])\n                                    }}\n                                </span>\n                            </el-form-item>\n                        </template>\n                    </el-transfer>\n                </div>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave()\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { computed, ComputedRef, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { UpdateAlertConfig } from '@/api/modules/alert';\nimport { Alert } from '@/api/interface/alert';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface Option {\n    key: string;\n    label: string;\n    disabled: boolean;\n}\n\ninterface NoticeAlert {\n    sendTimeRange: string;\n    type: string[];\n}\n\ninterface ResourceAlert {\n    sendTimeRange: string;\n    type: string[];\n}\n\ninterface SendTimeRange {\n    noticeAlert: NoticeAlert;\n    resourceAlert: ResourceAlert;\n}\n\ninterface DialogProps {\n    id: any;\n    sendTimeRange: SendTimeRange;\n    isOffline: string;\n}\n\ninterface ConfigInfo {\n    alertSendTimeRange: SendTimeRange;\n    isOffline: string;\n}\n\nconst drawerVisible = ref(false);\nconst loading = ref(false);\nconst isOffline = ref();\nconst id = ref();\nconst configInfo = ref<ConfigInfo>({\n    isOffline: '',\n    alertSendTimeRange: {\n        noticeAlert: { sendTimeRange: '', type: [] },\n        resourceAlert: { sendTimeRange: '', type: [] },\n    },\n});\nconst config = ref<Alert.AlertConfigInfo>({\n    id: 0,\n    type: '',\n    title: '',\n    status: '',\n    config: '',\n});\nconst resourceValue = ref([\n    'clams',\n    'cronJob',\n    'cpu',\n    'memory',\n    'load',\n    'disk',\n    'nodeException',\n    'licenseException',\n    'panelLogin',\n    'sshLogin',\n]);\nconst noticeDefaultTime: [Date, Date] = [new Date(0, 0, 1, 8, 0, 0), new Date(0, 0, 1, 23, 59, 59)];\nconst resourceDefaultTime: [Date, Date] = [new Date(0, 0, 1, 0, 0, 0), new Date(0, 0, 1, 23, 59, 59)];\nconst noticeTimeRange = ref(noticeDefaultTime);\nconst resourceTimeRange = ref(resourceDefaultTime);\nconst generateData = (): Option[] => {\n    const data: Option[] = [];\n    data.push({ key: 'panelPwdEndTime', label: i18n.global.t('xpack.alert.panelPwdEndTime'), disabled: false });\n    data.push({ key: 'panelLogin', label: i18n.global.t('xpack.alert.panelLogin'), disabled: false });\n    data.push({ key: 'sshLogin', label: i18n.global.t('xpack.alert.sshLogin'), disabled: false });\n    data.push({ key: 'licenseException', label: i18n.global.t('xpack.alert.licenseException'), disabled: false });\n    data.push({ key: 'nodeException', label: i18n.global.t('xpack.alert.nodeException'), disabled: false });\n    data.push({ key: 'ssl', label: i18n.global.t('xpack.alert.ssl'), disabled: false });\n    data.push({ key: 'siteEndTime', label: i18n.global.t('xpack.alert.siteEndTime'), disabled: false });\n    data.push({ key: 'cpu', label: i18n.global.t('xpack.alert.cpu'), disabled: false });\n    data.push({ key: 'memory', label: i18n.global.t('xpack.alert.memory'), disabled: false });\n    data.push({ key: 'disk', label: i18n.global.t('xpack.alert.disk'), disabled: false });\n    data.push({ key: 'load', label: i18n.global.t('xpack.alert.load'), disabled: false });\n    data.push({ key: 'clams', label: i18n.global.t('xpack.alert.clams'), disabled: false });\n    data.push({ key: 'cronJob', label: i18n.global.t('xpack.alert.cronjob'), disabled: false });\n    data.push({ key: 'panelUpdate', label: i18n.global.t('xpack.alert.panelUpdate'), disabled: false });\n    return data;\n};\n\nconst data = ref(generateData());\nconst formRef = ref<FormInstance>();\nconst noticeValue: ComputedRef<string[]> = computed(() => {\n    return data.value.filter((item) => !resourceValue.value.includes(item.key)).map((item) => item.key);\n});\n\nconst acceptParams = (params: DialogProps): void => {\n    if (typeof params.sendTimeRange === 'object' && params.sendTimeRange !== null) {\n        noticeTimeRange.value = parseTimeRange(params.sendTimeRange.noticeAlert.sendTimeRange);\n        resourceTimeRange.value = parseTimeRange(params.sendTimeRange.resourceAlert.sendTimeRange);\n        resourceValue.value = params.sendTimeRange.resourceAlert.type;\n    }\n    isOffline.value = params.isOffline;\n    id.value = params.id;\n    drawerVisible.value = true;\n};\n\nconst onSave = async () => {\n    if (\n        typeof noticeTimeRange.value === 'object' &&\n        noticeTimeRange.value !== null &&\n        typeof resourceTimeRange.value === 'object' &&\n        resourceTimeRange.value !== null\n    ) {\n        loading.value = true;\n        configInfo.value.alertSendTimeRange = {\n            noticeAlert: { sendTimeRange: stringifyTimeRange(noticeTimeRange.value), type: noticeValue.value },\n            resourceAlert: {\n                sendTimeRange: stringifyTimeRange(resourceTimeRange.value),\n                type: resourceValue.value,\n            },\n        };\n        configInfo.value.isOffline = isOffline.value;\n        try {\n            config.value.id = id.value;\n            config.value.type = 'common';\n            config.value.title = 'xpack.alert.commonConfig';\n            config.value.status = 'Enable';\n            config.value.config = JSON.stringify(configInfo.value);\n            await UpdateAlertConfig(config.value);\n\n            loading.value = false;\n            handleClose();\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        } catch (error) {\n            loading.value = false;\n        }\n    } else {\n        MsgError(i18n.global.t('commons.msg.confirmNoNull', [i18n.global.t('xpack.alert.timeRange').toLowerCase()]));\n    }\n};\n\nconst parseTimeRange = (timeRangeStr: string): [Date, Date] => {\n    return timeRangeStr.split(' - ').map((time) => {\n        const [hours, minutes, seconds] = time.split(':').map(Number);\n        return new Date(0, 0, 1, hours, minutes, seconds);\n    }) as [Date, Date];\n};\n\nconst stringifyTimeRange = (timeRange: [Date, Date]): string => {\n    return timeRange\n        .map(\n            (date) =>\n                `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(\n                    date.getSeconds(),\n                ).padStart(2, '0')}`,\n        )\n        .join(' - ');\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n:deep(.el-transfer) {\n    --el-transfer-panel-width: 265px;\n}\n\n:deep(.el-transfer__buttons) {\n    padding: 10px 30px;\n    @media (max-width: 600px) {\n        width: 265px;\n        text-align: center;\n        padding: 10px 0;\n        .el-button [class*='el-icon'] svg {\n            transform: rotate(90deg);\n        }\n    }\n\n    @media (min-width: 601px) {\n        display: inline-flex;\n        flex-direction: column;\n        align-items: center;\n        gap: 10px;\n        width: 40px;\n        height: 40px;\n        justify-content: center;\n        .el-button + .el-button {\n            margin-left: 0;\n        }\n    }\n}\n\n:deep(.el-transfer-panel .el-transfer-panel__footer) {\n    height: 65px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/alert/setting/webhook/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('xpack.alert.' + form.type)\" @close=\"handleClose\" size=\"736\">\n        <el-form\n            ref=\"formRef\"\n            :rules=\"rules\"\n            label-position=\"top\"\n            :model=\"form.config\"\n            @submit.prevent\n            v-loading=\"loading\"\n        >\n            <el-row type=\"flex\" justify=\"center\">\n                <el-col :span=\"22\">\n                    <el-form-item :label=\"$t('xpack.alert.webhookName')\" prop=\"displayName\">\n                        <el-input v-model=\"form.config.displayName\" />\n                    </el-form-item>\n                    <el-form-item :label=\"$t('xpack.alert.webhookUrl')\" prop=\"url\">\n                        <el-input v-model.trim=\"form.config.url\" :rows=\"2\" type=\"password\" show-password />\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { UpdateAlertConfig } from '@/api/modules/alert';\nimport { Rules } from '@/global/form-rules';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst rules = {\n    displayName: [Rules.requiredInput],\n    url: [Rules.requiredInput],\n};\ninterface Config {\n    displayName: string;\n    url: string;\n}\n\ninterface DialogProps {\n    id: number;\n    type: string;\n    title: string;\n    config: Config;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    id: undefined,\n    type: '',\n    title: '',\n    config: {\n        displayName: '',\n        url: '',\n    },\n});\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.id = params.id;\n    form.type = params.type;\n    form.title = params.title;\n    form.config = params.config;\n    form.config = { ...params.config };\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        try {\n            const configInfo = form.config;\n            await UpdateAlertConfig({\n                id: form.id,\n                type: form.type,\n                title: form.title,\n                status: 'Enable',\n                config: JSON.stringify(configInfo),\n            });\n\n            loading.value = false;\n            handleClose();\n            emit('search');\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        } catch (error) {\n            loading.value = false;\n        }\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/backup-account/helper.ts",
    "content": "import i18n from '@/lang';\n\nexport const cities = [\n    { value: 'ap-beijing-1', label: i18n.global.t('setting.ap_beijing_1') },\n    { value: 'ap-beijing', label: i18n.global.t('setting.ap_beijing') },\n    { value: 'ap-nanjing', label: i18n.global.t('setting.ap_nanjing') },\n    { value: 'ap-shanghai', label: i18n.global.t('setting.ap_shanghai') },\n    { value: 'ap-guangzhou', label: i18n.global.t('setting.ap_guangzhou') },\n    { value: 'ap-chengdu', label: i18n.global.t('setting.ap_chengdu') },\n    { value: 'ap-chongqing', label: i18n.global.t('setting.ap_chongqing') },\n    { value: 'ap-shenzhen_fsi', label: i18n.global.t('setting.ap_shenzhen_fsi') },\n    { value: 'ap-shanghai_fsi', label: i18n.global.t('setting.ap_shanghai_fsi') },\n    { value: 'ap-beijing_fsi', label: i18n.global.t('setting.ap_beijing_fsi') },\n    { value: 'ap-hongkong', label: i18n.global.t('setting.ap_hongkong') },\n    { value: 'ap-singapore', label: i18n.global.t('setting.ap_singapore') },\n    { value: 'ap-mumbai', label: i18n.global.t('setting.ap_mumbai') },\n    { value: 'ap-jakarta', label: i18n.global.t('setting.ap_jakarta') },\n    { value: 'ap-seoul', label: i18n.global.t('setting.ap_seoul') },\n    { value: 'ap-bangkok', label: i18n.global.t('setting.ap_bangkok') },\n    { value: 'ap-tokyo', label: i18n.global.t('setting.ap_tokyo') },\n    { value: 'na-siliconvalley', label: i18n.global.t('setting.na_siliconvalley') },\n    { value: 'na-ashburn', label: i18n.global.t('setting.na_ashburn') },\n    { value: 'na-toronto', label: i18n.global.t('setting.na_toronto') },\n    { value: 'sa-saopaulo', label: i18n.global.t('setting.sa_saopaulo') },\n    { value: 'eu-frankfurt', label: i18n.global.t('setting.eu_frankfurt') },\n];\n"
  },
  {
    "path": "frontend/src/views/setting/backup-account/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent :title=\"$t('setting.backupAccount')\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                    {{ $t('commons.button.add') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"paginationConfig.name\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"backup-account-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <el-alert type=\"info\" :closable=\"false\" class=\"common-div\" v-if=\"!globalStore.isFxplay\">\n                    <template #title>\n                        <span>\n                            {{ $t('setting.backupAlert') }}\n                            <el-link\n                                class=\"ml-1 text-xs\"\n                                type=\"primary\"\n                                target=\"_blank\"\n                                :href=\"globalStore.docsUrl + '/user_manual/settings/#4'\"\n                            >\n                                {{ $t('commons.button.helpDoc') }}\n                            </el-link>\n                        </span>\n                    </template>\n                </el-alert>\n                <ComplexTable :pagination-config=\"paginationConfig\" @sort-change=\"search\" @search=\"search\" :data=\"data\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        :min-width=\"80\"\n                        prop=\"name\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"onInspect(row)\">\n                                {{ row.name === 'localhost' ? $t('terminal.local') : row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        v-if=\"globalStore.isProductPro\"\n                        :label=\"$t('setting.scope')\"\n                        :min-width=\"80\"\n                        prop=\"isPublic\"\n                    >\n                        <template #default=\"{ row }\">\n                            <el-button plain size=\"small\">\n                                {{ row.isPublic ? $t('setting.public') : $t('setting.private') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.type')\" :min-width=\"100\" prop=\"type\">\n                        <template #default=\"{ row }\">\n                            <el-tag>{{ $t('setting.' + row.type) }}</el-tag>\n                            <el-tooltip v-if=\"hasTokenRefresh(row)\">\n                                <template #content>\n                                    {{ $t('setting.clickToRefresh') }}\n                                    <br />\n                                    <span v-if=\"row.varsJson['refresh_status'] === 'Success'\">\n                                        {{ $t('setting.refreshStatus') + ':' + $t('commons.status.success') }}\n                                    </span>\n                                    <div v-else>\n                                        <span>\n                                            {{ $t('setting.refreshStatus') + ':' + $t('commons.status.failed') }}\n                                        </span>\n                                        <br />\n                                        <span>\n                                            {{ $t('commons.table.message') + ':' + row.varsJson['refresh_msg'] }}\n                                        </span>\n                                    </div>\n                                    <br />\n                                    {{ $t('setting.refreshTime') + ':' + row.varsJson['refresh_time'] }}\n                                </template>\n                                <el-button\n                                    type=\"primary\"\n                                    link\n                                    icon=\"Refresh\"\n                                    @click=\"refreshItemToken(row)\"\n                                    class=\"ml-1\"\n                                />\n                            </el-tooltip>\n                        </template>\n                    </el-table-column>\n                    <el-table-column prop=\"bucket\" label=\"Bucket\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            {{ row.bucket || '-' }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column prop=\"endpoint\" label=\"Endpoint\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            {{ loadEndpoint(row) }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column prop=\"backupPath\" :label=\"$t('setting.backupDir')\" show-overflow-tooltip />\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        width=\"300px\"\n                        :buttons=\"buttons\"\n                        :ellipsis=\"10\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <Operate ref=\"dialogRef\" @search=\"search\" />\n        <DetailShow ref=\"detailRef\" />\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { dateFormat } from '@/utils/util';\nimport { onMounted, ref } from 'vue';\nimport { searchBackup, deleteBackup, refreshToken } from '@/api/modules/backup';\nimport Operate from '@/views/setting/backup-account/operate/index.vue';\nimport DetailShow from '@/components/detail-show/index.vue';\nimport { Backup } from '@/api/interface/backup';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nimport { Base64 } from 'js-base64';\nconst globalStore = GlobalStore();\n\nconst loading = ref();\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'backup-account-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('backup-account-size')) || 20,\n    total: 0,\n    type: '',\n    name: '',\n});\nconst opRef = ref();\nconst dialogRef = ref();\nconst detailRef = ref();\n\nconst search = async () => {\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        type: paginationConfig.type,\n        name: paginationConfig.name,\n    };\n    loading.value = true;\n    await searchBackup(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            for (const bac of data.value) {\n                if (bac.vars) {\n                    bac.varsJson = JSON.parse(bac.vars);\n                }\n            }\n            data.value.sort((a, b) => {\n                if (a.name === 'localhost') return -1;\n                if (b.name === 'localhost') return 1;\n                return 0;\n            });\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadEndpoint = (row: any) => {\n    if (row.type === 'COS' || row.type === 'MINIO' || row.type === 'OSS' || row.type === 'S3') {\n        return row.varsJson['endpoint'];\n    }\n    if (row.type === 'KODO') {\n        return row.varsJson['domain'];\n    }\n    return '';\n};\n\nconst hasTokenRefresh = (row: any) => {\n    return row.type === 'OneDrive' || row.type === 'ALIYUN';\n};\n\nconst onDelete = async (row: Backup.BackupInfo) => {\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: ['[ ' + row.type + ' ] ' + row.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('setting.backupAccount'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteBackup,\n        params: { id: row.id, name: row.name, isPublic: row.isPublic },\n    });\n};\n\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Backup.BackupInfo> = {\n        id: 0,\n        isPublic: false,\n        varsJson: {},\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onInspect = (row: any) => {\n    let list = [];\n    list.push({ label: i18n.global.t('commons.table.name'), value: row.name, hideCopy: true });\n    list.push({\n        label: i18n.global.t('commons.table.type'),\n        value: i18n.global.t('setting.' + row.type),\n        hideCopy: true,\n    });\n    if (row.type === 'S3') {\n        list.push({ label: i18n.global.t('setting.mode'), value: row.varsJson['mode'] });\n    }\n    if (row.type === 'COS' || row.type === 'KODO' || row.type === 'MINIO' || row.type === 'OSS' || row.type === 'S3') {\n        if (row.rememberAuth) {\n            list.push({ label: 'Access Key ID', value: Base64.decode(row.accessKey) });\n            list.push({ label: 'Secret Key', value: Base64.decode(row.credential) });\n        }\n    }\n    if (row.type === 'UPYUN') {\n        if (row.rememberAuth) {\n            list.push({ label: i18n.global.t('setting.operator'), value: Base64.decode(row.accessKey) });\n            list.push({ label: i18n.global.t('commons.login.password'), value: Base64.decode(row.credential) });\n        }\n    }\n    if (row.type === 'WebDAV' || row.type === 'SFTP') {\n        list.push({ label: i18n.global.t('setting.address'), value: row.varsJson['address'] || '' });\n        list.push({ label: i18n.global.t('commons.login.username'), value: Base64.decode(row.accessKey) });\n    }\n    if (row.type === 'SFTP') {\n        list.push({ label: i18n.global.t('commons.table.port'), value: row.varsJson['port'] || '' });\n        if (row.rememberAuth) {\n            list.push({ label: i18n.global.t('terminal.authMode'), value: row.varsJson['authMode'] });\n            if (row.varsJson['authMode'] === 'key') {\n                list.push({ label: i18n.global.t('terminal.key'), value: Base64.decode(row.credential) });\n                list.push({ label: i18n.global.t('terminal.keyPassword'), value: row.varsJson['passPhrase'] });\n            } else {\n                list.push({ label: i18n.global.t('commons.login.password'), value: Base64.decode(row.credential) });\n            }\n        }\n    }\n    if (row.type === 'COS' || row.type === 'S3') {\n        list.push({ label: 'Region', value: row.varsJson['region'] || '' });\n    }\n    if (row.type === 'COS' || row.type === 'KODO' || row.type === 'MINIO' || row.type === 'OSS' || row.type === 'S3') {\n        list.push({\n            label: row.type === 'KODO' ? i18n.global.t('setting.domain') : 'Endpoint',\n            value: row.varsJson['endpoint'] || '',\n        });\n        list.push({ label: 'Bucket', value: row.bucket });\n    }\n    if (row.type === 'UPYUN') {\n        list.push({ label: i18n.global.t('setting.serviceName'), value: row.bucket });\n    }\n    if (row.type === 'COS' || row.type === 'OOS' || row.type === 'S3') {\n        list.push({ label: i18n.global.t('setting.scType'), value: row.varsJson['scType'] });\n    }\n    if (row.type === 'KODO') {\n        list.push({ label: i18n.global.t('cronjob.requestExpirationTime'), value: row.varsJson['timeout'] });\n    }\n    list.push({ label: i18n.global.t('setting.backupLabel'), value: row.backupPath });\n    detailRef.value.acceptParams({ list: list });\n};\n\nconst refreshItemToken = async (row: any) => {\n    loading.value = true;\n    await refreshToken({ id: row.id, name: row.name, isPublic: row.isPublic })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Backup.BackupInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        disabled: (row: Backup.BackupInfo) => {\n            return row.type === 'LOCAL';\n        },\n        click: (row: Backup.BackupInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/backup-account/operate/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"title + $t('setting.backupAccount')\" @close=\"handleClose\" size=\"large\">\n        <el-form @submit.prevent ref=\"formRef\" v-loading=\"loading\" label-position=\"top\" :model=\"dialogData.rowData\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\" :rules=\"Rules.requiredInput\">\n                <el-tag v-if=\"dialogData.title === 'edit'\">\n                    {{ dialogData.rowData!.name === 'localhost' ? $t('terminal.local') : dialogData.rowData!.name }}\n                </el-tag>\n                <el-input v-else v-model=\"dialogData.rowData!.name\" />\n            </el-form-item>\n            <el-form-item\n                v-if=\"globalStore.isProductPro\"\n                :label=\"$t('setting.scope')\"\n                prop=\"isPublic\"\n                :rules=\"Rules.requiredSelect\"\n            >\n                <el-tag v-if=\"dialogData.title === 'edit'\">\n                    {{ dialogData.rowData!.isPublic ? $t('setting.public') : $t('setting.private') }}\n                </el-tag>\n                <el-radio-group v-else v-model=\"dialogData.rowData!.isPublic\">\n                    <el-radio :value=\"true\" size=\"large\">{{ $t('setting.public') }}</el-radio>\n                    <el-radio :value=\"false\" size=\"large\">{{ $t('setting.private') }}</el-radio>\n                </el-radio-group>\n                <span class=\"input-help\">\n                    {{ dialogData.rowData!.isPublic ? $t('setting.publicHelper') : $t('setting.privateHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.type')\" prop=\"type\" :rules=\"Rules.requiredSelect\">\n                <el-tag v-if=\"dialogData.title === 'edit'\">{{ $t('setting.' + dialogData.rowData!.type) }}</el-tag>\n                <el-select v-else v-model=\"dialogData.rowData!.type\" @change=\"changeType\">\n                    <el-option :label=\"$t('setting.OSS')\" value=\"OSS\"></el-option>\n                    <el-option :label=\"$t('setting.COS')\" value=\"COS\"></el-option>\n                    <el-option :label=\"$t('setting.S3')\" value=\"S3\"></el-option>\n                    <el-option :label=\"$t('setting.OneDrive')\" value=\"OneDrive\"></el-option>\n                    <el-option :label=\"$t('setting.GoogleDrive')\" value=\"GoogleDrive\"></el-option>\n                    <el-option :label=\"$t('setting.ALIYUN')\" value=\"ALIYUN\"></el-option>\n                    <el-option :label=\"$t('setting.MINIO')\" value=\"MINIO\"></el-option>\n                    <el-option :label=\"$t('setting.WebDAV')\" value=\"WebDAV\"></el-option>\n                    <el-option :label=\"$t('setting.SFTP')\" value=\"SFTP\"></el-option>\n                    <el-option :label=\"$t('setting.KODO')\" value=\"KODO\"></el-option>\n                    <el-option :label=\"$t('setting.UPYUN')\" value=\"UPYUN\"></el-option>\n                </el-select>\n                <span v-if=\"isALIYUNYUN()\" class=\"input-help\">{{ $t('setting.ALIYUNHelper') }}</span>\n                <span v-if=\"dialogData.rowData?.type === 'GoogleDrive' && !globalStore.isFxplay\" class=\"input-help\">\n                    {{ $t('setting.googleHelper', [$t('setting.' + dialogData.rowData?.type)]) }}\n                    <el-link\n                        style=\"font-size: 12px; margin-left: 5px\"\n                        icon=\"Position\"\n                        @click=\"toDoc('google-drive')\"\n                        type=\"primary\"\n                    >\n                        {{ $t('firewall.quickJump') }}\n                    </el-link>\n                </span>\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'S3'\"\n                :label=\"$t('setting.mode')\"\n                prop=\"varsJson.mode\"\n                :rules=\"Rules.requiredSelect\"\n            >\n                <el-radio-group v-model=\"dialogData.rowData!.varsJson['mode']\">\n                    <el-radio value=\"virtual hosted\">Virtual Hosted</el-radio>\n                    <el-radio value=\"path\">Path</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item v-if=\"hasAccessKey()\" label=\"Access Key ID\" prop=\"accessKey\" :rules=\"Rules.requiredInput\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.accessKey\" />\n            </el-form-item>\n            <el-form-item v-if=\"hasAccessKey()\" label=\"Secret Key\" prop=\"credential\" :rules=\"Rules.requiredInput\">\n                <el-input show-password clearable v-model.trim=\"dialogData.rowData!.credential\" />\n            </el-form-item>\n            <div v-if=\"isUPYUN()\">\n                <el-form-item :label=\"$t('setting.operator')\" prop=\"accessKey\" :rules=\"Rules.requiredInput\">\n                    <el-input clearable v-model.trim=\"dialogData.rowData!.accessKey\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.password')\" prop=\"credential\" :rules=\"Rules.requiredInput\">\n                    <el-input show-password clearable v-model.trim=\"dialogData.rowData!.credential\" />\n                </el-form-item>\n            </div>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'WebDAV'\"\n                :label=\"$t('setting.address')\"\n                prop=\"varsJson.address\"\n                :rules=\"Rules.requiredInput\"\n            >\n                <el-input v-model=\"dialogData.rowData!.varsJson['address']\" />\n                <span class=\"input-help\" v-if=\"!globalStore.isFxplay\">\n                    {{ $t('setting.WebDAVAlist') }}\n                    <el-link\n                        style=\"font-size: 12px; margin-left: 5px\"\n                        icon=\"Position\"\n                        @click=\"toDoc('webdav')\"\n                        type=\"primary\"\n                    >\n                        {{ $t('firewall.quickJump') }}\n                    </el-link>\n                </span>\n            </el-form-item>\n            <div v-if=\"dialogData.rowData!.type === 'SFTP'\">\n                <el-form-item :label=\"$t('setting.address')\" prop=\"varsJson.address\" :rules=\"Rules.host\">\n                    <el-input v-model.trim=\"dialogData.rowData!.varsJson['address']\" clearable />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.table.port')\" prop=\"varsJson.port\" :rules=\"[Rules.port]\">\n                    <el-input-number :min=\"0\" :max=\"65535\" v-model.number=\"dialogData.rowData!.varsJson['port']\" />\n                </el-form-item>\n            </div>\n            <div v-if=\"hasPassword()\">\n                <el-form-item :label=\"$t('commons.login.username')\" prop=\"accessKey\" :rules=\"[Rules.requiredInput]\">\n                    <el-input v-model.trim=\"dialogData.rowData!.accessKey\" />\n                </el-form-item>\n\n                <div v-if=\"dialogData.rowData!.type === 'SFTP'\">\n                    <el-form-item :label=\"$t('terminal.authMode')\" prop=\"varsJson.authMode\">\n                        <el-radio-group v-model=\"dialogData.rowData!.varsJson['authMode']\">\n                            <el-radio value=\"password\">{{ $t('terminal.passwordMode') }}</el-radio>\n                            <el-radio value=\"key\">{{ $t('terminal.keyMode') }}</el-radio>\n                        </el-radio-group>\n                    </el-form-item>\n                </div>\n                <div v-if=\"dialogData.rowData!.type === 'SFTP' && dialogData.rowData!.varsJson['authMode'] === 'key'\">\n                    <el-form-item :label=\"$t('terminal.key')\" prop=\"credential\" :rules=\"[Rules.requiredInput]\">\n                        <el-input type=\"textarea\" v-model=\"dialogData.rowData!.credential\" />\n                    </el-form-item>\n                    <el-form-item :label=\"$t('terminal.keyPassword')\" prop=\"varsJson.passPhrase\">\n                        <el-input\n                            type=\"password\"\n                            show-password\n                            clearable\n                            v-model=\"dialogData.rowData!.varsJson['passPhrase']\"\n                        />\n                    </el-form-item>\n                </div>\n                <el-form-item\n                    v-else\n                    :label=\"$t('commons.login.password')\"\n                    prop=\"credential\"\n                    :rules=\"[Rules.requiredInput]\"\n                >\n                    <el-input type=\"password\" clearable show-password v-model.trim=\"dialogData.rowData!.credential\" />\n                </el-form-item>\n            </div>\n            <el-form-item v-if=\"hasRemember()\" prop=\"rememberAuth\">\n                <el-checkbox v-model=\"dialogData.rowData!.rememberAuth\">\n                    {{ $t('terminal.rememberPassword') }}\n                </el-checkbox>\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'COS'\"\n                label=\"Region\"\n                prop=\"varsJson.region\"\n                :rules=\"Rules.requiredInput\"\n            >\n                <el-checkbox v-model=\"regionInput\" :label=\"$t('container.input')\" />\n                <el-select v-if=\"!regionInput\" v-model=\"dialogData.rowData!.varsJson['region']\" filterable clearable>\n                    <el-option v-for=\"item in cities\" :key=\"item.value\" :label=\"item.label\" :value=\"item.value\">\n                        <span class=\"float-left\">{{ item.label }}</span>\n                        <span class=\"option-help\">\n                            {{ item.value }}\n                        </span>\n                    </el-option>\n                </el-select>\n                <el-input v-else v-model.trim=\"dialogData.rowData!.varsJson['region']\" />\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'S3'\"\n                label=\"Region\"\n                prop=\"varsJson.region\"\n                :rules=\"Rules.requiredInput\"\n            >\n                <el-input v-model.trim=\"dialogData.rowData!.varsJson['region']\" />\n            </el-form-item>\n            <el-form-item\n                v-if=\"hasAccessKey()\"\n                :label=\"dialogData.rowData!.type === 'KODO' ? $t('setting.domain') : 'Endpoint'\"\n                prop=\"varsJson.endpointItem\"\n                :rules=\"Rules.requiredInput\"\n            >\n                <el-input v-model.trim=\"dialogData.rowData!.varsJson['endpointItem']\">\n                    <template #prepend>\n                        <el-select v-model.trim=\"domainProto\" class=\"p-w-100\">\n                            <el-option label=\"http\" value=\"http\" />\n                            <el-option label=\"https\" value=\"https\" />\n                        </el-select>\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item v-if=\"hasAccessKey()\" label=\"Bucket\" prop=\"bucket\" :rules=\"Rules.requiredInput\">\n                <el-checkbox v-model=\"dialogData.rowData!.bucketInput\" :label=\"$t('container.input')\" />\n                <el-input clearable v-if=\"dialogData.rowData!.bucketInput\" v-model=\"dialogData.rowData!.bucket\" />\n                <div v-else class=\"w-full\">\n                    <el-select class=\"!w-4/5\" v-model=\"dialogData.rowData!.bucket\">\n                        <el-option v-for=\"item in buckets\" :key=\"item\" :value=\"item\" />\n                    </el-select>\n                    <el-button class=\"!w-1/5\" plain @click=\"getBuckets(formRef)\">\n                        {{ $t('setting.loadBucket') }}\n                    </el-button>\n                </div>\n            </el-form-item>\n            <el-form-item\n                v-if=\"isUPYUN()\"\n                :label=\"$t('setting.serviceName')\"\n                prop=\"bucket\"\n                :rules=\"Rules.requiredInput\"\n            >\n                <el-input v-model=\"dialogData.rowData!.bucket\" />\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'COS'\"\n                :label=\"$t('setting.scType')\"\n                prop=\"varsJson.scType\"\n                :rules=\"[Rules.requiredSelect]\"\n            >\n                <el-select v-model=\"dialogData.rowData!.varsJson['scType']\">\n                    <el-option value=\"DEFAULT\" :label=\"$t('setting.scLighthouse')\" />\n                    <el-option value=\"Standard\" :label=\"$t('setting.scStandard')\" />\n                    <el-option value=\"Standard_IA\" :label=\"$t('setting.scStandard_IA')\" />\n                    <el-option value=\"Archive\" :label=\"$t('setting.scArchive')\" />\n                    <el-option value=\"Deep_Archive\" :label=\"$t('setting.scDeep_Archive')\" />\n                </el-select>\n                <el-alert\n                    v-if=\"dialogData.rowData!.varsJson['scType'] === 'Archive' || dialogData.rowData!.varsJson['scType'] === 'Deep_Archive'\"\n                    class=\"mt-2.5\"\n                    :closable=\"false\"\n                    type=\"warning\"\n                    :title=\"$t('setting.archiveHelper')\"\n                />\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'OSS'\"\n                :label=\"$t('setting.scType')\"\n                prop=\"varsJson.scType\"\n                :rules=\"[Rules.requiredSelect]\"\n            >\n                <el-select v-model=\"dialogData.rowData!.varsJson['scType']\">\n                    <el-option value=\"Standard\" :label=\"$t('setting.scStandard')\" />\n                    <el-option value=\"IA\" :label=\"$t('setting.scStandard_IA')\" />\n                    <el-option value=\"Archive\" :label=\"$t('setting.scArchive')\" />\n                    <el-option value=\"ColdArchive\" :label=\"$t('setting.scDeep_Archive')\" />\n                </el-select>\n                <el-alert\n                    v-if=\"dialogData.rowData!.varsJson['scType'] === 'Archive' || dialogData.rowData!.varsJson['scType'] === 'ColdArchive'\"\n                    class=\"mt-2.5\"\n                    :closable=\"false\"\n                    type=\"warning\"\n                    :title=\"$t('setting.archiveHelper')\"\n                />\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'S3'\"\n                :label=\"$t('setting.scType')\"\n                prop=\"varsJson.scType\"\n                :rules=\"[Rules.requiredSelect]\"\n            >\n                <el-select v-model=\"dialogData.rowData!.varsJson['scType']\">\n                    <el-option value=\"STANDARD\" :label=\"$t('setting.scStandard')\" />\n                    <el-option value=\"STANDARD_IA\" :label=\"$t('setting.scStandard_IA')\" />\n                    <el-option value=\"GLACIER\" :label=\"$t('setting.scArchive')\" />\n                    <el-option value=\"DEEP_ARCHIVE\" :label=\"$t('setting.scDeep_Archive')\" />\n                </el-select>\n                <el-alert\n                    v-if=\"dialogData.rowData!.varsJson['scType'] === 'GLACIER' || dialogData.rowData!.varsJson['scType'] === 'DEEP_ARCHIVE'\"\n                    class=\"mt-2.5\"\n                    :closable=\"false\"\n                    type=\"warning\"\n                    :title=\"$t('setting.archiveHelper')\"\n                />\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'KODO'\"\n                :label=\"$t('cronjob.requestExpirationTime')\"\n                prop=\"varsJson.timeout\"\n            >\n                <el-input-number\n                    style=\"width: 200px\"\n                    :min=\"1\"\n                    step-strictly\n                    :step=\"1\"\n                    v-model.number=\"dialogData.rowData!.varsJson['timeout']\"\n                ></el-input-number>\n            </el-form-item>\n            <div v-if=\"isALIYUNYUN()\">\n                <el-form-item label=\"Token\" prop=\"varsJson.token\">\n                    <div class=\"!w-full\">\n                        <el-input\n                            style=\"width: calc(100% - 80px)\"\n                            :rows=\"3\"\n                            type=\"textarea\"\n                            clearable\n                            v-model.trim=\"dialogData.rowData!.varsJson['token']\"\n                        />\n                        <el-button class=\"append-button\" @click=\"loadFromTokenForAliyun()\">\n                            {{ $t('setting.analysis') }}\n                        </el-button>\n                        <span class=\"input-help\" v-if=\"!globalStore.isFxplay\">\n                            {{ $t('setting.analysisHelper') }}\n                            <el-link\n                                style=\"font-size: 12px; margin-left: 5px\"\n                                icon=\"Position\"\n                                @click=\"toDoc('ali-pan')\"\n                                type=\"primary\"\n                            >\n                                {{ $t('firewall.quickJump') }}\n                            </el-link>\n                        </span>\n                    </div>\n                </el-form-item>\n                <el-form-item label=\"Drive ID\" prop=\"varsJson.drive_id\" :rules=\"Rules.requiredInput\">\n                    <el-input v-model.trim=\"dialogData.rowData!.varsJson['drive_id']\" />\n                </el-form-item>\n                <el-form-item label=\"Refresh Token\" prop=\"varsJson.refresh_token\" :rules=\"Rules.requiredInput\">\n                    <el-input v-model.trim=\"dialogData.rowData!.varsJson['refresh_token']\" />\n                </el-form-item>\n            </div>\n\n            <div v-if=\"hasClient()\">\n                <el-form-item v-if=\"isOneDrive()\">\n                    <el-radio-group v-model=\"dialogData.rowData!.varsJson['isCN']\" @change=\"changeClientFrom\">\n                        <el-radio-button :value=\"false\">{{ $t('setting.isNotCN') }}</el-radio-button>\n                        <el-radio-button :value=\"true\">{{ $t('setting.isCN') }}</el-radio-button>\n                    </el-radio-group>\n                    <span class=\"input-help\" v-if=\"!globalStore.isFxplay\">\n                        {{ $t('setting.onedrive_helper') }}\n                        <el-link\n                            style=\"font-size: 12px; margin-left: 5px\"\n                            icon=\"Position\"\n                            @click=\"toDoc('onedrive')\"\n                            type=\"primary\"\n                        >\n                            {{ $t('firewall.quickJump') }}\n                        </el-link>\n                    </span>\n                </el-form-item>\n                <el-form-item :label=\"$t('setting.client_id')\" prop=\"varsJson.client_id\" :rules=\"Rules.requiredInput\">\n                    <el-input v-model.trim=\"dialogData.rowData!.varsJson['client_id']\" />\n                </el-form-item>\n                <el-form-item\n                    :label=\"$t('setting.client_secret')\"\n                    prop=\"varsJson.client_secret\"\n                    :rules=\"Rules.requiredInput\"\n                >\n                    <el-input v-model.trim=\"dialogData.rowData!.varsJson['client_secret']\" />\n                </el-form-item>\n                <el-form-item\n                    :label=\"$t('setting.redirect_uri')\"\n                    prop=\"varsJson.redirect_uri\"\n                    :rules=\"Rules.requiredInput\"\n                >\n                    <el-input v-model.trim=\"dialogData.rowData!.varsJson['redirect_uri']\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('setting.code')\" prop=\"varsJson.code\">\n                    <div class=\"!w-full\">\n                        <el-input\n                            style=\"width: calc(100% - 80px)\"\n                            :rows=\"3\"\n                            type=\"textarea\"\n                            clearable\n                            v-model.trim=\"dialogData.rowData!.varsJson['code']\"\n                        />\n                        <el-button class=\"append-button\" @click=\"jumpForCode(formRef)\">\n                            {{ $t('setting.loadCode') }}\n                        </el-button>\n                    </div>\n                </el-form-item>\n            </div>\n            <el-form-item v-if=\"hasBackDir()\" :label=\"$t('setting.backupDir')\" prop=\"backupPath\">\n                <el-input clearable v-model.trim=\"dialogData.rowData!.backupPath\" placeholder=\"/1panel\" />\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'SFTP'\"\n                :label=\"$t('setting.backupDir')\"\n                prop=\"backupPath\"\n                :rules=\"[Rules.requiredInput]\"\n            >\n                <el-input v-model.trim=\"dialogData.rowData!.backupPath\" />\n            </el-form-item>\n            <el-form-item\n                v-if=\"dialogData.rowData!.type === 'LOCAL'\"\n                :label=\"$t('setting.backupDir')\"\n                prop=\"backupPath\"\n                :rules=\"Rules.requiredInput\"\n            >\n                <el-input v-model=\"dialogData.rowData!.backupPath\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: true })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button :disabled=\"loading\" @click=\"handleClose\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button :disabled=\"loading\" @click=\"onCheck(formRef)\">\n                {{ $t('terminal.testConn') }}\n            </el-button>\n            <el-button type=\"primary\" :disabled=\"!isOK || loading\" @click=\"onSubmit()\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"loadDir\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, watch, computed, onUnmounted } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { Backup } from '@/api/interface/backup';\nimport FileList from '@/components/file-list/index.vue';\nimport { addBackup, checkBackup, editBackup, getClientInfo, listBucket } from '@/api/modules/backup';\nimport { cities } from './../helper';\nimport { dateFormat, deepCopy, spliceHttp, splitHttp } from '@/utils/util';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Base64 } from 'js-base64';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\nconst buckets = ref();\nconst clientInfo = ref();\nconst fileRef = ref();\n\nconst isOK = ref();\nconst stopWatch = ref();\n\nconst regionInput = ref();\n\nconst domainProto = ref('http');\nconst emit = defineEmits(['search']);\n\ninterface DialogProps {\n    title: string;\n    rowData?: Backup.BackupInfo;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\n\nconst formWatcher = computed(() => {\n    const { type, isPublic, accessKey, bucket, credential, backupPath, bucketInput, varsJson } =\n        dialogData.value.rowData || {};\n    return { type, isPublic, accessKey, bucket, credential, backupPath, bucketInput, varsJson };\n});\nconst startWatcher = () => {\n    if (stopWatch.value) {\n        stopWatcher();\n    }\n    stopWatch.value = watch(\n        () => formWatcher.value,\n        () => {\n            stopWatcher();\n            isOK.value = false;\n        },\n        { deep: true },\n    );\n};\nconst stopWatcher = () => {\n    if (stopWatch.value) {\n        stopWatch.value();\n        stopWatch.value = null;\n    }\n};\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    dialogData.value.rowData.varsJson = dialogData.value.rowData!.vars\n        ? JSON.parse(dialogData.value.rowData!.vars)\n        : {};\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title);\n    if (dialogData.value.title === 'create') {\n        dialogData.value.rowData!.type = 'OSS';\n        changeType();\n        drawerVisible.value = true;\n        return;\n    }\n    buckets.value = [];\n    if (hasAccessKey()) {\n        let itemJson = dialogData.value.rowData!.varsJson['endpoint'];\n        if (dialogData.value.rowData!.type === 'KODO') {\n            itemJson = dialogData.value.rowData!.varsJson['domain'];\n        }\n        let httpItem = splitHttp(itemJson);\n        dialogData.value.rowData!.varsJson['endpointItem'] = httpItem.url;\n        domainProto.value = httpItem.proto;\n    }\n    if (dialogData.value.rowData!.rememberAuth) {\n        dialogData.value.rowData!.accessKey = Base64.decode(dialogData.value.rowData!.accessKey);\n        dialogData.value.rowData!.credential = Base64.decode(dialogData.value.rowData!.credential);\n    }\n    if (dialogData.value.rowData!.varsJson['timeout'] === undefined) {\n        dialogData.value.rowData!.varsJson['timeout'] = 1;\n    }\n    drawerVisible.value = true;\n};\nconst toDoc = (type: string) => {\n    let uri = '';\n    switch (type) {\n        case 'onedrive':\n            uri = '#42-onedrive';\n            break;\n        case 'onedrive-bind':\n            uri = '#43-onedrive';\n            break;\n        case 'ali-pan':\n            uri = '#44';\n            break;\n        case 'google-drive':\n            uri = '#45';\n            break;\n        case 'webdav':\n            uri = '#46-webdav-alist';\n            break;\n    }\n    window.open(globalStore.docsUrl + '/user_manual/settings/' + uri, '_blank', 'noopener,noreferrer');\n};\nconst jumpForCode = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    const result = await formEl.validateField('varsJson.client_id', callback);\n    if (!result) {\n        return;\n    }\n    const result1 = await formEl.validateField('varsJson.redirect_uri', callback);\n    if (!result1) {\n        return;\n    }\n    let client_id = dialogData.value.rowData.varsJson['client_id'];\n    let redirect_uri = dialogData.value.rowData.varsJson['redirect_uri'];\n    if (isOneDrive()) {\n        let commonUrl = `response_type=code&client_id=${client_id}&redirect_uri=${redirect_uri}&scope=offline_access+Files.ReadWrite.All+User.Read`;\n        if (!dialogData.value.rowData!.varsJson['isCN']) {\n            window.open('https://login.microsoftonline.com/common/oauth2/v2.0/authorize?' + commonUrl, '_blank');\n        } else {\n            window.open('https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize?' + commonUrl, '_blank');\n        }\n        return;\n    }\n\n    let url = `https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?client_id=${client_id}&response_type=code&redirect_uri=${redirect_uri}&scope=openid%20profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fphotoslibrary&access_type=offline&prompt=consent&service=lso&o2v=1&ddm=1&flowName=GeneralOAuthFlow`;\n    window.open(url, '_blank');\n};\nfunction callback(error: any) {\n    if (error) {\n        return error.message;\n    } else {\n        return;\n    }\n}\n\nconst loadFromTokenForAliyun = () => {\n    const obj = JSON.parse(dialogData.value.rowData!.varsJson['token']);\n    dialogData.value.rowData!.varsJson['drive_id'] = obj.default_drive_id;\n    dialogData.value.rowData!.varsJson['refresh_token'] = obj.refresh_token;\n};\nconst hasRemember = () => {\n    return (\n        dialogData.value.rowData!.type !== 'LOCAL' &&\n        dialogData.value.rowData!.type !== 'OneDrive' &&\n        dialogData.value.rowData!.type !== 'ALIYUN' &&\n        dialogData.value.rowData!.type !== 'GoogleDrive'\n    );\n};\nconst hasClient = () => {\n    let itemType = dialogData.value.rowData!.type;\n    return itemType === 'OneDrive' || itemType === 'GoogleDrive';\n};\nconst isOneDrive = () => {\n    let itemType = dialogData.value.rowData!.type;\n    return itemType === 'OneDrive';\n};\nconst isUPYUN = () => {\n    let itemType = dialogData.value.rowData!.type;\n    return itemType === 'UPYUN';\n};\nconst isALIYUNYUN = () => {\n    let itemType = dialogData.value.rowData!.type;\n    return itemType === 'ALIYUN';\n};\nconst hasAccessKey = () => {\n    let itemType = dialogData.value.rowData!.type;\n    return itemType === 'COS' || itemType === 'KODO' || itemType === 'MINIO' || itemType === 'OSS' || itemType === 'S3';\n};\nconst hasPassword = () => {\n    let itemType = dialogData.value.rowData!.type;\n    return itemType === 'SFTP' || itemType === 'WebDAV';\n};\n\nconst hasBackDir = () => {\n    let itemType = dialogData.value.rowData!.type;\n    return itemType !== 'LOCAL' && itemType !== 'SFTP';\n};\n\nconst loadDir = async (path: string) => {\n    dialogData.value.rowData!.backupPath = path;\n};\n\nconst changeType = async () => {\n    buckets.value = [];\n    dialogData.value.rowData!.varsJson = {};\n    dialogData.value.rowData!.rememberAuth = false;\n    switch (dialogData.value.rowData!.type) {\n        case 'COS':\n        case 'OSS':\n            dialogData.value.rowData.varsJson['scType'] = 'Standard';\n            break;\n        case 'S3':\n            dialogData.value.rowData.varsJson['scType'] = 'STANDARD';\n            dialogData.value.rowData.varsJson['mode'] = 'virtual hosted';\n            break;\n        case 'KODO':\n            dialogData.value.rowData!.varsJson['timeout'] = 1;\n            break;\n        case 'OneDrive':\n            dialogData.value.rowData.varsJson['isCN'] = false;\n            const res = await getClientInfo('Onedrive');\n            clientInfo.value = res.data;\n            if (!dialogData.value.rowData.id) {\n                dialogData.value.rowData.varsJson = {\n                    isCN: false,\n                    client_id: res.data.client_id,\n                    client_secret: res.data.client_secret,\n                    redirect_uri: res.data.redirect_uri,\n                };\n            }\n            break;\n        case 'SFTP':\n            dialogData.value.rowData.varsJson['port'] = 22;\n            dialogData.value.rowData.varsJson['authMode'] = 'password';\n            break;\n    }\n};\nconst changeClientFrom = () => {\n    if (dialogData.value.rowData.varsJson['isCN']) {\n        dialogData.value.rowData.varsJson = {\n            isCN: true,\n            client_id: '',\n            client_secret: '',\n            redirect_uri: '',\n        };\n    } else {\n        dialogData.value.rowData.varsJson = {\n            isCN: false,\n            client_id: clientInfo.value.client_id,\n            client_secret: clientInfo.value.client_secret,\n            redirect_uri: clientInfo.value.redirect_uri,\n        };\n    }\n};\n\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\nconst getBuckets = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    const result1 = await formEl.validateField('varsJson.endpointItem', callback);\n    const result2 = await formEl.validateField('accessKey', callback);\n    const result3 = await formEl.validateField('credential', callback);\n    const result4 = await formEl.validateField('varsJson.region', callback);\n    if (!result1 || !result2 || !result3 || !result4) {\n        return;\n    }\n    loading.value = true;\n    let item = deepCopy(dialogData.value.rowData!.varsJson);\n    let itemEndpoint = loadEndpoint();\n    if (dialogData.value.rowData!.type === 'KODO') {\n        item['domain'] = itemEndpoint;\n    } else {\n        item['endpoint'] = itemEndpoint;\n    }\n    item['endpointItem'] = undefined;\n    listBucket({\n        isPublic: dialogData.value.rowData!.isPublic,\n        type: dialogData.value.rowData!.type,\n        vars: JSON.stringify(item),\n        accessKey: dialogData.value.rowData!.accessKey,\n        credential: dialogData.value.rowData!.credential,\n    })\n        .then((res) => {\n            loading.value = false;\n            buckets.value = res.data;\n        })\n        .catch(() => {\n            buckets.value = [];\n            loading.value = false;\n        });\n};\n\nconst loadEndpoint = () => {\n    let item = splitHttp(dialogData.value.rowData!.varsJson['endpointItem']);\n    if (item.proto) {\n        domainProto.value = item.proto;\n        dialogData.value.rowData!.varsJson['endpointItem'] = item.url;\n    }\n    return spliceHttp(domainProto.value, dialogData.value.rowData!.varsJson['endpointItem']);\n};\n\nconst onCheck = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (!dialogData.value.rowData) return;\n        if (hasAccessKey()) {\n            let itemEndpoint = loadEndpoint();\n            if (dialogData.value.rowData!.type === 'KODO') {\n                dialogData.value.rowData!.varsJson['domain'] = itemEndpoint;\n            } else {\n                dialogData.value.rowData!.varsJson['endpoint'] = itemEndpoint;\n            }\n        }\n        if (isOneDrive()) {\n            dialogData.value.rowData!.varsJson['code'] = decodeURIComponent(\n                dialogData.value.rowData!.varsJson['code'] || '',\n            );\n        }\n        if (isALIYUNYUN()) {\n            dialogData.value.rowData!.varsJson['token'] = undefined;\n        }\n        dialogData.value.rowData.vars = JSON.stringify(dialogData.value.rowData!.varsJson);\n        loading.value = true;\n        await checkBackup(dialogData.value.rowData)\n            .then((res) => {\n                loading.value = false;\n                if (res.data.isOk) {\n                    isOK.value = true;\n                    MsgSuccess(i18n.global.t('terminal.connTestOk'));\n                    if (hasClient()) {\n                        dialogData.value.rowData!.varsJson['refresh_token'] = Base64.decode(res.data.token);\n                        dialogData.value.rowData!.varsJson['refresh_status'] = 'Success';\n                        dialogData.value.rowData!.varsJson['refresh_time'] = dateFormat(null, null, new Date());\n                    }\n                    if (isALIYUNYUN()) {\n                        dialogData.value.rowData!.varsJson['refresh_status'] = 'Success';\n                        dialogData.value.rowData!.varsJson['refresh_time'] = dateFormat(null, null, new Date());\n                    }\n                    startWatcher();\n                    return;\n                }\n                isOK.value = false;\n                MsgError(i18n.global.t('terminal.connTestFailed') + ':' + res.data.msg);\n            })\n            .catch(() => {\n                loading.value = false;\n                isOK.value = false;\n            });\n    });\n};\n\nconst onSubmit = async () => {\n    dialogData.value.rowData.vars = JSON.stringify(dialogData.value.rowData!.varsJson);\n    loading.value = true;\n    if (dialogData.value.title === 'create') {\n        await addBackup(dialogData.value.rowData)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n        return;\n    }\n    await editBackup(dialogData.value.rowData)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            drawerVisible.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nonUnmounted(() => {\n    if (stopWatch.value) stopWatcher();\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.option-help {\n    float: right;\n    font-size: 12px;\n    word-break: break-all;\n    color: #8f959e;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/expired.vue",
    "content": "<template>\n    <div>\n        <el-card style=\"margin-top: 20px\">\n            <template #header>\n                <div class=\"card-header\">\n                    <span style=\"font-size: 14px; font-weight: 500\">{{ $t('setting.expiredHelper') }}</span>\n                </div>\n            </template>\n            <el-row>\n                <el-col :span=\"1\"><br /></el-col>\n                <el-col :span=\"10\">\n                    <el-form\n                        :model=\"passForm\"\n                        ref=\"passFormRef\"\n                        :rules=\"passRules\"\n                        label-position=\"left\"\n                        label-width=\"160px\"\n                    >\n                        <el-form-item :label=\"$t('setting.oldPassword')\" prop=\"oldPass\">\n                            <el-input type=\"password\" show-password clearable v-model.trim=\"passForm.oldPass\" />\n                        </el-form-item>\n                        <el-form-item v-if=\"!isComplexity\" :label=\"$t('setting.newPassword')\" prop=\"newPass\">\n                            <el-input type=\"password\" show-password clearable v-model.trim=\"passForm.newPass\" />\n                        </el-form-item>\n                        <el-form-item v-if=\"isComplexity\" :label=\"$t('setting.newPassword')\" prop=\"newPassComplexity\">\n                            <el-input\n                                type=\"password\"\n                                show-password\n                                clearable\n                                v-model.trim=\"passForm.newPassComplexity\"\n                            />\n                        </el-form-item>\n                        <el-form-item :label=\"$t('setting.retryPassword')\" prop=\"rePass\">\n                            <el-input type=\"password\" show-password clearable v-model.trim=\"passForm.rePass\" />\n                        </el-form-item>\n                        <el-form-item>\n                            <el-button type=\"primary\" @click=\"submitChangePassword(passFormRef)\">\n                                {{ $t('commons.button.confirm') }}\n                            </el-button>\n                        </el-form-item>\n                    </el-form>\n                </el-col>\n            </el-row>\n        </el-card>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, reactive } from 'vue';\nimport { getSettingInfo, handleExpired } from '@/api/modules/setting';\nimport { ElForm } from 'element-plus';\nimport i18n from '@/lang';\nimport { Rules } from '@/global/form-rules';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { routerToName } from '@/utils/router';\n\nlet isComplexity = ref(false);\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst passFormRef = ref<FormInstance>();\nconst passRules = reactive({\n    oldPass: [Rules.requiredInput, Rules.noSpace],\n    newPass: [Rules.requiredInput, Rules.noSpace],\n    newPassComplexity: [Rules.requiredInput, Rules.noSpace, Rules.password],\n    rePass: [Rules.requiredInput, Rules.noSpace, { validator: checkPasswordSame, trigger: 'blur' }],\n});\nconst passForm = reactive({\n    oldPass: '',\n    newPass: '',\n    newPassComplexity: '',\n    rePass: '',\n});\n\nfunction checkPasswordSame(rule: any, value: any, callback: any) {\n    let password = !isComplexity.value ? passForm.newPass : passForm.newPassComplexity;\n    if (password !== passForm.rePass) {\n        return callback(new Error(i18n.global.t('commons.rule.rePassword')));\n    }\n    callback();\n}\n\nconst submitChangePassword = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let password = !isComplexity.value ? passForm.newPass : passForm.newPassComplexity;\n        if (password === passForm.oldPass) {\n            MsgError(i18n.global.t('setting.duplicatePassword'));\n            return;\n        }\n        await handleExpired({ oldPassword: passForm.oldPass, newPassword: password });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        routerToName('home');\n    });\n};\nconst search = async () => {\n    const res = await getSettingInfo();\n    let settingForm = res.data;\n    isComplexity.value = settingForm?.complexityVerification === 'Enable';\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { isOffLine, isFxplay } = useGlobalStore();\n\nconst buttons = [\n    {\n        label: i18n.global.t('setting.panel'),\n        path: '/settings/panel',\n    },\n    {\n        label: i18n.global.t('setting.safe'),\n        path: '/settings/safe',\n    },\n    {\n        label: i18n.global.t('xpack.alert.alertNotice'),\n        path: '/settings/alert',\n    },\n    {\n        label: i18n.global.t('setting.backupAccount', 2),\n        path: '/settings/backupaccount',\n    },\n    {\n        label: i18n.global.t('setting.snapshot', 2),\n        path: '/settings/snapshot',\n    },\n    {\n        label: i18n.global.t('setting.license'),\n        path: '/settings/license',\n    },\n    {\n        label: i18n.global.t('setting.about'),\n        path: '/settings/about',\n    },\n];\n\nonMounted(() => {\n    if (isOffLine.value) {\n        buttons.splice(5, 1);\n    }\n    if (isFxplay.value) {\n        buttons.splice(6, 1);\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/license/bind/free.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.license')\" :resource=\"licenseName\" @close=\"handleClose\">\n        <el-form label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('license.add')\" prop=\"nodeID\">\n                <el-select filterable multiple v-model=\"form.nodeIDs\" clearable class=\"w-full\">\n                    <div v-for=\"item in unboundOptions\" :key=\"item.id\">\n                        <el-option :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onBind()\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { changeBind, listNodeOptions } from '@/api/modules/setting';\nimport { Setting } from '@/api/interface/setting';\n\ninterface DialogProps {\n    licenseID: number;\n    licenseName: string;\n    freeNodes: Array<Setting.NodeItem>;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst licenseName = ref();\nconst unboundOptions = ref([]);\n\nconst form = reactive({\n    licenseID: null,\n    nodeIDs: [],\n});\n\nconst acceptParams = (params: DialogProps): void => {\n    licenseName.value = params.licenseName;\n    form.licenseID = params.licenseID;\n    form.nodeIDs = [];\n    for (const item of params.freeNodes) {\n        if (!item.isXpack) {\n            form.nodeIDs.push(item.id);\n        }\n    }\n    unboundOptions.value = [];\n    loadNodes();\n    drawerVisible.value = true;\n};\n\nconst onBind = async () => {\n    loading.value = true;\n    await changeBind(form.licenseID, form.nodeIDs)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            window.location.reload();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadNodes = async () => {\n    await listNodeOptions('free').then((res) => {\n        let nodeOptions = res.data || [];\n        for (const item of nodeOptions) {\n            if (!item.isBound) {\n                unboundOptions.value.push(item);\n                continue;\n            }\n            for (const item2 of form.nodeIDs) {\n                if (item.id === item2) {\n                    unboundOptions.value.push(item);\n                }\n            }\n        }\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/license/bind/xpack.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"$t('commons.button.bind')\"\n        :resource=\"licenseName\"\n        @close=\"handleClose\"\n        size=\"small\"\n    >\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.bindNode')\" prop=\"nodeID\" :rules=\"Rules.requiredSelect\">\n                <el-select filterable v-model=\"form.nodeID\" class=\"w-full\" clearable>\n                    <el-option v-for=\"item in freeNodes\" :key=\"item.id\" :label=\"item.name\" :value=\"item.id\" />\n                </el-select>\n            </el-form-item>\n            <el-card class=\"mt-5\" v-if=\"form.nodeID && form.nodeID !== 0\">\n                <div class=\"mb-2\">\n                    <span>{{ $t('xpack.node.syncInfo') }}</span>\n                </div>\n                <el-form-item prop=\"syncListItem\">\n                    <el-checkbox-group v-model=\"form.syncListItem\">\n                        <div class=\"ml-5\">\n                            <el-checkbox :label=\"$t('xpack.node.syncProxy')\" value=\"SyncSystemProxy\" />\n                        </div>\n                        <div class=\"ml-5\">\n                            <el-checkbox :label=\"$t('xpack.node.syncAlertSetting')\" value=\"SyncAlertSetting\" />\n                        </div>\n                        <div class=\"ml-5\">\n                            <el-checkbox :label=\"$t('xpack.node.syncCustomApp')\" value=\"SyncCustomApp\" />\n                        </div>\n                        <div class=\"ml-5\">\n                            <el-checkbox :label=\"$t('xpack.node.syncBackupAccount')\" value=\"SyncBackupAccounts\" />\n                        </div>\n                    </el-checkbox-group>\n                    <span class=\"input-help\">{{ $t('xpack.node.syncHelper') }}</span>\n                </el-form-item>\n            </el-card>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onBind(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n\n    <DockerProxyDialog ref=\"dockerProxyRef\" @submit=\"submit\" v-model:with-docker-restart=\"withDockerRestart\" />\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { bindLicense, listNodeOptions } from '@/api/modules/setting';\nimport DockerProxyDialog from '@/components/docker-proxy/dialog.vue';\nimport { FormInstance } from 'element-plus';\nimport { GlobalStore } from '@/store';\nimport { Rules } from '@/global/form-rules';\nconst globalStore = GlobalStore();\n\ninterface DialogProps {\n    licenseName: string;\n    licenseID: number;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst licenseName = ref();\nconst freeNodes = ref([]);\n\nconst dockerProxyRef = ref();\nconst withDockerRestart = ref(false);\n\nconst form = reactive({\n    nodeID: null,\n    licenseID: null,\n    syncList: '',\n    syncListItem: ['SyncSystemProxy', 'SyncAlertSetting', 'SyncCustomApp', 'SyncBackupAccounts'],\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    licenseName.value = params.licenseName;\n    form.licenseID = params.licenseID;\n    loadNodes();\n    drawerVisible.value = true;\n};\n\nconst onBind = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        form.syncList = form.syncListItem?.join(',') || '';\n        dockerProxyRef.value.acceptParams({ syncList: form.syncList });\n    });\n};\n\nconst submit = async () => {\n    loading.value = true;\n    await bindLicense({\n        licenseID: form.licenseID,\n        nodeID: form.nodeID,\n        syncList: form.syncList,\n        withDockerRestart: withDockerRestart.value,\n    })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            window.location.reload();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadNodes = async () => {\n    if (!globalStore.isMasterProductPro) {\n        freeNodes.value = [{ id: 0, name: globalStore.getMasterAlias() }];\n        return;\n    }\n    await listNodeOptions('free')\n        .then((res) => {\n            freeNodes.value = res.data || [];\n        })\n        .catch(() => {\n            freeNodes.value = [];\n        });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/license/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('setting.license')\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"toUpload()\">\n                    {{ $t('commons.button.add') }}\n                </el-button>\n            </template>\n            <template #prompt>\n                <el-alert type=\"info\" :title=\"$t('license.licenseAlert')\" :closable=\"false\" />\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"backup-account-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" @sort-change=\"search\" @search=\"search\" :data=\"data\">\n                    <el-table-column\n                        :label=\"$t('license.authorizationId')\"\n                        :min-width=\"80\"\n                        prop=\"licenseName\"\n                        show-overflow-tooltip\n                    />\n                    <el-table-column :label=\"$t('license.authorizedUser')\" prop=\"assigneeName\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            {{ row.assigneeName || '-' }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('license.trialInfo')\" show-overflow-tooltip :min-width=\"120\">\n                        <template #default=\"{ row }\">\n                            {{ loadVersion(row) }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.status\">\n                                <Status :status=\"row.status\" :msg=\"loadMsg(row)\"></Status>\n                            </div>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('setting.bindNode')\" :min-width=\"120\">\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.status && row.status !== 'Free'\">\n                                <div>\n                                    {{ $t('license.pro') }}:\n                                    {{ loadBindNode(row) }}\n                                </div>\n                                <div>\n                                    {{ $t('license.oss') }}:\n                                    <el-button class=\"bind-button\" @click=\"onBindFree(row)\" link type=\"primary\">\n                                        {{ row.bindCount }} / {{ row.freeCount }}\n                                    </el-button>\n                                </div>\n                            </div>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        width=\"300px\"\n                        :buttons=\"buttons\"\n                        :ellipsis=\"10\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <LicenseImport ref=\"licenseRef\" />\n        <BindFree ref=\"bindFreeRef\" />\n        <BindXpack ref=\"bindXpackRef\" />\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <OpDialog ref=\"opRef2\" @search=\"search\" @submit=\"submitUnbind\">\n            <template #content>\n                <el-form class=\"mt-4 mb-1\" ref=\"deleteForm\" label-position=\"left\">\n                    <el-form-item>\n                        <el-checkbox v-model=\"forceUnbind\" :label=\"$t('license.forceUnbind')\" />\n                        <span class=\"input-help\">\n                            {{ $t('license.forceUnbindHelper') }}\n                        </span>\n                    </el-form-item>\n\n                    <DockerProxy\n                        class=\"w-full\"\n                        v-model:with-docker-restart=\"withDockerRestart\"\n                        syncList=\"SyncSystemProxy\"\n                    />\n                </el-form>\n            </template>\n        </OpDialog>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, reactive, onMounted } from 'vue';\nimport { deleteLicense, searchLicense, syncLicense, unbindLicense } from '@/api/modules/setting';\nimport LicenseImport from '@/components/license-import/index.vue';\nimport DockerProxy from '@/components/docker-proxy/index.vue';\nimport BindFree from '@/views/setting/license/bind/free.vue';\nimport BindXpack from '@/views/setting/license/bind/xpack.vue';\nimport { dateFormat } from '@/utils/util';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref();\nconst licenseRef = ref();\nconst bindFreeRef = ref();\nconst bindXpackRef = ref();\nconst opRef = ref();\nconst opRef2 = ref();\nconst forceUnbind = ref();\nconst unbindRow = ref();\n\nconst withDockerRestart = ref();\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'license-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('license-page-size')) || 20,\n    total: 0,\n    type: '',\n    name: '',\n});\n\nconst loadMsg = (row: any) => {\n    if (row.status === 'Exceptional') {\n        return i18n.global.t('license.exceptionalHelper') + (row.message || '-');\n    }\n    if (row.status === 'Lost') {\n        return i18n.global.t('license.lostHelper') + (row.message || '-');\n    }\n    return '';\n};\n\nconst onSync = async (row: any) => {\n    loading.value = true;\n    await syncLicense(row.id)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            window.location.reload();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onBindFree = async (row: any) => {\n    bindFreeRef.value.acceptParams({\n        licenseID: row.id,\n        licenseName: row.licenseName,\n        freeNodes: row.freeNodes || [],\n    });\n};\n\nconst loadBindNode = (row: any) => {\n    if (!row.bindNode && row.freeNodes?.length) {\n        return row.bindNode;\n    }\n    if (row.freeNodes) {\n        for (const item of row.freeNodes) {\n            if (item.addr === row.bindNode) {\n                return item.name === 'local' ? globalStore.getMasterAlias() : item.name;\n            }\n        }\n    }\n    return row.bindNode;\n};\n\nconst onUnbind = async (row: any) => {\n    if (row.freeNodes && (row.freeNodes.length > 1 || !row.freeNodes[0].isXpack)) {\n        MsgError(i18n.global.t('license.licenseUnbindHelper'));\n        return;\n    }\n    unbindRow.value = row;\n    opRef2.value.acceptParams({\n        title: i18n.global.t('commons.button.unbind'),\n        names: [row.licenseName],\n        msg: i18n.global.t('license.unbindHelper'),\n        api: null,\n        params: null,\n    });\n};\nconst submitUnbind = async () => {\n    loading.value = true;\n    await unbindLicense({\n        id: unbindRow.value.id,\n        force: forceUnbind.value,\n        withDockerRestart: withDockerRestart.value,\n    })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            window.location.reload();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst search = async () => {\n    loading.value = true;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    await searchLicense(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            for (const item of data.value) {\n                item.productName = 'product-1panel-pro';\n                item.expiresAt = item.productPro === '0' ? '' : timestampToDate(Number(item.productPro));\n            }\n            data.value.sort((a, b) => {\n                const masterLabel = globalStore.getMasterAlias();\n                const nodeA = loadBindNode(a);\n                const nodeB = loadBindNode(b);\n\n                if (nodeA === masterLabel || nodeB === masterLabel) {\n                    return Number(nodeB === masterLabel) - Number(nodeA === masterLabel);\n                }\n                return a.status.localeCompare(b.status);\n            });\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst timestampToDate = (timestamp: number) => {\n    const date = new Date(timestamp * 1000);\n    const y = date.getFullYear();\n    let m: string | number = date.getMonth() + 1;\n    m = m < 10 ? `0${String(m)}` : m;\n    let d: string | number = date.getDate();\n    d = d < 10 ? `0${String(d)}` : d;\n    let h: string | number = date.getHours();\n    h = h < 10 ? `0${String(h)}` : h;\n    let minute: string | number = date.getMinutes();\n    minute = minute < 10 ? `0${String(minute)}` : minute;\n    let second: string | number = date.getSeconds();\n    second = second < 10 ? `0${String(second)}` : second;\n    return `${y}-${m}-${d} ${h}:${minute}:${second}`;\n};\n\nconst toUpload = () => {\n    licenseRef.value.acceptParams({ isImport: true });\n};\n\nconst loadVersion = (row: any) => {\n    if (row.trial === 'yes') {\n        return i18n.global.t('license.trial');\n    }\n    if (row.productPro && row.productPro !== '0') {\n        return i18n.global.t('license.subscription') + ' [ ' + row.expiresAt + ' ] ';\n    }\n    if (row.versionConstraint && row.versionConstraint !== 'all') {\n        return i18n.global.t('license.versionConstraint', ['v' + row.versionConstraint.replace('.x', '')]);\n    }\n    return i18n.global.t('license.perpetual');\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.bind'),\n        disabled: (row: any) => {\n            return row.status !== 'Free';\n        },\n        click: (row: any) => {\n            bindXpackRef.value.acceptParams({ licenseID: row.id, licenseName: row.licenseName });\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.unbind'),\n        disabled: (row: any) => {\n            return row.status === 'Free';\n        },\n        click: (row: any) => {\n            onUnbind(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        disabled: (row: any) => {\n            return row.status === 'Free';\n        },\n        click: (row: any) => {\n            licenseRef.value.acceptParams({ oldLicense: row.licenseName, isImport: true });\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.sync'),\n        disabled: (row: any) => {\n            return row.status === 'Free';\n        },\n        click: (row: any) => {\n            onSync(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: any) => {\n            opRef.value.acceptParams({\n                title: i18n.global.t('commons.button.delete'),\n                names: [row.licenseName],\n                msg: i18n.global.t('commons.msg.operatorHelper', [\n                    i18n.global.t('setting.license'),\n                    i18n.global.t('commons.button.delete'),\n                ]),\n                api: deleteLicense,\n                params: { id: row.id },\n            });\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.bind-button {\n    margin-top: -2px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/api-interface/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.apiInterface')\" @close=\"handleClose\" size=\"normal\">\n        <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\">\n            <template #default>\n                <ul>\n                    <li>\n                        <el-text type=\"danger\">{{ $t('setting.apiInterfaceAlert1') }}</el-text>\n                    </li>\n                    <li>\n                        <el-text type=\"danger\">{{ $t('setting.apiInterfaceAlert2') }}</el-text>\n                    </li>\n                    <li>\n                        <el-link :href=\"apiURL\" type=\"warning\" target=\"_blank\" class=\"mb-0.5 ml-0.5\">\n                            {{ $t('setting.apiInterfaceAlert3') }}\n                        </el-link>\n                    </li>\n                    <li v-if=\"!globalStore.isFxplay\">\n                        <el-link :href=\"panelURL\" type=\"warning\" target=\"_blank\" class=\"mb-0.5 ml-0.5\">\n                            {{ $t('setting.apiInterfaceAlert4') }}\n                        </el-link>\n                    </li>\n                </ul>\n            </template>\n        </el-alert>\n        <el-form :model=\"form\" ref=\"formRef\" @submit.prevent v-loading=\"loading\" label-position=\"top\" :rules=\"rules\">\n            <el-form-item :label=\"$t('setting.apiKey')\" prop=\"apiKey\">\n                <el-input v-model=\"form.apiKey\" readonly style=\"width: calc(100% - 225px)\" />\n                <el-button-group>\n                    <CopyButton class=\"copy_button\" :isIcon=\"false\" :content=\"form.apiKey\" />\n                    <el-button @click=\"resetApiKey()\">\n                        {{ $t('commons.button.reset') }}\n                    </el-button>\n                </el-button-group>\n                <span class=\"input-help\">{{ $t('setting.apiKeyHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.ipWhiteList')\" prop=\"ipWhiteList\">\n                <el-input\n                    type=\"textarea\"\n                    :placeholder=\"$t('setting.ipWhiteListEgs')\"\n                    :rows=\"4\"\n                    v-model=\"form.ipWhiteList\"\n                />\n                <span class=\"input-help\">{{ $t('setting.ipWhiteListHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.apiKeyValidityTime')\" prop=\"apiKeyValidityTime\">\n                <el-input :placeholder=\"$t('setting.apiKeyValidityTimeEgs')\" v-model=\"form.apiKeyValidityTime\">\n                    <template #append>{{ $t('commons.units.minute') }}</template>\n                </el-input>\n                <span class=\"input-help\">\n                    {{ $t('setting.apiKeyValidityTimeHelper') }}\n                </span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { generateApiKey, updateApiConfig } from '@/api/modules/setting';\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { checkCidr, checkCidrV6, checkIpV4V6 } from '@/utils/util';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref();\nconst drawerVisible = ref();\nconst formRef = ref();\nconst apiURL = `${window.location.protocol}//${window.location.hostname}${\n    window.location.port ? `:${window.location.port}` : ''\n}/1panel/swagger/index.html`;\nconst panelURL = `${globalStore.docsUrl}/dev_manual/api_manual/`;\nconst form = reactive({\n    apiKey: '',\n    ipWhiteList: '',\n    apiInterfaceStatus: '',\n    apiKeyValidityTime: 120,\n});\nconst rules = reactive({\n    ipWhiteList: [Rules.requiredInput, { validator: checkIPs, trigger: 'blur' }],\n    apiKey: [Rules.requiredInput],\n    apiKeyValidityTime: [Rules.requiredInput, Rules.integerNumberWith0],\n});\ninterface DialogProps {\n    apiInterfaceStatus: string;\n    apiKey: string;\n    ipWhiteList: string;\n    apiKeyValidityTime: number;\n}\nconst emit = defineEmits<{ (e: 'search'): void }>();\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    form.apiInterfaceStatus = params.apiInterfaceStatus;\n    form.apiKey = params.apiKey;\n    if (params.apiKey == '') {\n        await generateApiKey().then((res) => {\n            form.apiKey = res.data;\n        });\n    }\n    form.ipWhiteList = params.ipWhiteList;\n    form.apiKeyValidityTime = params.apiKeyValidityTime;\n    drawerVisible.value = true;\n};\nconst resetApiKey = async () => {\n    ElMessageBox.confirm(i18n.global.t('setting.apiKeyResetHelper'), i18n.global.t('setting.apiKeyReset'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    })\n        .then(async () => {\n            loading.value = true;\n            await generateApiKey()\n                .then((res) => {\n                    loading.value = false;\n                    form.apiKey = res.data;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nfunction checkIPs(rule: any, value: any, callback: any) {\n    if (form.ipWhiteList !== '') {\n        let addr = form.ipWhiteList.split('\\n');\n        for (const item of addr) {\n            if (item === '') {\n                continue;\n            }\n            if (item.indexOf('/') !== -1) {\n                if (item.indexOf(':') !== -1) {\n                    if (checkCidrV6(item)) {\n                        return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                    }\n                } else if (checkCidr(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            } else if (checkIpV4V6(item)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        }\n    }\n    callback();\n}\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let param = {\n            apiKey: form.apiKey,\n            ipWhiteList: form.ipWhiteList,\n            apiInterfaceStatus: form.apiInterfaceStatus,\n            apiKeyValidityTime: form.apiKeyValidityTime,\n        };\n        loading.value = true;\n        await updateApiConfig(param)\n            .then(() => {\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.copy_button {\n    border-radius: 0px;\n    border-left-width: 0px;\n}\n:deep(.el-input__wrapper) {\n    border-top-right-radius: 0px;\n    border-bottom-right-radius: 0px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/edition/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.runtimeEnv')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.region')\" prop=\"edition\">\n                <el-radio-group v-model=\"form.edition\">\n                    <el-radio value=\"cn\">\n                        <span>{{ $t('setting.cn') }}</span>\n                    </el-radio>\n                    <el-radio value=\"intl\">\n                        <span>{{ $t('setting.intl') }}</span>\n                    </el-radio>\n                </el-radio-group>\n                <span class=\"input-help\">{{ $t('setting.regionTip') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.docSource')\" prop=\"docSource\">\n                <el-radio-group v-model=\"form.docSource\">\n                    <el-radio value=\"withByRegion\">\n                        <span>{{ $t('setting.withByRegion') }}</span>\n                    </el-radio>\n                    <el-radio value=\"withByLang\">\n                        <span>{{ $t('setting.withByLang') }}</span>\n                    </el-radio>\n                </el-radio-group>\n                <span class=\"input-help\">{{ $t('setting.docSourceTip') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveEdition()\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateSetting } from '@/api/modules/setting';\nimport { FormInstance } from 'element-plus';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\ninterface DialogProps {\n    edition: string;\n    docSource: string;\n}\nconst drawerVisible = ref();\nconst loading = ref(false);\n\nconst form = reactive({\n    edition: '',\n    docSource: 'withByRegion',\n});\nconst initialForm = reactive({\n    edition: '',\n    docSource: 'withByRegion',\n});\n\nconst formRef = ref<FormInstance>();\nconst emit = defineEmits(['search']);\n\nconst acceptParams = (params: DialogProps): void => {\n    form.edition = params.edition;\n    form.docSource = params.docSource || 'withByRegion';\n    initialForm.edition = params.edition;\n    initialForm.docSource = params.docSource || 'withByRegion';\n    drawerVisible.value = true;\n};\n\nconst onSaveEdition = async () => {\n    const editionChanged = form.edition !== initialForm.edition;\n    const docSourceChanged = form.docSource !== initialForm.docSource;\n    if (!editionChanged && !docSourceChanged) {\n        drawerVisible.value = false;\n        return;\n    }\n\n    if (editionChanged) {\n        try {\n            await ElMessageBox.confirm(i18n.global.t('setting.regionHelper'), i18n.global.t('setting.region'), {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                dangerouslyUseHTMLString: true,\n            });\n        } catch {\n            return;\n        }\n    }\n\n    loading.value = true;\n    try {\n        if (editionChanged) {\n            await updateSetting({ key: 'Edition', value: form.edition });\n            globalStore.isIntl = form.edition === 'intl';\n        }\n        if (docSourceChanged) {\n            await updateSetting({ key: 'DocSource', value: form.docSource });\n            globalStore.docWithRegion = form.docSource === 'withByRegion';\n        }\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        if (editionChanged) {\n            location.reload();\n            return;\n        }\n        emit('search');\n        drawerVisible.value = false;\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/hidemenu/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.menuSetting')\" @close=\"handleClose\" size=\"normal\">\n        <el-alert :closable=\"false\" :title=\"$t('setting.menuSettingHelper')\" type=\"warning\" />\n        <el-tree\n            :data=\"treeData.hideMenu\"\n            :allow-drag=\"allowDrag\"\n            :allow-drop=\"allowDrop\"\n            draggable\n            node-key=\"id\"\n            class=\"mt-3\"\n            :icon=\"ArrowRight\"\n            @node-drop=\"handleDrop\"\n        >\n            <template #default=\"{ node, data }\">\n                <div class=\"grid grid-cols-4 gap-4 items-center w-full py-2 group\">\n                    <span class=\"col-span-2\" :style=\"{ paddingLeft: `${(node.level - 1) * 16}px` }\">\n                        {{ i18n.global.t(data.title) }}\n                    </span>\n                    <span class=\"flex justify-center w-[60px]\">\n                        <el-switch\n                            v-if=\"!data.disabled\"\n                            v-model=\"data.isShow\"\n                            @change=\"onChangeShow(data)\"\n                            @click.stop\n                            @mousedown.stop\n                        />\n                        <span v-else>-</span>\n                    </span>\n                    <span\n                        class=\"text-right hidden cursor-move\"\n                        :class=\"data.label == 'Home-Menu' || data.children?.length > 0 ? '' : 'group-hover:block'\"\n                    >\n                        ⋮⋮\n                    </span>\n                </div>\n            </template>\n        </el-tree>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"defaultHideMenus\">{{ $t('commons.button.setDefault') }}</el-button>\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"saveHideMenus\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport { AllowDropType, ElMessageBox, RenderContentContext } from 'element-plus';\nimport i18n from '@/lang';\nimport { defaultMenu, updateMenu } from '@/api/modules/setting';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nimport { ArrowRight } from '@element-plus/icons-vue';\nimport { sortMenu } from '@/utils/util';\nconst globalStore = GlobalStore();\n\nconst drawerVisible = ref();\nconst loading = ref();\ninterface DialogProps {\n    hideMenu: string;\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    drawerVisible.value = true;\n    let hideMenu = JSON.parse(params.hideMenu);\n    sortMenu(hideMenu);\n    treeData.hideMenu = hideMenu;\n    if (globalStore.isIntl) {\n        treeData.hideMenu = removeUpage(treeData.hideMenu);\n    }\n};\ntype Node = RenderContentContext['node'];\n\nconst allowDrag = (draggingNode: Node) => {\n    const { label } = draggingNode.data;\n    const forbidden = ['Home-Menu'];\n    return draggingNode.level < 3 && !forbidden.some((key) => label.includes(key));\n};\n\nconst allowDrop = (draggingNode: Node, dropNode: Node, type: AllowDropType) => {\n    const restricted = ['App-Menu', 'Setting-Menu'];\n    const isDraggingFirstLevel = draggingNode.level === 1;\n    const isDraggingSecondLevel = draggingNode.level === 2;\n    const isDropFirstLevel = dropNode.level === 1;\n    const isDropSecondLevel = dropNode.level === 2;\n    if (restricted.includes(draggingNode.data.label) && isDropSecondLevel) {\n        return false;\n    }\n    if (dropNode.data.label === 'Home-Menu') {\n        return type !== 'prev' && type !== 'inner';\n    }\n\n    if (draggingNode.parent && draggingNode.parent.childNodes.length === 1) {\n        return false;\n    }\n\n    if (\n        (isDraggingSecondLevel && isDropFirstLevel) ||\n        (isDraggingFirstLevel && isDropSecondLevel && draggingNode.childNodes?.length === 0)\n    ) {\n        return type === 'prev' || type === 'next';\n    }\n\n    if ((isDropFirstLevel && isDraggingFirstLevel) || (isDropSecondLevel && isDraggingSecondLevel)) {\n        return type === 'prev' || type === 'next';\n    }\n\n    return false;\n};\n\nconst handleDrop = (draggingNode: Node, dropNode: Node) => {\n    const siblingNodes = dropNode.level == 2 ? dropNode.parent.parent.data : dropNode.parent.data;\n    siblingNodes.forEach((node, index) => {\n        node.sort = (index + 1) * 100;\n    });\n\n    const updateChildSort = (nodes) => {\n        nodes.forEach((node, index) => {\n            node.sort = (index + 1) * 100;\n            if (node.children && node.children.length) {\n                updateChildSort(node.children);\n            }\n        });\n    };\n\n    if (siblingNodes.length) {\n        siblingNodes.forEach((node) => {\n            if (node.children && node.children.length) {\n                updateChildSort(node.children);\n            }\n        });\n    }\n};\n\nconst treeData = reactive({\n    hideMenu: [],\n    checkedData: [],\n});\n\nconst removeUpage = (data: any): any => {\n    return data\n        .filter((item: { label: string }) => item.label !== 'Upage')\n        .map((item: { children: any }) => {\n            if (Array.isArray(item.children)) {\n                item.children = removeUpage(item.children);\n            }\n            return item;\n        });\n};\n\nconst onChangeShow = async (row: any) => {\n    if (row.children) {\n        for (const item of row.children) {\n            item.isShow = row.isShow;\n        }\n        return;\n    }\n    for (const item of treeData.hideMenu) {\n        if (!item.children) {\n            continue;\n        }\n        let allHide = true;\n        for (const item2 of item.children) {\n            if (item2.isShow) {\n                allHide = false;\n            }\n            if (item2.id === row.id && item2.isShow) {\n                item.isShow = true;\n                return;\n            }\n        }\n        if (allHide) {\n            item.isShow = false;\n        }\n    }\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst saveHideMenus = async () => {\n    ElMessageBox.confirm(i18n.global.t('setting.confirmMessage'), i18n.global.t('setting.menuSetting'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        const updateJson = JSON.stringify(treeData.hideMenu);\n        await updateMenu({ key: 'HideMenu', value: updateJson })\n            .then(async () => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loading.value = false;\n                drawerVisible.value = false;\n                window.location.reload();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst defaultHideMenus = async () => {\n    ElMessageBox.confirm(i18n.global.t('setting.recoverMessage'), i18n.global.t('setting.menuSetting'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        await defaultMenu()\n            .then(async () => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loading.value = false;\n                drawerVisible.value = false;\n                window.location.reload();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style scoped lang=\"scss\">\n:deep(.el-tree) {\n    --el-tree-node-content-height: 26px;\n    font-size: 16px;\n}\n:deep(.el-tree-node__content) {\n    padding: 8px 8px !important;\n    border-bottom: var(--panel-border);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent :title=\"$t('setting.panel')\" :divider=\"true\">\n            <template #main>\n                <el-form :model=\"form\" :label-position=\"mobile ? 'top' : 'left'\" label-width=\"150px\">\n                    <el-row>\n                        <el-col :span=\"1\"><br /></el-col>\n                        <el-col :xs=\"24\" :sm=\"20\" :md=\"15\" :lg=\"12\" :xl=\"12\">\n                            <el-form-item :label=\"$t('setting.user')\" prop=\"userName\">\n                                <el-input disabled v-model=\"form.userName\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeUserName()\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.passwd')\" prop=\"password\">\n                                <el-input type=\"password\" disabled v-model=\"form.password\">\n                                    <template #append>\n                                        <el-button icon=\"Setting\" @click=\"onChangePassword\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.theme')\" prop=\"theme\">\n                                <div class=\"flex justify-center items-center sm:gap-6 gap-2\">\n                                    <div class=\"sm:contents hidden\">\n                                        <el-radio-group @change=\"onSave('Theme', form.theme)\" v-model=\"form.theme\">\n                                            <el-radio-button value=\"light\">\n                                                <span>{{ $t('setting.light') }}</span>\n                                            </el-radio-button>\n                                            <el-radio-button value=\"dark\">\n                                                <span>{{ $t('setting.dark') }}</span>\n                                            </el-radio-button>\n                                            <el-radio-button value=\"auto\">\n                                                <span>{{ $t('setting.auto') }}</span>\n                                            </el-radio-button>\n                                        </el-radio-group>\n                                    </div>\n                                    <div class=\"sm:hidden block w-32 !h-[33.5px]\">\n                                        <el-select @change=\"onSave('Theme', form.theme)\" v-model=\"form.theme\">\n                                            <el-option key=\"light\" value=\"light\" :label=\"$t('setting.light')\">\n                                                {{ $t('setting.light') }}\n                                            </el-option>\n                                            <el-option key=\"dark\" value=\"dark\" :label=\"$t('setting.dark')\">\n                                                {{ $t('setting.dark') }}\n                                            </el-option>\n                                            <el-option key=\"auto\" value=\"auto\" :label=\"$t('setting.auto')\">\n                                                {{ $t('setting.auto') }}\n                                            </el-option>\n                                        </el-select>\n                                    </div>\n                                    <div>\n                                        <el-button\n                                            v-if=\"isMasterProductPro\"\n                                            @click=\"onChangeThemeColor\"\n                                            icon=\"Setting\"\n                                            class=\"!h-[32px] sm:!h-[33.5px]\"\n                                        >\n                                            <span>{{ $t('container.custom') }}</span>\n                                        </el-button>\n                                    </div>\n                                </div>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.menuTabs')\" prop=\"menuTabs\">\n                                <el-radio-group @change=\"onSave('MenuTabs', form.menuTabs)\" v-model=\"form.menuTabs\">\n                                    <el-radio-button value=\"Enable\">\n                                        <span>{{ $t('commons.button.enable') }}</span>\n                                    </el-radio-button>\n                                    <el-radio-button value=\"Disable\">\n                                        <span>{{ $t('commons.button.disable') }}</span>\n                                    </el-radio-button>\n                                </el-radio-group>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.watermark')\" v-if=\"isMasterProductPro\" prop=\"watermark\">\n                                <el-radio-group class=\"w-full\" @change=\"onChangeWatermark\" v-model=\"form.watermarkShow\">\n                                    <el-radio-button value=\"Enable\">\n                                        <span>{{ $t('commons.button.enable') }}</span>\n                                    </el-radio-button>\n                                    <el-radio-button value=\"Disable\">\n                                        <span>{{ $t('commons.button.disable') }}</span>\n                                    </el-radio-button>\n                                </el-radio-group>\n                                <div v-if=\"form.watermarkShow === 'Enable'\">\n                                    <div>\n                                        <el-button link type=\"primary\" @click=\"onChangeWatermark\">\n                                            {{ $t('commons.button.view') }}\n                                        </el-button>\n                                    </div>\n                                </div>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.title')\" prop=\"panelName\">\n                                <el-input disabled v-model=\"form.panelName\">\n                                    <template #append>\n                                        <el-button icon=\"Setting\" @click=\"onChangeTitle\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.language')\" prop=\"language\">\n                                <el-select\n                                    class=\"sm:!w-1/2 !w-full\"\n                                    @change=\"onSave('Language', form.language)\"\n                                    v-model=\"form.language\"\n                                >\n                                    <el-option\n                                        v-for=\"option in languageOptions\"\n                                        :key=\"option.value\"\n                                        :value=\"option.value\"\n                                        :label=\"option.label\"\n                                    >\n                                        {{ option.label }}\n                                    </el-option>\n                                </el-select>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.sessionTimeout')\" prop=\"sessionTimeout\">\n                                <el-input disabled v-model.number=\"form.sessionTimeout\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeTimeout\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">\n                                    {{ $t('setting.sessionTimeoutHelper', [form.sessionTimeout]) }}\n                                </span>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.systemIP')\" prop=\"systemIP\">\n                                <el-input disabled v-if=\"form.systemIP\" v-model=\"form.systemIP\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeSystemIP\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <el-input disabled v-if=\"!form.systemIP\" v-model=\"unset\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeSystemIP\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('setting.systemIPHelper') }}</span>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.proxy')\" prop=\"proxyShow\" v-if=\"isMaster\">\n                                <el-input disabled v-model=\"form.proxyShow\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeProxy\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.apiInterface')\" prop=\"apiInterface\" v-if=\"isMaster\">\n                                <el-switch\n                                    @change=\"onChangeApiInterfaceStatus\"\n                                    v-model=\"form.apiInterfaceStatus\"\n                                    active-value=\"Enable\"\n                                    inactive-value=\"Disable\"\n                                />\n                                <span class=\"input-help\">{{ $t('setting.apiInterfaceHelper') }}</span>\n                                <div v-if=\"form.apiInterfaceStatus === 'Enable'\">\n                                    <div>\n                                        <el-button link type=\"primary\" @click=\"onChangeApiInterfaceStatus\">\n                                            {{ $t('commons.button.view') }}\n                                        </el-button>\n                                    </div>\n                                </div>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.developerMode')\" prop=\"developerMode\">\n                                <el-radio-group\n                                    @change=\"onSave('DeveloperMode', form.developerMode)\"\n                                    v-model=\"form.developerMode\"\n                                >\n                                    <el-radio-button value=\"Enable\">\n                                        <span>{{ $t('commons.button.enable') }}</span>\n                                    </el-radio-button>\n                                    <el-radio-button value=\"Disable\">\n                                        <span>{{ $t('commons.button.disable') }}</span>\n                                    </el-radio-button>\n                                </el-radio-group>\n                                <span class=\"input-help\">{{ $t('setting.developerModeHelper') }}</span>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.menuSetting')\">\n                                <el-button v-show=\"!show\" @click=\"onChangeHideMenus\" icon=\"Setting\">\n                                    {{ $t('commons.button.set') }}\n                                </el-button>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.runtimeEnv')\" prop=\"edition\">\n                                <el-button icon=\"Setting\" @click=\"onChangeRegion\">\n                                    {{ runtimeEnvLabel() }}\n                                </el-button>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                </el-form>\n            </template>\n        </LayoutContent>\n\n        <Password ref=\"passwordRef\" />\n        <UserName ref=\"userNameRef\" />\n        <PanelName ref=\"panelNameRef\" @search=\"search()\" />\n        <SystemIP ref=\"systemIPRef\" @search=\"search()\" />\n        <Proxy ref=\"proxyRef\" @search=\"search()\" />\n        <ApiInterface ref=\"apiInterfaceRef\" @search=\"search()\" />\n        <Timeout ref=\"timeoutRef\" @search=\"search()\" />\n        <HideMenu ref=\"hideMenuRef\" @search=\"search()\" />\n        <ThemeColor ref=\"themeColorRef\" />\n        <Watermark ref=\"watermarkRef\" @search=\"search()\" />\n        <Edition ref=\"editionRef\" @search=\"search()\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, reactive, onMounted, computed } from 'vue';\nimport { ElForm, ElMessageBox } from 'element-plus';\nimport {\n    getSettingInfo,\n    updateSetting,\n    getSystemAvailable,\n    updateApiConfig,\n    getAgentSettingInfo,\n} from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nimport { useTheme } from '@/global/use-theme';\nimport { MsgSuccess } from '@/utils/message';\nimport ThemeColor from '@/views/setting/panel/theme-color/index.vue';\nimport ApiInterface from '@/views/setting/panel/api-interface/index.vue';\nimport Password from '@/views/setting/panel/password/index.vue';\nimport Watermark from '@/views/setting/panel/watermark/index.vue';\nimport UserName from '@/views/setting/panel/username/index.vue';\nimport Timeout from '@/views/setting/panel/timeout/index.vue';\nimport PanelName from '@/views/setting/panel/name/index.vue';\nimport SystemIP from '@/views/setting/panel/systemip/index.vue';\nimport Proxy from '@/views/setting/panel/proxy/index.vue';\nimport HideMenu from '@/views/setting/panel/hidemenu/index.vue';\nimport Edition from '@/views/setting/panel/edition/index.vue';\nimport { storeToRefs } from 'pinia';\nimport { getXpackSetting, updateXpackSettingByKey } from '@/utils/xpack';\nimport { setPrimaryColor } from '@/utils/theme';\nimport i18n from '@/lang';\n\nconst loading = ref(false);\nconst globalStore = GlobalStore();\n\nconst { isMasterProductPro, isMaster } = storeToRefs(globalStore);\n\nconst { switchTheme } = useTheme();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\ninterface ThemeColor {\n    light: string;\n    dark: string;\n    themePredefineColors: {\n        light: string[];\n        dark: string[];\n    };\n}\n\nconst form = reactive({\n    userName: '',\n    password: '',\n    sessionTimeout: 0,\n    panelName: '',\n    theme: '',\n    watermark: '',\n    watermarkShow: '',\n    themeColor: {} as ThemeColor,\n    menuTabs: '',\n    language: '',\n    docSource: 'withByRegion',\n    edition: '',\n    complexityVerification: '',\n    developerMode: '',\n    systemIP: '',\n\n    proxyShow: '',\n    proxyUrl: '',\n    proxyType: '',\n    proxyPort: '',\n    proxyUser: '',\n    proxyPasswd: '',\n    proxyPasswdKeep: '',\n    proxyDocker: '',\n\n    apiInterfaceStatus: 'Disable',\n    apiKey: '',\n    ipWhiteList: '',\n    apiKeyValidityTime: 120,\n\n    hideMenu: '',\n});\n\nconst show = ref();\n\nconst userNameRef = ref();\nconst passwordRef = ref();\nconst panelNameRef = ref();\nconst systemIPRef = ref();\nconst proxyRef = ref();\nconst timeoutRef = ref();\nconst hideMenuRef = ref();\nconst watermarkRef = ref();\nconst themeColorRef = ref();\nconst apiInterfaceRef = ref();\nconst editionRef = ref();\nconst unset = ref(i18n.global.t('setting.unSetting'));\n\nconst languageOptions = ref([\n    { value: 'zh', label: '中文(简体)' },\n    { value: 'zh-Hant', label: '中文(繁體)' },\n    ...(!globalStore.isIntl ? [{ value: 'en', label: 'English' }] : []),\n    { value: 'ja', label: '日本語' },\n    { value: 'pt-BR', label: 'Português (Brasil)' },\n    { value: 'ko', label: '한국어' },\n    { value: 'ru', label: 'Русский' },\n    { value: 'ms', label: 'Bahasa Melayu' },\n    { value: 'tr', label: 'Turkish' },\n    { value: 'es-ES', label: 'España - Español' },\n]);\n\nif (globalStore.isIntl) {\n    languageOptions.value.unshift({ value: 'en', label: 'English' });\n}\n\nconst search = async () => {\n    const agentRes = await getAgentSettingInfo();\n    form.systemIP = agentRes.data.systemIP;\n\n    const res = await getSettingInfo();\n    form.userName = res.data.userName;\n    form.password = '******';\n    form.theme = res.data.theme;\n    form.menuTabs = res.data.menuTabs;\n    form.panelName = res.data.panelName;\n    form.language = res.data.language;\n    form.docSource = res.data.docSource || 'withByRegion';\n    form.edition = res.data.edition;\n    form.sessionTimeout = Number(res.data.sessionTimeout);\n\n    form.proxyUrl = res.data.proxyUrl;\n    form.proxyType = res.data.proxyType;\n    form.proxyPort = res.data.proxyPort;\n    form.proxyShow = form.proxyUrl ? form.proxyUrl + ':' + form.proxyPort : unset.value;\n    form.proxyUser = res.data.proxyUser;\n    form.proxyPasswd = res.data.proxyPasswd;\n    form.proxyPasswdKeep = res.data.proxyPasswdKeep;\n\n    form.apiInterfaceStatus = res.data.apiInterfaceStatus;\n    form.apiKey = res.data.apiKey;\n    form.ipWhiteList = res.data.ipWhiteList;\n    form.apiKeyValidityTime = res.data.apiKeyValidityTime;\n\n    form.developerMode = res.data.developerMode;\n    form.hideMenu = res.data.hideMenu;\n\n    form.complexityVerification = res.data.complexityVerification;\n\n    if (isMasterProductPro.value) {\n        const xpackRes = await getXpackSetting();\n        if (xpackRes) {\n            form.theme = xpackRes.data.theme || globalStore.themeConfig.theme || 'light';\n            form.themeColor = JSON.parse(xpackRes.data.themeColor || '{\"light\":\"#005eeb\",\"dark\":\"#F0BE96\"}');\n            globalStore.themeConfig.themeColor = xpackRes.data.themeColor\n                ? xpackRes.data.themeColor\n                : '{\"light\":\"#005eeb\",\"dark\":\"#F0BE96\"}';\n            globalStore.themeConfig.theme = form.theme;\n            form.proxyDocker = xpackRes.data.proxyDocker;\n            form.watermark = xpackRes.data.watermark;\n            form.watermarkShow = xpackRes.data.watermarkShow;\n            try {\n                globalStore.watermark = JSON.parse(xpackRes.data.watermark);\n            } catch {\n                globalStore.watermark = null;\n            }\n        }\n    } else {\n        globalStore.themeConfig.theme = form.theme;\n    }\n};\n\nconst onChangePassword = () => {\n    passwordRef.value.acceptParams({ complexityVerification: form.complexityVerification });\n};\nconst onChangeUserName = () => {\n    userNameRef.value.acceptParams({ userName: form.userName });\n};\nconst onChangeTitle = () => {\n    panelNameRef.value.acceptParams({ panelName: form.panelName });\n};\nconst onChangeTimeout = () => {\n    timeoutRef.value.acceptParams({ sessionTimeout: form.sessionTimeout });\n};\nconst onChangeSystemIP = () => {\n    systemIPRef.value.acceptParams({ systemIP: form.systemIP });\n};\nconst onChangeProxy = () => {\n    proxyRef.value.acceptParams({\n        url: form.proxyUrl,\n        type: form.proxyType,\n        port: form.proxyPort,\n        user: form.proxyUser,\n        passwd: form.proxyPasswd,\n        passwdKeep: form.proxyPasswdKeep,\n        proxyDocker: form.proxyDocker,\n    });\n};\n\nconst onChangeHideMenus = () => {\n    hideMenuRef.value.acceptParams({ hideMenu: form.hideMenu });\n};\n\nconst onChangeRegion = () => {\n    editionRef.value.acceptParams({ edition: form.edition, docSource: form.docSource });\n};\n\nconst runtimeEnvLabel = () => {\n    const editionLabel = form.edition === 'cn' ? i18n.global.t('setting.cn') : i18n.global.t('setting.intl');\n    const docSourceLabel = i18n.global.t(`setting.${form.docSource || 'withByRegion'}`);\n    return `${editionLabel} / ${docSourceLabel}`;\n};\n\nconst onChangeThemeColor = () => {\n    const themeColor: ThemeColor = JSON.parse(globalStore.themeConfig.themeColor);\n    themeColorRef.value.acceptParams({ themeColor: themeColor, theme: globalStore.themeConfig.theme });\n};\n\nconst onChangeWatermark = async () => {\n    if (form.watermarkShow === 'Enable') {\n        watermarkRef.value.acceptParams(form.watermark);\n        return;\n    }\n    ElMessageBox.confirm(i18n.global.t('setting.watermarkCloseHelper'), i18n.global.t('setting.watermark'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    })\n        .then(async () => {\n            loading.value = true;\n            await updateXpackSettingByKey('WatermarkShow', 'Disable')\n                .then(() => {\n                    loading.value = false;\n                    globalStore.watermark = null;\n                    globalStore.watermarkShow = false;\n                    search();\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            form.watermarkShow = 'Enable';\n        });\n};\n\nconst onChangeApiInterfaceStatus = async () => {\n    if (form.apiInterfaceStatus === 'Enable') {\n        apiInterfaceRef.value.acceptParams({\n            apiInterfaceStatus: form.apiInterfaceStatus,\n            apiKey: form.apiKey,\n            ipWhiteList: form.ipWhiteList,\n            apiKeyValidityTime: form.apiKeyValidityTime,\n        });\n        return;\n    }\n    ElMessageBox.confirm(i18n.global.t('setting.apiInterfaceClose'), i18n.global.t('setting.apiInterface'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    })\n        .then(async () => {\n            loading.value = true;\n            form.apiInterfaceStatus = 'Disable';\n            let param = {\n                apiKey: form.apiKey,\n                ipWhiteList: form.ipWhiteList,\n                apiInterfaceStatus: form.apiInterfaceStatus,\n                apiKeyValidityTime: form.apiKeyValidityTime,\n            };\n            await updateApiConfig(param)\n                .then(() => {\n                    loading.value = false;\n                    search();\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            form.apiInterfaceStatus = 'Enable';\n        });\n};\n\nconst handleThemeChange = async (val: string) => {\n    globalStore.themeConfig.theme = val;\n    switchTheme();\n    if (globalStore.isMasterProductPro) {\n        await updateXpackSettingByKey('Theme', val);\n        let color: string;\n        const themeColor: ThemeColor = JSON.parse(globalStore.themeConfig.themeColor);\n        if (val === 'auto') {\n            const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');\n            color = prefersDark.matches ? themeColor.dark : themeColor.light;\n        } else {\n            color = val === 'dark' ? themeColor.dark : themeColor.light;\n        }\n        globalStore.themeConfig.primary = color;\n        setPrimaryColor(color);\n    }\n};\nconst onSave = async (key: string, val: any) => {\n    loading.value = true;\n    let param = {\n        key: key,\n        value: val + '',\n    };\n    try {\n        await updateSetting(param);\n        switch (key) {\n            case 'Theme':\n                handleThemeChange(val);\n                break;\n            case 'MenuTabs':\n                globalStore.setOpenMenuTabs(val === 'Enable');\n                break;\n            case 'Language':\n                await globalStore.updateLanguage(val);\n                location.reload();\n                break;\n        }\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        search();\n    } catch (error) {\n        loading.value = false;\n        return;\n    }\n    loading.value = false;\n};\n\nonMounted(() => {\n    search();\n    getSystemAvailable();\n});\n</script>\n\n<style scoped lang=\"scss\">\n:deep(.el-radio-group) {\n    min-width: max-content;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/name/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.title')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.title')\" prop=\"panelName\">\n                <el-input clearable v-model=\"form.panelName\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSavePanelName(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { computed, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateSetting } from '@/api/modules/setting';\nimport { FormInstance } from 'element-plus';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\nconst themeConfig = computed(() => globalStore.themeConfig);\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    panelName: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    panelName: '',\n});\nconst rules = reactive({\n    panelName: [{ validator: checkPanelName, trigger: 'blur', required: true }],\n});\n\nfunction checkPanelName(rule: any, value: any, callback: any) {\n    if (value === '') {\n        return callback(new Error(i18n.global.t('setting.titleHelper')));\n    }\n    const reg = /^[a-zA-Z0-9\\u4e00-\\u9fa5 .,:!@#%&^*_+[\\]{}~\\-=?，。！｜？：；「」『』【】（）《》·]{3,30}$/;\n    if (!reg.test(value)) {\n        return callback(new Error(i18n.global.t('setting.titleHelper')));\n    }\n    callback();\n}\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.panelName = params.panelName;\n    drawerVisible.value = true;\n};\n\nconst onSavePanelName = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        await updateSetting({ key: 'PanelName', value: form.panelName })\n            .then(async () => {\n                globalStore.setThemeConfig({ ...themeConfig.value, panelName: form.panelName });\n                document.title = form.panelName;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                return;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/password/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"passwordVisible\" :header=\"$t('setting.changePassword')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"passFormRef\" label-position=\"top\" :model=\"passForm\" :rules=\"passRules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.oldPassword')\" prop=\"oldPassword\">\n                <el-input type=\"password\" show-password clearable v-model.trim=\"passForm.oldPassword\" />\n            </el-form-item>\n            <el-form-item\n                v-if=\"complexityVerification === 'Disable'\"\n                :label=\"$t('setting.newPassword')\"\n                prop=\"newPassword\"\n            >\n                <el-input type=\"password\" show-password clearable v-model.trim=\"passForm.newPassword\" />\n            </el-form-item>\n            <el-form-item\n                v-if=\"complexityVerification === 'Enable'\"\n                :label=\"$t('setting.newPassword')\"\n                prop=\"newPasswordComplexity\"\n            >\n                <el-input type=\"password\" show-password clearable v-model.trim=\"passForm.newPasswordComplexity\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.retryPassword')\" prop=\"retryPassword\">\n                <el-input type=\"password\" show-password clearable v-model.trim=\"passForm.retryPassword\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button :disabled=\"loading\" @click=\"passwordVisible = false\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"submitChangePassword(passFormRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport router from '@/routers';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { GlobalStore } from '@/store';\nimport { reactive, ref } from 'vue';\nimport { updatePassword } from '@/api/modules/setting';\nimport { logOutApi } from '@/api/modules/auth';\n\nconst globalStore = GlobalStore();\nconst passFormRef = ref<FormInstance>();\nconst passRules = reactive({\n    oldPassword: [Rules.requiredInput, Rules.noSpace],\n    newPassword: [Rules.requiredInput, Rules.noSpace],\n    newPasswordComplexity: [Rules.requiredInput, Rules.noSpace, Rules.password],\n    retryPassword: [Rules.requiredInput, Rules.noSpace, { validator: checkPassword, trigger: 'blur' }],\n});\n\nconst loading = ref(false);\nconst passwordVisible = ref<boolean>(false);\nconst passForm = reactive({\n    oldPassword: '',\n    newPassword: '',\n    newPasswordComplexity: '',\n    retryPassword: '',\n});\nconst complexityVerification = ref();\n\ninterface DialogProps {\n    complexityVerification: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    complexityVerification.value = params.complexityVerification;\n    passForm.oldPassword = '';\n    passForm.newPassword = '';\n    passForm.newPasswordComplexity = '';\n    passForm.retryPassword = '';\n    passwordVisible.value = true;\n};\n\nfunction checkPassword(rule: any, value: any, callback: any) {\n    let password = complexityVerification.value === 'Disable' ? passForm.newPassword : passForm.newPasswordComplexity;\n    if (password !== passForm.retryPassword) {\n        return callback(new Error(i18n.global.t('commons.rule.rePassword')));\n    }\n    callback();\n}\n\nconst submitChangePassword = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let password =\n            complexityVerification.value === 'Disable' ? passForm.newPassword : passForm.newPasswordComplexity;\n        if (password === passForm.oldPassword) {\n            MsgError(i18n.global.t('setting.duplicatePassword'));\n            return;\n        }\n        loading.value = true;\n        await updatePassword({ oldPassword: passForm.oldPassword, newPassword: password })\n            .then(async () => {\n                loading.value = false;\n                passwordVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                await logOutApi();\n                router.push({ name: 'entrance', params: { code: globalStore.entrance } });\n                globalStore.setLogStatus(false);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst handleClose = () => {\n    passwordVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/proxy/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"proxyVisible\" :header=\"$t('setting.proxy')\" @close=\"handleClose\" size=\"large\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" v-loading=\"loading\">\n            <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\">\n                <template #default>\n                    {{ $t('setting.proxyHelper') }}\n                    <ul class=\"-ml-5\">\n                        <li>{{ $t('setting.proxyHelper1') }}</li>\n                        <li>{{ $t('setting.proxyHelper5') }}</li>\n                        <li>{{ $t('setting.proxyHelper2') }}</li>\n                        <li>{{ $t('setting.proxyHelper4') }}</li>\n                        <li>{{ $t('setting.proxyHelper6') }}</li>\n                        <li>{{ $t('setting.proxyHelper3') }}</li>\n                    </ul>\n                </template>\n            </el-alert>\n            <el-form-item :label=\"$t('setting.proxyType')\" prop=\"proxyType\">\n                <el-select v-model=\"form.proxyType\" clearable>\n                    <el-option value=\"close\" :label=\"$t('commons.button.close')\" />\n                    <el-option value=\"socks5\" label=\"SOCKS5\" />\n                    <el-option value=\"http\" label=\"HTTP\" />\n                    <el-option value=\"https\" label=\"HTTPS\" />\n                </el-select>\n            </el-form-item>\n            <div v-if=\"form.proxyType !== 'close'\">\n                <el-form-item :label=\"$t('setting.proxyUrl')\" prop=\"proxyUrl\">\n                    <el-input\n                        clearable\n                        v-model.trim=\"form.proxyUrl\"\n                        v-if=\"form.proxyType == 'http' || form.proxyType === 'https'\"\n                    >\n                        <template #prepend>\n                            <span>{{ form.proxyType }}</span>\n                        </template>\n                    </el-input>\n                    <el-input clearable v-model.trim=\"form.proxyUrl\" v-else />\n                </el-form-item>\n                <el-form-item :label=\"$t('setting.proxyPort')\" prop=\"proxyPortItem\">\n                    <el-input clearable type=\"number\" v-model.number=\"form.proxyPortItem\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.username')\" prop=\"proxyUser\">\n                    <el-input clearable v-model.trim=\"form.proxyUser\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('commons.login.password')\" prop=\"proxyPasswd\">\n                    <el-input type=\"password\" show-password clearable v-model.trim=\"form.proxyPasswd\" />\n                </el-form-item>\n                <el-form-item>\n                    <el-checkbox v-model=\"form.proxyPasswdKeepItem\" :label=\"$t('setting.proxyPasswdKeep')\" />\n                </el-form-item>\n                <el-form-item v-if=\"isMasterProductPro\">\n                    <el-checkbox v-model=\"form.proxyDocker\" :label=\"$t('setting.proxyDocker')\" />\n                    <span class=\"input-help\">{{ $t('setting.proxyDockerHelper') }}</span>\n                </el-form-item>\n            </div>\n        </el-form>\n        <template #footer>\n            <el-button :disabled=\"loading\" @click=\"proxyVisible = false\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"submitChangePassword(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n\n    <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSubmit\" />\n    <DockerProxyDialog ref=\"dockerProxyRef\" @submit=\"onSubmit\" v-model:with-docker-restart=\"withDockerRestart\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport { updateProxy } from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nimport { storeToRefs } from 'pinia';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport DockerProxyDialog from '@/components/docker-proxy/dialog.vue';\nimport { loadDockerStatus } from '@/api/modules/container';\n\nconst globalStore = GlobalStore();\nconst emit = defineEmits<{ (e: 'search'): void }>();\nconst { isMasterProductPro } = storeToRefs(globalStore);\n\nconst confirmDialogRef = ref();\nconst formRef = ref<FormInstance>();\nconst rules = reactive({\n    proxyType: [Rules.requiredSelect],\n    proxyUrl: [Rules.noSpace, Rules.requiredInput],\n    proxyPortItem: [Rules.port],\n});\n\nconst loading = ref(false);\nconst proxyVisible = ref<boolean>(false);\nconst proxyDockerVisible = ref<boolean>(false);\nconst form = reactive({\n    proxyUrl: '',\n    proxyType: '',\n    proxyPort: '',\n    proxyPortItem: 7890,\n    proxyUser: '',\n    proxyPasswd: '',\n    proxyPasswdKeep: '',\n    proxyPasswdKeepItem: false,\n    proxyDocker: false,\n});\nconst dockerStatus = ref();\nconst withDockerRestart = ref(false);\nconst dockerProxyRef = ref();\n\ninterface DialogProps {\n    url: string;\n    type: string;\n    port: string;\n    user: string;\n    passwd: string;\n    passwdKeep: string;\n    proxyDocker: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    if (params.url) {\n        if (params.type === 'http' || params.type === 'https') {\n            form.proxyUrl = params.url.replaceAll(params.type + '://', '');\n        } else {\n            form.proxyUrl = params.url;\n        }\n    } else {\n        form.proxyUrl = '127.0.0.1';\n    }\n    form.proxyType = params.type || 'close';\n    form.proxyPortItem = params.port ? Number(params.port) : 7890;\n    form.proxyUser = params.user;\n    form.proxyPasswd = params.passwd;\n    form.proxyDocker = params.proxyDocker !== '';\n    proxyDockerVisible.value = params.proxyDocker !== '';\n    proxyVisible.value = true;\n    form.proxyPasswdKeepItem = params.passwdKeep === 'Enable';\n    loadDocker();\n};\n\nconst loadDocker = async () => {\n    const res = await loadDockerStatus();\n    dockerStatus.value = res.data.isExist;\n};\n\nconst submitChangePassword = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let isClose = form.proxyType === '' || form.proxyType === 'close';\n        let params = {\n            proxyType: isClose ? '' : form.proxyType,\n            proxyUrl: isClose ? '' : form.proxyUrl,\n            proxyPort: isClose ? '' : form.proxyPortItem + '',\n            proxyUser: isClose ? '' : form.proxyUser,\n            proxyPasswd: isClose ? '' : form.proxyPasswd,\n            proxyPasswdKeep: '',\n            proxyDocker: isClose ? false : form.proxyDocker,\n            withDockerRestart: false,\n        };\n        if (!isClose) {\n            params.proxyPasswdKeep = form.proxyPasswdKeepItem ? 'Enable' : 'Disable';\n        }\n        if (form.proxyType === 'http' || form.proxyType === 'https') {\n            params.proxyUrl = form.proxyUrl;\n        }\n        if (dockerStatus.value && isMasterProductPro.value && (params.proxyDocker || proxyDockerVisible.value)) {\n            dockerProxyRef.value.acceptParams({\n                syncList: 'SyncSystemProxy',\n                open: true,\n            });\n        } else {\n            loading.value = true;\n            await updateProxy(params)\n                .then(async () => {\n                    loading.value = false;\n                    emit('search');\n                    proxyVisible.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\nconst onSubmit = async () => {\n    try {\n        loading.value = true;\n        let isClose = form.proxyType === '' || form.proxyType === 'close';\n        let params = {\n            proxyType: isClose ? '' : form.proxyType,\n            proxyUrl: isClose ? '' : form.proxyUrl,\n            proxyPort: isClose ? '' : form.proxyPortItem + '',\n            proxyUser: isClose ? '' : form.proxyUser,\n            proxyPasswd: isClose ? '' : form.proxyPasswd,\n            proxyPasswdKeep: '',\n            proxyDocker: isClose ? false : form.proxyDocker,\n            withDockerRestart: withDockerRestart.value,\n        };\n        if (!isClose) {\n            params.proxyPasswdKeep = form.proxyPasswdKeepItem ? 'Enable' : 'Disable';\n        }\n        if (form.proxyType === 'http' || form.proxyType === 'https') {\n            params.proxyUrl = form.proxyUrl;\n        }\n        await updateProxy(params);\n        emit('search');\n        handleClose();\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst handleClose = () => {\n    proxyVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/systemip/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.systemIP')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.systemIP')\" prop=\"systemIP\">\n                <el-input clearable v-model=\"form.systemIP\" />\n                <span class=\"input-help\">{{ $t('commons.rule.hostHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveSystemIP(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateAgentSetting } from '@/api/modules/setting';\nimport { FormInstance } from 'element-plus';\nimport { checkDomain, checkIpV4V6 } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    systemIP: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    systemIP: '',\n});\n\nconst formRef = ref<FormInstance>();\nconst rules = reactive({\n    systemIP: [{ validator: checkSystemIP, trigger: 'blur' }],\n});\n\nfunction checkSystemIP(rule: any, value: any, callback: any) {\n    if (form.systemIP !== '') {\n        if (checkIpV4V6(form.systemIP) && checkDomain(form.systemIP)) {\n            return callback(new Error(i18n.global.t('commons.rule.host')));\n        }\n    }\n    callback();\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    form.systemIP = params.systemIP;\n    drawerVisible.value = true;\n};\n\nconst onSaveSystemIP = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        await updateAgentSetting({ key: 'SystemIP', value: form.systemIP })\n            .then(async () => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                return;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/theme-color/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('xpack.theme.customColor')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-row type=\"flex\" justify=\"center\">\n                <el-col :span=\"22\">\n                    <el-form-item :label=\"$t('setting.light')\" prop=\"light\">\n                        <div class=\"flex flex-wrap justify-between items-center sm:items-start\">\n                            <div class=\"flex gap-1\">\n                                <template v-for=\"colorConfig in lightColors\" :key=\"colorConfig.color\">\n                                    <el-tooltip :content=\"$t(colorConfig.label)\" placement=\"top\">\n                                        <el-button\n                                            :color=\"colorConfig.color\"\n                                            :class=\"form.light === colorConfig.color ? 'selected-white' : ''\"\n                                            circle\n                                            dark\n                                            @click=\"changeLightColor(colorConfig.color)\"\n                                        />\n                                    </el-tooltip>\n                                </template>\n                                <el-color-picker\n                                    v-model=\"form.light\"\n                                    class=\"ml-4\"\n                                    :predefine=\"lightPredefineColors\"\n                                    show-alpha\n                                    @change=\"changeThemeColor('light', form.light)\"\n                                />\n                            </div>\n                        </div>\n                    </el-form-item>\n\n                    <el-form-item :label=\"$t('setting.dark')\" prop=\"dark\">\n                        <div class=\"flex flex-wrap justify-between items-center sm:items-start\">\n                            <div class=\"flex flex-wrap justify-between items-center mt-4 sm:mt-0\">\n                                <div class=\"flex gap-1\">\n                                    <template v-for=\"colorConfig in darkColors\" :key=\"colorConfig.color\">\n                                        <el-tooltip :content=\"$t(colorConfig.label)\" placement=\"top\">\n                                            <el-button\n                                                :color=\"colorConfig.color\"\n                                                :class=\"form.dark === colorConfig.color ? 'selected-white' : ''\"\n                                                circle\n                                                dark\n                                                @click=\"changeDarkColor(colorConfig.color)\"\n                                            />\n                                        </el-tooltip>\n                                    </template>\n                                    <el-color-picker\n                                        v-model=\"form.dark\"\n                                        class=\"ml-4\"\n                                        :predefine=\"darkPredefineColors\"\n                                        show-alpha\n                                        @change=\"changeThemeColor('dark', form.dark)\"\n                                    />\n                                </div>\n                            </div>\n                        </div>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"onReSet\">{{ $t('commons.button.setDefault') }}</el-button>\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { initFavicon, updateXpackSettingByKey } from '@/utils/xpack';\nimport { setPrimaryColor } from '@/utils/theme';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\nconst emit = defineEmits<(e: 'search') => void>();\nconst drawerVisible = ref();\nconst loading = ref();\ninterface DialogProps {\n    themeColor: {\n        light: string;\n        dark: string;\n        themePredefineColors: {\n            light: string[];\n            dark: string[];\n        };\n    };\n    theme: '';\n}\ninterface ThemeColor {\n    light: string;\n    dark: string;\n    themePredefineColors: {\n        light: string[];\n        dark: string[];\n    };\n}\nconst form = reactive({\n    themeColor: {} as ThemeColor,\n    theme: '',\n    light: '',\n    dark: '',\n});\nconst formRef = ref<FormInstance>();\nconst STORAGE_KEY = 'theme-predefine-colors';\nconst lightPredefineColors = ref([\n    '#005eeb',\n    '#238636',\n    '#3D8EFF',\n    '#F0BE96',\n    '#00ced1',\n    '#c71585',\n    '#ff4500',\n    '#ff8c00',\n    '#ffd700',\n    '#333539',\n]);\nconst darkPredefineColors = ref([\n    '#238636',\n    '#3D8EFF',\n    '#005eeb',\n    '#F0BE96',\n    '#00ced1',\n    '#c71585',\n    '#ff4500',\n    '#ff8c00',\n    '#ffd700',\n    '#333539',\n]);\nconst defaultDarkColors = [\n    { color: '#3D8EFF', label: 'xpack.theme.classicBlue' },\n    { color: '#F0BE96', label: 'xpack.theme.lingXiaGold' },\n    { color: '#238636', label: 'xpack.theme.freshGreen' },\n];\nconst defaultLightColors = [\n    { color: '#005eeb', label: 'xpack.theme.classicBlue' },\n    { color: '#238636', label: 'xpack.theme.freshGreen' },\n];\nlet darkColors = [...defaultDarkColors];\nlet lightColors = [...defaultLightColors];\nconst addColorToTheme = (\n    colors: { color: string; label: string }[],\n    newColor: { color: string; label: string },\n    baseCount = 3,\n    maxNewColors = 2,\n): { color: string; label: string }[] => {\n    const updatedColors = [...colors];\n    const extraCount = Math.max(0, colors.length - baseCount);\n    if (extraCount >= maxNewColors) {\n        updatedColors.splice(baseCount, 1);\n    }\n    updatedColors.push(newColor);\n    return updatedColors;\n};\nconst defaultColors = {\n    light: lightPredefineColors.value,\n    dark: darkPredefineColors.value,\n};\nconst initThemeColors = () => {\n    try {\n        const storedColors = localStorage.getItem(STORAGE_KEY);\n        themeColors.value = storedColors ? JSON.parse(storedColors) : { ...defaultColors };\n    } catch (error) {\n        console.error('Failed to parse theme colors from localStorage:', error);\n        themeColors.value = { ...defaultColors };\n    } finally {\n        localStorage.setItem(STORAGE_KEY, JSON.stringify(themeColors.value));\n    }\n};\nconst themeColors = ref({ ...defaultColors });\n\nconst acceptParams = (params: DialogProps): void => {\n    form.themeColor = params.themeColor;\n    form.theme = params.theme;\n    form.dark = form.themeColor.dark;\n    form.light = form.themeColor.light;\n    if (form.themeColor.themePredefineColors) {\n        localStorage.setItem(STORAGE_KEY, JSON.stringify(form.themeColor.themePredefineColors));\n    }\n    initThemeColors();\n    lightPredefineColors.value = themeColors.value['light'];\n    darkPredefineColors.value = themeColors.value['dark'];\n    lightColors = defaultLightColors;\n    lightPredefineColors.value.slice(0, 2).forEach((color) => {\n        const exists = lightColors.some((item) => item.color === color);\n        if (!exists) {\n            lightColors.push({\n                color,\n                label: `xpack.theme.customColor`,\n            });\n        }\n    });\n    darkColors = defaultDarkColors;\n    darkPredefineColors.value.slice(0, 2).forEach((color) => {\n        const exists = darkColors.some((item) => item.color === color);\n        if (!exists) {\n            darkColors.push({\n                color,\n                label: `xpack.theme.customColor`,\n            });\n        }\n    });\n    drawerVisible.value = true;\n};\n\nconst updateThemeColors = (theme: 'light' | 'dark', newColors: string[]) => {\n    themeColors.value[theme] = newColors;\n    localStorage.setItem(STORAGE_KEY, JSON.stringify(themeColors.value));\n    lightPredefineColors.value = themeColors.value['light'];\n    darkPredefineColors.value = themeColors.value['dark'];\n};\nconst addAndRemoveColor = (theme: 'light' | 'dark', newColor: string) => {\n    const colors = [...themeColors.value[theme]];\n    colors.unshift(newColor);\n    colors.pop();\n    updateThemeColors(theme, colors);\n};\nconst changeThemeColor = (theme: 'light' | 'dark', color: string) => {\n    if (theme === 'light') {\n        form.light = color;\n        const newLightColor = { color: color, label: 'xpack.theme.customColor' };\n        lightColors = addColorToTheme(lightColors, newLightColor, 2);\n    } else {\n        form.dark = color;\n        const newDarkColor = { color: color, label: 'xpack.theme.customColor' };\n        darkColors = addColorToTheme(darkColors, newDarkColor);\n    }\n    addAndRemoveColor(theme, color);\n};\nconst changeLightColor = (color: string) => {\n    form.light = color;\n};\nconst changeDarkColor = (color: string) => {\n    form.dark = color;\n};\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    ElMessageBox.confirm(i18n.global.t('xpack.theme.setHelper'), i18n.global.t('commons.button.save'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        await formEl.validate(async (valid) => {\n            if (!valid) return;\n            form.themeColor = { light: form.light, dark: form.dark, themePredefineColors: themeColors.value };\n            if (globalStore.isMasterProductPro) {\n                await updateXpackSettingByKey('ThemeColor', JSON.stringify(form.themeColor));\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                globalStore.themeConfig.themeColor = JSON.stringify(form.themeColor);\n                loading.value = false;\n                let color: string;\n                if (form.theme === 'auto') {\n                    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');\n                    color = prefersDark.matches ? form.dark : form.light;\n                } else {\n                    color = form.theme === 'dark' ? form.dark : form.light;\n                }\n                globalStore.themeConfig.primary = color;\n                setPrimaryColor(color);\n                initFavicon();\n                drawerVisible.value = false;\n                emit('search');\n            }\n        });\n    });\n};\nconst onReSet = async () => {\n    ElMessageBox.confirm(i18n.global.t('xpack.theme.setDefaultHelper'), i18n.global.t('commons.button.setDefault'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        form.themeColor = { light: '#005eeb', dark: '#F0BE96', themePredefineColors: themeColors.value };\n        if (globalStore.isMasterProductPro) {\n            localStorage.setItem(STORAGE_KEY, JSON.stringify(defaultColors));\n            themeColors.value = { ...defaultColors };\n            darkColors = [...defaultDarkColors];\n            lightColors = [...defaultLightColors];\n            await updateXpackSettingByKey('ThemeColor', JSON.stringify(form.themeColor));\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loading.value = false;\n            globalStore.themeConfig.themeColor = JSON.stringify(form.themeColor);\n            let color: string;\n            if (form.theme === 'auto') {\n                const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');\n                color = prefersDark.matches ? '#F0BE96' : '#005eeb';\n            } else {\n                color = form.theme === 'dark' ? '#F0BE96' : '#005eeb';\n            }\n            globalStore.themeConfig.primary = color;\n            setPrimaryColor(color);\n            initFavicon();\n            drawerVisible.value = false;\n        }\n    });\n};\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style lang=\"scss\" scoped>\n.selected-white {\n    box-shadow: inset 0 0 0 1px white;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/timeout/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.sessionTimeout')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :rules=\"rules\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.sessionTimeout')\" prop=\"sessionTimeout\">\n                <el-input clearable v-model.number=\"form.sessionTimeout\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveTimeout(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { updateSetting } from '@/api/modules/setting';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    sessionTimeout: number;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    sessionTimeout: 86400,\n});\n\nconst rules = reactive({\n    sessionTimeout: [Rules.integerNumber, checkNumberRange(300, 864000)],\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.sessionTimeout = params.sessionTimeout;\n    drawerVisible.value = true;\n};\n\nconst onSaveTimeout = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        await updateSetting({ key: 'SessionTimeout', value: form.sessionTimeout + '' })\n            .then(async () => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                return;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/username/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.user')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\" :rules=\"rules\">\n            <el-form-item :label=\"$t('setting.user')\" prop=\"userName\">\n                <el-input clearable v-model.trim=\"form.userName\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveUserName(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateSetting } from '@/api/modules/setting';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { logOutApi } from '@/api/modules/auth';\nimport router from '@/routers';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\ninterface DialogProps {\n    userName: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    userName: '',\n});\nconst rules = reactive({\n    userName: [Rules.userName, Rules.noSpace],\n});\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.userName = params.userName;\n    drawerVisible.value = true;\n};\n\nconst onSaveUserName = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(i18n.global.t('setting.userChangeHelper'), i18n.global.t('setting.userChange'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        }).then(async () => {\n            await updateSetting({ key: 'UserName', value: form.userName })\n                .then(async () => {\n                    await logOutApi();\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    router.push({ name: 'entrance', params: { code: globalStore.entrance } });\n                    globalStore.setLogStatus(false);\n                    return;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/panel/watermark/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.watermark')\" @close=\"handleClose\" size=\"large\">\n        <el-watermark\n            class=\"watermark\"\n            :content=\"loadContent()\"\n            :font=\"{\n                fontSize: form.fontSize,\n                color: globalStore.isDarkTheme ? form.darkColor : form.lightColor,\n                textBaseline: 'top',\n            }\"\n            :rotate=\"form.rotate\"\n            :gap=\"[form.gap, form.gap]\"\n        >\n            <el-form\n                ref=\"formRef\"\n                label-position=\"top\"\n                :model=\"form\"\n                @submit.prevent\n                v-loading=\"loading\"\n                :rules=\"rules\"\n            >\n                <el-form-item :label=\"$t('setting.watermarkContent')\" prop=\"content\">\n                    <el-input clearable v-model.trim=\"form.content\" />\n                    <span class=\"input-help\">{{ $t('setting.contentHelper', ['${nodeName}', '${nodeAddr}']) }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('setting.light')\" prop=\"lightColor\">\n                    <el-color-picker v-model=\"form.lightColor\" show-alpha />\n                </el-form-item>\n                <el-form-item :label=\"$t('setting.dark')\" prop=\"darkColor\">\n                    <el-color-picker v-model=\"form.darkColor\" show-alpha />\n                </el-form-item>\n                <el-form-item :label=\"$t('setting.watermarkFont')\" prop=\"fontSize\">\n                    <el-slider v-model=\"form.fontSize\" :min=\"12\" :max=\"100\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('setting.watermarkRotate')\" prop=\"rotate\">\n                    <el-slider v-model=\"form.rotate\" :min=\"-180\" :max=\"180\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('setting.watermarkGap')\" prop=\"gap\">\n                    <el-input-number class=\"number-input\" v-model.number=\"form.gap\" />\n                </el-form-item>\n            </el-form>\n        </el-watermark>\n        <template #footer>\n            <el-button @click=\"setDefault\">{{ $t('commons.button.setDefault') }}</el-button>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { GlobalStore } from '@/store';\nimport { updateXpackSettingByKey } from '@/utils/xpack';\nconst globalStore = GlobalStore();\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst drawerVisible = ref();\nconst loading = ref();\n\nlet form = reactive({\n    lightColor: 'rgba(0, 0, 0, 0.15)',\n    darkColor: 'rgba(255, 255, 255, 0.15)',\n    fontSize: 16,\n    content: '${nodeName} - ${nodeAddr}',\n    rotate: -22,\n    gap: 100,\n});\n\nconst rules = reactive({\n    content: [Rules.requiredInput],\n});\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (watermark: string): void => {\n    if (watermark) {\n        const parsedData = JSON.parse(watermark);\n        form = reactive(parsedData);\n    }\n    drawerVisible.value = true;\n};\n\nconst loadContent = () => {\n    let itemName = form.content.replaceAll(\n        '${nodeName}',\n        globalStore.currentNode === 'local' ? globalStore.getMasterAlias() : globalStore.currentNode,\n    );\n    itemName = itemName.replaceAll('${nodeAddr}', globalStore.currentNodeAddr);\n    return itemName;\n};\n\nconst setDefault = () => {\n    form.lightColor = 'rgba(0, 0, 0, 0.15)';\n    form.darkColor = 'rgba(255, 255, 255, 0.15)';\n    form.fontSize = 16;\n    form.content = '${nodeName} - ${nodeAddr}';\n    form.rotate = -22;\n    form.gap = 100;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(i18n.global.t('setting.watermarkOpenHelper'), i18n.global.t('setting.watermark'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        }).then(async () => {\n            loading.value = true;\n            let itemVal = JSON.stringify(form);\n            await updateXpackSettingByKey('Watermark', itemVal)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    globalStore.watermark = {\n                        lightColor: form.lightColor,\n                        darkColor: form.darkColor,\n                        fontSize: form.fontSize,\n                        content: form.content,\n                        rotate: form.rotate,\n                        gap: form.gap,\n                    };\n                    globalStore.watermarkShow = true;\n                    handleClose();\n                    return;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.number-input {\n    width: 100%;\n}\n.watermark {\n    height: calc(100vh - 160px);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/allowips/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.allowIPs')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" @submit.prevent :model=\"form\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.allowIPs')\" prop=\"allowIPs\">\n                <el-input type=\"textarea\" :placeholder=\"$t('setting.allowIPEgs')\" :rows=\"3\" v-model=\"form.allowIPs\" />\n                <span class=\"input-help\">{{ $t('setting.allowIPsHelper1') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateSetting } from '@/api/modules/setting';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { checkCidr, checkCidrV6, checkIpV4V6 } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst form = reactive({\n    allowIPs: '',\n});\nconst rules = reactive({\n    allowIPs: [{ validator: checkIPs, trigger: 'blur' }],\n});\nfunction checkIPs(rule: any, value: any, callback: any) {\n    if (typeof value === 'string' && value.trim() !== '') {\n        let addr = value.split('\\n');\n        for (const item of addr) {\n            if (item === '') {\n                continue;\n            }\n            if (item === '0.0.0.0' || item === '::') {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n            if (item.indexOf('/') !== -1) {\n                if (item.indexOf(':') !== -1) {\n                    if (checkCidrV6(item)) {\n                        return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                    }\n                } else if (checkCidr(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            } else if (checkIpV4V6(item)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        }\n    }\n    callback();\n}\nconst formRef = ref<FormInstance>();\n\ninterface DialogProps {\n    allowIPs: string;\n}\n\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.allowIPs = params.allowIPs;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let title = form.allowIPs ? i18n.global.t('setting.allowIPs') : i18n.global.t('setting.unAllowIPs');\n        let allow = form.allowIPs\n            ? i18n.global.t('setting.allowIPsWarning')\n            : i18n.global.t('setting.unAllowIPsWarning');\n        ElMessageBox.confirm(allow, title, {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        }).then(async () => {\n            loading.value = true;\n            let ipItems = form.allowIPs.split('\\n');\n            let ips = [];\n            for (const item of ipItems) {\n                if (item !== '') {\n                    ips.push(item);\n                }\n            }\n            await updateSetting({ key: 'AllowIPs', value: ips.join(',') })\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    handleClose();\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/bind/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.bindInfo')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.ipv6')\" prop=\"ipv6\" :rules=\"Rules.requiredSelect\">\n                <el-radio-group style=\"width: 100%\" v-model=\"form.ipv6\" @change=\"onChangeMode()\">\n                    <el-radio value=\"Enable\">{{ $t('commons.button.enable') }}</el-radio>\n                    <el-radio value=\"Disable\">{{ $t('commons.button.disable') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item\n                v-if=\"form.ipv6 === 'Disable'\"\n                :label=\"$t('setting.bindAddress')\"\n                prop=\"bindAddress\"\n                :rules=\"Rules.ip\"\n            >\n                <el-checkbox v-model=\"form.input\" :label=\"$t('container.input')\" />\n                <el-select v-if=\"!form.input\" clearable v-model=\"form.bindAddress\">\n                    <el-option value=\"0.0.0.0\" label=\"0.0.0.0\"></el-option>\n                    <div v-for=\"item in interfaceOptions\" :key=\"item\">\n                        <el-option v-if=\"item.indexOf(':') === -1\" :value=\"item\" :label=\"item\" />\n                    </div>\n                </el-select>\n                <el-input v-else clearable v-model=\"form.bindAddress\"></el-input>\n            </el-form-item>\n            <el-form-item v-else :label=\"$t('setting.bindAddress')\" prop=\"bindAddress\" :rules=\"Rules.ipV6\">\n                <el-checkbox v-model=\"form.input\" :label=\"$t('container.input')\" />\n                <el-select v-if=\"!form.input\" clearable v-model=\"form.bindAddress\">\n                    <el-option value=\"::\" label=\"::\"></el-option>\n                    <div v-for=\"item in interfaceOptions\" :key=\"item\">\n                        <el-option v-if=\"item.indexOf(':') !== -1\" :value=\"item\" :label=\"item\" />\n                    </div>\n                </el-select>\n                <el-input v-else clearable v-model=\"form.bindAddress\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSavePort(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateBindInfo, loadInterfaceAddr } from '@/api/modules/setting';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\ninterface DialogProps {\n    ipv6: string;\n    bindAddress: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst interfaceOptions = ref();\n\nconst form = reactive({\n    ipv6: '',\n    bindAddress: '',\n    input: false,\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.ipv6 = params.ipv6;\n    form.bindAddress = params.bindAddress;\n    loadInterface();\n    drawerVisible.value = true;\n};\n\nconst loadInterface = async () => {\n    const res = await loadInterfaceAddr();\n    interfaceOptions.value = res.data || [];\n};\n\nconst onChangeMode = () => {\n    form.bindAddress = form.ipv6 === 'Enable' ? '::' : '0.0.0.0';\n};\n\nconst onSavePort = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(i18n.global.t('setting.bindInfoHelper'), i18n.global.t('setting.bindInfo'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        }).then(async () => {\n            loading.value = true;\n            await updateBindInfo(form.ipv6, form.bindAddress)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    globalStore.isLogin = false;\n                    let href = window.location.href;\n                    let address = '';\n                    if (globalStore.entrance) {\n                        address = href.replaceAll('settings/safe', globalStore.entrance);\n                    } else {\n                        address = href.replaceAll('settings/safe', 'login');\n                    }\n                    window.open(address, '_self');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/domain/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.bindDomain')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :rules=\"rules\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.bindDomain')\" prop=\"bindDomain\">\n                <el-input clearable v-model=\"form.bindDomain\" />\n                <span class=\"input-help\">{{ $t('setting.bindDomainHelper1') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSavePort(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateSetting } from '@/api/modules/setting';\nimport { ElMessageBox, FormInstance } from 'element-plus';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    bindDomain: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    bindDomain: '',\n});\nconst rules = reactive({\n    bindDomain: [{ validator: checkSecurityEntrance, trigger: 'blur' }],\n});\n\nfunction checkSecurityEntrance(rule: any, value: any, callback: any) {\n    if (form.bindDomain !== '') {\n        const reg =\n            /^([\\w\\u4e00-\\u9fa5\\-\\*]{1,100}\\.){1,10}([\\w\\u4e00-\\u9fa5\\-]{1,24}|[\\w\\u4e00-\\u9fa5\\-]{1,24}\\.[\\w\\u4e00-\\u9fa5\\-]{1,24})$/;\n        if (!reg.test(form.bindDomain)) {\n            return callback(new Error(i18n.global.t('commons.rule.domain')));\n        }\n    }\n    callback();\n}\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.bindDomain = params.bindDomain;\n    drawerVisible.value = true;\n};\n\nconst onSavePort = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let title = form.bindDomain ? i18n.global.t('setting.bindDomain') : i18n.global.t('setting.unBindDomain');\n        let helper = form.bindDomain\n            ? i18n.global.t('setting.bindDomainWarning')\n            : i18n.global.t('setting.unBindDomainHelper');\n        ElMessageBox.confirm(helper, title, {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        }).then(async () => {\n            loading.value = true;\n            await updateSetting({ key: 'BindDomain', value: form.bindDomain })\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    if (form.bindDomain) {\n                        let href = window.location.href;\n                        let ipLocal = href.split('//')[1].split(':')[0];\n                        href = href.replaceAll(ipLocal, form.bindDomain);\n                        window.open(href, '_self');\n                    } else {\n                        handleClose();\n                        emit('search');\n                    }\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/entrance/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.entrance')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\" :rules=\"rules\">\n            <el-form-item :label=\"$t('setting.entrance')\" prop=\"securityEntrance\">\n                <el-input clearable v-model=\"form.securityEntrance\">\n                    <template #append>\n                        <el-button @click=\"random\">{{ $t('setting.randomGenerate') }}</el-button>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">\n                    {{ $t('setting.entranceInputHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item>\n                <el-checkbox v-model=\"show\" :label=\"$t('setting.showEntrance')\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"submitEntrance(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateSetting } from '@/api/modules/setting';\nimport { GlobalStore } from '@/store';\nimport { getRandomStr } from '@/utils/util';\nimport { FormInstance } from 'element-plus';\nimport { clearDashboardCacheByPrefix } from '@/utils/dashboardCache';\nconst globalStore = GlobalStore();\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    securityEntrance: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst show = ref();\n\nconst form = reactive({\n    securityEntrance: '',\n});\n\nconst formRef = ref<FormInstance>();\nconst rules = reactive({\n    securityEntrance: [{ validator: checkSecurityEntrance, trigger: 'blur' }],\n});\n\nfunction checkSecurityEntrance(rule: any, value: any, callback: any) {\n    if (form.securityEntrance !== '') {\n        const reg = /^[A-Za-z0-9]{5,116}$/;\n        if (!reg.test(form.securityEntrance)) {\n            return callback(new Error(i18n.global.t('setting.entranceError')));\n        }\n    }\n    callback();\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    form.securityEntrance = params.securityEntrance;\n    show.value = globalStore.showEntranceWarn;\n    drawerVisible.value = true;\n};\n\nconst random = async () => {\n    form.securityEntrance = getRandomStr(10);\n};\n\nconst submitEntrance = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let param = {\n            key: 'SecurityEntrance',\n            value: form.securityEntrance,\n        };\n        loading.value = true;\n        await updateSetting(param)\n            .then(() => {\n                clearDashboardCacheByPrefix(['safeStatus']);\n                globalStore.setShowEntranceWarn(show.value);\n                globalStore.entrance = form.securityEntrance;\n                loading.value = false;\n                drawerVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('setting.safe')\" :divider=\"true\">\n            <template #main>\n                <el-form\n                    :model=\"form\"\n                    v-loading=\"loading\"\n                    :label-position=\"mobile ? 'top' : 'left'\"\n                    label-width=\"150px\"\n                >\n                    <el-row>\n                        <el-col :span=\"1\"><br /></el-col>\n                        <el-col :xs=\"24\" :sm=\"20\" :md=\"15\" :lg=\"12\" :xl=\"12\">\n                            <el-form-item :label=\"$t('setting.panelPort')\" prop=\"serverPort\">\n                                <el-input disabled v-model.number=\"form.serverPort\">\n                                    <template #append>\n                                        <el-button @click=\"onChangePort\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('setting.bindInfo')\" prop=\"bindAddress\">\n                                <el-input disabled v-model=\"form.bindAddress\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeBind\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('setting.entrance')\">\n                                <el-input\n                                    type=\"password\"\n                                    disabled\n                                    v-if=\"form.securityEntrance\"\n                                    v-model=\"form.securityEntrance\"\n                                >\n                                    <template #append>\n                                        <el-button @click=\"onChangeEntrance\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <el-input disabled v-if=\"!form.securityEntrance\" v-model=\"unset\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeEntrance\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('setting.entranceHelper') }}</span>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.noAuthSetting')\">\n                                <el-input disabled v-model=\"form.noAuthSetting\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeResponse\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.allowIPs')\">\n                                <div style=\"width: 100%\" v-if=\"form.allowIPs\">\n                                    <el-input\n                                        type=\"textarea\"\n                                        :rows=\"3\"\n                                        disabled\n                                        v-model=\"form.allowIPs\"\n                                        style=\"width: calc(100% - 80px)\"\n                                    />\n                                    <el-button class=\"append-button\" @click=\"onChangeAllowIPs\" icon=\"Setting\">\n                                        {{ $t('commons.button.set') }}\n                                    </el-button>\n                                </div>\n                                <el-input disabled v-if=\"!form.allowIPs\" v-model=\"unset\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeAllowIPs\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('setting.allowIPsHelper') }}</span>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.bindDomain')\">\n                                <el-input disabled v-if=\"form.bindDomain\" v-model=\"form.bindDomain\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeBindDomain\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <el-input disabled v-if=\"!form.bindDomain\" v-model=\"unset\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeBindDomain\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('setting.bindDomainHelper') }}</span>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.panelSSL')\" prop=\"ssl\">\n                                <el-switch\n                                    @change=\"handleSSL\"\n                                    v-model=\"form.sslItem\"\n                                    active-value=\"Enable\"\n                                    inactive-value=\"Disable\"\n                                />\n                                <span class=\"input-help\">{{ $t('setting.https') }}</span>\n                                <div v-if=\"form.ssl !== 'Disable' && sslInfo\">\n                                    <el-tag>{{ $t('setting.domainOrIP') }} {{ sslInfo.domain }}</el-tag>\n                                    <el-tag style=\"margin-left: 5px\">\n                                        {{ $t('setting.timeOut') }} {{ sslInfo.timeout }}\n                                    </el-tag>\n                                    <div>\n                                        <el-button link type=\"primary\" @click=\"handleSSL\">\n                                            {{ $t('commons.button.view') }}\n                                        </el-button>\n                                    </div>\n                                </div>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.expirationTime')\" prop=\"expirationTime\">\n                                <el-input disabled v-model=\"form.expirationTime\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeExpirationTime\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                                <div>\n                                    <span class=\"input-help\" v-if=\"form.expirationTime !== $t('setting.unSetting')\">\n                                        {{ $t('setting.timeoutHelper', [loadTimeOut()]) }}\n                                    </span>\n                                    <span class=\"input-help\" v-else>\n                                        {{ $t('setting.noneSetting') }}\n                                    </span>\n                                </div>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('setting.complexity')\" prop=\"complexityVerification\">\n                                <el-switch\n                                    @change=\"onSaveComplexity\"\n                                    v-model=\"form.complexityVerification\"\n                                    active-value=\"Enable\"\n                                    inactive-value=\"Disable\"\n                                />\n                                <span class=\"input-help\">\n                                    {{ $t('setting.complexityHelper') }}\n                                </span>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.mfa')\">\n                                <el-switch\n                                    @change=\"handleMFA\"\n                                    v-model=\"form.mfaStatus\"\n                                    active-value=\"Enable\"\n                                    inactive-value=\"Disable\"\n                                />\n                                <span class=\"input-help\">\n                                    {{ $t('setting.mfaHelper') }}\n                                </span>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('setting.passkey')\">\n                                <el-button @click=\"openPasskeyDialog\">\n                                    {{ $t('setting.passkeyManage') }}\n                                </el-button>\n                                <span class=\"input-help\">\n                                    {{ $t('setting.passkeyHelper') }}\n                                </span>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                </el-form>\n            </template>\n        </LayoutContent>\n\n        <PortSetting ref=\"portRef\" />\n        <BindSetting ref=\"bindRef\" />\n        <MfaSetting ref=\"mfaRef\" @search=\"search\" />\n        <SSLSetting ref=\"sslRef\" @search=\"search\" />\n        <EntranceSetting ref=\"entranceRef\" @search=\"search\" />\n        <TimeoutSetting ref=\"timeoutRef\" @search=\"search\" />\n        <DomainSetting ref=\"domainRef\" @search=\"search\" />\n        <AllowIPsSetting ref=\"allowIPsRef\" @search=\"search\" />\n        <ResponseSetting ref=\"responseRef\" @search=\"search()\" />\n        <PasskeySetting ref=\"passkeyRef\" @go-configure-domain=\"onChangeBindDomain\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, reactive, onMounted, computed } from 'vue';\nimport { ElForm, ElMessageBox } from 'element-plus';\nimport PortSetting from '@/views/setting/safe/port/index.vue';\nimport BindSetting from '@/views/setting/safe/bind/index.vue';\nimport ResponseSetting from '@/views/setting/safe/response/index.vue';\nimport SSLSetting from '@/views/setting/safe/ssl/index.vue';\nimport MfaSetting from '@/views/setting/safe/mfa/index.vue';\nimport TimeoutSetting from '@/views/setting/safe/timeout/index.vue';\nimport EntranceSetting from '@/views/setting/safe/entrance/index.vue';\nimport DomainSetting from '@/views/setting/safe/domain/index.vue';\nimport AllowIPsSetting from '@/views/setting/safe/allowips/index.vue';\nimport PasskeySetting from '@/views/setting/safe/passkey/index.vue';\nimport { updateSetting, getSettingInfo, getSystemAvailable, updateSSL, loadSSLInfo } from '@/api/modules/setting';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { Setting } from '@/api/interface/setting';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\nconst entranceRef = ref();\nconst portRef = ref();\nconst bindRef = ref();\nconst timeoutRef = ref();\nconst mfaRef = ref();\nconst responseRef = ref();\n\nconst sslRef = ref();\nconst lastSSL = ref('Disable');\nconst sslInfo = ref<Setting.SSLInfo>();\nconst domainRef = ref();\nconst allowIPsRef = ref();\nconst passkeyRef = ref();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst form = reactive({\n    serverPort: 9999,\n    ipv6: 'Disable',\n    bindAddress: '',\n    ssl: 'Disable',\n    sslItem: 'Disable',\n    sslType: 'self',\n    securityEntrance: '',\n    expirationDays: 0,\n    expirationTime: '',\n    complexityVerification: 'Disable',\n    mfaStatus: 'Disable',\n    mfaInterval: 30,\n    allowIPs: '',\n    bindDomain: '',\n    noAuthSetting: '200 - ' + i18n.global.t('setting.help200'),\n    noAuthSettingValue: '200',\n});\n\nconst unset = ref(i18n.global.t('setting.unSetting'));\n\nconst search = async () => {\n    const res = await getSettingInfo();\n    form.serverPort = Number(res.data.serverPort);\n    form.ipv6 = res.data.ipv6;\n    form.bindAddress = res.data.bindAddress;\n    form.ssl = res.data.ssl;\n    form.sslItem = res.data.ssl === 'Disable' ? 'Disable' : 'Enable';\n    lastSSL.value = form.ssl;\n    form.sslType = res.data.sslType;\n    if (form.ssl !== 'Disable') {\n        loadInfo();\n    }\n    form.securityEntrance = res.data.securityEntrance;\n    form.expirationDays = Number(res.data.expirationDays);\n    form.expirationTime = res.data.expirationTime;\n    form.complexityVerification = res.data.complexityVerification;\n    form.mfaStatus = res.data.mfaStatus;\n    form.mfaInterval = Number(res.data.mfaInterval);\n    form.allowIPs = res.data.allowIPs.replaceAll(',', '\\n');\n    form.bindDomain = res.data.bindDomain;\n    form.noAuthSettingValue = res.data.noAuthSetting;\n    if (res.data.noAuthSetting !== '200') {\n        form.noAuthSetting = res.data.noAuthSetting + ' - ' + i18n.global.t('setting.error' + res.data.noAuthSetting);\n    } else {\n        form.noAuthSetting = res.data.noAuthSetting + ' - ' + i18n.global.t('setting.help200');\n    }\n};\n\nconst onSaveComplexity = async () => {\n    let param = {\n        key: 'ComplexityVerification',\n        value: form.complexityVerification,\n    };\n    loading.value = true;\n    await updateSetting(param)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleMFA = async () => {\n    if (form.mfaStatus === 'Enable') {\n        mfaRef.value.acceptParams({ interval: form.mfaInterval });\n        return;\n    }\n    ElMessageBox.confirm(i18n.global.t('setting.mfaClose'), i18n.global.t('setting.mfa'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    })\n        .then(async () => {\n            loading.value = true;\n            await updateSetting({ key: 'MFAStatus', value: 'Disable' })\n                .then(() => {\n                    loading.value = false;\n                    search();\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                    search();\n                });\n        })\n        .catch(() => {\n            search();\n        });\n};\n\nconst openPasskeyDialog = async () => {\n    passkeyRef.value.acceptParams({ bindDomain: form.bindDomain });\n};\n\nconst onChangeEntrance = () => {\n    entranceRef.value.acceptParams({ securityEntrance: form.securityEntrance });\n};\nconst onChangePort = () => {\n    portRef.value.acceptParams({ serverPort: form.serverPort });\n};\nconst onChangeBind = () => {\n    bindRef.value.acceptParams({ ipv6: form.ipv6, bindAddress: form.bindAddress });\n};\nconst onChangeResponse = () => {\n    responseRef.value.acceptParams({ noAuthSetting: form.noAuthSettingValue });\n};\nconst onChangeBindDomain = () => {\n    domainRef.value.acceptParams({ bindDomain: form.bindDomain });\n};\nconst onChangeAllowIPs = () => {\n    allowIPsRef.value.acceptParams({ allowIPs: form.allowIPs });\n};\nconst handleSSL = async () => {\n    if (form.sslItem !== 'Disable') {\n        let params = {\n            ssl: form.ssl,\n            sslType: form.sslType,\n            sslInfo: sslInfo.value,\n        };\n        sslRef.value!.acceptParams(params);\n        return;\n    }\n    ElMessageBox.confirm(i18n.global.t('setting.sslDisableHelper'), i18n.global.t('setting.sslDisable'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    })\n        .then(async () => {\n            await updateSSL({ ssl: 'Disable', domain: '', sslType: form.sslType, key: '', cert: '', sslID: 0 });\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            lastSSL.value = 'Disable';\n            let href = window.location.href;\n            globalStore.isLogin = false;\n            let address = href.split('://')[1];\n            if (globalStore.entrance) {\n                address = address.replaceAll('settings/safe', globalStore.entrance);\n            } else {\n                address = address.replaceAll('settings/safe', 'login');\n            }\n            setTimeout(() => {\n                window.location.href = `http://${address}`;\n            }, 1000);\n        })\n        .catch(() => {\n            form.ssl = lastSSL.value;\n            form.sslItem = lastSSL.value === 'Disable' ? 'Disable' : 'Enable';\n        });\n};\n\nconst loadInfo = async () => {\n    await loadSSLInfo().then(async (res) => {\n        sslInfo.value = res.data;\n    });\n};\n\nconst onChangeExpirationTime = async () => {\n    timeoutRef.value.acceptParams({ expirationDays: form.expirationDays });\n};\n\nfunction loadTimeOut() {\n    if (form.expirationDays === 0) {\n        form.expirationTime = i18n.global.t('setting.unSetting');\n        return i18n.global.t('setting.unSetting');\n    }\n    let staytimeGap = new Date(form.expirationTime).getTime() - new Date().getTime();\n    if (staytimeGap < 0) {\n        form.expirationTime = i18n.global.t('setting.unSetting');\n        return i18n.global.t('setting.unSetting');\n    }\n    return Math.floor(staytimeGap / (3600 * 1000 * 24));\n}\n\nonMounted(() => {\n    search();\n    getSystemAvailable();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.append-button {\n    width: 80px;\n    background-color: var(--el-fill-color-light);\n    color: var(--el-color-info);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/mfa/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.mfa')\" @close=\"handleClose\" size=\"small\">\n        <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\">\n            <template #default>\n                <span>\n                    <span>{{ $t('setting.mfaAlert') }}</span>\n                </span>\n            </template>\n        </el-alert>\n        <el-form :model=\"form\" ref=\"formRef\" @submit.prevent v-loading=\"loading\" label-position=\"top\" :rules=\"rules\">\n            <el-form-item :label=\"$t('setting.mfaHelper1')\">\n                <ul class=\"help-ul\">\n                    <li>Google Authenticator</li>\n                    <li>Microsoft Authenticator</li>\n                    <li>1Password</li>\n                    <li>LastPass</li>\n                    <li>Authenticator</li>\n                </ul>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.mfaHelper2')\">\n                <el-image class=\"w-32 h-32\" :src=\"qrImage\" />\n                <span class=\"input-help flex items-center\">\n                    <span>{{ $t('setting.secret') }}: {{ form.secret }}</span>\n                    <CopyButton :content=\"form.secret\" />\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.title')\" prop=\"title\">\n                <el-input v-model=\"form.title\">\n                    <template #append>\n                        <el-button @click=\"loadMfaCodeBefore(formRef)\">\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('setting.mfaTitleHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.mfaInterval')\" prop=\"interval\">\n                <el-input v-model.number=\"form.interval\">\n                    <template #append>\n                        <el-button @click=\"loadMfaCodeBefore(formRef)\">\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('setting.mfaIntervalHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.mfaCode')\" prop=\"code\">\n                <el-input v-model=\"form.code\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onBind(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { bindMFA, loadMFA } from '@/api/modules/setting';\nimport { reactive, ref } from 'vue';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\n\nconst loading = ref();\nconst qrImage = ref();\nconst drawerVisible = ref();\nconst formRef = ref();\n\nconst form = reactive({\n    title: '1Panel',\n    code: '',\n    secret: '',\n    interval: 30,\n});\n\nconst rules = reactive({\n    code: [Rules.requiredInput],\n    title: [Rules.simpleName],\n    interval: [Rules.number, checkNumberRange(15, 60)],\n});\n\ninterface DialogProps {\n    interval: number;\n}\nconst emit = defineEmits<{ (e: 'search'): void }>();\nconst acceptParams = (params: DialogProps): void => {\n    form.interval = params.interval;\n    if (params.interval == 0) {\n        form.interval = 30;\n    }\n    loadMfaCode();\n    drawerVisible.value = true;\n};\n\nconst loadMfaCodeBefore = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    const result = await formEl.validateField('interval', callback);\n    if (!result) {\n        return;\n    }\n    const result2 = await formEl.validateField('title', callback);\n    if (!result2) {\n        return;\n    }\n    loadMfaCode();\n};\nconst loadMfaCode = async () => {\n    let param = {\n        title: form.title,\n        interval: form.interval,\n    };\n    const res = await loadMFA(param);\n    form.secret = res.data.secret;\n    qrImage.value = res.data.qrImage;\n};\n\nfunction callback(error: any) {\n    if (error) {\n        return error.message;\n    } else {\n        return;\n    }\n}\n\nconst onBind = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let param = {\n            code: form.code,\n            secret: form.secret,\n            interval: form.interval + '',\n        };\n        loading.value = true;\n        await bindMFA(param)\n            .then(() => {\n                loading.value = false;\n                drawerVisible.value = false;\n                emit('search');\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style scoped>\n.help-ul {\n    color: #8f959e;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/passkey/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.passkey')\" @close=\"handleClose\" size=\"large\">\n        <div class=\"mb-4\">\n            <el-alert\n                v-if=\"!allPrerequisitesMet\"\n                :title=\"$t('setting.passkeyPrereqTitle')\"\n                type=\"warning\"\n                :closable=\"false\"\n                class=\"mb-4\"\n            >\n                <template #default>\n                    <div class=\"flex flex-col gap-2 mt-2\">\n                        <div class=\"flex items-center gap-2\">\n                            <el-icon :color=\"prereqBindDomain ? '#67c23a' : '#f56c6c'\">\n                                <Check v-if=\"prereqBindDomain\" />\n                                <Close v-else />\n                            </el-icon>\n                            <span>{{ $t('setting.passkeyPrereqBindDomain') }}</span>\n                            <el-button v-if=\"!prereqBindDomain\" link type=\"primary\" @click=\"goConfigureDomain\">\n                                {{ $t('setting.passkeyPrereqGoSetup') }}\n                            </el-button>\n                        </div>\n                        <div class=\"flex items-center gap-2\">\n                            <el-icon :color=\"prereqHttps ? '#67c23a' : '#f56c6c'\">\n                                <Check v-if=\"prereqHttps\" />\n                                <Close v-else />\n                            </el-icon>\n                            <span>{{ $t('setting.passkeyPrereqHttps') }}</span>\n                        </div>\n                        <div class=\"flex items-center gap-2\">\n                            <el-icon :color=\"prereqBrowser ? '#67c23a' : '#f56c6c'\">\n                                <Check v-if=\"prereqBrowser\" />\n                                <Close v-else />\n                            </el-icon>\n                            <span>{{ $t('setting.passkeyPrereqBrowser') }}</span>\n                            <span v-if=\"!prereqBrowser && prereqBrowserDetail\" class=\"text-xs text-gray-500\">\n                                {{ prereqBrowserDetail }}\n                            </span>\n                        </div>\n                    </div>\n                </template>\n            </el-alert>\n        </div>\n        <el-tabs v-model=\"activeTab\" type=\"border-card\">\n            <el-tab-pane :label=\"$t('setting.passkeyKeyManagement')\" name=\"keys\">\n                <el-form label-position=\"top\">\n                    <el-form-item :label=\"$t('setting.passkeyName')\">\n                        <el-input\n                            v-model.trim=\"passkeyForm.name\"\n                            :placeholder=\"$t('setting.passkeyNameHelper')\"\n                            :disabled=\"!allPrerequisitesMet\"\n                        />\n                    </el-form-item>\n                    <el-button type=\"primary\" @click=\"registerPasskey\" :disabled=\"!canRegisterPasskey\">\n                        {{ $t('setting.passkeyAdd') }}\n                    </el-button>\n                    <span class=\"text-xs text-gray-500 ml-3\">{{ passkeyCountText }}</span>\n                </el-form>\n                <el-table class=\"mt-4\" :data=\"passkeyList\" v-loading=\"passkeyLoading\">\n                    <el-table-column prop=\"name\" :label=\"$t('setting.passkeyName')\" min-width=\"120\" />\n                    <el-table-column prop=\"createdAt\" :label=\"$t('setting.passkeyCreatedAt')\" min-width=\"160\" />\n                    <el-table-column :label=\"$t('setting.passkeyLastUsedAt')\" min-width=\"160\">\n                        <template #default=\"scope\">\n                            <span>{{ scope.row.lastUsedAt || '-' }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.operate')\" width=\"120\">\n                        <template #default=\"scope\">\n                            <el-button\n                                link\n                                type=\"danger\"\n                                :disabled=\"!allPrerequisitesMet\"\n                                @click=\"removePasskey(scope.row.id)\"\n                            >\n                                {{ $t('commons.button.delete') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                </el-table>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('app.advanced')\" name=\"advanced\">\n                <el-form label-position=\"top\">\n                    <el-form-item :label=\"$t('setting.passkeyTrustedProxies')\">\n                        <div class=\"w-full flex items-start gap-2\">\n                            <el-input v-model=\"passkeyTrustedProxies\" type=\"textarea\" :rows=\"3\" />\n                            <el-button :loading=\"savePasskeyProxyLoading\" @click=\"onSavePasskeyTrustedProxies\">\n                                {{ $t('commons.button.save') }}\n                            </el-button>\n                        </div>\n                        <span class=\"input-help\">\n                            {{ $t('setting.passkeyTrustedProxiesHelper') }}\n                        </span>\n                        <span class=\"input-help\">\n                            {{ $t('setting.allowIPEgs') }}\n                        </span>\n                    </el-form-item>\n                </el-form>\n            </el-tab-pane>\n        </el-tabs>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, reactive, ref } from 'vue';\nimport { Check, Close } from '@element-plus/icons-vue';\nimport { ElMessageBox } from 'element-plus';\nimport {\n    getSettingInfo,\n    passkeyRegisterBegin,\n    passkeyRegisterFinish,\n    passkeyList as fetchPasskeyList,\n    passkeyDelete,\n    updateSetting,\n} from '@/api/modules/setting';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Setting } from '@/api/interface/setting';\nimport { base64UrlToBuffer, bufferToBase64Url } from '@/utils/util';\n\ninterface DrawerParams {\n    bindDomain: string;\n}\n\nconst emit = defineEmits(['go-configure-domain']);\n\nconst activeTab = ref('keys');\nconst drawerVisible = ref(false);\nconst passkeyLoading = ref(false);\nconst savePasskeyProxyLoading = ref(false);\nconst passkeyList = ref<Setting.PasskeyInfo[]>([]);\nconst passkeyForm = reactive({ name: '' });\nconst passkeyTrustedProxies = ref('');\nconst hasBindDomain = ref(false);\nconst passkeyMaxCount = 5;\n\nconst prereqBindDomain = computed(() => hasBindDomain.value);\nconst prereqHttps = computed(() => window.isSecureContext);\nconst prereqBrowser = ref(false);\nconst prereqBrowserDetailKey = ref('');\nconst prereqBrowserDetail = computed(() => {\n    if (!prereqBrowserDetailKey.value) {\n        return '';\n    }\n    return i18n.global.t(prereqBrowserDetailKey.value);\n});\nconst allPrerequisitesMet = computed(() => prereqBindDomain.value && prereqHttps.value && prereqBrowser.value);\n\nconst passkeyCountText = computed(() => {\n    return i18n.global.t('setting.passkeyCount', [passkeyList.value.length, passkeyMaxCount]);\n});\n\nconst canRegisterPasskey = computed(() => {\n    return (\n        allPrerequisitesMet.value && passkeyList.value.length < passkeyMaxCount && passkeyForm.name.trim().length > 0\n    );\n});\n\nconst checkPasskeyBrowserSupport = async () => {\n    prereqBrowser.value = false;\n    prereqBrowserDetailKey.value = '';\n    const credentialApi = window.PublicKeyCredential;\n    if (!credentialApi) {\n        prereqBrowserDetailKey.value = 'setting.passkeyPrereqBrowserDetailWebAuthnUnavailable';\n        return;\n    }\n    if (typeof credentialApi.isUserVerifyingPlatformAuthenticatorAvailable !== 'function') {\n        prereqBrowserDetailKey.value = 'setting.passkeyPrereqBrowserDetailPlatformCapabilityUnavailable';\n        return;\n    }\n    try {\n        prereqBrowser.value = await credentialApi.isUserVerifyingPlatformAuthenticatorAvailable();\n        if (!prereqBrowser.value) {\n            prereqBrowserDetailKey.value = 'setting.passkeyPrereqBrowserDetailNoPlatformAuthenticator';\n        }\n    } catch (error) {\n        prereqBrowserDetailKey.value = 'setting.passkeyPrereqBrowserDetailDetectFailed';\n    }\n};\n\nconst acceptParams = async (params: DrawerParams) => {\n    hasBindDomain.value = params.bindDomain.trim().length > 0;\n    await checkPasskeyBrowserSupport();\n    drawerVisible.value = true;\n    await loadPasskeyTrustedProxies();\n    await loadPasskeys();\n};\n\nconst loadPasskeyTrustedProxies = async () => {\n    try {\n        const res = await getSettingInfo();\n        passkeyTrustedProxies.value = res.data.passkeyTrustedProxies || '';\n    } catch (error) {\n        passkeyTrustedProxies.value = '';\n    }\n};\n\nconst loadPasskeys = async () => {\n    passkeyLoading.value = true;\n    try {\n        const res = await fetchPasskeyList();\n        passkeyList.value = res.data || [];\n    } catch (error) {\n        passkeyList.value = [];\n    } finally {\n        passkeyLoading.value = false;\n    }\n};\n\nconst registerPasskey = async () => {\n    if (!allPrerequisitesMet.value) {\n        return;\n    }\n    if (passkeyList.value.length >= passkeyMaxCount) {\n        MsgError(i18n.global.t('setting.passkeyLimit'));\n        return;\n    }\n    if (!passkeyForm.name.trim()) {\n        MsgError(i18n.global.t('commons.rule.requiredInput'));\n        return;\n    }\n    passkeyLoading.value = true;\n    try {\n        const res = await passkeyRegisterBegin({ name: passkeyForm.name.trim() });\n        const publicKey = normalizePasskeyCreation(res.data.publicKey);\n        const credential = (await navigator.credentials.create({ publicKey })) as PublicKeyCredential | null;\n        if (!credential) {\n            MsgError(i18n.global.t('setting.passkeyFailed'));\n            return;\n        }\n        const payload = buildPasskeyAttestation(credential);\n        await passkeyRegisterFinish(payload, res.data.sessionId);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        passkeyForm.name = '';\n        await loadPasskeys();\n    } catch (res: any) {\n        if (res?.message) {\n            console.log(res.message);\n        }\n    } finally {\n        passkeyLoading.value = false;\n    }\n};\n\nconst removePasskey = async (id: string) => {\n    ElMessageBox.confirm(i18n.global.t('setting.passkeyDeleteConfirm'), i18n.global.t('setting.passkey'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    })\n        .then(async () => {\n            passkeyLoading.value = true;\n            await passkeyDelete(id);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            await loadPasskeys();\n        })\n        .catch(() => {})\n        .finally(() => {\n            passkeyLoading.value = false;\n        });\n};\n\nconst onSavePasskeyTrustedProxies = async () => {\n    savePasskeyProxyLoading.value = true;\n    await updateSetting({ key: 'PasskeyTrustedProxies', value: passkeyTrustedProxies.value })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loadPasskeyTrustedProxies();\n        })\n        .finally(() => {\n            savePasskeyProxyLoading.value = false;\n        });\n};\n\nconst normalizePasskeyCreation = (publicKey: Record<string, any>): PublicKeyCredentialCreationOptions => {\n    const request = { ...publicKey };\n    request.challenge = base64UrlToBuffer(request.challenge);\n    request.user = { ...request.user, id: base64UrlToBuffer(request.user.id) };\n    if (request.excludeCredentials && Array.isArray(request.excludeCredentials)) {\n        request.excludeCredentials = request.excludeCredentials.map((item) => {\n            return { ...item, id: base64UrlToBuffer(item.id) };\n        });\n    }\n    return request as PublicKeyCredentialCreationOptions;\n};\n\nconst buildPasskeyAttestation = (credential: PublicKeyCredential) => {\n    const response = credential.response as AuthenticatorAttestationResponse;\n    return {\n        id: credential.id,\n        rawId: bufferToBase64Url(credential.rawId),\n        type: credential.type,\n        response: {\n            clientDataJSON: bufferToBase64Url(response.clientDataJSON),\n            attestationObject: bufferToBase64Url(response.attestationObject),\n        },\n        clientExtensionResults: credential.getClientExtensionResults(),\n        authenticatorAttachment: credential.authenticatorAttachment,\n    };\n};\n\nconst goConfigureDomain = () => {\n    drawerVisible.value = false;\n    emit('go-configure-domain');\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/port/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.panelPort')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.panelPort')\" prop=\"serverPort\" :rules=\"Rules.port\">\n                <el-input clearable v-model.number=\"form.serverPort\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSavePort(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { updatePort } from '@/api/modules/setting';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\ninterface DialogProps {\n    serverPort: number;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    serverPort: 9999,\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.serverPort = params.serverPort;\n    drawerVisible.value = true;\n};\n\nconst onSavePort = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(i18n.global.t('setting.portChangeHelper'), i18n.global.t('setting.portChange'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        }).then(async () => {\n            loading.value = true;\n            let param = {\n                serverPort: form.serverPort,\n            };\n            await updatePort(param)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    globalStore.isLogin = false;\n                    let href = window.location.href;\n                    let ip = href.split('//')[1].split(':')[0];\n                    if (globalStore.entrance) {\n                        window.open(\n                            `${href.split('//')[0]}//${ip}:${form.serverPort}/${globalStore.entrance}`,\n                            '_self',\n                        );\n                    } else {\n                        window.open(`${href.split('//')[0]}//${ip}:${form.serverPort}/login`, '_self');\n                    }\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/response/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.noAuthSetting')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.responseSetting')\" prop=\"noAuthSetting\" :rules=\"Rules.requiredSelect\">\n                <el-select v-model=\"form.noAuthSetting\">\n                    <el-option v-for=\"item in options\" :key=\"item.value\" :label=\"item.label\" :value=\"item.value\" />\n                </el-select>\n                <span class=\"input-help\">{{ $t('setting.noAuthSettingHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { updateSetting } from '@/api/modules/setting';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\n\nconst drawerVisible = ref();\nconst loading = ref();\nconst formRef = ref<FormInstance>();\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst form = reactive({\n    noAuthSetting: '',\n});\n\nconst options = [\n    {\n        value: '200',\n        label: '200 - ' + i18n.global.t('setting.help200'),\n    },\n    {\n        value: '400',\n        label: '400 - ' + i18n.global.t('setting.error400'),\n    },\n    {\n        value: '401',\n        label: '401 - ' + i18n.global.t('setting.error401'),\n    },\n    {\n        value: '403',\n        label: '403 - ' + i18n.global.t('setting.error403'),\n    },\n    {\n        value: '404',\n        label: '404 - ' + i18n.global.t('setting.error404'),\n    },\n    {\n        value: '408',\n        label: '408 - ' + i18n.global.t('setting.error408'),\n    },\n    {\n        value: '416',\n        label: '416 - ' + i18n.global.t('setting.error416'),\n    },\n    {\n        value: '444',\n        label: '444 - ' + i18n.global.t('setting.error444'),\n    },\n    {\n        value: '500',\n        label: '500 - ' + i18n.global.t('setting.error500'),\n    },\n];\n\ninterface DialogProps {\n    noAuthSetting: string;\n    noAuthOptions: [{ value: string; label: string }];\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    form.noAuthSetting = params.noAuthSetting;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await updateSetting({ key: 'NoAuthSetting', value: form.noAuthSetting })\n            .then(() => {\n                loading.value = false;\n                handleClose();\n                emit('search');\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\"></style>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/ssl/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.panelSSL')\" @close=\"handleClose\" size=\"large\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.mode')\" prop=\"ssl\">\n                <el-radio-group v-model=\"form.ssl\">\n                    <el-radio-button value=\"Enable\">Strict</el-radio-button>\n                    <el-radio-button value=\"Mux\">Mux</el-radio-button>\n                </el-radio-group>\n                <span v-if=\"form.ssl === 'Enable'\" class=\"input-help\">{{ $t('setting.strictHelper') }}</span>\n                <span v-if=\"form.ssl === 'Mux'\" class=\"input-help\">{{ $t('setting.muxHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.certType')\">\n                <el-radio-group v-model=\"form.sslType\">\n                    <el-radio value=\"self\">{{ $t('setting.selfSigned') }}</el-radio>\n                    <el-radio value=\"select\">{{ $t('setting.select') }}</el-radio>\n                    <el-radio value=\"import\">{{ $t('commons.button.import') }}</el-radio>\n                </el-radio-group>\n                <span class=\"input-help\" v-if=\"form.sslType === 'self'\">\n                    {{ $t('setting.selfSignedHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item v-if=\"form.sslType === 'import'\" :label=\"$t('commons.button.import')\" prop=\"type\">\n                <el-select v-model=\"form.itemSSLType\">\n                    <el-option :label=\"$t('website.pasteSSL')\" value=\"paste\"></el-option>\n                    <el-option :label=\"$t('website.localSSL')\" value=\"local\"></el-option>\n                </el-select>\n            </el-form-item>\n\n            <el-form-item v-if=\"form.timeout\">\n                <el-tag>{{ $t('setting.domainOrIP') }} {{ form.domain }}</el-tag>\n                <el-tag class=\"p-ml-5\">{{ $t('setting.timeOut') }} {{ form.timeout }}</el-tag>\n                <el-button\n                    @click=\"onDownload\"\n                    class=\"p-ml-5\"\n                    v-if=\"form.sslType === 'self'\"\n                    type=\"primary\"\n                    link\n                    icon=\"Download\"\n                >\n                    {{ $t('setting.rootCrtDownload') }}\n                </el-button>\n            </el-form-item>\n\n            <div v-if=\"form.sslType === 'import' && form.itemSSLType === 'paste'\">\n                <el-form-item :label=\"$t('website.privateKey')\" prop=\"key\">\n                    <el-input v-model=\"form.key\" :rows=\"5\" type=\"textarea\" />\n                </el-form-item>\n                <el-form-item class=\"marginTop\" :label=\"$t('website.certificate')\" prop=\"cert\">\n                    <el-input v-model=\"form.cert\" :rows=\"5\" type=\"textarea\" />\n                </el-form-item>\n            </div>\n\n            <div v-if=\"form.sslType === 'import' && form.itemSSLType === 'local'\">\n                <el-form-item :label=\"$t('website.privateKey')\" prop=\"key\">\n                    <el-input v-model=\"form.key\">\n                        <template #prepend>\n                            <el-button icon=\"Folder\" @click=\"keyFileRef.acceptParams({ dir: false })\" />\n                        </template>\n                    </el-input>\n                </el-form-item>\n                <el-form-item class=\"marginTop\" :label=\"$t('website.certificate')\" prop=\"cert\">\n                    <el-input v-model=\"form.cert\">\n                        <template #prepend>\n                            <el-button icon=\"Folder\" @click=\"certFileRef.acceptParams({ dir: false })\" />\n                        </template>\n                    </el-input>\n                </el-form-item>\n            </div>\n\n            <div v-if=\"form.sslType === 'select'\">\n                <el-form-item :label=\"$t('setting.certificate')\" prop=\"sslID\">\n                    <el-select v-model=\"form.sslID\" @change=\"changeSSl(form.sslID)\">\n                        <el-option\n                            v-for=\"(item, index) in sslList\"\n                            :key=\"index\"\n                            :label=\"item.primaryDomain\"\n                            :value=\"item.id\"\n                        ></el-option>\n                    </el-select>\n                </el-form-item>\n                <el-descriptions\n                    class=\"marginTop\"\n                    :column=\"5\"\n                    border\n                    direction=\"vertical\"\n                    v-if=\"form.sslID > 0 && itemSSL\"\n                >\n                    <el-descriptions-item :label=\"$t('website.primaryDomain')\">\n                        {{ itemSSL.primaryDomain }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('website.otherDomains')\">\n                        {{ itemSSL.domains }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('ssl.provider')\">\n                        {{ getProvider(itemSSL.provider) }}\n                    </el-descriptions-item>\n                    <el-descriptions-item\n                        :label=\"$t('ssl.acmeAccount')\"\n                        v-if=\"itemSSL.acmeAccount?.email && itemSSL.provider !== 'manual'\"\n                    >\n                        {{ itemSSL.acmeAccount.email }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('website.expireDate')\">\n                        {{ dateFormatSimple(itemSSL.expireDate) }}\n                    </el-descriptions-item>\n                </el-descriptions>\n            </div>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveSSL(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"keyFileRef\" @choose=\"getKeyPath\" />\n    <FileList ref=\"certFileRef\" @choose=\"getCertPath\" />\n</template>\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { dateFormatSimple, getProvider } from '@/utils/util';\nimport { listLocalNodeSSL } from '@/api/modules/website';\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { downloadSSL, updateSSL } from '@/api/modules/setting';\nimport { Rules } from '@/global/form-rules';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Setting } from '@/api/interface/setting';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref();\nconst drawerVisible = ref();\nconst keyFileRef = ref();\nconst certFileRef = ref();\n\nconst form = reactive({\n    ssl: 'Enable',\n    domain: '',\n    sslType: 'self',\n    itemSSLType: 'paste',\n    sslID: null as number,\n    cert: '',\n    key: '',\n    rootPath: '',\n    timeout: '',\n});\n\nconst rules = reactive({\n    cert: [Rules.requiredInput],\n    key: [Rules.requiredInput],\n    sslID: [Rules.requiredSelect],\n});\n\nconst formRef = ref<FormInstance>();\n\nconst sslList = ref();\nconst itemSSL = ref();\n\ninterface DialogProps {\n    ssl: string;\n    sslType: string;\n    sslInfo?: Setting.SSLInfo;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    form.ssl = params.ssl === 'Disable' ? 'Enable' : params.ssl;\n    if (params.sslType.indexOf('-') !== -1) {\n        form.sslType = 'import';\n        form.itemSSLType = params.sslType.split('-')[1];\n    } else {\n        form.sslType = params.sslType;\n    }\n    form.cert = params.sslInfo?.cert || '';\n    form.key = params.sslInfo?.key || '';\n    form.rootPath = params.sslInfo?.rootPath || '';\n    form.domain = params.sslInfo?.domain || '';\n    form.timeout = params.sslInfo?.timeout || '';\n\n    if (params.sslInfo?.sslID) {\n        form.sslID = params.sslInfo.sslID;\n        const ssls = await listLocalNodeSSL({});\n        sslList.value = ssls.data || [];\n        changeSSl(params.sslInfo?.sslID);\n    } else {\n        loadSSLs();\n    }\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst loadSSLs = async () => {\n    const res = await listLocalNodeSSL({});\n    sslList.value = res.data || [];\n};\n\nconst changeSSl = (sslid: number) => {\n    const res = sslList.value.filter((element: Website.SSL) => {\n        return element.id == sslid;\n    });\n    itemSSL.value = res[0];\n};\n\nconst getKeyPath = (path: string) => {\n    form.key = path;\n};\n\nconst getCertPath = (path: string) => {\n    form.cert = path;\n};\n\nconst onDownload = async () => {\n    await downloadSSL().then(async (file) => {\n        const downloadUrl = window.URL.createObjectURL(new Blob([file]));\n        const a = document.createElement('a');\n        a.style.display = 'none';\n        a.href = downloadUrl;\n        a.download = 'server.crt';\n        const event = new MouseEvent('click');\n        a.dispatchEvent(event);\n    });\n};\n\nconst onSaveSSL = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(i18n.global.t('setting.sslChangeHelper'), 'https', {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        }).then(async () => {\n            let itemType = form.sslType;\n            if (form.sslType === 'import') {\n                itemType = form.itemSSLType === 'paste' ? 'import-paste' : 'import-local';\n            }\n            let param = {\n                ssl: form.ssl,\n                sslType: itemType,\n                domain: '',\n                sslID: form.sslID,\n                cert: form.cert,\n                key: form.key,\n            };\n            let href = window.location.href;\n            param.domain = href.split('//')[1].split(':')[0];\n            await updateSSL(param).then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                let href = window.location.href;\n                globalStore.isLogin = false;\n                let address = href.split('://')[1];\n                if (globalStore.entrance) {\n                    address = address.replaceAll('settings/safe', globalStore.entrance);\n                } else {\n                    address = address.replaceAll('settings/safe', '');\n                }\n                setTimeout(() => {\n                    window.open(`https://${address}`, '_self');\n                }, 1000);\n            });\n        });\n    });\n};\n\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.marginTop {\n    margin-top: 10px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/safe/timeout/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.expirationTime')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"timeoutFormRef\" @submit.prevent label-position=\"top\" :model=\"form\">\n            <el-form-item\n                :label=\"$t('setting.days')\"\n                prop=\"days\"\n                :rules=\"[Rules.integerNumberWith0, checkNumberRange(0, 60)]\"\n            >\n                <el-input clearable v-model.number=\"form.days\" />\n                <span class=\"input-help\">{{ $t('setting.expirationHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submitTimeout(timeoutFormRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateSetting } from '@/api/modules/setting';\nimport { FormInstance } from 'element-plus';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    expirationDays: number;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst timeoutFormRef = ref();\nconst form = reactive({\n    days: 0,\n});\n\nconst acceptParams = (params: DialogProps): void => {\n    form.days = params.expirationDays;\n    drawerVisible.value = true;\n};\n\nconst submitTimeout = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await updateSetting({ key: 'ExpirationDays', value: form.days + '' })\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/setting/snapshot/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.snapshot')\" @close=\"handleClose\" size=\"large\">\n        <fu-steps\n            class=\"steps\"\n            :space=\"50\"\n            ref=\"stepsRef\"\n            direction=\"vertical\"\n            :isLoading=\"loading\"\n            :finishButtonText=\"$t('commons.button.create')\"\n            @change=\"changeStep\"\n            :beforeLeave=\"beforeLeave\"\n        >\n            <fu-step id=\"baseData\" :title=\"$t('setting.stepBaseData')\">\n                <el-form class=\"mt-5\" label-position=\"top\" ref=\"formRef\" :model=\"form\" :rules=\"rules\">\n                    <el-form-item :label=\"$t('setting.backupAccount')\" prop=\"fromAccounts\">\n                        <el-select multiple @change=\"changeAccount(false)\" v-model=\"form.fromAccounts\" clearable>\n                            <div v-for=\"item in backupOptions\" :key=\"item.id\">\n                                <el-option v-if=\"item.type !== $t('setting.LOCAL')\" :value=\"item.id\" :label=\"item.name\">\n                                    {{ item.name }}\n                                    <el-tag class=\"tagClass\" type=\"primary\">{{ item.type }}</el-tag>\n                                </el-option>\n                                <el-option v-else :value=\"item.id\" :label=\"item.type\" />\n                            </div>\n                        </el-select>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('cronjob.default_download_path')\" prop=\"downloadAccountID\">\n                        <el-select v-model=\"form.downloadAccountID\" clearable>\n                            <div v-for=\"item in accountOptions\" :key=\"item.id\">\n                                <el-option v-if=\"item.type !== $t('setting.LOCAL')\" :value=\"item.id\" :label=\"item.name\">\n                                    {{ item.name }}\n                                    <el-tag class=\"tagClass\" type=\"primary\">{{ item.type }}</el-tag>\n                                </el-option>\n                                <el-option v-else :value=\"item.id\" :label=\"item.type\" />\n                            </div>\n                        </el-select>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('setting.compressPassword')\" prop=\"secret\">\n                        <el-input type=\"password\" show-password clearable v-model=\"form.secret\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('cronjob.timeout')\" prop=\"timeoutItem\">\n                        <el-input type=\"number\" class=\"selectClass\" v-model.number=\"form.timeoutItem\">\n                            <template #append>\n                                <el-select v-model=\"form.timeoutUnit\" style=\"width: 80px\">\n                                    <el-option :label=\"$t('commons.units.second')\" value=\"s\" />\n                                    <el-option :label=\"$t('commons.units.minute')\" value=\"m\" />\n                                    <el-option :label=\"$t('commons.units.hour')\" value=\"h\" />\n                                </el-select>\n                            </template>\n                        </el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                        <el-input type=\"textarea\" clearable v-model=\"form.description\" />\n                    </el-form-item>\n                </el-form>\n            </fu-step>\n            <fu-step id=\"appData\" :title=\"$t('setting.stepAppData')\">\n                <div class=\"mt-5 mb-5\" v-if=\"!form.appData || form.appData.length === 0\">\n                    <span class=\"input-help\">{{ $t('setting.noAppData') }}</span>\n                </div>\n                <div v-else>\n                    <el-checkbox\n                        class=\"ml-6\"\n                        v-model=\"form.backupAllImage\"\n                        @change=\"selectAllImage\"\n                        :label=\"$t('setting.selectAllImage')\"\n                        size=\"large\"\n                    />\n                    <el-tree\n                        style=\"max-width: 600px\"\n                        ref=\"appRef\"\n                        node-key=\"id\"\n                        :data=\"form.appData\"\n                        :props=\"defaultProps\"\n                        @check=\"onChangeAppData\"\n                        show-checkbox\n                    >\n                        <template #default=\"{ data }\">\n                            <div class=\"float-left\">\n                                <span>{{ loadApp18n(data.label) }}</span>\n                            </div>\n                            <div class=\"ml-4 float-left\">\n                                <span v-if=\"data.size\">{{ computeSize(data.size) }}</span>\n                            </div>\n                        </template>\n                    </el-tree>\n                </div>\n            </fu-step>\n            <fu-step id=\"panelData\" :title=\"$t('setting.stepPanelData')\">\n                <el-tree\n                    style=\"max-width: 600px\"\n                    ref=\"panelRef\"\n                    node-key=\"id\"\n                    :data=\"form.panelData\"\n                    :props=\"defaultProps\"\n                    @check=\"onChangePanelData\"\n                    show-checkbox\n                >\n                    <template #default=\"{ data }\">\n                        <div class=\"float-left\">\n                            <span>{{ data.label }}</span>\n                        </div>\n                        <div class=\"ml-4 float-left\">\n                            <span v-if=\"data.size\">{{ computeSize(data.size) }}</span>\n                        </div>\n                    </template>\n                </el-tree>\n            </fu-step>\n            <fu-step id=\"backupData\" :title=\"$t('setting.stepBackupData')\">\n                <div class=\"mt-5 mb-5\" v-if=\"!form.backupData || form.backupData.length === 0\">\n                    <span class=\"input-help\">{{ $t('setting.noBackupData') }}</span>\n                </div>\n                <div v-else>\n                    <el-tree\n                        style=\"max-width: 600px\"\n                        ref=\"backupRef\"\n                        node-key=\"id\"\n                        :data=\"form.backupData\"\n                        :props=\"defaultProps\"\n                        show-checkbox\n                    >\n                        <template #default=\"{ node, data }\">\n                            <div class=\"float-left\">\n                                <span>{{ load18n(node, data.label) }}</span>\n                            </div>\n                            <div class=\"ml-4 float-left\">\n                                <span v-if=\"data.size\">{{ computeSize(data.size) }}</span>\n                            </div>\n                        </template>\n                    </el-tree>\n                </div>\n            </fu-step>\n            <fu-step id=\"otherData\" :title=\"$t('setting.stepOtherData')\">\n                <div class=\"ml-5\">\n                    <el-checkbox v-model=\"form.withDockerConf\" :label=\"$t('setting.dockerConf')\" size=\"large\" />\n                </div>\n                <div class=\"ml-5\">\n                    <el-checkbox v-model=\"form.withOperationLog\" :label=\"$t('logs.operation')\" size=\"large\" />\n                </div>\n                <div class=\"ml-5\">\n                    <el-checkbox v-model=\"form.withLoginLog\" :label=\"$t('logs.login')\" size=\"large\" />\n                </div>\n                <div class=\"ml-5\">\n                    <el-checkbox v-model=\"form.withSystemLog\" :label=\"$t('logs.system')\" size=\"large\" />\n                </div>\n                <div class=\"ml-5\">\n                    <el-checkbox v-model=\"form.withTaskLog\" :label=\"$t('logs.task')\" size=\"large\" />\n                </div>\n                <div class=\"ml-5\">\n                    <el-checkbox v-model=\"form.withMonitorData\" :label=\"$t('setting.monitorData')\" size=\"large\" />\n                </div>\n            </fu-step>\n            <template #footer></template>\n            <fu-step id=\"ignoreFiles\" :title=\"$t('cronjob.exclusionRules')\">\n                <InputTag\n                    class=\"w-full\"\n                    v-model:tags=\"form.ignoreFiles\"\n                    :withFile=\"true\"\n                    :baseDir=\"baseDir\"\n                    :egHelp=\"$t('cronjob.exclusionRulesHelper')\"\n                />\n            </fu-step>\n        </fu-steps>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button @click=\"prev\" v-if=\"nowIndex !== 0\">{{ $t('commons.button.prev') }}</el-button>\n            <el-button type=\"primary\" v-if=\"nowIndex === 5\" :disabled=\"loading\" @click=\"submitAddSnapshot\">\n                {{ $t('commons.button.create') }}\n            </el-button>\n            <el-button @click=\"next\" v-else>{{ $t('commons.button.next') }}</el-button>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"handleClose\" />\n</template>\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { loadBaseDir, loadSnapshotInfo, snapshotCreate } from '@/api/modules/setting';\nimport { computeSize, newUUID, transferTimeToSecond } from '@/utils/util';\nimport i18n from '@/lang';\nimport TaskLog from '@/components/log/task/index.vue';\nimport InputTag from '@/components/input-tag/index.vue';\nimport { listBackupOptions } from '@/api/modules/backup';\nimport { Rules } from '@/global/form-rules';\nimport { ElForm } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst stepsRef = ref();\nconst nowIndex = ref(0);\n\nconst appRef = ref();\nconst panelRef = ref();\nconst backupRef = ref();\nconst taskLogRef = ref();\n\nconst baseDir = ref();\n\nconst backupOptions = ref();\nconst accountOptions = ref();\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\nconst form = reactive({\n    id: 0,\n    taskID: '',\n    downloadAccountID: '',\n    fromAccounts: [],\n    sourceAccountIDs: '',\n    description: '',\n    secret: '',\n\n    timeout: 3600,\n    timeoutItem: 3600,\n    timeoutUnit: 's',\n\n    backupAllImage: false,\n    withDockerConf: true,\n    withLoginLog: false,\n    withOperationLog: false,\n    withSystemLog: false,\n    withTaskLog: false,\n    withMonitorData: false,\n\n    panelData: [],\n    backupData: [],\n    appData: [],\n    ignoreFiles: [],\n});\nconst rules = reactive({\n    fromAccounts: [Rules.requiredSelect],\n    downloadAccountID: [Rules.requiredSelect],\n\n    timeoutItem: [Rules.number],\n});\n\nconst defaultProps = {\n    children: 'children',\n    label: 'label',\n    checked: 'isCheck',\n    disabled: 'isDisable',\n};\nconst drawerVisible = ref();\n\nconst emit = defineEmits(['search']);\nconst acceptParams = (): void => {\n    form.downloadAccountID = '';\n    form.fromAccounts = [];\n    form.description = '';\n    form.secret = '';\n    nowIndex.value = 0;\n    search();\n    loadBackups();\n    loadInstallDir();\n    drawerVisible.value = true;\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n    emit('search');\n};\n\nconst submitForm = async (formEl: any) => {\n    let bool;\n    if (!formEl) return;\n    await formEl.validate((valid: boolean) => {\n        if (valid) {\n            bool = true;\n        } else {\n            bool = false;\n        }\n    });\n    return bool;\n};\nconst beforeLeave = async (stepItem: any) => {\n    switch (stepItem.id) {\n        case 'baseData':\n            if (await submitForm(formRef.value)) {\n                stepsRef.value.next();\n                return true;\n            } else {\n                return false;\n            }\n        case 'appData':\n            if (form.appData && form.appData.length !== 0) {\n                let appChecks = appRef.value.getCheckedNodes();\n                loadCheckForSubmit(appChecks, form.appData);\n            }\n            return true;\n        case 'panelData':\n            let panelChecks = panelRef.value.getCheckedNodes();\n            loadCheckForSubmit(panelChecks, form.panelData);\n            return true;\n        case 'backupData':\n            if (!form.backupData || form.backupData.length === 0) {\n                return true;\n            }\n            if (form.backupData && form.backupData.length !== 0) {\n                let backupChecks = backupRef.value.getCheckedNodes();\n                loadCheckForSubmit(backupChecks, form.backupData);\n            }\n            return true;\n    }\n    return true;\n};\n\nfunction next() {\n    stepsRef.value.next();\n}\nfunction prev() {\n    stepsRef.value.prev();\n}\n\nconst loadApp18n = (label: string) => {\n    switch (label) {\n        case 'appData':\n            return i18n.global.t('setting.appDataLabel');\n        case 'appImage':\n        case 'appBackup':\n            return i18n.global.t('setting.' + label);\n        default:\n            return label;\n    }\n};\n\nconst loadBackups = async () => {\n    const res = await listBackupOptions();\n    backupOptions.value = [];\n    for (const item of res.data) {\n        if (item.id !== 0) {\n            backupOptions.value.push({ id: item.id, type: i18n.global.t('setting.' + item.type), name: item.name });\n        }\n    }\n    changeAccount(true);\n};\n\nconst changeStep = (currentStep: any) => {\n    nowIndex.value = currentStep.index;\n    switch (currentStep.id) {\n        case 'appData':\n            if (appRef.value) {\n                return;\n            }\n            nextTick(() => {\n                setAppDefaultCheck(form.appData);\n            });\n            return;\n        case 'panelData':\n            if (panelRef.value) {\n                return;\n            }\n            nextTick(() => {\n                setPanelDefaultCheck(form.panelData);\n                return;\n            });\n            return;\n        case 'backupData':\n            if (backupRef.value) {\n                return;\n            }\n            nextTick(() => {\n                setBackupDefaultCheck(form.backupData);\n                return;\n            });\n            return;\n    }\n};\n\nconst changeAccount = async (isInit: boolean) => {\n    accountOptions.value = [];\n    let isInAccounts = false;\n    for (const item of backupOptions.value) {\n        let exist = false;\n        for (const ac of form.fromAccounts) {\n            if (item.id == ac) {\n                exist = true;\n                break;\n            }\n        }\n        if (exist) {\n            if (item.id === form.downloadAccountID) {\n                isInAccounts = true;\n            }\n            accountOptions.value.push(item);\n        }\n    }\n    if (!isInAccounts && !isInit) {\n        form.downloadAccountID = form.fromAccounts.length === 0 ? undefined : form.fromAccounts[0];\n    }\n};\n\nconst load18n = (node: any, label: string) => {\n    if (node.level === 1) {\n        switch (label) {\n            case 'log':\n            case 'app':\n            case 'database':\n            case 'website':\n            case 'directory':\n                return i18n.global.t('setting.' + label + 'Label');\n            case 'system_snapshot':\n                return i18n.global.t('setting.snapshotLabel');\n            case 'master':\n                return i18n.global.t('xpack.node.masterBackup');\n            default:\n                return label;\n        }\n    }\n    if (node.level === 2) {\n        switch (label) {\n            case 'App':\n                return i18n.global.t('setting.appLabel');\n            case 'AppStore':\n                return i18n.global.t('menu.apps');\n            case 'shell':\n                return i18n.global.t('setting.shellLabel');\n            default:\n                return label;\n        }\n    }\n    return label;\n};\n\nconst submitAddSnapshot = async () => {\n    loading.value = true;\n    form.taskID = newUUID();\n    form.sourceAccountIDs = form.fromAccounts.join(',');\n    form.timeout = transferTimeToSecond(form.timeoutItem + form.timeoutUnit);\n    await snapshotCreate(form)\n        .then(() => {\n            loading.value = false;\n            drawerVisible.value = false;\n            emit('search');\n            openTaskLog(form.taskID);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst loadCheckForSubmit = (checks: any, list: any) => {\n    for (const item of list) {\n        let isCheck = false;\n        for (const check of checks) {\n            if (item.id == check.id) {\n                isCheck = true;\n                break;\n            }\n        }\n        item.isCheck = isCheck;\n        if (item.children) {\n            loadCheckForSubmit(checks, item.children);\n        }\n    }\n};\n\nconst selectAllImage = () => {\n    for (const item of form.appData) {\n        for (const item2 of item.children) {\n            if (item2.label === 'appImage') {\n                appRef.value.setChecked(item2.id, form.backupAllImage, false);\n            }\n        }\n    }\n};\n\nconst loadInstallDir = async () => {\n    const pathRes = await loadBaseDir();\n    baseDir.value = pathRes.data;\n};\n\nconst search = async () => {\n    loading.value = true;\n    await loadSnapshotInfo()\n        .then((res) => {\n            loading.value = false;\n            form.panelData = res.data.panelData || [];\n            form.backupData = res.data.backupData || [];\n            form.appData = res.data.appData || [];\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nfunction onChangeAppData(data: any, checked: any) {\n    let isChecked = false;\n    let keys = checked.checkedKeys || [];\n    for (const item of keys) {\n        if (data.id === item) {\n            isChecked = true;\n        }\n    }\n    let relationID = data.relationItemID;\n    if (!relationID) {\n        let list = data.children || [];\n        for (const item of list) {\n            if (item.label === 'appData' && item.relationItemID && !item.isDisable) {\n                relationID = item.relationItemID;\n                break;\n            }\n        }\n    }\n    if (!relationID) {\n        return;\n    }\n    changeRelation(relationID, isChecked);\n}\nconst changeRelation = (relationID: string, isChecked: boolean) => {\n    for (const item of form.appData) {\n        if (!item.children) {\n            return;\n        }\n        for (const relationItem of item.children) {\n            if (relationItem.id !== relationID) {\n                continue;\n            }\n            relationItem.isDisable = isChecked;\n            if (isChecked) {\n                appRef.value.setChecked(relationItem.id, isChecked, isChecked);\n            }\n            if (relationItem.relationID) {\n                changeRelation(relationItem.relationID, isChecked);\n            }\n            break;\n        }\n    }\n};\n\nconst onChangePanelData = (data: any, checked: any) => {\n    let isChecked = false;\n    let keys = checked.checkedKeys || [];\n    for (const item of keys) {\n        if (data.id === item) {\n            isChecked = true;\n        }\n    }\n    if (!isChecked && data.label === 'agent' && data.path.indexOf('1panel/agent') !== -1) {\n        for (const item of data.children) {\n            if (item.label !== 'package') {\n                item.isCheck = true;\n                panelRef.value.setChecked(item.id, true, true);\n            }\n        }\n    }\n};\nconst setAppDefaultCheck = async (list: any) => {\n    for (const item of list) {\n        if (item.isCheck) {\n            appRef.value.setChecked(item.id, true, true);\n        }\n        if (item.children) {\n            setAppDefaultCheck(item.children);\n        }\n    }\n};\nconst setPanelDefaultCheck = async (list: any) => {\n    for (const item of list) {\n        if (item.isCheck) {\n            panelRef.value.setChecked(item.id, true, true);\n            continue;\n        }\n        if (item.children) {\n            setPanelDefaultCheck(item.children);\n        }\n    }\n};\nconst setBackupDefaultCheck = async (list: any) => {\n    if (!form.backupData || form.backupData.length === 0) {\n        return;\n    }\n    for (const item of list) {\n        if (item.isCheck) {\n            backupRef.value.setChecked(item.id, true, true);\n            continue;\n        }\n        if (item.children) {\n            setBackupDefaultCheck(item.children);\n        }\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.steps {\n    width: 100%;\n    margin-top: 20px;\n    :deep(.el-step) {\n        .el-step__line {\n            background-color: var(--el-color-primary-light-5);\n        }\n        .el-step__head.is-success {\n            color: var(--el-color-primary-light-5);\n            border-color: var(--el-color-primary-light-5);\n        }\n        .el-step__icon {\n            color: var(--el-color-primary-light-2);\n        }\n        .el-step__icon.is-text {\n            border-radius: 50%;\n            border: 2px solid;\n            border-color: var(--el-color-primary-light-2);\n        }\n\n        .el-step__title.is-finish {\n            color: #717379;\n            font-size: 13px;\n            font-weight: bold;\n        }\n\n        .el-step__description.is-finish {\n            color: #606266;\n        }\n\n        .el-step__title.is-success {\n            font-weight: bold;\n            color: var(--el-color-primary-light-2);\n        }\n\n        .el-step__title.is-process {\n            font-weight: bold;\n            color: var(--el-text-color-regular);\n        }\n    }\n}\n.tagClass {\n    float: right;\n    margin-right: 10px;\n    font-size: 12px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/snapshot/import/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('commons.button.sync')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('setting.backupAccount')\" prop=\"from\">\n                <el-select @change=\"loadFiles()\" v-model=\"form.backupAccountID\" clearable>\n                    <div v-for=\"item in backupOptions\" :key=\"item.id\">\n                        <el-option v-if=\"item.type !== $t('setting.LOCAL')\" :value=\"item.id\" :label=\"item.name\">\n                            {{ item.name }}\n                            <el-tag class=\"tagClass\" type=\"primary\">{{ item.type }}</el-tag>\n                        </el-option>\n                        <el-option v-else :value=\"item.id\" :label=\"item.type\" />\n                    </div>\n                </el-select>\n                <div v-if=\"form.backupAccountID === localAccountID\">\n                    <span class=\"import-help\">{{ $t('setting.importHelper') }}</span>\n                    <span @click=\"toFolder()\" class=\"import-link-help\">{{ backupPath }}</span>\n                </div>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"names\">\n                <el-select v-model=\"form.names\" multiple clearable>\n                    <el-option\n                        :disabled=\"checkDisable(item)\"\n                        v-for=\"item in fileNames\"\n                        :key=\"item\"\n                        :value=\"item\"\n                        :label=\"item\"\n                    />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input type=\"textarea\" clearable v-model=\"form.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button :disabled=\"loading\" @click=\"drawerVisible = false\">\n                {{ $t('commons.button.cancel') }}\n            </el-button>\n            <el-button :disabled=\"loading\" type=\"primary\" @click=\"submitImport(formRef)\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport i18n from '@/lang';\nimport { snapshotImport } from '@/api/modules/setting';\nimport { listBackupOptions, getFilesFromBackup, getLocalBackupDir } from '@/api/modules/backup';\nimport { Rules } from '@/global/form-rules';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { checkFile } from '@/api/modules/files';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst drawerVisible = ref(false);\nconst loading = ref();\n\nconst formRef = ref();\nconst backupOptions = ref();\nconst fileNames = ref();\nconst existNames = ref();\nconst backupPath = ref('');\nconst localAccountID = ref(0);\n\nconst form = reactive({\n    backupAccountID: 0,\n    names: [],\n    description: '',\n});\n\nconst rules = reactive({\n    backupAccountID: [Rules.requiredSelect],\n    names: [Rules.requiredSelect],\n});\n\ninterface DialogProps {\n    names: Array<string>;\n}\n\nconst acceptParams = (params: DialogProps): void => {\n    form.backupAccountID = undefined;\n    existNames.value = params.names;\n    form.names = [] as Array<string>;\n    loadBackups();\n    loadBackupDir();\n    drawerVisible.value = true;\n};\nconst emit = defineEmits(['search']);\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst checkDisable = (val: string) => {\n    for (const item of existNames.value) {\n        if (val === item + '.tar.gz') {\n            return true;\n        }\n    }\n    return false;\n};\nconst toFolder = async () => {\n    const res = await checkFile(backupPath.value, true);\n    if (res.data) {\n        routerToFileWithPath(backupPath.value);\n    } else {\n        MsgError(i18n.global.t('file.noSuchFile'));\n    }\n};\n\nconst loadBackupDir = async () => {\n    const res = await getLocalBackupDir(globalStore.currentNode);\n    backupPath.value = res.data;\n};\n\nconst submitImport = async (formEl: FormInstance | undefined) => {\n    loading.value = true;\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        await snapshotImport(form)\n            .then(() => {\n                emit('search');\n                loading.value = false;\n                drawerVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst loadBackups = async () => {\n    loading.value = true;\n    await listBackupOptions()\n        .then((res) => {\n            loading.value = false;\n            backupOptions.value = [];\n            for (const item of res.data) {\n                if (item.type === 'LOCAL') {\n                    localAccountID.value = item.id;\n                }\n                backupOptions.value.push({\n                    id: item.id,\n                    type: i18n.global.t('setting.' + item.type),\n                    name: item.name,\n                });\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadFiles = async () => {\n    form.names = [];\n    const res = await getFilesFromBackup(form.backupAccountID);\n    fileNames.value = res.data || [];\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.import-help {\n    font-size: 12px;\n    color: #8f959e;\n}\n.import-link-help {\n    color: $primary-color;\n    cursor: pointer;\n}\n\n.import-link-help:hover {\n    opacity: 0.6;\n}\n\n.tagClass {\n    float: right;\n    margin-right: 10px;\n    font-size: 12px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/snapshot/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" v-if=\"!isRecordShow\" :title=\"$t('setting.snapshot', 2)\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onCreate()\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onImport()\">\n                    {{ $t('commons.button.sync') }}\n                </el-button>\n                <el-button type=\"primary\" plain :disabled=\"selects.length === 0\" @click=\"batchDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"snapshot-refresh\" ref=\"timerRef\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @sort-change=\"search\"\n                    class=\"mt-5\"\n                    @search=\"search\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column\n                        show-overflow-tooltip\n                        :label=\"$t('commons.table.name')\"\n                        min-width=\"100\"\n                        prop=\"name\"\n                        sortable\n                        fix\n                    />\n                    <el-table-column prop=\"version\" :label=\"$t('app.version')\" />\n                    <el-table-column :label=\"$t('setting.backupAccount')\" :min-width=\"120\" prop=\"from\">\n                        <template #default=\"{ row }\">\n                            <div v-for=\"(item, index) of row.sourceAccounts\" :key=\"index\">\n                                <div v-if=\"row.expand || (!row.expand && index < 3)\">\n                                    <span type=\"info\">\n                                        <span>\n                                            {{ item === 'localhost' ? $t('setting.LOCAL') : item }}\n                                        </span>\n                                        <el-icon\n                                            v-if=\"item === row.downloadAccount\"\n                                            size=\"12\"\n                                            class=\"relative top-px left-1\"\n                                        >\n                                            <Star />\n                                        </el-icon>\n                                    </span>\n                                </div>\n                            </div>\n                            <div v-if=\"!row.expand && row.sourceAccounts?.length > 3\">\n                                <el-button type=\"primary\" link @click=\"row.expand = true\">\n                                    {{ $t('commons.button.expand') }}...\n                                </el-button>\n                            </div>\n                            <div v-if=\"row.expand && row.sourceAccounts?.length > 3\">\n                                <el-button type=\"primary\" link @click=\"row.expand = false\">\n                                    {{ $t('commons.button.collapse') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('file.size')\" prop=\"size\" min-width=\"60\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.hasLoad\">\n                                <span v-if=\"row.size\">\n                                    {{ computeSize(row.size) }}\n                                </span>\n                                <span v-else>-</span>\n                            </div>\n                            <div v-if=\"!row.hasLoad\">\n                                <el-button link loading></el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" min-width=\"80\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <div>\n                                <el-button\n                                    @click=\"openTaskLog(row.taskID)\"\n                                    link\n                                    v-if=\"row.status === 'Waiting'\"\n                                    type=\"primary\"\n                                >\n                                    {{ $t('setting.snapshot') }}{{ $t('commons.status.waiting') }}\n                                </el-button>\n                                <el-button\n                                    @click=\"openTaskLog(row.taskID)\"\n                                    link\n                                    v-if=\"row.status === 'Failed'\"\n                                    type=\"danger\"\n                                >\n                                    {{ $t('setting.snapshot') }}{{ $t('commons.status.error') }}\n                                </el-button>\n                                <el-button\n                                    v-if=\"row.status === 'Failed'\"\n                                    type=\"danger\"\n                                    class=\"retry\"\n                                    icon=\"Warning\"\n                                    link\n                                    @click=\"reCreate(row)\"\n                                />\n                                <el-button\n                                    @click=\"openTaskLog(row.taskID)\"\n                                    link\n                                    v-if=\"row.status === 'Success'\"\n                                    type=\"success\"\n                                >\n                                    {{ $t('setting.snapshot') }}{{ $t('commons.status.success') }}\n                                </el-button>\n                            </div>\n                            <div v-if=\"row.recoverStatus\">\n                                <el-button\n                                    @click=\"openWithResource(row, 'recover')\"\n                                    link\n                                    v-if=\"row.recoverStatus === 'Waiting'\"\n                                    type=\"primary\"\n                                >\n                                    {{ $t('commons.button.recover') }}{{ $t('commons.status.waiting') }}\n                                </el-button>\n                                <el-button\n                                    v-if=\"row.recoverStatus === 'Failed'\"\n                                    @click=\"openWithResource(row, 'recover')\"\n                                    type=\"danger\"\n                                    link\n                                >\n                                    {{ $t('commons.button.recover') }}{{ $t('commons.status.error') }}\n                                </el-button>\n                                <el-button\n                                    v-if=\"row.recoverStatus === 'Failed'\"\n                                    type=\"danger\"\n                                    class=\"retry\"\n                                    icon=\"Warning\"\n                                    link\n                                    @click=\"onRecover(row)\"\n                                />\n                                <el-button\n                                    @click=\"openWithResource(row, 'recover')\"\n                                    link\n                                    v-if=\"row.recoverStatus === 'Success'\"\n                                    type=\"success\"\n                                >\n                                    {{ $t('commons.button.recover') }}{{ $t('commons.status.success') }}\n                                </el-button>\n                            </div>\n                            <div v-if=\"row.rollbackStatus\">\n                                <el-button link v-if=\"row.rollbackStatus === 'Waiting'\" type=\"primary\">\n                                    {{ $t('setting.rollback') }}{{ $t('commons.status.waiting') }}\n                                </el-button>\n                                <el-button\n                                    link\n                                    v-if=\"row.rollbackStatus === 'Failed'\"\n                                    @click=\"reRollback(row)\"\n                                    type=\"danger\"\n                                >\n                                    {{ $t('setting.rollback') }}{{ $t('commons.status.error') }}\n                                </el-button>\n                                <el-button\n                                    v-if=\"row.rollbackStatus === 'Failed'\"\n                                    icon=\"Warning\"\n                                    class=\"retry\"\n                                    type=\"danger\"\n                                    link\n                                    @click=\"reRollback(row)\"\n                                />\n                                <el-button link v-if=\"row.recoverStatus === 'Success'\" type=\"success\">\n                                    {{ $t('setting.rollback') }}{{ $t('commons.status.success') }}\n                                </el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.description')\" prop=\"description\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            <fu-input-rw-switch\n                                v-model=\"row.description\"\n                                @enter=\"onChange(row)\"\n                                @blur=\"onChange(row)\"\n                            />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        sortable\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                    />\n                    <fu-table-operations\n                        width=\"240px\"\n                        :ellipsis=\"10\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <RecoverStatus ref=\"recoverStatusRef\" @search=\"search()\"></RecoverStatus>\n        <SnapshotCreate ref=\"createRef\" @search=\"search()\" />\n        <SnapshotImport ref=\"importRef\" @search=\"search()\" />\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"onSubmitDelete()\">\n            <template #content>\n                <el-form class=\"mt-4 mb-1\" ref=\"deleteForm\" label-position=\"left\">\n                    <el-form-item>\n                        <el-checkbox v-model=\"cleanData\" :label=\"$t('cronjob.cleanData')\" />\n                        <span class=\"input-help\">\n                            {{ $t('setting.deleteHelper') }}\n                        </span>\n                    </el-form-item>\n                </el-form>\n            </template>\n        </OpDialog>\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"search()\" />\n        <SnapRecover ref=\"recoverRef\" @search=\"search\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n    searchSnapshotPage,\n    snapshotDelete,\n    snapshotRecreate,\n    snapshotRollback,\n    updateSnapshotDescription,\n} from '@/api/modules/setting';\nimport { onMounted, reactive, ref } from 'vue';\nimport { computeSize, dateFormat, newUUID } from '@/utils/util';\nimport { ElForm } from 'element-plus';\nimport i18n from '@/lang';\nimport { Setting } from '@/api/interface/setting';\nimport TaskLog from '@/components/log/task/index.vue';\nimport RecoverStatus from '@/views/setting/snapshot/status/index.vue';\nimport SnapshotImport from '@/views/setting/snapshot/import/index.vue';\nimport SnapshotCreate from '@/views/setting/snapshot/create/index.vue';\nimport SnapRecover from '@/views/setting/snapshot/recover/index.vue';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { loadOsInfo } from '@/api/modules/dashboard';\nimport { loadRecordSize } from '@/api/modules/backup';\n\nconst loading = ref(false);\nconst data = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    cacheSizeKey: 'snapshot-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('snapshot-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst opRef = ref();\n\nconst createRef = ref();\nconst recoverStatusRef = ref();\nconst importRef = ref();\nconst isRecordShow = ref();\nconst taskLogRef = ref();\nconst recoverRef = ref();\n\nconst operateIDs = ref();\nconst cleanData = ref();\n\nconst onImport = () => {\n    let names = [];\n    for (const item of data.value) {\n        names.push(item.name);\n    }\n    importRef.value.acceptParams({ names: names });\n};\n\nconst onCreate = () => {\n    createRef.value.acceptParams();\n};\n\nconst reCreate = (row: any) => {\n    ElMessageBox.confirm(row.message, i18n.global.t('setting.reCreate'), {\n        confirmButtonText: i18n.global.t('commons.button.retry'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'error',\n    }).then(async () => {\n        await snapshotRecreate(row.id)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(row.taskID);\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\nconst openWithResource = (row: any, op: string) => {\n    op = op === 'create' ? 'TaskCreate' : 'TaskRecover';\n    taskLogRef.value.openWithResourceID('Snapshot', op, row.id);\n};\n\nconst reRollback = (row: any) => {\n    ElMessageBox.confirm(row.rollbackMessage, i18n.global.t('setting.reRollback'), {\n        confirmButtonText: i18n.global.t('commons.button.retry'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'error',\n    }).then(async () => {\n        let param = {\n            id: row.id,\n            taskID: newUUID(),\n            isNew: false,\n            reDownload: false,\n            secret: '',\n        };\n        await snapshotRollback(param)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(row.taskRollbackID || param.taskID);\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onChange = async (info: any) => {\n    await updateSnapshotDescription({ id: info.id, description: info.description });\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst onRecover = async (row: any) => {\n    if (row.downloadAccount.indexOf('ALIYUN') !== -1 && row.size > 100 * 1024 * 1024) {\n        MsgError(i18n.global.t('setting.ALIYUNRecover'));\n        return;\n    }\n    loading.value = true;\n    await loadOsInfo()\n        .then((res) => {\n            loading.value = false;\n            let params = {\n                id: row.id,\n                taskID: row.taskRecoverID,\n                isNew: row.recoverStatus === '',\n                name: row.name,\n                reDownload: false,\n                secret: row.secret,\n\n                arch: res.data.kernelArch,\n                size: row.size,\n                freeSize: res.data.diskSize,\n\n                interruptStep: row.interruptStep,\n                status: row.recoverStatus,\n                message: row.recoverMessage,\n            };\n            recoverRef.value.acceptParams(params);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst batchDelete = async (row: Setting.SnapshotInfo | null) => {\n    let names = [];\n    let ids = [];\n    if (row) {\n        ids.push(row.id);\n        names.push(row.name);\n    } else {\n        selects.value.forEach((item: Setting.SnapshotInfo) => {\n            ids.push(item.id);\n            names.push(item.name);\n        });\n    }\n    operateIDs.value = ids;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('setting.snapshot'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\n\nconst onSubmitDelete = async () => {\n    loading.value = true;\n    await snapshotDelete({ ids: operateIDs.value, deleteWithFile: cleanData.value })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.recover'),\n        click: (row: any) => {\n            onRecover(row);\n        },\n        disabled: (row: any) => {\n            return !(row.status === 'Success');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        icon: 'Delete',\n        click: batchDelete,\n    },\n];\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    loading.value = true;\n    await searchSnapshotPage(params)\n        .then((res) => {\n            loading.value = false;\n            loadSize(params);\n            cleanData.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadSize = async (params: any) => {\n    params.type = 'snapshot';\n    await loadRecordSize(params)\n        .then((res) => {\n            let stats = res.data || [];\n            if (stats.length === 0) {\n                return;\n            }\n            for (const snap of data.value) {\n                for (const item of stats) {\n                    if (snap.id === item.id) {\n                        snap.hasLoad = true;\n                        snap.size = item.size;\n                        break;\n                    }\n                }\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.retry {\n    margin-left: -1px;\n    margin-top: -2px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/setting/snapshot/recover/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.recover')\" @close=\"handleClose\">\n        <el-form ref=\"recoverForm\" label-position=\"top\" v-loading=\"loading\">\n            <div style=\"margin-left: 20px; line-height: 32px\" v-if=\"recoverReq.isNew\">\n                {{ $t('setting.recoverHelper', [recoverReq.name]) }}\n                <div>\n                    <el-button style=\"margin-top: -4px\" type=\"warning\" link icon=\"WarningFilled\" />\n                    {{ $t('setting.recoverHelper1') }}\n                </div>\n                <div>\n                    <el-button\n                        style=\"margin-top: -4px\"\n                        :type=\"isSizeOk() ? 'success' : 'danger'\"\n                        link\n                        :icon=\"isSizeOk() ? 'CircleCheckFilled' : 'CircleCloseFilled'\"\n                    />\n                    {{ $t('setting.recoverHelper2', [computeSize(recoverReq.size), computeSize(recoverReq.freeSize)]) }}\n                </div>\n                <div>\n                    <el-button\n                        style=\"margin-top: -4px\"\n                        :type=\"isArchOk() ? 'success' : 'danger'\"\n                        link\n                        :icon=\"isArchOk() ? 'CircleCheckFilled' : 'CircleCloseFilled'\"\n                    />\n                    {{ $t('setting.recoverHelper3', [recoverReq.arch]) }}\n                </div>\n            </div>\n            <el-form-item v-if=\"!recoverReq.isNew\" :label=\"$t('setting.recoverFailed')\">\n                <span>{{ recoverReq.message }}</span>\n            </el-form-item>\n            <el-form-item v-if=\"!recoverReq.isNew\" :label=\"$t('setting.snapshotLabel')\">\n                <el-checkbox v-model=\"recoverReq.reDownload\">{{ $t('setting.reDownload') }}</el-checkbox>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.compressPassword')\">\n                <el-input v-model=\"recoverReq.secret\" :placeholder=\"$t('setting.backupRecoverMessage')\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button @click=\"onRollback\" v-if=\"canRollback()\" :disabled=\"loading\">\n                    {{ $t('setting.rollback') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" v-if=\"!recoverReq.isNew\" :disabled=\"loading\">\n                    {{ $t('commons.button.retry') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" v-if=\"recoverReq.isNew\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"handleClose\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { snapshotRecover, snapshotRollback } from '@/api/modules/setting';\nimport { computeSize, newUUID } from '@/utils/util';\n\nlet loading = ref(false);\nlet open = ref(false);\nconst recoverForm = ref<FormInstance>();\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst taskLogRef = ref();\n\ninterface DialogProps {\n    id: number;\n    isNew: boolean;\n    name: string;\n    taskID: string;\n    reDownload: boolean;\n    arch: string;\n    size: number;\n    freeSize: number;\n    interruptStep: string;\n    status: string;\n    message: string;\n}\n\nlet recoverReq = ref({\n    id: 0,\n    isNew: true,\n    name: '',\n    taskID: '',\n    reDownload: true,\n    secret: '',\n    arch: '',\n    size: 0,\n    freeSize: 0,\n    interruptStep: '',\n    status: '',\n    message: '',\n});\n\nconst handleClose = () => {\n    open.value = false;\n    emit('search');\n};\nconst acceptParams = (params: DialogProps): void => {\n    recoverReq.value = {\n        id: params.id,\n        isNew: params.isNew,\n        name: params.name,\n        taskID: params.taskID,\n        reDownload: params.reDownload,\n        secret: '',\n        arch: params.arch,\n        size: params.size,\n        freeSize: params.freeSize,\n        interruptStep: params.interruptStep,\n        status: params.status,\n        message: params.message,\n    };\n    open.value = true;\n};\n\nconst isSizeOk = () => {\n    if (recoverReq.value.size === 0 || recoverReq.value.freeSize === 0) {\n        return false;\n    }\n    return recoverReq.value.size * 2 < recoverReq.value.freeSize;\n};\nconst isArchOk = () => {\n    if (recoverReq.value.arch.length === 0) {\n        return false;\n    }\n    return recoverReq.value.name.indexOf(recoverReq.value.arch) !== -1;\n};\n\nconst canRollback = () => {\n    return (\n        !recoverReq.value.isNew &&\n        recoverReq.value.interruptStep !== '' &&\n        recoverReq.value.interruptStep !== 'RecoverDownload' &&\n        recoverReq.value.interruptStep !== 'RecoverDecompress' &&\n        recoverReq.value.interruptStep !== 'BackupBeforeRecover'\n    );\n};\n\nconst submit = async () => {\n    loading.value = true;\n    let param = {\n        id: recoverReq.value.id,\n        taskID: newUUID(),\n        isNew: recoverReq.value.isNew,\n        reDownload: recoverReq.value.reDownload,\n        secret: recoverReq.value.secret,\n    };\n    await snapshotRecover(param)\n        .then(() => {\n            emit('search');\n            loading.value = false;\n            handleClose();\n            openTaskLog(recoverReq.value.taskID || param.taskID);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst onRollback = async () => {\n    ElMessageBox.confirm(i18n.global.t('setting.rollbackHelper'), i18n.global.t('setting.rollback'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        await snapshotRollback({\n            id: recoverReq.value.id,\n            taskID: newUUID(),\n            isNew: false,\n            reDownload: false,\n            secret: '',\n        })\n            .then(() => {\n                emit('search');\n                handleClose();\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style scoped lang=\"scss\"></style>\n"
  },
  {
    "path": "frontend/src/views/setting/snapshot/status/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('setting.recoverDetail')\" @close=\"handleClose\" size=\"small\">\n        <el-form label-position=\"top\" v-loading=\"loading\">\n            <span class=\"card-title\">{{ $t('commons.button.recover') }}</span>\n            <el-divider class=\"divider\" />\n            <div v-if=\"!snapInfo.recoverStatus && !snapInfo.lastRecoveredAt\">\n                <el-alert center class=\"alert\" style=\"height: 257px\" :closable=\"false\">\n                    <el-button size=\"large\" round plain type=\"primary\" @click=\"recoverSnapshot(true)\">\n                        {{ $t('commons.button.recover') }}\n                    </el-button>\n                </el-alert>\n            </div>\n            <el-card v-else class=\"mini-border-card\">\n                <div v-if=\"!snapInfo.recoverStatus\" class=\"mini-border-card\">\n                    <div v-if=\"snapInfo.lastRecoveredAt\">\n                        <el-form-item :label=\"$t('commons.table.status')\">\n                            <el-tag type=\"success\">\n                                {{ $t('commons.status.success') }}\n                            </el-tag>\n                            <el-button @click=\"recoverSnapshot(true)\" style=\"margin-left: 10px\" type=\"primary\">\n                                {{ $t('commons.button.recover') }}\n                            </el-button>\n                        </el-form-item>\n                        <el-form-item :label=\"$t('setting.lastRecoverAt')\">\n                            {{ snapInfo.lastRecoveredAt }}\n                        </el-form-item>\n                    </div>\n                </div>\n                <div v-else>\n                    <el-form-item :label=\"$t('commons.table.status')\">\n                        <el-tag type=\"danger\" v-if=\"snapInfo.recoverStatus === 'Failed'\">\n                            {{ $t('commons.status.failed') }}\n                        </el-tag>\n                        <el-tag type=\"success\" v-if=\"snapInfo.recoverStatus === 'Success'\">\n                            {{ $t('commons.status.success') }}\n                        </el-tag>\n                        <el-tag type=\"info\" v-if=\"snapInfo.recoverStatus === 'Waiting'\">\n                            {{ $t('commons.status.waiting') }}\n                        </el-tag>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('setting.lastRecoverAt')\" v-if=\"snapInfo.recoverStatus !== 'Waiting'\">\n                        {{ snapInfo.lastRecoveredAt }}\n                    </el-form-item>\n                    <div v-if=\"snapInfo.recoverStatus === 'Failed'\">\n                        <el-form-item :label=\"$t('commons.button.log')\">\n                            <span style=\"word-break: break-all; flex-wrap: wrap; word-wrap: break-word\">\n                                {{ snapInfo.recoverMessage }}\n                            </span>\n                        </el-form-item>\n                        <el-form-item>\n                            <el-button @click=\"recoverSnapshot(false)\" type=\"primary\">\n                                {{ $t('commons.button.retry') }}\n                            </el-button>\n                        </el-form-item>\n                    </div>\n                </div>\n            </el-card>\n\n            <div v-if=\"snapInfo.recoverStatus === 'Failed'\">\n                <span class=\"card-title\">{{ $t('setting.rollback') }}</span>\n                <el-divider class=\"divider\" />\n                <div v-if=\"!snapInfo.rollbackStatus && !snapInfo.lastRollbackedAt\">\n                    <el-alert center class=\"alert\" style=\"height: 257px\" :closable=\"false\">\n                        <el-button size=\"large\" round plain type=\"primary\" @click=\"rollbackSnapshot()\">\n                            {{ $t('setting.rollback') }}\n                        </el-button>\n                    </el-alert>\n                </div>\n                <div v-if=\"!snapInfo.rollbackStatus\">\n                    <div v-if=\"snapInfo.lastRollbackedAt\">\n                        <el-form-item :label=\"$t('commons.table.status')\">\n                            <el-tag type=\"success\">\n                                {{ $t('commons.status.success') }}\n                            </el-tag>\n                            <el-button @click=\"rollbackSnapshot\" style=\"margin-left: 10px\" type=\"primary\">\n                                {{ $t('setting.rollback') }}\n                            </el-button>\n                        </el-form-item>\n                        <el-form-item :label=\"$t('setting.lastRollbackAt')\">\n                            {{ snapInfo.lastRollbackedAt }}\n                        </el-form-item>\n                    </div>\n                </div>\n                <div v-else>\n                    <el-form-item :label=\"$t('commons.table.status')\">\n                        <el-tag type=\"success\" v-if=\"snapInfo.rollbackStatus === 'Success'\">\n                            {{ $t('commons.status.success') }}\n                        </el-tag>\n                        <el-tag type=\"danger\" v-if=\"snapInfo.rollbackStatus === 'Failed'\">\n                            {{ $t('commons.status.failed') }}\n                        </el-tag>\n                        <el-tag type=\"info\" v-if=\"snapInfo.rollbackStatus === 'Waiting'\">\n                            {{ $t('commons.status.waiting') }}\n                        </el-tag>\n                        <el-button\n                            style=\"margin-left: 15px\"\n                            :disabled=\"snapInfo.rollbackStatus !== 'Success'\"\n                            @click=\"rollbackSnapshot\"\n                        >\n                            {{ $t('setting.rollback') }}\n                        </el-button>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('setting.lastRollbackAt')\" v-if=\"snapInfo.rollbackStatus !== 'Waiting'\">\n                        {{ snapInfo.lastRollbackedAt }}\n                    </el-form-item>\n                    <div v-if=\"snapInfo.rollbackStatus === 'Failed'\">\n                        <el-form-item :label=\"$t('commons.button.log')\">\n                            <span style=\"word-break: break-all; flex-wrap: wrap; word-wrap: break-word\">\n                                {{ snapInfo.rollbackMessage }}\n                            </span>\n                        </el-form-item>\n                        <el-form-item>\n                            <el-button @click=\"rollbackSnapshot()\" type=\"primary\">\n                                {{ $t('commons.button.retry') }}\n                            </el-button>\n                        </el-form-item>\n                    </div>\n                </div>\n            </div>\n        </el-form>\n    </DrawerPro>\n    <SnapRecover ref=\"recoverRef\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { Setting } from '@/api/interface/setting';\nimport { ElMessageBox } from 'element-plus';\nimport i18n from '@/lang';\nimport { snapshotRollback } from '@/api/modules/setting';\nimport { MsgSuccess } from '@/utils/message';\nimport { loadOsInfo } from '@/api/modules/dashboard';\nimport SnapRecover from '@/views/setting/snapshot/recover/index.vue';\nimport { newUUID } from '@/utils/util';\n\nconst drawerVisible = ref(false);\nconst snapInfo = ref();\nconst loading = ref();\n\nconst recoverRef = ref();\n\ninterface DialogProps {\n    snapInfo: Setting.SnapshotInfo;\n}\nconst acceptParams = (params: DialogProps): void => {\n    snapInfo.value = params.snapInfo;\n    drawerVisible.value = true;\n};\nconst emit = defineEmits(['search']);\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst recoverSnapshot = async (isNew: boolean) => {\n    loading.value = true;\n    await loadOsInfo()\n        .then((res) => {\n            loading.value = false;\n            let params = {\n                id: snapInfo.value.id,\n                isNew: isNew,\n                name: snapInfo.value.name,\n                reDownload: false,\n                secret: snapInfo.value.secret,\n\n                arch: res.data.kernelArch,\n                size: snapInfo.value.size,\n                freeSize: res.data.diskSize,\n            };\n            recoverRef.value.acceptParams(params);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst rollbackSnapshot = async () => {\n    ElMessageBox.confirm(i18n.global.t('setting.rollbackHelper'), i18n.global.t('setting.rollback'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        let taskID = newUUID();\n        await snapshotRollback({ id: snapInfo.value.id, isNew: false, reDownload: false, secret: '', taskID: taskID })\n            .then(() => {\n                emit('search');\n                loading.value = false;\n                drawerVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.divider {\n    display: block;\n    height: 1px;\n    width: 100%;\n    margin: 12px 0;\n    border-top: 1px var(--el-border-color) var(--el-border-style);\n}\n.alert {\n    background-color: rgba(0, 94, 235, 0.03);\n}\n\n.card-title {\n    font-size: 14px;\n    font-weight: 500;\n    line-height: 25px;\n    color: var(--el-button-text-color, var(--el-text-color-regular));\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/terminal/command/import/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"visible\" :title=\"$t('commons.button.import')\" size=\"large\">\n        <div>\n            <el-upload\n                action=\"#\"\n                :auto-upload=\"false\"\n                ref=\"uploadRef\"\n                class=\"float-left mt-2\"\n                :show-file-list=\"false\"\n                :limit=\"1\"\n                accept=\".csv\"\n                :on-change=\"fileOnChange\"\n                :on-exceed=\"handleExceed\"\n                v-model:file-list=\"uploaderFiles\"\n            >\n                <el-button class=\"float-left\" type=\"primary\">{{ $t('commons.button.upload') }}</el-button>\n            </el-upload>\n\n            <el-button :disabled=\"selects.length === 0\" @click=\"onImport\" class=\"ml-2 mt-2\">\n                {{ $t('commons.button.import') }}\n            </el-button>\n\n            <el-select\n                filterable\n                :placeholder=\"$t('terminal.groupChange')\"\n                v-model=\"currentGroup\"\n                @change=\"changeGroup\"\n                class=\"p-w-200 ml-2 mt-2\"\n            >\n                <div v-for=\"item in groupList\" :key=\"item.id\">\n                    <el-option v-if=\"item.name === 'Default'\" :label=\"$t('commons.table.default')\" :value=\"item.id\" />\n                    <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                </div>\n            </el-select>\n\n            <el-card class=\"mt-2 w-full\" v-loading=\"loading\">\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    @search=\"search\"\n                    v-model:selects=\"selects\"\n                    :data=\"pageData\"\n                    :height=\"440\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        :min-width=\"80\"\n                        prop=\"name\"\n                        show-overflow-tooltip\n                    />\n                    <el-table-column\n                        :label=\"$t('commons.table.group')\"\n                        show-overflow-tooltip\n                        min-width=\"80\"\n                        prop=\"groupBelong\"\n                        fix\n                    >\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.groupBelong === 'Default'\">{{ $t('commons.table.default') }}</span>\n                            <span v-else>{{ row.groupBelong }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('terminal.command')\"\n                        :min-width=\"120\"\n                        prop=\"command\"\n                        show-overflow-tooltip\n                    />\n                </ComplexTable>\n            </el-card>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"visible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { genFileId, UploadFile, UploadFiles, UploadProps, UploadRawFile } from 'element-plus';\nimport { importCommands, uploadCommands } from '@/api/modules/command';\nimport { getGroupList } from '@/api/modules/group';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst visible = ref(false);\nconst loading = ref();\nconst selects = ref<any>([]);\nconst data = ref([]);\n\nconst uploadRef = ref();\nconst uploaderFiles = ref();\nconst pageData = ref([]);\nconst paginationConfig = reactive({\n    currentPage: 1,\n    pageSize: 10,\n    total: 0,\n});\n\nconst currentGroup = ref();\nconst groupList = ref();\n\nconst acceptParams = (): void => {\n    visible.value = true;\n    selects.value = [];\n    loadGroups();\n    currentGroup.value = '';\n    data.value = [];\n    pageData.value = [];\n};\n\nconst search = () => {\n    const startIndex = (paginationConfig.currentPage - 1) * paginationConfig.pageSize;\n    const endIndex = startIndex + paginationConfig.pageSize;\n    pageData.value = data.value.slice(startIndex, endIndex);\n};\n\nconst loadGroups = async () => {\n    const res = await getGroupList('command');\n    groupList.value = res.data || [];\n};\nconst changeGroup = () => {\n    let itemGroup;\n    for (const g of groupList.value) {\n        if (g.id === currentGroup.value) {\n            itemGroup = g;\n            break;\n        }\n    }\n    for (const item of data.value) {\n        item.groupID = currentGroup.value;\n        item.groupBelong = itemGroup.name;\n    }\n};\n\nconst fileOnChange = async (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {\n    uploaderFiles.value = uploadFiles;\n    if (uploaderFiles.value.length !== 1) {\n        return;\n    }\n    const file = uploaderFiles.value[0];\n    const formData = new FormData();\n    formData.append('file', file.raw);\n    loading.value = true;\n    await uploadCommands(formData)\n        .then((res) => {\n            loading.value = false;\n            uploadRef.value!.clearFiles();\n            uploaderFiles.value = [];\n            data.value = res.data || [];\n            if (data.value.length === 0) {\n                MsgError(i18n.global.t('terminal.noSuchCommand'));\n            }\n            paginationConfig.total = data.value.length;\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n            uploadRef.value!.clearFiles();\n            uploaderFiles.value = [];\n        });\n};\n\nconst handleExceed: UploadProps['onExceed'] = (files) => {\n    uploadRef.value!.clearFiles();\n    const file = files[0] as UploadRawFile;\n    file.uid = genFileId();\n    uploadRef.value!.handleStart(file);\n};\n\nconst onImport = async () => {\n    loading.value = true;\n    importCommands(selects.value)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            loading.value = false;\n            emit('search');\n            visible.value = false;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/terminal/command/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('terminal.quickCommand')\">\n            <template #prompt>\n                <el-alert type=\"info\" :title=\"$t('terminal.quickCommandHelper')\" :closable=\"false\" />\n            </template>\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onOpenGroupDialog()\">\n                    {{ $t('commons.table.group') }}\n                </el-button>\n                <el-button type=\"primary\" plain :disabled=\"selects.length === 0\" @click=\"batchDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n\n                <el-button-group>\n                    <el-button @click=\"onImport\">\n                        {{ $t('commons.button.import') }}\n                    </el-button>\n                    <el-button @click=\"onExport\">\n                        {{ $t('commons.button.export') }}\n                    </el-button>\n                </el-button-group>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"group\" @change=\"search()\" clearable class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.group') }}</template>\n                    <div v-for=\"item in groupList\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"info\" />\n                <TableRefresh @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @sort-change=\"search\"\n                    @search=\"search\"\n                    :heightDiff=\"350\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        show-overflow-tooltip\n                        min-width=\"100\"\n                        prop=\"name\"\n                        fix\n                        sortable\n                    />\n                    <el-table-column\n                        :label=\"$t('terminal.command')\"\n                        min-width=\"300\"\n                        show-overflow-tooltip\n                        prop=\"command\"\n                        sortable\n                    />\n                    <el-table-column\n                        :label=\"$t('commons.table.group')\"\n                        show-overflow-tooltip\n                        min-width=\"100\"\n                        prop=\"groupBelong\"\n                        fix\n                    >\n                        <template #default=\"{ row }\">\n                            <fu-select-rw-switch v-model=\"row.groupID\" @change=\"updateGroup(row)\">\n                                <template #read>\n                                    {{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}\n                                </template>\n                                <div v-for=\"item in groupList\" :key=\"item.id\">\n                                    <el-option\n                                        v-if=\"item.name === 'Default'\"\n                                        :label=\"$t('commons.table.default')\"\n                                        :value=\"item.id\"\n                                    />\n                                    <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                                </div>\n                            </fu-select-rw-switch>\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations width=\"200px\" :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <ImportDialog @search=\"search\" ref=\"importDialogRef\" />\n        <GroupDialog @search=\"loadGroups\" ref=\"dialogGroupRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { Command } from '@/api/interface/command';\nimport GroupDialog from '@/components/group/index.vue';\nimport OperateDialog from '@/views/terminal/command/operate/index.vue';\nimport ImportDialog from '@/views/terminal/command/import/index.vue';\nimport { editCommand, deleteCommand, getCommandPage, exportCommands } from '@/api/modules/command';\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { getGroupList } from '@/api/modules/group';\nimport { downloadFile } from '@/utils/util';\n\nconst loading = ref();\nconst data = ref();\nconst selects = ref<any>([]);\nconst groupList = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'terminal-command-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('terminal-command-page-size')) || 20,\n    total: 0,\n    orderBy: 'name',\n    order: 'ascending',\n});\nconst info = ref();\nconst group = ref<string>('');\nconst dialogRef = ref();\nconst opRef = ref();\nconst importDialogRef = ref();\n\nconst acceptParams = () => {\n    search();\n    loadGroups();\n};\n\nconst loadGroups = async () => {\n    const res = await getGroupList('command');\n    groupList.value = res.data || [];\n};\n\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Command.CommandInfo> = {\n        type: 'command',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst dialogGroupRef = ref();\nconst onOpenGroupDialog = () => {\n    dialogGroupRef.value!.acceptParams({ type: 'command' });\n};\n\nconst updateGroup = async (row: any) => {\n    await editCommand(row);\n    search();\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst onExport = async () => {\n    loading.value = true;\n    await exportCommands()\n        .then((res) => {\n            if (res.data) {\n                loading.value = false;\n                downloadFile(res.data, 'local');\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onImport = () => {\n    importDialogRef.value.acceptParams();\n};\n\nconst batchDelete = async (row: Command.CommandInfo | null) => {\n    let names = [];\n    let ids = [];\n    if (row) {\n        ids = [row.id];\n        names = [row.name];\n    } else {\n        selects.value.forEach((item: Command.CommandInfo) => {\n            ids.push(item.id);\n            names.push(item.name);\n        });\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('terminal.quickCommand'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteCommand,\n        params: { ids: ids },\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        icon: 'Edit',\n        click: (row: any) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        icon: 'Delete',\n        click: batchDelete,\n    },\n];\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        groupID: Number(group.value),\n        info: info.value,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n        type: 'command',\n    };\n    loading.value = true;\n    await getCommandPage(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/terminal/command/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"title\"\n        :resource=\"dialogData.title === 'create' ? '' : dialogData.rowData?.name\"\n        @close=\"handleClose\"\n        size=\"small\"\n    >\n        <el-form\n            @submit.prevent\n            ref=\"formRef\"\n            label-width=\"100px\"\n            label-position=\"top\"\n            :model=\"dialogData.rowData\"\n            :rules=\"rules\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input clearable v-model=\"dialogData.rowData!.name\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.group')\" prop=\"groupID\">\n                <el-select filterable v-model=\"dialogData.rowData!.groupID\" clearable style=\"width: 100%\">\n                    <div v-for=\"item in groupList\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.command')\" prop=\"command\">\n                <el-input type=\"textarea\" clearable v-model=\"dialogData.rowData!.command\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { Command } from '@/api/interface/command';\nimport { addCommand, editCommand } from '@/api/modules/command';\nimport { getGroupList } from '@/api/modules/group';\n\nconst loading = ref();\nconst open = ref();\nconst groupList = ref();\n\nconst rules = reactive({\n    name: [Rules.requiredInput],\n    command: [Rules.requiredInput],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\ninterface DialogProps {\n    title: string;\n    rowData?: Command.CommandInfo;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title) + i18n.global.t('terminal.quickCommand');\n    loadGroups();\n    open.value = true;\n};\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        if (!valid) return;\n        if (dialogData.value.title === 'create') {\n            await addCommand(dialogData.value.rowData);\n        } else {\n            await editCommand(dialogData.value.rowData);\n        }\n        open.value = false;\n        emit('search');\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\n\nconst loadGroups = async () => {\n    const res = await getGroupList('command');\n    groupList.value = res.data;\n    if (dialogData.value.title === 'edit') {\n        return;\n    }\n    for (const group of groupList.value) {\n        if (group.isDefault) {\n            dialogData.value.rowData.groupID = group.id;\n            break;\n        }\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/terminal/host/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" :title=\"$t('terminal.host', 2)\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"onOpenDialog('create')\">\n                    {{ $t('terminal.addHost') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"onOpenGroupDialog()\">\n                    {{ $t('commons.table.group') }}\n                </el-button>\n                <el-button type=\"primary\" plain :disabled=\"selects.length === 0\" @click=\"onBatchDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <el-select v-model=\"group\" @change=\"search()\" clearable class=\"p-w-200 mr-5\">\n                    <template #prefix>{{ $t('commons.table.group') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"\"></el-option>\n                    <div v-for=\"item in groupList\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"info\" />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    :data=\"data\"\n                    @search=\"search\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column :label=\"$t('terminal.ip')\" prop=\"addr\" fix />\n                    <el-table-column :label=\"$t('commons.login.username')\" show-overflow-tooltip prop=\"user\" />\n                    <el-table-column :label=\"$t('commons.table.port')\" prop=\"port\" />\n                    <el-table-column\n                        :label=\"$t('commons.table.group')\"\n                        prop=\"description\"\n                        min-width=\"80\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <fu-select-rw-switch v-model=\"row.groupID\" @change=\"updateGroup(row)\">\n                                <template #read>\n                                    {{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}\n                                </template>\n                                <div v-for=\"item in groupList\" :key=\"item.id\">\n                                    <el-option\n                                        v-if=\"item.name === 'Default'\"\n                                        :label=\"$t('commons.table.default')\"\n                                        :value=\"item.id\"\n                                    />\n                                    <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                                </div>\n                            </fu-select-rw-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.title')\" show-overflow-tooltip prop=\"name\" />\n                    <el-table-column\n                        :label=\"$t('commons.table.description')\"\n                        show-overflow-tooltip\n                        prop=\"description\"\n                    />\n                    <fu-table-operations width=\"200px\" :buttons=\"buttons\" :label=\"$t('commons.table.operate')\" fix />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <GroupDialog @search=\"search\" ref=\"dialogGroupRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport GroupDialog from '@/components/agent-group/index.vue';\nimport OperateDialog from '@/views/terminal/host/operate/index.vue';\nimport { deleteHost, editHostGroup, searchHosts } from '@/api/modules/terminal';\nimport { getAgentGroupList } from '@/api/modules/group';\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { Host } from '@/api/interface/host';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst data = ref();\nconst groupList = ref();\nconst selects = ref<any>([]);\nconst paginationConfig = reactive({\n    cacheSizeKey: 'terminal-host-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('terminal-host-page-size')) || 20,\n    total: 0,\n});\nconst info = ref();\nconst group = ref<string>('');\n\nconst opRef = ref();\n\nconst acceptParams = () => {\n    search();\n};\n\nconst dialogRef = ref();\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Host.Host> = {\n        port: 22,\n        user: 'root',\n        authMode: 'password',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst dialogGroupRef = ref();\nconst onOpenGroupDialog = () => {\n    dialogGroupRef.value!.acceptParams({ type: 'host' });\n};\n\nconst onBatchDelete = async (row: Host.Host | null) => {\n    let names = [];\n    let ids = [];\n    if (row) {\n        names = [row.name + '[' + row.addr + ']'];\n        ids = [row.id];\n    } else {\n        selects.value.forEach((item: Host.Host) => {\n            names.push(item.name + '[' + item.addr + ']');\n            ids.push(item.id);\n        });\n    }\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('terminal.host'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteHost,\n        params: { ids: ids },\n    });\n};\n\nconst loadGroups = async () => {\n    const res = await getAgentGroupList('host');\n    groupList.value = res.data;\n};\n\nconst updateGroup = async (row: any) => {\n    await editHostGroup({ id: row.id, groupID: row.groupID });\n    search();\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: any) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Host.Host) => {\n            onBatchDelete(row);\n        },\n    },\n];\n\nconst search = async () => {\n    let params = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        groupID: Number(group.value),\n        info: info.value,\n    };\n    loadGroups();\n    loading.value = true;\n    await searchHosts(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/terminal/host/operate/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('terminal.host')\" @close=\"handleClose\" size=\"large\">\n        <el-form ref=\"hostInfoRef\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('terminal.ip')\" prop=\"addr\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"dialogData.rowData!.addr\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.username')\" prop=\"user\">\n                <el-input @change=\"isOK = false\" clearable v-model=\"dialogData.rowData!.user\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.authMode')\" prop=\"authMode\">\n                <el-radio-group @change=\"isOK = false\" v-model=\"dialogData.rowData!.authMode\">\n                    <el-radio value=\"password\">{{ $t('terminal.passwordMode') }}</el-radio>\n                    <el-radio value=\"key\">{{ $t('terminal.keyMode') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item\n                :label=\"$t('commons.login.password')\"\n                v-if=\"dialogData.rowData!.authMode === 'password'\"\n                prop=\"password\"\n            >\n                <el-input\n                    @change=\"isOK = false\"\n                    clearable\n                    show-password\n                    type=\"password\"\n                    v-model=\"dialogData.rowData!.password\"\n                />\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.key')\" v-if=\"dialogData.rowData!.authMode === 'key'\" prop=\"privateKey\">\n                <el-input @change=\"isOK = false\" clearable type=\"textarea\" v-model=\"dialogData.rowData!.privateKey\" />\n            </el-form-item>\n            <el-form-item\n                :label=\"$t('terminal.keyPassword')\"\n                v-if=\"dialogData.rowData!.authMode === 'key'\"\n                prop=\"passPhrase\"\n            >\n                <el-input\n                    @change=\"isOK = false\"\n                    type=\"password\"\n                    show-password\n                    clearable\n                    v-model=\"dialogData.rowData!.passPhrase\"\n                />\n            </el-form-item>\n            <el-checkbox clearable v-model.number=\"dialogData.rowData!.rememberPassword\">\n                {{ $t('terminal.rememberPassword') }}\n            </el-checkbox>\n            <el-form-item style=\"margin-top: 10px\" :label=\"$t('commons.table.port')\" prop=\"port\">\n                <el-input @change=\"isOK = false\" clearable v-model.number=\"dialogData.rowData!.port\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.group')\" prop=\"groupID\">\n                <el-select filterable v-model=\"dialogData.rowData!.groupID\" clearable style=\"width: 100%\">\n                    <div v-for=\"item in groupList\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.title')\" prop=\"name\">\n                <el-input clearable v-model=\"dialogData.rowData!.name\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable type=\"textarea\" v-model=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button @click=\"submitAddHost(hostInfoRef, 'testconn')\">\n                    {{ $t('terminal.testConn') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"!isOK\" @click=\"submitAddHost(hostInfoRef, dialogData.title)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, reactive } from 'vue';\nimport type { ElForm } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { addHost, editHost, testByInfo } from '@/api/modules/terminal';\nimport { getAgentGroupList } from '@/api/modules/group';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst isOK = ref(false);\ninterface DialogProps {\n    title: string;\n    rowData?: any;\n    getTableList?: () => Promise<any>;\n}\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\n\nconst groupList = ref();\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title);\n    drawerVisible.value = true;\n    loadGroups();\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst hostInfoRef = ref<FormInstance>();\nconst rules = reactive({\n    groupID: [Rules.requiredSelect],\n    addr: [Rules.ipV4V6OrDomain],\n    port: [Rules.requiredInput, Rules.port],\n    user: [Rules.requiredInput],\n    authMode: [Rules.requiredSelect],\n});\n\nconst loadGroups = async () => {\n    const res = await getAgentGroupList('host');\n    groupList.value = res.data;\n    if (dialogData.value.title === 'create') {\n        for (const item of groupList.value) {\n            if (item.isDefault) {\n                dialogData.value.rowData.groupID = item.id;\n                break;\n            }\n        }\n    }\n};\n\nconst submitAddHost = (formEl: FormInstance | undefined, ops: string) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        if (ops === 'create') {\n            loading.value = true;\n            await addHost(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n        if (ops === 'edit') {\n            loading.value = true;\n            await editHost(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        }\n        if (ops === 'testconn') {\n            loading.value = true;\n            await testByInfo(dialogData.value.rowData).then((res) => {\n                loading.value = false;\n                if (res.data) {\n                    isOK.value = true;\n                    MsgSuccess(i18n.global.t('terminal.connTestOk'));\n                } else {\n                    isOK.value = false;\n                    MsgError(i18n.global.t('terminal.connTestFailed'));\n                }\n            });\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/terminal/index.vue",
    "content": "<template>\n    <div>\n        <el-card class=\"router_card\">\n            <el-radio-group v-model=\"activeNames\" @change=\"handleChange\">\n                <el-radio-button class=\"router_card_button\" size=\"large\" value=\"terminal\">\n                    {{ $t('menu.terminal', 2) }}\n                </el-radio-button>\n                <el-radio-button class=\"router_card_button\" size=\"large\" value=\"host\">\n                    {{ $t('terminal.host', 2) }}\n                </el-radio-button>\n                <el-radio-button class=\"router_card_button\" size=\"large\" value=\"command\">\n                    {{ $t('terminal.quickCommand', 2) }}\n                </el-radio-button>\n                <el-radio-button class=\"router_card_button\" size=\"large\" value=\"setting\">\n                    {{ $t('container.setting') }}\n                </el-radio-button>\n            </el-radio-group>\n        </el-card>\n\n        <div v-show=\"activeNames === 'terminal'\">\n            <TerminalTab ref=\"terminalTabRef\" />\n        </div>\n        <div v-if=\"activeNames === 'host'\">\n            <HostTab ref=\"hostTabRef\" />\n        </div>\n        <div v-if=\"activeNames === 'command'\">\n            <CommandTab ref=\"commandTabRef\" />\n        </div>\n        <div v-if=\"activeNames === 'setting'\">\n            <SettingTab ref=\"settingTabRef\" />\n        </div>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport HostTab from '@/views/terminal/host/index.vue';\nimport CommandTab from '@/views/terminal/command/index.vue';\nimport TerminalTab from '@/views/terminal/terminal/index.vue';\nimport SettingTab from '@/views/terminal/setting/index.vue';\nimport { onMounted, onUnmounted, ref } from 'vue';\nimport { getTerminalInfo } from '@/api/modules/setting';\nimport { TerminalStore } from '@/store';\n\nconst terminalStore = TerminalStore();\nconst activeNames = ref<string>('terminal');\nconst hostTabRef = ref();\nconst commandTabRef = ref();\nconst terminalTabRef = ref();\nconst settingTabRef = ref();\n\nconst handleChange = (tab: any) => {\n    if (tab === 'host') {\n        hostTabRef.value!.acceptParams();\n    }\n    if (tab === 'command') {\n        commandTabRef.value!.acceptParams();\n    }\n    if (tab === 'terminal') {\n        terminalTabRef.value!.acceptParams();\n    }\n    if (tab === 'setting') {\n        settingTabRef.value!.acceptParams();\n    }\n};\n\nconst loadTerminalSetting = async () => {\n    await getTerminalInfo().then((res) => {\n        terminalStore.setLineHeight(Number(res.data.lineHeight));\n        terminalStore.setLetterSpacing(Number(res.data.letterSpacing));\n        terminalStore.setFontSize(Number(res.data.fontSize));\n        terminalStore.setFontFamily(res.data.fontFamily || \"Monaco, Menlo, Consolas, 'Courier New', monospace\");\n        terminalStore.setBackgroundColor(res.data.backgroundColor || '#000000');\n        terminalStore.setForegroundColor(res.data.foregroundColor || '#f5f5f5');\n        terminalStore.setCursorBlink(res.data.cursorBlink);\n        terminalStore.setCursorStyle(res.data.cursorStyle);\n        terminalStore.setScrollback(Number(res.data.scrollback));\n        terminalStore.setScrollSensitivity(Number(res.data.scrollSensitivity));\n    });\n};\n\nonMounted(() => {\n    loadTerminalSetting();\n    handleChange('terminal');\n});\nonUnmounted(() => {\n    terminalTabRef.value?.cleanTimer();\n});\n</script>\n\n<style lang=\"scss\">\n.router_card {\n    --el-card-padding: 0;\n}\n\n.router_card_button {\n    .el-radio-button__inner {\n        min-width: 100px;\n        height: 100%;\n        background-color: var(--panel-button-active) !important;\n        box-shadow: none !important;\n        border: 2px solid transparent !important;\n    }\n\n    .el-radio-button__original-radio:checked + .el-radio-button__inner {\n        color: $primary-color;\n        border-color: $primary-color !important;\n        border-radius: 4px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/terminal/setting/ai/helper.ts",
    "content": "export const DEFAULT_AI_PREFIX = '#';\n\nexport const DEFAULT_AI_RISK_COMMANDS = [\n    'rm -rf',\n    'mkfs',\n    'dd if=',\n    'curl | sh',\n    'wget | sh',\n    'chmod -R 777 /',\n    'shutdown',\n    'reboot',\n    'poweroff',\n    'init 0',\n    ':(){ :|:& };:',\n];\n\nexport const parseRiskCommands = (value: string): string[] => {\n    if (!value) {\n        return [...DEFAULT_AI_RISK_COMMANDS];\n    }\n    try {\n        const parsed = JSON.parse(value);\n        if (!Array.isArray(parsed)) {\n            return [...DEFAULT_AI_RISK_COMMANDS];\n        }\n        return parsed.map((item) => String(item).trim()).filter((item) => item.length > 0);\n    } catch {\n        return [...DEFAULT_AI_RISK_COMMANDS];\n    }\n};\n\nexport const normalizeRiskCommands = (riskCommands: string[]): string[] => {\n    const seen = new Set<string>();\n    const result: string[] = [];\n    for (const command of riskCommands) {\n        const normalized = command.trim();\n        if (!normalized || seen.has(normalized)) {\n            continue;\n        }\n        seen.add(normalized);\n        result.push(normalized);\n    }\n    return result.length > 0 ? result : [...DEFAULT_AI_RISK_COMMANDS];\n};\n"
  },
  {
    "path": "frontend/src/views/terminal/setting/ai/index.vue",
    "content": "<template>\n    <el-form-item :label=\"$t('terminal.aiSettings')\">\n        <el-input :value=\"aiSummary\" disabled>\n            <template #append>\n                <el-button @click=\"openDrawer\" icon=\"Setting\">\n                    {{ $t('commons.button.set') }}\n                </el-button>\n            </template>\n        </el-input>\n    </el-form-item>\n\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('terminal.aiSettings')\" size=\"60%\" @close=\"handleClose\">\n        <el-form ref=\"formRef\" :model=\"formModel\" label-position=\"top\">\n            <el-form-item :label=\"$t('terminal.aiStatus')\">\n                <el-switch v-model=\"formModel.status\" active-value=\"Enable\" inactive-value=\"Disable\" />\n            </el-form-item>\n            <el-form-item\n                :label=\"$t('aiTools.agents.account')\"\n                prop=\"accountId\"\n                :rules=\"accountRules\"\n                v-if=\"formModel.status === 'Enable'\"\n            >\n                <el-select class=\"formInput\" v-model=\"formModel.accountId\" clearable filterable>\n                    <el-option\n                        v-for=\"item in agentAccountOptions\"\n                        :key=\"item.id\"\n                        :label=\"item.name\"\n                        :value=\"String(item.id)\"\n                    >\n                        <div class=\"account-option\">\n                            <span class=\"account-option__name\">{{ item.name }}</span>\n                            <div class=\"account-option__tags\">\n                                <el-tag size=\"small\" effect=\"plain\">\n                                    {{ item.providerName || item.provider }}\n                                </el-tag>\n                                <el-tag size=\"small\" effect=\"plain\" :type=\"verificationTagType(item)\">\n                                    {{ verificationLabel(item) }}\n                                </el-tag>\n                            </div>\n                        </div>\n                    </el-option>\n                </el-select>\n                <span class=\"input-help\">{{ $t('terminal.aiAccountHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.aiPrefix')\" prop=\"prefix\" :rules=\"prefixRules\">\n                <el-input class=\"formInput\" v-model=\"formModel.prefix\" />\n                <span class=\"input-help\">{{ $t('terminal.aiPrefixHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.aiRiskCommands')\" prop=\"riskCommands\" :rules=\"riskCommandRules\">\n                <div class=\"risk-command-list\">\n                    <div class=\"risk-command-item\" v-for=\"(command, index) in formModel.riskCommands\" :key=\"index\">\n                        <el-input :model-value=\"command\" @update:model-value=\"updateRiskCommand(index, $event)\" />\n                        <el-button link type=\"danger\" @click=\"removeRiskCommand(index)\">\n                            {{ $t('terminal.aiRemoveRiskCommand') }}\n                        </el-button>\n                    </div>\n                    <div class=\"risk-command-actions\">\n                        <el-button plain @click=\"addRiskCommand\">{{ $t('terminal.aiAddRiskCommand') }}</el-button>\n                    </div>\n                </div>\n                <span class=\"input-help\">{{ $t('terminal.aiRiskCommandsHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"drawerVisible = false\" :disabled=\"saving\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button plain @click=\"resetRiskCommands\" :disabled=\"saving\">\n                {{ $t('commons.button.setDefault') }}\n            </el-button>\n            <el-button type=\"primary\" @click=\"handleConfirm\" :loading=\"saving\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, nextTick, reactive, ref, watch } from 'vue';\nimport type { ElForm } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { pageAgentAccounts } from '@/api/modules/ai';\nimport { updateAgentTerminalAIInfo } from '@/api/modules/setting';\nimport { MsgSuccess } from '@/utils/message';\nimport { DEFAULT_AI_PREFIX, DEFAULT_AI_RISK_COMMANDS, normalizeRiskCommands } from '@/views/terminal/setting/ai/helper';\n\ninterface AgentAccountOption {\n    id: number | string;\n    name: string;\n    provider?: string;\n    providerName?: string;\n    verified?: boolean;\n}\n\nconst props = defineProps<{\n    status: string;\n    accountId: string;\n    prefix: string;\n    riskCommands: string[];\n}>();\n\nconst emit = defineEmits<{\n    (e: 'refresh'): void;\n}>();\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\nconst drawerVisible = ref(false);\nconst saving = ref(false);\nconst agentAccountOptions = ref<AgentAccountOption[]>([]);\nconst formModel = reactive({\n    status: props.status,\n    accountId: props.accountId,\n    prefix: props.prefix,\n    riskCommands: [...props.riskCommands],\n});\n\nconst syncFormFromProps = () => {\n    formModel.status = props.status;\n    formModel.accountId = props.accountId;\n    formModel.prefix = props.prefix;\n    formModel.riskCommands = [...props.riskCommands];\n};\n\nconst openDrawer = () => {\n    syncFormFromProps();\n    loadAgentAccounts();\n    drawerVisible.value = true;\n};\n\nwatch(\n    () => [props.status, props.accountId, props.prefix, props.riskCommands],\n    () => {\n        if (drawerVisible.value) {\n            return;\n        }\n        syncFormFromProps();\n    },\n    { deep: true },\n);\n\nconst aiSummary = computed(() => {\n    if (props.status !== 'Enable') {\n        return i18n.global.t('setting.unSetting');\n    }\n    const prefix = String(props.prefix || '').trim();\n    return i18n.global.t('terminal.aiSummary', [prefix]);\n});\n\nconst isVerificationSkipped = (provider?: string) => {\n    const key = (provider || '').toLowerCase();\n    return key === 'custom' || key === 'vllm' || key === 'ollama' || key === 'kimi-coding';\n};\n\nconst verificationLabel = (item: AgentAccountOption) => {\n    if (isVerificationSkipped(item.provider)) {\n        return i18n.global.t('aiTools.agents.verifySkipped');\n    }\n    return item.verified ? 'OK' : 'N/A';\n};\n\nconst verificationTagType = (item: AgentAccountOption) => {\n    if (isVerificationSkipped(item.provider)) {\n        return 'info';\n    }\n    return item.verified ? 'success' : 'warning';\n};\n\nconst loadAgentAccounts = async () => {\n    await pageAgentAccounts({\n        page: 1,\n        pageSize: 1000,\n        provider: '',\n        name: '',\n    }).then((res) => {\n        agentAccountOptions.value = res.data?.items || [];\n    });\n};\n\nconst accountRules = [\n    {\n        ...Rules.requiredSelect,\n        validator: (_rule, value, callback) => {\n            if (formModel.status !== 'Enable') {\n                callback();\n                return;\n            }\n            if (!value) {\n                callback(new Error(i18n.global.t('commons.rule.requiredSelect')));\n                return;\n            }\n            callback();\n        },\n    },\n];\n\nconst prefixRules = [\n    Rules.requiredInput,\n    {\n        validator: (_rule, value, callback) => {\n            const normalized = String(value ?? '').trim();\n            if (!normalized) {\n                callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n                return;\n            }\n            if (!/^[!-~]+$/.test(normalized)) {\n                callback(new Error(i18n.global.t('terminal.aiPrefixAsciiVisible')));\n                return;\n            }\n            callback();\n        },\n        trigger: 'blur',\n    },\n];\n\nconst riskCommandRules = [\n    {\n        validator: (_rule, value, callback) => {\n            const commands = Array.isArray(value) ? value : [];\n            if (commands.some((item) => String(item ?? '').trim().length === 0)) {\n                callback(new Error(i18n.global.t('commons.rule.requiredInput')));\n                return;\n            }\n            callback();\n        },\n        trigger: 'blur',\n    },\n];\n\nconst addRiskCommand = () => {\n    formModel.riskCommands = [...formModel.riskCommands, ''];\n};\n\nconst updateRiskCommand = (index: number, value: string) => {\n    formModel.riskCommands = formModel.riskCommands.map((item, currentIndex) =>\n        currentIndex === index ? value : item,\n    );\n};\n\nconst removeRiskCommand = (index: number) => {\n    formModel.riskCommands = formModel.riskCommands.filter((_, currentIndex) => currentIndex !== index);\n};\n\nconst resetRiskCommands = () => {\n    formModel.prefix = DEFAULT_AI_PREFIX;\n    formModel.riskCommands = [...DEFAULT_AI_RISK_COMMANDS];\n};\n\nconst handleConfirm = async () => {\n    await nextTick();\n    if (!formRef.value) {\n        return;\n    }\n    try {\n        await formRef.value.validate();\n    } catch {\n        return;\n    }\n    saving.value = true;\n    try {\n        await updateAgentTerminalAIInfo({\n            aiStatus: formModel.status,\n            aiAccountId: formModel.status === 'Enable' ? formModel.accountId : '',\n            aiPrefix: formModel.prefix.trim() || DEFAULT_AI_PREFIX,\n            aiRiskCommands: JSON.stringify(normalizeRiskCommands(formModel.riskCommands)),\n        });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        drawerVisible.value = false;\n    } finally {\n        saving.value = false;\n    }\n};\n\nconst handleClose = () => {\n    syncFormFromProps();\n    emit('refresh');\n};\n</script>\n\n<style lang=\"css\" scoped>\n.formInput {\n    width: 100%;\n}\n\n.risk-command-list {\n    width: 100%;\n    display: flex;\n    flex-direction: column;\n    gap: 8px;\n}\n\n.risk-command-actions {\n    display: flex;\n    gap: 8px;\n}\n\n.risk-command-item {\n    display: flex;\n    gap: 8px;\n    align-items: center;\n}\n\n.account-option {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 12px;\n    width: 100%;\n}\n\n.account-option__name {\n    min-width: 0;\n    flex: 1;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n}\n\n.account-option__tags {\n    display: inline-flex;\n    align-items: center;\n    gap: 6px;\n    flex-shrink: 0;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/terminal/setting/default_conn/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('terminal.defaultConn')\" @close=\"handleClose\" size=\"large\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('terminal.ip')\" prop=\"addr\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"form.addr\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.username')\" prop=\"user\">\n                <el-input @change=\"isOK = false\" clearable v-model=\"form.user\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.authMode')\" prop=\"authMode\">\n                <el-radio-group @change=\"isOK = false\" v-model=\"form.authMode\">\n                    <el-radio value=\"password\">{{ $t('terminal.passwordMode') }}</el-radio>\n                    <el-radio value=\"key\">{{ $t('terminal.keyMode') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.password')\" v-if=\"form.authMode === 'password'\" prop=\"password\">\n                <el-input @change=\"isOK = false\" clearable show-password type=\"password\" v-model=\"form.password\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.key')\" v-if=\"form.authMode === 'key'\" prop=\"privateKey\">\n                <el-input @change=\"isOK = false\" clearable type=\"textarea\" v-model=\"form.privateKey\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.keyPassword')\" v-if=\"form.authMode === 'key'\" prop=\"passPhrase\">\n                <el-input @change=\"isOK = false\" type=\"password\" show-password clearable v-model=\"form.passPhrase\" />\n            </el-form-item>\n            <el-form-item style=\"margin-top: 10px\" :label=\"$t('commons.table.port')\" prop=\"port\">\n                <el-input @change=\"isOK = false\" clearable v-model.number=\"form.port\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button @click=\"onTest(formRef)\">\n                    {{ $t('terminal.testConn') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"!isOK\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, reactive } from 'vue';\nimport type { ElForm } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { addHost, loadLocalConn, testByInfo } from '@/api/modules/terminal';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Base64 } from 'js-base64';\n\nconst loading = ref();\nconst isOK = ref(false);\nconst drawerVisible = ref(false);\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\nconst form = reactive({\n    user: 'root',\n    addr: '127.0.0.1',\n    port: 22,\n    authMode: 'password',\n    password: '',\n    privateKey: '',\n    passPhrase: '',\n    isLocal: true,\n\n    id: 0,\n    name: '',\n    groupID: 0,\n    description: '',\n    rememberPassword: false,\n    localSSHConnShow: '',\n});\nconst rules = reactive({\n    addr: [Rules.ipV4V6OrDomain],\n    user: [Rules.requiredInput],\n    port: [Rules.requiredInput, Rules.port],\n    authMode: [Rules.requiredSelect],\n    password: [Rules.requiredInput],\n    privateKey: [Rules.requiredInput],\n});\n\nconst acceptParams = (): void => {\n    search();\n    drawerVisible.value = true;\n};\n\nconst search = async () => {\n    await loadLocalConn().then((res) => {\n        if (res.data) {\n            form.addr = res.data.addr || '127.0.0.1';\n            form.port = res.data.port || 22;\n            form.authMode = res.data.authMode || 'password';\n            form.password = Base64.decode(res.data.password);\n            form.privateKey = Base64.decode(res.data.privateKey);\n            form.passPhrase = Base64.decode(res.data.passPhrase);\n        }\n    });\n};\n\nconst handleClose = () => {\n    emit('search');\n    drawerVisible.value = false;\n};\n\nconst onSave = (formEl: FormInstance) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await addHost(form)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                drawerVisible.value = false;\n                emit('search');\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onTest = (formEl: FormInstance) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await testByInfo(form).then((res) => {\n            loading.value = false;\n            if (res.data) {\n                isOK.value = true;\n                MsgSuccess(i18n.global.t('terminal.connTestOk'));\n            } else {\n                isOK.value = false;\n                MsgError(i18n.global.t('terminal.connTestFailed'));\n            }\n        });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/terminal/setting/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent :title=\"$t('container.setting')\" :divider=\"true\">\n            <template #main>\n                <el-form :model=\"form\" label-position=\"left\" label-width=\"150px\">\n                    <el-row>\n                        <el-col :span=\"1\"><br /></el-col>\n                        <el-col :xs=\"24\" :sm=\"20\" :md=\"15\" :lg=\"12\" :xl=\"12\">\n                            <el-form-item :label=\"$t('terminal.lineHeight')\">\n                                <el-input-number\n                                    class=\"formInput\"\n                                    :min=\"1\"\n                                    :max=\"2.0\"\n                                    :precision=\"1\"\n                                    :step=\"0.1\"\n                                    v-model=\"form.lineHeight\"\n                                    @change=\"changeItem()\"\n                                />\n                            </el-form-item>\n                            <el-form-item :label=\"$t('terminal.letterSpacing')\">\n                                <el-input-number\n                                    class=\"formInput\"\n                                    :min=\"0\"\n                                    :max=\"3.5\"\n                                    :precision=\"1\"\n                                    :step=\"0.5\"\n                                    v-model=\"form.letterSpacing\"\n                                    @change=\"changeItem()\"\n                                />\n                            </el-form-item>\n                            <el-form-item :label=\"$t('terminal.fontSize')\">\n                                <el-input-number\n                                    class=\"formInput\"\n                                    :step=\"1\"\n                                    :min=\"12\"\n                                    :max=\"20\"\n                                    v-model=\"form.fontSize\"\n                                    @change=\"changeItem()\"\n                                />\n                            </el-form-item>\n                            <el-form-item :label=\"$t('terminal.fontFamily')\">\n                                <el-select\n                                    class=\"formInput\"\n                                    clearable\n                                    v-model=\"selectedFontFamilies\"\n                                    multiple\n                                    filterable\n                                    allow-create\n                                    default-first-option\n                                    :reserve-keyword=\"false\"\n                                >\n                                    <el-option\n                                        v-for=\"item in fontFamilyOptions\"\n                                        :key=\"item.value\"\n                                        :label=\"item.label\"\n                                        :value=\"item.value\"\n                                    />\n                                </el-select>\n                                <span class=\"input-help\">{{ $t('terminal.fontFamilySupportHelper') }}</span>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('terminal.backgroundColor')\">\n                                <el-color-picker v-model=\"form.backgroundColor\" @change=\"changeItem()\" />\n                            </el-form-item>\n                            <el-form-item :label=\"$t('terminal.foregroundColor')\">\n                                <el-color-picker v-model=\"form.foregroundColor\" @change=\"changeItem()\" />\n                            </el-form-item>\n\n                            <el-form-item>\n                                <div class=\"terminal\" ref=\"terminalElement\"></div>\n                            </el-form-item>\n\n                            <el-form-item :label=\"$t('terminal.cursorBlink')\">\n                                <el-switch\n                                    v-model=\"form.cursorBlink\"\n                                    active-value=\"Enable\"\n                                    inactive-value=\"Disable\"\n                                    @change=\"changeItem()\"\n                                />\n                            </el-form-item>\n                            <el-form-item :label=\"$t('terminal.cursorStyle')\">\n                                <el-select class=\"formInput\" v-model=\"form.cursorStyle\" @change=\"changeItem()\">\n                                    <el-option value=\"block\" :label=\"$t('terminal.cursorBlock')\" />\n                                    <el-option value=\"underline\" :label=\"$t('terminal.cursorUnderline')\" />\n                                    <el-option value=\"bar\" :label=\"$t('terminal.cursorBar')\" />\n                                </el-select>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('terminal.scrollback')\">\n                                <el-input-number\n                                    class=\"formInput\"\n                                    :step=\"50\"\n                                    :min=\"0\"\n                                    :max=\"10000\"\n                                    v-model=\"form.scrollback\"\n                                    @change=\"changeItem()\"\n                                />\n                            </el-form-item>\n                            <el-form-item :label=\"$t('terminal.scrollSensitivity')\">\n                                <el-input-number\n                                    class=\"formInput\"\n                                    :step=\"1\"\n                                    :min=\"0\"\n                                    :max=\"16\"\n                                    v-model=\"form.scrollSensitivity\"\n                                    @change=\"changeItem()\"\n                                />\n                            </el-form-item>\n                            <el-form-item>\n                                <el-button @click=\"onSetDefault()\" plain>\n                                    {{ $t('commons.button.setDefault') }}\n                                </el-button>\n                                <el-button @click=\"search(true)\" plain>{{ $t('commons.button.reset') }}</el-button>\n                                <el-button @click=\"onSave\" type=\"primary\">{{ $t('commons.button.save') }}</el-button>\n                            </el-form-item>\n\n                            <el-divider border-style=\"dashed\" />\n\n                            <AiSetting\n                                :status=\"aiForm.aiStatus\"\n                                :account-id=\"aiForm.aiAccountId\"\n                                :prefix=\"aiForm.aiPrefix\"\n                                :risk-commands=\"aiForm.aiRiskCommands\"\n                                @refresh=\"loadAISettings\"\n                            />\n\n                            <el-divider border-style=\"dashed\" />\n\n                            <el-form-item :label=\"$t('terminal.defaultConn')\">\n                                <el-switch v-model=\"form.showDefaultConn\" @change=\"changeShow\" />\n                            </el-form-item>\n                            <el-form-item :label=\"$t('xpack.node.connInfo')\">\n                                <el-input disabled v-model=\"form.defaultConn\">\n                                    <template #append>\n                                        <el-button @click=\"dialogRef.acceptParams(false)\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                </el-form>\n            </template>\n        </LayoutContent>\n        <OperateDialog @search=\"loadConnShow\" ref=\"dialogRef\" />\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @cancel=\"loadConnShow\" @submit=\"submitChangeShow\">\n            <template #content>\n                <el-form class=\"mt-4 mb-1\" ref=\"deleteForm\" v-if=\"!form.showDefaultConn\" label-position=\"left\">\n                    <el-form-item>\n                        <el-checkbox v-model=\"resetConn\" :label=\"$t('terminal.withReset')\" />\n                    </el-form-item>\n                </el-form>\n            </template>\n        </OpDialog>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, reactive, watch, onMounted, onBeforeUnmount } from 'vue';\nimport { getAgentTerminalAIInfo, getTerminalInfo, UpdateTerminalInfo } from '@/api/modules/setting';\nimport { Terminal } from '@xterm/xterm';\nimport OperateDialog from '@/views/terminal/setting/default_conn/index.vue';\nimport AiSetting from '@/views/terminal/setting/ai/index.vue';\nimport { DEFAULT_AI_PREFIX, parseRiskCommands } from '@/views/terminal/setting/ai/helper';\nimport '@xterm/xterm/css/xterm.css';\nimport { FitAddon } from '@xterm/addon-fit';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { TerminalStore } from '@/store';\nimport { loadLocalConn, updateLocalConn } from '@/api/modules/terminal';\n\nconst loading = ref(false);\nconst terminalStore = TerminalStore();\nconst dialogRef = ref();\n\nconst terminalElement = ref<HTMLDivElement | null>(null);\nconst fitAddon = new FitAddon();\nconst term = ref();\nconst previewResizeObserver = ref<ResizeObserver>();\nconst DEFAULT_FONT_FAMILY = \"Monaco, Menlo, Consolas, 'Courier New', monospace\";\nconst selectedFontFamilies = ref<string[]>([]);\nconst fontFamilyOptions = [\n    { label: 'Monaco', value: 'Monaco' },\n    { label: 'Menlo', value: 'Menlo' },\n    { label: 'Consolas', value: 'Consolas' },\n    { label: 'JetBrains Mono', value: \"'JetBrains Mono'\" },\n    { label: 'Fira Code', value: \"'Fira Code'\" },\n    { label: 'Cascadia Code', value: \"'Cascadia Code'\" },\n    { label: 'Source Code Pro', value: \"'Source Code Pro'\" },\n    { label: 'Ubuntu Mono', value: \"'Ubuntu Mono'\" },\n    { label: 'DejaVu Sans Mono', value: \"'DejaVu Sans Mono'\" },\n    { label: 'Courier New', value: \"'Courier New'\" },\n    { label: 'monospace', value: 'monospace' },\n];\n\nconst form = reactive({\n    lineHeight: 1.2,\n    letterSpacing: 1.2,\n    fontSize: 12,\n    fontFamily: DEFAULT_FONT_FAMILY,\n    backgroundColor: '#000000',\n    foregroundColor: '#f5f5f5',\n    cursorBlink: 'Enable',\n    cursorStyle: 'underline',\n    scrollback: 1000,\n    scrollSensitivity: 10,\n    showDefaultConn: false,\n    defaultConn: '',\n});\nconst aiForm = reactive({\n    aiStatus: 'Disable',\n    aiAccountId: '',\n    aiPrefix: '',\n    aiRiskCommands: [],\n});\n\nconst resetConn = ref(false);\nconst opRef = ref();\n\nconst splitFontFamily = (value: string): string[] => {\n    return value\n        .split(',')\n        .map((item) => item.trim())\n        .filter((item) => item.length > 0);\n};\n\nconst syncFontFamilyFromSelected = () => {\n    const values = selectedFontFamilies.value.map((item) => item.trim()).filter((item) => item.length > 0);\n    form.fontFamily = values.join(', ');\n};\n\nconst ensureFontFamily = () => {\n    if (selectedFontFamilies.value.length > 0) return;\n    selectedFontFamilies.value = splitFontFamily(DEFAULT_FONT_FAMILY);\n    form.fontFamily = DEFAULT_FONT_FAMILY;\n    if (term.value) {\n        changeItem();\n    }\n};\n\nwatch(\n    selectedFontFamilies,\n    () => {\n        syncFontFamilyFromSelected();\n        if (!term.value) return;\n        changeItem();\n    },\n    { deep: true },\n);\n\nconst acceptParams = () => {\n    search(true);\n    loadAISettings();\n    loadConnShow();\n    iniTerm();\n};\n\nonMounted(() => {\n    previewResizeObserver.value = new ResizeObserver(() => {\n        if (!term.value) return;\n        fitAddon.fit();\n    });\n    if (terminalElement.value) {\n        previewResizeObserver.value.observe(terminalElement.value);\n    }\n});\n\nonBeforeUnmount(() => {\n    previewResizeObserver.value?.disconnect();\n});\n\nconst search = async (withReset?: boolean) => {\n    loading.value = true;\n    await getTerminalInfo()\n        .then((res) => {\n            loading.value = false;\n            form.lineHeight = Number(res.data.lineHeight);\n            form.letterSpacing = Number(res.data.letterSpacing);\n            form.fontSize = Number(res.data.fontSize);\n            form.fontFamily = res.data.fontFamily || DEFAULT_FONT_FAMILY;\n            selectedFontFamilies.value = splitFontFamily(form.fontFamily);\n            form.backgroundColor = res.data.backgroundColor || '#000000';\n            form.foregroundColor = res.data.foregroundColor || '#f5f5f5';\n            form.cursorBlink = res.data.cursorBlink;\n            form.cursorStyle = res.data.cursorStyle;\n            form.scrollback = Number(res.data.scrollback);\n            form.scrollSensitivity = Number(res.data.scrollSensitivity);\n            terminalStore.setFontFamily(res.data.fontFamily || '');\n\n            if (withReset) {\n                changeItem();\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadAISettings = async () => {\n    loading.value = true;\n    await getAgentTerminalAIInfo()\n        .then((res) => {\n            aiForm.aiStatus = res.data.aiStatus || 'Disable';\n            aiForm.aiAccountId = res.data.aiAccountId || '';\n            aiForm.aiPrefix = res.data.aiPrefix || DEFAULT_AI_PREFIX;\n            aiForm.aiRiskCommands = parseRiskCommands(res.data.aiRiskCommands || '');\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst loadConnShow = async () => {\n    await loadLocalConn().then((res) => {\n        form.showDefaultConn = res.data.localSSHConnShow === 'Enable';\n        if (res.data.addr && res.data.port && res.data.user) {\n            form.defaultConn = res.data.user + '@' + res.data.addr + ':' + res.data.port;\n        } else {\n            form.defaultConn = '-';\n        }\n        resetConn.value = false;\n    });\n};\n\nconst changeShow = async () => {\n    let op = form.showDefaultConn ? i18n.global.t('xpack.waf.allow') : i18n.global.t('xpack.waf.deny');\n    opRef.value.acceptParams({\n        title: i18n.global.t('terminal.defaultConn'),\n        names: [],\n        msg: i18n.global.t('terminal.defaultConnHelper', [op]),\n        api: null,\n        params: {},\n    });\n};\nconst submitChangeShow = async () => {\n    loading.value = true;\n    await updateLocalConn({\n        withReset: resetConn.value,\n        defaultConn: form.showDefaultConn ? 'Enable' : 'Disable',\n    })\n        .then(() => {\n            loading.value = false;\n            loadConnShow();\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst iniTerm = () => {\n    const defaultFontFamily = \"Monaco, Menlo, Consolas, 'Courier New', monospace\";\n    const fontFamily = form.fontFamily || defaultFontFamily;\n\n    term.value = new Terminal({\n        lineHeight: 1.2,\n        fontSize: 12,\n        fontFamily: fontFamily,\n        theme: {\n            background: '#000000',\n            foreground: '#f5f5f5',\n        },\n        cursorBlink: true,\n        cursorStyle: 'block',\n        scrollback: 1000,\n        scrollSensitivity: 15,\n    });\n    term.value.open(terminalElement.value);\n    applyPreviewBackground();\n    term.value.loadAddon(fitAddon);\n    term.value.write('the first line \\r\\nthe second line');\n    fitAddon.fit();\n};\n\nconst applyPreviewBackground = () => {\n    if (!terminalElement.value) return;\n    terminalElement.value.style.backgroundColor = form.backgroundColor || '#000000';\n    terminalElement.value.style.backgroundImage = '';\n    terminalElement.value.style.backgroundSize = '';\n    terminalElement.value.style.backgroundPosition = '';\n    terminalElement.value.style.backgroundRepeat = '';\n    terminalElement.value.style.imageRendering = '';\n};\n\nconst changeItem = () => {\n    const defaultFontFamily = \"Monaco, Menlo, Consolas, 'Courier New', monospace\";\n    const fontFamily = form.fontFamily || defaultFontFamily;\n\n    term.value.options.lineHeight = form.lineHeight;\n    term.value.options.letterSpacing = form.letterSpacing;\n    term.value.options.fontSize = form.fontSize;\n    term.value.options.fontFamily = fontFamily;\n    term.value.options.theme = {\n        ...(term.value.options.theme || {}),\n        background: form.backgroundColor,\n        foreground: form.foregroundColor,\n    };\n    term.value.options.cursorBlink = form.cursorBlink === 'Enable';\n    term.value.options.cursorStyle = form.cursorStyle;\n    term.value.options.scrollback = form.scrollback;\n    term.value.options.scrollSensitivity = form.scrollSensitivity;\n    applyPreviewBackground();\n\n    fitAddon.fit();\n};\n\nconst onSetDefault = () => {\n    form.lineHeight = 1.2;\n    form.letterSpacing = 0;\n    form.fontSize = 12;\n    form.fontFamily = DEFAULT_FONT_FAMILY;\n    selectedFontFamilies.value = splitFontFamily(DEFAULT_FONT_FAMILY);\n    form.backgroundColor = '#000000';\n    form.foregroundColor = '#f5f5f5';\n    form.cursorBlink = 'Enable';\n    form.cursorStyle = 'block';\n    form.scrollback = 1000;\n    form.scrollSensitivity = 6;\n\n    changeItem();\n};\n\nconst onSave = () => {\n    ensureFontFamily();\n    ElMessageBox.confirm(i18n.global.t('terminal.saveHelper'), i18n.global.t('container.setting'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        try {\n            let param = {\n                lineHeight: form.lineHeight + '',\n                letterSpacing: form.letterSpacing + '',\n                fontSize: form.fontSize + '',\n                fontFamily: form.fontFamily,\n                backgroundColor: form.backgroundColor,\n                foregroundColor: form.foregroundColor,\n                cursorBlink: form.cursorBlink,\n                cursorStyle: form.cursorStyle,\n                scrollback: form.scrollback + '',\n                scrollSensitivity: form.scrollSensitivity + '',\n            };\n            await UpdateTerminalInfo(param);\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            terminalStore.setLineHeight(form.lineHeight);\n            terminalStore.setLetterSpacing(form.letterSpacing);\n            terminalStore.setFontSize(form.fontSize);\n            terminalStore.setFontFamily(form.fontFamily);\n            terminalStore.setBackgroundColor(form.backgroundColor);\n            terminalStore.setForegroundColor(form.foregroundColor);\n            terminalStore.setCursorBlink(form.cursorBlink);\n            terminalStore.setCursorStyle(form.cursorStyle);\n            terminalStore.setScrollback(form.scrollback);\n            terminalStore.setScrollSensitivity(form.scrollSensitivity);\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"css\" scoped>\n.formInput {\n    width: 100%;\n}\n.terminal {\n    width: 100%;\n    height: 100px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/terminal/terminal/host-create.vue",
    "content": "<template>\n    <DrawerPro v-model=\"dialogVisible\" :header=\"$t('terminal.addHost')\" @close=\"handleClose\" size=\"large\">\n        <el-form ref=\"hostRef\" label-width=\"100px\" label-position=\"top\" :model=\"form\" :rules=\"rules\">\n            <el-alert\n                v-if=\"form.isLocal\"\n                class=\"common-prompt\"\n                center\n                :title=\"$t('terminal.connLocalErr')\"\n                :closable=\"false\"\n                type=\"warning\"\n            />\n            <el-form-item :label=\"$t('terminal.ip')\" prop=\"addr\">\n                <el-input @change=\"isOK = false\" clearable v-model.trim=\"form.addr\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.username')\" prop=\"user\">\n                <el-input @change=\"isOK = false\" clearable v-model=\"form.user\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.authMode')\" prop=\"authMode\">\n                <el-radio-group @change=\"isOK = false\" v-model=\"form.authMode\">\n                    <el-radio value=\"password\">{{ $t('terminal.passwordMode') }}</el-radio>\n                    <el-radio value=\"key\">{{ $t('terminal.keyMode') }}</el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.password')\" v-if=\"form.authMode === 'password'\" prop=\"password\">\n                <el-input @change=\"isOK = false\" clearable show-password type=\"password\" v-model=\"form.password\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.key')\" v-if=\"form.authMode === 'key'\" prop=\"privateKey\">\n                <el-input @change=\"isOK = false\" clearable type=\"textarea\" v-model=\"form.privateKey\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('terminal.keyPassword')\" v-if=\"form.authMode === 'key'\" prop=\"passPhrase\">\n                <el-input @change=\"isOK = false\" type=\"password\" show-password clearable v-model=\"form.passPhrase\" />\n            </el-form-item>\n            <el-checkbox clearable v-model.number=\"form.rememberPassword\">\n                {{ $t('terminal.rememberPassword') }}\n            </el-checkbox>\n            <el-form-item class=\"mt-2.5\" :label=\"$t('commons.table.port')\" prop=\"port\">\n                <el-input @change=\"isOK = false\" clearable v-model.number=\"form.port\" />\n            </el-form-item>\n            <el-form-item v-if=\"!form.isLocal\" :label=\"$t('commons.table.group')\" prop=\"groupID\">\n                <el-select filterable v-model=\"form.groupID\" clearable style=\"width: 100%\">\n                    <div v-for=\"item in groupList\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n            </el-form-item>\n            <el-form-item v-if=\"!form.isLocal\" :label=\"$t('commons.table.title')\" prop=\"name\">\n                <el-input clearable v-model=\"form.name\" />\n            </el-form-item>\n            <el-form-item v-if=\"!form.isLocal\" :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input clearable v-model=\"form.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"dialogVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button @click=\"submitAddHost(hostRef, 'testConn')\">\n                    {{ $t('terminal.testConn') }}\n                </el-button>\n                <el-button type=\"primary\" :disabled=\"!isOK\" @click=\"submitAddHost(hostRef, 'saveAndConn')\">\n                    {{ $t('terminal.saveAndConn') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script setup lang=\"ts\">\nimport { ElForm } from 'element-plus';\nimport { Host } from '@/api/interface/host';\nimport { Rules } from '@/global/form-rules';\nimport { addHost, editHost, testByInfo } from '@/api/modules/terminal';\nimport i18n from '@/lang';\nimport { reactive, ref } from 'vue';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { getAgentGroupList } from '@/api/modules/group';\n\nconst dialogVisible = ref();\nconst isOK = ref(false);\ntype FormInstance = InstanceType<typeof ElForm>;\nconst hostRef = ref<FormInstance>();\n\nconst groupList = ref();\nconst defaultGroup = ref();\n\nlet form = reactive<Host.HostOperate>({\n    id: 0,\n    name: '',\n    groupID: 0,\n    addr: '',\n    port: 22,\n    user: '',\n    authMode: 'password',\n    password: '',\n    privateKey: '',\n    passPhrase: '',\n    rememberPassword: false,\n    description: '',\n    isLocal: false,\n});\n\nconst rules = reactive({\n    addr: [Rules.ipV4V6OrDomain],\n    port: [Rules.requiredInput, Rules.port],\n    user: [Rules.requiredInput],\n    authMode: [Rules.requiredSelect],\n    password: [Rules.requiredInput],\n    privateKey: [Rules.requiredInput],\n});\n\ninterface DialogProps {\n    isLocal: boolean;\n}\nconst acceptParams = (props: DialogProps) => {\n    form.isLocal = props.isLocal;\n    loadGroups();\n    dialogVisible.value = true;\n};\n\nconst handleClose = () => {\n    dialogVisible.value = false;\n};\n\nconst emit = defineEmits(['on-conn-terminal', 'on-new-local', 'load-host-tree']);\n\nconst loadGroups = async () => {\n    const res = await getAgentGroupList('host');\n    groupList.value = res.data;\n    for (const item of groupList.value) {\n        if (item.isDefault) {\n            defaultGroup.value = item.id;\n            break;\n        }\n    }\n    if (form.isLocal) {\n        loadLocal();\n    } else {\n        setDefault();\n    }\n};\nconst loadLocal = async () => {\n    form.id = 0;\n    form.addr = '127.0.0.1';\n    form.port = 22;\n    form.user = 'root';\n    form.authMode = 'password';\n    form.password = '';\n    form.privateKey = '';\n};\n\nconst setDefault = () => {\n    form.addr = '';\n    form.name = '';\n    form.groupID = defaultGroup.value;\n    form.port = 22;\n    form.user = '';\n    form.authMode = 'password';\n    form.password = '';\n    form.privateKey = '';\n    form.description = '';\n};\n\nconst submitAddHost = (formEl: FormInstance | undefined, ops: string) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        switch (ops) {\n            case 'testConn':\n                await testByInfo(form).then((res) => {\n                    if (res.data) {\n                        isOK.value = true;\n                        MsgSuccess(i18n.global.t('terminal.connTestOk'));\n                    } else {\n                        isOK.value = false;\n                        MsgError(i18n.global.t('terminal.connTestFailed'));\n                    }\n                });\n                break;\n            case 'saveAndConn':\n                let res;\n                if (form.id == 0) {\n                    res = await addHost(form);\n                } else {\n                    res = await editHost(form);\n                }\n                dialogVisible.value = false;\n                if (form.isLocal) {\n                    emit('on-new-local');\n                    emit('load-host-tree');\n                    return;\n                }\n                let title = res.data.user + '@' + res.data.addr + ':' + res.data.port;\n                if (res.data.name.length !== 0) {\n                    title = res.data.name + '-' + title;\n                }\n                emit('on-conn-terminal', title, res.data.id);\n                emit('load-host-tree');\n                break;\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/terminal/terminal/index.vue",
    "content": "<template>\n    <div>\n        <el-tabs\n            type=\"card\"\n            class=\"terminal-tabs card-interval\"\n            style=\"background-color: var(--panel-terminal-tag-bg-color)\"\n            v-model=\"terminalValue\"\n            :before-leave=\"beforeLeave\"\n            @tab-change=\"quickCmd = ''\"\n            @edit=\"handleTabsRemove\"\n        >\n            <el-tab-pane\n                :key=\"item.index\"\n                v-for=\"item in terminalTabs\"\n                :closable=\"true\"\n                :label=\"item.title\"\n                :name=\"item.index\"\n            >\n                <template #label>\n                    <span class=\"custom-tabs-label\">\n                        <span\n                            v-if=\"item.status === 'online'\"\n                            :style=\"`color: ${\n                                item.latency < 100 ? '#69db7c' : item.latency < 300 ? '#f59f00' : '#d9480f'\n                            }; display: inline-flex; align-items: center`\"\n                        >\n                            <span>&nbsp;{{ item.latency }}&nbsp;ms&nbsp;</span>\n                            <el-icon>\n                                <circleCheck />\n                            </el-icon>\n                        </span>\n                        <el-button\n                            v-if=\"item.status === 'closed'\"\n                            icon=\"Refresh\"\n                            class=\"text-white\"\n                            size=\"default\"\n                            link\n                            @click=\"onReconnect(item)\"\n                        />\n                        <span v-if=\"item.title.length <= 20\">&nbsp;{{ item.title }}&nbsp;</span>\n                        <el-tooltip v-else :content=\"item.title\" placement=\"top-start\">\n                            <span>&nbsp;{{ item.title.substring(0, 17) }}...&nbsp;</span>\n                        </el-tooltip>\n                    </span>\n                </template>\n                <Terminal\n                    :style=\"{\n                        height: cmdPanelVisible\n                            ? `calc(100vh - ${loadHeightWithPanel()})`\n                            : `calc(100vh - ${loadHeight()})`,\n                        'background-color': `var(--panel-logs-bg-color)`,\n                    }\"\n                    :ref=\"'t-' + item.index\"\n                    :key=\"item.Refresh\"\n                ></Terminal>\n\n                <transition name=\"el-fade-in\">\n                    <div\n                        v-show=\"cmdPanelVisible\"\n                        class=\"mb-2 border-b border-[var(--el-border-color)] pb-2 w-full bg-[var(--el-bg-color)]\"\n                    >\n                        <el-tabs v-model=\"activeGroupTab\" type=\"card\" class=\"command-tabs\">\n                            <el-tab-pane\n                                v-for=\"group in commandTree\"\n                                :key=\"group.value\"\n                                :label=\"''\"\n                                :name=\"group.value\"\n                            >\n                                <template #label>\n                                    <span class=\"group-tab-label\">\n                                        <span v-if=\"group.label.length <= 6\">{{ group.label }}</span>\n                                        <el-tooltip v-else :content=\"group.label\" placement=\"top\">\n                                            <span>{{ group.label.substring(0, 6) }}...</span>\n                                        </el-tooltip>\n                                    </span>\n                                </template>\n                                <div class=\"grid grid-cols-[repeat(auto-fill,minmax(160px,1fr))] gap-2 p-0\">\n                                    <el-tag\n                                        v-for=\"cmd in group.children\"\n                                        :key=\"cmd.value\"\n                                        class=\"command-tag\"\n                                        @click=\"executeCommand(cmd.value)\"\n                                        type=\"info\"\n                                        effect=\"plain\"\n                                    >\n                                        <div class=\"flex items-center justify-between w-full gap-1.5\">\n                                            <span class=\"command-tag-name\" :title=\"cmd.label\">\n                                                {{\n                                                    cmd.label.length > 8 ? cmd.label.substring(0, 8) + '...' : cmd.label\n                                                }}\n                                            </span>\n                                            <el-popover placement=\"top\" :width=\"320\" trigger=\"hover\">\n                                                <template #reference>\n                                                    <el-icon class=\"command-preview-icon\">\n                                                        <InfoFilled />\n                                                    </el-icon>\n                                                </template>\n                                                <div class=\"command-preview\">\n                                                    <div class=\"command-preview-name\">\n                                                        <strong>{{ cmd.label }}</strong>\n                                                    </div>\n                                                    <div class=\"command-preview-value\">{{ cmd.value }}</div>\n                                                </div>\n                                            </el-popover>\n                                        </div>\n                                    </el-tag>\n                                </div>\n                            </el-tab-pane>\n                        </el-tabs>\n                    </div>\n                </transition>\n\n                <div class=\"flex items-center gap-3 w-full py-2 flex-wrap\">\n                    <el-button\n                        @click=\"cmdPanelVisible = !cmdPanelVisible\"\n                        type=\"primary\"\n                        class=\"min-w-[120px] max-w-[150px] shrink-0\"\n                    >\n                        {{ $t('terminal.quickCommand') }}\n                        <el-icon class=\"ml-1\">\n                            <component :is=\"cmdPanelVisible ? 'ArrowUp' : 'ArrowDown'\" />\n                        </el-icon>\n                    </el-button>\n                    <el-input\n                        v-model=\"batchVal\"\n                        @keydown.enter.exact.prevent=\"batchInput\"\n                        type=\"textarea\"\n                        :autosize=\"{ minRows: 1, maxRows: 3 }\"\n                        class=\"flex-1 basis-[300px] min-w-[200px]\"\n                        placeholder=\">\"\n                    ></el-input>\n                    <el-checkbox\n                        :label=\"$t('terminal.batchInput')\"\n                        v-model=\"isBatch\"\n                        class=\"shrink-0 whitespace-nowrap\"\n                    />\n                </div>\n            </el-tab-pane>\n            <el-tab-pane :closable=\"false\" name=\"newTabs\">\n                <template #label>\n                    <el-button v-popover=\"popoverRef\" class=\"tagButton\" icon=\"Plus\"></el-button>\n                    <el-popover\n                        ref=\"popoverRef\"\n                        width=\"320px\"\n                        trigger=\"hover\"\n                        virtual-triggering\n                        persistent\n                        :offset=\"-4\"\n                    >\n                        <div class=\"p-2 space-y-2\">\n                            <div class=\"flex gap-2\">\n                                <button\n                                    @click=\"onNewSsh\"\n                                    class=\"flex-1 flex flex-col items-center justify-center px-3 py-2.5 bg-[var(--el-fill-color-light)] hover:bg-[var(--panel-main-bg-color-9)] rounded transition-colors duration-200 cursor-pointer group border-0 outline-none\"\n                                >\n                                    <el-icon\n                                        class=\"text-xl mb-1 text-[var(--el-text-color-primary)] group-hover:text-[var(--el-color-primary)] transition-colors\"\n                                    >\n                                        <Plus />\n                                    </el-icon>\n                                    <span\n                                        class=\"text-xs text-[var(--el-text-color-primary)] group-hover:text-[var(--el-color-primary)] font-medium truncate w-full text-center transition-colors\"\n                                    >\n                                        {{ $t('terminal.createConn') }}\n                                    </span>\n                                </button>\n                                <button\n                                    @click=\"onNewLocal\"\n                                    class=\"flex-1 flex flex-col items-center justify-center px-3 py-2.5 bg-[var(--el-fill-color-light)] hover:bg-[var(--panel-main-bg-color-9)] rounded transition-colors duration-200 cursor-pointer group border-0 outline-none\"\n                                >\n                                    <el-icon\n                                        class=\"text-xl mb-1 text-[var(--el-text-color-primary)] group-hover:text-[var(--el-color-primary)] transition-colors\"\n                                    >\n                                        <House />\n                                    </el-icon>\n                                    <span\n                                        class=\"text-xs text-[var(--el-text-color-primary)] group-hover:text-[var(--el-color-primary)] font-medium truncate w-full text-center transition-colors\"\n                                    >\n                                        {{ $t('terminal.localhost') }}\n                                    </span>\n                                </button>\n                            </div>\n\n                            <el-divider class=\"my-0\" />\n\n                            <div class=\"search-container px-1 py-1 bg-[var(--el-fill-color-light)] rounded\">\n                                <el-input\n                                    v-model=\"hostFilterInfo\"\n                                    class=\"w-full\"\n                                    clearable\n                                    suffix-icon=\"Search\"\n                                    :placeholder=\"$t('commons.button.search')\"\n                                    size=\"small\"\n                                >\n                                    <template #prefix>\n                                        <el-icon class=\"el-input__icon\"><Search /></el-icon>\n                                    </template>\n                                </el-input>\n                            </div>\n                            <el-tree\n                                ref=\"treeRef\"\n                                :expand-on-click-node=\"false\"\n                                node-key=\"id\"\n                                :default-expand-all=\"true\"\n                                :data=\"hostTree\"\n                                :props=\"defaultProps\"\n                                :filter-node-method=\"filterHost\"\n                                :empty-text=\"$t('terminal.noHost')\"\n                                class=\"host-tree\"\n                            >\n                                <template #default=\"{ node, data }\">\n                                    <span class=\"custom-tree-node w-full\">\n                                        <span\n                                            v-if=\"node.label === 'Default'\"\n                                            class=\"text-xs font-medium text-[var(--el-text-color-primary)]\"\n                                        >\n                                            {{ $t('commons.table.default') }}\n                                        </span>\n                                        <div v-else class=\"w-full min-w-0\">\n                                            <span v-if=\"node.label.length <= 22\">\n                                                <a\n                                                    @click=\"onClickConn(node, data)\"\n                                                    class=\"text-xs text-[var(--el-text-color-primary)] hover:text-[var(--el-color-primary)] transition-colors cursor-pointer block truncate\"\n                                                >\n                                                    {{ node.label }}\n                                                </a>\n                                            </span>\n                                            <el-tooltip v-else :content=\"node.label\" placement=\"right\">\n                                                <span>\n                                                    <a\n                                                        @click=\"onClickConn(node, data)\"\n                                                        class=\"text-xs text-[var(--el-text-color-primary)] hover:text-[var(--el-color-primary)] transition-colors cursor-pointer block truncate\"\n                                                    >\n                                                        {{ node.label.substring(0, 30) }}...\n                                                    </a>\n                                                </span>\n                                            </el-tooltip>\n                                        </div>\n                                    </span>\n                                </template>\n                            </el-tree>\n                        </div>\n                    </el-popover>\n                </template>\n            </el-tab-pane>\n            <div v-if=\"terminalTabs.length === 0\">\n                <el-empty\n                    :style=\"{ height: `calc(100vh - ${loadEmptyHeight()})`, 'background-color': '#000' }\"\n                    :description=\"$t('terminal.emptyTerminal')\"\n                ></el-empty>\n            </div>\n        </el-tabs>\n        <el-tooltip :content=\"loadTooltip()\" placement=\"top\">\n            <el-button\n                @click=\"toggleFullscreen\"\n                v-if=\"!mobile\"\n                class=\"bg-transparent border-0 absolute right-[50px] font-semibold text-sm\"\n                :style=\"{ top: loadFullScreenHeight() }\"\n                icon=\"FullScreen\"\n            ></el-button>\n        </el-tooltip>\n\n        <HostDialog\n            ref=\"dialogRef\"\n            @on-conn-terminal=\"onConnTerminal\"\n            @on-new-local=\"onNewLocal\"\n            @load-host-tree=\"loadHostTree\"\n        />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, getCurrentInstance, watch, nextTick, computed, onMounted } from 'vue';\nimport Terminal from '@/components/terminal/index.vue';\nimport HostDialog from '@/views/terminal/terminal/host-create.vue';\nimport type Node from 'element-plus/es/components/tree/src/model/node';\nimport { ElTree } from 'element-plus';\nimport screenfull from 'screenfull';\nimport i18n from '@/lang';\nimport { Host } from '@/api/interface/host';\nimport { getHostTree, testByID, testLocalConn } from '@/api/modules/terminal';\nimport { GlobalStore } from '@/store';\nimport router from '@/routers';\nimport { getCommandTree } from '@/api/modules/command';\nimport { getAgentSettingByKey } from '@/api/modules/setting';\n\nconst dialogRef = ref();\nconst ctx = getCurrentInstance() as any;\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst toggleFullscreen = () => {\n    if (screenfull.isEnabled) {\n        screenfull.toggle();\n    }\n};\nconst loadTooltip = () => {\n    return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen'));\n};\n\nlet timer: ReturnType<typeof setInterval> | null = null;\nconst terminalValue = ref();\nconst terminalTabs = ref([]) as any;\nlet tabIndex = 0;\n\nconst commandTree = ref();\nconst cmdPanelVisible = ref(false);\nconst activeGroupTab = ref('');\nlet quickCmd = ref();\nlet batchVal = ref();\nlet isBatch = ref<boolean>(false);\n\nconst popoverRef = ref();\n\nconst hostFilterInfo = ref('');\nconst hostTree = ref<Array<Host.HostTree>>();\nconst treeRef = ref<InstanceType<typeof ElTree>>();\nconst defaultProps = {\n    label: 'label',\n    children: 'children',\n};\ninterface Tree {\n    id: number;\n    label: string;\n    children?: Tree[];\n}\nconst initCmd = ref('');\n\nconst acceptParams = async () => {\n    globalStore.isFullScreen = false;\n    loadCommandTree();\n    loadHostTree();\n    if (terminalTabs.value.length === 0) {\n        await getAgentSettingByKey('LocalSSHConnShow').then((res) => {\n            if (res.data === 'Enable') {\n                onNewLocal();\n            }\n        });\n    }\n    timer = setInterval(() => {\n        syncTerminal();\n    }, 1000 * 5);\n    if (!mobile.value) {\n        screenfull.on('change', () => {\n            globalStore.isFullScreen = screenfull.isFullscreen;\n        });\n    }\n};\n\nconst cleanTimer = () => {\n    clearInterval(Number(timer));\n    timer = null;\n    for (const terminal of terminalTabs.value) {\n        if (ctx && ctx.refs[`t-${terminal.index}`][0]) {\n            terminal.status = ctx.refs[`t-${terminal.index}`][0].onClose();\n        }\n    }\n};\n\nconst loadHeight = () => {\n    return globalStore.openMenuTabs ? '250px' : '210px';\n};\nconst loadHeightWithPanel = () => {\n    return globalStore.openMenuTabs ? '490px' : '450px';\n};\nconst loadEmptyHeight = () => {\n    return globalStore.openMenuTabs ? '201px' : '156px';\n};\nconst loadFullScreenHeight = () => {\n    return globalStore.openMenuTabs ? '105px' : '60px';\n};\n\nconst handleTabsRemove = (targetName: string, action: 'remove' | 'add') => {\n    if (action !== 'remove') {\n        return;\n    }\n    if (ctx) {\n        ctx.refs[`t-${targetName}`] && ctx.refs[`t-${targetName}`][0].onClose();\n    }\n    const tabs = terminalTabs.value;\n    let activeName = terminalValue.value;\n    if (activeName === targetName) {\n        tabs.forEach((tab: any, index: any) => {\n            if (tab.index === targetName) {\n                const nextTab = tabs[index + 1] || tabs[index - 1];\n                if (nextTab) {\n                    activeName = nextTab.index;\n                }\n            }\n        });\n    }\n    terminalValue.value = activeName;\n    terminalTabs.value = tabs.filter((tab: any) => tab.index !== targetName);\n};\n\nconst loadHostTree = async () => {\n    const res = await getHostTree({});\n    hostTree.value = res.data;\n};\nwatch(hostFilterInfo, (val: any) => {\n    treeRef.value!.filter(val);\n});\nconst filterHost = (value: string, data: any) => {\n    if (!value) return true;\n    return data.label.includes(value);\n};\nconst loadCommandTree = async () => {\n    const res = await getCommandTree('command');\n    commandTree.value = res.data || [];\n    for (const item of commandTree.value) {\n        if (item.label === 'Default') {\n            item.label = i18n.global.t('commons.table.default');\n        }\n    }\n    if (commandTree.value.length > 0) {\n        activeGroupTab.value = commandTree.value[0].value;\n    }\n};\n\nconst executeCommand = (command: string) => {\n    if (!ctx) {\n        return;\n    }\n    if (isBatch.value) {\n        for (const tab of terminalTabs.value) {\n            ctx.refs[`t-${tab.index}`] && ctx.refs[`t-${tab.index}`][0].sendMsg(command + '\\n');\n        }\n    } else {\n        ctx.refs[`t-${terminalValue.value}`] && ctx.refs[`t-${terminalValue.value}`][0].sendMsg(command + '\\n');\n    }\n};\n\nfunction batchInput() {\n    if (batchVal.value === '' || !ctx) {\n        return;\n    }\n    if (isBatch.value) {\n        for (const tab of terminalTabs.value) {\n            ctx.refs[`t-${tab.index}`] && ctx.refs[`t-${tab.index}`][0].sendMsg(batchVal.value + '\\n');\n        }\n        batchVal.value = '';\n        return;\n    }\n    ctx.refs[`t-${terminalValue.value}`] && ctx.refs[`t-${terminalValue.value}`][0].sendMsg(batchVal.value + '\\n');\n    batchVal.value = '';\n}\n\nfunction beforeLeave(activeName: string) {\n    if (activeName === 'newTabs') {\n        return false;\n    }\n}\n\nconst onNewSsh = () => {\n    dialogRef.value!.acceptParams({ isLocal: false });\n};\nconst onNewLocal = async () => {\n    const res = await testLocalConn();\n    if (!res.data) {\n        dialogRef.value!.acceptParams({ isLocal: true });\n        return;\n    }\n    terminalTabs.value.push({\n        index: tabIndex,\n        title: i18n.global.t('terminal.localhost'),\n        wsID: 0,\n        status: 'online',\n        latency: 0,\n    });\n    terminalValue.value = tabIndex;\n    nextTick(() => {\n        ctx.refs[`t-${terminalValue.value}`] &&\n            ctx.refs[`t-${terminalValue.value}`][0].acceptParams({\n                endpoint: '/api/v2/hosts/terminal',\n                initCmd: initCmd.value,\n                error: '',\n            });\n        initCmd.value = '';\n    });\n    tabIndex++;\n};\n\nconst onClickConn = (node: Node, data: Tree) => {\n    if (node.level === 1) {\n        return;\n    }\n    onConnTerminal(node.label, data.id);\n};\n\nconst onReconnect = async (item: any) => {\n    if (ctx) {\n        ctx.refs[`t-${item.index}`] && ctx.refs[`t-${item.index}`][0].onClose();\n    }\n    item.Refresh = !item.Refresh;\n    if (item.wsID === 0) {\n        const res = await testLocalConn();\n        nextTick(() => {\n            ctx.refs[`t-${item.index}`] &&\n                ctx.refs[`t-${item.index}`][0].acceptParams({\n                    endpoint: '/api/v2/hosts/terminal',\n                    initCmd: initCmd.value,\n                    error: res.data ? '' : 'Failed to set up the connection. Please check the host information',\n                });\n            initCmd.value = '';\n        });\n        syncTerminal();\n        return;\n    }\n\n    const res = await testByID(item.wsID);\n    nextTick(() => {\n        ctx.refs[`t-${item.index}`] &&\n            ctx.refs[`t-${item.index}`][0].acceptParams({\n                endpoint: '/api/v2/hosts/terminal',\n                args: `id=${item.wsID}`,\n                initCmd: initCmd.value,\n                error: res.data ? '' : 'Failed to set up the connection. Please check the host information',\n            });\n        initCmd.value = '';\n    });\n    syncTerminal();\n};\n\nconst onConnTerminal = async (title: string, wsID: number) => {\n    const res = await testByID(wsID);\n    terminalTabs.value.push({\n        index: tabIndex,\n        title: title,\n        wsID: wsID,\n        status: res.data ? 'online' : 'closed',\n        latency: 0,\n    });\n    terminalValue.value = tabIndex;\n    nextTick(() => {\n        ctx.refs[`t-${terminalValue.value}`] &&\n            ctx.refs[`t-${terminalValue.value}`][0].acceptParams({\n                endpoint: '/api/v2/hosts/terminal',\n                args: `id=${wsID}`,\n                initCmd: initCmd.value,\n                error: res.data ? '' : 'Authentication failed. Please check the host information!',\n            });\n        initCmd.value = '';\n    });\n    tabIndex++;\n};\n\nfunction syncTerminal() {\n    for (const terminal of terminalTabs.value) {\n        if (ctx && ctx.refs[`t-${terminal.index}`][0]) {\n            terminal.status = ctx.refs[`t-${terminal.index}`][0].isWsOpen() ? 'online' : 'closed';\n            terminal.latency = ctx.refs[`t-${terminal.index}`][0].getLatency();\n        }\n    }\n}\n\nconst changeFullScreen = () => {\n    globalStore.isFullScreen = screenfull.isFullscreen;\n};\n\ndefineExpose({\n    acceptParams,\n    cleanTimer,\n});\n\nonBeforeUnmount(() => {\n    document.removeEventListener('fullscreenchange', changeFullScreen);\n});\n\nonMounted(() => {\n    if (router.currentRoute.value.query.path) {\n        const path = String(router.currentRoute.value.query.path);\n        initCmd.value = `cd \"${path}\" \\n`;\n    }\n    document.addEventListener('fullscreenchange', changeFullScreen);\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.terminal-tabs {\n    :deep(.el-tabs__header) {\n        padding: 0;\n        position: relative;\n        margin: 0 0 3px 0;\n    }\n    :deep(.el-tabs__nav) {\n        white-space: nowrap;\n        position: relative;\n        transition: transform var(--el-transition-duration);\n        float: left;\n        z-index: calc(var(--el-index-normal) + 1);\n    }\n    :deep(.el-tabs__item) {\n        padding: 0;\n    }\n    :deep(.el-tabs__item.is-active) {\n        color: var(--panel-terminal-tag-active-text-color);\n        background-color: var(--panel-terminal-tag-active-bg-color);\n    }\n    :deep(.el-tabs__item:hover) {\n        color: var(--panel-terminal-tag-hover-text-color);\n    }\n    :deep(.el-tabs__item.is-active:hover) {\n        color: var(--panel-terminal-tag-active-text-color);\n    }\n}\n\n.tagButton {\n    border: 0;\n    background-color: var(--el-tabs__item);\n}\n\n.host-tree {\n    max-height: 300px;\n    overflow-y: auto;\n}\n\n.search-container {\n    :deep(.el-input__wrapper) {\n        border-radius: 6px;\n        box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n\n        &:hover {\n            box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);\n        }\n\n        &.is-focus {\n            box-shadow: 0 0 0 2px var(--el-color-primary-light-3);\n        }\n    }\n}\n\n.vertical-tabs > .el-tabs__content {\n    padding: 32px;\n    color: #6b778c;\n    font-size: 32px;\n    font-weight: 600;\n}\n.el-tabs--top.el-tabs--card > .el-tabs__header .el-tabs__item:last-child {\n    padding-right: 0px;\n}\n.el-input__wrapper {\n    border-radius: 50px;\n}\n\n:deep(.el-textarea__inner) {\n    border-radius: 4px;\n    resize: none;\n    min-height: 32px;\n    transition: height 0.2s ease;\n}\n\n.command-tabs {\n    :deep(.el-tabs__header) {\n        margin-bottom: 0;\n        background-color: var(--el-bg-color);\n    }\n    :deep(.el-tabs__content) {\n        height: 180px;\n        overflow-y: auto;\n        overflow-x: hidden;\n        background-color: var(--el-bg-color);\n    }\n    :deep(.el-tabs__item) {\n        min-width: 80px;\n        max-width: 110px;\n        text-align: center;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        overflow: hidden;\n        padding: 0 8px;\n    }\n}\n.group-tab-label {\n    width: 90px;\n    display: inline-block;\n    text-align: center;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    font-size: 14px;\n}\n\n.command-tag {\n    cursor: pointer;\n    height: auto;\n    padding: 8px 12px;\n    transition: all 0.3s;\n    border-radius: 4px;\n    white-space: nowrap;\n    border: 1px solid transparent;\n\n    &:hover {\n        border-color: var(--el-color-primary);\n    }\n}\n\n.command-tag-name {\n    font-weight: 500;\n    font-size: 13px;\n    flex: 1;\n    text-align: left;\n}\n\n.command-preview-icon {\n    font-size: 14px;\n    opacity: 0.6;\n    transition: opacity 0.3s;\n    cursor: help;\n    flex-shrink: 0;\n    display: flex;\n    align-items: center;\n\n    &:hover {\n        opacity: 1;\n    }\n}\n\n.command-preview {\n    .command-preview-name {\n        font-size: 13px;\n        margin-bottom: 6px;\n        color: var(--el-text-color-primary);\n        word-break: break-word;\n    }\n\n    .command-preview-value {\n        font-size: 12px;\n        font-family: monospace;\n        padding: 8px;\n        background-color: var(--el-fill-color-light);\n        border-radius: 4px;\n        color: var(--el-text-color-regular);\n        word-break: break-all;\n        white-space: pre-wrap;\n    }\n}\n\n.command-tag-name {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    display: inline-block;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/clam/index.vue",
    "content": "<template>\n    <div>\n        <LayoutContent v-loading=\"loading\" v-if=\"!isRecordShow && !isSettingShow\" :title=\"$t('toolbox.clam.clam')\">\n            <template #prompt>\n                <el-alert type=\"info\" :closable=\"false\">\n                    <template #title>\n                        {{ $t('toolbox.clam.clamHelper') }}\n                        <el-link class=\"ml-1 text-xs\" v-if=\"!globalStore.isFxplay\" @click=\"toDoc()\" type=\"primary\">\n                            {{ $t('commons.button.helpDoc') }}\n                        </el-link>\n                    </template>\n                </el-alert>\n            </template>\n            <template #app>\n                <ClamStatus\n                    @setting=\"setting\"\n                    v-model:loading=\"loading\"\n                    @get-status=\"getStatus\"\n                    v-model:mask-show=\"maskShow\"\n                />\n            </template>\n            <template #leftToolBar v-if=\"clamStatus.isExist\">\n                <el-button type=\"primary\" :disabled=\"!clamStatus.isRunning\" @click=\"onOpenDialog('add')\">\n                    {{ $t('toolbox.clam.clamCreate') }}\n                </el-button>\n                <el-button plain :disabled=\"selects.length === 0 || !clamStatus.isRunning\" @click=\"onDelete(null)\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"clam-refresh\" @search=\"search()\" />\n            </template>\n            <el-card v-if=\"clamStatus.isExist && !clamStatus.isRunning && maskShow\" class=\"mask-prompt\">\n                <span>{{ $t('toolbox.clam.notStart') }}</span>\n            </el-card>\n            <template #main v-if=\"clamStatus.isExist\">\n                <ComplexTable\n                    :class=\"{ mask: !clamStatus.isRunning }\"\n                    v-if=\"!isSettingShow\"\n                    :pagination-config=\"paginationConfig\"\n                    v-model:selects=\"selects\"\n                    @sort-change=\"search\"\n                    @search=\"search\"\n                    :data=\"data\"\n                >\n                    <el-table-column type=\"selection\" fix />\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        :min-width=\"60\"\n                        prop=\"name\"\n                        sortable\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"onOpenRecord(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('toolbox.clam.scanDir')\"\n                        :min-width=\"120\"\n                        prop=\"path\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-button link type=\"primary\" @click=\"routerToFileWithPath(row.path)\">\n                                {{ row.path }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        v-if=\"isProductPro\"\n                        :label=\"$t('commons.table.status')\"\n                        :min-width=\"70\"\n                        prop=\"status\"\n                        sortable\n                    >\n                        <template #default=\"{ row }\">\n                            <Status\n                                v-if=\"row.status === 'Enable'\"\n                                :status=\"row.status\"\n                                @click=\"onChangeStatus(row.id, 'disable')\"\n                            />\n                            <Status\n                                v-if=\"row.status === 'Disable'\"\n                                :status=\"row.status\"\n                                @click=\"onChangeStatus(row.id, 'enable')\"\n                            />\n                            <span v-if=\"row.status === ''\">-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        v-if=\"isProductPro\"\n                        :label=\"$t('cronjob.cronSpec')\"\n                        show-overflow-tooltip\n                        :min-width=\"120\"\n                    >\n                        <template #default=\"{ row }\">\n                            <span>\n                                {{ row.spec !== '' ? transSpecToStr(row.spec) : '-' }}\n                            </span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('toolbox.clam.infectedDir')\"\n                        :min-width=\"120\"\n                        prop=\"path\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-button\n                                v-if=\"row.infectedStrategy === 'copy' || row.infectedStrategy === 'move'\"\n                                link\n                                type=\"primary\"\n                                @click=\"routerToFileWithPath(row.infectedDir + '/1panel-infected/' + row.name)\"\n                            >\n                                {{ row.infectedDir + '/1panel-infected/' + row.name }}\n                            </el-button>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('cronjob.lastRecordTime')\"\n                        :min-width=\"120\"\n                        show-overflow-tooltip\n                        prop=\"lastRecordTime\"\n                    >\n                        <template #default=\"{ row }\">\n                            <el-button v-if=\"row.lastRecordStatus === 'Done'\" icon=\"Select\" link type=\"success\" />\n                            <el-button v-if=\"row.lastRecordStatus === 'Failed'\" icon=\"CloseBold\" link type=\"danger\" />\n                            <el-button v-if=\"row.lastRecordStatus === 'Waiting'\" :loading=\"true\" link type=\"info\" />\n                            {{ row.lastRecordTime }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.description')\" prop=\"description\" show-overflow-tooltip>\n                        <template #default=\"{ row }\">\n                            <fu-input-rw-switch\n                                v-model=\"row.description\"\n                                @enter=\"onChange(row)\"\n                                @blur=\"onChange(row)\"\n                            />\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations\n                        width=\"300px\"\n                        :buttons=\"buttons\"\n                        :ellipsis=\"10\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"onSubmitDelete()\">\n            <template #content>\n                <el-form class=\"mt-4 mb-1\" ref=\"deleteForm\" label-position=\"left\">\n                    <el-form-item>\n                        <el-checkbox v-model=\"removeInfected\" :label=\"$t('toolbox.clam.removeInfected')\" />\n                        <span class=\"input-help\">{{ $t('toolbox.clam.removeInfectedHelper') }}</span>\n                    </el-form-item>\n                </el-form>\n            </template>\n        </OpDialog>\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <LogDialog ref=\"dialogLogRef\" />\n        <SettingDialog v-if=\"isSettingShow\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { deleteClam, handleClamScan, searchClam, updateClam, updateClamStatus } from '@/api/modules/toolbox';\nimport OperateDialog from '@/views/toolbox/clam/operate/index.vue';\nimport LogDialog from '@/views/toolbox/clam/record/index.vue';\nimport ClamStatus from '@/views/toolbox/clam/status/index.vue';\nimport SettingDialog from '@/views/toolbox/clam/setting/index.vue';\nimport { Toolbox } from '@/api/interface/toolbox';\nimport { transSpecToStr } from '@/views/cronjob/cronjob/helper';\nimport { GlobalStore } from '@/store';\nimport { storeToRefs } from 'pinia';\nimport { routerToFileWithPath, routerToName } from '@/utils/router';\n\nconst loading = ref();\nconst selects = ref<any>([]);\n\nconst globalStore = GlobalStore();\nconst { isProductPro } = storeToRefs(globalStore);\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'clam-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('clam-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst opRef = ref();\nconst dialogRef = ref();\nconst operateIDs = ref();\nconst dialogLogRef = ref();\nconst isRecordShow = ref();\n\nconst removeInfected = ref();\n\nconst isSettingShow = ref();\nconst maskShow = ref(true);\nconst clamStatus = ref({\n    isExist: false,\n    isRunning: true,\n});\n\nconst search = async (column?: any) => {\n    paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n    paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n    loading.value = true;\n    let params = {\n        info: searchName.value,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        orderBy: paginationConfig.orderBy,\n        order: paginationConfig.order,\n    };\n    await searchClam(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst setting = () => {\n    routerToName('Clam-Setting');\n};\nconst getStatus = (status: any) => {\n    clamStatus.value = status;\n    search();\n};\n\nconst toDoc = () => {\n    window.open(globalStore.docsUrl + '/user_manual/toolbox/clam/', '_blank', 'noopener,noreferrer');\n};\n\nconst onChange = async (row: any) => {\n    await updateClam(row);\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Toolbox.ClamInfo> = {\n        infectedStrategy: 'none',\n        specObj: {\n            specType: 'perDay',\n            week: 1,\n            day: 3,\n            hour: 1,\n            minute: 30,\n            second: 30,\n        },\n        timeoutItem: 5,\n        timeoutUnit: 'h',\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\nconst onOpenRecord = (row: Toolbox.ClamInfo) => {\n    isRecordShow.value = true;\n    let params = {\n        rowData: { ...row },\n    };\n    dialogLogRef.value!.acceptParams(params);\n};\n\nconst onDelete = async (row: Toolbox.ClamInfo | null) => {\n    let names = [];\n    let ids = [];\n    if (row) {\n        ids = [row.id];\n        names = [row.name];\n    } else {\n        for (const item of selects.value) {\n            names.push(item.name);\n            ids.push(item.id);\n        }\n    }\n    operateIDs.value = ids;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('toolbox.clam.clam'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\n\nconst onSubmitDelete = async () => {\n    loading.value = true;\n    await deleteClam({ ids: operateIDs.value, removeInfected: removeInfected.value })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onChangeStatus = async (id: number, status: string) => {\n    ElMessageBox.confirm(i18n.global.t('toolbox.clam.' + status + 'Msg'), i18n.global.t('cronjob.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        let itemStatus = status === 'enable' ? 'Enable' : 'Disable';\n        await updateClamStatus(id, itemStatus);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        search();\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.handle'),\n        click: async (row: Toolbox.ClamInfo) => {\n            loading.value = true;\n            await handleClamScan(row.id)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    search();\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: Toolbox.ClamInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('cronjob.record'),\n        click: (row: Toolbox.ClamInfo) => {\n            onOpenRecord(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Toolbox.ClamInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/clam/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"title\"\n        size=\"large\"\n        :resource=\"dialogData.title === 'add' ? '' : dialogData.rowData?.name\"\n        @close=\"handleClose\"\n    >\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\" v-loading=\"loading\">\n            <el-row type=\"flex\" justify=\"center\">\n                <el-col :span=\"22\">\n                    <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                        <el-input\n                            :disabled=\"dialogData.title === 'edit'\"\n                            clearable\n                            v-model.trim=\"dialogData.rowData!.name\"\n                        />\n                    </el-form-item>\n                    <el-form-item :label=\"$t('toolbox.clam.scanDir')\" prop=\"path\">\n                        <el-input v-model=\"dialogData.rowData!.path\">\n                            <template #prepend>\n                                <el-button icon=\"Folder\" @click=\"scanDirRef.acceptParams({ dir: true })\" />\n                            </template>\n                        </el-input>\n                    </el-form-item>\n\n                    <el-form-item :label=\"$t('toolbox.clam.infectedStrategy')\" prop=\"infectedStrategy\">\n                        <el-radio-group v-model=\"dialogData.rowData!.infectedStrategy\">\n                            <el-radio value=\"none\">{{ $t('toolbox.clam.none') }}</el-radio>\n                            <el-radio value=\"remove\">{{ $t('commons.button.delete') }}</el-radio>\n                            <el-radio value=\"move\">{{ $t('toolbox.clam.move') }}</el-radio>\n                            <el-radio value=\"copy\">{{ $t('commons.button.copy') }}</el-radio>\n                        </el-radio-group>\n                        <span class=\"input-help\">\n                            {{ $t('toolbox.clam.' + dialogData.rowData!.infectedStrategy + 'Helper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item v-if=\"hasInfectedDir()\" :label=\"$t('toolbox.clam.infectedDir')\" prop=\"infectedDir\">\n                        <el-input v-model=\"dialogData.rowData!.infectedDir\">\n                            <template #prepend>\n                                <el-button icon=\"Folder\" @click=\"infectedDirRef.acceptParams({ dir: true })\" />\n                            </template>\n                        </el-input>\n                    </el-form-item>\n\n                    <el-form-item prop=\"hasSpec\">\n                        <el-checkbox v-model=\"dialogData.rowData!.hasSpec\" :label=\"$t('toolbox.clam.cron')\" />\n                    </el-form-item>\n                    <el-form-item prop=\"spec\" v-if=\"dialogData.rowData!.hasSpec && isProductPro\">\n                        <div class=\"grid sm:grid-cols-4 gap-4 grid-cols-1\">\n                            <el-select v-model=\"dialogData.rowData!.specObj.specType\" @change=\"changeSpecType()\">\n                                <el-option\n                                    v-for=\"item in specOptions\"\n                                    :key=\"item.label\"\n                                    :value=\"item.value\"\n                                    :label=\"item.label\"\n                                />\n                            </el-select>\n                            <el-select\n                                v-if=\"dialogData.rowData!.specObj.specType === 'perWeek'\"\n                                v-model=\"dialogData.rowData!.specObj.week\"\n                            >\n                                <el-option\n                                    v-for=\"item in weekOptions\"\n                                    :key=\"item.label\"\n                                    :value=\"item.value\"\n                                    :label=\"item.label\"\n                                />\n                            </el-select>\n                            <el-input\n                                v-if=\"hasDay(dialogData.rowData!.specObj)\"\n                                v-model.number=\"dialogData.rowData!.specObj.day\"\n                            >\n                                <template #append>\n                                    <div class=\"sm:min-w-8 min-w-14 text-center\">\n                                        <el-tooltip :content=\"$t('commons.units.day')\" placement=\"top\">\n                                            {{ $t('commons.units.dayUnit') }}\n                                        </el-tooltip>\n                                    </div>\n                                </template>\n                            </el-input>\n                            <el-input\n                                v-if=\"hasHour(dialogData.rowData!.specObj)\"\n                                v-model.number=\"dialogData.rowData!.specObj.hour\"\n                            >\n                                <template #append>\n                                    <div class=\"sm:min-w-8 min-w-14 text-center\">\n                                        <el-tooltip :content=\"$t('commons.units.hour')\" placement=\"top\">\n                                            {{ $t('commons.units.hourUnit') }}\n                                        </el-tooltip>\n                                    </div>\n                                </template>\n                            </el-input>\n                            <el-input\n                                v-if=\"dialogData.rowData!.specObj.specType !== 'perNSecond'\"\n                                v-model.number=\"dialogData.rowData!.specObj.minute\"\n                            >\n                                <template #append>\n                                    <div class=\"sm:min-w-8 min-w-14 text-center\">\n                                        <el-tooltip :content=\"$t('commons.units.minute')\" placement=\"top\">\n                                            {{ $t('commons.units.minuteUnit') }}\n                                        </el-tooltip>\n                                    </div>\n                                </template>\n                            </el-input>\n                            <el-input\n                                v-if=\"dialogData.rowData!.specObj.specType === 'perNSecond'\"\n                                v-model.number=\"dialogData.rowData!.specObj.second\"\n                            >\n                                <template #append>\n                                    <div class=\"sm:min-w-8 min-w-14 text-center\">\n                                        <el-tooltip :content=\"$t('commons.units.second')\" placement=\"top\">\n                                            {{ $t('commons.units.secondUnit') }}\n                                        </el-tooltip>\n                                    </div>\n                                </template>\n                            </el-input>\n                        </div>\n                    </el-form-item>\n                    <div v-if=\"globalStore.isIntl\">\n                        <el-form-item v-if=\"(dialogData.rowData!.hasSpec) && !isProductPro\">\n                            <span class=\"input-help logText\">\n                                {{ $t('toolbox.clam.alertHelper') }}\n                                <el-link class=\"link\" type=\"primary\" @click=\"toUpload\">\n                                    {{ $t('license.levelUpPro') }}\n                                </el-link>\n                            </span>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"!globalStore.isIntl\">\n                        <el-form-item prop=\"hasAlert\">\n                            <el-checkbox v-model=\"dialogData.rowData!.hasAlert\" :label=\"$t('xpack.alert.isAlert')\" />\n                            <span class=\"input-help\">{{ $t('xpack.alert.clamHelper') }}</span>\n                        </el-form-item>\n                        <el-form-item\n                            v-if=\"(dialogData.rowData!.hasAlert || dialogData.rowData!.hasSpec) && !isProductPro\"\n                        >\n                            <span class=\"input-help logText\">\n                                {{ $t('toolbox.clam.alertHelper') }}\n                                <el-link class=\"link\" type=\"primary\" @click=\"toUpload\">\n                                    {{ $t('license.levelUpPro') }}\n                                </el-link>\n                            </span>\n                        </el-form-item>\n                        <el-form-item\n                            :label=\"$t('xpack.alert.alertMethod')\"\n                            v-if=\"dialogData.rowData!.hasAlert\"\n                            prop=\"alertMethodItems\"\n                        >\n                            <el-select\n                                class=\"selectClass\"\n                                v-model=\"dialogData.rowData!.alertMethodItems\"\n                                multiple\n                                cleanable\n                            >\n                                <el-option value=\"mail\" :label=\"$t('xpack.alert.mail')\" />\n                                <el-option\n                                    value=\"weCom\"\n                                    v-if=\"!globalStore.isIntl\"\n                                    :disabled=\"!dialogData.rowData!.hasAlert || !isProductPro\"\n                                    :label=\"$t('xpack.alert.weCom')\"\n                                />\n                                <el-option\n                                    value=\"dingTalk\"\n                                    v-if=\"!globalStore.isIntl\"\n                                    :disabled=\"!dialogData.rowData!.hasAlert || !isProductPro\"\n                                    :label=\"$t('xpack.alert.dingTalk')\"\n                                />\n                                <el-option\n                                    value=\"feiShu\"\n                                    v-if=\"!globalStore.isIntl\"\n                                    :disabled=\"!dialogData.rowData!.hasAlert || !isProductPro\"\n                                    :label=\"$t('xpack.alert.feiShu')\"\n                                />\n                                <el-option\n                                    value=\"sms\"\n                                    v-if=\"!globalStore.isIntl\"\n                                    :disabled=\"!dialogData.rowData!.hasAlert || !isProductPro\"\n                                    :label=\"$t('xpack.alert.sms')\"\n                                />\n                            </el-select>\n                        </el-form-item>\n                        <el-form-item\n                            prop=\"alertCount\"\n                            v-if=\"dialogData.rowData!.hasAlert\"\n                            :label=\"$t('xpack.alert.alertCount')\"\n                        >\n                            <el-input-number\n                                style=\"width: 200px\"\n                                :min=\"1\"\n                                step-strictly\n                                :step=\"1\"\n                                v-model.number=\"dialogData.rowData!.alertCount\"\n                            ></el-input-number>\n                            <span class=\"input-help\">{{ $t('xpack.alert.alertCountHelper') }}</span>\n                        </el-form-item>\n                    </div>\n                    <el-form-item :label=\"$t('cronjob.timeout')\" prop=\"timeoutItem\">\n                        <el-input type=\"number\" class=\"selectClass\" v-model.number=\"dialogData.rowData!.timeoutItem\">\n                            <template #append>\n                                <el-select v-model=\"dialogData.rowData!.timeoutUnit\" style=\"width: 80px\">\n                                    <el-option :label=\"$t('commons.units.second')\" value=\"s\" />\n                                    <el-option :label=\"$t('commons.units.minute')\" value=\"m\" />\n                                    <el-option :label=\"$t('commons.units.hour')\" value=\"h\" />\n                                </el-select>\n                            </template>\n                        </el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                        <el-input type=\"textarea\" :rows=\"3\" clearable v-model=\"dialogData.rowData!.description\" />\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n        <LicenseImport ref=\"licenseRef\" />\n    </DrawerPro>\n    <FileList ref=\"scanDirRef\" @choose=\"loadDir\" />\n    <FileList ref=\"infectedDirRef\" @choose=\"loadInfectedDir\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport FileList from '@/components/file-list/index.vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport LicenseImport from '@/components/license-import/index.vue';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Toolbox } from '@/api/interface/toolbox';\nimport { createClam, updateClam } from '@/api/modules/toolbox';\nimport { storeToRefs } from 'pinia';\nimport { GlobalStore } from '@/store';\nimport { specOptions, transObjToSpec, transSpecToObj, weekOptions } from '@/views/cronjob/cronjob/helper';\nimport { splitTimeFromSecond, transferTimeToSecond } from '@/utils/util';\n\nconst globalStore = GlobalStore();\nconst licenseRef = ref();\nconst scanDirRef = ref();\nconst infectedDirRef = ref();\nconst { isProductPro } = storeToRefs(globalStore);\ninterface DialogProps {\n    title: string;\n    rowData?: Toolbox.ClamInfo;\n    getTableList?: () => Promise<any>;\n}\nconst loading = ref();\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    if (dialogData.value.rowData?.spec) {\n        dialogData.value.rowData.hasSpec = true;\n        dialogData.value.rowData.specObj = transSpecToObj(dialogData.value.rowData.spec);\n    } else {\n        dialogData.value.rowData.specObj = {\n            specType: 'perDay',\n            week: 1,\n            day: 3,\n            hour: 1,\n            minute: 30,\n            second: 30,\n        };\n    }\n    if (dialogData.value.rowData!.timeout) {\n        let item = splitTimeFromSecond(dialogData.value.rowData!.timeout);\n        dialogData.value.rowData.timeoutItem = item.timeItem;\n        dialogData.value.rowData.timeoutUnit = item.timeUnit;\n    }\n    dialogData.value.rowData.hasAlert = dialogData.value.rowData!.alertCount > 0;\n    dialogData.value.rowData!.alertCount = dialogData.value.rowData!.alertCount || 3;\n    if (dialogData.value.rowData!.alertMethod) {\n        dialogData.value.rowData!.alertMethodItems = dialogData.value.rowData!.alertMethod.split(',') || [];\n    } else {\n        dialogData.value.rowData!.alertMethodItems = [];\n    }\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst verifySpec = (rule: any, value: any, callback: any) => {\n    let item = dialogData.value.rowData!.specObj;\n    if (\n        !Number.isInteger(item.day) ||\n        !Number.isInteger(item.hour) ||\n        !Number.isInteger(item.minute) ||\n        !Number.isInteger(item.second) ||\n        !Number.isInteger(item.week)\n    ) {\n        callback(new Error(i18n.global.t('cronjob.specErr')));\n        return;\n    }\n    switch (item.specType) {\n        case 'perMonth':\n            if (\n                item.day < 0 ||\n                item.day > 31 ||\n                item.hour < 0 ||\n                item.hour > 23 ||\n                item.minute < 0 ||\n                item.minute > 59\n            ) {\n                callback(new Error(i18n.global.t('cronjob.specErr')));\n                return;\n            }\n            break;\n        case 'perNDay':\n            if (\n                item.day < 0 ||\n                item.day > 366 ||\n                item.hour < 0 ||\n                item.hour > 23 ||\n                item.minute < 0 ||\n                item.minute > 59\n            ) {\n                callback(new Error(i18n.global.t('cronjob.specErr')));\n                return;\n            }\n            break;\n        case 'perWeek':\n            if (\n                item.week < 0 ||\n                item.week > 6 ||\n                item.hour < 0 ||\n                item.hour > 23 ||\n                item.minute < 0 ||\n                item.minute > 59\n            ) {\n                callback(new Error(i18n.global.t('cronjob.specErr')));\n                return;\n            }\n            break;\n        case 'perDay':\n            if (item.hour < 0 || item.hour > 23 || item.minute < 0 || item.minute > 59) {\n                callback(new Error(i18n.global.t('cronjob.specErr')));\n                return;\n            }\n            break;\n        case 'perNHour':\n            if (item.hour < 0 || item.hour > 8784 || item.minute < 0 || item.minute > 59) {\n                callback(new Error(i18n.global.t('cronjob.specErr')));\n                return;\n            }\n            break;\n        case 'perHour':\n            if (item.minute < 0 || item.minute > 59) {\n                callback(new Error(i18n.global.t('cronjob.specErr')));\n                return;\n            }\n        case 'perNMinute':\n            if (item.minute < 0 || item.minute > 527040) {\n                callback(new Error(i18n.global.t('cronjob.specErr')));\n                return;\n            }\n            break;\n        case 'perNSecond':\n            if (item.second < 0 || item.second > 31622400) {\n                callback(new Error(i18n.global.t('cronjob.specErr')));\n                return;\n            }\n            break;\n    }\n    callback();\n};\n\nconst checkSendCount = (rule: any, value: any, callback: any) => {\n    if (value === '') {\n        callback();\n    }\n    const regex = /^(?:[1-9]|[12][0-9]|30)$/;\n    if (!regex.test(value)) {\n        return callback(new Error(i18n.global.t('commons.rule.numberRange', [1, 30])));\n    }\n    callback();\n};\nconst rules = reactive({\n    name: [Rules.simpleName],\n    path: [Rules.requiredInput, Rules.noSpace],\n    spec: [\n        { validator: verifySpec, trigger: 'blur', required: true },\n        { validator: verifySpec, trigger: 'change', required: true },\n    ],\n    alertCount: [Rules.integerNumber, { validator: checkSendCount, trigger: 'blur' }],\n    alertMethodItems: [Rules.requiredSelect],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst hasInfectedDir = () => {\n    return (\n        dialogData.value.rowData!.infectedStrategy === 'move' || dialogData.value.rowData!.infectedStrategy === 'copy'\n    );\n};\nconst loadDir = async (path: string) => {\n    dialogData.value.rowData!.path = path;\n};\nconst loadInfectedDir = async (path: string) => {\n    dialogData.value.rowData!.infectedDir = path;\n};\nconst hasDay = (item: any) => {\n    return item.specType === 'perMonth' || item.specType === 'perNDay';\n};\nconst hasHour = (item: any) => {\n    return item.specType !== 'perHour' && item.specType !== 'perNMinute' && item.specType !== 'perNSecond';\n};\n\nconst toUpload = () => {\n    licenseRef.value.acceptParams();\n};\n\nconst changeSpecType = () => {\n    let item = dialogData.value.rowData!.specObj;\n    switch (item.specType) {\n        case 'perMonth':\n        case 'perNDay':\n            item.day = 3;\n            item.hour = 1;\n            item.minute = 30;\n            break;\n        case 'perWeek':\n            item.week = 1;\n            item.hour = 1;\n            item.minute = 30;\n            break;\n        case 'perDay':\n        case 'perNHour':\n            item.hour = 2;\n            item.minute = 30;\n            break;\n        case 'perHour':\n        case 'perNMinute':\n            item.minute = 30;\n            break;\n        case 'perNSecond':\n            item.second = 30;\n            break;\n    }\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        let spec = '';\n        let item = dialogData.value.rowData.specObj;\n        if (dialogData.value.rowData!.hasSpec) {\n            spec = transObjToSpec(item.specType, item.week, item.day, item.hour, item.minute, item.second);\n            if (spec === '') {\n                MsgError(i18n.global.t('cronjob.cronSpecHelper'));\n                return;\n            }\n        }\n        dialogData.value.rowData.timeout = transferTimeToSecond(\n            dialogData.value.rowData.timeoutItem + dialogData.value.rowData.timeoutUnit,\n        );\n        dialogData.value.rowData.spec = spec;\n        if (dialogData.value.rowData!.hasAlert) {\n            dialogData.value.rowData.alertCount = dialogData.value.rowData!.hasAlert\n                ? dialogData.value.rowData.alertCount\n                : 0;\n            dialogData.value.rowData.alertTitle = i18n.global.t('toolbox.clam.alertTitle', [\n                dialogData.value.rowData.name,\n            ]);\n            dialogData.value.rowData.alertMethod = dialogData.value.rowData.alertMethodItems.join(',');\n        } else {\n            dialogData.value.rowData.alertTitle = '';\n            dialogData.value.rowData.alertCount = 0;\n            dialogData.value.rowData.hasAlert = false;\n            dialogData.value.rowData.alertMethod = '';\n            dialogData.value.rowData.alertMethodItems = [];\n        }\n\n        if (dialogData.value.title === 'edit') {\n            await updateClam(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n\n            return;\n        }\n\n        await createClam(dialogData.value.rowData)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                drawerVisible.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style scoped lang=\"scss\">\n.logText {\n    line-height: 22px;\n    font-size: 12px;\n    .link {\n        font-size: 12px !important;\n        margin-top: -3px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/clam/record/index.vue",
    "content": "<template>\n    <div v-if=\"recordShow\" v-loading=\"loading\">\n        <div class=\"app-status card-interval\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag class=\"float-left\" effect=\"dark\" type=\"success\">\n                            {{ $t('commons.table.name') }}: {{ dialogData.rowData.name }}\n                        </el-tag>\n                        <el-popover\n                            v-if=\"dialogData.rowData.path.length >= 35\"\n                            placement=\"top-start\"\n                            trigger=\"hover\"\n                            width=\"250\"\n                            :content=\"dialogData.rowData.path\"\n                        >\n                            <template #reference>\n                                <el-tag style=\"float: left\" effect=\"dark\" type=\"success\">\n                                    {{ $t('file.path') }}: {{ dialogData.rowData.path.substring(0, 20) }}...\n                                </el-tag>\n                            </template>\n                        </el-popover>\n                        <el-tag\n                            v-if=\"dialogData.rowData.path.length < 35\"\n                            class=\"float-left\"\n                            effect=\"dark\"\n                            type=\"success\"\n                        >\n                            {{ $t('toolbox.clam.scanDir') }}: {{ dialogData.rowData.path }}\n                        </el-tag>\n\n                        <span class=\"mt-0.5\">\n                            <el-button type=\"primary\" @click=\"onHandle(dialogData.rowData)\" link>\n                                {{ $t('commons.button.handle') }}\n                            </el-button>\n                            <el-divider direction=\"vertical\" />\n                            <el-button :disabled=\"!hasRecords\" type=\"primary\" @click=\"onClean\" link>\n                                {{ $t('commons.button.clean') }}\n                            </el-button>\n                        </span>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n\n        <LayoutContent :title=\"$t('cronjob.record')\" :reload=\"true\">\n            <template #rightToolBar>\n                <el-date-picker\n                    class=\"mr-2.5\"\n                    @change=\"search(true)\"\n                    v-model=\"timeRangeLoad\"\n                    type=\"datetimerange\"\n                    range-separator=\"-\"\n                    :start-placeholder=\"$t('commons.search.timeStart')\"\n                    :end-placeholder=\"$t('commons.search.timeEnd')\"\n                    :shortcuts=\"shortcuts\"\n                ></el-date-picker>\n                <el-select @change=\"search(true)\" v-model=\"searchInfo.status\" class=\"p-w-200\">\n                    <template #prefix>{{ $t('commons.table.status') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" value=\"\" />\n                    <el-option :label=\"$t('commons.status.done')\" value=\"Done\" />\n                    <el-option :label=\"$t('commons.status.waiting')\" value=\"Waiting\" />\n                    <el-option :label=\"$t('commons.status.failed')\" value=\"Failed\" />\n                </el-select>\n                <TableRefresh @search=\"search(false)\" />\n            </template>\n            <template #main>\n                <div class=\"mainClass\">\n                    <el-row :gutter=\"20\" v-show=\"hasRecords\" class=\"mainRowClass row-box\">\n                        <el-col :span=\"7\">\n                            <el-card class=\"el-card\">\n                                <div class=\"infinite-list\" style=\"overflow: auto\">\n                                    <el-table\n                                        style=\"cursor: pointer\"\n                                        :data=\"records\"\n                                        border\n                                        :show-header=\"false\"\n                                        @row-click=\"forDetail\"\n                                    >\n                                        <el-table-column>\n                                            <template #default=\"{ row }\">\n                                                <span v-if=\"row.id === currentRecord.id\" class=\"select-sign\"></span>\n                                                <Status class=\"mr-2 ml-1 float-left\" :status=\"row.status\" />\n                                                <div class=\"mt-0.5 float-left\">\n                                                    <span>\n                                                        {{ dateFormat(0, 0, row.startTime) }}\n                                                    </span>\n                                                </div>\n                                                <el-button\n                                                    class=\"mt-0.5 float-right\"\n                                                    type=\"danger\"\n                                                    icon=\"Warning\"\n                                                    link\n                                                    v-if=\"row.infectedFiles && row.infectedFiles !== '0'\"\n                                                />\n                                            </template>\n                                        </el-table-column>\n                                    </el-table>\n                                </div>\n                                <div class=\"page-item\">\n                                    <el-pagination\n                                        :page-size=\"searchInfo.pageSize\"\n                                        :current-page=\"searchInfo.page\"\n                                        @current-change=\"handleCurrentChange\"\n                                        @size-change=\"handleSizeChange\"\n                                        :pager-count=\"5\"\n                                        :page-sizes=\"[5, 10, 20, 50, 100, 200, 500, 1000]\"\n                                        small\n                                        layout=\"total, sizes, prev, pager, next\"\n                                        :total=\"searchInfo.recordTotal\"\n                                    />\n                                </div>\n                            </el-card>\n                        </el-col>\n                        <el-col :span=\"17\">\n                            <el-card class=\"el-card\">\n                                <el-form label-position=\"top\" :v-key=\"refresh\">\n                                    <el-row>\n                                        <el-form-item class=\"descriptionWide\">\n                                            <template #label>\n                                                <span class=\"status-label\">{{ $t('commons.table.interval') }}</span>\n                                            </template>\n                                            <span class=\"status-count\">\n                                                {{ currentRecord?.status === 'Done' ? currentRecord?.scanTime : '-' }}\n                                            </span>\n                                        </el-form-item>\n                                        <el-form-item class=\"descriptionWide\">\n                                            <template #label>\n                                                <span class=\"status-label\">{{ $t('toolbox.clam.infectedFiles') }}</span>\n                                            </template>\n                                            <span class=\"status-count\" v-if=\"!hasInfectedDir()\">\n                                                {{\n                                                    currentRecord?.status === 'Done'\n                                                        ? currentRecord?.infectedFiles\n                                                        : '-'\n                                                }}\n                                            </span>\n                                            <div class=\"count\" v-else>\n                                                <span @click=\"toFolder(currentRecord)\">\n                                                    {{\n                                                        currentRecord?.status === 'Done'\n                                                            ? currentRecord?.infectedFiles\n                                                            : '-'\n                                                    }}\n                                                </span>\n                                            </div>\n                                        </el-form-item>\n                                    </el-row>\n                                    <el-row v-if=\"currentRecord?.taskID && currentRecord?.taskID != ''\">\n                                        <LogFile\n                                            :defaultButton=\"true\"\n                                            class=\"w-full\"\n                                            :key=\"currentRecord?.taskID\"\n                                            @stop-reading=\"search(false)\"\n                                            :heightDiff=\"420\"\n                                            :config=\"{\n                                                type: 'task',\n                                                colorMode: 'task',\n                                                taskID: currentRecord?.taskID,\n                                                tail: true,\n                                            }\"\n                                        />\n                                    </el-row>\n                                </el-form>\n                            </el-card>\n                        </el-col>\n                    </el-row>\n                </div>\n                <div class=\"app-warn\" v-show=\"!hasRecords\">\n                    <div>\n                        <span>{{ $t('toolbox.clam.noRecords') }}</span>\n                        <div>\n                            <img src=\"@/assets/images/no_app.svg\" />\n                        </div>\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { ElMessageBox } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { shortcuts } from '@/utils/shortcuts';\nimport { dateFormat, dateFormatForName } from '@/utils/util';\nimport { Toolbox } from '@/api/interface/toolbox';\nimport LogFile from '@/components/log/file/index.vue';\nimport { cleanClamRecord, handleClamScan, searchClamRecord } from '@/api/modules/toolbox';\nimport { routerToFileWithPath } from '@/utils/router';\n\nconst loading = ref();\nconst refresh = ref(false);\nconst hasRecords = ref();\n\nconst recordShow = ref(false);\ninterface DialogProps {\n    rowData: Toolbox.ClamInfo;\n}\nconst dialogData = ref();\nconst records = ref<Array<Toolbox.ClamRecord>>([]);\nconst currentRecord = ref<Toolbox.ClamRecord>();\n\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    let itemSize = Number(localStorage.getItem(searchInfo.cacheSizeKey));\n    if (itemSize) {\n        searchInfo.pageSize = itemSize;\n    }\n\n    recordShow.value = true;\n    dialogData.value = params;\n    search(true);\n};\n\nconst handleSizeChange = (val: number) => {\n    searchInfo.pageSize = val;\n    localStorage.setItem(searchInfo.cacheSizeKey, val + '');\n    search(true);\n};\nconst handleCurrentChange = (val: number) => {\n    searchInfo.page = val;\n    search(false);\n};\nconst hasInfectedDir = () => {\n    return (\n        dialogData.value.rowData!.infectedStrategy === 'move' || dialogData.value.rowData!.infectedStrategy === 'copy'\n    );\n};\n\nconst timeRangeLoad = ref<[Date, Date]>([\n    new Date(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7).setHours(0, 0, 0, 0)),\n    new Date(new Date().setHours(23, 59, 59, 999)),\n]);\nconst searchInfo = reactive({\n    cacheSizeKey: 'clam-record-page-size',\n    page: 1,\n    pageSize: 10,\n    status: '',\n    recordTotal: 0,\n    startTime: new Date(),\n    endTime: new Date(),\n});\n\nconst onHandle = async (row: Toolbox.ClamInfo) => {\n    loading.value = true;\n    await handleClamScan(row.id)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search(true);\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst toFolder = async (row: any) => {\n    let folder =\n        dialogData.value.rowData!.infectedDir +\n        '/1panel-infected/' +\n        dialogData.value.rowData!.name +\n        '/' +\n        dateFormatForName(row.startTime);\n    routerToFileWithPath(folder);\n};\n\nconst search = async (changeToLatest: boolean) => {\n    if (timeRangeLoad.value && timeRangeLoad.value.length === 2) {\n        searchInfo.startTime = timeRangeLoad.value[0];\n        searchInfo.endTime = timeRangeLoad.value[1];\n    } else {\n        searchInfo.startTime = new Date(new Date().setHours(0, 0, 0, 0));\n        searchInfo.endTime = new Date();\n    }\n    let params = {\n        page: searchInfo.page,\n        pageSize: searchInfo.pageSize,\n        clamID: dialogData.value.rowData!.id,\n        status: searchInfo.status,\n        startTime: searchInfo.startTime,\n        endTime: searchInfo.endTime,\n    };\n    const res = await searchClamRecord(params);\n    records.value = res.data.items;\n    searchInfo.recordTotal = res.data.total;\n    hasRecords.value = searchInfo.recordTotal !== 0;\n    if (!hasRecords.value) {\n        return;\n    }\n    if (changeToLatest) {\n        currentRecord.value = records.value[0];\n        return;\n    }\n    for (const item of records.value) {\n        if (item.id === currentRecord.value.id) {\n            currentRecord.value = item;\n            break;\n        }\n    }\n};\n\nconst forDetail = async (row: Toolbox.ClamRecord) => {\n    currentRecord.value = row;\n};\n\nconst onClean = async () => {\n    ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('commons.button.delete'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'warning',\n    }).then(async () => {\n        loading.value = true;\n        cleanClamRecord(dialogData.value.rowData.id)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search(false);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.infinite-list {\n    height: calc(100vh - 318px);\n    .select-sign {\n        &::before {\n            float: left;\n            margin-left: -3px;\n            position: relative;\n            width: 3px;\n            height: 24px;\n            content: '';\n            background: $primary-color;\n            border-radius: 20px;\n        }\n    }\n    .el-tag {\n        margin-left: 20px;\n        margin-right: 20px;\n    }\n}\n\n.descriptionWide {\n    width: 40%;\n}\n.description {\n    width: 30%;\n}\n.page-item {\n    margin-top: 10px;\n    font-size: 12px;\n    float: right;\n}\n\n.count {\n    span {\n        font-size: 25px;\n        color: $primary-color;\n        font-weight: 500;\n        line-height: 32px;\n        cursor: pointer;\n    }\n}\n\n@media only screen and (max-width: 1400px) {\n    .mainClass {\n        overflow: auto;\n    }\n    .mainRowClass {\n        min-width: 1200px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/clam/setting/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent backName=\"Clam\">\n            <template #app>\n                <ClamStatus v-model:loading=\"loading\" />\n            </template>\n            <template #leftToolBar>\n                <el-button type=\"primary\" :plain=\"activeName !== 'clamd'\" @click=\"search('clamd')\">\n                    {{ $t('toolbox.clam.clamConf') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"activeName !== 'freshclam'\" @click=\"search('freshclam')\">\n                    {{ $t('toolbox.clam.freshClam') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"activeName !== 'clamd-log'\" @click=\"search('clamd-log')\">\n                    {{ $t('toolbox.clam.clamLog') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"activeName !== 'freshclam-log'\" @click=\"search('freshclam-log')\">\n                    {{ $t('toolbox.clam.freshClamLog') }}\n                </el-button>\n            </template>\n\n            <template #main>\n                <div>\n                    <el-select v-if=\"!canUpdate()\" style=\"width: 20%\" @change=\"search()\" v-model.number=\"tail\">\n                        <template #prefix>{{ $t('toolbox.clam.tail') }}</template>\n                        <el-option :value=\"0\" :label=\"$t('commons.table.all')\" />\n                        <el-option :value=\"10\" :label=\"10\" />\n                        <el-option :value=\"100\" :label=\"100\" />\n                        <el-option :value=\"200\" :label=\"200\" />\n                        <el-option :value=\"500\" :label=\"500\" />\n                        <el-option :value=\"1000\" :label=\"1000\" />\n                    </el-select>\n                    <CodemirrorPro\n                        :heightDiff=\"400\"\n                        class=\"mt-5\"\n                        v-model=\"content\"\n                        :disabled=\"!canUpdate()\"\n                        :placeholder=\"$t('commons.msg.noneData')\"\n                    ></CodemirrorPro>\n                    <el-button type=\"primary\" class=\"mt-5\" v-if=\"canUpdate()\" @click=\"onSave\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </div>\n            </template>\n        </LayoutContent>\n\n        <ConfirmDialog ref=\"confirmRef\" @confirm=\"onSubmit\"></ConfirmDialog>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, ref } from 'vue';\nimport ClamStatus from '@/views/toolbox/clam/status/index.vue';\nimport { searchClamFile, updateClamFile } from '@/api/modules/toolbox';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref(false);\n\nconst activeName = ref('clamd');\nconst tail = ref(200);\nconst content = ref();\nconst confirmRef = ref();\n\nconst canUpdate = () => {\n    return activeName.value.indexOf('-log') === -1;\n};\n\nconst search = async (itemName?: string) => {\n    if (itemName) {\n        tail.value = itemName.indexOf('-log') === -1 ? 0 : 200;\n        activeName.value = itemName;\n    }\n    loading.value = true;\n    await searchClamFile(activeName.value, tail.value + '')\n        .then((res) => {\n            loading.value = false;\n            content.value = res.data;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onSave = async () => {\n    let params = {\n        header: i18n.global.t('database.confChange'),\n        operationInfo: i18n.global.t('database.restartNowHelper'),\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmRef.value!.acceptParams(params);\n};\n\nconst onSubmit = async () => {\n    loading.value = true;\n    await updateClamFile(activeName.value, content.value)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    search(activeName.value);\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/clam/status/index.vue",
    "content": "<template>\n    <div>\n        <div class=\"app-status\" v-if=\"data.isExist\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag class=\"w-17\" effect=\"dark\" type=\"success\">ClamAV</el-tag>\n                        <Status class=\"mt-0.5\" :status=\"data.isActive ? 'enable' : 'disable'\" />\n                        <el-tag class=\"w-24\">{{ $t('app.version') }}:{{ data.version }}</el-tag>\n                    </div>\n                    <div class=\"mt-0.5\">\n                        <el-button type=\"primary\" v-if=\"!data.isActive\" link @click=\"onOperate('ClamAV', 'start')\">\n                            {{ $t('commons.operate.start') }}\n                        </el-button>\n                        <el-button type=\"primary\" v-if=\"data.isActive\" link @click=\"onOperate('ClamAV', 'stop')\">\n                            {{ $t('commons.operate.stop') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" link @click=\"onOperate('ClamAV', 'restart')\">\n                            {{ $t('commons.button.restart') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" link @click=\"setting\">\n                            {{ $t('commons.button.set') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" v-if=\"showFresh\" link @click=\"changeShow(false)\">\n                            {{ $t('toolbox.clam.hideFresh') }}\n                        </el-button>\n                        <el-button type=\"primary\" v-if=\"!showFresh\" link @click=\"changeShow(true)\">\n                            {{ $t('toolbox.clam.showFresh') }}\n                        </el-button>\n                    </div>\n                </div>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row mt-5\" v-if=\"showFresh\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag class=\"w-16\" effect=\"dark\" type=\"success\">FreshClam</el-tag>\n                        <Status class=\"mt-0.5\" :status=\"data.freshIsActive ? 'enable' : 'disable'\" />\n                        <el-tag class=\"w-24\">{{ $t('app.version') }}:{{ data.freshVersion }}</el-tag>\n                    </div>\n                    <div class=\"mt-0.5\">\n                        <el-button\n                            type=\"primary\"\n                            v-if=\"!data.freshIsActive\"\n                            link\n                            @click=\"onOperate('FreshClam', 'start')\"\n                        >\n                            {{ $t('commons.operate.start') }}\n                        </el-button>\n                        <el-button\n                            type=\"primary\"\n                            v-if=\"data.freshIsActive\"\n                            link\n                            @click=\"onOperate('FreshClam', 'stop')\"\n                        >\n                            {{ $t('commons.operate.stop') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" link @click=\"onOperate('FreshClam', 'restart')\">\n                            {{ $t('commons.button.restart') }}\n                        </el-button>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n        <NoSuchService v-if=\"!data.isExist\" name=\"ClamAV\" />\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { searchClamBaseInfo, updateClamBaseInfo } from '@/api/modules/toolbox';\nimport NoSuchService from '@/components/layout-content/no-such-service.vue';\nimport { onMounted, ref } from 'vue';\nimport { ElMessageBox } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst data = ref({\n    isExist: true,\n    isActive: true,\n    version: '',\n\n    freshIsExist: false,\n    freshIsActive: false,\n    freshVersion: '',\n});\nconst loading = ref(false);\nconst showFresh = ref(localStorage.getItem('clam-fresh-show') !== 'hide');\n\nconst em = defineEmits(['setting', 'getStatus', 'update:loading', 'update:maskShow']);\n\nconst setting = () => {\n    em('setting', true);\n};\n\nconst changeShow = (val: boolean) => {\n    showFresh.value = val;\n    localStorage.setItem('clam-fresh-show', showFresh.value ? 'show' : 'hide');\n};\n\nconst onOperate = async (service: string, operation: string) => {\n    em('update:maskShow', false);\n    ElMessageBox.confirm(\n        i18n.global.t('commons.msg.operatorHelper', [\n            ' ' + service + ' ',\n            i18n.global.t('commons.operate.' + operation),\n        ]),\n        i18n.global.t('commons.operate.' + operation),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    )\n        .then(() => {\n            em('update:loading', true);\n            if (service === 'FreshClam') {\n                operation = 'fresh-' + operation;\n            }\n            updateClamBaseInfo(operation)\n                .then(() => {\n                    em('update:maskShow', true);\n                    getStatus();\n                    em('update:loading', false);\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    em('update:loading', false);\n                });\n        })\n        .catch(() => {\n            em('update:maskShow', true);\n        });\n};\n\nconst getStatus = async () => {\n    try {\n        loading.value = true;\n        em('update:loading', true);\n        const res = await searchClamBaseInfo();\n        data.value = res.data;\n        const status = {\n            isExist: data.value.isExist,\n            isRunning: data.value.isActive,\n        };\n        em('getStatus', status);\n    } catch (error) {}\n    em('update:loading', false);\n    loading.value = false;\n};\n\nonMounted(() => {\n    getStatus();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.tool-status {\n    margin-top: 20px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/clean/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent :title=\"$t('setting.diskClean')\" :divider=\"true\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"scanData\">\n                    {{ $t('clean.scan') }}\n                </el-button>\n            </template>\n            <template #main>\n                <div v-if=\"scanStatus === 'beforeScan'\" class=\"ml-5\">\n                    <div v-if=\"form.lastCleanTime\">\n                        <el-text class=\"clean_title\">\n                            {{ $t('clean.lastCleanTime', [form.lastCleanTime || '-']) }}\n                        </el-text>\n                        <div class=\"mt-4\">\n                            <el-text>\n                                {{\n                                    $t('clean.lastCleanHelper', [\n                                        form.lastCleanData || '-',\n                                        form.lastCleanSize ? computeSize(Number(form.lastCleanSize)) : '-',\n                                    ])\n                                }}\n                            </el-text>\n                        </div>\n                    </div>\n                    <div v-else>\n                        <el-text class=\"clean_title\">\n                            <el-icon>\n                                <MagicStick />\n                            </el-icon>\n                            {{ $t('clean.scanHelper') }}\n                        </el-text>\n                    </div>\n                    <el-row type=\"flex\" justify=\"center\" :gutter=\"20\" class=\"mb-10\">\n                        <el-col :xs=\"24\" :sm=\"24\" :md=\"24\" :lg=\"12\" :xl=\"12\" v-for=\"item in cleanTag\" :key=\"item.title\">\n                            <el-card class=\"clean-card\">\n                                <el-row>\n                                    <el-col :span=\"4\">\n                                        <el-button\n                                            v-if=\"item.icon.indexOf('p-') === -1\"\n                                            :icon=\"item.icon\"\n                                            link\n                                            class=\"card_icon\"\n                                        />\n                                        <template v-else>\n                                            <el-button link class=\"svg-icon\">\n                                                <svg-icon :iconName=\"item.icon\"></svg-icon>\n                                            </el-button>\n                                        </template>\n                                    </el-col>\n                                    <el-col :span=\"20\">\n                                        <div>\n                                            <el-text class=\"mx-1 card_title\" type=\"primary\">\n                                                {{ $t(item.title) }}\n                                            </el-text>\n                                        </div>\n                                        <span class=\"input-help\">{{ $t(item.description) }}</span>\n                                    </el-col>\n                                </el-row>\n                            </el-card>\n                        </el-col>\n                    </el-row>\n                </div>\n                <div v-if=\"scanStatus === 'afterScan'\" class=\"ml-5\">\n                    <el-text class=\"clean_title\">{{ $t('clean.cleanSuccessful') }}</el-text>\n                    <div class=\"mt-4\">\n                        <el-text>\n                            {{\n                                $t('clean.currentCleanHelper', [\n                                    form.lastCleanData,\n                                    computeSize(Number(form.lastCleanSize)),\n                                ])\n                            }}\n                        </el-text>\n                    </div>\n                </div>\n                <div v-if=\"scanStatus === 'scanned'\" class=\"ml-5\">\n                    <div>\n                        <el-text class=\"clean_title\">{{ $t('clean.totalScan') }} {{ computeSize(totalSize) }}</el-text>\n                        <el-button\n                            type=\"primary\"\n                            class=\"-mt-2 ml-20\"\n                            :disabled=\"selectSize <= 0\"\n                            @click=\"onSubmitClean\"\n                        >\n                            {{ $t('clean.clean') }}\n                        </el-button>\n                        <div class=\"mt-4\">\n                            <el-text type=\"info\">{{ $t('clean.selectScan') }} {{ computeSize(selectSize) }}</el-text>\n                        </div>\n                    </div>\n                    <el-row type=\"flex\" :gutter=\"20\" class=\"mb-10\">\n                        <el-col :xs=\"24\" :sm=\"24\" :md=\"24\" :lg=\"16\" :xl=\"16\">\n                            <div class=\"tree-container\">\n                                <el-collapse v-model=\"activeNames\" class=\"tree-collapse\">\n                                    <el-collapse-item :title=\"$t('clean.system')\" name=\"system\">\n                                        <el-tree\n                                            ref=\"systemRef\"\n                                            :data=\"cleanData.systemClean\"\n                                            node-key=\"id\"\n                                            :default-checked-keys=\"systemDefaultCheck\"\n                                            show-checkbox\n                                            :props=\"defaultProps\"\n                                            @check=\"onChange\"\n                                            class=\"responsive-tree\"\n                                            :empty-text=\"$t('clean.statusEmpty')\"\n                                        >\n                                            <template #default=\"{ node, data }\">\n                                                <el-text>\n                                                    {{\n                                                        load18n(data.label) +\n                                                        '     ' +\n                                                        computeSize(data.size) +\n                                                        '     ' +\n                                                        loadTag(node, data)\n                                                    }}\n                                                </el-text>\n                                            </template>\n                                        </el-tree>\n                                    </el-collapse-item>\n\n                                    <el-collapse-item :title=\"$t('clean.unknownBackup')\" name=\"backup\">\n                                        <el-tree\n                                            ref=\"backupRef\"\n                                            :data=\"cleanData.backupClean\"\n                                            node-key=\"id\"\n                                            :default-checked-keys=\"backupDefaultCheck\"\n                                            show-checkbox\n                                            :props=\"defaultProps\"\n                                            @check=\"onChange\"\n                                            class=\"responsive-tree\"\n                                            :empty-text=\"$t('clean.statusEmpty')\"\n                                        >\n                                            <template #default=\"{ node, data }\">\n                                                <el-text>\n                                                    {{\n                                                        load18n(data.label) +\n                                                        '     ' +\n                                                        computeSize(data.size) +\n                                                        '     ' +\n                                                        loadTag(node, data)\n                                                    }}\n                                                </el-text>\n                                            </template>\n                                        </el-tree>\n                                    </el-collapse-item>\n\n                                    <el-collapse-item :title=\"$t('clean.docker')\" name=\"docker\">\n                                        <el-tree\n                                            ref=\"containerRef\"\n                                            :data=\"cleanData.containerClean\"\n                                            node-key=\"id\"\n                                            :default-checked-keys=\"containerDefaultCheck\"\n                                            show-checkbox\n                                            :props=\"defaultProps\"\n                                            @check=\"onChange\"\n                                            class=\"responsive-tree\"\n                                            :empty-text=\"$t('clean.statusEmpty')\"\n                                        >\n                                            <template #default=\"{ node, data }\">\n                                                <el-text>\n                                                    {{\n                                                        load18n(data.label) +\n                                                        '     ' +\n                                                        computeSize(data.size) +\n                                                        '     ' +\n                                                        loadTag(node, data)\n                                                    }}\n                                                </el-text>\n                                            </template>\n                                        </el-tree>\n                                    </el-collapse-item>\n                                    <el-collapse-item :title=\"$t('clean.upload')\" name=\"upload\">\n                                        <el-tree\n                                            ref=\"uploadRef\"\n                                            :data=\"cleanData.uploadClean\"\n                                            node-key=\"id\"\n                                            :default-checked-keys=\"uploadDefaultCheck\"\n                                            show-checkbox\n                                            :props=\"defaultProps\"\n                                            @check=\"onChange\"\n                                            class=\"responsive-tree\"\n                                            :empty-text=\"$t('clean.statusEmpty')\"\n                                        >\n                                            <template #default=\"{ node, data }\">\n                                                <el-text>\n                                                    {{\n                                                        load18n(data.label) +\n                                                        '     ' +\n                                                        computeSize(data.size) +\n                                                        '     ' +\n                                                        loadTag(node, data)\n                                                    }}\n                                                </el-text>\n                                            </template>\n                                        </el-tree>\n                                    </el-collapse-item>\n                                    <el-collapse-item :title=\"$t('clean.download')\" name=\"download\">\n                                        <el-tree\n                                            ref=\"downloadRef\"\n                                            :data=\"cleanData.downloadClean\"\n                                            node-key=\"id\"\n                                            :default-checked-keys=\"downloadDefaultCheck\"\n                                            show-checkbox\n                                            :props=\"defaultProps\"\n                                            @check=\"onChange\"\n                                            class=\"responsive-tree\"\n                                            :empty-text=\"$t('clean.statusEmpty')\"\n                                        >\n                                            <template #default=\"{ node, data }\">\n                                                <el-text>\n                                                    {{\n                                                        load18n(data.label) +\n                                                        '     ' +\n                                                        computeSize(data.size) +\n                                                        '     ' +\n                                                        loadTag(node, data)\n                                                    }}\n                                                </el-text>\n                                            </template>\n                                        </el-tree>\n                                    </el-collapse-item>\n                                    <el-collapse-item :title=\"$t('clean.systemLog')\" name=\"system_log\">\n                                        <el-tree\n                                            ref=\"systemLogRef\"\n                                            :data=\"cleanData.systemLogClean\"\n                                            node-key=\"id\"\n                                            :default-checked-keys=\"systemLogDefaultCheck\"\n                                            show-checkbox\n                                            :props=\"defaultProps\"\n                                            @check=\"onChange\"\n                                            class=\"responsive-tree\"\n                                            :empty-text=\"$t('clean.statusEmpty')\"\n                                        >\n                                            <template #default=\"{ node, data }\">\n                                                <el-text>\n                                                    {{\n                                                        load18n(data.label) +\n                                                        '     ' +\n                                                        computeSize(data.size) +\n                                                        '     ' +\n                                                        loadTag(node, data)\n                                                    }}\n                                                </el-text>\n                                            </template>\n                                        </el-tree>\n                                    </el-collapse-item>\n                                </el-collapse>\n                            </div>\n                        </el-col>\n                    </el-row>\n                </div>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref } from 'vue';\nimport { computeSize } from '@/utils/util';\nimport { getSettingInfo } from '@/api/modules/setting';\nimport { clean, scan } from '@/api/modules/toolbox';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref();\nconst totalSize = ref<number>(0);\nconst selectSize = ref<number>(0);\n\nconst scanStatus = ref<string>('beforeScan');\n\nconst defaultProps = {\n    children: 'children',\n    label: 'label',\n};\nconst cleanData = reactive({\n    systemClean: [],\n    backupClean: [],\n    uploadClean: [],\n    downloadClean: [],\n    systemLogClean: [],\n    containerClean: [],\n});\nconst systemRef = ref();\nconst systemDefaultCheck = ref([]);\nconst backupRef = ref();\nconst backupDefaultCheck = ref([]);\nconst uploadRef = ref();\nconst uploadDefaultCheck = ref([]);\nconst downloadRef = ref();\nconst downloadDefaultCheck = ref([]);\nconst systemLogRef = ref();\nconst systemLogDefaultCheck = ref([]);\nconst containerRef = ref();\nconst containerDefaultCheck = ref([]);\nconst activeNames = ref(['system', 'backup', 'upload', 'download', 'system_log', 'docker']);\n\nconst cleanTag = [\n    { icon: 'Setting', title: i18n.global.t('clean.system'), description: i18n.global.t('clean.systemHelper') },\n    { icon: 'Upload', title: i18n.global.t('clean.upload'), description: i18n.global.t('clean.uploadHelper') },\n    { icon: 'Files', title: i18n.global.t('clean.backup'), description: i18n.global.t('clean.backupHelper') },\n    { icon: 'Download', title: i18n.global.t('clean.download'), description: i18n.global.t('clean.downloadHelper') },\n    {\n        icon: 'p-docker1',\n        title: i18n.global.t('clean.docker'),\n        description: i18n.global.t('clean.dockerHelper'),\n    },\n    { icon: 'Document', title: i18n.global.t('clean.systemLog'), description: i18n.global.t('clean.systemLogHelper') },\n];\n\nconst submitCleans = ref();\n\nconst form = reactive({\n    lastCleanTime: '',\n    lastCleanSize: '',\n    lastCleanData: '',\n});\n\nconst scanData = async () => {\n    loading.value = true;\n    await scan()\n        .then((res) => {\n            loading.value = false;\n            selectSize.value = 0;\n            totalSize.value = 0;\n            cleanData.systemClean = res.data.systemClean || [];\n            for (const item of cleanData.systemClean) {\n                totalSize.value += item.size;\n            }\n            cleanData.backupClean = res.data.backupClean || [];\n            for (const item of cleanData.backupClean) {\n                totalSize.value += item.size;\n            }\n            cleanData.uploadClean = res.data.uploadClean || [];\n            for (const item of cleanData.uploadClean) {\n                totalSize.value += item.size;\n            }\n            cleanData.downloadClean = res.data.downloadClean || [];\n            for (const item of cleanData.downloadClean) {\n                totalSize.value += item.size;\n            }\n            cleanData.systemLogClean = res.data.systemLogClean || [];\n            for (const item of cleanData.systemLogClean) {\n                totalSize.value += item.size;\n            }\n            cleanData.containerClean = res.data.containerClean || [];\n            for (const item of cleanData.containerClean) {\n                totalSize.value += item.size;\n            }\n            loadCheck(cleanData.systemClean, systemDefaultCheck.value);\n            loadCheck(cleanData.backupClean, backupDefaultCheck.value);\n            loadCheck(cleanData.uploadClean, uploadDefaultCheck.value);\n            loadCheck(cleanData.downloadClean, downloadDefaultCheck.value);\n            loadCheck(cleanData.systemLogClean, systemLogDefaultCheck.value);\n            loadCheck(cleanData.containerClean, containerDefaultCheck.value);\n            scanStatus.value = 'scanned';\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onSubmitClean = async () => {\n    ElMessageBox.confirm(i18n.global.t('clean.cleanHelper'), i18n.global.t('clean.clean'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        submitCleans.value = [];\n        loadSubmitCheck(cleanData.systemClean);\n        loadSubmitCheck(cleanData.backupClean);\n        loadSubmitCheck(cleanData.uploadClean);\n        loadSubmitCheck(cleanData.downloadClean);\n        loadSubmitCheck(cleanData.systemLogClean);\n        loadSubmitCheck(cleanData.containerClean);\n        await clean(submitCleans.value)\n            .then(() => {\n                form.lastCleanSize = selectSize.value + '';\n                form.lastCleanData = submitCleans.value.length + '';\n                scanStatus.value = 'afterScan';\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                loading.value = false;\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst search = async () => {\n    const res = await getSettingInfo();\n    form.lastCleanTime = res.data.lastCleanTime;\n    form.lastCleanSize = res.data.lastCleanSize;\n    form.lastCleanData = res.data.lastCleanData;\n};\n\nconst loadSubmitCheck = (data: any) => {\n    if (data.children === null) {\n        if (data.isCheck) {\n            submitCleans.value.push({ treeType: data.type, name: data.name, size: data.size });\n        }\n        return;\n    }\n    for (const item of data) {\n        if (item.type === 'unknown_backup' && item.isCheck && item.children) {\n            loadSubmitCheck(item.children);\n            continue;\n        }\n        if (item.isCheck && item.type !== 'app_tmp_download') {\n            submitCleans.value.push({ treeType: item.type, name: item.name, size: item.size });\n            continue;\n        }\n        if (item.children) {\n            loadSubmitCheck(item.children);\n        }\n    }\n};\n\nconst changeCheckStatus = (data: any, checked: Array<string>) => {\n    data.isCheck = checked.indexOf(data.id) !== -1;\n    if (data.children) {\n        for (const item of data.children) {\n            changeCheckStatus(item, checked);\n        }\n    }\n};\n\nfunction onChange(data: any, checked: any) {\n    let keys = checked.checkedKeys || [];\n    changeCheckStatus(data, keys);\n    selectSize.value = 0;\n    let systemSelects = systemRef.value.getCheckedNodes(false, true);\n    for (const item of systemSelects) {\n        if (item.children === null) {\n            selectSize.value = selectSize.value + Number(item.size);\n        }\n    }\n    let backupSelects = backupRef.value.getCheckedNodes(false, true);\n    for (const item of backupSelects) {\n        if (item.children === null) {\n            selectSize.value = selectSize.value + Number(item.size);\n        }\n    }\n    let uploadSelects = uploadRef.value.getCheckedNodes(false, true);\n    for (const item of uploadSelects) {\n        if (item.children === null) {\n            selectSize.value = selectSize.value + Number(item.size);\n        }\n    }\n    let downloadSelects = downloadRef.value.getCheckedNodes(false, true);\n    for (const item of downloadSelects) {\n        if (item.children === null) {\n            selectSize.value = selectSize.value + Number(item.size);\n        }\n    }\n    let systemLogSelects = systemLogRef.value.getCheckedNodes(false, true);\n    for (const item of systemLogSelects) {\n        if (item.children === null) {\n            selectSize.value = selectSize.value + Number(item.size);\n        }\n    }\n    let containerSelects = containerRef.value.getCheckedNodes(false, true);\n    for (const item of containerSelects) {\n        if (item.children === null) {\n            selectSize.value = selectSize.value + Number(item.size);\n        }\n    }\n}\n\nfunction loadCheck(data: any, checkList: any) {\n    if (data.children === null) {\n        if (data.isCheck) {\n            checkList.push(data.id);\n        }\n        return;\n    }\n    for (const item of data) {\n        if (item.isCheck) {\n            selectSize.value = selectSize.value + Number(item.size);\n            checkList.push(item.id);\n            continue;\n        }\n        if (item.children) {\n            loadCheck(item.children, checkList);\n        }\n    }\n}\n\nfunction loadTag(node: any, data: any) {\n    if (node.level !== 1) {\n        return '';\n    }\n    if (data.size === 0) {\n        return i18n.global.t('clean.statusClean');\n    }\n    if (data.label === 'container_images') {\n        return i18n.global.t('container.cleanImagesHelper');\n    }\n    if (data.label === 'container_containers') {\n        return i18n.global.t('container.cleanContainersHelper');\n    }\n    if (data.label === 'container_volumes') {\n        return i18n.global.t('container.cleanVolumesHelper');\n    }\n    if (data.label === 'upgrade') {\n        return i18n.global.t('clean.upgradeHelper');\n    }\n    return data.isRecommend ? i18n.global.t('clean.statusSuggest') : i18n.global.t('clean.statusWarning');\n}\n\nfunction load18n(label: string) {\n    switch (label) {\n        case '1panel_original':\n            return i18n.global.t('clean.panelOriginal');\n        case 'upgrade':\n            return i18n.global.t('clean.upgrade');\n        case 'agent_packages':\n            return i18n.global.t('clean.agentPackages');\n        case 'snapshot':\n            return i18n.global.t('clean.snapshot');\n        case 'rollback':\n            return i18n.global.t('clean.rollback');\n        case 'tmp_backup':\n            return i18n.global.t('clean.tmpBackup');\n        case 'unknown_app':\n            return i18n.global.t('clean.unknownApp');\n        case 'unknown_database':\n            return i18n.global.t('clean.unknownDatabase');\n        case 'unknown_website':\n            return i18n.global.t('clean.unknownWebsite');\n        case 'unknown_snapshot':\n            return i18n.global.t('clean.unknownSnapshot');\n        case 'upload':\n        case 'upload_tmp':\n            return i18n.global.t('clean.upload');\n        case 'download':\n            return i18n.global.t('clean.download');\n        case 'upload_website':\n        case 'rollback_website':\n        case 'tmp_backup_website':\n        case 'download_website':\n            return i18n.global.t('menu.website');\n        case 'upload_app':\n        case 'rollback_app':\n        case 'tmp_backup_app':\n        case 'download_app':\n            return i18n.global.t('app.app');\n        case 'upload_database':\n        case 'rollback_database':\n        case 'tmp_backup_database':\n        case 'download_database':\n            return i18n.global.t('menu.database');\n        case 'upload_directory':\n        case 'tmp_backup_directory':\n        case 'download_directory':\n            return i18n.global.t('clean.directory');\n        case 'system_log':\n            return i18n.global.t('logs.system');\n        case 'tmp_backup_log':\n            return i18n.global.t('clean.systemLog');\n        case 'script':\n            return i18n.global.t('cronjob.library.script');\n        case 'docker_log':\n            return i18n.global.t('clean.dockerLog');\n        case 'task_log':\n            return i18n.global.t('logs.task');\n        case 'Database':\n        case 'Cronjob':\n        case 'Container':\n        case 'App':\n        case 'System':\n        case 'Website':\n            return i18n.global.t('menu.' + label.toLowerCase());\n        case 'Backup':\n            return i18n.global.t('logs.detail.backups');\n        case 'Tamper':\n            return i18n.global.t('xpack.tamper.tamper');\n        case 'TaskRollback':\n            return i18n.global.t('setting.rollback');\n        case 'Script':\n            return i18n.global.t('cronjob.library.library');\n        case 'ScriptLibrary':\n            return i18n.global.t('cronjob.library.remoteLibrary');\n        case 'Compose':\n            return i18n.global.t('container.compose');\n        case 'CustomAppstore':\n            return i18n.global.t('xpack.customApp.name');\n        case 'NodeFile':\n            return i18n.global.t('xpack.exchange.exchange');\n        case 'RuntimeExtension':\n            return i18n.global.t('website.runtime');\n        case 'Image':\n            return i18n.global.t('container.image');\n        case 'Snapshot':\n        case 'tmp_backup_snapshot':\n            return i18n.global.t('setting.snapshot');\n        case 'AppStore':\n            return i18n.global.t('menu.apps');\n        case 'AITools':\n            return i18n.global.t('menu.aiTools');\n        case 'shell':\n            return i18n.global.t('clean.shell');\n        case 'containerShell':\n            return i18n.global.t('clean.containerShell');\n        case 'curl':\n            return i18n.global.t('clean.curl');\n        case 'container_images':\n            return i18n.global.t('container.image');\n        case 'container_containers':\n            return i18n.global.t('menu.container');\n        case 'container_volumes':\n            return i18n.global.t('clean.volumes');\n        case 'build_cache':\n            return i18n.global.t('clean.buildCache');\n        case 'website_log':\n            return i18n.global.t('logs.websiteLog');\n        case 'app_tmp_download':\n            return i18n.global.t('clean.appTmpDownload');\n        case 'unknown_website_log':\n            return i18n.global.t('clean.unknownWebsiteLog');\n        default:\n            return label;\n    }\n}\n\nonMounted(() => {\n    search();\n    scanStatus.value = 'beforeScan';\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.clean-card {\n    margin-top: 20px;\n    cursor: pointer;\n    border: var(--panel-border) !important;\n\n    &:hover {\n        cursor: pointer;\n        border: 1px solid var(--el-color-primary) !important;\n    }\n}\n\n.card_icon {\n    font-size: 36px;\n    float: right;\n    margin-right: 15px;\n}\n\n.card_title {\n    font-size: 18px;\n}\n\n.clean_title {\n    font-size: 22px;\n}\n\n.large_button {\n    float: right;\n    margin-top: -40px;\n}\n\n.svg-icon {\n    font-size: 14px;\n    float: right;\n    margin-right: 5px;\n}\n\n.tree-container {\n    width: 100%;\n    display: flex;\n    flex-direction: column;\n    height: 100%;\n    overflow: auto;\n}\n.tree-collapse {\n    flex: 1;\n    overflow: hidden;\n}\n.responsive-tree {\n    width: 100%;\n    max-height: 100%;\n    overflow: auto;\n\n    :deep(.el-tree-node .el-tree-node__content) {\n        width: fit-content;\n        overflow: auto;\n        min-width: 300px;\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/device/dns/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" header=\"DNS\" @close=\"handleClose\" size=\"large\">\n        <el-alert :title=\"$t('toolbox.device.dnsAlert')\" class=\"common-prompt\" :closable=\"false\" type=\"warning\" />\n        <el-radio-group v-model=\"confShowType\" @change=\"changeMode\">\n            <el-radio-button value=\"form\">{{ $t('database.baseConf') }}</el-radio-button>\n            <el-radio-button value=\"all\">{{ $t('database.allConf') }}</el-radio-button>\n        </el-radio-group>\n        <el-form\n            class=\"mt-4\"\n            v-if=\"confShowType === 'form'\"\n            ref=\"formRef\"\n            label-position=\"top\"\n            @submit.prevent\n            :model=\"form\"\n            v-loading=\"loading\"\n        >\n            <el-form-item label=\"DNS\" prop=\"dns\">\n                <el-input type=\"textarea\" :placeholder=\"$t('toolbox.device.dnsHelper1')\" :rows=\"8\" v-model=\"form.dns\" />\n            </el-form-item>\n        </el-form>\n        <div v-else>\n            <CodemirrorPro\n                placeholder=\"# The DNS configuration file does not exist or is empty (/etc/resolv.conf)\"\n                v-model=\"dnsConf\"\n                :heightDiff=\"260\"\n            ></CodemirrorPro>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" @click=\"onTest()\">\n                    {{ $t('toolbox.device.dnsCheck') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave()\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { loadDeviceConf, checkDNS, updateDevice, updateDeviceByConf } from '@/api/modules/toolbox';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst confShowType = ref('form');\nconst dnsConf = ref();\n\nconst form = reactive({\n    dns: '',\n});\n\ninterface DialogProps {\n    dns: Array<string>;\n}\n\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.dns = params.dns ? params.dns.join('\\n') : '';\n    drawerVisible.value = true;\n};\n\nconst loadDNSConf = async () => {\n    const res = await loadDeviceConf('DNS');\n    dnsConf.value = res.data || '';\n};\n\nconst changeMode = async () => {\n    if (confShowType.value === 'all') {\n        loadDNSConf();\n    }\n};\n\nconst onTest = async () => {\n    loading.value = true;\n    let value = '';\n    if (confShowType.value === 'form') {\n        value = form.dns.replaceAll('\\n', ',');\n    } else {\n        value = dnsConf.value;\n    }\n    await checkDNS(confShowType.value, value)\n        .then((res) => {\n            loading.value = false;\n            if (res.data) {\n                MsgSuccess(i18n.global.t('toolbox.device.dnsOK'));\n            } else {\n                MsgError(i18n.global.t('toolbox.device.dnsTestFailed'));\n            }\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onSave = async () => {\n    loading.value = true;\n    if (confShowType.value == 'form') {\n        await updateDevice('DNS', form.dns.replaceAll('\\n', ','))\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                handleClose();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n        return;\n    }\n    await updateDeviceByConf('DNS', dnsConf.value)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            emit('search');\n            handleClose();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/device/hostname/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.device.hostname')\" @close=\"handleClose\" size=\"small\">\n        <el-alert :title=\"$t('toolbox.device.hostnameHelper')\" class=\"common-prompt\" :closable=\"false\" type=\"warning\" />\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.device.hostname')\" prop=\"hostname\" :rules=\"Rules.requiredInput\">\n                <el-input clearable v-model.trim=\"form.hostname\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveHostname(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { updateDevice } from '@/api/modules/toolbox';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    hostname: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    hostname: '',\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.hostname = params.hostname;\n    drawerVisible.value = true;\n};\n\nconst onSaveHostname = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('toolbox.device.hostname'), form.hostname]),\n            i18n.global.t('database.confChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            loading.value = true;\n            await updateDevice('Hostname', form.hostname)\n                .then(async () => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/device/hosts/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" header=\"Hosts\" @close=\"handleClose\" size=\"large\">\n        <el-radio-group v-model=\"confShowType\" @change=\"changeMode\">\n            <el-radio-button value=\"base\">{{ $t('database.baseConf') }}</el-radio-button>\n            <el-radio-button value=\"all\">{{ $t('database.allConf') }}</el-radio-button>\n        </el-radio-group>\n        <div v-if=\"confShowType === 'base'\">\n            <el-table :data=\"form.hosts\">\n                <el-table-column label=\"IP\" min-width=\"60\">\n                    <template #default=\"{ row }\">\n                        <el-input placeholder=\"172.16.10.111\" v-model=\"row.ip\" />\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('toolbox.device.hosts')\" min-width=\"150\">\n                    <template #default=\"{ row }\">\n                        <el-input placeholder=\"test.hostname.com\" v-model=\"row.host\" />\n                    </template>\n                </el-table-column>\n                <el-table-column min-width=\"30\">\n                    <template #default=\"scope\">\n                        <el-button link type=\"primary\" @click=\"handleHostsDelete(scope.$index)\">\n                            {{ $t('commons.button.delete') }}\n                        </el-button>\n                    </template>\n                </el-table-column>\n            </el-table>\n            <el-button class=\"ml-3 mt-2\" @click=\"handleHostsAdd()\">\n                {{ $t('commons.button.add') }}\n            </el-button>\n        </div>\n        <div v-else>\n            <CodemirrorPro\n                class=\"mt-5\"\n                placeholder=\"# The hosts configuration file does not exist or is empty (/etc/hosts)\"\n                v-model=\"hostsConf\"\n                :heightDiff=\"300\"\n            ></CodemirrorPro>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave()\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { loadDeviceConf, updateDeviceByConf, updateDeviceHost } from '@/api/modules/toolbox';\nimport { Toolbox } from '@/api/interface/toolbox';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst confShowType = ref('base');\nconst hostsConf = ref();\n\nconst form = reactive({\n    hosts: [],\n});\n\ninterface DialogProps {\n    hosts: Array<Toolbox.HostHelper>;\n}\n\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst acceptParams = (params: DialogProps): void => {\n    confShowType.value = 'base';\n    form.hosts = params.hosts;\n    drawerVisible.value = true;\n};\n\nconst loadHostsConf = async () => {\n    const res = await loadDeviceConf('Hosts');\n    hostsConf.value = res.data || '';\n};\n\nconst changeMode = async () => {\n    if (confShowType.value === 'all') {\n        loadHostsConf();\n    }\n};\n\nconst handleHostsAdd = () => {\n    let item = {\n        ip: '',\n        host: '',\n    };\n    form.hosts.push(item);\n};\nconst handleHostsDelete = (index: number) => {\n    form.hosts.splice(index, 1);\n};\n\nconst onSave = async () => {\n    loading.value = true;\n    if (confShowType.value === 'base') {\n        await updateDeviceHost(form.hosts)\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                emit('search');\n                handleClose();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n        return;\n    }\n    for (const item of form.hosts) {\n        if (item.ip === '' || item.host === '') {\n            MsgError(i18n.global.t('toolbox.device.hostHelper'));\n            return;\n        }\n    }\n    await updateDeviceByConf('Hosts', hostsConf.value)\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            emit('search');\n            handleClose();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/device/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <LayoutContent :title=\"$t('toolbox.device.toolbox')\" :divider=\"true\">\n            <template #main>\n                <el-row style=\"margin-top: 20px\">\n                    <el-col :span=\"1\"><br /></el-col>\n                    <el-col :xs=\"24\" :sm=\"20\" :md=\"20\" :lg=\"10\" :xl=\"10\">\n                        <el-form\n                            :model=\"form\"\n                            :label-position=\"mobile ? 'top' : 'left'\"\n                            ref=\"formRef\"\n                            label-width=\"130px\"\n                        >\n                            <el-form-item label=\"DNS\" prop=\"dnsItem\">\n                                <el-input disabled v-model=\"form.dnsItem\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeDNS\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item label=\"Hosts\" prop=\"hosts\">\n                                <el-input disabled v-model=\"form.hostItem\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeHost\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item label=\"Swap\" prop=\"swap\">\n                                <el-input disabled v-model=\"form.swapItem\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeSwap\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('toolbox.device.hostname')\" prop=\"hostname\">\n                                <el-input disabled v-model=\"form.hostname\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeHostname\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('toolbox.device.passwd')\" prop=\"passwd\">\n                                <el-input disabled v-model=\"form.passwd\" type=\"password\">\n                                    <template #append>\n                                        <el-button @click=\"onChangePasswd\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('toolbox.device.syncSite')\" prop=\"ntp\">\n                                <el-input disabled v-model=\"form.ntp\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeNtp\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('toolbox.device.timeZone')\" prop=\"timeZone\">\n                                <el-input disabled v-model=\"form.timeZone\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeTimeZone\" icon=\"Setting\">\n                                            {{ $t('commons.button.set') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                            <el-form-item :label=\"$t('toolbox.device.localTime')\" prop=\"localTime\">\n                                <el-input disabled v-model=\"form.localTime\">\n                                    <template #append>\n                                        <el-button @click=\"onChangeLocalTime\" icon=\"Refresh\" width=\"150px\">\n                                            {{ $t('commons.button.sync') }}\n                                        </el-button>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                        </el-form>\n                    </el-col>\n                </el-row>\n            </template>\n        </LayoutContent>\n\n        <Swap ref=\"swapRef\" @search=\"search\" />\n        <Passwd ref=\"passwdRef\" @search=\"search\" />\n        <TimeZone ref=\"timeZoneRef\" @search=\"search\" />\n        <Ntp ref=\"ntpRef\" @search=\"search\" />\n        <DNS ref=\"dnsRef\" @search=\"search\" />\n        <Hostname ref=\"hostnameRef\" @search=\"search\" />\n        <Hosts ref=\"hostsRef\" @search=\"search\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref, computed } from 'vue';\nimport Swap from '@/views/toolbox/device/swap/index.vue';\nimport Passwd from '@/views/toolbox/device/passwd/index.vue';\nimport TimeZone from '@/views/toolbox/device/time-zone/index.vue';\nimport Ntp from '@/views/toolbox/device/ntp/index.vue';\nimport DNS from '@/views/toolbox/device/dns/index.vue';\nimport Hostname from '@/views/toolbox/device/hostname/index.vue';\nimport Hosts from '@/views/toolbox/device/hosts/index.vue';\nimport { getDeviceBase, updateDevice } from '@/api/modules/toolbox';\nimport i18n from '@/lang';\nimport { computeSize } from '@/utils/util';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst loading = ref(false);\n\nconst swapRef = ref();\nconst timeZoneRef = ref();\nconst ntpRef = ref();\nconst passwdRef = ref();\nconst dnsRef = ref();\nconst hostnameRef = ref();\nconst hostsRef = ref();\n\nconst form = reactive({\n    dns: [],\n    dnsItem: '',\n    hosts: [],\n    hostItem: '',\n    hostname: '',\n    user: '',\n    passwd: '******',\n    timeZone: '',\n    localTime: '',\n    ntp: '',\n\n    swapItem: '',\n});\n\nconst onChangeTimeZone = () => {\n    timeZoneRef.value.acceptParams({ timeZone: form.timeZone });\n};\nconst onChangeNtp = () => {\n    ntpRef.value.acceptParams({ ntpSite: form.ntp });\n};\nconst onChangeLocalTime = async () => {\n    loading.value = true;\n    await updateDevice('LocalTime', '')\n        .then(() => {\n            loading.value = false;\n            search();\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\nconst onChangePasswd = () => {\n    passwdRef.value.acceptParams({ user: form.user });\n};\nconst onChangeDNS = () => {\n    dnsRef.value.acceptParams({ dns: form.dns });\n};\nconst onChangeHostname = () => {\n    hostnameRef.value.acceptParams({ hostname: form.hostname });\n};\nconst onChangeHost = () => {\n    hostsRef.value.acceptParams({ hosts: form.hosts });\n};\nconst onChangeSwap = () => {\n    swapRef.value.acceptParams();\n};\n\nconst search = async () => {\n    const res = await getDeviceBase();\n    form.timeZone = res.data.timeZone;\n    form.localTime = res.data.localTime;\n    form.hostname = res.data.hostname;\n    form.ntp = res.data.ntp;\n    form.user = res.data.user;\n    form.dns = res.data.dns || [];\n    form.dnsItem = form.dns ? i18n.global.t('toolbox.device.dnsHelper') : i18n.global.t('setting.unSetting');\n    form.hosts = res.data.hosts || [];\n    form.hostItem = form.hosts ? i18n.global.t('toolbox.device.hostsHelper') : i18n.global.t('setting.unSetting');\n\n    form.swapItem = res.data.swapMemoryTotal\n        ? computeSize(res.data.swapMemoryTotal)\n        : i18n.global.t('setting.unSetting');\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/device/ntp/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.device.syncSite')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.device.syncSite')\" prop=\"ntpSite\" :rules=\"Rules.domain\">\n                <el-input v-model=\"form.ntpSite\" />\n                <el-button type=\"primary\" link class=\"tagClass\" @click=\"form.ntpSite = 'pool.ntp.org'\">\n                    {{ $t('commons.table.default') }}\n                </el-button>\n                <el-button type=\"primary\" link class=\"tagClass\" @click=\"form.ntpSite = 'ntp.aliyun.com'\">\n                    {{ $t('toolbox.device.ntpALi') }}\n                </el-button>\n                <el-button type=\"primary\" link class=\"tagClass\" @click=\"form.ntpSite = 'time.google.com'\">\n                    {{ $t('toolbox.device.ntpGoogle') }}\n                </el-button>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSyncTime(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { updateDevice } from '@/api/modules/toolbox';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    ntpSite: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    ntpSite: '',\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.ntpSite = params.ntpSite;\n    drawerVisible.value = true;\n};\n\nconst onSyncTime = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('toolbox.device.syncSiteHelper', [form.ntpSite]),\n            i18n.global.t('toolbox.device.syncSite'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            loading.value = true;\n            await updateDevice('Ntp', form.ntpSite)\n                .then(() => {\n                    loading.value = false;\n                    emit('search');\n                    handleClose();\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.tagClass {\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/device/passwd/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"passwordVisible\" :header=\"$t('setting.changePassword')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"passRules\" v-loading=\"loading\">\n            <el-alert\n                :title=\"$t('toolbox.device.passwordHelper')\"\n                class=\"common-prompt\"\n                :closable=\"false\"\n                type=\"warning\"\n            />\n            <el-form-item :label=\"$t('setting.user')\" prop=\"user\">\n                <el-input clearable v-model.trim=\"form.user\" />\n                <span class=\"input-help\">{{ $t('toolbox.device.userHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.newPassword')\" prop=\"newPassword\">\n                <el-input type=\"password\" show-password clearable v-model.trim=\"form.newPassword\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('setting.retryPassword')\" prop=\"retryPassword\">\n                <el-input type=\"password\" show-password clearable v-model.trim=\"form.retryPassword\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button :disabled=\"loading\" @click=\"passwordVisible = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"submitChangePassword(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport { updateDevicePasswd } from '@/api/modules/toolbox';\n\nconst formRef = ref<FormInstance>();\nconst passRules = reactive({\n    user: Rules.requiredInput,\n    newPassword: [Rules.requiredInput, Rules.noSpace, { validator: checkPassword, trigger: 'blur' }],\n    retryPassword: [Rules.requiredInput, Rules.noSpace, { validator: checkRePassword, trigger: 'blur' }],\n});\n\nconst loading = ref(false);\nconst passwordVisible = ref<boolean>(false);\nconst form = reactive({\n    user: '',\n    newPassword: '',\n    retryPassword: '',\n});\n\ninterface DialogProps {\n    user: string;\n}\nconst acceptParams = (params: DialogProps): void => {\n    form.user = params.user;\n    form.newPassword = '';\n    form.retryPassword = '';\n    passwordVisible.value = true;\n};\n\nfunction checkPassword(rule: any, value: any, callback: any) {\n    if (form.newPassword.indexOf('&') !== -1 || form.newPassword.indexOf('$') !== -1) {\n        return callback(new Error(i18n.global.t('toolbox.device.passwdHelper')));\n    }\n    callback();\n}\n\nfunction checkRePassword(rule: any, value: any, callback: any) {\n    if (form.newPassword !== form.retryPassword) {\n        return callback(new Error(i18n.global.t('commons.rule.rePassword')));\n    }\n    callback();\n}\n\nconst submitChangePassword = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        loading.value = true;\n        await updateDevicePasswd(form.user, form.newPassword)\n            .then(async () => {\n                loading.value = false;\n                passwordVisible.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst handleClose = () => {\n    passwordVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/device/swap/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" header=\"Swap\" @close=\"handleClose\" size=\"large\">\n        <el-alert class=\"common-prompt\" :closable=\"false\" type=\"warning\">\n            <template #default>\n                <ul style=\"margin-left: -20px\">\n                    <li>{{ $t('toolbox.swap.swapHelper1') }}</li>\n                    <li>{{ $t('toolbox.swap.swapHelper2') }}</li>\n                    <li>{{ $t('toolbox.swap.swapHelper3') }}</li>\n                    <li>{{ $t('toolbox.swap.swapHelper4') }}</li>\n                </ul>\n            </template>\n        </el-alert>\n        <el-card>\n            <el-form label-position=\"top\" class=\"ml-3\" v-loading=\"loading\">\n                <el-row type=\"flex\" justify=\"center\" :gutter=\"20\">\n                    <el-col :xs=\"8\" :sm=\"8\" :md=\"8\" :lg=\"8\" :xl=\"8\">\n                        <el-form-item>\n                            <template #label>\n                                <span class=\"status-label\">Swap {{ $t('home.total') }}</span>\n                            </template>\n                            <span class=\"status-count\">{{ form.swapMemoryTotal }}</span>\n                        </el-form-item>\n                    </el-col>\n                    <el-col :xs=\"8\" :sm=\"8\" :md=\"8\" :lg=\"8\" :xl=\"8\">\n                        <el-form-item>\n                            <template #label>\n                                <span class=\"status-label\">Swap {{ $t('home.used') }}</span>\n                            </template>\n                            <span class=\"status-count\">{{ form.swapMemoryUsed }}</span>\n                        </el-form-item>\n                    </el-col>\n                    <el-col :xs=\"8\" :sm=\"8\" :md=\"8\" :lg=\"8\" :xl=\"8\">\n                        <el-form-item>\n                            <template #label>\n                                <span class=\"status-label\">Swap {{ $t('home.free') }}</span>\n                            </template>\n                            <span class=\"status-count\">{{ form.swapMemoryAvailable }}</span>\n                        </el-form-item>\n                    </el-col>\n                </el-row>\n            </el-form>\n        </el-card>\n\n        <el-table :data=\"form.swapDetails\" class=\"mt-5\">\n            <el-table-column :label=\"$t('file.path')\" min-width=\"120\" prop=\"path\">\n                <template #default=\"{ row }\">\n                    <span>{{ row.path }}</span>\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('file.size')\" min-width=\"150\">\n                <template #default=\"{ row }\">\n                    <el-input placeholder=\"1024\" type=\"number\" v-model.number=\"row.size\">\n                        <template #append>\n                            <el-select v-model=\"row.sizeUnit\" style=\"width: 85px\">\n                                <el-option label=\"KB\" value=\"KB\" />\n                                <el-option label=\"MB\" value=\"MB\" />\n                                <el-option label=\"GB\" value=\"GB\" />\n                            </el-select>\n                        </template>\n                    </el-input>\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('home.used')\" min-width=\"70\" prop=\"used\">\n                <template #default=\"{ row }\">\n                    <span v-if=\"row.used !== '-'\">{{ computeSize(row.used * 1024) }}</span>\n                </template>\n            </el-table-column>\n            <el-table-column min-width=\"70\">\n                <template #default=\"scope\">\n                    <el-button link type=\"primary\" @click=\"onSave(scope.row)\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </template>\n            </el-table-column>\n        </el-table>\n        <span class=\"input-help\">{{ $t('toolbox.swap.swapOff') }}</span>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"search\" />\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { updateDeviceSwap, getDeviceBase } from '@/api/modules/toolbox';\nimport { computeSize, newUUID, splitSize } from '@/utils/util';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { loadBaseDir } from '@/api/modules/setting';\n\nconst form = reactive({\n    swapMemoryTotal: '',\n    swapMemoryAvailable: '',\n    swapMemoryUsed: '',\n    maxSize: 0,\n\n    swapDetails: [],\n});\n\nconst drawerVisible = ref();\nconst loading = ref();\nconst taskLogRef = ref();\n\nconst acceptParams = (): void => {\n    search();\n    drawerVisible.value = true;\n};\n\nconst search = async () => {\n    loading.value = true;\n    const res = await getDeviceBase();\n    form.swapMemoryTotal = computeSize(res.data.swapMemoryTotal);\n    form.swapMemoryUsed = computeSize(res.data.swapMemoryUsed);\n    form.swapMemoryAvailable = computeSize(res.data.swapMemoryAvailable);\n    form.swapDetails = res.data.swapDetails || [];\n    form.maxSize = res.data.maxSize;\n\n    await loadBaseDir()\n        .then((res) => {\n            loading.value = false;\n            loadData(res.data.substring(0, res.data.lastIndexOf('/1panel')));\n        })\n        .catch(() => {\n            loading.value = false;\n            loadData('');\n        });\n};\n\nconst loadData = (path: string) => {\n    let isExist = false;\n    for (const item of form.swapDetails) {\n        if (item.path === path + '/.1panel_swap') {\n            isExist = true;\n        }\n        let itemSize = splitSize(item.size * 1024);\n        item.size = itemSize.size;\n        item.sizeUnit = itemSize.unit;\n    }\n    if (!isExist) {\n        form.swapDetails.push({\n            path: path + '/.1panel_swap',\n            size: 0,\n            used: 0,\n            isNew: true,\n            sizeUnit: 'MB',\n        });\n    }\n};\n\nconst onSave = async (row) => {\n    if (row.size === '') {\n        MsgError(i18n.global.t('commons.msg.confirmNoNull', ['Swap ' + i18n.global.t('file.size')]));\n        return;\n    }\n    const itemSize = loadItemSize(row);\n    if (itemSize < 40 && row.size !== 0) {\n        MsgError(i18n.global.t('toolbox.swap.swapMin'));\n        return;\n    }\n    if (itemSize * 1024 > form.maxSize) {\n        MsgError(i18n.global.t('toolbox.swap.swapMax', [computeSize(form.maxSize)]));\n        return;\n    }\n    ElMessageBox.confirm(\n        i18n.global.t('toolbox.swap.saveSwap', [row.path, row.size + ' ' + row.sizeUnit]),\n        i18n.global.t('commons.button.save'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(async () => {\n        let params = {\n            path: row.path,\n            size: itemSize,\n            used: '0',\n\n            isNew: row.isNew,\n            taskID: newUUID(),\n        };\n        loading.value = true;\n        await updateDeviceSwap(params)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(params.taskID);\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst loadItemSize = (row: any) => {\n    switch (row.sizeUnit) {\n        case 'KB':\n            return row.size;\n        case 'MB':\n            return row.size * 1024;\n        case 'GB':\n            return row.size * 1024 * 1024;\n    }\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/device/time-zone/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.device.timeZone')\" @close=\"handleClose\" size=\"small\">\n        <el-alert :title=\"$t('toolbox.device.timeZoneHelper')\" class=\"common-prompt\" :closable=\"false\" type=\"warning\" />\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.device.timeZone')\" prop=\"timeZone\" :rules=\"Rules.requiredInput\">\n                <el-select filterable :disabled=\"canChangeZone()\" v-model=\"form.timeZone\">\n                    <el-option v-for=\"item in zones\" :key=\"item\" :label=\"item\" :value=\"item\" />\n                </el-select>\n                <el-button\n                    :disabled=\"canChangeZone()\"\n                    type=\"primary\"\n                    link\n                    class=\"tagClass\"\n                    @click=\"form.timeZone = 'Asia/Shanghai'\"\n                >\n                    {{ $t('toolbox.device.timeZoneCN') }}\n                </el-button>\n                <el-button\n                    :disabled=\"canChangeZone()\"\n                    type=\"primary\"\n                    link\n                    class=\"tagClass\"\n                    @click=\"form.timeZone = 'America/Los_Angeles'\"\n                >\n                    {{ $t('toolbox.device.timeZoneAM') }}\n                </el-button>\n                <el-button\n                    :disabled=\"canChangeZone()\"\n                    type=\"primary\"\n                    link\n                    class=\"tagClass\"\n                    @click=\"form.timeZone = 'America/New_York'\"\n                >\n                    {{ $t('toolbox.device.timeZoneNY') }}\n                </el-button>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { GlobalStore } from '@/store';\nimport { loadTimeZoneOptions, updateDevice } from '@/api/modules/toolbox';\nimport { changeToLocal } from '@/utils/node';\nconst globalStore = GlobalStore();\n\ninterface DialogProps {\n    timeZone: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    timeZone: '',\n});\n\nconst formRef = ref<FormInstance>();\nconst zones = ref<Array<string>>([]);\n\nconst acceptParams = (params: DialogProps): void => {\n    loadTimeZones();\n    form.timeZone = params.timeZone;\n    drawerVisible.value = true;\n};\n\nconst loadTimeZones = async () => {\n    const res = await loadTimeZoneOptions();\n    zones.value = res.data;\n};\n\nconst canChangeZone = () => {\n    return zones.value.length === 0;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('toolbox.device.timeZoneChangeHelper'),\n            i18n.global.t('toolbox.device.timeZone'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            await updateDevice('TimeZone', form.timeZone)\n                .then(async () => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    if (globalStore.currentNode === 'local') {\n                        globalStore.isLogin = false;\n                        let href = window.location.href;\n                        window.open(href, '_self');\n                    } else {\n                        changeToLocal();\n                        location.reload();\n                    }\n                })\n                .catch(() => {\n                    loading.value = false;\n                    let href = window.location.href;\n                    window.open(href, '_self');\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style scoped lang=\"scss\">\n.tagClass {\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/fail2ban/ban-action/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.fail2ban.banAction')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.fail2ban.banAction')\" prop=\"banAction\" :rules=\"Rules.requiredSelect\">\n                <el-select v-model=\"form.banAction\">\n                    <el-option value=\"iptables-allports\" class=\"option\">\n                        <span class=\"option-content\">iptables-allports</span>\n                        <span class=\"input-help option-helper\">\n                            {{ $t('toolbox.fail2ban.banActionOption', ['iptables']) + $t('toolbox.fail2ban.allPorts') }}\n                        </span>\n                    </el-option>\n                    <el-option value=\"iptables-multiport\" class=\"option\">\n                        <span class=\"option-content\">iptables-multiport</span>\n                        <span class=\"input-help option-helper\">\n                            {{ $t('toolbox.fail2ban.banActionOption', ['iptables']) }}\n                        </span>\n                    </el-option>\n                    <el-option value=\"firewallcmd-ipset\" class=\"option\">\n                        <span class=\"option-content\">firewallcmd-ipset</span>\n                        <span class=\"input-help option-helper\">\n                            {{ $t('toolbox.fail2ban.banActionOption', ['firewallcmd ipset']) }}\n                        </span>\n                    </el-option>\n                    <el-option value=\"ufw\" class=\"option\">\n                        <span class=\"option-content\">ufw</span>\n                        <span class=\"input-help option-helper\">\n                            {{ $t('toolbox.fail2ban.banActionOption', ['ufw']) }}\n                        </span>\n                    </el-option>\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { updateFail2ban } from '@/api/modules/toolbox';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    banAction: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    banAction: '',\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.banAction = params.banAction;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('toolbox.fail2ban.banAction'), form.banAction]),\n            i18n.global.t('toolbox.fail2ban.fail2banChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            await updateFail2ban({ key: 'banaction', value: form.banAction })\n                .then(async () => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.option {\n    height: 50px;\n}\n.option-content {\n    float: left;\n    position: relative;\n    display: block;\n}\n.option-helper {\n    float: left;\n    display: block;\n    position: absolute;\n    margin-top: 20px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/fail2ban/ban-time/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.fail2ban.banTime')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" :rules=\"rules\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.fail2ban.banTime')\" prop=\"banTime\">\n                <el-input type=\"number\" v-model.number=\"form.banTime\">\n                    <template #append>\n                        <el-select v-model.number=\"form.banTimeUnit\" style=\"width: 100px\">\n                            <el-option :label=\"$t('commons.units.second')\" value=\"s\" />\n                            <el-option :label=\"$t('commons.units.minute')\" value=\"m\" />\n                            <el-option :label=\"$t('commons.units.hour')\" value=\"h\" />\n                            <el-option :label=\"$t('commons.units.day')\" value=\"d\" />\n                            <el-option :label=\"$t('commons.units.year')\" value=\"y\" />\n                        </el-select>\n                    </template>\n                </el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { updateFail2ban } from '@/api/modules/toolbox';\nimport { splitTime, transTimeUnit } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    banTime: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    banTime: 300,\n    banTimeUnit: 's',\n});\n\nconst rules = reactive({\n    banTime: [{ validator: checkBanTime, trigger: 'blur' }],\n});\nfunction checkBanTime(rule: any, value: any, callback: any) {\n    if (value === -1) {\n        callback();\n    }\n    const reg = /^[1-9]\\d*$/;\n    if (!reg.test(value)) {\n        return callback(new Error(i18n.global.t('toolbox.fail2ban.banTimeRule')));\n    }\n    callback();\n}\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    let item = splitTime(params.banTime);\n    form.banTime = item.time;\n    form.banTimeUnit = item.unit;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let itemMsg =\n            form.banTime === -1\n                ? i18n.global.t('toolbox.fail2ban.banAllTime')\n                : form.banTime + transTimeUnit(form.banTimeUnit);\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('toolbox.fail2ban.banTime'), itemMsg]),\n            i18n.global.t('toolbox.fail2ban.fail2banChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            let itemValue = form.banTime === -1 ? '-1' : form.banTime + form.banTimeUnit;\n            await updateFail2ban({ key: 'bantime', value: itemValue })\n                .then(async () => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/fail2ban/find-time/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.fail2ban.findTime')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.fail2ban.findTime')\" prop=\"findTime\" :rules=\"Rules.integerNumber\">\n                <el-input clearable v-model.number=\"form.findTime\">\n                    <template #append>\n                        <el-select v-model=\"form.findTimeUnit\" style=\"width: 100px\">\n                            <el-option :label=\"$t('commons.units.second')\" value=\"s\" />\n                            <el-option :label=\"$t('commons.units.minute')\" value=\"m\" />\n                            <el-option :label=\"$t('commons.units.hour')\" value=\"h\" />\n                            <el-option :label=\"$t('commons.units.day')\" value=\"d\" />\n                            <el-option :label=\"$t('commons.units.year')\" value=\"y\" />\n                        </el-select>\n                    </template>\n                </el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { updateFail2ban } from '@/api/modules/toolbox';\nimport { splitTime, transTimeUnit } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    findTime: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    findTime: 300,\n    findTimeUnit: 's',\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    let item = splitTime(params.findTime);\n    form.findTime = item.time;\n    form.findTimeUnit = item.unit;\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [\n                i18n.global.t('toolbox.fail2ban.findTime'),\n                form.findTime + transTimeUnit(form.findTimeUnit),\n            ]),\n            i18n.global.t('toolbox.fail2ban.fail2banChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            await updateFail2ban({ key: 'findtime', value: form.findTime + form.findTimeUnit })\n                .then(async () => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/fail2ban/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <div class=\"app-status card-interval\">\n            <el-card v-if=\"form.isExist\">\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag effect=\"dark\" type=\"success\">Fail2ban</el-tag>\n                        <Status class=\"mt-0.5\" :status=\"form.isActive ? 'enable' : 'disable'\" />\n                        <el-tag>{{ form.version }}</el-tag>\n                    </div>\n                    <div class=\"mt-0.5\">\n                        <el-button v-if=\"form.isActive\" type=\"primary\" @click=\"onOperate('stop')\" link>\n                            {{ $t('commons.button.stop') }}\n                        </el-button>\n                        <el-button v-if=\"!form.isActive\" type=\"primary\" @click=\"onOperate('start')\" link>\n                            {{ $t('commons.button.start') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" @click=\"onOperate('restart')\" link>\n                            {{ $t('commons.button.restart') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" link>\n                            {{ $t('ssh.autoStart') }}\n                        </el-button>\n                        <el-switch\n                            size=\"small\"\n                            class=\"ml-2\"\n                            inactive-value=\"disable\"\n                            active-value=\"enable\"\n                            @change=\"onOperate(autoStart)\"\n                            v-model=\"autoStart\"\n                        />\n                    </div>\n                </div>\n            </el-card>\n        </div>\n\n        <div v-if=\"form.isExist\">\n            <LayoutContent title=\"Fail2ban\" :divider=\"true\">\n                <template #promp v-if=\"!globalStore.isFxplay\">\n                    <el-alert type=\"info\" :closable=\"false\">\n                        <template #title>\n                            {{ $t('toolbox.common.toolboxHelper') }}\n                            <el-link class=\"ml-1 text-xs\" @click=\"toDoc()\" type=\"primary\">\n                                {{ $t('commons.button.helpDoc') }}\n                            </el-link>\n                        </template>\n                    </el-alert>\n                </template>\n                <template #leftToolBar>\n                    <el-button :disabled=\"!form.isActive\" type=\"primary\" plain @click=\"onLoadList('ignore')\">\n                        {{ $t('toolbox.fail2ban.ignoreIP') }}\n                    </el-button>\n                    <el-button :disabled=\"!form.isActive\" type=\"primary\" plain @click=\"onLoadList('banned')\">\n                        {{ $t('toolbox.fail2ban.bannedIP') }}\n                    </el-button>\n                </template>\n                <template #main>\n                    <el-radio-group v-model=\"confShowType\" @change=\"changeMode\">\n                        <el-radio-button value=\"base\">{{ $t('database.baseConf') }}</el-radio-button>\n                        <el-radio-button value=\"all\">{{ $t('database.allConf') }}</el-radio-button>\n                    </el-radio-group>\n                    <el-row style=\"margin-top: 20px\" v-if=\"confShowType === 'base'\">\n                        <el-col :span=\"1\"><br /></el-col>\n                        <el-col :xs=\"24\" :sm=\"20\" :md=\"20\" :lg=\"10\" :xl=\"10\">\n                            <el-form :model=\"form\" label-position=\"left\" ref=\"formRef\" label-width=\"120px\">\n                                <el-form-item :label=\"$t('toolbox.fail2ban.sshPort')\" prop=\"port\">\n                                    <el-input disabled v-model=\"form.port\">\n                                        <template #append>\n                                            <el-button @click=\"onChangePort\" icon=\"Setting\">\n                                                {{ $t('commons.button.set') }}\n                                            </el-button>\n                                        </template>\n                                    </el-input>\n                                    <span class=\"input-help\">{{ $t('toolbox.fail2ban.sshPortHelper') }}</span>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('toolbox.fail2ban.maxRetry')\" prop=\"maxRetry\">\n                                    <el-input disabled v-model=\"form.maxRetry\">\n                                        <template #append>\n                                            <el-button @click=\"onChangeMaxRetry\" icon=\"Setting\">\n                                                {{ $t('commons.button.set') }}\n                                            </el-button>\n                                        </template>\n                                    </el-input>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('toolbox.fail2ban.banTime')\" prop=\"banTime\">\n                                    <el-input disabled v-model=\"form.banTimeItem\">\n                                        <template #append>\n                                            <el-button @click=\"onChangeBanTime\" icon=\"Setting\">\n                                                {{ $t('commons.button.set') }}\n                                            </el-button>\n                                        </template>\n                                    </el-input>\n                                    <span class=\"input-help\">{{ $t('toolbox.fail2ban.banTimeHelper') }}</span>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('toolbox.fail2ban.findTime')\" prop=\"findTime\">\n                                    <el-input disabled v-model=\"form.findTimeItem\">\n                                        <template #append>\n                                            <el-button @click=\"onChangeFindTime\" icon=\"Setting\">\n                                                {{ $t('commons.button.set') }}\n                                            </el-button>\n                                        </template>\n                                    </el-input>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('toolbox.fail2ban.banAction')\" prop=\"banAction\">\n                                    <el-input disabled v-model=\"form.banAction\">\n                                        <template #append>\n                                            <el-button @click=\"onChangeBanAction\" icon=\"Setting\">\n                                                {{ $t('commons.button.set') }}\n                                            </el-button>\n                                        </template>\n                                    </el-input>\n                                </el-form-item>\n                                <el-form-item :label=\"$t('toolbox.fail2ban.logPath')\" prop=\"logPath\">\n                                    <el-input disabled v-model=\"form.logPath\">\n                                        <template #append>\n                                            <el-button @click=\"onChangeLogPath\" icon=\"Setting\">\n                                                {{ $t('commons.button.set') }}\n                                            </el-button>\n                                        </template>\n                                    </el-input>\n                                </el-form-item>\n                            </el-form>\n                        </el-col>\n                    </el-row>\n\n                    <div v-if=\"confShowType === 'all'\">\n                        <CodemirrorPro\n                            class=\"mt-5\"\n                            placeholder=\"# The Fail2ban configuration file does not exist or is empty (/etc/ssh/sshd_config)\"\n                            v-model=\"fail2banConf\"\n                            :heightDiff=\"460\"\n                        ></CodemirrorPro>\n                        <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSaveFile\" class=\"mt-2.5\">\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                    </div>\n                </template>\n            </LayoutContent>\n        </div>\n        <NoSuchService v-else name=\"Fail2ban\" />\n\n        <MaxRetry ref=\"maxRetryRef\" @search=\"search\" />\n        <BanTime ref=\"banTimeRef\" @search=\"search\" />\n        <FindTime ref=\"findTimeRef\" @search=\"search\" />\n        <BanAction ref=\"banActionRef\" @search=\"search\" />\n        <LogPath ref=\"logPathRef\" @search=\"search\" />\n        <Port ref=\"portRef\" @search=\"search\" />\n\n        <IPs ref=\"listRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref } from 'vue';\nimport MaxRetry from '@/views/toolbox/fail2ban/max-retry/index.vue';\nimport BanTime from '@/views/toolbox/fail2ban/ban-time/index.vue';\nimport FindTime from '@/views/toolbox/fail2ban/find-time/index.vue';\nimport BanAction from '@/views/toolbox/fail2ban/ban-action/index.vue';\nimport LogPath from '@/views/toolbox/fail2ban/log-path/index.vue';\nimport NoSuchService from '@/components/layout-content/no-such-service.vue';\nimport Port from '@/views/toolbox/fail2ban/port/index.vue';\nimport IPs from '@/views/toolbox/fail2ban/ips/index.vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { getFail2banConf, getFail2banBase, operateFail2ban, updateFail2banByFile } from '@/api/modules/toolbox';\nimport { ElMessageBox } from 'element-plus';\nimport { transTimeUnit } from '@/utils/util';\nimport { GlobalStore } from '@/store';\n\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\nconst formRef = ref();\nconst confShowType = ref('base');\n\nconst portRef = ref();\nconst maxRetryRef = ref();\nconst banTimeRef = ref();\nconst findTimeRef = ref();\nconst banActionRef = ref();\nconst listRef = ref();\nconst logPathRef = ref();\n\nconst autoStart = ref('enable');\n\nconst fail2banConf = ref();\nconst form = reactive({\n    isEnable: true,\n    isActive: true,\n    isExist: false,\n    version: '-',\n\n    port: 22,\n    maxRetry: 5,\n    banTime: '',\n    banTimeItem: '',\n    findTime: '',\n    findTimeItem: '',\n    banAction: '',\n    logPath: '',\n});\n\nconst onLoadList = async (type: string) => {\n    listRef.value.acceptParams({ operate: type });\n};\n\nconst onSaveFile = async () => {\n    ElMessageBox.confirm(i18n.global.t('ssh.sshFileChangeHelper'), i18n.global.t('toolbox.fail2ban.fail2banChange'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        await updateFail2banByFile({ file: fail2banConf.value })\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst onChangePort = () => {\n    portRef.value.acceptParams({ port: form.port });\n};\nconst onChangeMaxRetry = () => {\n    maxRetryRef.value.acceptParams({ maxRetry: form.maxRetry });\n};\nconst onChangeBanTime = () => {\n    banTimeRef.value.acceptParams({ banTime: form.banTime });\n};\nconst onChangeFindTime = () => {\n    findTimeRef.value.acceptParams({ findTime: form.findTime });\n};\nconst onChangeBanAction = () => {\n    banActionRef.value.acceptParams({ banAction: form.banAction });\n};\nconst onChangeLogPath = () => {\n    logPathRef.value.acceptParams({ logPath: form.logPath });\n};\n\nconst onOperate = async (operation: string) => {\n    let msg = operation === 'enable' || operation === 'disable' ? 'ssh.' : 'commons.button.';\n    ElMessageBox.confirm(i18n.global.t('toolbox.fail2ban.operation', [i18n.global.t(msg + operation)]), 'Fail2ban', {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    })\n        .then(async () => {\n            loading.value = true;\n            await operateFail2ban(operation)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    search();\n                })\n                .catch(() => {\n                    autoStart.value = operation === 'enable' ? 'disable' : 'enable';\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            search();\n        });\n};\n\nconst loadSSHConf = async () => {\n    const res = await getFail2banConf();\n    fail2banConf.value = res.data || '';\n};\n\nconst changeMode = async () => {\n    if (confShowType.value === 'all') {\n        loadSSHConf();\n    } else {\n        search();\n    }\n};\n\nconst search = async () => {\n    const res = await getFail2banBase();\n    form.isEnable = res.data.isEnable;\n    form.isActive = res.data.isActive;\n    form.isExist = res.data.isExist;\n    autoStart.value = form.isEnable ? 'enable' : 'disable';\n    form.version = res.data.version;\n\n    form.port = res.data.port;\n    form.maxRetry = res.data.maxRetry;\n    form.banTime = res.data.banTime;\n    form.banTimeItem =\n        form.banTime === '-1' ? i18n.global.t('toolbox.fail2ban.banAllTime') : transTimeUnit(form.banTime);\n    form.findTime = res.data.findTime;\n    form.findTimeItem = transTimeUnit(form.findTime);\n    form.banAction = res.data.banAction;\n    form.logPath = res.data.logPath;\n};\n\nconst toDoc = () => {\n    window.open(globalStore.docsUrl + '/user_manual/toolbox/fail2ban/', '_blank', 'noopener,noreferrer');\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/fail2ban/ips/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"$t('toolbox.fail2ban.' + form.operate + 'IP')\"\n        @close=\"handleClose\"\n        size=\"small\"\n    >\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.fail2ban.' + form.operate + 'IP')\" prop=\"ips\">\n                <el-input :rows=\"25\" type=\"textarea\" clearable v-model=\"form.ips\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { operatorFail2banSSHD, searchFail2ban } from '@/api/modules/toolbox';\nimport { FormInstance } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { checkCidr, checkIpV4V6 } from '@/utils/util';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst drawerVisible = ref(false);\nconst loading = ref();\n\nconst form = reactive({\n    operate: 'ignore',\n    ips: '',\n});\n\nconst rules = reactive({\n    ips: [{ validator: checkIPs, trigger: 'blur' }],\n});\nfunction checkIPs(rule: any, value: any, callback: any) {\n    if (form.ips !== '') {\n        let addr = form.ips.split('\\n');\n        for (const item of addr) {\n            if (item === '') {\n                continue;\n            }\n            if (item.indexOf('/') !== -1) {\n                if (checkCidr(item)) {\n                    return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n                }\n            } else if (checkIpV4V6(item)) {\n                return callback(new Error(i18n.global.t('firewall.addressFormatError')));\n            }\n        }\n    }\n    callback();\n}\n\ninterface DialogProps {\n    operate: string;\n}\nconst formRef = ref<FormInstance>();\nconst acceptParams = (params: DialogProps): void => {\n    form.operate = params.operate;\n    form.ips = '';\n    drawerVisible.value = true;\n    search();\n};\n\nconst search = () => {\n    let params = {\n        status: form.operate,\n    };\n    searchFail2ban(params).then((res) => {\n        let dataItem = res.data || [];\n        form.ips = dataItem.join('\\n');\n    });\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('toolbox.fail2ban.' + form.operate + 'Helper'),\n            i18n.global.t('commons.button.set'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            let ipItems = form.ips.split('\\n');\n            let ips = [];\n            for (const item of ipItems) {\n                if (item !== '') {\n                    ips.push(item);\n                }\n            }\n            let param = {\n                operate: form.operate,\n                ips: ips,\n            };\n            loading.value = true;\n            await operatorFail2banSSHD(param)\n                .then(async () => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/fail2ban/log-path/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.fail2ban.logPath')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.fail2ban.logPath')\" prop=\"logPath\" :rules=\"Rules.requiredInput\">\n                <el-input v-model=\"form.logPath\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({})\" />\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('toolbox.fail2ban.logPathHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"loadLogPath\" />\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport FileList from '@/components/file-list/index.vue';\nimport { updateFail2ban } from '@/api/modules/toolbox';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    logPath: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\nconst fileRef = ref();\n\nconst form = reactive({\n    logPath: '',\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.logPath = params.logPath;\n    drawerVisible.value = true;\n};\n\nconst loadLogPath = async (path: string) => {\n    form.logPath = path;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('toolbox.fail2ban.logPath'), form.logPath]),\n            i18n.global.t('toolbox.fail2ban.fail2banChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            await updateFail2ban({ key: 'logpath', value: form.logPath })\n                .then(async () => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/fail2ban/max-retry/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.fail2ban.maxRetry')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :rules=\"rules\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.fail2ban.maxRetry')\" prop=\"maxRetry\">\n                <el-input type=\"number\" clearable v-model.number=\"form.maxRetry\">\n                    <template #append>{{ $t('commons.units.time') }}</template>\n                </el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { updateFail2ban } from '@/api/modules/toolbox';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    maxRetry: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    maxRetry: 5,\n});\n\nconst rules = reactive({\n    maxRetry: [Rules.integerNumber, checkNumberRange(1, 99)],\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.maxRetry = Number(params.maxRetry);\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('toolbox.fail2ban.maxRetry'), form.maxRetry]),\n            i18n.global.t('toolbox.fail2ban.fail2banChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            await updateFail2ban({ key: 'maxretry', value: form.maxRetry + '' })\n                .then(async () => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/fail2ban/port/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" :header=\"$t('toolbox.fail2ban.sshPort')\" @close=\"handleClose\" size=\"small\">\n        <el-form ref=\"formRef\" label-position=\"top\" :rules=\"rules\" :model=\"form\" @submit.prevent v-loading=\"loading\">\n            <el-form-item :label=\"$t('toolbox.fail2ban.sshPort')\" prop=\"port\">\n                <el-input type=\"number\" clearable v-model.number=\"form.port\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSave(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { updateFail2ban } from '@/api/modules/toolbox';\n\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\ninterface DialogProps {\n    port: string;\n}\nconst drawerVisible = ref();\nconst loading = ref();\n\nconst form = reactive({\n    port: 22,\n});\n\nconst rules = reactive({\n    port: [Rules.integerNumber, checkNumberRange(1, 65535)],\n});\n\nconst formRef = ref<FormInstance>();\n\nconst acceptParams = (params: DialogProps): void => {\n    form.port = Number(params.port);\n    drawerVisible.value = true;\n};\n\nconst onSave = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(\n            i18n.global.t('ssh.sshChangeHelper', [i18n.global.t('toolbox.fail2ban.sshPort'), form.port]),\n            i18n.global.t('toolbox.fail2ban.fail2banChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        ).then(async () => {\n            await updateFail2ban({ key: 'port', value: form.port + '' })\n                .then(async () => {\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    loading.value = false;\n                    drawerVisible.value = false;\n                    emit('search');\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/ftp/index.vue",
    "content": "<template>\n    <div>\n        <div class=\"app-status card-interval\">\n            <el-card v-if=\"form.isExist\">\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag effect=\"dark\" type=\"success\">FTP</el-tag>\n                        <Status class=\"mt-0.5\" :status=\"form.isActive ? 'enable' : 'disable'\" />\n                    </div>\n                    <div class=\"mt-0.5\">\n                        <el-button v-if=\"form.isActive\" type=\"primary\" @click=\"onOperate('stop')\" link>\n                            {{ $t('commons.button.stop') }}\n                        </el-button>\n                        <el-button v-if=\"!form.isActive\" type=\"primary\" @click=\"onOperate('start')\" link>\n                            {{ $t('commons.button.start') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" @click=\"onOperate('restart')\" link>\n                            {{ $t('commons.button.restart') }}\n                        </el-button>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n        <div v-if=\"form.isExist\">\n            <LayoutContent v-loading=\"loading\" :title=\"$t('toolbox.ftp.ftp', 2)\" :class=\"{ mask: !form.isActive }\">\n                <template #prompt v-if=\"!globalStore.isFxplay\">\n                    <el-alert type=\"info\" :closable=\"false\">\n                        <template #title>\n                            {{ $t('toolbox.common.toolboxHelper') }}\n                            <el-link class=\"ml-1 text-xs\" @click=\"toDoc()\" type=\"primary\">\n                                {{ $t('commons.button.helpDoc') }}\n                            </el-link>\n                        </template>\n                    </el-alert>\n                </template>\n                <template #leftToolBar>\n                    <el-button type=\"primary\" @click=\"onOpenDialog('add')\">\n                        {{ $t('commons.button.add') }}\n                    </el-button>\n                    <el-button @click=\"onSync()\">\n                        {{ $t('commons.button.sync') }}\n                    </el-button>\n                    <el-button plain :disabled=\"selects.length === 0\" @click=\"onDelete(null)\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </template>\n                <template #rightToolBar>\n                    <TableSearch @search=\"search()\" v-model:searchName=\"searchName\" />\n                </template>\n                <template #main>\n                    <ComplexTable\n                        :pagination-config=\"paginationConfig\"\n                        v-model:selects=\"selects\"\n                        @sort-change=\"search\"\n                        @search=\"search\"\n                        :data=\"data\"\n                    >\n                        <el-table-column type=\"selection\" fix />\n                        <el-table-column\n                            :label=\"$t('commons.login.username')\"\n                            :min-width=\"60\"\n                            prop=\"user\"\n                            show-overflow-tooltip\n                        />\n                        <el-table-column :label=\"$t('commons.login.password')\" prop=\"password\">\n                            <template #default=\"{ row }\">\n                                <div v-if=\"row.password.length === 0\">-</div>\n                                <div v-else class=\"flex items-center flex-wrap\">\n                                    <div class=\"star-center\" v-if=\"!row.showPassword\">\n                                        <span>**********</span>\n                                    </div>\n                                    <div>\n                                        <span v-if=\"row.showPassword\">\n                                            {{ row.password }}\n                                        </span>\n                                    </div>\n                                    <el-button\n                                        v-if=\"!row.showPassword\"\n                                        link\n                                        @click=\"row.showPassword = true\"\n                                        icon=\"View\"\n                                        class=\"ml-1.5\"\n                                    ></el-button>\n                                    <el-button\n                                        v-if=\"row.showPassword\"\n                                        link\n                                        @click=\"row.showPassword = false\"\n                                        icon=\"Hide\"\n                                        class=\"ml-1.5\"\n                                    ></el-button>\n                                    <div>\n                                        <CopyButton :content=\"row.password\" />\n                                    </div>\n                                </div>\n                            </template>\n                        </el-table-column>\n                        <el-table-column :label=\"$t('commons.table.status')\" :min-width=\"60\" prop=\"status\">\n                            <template #default=\"{ row }\">\n                                <Status v-if=\"row.status === 'Deleted'\" :status=\"row.status\" />\n                                <Status\n                                    v-if=\"row.status === 'Enable'\"\n                                    :status=\"row.status\"\n                                    @click=\"onChangeStatus(row, 'disable')\"\n                                />\n                                <Status\n                                    v-if=\"row.status === 'Disable'\"\n                                    :status=\"row.status\"\n                                    @click=\"onChangeStatus(row, 'enable')\"\n                                />\n                            </template>\n                        </el-table-column>\n                        <el-table-column :label=\"$t('file.root')\" :min-width=\"120\" prop=\"path\" show-overflow-tooltip>\n                            <template #default=\"{ row }\">\n                                <el-button text type=\"primary\" @click=\"routerToFileWithPath(row.path)\">\n                                    {{ row.path }}\n                                </el-button>\n                            </template>\n                        </el-table-column>\n                        <el-table-column\n                            :label=\"$t('commons.table.description')\"\n                            prop=\"description\"\n                            show-overflow-tooltip\n                        >\n                            <template #default=\"{ row }\">\n                                <fu-input-rw-switch\n                                    v-model=\"row.description\"\n                                    @enter=\"onChange(row)\"\n                                    @blur=\"onChange(row)\"\n                                />\n                            </template>\n                        </el-table-column>\n                        <fu-table-operations\n                            width=\"200px\"\n                            :buttons=\"buttons\"\n                            :ellipsis=\"10\"\n                            :label=\"$t('commons.table.operate')\"\n                            fix\n                        />\n                    </ComplexTable>\n                </template>\n            </LayoutContent>\n\n            <el-card v-if=\"form.isExist && !form.isActive\" class=\"mask-prompt\">\n                <span>{{ $t('toolbox.ftp.notStart') }}</span>\n            </el-card>\n        </div>\n        <NoSuchService v-else name=\"FTP (pure-ftpd)\" />\n\n        <OpDialog ref=\"opRef\" @search=\"search\" @submit=\"onSubmitDelete()\" />\n        <OperateDialog @search=\"search\" ref=\"dialogRef\" />\n        <LogDialog ref=\"dialogLogRef\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { deleteFtp, searchFtp, updateFtp, syncFtp, operateFtp, getFtpBase } from '@/api/modules/toolbox';\nimport OperateDialog from '@/views/toolbox/ftp/operate/index.vue';\nimport LogDialog from '@/views/toolbox/ftp/log/index.vue';\nimport { Toolbox } from '@/api/interface/toolbox';\nimport { GlobalStore } from '@/store';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { getRandomStr } from '@/utils/util';\n\nconst globalStore = GlobalStore();\n\nconst loading = ref();\nconst selects = ref<any>([]);\n\nconst data = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'ftp-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('ftp-page-size')) || 20,\n    total: 0,\n    orderBy: 'createdAt',\n    order: 'null',\n});\nconst searchName = ref();\n\nconst form = reactive({\n    isActive: true,\n    isExist: true,\n});\n\nconst opRef = ref();\nconst dialogRef = ref();\nconst operateIDs = ref();\nconst dialogLogRef = ref();\n\nconst search = async (column?: any) => {\n    loading.value = true;\n    await getFtpBase()\n        .then(async (res) => {\n            form.isActive = res.data.isActive;\n            form.isExist = res.data.isExist;\n            paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;\n            paginationConfig.order = column?.order ? column.order : paginationConfig.order;\n            let params = {\n                info: searchName.value,\n                page: paginationConfig.currentPage,\n                pageSize: paginationConfig.pageSize,\n            };\n            await searchFtp(params)\n                .then((res) => {\n                    loading.value = false;\n                    data.value = res.data.items || [];\n                    paginationConfig.total = res.data.total;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst onOperate = async (operation: string) => {\n    let msg = operation === 'enable' || operation === 'disable' ? 'ssh.' : 'commons.button.';\n    ElMessageBox.confirm(i18n.global.t('toolbox.ftp.operation', [i18n.global.t(msg + operation)]), 'FTP', {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    })\n        .then(async () => {\n            loading.value = true;\n            await operateFtp(operation)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    search();\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        })\n        .catch(() => {\n            search();\n        });\n};\n\nconst onChangeStatus = async (row: Toolbox.FtpInfo, status: string) => {\n    if (row.password.length === 0) {\n        MsgError(i18n.global.t('toolbox.ftp.noPasswdMsg'));\n        return;\n    }\n    ElMessageBox.confirm(i18n.global.t('toolbox.ftp.' + status + 'Helper'), i18n.global.t('cronjob.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        row.status = status === 'enable' ? 'Enable' : 'Disable';\n        await updateFtp(row);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        search();\n    });\n};\n\nconst onChange = async (row: any) => {\n    await await updateFtp(row);\n    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n};\n\nconst onOpenDialog = async (\n    title: string,\n    rowData: Partial<Toolbox.FtpInfo> = {\n        password: getRandomStr(12),\n    },\n) => {\n    let params = {\n        title,\n        rowData: { ...rowData },\n    };\n    dialogRef.value!.acceptParams(params);\n};\n\nconst onSync = async () => {\n    ElMessageBox.confirm(i18n.global.t('toolbox.ftp.syncHelper'), i18n.global.t('commons.button.sync'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        loading.value = true;\n        await syncFtp()\n            .then(() => {\n                loading.value = false;\n                MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                search();\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst onDelete = async (row: Toolbox.FtpInfo | null) => {\n    let names = [];\n    let ids = [];\n    if (row) {\n        ids = [row.id];\n        names = [row.user];\n    } else {\n        for (const item of selects.value) {\n            names.push(item.user);\n            ids.push(item.id);\n        }\n    }\n    operateIDs.value = ids;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.ftpUser'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: null,\n        params: null,\n    });\n};\n\nconst onSubmitDelete = async () => {\n    loading.value = true;\n    await deleteFtp({ ids: operateIDs.value })\n        .then(() => {\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n            search();\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        disabled: (row: Toolbox.FtpInfo) => {\n            return row.status === 'deleted';\n        },\n        click: (row: Toolbox.FtpInfo) => {\n            onOpenDialog('edit', row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.log'),\n        disabled: (row: Toolbox.FtpInfo) => {\n            return row.status === 'deleted';\n        },\n        click: (row: Toolbox.FtpInfo) => {\n            dialogLogRef.value!.acceptParams({ user: row.user, path: row.path });\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: Toolbox.FtpInfo) => {\n            onDelete(row);\n        },\n    },\n];\n\nconst toDoc = () => {\n    window.open(globalStore.docsUrl + '/user_manual/toolbox/ftp/', '_blank', 'noopener,noreferrer');\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/ftp/log/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"drawerVisible\" header=\"FTP\" :resource=\"paginationConfig.user\" @close=\"handleClose\" size=\"large\">\n        <el-select @change=\"search\" class=\"p-w-200\" clearable v-model=\"paginationConfig.operation\">\n            <template #prefix>{{ $t('commons.table.operate') }}</template>\n            <el-option value=\"PUT\" :label=\"$t('commons.button.upload')\" />\n            <el-option value=\"GET\" :label=\"$t('commons.button.download')\" />\n        </el-select>\n        <ComplexTable class=\"mt-2\" :pagination-config=\"paginationConfig\" :data=\"data\" @search=\"search\">\n            <el-table-column label=\"ip\" prop=\"ip\" show-overflow-tooltip />\n            <el-table-column :label=\"$t('commons.table.status')\" min-width=\"50\" show-overflow-tooltip prop=\"status\">\n                <template #default=\"{ row }\">\n                    <Status :status=\"row.status === '200' ? 'success' : 'failed'\" />\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('commons.table.operate')\" min-width=\"40\" show-overflow-tooltip>\n                <template #default=\"{ row }\">\n                    {{ loadOperation(row.operation) }}\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('menu.files')\" show-overflow-tooltip>\n                <template #default=\"{ row }\">\n                    {{ loadFileName(row.operation) }}\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('file.size')\" show-overflow-tooltip prop=\"size\" min-width=\"60\">\n                <template #default=\"{ row }\">\n                    {{ computeSizeFromByte(Number(row.size)) }}\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('commons.table.date')\" prop=\"time\" show-overflow-tooltip min-width=\"100\" />\n        </ComplexTable>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { searchFtpLog } from '@/api/modules/toolbox';\nimport { computeSizeFromByte } from '@/utils/util';\nimport i18n from '@/lang';\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'ftp-log-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('ftp-log-page-size')) || 20,\n    total: 0,\n    user: '',\n    operation: '',\n});\nconst data = ref();\n\nconst itemPath = ref();\ninterface DialogProps {\n    user: string;\n    path: string;\n}\nconst loading = ref();\nconst drawerVisible = ref(false);\n\nconst acceptParams = (params: DialogProps): void => {\n    paginationConfig.user = params.user;\n    paginationConfig.operation = '';\n    itemPath.value = params.path;\n    search();\n    drawerVisible.value = true;\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst search = async () => {\n    let params = {\n        user: paginationConfig.user,\n        operation: paginationConfig.operation,\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    loading.value = true;\n    await searchFtpLog(params)\n        .then((res) => {\n            loading.value = false;\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .catch(() => {\n            loading.value = false;\n        });\n};\n\nconst loadOperation = (operation: string) => {\n    if (operation.startsWith('\"PUT')) {\n        return i18n.global.t('commons.button.upload');\n    }\n    if (operation.startsWith('\"GET')) {\n        return i18n.global.t('commons.button.download');\n    }\n};\nconst loadFileName = (operation: string) => {\n    return operation\n        .replaceAll('\"', '')\n        .replaceAll('PUT', '')\n        .replaceAll('GET', '')\n        .replaceAll(itemPath.value + '/', '');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/ftp/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"drawerVisible\"\n        :header=\"title\"\n        :resource=\"dialogData.title === 'add' ? '' : dialogData.rowData?.user\"\n        @close=\"handleClose\"\n        size=\"large\"\n    >\n        <el-form ref=\"formRef\" label-position=\"top\" :model=\"dialogData.rowData\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('commons.login.username')\" prop=\"user\">\n                <el-input :disabled=\"dialogData.title === 'edit'\" clearable v-model.trim=\"dialogData.rowData!.user\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                <el-input type=\"password\" clearable v-model=\"dialogData.rowData!.password\" show-password>\n                    <template #append>\n                        <el-button @click=\"random\">{{ $t('commons.button.random') }}</el-button>\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('file.root')\" prop=\"path\">\n                <el-input v-model=\"dialogData.rowData!.path\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: true })\" />\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('toolbox.ftp.dirHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.description')\" prop=\"description\">\n                <el-input type=\"textarea\" :rows=\"3\" clearable v-model=\"dialogData.rowData!.description\" />\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"drawerVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button :disabled=\"loading\" type=\"primary\" @click=\"onSubmit(formRef)\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"loadDir\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport FileList from '@/components/file-list/index.vue';\nimport i18n from '@/lang';\nimport { ElForm } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { Toolbox } from '@/api/interface/toolbox';\nimport { createFtp, updateFtp } from '@/api/modules/toolbox';\nimport { getRandomStr, isSensitiveLinuxPath } from '@/utils/util';\n\ninterface DialogProps {\n    title: string;\n    rowData?: Toolbox.FtpInfo;\n    getTableList?: () => Promise<any>;\n}\nconst loading = ref();\nconst title = ref<string>('');\nconst drawerVisible = ref(false);\nconst fileRef = ref();\nconst dialogData = ref<DialogProps>({\n    title: '',\n});\n\nconst acceptParams = (params: DialogProps): void => {\n    dialogData.value = params;\n    title.value = i18n.global.t('commons.button.' + dialogData.value.title);\n    drawerVisible.value = true;\n};\nconst emit = defineEmits<{ (e: 'search'): void }>();\n\nconst random = async () => {\n    dialogData.value.rowData.password = getRandomStr(16);\n};\n\nconst handleClose = () => {\n    drawerVisible.value = false;\n};\n\nconst verifyPath = (rule: any, value: any, callback: any) => {\n    if (isSensitiveLinuxPath(dialogData.value.rowData.path)) {\n        callback(new Error(i18n.global.t('toolbox.ftp.dirSystem')));\n        return;\n    }\n    callback();\n};\n\nconst rules = reactive({\n    user: [Rules.simpleName],\n    password: [Rules.simplePassword],\n    path: [Rules.requiredInput, Rules.noSpace, { validator: verifyPath, trigger: 'change', required: true }],\n});\n\ntype FormInstance = InstanceType<typeof ElForm>;\nconst formRef = ref<FormInstance>();\n\nconst loadDir = async (path: string) => {\n    dialogData.value.rowData!.path = path;\n};\n\nconst onSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        ElMessageBox.confirm(i18n.global.t('toolbox.ftp.dirMsg', [dialogData.value.rowData.path]), 'FTP', {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        }).then(async () => {\n            loading.value = true;\n            if (dialogData.value.title === 'edit') {\n                await updateFtp(dialogData.value.rowData)\n                    .then(() => {\n                        loading.value = false;\n                        drawerVisible.value = false;\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                        emit('search');\n                    })\n                    .catch(() => {\n                        loading.value = false;\n                    });\n\n                return;\n            }\n\n            await createFtp(dialogData.value.rowData)\n                .then(() => {\n                    loading.value = false;\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                    emit('search');\n                    drawerVisible.value = false;\n                })\n                .catch(() => {\n                    loading.value = false;\n                });\n        });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\">\n            <template #route-button>\n                <div class=\"router-button\">\n                    <el-button link type=\"primary\" @click=\"onRestart('1panel')\">\n                        {{ $t('home.restart_1panel') }}\n                    </el-button>\n                    <el-divider direction=\"vertical\" />\n                    <el-button link type=\"primary\" @click=\"onRestart('system')\">\n                        {{ $t('home.restart_system') }}\n                    </el-button>\n                </div>\n            </template>\n        </RouterButton>\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"onSave\"></ConfirmDialog>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { ref } from 'vue';\nimport ConfirmDialog from '@/components/confirm-dialog/index.vue';\nimport { GlobalStore } from '@/store';\nimport { MsgSuccess } from '@/utils/message';\nimport { systemRestart } from '@/api/modules/dashboard';\n\nconst restartType = ref();\nconst confirmDialogRef = ref();\nconst globalStore = GlobalStore();\n\nconst onRestart = (type: string) => {\n    let header = i18n.global.t('home.restart_' + type);\n    if (globalStore.currentNode != 'local' && type === '1panel') {\n        type = '1panel-agent';\n    }\n    restartType.value = type;\n    let params = {\n        header: header,\n        operationInfo: '',\n        submitInputInfo: i18n.global.t('database.restartNow'),\n    };\n    confirmDialogRef.value!.acceptParams(params);\n};\nconst onSave = async () => {\n    globalStore.isOnRestart = true;\n    MsgSuccess(i18n.global.t('home.operationSuccess'));\n    await systemRestart(restartType.value);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('toolbox.device.toolbox'),\n        path: '/toolbox/device',\n    },\n    {\n        label: i18n.global.t('setting.diskClean'),\n        path: '/toolbox/clean',\n    },\n    {\n        label: i18n.global.t('menu.supervisor'),\n        path: '/toolbox/supervisor',\n    },\n    {\n        label: i18n.global.t('toolbox.clam.clam'),\n        path: '/toolbox/clam',\n    },\n    {\n        label: 'FTP',\n        path: '/toolbox/ftp',\n    },\n    {\n        label: 'Fail2ban',\n        path: '/toolbox/fail2ban',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/config/basic/index.vue",
    "content": "<template>\n    <el-row v-loading=\"loading\">\n        <el-col :xs=\"24\" :sm=\"12\" :md=\"12\" :lg=\"12\" :xl=\"12\" :offset=\"1\">\n            <el-form ref=\"initForm\" label-position=\"top\" :model=\"data\" label-width=\"100px\" :rules=\"rules\">\n                <el-form-item :label=\"$t('tool.supervisor.primaryConfig')\" prop=\"configPath\">\n                    <el-input v-model.trim=\"data.configPath\"></el-input>\n                </el-form-item>\n                <el-form-item :label=\"$t('tool.supervisor.serviceName')\" prop=\"serviceName\">\n                    <el-input v-model.trim=\"data.serviceName\"></el-input>\n                    <span class=\"input-help\">{{ $t('tool.supervisor.serviceNameHelper') }}</span>\n                </el-form-item>\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"submit(initForm)\" :disabled=\"loading\">\n                        {{ $t('commons.button.confirm') }}\n                    </el-button>\n                </el-form-item>\n            </el-form>\n        </el-col>\n    </el-row>\n</template>\n\n<script lang=\"ts\" setup>\nimport { HostTool } from '@/api/interface/host-tool';\nimport { getSupervisorStatus, initSupervisor } from '@/api/modules/host-tool';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { onMounted, ref } from 'vue';\nconst loading = ref(false);\nconst initForm = ref<FormInstance>();\nconst rules = ref({\n    configPath: [Rules.requiredInput],\n    serviceName: [Rules.requiredInput],\n});\n\nconst data = ref({\n    isExist: false,\n    version: '',\n    status: 'running',\n    init: false,\n    configPath: '',\n    ctlExist: false,\n    serviceName: '',\n});\n\nconst getStatus = async () => {\n    try {\n        loading.value = true;\n        const res = await getSupervisorStatus();\n        data.value = res.data.config as HostTool.Supersivor;\n    } catch (error) {}\n    loading.value = false;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        initSupervisor({\n            type: 'supervisord',\n            configPath: data.value.configPath,\n            serviceName: data.value.serviceName,\n        })\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nonMounted(() => {\n    getStatus();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/config/index.vue",
    "content": "<template>\n    <LayoutContent :title=\"$t('tool.supervisor.config')\" :reload=\"true\">\n        <template #leftToolBar>\n            <el-button type=\"primary\" :plain=\"activeName !== '1'\" @click=\"changeTab('1')\">\n                {{ $t('nginx.configResource') }}\n            </el-button>\n            <el-button type=\"primary\" :plain=\"activeName !== '2'\" @click=\"changeTab('2')\">\n                {{ $t('commons.button.log') }}\n            </el-button>\n            <el-button type=\"primary\" :plain=\"activeName !== '3'\" @click=\"changeTab('3')\">\n                {{ $t('commons.button.init') }}\n            </el-button>\n        </template>\n        <template #main>\n            <Source v-if=\"activeName === '1'\"></Source>\n            <div v-if=\"activeName === '2'\">\n                <LogFile\n                    :config=\"{ id: 0, type: 'supervisord', name: 'supervisor', colorMode: 'container' }\"\n                    ref=\"logRef\"\n                ></LogFile>\n            </div>\n            <Basic v-if=\"activeName === '3'\"></Basic>\n        </template>\n    </LayoutContent>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport Source from './source/index.vue';\nimport LogFile from '@/components/log/file/index.vue';\nimport Basic from './basic/index.vue';\n\nconst activeName = ref('1');\n\nconst changeTab = (index: string) => {\n    activeName.value = index;\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/config/source/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <CodemirrorPro class=\"mt-5\" v-model=\"content\" :heightDiff=\"375\"></CodemirrorPro>\n        <div class=\"mt-5\">\n            <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                {{ $t('commons.button.save') }}\n            </el-button>\n        </div>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { onMounted, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { operateSupervisorConfig } from '@/api/modules/host-tool';\n\nlet data = ref();\nlet content = ref('');\nlet loading = ref(false);\n\nconst submit = () => {\n    loading.value = true;\n    operateSupervisorConfig({ type: 'supervisord', operate: 'set', content: content.value })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            getConfig();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst getConfig = async () => {\n    const res = await operateSupervisorConfig({ type: 'supervisord', operate: 'get' });\n    data.value = res.data;\n    content.value = data.value.content;\n};\n\nonMounted(() => {\n    getConfig();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/create/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"process.operate == 'create' ? $t('commons.button.create') : $t('commons.button.edit')\"\n        @close=\"handleClose\"\n        size=\"small\"\n    >\n        <el-form\n            ref=\"processForm\"\n            label-position=\"top\"\n            :model=\"process\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model.trim=\"process.name\" :disabled=\"process.operate == 'update'\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.user')\" prop=\"user\">\n                <el-input v-model.trim=\"process.user\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.dir')\" prop=\"dir\">\n                <el-input v-model.trim=\"process.dir\">\n                    <template #prepend>\n                        <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ dir: true })\" />\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.command')\" prop=\"command\">\n                <el-input v-model=\"process.command\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.numprocs')\" prop=\"numprocsNum\">\n                <el-input type=\"number\" v-model.number=\"process.numprocsNum\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('process.env')\" prop=\"environment\">\n                <el-input type=\"text\" v-model=\"process.environment\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.autoRestart')\" prop=\"autoRestart\">\n                <el-switch v-model=\"process.autoRestart\" active-value=\"true\" inactive-value=\"false\"></el-switch>\n                <span class=\"input-help\">{{ $t('tool.supervisor.autoRestartHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.autoStart')\" prop=\"autoStart\">\n                <el-switch v-model=\"process.autoStart\" active-value=\"true\" inactive-value=\"false\"></el-switch>\n                <span class=\"input-help\">{{ $t('tool.supervisor.autoStartHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(processForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"getPath\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { createSupervisorProcess } from '@/api/modules/host-tool';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport FileList from '@/components/file-list/index.vue';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { HostTool } from '@/api/interface/host-tool';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst fileRef = ref();\nconst processForm = ref<FormInstance>();\nconst rules = ref({\n    name: [Rules.requiredInput, Rules.supervisorName],\n    dir: [Rules.requiredInput],\n    command: [Rules.requiredInput],\n    user: [Rules.requiredInput],\n    numprocsNum: [Rules.requiredInput, Rules.integerNumber, checkNumberRange(1, 9999)],\n});\nconst initData = () => ({\n    operate: 'create',\n    name: '',\n    command: '',\n    user: 'root',\n    dir: '',\n    numprocsNum: 1,\n    numprocs: '1',\n    autoRestart: 'true',\n    autoStart: 'true',\n    environment: '',\n});\nconst process = ref(initData());\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    resetForm();\n    em('close', open);\n};\n\nconst getPath = (path: string) => {\n    process.value.dir = path;\n};\n\nconst resetForm = () => {\n    process.value = initData();\n    processForm.value?.resetFields();\n};\n\nconst acceptParams = (operate: string, config: HostTool.SupersivorProcess) => {\n    process.value = initData();\n    if (operate == 'update') {\n        process.value = {\n            operate: 'update',\n            name: config.name,\n            command: config.command,\n            user: config.user,\n            dir: config.dir,\n            numprocsNum: 1,\n            numprocs: config.numprocs,\n            autoRestart: config.autoRestart,\n            autoStart: config.autoStart,\n            environment: config.environment || '',\n        };\n        process.value.numprocsNum = Number(config.numprocs);\n    }\n    open.value = true;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        process.value.numprocs = String(process.value.numprocsNum);\n        createSupervisorProcess(process.value)\n            .then(() => {\n                open.value = false;\n                em('close', open);\n                MsgSuccess(i18n.global.t('commons.msg.' + process.value.operate + 'Success'));\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/file/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('website.source')\"\n        @close=\"handleClose\"\n        :size=\"globalStore.isFullScreen ? 'full' : 'large'\"\n        :fullScreen=\"true\"\n    >\n        <template #content>\n            <div v-loading=\"loading\">\n                <CodemirrorPro class=\"mt-5\" v-model=\"content\" :heightDiff=\"400\"></CodemirrorPro>\n            </div>\n        </template>\n\n        <template #footer>\n            <span>\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" :disabled=\"loading\" @click=\"submit()\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { onUnmounted, reactive, ref } from 'vue';\nimport { operateSupervisorProcessFile } from '@/api/modules/host-tool';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\nconst content = ref('');\nconst tailLog = ref(false);\nconst open = ref(false);\nconst req = reactive({\n    name: '',\n    file: 'conf',\n    operate: '',\n    content: '',\n});\n\nconst em = defineEmits(['search']);\n\nconst getContent = () => {\n    loading.value = true;\n    operateSupervisorProcessFile(req)\n        .then((res) => {\n            content.value = res.data;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst handleClose = () => {\n    content.value = '';\n    open.value = false;\n};\n\nconst submit = () => {\n    const updateReq = {\n        name: req.name,\n        operate: 'update',\n        file: req.file,\n        content: content.value,\n    };\n    loading.value = true;\n    operateSupervisorProcessFile(updateReq)\n        .then(() => {\n            em('search');\n            open.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst acceptParams = (name: string, file: string, operate: string) => {\n    req.name = name;\n    req.file = file;\n    req.operate = operate;\n\n    getContent();\n    open.value = true;\n};\n\nconst onCloseLog = async () => {\n    tailLog.value = false;\n};\n\nonUnmounted(() => {\n    onCloseLog();\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.fullScreen {\n    border: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/index.vue",
    "content": "<template>\n    <div>\n        <el-card v-if=\"showStopped\" class=\"mask-prompt\">\n            <span>{{ $t('tool.supervisor.notStartWarn') }}</span>\n        </el-card>\n        <LayoutContent :title=\"$t(' tool.supervisor.list', 2)\" v-loading=\"loading\">\n            <template #prompt v-if=\"!globalStore.isFxplay\">\n                <el-alert type=\"info\" :closable=\"false\">\n                    <template #title>\n                        {{ $t('toolbox.common.toolboxHelper') }}\n                        <el-link class=\"ml-1 text-xs\" @click=\"toDoc()\" type=\"primary\">\n                            {{ $t('commons.button.helpDoc') }}\n                        </el-link>\n                    </template>\n                </el-alert>\n            </template>\n            <template #app>\n                <SuperVisorStatus\n                    @setting=\"setting\"\n                    v-model:loading=\"loading\"\n                    @get-status=\"getStatus\"\n                    v-model:mask-show=\"maskShow\"\n                />\n            </template>\n            <template v-if=\"showTable\" #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\" :disabled=\"showStopped\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n            </template>\n            <template v-if=\"showTable\" #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n            </template>\n            <template #main v-if=\"showTable\">\n                <ComplexTable :data=\"data\" :class=\"{ mask: !supervisorStatus.isRunning }\" v-loading=\"dataLoading\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"name\"\n                        min-width=\"80px\"\n                        show-overflow-tooltip\n                    ></el-table-column>\n                    <el-table-column\n                        :label=\"$t('tool.supervisor.command')\"\n                        prop=\"command\"\n                        min-width=\"100px\"\n                        fix\n                        show-overflow-tooltip\n                    ></el-table-column>\n                    <el-table-column\n                        :label=\"$t('tool.supervisor.dir')\"\n                        prop=\"dir\"\n                        min-width=\"100px\"\n                        fix\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-button link type=\"primary\" @click=\"routerToFileWithPath(row.dir)\">\n                                {{ row.dir }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('tool.supervisor.user')\"\n                        prop=\"user\"\n                        show-overflow-tooltip\n                        min-width=\"50px\"\n                    ></el-table-column>\n                    <el-table-column\n                        :label=\"$t('tool.supervisor.numprocs')\"\n                        prop=\"numprocs\"\n                        min-width=\"60px\"\n                    ></el-table-column>\n                    <el-table-column :label=\"$t('tool.supervisor.manage')\" min-width=\"80px\">\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.status && row.status.length > 0 && row.hasLoad\">\n                                <Status\n                                    v-if=\"checkStatus(row.status) === 'RUNNING'\"\n                                    status=\"running\"\n                                    @click=\"operate('stop', row.name)\"\n                                />\n                                <Status\n                                    v-else-if=\"checkStatus(row.status) === 'WARNING'\"\n                                    status=\"unhealthy\"\n                                    @click=\"operate('restart', row.name)\"\n                                />\n                                <Status v-else status=\"stopped\" @click=\"operate('start', row.name)\" />\n                            </div>\n                            <div v-if=\"!row.hasLoad\">\n                                <el-button link loading></el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" min-width=\"60px\">\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.hasLoad\">\n                                <el-popover placement=\"bottom\" :width=\"700\" trigger=\"hover\">\n                                    <template #reference>\n                                        <el-button type=\"primary\" link v-if=\"row.status.length > 1\">\n                                            {{ $t('website.check') }}\n                                        </el-button>\n                                        <el-button type=\"primary\" link v-else>\n                                            <span>{{ $t('tool.supervisor.' + row.status[0].status) }}</span>\n                                        </el-button>\n                                    </template>\n                                    <el-table :data=\"row.status\">\n                                        <el-table-column\n                                            property=\"name\"\n                                            :label=\"$t('commons.table.name')\"\n                                            show-overflow-tooltip\n                                        />\n                                        <el-table-column\n                                            property=\"status\"\n                                            :label=\"$t('tool.supervisor.statusCode')\"\n                                            width=\"100px\"\n                                        />\n                                        <el-table-column property=\"PID\" label=\"PID\" width=\"120px\">\n                                            <template #default=\"scope\">\n                                                <el-button\n                                                    link\n                                                    type=\"primary\"\n                                                    v-if=\"scope.row.PID != ''\"\n                                                    @click=\"processDetailRef.acceptParams(scope.row.PID)\"\n                                                >\n                                                    {{ scope.row.PID }}\n                                                </el-button>\n                                            </template>\n                                        </el-table-column>\n                                        <el-table-column\n                                            property=\"uptime\"\n                                            :label=\"$t('tool.supervisor.uptime')\"\n                                            width=\"100px\"\n                                        />\n                                        <el-table-column\n                                            property=\"msg\"\n                                            :label=\"$t('commons.table.message')\"\n                                            show-overflow-tooltip\n                                        />\n                                    </el-table>\n                                </el-popover>\n                            </div>\n                            <div v-if=\"!row.hasLoad\">\n                                <el-button link loading></el-button>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations\n                        :ellipsis=\"6\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        :fixed=\"mobile ? false : 'right'\"\n                        width=\"280px\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n            <ConfigSuperVisor v-if=\"setSuperVisor\" />\n        </LayoutContent>\n        <Create ref=\"createRef\" @close=\"search\"></Create>\n        <File ref=\"fileRef\" @search=\"search\"></File>\n        <Log ref=\"logRef\" @close=\"search\" />\n        <ProcessDetail ref=\"processDetailRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Log from './log/index.vue';\n\nimport SuperVisorStatus from './status/index.vue';\nimport { ref } from 'vue';\nimport ConfigSuperVisor from './config/index.vue';\nimport { computed, onMounted } from 'vue';\nimport Create from './create/index.vue';\nimport File from './file/index.vue';\nimport ProcessDetail from '@/views/host/process/process/detail/index.vue';\nimport { getSupervisorProcess, operateSupervisorProcess } from '@/api/modules/host-tool';\nimport { GlobalStore } from '@/store';\nimport i18n from '@/lang';\nimport { HostTool } from '@/api/interface/host-tool';\nimport { MsgSuccess } from '@/utils/message';\nimport { routerToFileWithPath } from '@/utils/router';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\nconst setSuperVisor = ref(false);\nconst createRef = ref();\nconst fileRef = ref();\nconst logRef = ref();\nconst processDetailRef = ref();\nconst data = ref();\nconst maskShow = ref(true);\nconst supervisorStatus = ref({\n    maskShow: true,\n    isExist: false,\n    isRunning: false,\n    init: true,\n});\nconst dataLoading = ref(false);\n\nconst setting = () => {\n    setSuperVisor.value = true;\n};\n\nconst getStatus = (status: any) => {\n    supervisorStatus.value = status;\n    search();\n};\n\nconst showStopped = computed((): boolean => {\n    if (supervisorStatus.value.init || setSuperVisor.value) {\n        return false;\n    }\n    if (supervisorStatus.value.isExist && !supervisorStatus.value.isRunning && maskShow.value) {\n        return true;\n    }\n    return false;\n});\n\nconst showTable = computed((): boolean => {\n    if (supervisorStatus.value.init || setSuperVisor.value || !supervisorStatus.value.isExist) {\n        return false;\n    }\n    if (supervisorStatus.value.isExist && !setSuperVisor.value) {\n        return true;\n    }\n    return true;\n});\n\nconst openCreate = () => {\n    createRef.value.acceptParams();\n};\n\nconst search = async () => {\n    if (!supervisorStatus.value.isExist) {\n        return;\n    }\n\n    let needLoadStatus = false;\n    dataLoading.value = true;\n    try {\n        const res = await getSupervisorProcess();\n        data.value = res.data;\n        for (const process of data.value) {\n            if (process.status && process.status.length > 0) {\n                process.hasLoad = true;\n            } else {\n                process.hasLoad = false;\n                needLoadStatus = true;\n            }\n        }\n        if (supervisorStatus.value.isRunning && needLoadStatus) {\n            setTimeout(loadStatus, 1000);\n        }\n    } catch (error) {\n    } finally {\n        dataLoading.value = false;\n    }\n};\n\nconst loadStatus = async () => {\n    let needLoadStatus = false;\n    try {\n        const res = await getSupervisorProcess();\n        const stats = res.data || [];\n        for (const process of data.value) {\n            for (const item of stats) {\n                if (process.name === item.name) {\n                    if (item.status && item.status.length > 0) {\n                        process.status = item.status;\n                        process.hasLoad = true;\n                    } else {\n                        needLoadStatus = true;\n                    }\n                }\n            }\n        }\n        if (supervisorStatus.value.isRunning && needLoadStatus) {\n            setTimeout(loadStatus, 2000);\n        }\n    } catch (error) {}\n};\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst checkStatus = (status: HostTool.ProcessStatus[]): string => {\n    if (!status || status.length === 0) return 'STOPPED';\n\n    const statusCounts = status.reduce((acc, curr) => {\n        acc[curr.status] = (acc[curr.status] || 0) + 1;\n        return acc;\n    }, {} as Record<string, number>);\n\n    if (statusCounts['STARTING']) return 'STARTING';\n    if (statusCounts['RUNNING'] === status.length) return 'RUNNING';\n    if (statusCounts['RUNNING'] > 0) return 'WARNING';\n    return 'STOPPED';\n};\n\nconst operate = async (operation: string, name: string) => {\n    try {\n        ElMessageBox.confirm(\n            i18n.global.t('tool.supervisor.operatorHelper', [name, i18n.global.t('commons.operate.' + operation)]),\n            i18n.global.t('commons.operate.' + operation),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        )\n            .then(() => {\n                loading.value = true;\n                operateSupervisorProcess({ operate: operation, name: name })\n                    .then(() => {\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                        search();\n                    })\n                    .catch(() => {})\n                    .finally(() => {\n                        loading.value = false;\n                    });\n            })\n            .catch(() => {});\n    } catch (error) {}\n};\n\nconst getFile = (name: string, file: string) => {\n    fileRef.value.acceptParams(name, file, 'get');\n};\n\nconst openLog = (name: string) => {\n    logRef.value.acceptParams(name);\n};\n\nconst edit = (row: HostTool.SupersivorProcess) => {\n    createRef.value.acceptParams('update', row);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: HostTool.SupersivorProcess) {\n            edit(row);\n        },\n    },\n    {\n        label: i18n.global.t('website.sourceFile'),\n        click: function (row: HostTool.SupersivorProcess) {\n            getFile(row.name, 'config');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.log'),\n        click: function (row: HostTool.SupersivorProcess) {\n            openLog(row.name);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: function (row: HostTool.SupersivorProcess) {\n            operate('restart', row.name);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: HostTool.SupersivorProcess) {\n            operate('delete', row.name);\n        },\n    },\n];\n\nconst toDoc = () => {\n    window.open(globalStore.docsUrl + '/user_manual/toolbox/supervisor/', '_blank', 'noopener,noreferrer');\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/log/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.log') + ' - ' + supervisorName\"\n        @close=\"handleClose\"\n        :size=\"globalStore.isFullScreen ? 'full' : 'large'\"\n        :fullScreen=\"true\"\n    >\n        <template #content>\n            <el-tabs v-model=\"tab\" type=\"card\" @tab-click=\"handleChange\">\n                <el-tab-pane :label=\"$t('logs.runLog')\" name=\"out.log\"></el-tab-pane>\n                <el-tab-pane :label=\"$t('logs.errLog')\" name=\"err.log\"></el-tab-pane>\n            </el-tabs>\n\n            <LogFile\n                :key=\"logKey\"\n                ref=\"logRef\"\n                v-if=\"openLog\"\n                :config=\"logConfig\"\n                v-model:loading=\"loading\"\n                v-model:has-content=\"hasContent\"\n                :height-diff=\"300\"\n            >\n                <template #button>\n                    <el-button @click=\"cleanLog\" icon=\"Delete\" :disabled=\"hasContent === false\">\n                        {{ $t('commons.button.clean') }}\n                    </el-button>\n                </template>\n            </LogFile>\n        </template>\n\n        <template #footer>\n            <span>\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <OpDialog ref=\"opRef\" @search=\"refreshLog\" />\n</template>\n<script lang=\"ts\" setup>\nimport LogFile from '@/components/log/file/index.vue';\n\nimport { onUnmounted, reactive, ref } from 'vue';\nimport { GlobalStore } from '@/store';\nimport { operateSupervisorProcessFile } from '@/api/modules/host-tool';\nimport i18n from '@/lang';\nimport { TabsPaneContext } from 'element-plus';\nconst globalStore = GlobalStore();\n\nconst logConfig = reactive({\n    type: 'supervisor',\n    id: undefined,\n    name: '',\n    colorMode: 'nginx',\n});\nconst loading = ref(false);\nconst open = ref(false);\nconst tab = ref('out.log');\nconst openLog = ref();\nconst supervisorName = ref('');\nconst opRef = ref();\nconst logKey = ref(0);\nconst hasContent = ref(false);\n\nconst em = defineEmits(['close']);\n\nconst handleChange = (tab: TabsPaneContext) => {\n    openLog.value = false;\n    logConfig.name = supervisorName.value + '.' + tab.props.name.toString();\n    logKey.value++;\n    openLog.value = true;\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = (name: string) => {\n    tab.value = 'out.log';\n    supervisorName.value = name;\n    logConfig.name = name + '.' + tab.value;\n    open.value = true;\n    openLog.value = true;\n};\n\nconst onCloseLog = async () => {\n    em('close');\n};\n\nconst refreshLog = () => {\n    logKey.value++;\n};\n\nconst cleanLog = async () => {\n    let log = tab.value === 'out.log' ? i18n.global.t('logs.runLog') : i18n.global.t('logs.errLog');\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.clean'),\n        names: [supervisorName.value],\n        msg: i18n.global.t('commons.msg.operatorHelper', [log, i18n.global.t('commons.button.clean')]),\n        api: operateSupervisorProcessFile,\n        params: { name: supervisorName.value, operate: 'clear', file: tab.value },\n    });\n};\n\nonUnmounted(() => {\n    onCloseLog();\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.fullScreen {\n    border: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/status/index.vue",
    "content": "<template>\n    <div>\n        <div class=\"app-status\" v-if=\"data.isExist\">\n            <el-card>\n                <div class=\"flex w-full flex-col gap-4 md:flex-row\">\n                    <div class=\"flex flex-wrap gap-4 ml-3\">\n                        <el-tag effect=\"dark\" type=\"success\">{{ 'Supervisor' }}</el-tag>\n                        <Status class=\"mt-0.5\" :key=\"data.status\" :status=\"data.status\"></Status>\n                        <el-tag>{{ $t('app.version') }}: {{ $t('commons.colon') }}{{ data.version }}</el-tag>\n                    </div>\n                    <div class=\"mt-0.5\" v-if=\"!data.init\">\n                        <el-button type=\"primary\" v-if=\"data.status != 'running'\" link @click=\"onOperate('start')\">\n                            {{ $t('commons.operate.start') }}\n                        </el-button>\n                        <el-button type=\"primary\" v-if=\"data.status == 'running'\" link @click=\"onOperate('stop')\">\n                            {{ $t('commons.operate.stop') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" link @click=\"onOperate('restart')\">\n                            {{ $t('commons.button.restart') }}\n                        </el-button>\n                        <el-divider direction=\"vertical\" />\n                        <el-button type=\"primary\" link @click=\"setting\">\n                            {{ $t('commons.button.set') }}\n                        </el-button>\n                    </div>\n                    <div class=\"mt-0.5\" v-else>\n                        <el-button type=\"primary\" link @click=\"init\">\n                            {{ $t('commons.button.init') }}\n                        </el-button>\n                    </div>\n                </div>\n            </el-card>\n        </div>\n        <LayoutContent\n            :title=\"$t('tool.supervisor.list')\"\n            :divider=\"true\"\n            v-if=\"!data.isExist || !data.ctlExist || data.init\"\n            v-loading=\"loading\"\n        >\n            <template #main>\n                <div class=\"app-warn\">\n                    <div class=\"flex flex-col gap-2 items-center justify-center w-full sm:flex-row\">\n                        <template v-if=\"!data.isExist\">\n                            <span>{{ $t('tool.supervisor.notSupport') }}</span>\n                        </template>\n\n                        <template v-else-if=\"!data.ctlExist\">\n                            <span>{{ $t('tool.supervisor.notSupportCtl') }}</span>\n                        </template>\n\n                        <template v-else-if=\"data.init\">\n                            <span>{{ $t('tool.supervisor.initHelper') }}</span>\n                            <span\n                                v-if=\"!globalStore.isFxplay\"\n                                class=\"flex items-center justify-center gap-0.5\"\n                                @click=\"toDoc()\"\n                            >\n                                <el-icon><Position /></el-icon>\n                                {{ $t('commons.button.helpDoc') }}\n                            </span>\n                        </template>\n                        <span\n                            @click=\"toLibrary()\"\n                            v-if=\"!data.isExist || !data.ctlExist\"\n                            class=\"flex items-center justify-center gap-0.5\"\n                        >\n                            <el-icon><Position /></el-icon>\n                            {{ $t('firewall.quickJump') }}\n                        </span>\n                    </div>\n                    <div>\n                        <img alt=\"\" src=\"@/assets/images/no_app.svg\" />\n                    </div>\n                </div>\n            </template>\n        </LayoutContent>\n        <InitPage ref=\"initRef\" @close=\"getStatus\"></InitPage>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { getSupervisorStatus, operateSupervisor } from '@/api/modules/host-tool';\nimport { onMounted, reactive, ref } from 'vue';\nimport Status from '@/components/status/index.vue';\nimport { ElMessageBox } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { HostTool } from '@/api/interface/host-tool';\nimport InitPage from './init/index.vue';\nimport { GlobalStore } from '@/store';\nimport { routerToNameWithQuery } from '@/utils/router';\n\nconst globalStore = GlobalStore();\n\nlet operateReq = reactive({\n    installId: 0,\n    operate: '',\n});\nconst initRef = ref();\nconst data = ref({\n    isExist: false,\n    version: '',\n    status: 'running',\n    init: false,\n    configPath: '',\n    ctlExist: false,\n    serviceName: '',\n});\nconst loading = ref(false);\n\nconst em = defineEmits(['setting', 'getStatus', 'update:loading', 'update:maskShow']);\n\nconst setting = () => {\n    em('setting', true);\n};\n\nconst toLibrary = () => {\n    routerToNameWithQuery('Library', { uncached: 'true' });\n};\n\nconst toDoc = () => {\n    window.open(globalStore.docsUrl + '/user_manual/toolbox/supervisor/', '_blank', 'noopener,noreferrer');\n};\n\nconst init = async () => {\n    initRef.value.acceptParams(data.value.configPath, data.value.serviceName);\n};\n\nconst onOperate = async (operation: string) => {\n    em('update:maskShow', false);\n    operateReq.operate = operation;\n    ElMessageBox.confirm(\n        i18n.global.t('tool.supervisor.operatorHelper', ['Supervisor', i18n.global.t('commons.operate.' + operation)]),\n        i18n.global.t('commons.operate.' + operation),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    )\n        .then(() => {\n            em('update:loading', true);\n            operateSupervisor(operation)\n                .then(() => {\n                    em('update:maskShow', true);\n                    getStatus();\n                    em('update:loading', false);\n                    MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                })\n                .catch(() => {\n                    em('update:loading', false);\n                });\n        })\n        .catch(() => {\n            em('update:maskShow', true);\n        });\n};\n\nconst getStatus = async () => {\n    try {\n        loading.value = true;\n        em('update:loading', true);\n        const res = await getSupervisorStatus();\n        if (res.data.config) {\n            data.value = res.data.config as HostTool.Supersivor;\n        }\n\n        const status = {\n            isExist: data.value.isExist && data.value.ctlExist,\n            isRunning: data.value.status === 'running',\n            init: data.value.init,\n        };\n        em('getStatus', status);\n    } catch (error) {}\n    em('update:loading', false);\n    loading.value = false;\n};\n\nonMounted(() => {\n    getStatus();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.tool-status {\n    margin-top: 20px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/toolbox/supervisor/status/init/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.init')\" @close=\"handleClose\" size=\"small\">\n        <el-form\n            ref=\"initForm\"\n            label-position=\"top\"\n            :model=\"initModel\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('tool.supervisor.primaryConfig')\" prop=\"primaryConfig\">\n                <el-input v-model.trim=\"initModel.primaryConfig\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.serviceName')\" prop=\"serviceName\">\n                <el-input v-model.trim=\"initModel.serviceName\"></el-input>\n                <span class=\"input-help\">{{ $t('tool.supervisor.serviceNameHelper') }}</span>\n            </el-form-item>\n            <el-alert :title=\"$t('tool.supervisor.initWarn')\" class=\"common-prompt\" :closable=\"false\" type=\"error\" />\n            <el-alert\n                :title=\"$t('tool.supervisor.restartHelper')\"\n                class=\"common-prompt\"\n                :closable=\"false\"\n                type=\"error\"\n            />\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose()\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"openSubmit(initForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n        <ConfirmDialog ref=\"confirmDialogRef\" @confirm=\"submit(initForm)\"></ConfirmDialog>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { initSupervisor } from '@/api/modules/host-tool';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst initForm = ref<FormInstance>();\nconst rules = ref({\n    primaryConfig: [Rules.requiredInput],\n    serviceName: [Rules.requiredInput],\n});\nconst initModel = ref({\n    primaryConfig: '',\n    serviceName: '',\n});\nconst confirmDialogRef = ref();\nconst em = defineEmits(['close']);\n\nconst acceptParams = (primaryConfig: string, serviceName: string) => {\n    initModel.value.primaryConfig = primaryConfig;\n    initModel.value.serviceName = serviceName;\n    open.value = true;\n};\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst openSubmit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        let params = {\n            header: i18n.global.t('commons.button.init'),\n            operationInfo: i18n.global.t('tool.supervisor.restartHelper'),\n            submitInputInfo: i18n.global.t('database.restartNow'),\n        };\n        confirmDialogRef.value!.acceptParams(params);\n    });\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        initSupervisor({\n            type: 'supervisord',\n            configPath: initModel.value.primaryConfig,\n            serviceName: initModel.value.serviceName,\n        })\n            .then(() => {\n                open.value = false;\n                em('close', true);\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/app/index.vue",
    "content": "<template>\n    <el-form-item :label=\"$t('app.app')\" prop=\"appID\">\n        <el-row :gutter=\"20\">\n            <el-col :span=\"12\">\n                <el-select\n                    v-model=\"runtime.appID\"\n                    :disabled=\"mode === 'edit'\"\n                    @change=\"changeApp(runtime.appID)\"\n                    class=\"p-w-200\"\n                >\n                    <el-option v-for=\"(app, index) in apps\" :key=\"index\" :label=\"app.name\" :value=\"app.id\"></el-option>\n                </el-select>\n            </el-col>\n            <el-col :span=\"12\">\n                <el-select\n                    v-model=\"runtime.version\"\n                    :disabled=\"mode === 'edit'\"\n                    @change=\"changeVersion()\"\n                    class=\"p-w-200\"\n                >\n                    <el-option\n                        v-for=\"(version, index) in appVersions\"\n                        :key=\"index\"\n                        :label=\"version\"\n                        :value=\"version\"\n                    ></el-option>\n                </el-select>\n            </el-col>\n        </el-row>\n    </el-form-item>\n</template>\n\n<script setup lang=\"ts\">\nimport { App } from '@/api/interface/app';\nimport { getAppByKey, getAppDetail, searchApp } from '@/api/modules/app';\nimport { useVModel } from '@vueuse/core';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { isOffLine } = useGlobalStore();\n\nconst props = defineProps({\n    mode: {\n        type: String,\n        required: true,\n    },\n    appKey: {\n        type: String,\n        required: true,\n    },\n    modelValue: {\n        type: Object,\n        required: true,\n    },\n});\nconst apps = ref<App.AppItem[]>([]);\nconst appVersions = ref<string[]>([]);\nconst emit = defineEmits(['update:modelValue']);\nconst runtime = useVModel(props, 'modelValue', emit);\nconst appReq = reactive({\n    type: props.appKey,\n    page: 1,\n    pageSize: 20,\n    resource: 'remote',\n});\n\nconst changeApp = (appID: number) => {\n    for (const app of apps.value) {\n        if (app.id === appID) {\n            getApp(app.key, props.mode);\n            break;\n        }\n    }\n};\n\nconst changeVersion = async () => {\n    try {\n        const res = await getAppDetail(runtime.value.appID, runtime.value.version, 'runtime');\n        runtime.value.appDetailID = res.data.id;\n    } catch (error) {}\n};\n\nconst getApp = async (appkey: string, mode: string) => {\n    try {\n        const res = await getAppByKey(appkey);\n        appVersions.value = res.data.versions || [];\n        if (res.data.versions.length > 0) {\n            if (mode === 'create') {\n                runtime.value.version = res.data.versions[0];\n                changeVersion();\n            }\n        }\n    } catch (error) {}\n};\n\nconst searchAppList = async (appID: number) => {\n    try {\n        if (isOffLine.value) {\n            appReq.resource = 'custom';\n        }\n        const res = await searchApp(appReq);\n        apps.value = res.data.items || [];\n        if (res.data && res.data.items && res.data.items.length > 0) {\n            if (appID == null) {\n                runtime.value.appID = res.data.items[0].id;\n                getApp(res.data.items[0].key, props.mode);\n            } else {\n                res.data.items.forEach((item) => {\n                    if (item.id === appID) {\n                        getApp(item.key, props.mode);\n                    }\n                });\n            }\n        }\n    } catch (error) {}\n};\n\nonMounted(() => {\n    if (props.mode === 'create') {\n        searchAppList(null);\n    } else {\n        searchAppList(runtime.value.appID);\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/common/utils.ts",
    "content": "import { ElMessageBox } from 'element-plus';\nimport i18n from '@/lang';\nimport { OperateRuntime, updateRemark } from '@/api/modules/runtime';\nimport { Ref } from 'vue';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Runtime } from '@/api/interface/runtime';\n\nexport const operateRuntime = async (operate: string, ID: number, loading: Ref<boolean>, search: () => void) => {\n    try {\n        const action = await ElMessageBox.confirm(\n            i18n.global.t('runtime.operatorHelper', [i18n.global.t('commons.operate.' + operate)]),\n            i18n.global.t('commons.operate.' + operate),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        );\n\n        if (action === 'confirm') {\n            loading.value = true;\n            await OperateRuntime({ operate: operate, ID: ID });\n            search();\n        }\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nexport const updateRuntimeRemark = async (row: Runtime.Runtime, bulr: Function) => {\n    bulr();\n    if (row.remark && row.remark.length > 128) {\n        MsgError(i18n.global.t('commons.rule.length128Err'));\n        return;\n    }\n    try {\n        await updateRemark({\n            id: row.id,\n            remark: row.remark,\n        }).then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        });\n    } catch (error) {}\n};\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/dir/index.vue",
    "content": "<template>\n    <el-form-item :label=\"$t('runtime.codeDir')\" prop=\"codeDir\">\n        <el-input v-model.trim=\"runtime.codeDir\" :disabled=\"mode === 'edit'\" @blur=\"changeDir\">\n            <template #prepend>\n                <el-button\n                    icon=\"Folder\"\n                    @click=\"fileRef.acceptParams({ path: runtime.codeDir, dir: true, disabled: mode === 'edit' })\"\n                />\n            </template>\n        </el-input>\n        <span class=\"input-help\">\n            {{ dirHelper }}\n        </span>\n    </el-form-item>\n    <div v-if=\"appKey == 'node'\">\n        <el-row :gutter=\"20\">\n            <el-col :span=\"18\">\n                <el-form-item :label=\"$t('runtime.runScript')\" prop=\"params.EXEC_SCRIPT\">\n                    <el-select v-model=\"runtime.params['EXEC_SCRIPT']\" v-if=\"runtime.params['CUSTOM_SCRIPT'] == '0'\">\n                        <el-option\n                            v-for=\"(script, index) in scripts\"\n                            :key=\"index\"\n                            :label=\"script.name + ' 【 ' + script.script + ' 】'\"\n                            :value=\"script.name\"\n                        >\n                            <el-row :gutter=\"10\">\n                                <el-col :span=\"4\">{{ script.name }}</el-col>\n                                <el-col :span=\"10\">{{ ' 【 ' + script.script + ' 】' }}</el-col>\n                            </el-row>\n                        </el-option>\n                    </el-select>\n                    <el-input v-else v-model=\"runtime.params['EXEC_SCRIPT']\"></el-input>\n                    <span class=\"input-help\" v-if=\"runtime.params['CUSTOM_SCRIPT'] == '0'\">\n                        {{ $t('runtime.runScriptHelper') }}\n                    </span>\n                    <span class=\"input-help\" v-else>\n                        {{ scriptHelper }}\n                    </span>\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"6\">\n                <el-form-item :label=\"$t('runtime.customScript')\" prop=\"params.CUSTOM_SCRIPT\">\n                    <el-switch\n                        v-model=\"runtime.params['CUSTOM_SCRIPT']\"\n                        :active-value=\"'1'\"\n                        :inactive-value=\"'0'\"\n                        @change=\"changeScriptType\"\n                    />\n                </el-form-item>\n            </el-col>\n        </el-row>\n    </div>\n    <div v-else>\n        <el-form-item :label=\"$t('runtime.runScript')\" prop=\"params.EXEC_SCRIPT\">\n            <el-input v-model=\"runtime.params['EXEC_SCRIPT']\"></el-input>\n            <span class=\"input-help\">\n                {{ scriptHelper }}\n            </span>\n        </el-form-item>\n    </div>\n    <FileList ref=\"fileRef\" @choose=\"getPath\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { Runtime } from '@/api/interface/runtime';\nimport FileList from '@/components/file-list/index.vue';\nimport { GetNodeScripts } from '@/api/modules/runtime';\nimport { useVModel } from '@vueuse/core';\n\nconst fileRef = ref();\n\nconst props = defineProps({\n    mode: {\n        type: String,\n        required: true,\n    },\n    modelValue: {\n        type: Object,\n        required: true,\n    },\n    dirHelper: {\n        type: String,\n        required: false,\n    },\n    scriptHelper: {\n        type: String,\n        required: true,\n    },\n    appKey: {\n        type: String,\n        required: false,\n    },\n});\nconst emit = defineEmits(['update:modelValue']);\nconst runtime = useVModel(props, 'modelValue', emit);\nconst scripts = ref<Runtime.NodeScripts[]>([]);\n\nwatch(\n    () => runtime.value.name,\n    (newVal) => {\n        if (newVal && props.mode == 'create') {\n            runtime.value.params['CONTAINER_NAME'] = newVal;\n        }\n    },\n    { deep: true },\n);\n\nconst changeDir = () => {\n    if (props.appKey == 'node') {\n        getScripts();\n    }\n};\n\nconst getPath = (codeDir: string) => {\n    runtime.value.codeDir = codeDir;\n    if (props.appKey == 'node') {\n        getScripts();\n    }\n};\n\nconst changeScriptType = () => {\n    runtime.value.params['EXEC_SCRIPT'] = '';\n    if (runtime.value.params['CUSTOM_SCRIPT'] == '0') {\n        getScripts();\n    }\n};\n\nconst getScripts = () => {\n    GetNodeScripts({ codeDir: runtime.value.codeDir }).then((res) => {\n        scripts.value = res.data;\n        if (props.mode == 'create' && scripts.value.length > 0) {\n            runtime.value.params['EXEC_SCRIPT'] = scripts.value[0].name;\n        }\n    });\n};\n\nonMounted(() => {\n    if (props.mode == 'edit' && props.appKey == 'node') {\n        getScripts();\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/environment/index.vue",
    "content": "<template>\n    <el-text type=\"info\">{{ $t('php.dateTimezoneHelper') }}</el-text>\n    <div class=\"mt-1.5\">\n        <el-row :gutter=\"20\" v-for=\"(env, index) in environments\" :key=\"index\">\n            <el-col :span=\"7\">\n                <el-form-item :prop=\"`environments.${index}.key`\" :rules=\"rules.value\">\n                    <el-input v-model=\"env.key\" :placeholder=\"$t('runtime.envKey')\" />\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"1\">\n                <div class=\"mt-1\">=</div>\n            </el-col>\n            <el-col :span=\"7\">\n                <el-form-item :prop=\"`environments.${index}.value`\" :rules=\"rules.value\">\n                    <el-input v-model=\"env.value\" :placeholder=\"$t('runtime.envValue')\" />\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"3\">\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"removeEnv(index)\" link class=\"mt-1\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </el-form-item>\n            </el-col>\n        </el-row>\n        <el-row :gutter=\"20\">\n            <el-col :span=\"4\">\n                <el-button @click=\"addEnv\">{{ $t('commons.button.add') }}</el-button>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive } from 'vue';\nimport { FormRules } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { Runtime } from '@/api/interface/runtime';\n\nconst props = defineProps({\n    environments: {\n        type: Array<Runtime.Environment>,\n        required: true,\n    },\n});\n\nconst rules = reactive<FormRules>({\n    value: [Rules.requiredInput],\n});\n\nconst addEnv = () => {\n    props.environments.push({\n        key: '',\n        value: '',\n    });\n};\n\nconst removeEnv = (index: number) => {\n    props.environments.splice(index, 1);\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/extra_hosts/index.vue",
    "content": "<template>\n    <div class=\"mt-1.5\">\n        <el-row :gutter=\"20\" v-for=\"(volume, index) in extraHosts\" :key=\"index\">\n            <el-col :span=\"7\">\n                <el-form-item :prop=\"`volumes.${index}.hostname`\" :rules=\"rules.hostname\">\n                    <el-input v-model=\"volume.hostname\" :placeholder=\"$t('toolbox.device.hostname')\" />\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"7\">\n                <el-form-item :prop=\"`volumes.${index}.ip`\" :rules=\"rules.ip\">\n                    <el-input v-model=\"volume.ip\" :placeholder=\"'IP'\" />\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"4\">\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"removeEnv(index)\" link class=\"mt-1\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </el-form-item>\n            </el-col>\n        </el-row>\n        <el-row :gutter=\"20\">\n            <el-col :span=\"4\">\n                <el-button @click=\"addEnv\">{{ $t('commons.button.add') }}</el-button>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive } from 'vue';\nimport { FormRules } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { Runtime } from '@/api/interface/runtime';\n\nconst props = defineProps({\n    extraHosts: {\n        type: Array<Runtime.ExtraHost>,\n        required: true,\n    },\n});\n\nconst rules = reactive<FormRules>({\n    value: [Rules.requiredInput],\n});\n\nconst addEnv = () => {\n    props.extraHosts.push({\n        hostname: '',\n        ip: '',\n    });\n};\n\nconst removeEnv = (index: number) => {\n    props.extraHosts.splice(index, 1);\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/node-config.vue",
    "content": "<template>\n    <el-tabs type=\"border-card\">\n        <el-tab-pane :label=\"$t('commons.table.port')\">\n            <PortConfig :exposedPorts=\"runtime.exposedPorts\" />\n        </el-tab-pane>\n        <el-tab-pane :label=\"$t('runtime.environment')\">\n            <Environment :environments=\"runtime.environments\" />\n        </el-tab-pane>\n        <el-tab-pane :label=\"$t('container.mount')\">\n            <Volumes :volumes=\"runtime.volumes\" />\n        </el-tab-pane>\n        <el-tab-pane :label=\"$t('runtime.extraHosts')\">\n            <ExtraHosts :extraHosts=\"runtime.extraHosts\" />\n        </el-tab-pane>\n    </el-tabs>\n</template>\n\n<script lang=\"ts\" setup>\nimport PortConfig from '@/views/website/runtime/components/port/index.vue';\nimport Environment from '@/views/website/runtime/components/environment/index.vue';\nimport Volumes from '@/views/website/runtime/components/volume/index.vue';\nimport ExtraHosts from '@/views/website/runtime/components/extra_hosts/index.vue';\nimport { useVModel } from '@vueuse/core';\nimport type { PropType } from 'vue';\n\ninterface RuntimeConfigFields {\n    exposedPorts: any[];\n    environments: any[];\n    volumes: any[];\n    extraHosts: any[];\n}\n\nconst props = defineProps({\n    modelValue: {\n        type: Object as PropType<RuntimeConfigFields>,\n        required: true,\n    },\n});\n\nconst emit = defineEmits<{\n    'update:modelValue': [value: RuntimeConfigFields];\n}>();\n\nconst runtime = useVModel(props, 'modelValue', emit);\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/port/index.vue",
    "content": "<template>\n    <div class=\"mt-1.5\">\n        <el-row :gutter=\"20\" v-for=\"(port, index) in exposedPorts\" :key=\"index\">\n            <el-col :span=\"7\">\n                <el-form-item :prop=\"`exposedPorts.${index}.hostPort`\" :rules=\"rules.port\">\n                    <el-input v-model.number=\"port.hostPort\" :placeholder=\"$t('runtime.externalPort')\" />\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"7\">\n                <el-form-item :prop=\"`exposedPorts.${index}.containerPort`\" :rules=\"rules.port\">\n                    <el-input v-model.number=\"port.containerPort\" :placeholder=\"$t('runtime.appPort')\" />\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"7\">\n                <el-text>{{ $t('app.allowPort') }}</el-text>\n                <el-switch\n                    class=\"ml-1\"\n                    :model-value=\"port.hostIP === ''\"\n                    @update:model-value=\"handleSwitchChange($event, index)\"\n                />\n            </el-col>\n            <el-col :span=\"2\">\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"removePort(index)\" link class=\"mt-1\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </el-form-item>\n            </el-col>\n        </el-row>\n        <el-row :gutter=\"20\">\n            <el-col :span=\"4\">\n                <el-button @click=\"addPort\">{{ $t('commons.button.add') }}</el-button>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { FormRules } from 'element-plus';\nimport { Runtime } from '@/api/interface/runtime';\n\nconst props = defineProps({\n    exposedPorts: {\n        type: Array<Runtime.ExposedPort>,\n        required: true,\n    },\n});\n\nconst rules = reactive<FormRules>({\n    port: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n});\n\nconst handleSwitchChange = (value: boolean, index: number) => {\n    props.exposedPorts[index].hostIP = value ? '' : '127.0.0.1';\n};\n\nconst addPort = () => {\n    props.exposedPorts.push({\n        hostPort: undefined,\n        containerPort: undefined,\n        hostIP: '',\n    });\n};\n\nconst removePort = (index: number) => {\n    props.exposedPorts.splice(index, 1);\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/port-jump.vue",
    "content": "<template>\n    <div v-if=\"row.port != ''\">\n        <span v-for=\"(port, index) in row.exposedPorts\" :key=\"index\">\n            <el-button icon=\"Position\" plain size=\"small\" @click=\"jump(port.hostPort, 'http')\">\n                {{ port.hostIP }}:{{ port.hostPort }}->{{ port.containerPort }}\n            </el-button>\n        </span>\n    </div>\n</template>\n\n<script setup>\ndefineProps({\n    row: {\n        type: Object,\n        required: true,\n    },\n    jump: {\n        type: Function,\n        required: true,\n    },\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/runtime-status.vue",
    "content": "<template>\n    <el-popover v-if=\"showMessage\" placement=\"bottom\" :width=\"400\" trigger=\"hover\" :content=\"row.message\">\n        <template #reference>\n            <Status :key=\"row.status\" :status=\"row.status\" />\n        </template>\n    </el-popover>\n\n    <div v-else>\n        <Status :key=\"row.status\" :status=\"row.status\" />\n    </div>\n</template>\n\n<script setup>\nimport { computed } from 'vue';\nimport Status from '@/components/status/index.vue';\n\nconst props = defineProps({\n    row: {\n        type: Object,\n        required: true,\n    },\n});\n\nconst showMessage = computed(() => {\n    return props.row.status === 'Error' || props.row.status === 'SystemRestart';\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/terminal.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"terminalVisible\"\n        :header=\"$t('menu.terminal')\"\n        @close=\"handleClose\"\n        :resource=\"title\"\n        fullScreen\n        :size=\"globalStore.isFullScreen ? 'full' : 'large'\"\n        :autoClose=\"false\"\n    >\n        <template #content>\n            <el-form ref=\"formRef\" :model=\"form\" label-position=\"top\" @submit.prevent>\n                <el-form-item :label=\"$t('commons.table.user')\" prop=\"user\">\n                    <el-input placeholder=\"root\" clearable v-model=\"form.user\" @keyup.enter=\"reConnect\">\n                        <template #append>\n                            <el-button @click=\"reConnect\">{{ $t('commons.button.conn') }}</el-button>\n                        </template>\n                    </el-input>\n                </el-form-item>\n            </el-form>\n            <Terminal class=\"terminal\" ref=\"terminalRef\" v-if=\"terminalOpen\"></Terminal>\n        </template>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button type=\"primary\" @click=\"handleClose\">\n                    {{ $t('commons.button.disConn') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { reactive, ref, nextTick } from 'vue';\nimport Terminal from '@/components/terminal/index.vue';\nimport { GlobalStore } from '@/store';\n\nconst globalStore = GlobalStore();\nconst title = ref();\nconst terminalVisible = ref(false);\nconst terminalOpen = ref(false);\nconst form = reactive({\n    isCustom: false,\n    command: '',\n    user: '',\n    containerID: '',\n});\nconst terminalRef = ref<InstanceType<typeof Terminal> | null>(null);\n\ninterface DialogProps {\n    containerID: string;\n    container: string;\n    user: string;\n}\nconst acceptParams = async (params: DialogProps): Promise<void> => {\n    terminalVisible.value = true;\n    form.containerID = params.containerID;\n    title.value = params.container;\n    form.isCustom = false;\n    if (params.user && params.user != '') {\n        form.user = params.user;\n    }\n    form.command = '/bin/bash';\n    initTerm();\n};\n\nconst initTerm = async () => {\n    terminalOpen.value = true;\n    await nextTick();\n    terminalRef.value!.acceptParams({\n        endpoint: '/api/v2/containers/exec',\n        args: `source=container&containerid=${form.containerID}&user=${form.user}&command=${form.command}`,\n        error: '',\n        initCmd: '',\n    });\n};\n\nconst reConnect = async () => {\n    if (terminalRef.value) {\n        terminalRef.value.onClose();\n    }\n    terminalOpen.value = false;\n    await nextTick();\n    initTerm();\n};\n\nconst onClose = () => {\n    terminalRef.value?.onClose();\n    terminalOpen.value = false;\n};\n\nfunction handleClose() {\n    onClose();\n    terminalVisible.value = false;\n}\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style lang=\"scss\" scoped>\n.terminal {\n    height: calc(100vh - 180px);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/components/volume/index.vue",
    "content": "<template>\n    <div class=\"mt-1.5\">\n        <el-row :gutter=\"20\" v-for=\"(volume, index) in volumes\" :key=\"index\">\n            <el-col :span=\"7\">\n                <el-form-item :prop=\"`volumes.${index}.source`\" :rules=\"rules.value\">\n                    <el-input v-model=\"volume.source\" :placeholder=\"$t('container.hostOption')\" />\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"7\">\n                <el-form-item :prop=\"`volumes.${index}.target`\" :rules=\"rules.value\">\n                    <el-input v-model=\"volume.target\" :placeholder=\"$t('container.containerDir')\" />\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"4\">\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"removeEnv(index)\" link class=\"mt-1\">\n                        {{ $t('commons.button.delete') }}\n                    </el-button>\n                </el-form-item>\n            </el-col>\n        </el-row>\n        <el-row :gutter=\"20\">\n            <el-col :span=\"4\">\n                <el-button @click=\"addEnv\">{{ $t('commons.button.add') }}</el-button>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { reactive } from 'vue';\nimport { FormRules } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { Runtime } from '@/api/interface/runtime';\n\nconst props = defineProps({\n    volumes: {\n        type: Array<Runtime.Volume>,\n        required: true,\n    },\n});\n\nconst rules = reactive<FormRules>({\n    value: [Rules.requiredInput],\n});\n\nconst addEnv = () => {\n    props.volumes.push({\n        source: '',\n        target: '',\n    });\n};\n\nconst removeEnv = (index: number) => {\n    props.volumes.splice(index, 1);\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/delete/index.vue",
    "content": "<template>\n    <DialogPro\n        v-model=\"open\"\n        :title=\"$t('commons.button.delete') + ' - ' + resourceName\"\n        size=\"small\"\n        @close=\"handleClose\"\n    >\n        <div :key=\"key\" :loading=\"loading\">\n            <el-form ref=\"deleteForm\" label-position=\"left\">\n                <el-form-item>\n                    <el-checkbox v-model=\"deleteReq.forceDelete\" :label=\"$t('website.forceDelete')\" />\n                    <span class=\"input-help\">\n                        {{ $t('website.forceDeleteHelper') }}\n                    </span>\n                </el-form-item>\n            </el-form>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :loading=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { DeleteRuntime } from '@/api/modules/runtime';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\n\nconst key = 1;\nconst open = ref(false);\nconst loading = ref(false);\nconst deleteReq = ref({\n    id: 0,\n    forceDelete: false,\n});\nconst em = defineEmits(['close']);\nconst deleteForm = ref<FormInstance>();\nconst resourceName = ref('');\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = async (id: number, name: string) => {\n    deleteReq.value = {\n        id: id,\n        forceDelete: false,\n    };\n    resourceName.value = name;\n    open.value = true;\n};\n\nconst submit = () => {\n    loading.value = true;\n    DeleteRuntime(deleteReq.value)\n        .then(() => {\n            handleClose();\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/dotnet/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <DockerStatus v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" />\n        <LayoutContent v-loading=\"loading\" v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"dotnet-runtime-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"items\" @search=\"search()\" :heightDiff=\"260\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"name\"\n                        min-width=\"120px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"openDetail(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.runDir')\" prop=\"codeDir\" min-width=\"120px\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"routerToFileWithPath(row.codeDir)\">\n                                <el-icon>\n                                    <FolderOpened />\n                                </el-icon>\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('app.version')\" prop=\"version\"></el-table-column>\n                    <el-table-column :label=\"$t('runtime.externalPort')\" prop=\"port\" min-width=\"110px\">\n                        <template #default=\"{ row }\">\n                            <PortJump :row=\"row\" :jump=\"goDashboard\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <RuntimeStatus :row=\"row\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" prop=\"path\" min-width=\"90px\">\n                        <template #default=\"{ row }\">\n                            <el-button @click=\"openLog(row)\" link type=\"primary\" v-if=\"row.status != 'Stopped'\">\n                                {{ $t('website.check') }}\n                            </el-button>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.remark')\" prop=\"remark\" min-width=\"150px\">\n                        <template #default=\"{ row }\">\n                            <fu-read-write-switch>\n                                <template #read>\n                                    <MsgInfo :info=\"row.remark\" :width=\"'150'\" />\n                                </template>\n                                <template #default=\"{ read }\">\n                                    <el-input v-model=\"row.remark\" @blur=\"updateRuntimeRemark(row, read)\" />\n                                </template>\n                            </fu-read-write-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        min-width=\"120\"\n                        fix\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 5\"\n                        :min-width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        fixed=\"right\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <OperateDonet ref=\"operateRef\" @close=\"search\" />\n        <Delete ref=\"deleteRef\" @close=\"search\" />\n        <ComposeLogs ref=\"composeLogRef\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <AppResources ref=\"checkRef\" @close=\"search\" />\n        <Terminal ref=\"terminalRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, reactive, ref, computed } from 'vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport { RuntimeDeleteCheck, SearchRuntimes, SyncRuntime } from '@/api/modules/runtime';\nimport { dateFormat } from '@/utils/util';\nimport OperateDonet from '@/views/website/runtime/dotnet/operate/index.vue';\nimport Delete from '@/views/website/runtime/delete/index.vue';\nimport i18n from '@/lang';\nimport RouterMenu from '../index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport AppResources from '@/views/website/runtime/php/check/index.vue';\nimport { GlobalStore } from '@/store';\nimport RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';\nimport PortJump from '@/views/website/runtime/components/port-jump.vue';\nimport Terminal from '@/views/website/runtime/components/terminal.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { disabledButton } from '@/utils/runtime';\nimport { operateRuntime, updateRuntimeRemark } from '../common/utils';\nimport { routerToFileWithPath } from '@/utils/router';\n\nconst loading = ref(false);\nconst items = ref<Runtime.RuntimeDTO[]>([]);\nconst operateRef = ref();\nconst deleteRef = ref();\nconst dialogPortJumpRef = ref();\nconst composeLogRef = ref();\nconst checkRef = ref();\nconst terminalRef = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'runtime-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('runtime-page-size')) || 20,\n    total: 0,\n});\nconst req = reactive<Runtime.RuntimeReq>({\n    name: '',\n    page: 1,\n    pageSize: 40,\n    type: 'dotnet',\n});\nconst buttons = [\n    {\n        label: i18n.global.t('commons.operate.stop'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('down', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'stop');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.start'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('up', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'start');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.restart'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('restart', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'restart');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Runtime.Runtime) {\n            openDetail(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'edit');\n        },\n    },\n    {\n        label: i18n.global.t('menu.terminal'),\n        click: function (row: Runtime.Runtime) {\n            openTerminal(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'config');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Runtime.Runtime) {\n            openDelete(row);\n        },\n    },\n];\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    try {\n        const res = await SearchRuntimes(req);\n        items.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst sync = () => {\n    SyncRuntime();\n};\n\nconst openCreate = () => {\n    operateRef.value.acceptParams({ type: 'dotnet', mode: 'create' });\n};\n\nconst openDetail = (row: Runtime.Runtime) => {\n    operateRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });\n};\n\nconst openDelete = (row: Runtime.Runtime) => {\n    RuntimeDeleteCheck(row.id).then(async (res) => {\n        const items = res.data;\n        if (res.data && res.data.length > 0) {\n            checkRef.value.acceptParams({ items: items, key: 'website', installID: row.id });\n        } else {\n            deleteRef.value.acceptParams(row.id, row.name);\n        }\n    });\n};\n\nconst openTerminal = (row: Runtime.Runtime) => {\n    const container = row.params['CONTAINER_NAME'];\n    terminalRef.value.acceptParams({ containerID: container, container: container });\n};\n\nconst openLog = (row: any) => {\n    composeLogRef.value.acceptParams({\n        compose: row.path + '/docker-compose.yml',\n        resource: row.name,\n        container: row.container,\n    });\n};\n\nconst goDashboard = async (port: any, protocol: string) => {\n    dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });\n};\n\nonMounted(() => {\n    sync();\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped></style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/dotnet/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.' + mode)\"\n        size=\"large\"\n        :resource=\"mode === 'edit' ? runtime.name : ''\"\n        @close=\"handleClose\"\n    >\n        <el-form\n            v-loading=\"loading\"\n            ref=\"runtimeForm\"\n            label-position=\"top\"\n            :model=\"runtime\"\n            label-width=\"125px\"\n            :rules=\"rules\"\n            :validate-on-rule-change=\"false\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input :disabled=\"mode === 'edit'\" v-model=\"runtime.name\"></el-input>\n            </el-form-item>\n            <AppConfig v-model=\"runtime\" :mode=\"mode\" appKey=\"dotnet\" />\n            <DirConfig v-model=\"runtime\" :mode=\"mode\" :scriptHelper=\"$t('runtime.dotnetHelper')\" />\n            <el-form-item :label=\"$t('app.containerName')\" prop=\"params.CONTAINER_NAME\">\n                <el-input v-model.trim=\"runtime.params['CONTAINER_NAME']\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                <el-input type=\"textarea\" :rows=\"1\" clearable v-model=\"runtime.remark\" />\n            </el-form-item>\n            <NodeConfig v-model=\"runtime\" />\n        </el-form>\n        <template #footer>\n            <span>\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(runtimeForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport AppConfig from '@/views/website/runtime/app/index.vue';\nimport NodeConfig from '../../components/node-config.vue';\nimport DirConfig from '@/views/website/runtime/components/dir/index.vue';\n\nimport { App } from '@/api/interface/app';\nimport { Runtime } from '@/api/interface/runtime';\nimport { CreateRuntime, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref, watch } from 'vue';\n\ninterface OperateRrops {\n    id?: number;\n    mode: string;\n    type: string;\n}\n\nconst open = ref(false);\nconst runtimeForm = ref<FormInstance>();\nconst loading = ref(false);\nconst mode = ref('create');\nconst editParams = ref<App.InstallParams[]>();\nconst initData = (type: string) => ({\n    name: '',\n    appDetailID: undefined,\n    image: '',\n    params: {\n        HOST_IP: '0.0.0.0',\n    },\n    type: type,\n    resource: 'appstore',\n    rebuild: false,\n    codeDir: '/',\n    port: 8080,\n    exposedPorts: [],\n    environments: [],\n    extraHosts: [],\n    volumes: [],\n    remark: '',\n});\nlet runtime = reactive<Runtime.RuntimeCreate>(initData('dotnet'));\nconst rules = ref<any>({\n    name: [Rules.requiredInput, Rules.appName],\n    appID: [Rules.requiredSelect],\n    codeDir: [Rules.requiredInput],\n    port: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n    source: [Rules.requiredSelect],\n    params: {\n        APP_PORT: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n        HOST_IP: [Rules.requiredSelect],\n        CONTAINER_NAME: [Rules.requiredInput, Rules.containerName],\n        EXEC_SCRIPT: [Rules.requiredInput],\n    },\n});\nconst scripts = ref<Runtime.NodeScripts[]>([]);\nconst em = defineEmits(['close']);\n\nwatch(\n    () => runtime.name,\n    (newVal) => {\n        if (newVal && mode.value == 'create') {\n            runtime.params['CONTAINER_NAME'] = newVal;\n        }\n    },\n    { deep: true },\n);\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n    runtimeForm.value?.resetFields();\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {\n            const containerPortMap = new Map();\n            const hostPortMap = new Map();\n            for (const port of runtime.exposedPorts) {\n                if (containerPortMap[port.containerPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                if (hostPortMap[port.hostPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                hostPortMap[port.hostPort] = true;\n                containerPortMap[port.containerPort] = true;\n            }\n        }\n\n        if (mode.value == 'create') {\n            loading.value = true;\n            CreateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        } else {\n            loading.value = true;\n            UpdateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\nconst getRuntime = async (id: number) => {\n    try {\n        const res = await GetRuntime(id);\n        const data = res.data;\n        Object.assign(runtime, {\n            id: data.id,\n            name: data.name,\n            appDetailID: data.appDetailID,\n            image: data.image,\n            type: data.type,\n            resource: data.resource,\n            appID: data.appID,\n            version: data.version,\n            rebuild: true,\n            source: data.source,\n            params: data.params,\n            codeDir: data.codeDir,\n            port: data.port,\n            remark: data.remark,\n        });\n        runtime.exposedPorts = data.exposedPorts || [];\n        runtime.environments = data.environments || [];\n        runtime.volumes = data.volumes || [];\n        runtime.extraHosts = data.extraHosts || [];\n        editParams.value = data.appParams;\n        open.value = true;\n    } catch (error) {}\n};\n\nconst acceptParams = async (props: OperateRrops) => {\n    mode.value = props.mode;\n    scripts.value = [];\n    if (props.mode === 'create') {\n        Object.assign(runtime, initData(props.type));\n        open.value = true;\n    } else {\n        getRuntime(props.id);\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/go/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <DockerStatus v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" />\n        <LayoutContent v-loading=\"loading\" v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"go-runtime-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"items\" @search=\"search()\" :heightDiff=\"260\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"name\"\n                        min-width=\"120px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"openDetail(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('runtime.codeDir')\" prop=\"codeDir\" min-width=\"120px\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"routerToFileWithPath(row.codeDir)\">\n                                <el-icon>\n                                    <FolderOpened />\n                                </el-icon>\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('app.version')\" prop=\"version\"></el-table-column>\n                    <el-table-column :label=\"$t('runtime.externalPort')\" prop=\"port\" min-width=\"110px\">\n                        <template #default=\"{ row }\">\n                            <PortJump :row=\"row\" :jump=\"goDashboard\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <RuntimeStatus :row=\"row\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" prop=\"path\" min-width=\"90px\">\n                        <template #default=\"{ row }\">\n                            <el-button @click=\"openLog(row)\" link type=\"primary\" v-if=\"row.status != 'Stopped'\">\n                                {{ $t('website.check') }}\n                            </el-button>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.remark')\" prop=\"remark\" min-width=\"150px\">\n                        <template #default=\"{ row }\">\n                            <fu-read-write-switch>\n                                <template #read>\n                                    <MsgInfo :info=\"row.remark\" :width=\"'150'\" />\n                                </template>\n                                <template #default=\"{ read }\">\n                                    <el-input v-model=\"row.remark\" @blur=\"updateRuntimeRemark(row, read)\" />\n                                </template>\n                            </fu-read-write-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        min-width=\"120\"\n                        fix\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 5\"\n                        :min-width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        fixed=\"right\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <Operate ref=\"operateRef\" @close=\"search\" />\n        <Delete ref=\"deleteRef\" @close=\"search\" />\n        <ComposeLogs ref=\"composeLogRef\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <AppResources ref=\"checkRef\" @close=\"search\" />\n        <Terminal ref=\"terminalRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, reactive, ref } from 'vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport { RuntimeDeleteCheck, SearchRuntimes, SyncRuntime } from '@/api/modules/runtime';\nimport { dateFormat } from '@/utils/util';\nimport Operate from '@/views/website/runtime/go/operate/index.vue';\nimport Delete from '@/views/website/runtime/delete/index.vue';\nimport i18n from '@/lang';\nimport RouterMenu from '../index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport AppResources from '@/views/website/runtime/php/check/index.vue';\nimport RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';\nimport PortJump from '@/views/website/runtime/components/port-jump.vue';\nimport Terminal from '@/views/website/runtime/components/terminal.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { disabledButton } from '@/utils/runtime';\nimport { GlobalStore } from '@/store';\nimport { operateRuntime, updateRuntimeRemark } from '../common/utils';\nimport { routerToFileWithPath } from '@/utils/router';\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref(false);\nconst items = ref<Runtime.RuntimeDTO[]>([]);\nconst operateRef = ref();\nconst deleteRef = ref();\nconst dialogPortJumpRef = ref();\nconst composeLogRef = ref();\nconst checkRef = ref();\nconst terminalRef = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'runtime-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('runtime-page-size')) || 20,\n    total: 0,\n});\nconst req = reactive<Runtime.RuntimeReq>({\n    name: '',\n    page: 1,\n    pageSize: 40,\n    type: 'go',\n});\nconst buttons = [\n    {\n        label: i18n.global.t('commons.operate.stop'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('down', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'stop');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.start'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('up', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'start');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('restart', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'restart');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Runtime.Runtime) {\n            openDetail(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'edit');\n        },\n    },\n    {\n        label: i18n.global.t('menu.terminal'),\n        click: function (row: Runtime.Runtime) {\n            openTerminal(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'config');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Runtime.Runtime) {\n            openDelete(row);\n        },\n    },\n];\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    try {\n        const res = await SearchRuntimes(req);\n        items.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst sync = () => {\n    SyncRuntime();\n};\n\nconst openCreate = () => {\n    operateRef.value.acceptParams({ type: 'go', mode: 'create' });\n};\n\nconst openDetail = (row: Runtime.Runtime) => {\n    operateRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });\n};\n\nconst openDelete = async (row: Runtime.Runtime) => {\n    RuntimeDeleteCheck(row.id).then(async (res) => {\n        const items = res.data;\n        if (res.data && res.data.length > 0) {\n            checkRef.value.acceptParams({ items: items, key: 'website', installID: row.id });\n        } else {\n            deleteRef.value.acceptParams(row.id, row.name);\n        }\n    });\n};\n\nconst openLog = (row: any) => {\n    composeLogRef.value.acceptParams({\n        compose: row.path + '/docker-compose.yml',\n        resource: row.name,\n        container: row.container,\n    });\n};\n\nconst goDashboard = async (port: any, protocol: string) => {\n    dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });\n};\n\nconst openTerminal = (row: Runtime.Runtime) => {\n    const container = row.params['CONTAINER_NAME'];\n    terminalRef.value.acceptParams({ containerID: container, container: container });\n};\n\nonMounted(() => {\n    sync();\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped></style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/go/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.' + mode)\"\n        size=\"large\"\n        :resource=\"mode === 'edit' ? runtime.name : ''\"\n        @close=\"handleClose\"\n    >\n        <el-form\n            v-loading=\"loading\"\n            ref=\"runtimeForm\"\n            label-position=\"top\"\n            :model=\"runtime\"\n            label-width=\"125px\"\n            :rules=\"rules\"\n            :validate-on-rule-change=\"false\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input :disabled=\"mode === 'edit'\" v-model=\"runtime.name\"></el-input>\n            </el-form-item>\n            <AppConfig v-model=\"runtime\" :mode=\"mode\" appKey=\"go\" />\n            <el-form-item :label=\"$t('tool.supervisor.dir')\" prop=\"codeDir\">\n                <el-input v-model.trim=\"runtime.codeDir\" :disabled=\"mode === 'edit'\">\n                    <template #prepend>\n                        <el-button\n                            icon=\"Folder\"\n                            @click=\"\n                                fileRef.acceptParams({ disabled: mode === 'edit', path: runtime.codeDir, dir: true })\n                            \"\n                        />\n                    </template>\n                </el-input>\n                <span class=\"input-help\">\n                    {{ $t('runtime.goDirHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('runtime.runScript')\" prop=\"params.EXEC_SCRIPT\">\n                <el-input v-model=\"runtime.params['EXEC_SCRIPT']\"></el-input>\n                <span class=\"input-help\">\n                    {{ $t('runtime.goHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('app.containerName')\" prop=\"params.CONTAINER_NAME\">\n                <el-input v-model.trim=\"runtime.params['CONTAINER_NAME']\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                <el-input type=\"textarea\" :rows=\"1\" clearable v-model=\"runtime.remark\" />\n            </el-form-item>\n            <NodeConfig v-model=\"runtime\" />\n        </el-form>\n\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit(runtimeForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"getPath\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport NodeConfig from '../../components/node-config.vue';\nimport AppConfig from '@/views/website/runtime/app/index.vue';\nimport FileList from '@/components/file-list/index.vue';\n\nimport { App } from '@/api/interface/app';\nimport { Runtime } from '@/api/interface/runtime';\nimport { CreateRuntime, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref, watch } from 'vue';\n\ninterface OperateRrops {\n    id?: number;\n    mode: string;\n    type: string;\n}\n\nconst open = ref(false);\nconst fileRef = ref();\nconst runtimeForm = ref<FormInstance>();\nconst loading = ref(false);\nconst mode = ref('create');\nconst editParams = ref<App.InstallParams[]>();\nconst initData = (type: string) => ({\n    name: '',\n    appDetailID: undefined,\n    image: '',\n    params: {\n        HOST_IP: '0.0.0.0',\n    },\n    type: type,\n    resource: 'appstore',\n    rebuild: false,\n    codeDir: '/',\n    port: 8080,\n    exposedPorts: [],\n    environments: [],\n    volumes: [],\n    remark: '',\n    extraHosts: [],\n});\nlet runtime = reactive<Runtime.RuntimeCreate>(initData('go'));\nconst rules = ref<any>({\n    name: [Rules.requiredInput, Rules.appName],\n    appID: [Rules.requiredSelect],\n    codeDir: [Rules.requiredInput],\n    port: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n    source: [Rules.requiredSelect],\n    params: {\n        APP_PORT: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n        HOST_IP: [Rules.requiredSelect],\n        CONTAINER_NAME: [Rules.requiredInput, Rules.containerName],\n        EXEC_SCRIPT: [Rules.requiredInput],\n    },\n});\nconst scripts = ref<Runtime.NodeScripts[]>([]);\nconst em = defineEmits(['close']);\n\nwatch(\n    () => runtime.name,\n    (newVal) => {\n        if (newVal && mode.value == 'create') {\n            runtime.params['CONTAINER_NAME'] = newVal;\n        }\n    },\n    { deep: true },\n);\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n    runtimeForm.value?.resetFields();\n};\n\nconst getPath = (codeDir: string) => {\n    runtime.codeDir = codeDir;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {\n            const containerPortMap = new Map();\n            const hostPortMap = new Map();\n            for (const port of runtime.exposedPorts) {\n                if (containerPortMap[port.containerPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                if (hostPortMap[port.hostPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                hostPortMap[port.hostPort] = true;\n                containerPortMap[port.containerPort] = true;\n            }\n        }\n\n        if (mode.value == 'create') {\n            loading.value = true;\n            CreateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        } else {\n            loading.value = true;\n            UpdateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\nconst getRuntime = async (id: number) => {\n    try {\n        const res = await GetRuntime(id);\n        const data = res.data;\n        Object.assign(runtime, {\n            id: data.id,\n            name: data.name,\n            appDetailID: data.appDetailID,\n            image: data.image,\n            type: data.type,\n            resource: data.resource,\n            appID: data.appID,\n            version: data.version,\n            rebuild: true,\n            source: data.source,\n            params: data.params,\n            codeDir: data.codeDir,\n            port: data.port,\n            remark: data.remark,\n        });\n        runtime.exposedPorts = data.exposedPorts || [];\n        runtime.environments = data.environments || [];\n        runtime.volumes = data.volumes || [];\n        runtime.extraHosts = data.extraHosts || [];\n        editParams.value = data.appParams;\n        open.value = true;\n    } catch (error) {}\n};\n\nconst acceptParams = async (props: OperateRrops) => {\n    mode.value = props.mode;\n    scripts.value = [];\n    if (props.mode === 'create') {\n        Object.assign(runtime, initData(props.type));\n        open.value = true;\n    } else {\n        getRuntime(props.id);\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"buttons\" />\n        <LayoutContent>\n            <router-view></router-view>\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nconst buttons = [\n    {\n        label: 'PHP',\n        path: '/websites/runtimes/php',\n    },\n    {\n        label: 'Java',\n        path: '/websites/runtimes/java',\n    },\n    {\n        label: 'Node.js',\n        path: '/websites/runtimes/node',\n    },\n    {\n        label: 'Go',\n        path: '/websites/runtimes/go',\n    },\n    {\n        label: 'Python',\n        path: '/websites/runtimes/python',\n    },\n    {\n        label: '.NET',\n        path: '/websites/runtimes/dotnet',\n    },\n];\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/java/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <DockerStatus v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" />\n        <LayoutContent v-loading=\"loading\" v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"java-runtime-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"items\" @search=\"search()\" :heightDiff=\"260\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"name\"\n                        min-width=\"120px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"openDetail(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('runtime.codeDir')\" prop=\"codeDir\" min-width=\"120px\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"routerToFileWithPath(row.codeDir)\">\n                                <el-icon>\n                                    <FolderOpened />\n                                </el-icon>\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('app.version')\" prop=\"version\"></el-table-column>\n                    <el-table-column :label=\"$t('runtime.externalPort')\" prop=\"port\" min-width=\"110px\">\n                        <template #default=\"{ row }\">\n                            <PortJump :row=\"row\" :jump=\"goDashboard\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <RuntimeStatus :row=\"row\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" prop=\"path\" min-width=\"90px\">\n                        <template #default=\"{ row }\">\n                            <el-button @click=\"openLog(row)\" link type=\"primary\" v-if=\"row.status != 'Stopped'\">\n                                {{ $t('website.check') }}\n                            </el-button>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.remark')\" prop=\"remark\" min-width=\"150px\">\n                        <template #default=\"{ row }\">\n                            <fu-read-write-switch>\n                                <template #read>\n                                    <MsgInfo :info=\"row.remark\" :width=\"'150'\" />\n                                </template>\n                                <template #default=\"{ read }\">\n                                    <el-input v-model=\"row.remark\" @blur=\"updateRuntimeRemark(row, read)\" />\n                                </template>\n                            </fu-read-write-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        min-width=\"120\"\n                        fix\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 5\"\n                        :min-width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        fixed=\"right\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <OperateJava ref=\"operateRef\" @close=\"search\" />\n        <Delete ref=\"deleteRef\" @close=\"search\" />\n        <ComposeLogs ref=\"composeLogRef\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <AppResources ref=\"checkRef\" @close=\"search\" />\n        <Terminal ref=\"terminalRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, reactive, ref } from 'vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport { RuntimeDeleteCheck, SearchRuntimes, SyncRuntime } from '@/api/modules/runtime';\nimport { dateFormat } from '@/utils/util';\nimport OperateJava from '@/views/website/runtime/java/operate/index.vue';\nimport Delete from '@/views/website/runtime/delete/index.vue';\nimport i18n from '@/lang';\nimport RouterMenu from '../index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport AppResources from '@/views/website/runtime/php/check/index.vue';\nimport RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';\nimport PortJump from '@/views/website/runtime/components/port-jump.vue';\nimport Terminal from '@/views/website/runtime/components/terminal.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { disabledButton } from '@/utils/runtime';\nimport { GlobalStore } from '@/store';\nimport { operateRuntime, updateRuntimeRemark } from '../common/utils';\nimport { routerToFileWithPath } from '@/utils/router';\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref(false);\nconst items = ref<Runtime.RuntimeDTO[]>([]);\nconst operateRef = ref();\nconst deleteRef = ref();\nconst dialogPortJumpRef = ref();\nconst composeLogRef = ref();\nconst checkRef = ref();\nconst terminalRef = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'runtime-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('runtime-page-size')) || 20,\n    total: 0,\n});\nconst req = reactive<Runtime.RuntimeReq>({\n    name: '',\n    page: 1,\n    pageSize: 40,\n    type: 'java',\n});\nconst buttons = [\n    {\n        label: i18n.global.t('commons.operate.stop'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('down', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'stop');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.start'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('up', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'start');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('restart', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'restart');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Runtime.Runtime) {\n            openDetail(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'edit');\n        },\n    },\n    {\n        label: i18n.global.t('menu.terminal'),\n        click: function (row: Runtime.Runtime) {\n            openTerminal(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'config');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Runtime.Runtime) {\n            openDelete(row);\n        },\n    },\n];\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    try {\n        const res = await SearchRuntimes(req);\n        items.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst sync = () => {\n    SyncRuntime();\n};\n\nconst openCreate = () => {\n    operateRef.value.acceptParams({ type: 'java', mode: 'create' });\n};\n\nconst openDetail = (row: Runtime.Runtime) => {\n    operateRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });\n};\n\nconst openDelete = (row: Runtime.Runtime) => {\n    RuntimeDeleteCheck(row.id).then(async (res) => {\n        const items = res.data;\n        if (res.data && res.data.length > 0) {\n            checkRef.value.acceptParams({ items: items, key: 'website', installID: row.id });\n        } else {\n            deleteRef.value.acceptParams(row.id, row.name);\n        }\n    });\n};\n\nconst openLog = (row: any) => {\n    composeLogRef.value.acceptParams({\n        compose: row.path + '/docker-compose.yml',\n        resource: row.name,\n        container: row.container,\n    });\n};\n\nconst openTerminal = (row: Runtime.Runtime) => {\n    const container = row.params['CONTAINER_NAME'];\n    terminalRef.value.acceptParams({ containerID: container, container: container });\n};\n\nconst goDashboard = async (port: any, protocol: string) => {\n    dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });\n};\n\nonMounted(() => {\n    sync();\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped></style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/java/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.' + mode)\"\n        size=\"large\"\n        :resource=\"mode === 'edit' ? runtime.name : ''\"\n        @close=\"handleClose\"\n    >\n        <el-form\n            ref=\"runtimeForm\"\n            label-position=\"top\"\n            :model=\"runtime\"\n            label-width=\"125px\"\n            :rules=\"rules\"\n            :validate-on-rule-change=\"false\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input :disabled=\"mode === 'edit'\" v-model=\"runtime.name\"></el-input>\n            </el-form-item>\n            <AppConfig v-model=\"runtime\" :mode=\"mode\" appKey=\"java\" />\n            <DirConfig\n                v-model=\"runtime\"\n                :mode=\"mode\"\n                :dirHelper=\"$t('runtime.javaDirHelper')\"\n                :scriptHelper=\"$t('runtime.javaScriptHelper')\"\n            />\n            <el-form-item :label=\"$t('app.containerName')\" prop=\"params.CONTAINER_NAME\">\n                <el-input v-model.trim=\"runtime.params['CONTAINER_NAME']\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                <el-input type=\"textarea\" :rows=\"1\" clearable v-model=\"runtime.remark\" />\n            </el-form-item>\n            <NodeConfig v-model=\"runtime\" />\n        </el-form>\n        <template #footer>\n            <span>\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(runtimeForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport AppConfig from '@/views/website/runtime/app/index.vue';\nimport DirConfig from '@/views/website/runtime/components/dir/index.vue';\nimport NodeConfig from '../../components/node-config.vue';\n\nimport { App } from '@/api/interface/app';\nimport { Runtime } from '@/api/interface/runtime';\nimport { CreateRuntime, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref, watch } from 'vue';\n\ninterface OperateRrops {\n    id?: number;\n    mode: string;\n    type: string;\n}\n\nconst open = ref(false);\nconst runtimeForm = ref<FormInstance>();\nconst loading = ref(false);\nconst mode = ref('create');\nconst editParams = ref<App.InstallParams[]>();\nconst initData = (type: string) => ({\n    name: '',\n    appDetailID: undefined,\n    image: '',\n    params: {\n        HOST_IP: '0.0.0.0',\n    },\n    type: type,\n    resource: 'appstore',\n    rebuild: false,\n    codeDir: '/',\n    port: 8080,\n    exposedPorts: [],\n    environments: [],\n    extraHosts: [],\n    volumes: [],\n    remark: '',\n});\nlet runtime = reactive<Runtime.RuntimeCreate>(initData('java'));\nconst rules = ref<any>({\n    name: [Rules.requiredInput, Rules.appName],\n    appID: [Rules.requiredSelect],\n    codeDir: [Rules.requiredInput],\n    port: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n    source: [Rules.requiredSelect],\n    params: {\n        HOST_IP: [Rules.requiredSelect],\n        CONTAINER_NAME: [Rules.requiredInput, Rules.containerName],\n        EXEC_SCRIPT: [Rules.requiredInput],\n    },\n});\nconst scripts = ref<Runtime.NodeScripts[]>([]);\nconst em = defineEmits(['close']);\n\nwatch(\n    () => runtime.params['APP_PORT'],\n    (newVal) => {\n        if (newVal && mode.value == 'create') {\n            runtime.port = newVal;\n        }\n    },\n    { deep: true },\n);\n\nwatch(\n    () => runtime.name,\n    (newVal) => {\n        if (newVal && mode.value == 'create') {\n            runtime.params['CONTAINER_NAME'] = newVal;\n        }\n    },\n    { deep: true },\n);\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n    runtimeForm.value?.resetFields();\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {\n            const containerPortMap = new Map();\n            const hostPortMap = new Map();\n            for (const port of runtime.exposedPorts) {\n                if (containerPortMap[port.containerPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                if (hostPortMap[port.hostPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                hostPortMap[port.hostPort] = true;\n                containerPortMap[port.containerPort] = true;\n            }\n        }\n\n        if (mode.value == 'create') {\n            loading.value = true;\n            CreateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        } else {\n            loading.value = true;\n            UpdateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\nconst getRuntime = async (id: number) => {\n    try {\n        const res = await GetRuntime(id);\n        const data = res.data;\n        Object.assign(runtime, {\n            id: data.id,\n            name: data.name,\n            appDetailID: data.appDetailID,\n            image: data.image,\n            type: data.type,\n            resource: data.resource,\n            appID: data.appID,\n            version: data.version,\n            rebuild: true,\n            source: data.source,\n            params: data.params,\n            codeDir: data.codeDir,\n            port: data.port,\n            remark: data.remark,\n        });\n        runtime.exposedPorts = data.exposedPorts || [];\n        runtime.environments = data.environments || [];\n        runtime.volumes = data.volumes || [];\n        runtime.extraHosts = data.extraHosts || [];\n        editParams.value = data.appParams;\n        open.value = true;\n    } catch (error) {}\n};\n\nconst acceptParams = async (props: OperateRrops) => {\n    mode.value = props.mode;\n    scripts.value = [];\n    if (props.mode === 'create') {\n        Object.assign(runtime, initData(props.type));\n        open.value = true;\n    } else {\n        getRuntime(props.id);\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/node/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <DockerStatus v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" />\n        <LayoutContent v-loading=\"loading\" v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"node-runtime-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"items\" @search=\"search()\" :heightDiff=\"260\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"name\"\n                        min-width=\"120px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"openDetail(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('runtime.codeDir')\" prop=\"codeDir\" min-width=\"120px\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"routerToFileWithPath(row.codeDir)\">\n                                <el-icon>\n                                    <FolderOpened />\n                                </el-icon>\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('app.version')\" prop=\"version\"></el-table-column>\n                    <el-table-column :label=\"$t('runtime.externalPort')\" prop=\"port\" min-width=\"110px\">\n                        <template #default=\"{ row }\">\n                            <PortJump :row=\"row\" :jump=\"goDashboard\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <RuntimeStatus :row=\"row\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" prop=\"path\" min-width=\"90px\">\n                        <template #default=\"{ row }\">\n                            <el-button @click=\"openLog(row)\" link type=\"primary\" v-if=\"row.status != 'Stopped'\">\n                                {{ $t('website.check') }}\n                            </el-button>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.remark')\" prop=\"remark\" min-width=\"150px\">\n                        <template #default=\"{ row }\">\n                            <fu-read-write-switch>\n                                <template #read>\n                                    <MsgInfo :info=\"row.remark\" :width=\"'150'\" />\n                                </template>\n                                <template #default=\"{ read }\">\n                                    <el-input v-model=\"row.remark\" @blur=\"updateRuntimeRemark(row, read)\" />\n                                </template>\n                            </fu-read-write-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        min-width=\"120\"\n                        fix\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 5\"\n                        :min-width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        fixed=\"right\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <OperateNode ref=\"operateRef\" @close=\"search\" />\n        <Delete ref=\"deleteRef\" @close=\"search\" />\n        <ComposeLogs ref=\"composeLogRef\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <Modules ref=\"moduleRef\" />\n        <AppResources ref=\"checkRef\" @close=\"search\" />\n        <Terminal ref=\"terminalRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, reactive, ref } from 'vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport { RuntimeDeleteCheck, SearchRuntimes, SyncRuntime } from '@/api/modules/runtime';\nimport { dateFormat } from '@/utils/util';\nimport OperateNode from '@/views/website/runtime/node/operate/index.vue';\nimport Delete from '@/views/website/runtime/delete/index.vue';\nimport i18n from '@/lang';\nimport RouterMenu from '../index.vue';\nimport Modules from '@/views/website/runtime/node/module/index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport AppResources from '@/views/website/runtime/php/check/index.vue';\nimport RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';\nimport PortJump from '@/views/website/runtime/components/port-jump.vue';\nimport Terminal from '@/views/website/runtime/components/terminal.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { disabledButton } from '@/utils/runtime';\nimport { GlobalStore } from '@/store';\nimport { operateRuntime, updateRuntimeRemark } from '../common/utils';\nimport { routerToFileWithPath } from '@/utils/router';\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref(false);\nconst items = ref<Runtime.RuntimeDTO[]>([]);\nconst operateRef = ref();\nconst deleteRef = ref();\nconst dialogPortJumpRef = ref();\nconst composeLogRef = ref();\nconst moduleRef = ref();\nconst checkRef = ref();\nconst terminalRef = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'runtime-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('runtime-page-size')) || 20,\n    total: 0,\n});\nconst req = reactive<Runtime.RuntimeReq>({\n    name: '',\n    page: 1,\n    pageSize: 40,\n    type: 'node',\n});\nconst buttons = [\n    {\n        label: i18n.global.t('runtime.module'),\n        click: function (row: Runtime.Runtime) {\n            openModules(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'stop');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.stop'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('down', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'stop');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.start'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('up', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'start');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('restart', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'restart');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Runtime.Runtime) {\n            openDetail(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'edit');\n        },\n    },\n    {\n        label: i18n.global.t('menu.terminal'),\n        click: function (row: Runtime.Runtime) {\n            openTerminal(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'config');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Runtime.Runtime) {\n            openDelete(row);\n        },\n    },\n];\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    try {\n        const res = await SearchRuntimes(req);\n        items.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst sync = () => {\n    SyncRuntime();\n};\n\nconst openModules = (row: Runtime.Runtime) => {\n    moduleRef.value.acceptParams({ id: row.id, packageManager: row.params['PACKAGE_MANAGER'] });\n};\n\nconst openCreate = () => {\n    operateRef.value.acceptParams({ type: 'node', mode: 'create' });\n};\n\nconst openDetail = (row: Runtime.Runtime) => {\n    operateRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });\n};\n\nconst openDelete = async (row: Runtime.Runtime) => {\n    RuntimeDeleteCheck(row.id).then(async (res) => {\n        const items = res.data;\n        if (res.data && res.data.length > 0) {\n            checkRef.value.acceptParams({ items: items, key: 'website', installID: row.id });\n        } else {\n            deleteRef.value.acceptParams(row.id, row.name);\n        }\n    });\n};\n\nconst openLog = (row: any) => {\n    composeLogRef.value.acceptParams({\n        compose: row.path + '/docker-compose.yml',\n        resource: row.name,\n        container: row.container,\n    });\n};\n\nconst goDashboard = async (port: any, protocol: string) => {\n    dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });\n};\n\nconst openTerminal = (row: Runtime.Runtime) => {\n    const container = row.params['CONTAINER_NAME'];\n    terminalRef.value.acceptParams({ containerID: container, container: container });\n};\n\nonMounted(() => {\n    sync();\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped></style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/node/module/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('runtime.moduleManager')\" size=\"large\" @close=\"handleClose\">\n        <template #content>\n            <el-row :gutter=\"20\" v-loading=\"loading\">\n                <el-col :span=\"10\">\n                    <el-input v-model=\"module\">\n                        <template #prepend>{{ packageManager }}</template>\n                    </el-input>\n                </el-col>\n                <el-col :span=\"14\">\n                    <el-button @click=\"operateModule('install', module)\" type=\"primary\" :disabled=\"module === ''\">\n                        {{ $t('commons.button.install') }}\n                    </el-button>\n                </el-col>\n                <el-col>\n                    <ComplexTable :data=\"data\" @search=\"search()\" class=\"mt-5\" :height=\"800\">\n                        <el-table-column\n                            :label=\"$t('commons.table.name')\"\n                            prop=\"name\"\n                            min-width=\"100px\"\n                        ></el-table-column>\n                        <el-table-column :label=\"$t('app.version')\" prop=\"version\" width=\"80px\"></el-table-column>\n                        <el-table-column\n                            :label=\"$t('commons.table.protocol')\"\n                            prop=\"license\"\n                            width=\"120px\"\n                        ></el-table-column>\n                        <el-table-column\n                            :label=\"$t('commons.table.description')\"\n                            fix\n                            min-width=\"120px\"\n                            prop=\"description\"\n                        ></el-table-column>\n                        <fu-table-operations\n                            :ellipsis=\"10\"\n                            width=\"150px\"\n                            :buttons=\"buttons\"\n                            :label=\"$t('commons.table.operate')\"\n                            fixed=\"right\"\n                            fix\n                        />\n                    </ComplexTable>\n                </el-col>\n            </el-row>\n        </template>\n    </DrawerPro>\n</template>\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { GetNodeModules, OperateNodeModule } from '@/api/modules/runtime';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\nimport { Runtime } from '@/api/interface/runtime';\n\ninterface NoodeRrops {\n    packageManager: string;\n    id: number;\n}\n\nconst open = ref(false);\nconst id = ref(0);\nconst data = ref([]);\nconst module = ref('');\nconst packageManager = ref('');\nconst loading = ref(false);\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.update'),\n        click: function (row: Runtime.Runtime) {\n            operateModule('update', row.name);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.uninstall'),\n        click: function (row: Runtime.Runtime) {\n            operateModule('uninstall', row.name);\n        },\n    },\n];\n\nconst acceptParams = async (props: NoodeRrops) => {\n    id.value = props.id;\n    packageManager.value = props.packageManager;\n    module.value = '';\n    data.value = [];\n    open.value = true;\n    loading.value = true;\n    await search();\n    loading.value = false;\n};\n\nconst search = async () => {\n    try {\n        const res = await GetNodeModules({ ID: id.value });\n        data.value = res.data;\n    } catch (error) {}\n};\n\nconst operateModule = (operate: string, moduleName: string) => {\n    ElMessageBox.confirm(\n        i18n.global.t('runtime.nodeOperatorHelper', [i18n.global.t('commons.button.' + operate), moduleName]),\n        i18n.global.t('commons.button.' + operate),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(async () => {\n        loading.value = true;\n        try {\n            await OperateNodeModule({\n                ID: id.value,\n                Operate: operate,\n                Module: moduleName,\n                PkgManager: packageManager.value,\n            });\n            loading.value = false;\n            MsgSuccess(i18n.global.t('commons.button.' + operate) + i18n.global.t('commons.status.success'));\n            await search();\n            module.value = '';\n        } catch (error) {\n            MsgError(\n                i18n.global.t('commons.button.' + operate) +\n                    i18n.global.t('commons.status.failed') +\n                    '  ' +\n                    error.message,\n            );\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.table-border {\n    border: 1px solid var(--el-border-color);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/node/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.' + mode)\"\n        :resource=\"mode === 'edit' ? runtime.name : ''\"\n        size=\"large\"\n        @close=\"handleClose\"\n    >\n        <el-form\n            v-loading=\"loading\"\n            ref=\"runtimeForm\"\n            label-position=\"top\"\n            :model=\"runtime\"\n            label-width=\"125px\"\n            :rules=\"rules\"\n            :validate-on-rule-change=\"false\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input :disabled=\"mode === 'edit'\" v-model=\"runtime.name\"></el-input>\n            </el-form-item>\n            <AppConfig v-model=\"runtime\" :mode=\"mode\" appKey=\"node\" />\n            <DirConfig v-model=\"runtime\" :mode=\"mode\" appKey=\"node\" :scriptHelper=\"$t('runtime.customScriptHelper')\" />\n            <el-form-item :label=\"$t('app.containerName')\" prop=\"params.CONTAINER_NAME\">\n                <el-input v-model.trim=\"runtime.params['CONTAINER_NAME']\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                <el-input type=\"textarea\" :rows=\"1\" clearable v-model=\"runtime.remark\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('runtime.packageManager')\" prop=\"params.PACKAGE_MANAGER\">\n                <el-select v-model=\"runtime.params['PACKAGE_MANAGER']\">\n                    <el-option label=\"npm\" value=\"npm\"></el-option>\n                    <el-option label=\"yarn\" value=\"yarn\"></el-option>\n                    <el-option v-if=\"hasPnpm\" label=\"pnpm\" value=\"pnpm\"></el-option>\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('runtime.imageSource')\" prop=\"source\">\n                <el-select v-model=\"runtime.source\" filterable allow-create default-first-option>\n                    <el-option\n                        v-for=\"(source, index) in imageSources\"\n                        :key=\"index\"\n                        :label=\"source.label + ' [' + source.value + ']'\"\n                        :value=\"source.value\"\n                    ></el-option>\n                </el-select>\n                <span class=\"input-help\">\n                    {{ $t('runtime.phpsourceHelper') }}\n                </span>\n            </el-form-item>\n            <NodeConfig v-model=\"runtime\" />\n        </el-form>\n\n        <template #footer>\n            <span>\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(runtimeForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport NodeConfig from '../../components/node-config.vue';\nimport AppConfig from '@/views/website/runtime/app/index.vue';\nimport DirConfig from '@/views/website/runtime/components/dir/index.vue';\n\nimport { App } from '@/api/interface/app';\nimport { Runtime } from '@/api/interface/runtime';\nimport { CreateRuntime, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { computed, reactive, ref, watch } from 'vue';\n\ninterface OperateRrops {\n    id?: number;\n    mode: string;\n    type: string;\n}\n\nconst open = ref(false);\nconst runtimeForm = ref<FormInstance>();\nconst loading = ref(false);\nconst mode = ref('create');\nconst editParams = ref<App.InstallParams[]>();\nconst initData = (type: string) => ({\n    name: '',\n    appDetailID: undefined,\n    image: '',\n    params: {\n        PACKAGE_MANAGER: 'npm',\n        HOST_IP: '0.0.0.0',\n        CUSTOM_SCRIPT: '0',\n    },\n    type: type,\n    resource: 'appstore',\n    rebuild: false,\n    codeDir: '/',\n    port: 4004,\n    source: 'https://registry.npmjs.org/',\n    exposedPorts: [],\n    environments: [],\n    extraHosts: [],\n    volumes: [],\n    remark: '',\n});\nlet runtime = reactive<Runtime.RuntimeCreate>(initData('node'));\nconst rules = ref<any>({\n    name: [Rules.requiredInput, Rules.appName],\n    appID: [Rules.requiredSelect],\n    codeDir: [Rules.requiredInput],\n    port: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n    source: [Rules.requiredSelect],\n    params: {\n        PACKAGE_MANAGER: [Rules.requiredSelect],\n        HOST_IP: [Rules.requiredSelect],\n        EXEC_SCRIPT: [Rules.requiredSelect],\n        CONTAINER_NAME: [Rules.requiredInput, Rules.containerName],\n    },\n});\nconst em = defineEmits(['close']);\n\nconst hasPnpm = computed(() => {\n    if (runtime.version == undefined) {\n        return false;\n    }\n    return parseFloat(runtime.version) > 18;\n});\n\nconst imageSources = [\n    {\n        label: i18n.global.t('commons.table.default'),\n        value: 'https://registry.npmjs.org/',\n    },\n    {\n        label: i18n.global.t('runtime.taobao'),\n        value: 'https://registry.npmmirror.com',\n    },\n    {\n        label: i18n.global.t('runtime.tencent'),\n        value: 'https://mirrors.cloud.tencent.com/npm/',\n    },\n];\n\nwatch(\n    () => runtime.name,\n    (newVal) => {\n        if (newVal && mode.value == 'create') {\n            runtime.params['CONTAINER_NAME'] = newVal;\n        }\n    },\n    { deep: true },\n);\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n    runtimeForm.value?.resetFields();\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {\n            const containerPortMap = new Map();\n            const hostPortMap = new Map();\n            for (const port of runtime.exposedPorts) {\n                if (containerPortMap[port.containerPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                if (hostPortMap[port.hostPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                hostPortMap[port.hostPort] = true;\n                containerPortMap[port.containerPort] = true;\n            }\n        }\n\n        if (mode.value == 'create') {\n            loading.value = true;\n            CreateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        } else {\n            loading.value = true;\n            UpdateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\nconst getRuntime = async (id: number) => {\n    try {\n        const res = await GetRuntime(id);\n        const data = res.data;\n        Object.assign(runtime, {\n            id: data.id,\n            name: data.name,\n            appDetailID: data.appDetailID,\n            image: data.image,\n            type: data.type,\n            resource: data.resource,\n            appID: data.appID,\n            version: data.version,\n            rebuild: true,\n            source: data.source,\n            params: data.params,\n            codeDir: data.codeDir,\n            port: data.port,\n            remark: data.remark,\n        });\n        runtime.exposedPorts = data.exposedPorts || [];\n        runtime.environments = data.environments || [];\n        runtime.volumes = data.volumes || [];\n        runtime.extraHosts = data.extraHosts || [];\n        editParams.value = data.appParams;\n        if (data.params['CUSTOM_SCRIPT'] == undefined || data.params['CUSTOM_SCRIPT'] == '0') {\n            data.params['CUSTOM_SCRIPT'] = '0';\n        }\n        open.value = true;\n    } catch (error) {}\n};\n\nconst acceptParams = async (props: OperateRrops) => {\n    mode.value = props.mode;\n    if (props.mode === 'create') {\n        Object.assign(runtime, initData(props.type));\n        open.value = true;\n    } else {\n        getRuntime(props.id);\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/check/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"large\">\n        <el-row>\n            <el-col :span=\"20\" :offset=\"2\" v-if=\"open\">\n                <el-alert\n                    type=\"error\"\n                    :description=\"$t('app.deleteHelper', [$t('runtime.runtime')])\"\n                    center\n                    show-icon\n                    :closable=\"false\"\n                />\n                <br />\n                <el-descriptions border :column=\"1\" class=\"mt-5\">\n                    <el-descriptions-item\n                        v-for=\"(item, key) in map\"\n                        :key=\"key\"\n                        label-class-name=\"check-label\"\n                        class-name=\"check-content\"\n                        min-width=\"60px\"\n                    >\n                        <template #label>\n                            <a href=\"javascript:void(0);\" class=\"check-label-a\" @click=\"toPage(item[0])\">\n                                {{ $t(item[0] === 'website' ? 'menu.website' : 'app.' + item[0]) }}\n                            </a>\n                        </template>\n                        <span class=\"resources\">\n                            {{ map.get(item[0]).toString() }}\n                        </span>\n                    </el-descriptions-item>\n                </el-descriptions>\n                <div v-if=\"installData.key === 'openresty'\" class=\"mt-5\">\n                    <el-checkbox v-model=\"forceDelete\" label=\"true\">{{ $t('app.forceDelete') }}</el-checkbox>\n                    <ErrPrompt :title=\"$t('app.openrestyDeleteHelper')\" />\n                </div>\n            </el-col>\n        </el-row>\n\n        <template #footer v-if=\"forceDelete\">\n            <span class=\"dialog-footer\">\n                <el-button @click=\"open = false\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"onConfirm\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { App } from '@/api/interface/app';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ref } from 'vue';\nimport { DeleteRuntime } from '@/api/modules/runtime';\nimport { routerToName } from '@/utils/router';\nimport ErrPrompt from '@/components/error-prompt/index.vue';\n\ninterface CheckRrops {\n    items: App.AppInstallResource[];\n    installID: Number;\n    key: string;\n}\nconst installData = ref<CheckRrops>({\n    items: [],\n    installID: 0,\n    key: '',\n});\nconst open = ref(false);\nconst map = new Map();\nconst forceDelete = ref(false);\nconst em = defineEmits(['close']);\n\nconst acceptParams = (props: CheckRrops) => {\n    map.clear();\n    forceDelete.value = false;\n    installData.value.installID = props.installID;\n    installData.value.key = props.key;\n    installData.value.items = [];\n    installData.value.items = props.items;\n    installData.value.items.forEach((item) => {\n        if (map.has(item.type)) {\n            const array = map.get(item.type);\n            array.push(item.name);\n            map.set(item.type, array);\n        } else {\n            map.set(item.type, [item.name]);\n        }\n    });\n    open.value = true;\n};\n\nconst toPage = (key: string) => {\n    if (key === 'website') {\n        routerToName('Website');\n    }\n};\n\nconst onConfirm = () => {\n    ElMessageBox.confirm(\n        i18n.global.t('app.operatorHelper', [i18n.global.t('commons.button.delete')]),\n        i18n.global.t('commons.button.delete'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    ).then(() => {\n        const params = {\n            id: installData.value.installID.valueOf(),\n            forceDelete: true,\n        };\n        DeleteRuntime(params).then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            open.value = false;\n            em('close', open);\n        });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped>\n.resources {\n    word-break: break-all;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/config/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <el-form :model=\"form\" :rules=\"variablesRules\" ref=\"phpFormRef\" label-position=\"top\">\n            <el-row v-loading=\"loading\">\n                <el-col :span=\"11\" :offset=\"1\">\n                    <el-form-item label=\"short_open_tag\" prop=\"short_open_tag\">\n                        <el-select v-model=\"form.short_open_tag\">\n                            <el-option :label=\"$t('website.isOff')\" :value=\"'Off'\"></el-option>\n                            <el-option :label=\"$t('website.isOn')\" :value=\"'On'\"></el-option>\n                        </el-select>\n                        <span class=\"input-help\">{{ $t('php.short_open_tag') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"max_execution_time\" prop=\"max_execution_time\">\n                        <el-input clearable v-model.number=\"form.max_execution_time\" maxlength=\"15\">\n                            <template #append>{{ $t('commons.units.second') }}</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('php.max_execution_time') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"post_max_size\" prop=\"post_max_size\">\n                        <el-input clearable v-model.number=\"form.post_max_size\" maxlength=\"15\">\n                            <template #append>M</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('php.post_max_size') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"file_uploads\" prop=\"file_uploads\">\n                        <el-select v-model=\"form.file_uploads\">\n                            <el-option :label=\"$t('website.isOff')\" :value=\"'Off'\"></el-option>\n                            <el-option :label=\"$t('website.isOn')\" :value=\"'On'\"></el-option>\n                        </el-select>\n                        <span class=\"input-help\">{{ $t('php.file_uploads') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"upload_max_filesize\" prop=\"upload_max_filesize\">\n                        <el-input clearable v-model.number=\"form.upload_max_filesize\" maxlength=\"15\">\n                            <template #append>M</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('php.upload_max_filesize') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"max_file_uploads\" prop=\"max_file_uploads\">\n                        <el-input clearable v-model.number=\"form.max_file_uploads\" maxlength=\"15\"></el-input>\n                        <span class=\"input-help\">{{ $t('php.max_file_uploads') }}</span>\n                    </el-form-item>\n                    <el-form-item>\n                        <el-button type=\"primary\" @click=\"onSaveStart(phpFormRef)\">\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                    </el-form-item>\n                </el-col>\n                <el-col :span=\"1\"><br /></el-col>\n                <el-col :span=\"11\">\n                    <el-form-item label=\"default_socket_timeout\" prop=\"default_socket_timeout\">\n                        <el-input clearable v-model.number=\"form.default_socket_timeout\" maxlength=\"15\">\n                            <template #append>{{ $t('commons.units.second') }}</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('php.default_socket_timeout') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"error_reporting\" prop=\"error_reporting\">\n                        <el-input clearable v-model.trim=\"form.error_reporting\"></el-input>\n                        <span class=\"input-help\">{{ $t('php.error_reporting') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"display_errors\" prop=\"display_errors\">\n                        <el-select v-model=\"form.display_errors\">\n                            <el-option :label=\"$t('website.isOff')\" :value=\"'Off'\"></el-option>\n                            <el-option :label=\"$t('website.isOn')\" :value=\"'On'\"></el-option>\n                        </el-select>\n                        <span class=\"input-help\">{{ $t('php.display_errors') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"max_input_time\" prop=\"max_input_time\">\n                        <el-input clearable v-model.number=\"form.max_input_time\" maxlength=\"15\">\n                            <template #append>{{ $t('commons.units.second') }}</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('php.max_input_time') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"memory_limit\" prop=\"memory_limit\">\n                        <el-input clearable v-model.number=\"form.memory_limit\" maxlength=\"15\">\n                            <template #append>M</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('php.memory_limit') }}</span>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/runtime';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { computed, onMounted, reactive, ref } from 'vue';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst id = computed(() => {\n    return props.id;\n});\nconst loading = ref(false);\nconst phpFormRef = ref();\nlet form = reactive({\n    short_open_tag: 'Off',\n    max_execution_time: 50,\n    max_input_time: 50,\n    memory_limit: 50,\n    post_max_size: 50,\n    file_uploads: 'On',\n    upload_max_filesize: 50,\n    max_file_uploads: 20,\n    default_socket_timeout: 50,\n    error_reporting: '',\n    display_errors: 'On',\n});\nconst variablesRules = reactive({\n    max_execution_time: [checkNumberRange(0, 999999999)],\n    max_input_time: [checkNumberRange(0, 999999999)],\n    memory_limit: [checkNumberRange(0, 999999999)],\n    post_max_size: [checkNumberRange(0, 999999999)],\n    upload_max_filesize: [checkNumberRange(0, 999999999)],\n    max_file_uploads: [checkNumberRange(0, 999999999)],\n    default_socket_timeout: [checkNumberRange(0, 999999999)],\n    error_reporting: [Rules.requiredInput],\n    short_open_tag: [Rules.requiredSelect],\n    file_uploads: [Rules.requiredSelect],\n    display_errors: [Rules.requiredSelect],\n});\n\nconst get = () => {\n    loading.value = true;\n    GetPHPConfig(id.value)\n        .then((res) => {\n            const param = res.data.params;\n            form.short_open_tag = param.short_open_tag;\n            form.max_execution_time = Number(param.max_execution_time);\n            form.max_input_time = Number(param.max_input_time);\n            form.memory_limit = parseFloat(param.memory_limit.replace(/[^\\d.]/g, ''));\n            form.post_max_size = parseFloat(param.post_max_size.replace(/[^\\d.]/g, ''));\n            form.file_uploads = param.file_uploads;\n            form.upload_max_filesize = parseFloat(param.upload_max_filesize.replace(/[^\\d.]/g, ''));\n            form.max_file_uploads = Number(param.max_file_uploads);\n            form.default_socket_timeout = Number(param.default_socket_timeout);\n            form.error_reporting = param.error_reporting;\n            form.display_errors = param.display_errors;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst onSaveStart = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        const action = await ElMessageBox.confirm(\n            i18n.global.t('runtime.phpConfigHelper'),\n            i18n.global.t('database.confChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        );\n        if (action === 'confirm') {\n            loading.value = true;\n            submit();\n        }\n    });\n};\n\nconst submit = async () => {\n    const params = {\n        short_open_tag: form.short_open_tag,\n        max_execution_time: String(form.max_execution_time),\n        max_input_time: String(form.max_input_time),\n        memory_limit: form.memory_limit + 'M',\n        post_max_size: form.post_max_size + 'M',\n        file_uploads: form.file_uploads,\n        upload_max_filesize: form.upload_max_filesize + 'M',\n        max_file_uploads: String(form.max_file_uploads),\n        default_socket_timeout: String(form.default_socket_timeout),\n        error_reporting: form.error_reporting,\n        display_errors: form.display_errors,\n    };\n    loading.value = true;\n    UpdatePHPConfig({ id: id.value, params: params, scope: 'params' })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/container/index.vue",
    "content": "<template>\n    <div>\n        <el-row v-loading=\"loading\">\n            <el-col :span=\"22\" :offset=\"1\">\n                <el-form\n                    v-loading=\"loading\"\n                    ref=\"runtimeForm\"\n                    label-position=\"top\"\n                    :model=\"containerConfig\"\n                    :rules=\"rules\"\n                    label-width=\"125px\"\n                    :validate-on-rule-change=\"false\"\n                >\n                    <el-form-item :label=\"$t('app.containerName')\" prop=\"containerName\">\n                        <el-input v-model.trim=\"containerConfig.containerName\"></el-input>\n                    </el-form-item>\n                    <el-tabs type=\"border-card\">\n                        <el-tab-pane :label=\"$t('commons.table.port')\">\n                            <PortConfig :exposedPorts=\"containerConfig.exposedPorts\" />\n                        </el-tab-pane>\n                        <el-tab-pane :label=\"$t('runtime.environment')\">\n                            <Environment :environments=\"containerConfig.environments\" />\n                        </el-tab-pane>\n                        <el-tab-pane :label=\"$t('container.mount')\">\n                            <Volumes :volumes=\"containerConfig.volumes\" />\n                        </el-tab-pane>\n                        <el-tab-pane :label=\"$t('runtime.extraHosts')\">\n                            <ExtraHosts :extraHosts=\"containerConfig.extraHosts\" />\n                        </el-tab-pane>\n                    </el-tabs>\n                    <el-form-item class=\"mt-2\">\n                        <el-button type=\"primary\" @click=\"onSaveStart(runtimeForm)\">\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                    </el-form-item>\n                </el-form>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport PortConfig from '@/views/website/runtime/components/port/index.vue';\nimport Environment from '@/views/website/runtime/components/environment/index.vue';\nimport Volumes from '@/views/website/runtime/components/volume/index.vue';\nimport ExtraHosts from '@/views/website/runtime/components/extra_hosts/index.vue';\n\nimport { Runtime } from '@/api/interface/runtime';\nimport { getPHPContainerConfig, updatePHPContainerConfig } from '@/api/modules/runtime';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\n\nconst props = defineProps<{\n    id: number;\n}>();\nconst loading = ref<boolean>(false);\nconst runtimeForm = ref<FormInstance>();\nconst containerConfig = ref<Runtime.PHPContainerConfig>({\n    id: 0,\n    containerName: '',\n    exposedPorts: [],\n    environments: [],\n    volumes: [],\n    extraHosts: [],\n});\n\nconst rules = {\n    containerName: [Rules.containerName, Rules.requiredInput],\n};\n\nconst getConfig = async () => {\n    const res = await getPHPContainerConfig(props.id);\n    containerConfig.value.exposedPorts = res.data.exposedPorts || [];\n    containerConfig.value.environments = res.data.environments || [];\n    containerConfig.value.volumes = res.data.volumes || [];\n    containerConfig.value.containerName = res.data.containerName || '';\n    containerConfig.value.extraHosts = res.data.extraHosts || [];\n};\n\nconst onSaveStart = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        const action = await ElMessageBox.confirm(\n            i18n.global.t('runtime.phpConfigHelper'),\n            i18n.global.t('database.confChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        );\n        if (action === 'confirm') {\n            loading.value = true;\n            submit();\n        }\n    });\n};\n\nconst submit = async () => {\n    try {\n        await updatePHPContainerConfig({\n            id: props.id,\n            containerName: containerConfig.value.containerName,\n            exposedPorts: containerConfig.value.exposedPorts,\n            environments: containerConfig.value.environments,\n            volumes: containerConfig.value.volumes,\n            extraHosts: containerConfig.value.extraHosts,\n        });\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nonMounted(() => {\n    getConfig();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/fpm-status/index.vue",
    "content": "<template>\n    <div>\n        <el-row>\n            <el-col :span=\"22\" :offset=\"1\">\n                <el-descriptions border :column=\"1\">\n                    <el-descriptions-item v-for=\"(item, key) in data\" :label=\"item.key\" :key=\"key\">\n                        {{ item.value }}\n                    </el-descriptions-item>\n                </el-descriptions>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getFPMStatus } from '@/api/modules/runtime';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst data = ref([]);\n\nconst getStatus = async () => {\n    try {\n        const res = await getFPMStatus(props.id);\n        data.value = res.data || [];\n    } catch (error) {}\n};\n\nonMounted(() => {\n    getStatus();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/function/index.vue",
    "content": "<template>\n    <el-row>\n        <el-col :span=\"22\" :offset=\"1\">\n            <el-form :model=\"form\" :rules=\"rules\" ref=\"formRef\">\n                <el-form-item prop=\"funcs\">\n                    <el-input v-model=\"form.funcs\" label=\"value\" :placeholder=\"$t('php.disableFunctionHelper')\" />\n                </el-form-item>\n            </el-form>\n            <ComplexTable :data=\"data\" v-loading=\"loading\">\n                <template #toolbar>\n                    <el-button type=\"primary\" icon=\"Plus\" @click=\"openCreate(formRef)\">\n                        {{ $t('commons.button.add') }}\n                    </el-button>\n                </template>\n                <el-table-column :label=\"$t('commons.table.name')\" prop=\"func\"></el-table-column>\n                <el-table-column :label=\"$t('commons.table.operate')\">\n                    <template #default=\"{ $index }\">\n                        <el-button link type=\"primary\" @click=\"remove($index)\">\n                            {{ $t('commons.button.delete') }}\n                        </el-button>\n                    </template>\n                </el-table-column>\n            </ComplexTable>\n        </el-col>\n    </el-row>\n</template>\n<script setup lang=\"ts\">\nimport { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/runtime';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { onMounted, reactive } from 'vue';\nimport { computed, ref } from 'vue';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst rules = reactive({\n    funcs: [Rules.requiredInput, Rules.disabledFunctions],\n});\n\nconst websiteID = computed(() => {\n    return props.id;\n});\nconst formRef = ref();\nconst loading = ref(false);\nconst form = ref({\n    funcs: '',\n});\nconst data = ref([]);\n\nconst search = () => {\n    loading.value = true;\n    GetPHPConfig(websiteID.value)\n        .then((res) => {\n            const functions = res.data.disableFunctions || [];\n            if (functions.length > 0) {\n                functions.forEach((value: string) => {\n                    data.value.push({\n                        func: value,\n                    });\n                });\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst openCreate = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        const action = await ElMessageBox.confirm(\n            i18n.global.t('database.restartNowHelper'),\n            i18n.global.t('database.confChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        );\n        if (action === 'confirm') {\n            loading.value = true;\n            submit(false, ['']);\n        }\n    });\n};\n\nconst remove = async (index: number) => {\n    ElMessageBox.confirm(i18n.global.t('commons.msg.delete'), i18n.global.t('commons.button.delete'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    })\n        .then(() => {\n            const copyList = data.value.concat();\n            copyList.splice(index, 1);\n            const funcArray: string[] = [];\n            copyList.forEach((d) => {\n                funcArray.push(d.func);\n            });\n            submit(true, funcArray);\n        })\n        .catch(() => {});\n};\n\nconst submit = async (del: boolean, funcArray: string[]) => {\n    let disableFunctions = [];\n    if (del) {\n        disableFunctions = funcArray;\n    } else {\n        disableFunctions = form.value.funcs.split(',');\n        data.value.forEach((d) => {\n            disableFunctions.push(d.func);\n        });\n    }\n\n    loading.value = true;\n    UpdatePHPConfig({ scope: 'disable_functions', id: websiteID.value, disableFunctions: disableFunctions })\n        .then(() => {\n            form.value.funcs = '';\n            data.value = [];\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('runtime.runtime')\"\n        size=\"large\"\n        :resource=\"runtime.name\"\n        @close=\"handleClose\"\n    >\n        <template #content>\n            <el-tabs tab-position=\"left\" v-model=\"index\">\n                <el-tab-pane :label=\"$t('php.containerConfig')\" name=\"6\">\n                    <Container :id=\"runtime.id\" v-if=\"index == '6'\"></Container>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('website.updateConfig')\" name=\"0\">\n                    <Config :id=\"runtime.id\" v-if=\"index == '0'\"></Config>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('php.disableFunction')\" name=\"1\">\n                    <Function :id=\"runtime.id\" v-if=\"index == '1'\"></Function>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('php.uploadMaxSize')\" name=\"2\">\n                    <Upload :id=\"runtime.id\" v-if=\"index == '2'\"></Upload>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('cronjob.timeout')\" name=\"8\">\n                    <Timeout :id=\"runtime.id\" v-if=\"index == '8'\"></Timeout>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('website.nginxPer')\" name=\"5\">\n                    <Performance :id=\"runtime.id\" v-if=\"index == '5'\"></Performance>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('runtime.loadStatus')\" name=\"7\">\n                    <FpmStatus :id=\"runtime.id\" v-if=\"index == '7'\"></FpmStatus>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('database.slowLog')\" name=\"9\">\n                    <SlowLog :id=\"runtime.id\" v-if=\"index == '9'\"></SlowLog>\n                </el-tab-pane>\n                <el-tab-pane :label=\"$t('website.source')\" name=\"4\">\n                    <PHP :id=\"runtime.id\" v-if=\"index == '4'\" :type=\"'php'\"></PHP>\n                </el-tab-pane>\n                <el-tab-pane :label=\"'FPM ' + $t('website.source')\" name=\"3\">\n                    <PHP :id=\"runtime.id\" v-if=\"index == '3'\" :type=\"'fpm'\"></PHP>\n                </el-tab-pane>\n            </el-tabs>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, ref } from 'vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport Config from './config/index.vue';\nimport Function from './function/index.vue';\nimport Upload from './upload/index.vue';\nimport PHP from './php-fpm/index.vue';\nimport Performance from './performance/index.vue';\nimport Container from './container/index.vue';\nimport FpmStatus from './fpm-status/index.vue';\nimport Timeout from './timeout/index.vue';\nimport SlowLog from './slow-log/index.vue';\n\nconst index = ref('6');\nconst open = ref(false);\nconst runtime = ref({\n    name: '',\n    id: 0,\n});\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = async (req: Runtime.Runtime) => {\n    runtime.value = req;\n    open.value = true;\n};\n\nonMounted(() => {});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/performance/index.vue",
    "content": "<template>\n    <el-form :model=\"params\" :rules=\"variablesRules\" ref=\"phpFormRef\" label-position=\"top\" v-loading=\"loading\">\n        <el-row v-loading=\"loading\">\n            <el-col :span=\"22\" :offset=\"1\">\n                <el-form-item :label=\"$t('runtime.concurrency')\">\n                    <el-select v-model=\"concurrency\" @change=\"changeConcurrency\">\n                        <el-option\n                            v-for=\"item in concurrencyOptions\"\n                            :key=\"item.value\"\n                            :label=\"item.label\"\n                            :value=\"item.value\"\n                        ></el-option>\n                    </el-select>\n                </el-form-item>\n                <el-form-item :label=\"$t('runtime.operateMode')\" prop=\"pm\">\n                    <el-select v-model=\"params.pm\">\n                        <el-option :label=\"$t('runtime.dynamic')\" :value=\"'dynamic'\"></el-option>\n                        <el-option :label=\"$t('runtime.static')\" :value=\"'static'\"></el-option>\n                        <el-option :label=\"$t('runtime.ondemand')\" :value=\"'ondemand'\"></el-option>\n                    </el-select>\n                    <span class=\"input-help\">\n                        <el-text v-if=\"params.pm == 'dynamic'\">{{ $t('runtime.dynamicHelper') }}</el-text>\n                        <el-text v-if=\"params.pm == 'static'\">{{ $t('runtime.staticHelper') }}</el-text>\n                        <el-text v-if=\"params.pm == 'ondemand'\">{{ $t('runtime.ondemandHelper') }}</el-text>\n                    </span>\n                </el-form-item>\n                <el-form-item label=\"max_children\" prop=\"pm.max_children\">\n                    <el-input clearable v-model.number=\"params['pm.max_children']\" maxlength=\"15\"></el-input>\n                    <span class=\"input-help\">\n                        {{ $t('runtime.max_children') }}\n                    </span>\n                </el-form-item>\n                <el-form-item label=\"start_servers\" prop=\"pm.start_servers\">\n                    <el-input clearable v-model.number=\"params['pm.start_servers']\" maxlength=\"15\"></el-input>\n                    <span class=\"input-help\">\n                        {{ $t('runtime.start_servers') }}\n                    </span>\n                </el-form-item>\n                <el-form-item label=\"min_spare_servers\" prop=\"pm.min_spare_servers\">\n                    <el-input clearable v-model.number=\"params['pm.min_spare_servers']\" maxlength=\"15\"></el-input>\n                    <span class=\"input-help\">\n                        {{ $t('runtime.min_spare_servers') }}\n                    </span>\n                </el-form-item>\n                <el-form-item label=\"max_spare_servers\" prop=\"pm.max_spare_servers\">\n                    <el-input clearable v-model.number=\"params['pm.max_spare_servers']\" maxlength=\"15\"></el-input>\n                    <span class=\"input-help\">\n                        {{ $t('runtime.max_spare_servers') }}\n                    </span>\n                </el-form-item>\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"onSaveStart(phpFormRef)\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </el-form-item>\n            </el-col>\n        </el-row>\n    </el-form>\n</template>\n\n<script lang=\"ts\" setup>\nimport { GetFPMConfig, UpdateFPMConfig } from '@/api/modules/runtime';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { computed, onMounted, reactive, ref } from 'vue';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst id = computed(() => {\n    return props.id;\n});\nconst loading = ref(false);\nconst phpFormRef = ref();\nconst initData = () => {\n    return {\n        pm: 'dynamic',\n        'pm.max_children': 150,\n        'pm.start_servers': 10,\n        'pm.min_spare_servers': 10,\n        'pm.max_spare_servers': 30,\n    };\n};\nconst params = reactive(initData());\nconst variablesRules = reactive({\n    pm: [Rules.requiredSelect],\n    'pm.max_children': [checkNumberRange(0, 5000)],\n    'pm.start_servers': [checkNumberRange(0, 99999)],\n    'pm.min_spare_servers': [checkNumberRange(0, 99999)],\n    'pm.max_spare_servers': [checkNumberRange(0, 99999)],\n});\nconst concurrency = ref('');\nconst concurrencyOptions = ref([\n    { label: '4 GB', value: '4' },\n    { label: '8 GB', value: '8' },\n    { label: '16 GB', value: '16' },\n    { label: '32 GB', value: '32' },\n    { label: '48 GB', value: '48' },\n    { label: '64 GB', value: '64' },\n    { label: '96 GB', value: '96' },\n    { label: '128 GB', value: '128' },\n]);\n\nconst fpmConfigMap = {\n    '4': {\n        max_children: 80,\n        start_servers: 10,\n        min_spare_servers: 10,\n        max_spare_servers: 30,\n    },\n    '8': {\n        max_children: 120,\n        start_servers: 10,\n        min_spare_servers: 10,\n        max_spare_servers: 30,\n    },\n    '16': {\n        max_children: 200,\n        start_servers: 15,\n        min_spare_servers: 15,\n        max_spare_servers: 50,\n    },\n    '32': {\n        max_children: 300,\n        start_servers: 20,\n        min_spare_servers: 20,\n        max_spare_servers: 50,\n    },\n    '48': {\n        max_children: 400,\n        start_servers: 20,\n        min_spare_servers: 20,\n        max_spare_servers: 50,\n    },\n    '64': {\n        max_children: 500,\n        start_servers: 30,\n        min_spare_servers: 30,\n        max_spare_servers: 60,\n    },\n    '96': {\n        max_children: 700,\n        start_servers: 40,\n        min_spare_servers: 40,\n        max_spare_servers: 70,\n    },\n    '128': {\n        max_children: 1000,\n        start_servers: 50,\n        min_spare_servers: 50,\n        max_spare_servers: 100,\n    },\n};\n\nconst changeConcurrency = () => {\n    const config = fpmConfigMap[concurrency.value];\n\n    if (config) {\n        params['pm.max_children'] = config.max_children;\n        params['pm.start_servers'] = config.start_servers;\n        params['pm.min_spare_servers'] = config.min_spare_servers;\n        params['pm.max_spare_servers'] = config.max_spare_servers;\n    }\n};\n\nconst get = () => {\n    loading.value = true;\n    GetFPMConfig(id.value)\n        .then((res) => {\n            const resParams = res.data.params;\n            params['pm'] = resParams['pm'];\n            params['pm.max_children'] = Number(resParams['pm.max_children']);\n            params['pm.start_servers'] = Number(resParams['pm.start_servers']);\n            params['pm.min_spare_servers'] = Number(resParams['pm.min_spare_servers']);\n            params['pm.max_spare_servers'] = Number(resParams['pm.max_spare_servers']);\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst onSaveStart = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        const action = await ElMessageBox.confirm(\n            i18n.global.t('runtime.phpConfigHelper'),\n            i18n.global.t('database.confChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        );\n        if (action === 'confirm') {\n            loading.value = true;\n            submit();\n        }\n    });\n};\n\nconst submit = async () => {\n    loading.value = true;\n    UpdateFPMConfig({ id: id.value, params: params })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            get();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/php-fpm/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <CodemirrorPro v-model=\"content\"></CodemirrorPro>\n        <el-button type=\"primary\" @click=\"openUpdate()\" class=\"mt-2.5\">\n            {{ $t('nginx.saveAndReload') }}\n        </el-button>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { GetPHPConfigFile, UpdatePHPFile } from '@/api/modules/runtime';\nimport { computed, onMounted, ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n    type: {\n        type: String,\n        default: 'fpm',\n    },\n});\n\nconst id = computed(() => {\n    return props.id;\n});\n\nconst data = ref<File.File>();\nconst loading = ref(false);\nconst content = ref('');\n\nconst get = () => {\n    loading.value = true;\n    GetPHPConfigFile({ id: id.value, type: props.type })\n        .then((res) => {\n            data.value = res.data;\n            content.value = data.value.content;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst openUpdate = async () => {\n    const action = await ElMessageBox.confirm(\n        i18n.global.t('database.restartNowHelper'),\n        i18n.global.t('database.confChange'),\n        {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'info',\n        },\n    );\n    if (action === 'confirm') {\n        loading.value = true;\n        submit();\n    }\n};\n\nconst submit = async () => {\n    loading.value = true;\n    UpdatePHPFile({\n        id: id.value,\n        content: content.value,\n        type: props.type,\n    })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/slow-log/index.vue",
    "content": "<template>\n    <LogFile :config=\"logConfig\"></LogFile>\n</template>\n\n<script lang=\"ts\" setup>\nimport LogFile from '@/components/log/file/index.vue';\n\nconst props = defineProps({\n    id: Number,\n});\n\nconst logConfig = reactive({\n    id: props.id,\n    type: 'php-fpm-slow-logs',\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/timeout/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <el-row>\n            <el-col :xs=\"20\" :sm=\"12\" :md=\"10\" :lg=\"10\" :xl=\"8\" :offset=\"1\">\n                <el-form :model=\"form\" :rules=\"rules\" ref=\"phpFormRef\">\n                    <el-form-item prop=\"timeout\">\n                        <el-input clearable type=\"number\" v-model.number=\"form.timeout\" maxlength=\"15\">\n                            <template #append>{{ $t('commons.units.second') }}</template>\n                        </el-input>\n                    </el-form-item>\n                </el-form>\n                <el-button type=\"primary\" @click=\"openCreate(phpFormRef)\">\n                    {{ $t('commons.button.save') }}\n                </el-button>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/runtime';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { computed, onMounted, reactive } from 'vue';\nimport { ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst websiteID = computed(() => {\n    return props.id;\n});\nconst rules = reactive({\n    timeout: [Rules.requiredInput, checkNumberRange(0, 999999999)],\n});\nconst phpFormRef = ref();\nconst loading = ref(false);\nconst form = ref({\n    timeout: 0,\n});\n\nconst search = () => {\n    loading.value = true;\n    GetPHPConfig(websiteID.value)\n        .then((res) => {\n            form.value.timeout = parseFloat(res.data.maxExecutionTime.replace(/[^\\d.]/g, ''));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst openCreate = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        const action = await ElMessageBox.confirm(\n            i18n.global.t('database.restartNowHelper'),\n            i18n.global.t('database.confChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        );\n        if (action === 'confirm') {\n            loading.value = true;\n            submit();\n        }\n    });\n};\n\nconst submit = () => {\n    loading.value = true;\n    UpdatePHPConfig({ scope: 'max_execution_time', id: websiteID.value, maxExecutionTime: String(form.value.timeout) })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/config/upload/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <el-row>\n            <el-col :xs=\"20\" :sm=\"12\" :md=\"10\" :lg=\"10\" :xl=\"8\" :offset=\"1\">\n                <el-form :model=\"form\" :rules=\"rules\" ref=\"phpFormRef\">\n                    <el-form-item prop=\"uploadSize\">\n                        <el-input clearable type=\"number\" v-model.number=\"form.uploadSize\" maxlength=\"15\">\n                            <template #append>M</template>\n                        </el-input>\n                    </el-form-item>\n                </el-form>\n                <el-button type=\"primary\" @click=\"openCreate(phpFormRef)\">\n                    {{ $t('commons.button.save') }}\n                </el-button>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { GetPHPConfig, UpdatePHPConfig } from '@/api/modules/runtime';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { computed, onMounted, reactive } from 'vue';\nimport { ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst websiteID = computed(() => {\n    return props.id;\n});\nconst rules = reactive({\n    uploadSize: [Rules.requiredInput, checkNumberRange(0, 999999999)],\n});\nconst phpFormRef = ref();\nconst loading = ref(false);\nconst form = ref({\n    uploadSize: 0,\n});\n\nconst search = () => {\n    loading.value = true;\n    GetPHPConfig(websiteID.value)\n        .then((res) => {\n            form.value.uploadSize = parseFloat(res.data.uploadMaxSize.replace(/[^\\d.]/g, ''));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst openCreate = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    formEl.validate(async (valid) => {\n        if (!valid) return;\n        const action = await ElMessageBox.confirm(\n            i18n.global.t('database.restartNowHelper'),\n            i18n.global.t('database.confChange'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        );\n        if (action === 'confirm') {\n            loading.value = true;\n            submit();\n        }\n    });\n};\n\nconst submit = () => {\n    loading.value = true;\n    const uploadMaxSize = form.value.uploadSize + 'M';\n    UpdatePHPConfig({ scope: 'upload_max_filesize', id: websiteID.value, uploadMaxSize: uploadMaxSize })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/create/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.' + mode)\"\n        size=\"large\"\n        :resource=\"mode === 'edit' ? runtime.name : ''\"\n        @close=\"handleClose\"\n    >\n        <el-form\n            ref=\"runtimeForm\"\n            label-position=\"top\"\n            :model=\"runtime\"\n            label-width=\"125px\"\n            :rules=\"rules\"\n            :validate-on-rule-change=\"false\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input :disabled=\"mode === 'edit'\" v-model=\"runtime.name\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('app.source')\" prop=\"resource\">\n                <el-radio-group\n                    :disabled=\"mode === 'edit'\"\n                    v-model=\"runtime.resource\"\n                    @change=\"changeResource(runtime.resource)\"\n                >\n                    <el-radio :value=\"'appstore'\" v-if=\"!globalStore.isOffLine\">\n                        {{ $t('menu.apps') }}\n                    </el-radio>\n                    <el-radio :value=\"'local'\">\n                        {{ $t('commons.table.local') }}\n                    </el-radio>\n                </el-radio-group>\n            </el-form-item>\n            <div v-if=\"runtime.resource === 'appstore'\">\n                <el-form-item :label=\"$t('app.app')\" prop=\"appID\">\n                    <el-row :gutter=\"20\">\n                        <el-col :span=\"12\">\n                            <el-select\n                                v-model=\"runtime.appID\"\n                                :disabled=\"mode === 'edit'\"\n                                @change=\"changeApp(runtime.appID)\"\n                                class=\"p-w-200\"\n                            >\n                                <el-option\n                                    v-for=\"(app, index) in apps\"\n                                    :key=\"index\"\n                                    :label=\"app.name\"\n                                    :value=\"app.id\"\n                                ></el-option>\n                            </el-select>\n                        </el-col>\n                        <el-col :span=\"12\">\n                            <el-select\n                                v-model=\"runtime.version\"\n                                :disabled=\"mode === 'edit'\"\n                                @change=\"changeVersion()\"\n                                class=\"p-w-200\"\n                            >\n                                <el-option\n                                    v-for=\"(version, index) in appVersions\"\n                                    :key=\"index\"\n                                    :label=\"version\"\n                                    :value=\"version\"\n                                ></el-option>\n                            </el-select>\n                        </el-col>\n                    </el-row>\n                </el-form-item>\n                <div v-if=\"initParam\">\n                    <el-form-item\n                        :label=\"getLabel(formFields['PHP_VERSION'])\"\n                        :rules=\"rules.params.PHP_VERSION\"\n                        v-if=\"formFields['PHP_VERSION']\"\n                    >\n                        <el-select\n                            v-model=\"runtime.params['PHP_VERSION']\"\n                            filterable\n                            default-first-option\n                            @change=\"changePHPVersion(runtime.params['PHP_VERSION'])\"\n                        >\n                            <el-option\n                                v-for=\"service in formFields['PHP_VERSION'].values\"\n                                :key=\"service.label\"\n                                :value=\"service.value\"\n                                :label=\"service.label\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('container.image')\" prop=\"image\">\n                        <el-input v-model=\"runtime.image\"></el-input>\n                    </el-form-item>\n                    <el-form-item\n                        :label=\"getLabel(formFields['CONTAINER_PACKAGE_URL'])\"\n                        :rules=\"rules.params.CONTAINER_PACKAGE_URL\"\n                        v-if=\"runtime.params['PHP_VERSION'] != '5.6.40' && formFields['CONTAINER_PACKAGE_URL']\"\n                    >\n                        <el-select v-model=\"runtime.source\" filterable default-first-option allow-create>\n                            <el-option\n                                v-for=\"source in phpSources\"\n                                :key=\"source.label\"\n                                :value=\"source.value\"\n                                :label=\"source.label + ' [' + source.value + ']'\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n\n                    <el-form-item\n                        :label=\"getLabel(formFields['PANEL_APP_PORT_HTTP'])\"\n                        prop=\"params.PANEL_APP_PORT_HTTP\"\n                        v-if=\"formFields['PANEL_APP_PORT_HTTP']\"\n                    >\n                        <el-input\n                            v-model.number=\"runtime.params['PANEL_APP_PORT_HTTP']\"\n                            maxlength=\"15\"\n                            :disabled=\"mode == 'edit'\"\n                        ></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('app.containerName')\" prop=\"params.CONTAINER_NAME\">\n                        <el-input v-model.trim=\"runtime.params['CONTAINER_NAME']\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('php.extensions')\">\n                        <el-select v-model=\"extensions\" @change=\"changePHPExtension()\" clearable>\n                            <el-option\n                                v-for=\"(extension, index) in phpExtensions\"\n                                :key=\"index\"\n                                :label=\"extension.name\"\n                                :value=\"extension.extensions\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                    <el-form-item :label=\"getLabel(formFields['PHP_EXTENSIONS'])\" v-if=\"formFields['PHP_EXTENSIONS']\">\n                        <el-select v-model=\"runtime.params['PHP_EXTENSIONS']\" multiple allowCreate filterable>\n                            <el-option\n                                v-for=\"service in formFields['PHP_EXTENSIONS'].values\"\n                                :key=\"service.label\"\n                                :value=\"service.value\"\n                                :label=\"service.label\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n\n                    <el-form-item>\n                        <el-alert type=\"warning\" :closable=\"false\">\n                            <template #default>\n                                <div>\n                                    <div>{{ $t('runtime.buildHelper') }}</div>\n                                    <span>\n                                        {{ $t('runtime.extendHelper') }}\n                                    </span>\n                                    <div>\n                                        <span\n                                            v-if=\"!globalStore.isFxplay\"\n                                            class=\"custom-link\"\n                                            @click=\"openLink(globalStore.docsUrl + '/user_manual/websites/php/#php_1')\"\n                                        >\n                                            {{ $t('php.toExtensionsList') }}\n                                        </span>\n                                    </div>\n                                </div>\n                            </template>\n                        </el-alert>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                        <el-input type=\"textarea\" :rows=\"1\" clearable v-model=\"runtime.remark\" />\n                    </el-form-item>\n                    <el-form-item>\n                        <el-alert :title=\"$t('php.containerConfigHelper')\" type=\"info\" :closable=\"false\" />\n                    </el-form-item>\n                </div>\n            </div>\n            <div v-else>\n                <el-form-item>\n                    <el-alert type=\"info\" :closable=\"false\">\n                        <template #title>\n                            <span>\n                                {{ $t('runtime.localHelper') }}\n                                <el-link\n                                    class=\"ml-1 text-xs\"\n                                    type=\"primary\"\n                                    target=\"_blank\"\n                                    :href=\"globalStore.docsUrl + '/user_manual/websites/php/'\"\n                                >\n                                    {{ $t('commons.button.helpDoc') }}\n                                </el-link>\n                            </span>\n                        </template>\n                    </el-alert>\n                </el-form-item>\n                <el-form-item :label=\"$t('app.version')\" prop=\"version\">\n                    <el-input v-model=\"runtime.version\" :placeholder=\"$t('runtime.versionHelper')\"></el-input>\n                </el-form-item>\n            </div>\n        </el-form>\n        <template #footer>\n            <span>\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(runtimeForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { App } from '@/api/interface/app';\nimport { Runtime } from '@/api/interface/runtime';\nimport { getAppByKey, getAppDetail, searchApp } from '@/api/modules/app';\nimport { CreateRuntime, GetRuntime, ListPHPExtensions, UpdateRuntime } from '@/api/modules/runtime';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport { getLabel } from '@/utils/util';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { globalStore } = useGlobalStore();\n\ninterface OperateRrops {\n    id?: number;\n    mode: string;\n    type: string;\n    appID?: number;\n}\n\nconst open = ref(false);\nconst apps = ref<App.AppItem[]>([]);\nconst runtimeForm = ref<FormInstance>();\nconst loading = ref(false);\nconst initParam = ref(false);\nconst mode = ref('create');\nconst appParams = ref<App.AppParams>();\nconst appVersions = ref<string[]>([]);\nconst phpExtensions = ref([]);\nconst appReq = reactive({\n    type: 'php',\n    page: 1,\n    pageSize: 20,\n});\nconst phpSources = globalStore.isIntl\n    ? [\n          {\n              label: i18n.global.t('runtime.default'),\n              value: 'https://deb.debian.org',\n          },\n          {\n              label: i18n.global.t('runtime.xtom'),\n              value: 'https://mirrors.xtom.com',\n          },\n      ]\n    : [\n          {\n              label: i18n.global.t('runtime.ustc'),\n              value: 'https://mirrors.ustc.edu.cn',\n          },\n          {\n              label: i18n.global.t('runtime.netease'),\n              value: 'https://mirrors.163.com',\n          },\n          {\n              label: i18n.global.t('runtime.aliyun'),\n              value: 'https://mirrors.aliyun.com',\n          },\n          {\n              label: i18n.global.t('runtime.tsinghua'),\n              value: 'https://mirrors.tuna.tsinghua.edu.cn',\n          },\n          {\n              label: i18n.global.t('runtime.xtomhk'),\n              value: 'https://mirrors.xtom.com.hk',\n          },\n          {\n              label: i18n.global.t('runtime.xtom'),\n              value: 'https://mirrors.xtom.com',\n          },\n          {\n              label: i18n.global.t('commons.table.default'),\n              value: 'https://deb.debian.org',\n          },\n      ];\n\nconst initData = (type: string) => ({\n    name: '',\n    appDetailID: undefined,\n    image: '',\n    params: {},\n    type: type,\n    resource: 'appstore',\n    rebuild: false,\n    source: phpSources[0].value,\n    environments: [],\n    remark: '',\n});\nconst extensions = ref();\nconst formFields = ref();\n\nlet runtime = reactive<Runtime.RuntimeCreate>(initData('php'));\n\nconst rules = ref<any>({\n    name: [Rules.appName],\n    resource: [Rules.requiredInput],\n    appID: [Rules.requiredSelect],\n    version: [Rules.requiredInput, Rules.paramCommon],\n    image: [Rules.requiredInput, Rules.imageName],\n    source: [Rules.requiredSelect],\n    params: {\n        PANEL_APP_PORT_HTTP: [Rules.requiredInput, Rules.port],\n        PHP_VERSION: [Rules.requiredSelect],\n        CONTAINER_PACKAGE_URL: [Rules.requiredSelect],\n        CONTAINER_NAME: [Rules.containerName, Rules.requiredInput],\n    },\n});\n\nconst em = defineEmits(['close', 'submit']);\n\nconst openLink = (url: string) => {\n    window.open(url, '_blank');\n};\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst changeResource = (resource: string) => {\n    if (resource === 'local') {\n        runtime.appDetailID = undefined;\n        runtime.version = '';\n        runtime.params = {};\n        runtime.image = '';\n    } else {\n        runtime.version = '';\n        searchAppList(null);\n    }\n};\n\nconst searchAppList = (appId: number) => {\n    searchApp(appReq).then((res) => {\n        apps.value = res.data.items || [];\n        if (res.data && res.data.items && res.data.items.length > 0) {\n            if (appId == null) {\n                runtime.appID = res.data.items[0].id;\n                getApp(res.data.items[0].key, mode.value);\n            } else {\n                res.data.items.forEach((item) => {\n                    if (item.id === appId) {\n                        getApp(item.key, mode.value);\n                    }\n                });\n            }\n        }\n    });\n};\n\nconst changeApp = (appId: number) => {\n    extensions.value = undefined;\n    for (const app of apps.value) {\n        if (app.id === appId) {\n            initParam.value = false;\n            getApp(app.key, mode.value);\n            break;\n        }\n    }\n};\n\nconst changePHPVersion = (version: string) => {\n    runtime.image = '1panel-php-fpm:' + version;\n};\n\nconst changeVersion = () => {\n    loading.value = true;\n    initParam.value = false;\n    extensions.value = undefined;\n    getAppDetail(runtime.appID, runtime.version, 'runtime')\n        .then((res) => {\n            runtime.appDetailID = res.data.id;\n            runtime.image = res.data.image + ':' + runtime.version;\n            appParams.value = res.data.params;\n            const fileds = res.data.params.formFields;\n            formFields.value = {};\n            for (const index in fileds) {\n                formFields.value[fileds[index]['envKey']] = fileds[index];\n                runtime.params[fileds[index]['envKey']] = fileds[index]['default'];\n                if (fileds[index]['envKey'] == 'PHP_VERSION') {\n                    runtime.image = '1panel-php-fpm:' + fileds[index]['default'];\n                }\n            }\n            initParam.value = true;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst getApp = (appkey: string, mode: string) => {\n    getAppByKey(appkey).then((res) => {\n        appVersions.value = res.data.versions || [];\n        if (res.data.versions.length > 0) {\n            if (mode === 'create') {\n                runtime.version = res.data.versions[0];\n                changeVersion();\n            } else {\n                initParam.value = true;\n            }\n        }\n    });\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) {\n            return;\n        }\n        try {\n            let res;\n            if (mode.value == 'create') {\n                loading.value = true;\n                res = await CreateRuntime(runtime);\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n            } else {\n                loading.value = true;\n                res = await UpdateRuntime(runtime);\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            }\n            handleClose();\n            em('submit', res.data.id);\n        } catch (error) {\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\nconst getRuntime = async (id: number) => {\n    try {\n        const res = await GetRuntime(id);\n        const data = res.data;\n        Object.assign(runtime, {\n            id: data.id,\n            name: data.name,\n            appDetailID: data.appDetailID,\n            image: data.image,\n            params: data.params,\n            type: data.type,\n            resource: data.resource,\n            appID: data.appID,\n            version: data.version,\n            rebuild: true,\n            source: data.source,\n            remark: data.remark,\n            versionn: data.version,\n        });\n\n        const fileds = data.appParams;\n        const forms = {};\n        for (const index in fileds) {\n            forms[fileds[index].key] = fileds[index];\n        }\n        formFields.value = forms;\n        if (data.params['PHP_EXTENSIONS'] != '') {\n            runtime.params['PHP_EXTENSIONS'] = runtime.params['PHP_EXTENSIONS']\n                .split(',')\n                .filter((item) => item !== '');\n        }\n        initParam.value = true;\n    } catch (error) {}\n};\n\nconst listPHPExtensions = async () => {\n    try {\n        const res = await ListPHPExtensions({\n            all: true,\n            page: 1,\n            pageSize: 100,\n        });\n        phpExtensions.value = res.data.items;\n    } catch (error) {}\n};\n\nconst changePHPExtension = () => {\n    if (extensions.value == '') {\n        return;\n    }\n    runtime.params['PHP_EXTENSIONS'] = extensions.value.split(',');\n};\n\nconst acceptParams = async (props: OperateRrops) => {\n    mode.value = props.mode;\n    initParam.value = false;\n    if (props.mode === 'create') {\n        Object.assign(runtime, initData(props.type));\n        if (globalStore.isOffLine) {\n            runtime.resource = 'local';\n        } else {\n            searchAppList(null);\n        }\n    } else {\n        searchAppList(props.appID);\n        getRuntime(props.id);\n    }\n    extensions.value = '';\n    open.value = true;\n    listPHPExtensions();\n};\n\nwatch(\n    () => runtime.name,\n    (newVal) => {\n        if (newVal && mode.value == 'create') {\n            runtime.params['CONTAINER_NAME'] = newVal;\n        }\n    },\n    { deep: true },\n);\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped>\n.custom-link {\n    color: var(--el-color-primary);\n    cursor: pointer;\n    text-decoration: underline;\n    font-size: inherit;\n    line-height: inherit;\n}\n\n.custom-link:hover {\n    color: var(--el-color-primary-light-3);\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/extension-management/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('runtime.extension')\" size=\"large\" @close=\"handleClose\">\n        <el-descriptions title=\"\" border>\n            <el-descriptions-item :label=\"$t('runtime.loadedExtension')\" width=\"100px\">\n                <el-tag v-for=\"(ext, index) in extensions\" :key=\"index\" type=\"info\" class=\"mr-1 mt-1\">{{ ext }}</el-tag>\n            </el-descriptions-item>\n        </el-descriptions>\n        <div class=\"mt-5\">\n            <TableSearch @search=\"searchByName()\" v-model:searchName=\"searchName\" />\n        </div>\n        <div class=\"mt-5\">\n            <el-text>{{ $t('runtime.popularExtension') }}</el-text>\n        </div>\n        <ComplexTable :data=\"supportExtensions\" @search=\"search()\" :heightDiff=\"350\" v-loading=\"loading\">\n            <el-table-column prop=\"name\" :label=\"$t('commons.table.name')\" />\n            <el-table-column prop=\"installed\" :label=\"$t('commons.table.status')\">\n                <template #default=\"{ row }\">\n                    <el-icon v-if=\"row.installed\" color=\"green\"><Select /></el-icon>\n                    <el-icon v-else><CloseBold /></el-icon>\n                </template>\n            </el-table-column>\n            <fu-table-operations\n                :ellipsis=\"2\"\n                width=\"100px\"\n                :buttons=\"buttons\"\n                :label=\"$t('commons.table.operate')\"\n                fixed=\"right\"\n                fix\n            />\n        </ComplexTable>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" @close=\"search()\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { Runtime } from '@/api/interface/runtime';\nimport { GetPHPExtensions, InstallPHPExtension, UnInstallPHPExtension } from '@/api/modules/runtime';\nimport i18n from '@/lang';\nimport { ref } from 'vue';\nimport { newUUID } from '@/utils/util';\nimport { MsgSuccess } from '@/utils/message';\nimport TaskLog from '@/components/log/task/index.vue';\n\nconst open = ref(false);\nconst runtime = ref();\nconst extensions = ref([]);\nconst supportExtensions = ref([]);\nconst loading = ref(false);\nconst taskLogRef = ref();\nconst searchName = ref('');\nconst data = ref([]);\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.install'),\n        click: function (row: Runtime.SupportExtension) {\n            installExtension(row);\n        },\n        show: function (row: Runtime.SupportExtension) {\n            return !row.installed;\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.uninstall'),\n        click: function (row: Runtime.SupportExtension) {\n            unInstallPHPExtension(row);\n        },\n        show: function (row: Runtime.SupportExtension) {\n            return row.installed;\n        },\n    },\n];\n\nconst installExtension = async (row: Runtime.SupportExtension) => {\n    ElMessageBox.confirm(i18n.global.t('runtime.installExtension', [row.name]), i18n.global.t('runtime.extension'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        const req = {\n            id: runtime.value.id,\n            name: row.name,\n            taskID: newUUID(),\n        };\n        loading.value = true;\n        try {\n            await InstallPHPExtension(req);\n            taskLogRef.value.openWithTaskID(req.taskID);\n        } catch (error) {\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\nconst unInstallPHPExtension = async (row: Runtime.SupportExtension) => {\n    ElMessageBox.confirm(i18n.global.t('runtime.uninstallExtension', [row.name]), i18n.global.t('runtime.extension'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        const req = {\n            id: runtime.value.id,\n            name: row.name,\n        };\n        loading.value = true;\n        try {\n            await UnInstallPHPExtension(req);\n            MsgSuccess(i18n.global.t('commons.msg.uninstallSuccess'));\n            loading.value = false;\n            search();\n        } catch (error) {}\n    });\n};\n\nconst search = async () => {\n    try {\n        const res = await GetPHPExtensions(runtime.value.id);\n        extensions.value = res.data.extensions;\n        data.value = res.data.supportExtensions;\n        searchByName();\n    } catch (error) {}\n};\n\nconst searchByName = () => {\n    if (searchName.value === '') {\n        supportExtensions.value = data.value;\n        return;\n    }\n    supportExtensions.value = data.value.filter((ext) =>\n        ext.name.toLowerCase().includes(searchName.value.toLowerCase()),\n    );\n};\n\nconst acceptParams = (req: Runtime.Runtime): void => {\n    searchName.value = '';\n    open.value = true;\n    runtime.value = req;\n    search();\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/extension-template/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('php.extensions')\" size=\"large\" @close=\"handleClose\">\n        <template #content>\n            <ComplexTable :data=\"data\" @search=\"search()\" :pagination-config=\"paginationConfig\">\n                <template #toolbar>\n                    <el-button type=\"primary\" @click=\"openCreate\">{{ $t('commons.button.create') }}</el-button>\n                </template>\n                <el-table-column :label=\"$t('commons.table.name')\" width=\"150px\" prop=\"name\"></el-table-column>\n                <el-table-column :label=\"$t('php.extension')\" fix prop=\"extensions\"></el-table-column>\n                <fu-table-operations\n                    :ellipsis=\"10\"\n                    width=\"120px\"\n                    :buttons=\"buttons\"\n                    :label=\"$t('commons.table.operate')\"\n                    fixed=\"right\"\n                    fix\n                />\n            </ComplexTable>\n            <Create ref=\"createRef\" @close=\"search()\" />\n            <OpDialog ref=\"opRef\" @search=\"search\" />\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { DeletePHPExtensions, SearchPHPExtensions } from '@/api/modules/runtime';\nimport { reactive, ref } from 'vue';\nimport Create from './operate/index.vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport i18n from '@/lang';\n\nconst open = ref(false);\nconst data = ref();\nconst createRef = ref();\nconst opRef = ref();\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'website-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('website-page-size')) || 20,\n    total: 0,\n});\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Runtime.PHPExtensions) {\n            openUpdate(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Runtime.PHPExtensions) {\n            openDelete(row);\n        },\n    },\n];\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = (): void => {\n    open.value = true;\n    search();\n};\n\nconst openCreate = () => {\n    createRef.value.acceptParams('create');\n};\n\nconst search = async () => {\n    try {\n        const res = await SearchPHPExtensions({\n            page: paginationConfig.currentPage,\n            pageSize: paginationConfig.pageSize,\n        });\n        data.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {}\n};\n\nconst openDelete = async (row: Runtime.PHPExtensions) => {\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [row.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('php.extensions'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: DeletePHPExtensions,\n        params: { id: row.id },\n    });\n};\n\nconst openUpdate = async (row: Runtime.PHPExtensions) => {\n    createRef.value.acceptParams('edit', row);\n};\n\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/extension-template/operate/index.vue",
    "content": "<template>\n    <DialogPro\n        v-model=\"open\"\n        :title=\"$t('commons.button.' + operate) + $t('php.extensions')\"\n        size=\"small\"\n        @close=\"handleClose\"\n    >\n        <el-row v-loading=\"loading\">\n            <el-col :span=\"22\" :offset=\"1\">\n                <el-form @submit.prevent ref=\"extensionsForm\" label-position=\"top\" :model=\"extensions\" :rules=\"rules\">\n                    <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                        <el-input v-model.trim=\"extensions.name\" :disabled=\"operate == 'edit'\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('php.extension')\" prop=\"extensions\">\n                        <el-input\n                            type=\"textarea\"\n                            :placeholder=\"$t('php.extensionsHelper')\"\n                            :rows=\"3\"\n                            v-model=\"extensions.extensions\"\n                        />\n                    </el-form-item>\n                    <el-link\n                        target=\"_blank\"\n                        type=\"primary\"\n                        v-if=\"!globalStore.isFxplay\"\n                        :href=\"globalStore.docsUrl + '/user_manual/websites/php/#php_1'\"\n                    >\n                        {{ $t('php.toExtensionsList') }}\n                    </el-link>\n                </el-form>\n            </el-col>\n        </el-row>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit(extensionsForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport { FormInstance } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\nimport { CreatePHPExtensions, UpdatePHPExtensions } from '@/api/modules/runtime';\nimport i18n from '@/lang';\nimport { Runtime } from '@/api/interface/runtime';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst open = ref(false);\nconst operate = ref('create');\nconst loading = ref(false);\nconst updateID = ref(0);\nconst extensionsForm = ref<FormInstance>();\nconst rules = ref({\n    name: [Rules.requiredInput, Rules.name],\n    extensions: [Rules.requiredInput, Rules.phpExtensions],\n});\nconst em = defineEmits(['close']);\n\nconst initData = () => ({\n    name: '',\n    extensions: '',\n});\n\nconst extensions = ref(initData());\n\nconst acceptParams = (op: string, extend: Runtime.PHPExtensions) => {\n    operate.value = op;\n    open.value = true;\n    extensions.value = initData();\n    if (operate.value == 'edit') {\n        extensions.value = extend;\n        updateID.value = extend.id;\n    }\n};\n\nconst handleClose = () => {\n    open.value = false;\n    extensionsForm.value?.resetFields();\n    em('close', false);\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        if (operate.value == 'create') {\n            CreatePHPExtensions(extensions.value)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        } else {\n            UpdatePHPExtensions({\n                id: updateID.value,\n                name: extensions.value.name,\n                extensions: extensions.value.extensions,\n            })\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <DockerStatus v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" />\n        <LayoutContent v-loading=\"loading\" v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n\n                <el-button type=\"primary\" plain @click=\"openExtensions\">\n                    {{ $t('php.extensions') }}\n                </el-button>\n\n                <el-button type=\"primary\" plain @click=\"onOpenBuildCache()\">\n                    {{ $t('container.cleanBuildCache') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"php-runtime-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"items\" @search=\"search()\" :heightDiff=\"260\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"name\"\n                        min-width=\"120px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text\n                                type=\"primary\"\n                                class=\"cursor-pointer\"\n                                @click=\"openDetail(row)\"\n                                v-if=\"row.status != 'building'\"\n                            >\n                                {{ row.name }}\n                            </el-text>\n                            <el-text type=\"info\" class=\"cursor-pointer\" v-else>\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('home.dir')\" prop=\"codeDir\" width=\"100px\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"routerToFileWithPath(row.path)\">\n                                <el-icon>\n                                    <FolderOpened />\n                                </el-icon>\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('app.source')\" prop=\"resource\">\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.resource == 'appstore'\">{{ $t('menu.apps') }}</span>\n                            <span v-if=\"row.resource == 'local'\">{{ $t('commons.table.local') }}</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('app.version')\" prop=\"version\">\n                        <template #default=\"{ row }\">{{ row.params['PHP_VERSION'] }}</template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('container.image')\"\n                        prop=\"image\"\n                        show-overflow-tooltip\n                    ></el-table-column>\n                    <el-table-column :label=\"$t('commons.table.port')\" prop=\"port\">\n                        <template #default=\"{ row }\">\n                            {{ row.port }}\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\" width=\"100px\">\n                        <template #default=\"{ row }\">\n                            <RuntimeStatus :row=\"row\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" prop=\"\">\n                        <template #default=\"{ row }\">\n                            <el-button @click=\"openLog(row)\" link type=\"primary\" :disabled=\"row.resource == 'local'\">\n                                {{ $t('website.check') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.remark')\" prop=\"remark\" min-width=\"150px\">\n                        <template #default=\"{ row }\">\n                            <fu-read-write-switch>\n                                <template #read>\n                                    <MsgInfo :info=\"row.remark\" :width=\"'150'\" />\n                                </template>\n                                <template #default=\"{ read }\">\n                                    <el-input v-model=\"row.remark\" @blur=\"updateRuntimeRemark(row, read)\" />\n                                </template>\n                            </fu-read-write-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        width=\"180\"\n                        fix\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 3\"\n                        :width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        fixed=\"right\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n\n        <CreateRuntime ref=\"createRef\" @close=\"search\" @submit=\"openCreateLog\" />\n        <OpDialog ref=\"opRef\" @search=\"search\" />\n        <Log ref=\"logRef\" @close=\"search\" :heightDiff=\"200\" />\n        <Extensions ref=\"extensionsRef\" @close=\"search\" />\n        <AppResources ref=\"checkRef\" @close=\"search\" />\n        <ExtManagement ref=\"extManagementRef\" />\n        <ComposeLogs ref=\"composeLogRef\" :highlightDiff=\"200\" />\n        <Config ref=\"configRef\" />\n        <Supervisor ref=\"supervisorRef\" />\n        <Terminal ref=\"terminalRef\" />\n        <TaskLog ref=\"taskLogRef\" width=\"70%\" @close=\"search\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, reactive, ref } from 'vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport { DeleteRuntime, RuntimeDeleteCheck, SearchRuntimes } from '@/api/modules/runtime';\nimport { dateFormat, newUUID } from '@/utils/util';\nimport { ElMessageBox } from 'element-plus';\nimport { containerPrune } from '@/api/modules/container';\nimport TaskLog from '@/components/log/task/index.vue';\nimport i18n from '@/lang';\nimport ExtManagement from './extension-management/index.vue';\nimport Extensions from './extension-template/index.vue';\nimport AppResources from '@/views/website/runtime/php/check/index.vue';\nimport CreateRuntime from '@/views/website/runtime/php/create/index.vue';\nimport RouterMenu from '../index.vue';\nimport Log from '@/components/log/file-drawer/index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport Config from '@/views/website/runtime/php/config/index.vue';\nimport Supervisor from '@/views/website/runtime/php/supervisor/index.vue';\nimport RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';\nimport Terminal from '@/views/website/runtime/components/terminal.vue';\nimport { disabledButton } from '@/utils/runtime';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { operateRuntime, updateRuntimeRemark } from '../common/utils';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { globalStore } = useGlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst taskLogRef = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'runtime-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('runtime-page-size')) || 20,\n    total: 0,\n});\nlet req = reactive<Runtime.RuntimeReq>({\n    name: '',\n    page: 1,\n    pageSize: 40,\n    type: 'php',\n});\nconst opRef = ref();\nconst logRef = ref();\nconst extensionsRef = ref();\nconst extManagementRef = ref();\nconst checkRef = ref();\nconst createRef = ref();\nconst loading = ref(false);\nconst items = ref<Runtime.RuntimeDTO[]>([]);\nconst composeLogRef = ref();\nconst configRef = ref();\nconst supervisorRef = ref();\nconst terminalRef = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Runtime.Runtime) {\n            openDetail(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'edit');\n        },\n    },\n    {\n        label: i18n.global.t('runtime.extension'),\n        click: function (row: Runtime.Runtime) {\n            openExtensionsManagement(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'extension');\n        },\n    },\n    {\n        label: i18n.global.t('menu.terminal'),\n        click: function (row: Runtime.Runtime) {\n            openTerminal(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'config');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.stop'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('down', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'stop');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.start'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('up', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'start');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('restart', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'restart');\n        },\n    },\n    {\n        label: i18n.global.t('menu.config'),\n        click: function (row: Runtime.Runtime) {\n            openConfig(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'config');\n        },\n    },\n    {\n        label: i18n.global.t('menu.supervisor'),\n        click: function (row: Runtime.Runtime) {\n            openSupervisor(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'config');\n        },\n    },\n\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Runtime.Runtime) {\n            openDelete(row);\n        },\n    },\n];\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    try {\n        const res = await SearchRuntimes(req);\n        items.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst openCreate = () => {\n    createRef.value.acceptParams({ type: 'php', mode: 'create' });\n};\n\nconst openDetail = (row: Runtime.Runtime) => {\n    createRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id, appID: row.appID });\n};\n\nconst openConfig = (row: Runtime.Runtime) => {\n    configRef.value.acceptParams(row);\n};\n\nconst openSupervisor = (row: Runtime.Runtime) => {\n    supervisorRef.value.acceptParams(row.id);\n};\n\nconst openTerminal = (row: Runtime.Runtime) => {\n    const container = row.params['CONTAINER_NAME'];\n    terminalRef.value.acceptParams({ containerID: container, container: container, user: 'www-data' });\n};\n\nconst openLog = (row: Runtime.RuntimeDTO) => {\n    if (row.status == 'Running') {\n        composeLogRef.value.acceptParams({\n            compose: row.path + '/docker-compose.yml',\n            resource: row.name,\n            container: row.container,\n        });\n    } else {\n        logRef.value.acceptParams({ id: row.id, type: 'php', tail: row.status == 'Building' });\n    }\n};\n\nconst openCreateLog = (id: number) => {\n    logRef.value.acceptParams({ id: id, type: 'php', tail: true });\n};\n\nconst openExtensions = () => {\n    extensionsRef.value.acceptParams();\n};\n\nconst openExtensionsManagement = (row: Runtime.Runtime) => {\n    extManagementRef.value.acceptParams(row);\n};\n\nconst openDelete = async (row: Runtime.Runtime) => {\n    RuntimeDeleteCheck(row.id).then(async (res) => {\n        const items = res.data;\n        if (res.data && res.data.length > 0) {\n            checkRef.value.acceptParams({ items: items, key: 'website', installID: row.id });\n        } else {\n            opRef.value.acceptParams({\n                title: i18n.global.t('commons.button.delete'),\n                names: [row.name],\n                msg: i18n.global.t('commons.msg.operatorHelper', [\n                    i18n.global.t('website.runtime'),\n                    i18n.global.t('commons.button.delete'),\n                ]),\n                api: DeleteRuntime,\n                params: { id: row.id, forceDelete: true },\n            });\n        }\n    });\n};\n\nconst onOpenBuildCache = () => {\n    ElMessageBox.confirm(i18n.global.t('container.delBuildCacheHelper'), i18n.global.t('container.cleanBuildCache'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n        type: 'info',\n    }).then(async () => {\n        loading.value = true;\n        let params = {\n            taskID: newUUID(),\n            pruneType: 'buildcache',\n            withTagAll: false,\n        };\n        await containerPrune(params)\n            .then(() => {\n                loading.value = false;\n                openTaskLog(params.taskID);\n            })\n            .catch(() => {\n                loading.value = false;\n            });\n    });\n};\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.open-warn {\n    color: $primary-color;\n    cursor: pointer;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/supervisor/create/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"process.operate == 'create' ? $t('commons.button.create') : $t('commons.button.edit')\"\n        @close=\"handleClose\"\n        size=\"small\"\n    >\n        <el-form\n            ref=\"processForm\"\n            label-position=\"top\"\n            :model=\"process\"\n            label-width=\"100px\"\n            :rules=\"rules\"\n            v-loading=\"loading\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model.trim=\"process.name\" :disabled=\"process.operate == 'update'\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.user')\" prop=\"user\">\n                <el-input v-model.trim=\"process.user\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.dir')\" prop=\"dir\">\n                <el-input v-model.trim=\"process.dir\"></el-input>\n                <span class=\"input-help\">{{ $t('runtime.dirHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.command')\" prop=\"command\">\n                <el-input v-model=\"process.command\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.numprocs')\" prop=\"numprocsNum\">\n                <el-input type=\"number\" v-model.number=\"process.numprocsNum\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('process.env')\" prop=\"environment\">\n                <el-input type=\"text\" v-model=\"process.environment\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.autoRestart')\" prop=\"autoRestart\">\n                <el-switch v-model=\"process.autoRestart\" active-value=\"true\" inactive-value=\"false\"></el-switch>\n            </el-form-item>\n            <el-form-item :label=\"$t('tool.supervisor.autoStart')\" prop=\"autoStart\">\n                <el-switch v-model=\"process.autoStart\" active-value=\"true\" inactive-value=\"false\"></el-switch>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(processForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { createSupervisorProcess } from '@/api/modules/runtime';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { HostTool } from '@/api/interface/host-tool';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst processForm = ref<FormInstance>();\nconst rules = ref({\n    name: [Rules.requiredInput, Rules.supervisorName],\n    dir: [Rules.requiredInput],\n    command: [Rules.requiredInput],\n    user: [Rules.requiredInput],\n    numprocsNum: [Rules.requiredInput, Rules.integerNumber, checkNumberRange(1, 9999)],\n});\nconst initData = (runtimeID: number) => ({\n    operate: 'create',\n    name: '',\n    command: '',\n    user: 'www-data',\n    dir: '',\n    numprocsNum: 1,\n    numprocs: '1',\n    id: runtimeID,\n    autoRestart: 'true',\n    autoStart: 'true',\n    environment: '',\n});\nconst process = ref(initData(0));\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    open.value = false;\n    resetForm();\n    em('close', open);\n};\n\nconst resetForm = () => {\n    process.value = initData(0);\n    processForm.value?.resetFields();\n};\n\nconst acceptParams = (operate: string, config: HostTool.SupersivorProcess, id: number) => {\n    process.value = initData(id);\n    if (operate == 'update') {\n        process.value = {\n            operate: 'update',\n            name: config.name,\n            command: config.command,\n            user: config.user,\n            dir: config.dir,\n            numprocsNum: 1,\n            numprocs: config.numprocs,\n            id: id,\n            autoRestart: config.autoRestart,\n            autoStart: config.autoStart,\n            environment: config.environment || '',\n        };\n        process.value.numprocsNum = Number(config.numprocs);\n    }\n    open.value = true;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        process.value.numprocs = String(process.value.numprocsNum);\n        createSupervisorProcess(process.value)\n            .then(() => {\n                open.value = false;\n                em('close', open);\n                MsgSuccess(i18n.global.t('commons.msg.' + process.value.operate + 'Success'));\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/supervisor/file/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"title\" @close=\"handleClose\" size=\"large\" :fullScreen=\"true\">\n        <template #content>\n            <div v-if=\"req.file != 'config'\">\n                <el-tabs v-model=\"req.file\" type=\"card\" @tab-click=\"handleChange\">\n                    <el-tab-pane :label=\"$t('logs.runLog')\" name=\"out.log\"></el-tab-pane>\n                    <el-tab-pane :label=\"$t('logs.errLog')\" name=\"err.log\"></el-tab-pane>\n                </el-tabs>\n                <el-checkbox border v-model=\"tailLog\" class=\"float-left\" @change=\"changeTail\">\n                    {{ $t('commons.button.watch') }}\n                </el-checkbox>\n                <el-button class=\"ml-5\" @click=\"cleanLog\" icon=\"Delete\">\n                    {{ $t('commons.button.clean') }}\n                </el-button>\n            </div>\n            <br />\n            <div v-loading=\"loading\">\n                <CodemirrorPro class=\"mt-5\" v-model=\"content\" :heightDiff=\"400\"></CodemirrorPro>\n            </div>\n        </template>\n\n        <template #footer>\n            <span>\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" :disabled=\"loading\" @click=\"submit()\" v-if=\"req.file === 'config'\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <OpDialog ref=\"opRef\" @search=\"getContent\" />\n</template>\n<script lang=\"ts\" setup>\nimport { onUnmounted, reactive, ref } from 'vue';\nimport { operateSupervisorProcessFile } from '@/api/modules/runtime';\nimport i18n from '@/lang';\nimport { TabsPaneContext } from 'element-plus';\nimport { MsgSuccess } from '@/utils/message';\n\nconst loading = ref(false);\nconst content = ref('');\nconst tailLog = ref(false);\nconst open = ref(false);\nconst req = reactive({\n    name: '',\n    file: 'conf',\n    operate: '',\n    content: '',\n    id: 0,\n});\nconst title = ref('');\nconst opRef = ref();\nlet timer: NodeJS.Timer | null = null;\nconst em = defineEmits(['search']);\n\nconst getContent = () => {\n    loading.value = true;\n    operateSupervisorProcessFile(req)\n        .then((res) => {\n            content.value = res.data;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst handleChange = (tab: TabsPaneContext) => {\n    req.file = tab.props.name.toString();\n    getContent();\n};\n\nconst changeTail = () => {\n    if (tailLog.value) {\n        timer = setInterval(() => {\n            getContent();\n        }, 1000 * 5);\n    } else {\n        onCloseLog();\n    }\n};\n\nconst handleClose = () => {\n    content.value = '';\n    open.value = false;\n};\n\nconst submit = () => {\n    const updateReq = {\n        name: req.name,\n        operate: 'update',\n        file: req.file,\n        content: content.value,\n        id: req.id,\n    };\n    loading.value = true;\n    operateSupervisorProcessFile(updateReq)\n        .then(() => {\n            em('search');\n            open.value = false;\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst acceptParams = (name: string, file: string, operate: string, runtimeID: number) => {\n    req.name = name;\n    req.file = file;\n    req.operate = operate;\n    req.id = runtimeID;\n\n    title.value = file == 'config' ? i18n.global.t('website.source') : i18n.global.t('commons.button.log');\n    getContent();\n    open.value = true;\n};\n\nconst cleanLog = async () => {\n    let log = req.file === 'out.log' ? i18n.global.t('logs.runLog') : i18n.global.t('logs.errLog');\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.msg.clean'),\n        names: [req.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [log, i18n.global.t('commons.msg.clean')]),\n        api: operateSupervisorProcessFile,\n        params: { name: req.name, operate: 'clear', file: req.file, id: req.id },\n    });\n};\n\nconst onCloseLog = async () => {\n    tailLog.value = false;\n    clearInterval(Number(timer));\n    timer = null;\n};\n\nonUnmounted(() => {\n    onCloseLog();\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.fullScreen {\n    border: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/php/supervisor/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('tool.supervisor.list')\" size=\"60%\" @close=\"handleClose\">\n        <template #content>\n            <ComplexTable :data=\"data\" v-loading=\"loading\" v-model:selects=\"selects\">\n                <template #toolbar>\n                    <el-button type=\"primary\" @click=\"openCreate\">\n                        {{ $t('commons.button.create') }}\n                    </el-button>\n                    <el-button\n                        @click=\"batchRestart\"\n                        :disabled=\"!selects.length || selects.every((item) => item.name === 'php-fpm')\"\n                    >\n                        {{ $t('commons.button.restart') }}\n                    </el-button>\n                </template>\n                <el-table-column type=\"selection\" width=\"55\" :selectable=\"checkSelectable\" />\n                <el-table-column\n                    :label=\"$t('commons.table.name')\"\n                    fix\n                    prop=\"name\"\n                    min-width=\"80px\"\n                    show-overflow-tooltip\n                ></el-table-column>\n                <el-table-column\n                    :label=\"$t('tool.supervisor.command')\"\n                    prop=\"command\"\n                    min-width=\"100px\"\n                    fix\n                    show-overflow-tooltip\n                ></el-table-column>\n                <el-table-column\n                    :label=\"$t('tool.supervisor.dir')\"\n                    prop=\"dir\"\n                    min-width=\"100px\"\n                    fix\n                    show-overflow-tooltip\n                ></el-table-column>\n                <el-table-column\n                    :label=\"$t('tool.supervisor.user')\"\n                    prop=\"user\"\n                    show-overflow-tooltip\n                    min-width=\"60px\"\n                ></el-table-column>\n                <el-table-column\n                    :label=\"$t('tool.supervisor.numprocs')\"\n                    prop=\"numprocs\"\n                    min-width=\"60px\"\n                ></el-table-column>\n                <el-table-column :label=\"$t('tool.supervisor.manage')\" min-width=\"80px\">\n                    <template #default=\"{ row }\">\n                        <div v-if=\"row.status && row.status.length > 0 && row.hasLoad\">\n                            <Status\n                                v-if=\"checkStatus(row.status) === 'RUNNING'\"\n                                status=\"running\"\n                                @click=\"operate('stop', row.name)\"\n                            />\n                            <Status\n                                v-else-if=\"checkStatus(row.status) === 'WARNING'\"\n                                status=\"unhealthy\"\n                                @click=\"operate('restart', row.name)\"\n                            />\n                            <Status v-else status=\"stopped\" @click=\"operate('start', row.name)\" />\n                        </div>\n                        <div v-if=\"!row.hasLoad\">\n                            <el-button link loading></el-button>\n                        </div>\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('commons.table.status')\" min-width=\"60px\">\n                    <template #default=\"{ row }\">\n                        <div v-if=\"row.hasLoad\">\n                            <el-popover placement=\"bottom\" :width=\"600\" trigger=\"hover\">\n                                <template #reference>\n                                    <el-button type=\"primary\" link v-if=\"row.status.length > 1\">\n                                        {{ $t('website.check') }}\n                                    </el-button>\n                                    <el-button type=\"primary\" link v-else>\n                                        <span>{{ $t('tool.supervisor.' + row.status[0].status) }}</span>\n                                    </el-button>\n                                </template>\n                                <el-table :data=\"row.status\">\n                                    <el-table-column\n                                        property=\"name\"\n                                        :label=\"$t('commons.table.name')\"\n                                        fix\n                                        show-overflow-tooltip\n                                    />\n                                    <el-table-column\n                                        property=\"status\"\n                                        :label=\"$t('tool.supervisor.statusCode')\"\n                                        width=\"100px\"\n                                    />\n                                    <el-table-column property=\"PID\" label=\"PID\" width=\"100px\" />\n                                    <el-table-column\n                                        property=\"uptime\"\n                                        :label=\"$t('tool.supervisor.uptime')\"\n                                        width=\"100px\"\n                                    />\n                                    <el-table-column\n                                        property=\"msg\"\n                                        :label=\"$t('commons.table.message')\"\n                                        fix\n                                        show-overflow-tooltip\n                                    />\n                                </el-table>\n                            </el-popover>\n                        </div>\n                        <div v-if=\"!row.hasLoad\">\n                            <el-button link loading></el-button>\n                        </div>\n                    </template>\n                </el-table-column>\n                <fu-table-operations\n                    :ellipsis=\"6\"\n                    :buttons=\"buttons\"\n                    :label=\"$t('commons.table.operate')\"\n                    :fixed=\"mobile ? false : 'right'\"\n                    width=\"280px\"\n                    fix\n                />\n            </ComplexTable>\n        </template>\n    </DrawerPro>\n    <File ref=\"fileRef\" @search=\"search\" />\n    <Create ref=\"createRef\" @close=\"search\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { computed } from 'vue';\nimport Create from './create/index.vue';\nimport File from './file/index.vue';\nimport { GetSupervisorProcess, operateSupervisorProcess } from '@/api/modules/runtime';\nimport { GlobalStore } from '@/store';\nimport i18n from '@/lang';\nimport { HostTool } from '@/api/interface/host-tool';\nimport { MsgSuccess } from '@/utils/message';\nconst globalStore = GlobalStore();\n\nconst loading = ref(false);\nconst fileRef = ref();\nconst data = ref();\nconst createRef = ref();\nconst dataLoading = ref(false);\nconst open = ref(false);\nconst runtimeID = ref(0);\nconst selects = ref<any>([]);\n\nfunction checkSelectable(row) {\n    return row.name != 'php-fpm';\n}\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = async (id: number) => {\n    runtimeID.value = id;\n    search();\n    open.value = true;\n};\n\nconst openCreate = () => {\n    createRef.value.acceptParams('create', undefined, runtimeID.value);\n};\n\nconst search = async () => {\n    let needLoadStatus = false;\n    dataLoading.value = true;\n    try {\n        const res = await GetSupervisorProcess(runtimeID.value);\n        data.value = res.data;\n        for (const process of data.value) {\n            if (process.status && process.status.length > 0) {\n                process.hasLoad = true;\n            } else {\n                process.hasLoad = false;\n                needLoadStatus = true;\n            }\n        }\n        if (needLoadStatus) {\n            setTimeout(loadStatus, 1000);\n        }\n    } catch (error) {\n    } finally {\n        dataLoading.value = false;\n    }\n};\n\nconst loadStatus = async () => {\n    let needLoadStatus = false;\n    try {\n        const res = await GetSupervisorProcess(runtimeID.value);\n        const stats = res.data || [];\n        for (const process of data.value) {\n            for (const item of stats) {\n                if (process.name === item.name) {\n                    if (item.status && item.status.length > 0) {\n                        process.status = item.status;\n                        process.hasLoad = true;\n                    } else {\n                        needLoadStatus = true;\n                    }\n                }\n            }\n        }\n        if (needLoadStatus) {\n            setTimeout(loadStatus, 20000);\n        }\n    } catch (error) {}\n};\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst checkStatus = (status: HostTool.ProcessStatus[]): string => {\n    if (!status || status.length === 0) return 'STOPPED';\n\n    const statusCounts = status.reduce((acc, curr) => {\n        acc[curr.status] = (acc[curr.status] || 0) + 1;\n        return acc;\n    }, {} as Record<string, number>);\n\n    if (statusCounts['STARTING']) return 'STARTING';\n    if (statusCounts['RUNNING'] === status.length) return 'RUNNING';\n    if (statusCounts['RUNNING'] > 0) return 'WARNING';\n    return 'STOPPED';\n};\n\nconst operate = async (operation: string, name: string) => {\n    try {\n        ElMessageBox.confirm(\n            i18n.global.t('tool.supervisor.operatorHelper', [name, i18n.global.t('commons.operate.' + operation)]),\n            i18n.global.t('commons.operate.' + operation),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'info',\n            },\n        )\n            .then(() => {\n                loading.value = true;\n                operateSupervisorProcess({ operate: operation, name: name, id: runtimeID.value })\n                    .then(() => {\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                        search();\n                    })\n                    .catch(() => {})\n                    .finally(() => {\n                        loading.value = false;\n                    });\n            })\n            .catch(() => {});\n    } catch (error) {}\n};\n\nconst batchRestart = async () => {\n    if (!selects.value.length) return;\n\n    const filteredSelects = selects.value.filter((item) => item.name !== 'php-fpm');\n    if (!filteredSelects.length) {\n        return;\n    }\n\n    const names = filteredSelects.map((item) => item.name).join(', ');\n\n    try {\n        await ElMessageBox.confirm(\n            i18n.global.t('tool.supervisor.operatorHelper', [names, i18n.global.t('commons.operate.restart')]),\n            i18n.global.t('commons.button.restart'),\n            {\n                confirmButtonText: i18n.global.t('commons.button.confirm'),\n                cancelButtonText: i18n.global.t('commons.button.cancel'),\n                type: 'warning',\n            },\n        );\n\n        loading.value = true;\n\n        const promises = filteredSelects.map((item) =>\n            operateSupervisorProcess({\n                operate: 'restart',\n                name: item.name,\n                id: runtimeID.value,\n            }),\n        );\n\n        await Promise.all(promises);\n\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        selects.value = [];\n        search();\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst getFile = (name: string, file: string, runtimeID: number) => {\n    fileRef.value.acceptParams(name, file, 'get', runtimeID);\n};\n\nconst edit = (row: HostTool.SupersivorProcess) => {\n    createRef.value.acceptParams('update', row, runtimeID.value);\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: HostTool.SupersivorProcess) {\n            edit(row);\n        },\n        show: function (row: HostTool.SupersivorProcess) {\n            return row.name != 'php-fpm';\n        },\n    },\n    {\n        label: i18n.global.t('website.sourceFile'),\n        click: function (row: HostTool.SupersivorProcess) {\n            getFile(row.name, 'config', runtimeID.value);\n        },\n        show: function (row: HostTool.SupersivorProcess) {\n            return row.name != 'php-fpm';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.log'),\n        click: function (row: HostTool.SupersivorProcess) {\n            getFile(row.name, 'out.log', runtimeID.value);\n        },\n        show: function (row: HostTool.SupersivorProcess) {\n            return row.name != 'php-fpm';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: function (row: HostTool.SupersivorProcess) {\n            operate('restart', row.name);\n        },\n        show: function (row: HostTool.SupersivorProcess) {\n            return row.name != 'php-fpm';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: HostTool.SupersivorProcess) {\n            operate('delete', row.name);\n        },\n        show: function (row: HostTool.SupersivorProcess) {\n            return row.name != 'php-fpm';\n        },\n    },\n];\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/python/index.vue",
    "content": "<template>\n    <div>\n        <RouterMenu />\n        <DockerStatus v-model:isActive=\"isActive\" v-model:isExist=\"isExist\" />\n        <LayoutContent v-loading=\"loading\" v-if=\"isExist\" :class=\"{ mask: !isActive }\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableRefresh @search=\"search()\" />\n                <TableSetting title=\"python-runtime-refresh\" @search=\"search()\" />\n            </template>\n            <template #main>\n                <ComplexTable :pagination-config=\"paginationConfig\" :data=\"items\" @search=\"search()\" :heightDiff=\"260\">\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"name\"\n                        min-width=\"120px\"\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row }\">\n                            <el-text type=\"primary\" class=\"cursor-pointer\" @click=\"openDetail(row)\">\n                                {{ row.name }}\n                            </el-text>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.runDir')\" prop=\"codeDir\" min-width=\"120px\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"routerToFileWithPath(row.codeDir)\">\n                                <el-icon>\n                                    <FolderOpened />\n                                </el-icon>\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('app.version')\" prop=\"version\"></el-table-column>\n                    <el-table-column :label=\"$t('runtime.externalPort')\" prop=\"port\" min-width=\"110px\">\n                        <template #default=\"{ row }\">\n                            <PortJump :row=\"row\" :jump=\"goDashboard\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.table.status')\" prop=\"status\">\n                        <template #default=\"{ row }\">\n                            <RuntimeStatus :row=\"row\" />\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" prop=\"path\" min-width=\"90px\">\n                        <template #default=\"{ row }\">\n                            <el-button @click=\"openLog(row)\" link type=\"primary\" v-if=\"row.status != 'Stopped'\">\n                                {{ $t('website.check') }}\n                            </el-button>\n                            <span v-else>-</span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.remark')\" prop=\"remark\" min-width=\"150px\">\n                        <template #default=\"{ row }\">\n                            <fu-read-write-switch>\n                                <template #read>\n                                    <MsgInfo :info=\"row.remark\" :width=\"'150'\" />\n                                </template>\n                                <template #default=\"{ read }\">\n                                    <el-input v-model=\"row.remark\" @blur=\"updateRuntimeRemark(row, read)\" />\n                                </template>\n                            </fu-read-write-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"createdAt\"\n                        :label=\"$t('commons.table.date')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        min-width=\"120\"\n                        fix\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"mobile ? 0 : 5\"\n                        :min-width=\"mobile ? 'auto' : 300\"\n                        :buttons=\"buttons\"\n                        fixed=\"right\"\n                        :label=\"$t('commons.table.operate')\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n        </LayoutContent>\n        <Operate ref=\"operateRef\" @close=\"search\" />\n        <Delete ref=\"deleteRef\" @close=\"search\" />\n        <ComposeLogs ref=\"composeLogRef\" />\n        <PortJumpDialog ref=\"dialogPortJumpRef\" />\n        <AppResources ref=\"checkRef\" @close=\"search\" />\n        <Terminal ref=\"terminalRef\" />\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, reactive, ref } from 'vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport { RuntimeDeleteCheck, SearchRuntimes, SyncRuntime } from '@/api/modules/runtime';\nimport { dateFormat } from '@/utils/util';\nimport Operate from '@/views/website/runtime/python/operate/index.vue';\nimport Delete from '@/views/website/runtime/delete/index.vue';\nimport i18n from '@/lang';\nimport RouterMenu from '../index.vue';\nimport ComposeLogs from '@/components/log/compose/index.vue';\nimport PortJumpDialog from '@/components/port-jump/index.vue';\nimport AppResources from '@/views/website/runtime/php/check/index.vue';\nimport RuntimeStatus from '@/views/website/runtime/components/runtime-status.vue';\nimport PortJump from '@/views/website/runtime/components/port-jump.vue';\nimport Terminal from '@/views/website/runtime/components/terminal.vue';\nimport DockerStatus from '@/views/container/docker-status/index.vue';\nimport { disabledButton } from '@/utils/runtime';\nimport { GlobalStore } from '@/store';\nimport { operateRuntime, updateRuntimeRemark } from '../common/utils';\nimport { routerToFileWithPath } from '@/utils/router';\nconst globalStore = GlobalStore();\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst loading = ref(false);\nconst items = ref<Runtime.RuntimeDTO[]>([]);\nconst operateRef = ref();\nconst deleteRef = ref();\nconst dialogPortJumpRef = ref();\nconst composeLogRef = ref();\nconst checkRef = ref();\nconst terminalRef = ref();\nconst isActive = ref(false);\nconst isExist = ref(false);\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'runtime-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('runtime-page-size')) || 20,\n    total: 0,\n});\nconst req = reactive<Runtime.RuntimeReq>({\n    name: '',\n    page: 1,\n    pageSize: 40,\n    type: 'python',\n});\nconst buttons = [\n    {\n        label: i18n.global.t('commons.operate.stop'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('down', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'stop');\n        },\n    },\n    {\n        label: i18n.global.t('commons.operate.start'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('up', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'start');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.restart'),\n        click: function (row: Runtime.Runtime) {\n            operateRuntime('restart', row.id, loading, search);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'restart');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Runtime.Runtime) {\n            openDetail(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'edit');\n        },\n    },\n    {\n        label: i18n.global.t('menu.terminal'),\n        click: function (row: Runtime.Runtime) {\n            openTerminal(row);\n        },\n        disabled: function (row: Runtime.Runtime) {\n            return disabledButton(row, 'config');\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Runtime.Runtime) {\n            openDelete(row);\n        },\n    },\n];\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n    loading.value = true;\n    try {\n        const res = await SearchRuntimes(req);\n        items.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst sync = () => {\n    SyncRuntime();\n};\n\nconst openCreate = () => {\n    operateRef.value.acceptParams({ type: 'python', mode: 'create' });\n};\n\nconst openDetail = (row: Runtime.Runtime) => {\n    operateRef.value.acceptParams({ type: row.type, mode: 'edit', id: row.id });\n};\n\nconst openDelete = async (row: Runtime.Runtime) => {\n    RuntimeDeleteCheck(row.id).then(async (res) => {\n        const items = res.data;\n        if (res.data && res.data.length > 0) {\n            checkRef.value.acceptParams({ items: items, key: 'website', installID: row.id });\n        } else {\n            deleteRef.value.acceptParams(row.id, row.name);\n        }\n    });\n};\n\nconst openLog = (row: any) => {\n    composeLogRef.value.acceptParams({\n        compose: row.path + '/docker-compose.yml',\n        resource: row.name,\n        container: row.container,\n    });\n};\n\nconst goDashboard = async (port: any, protocol: string) => {\n    dialogPortJumpRef.value.acceptParams({ port: port, protocol: protocol });\n};\n\nconst openTerminal = (row: Runtime.Runtime) => {\n    const container = row.params['CONTAINER_NAME'];\n    terminalRef.value.acceptParams({ containerID: container, container: container });\n};\n\nonMounted(() => {\n    sync();\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped></style>\n"
  },
  {
    "path": "frontend/src/views/website/runtime/python/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('commons.button.' + mode)\"\n        size=\"large\"\n        :resource=\"mode === 'edit' ? runtime.name : ''\"\n        @close=\"handleClose\"\n    >\n        <el-form\n            v-loading=\"loading\"\n            ref=\"runtimeForm\"\n            label-position=\"top\"\n            :model=\"runtime\"\n            label-width=\"125px\"\n            :rules=\"rules\"\n            :validate-on-rule-change=\"false\"\n        >\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input :disabled=\"mode === 'edit'\" v-model=\"runtime.name\"></el-input>\n            </el-form-item>\n            <DirConfig v-model=\"runtime\" :mode=\"mode\" :scriptHelper=\"$t('runtime.pythonHelper')\" />\n            <AppConfig v-model=\"runtime\" :mode=\"mode\" appKey=\"python\" />\n            <el-form-item :label=\"$t('app.containerName')\" prop=\"params.CONTAINER_NAME\">\n                <el-input v-model.trim=\"runtime.params['CONTAINER_NAME']\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                <el-input type=\"textarea\" :rows=\"1\" clearable v-model=\"runtime.remark\" />\n            </el-form-item>\n            <NodeConfig v-model=\"runtime\" />\n        </el-form>\n\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit(runtimeForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport NodeConfig from '../../components/node-config.vue';\nimport AppConfig from '@/views/website/runtime/app/index.vue';\nimport DirConfig from '@/views/website/runtime/components/dir/index.vue';\n\nimport { App } from '@/api/interface/app';\nimport { Runtime } from '@/api/interface/runtime';\nimport { CreateRuntime, GetRuntime, UpdateRuntime } from '@/api/modules/runtime';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref } from 'vue';\n\ninterface OperateRrops {\n    id?: number;\n    mode: string;\n    type: string;\n}\n\nconst open = ref(false);\nconst runtimeForm = ref<FormInstance>();\nconst loading = ref(false);\nconst mode = ref('create');\nconst editParams = ref<App.InstallParams[]>();\nconst initData = (type: string) => ({\n    name: '',\n    appDetailID: undefined,\n    image: '',\n    params: {\n        HOST_IP: '0.0.0.0',\n    },\n    type: type,\n    resource: 'appstore',\n    rebuild: false,\n    codeDir: '/',\n    port: 8080,\n    exposedPorts: [],\n    environments: [],\n    extraHosts: [],\n    volumes: [],\n    remark: '',\n});\nlet runtime = reactive<Runtime.RuntimeCreate>(initData('python'));\nconst rules = ref<any>({\n    name: [Rules.requiredInput, Rules.appName],\n    appID: [Rules.requiredSelect],\n    codeDir: [Rules.requiredInput],\n    port: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n    source: [Rules.requiredSelect],\n    params: {\n        APP_PORT: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n        HOST_IP: [Rules.requiredSelect],\n        CONTAINER_NAME: [Rules.requiredInput, Rules.containerName],\n        EXEC_SCRIPT: [Rules.requiredInput],\n    },\n});\nconst scripts = ref<Runtime.NodeScripts[]>([]);\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n    runtimeForm.value?.resetFields();\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        if (runtime.exposedPorts && runtime.exposedPorts.length > 0) {\n            const containerPortMap = new Map();\n            const hostPortMap = new Map();\n            for (const port of runtime.exposedPorts) {\n                if (containerPortMap[port.containerPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                if (hostPortMap[port.hostPort]) {\n                    MsgError(i18n.global.t('runtime.portError'));\n                    return;\n                }\n                hostPortMap[port.hostPort] = true;\n                containerPortMap[port.containerPort] = true;\n            }\n        }\n\n        if (mode.value == 'create') {\n            loading.value = true;\n            CreateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        } else {\n            loading.value = true;\n            UpdateRuntime(runtime)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\nconst getRuntime = async (id: number) => {\n    try {\n        const res = await GetRuntime(id);\n        const data = res.data;\n        Object.assign(runtime, {\n            id: data.id,\n            name: data.name,\n            appDetailID: data.appDetailID,\n            image: data.image,\n            type: data.type,\n            resource: data.resource,\n            appID: data.appID,\n            version: data.version,\n            rebuild: true,\n            source: data.source,\n            params: data.params,\n            codeDir: data.codeDir,\n            port: data.port,\n            remark: data.remark,\n        });\n        runtime.exposedPorts = data.exposedPorts || [];\n        runtime.environments = data.environments || [];\n        runtime.volumes = data.volumes || [];\n        runtime.extraHosts = data.extraHosts || [];\n        editParams.value = data.appParams;\n        open.value = true;\n    } catch (error) {}\n};\n\nconst acceptParams = async (props: OperateRrops) => {\n    mode.value = props.mode;\n    scripts.value = [];\n    if (props.mode === 'create') {\n        Object.assign(runtime, initData(props.type));\n        open.value = true;\n    } else {\n        getRuntime(props.id);\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/acme-account/create/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.create')\" size=\"small\" @close=\"handleClose\">\n        <el-row v-loading=\"loading\">\n            <el-col :span=\"22\" :offset=\"1\">\n                <el-form @submit.prevent ref=\"accountForm\" label-position=\"top\" :model=\"account\" :rules=\"rules\">\n                    <el-form-item :label=\"$t('website.email')\" prop=\"email\">\n                        <el-input v-model.trim=\"account.email\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.useProxy')\" prop=\"useProxy\" v-if=\"globalStore.isProductPro\">\n                        <el-switch v-model=\"account.useProxy\"></el-switch>\n                        <span class=\"input-help\">\n                            {{ $t('website.useProxyHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.acmeAccountType')\" prop=\"type\">\n                        <el-select v-model=\"account.type\">\n                            <el-option\n                                v-for=\"(acme, index) in AcmeAccountTypes\"\n                                :key=\"index\"\n                                :label=\"acme.label\"\n                                :value=\"acme.value\"\n                            ></el-option>\n                        </el-select>\n                        <span class=\"input-help\" v-if=\"account.type === 'buypass'\">\n                            {{ $t('ssl.buypassHelper') }}\n                        </span>\n                        <span class=\"input-help\" v-if=\"account.type == 'google'\">\n                            {{ $t('ssl.googleCloudHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.keyType')\" prop=\"keyType\">\n                        <el-select v-model=\"account.keyType\">\n                            <el-option\n                                v-for=\"(keyType, index) in KeyTypes\"\n                                :key=\"index\"\n                                :label=\"keyType.label\"\n                                :value=\"keyType.value\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                    <div v-if=\"account.type == 'custom'\">\n                        <el-form-item :label=\"$t('ssl.customAcmeURL')\" prop=\"caDirURL\">\n                            <el-input v-model.trim=\"account.caDirURL\"></el-input>\n                        </el-form-item>\n                        <el-form-item :label=\"$t('ssl.useEAB')\" prop=\"useEAB\">\n                            <el-switch v-model=\"account.useEAB\"></el-switch>\n                        </el-form-item>\n                    </div>\n\n                    <div\n                        v-if=\"\n                            account.type == 'google' ||\n                            account.type == 'freessl' ||\n                            (account.type === 'custom' && account.useEAB)\n                        \"\n                    >\n                        <el-form-item label=\"EAB kid\" prop=\"eabKid\">\n                            <el-input v-model.trim=\"account.eabKid\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"EAB HmacKey\" prop=\"eabHmacKey\">\n                            <el-input type=\"textarea\" :rows=\"3\" v-model.trim=\"account.eabHmacKey\"></el-input>\n                        </el-form-item>\n                        <el-link\n                            v-if=\"account.type == 'google'\"\n                            class=\"ml-1.5\"\n                            type=\"primary\"\n                            target=\"_blank\"\n                            href=\"https://cloud.google.com/certificate-manager/docs/public-ca-tutorial?hl=zh-cn\"\n                        >\n                            {{ $t('ssl.googleHelper') }}\n                        </el-link>\n                    </div>\n                </el-form>\n            </el-col>\n        </el-row>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(accountForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport { createAcmeAccount } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { AcmeAccountTypes, KeyTypes } from '@/global/mimetype';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst open = ref();\nconst loading = ref(false);\nconst accountForm = ref<FormInstance>();\nconst rules = ref({\n    email: [Rules.requiredInput, Rules.email],\n    type: [Rules.requiredSelect],\n    eabKid: [Rules.requiredInput],\n    eabHmacKey: [Rules.requiredInput],\n    keyType: [Rules.requiredSelect],\n    caDirURL: [Rules.requiredInput],\n});\n\nconst initData = () => ({\n    email: '',\n    type: 'letsencrypt',\n    eabKid: '',\n    eabHmacKey: '',\n    keyType: 'P256',\n    useProxy: false,\n    caDirURL: '',\n    useEAB: false,\n});\n\nconst account = ref(initData());\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    resetForm();\n    open.value = false;\n    em('close', false);\n};\n\nconst resetForm = () => {\n    accountForm.value.resetFields();\n    account.value = initData();\n};\n\nconst acceptParams = () => {\n    open.value = true;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n\n        createAcmeAccount(account.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/acme-account/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('website.acmeAccountManage')\" size=\"large\" @close=\"handleClose\">\n        <template #content>\n            <div class=\"mb-1.5\">\n                <el-alert :title=\"$t('ssl.acmeHelper')\" type=\"info\" :closable=\"false\" />\n            </div>\n            <ComplexTable :data=\"data\" :pagination-config=\"paginationConfig\" v-loading=\"loading\">\n                <template #toolbar>\n                    <el-button type=\"primary\" @click=\"openCreate\">{{ $t('commons.button.create') }}</el-button>\n                </template>\n                <el-table-column\n                    :label=\"$t('website.email')\"\n                    fix\n                    show-overflow-tooltip\n                    prop=\"email\"\n                    min-width=\"100px\"\n                ></el-table-column>\n                <el-table-column :label=\"$t('website.acmeAccountType')\" fix show-overflow-tooltip prop=\"type\">\n                    <template #default=\"{ row }\">\n                        {{ getAccountName(row.type) }}\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('website.keyType')\" prop=\"keyType\">\n                    <template #default=\"{ row }\">\n                        {{ getKeyName(row.keyType) }}\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('website.useProxy')\" min-width=\"100px\" v-if=\"globalStore.isProductPro\">\n                    <template #default=\"{ row }\">\n                        <el-switch v-model=\"row.useProxy\" @change=\"update(row)\"></el-switch>\n                    </template>\n                </el-table-column>\n                <el-table-column label=\"URL\" show-overflow-tooltip prop=\"url\" min-width=\"300px\"></el-table-column>\n                <fu-table-operations\n                    :ellipsis=\"1\"\n                    :buttons=\"buttons\"\n                    :label=\"$t('commons.table.operate')\"\n                    fixed=\"right\"\n                    fix\n                />\n            </ComplexTable>\n            <Create ref=\"createRef\" @close=\"search()\"></Create>\n        </template>\n    </DrawerPro>\n    <OpDialog ref=\"opRef\" @search=\"search\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { deleteAcmeAccount, searchAcmeAccount, updateAcmeAccount } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { reactive, ref } from 'vue';\nimport Create from './create/index.vue';\nimport { getAccountName, getKeyName } from '@/utils/util';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst open = ref(false);\nconst loading = ref(false);\nconst data = ref();\nconst createRef = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'acme-account-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('acme-account-page-size')) || 20,\n    total: 0,\n});\nconst opRef = ref();\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.AcmeAccount) {\n            deleteAccount(row);\n        },\n    },\n];\n\nconst acceptParams = () => {\n    search();\n    open.value = true;\n};\n\nconst search = async () => {\n    const req = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    await searchAcmeAccount(req).then((res) => {\n        data.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    });\n};\n\nconst update = (row: Website.AcmeAccount) => {\n    updateAcmeAccount(row).then(() => {\n        search();\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n    });\n};\n\nconst openCreate = () => {\n    createRef.value.acceptParams();\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst deleteAccount = async (row: any) => {\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [row.email],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.acmeAccountManage'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteAcmeAccount,\n        params: { id: row.id },\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/apply/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('ssl.apply')\" size=\"large\" @close=\"handleClose\">\n        <div v-if=\"loading\">\n            <el-alert type=\"info\" :closable=\"false\" center>{{ $t('ssl.getDnsResolve') }}</el-alert>\n        </div>\n\n        <div v-if=\"dnsResolve.length > 0\">\n            <span>{{ $t('ssl.dnsResolveHelper') }}</span>\n            <el-table :data=\"dnsResolve\" border :table-layout=\"'auto'\">\n                <el-table-column prop=\"domain\" :label=\"$t('website.domain')\" />\n                <el-table-column prop=\"resolve\" :label=\"$t('ssl.resolveDomain')\">\n                    <template #default=\"{ row }\">\n                        <span>{{ row.resolve }}</span>\n                        <CopyButton :content=\"row.resolve\" />\n                    </template>\n                </el-table-column>\n                <el-table-column prop=\"value\" :label=\"$t('ssl.value')\">\n                    <template #default=\"{ row }\">\n                        <span>{{ row.value }}</span>\n                        <CopyButton :content=\"row.value\" />\n                    </template>\n                </el-table-column>\n                <el-table-column :label=\"$t('commons.table.type')\">TXT</el-table-column>\n            </el-table>\n        </div>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { getDnsResolve, obtainSSL } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ref } from 'vue';\n\ninterface RenewProps {\n    ssl: Website.SSL;\n}\n\nconst open = ref(false);\nconst loading = ref(false);\nconst dnsResolve = ref<Website.DNSResolve[]>([]);\nconst sslID = ref(0);\nconst em = defineEmits(['close', 'submit']);\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = async (props: RenewProps) => {\n    open.value = true;\n    dnsResolve.value = [];\n    sslID.value = props.ssl.id;\n    getDnsResolveRes(props.ssl);\n};\n\nconst getDnsResolveRes = async (row: Website.SSL) => {\n    loading.value = true;\n    try {\n        const res = await getDnsResolve({ acmeAccountId: row.acmeAccountId, websiteSSLId: row.id });\n        if (res.data) {\n            dnsResolve.value = res.data;\n        }\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst submit = () => {\n    obtainSSL({ ID: sslID.value })\n        .then(() => {\n            MsgSuccess(i18n.global.t('ssl.applyStart'));\n            handleClose();\n            em('submit', sslID.value);\n        })\n        .finally(() => {});\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/ca/create/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.create')\" @close=\"handleClose\">\n        <el-row v-loading=\"loading\">\n            <el-col :span=\"22\" :offset=\"1\">\n                <el-form @submit.prevent ref=\"caForm\" label-position=\"top\" :model=\"ca\" :rules=\"rules\">\n                    <el-form-item :label=\"$t('ssl.caName')\" prop=\"name\">\n                        <el-input v-model.trim=\"ca.name\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.commonName')\" prop=\"commonName\">\n                        <el-input v-model.trim=\"ca.commonName\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.company')\" prop=\"organization\">\n                        <el-input v-model.trim=\"ca.organization\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.department')\" prop=\"organizationUint\">\n                        <el-input v-model.trim=\"ca.organizationUint\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.country')\" prop=\"country\">\n                        <el-input v-model.trim=\"ca.country\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.province')\" prop=\"province\">\n                        <el-input v-model.trim=\"ca.province\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.city')\" prop=\"city\">\n                        <el-input v-model.trim=\"ca.city\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.keyType')\" prop=\"keyType\">\n                        <el-select v-model=\"ca.keyType\">\n                            <el-option\n                                v-for=\"(keyType, index) in KeyTypes\"\n                                :key=\"index\"\n                                :label=\"keyType.label\"\n                                :value=\"keyType.value\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                </el-form>\n            </el-col>\n        </el-row>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(caForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\nimport { KeyTypes } from '@/global/mimetype';\nimport { createCA } from '@/api/modules/website';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst caForm = ref<FormInstance>();\nconst em = defineEmits(['close']);\n\nconst rules = ref({\n    keyType: [Rules.requiredSelect],\n    name: [Rules.requiredInput, Rules.name],\n    country: [Rules.requiredSelect],\n    organization: [Rules.requiredInput, Rules.name],\n    commonName: [Rules.requiredInput, Rules.name],\n});\n\nconst initData = () => ({\n    name: '',\n    keyType: 'P256',\n    commonName: '',\n    country: 'CN',\n    organization: '',\n    organizationUint: '',\n    province: '',\n    city: '',\n});\n\nconst ca = ref(initData());\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n    resetForm();\n};\n\nconst resetForm = () => {\n    caForm.value.resetFields();\n    ca.value = initData();\n};\n\nconst acceptParams = () => {\n    open.value = true;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n\n        createCA(ca.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/ca/detail/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('ssl.organizationDetail')\" @close=\"handleClose\" size=\"normal\">\n        <div v-loading=\"loading\">\n            <el-radio-group v-model=\"curr\">\n                <el-radio-button value=\"detail\">{{ $t('ssl.organizationDetail') }}</el-radio-button>\n                <el-radio-button value=\"ssl\">{{ $t('ssl.ssl') }}</el-radio-button>\n                <el-radio-button value=\"key\">{{ $t('ssl.key') }}</el-radio-button>\n            </el-radio-group>\n            <div v-if=\"curr === 'detail'\" class=\"mt-5\">\n                <el-descriptions border :column=\"1\">\n                    <el-descriptions-item :label=\"$t('commons.table.name')\">\n                        {{ ca.name }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('ssl.commonName')\">\n                        {{ ca.commonName }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('website.brand')\">\n                        {{ ca.organization }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('ssl.department')\">\n                        {{ ca.organizationUint }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('ssl.country')\">\n                        {{ ca.country }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('ssl.province')\">\n                        {{ ca.province }}\n                    </el-descriptions-item>\n                    <el-descriptions-item :label=\"$t('ssl.city')\">\n                        {{ ca.city }}\n                    </el-descriptions-item>\n                </el-descriptions>\n            </div>\n            <div v-else-if=\"curr === 'ssl'\" class=\"mt-5\">\n                <el-input v-model=\"ca.csr\" :rows=\"15\" type=\"textarea\" id=\"textArea\" />\n                <div>\n                    <br />\n                    <CopyButton :content=\"ca.csr\" :isIcon=\"false\" />\n                </div>\n            </div>\n            <div v-else class=\"mt-5\">\n                <el-input v-model=\"ca.privateKey\" :rows=\"15\" type=\"textarea\" id=\"textArea\" />\n                <div>\n                    <br />\n                    <CopyButton :content=\"ca.privateKey\" :isIcon=\"false\" />\n                </div>\n            </div>\n        </div>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { getCA } from '@/api/modules/website';\nimport { ref } from 'vue';\n\nconst open = ref(false);\nconst id = ref(0);\nconst curr = ref('detail');\nconst ca = ref<any>({});\nconst loading = ref(false);\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = (caID: number) => {\n    ca.value = {};\n    id.value = caID;\n    curr.value = 'detail';\n    get();\n    open.value = true;\n};\n\nconst get = async () => {\n    const res = await getCA(id.value);\n    ca.value = res.data;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/ca/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('ssl.selfSigned')\" size=\"large\" @close=\"handleClose\">\n        <template #content>\n            <ComplexTable :data=\"data\" :pagination-config=\"paginationConfig\" @search=\"search()\" v-loading=\"loading\">\n                <template #toolbar>\n                    <el-button type=\"primary\" @click=\"openCreate\">{{ $t('commons.button.create') }}</el-button>\n                </template>\n                <el-table-column :label=\"$t('commons.table.name')\" show-overflow-tooltip prop=\"name\"></el-table-column>\n                <el-table-column :label=\"$t('website.keyType')\" show-overflow-tooltip prop=\"keyType\">\n                    <template #default=\"{ row }\">\n                        {{ getKeyName(row.keyType) }}\n                    </template>\n                </el-table-column>\n                <el-table-column\n                    prop=\"createdAt\"\n                    :label=\"$t('commons.table.date')\"\n                    :formatter=\"dateFormat\"\n                    show-overflow-tooltip\n                />\n                <fu-table-operations\n                    :ellipsis=\"3\"\n                    :buttons=\"buttons\"\n                    :label=\"$t('commons.table.operate')\"\n                    fix\n                    width=\"250px\"\n                />\n            </ComplexTable>\n            <Create ref=\"createRef\" @close=\"search()\" />\n            <Obtain ref=\"obtainRef\" @close=\"search()\" />\n            <Detail ref=\"detailRef\" />\n        </template>\n    </DrawerPro>\n    <OpDialog ref=\"opRef\" @search=\"search\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { deleteCA, searchCAs, downloadCAFile } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { reactive, ref } from 'vue';\nimport Create from './create/index.vue';\nimport Detail from './detail/index.vue';\nimport { getKeyName, dateFormat } from '@/utils/util';\nimport Obtain from './obtain/index.vue';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst data = ref();\nconst createRef = ref();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'ca-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('ca-page-size')) || 20,\n    total: 0,\n});\nconst opRef = ref();\nconst obtainRef = ref();\nconst em = defineEmits(['close']);\nconst detailRef = ref();\n\nconst buttons = [\n    {\n        label: i18n.global.t('ssl.selfSign'),\n        click: function (row: Website.CA) {\n            obtain(row);\n        },\n    },\n    {\n        label: i18n.global.t('ssl.detail'),\n        click: function (row: Website.CA) {\n            detailRef.value.acceptParams(row.id);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.download'),\n        click: function (row: Website.CA) {\n            onDownload(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.CA) {\n            deleteca(row);\n        },\n    },\n];\n\nconst acceptParams = () => {\n    search();\n    open.value = true;\n};\n\nconst obtain = (row: any) => {\n    obtainRef.value.acceptParams(row.id);\n};\n\nconst search = async () => {\n    const req = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    await searchCAs(req).then((res) => {\n        data.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    });\n};\n\nconst openCreate = () => {\n    createRef.value.acceptParams();\n};\n\nconst handleClose = () => {\n    em('close', false);\n    open.value = false;\n};\n\nconst deleteca = async (row: any) => {\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [row.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('ssl.ca'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteCA,\n        params: { id: row.id },\n    });\n};\n\nconst onDownload = (row: Website.CA) => {\n    loading.value = true;\n    downloadCAFile({ id: row.id })\n        .then((res) => {\n            const downloadUrl = window.URL.createObjectURL(new Blob([res]));\n            const a = document.createElement('a');\n            a.style.display = 'none';\n            a.href = downloadUrl;\n            a.download = row.name + '.zip';\n            const event = new MouseEvent('click');\n            a.dispatchEvent(event);\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/ca/obtain/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('ssl.selfSigned')\" @close=\"handleClose\">\n        <el-row v-loading=\"loading\">\n            <el-col :span=\"22\" :offset=\"1\">\n                <el-form @submit.prevent ref=\"obtainForm\" label-position=\"top\" :model=\"obtain\" :rules=\"rules\">\n                    <el-form-item :label=\"$t('website.domain')\" prop=\"domains\">\n                        <el-input\n                            type=\"textarea\"\n                            :rows=\"4\"\n                            v-model=\"obtain.domains\"\n                            :placeholder=\"$t('ssl.domainHelper')\"\n                        ></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.remark')\" prop=\"description\">\n                        <el-input v-model=\"obtain.description\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.keyType')\" prop=\"keyType\">\n                        <el-select v-model=\"obtain.keyType\">\n                            <el-option\n                                v-for=\"(keyType, index) in KeyTypes\"\n                                :key=\"index\"\n                                :label=\"keyType.label\"\n                                :value=\"keyType.value\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.days')\" prop=\"time\">\n                        <el-input type=\"number\" v-model.number=\"obtain.time\">\n                            <template #append>\n                                <el-select v-model=\"obtain.unit\" style=\"width: 100px\">\n                                    <el-option :label=\"$t('commons.units.day')\" value=\"day\"></el-option>\n                                    <el-option :label=\"$t('commons.units.year')\" value=\"year\"></el-option>\n                                </el-select>\n                            </template>\n                        </el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"''\" prop=\"autoRenew\">\n                        <el-checkbox v-model=\"obtain.autoRenew\" :label=\"$t('ssl.autoRenew')\" />\n                    </el-form-item>\n                    <el-form-item :label=\"''\" prop=\"pushDir\">\n                        <el-checkbox v-model=\"obtain.pushDir\" :label=\"$t('ssl.pushDir')\" />\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.dir')\" prop=\"dir\" v-if=\"obtain.pushDir\">\n                        <el-input v-model.trim=\"obtain.dir\">\n                            <template #prepend>\n                                <el-button\n                                    icon=\"Folder\"\n                                    @click=\"fileRef.acceptParams({ path: obtain.dir, dir: true })\"\n                                />\n                            </template>\n                        </el-input>\n                        <span class=\"input-help\">\n                            {{ $t('ssl.pushDirHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item :label=\"''\" prop=\"execShell\">\n                        <el-checkbox v-model=\"obtain.execShell\" :label=\"$t('ssl.execShell')\" />\n                    </el-form-item>\n                    <el-form-item :label=\"$t('ssl.shell')\" prop=\"shell\" v-if=\"obtain.execShell\">\n                        <el-input type=\"textarea\" :rows=\"4\" v-model=\"obtain.shell\" />\n                        <span class=\"input-help\">\n                            {{ $t('ssl.shellHelper') }}\n                        </span>\n                    </el-form-item>\n                </el-form>\n            </el-col>\n        </el-row>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(obtainForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n    <FileList ref=\"fileRef\" @choose=\"getPath\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { obtainSSLByCA } from '@/api/modules/website';\nimport { Rules, checkNumberRange, checkMaxLength } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport FileList from '@/components/file-list/index.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { KeyTypes } from '@/global/mimetype';\n\nconst open = ref(false);\nconst fileRef = ref();\nconst loading = ref(false);\nconst obtainForm = ref<FormInstance>();\nconst em = defineEmits(['close']);\n\nconst rules = ref({\n    keyType: [Rules.requiredSelect],\n    domains: [Rules.requiredInput],\n    dir: [Rules.requiredInput],\n    time: [Rules.integerNumber, checkNumberRange(1, 10000)],\n    shell: [Rules.requiredInput],\n    description: [checkMaxLength(128)],\n});\n\nconst initData = () => ({\n    keyType: 'P256',\n    domains: '',\n    id: 0,\n    time: 10,\n    unit: 'year',\n    pushDir: false,\n    dir: '',\n    autoRenew: true,\n    description: '',\n    execShell: false,\n    shell: '',\n});\nconst obtain = ref(initData());\n\nconst acceptParams = (id: number) => {\n    open.value = true;\n    obtain.value.id = id;\n};\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n    resetForm();\n};\n\nconst resetForm = () => {\n    obtainForm.value?.resetFields();\n    obtain.value = initData();\n};\n\nconst getPath = (dir: string) => {\n    obtain.value.dir = dir;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n\n        obtainSSLByCA(obtain.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('ssl.' + operate)\" size=\"large\" @close=\"handleClose\">\n        <el-form ref=\"sslForm\" label-position=\"top\" :model=\"ssl\" label-width=\"100px\" :rules=\"rules\" v-loading=\"loading\">\n            <el-row :gutter=\"20\">\n                <el-col :span=\"12\">\n                    <el-form-item :label=\"$t('website.primaryDomain')\" prop=\"primaryDomain\">\n                        <el-input v-model.trim=\"ssl.primaryDomain\"></el-input>\n                    </el-form-item>\n                </el-col>\n                <el-col :span=\"12\">\n                    <el-form-item :label=\"$t('ssl.fromWebsite')\">\n                        <el-select v-model=\"websiteID\" @change=\"changeWebsite\">\n                            <el-option\n                                v-for=\"(site, key) in websites\"\n                                :key=\"key\"\n                                :value=\"site.id\"\n                                :label=\"site.primaryDomain\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n            <el-form-item :label=\"''\" prop=\"autoRenew\">\n                <el-checkbox v-model=\"ssl.isIP\" :label=\"$t('ssl.isIP')\" @change=\"changeIP\" />\n            </el-form-item>\n            <el-form-item :label=\"$t('website.otherDomains')\" prop=\"otherDomains\" v-if=\"!ssl.isIP\">\n                <el-input type=\"textarea\" :rows=\"3\" v-model=\"ssl.otherDomains\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"description\">\n                <el-input v-model=\"ssl.description\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.acmeAccount')\" prop=\"acmeAccountId\" v-if=\"ssl.provider != 'selfSigned'\">\n                <el-select v-model=\"ssl.acmeAccountId\">\n                    <el-option\n                        v-for=\"(acme, index) in acmeAccounts\"\n                        :key=\"index\"\n                        :label=\"acme.email + ' [' + getAccountName(acme.type) + '] '\"\n                        :value=\"acme.id\"\n                        :disabled=\"ssl.isIP && acme.type !== 'letsencrypt'\"\n                    >\n                        <el-row>\n                            <el-col :span=\"20\">\n                                <span class=\"dns-name\">{{ acme.email }}</span>\n                            </el-col>\n                            <el-col :span=\"4\">\n                                <span>\n                                    <el-tag type=\"success\">{{ getAccountName(acme.type) }}</el-tag>\n                                </span>\n                            </el-col>\n                        </el-row>\n                    </el-option>\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.keyType')\" prop=\"keyType\">\n                <el-select v-model=\"ssl.keyType\" :disabled=\"operate == 'edit'\">\n                    <el-option\n                        v-for=\"(keyType, index) in KeyTypes\"\n                        :key=\"index\"\n                        :label=\"keyType.label\"\n                        :value=\"keyType.value\"\n                    ></el-option>\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.provider')\" prop=\"provider\" v-if=\"ssl.provider != 'selfSigned'\">\n                <el-radio-group v-model=\"ssl.provider\" @change=\"changeProvider()\">\n                    <el-radio value=\"dnsAccount\" :disabled=\"ssl.isIP\">{{ $t('website.dnsAccount') }}</el-radio>\n                    <el-radio value=\"dnsManual\" :disabled=\"ssl.isIP\">{{ $t('website.dnsManual') }}</el-radio>\n                    <el-radio value=\"http\">HTTP</el-radio>\n                </el-radio-group>\n                <span class=\"input-help\" v-if=\"ssl.provider === 'dnsManual'\">\n                    {{ $t('ssl.dnsMauanlHelper') }}\n                </span>\n                <span class=\"input-help text-red-500\" v-if=\"ssl.provider === 'http'\">\n                    {{ $t('ssl.httpHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.dnsAccount')\" prop=\"dnsAccountId\" v-if=\"ssl.provider === 'dnsAccount'\">\n                <el-select v-model=\"ssl.dnsAccountId\">\n                    <el-option\n                        v-for=\"(dns, index) in dnsAccounts\"\n                        :key=\"index\"\n                        :label=\"dns.name + ' [' + getDNSName(dns.type) + '] '\"\n                        :value=\"dns.id\"\n                    >\n                        <el-row>\n                            <el-col :span=\"20\">\n                                <span class=\"dns-name\">{{ dns.name }}</span>\n                            </el-col>\n                            <el-col :span=\"4\">\n                                <span>\n                                    <el-tag type=\"success\">{{ getDNSName(dns.type) }}</el-tag>\n                                </span>\n                            </el-col>\n                        </el-row>\n                    </el-option>\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"''\" prop=\"autoRenew\" v-if=\"ssl.provider !== 'dnsManual'\">\n                <el-checkbox v-model=\"ssl.autoRenew\" :label=\"$t('ssl.autoRenew')\" />\n            </el-form-item>\n            <div v-if=\"ssl.provider != 'selfSigned'\">\n                <el-form-item :label=\"''\" prop=\"disableCNAME\">\n                    <el-checkbox v-model=\"ssl.disableCNAME\" :label=\"$t('ssl.disableCNAME')\" />\n                    <span class=\"input-help\">\n                        {{ $t('ssl.disableCNAMEHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item :label=\"''\" prop=\"skipDNS\">\n                    <el-checkbox v-model=\"ssl.skipDNS\" :label=\"$t('ssl.skipDNSCheck')\" />\n                    <span class=\"input-help\">\n                        {{ $t('ssl.skipDNSCheckHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item :label=\"$t('ssl.nameserver') + '1'\" prop=\"nameserver1\">\n                    <el-input v-model.trim=\"ssl.nameserver1\"></el-input>\n                    <span class=\"input-help\">\n                        {{ $t('ssl.nameserverHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item :label=\"$t('ssl.nameserver') + '2'\" prop=\"nameserver1\">\n                    <el-input v-model.trim=\"ssl.nameserver2\"></el-input>\n                    <span class=\"input-help\">\n                        {{ $t('ssl.nameserverHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item :label=\"''\" prop=\"pushDir\">\n                    <el-checkbox v-model=\"ssl.pushDir\" :label=\"$t('ssl.pushDir')\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('ssl.dir')\" prop=\"dir\" v-if=\"ssl.pushDir\">\n                    <el-input v-model.trim=\"ssl.dir\">\n                        <template #prepend>\n                            <el-button icon=\"Folder\" @click=\"fileRef.acceptParams({ path: ssl.dir, dir: true })\" />\n                        </template>\n                    </el-input>\n                    <span class=\"input-help\">\n                        {{ $t('ssl.pushDirHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item :label=\"''\" prop=\"execShell\">\n                    <el-checkbox v-model=\"ssl.execShell\" :label=\"$t('ssl.execShell')\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('ssl.shell')\" prop=\"shell\" v-if=\"ssl.execShell\">\n                    <el-input type=\"textarea\" :rows=\"4\" v-model=\"ssl.shell\" />\n                    <span class=\"input-help\">\n                        {{ $t('ssl.shellHelper') }}\n                    </span>\n                </el-form-item>\n                <PushtoNode\n                    v-if=\"isMaster && isMasterProductPro\"\n                    :push-node=\"ssl.pushNode\"\n                    :nodes=\"ssl.pushNodes\"\n                    type=\"ssl\"\n                    @update:push-node=\"ssl.pushNode = $event\"\n                    @update:nodes=\"ssl.pushNodes = $event\"\n                />\n            </div>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(sslForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"fileRef\" @choose=\"getPath\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { createSSL, listWebsites, searchAcmeAccount, searchDnsAccount, updateSSL } from '@/api/modules/website';\nimport { Rules, checkMaxLength } from '@/global/form-rules';\nimport FileList from '@/components/file-list/index.vue';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { computed, reactive, ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { KeyTypes } from '@/global/mimetype';\nimport { getDNSName, getAccountName } from '@/utils/util';\nimport { defineAsyncComponent } from 'vue';\nimport { useGlobalStore } from '@/composables/useGlobalStore';\nconst { isMasterProductPro, isMaster } = useGlobalStore();\n\nconst PushtoNode = defineAsyncComponent(async () => {\n    const modules = import.meta.glob('@/xpack/views/ssl/index.vue');\n    const loader = modules['/src/xpack/views/ssl/index.vue'];\n    if (loader) {\n        return ((await loader()) as any).default;\n    }\n    return { template: '<div></div>' };\n});\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst id = computed(() => {\n    return props.id;\n});\n\nconst open = ref(false);\nconst fileRef = ref();\nconst loading = ref(false);\nconst dnsReq = reactive({\n    page: 1,\n    pageSize: 20,\n});\nconst acmeReq = reactive({\n    page: 1,\n    pageSize: 20,\n});\nconst dnsAccounts = ref<Website.DnsAccount[]>();\nconst acmeAccounts = ref<Website.AcmeAccount[]>();\nconst sslForm = ref<FormInstance>();\nconst websites = ref();\nconst rules = ref({\n    primaryDomain: [Rules.requiredInput, Rules.domainOrIP],\n    acmeAccountId: [Rules.requiredSelectBusiness],\n    dnsAccountId: [Rules.requiredSelectBusiness],\n    provider: [Rules.requiredInput],\n    autoRenew: [Rules.requiredInput],\n    keyType: [Rules.requiredInput],\n    dir: [Rules.requiredInput],\n    nameserver1: [Rules.ipv4],\n    nameserver2: [Rules.ipv4],\n    shell: [Rules.requiredInput],\n    description: [checkMaxLength(128)],\n    pushNodes: [Rules.requiredSelect],\n});\nconst websiteID = ref();\n\nconst initData = () => ({\n    id: 0,\n    primaryDomain: '',\n    otherDomains: '',\n    provider: 'dnsAccount',\n    websiteId: 0,\n    acmeAccountId: undefined,\n    dnsAccountId: undefined,\n    autoRenew: true,\n    keyType: 'P256',\n    pushDir: false,\n    dir: '',\n    description: '',\n    disableCNAME: false,\n    skipDNS: false,\n    nameserver1: '',\n    nameserver2: '',\n    execShell: false,\n    shell: '',\n    pushNode: false,\n    pushNodes: [],\n    nodes: '',\n    isIP: false,\n});\n\nconst ssl = ref(initData());\nconst operate = ref('create');\nconst dnsResolve = ref<Website.DNSResolve[]>([]);\nconst em = defineEmits(['close', 'submit']);\n\nconst handleClose = () => {\n    resetForm();\n    open.value = false;\n    em('close', false);\n};\nconst resetForm = () => {\n    sslForm.value?.resetFields();\n    dnsResolve.value = [];\n    ssl.value = initData();\n    websiteID.value = undefined;\n};\n\nconst changeIP = () => {\n    if (ssl.value.isIP) {\n        ssl.value.provider = 'http';\n        for (const acmeAccount of acmeAccounts.value!) {\n            if (acmeAccount.type == 'letsencrypt') {\n                ssl.value.acmeAccountId = acmeAccount.id;\n                break;\n            }\n        }\n    } else {\n        ssl.value.provider = 'dnsAccount';\n    }\n};\n\nconst acceptParams = (op: string, websiteSSL: Website.SSLDTO) => {\n    operate.value = op;\n    if (op == 'create') {\n        resetForm();\n    }\n    if (op == 'edit') {\n        ssl.value.acmeAccountId = websiteSSL.acmeAccountId;\n        if (websiteSSL.dnsAccountId > 0) {\n            ssl.value.dnsAccountId = websiteSSL.dnsAccountId;\n        }\n        ssl.value.primaryDomain = websiteSSL.primaryDomain;\n        ssl.value.pushDir = websiteSSL.pushDir;\n        ssl.value.dir = websiteSSL.dir;\n        ssl.value.otherDomains = websiteSSL.domains?.replace(/,/g, '\\n');\n        ssl.value.autoRenew = websiteSSL.autoRenew;\n        ssl.value.description = websiteSSL.description;\n        ssl.value.id = websiteSSL.id;\n        ssl.value.provider = websiteSSL.provider;\n        ssl.value.skipDNS = websiteSSL.skipDNS;\n        ssl.value.disableCNAME = websiteSSL.disableCNAME;\n        ssl.value.nameserver1 = websiteSSL.nameserver1;\n        ssl.value.nameserver2 = websiteSSL.nameserver2;\n        ssl.value.keyType = websiteSSL.keyType;\n        ssl.value.execShell = websiteSSL.execShell;\n        ssl.value.shell = websiteSSL.shell;\n        if (ssl.value.provider == 'selfSigned') {\n            rules.value.primaryDomain = [];\n        }\n        ssl.value.pushNode = websiteSSL.pushNode;\n        if (websiteSSL.nodes != '') {\n            ssl.value.pushNodes = websiteSSL.nodes\n                .split(',')\n                .map((item) => item.trim())\n                .filter((item) => item !== '');\n        }\n        ssl.value.isIP = websiteSSL.isIP;\n    }\n    ssl.value.websiteId = Number(id.value);\n    getAcmeAccounts();\n    getDnsAccounts();\n    getwebsites();\n    open.value = true;\n};\n\nconst getPath = (dir: string) => {\n    ssl.value.dir = dir;\n};\n\nconst getAcmeAccounts = async () => {\n    const res = await searchAcmeAccount(acmeReq);\n    acmeAccounts.value = res.data.items || [];\n    if (acmeAccounts.value.length > 0 && ssl.value.acmeAccountId == undefined) {\n        ssl.value.acmeAccountId = res.data.items[0].id;\n    }\n};\n\nconst getDnsAccounts = async () => {\n    const res = await searchDnsAccount(dnsReq);\n    dnsAccounts.value = res.data.items || [];\n    if (dnsAccounts.value.length > 0 && ssl.value.dnsAccountId == undefined) {\n        ssl.value.dnsAccountId = res.data.items[0].id;\n    }\n};\n\nconst changeProvider = () => {\n    dnsResolve.value = [];\n};\n\nconst getwebsites = async () => {\n    const res = await listWebsites();\n    websites.value = res.data;\n};\n\nconst changeWebsite = () => {\n    if (websiteID.value > 0) {\n        const selectedWebsite = websites.value.find((website) => website.id == websiteID.value);\n\n        if (selectedWebsite && selectedWebsite.domains && selectedWebsite.domains.length > 0) {\n            const primaryDomain = selectedWebsite.domains[0].domain;\n            const otherDomains = selectedWebsite.domains\n                .slice(1)\n                .map((domain) => domain.domain)\n                .join('\\n');\n\n            ssl.value.primaryDomain = primaryDomain;\n            ssl.value.otherDomains = otherDomains;\n        }\n    }\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        let nodes = '';\n        if (ssl.value.pushNode) {\n            nodes = ssl.value.pushNodes.join(',');\n        }\n        loading.value = true;\n        if (operate.value == 'create') {\n            ssl.value.nodes = nodes;\n            createSSL(ssl.value)\n                .then((res: any) => {\n                    if (ssl.value.provider != 'dnsManual') {\n                        em('submit', res.data.id);\n                    }\n                    handleClose();\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n        if (operate.value == 'edit') {\n            const sslUpdate = {\n                id: ssl.value.id,\n                primaryDomain: ssl.value.primaryDomain,\n                otherDomains: ssl.value.otherDomains,\n                acmeAccountId: ssl.value.acmeAccountId,\n                dnsAccountId: ssl.value.dnsAccountId,\n                autoRenew: ssl.value.autoRenew,\n                keyType: ssl.value.keyType,\n                pushDir: ssl.value.pushDir,\n                dir: ssl.value.dir,\n                description: ssl.value.description,\n                provider: ssl.value.provider,\n                disableCNAME: ssl.value.disableCNAME,\n                skipDNS: ssl.value.skipDNS,\n                nameserver1: ssl.value.nameserver1,\n                nameserver2: ssl.value.nameserver2,\n                execShell: ssl.value.execShell,\n                shell: ssl.value.shell,\n                pushNode: ssl.value.pushNode,\n                nodes: nodes,\n                isIP: ssl.value.isIP,\n            };\n            updateSSL(sslUpdate)\n                .then(() => {\n                    handleClose();\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n<style lang=\"scss\" scoped>\n.dns-name {\n    display: inline-block;\n    max-width: 300px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    vertical-align: top;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/detail/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('ssl.detail')\" size=\"large\" @close=\"handleClose\">\n        <template #content>\n            <div v-loading=\"loading\">\n                <el-radio-group v-model=\"curr\">\n                    <el-radio-button value=\"detail\">{{ $t('ssl.msg') }}</el-radio-button>\n                    <el-radio-button value=\"ssl\">{{ $t('ssl.ssl') }}</el-radio-button>\n                    <el-radio-button value=\"key\">{{ $t('ssl.key') }}</el-radio-button>\n                </el-radio-group>\n                <div v-if=\"curr === 'detail'\" class=\"mt-5\">\n                    <el-descriptions border :column=\"1\">\n                        <el-descriptions-item :label=\"$t('website.primaryDomain')\">\n                            {{ ssl.primaryDomain }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('website.otherDomains')\">\n                            {{ ssl.domains }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('ssl.commonName')\">\n                            {{ ssl.type }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('website.brand')\">\n                            {{ ssl.organization }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('ssl.startDate')\">\n                            {{ dateFormatSimple(ssl.startDate) }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('website.expireDate')\">\n                            {{ dateFormatSimple(ssl.expireDate) }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('ssl.applyType')\">\n                            {{ getProvider(ssl.provider) }}\n                        </el-descriptions-item>\n                        <el-descriptions-item\n                            :label=\"$t('website.dnsAccount')\"\n                            v-if=\"ssl.dnsAccount && ssl.dnsAccount.id > 0\"\n                        >\n                            {{ ssl.dnsAccount.name }}\n                            <el-tag type=\"info\">{{ getDNSName(ssl.dnsAccount.type) }}</el-tag>\n                        </el-descriptions-item>\n                        <el-descriptions-item\n                            :label=\"$t('ssl.acmeAccount')\"\n                            v-if=\"ssl.acmeAccount && ssl.acmeAccount.id > 0\"\n                        >\n                            {{ ssl.acmeAccount.email }}\n                        </el-descriptions-item>\n                        <el-descriptions-item :label=\"$t('ssl.pushDir')\" v-if=\"ssl.pushDir\">\n                            {{ ssl.dir }}\n                        </el-descriptions-item>\n                    </el-descriptions>\n                </div>\n                <div v-else-if=\"curr === 'ssl'\" class=\"mt-5\">\n                    <el-input v-model=\"ssl.pem\" :rows=\"15\" type=\"textarea\" id=\"textArea\" />\n                    <div>\n                        <br />\n                        <CopyButton :content=\"ssl.pem\" :isIcon=\"false\" />\n                    </div>\n                </div>\n                <div v-else class=\"mt-5\">\n                    <el-input v-model=\"ssl.privateKey\" :rows=\"15\" type=\"textarea\" id=\"textArea\" />\n                    <div>\n                        <br />\n                        <CopyButton :content=\"ssl.privateKey\" :isIcon=\"false\" />\n                    </div>\n                </div>\n            </div>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { getSSL } from '@/api/modules/website';\nimport { ref } from 'vue';\nimport { dateFormatSimple, getProvider, getDNSName } from '@/utils/util';\n\nconst open = ref(false);\nconst id = ref(0);\nconst curr = ref('detail');\nconst ssl = ref<any>({});\nconst loading = ref(false);\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = (sslId: number) => {\n    ssl.value = {};\n    id.value = sslId;\n    curr.value = 'detail';\n    get();\n    open.value = true;\n};\n\nconst get = async () => {\n    const res = await getSSL(id.value);\n    ssl.value = res.data;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/dns-account/create/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('commons.button.' + accountData.mode)\" @close=\"handleClose\">\n        <el-row>\n            <el-col :span=\"22\" :offset=\"1\">\n                <el-form ref=\"accountForm\" label-position=\"top\" :model=\"account\" :rules=\"rules\">\n                    <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                        <el-input v-model.trim=\"account.name\"></el-input>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('commons.table.type')\" prop=\"type\">\n                        <el-select v-model=\"account.type\" :disabled=\"accountData.mode === 'edit'\">\n                            <el-option\n                                v-for=\"(type, index) in DNSTypes\"\n                                :key=\"index\"\n                                :label=\"type.label\"\n                                :value=\"type.value\"\n                            ></el-option>\n                        </el-select>\n                        <span class=\"input-help text-red-500\" v-if=\"account.type === 'DnsPod'\">\n                            {{ $t('ssl.deprecatedHelper') }}\n                        </span>\n                    </el-form-item>\n                    <div\n                        v-if=\"\n                            account.type === 'AliYun' ||\n                            account.type === 'AliESA' ||\n                            account.type === 'HuaweiCloud' ||\n                            account.type === 'BaiduCloud' ||\n                            account.type === 'AWSRoute53'\n                        \"\n                    >\n                        <el-form-item label=\"Access key\" prop=\"authorization.accessKey\">\n                            <el-input v-model.trim=\"account.authorization['accessKey']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Secret key\" prop=\"authorization.secretKey\">\n                            <el-input v-model.trim=\"account.authorization['secretKey']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <el-form-item label=\"Region\" prop=\"authorization.region\" v-if=\"account.type === 'HuaweiCloud'\">\n                        <el-input v-model.trim=\"account.authorization['region']\" :placeholder=\"'cn-north-1'\"></el-input>\n                    </el-form-item>\n                    <div v-if=\"account.type === 'TencentCloud'\">\n                        <el-form-item label=\"Secret ID\" prop=\"authorization.secretID\">\n                            <el-input v-model.trim=\"account.authorization['secretID']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Secret Key\" prop=\"authorization.secretKey\">\n                            <el-input v-model.trim=\"account.authorization['secretKey']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'DnsPod'\">\n                        <el-form-item label=\"ID\" prop=\"authorization.id\">\n                            <el-input v-model.trim=\"account.authorization['id']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Token\" prop=\"authorization.token\">\n                            <el-input v-model.trim=\"account.authorization['token']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'Volcengine'\">\n                        <el-form-item label=\"Access key\" prop=\"authorization.accessKey\">\n                            <el-input v-model.trim=\"account.authorization['accessKey']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Secret key\" prop=\"authorization.secretKey\">\n                            <el-input v-model.trim=\"account.authorization['secretKey']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'CloudDns'\">\n                        <el-form-item label=\"Client ID\" prop=\"authorization.clientID\">\n                            <el-input v-model.trim=\"account.authorization['clientID']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Email\" prop=\"authorization.email\">\n                            <el-input v-model.trim=\"account.authorization['email']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Password\" prop=\"authorization.password\">\n                            <el-input v-model.trim=\"account.authorization['password']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'CloudFlare'\">\n                        <el-form-item label=\"EMAIL\" prop=\"authorization.email\">\n                            <el-input v-model.trim=\"account.authorization['email']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"API Token\" prop=\"authorization.apiKey\">\n                            <el-input v-model.trim=\"account.authorization['apiKey']\"></el-input>\n                            <span class=\"input-help text-red-500\">\n                                {{ $t('ssl.cfHelper') }}\n                            </span>\n                        </el-form-item>\n                    </div>\n                    <el-form-item\n                        label=\"API Key\"\n                        prop=\"authorization.apiKey\"\n                        v-if=\"\n                            account.type === 'NameCheap' ||\n                            account.type === 'NameSilo' ||\n                            account.type === 'Godaddy' ||\n                            account.type === 'RainYun' ||\n                            account.type === 'Spaceship' ||\n                            account.type === 'Dynu'\n                        \"\n                    >\n                        <el-input v-model.trim=\"account.authorization['apiKey']\"></el-input>\n                    </el-form-item>\n                    <el-form-item label=\"API User\" prop=\"authorization.apiUser\" v-if=\"account.type === 'NameCheap'\">\n                        <el-input v-model.trim=\"account.authorization['apiUser']\"></el-input>\n                    </el-form-item>\n                    <el-form-item\n                        label=\"API Secret\"\n                        prop=\"authorization.apiSecret\"\n                        v-if=\"account.type === 'Godaddy' || account.type === 'Spaceship'\"\n                    >\n                        <el-input v-model.trim=\"account.authorization['apiSecret']\"></el-input>\n                    </el-form-item>\n                    <div v-if=\"account.type === 'NameCom'\">\n                        <el-form-item label=\"Username\" prop=\"authorization.apiUser\">\n                            <el-input v-model.trim=\"account.authorization['apiUser']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Token\" prop=\"authorization.token\">\n                            <el-input v-model.trim=\"account.authorization['token']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'FreeMyIP' || account.type === 'Vercel'\">\n                        <el-form-item label=\"Token\" prop=\"authorization.token\">\n                            <el-input v-model.trim=\"account.authorization['token']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'ClouDNS'\">\n                        <el-form-item label=\"Auth ID\" prop=\"authorization.authID\">\n                            <el-input v-model.trim=\"account.authorization['authID']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Sub Auth ID\" prop=\"authorization.subAuthID\">\n                            <el-input v-model.trim=\"account.authorization['subAuthID']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Auth Password\" prop=\"authorization.authPassword\">\n                            <el-input v-model.trim=\"account.authorization['authPassword']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'WestCN' || account.type === 'RegRu'\">\n                        <el-form-item label=\"Username\" prop=\"authorization.username\">\n                            <el-input v-model.trim=\"account.authorization['username']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Password\" prop=\"authorization.password\">\n                            <el-input v-model.trim=\"account.authorization['password']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'Ovh'\">\n                        <el-form-item label=\"Endpoint\" prop=\"authorization.endpoint\" :rules=\"[Rules.requiredInput]\">\n                            <el-input v-model.trim=\"account.authorization['endpoint']\"></el-input>\n                        </el-form-item>\n                        <el-form-item\n                            label=\"AccessToken\"\n                            prop=\"authorization.accessToken\"\n                            :rules=\"[Rules.requiredInput]\"\n                        >\n                            <el-input v-model.trim=\"account.authorization['accessToken']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'AcmeDNS'\">\n                        <el-form-item label=\"API BASE\" prop=\"authorization.endpoint\" :rules=\"[Rules.requiredInput]\">\n                            <el-input v-model.trim=\"account.authorization['endpoint']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"BASE URL\" prop=\"authorization.baseURL\" :rules=\"[Rules.requiredInput]\">\n                            <el-input v-model.trim=\"account.authorization['baseURL']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'AWSRoute53'\">\n                        <el-form-item label=\"Region\" prop=\"authorization.region\">\n                            <el-input\n                                v-model.trim=\"account.authorization['region']\"\n                                :placeholder=\"'us-east-1'\"\n                            ></el-input>\n                        </el-form-item>\n                        <el-form-item\n                            :label=\"$t('ssl.hostedZoneID')\"\n                            prop=\"authorization.endpoint\"\n                            :rules=\"[Rules.requiredInput]\"\n                        >\n                            <el-input v-model.trim=\"account.authorization['endpoint']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'PorkBun'\">\n                        <el-form-item label=\"API Key\" prop=\"authorization.apiKey\">\n                            <el-input v-model.trim=\"account.authorization['apiKey']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Secret Key\" prop=\"authorization.secretKey\">\n                            <el-input v-model.trim=\"account.authorization['secretKey']\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <div v-if=\"account.type === 'Technitium'\">\n                        <el-form-item label=\"BASE URL\" prop=\"authorization.baseURL\" :rules=\"[Rules.requiredInput]\">\n                            <el-input v-model.trim=\"account.authorization['baseURL']\"></el-input>\n                        </el-form-item>\n                        <el-form-item label=\"Token\" prop=\"authorization.token\">\n                            <el-input v-model.trim=\"account.authorization['token']\"></el-input>\n                        </el-form-item>\n                    </div>\n                </el-form>\n            </el-col>\n        </el-row>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(accountForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { createDnsAccount, updateDnsAccount } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { DNSTypes } from '@/global/mimetype';\n\ninterface AccountProps {\n    mode: string;\n    form: any;\n}\nconst accountData = ref<AccountProps>({\n    mode: 'create',\n    form: {},\n});\n\nconst open = ref();\nconst loading = ref(false);\nconst accountForm = ref<FormInstance>();\nconst rules = ref<any>({\n    name: [Rules.requiredInput, Rules.linuxName],\n    type: [Rules.requiredSelect],\n    authorization: {\n        accessKey: [Rules.requiredInput],\n        secretKey: [Rules.requiredInput],\n        id: [Rules.requiredInput],\n        token: [Rules.requiredInput],\n        apiKey: [Rules.requiredInput],\n        apiUser: [Rules.requiredInput],\n        secretID: [Rules.requiredInput],\n        apiSecret: [Rules.requiredInput],\n        username: [Rules.requiredInput],\n        password: [Rules.requiredInput],\n    },\n});\nconst account = ref({\n    id: 0,\n    name: '',\n    type: 'AliYun',\n    authorization: {},\n});\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    resetForm();\n    open.value = false;\n    em('close', false);\n};\n\nconst resetForm = () => {\n    account.value = {\n        id: 0,\n        name: '',\n        type: 'DnsPod',\n        authorization: {},\n    };\n    accountForm.value?.resetFields();\n};\n\nconst acceptParams = async (props: AccountProps) => {\n    accountData.value.mode = props.mode;\n    if (props.mode === 'edit') {\n        account.value = props.form;\n    }\n    open.value = true;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n\n        if (accountData.value.mode === 'edit') {\n            updateDnsAccount(account.value)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        } else {\n            createDnsAccount(account.value)\n                .then(() => {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                    handleClose();\n                })\n                .finally(() => {\n                    loading.value = false;\n                });\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/dns-account/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('website.dnsAccountManage')\" size=\"large\" @close=\"handleClose\">\n        <template #content>\n            <ComplexTable :data=\"data\" :pagination-config=\"paginationConfig\" @search=\"search()\">\n                <template #toolbar>\n                    <el-button type=\"primary\" @click=\"openCreate\">\n                        {{ $t('commons.button.create') }}\n                    </el-button>\n                </template>\n                <el-table-column\n                    :label=\"$t('commons.table.name')\"\n                    fix\n                    show-overflow-tooltip\n                    prop=\"name\"\n                ></el-table-column>\n                <el-table-column :label=\"$t('commons.table.type')\" prop=\"type\">\n                    <template #default=\"{ row }\">\n                        <span>{{ getDNSName(row.type) }}</span>\n                    </template>\n                </el-table-column>\n                <fu-table-operations\n                    :ellipsis=\"1\"\n                    :buttons=\"buttons\"\n                    :label=\"$t('commons.table.operate')\"\n                    fixed=\"right\"\n                    fix\n                />\n            </ComplexTable>\n            <Create ref=\"createRef\" @close=\"search()\"></Create>\n        </template>\n    </DrawerPro>\n    <OpDialog ref=\"opRef\" @search=\"search\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport Create from './create/index.vue';\nimport { Website } from '@/api/interface/website';\nimport { deleteDnsAccount, searchDnsAccount } from '@/api/modules/website';\nimport { onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { getDNSName } from '@/utils/util';\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'dns-account-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('dns-account-page-size')) || 20,\n    total: 0,\n});\nlet data = ref<Website.DnsAccount[]>();\nlet createRef = ref();\nlet open = ref(false);\nconst opRef = ref();\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Website.DnsAccount) {\n            openEdit(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.DnsAccount) {\n            deleteAccount(row);\n        },\n    },\n];\n\nconst acceptParams = () => {\n    search();\n    open.value = true;\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst search = () => {\n    const req = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n    };\n    searchDnsAccount(req).then((res) => {\n        data.value = res.data.items;\n        paginationConfig.total = res.data.total;\n    });\n};\n\nconst openCreate = () => {\n    createRef.value.acceptParams({ mode: 'create' });\n};\n\nconst openEdit = (form: Website.DnsAccount) => {\n    createRef.value.acceptParams({ mode: 'edit', form: form });\n};\n\nconst deleteAccount = async (row: any) => {\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [row.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.dnsAccountManage'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteDnsAccount,\n        params: { id: row.id },\n    });\n};\n\nonMounted(() => {\n    search();\n});\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton :buttons=\"routerButton\" />\n        <LayoutContent :title=\"$t('website.ssl', 2)\">\n            <template #leftToolBar>\n                <el-button type=\"primary\" @click=\"openSSL()\">\n                    {{ $t('ssl.create') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"openUpload()\">\n                    {{ $t('ssl.upload') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"openCA()\">\n                    {{ $t('ssl.selfSigned') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"openAcmeAccount()\">\n                    {{ $t('website.acmeAccountManage') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"openDnsAccount()\">\n                    {{ $t('website.dnsAccountManage') }}\n                </el-button>\n                <el-button plain @click=\"deletessl(null)\" :disabled=\"selects.length === 0\">\n                    {{ $t('commons.button.delete') }}\n                </el-button>\n            </template>\n            <template #rightToolBar>\n                <TableSearch @search=\"search()\" v-model:searchName=\"req.domain\" />\n                <TableRefresh @search=\"search()\" />\n                <fu-table-column-select\n                    :columns=\"columns\"\n                    trigger=\"hover\"\n                    :title=\"$t('commons.table.selectColumn')\"\n                    popper-class=\"popper-class\"\n                    :only-icon=\"true\"\n                />\n            </template>\n            <template #main>\n                <ComplexTable\n                    :data=\"data\"\n                    :pagination-config=\"paginationConfig\"\n                    @search=\"search()\"\n                    v-model:selects=\"selects\"\n                    v-loading=\"loading\"\n                    :columns=\"columns\"\n                    localKey=\"sslColumn\"\n                    :height-diff=\"260\"\n                    @sort-change=\"changeSort\"\n                >\n                    <el-table-column type=\"selection\" width=\"30\" />\n                    <el-table-column label=\"ID\" prop=\"id\" width=\"50px\" />\n                    <el-table-column\n                        :label=\"$t('website.domain')\"\n                        show-overflow-tooltip\n                        prop=\"primaryDomain\"\n                        min-width=\"150px\"\n                    ></el-table-column>\n                    <el-table-column\n                        :label=\"$t('website.otherDomains')\"\n                        show-overflow-tooltip\n                        prop=\"domains\"\n                        min-width=\"90px\"\n                    ></el-table-column>\n                    <el-table-column :label=\"$t('ssl.applyType')\" show-overflow-tooltip prop=\"provider\" width=\"200px\">\n                        <template #default=\"{ row }\">{{ getProvider(row.provider) }}</template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('ssl.acmeAccount')\"\n                        show-overflow-tooltip\n                        prop=\"acmeAccount.email\"\n                        width=\"150px\"\n                    ></el-table-column>\n                    <el-table-column\n                        :label=\"$t('commons.table.status')\"\n                        show-overflow-tooltip\n                        prop=\"status\"\n                        width=\"110px\"\n                    >\n                        <template #default=\"{ row }\">\n                            <el-popover\n                                v-if=\"\n                                    row.status === 'error' ||\n                                    row.status === 'applyError' ||\n                                    row.status === 'systemRestart'\n                                \"\n                                placement=\"bottom\"\n                                :width=\"400\"\n                                trigger=\"hover\"\n                            >\n                                <template #reference>\n                                    <Status :key=\"row.status\" :status=\"row.status\"></Status>\n                                </template>\n                                <div class=\"max-h-96 overflow-auto\">\n                                    <span>{{ row.message }}</span>\n                                </div>\n                            </el-popover>\n                            <div v-else>\n                                <Status :key=\"row.status\" :status=\"row.status\"></Status>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('commons.button.log')\" width=\"80px\">\n                        <template #default=\"{ row }\">\n                            <el-button\n                                @click=\"openSSLLog(row)\"\n                                link\n                                type=\"primary\"\n                                v-if=\"row.provider != 'manual' && row.provider !== 'fromMaster'\"\n                            >\n                                {{ $t('website.check') }}\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('website.brand')\"\n                        show-overflow-tooltip\n                        prop=\"organization\"\n                        width=\"150px\"\n                    ></el-table-column>\n                    <el-table-column :label=\"$t('website.remark')\" prop=\"description\" width=\"100px\">\n                        <template #default=\"{ row }\">\n                            <fu-read-write-switch>\n                                <template #read>\n                                    <MsgInfo :info=\"row.description\" width=\"200\" />\n                                </template>\n                                <template #default=\"{ read }\">\n                                    <el-input v-model=\"row.description\" @blur=\"updateDesc(row, read)\" />\n                                </template>\n                            </fu-read-write-switch>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('ssl.autoRenew')\" width=\"200px\">\n                        <template #default=\"{ row }\">\n                            <el-switch\n                                :disabled=\"\n                                    row.provider === 'dnsManual' ||\n                                    row.provider === 'manual' ||\n                                    row.provider === 'fromMaster'\n                                \"\n                                v-model=\"row.autoRenew\"\n                                @change=\"updateConfig(row)\"\n                            />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        prop=\"expireDate\"\n                        :label=\"$t('website.expireDate')\"\n                        :formatter=\"dateFormat\"\n                        show-overflow-tooltip\n                        width=\"180px\"\n                        sortable\n                    />\n                    <fu-table-operations\n                        :ellipsis=\"3\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        :fixed=\"mobile ? false : 'right'\"\n                        width=\"320px\"\n                        fix\n                    />\n                </ComplexTable>\n            </template>\n            <DnsAccount ref=\"dnsAccountRef\" />\n            <AcmeAccount ref=\"acmeAccountRef\" />\n            <Create ref=\"sslCreateRef\" @close=\"search()\" @submit=\"openLog\" />\n            <Detail ref=\"detailRef\" />\n            <SSLUpload ref=\"sslUploadRef\" @close=\"search()\" />\n            <Apply ref=\"applyRef\" @search=\"search\" @submit=\"openLog\" />\n            <OpDialog ref=\"opRef\" @search=\"search\" @cancel=\"search\" />\n            <Log ref=\"logRef\" @close=\"search()\" :heightDiff=\"220\" />\n            <CA ref=\"caRef\" @close=\"search()\" />\n            <Obtain ref=\"obtainRef\" @close=\"search()\" @submit=\"openLog\" />\n        </LayoutContent>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { onMounted, reactive, ref, computed } from 'vue';\nimport { deleteSSL, downloadFile, searchSSL, updateSSL } from '@/api/modules/website';\nimport DnsAccount from './dns-account/index.vue';\nimport AcmeAccount from './acme-account/index.vue';\nimport CA from './ca/index.vue';\nimport Create from './create/index.vue';\nimport Detail from './detail/index.vue';\nimport { dateFormat, getProvider } from '@/utils/util';\nimport i18n from '@/lang';\nimport { Website } from '@/api/interface/website';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nimport SSLUpload from './upload/index.vue';\nimport Apply from './apply/index.vue';\nimport Log from '@/components/log/file-drawer/index.vue';\nimport Obtain from './obtain/index.vue';\nimport MsgInfo from '@/components/msg-info/index.vue';\n\nconst globalStore = GlobalStore();\nconst paginationConfig = reactive({\n    cacheSizeKey: 'ssl-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('ssl-page-size')) || 20,\n    total: 0,\n});\nconst acmeAccountRef = ref();\nconst dnsAccountRef = ref();\nconst sslCreateRef = ref();\nconst detailRef = ref();\nconst data = ref();\nconst loading = ref(false);\nconst opRef = ref();\nconst sslUploadRef = ref();\nconst applyRef = ref();\nconst logRef = ref();\nconst caRef = ref();\nconst obtainRef = ref();\nlet selects = ref<any>([]);\nconst columns = ref([]);\nconst req = reactive({\n    domain: '',\n    orderBy: 'expire_date',\n    order: 'ascending',\n});\n\nconst routerButton = [\n    {\n        label: i18n.global.t('website.ssl', 2),\n        path: '/websites/ssl',\n    },\n];\n\nconst buttons = [\n    {\n        label: i18n.global.t('ssl.detail'),\n        disabled: function (row: Website.SSLDTO) {\n            return row.status === 'init' || row.status === 'error';\n        },\n        click: function (row: Website.SSLDTO) {\n            openDetail(row.id);\n        },\n    },\n    {\n        label: i18n.global.t('ssl.apply'),\n        disabled: function (row: Website.SSLDTO) {\n            return row.status === 'applying' || row.provider === 'manual' || row.provider === 'fromMaster';\n        },\n        click: function (row: Website.SSLDTO) {\n            if (row.provider === 'dnsManual') {\n                applyRef.value.acceptParams({ ssl: row });\n            } else {\n                applySSL(row);\n            }\n        },\n        show: function (row: Website.SSLDTO) {\n            return row.provider != 'manual';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.update'),\n        click: function (row: Website.SSLDTO) {\n            sslUploadRef.value.acceptParams(row);\n        },\n        show: function (row: Website.SSLDTO) {\n            return row.provider == 'manual';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        disabled: function (row: Website.SSLDTO) {\n            return row.provider === 'fromMaster';\n        },\n        click: function (row: Website.SSLDTO) {\n            onEdit(row);\n        },\n        show: function (row: Website.SSLDTO) {\n            return row.provider != 'manual';\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.download'),\n        click: function (row: Website.SSLDTO) {\n            onDownload(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.SSLDTO) {\n            deletessl(row);\n        },\n    },\n];\n\nconst onDownload = (ssl: Website.SSLDTO) => {\n    loading.value = true;\n    downloadFile({ id: ssl.id })\n        .then((res) => {\n            const downloadUrl = window.URL.createObjectURL(new Blob([res]));\n            const a = document.createElement('a');\n            a.style.display = 'none';\n            a.href = downloadUrl;\n            a.download = ssl.primaryDomain + '.zip';\n            const event = new MouseEvent('click');\n            a.dispatchEvent(event);\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst changeSort = ({ order }) => {\n    req.orderBy = 'expire_date';\n    req.order = order || 'descending';\n    search();\n};\n\nconst search = () => {\n    const request = {\n        page: paginationConfig.currentPage,\n        pageSize: paginationConfig.pageSize,\n        domain: req.domain,\n        orderBy: req.orderBy,\n        order: req.order || 'descending',\n    };\n    loading.value = true;\n    searchSSL(request)\n        .then((res) => {\n            data.value = res.data.items || [];\n            paginationConfig.total = res.data.total;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst updateDesc = (row: Website.SSLDTO, bulr: Function) => {\n    bulr();\n    if (row.description && row.description.length > 128) {\n        MsgError(i18n.global.t('commons.rule.length128Err'));\n        return;\n    }\n    updateConfig(row);\n};\n\nconst updateConfig = (row: Website.SSLDTO) => {\n    loading.value = true;\n    row.otherDomains = row.domains?.replace(/,/g, '\\n');\n    updateSSL(row)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst openAcmeAccount = () => {\n    acmeAccountRef.value.acceptParams();\n};\nconst openDnsAccount = () => {\n    dnsAccountRef.value.acceptParams();\n};\nconst openSSL = () => {\n    sslCreateRef.value.acceptParams('create');\n};\nconst onEdit = (row: Website.SSL) => {\n    sslCreateRef.value.acceptParams('edit', row);\n};\n\nconst openUpload = () => {\n    sslUploadRef.value.acceptParams();\n};\nconst openDetail = (id: number) => {\n    detailRef.value.acceptParams(id);\n};\nconst openLog = (id: number) => {\n    logRef.value.acceptParams({ id: id, type: 'ssl', tail: true });\n};\nconst openSSLLog = (row: Website.SSL) => {\n    logRef.value.acceptParams({ id: row.id, type: 'ssl', tail: row.status === 'applying' });\n};\n\nconst openCA = () => {\n    caRef.value.acceptParams();\n};\n\nconst applySSL = (row: Website.SSLDTO) => {\n    obtainRef.value.acceptParams({ ssl: row });\n};\n\nconst deletessl = async (row: any) => {\n    let names = [];\n    let params = {};\n    if (row == null) {\n        names = selects.value.map((item: Website.SSLDTO) => item.primaryDomain);\n        params = { ids: selects.value.map((item: Website.SSLDTO) => item.id) };\n    } else {\n        names = [row.primaryDomain];\n        params = { ids: [row.id] };\n    }\n\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: names,\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.ssl'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteSSL,\n        params: params,\n    });\n    search();\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/obtain/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('ssl.apply')\" size=\"small\" @close=\"handleClose\">\n        <div class=\"text-center\" v-loading=\"loading\">\n            <div v-if=\"ssl.websites && ssl.websites.length > 0\">\n                <span>{{ $t('ssl.renewWebsite') }}</span>\n                <div>\n                    <br />\n                    <span>\n                        <span v-for=\"(website, index) in ssl.websites\" :key=\"index\">\n                            <el-tag type=\"info\">{{ website.primaryDomain }}</el-tag>\n                        </span>\n                    </span>\n                </div>\n                <br />\n            </div>\n            <span>{{ $t('ssl.renewConfirm', [ssl.primaryDomain]) }}</span>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { obtainSSL, renewSSLByCA } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ref } from 'vue';\n\ninterface RenewProps {\n    ssl: Website.SSL;\n}\n\nconst open = ref(false);\nconst loading = ref(false);\nconst em = defineEmits(['close', 'submit']);\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\nconst ssl = ref();\n\nconst acceptParams = async (props: RenewProps) => {\n    ssl.value = props.ssl;\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    try {\n        if (ssl.value.provider == 'selfSigned') {\n            await renewSSLByCA({ SSLID: ssl.value.id });\n        } else {\n            await obtainSSL({ ID: ssl.value.id });\n        }\n        handleClose();\n        MsgSuccess(i18n.global.t('ssl.applyStart'));\n        loading.value = false;\n        em('submit', ssl.value.id);\n    } catch (error) {}\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/ssl/upload/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('ssl.upload')\" size=\"large\" @close=\"handleClose\">\n        <el-form ref=\"sslForm\" label-position=\"top\" :model=\"ssl\" label-width=\"100px\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('website.importType')\" prop=\"type\">\n                <el-select v-model=\"ssl.type\">\n                    <el-option :label=\"$t('website.pasteSSL')\" :value=\"'paste'\"></el-option>\n                    <el-option :label=\"$t('website.localSSL')\" :value=\"'local'\"></el-option>\n                    <el-option :label=\"$t('commons.button.upload') + $t('menu.files')\" :value=\"'upload'\"></el-option>\n                </el-select>\n            </el-form-item>\n            <div v-if=\"ssl.type === 'paste'\">\n                <el-form-item :label=\"$t('website.privateKey')\" prop=\"privateKey\">\n                    <el-input v-model=\"ssl.privateKey\" :rows=\"6\" type=\"textarea\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('website.certificate')\" prop=\"certificate\">\n                    <el-input v-model=\"ssl.certificate\" :rows=\"6\" type=\"textarea\" />\n                </el-form-item>\n            </div>\n            <div v-if=\"ssl.type === 'local'\">\n                <el-form-item :label=\"$t('website.privateKeyPath')\" prop=\"privateKeyPath\">\n                    <el-input v-model=\"ssl.privateKeyPath\">\n                        <template #prepend>\n                            <el-button icon=\"Folder\" @click=\"keyFileRef.acceptParams({ dir: false })\" />\n                        </template>\n                    </el-input>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.certificatePath')\" prop=\"certificatePath\">\n                    <el-input v-model=\"ssl.certificatePath\">\n                        <template #prepend>\n                            <el-button icon=\"Folder\" @click=\"certFileRef.acceptParams({ dir: false })\" />\n                        </template>\n                    </el-input>\n                </el-form-item>\n            </div>\n            <div v-if=\"ssl.type === 'upload'\">\n                <el-form-item :label=\"$t('website.privateKey')\" prop=\"privateKeyFile\">\n                    <el-upload\n                        ref=\"privateKeyUpload\"\n                        :auto-upload=\"false\"\n                        :limit=\"1\"\n                        :on-change=\"handlePrivateKeyChange\"\n                        :file-list=\"privateKeyFileList\"\n                        class=\"p-w-200\"\n                    >\n                        <template #trigger>\n                            <el-button type=\"primary\" icon=\"Upload\">\n                                {{ $t('file.selectFile') }}\n                            </el-button>\n                        </template>\n                    </el-upload>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.certificate')\" prop=\"certificateFile\">\n                    <el-upload\n                        ref=\"certificateUpload\"\n                        :auto-upload=\"false\"\n                        :limit=\"1\"\n                        :on-change=\"handleCertificateChange\"\n                        :file-list=\"certificateFileList\"\n                        class=\"p-w-200\"\n                    >\n                        <template #trigger>\n                            <el-button type=\"primary\" icon=\"Upload\">\n                                {{ $t('file.selectFile') }}\n                            </el-button>\n                        </template>\n                    </el-upload>\n                </el-form-item>\n            </div>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"description\">\n                <el-input v-model=\"ssl.description\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n    <FileList ref=\"keyFileRef\" @choose=\"getPrivateKeyPath\" />\n    <FileList ref=\"certFileRef\" @choose=\"getCertificatePath\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport { uploadSSL, uploadSSLFile } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport FileList from '@/components/file-list/index.vue';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { Website } from '@/api/interface/website';\n\nconst open = ref(false);\nconst keyFileRef = ref();\nconst certFileRef = ref();\nconst loading = ref(false);\nconst sslForm = ref<FormInstance>();\nconst privateKeyFileList = ref([]);\nconst certificateFileList = ref([]);\nconst privateKeyUpload = ref();\nconst certificateUpload = ref();\n\nconst rules = ref({\n    privateKey: [Rules.requiredInput],\n    certificate: [Rules.requiredInput],\n    privateKeyPath: [Rules.requiredInput],\n    certificatePath: [Rules.requiredInput],\n    type: [Rules.requiredSelect],\n    certificateFile: [Rules.requiredInput],\n    privateKeyFile: [Rules.requiredInput],\n});\nconst initData = () => ({\n    privateKey: '',\n    certificate: '',\n    privateKeyPath: '',\n    certificatePath: '',\n    type: 'paste',\n    sslID: 0,\n    description: '',\n    privateKeyFile: null as File | null,\n    certificateFile: null as File | null,\n});\nconst ssl = ref(initData());\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    resetForm();\n    open.value = false;\n    em('close', false);\n};\nconst resetForm = () => {\n    sslForm.value?.resetFields();\n    ssl.value = initData();\n    privateKeyFileList.value = [];\n    certificateFileList.value = [];\n};\n\nconst handlePrivateKeyChange = (file: any) => {\n    ssl.value.privateKeyFile = file.raw;\n    privateKeyFileList.value = [file];\n};\n\nconst handleCertificateChange = (file: any) => {\n    ssl.value.certificateFile = file.raw;\n    certificateFileList.value = [file];\n};\n\nconst acceptParams = (websiteSSL: Website.SSLDTO) => {\n    resetForm();\n    if (websiteSSL && websiteSSL.id > 0) {\n        ssl.value.sslID = websiteSSL.id;\n        ssl.value.description = websiteSSL.description;\n        ssl.value.privateKeyPath = websiteSSL.privateKeyPath;\n        ssl.value.certificatePath = websiteSSL.certPath;\n        if (ssl.value.certificatePath != '' && ssl.value.privateKeyPath != '') {\n            ssl.value.type = 'local';\n        }\n    }\n    open.value = true;\n};\n\nconst getPrivateKeyPath = (path: string) => {\n    ssl.value.privateKeyPath = path;\n};\n\nconst getCertificatePath = (path: string) => {\n    ssl.value.certificatePath = path;\n};\n\nconst submit = async () => {\n    try {\n        await sslForm.value?.validate();\n        loading.value = true;\n        if (ssl.value.type === 'upload') {\n            const formData = new FormData();\n            formData.append('type', ssl.value.type);\n            formData.append('description', ssl.value.description);\n            formData.append('sslID', ssl.value.sslID.toString());\n\n            if (ssl.value.privateKeyFile) {\n                formData.append('privateKeyFile', ssl.value.privateKeyFile);\n            }\n            if (ssl.value.certificateFile) {\n                formData.append('certificateFile', ssl.value.certificateFile);\n            }\n            await uploadSSLFile(formData);\n        } else {\n            await uploadSSL(ssl.value);\n        }\n        handleClose();\n        MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n    } catch (err) {\n    } finally {\n        loading.value = false;\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/batch-op/group.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.table.group')\" size=\"30%\" @close=\"handleClose\">\n        <el-form ref=\"websiteForm\" label-position=\"top\" :model=\"form\" :rules=\"rules\" v-loading=\"loading\">\n            <GroupSelect v-model=\"form.groupID\" :prop=\"'groupID'\" :groupType=\"'website'\"></GroupSelect>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport GroupSelect from '@/views/website/website/components/group/index.vue';\n\nimport { batchSetGroup } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst websiteForm = ref<FormInstance>();\nconst form = reactive({\n    ids: [] as number[],\n    groupID: 0,\n});\nconst rules = ref({\n    groupID: [Rules.requiredSelect],\n});\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = async (ids: []) => {\n    form.ids = ids;\n    form.groupID = 0;\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    const valid = await websiteForm.value.validate();\n    if (!valid) {\n        loading.value = false;\n        return;\n    }\n    batchSetGroup(form)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n            open.value = false;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/batch-op/https.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.set') + $t('website.ssl')\" size=\"50%\" @close=\"handleClose\">\n        <el-form ref=\"websiteForm\" label-position=\"top\" :model=\"form\" :rules=\"rules\" v-loading=\"loading\">\n            <HttpsConfig v-model=\"form\" :website-ssl=\"websiteSSL\" @ssl-change=\"handleSSLChange\" />\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport HttpsConfig from '@/views/website/website/components/https/index.vue';\n\nimport { Website } from '@/api/interface/website';\nimport { batchSetHttps } from '@/api/modules/website';\nimport { FormInstance } from 'element-plus';\nimport { Rules } from '@/global/form-rules';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst websiteForm = ref<FormInstance>();\nconst websiteSSL = ref();\nconst form = ref({\n    ids: [] as number[],\n    acmeAccountID: 0,\n    enable: false,\n    websiteSSLId: undefined,\n    type: 'existed',\n    importType: 'paste',\n    privateKey: '',\n    certificate: '',\n    privateKeyPath: '',\n    certificatePath: '',\n    httpConfig: 'HTTPToHTTPS',\n    hsts: true,\n    hstsIncludeSubDomains: false,\n    algorithm:\n        'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED',\n    SSLProtocol: ['TLSv1.3', 'TLSv1.2'],\n    httpsPort: '443',\n    http3: false,\n    taskID: '',\n});\nconst rules = ref({\n    type: [Rules.requiredSelect],\n    privateKey: [Rules.requiredInput],\n    certificate: [Rules.requiredInput],\n    privateKeyPath: [Rules.requiredInput],\n    certificatePath: [Rules.requiredInput],\n    websiteSSLId: [Rules.requiredSelect],\n    httpConfig: [Rules.requiredSelect],\n    SSLProtocol: [Rules.requiredSelect],\n    algorithm: [Rules.requiredInput],\n    acmeAccountID: [Rules.requiredInput],\n});\nconst emit = defineEmits(['openTask']);\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = async (ids: [], taskID: string) => {\n    form.value.ids = ids;\n    form.value.taskID = taskID;\n    open.value = true;\n};\n\nconst handleSSLChange = (ssl: Website.SSL) => {\n    websiteSSL.value = ssl;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    const valid = await websiteForm.value.validate();\n    if (!valid) {\n        loading.value = false;\n        return;\n    }\n    batchSetHttps(form.value)\n        .then(() => {\n            open.value = false;\n            emit('openTask', form.value.taskID);\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/check/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('app.checkTitle')\" size=\"large\" @close=\"handleClose\">\n        <el-row>\n            <el-alert type=\"warning\" :description=\"$t('website.deleteHelper')\" center show-icon :closable=\"false\" />\n            <el-col :span=\"24\">\n                <br />\n                <el-table :data=\"items\" style=\"width: 100%\">\n                    <el-table-column prop=\"name\" :label=\"$t('app.installName')\" />\n                    <el-table-column prop=\"appName\" :label=\"$t('app.appName')\" />\n                    <el-table-column prop=\"version\" :label=\"$t('app.version')\" />\n                    <el-table-column prop=\"status\" :label=\"$t('commons.table.status')\" />\n                </el-table>\n            </el-col>\n        </el-row>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"toPage\">\n                    {{ $t('website.toApp') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { routerToName } from '@/utils/router';\nimport { ref } from 'vue';\n\ninterface InstallRrops {\n    items: Website.CheckRes[];\n}\n\nlet open = ref(false);\nlet items = ref([]);\n\nconst acceptParams = async (props: InstallRrops) => {\n    items.value = props.items;\n    open.value = true;\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst toPage = () => {\n    routerToName('AppInstalled');\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/components/group/index.vue",
    "content": "<template>\n    <el-form-item :label=\"$t('commons.table.group')\" :prop=\"prop\">\n        <el-select v-model=\"selectedValue\">\n            <el-option\n                v-for=\"(group, index) in groups\"\n                :key=\"index\"\n                :label=\"group.name === 'Default' ? $t('commons.table.default') : group.name\"\n                :value=\"group.id\"\n            />\n        </el-select>\n    </el-form-item>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, computed, onMounted } from 'vue';\nimport { Group } from '@/api/interface/group';\nimport { getAgentGroupList } from '@/api/modules/group';\n\nconst props = defineProps({\n    modelValue: {\n        type: Number,\n        required: true,\n    },\n    groupType: {\n        type: String,\n        default: 'website',\n    },\n    prop: {\n        type: String,\n        default: '',\n    },\n});\n\nconst emit = defineEmits(['update:modelValue']);\n\nconst groups = ref<Group.GroupInfo[]>([]);\n\nconst selectedValue = computed({\n    get: () => props.modelValue,\n    set: (value) => emit('update:modelValue', value),\n});\n\nonMounted(async () => {\n    const res = await getAgentGroupList(props.groupType);\n    groups.value = res.data;\n    if (groups.value.length > 0 && !props.modelValue && props.modelValue == 0) {\n        selectedValue.value = groups.value[0].id;\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/components/https/index.vue",
    "content": "<template>\n    <div>\n        <el-form-item :label=\"$t('website.HTTPConfig')\" prop=\"httpConfig\">\n            <el-select v-model=\"formData.httpConfig\" class=\"p-w-400\">\n                <el-option :label=\"$t('website.HTTPToHTTPS')\" value=\"HTTPToHTTPS\"></el-option>\n                <el-option :label=\"$t('website.HTTPAlso')\" value=\"HTTPAlso\"></el-option>\n                <el-option :label=\"$t('website.HTTPSOnly')\" value=\"HTTPSOnly\"></el-option>\n            </el-select>\n        </el-form-item>\n\n        <el-form-item label=\"HSTS\" prop=\"hsts\">\n            <el-checkbox v-model=\"formData.hsts\">{{ $t('commons.button.enable') }}</el-checkbox>\n            <span class=\"input-help\">{{ $t('website.hstsHelper') }}</span>\n        </el-form-item>\n\n        <el-form-item\n            :label=\"'HSTS ' + $t('website.includeSubDomains')\"\n            prop=\"hstsIncludeSubDomains\"\n            v-if=\"formData.hsts\"\n        >\n            <el-checkbox v-model=\"formData.hstsIncludeSubDomains\">\n                {{ $t('commons.button.enable') }}\n            </el-checkbox>\n            <span class=\"input-help\">{{ $t('website.hstsIncludeSubDomainsHelper') }}</span>\n        </el-form-item>\n\n        <el-form-item label=\"HTTP3\" prop=\"http3\">\n            <el-checkbox v-model=\"formData.http3\">{{ $t('commons.button.enable') }}</el-checkbox>\n            <span class=\"input-help\">{{ $t('website.http3Helper') }}</span>\n        </el-form-item>\n\n        <el-form-item :label=\"$t('website.sslConfig')\" prop=\"type\">\n            <el-select v-model=\"formData.type\" @change=\"handleTypeChange\" class=\"p-w-400\">\n                <el-option :label=\"$t('website.oldSSL')\" value=\"existed\"></el-option>\n                <el-option :label=\"$t('website.manualSSL')\" value=\"manual\"></el-option>\n            </el-select>\n        </el-form-item>\n\n        <div v-if=\"formData.type === 'existed'\">\n            <el-form-item :label=\"$t('website.acmeAccountManage')\" prop=\"acmeAccountID\">\n                <el-select\n                    v-model=\"formData.acmeAccountID\"\n                    :placeholder=\"$t('website.selectAcme')\"\n                    @change=\"handleAcmeAccountChange\"\n                    class=\"p-w-400\"\n                >\n                    <el-option :key=\"0\" :label=\"$t('website.imported')\" :value=\"0\"></el-option>\n                    <el-option v-for=\"(acme, index) in acmeAccounts\" :key=\"index\" :label=\"acme.email\" :value=\"acme.id\">\n                        <span>\n                            {{ acme.email }}\n                            <el-tag class=\"ml-5\">{{ getAccountName(acme.type) }}</el-tag>\n                        </span>\n                    </el-option>\n                </el-select>\n            </el-form-item>\n\n            <el-form-item :label=\"$t('website.ssl')\" prop=\"websiteSSLId\" :hide-required-asterisk=\"true\">\n                <el-select\n                    v-model=\"formData.websiteSSLId\"\n                    :placeholder=\"$t('website.selectSSL')\"\n                    @change=\"handleSSLChange\"\n                    class=\"p-w-400\"\n                >\n                    <el-option\n                        v-for=\"(ssl, index) in ssls\"\n                        :key=\"index\"\n                        :label=\"ssl.primaryDomain\"\n                        :value=\"ssl.id\"\n                        :disabled=\"ssl.pem === ''\"\n                    >\n                        <span>{{ ssl.primaryDomain }}</span>\n                        <el-tag class=\"tagClass\" v-if=\"ssl.expireDate\">\n                            {{ dateFormatSimple(ssl.expireDate) }}\n                        </el-tag>\n                        <el-tag class=\"tagClass\" v-if=\"ssl.organization\">\n                            {{ ssl.organization }}\n                        </el-tag>\n                    </el-option>\n                </el-select>\n            </el-form-item>\n        </div>\n\n        <div v-if=\"formData.type === 'manual'\">\n            <el-form-item :label=\"$t('website.importType')\" prop=\"importType\">\n                <el-select v-model=\"formData.importType\">\n                    <el-option :label=\"$t('website.pasteSSL')\" value=\"paste\"></el-option>\n                    <el-option :label=\"$t('website.localSSL')\" value=\"local\"></el-option>\n                </el-select>\n            </el-form-item>\n\n            <div v-if=\"formData.importType === 'paste'\">\n                <el-form-item :label=\"$t('website.privateKey')\" prop=\"privateKey\">\n                    <el-input v-model=\"formData.privateKey\" :rows=\"6\" type=\"textarea\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('website.certificate')\" prop=\"certificate\">\n                    <el-input v-model=\"formData.certificate\" :rows=\"6\" type=\"textarea\" />\n                </el-form-item>\n            </div>\n\n            <div v-if=\"formData.importType === 'local'\">\n                <el-form-item :label=\"$t('website.privateKeyPath')\" prop=\"privateKeyPath\">\n                    <el-input v-model=\"formData.privateKeyPath\">\n                        <template #prepend>\n                            <el-button icon=\"Folder\" @click=\"keyFileRef.acceptParams({ dir: false })\" />\n                        </template>\n                    </el-input>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.certificatePath')\" prop=\"certificatePath\">\n                    <el-input v-model=\"formData.certificatePath\">\n                        <template #prepend>\n                            <el-button icon=\"Folder\" @click=\"certFileRef.acceptParams({ dir: false })\" />\n                        </template>\n                    </el-input>\n                </el-form-item>\n            </div>\n        </div>\n\n        <el-form-item :label=\"' '\" v-if=\"websiteSSL && websiteSSL.id > 0\">\n            <slot name=\"ssl-info\" :websiteSSL=\"websiteSSL\">\n                <WebsiteSSL :websiteSSL=\"websiteSSL\" />\n            </slot>\n        </el-form-item>\n\n        <el-divider content-position=\"left\">{{ $t('website.SSLProConfig') }}</el-divider>\n\n        <el-form-item :label=\"$t('website.supportProtocol')\" prop=\"SSLProtocol\">\n            <el-checkbox-group v-model=\"formData.SSLProtocol\">\n                <el-checkbox value=\"TLSv1.3\">TLS 1.3</el-checkbox>\n                <el-checkbox value=\"TLSv1.2\">TLS 1.2</el-checkbox>\n                <el-checkbox value=\"TLSv1.1\">\n                    {{ 'TLS 1.1' + $t('website.notSecurity') }}\n                </el-checkbox>\n                <el-checkbox value=\"TLSv1\">\n                    {{ 'TLS 1.0' + $t('website.notSecurity') }}\n                </el-checkbox>\n            </el-checkbox-group>\n        </el-form-item>\n\n        <el-form-item prop=\"algorithm\" :label=\"$t('website.encryptionAlgorithm')\">\n            <el-input type=\"textarea\" :rows=\"3\" v-model.trim=\"formData.algorithm\"></el-input>\n        </el-form-item>\n\n        <FileList ref=\"keyFileRef\" @choose=\"getPrivateKeyPath\" />\n        <FileList ref=\"certFileRef\" @choose=\"getCertificatePath\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, watch, onMounted, ref } from 'vue';\nimport { getAccountName, dateFormatSimple } from '@/utils/util';\nimport WebsiteSSL from '@/views/website/website/components/website-ssl/index.vue';\nimport { Website } from '@/api/interface/website';\nimport { listSSL, searchAcmeAccount } from '@/api/modules/website';\n\ntype SSLItem = Website.SSL & { organization?: string };\n\ninterface AcmeAccount {\n    id: number;\n    email: string;\n    type: string;\n}\n\ninterface Props {\n    modelValue: {\n        httpsPort: string;\n        httpConfig: string;\n        hsts: boolean;\n        hstsIncludeSubDomains: boolean;\n        http3: boolean;\n        type: string;\n        acmeAccountID: number;\n        websiteSSLId?: number;\n        importType: string;\n        privateKey: string;\n        certificate: string;\n        privateKeyPath: string;\n        certificatePath: string;\n        SSLProtocol: string[];\n        algorithm: string;\n    };\n    websiteSSL?: Website.SSL;\n    autoLoad?: boolean;\n}\n\ninterface Emits {\n    (e: 'update:modelValue', value: Props['modelValue']): void;\n    (e: 'type-change', type: string): void;\n    (e: 'ssl-change', ssl: Website.SSL): void;\n    (e: 'select-key-file'): void;\n    (e: 'select-cert-file'): void;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n    autoLoad: true,\n});\n\nconst emit = defineEmits<Emits>();\n\nconst acmeAccounts = ref<AcmeAccount[]>([]);\nconst ssls = ref<SSLItem[]>([]);\nconst keyFileRef = ref();\nconst certFileRef = ref();\n\nconst formData = computed({\n    get: () => props.modelValue,\n    set: (value) => emit('update:modelValue', value),\n});\n\nconst listAcmeAccount = async () => {\n    try {\n        const res = await searchAcmeAccount({ page: 1, pageSize: 100 });\n        acmeAccounts.value = res.data.items || [];\n    } catch (error) {\n        console.error('Failed to load ACME accounts:', error);\n    }\n};\n\nconst listSSLs = async () => {\n    try {\n        const res = await listSSL({\n            acmeAccountID: String(formData.value.acmeAccountID),\n        });\n        ssls.value = res.data || [];\n\n        if (ssls.value.length > 0) {\n            let exist = false;\n            for (const ssl of ssls.value) {\n                if (ssl.id === formData.value.websiteSSLId) {\n                    exist = true;\n                    break;\n                }\n            }\n\n            if (!exist) {\n                for (const ssl of ssls.value) {\n                    if (ssl.pem !== '') {\n                        formData.value.websiteSSLId = ssl.id;\n                        handleSSLChange(ssl.id);\n                        break;\n                    }\n                }\n            } else {\n                handleSSLChange(formData.value.websiteSSLId);\n            }\n        } else {\n            formData.value.websiteSSLId = undefined;\n        }\n    } catch (error) {\n        console.error('Failed to load SSL certificates:', error);\n    }\n};\n\nconst handleTypeChange = (type: string) => {\n    if (type !== 'existed') {\n        formData.value.websiteSSLId = undefined;\n    }\n    emit('type-change', type);\n};\n\nconst handleAcmeAccountChange = () => {\n    listSSLs();\n};\n\nconst handleSSLChange = (sslId: number) => {\n    const selectedSSL = ssls.value.find((ssl: Website.SSL) => ssl.id === sslId);\n    if (selectedSSL && selectedSSL.pem !== '') {\n        emit('ssl-change', selectedSSL);\n    }\n};\n\nwatch(\n    () => formData.value.acmeAccountID,\n    () => {\n        listSSLs();\n    },\n);\n\nconst refresh = () => {\n    listAcmeAccount();\n    listSSLs();\n};\n\nconst getPrivateKeyPath = (path: string) => {\n    formData.value.privateKeyPath = path;\n};\n\nconst getCertificatePath = (path: string) => {\n    formData.value.certificatePath = path;\n};\n\ndefineExpose({\n    refresh,\n    listSSLs,\n    listAcmeAccount,\n});\n\nonMounted(() => {\n    if (props.autoLoad) {\n        listAcmeAccount();\n        listSSLs();\n    }\n});\n</script>\n\n<style scoped>\n.input-help {\n    margin-left: 8px;\n    color: var(--el-text-color-secondary);\n    font-size: 12px;\n}\n.tagClass {\n    float: right;\n    margin-right: 10px;\n    font-size: 12px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/website/components/website-ssl/index.vue",
    "content": "<template>\n    <el-descriptions :column=\"7\" border direction=\"vertical\">\n        <el-descriptions-item :label=\"$t('website.primaryDomain')\">\n            {{ websiteSSL.primaryDomain }}\n        </el-descriptions-item>\n        <el-descriptions-item :label=\"$t('website.otherDomains')\">\n            {{ websiteSSL.domains }}\n        </el-descriptions-item>\n        <el-descriptions-item :label=\"$t('website.brand')\">\n            {{ websiteSSL.organization }}\n        </el-descriptions-item>\n        <el-descriptions-item :label=\"$t('ssl.provider')\">\n            {{ getProvider(websiteSSL.provider) }}\n        </el-descriptions-item>\n        <el-descriptions-item\n            :label=\"$t('ssl.acmeAccount')\"\n            v-if=\"websiteSSL.acmeAccount && websiteSSL.provider !== 'manual'\"\n        >\n            {{ websiteSSL.acmeAccount.email }}\n        </el-descriptions-item>\n        <el-descriptions-item :label=\"$t('website.expireDate')\">\n            {{ dateFormatSimple(websiteSSL.expireDate) }}\n        </el-descriptions-item>\n        <el-descriptions-item :label=\"$t('website.remark')\">\n            {{ websiteSSL.description }}\n        </el-descriptions-item>\n    </el-descriptions>\n</template>\n\n<script lang=\"ts\" setup>\nimport { dateFormatSimple, getProvider } from '@/utils/util';\ndefineProps({\n    websiteSSL: {\n        type: Object,\n        required: true,\n    },\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/anti-Leech/index.vue",
    "content": "<template>\n    <el-row :gutter=\"20\" v-loading=\"loading\">\n        <el-col :xs=\"24\" :sm=\"18\" :md=\"18\" :lg=\"14\" :xl=\"14\">\n            <el-form\n                :model=\"form\"\n                :rules=\"rules\"\n                ref=\"leechRef\"\n                label-position=\"right\"\n                label-width=\"150px\"\n                class=\"moblie-form\"\n            >\n                <el-form-item :label=\"$t('website.extends')\" prop=\"extends\" class=\"mt-2\">\n                    <el-input v-model=\"form.extends\" class=\"p-w-600\"></el-input>\n                </el-form-item>\n                <el-divider content-position=\"left\">{{ $t('website.antiLeech') }}</el-divider>\n                <el-form-item :label=\"$t('website.enableOrNot')\" prop=\"enable\">\n                    <el-switch v-model=\"form.enable\" @change=\"changeEnable\"></el-switch>\n                </el-form-item>\n                <template v-if=\"form.enable\">\n                    <el-form-item :label=\"$t('website.accessDomain')\" prop=\"domains\">\n                        <div class=\"domain-list-container\">\n                            <div v-for=\"(_, index) in domainList\" :key=\"index\" class=\"flex items-center mb-2\">\n                                <el-input\n                                    v-model=\"domainList[index]\"\n                                    @input=\"updateDomainsString\"\n                                    class=\"flex-1 mr-2\"\n                                ></el-input>\n                                <el-button\n                                    type=\"danger\"\n                                    size=\"small\"\n                                    :icon=\"Delete\"\n                                    @click=\"removeDomain(index)\"\n                                    v-if=\"domainList.length > 1\"\n                                ></el-button>\n                            </div>\n                            <el-button type=\"primary\" size=\"small\" :icon=\"Plus\" @click=\"addDomain\" plain>\n                                {{ $t('commons.button.add') }}\n                            </el-button>\n                        </div>\n                    </el-form-item>\n                    <el-row :gutter=\"15\">\n                        <el-col>\n                            <el-form-item :label=\"$t('website.noneRef')\" prop=\"noneRef\">\n                                <el-switch v-model=\"form.noneRef\" />\n                            </el-form-item>\n                        </el-col>\n\n                        <el-col>\n                            <el-form-item :label=\"$t('website.blockedRef')\" prop=\"blocked\">\n                                <el-switch v-model=\"form.blocked\" />\n                                <span class=\"input-help\">\n                                    {{ $t('website.leechSpecialValidHelper') }}\n                                </span>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                    <el-form-item :label=\"$t('website.leechReturn')\" prop=\"return\">\n                        <el-select v-model=\"form.return\" class=\"p-w-200\">\n                            <el-option\n                                v-for=\"option in returnOptions\"\n                                :key=\"option.value\"\n                                :label=\"option.label\"\n                                :value=\"option.value\"\n                            ></el-option>\n                        </el-select>\n                        <span class=\"input-help\">\n                            {{ $t('website.leechInvalidReturnHelper') }}\n                        </span>\n                    </el-form-item>\n                </template>\n                <el-divider content-position=\"left\">{{ $t('website.leechcacheControl') }}</el-divider>\n                <el-form-item :label=\"$t('website.browserCache')\" prop=\"cache\">\n                    <el-switch v-model=\"form.cache\" />\n                </el-form-item>\n                <el-form-item :label=\"$t('website.cacheTime')\" prop=\"cacheTime\" v-if=\"form.cache\">\n                    <el-input v-model.number=\"form.cacheTime\" maxlength=\"15\" class=\"p-w-300\">\n                        <template #append>\n                            <el-select v-model=\"form.cacheUint\" class=\"w-s-button p-w-100\">\n                                <el-option\n                                    v-for=\"(unit, index) in Units\"\n                                    :key=\"index\"\n                                    :label=\"unit.label\"\n                                    :value=\"unit.value\"\n                                ></el-option>\n                            </el-select>\n                        </template>\n                    </el-input>\n                    <span class=\"input-help\">{{ $t('website.browserCacheTimeHelper') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.logEnableControl')\" prop=\"logEnable\" v-if=\"form.cache || form.enable\">\n                    <el-switch v-model=\"form.logEnable\" />\n                    <span class=\"input-help\">{{ $t('website.leechlogControlHelper') }}</span>\n                </el-form-item>\n                <div class=\"flex items-center gap-4 mt-2\">\n                    <el-button type=\"primary\" @click=\"submit(leechRef, form.enable)\" :disabled=\"loading\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </div>\n            </el-form>\n        </el-col>\n    </el-row>\n</template>\n\n<script setup lang=\"ts\">\nimport { getAntiLeech, listDomains, updateAntiLeech } from '@/api/modules/website';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { FormInstance } from 'element-plus';\nimport { computed, onMounted, reactive } from 'vue';\nimport { ref } from 'vue';\nimport { Units } from '@/global/mimetype';\nimport { MsgSuccess, MsgError } from '@/utils/message';\nimport i18n from '@/lang';\nimport { Plus, Delete } from '@element-plus/icons-vue';\n\nconst loading = ref(false);\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst id = computed(() => {\n    return props.id;\n});\nconst leechRef = ref<FormInstance>();\nconst resData = ref({\n    enable: false,\n});\nconst returnOptions = [\n    { label: '400 Bad Request', value: '400' },\n    { label: '403 Forbidden', value: '403' },\n    { label: '404 Not Found', value: '404' },\n];\n\nconst domainList = ref(['']);\n\nconst form = reactive({\n    enable: false,\n    cache: false,\n    cacheTime: 30,\n    cacheUint: 'd',\n    extends: 'js,css,png,jpg,jpeg,gif,webp,webm,avif,ico,bmp,swf,eot,svg,ttf,woff,woff2',\n    return: '404',\n    domains: '',\n    noneRef: true,\n    logEnable: false,\n    blocked: false,\n    serverNames: [],\n    websiteID: 0,\n});\n\nconst rules = ref({\n    extends: [Rules.requiredInput, Rules.leechExts],\n    cacheTime: [Rules.requiredInput, checkNumberRange(1, 65535)],\n    return: [Rules.requiredInput],\n    domains: [Rules.requiredInput],\n});\n\nconst addDomain = () => {\n    domainList.value.push('');\n};\n\nconst removeDomain = (index: number) => {\n    domainList.value.splice(index, 1);\n    updateDomainsString();\n};\n\nconst updateDomainsString = () => {\n    form.domains = domainList.value.filter((domain) => domain.trim() !== '').join('\\n');\n};\n\nconst initDomainList = (domainsStr: string) => {\n    if (domainsStr) {\n        domainList.value = domainsStr.split('\\n').filter((domain) => domain.trim() !== '');\n        domainList.value = Array.from(new Set(domainList.value));\n        if (domainList.value.length === 0) {\n            domainList.value = [''];\n        }\n    } else {\n        domainList.value = [''];\n    }\n};\n\nconst changeEnable = (enable: boolean) => {\n    if (enable) {\n        listDomains(id.value)\n            .then((res) => {\n                const domains = res.data || [];\n                let serverNameStr = '';\n                for (const param of domains) {\n                    serverNameStr = serverNameStr + param.domain + '\\n';\n                }\n                form.domains = serverNameStr;\n                initDomainList(serverNameStr);\n            })\n            .finally(() => {});\n    }\n};\n\nconst search = async () => {\n    loading.value = true;\n    const res = await getAntiLeech({ websiteID: id.value });\n    loading.value = false;\n    if (!res.data.enable && !res.data.cache) {\n        return;\n    }\n    resData.value = res.data;\n    form.blocked = res.data.blocked;\n    form.cache = res.data.cache;\n    form.enable = res.data.enable;\n    if (res.data.cache) {\n        form.cacheTime = res.data.cacheTime;\n        form.cacheUint = res.data.cacheUint;\n    }\n    form.extends = res.data.extends;\n    form.return = res.data.return;\n    form.logEnable = res.data.logEnable;\n    form.noneRef = res.data.noneRef;\n\n    const serverNames = res.data.serverNames;\n    let serverNameStr = '';\n    for (const param of serverNames) {\n        serverNameStr = serverNameStr + param + '\\n';\n    }\n    form.domains = serverNameStr;\n    initDomainList(serverNameStr);\n};\n\nconst submit = async (formEl: FormInstance | undefined, enable: boolean) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        update(enable);\n    });\n};\n\nconst update = async (enable: boolean) => {\n    if (enable) {\n        updateDomainsString();\n        form.serverNames = form.domains.split('\\n');\n        if (!checkReturn()) {\n            return;\n        }\n    }\n    form.enable = enable;\n    loading.value = true;\n    form.websiteID = id.value;\n    await updateAntiLeech(form)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst checkReturn = (): boolean => {\n    let returns = form.return.split(' ');\n    if (returns[0]) {\n        if (isHttpStatusCode(returns[0])) {\n            return true;\n        } else {\n            MsgError(i18n.global.t('website.leechReturnError'));\n            return false;\n        }\n    } else {\n        return false;\n    }\n};\n\nfunction isHttpStatusCode(input: string): boolean {\n    const statusCodeRegex = /^[1-5][0-9]{2}$/;\n    return statusCodeRegex.test(input);\n}\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/auth-basic/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.' + authBasic.operate)\" @close=\"handleClose\">\n        <el-form-item>\n            <el-alert\n                v-if=\"authBasic.operate === 'edit'\"\n                :title=\"$t('website.editBasicAuthHelper')\"\n                type=\"info\"\n                :closable=\"false\"\n            />\n        </el-form-item>\n        <el-form ref=\"proxyForm\" label-position=\"top\" :model=\"authBasic\" :rules=\"rules\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\" v-if=\"authBasic.scope != 'root'\">\n                <el-input v-model.trim=\"authBasic.name\" :disabled=\"authBasic.operate === 'edit'\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.path')\" prop=\"path\" v-if=\"authBasic.scope != 'root'\">\n                <el-input v-model.trim=\"authBasic.path\" :disabled=\"authBasic.operate === 'edit'\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.username')\" prop=\"username\">\n                <el-input\n                    v-model.trim=\"authBasic.username\"\n                    :disabled=\"authBasic.scope == 'root' && authBasic.operate === 'edit'\"\n                ></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.login.password')\" prop=\"password\">\n                <el-input type=\"password\" clearable show-password v-model.trim=\"authBasic.password\">\n                    <template #append>\n                        <el-button @click=\"random\">\n                            {{ $t('commons.button.random') }}\n                        </el-button>\n                    </template>\n                </el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                <el-input v-model.trim=\"authBasic.remark\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit(proxyForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { operateAuthConfig, operatePathAuthConfig } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { Website } from '@/api/interface/website';\nimport { getRandomStr } from '@/utils/util';\n\nconst proxyForm = ref<FormInstance>();\nconst rules = ref({\n    username: [Rules.requiredInput, Rules.name],\n    password: [Rules.requiredInput, Rules.authBasicPassword],\n    name: [Rules.requiredInput],\n    path: [Rules.requiredInput],\n});\nconst open = ref(false);\nconst loading = ref(false);\n\nconst initData = (): Website.NginxAuthConfig => ({\n    websiteID: 0,\n    operate: 'create',\n    username: '',\n    password: '',\n    remark: '',\n    scope: 'root',\n    path: '',\n});\n\nlet authBasic = ref(initData());\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    proxyForm.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\n\nconst random = async () => {\n    authBasic.value.password = getRandomStr(16);\n};\n\nconst acceptParams = (proxyParam: Website.NginxAuthConfig) => {\n    authBasic.value = proxyParam;\n    open.value = true;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        try {\n            if (authBasic.value.scope == 'root') {\n                await operateAuthConfig(authBasic.value);\n            } else {\n                const req = {\n                    websiteID: authBasic.value.websiteID,\n                    path: authBasic.value.path,\n                    name: authBasic.value.name,\n                    username: authBasic.value.username,\n                    password: authBasic.value.password,\n                    operate: authBasic.value.operate,\n                    remark: authBasic.value.remark,\n                };\n                await operatePathAuthConfig(req);\n            }\n            if (authBasic.value.operate == 'create') {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n            } else {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            }\n            handleClose();\n        } catch (error) {\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/auth-basic/index.vue",
    "content": "<template>\n    <el-tabs type=\"border-card\" @tab-change=\"searchAll()\">\n        <el-tab-pane :label=\"$t('website.global')\">\n            <ComplexTable :data=\"data\" @search=\"search\" v-loading=\"loading\" :heightDiff=\"420\">\n                <template #toolbar>\n                    <el-button type=\"primary\" plain @click=\"openCreate('root')\">\n                        {{ $t('commons.button.create') }}\n                    </el-button>\n                    <el-switch\n                        class=\"ml-5\"\n                        v-model=\"enable\"\n                        @change=\"changeEnable\"\n                        :disabled=\"data.length === 0\"\n                    ></el-switch>\n                </template>\n                <el-table-column :label=\"$t('commons.login.username')\" prop=\"username\"></el-table-column>\n                <el-table-column :label=\"$t('website.remark')\" prop=\"remark\"></el-table-column>\n                <fu-table-operations\n                    :ellipsis=\"10\"\n                    width=\"260px\"\n                    :buttons=\"buttons\"\n                    :label=\"$t('commons.table.operate')\"\n                    :fixed=\"mobile ? false : 'right'\"\n                    fix\n                />\n            </ComplexTable>\n        </el-tab-pane>\n        <el-tab-pane :label=\"$t('website.path')\" v-if=\"showPath\">\n            <ComplexTable :data=\"pathData\" @search=\"searchPath\" v-loading=\"loading\" :heightDiff=\"420\">\n                <template #toolbar>\n                    <el-button type=\"primary\" plain @click=\"openCreate('path')\">\n                        {{ $t('commons.button.create') }}\n                    </el-button>\n                </template>\n                <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\"></el-table-column>\n                <el-table-column :label=\"$t('website.path')\" prop=\"path\"></el-table-column>\n                <el-table-column :label=\"$t('commons.login.username')\" prop=\"username\"></el-table-column>\n                <el-table-column :label=\"$t('website.remark')\" prop=\"remark\"></el-table-column>\n                <fu-table-operations\n                    :ellipsis=\"10\"\n                    width=\"260px\"\n                    :buttons=\"pathButtons\"\n                    :label=\"$t('commons.table.operate')\"\n                    :fixed=\"mobile ? false : 'right'\"\n                    fix\n                />\n            </ComplexTable>\n        </el-tab-pane>\n    </el-tabs>\n\n    <Create ref=\"createRef\" @close=\"searchAll()\" />\n    <OpDialog ref=\"opRef\" @search=\"searchAll\" />\n</template>\n\n<script lang=\"ts\" setup name=\"proxy\">\nimport { Website } from '@/api/interface/website';\nimport {\n    operateAuthConfig,\n    getAuthConfig,\n    getPathAuthConfig,\n    operatePathAuthConfig,\n    getWebsite,\n} from '@/api/modules/website';\nimport { computed, onMounted, ref } from 'vue';\nimport i18n from '@/lang';\nimport Create from './create/index.vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst id = computed(() => {\n    return props.id;\n});\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst loading = ref(false);\nconst data = ref([]);\nconst createRef = ref();\nconst enable = ref(false);\nconst opRef = ref();\nconst pathData = ref([]);\nconst showPath = ref(false);\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Website.NginxAuthConfig) {\n            row.scope = 'root';\n            openEdit(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.NginxAuthConfig) {\n            deleteAuth(row);\n        },\n    },\n];\n\nconst pathButtons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Website.NginxAuthConfig) {\n            row.scope = 'path';\n            openEdit(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.NginxPathAuthConfig) {\n            deletePathAuth(row);\n        },\n    },\n];\n\nconst initData = (id: number): Website.NginxAuthConfig => ({\n    websiteID: id,\n    operate: 'create',\n    username: '',\n    password: '',\n    remark: '',\n    scope: 'root',\n});\n\nconst openCreate = (scope: string) => {\n    let req = initData(id.value);\n    req.scope = scope;\n    createRef.value.acceptParams(req);\n};\n\nconst openEdit = (authConfig: Website.NginxAuthConfig) => {\n    let authParam = JSON.parse(JSON.stringify(authConfig));\n    authParam.operate = 'edit';\n    authParam.websiteID = id.value;\n    createRef.value.acceptParams(authParam);\n};\n\nconst deleteAuth = async (authConfig: Website.NginxAuthConfig) => {\n    authConfig.operate = 'delete';\n    authConfig.websiteID = id.value;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [authConfig.username],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.basicAuth'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: operateAuthConfig,\n        params: authConfig,\n    });\n};\n\nconst deletePathAuth = async (authConfig: Website.NginxPathAuthConfig) => {\n    authConfig.operate = 'delete';\n    authConfig.websiteID = id.value;\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [authConfig.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.basicAuth'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: operatePathAuthConfig,\n        params: authConfig,\n    });\n};\n\nconst changeEnable = () => {\n    const req = initData(id.value);\n    req.operate = enable.value ? 'enable' : 'disable';\n    loading.value = true;\n    operateAuthConfig(req)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst search = async () => {\n    try {\n        loading.value = true;\n        const res = await getAuthConfig({ websiteID: id.value });\n        data.value = res.data.items || [];\n        enable.value = res.data.enable;\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst searchPath = async () => {\n    try {\n        loading.value = true;\n        const res = await getPathAuthConfig({ websiteID: id.value });\n        pathData.value = res.data || [];\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst searchAll = () => {\n    search();\n    searchPath();\n};\n\nconst getSiteDetail = async () => {\n    getWebsite(id.value).then(async (res) => {\n        showPath.value = res.data.type !== 'proxy' && res.data.type !== 'deployment';\n    });\n};\n\nonMounted(() => {\n    getSiteDetail();\n    searchAll();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/cors/index.vue",
    "content": "<template>\n    <el-row>\n        <el-col :xs=\"24\" :sm=\"18\" :md=\"18\" :lg=\"14\" :xl=\"14\">\n            <el-form\n                ref=\"corsForm\"\n                label-position=\"right\"\n                label-width=\"150px\"\n                :model=\"corsSetting\"\n                :rules=\"rules\"\n                v-loading=\"loading\"\n            >\n                <CorsSetting\n                    v-model=\"corsSetting.cors\"\n                    :config=\"{\n                        allowOrigins: corsSetting.allowOrigins,\n                        allowMethods: corsSetting.allowMethods,\n                        allowHeaders: corsSetting.allowHeaders,\n                        allowCredentials: corsSetting.allowCredentials,\n                        preflight: corsSetting.preflight,\n                    }\"\n                    enable-size=\"small\"\n                    @update:config=\"updateonfig\"\n                ></CorsSetting>\n\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"submit()\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </el-form-item>\n            </el-form>\n        </el-col>\n    </el-row>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getCorsConfig, updateCorsConfig } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport CorsSetting from '@/views/website/website/cors/index.vue';\nimport { FormInstance } from 'element-plus';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst rules = ref({\n    allowOrigins: [Rules.requiredInput],\n});\n\nconst corsForm = ref<FormInstance>();\nconst loading = ref(false);\n\nconst corsSetting = ref({\n    cors: false,\n    allowOrigins: '*',\n    allowMethods: 'GET,POST,OPTIONS,PUT,DELETE',\n    allowHeaders: '',\n    allowCredentials: false,\n    preflight: true,\n    websiteID: props.id,\n});\n\nconst updateonfig = (config: any) => {\n    corsSetting.value.allowOrigins = config.allowOrigins;\n    corsSetting.value.allowMethods = config.allowMethods;\n    corsSetting.value.allowHeaders = config.allowHeaders;\n    corsSetting.value.allowCredentials = config.allowCredentials;\n    corsSetting.value.preflight = config.preflight;\n};\n\nconst submit = async () => {\n    const isValid = await corsForm.value?.validate();\n    if (!isValid) return;\n\n    corsSetting.value.websiteID = props.id;\n    try {\n        loading.value = true;\n        await updateCorsConfig(corsSetting.value);\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n    } catch (error) {\n        return;\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst getCors = () => {\n    getCorsConfig(props.id).then((res: any) => {\n        if (res.data) {\n            corsSetting.value = res.data;\n        }\n    });\n};\n\nonMounted(() => {\n    getCors();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/default-doc/index.vue",
    "content": "<template>\n    <el-row :gutter=\"20\" v-loading=\"loading\">\n        <el-col :xs=\"24\" :sm=\"18\" :md=\"8\" :lg=\"8\" :xl=\"8\">\n            <el-form @submit.prevent ref=\"defaultForm\" label-position=\"top\" :model=\"defaultModel\" :rules=\"rules\">\n                <el-form-item :label=\"$t('website.defaultDoc')\" prop=\"index\">\n                    <el-input v-model=\"defaultModel.index\" type=\"textarea\" :rows=\"8\"></el-input>\n                </el-form-item>\n            </el-form>\n            <el-button type=\"primary\" @click=\"submit(defaultForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.save') }}\n            </el-button>\n        </el-col>\n    </el-row>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { getNginxConfig, updateNginxConfig } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport { FormInstance } from 'element-plus';\nimport { computed, onMounted, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst websiteId = computed(() => {\n    return Number(props.id);\n});\nconst defaultForm = ref<FormInstance>();\nlet rules = ref({\n    index: [Rules.requiredInput, Rules.nginxDoc],\n});\nlet defaultModel = ref({\n    index: '',\n});\nlet req = ref({\n    operate: 'update',\n    scope: 'index',\n    websiteId: websiteId.value,\n    params: {},\n});\n\nlet loading = ref(false);\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        req.value.params = defaultModel.value;\n        loading.value = true;\n        updateNginxConfig(req.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                search(req.value);\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst search = (req: Website.NginxConfigReq) => {\n    loading.value = true;\n    getNginxConfig(req)\n        .then((res) => {\n            if (res.data && res.data.params.length > 0) {\n                const params = res.data.params[0].params;\n                let values = '';\n                for (const param of params) {\n                    values = values + param + '\\n';\n                }\n                defaultModel.value.index = values;\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    search(req.value);\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/domain/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('website.addDomain')\" @close=\"handleClose\">\n        <el-form ref=\"domainForm\" label-position=\"top\" :model=\"create\">\n            <DomainCreate v-model:form=\"create\" @gengerate=\"domainForm.clearValidate()\"></DomainCreate>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(domainForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { createDomain } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport DomainCreate from '@/views/website/website/domain-create/index.vue';\n\nconst domainForm = ref<FormInstance>();\n\nconst initDomain = () => ({\n    domain: '',\n    port: 80,\n    ssl: false,\n});\n\nconst open = ref(false);\nconst loading = ref(false);\nconst create = ref({\n    websiteID: 0,\n    domains: [initDomain()],\n    domainStr: '',\n});\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    domainForm.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = async (websiteId: number) => {\n    create.value.websiteID = Number(websiteId);\n    create.value.domains = [initDomain()];\n    create.value.domainStr = '';\n    open.value = true;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        createDomain(create.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/domain/index.vue",
    "content": "<template>\n    <ComplexTable :data=\"data\" @search=\"search\" v-loading=\"loading\" :heightDiff=\"400\">\n        <template #toolbar>\n            <el-button type=\"primary\" plain @click=\"openCreate\">{{ $t('website.addDomain') }}</el-button>\n        </template>\n        <el-table-column width=\"30px\">\n            <template #default=\"{ row }\">\n                <el-button link :icon=\"Promotion\" @click=\"openUrl(row)\"></el-button>\n            </template>\n        </el-table-column>\n        <el-table-column :label=\"$t('website.domain')\" prop=\"domain\">\n            <template #default=\"{ row }\">\n                {{ row.domain }}\n                <span class=\"text-gray-400\" v-if=\"isPunycoded(row.domain)\">({{ GetPunyCodeDomain(row.domain) }})</span>\n            </template>\n        </el-table-column>\n        <el-table-column :label=\"$t('commons.table.port')\" prop=\"port\"></el-table-column>\n        <el-table-column :label=\"'SSL'\" prop=\"ssl\">\n            <template #default=\"{ row }\">\n                <el-switch v-model=\"row.ssl\" @change=\"update(row)\" :disabled=\"row.port == 80\" />\n            </template>\n        </el-table-column>\n        <fu-table-operations\n            :ellipsis=\"1\"\n            :buttons=\"buttons\"\n            :label=\"$t('commons.table.operate')\"\n            :fixed=\"mobile ? false : 'right'\"\n            fix\n        />\n    </ComplexTable>\n    <Domain ref=\"domainRef\" @close=\"search(id)\"></Domain>\n    <OpDialog ref=\"opRef\" @search=\"search(id)\" />\n</template>\n\n<script lang=\"ts\" setup>\nimport Domain from './create/index.vue';\nimport { Website } from '@/api/interface/website';\nimport { deleteDomain, getWebsite, listDomains, updateDomain } from '@/api/modules/website';\nimport { computed, onMounted, ref } from 'vue';\nimport i18n from '@/lang';\nimport { Promotion } from '@element-plus/icons-vue';\nimport { GlobalStore } from '@/store';\nimport { checkAppInstalled } from '@/api/modules/app';\nimport { MsgSuccess } from '@/utils/message';\nimport { GetPunyCodeDomain, isPunycoded } from '@/utils/util';\nconst globalStore = GlobalStore();\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst id = computed(() => {\n    return props.id;\n});\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nlet loading = ref(false);\nconst data = ref<Website.Domain[]>([]);\nconst domainRef = ref();\nconst website = ref<Website.WebsiteDTO>();\nconst opRef = ref();\nconst httpPort = ref(80);\nconst httpsPort = ref(443);\n\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.Domain) {\n            deleteWebsiteDomain(row);\n        },\n        disabled: () => {\n            return data.value.length == 1;\n        },\n    },\n];\n\nconst openCreate = () => {\n    domainRef.value.acceptParams(id.value);\n};\n\nconst openUrl = (domain: Website.Domain) => {\n    const protocol = website.value.protocol.toLowerCase();\n    let url = protocol + '://' + domain.domain;\n    if (protocol == 'http' && domain.port != 80) {\n        url = url + ':' + domain.port;\n    }\n    if (protocol == 'https') {\n        if (httpsPort.value != 443) {\n            url = url + ':' + httpsPort.value;\n        }\n        if (domain.port != 80) {\n            url = 'http://' + domain.domain + ':' + domain.port;\n        }\n    }\n    window.open(url);\n};\n\nconst deleteWebsiteDomain = async (row: Website.Domain) => {\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [row.domain],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.domain'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteDomain,\n        params: { id: row.id },\n    });\n};\n\nconst search = (id: number) => {\n    loading.value = true;\n    listDomains(id)\n        .then((res) => {\n            data.value = res.data;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n    onCheck();\n};\n\nconst get = (id: number) => {\n    getWebsite(id).then((res) => {\n        website.value = res.data;\n    });\n};\n\nconst onCheck = async () => {\n    await checkAppInstalled('openresty', '')\n        .then((res) => {\n            httpPort.value = res.data.httpPort;\n            httpsPort.value = res.data.httpsPort;\n        })\n        .catch(() => {});\n};\n\nconst update = async (row: Website.Domain) => {\n    try {\n        await updateDomain({\n            id: row.id,\n            ssl: row.ssl,\n        });\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n    } catch {}\n};\n\nonMounted(() => {\n    search(id.value);\n    get(id.value);\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/https/index.vue",
    "content": "<template>\n    <el-row :gutter=\"20\" v-loading=\"loading\">\n        <el-col :xs=\"24\" :sm=\"18\" :md=\"18\" :lg=\"14\" :xl=\"14\">\n            <el-alert :closable=\"false\">\n                <template #default>\n                    <span class=\"whitespace-pre-line\">{{ $t('website.SSLHelper') }}</span>\n                </template>\n            </el-alert>\n            <el-form\n                class=\"moblie-form\"\n                ref=\"httpsForm\"\n                label-position=\"right\"\n                label-width=\"150px\"\n                :model=\"form\"\n                :rules=\"rules\"\n            >\n                <el-form-item prop=\"enable\" :label=\"$t('website.enableHTTPS')\">\n                    <el-switch v-model=\"form.enable\" @change=\"changeEnable\"></el-switch>\n                </el-form-item>\n\n                <el-collapse-transition>\n                    <div v-if=\"form.enable\">\n                        <el-form-item :label=\"'HTTPS ' + $t('commons.table.port')\" prop=\"httpsPort\">\n                            <el-text>{{ form.httpsPort }}</el-text>\n                        </el-form-item>\n\n                        <el-text type=\"warning\" class=\"!ml-2\">{{ $t('website.ipWebsiteWarn') }}</el-text>\n\n                        <el-divider content-position=\"left\">{{ $t('website.SSLConfig') }}</el-divider>\n                        <HttpsConfig v-model=\"form\" :website-ssl=\"websiteSSL\" @ssl-change=\"handleSSLChange\" />\n                        <el-form-item>\n                            <el-button type=\"primary\" @click=\"submit(httpsForm)\">\n                                {{ $t('commons.button.save') }}\n                            </el-button>\n                        </el-form-item>\n                    </div>\n                </el-collapse-transition>\n            </el-form>\n        </el-col>\n    </el-row>\n</template>\n<script lang=\"ts\" setup>\nimport HttpsConfig from '@/views/website/website/components/https/index.vue';\n\nimport { Website } from '@/api/interface/website';\nimport { getHTTPSConfig, updateHTTPSConfig } from '@/api/modules/website';\nimport { ElMessageBox, FormInstance } from 'element-plus';\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { Rules } from '@/global/form-rules';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst id = computed(() => {\n    return props.id;\n});\nconst httpsForm = ref<FormInstance>();\nconst form = reactive({\n    acmeAccountID: 0,\n    enable: false,\n    websiteId: id.value,\n    websiteSSLId: undefined,\n    type: 'existed',\n    importType: 'paste',\n    privateKey: '',\n    certificate: '',\n    privateKeyPath: '',\n    certificatePath: '',\n    httpConfig: 'HTTPToHTTPS',\n    hsts: true,\n    hstsIncludeSubDomains: false,\n    algorithm:\n        'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED',\n    SSLProtocol: ['TLSv1.3', 'TLSv1.2'],\n    httpsPort: '443',\n    http3: false,\n});\nconst loading = ref(false);\nconst websiteSSL = ref();\nconst rules = ref({\n    type: [Rules.requiredSelect],\n    privateKey: [Rules.requiredInput],\n    certificate: [Rules.requiredInput],\n    privateKeyPath: [Rules.requiredInput],\n    certificatePath: [Rules.requiredInput],\n    websiteSSLId: [Rules.requiredSelect],\n    httpConfig: [Rules.requiredSelect],\n    SSLProtocol: [Rules.requiredSelect],\n    algorithm: [Rules.requiredInput],\n    acmeAccountID: [Rules.requiredInput],\n});\nconst resData = ref();\n\nconst handleSSLChange = (ssl: Website.SSL) => {\n    websiteSSL.value = ssl;\n};\n\nconst get = () => {\n    getHTTPSConfig(id.value).then((res) => {\n        if (res.data) {\n            form.type = 'existed';\n            const data = res.data;\n            resData.value = data;\n            form.enable = data.enable;\n            if (data.httpConfig != '') {\n                form.httpConfig = data.httpConfig;\n            }\n            if (data.SSLProtocol && data.SSLProtocol.length > 0) {\n                form.SSLProtocol = data.SSLProtocol;\n            }\n            if (data.algorithm != '') {\n                form.algorithm = data.algorithm;\n            }\n            if (data.SSL && data.SSL.id > 0) {\n                form.websiteSSLId = data.SSL.id;\n                websiteSSL.value = data.SSL;\n                form.acmeAccountID = data.SSL.acmeAccountId;\n            }\n            form.hsts = data.hsts;\n            form.hstsIncludeSubDomains = data.hstsIncludeSubDomains || false;\n            form.http3 = data.http3;\n            form.httpsPort = data.httpsPort;\n        }\n    });\n};\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        form.websiteId = id.value;\n        updateHTTPSConfig(form)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                get();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst changeEnable = (enable: boolean) => {\n    if (enable) {\n        form.hsts = true;\n    } else {\n        form.hstsIncludeSubDomains = false;\n    }\n    if (resData.value.enable && !enable) {\n        ElMessageBox.confirm(i18n.global.t('website.disableHTTPSHelper'), i18n.global.t('website.disableHTTPS'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n            type: 'error',\n            closeOnClickModal: false,\n            beforeClose: async (action, instance, done) => {\n                if (action !== 'confirm') {\n                    form.enable = true;\n                    done();\n                } else {\n                    instance.confirmButtonLoading = true;\n                    form.enable = false;\n                    form.websiteId = id.value;\n                    updateHTTPSConfig(form).then(() => {\n                        done();\n                        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n                        get();\n                    });\n                }\n            },\n        }).then(() => {});\n    }\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n<style lang=\"scss\">\n.el-collapse,\n.el-collapse-item__wrap {\n    border: none;\n}\n.el-collapse-item__header {\n    border: none;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/index.vue",
    "content": "<template>\n    <div :style=\"{ '--main-height': mainHeight + 'px' }\">\n        <el-tabs\n            tab-position=\"left\"\n            v-model=\"tabIndex\"\n            v-if=\"id > 0 && website.type != 'stream'\"\n            class=\"custom-tabs\"\n            ref=\"tabsRef\"\n        >\n            <el-tab-pane :label=\"$t('website.domainConfig')\" name=\"0\">\n                <Domain :key=\"id\" :id=\"id\" v-if=\"tabIndex == '0'\"></Domain>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.sitePath')\" name=\"1\">\n                <SitePath :id=\"id\" v-if=\"tabIndex == '1'\"></SitePath>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.defaultDoc')\" name=\"2\">\n                <Default :id=\"id\" v-if=\"tabIndex == '2'\"></Default>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.rate')\" name=\"3\">\n                <LimitConn :id=\"id\" v-if=\"tabIndex == '3'\"></LimitConn>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.proxy')\" name=\"4\">\n                <Proxy :id=\"id\" v-if=\"tabIndex == '4'\"></Proxy>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.loadBalance')\" name=\"5\">\n                <LoadBalance :id=\"id\" v-if=\"tabIndex == '5'\"></LoadBalance>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.basicAuth')\" name=\"6\">\n                <AuthBasic :id=\"id\" v-if=\"tabIndex == '6'\"></AuthBasic>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.cors')\" name=\"16\">\n                <Cors :id=\"id\" v-if=\"tabIndex == '16'\"></Cors>\n            </el-tab-pane>\n            <el-tab-pane :label=\"'HTTPS'\" name=\"7\">\n                <HTTPS :id=\"id\" v-if=\"tabIndex == '7'\"></HTTPS>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.realIP')\" name=\"8\">\n                <RealIP :id=\"id\" v-if=\"tabIndex == '8'\"></RealIP>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.rewrite')\" name=\"9\">\n                <Rewrite :id=\"id\" v-if=\"tabIndex == '9'\"></Rewrite>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.antiLeech')\" name=\"10\">\n                <AntiLeech :id=\"id\" v-if=\"tabIndex == '10'\"></AntiLeech>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.redirect')\" name=\"11\">\n                <Redirect :id=\"id\" v-if=\"tabIndex == '11'\"></Redirect>\n            </el-tab-pane>\n\n            <el-tab-pane\n                :label=\"'PHP'\"\n                name=\"13\"\n                v-if=\"(website.type === 'runtime' && website.runtimeType === 'php') || website.type === 'static'\"\n            >\n                <PHP :id=\"id\" v-if=\"tabIndex == '13'\"></PHP>\n            </el-tab-pane>\n            <el-tab-pane\n                :label=\"$t('logs.resource')\"\n                name=\"14\"\n                v-if=\"website.type === 'runtime' || website.type === 'static'\"\n            >\n                <Resource :id=\"id\" v-if=\"tabIndex == '14'\"></Resource>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.other')\" name=\"12\">\n                <Other :id=\"id\" v-if=\"tabIndex == '12'\"></Other>\n            </el-tab-pane>\n        </el-tabs>\n        <el-tabs\n            tab-position=\"left\"\n            v-model=\"tabIndex\"\n            v-if=\"id > 0 && website.type == 'stream'\"\n            class=\"custom-tabs\"\n            ref=\"tabsRef\"\n        >\n            <el-tab-pane :label=\"$t('website.other')\" name=\"12\">\n                <Other :id=\"id\" v-if=\"tabIndex == '12'\"></Other>\n            </el-tab-pane>\n            <el-tab-pane :label=\"$t('website.stream')\" name=\"13\">\n                <Stream :id=\"id\" v-if=\"tabIndex == '13'\"></Stream>\n            </el-tab-pane>\n        </el-tabs>\n    </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Basic\">\nimport { computed, onMounted, ref, watch } from 'vue';\n\nimport Domain from './domain/index.vue';\nimport Default from './default-doc/index.vue';\nimport LimitConn from './limit-conn/index.vue';\nimport Other from './other/index.vue';\nimport HTTPS from './https/index.vue';\nimport SitePath from './site-folder/index.vue';\nimport Rewrite from './rewrite/index.vue';\nimport Proxy from './proxy/index.vue';\nimport AuthBasic from './auth-basic/index.vue';\nimport AntiLeech from './anti-Leech/index.vue';\nimport Redirect from './redirect/index.vue';\nimport LoadBalance from './load-balance/index.vue';\nimport PHP from './php/index.vue';\nimport RealIP from './real-ip/index.vue';\nimport Resource from './resource/index.vue';\nimport Cors from './cors/index.vue';\nimport Stream from './stream/index.vue';\n\nconst props = defineProps({\n    website: {\n        type: Object,\n    },\n    heightDiff: {\n        type: Number,\n        default: 0,\n    },\n});\nconst windowHeight = ref(window.innerHeight);\nconst mainHeight = computed(() => windowHeight.value - props.heightDiff);\n\nconst id = computed(() => {\n    return props.website.id;\n});\nconst tabIndex = ref('0');\nconst menuKey = ref('site-tabIndex-');\n\nwatch(tabIndex, (newVal) => {\n    localStorage.setItem(menuKey.value, newVal);\n});\n\nconst handleResize = () => {\n    windowHeight.value = window.innerHeight;\n};\n\nconst tabsRef = ref();\nconst handleScroll = (event: WheelEvent) => {\n    if (!tabsRef.value) return;\n    const tabContainer = tabsRef.value.$el.querySelector('.el-tabs__nav-scroll');\n    if (!tabContainer) return;\n\n    const currentScrollTop = tabContainer.scrollTop;\n    if (currentScrollTop == 0) {\n        return;\n    }\n\n    const target = event.target as HTMLElement;\n    if (!target.classList.contains('el-tabs__item')) {\n        return;\n    }\n\n    event.preventDefault();\n    tabContainer.scrollTop += event.deltaY;\n};\n\nonMounted(() => {\n    menuKey.value = 'site-tabIndex-' + props.website.id;\n    const storedTabIndex = localStorage.getItem(menuKey.value);\n    if (storedTabIndex !== null) {\n        tabIndex.value = storedTabIndex;\n    } else {\n        tabIndex.value = '0';\n        if (props.website.type == 'stream') {\n            tabIndex.value = '12';\n        }\n    }\n    window.addEventListener('resize', handleResize);\n    document.addEventListener('wheel', handleScroll, { passive: false });\n});\n\nonUnmounted(() => {\n    window.removeEventListener('resize', handleResize);\n    document.removeEventListener('wheel', handleScroll);\n});\n</script>\n<style scoped>\n.custom-tabs {\n    height: var(--main-height);\n    display: flex;\n    overflow: hidden;\n}\n\n.custom-tabs :deep(.el-tabs__header.is-left) {\n    height: 100%;\n    overflow-y: auto;\n    overflow-x: hidden;\n    flex-shrink: 0;\n}\n\n.custom-tabs :deep(.el-tabs__content) {\n    height: 100%;\n    overflow-y: auto;\n    overflow-x: hidden;\n    flex-grow: 1;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/limit-conn/index.vue",
    "content": "<template>\n    <el-row :gutter=\"20\" v-loading=\"loading\">\n        <el-col :xs=\"24\" :sm=\"18\" :md=\"8\" :lg=\"8\" :xl=\"8\">\n            <el-form ref=\"limitForm\" label-position=\"right\" :model=\"form\" :rules=\"rules\" label-width=\"100px\">\n                <el-form-item prop=\"enable\" :label=\"$t('website.enableOrNot')\">\n                    <el-switch v-model=\"enable\" @change=\"changeEnable\"></el-switch>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.limit')\">\n                    <el-select v-model=\"ruleKey\" @change=\"changeRule(ruleKey)\">\n                        <el-option :label=\"$t('website.current')\" :value=\"'current'\"></el-option>\n                        <el-option\n                            v-for=\"(limit, index) in limitRules\"\n                            :key=\"index\"\n                            :label=\"limit.key\"\n                            :value=\"limit.key\"\n                        ></el-option>\n                    </el-select>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.perserver')\" prop=\"perserver\">\n                    <el-input v-model.number=\"form.perserver\" maxlength=\"15\"></el-input>\n                    <span class=\"input-help\">{{ $t('website.perserverHelper') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.perip')\" prop=\"perip\">\n                    <el-input v-model.number=\"form.perip\" maxlength=\"15\"></el-input>\n                    <span class=\"input-help\">{{ $t('website.peripHelper') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.rateLimit')\" prop=\"rate\">\n                    <el-input v-model.number=\"form.rate\" maxlength=\"15\"></el-input>\n                    <span class=\"input-help\">{{ $t('website.rateLimitHelper') }}</span>\n                </el-form-item>\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"submit(limitForm)\" :disabled=\"loading\">\n                        <span v-if=\"enable\">{{ $t('commons.button.save') }}</span>\n                        <span v-else>{{ $t('commons.button.saveAndEnable') }}</span>\n                    </el-button>\n                </el-form-item>\n            </el-form>\n        </el-col>\n    </el-row>\n</template>\n\n<script lang=\"ts\" setup>\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport { Website } from '@/api/interface/website';\nimport { getNginxConfig, updateNginxConfig } from '@/api/modules/website';\nimport { FormInstance } from 'element-plus';\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst websiteId = computed(() => {\n    return Number(props.id);\n});\nlet rules = reactive({\n    perserver: [Rules.requiredInput, checkNumberRange(1, 65535)],\n    perip: [Rules.requiredInput, checkNumberRange(1, 65535)],\n    rate: [Rules.requiredInput, checkNumberRange(1, 99999999)],\n});\nconst limitForm = ref<FormInstance>();\nlet form = reactive({\n    perserver: 300,\n    perip: 25,\n    rate: 512,\n});\nlet req = reactive({\n    operate: 'update',\n    scope: 'limit-conn',\n    websiteId: websiteId.value,\n    params: [{}],\n});\nlet scopeReq = reactive({\n    scope: 'limit-conn',\n    websiteId: websiteId.value,\n});\nlet enable = ref(false);\nlet loading = ref(false);\n\nconst limitRules = [\n    { key: i18n.global.t('website.blog'), values: [300, 25, 512] },\n    { key: i18n.global.t('website.imageSite'), values: [200, 10, 1024] },\n    { key: i18n.global.t('website.downloadSite'), values: [50, 3, 2048] },\n    { key: i18n.global.t('website.shopSite'), values: [500, 10, 2048] },\n    { key: i18n.global.t('website.doorSite'), values: [400, 15, 1024] },\n    { key: i18n.global.t('website.qiteSite'), values: [60, 10, 512] },\n    { key: i18n.global.t('website.videoSite'), values: [150, 4, 1024] },\n];\n\nlet ruleKey = ref('');\n\nconst search = (scopeReq: Website.NginxScopeReq) => {\n    loading.value = true;\n    getNginxConfig(scopeReq)\n        .then((res) => {\n            ruleKey.value = 'current';\n            if (res.data) {\n                enable.value = res.data.enable;\n                if (res.data.enable == false) {\n                    req.operate = 'add';\n                }\n                for (const param of res.data.params) {\n                    if (param.name === 'limit_conn') {\n                        if (param.params[0] === 'perserver' && param.params[1]) {\n                            form.perserver = Number(param.params[1].match(/\\d+/g));\n                        }\n                        if (param.params[0] === 'perip' && param.params[1]) {\n                            form.perip = Number(param.params[1].match(/\\d+/g));\n                        }\n                    }\n                    if (param.name === 'limit_rate' && param.params[0]) {\n                        form.rate = Number(param.params[0].match(/\\d+/g));\n                    }\n                }\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        let params = [\n            {\n                limit_conn: 'perserver ' + String(form.perserver),\n            },\n            {\n                limit_conn: 'perip ' + String(form.perip),\n            },\n            {\n                limit_rate: String(form.rate) + 'k',\n            },\n        ];\n        req.params = params;\n        if (req.operate === 'add') {\n            enable.value = true;\n        }\n        updateNginxConfig(req)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                search(req);\n            })\n            .finally(() => {\n                if (req.operate === 'add') {\n                    enable.value = false;\n                }\n                loading.value = false;\n            });\n    });\n};\n\nconst changeEnable = () => {\n    if (!enable.value) {\n        req.operate = 'delete';\n    } else {\n        req.operate = 'add';\n    }\n    submit(limitForm.value);\n};\n\nconst changeRule = (key: string) => {\n    limitRules.forEach((limit) => {\n        if (limit.key === key) {\n            form.perserver = limit.values[0];\n            form.perip = limit.values[1];\n            form.rate = limit.values[2];\n        }\n    });\n};\n\nonMounted(() => {\n    search(scopeReq);\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/load-balance/file/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('website.sourceFile')\"\n        @close=\"handleClose\"\n        :size=\"mobile ? 'full' : 'normal'\"\n    >\n        <CodemirrorPro v-model=\"req.content\" mode=\"nginx\"></CodemirrorPro>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { computed, reactive, ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateLoadBalanceFile } from '@/api/modules/website';\nimport { GlobalStore } from '@/store';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport { Website } from '@/api/interface/website';\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst proxyForm = ref<FormInstance>();\nconst open = ref(false);\nconst loading = ref(false);\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    proxyForm.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\nconst req = reactive({\n    name: '',\n    websiteID: 0,\n    content: '',\n});\n\nconst acceptParams = async (ups: Website.NginxUpstreamFile) => {\n    req.name = ups.name;\n    req.websiteID = ups.websiteID;\n    req.content = ups.content;\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    updateLoadBalanceFile(req)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            handleClose();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/load-balance/form/index.vue",
    "content": "<template>\n    <el-form ref=\"lbFormRef\" label-position=\"top\" :model=\"formData\" :rules=\"rules\">\n        <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n            <el-input v-model.trim=\"formData.name\" :disabled=\"disabled\"></el-input>\n        </el-form-item>\n        <el-form-item :label=\"$t('website.algorithm')\" prop=\"algorithm\">\n            <el-select v-model=\"formData.algorithm\" @change=\"handleAlgorithmChange\">\n                <el-option\n                    v-for=\"(algorithm, index) in Algorithms\"\n                    :label=\"algorithm.label\"\n                    :key=\"index\"\n                    :value=\"algorithm.value\"\n                ></el-option>\n            </el-select>\n            <span class=\"input-help\">{{ getHelper(formData.algorithm) }}</span>\n        </el-form-item>\n\n        <div>\n            <el-card v-for=\"(server, index) of formData.servers\" :key=\"index\" class=\"server-card\" shadow=\"hover\">\n                <template #header>\n                    <div class=\"card-header\">\n                        <span class=\"server-title\">\n                            <el-icon><Monitor /></el-icon>\n                            <span class=\"ml-2\">{{ $t('website.server') }} - {{ server.server || index + 1 }}</span>\n                        </span>\n                        <el-button\n                            v-if=\"formData.servers.length > 1\"\n                            text\n                            type=\"danger\"\n                            icon=\"Delete\"\n                            size=\"small\"\n                            @click=\"removeServer(index)\"\n                        ></el-button>\n                    </div>\n                </template>\n\n                <div>\n                    <el-row :gutter=\"16\">\n                        <el-col :span=\"24\">\n                            <el-form-item\n                                :label=\"$t('setting.address')\"\n                                :prop=\"`servers.${index}.server`\"\n                                :rules=\"rules.server\"\n                            >\n                                <el-input\n                                    v-model=\"formData.servers[index].server\"\n                                    :placeholder=\"type === 'stream' ? '127.0.0.1:3306' : '127.0.0.1:8080'\"\n                                    @input=\"handleChange\"\n                                ></el-input>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                    <el-row :gutter=\"16\">\n                        <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                            <el-form-item\n                                :label=\"$t('website.weight')\"\n                                :prop=\"`servers.${index}.weight`\"\n                                :rules=\"rules.weight\"\n                            >\n                                <el-input\n                                    type=\"number\"\n                                    v-model.number=\"formData.servers[index].weight\"\n                                    @input=\"handleChange\"\n                                ></el-input>\n                            </el-form-item>\n                        </el-col>\n                        <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                            <el-form-item\n                                :label=\"$t('website.strategy')\"\n                                :prop=\"`servers.${index}.flag`\"\n                                :rules=\"rules.flag\"\n                            >\n                                <el-select v-model=\"formData.servers[index].flag\" clearable @change=\"handleChange\">\n                                    <el-option\n                                        v-for=\"flag in getStatusStrategy()\"\n                                        :label=\"flag.label\"\n                                        :key=\"flag.value\"\n                                        :value=\"flag.value\"\n                                    ></el-option>\n                                </el-select>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                    <el-row :gutter=\"16\">\n                        <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                            <el-form-item\n                                :label=\"$t('website.maxFails')\"\n                                :prop=\"`servers.${index}.maxFails`\"\n                                :rules=\"rules.maxFails\"\n                            >\n                                <el-input\n                                    type=\"number\"\n                                    v-model.number=\"formData.servers[index].maxFails\"\n                                    @input=\"handleChange\"\n                                >\n                                    <template #append>{{ $t('commons.units.time') }}</template>\n                                </el-input>\n                            </el-form-item>\n                        </el-col>\n                        <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                            <el-form-item :prop=\"`servers.${index}.failTimeout`\" :rules=\"rules.failTimeout\">\n                                <template #label>\n                                    <span class=\"inline-flex items-center\">\n                                        {{ $t('website.failTimeout') }}\n                                        <el-tooltip :content=\"$t('website.failTimeoutHelper')\" placement=\"top\">\n                                            <el-icon><QuestionFilled /></el-icon>\n                                        </el-tooltip>\n                                    </span>\n                                </template>\n                                <el-input\n                                    type=\"number\"\n                                    v-model.number=\"formData.servers[index].failTimeout\"\n                                    @input=\"handleChange\"\n                                >\n                                    <template #append>\n                                        <el-select\n                                            v-model.number=\"formData.servers[index].failTimeoutUnit\"\n                                            class=\"!w-24\"\n                                            @change=\"handleChange\"\n                                        >\n                                            <el-option\n                                                v-for=\"(unit, indexKey) in Units\"\n                                                :key=\"indexKey\"\n                                                :label=\"unit.label\"\n                                                :value=\"unit.value\"\n                                            />\n                                        </el-select>\n                                    </template>\n                                </el-input>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                    <el-row :gutter=\"16\">\n                        <el-col :xs=\"24\" :sm=\"24\" :md=\"12\" :lg=\"12\" :xl=\"12\">\n                            <el-form-item\n                                :label=\"$t('website.maxConns')\"\n                                :prop=\"`servers.${index}.maxConns`\"\n                                :rules=\"rules.maxConns\"\n                            >\n                                <el-input\n                                    type=\"number\"\n                                    v-model.number=\"formData.servers[index].maxConns\"\n                                    @input=\"handleChange\"\n                                ></el-input>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                </div>\n            </el-card>\n\n            <el-button class=\"add-server-btn\" type=\"primary\" plain @click=\"addServer\" icon=\"Plus\">\n                {{ $t('commons.button.add') }}\n            </el-button>\n        </div>\n    </el-form>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, computed } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { getAlgorithms, getStatusStrategy, Units } from '@/global/mimetype';\nimport { Monitor, QuestionFilled } from '@element-plus/icons-vue';\n\ninterface Server {\n    server: string;\n    weight?: number;\n    maxFails?: number;\n    maxConns?: number;\n    failTimeout?: number;\n    failTimeoutUnit: string;\n    flag: string;\n}\n\ninterface LoadBalanceFormData {\n    name: string;\n    algorithm: string;\n    servers: Server[];\n}\n\ninterface Props {\n    modelValue: LoadBalanceFormData;\n    disabled?: boolean;\n    type?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n    disabled: false,\n    type: 'loadbanlance',\n});\n\nconst emit = defineEmits<{\n    (e: 'update:modelValue', value: LoadBalanceFormData): void;\n}>();\n\nconst lbFormRef = ref<FormInstance>();\n\nconst formData = computed({\n    get: () => props.modelValue,\n    set: (value) => emit('update:modelValue', value),\n});\n\nconst rules = ref<any>({\n    name: [Rules.appName],\n    algorithm: [Rules.requiredSelect],\n    server: [Rules.requiredInput],\n    weight: [checkNumberRange(0, 100)],\n    servers: {\n        type: Array,\n    },\n    maxFails: [checkNumberRange(0, 1000)],\n    maxConns: [checkNumberRange(0, 10000)],\n    failTimeout: [checkNumberRange(0, 300)],\n});\n\nconst Algorithms = getAlgorithms(props.type);\n\nconst helper = ref();\nconst getHelper = (key: string) => {\n    Algorithms.forEach((algorithm) => {\n        if (algorithm.value === key) {\n            helper.value = algorithm.placeHolder;\n        }\n    });\n    return helper.value;\n};\n\nconst initServer = (): Server => ({\n    server: '',\n    weight: undefined,\n    maxFails: undefined,\n    maxConns: undefined,\n    failTimeout: undefined,\n    failTimeoutUnit: 's',\n    flag: '',\n});\n\nconst addServer = () => {\n    const newServers = [...formData.value.servers, initServer()];\n    emit('update:modelValue', { ...formData.value, servers: newServers });\n};\n\nconst removeServer = (index: number) => {\n    const newServers = [...formData.value.servers];\n    newServers.splice(index, 1);\n    emit('update:modelValue', { ...formData.value, servers: newServers });\n};\n\nconst handleChange = () => {\n    emit('update:modelValue', { ...formData.value });\n};\n\nconst handleAlgorithmChange = () => {\n    handleChange();\n};\n\nconst validate = async () => {\n    if (!lbFormRef.value) return false;\n    return await lbFormRef.value.validate();\n};\n\nconst resetFields = () => {\n    lbFormRef.value?.resetFields();\n};\n\nconst clearValidate = () => {\n    lbFormRef.value?.clearValidate();\n};\n\ndefineExpose({\n    validate,\n    resetFields,\n    clearValidate,\n    formRef: lbFormRef,\n});\n</script>\n\n<style scoped lang=\"scss\">\n.server-card {\n    margin-bottom: 16px;\n    border-radius: 8px;\n    transition: all 0.3s ease;\n\n    &:hover {\n        border-color: var(--el-color-primary);\n    }\n\n    :deep(.el-card__header) {\n        padding: 12px 20px;\n        background-color: var(--el-fill-color-light);\n    }\n}\n\n.card-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    height: 20px;\n\n    .server-title {\n        display: flex;\n        align-items: center;\n        font-weight: 500;\n        font-size: 14px;\n        color: var(--el-text-color-primary);\n    }\n}\n\n.add-server-btn {\n    width: 100%;\n    height: 48px;\n    border-style: dashed;\n    font-size: 14px;\n    margin-top: 8px;\n\n    &:hover {\n        border-color: var(--el-color-primary);\n        background-color: var(--el-color-primary-light-9);\n    }\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/load-balance/index.vue",
    "content": "<template>\n    <div>\n        <ComplexTable :data=\"data\" @search=\"search\" v-loading=\"loading\" :heightDiff=\"420\">\n            <template #toolbar>\n                <el-button type=\"primary\" plain @click=\"create()\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-alert :closable=\"false\" class=\"!mt-2\">\n                    <template #default>\n                        <span class=\"whitespace-pre-line\">{{ $t('website.loadBalanceHelper') }}</span>\n                    </template>\n                </el-alert>\n            </template>\n            <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\"></el-table-column>\n            <el-table-column :label=\"$t('website.algorithm')\" prop=\"algorithm\">\n                <template #default=\"{ row }\">\n                    {{ getAlgorithm(row.algorithm) }}\n                </template>\n            </el-table-column>\n            <el-table-column :label=\"$t('website.server')\" prop=\"servers\" minWidth=\"400px\">\n                <template #default=\"{ row }\">\n                    <table>\n                        <tr v-for=\"(item, index) in row.servers\" :key=\"index\">\n                            <td>\n                                <el-tag>\n                                    {{ item.server }}\n                                </el-tag>\n                            </td>\n                            <td v-if=\"item.weight > 0\">\n                                <el-tag type=\"success\">{{ $t('website.weight') }}: {{ item.weight }}</el-tag>\n                            </td>\n                            <td v-if=\"item.failTimeout != ''\">\n                                <el-tag type=\"warning\">\n                                    {{ $t('website.failTimeout') }}: {{ item.failTimeout }} {{ item.failTimeoutUnit }}\n                                </el-tag>\n                            </td>\n                            <td v-if=\"item.maxFails > 0\">\n                                <el-tag type=\"danger\">{{ $t('website.maxFails') }}: {{ item.maxFails }}</el-tag>\n                            </td>\n                            <td v-if=\"item.maxConns > 0\">\n                                <el-tag>{{ $t('website.maxConns') }}: {{ item.maxConns }}</el-tag>\n                            </td>\n                            <td v-if=\"item.flag != ''\">\n                                <el-tag type=\"info\">\n                                    {{ $t('website.strategy') }}:\n                                    <span v-if=\"item.flag === 'backup'\">\n                                        {{ $t('website.strategyBackup') }}\n                                    </span>\n                                    <span v-if=\"item.flag === 'down'\">\n                                        {{ $t('website.strategyDown') }}\n                                    </span>\n                                </el-tag>\n                            </td>\n                        </tr>\n                    </table>\n                </template>\n            </el-table-column>\n            <fu-table-operations\n                :ellipsis=\"10\"\n                width=\"260px\"\n                :buttons=\"buttons\"\n                :label=\"$t('commons.table.operate')\"\n                fix\n            />\n        </ComplexTable>\n    </div>\n    <Operate ref=\"operateRef\" @close=\"search()\" />\n    <OpDialog ref=\"delRef\" @search=\"search()\" />\n    <File ref=\"fileRef\" @search=\"search()\" />\n</template>\n\n<script setup lang=\"ts\">\nimport { deleteLoadBalance, getLoadBalances } from '@/api/modules/website';\nimport { onMounted, ref } from 'vue';\nimport Operate from './operate/index.vue';\nimport i18n from '@/lang';\nimport { Website } from '@/api/interface/website';\nimport { getAlgorithms } from '@/global/mimetype';\nimport File from './file/index.vue';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst data = ref([]);\nconst loading = ref(false);\nconst operateRef = ref();\nconst delRef = ref();\nconst fileRef = ref();\nconst Algorithms = getAlgorithms('');\n\nconst buttons = [\n    {\n        label: i18n.global.t('website.sourceFile'),\n        click: function (row: any) {\n            openEditFile(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: (row: any) => {\n            update(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: (row: any) => {\n            deleteLb(row);\n        },\n    },\n];\n\nconst search = () => {\n    getLoadBalances(props.id).then((res) => {\n        data.value = res.data;\n    });\n};\n\nconst getAlgorithm = (key: string) => {\n    let label = '';\n    Algorithms.forEach((algorithm) => {\n        if (algorithm.value === key) {\n            label = algorithm.label;\n        }\n    });\n    if (label === '') {\n        return i18n.global.t('commons.table.default');\n    }\n    return label;\n};\n\nconst deleteLb = async (row: Website.NginxUpstream) => {\n    delRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [row.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.loadBalance'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteLoadBalance,\n        params: { websiteID: props.id, name: row.name },\n    });\n};\n\nconst create = () => {\n    operateRef.value.acceptParams({\n        websiteID: props.id,\n        operate: 'create',\n    });\n};\n\nconst update = (row: Website.NginxUpstream) => {\n    operateRef.value.acceptParams({\n        websiteID: props.id,\n        operate: 'edit',\n        upstream: row,\n    });\n};\n\nconst openEditFile = (row: Website.NginxUpstream) => {\n    fileRef.value.acceptParams({\n        websiteID: props.id,\n        name: row.name,\n        content: row.content,\n    });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/load-balance/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        @close=\"handleClose\"\n        size=\"large\"\n        :header=\"$t('commons.button.' + operate) + $t('website.loadBalance')\"\n        :resource=\"operate == 'create' ? '' : formData.name\"\n    >\n        <LoadBalanceForm ref=\"lbFormRef\" v-model=\"formData\" :disabled=\"operate === 'edit' && disableName\" />\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">\n                    {{ $t('commons.button.cancel') }}\n                </el-button>\n                <el-button type=\"primary\" @click=\"submit\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { createLoadBalance, updateLoadBalance } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Website } from '@/api/interface/website';\nimport LoadBalanceForm from '@/views/website/website/config/basic/load-balance/form/index.vue';\n\ninterface LoadBalanceOperate {\n    websiteID: number;\n    operate: string;\n    upstream?: Website.NginxUpstream;\n}\n\nconst lbFormRef = ref();\nconst open = ref(false);\nconst loading = ref(false);\nconst operate = ref('create');\nconst websiteID = ref(0);\nconst disableName = ref(false);\n\nconst formData = ref({\n    name: '',\n    algorithm: 'default',\n    servers: [\n        {\n            server: '',\n            weight: undefined,\n            maxFails: undefined,\n            maxConns: undefined,\n            failTimeout: undefined,\n            failTimeoutUnit: 's',\n            flag: '',\n        },\n    ],\n});\n\nconst em = defineEmits(['close']);\n\nconst handleClose = () => {\n    lbFormRef.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = async (req: LoadBalanceOperate) => {\n    websiteID.value = req.websiteID;\n    operate.value = req.operate;\n\n    if (req.operate == 'edit') {\n        disableName.value = true;\n        formData.value.name = req.upstream?.name || '';\n        formData.value.algorithm = req.upstream?.algorithm || 'default';\n\n        let servers = [];\n        req.upstream?.servers?.forEach((server) => {\n            const weight = server.weight == 0 ? undefined : server.weight;\n            const maxFails = server.maxFails == 0 ? undefined : server.maxFails;\n            const maxConns = server.maxConns == 0 ? undefined : server.maxConns;\n            const failTimeout = server.failTimeout == 0 ? undefined : server.failTimeout;\n            const failTimeoutUnit = server.failTimeoutUnit || 's';\n            servers.push({\n                server: server.server,\n                weight: weight,\n                maxFails: maxFails,\n                maxConns: maxConns,\n                failTimeout: failTimeout,\n                failTimeoutUnit: failTimeoutUnit,\n                flag: server.flag,\n            });\n        });\n        formData.value.servers = servers;\n    } else {\n        disableName.value = false;\n        formData.value.name = '';\n        formData.value.algorithm = 'default';\n        formData.value.servers = [\n            {\n                server: '',\n                weight: undefined,\n                maxFails: undefined,\n                maxConns: undefined,\n                failTimeout: undefined,\n                failTimeoutUnit: 's',\n                flag: '',\n            },\n        ];\n    }\n    open.value = true;\n};\n\nconst handleServers = () => {\n    for (const server of formData.value.servers) {\n        if (!server.weight || server.weight == '') {\n            server.weight = 0;\n        }\n        if (!server.maxFails || server.maxFails == '') {\n            server.maxFails = 0;\n        }\n        if (!server.maxConns || server.maxConns == '') {\n            server.maxConns = 0;\n        }\n        if (!server.failTimeout || server.failTimeout == '') {\n            server.failTimeout = 0;\n        }\n    }\n};\n\nconst rollBackServers = () => {\n    for (const server of formData.value.servers) {\n        if (server.weight == 0) {\n            server.weight = undefined;\n        }\n        if (server.maxFails == 0) {\n            server.maxFails = undefined;\n        }\n        if (server.maxConns == 0) {\n            server.maxConns = undefined;\n        }\n        if (server.failTimeout == 0) {\n            server.failTimeout = undefined;\n        }\n    }\n};\n\nconst submit = async () => {\n    try {\n        const valid = await lbFormRef.value?.validate();\n        if (!valid) return;\n\n        if (formData.value.algorithm == 'ip_hash') {\n            for (const server of formData.value.servers) {\n                if (server.flag == 'backup') {\n                    MsgError(i18n.global.t('website.ipHashBackupErr'));\n                    return;\n                }\n            }\n        }\n\n        handleServers();\n        loading.value = true;\n\n        const submitData = {\n            websiteID: websiteID.value,\n            ...formData.value,\n        };\n\n        if (operate.value === 'edit') {\n            await updateLoadBalance(submitData);\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        } else {\n            await createLoadBalance(submitData);\n            MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n        }\n        handleClose();\n    } catch (error) {\n        rollBackServers();\n    } finally {\n        loading.value = false;\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/other/index.vue",
    "content": "<template>\n    <el-row :gutter=\"20\" v-loading=\"loading\">\n        <el-col :xs=\"24\" :sm=\"24\" :md=\"18\" :lg=\"14\" :xl=\"8\">\n            <el-form ref=\"websiteForm\" label-position=\"right\" label-width=\"80px\" :model=\"form\" :rules=\"rules\">\n                <el-form-item :label=\"$t('commons.table.name')\" prop=\"primaryDomain\">\n                    <el-input v-model=\"form.primaryDomain\"></el-input>\n                </el-form-item>\n                <el-form-item :label=\"$t('website.alias')\" prop=\"alias\">\n                    <el-input v-model=\"form.alias\" disabled></el-input>\n                </el-form-item>\n                <GroupSelect\n                    v-model=\"form.webSiteGroupId\"\n                    :prop=\"'webSiteGroupId'\"\n                    :groupType=\"'website'\"\n                ></GroupSelect>\n                <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                    <el-input v-model=\"form.remark\"></el-input>\n                </el-form-item>\n                <el-form-item prop=\"IPV6\">\n                    <el-checkbox v-model=\"form.IPV6\" :label=\"$t('website.ipv6')\" size=\"large\" />\n                </el-form-item>\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"submit(websiteForm)\" :disabled=\"loading\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </el-form-item>\n            </el-form>\n        </el-col>\n    </el-row>\n</template>\n\n<script lang=\"ts\" setup>\nimport GroupSelect from '@/views/website/website/components/group/index.vue';\n\nimport { getWebsite, updateWebsite } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport { computed, onMounted, reactive, ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport i18n from '@/lang';\nimport { MsgError, MsgSuccess } from '@/utils/message';\n\nconst websiteForm = ref<FormInstance>();\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst websiteId = computed(() => {\n    return Number(props.id);\n});\nconst loading = ref(false);\nconst form = reactive({\n    id: websiteId.value,\n    primaryDomain: '',\n    remark: '',\n    webSiteGroupId: 0,\n    IPV6: false,\n    alias: '',\n    favorite: false,\n});\nconst rules = ref({\n    primaryDomain: [Rules.requiredInput, Rules.linuxName],\n    webSiteGroupId: [Rules.requiredSelect],\n});\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        if (form.remark && form.remark.length > 128) {\n            MsgError(i18n.global.t('commons.rule.length128Err'));\n            return;\n        }\n        loading.value = true;\n        updateWebsite(form)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                search();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\nconst search = async () => {\n    getWebsite(websiteId.value).then((res) => {\n        form.primaryDomain = res.data.primaryDomain;\n        form.remark = res.data.remark;\n        form.webSiteGroupId = res.data.webSiteGroupId;\n        form.IPV6 = res.data.IPV6;\n        form.alias = res.data.alias;\n        form.favorite = res.data.favorite;\n    });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/php/composer/index.vue",
    "content": "<template>\n    <el-form label-position=\"right\" label-width=\"100px\">\n        <el-form-item :label=\"$t('website.execParameters')\">\n            <el-select v-model=\"req.command\" class=\"p-w-400\">\n                <el-option label=\"install\" value=\"install\"></el-option>\n                <el-option label=\"update\" value=\"update\"></el-option>\n                <el-option label=\"require\" value=\"require\"></el-option>\n                <el-option label=\"create-project\" value=\"create-project\"></el-option>\n                <el-option :label=\"$t('container.custom')\" value=\"custom\"></el-option>\n            </el-select>\n        </el-form-item>\n        <el-form-item :label=\"$t('website.extCommand')\">\n            <el-input v-model.trim=\"req.extCommand\" class=\"p-w-400\"></el-input>\n        </el-form-item>\n        <el-form-item :label=\"$t('website.mirror')\">\n            <el-select v-model=\"req.mirror\" class=\"p-w-400\">\n                <el-option\n                    v-for=\"mirror in mirrors\"\n                    :key=\"mirror.label\"\n                    :value=\"mirror.value\"\n                    :label=\"mirror.label + ' [' + mirror.value + ']'\"\n                ></el-option>\n            </el-select>\n        </el-form-item>\n        <el-form-item :label=\"$t('website.execUser')\">\n            <el-select v-model=\"req.user\" class=\"p-w-400\">\n                <el-option label=\"www-data\" value=\"www-data\"></el-option>\n                <el-option label=\"root\" value=\"root\"></el-option>\n            </el-select>\n        </el-form-item>\n        <el-form-item :label=\"$t('website.execDir')\">\n            <el-input v-model.trim=\"req.dir\" class=\"p-w-400\">\n                <template #prepend>\n                    <el-button icon=\"Folder\" @click=\"dirRef.acceptParams({ dir: true, path: req.dir })\" />\n                </template>\n            </el-input>\n        </el-form-item>\n        <el-form-item>\n            <el-button type=\"primary\" @click=\"exec\">{{ $t('commons.button.handle') }}</el-button>\n        </el-form-item>\n    </el-form>\n    <TaskLog ref=\"taskLogRef\" @close=\"search\" />\n    <FileList ref=\"dirRef\" :dir=\"true\" @choose=\"getPath\" />\n</template>\n<script setup lang=\"ts\">\nimport { execComposer, getWebsite } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { newUUID } from '@/utils/util';\nimport TaskLog from '@/components/log/task/index.vue';\nimport FileList from '@/components/file-list/index.vue';\n\nconst props = defineProps({\n    websiteID: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst req = reactive({\n    websiteID: 0,\n    command: 'install',\n    extCommand: '',\n    mirror: 'https://mirrors.aliyun.com/composer/',\n    dir: '',\n    user: 'www-data',\n    taskID: '',\n});\nconst loading = ref(false);\nconst taskLogRef = ref();\nconst dirRef = ref();\n\nconst mirrors = [\n    {\n        label: i18n.global.t('runtime.aliyun') + '(mirrors.aliyun.com)',\n        value: 'https://mirrors.aliyun.com/composer/',\n    },\n    {\n        label: i18n.global.t('website.tencentCloud') + '(mirrors.cloud.tencent.com)',\n        value: 'https://mirrors.cloud.tencent.com/composer/',\n    },\n    {\n        label: i18n.global.t('commons.table.default') + '(repo.packagist.org)',\n        value: 'https://repo.packagist.org',\n    },\n    {\n        label: i18n.global.t('website.packagist') + '(packagist.phpcomposer.com)',\n        value: 'https://packagist.phpcomposer.com',\n    },\n    {\n        label: i18n.global.t('website.huaweicloud') + '(mirrors.huaweicloud.com)',\n        value: 'https://mirrors.huaweicloud.com/repository/php',\n    },\n    {\n        label: 'Packagist Mirror' + '(packagist.mirrors.sjtug.sjtu.edu.cn)',\n        value: 'https://packagist.mirrors.sjtug.sjtu.edu.cn/',\n    },\n];\n\nconst getPath = (execDir: string) => {\n    req.dir = execDir;\n};\n\nconst search = () => {\n    loading.value = true;\n    getWebsite(req.websiteID)\n        .then((res) => {\n            req.dir = res.data.sitePath + '/index';\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst exec = async () => {\n    const taskID = newUUID();\n    req.taskID = taskID;\n    try {\n        await execComposer(req);\n        taskLogRef.value.openWithTaskID(taskID);\n    } catch (error) {\n        return;\n    }\n};\n\nonMounted(() => {\n    req.websiteID = props.websiteID;\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/php/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <el-tabs type=\"border-card\" v-model=\"tabIndex\">\n            <el-tab-pane :label=\"$t('website.changeVersion')\">\n                <el-form label-position=\"right\" label-width=\"100px\" v-if=\"tabIndex == '0'\">\n                    <el-form-item v-if=\"website.type === 'static'\">\n                        <el-text type=\"info\">\n                            {{ $t('website.staticChangePHPHelper') }}\n                        </el-text>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.changeVersion')\">\n                        <el-row :gutter=\"20\">\n                            <el-col :span=\"20\">\n                                <el-select v-model=\"versionReq.runtimeID\" class=\"p-w-200\">\n                                    <el-option :key=\"-1\" :label=\"$t('website.static')\" :value=\"0\"></el-option>\n                                    <el-option\n                                        v-for=\"(item, index) in versions\"\n                                        :key=\"index\"\n                                        :label=\"item.label\"\n                                        :value=\"item.value\"\n                                    ></el-option>\n                                </el-select>\n                            </el-col>\n                            <el-col :span=\"4\">\n                                <el-button\n                                    type=\"primary\"\n                                    @click=\"submit()\"\n                                    :disabled=\"versionReq.runtimeID === oldRuntimeID\"\n                                >\n                                    {{ $t('commons.button.save') }}\n                                </el-button>\n                            </el-col>\n                        </el-row>\n                    </el-form-item>\n                </el-form>\n            </el-tab-pane>\n            <el-tab-pane\n                :label=\"$t('website.openBaseDir')\"\n                v-if=\"website.type === 'runtime' && website.runtimeType == 'php'\"\n            >\n                <el-form label-position=\"right\" label-width=\"100px\" v-if=\"tabIndex == '1'\">\n                    <el-form-item :label=\"$t('website.openBaseDir')\">\n                        <el-switch v-model=\"openBaseDir\" @change=\"operateCrossSite\"></el-switch>\n                        <span class=\"input-help\">{{ $t('website.openBaseDirHelper') }}</span>\n                    </el-form-item>\n                </el-form>\n            </el-tab-pane>\n            <el-tab-pane :label=\"'Composer'\" v-if=\"website.type === 'runtime' && website.runtimeType == 'php'\">\n                <Composer :websiteID=\"id\" v-if=\"tabIndex == '2'\" />\n            </el-tab-pane>\n        </el-tabs>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { SearchRuntimes } from '@/api/modules/runtime';\nimport { onMounted, reactive, ref } from 'vue';\nimport { Runtime } from '@/api/interface/runtime';\nimport { Website } from '@/api/interface/website';\nimport { changePHPVersion, getWebsite, operateCrossSiteAccess } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport Composer from './composer/index.vue';\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst runtimeReq = reactive<Runtime.RuntimeReq>({ page: 1, pageSize: 200, type: 'php' });\nconst versionReq = reactive<Website.PHPVersionChange>({\n    websiteID: undefined,\n    runtimeID: undefined,\n});\nconst versions = ref([]);\nconst loading = ref(false);\nconst oldRuntimeID = ref(0);\nconst website = ref({\n    type: '',\n    openBaseDir: false,\n    runtimeType: '',\n});\nconst openBaseDir = ref(false);\nconst tabIndex = ref('0');\n\nconst getRuntimes = async () => {\n    try {\n        loading.value = true;\n        const res = await SearchRuntimes(runtimeReq);\n        const items = res.data.items || [];\n        for (const item of items) {\n            versions.value.push({\n                value: item.id,\n                label: item.name + ' [' + i18n.global.t('app.version') + ':' + item.params['PHP_VERSION'] + ']',\n            });\n        }\n    } catch (error) {}\n    loading.value = false;\n};\n\nconst submit = async () => {\n    try {\n        ElMessageBox.confirm(i18n.global.t('website.changePHPVersionWarn'), i18n.global.t('website.changeVersion'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        }).then(async () => {\n            loading.value = true;\n            try {\n                await changePHPVersion(versionReq);\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                getWebsiteDetail();\n            } catch (error) {}\n            loading.value = false;\n        });\n    } catch (error) {}\n};\n\nconst getWebsiteDetail = async () => {\n    const res = await getWebsite(props.id);\n    versionReq.runtimeID = res.data.runtimeID;\n    oldRuntimeID.value = res.data.runtimeID;\n    website.value = res.data;\n    openBaseDir.value = res.data.openBaseDir || false;\n};\n\nconst operateCrossSite = async () => {\n    try {\n        await operateCrossSiteAccess({\n            websiteID: props.id,\n            operation: openBaseDir.value ? 'Enable' : 'Disable',\n        });\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        getWebsiteDetail();\n    } catch (error) {}\n};\n\nonMounted(() => {\n    versionReq.websiteID = props.id;\n    getWebsiteDetail();\n    getRuntimes();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/proxy/cache/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('website.proxyCache')\" size=\"normal\" @close=\"handleClose\">\n        <el-form\n            v-loading=\"loading\"\n            @submit.prevent\n            ref=\"proxyForm\"\n            label-position=\"top\"\n            :model=\"req\"\n            :rules=\"rules\"\n            :validate-on-rule-change=\"false\"\n        >\n            <el-form-item :label=\"$t('commons.button.start')\" prop=\"open\">\n                <el-switch v-model=\"req.open\" :disabled=\"hasCache\"></el-switch>\n                <span class=\"input-help\" v-if=\"hasCache\">{{ $t('website.cacheWarn') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.cacheLimit')\" prop=\"cacheLimit\">\n                <el-input v-model.number=\"req.cacheLimit\" class=\"p-w-200\">\n                    <template #append>\n                        <el-select v-model=\"req.cacheLimitUnit\" class=\"p-w-100\">\n                            <el-option\n                                v-for=\"(unit, index) in sizeUnits\"\n                                :key=\"index\"\n                                :label=\"unit.label\"\n                                :value=\"unit.value\"\n                            ></el-option>\n                        </el-select>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('website.cacheLimitHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.shareCahe')\" prop=\"shareCache\">\n                <el-input v-model.number=\"req.shareCache\" class=\"p-w-200\">\n                    <template #append>\n                        <el-select v-model=\"req.shareCacheUnit\" class=\"p-w-100\">\n                            <el-option\n                                v-for=\"(unit, index) in sizeUnits\"\n                                :key=\"index\"\n                                :label=\"unit.label\"\n                                :value=\"unit.value\"\n                            ></el-option>\n                        </el-select>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('website.shareCaheHelper') }}</span>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.cacheExpire')\" prop=\"cacheExpire\">\n                <el-input v-model.number=\"req.cacheExpire\" class=\"p-w-200\">\n                    <template #append>\n                        <el-select v-model=\"req.cacheExpireUnit\" class=\"p-w-100\">\n                            <el-option\n                                v-for=\"(unit, index) in Units\"\n                                :key=\"index\"\n                                :label=\"unit.label\"\n                                :value=\"unit.value\"\n                            ></el-option>\n                        </el-select>\n                    </template>\n                </el-input>\n                <span class=\"input-help\">{{ $t('website.cacheExpireJHelper') }}</span>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit(proxyForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { ref } from 'vue';\nimport { Units, sizeUnits } from '@/global/mimetype';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { FormInstance } from 'element-plus';\nimport { getCacheConfig, updateCacheConfig } from '@/api/modules/website';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst proxyForm = ref<FormInstance>();\nconst hasCache = ref(false);\n\nconst req = reactive({\n    open: false,\n    cacheLimit: 1,\n    cacheLimitUnit: 'g',\n    shareCache: 5,\n    shareCacheUnit: 'm',\n    cacheExpire: 24,\n    cacheExpireUnit: 'h',\n    websiteID: 0,\n});\n\nconst rules = {\n    cacheLimit: [Rules.requiredInput, checkNumberRange(0, 9999)],\n    shareCache: [Rules.requiredInput, checkNumberRange(0, 9999)],\n    cacheExpire: [Rules.requiredInput, checkNumberRange(0, 9999)],\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst acceptParams = (websiteID: number, cache: boolean) => {\n    req.websiteID = websiteID;\n    hasCache.value = cache;\n    get();\n    open.value = true;\n};\n\nconst get = async () => {\n    try {\n        const res = await getCacheConfig(req.websiteID);\n        req.open = res.data.open;\n        if (req.open) {\n            req.cacheLimit = res.data.cacheLimit;\n            req.cacheLimitUnit = res.data.cacheLimitUnit;\n            req.shareCache = res.data.shareCache;\n            req.shareCacheUnit = res.data.shareCacheUnit;\n            req.cacheExpire = res.data.cacheExpire;\n            req.cacheExpireUnit = res.data.cacheExpireUnit;\n        }\n    } catch (error) {}\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) {\n            return;\n        }\n        try {\n            await updateCacheConfig(req);\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            handleClose();\n        } catch (error) {}\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/proxy/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.' + proxy.operate)\" @close=\"handleClose\" size=\"normal\">\n        <el-form ref=\"proxyForm\" label-position=\"top\" :model=\"proxy\" :rules=\"rules\" v-loading=\"loading\">\n            <el-tabs v-model=\"activeTab\" type=\"border-card\">\n                <el-tab-pane :label=\"$t('website.proxySettings')\" name=\"basic\">\n                    <el-divider content-position=\"left\">{{ $t('website.proxy') }}</el-divider>\n\n                    <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                        <el-input v-model.trim=\"proxy.name\" :disabled=\"proxy.operate === 'edit'\" />\n                    </el-form-item>\n\n                    <el-row :gutter=\"16\">\n                        <el-col :span=\"12\">\n                            <el-form-item :label=\"$t('website.modifier')\" prop=\"modifier\">\n                                <el-input v-model.trim=\"proxy.modifier\" />\n                                <span class=\"input-help\">{{ $t('website.modifierHelper') }}</span>\n                            </el-form-item>\n                        </el-col>\n                        <el-col :span=\"12\">\n                            <el-form-item :label=\"$t('website.proxyPath')\" prop=\"match\">\n                                <el-input v-model.trim=\"proxy.match\" />\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n\n                    <el-row :gutter=\"16\">\n                        <el-col :span=\"12\">\n                            <el-form-item :label=\"$t('website.proxyPass')\" prop=\"proxyAddress\">\n                                <el-input\n                                    v-model.trim=\"proxy.proxyAddress\"\n                                    :placeholder=\"$t('website.proxyHelper')\"\n                                    @blur=\"getProxyHost\"\n                                >\n                                    <template #prepend>\n                                        <el-select v-model=\"proxy.proxyProtocol\" class=\"!w-24\">\n                                            <el-option label=\"http\" value=\"http://\" />\n                                            <el-option label=\"https\" value=\"https://\" />\n                                        </el-select>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('website.proxyPassHelper') }}</span>\n                            </el-form-item>\n                        </el-col>\n                        <el-col :span=\"12\">\n                            <el-form-item :label=\"$t('website.proxyHost')\" prop=\"proxyHost\">\n                                <el-input v-model.trim=\"proxy.proxyHost\" />\n                                <span class=\"input-help\">{{ $t('website.proxyHostHelper') }}</span>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n\n                    <template v-if=\"proxy.proxyProtocol === 'https://'\">\n                        <el-divider content-position=\"left\">{{ $t('website.sniSettings') }}</el-divider>\n\n                        <div class=\"flex justify-between items-center py-3\">\n                            <div class=\"flex flex-col gap-1\">\n                                <span class=\"font-medium\">{{ $t('website.sni') }}</span>\n                                <span class=\"input-help\">{{ $t('website.sniHelper') }}</span>\n                            </div>\n                            <el-switch v-model=\"proxy.sni\" size=\"large\" @change=\"handleSNIChange\" />\n                        </div>\n\n                        <el-form-item\n                            :label=\"$t('website.proxySslName')\"\n                            prop=\"proxySSLName\"\n                            v-if=\"proxy.sni\"\n                            class=\"mt-4\"\n                        >\n                            <el-input v-model.trim=\"proxy.proxySSLName\" />\n                        </el-form-item>\n                    </template>\n                </el-tab-pane>\n\n                <el-tab-pane :label=\"$t('website.advancedSettings')\" name=\"advanced\">\n                    <el-divider content-position=\"left\">{{ $t('website.cacheSettings') }}</el-divider>\n\n                    <!-- Server Cache Control -->\n                    <div class=\"flex justify-between items-center py-3\">\n                        <div class=\"flex flex-col gap-1\">\n                            <span class=\"font-medium\">{{ $t('website.serverCache') }}</span>\n                        </div>\n                        <el-button-group>\n                            <el-button\n                                :type=\"proxy.cache ? 'primary' : 'default'\"\n                                @click=\"changeServerCache(true)\"\n                                size=\"small\"\n                            >\n                                {{ $t('commons.button.enable') }}\n                            </el-button>\n                            <el-button\n                                :type=\"!proxy.cache ? 'primary' : 'default'\"\n                                @click=\"changeServerCache(false)\"\n                                size=\"small\"\n                            >\n                                {{ $t('commons.button.disable') }}\n                            </el-button>\n                        </el-button-group>\n                    </div>\n\n                    <el-collapse-transition>\n                        <div v-if=\"proxy.cache\" class=\"mt-4 mb-6\">\n                            <el-form-item :label=\"$t('website.serverCacheTime')\" prop=\"serverCacheTime\">\n                                <el-input v-model.number=\"proxy.serverCacheTime\" maxlength=\"15\" class=\"!w-64\">\n                                    <template #append>\n                                        <el-select v-model=\"proxy.serverCacheUnit\" class=\"!w-24\">\n                                            <el-option\n                                                v-for=\"(unit, index) in Units\"\n                                                :key=\"index\"\n                                                :label=\"unit.label\"\n                                                :value=\"unit.value\"\n                                            />\n                                        </el-select>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('website.serverCacheTimeHelper') }}</span>\n                            </el-form-item>\n                        </div>\n                    </el-collapse-transition>\n\n                    <!-- Browser Cache Control -->\n                    <div class=\"flex justify-between items-center py-3\">\n                        <div class=\"flex flex-col gap-1\">\n                            <span class=\"font-medium\">{{ $t('website.browserCache') }}</span>\n                        </div>\n                        <el-button-group>\n                            <el-button\n                                :type=\"proxy.browserCache === 'enable' ? 'primary' : 'default'\"\n                                @click=\"changeBrowserCache('enable')\"\n                                size=\"small\"\n                            >\n                                {{ $t('commons.button.enable') }}\n                            </el-button>\n                            <el-button\n                                :type=\"proxy.browserCache === 'disable' ? 'primary' : 'default'\"\n                                @click=\"changeBrowserCache('disable')\"\n                                size=\"small\"\n                            >\n                                {{ $t('commons.button.disable') }}\n                            </el-button>\n                            <el-button\n                                :type=\"proxy.browserCache === 'noModify' ? 'primary' : 'default'\"\n                                @click=\"changeBrowserCache('noModify')\"\n                                size=\"small\"\n                            >\n                                {{ $t('website.noModify') }}\n                            </el-button>\n                        </el-button-group>\n                    </div>\n\n                    <el-collapse-transition>\n                        <div v-if=\"proxy.browserCache === 'enable'\" class=\"mt-4 mb-6\">\n                            <el-form-item :label=\"$t('website.browserCacheTime')\" prop=\"cacheTime\">\n                                <el-input v-model.number=\"proxy.cacheTime\" maxlength=\"15\" class=\"!w-64\">\n                                    <template #append>\n                                        <el-select v-model=\"proxy.cacheUnit\" class=\"!w-24\">\n                                            <el-option\n                                                v-for=\"(unit, index) in Units\"\n                                                :key=\"index\"\n                                                :label=\"unit.label\"\n                                                :value=\"unit.value\"\n                                            />\n                                        </el-select>\n                                    </template>\n                                </el-input>\n                                <span class=\"input-help\">{{ $t('website.browserCacheTimeHelper') }}</span>\n                            </el-form-item>\n                        </div>\n                    </el-collapse-transition>\n\n                    <el-divider content-position=\"left\">{{ $t('website.cors') }}</el-divider>\n                    <CorsSetting\n                        v-model=\"proxy.cors\"\n                        :config=\"{\n                            allowOrigins: proxy.allowOrigins,\n                            allowMethods: proxy.allowMethods,\n                            allowHeaders: proxy.allowHeaders,\n                            allowCredentials: proxy.allowCredentials,\n                            preflight: proxy.preflight,\n                        }\"\n                        enable-size=\"large\"\n                        @update:config=\"updateCorsConfig\"\n                    ></CorsSetting>\n\n                    <el-divider content-position=\"left\">{{ $t('website.replace') }}</el-divider>\n                    <div>\n                        <div v-for=\"(replace, index) in replaces\" :key=\"index\" class=\"mb-3\">\n                            <el-row :gutter=\"16\">\n                                <el-col :span=\"10\">\n                                    <el-input\n                                        v-model.trim=\"replace.key\"\n                                        :placeholder=\"$t('website.replaced')\"\n                                        size=\"default\"\n                                    ></el-input>\n                                </el-col>\n                                <el-col :span=\"10\">\n                                    <el-input\n                                        v-model.trim=\"replace.value\"\n                                        :placeholder=\"$t('website.replaceText')\"\n                                        size=\"default\"\n                                    ></el-input>\n                                </el-col>\n                                <el-col :span=\"2\">\n                                    <el-button\n                                        @click=\"removeReplace(index)\"\n                                        type=\"danger\"\n                                        link\n                                        size=\"small\"\n                                        class=\"mt-1\"\n                                    >\n                                        <el-icon><Delete /></el-icon>\n                                        {{ $t('commons.button.delete') }}\n                                    </el-button>\n                                </el-col>\n                            </el-row>\n                        </div>\n                        <div class=\"mt-4\">\n                            <el-button type=\"primary\" @click=\"addReplaces\" :icon=\"Plus\">\n                                {{ $t('website.addReplace') }}\n                            </el-button>\n                            <span class=\"input-help mt-4\">{{ $t('website.replaceHelper') }}</span>\n                        </div>\n                    </div>\n                </el-tab-pane>\n            </el-tabs>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit(proxyForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { operateProxyConfig } from '@/api/modules/website';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref, watch } from 'vue';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { Website } from '@/api/interface/website';\nimport { Units } from '@/global/mimetype';\nimport { isDomain } from '@/utils/util';\nimport { Delete, Plus } from '@element-plus/icons-vue';\nimport CorsSetting from '@/views/website/website/cors/index.vue';\n\nconst proxyForm = ref<FormInstance>();\nconst rules = ref({\n    name: [Rules.requiredInput, Rules.appName],\n    match: [Rules.requiredInput],\n    cacheTime: [Rules.requiredInput, checkNumberRange(1, 65535)],\n    proxyPass: [Rules.requiredInput],\n    proxyHost: [Rules.requiredInput],\n    proxyAddress: [Rules.requiredInput],\n    allowOrigins: [Rules.requiredInput],\n});\nconst open = ref(false);\nconst loading = ref(false);\nconst activeTab = ref('basic');\nconst shouldAutoEnableSNI = ref(false);\nconst sniTouched = ref(false);\n\nconst initData = (): Website.ProxyConfig => ({\n    id: 0,\n    operate: 'create',\n    enable: true,\n    cache: false,\n    cacheTime: 0,\n    cacheUnit: '',\n    name: '',\n    modifier: '',\n    match: '/',\n    proxyPass: 'http://127.0.0.1:8080',\n    proxyHost: '$host',\n    filePath: '',\n    replaces: {},\n    proxyAddress: '',\n    proxyProtocol: 'http://',\n    sni: false,\n    proxySSLName: '',\n    serverCacheTime: 10,\n    serverCacheUnit: 'm',\n    browserCache: 'noModify',\n    cors: false,\n    allowOrigins: '*',\n    allowMethods: 'GET,POST,OPTIONS,PUT,DELETE',\n    allowHeaders: '',\n    allowCredentials: false,\n    preflight: true,\n});\nlet proxy = ref(initData());\nconst replaces = ref<any>([]);\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    proxyForm.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = (proxyParam: Website.ProxyConfig) => {\n    replaces.value = [];\n    proxy.value = proxyParam;\n    activeTab.value = 'basic';\n    shouldAutoEnableSNI.value = proxy.value.operate === 'create';\n    sniTouched.value = false;\n\n    // Initialize browserCache based on cacheTime value\n    if (proxy.value.cacheTime > 0) {\n        proxy.value.browserCache = 'enable';\n    } else if (proxy.value.cacheTime === 0) {\n        proxy.value.browserCache = 'noModify';\n    } else {\n        proxy.value.browserCache = 'disable';\n    }\n\n    const res = getProtocolAndHost(proxyParam.proxyPass);\n    if (res != null) {\n        proxy.value.proxyProtocol = res.protocol;\n        proxy.value.proxyAddress = res.host;\n    } else {\n        proxy.value.proxyProtocol = 'http://';\n    }\n\n    open.value = true;\n    if (proxy.value.replaces) {\n        for (const key in proxy.value.replaces) {\n            replaces.value.push({ key: key, value: proxy.value.replaces[key] });\n        }\n    }\n};\n\nconst changeServerCache = (cache: boolean) => {\n    proxy.value.cache = cache;\n    if (cache) {\n        proxy.value.serverCacheTime = 10;\n        proxy.value.serverCacheUnit = 'm';\n    } else {\n        proxy.value.serverCacheTime = 0;\n        proxy.value.serverCacheUnit = '';\n    }\n};\n\nconst changeBrowserCache = (mode: 'enable' | 'disable' | 'noModify') => {\n    proxy.value.browserCache = mode;\n    if (mode === 'enable') {\n        proxy.value.cacheTime = 4;\n        proxy.value.cacheUnit = 'h';\n    } else if (mode === 'disable') {\n        proxy.value.cacheTime = -1;\n        proxy.value.cacheUnit = '';\n    } else {\n        proxy.value.cacheTime = 0;\n        proxy.value.cacheUnit = '';\n    }\n};\n\nconst addReplaces = () => {\n    replaces.value.push({ key: '', value: '' });\n};\n\nconst removeReplace = (index: number) => {\n    replaces.value.splice(index, 1);\n};\n\nconst handleSNIChange = () => {\n    sniTouched.value = true;\n};\n\nconst getProxyHost = () => {\n    if (isDomain(proxy.value.proxyAddress)) {\n        proxy.value.proxyHost = proxy.value.proxyAddress;\n    } else {\n        proxy.value.proxyHost = '$host';\n    }\n};\n\nconst updateCorsConfig = (config: any) => {\n    proxy.value.allowOrigins = config.allowOrigins;\n    proxy.value.allowMethods = config.allowMethods;\n    proxy.value.allowHeaders = config.allowHeaders;\n    proxy.value.allowCredentials = config.allowCredentials;\n    proxy.value.preflight = config.preflight;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n\n        proxy.value.replaces = {};\n        if (replaces.value.length > 0) {\n            let keyMap = new Map();\n            for (const rep of replaces.value) {\n                if (keyMap.get(rep.key) != undefined) {\n                    MsgError(i18n.global.t('website.replacedErr2'));\n                    return;\n                }\n                keyMap.set(rep.key, '');\n                if (rep.key === '') {\n                    MsgError(i18n.global.t('website.replacedErr'));\n                    return;\n                }\n                proxy.value.replaces[rep.key] = rep.value;\n            }\n        }\n        loading.value = true;\n        proxy.value.proxyPass = proxy.value.proxyProtocol + proxy.value.proxyAddress;\n        operateProxyConfig(proxy.value)\n            .then(() => {\n                if (proxy.value.operate == 'create') {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                } else {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                }\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst getProtocolAndHost = (url: string): { protocol: string; host: string } | null => {\n    if (url.startsWith('https://')) {\n        return { protocol: 'https://', host: url.replaceAll('https://', '') };\n    }\n    if (url.startsWith('http://')) {\n        return { protocol: 'http://', host: url.replaceAll('http://', '') };\n    }\n    return { protocol: '', host: url };\n};\n\nwatch(\n    () => proxy.value.proxyProtocol,\n    (protocol) => {\n        if (proxy.value.operate !== 'create' || sniTouched.value || !shouldAutoEnableSNI.value) {\n            return;\n        }\n        proxy.value.sni = protocol === 'https://';\n    },\n);\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/proxy/file/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('website.sourceFile')\"\n        @close=\"handleClose\"\n        :size=\"mobile ? 'full' : 'normal'\"\n    >\n        <CodemirrorPro v-model=\"req.content\" mode=\"nginx\"></CodemirrorPro>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { computed, reactive, ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateProxyConfigFile } from '@/api/modules/website';\nimport { GlobalStore } from '@/store';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nconst globalStore = GlobalStore();\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst proxyForm = ref<FormInstance>();\nconst open = ref(false);\nconst loading = ref(false);\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    proxyForm.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\nconst req = reactive({\n    name: '',\n    websiteID: 0,\n    content: '',\n});\nconst size = ref('50%');\n\nconst acceptParams = async (proxyreq: any) => {\n    req.name = proxyreq.name;\n    req.websiteID = proxyreq.websiteID;\n    req.content = proxyreq.content;\n    open.value = true;\n\n    if (mobile.value) {\n        size.value = '100%';\n    }\n};\n\nconst submit = async () => {\n    loading.value = true;\n    updateProxyConfigFile(req)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            handleClose();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/proxy/index.vue",
    "content": "<template>\n    <ComplexTable :data=\"data\" @search=\"search\" v-loading=\"loading\">\n        <template #toolbar>\n            <el-button type=\"primary\" plain @click=\"openCreate\">{{ $t('commons.button.create') }}</el-button>\n            <el-button @click=\"openCache\">{{ $t('website.proxyCache') }}</el-button>\n            <el-button type=\"primary\" @click=\"clear\" link>\n                {{ $t('nginx.clearProxyCache') }}\n            </el-button>\n        </template>\n        <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\"></el-table-column>\n        <el-table-column :label=\"$t('website.proxyPath')\" prop=\"match\"></el-table-column>\n        <el-table-column :label=\"$t('website.proxyPass')\" prop=\"proxyPass\"></el-table-column>\n        <el-table-column :label=\"$t('website.cache')\" prop=\"cache\">\n            <template #default=\"{ row }\">\n                <el-tag class=\"mr-2\" :type=\"row.cacheTime > 0 ? 'success' : 'info'\" v-if=\"row.cacheTime != 0\">\n                    {{ $t('website.browserCache') + ':' }}\n                    {{ row.cacheTime > 0 ? row.cacheTime + row.cacheUnit : $t('setting.sslDisable') }}\n                </el-tag>\n                <el-tag :type=\"row.serverCacheTime > 0 ? 'success' : 'info'\">\n                    {{ $t('website.serverCache') + ':' }}\n                    {{ row.serverCacheTime > 0 ? row.serverCacheTime + row.serverCacheUnit : $t('setting.sslDisable') }}\n                </el-tag>\n            </template>\n        </el-table-column>\n        <el-table-column :label=\"$t('commons.table.status')\" prop=\"enable\" width=\"100\">\n            <template #default=\"{ row }\">\n                <Status :status=\"row.enable ? 'enable' : 'disable'\" @click=\"opProxy(row)\" :operate=\"true\" />\n            </template>\n        </el-table-column>\n        <fu-table-operations\n            :ellipsis=\"10\"\n            width=\"260px\"\n            :buttons=\"buttons\"\n            :label=\"$t('commons.table.operate')\"\n            :fixed=\"mobile ? false : 'right'\"\n            fix\n        />\n    </ComplexTable>\n\n    <Create ref=\"createRef\" @close=\"search()\" />\n    <File ref=\"fileRef\" @close=\"search()\" />\n    <OpDialog ref=\"opRef\" @search=\"search()\" />\n    <Cache ref=\"cacheRef\" @close=\"search()\" />\n</template>\n\n<script lang=\"ts\" setup name=\"proxy\">\nimport { Website } from '@/api/interface/website';\nimport { getProxyConfig, deleteProxyConfig, updateProxyConfigStatus, clearProxyCache } from '@/api/modules/website';\nimport { computed, onMounted, ref } from 'vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox } from 'element-plus';\nimport { GlobalStore } from '@/store';\nimport Create from './create/index.vue';\nimport File from './file/index.vue';\nimport Cache from './cache/index.vue';\nconst globalStore = GlobalStore();\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst id = computed(() => {\n    return props.id;\n});\nconst loading = ref(false);\nconst data = ref();\nconst createRef = ref();\nconst fileRef = ref();\nconst opRef = ref();\nconst cacheRef = ref();\nconst hasCache = ref(false);\n\nconst buttons = [\n    {\n        label: i18n.global.t('website.sourceFile'),\n        click: function (row: Website.ProxyConfig) {\n            openEditFile(row);\n        },\n        disabled: (row: Website.ProxyConfig) => {\n            return !row.enable;\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Website.ProxyConfig) {\n            openEdit(row);\n        },\n        disabled: (row: Website.ProxyConfig) => {\n            return !row.enable;\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.ProxyConfig) {\n            deleteProxy(row);\n        },\n    },\n];\n\nconst initData = (id: number): Website.ProxyConfig => ({\n    id: id,\n    operate: 'create',\n    enable: true,\n    cache: false,\n    cacheTime: 0,\n    cacheUnit: '',\n    name: '',\n    modifier: '',\n    match: '/',\n    proxyPass: 'http://',\n    proxyHost: '$host',\n    replaces: {},\n    proxySSLName: '',\n    serverCacheTime: 10,\n    serverCacheUnit: 'm',\n    cors: false,\n    allowOrigins: '',\n    allowMethods: '',\n    allowHeaders: '',\n    allowCredentials: false,\n    preflight: false,\n});\n\nconst openCreate = () => {\n    createRef.value.acceptParams(initData(id.value));\n};\n\nconst openCache = () => {\n    cacheRef.value.acceptParams(id.value, hasCache.value);\n};\n\nconst openEdit = (proxyConfig: Website.ProxyConfig) => {\n    let proxy = JSON.parse(JSON.stringify(proxyConfig));\n    proxy.operate = 'edit';\n    if (proxy.replaces == null) {\n        proxy.replaces = {};\n    }\n    createRef.value.acceptParams(proxy);\n};\n\nconst openEditFile = (proxyConfig: Website.ProxyConfig) => {\n    fileRef.value.acceptParams({ name: proxyConfig.name, content: proxyConfig.content, websiteID: proxyConfig.id });\n};\n\nconst deleteProxy = async (proxyConfig: Website.ProxyConfig) => {\n    proxyConfig.operate = 'delete';\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [proxyConfig.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.proxy'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: deleteProxyConfig,\n        params: {\n            id: proxyConfig.id,\n            name: proxyConfig.name,\n        },\n    });\n};\n\nconst submit = async (proxyConfig: Website.ProxyStatusUpdate) => {\n    loading.value = true;\n    updateProxyConfigStatus(proxyConfig)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst opProxy = (proxyConfig: Website.ProxyConfig) => {\n    const enable = !proxyConfig.enable;\n    let status = '';\n    let message = '';\n    if (enable) {\n        status = 'enable';\n        message = i18n.global.t('website.startProxy');\n    } else {\n        status = 'disable';\n        message = i18n.global.t('website.stopProxy');\n    }\n    ElMessageBox.confirm(message, i18n.global.t('cronjob.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    })\n        .then(async () => {\n            await submit({\n                id: proxyConfig.id,\n                name: proxyConfig.name,\n                status,\n            });\n            search();\n        })\n        .catch(() => {});\n};\n\nconst search = async () => {\n    try {\n        loading.value = true;\n        const res = await getProxyConfig({ id: id.value });\n        data.value = res.data || [];\n        hasCache.value = data.value.some((item: Website.ProxyConfig) => item.cache);\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst clear = () => {\n    ElMessageBox.confirm(i18n.global.t('nginx.clearProxyCacheWarn'), i18n.global.t('nginx.clearProxyCache'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await clearProxyCache({ websiteID: id.value });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/real-ip/index.vue",
    "content": "<template>\n    <el-row :gutter=\"20\" v-loading=\"loading\">\n        <el-col :xs=\"24\" :sm=\"14\" :md=\"14\" :lg=\"10\" :xl=\"8\">\n            <el-alert :closable=\"false\">\n                {{ $t('website.ipFromHelper') }}\n                <div>\n                    {{ $t('website.ipFromExample1') }}\n                </div>\n                <div>\n                    {{ $t('website.ipFromExample2') }}\n                </div>\n                <div>\n                    {{ $t('website.ipFromExample3') }}\n                </div>\n            </el-alert>\n            <el-form\n                v-loading=\"loading\"\n                @submit.prevent\n                ref=\"realIPForm\"\n                label-position=\"right\"\n                label-width=\"100px\"\n                :model=\"req\"\n                :rules=\"rules\"\n                :validate-on-rule-change=\"false\"\n            >\n                <el-form-item :label=\"$t('commons.button.start')\" prop=\"open\">\n                    <el-switch v-model=\"req.open\"></el-switch>\n                </el-form-item>\n                <div v-if=\"req.open\">\n                    <el-form-item :label=\"$t('website.ipFrom')\" prop=\"ipFrom\">\n                        <el-input\n                            type=\"textarea\"\n                            :rows=\"10\"\n                            clearable\n                            v-model=\"req.ipFrom\"\n                            :placeholder=\"$t('website.wafInputHelper')\"\n                        ></el-input>\n                        <span class=\"input-help\">\n                            {{ $t('website.wafInputHelper') }}\n                        </span>\n                    </el-form-item>\n                    <el-form-item label=\"IP Header\" prop=\"ipHeader\">\n                        <el-select v-model=\"req.ipHeader\">\n                            <el-option :label=\"$t('website.other')\" key=\"other\" value=\"other\"></el-option>\n                            <el-option\n                                v-for=\"item in ['X-Forwarded-For', 'X-Real-IP', 'CF-Connecting-IP']\"\n                                :key=\"item\"\n                                :label=\"item\"\n                                :value=\"item\"\n                            />\n                        </el-select>\n                    </el-form-item>\n\n                    <el-form-item prop=\"ipOther\" v-if=\"req.ipHeader === 'other'\">\n                        <el-input v-model.trim=\"req.ipOther\" />\n                    </el-form-item>\n                </div>\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"submit(realIPForm)\" :loading=\"loading\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </el-form-item>\n            </el-form>\n        </el-col>\n    </el-row>\n</template>\n\n<script setup lang=\"ts\">\nimport { getRealIPConfig, updateRealIPConfig } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\n\nconst loading = ref(false);\nconst realIPForm = ref<FormInstance>();\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst req = reactive({\n    websiteID: 0,\n    open: false,\n    ipFrom: '127.0.0.1',\n    ipHeader: 'X-Real-IP',\n    ipOther: '',\n});\nconst rules = {\n    ipFrom: [Rules.requiredInput],\n    ipHeader: [Rules.requiredSelect],\n    ipOther: [Rules.requiredInput],\n};\n\nconst get = () => {\n    getRealIPConfig(props.id).then((res) => {\n        req.open = res.data.open;\n        if (res.data.open) {\n            req.ipFrom = res.data.ipFrom;\n            req.ipHeader = res.data.ipHeader;\n            req.ipOther = res.data.ipOther;\n        }\n    });\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) {\n            return;\n        }\n        req.websiteID = props.id;\n        try {\n            await updateRealIPConfig(req);\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        } catch (error) {}\n    });\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/redirect/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.' + redirect.operate)\" @close=\"handleClose\">\n        <el-form ref=\"redirectForm\" label-position=\"top\" :model=\"redirect\" :rules=\"rules\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input\n                    v-model.trim=\"redirect.name\"\n                    :disabled=\"redirect.operate === 'edit' || redirect.type == '404'\"\n                ></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('commons.table.type')\" prop=\"type\">\n                <el-select\n                    v-model=\"redirect.type\"\n                    @change=\"changeType(redirect.type)\"\n                    :disabled=\"redirect.operate === 'edit'\"\n                >\n                    <el-option :label=\"$t('website.domain')\" :value=\"'domain'\"></el-option>\n                    <el-option :label=\"$t('website.path')\" :value=\"'path'\"></el-option>\n                    <el-option :label=\"'404'\" :value=\"'404'\"></el-option>\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.redirectWay')\" prop=\"redirect\">\n                <el-select v-model=\"redirect.redirect\">\n                    <el-option :label=\"'301'\" :value=\"'301'\"></el-option>\n                    <el-option :label=\"'302'\" :value=\"'302'\"></el-option>\n                </el-select>\n                <span class=\"input-help\">\n                    {{ $t('website.redirectHelper') }}\n                </span>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.path')\" prop=\"path\" v-if=\"redirect.type == 'path'\">\n                <el-input v-model.trim=\"redirect.path\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.domain')\" prop=\"domains\" v-if=\"redirect.type == 'domain'\">\n                <el-select v-model=\"redirect.domains\" multiple>\n                    <el-option\n                        v-for=\"(item, index) in domains\"\n                        :key=\"index\"\n                        :value=\"item.domain\"\n                        :label=\"item.domain\"\n                    />\n                </el-select>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.redirectRoot')\" prop=\"redirectRoot\" v-if=\"redirect.type == '404'\">\n                <el-switch v-model=\"redirect.redirectRoot\"></el-switch>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.targetURL')\" prop=\"target\" v-if=\"!redirect.redirectRoot\">\n                <el-input v-model.trim=\"redirect.target\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('website.keepPath')\" prop=\"keepPath\" v-if=\"redirect.type !== '404'\">\n                <el-switch v-model=\"redirect.keepPath\"></el-switch>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit(redirectForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { listDomains, operateRedirectConfig, getRedirectConfig } from '@/api/modules/website';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { Website } from '@/api/interface/website';\n\nconst redirectForm = ref<FormInstance>();\nconst rules = ref({\n    name: [Rules.requiredInput, Rules.appName],\n    type: [Rules.requiredSelect],\n    redirect: [Rules.requiredSelect],\n    domains: [Rules.requiredSelect],\n    target: [Rules.requiredInput],\n    path: [Rules.requiredInput],\n});\nconst open = ref(false);\nconst loading = ref(false);\n\nconst initData = (): Website.RedirectConfig => ({\n    websiteID: 0,\n    operate: 'create',\n    enable: true,\n    name: '',\n    domains: [],\n    keepPath: true,\n    type: 'domain',\n    redirect: '301',\n    target: 'http://',\n    redirectRoot: false,\n});\nlet redirect = ref(initData());\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    redirectForm.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\nconst domains = ref([]);\n\nconst acceptParams = (redirectParam: Website.RedirectConfig) => {\n    if (redirectParam.operate == 'edit') {\n        redirect.value = redirectParam;\n    } else {\n        redirect.value = initData();\n        redirect.value.websiteID = redirectParam.websiteID;\n    }\n    domains.value = [];\n    getDomains();\n    open.value = true;\n};\n\nconst changeType = (type: string) => {\n    redirectForm.value?.clearValidate('name');\n    if (type != '404') {\n        redirect.value.name = '';\n        redirect.value.redirectRoot = false;\n    } else {\n        redirect.value.name = '404';\n    }\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        operateRedirectConfig(redirect.value)\n            .then(() => {\n                if (redirect.value.operate == 'create') {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                } else {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                }\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst getDomains = async () => {\n    try {\n        loading.value = true;\n        const res = await getRedirectConfig({ websiteID: redirect.value.websiteID });\n        let oldDomains = [];\n        if (res.data) {\n            for (const old of res.data) {\n                if (old.type == 'domain') {\n                    oldDomains = oldDomains.concat(old.domains);\n                }\n            }\n        }\n        listDomains(redirect.value.websiteID)\n            .then((domainRes) => {\n                if (domainRes.data) {\n                    if (oldDomains.length > 0) {\n                        for (const data of domainRes.data) {\n                            if (oldDomains.indexOf(data.domain) > -1) {\n                                continue;\n                            }\n                            domains.value.push(data);\n                        }\n                    } else {\n                        domains.value = domainRes.data || [];\n                    }\n                }\n            })\n            .finally(() => {});\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/redirect/file/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('website.sourceFile')\" @close=\"handleClose\" size=\"normal\">\n        <CodemirrorPro v-model=\"req.content\" mode=\"nginx\"></CodemirrorPro>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { reactive, ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { updateRedirectConfigFile } from '@/api/modules/website';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\n\nconst proxyForm = ref<FormInstance>();\nconst open = ref(false);\nconst loading = ref(false);\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    proxyForm.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\nconst req = reactive({\n    name: '',\n    websiteID: 0,\n    content: '',\n});\n\nconst acceptParams = async (proxyreq: any) => {\n    req.name = proxyreq.name;\n    req.websiteID = proxyreq.websiteID;\n    req.content = proxyreq.content;\n    open.value = true;\n};\n\nconst submit = async () => {\n    loading.value = true;\n    updateRedirectConfigFile(req)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            handleClose();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/redirect/index.vue",
    "content": "<template>\n    <ComplexTable :data=\"data\" @search=\"search\" v-loading=\"loading\">\n        <template #toolbar>\n            <el-button type=\"primary\" plain @click=\"openCreate\">\n                {{ $t('commons.button.create') }}\n            </el-button>\n        </template>\n        <el-table-column :label=\"$t('commons.table.name')\" prop=\"name\" min-width=\"60px\" show-overflow-tooltip />\n        <el-table-column :label=\"$t('website.sourceDomain')\" prop=\"domain\" min-width=\"80px\" show-overflow-tooltip>\n            <template #default=\"{ row }\">\n                <span v-if=\"row.type === 'domain'\">{{ row.domains.join(',') }}</span>\n                <span v-else>{{ row.path }}</span>\n            </template>\n        </el-table-column>\n        <el-table-column :label=\"$t('commons.table.type')\" prop=\"type\" min-width=\"60px\">\n            <template #default=\"{ row }\">\n                <span v-if=\"row.type && row.type != 404\">{{ $t('website.' + row.type) }}</span>\n                <span v-else>{{ 404 }}</span>\n            </template>\n        </el-table-column>\n        <el-table-column :label=\"$t('website.redirectWay')\" prop=\"redirect\" min-width=\"50px\"></el-table-column>\n        <el-table-column :label=\"$t('website.targetURL')\" prop=\"target\" min-width=\"100px\" show-overflow-tooltip />\n        <el-table-column :label=\"$t('website.keepPath')\" prop=\"keepPath\" min-width=\"80px\" show-overflow-tooltip>\n            <template #default=\"{ row }\">\n                <span v-if=\"row.type != '404'\">\n                    {{ row.keepPath ? $t('website.keep') : $t('website.notKeep') }}\n                </span>\n                <span v-else></span>\n            </template>\n        </el-table-column>\n        <el-table-column :label=\"$t('commons.table.status')\" prop=\"enable\" min-width=\"50px\">\n            <template #default=\"{ row }\">\n                <Status :status=\"row.enable ? 'enable' : 'disable'\" @click=\"opProxy(row)\" />\n            </template>\n        </el-table-column>\n        <fu-table-operations\n            :ellipsis=\"10\"\n            width=\"180px\"\n            :buttons=\"buttons\"\n            :label=\"$t('commons.table.operate')\"\n            :fixed=\"mobile ? false : 'right'\"\n            fix\n        />\n    </ComplexTable>\n\n    <Create ref=\"createRef\" @close=\"search()\" />\n    <File ref=\"fileRef\" @close=\"search()\" />\n    <OpDialog ref=\"opRef\" @search=\"search()\" />\n</template>\n\n<script lang=\"ts\" setup name=\"proxy\">\nimport { Website } from '@/api/interface/website';\nimport { operateRedirectConfig, getRedirectConfig } from '@/api/modules/website';\nimport { computed, onMounted, ref } from 'vue';\nimport Create from './create/index.vue';\nimport File from './file/index.vue';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { ElMessageBox } from 'element-plus';\nimport { GlobalStore } from '@/store';\nconst globalStore = GlobalStore();\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\nconst id = computed(() => {\n    return props.id;\n});\nconst loading = ref(false);\nconst data = ref();\nconst createRef = ref();\nconst fileRef = ref();\nconst opRef = ref();\n\nconst buttons = [\n    {\n        label: i18n.global.t('website.sourceFile'),\n        click: function (row: Website.RedirectConfig) {\n            openEditFile(row);\n        },\n        disabled: (row: Website.RedirectConfig) => {\n            return !row.enable;\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Website.RedirectConfig) {\n            openEdit(row);\n        },\n        disabled: (row: Website.ProxyConfig) => {\n            return !row.enable;\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.RedirectConfig) {\n            deleteProxy(row);\n        },\n    },\n];\n\nconst initData = (id: number): Website.RedirectConfig => ({\n    websiteID: id,\n    operate: 'create',\n    enable: true,\n    name: '',\n    domains: [],\n    keepPath: true,\n    type: '',\n    redirect: '',\n    target: '',\n});\n\nconst openCreate = () => {\n    createRef.value.acceptParams(initData(id.value));\n};\n\nconst openEdit = (proxyConfig: Website.RedirectConfig) => {\n    let proxy = JSON.parse(JSON.stringify(proxyConfig));\n    proxy.operate = 'edit';\n    createRef.value.acceptParams(proxy);\n};\n\nconst openEditFile = (proxyConfig: Website.RedirectConfig) => {\n    fileRef.value.acceptParams({\n        name: proxyConfig.name,\n        content: proxyConfig.content,\n        websiteID: proxyConfig.websiteID,\n    });\n};\n\nconst deleteProxy = async (redirectConfig: Website.RedirectConfig) => {\n    redirectConfig.operate = 'delete';\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [redirectConfig.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('website.redirect'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: operateRedirectConfig,\n        params: redirectConfig,\n    });\n};\n\nconst submit = async (redirectConfig: Website.RedirectConfig) => {\n    loading.value = true;\n    await operateRedirectConfig(redirectConfig)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst opProxy = (redirectConfig: Website.RedirectConfig) => {\n    let proxy = JSON.parse(JSON.stringify(redirectConfig));\n    proxy.enable = !redirectConfig.enable;\n    let message = '';\n    if (proxy.enable) {\n        proxy.operate = 'enable';\n        message = i18n.global.t('commons.button.start') + i18n.global.t('website.redirect');\n    } else {\n        proxy.operate = 'disable';\n        message = i18n.global.t('commons.button.stop') + i18n.global.t('website.redirect');\n    }\n    ElMessageBox.confirm(message, i18n.global.t('cronjob.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    })\n        .then(async () => {\n            await submit(proxy);\n        })\n        .catch(() => {});\n};\n\nconst search = async () => {\n    try {\n        loading.value = true;\n        const res = await getRedirectConfig({ websiteID: id.value });\n        data.value = res.data || [];\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/resource/index.vue",
    "content": "<template>\n    <div class=\"p-w-400\">\n        <el-descriptions border :column=\"1\">\n            <div v-for=\"(resource, index) of data\" :key=\"index\">\n                <el-descriptions-item :label=\"$t('menu.' + resource.type)\">{{ resource.name }}</el-descriptions-item>\n            </div>\n        </el-descriptions>\n        <el-form\n            ref=\"changeForm\"\n            :model=\"req\"\n            label-position=\"left\"\n            label-width=\"90px\"\n            class=\"mt-5\"\n            v-if=\"website.type === 'static' || website.type === 'runtime'\"\n        >\n            <el-form-item :label=\"$t('website.changeDatabase')\" prop=\"db\">\n                <el-select v-model=\"req.db\" class=\"w-full\" @change=\"changeDB\">\n                    <el-option :label=\"$t('website.donotLinkeDB')\" :value=\"0\"></el-option>\n                    <el-option\n                        v-for=\"(item, index) in databases\"\n                        :key=\"index\"\n                        :label=\"item.name\"\n                        :value=\"item.id + item.type\"\n                    >\n                        <div class=\"flex justify-between items-center\">\n                            <span>{{ item.name }}</span>\n                            <div>\n                                <el-tag>{{ item.databaseName }}</el-tag>\n                                <el-tag class=\"ml-1\">\n                                    {{ item.from === 'local' ? $t('commons.table.local') : $t('database.remote') }}\n                                </el-tag>\n                            </div>\n                        </div>\n                    </el-option>\n                </el-select>\n                <el-text type=\"warning\">{{ $t('website.changeDatabaseHelper1') }}</el-text>\n                <el-text type=\"warning\">{{ $t('website.changeDatabaseHelper2') }}</el-text>\n            </el-form-item>\n            <el-form-item>\n                <el-button type=\"primary\" @click=\"submit()\">\n                    {{ $t('commons.button.save') }}\n                </el-button>\n            </el-form-item>\n        </el-form>\n    </div>\n</template>\n<script setup lang=\"ts\">\nimport { changeDatabase, getWebsite, getWebsiteDatabase, getWebsiteResource } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst data = ref([]);\nconst req = reactive({\n    websiteID: props.id,\n    databaseID: 0,\n    databaseType: '',\n    db: '',\n});\nconst databases = ref([]);\nconst website = ref({\n    type: '',\n    dbID: 0,\n    dbType: '',\n});\n\nconst search = async () => {\n    try {\n        const res = await getWebsiteResource(props.id);\n        data.value = res.data;\n    } catch (error) {}\n};\n\nconst listDatabases = async () => {\n    try {\n        const res = await getWebsiteDatabase();\n        databases.value = res.data;\n        if (databases.value.length > 0 && website.value.dbID > 0) {\n            for (let i = 0; i < databases.value.length; i++) {\n                if (databases.value[i].id === website.value.dbID && databases.value[i].type === website.value.dbType) {\n                    req.db = databases.value[i].id + databases.value[i].type;\n                    break;\n                }\n            }\n        }\n    } catch (error) {}\n};\n\nconst changeDB = () => {\n    for (let i = 0; i < databases.value.length; i++) {\n        if (databases.value[i].id + databases.value[i].type === req.db) {\n            req.databaseID = databases.value[i].id;\n            req.databaseType = databases.value[i].type;\n            break;\n        }\n    }\n};\n\nconst submit = async () => {\n    try {\n        await changeDatabase(req);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        search();\n    } catch (error) {}\n};\n\nconst get = async () => {\n    try {\n        const res = await getWebsite(props.id);\n        website.value = res.data;\n        req.db = '';\n        search();\n        listDatabases();\n    } catch (error) {}\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/rewrite/custom/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('website.saveCustom')\" @close=\"handleClose\">\n        <el-form ref=\"rewriteForm\" label-position=\"top\" :model=\"req\" :rules=\"rules\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model=\"req.name\"></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit(rewriteForm)\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { operateCustomRewrite } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { Rules } from '@/global/form-rules';\n\nconst rewriteForm = ref<FormInstance>();\nconst open = ref(false);\nconst loading = ref(false);\nconst req = ref({\n    name: '',\n    operate: 'create',\n    content: '',\n});\nconst rules = ref({\n    name: [Rules.requiredInput],\n});\n\nconst em = defineEmits(['close']);\nconst handleClose = () => {\n    rewriteForm.value?.resetFields();\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = async (conetnt: string) => {\n    req.value.content = conetnt;\n    open.value = true;\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        operateCustomRewrite(req.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/rewrite/index.vue",
    "content": "<template>\n    <div>\n        <el-form-item :label=\"$t('website.rewriteMode')\">\n            <el-select v-model=\"req.name\" filterable @change=\"getRewrite(req.name)\" class=\"p-w-200\">\n                <el-option :label=\"$t('website.current')\" :value=\"'current'\"></el-option>\n                <el-option\n                    v-for=\"(rewrite, index) in rewrites\"\n                    :key=\"index\"\n                    :label=\"rewrite.name\"\n                    :value=\"rewrite.name\"\n                >\n                    <span>{{ rewrite.name }}</span>\n                    <el-button\n                        class=\"float-right mt-1.5\"\n                        v-if=\"rewrite.resource == 'custom'\"\n                        link\n                        icon=\"Close\"\n                        @click=\"deleteCustomRewrite(rewrite.name)\"\n                    ></el-button>\n                </el-option>\n            </el-select>\n        </el-form-item>\n        <el-text type=\"warning\">{{ $t('website.rewriteHelper2') }}</el-text>\n        <CodemirrorPro v-model=\"content\" mode=\"nginx\" :heightDiff=\"500\"></CodemirrorPro>\n        <div class=\"mt-2\">\n            <el-form-item>\n                <el-alert :title=\"$t('website.rewriteHelper')\" type=\"info\" :closable=\"false\" />\n            </el-form-item>\n            <el-button type=\"primary\" @click=\"submit()\">\n                {{ $t('nginx.saveAndReload') }}\n            </el-button>\n            <el-button type=\"primary\" @click=\"opCustomRewrite()\" :disabled=\"content == ''\">\n                {{ $t('website.saveCustom') }}\n            </el-button>\n        </div>\n        <CustomRewrite ref=\"customRef\" @close=\"init()\" />\n        <OpDialog ref=\"deleteRef\" @search=\"init()\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, nextTick, onMounted, reactive, ref } from 'vue';\nimport {\n    getWebsite,\n    getRewriteConfig,\n    updateRewriteConfig,\n    listCustomRewrite,\n    operateCustomRewrite,\n} from '@/api/modules/website';\nimport { Rewrites } from '@/global/mimetype';\nimport { MsgSuccess } from '@/utils/message';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\nimport i18n from '@/lang';\nimport CustomRewrite from '@/views/website/website/config/basic/rewrite/custom/index.vue';\n\nconst loading = ref(false);\nconst content = ref(' ');\nconst codeRef = ref();\nconst customRef = ref();\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst id = computed(() => {\n    return props.id;\n});\nconst req = reactive({\n    websiteID: id.value,\n    name: 'default',\n});\nconst update = reactive({\n    websiteID: id.value,\n    content: 'd',\n    name: '',\n});\nconst rewrites = ref([]);\nconst deleteRef = ref();\n\nconst getRewrite = async (rewrite: string) => {\n    loading.value = true;\n    req.name = rewrite;\n    req.websiteID = id.value;\n    try {\n        const res = await getRewriteConfig(req);\n        content.value = res.data.content;\n        if (res.data.content == '') {\n            content.value = ' ';\n        }\n        setCursorPosition();\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst setCursorPosition = () => {\n    nextTick(() => {\n        const codeMirrorInstance = codeRef.value?.codemirror;\n        codeMirrorInstance?.setCursor(0, 0);\n    });\n};\n\nconst submit = async () => {\n    update.name = req.name;\n    update.websiteID = id.value;\n    update.content = content.value;\n    loading.value = true;\n    try {\n        await updateRewriteConfig(update);\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\n\nconst opCustomRewrite = async () => {\n    customRef.value.acceptParams(content.value);\n};\n\nconst deleteCustomRewrite = (name: string) => {\n    deleteRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('container.template'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: operateCustomRewrite,\n        params: { name: name, operate: 'delete' },\n    });\n};\n\nconst init = () => {\n    listCustomRewrite().then((res) => {\n        rewrites.value = [];\n        if (res && res.data) {\n            for (const d of res.data) {\n                rewrites.value.push({\n                    resource: 'custom',\n                    name: d,\n                });\n            }\n        }\n        for (const r of Rewrites) {\n            rewrites.value.push({\n                resource: 'default',\n                name: r,\n            });\n        }\n    });\n    getWebsite(id.value).then((res) => {\n        const name = res.data.rewrite == '' ? 'default' : 'current';\n        if (name === 'current') {\n            req.name = 'current';\n        }\n        getRewrite(name);\n    });\n};\n\nonMounted(() => {\n    init();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/site-folder/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <div class=\"site-form-wrapper\">\n            <el-form class=\"site-form moblie-form\" ref=\"siteForm\" :model=\"update\" label-width=\"100px\">\n                <el-form-item :label=\"$t('website.siteAlias')\">\n                    {{ website.alias }}\n                </el-form-item>\n                <el-form-item :label=\"$t('website.primaryPath')\">\n                    <el-space wrap>\n                        {{ website.sitePath + '/index' }}\n                        <el-button type=\"primary\" link @click=\"routerToFileWithPath(website.sitePath + '/index')\">\n                            <el-icon>\n                                <FolderOpened />\n                            </el-icon>\n                        </el-button>\n                    </el-space>\n                    <span class=\"input-help\" v-if=\"configDir\">\n                        {{ $t('php.indexHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item v-if=\"configDir\" :label=\"$t('website.runDir')\">\n                    <el-space wrap>\n                        <el-select v-model=\"update.siteDir\" filterable class=\"p-w-200\">\n                            <el-option\n                                v-for=\"(item, index) in dirs\"\n                                :label=\"item\"\n                                :value=\"item\"\n                                :key=\"index\"\n                            ></el-option>\n                        </el-select>\n                        <el-button type=\"primary\" @click=\"submit(siteForm)\">\n                            {{ $t('nginx.saveAndReload') }}\n                        </el-button>\n                    </el-space>\n                    <span class=\"input-help\">\n                        {{ $t('website.runDirHelper2') }}\n                    </span>\n                </el-form-item>\n                <el-form-item v-if=\"configDir\" :label=\"$t('website.userGroup')\">\n                    <el-space wrap>\n                        <el-input v-model=\"updatePermission.user\" class=\"user-num-input\">\n                            <template #prepend>{{ $t('commons.table.user') }}</template>\n                        </el-input>\n                        <el-input v-model=\"updatePermission.group\" class=\"user-num-input\">\n                            <template #prepend>{{ $t('website.uGroup') }}</template>\n                        </el-input>\n                        <el-button type=\"primary\" @click=\"submitPermission()\">\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                    </el-space>\n                </el-form-item>\n            </el-form>\n            <el-text type=\"warning\" v-if=\"configDir\">{{ $t('website.runUserHelper') }}</el-text>\n            <br />\n            <el-text type=\"danger\" v-if=\"dirConfig.msg != ''\">{{ dirConfig.msg }}</el-text>\n            <br />\n            <el-descriptions :title=\"$t('website.folderTitle')\" :column=\"1\" border>\n                <el-descriptions-item label=\"ssl\">{{ $t('website.sslFolder') }}</el-descriptions-item>\n                <el-descriptions-item label=\"log\">{{ $t('logs.websiteLog') }}</el-descriptions-item>\n                <el-descriptions-item label=\"index\">{{ $t('website.indexFolder') }}</el-descriptions-item>\n            </el-descriptions>\n        </div>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { getDirConfig, getWebsite, updateWebsiteDir, updateWebsiteDirPermission } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { routerToFileWithPath } from '@/utils/router';\nimport { FormInstance } from 'element-plus';\nimport { computed, onMounted, reactive, ref } from 'vue';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst websiteId = computed(() => {\n    return Number(props.id);\n});\nconst website = ref<any>({});\nconst loading = ref(false);\nconst configDir = ref(false);\nconst update = reactive({\n    id: 0,\n    siteDir: '/',\n});\nconst updatePermission = reactive({\n    id: 0,\n    user: '1000',\n    group: '1000',\n});\nconst siteForm = ref<FormInstance>();\nconst dirs = ref([]);\nconst dirConfig = ref<Website.DirConfig>({\n    dirs: [''],\n    user: '',\n    userGroup: '',\n    msg: '',\n});\n\nconst search = () => {\n    loading.value = true;\n    getWebsite(websiteId.value)\n        .then((res) => {\n            website.value = res.data;\n            update.id = website.value.id;\n            update.siteDir = website.value.siteDir.startsWith('/')\n                ? website.value.siteDir\n                : '/' + website.value.siteDir;\n            updatePermission.id = website.value.id;\n            updatePermission.group = website.value.group === '' ? '1000' : website.value.group;\n            updatePermission.user = website.value.user === '' ? '1000' : website.value.user;\n            if ((website.value.type === 'static' || website.value.runtimeID > 0) && website.value.type != 'subsite') {\n                configDir.value = true;\n                getConfig();\n            }\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        updateWebsiteDir(update)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                search();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nconst submitPermission = async () => {\n    if (updatePermission.user === '' || updatePermission.group === '') {\n        return;\n    }\n    loading.value = true;\n    updateWebsiteDirPermission(updatePermission)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            search();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst initData = () => {\n    dirs.value = [];\n};\n\nconst getConfig = async () => {\n    try {\n        const res = await getDirConfig({ id: props.id });\n        dirs.value = res.data.dirs;\n        dirConfig.value = res.data;\n    } catch (error) {}\n};\n\nonMounted(() => {\n    initData();\n    search();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.site-form-wrapper {\n    min-width: 600px;\n    width: 60%;\n    padding: 20px;\n}\n.site-form {\n    :deep(.el-form-item__label) {\n        padding-right: 20px !important;\n        box-sizing: content-box;\n    }\n    .user-num-input {\n        width: 190px;\n    }\n}\n.warnHelper {\n    white-space: pre-line;\n    display: block;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/basic/stream/index.vue",
    "content": "<template>\n    <div>\n        <el-form ref=\"streamFormRef\" :model=\"form\" label-width=\"120px\" label-position=\"top\">\n            <el-row v-loading=\"loading\">\n                <el-col :span=\"12\" :offset=\"1\">\n                    <el-form-item :label=\"$t('website.streamPorts')\" :rules=\"Rules.requiredInput\">\n                        <el-input v-model=\"form.streamPorts\" />\n                    </el-form-item>\n                    <el-form-item prop=\"udp\">\n                        <el-checkbox v-model=\"form.udp\" :label=\"$t('website.udp')\" size=\"large\" />\n                    </el-form-item>\n                    <LoadBalanceForm ref=\"lbFormRef\" v-model=\"form\" :disabled=\"true\" />\n                    <el-form-item>\n                        <el-button type=\"primary\" @click=\"submit()\" class=\"mt-2\">\n                            {{ $t('commons.button.save') }}\n                        </el-button>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n        </el-form>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport LoadBalanceForm from '@/views/website/website/config/basic/load-balance/form/index.vue';\n\nimport { getWebsite, updateWebsiteStream } from '@/api/modules/website';\nimport { ref, onMounted } from 'vue';\nimport { Rules } from '@/global/form-rules';\nimport { MsgSuccess } from '@/utils/message';\nimport i18n from '@/lang';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst form = ref({\n    streamPorts: '',\n    servers: [],\n    name: '',\n    algorithm: '',\n    websiteID: props.id,\n    udp: false,\n});\nconst streamFormRef = ref();\nconst lbFormRef = ref();\nconst loading = ref(false);\n\nconst submit = async () => {\n    try {\n        loading.value = true;\n        const formValid = await streamFormRef.value?.validate();\n        if (!formValid) return;\n        const lbValid = await lbFormRef.value?.validate();\n        if (!lbValid) return;\n        await updateWebsiteStream(form.value);\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n    } finally {\n        loading.value = false;\n    }\n};\n\nonMounted(async () => {\n    const res = await getWebsite(props.id);\n    form.value.streamPorts = res.data.streamPorts;\n    form.value.name = res.data.primaryDomain;\n    form.value.algorithm = res.data.algorithm == '' ? 'default' : res.data.algorithm;\n    form.value.streamPorts = res.data.streamPorts;\n    form.value.udp = res.data.udp;\n\n    let servers = [];\n    res.data?.servers?.forEach((server) => {\n        const weight = server.weight == 0 ? undefined : server.weight;\n        const maxFails = server.maxFails == 0 ? undefined : server.maxFails;\n        const maxConns = server.maxConns == 0 ? undefined : server.maxConns;\n        const failTimeout = server.failTimeout == 0 ? undefined : server.failTimeout;\n        const failTimeoutUnit = server.failTimeoutUnit || 's';\n        servers.push({\n            server: server.server,\n            weight: weight,\n            maxFails: maxFails,\n            maxConns: maxConns,\n            failTimeout: failTimeout,\n            failTimeoutUnit: failTimeoutUnit,\n            flag: server.flag,\n        });\n    });\n    form.value.servers = servers;\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton\n            :buttons=\"[\n                {\n                    label: $t('menu.website'),\n                    path: '/websites',\n                },\n            ]\"\n        />\n        <LayoutContent :title=\"$t('website.websiteConfig')\" :back-name=\"'Website'\" v-loading=\"loading\">\n            <template #app>\n                <WebsiteStatus\n                    v-if=\"website.id > 0\"\n                    :primary-domain=\"website.primaryDomain\"\n                    :status=\"website.status\"\n                    :expire-date=\"website.expireDate\"\n                />\n            </template>\n            <template #leftToolBar>\n                <el-button type=\"primary\" :plain=\"index !== 'basic'\" @click=\"changeTab('basic')\">\n                    {{ $t('website.basic') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"index !== 'log'\" @click=\"changeTab('log')\">\n                    {{ $t('commons.button.log') }}\n                </el-button>\n                <el-button type=\"primary\" :plain=\"index !== 'resource'\" @click=\"changeTab('resource')\">\n                    {{ $t('website.source', 2) }}\n                </el-button>\n            </template>\n            <template #main>\n                <MainDiv :heightDiff=\"260\">\n                    <Basic :website=\"website\" v-if=\"index === 'basic' && website.id\" :heightDiff=\"320\"></Basic>\n                    <Log :id=\"id\" v-if=\"index === 'log'\"></Log>\n                    <Resource :id=\"id\" v-if=\"index === 'resource'\"></Resource>\n                </MainDiv>\n            </template>\n        </LayoutContent>\n    </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, ref, watch } from 'vue';\nimport Basic from './basic/index.vue';\nimport Resource from './resource/index.vue';\nimport Log from './log/index.vue';\nimport WebsiteStatus from '@/views/website/website/status/index.vue';\nimport { getWebsite } from '@/api/modules/website';\nimport { GetRuntime } from '@/api/modules/runtime';\nimport { routerToNameWithParams } from '@/utils/router';\nimport MainDiv from '@/components/main-div/index.vue';\n\nconst props = defineProps({\n    id: {\n        type: String,\n        default: '0',\n    },\n    tab: {\n        type: String,\n        default: 'basic',\n    },\n});\n\nconst id = ref(0);\nconst index = ref('basic');\nconst website = ref<any>({});\nconst loading = ref(false);\nconst configPHP = ref(false);\n\nwatch(index, (curr, old) => {\n    if (curr != old) {\n        changeTab(curr);\n    }\n});\n\nconst changeTab = (index: string) => {\n    routerToNameWithParams('WebsiteConfig', { id: id.value, tab: index });\n};\n\nonMounted(async () => {\n    index.value = props.tab;\n    id.value = Number(props.id);\n    loading.value = true;\n    const res = await getWebsite(id.value);\n    website.value = res.data;\n    if (res.data.type === 'runtime') {\n        const runRes = await GetRuntime(res.data.runtimeID);\n        if (runRes.data.type == 'php' && runRes.data.resource === 'appstore') {\n            configPHP.value = true;\n        }\n    }\n    loading.value = false;\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/log/index.vue",
    "content": "<template>\n    <el-tabs tab-position=\"left\" v-model=\"index\">\n        <el-tab-pane :label=\"$t('logs.websiteLog')\" name=\"0\">\n            <LogFile :id=\"id\" :log-type=\"'access.log'\" v-if=\"index == '0'\"></LogFile>\n        </el-tab-pane>\n        <el-tab-pane :label=\"$t('website.errLog')\" name=\"1\">\n            <LogFile :id=\"id\" :log-type=\"'error.log'\" v-if=\"index == '1'\"></LogFile>\n        </el-tab-pane>\n    </el-tabs>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, ref } from 'vue';\nimport LogFile from './log-fiile/index.vue';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst id = computed(() => {\n    return props.id;\n});\n\nlet index = ref('0');\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/log/log-fiile/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <div>\n            <el-form-item :label=\"$t('website.enable')\">\n                <el-switch v-model=\"data.enable\" @change=\"updateEnable\"></el-switch>\n            </el-form-item>\n        </div>\n        <LogFile :config=\"{ id: id, type: 'website', name: logName, colorMode: 'nginx' }\" ref=\"logRef\">\n            <template #button>\n                <el-button @click=\"cleanLog\" icon=\"Delete\">\n                    {{ $t('commons.button.clean') }}\n                </el-button>\n            </template>\n        </LogFile>\n    </div>\n    <OpDialog ref=\"opRef\" @search=\"clearLog\" />\n</template>\n<script lang=\"ts\" setup>\nimport { computed, onMounted, ref } from 'vue';\nimport { getWebsite, opWebsiteLog } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport LogFile from '@/components/log/file/index.vue';\nimport { MsgSuccess } from '@/utils/message';\n\nconst props = defineProps({\n    logType: {\n        type: String,\n        default: '',\n    },\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\nconst logName = computed(() => {\n    return props.logType;\n});\nconst id = computed(() => {\n    return props.id;\n});\nconst loading = ref(false);\nconst data = ref({\n    enable: false,\n    content: '',\n    path: '',\n});\nconst opRef = ref();\nconst logRef = ref();\n\nconst updateEnable = () => {\n    const operate = data.value.enable ? 'enable' : 'disable';\n    const req = {\n        id: id.value,\n        operate: operate,\n        logType: props.logType,\n    };\n    loading.value = true;\n    opWebsiteLog(req)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst clearLog = () => {\n    logRef.value.clearLog();\n};\n\nconst cleanLog = async () => {\n    let log = props.logType === 'access.log' ? i18n.global.t('logs.websiteLog') : i18n.global.t('website.errLog');\n    opRef.value.acceptParams({\n        title: i18n.global.t('commons.button.clean'),\n        names: [],\n        msg: i18n.global.t('commons.msg.operatorHelper', [log, i18n.global.t('commons.button.clean')]),\n        api: opWebsiteLog,\n        params: { id: id.value, operate: 'delete', logType: props.logType },\n    });\n};\n\nconst get = async () => {\n    try {\n        const res = await getWebsite(props.id);\n        if (props.logType === 'access.log') {\n            data.value.enable = res.data.accessLog;\n        }\n        if (props.logType === 'error.log') {\n            data.value.enable = res.data.errorLog;\n        }\n    } catch (error) {}\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/resource/index.vue",
    "content": "<template>\n    <el-tabs tab-position=\"left\" v-model=\"index\">\n        <el-tab-pane :label=\"'OpenResty'\" name=\"0\">\n            <Nginx :id=\"id\" v-if=\"index == '0'\"></Nginx>\n        </el-tab-pane>\n    </el-tabs>\n</template>\n\n<script lang=\"ts\" setup>\nimport { GetRuntime } from '@/api/modules/runtime';\nimport { getWebsite } from '@/api/modules/website';\nimport { computed, onMounted, ref } from 'vue';\nimport Nginx from './nginx/index.vue';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst id = computed(() => {\n    return props.id;\n});\n\nlet index = ref('0');\nlet configPHP = ref(false);\nlet installId = ref(0);\n\nconst getWebsiteDetail = async () => {\n    const res = await getWebsite(props.id);\n    if (res.data.type === 'runtime') {\n        installId.value = res.data.appInstallId;\n        const runRes = await GetRuntime(res.data.runtimeID);\n        if (runRes.data.type == 'php' && runRes.data.resource === 'appstore') {\n            configPHP.value = true;\n        }\n    }\n};\n\nonMounted(() => {\n    getWebsiteDetail();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/config/resource/nginx/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <CodemirrorPro v-model=\"content\" mode=\"nginx\" :heightDiff=\"400\" />\n        <el-button type=\"primary\" @click=\"submit()\" class=\"mt-2.5\">\n            {{ $t('nginx.saveAndReload') }}\n        </el-button>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { getWebsiteConfig, updateNginxFile } from '@/api/modules/website';\nimport { computed, onMounted, ref } from 'vue';\nimport { File } from '@/api/interface/file';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\n\nconst props = defineProps({\n    id: {\n        type: Number,\n        default: 0,\n    },\n});\n\nconst id = computed(() => {\n    return props.id;\n});\n\nlet data = ref<File.File>();\nlet loading = ref(false);\nlet content = ref('');\n\nconst get = () => {\n    loading.value = true;\n    getWebsiteConfig(id.value, 'openresty')\n        .then((res) => {\n            data.value = res.data;\n            content.value = data.value.content;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst submit = () => {\n    loading.value = true;\n    updateNginxFile({\n        id: id.value,\n        content: content.value,\n    })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/cors/index.vue",
    "content": "<template>\n    <div>\n        <div class=\"flex justify-between items-center py-3\" v-if=\"enableSize == 'large'\">\n            <div class=\"flex flex-col gap-1\">\n                <span class=\"font-medium\">{{ $t('website.enableCors') }}</span>\n            </div>\n            <el-switch v-model=\"corsEnabled\" size=\"large\" @change=\"handleCorsChange\" />\n        </div>\n\n        <el-form-item :label=\"$t('website.enableCors')\" v-if=\"enableSize == 'small'\">\n            <el-switch v-model=\"corsEnabled\" size=\"large\" @change=\"handleCorsChange\" />\n        </el-form-item>\n\n        <el-collapse-transition>\n            <div v-if=\"corsEnabled\" class=\"mt-4\">\n                <el-form-item :label=\"$t('website.allowOrigins')\" prop=\"allowOrigins\">\n                    <el-input\n                        v-model=\"corsConfig.allowOrigins\"\n                        type=\"textarea\"\n                        placeholder=\"*\"\n                        @input=\"emitUpdate\"\n                    ></el-input>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('website.allowMethods')\" prop=\"allowMethods\">\n                    <el-input\n                        v-model=\"corsConfig.allowMethods\"\n                        type=\"textarea\"\n                        placeholder=\"GET,POST,OPTIONS,PUT,DELETE\"\n                        @input=\"emitUpdate\"\n                    ></el-input>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('website.allowHeaders')\" prop=\"allowHeaders\">\n                    <el-input v-model=\"corsConfig.allowHeaders\" type=\"textarea\" @input=\"emitUpdate\"></el-input>\n                </el-form-item>\n\n                <el-form-item :label=\"$t('website.allowCredentials')\" prop=\"allowCredentials\">\n                    <el-switch v-model=\"corsConfig.allowCredentials\" @change=\"emitUpdate\" />\n                </el-form-item>\n\n                <el-form-item :label=\"$t('website.preflight')\" prop=\"preflight\">\n                    <el-switch v-model=\"corsConfig.preflight\" @change=\"emitUpdate\" />\n                    <span class=\"input-help\">{{ $t('website.preflightHleper') }}</span>\n                </el-form-item>\n            </div>\n        </el-collapse-transition>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, watch } from 'vue';\n\ninterface CorsConfig {\n    allowOrigins: string;\n    allowMethods: string;\n    allowHeaders: string;\n    allowCredentials: boolean;\n    preflight: boolean;\n}\n\ninterface Props {\n    modelValue: boolean;\n    config: CorsConfig;\n    enableSize: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n    modelValue: false,\n    config: () => ({\n        allowOrigins: '*',\n        allowMethods: 'GET,POST,OPTIONS,PUT,DELETE',\n        allowHeaders: '',\n        allowCredentials: false,\n        preflight: true,\n    }),\n    enableSize: 'large',\n});\n\nconst emit = defineEmits(['update:modelValue', 'update:config']);\n\nconst corsEnabled = ref(props.modelValue);\nconst corsConfig = ref<CorsConfig>({ ...props.config });\n\nwatch(\n    () => props.modelValue,\n    (val) => {\n        corsEnabled.value = val;\n    },\n);\n\nwatch(\n    () => props.config,\n    (val) => {\n        corsConfig.value = { ...val };\n    },\n    { deep: true },\n);\n\nconst handleCorsChange = (enabled: boolean) => {\n    emit('update:modelValue', enabled);\n\n    if (enabled) {\n        corsConfig.value = {\n            allowOrigins: '*',\n            allowMethods: 'GET,POST,OPTIONS,PUT,DELETE',\n            allowHeaders: '',\n            allowCredentials: false,\n            preflight: true,\n        };\n    } else {\n        corsConfig.value = {\n            allowOrigins: '',\n            allowMethods: '',\n            allowHeaders: '',\n            allowCredentials: false,\n            preflight: true,\n        };\n    }\n    emitUpdate();\n};\n\nconst emitUpdate = () => {\n    emit('update:config', corsConfig.value);\n};\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/create/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('commons.button.create')\" size=\"60%\" @close=\"handleClose\">\n        <div v-loading=\"loading\" :class=\"{ mask: !versionExist }\">\n            <el-form\n                ref=\"websiteForm\"\n                label-position=\"top\"\n                :model=\"website\"\n                label-width=\"125px\"\n                :rules=\"rules\"\n                :validate-on-rule-change=\"false\"\n                v-loading=\"loading\"\n            >\n                <el-form-item>\n                    <el-radio-group v-model=\"website.type\" @change=\"changeType\">\n                        <el-radio-button v-for=\"item in WebsiteTypes\" :key=\"item.value\" :value=\"item.value\">\n                            {{ item.label }}\n                        </el-radio-button>\n                    </el-radio-group>\n                </el-form-item>\n                <SSLAlert :websiteType=\"website.type\" class=\"mb-2\" :versionNotMatch=\"versionNotMatch\" />\n\n                <GroupSelect\n                    v-model=\"website.webSiteGroupId\"\n                    :prop=\"'webSiteGroupId'\"\n                    :groupType=\"'website'\"\n                ></GroupSelect>\n                <div v-if=\"website.type === 'deployment'\">\n                    <el-form-item prop=\"appType\">\n                        <el-radio-group v-model=\"website.appType\" @change=\"changeAppType(website.appType)\">\n                            <el-radio :label=\"'installed'\" :value=\"'installed'\">\n                                {{ $t('website.appInstalled') }}\n                            </el-radio>\n                            <el-radio :label=\"'new'\" :value=\"'new'\">\n                                {{ $t('website.appNew') }}\n                            </el-radio>\n                        </el-radio-group>\n                    </el-form-item>\n                    <el-form-item\n                        v-if=\"website.appType == 'installed'\"\n                        :label=\"$t('website.appInstalled')\"\n                        prop=\"appInstallId\"\n                    >\n                        <el-select v-model=\"website.appInstallId\" class=\"p-w-300\">\n                            <el-option\n                                v-for=\"(appInstall, index) in appInstalls\"\n                                :key=\"index\"\n                                :label=\"appInstall.name\"\n                                :value=\"appInstall.id\"\n                                :disabled=\"appInstall.status !== 'Running'\"\n                            >\n                                <div class=\"flex justify-between items-center w-full\">\n                                    <span>{{ appInstall.name }}</span>\n                                    <span><Status :key=\"appInstall.status\" :status=\"appInstall.status\"></Status></span>\n                                </div>\n                            </el-option>\n                        </el-select>\n                    </el-form-item>\n                    <div v-if=\"website.appType == 'new'\">\n                        <el-form-item :label=\"$t('app.app')\" prop=\"appinstall.appId\">\n                            <el-select\n                                v-model=\"website.appinstall.appId\"\n                                @change=\"changeApp()\"\n                                class=\"p-w-300\"\n                                filterable\n                            >\n                                <el-option\n                                    v-for=\"(app, index) in apps\"\n                                    :key=\"index\"\n                                    :label=\"app.name\"\n                                    :value=\"app.id\"\n                                    :disabled=\"app.key == 'openclaw'\"\n                                ></el-option>\n                            </el-select>\n                        </el-form-item>\n                        <AppInstallForm ref=\"installFormRef\" v-model=\"website.appinstall\" :loading=\"loading\" />\n                    </div>\n                </div>\n                <div v-if=\"website.type === 'subsite'\">\n                    <el-form-item :label=\"$t('website.parentWbeiste')\" prop=\"parentWebsiteID\">\n                        <el-select v-model=\"website.parentWebsiteID\" @change=\"getDir(website.parentWebsiteID)\">\n                            <el-option\n                                v-for=\"(site, index) in parentWebsites\"\n                                :key=\"index\"\n                                :label=\"site.primaryDomain\"\n                                :value=\"site.id\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                    <el-form-item :label=\"$t('website.runDir')\" prop=\"siteDir\">\n                        <el-select v-model=\"website.siteDir\" filterable class=\"p-w-200\">\n                            <el-option\n                                v-for=\"(item, index) in dirs\"\n                                :label=\"item\"\n                                :value=\"item\"\n                                :key=\"index\"\n                            ></el-option>\n                        </el-select>\n                    </el-form-item>\n                </div>\n                <div v-if=\"website.type === 'runtime'\">\n                    <el-row :gutter=\"20\">\n                        <el-col :span=\"8\">\n                            <el-form-item :label=\"$t('commons.table.type')\" prop=\"runtimeType\">\n                                <el-select v-model=\"website.runtimeType\" @change=\"changeRuntimeType()\">\n                                    <el-option label=\"PHP\" value=\"php\"></el-option>\n                                    <el-option label=\"Node.js\" value=\"node\"></el-option>\n                                    <el-option label=\"Java\" value=\"java\"></el-option>\n                                    <el-option label=\"Go\" value=\"go\"></el-option>\n                                    <el-option label=\"Python\" value=\"python\"></el-option>\n                                    <el-option label=\".NET\" value=\"dotnet\"></el-option>\n                                </el-select>\n                            </el-form-item>\n                        </el-col>\n                        <el-col :span=\"16\">\n                            <el-form-item :label=\"$t('runtime.runtime')\" prop=\"runtimeID\">\n                                <el-select\n                                    v-model=\"website.runtimeID\"\n                                    @change=\"changeRuntime(website.runtimeID)\"\n                                    filterable\n                                >\n                                    <el-option\n                                        v-for=\"run in runtimes\"\n                                        :key=\"run.name\"\n                                        :label=\"run.name + ' [' + getRuntimeLabel(run.resource) + ']'\"\n                                        :value=\"run.id\"\n                                    >\n                                        <el-row>\n                                            <el-col :span=\"14\">\n                                                <span class=\"runtimeName\">\n                                                    {{ run.name }}\n                                                </span>\n                                            </el-col>\n                                            <el-col :span=\"10\">\n                                                {{ ' [' + getRuntimeLabel(run.resource) + ']' }}\n                                            </el-col>\n                                        </el-row>\n                                    </el-option>\n                                </el-select>\n                            </el-form-item>\n                        </el-col>\n                    </el-row>\n                    <div v-if=\"website.runtimeType === 'php' && runtimeResource === 'local'\">\n                        <el-form-item :label=\"$t('website.proxyType')\" prop=\"proxyType\">\n                            <el-select v-model=\"website.proxyType\">\n                                <el-option :label=\"$t('website.tcp')\" :value=\"'tcp'\"></el-option>\n                                <el-option :label=\"$t('website.unix')\" :value=\"'unix'\"></el-option>\n                            </el-select>\n                        </el-form-item>\n                        <el-form-item v-if=\"website.proxyType === 'tcp'\" :label=\"$t('commons.table.port')\" prop=\"port\">\n                            <el-input v-model.number=\"website.port\"></el-input>\n                        </el-form-item>\n                    </div>\n                    <el-form-item\n                        :label=\"$t('setting.proxyPort')\"\n                        prop=\"port\"\n                        v-if=\"website.runtimeType !== 'php' && runtimePorts.length > 1\"\n                    >\n                        <el-select v-model=\"website.port\">\n                            <el-option\n                                v-for=\"(port, index) in runtimePorts\"\n                                :key=\"index\"\n                                :label=\"port\"\n                                :value=\"port\"\n                            ></el-option>\n                        </el-select>\n                        <span class=\"input-help\">{{ $t('website.runtimePortHelper') }}</span>\n                    </el-form-item>\n                    <el-text\n                        v-if=\"\n                            runtimes.length > 0 &&\n                            website.type === 'runtime' &&\n                            website.runtimeType !== 'php' &&\n                            website.port == 0\n                        \"\n                        type=\"danger\"\n                    >\n                        {{ $t('website.runtimePortWarn') }}\n                    </el-text>\n                </div>\n\n                <el-divider content-position=\"left\" v-if=\"website.type !== 'stream'\">\n                    <el-text type=\"info\" size=\"small\">{{ $t('website.domain') }}</el-text>\n                </el-divider>\n                <div v-if=\"website.type === 'stream'\">\n                    <el-form-item :label=\"$t('website.streamPorts')\" prop=\"streamPorts\">\n                        <el-input\n                            v-model=\"website.streamPorts\"\n                            :placeholder=\"$t('website.streamPortsHelper')\"\n                        ></el-input>\n                    </el-form-item>\n                    <el-form-item prop=\"udp\">\n                        <el-checkbox v-model=\"website.udp\" :label=\"$t('website.udp')\" size=\"large\" />\n                    </el-form-item>\n                </div>\n                <div v-else>\n                    <DomainCreate\n                        v-model:form=\"website\"\n                        @gengerate=\"websiteForm.clearValidate()\"\n                        @domain-blur=\"handleFirstDomainBlur\"\n                    ></DomainCreate>\n                </div>\n                <el-divider content-position=\"left\">\n                    <el-text type=\"info\" size=\"small\">{{ $t('website.advancedSettings') }}</el-text>\n                </el-divider>\n                <el-form-item :label=\"$t('website.alias')\" prop=\"alias\" class=\"mt-2\">\n                    <el-input v-model.trim=\"website.alias\" :placeholder=\"$t('website.aliasHelper')\"></el-input>\n                    <div>\n                        <span class=\"input-help\">\n                            <span>{{ $t('website.staticPath') + staticPath + website.alias }}</span>\n                            <span v-if=\"website.type === 'static' && website.alias != ''\">{{ '/index' }}</span>\n                        </span>\n                    </div>\n                </el-form-item>\n                <el-form-item prop=\"IPV6\">\n                    <el-checkbox v-model=\"website.IPV6\" :label=\"$t('website.ipv6')\" size=\"large\" />\n                </el-form-item>\n                <div v-if=\"website.type == 'stream'\">\n                    <LoadBalanceForm ref=\"lbFormRef\" v-model=\"steamConfig\" :disabled=\"true\" type=\"stream\" />\n                </div>\n                <div v-else>\n                    <el-form-item prop=\"enableSSL\">\n                        <el-checkbox v-model=\"website.enableSSL\" :label=\"$t('website.enableHTTPS')\" size=\"large\" />\n                        <span class=\"input-help\">{{ $t('website.enableSSLHelper') }}</span>\n                    </el-form-item>\n                    <div v-if=\"website.enableSSL\">\n                        <el-form-item :label=\"$t('website.acmeAccountManage')\" prop=\"acmeAccountID\">\n                            <el-select\n                                v-model=\"website.acmeAccountID\"\n                                :placeholder=\"$t('website.selectAcme')\"\n                                @change=\"listSSLs\"\n                            >\n                                <el-option :key=\"0\" :label=\"$t('website.imported')\" :value=\"0\"></el-option>\n                                <el-option\n                                    v-for=\"(acme, index) in acmeAccounts\"\n                                    :key=\"index\"\n                                    :label=\"acme.email\"\n                                    :value=\"acme.id\"\n                                >\n                                    <span>\n                                        {{ acme.email }}\n                                        <el-tag class=\"ml-5\">{{ getAccountName(acme.type) }}</el-tag>\n                                    </span>\n                                </el-option>\n                            </el-select>\n                        </el-form-item>\n                        <el-form-item :label=\"$t('website.ssl')\" prop=\"websiteSSLID\" :hide-required-asterisk=\"true\">\n                            <el-select\n                                v-model=\"website.websiteSSLID\"\n                                :placeholder=\"$t('website.selectSSL')\"\n                                @change=\"handleSSLSelectChange\"\n                            >\n                                <el-option\n                                    v-for=\"(ssl, index) in ssls\"\n                                    :key=\"index\"\n                                    :label=\"ssl.primaryDomain\"\n                                    :value=\"ssl.id\"\n                                    :disabled=\"ssl.pem == ''\"\n                                >\n                                    <span>{{ ssl.primaryDomain }}</span>\n                                    <el-tag class=\"tagClass\" v-if=\"ssl.expireDate\">\n                                        {{ dateFormatSimple(ssl.expireDate) }}\n                                    </el-tag>\n                                    <el-tag class=\"tagClass\" v-if=\"ssl.organization\">\n                                        {{ ssl.organization }}\n                                    </el-tag>\n                                </el-option>\n                            </el-select>\n                        </el-form-item>\n                        <el-form-item :label=\"' '\" v-if=\"websiteSSL && websiteSSL.id > 0\">\n                            <WebsiteSSL :websiteSSL=\"websiteSSL\" />\n                        </el-form-item>\n                    </div>\n                </div>\n                <el-form-item prop=\"enableFtp\" v-if=\"website.type === 'static' || website.type === 'runtime'\">\n                    <el-checkbox\n                        @change=\"random\"\n                        v-model=\"website.enableFtp\"\n                        :label=\"$t('commons.button.create')\"\n                        size=\"large\"\n                    />\n                    <span class=\"input-help\">{{ $t('website.ftpHelper') }}</span>\n                </el-form-item>\n                <el-row :gutter=\"20\" v-if=\"website.enableFtp\">\n                    <el-col :span=\"12\">\n                        <el-form-item prop=\"ftpUser\" :label=\"$t('website.ftpUser')\">\n                            <el-input v-model=\"website.ftpUser\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item prop=\"ftpPassword\" :label=\"$t('website.ftpPassword')\">\n                            <el-input type=\"password\" clearable v-model=\"website.ftpPassword\" show-password>\n                                <template #append>\n                                    <el-button @click=\"random\">{{ $t('commons.button.random') }}</el-button>\n                                </template>\n                            </el-input>\n                        </el-form-item>\n                    </el-col>\n                </el-row>\n\n                <el-row :gutter=\"20\" v-if=\"website.type === 'proxy'\">\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('website.proxyAddress')\" prop=\"proxyAddress\">\n                            <el-input v-model=\"website.proxyAddress\" :placeholder=\"$t('website.proxyHelper')\">\n                                <template #prepend>\n                                    <el-select v-model=\"website.proxyProtocol\" class=\"pre-select\">\n                                        <el-option label=\"http\" value=\"http://\" />\n                                        <el-option label=\"https\" value=\"https://\" />\n                                        <el-option :label=\"$t('website.other')\" value=\"\" />\n                                    </el-select>\n                                </template>\n                            </el-input>\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('app.app')\">\n                            <el-select\n                                v-model=\"website.appInstallId\"\n                                class=\"p-w-200\"\n                                filterable\n                                @change=\"changeInstall\"\n                            >\n                                <el-option\n                                    v-for=\"(appInstall, index) in appInstalls\"\n                                    :key=\"index\"\n                                    :label=\"appInstall.name\"\n                                    :value=\"appInstall.id\"\n                                ></el-option>\n                            </el-select>\n                        </el-form-item>\n                    </el-col>\n                </el-row>\n\n                <el-form-item prop=\"createDb\" v-if=\"website.type === 'runtime'\">\n                    <el-checkbox\n                        @change=\"randomDbPassword\"\n                        v-model=\"website.createDb\"\n                        :label=\"$t('website.createDb')\"\n                        size=\"large\"\n                    />\n                </el-form-item>\n                <el-row :gutter=\"20\" v-if=\"website.type === 'runtime' && website.createDb\">\n                    <el-col :span=\"24\">\n                        <el-form-item :label=\"$t('menu.database')\" prop=\"dbHost\">\n                            <el-row :gutter=\"20\">\n                                <el-col :span=\"12\">\n                                    <el-select\n                                        v-model=\"website.dbType\"\n                                        class=\"p-w-200\"\n                                        @change=\"getAppByService(website.dbType)\"\n                                    >\n                                        <el-option label=\"MySQL\" value=\"mysql\" />\n                                        <el-option label=\"MariaDB\" value=\"mariadb\" />\n                                        <el-option label=\"PostgreSQL\" value=\"postgresql\" />\n                                    </el-select>\n                                </el-col>\n                                <el-col :span=\"12\">\n                                    <el-select v-model=\"website.dbHost\" class=\"p-w-200\">\n                                        <el-option\n                                            v-for=\"(service, index) in dbServices\"\n                                            :key=\"index\"\n                                            :label=\"service.label\"\n                                            :value=\"service.value\"\n                                        ></el-option>\n                                    </el-select>\n                                </el-col>\n                            </el-row>\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"24\">\n                        <el-form-item :label=\"$t('commons.table.name')\" prop=\"dbName\">\n                            <el-input clearable v-model.trim=\"website.dbName\" @input=\"website.dbUser = website.dbName\">\n                                <template #append>\n                                    <el-select v-model=\"website.dbFormat\" class=\"p-w-100\">\n                                        <el-option label=\"utf8mb4\" value=\"utf8mb4\" />\n                                        <el-option label=\"utf-8\" value=\"utf8\" />\n                                        <el-option label=\"gbk\" value=\"gbk\" />\n                                        <el-option label=\"big5\" value=\"big5\" />\n                                    </el-select>\n                                </template>\n                            </el-input>\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('commons.login.username')\" prop=\"dbUser\">\n                            <el-input clearable v-model.trim=\"website.dbUser\" />\n                        </el-form-item>\n                    </el-col>\n                    <el-col :span=\"12\">\n                        <el-form-item :label=\"$t('commons.login.password')\" prop=\"dbPassword\">\n                            <el-input type=\"dbPassword\" clearable show-password v-model.trim=\"website.dbPassword\">\n                                <template #append>\n                                    <el-button @click=\"randomDbPassword\">{{ $t('commons.button.random') }}</el-button>\n                                </template>\n                            </el-input>\n                        </el-form-item>\n                    </el-col>\n                </el-row>\n\n                <el-divider content-position=\"left\">\n                    <el-text type=\"info\" size=\"small\">{{ $t('commons.table.description') }}</el-text>\n                </el-divider>\n                <el-form-item :label=\"$t('website.remark')\" prop=\"remark\">\n                    <el-input type=\"textarea\" :rows=\"3\" clearable v-model=\"website.remark\" />\n                </el-form-item>\n            </el-form>\n        </div>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button\n                type=\"primary\"\n                @click=\"submit(websiteForm)\"\n                :disabled=\"loading || (website.type == 'stream' && versionNotMatch)\"\n            >\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n        <Check ref=\"preCheckRef\"></Check>\n        <el-card width=\"30%\" v-if=\"!versionExist\" class=\"mask-prompt\">\n            <span>\n                {{ $t('runtime.openrestyWarn') }}\n            </span>\n        </el-card>\n        <TaskLog ref=\"taskLog\" />\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup name=\"CreateWebSite\">\nimport AppInstallForm from '@/views/app-store/detail/form/index.vue';\nimport SSLAlert from '@/views/website/website/create/site-alert/index.vue';\nimport DomainCreate from '@/views/website/website/domain-create/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport Check from '../check/index.vue';\nimport GroupSelect from '@/views/website/website/components/group/index.vue';\nimport WebsiteSSL from '@/views/website/website/components/website-ssl/index.vue';\nimport LoadBalanceForm from '@/views/website/website/config/basic/load-balance/form/index.vue';\n\nimport { App } from '@/api/interface/app';\nimport { searchApp, getAppInstalled } from '@/api/modules/app';\nimport {\n    createWebsite,\n    getWebsiteOptions,\n    listSSL,\n    preCheck,\n    searchAcmeAccount,\n    getDirConfig,\n} from '@/api/modules/website';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { ElForm, FormInstance } from 'element-plus';\nimport { reactive, ref, watch } from 'vue';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { SearchRuntimes } from '@/api/modules/runtime';\nimport { Runtime } from '@/api/interface/runtime';\nimport { getRandomStr, getRuntimeLabel, dateFormatSimple } from '@/utils/util';\nimport { getAppService } from '@/api/modules/app';\nimport { v4 as uuidv4 } from 'uuid';\nimport { getAccountName } from '@/utils/util';\nimport { Website } from '@/api/interface/website';\nimport { getPathByType } from '@/api/modules/files';\nimport { getWebsiteTypes } from '@/global/mimetype';\nimport { compareVersion } from '@/utils/version';\n\ntype SSLItem = Website.SSLDTO & {\n    organization?: string;\n    acmeAccount?: {\n        email?: string;\n    };\n};\n\nconst websiteForm = ref<FormInstance>();\n\nconst initData = () => ({\n    primaryDomain: '',\n    type: 'deployment',\n    alias: '',\n    remark: '',\n    appType: 'installed',\n    appInstallId: undefined,\n    webSiteGroupId: 0,\n    otherDomains: '',\n    proxy: '',\n    runtimeID: undefined,\n    appinstall: {\n        appId: 0,\n        name: '',\n        appDetailId: 0,\n        params: {},\n        version: '',\n        appkey: '',\n\n        advanced: false,\n        cpuQuota: 0,\n        memoryLimit: 0,\n        memoryUnit: 'MB',\n        containerName: '',\n        allowPort: false,\n\n        format: 'utf8mb4',\n        collation: '',\n    },\n    IPV6: false,\n    enableFtp: false,\n    ftpUser: '',\n    ftpPassword: '',\n    proxyType: 'tcp',\n    port: 9000,\n    proxyProtocol: 'http://',\n    proxyAddress: '',\n    runtimeType: 'php',\n    taskID: '',\n    createDb: false,\n    dbName: '',\n    dbPassword: '',\n    dbFormat: 'utf8mb4',\n    dbUser: '',\n    dbType: 'mysql',\n    dbHost: '',\n    enableSSL: false,\n    websiteSSLID: undefined,\n    acmeAccountID: undefined,\n    domains: [],\n    parentWebsiteID: undefined,\n    siteDir: '',\n\n    streamPorts: '',\n    udp: false,\n    name: '',\n    algorithm: '',\n    servers: [],\n});\nconst website = ref(initData());\nconst rules = ref<any>({\n    alias: [Rules.alias],\n    type: [Rules.requiredInput],\n    webSiteGroupId: [Rules.requiredSelectBusiness],\n    appInstallId: [Rules.requiredSelectBusiness],\n    appType: [Rules.requiredInput],\n    proxyAddress: [Rules.requiredInput],\n    runtimeID: [Rules.requiredSelectBusiness],\n    appinstall: {\n        name: [Rules.appName],\n        appId: [Rules.requiredSelectBusiness],\n        params: {},\n        cpuQuota: [Rules.requiredInput, checkNumberRange(0, 99999)],\n        memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],\n        containerName: [Rules.containerName],\n    },\n    ftpUser: [Rules.simpleName],\n    ftpPassword: [Rules.simplePassword],\n    proxyType: [Rules.requiredSelect],\n    port: [Rules.port],\n    runtimeType: [Rules.requiredInput],\n    dbName: [Rules.requiredInput, Rules.dbName],\n    dbUser: [Rules.requiredInput, Rules.name],\n    dbPassword: [Rules.requiredInput, Rules.paramComplexity],\n    dbHost: [Rules.requiredSelect],\n    websiteSSLID: [Rules.requiredSelect],\n    parentWebsiteID: [Rules.requiredSelect],\n    siteDir: [Rules.requiredSelect],\n    streamPorts: [Rules.requiredInput],\n});\n\nconst open = ref(false);\nconst loading = ref(false);\nconst acmeAccounts = ref();\nconst appInstalls = ref<App.AppInstalled[]>([]);\nconst appReq = reactive({\n    type: 'website',\n    page: 1,\n    pageSize: 100,\n});\nconst apps = ref<App.AppItem[]>([]);\nconst preCheckRef = ref();\nconst staticPath = ref('');\nconst runtimeResource = ref('appstore');\nconst initRuntimeReq = () => ({\n    page: 1,\n    pageSize: 100,\n    status: 'Running',\n    type: 'php',\n});\nconst runtimeReq = ref<Runtime.RuntimeReq>(initRuntimeReq());\nconst runtimes = ref<Runtime.RuntimeDTO[]>([]);\nconst versionExist = ref(true);\nconst em = defineEmits(['close']);\nconst taskLog = ref();\nconst dbServices = ref();\nconst ssls = ref<SSLItem[]>([]);\nconst websiteSSL = ref<SSLItem | undefined>(undefined);\nconst userSelectedSSL = ref(false);\nconst parentWebsites = ref();\nconst dirs = ref([]);\nconst runtimePorts = ref([]);\nconst WebsiteTypes = getWebsiteTypes();\nconst installFormRef = ref();\nconst lbFormRef = ref();\nconst versionNotMatch = ref();\nconst initLbForm = () => ({\n    name: '',\n    type: 'stream',\n    algorithm: 'default',\n    servers: [\n        {\n            server: '',\n            weight: undefined,\n            maxFails: undefined,\n            maxConns: undefined,\n            failTimeout: undefined,\n            failTimeoutUnit: 's',\n            flag: '',\n        },\n    ],\n});\nconst steamConfig = ref(initLbForm());\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst random = async () => {\n    website.value.ftpPassword = getRandomStr(16);\n};\n\nconst randomDbPassword = async () => {\n    website.value.dbPassword = getRandomStr(16);\n};\n\nconst changeType = (type: string) => {\n    localStorage.setItem('website-type', type);\n    switch (type) {\n        case 'deployment':\n            website.value.appType = 'installed';\n            searchAppInstalled('website');\n            break;\n        case 'runtime':\n            getRuntimes();\n            getAppByService(website.value.dbType);\n            break;\n        case 'proxy':\n            website.value.proxyAddress = '';\n            searchAppInstalled('proxy');\n            break;\n        case 'subsite':\n            listWebsites();\n            break;\n        default:\n            website.value.appInstallId = undefined;\n            break;\n    }\n    website.value.type = type;\n    versionExist.value = true;\n};\n\nconst searchAppInstalled = (appType: string) => {\n    getAppInstalled({ type: appType, unused: true, all: true, page: 1, pageSize: 100 }).then((res) => {\n        appInstalls.value = res.data.items;\n        website.value.appInstallId = undefined;\n        if (\n            appType == 'website' &&\n            res.data.items &&\n            res.data.items.length > 0 &&\n            res.data.items[0].status === 'Running'\n        ) {\n            website.value.appInstallId = res.data.items[0].id;\n        }\n    });\n};\n\nconst getAppByService = async (key: string) => {\n    const res = await getAppService(key);\n    dbServices.value = res.data;\n};\n\nconst getProxyTargetFromApp = (app: Pick<App.AppInstalled, 'httpPort' | 'httpsPort'>) => {\n    if ((app.httpPort ?? 0) > 0) {\n        return {\n            protocol: 'http://',\n            address: `127.0.0.1:${app.httpPort}`,\n        };\n    }\n    if ((app.httpsPort ?? 0) > 0) {\n        return {\n            protocol: 'https://',\n            address: `127.0.0.1:${app.httpsPort}`,\n        };\n    }\n    return {\n        protocol: 'http://',\n        address: '',\n    };\n};\n\nconst changeInstall = () => {\n    appInstalls.value.forEach((app) => {\n        if (app.id === website.value.appInstallId) {\n            const target = getProxyTargetFromApp(app);\n            website.value.proxyProtocol = target.protocol;\n            website.value.proxyAddress = target.address;\n        }\n    });\n};\n\nconst searchAppList = () => {\n    searchApp(appReq).then((res) => {\n        apps.value = res.data.items;\n\n        const selectableApp = res.data.items.find((item) => item.key !== 'openclaw');\n        if (selectableApp) {\n            website.value.appinstall.appId = selectableApp.id;\n            website.value.appinstall.appkey = selectableApp.key;\n            changeApp();\n        }\n    });\n};\n\nconst changeApp = () => {\n    apps.value.forEach((app) => {\n        if (app.id === website.value.appinstall.appId) {\n            website.value.appinstall.appkey = app.key;\n            installFormRef.value.initForm(app.key);\n        }\n    });\n};\n\nconst changeRuntimeType = () => {\n    runtimeReq.value.type = website.value.runtimeType;\n    website.value.appinstall.advanced = false;\n    website.value.runtimeID = undefined;\n    getRuntimes();\n};\n\nconst changeRuntime = (runID: number) => {\n    website.value.port = 0;\n    runtimes.value.forEach((item) => {\n        if (item.id === runID) {\n            runtimeResource.value = item.resource;\n            if (runtimeResource.value == 'local') {\n                website.value.port = 9000;\n            } else {\n                runtimePorts.value = item.port.split(',').map((port: string) => parseInt(port.trim(), 10));\n                if (runtimePorts.value.length > 0) {\n                    website.value.port = runtimePorts.value[0];\n                }\n            }\n        }\n    });\n};\n\nconst getRuntimes = async () => {\n    website.value.port = 0;\n    try {\n        const res = await SearchRuntimes(runtimeReq.value);\n        runtimes.value = res.data.items || [];\n        if (runtimes.value.length > 0) {\n            const first = runtimes.value[0];\n            website.value.runtimeID = first.id;\n            runtimeResource.value = first.resource;\n            if (first.port != '') {\n                runtimePorts.value = first.port.split(',').map((port: string) => parseInt(port.trim(), 10));\n                if (runtimePorts.value.length > 0) {\n                    website.value.port = runtimePorts.value[0];\n                }\n            }\n        }\n    } catch (error) {}\n};\n\nconst acceptParams = async (openrestyVersion: string) => {\n    versionNotMatch.value = false;\n    if (!compareVersion(openrestyVersion, '1.27.1.2-3-3-focal')) {\n        versionNotMatch.value = true;\n    }\n    website.value = initData();\n    if (websiteForm.value) {\n        websiteForm.value.resetFields();\n    }\n    const websiteType = localStorage.getItem('website-type') || 'deployment';\n    website.value.type = websiteType;\n    const dirRes = await getPathByType('websiteDir');\n    staticPath.value = dirRes.data + '/sites/';\n    changeType(websiteType);\n\n    runtimeResource.value = 'appstore';\n    runtimeReq.value = initRuntimeReq();\n    listAcmeAccount();\n\n    steamConfig.value = initLbForm();\n\n    open.value = true;\n};\n\nconst changeAppType = (type: string) => {\n    if (type === 'installed') {\n        searchAppInstalled('website');\n    } else {\n        searchAppList();\n    }\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLog.value.acceptParams(taskID);\n};\n\nconst listAcmeAccount = () => {\n    searchAcmeAccount({ page: 1, pageSize: 100 }).then((res) => {\n        acmeAccounts.value = res.data.items || [];\n    });\n};\n\nconst changeSSl = (sslid?: number) => {\n    if (!sslid) {\n        websiteSSL.value = undefined;\n        return;\n    }\n    const selected = ssls.value.find((element) => element.id === sslid);\n    websiteSSL.value = selected;\n};\n\nconst applySSLSelection = (sslId: number | undefined, markManual = false) => {\n    if (markManual) {\n        userSelectedSSL.value = true;\n    }\n    if (!sslId) {\n        website.value.websiteSSLID = undefined;\n        websiteSSL.value = undefined;\n        return;\n    }\n    website.value.websiteSSLID = sslId;\n    changeSSl(sslId);\n};\n\nconst selectFirstAvailableSSL = () => {\n    const fallback = ssls.value.find((item) => item.pem !== '');\n    if (fallback) {\n        applySSLSelection(fallback.id);\n    }\n};\n\nconst normalizeDomain = (domain?: string) => {\n    if (!domain) {\n        return '';\n    }\n    return domain.split(':')[0].trim().toLowerCase();\n};\n\nconst wildcardMatches = (pattern: string, target: string) => {\n    if (!pattern.startsWith('*.')) {\n        return false;\n    }\n    const suffix = pattern.slice(1);\n    if (!suffix) {\n        return false;\n    }\n    if (!target.endsWith(suffix)) {\n        return false;\n    }\n    const suffixLabels = suffix.slice(1).split('.');\n    const targetLabels = target.split('.');\n    return targetLabels.length > suffixLabels.length;\n};\n\nconst domainMatches = (pattern: string, target: string) => {\n    if (!pattern || !target) {\n        return false;\n    }\n    if (pattern === target) {\n        return true;\n    }\n    return wildcardMatches(pattern, target);\n};\n\nconst getWebsiteDomains = (): string[] => {\n    const domains = new Set<string>();\n    const pushDomain = (value?: string) => {\n        const normalized = normalizeDomain(value);\n        if (normalized) {\n            domains.add(normalized);\n        }\n    };\n    pushDomain(website.value.primaryDomain);\n    if (Array.isArray(website.value.domains)) {\n        website.value.domains.forEach((item: any) => {\n            pushDomain(item?.domain);\n        });\n    }\n    return Array.from(domains);\n};\n\nconst getCertificateDomains = (ssl: SSLItem): string[] => {\n    const domains = new Set<string>();\n    const tokens: string[] = [];\n    if (ssl.primaryDomain) {\n        tokens.push(ssl.primaryDomain);\n    }\n    if (ssl.domains) {\n        ssl.domains\n            .replace(/\\n/g, ',')\n            .split(',')\n            .map((item) => item.trim())\n            .filter((item) => item !== '')\n            .forEach((item) => tokens.push(item));\n    }\n    tokens.forEach((token) => {\n        const normalized = normalizeDomain(token);\n        if (normalized) {\n            domains.add(normalized);\n        }\n    });\n    return Array.from(domains);\n};\n\nconst tryAutoSelectSSL = (): boolean => {\n    if (!website.value.enableSSL) {\n        return false;\n    }\n    if (userSelectedSSL.value) {\n        return false;\n    }\n    if (!ssls.value.length) {\n        return false;\n    }\n\n    const siteDomains = getWebsiteDomains();\n    if (!siteDomains.length) {\n        return false;\n    }\n\n    const candidates = ssls.value\n        .filter((ssl) => ssl.pem !== '')\n        .map((ssl) => {\n            const certDomains = getCertificateDomains(ssl);\n            if (!certDomains.length) {\n                return { ssl, ratio: 0, matchCount: 0 };\n            }\n            const matchCount = certDomains.reduce((count, domain) => {\n                return count + (siteDomains.some((candidate) => domainMatches(domain, candidate)) ? 1 : 0);\n            }, 0);\n            const ratio = certDomains.length > 0 ? matchCount / certDomains.length : 0;\n            return { ssl, ratio, matchCount };\n        })\n        .filter((item) => item.matchCount > 0 && item.ratio > 0);\n\n    if (!candidates.length) {\n        return false;\n    }\n\n    candidates.sort((a, b) => {\n        if (b.ratio !== a.ratio) {\n            return b.ratio - a.ratio;\n        }\n        if (b.matchCount !== a.matchCount) {\n            return b.matchCount - a.matchCount;\n        }\n        return b.ssl.id - a.ssl.id;\n    });\n\n    const best = candidates[0];\n    if (!best) {\n        return false;\n    }\n    if (website.value.websiteSSLID !== best.ssl.id) {\n        applySSLSelection(best.ssl.id);\n    } else {\n        changeSSl(best.ssl.id);\n    }\n    return true;\n};\n\nconst handleSSLSelectChange = (sslId: number | undefined) => {\n    applySSLSelection(sslId, true);\n};\n\nconst listSSLs = () => {\n    listSSL({\n        acmeAccountID: String(website.value.acmeAccountID),\n    }).then((res) => {\n        ssls.value = res.data || [];\n        website.value.websiteSSLID = undefined;\n        websiteSSL.value = undefined;\n        userSelectedSSL.value = false;\n        const autoSelected = tryAutoSelectSSL();\n        if (!autoSelected) {\n            selectFirstAvailableSSL();\n        }\n    });\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate(async (valid) => {\n        if (!valid) {\n            return;\n        }\n        if (website.value.type == 'deployment' && website.value.appType === 'new') {\n            const isValid = await installFormRef.value?.validate();\n            if (!isValid) return;\n        }\n        if (website.value.type === 'runtime' && website.value.runtimeType !== 'php' && website.value.port == 0) {\n            MsgError(i18n.global.t('website.runtimePortWarn'));\n            return;\n        }\n        if (website.value.type == 'stream') {\n            const isValid = await lbFormRef.value?.validate();\n            if (!isValid) return;\n        }\n        loading.value = true;\n        try {\n            const res = await preCheck({});\n            if (res.data) {\n                preCheckRef.value.acceptParams({ items: res.data });\n            } else {\n                if (website.value.type === 'proxy') {\n                    website.value.proxy = website.value.proxyProtocol + website.value.proxyAddress;\n                }\n                if (!website.value.enableFtp) {\n                    website.value.ftpUser = '';\n                    website.value.ftpPassword = '';\n                }\n                if (website.value.type == 'stream') {\n                    website.value.name = steamConfig.value.name;\n                    website.value.algorithm = steamConfig.value.algorithm;\n                    website.value.servers = steamConfig.value.servers;\n                }\n                const taskID = uuidv4();\n                website.value.taskID = taskID;\n                await createWebsite(website.value);\n                MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                handleClose();\n                openTaskLog(taskID);\n            }\n        } catch (error) {\n        } finally {\n            loading.value = false;\n        }\n    });\n};\n\nwatch(\n    () => website.value.domains,\n    () => {\n        tryAutoSelectSSL();\n    },\n    { deep: true },\n);\n\nwatch(\n    () => website.value.primaryDomain,\n    () => {\n        tryAutoSelectSSL();\n    },\n);\n\nwatch(\n    () => website.value.enableSSL,\n    (enabled) => {\n        if (!enabled) {\n            applySSLSelection(undefined);\n            userSelectedSSL.value = false;\n            return;\n        }\n        tryAutoSelectSSL();\n    },\n);\n\nwatch(\n    () => website.value.alias,\n    (value) => {\n        if (website.value.type === 'stream') {\n            steamConfig.value.name = value;\n        }\n    },\n    { deep: true },\n);\n\nconst handleFirstDomainBlur = (index: number) => {\n    if (index !== 0) {\n        return;\n    }\n    const firstDomain = website.value.domains?.[0]?.domain || '';\n    fillAliasFromDomainIfEmpty(firstDomain);\n};\n\nconst fillAliasFromDomainIfEmpty = (value: string) => {\n    if (!value || website.value.alias.trim() !== '') {\n        return;\n    }\n    const domain = value.split(':')[0].trim();\n    if (!domain) {\n        return;\n    }\n    website.value.alias = domain;\n};\n\nconst listWebsites = async () => {\n    try {\n        const res = await getWebsiteOptions({ types: ['static', 'runtime'] });\n        parentWebsites.value = res.data;\n        if (res.data.length > 0) {\n            website.value.parentWebsiteID = res.data[0].id;\n            getDir(res.data[0].id);\n        }\n    } catch (error) {}\n};\n\nconst getDir = async (websiteID: number) => {\n    try {\n        const res = await getDirConfig({ id: websiteID });\n        dirs.value = res.data.dirs;\n        if (res.data.dirs.length > 0) {\n            website.value.siteDir = res.data.dirs[0];\n        }\n    } catch (error) {}\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.runtimeName {\n    width: 250px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    display: inline-block;\n}\n.tagClass {\n    float: right;\n    margin-right: 10px;\n    font-size: 12px;\n    margin-top: 5px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/website/create/site-alert/index.vue",
    "content": "<template>\n    <div>\n        <el-alert\n            v-if=\"websiteType == 'deployment'\"\n            :title=\"$t('website.websiteDeploymentHelper')\"\n            type=\"info\"\n            :closable=\"false\"\n        />\n        <el-alert\n            v-if=\"websiteType == 'static'\"\n            :title=\"$t('website.websiteStatictHelper')\"\n            type=\"info\"\n            :closable=\"false\"\n        />\n        <el-alert\n            v-if=\"websiteType == 'proxy'\"\n            :title=\"$t('website.websiteProxyHelper')\"\n            type=\"info\"\n            :closable=\"false\"\n        />\n        <el-alert\n            v-if=\"websiteType == 'runtime'\"\n            :title=\"$t('website.runtimeProxyHelper')\"\n            type=\"info\"\n            :closable=\"false\"\n        />\n        <el-alert v-if=\"websiteType == 'subsite'\" :title=\"$t('website.subsiteHelper')\" type=\"info\" :closable=\"false\" />\n        <el-alert\n            v-if=\"websiteType == 'stream'\"\n            :title=\"\n                versionNotMatch ? $t('xpack.waf.openRestyAlert', ['1.27.1.2-2-3-focal']) : $t('website.streamHelper')\n            \"\n            :type=\"versionNotMatch ? 'error' : 'info'\"\n            :closable=\"false\"\n        />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\ndefineProps({\n    websiteType: {\n        type: String,\n        default: 'deployment',\n    },\n    versionNotMatch: {\n        type: Boolean,\n        default: false,\n    },\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/default/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('website.defaultServer')\" size=\"normal\" @close=\"handleClose\">\n        <el-form @submit.prevent label-position=\"top\" v-loading=\"loading\">\n            <el-form-item :label=\"$t('website.defaultServer')\">\n                <el-select v-model=\"defaultId\">\n                    <el-option :value=\"0\" :key=\"-1\" :label=\"$t('website.noDefaultServer')\"></el-option>\n                    <el-option\n                        v-for=\"(website, key) in websites\"\n                        :key=\"key\"\n                        :value=\"website.id\"\n                        :label=\"website.primaryDomain\"\n                    ></el-option>\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <el-alert :closable=\"false\">\n            <template #default>\n                <span class=\"whitespace-pre-line\">{{ $t('website.defaultServerHelper') }}</span>\n            </template>\n        </el-alert>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { Website } from '@/api/interface/website';\nimport { changeDefaultServer, listWebsites } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\n\nlet open = ref(false);\nlet websites = ref<any>();\nlet defaultId = ref(-1);\nlet loading = ref(false);\n\nconst acceptParams = () => {\n    defaultId.value = 0;\n    get();\n    open.value = true;\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst get = async () => {\n    const res = await listWebsites();\n    websites.value = res.data;\n    websites.value.forEach((website: Website.WebsiteDTO) => {\n        if (website.defaultServer) {\n            defaultId.value = website.id;\n        }\n    });\n};\n\nconst submit = () => {\n    loading.value = true;\n    changeDefaultServer({ id: defaultId.value })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            handleClose();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\ndefineExpose({ acceptParams });\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/delete/index.vue",
    "content": "<template>\n    <DialogPro v-model=\"open\" :title=\"$t('website.delete') + ' - ' + websiteName\" size=\"small\" @close=\"handleClose\">\n        <div :key=\"key\" v-loading=\"loading\">\n            <el-form ref=\"deleteForm\" label-position=\"left\">\n                <el-form-item>\n                    <el-checkbox v-model=\"deleteReq.forceDelete\" :label=\"$t('website.forceDelete')\" />\n                    <span class=\"input-help\">\n                        {{ $t('website.forceDeleteHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item v-if=\"type === 'deployment'\">\n                    <el-checkbox v-model=\"deleteReq.deleteApp\" :label=\"$t('website.deleteApp')\" />\n                    <span class=\"input-help\">\n                        {{ $t('website.deleteAppHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item>\n                    <el-checkbox v-model=\"deleteReq.deleteBackup\" :label=\"$t('website.deleteBackup')\" />\n                    <span class=\"input-help\">\n                        {{ $t('website.deleteBackupHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item v-if=\"type === 'runtime'\">\n                    <el-checkbox v-model=\"deleteReq.deleteDB\" :label=\"$t('app.deleteDB')\" />\n                    <span class=\"input-help\">\n                        {{ $t('website.deleteDatabaseHelper') }}\n                    </span>\n                </el-form-item>\n                <el-form-item v-if=\"subSites != ''\">\n                    <span class=\"input-help text-red-500\">\n                        {{ $t('website.deleteSubsite', [subSites]) }}\n                    </span>\n                </el-form-item>\n                <el-form-item>\n                    <span v-html=\"deleteHelper\"></span>\n                    <el-input v-model=\"deleteInfo\" :placeholder=\"websiteName\" />\n                </el-form-item>\n            </el-form>\n        </div>\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading || deleteInfo != websiteName\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </span>\n        </template>\n    </DialogPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { deleteWebsite } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { FormInstance } from 'element-plus';\nimport { ref } from 'vue';\nimport { Website } from '@/api/interface/website';\nimport { MsgSuccess } from '@/utils/message';\n\nconst initData = () => ({\n    id: 0,\n    deleteApp: false,\n    deleteBackup: false,\n    forceDelete: false,\n    deleteDB: false,\n});\nconst key = 1;\nconst open = ref(false);\nconst loading = ref(false);\nconst deleteReq = ref(initData());\nconst type = ref('');\nconst em = defineEmits(['close']);\nconst deleteForm = ref<FormInstance>();\nconst deleteInfo = ref('');\nconst websiteName = ref('');\nconst deleteHelper = ref('');\nconst subSites = ref('');\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = async (website: Website.WebsiteDTO) => {\n    deleteReq.value = initData();\n\n    subSites.value = '';\n    if (website.childSites && website.childSites.length > 0) {\n        subSites.value = website.childSites.join(',');\n    }\n    deleteInfo.value = '';\n    deleteReq.value.id = website.id;\n    websiteName.value = website.primaryDomain;\n    deleteHelper.value = i18n.global.t('website.deleteConfirmHelper', [website.primaryDomain]);\n    type.value = website.type;\n    open.value = true;\n};\n\nconst submit = () => {\n    loading.value = true;\n    deleteWebsite(deleteReq.value)\n        .then(() => {\n            handleClose();\n            MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/domain/index.vue",
    "content": "<template>\n    <div class=\"name-row\">\n        <div>\n            <el-form :model=\"formData\" :rules=\"rules\" ref=\"formRef\" v-if=\"isEditing\" @submit.prevent>\n                <el-form-item prop=\"domainName\" class=\"inline-form-item\">\n                    <el-input\n                        v-model=\"formData.domainName\"\n                        @keyup.enter.prevent=\"saveEdit\"\n                        @blur=\"saveEdit\"\n                        @keyup.esc=\"cancelEdit\"\n                        class=\"domain-input\"\n                        ref=\"inputRef\"\n                    />\n                </el-form-item>\n            </el-form>\n            <el-text v-else type=\"primary\" class=\"cursor-pointer\" @click=\"openConfig(row.id)\">\n                {{ row.primaryDomain }}\n                <span class=\"text-gray-400\" v-if=\"isPunycoded(row.primaryDomain)\">\n                    ({{ GetPunyCodeDomain(row.primaryDomain) }})\n                </span>\n            </el-text>\n            <el-popover\n                placement=\"right\"\n                trigger=\"hover\"\n                :width=\"popoverWidth\"\n                @before-enter=\"searchDomains(row.id)\"\n                v-if=\"row.type != 'stream'\"\n            >\n                <template #reference>\n                    <el-button link icon=\"Promotion\" class=\"ml-2.5\"></el-button>\n                </template>\n                <table>\n                    <tbody>\n                        <tr v-for=\"(domain, index) in domains\" :key=\"index\">\n                            <td>\n                                <el-button type=\"primary\" link @click=\"openUrl(getUrl(domain, row))\">\n                                    {{ getUrl(domain, row) }}\n                                </el-button>\n                            </td>\n                            <td>\n                                <CopyButton :content=\"getUrl(domain, row)\" />\n                            </td>\n                        </tr>\n                    </tbody>\n                </table>\n            </el-popover>\n            <el-button link icon=\"edit\" class=\"ml-2.5\" @click=\"startEdit\" v-if=\"!isEditing\"></el-button>\n        </div>\n        <div>\n            <el-tooltip effect=\"dark\" :content=\"$t('website.cancelFavorite')\" placement=\"top-start\" v-if=\"row.favorite\">\n                <el-button link size=\"large\" icon=\"StarFilled\" type=\"warning\" @click=\"favoriteWebsite(row)\"></el-button>\n            </el-tooltip>\n\n            <el-tooltip\n                effect=\"dark\"\n                :content=\"$t('website.favorite')\"\n                placement=\"top-start\"\n                v-if=\"!row.favorite && isHovered\"\n            >\n                <el-button link icon=\"Star\" type=\"info\" @click=\"favoriteWebsite(row)\"></el-button>\n            </el-tooltip>\n        </div>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, nextTick, computed } from 'vue';\nimport { listDomains } from '@/api/modules/website';\nimport { Website } from '@/api/interface/website';\nimport { routerToNameWithParams } from '@/utils/router';\nimport { Rules } from '@/global/form-rules';\nimport { GetPunyCodeDomain, isPunycoded } from '@/utils/util';\n\ninterface Props {\n    row: Website.Website;\n    isHovered: boolean;\n    defaultHttpPort: number;\n    defaultHttpsPort: number;\n}\nconst props = defineProps<Props>();\nconst emit = defineEmits(['favoriteChange', 'domainEdit']);\nconst inputRef = ref();\nconst isEditing = ref(false);\nconst domains = ref<Website.Domain[]>([]);\nconst formData = reactive({\n    domainName: '',\n});\nconst rules = ref({\n    domainName: [Rules.requiredInput, Rules.linuxName],\n});\nconst formRef = ref();\n\nconst ipv6Regex =\n    /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;\n\nconst isIPv6 = (domain: string): boolean => {\n    const cleanDomain = domain.replace(/^\\[|\\]$/g, '');\n    return ipv6Regex.test(cleanDomain);\n};\n\nconst popoverWidth = computed(() => {\n    if (domains.value.length === 0) return 300;\n\n    let maxLength = 0;\n    domains.value.forEach((domain) => {\n        const url = getUrl(domain, props.row);\n        maxLength = Math.max(maxLength, url.length);\n    });\n\n    const calculatedWidth = 200 + maxLength * 8 + 60 + 40;\n\n    return Math.min(Math.max(calculatedWidth, 300), 800);\n});\n\nconst startEdit = () => {\n    formData.domainName = props.row.primaryDomain;\n    isEditing.value = true;\n    nextTick(() => {\n        inputRef.value?.focus();\n        inputRef.value?.select();\n    });\n};\n\nconst saveEdit = async () => {\n    await formRef.value.validate((valid) => {\n        if (valid) {\n            const editValue = formData.domainName.trim();\n            if (editValue && editValue !== props.row.primaryDomain) {\n                emit('domainEdit', props.row, editValue);\n            }\n            isEditing.value = false;\n        }\n    });\n};\n\nconst cancelEdit = () => {\n    formData.domainName = props.row.primaryDomain;\n    isEditing.value = false;\n};\n\nconst openConfig = (id: number) => {\n    routerToNameWithParams('WebsiteConfig', { id: id, tab: 'basic' });\n};\n\nconst searchDomains = (id: number) => {\n    listDomains(id).then((res) => {\n        domains.value = res.data;\n    });\n};\n\nconst openUrl = (url: string) => {\n    window.open(url);\n};\n\nconst getUrl = (domain: Website.Domain, website: Website.Website): string => {\n    const protocol = website.protocol.toLowerCase();\n    let domainStr = domain.domain;\n\n    const cleanDomain = domainStr.replace(/^\\[|\\]$/g, '');\n\n    if (isIPv6(cleanDomain)) {\n        domainStr = `[${cleanDomain}]`;\n    }\n\n    let url = `${protocol}://${domainStr}`;\n\n    if (protocol === 'http' && domain.port && domain.port !== 80) {\n        url = `${url}:${domain.port}`;\n    } else if (protocol === 'https') {\n        let port = domain.port;\n        if (!domain.ssl) {\n            port = props.defaultHttpsPort || 443;\n        }\n        if (port && port !== 443) {\n            url = `${url}:${port}`;\n        }\n    }\n\n    return url;\n};\n\nconst favoriteWebsite = (row: Website.Website) => {\n    emit('favoriteChange', row);\n};\n</script>\n\n<style lang=\"css\" scoped>\n.name-row {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    width: 100%;\n}\n:deep(.el-form) {\n    margin: 0;\n    line-height: 1;\n}\n:deep(.el-form-item) {\n    margin-bottom: 0;\n}\n:deep(.el-form-item__error) {\n    position: absolute;\n    top: 100%;\n    left: 0;\n    padding-top: 2px;\n}\n\n.domain-input {\n    width: 200px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/website/domain-create/index.vue",
    "content": "<template>\n    <div>\n        <el-row :gutter=\"22\" v-for=\"(domain, index) of create.domains\" :key=\"index\">\n            <el-col :span=\"6\">\n                <el-form-item\n                    :label=\"index == 0 ? $t('website.domain') : ''\"\n                    :prop=\"`domains.${index}.domain`\"\n                    :rules=\"rules.domain\"\n                >\n                    <el-input\n                        type=\"string\"\n                        v-model=\"create.domains[index].domain\"\n                        :placeholder=\"index > 0 ? $t('website.domain') : ''\"\n                        @blur=\"handleDomainBlur(index)\"\n                    ></el-input>\n                    <span class=\"input-help\" v-if=\"domainWarnings[index]\">{{ $t('website.domainNotFQDN') }}</span>\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"6\">\n                <el-form-item :label=\"index == 0 ? $t('toolbox.device.hostname') : ''\">\n                    <el-input\n                        type=\"string\"\n                        :model-value=\"create.domains[index].host\"\n                        :placeholder=\"index > 0 ? $t('toolbox.device.hostname') : ''\"\n                        disabled\n                    ></el-input>\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"4\">\n                <el-form-item\n                    :label=\"index == 0 ? $t('commons.table.port') : ''\"\n                    :prop=\"`domains.${index}.port`\"\n                    :rules=\"rules.port\"\n                >\n                    <el-input type=\"number\" v-model.number=\"create.domains[index].port\"></el-input>\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"2\">\n                <el-form-item :label=\"index == 0 ? 'SSL' : ''\" :prop=\"`domains.${index}.ssl`\">\n                    <el-checkbox\n                        v-model=\"create.domains[index].ssl\"\n                        :disabled=\"create.domains[index].port == 80\"\n                    ></el-checkbox>\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"4\" v-if=\"index == 0\">\n                <el-form-item :label=\"$t('commons.table.operate')\">\n                    <el-button @click=\"addDomain\">\n                        <el-icon><Plus /></el-icon>\n                    </el-button>\n                </el-form-item>\n            </el-col>\n            <el-col :span=\"4\" v-else>\n                <el-form-item>\n                    <el-button @click=\"removeDomain(index)\">\n                        <el-icon><Delete /></el-icon>\n                    </el-button>\n                </el-form-item>\n            </el-col>\n        </el-row>\n        <el-button @click=\"openBatchDialog\" type=\"primary\" plain>\n            {{ $t('website.batchInput') }}\n        </el-button>\n\n        <el-dialog v-model=\"batchDialogVisible\" :title=\"$t('website.batchAdd')\" width=\"600px\">\n            <el-input\n                type=\"textarea\"\n                :rows=\"8\"\n                v-model=\"create.domainStr\"\n                :placeholder=\"$t('website.domainBatchHelper')\"\n            ></el-input>\n            <template #footer>\n                <el-button @click=\"batchDialogVisible = false\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"saveBatchInput\" :disabled=\"create.domainStr == ''\">\n                    {{ $t('commons.button.confirm') }}\n                </el-button>\n            </template>\n        </el-dialog>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { checkAppInstalled } from '@/api/modules/app';\nimport { Rules, checkNumberRange } from '@/global/form-rules';\nimport { MsgError } from '@/utils/message';\nimport { toASCII } from 'punycode';\nimport { ref, onMounted, nextTick } from 'vue';\nimport { useI18n } from 'vue-i18n';\n\nconst i18n = useI18n();\n\nconst props = defineProps({\n    form: {\n        type: Object,\n        default: function () {\n            return {};\n        },\n    },\n});\n\nconst emit = defineEmits(['gengerate', 'domain-blur']);\n\nconst ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;\n\nconst ipv6Regex =\n    /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3}(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;\n\nconst singleDomainRegex = /^(?:\\*|[\\w\\u4e00-\\u9fa5-]{1,63})(?:\\.(?:\\*|[\\w\\u4e00-\\u9fa5-]{1,63}))*$/;\nconst FQDNDomainRegex =\n    /^(?:\\*\\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,63}(?::(6553[0-5]|655[0-2]\\d|65[0-4]\\d{2}|6[0-4]\\d{3}|[1-5]?\\d{1,4}))?$/;\n\nconst isIPv4 = (str: string): boolean => {\n    return ipv4Regex.test(str);\n};\n\nconst isIPv6 = (str: string): boolean => {\n    const cleanStr = str.replace(/^\\[|\\]$/g, '');\n    return ipv6Regex.test(cleanStr);\n};\n\nconst isFQDN = (domain: string): boolean => {\n    if (!domain) return true;\n    return FQDNDomainRegex.test(domain);\n};\n\nfunction toPunycode(hostname: string): string {\n    try {\n        if (isIPv4(hostname) || isIPv6(hostname.replace(/^\\[|\\]$/g, ''))) {\n            return hostname;\n        }\n\n        const convertLabels = (value: string): string => {\n            return value\n                .split('.')\n                .map((label) => (label === '*' || label === '' ? label : toASCII(label)))\n                .join('.');\n        };\n\n        if (hostname.startsWith('*.')) {\n            const domainPart = hostname.substring(2);\n            return `*.${convertLabels(domainPart)}`;\n        }\n        if (hostname.endsWith('.*')) {\n            const domainPart = hostname.slice(0, -2);\n            return `${convertLabels(domainPart)}.*`;\n        }\n        return convertLabels(hostname);\n    } catch {\n        return hostname;\n    }\n}\n\nconst handleDomainBlur = (index: number) => {\n    const originalDomain = create.value.domains[index].domain;\n    if (!originalDomain) {\n        create.value.domains[index].host = '';\n        domainWarnings.value[index] = false;\n        emit('domain-blur', index);\n        return;\n    }\n\n    const cleanDomain = originalDomain.replace(/^\\[|\\]$/g, '');\n    if (isIPv4(cleanDomain) || isIPv6(cleanDomain)) {\n        create.value.domains[index].host = originalDomain;\n        create.value.domains[index].domain = originalDomain;\n        domainWarnings.value[index] = false;\n        emit('domain-blur', index);\n        return;\n    }\n\n    let singleRegexChecked = singleDomainRegex.test(originalDomain);\n    if (!singleRegexChecked) {\n        domainWarnings.value[index] = false;\n        emit('domain-blur', index);\n        return;\n    }\n\n    const punycoded = toPunycode(originalDomain);\n    if (punycoded !== originalDomain) {\n        create.value.domains[index].host = originalDomain;\n        create.value.domains[index].domain = punycoded;\n    } else {\n        create.value.domains[index].host = originalDomain;\n    }\n\n    if (singleRegexChecked) {\n        domainWarnings.value[index] = !isFQDN(create.value.domains[index].domain);\n    } else {\n        domainWarnings.value[index] = false;\n    }\n    emit('domain-blur', index);\n};\n\nconst validateSingleDomain = (_rule: any, value: string, callback: (error?: Error) => void) => {\n    if (!value) {\n        return callback();\n    }\n\n    const cleanValue = value.replace(/^\\[|\\]$/g, '');\n\n    if (isIPv4(cleanValue) || isIPv6(cleanValue) || singleDomainRegex.test(value)) {\n        return callback();\n    }\n\n    callback(new Error(i18n.t('website.domainInvalid')));\n};\n\nconst rules = ref({\n    port: [Rules.requiredInput, Rules.paramPort, checkNumberRange(1, 65535)],\n    domain: [Rules.requiredInput, { validator: validateSingleDomain, trigger: 'blur' }],\n    domains: {\n        type: Array,\n    },\n});\nconst defaultPort = ref(80);\nconst batchDialogVisible = ref(false);\nconst domainWarnings = ref<{ [key: number]: boolean }>({});\n\nconst initDomain = () => ({\n    domain: '',\n    host: '',\n    port: defaultPort.value,\n    ssl: false,\n});\nconst create = ref({\n    websiteID: 0,\n    domains: [initDomain()],\n    domainStr: '',\n});\n\nconst domainToString = (domain: { domain: string; port: number; ssl: boolean }): string => {\n    if (!domain.domain) return '';\n    let str = domain.domain;\n\n    if (domain.port && domain.port !== 80 && isIPv6(str.replace(/^\\[|\\]$/g, ''))) {\n        if (!str.startsWith('[')) {\n            str = `[${str}]`;\n        }\n    }\n\n    if (domain.port && domain.port !== 80) {\n        str += `:${domain.port}`;\n    }\n    return str;\n};\n\nconst stringToDomain = (line: string): { domain: string; host: string; port: number; ssl: boolean } | null => {\n    if (!line.trim()) return null;\n\n    let ssl = false;\n    let str = line.trim();\n    let domainRaw = '';\n    let portStr = '';\n\n    const ipv6Match = str.match(/^\\[([^\\]]+)\\](?::(\\d+))?$/);\n    if (ipv6Match) {\n        domainRaw = ipv6Match[1];\n        portStr = ipv6Match[2] || '';\n    } else {\n        const parts = str.split(':');\n        if (parts.length === 2) {\n            domainRaw = parts[0];\n            portStr = parts[1];\n        } else if (parts.length === 1) {\n            domainRaw = parts[0];\n        } else {\n            if (isIPv6(str)) {\n                domainRaw = str;\n            } else {\n                return null;\n            }\n        }\n    }\n\n    if (!domainRaw) return null;\n\n    const cleanDomain = domainRaw.replace(/^\\[|\\]$/g, '');\n    let domain: string;\n    let host: string;\n\n    if (isIPv4(cleanDomain) || isIPv6(cleanDomain)) {\n        domain = domainRaw;\n        host = domainRaw;\n    } else {\n        domain = toPunycode(domainRaw);\n        host = domain !== domainRaw ? domainRaw : domain;\n    }\n\n    const port = portStr ? Number(portStr) : defaultPort.value;\n\n    if (port === 443) {\n        ssl = true;\n    } else if (port === 80) {\n        ssl = false;\n    }\n\n    return { domain, host, port, ssl };\n};\n\nconst openBatchDialog = () => {\n    const lines = create.value.domains.map(domainToString).filter((line) => line !== '');\n    create.value.domainStr = lines.join('\\n');\n    batchDialogVisible.value = true;\n};\n\nconst addDomain = () => {\n    create.value.domains.push(initDomain());\n};\n\nconst removeDomain = (index: number) => {\n    create.value.domains.splice(index, 1);\n};\n\nconst saveBatchInput = () => {\n    if (create.value.domainStr.trim() === '') {\n        return true;\n    }\n    const lines = create.value.domainStr.split(/\\r?\\n/);\n    const newDomains: { domain: string; host: string; port: number; ssl: boolean }[] = [];\n    let hasError = false;\n\n    const isFirstBatchInput = create.value.domains.length === 1 && !create.value.domains[0].domain;\n\n    lines.forEach((line, index) => {\n        const parsed = stringToDomain(line);\n        if (!parsed) return;\n\n        const { domain, host, port, ssl } = parsed;\n        const cleanDomain = domain.replace(/^\\[|\\]$/g, '');\n\n        if (!domain.trim() || (!isIPv4(cleanDomain) && !isIPv6(cleanDomain) && !singleDomainRegex.test(domain))) {\n            MsgError(line + ' ' + i18n.t('website.domainInvalid'));\n            hasError = true;\n            return;\n        }\n\n        if (!newDomains.some((d) => d.domain === domain && d.port === port)) {\n            newDomains.push({ domain, host, port, ssl });\n        }\n\n        if (isFirstBatchInput && index === 0) {\n            const alias = domain.split(':')[0].replace(/^\\[|\\]$/g, '');\n            if (props.form.alias !== undefined) {\n                props.form.alias = alias;\n            }\n        }\n    });\n\n    if (hasError) return false;\n\n    if (newDomains.length > 0) {\n        create.value.domains = newDomains;\n    } else {\n        create.value.domains = [initDomain()];\n    }\n\n    handleParams();\n    nextTick(() => emit('gengerate'));\n\n    batchDialogVisible.value = false;\n    return true;\n};\n\nconst handleParams = () => {\n    props.form.domains = create.value.domains;\n};\n\nconst getOprensty = async () => {\n    try {\n        await checkAppInstalled('openresty', '')\n            .then((res) => {\n                defaultPort.value = res.data.httpPort || 80;\n                create.value.domains[0].port = defaultPort.value;\n            })\n            .catch(() => {});\n    } catch (error) {}\n};\n\nonMounted(() => {\n    getOprensty();\n    handleParams();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/html/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('website.defaultHtml')\" @close=\"handleClose\" size=\"normal\">\n        <el-select v-model=\"req.type\" class=\"w-full\" @change=\"get()\" v-loading=\"loading\">\n            <el-option :value=\"'404'\" :label=\"$t('website.website404')\"></el-option>\n            <el-option :value=\"'domain404'\" :label=\"$t('website.domain404')\"></el-option>\n            <el-option :value=\"'index'\" :label=\"$t('website.indexHtml')\"></el-option>\n            <el-option :value=\"'php'\" :label=\"$t('website.indexPHP')\"></el-option>\n            <el-option :value=\"'stop'\" :label=\"$t('website.stopHtml')\"></el-option>\n        </el-select>\n        <div class=\"mt-1.5\">\n            <el-text v-if=\"req.type == '404'\" type=\"info\">\n                {{ $t('website.website404Helper') }}\n            </el-text>\n        </div>\n        <div class=\"mt-1.5\">\n            <el-checkbox v-model=\"req.sync\">{{ $t('website.syncHtmlHelper') }}</el-checkbox>\n        </div>\n        <div ref=\"htmlRef\" class=\"default-html\"></div>\n\n        <template #footer>\n            <span class=\"dialog-footer\">\n                <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n                <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                    {{ $t('commons.button.save') }}\n                </el-button>\n            </span>\n        </template>\n    </DrawerPro>\n</template>\n<script lang=\"ts\" setup>\nimport { updateDefaultHtml, getDefaultHtml } from '@/api/modules/website';\nimport i18n from '@/lang';\nimport { ref } from 'vue';\nimport { MsgSuccess } from '@/utils/message';\nimport { EditorState } from '@codemirror/state';\nimport { basicSetup, EditorView } from 'codemirror';\nimport { html } from '@codemirror/lang-html';\nimport { php } from '@codemirror/lang-php';\nimport { oneDark } from '@codemirror/theme-one-dark';\n\nlet open = ref(false);\nlet loading = ref(false);\nconst content = ref('');\nconst view = ref();\nconst htmlRef = ref();\nconst req = reactive({\n    type: '404',\n    sync: false,\n});\n\nconst acceptParams = () => {\n    req.type = '404';\n    get();\n    open.value = true;\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\nconst get = async () => {\n    const res = await getDefaultHtml(req.type);\n    content.value = res.data.content;\n    initEditor();\n};\n\nconst initEditor = () => {\n    if (view.value) {\n        view.value.destroy();\n    }\n    let extensions = [basicSetup, oneDark];\n    if (req.type === 'php') {\n        extensions.push(php());\n    } else {\n        extensions.push(html());\n    }\n    const startState = EditorState.create({\n        doc: content.value,\n        extensions: extensions,\n    });\n    if (htmlRef.value) {\n        view.value = new EditorView({\n            state: startState,\n            parent: htmlRef.value,\n        });\n    }\n};\n\nconst submit = async () => {\n    loading.value = true;\n    try {\n        const content = view.value.state.doc.toString();\n        await updateDefaultHtml({ type: req.type, content: content, sync: req.sync });\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n    } catch (error) {\n    } finally {\n        loading.value = false;\n    }\n};\ndefineExpose({ acceptParams });\n</script>\n\n<style scoped lang=\"scss\">\n.default-html {\n    width: 100%;\n    min-height: 300px;\n}\n</style>\n"
  },
  {
    "path": "frontend/src/views/website/website/index.vue",
    "content": "<template>\n    <div>\n        <RouterButton\n            :buttons=\"[\n                {\n                    label: i18n.global.t('menu.website', 2),\n                    path: '/websites',\n                },\n            ]\"\n        />\n        <LayoutContent :title=\"$t('menu.website', 2)\" v-loading=\"loading\">\n            <template #app>\n                <AppStatus\n                    :app-key=\"'openresty'\"\n                    @setting=\"setting\"\n                    v-model:mask-show=\"maskShow\"\n                    v-model:loading=\"loading\"\n                    @is-exist=\"checkExist\"\n                    ref=\"appStatusRef\"\n                ></AppStatus>\n            </template>\n            <template v-if=\"!openNginxConfig && nginxIsExist\" #leftToolBar>\n                <el-button type=\"primary\" @click=\"openCreate\" :disabled=\"disabledConfig\">\n                    {{ $t('commons.button.create') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"openGroup\" :disabled=\"disabledConfig\">\n                    {{ $t('commons.table.group') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"openDefault\" :disabled=\"disabledConfig\">\n                    {{ $t('website.defaultServer') }}\n                </el-button>\n                <el-button type=\"primary\" plain @click=\"openDefaultHtml\" :disabled=\"disabledConfig\">\n                    {{ $t('website.defaultHtml') }}\n                </el-button>\n            </template>\n            <template v-if=\"!openNginxConfig && nginxIsExist\" #rightToolBar>\n                <el-select class=\"p-w-200\" v-model=\"req.type\" @change=\"search()\" :disabled=\"disabledConfig\">\n                    <template #prefix>{{ $t('commons.table.type') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" :value=\"''\"></el-option>\n                    <el-option\n                        v-for=\"item in WebsiteTypes\"\n                        :label=\"item.label\"\n                        :value=\"item.value\"\n                        :key=\"item.value\"\n                    ></el-option>\n                </el-select>\n                <el-select v-model=\"req.websiteGroupId\" @change=\"search()\" class=\"p-w-200\" :disabled=\"disabledConfig\">\n                    <template #prefix>{{ $t('commons.table.group') }}</template>\n                    <el-option :label=\"$t('commons.table.all')\" :value=\"0\"></el-option>\n                    <div v-for=\"item in groups\" :key=\"item.id\">\n                        <el-option\n                            v-if=\"item.name === 'Default'\"\n                            :label=\"$t('commons.table.default')\"\n                            :value=\"item.id\"\n                        />\n                        <el-option v-else :label=\"item.name\" :value=\"item.id\" />\n                    </div>\n                </el-select>\n                <TableSearch @search=\"search()\" v-model:searchName=\"req.name\" :disabled=\"disabledConfig\" />\n                <TableRefresh @search=\"search()\" :disabled=\"disabledConfig\" />\n                <fu-table-column-select\n                    :columns=\"columns\"\n                    trigger=\"hover\"\n                    :title=\"$t('commons.table.selectColumn')\"\n                    popper-class=\"popper-class\"\n                    :only-icon=\"true\"\n                />\n            </template>\n            <template v-if=\"!openNginxConfig\" #main>\n                <ComplexTable\n                    :pagination-config=\"paginationConfig\"\n                    :data=\"data\"\n                    @sort-change=\"changeSort\"\n                    @search=\"search()\"\n                    :class=\"{ mask: disabledConfig }\"\n                    :heightDiff=\"310\"\n                    :columns=\"columns\"\n                    @cell-mouse-enter=\"showFavorite\"\n                    @cell-mouse-leave=\"hideFavorite\"\n                    localKey=\"websiteColumn\"\n                    v-model:selects=\"selects\"\n                    :tooltip-options=\"{\n                        placement: 'bottom-start',\n                    }\"\n                >\n                    <el-table-column type=\"selection\" width=\"30\" />\n                    <el-table-column\n                        :label=\"$t('commons.table.name')\"\n                        fix\n                        prop=\"primaryDomain\"\n                        min-width=\"250px\"\n                        sortable\n                        show-overflow-tooltip\n                    >\n                        <template #default=\"{ row, $index }\">\n                            <Domain\n                                :row=\"row\"\n                                :is-hovered=\"hoveredRowIndex === $index\"\n                                :defaultHttpPort=\"appStatusRef?.getHttpPort?.() || 0\"\n                                :defaultHttpsPort=\"appStatusRef?.getHttpsPort?.() || 0\"\n                                @favorite-change=\"favoriteWebsite\"\n                                @domain-edit=\"handleDomainEdit\"\n                            />\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        width=\"180px\"\n                        :label=\"$t('commons.table.type')\"\n                        fix\n                        show-overflow-tooltip\n                        prop=\"type\"\n                        sortable\n                    >\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.type\">\n                                {{ $t('website.' + row.type) }}\n                                <span v-if=\"row.type === 'deployment'\">[{{ row.appName }}]</span>\n                                <span v-if=\"row.type === 'runtime'\">[{{ row.runtimeName }}]</span>\n                                <span v-if=\"row.type === 'subsite'\">[{{ row.parentSite }}]</span>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.sitePath')\" prop=\"sitePath\" width=\"90px\">\n                        <template #default=\"{ row }\">\n                            <el-button type=\"primary\" link @click=\"routerToFileWithPath(row.sitePath + '/index')\">\n                                <el-icon>\n                                    <FolderOpened />\n                                </el-icon>\n                            </el-button>\n                        </template>\n                    </el-table-column>\n                    <el-table-column\n                        :label=\"$t('commons.table.status')\"\n                        prop=\"status\"\n                        width=\"120px\"\n                        sortable\n                        align=\"center\"\n                    >\n                        <template #default=\"{ row }\">\n                            <span v-if=\"row.type === 'stream'\">\n                                <el-text type=\"success\">\n                                    {{ $t('commons.status.' + row.status.toLocaleLowerCase()) }}\n                                </el-text>\n                            </span>\n                            <span v-else>\n                                <Status\n                                    v-if=\"row.status === 'Running'\"\n                                    :operate=\"true\"\n                                    :status=\"row.status\"\n                                    @click=\"operateWebsite('stop', row)\"\n                                />\n                                <Status\n                                    v-else\n                                    :status=\"row.status\"\n                                    :operate=\"true\"\n                                    @click=\"operateWebsite('start', row)\"\n                                />\n                            </span>\n                        </template>\n                    </el-table-column>\n\n                    <el-table-column\n                        :label=\"$t('commons.table.protocol')\"\n                        prop=\"protocol\"\n                        width=\"90px\"\n                    ></el-table-column>\n                    <el-table-column\n                        :label=\"$t('website.expireDate')\"\n                        prop=\"expireDate\"\n                        :sortable=\"'custom'\"\n                        width=\"150px\"\n                    >\n                        <template #default=\"{ row }\">\n                            <div v-if=\"row.showdate\">\n                                <el-date-picker\n                                    :key=\"row.id\"\n                                    class=\"p-w-100\"\n                                    v-model=\"row.expireDate\"\n                                    type=\"date\"\n                                    :disabled-date=\"checkDate\"\n                                    :shortcuts=\"shortcuts\"\n                                    :clearable=\"false\"\n                                    @change=\"updateWebsitConfig(row)\"\n                                    :ref=\"(el) => setdateRefs(el)\"\n                                    @visible-change=\"(visibility:boolean) => pickerVisibility(visibility, row)\"\n                                    size=\"small\"\n                                    :mounted=\"initDatePicker(row)\"\n                                ></el-date-picker>\n                            </div>\n                            <div v-else>\n                                <el-link type=\"primary\" underline=\"never\" @click.stop=\"openDatePicker(row)\">\n                                    <span v-if=\"isEver(row.expireDate)\">\n                                        {{ $t('website.neverExpire') }}\n                                    </span>\n                                    <span v-else>\n                                        {{ dateFormatSimple(row.expireDate) }}\n                                    </span>\n                                </el-link>\n                            </div>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.sslExpireDate')\" prop=\"sslExpireDate\" width=\"160px\">\n                        <template #default=\"{ row }\">\n                            <el-tag v-if=\"row.protocol == 'HTTPS'\" :type=\"row.sslStatus\">\n                                {{ dateFormatSimple(row.sslExpireDate) }}\n                            </el-tag>\n                            <span v-else></span>\n                        </template>\n                    </el-table-column>\n                    <el-table-column :label=\"$t('website.remark')\" prop=\"remark\" min-width=\"150px\">\n                        <template #default=\"{ row }\">\n                            <fu-read-write-switch>\n                                <template #read>\n                                    <MsgInfo :info=\"row.remark\" :width=\"'150'\" />\n                                </template>\n                                <template #default=\"{ read }\">\n                                    <el-input v-model=\"row.remark\" @blur=\"updateRemark(row, read)\" />\n                                </template>\n                            </fu-read-write-switch>\n                        </template>\n                    </el-table-column>\n                    <fu-table-operations\n                        :ellipsis=\"1\"\n                        width=\"180px\"\n                        :buttons=\"buttons\"\n                        :label=\"$t('commons.table.operate')\"\n                        :fixed=\"mobile ? false : 'right'\"\n                        fix\n                    />\n                    <template #footerLeft>\n                        <div class=\"footer-left-button\">\n                            <el-select class=\"p-w-200\" v-model=\"batchReq.operate\">\n                                <el-option\n                                    :label=\"$t('commons.button.start') + $t('menu.website')\"\n                                    value=\"start\"\n                                ></el-option>\n                                <el-option\n                                    :label=\"$t('commons.button.stop') + $t('menu.website')\"\n                                    value=\"stop\"\n                                ></el-option>\n                                <el-option\n                                    :label=\"$t('commons.button.delete') + $t('menu.website')\"\n                                    value=\"delete\"\n                                ></el-option>\n                                <el-option\n                                    :label=\"$t('commons.button.set') + $t('commons.table.group')\"\n                                    value=\"group\"\n                                ></el-option>\n                                <el-option\n                                    :label=\"$t('commons.button.set') + $t('website.ssl')\"\n                                    value=\"setHttps\"\n                                ></el-option>\n                            </el-select>\n                            <el-button\n                                class=\"ml-2\"\n                                type=\"primary\"\n                                :disabled=\"selects.length == 0 || batchReq.operate == ''\"\n                                @click=\"batchOp\"\n                            >\n                                {{ $t('website.batchOperate') }}\n                                <span class=\"ml-1\" v-if=\"selects.length > 0\">({{ selects.length }})</span>\n                            </el-button>\n                        </div>\n                    </template>\n                </ComplexTable>\n                <el-card width=\"30%\" v-if=\"disabledConfig && maskShow\" class=\"mask-prompt\">\n                    <span v-if=\"nginxIsExist\">\n                        {{ $t('commons.service.serviceNotStarted', ['OpenResty']) }}\n                        <el-button type=\"primary\" link @click=\"routerToFileWithPath(websiteDir)\" icon=\"FolderOpened\">\n                            {{ $t('website.toWebsiteDir') }}\n                        </el-button>\n                    </span>\n                    <span v-else>\n                        {{ $t('app.checkInstalledWarn', ['OpenResty']) }}\n                        <el-button @click=\"goRouter('openresty')\" link icon=\"Position\" type=\"primary\">\n                            {{ $t('database.goInstall') }}\n                        </el-button>\n                    </span>\n                </el-card>\n            </template>\n        </LayoutContent>\n        <CreateWebSite ref=\"createRef\" @close=\"search\" />\n        <DeleteWebsite ref=\"deleteRef\" @close=\"search\" />\n        <UploadDialog ref=\"uploadRef\" />\n        <Backups ref=\"dialogBackupRef\" />\n        <DefaultServer ref=\"defaultRef\" />\n        <GroupDialog @search=\"listGroup\" ref=\"groupRef\" />\n        <NginxConfig v-if=\"openNginxConfig\" v-loading=\"loading\" :containerName=\"containerName\" :status=\"nginxStatus\" />\n        <DefaultHtml ref=\"defaultHtmlRef\" />\n        <TaskLog ref=\"taskLogRef\" @close=\"search\" />\n        <OpDialog ref=\"opRef\" @search=\"openTaskLog\" />\n        <BatchSetGroup ref=\"batchSetGroupRef\" @close=\"search\" />\n        <BatchSetHttps ref=\"batchSetHttpsRef\" @open-task=\"openTaskLog\" />\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport Backups from '@/components/backup/index.vue';\nimport UploadDialog from '@/components/upload/index.vue';\nimport DefaultServer from '@/views/website/website/default/index.vue';\nimport DefaultHtml from '@/views/website/website/html/index.vue';\nimport CreateWebSite from '@/views/website/website/create/index.vue';\nimport DeleteWebsite from '@/views/website/website/delete/index.vue';\nimport NginxConfig from '@/views/website/website/nginx/index.vue';\nimport GroupDialog from '@/components/agent-group/index.vue';\nimport AppStatus from '@/components/app-status/index.vue';\nimport TaskLog from '@/components/log/task/index.vue';\nimport Domain from '@/views/website/website/domain/index.vue';\nimport BatchSetGroup from '@/views/website/website/batch-op/group.vue';\nimport BatchSetHttps from '@/views/website/website/batch-op/https.vue';\n\nimport i18n from '@/lang';\nimport { onMounted, reactive, ref, computed } from 'vue';\nimport { batchOperate, opWebsite, searchWebsites, updateWebsite } from '@/api/modules/website';\nimport { Website } from '@/api/interface/website';\nimport { App } from '@/api/interface/app';\nimport { ElMessageBox } from 'element-plus';\nimport { dateFormatSimple, newUUID } from '@/utils/util';\nimport { MsgError, MsgSuccess } from '@/utils/message';\nimport { useI18n } from 'vue-i18n';\nimport { getAgentGroupList } from '@/api/modules/group';\nimport { Group } from '@/api/interface/group';\nimport { GlobalStore } from '@/store';\nimport { getWebsiteTypes } from '@/global/mimetype';\nimport { routerToFileWithPath, routerToNameWithParams, routerToNameWithQuery } from '@/utils/router';\nconst globalStore = GlobalStore();\n\nconst shortcuts = [\n    {\n        text: useI18n().t('website.ever'),\n        value: () => {\n            return new Date('2006-01-02');\n        },\n    },\n    {\n        text: i18n.global.t('website.nextYear'),\n        value: () => {\n            const now = new Date();\n            now.setFullYear(now.getFullYear() + 1);\n            return now;\n        },\n    },\n];\nconst WebsiteTypes = getWebsiteTypes();\nconst loading = ref(false);\nconst maskShow = ref(false);\nconst createRef = ref();\nconst deleteRef = ref();\nconst groupRef = ref();\nconst defaultHtmlRef = ref();\nconst openNginxConfig = ref(false);\nconst nginxIsExist = ref(false);\nconst containerName = ref('');\nconst nginxStatus = ref('');\nconst uploadRef = ref();\nconst dialogBackupRef = ref();\nconst defaultRef = ref();\nconst data = ref();\nlet groups = ref<Group.GroupInfo[]>([]);\nconst dataRef = ref();\nconst columns = ref([]);\nconst hoveredRowIndex = ref(-1);\nconst websiteDir = ref();\nconst selects = ref([]);\nconst batchReq = reactive({\n    operate: '',\n    ids: [] as number[],\n    taskID: '',\n});\nconst taskLogRef = ref();\nconst opRef = ref();\nconst batchSetGroupRef = ref();\nconst batchSetHttpsRef = ref();\nconst nginxVersion = ref();\nconst appStatusRef = ref();\n\nconst paginationConfig = reactive({\n    cacheSizeKey: 'website-page-size',\n    currentPage: 1,\n    pageSize: Number(localStorage.getItem('website-page-size')) || 20,\n    total: 0,\n});\nlet req = reactive({\n    name: '',\n    page: 1,\n    pageSize: 10,\n    orderBy: 'favorite',\n    order: 'descending',\n    websiteGroupId: 0,\n    type: '',\n});\nconst mobile = computed(() => {\n    return globalStore.isMobile();\n});\n\nconst goRouter = async (key: string) => {\n    routerToNameWithQuery('AppAll', { install: key });\n};\n\nconst showFavorite = (row: any) => {\n    hoveredRowIndex.value = data.value.findIndex((item) => item === row);\n};\n\nconst hideFavorite = () => {\n    hoveredRowIndex.value = -1;\n};\n\nconst favoriteWebsite = (row: Website.Website) => {\n    row.favorite = !row.favorite;\n    updateWebsitConfig(row);\n};\n\nconst handleDomainEdit = (row: Website.Website, domain: string) => {\n    row.primaryDomain = domain;\n    updateWebsitConfig(row);\n};\n\nconst disabledConfig = computed(() => {\n    return nginxStatus.value != 'Running';\n});\n\nconst changeSort = ({ prop, order }) => {\n    if (order) {\n        switch (prop) {\n            case 'primaryDomain':\n                prop = 'primary_domain';\n                break;\n            case 'expireDate':\n                prop = 'expire_date';\n                break;\n            default:\n                break;\n        }\n        req.orderBy = prop;\n        req.order = order;\n    } else {\n        req.orderBy = 'favorite';\n        req.order = 'descending';\n    }\n    search();\n};\n\nconst search = async () => {\n    req.page = paginationConfig.currentPage;\n    req.pageSize = paginationConfig.pageSize;\n\n    loading.value = true;\n    data.value = [];\n    await searchWebsites(req)\n        .then((res) => {\n            data.value = res.data.items;\n            paginationConfig.total = res.data.total;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst listGroup = async () => {\n    const res = await getAgentGroupList('website');\n    groups.value = res.data;\n};\n\nconst setting = () => {\n    openNginxConfig.value = true;\n};\n\nconst openConfig = (id: number) => {\n    routerToNameWithParams('WebsiteConfig', { id: id, tab: 'basic' });\n};\n\nconst isEver = (time: string) => {\n    const expireDate = new Date(time);\n    return expireDate > new Date('9999-12-30');\n};\n\nconst isBeforeNow = (time: string) => {\n    return new Date() > new Date(time);\n};\n\nconst refreshData = () => {\n    for (let i = 0; i < data.value.length; i++) {\n        if (data.value[i].showdate) {\n            data.value[i].showdate = false;\n            if (data.value[i].oldExpireDate) {\n                data.value[i].expireDate = data.value[i].oldExpireDate;\n                data.value[i].oldExpireDate = undefined;\n            }\n        }\n    }\n    if (dataRef.value) {\n        dataRef.value.handleClose();\n        dataRef.value = undefined;\n    }\n};\n\nconst openDatePicker = (row: any) => {\n    if (row.type === 'stream') {\n        return;\n    }\n    refreshData();\n    row.showdate = true;\n};\n\nconst setdateRefs = (ref: any) => {\n    dataRef.value = ref;\n    if (dataRef.value != undefined) {\n        dataRef.value.handleOpen();\n    }\n};\n\nconst initDatePicker = (row: any) => {\n    if (\n        (dataRef.value == undefined && row.oldExpireDate == undefined && isBeforeNow(row.expireDate)) ||\n        isEver(row.expireDate)\n    ) {\n        row.oldExpireDate = row.expireDate;\n        const date = new Date().toLocaleDateString();\n        row.expireDate = date;\n        return;\n    }\n};\n\nconst pickerVisibility = (visibility: boolean, row: any) => {\n    if (!visibility) {\n        dataRef.value = undefined;\n        if (row.oldExpireDate) {\n            row.expireDate = row.oldExpireDate;\n            row.oldExpireDate = undefined;\n        }\n        row.showdate = false;\n    }\n};\n\nconst updateWebsitConfig = (row: any) => {\n    let reqDate = dateFormatSimple(row.expireDate);\n    if (reqDate == '2006-01-02') {\n        reqDate = '9999-12-31';\n    }\n    const req = {\n        id: row.id,\n        primaryDomain: row.primaryDomain,\n        remark: row.remark,\n        webSiteGroupId: row.webSiteGroupId,\n        expireDate: reqDate,\n        IPV6: row.IPV6,\n        favorite: row.favorite,\n    };\n\n    updateWebsite(req).then(() => {\n        MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        search();\n    });\n};\n\nconst buttons = [\n    {\n        label: i18n.global.t('menu.config'),\n        click: function (row: Website.Website) {\n            openConfig(row.id);\n        },\n    },\n    {\n        label: i18n.global.t('database.backupList'),\n        click: (row: Website.Website) => {\n            let params = {\n                type: 'website',\n                name: row.alias,\n                detailName: row.alias,\n            };\n            dialogBackupRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('database.loadBackup'),\n        click: (row: Website.Website) => {\n            let params = {\n                type: 'website',\n                name: row.primaryDomain,\n                detailName: row.alias,\n            };\n            uploadRef.value!.acceptParams(params);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Website.Website) {\n            openDelete(row);\n        },\n    },\n];\n\nconst openDelete = (website: Website.Website) => {\n    deleteRef.value.acceptParams(website);\n};\n\nconst openCreate = () => {\n    createRef.value.acceptParams(nginxVersion.value);\n};\n\nconst openGroup = () => {\n    groupRef.value.acceptParams({ type: 'website' });\n};\n\nconst openDefault = () => {\n    defaultRef.value.acceptParams();\n};\n\nconst openDefaultHtml = () => {\n    defaultHtmlRef.value.acceptParams();\n};\n\nconst checkExist = (data: App.CheckInstalled) => {\n    nginxIsExist.value = data.isExist;\n    containerName.value = data.containerName;\n    nginxStatus.value = data.status;\n    websiteDir.value = data.websiteDir;\n    nginxVersion.value = data.version;\n};\n\nconst checkDate = (date: Date) => {\n    const now = new Date();\n    return date.getTime() < now.getTime();\n};\n\nconst operateWebsite = (op: string, row: Website.Website) => {\n    if (row.type === 'stream') {\n        return;\n    }\n    ElMessageBox.confirm(i18n.global.t('website.' + op + 'Helper'), i18n.global.t('cronjob.changeStatus'), {\n        confirmButtonText: i18n.global.t('commons.button.confirm'),\n        cancelButtonText: i18n.global.t('commons.button.cancel'),\n    }).then(async () => {\n        await opWebsite({ id: row.id, operate: op });\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n        search();\n    });\n};\n\nconst updateRemark = (row: Website.Website, bulr: Function) => {\n    bulr();\n    if (row.remark && row.remark.length > 128) {\n        MsgError(i18n.global.t('commons.rule.length128Err'));\n        return;\n    }\n    updateWebsitConfig(row);\n};\n\nconst openTaskLog = () => {\n    selects.value = [];\n    batchReq.operate = '';\n    taskLogRef.value.openWithTaskID(batchReq.taskID);\n};\n\nconst batchOp = () => {\n    switch (batchReq.operate) {\n        case 'setHttps':\n            const tID = newUUID();\n            batchReq.taskID = tID;\n            batchSetHttpsRef.value.acceptParams(\n                selects.value.map((item) => item.id),\n                tID,\n            );\n            break;\n        case 'group':\n            batchSetGroupRef.value.acceptParams(selects.value.map((item) => item.id));\n            break;\n        default:\n            const names = selects.value.map((item) => item.primaryDomain);\n            batchReq.ids = selects.value.map((item) => item.id);\n            const taskID = newUUID();\n            batchReq.taskID = taskID;\n            opRef.value.acceptParams({\n                names: names,\n                title: i18n.global.t('website.batchOperate'),\n                api: batchOperate,\n                msg: i18n.global.t('website.batchOperateHelper', [i18n.global.t('commons.button.' + batchReq.operate)]),\n                params: batchReq,\n                noMsg: true,\n            });\n            return;\n    }\n};\n\nonMounted(() => {\n    search();\n    listGroup();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/index.vue",
    "content": "<template>\n    <LayoutContent :title=\"$t('commons.button.set')\" :reload=\"true\">\n        <template #leftToolBar>\n            <el-button\n                type=\"primary\"\n                :plain=\"activeName !== '1'\"\n                @click=\"changeTab('1')\"\n                :disabled=\"status != 'Running'\"\n            >\n                {{ $t('nginx.status') }}\n            </el-button>\n            <el-button type=\"primary\" :plain=\"activeName !== '2'\" @click=\"changeTab('2')\">\n                {{ $t('nginx.configResource') }}\n            </el-button>\n            <el-button\n                type=\"primary\"\n                :plain=\"activeName !== '3'\"\n                @click=\"changeTab('3')\"\n                :disabled=\"status != 'Running'\"\n            >\n                {{ $t('website.nginxPer') }}\n            </el-button>\n            <el-button\n                type=\"primary\"\n                :plain=\"activeName !== '4'\"\n                @click=\"changeTab('4')\"\n                :disabled=\"status != 'Running'\"\n            >\n                {{ $t('commons.button.log') }}\n            </el-button>\n            <el-button type=\"primary\" :plain=\"activeName !== '5'\" @click=\"changeTab('5')\">\n                {{ $t('runtime.module') }}\n            </el-button>\n            <el-button\n                type=\"primary\"\n                :plain=\"activeName !== '6'\"\n                @click=\"changeTab('6')\"\n                :disabled=\"status != 'Running'\"\n            >\n                {{ $t('website.other') }}\n            </el-button>\n        </template>\n        <template #main>\n            <Status v-if=\"activeName === '1'\" :status=\"status\" />\n            <Source v-if=\"activeName === '2'\" />\n            <NginxPer v-if=\"activeName === '3'\" />\n            <ContainerLog v-if=\"activeName === '4'\" :container=\"containerName\" :highlightDiff=\"350\" />\n            <Module v-if=\"activeName === '5'\" />\n            <Other v-if=\"activeName === '6'\" />\n        </template>\n    </LayoutContent>\n</template>\n\n<script lang=\"ts\" setup>\nimport Source from './source/index.vue';\nimport { ref } from 'vue';\nimport ContainerLog from '@/components/log/container/index.vue';\nimport NginxPer from './performance/index.vue';\nimport Status from './status/index.vue';\nimport Module from './module/index.vue';\nimport Other from './other/index.vue';\n\nconst activeName = ref('1');\n\nconst props = defineProps({\n    containerName: {\n        type: String,\n        default: '',\n    },\n    status: {\n        type: String,\n        default: 'Running',\n    },\n});\nconst changeTab = (index: string) => {\n    activeName.value = index;\n};\n\nonMounted(() => {\n    if (props.status != 'Running') {\n        activeName.value = '2';\n    }\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/module/build/index.vue",
    "content": "<template>\n    <DrawerPro v-model=\"open\" :header=\"$t('nginx.build')\" size=\"normal\" @close=\"handleClose\">\n        <el-form ref=\"buildForm\" label-position=\"top\" :model=\"build\" :rules=\"rules\">\n            <el-form-item :label=\"$t('nginx.mirrorUrl')\" prop=\"mirror\">\n                <el-select v-model=\"build.mirror\">\n                    <el-option\n                        value=\"http://archive.ubuntu.com/ubuntu/\"\n                        label=\"http://archive.ubuntu.com/ubuntu/\"\n                    ></el-option>\n                    <el-option\n                        value=\"http://mirrors.aliyun.com/ubuntu/\"\n                        label=\"http://mirrors.aliyun.com/ubuntu/\"\n                    ></el-option>\n                    <el-option\n                        value=\"http://mirrors.tuna.tsinghua.edu.cn/ubuntu/\"\n                        label=\"http://mirrors.tuna.tsinghua.edu.cn/ubuntu/\"\n                    ></el-option>\n                    <el-option\n                        value=\"http://mirrors.ustc.edu.cn/ubuntu/\"\n                        label=\"http://mirrors.ustc.edu.cn/ubuntu/\"\n                    ></el-option>\n                </el-select>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit(buildForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n    <TaskLog ref=\"taskLogRef\" />\n</template>\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport { FormInstance } from 'element-plus';\nimport { getNginxModules, buildNginx } from '@/api/modules/nginx';\nimport i18n from '@/lang';\nimport { newUUID } from '@/utils/util';\nimport TaskLog from '@/components/log/task/index.vue';\nimport { Rules } from '@/global/form-rules';\n\nconst open = ref(false);\nconst loading = ref(false);\nconst buildForm = ref<FormInstance>();\nconst build = ref({\n    mirror: 'http://archive.ubuntu.com/ubuntu',\n});\nconst rules = {\n    mirror: [Rules.requiredSelect],\n};\nconst taskLogRef = ref();\n\nconst acceptParams = async () => {\n    getModules();\n    open.value = true;\n};\n\nconst getModules = async () => {\n    try {\n        const res = await getNginxModules();\n        build.value.mirror = res.data.mirror;\n    } catch (error) {}\n};\n\nconst submit = async (form: FormInstance) => {\n    await form.validate();\n    if (form.validate()) {\n        ElMessageBox.confirm(i18n.global.t('nginx.buildWarn'), i18n.global.t('nginx.build'), {\n            confirmButtonText: i18n.global.t('commons.button.confirm'),\n            cancelButtonText: i18n.global.t('commons.button.cancel'),\n        }).then(async () => {\n            const taskID = newUUID();\n            try {\n                await buildNginx({\n                    taskID: taskID,\n                    mirror: build.value.mirror,\n                });\n                handleClose();\n                openTaskLog(taskID);\n            } catch (error) {}\n        });\n    }\n};\n\nconst openTaskLog = (taskID: string) => {\n    taskLogRef.value.openWithTaskID(taskID);\n};\n\nconst handleClose = () => {\n    open.value = false;\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/module/index.vue",
    "content": "<template>\n    <div>\n        <ComplexTable :data=\"data\" @search=\"search()\" :heightDiff=\"350\" v-loading=\"loading\">\n            <template #toolbar>\n                <el-button type=\"primary\" @click=\"openOperate\">{{ $t('commons.button.create') }}</el-button>\n                <el-button type=\"primary\" plain @click=\"buildNginx\">{{ $t('nginx.build') }}</el-button>\n                <el-text type=\"warning\" class=\"!ml-2\">{{ $t('nginx.buildHelper') }}</el-text>\n            </template>\n            <el-table-column prop=\"name\" :label=\"$t('commons.table.name')\" />\n            <el-table-column prop=\"params\" :label=\"$t('nginx.params')\" />\n            <el-table-column :label=\"$t('commons.table.status')\" fix>\n                <template #default=\"{ row }\">\n                    <el-switch v-model=\"row.enable\" @click=\"updateModule(row)\" />\n                </template>\n            </el-table-column>\n            <fu-table-operations\n                :ellipsis=\"2\"\n                width=\"200px\"\n                :buttons=\"buttons\"\n                :label=\"$t('commons.table.operate')\"\n                fixed=\"right\"\n                fix\n            />\n        </ComplexTable>\n        <Operate ref=\"operateRef\" @close=\"search\" />\n        <OpDialog ref=\"deleteRef\" @search=\"search\" @cancel=\"search\" />\n        <Build ref=\"buildRef\" />\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { getNginxModules, updateNginxModule } from '@/api/modules/nginx';\nimport i18n from '@/lang';\nimport { Nginx } from '@/api/interface/nginx';\nimport { MsgSuccess } from '@/utils/message';\nimport Operate from './operate/index.vue';\nimport Build from './build/index.vue';\n\nconst data = ref([]);\nconst loading = ref(false);\nconst buttons = [\n    {\n        label: i18n.global.t('commons.button.edit'),\n        click: function (row: Nginx.NginxModule) {\n            openEdit(row);\n        },\n    },\n    {\n        label: i18n.global.t('commons.button.delete'),\n        click: function (row: Nginx.NginxModule) {\n            deleteModule(row);\n        },\n    },\n];\nconst operateRef = ref();\nconst deleteRef = ref();\nconst buildRef = ref();\n\nconst buildNginx = async () => {\n    buildRef.value.acceptParams();\n};\n\nconst search = () => {\n    loading.value = true;\n    getNginxModules()\n        .then((res) => {\n            data.value = res.data.modules;\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst openOperate = () => {\n    operateRef.value.acceptParams('create');\n};\n\nconst openEdit = (row: Nginx.NginxModule) => {\n    operateRef.value.acceptParams('update', row);\n};\n\nconst updateModule = (row: Nginx.NginxModule) => {\n    loading.value = true;\n    const data = {\n        ...row,\n        operate: 'update',\n    };\n    updateNginxModule(data)\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst deleteModule = async (row: Nginx.NginxModule) => {\n    const data = {\n        name: row.name,\n        operate: 'delete',\n    };\n    deleteRef.value.acceptParams({\n        title: i18n.global.t('commons.button.delete'),\n        names: [row.name],\n        msg: i18n.global.t('commons.msg.operatorHelper', [\n            i18n.global.t('nginx.module'),\n            i18n.global.t('commons.button.delete'),\n        ]),\n        api: updateNginxModule,\n        params: data,\n    });\n};\n\nonMounted(() => {\n    search();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/module/operate/index.vue",
    "content": "<template>\n    <DrawerPro\n        v-model=\"open\"\n        :header=\"$t('nginx.' + mode)\"\n        size=\"large\"\n        :resource=\"mode === 'update' ? module.name : ''\"\n        @close=\"handleClose\"\n    >\n        <el-form ref=\"moduleForm\" label-position=\"top\" :model=\"module\" :rules=\"rules\">\n            <el-form-item :label=\"$t('commons.table.name')\" prop=\"name\">\n                <el-input v-model.trim=\"module.name\" :disabled=\"mode === 'update'\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('nginx.params')\" prop=\"params\">\n                <el-input v-model.trim=\"module.params\" :placeholder=\"$t('nginx.paramsHelper')\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('nginx.packages')\" prop=\"packages\">\n                <el-input v-model.trim=\"module.packages\" :placeholder=\"$t('nginx.packagesHelper')\"></el-input>\n            </el-form-item>\n            <el-form-item :label=\"$t('nginx.script')\" prop=\"script\">\n                <el-input\n                    v-model=\"module.script\"\n                    type=\"textarea\"\n                    :rows=\"10\"\n                    :placeholder=\"$t('nginx.scriptHelper')\"\n                ></el-input>\n            </el-form-item>\n        </el-form>\n        <template #footer>\n            <el-button @click=\"handleClose\" :disabled=\"loading\">{{ $t('commons.button.cancel') }}</el-button>\n            <el-button type=\"primary\" @click=\"submit(moduleForm)\" :disabled=\"loading\">\n                {{ $t('commons.button.confirm') }}\n            </el-button>\n        </template>\n    </DrawerPro>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Nginx } from '@/api/interface/nginx';\nimport { updateNginxModule } from '@/api/modules/nginx';\nimport { Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\n\nconst moduleForm = ref<FormInstance>();\nconst open = ref(false);\nconst em = defineEmits(['close']);\nconst mode = ref('create');\nconst loading = ref(false);\nconst module = ref({\n    name: '',\n    operate: 'create',\n    script: '',\n    enable: true,\n    params: '',\n    packages: '',\n});\nconst rules = ref({\n    name: [Rules.requiredInput, Rules.simpleName],\n    params: [Rules.requiredInput],\n});\n\nconst handleClose = () => {\n    open.value = false;\n    em('close', false);\n};\n\nconst acceptParams = async (operate: string, editModule: Nginx.NginxModule) => {\n    mode.value = operate;\n    if (operate === 'update') {\n        module.value = {\n            name: editModule.name,\n            script: editModule.script,\n            enable: editModule.enable,\n            params: editModule.params,\n            packages: editModule.packages,\n            operate: 'update',\n        };\n    }\n    open.value = true;\n};\n\nconst submit = async (form: FormInstance) => {\n    await form.validate();\n    if (form.validate()) {\n        loading.value = true;\n        const data = {\n            ...module.value,\n            operate: mode.value,\n        };\n        updateNginxModule(data)\n            .then(() => {\n                if (mode.value === 'update') {\n                    MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                } else if (mode.value === 'create') {\n                    MsgSuccess(i18n.global.t('commons.msg.createSuccess'));\n                }\n                handleClose();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    }\n};\n\ndefineExpose({\n    acceptParams,\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/other/https/index.vue",
    "content": "<template>\n    <el-row :gutter=\"20\" v-loading=\"loading\">\n        <el-col :xs=\"24\" :sm=\"18\" :md=\"8\" :lg=\"8\" :xl=\"8\">\n            <el-form ref=\"nginxForm\" label-position=\"right\" label-width=\"180px\" :model=\"form\" :rules=\"rules\">\n                <el-form-item :label=\"$t('commons.table.status')\" prop=\"enable\">\n                    <el-switch v-model=\"enable\"></el-switch>\n                    <span class=\"input-help\">{{ $t('nginx.defaultHttpsHelper1') }}</span>\n                </el-form-item>\n                <el-form-item :label=\"$t('nginx.sslRejectHandshake')\" prop=\"sslRejectHandshake\">\n                    <el-switch v-model=\"form.sslRejectHandshake\"></el-switch>\n                    <span class=\"input-help\">{{ $t('nginx.sslRejectHandshakeHelper') }}</span>\n                </el-form-item>\n                <el-form-item>\n                    <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                        {{ $t('commons.button.save') }}\n                    </el-button>\n                </el-form-item>\n            </el-form>\n        </el-col>\n    </el-row>\n</template>\n\n<script lang=\"ts\" setup>\nimport { getHttpsStatus, operateHttps } from '@/api/modules/nginx';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\n\nconst showPage = ref(false);\nconst enable = ref(false);\nconst loading = ref(false);\nconst form = reactive({\n    operate: 'enable',\n    sslRejectHandshake: false,\n});\n\nconst getStatus = async () => {\n    try {\n        const res = await getHttpsStatus();\n        enable.value = res.data.https;\n        form.sslRejectHandshake = res.data.sslRejectHandshake;\n        showPage.value = true;\n    } catch {}\n};\n\nconst submit = async () => {\n    loading.value = true;\n    try {\n        form.operate = enable.value ? 'enable' : 'disable';\n        await operateHttps(form);\n        MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));\n    } catch (error) {\n        getStatus();\n    } finally {\n        loading.value = false;\n    }\n};\n\nonMounted(() => {\n    getStatus();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/other/index.vue",
    "content": "<template>\n    <div>\n        <el-tabs tab-position=\"left\" v-model=\"tabIndex\" ref=\"tabsRef\">\n            <el-tab-pane :label=\"$t('nginx.defaultHttps')\" name=\"0\">\n                <Https v-if=\"tabIndex == '0'\"></Https>\n            </el-tab-pane>\n        </el-tabs>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport Https from './https/index.vue';\n\nconst tabIndex = ref('0');\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/performance/index.vue",
    "content": "<template>\n    <div>\n        <el-form :model=\"form\" :rules=\"variablesRules\" ref=\"nginxFormRef\" label-position=\"top\">\n            <el-row v-loading=\"loading\" :gutter=\"20\">\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"9\" :lg=\"9\" :xl=\"9\">\n                    <el-form-item label=\"server_names_hash_bucket_size\" prop=\"server_names_hash_bucket_size\">\n                        <el-input\n                            clearable\n                            type=\"number\"\n                            v-model.number=\"form.server_names_hash_bucket_size\"\n                        ></el-input>\n                        <span class=\"input-help\">{{ $t('nginx.serverNamesHashBucketSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"client_header_buffer_size\" prop=\"client_header_buffer_size\">\n                        <el-input clearable type=\"number\" v-model.number=\"form.client_header_buffer_size\">\n                            <template #append>K</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('nginx.clientHeaderBufferSizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"client_max_body_size\" prop=\"client_max_body_size\">\n                        <el-input clearable type=\"number\" v-model.number=\"form.client_max_body_size\">\n                            <template #append>MB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('nginx.clientMaxBodySizeHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"keepalive_timeout\" prop=\"keepalive_timeout\">\n                        <el-input clearable type=\"number\" v-model.number=\"form.keepalive_timeout\"></el-input>\n                        <span class=\"input-help\">{{ $t('nginx.keepaliveTimeoutHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n                <el-col :xs=\"24\" :sm=\"24\" :md=\"9\" :lg=\"9\" :xl=\"9\">\n                    <el-form-item label=\"gzip\" prop=\"gzip\">\n                        <el-select v-model=\"form.gzip\">\n                            <el-option :label=\"'on'\" :value=\"'on'\"></el-option>\n                            <el-option :label=\"'off'\" :value=\"'off'\"></el-option>\n                        </el-select>\n                        <span class=\"input-help\">{{ $t('nginx.gzipHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"gzip_min_length\" prop=\"gzip_min_length\">\n                        <el-input clearable v-model.number=\"form.gzip_min_length\">\n                            <template #append>KB</template>\n                        </el-input>\n                        <span class=\"input-help\">{{ $t('nginx.gzipMinLengthHelper') }}</span>\n                    </el-form-item>\n                    <el-form-item label=\"gzip_comp_level\" prop=\"gzip_comp_level\">\n                        <el-input clearable v-model.number=\"form.gzip_comp_level\"></el-input>\n                        <span class=\"input-help\">{{ $t('nginx.gzipCompLevelHelper') }}</span>\n                    </el-form-item>\n                </el-col>\n            </el-row>\n            <el-form-item>\n                <el-button type=\"primary\" @click=\"submit(nginxFormRef)\">\n                    {{ $t('commons.button.save') }}\n                </el-button>\n            </el-form-item>\n        </el-form>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { Nginx } from '@/api/interface/nginx';\nimport { getNginxConfigByScope, updateNginxConfigByScope } from '@/api/modules/nginx';\nimport { checkNumberRange, Rules } from '@/global/form-rules';\nimport i18n from '@/lang';\nimport { MsgSuccess } from '@/utils/message';\nimport { FormInstance } from 'element-plus';\nimport { onMounted, reactive, ref } from 'vue';\n\nlet req = ref<Nginx.NginxScopeReq>({\n    scope: 'http-per',\n});\nlet updateReq = ref<Nginx.NginxConfigReq>({\n    scope: 'http-per',\n    operate: 'update',\n    params: {},\n});\nlet data = ref();\nlet form = ref({\n    server_names_hash_bucket_size: 512,\n    client_header_buffer_size: 32,\n    client_max_body_size: 50,\n    keepalive_timeout: 60,\n    gzip_min_length: 1,\n    gzip_comp_level: 2,\n    gzip: 'on',\n});\nlet nginxFormRef = ref();\nlet loading = ref(false);\n\nconst variablesRules = reactive({\n    server_names_hash_bucket_size: [checkNumberRange(1, 9999)],\n    client_header_buffer_size: [checkNumberRange(0, 999999999)],\n    client_max_body_size: [checkNumberRange(0, 999999999)],\n    keepalive_timeout: [checkNumberRange(0, 999999999)],\n    gzip: [Rules.requiredSelect],\n    gzip_min_length: [Rules.requiredSelect],\n    gzip_comp_level: [checkNumberRange(1, 9)],\n});\n\nconst getParams = async () => {\n    const res = await getNginxConfigByScope(req.value);\n    data.value = res.data;\n    for (const param of res.data) {\n        if (param.params.length === 0) {\n            continue;\n        }\n        if (param.name == 'gzip') {\n            form.value.gzip = param.params[0];\n        } else {\n            form.value[param.name] = Number(param.params[0].match(/\\d+/g));\n        }\n    }\n};\n\nconst submit = async (formEl: FormInstance | undefined) => {\n    if (!formEl) return;\n    await formEl.validate((valid) => {\n        if (!valid) {\n            return;\n        }\n        loading.value = true;\n        let params = {\n            gzip: form.value.gzip,\n            server_names_hash_bucket_size: String(form.value.server_names_hash_bucket_size),\n            client_header_buffer_size: String(form.value.client_header_buffer_size) + 'k',\n            client_max_body_size: String(form.value.client_max_body_size) + 'm',\n            keepalive_timeout: String(form.value.keepalive_timeout),\n            gzip_min_length: String(form.value.gzip_min_length) + 'k',\n            gzip_comp_level: String(form.value.gzip_comp_level),\n        };\n        updateReq.value.params = params;\n        updateNginxConfigByScope(updateReq.value)\n            .then(() => {\n                MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n                getParams();\n            })\n            .finally(() => {\n                loading.value = false;\n            });\n    });\n};\n\nonMounted(() => {\n    getParams();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/source/index.vue",
    "content": "<template>\n    <div v-loading=\"loading\">\n        <CodemirrorPro v-model=\"content\" mode=\"nginx\" :heightDiff=\"350\"></CodemirrorPro>\n        <div class=\"mt-2.5\">\n            <el-button @click=\"getDefaultConfig()\" :disabled=\"loading\">\n                {{ $t('app.defaultConfig') }}\n            </el-button>\n            <el-button type=\"primary\" @click=\"submit()\" :disabled=\"loading\">\n                {{ $t('commons.button.save') }}\n            </el-button>\n        </div>\n        <el-row>\n            <el-col :span=\"4\">\n                <el-alert\n                    v-if=\"useOld\"\n                    class=\"mt-2.5\"\n                    :title=\"$t('app.defaultConfigHelper')\"\n                    type=\"info\"\n                    :closable=\"false\"\n                ></el-alert>\n            </el-col>\n        </el-row>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport { getNginx, updateNginxConfigFile } from '@/api/modules/nginx';\nimport { onMounted, ref } from 'vue';\nimport i18n from '@/lang';\nimport { getAppDefaultConfig } from '@/api/modules/app';\nimport { MsgSuccess } from '@/utils/message';\nimport CodemirrorPro from '@/components/codemirror-pro/index.vue';\n\nlet content = ref('');\nlet loading = ref(false);\nlet useOld = ref(false);\n\nconst submit = () => {\n    loading.value = true;\n    updateNginxConfigFile({\n        content: content.value,\n        backup: useOld.value,\n    })\n        .then(() => {\n            MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));\n            getNginxConfig();\n        })\n        .finally(() => {\n            loading.value = false;\n        });\n};\n\nconst getNginxConfig = async () => {\n    try {\n        const res = await getNginx();\n        content.value = res.data.content;\n        useOld.value = false;\n    } catch (error) {}\n};\n\nconst getDefaultConfig = async () => {\n    loading.value = true;\n    try {\n        const res = await getAppDefaultConfig('openresty', '');\n        content.value = res.data;\n        useOld.value = true;\n    } catch (error) {}\n    loading.value = false;\n};\n\nonMounted(() => {\n    getNginxConfig();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/nginx/status/index.vue",
    "content": "<template>\n    <div>\n        <el-form label-position=\"top\">\n            <el-row type=\"flex\" class=\"ml-5\" justify=\"center\">\n                <el-form-item class=\"w-1/4\">\n                    <template #label>\n                        <span class=\"status-label\">{{ $t('nginx.connections') }}</span>\n                    </template>\n                    <span class=\"status-count\">{{ data.active }}</span>\n                </el-form-item>\n                <el-form-item class=\"w-1/4\">\n                    <template #label>\n                        <span class=\"status-label\">{{ $t('nginx.accepts') }}</span>\n                    </template>\n                    <span class=\"status-count\">{{ data.accepts }}</span>\n                </el-form-item>\n                <el-form-item class=\"w-1/4\">\n                    <template #label>\n                        <span class=\"status-label\">{{ $t('nginx.handled') }}</span>\n                    </template>\n                    <span class=\"status-count\">{{ data.handled }}</span>\n                </el-form-item>\n                <el-form-item class=\"w-1/4\">\n                    <template #label>\n                        <span class=\"status-label\">{{ $t('nginx.requests') }}</span>\n                    </template>\n                    <span class=\"status-count\">{{ data.requests }}</span>\n                </el-form-item>\n                <el-form-item class=\"w-1/4\">\n                    <template #label>\n                        <span class=\"status-label\">{{ $t('nginx.reading') }}</span>\n                    </template>\n                    <span class=\"status-count\">{{ data.reading }}</span>\n                </el-form-item>\n                <el-form-item class=\"w-1/4\">\n                    <template #label>\n                        <span class=\"status-label\">{{ $t('nginx.writing') }}</span>\n                    </template>\n                    <span class=\"status-count\">{{ data.writing }}</span>\n                </el-form-item>\n                <el-form-item class=\"w-1/4\">\n                    <template #label>\n                        <span class=\"status-label\">{{ $t('nginx.waiting') }}</span>\n                    </template>\n                    <span class=\"status-count\">{{ data.waiting }}</span>\n                </el-form-item>\n                <el-form-item class=\"w-1/4\" />\n            </el-row>\n        </el-form>\n    </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { Nginx } from '@/api/interface/nginx';\nimport { getNginxStatus } from '@/api/modules/nginx';\nimport { onMounted, ref } from 'vue';\n\nconst props = defineProps({\n    status: {\n        type: String,\n        default: 'Running',\n    },\n});\n\nlet data = ref<Nginx.NginxStatus>({\n    accepts: 0,\n    handled: 0,\n    requests: 0,\n    reading: 0,\n    waiting: 0,\n    writing: 0,\n    active: 0,\n});\n\nconst get = async () => {\n    if (props.status != 'Running') {\n        return;\n    }\n    const res = await getNginxStatus();\n    data.value = res.data;\n};\n\nonMounted(() => {\n    get();\n});\n</script>\n"
  },
  {
    "path": "frontend/src/views/website/website/status/index.vue",
    "content": "<template>\n    <div class=\"app-status\">\n        <el-card>\n            <div>\n                <el-tag effect=\"dark\" type=\"success\">{{ props.primaryDomain }}</el-tag>\n                <span class=\"status-content\">\n                    <Status class=\"span-font\" :key=\"props.status\" :status=\"props.status\"></Status>\n                </span>\n                <span class=\"status-content\">\n                    <el-tag type=\"info\">\n                        {{ $t('website.expireDate') }}:\n                        <span v-if=\"isEver(props.expireDate)\">\n                            {{ $t('website.neverExpire') }}\n                        </span>\n                        <span v-else>{{ dateFormatSimple(props.expireDate) }}</span>\n                    </el-tag>\n                </span>\n            </div>\n        </el-card>\n    </div>\n</template>\n<script lang=\"ts\" setup>\nimport Status from '@/components/status/index.vue';\nimport { dateFormatSimple } from '@/utils/util';\nconst props = defineProps({\n    primaryDomain: {\n        type: String,\n        default: '',\n    },\n    status: {\n        type: String,\n        default: '',\n    },\n    expireDate: {\n        type: String,\n        default: '',\n    },\n});\n\nconst isEver = (time: string) => {\n    const expireDate = new Date(time);\n    return expireDate > new Date('9999-12-30');\n};\n</script>\n\n<style lang=\"scss\"></style>\n"
  },
  {
    "path": "frontend/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n    content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],\n    theme: {\n        extend: {},\n    },\n    plugins: [],\n    corePlugins: {\n        preflight: false,\n    },\n};\n"
  },
  {
    "path": "frontend/tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"esnext\",\n        \"useDefineForClassFields\": true,\n        \"module\": \"esnext\",\n        \"moduleResolution\": \"node\",\n\n        /* Strict Type-Checking Options */\n        \"strict\": false /* Enable all strict type-checking options. */,\n        // \"noImplicitAny\": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */\n        // \"strictNullChecks\": true,              /* Enable strict null checks. */\n        // \"strictFunctionTypes\": true,           /* Enable strict checking of function types. */\n        // \"strictBindCallApply\": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */\n        // \"strictPropertyInitialization\": true,  /* Enable strict checking of property initialization in classes. */\n        // \"noImplicitThis\": true,                /* Raise error on 'this' expressions with an implied 'any' type. */\n        // \"alwaysStrict\": true,                  /* Parse in strict mode and emit \"use strict\" for each source file. */\n\n        \"jsx\": \"preserve\",\n        \"jsxFactory\": \"h\",\n        \"jsxFragmentFactory\": \"Fragment\",\n        \"sourceMap\": true,\n        \"resolveJsonModule\": true,\n        \"esModuleInterop\": true,\n        \"lib\": [\"esnext\", \"dom\"],\n        // 解析非相对模块名的基准目录\n        \"baseUrl\": \"./\",\n        // 模块名到基于 baseUrl的路径映射的列表。\n        \"paths\": {\n            \"@\": [\"src\"],\n            \"@/*\": [\"src/*\"]\n        },\n        // 跳过库检查，解决打包失败\n        \"skipLibCheck\": true,\n        \"ignoreDeprecations\": \"5.0\"\n    },\n    \"include\": [\n        \"src/**/*.ts\",\n        \"src/**/*.d.ts\",\n        \"src/**/*.tsx\",\n        \"src/**/*.vue\",\n        \"build/**/*.ts\",\n        \"build/**/*.d.ts\",\n        \"vite.config.ts\",\n        \"auto-imports.d.ts\"\n    ],\n    \"exclude\": [\"node_modules\", \"dist\", \"**/*.js\", \"*.json\", \"*.md\"]\n}\n"
  },
  {
    "path": "frontend/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n/// <reference types=\"vite-svg-loader\" />\n"
  },
  {
    "path": "frontend/vite.config.ts",
    "content": "import { resolve } from 'path';\nimport { wrapperEnv } from './src/utils/get-env';\nimport { visualizer } from 'rollup-plugin-visualizer';\nimport viteCompression from 'vite-plugin-compression';\nimport VueSetupExtend from 'vite-plugin-vue-setup-extend';\nimport eslintPlugin from 'vite-plugin-eslint';\nimport vueJsx from '@vitejs/plugin-vue-jsx';\nimport DefineOptions from 'unplugin-vue-define-options/vite';\nimport { defineConfig, loadEnv, ConfigEnv, UserConfig } from 'vite';\nimport vue from '@vitejs/plugin-vue';\nimport pkg from './package.json';\nimport dayjs from 'dayjs';\n\nimport AutoImport from 'unplugin-auto-import/vite';\nimport Components from 'unplugin-vue-components/vite';\nimport { ElementPlusResolver } from 'unplugin-vue-components/resolvers';\nimport svgLoader from 'vite-svg-loader';\n\nconst prefix = `monaco-editor/esm/vs`;\n\nconst { dependencies, devDependencies, name, version } = pkg;\nconst __APP_INFO__ = {\n    pkg: { dependencies, devDependencies, name, version },\n    lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),\n};\n\nexport default defineConfig(({ mode }: ConfigEnv): UserConfig => {\n    const env = loadEnv(mode, process.cwd());\n    const viteEnv = wrapperEnv(env);\n\n    return {\n        resolve: {\n            alias: {\n                '@': resolve(__dirname, './src'),\n                'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',\n                xpack: resolve(__dirname, './src/xpack'),\n            },\n        },\n        define: {\n            __APP_INFO__: JSON.stringify(__APP_INFO__),\n        },\n        css: {\n            preprocessorOptions: {\n                scss: {\n                    additionalData: `@use \"@/styles/var.scss\" as *;`,\n                    silenceDeprecations: ['legacy-js-api'],\n                },\n            },\n        },\n        server: {\n            port: viteEnv.VITE_PORT,\n            open: viteEnv.VITE_OPEN,\n            host: '0.0.0.0',\n            sourcemapIgnoreList: (sourcePath) => {\n                return sourcePath.includes('node_modules');\n            },\n            proxy: {\n                '/api/v2': {\n                    target: 'http://localhost:9999/',\n                    changeOrigin: true,\n                    ws: true,\n                },\n            },\n        },\n        plugins: [\n            vue(),\n            DefineOptions(),\n            eslintPlugin({\n                exclude: ['**/*.js'],\n            }),\n            vueJsx(),\n            VueSetupExtend(),\n            viteEnv.VITE_REPORT && visualizer(),\n            viteEnv.VITE_BUILD_GZIP &&\n                viteCompression({\n                    verbose: true,\n                    disable: false,\n                    threshold: 10240,\n                    algorithm: 'gzip',\n                    ext: '.gz',\n                }),\n            AutoImport({\n                imports: ['vue', 'vue-router'],\n                resolvers: [\n                    ElementPlusResolver({\n                        importStyle: 'sass',\n                    }),\n                ],\n            }),\n            Components({\n                resolvers: [\n                    ElementPlusResolver({\n                        importStyle: 'sass',\n                    }),\n                ],\n            }),\n            svgLoader({\n                defaultImport: 'url',\n            }),\n        ],\n        esbuild: {\n            pure: viteEnv.VITE_DROP_CONSOLE ? ['console.log'] : [],\n            drop: viteEnv.VITE_DROP_CONSOLE && process.env.NODE_ENV === 'production' ? ['debugger'] : [],\n        },\n        build: {\n            sourcemap: false,\n            outDir: '../core/cmd/server/web',\n            minify: 'esbuild',\n            target: 'esnext',\n            cssCodeSplit: false,\n            rollupOptions: {\n                output: {\n                    chunkFileNames: 'assets/js/[name]-[hash].js',\n                    entryFileNames: 'assets/js/[name]-[hash].js',\n                    assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',\n                    manualChunks: {\n                        jsonWorker: [`${prefix}/language/json/json.worker`],\n                        cssWorker: [`${prefix}/language/css/css.worker`],\n                        htmlWorker: [`${prefix}/language/html/html.worker`],\n                        tsWorker: [`${prefix}/language/typescript/ts.worker`],\n                        editorWorker: [`${prefix}/editor/editor.worker`],\n                    },\n                },\n            },\n        },\n    };\n});\n"
  },
  {
    "path": "sonar-project.properties",
    "content": "sonar.projectKey=1Panel-dev_1Panel\nsonar.organization=1panel-dev\n\nsonar.exclusions=frontend/src/lang/modules/*,backend/i18n/lang/*\n\n# This is the name and version displayed in the SonarCloud UI.\n#sonar.projectName=1Panel\n#sonar.projectVersion=1.0\n\n\n# Path is relative to the sonar-project.properties file. Replace \"\\\" by \"/\" on Windows.\n#sonar.sources=.\n\n# Encoding of the source code. Default is default system encoding\n#sonar.sourceEncoding=UTF-8\n"
  }
]